diff --git a/include/nif_versions.h b/include/nif_versions.h index 7a430afffa4358b9d2ee2b8754d2b931eb092550..6d77e1556ba9acc3b84bccc36bac93f5748d4662 100644 --- a/include/nif_versions.h +++ b/include/nif_versions.h @@ -5,17 +5,19 @@ All rights reserved. Please see niflib.h for licence. */ #define _NIF_VERSIONS_H_ //NIF Version Constants -const unsigned int VER_4_0_0_2 = 0x04000002; /*!< Nif Version 4.0.0.2 */ -const unsigned int VER_4_1_0_12 = 0x0401000C; /*!< Nif Version 4.1.0.12 */ -const unsigned int VER_4_2_0_2 = 0x04020002; /*!< Nif Version 4.2.0.2 */ -const unsigned int VER_4_2_1_0 = 0x04020100; /*!< Nif Version 4.2.1.0 */ -const unsigned int VER_4_2_2_0 = 0x04020200; /*!< Nif Version 4.2.2.0 */ -const unsigned int VER_10_0_1_0 = 0x0A000100; /*!< Nif Version 10.0.1.0 */ -const unsigned int VER_10_1_0_0 = 0x0A010000; /*!< Nif Version 10.1.0.0 */ -const unsigned int VER_10_2_0_0 = 0x0A020000; /*!< Nif Version 10.2.0.0 */ -const unsigned int VER_20_0_0_4 = 0x14000004; /*!< Nif Version 20.0.0.4 */ -const unsigned int VER_20_0_0_5 = 0x14000005; /*!< Nif Version 20.0.0.4 */ -const unsigned int VER_UNSUPPORTED = 0xFFFFFFFF; /*!< Unsupported Nif Version */ -const unsigned int VER_INVALID = 0xFFFFFFFE; /*!< Not a Nif file */ +const unsigned VER_4_0_0_0 = 0X04000000; /*!< NIF Version 4.0.0.0 */ +const unsigned VER_4_0_0_2 = 0x04000002; /*!< NIF Version 4.0.0.2 */ +const unsigned VER_4_1_0_12 = 0x0401000C; /*!< NIF Version 4.1.0.12 */ +const unsigned VER_4_2_0_2 = 0x04020002; /*!< NIF Version 4.2.0.2 */ +const unsigned VER_4_2_1_0 = 0x04020100; /*!< NIF Version 4.2.1.0 */ +const unsigned VER_4_2_2_0 = 0x04020200; /*!< NIF Version 4.2.2.0 */ +const unsigned VER_10_0_1_0 = 0x0A000100; /*!< NIF Version 10.0.1.0 */ +const unsigned VER_10_1_0_0 = 0x0A010000; /*!< NIF Version 10.1.0.0 */ +const unsigned VER_10_1_0_106 = 0x0A01006A; /*!< NIF Version 10.1.0.106 */ +const unsigned VER_10_2_0_0 = 0x0A020000; /*!< NIF Version 10.2.0.0 */ +const unsigned VER_20_0_0_4 = 0x14000004; /*!< NIF Version 20.0.0.4 */ +const unsigned VER_20_0_0_5 = 0x14000005; /*!< NIF Version 20.0.0.4 */ +const unsigned VER_UNSUPPORTED = 0xFFFFFFFF; /*!< Unsupported NIF Version */ +const unsigned VER_INVALID = 0xFFFFFFFE; /*!< Not a NIF file */ #endif \ No newline at end of file diff --git a/include/niflib.h b/include/niflib.h index 91323f918f10b8fd68e0c427dd97d4958d5fbeb4..327c4631f1270401a214826e086bbc96acd4ce64 100644 --- a/include/niflib.h +++ b/include/niflib.h @@ -115,36 +115,33 @@ struct NifInfo { //--Main Functions--// /*! - * Reads the header of the given file by file name and returns the NIF version. Call this - * function prior to calling ReadNifList or ReadNifTree, if you need to make sure that the NIF file is supported. + * Reads the header of the given file by file name and returns the NIF version + * if it is a valid NIF file. Call this function prior to calling ReadNifList + * or ReadNifTree, if you want to make sure that its NIF version is supported + * before trying to read it. * \param file_name The name of the file to load, or the complete path if it is not in the working directory. - * \return The NIF version of the file, in hexadecimal format. If the file is not a NIF file, it returns VER_INVALID. If it is a NIF file, but its version is not supported by the library, it returns VER_UNSUPPORTED. + * \return The NIF version of the file, in hexadecimal format. If the file is not a NIF file, it returns VER_INVALID. * * <b>Example:</b> * \code - * unsigned int ver = CheckNifHeader("test_in.nif"); - * if ( ver == VER_UNSUPPORTED ) cout << "unsupported" << endl; - * else if ( ver == VER_INVALID ) cout << "invalid" << endl; - * else { - * vector<NiObjectRef> blocks = ReadNifList( "test_in.nif" ); - * cout << blocks[0] << endl; - * }; - * + * unsigned ver = GetNifVersion("test_in.nif"); + * if ( IsSupportedVersion(ver) == false ) { + * cout << "Unsupported.\n" << endl; + * } else if ( ver == VER_INVALID ) { + * cout << "Not a NIF file.\n" << endl; + * } * \endcode * * <b>In Python:</b> * \code * ver = CheckNifHeader("test_in.nif") - * if ( ver == VER_UNSUPPORTED ): - * print "unsupported" + * if ( IsSupportedVersion(ver) == false ): + * print "Unsupported." * elif ( ver == VER_INVALID ): - * print "invalid" - * else: - * blocks = ReadNifList( "test_in.nif" ) - * print blocks[0] + * print "Not a NIF file." * \endcode */ -NIFLIB_API unsigned int CheckNifHeader( string const & file_name ); +NIFLIB_API unsigned int GetNifVersion( string const & file_name ); /*! * Reads the given file by file name and returns a vector of block references @@ -296,18 +293,28 @@ NIFLIB_HIDDEN Ref<NiObject> CreateObject( string block_type ); #endif /*! - * Returns whether the requested version is supported. - * \param version The version of the nif format to test for availablity. + * Returns whether the requested version is explicitly supported. This does + * not mean that the file will not open, rather it means that we have not + * encountered files with this version in our tests yet. + * \param version The version of the NIF format to test for the support level of. * \return Whether the requested version is supported. */ -NIFLIB_API bool IsVersionSupported(unsigned int ver); +NIFLIB_API bool IsSupportedVersion( unsigned int version ); + +/*! + * Parses a version string and returns the equivalent version as a byte-packed integer. + * \param version The version number of the NIF format to parse in string format. + * \return The version in integer format or VER_INVALID if the version string is not in the correct format. + */ +NIFLIB_API unsigned ParseVersionString( string version ); /*! - * Parses the version string and returns in the equivalent version as integer - * \param version The version of the nif format to parse. - * \return The version in integer format. Returns VER_INVALID for invalid version strings. + * Takes a NIF version in byte-packed integer format and returns a formatted human- + * readable string. For example, 0x04000002 returns the string "4.0.0.2" + * \param version The NIF version in integer form. + * \return The equivalent string representation of the version number. */ -NIFLIB_API unsigned int GetVersion(string version); +NIFLIB_API string FormatVersionString( unsigned version ); //--USER GUIDE DOCUMENTATION--// diff --git a/src/niflib.cpp b/src/niflib.cpp index e921dbe3483614a1ae69d572e481905b0607901b..6e2b89dc3c0247ef892dec6b3181eff0438fbd7a 100644 --- a/src/niflib.cpp +++ b/src/niflib.cpp @@ -113,7 +113,7 @@ NiObjectRef FindRoot( vector<NiObjectRef> const & blocks ) { return StaticCast<NiObject>(root); } -unsigned int CheckNifHeader( string const & file_name ) { +unsigned int GetNifVersion( string const & file_name ) { //--Open File--// ifstream in( file_name.c_str(), ifstream::binary ); @@ -123,24 +123,18 @@ unsigned int CheckNifHeader( string const & file_name ) { string headerstr(header_string); // make sure this is a NIF file - if ( ( headerstr.substr(0, 22) != "NetImmerse File Format" ) - && ( headerstr.substr(0, 20) != "Gamebryo File Format" ) ) + unsigned ver_start = 0; + if ( headerstr.substr(0, 22) == "NetImmerse File Format" ) { + ver_start = 32; + } else if ( headerstr.substr(0, 20) == "Gamebryo File Format" ) { + ver_start = 30; + } else { + //Not a NIF file return VER_INVALID; + } - // supported versions - if ( headerstr == "NetImmerse File Format, Version 4.0.0.2" ) return VER_4_0_0_2; - if ( headerstr == "NetImmerse File Format, Version 4.1.0.12" ) return VER_4_1_0_12; - if ( headerstr == "NetImmerse File Format, Version 4.2.0.2" ) return VER_4_2_0_2; - if ( headerstr == "NetImmerse File Format, Version 4.2.1.0" ) return VER_4_2_1_0; - if ( headerstr == "NetImmerse File Format, Version 4.2.2.0" ) return VER_4_2_2_0; - if ( headerstr == "NetImmerse File Format, Version 10.0.1.0" ) return VER_10_0_1_0; - if ( headerstr == "Gamebryo File Format, Version 10.1.0.0" ) return VER_10_1_0_0; - if ( headerstr == "Gamebryo File Format, Version 10.2.0.0" ) return VER_10_2_0_0; - if ( headerstr == "Gamebryo File Format, Version 20.0.0.4" ) return VER_20_0_0_4; - if ( headerstr == "Gamebryo File Format, Version 20.0.0.5" ) return VER_20_0_0_5; - - // anything else: unsupported - return VER_UNSUPPORTED; + //Parse version string and return result. + return ParseVersionString( headerstr.substr( ver_start ) ); } //Reads the given file by file name and returns a vector of block references @@ -1009,9 +1003,10 @@ void MergeNifTrees( const Ref<NiNode> & target, const Ref<NiSequenceStreamHelper } -bool IsVersionSupported(unsigned int ver) { - switch (ver) +bool IsSupportedVersion( unsigned int version ) { + switch (version) { + case VER_4_0_0_0: case VER_4_0_0_2: case VER_4_1_0_12: case VER_4_2_0_2: @@ -1019,6 +1014,7 @@ bool IsVersionSupported(unsigned int ver) { case VER_4_2_2_0: case VER_10_0_1_0: case VER_10_1_0_0: + case VER_10_1_0_106: case VER_10_2_0_0: case VER_20_0_0_4: case VER_20_0_0_5: @@ -1027,29 +1023,62 @@ bool IsVersionSupported(unsigned int ver) { return false; } -unsigned int GetVersion(string version){ - unsigned int outver = 0; - string::size_type start = 0; - for(int offset = 3; offset >= 0 && start < version.length(); --offset) { - string::size_type end = version.find_first_of(".", start); - string::size_type len = (end == string.npos) ? end : end-start; - int num = 0; - stringstream sstr(version.substr(start, len)); - sstr >> num; - if (num > 0xFF) { - outver = VER_INVALID; - break; - } - outver |= (num << (offset * 8)); - if (len == string::npos) - break; - start = start + len + 1; - } - if (outver == 0) - outver = VER_INVALID; - return outver; +unsigned int ParseVersionString(string version) { + + unsigned int outver = 0; + + string::size_type start = 0, len, end; + for( int offset = 3; offset >= 0 && start < version.length(); --offset ) { + end = version.find_first_of( ".", start ); + + if ( end == string.npos ) { + len = end; + } else { + len = end-start; + } + + int num = 0; + stringstream sstr( version.substr(start, len) ); + sstr >> num; + if ( num > 0xFF ) { + return VER_INVALID; + } + outver |= ( num << (offset * 8) ); + if ( len == string::npos ) { + break; + } + start = start + len + 1; + } + + if ( outver == 0 ) { + return VER_INVALID; + } else { + return outver; + } +} + +string FormatVersionString(unsigned version) { + //Cast the version to an array of 4 bytes + char * byte_ver = (char*)&version; + + //Put the version parts into an integer array, reversing their order + int int_ver[4] = { byte_ver[3], byte_ver[2], byte_ver[1], byte_ver[0] }; + + //Format the version string and return it + stringstream out; + + if ( int_ver[0] >= 4 ) { + //Version 4+ is in x.x.x.x format. + out << int_ver[0] << "." << int_ver[1] << "." << int_ver[2] << "." << int_ver[3]; + } else { + //Versions before 4 are in x.x format. + out << int_ver[0] << "." << int_ver[1]; + } + + return out.str(); } + Ref<NiObject> CloneNifTree( Ref<NiObject> const & root, unsigned version, unsigned user_version ) { //Create a string stream to temporarily hold the state-save of this tree stringstream tmp;