diff --git a/contrib/extractor/System.cpp b/contrib/extractor/System.cpp index 725e139d392..fee116c9f56 100644 --- a/contrib/extractor/System.cpp +++ b/contrib/extractor/System.cpp @@ -218,12 +218,12 @@ uint32 ReadBuild(int locale) exit(1); } - unsigned int data_size = SFileGetFileSize(fileHandle); + unsigned int data_size = SFileGetFileSize(fileHandle, NULL); std::string text; text.resize(data_size); - if (!SFileReadFile(fileHandle, &text[0], data_size)) + if (!SFileReadFile(fileHandle, &text[0], data_size, NULL, NULL)) { printf("Fatal error: Can't read %s file!\n", filename.c_str()); exit(1); diff --git a/contrib/extractor/dbcfile.cpp b/contrib/extractor/dbcfile.cpp index f3295f489b4..a120bf45138 100644 --- a/contrib/extractor/dbcfile.cpp +++ b/contrib/extractor/dbcfile.cpp @@ -19,7 +19,7 @@ bool DBCFile::open() char header[4]; unsigned int na,nb,es,ss; - if (!SFileReadFile(fileHandle, header, 4)) // Magic header + if (!SFileReadFile(fileHandle, header, 4, NULL, NULL)) // Magic header { SFileCloseFile(fileHandle); return false; @@ -31,25 +31,25 @@ bool DBCFile::open() return false; } - if (!SFileReadFile(fileHandle, &na, 4)) // Number of records + if (!SFileReadFile(fileHandle, &na, 4, NULL, NULL)) // Number of records { SFileCloseFile(fileHandle); return false; } - if (!SFileReadFile(fileHandle, &nb, 4)) // Number of fields + if (!SFileReadFile(fileHandle, &nb, 4, NULL, NULL)) // Number of fields { SFileCloseFile(fileHandle); return false; } - if (!SFileReadFile(fileHandle, &es, 4)) // Size of a record + if (!SFileReadFile(fileHandle, &es, 4, NULL, NULL)) // Size of a record { SFileCloseFile(fileHandle); return false; } - if (!SFileReadFile(fileHandle, &ss, 4)) // String size + if (!SFileReadFile(fileHandle, &ss, 4, NULL, NULL)) // String size { SFileCloseFile(fileHandle); return false; @@ -70,7 +70,7 @@ bool DBCFile::open() size_t data_size = recordSize*recordCount+stringSize; - if (!SFileReadFile(fileHandle, data, data_size)) + if (!SFileReadFile(fileHandle, data, data_size, NULL, NULL)) { SFileCloseFile(fileHandle); return false; diff --git a/contrib/extractor/loadlib/loadlib.cpp b/contrib/extractor/loadlib/loadlib.cpp index e3cf54411e1..611726d40de 100644 --- a/contrib/extractor/loadlib/loadlib.cpp +++ b/contrib/extractor/loadlib/loadlib.cpp @@ -45,7 +45,7 @@ bool ExtractFile( char const* mpq_name, std::string const& filename ) if (!SFileOpenFileEx(*i, mpq_name, SFILE_OPEN_PATCHED_FILE, &fileHandle)) continue; - if (SFileGetFileSize(fileHandle) == 0) // some files removed in next updates and its reported size 0 + if (SFileGetFileSize(fileHandle, NULL) == 0) // some files removed in next updates and its reported size 0 { SFileCloseFile(fileHandle); return true; @@ -99,7 +99,7 @@ bool FileLoader::loadFile(char *filename, bool log) return false; } - data_size = SFileGetFileSize(fileHandle); + data_size = SFileGetFileSize(fileHandle, NULL); data = new uint8 [data_size]; if (!data) @@ -108,7 +108,7 @@ bool FileLoader::loadFile(char *filename, bool log) return false; } - if (!SFileReadFile(fileHandle, data, data_size)) + if (!SFileReadFile(fileHandle, data, data_size, NULL, NULL)) { if (log) printf("Can't read file %s\n", filename); diff --git a/dep/StormLib/CMakeLists.txt b/dep/StormLib/CMakeLists.txt index c8b00f7ad06..e4bd1b16f18 100644 --- a/dep/StormLib/CMakeLists.txt +++ b/dep/StormLib/CMakeLists.txt @@ -5,8 +5,6 @@ set(SRC_FILES src/adpcm/adpcm.cpp src/huffman/huff.cpp src/jenkins/lookup3.c - src/libtomcrypt/src/misc/crypt_libc.c - src/libtomcrypt/src/pk/rsa/rsa_verify_simple.c src/lzma/C/LzFind.c src/lzma/C/LzmaDec.c src/lzma/C/LzmaEnc.c @@ -15,6 +13,7 @@ set(SRC_FILES src/sparse/sparse.cpp src/FileStream.cpp src/SBaseCommon.cpp + src/SBaseDumpData.cpp src/SBaseFileTable.cpp src/SCompression.cpp src/SFileAddFile.cpp @@ -44,6 +43,7 @@ set(TOMCRYPT_FILES src/libtomcrypt/src/misc/crypt_find_prng.c src/libtomcrypt/src/misc/crypt_hash_descriptor.c src/libtomcrypt/src/misc/crypt_hash_is_valid.c + src/libtomcrypt/src/misc/crypt_libc.c src/libtomcrypt/src/misc/crypt_ltc_mp_descriptor.c src/libtomcrypt/src/misc/crypt_prng_descriptor.c src/libtomcrypt/src/misc/crypt_prng_is_valid.c @@ -91,6 +91,7 @@ set(TOMCRYPT_FILES src/libtomcrypt/src/pk/rsa/rsa_import.c src/libtomcrypt/src/pk/rsa/rsa_make_key.c src/libtomcrypt/src/pk/rsa/rsa_verify_hash.c + src/libtomcrypt/src/pk/rsa/rsa_verify_simple.c ) set(TOMMATH_FILES @@ -233,9 +234,9 @@ set(ZLIB_BZIP2_FILES src/zlib/zutil.c ) -set(TEST_SRC_FILES - test/Test.cpp -) +# set(TEST_SRC_FILES +# test/Test.cpp +# ) add_definitions(-D_7ZIP_ST -DBZ_STRICT_ANSI) @@ -247,6 +248,7 @@ if(WIN32) message(STATUS "Using mingw") endif() set(SRC_ADDITIONAL_FILES ${ZLIB_BZIP2_FILES} ${TOMCRYPT_FILES} ${TOMMATH_FILES}) + set(LINK_LIBS wininet) endif() if(APPLE) @@ -266,25 +268,38 @@ if (${CMAKE_SYSTEM_NAME} STREQUAL Linux) endif() endif() -add_library(StormLib SHARED ${SRC_FILES} ${SRC_ADDITIONAL_FILES}) -target_link_libraries(StormLib ${LINK_LIBS}) +add_library(storm SHARED ${SRC_FILES} ${SRC_ADDITIONAL_FILES}) +target_link_libraries(storm ${LINK_LIBS}) -add_library(StormLib_static STATIC ${SRC_FILES} ${SRC_ADDITIONAL_FILES}) -target_link_libraries(StormLib_static ${LINK_LIBS}) -set_target_properties(StormLib_static PROPERTIES OUTPUT_NAME StormLib) +# option(WITH_TEST "Compile Test application" OFF) +# if(WITH_TEST) +# add_executable(storm_test ${TEST_SRC_FILES}) +# target_link_libraries(storm_test storm) +# endif() -add_executable(StormLib_test ${TEST_SRC_FILES}) -target_link_libraries(StormLib_test StormLib_static) +# option(WITH_STATIC "Compile static linked library" OFF) +# if(WITH_STATIC) +# add_library(StormLib_static STATIC ${SRC_FILES} ${SRC_ADDITIONAL_FILES}) +# target_link_libraries(StormLib_static ${LINK_LIBS}) +# set_target_properties(StormLib_static PROPERTIES OUTPUT_NAME StormLib) +# endif() if(APPLE) - set_target_properties(StormLib PROPERTIES FRAMEWORK true) - set_target_properties(StormLib PROPERTIES PUBLIC_HEADER "src/StormLib.h src/StormPort.h") - set_target_properties(StormLib PROPERTIES LINK_FLAGS "-framework Carbon") + set_target_properties(storm PROPERTIES FRAMEWORK true) + set_target_properties(storm PROPERTIES PUBLIC_HEADER "src/StormLib.h src/StormPort.h") + set_target_properties(storm PROPERTIES LINK_FLAGS "-framework Carbon") endif() if(UNIX) - set_target_properties(StormLib PROPERTIES SOVERSION 0) + set_target_properties(storm PROPERTIES VERSION 8.20.0) + set_target_properties(storm PROPERTIES SOVERSION 8.20) endif() -install(TARGETS StormLib StormLib_static RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib) +# On Win32, build StormLib.dll since we don't want to clash with Storm.dll +if(WIN32) + set_target_properties(storm PROPERTIES OUTPUT_NAME StormLib) +endif() + +install(TARGETS storm RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib FRAMEWORK DESTINATION /Library/Frameworks) install(FILES src/StormLib.h src/StormPort.h DESTINATION include) + diff --git a/dep/StormLib/Publish.bat b/dep/StormLib/Publish.bat index 5ee392501df..59db0b1e3fd 100644 --- a/dep/StormLib/Publish.bat +++ b/dep/StormLib/Publish.bat @@ -10,13 +10,12 @@ zip.exe -ur9 ..\WWW\web\download\stormlib.zip StormLib\storm_dll\* zip.exe -ur9 ..\WWW\web\download\stormlib.zip StormLib\StormLib.xcodeproj\* zip.exe -ur9 ..\WWW\web\download\stormlib.zip StormLib\stormlib_dll\* zip.exe -ur9 ..\WWW\web\download\stormlib.zip StormLib\test\* -zip.exe -u9 ..\WWW\web\download\stormlib.zip StormLib\makefile* +zip.exe -u9 ..\WWW\web\download\stormlib.zip StormLib\CMakeLists.txt +zip.exe -u9 ..\WWW\web\download\stormlib.zip StormLib\makefile.* +zip.exe -u9 ..\WWW\web\download\stormlib.zip StormLib\Info.plist zip.exe -u9 ..\WWW\web\download\stormlib.zip StormLib\*.bat zip.exe -u9 ..\WWW\web\download\stormlib.zip StormLib\*.sln zip.exe -u9 ..\WWW\web\download\stormlib.zip StormLib\*.vcproj -zip.exe -u9 ..\WWW\web\download\stormlib.zip StormLib\*.plist -zip.exe -u9 ..\WWW\web\download\stormlib.zip StormLib\*.txt -zip.exe -u9 ..\WWW\web\download\stormlib.zip StormLib\*.kdev4 echo. echo Press any key to exit ... diff --git a/dep/StormLib/Publish_beta.bat b/dep/StormLib/Publish_beta.bat index bc5661b70ca..380ea1c84db 100644 --- a/dep/StormLib/Publish_beta.bat +++ b/dep/StormLib/Publish_beta.bat @@ -10,13 +10,12 @@ zip.exe -ur9 ..\WWW\web\download\stormlib_beta.zip StormLib\storm_dll\* zip.exe -ur9 ..\WWW\web\download\stormlib_beta.zip StormLib\StormLib.xcodeproj\* zip.exe -ur9 ..\WWW\web\download\stormlib_beta.zip StormLib\stormlib_dll\* zip.exe -ur9 ..\WWW\web\download\stormlib_beta.zip StormLib\test\* -zip.exe -u9 ..\WWW\web\download\stormlib_beta.zip StormLib\makefile* +zip.exe -u9 ..\WWW\web\download\stormlib_beta.zip StormLib\CMakeLists.txt +zip.exe -u9 ..\WWW\web\download\stormlib_beta.zip StormLib\makefile.* +zip.exe -u9 ..\WWW\web\download\stormlib_beta.zip StormLib\Info.plist zip.exe -u9 ..\WWW\web\download\stormlib_beta.zip StormLib\*.bat zip.exe -u9 ..\WWW\web\download\stormlib_beta.zip StormLib\*.sln zip.exe -u9 ..\WWW\web\download\stormlib_beta.zip StormLib\*.vcproj -zip.exe -u9 ..\WWW\web\download\stormlib_beta.zip StormLib\*.plist -zip.exe -u9 ..\WWW\web\download\stormlib_beta.zip StormLib\*.txt -zip.exe -u9 ..\WWW\web\download\stormlib_beta.zip StormLib\*.kdev4 echo. echo Press any key to exit ... diff --git a/dep/StormLib/README.md b/dep/StormLib/README.md new file mode 100644 index 00000000000..b8737e15d42 --- /dev/null +++ b/dep/StormLib/README.md @@ -0,0 +1,23 @@ +stormlib ![Project status](http://getmangos.com/assets/img/repository-status-maintained.png) +======== + +The StormLib library is a pack of modules, written in C++, which are able to +read and also to write files from/to the MPQ archives. + +MPQ (MoPaQ) is an archive format developed by Blizzard Entertainment, +purposed for storing data files, images, sounds, music and videos for their +games. + +*Notice*: this repository is meant to track the development of the original +[stormlib][1], developed by [Ladislav Zezula][2]. + +Usage +----- +This library is used inside of [mangos-zero][3] to access data inside of MPQ +archives included in the [World of Warcraft][4] game client. + + +[1]: http://www.zezula.net/en/mpq/stormlib.html "StormLib" +[2]: http://www.zezula.net/ "Ladislav Zezula" +[3]: http://github.com/mangos-zero "mangos-zero" +[4]: http://eu.blizzard.com/en-gb/games/wow/ "World of Warcraft" diff --git a/dep/StormLib/StormLib.sln b/dep/StormLib/StormLib.sln index 8a677992de7..758f29c30e2 100644 --- a/dep/StormLib/StormLib.sln +++ b/dep/StormLib/StormLib.sln @@ -1,14 +1,12 @@  Microsoft Visual Studio Solution File, Format Version 10.00 # Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StormLib", "StormLib.vcproj", "{78424708-1F6E-4D4B-920C-FB6D26847055}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StormLib_dll", "StormLib_dll.vcproj", "{CB385198-50B1-4CF4-883B-11F042DED6AA}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StormLib_test", "StormLib_test.vcproj", "{AA561A7B-26EA-49AF-90E8-C53C1FA2965D}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Storm_dll", "Storm_dll.vcproj", "{BD600973-C6FA-4CE3-8821-67F6418B7F9C}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "StormLib", "StormLib.vcproj", "{78424708-1F6E-4D4B-920C-FB6D26847055}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -17,73 +15,57 @@ Global DebugAD|x64 = DebugAD|x64 DebugAS|Win32 = DebugAS|Win32 DebugAS|x64 = DebugAS|x64 + DebugUD|Win32 = DebugUD|Win32 + DebugUD|x64 = DebugUD|x64 + DebugUS|Win32 = DebugUS|Win32 + DebugUS|x64 = DebugUS|x64 Release|Win32 = Release|Win32 Release|x64 = Release|x64 ReleaseAD|Win32 = ReleaseAD|Win32 ReleaseAD|x64 = ReleaseAD|x64 ReleaseAS|Win32 = ReleaseAS|Win32 ReleaseAS|x64 = ReleaseAS|x64 + ReleaseUD|Win32 = ReleaseUD|Win32 + ReleaseUD|x64 = ReleaseUD|x64 + ReleaseUS|Win32 = ReleaseUS|Win32 + ReleaseUS|x64 = ReleaseUS|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {CB385198-50B1-4CF4-883B-11F042DED6AA}.Debug|Win32.ActiveCfg = Debug|Win32 - {CB385198-50B1-4CF4-883B-11F042DED6AA}.Debug|Win32.Build.0 = Debug|Win32 - {CB385198-50B1-4CF4-883B-11F042DED6AA}.Debug|x64.ActiveCfg = Debug|x64 - {CB385198-50B1-4CF4-883B-11F042DED6AA}.Debug|x64.Build.0 = Debug|x64 - {CB385198-50B1-4CF4-883B-11F042DED6AA}.DebugAD|Win32.ActiveCfg = Debug|x64 - {CB385198-50B1-4CF4-883B-11F042DED6AA}.DebugAD|x64.ActiveCfg = Debug|x64 - {CB385198-50B1-4CF4-883B-11F042DED6AA}.DebugAD|x64.Build.0 = Debug|x64 - {CB385198-50B1-4CF4-883B-11F042DED6AA}.DebugAS|Win32.ActiveCfg = Debug|x64 - {CB385198-50B1-4CF4-883B-11F042DED6AA}.DebugAS|x64.ActiveCfg = Debug|x64 - {CB385198-50B1-4CF4-883B-11F042DED6AA}.DebugAS|x64.Build.0 = Debug|x64 - {CB385198-50B1-4CF4-883B-11F042DED6AA}.Release|Win32.ActiveCfg = Release|Win32 - {CB385198-50B1-4CF4-883B-11F042DED6AA}.Release|Win32.Build.0 = Release|Win32 - {CB385198-50B1-4CF4-883B-11F042DED6AA}.Release|x64.ActiveCfg = Release|x64 - {CB385198-50B1-4CF4-883B-11F042DED6AA}.Release|x64.Build.0 = Release|x64 - {CB385198-50B1-4CF4-883B-11F042DED6AA}.ReleaseAD|Win32.ActiveCfg = Release|x64 - {CB385198-50B1-4CF4-883B-11F042DED6AA}.ReleaseAD|x64.ActiveCfg = Release|x64 - {CB385198-50B1-4CF4-883B-11F042DED6AA}.ReleaseAD|x64.Build.0 = Release|x64 - {CB385198-50B1-4CF4-883B-11F042DED6AA}.ReleaseAS|Win32.ActiveCfg = Release|x64 - {CB385198-50B1-4CF4-883B-11F042DED6AA}.ReleaseAS|x64.ActiveCfg = Release|x64 - {CB385198-50B1-4CF4-883B-11F042DED6AA}.ReleaseAS|x64.Build.0 = Release|x64 {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.Debug|Win32.ActiveCfg = Debug|Win32 {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.Debug|Win32.Build.0 = Debug|Win32 - {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.Debug|x64.ActiveCfg = Debug|Win32 - {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.DebugAD|Win32.ActiveCfg = Debug|Win32 - {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.DebugAD|Win32.Build.0 = Debug|Win32 - {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.DebugAD|x64.ActiveCfg = Debug|Win32 - {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.DebugAS|Win32.ActiveCfg = Debug|Win32 - {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.DebugAS|Win32.Build.0 = Debug|Win32 - {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.DebugAS|x64.ActiveCfg = Debug|Win32 + {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.Debug|x64.ActiveCfg = Debug|x64 + {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.Debug|x64.Build.0 = Debug|x64 + {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.DebugAD|Win32.ActiveCfg = Debug|x64 + {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.DebugAD|x64.ActiveCfg = Debug|x64 + {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.DebugAD|x64.Build.0 = Debug|x64 + {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.DebugAS|Win32.ActiveCfg = Debug|x64 + {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.DebugAS|x64.ActiveCfg = Debug|x64 + {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.DebugAS|x64.Build.0 = Debug|x64 + {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.DebugUD|Win32.ActiveCfg = Debug|x64 + {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.DebugUD|x64.ActiveCfg = Debug|x64 + {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.DebugUD|x64.Build.0 = Debug|x64 + {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.DebugUS|Win32.ActiveCfg = Debug|x64 + {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.DebugUS|x64.ActiveCfg = Debug|x64 + {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.DebugUS|x64.Build.0 = Debug|x64 {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.Release|Win32.ActiveCfg = Release|Win32 {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.Release|Win32.Build.0 = Release|Win32 - {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.Release|x64.ActiveCfg = Release|Win32 - {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.ReleaseAD|Win32.ActiveCfg = Release|Win32 - {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.ReleaseAD|Win32.Build.0 = Release|Win32 - {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.ReleaseAD|x64.ActiveCfg = Release|Win32 - {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.ReleaseAS|Win32.ActiveCfg = Release|Win32 - {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.ReleaseAS|Win32.Build.0 = Release|Win32 - {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.ReleaseAS|x64.ActiveCfg = Release|Win32 - {BD600973-C6FA-4CE3-8821-67F6418B7F9C}.Debug|Win32.ActiveCfg = Debug|Win32 - {BD600973-C6FA-4CE3-8821-67F6418B7F9C}.Debug|Win32.Build.0 = Debug|Win32 - {BD600973-C6FA-4CE3-8821-67F6418B7F9C}.Debug|x64.ActiveCfg = Debug|Win32 - {BD600973-C6FA-4CE3-8821-67F6418B7F9C}.DebugAD|Win32.ActiveCfg = Debug|Win32 - {BD600973-C6FA-4CE3-8821-67F6418B7F9C}.DebugAD|Win32.Build.0 = Debug|Win32 - {BD600973-C6FA-4CE3-8821-67F6418B7F9C}.DebugAD|x64.ActiveCfg = Debug|Win32 - {BD600973-C6FA-4CE3-8821-67F6418B7F9C}.DebugAS|Win32.ActiveCfg = Debug|Win32 - {BD600973-C6FA-4CE3-8821-67F6418B7F9C}.DebugAS|Win32.Build.0 = Debug|Win32 - {BD600973-C6FA-4CE3-8821-67F6418B7F9C}.DebugAS|x64.ActiveCfg = Debug|Win32 - {BD600973-C6FA-4CE3-8821-67F6418B7F9C}.Release|Win32.ActiveCfg = Release|Win32 - {BD600973-C6FA-4CE3-8821-67F6418B7F9C}.Release|Win32.Build.0 = Release|Win32 - {BD600973-C6FA-4CE3-8821-67F6418B7F9C}.Release|x64.ActiveCfg = Release|Win32 - {BD600973-C6FA-4CE3-8821-67F6418B7F9C}.ReleaseAD|Win32.ActiveCfg = Release|Win32 - {BD600973-C6FA-4CE3-8821-67F6418B7F9C}.ReleaseAD|Win32.Build.0 = Release|Win32 - {BD600973-C6FA-4CE3-8821-67F6418B7F9C}.ReleaseAD|x64.ActiveCfg = Release|Win32 - {BD600973-C6FA-4CE3-8821-67F6418B7F9C}.ReleaseAS|Win32.ActiveCfg = Release|Win32 - {BD600973-C6FA-4CE3-8821-67F6418B7F9C}.ReleaseAS|Win32.Build.0 = Release|Win32 - {BD600973-C6FA-4CE3-8821-67F6418B7F9C}.ReleaseAS|x64.ActiveCfg = Release|Win32 - {78424708-1F6E-4D4B-920C-FB6D26847055}.Debug|Win32.ActiveCfg = DebugAS|x64 - {78424708-1F6E-4D4B-920C-FB6D26847055}.Debug|x64.ActiveCfg = DebugAS|x64 - {78424708-1F6E-4D4B-920C-FB6D26847055}.Debug|x64.Build.0 = DebugAS|x64 + {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.Release|x64.ActiveCfg = Release|x64 + {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.Release|x64.Build.0 = Release|x64 + {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.ReleaseAD|Win32.ActiveCfg = Release|x64 + {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.ReleaseAD|x64.ActiveCfg = Release|x64 + {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.ReleaseAD|x64.Build.0 = Release|x64 + {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.ReleaseAS|Win32.ActiveCfg = Release|x64 + {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.ReleaseAS|x64.ActiveCfg = Release|x64 + {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.ReleaseAS|x64.Build.0 = Release|x64 + {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.ReleaseUD|Win32.ActiveCfg = Release|x64 + {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.ReleaseUD|x64.ActiveCfg = Release|x64 + {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.ReleaseUD|x64.Build.0 = Release|x64 + {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.ReleaseUS|Win32.ActiveCfg = Release|x64 + {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.ReleaseUS|x64.ActiveCfg = Release|x64 + {AA561A7B-26EA-49AF-90E8-C53C1FA2965D}.ReleaseUS|x64.Build.0 = Release|x64 + {78424708-1F6E-4D4B-920C-FB6D26847055}.Debug|Win32.ActiveCfg = DebugUS|x64 + {78424708-1F6E-4D4B-920C-FB6D26847055}.Debug|x64.ActiveCfg = DebugUS|x64 + {78424708-1F6E-4D4B-920C-FB6D26847055}.Debug|x64.Build.0 = DebugUS|x64 {78424708-1F6E-4D4B-920C-FB6D26847055}.DebugAD|Win32.ActiveCfg = DebugAD|Win32 {78424708-1F6E-4D4B-920C-FB6D26847055}.DebugAD|Win32.Build.0 = DebugAD|Win32 {78424708-1F6E-4D4B-920C-FB6D26847055}.DebugAD|x64.ActiveCfg = DebugAD|x64 @@ -92,9 +74,17 @@ Global {78424708-1F6E-4D4B-920C-FB6D26847055}.DebugAS|Win32.Build.0 = DebugAS|Win32 {78424708-1F6E-4D4B-920C-FB6D26847055}.DebugAS|x64.ActiveCfg = DebugAS|x64 {78424708-1F6E-4D4B-920C-FB6D26847055}.DebugAS|x64.Build.0 = DebugAS|x64 - {78424708-1F6E-4D4B-920C-FB6D26847055}.Release|Win32.ActiveCfg = ReleaseAS|x64 - {78424708-1F6E-4D4B-920C-FB6D26847055}.Release|x64.ActiveCfg = ReleaseAS|x64 - {78424708-1F6E-4D4B-920C-FB6D26847055}.Release|x64.Build.0 = ReleaseAS|x64 + {78424708-1F6E-4D4B-920C-FB6D26847055}.DebugUD|Win32.ActiveCfg = DebugUD|Win32 + {78424708-1F6E-4D4B-920C-FB6D26847055}.DebugUD|Win32.Build.0 = DebugUD|Win32 + {78424708-1F6E-4D4B-920C-FB6D26847055}.DebugUD|x64.ActiveCfg = DebugUD|x64 + {78424708-1F6E-4D4B-920C-FB6D26847055}.DebugUD|x64.Build.0 = DebugUD|x64 + {78424708-1F6E-4D4B-920C-FB6D26847055}.DebugUS|Win32.ActiveCfg = DebugUS|Win32 + {78424708-1F6E-4D4B-920C-FB6D26847055}.DebugUS|Win32.Build.0 = DebugUS|Win32 + {78424708-1F6E-4D4B-920C-FB6D26847055}.DebugUS|x64.ActiveCfg = DebugUS|x64 + {78424708-1F6E-4D4B-920C-FB6D26847055}.DebugUS|x64.Build.0 = DebugUS|x64 + {78424708-1F6E-4D4B-920C-FB6D26847055}.Release|Win32.ActiveCfg = ReleaseUS|x64 + {78424708-1F6E-4D4B-920C-FB6D26847055}.Release|x64.ActiveCfg = ReleaseUS|x64 + {78424708-1F6E-4D4B-920C-FB6D26847055}.Release|x64.Build.0 = ReleaseUS|x64 {78424708-1F6E-4D4B-920C-FB6D26847055}.ReleaseAD|Win32.ActiveCfg = ReleaseAD|Win32 {78424708-1F6E-4D4B-920C-FB6D26847055}.ReleaseAD|Win32.Build.0 = ReleaseAD|Win32 {78424708-1F6E-4D4B-920C-FB6D26847055}.ReleaseAD|x64.ActiveCfg = ReleaseAD|x64 @@ -103,6 +93,46 @@ Global {78424708-1F6E-4D4B-920C-FB6D26847055}.ReleaseAS|Win32.Build.0 = ReleaseAS|Win32 {78424708-1F6E-4D4B-920C-FB6D26847055}.ReleaseAS|x64.ActiveCfg = ReleaseAS|x64 {78424708-1F6E-4D4B-920C-FB6D26847055}.ReleaseAS|x64.Build.0 = ReleaseAS|x64 + {78424708-1F6E-4D4B-920C-FB6D26847055}.ReleaseUD|Win32.ActiveCfg = ReleaseUD|Win32 + {78424708-1F6E-4D4B-920C-FB6D26847055}.ReleaseUD|Win32.Build.0 = ReleaseUD|Win32 + {78424708-1F6E-4D4B-920C-FB6D26847055}.ReleaseUD|x64.ActiveCfg = ReleaseUD|x64 + {78424708-1F6E-4D4B-920C-FB6D26847055}.ReleaseUD|x64.Build.0 = ReleaseUD|x64 + {78424708-1F6E-4D4B-920C-FB6D26847055}.ReleaseUS|Win32.ActiveCfg = ReleaseUS|Win32 + {78424708-1F6E-4D4B-920C-FB6D26847055}.ReleaseUS|Win32.Build.0 = ReleaseUS|Win32 + {78424708-1F6E-4D4B-920C-FB6D26847055}.ReleaseUS|x64.ActiveCfg = ReleaseUS|x64 + {78424708-1F6E-4D4B-920C-FB6D26847055}.ReleaseUS|x64.Build.0 = ReleaseUS|x64 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.Debug|Win32.ActiveCfg = Debug|Win32 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.Debug|Win32.Build.0 = Debug|Win32 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.Debug|x64.ActiveCfg = Debug|x64 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.Debug|x64.Build.0 = Debug|x64 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.DebugAD|Win32.ActiveCfg = Debug|x64 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.DebugAD|x64.ActiveCfg = Debug|x64 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.DebugAD|x64.Build.0 = Debug|x64 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.DebugAS|Win32.ActiveCfg = Debug|x64 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.DebugAS|x64.ActiveCfg = Debug|x64 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.DebugAS|x64.Build.0 = Debug|x64 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.DebugUD|Win32.ActiveCfg = Debug|x64 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.DebugUD|x64.ActiveCfg = Debug|x64 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.DebugUD|x64.Build.0 = Debug|x64 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.DebugUS|Win32.ActiveCfg = Debug|x64 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.DebugUS|x64.ActiveCfg = Debug|x64 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.DebugUS|x64.Build.0 = Debug|x64 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.Release|Win32.ActiveCfg = Release|Win32 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.Release|Win32.Build.0 = Release|Win32 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.Release|x64.ActiveCfg = Release|x64 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.Release|x64.Build.0 = Release|x64 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.ReleaseAD|Win32.ActiveCfg = Release|x64 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.ReleaseAD|x64.ActiveCfg = Release|x64 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.ReleaseAD|x64.Build.0 = Release|x64 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.ReleaseAS|Win32.ActiveCfg = Release|x64 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.ReleaseAS|x64.ActiveCfg = Release|x64 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.ReleaseAS|x64.Build.0 = Release|x64 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.ReleaseUD|Win32.ActiveCfg = Release|x64 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.ReleaseUD|x64.ActiveCfg = Release|x64 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.ReleaseUD|x64.Build.0 = Release|x64 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.ReleaseUS|Win32.ActiveCfg = Release|x64 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.ReleaseUS|x64.ActiveCfg = Release|x64 + {CB385198-50B1-4CF4-883B-11F042DED6AA}.ReleaseUS|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/dep/StormLib/StormLib.vcproj b/dep/StormLib/StormLib.vcproj index e34ea601642..cccd638d3b5 100644 --- a/dep/StormLib/StormLib.vcproj +++ b/dep/StormLib/StormLib.vcproj @@ -298,6 +298,7 @@ /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -527,45 +1035,1359 @@ Name="Doc Files" > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -574,6 +2396,8 @@ > @@ -582,6 +2406,8 @@ > @@ -590,6 +2416,8 @@ > @@ -598,6 +2426,8 @@ > @@ -606,6 +2436,8 @@ > @@ -614,6 +2446,8 @@ > @@ -622,86 +2456,102 @@ > - - @@ -710,6 +2560,8 @@ > @@ -718,6 +2570,8 @@ > @@ -726,6 +2580,8 @@ > @@ -734,6 +2590,8 @@ > @@ -742,6 +2600,8 @@ > @@ -750,6 +2610,8 @@ > @@ -758,86 +2620,102 @@ > - - @@ -846,6 +2724,8 @@ > @@ -854,6 +2734,8 @@ > @@ -862,6 +2744,8 @@ > @@ -870,6 +2754,8 @@ > @@ -878,6 +2764,8 @@ > @@ -886,6 +2774,8 @@ > @@ -894,86 +2784,102 @@ > - - @@ -982,6 +2888,8 @@ > @@ -990,6 +2898,8 @@ > @@ -998,6 +2908,8 @@ > @@ -1006,6 +2918,8 @@ > @@ -1014,6 +2928,8 @@ > @@ -1022,6 +2938,8 @@ > @@ -1030,86 +2948,102 @@ > - - @@ -1118,6 +3052,8 @@ > @@ -1126,6 +3062,8 @@ > @@ -1134,6 +3072,8 @@ > @@ -1142,6 +3082,8 @@ > @@ -1150,6 +3092,8 @@ > @@ -1158,6 +3102,8 @@ > @@ -1166,86 +3112,102 @@ > - - @@ -1254,6 +3216,8 @@ > @@ -1262,6 +3226,8 @@ > @@ -1270,6 +3236,8 @@ > @@ -1278,6 +3246,8 @@ > @@ -1286,6 +3256,8 @@ > @@ -1294,6 +3266,8 @@ > @@ -1302,86 +3276,102 @@ > - - @@ -1390,6 +3380,8 @@ > @@ -1398,6 +3390,8 @@ > @@ -1406,6 +3400,8 @@ > @@ -1414,6 +3410,8 @@ > @@ -1422,6 +3420,8 @@ > @@ -1430,6 +3430,8 @@ > @@ -1438,78 +3440,88 @@ > - - - - @@ -1522,6 +3534,8 @@ > @@ -1530,6 +3544,8 @@ > @@ -1538,6 +3554,8 @@ > @@ -1546,6 +3564,8 @@ > @@ -1554,6 +3574,8 @@ > @@ -1562,6 +3584,8 @@ > @@ -1570,6 +3594,8 @@ > @@ -1578,6 +3604,88 @@ > + + + + + + + + + + + + + + + + + + + + + + + + @@ -1924,474 +4032,17466 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -173,6 +257,84 @@ Name="VCPostBuildEventTool" /> + + + + + + + + + + + + + + + + + + + + @@ -196,6 +358,10 @@ + + @@ -223,12 +389,8 @@ WarningLevel="4" /> - - + + - - @@ -300,39 +462,35 @@ /> - - - - + + + + - - + + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - @@ -1846,22 +1844,57 @@ + + + + + + + + + + + + + + + 0) { @@ -374,7 +374,7 @@ bool DeleteFile(HashTableEntry *lpHashTable, unsigned long nHashTableSize, Block } else memset(&lpBlockTable[iFileBlockEntry], 0, sizeof(BlockTableEntry); - + return true; } diff --git a/dep/StormLib/doc/d3-authenticationcode-deDE.txt b/dep/StormLib/doc/d3-authenticationcode-deDE.txt new file mode 100644 index 00000000000..cac66712af0 --- /dev/null +++ b/dep/StormLib/doc/d3-authenticationcode-deDE.txt @@ -0,0 +1 @@ +UCMXF6EJY352EFH4XFRXCFH2XC9MQRZK \ No newline at end of file diff --git a/dep/StormLib/doc/d3-authenticationcode-enGB.txt b/dep/StormLib/doc/d3-authenticationcode-enGB.txt new file mode 100644 index 00000000000..2bc9c83859d --- /dev/null +++ b/dep/StormLib/doc/d3-authenticationcode-enGB.txt @@ -0,0 +1 @@ +MMKVHY48RP7WXP4GHYBQ7SL9J9UNPHBP \ No newline at end of file diff --git a/dep/StormLib/doc/d3-authenticationcode-enSG.txt b/dep/StormLib/doc/d3-authenticationcode-enSG.txt new file mode 100644 index 00000000000..e6f1ec29609 --- /dev/null +++ b/dep/StormLib/doc/d3-authenticationcode-enSG.txt @@ -0,0 +1 @@ +8MXLWHQ7VGGLTZ9MQZQSFDCLJYET3CPP \ No newline at end of file diff --git a/dep/StormLib/doc/d3-authenticationcode-enUS.txt b/dep/StormLib/doc/d3-authenticationcode-enUS.txt new file mode 100644 index 00000000000..8d73e61def9 --- /dev/null +++ b/dep/StormLib/doc/d3-authenticationcode-enUS.txt @@ -0,0 +1 @@ +EJ2R5TM6XFE2GUNG5QDGHKQ9UAKPWZSZ \ No newline at end of file diff --git a/dep/StormLib/doc/d3-authenticationcode-esES.txt b/dep/StormLib/doc/d3-authenticationcode-esES.txt new file mode 100644 index 00000000000..6b1b0a1b811 --- /dev/null +++ b/dep/StormLib/doc/d3-authenticationcode-esES.txt @@ -0,0 +1 @@ +PBGFBE42Z6LNK65UGJQ3WZVMCLP4HQQT \ No newline at end of file diff --git a/dep/StormLib/doc/d3-authenticationcode-esMX.txt b/dep/StormLib/doc/d3-authenticationcode-esMX.txt new file mode 100644 index 00000000000..504759e8d55 --- /dev/null +++ b/dep/StormLib/doc/d3-authenticationcode-esMX.txt @@ -0,0 +1 @@ +X7SEJJS9TSGCW5P28EBSC47AJPEY8VU2 \ No newline at end of file diff --git a/dep/StormLib/doc/d3-authenticationcode-frFR.txt b/dep/StormLib/doc/d3-authenticationcode-frFR.txt new file mode 100644 index 00000000000..bb35a2bfdc6 --- /dev/null +++ b/dep/StormLib/doc/d3-authenticationcode-frFR.txt @@ -0,0 +1 @@ +5KVBQA8VYE6XRY3DLGC5ZDE4XS4P7YA2 \ No newline at end of file diff --git a/dep/StormLib/doc/d3-authenticationcode-itIT.txt b/dep/StormLib/doc/d3-authenticationcode-itIT.txt new file mode 100644 index 00000000000..a62031d3883 --- /dev/null +++ b/dep/StormLib/doc/d3-authenticationcode-itIT.txt @@ -0,0 +1 @@ +478JD2K56EVNVVY4XX8TDWYT5B8KB254 \ No newline at end of file diff --git a/dep/StormLib/doc/d3-authenticationcode-koKR.txt b/dep/StormLib/doc/d3-authenticationcode-koKR.txt new file mode 100644 index 00000000000..296ffcc9450 --- /dev/null +++ b/dep/StormLib/doc/d3-authenticationcode-koKR.txt @@ -0,0 +1 @@ +8TS4VNFQRZTN6YWHE9CHVDH9NVWD474A \ No newline at end of file diff --git a/dep/StormLib/doc/d3-authenticationcode-plPL.txt b/dep/StormLib/doc/d3-authenticationcode-plPL.txt new file mode 100644 index 00000000000..a92563c1815 --- /dev/null +++ b/dep/StormLib/doc/d3-authenticationcode-plPL.txt @@ -0,0 +1 @@ +LJ52Z32DF4LZ4ZJJXVKK3AZQA6GABLJB \ No newline at end of file diff --git a/dep/StormLib/doc/d3-authenticationcode-ptBR.txt b/dep/StormLib/doc/d3-authenticationcode-ptBR.txt new file mode 100644 index 00000000000..e6e5c3568d9 --- /dev/null +++ b/dep/StormLib/doc/d3-authenticationcode-ptBR.txt @@ -0,0 +1 @@ +K6BDHY2ECUE2545YKNLBJPVYWHE7XYAG \ No newline at end of file diff --git a/dep/StormLib/doc/d3-authenticationcode-zhTW.txt b/dep/StormLib/doc/d3-authenticationcode-zhTW.txt new file mode 100644 index 00000000000..138a5449c7a --- /dev/null +++ b/dep/StormLib/doc/d3-authenticationcode-zhTW.txt @@ -0,0 +1 @@ +6VWCQTN8V3ZZMRUCZXV8A8CGUX2TAA8H \ No newline at end of file diff --git a/dep/StormLib/doc/diablo3_ruru_disk_encrypted_win.blob b/dep/StormLib/doc/diablo3_ruru_disk_encrypted_win.blob new file mode 100644 index 00000000000..7972b2d7636 --- /dev/null +++ b/dep/StormLib/doc/diablo3_ruru_disk_encrypted_win.blob @@ -0,0 +1,49 @@ +{ +"config":{ + "product": "D3", + "install_progress_percent": 70.0, + "install_progress_info": [8000000000.0, 0.0, 0.0], + "download_progress_info": [10000000.0, 500000000.0, 500000000.0], + "install_progress_speed": 5000000.0, + "tome_download_progress_percent": 0.0, + "updater_product": "d3_patch", + "expansion_level": 0, + "ptr": false, + "beta": false, + "update_method": "patch on demand", + "supports_multibox": false, + "data_dir": "Data_D3/PC/MPQs/", + "patch_url": "http://ruRU.patch.battle.net:1119/patch", + "update_regex": "(?Pd3-update-(?P\\w+))-(?P\\d+)\\.mpq$", + "update_identifier": "d3-update-", + "torrent_file_path": "Diablo III.tfil", + "manifest_file_path": "Diablo III.mfil", + "priority_file_path": "Diablo III.pfil", + "priority_file_layout": "Retail", + "binary_version_path": "Diablo III.exe", + "binary_launch_path": "Diablo III.exe", + "display_locales":["ruRU"], + "supported_locales" : ["enUS", "esMX", "ptBR", "enGB", "deDE", "esES", "frFR", "itIT", "plPL", "enSG", "ptPT", "ruRU", "koKR", "zhTW", "zhCN"], + "launch_arguments":["-launch","-uid","diablo3_ruru"], + "form": { + "eula": { + "eula":false + }, + "game_dir": { + "default": "Program Files", + "dirname": "Diablo III", + "required_space": 15032385536 + }, + "language": { + "default":["ruRU"], + "list":["ruRU"] + }, + "authentication_key": { + "url": [ + "http://ruru.nydus.battle.net/D3/ruRU/setup/mediakey", + "http://dist.blizzard.com/mediakey/d3-authenticationcode-ruRU.txt" + ] + } + } + } +} \ No newline at end of file diff --git a/dep/StormLib/doc/diablo3_urls.txt b/dep/StormLib/doc/diablo3_urls.txt new file mode 100644 index 00000000000..1fbb6e0b5f3 --- /dev/null +++ b/dep/StormLib/doc/diablo3_urls.txt @@ -0,0 +1,14 @@ +shttp://dist.blizzard.com/mediakey/d3-authenticationcode-deDE.txt +http://dist.blizzard.com/mediakey/d3-authenticationcode-enGB.txt +http://dist.blizzard.com/mediakey/d3-authenticationcode-enSG.txt +http://dist.blizzard.com/mediakey/d3-authenticationcode-enUS.txt +http://dist.blizzard.com/mediakey/d3-authenticationcode-esES.txt +http://dist.blizzard.com/mediakey/d3-authenticationcode-esMX.txt +http://dist.blizzard.com/mediakey/d3-authenticationcode-frFR.txt +http://dist.blizzard.com/mediakey/d3-authenticationcode-itIT.txt +http://dist.blizzard.com/mediakey/d3-authenticationcode-koKR.txt +http://dist.blizzard.com/mediakey/d3-authenticationcode-plPL.txt +http://dist.blizzard.com/mediakey/d3-authenticationcode-ptBR.txt +http://dist.blizzard.com/mediakey/d3-authenticationcode-ruRU.txt <==== +http://dist.blizzard.com/mediakey/d3-authenticationcode-zhTW.txt +http://dist.blizzard.com/mediakey/d3-authenticationcode-zhCN.txt <==== diff --git a/dep/StormLib/src/FileStream.cpp b/dep/StormLib/src/FileStream.cpp index 8c75abb3ec7..620373d9ea7 100644 --- a/dep/StormLib/src/FileStream.cpp +++ b/dep/StormLib/src/FileStream.cpp @@ -16,6 +16,11 @@ #define __STORMLIB_SELF__ #include "StormLib.h" #include "StormCommon.h" +#include "FileStream.h" + +#ifdef _MSC_VER +#pragma comment(lib, "wininet.lib") +#endif //----------------------------------------------------------------------------- // Local defines @@ -25,60 +30,11 @@ #endif #ifdef _MSC_VER -#pragma warning(disable: 4800) // 'BOOL' : forcing value to bool 'true' or 'false' (performance warning) +#pragma warning(disable: 4800) // 'BOOL' : forcing value to bool 'true' or 'false' (performance warning) #endif //----------------------------------------------------------------------------- -// Local structures - -// Structure describing the PART file header -typedef struct _PART_FILE_HEADER -{ - DWORD PartialVersion; // Always set to 2 - char GameBuildNumber[8]; // Minimum build number of the game that can use this MPQ - DWORD Unknown0C; - DWORD Unknown10; - DWORD Unknown14; // Seems to contain 0x1C, which is the size of the rest of the header - DWORD Unknown18; - DWORD Unknown1C; - DWORD Unknown20; - DWORD ZeroValue; // Seems to always be zero - DWORD FileSizeLo; // Low 32 bits of the file size - DWORD FileSizeHi; // High 32 bits of the file size - DWORD PartSize; // Size of one file part, in bytes - -} PART_FILE_HEADER, *PPART_FILE_HEADER; - -// Structure describing the block-to-file map entry -typedef struct _PART_FILE_MAP_ENTRY -{ - DWORD Flags; // 3 = the block is present in the file - DWORD BlockOffsLo; // Low 32 bits of the block position in the file - DWORD BlockOffsHi; // High 32 bits of the block position in the file - DWORD Unknown0C; - DWORD Unknown10; - -} PART_FILE_MAP_ENTRY, *PPART_FILE_MAP_ENTRY; - -struct TPartFileStream : public TFileStream -{ - ULONGLONG VirtualSize; // Virtual size of the file - ULONGLONG VirtualPos; // Virtual position in the file - DWORD PartCount; // Number of file parts. Used by partial file stream - DWORD PartSize; // Size of one part. Used by partial file stream - - PART_FILE_MAP_ENTRY PartMap[1]; // File map, variable length -}; - -#define MPQE_CHUNK_SIZE 0x40 // Size of one chunk to be decrypted - -struct TEncryptedStream : public TFileStream -{ - BYTE Key[MPQE_CHUNK_SIZE]; // File key -}; - -//----------------------------------------------------------------------------- -// Non-Windows support for LastError +// Local functions - platform-specific functions #ifndef PLATFORM_WINDOWS static int nLastError = ERROR_SUCCESS; @@ -94,407 +50,149 @@ void SetLastError(int nError) } #endif -//----------------------------------------------------------------------------- -// Local functions - platform-specific functions - #ifndef PLATFORM_LITTLE_ENDIAN void ConvertPartHeader(void * partHeader) { PPART_FILE_HEADER theHeader = (PPART_FILE_HEADER)partHeader; theHeader->PartialVersion = SwapUInt32(theHeader->PartialVersion); - theHeader->Unknown0C = SwapUInt32(theHeader->Unknown0C); - theHeader->Unknown10 = SwapUInt32(theHeader->Unknown10); - theHeader->Unknown14 = SwapUInt32(theHeader->Unknown14); - theHeader->Unknown18 = SwapUInt32(theHeader->Unknown18); - theHeader->Unknown1C = SwapUInt32(theHeader->Unknown1C); - theHeader->Unknown20 = SwapUInt32(theHeader->Unknown20); - theHeader->ZeroValue = SwapUInt32(theHeader->ZeroValue); + theHeader->Flags = SwapUInt32(theHeader->Flags); theHeader->FileSizeLo = SwapUInt32(theHeader->FileSizeLo); theHeader->FileSizeHi = SwapUInt32(theHeader->FileSizeHi); - theHeader->PartSize = SwapUInt32(theHeader->PartSize); + theHeader->BlockSize = SwapUInt32(theHeader->BlockSize); } #endif -#ifdef PLATFORM_MAC -static void ConvertUTCDateTimeToFileTime(const UTCDateTimePtr inTime, ULONGLONG * pFT) -{ - UInt64 intTime = ((UInt64)inTime->highSeconds << 32) + inTime->lowSeconds; - intTime *= 10000000; - intTime += 0x0153b281e0fb4000ull; - - *pFT = intTime; -} - -static OSErr FSOpenDFCompat(FSRef *ref, char permission, short *refNum) -{ - HFSUniStr255 forkName; - OSErr theErr; - Boolean isFolder, wasChanged; - - theErr = FSResolveAliasFile(ref, true, &isFolder, &wasChanged); - if (theErr != noErr) - { - return theErr; - } - - FSGetDataForkName(&forkName); -#ifdef PLATFORM_64BIT - theErr = FSOpenFork(ref, forkName.length, forkName.unicode, permission, (FSIORefNum *)refNum); -#else - theErr = FSOpenFork(ref, forkName.length, forkName.unicode, permission, refNum); -#endif - return theErr; -} -#endif +//----------------------------------------------------------------------------- +// Preparing file bitmap for a complete file of a given size -#ifdef PLATFORM_LINUX -// time_t is number of seconds since 1.1.1970, UTC. -// 1 second = 10000000 (decimal) in FILETIME -static void ConvertTimeTToFileTime(ULONGLONG * pFileTime, time_t crt_time) -{ - // Set the start to 1.1.1970 00:00:00 - *pFileTime = 0x019DB1DED53E8000ULL + (10000000 * crt_time); -} -#endif +#define DEFAULT_BLOCK_SIZE 0x4000 -static HANDLE CreateNewFile( - const char * szFileName) // Name of the file to open +static bool Dummy_GetBitmap( + TFileStream * pStream, + TFileBitmap * pBitmap, + DWORD Length, + LPDWORD LengthNeeded) { - HANDLE hFile = INVALID_HANDLE_VALUE; // Pre-set the file handle to INVALID_HANDLE_VALUE - -#ifdef PLATFORM_WINDOWS - { - DWORD dwShareMode = FILE_SHARE_READ; - - if(dwGlobalFlags & SFILE_FLAG_ALLOW_WRITE_SHARE) - dwShareMode |= FILE_SHARE_WRITE; - - hFile = CreateFile(szFileName, - GENERIC_READ | GENERIC_WRITE, - dwShareMode, - NULL, - CREATE_ALWAYS, - 0, - NULL); - } -#endif - -#ifdef PLATFORM_MAC - { - FSRef theParentRef; - FSRef theFileRef; - OSErr theErr; - short fileRef; - - theErr = FSPathMakeRef((const UInt8 *)szFileName, &theFileRef, NULL); - - if (theErr == noErr) - FSDeleteObject(&theFileRef); - - // Create the FSRef for the parent directory. - UInt8 folderName[MAX_PATH]; - memset(&theFileRef, 0, sizeof(FSRef)); - CFStringRef filePathCFString = CFStringCreateWithCString(NULL, szFileName, kCFStringEncodingUTF8); - CFURLRef fileURL = CFURLCreateWithFileSystemPath(NULL, filePathCFString, kCFURLPOSIXPathStyle, false); - CFURLRef folderURL = CFURLCreateCopyDeletingLastPathComponent(NULL, fileURL); - CFURLGetFileSystemRepresentation(folderURL, true, folderName, MAX_PATH); - theErr = FSPathMakeRef(folderName, &theParentRef, NULL); - CFRelease(fileURL); - CFRelease(folderURL); - - if (theErr != noErr) - { - nLastError = theErr; - return INVALID_HANDLE_VALUE; - } - - // Create the file - UniChar unicodeFileName[256]; - fileURL = CFURLCreateWithFileSystemPath(NULL, filePathCFString, kCFURLPOSIXPathStyle, false); - CFStringRef fileNameCFString = CFURLCopyLastPathComponent(fileURL); - CFStringGetCharacters(fileNameCFString, CFRangeMake(0, CFStringGetLength(fileNameCFString)), - unicodeFileName); - theErr = FSCreateFileUnicode(&theParentRef, CFStringGetLength(fileNameCFString), unicodeFileName, - kFSCatInfoNone, NULL, &theFileRef, NULL); - CFRelease(fileNameCFString); - CFRelease(filePathCFString); - CFRelease(fileURL); - if (theErr != noErr) - { - nLastError = theErr; - return INVALID_HANDLE_VALUE; - } - - theErr = FSOpenDFCompat(&theFileRef, fsRdWrPerm, &fileRef); - if(theErr != noErr) - { - nLastError = theErr; - return INVALID_HANDLE_VALUE; - } - - hFile = (HANDLE)(int)fileRef; - } -#endif - -#ifdef PLATFORM_LINUX - { - intptr_t handle; - - handle = open(szFileName, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - if(handle == -1) - { - nLastError = errno; - return INVALID_HANDLE_VALUE; - } - - hFile = (HANDLE)handle; - } -#endif + ULONGLONG FileSize = 0; + DWORD TotalLength; + DWORD BlockCount; + DWORD BitmapSize; + DWORD LastByte; + bool bResult = false; - // Return the file handle - return hFile; -} + // Get file size and calculate bitmap length + FileStream_GetSize(pStream, &FileSize); + BlockCount = (DWORD)(((FileSize - 1) / DEFAULT_BLOCK_SIZE) + 1); + BitmapSize = (DWORD)(((BlockCount - 1) / 8) + 1); -static HANDLE OpenExistingFile( - const char * szFileName, // Name of the file to open - bool bWriteAccess) // false = read-only, true = read/write -{ - HANDLE hFile = INVALID_HANDLE_VALUE; // Pre-set the file handle to INVALID_HANDLE_VALUE + // Calculate and give the total length + TotalLength = sizeof(TFileBitmap) + BitmapSize; + if(LengthNeeded != NULL) + *LengthNeeded = TotalLength; -#ifdef PLATFORM_WINDOWS + // Has the caller given enough space for storing the structure? + if(Length >= sizeof(TFileBitmap)) { - DWORD dwShareMode = FILE_SHARE_READ; - - if(dwGlobalFlags & SFILE_FLAG_ALLOW_WRITE_SHARE) - dwShareMode |= FILE_SHARE_WRITE; - - hFile = CreateFile(szFileName, - bWriteAccess ? (GENERIC_READ | GENERIC_WRITE) : GENERIC_READ, - dwShareMode, - NULL, - OPEN_EXISTING, - 0, - NULL); + memset(pBitmap, 0, sizeof(TFileBitmap)); + pBitmap->EndOffset = FileSize; + pBitmap->IsComplete = 1; + pBitmap->BitmapSize = BitmapSize; + pBitmap->BlockSize = DEFAULT_BLOCK_SIZE; + bResult = true; } -#endif -#ifdef PLATFORM_MAC + // Do we have enough space to fill the bitmap as well? + if(Length >= TotalLength) { - FSRef theFileRef; - OSErr theErr; - short fileRef; - char permission = bWriteAccess ? fsRdWrPerm : fsRdPerm; - - theErr = FSPathMakeRef((const UInt8 *)szFileName, &theFileRef, NULL); - if(theErr != noErr) - { - nLastError = theErr; - return INVALID_HANDLE_VALUE; - } - - theErr = FSOpenDFCompat(&theFileRef, permission, &fileRef); - if (theErr != noErr) - { - nLastError = theErr; - return INVALID_HANDLE_VALUE; - } + LPBYTE pbBitmap = (LPBYTE)(pBitmap + 1); - hFile = (HANDLE)(int)fileRef; - } -#endif - -#ifdef PLATFORM_LINUX - { - int oflag = bWriteAccess ? O_RDWR : O_RDONLY; - intptr_t handle; + // Fill the full blocks + memset(pbBitmap, 0xFF, (BlockCount / 8)); + pbBitmap += (BlockCount / 8); + bResult = true; - handle = open(szFileName, oflag | O_LARGEFILE); - if(handle == -1) + // Supply the last block + if(BlockCount & 7) { - nLastError = errno; - return INVALID_HANDLE_VALUE; + LastByte = (1 << (BlockCount & 7)) - 1; + pbBitmap[0] = (BYTE)LastByte; } - - hFile = (HANDLE)handle; - } -#endif - - // Return the file handle - return hFile; -} - -static void CloseTheFile(HANDLE hFile) -{ -#ifdef PLATFORM_WINDOWS - CloseHandle(hFile); -#endif - -#ifdef PLATFORM_MAC - FSCloseFork((short)(long)hFile); -#endif - -#ifdef PLATFORM_LINUX - close((intptr_t)hFile); -#endif -} - -// Renames a file to another name. -// Note that the "szNewFile" file usually exists when this function is called, -// so the function must deal with it properly -static bool RenameFile(const char * szExistingFile, const char * szNewFile) -{ -#ifdef PLATFORM_WINDOWS - // Delete the original stream file. Don't check the result value, - // because if the file doesn't exist, it would fail - DeleteFile(szNewFile); - - // Rename the new file to the old stream's file - return (bool)MoveFile(szExistingFile, szNewFile); -#endif - -#ifdef PLATFORM_MAC - OSErr theErr; - FSRef fromFileRef; - FSRef toFileRef; - - if (FSPathMakeRef((const UInt8 *)szNewFile, &toFileRef, NULL) == noErr) - FSDeleteObject(&toFileRef); - - // Get the path to the old file - theErr = FSPathMakeRef((const UInt8 *)szExistingFile, &fromFileRef, NULL); - if (theErr != noErr) - { - nLastError = theErr; - return false; - } - - // Get a CFString for the new file name - CFStringRef newFileNameCFString = CFStringCreateWithCString(NULL, szNewFile, kCFStringEncodingUTF8); - CFURLRef fileURL = CFURLCreateWithFileSystemPath(NULL, newFileNameCFString, kCFURLPOSIXPathStyle, false); - CFRelease(newFileNameCFString); - newFileNameCFString = CFURLCopyLastPathComponent(fileURL); - CFRelease(fileURL); - - // Convert CFString to Unicode and rename the file - UniChar unicodeFileName[256]; - CFStringGetCharacters(newFileNameCFString, CFRangeMake(0, CFStringGetLength(newFileNameCFString)), - unicodeFileName); - theErr = FSRenameUnicode(&fromFileRef, CFStringGetLength(newFileNameCFString), unicodeFileName, - kTextEncodingUnknown, NULL); - if (theErr != noErr) - { - CFRelease(newFileNameCFString); - nLastError = theErr; - return false; - } - - CFRelease(newFileNameCFString); - - return true; -#endif - -#ifdef PLATFORM_LINUX - // "rename" on Linux also works if the target file exists - if(rename(szExistingFile, szNewFile) == -1) - { - nLastError = errno; - return false; } - return true; -#endif + return bResult; } //----------------------------------------------------------------------------- -// Stream functions - normal file stream - -static bool File_GetPos( - TFileStream * pStream, // Pointer to an open stream - ULONGLONG & ByteOffset) // Pointer to file byte offset -{ - ByteOffset = pStream->RawFilePos; - return true; -} +// Local functions - base file support -static bool File_Read( - TFileStream * pStream, // Pointer to an open stream - ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position - void * pvBuffer, // Pointer to data to be read - DWORD dwBytesToRead) // Number of bytes to read from the file +static bool BaseFile_Read( + TFileStream * pStream, // Pointer to an open stream + ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position + void * pvBuffer, // Pointer to data to be read + DWORD dwBytesToRead) // Number of bytes to read from the file { + ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.File.FilePos; DWORD dwBytesRead = 0; // Must be set by platform-specific code - // If the byte offset is not entered, use the current position - if(pByteOffset == NULL) - pByteOffset = &pStream->RawFilePos; - #ifdef PLATFORM_WINDOWS { - // If the byte offset is different from the current file position, - // we have to update the file position - if(*pByteOffset != pStream->RawFilePos) - { - LONG ByteOffsetHi = (LONG)(*pByteOffset >> 32); - LONG ByteOffsetLo = (LONG)(*pByteOffset); + // Note: StormLib no longer supports Windows 9x. + // Thus, we can use the OVERLAPPED structure to specify + // file offset to read from file. This allows us to skip + // one system call to SetFilePointer - SetFilePointer(pStream->hFile, ByteOffsetLo, &ByteOffsetHi, FILE_BEGIN); - pStream->RawFilePos = *pByteOffset; - } + // Update the byte offset + pStream->Base.File.FilePos = ByteOffset; // Read the data if(dwBytesToRead != 0) { - if(!ReadFile(pStream->hFile, pvBuffer, dwBytesToRead, &dwBytesRead, NULL)) + OVERLAPPED Overlapped; + + Overlapped.OffsetHigh = (DWORD)(ByteOffset >> 32); + Overlapped.Offset = (DWORD)ByteOffset; + Overlapped.hEvent = NULL; + if(!ReadFile(pStream->Base.File.hFile, pvBuffer, dwBytesToRead, &dwBytesRead, &Overlapped)) return false; } - } -#endif - -#ifdef PLATFORM_MAC - { - ByteCount nBytesToRead = (ByteCount)dwBytesToRead; - ByteCount nBytesRead = 0; - OSErr theErr; - + /* // If the byte offset is different from the current file position, // we have to update the file position - if(*pByteOffset != pStream->RawFilePos) + if(ByteOffset != pStream->Base.File.FilePos) { - FSSetForkPosition((short)(long)pStream->hFile, fsFromStart, (SInt64)(*pByteOffset)); - pStream->RawFilePos = *pByteOffset; + LONG ByteOffsetHi = (LONG)(ByteOffset >> 32); + + SetFilePointer(pStream->Base.File.hFile, (LONG)ByteOffset, &ByteOffsetHi, FILE_BEGIN); + pStream->Base.File.FilePos = ByteOffset; } // Read the data - if(nBytesToRead != 0) + if(dwBytesToRead != 0) { - theErr = FSReadFork((short)(long)pStream->hFile, fsAtMark, 0, nBytesToRead, pvBuffer, &nBytesRead); - if (theErr != noErr && theErr != eofErr) - { - nLastError = theErr; + if(!ReadFile(pStream->Base.File.hFile, pvBuffer, dwBytesToRead, &dwBytesRead, NULL)) return false; - } - dwBytesRead = (DWORD)nBytesRead; } +*/ } #endif -#ifdef PLATFORM_LINUX +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) { ssize_t bytes_read; // If the byte offset is different from the current file position, // we have to update the file position - if(*pByteOffset != pStream->RawFilePos) + if(ByteOffset != pStream->Base.File.FilePos) { - lseek64((intptr_t)pStream->hFile, (off64_t)(*pByteOffset), SEEK_SET); - pStream->RawFilePos = *pByteOffset; + lseek((intptr_t)pStream->Base.File.hFile, (off_t)(ByteOffset), SEEK_SET); + pStream->Base.File.FilePos = ByteOffset; } // Perform the read operation if(dwBytesToRead != 0) { - bytes_read = read((intptr_t)pStream->hFile, pvBuffer, (size_t)dwBytesToRead); + bytes_read = read((intptr_t)pStream->Base.File.hFile, pvBuffer, (size_t)dwBytesToRead); if(bytes_read == -1) { nLastError = errno; @@ -504,85 +202,84 @@ static bool File_Read( dwBytesRead = (DWORD)(size_t)bytes_read; } } -#endif +#endif // Increment the current file position by number of bytes read // If the number of bytes read doesn't match to required amount, return false - pStream->RawFilePos = *pByteOffset + dwBytesRead; + pStream->Base.File.FilePos = ByteOffset + dwBytesRead; if(dwBytesRead != dwBytesToRead) SetLastError(ERROR_HANDLE_EOF); return (dwBytesRead == dwBytesToRead); } -static bool File_Write( - TFileStream * pStream, // Pointer to an open stream - ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it writes to current position - const void * pvBuffer, // Pointer to data to be written - DWORD dwBytesToWrite) // Number of bytes to write to the file +/** + * \a pStream Pointer to an open stream + * \a pByteOffset Pointer to file byte offset. If NULL, writes to current position + * \a pvBuffer Pointer to data to be written + * \a dwBytesToWrite Number of bytes to write to the file + */ + +static bool BaseFile_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const void * pvBuffer, DWORD dwBytesToWrite) { + ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.File.FilePos; DWORD dwBytesWritten = 0; // Must be set by platform-specific code - // If the byte offset is not entered, use the current position - if(pByteOffset == NULL) - pByteOffset = &pStream->RawFilePos; - #ifdef PLATFORM_WINDOWS { - // If the byte offset is different from the current file position, - // we have to update the file position - if(*pByteOffset != pStream->RawFilePos) - { - LONG ByteOffsetHi = (LONG)(*pByteOffset >> 32); - LONG ByteOffsetLo = (LONG)(*pByteOffset); + // Note: StormLib no longer supports Windows 9x. + // Thus, we can use the OVERLAPPED structure to specify + // file offset to read from file. This allows us to skip + // one system call to SetFilePointer - SetFilePointer(pStream->hFile, ByteOffsetLo, &ByteOffsetHi, FILE_BEGIN); - pStream->RawFilePos = *pByteOffset; - } + // Update the byte offset + pStream->Base.File.FilePos = ByteOffset; // Read the data - if(!WriteFile(pStream->hFile, pvBuffer, dwBytesToWrite, &dwBytesWritten, NULL)) - return false; - } -#endif - -#ifdef PLATFORM_MAC - { - ByteCount nBytesToWrite = (ByteCount)dwBytesToWrite; - ByteCount nBytesWritten = 0; - OSErr theErr; + if(dwBytesToWrite != 0) + { + OVERLAPPED Overlapped; + Overlapped.OffsetHigh = (DWORD)(ByteOffset >> 32); + Overlapped.Offset = (DWORD)ByteOffset; + Overlapped.hEvent = NULL; + if(!WriteFile(pStream->Base.File.hFile, pvBuffer, dwBytesToWrite, &dwBytesWritten, &Overlapped)) + return false; + } + /* // If the byte offset is different from the current file position, // we have to update the file position - if(*pByteOffset != pStream->RawFilePos) + if(ByteOffset != pStream->Base.File.FilePos) { - FSSetForkPosition((short)(long)pStream->hFile, fsFromStart, (SInt64)(*pByteOffset)); - pStream->RawFilePos = *pByteOffset; + LONG ByteOffsetHi = (LONG)(ByteOffset >> 32); + + SetFilePointer(pStream->Base.File.hFile, (LONG)ByteOffset, &ByteOffsetHi, FILE_BEGIN); + pStream->Base.File.FilePos = ByteOffset; } - theErr = FSWriteFork((short)(long)pStream->hFile, fsAtMark, 0, nBytesToWrite, pvBuffer, &nBytesWritten); - if (theErr != noErr) + // Read the data + if(dwBytesToWrite != 0) { - nLastError = theErr; + if(!WriteFile(pStream->Base.File.hFile, pvBuffer, dwBytesToWrite, &dwBytesWritten, NULL)) return false; - } - dwBytesWritten = (DWORD)nBytesWritten; + } +*/ } #endif -#ifdef PLATFORM_LINUX +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) { ssize_t bytes_written; // If the byte offset is different from the current file position, // we have to update the file position - if(*pByteOffset != pStream->RawFilePos) + if(ByteOffset != pStream->Base.File.FilePos) { - lseek64((intptr_t)pStream->hFile, (off64_t)(*pByteOffset), SEEK_SET); - pStream->RawFilePos = *pByteOffset; + lseek((intptr_t)pStream->Base.File.hFile, (off_t)(ByteOffset), SEEK_SET); + pStream->Base.File.FilePos = ByteOffset; } // Perform the read operation - bytes_written = write((intptr_t)pStream->hFile, pvBuffer, (size_t)dwBytesToWrite); + bytes_written = write((intptr_t)pStream->Base.File.hFile, pvBuffer, (size_t)dwBytesToWrite); if(bytes_written == -1) { nLastError = errno; @@ -594,102 +291,65 @@ static bool File_Write( #endif // Increment the current file position by number of bytes read - pStream->RawFilePos = *pByteOffset + dwBytesWritten; + pStream->Base.File.FilePos = ByteOffset + dwBytesWritten; + + // Also modify the file size, if needed + if(pStream->Base.File.FilePos > pStream->Base.File.FileSize) + pStream->Base.File.FileSize = pStream->Base.File.FilePos; + if(dwBytesWritten != dwBytesToWrite) SetLastError(ERROR_DISK_FULL); return (dwBytesWritten == dwBytesToWrite); } -static bool File_GetSize( - TFileStream * pStream, // Pointer to an open stream - ULONGLONG & FileSize) // Pointer where to store file size +static bool BaseFile_GetPos( + TFileStream * pStream, // Pointer to an open stream + ULONGLONG * pByteOffset) // Pointer to file byte offset { -#ifdef PLATFORM_WINDOWS - DWORD FileSizeHi = 0; - DWORD FileSizeLo; - - FileSizeLo = GetFileSize(pStream->hFile, &FileSizeHi); - if(FileSizeLo == INVALID_FILE_SIZE && GetLastError() != ERROR_SUCCESS) - return false; - - FileSize = MAKE_OFFSET64(FileSizeHi, FileSizeLo); - return true; -#endif - -#ifdef PLATFORM_MAC - SInt64 fileLength = 0; - OSErr theErr; - - theErr = FSGetForkSize((short)(long)pStream->hFile, &fileLength); - if(theErr != noErr) - { - nLastError = theErr; - return false; - } - - FileSize = (ULONGLONG)fileLength; + *pByteOffset = pStream->Base.File.FilePos; return true; -#endif - -#ifdef PLATFORM_LINUX - struct stat64 fileinfo; - - if(fstat64((intptr_t)pStream->hFile, &fileinfo) == -1) - { - nLastError = errno; - return false; - } +} - FileSize = (ULONGLONG)fileinfo.st_size; +static bool BaseFile_GetSize( + TFileStream * pStream, // Pointer to an open stream + ULONGLONG * pFileSize) // Pointer where to store file size +{ + *pFileSize = pStream->Base.File.FileSize; return true; -#endif } -static bool File_SetSize( - TFileStream * pStream, // Pointer to an open stream - ULONGLONG NewFileSize) // new size of the file +/** + * \a pStream Pointer to an open stream + * \a NewFileSize New size of the file + */ +static bool BaseFile_SetSize(TFileStream * pStream, ULONGLONG NewFileSize) { #ifdef PLATFORM_WINDOWS { LONG FileSizeHi = (LONG)(NewFileSize >> 32); - LONG FileSizeLo = (LONG)(NewFileSize); + LONG FileSizeLo; DWORD dwNewPos; bool bResult; // Set the position at the new file size - dwNewPos = SetFilePointer(pStream->hFile, FileSizeLo, &FileSizeHi, FILE_BEGIN); + dwNewPos = SetFilePointer(pStream->Base.File.hFile, (LONG)NewFileSize, &FileSizeHi, FILE_BEGIN); if(dwNewPos == INVALID_SET_FILE_POINTER && GetLastError() != ERROR_SUCCESS) return false; // Set the current file pointer as the end of the file - bResult = (bool)SetEndOfFile(pStream->hFile); + bResult = (bool)SetEndOfFile(pStream->Base.File.hFile); // Restore the file position - FileSizeHi = (LONG)(pStream->RawFilePos >> 32); - FileSizeLo = (LONG)(pStream->RawFilePos); - SetFilePointer(pStream->hFile, FileSizeLo, &FileSizeHi, FILE_BEGIN); + FileSizeHi = (LONG)(pStream->Base.File.FilePos >> 32); + FileSizeLo = (LONG)(pStream->Base.File.FilePos); + SetFilePointer(pStream->Base.File.hFile, FileSizeLo, &FileSizeHi, FILE_BEGIN); return bResult; } #endif - -#ifdef PLATFORM_MAC - { - OSErr theErr; - - theErr = FSSetForkSize((short)(long)pStream->hFile, fsFromStart, (SInt64)NewFileSize); - if(theErr != noErr) - { - nLastError = theErr; - return false; - } - - return true; - } -#endif -#ifdef PLATFORM_LINUX +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) { - if(ftruncate((intptr_t)pStream->hFile, (off_t)NewFileSize) == -1) + if(ftruncate((intptr_t)pStream->Base.File.hFile, (off_t)NewFileSize) == -1) { nLastError = errno; return false; @@ -700,84 +360,839 @@ static bool File_SetSize( #endif } -//----------------------------------------------------------------------------- -// Stream functions - partial normal file stream - -static bool PartFile_GetPos( - TPartFileStream * pStream, // Pointer to an open stream - ULONGLONG & ByteOffset) // Pointer to file byte offset +static bool BaseFile_GetTime(TFileStream * pStream, ULONGLONG * pFileTime) { - ByteOffset = pStream->VirtualPos; + *pFileTime = pStream->Base.File.FileTime; return true; } -static bool PartFile_Read( - TPartFileStream * pStream, // Pointer to an open stream - ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position - void * pvBuffer, // Pointer to data to be read - DWORD dwBytesToRead) // Number of bytes to read from the file +// Renames the file pointed by pStream so that it contains data from pNewStream +static bool BaseFile_Switch(TFileStream * pStream, TFileStream * pNewStream) { - ULONGLONG RawByteOffset; - LPBYTE pbBuffer = (LPBYTE)pvBuffer; - DWORD dwBytesRemaining = dwBytesToRead; - DWORD dwPartOffset; - DWORD dwPartIndex; - DWORD dwBytesRead = 0; - DWORD dwPartSize = pStream->PartSize; - bool bResult = false; - int nFailReason = ERROR_HANDLE_EOF; // Why it failed if not enough bytes was read +#ifdef PLATFORM_WINDOWS + // Delete the original stream file. Don't check the result value, + // because if the file doesn't exist, it would fail + DeleteFile(pStream->szFileName); - // If the byte offset is not entered, use the current position - if(pByteOffset == NULL) - pByteOffset = &pStream->VirtualPos; + // Rename the new file to the old stream's file + return (bool)MoveFile(pNewStream->szFileName, pStream->szFileName); +#endif - // Check if the file position is not at or beyond end of the file - if(*pByteOffset >= pStream->VirtualSize) +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) + // "rename" on Linux also works if the target file exists + if(rename(pNewStream->szFileName, pStream->szFileName) == -1) { - SetLastError(ERROR_HANDLE_EOF); + nLastError = errno; return false; } - // Get the part index where the read offset is - // Note that the part index should now be within the range, - // as read requests beyond-EOF are handled by the previous test - dwPartIndex = (DWORD)(*pByteOffset / pStream->PartSize); - assert(dwPartIndex < pStream->PartCount); + return true; +#endif +} - // If the number of bytes remaining goes past - // the end of the file, cut them - if((*pByteOffset + dwBytesRemaining) > pStream->VirtualSize) - dwBytesRemaining = (DWORD)(pStream->VirtualSize - *pByteOffset); +static void BaseFile_Close(TFileStream * pStream) +{ + if(pStream->Base.File.hFile != INVALID_HANDLE_VALUE) + { +#ifdef PLATFORM_WINDOWS + CloseHandle(pStream->Base.File.hFile); +#endif - // Calculate the offset in the current part - dwPartOffset = (DWORD)(*pByteOffset) & (pStream->PartSize - 1); +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) + close((intptr_t)pStream->Base.File.hFile); +#endif + } - // Read all data, one part at a time - while(dwBytesRemaining != 0) + // Also invalidate the handle + pStream->Base.File.hFile = INVALID_HANDLE_VALUE; +} + +static bool BaseFile_Create( + TFileStream * pStream, + const TCHAR * szFileName, + DWORD dwStreamFlags) +{ +#ifdef PLATFORM_WINDOWS { - PPART_FILE_MAP_ENTRY PartMap = pStream->PartMap + dwPartIndex; - DWORD dwBytesInPart; + DWORD dwWriteShare = (dwStreamFlags & STREAM_FLAG_WRITE_SHARE) ? FILE_SHARE_WRITE : 0; + + pStream->Base.File.hFile = CreateFile(szFileName, + GENERIC_READ | GENERIC_WRITE, + dwWriteShare | FILE_SHARE_READ, + NULL, + CREATE_ALWAYS, + 0, + NULL); + if(pStream->Base.File.hFile == INVALID_HANDLE_VALUE) + return false; + } +#endif - // If the part is not present in the file, we fail the read - if((PartMap->Flags & 3) == 0) +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) + { + intptr_t handle; + + handle = open(szFileName, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if(handle == -1) { - nFailReason = ERROR_CAN_NOT_COMPLETE; + nLastError = errno; + return false; + } + + pStream->Base.File.hFile = (HANDLE)handle; + } +#endif + + // Fill-in the entry points + pStream->BaseRead = BaseFile_Read; + pStream->BaseWrite = BaseFile_Write; + pStream->BaseGetPos = BaseFile_GetPos; + pStream->BaseGetSize = BaseFile_GetSize; + pStream->BaseSetSize = BaseFile_SetSize; + pStream->BaseSetSize = BaseFile_SetSize; + pStream->BaseGetTime = BaseFile_GetTime; + pStream->BaseClose = BaseFile_Close; + + // Reset the file position + pStream->Base.File.FileSize = 0; + pStream->Base.File.FilePos = 0; + pStream->dwFlags = dwStreamFlags; + return true; +} + +static bool BaseFile_Open( + TFileStream * pStream, + const TCHAR * szFileName, + DWORD dwStreamFlags) +{ +#ifdef PLATFORM_WINDOWS + { + ULARGE_INTEGER FileSize; + DWORD dwDesiredAccess = (dwStreamFlags & STREAM_FLAG_READ_ONLY) ? GENERIC_READ : GENERIC_ALL; + DWORD dwWriteShare = (dwStreamFlags & STREAM_FLAG_WRITE_SHARE) ? FILE_SHARE_WRITE : 0; + + // Open the file + pStream->Base.File.hFile = CreateFile(szFileName, + dwDesiredAccess, + dwWriteShare | FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + 0, + NULL); + if(pStream->Base.File.hFile == INVALID_HANDLE_VALUE) + return false; + + // Query the file size + FileSize.LowPart = GetFileSize(pStream->Base.File.hFile, &FileSize.HighPart); + pStream->Base.File.FileSize = FileSize.QuadPart; + + // Query last write time + GetFileTime(pStream->Base.File.hFile, NULL, NULL, (LPFILETIME)&pStream->Base.File.FileTime); + } +#endif + +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) + { + struct stat fileinfo; + int oflag = (dwStreamFlags & STREAM_FLAG_READ_ONLY) ? O_RDONLY : O_RDWR; + intptr_t handle; + + // Open the file + handle = open(szFileName, oflag); + if(handle == -1) + { + nLastError = errno; + return false; + } + + // Get the file size + if(fstat(handle, &fileinfo) == -1) + { + nLastError = errno; + return false; + } + + // time_t is number of seconds since 1.1.1970, UTC. + // 1 second = 10000000 (decimal) in FILETIME + // Set the start to 1.1.1970 00:00:00 + pStream->Base.File.FileTime = 0x019DB1DED53E8000ULL + (10000000 * fileinfo.st_mtime); + pStream->Base.File.FileSize = (ULONGLONG)fileinfo.st_size; + pStream->Base.File.hFile = (HANDLE)handle; + } +#endif + + // Fill-in the entry points + pStream->BaseRead = BaseFile_Read; + pStream->BaseWrite = BaseFile_Write; + pStream->BaseGetPos = BaseFile_GetPos; + pStream->BaseGetSize = BaseFile_GetSize; + pStream->BaseSetSize = BaseFile_SetSize; + pStream->BaseGetTime = BaseFile_GetTime; + pStream->BaseClose = BaseFile_Close; + + // Reset the file position + pStream->Base.File.FilePos = 0; + pStream->dwFlags = dwStreamFlags; + return true; +} + +//----------------------------------------------------------------------------- +// Local functions - base memory-mapped file support + +static bool BaseMap_Read( + TFileStream * pStream, // Pointer to an open stream + ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position + void * pvBuffer, // Pointer to data to be read + DWORD dwBytesToRead) // Number of bytes to read from the file +{ + ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.Map.FilePos; + + // Do we have to read anything at all? + if(dwBytesToRead != 0) + { + // Don't allow reading past file size + if((ByteOffset + dwBytesToRead) > pStream->Base.Map.FileSize) + return false; + + // Copy the required data + memcpy(pvBuffer, pStream->Base.Map.pbFile + (size_t)ByteOffset, dwBytesToRead); + } + + // Move the current file position + pStream->Base.Map.FilePos += dwBytesToRead; + return true; +} + +static bool BaseMap_GetPos( + TFileStream * pStream, // Pointer to an open stream + ULONGLONG * pByteOffset) // Pointer to file byte offset +{ + *pByteOffset = pStream->Base.Map.FilePos; + return true; +} + +static bool BaseMap_GetSize( + TFileStream * pStream, // Pointer to an open stream + ULONGLONG * pFileSize) // Pointer where to store file size +{ + *pFileSize = pStream->Base.Map.FileSize; + return true; +} + +static bool BaseMap_GetTime(TFileStream * pStream, ULONGLONG * pFileTime) +{ + *pFileTime = pStream->Base.Map.FileTime; + return true; +} + +static void BaseMap_Close(TFileStream * pStream) +{ +#ifdef PLATFORM_WINDOWS + if(pStream->Base.Map.pbFile != NULL) + UnmapViewOfFile(pStream->Base.Map.pbFile); +#endif + +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) + if(pStream->Base.Map.pbFile != NULL) + munmap(pStream->Base.Map.pbFile, (size_t )pStream->Base.Map.FileSize); +#endif + + pStream->Base.Map.pbFile = NULL; +} + +static bool BaseMap_Open( + TFileStream * pStream, + const TCHAR * szFileName, + DWORD dwStreamFlags) +{ +#ifdef PLATFORM_WINDOWS + + ULARGE_INTEGER FileSize; + HANDLE hFile; + HANDLE hMap; + bool bResult = false; + + // Open the file for read access + hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if(hFile != NULL) + { + // Retrieve file size. Don't allow mapping file of a zero size. + FileSize.LowPart = GetFileSize(hFile, &FileSize.HighPart); + if(FileSize.QuadPart != 0) + { + // Retrieve file time + GetFileTime(hFile, NULL, NULL, (LPFILETIME)&pStream->Base.Map.FileTime); + + // Now create mapping object + hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); + if(hMap != NULL) + { + // Map the entire view into memory + // Note that this operation will fail if the file can't fit + // into usermode address space + pStream->Base.Map.pbFile = (LPBYTE)MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0); + if(pStream->Base.Map.pbFile != NULL) + { + pStream->Base.Map.FileSize = FileSize.QuadPart; + pStream->Base.Map.FilePos = 0; + bResult = true; + } + + // Close the map handle + CloseHandle(hMap); + } + } + + // Close the file handle + CloseHandle(hFile); + } + + // If the file is not there and is not available for random access, + // report error + if(bResult == false) + return false; +#endif + +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) + struct stat fileinfo; + intptr_t handle; + bool bResult = false; + + // Open the file + handle = open(szFileName, O_RDONLY); + if(handle != -1) + { + // Get the file size + if(fstat(handle, &fileinfo) != -1) + { + pStream->Base.Map.pbFile = (LPBYTE)mmap(NULL, (size_t)fileinfo.st_size, PROT_READ, MAP_PRIVATE, handle, 0); + if(pStream->Base.Map.pbFile != NULL) + { + // time_t is number of seconds since 1.1.1970, UTC. + // 1 second = 10000000 (decimal) in FILETIME + // Set the start to 1.1.1970 00:00:00 + pStream->Base.Map.FileTime = 0x019DB1DED53E8000ULL + (10000000 * fileinfo.st_mtime); + pStream->Base.Map.FileSize = (ULONGLONG)fileinfo.st_size; + pStream->Base.Map.FilePos = 0; + bResult = true; + } + } + close(handle); + } + + // Did the mapping fail? + if(bResult == false) + { + nLastError = errno; + return false; + } +#endif + + // Fill-in entry points + pStream->BaseRead = BaseMap_Read; + pStream->BaseGetPos = BaseMap_GetPos; + pStream->BaseGetSize = BaseMap_GetSize; + pStream->BaseGetTime = BaseMap_GetTime; + pStream->BaseClose = BaseMap_Close; + pStream->dwFlags = dwStreamFlags; + return true; +} + +//----------------------------------------------------------------------------- +// Local functions - base HTTP file support + +static const TCHAR * BaseHttp_ExtractServerName(const TCHAR * szFileName, TCHAR * szServerName) +{ + // Check for HTTP + if(!_tcsnicmp(szFileName, _T("http://"), 7)) + szFileName += 7; + + // Cut off the server name + if(szServerName != NULL) + { + while(szFileName[0] != 0 && szFileName[0] != _T('/')) + *szServerName++ = *szFileName++; + *szServerName = 0; + } + else + { + while(szFileName[0] != 0 && szFileName[0] != _T('/')) + *szFileName++; + } + + // Return the remainder + return szFileName; +} + +static bool BaseHttp_Read( + TFileStream * pStream, // Pointer to an open stream + ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position + void * pvBuffer, // Pointer to data to be read + DWORD dwBytesToRead) // Number of bytes to read from the file +{ +#ifdef PLATFORM_WINDOWS + ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.Http.FilePos; + DWORD dwTotalBytesRead = 0; + + // Do we have to read anything at all? + if(dwBytesToRead != 0) + { + HINTERNET hRequest; + LPCTSTR szFileName; + LPBYTE pbBuffer = (LPBYTE)pvBuffer; + TCHAR szRangeRequest[0x80]; + DWORD dwStartOffset = (DWORD)ByteOffset; + DWORD dwEndOffset = dwStartOffset + dwBytesToRead; + BYTE Buffer[0x200]; + + // Open HTTP request to the file + szFileName = BaseHttp_ExtractServerName(pStream->szFileName, NULL); + hRequest = HttpOpenRequest(pStream->Base.Http.hConnect, _T("GET"), szFileName, NULL, NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE, 0); + if(hRequest != NULL) + { + // Add range request to the HTTP headers + // http://www.clevercomponents.com/articles/article015/resuming.asp + _stprintf(szRangeRequest, _T("Range: bytes=%d-%d"), dwStartOffset, dwEndOffset); + HttpAddRequestHeaders(hRequest, szRangeRequest, 0xFFFFFFFF, HTTP_ADDREQ_FLAG_ADD_IF_NEW); + + // Send the request to the server + if(HttpSendRequest(hRequest, NULL, 0, NULL, 0)) + { + while(dwTotalBytesRead < dwBytesToRead) + { + DWORD dwBlockBytesToRead = dwBytesToRead - dwTotalBytesRead; + DWORD dwBlockBytesRead = 0; + + // Read the block from the file + if(dwBlockBytesToRead > sizeof(Buffer)) + dwBlockBytesToRead = sizeof(Buffer); + InternetReadFile(hRequest, pbBuffer, dwBlockBytesToRead, &dwBlockBytesRead); + + // Check for end + if(dwBlockBytesRead == 0) + break; + + // Move buffers + dwTotalBytesRead += dwBlockBytesRead; + pbBuffer += dwBlockBytesRead; + } + } + InternetCloseHandle(hRequest); + } + } + + // Increment the current file position by number of bytes read + pStream->Base.Http.FilePos = ByteOffset + dwTotalBytesRead; + + // If the number of bytes read doesn't match the required amount, return false + if(dwTotalBytesRead != dwBytesToRead) + SetLastError(ERROR_HANDLE_EOF); + return (dwTotalBytesRead == dwBytesToRead); + +#else + + // Not supported + pStream = pStream; + pByteOffset = pByteOffset; + pvBuffer = pvBuffer; + dwBytesToRead = dwBytesToRead; + SetLastError(ERROR_NOT_SUPPORTED); + return false; + +#endif +} + +static bool BaseHttp_GetPos( + TFileStream * pStream, // Pointer to an open stream + ULONGLONG * pByteOffset) // Pointer to file byte offset +{ + *pByteOffset = pStream->Base.Http.FilePos; + return true; +} + +static bool BaseHttp_GetSize( + TFileStream * pStream, // Pointer to an open stream + ULONGLONG * pFileSize) // Pointer where to store file size +{ + *pFileSize = pStream->Base.Http.FileSize; + return true; +} + +static bool BaseHttp_GetTime(TFileStream * pStream, ULONGLONG * pFileTime) +{ + *pFileTime = pStream->Base.Http.FileTime; + return true; +} + +static void BaseHttp_Close(TFileStream * pStream) +{ +#ifdef PLATFORM_WINDOWS + if(pStream->Base.Http.hConnect != NULL) + InternetCloseHandle(pStream->Base.Http.hConnect); + pStream->Base.Http.hConnect = NULL; + + if(pStream->Base.Http.hInternet != NULL) + InternetCloseHandle(pStream->Base.Http.hInternet); + pStream->Base.Http.hInternet = NULL; +#else + pStream = pStream; +#endif +} + +static bool BaseHttp_Open( + TFileStream * pStream, + const TCHAR * szFileName, + DWORD dwStreamFlags) +{ +#ifdef PLATFORM_WINDOWS + + HINTERNET hRequest; + DWORD dwTemp = 0; + bool bFileAvailable = false; + int nError = ERROR_SUCCESS; + + // Don't connect to the internet + if(!InternetGetConnectedState(&dwTemp, 0)) + nError = GetLastError(); + + // Initiate the connection to the internet + if(nError == ERROR_SUCCESS) + { + pStream->Base.Http.hInternet = InternetOpen(_T("StormLib HTTP MPQ reader"), + INTERNET_OPEN_TYPE_PRECONFIG, + NULL, + NULL, + 0); + if(pStream->Base.Http.hInternet == NULL) + nError = GetLastError(); + } + + // Connect to the server + if(nError == ERROR_SUCCESS) + { + TCHAR szServerName[MAX_PATH]; + DWORD dwFlags = INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_UI | INTERNET_FLAG_NO_CACHE_WRITE; + + // Initiate connection with the server + szFileName = BaseHttp_ExtractServerName(szFileName, szServerName); + pStream->Base.Http.hConnect = InternetConnect(pStream->Base.Http.hInternet, + szServerName, + INTERNET_DEFAULT_HTTP_PORT, + NULL, + NULL, + INTERNET_SERVICE_HTTP, + dwFlags, + 0); + if(pStream->Base.Http.hConnect == NULL) + nError = GetLastError(); + } + + // Now try to query the file size + if(nError == ERROR_SUCCESS) + { + // Open HTTP request to the file + hRequest = HttpOpenRequest(pStream->Base.Http.hConnect, _T("GET"), szFileName, NULL, NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE, 0); + if(hRequest != NULL) + { + if(HttpSendRequest(hRequest, NULL, 0, NULL, 0)) + { + ULONGLONG FileTime = 0; + DWORD dwFileSize = 0; + DWORD dwDataSize; + DWORD dwIndex = 0; + + // Check if the MPQ has Last Modified field + dwDataSize = sizeof(ULONGLONG); + if(HttpQueryInfo(hRequest, HTTP_QUERY_LAST_MODIFIED | HTTP_QUERY_FLAG_SYSTEMTIME, &FileTime, &dwDataSize, &dwIndex)) + pStream->Base.Http.FileTime = FileTime; + + // Verify if the server supports random access + dwDataSize = sizeof(DWORD); + if(HttpQueryInfo(hRequest, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &dwFileSize, &dwDataSize, &dwIndex)) + { + if(dwFileSize != 0) + { + pStream->Base.Http.FileSize = dwFileSize; + pStream->Base.Http.FilePos = 0; + bFileAvailable = true; + } + } + } + InternetCloseHandle(hRequest); + } + } + + // If the file is not there and is not available for random access, + // report error + if(bFileAvailable == false) + { + BaseHttp_Close(pStream); + return false; + } + + // Fill-in entry points + pStream->BaseRead = BaseHttp_Read; + pStream->BaseGetPos = BaseHttp_GetPos; + pStream->BaseGetSize = BaseHttp_GetSize; + pStream->BaseGetTime = BaseHttp_GetTime; + pStream->BaseClose = BaseHttp_Close; + pStream->dwFlags = dwStreamFlags; + return true; + +#else + + // Not supported + pStream = pStream; + szFileName = szFileName; + SetLastError(ERROR_NOT_SUPPORTED); + return false; + +#endif +} + +//----------------------------------------------------------------------------- +// Local functions - linear stream support + +static bool LinearStream_Read( + TLinearStream * pStream, // Pointer to an open stream + ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position + void * pvBuffer, // Pointer to data to be read + DWORD dwBytesToRead) // Number of bytes to read from the file +{ + ULONGLONG ByteOffset; + ULONGLONG EndOffset; + LPBYTE pbBitmap; + DWORD BlockIndex; + DWORD ByteIndex; + DWORD BitMask; + + // At this point, we must have a bitmap set + assert(pStream->pBitmap != NULL); + + // If we have data map, we must check if the data block is present in the MPQ + if(dwBytesToRead != 0) + { + DWORD BlockSize = pStream->pBitmap->BlockSize; + + // Get the offset where we read it from + if(pByteOffset == NULL) + pStream->BaseGetPos(pStream, &ByteOffset); + else + ByteOffset = *pByteOffset; + EndOffset = ByteOffset + dwBytesToRead; + + // If the start of the area is within the region + // protected by data map, check each block + if(ByteOffset < pStream->pBitmap->EndOffset) + { + // Cut the end of the stream protected by the data map + EndOffset = STORMLIB_MIN(EndOffset, pStream->pBitmap->EndOffset); + + // Calculate the initial block index + BlockIndex = (DWORD)(ByteOffset / BlockSize); + pbBitmap = (LPBYTE)(pStream->pBitmap + 1); + + // Parse each block + while(ByteOffset < EndOffset) + { + // Prepare byte index and bit mask + ByteIndex = BlockIndex / 8; + BitMask = 1 << (BlockIndex & 0x07); + + // If that bit is not set, it means that the block is not present + if((pbBitmap[ByteIndex] & BitMask) == 0) + { + SetLastError(ERROR_FILE_CORRUPT); + return false; + } + + // Move to tne next block + ByteOffset += BlockSize; + BlockIndex++; + } + } + } + + // Now if all tests passed, we can call the base read function + return pStream->BaseRead(pStream, pByteOffset, pvBuffer, dwBytesToRead); +} + +static bool LinearStream_Switch(TLinearStream * pStream, TLinearStream * pNewStream) +{ + // Sanity checks + assert((pNewStream->dwFlags & STREAM_PROVIDER_MASK) == STREAM_PROVIDER_LINEAR); + assert((pNewStream->dwFlags & BASE_PROVIDER_MASK) == BASE_PROVIDER_FILE); + assert((pStream->dwFlags & STREAM_PROVIDER_MASK) == STREAM_PROVIDER_LINEAR); + assert((pStream->dwFlags & BASE_PROVIDER_MASK) == BASE_PROVIDER_FILE); + + // Close the new stream + pNewStream->BaseClose(pNewStream); + + // Close the source stream + pStream->BaseClose(pStream); + + // Rename the new data source file to the existing file + if(!BaseFile_Switch(pStream, pNewStream)) + return false; + + // Now we have to open the "pStream" again + if(!BaseFile_Open(pStream, pStream->szFileName, pNewStream->dwFlags)) + return false; + + // We need to cleanup the new data stream + FileStream_Close(pNewStream); + return true; +} + +static bool LinearStream_GetBitmap( + TLinearStream * pStream, + TFileBitmap * pBitmap, + DWORD Length, + LPDWORD LengthNeeded) +{ + DWORD TotalLength; + bool bResult = false; + + // Assumed that we have bitmap now + assert(pStream->pBitmap != NULL); + + // Give the bitmap length + TotalLength = sizeof(TFileBitmap) + pStream->pBitmap->BitmapSize; + if(LengthNeeded != NULL) + *LengthNeeded = TotalLength; + + // Do we have enough space to fill at least the bitmap structure? + if(Length >= sizeof(TFileBitmap)) + { + // Enough space for complete bitmap? + if(Length >= TotalLength) + { + memcpy(pBitmap, pStream->pBitmap, TotalLength); + bResult = true; + } + else + { + memcpy(pBitmap, pStream->pBitmap, sizeof(TFileBitmap)); + bResult = true; + } + } + + return bResult; +} + +static void LinearStream_Close(TLinearStream * pStream) +{ + // Free the data map, if any + if(pStream->pBitmap != NULL) + STORM_FREE(pStream->pBitmap); + pStream->pBitmap = NULL; + + // Call the base class for closing the stream + return pStream->BaseClose(pStream); +} + +static bool LinearStream_Open(TLinearStream * pStream) +{ + // No extra work here really; just set entry points + pStream->StreamRead = pStream->BaseRead; + pStream->StreamWrite = pStream->BaseWrite; + pStream->StreamGetPos = pStream->BaseGetPos; + pStream->StreamGetSize = pStream->BaseGetSize; + pStream->StreamSetSize = pStream->BaseSetSize; + pStream->StreamGetTime = pStream->BaseGetTime; + pStream->StreamGetBmp = (STREAM_GETBMP)Dummy_GetBitmap; + pStream->StreamSwitch = (STREAM_SWITCH)LinearStream_Switch; + pStream->StreamClose = (STREAM_CLOSE)LinearStream_Close; + return true; +} + +//----------------------------------------------------------------------------- +// Local functions - partial stream support + +static bool IsPartHeader(PPART_FILE_HEADER pPartHdr) +{ + // Version number must be 2 + if(pPartHdr->PartialVersion == 2) + { + // GameBuildNumber must be an ASCII number + if(isdigit(pPartHdr->GameBuildNumber[0]) && isdigit(pPartHdr->GameBuildNumber[1]) && isdigit(pPartHdr->GameBuildNumber[2])) + { + // Block size must be power of 2 + if((pPartHdr->BlockSize & (pPartHdr->BlockSize - 1)) == 0) + return true; + } + } + + return false; +} + +static bool PartialStream_Read( + TPartialStream * pStream, + ULONGLONG * pByteOffset, + void * pvBuffer, + DWORD dwBytesToRead) +{ + ULONGLONG RawByteOffset; + LPBYTE pbBuffer = (LPBYTE)pvBuffer; + DWORD dwBytesRemaining = dwBytesToRead; + DWORD dwPartOffset; + DWORD dwPartIndex; + DWORD dwBytesRead = 0; + DWORD dwBlockSize = pStream->BlockSize; + bool bResult = false; + int nFailReason = ERROR_HANDLE_EOF; // Why it failed if not enough bytes was read + + // If the byte offset is not entered, use the current position + if(pByteOffset == NULL) + pByteOffset = &pStream->VirtualPos; + + // Check if the file position is not at or beyond end of the file + if(*pByteOffset >= pStream->VirtualSize) + { + SetLastError(ERROR_HANDLE_EOF); + return false; + } + + // Get the part index where the read offset is + // Note that the part index should now be within the range, + // as read requests beyond-EOF are handled by the previous test + dwPartIndex = (DWORD)(*pByteOffset / pStream->BlockSize); + assert(dwPartIndex < pStream->BlockCount); + + // If the number of bytes remaining goes past + // the end of the file, cut them + if((*pByteOffset + dwBytesRemaining) > pStream->VirtualSize) + dwBytesRemaining = (DWORD)(pStream->VirtualSize - *pByteOffset); + + // Calculate the offset in the current part + dwPartOffset = (DWORD)(*pByteOffset) & (pStream->BlockSize - 1); + + // Read all data, one part at a time + while(dwBytesRemaining != 0) + { + PPART_FILE_MAP_ENTRY PartMap = pStream->PartMap + dwPartIndex; + DWORD dwBytesInPart; + + // If the part is not present in the file, we fail the read + if((PartMap->Flags & 3) == 0) + { + nFailReason = ERROR_FILE_CORRUPT; bResult = false; break; } // If we are in the last part, we have to cut the number of bytes in the last part - if(dwPartIndex == pStream->PartCount - 1) - dwPartSize = (DWORD)pStream->VirtualSize & (pStream->PartSize - 1); + if(dwPartIndex == pStream->BlockCount - 1) + dwBlockSize = (DWORD)pStream->VirtualSize & (pStream->BlockSize - 1); // Get the number of bytes reamining in the current part - dwBytesInPart = dwPartSize - dwPartOffset; + dwBytesInPart = dwBlockSize - dwPartOffset; // Compute the raw file offset of the file part RawByteOffset = MAKE_OFFSET64(PartMap->BlockOffsHi, PartMap->BlockOffsLo); if(RawByteOffset == 0) { - nFailReason = ERROR_CAN_NOT_COMPLETE; + nFailReason = ERROR_FILE_CORRUPT; bResult = false; break; } @@ -788,9 +1203,9 @@ static bool PartFile_Read( // Append the offset within the part RawByteOffset += dwPartOffset; - if(!File_Read(pStream, &RawByteOffset, pbBuffer, dwBytesInPart)) + if(!pStream->BaseRead(pStream, &RawByteOffset, pbBuffer, dwBytesInPart)) { - nFailReason = ERROR_CAN_NOT_COMPLETE; + nFailReason = ERROR_FILE_CORRUPT; bResult = false; break; } @@ -812,68 +1227,214 @@ static bool PartFile_Read( return (dwBytesRead == dwBytesToRead); } -static bool PartFile_Write( - TPartFileStream * pStream, // Pointer to an open stream - ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position - const void * pvBuffer, // Pointer to data to be read - DWORD dwBytesToRead) // Number of bytes to read from the file +static bool PartialStream_GetPos( + TPartialStream * pStream, + ULONGLONG & ByteOffset) { - // Keep compiler happy - dwBytesToRead = dwBytesToRead; - pByteOffset = pByteOffset; - pvBuffer = pvBuffer; - pStream = pStream; + ByteOffset = pStream->VirtualPos; + return true; +} - // Not allowed - return false; +static bool PartialStream_GetSize( + TPartialStream * pStream, // Pointer to an open stream + ULONGLONG & FileSize) // Pointer where to store file size +{ + FileSize = pStream->VirtualSize; + return true; +} + +static bool PartialStream_GetBitmap( + TPartialStream * pStream, + TFileBitmap * pBitmap, + DWORD Length, + LPDWORD LengthNeeded) +{ + LPBYTE pbBitmap; + DWORD TotalLength; + DWORD BitmapSize = 0; + DWORD ByteOffset; + DWORD BitMask; + bool bResult = false; + + // Do we have stream bitmap? + BitmapSize = ((pStream->BlockCount - 1) / 8) + 1; + + // Give the bitmap length + TotalLength = sizeof(TFileBitmap) + BitmapSize; + if(LengthNeeded != NULL) + *LengthNeeded = TotalLength; + + // Do we have enough to fill at least the header? + if(Length >= sizeof(TFileBitmap)) + { + // Fill the bitmap header + pBitmap->StartOffset = 0; + pBitmap->EndOffset = pStream->VirtualSize; + pBitmap->IsComplete = 1; + pBitmap->BitmapSize = BitmapSize; + pBitmap->BlockSize = pStream->BlockSize; + pBitmap->Reserved = 0; + + // Is there at least one incomplete block? + for(DWORD i = 0; i < pStream->BlockCount; i++) + { + if(pStream->PartMap[i].Flags != 3) + { + pBitmap->IsComplete = 0; + break; + } + } + + bResult = true; + } + + // Do we have enough space for supplying the bitmap? + if(Length >= TotalLength) + { + // Fill the file bitmap + pbBitmap = (LPBYTE)(pBitmap + 1); + for(DWORD i = 0; i < pStream->BlockCount; i++) + { + // Is the block there? + if(pStream->PartMap[i].Flags == 3) + { + ByteOffset = i / 8; + BitMask = 1 << (i & 7); + pbBitmap[ByteOffset] |= BitMask; + } + } + bResult = true; + } + + return bResult; +} + +static void PartialStream_Close(TPartialStream * pStream) +{ + // Free the part map + if(pStream->PartMap != NULL) + STORM_FREE(pStream->PartMap); + pStream->PartMap = NULL; + + // Clear variables + pStream->VirtualSize = 0; + pStream->VirtualPos = 0; + + // Close the base stream + assert(pStream->BaseClose != NULL); + pStream->BaseClose(pStream); } -static bool PartFile_GetSize( - TPartFileStream * pStream, // Pointer to an open stream - ULONGLONG & FileSize) // Pointer where to store file size -{ - FileSize = pStream->VirtualSize; - return true; -} +static bool PartialStream_Open(TPartialStream * pStream) +{ + PART_FILE_HEADER PartHdr; + ULONGLONG VirtualSize; // Size of the file stored in part file + ULONGLONG ByteOffset = {0}; + DWORD BlockCount; + + // Sanity check + assert(pStream->BaseRead != NULL); + + // Attempt to read PART file header + if(pStream->BaseRead(pStream, &ByteOffset, &PartHdr, sizeof(PART_FILE_HEADER))) + { + // We need to swap PART file header on big-endian platforms + BSWAP_PART_HEADER(&PartHdr); + + // Verify the PART file header + if(IsPartHeader(&PartHdr)) + { + // Calculate the number of parts in the file + VirtualSize = MAKE_OFFSET64(PartHdr.FileSizeHi, PartHdr.FileSizeLo); + assert(VirtualSize != 0); + BlockCount = (DWORD)((VirtualSize + PartHdr.BlockSize - 1) / PartHdr.BlockSize); + + // Allocate the map entry array + pStream->PartMap = STORM_ALLOC(PART_FILE_MAP_ENTRY, BlockCount); + if(pStream->PartMap != NULL) + { + // Load the block map + if(pStream->BaseRead(pStream, NULL, pStream->PartMap, BlockCount * sizeof(PART_FILE_MAP_ENTRY))) + { + // Swap the array of file map entries + BSWAP_ARRAY32_UNSIGNED(pStream->PartMap, BlockCount * sizeof(PART_FILE_MAP_ENTRY)); + + // Fill the members of PART file stream + pStream->VirtualSize = ((ULONGLONG)PartHdr.FileSizeHi) + PartHdr.FileSizeLo; + pStream->VirtualPos = 0; + pStream->BlockCount = BlockCount; + pStream->BlockSize = PartHdr.BlockSize; + + // Set new function pointers + pStream->StreamRead = (STREAM_READ)PartialStream_Read; + pStream->StreamGetPos = (STREAM_GETPOS)PartialStream_GetPos; + pStream->StreamGetSize = (STREAM_GETSIZE)PartialStream_GetSize; + pStream->StreamGetTime = pStream->BaseGetTime; + pStream->StreamGetTime = pStream->BaseGetTime; + pStream->StreamGetBmp = (STREAM_GETBMP)PartialStream_GetBitmap; + pStream->StreamClose = (STREAM_CLOSE)PartialStream_Close; + return true; + } -static bool PartFile_SetSize( - TPartFileStream * pStream, // Pointer to an open stream - ULONGLONG NewSize) // new size of the file -{ - // Keep compiler happy - pStream = pStream; - NewSize = NewSize; + // Free the part map + STORM_FREE(pStream->PartMap); + pStream->PartMap = NULL; + } + } + } - // Not allowed + SetLastError(ERROR_BAD_FORMAT); return false; } //----------------------------------------------------------------------------- -// Stream functions - encrypted stream -// -// Note: In original Starcraft II Installer.exe: Suffix derived from battle.net auth. code -// Address of decryption routine: 0053A3D0 http://us.battle.net/static/mediakey/sc2-authenticationcode-enUS.txt -// Pointer to decryptor object: ECX Numbers mean offset of 4-char group of auth code -// Pointer to key: ECX+0x5C -0C- -1C--08- -18--04- -14--00- -10- -static const char * MpqeKey_Starcraft2_Install_enUS = "expand 32-byte kTFD80000ETR5VM5G0000K859RE5N0000WT6F3DH500005LXG"; -static const char * MpqeKey_Starcraft2_Install_enGB = "expand 32-byte kANGY000029ZH6NA20000HRGF8UDG0000NY82G8MN00006A3D"; -static const char * MpqeKey_Starcraft2_Install_deDE = "expand 32-byte kSSXH00004XFXK4KX00008EKJD3CA0000Y64ZY45M0000YD9V"; -static const char * MpqeKey_Starcraft2_Install_esES = "expand 32-byte kQU4Y0000XKTQ94PF0000N4R4UAXE0000AZ248WLK0000249P"; -static const char * MpqeKey_Starcraft2_Install_frFR = "expand 32-byte kFWPQ00006EAJ8HJE0000PFER9K9300008MA2ZG7J0000UA76"; -static const char * MpqeKey_Starcraft2_Install_itIT = "expand 32-byte kXV7E00008BL2TVAP0000GVMWUNNN0000SVBWNE7C00003G2B"; -static const char * MpqeKey_Starcraft2_Install_plPL = "expand 32-byte k83U6000048L6LULJ00004MQDB8ME0000UP6K2NSF0000YHA3"; -static const char * MpqeKey_Starcraft2_Install_ruRU = "expand 32-byte k9SH70000YEGT4BAT0000QDK978W60000V9NLVHB30000D68V"; - -static const char * MpqKeyArray[] = +// Local functions - encrypted stream support + +static const char * szKeyTemplate = "expand 32-byte k000000000000000000000000000000000000000000000000"; + +static const char * AuthCodeArray[] = { - MpqeKey_Starcraft2_Install_enUS, - MpqeKey_Starcraft2_Install_enGB, - MpqeKey_Starcraft2_Install_deDE, - MpqeKey_Starcraft2_Install_esES, - MpqeKey_Starcraft2_Install_frFR, - MpqeKey_Starcraft2_Install_itIT, - MpqeKey_Starcraft2_Install_plPL, - MpqeKey_Starcraft2_Install_ruRU, + // Diablo III: Agent.exe (1.0.0.954) + // Address of decryption routine: 00502b00 + // Pointer to decryptor object: ECX + // Pointer to key: ECX+0x5C + // Authentication code URL: http://dist.blizzard.com/mediakey/d3-authenticationcode-enGB.txt + // -0C- -1C--08- -18--04- -14--00- -10- + "UCMXF6EJY352EFH4XFRXCFH2XC9MQRZK", // Diablo III Installer (deDE): "expand 32-byte kEFH40000QRZKY3520000XC9MF6EJ0000CFH2UCMX0000XFRX" + "MMKVHY48RP7WXP4GHYBQ7SL9J9UNPHBP", // Diablo III Installer (enGB): "expand 32-byte kXP4G0000PHBPRP7W0000J9UNHY4800007SL9MMKV0000HYBQ" + "8MXLWHQ7VGGLTZ9MQZQSFDCLJYET3CPP", // Diablo III Installer (enSG): "expand 32-byte kTZ9M00003CPPVGGL0000JYETWHQ70000FDCL8MXL0000QZQS" + "EJ2R5TM6XFE2GUNG5QDGHKQ9UAKPWZSZ", // Diablo III Installer (enUS): "expand 32-byte kGUNG0000WZSZXFE20000UAKP5TM60000HKQ9EJ2R00005QDG" + "PBGFBE42Z6LNK65UGJQ3WZVMCLP4HQQT", // Diablo III Installer (esES): "expand 32-byte kK65U0000HQQTZ6LN0000CLP4BE420000WZVMPBGF0000GJQ3" + "X7SEJJS9TSGCW5P28EBSC47AJPEY8VU2", // Diablo III Installer (esMX): "expand 32-byte kW5P200008VU2TSGC0000JPEYJJS90000C47AX7SE00008EBS" + "5KVBQA8VYE6XRY3DLGC5ZDE4XS4P7YA2", // Diablo III Installer (frFR): "expand 32-byte kRY3D00007YA2YE6X0000XS4PQA8V0000ZDE45KVB0000LGC5" + "478JD2K56EVNVVY4XX8TDWYT5B8KB254", // Diablo III Installer (itIT): "expand 32-byte kVVY40000B2546EVN00005B8KD2K50000DWYT478J0000XX8T" + "8TS4VNFQRZTN6YWHE9CHVDH9NVWD474A", // Diablo III Installer (koKR): "expand 32-byte k6YWH0000474ARZTN0000NVWDVNFQ0000VDH98TS40000E9CH" + "LJ52Z32DF4LZ4ZJJXVKK3AZQA6GABLJB", // Diablo III Installer (plPL): "expand 32-byte k4ZJJ0000BLJBF4LZ0000A6GAZ32D00003AZQLJ520000XVKK" + "K6BDHY2ECUE2545YKNLBJPVYWHE7XYAG", // Diablo III Installer (ptBR): "expand 32-byte k545Y0000XYAGCUE20000WHE7HY2E0000JPVYK6BD0000KNLB" + "NDVW8GWLAYCRPGRNY8RT7ZZUQU63VLPR", // Diablo III Installer (ruRU): "expand 32-byte kXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "6VWCQTN8V3ZZMRUCZXV8A8CGUX2TAA8H", // Diablo III Installer (zhTW): "expand 32-byte kMRUC0000AA8HV3ZZ0000UX2TQTN80000A8CG6VWC0000ZXV8" +// "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", // Diablo III Installer (zhCN): "expand 32-byte kXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + + // Note: Starcraft II (Wings of Liberty): Installer.exe (4.1.1.4219) + // Address of decryption routine: 0053A3D0 + // Pointer to decryptor object: ECX + // Pointer to key: ECX+0x5C + // Authentication code URL: http://dist.blizzard.com/mediakey/sc2-authenticationcode-enUS.txt + // -0C- -1C--08- -18--04- -14--00- -10- + "Y45MD3CAK4KXSSXHYD9VY64Z8EKJ4XFX", // SC2 Wings of Liberty (deDE): "expand 32-byte kSSXH00004XFXK4KX00008EKJD3CA0000Y64ZY45M0000YD9V" + "G8MN8UDG6NA2ANGY6A3DNY82HRGF29ZH", // SC2 Wings of Liberty (enGB): "expand 32-byte kANGY000029ZH6NA20000HRGF8UDG0000NY82G8MN00006A3D" + "W9RRHLB2FDU9WW5B3ECEBLRSFWZSF7HW", // SC2 Wings of Liberty (enSG): "expand 32-byte kWW5B0000F7HWFDU90000FWZSHLB20000BLRSW9RR00003ECE" + "3DH5RE5NVM5GTFD85LXGWT6FK859ETR5", // SC2 Wings of Liberty (enUS): "expand 32-byte kTFD80000ETR5VM5G0000K859RE5N0000WT6F3DH500005LXG" + "8WLKUAXE94PFQU4Y249PAZ24N4R4XKTQ", // SC2 Wings of Liberty (esES): "expand 32-byte kQU4Y0000XKTQ94PF0000N4R4UAXE0000AZ248WLK0000249P" + "A34DXX3VHGGXSQBRFE5UFFDXMF9G4G54", // SC2 Wings of Liberty (esMX): "expand 32-byte kSQBR00004G54HGGX0000MF9GXX3V0000FFDXA34D0000FE5U" + "ZG7J9K938HJEFWPQUA768MA2PFER6EAJ", // SC2 Wings of Liberty (frFR): "expand 32-byte kFWPQ00006EAJ8HJE0000PFER9K9300008MA2ZG7J0000UA76" + "NE7CUNNNTVAPXV7E3G2BSVBWGVMW8BL2", // SC2 Wings of Liberty (itIT): "expand 32-byte kXV7E00008BL2TVAP0000GVMWUNNN0000SVBWNE7C00003G2B" + "3V9E2FTMBM9QQWK7U6MAMWAZWQDB838F", // SC2 Wings of Liberty (koKR): "expand 32-byte kQWK70000838FBM9Q0000WQDB2FTM0000MWAZ3V9E0000U6MA" + "2NSFB8MELULJ83U6YHA3UP6K4MQD48L6", // SC2 Wings of Liberty (plPL): "expand 32-byte k83U6000048L6LULJ00004MQDB8ME0000UP6K2NSF0000YHA3" + "QA2TZ9EWZ4CUU8BMB5WXCTY65F9CSW4E", // SC2 Wings of Liberty (ptBR): "expand 32-byte kU8BM0000SW4EZ4CU00005F9CZ9EW0000CTY6QA2T0000B5WX" + "VHB378W64BAT9SH7D68VV9NLQDK9YEGT", // SC2 Wings of Liberty (ruRU): "expand 32-byte k9SH70000YEGT4BAT0000QDK978W60000V9NLVHB30000D68V" + "U3NFQJV4M6GC7KBN9XQJ3BRDN3PLD9NE", // SC2 Wings of Liberty (zhTW): "expand 32-byte k7KBN0000D9NEM6GC0000N3PLQJV400003BRDU3NF00009XQJ" + NULL }; @@ -884,11 +1445,30 @@ static DWORD Rol32(DWORD dwValue, DWORD dwRolCount) return (dwValue << dwRolCount) | (dwValue >> dwShiftRight); } +static void CreateKeyFromAuthCode( + LPBYTE pbKeyBuffer, + const char * szAuthCode) +{ + LPDWORD KeyPosition = (LPDWORD)(pbKeyBuffer + 0x10); + LPDWORD AuthCode32 = (LPDWORD)szAuthCode; + + memcpy(pbKeyBuffer, szKeyTemplate, MPQE_CHUNK_SIZE); + KeyPosition[0x00] = AuthCode32[0x03]; + KeyPosition[0x02] = AuthCode32[0x07]; + KeyPosition[0x03] = AuthCode32[0x02]; + KeyPosition[0x05] = AuthCode32[0x06]; + KeyPosition[0x06] = AuthCode32[0x01]; + KeyPosition[0x08] = AuthCode32[0x05]; + KeyPosition[0x09] = AuthCode32[0x00]; + KeyPosition[0x0B] = AuthCode32[0x04]; + BSWAP_ARRAY32_UNSIGNED(pbKeyBuffer, MPQE_CHUNK_SIZE); +} + static void DecryptFileChunk( - DWORD * MpqData, - LPBYTE pbKey, - ULONGLONG ByteOffset, - DWORD dwLength) + DWORD * MpqData, + LPBYTE pbKey, + ULONGLONG ByteOffset, + DWORD dwLength) { ULONGLONG ChunkOffset; DWORD KeyShuffled[0x10]; @@ -921,7 +1501,7 @@ static void DecryptFileChunk( KeyShuffled[0x04] = KeyMirror[0x0D]; KeyShuffled[0x01] = KeyMirror[0x0E]; KeyShuffled[0x00] = KeyMirror[0x0F]; - + // Shuffle the key - part 2 for(DWORD i = 0; i < RoundCount; i += 2) { @@ -997,29 +1577,22 @@ static void DecryptFileChunk( } } - -static bool DetectFileKey(TEncryptedStream * pStream) +static bool DetectFileKey(LPBYTE pbKeyBuffer, LPBYTE pbEncryptedHeader) { ULONGLONG ByteOffset = 0; - BYTE EncryptedHeader[MPQE_CHUNK_SIZE]; BYTE FileHeader[MPQE_CHUNK_SIZE]; - // Load the chunk from the file - if(!FileStream_Read(pStream, &ByteOffset, EncryptedHeader, sizeof(EncryptedHeader))) - return false; - // We just try all known keys one by one - for(int i = 0; MpqKeyArray[i] != NULL; i++) + for(int i = 0; AuthCodeArray[i] != NULL; i++) { - // Copy the key there - memcpy(pStream->Key, MpqKeyArray[i], MPQE_CHUNK_SIZE); - BSWAP_ARRAY32_UNSIGNED(pStream->Key, MPQE_CHUNK_SIZE); + // Prepare they decryption key from game serial number + CreateKeyFromAuthCode(pbKeyBuffer, AuthCodeArray[i]); - // Try to decrypt with the given key - memcpy(FileHeader, EncryptedHeader, MPQE_CHUNK_SIZE); - DecryptFileChunk((LPDWORD)FileHeader, pStream->Key, ByteOffset, MPQE_CHUNK_SIZE); + // Try to decrypt with the given key + memcpy(FileHeader, pbEncryptedHeader, MPQE_CHUNK_SIZE); + DecryptFileChunk((LPDWORD)FileHeader, pbKeyBuffer, ByteOffset, MPQE_CHUNK_SIZE); - // We check the decrypoted data + // We check the decrypted data // All known encrypted MPQs have header at the begin of the file, // so we check for MPQ signature there. if(FileHeader[0] == 'M' && FileHeader[1] == 'P' && FileHeader[2] == 'Q') @@ -1030,11 +1603,11 @@ static bool DetectFileKey(TEncryptedStream * pStream) return false; } -static bool EncryptedFile_Read( - TEncryptedStream * pStream, // Pointer to an open stream - ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position - void * pvBuffer, // Pointer to data to be read - DWORD dwBytesToRead) // Number of bytes to read from the file +static bool EncryptedStream_Read( + TEncryptedStream * pStream, // Pointer to an open stream + ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position + void * pvBuffer, // Pointer to data to be read + DWORD dwBytesToRead) // Number of bytes to read from the file { ULONGLONG StartOffset; // Offset of the first byte to be read from the file ULONGLONG ByteOffset; // Offset that the caller wants @@ -1046,10 +1619,10 @@ static bool EncryptedFile_Read( bool bResult = false; // Get the byte offset - if(pByteOffset != NULL) - ByteOffset = *pByteOffset; + if(pByteOffset == NULL) + pStream->BaseGetPos(pStream, &ByteOffset); else - ByteOffset = pStream->RawFilePos; + ByteOffset = *pByteOffset; // Cut it down to MPQE chunk size StartOffset = ByteOffset; @@ -1061,14 +1634,14 @@ static bool EncryptedFile_Read( dwBytesToAllocate = (dwBytesToDecrypt + (MPQE_CHUNK_SIZE - 1)) & ~(MPQE_CHUNK_SIZE - 1); // Allocate buffers for encrypted and decrypted data - pbMpqData = ALLOCMEM(BYTE, dwBytesToAllocate); + pbMpqData = STORM_ALLOC(BYTE, dwBytesToAllocate); if(pbMpqData) { // Get the offset of the desired data in the cache dwOffsetInCache = (DWORD)(ByteOffset - StartOffset); // Read the file from the stream as-is - if(File_Read(pStream, &StartOffset, pbMpqData, dwBytesToDecrypt)) + if(pStream->BaseRead(pStream, &StartOffset, pbMpqData, dwBytesToDecrypt)) { // Decrypt the data DecryptFileChunk((LPDWORD)pbMpqData, pStream->Key, StartOffset, dwBytesToAllocate); @@ -1082,740 +1655,640 @@ static bool EncryptedFile_Read( assert(false); } - // Free decryption buffer - FREEMEM(pbMpqData); + // Free decryption buffer + STORM_FREE(pbMpqData); } // Free buffers and exit return bResult; } -static bool EncryptedFile_Write( - TEncryptedStream * pStream, // Pointer to an open stream - ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position - const void * pvBuffer, // Pointer to data to be read - DWORD dwBytesToRead) // Number of bytes to read from the file +static bool EncryptedStream_Open(TEncryptedStream * pStream) { - // Keep compiler happy - dwBytesToRead = dwBytesToRead; - pByteOffset = pByteOffset; - pvBuffer = pvBuffer; - pStream = pStream; + ULONGLONG ByteOffset = 0; + BYTE EncryptedHeader[MPQE_CHUNK_SIZE]; - // Not allowed - return false; -} + // Sanity check + assert(pStream->BaseRead != NULL); -static bool EncryptedFile_SetSize( - TEncryptedStream * pStream, // Pointer to an open stream - ULONGLONG NewSize) // new size of the file -{ - // Keep compiler happy - pStream = pStream; - NewSize = NewSize; + // Load one MPQE chunk and try to detect the file key + if(pStream->BaseRead(pStream, &ByteOffset, EncryptedHeader, sizeof(EncryptedHeader))) + { + // Attempt to decrypt the MPQ header with all known keys + if(DetectFileKey(pStream->Key, EncryptedHeader)) + { + // Assign functions + pStream->StreamRead = (STREAM_READ)EncryptedStream_Read; + pStream->StreamGetPos = pStream->BaseGetPos; + pStream->StreamGetSize = pStream->BaseGetSize; + pStream->StreamGetTime = pStream->BaseGetTime; + pStream->StreamGetBmp = (STREAM_GETBMP)Dummy_GetBitmap; + pStream->StreamClose = pStream->BaseClose; + + // We need to reset the position back to the begin of the file + pStream->BaseRead(pStream, &ByteOffset, EncryptedHeader, 0); + return true; + } - // Not allowed + // An unknown key + SetLastError(ERROR_UNKNOWN_FILE_KEY); + } return false; } //----------------------------------------------------------------------------- // Public functions -// -// This function creates a new file for read or read-write access -// -// * If the current platform supports file sharing, -// the file must be created for read sharing (i.e. another application -// can open the file for read, but not for write) -// * If the file does not exist, the function must create new one -// * If the file exists, the function must rewrite it and set to zero size -// * The parameters of the function must be validate by the caller -// * The function must initialize all stream function pointers in TFileStream -// * If the function fails from any reason, it must close all handles -// and free all memory that has been allocated in the process of stream creation, -// including the TFileStream structure itself -// +/** + * This function creates a new file for read-write access + * + * - If the current platform supports file sharing, + * the file must be created for read sharing (i.e. another application + * can open the file for read, but not for write) + * - If the file does not exist, the function must create new one + * - If the file exists, the function must rewrite it and set to zero size + * - The parameters of the function must be validate by the caller + * - The function must initialize all stream function pointers in TFileStream + * - If the function fails from any reason, it must close all handles + * and free all memory that has been allocated in the process of stream creation, + * including the TFileStream structure itself + * + * \a szFileName Name of the file to create + */ TFileStream * FileStream_CreateFile( - const char * szFileName) // Name of the file to create -{ - TFileStream * pStream = NULL; - HANDLE hFile; - - // Create the file - hFile = CreateNewFile(szFileName); - if(hFile != INVALID_HANDLE_VALUE) - { - // Allocate the FileStream structure and fill it - pStream = ALLOCMEM(TFileStream, 1); - if(pStream != NULL) - { - // Reset entire structure to zero - memset(pStream, 0, sizeof(TFileStream)); - - // Save file name and set function pointers - strcpy(pStream->szFileName, szFileName); - pStream->StreamGetPos = File_GetPos; - pStream->StreamRead = File_Read; - pStream->StreamWrite = File_Write; - pStream->StreamGetSize = File_GetSize; - pStream->StreamSetSize = File_SetSize; - pStream->hFile = hFile; - } - else - { - CloseTheFile(hFile); - } - } - - // Return the stream - return pStream; -} - -// -// This function opens an existing file for read or read-write access -// -// * If the current platform supports file sharing, -// the file must be open for read sharing (i.e. another application -// can open the file for read, but not for write) -// * If the file does not exist, the function must return NULL -// * If the file exists but cannot be open, then function must return NULL -// * The parameters of the function must be validate by the caller -// * The function must check if the file is a PART file, -// and create TPartFileStream object if so. -// * The function must initialize all stream function pointers in TFileStream -// * If the function fails from any reason, it must close all handles -// and free all memory that has been allocated in the process of stream creation, -// including the TFileStream structure itself -// - -TFileStream * FileStream_OpenRawFile( - const char * szFileName, // Name of the file to create - bool bWriteAccess) // false = read-only, true = read+write + const TCHAR * szFileName, + DWORD dwStreamFlags) { TFileStream * pStream; - HANDLE hFile; - // Create the file - hFile = OpenExistingFile(szFileName, bWriteAccess); - if(hFile == INVALID_HANDLE_VALUE) + // We only support creation of linear, local file + if((dwStreamFlags & (STREAM_PROVIDER_MASK | BASE_PROVIDER_MASK)) != (STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE)) + { + SetLastError(ERROR_NOT_SUPPORTED); return NULL; + } - // Initialize the file as normal file stream - pStream = ALLOCMEM(TFileStream, 1); + // Allocate file stream structure for linear stream + pStream = STORM_ALLOC(TFileStream, 1); if(pStream != NULL) { // Reset entire structure to zero memset(pStream, 0, sizeof(TFileStream)); + _tcscpy(pStream->szFileName, szFileName); + + // Attempt to create the disk file + if(BaseFile_Create(pStream, szFileName, dwStreamFlags)) + { + // Fill the stream provider functions + pStream->StreamRead = pStream->BaseRead; + pStream->StreamWrite = pStream->BaseWrite; + pStream->StreamGetPos = pStream->BaseGetPos; + pStream->StreamGetSize = pStream->BaseGetSize; + pStream->StreamSetSize = pStream->BaseSetSize; + pStream->StreamGetTime = pStream->BaseGetTime; + pStream->StreamGetBmp = (STREAM_GETBMP)Dummy_GetBitmap;; + pStream->StreamSwitch = (STREAM_SWITCH)LinearStream_Switch; + pStream->StreamClose = pStream->BaseClose; + return pStream; + } - // Save file name and set function pointers - strcpy(pStream->szFileName, szFileName); - pStream->StreamGetPos = File_GetPos; - pStream->StreamRead = File_Read; - pStream->StreamWrite = File_Write; - pStream->StreamGetSize = File_GetSize; - pStream->StreamSetSize = File_SetSize; - if(bWriteAccess == false) - pStream->StreamFlags |= STREAM_FLAG_READ_ONLY; - pStream->hFile = hFile; - return pStream; + // File create failed, delete the stream + STORM_FREE(pStream); + pStream = NULL; } - CloseTheFile(hFile); - return NULL; + // Return the stream + return pStream; } +/** + * This function opens an existing file for read or read-write access + * - If the current platform supports file sharing, + * the file must be open for read sharing (i.e. another application + * can open the file for read, but not for write) + * - If the file does not exist, the function must return NULL + * - If the file exists but cannot be open, then function must return NULL + * - The parameters of the function must be validate by the caller + * - The function must check if the file is a PART file, + * and create TPartialStream object if so. + * - The function must initialize all stream function pointers in TFileStream + * - If the function fails from any reason, it must close all handles + * and free all memory that has been allocated in the process of stream creation, + * including the TFileStream structure itself + * + * \a szFileName Name of the file to open + * \a dwStreamFlags specifies the provider and base storage type + */ + TFileStream * FileStream_OpenFile( - const char * szFileName, // Name of the file to create - bool bWriteAccess) // false = read-only, true = read+write + const TCHAR * szFileName, + DWORD dwStreamFlags) { - PART_FILE_HEADER PartHdr; - ULONGLONG VirtualSize; // Size of the file stored in part file - ULONGLONG ByteOffset = {0}; - TFileStream * pStream; - size_t nStructLength; - DWORD PartCount; - - // Open the file as normal stream - pStream = FileStream_OpenRawFile(szFileName, bWriteAccess); - if(pStream == NULL) - return NULL; + TFileStream * pStream = NULL; + size_t StreamSize = 0; + bool bStreamResult = false; + bool bBaseResult = false; - // Attempt to read PART file header - if(FileStream_Read(pStream, &ByteOffset, &PartHdr, sizeof(PART_FILE_HEADER))) + // Allocate file stream for each stream provider + switch(dwStreamFlags & STREAM_PROVIDER_MASK) { - // We need to swap PART file header on big-endian platforms - BSWAP_PART_HEADER(&PartHdr); - - // Verify the PART file header - if(PartHdr.PartialVersion == 2 && PartHdr.GameBuildNumber[0] != 0 && - PartHdr.PartSize != 0 && (PartHdr.PartSize & (PartHdr.PartSize - 1)) == 0) - { - TPartFileStream * pPartStream; + case STREAM_PROVIDER_LINEAR: // Allocate structure for linear stream + StreamSize = sizeof(TLinearStream); + break; - // Calculate the number of parts in the file - VirtualSize = MAKE_OFFSET64(PartHdr.FileSizeHi, PartHdr.FileSizeLo); - PartCount = (DWORD)((VirtualSize + PartHdr.PartSize - 1) / PartHdr.PartSize); - - // Calculate the size of the entire structure - // Note that we decrement number of parts by one, - // because there already is one entry in the TPartFileStream structure - nStructLength = sizeof(TPartFileStream) + (PartCount - 1) * sizeof(PART_FILE_MAP_ENTRY); - pPartStream = (TPartFileStream *)ALLOCMEM(char, nStructLength); - if(pPartStream != NULL) - { - // Initialize the part file stream - memset(pPartStream, 0, nStructLength); - memcpy(pPartStream, pStream, sizeof(TFileStream)); + case STREAM_PROVIDER_PARTIAL: + dwStreamFlags |= STREAM_FLAG_READ_ONLY; + StreamSize = sizeof(TPartialStream); + break; - // Load the block map - if(!FileStream_Read(pPartStream, NULL, pPartStream->PartMap, PartCount * sizeof(PART_FILE_MAP_ENTRY))) - { - FileStream_Close(pStream); - FREEMEM(pPartStream); - return NULL; - } + case STREAM_PROVIDER_ENCRYPTED: + dwStreamFlags |= STREAM_FLAG_READ_ONLY; + StreamSize = sizeof(TEncryptedStream); + break; - // Swap the array of file map entries - BSWAP_ARRAY32_UNSIGNED(pPartStream->PartMap, PartCount * sizeof(PART_FILE_MAP_ENTRY)); + default: + return NULL; + } - // Set new function pointers - pPartStream->StreamGetPos = (STREAM_GETPOS)PartFile_GetPos; - pPartStream->StreamRead = (STREAM_READ)PartFile_Read; - pPartStream->StreamWrite = (STREAM_WRITE)PartFile_Write; - pPartStream->StreamGetSize = (STREAM_GETSIZE)PartFile_GetSize; - pPartStream->StreamSetSize = (STREAM_SETSIZE)PartFile_SetSize; - pPartStream->StreamFlags |= (STREAM_FLAG_READ_ONLY | STREAM_FLAG_PART_FILE); + // Allocate the stream for each type + pStream = (TFileStream *)STORM_ALLOC(BYTE, StreamSize); + if(pStream == NULL) + return NULL; - // Fill the members of PART file stream - pPartStream->VirtualSize = ((ULONGLONG)PartHdr.FileSizeHi) + PartHdr.FileSizeLo; - pPartStream->VirtualPos = 0; - pPartStream->PartCount = PartCount; - pPartStream->PartSize = PartHdr.PartSize; + // Fill the stream structure with zeros + memset(pStream, 0, StreamSize); + _tcscpy(pStream->szFileName, szFileName); - FREEMEM(pStream); - } - return pPartStream; - } + // Now initialize the respective base provider + switch(dwStreamFlags & BASE_PROVIDER_MASK) + { + case BASE_PROVIDER_FILE: + bBaseResult = BaseFile_Open(pStream, szFileName, dwStreamFlags); + break; + + case BASE_PROVIDER_MAP: + dwStreamFlags |= STREAM_FLAG_READ_ONLY; + bBaseResult = BaseMap_Open(pStream, szFileName, dwStreamFlags); + break; + + case BASE_PROVIDER_HTTP: + dwStreamFlags |= STREAM_FLAG_READ_ONLY; + bBaseResult = BaseHttp_Open(pStream, szFileName, dwStreamFlags); + break; } - // If the file doesn't contain PART file header, - // reset the file position to begin of the file - FileStream_Read(pStream, &ByteOffset, NULL, 0); - return pStream; -} - -TFileStream * FileStream_OpenEncrypted(const char * szFileName) -{ - TEncryptedStream * pEncryptedStream; - TFileStream * pStream; - - // Open the file as raw stream - pStream = FileStream_OpenRawFile(szFileName, false); - if(pStream) + // If we failed to open the base storage, fail the operation + if(bBaseResult == false) { - // Allocate new stream for handling encryption - pEncryptedStream = ALLOCMEM(TEncryptedStream, 1); - if(pEncryptedStream != NULL) - { - // Copy the file stream to the encrypted stream - memset(pEncryptedStream, 0, sizeof(TEncryptedStream)); - memcpy(pEncryptedStream, pStream, sizeof(TFileStream)); + STORM_FREE(pStream); + return NULL; + } - // Assign functions - pEncryptedStream->StreamRead = (STREAM_READ)EncryptedFile_Read; - pEncryptedStream->StreamWrite = (STREAM_WRITE)EncryptedFile_Write; - pEncryptedStream->StreamSetSize = (STREAM_SETSIZE)EncryptedFile_SetSize; - pEncryptedStream->StreamFlags |= (STREAM_FLAG_READ_ONLY | STREAM_FLAG_ENCRYPTED_FILE); + // Now initialize the stream provider + switch(dwStreamFlags & STREAM_PROVIDER_MASK) + { + case STREAM_PROVIDER_LINEAR: + bStreamResult = LinearStream_Open((TLinearStream *)pStream); + break; - // Get the file key - if(!DetectFileKey(pEncryptedStream)) - { - SetLastError(ERROR_UNKNOWN_FILE_KEY); - FREEMEM(pEncryptedStream); - pEncryptedStream = NULL; - } - } + case STREAM_PROVIDER_PARTIAL: + bStreamResult = PartialStream_Open((TPartialStream *)pStream); + break; - FREEMEM(pStream); - return pEncryptedStream; + case STREAM_PROVIDER_ENCRYPTED: + bStreamResult = EncryptedStream_Open((TEncryptedStream *)pStream); + break; } - return NULL; -} + // If the operation failed, free the stream and set it to NULL + if(bStreamResult == false) + { + // Only close the base stream + pStream->BaseClose(pStream); + STORM_FREE(pStream); + pStream = NULL; + } -// This function returns the current file position -bool FileStream_GetPos( - TFileStream * pStream, - ULONGLONG & ByteOffset) -{ - assert(pStream->StreamGetPos != NULL); - return pStream->StreamGetPos(pStream, ByteOffset); + return pStream; } -// -// This function reads data from the stream -// -// * Returns true if the read operation succeeded and all bytes have been read -// * Returns false if either read failed or not all bytes have been read -// * If the pByteOffset is NULL, the function must read the data from the current file position -// * The function can be called with dwBytesToRead = 0. In that case, pvBuffer is ignored -// and the function just adjusts file pointer. -// -// Returned value: -// -// * If the function reads the required amount of bytes, it returns true. -// * If the function reads less than required bytes, it returns false and GetLastError() returns ERROR_HANDLE_EOF -// * If the function fails, it reads false and GetLastError() returns an error code different from ERROR_HANDLE_EOF -// - -bool FileStream_Read( - TFileStream * pStream, // Pointer to an open stream - ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position - void * pvBuffer, // Pointer to data to be read - DWORD dwBytesToRead) // Number of bytes to read from the file +/** + * Reads data from the stream + * + * - Returns true if the read operation succeeded and all bytes have been read + * - Returns false if either read failed or not all bytes have been read + * - If the pByteOffset is NULL, the function must read the data from the current file position + * - The function can be called with dwBytesToRead = 0. In that case, pvBuffer is ignored + * and the function just adjusts file pointer. + * + * \a pStream Pointer to an open stream + * \a pByteOffset Pointer to file byte offset. If NULL, it reads from the current position + * \a pvBuffer Pointer to data to be read + * \a dwBytesToRead Number of bytes to read from the file + * + * \returns + * - If the function reads the required amount of bytes, it returns true. + * - If the function reads less than required bytes, it returns false and GetLastError() returns ERROR_HANDLE_EOF + * - If the function fails, it reads false and GetLastError() returns an error code different from ERROR_HANDLE_EOF + */ +bool FileStream_Read(TFileStream * pStream, ULONGLONG * pByteOffset, void * pvBuffer, DWORD dwBytesToRead) { assert(pStream->StreamRead != NULL); return pStream->StreamRead(pStream, pByteOffset, pvBuffer, dwBytesToRead); } -// -// This function writes data to the stream -// -// * Returns true if the write operation succeeded and all bytes have been written -// * Returns false if either write failed or not all bytes have been written -// * If the pByteOffset is NULL, the function must write the data to the current file position -// - -bool FileStream_Write( - TFileStream * pStream, // Pointer to an open stream - ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it writes to the current position - const void * pvBuffer, // Pointer to data to be written - DWORD dwBytesToWrite) // Number of bytes to read from the file +/** + * This function writes data to the stream + * + * - Returns true if the write operation succeeded and all bytes have been written + * - Returns false if either write failed or not all bytes have been written + * - If the pByteOffset is NULL, the function must write the data to the current file position + * + * \a pStream Pointer to an open stream + * \a pByteOffset Pointer to file byte offset. If NULL, it reads from the current position + * \a pvBuffer Pointer to data to be written + * \a dwBytesToWrite Number of bytes to write to the file + */ +bool FileStream_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const void * pvBuffer, DWORD dwBytesToWrite) { - if(pStream->StreamFlags & STREAM_FLAG_READ_ONLY) + if(pStream->dwFlags & STREAM_FLAG_READ_ONLY) return false; - assert(pStream->StreamWrite != NULL); + assert(pStream->StreamWrite != NULL); return pStream->StreamWrite(pStream, pByteOffset, pvBuffer, dwBytesToWrite); } -// -// Retrieves the last write time to the file -// +/** + * This function returns the current file position + * \a pStream + * \a ByteOffset + */ +bool FileStream_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset) +{ + assert(pStream->StreamGetPos != NULL); + return pStream->StreamGetPos(pStream, pByteOffset); +} -bool FileStream_GetLastWriteTime( - TFileStream * pStream, // Pointer to an open stream - ULONGLONG * pFileTime) // Pointer where to store file time +/** + * Returns the size of a file + * + * \a pStream Pointer to an open stream + * \a FileSize Pointer where to store the file size + */ +bool FileStream_GetSize(TFileStream * pStream, ULONGLONG * pFileSize) { -#ifdef PLATFORM_WINDOWS - FILETIME ft; + assert(pStream->StreamGetSize != NULL); + return pStream->StreamGetSize(pStream, pFileSize); +} - if(!GetFileTime(pStream->hFile, NULL, NULL, &ft)) +/** + * Sets the size of a file + * + * \a pStream Pointer to an open stream + * \a NewFileSize File size to set + */ +bool FileStream_SetSize(TFileStream * pStream, ULONGLONG NewFileSize) +{ + if(pStream->dwFlags & STREAM_FLAG_READ_ONLY) return false; - *pFileTime = MAKE_OFFSET64(ft.dwHighDateTime, ft.dwLowDateTime); - return true; -#endif + assert(pStream->StreamSetSize != NULL); + return pStream->StreamSetSize(pStream, NewFileSize); +} -#ifdef PLATFORM_MAC - OSErr theErr; - FSRef theFileRef; - FSCatalogInfo theCatInfo; - - theErr = FSGetForkCBInfo((short)(long)pStream->hFile, 0, NULL, NULL, NULL, &theFileRef, NULL); - if(theErr != noErr) - { - nLastError = theErr; - return false; - } - - theErr = FSGetCatalogInfo(&theFileRef, kFSCatInfoContentMod, &theCatInfo, NULL, NULL, NULL); - if(theErr != noErr) - { - nLastError = theErr; - return false; - } +/** + * Returns the last write time of a file + * + * \a pStream Pointer to an open stream + * \a pFileType Pointer where to store the file last write time + */ +bool FileStream_GetTime(TFileStream * pStream, ULONGLONG * pFileTime) +{ + assert(pStream->StreamGetTime != NULL); + return pStream->StreamGetTime(pStream, pFileTime); +} - ConvertUTCDateTimeToFileTime(&theCatInfo.contentModDate, pFileTime); +/** + * Returns the stream flags + * + * \a pStream Pointer to an open stream + * \a pdwStreamFlags Pointer where to store the stream flags + */ +bool FileStream_GetFlags(TFileStream * pStream, LPDWORD pdwStreamFlags) +{ + *pdwStreamFlags = pStream->dwFlags; return true; -#endif - -#ifdef PLATFORM_LINUX - struct stat file_stats; +} - if(fstat((int)(size_t)pStream->hFile, &file_stats) == -1) - { - nLastError = errno; +/** + * Switches a stream with another. Used for final phase of archive compacting. + * Performs these steps: + * + * 1) Closes the handle to the existing MPQ + * 2) Renames the temporary MPQ to the original MPQ, overwrites existing one + * 3) Opens the MPQ stores the handle and stream position to the new stream structure + * + * \a pStream Pointer to an open stream + * \a pTempStream Temporary ("working") stream (created during archive compacting) + */ +bool FileStream_Switch(TFileStream * pStream, TFileStream * pNewStream) +{ + if(pStream->dwFlags & STREAM_FLAG_READ_ONLY) return false; - } - ConvertTimeTToFileTime(pFileTime, file_stats.st_mtime); - return true; -#endif + assert(pStream->StreamSwitch != NULL); + return pStream->StreamSwitch(pStream, pNewStream); } -// -// Retrieves the size of the file -// - -bool FileStream_GetSize( - TFileStream * pStream, // Pointer to an open stream - ULONGLONG & FileSize) // Pointer where to store file size +/** + * Returns the file name of the stream + * + * \a pStream Pointer to an open stream + */ +TCHAR * FileStream_GetFileName(TFileStream * pStream) { - assert(pStream->StreamGetSize != NULL); - return pStream->StreamGetSize(pStream, FileSize); + assert(pStream != NULL); + return pStream->szFileName; } -// -// Retrieves the size of the file -// - -bool FileStream_SetSize( - TFileStream * pStream, // Pointer to an open stream - ULONGLONG NewFileSize) // Pointer where to store file size -{ - if(pStream->StreamFlags & STREAM_FLAG_READ_ONLY) - return false; - assert(pStream->StreamSetSize != NULL); - - return pStream->StreamSetSize(pStream, NewFileSize); -} - -// -// Switches a stream with another. Used for final phase of archive compacting. -// Performs these steps: -// -// 1) Closes the handle to the existing MPQ -// 2) Renames the temporary MPQ to the original MPQ, overwrites existing one -// 3) Opens the MPQ stores the handle and stream position to the new stream structure -// - -bool FileStream_MoveFile( - TFileStream * pStream, // Existing stream - TFileStream * pTempStream) // Temporary ("working") stream (created during archive compacting) +/** + * Returns true if the stream is read-only + * + * \a pStream Pointer to an open stream + */ +bool FileStream_IsReadOnly(TFileStream * pStream) { - bool bWriteAccess; + return (pStream->dwFlags & STREAM_FLAG_READ_ONLY) ? true : false; +} - // Close the handle to the temporary file - CloseTheFile(pTempStream->hFile); - pTempStream->hFile = INVALID_HANDLE_VALUE; +/** + * This function enabled a linear stream to include data bitmap. + * Used by MPQs v 4.0 from WoW. Each file block is represented by + * a bit in the bitmap. 1 means the block is present, 0 means it's not. + * + * \a pStream Pointer to an open stream + * \a pBitmap Pointer to file bitmap + */ - // Close the handle to the source file - CloseTheFile(pStream->hFile); - pStream->hFile = INVALID_HANDLE_VALUE; +bool FileStream_SetBitmap(TFileStream * pStream, TFileBitmap * pBitmap) +{ + TLinearStream * pLinearStream; - // Rename the temp file to the final file - if(!RenameFile(pTempStream->szFileName, pStream->szFileName)) + // It must be a linear stream. + if((pStream->dwFlags & STREAM_PROVIDER_MASK) != STREAM_PROVIDER_LINEAR) return false; + pLinearStream = (TLinearStream *)pStream; - // Now open the renamed file again, and store its handle to the old stream - bWriteAccess = (pStream->StreamFlags & STREAM_FLAG_READ_ONLY) ? false : true; - pStream->hFile = OpenExistingFile(pStream->szFileName, bWriteAccess); - if(pStream->hFile == INVALID_HANDLE_VALUE) + // Two bitmaps are not allowed + if(pLinearStream->pBitmap != NULL) return false; - // Delete the temporary file stream - FileStream_Close(pTempStream); + // We need to change some entry points + pLinearStream->StreamRead = (STREAM_READ)LinearStream_Read; + pLinearStream->StreamGetBmp = (STREAM_GETBMP)LinearStream_GetBitmap; - // The file position has been reset to zero by reopening the file - pStream->RawFilePos = 0; + // Using data bitmap renders the stream to be read only. + pLinearStream->dwFlags |= STREAM_FLAG_READ_ONLY; + pLinearStream->pBitmap = pBitmap; return true; } -// -// This function closes an archive file and frees any data buffers -// that have been allocated for stream management. The function must also -// support partially allocated structure, i.e. one or more buffers -// can be NULL, if there was an allocation failure during the process -// +/** + * This function retrieves the file bitmap. A file bitmap is an array + * of bits, each bit representing one file block. A value of 1 means + * that the block is present in the file, a value of 0 means that the + * block is not present. + * + * \a pStream Pointer to an open stream + * \a pBitmap Pointer to buffer where to store the file bitmap + * \a Length Size of buffer pointed by pBitmap, in bytes + * \a LengthNeeded If non-NULL, the function supplies the necessary byte size of the buffer + */ +bool FileStream_GetBitmap(TFileStream * pStream, TFileBitmap * pBitmap, DWORD Length, LPDWORD LengthNeeded) +{ + assert(pStream->StreamGetBmp != NULL); + return pStream->StreamGetBmp(pStream, pBitmap, Length, LengthNeeded); +} -void FileStream_Close( - TFileStream * pStream) // Pointer to an open stream +/** + * This function closes an archive file and frees any data buffers + * that have been allocated for stream management. The function must also + * support partially allocated structure, i.e. one or more buffers + * can be NULL, if there was an allocation failure during the process + * + * \a pStream Pointer to an open stream + */ +void FileStream_Close(TFileStream * pStream) { // Check if the stream structure is allocated at all if(pStream != NULL) { - // Close the file handle - if(pStream->hFile != INVALID_HANDLE_VALUE) - CloseTheFile(pStream->hFile); + // Close the stream provider. + // This will also close the base stream + assert(pStream->StreamClose != NULL); + pStream->StreamClose(pStream); // Free the stream itself - FREEMEM(pStream); + STORM_FREE(pStream); } } //----------------------------------------------------------------------------- // main - for testing purposes -/* -int main(void) + +#ifdef __STORMLIB_TEST__ +int FileStream_Test(const TCHAR * szFileName, DWORD dwStreamFlags) { - ULONGLONG FilePos; - ULONGLONG FileSize; - TMPQFileTime * pFT; - TFileStream * pTempStream; TFileStream * pStream; + TMPQHeader MpqHeader; + ULONGLONG FilePos; TMPQBlock * pBlock; TMPQHash * pHash; - TMPQHeader2 MpqHeader; - char szString1[100] = "This is a single line\n\r"; - char szString2[100]; - char Buffer[0x80]; - DWORD dwLength = strlen(szString1); - // - // Test 1: Write to a stream - // + InitializeMpqCryptography(); - pStream = FileStream_CreateFile("E:\\Stream.bin"); + pStream = FileStream_OpenFile(szFileName, dwStreamFlags); if(pStream == NULL) + return GetLastError(); + + // Read the MPQ header + FileStream_Read(pStream, NULL, &MpqHeader, MPQ_HEADER_SIZE_V2); + if(MpqHeader.dwID != ID_MPQ) + return ERROR_FILE_CORRUPT; + + // Read the hash table + pHash = STORM_ALLOC(TMPQHash, MpqHeader.dwHashTableSize); + if(pHash != NULL) { - printf("Failed to create new file\n"); - return -1; + FilePos = MpqHeader.dwHashTablePos; + FileStream_Read(pStream, &FilePos, pHash, MpqHeader.dwHashTableSize * sizeof(TMPQHash)); + DecryptMpqBlock(pHash, MpqHeader.dwHashTableSize * sizeof(TMPQHash), MPQ_KEY_HASH_TABLE); + STORM_FREE(pHash); } - for(int i = 0; i < 10; i++) + // Read the block table + pBlock = STORM_ALLOC(TMPQBlock, MpqHeader.dwBlockTableSize); + if(pBlock != NULL) { - if(!FileStream_Write(pStream, NULL, szString1, dwLength)) - { - printf("Failed to write to the stream\n"); - return -1; - } + FilePos = MpqHeader.dwBlockTablePos; + FileStream_Read(pStream, &FilePos, pBlock, MpqHeader.dwBlockTableSize * sizeof(TMPQBlock)); + DecryptMpqBlock(pBlock, MpqHeader.dwBlockTableSize * sizeof(TMPQBlock), MPQ_KEY_BLOCK_TABLE); + STORM_FREE(pBlock); } + FileStream_Close(pStream); + return ERROR_SUCCESS; +} +#endif + +/* +int FileStream_Test() +{ + TFileStream * pStream; + + InitializeMpqCryptography(); // - // Test2: Read from the stream + // Test 1: Write to a stream // - pStream = FileStream_OpenFile("E:\\Stream.bin", false); - if(pStream == NULL) + pStream = FileStream_CreateFile("E:\\Stream.bin", 0); + if(pStream != NULL) { - printf("Failed to open existing file\n"); - return -1; - } + char szString1[100] = "This is a single line\n\r"; + DWORD dwLength = strlen(szString1); - // This call must end with an error - if(FileStream_Write(pStream, NULL, "aaa", 3)) - { - printf("Write succeeded while it should fail\n"); - return -1; + for(int i = 0; i < 10; i++) + { + if(!FileStream_Write(pStream, NULL, szString1, dwLength)) + { + printf("Failed to write to the stream\n"); + return ERROR_CAN_NOT_COMPLETE; + } + } + FileStream_Close(pStream); } - for(int i = 0; i < 10; i++) + // + // Test2: Read from the stream + // + + pStream = FileStream_OpenFile("E:\\Stream.bin", STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); + if(pStream != NULL) { - if(!FileStream_Read(pStream, NULL, szString2, dwLength)) + char szString1[100] = "This is a single line\n\r"; + char szString2[100]; + DWORD dwLength = strlen(szString1); + + // This call must end with an error + if(FileStream_Write(pStream, NULL, "aaa", 3)) { - printf("Failed to read from the stream\n"); + printf("Write succeeded while it should fail\n"); return -1; } - szString2[dwLength] = 0; - if(strcmp(szString1, szString2)) + for(int i = 0; i < 10; i++) { - printf("Data read from file are different from data written\n"); - return -1; + if(!FileStream_Read(pStream, NULL, szString2, dwLength)) + { + printf("Failed to read from the stream\n"); + return -1; + } + + szString2[dwLength] = 0; + if(strcmp(szString1, szString2)) + { + printf("Data read from file are different from data written\n"); + return -1; + } } + FileStream_Close(pStream); } - FileStream_Close(pStream); // // Test3: Open the temp stream, write some data and switch it to the original stream // - pStream = FileStream_OpenFile("E:\\Stream.bin", false); - if(pStream == NULL) - { - printf("Failed to open existing file\n"); - return -1; - } - - pTempStream = FileStream_CreateFile("E:\\TempStream.bin"); - if(pTempStream == NULL) - { - printf("Failed to create temp stream\n"); - return -1; - } - - // Copy the original stream to the temp - if(!FileStream_GetSize(pStream, &FileSize)) - { - printf("Failed to get the file size\n"); - return -1; - } - - while(FileSize.QuadPart != 0) + pStream = FileStream_OpenFile("E:\\Stream.bin", STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); + if(pStream != NULL) { - DWORD dwBytesToRead = FileSize.LowPart; + TFileStream * pTempStream; + ULONGLONG FileSize; - if(dwBytesToRead > sizeof(Buffer)) - dwBytesToRead = sizeof(Buffer); - - if(!FileStream_Read(pStream, NULL, Buffer, dwBytesToRead)) + pTempStream = FileStream_CreateFile("E:\\TempStream.bin", 0); + if(pTempStream == NULL) { - printf("CopyStream: Read source file failed\n"); + printf("Failed to create temp stream\n"); return -1; } - if(!FileStream_Write(pTempStream, NULL, Buffer, dwBytesToRead)) + // Copy the original stream to the temp + if(!FileStream_GetSize(pStream, FileSize)) { - printf("CopyStream: Write target file failed\n"); + printf("Failed to get the file size\n"); return -1; } - FileSize.QuadPart -= dwBytesToRead; - } + while(FileSize != 0) + { + DWORD dwBytesToRead = (DWORD)FileSize; + char Buffer[0x80]; - // Switch the streams - // Note that the pTempStream is closed by the operation - FileStream_MoveFile(pStream, pTempStream); - FileStream_Close(pStream); + if(dwBytesToRead > sizeof(Buffer)) + dwBytesToRead = sizeof(Buffer); - // - // Test4: Read from the stream again - // + if(!FileStream_Read(pStream, NULL, Buffer, dwBytesToRead)) + { + printf("CopyStream: Read source file failed\n"); + return -1; + } - pStream = FileStream_OpenFile("E:\\Stream.bin", false); - if(pStream == NULL) - { - printf("Failed to open existing file\n"); - return -1; - } + if(!FileStream_Write(pTempStream, NULL, Buffer, dwBytesToRead)) + { + printf("CopyStream: Write target file failed\n"); + return -1; + } - for(int i = 0; i < 10; i++) - { - if(!FileStream_Read(pStream, NULL, szString2, dwLength)) - { - printf("Failed to read from the stream\n"); - return -1; + FileSize -= dwBytesToRead; } - szString2[dwLength] = 0; - if(strcmp(szString1, szString2)) - { - printf("Data read from file are different from data written\n"); - return -1; - } + // Switch the streams + // Note that the pTempStream is closed by the operation + FileStream_Switch(pStream, pTempStream); + FileStream_Close(pStream); } - FileStream_Close(pStream); // - // Test5: Open partial MPQ stream + // Test4: Read from the stream again // -// InitializeMpqCryptography(); - pStream = FileStream_OpenFile("e:\\Multimedia\\MPQs\\PartialMPQs\\patch.MPQ.part", false); + pStream = FileStream_OpenFile("E:\\Stream.bin", STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); if(pStream != NULL) { - // Read the MPQ header - FileStream_Read(pStream, NULL, &MpqHeader, MPQ_HEADER_SIZE_V2); - - // Read the hash table - pHash = ALLOCMEM(TMPQHash, MpqHeader.dwHashTableSize); - FilePos.HighPart = 0; - FilePos.LowPart = MpqHeader.dwHashTablePos; - FileStream_Read(pStream, &FilePos, pHash, MpqHeader.dwHashTableSize * sizeof(TMPQHash)); - - // - // At this point, the encrypted hash table should be like this: - // - // 416c7175 5ddfb61b 84bb75c8 c0399515 - // a9793eca 9ec773d7 ed8a54d6 74fb2adf - // 6acd4ae5 b13816b5 ffad2341 2f2b2a54 - // 614339c7 5fd0adf0 62434e91 d62439e3 - // 8f317aa5 f12706d6 bd83d2ca 97d7f108 - // 7586d373 51d85b05 8540beca f37ef3d7 - // d931d4d6 d592aadf 9044e960 c4592e92 - // 47dc03f7 0982dea4 afb31943 7c3c7cec - // 0c28fd0d bcbfb7df 4d13b6e4 b5b0ef31 - // e1a33b70 ec30e4b9 7aaa5e7a fb6d46ec - // 61732791 55fe757e 8ba18b5d d5f93246 - // 6d275f38 a89b5781 c34189a9 654c6472 - // 07e1d4e1 814bc8ee c72d2730 815afd43 - // 40bd2a92 640a9391 d868f813 0f61b73d - // 6d202746 2c5124ca 65db3ad0 5b1c3e39 - // b731013c 73776405 eac0c746 6e50c938 - // a4a7fd00 56db3805 6d6dbab7 44fed28a - // 2383394b bf617bdd a3edfaa2 e7d3aaaf - // - - // Decrypt the hash table -// DecryptMpqBlock(pHash, MpqHeader.dwHashTableSize * sizeof(TMPQHash), MPQ_KEY_HASH_TABLE); - - // - // At this point, the hash table should be like this: - // - // c750beb9 72c2538a 00000000 00000466 - // ffffffff ffffffff ffffffff ffffffff - // 898fdc7a 18963b5d 00000000 000005e1 - // ffffffff ffffffff ffffffff ffffffff - // e3c6fc32 d8afff2b 00000000 000001ea - // ffffffff ffffffff ffffffff ffffffff - // ffffffff ffffffff ffffffff ffffffff - // ffffffff ffffffff ffffffff ffffffff - // ffffffff ffffffff ffffffff ffffffff - // ffffffff ffffffff ffffffff ffffffff - // ffffffff ffffffff ffffffff ffffffff - // 0fa4fd60 3fbe8626 00000000 0000076f - // 9ee5bccf 031b277b 00000000 0000095c - // f4e154c5 0aadd1c1 00000000 00000876 - // 9e1ce9e7 e12d575d 00000000 0000071d - // - - // Read the block table - pBlock = ALLOCMEM(TMPQBlock, MpqHeader.dwBlockTableSize); - FilePos.HighPart = 0; - FilePos.LowPart = MpqHeader.dwBlockTablePos; - FileStream_Read(pStream, &FilePos, pBlock, MpqHeader.dwBlockTableSize * sizeof(TMPQBlock)); + char szString1[100] = "This is a single line\n\r"; + char szString2[100]; + DWORD dwLength = strlen(szString1); - // - // At this point, the encrypted block table should be like this: - // - // 3d4867a7 ca0f533e f82c54d6 ed3c9dec - // d8d607dc d9ad13ab f4588b46 8d058704 - // e8084fc8 63bc8064 b058c777 3683e9e3 - // 6c0da998 7703be0d 91ce3607 c14e29b9 - // 481b5c0d 42d902d2 8302acb7 e8f3e715 - // c9cdfc91 7cc38c15 ea3dfd22 ad20c856 - // b6450c7f 08522866 4cedb064 e03e3a86 - // 4509c7cc ddffbfc3 82fc8c66 e82a4424 - // afc4a982 23169037 5af6a3e2 34e1d24e - // 362c9e34 846cfc3d 4c611fcd d645fe8f - // f4061640 6d08d196 f330a975 66e30993 - // fd96a033 2b16def6 62ff30af 3e190b0b - // 664a5b91 b8558235 fd631825 a7807be7 - // ec906b9b 76d8b32e 36f3ea0b 1b0f5391 - // - - // Decrypt the block table -// DecryptMpqBlock(pBlock, MpqHeader.dwBlockTableSize * sizeof(TMPQBlock), MPQ_KEY_BLOCK_TABLE); - - // - // At this point, the block table should be like this: - // - // 0000002c 00078093 00116824 84000200 - // 000780bf 000002d5 00008044 84000200 - // 00078394 00001516 0000874c 84000200 - // 000798aa 00003797 0000af4e 84000200 - // 0007d041 000001db 00008044 84000200 - // 0007d21c 0000005e 0000005e 84000200 - // 0007d27a 000022fb 00009674 84000200 - // 0007f575 00002389 00009c64 84000200 - // 000818fe 000023cb 00009d58 84000200 - // 00083cc9 000024d9 0000a0d8 84000200 - // 000861a2 00002356 00009c70 84000200 - // 000884f8 000023d3 00009da4 84000200 - // 0008a8cb 000022d6 00009cd4 84000200 - // 0008cba1 00002339 00009714 84000200 - // 0008eeda 000023dc 00009b24 84000200 - // 000912b6 00002481 00009eac 84000200 - // 00093737 00002444 0000a028 84000200 - // 00095b7b 00002440 00009fc4 84000200 - // + for(int i = 0; i < 10; i++) + { + if(!FileStream_Read(pStream, NULL, szString2, dwLength)) + { + printf("Failed to read from the stream\n"); + return -1; + } + szString2[dwLength] = 0; + if(strcmp(szString1, szString2)) + { + printf("Data read from file are different from data written\n"); + return -1; + } + } FileStream_Close(pStream); } return 0; } */ + diff --git a/dep/StormLib/src/FileStream.h b/dep/StormLib/src/FileStream.h new file mode 100644 index 00000000000..5c50a0d2acc --- /dev/null +++ b/dep/StormLib/src/FileStream.h @@ -0,0 +1,189 @@ +/*****************************************************************************/ +/* FileStream.h Copyright (c) Ladislav Zezula 2012 */ +/*---------------------------------------------------------------------------*/ +/* Description: Definitions for FileStream object */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 14.04.12 1.00 Lad The first version of FileStream.h */ +/*****************************************************************************/ + +#ifndef __FILESTREAM_H__ +#define __FILESTREAM_H__ + +//----------------------------------------------------------------------------- +// Function prototypes + +typedef bool (*STREAM_READ)( + struct TFileStream * pStream, // Pointer to an open stream + ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position + void * pvBuffer, // Pointer to data to be read + DWORD dwBytesToRead // Number of bytes to read from the file + ); + +typedef bool (*STREAM_WRITE)( + struct TFileStream * pStream, // Pointer to an open stream + ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it writes to the current position + const void * pvBuffer, // Pointer to data to be written + DWORD dwBytesToWrite // Number of bytes to read from the file + ); + +typedef bool (*STREAM_GETPOS)( + struct TFileStream * pStream, // Pointer to an open stream + ULONGLONG * pByteOffset // Pointer to store current file position + ); + +typedef bool (*STREAM_GETSIZE)( + struct TFileStream * pStream, // Pointer to an open stream + ULONGLONG * pFileSize // Receives the file size, in bytes + ); + +typedef bool (*STREAM_SETSIZE)( + struct TFileStream * pStream, // Pointer to an open stream + ULONGLONG FileSize // New size for the file, in bytes + ); + +typedef bool (*STREAM_GETTIME)( + struct TFileStream * pStream, + ULONGLONG * pFT + ); + +typedef bool (*STREAM_SWITCH)( + struct TFileStream * pStream, + struct TFileStream * pNewStream + ); + +typedef bool (*STREAM_GETBMP)( + TFileStream * pStream, + TFileBitmap * pBitmap, + DWORD Length, + LPDWORD LengthNeeded + ); + +typedef void (*STREAM_CLOSE)( + struct TFileStream * pStream + ); + +//----------------------------------------------------------------------------- +// Local structures - part file structure + +typedef struct _PART_FILE_HEADER +{ + DWORD PartialVersion; // Always set to 2 + char GameBuildNumber[0x20]; // Minimum build number of the game that can use this MPQ + DWORD Flags; // Flags (details unknown) + DWORD FileSizeLo; // Low 32 bits of the contained file size + DWORD FileSizeHi; // High 32 bits of the contained file size + DWORD BlockSize; // Size of one file block, in bytes + +} PART_FILE_HEADER, *PPART_FILE_HEADER; + +// Structure describing the block-to-file map entry +typedef struct _PART_FILE_MAP_ENTRY +{ + DWORD Flags; // 3 = the block is present in the file + DWORD BlockOffsLo; // Low 32 bits of the block position in the file + DWORD BlockOffsHi; // High 32 bits of the block position in the file + DWORD LargeValueLo; // 64-bit value, meaning is unknown + DWORD LargeValueHi; + +} PART_FILE_MAP_ENTRY, *PPART_FILE_MAP_ENTRY; + +//----------------------------------------------------------------------------- +// Local structures + +union TBaseData +{ + struct + { + ULONGLONG FileSize; // Size of the file + ULONGLONG FilePos; // Current file position + ULONGLONG FileTime; // Date/time of last modification of the file + HANDLE hFile; // File handle + } File; + + struct + { + ULONGLONG FileSize; // Mapped file size + ULONGLONG FilePos; // Current stream position + ULONGLONG FileTime; // Date/time of last modification of the file + LPBYTE pbFile; // Pointer to mapped view + } Map; + + struct + { + ULONGLONG FileSize; // Size of the internet file + ULONGLONG FilePos; // Current position in the file + ULONGLONG FileTime; // Date/time of last modification of the file + HANDLE hInternet; // Internet handle + HANDLE hConnect; // Connection to the internet server + } Http; +}; + +//----------------------------------------------------------------------------- +// Structure for linear stream + +struct TFileStream +{ + // Stream provider functions + STREAM_READ StreamRead; // Pointer to stream read function for this archive. Do not use directly. + STREAM_WRITE StreamWrite; // Pointer to stream write function for this archive. Do not use directly. + STREAM_GETPOS StreamGetPos; // Pointer to function that returns current file position + STREAM_GETSIZE StreamGetSize; // Pointer to function returning file size + STREAM_SETSIZE StreamSetSize; // Pointer to function changing file size + STREAM_GETTIME StreamGetTime; // Pointer to function retrieving the file time + STREAM_GETBMP StreamGetBmp; // Pointer to function that retrieves the file bitmap + STREAM_SWITCH StreamSwitch; // Pointer to function changing the stream to another file + STREAM_CLOSE StreamClose; // Pointer to function closing the stream + + // Stream provider data members + TCHAR szFileName[MAX_PATH]; // File name + DWORD dwFlags; // Stream flags + + // Base provider functions + STREAM_READ BaseRead; + STREAM_WRITE BaseWrite; + STREAM_GETPOS BaseGetPos; // Pointer to function that returns current file position + STREAM_GETSIZE BaseGetSize; // Pointer to function returning file size + STREAM_SETSIZE BaseSetSize; // Pointer to function changing file size + STREAM_GETTIME BaseGetTime; // Pointer to function retrieving the file time + STREAM_CLOSE BaseClose; // Pointer to function closing the stream + + // Base provider data members + TBaseData Base; // Base provider data + + // Followed by stream provider data, with variable length +}; + +//----------------------------------------------------------------------------- +// Structure for linear stream + +struct TLinearStream : public TFileStream +{ + TFileBitmap * pBitmap; // Pointer to the stream bitmap +}; + +//----------------------------------------------------------------------------- +// Structure for partial stream + +struct TPartialStream : public TFileStream +{ + ULONGLONG VirtualSize; // Virtual size of the file + ULONGLONG VirtualPos; // Virtual position in the file + DWORD BlockCount; // Number of file blocks. Used by partial file stream + DWORD BlockSize; // Size of one block. Used by partial file stream + + PPART_FILE_MAP_ENTRY PartMap; // File map, variable length +}; + +//----------------------------------------------------------------------------- +// Structure for encrypted stream + +#define MPQE_CHUNK_SIZE 0x40 // Size of one chunk to be decrypted + +struct TEncryptedStream : public TFileStream +{ + BYTE Key[MPQE_CHUNK_SIZE]; // File key +}; + +#endif // __FILESTREAM_H__ diff --git a/dep/StormLib/src/SBaseCommon.cpp b/dep/StormLib/src/SBaseCommon.cpp index cd9d8ef7565..82a7f5d7eac 100644 --- a/dep/StormLib/src/SBaseCommon.cpp +++ b/dep/StormLib/src/SBaseCommon.cpp @@ -12,16 +12,14 @@ /*****************************************************************************/ #define __STORMLIB_SELF__ -#define __INCLUDE_CRYPTOGRAPHY__ #include "StormLib.h" #include "StormCommon.h" -char StormLibCopyright[] = "StormLib v " STORMLIB_VERSION_STRING " Copyright Ladislav Zezula 1998-2010"; +char StormLibCopyright[] = "StormLib v " STORMLIB_VERSION_STRING " Copyright Ladislav Zezula 1998-2012"; //----------------------------------------------------------------------------- // The buffer for decryption engine. -DWORD dwGlobalFlags = 0; // Global flags LCID lcFileLocale = LANG_NEUTRAL; // File locale USHORT wPlatform = 0; // File platform @@ -83,7 +81,7 @@ void InitializeMpqCryptography() register_hash(&sha1_desc); // Use LibTomMath as support math library for LibTomCrypt - ltc_mp = ltm_desc; + ltc_mp = ltm_desc; // Don't do that again bMpqCryptographyInitialized = true; @@ -96,7 +94,7 @@ void InitializeMpqCryptography() DWORD GetHashTableSizeForFileCount(DWORD dwFileCount) { DWORD dwPowerOfTwo; - + // Round the hash table size up to the nearest power of two for(dwPowerOfTwo = HASH_TABLE_SIZE_MIN; dwPowerOfTwo < HASH_TABLE_SIZE_MAX; dwPowerOfTwo <<= 1) { @@ -110,36 +108,6 @@ DWORD GetHashTableSizeForFileCount(DWORD dwFileCount) return HASH_TABLE_SIZE_MAX; } -//----------------------------------------------------------------------------- -// Verifies if the file name is a pseudo-name - -bool IsPseudoFileName(const char * szFileName, DWORD * pdwFileIndex) -{ - const char * szExt = strrchr(szFileName, '.'); - - // Must have an extension - if(szExt != NULL) - { - // Length of the name part must be 12 characters - if((szExt - szFileName) == 12) - { - // Must begin with "File" - if(!_strnicmp(szFileName, "File", 4)) - { - // Must be 8 digits after "File" - if(isdigit(szFileName[4]) && isdigit(szFileName[11])) - { - if(pdwFileIndex != NULL) - *pdwFileIndex = strtol(szFileName + 4, (char **)&szExt, 10); - return true; - } - } - } - } - - return false; -} - //----------------------------------------------------------------------------- // Calculates a Jenkin's Encrypting and decrypting MPQ file data @@ -184,15 +152,16 @@ ULONGLONG HashStringJenkins(const char * szFileName) //----------------------------------------------------------------------------- // This function converts the MPQ header so it always looks like version 4 -void ConvertMpqHeaderToFormat4( - TMPQArchive * ha, - ULONGLONG FileSize, - DWORD dwFlags) +int ConvertMpqHeaderToFormat4( + TMPQArchive * ha, + ULONGLONG FileSize, + DWORD dwFlags) { ULONGLONG ByteOffset; TMPQHeader * pHeader = ha->pHeader; DWORD dwExpectedArchiveSize; USHORT wFormatVersion = pHeader->wFormatVersion; + int nError = ERROR_SUCCESS; // If version 1.0 is forced, then the format version is forced to be 1.0 // Reason: Storm.dll in Warcraft III ignores format version value @@ -202,150 +171,166 @@ void ConvertMpqHeaderToFormat4( // Format-specific fixes switch(wFormatVersion) { - case MPQ_FORMAT_VERSION_1: + case MPQ_FORMAT_VERSION_1: - // Check for malformed MPQ header version 1.0 - if(pHeader->dwHeaderSize != MPQ_HEADER_SIZE_V1) - { - pHeader->dwHeaderSize = MPQ_HEADER_SIZE_V1; - ha->dwFlags |= MPQ_FLAG_PROTECTED; - } + // Check for malformed MPQ header version 1.0 + if(pHeader->dwHeaderSize != MPQ_HEADER_SIZE_V1) + { + pHeader->dwHeaderSize = MPQ_HEADER_SIZE_V1; + ha->dwFlags |= MPQ_FLAG_PROTECTED; + } - // - // The value of "dwArchiveSize" member in the MPQ header - // is ignored by Storm.dll and can contain garbage value - // ("w3xmaster" protector). - // + // + // The value of "dwArchiveSize" member in the MPQ header + // is ignored by Storm.dll and can contain garbage value + // ("w3xmaster" protector). + // - dwExpectedArchiveSize = (DWORD)(FileSize - ha->MpqPos); - if(pHeader->dwArchiveSize != dwExpectedArchiveSize) - { - // Note: dwExpectedArchiveSize might be incorrect at this point. - // MPQs version 1.0 can have strong digital signature appended at the end, - // or they might just have arbitrary data there. - // In either case, we recalculate the archive size later when - // block table is loaded and positions of all files is known. - pHeader->dwArchiveSize = dwExpectedArchiveSize; - ha->dwFlags |= MPQ_FLAG_NEED_FIX_SIZE; - } + dwExpectedArchiveSize = (DWORD)(FileSize - ha->MpqPos); + if(pHeader->dwArchiveSize != dwExpectedArchiveSize) + { + // Note: dwExpectedArchiveSize might be incorrect at this point. + // MPQs version 1.0 can have strong digital signature appended at the end, + // or they might just have arbitrary data there. + // In either case, we recalculate the archive size later when + // block table is loaded and positions of all files is known. + pHeader->dwArchiveSize = dwExpectedArchiveSize; + ha->dwFlags |= MPQ_FLAG_NEED_FIX_SIZE; + } + + // Zero the fields in 2.0 part of the MPQ header + pHeader->HiBlockTablePos64 = 0; + pHeader->wHashTablePosHi = 0; + pHeader->wBlockTablePosHi = 0; + // No break here !!! - // Zero the fields in 2.0 part of the MPQ header - pHeader->HiBlockTablePos64 = 0; - pHeader->wHashTablePosHi = 0; - pHeader->wBlockTablePosHi = 0; - // No break here !!! + case MPQ_FORMAT_VERSION_2: + case MPQ_FORMAT_VERSION_3: - case MPQ_FORMAT_VERSION_2: - case MPQ_FORMAT_VERSION_3: + // In MPQ format 3.0, the entire header is optional + // and the size of the header can actually be identical + // to size of header 2.0 + if(pHeader->dwHeaderSize < MPQ_HEADER_SIZE_V3) + { + ULONGLONG ArchiveSize64 = pHeader->dwArchiveSize; - // In MPQ format 3.0, the entire header is optional - // and the size of the header can actually be identical - // to size of header 2.0 - if(pHeader->dwHeaderSize < MPQ_HEADER_SIZE_V3) + // In format 2.0, the archive size is obsolete and is calculated + // as the highest offset of hash table, block table or hi-block table. + // However, we can still rely on it, if the size of the archive is under 4 GB + if((FileSize - ha->MpqPos) >> 32) { - ULONGLONG ArchiveSize64 = pHeader->dwArchiveSize; + ByteOffset = MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos) + (pHeader->dwHashTableSize * sizeof(TMPQHash)); + if(ByteOffset > ArchiveSize64) + ArchiveSize64 = ByteOffset; - // In format 2.0, the archive size is obsolete and is calculated - // as the highest offset of hash table, block table or hi-block table. - // However, we can still rely on it, if the size of the archive is under 4 GB - if((FileSize - ha->MpqPos) >> 32) - { - ByteOffset = MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos) + (pHeader->dwHashTableSize * sizeof(TMPQHash)); - if(ByteOffset > ArchiveSize64) - ArchiveSize64 = ByteOffset; + ByteOffset = MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos) + (pHeader->dwBlockTableSize * sizeof(TMPQBlock)); + if(ByteOffset > ArchiveSize64) + ArchiveSize64 = ByteOffset; - ByteOffset = MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos) + (pHeader->dwBlockTableSize * sizeof(TMPQBlock)); + // Only if we actually have a hi-block table + if(pHeader->HiBlockTablePos64) + { + ByteOffset = pHeader->HiBlockTablePos64 + (pHeader->dwBlockTableSize * sizeof(USHORT)); if(ByteOffset > ArchiveSize64) ArchiveSize64 = ByteOffset; - - // Only if we actually have a hi-block table - if(pHeader->HiBlockTablePos64) - { - ByteOffset = pHeader->HiBlockTablePos64 + (pHeader->dwBlockTableSize * sizeof(USHORT)); - if(ByteOffset > ArchiveSize64) - ArchiveSize64 = ByteOffset; - } - - // We need to recalculate archive size later, - // when block table is loaded and the position of files is known - ha->dwFlags |= MPQ_FLAG_NEED_FIX_SIZE; } - // Initialize the rest of the 3.0 header - pHeader->ArchiveSize64 = ArchiveSize64; - pHeader->HetTablePos64 = 0; - pHeader->BetTablePos64 = 0; + // We need to recalculate archive size later, + // when block table is loaded and the position of files is known + ha->dwFlags |= MPQ_FLAG_NEED_FIX_SIZE; } - // - // Calculate compressed size of each table. We assume the following order: - // 1) HET table - // 2) BET table - // 3) Classic hash table - // 4) Classic block table - // 5) Hi-block table - // + // Initialize the rest of the 3.0 header + pHeader->ArchiveSize64 = ArchiveSize64; + pHeader->HetTablePos64 = 0; + pHeader->BetTablePos64 = 0; + } - // Set all sizes to zero - pHeader->HetTableSize64 = 0; - pHeader->BetTableSize64 = 0; + // + // Calculate compressed size of each table. We assume the following order: + // 1) HET table + // 2) BET table + // 3) Classic hash table + // 4) Classic block table + // 5) Hi-block table + // + + // Set all sizes to zero + pHeader->HetTableSize64 = 0; + pHeader->BetTableSize64 = 0; + + // Either both HET and BET table exist or none of them does. + if(pHeader->HetTablePos64) + { + // Compressed size of the HET and BET tables + pHeader->HetTableSize64 = pHeader->BetTablePos64 - pHeader->HetTablePos64; + pHeader->BetTableSize64 = MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos) - pHeader->HetTablePos64; + } - // Either both HET and BET table exist or none of them does. - if(pHeader->HetTablePos64) - { - // Compressed size of the HET and BET tables - pHeader->HetTableSize64 = pHeader->BetTablePos64 - pHeader->HetTablePos64; - pHeader->BetTableSize64 = MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos) - pHeader->HetTablePos64; - } + // Compressed size of hash and block table + if(wFormatVersion >= MPQ_FORMAT_VERSION_2) + { + // Compressed size of the hash table + pHeader->HashTableSize64 = MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos) - MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos); - // Compressed size of hash and block table - if(wFormatVersion >= MPQ_FORMAT_VERSION_3) + // Block and hi-block table + if(pHeader->HiBlockTablePos64) { - // Compressed size of the hash table - pHeader->HashTableSize64 = MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos) - MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos); - - // Block and hi-block table - if(pHeader->HiBlockTablePos64) - { - pHeader->BlockTableSize64 = pHeader->HiBlockTablePos64 - MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); - pHeader->HiBlockTableSize64 = pHeader->ArchiveSize64 - pHeader->HiBlockTablePos64; - } - else - { - pHeader->BlockTableSize64 = pHeader->ArchiveSize64 - MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); - pHeader->HiBlockTableSize64 = 0; - } + pHeader->BlockTableSize64 = pHeader->HiBlockTablePos64 - MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); + pHeader->HiBlockTableSize64 = pHeader->ArchiveSize64 - pHeader->HiBlockTablePos64; } else { - // No known MPQ in format 1.0 has any of the tables compressed - pHeader->HashTableSize64 = pHeader->dwHashTableSize * sizeof(TMPQHash); - pHeader->BlockTableSize64 = pHeader->dwBlockTableSize * sizeof(TMPQBlock); + pHeader->BlockTableSize64 = pHeader->ArchiveSize64 - MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); pHeader->HiBlockTableSize64 = 0; } + } + else + { + // No known MPQ in format 1.0 has any of the tables compressed + pHeader->HashTableSize64 = pHeader->dwHashTableSize * sizeof(TMPQHash); + pHeader->BlockTableSize64 = pHeader->dwBlockTableSize * sizeof(TMPQBlock); + pHeader->HiBlockTableSize64 = 0; + } - // Set the data chunk size for MD5 to zero - pHeader->dwRawChunkSize = 0; + // Set the data chunk size for MD5 to zero + pHeader->dwRawChunkSize = 0; + + // Fill the MD5's + memset(pHeader->MD5_BlockTable, 0, MD5_DIGEST_SIZE); + memset(pHeader->MD5_HashTable, 0, MD5_DIGEST_SIZE); + memset(pHeader->MD5_HiBlockTable, 0, MD5_DIGEST_SIZE); + memset(pHeader->MD5_BetTable, 0, MD5_DIGEST_SIZE); + memset(pHeader->MD5_HetTable, 0, MD5_DIGEST_SIZE); + memset(pHeader->MD5_MpqHeader, 0, MD5_DIGEST_SIZE); + // No break here !!!! + + case MPQ_FORMAT_VERSION_4: + + // Verify header MD5. Header MD5 is calculated from the MPQ header since the 'MPQ\x1A' + // signature until the position of header MD5 at offset 0xC0 + if(!VerifyDataBlockHash(ha->pHeader, MPQ_HEADER_SIZE_V4 - MD5_DIGEST_SIZE, ha->pHeader->MD5_MpqHeader)) + nError = ERROR_FILE_CORRUPT; + break; + } - // Fill the MD5's - memset(pHeader->MD5_BlockTable, 0, MD5_DIGEST_SIZE); - memset(pHeader->MD5_HashTable, 0, MD5_DIGEST_SIZE); - memset(pHeader->MD5_HiBlockTable, 0, MD5_DIGEST_SIZE); - memset(pHeader->MD5_BetTable, 0, MD5_DIGEST_SIZE); - memset(pHeader->MD5_HetTable, 0, MD5_DIGEST_SIZE); - memset(pHeader->MD5_MpqHeader, 0, MD5_DIGEST_SIZE); - // No break here !!!! + return nError; +} - case MPQ_FORMAT_VERSION_4: +//----------------------------------------------------------------------------- +// Default flags for (attributes) and (listfile) - // Verify header MD5. Header MD5 is calculated from the MPQ header since the 'MPQ\x1A' - // signature until the position of header MD5 at offset 0xC0 -// if(dwHeaderSize >= MPQ_HEADER_SIZE_V4) -// wow_SFileVerifyMpqHeaderMD5(ha->pHeader); - break; - } +DWORD GetDefaultSpecialFileFlags(TMPQArchive * ha, DWORD dwFileSize) +{ + // Fixed for format 1.0 + if(ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1) + return MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED | MPQ_FILE_FIX_KEY; + + // Size-dependent for formats 2.0-4.0 + return (dwFileSize > 0x4000) ? (MPQ_FILE_COMPRESS | MPQ_FILE_SECTOR_CRC) : (MPQ_FILE_COMPRESS | MPQ_FILE_SINGLE_UNIT); } + //----------------------------------------------------------------------------- // Encrypting and decrypting MPQ file data @@ -400,16 +385,18 @@ void DecryptMpqTable(void * pvMpqTable, DWORD dwLength, const char * szKey) DecryptMpqBlock(pvMpqTable, dwLength, HashString(szKey, MPQ_HASH_FILE_KEY)); } */ -//----------------------------------------------------------------------------- -// Functions tries to get file decryption key. The trick comes from sector -// positions which are stored at the begin of each compressed file. We know the -// file size, that means we know number of sectors that means we know the first -// DWORD value in sector position. And if we know encrypted and decrypted value, -// we can find the decryption key !!! -// -// hf - MPQ file handle -// SectorOffsets - DWORD array of sector positions -// ch - Decrypted value of the first sector pos + +/** + * Functions tries to get file decryption key. The trick comes from sector + * positions which are stored at the begin of each compressed file. We know the + * file size, that means we know number of sectors that means we know the first + * DWORD value in sector position. And if we know encrypted and decrypted value, + * we can find the decryption key !!! + * + * hf - MPQ file handle + * SectorOffsets - DWORD array of sector positions + * ch - Decrypted value of the first sector pos + */ DWORD DetectFileKeyBySectorSize(LPDWORD SectorOffsets, DWORD decrypted) { @@ -458,16 +445,16 @@ DWORD DetectFileKeyByKnownContent(void * pvFileContent, DWORD nDwords, ...) DWORD saveKey1; DWORD dwTemp; DWORD i, j; - + // We need at least two DWORDS to detect the file key if(nDwords < 0x02 || nDwords > 0x10) return 0; - + va_start(argList, nDwords); for(i = 0; i < nDwords; i++) dwDecrypted[i] = va_arg(argList, DWORD); va_end(argList); - + dwTemp = (*pdwContent ^ dwDecrypted[0]) - 0xEEEEEEEE; for(i = 0; i < 0x100; i++) // Try all 256 possibilities { @@ -534,16 +521,16 @@ DWORD DetectFileKeyByContent(void * pvFileContent, DWORD dwFileSize) } DWORD DecryptFileKey( - const char * szFileName, - ULONGLONG MpqPos, - DWORD dwFileSize, - DWORD dwFlags) + const char * szFileName, + ULONGLONG MpqPos, + DWORD dwFileSize, + DWORD dwFlags) { DWORD dwFileKey; DWORD dwMpqPos = (DWORD)MpqPos; // File key is calculated from plain name - szFileName = GetPlainFileName(szFileName); + szFileName = GetPlainFileNameA(szFileName); dwFileKey = HashString(szFileName, MPQ_HASH_FILE_KEY); // Fix the key, if needed @@ -563,7 +550,7 @@ bool IsValidMpqHandle(TMPQArchive * ha) return false; if(ha->pHeader == NULL || ha->pHeader->dwID != ID_MPQ) return false; - + return (bool)(ha->pHeader->dwID == ID_MPQ); } @@ -652,8 +639,8 @@ TMPQHash * GetNextHashEntry(TMPQArchive * ha, TMPQHash * pFirstHash, TMPQHash * // Allocates an entry in the hash table DWORD AllocateHashEntry( - TMPQArchive * ha, - TFileEntry * pFileEntry) + TMPQArchive * ha, + TFileEntry * pFileEntry) { TMPQHash * pStartHash; // File hash entry (start) TMPQHash * pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize; @@ -697,58 +684,39 @@ DWORD AllocateHashEntry( // Finds a free space in the MPQ where to store next data // The free space begins beyond the file that is stored at the fuhrtest // position in the MPQ. -void FindFreeMpqSpace(TMPQArchive * ha, ULONGLONG * pMpqPos) +void FindFreeMpqSpace(TMPQArchive * ha, ULONGLONG * pFreeSpacePos) { TMPQHeader * pHeader = ha->pHeader; TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; TFileEntry * pFileEntry = ha->pFileTable; - TFileEntry * pTempEntry1 = NULL; - TFileEntry * pTempEntry2 = NULL; - ULONGLONG MpqPos = ha->pHeader->dwHeaderSize; + ULONGLONG FreeSpacePos = ha->pHeader->dwHeaderSize; DWORD dwChunkCount; - // If the listfile is not saved yet, we invalidate the file entry for it - if(!(ha->dwFlags & MPQ_FLAG_LISTFILE_VALID)) - pTempEntry1 = GetFileEntryExact(ha, LISTFILE_NAME, LANG_NEUTRAL); - - // If attributes file is not saved yet, we invalidate the file entry for it - if(!(ha->dwFlags & MPQ_FLAG_ATTRIBS_VALID)) - pTempEntry2 = GetFileEntryExact(ha, ATTRIBUTES_NAME, LANG_NEUTRAL); - // Parse the entire block table for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) { // Only take existing files if(pFileEntry->dwFlags & MPQ_FILE_EXISTS) { - // If (listfile) and/or (attributes) are not saved yet, ignore - // their entries. As result, the space for both files in the MPQ - // is reused and thus produces less amount of gaps - // created when adding new files to the MPQ - if(pFileEntry != pTempEntry1 && pFileEntry != pTempEntry2) + // If the end of the file is bigger than current MPQ table pos, update it + if((pFileEntry->ByteOffset + pFileEntry->dwCmpSize) > FreeSpacePos) { - // If the end of the file is bigger than current MPQ table pos, update it - if((pFileEntry->ByteOffset + pFileEntry->dwCmpSize) > MpqPos) - { - // Get the end of the file data - MpqPos = pFileEntry->ByteOffset + pFileEntry->dwCmpSize; + // Get the end of the file data + FreeSpacePos = pFileEntry->ByteOffset + pFileEntry->dwCmpSize; - // Add the MD5 chunks, if present - if(pHeader->dwRawChunkSize != 0) - { - dwChunkCount = pFileEntry->dwCmpSize / pHeader->dwRawChunkSize; - if(pFileEntry->dwCmpSize % pHeader->dwRawChunkSize) - dwChunkCount++; - MpqPos += dwChunkCount * MD5_DIGEST_SIZE; - } + // Add the MD5 chunks, if present + if(pHeader->dwRawChunkSize != 0 && pFileEntry->dwCmpSize != 0) + { + dwChunkCount = ((pFileEntry->dwCmpSize - 1) / pHeader->dwRawChunkSize) + 1; + FreeSpacePos += dwChunkCount * MD5_DIGEST_SIZE; } } } } // Give the free space position to the caller - if(pMpqPos != NULL) - *pMpqPos = MpqPos; + if(pFreeSpacePos != NULL) + *pFreeSpacePos = FreeSpacePos; } //----------------------------------------------------------------------------- @@ -759,7 +727,7 @@ TMPQFile * CreateMpqFile(TMPQArchive * ha) TMPQFile * hf; // Allocate space for TMPQFile - hf = ALLOCMEM(TMPQFile, 1); + hf = STORM_ALLOC(TMPQFile, 1); if(hf != NULL) { // Fill the file structure @@ -775,14 +743,13 @@ TMPQFile * CreateMpqFile(TMPQArchive * ha) // Loads a table from MPQ. // Can be used for hash table, block table, sector offset table or sector checksum table int LoadMpqTable( - TMPQArchive * ha, - ULONGLONG ByteOffset, - void * pvTable, - DWORD dwCompressedSize, - DWORD dwRealSize, - DWORD dwKey) + TMPQArchive * ha, + ULONGLONG ByteOffset, + void * pvTable, + DWORD dwCompressedSize, + DWORD dwRealSize, + DWORD dwKey) { - ULONGLONG ByteOffsetLi; LPBYTE pbCompressed = NULL; LPBYTE pbToRead = (LPBYTE)pvTable; int nError = ERROR_SUCCESS; @@ -792,7 +759,7 @@ int LoadMpqTable( if(dwCompressedSize < dwRealSize) { // Allocate temporary buffer for holding compressed data - pbCompressed = ALLOCMEM(BYTE, dwCompressedSize); + pbCompressed = STORM_ALLOC(BYTE, dwCompressedSize); if(pbCompressed == NULL) return ERROR_NOT_ENOUGH_MEMORY; @@ -801,8 +768,7 @@ int LoadMpqTable( } // Read the table - ByteOffsetLi = ByteOffset; - if(FileStream_Read(ha->pStream, &ByteOffsetLi, pbToRead, dwCompressedSize)) + if(FileStream_Read(ha->pStream, &ByteOffset, pbToRead, dwCompressedSize)) { // First of all, decrypt the table if(dwKey != 0) @@ -818,11 +784,11 @@ int LoadMpqTable( int cbOutBuffer = (int)dwRealSize; int cbInBuffer = (int)dwCompressedSize; - if(!SCompDecompress((char *)pvTable, &cbOutBuffer, (char *)pbCompressed, cbInBuffer)) + if(!SCompDecompress2((char *)pvTable, &cbOutBuffer, (char *)pbCompressed, cbInBuffer)) nError = GetLastError(); // Free the temporary buffer - FREEMEM(pbCompressed); + STORM_FREE(pbCompressed); } } else @@ -835,9 +801,9 @@ int LoadMpqTable( } void CalculateRawSectorOffset( - ULONGLONG & RawFilePos, - TMPQFile * hf, - DWORD dwSectorOffset) + ULONGLONG & RawFilePos, + TMPQFile * hf, + DWORD dwSectorOffset) { // // Some MPQ protectors place the sector offset table after the actual file data. @@ -865,24 +831,22 @@ void CalculateRawSectorOffset( } unsigned char * AllocateMd5Buffer( - DWORD dwRawDataSize, - DWORD dwChunkSize, - LPDWORD pcbMd5Size) + DWORD dwRawDataSize, + DWORD dwChunkSize, + LPDWORD pcbMd5Size) { unsigned char * md5_array; DWORD cbMd5Size; // Sanity check + assert(dwRawDataSize != 0); assert(dwChunkSize != 0); // Calculate how many MD5's we will calculate - cbMd5Size = dwRawDataSize / dwChunkSize; - if(dwRawDataSize % dwChunkSize) - cbMd5Size++; - cbMd5Size *= MD5_DIGEST_SIZE; + cbMd5Size = (((dwRawDataSize - 1) / dwChunkSize) + 1) * MD5_DIGEST_SIZE; // Allocate space for array or MD5s - md5_array = ALLOCMEM(BYTE, cbMd5Size); + md5_array = STORM_ALLOC(BYTE, cbMd5Size); // Give the size of the MD5 array if(pcbMd5Size != NULL) @@ -898,12 +862,15 @@ int AllocateSectorBuffer(TMPQFile * hf) // Caller of AllocateSectorBuffer must ensure these assert(hf->pbFileSector == NULL); assert(hf->pFileEntry != NULL); - assert(hf->dwDataSize != 0); assert(hf->ha != NULL); + // Don't allocate anything if the file has zero size + if(hf->pFileEntry->dwFileSize == 0 || hf->dwDataSize == 0) + return ERROR_SUCCESS; + // Determine the file sector size and allocate buffer for it hf->dwSectorSize = (hf->pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT) ? hf->dwDataSize : ha->dwSectorSize; - hf->pbFileSector = ALLOCMEM(BYTE, hf->dwSectorSize); + hf->pbFileSector = STORM_ALLOC(BYTE, hf->dwSectorSize); hf->dwSectorOffs = SFILE_INVALID_POS; // Return result @@ -924,7 +891,7 @@ int AllocatePatchInfo(TMPQFile * hf, bool bLoadFromFile) // Allocate space for patch header. Start with default size, // and if its size if bigger, then we reload them - hf->pPatchInfo = (TPatchInfo *)ALLOCMEM(BYTE, dwLength); + hf->pPatchInfo = (TPatchInfo *)STORM_ALLOC(BYTE, dwLength); if(hf->pPatchInfo == NULL) return ERROR_NOT_ENOUGH_MEMORY; @@ -934,8 +901,8 @@ int AllocatePatchInfo(TMPQFile * hf, bool bLoadFromFile) // Load the patch header if(!FileStream_Read(ha->pStream, &hf->RawFilePos, hf->pPatchInfo, dwLength)) { - // Free the sector offsets - FREEMEM(hf->pPatchInfo); + // Free the patch info + STORM_FREE(hf->pPatchInfo); hf->pPatchInfo = NULL; return GetLastError(); } @@ -949,10 +916,14 @@ int AllocatePatchInfo(TMPQFile * hf, bool bLoadFromFile) // If it's not default size, we have to reload them if(hf->pPatchInfo->dwLength > dwLength) { + // Free the patch info dwLength = hf->pPatchInfo->dwLength; - FREEMEM(hf->pPatchInfo); + STORM_FREE(hf->pPatchInfo); hf->pPatchInfo = NULL; + // If the length is out of all possible ranges, fail the operation + if(dwLength > 0x400) + return ERROR_FILE_CORRUPT; goto __AllocateAndLoadPatchInfo; } @@ -976,6 +947,7 @@ int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile) TMPQArchive * ha = hf->ha; TFileEntry * pFileEntry = hf->pFileEntry; DWORD dwSectorOffsLen; + bool bSectorOffsetTableCorrupt = false; // Caller of AllocateSectorOffsets must ensure these assert(hf->SectorOffsets == NULL); @@ -991,23 +963,25 @@ int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile) } // Calculate the number of data sectors - hf->dwSectorCount = (hf->dwDataSize / hf->dwSectorSize); - if(hf->dwDataSize % hf->dwSectorSize) - hf->dwSectorCount++; + // Note that this doesn't work if the file size is zero + hf->dwSectorCount = ((hf->dwDataSize - 1) / hf->dwSectorSize) + 1; // Calculate the number of file sectors - dwSectorOffsLen = hf->dwSectorCount * sizeof(DWORD); + dwSectorOffsLen = (hf->dwSectorCount + 1) * sizeof(DWORD); + + // If MPQ_FILE_SECTOR_CRC flag is set, there will either be extra DWORD + // or an array of MD5's. Either way, we read at least 4 bytes more + // in order to save additional read from the file. if(pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC) dwSectorOffsLen += sizeof(DWORD); - dwSectorOffsLen += sizeof(DWORD); // Only allocate and load the table if the file is compressed if(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED) { - __LoadSectorOffsets: +__LoadSectorOffsets: // Allocate the sector offset table - hf->SectorOffsets = (DWORD *)ALLOCMEM(BYTE, dwSectorOffsLen); + hf->SectorOffsets = (DWORD *)STORM_ALLOC(BYTE, dwSectorOffsLen); if(hf->SectorOffsets == NULL) return ERROR_NOT_ENOUGH_MEMORY; @@ -1023,7 +997,7 @@ int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile) if(!FileStream_Read(ha->pStream, &RawFilePos, hf->SectorOffsets, dwSectorOffsLen)) { // Free the sector offsets - FREEMEM(hf->SectorOffsets); + STORM_FREE(hf->SectorOffsets); hf->SectorOffsets = NULL; return GetLastError(); } @@ -1040,7 +1014,7 @@ int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile) hf->dwFileKey = DetectFileKeyBySectorSize(hf->SectorOffsets, dwSectorOffsLen); if(hf->dwFileKey == 0) { - FREEMEM(hf->SectorOffsets); + STORM_FREE(hf->SectorOffsets); hf->SectorOffsets = NULL; return ERROR_UNKNOWN_FILE_KEY; } @@ -1051,41 +1025,55 @@ int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile) } // - // I've seen MPQs that had MPQ_FILE_SECTOR_CRC flag absent, - // but there was one extra entry in the sector offset table - // (Example: expansion-locale-frFR.MPQ from WoW Cataclysm BETA) - // We detect such behavior here by verifying the value - // of the first entry in the sector offset table - // - // Also, the (attributes) file from patch MPQs from WoW tends - // to have some additional values in the sector offset table, - // with an unknown meaning + // Validate the sector offset table + // + // Note: Some MPQ protectors put the actual file data before the sector offset table. + // In this case, the sector offsets are negative (> 0x80000000). // - if(hf->SectorOffsets[0] > dwSectorOffsLen) + for(DWORD i = 0; i < hf->dwSectorCount; i++) { - // Get the real size of the sector offset table - dwSectorOffsLen = hf->SectorOffsets[0]; + DWORD dwSectorOffset1 = hf->SectorOffsets[i+1]; + DWORD dwSectorOffset0 = hf->SectorOffsets[i]; - // Free the current sector offset table - FREEMEM(hf->SectorOffsets); - hf->SectorOffsets = NULL; + // Every following sector offset must be bigger than the previous one + if(dwSectorOffset1 <= dwSectorOffset0) + { + bSectorOffsetTableCorrupt = true; + break; + } - // Increment number of data sectors by 1 and retry - goto __LoadSectorOffsets; + // The sector size must not be bigger than compressed file size + if((dwSectorOffset1 - dwSectorOffset0) > pFileEntry->dwCmpSize) + { + bSectorOffsetTableCorrupt = true; + break; + } + } + + // If data corruption detected, free the sector offset table + if(bSectorOffsetTableCorrupt) + { + STORM_FREE(hf->SectorOffsets); + hf->SectorOffsets = NULL; + return ERROR_FILE_CORRUPT; } // - // Check if the sector positions are correct. - // I saw a protector who puts negative offset into the sector offset table. - // Because there are always at least 2 sector offsets, we can check their difference + // There may be various extra DWORDs loaded after the sector offset table. + // They are mostly empty on WoW release MPQs, but on MPQs from PTR, + // they contain random non-zero data. Their meaning is unknown. + // + // These extra values are, however, include in the dwCmpSize in the file + // table. We cannot ignore them, because compacting archive would fail // - if((hf->SectorOffsets[1] - hf->SectorOffsets[0]) > ha->dwSectorSize) + if(hf->SectorOffsets[0] > dwSectorOffsLen) { - FREEMEM(hf->SectorOffsets); + dwSectorOffsLen = hf->SectorOffsets[0]; + STORM_FREE(hf->SectorOffsets); hf->SectorOffsets = NULL; - return ERROR_FILE_CORRUPT; + goto __LoadSectorOffsets; } } else @@ -1103,9 +1091,9 @@ int AllocateSectorChecksums(TMPQFile * hf, bool bLoadFromFile) TMPQArchive * ha = hf->ha; TFileEntry * pFileEntry = hf->pFileEntry; ULONGLONG RawFilePos; - DWORD dwCompressedSize; + DWORD dwCompressedSize = 0; + DWORD dwExpectedSize; DWORD dwCrcOffset; // Offset of the CRC table, relative to file offset in the MPQ - DWORD dwLastIndex; DWORD dwCrcSize; // Caller of AllocateSectorChecksums must ensure these @@ -1121,42 +1109,53 @@ int AllocateSectorChecksums(TMPQFile * hf, bool bLoadFromFile) // Caller must ensure that we are only called when we have sector checksums assert(pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC); - // If we only have to allocate the buffer, do it - if(bLoadFromFile == false) + // + // Older MPQs store an array of CRC32's after + // the raw file data in the MPQ. + // + // In newer MPQs, the (since Cataclysm BETA) the (attributes) file + // contains additional 32-bit values beyond the sector table. + // Their number depends on size of the (attributes), but their + // meaning is unknown. They are usually zeroed in retail game files, + // but contain some sort of checksum in BETA MPQs + // + + // Does the size of the file table match with the CRC32-based checksums? + dwExpectedSize = (hf->dwSectorCount + 2) * sizeof(DWORD); + if(hf->SectorOffsets[0] == dwExpectedSize) { - // Allocate buffer for sector checksums - hf->SectorChksums = ALLOCMEM(DWORD, hf->dwSectorCount); - if(hf->SectorChksums == NULL) - return ERROR_NOT_ENOUGH_MEMORY; + // Is there valid size of the sector checksums? + if(hf->SectorOffsets[hf->dwSectorCount + 1] >= hf->SectorOffsets[hf->dwSectorCount]) + dwCompressedSize = hf->SectorOffsets[hf->dwSectorCount + 1] - hf->SectorOffsets[hf->dwSectorCount]; - memset(hf->SectorChksums, 0, hf->dwSectorCount * sizeof(DWORD)); - return ERROR_SUCCESS; - } + // Ignore cases when the length is too small or too big. + if(dwCompressedSize < sizeof(DWORD) || dwCompressedSize > hf->dwSectorSize) + return ERROR_SUCCESS; - // Note: I've seen files that had sector offset table with - // the size of 0x20 bytes, but they supposed to be only 0x08 - // (dwDataSize = 0x000059ea, dwSectorSize = 0x00004000) - // Probable cause: This is a file that is to be downloaded - // from the server as soon as it's accessed by the game. - dwLastIndex = (hf->SectorOffsets[0] / sizeof(DWORD)) - 2; - dwCompressedSize = hf->SectorOffsets[dwLastIndex + 1] - hf->SectorOffsets[dwLastIndex]; + // Allocate the array for the sector checksums + hf->SectorChksums = STORM_ALLOC(DWORD, hf->dwSectorCount); + if(hf->SectorChksums == NULL) + return ERROR_NOT_ENOUGH_MEMORY; - // Check size of the checksums. If zero, there aren't any - if(dwCompressedSize == 0) - return ERROR_SUCCESS; + // If we are not supposed to load it from the file, allocate empty buffer + if(bLoadFromFile == false) + { + memset(hf->SectorChksums, 0, hf->dwSectorCount * sizeof(DWORD)); + return ERROR_SUCCESS; + } - // Allocate buffer for sector CRCs - hf->SectorChksums = ALLOCMEM(DWORD, hf->dwSectorCount); - if(hf->SectorChksums == NULL) - return ERROR_NOT_ENOUGH_MEMORY; + // Calculate offset of the CRC table + dwCrcSize = hf->dwSectorCount * sizeof(DWORD); + dwCrcOffset = hf->SectorOffsets[hf->dwSectorCount]; + CalculateRawSectorOffset(RawFilePos, hf, dwCrcOffset); - // Calculate offset of the CRC table - dwCrcSize = hf->dwSectorCount * sizeof(DWORD); - dwCrcOffset = hf->SectorOffsets[hf->dwSectorCount]; - CalculateRawSectorOffset(RawFilePos, hf, dwCrcOffset); + // Now read the table from the MPQ + return LoadMpqTable(ha, RawFilePos, hf->SectorChksums, dwCompressedSize, dwCrcSize, 0); + } - // Now read the table from the MPQ - return LoadMpqTable(ha, RawFilePos, hf->SectorChksums, dwCompressedSize, dwCrcSize, 0); + // If the size doesn't match, we ignore sector checksums + // assert(false); + return ERROR_SUCCESS; } int WritePatchInfo(TMPQFile * hf) @@ -1201,10 +1200,10 @@ int WriteSectorOffsets(TMPQFile * hf) // Write sector offsets to the archive if(!FileStream_Write(ha->pStream, &RawFilePos, hf->SectorOffsets, dwSectorOffsLen)) return GetLastError(); - + // Not necessary, as the sector checksums // are going to be freed when this is done. -// BSWAP_ARRAY32_UNSIGNED(hf->SectorOffsets, dwSectorOffsLen); + // BSWAP_ARRAY32_UNSIGNED(hf->SectorOffsets, dwSectorOffsLen); return ERROR_SUCCESS; } @@ -1238,7 +1237,7 @@ int WriteSectorChecksums(TMPQFile * hf) dwCrcSize = hf->dwSectorCount * sizeof(DWORD); // Allocate buffer for compressed sector CRCs. - pbCompressed = ALLOCMEM(BYTE, dwCrcSize); + pbCompressed = STORM_ALLOC(BYTE, dwCrcSize); if(pbCompressed == NULL) return ERROR_NOT_ENOUGH_MEMORY; @@ -1258,24 +1257,23 @@ int WriteSectorChecksums(TMPQFile * hf) // Not necessary, as the sector checksums // are going to be freed when this is done. -// BSWAP_ARRAY32_UNSIGNED(hf->SectorChksums, dwCrcSize); + // BSWAP_ARRAY32_UNSIGNED(hf->SectorChksums, dwCrcSize); - // Store the sector CRCs + // Store the sector CRCs hf->SectorOffsets[hf->dwSectorCount + 1] = hf->SectorOffsets[hf->dwSectorCount] + dwCompressedSize; pFileEntry->dwCmpSize += dwCompressedSize; - FREEMEM(pbCompressed); + STORM_FREE(pbCompressed); return nError; } int WriteMemDataMD5( - TFileStream * pStream, - ULONGLONG RawDataOffs, - void * pvRawData, - DWORD dwRawDataSize, - DWORD dwChunkSize, - LPDWORD pcbTotalSize) + TFileStream * pStream, + ULONGLONG RawDataOffs, + void * pvRawData, + DWORD dwRawDataSize, + DWORD dwChunkSize, + LPDWORD pcbTotalSize) { - hash_state md5_state; unsigned char * md5_array; unsigned char * md5; LPBYTE pbRawData = (LPBYTE)pvRawData; @@ -1295,9 +1293,7 @@ int WriteMemDataMD5( dwChunkSize = STORMLIB_MIN(dwBytesRemaining, dwChunkSize); // Calculate MD5 - md5_init(&md5_state); - md5_process(&md5_state, (unsigned char *)pbRawData, dwChunkSize); - md5_done(&md5_state, md5); + CalculateDataBlockHash(pbRawData, dwChunkSize, md5); md5 += MD5_DIGEST_SIZE; // Move offset and size @@ -1315,19 +1311,18 @@ int WriteMemDataMD5( *pcbTotalSize = dwRawDataSize + dwMd5ArraySize; // Free buffers and exit - FREEMEM(md5_array); + STORM_FREE(md5_array); return nError; } // Writes the MD5 for each chunk of the raw file data int WriteMpqDataMD5( - TFileStream * pStream, - ULONGLONG RawDataOffs, - DWORD dwRawDataSize, - DWORD dwChunkSize) + TFileStream * pStream, + ULONGLONG RawDataOffs, + DWORD dwRawDataSize, + DWORD dwChunkSize) { - hash_state md5_state; unsigned char * md5_array; unsigned char * md5; LPBYTE pbFileChunk; @@ -1341,10 +1336,10 @@ int WriteMpqDataMD5( return ERROR_NOT_ENOUGH_MEMORY; // Allocate space for file chunk - pbFileChunk = ALLOCMEM(BYTE, dwChunkSize); + pbFileChunk = STORM_ALLOC(BYTE, dwChunkSize); if(pbFileChunk == NULL) { - FREEMEM(md5_array); + STORM_FREE(md5_array); return ERROR_NOT_ENOUGH_MEMORY; } @@ -1362,9 +1357,7 @@ int WriteMpqDataMD5( } // Calculate MD5 - md5_init(&md5_state); - md5_process(&md5_state, (unsigned char *)pbFileChunk, dwToRead); - md5_done(&md5_state, md5); + CalculateDataBlockHash(pbFileChunk, dwToRead, md5); md5 += MD5_DIGEST_SIZE; // Move offset and size @@ -1380,8 +1373,8 @@ int WriteMpqDataMD5( } // Free buffers and exit - FREEMEM(pbFileChunk); - FREEMEM(md5_array); + STORM_FREE(pbFileChunk); + STORM_FREE(md5_array); return nError; } @@ -1396,19 +1389,19 @@ void FreeMPQFile(TMPQFile *& hf) // Then free all buffers allocated in the file structure if(hf->pPatchHeader != NULL) - FREEMEM(hf->pPatchHeader); + STORM_FREE(hf->pPatchHeader); if(hf->pbFileData != NULL) - FREEMEM(hf->pbFileData); + STORM_FREE(hf->pbFileData); if(hf->pPatchInfo != NULL) - FREEMEM(hf->pPatchInfo); + STORM_FREE(hf->pPatchInfo); if(hf->SectorOffsets != NULL) - FREEMEM(hf->SectorOffsets); + STORM_FREE(hf->SectorOffsets); if(hf->SectorChksums != NULL) - FREEMEM(hf->SectorChksums); + STORM_FREE(hf->SectorChksums); if(hf->pbFileSector != NULL) - FREEMEM(hf->pbFileSector); + STORM_FREE(hf->pbFileSector); FileStream_Close(hf->pStream); - FREEMEM(hf); + STORM_FREE(hf); hf = NULL; } } @@ -1422,39 +1415,58 @@ void FreeMPQArchive(TMPQArchive *& ha) if(ha->haPatch != NULL) FreeMPQArchive(ha->haPatch); + // Close the file stream + FileStream_Close(ha->pStream); + ha->pStream = NULL; + // Free the file names from the file table if(ha->pFileTable != NULL) { for(DWORD i = 0; i < ha->dwFileTableSize; i++) { if(ha->pFileTable[i].szFileName != NULL) - FREEMEM(ha->pFileTable[i].szFileName); + STORM_FREE(ha->pFileTable[i].szFileName); ha->pFileTable[i].szFileName = NULL; } // Then free all buffers allocated in the archive structure - FREEMEM(ha->pFileTable); + STORM_FREE(ha->pFileTable); } + if(ha->pBitmap != NULL) + STORM_FREE(ha->pBitmap); if(ha->pHashTable != NULL) - FREEMEM(ha->pHashTable); + STORM_FREE(ha->pHashTable); if(ha->pHetTable != NULL) FreeHetTable(ha->pHetTable); - FileStream_Close(ha->pStream); - FREEMEM(ha); + STORM_FREE(ha); ha = NULL; } } -const char * GetPlainFileName(const char * szFileName) +const char * GetPlainFileNameA(const char * szFileName) { - const char * szPlainName = szFileName + strlen(szFileName); + const char * szPlainName = szFileName; - while(szPlainName > szFileName) + while(*szFileName != 0) { - if(szPlainName[0] == '\\' || szPlainName[0] == '/') - return szPlainName + 1; - szPlainName--; + if(*szFileName == '\\' || *szFileName == '/') + szPlainName = szFileName + 1; + szFileName++; + } + + return szPlainName; +} + +const TCHAR * GetPlainFileNameT(const TCHAR * szFileName) +{ + const TCHAR * szPlainName = szFileName; + + while(*szFileName != 0) + { + if(*szFileName == '\\' || *szFileName == '/') + szPlainName = szFileName + 1; + szFileName++; } return szPlainName; @@ -1462,11 +1474,11 @@ const char * GetPlainFileName(const char * szFileName) bool IsInternalMpqFileName(const char * szFileName) { - if(szFileName[0] == '(') + if(szFileName != NULL && szFileName[0] == '(') { if(!_stricmp(szFileName, LISTFILE_NAME) || - !_stricmp(szFileName, ATTRIBUTES_NAME) || - !_stricmp(szFileName, SIGNATURE_NAME)) + !_stricmp(szFileName, ATTRIBUTES_NAME) || + !_stricmp(szFileName, SIGNATURE_NAME)) { return true; } @@ -1475,6 +1487,79 @@ bool IsInternalMpqFileName(const char * szFileName) return false; } +// Verifies if the file name is a pseudo-name +bool IsPseudoFileName(const char * szFileName, DWORD * pdwFileIndex) +{ + DWORD dwFileIndex = 0; + + if(szFileName != NULL) + { + // Must be "File########.ext" + if(!_strnicmp(szFileName, "File", 4)) + { + // Check 8 digits + for(int i = 4; i < 4+8; i++) + { + if(szFileName[i] < '0' || szFileName[i] > '9') + return false; + dwFileIndex = (dwFileIndex * 10) + (szFileName[i] - '0'); + } + + // An extension must follow + if(szFileName[12] == '.') + { + if(pdwFileIndex != NULL) + *pdwFileIndex = dwFileIndex; + return true; + } + } + } + + // Not a pseudo-name + return false; +} + +//----------------------------------------------------------------------------- +// Functions calculating and verifying the MD5 signature + +bool IsValidMD5(LPBYTE pbMd5) +{ + BYTE BitSummary = 0; + + // The MD5 is considered invalid of it is zeroed + BitSummary |= pbMd5[0x00] | pbMd5[0x01] | pbMd5[0x02] | pbMd5[0x03] | pbMd5[0x04] | pbMd5[0x05] | pbMd5[0x06] | pbMd5[0x07]; + BitSummary |= pbMd5[0x08] | pbMd5[0x09] | pbMd5[0x0A] | pbMd5[0x0B] | pbMd5[0x0C] | pbMd5[0x0D] | pbMd5[0x0E] | pbMd5[0x0F]; + return (BitSummary != 0); +} + +bool VerifyDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE expected_md5) +{ + hash_state md5_state; + BYTE md5_digest[MD5_DIGEST_SIZE]; + + // Don't verify the block if the MD5 is not valid. + if(!IsValidMD5(expected_md5)) + return true; + + // Calculate the MD5 of the data block + md5_init(&md5_state); + md5_process(&md5_state, (unsigned char *)pvDataBlock, cbDataBlock); + md5_done(&md5_state, md5_digest); + + // Does the MD5's match? + return (memcmp(md5_digest, expected_md5, MD5_DIGEST_SIZE) == 0); +} + +void CalculateDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE md5_hash) +{ + hash_state md5_state; + + md5_init(&md5_state); + md5_process(&md5_state, (unsigned char *)pvDataBlock, cbDataBlock); + md5_done(&md5_state, md5_hash); +} + + //----------------------------------------------------------------------------- // Swapping functions @@ -1488,37 +1573,37 @@ bool IsInternalMpqFileName(const char * szFileName) // Swaps a signed 16-bit integer int16_t SwapInt16(uint16_t data) { - return (int16_t)CFSwapInt16(data); + return (int16_t)CFSwapInt16(data); } // Swaps an unsigned 16-bit integer uint16_t SwapUInt16(uint16_t data) { - return CFSwapInt16(data); + return CFSwapInt16(data); } // Swaps signed 32-bit integer int32_t SwapInt32(uint32_t data) { - return (int32_t)CFSwapInt32(data); + return (int32_t)CFSwapInt32(data); } // Swaps an unsigned 32-bit integer uint32_t SwapUInt32(uint32_t data) { - return CFSwapInt32(data); + return CFSwapInt32(data); } // Swaps signed 64-bit integer int64_t SwapInt64(int64_t data) { - return (int64_t)CFSwapInt64(data); + return (int64_t)CFSwapInt64(data); } // Swaps an unsigned 64-bit integer uint64_t SwapUInt64(uint64_t data) { - return CFSwapInt64(data); + return CFSwapInt64(data); } // Swaps array of unsigned 16-bit integers @@ -1528,10 +1613,10 @@ void ConvertUInt16Buffer(void * ptr, size_t length) uint32_t nElements = (uint32_t)(length / sizeof(uint16_t)); while(nElements-- > 0) - { - *buffer = SwapUInt16(*buffer); - buffer++; - } + { + *buffer = SwapUInt16(*buffer); + buffer++; + } } // Swaps array of unsigned 32-bit integers @@ -1540,11 +1625,11 @@ void ConvertUInt32Buffer(void * ptr, size_t length) uint32_t * buffer = (uint32_t *)ptr; uint32_t nElements = (uint32_t)(length / sizeof(uint32_t)); - while(nElements-- > 0) - { - *buffer = SwapUInt32(*buffer); - buffer++; - } + while(nElements-- > 0) + { + *buffer = SwapUInt32(*buffer); + buffer++; + } } // Swaps array of unsigned 64-bit integers @@ -1553,55 +1638,55 @@ void ConvertUInt64Buffer(void * ptr, size_t length) uint64_t * buffer = (uint64_t *)ptr; uint32_t nElements = (uint32_t)(length / sizeof(uint64_t)); - while(nElements-- > 0) - { - *buffer = SwapUInt64(*buffer); - buffer++; - } + while(nElements-- > 0) + { + *buffer = SwapUInt64(*buffer); + buffer++; + } } // Swaps the TMPQUserData structure void ConvertTMPQUserData(void *userData) { - TMPQUserData * theData = (TMPQUserData *)userData; + TMPQUserData * theData = (TMPQUserData *)userData; - theData->dwID = SwapUInt32(theData->dwID); - theData->cbUserDataSize = SwapUInt32(theData->cbUserDataSize); - theData->dwHeaderOffs = SwapUInt32(theData->dwHeaderOffs); - theData->cbUserDataHeader = SwapUInt32(theData->cbUserDataHeader); + theData->dwID = SwapUInt32(theData->dwID); + theData->cbUserDataSize = SwapUInt32(theData->cbUserDataSize); + theData->dwHeaderOffs = SwapUInt32(theData->dwHeaderOffs); + theData->cbUserDataHeader = SwapUInt32(theData->cbUserDataHeader); } // Swaps the TMPQHeader structure void ConvertTMPQHeader(void *header) { - TMPQHeader * theHeader = (TMPQHeader *)header; - - theHeader->dwID = SwapUInt32(theHeader->dwID); - theHeader->dwHeaderSize = SwapUInt32(theHeader->dwHeaderSize); - theHeader->dwArchiveSize = SwapUInt32(theHeader->dwArchiveSize); - theHeader->wFormatVersion = SwapUInt16(theHeader->wFormatVersion); - theHeader->wSectorSize = SwapUInt16(theHeader->wSectorSize); - theHeader->dwHashTablePos = SwapUInt32(theHeader->dwHashTablePos); - theHeader->dwBlockTablePos = SwapUInt32(theHeader->dwBlockTablePos); - theHeader->dwHashTableSize = SwapUInt32(theHeader->dwHashTableSize); - theHeader->dwBlockTableSize = SwapUInt32(theHeader->dwBlockTableSize); - - if(theHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2) - { - // Swap the hi-block table position - theHeader->HiBlockTablePos64 = SwapUInt64(theHeader->HiBlockTablePos64); - + TMPQHeader * theHeader = (TMPQHeader *)header; + + theHeader->dwID = SwapUInt32(theHeader->dwID); + theHeader->dwHeaderSize = SwapUInt32(theHeader->dwHeaderSize); + theHeader->dwArchiveSize = SwapUInt32(theHeader->dwArchiveSize); + theHeader->wFormatVersion = SwapUInt16(theHeader->wFormatVersion); + theHeader->wSectorSize = SwapUInt16(theHeader->wSectorSize); + theHeader->dwHashTablePos = SwapUInt32(theHeader->dwHashTablePos); + theHeader->dwBlockTablePos = SwapUInt32(theHeader->dwBlockTablePos); + theHeader->dwHashTableSize = SwapUInt32(theHeader->dwHashTableSize); + theHeader->dwBlockTableSize = SwapUInt32(theHeader->dwBlockTableSize); + + if(theHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2) + { + // Swap the hi-block table position + theHeader->HiBlockTablePos64 = SwapUInt64(theHeader->HiBlockTablePos64); + theHeader->wHashTablePosHi = SwapUInt16(theHeader->wHashTablePosHi); - theHeader->wBlockTablePosHi = SwapUInt16(theHeader->wBlockTablePosHi); + theHeader->wBlockTablePosHi = SwapUInt16(theHeader->wBlockTablePosHi); if(theHeader->wFormatVersion >= MPQ_FORMAT_VERSION_3) - { + { theHeader->ArchiveSize64 = SwapUInt64(theHeader->ArchiveSize64); theHeader->BetTablePos64 = SwapUInt64(theHeader->BetTablePos64); theHeader->HetTablePos64 = SwapUInt64(theHeader->HetTablePos64); if(theHeader->wFormatVersion >= MPQ_FORMAT_VERSION_4) - { + { theHeader->HashTableSize64 = SwapUInt64(theHeader->HashTableSize64); theHeader->BlockTableSize64 = SwapUInt64(theHeader->BlockTableSize64); theHeader->HiBlockTableSize64 = SwapUInt64(theHeader->HiBlockTableSize64); @@ -1609,7 +1694,7 @@ void ConvertTMPQHeader(void *header) theHeader->BetTableSize64 = SwapUInt64(theHeader->BetTableSize64); } } - } + } } #endif // PLATFORM_LITTLE_ENDIAN diff --git a/dep/StormLib/src/SBaseDumpData.cpp b/dep/StormLib/src/SBaseDumpData.cpp index f2b5fad1e8f..9b6537bd3aa 100644 --- a/dep/StormLib/src/SBaseDumpData.cpp +++ b/dep/StormLib/src/SBaseDumpData.cpp @@ -17,18 +17,18 @@ void DumpMpqHeader(TMPQHeader * pHeader) { printf("== MPQ Header =================================\n"); - printf("DWORD dwID = %08lX\n", pHeader->dwID); - printf("DWORD dwHeaderSize = %08lX\n", pHeader->dwHeaderSize); - printf("DWORD dwArchiveSize = %08lX\n", pHeader->dwArchiveSize); - printf("USHORT wFormatVersion = %04lX\n", pHeader->wFormatVersion); - printf("USHORT wSectorSize = %04lX\n", pHeader->wSectorSize); - printf("DWORD dwHashTablePos = %08lX\n", pHeader->dwHashTablePos); - printf("DWORD dwBlockTablePos = %08lX\n", pHeader->dwBlockTablePos); - printf("DWORD dwHashTableSize = %08lX\n", pHeader->dwHashTableSize); - printf("DWORD dwBlockTableSize = %08lX\n", pHeader->dwBlockTableSize); + printf("DWORD dwID = %08X\n", pHeader->dwID); + printf("DWORD dwHeaderSize = %08X\n", pHeader->dwHeaderSize); + printf("DWORD dwArchiveSize = %08X\n", pHeader->dwArchiveSize); + printf("USHORT wFormatVersion = %04X\n", pHeader->wFormatVersion); + printf("USHORT wSectorSize = %04X\n", pHeader->wSectorSize); + printf("DWORD dwHashTablePos = %08X\n", pHeader->dwHashTablePos); + printf("DWORD dwBlockTablePos = %08X\n", pHeader->dwBlockTablePos); + printf("DWORD dwHashTableSize = %08X\n", pHeader->dwHashTableSize); + printf("DWORD dwBlockTableSize = %08X\n", pHeader->dwBlockTableSize); printf("ULONGLONG HiBlockTablePos64 = %016llX\n", pHeader->HiBlockTablePos64); - printf("USHORT wHashTablePosHi = %04lX\n", pHeader->wHashTablePosHi); - printf("USHORT wBlockTablePosHi = %04lX\n", pHeader->wBlockTablePosHi); + printf("USHORT wHashTablePosHi = %04X\n", pHeader->wHashTablePosHi); + printf("USHORT wBlockTablePosHi = %04X\n", pHeader->wBlockTablePosHi); printf("ULONGLONG ArchiveSize64 = %016llX\n", pHeader->ArchiveSize64); printf("ULONGLONG BetTablePos64 = %016llX\n", pHeader->BetTablePos64); printf("ULONGLONG HetTablePos64 = %016llX\n", pHeader->HetTablePos64); @@ -37,7 +37,7 @@ void DumpMpqHeader(TMPQHeader * pHeader) printf("ULONGLONG HiBlockTableSize64 = %016llX\n", pHeader->HiBlockTableSize64); printf("ULONGLONG HetTableSize64 = %016llX\n", pHeader->HetTableSize64); printf("ULONGLONG BetTableSize64 = %016llX\n", pHeader->BetTableSize64); - printf("DWORD dwRawChunkSize = %08lX\n", pHeader->dwRawChunkSize); + printf("DWORD dwRawChunkSize = %08X\n", pHeader->dwRawChunkSize); printf("-----------------------------------------------\n\n"); } @@ -49,33 +49,33 @@ void DumpHetAndBetTable(TMPQHetTable * pHetTable, TMPQBetTable * pBetTable) return; printf("== HET Header =================================\n"); - printf("ULONGLONG AndMask64 = %016llX\n", pHetTable->AndMask64); - printf("ULONGLONG OrMask64 = %016llX\n", pHetTable->OrMask64); - printf("DWORD dwIndexSizeTotal = %08lX\n", pHetTable->dwIndexSizeTotal); - printf("DWORD dwIndexSizeExtra = %08lX\n", pHetTable->dwIndexSizeExtra); - printf("DWORD dwIndexSize = %08lX\n", pHetTable->dwIndexSize); - printf("DWORD dwMaxFileCount = %08lX\n", pHetTable->dwMaxFileCount); - printf("DWORD dwHashTableSize = %08lX\n", pHetTable->dwHashTableSize); - printf("DWORD dwHashBitSize = %08lX\n", pHetTable->dwHashBitSize); + printf("ULONGLONG AndMask64 = %016llX\n", pHetTable->AndMask64); + printf("ULONGLONG OrMask64 = %016llX\n", pHetTable->OrMask64); + printf("DWORD dwIndexSizeTotal = %08X\n", pHetTable->dwIndexSizeTotal); + printf("DWORD dwIndexSizeExtra = %08X\n", pHetTable->dwIndexSizeExtra); + printf("DWORD dwIndexSize = %08X\n", pHetTable->dwIndexSize); + printf("DWORD dwMaxFileCount = %08X\n", pHetTable->dwMaxFileCount); + printf("DWORD dwHashTableSize = %08X\n", pHetTable->dwHashTableSize); + printf("DWORD dwHashBitSize = %08X\n", pHetTable->dwHashBitSize); printf("-----------------------------------------------\n\n"); printf("== BET Header =================================\n"); - printf("DWORD dwTableEntrySize = %08lX\n", pBetTable->dwTableEntrySize); - printf("DWORD dwBitIndex_FilePos = %08lX\n", pBetTable->dwBitIndex_FilePos); - printf("DWORD dwBitIndex_FileSize = %08lX\n", pBetTable->dwBitIndex_FileSize); - printf("DWORD dwBitIndex_CmpSize = %08lX\n", pBetTable->dwBitIndex_CmpSize); - printf("DWORD dwBitIndex_FlagIndex = %08lX\n", pBetTable->dwBitIndex_FlagIndex); - printf("DWORD dwBitIndex_Unknown = %08lX\n", pBetTable->dwBitIndex_Unknown); - printf("DWORD dwBitCount_FilePos = %08lX\n", pBetTable->dwBitCount_FilePos); - printf("DWORD dwBitCount_FileSize = %08lX\n", pBetTable->dwBitCount_FileSize); - printf("DWORD dwBitCount_CmpSize = %08lX\n", pBetTable->dwBitCount_CmpSize); - printf("DWORD dwBitCount_FlagIndex = %08lX\n", pBetTable->dwBitCount_FlagIndex); - printf("DWORD dwBitCount_Unknown = %08lX\n", pBetTable->dwBitCount_Unknown); - printf("DWORD dwBetHashSizeTotal = %08lX\n", pBetTable->dwBetHashSizeTotal); - printf("DWORD dwBetHashSizeExtra = %08lX\n", pBetTable->dwBetHashSizeExtra); - printf("DWORD dwBetHashSize = %08lX\n", pBetTable->dwBetHashSize); - printf("DWORD dwMaxFileCount = %08lX\n", pBetTable->dwMaxFileCount); - printf("DWORD dwFlagCount = %08lX\n", pBetTable->dwFlagCount); + printf("DWORD dwTableEntrySize = %08X\n", pBetTable->dwTableEntrySize); + printf("DWORD dwBitIndex_FilePos = %08X\n", pBetTable->dwBitIndex_FilePos); + printf("DWORD dwBitIndex_FileSize = %08X\n", pBetTable->dwBitIndex_FileSize); + printf("DWORD dwBitIndex_CmpSize = %08X\n", pBetTable->dwBitIndex_CmpSize); + printf("DWORD dwBitIndex_FlagIndex = %08X\n", pBetTable->dwBitIndex_FlagIndex); + printf("DWORD dwBitIndex_Unknown = %08X\n", pBetTable->dwBitIndex_Unknown); + printf("DWORD dwBitCount_FilePos = %08X\n", pBetTable->dwBitCount_FilePos); + printf("DWORD dwBitCount_FileSize = %08X\n", pBetTable->dwBitCount_FileSize); + printf("DWORD dwBitCount_CmpSize = %08X\n", pBetTable->dwBitCount_CmpSize); + printf("DWORD dwBitCount_FlagIndex = %08X\n", pBetTable->dwBitCount_FlagIndex); + printf("DWORD dwBitCount_Unknown = %08X\n", pBetTable->dwBitCount_Unknown); + printf("DWORD dwBetHashSizeTotal = %08X\n", pBetTable->dwBetHashSizeTotal); + printf("DWORD dwBetHashSizeExtra = %08X\n", pBetTable->dwBetHashSizeExtra); + printf("DWORD dwBetHashSize = %08X\n", pBetTable->dwBetHashSize); + printf("DWORD dwMaxFileCount = %08X\n", pBetTable->dwMaxFileCount); + printf("DWORD dwFlagCount = %08X\n", pBetTable->dwFlagCount); printf("-----------------------------------------------\n\n"); printf("== HET & Bet Table ======================================================================\n\n"); @@ -91,52 +91,52 @@ void DumpHetAndBetTable(TMPQHetTable * pHetTable, TMPQBetTable * pBetTable) DWORD dwFlags = 0; DWORD dwBetIndex = 0; - pHetTable->pBetIndexes->GetBits(i * pHetTable->dwIndexSizeTotal, + GetBits(pHetTable->pBetIndexes, i * pHetTable->dwIndexSizeTotal, pHetTable->dwIndexSize, - &dwBetIndex, + &dwBetIndex, 4); - + if(dwBetIndex < pHetTable->dwMaxFileCount) { DWORD dwEntryIndex = pBetTable->dwTableEntrySize * dwBetIndex; - pBetTable->pBetHashes->GetBits(dwBetIndex * pBetTable->dwBetHashSizeTotal, + GetBits(pBetTable->pBetHashes, dwBetIndex * pBetTable->dwBetHashSizeTotal, pBetTable->dwBetHashSize, - &BetHash, + &BetHash, 8); - pBetTable->pFileTable->GetBits(dwEntryIndex + pBetTable->dwBitIndex_FilePos, + GetBits(dwEntryIndex + pBetTable->dwBitIndex_FilePos, pBetTable->dwBitCount_FilePos, - &ByteOffset, + &ByteOffset, 8); - pBetTable->pFileTable->GetBits(dwEntryIndex + pBetTable->dwBitIndex_FileSize, + GetBits(dwEntryIndex + pBetTable->dwBitIndex_FileSize, pBetTable->dwBitCount_FileSize, - &dwFileSize, + &dwFileSize, 4); - pBetTable->pFileTable->GetBits(dwEntryIndex + pBetTable->dwBitIndex_CmpSize, + GetBits(dwEntryIndex + pBetTable->dwBitIndex_CmpSize, pBetTable->dwBitCount_CmpSize, - &dwCmpSize, + &dwCmpSize, 4); - pBetTable->pFileTable->GetBits(dwEntryIndex + pBetTable->dwBitIndex_FlagIndex, + GetBits(dwEntryIndex + pBetTable->dwBitIndex_FlagIndex, pBetTable->dwBitCount_FlagIndex, - &dwFlagIndex, + &dwFlagIndex, 4); dwFlags = pBetTable->pFileFlags[dwFlagIndex]; } - printf(" %04lX %02lX %04lX %016llX %016llX %08lX %08lX %04lX %08lX\n", i, - pHetTable->pHetHashes[i], - dwBetIndex, - BetHash, - ByteOffset, - dwFileSize, - dwCmpSize, - dwFlagIndex, - dwFlags); + printf(" %04X %02lX %04X %016llX %016llX %08X %08X %04X %08X\n", i, + pHetTable->pHetHashes[i], + dwBetIndex, + BetHash, + ByteOffset, + dwFileSize, + dwCmpSize, + dwFlagIndex, + dwFlags); } printf("-----------------------------------------------------------------------------------------\n"); } diff --git a/dep/StormLib/src/SBaseFileTable.cpp b/dep/StormLib/src/SBaseFileTable.cpp index 94e6311a97d..0e25101edec 100644 --- a/dep/StormLib/src/SBaseFileTable.cpp +++ b/dep/StormLib/src/SBaseFileTable.cpp @@ -9,17 +9,14 @@ /*****************************************************************************/ #define __STORMLIB_SELF__ -#define __INCLUDE_CRYPTOGRAPHY__ #include "StormLib.h" #include "StormCommon.h" //----------------------------------------------------------------------------- // Local defines -#define HET_TABLE_SIGNATURE 0x1A544548 // 'HET\x1a' -#define BET_TABLE_SIGNATURE 0x1A544542 // 'BET\x1a' - -#define MAX_FLAG_INDEX 256 +#define INVALID_FLAG_VALUE 0xCCCCCCCC +#define MAX_FLAG_INDEX 512 //----------------------------------------------------------------------------- // Local structures @@ -63,77 +60,13 @@ typedef struct _BET_TABLE_HEADER } BET_TABLE_HEADER, *PBET_TABLE_HEADER; -// Header for HET and BET tables -struct TMPQExtTable -{ - DWORD dwSignature; // 'HET\x1A' or 'BET\x1A' - DWORD dwVersion; // Version. Seems to be always 1 - DWORD dwDataSize; // Size of the contained table - - // Followed by the table header - // Followed by the table data -}; - //----------------------------------------------------------------------------- // Support for calculating bit sizes -static DWORD GetNecessaryBitCount(ULONGLONG MaxValue) -{ - DWORD dwBitCount = 0; - - while(MaxValue > 0) - { - MaxValue >>= 1; - dwBitCount++; - } - - return dwBitCount; -} - -static BYTE GetFlagsComboIndex(DWORD dwFlags) +static void InitFileFlagArray(LPDWORD FlagArray) { - BYTE FlagComboIndex = 0; - - // - // We only use 8 different bits. This allows us to - // construct 256 unique values for every possible combination of MPQ file flags. - // - - if(dwFlags & MPQ_FILE_IMPLODE) - FlagComboIndex |= 0x01; - dwFlags &= ~MPQ_FILE_IMPLODE; - - if(dwFlags & MPQ_FILE_COMPRESS) - FlagComboIndex |= 0x02; - dwFlags &= ~MPQ_FILE_COMPRESS; - - if(dwFlags & MPQ_FILE_ENCRYPTED) - FlagComboIndex |= 0x04; - dwFlags &= ~MPQ_FILE_ENCRYPTED; - - if(dwFlags & MPQ_FILE_FIX_KEY) - FlagComboIndex |= 0x08; - dwFlags &= ~MPQ_FILE_FIX_KEY; - - if(dwFlags & MPQ_FILE_PATCH_FILE) - FlagComboIndex |= 0x10; - dwFlags &= ~MPQ_FILE_PATCH_FILE; - - if(dwFlags & MPQ_FILE_SINGLE_UNIT) - FlagComboIndex |= 0x20; - dwFlags &= ~MPQ_FILE_SINGLE_UNIT; - - if(dwFlags & MPQ_FILE_DELETE_MARKER) - FlagComboIndex |= 0x40; - dwFlags &= ~MPQ_FILE_DELETE_MARKER; - - if(dwFlags & MPQ_FILE_SECTOR_CRC) - FlagComboIndex |= 0x80; - dwFlags &= ~MPQ_FILE_SECTOR_CRC; - - // Sanity check - the flags must now be zero - assert((dwFlags & 0x7FFFFFFF) == 0); - return FlagComboIndex; + for(DWORD dwFlagIndex = 0; dwFlagIndex < MAX_FLAG_INDEX; dwFlagIndex++) + FlagArray[dwFlagIndex] = INVALID_FLAG_VALUE; } static DWORD GetFileFlagIndex(LPDWORD FlagArray, DWORD dwFlags) @@ -141,7 +74,7 @@ static DWORD GetFileFlagIndex(LPDWORD FlagArray, DWORD dwFlags) // Find free or equal entry in the flag array for(DWORD dwFlagIndex = 0; dwFlagIndex < MAX_FLAG_INDEX; dwFlagIndex++) { - if(FlagArray[dwFlagIndex] == 0 || FlagArray[dwFlagIndex] == dwFlags) + if(FlagArray[dwFlagIndex] == INVALID_FLAG_VALUE || FlagArray[dwFlagIndex] == dwFlags) { FlagArray[dwFlagIndex] = dwFlags; return dwFlagIndex; @@ -153,20 +86,33 @@ static DWORD GetFileFlagIndex(LPDWORD FlagArray, DWORD dwFlags) return 0xFFFFFFFF; } +static DWORD GetNecessaryBitCount(ULONGLONG MaxValue) +{ + DWORD dwBitCount = 0; + + while(MaxValue > 0) + { + MaxValue >>= 1; + dwBitCount++; + } + + return dwBitCount; +} + //----------------------------------------------------------------------------- // Support functions for BIT_ARRAY static USHORT SetBitsMask[] = {0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF}; static TBitArray * CreateBitArray( - DWORD NumberOfBits, - BYTE FillValue) + DWORD NumberOfBits, + BYTE FillValue) { TBitArray * pBitArray; size_t nSize = sizeof(TBitArray) + (NumberOfBits + 7) / 8; // Allocate the bit array - pBitArray = (TBitArray *)ALLOCMEM(BYTE, nSize); + pBitArray = (TBitArray *)STORM_ALLOC(BYTE, nSize); if(pBitArray != NULL) { memset(pBitArray, FillValue, nSize); @@ -176,11 +122,12 @@ static TBitArray * CreateBitArray( return pBitArray; } -void TBitArray::GetBits( - unsigned int nBitPosition, - unsigned int nBitLength, - void * pvBuffer, - int nResultByteSize) +void GetBits( + TBitArray * pArray, + unsigned int nBitPosition, + unsigned int nBitLength, + void * pvBuffer, + int nResultByteSize) { unsigned char * pbBuffer = (unsigned char *)pvBuffer; unsigned int nBytePosition0 = (nBitPosition / 8); @@ -201,7 +148,7 @@ void TBitArray::GetBits( #ifndef PLATFORM_LITTLE_ENDIAN // Adjust the buffer pointer for big endian platforms pbBuffer += (nResultByteSize - 1); -#endif +#endif // Copy whole bytes, if any while(nByteLength > 0) @@ -209,11 +156,11 @@ void TBitArray::GetBits( // Is the current position in the Elements byte-aligned? if(nBitOffset != 0) { - BitBuffer = (unsigned char)((Elements[nBytePosition0] >> nBitOffset) | (Elements[nBytePosition1] << (0x08 - nBitOffset))); + BitBuffer = (unsigned char)((pArray->Elements[nBytePosition0] >> nBitOffset) | (pArray->Elements[nBytePosition1] << (0x08 - nBitOffset))); } else { - BitBuffer = Elements[nBytePosition0]; + BitBuffer = pArray->Elements[nBytePosition0]; } #ifdef PLATFORM_LITTLE_ENDIAN @@ -232,20 +179,21 @@ void TBitArray::GetBits( nBitLength = (nBitLength & 0x07); if(nBitLength != 0) { - *pbBuffer = (unsigned char)(Elements[nBytePosition0] >> nBitOffset); + *pbBuffer = (unsigned char)(pArray->Elements[nBytePosition0] >> nBitOffset); if(nBitLength > (8 - nBitOffset)) - *pbBuffer = (unsigned char)((Elements[nBytePosition1] << (8 - nBitOffset)) | (Elements[nBytePosition0] >> nBitOffset)); + *pbBuffer = (unsigned char)((pArray->Elements[nBytePosition1] << (8 - nBitOffset)) | (pArray->Elements[nBytePosition0] >> nBitOffset)); *pbBuffer &= (0x01 << nBitLength) - 1; } } -void TBitArray::SetBits( - unsigned int nBitPosition, - unsigned int nBitLength, - void * pvBuffer, - int nResultByteSize) +void SetBits( + TBitArray * pArray, + unsigned int nBitPosition, + unsigned int nBitLength, + void * pvBuffer, + int nResultByteSize) { unsigned char * pbBuffer = (unsigned char *)pvBuffer; unsigned int nBytePosition = (nBitPosition / 8); @@ -260,7 +208,7 @@ void TBitArray::SetBits( #ifndef PLATFORM_LITTLE_ENDIAN // Adjust the buffer pointer for big endian platforms pbBuffer += (nResultByteSize - 1); -#endif +#endif // Copy whole bytes, if any while(nBitLength > 8) @@ -276,7 +224,7 @@ void TBitArray::SetBits( AndMask = (AndMask >> 0x08) | (0x00FF << nBitOffset); // Update the byte in the array - Elements[nBytePosition] = (BYTE)((Elements[nBytePosition] & ~AndMask) | BitBuffer); + pArray->Elements[nBytePosition] = (BYTE)((pArray->Elements[nBytePosition] & ~AndMask) | BitBuffer); // Move byte positions and lengths nBytePosition++; @@ -293,7 +241,7 @@ void TBitArray::SetBits( AndMask = (AndMask >> 0x08) | (SetBitsMask[nBitLength] << nBitOffset); // Update the byte in the array - Elements[nBytePosition] = (BYTE)((Elements[nBytePosition] & ~AndMask) | BitBuffer); + pArray->Elements[nBytePosition] = (BYTE)((pArray->Elements[nBytePosition] & ~AndMask) | BitBuffer); // Update the next byte, if needed if(AndMask & 0xFF00) @@ -302,7 +250,7 @@ void TBitArray::SetBits( BitBuffer >>= 0x08; AndMask >>= 0x08; - Elements[nBytePosition] = (BYTE)((Elements[nBytePosition] & ~AndMask) | BitBuffer); + pArray->Elements[nBytePosition] = (BYTE)((pArray->Elements[nBytePosition] & ~AndMask) | BitBuffer); } } } @@ -332,7 +280,7 @@ static TMPQHash * GetHashEntryAny(TMPQArchive * ha, const char * szFileName) pHashAny = pHash; // Get the next hash entry for that file - pHash = GetNextHashEntry(ha, pFirstHash, pHash); + pHash = GetNextHashEntry(ha, pFirstHash, pHash); } // At the end, return neutral hash (if found), otherwise NULL @@ -355,13 +303,13 @@ static TMPQHash * GetHashEntryLocale(TMPQArchive * ha, const char * szFileName, // If the locales match, return it if(pHash->lcLocale == lcLocale) return pHash; - + // If we found neutral hash, remember it if(pHash->lcLocale == 0) pHashNeutral = pHash; // Get the next hash entry for that file - pHash = GetNextHashEntry(ha, pFirstHash, pHash); + pHash = GetNextHashEntry(ha, pFirstHash, pHash); } // At the end, return neutral hash (if found), otherwise NULL @@ -382,9 +330,9 @@ static TMPQHash * GetHashEntryExact(TMPQArchive * ha, const char * szFileName, L // If the locales match, return it if(pHash->lcLocale == lcLocale) return pHash; - + // Get the next hash entry for that file - pHash = GetNextHashEntry(ha, pFirstHash, pHash); + pHash = GetNextHashEntry(ha, pFirstHash, pHash); } // Not found @@ -392,14 +340,14 @@ static TMPQHash * GetHashEntryExact(TMPQArchive * ha, const char * szFileName, L } static TMPQHash * TranslateHashTable( - TMPQArchive * ha, - ULONGLONG * pcbTableSize) + TMPQArchive * ha, + ULONGLONG * pcbTableSize) { TMPQHash * pHashTable; size_t HashTableSize; // Allocate copy of the hash table - pHashTable = ALLOCMEM(TMPQHash, ha->pHeader->dwHashTableSize); + pHashTable = STORM_ALLOC(TMPQHash, ha->pHeader->dwHashTableSize); if(pHashTable != NULL) { // Copy the hash table @@ -417,9 +365,9 @@ static TMPQHash * TranslateHashTable( } static TMPQBlock * TranslateBlockTable( - TMPQArchive * ha, - ULONGLONG * pcbTableSize, - bool * pbNeedHiBlockTable) + TMPQArchive * ha, + ULONGLONG * pcbTableSize, + bool * pbNeedHiBlockTable) { TFileEntry * pFileEntry = ha->pFileTable; TMPQBlock * pBlockTable; @@ -428,15 +376,14 @@ static TMPQBlock * TranslateBlockTable( bool bNeedHiBlockTable = false; // Allocate copy of the hash table - pBlockTable = pBlock = ALLOCMEM(TMPQBlock, ha->dwFileTableSize); + pBlockTable = pBlock = STORM_ALLOC(TMPQBlock, ha->dwFileTableSize); if(pBlockTable != NULL) { // Copy the block table BlockTableSize = sizeof(TMPQBlock) * ha->dwFileTableSize; for(DWORD i = 0; i < ha->dwFileTableSize; i++) { - if(pFileEntry->ByteOffset >> 32) - bNeedHiBlockTable = true; + bNeedHiBlockTable = (pFileEntry->ByteOffset >> 32) ? true : false; pBlock->dwFilePos = (DWORD)pFileEntry->ByteOffset; pBlock->dwFSize = pFileEntry->dwFileSize; pBlock->dwCSize = pFileEntry->dwCmpSize; @@ -458,8 +405,8 @@ static TMPQBlock * TranslateBlockTable( } static USHORT * TranslateHiBlockTable( - TMPQArchive * ha, - ULONGLONG * pcbTableSize) + TMPQArchive * ha, + ULONGLONG * pcbTableSize) { TFileEntry * pFileEntry = ha->pFileTable; USHORT * pHiBlockTable; @@ -467,7 +414,7 @@ static USHORT * TranslateHiBlockTable( size_t HiBlockTableSize; // Allocate copy of the hash table - pHiBlockTable = pHiBlock = ALLOCMEM(USHORT, ha->dwFileTableSize); + pHiBlockTable = pHiBlock = STORM_ALLOC(USHORT, ha->dwFileTableSize); if(pHiBlockTable != NULL) { // Copy the block table @@ -486,12 +433,12 @@ static USHORT * TranslateHiBlockTable( //----------------------------------------------------------------------------- // General EXT table functions -static TMPQExtTable * LoadExtTable( - TMPQArchive * ha, - ULONGLONG ByteOffset, - size_t Size, - DWORD dwSignature, - DWORD dwKey) +TMPQExtTable * LoadExtTable( + TMPQArchive * ha, + ULONGLONG ByteOffset, + size_t Size, + DWORD dwSignature, + DWORD dwKey) { TMPQExtTable * pCompressed = NULL; // Compressed table TMPQExtTable * pExtTable = NULL; // Uncompressed table @@ -500,14 +447,14 @@ static TMPQExtTable * LoadExtTable( if(ByteOffset != 0 && Size != 0) { // Allocate size for the compressed table - pExtTable = (TMPQExtTable *)ALLOCMEM(BYTE, Size); + pExtTable = (TMPQExtTable *)STORM_ALLOC(BYTE, Size); if(pExtTable != NULL) { // Load the table from the MPQ ByteOffset += ha->MpqPos; if(!FileStream_Read(ha->pStream, &ByteOffset, pExtTable, (DWORD)Size)) { - FREEMEM(pExtTable); + STORM_FREE(pExtTable); return NULL; } @@ -515,7 +462,7 @@ static TMPQExtTable * LoadExtTable( BSWAP_ARRAY32_UNSIGNED(pExtTable, sizeof(TMPQExtTable)); if(pExtTable->dwSignature != dwSignature) { - FREEMEM(pExtTable); + STORM_FREE(pExtTable); return NULL; } @@ -528,21 +475,25 @@ static TMPQExtTable * LoadExtTable( if((pExtTable->dwDataSize + sizeof(TMPQExtTable)) > Size) { pCompressed = pExtTable; - pExtTable = (TMPQExtTable *)ALLOCMEM(BYTE, sizeof(TMPQExtTable) + pCompressed->dwDataSize); + pExtTable = (TMPQExtTable *)STORM_ALLOC(BYTE, sizeof(TMPQExtTable) + pCompressed->dwDataSize); if(pExtTable != NULL) { int cbOutBuffer = (int)pCompressed->dwDataSize; int cbInBuffer = (int)Size; - // Decompress the XXX block + // Decompress the extended table pExtTable->dwSignature = pCompressed->dwSignature; pExtTable->dwVersion = pCompressed->dwVersion; pExtTable->dwDataSize = pCompressed->dwDataSize; - SCompDecompress((char *)(pExtTable + 1), &cbOutBuffer, (char *)(pCompressed + 1), cbInBuffer); + if(!SCompDecompress2((char *)(pExtTable + 1), &cbOutBuffer, (char *)(pCompressed + 1), cbInBuffer)) + { + STORM_FREE(pExtTable); + pExtTable = NULL; + } } // Free the compressed block - FREEMEM(pCompressed); + STORM_FREE(pCompressed); } } } @@ -551,14 +502,20 @@ static TMPQExtTable * LoadExtTable( return pExtTable; } +// Used in MPQ Editor +void FreeMpqBuffer(void * pvBuffer) +{ + STORM_FREE(pvBuffer); +} + static int SaveMpqTable( - TMPQArchive * ha, - void * pMpqTable, - ULONGLONG ByteOffset, - size_t Size, - unsigned char * md5_digest, - DWORD dwKey, - bool bCompress) + TMPQArchive * ha, + void * pMpqTable, + ULONGLONG ByteOffset, + size_t Size, + unsigned char * md5, + DWORD dwKey, + bool bCompress) { ULONGLONG FileOffset; void * pCompressed = NULL; @@ -571,7 +528,7 @@ static int SaveMpqTable( int cbInBuffer = (int)Size; // Allocate extra space for compressed table - pCompressed = ALLOCMEM(BYTE, Size); + pCompressed = STORM_ALLOC(BYTE, Size); if(pCompressed == NULL) return ERROR_NOT_ENOUGH_MEMORY; @@ -581,7 +538,7 @@ static int SaveMpqTable( // If the compression failed, revert it. Otherwise, swap the tables if(cbOutBuffer >= cbInBuffer) { - FREEMEM(pCompressed); + STORM_FREE(pCompressed); pCompressed = NULL; } else @@ -599,13 +556,9 @@ static int SaveMpqTable( } // Calculate the MD5 - if(md5_digest != NULL) + if(md5 != NULL) { - hash_state md5_state; - - md5_init(&md5_state); - md5_process(&md5_state, (unsigned char *)pMpqTable, (DWORD)Size); - md5_done(&md5_state, md5_digest); + CalculateDataBlockHash(pMpqTable, (DWORD)Size, md5); } // Save the table to the MPQ @@ -616,19 +569,19 @@ static int SaveMpqTable( // Free the compressed table, if any if(pCompressed != NULL) - FREEMEM(pCompressed); + STORM_FREE(pCompressed); return nError; } static int SaveExtTable( - TMPQArchive * ha, - TMPQExtTable * pExtTable, - ULONGLONG ByteOffset, - DWORD dwTableSize, - unsigned char * md5_digest, - DWORD dwKey, - bool bCompress, - LPDWORD pcbTotalSize) + TMPQArchive * ha, + TMPQExtTable * pExtTable, + ULONGLONG ByteOffset, + DWORD dwTableSize, + unsigned char * md5, + DWORD dwKey, + bool bCompress, + LPDWORD pcbTotalSize) { ULONGLONG FileOffset; TMPQExtTable * pCompressed = NULL; @@ -642,7 +595,7 @@ static int SaveExtTable( int cbInBuffer = (int)dwTableSize; // Allocate extra space for compressed table - pCompressed = (TMPQExtTable *)ALLOCMEM(BYTE, dwTableSize); + pCompressed = (TMPQExtTable *)STORM_ALLOC(BYTE, dwTableSize); if(pCompressed == NULL) return ERROR_NOT_ENOUGH_MEMORY; @@ -655,7 +608,7 @@ static int SaveExtTable( // If the compression failed, revert it. Otherwise, swap the tables if(cbOutBuffer >= cbInBuffer) { - FREEMEM(pCompressed); + STORM_FREE(pCompressed); pCompressed = NULL; } else @@ -673,13 +626,9 @@ static int SaveExtTable( } // Calculate the MD5 of the table after - if(md5_digest != NULL) + if(md5 != NULL) { - hash_state md5_state; - - md5_init(&md5_state); - md5_process(&md5_state, (unsigned char *)pExtTable, dwTableSize); - md5_done(&md5_state, md5_digest); + CalculateDataBlockHash(pExtTable, dwTableSize, md5); } // Save the table to the MPQ @@ -697,7 +646,7 @@ static int SaveExtTable( pExtTable, dwTableSize, ha->pHeader->dwRawChunkSize, - &cbTotalSize); + &cbTotalSize); } // Give the total written size, if needed @@ -706,7 +655,7 @@ static int SaveExtTable( // Free the compressed table, if any if(pCompressed != NULL) - FREEMEM(pCompressed); + STORM_FREE(pCompressed); return nError; } @@ -714,8 +663,8 @@ static int SaveExtTable( // Support for HET table static void CreateHetHeader( - TMPQHetTable * pHetTable, - PHET_TABLE_HEADER pHetHeader) + TMPQHetTable * pHetTable, + PHET_TABLE_HEADER pHetHeader) { // Fill the BET header pHetHeader->dwMaxFileCount = pHetTable->dwMaxFileCount; @@ -728,15 +677,15 @@ static void CreateHetHeader( // Calculate the total size needed for holding HET table pHetHeader->dwTableSize = sizeof(HET_TABLE_HEADER) + - pHetHeader->dwHashTableSize + - pHetHeader->dwIndexTableSize; + pHetHeader->dwHashTableSize + + pHetHeader->dwIndexTableSize; } TMPQHetTable * CreateHetTable(DWORD dwMaxFileCount, DWORD dwHashBitSize, bool bCreateEmpty) { TMPQHetTable * pHetTable; - pHetTable = ALLOCMEM(TMPQHetTable, 1); + pHetTable = STORM_ALLOC(TMPQHetTable, 1); if(pHetTable != NULL) { pHetTable->dwIndexSizeTotal = 0; @@ -752,7 +701,7 @@ TMPQHetTable * CreateHetTable(DWORD dwMaxFileCount, DWORD dwHashBitSize, bool bC pHetTable->dwIndexSize = pHetTable->dwIndexSizeTotal; // Allocate hash table - pHetTable->pHetHashes = ALLOCMEM(BYTE, pHetTable->dwHashTableSize); + pHetTable->pHetHashes = STORM_ALLOC(BYTE, pHetTable->dwHashTableSize); memset(pHetTable->pHetHashes, 0, pHetTable->dwHashTableSize); // If we shall create empty HET table, we have to allocate empty block index table as well @@ -820,8 +769,8 @@ static TMPQHetTable * TranslateHetTable(TMPQExtTable * pExtTable) static TMPQExtTable * TranslateHetTable(TMPQHetTable * pHetTable, ULONGLONG * pcbHetTable) { - HET_TABLE_HEADER HetHeader; TMPQExtTable * pExtTable = NULL; + HET_TABLE_HEADER HetHeader; LPBYTE pbLinearTable = NULL; LPBYTE pbTrgData; size_t HetTableSize; @@ -833,7 +782,7 @@ static TMPQExtTable * TranslateHetTable(TMPQHetTable * pHetTable, ULONGLONG * pc HetTableSize = HetHeader.dwTableSize; // Allocate space for the linear table - pbLinearTable = ALLOCMEM(BYTE, sizeof(TMPQExtTable) + HetTableSize); + pbLinearTable = STORM_ALLOC(BYTE, sizeof(TMPQExtTable) + HetTableSize); if(pbLinearTable != NULL) { // Create the common ext table header @@ -855,7 +804,7 @@ static TMPQExtTable * TranslateHetTable(TMPQHetTable * pHetTable, ULONGLONG * pc // Copy the bit array of BET indexes memcpy(pbTrgData, pHetTable->pBetIndexes->Elements, HetHeader.dwIndexTableSize); - // Calculate the total size of the table, including the TMPQExtTable header + // Calculate the total size of the table, including the TMPQExtTable if(pcbHetTable != NULL) { *pcbHetTable = (ULONGLONG)(sizeof(TMPQExtTable) + HetTableSize); @@ -902,13 +851,20 @@ DWORD GetFileIndex_Het(TMPQArchive * ha, const char * szFileName) DWORD dwFileIndex = 0; // Get the index of the BetHash - pHetTable->pBetIndexes->GetBits(pHetTable->dwIndexSizeTotal * Index, + GetBits(pHetTable->pBetIndexes, pHetTable->dwIndexSizeTotal * Index, pHetTable->dwIndexSize, - &dwFileIndex, + &dwFileIndex, 4); + // + // TODO: This condition only happens when we are opening a MPQ + // where some files were deleted by StormLib. Perhaps + // we should not allow shrinking of the file table in MPQs v 4.0? + // assert(dwFileIndex <= ha->dwFileTableSize); + // + // Verify the BetHash against the entry in the table of BET hashes - if(ha->pFileTable[dwFileIndex].BetHash == BetHash) + if(dwFileIndex <= ha->dwFileTableSize && ha->pFileTable[dwFileIndex].BetHash == BetHash) return dwFileIndex; } @@ -924,8 +880,8 @@ DWORD GetFileIndex_Het(TMPQArchive * ha, const char * szFileName) } DWORD AllocateHetEntry( - TMPQArchive * ha, - TFileEntry * pFileEntry) + TMPQArchive * ha, + TFileEntry * pFileEntry) { TMPQHetTable * pHetTable = ha->pHetTable; ULONGLONG FileNameHash; @@ -966,11 +922,11 @@ DWORD AllocateHetEntry( // Verify the BET index. If it's really free, we can use it dwFileIndex = (DWORD)(pFileEntry - ha->pFileTable); - pHetTable->pBetIndexes->GetBits(pHetTable->dwIndexSizeTotal * Index, + GetBits(pHetTable->pBetIndexes, pHetTable->dwIndexSizeTotal * Index, pHetTable->dwIndexSize, - &dwBetIndex, + &dwBetIndex, 4); - + if(dwBetIndex == dwInvalidBetIndex) { FreeHetIndex = Index; @@ -995,9 +951,9 @@ DWORD AllocateHetEntry( // Fill the HET table entry dwFileIndex = (DWORD)(pFileEntry - ha->pFileTable); pHetTable->pHetHashes[FreeHetIndex] = HetHash; - pHetTable->pBetIndexes->SetBits(pHetTable->dwIndexSizeTotal * FreeHetIndex, + SetBits(pHetTable->pBetIndexes, pHetTable->dwIndexSizeTotal * FreeHetIndex, pHetTable->dwIndexSize, - &dwFileIndex, + &dwFileIndex, 4); // Fill the file entry pFileEntry->BetHash = BetHash; @@ -1010,11 +966,11 @@ void FreeHetTable(TMPQHetTable * pHetTable) if(pHetTable != NULL) { if(pHetTable->pHetHashes != NULL) - FREEMEM(pHetTable->pHetHashes); + STORM_FREE(pHetTable->pHetHashes); if(pHetTable->pBetIndexes != NULL) - FREEMEM(pHetTable->pBetIndexes); + STORM_FREE(pHetTable->pBetIndexes); - FREEMEM(pHetTable); + STORM_FREE(pHetTable); } } @@ -1022,22 +978,23 @@ void FreeHetTable(TMPQHetTable * pHetTable) // Support for BET table static void CreateBetHeader( - TMPQArchive * ha, - PBET_TABLE_HEADER pBetHeader) + TMPQArchive * ha, + PBET_TABLE_HEADER pBetHeader) { - TFileEntry * pFileEntry = ha->pFileTable; + TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; + TFileEntry * pFileEntry; ULONGLONG MaxByteOffset = 0; + DWORD FlagArray[MAX_FLAG_INDEX]; + DWORD dwMaxFlagIndex = 0; DWORD dwMaxFileSize = 0; DWORD dwMaxCmpSize = 0; - DWORD dwFlagCount = 0; - BYTE FlagComboArray[MAX_FLAG_INDEX]; - BYTE FlagComboIndex; + DWORD dwFlagIndex; // Initialize array of flag combinations - memset(FlagComboArray, 0, sizeof(FlagComboArray)); + InitFileFlagArray(FlagArray); // Get the maximum values for the BET table - for(DWORD i = 0; i < ha->dwFileTableSize; pFileEntry++, i++) + for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) { // Highest file position in the MPQ if(pFileEntry->ByteOffset > MaxByteOffset) @@ -1052,10 +1009,9 @@ static void CreateBetHeader( dwMaxCmpSize = pFileEntry->dwCmpSize; // Check if this flag was there before - FlagComboIndex = GetFlagsComboIndex(pFileEntry->dwFlags); - if(FlagComboArray[FlagComboIndex] == 0) - dwFlagCount++; - FlagComboArray[FlagComboIndex] = 1; + dwFlagIndex = GetFileFlagIndex(FlagArray, pFileEntry->dwFlags); + if(dwFlagIndex > dwMaxFlagIndex) + dwMaxFlagIndex = dwFlagIndex; } // Now save bit count for every piece of file information @@ -1069,20 +1025,21 @@ static void CreateBetHeader( pBetHeader->dwBitCount_CmpSize = GetNecessaryBitCount(dwMaxCmpSize); pBetHeader->dwBitIndex_FlagIndex = pBetHeader->dwBitIndex_CmpSize + pBetHeader->dwBitCount_CmpSize; - pBetHeader->dwBitCount_FlagIndex = GetNecessaryBitCount(dwFlagCount); + pBetHeader->dwBitCount_FlagIndex = GetNecessaryBitCount(dwMaxFlagIndex + 1); pBetHeader->dwBitIndex_Unknown = pBetHeader->dwBitIndex_FlagIndex + pBetHeader->dwBitCount_FlagIndex; pBetHeader->dwBitCount_Unknown = 0; // Calculate the total size of one entry pBetHeader->dwTableEntrySize = pBetHeader->dwBitCount_FilePos + - pBetHeader->dwBitCount_FileSize + - pBetHeader->dwBitCount_CmpSize + - pBetHeader->dwBitCount_FlagIndex + - pBetHeader->dwBitCount_Unknown; + pBetHeader->dwBitCount_FileSize + + pBetHeader->dwBitCount_CmpSize + + pBetHeader->dwBitCount_FlagIndex + + pBetHeader->dwBitCount_Unknown; - // Save the file count + // Save the file count and flag count pBetHeader->dwFileCount = ha->dwFileTableSize; + pBetHeader->dwFlagCount = dwMaxFlagIndex + 1; pBetHeader->dwUnknown08 = 0x10; // Save the total size of the BET hash @@ -1091,34 +1048,31 @@ static void CreateBetHeader( pBetHeader->dwBetHashSize = pBetHeader->dwBetHashSizeTotal; pBetHeader->dwBetHashArraySize = ((pBetHeader->dwBetHashSizeTotal * pBetHeader->dwFileCount) + 7) / 8; - // Save the number of file flags - pBetHeader->dwFlagCount = dwFlagCount; - // Save the total table size pBetHeader->dwTableSize = sizeof(BET_TABLE_HEADER) + - pBetHeader->dwFlagCount * sizeof(DWORD) + - ((pBetHeader->dwTableEntrySize * pBetHeader->dwFileCount) + 7) / 8 + - pBetHeader->dwBetHashArraySize; + pBetHeader->dwFlagCount * sizeof(DWORD) + + ((pBetHeader->dwTableEntrySize * pBetHeader->dwFileCount) + 7) / 8 + + pBetHeader->dwBetHashArraySize; } -TMPQBetTable * CreateBetTable(DWORD dwMaxFileCount) +TMPQBetTable * CreateBetTable(DWORD dwFileCount) { TMPQBetTable * pBetTable; // Allocate BET table - pBetTable = ALLOCMEM(TMPQBetTable, 1); + pBetTable = STORM_ALLOC(TMPQBetTable, 1); if(pBetTable != NULL) { memset(pBetTable, 0, sizeof(TMPQBetTable)); - pBetTable->dwMaxFileCount = dwMaxFileCount; + pBetTable->dwFileCount = dwFileCount; } return pBetTable; } static TMPQBetTable * TranslateBetTable( - TMPQArchive * ha, - TMPQExtTable * pExtTable) + TMPQArchive * ha, + TMPQExtTable * pExtTable) { BET_TABLE_HEADER BetHeader; TMPQBetTable * pBetTable = NULL; @@ -1139,6 +1093,11 @@ static TMPQBetTable * TranslateBetTable( BSWAP_ARRAY32_UNSIGNED(&BetHeader, sizeof(BET_TABLE_HEADER)); pbSrcData += sizeof(BET_TABLE_HEADER); + // Some MPQs affected by a bug in StormLib have pBetTable->dwFileCount + // greater than ha->dwMaxFileCount + if(BetHeader.dwFileCount > ha->dwMaxFileCount) + return NULL; + // Verify the size of the table in the header if(BetHeader.dwTableSize == pExtTable->dwDataSize) { @@ -1159,11 +1118,14 @@ static TMPQBetTable * TranslateBetTable( pBetTable->dwBitCount_FlagIndex = BetHeader.dwBitCount_FlagIndex; pBetTable->dwBitCount_Unknown = BetHeader.dwBitCount_Unknown; + // Since we don't know what the "unknown" is, we'll assert when it's nonzero + assert(pBetTable->dwBitCount_Unknown == 0); + // Allocate array for flags if(BetHeader.dwFlagCount != 0) { // Allocate array for file flags and load it - pBetTable->pFileFlags = ALLOCMEM(DWORD, BetHeader.dwFlagCount); + pBetTable->pFileFlags = STORM_ALLOC(DWORD, BetHeader.dwFlagCount); if(pBetTable->pFileFlags != NULL) { LengthInBytes = BetHeader.dwFlagCount * sizeof(DWORD); @@ -1187,7 +1149,7 @@ static TMPQBetTable * TranslateBetTable( pBetTable->dwBetHashSizeTotal = BetHeader.dwBetHashSizeTotal; pBetTable->dwBetHashSizeExtra = BetHeader.dwBetHashSizeExtra; pBetTable->dwBetHashSize = BetHeader.dwBetHashSize; - + // Create and load the array of BET hashes pBetTable->pBetHashes = CreateBitArray(pBetTable->dwBetHashSizeTotal * BetHeader.dwFileCount, 0); LengthInBytes = (pBetTable->pBetHashes->NumberOfBits + 7) / 8; @@ -1196,7 +1158,7 @@ static TMPQBetTable * TranslateBetTable( pbSrcData += BetHeader.dwBetHashArraySize; // Dump both tables -// DumpHetAndBetTable(ha->pHetTable, pBetTable); + // DumpHetAndBetTable(ha->pHetTable, pBetTable); } } } @@ -1205,11 +1167,11 @@ static TMPQBetTable * TranslateBetTable( } TMPQExtTable * TranslateBetTable( - TMPQArchive * ha, - ULONGLONG * pcbBetTable) + TMPQArchive * ha, + ULONGLONG * pcbBetTable) { - BET_TABLE_HEADER BetHeader; TMPQExtTable * pExtTable = NULL; + BET_TABLE_HEADER BetHeader; TBitArray * pBitArray = NULL; LPBYTE pbLinearTable = NULL; LPBYTE pbTrgData; @@ -1219,17 +1181,17 @@ TMPQExtTable * TranslateBetTable( DWORD i; // Calculate the bit sizes of various entries + InitFileFlagArray(FlagArray); CreateBetHeader(ha, &BetHeader); - memset(FlagArray, 0, sizeof(FlagArray)); // Calculate the size of the BET table BetTableSize = sizeof(BET_TABLE_HEADER) + - BetHeader.dwFlagCount * sizeof(DWORD) + - ((BetHeader.dwTableEntrySize * BetHeader.dwFileCount) + 7) / 8 + - BetHeader.dwBetHashArraySize; + BetHeader.dwFlagCount * sizeof(DWORD) + + ((BetHeader.dwTableEntrySize * BetHeader.dwFileCount) + 7) / 8 + + BetHeader.dwBetHashArraySize; // Allocate space - pbLinearTable = ALLOCMEM(BYTE, sizeof(TMPQExtTable) + BetTableSize); + pbLinearTable = STORM_ALLOC(BYTE, sizeof(TMPQExtTable) + BetTableSize); if(pbLinearTable != NULL) { // Create the common ext table header @@ -1255,25 +1217,31 @@ TMPQExtTable * TranslateBetTable( // Construct the array of flag values and bit-based file table for(i = 0; i < BetHeader.dwFileCount; i++, pFileEntry++) { - // Get the flag array for the file - dwFlagIndex = GetFileFlagIndex(FlagArray, pFileEntry->dwFlags); + // + // Note: Blizzard MPQs contain valid values even for non-existant files + // (FilePos, FileSize, CmpSize and FlagIndex) + // Note: If flags is zero, it must be in the flag table too !!! + // // Save the byte offset - pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_FilePos, + SetBits(pBitArray, nBitOffset + BetHeader.dwBitIndex_FilePos, BetHeader.dwBitCount_FilePos, - &pFileEntry->ByteOffset, + &pFileEntry->ByteOffset, 8); - pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_FileSize, + SetBits(pBitArray, nBitOffset + BetHeader.dwBitIndex_FileSize, BetHeader.dwBitCount_FileSize, - &pFileEntry->dwFileSize, + &pFileEntry->dwFileSize, 4); - pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_CmpSize, + SetBits(pBitArray, nBitOffset + BetHeader.dwBitIndex_CmpSize, BetHeader.dwBitCount_CmpSize, - &pFileEntry->dwCmpSize, + &pFileEntry->dwCmpSize, 4); - pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_FlagIndex, + + // Save the flag index + dwFlagIndex = GetFileFlagIndex(FlagArray, pFileEntry->dwFlags); + SetBits(pBitArray, nBitOffset + BetHeader.dwBitIndex_FlagIndex, BetHeader.dwBitCount_FlagIndex, - &dwFlagIndex, + &dwFlagIndex, 4); // Move the bit offset @@ -1292,7 +1260,7 @@ TMPQExtTable * TranslateBetTable( pbTrgData += LengthInBytes; // Free the bit array - FREEMEM(pBitArray); + STORM_FREE(pBitArray); } // Create bit array for BET hashes @@ -1315,9 +1283,9 @@ TMPQExtTable * TranslateBetTable( } // Insert the name hash to the bit array - pBitArray->SetBits(BetHeader.dwBetHashSizeTotal * i, + SetBits(pBitArray, BetHeader.dwBetHashSizeTotal * i, BetHeader.dwBetHashSize, - &FileNameHash, + &FileNameHash, 8); // Move to the next file entry @@ -1330,7 +1298,7 @@ TMPQExtTable * TranslateBetTable( pbTrgData += LengthInBytes; // Free the bit array - FREEMEM(pBitArray); + STORM_FREE(pBitArray); } // Write the size of the BET table in the MPQ @@ -1348,13 +1316,13 @@ void FreeBetTable(TMPQBetTable * pBetTable) if(pBetTable != NULL) { if(pBetTable->pFileTable != NULL) - FREEMEM(pBetTable->pFileTable); + STORM_FREE(pBetTable->pFileTable); if(pBetTable->pFileFlags != NULL) - FREEMEM(pBetTable->pFileFlags); + STORM_FREE(pBetTable->pFileFlags); if(pBetTable->pBetHashes != NULL) - FREEMEM(pBetTable->pBetHashes); + STORM_FREE(pBetTable->pBetHashes); - FREEMEM(pBetTable); + STORM_FREE(pBetTable); } } @@ -1381,7 +1349,7 @@ TFileEntry * GetFileEntryAny(TMPQArchive * ha, const char * szFileName) if(pHash != NULL && pHash->dwBlockIndex < ha->dwFileTableSize) return ha->pFileTable + pHash->dwBlockIndex; } - + // Not found return NULL; } @@ -1406,7 +1374,7 @@ TFileEntry * GetFileEntryLocale(TMPQArchive * ha, const char * szFileName, LCID if(pHash != NULL && pHash->dwBlockIndex < ha->dwFileTableSize) return ha->pFileTable + pHash->dwBlockIndex; } - + // Not found return NULL; } @@ -1431,7 +1399,7 @@ TFileEntry * GetFileEntryExact(TMPQArchive * ha, const char * szFileName, LCID l if(pHash != NULL && pHash->dwBlockIndex < ha->dwFileTableSize) return ha->pFileTable + pHash->dwBlockIndex; } - + // Not found return NULL; } @@ -1449,30 +1417,52 @@ void AllocateFileName(TFileEntry * pFileEntry, const char * szFileName) // Sanity check assert(pFileEntry != NULL); + // If the file name is pseudo file name, free it at this point + if(IsPseudoFileName(pFileEntry->szFileName, NULL)) + { + if(pFileEntry->szFileName != NULL) + STORM_FREE(pFileEntry->szFileName); + pFileEntry->szFileName = NULL; + } + // Only allocate new file name if it's not there yet if(pFileEntry->szFileName == NULL) { - pFileEntry->szFileName = ALLOCMEM(char, strlen(szFileName) + 1); + pFileEntry->szFileName = STORM_ALLOC(char, strlen(szFileName) + 1); if(pFileEntry->szFileName != NULL) strcpy(pFileEntry->szFileName, szFileName); } } -// Finds a free file entry. Does NOT increment table size, -// althought it might reallocate it. +// Finds a free file entry. Does NOT increment table size. TFileEntry * FindFreeFileEntry(TMPQArchive * ha) { TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; + TFileEntry * pFreeEntry = NULL; TFileEntry * pFileEntry; - // Otherwise, find a free entry within existing entries in the file table + // Try to find a free entry for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) { - // If that entry is free, we don't need - // to reallocate file table + // If that entry is free, we reuse it if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0) - return pFileEntry; + { + pFreeEntry = pFileEntry; + break; + } + + // + // Note: Files with "delete marker" are not deleted. + // Don't consider them free entries + // + } + + // Do we have a deleted entry? + if(pFreeEntry != NULL) + { + ClearFileEntry(ha, pFreeEntry); + return pFreeEntry; } // If no file entry within the existing file table is free, @@ -1534,7 +1524,7 @@ TFileEntry * AllocateFileEntry(TMPQArchive * ha, const char * szFileName, LCID l pFileEntry->dwFileSize = 0; pFileEntry->dwCmpSize = 0; pFileEntry->dwFlags = 0; - pFileEntry->lcLocale = 0; + pFileEntry->lcLocale = (USHORT)lcLocale; pFileEntry->wPlatform = 0; pFileEntry->dwCrc32 = 0; memset(pFileEntry->md5, 0, MD5_DIGEST_SIZE); @@ -1571,13 +1561,16 @@ TFileEntry * AllocateFileEntry(TMPQArchive * ha, const char * szFileName, LCID l return pFileEntry; } -void FreeFileEntry( - TMPQArchive * ha, - TFileEntry * pFileEntry) +int RenameFileEntry( + TMPQArchive * ha, + TFileEntry * pFileEntry, + const char * szNewFileName) { - TMPQHash * pHash = NULL; + TMPQHash * pHash; + DWORD dwFileIndex; + int nError = ERROR_SUCCESS; - // If the MPQ has classic hash table, clear the entry there as well + // If the MPQ has classic hash table, clear the entry there if(ha->pHashTable != NULL) { assert(pFileEntry->dwHashIndex < ha->pHeader->dwHashTableSize); @@ -1587,7 +1580,7 @@ void FreeFileEntry( pHash->dwBlockIndex = HASH_ENTRY_DELETED; } - // If the MPQ has HET table, clear the entry there + // If the MPQ has HET table, clear the entry there as well if(ha->pHetTable != NULL) { TMPQHetTable * pHetTable = ha->pHetTable; @@ -1599,329 +1592,258 @@ void FreeFileEntry( pHetTable->pHetHashes[pFileEntry->dwHetIndex] = HET_ENTRY_DELETED; // Set the BET index to invalid index - pHetTable->pBetIndexes->SetBits(pHetTable->dwIndexSizeTotal * pFileEntry->dwHetIndex, + SetBits(pHetTable->pBetIndexes, pHetTable->dwIndexSizeTotal * pFileEntry->dwHetIndex, pHetTable->dwIndexSize, - &dwInvalidFileIndex, + &dwInvalidFileIndex, 4); } - // Free the file name, if any + // Free the old file name if(pFileEntry->szFileName != NULL) - FREEMEM(pFileEntry->szFileName); + STORM_FREE(pFileEntry->szFileName); pFileEntry->szFileName = NULL; - // Clear the block entry - memset(pFileEntry, 0, sizeof(TFileEntry)); + // Allocate new file name + AllocateFileName(pFileEntry, szNewFileName); - // Decrement block entry size, if necessary - if(pFileEntry == ha->pFileTable + (ha->dwFileTableSize - 1)) - ha->dwFileTableSize--; + // Now find a hash entry for the new file name + if(ha->pHashTable != NULL) + { + // Try to find the hash table entry for the new file name + // Note: If this fails, we leave the MPQ in a corrupt state + dwFileIndex = AllocateHashEntry(ha, pFileEntry); + if(dwFileIndex == HASH_ENTRY_FREE) + nError = ERROR_FILE_CORRUPT; + } - // Make sure that the block table has the proper size - ha->pHeader->dwBlockTableSize = ha->dwFileTableSize; -} + // If the archive has HET table, we have to allocate HET table for the file as well + // finding the file + if(ha->pHetTable != NULL) + { + dwFileIndex = AllocateHetEntry(ha, pFileEntry); + if(dwFileIndex == HASH_ENTRY_FREE) + nError = ERROR_FILE_CORRUPT; + } -//----------------------------------------------------------------------------- -// Support for file tables - hash table, block table, hi-block table, -// (attributes) and (listfile) + // Invalidate the entries for (listfile) and (attributes) + // After we are done with MPQ changes, we need to re-create them + InvalidateInternalFiles(ha); + return nError; +} -static void FixBlockTableSize( - TMPQArchive * ha, - TMPQBlock * pBlockTable, - DWORD dwClaimedSize) +void ClearFileEntry( + TMPQArchive * ha, + TFileEntry * pFileEntry) { - TMPQHeader * pHeader = ha->pHeader; - ULONGLONG BlockTableStart; - ULONGLONG BlockTableEnd; - ULONGLONG FileDataStart; + TMPQHash * pHash = NULL; - // Only perform this check on MPQs version 1.0 - if(pHeader->dwHeaderSize == MPQ_HEADER_SIZE_V1) + // If the MPQ has classic hash table, clear the entry there + if(ha->pHashTable != NULL) { - // Calculate claimed block table begin and end - BlockTableStart = ha->MpqPos + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); - BlockTableEnd = BlockTableStart + (pHeader->dwBlockTableSize * sizeof(TMPQBlock)); + assert(pFileEntry->dwHashIndex < ha->pHeader->dwHashTableSize); - for(DWORD i = 0; i < dwClaimedSize; i++) - { - // If the block table end goes into that file, fix the block table end - FileDataStart = ha->MpqPos + pBlockTable[i].dwFilePos; - if(BlockTableStart < FileDataStart && BlockTableEnd > FileDataStart) - { - dwClaimedSize = (DWORD)((FileDataStart - BlockTableStart) / sizeof(TMPQBlock)); - BlockTableEnd = FileDataStart; - } - } + pHash = ha->pHashTable + pFileEntry->dwHashIndex; + memset(pHash, 0xFF, sizeof(TMPQHash)); + pHash->dwBlockIndex = HASH_ENTRY_DELETED; } - // Fix the block table size - pHeader->BlockTableSize64 = dwClaimedSize * sizeof(TMPQBlock); - pHeader->dwBlockTableSize = dwClaimedSize; + // If the MPQ has HET table, clear the entry there as well + if(ha->pHetTable != NULL) + { + TMPQHetTable * pHetTable = ha->pHetTable; + DWORD dwInvalidFileIndex = (1 << pHetTable->dwIndexSizeTotal) - 1; + + assert(pFileEntry->dwHetIndex < pHetTable->dwHashTableSize); + + // Clear the entry in the HET hash array + pHetTable->pHetHashes[pFileEntry->dwHetIndex] = HET_ENTRY_DELETED; + + // Set the BET index to invalid index + SetBits(pHetTable->pBetIndexes, pHetTable->dwIndexSizeTotal * pFileEntry->dwHetIndex, + pHetTable->dwIndexSize, + &dwInvalidFileIndex, + 4); + } + + // Free the file name, and set the file entry as deleted + if(pFileEntry->szFileName != NULL) + STORM_FREE(pFileEntry->szFileName); + + // Invalidate the file entry + memset(pFileEntry, 0, sizeof(TFileEntry)); } -static int BuildFileTable_Classic( - TMPQArchive * ha, - TFileEntry * pFileTable, - ULONGLONG FileSize) +int FreeFileEntry( + TMPQArchive * ha, + TFileEntry * pFileEntry) { - TFileEntry * pFileEntry; - TMPQHeader * pHeader = ha->pHeader; - TMPQBlock * pBlockTable; - TMPQBlock * pBlock; + TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; + TFileEntry * pTempEntry; int nError = ERROR_SUCCESS; - // Sanity checks - assert(ha->pHashTable != NULL); + // + // If we have HET table, we cannot just get rid of the file + // Doing so would lead to empty gaps in the HET table + // We have to keep BET hash, hash index, HET index, locale, platform and file name + // - // Do nothing if the size of the block table is zero - if(pHeader->dwBlockTablePos != 0 && pHeader->dwBlockTableSize != 0) + if(ha->pHetTable == NULL) { - // Allocate space for the block table - // Note: pHeader->dwBlockTableSize can be zero !!! - pBlockTable = ALLOCMEM(TMPQBlock, ha->dwMaxFileCount); - if(pBlockTable != NULL) - { - ULONGLONG ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); - TMPQHash * pHashEnd = ha->pHashTable + pHeader->dwHashTableSize; - TMPQHash * pHash; - DWORD dwTableSize = pHeader->dwBlockTableSize * sizeof(TMPQBlock); - DWORD dwCmpSize = (DWORD)pHeader->BlockTableSize64; - - // Fill the block table with zeros - memset(pBlockTable, 0, dwTableSize); - - // I have found a MPQ which claimed 0x200 entries in the block table, - // but the file was cut and there was only 0x1A0 entries. - // We will handle this case properly. - if(dwTableSize == dwCmpSize && (ByteOffset + dwTableSize) > FileSize) - { - pHeader->dwBlockTableSize = (DWORD)((FileSize - ByteOffset) / sizeof(TMPQBlock)); - pHeader->BlockTableSize64 = pHeader->dwBlockTableSize * sizeof(TMPQBlock); - dwTableSize = dwCmpSize = pHeader->dwBlockTableSize * sizeof(TMPQBlock); - } + TFileEntry * pLastFileEntry = ha->pFileTable + ha->dwFileTableSize - 1; + TFileEntry * pLastUsedEntry = pLastFileEntry; - // - // One of the first cracked versions of Diablo had block table unencrypted - // StormLib does NOT support such MPQs anymore, as they are incompatible - // with compressed block table feature - // + // Zero the file entry + ClearFileEntry(ha, pFileEntry); - // Load the block table - nError = LoadMpqTable(ha, ByteOffset, pBlockTable, dwCmpSize, dwTableSize, MPQ_KEY_BLOCK_TABLE); - if(nError == ERROR_SUCCESS) - { - // Defense against MPQs that that claim block table to be bigger than it really is - FixBlockTableSize(ha, pBlockTable, pHeader->dwBlockTableSize); - - // Merge the block table to the file table - for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++) - { - if(pHash->dwBlockIndex < pHeader->dwBlockTableSize) - { - pBlock = pBlockTable + pHash->dwBlockIndex; - - // - // Yet another silly map protector: For each valid file, - // there are 4 items in the hash table, that appears to be valid: - // - // a6d79af0 e61a0932 001e0000 0000770b <== Fake valid - // a6d79af0 e61a0932 0000d761 0000dacb <== Fake valid - // a6d79af0 e61a0932 00000000 0000002f <== Real file entry - // a6d79af0 e61a0932 00005a4f 000093bc <== Fake valid - // - - if(!(pBlock->dwFlags & ~MPQ_FILE_VALID_FLAGS) && (pBlock->dwFlags & MPQ_FILE_EXISTS)) - { - // Get the entry - pFileEntry = pFileTable + pHash->dwBlockIndex; - - // Fill the entry - pFileEntry->ByteOffset = pBlock->dwFilePos; - pFileEntry->dwHashIndex = (DWORD)(pHash - ha->pHashTable); - pFileEntry->dwFileSize = pBlock->dwFSize; - pFileEntry->dwCmpSize = pBlock->dwCSize; - pFileEntry->dwFlags = pBlock->dwFlags; - pFileEntry->lcLocale = pHash->lcLocale; - pFileEntry->wPlatform = pHash->wPlatform; - } - else - { - // If the hash table entry doesn't point to the valid file item, - // we invalidate the entire hash table entry - pHash->dwName1 = 0xFFFFFFFF; - pHash->dwName2 = 0xFFFFFFFF; - pHash->lcLocale = 0xFFFF; - pHash->wPlatform = 0xFFFF; - pHash->dwBlockIndex = HASH_ENTRY_DELETED; - } - } - } - } - - // Free the block table - FREEMEM(pBlockTable); + // Now there is a chance that we created a chunk of free + // file entries at the end of the file table. We check this + // and eventually free all deleted file entries at the end + for(pTempEntry = ha->pFileTable; pTempEntry < pFileTableEnd; pTempEntry++) + { + // Is that an occupied file entry? + if(pTempEntry->dwFlags & MPQ_FILE_EXISTS) + pLastUsedEntry = pTempEntry; } - else + + // Can we free some entries at the end? + if(pLastUsedEntry < pLastFileEntry) { - nError = ERROR_NOT_ENOUGH_MEMORY; + // Fix the size of the file table entry + ha->dwFileTableSize = (DWORD)(pLastUsedEntry - ha->pFileTable) + 1; + ha->pHeader->dwBlockTableSize = ha->dwFileTableSize; } } - - // Load the hi-block table - if(nError == ERROR_SUCCESS && pHeader->HiBlockTablePos64 != 0) + else { - ULONGLONG ByteOffset; - USHORT * pHiBlockTable = NULL; - DWORD dwTableSize = pHeader->dwBlockTableSize * sizeof(USHORT); + // Note: Deleted entries in Blizzard MPQs version 4.0 + // normally contain valid byte offset and length + pFileEntry->dwFlags &= ~MPQ_FILE_EXISTS; + nError = ERROR_SUCCESS; + } - // Allocate space for the hi-block table - // Note: pHeader->dwBlockTableSize can be zero !!! - pHiBlockTable = ALLOCMEM(USHORT, pHeader->dwBlockTableSize + 1); - if(pHiBlockTable != NULL) - { - // Load the hi-block table. It is not encrypted, nor compressed - ByteOffset = ha->MpqPos + pHeader->HiBlockTablePos64; - if(!FileStream_Read(ha->pStream, &ByteOffset, pHiBlockTable, dwTableSize)) - nError = GetLastError(); + return nError; +} - // Now merge the hi-block table to the file table - if(nError == ERROR_SUCCESS) - { - pFileEntry = pFileTable; +void InvalidateInternalFiles(TMPQArchive * ha) +{ + TFileEntry * pFileEntry; - // Add the high file offset to the base file offset. - // We also need to swap it during the process. - for(DWORD i = 0; i < pHeader->dwBlockTableSize; i++) - { - pFileEntry->ByteOffset |= ((ULONGLONG)BSWAP_INT16_UNSIGNED(pHiBlockTable[i]) << 32); - pFileEntry++; - } - } + // Invalidate the (listfile), if not done yet + if(!(ha->dwFlags & MPQ_FLAG_INV_LISTFILE)) + { + pFileEntry = GetFileEntryExact(ha, LISTFILE_NAME, LANG_NEUTRAL); + if(pFileEntry != NULL) + FreeFileEntry(ha, pFileEntry); + ha->dwFlags |= MPQ_FLAG_INV_LISTFILE; + } - // Free the hi-block table - FREEMEM(pHiBlockTable); - } - else - { - nError = ERROR_NOT_ENOUGH_MEMORY; - } + // Invalidate the (attributes), if not done yet + if(!(ha->dwFlags & MPQ_FLAG_INV_ATTRIBUTES)) + { + pFileEntry = GetFileEntryExact(ha, ATTRIBUTES_NAME, LANG_NEUTRAL); + if(pFileEntry != NULL) + FreeFileEntry(ha, pFileEntry); + ha->dwFlags |= MPQ_FLAG_INV_ATTRIBUTES; } - // Set the current size of the file table - ha->dwFileTableSize = pHeader->dwBlockTableSize; - return nError; + // Remember that the MPQ has been changed and it will be necessary + // to update the tables + ha->dwFlags |= MPQ_FLAG_CHANGED; } -static int BuildFileTable_HetBet( - TMPQArchive * ha, - TFileEntry * pFileTable) -{ - TMPQHetTable * pHetTable = ha->pHetTable; - TMPQBetTable * pBetTable = NULL; - TMPQExtTable * pExtTable; - TFileEntry * pFileEntry = pFileTable; - TMPQHeader * pHeader = ha->pHeader; - TBitArray * pBitArray; - DWORD dwBitPosition = 0; - DWORD i; - int nError = ERROR_FILE_CORRUPT; +//----------------------------------------------------------------------------- +// Functions that loads and verify MPQ data bitmap - // Load the BET table from the MPQ - pExtTable = LoadExtTable(ha, pHeader->BetTablePos64, (size_t)pHeader->BetTableSize64, BET_TABLE_SIGNATURE, MPQ_KEY_BLOCK_TABLE); - if(pExtTable != NULL) - { - // Do nothing if the BET table is missing or corrupt - pBetTable = TranslateBetTable(ha, pExtTable); - if(pBetTable != NULL) +int LoadMpqDataBitmap(TMPQArchive * ha, ULONGLONG FileSize, bool * pbFileIsComplete) +{ + TMPQBitmap * pBitmap = NULL; + TMPQBitmap DataBitmap; + ULONGLONG BitmapOffset; + ULONGLONG EndOfMpq; + DWORD DataBlockCount = 0; + DWORD BitmapByteSize; + DWORD WholeByteCount; + DWORD ExtraBitsCount; + + // Is there enough space for a MPQ bitmap? + EndOfMpq = ha->MpqPos + ha->pHeader->ArchiveSize64; + FileSize = FileSize - sizeof(TMPQBitmap); + if(FileSize > EndOfMpq) + { + // Try to load the data bitmap from the end of the file + if(FileStream_Read(ha->pStream, &FileSize, &DataBitmap, sizeof(TMPQBitmap))) { - // Step one: Fill the indexes to the HET table - for(i = 0; i < pHetTable->dwHashTableSize; i++) + // Is it a valid data bitmap? + BSWAP_ARRAY32_UNSIGNED((LPDWORD)(&DataBitmap), sizeof(TMPQBitmap)); + if(DataBitmap.dwSignature == MPQ_DATA_BITMAP_SIGNATURE) { - DWORD dwFileIndex = 0; + // We assume that MPQs with data bitmap begin at position 0 + assert(ha->MpqPos == 0); - // Is the entry in the HET table occupied? - if(pHetTable->pHetHashes[i] != 0) + // Calculate the number of extra bytes for data bitmap + DataBlockCount = (DWORD)(((ha->pHeader->ArchiveSize64 - 1) / DataBitmap.dwBlockSize) + 1); + BitmapByteSize = ((DataBlockCount - 1) / 8) + 1; + + // Verify the data block size + BitmapOffset = ((ULONGLONG)DataBitmap.dwMapOffsetHi << 32) | DataBitmap.dwMapOffsetLo; + assert((DWORD)(FileSize - BitmapOffset) == BitmapByteSize); + + // Allocate space for the data bitmap + pBitmap = (TMPQBitmap *)STORM_ALLOC(BYTE, sizeof(TMPQBitmap) + BitmapByteSize); + if(pBitmap != NULL) { - // Load the index to the BET table - pHetTable->pBetIndexes->GetBits(pHetTable->dwIndexSizeTotal * i, - pHetTable->dwIndexSize, - &dwFileIndex, - 4); - // Overflow test - if(dwFileIndex < ha->dwMaxFileCount) + // Copy the bitmap header + memcpy(pBitmap, &DataBitmap, sizeof(TMPQBitmap)); + + // Read the remaining part + if(!FileStream_Read(ha->pStream, &BitmapOffset, (pBitmap + 1), BitmapByteSize)) { - // Get the file entry and save HET index - pFileEntry = pFileTable + dwFileIndex; - pFileEntry->dwHetIndex = i; - - // Load the BET hash - pBetTable->pBetHashes->GetBits(pBetTable->dwBetHashSizeTotal * dwFileIndex, - pBetTable->dwBetHashSize, - &pFileEntry->BetHash, - 8); + STORM_FREE(pBitmap); + pBitmap = NULL; } } } + } + } - // Go through the entire BET table and convert it to the file table. - pFileEntry = pFileTable; - pBitArray = pBetTable->pFileTable; - for(i = 0; i < pBetTable->dwMaxFileCount; i++) - { - DWORD dwFlagIndex = 0; - - // Read the file position - pBitArray->GetBits(dwBitPosition + pBetTable->dwBitIndex_FilePos, - pBetTable->dwBitCount_FilePos, - &pFileEntry->ByteOffset, - 8); - - // Read the file size - pBitArray->GetBits(dwBitPosition + pBetTable->dwBitIndex_FileSize, - pBetTable->dwBitCount_FileSize, - &pFileEntry->dwFileSize, - 4); - - // Read the compressed size - pBitArray->GetBits(dwBitPosition + pBetTable->dwBitIndex_CmpSize, - pBetTable->dwBitCount_CmpSize, - &pFileEntry->dwCmpSize, - 4); - - - // Read the flag index - if(pBetTable->dwFlagCount != 0) - { - pBitArray->GetBits(dwBitPosition + pBetTable->dwBitIndex_FlagIndex, - pBetTable->dwBitCount_FlagIndex, - &dwFlagIndex, - 4); + // If the caller asks for file completeness, check it + if(pBitmap != NULL && pbFileIsComplete != NULL) + { + LPBYTE pbBitmap = (LPBYTE)(pBitmap + 1); + DWORD i; + bool bFileIsComplete = true; - pFileEntry->dwFlags = pBetTable->pFileFlags[dwFlagIndex]; - } + // Calculate the number of whole bytes and extra bits of the bitmap + WholeByteCount = (DataBlockCount / 8); + ExtraBitsCount = (DataBlockCount & 7); - // - // TODO: Locale (?) - // + // Verify the whole bytes - their value must be 0xFF + for(i = 0; i < WholeByteCount; i++) + { + if(pbBitmap[i] != 0xFF) + bFileIsComplete = false; + } - // Move the current bit position - dwBitPosition += pBetTable->dwTableEntrySize; - pFileEntry++; - } + // If there are extra bits, calculate the mask + if(ExtraBitsCount != 0) + { + BYTE ExpectedValue = (BYTE)((1 << ExtraBitsCount) - 1); - // Set the current size of the file table - ha->dwFileTableSize = pBetTable->dwMaxFileCount; - FreeBetTable(pBetTable); - nError = ERROR_SUCCESS; + if(pbBitmap[i] != ExpectedValue) + bFileIsComplete = false; } - // Free the loaded extended table - FREEMEM(pExtTable); + // Give the result to the caller + *pbFileIsComplete = bFileIsComplete; } - return nError; + ha->pBitmap = pBitmap; + return ERROR_SUCCESS; } +//----------------------------------------------------------------------------- +// Support for file tables - hash table, block table, hi-block table + int CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize) { TMPQHash * pHashTable; @@ -1931,18 +1853,21 @@ int CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize) assert(ha->pHashTable == NULL); // Create the hash table - pHashTable = ALLOCMEM(TMPQHash, dwHashTableSize); + pHashTable = STORM_ALLOC(TMPQHash, dwHashTableSize); if(pHashTable == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Fill it memset(pHashTable, 0xFF, dwHashTableSize * sizeof(TMPQHash)); - ha->dwMaxFileCount = dwHashTableSize; ha->pHashTable = pHashTable; + + // Set the max file count, if needed + if(ha->pHetTable == NULL) + ha->dwMaxFileCount = dwHashTableSize; return ERROR_SUCCESS; } -int LoadHashTable(TMPQArchive * ha) +TMPQHash * LoadHashTable(TMPQArchive * ha) { TMPQHeader * pHeader = ha->pHeader; ULONGLONG ByteOffset; @@ -1953,43 +1878,137 @@ int LoadHashTable(TMPQArchive * ha) // If the MPQ has no hash table, do nothing if(pHeader->dwHashTablePos == 0 && pHeader->wHashTablePosHi == 0) - return ERROR_SUCCESS; + return NULL; // If the hash table size is zero, do nothing if(pHeader->dwHashTableSize == 0) - return ERROR_SUCCESS; + return NULL; // Allocate buffer for the hash table dwTableSize = pHeader->dwHashTableSize * sizeof(TMPQHash); - pHashTable = ALLOCMEM(TMPQHash, pHeader->dwHashTableSize); + pHashTable = STORM_ALLOC(TMPQHash, pHeader->dwHashTableSize); if(pHashTable == NULL) - return ERROR_NOT_ENOUGH_MEMORY; + return NULL; // Compressed size of the hash table dwCmpSize = (DWORD)pHeader->HashTableSize64; - // + // // Load the table from the MPQ, with decompression // // Note: We will NOT check if the hash table is properly decrypted. // Some MPQ protectors corrupt the hash table by rewriting part of it. // Hash table, the way how it works, allows arbitrary values for unused entries. - // + // ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos); nError = LoadMpqTable(ha, ByteOffset, pHashTable, dwCmpSize, dwTableSize, MPQ_KEY_HASH_TABLE); if(nError != ERROR_SUCCESS) { - FREEMEM(pHashTable); + STORM_FREE(pHashTable); pHashTable = NULL; - return nError; } - // Set the maximum file count to the size of the hash table - // Store the hash table to the MPQ - ha->dwMaxFileCount = pHeader->dwHashTableSize; - ha->pHashTable = pHashTable; - return ERROR_SUCCESS; + // Return the hash table + return pHashTable; +} + +static void FixBlockTableSize( + TMPQArchive * ha, + TMPQBlock * pBlockTable, + DWORD dwClaimedSize) +{ + TMPQHeader * pHeader = ha->pHeader; + ULONGLONG BlockTableStart; + ULONGLONG BlockTableEnd; + ULONGLONG FileDataStart; + + // Only perform this check on MPQs version 1.0 + if(pHeader->dwHeaderSize == MPQ_HEADER_SIZE_V1) + { + // Calculate claimed block table begin and end + BlockTableStart = ha->MpqPos + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); + BlockTableEnd = BlockTableStart + (pHeader->dwBlockTableSize * sizeof(TMPQBlock)); + + for(DWORD i = 0; i < dwClaimedSize; i++) + { + // If the block table end goes into that file, fix the block table end + FileDataStart = ha->MpqPos + pBlockTable[i].dwFilePos; + if(BlockTableStart < FileDataStart && BlockTableEnd > FileDataStart) + { + dwClaimedSize = (DWORD)((FileDataStart - BlockTableStart) / sizeof(TMPQBlock)); + BlockTableEnd = FileDataStart; + } + } + } + + // Fix the block table size + pHeader->BlockTableSize64 = dwClaimedSize * sizeof(TMPQBlock); + pHeader->dwBlockTableSize = dwClaimedSize; +} + +TMPQBlock * LoadBlockTable(TMPQArchive * ha, ULONGLONG FileSize) +{ + TMPQHeader * pHeader = ha->pHeader; + TMPQBlock * pBlockTable; + ULONGLONG ByteOffset; + DWORD dwTableSize; + DWORD dwCmpSize; + int nError; + + // Do nothing if the block table position is zero + if(pHeader->dwBlockTablePos == 0 && pHeader->wBlockTablePosHi == 0) + return NULL; + + // Do nothing if the block table size is zero + if(pHeader->dwBlockTableSize == 0) + return NULL; + + // Sanity check, enforced by LoadAnyHashTable + assert(ha->dwMaxFileCount >= pHeader->dwBlockTableSize); + + // Calculate sizes of both tables + ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); + dwTableSize = pHeader->dwBlockTableSize * sizeof(TMPQBlock); + dwCmpSize = (DWORD)pHeader->BlockTableSize64; + + // Allocate space for the block table + // Note: pHeader->dwBlockTableSize can be zero !!! + pBlockTable = STORM_ALLOC(TMPQBlock, ha->dwMaxFileCount); + if(pBlockTable == NULL) + return NULL; + + // Fill the block table with zeros + memset(pBlockTable, 0, dwTableSize); + + // I found a MPQ which claimed 0x200 entries in the block table, + // but the file was cut and there was only 0x1A0 entries. + // We will handle this case properly. + if(dwTableSize == dwCmpSize && (ByteOffset + dwTableSize) > FileSize) + { + pHeader->dwBlockTableSize = (DWORD)((FileSize - ByteOffset) / sizeof(TMPQBlock)); + pHeader->BlockTableSize64 = pHeader->dwBlockTableSize * sizeof(TMPQBlock); + dwTableSize = dwCmpSize = pHeader->dwBlockTableSize * sizeof(TMPQBlock); + } + + // + // One of the first cracked versions of Diablo I had block table unencrypted + // StormLib does NOT support such MPQs anymore, as they are incompatible + // with compressed block table feature + // + + // Load the block table + nError = LoadMpqTable(ha, ByteOffset, pBlockTable, dwCmpSize, dwTableSize, MPQ_KEY_BLOCK_TABLE); + if(nError != ERROR_SUCCESS) + { + // Failed, sorry + STORM_FREE(pBlockTable); + return NULL; + } + + // Defense against MPQs that claim block table to be bigger than it really is + FixBlockTableSize(ha, pBlockTable, pHeader->dwBlockTableSize); + return pBlockTable; } int LoadHetTable(TMPQArchive * ha) @@ -2013,7 +2032,7 @@ int LoadHetTable(TMPQArchive * ha) if(ha->pHetTable != NULL) ha->dwMaxFileCount = ha->pHetTable->dwMaxFileCount; - FREEMEM(pExtTable); + STORM_FREE(pExtTable); } // If the HET hable failed to load, it's corrupt. @@ -2024,26 +2043,301 @@ int LoadHetTable(TMPQArchive * ha) return nError; } +TMPQBetTable * LoadBetTable(TMPQArchive * ha) +{ + TMPQExtTable * pExtTable; + TMPQBetTable * pBetTable = NULL; + TMPQHeader * pHeader = ha->pHeader; + + // If the HET table position is not NULL, we expect + // both HET and BET tables to be present. + if(pHeader->BetTablePos64 != 0) + { + // Attempt to load the HET table (Hash Extended Table) + pExtTable = LoadExtTable(ha, pHeader->BetTablePos64, (size_t)pHeader->BetTableSize64, BET_TABLE_SIGNATURE, MPQ_KEY_BLOCK_TABLE); + if(pExtTable != NULL) + { + // If succeeded, we translate the BET table + // to more readable form + pBetTable = TranslateBetTable(ha, pExtTable); + STORM_FREE(pExtTable); + } + } + + return pBetTable; +} + int LoadAnyHashTable(TMPQArchive * ha) { TMPQHeader * pHeader = ha->pHeader; - bool bHashTableLoaded = false; // If the MPQ archive is empty, don't bother trying to load anything if(pHeader->dwHashTableSize == 0 && pHeader->HetTableSize64 == 0) return CreateHashTable(ha, HASH_TABLE_SIZE_DEFAULT); - // Try to load HET table - if(LoadHetTable(ha) == ERROR_SUCCESS) - bHashTableLoaded = true; + // Try to load HET and/or classic hash table + LoadHetTable(ha); + + // Load the HASH table + ha->pHashTable = LoadHashTable(ha); + + // Set the maximum file count to the size of the hash table + // In case there is HET table, we have to keep the file limit + if(ha->pHetTable == NULL) + ha->dwMaxFileCount = pHeader->dwHashTableSize; + + // Did at least one succeed? + if(ha->pHetTable == NULL && ha->pHashTable == NULL) + return ERROR_FILE_CORRUPT; - // Try to load the classic hash table - if(LoadHashTable(ha) == ERROR_SUCCESS) - bHashTableLoaded = true; - - return bHashTableLoaded ? ERROR_SUCCESS : ERROR_FILE_CORRUPT; + // In theory, a MPQ could have bigger block table than hash table + if(ha->pHeader->dwBlockTableSize > ha->dwMaxFileCount) + { + ha->dwMaxFileCount = ha->pHeader->dwBlockTableSize; + ha->dwFlags |= MPQ_FLAG_READ_ONLY; + } + + return ERROR_SUCCESS; } +int BuildFileTable_Classic( + TMPQArchive * ha, + TFileEntry * pFileTable, + ULONGLONG FileSize) +{ + TFileEntry * pFileEntry; + TMPQHeader * pHeader = ha->pHeader; + TMPQBlock * pBlockTable; + TMPQBlock * pBlock; + int nError = ERROR_SUCCESS; + + // Sanity checks + assert(ha->pHashTable != NULL); + + // Load the block table + pBlockTable = LoadBlockTable(ha, FileSize); + if(pBlockTable != NULL) + { + TMPQHash * pHashEnd = ha->pHashTable + pHeader->dwHashTableSize; + TMPQHash * pHash; + + // If we don't have HET table, we build the file entries from the hash&block tables + if(ha->pHetTable == NULL) + { + for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++) + { + if(pHash->dwBlockIndex < pHeader->dwBlockTableSize) + { + pFileEntry = pFileTable + pHash->dwBlockIndex; + pBlock = pBlockTable + pHash->dwBlockIndex; + + // + // Yet another silly map protector: For each valid file, + // there are 4 items in the hash table, that appears to be valid: + // + // a6d79af0 e61a0932 001e0000 0000770b <== Fake valid + // a6d79af0 e61a0932 0000d761 0000dacb <== Fake valid + // a6d79af0 e61a0932 00000000 0000002f <== Real file entry + // a6d79af0 e61a0932 00005a4f 000093bc <== Fake valid + // + + if(!(pBlock->dwFlags & ~MPQ_FILE_VALID_FLAGS) && (pBlock->dwFlags & MPQ_FILE_EXISTS)) + { + // Fill the entry + pFileEntry->ByteOffset = pBlock->dwFilePos; + pFileEntry->dwHashIndex = (DWORD)(pHash - ha->pHashTable); + pFileEntry->dwFileSize = pBlock->dwFSize; + pFileEntry->dwCmpSize = pBlock->dwCSize; + pFileEntry->dwFlags = pBlock->dwFlags; + pFileEntry->lcLocale = pHash->lcLocale; + pFileEntry->wPlatform = pHash->wPlatform; + } + else + { + // If the hash table entry doesn't point to the valid file item, + // we invalidate the entire hash table entry + pHash->dwName1 = 0xFFFFFFFF; + pHash->dwName2 = 0xFFFFFFFF; + pHash->lcLocale = 0xFFFF; + pHash->wPlatform = 0xFFFF; + pHash->dwBlockIndex = HASH_ENTRY_DELETED; + } + } + } + } + else + { + for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++) + { + if(pHash->dwBlockIndex < ha->dwFileTableSize) + { + pFileEntry = pFileTable + pHash->dwBlockIndex; + if(pFileEntry->dwFlags & MPQ_FILE_EXISTS) + { + pFileEntry->dwHashIndex = (DWORD)(pHash - ha->pHashTable); + pFileEntry->lcLocale = pHash->lcLocale; + pFileEntry->wPlatform = pHash->wPlatform; + } + } + } + } + + // Free the block table + STORM_FREE(pBlockTable); + } + else + { + nError = ERROR_NOT_ENOUGH_MEMORY; + } + + // Load the hi-block table + if(nError == ERROR_SUCCESS && pHeader->HiBlockTablePos64 != 0) + { + ULONGLONG ByteOffset; + USHORT * pHiBlockTable = NULL; + DWORD dwTableSize = pHeader->dwBlockTableSize * sizeof(USHORT); + + // Allocate space for the hi-block table + // Note: pHeader->dwBlockTableSize can be zero !!! + pHiBlockTable = STORM_ALLOC(USHORT, pHeader->dwBlockTableSize + 1); + if(pHiBlockTable != NULL) + { + // Load the hi-block table. It is not encrypted, nor compressed + ByteOffset = ha->MpqPos + pHeader->HiBlockTablePos64; + if(!FileStream_Read(ha->pStream, &ByteOffset, pHiBlockTable, dwTableSize)) + nError = GetLastError(); + + // Now merge the hi-block table to the file table + if(nError == ERROR_SUCCESS) + { + pFileEntry = pFileTable; + + // Add the high file offset to the base file offset. + // We also need to swap it during the process. + for(DWORD i = 0; i < pHeader->dwBlockTableSize; i++) + { + pFileEntry->ByteOffset |= ((ULONGLONG)BSWAP_INT16_UNSIGNED(pHiBlockTable[i]) << 32); + pFileEntry++; + } + } + + // Free the hi-block table + STORM_FREE(pHiBlockTable); + } + else + { + nError = ERROR_NOT_ENOUGH_MEMORY; + } + } + + // Set the current size of the file table + ha->dwFileTableSize = pHeader->dwBlockTableSize; + return nError; +} + +int BuildFileTable_HetBet( + TMPQArchive * ha, + TFileEntry * pFileTable) +{ + TMPQHetTable * pHetTable = ha->pHetTable; + TMPQBetTable * pBetTable; + TFileEntry * pFileEntry = pFileTable; + TBitArray * pBitArray; + DWORD dwBitPosition = 0; + DWORD i; + int nError = ERROR_FILE_CORRUPT; + + // Load the BET table from the MPQ + pBetTable = LoadBetTable(ha); + if(pBetTable != NULL) + { + // Step one: Fill the indexes to the HET table + for(i = 0; i < pHetTable->dwHashTableSize; i++) + { + DWORD dwFileIndex = 0; + + // Is the entry in the HET table occupied? + if(pHetTable->pHetHashes[i] != 0) + { + // Load the index to the BET table + GetBits(pHetTable->pBetIndexes, pHetTable->dwIndexSizeTotal * i, + pHetTable->dwIndexSize, + &dwFileIndex, + 4); + // Overflow test + if(dwFileIndex < pBetTable->dwFileCount) + { + // Get the file entry and save HET index + pFileEntry = pFileTable + dwFileIndex; + pFileEntry->dwHetIndex = i; + + // Load the BET hash + GetBits(pBetTable->pBetHashes, pBetTable->dwBetHashSizeTotal * dwFileIndex, + pBetTable->dwBetHashSize, + &pFileEntry->BetHash, + 8); + } + } + } + + // Go through the entire BET table and convert it to the file table. + pFileEntry = pFileTable; + pBitArray = pBetTable->pFileTable; + for(i = 0; i < pBetTable->dwFileCount; i++) + { + DWORD dwFlagIndex = 0; + + // Read the file position + GetBits(pBitArray, dwBitPosition + pBetTable->dwBitIndex_FilePos, + pBetTable->dwBitCount_FilePos, + &pFileEntry->ByteOffset, + 8); + + // Read the file size + GetBits(pBitArray, dwBitPosition + pBetTable->dwBitIndex_FileSize, + pBetTable->dwBitCount_FileSize, + &pFileEntry->dwFileSize, + 4); + + // Read the compressed size + GetBits(pBitArray, dwBitPosition + pBetTable->dwBitIndex_CmpSize, + pBetTable->dwBitCount_CmpSize, + &pFileEntry->dwCmpSize, + 4); + + + // Read the flag index + if(pBetTable->dwFlagCount != 0) + { + GetBits(pBitArray, dwBitPosition + pBetTable->dwBitIndex_FlagIndex, + pBetTable->dwBitCount_FlagIndex, + &dwFlagIndex, + 4); + + pFileEntry->dwFlags = pBetTable->pFileFlags[dwFlagIndex]; + } + + // + // TODO: Locale (?) + // + + // Move the current bit position + dwBitPosition += pBetTable->dwTableEntrySize; + pFileEntry++; + } + + // Set the current size of the file table + ha->dwFileTableSize = pBetTable->dwFileCount; + FreeBetTable(pBetTable); + nError = ERROR_SUCCESS; + } + else + { + nError = ERROR_FILE_CORRUPT; + } + + return nError; +} int BuildFileTable(TMPQArchive * ha, ULONGLONG FileSize) { @@ -2055,7 +2349,7 @@ int BuildFileTable(TMPQArchive * ha, ULONGLONG FileSize) assert(ha->dwMaxFileCount != 0); // Allocate the file table with size determined before - pFileTable = ALLOCMEM(TFileEntry, ha->dwMaxFileCount); + pFileTable = STORM_ALLOC(TFileEntry, ha->dwMaxFileCount); if(pFileTable == NULL) return ERROR_NOT_ENOUGH_MEMORY; @@ -2081,11 +2375,11 @@ int BuildFileTable(TMPQArchive * ha, ULONGLONG FileSize) else bFileTableCreated = true; } - + // If something failed, we free the file table entry if(bFileTableCreated == false) { - FREEMEM(pFileTable); + STORM_FREE(pFileTable); return ERROR_FILE_CORRUPT; } @@ -2097,9 +2391,9 @@ int BuildFileTable(TMPQArchive * ha, ULONGLONG FileSize) // Saves MPQ header, hash table, block table and hi-block table. int SaveMPQTables(TMPQArchive * ha) { - TMPQHeader * pHeader = ha->pHeader; TMPQExtTable * pHetTable = NULL; TMPQExtTable * pBetTable = NULL; + TMPQHeader * pHeader = ha->pHeader; TMPQBlock * pBlockTable = NULL; TMPQHash * pHashTable = NULL; ULONGLONG HetTableSize64 = 0; @@ -2116,7 +2410,7 @@ int SaveMPQTables(TMPQArchive * ha) // We expect this function to be called only when tables have been changed assert(ha->dwFlags & MPQ_FLAG_CHANGED); - // Find the space where the MPQ tables will be saved + // Find the space where the MPQ tables will be saved FindFreeMpqSpace(ha, &TablePos); // If the MPQ has HET table, we prepare a ready-to-save version @@ -2207,7 +2501,7 @@ int SaveMPQTables(TMPQArchive * ha) pHeader->HiBlockTableSize64 = HiBlockTableSize64; pHeader->HiBlockTablePos64 = TablePos; BSWAP_ARRAY16_UNSIGNED(pHiBlockTable, HiBlockTableSize64); - + if(!FileStream_Write(ha->pStream, &ByteOffset, pHiBlockTable, (DWORD)HiBlockTableSize64)) nError = GetLastError(); TablePos += HiBlockTableSize64; @@ -2225,16 +2519,12 @@ int SaveMPQTables(TMPQArchive * ha) // Write the MPQ header if(nError == ERROR_SUCCESS) { - hash_state md5_state; - // Update the size of the archive pHeader->ArchiveSize64 = TablePos; pHeader->dwArchiveSize = (DWORD)TablePos; - + // Update the MD5 of the archive header - md5_init(&md5_state); - md5_process(&md5_state, (unsigned char *)pHeader, MPQ_HEADER_SIZE_V4 - MD5_DIGEST_SIZE); - md5_done(&md5_state, pHeader->MD5_MpqHeader); + CalculateDataBlockHash(pHeader, MPQ_HEADER_SIZE_V4 - MD5_DIGEST_SIZE, pHeader->MD5_MpqHeader); // Write the MPQ header to the file BSWAP_TMPQHEADER(pHeader); @@ -2245,18 +2535,18 @@ int SaveMPQTables(TMPQArchive * ha) // Clear the changed flag if(nError == ERROR_SUCCESS) - ha->dwFlags &= ~(MPQ_FLAG_CHANGED | MPQ_FLAG_LISTFILE_VALID | MPQ_FLAG_ATTRIBS_VALID); + ha->dwFlags &= ~MPQ_FLAG_CHANGED; // Cleanup and exit if(pHetTable != NULL) - FREEMEM(pHetTable); + STORM_FREE(pHetTable); if(pBetTable != NULL) - FREEMEM(pBetTable); + STORM_FREE(pBetTable); if(pHashTable != NULL) - FREEMEM(pHashTable); + STORM_FREE(pHashTable); if(pBlockTable != NULL) - FREEMEM(pBlockTable); + STORM_FREE(pBlockTable); if(pHiBlockTable != NULL) - FREEMEM(pHiBlockTable); + STORM_FREE(pHiBlockTable); return nError; } diff --git a/dep/StormLib/src/SCompression.cpp b/dep/StormLib/src/SCompression.cpp index 966bb704ee1..19ef5940fc4 100644 --- a/dep/StormLib/src/SCompression.cpp +++ b/dep/StormLib/src/SCompression.cpp @@ -2,7 +2,7 @@ /* SCompression.cpp Copyright (c) Ladislav Zezula 2003 */ /*---------------------------------------------------------------------------*/ /* This module serves as a bridge between StormLib code and (de)compression */ -/* functions. All (de)compression calls go (and should only go) through this */ +/* functions. All (de)compression calls go (and should only go) through this */ /* module. No system headers should be included in this module to prevent */ /* compile-time problems. */ /*---------------------------------------------------------------------------*/ @@ -13,7 +13,6 @@ /*****************************************************************************/ #define __STORMLIB_SELF__ -#define __INCLUDE_COMPRESSION__ #include "StormLib.h" #include "StormCommon.h" @@ -33,25 +32,25 @@ typedef struct // Function doesn't return an error. A success means that the size of compressed buffer // is lower than size of uncompressed buffer. typedef void (*COMPRESS)( - char * pbOutBuffer, // [out] Pointer to the buffer where the compressed data will be stored - int * pcbOutBuffer, // [in] Pointer to length of the buffer pointed by pbOutBuffer - // [out] Contains length of the compressed data - char * pbInBuffer, // [in] Pointer to the buffer with data to compress - int cbInBuffer, // [in] Length of the buffer pointer by pbInBuffer - int * pCmpType, // [in] Compression-method specific value. ADPCM Setups this for the following Huffman compression - int nCmpLevel); // [in] Compression specific value. ADPCM uses this. Should be set to zero. + char * pbOutBuffer, // [out] Pointer to the buffer where the compressed data will be stored + int * pcbOutBuffer, // [in] Pointer to length of the buffer pointed by pbOutBuffer + // [out] Contains length of the compressed data + char * pbInBuffer, // [in] Pointer to the buffer with data to compress + int cbInBuffer, // [in] Length of the buffer pointer by pbInBuffer + int * pCmpType, // [in] Compression-method specific value. ADPCM Setups this for the following Huffman compression + int nCmpLevel); // [in] Compression specific value. ADPCM uses this. Should be set to zero. // Prototype of the decompression function // Returns 1 if success, 0 if failure typedef int (*DECOMPRESS)( - char * pbOutBuffer, // [out] Pointer to the buffer where to store decompressed data - int * pcbOutBuffer, // [in] Pointer to total size of the buffer pointed by pbOutBuffer - // [out] Contains length of the decompressed data - char * pbInBuffer, // [in] Pointer to data to be decompressed - int cbInBuffer); // [in] Length of the data to be decompressed + char * pbOutBuffer, // [out] Pointer to the buffer where to store decompressed data + int * pcbOutBuffer, // [in] Pointer to total size of the buffer pointed by pbOutBuffer + // [out] Contains length of the decompressed data + char * pbInBuffer, // [in] Pointer to data to be decompressed + int cbInBuffer); // [in] Length of the data to be decompressed // Table of compression functions -typedef struct +typedef struct { unsigned long uMask; // Compression mask COMPRESS Compress; // Compression function @@ -73,12 +72,12 @@ typedef struct // 1500F4C0 void Compress_huff( - char * pbOutBuffer, - int * pcbOutBuffer, - char * pbInBuffer, - int cbInBuffer, - int * pCmpType, - int /* nCmpLevel */) + char * pbOutBuffer, + int * pcbOutBuffer, + char * pbInBuffer, + int cbInBuffer, + int * pCmpType, + int /* nCmpLevel */) { THuffmannTree ht; // Huffmann tree for compression TOutputStream os; // Output stream @@ -98,7 +97,7 @@ void Compress_huff( // The following code is not necessary to run, because it has no // effect on the output data. It only clears the huffmann tree, but when // the tree is on the stack, who cares ? -// ht.UninitTree(); + // ht.UninitTree(); } // 1500F5F0 @@ -122,7 +121,7 @@ int Decompress_huff(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, i // The following code is not necessary to run, because it has no // effect on the output data. It only clears the huffmann tree, but when // the tree is on the stack, who cares ? -// ht.UninitTree(); + // ht.UninitTree(); return 1; } @@ -133,12 +132,12 @@ int Decompress_huff(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, i /******************************************************************************/ void Compress_ZLIB( - char * pbOutBuffer, - int * pcbOutBuffer, - char * pbInBuffer, - int cbInBuffer, - int * /* pCmpType */, - int /* nCmpLevel */) + char * pbOutBuffer, + int * pcbOutBuffer, + char * pbInBuffer, + int cbInBuffer, + int * /* pCmpType */, + int /* nCmpLevel */) { z_stream z; // Stream information for zlib int windowBits; @@ -176,16 +175,16 @@ void Compress_ZLIB( // Storm.dll uses zlib version 1.1.3 // Wow.exe uses zlib version 1.2.3 nResult = deflateInit2(&z, - Z_DEFAULT_COMPRESSION, - Z_DEFLATED, - windowBits, - 8, - Z_DEFAULT_STRATEGY); + 6, // Compression level used by WoW MPQs + Z_DEFLATED, + windowBits, + 8, + Z_DEFAULT_STRATEGY); if(nResult == Z_OK) { // Call zlib to compress the data nResult = deflate(&z, Z_FINISH); - + if(nResult == Z_OK || nResult == Z_STREAM_END) *pcbOutBuffer = z.total_out; @@ -228,7 +227,7 @@ int Decompress_ZLIB(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, i // Function loads data from the input buffer. Used by Pklib's "implode" // and "explode" function as user-defined callback // Returns number of bytes loaded -// +// // char * buf - Pointer to a buffer where to store loaded data // unsigned int * size - Max. number of bytes to read // void * param - Custom pointer, parameter of implode/explode @@ -242,7 +241,7 @@ static unsigned int ReadInputData(char * buf, unsigned int * size, void * param) // Check the case when not enough data available if(nToRead > nMaxAvail) nToRead = nMaxAvail; - + // Load data and increment offsets memcpy(buf, pInfo->pbInBuff, nToRead); pInfo->pbInBuff += nToRead; @@ -252,7 +251,7 @@ static unsigned int ReadInputData(char * buf, unsigned int * size, void * param) // Function for store output data. Used by Pklib's "implode" and "explode" // as user-defined callback -// +// // char * buf - Pointer to data to be written // unsigned int * size - Number of bytes to write // void * param - Custom pointer, parameter of implode/explode @@ -274,17 +273,17 @@ static void WriteOutputData(char * buf, unsigned int * size, void * param) } static void Compress_PKLIB( - char * pbOutBuffer, - int * pcbOutBuffer, - char * pbInBuffer, - int cbInBuffer, - int * /* pCmpType */, - int /* nCmpLevel */) + char * pbOutBuffer, + int * pcbOutBuffer, + char * pbInBuffer, + int cbInBuffer, + int * /* pCmpType */, + int /* nCmpLevel */) { - TDataInfo Info; // Data information - char * work_buf = ALLOCMEM(char, CMP_BUFFER_SIZE); // Pklib's work buffer - unsigned int dict_size; // Dictionary size - unsigned int ctype = CMP_BINARY; // Compression type + TDataInfo Info; // Data information + char * work_buf = STORM_ALLOC(char, CMP_BUFFER_SIZE);// Pklib's work buffer + unsigned int dict_size; // Dictionary size + unsigned int ctype = CMP_BINARY; // Compression type // Fill data information structure memset(work_buf, 0, CMP_BUFFER_SIZE); @@ -293,7 +292,13 @@ static void Compress_PKLIB( Info.pbOutBuff = pbOutBuffer; Info.pbOutBuffEnd = pbOutBuffer + *pcbOutBuffer; - // Set the compression type and dictionary size + // + // Set the dictionary size + // + // Diablo I ues fixed dictionary size of CMP_IMPLODE_DICT_SIZE3 + // Starcraft uses the variable dictionary size based on algorithm below + // + if (cbInBuffer < 0x600) dict_size = CMP_IMPLODE_DICT_SIZE1; else if(0x600 <= cbInBuffer && cbInBuffer < 0xC00) @@ -305,13 +310,13 @@ static void Compress_PKLIB( if(implode(ReadInputData, WriteOutputData, work_buf, &Info, &ctype, &dict_size) == CMP_NO_ERROR) *pcbOutBuffer = (int)(Info.pbOutBuff - pbOutBuffer); - FREEMEM(work_buf); + STORM_FREE(work_buf); } static int Decompress_PKLIB(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer) { TDataInfo Info; // Data information - char * work_buf = ALLOCMEM(char, EXP_BUFFER_SIZE);// Pklib's work buffer + char * work_buf = STORM_ALLOC(char, EXP_BUFFER_SIZE);// Pklib's work buffer // Fill data information structure memset(work_buf, 0, EXP_BUFFER_SIZE); @@ -322,14 +327,14 @@ static int Decompress_PKLIB(char * pbOutBuffer, int * pcbOutBuffer, char * pbInB // Do the decompression explode(ReadInputData, WriteOutputData, work_buf, &Info); - + // If PKLIB is unable to decompress the data, return 0; if(Info.pbOutBuff == pbOutBuffer) return 0; // Give away the number of decompressed bytes *pcbOutBuffer = (int)(Info.pbOutBuff - pbOutBuffer); - FREEMEM(work_buf); + STORM_FREE(work_buf); return 1; } @@ -340,12 +345,12 @@ static int Decompress_PKLIB(char * pbOutBuffer, int * pcbOutBuffer, char * pbInB /******************************************************************************/ static void Compress_BZIP2( - char * pbOutBuffer, - int * pcbOutBuffer, - char * pbInBuffer, - int cbInBuffer, - int * /* pCmpType */, - int /* nCmpLevel */) + char * pbOutBuffer, + int * pcbOutBuffer, + char * pbInBuffer, + int cbInBuffer, + int * /* pCmpType */, + int /* nCmpLevel */) { bz_stream strm; int blockSize100k = 9; @@ -400,8 +405,8 @@ static int Decompress_BZIP2(char * pbOutBuffer, int * pcbOutBuffer, char * pbInB while(nResult != BZ_STREAM_END) { nResult = BZ2_bzDecompress(&strm); - - // If any error there, break the loop + + // If any error there, break the loop if(nResult < BZ_OK) break; } @@ -438,7 +443,7 @@ static SRes LZMA_Callback_Progress(void * /* p */, UInt64 /* inSize */, UInt64 / static void * LZMA_Callback_Alloc(void *p, size_t size) { p = p; - return ALLOCMEM(BYTE, size); + return STORM_ALLOC(BYTE, size); } /* address can be 0 */ @@ -446,7 +451,7 @@ static void LZMA_Callback_Free(void *p, void *address) { p = p; if(address != NULL) - FREEMEM(address); + STORM_FREE(address); } // @@ -456,13 +461,13 @@ static void LZMA_Callback_Free(void *p, void *address) // the data compressed by StormLib. // -static void Compress_LZMA( - char * pbOutBuffer, - int * pcbOutBuffer, - char * pbInBuffer, - int cbInBuffer, - int * /* pCmpType */, - int /* nCmpLevel */) +/*static */ void Compress_LZMA( + char * pbOutBuffer, + int * pcbOutBuffer, + char * pbInBuffer, + int cbInBuffer, + int * /* pCmpType */, + int /* nCmpLevel */) { ICompressProgress Progress; CLzmaEncProps props; @@ -486,16 +491,16 @@ static void Compress_LZMA( destBuffer = (Byte *)pbOutBuffer + LZMA_HEADER_SIZE; destLen = *pcbOutBuffer - LZMA_HEADER_SIZE; nResult = LzmaEncode(destBuffer, - &destLen, - (Byte *)pbInBuffer, + &destLen, + (Byte *)pbInBuffer, srcLen, - &props, + &props, encodedProps, - &encodedPropsSize, + &encodedPropsSize, 0, - &Progress, - &SzAlloc, - &SzAlloc); + &Progress, + &SzAlloc, + &SzAlloc); if(nResult != SZ_OK) return; @@ -535,7 +540,7 @@ static int Decompress_LZMA(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBu SRes nResult; // There must be at least 0x0E bytes in the buffer - if(srcLen <= LZMA_HEADER_SIZE) + if(srcLen <= LZMA_HEADER_SIZE) return 0; // We only accept blocks that have no filter used @@ -549,14 +554,14 @@ static int Decompress_LZMA(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBu // Perform compression srcLen = cbInBuffer - LZMA_HEADER_SIZE; nResult = LzmaDecode(destBuffer, - &destLen, + &destLen, srcBuffer + LZMA_HEADER_SIZE, - &srcLen, - srcBuffer + 1, + &srcLen, + srcBuffer + 1, LZMA_PROPS_SIZE, LZMA_FINISH_END, - &LzmaStatus, - &SzAlloc); + &LzmaStatus, + &SzAlloc); if(nResult != SZ_OK) return 0; @@ -571,12 +576,12 @@ static int Decompress_LZMA(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBu /******************************************************************************/ void Compress_SPARSE( - char * pbOutBuffer, - int * pcbOutBuffer, - char * pbInBuffer, - int cbInBuffer, - int * /* pCmpType */, - int /* nCmpLevel */) + char * pbOutBuffer, + int * pcbOutBuffer, + char * pbInBuffer, + int cbInBuffer, + int * /* pCmpType */, + int /* nCmpLevel */) { CompressSparse((unsigned char *)pbOutBuffer, pcbOutBuffer, (unsigned char *)pbInBuffer, cbInBuffer); } @@ -593,12 +598,12 @@ int Decompress_SPARSE(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, /******************************************************************************/ static void Compress_ADPCM_mono( - char * pbOutBuffer, - int * pcbOutBuffer, - char * pbInBuffer, - int cbInBuffer, - int * pCmpType, - int nCmpLevel) + char * pbOutBuffer, + int * pcbOutBuffer, + char * pbInBuffer, + int cbInBuffer, + int * pCmpType, + int nCmpLevel) { // Prepare the compression level for Huffmann compression, // which will be called as next step @@ -633,12 +638,12 @@ static int Decompress_ADPCM_mono(char * pbOutBuffer, int * pcbOutBuffer, char * /******************************************************************************/ static void Compress_ADPCM_stereo( - char * pbOutBuffer, - int * pcbOutBuffer, - char * pbInBuffer, - int cbInBuffer, - int * pCmpType, - int nCmpLevel) + char * pbOutBuffer, + int * pcbOutBuffer, + char * pbInBuffer, + int cbInBuffer, + int * pCmpType, + int nCmpLevel) { // Prepare the compression level for Huffmann compression, // which will be called as next step @@ -724,7 +729,7 @@ int WINAPI SCompExplode(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffe memcpy(pbOutBuffer, pbInBuffer, cbInBuffer); return 1; } - + // Perform decompression if(!Decompress_PKLIB(pbOutBuffer, &cbOutBuffer, pbInBuffer, cbInBuffer)) { @@ -753,23 +758,23 @@ int WINAPI SCompExplode(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffe static TCompressTable cmp_table[] = { - {MPQ_COMPRESSION_SPARSE, Compress_SPARSE}, // Sparse compression - {MPQ_COMPRESSION_WAVE_MONO, Compress_ADPCM_mono}, // IMA ADPCM mono compression - {MPQ_COMPRESSION_WAVE_STEREO, Compress_ADPCM_stereo}, // IMA ADPCM stereo compression - {MPQ_COMPRESSION_HUFFMANN, Compress_huff}, // Huffmann compression - {MPQ_COMPRESSION_ZLIB, Compress_ZLIB}, // Compression with the "zlib" library - {MPQ_COMPRESSION_PKWARE, Compress_PKLIB}, // Compression with Pkware DCL - {MPQ_COMPRESSION_BZIP2, Compress_BZIP2} // Compression Bzip2 library + {MPQ_COMPRESSION_SPARSE, Compress_SPARSE}, // Sparse compression + {MPQ_COMPRESSION_ADPCM_MONO, Compress_ADPCM_mono}, // IMA ADPCM mono compression + {MPQ_COMPRESSION_ADPCM_STEREO, Compress_ADPCM_stereo}, // IMA ADPCM stereo compression + {MPQ_COMPRESSION_HUFFMANN, Compress_huff}, // Huffmann compression + {MPQ_COMPRESSION_ZLIB, Compress_ZLIB}, // Compression with the "zlib" library + {MPQ_COMPRESSION_PKWARE, Compress_PKLIB}, // Compression with Pkware DCL + {MPQ_COMPRESSION_BZIP2, Compress_BZIP2} // Compression Bzip2 library }; int WINAPI SCompCompress( - char * pbOutBuffer, - int * pcbOutBuffer, - char * pbInBuffer, - int cbInBuffer, - unsigned uCompressionMask, - int nCmpType, - int nCmpLevel) + char * pbOutBuffer, + int * pcbOutBuffer, + char * pbInBuffer, + int cbInBuffer, + unsigned uCompressionMask, + int nCmpType, + int nCmpLevel) { COMPRESS CompressFuncArray[0x10]; // Array of compression functions, applied sequentially unsigned char CompressByte[0x10]; // CompressByte for each method in the CompressFuncArray array @@ -833,7 +838,7 @@ int WINAPI SCompCompress( // If we need to do more than 1 compression, allocate intermediate buffer if(nCompressCount > 1) { - pbWorkBuffer = ALLOCMEM(char, *pcbOutBuffer); + pbWorkBuffer = STORM_ALLOC(char, *pcbOutBuffer); if(pbWorkBuffer == NULL) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); @@ -890,7 +895,7 @@ int WINAPI SCompCompress( // Cleanup and return if(pbWorkBuffer != NULL) - FREEMEM(pbWorkBuffer); + STORM_FREE(pbWorkBuffer); return nResult; } @@ -905,124 +910,101 @@ int WINAPI SCompCompress( // of compressed data static TDecompressTable dcmp_table[] = { - {MPQ_COMPRESSION_BZIP2, Decompress_BZIP2}, // Decompression with Bzip2 library - {MPQ_COMPRESSION_PKWARE, Decompress_PKLIB}, // Decompression with Pkware Data Compression Library - {MPQ_COMPRESSION_ZLIB, Decompress_ZLIB}, // Decompression with the "zlib" library - {MPQ_COMPRESSION_HUFFMANN, Decompress_huff}, // Huffmann decompression - {MPQ_COMPRESSION_WAVE_STEREO, Decompress_ADPCM_stereo}, // IMA ADPCM stereo decompression - {MPQ_COMPRESSION_WAVE_MONO, Decompress_ADPCM_mono}, // IMA ADPCM mono decompression - {MPQ_COMPRESSION_SPARSE, Decompress_SPARSE} // Sparse decompression + {MPQ_COMPRESSION_BZIP2, Decompress_BZIP2}, // Decompression with Bzip2 library + {MPQ_COMPRESSION_PKWARE, Decompress_PKLIB}, // Decompression with Pkware Data Compression Library + {MPQ_COMPRESSION_ZLIB, Decompress_ZLIB}, // Decompression with the "zlib" library + {MPQ_COMPRESSION_HUFFMANN, Decompress_huff}, // Huffmann decompression + {MPQ_COMPRESSION_ADPCM_STEREO, Decompress_ADPCM_stereo}, // IMA ADPCM stereo decompression + {MPQ_COMPRESSION_ADPCM_MONO, Decompress_ADPCM_mono}, // IMA ADPCM mono decompression + {MPQ_COMPRESSION_SPARSE, Decompress_SPARSE} // Sparse decompression }; int WINAPI SCompDecompress( - char * pbOutBuffer, - int * pcbOutBuffer, - char * pbInBuffer, - int cbInBuffer) + char * pbOutBuffer, + int * pcbOutBuffer, + char * pbInBuffer, + int cbInBuffer) { - DECOMPRESS DecompressFuncArray[0x10]; // Array of compression functions, applied sequentially char * pbWorkBuffer = NULL; // Temporary storage for decompressed data char * pbOutput = pbOutBuffer; // Where to store decompressed data char * pbInput; // Where to store decompressed data unsigned uCompressionMask; // Decompressions applied to the data + unsigned uCompressionCopy; // Decompressions applied to the data int cbOutBuffer = *pcbOutBuffer; // Current size of the output buffer int cbInLength; // Current size of the input buffer int nCompressCount = 0; // Number of compressions to be applied int nCompressIndex = 0; int nResult = 1; - // Zero input data bring zero output data - if(cbInBuffer == 0) - { - *pcbOutBuffer = 0; - return 1; - } + // Verify buffer sizes + if(cbOutBuffer < cbInBuffer || cbInBuffer < 1) + return 0; -/* // If the input length is the same as output length, do nothing. - // Unfortunately, some data in WoW-Cataclysm BETA MPQs are like that .... - if(cbInBuffer == cbOutBuffer) + if(cbOutBuffer == cbInBuffer) { // If the buffers are equal, don't copy anything. - if(pbInBuffer == pbOutBuffer) - return 1; - - memcpy(pbOutBuffer, pbInBuffer, cbInBuffer); + if(pbInBuffer != pbOutBuffer) + memcpy(pbOutBuffer, pbInBuffer, cbInBuffer); return 1; } -*/ // Get applied compression types and decrement data length - uCompressionMask = (unsigned char)*pbInBuffer++; + uCompressionMask = uCompressionCopy = (unsigned char)*pbInBuffer++; cbInBuffer--; // Get current compressed data and length of it pbInput = pbInBuffer; cbInLength = cbInBuffer; - // - // Beginning with Starcraft II, the decompression byte can no longer contain - // any arbitrary combination types. Instead, it can be one of the few - // pre-set values (0x02, 0x08, 0x10, 0x12, 0x20, 0x22, 0x30) - // Note that Starcraft II no longer uses WAVE files, so compressions 0x41, 0x48, 0x81, 0x88 - // are no longer used. - // + // This compression function doesn't support LZMA + assert(uCompressionMask != MPQ_COMPRESSION_LZMA); - // Special case: LZMA decompression (added in Starcraft II) - if(uCompressionMask == MPQ_COMPRESSION_LZMA) - { - DecompressFuncArray[0] = Decompress_LZMA; - nCompressCount = 1; - } - else + // Parse the compression mask + for(size_t i = 0; i < (sizeof(dcmp_table) / sizeof(TDecompressTable)); i++) { - // Fill the compressions array - for(size_t i = 0; i < (sizeof(dcmp_table) / sizeof(TDecompressTable)); i++) + // If the mask agrees, insert the compression function to the array + if(uCompressionMask & dcmp_table[i].uMask) { - // If the mask agrees, insert the compression function to the array - if(uCompressionMask & dcmp_table[i].uMask) - { - DecompressFuncArray[nCompressCount] = dcmp_table[i].Decompress; - uCompressionMask &= ~dcmp_table[i].uMask; - nCompressCount++; - } + uCompressionCopy &= ~dcmp_table[i].uMask; + nCompressCount++; } + } - // If at least one of the compressions remaing unknown, return an error - if(uCompressionMask != 0) - { - SetLastError(ERROR_NOT_SUPPORTED); - return 0; - } + // If at least one of the compressions remaing unknown, return an error + if(nCompressCount == 0 || uCompressionCopy != 0) + { + SetLastError(ERROR_NOT_SUPPORTED); + return 0; } - // If there is at least one decompression, do it - if(nCompressCount > 0) + // If there is more than one compression, we have to allocate extra buffer + if(nCompressCount > 1) { - // If there is more than one compression, we have to allocate extra buffer - if(nCompressCount > 1) + pbWorkBuffer = STORM_ALLOC(char, cbOutBuffer); + if(pbWorkBuffer == NULL) { - pbWorkBuffer = ALLOCMEM(char, *pcbOutBuffer); - if(pbWorkBuffer == NULL) - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return 0; - } + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return 0; } + } - // Get the current compression index - nCompressIndex = nCompressCount - 1; + // Get the current compression index + nCompressIndex = nCompressCount - 1; - // Apply all decompressions - for(int i = 0; i < nCompressCount; i++) + // Apply all decompressions + for(size_t i = 0; i < (sizeof(dcmp_table) / sizeof(TDecompressTable)); i++) + { + // Perform the (next) decompression + if(uCompressionMask & dcmp_table[i].uMask) { // Get the correct output buffer pbOutput = (nCompressIndex & 1) ? pbWorkBuffer : pbOutBuffer; nCompressIndex--; - - // Perform the (next) decompression + + // Perform the decompression cbOutBuffer = *pcbOutBuffer; - nResult = DecompressFuncArray[i](pbOutput, &cbOutBuffer, pbInput, cbInLength); + nResult = dcmp_table[i].Decompress(pbOutput, &cbOutBuffer, pbInput, cbInLength); if(nResult == 0 || cbOutBuffer == 0) { SetLastError(ERROR_FILE_CORRUPT); @@ -1031,21 +1013,123 @@ int WINAPI SCompDecompress( } // Switch buffers - pbInput = pbOutput; cbInLength = cbOutBuffer; + pbInput = pbOutput; } + } - // Put the length of the decompressed data to the output buffer - *pcbOutBuffer = cbOutBuffer; + // Put the length of the decompressed data to the output buffer + *pcbOutBuffer = cbOutBuffer; + + // Cleanup and return + if(pbWorkBuffer != NULL) + STORM_FREE(pbWorkBuffer); + return nResult; +} + +int WINAPI SCompDecompress2( + char * pbOutBuffer, + int * pcbOutBuffer, + char * pbInBuffer, + int cbInBuffer) +{ + DECOMPRESS pfnDecompress1 = NULL; + DECOMPRESS pfnDecompress2 = NULL; + char * pbWorkBuffer = pbOutBuffer; + int cbWorkBuffer = *pcbOutBuffer; + int nResult; + char CompressionMethod; + + // Verify buffer sizes + if(*pcbOutBuffer < cbInBuffer || cbInBuffer < 1) + return 0; + + // If the outputbuffer is as big as input buffer, just copy the block + if(*pcbOutBuffer == cbInBuffer) + { + if(pbOutBuffer != pbInBuffer) + memcpy(pbOutBuffer, pbInBuffer, cbInBuffer); + return 1; } - else + + // Get the compression methods + CompressionMethod = *pbInBuffer++; + cbInBuffer--; + + // We only recognize a fixed set of compression methods + switch((unsigned char)CompressionMethod) { - memcpy(pbOutBuffer, pbInBuffer, cbInBuffer); - *pcbOutBuffer = cbInBuffer; + case MPQ_COMPRESSION_ZLIB: + pfnDecompress1 = Decompress_ZLIB; + break; + + case MPQ_COMPRESSION_PKWARE: + pfnDecompress1 = Decompress_PKLIB; + break; + + case MPQ_COMPRESSION_BZIP2: + pfnDecompress1 = Decompress_BZIP2; + break; + + case MPQ_COMPRESSION_LZMA: + pfnDecompress1 = Decompress_LZMA; + break; + + case MPQ_COMPRESSION_SPARSE: + pfnDecompress1 = Decompress_SPARSE; + break; + + case (MPQ_COMPRESSION_SPARSE | MPQ_COMPRESSION_ZLIB): + pfnDecompress1 = Decompress_ZLIB; + pfnDecompress2 = Decompress_SPARSE; + break; + + case (MPQ_COMPRESSION_SPARSE | MPQ_COMPRESSION_BZIP2): + pfnDecompress1 = Decompress_BZIP2; + pfnDecompress2 = Decompress_SPARSE; + break; + + // + // Note: Any combination including MPQ_COMPRESSION_ADPCM_MONO, + // MPQ_COMPRESSION_ADPCM_STEREO or MPQ_COMPRESSION_HUFFMANN + // is not supported by newer MPQs. + // + + default: + SetLastError(ERROR_FILE_CORRUPT); + return 0; } - // Cleanup and return - if(pbWorkBuffer != NULL) - FREEMEM(pbWorkBuffer); + // If we have to use two decompressions, allocate temporary buffer + if(pfnDecompress2 != NULL) + { + pbWorkBuffer = STORM_ALLOC(char, *pcbOutBuffer); + if(pbWorkBuffer == NULL) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return 0; + } + } + + // Apply the first decompression method + nResult = pfnDecompress1(pbWorkBuffer, &cbWorkBuffer, pbInBuffer, cbInBuffer); + + // Apply the second decompression method, if any + if(pfnDecompress2 != NULL && nResult != 0) + { + cbInBuffer = cbWorkBuffer; + cbWorkBuffer = *pcbOutBuffer; + nResult = pfnDecompress2(pbOutBuffer, &cbWorkBuffer, pbWorkBuffer, cbInBuffer); + } + + // Supply the output buffer size + *pcbOutBuffer = cbWorkBuffer; + + // Free temporary buffer + if(pbWorkBuffer != pbOutBuffer) + STORM_FREE(pbWorkBuffer); + + if(nResult == 0) + SetLastError(ERROR_FILE_CORRUPT); return nResult; } diff --git a/dep/StormLib/src/SFileAddFile.cpp b/dep/StormLib/src/SFileAddFile.cpp index 17292cbb15e..3f12e697f7d 100644 --- a/dep/StormLib/src/SFileAddFile.cpp +++ b/dep/StormLib/src/SFileAddFile.cpp @@ -9,11 +9,36 @@ /*****************************************************************************/ #define __STORMLIB_SELF__ -#define __INCLUDE_COMPRESSION__ -#define __INCLUDE_CRYPTOGRAPHY__ #include "StormLib.h" #include "StormCommon.h" +//----------------------------------------------------------------------------- +// Local structures + +#define FILE_SIGNATURE_RIFF 0x46464952 +#define FILE_SIGNATURE_WAVE 0x45564157 +#define FILE_SIGNATURE_FMT 0x20746D66 +#define AUDIO_FORMAT_PCM 1 + +typedef struct _WAVE_FILE_HEADER +{ + DWORD dwChunkId; // 0x52494646 ("RIFF") + DWORD dwChunkSize; // Size of that chunk, in bytes + DWORD dwFormat; // Must be 0x57415645 ("WAVE") + + // Format sub-chunk + DWORD dwSubChunk1Id; // 0x666d7420 ("fmt ") + DWORD dwSubChunk1Size; // 0x16 for PCM + USHORT wAudioFormat; // 1 = PCM. Other value means some sort of compression + USHORT wChannels; // Number of channels + DWORD dwSampleRate; // 8000, 44100, etc. + DWORD dwBytesRate; // SampleRate * NumChannels * BitsPerSample/8 + USHORT wBlockAlign; // NumChannels * BitsPerSample/8 + USHORT wBitsPerSample; // 8 bits = 8, 16 bits = 16, etc. + + // Followed by "data" sub-chunk (we don't care) +} WAVE_FILE_HEADER, *PWAVE_FILE_HEADER; + //----------------------------------------------------------------------------- // Local variables @@ -29,12 +54,35 @@ static void * pvUserData = NULL; #define LOSSY_COMPRESSION_MASK (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN) +static int IsWaveFile( + LPBYTE pbFileData, + DWORD cbFileData, + LPDWORD pdwChannels) +{ + PWAVE_FILE_HEADER pWaveHdr = (PWAVE_FILE_HEADER)pbFileData; + + if(cbFileData > sizeof(WAVE_FILE_HEADER)) + { + if(pWaveHdr->dwChunkId == FILE_SIGNATURE_RIFF && pWaveHdr->dwFormat == FILE_SIGNATURE_WAVE) + { + if(pWaveHdr->dwSubChunk1Id == FILE_SIGNATURE_FMT && pWaveHdr->wAudioFormat == AUDIO_FORMAT_PCM) + { + *pdwChannels = pWaveHdr->wChannels; + return true; + } + } + } + + return false; +} + + static int WriteDataToMpqFile( - TMPQArchive * ha, - TMPQFile * hf, - LPBYTE pbFileData, - DWORD dwDataSize, - DWORD dwCompression) + TMPQArchive * ha, + TMPQFile * hf, + LPBYTE pbFileData, + DWORD dwDataSize, + DWORD dwCompression) { TFileEntry * pFileEntry = hf->pFileEntry; ULONGLONG ByteOffset; @@ -63,11 +111,11 @@ static int WriteDataToMpqFile( DWORD dwSectorIndex = hf->dwFilePos / hf->dwSectorSize; DWORD dwBytesToCopy; - // Process all data. + // Process all data. while(dwDataSize != 0) { dwBytesToCopy = dwDataSize; - + // Check for sector overflow if(dwBytesToCopy > (hf->dwSectorSize - dwBytesInSector)) dwBytesToCopy = (hf->dwSectorSize - dwBytesInSector); @@ -88,16 +136,6 @@ static int WriteDataToMpqFile( // Set the position in the file ByteOffset = hf->RawFilePos + pFileEntry->dwCmpSize; - // If the file is compressed, allocate buffer for the compressed data. - // Note that we allocate buffer that is a bit longer than sector size, - // for case if the compression method performs a buffer overrun - if((pFileEntry->dwFlags & MPQ_FILE_COMPRESSED) && pbCompressed == NULL) - { - pbToWrite = pbCompressed = ALLOCMEM(BYTE, hf->dwSectorSize + 0x100); - if(pbCompressed == NULL) - nError = ERROR_NOT_ENOUGH_MEMORY; - } - // Update CRC32 and MD5 of the file md5_process((hash_state *)hf->hctx, hf->pbFileSector, dwBytesInSector); hf->dwCrc32 = crc32(hf->dwCrc32, hf->pbFileSector, dwBytesInSector); @@ -108,7 +146,18 @@ static int WriteDataToMpqFile( int nOutBuffer = (int)dwBytesInSector; int nInBuffer = (int)dwBytesInSector; - assert(pbCompressed != NULL); + // If the file is compressed, allocate buffer for the compressed data. + // Note that we allocate buffer that is a bit longer than sector size, + // for case if the compression method performs a buffer overrun + if(pbCompressed == NULL) + { + pbToWrite = pbCompressed = STORM_ALLOC(BYTE, hf->dwSectorSize + 0x100); + if(pbCompressed == NULL) + { + nError = ERROR_NOT_ENOUGH_MEMORY; + break; + } + } // // Note that both SCompImplode and SCompCompress give original buffer, @@ -118,20 +167,20 @@ static int WriteDataToMpqFile( if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE) { SCompImplode((char *)pbCompressed, - &nOutBuffer, + &nOutBuffer, (char *)hf->pbFileSector, - nInBuffer); + nInBuffer); } if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS) { SCompCompress((char *)pbCompressed, - &nOutBuffer, + &nOutBuffer, (char *)hf->pbFileSector, - nInBuffer, - (unsigned)dwCompression, - 0, - nCompressionLevel); + nInBuffer, + (unsigned)dwCompression, + 0, + nCompressionLevel); } // Update sector positions @@ -142,7 +191,7 @@ static int WriteDataToMpqFile( // We have to calculate sector CRC, if enabled if(hf->SectorChksums != NULL) hf->SectorChksums[dwSectorIndex] = adler32(0, pbCompressed, nOutBuffer); - } + } // Encrypt the sector, if necessary if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) @@ -173,7 +222,7 @@ static int WriteDataToMpqFile( // Cleanup if(pbCompressed != NULL) - FREEMEM(pbCompressed); + STORM_FREE(pbCompressed); return nError; } @@ -181,10 +230,10 @@ static int WriteDataToMpqFile( // Recrypts file data for file renaming static int RecryptFileData( - TMPQArchive * ha, - TMPQFile * hf, - const char * szFileName, - const char * szNewFileName) + TMPQArchive * ha, + TMPQFile * hf, + const char * szFileName, + const char * szNewFileName) { ULONGLONG RawFilePos; TFileEntry * pFileEntry = hf->pFileEntry; @@ -197,8 +246,8 @@ static int RecryptFileData( assert(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED); // File decryption key is calculated from the plain name - szNewFileName = GetPlainFileName(szNewFileName); - szFileName = GetPlainFileName(szFileName); + szNewFileName = GetPlainFileNameA(szNewFileName); + szFileName = GetPlainFileNameA(szFileName); // Calculate both file keys dwOldKey = DecryptFileKey(szFileName, pFileEntry->ByteOffset, pFileEntry->dwFileSize, pFileEntry->dwFlags); @@ -228,7 +277,7 @@ static int RecryptFileData( if(hf->SectorOffsets != NULL) { // Allocate secondary buffer for sectors copy - DWORD * SectorOffsetsCopy = (DWORD *)ALLOCMEM(BYTE, hf->SectorOffsets[0]); + DWORD * SectorOffsetsCopy = (DWORD *)STORM_ALLOC(BYTE, hf->SectorOffsets[0]); DWORD dwSectorOffsLen = hf->SectorOffsets[0]; if(SectorOffsetsCopy == NULL) @@ -242,7 +291,7 @@ static int RecryptFileData( // Write the recrypted array back if(!FileStream_Write(ha->pStream, &hf->RawFilePos, SectorOffsetsCopy, dwSectorOffsLen)) nError = GetLastError(); - FREEMEM(SectorOffsetsCopy); + STORM_FREE(SectorOffsetsCopy); } // Now we have to recrypt all file sectors. We do it without @@ -276,7 +325,7 @@ static int RecryptFileData( } // If necessary, re-encrypt the sector - // Note: Recompression is not necessary here. Unlike encryption, + // Note: Recompression is not necessary here. Unlike encryption, // the compression does not depend on the position of the file in MPQ. BSWAP_ARRAY32_UNSIGNED(hf->pbFileSector, dwRawDataInSector); DecryptMpqBlock(hf->pbFileSector, dwRawDataInSector, dwOldKey + dwSector); @@ -302,13 +351,13 @@ static int RecryptFileData( // Support functions for adding files to the MPQ int SFileAddFile_Init( - TMPQArchive * ha, - const char * szFileName, - ULONGLONG FileTime, - DWORD dwFileSize, - LCID lcLocale, - DWORD dwFlags, - TMPQFile ** phf) + TMPQArchive * ha, + const char * szFileName, + ULONGLONG FileTime, + DWORD dwFileSize, + LCID lcLocale, + DWORD dwFlags, + TMPQFile ** phf) { TFileEntry * pFileEntry = NULL; ULONGLONG TempPos; // For various file offset calculations @@ -321,11 +370,17 @@ int SFileAddFile_Init( // flags get to this point // - // Adjust file flags for too-small files - if(dwFileSize < 0x04) - dwFlags &= ~(MPQ_FILE_ENCRYPTED | MPQ_FILE_FIX_KEY); - if(dwFileSize < 0x20) - dwFlags &= ~(MPQ_FILE_COMPRESSED | MPQ_FILE_SECTOR_CRC); + // Sestor CRC is not allowed with single unit files + if(dwFlags & MPQ_FILE_SINGLE_UNIT) + dwFlags &= ~MPQ_FILE_SECTOR_CRC; + + // Sector CRC is not allowed if the file is not compressed + if(!(dwFlags & MPQ_FILE_COMPRESSED)) + dwFlags &= ~MPQ_FILE_SECTOR_CRC; + + // Fix Key is not allowed if the file is not enrypted + if(!(dwFlags & MPQ_FILE_ENCRYPTED)) + dwFlags &= ~MPQ_FILE_FIX_KEY; // If the MPQ is of version 3.0 or higher, we ignore file locale. // This is because HET and BET tables have no known support for it @@ -345,6 +400,9 @@ int SFileAddFile_Init( hf->RawFilePos = ha->MpqPos + hf->MpqFilePos; hf->bIsWriteHandle = true; + // Sanity check: The MPQ must be marked as changed at this point + assert((ha->dwFlags & MPQ_FLAG_CHANGED) != 0); + // When format V1, the size of the archive cannot exceed 4 GB if(ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1) { @@ -370,8 +428,16 @@ int SFileAddFile_Init( } else { + // If the file exists and "replace existing" is not set, fail it if((dwFlags & MPQ_FILE_REPLACEEXISTING) == 0) nError = ERROR_ALREADY_EXISTS; + + // If the file entry already contains a file + // and it is a pseudo-name, replace it + if(nError == ERROR_SUCCESS) + { + AllocateFileName(pFileEntry, szFileName); + } } } @@ -407,9 +473,6 @@ int SFileAddFile_Init( // If the caller gave us a file time, use it. pFileEntry->FileTime = FileTime; - // Remember that the MPQ has been modified - ha->dwFlags |= MPQ_FLAG_CHANGED; - // Call the callback, if needed if(AddFileCB != NULL) AddFileCB(pvUserData, 0, hf->dwDataSize, false); @@ -450,7 +513,7 @@ int SFileAddFile_Write(TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD d } // Allocate patch info, if the data is patch - if(hf->pPatchInfo == NULL && IsPatchData(pvData, dwSize, &hf->dwPatchedFileSize)) + if(hf->pPatchInfo == NULL && IsIncrementalPatchFile(pvData, dwSize, &hf->dwPatchedFileSize)) { // Set the MPQ_FILE_PATCH_FILE flag hf->pFileEntry->dwFlags |= MPQ_FILE_PATCH_FILE; @@ -491,7 +554,7 @@ int SFileAddFile_Write(TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD d { if(!FileStream_Write(ha->pStream, &RawFilePos, hf->pPatchInfo, hf->pPatchInfo->dwLength)) nError = GetLastError(); - + pFileEntry->dwCmpSize += hf->pPatchInfo->dwLength; RawFilePos += hf->pPatchInfo->dwLength; } @@ -617,9 +680,6 @@ int SFileAddFile_Finish(TMPQFile * hf) FreeFileEntry(ha, pFileEntry); } - // Schedule to saving MPQ tables regardless of success or error - ha->dwFlags |= MPQ_FLAG_CHANGED; - // Clear the add file callback FreeMPQFile(hf); pvUserData = NULL; @@ -628,16 +688,16 @@ int SFileAddFile_Finish(TMPQFile * hf) } //----------------------------------------------------------------------------- -// Adds data as file to the archive +// Adds data as file to the archive bool WINAPI SFileCreateFile( - HANDLE hMpq, - const char * szArchivedName, - ULONGLONG FileTime, - DWORD dwFileSize, - LCID lcLocale, - DWORD dwFlags, - HANDLE * phFile) + HANDLE hMpq, + const char * szArchivedName, + ULONGLONG FileTime, + DWORD dwFileSize, + LCID lcLocale, + DWORD dwFlags, + HANDLE * phFile) { TMPQArchive * ha = (TMPQArchive *)hMpq; int nError = ERROR_SUCCESS; @@ -649,11 +709,15 @@ bool WINAPI SFileCreateFile( nError = ERROR_INVALID_PARAMETER; if(phFile == NULL) nError = ERROR_INVALID_PARAMETER; - + // Don't allow to add file if the MPQ is open for read only if(ha->dwFlags & MPQ_FLAG_READ_ONLY) nError = ERROR_ACCESS_DENIED; - + + // Don't allow to add a file under pseudo-file name + if(IsPseudoFileName(szArchivedName, NULL)) + nError = ERROR_INVALID_PARAMETER; + // Don't allow to add any of the internal files if(IsInternalMpqFileName(szArchivedName)) nError = ERROR_INTERNAL_FILE; @@ -671,7 +735,14 @@ bool WINAPI SFileCreateFile( // Create the file in MPQ if(nError == ERROR_SUCCESS) + { + // Invalidate the entries for (listfile) and (attributes) + // After we are done with MPQ changes, we need to re-create them anyway + InvalidateInternalFiles(ha); + + // Initiate the add file operation nError = SFileAddFile_Init(ha, szArchivedName, FileTime, dwFileSize, lcLocale, dwFlags, (TMPQFile **)phFile); + } // Deal with the errors if(nError != ERROR_SUCCESS) @@ -680,10 +751,10 @@ bool WINAPI SFileCreateFile( } bool WINAPI SFileWriteFile( - HANDLE hFile, - const void * pvData, - DWORD dwSize, - DWORD dwCompression) + HANDLE hFile, + const void * pvData, + DWORD dwSize, + DWORD dwCompression) { TMPQFile * hf = (TMPQFile *)hFile; int nError = ERROR_SUCCESS; @@ -703,12 +774,12 @@ bool WINAPI SFileWriteFile( // the calling application must ensure that such flag combination doesn't get here // -// if(dwFlags & MPQ_FILE_IMPLODE) -// nError = ERROR_INVALID_PARAMETER; -// -// if(dwFlags & MPQ_FILE_ENCRYPTED) -// nError = ERROR_INVALID_PARAMETER; - + // if(dwFlags & MPQ_FILE_IMPLODE) + // nError = ERROR_INVALID_PARAMETER; + // + // if(dwFlags & MPQ_FILE_ENCRYPTED) + // nError = ERROR_INVALID_PARAMETER; + // Lossy compression is not allowed on single unit files if(dwCompression & LOSSY_COMPRESSION_MASK) nError = ERROR_INVALID_PARAMETER; @@ -718,7 +789,7 @@ bool WINAPI SFileWriteFile( // Write the data to the file if(nError == ERROR_SUCCESS) nError = SFileAddFile_Write(hf, pvData, dwSize, dwCompression); - + // Deal with errors if(nError != ERROR_SUCCESS) SetLastError(nError); @@ -739,7 +810,7 @@ bool WINAPI SFileFinishFile(HANDLE hFile) // Finish the file if(nError == ERROR_SUCCESS) nError = SFileAddFile_Finish(hf); - + // Deal with errors if(nError != ERROR_SUCCESS) SetLastError(nError); @@ -747,15 +818,15 @@ bool WINAPI SFileFinishFile(HANDLE hFile) } //----------------------------------------------------------------------------- -// Adds a file to the archive +// Adds a file to the archive bool WINAPI SFileAddFileEx( - HANDLE hMpq, - const char * szFileName, - const char * szArchivedName, - DWORD dwFlags, - DWORD dwCompression, // Compression of the first sector - DWORD dwCompressionNext) // Compression of next sectors + HANDLE hMpq, + const TCHAR * szFileName, + const char * szArchivedName, + DWORD dwFlags, + DWORD dwCompression, // Compression of the first sector + DWORD dwCompressionNext) // Compression of next sectors { ULONGLONG FileSize = 0; ULONGLONG FileTime = 0; @@ -765,6 +836,9 @@ bool WINAPI SFileAddFileEx( DWORD dwBytesRemaining = 0; DWORD dwBytesToRead; DWORD dwSectorSize = 0x1000; + DWORD dwChannels = 0; + bool bIsAdpcmCompression = false; + bool bIsFirstSector = true; int nError = ERROR_SUCCESS; // Check parameters @@ -774,7 +848,7 @@ bool WINAPI SFileAddFileEx( // Open added file if(nError == ERROR_SUCCESS) { - pStream = FileStream_OpenFile(szFileName, false); + pStream = FileStream_OpenFile(szFileName, STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); if(pStream == NULL) nError = GetLastError(); } @@ -782,9 +856,9 @@ bool WINAPI SFileAddFileEx( // Get the file size and file time if(nError == ERROR_SUCCESS) { - FileStream_GetLastWriteTime(pStream, &FileTime); - FileStream_GetSize(pStream, FileSize); - + FileStream_GetTime(pStream, &FileTime); + FileStream_GetSize(pStream, &FileSize); + // Files bigger than 4GB cannot be added to MPQ if(FileSize >> 32) nError = ERROR_DISK_FULL; @@ -794,7 +868,7 @@ bool WINAPI SFileAddFileEx( if(nError == ERROR_SUCCESS) { dwBytesRemaining = (DWORD)FileSize; - pbFileData = ALLOCMEM(BYTE, dwSectorSize); + pbFileData = STORM_ALLOC(BYTE, dwSectorSize); if(pbFileData == NULL) nError = ERROR_NOT_ENOUGH_MEMORY; } @@ -804,16 +878,19 @@ bool WINAPI SFileAddFileEx( { // When the compression for next blocks is set to default, // we will copy the compression for the first sector - if(dwCompressionNext == 0xFFFFFFFF) + if(dwCompressionNext == MPQ_COMPRESSION_NEXT_SAME) dwCompressionNext = dwCompression; - - // If the caller wants ADPCM compression, we make sure that the first sector is not - // compressed with lossy compression - if(dwCompressionNext & (MPQ_COMPRESSION_WAVE_MONO | MPQ_COMPRESSION_WAVE_STEREO)) + + // If the caller wants ADPCM compression, we make sure + // that the first sector is not compressed with lossy compression + if(dwCompressionNext & (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO)) { // The first compression must not be WAVE - if(dwCompression & (MPQ_COMPRESSION_WAVE_MONO | MPQ_COMPRESSION_WAVE_STEREO)) + if(dwCompression & (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO)) dwCompression = MPQ_COMPRESSION_PKWARE; + + dwCompressionNext &= ~(MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO); + bIsAdpcmCompression = true; } // Initiate adding file to the MPQ @@ -836,6 +913,21 @@ bool WINAPI SFileAddFileEx( break; } + // If the file being added is a WAVE file, we check number of channels + if(bIsFirstSector && bIsAdpcmCompression) + { + // The file must really be a wave file, otherwise it's data corruption + if(!IsWaveFile(pbFileData, dwBytesToRead, &dwChannels)) + { + nError = ERROR_BAD_FORMAT; + break; + } + + // Setup the compression according to number of channels + dwCompressionNext |= (dwChannels == 1) ? MPQ_COMPRESSION_ADPCM_MONO : MPQ_COMPRESSION_ADPCM_STEREO; + bIsFirstSector = false; + } + // Add the file sectors to the MPQ if(!SFileWriteFile(hMpqFile, pbFileData, dwBytesToRead, dwCompression)) { @@ -857,16 +949,16 @@ bool WINAPI SFileAddFileEx( // Cleanup and exit if(pbFileData != NULL) - FREEMEM(pbFileData); + STORM_FREE(pbFileData); if(pStream != NULL) FileStream_Close(pStream); if(nError != ERROR_SUCCESS) SetLastError(nError); return (nError == ERROR_SUCCESS); } - + // Adds a data file into the archive -bool WINAPI SFileAddFile(HANDLE hMpq, const char * szFileName, const char * szArchivedName, DWORD dwFlags) +bool WINAPI SFileAddFile(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags) { return SFileAddFileEx(hMpq, szFileName, @@ -877,7 +969,7 @@ bool WINAPI SFileAddFile(HANDLE hMpq, const char * szFileName, const char * szAr } // Adds a WAVE file into the archive -bool WINAPI SFileAddWave(HANDLE hMpq, const char * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwQuality) +bool WINAPI SFileAddWave(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwQuality) { DWORD dwCompression = 0; @@ -891,25 +983,25 @@ bool WINAPI SFileAddWave(HANDLE hMpq, const char * szFileName, const char * szAr // Starcraft files are packed as Mono (0x41) on medium quality. // Because this compression is not used anymore, our compression functions // will default to WaveCompressionLevel = 4 when using ADPCM compression - // + // // Convert quality to data compression switch(dwQuality) { - case MPQ_WAVE_QUALITY_HIGH: -// WaveCompressionLevel = -1; - dwCompression = MPQ_COMPRESSION_PKWARE; - break; - - case MPQ_WAVE_QUALITY_MEDIUM: -// WaveCompressionLevel = 4; - dwCompression = MPQ_COMPRESSION_WAVE_STEREO | MPQ_COMPRESSION_HUFFMANN; - break; - - case MPQ_WAVE_QUALITY_LOW: -// WaveCompressionLevel = 2; - dwCompression = MPQ_COMPRESSION_WAVE_STEREO | MPQ_COMPRESSION_HUFFMANN; - break; + case MPQ_WAVE_QUALITY_HIGH: + // WaveCompressionLevel = -1; + dwCompression = MPQ_COMPRESSION_PKWARE; + break; + + case MPQ_WAVE_QUALITY_MEDIUM: + // WaveCompressionLevel = 4; + dwCompression = MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN; + break; + + case MPQ_WAVE_QUALITY_LOW: + // WaveCompressionLevel = 2; + dwCompression = MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN; + break; } return SFileAddFileEx(hMpq, @@ -925,21 +1017,27 @@ bool WINAPI SFileAddWave(HANDLE hMpq, const char * szFileName, const char * szAr // // This function removes a file from the archive. The file content // remains there, only the entries in the hash table and in the block -// table are updated. +// table are updated. bool WINAPI SFileRemoveFile(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope) { TMPQArchive * ha = (TMPQArchive *)hMpq; TFileEntry * pFileEntry = NULL; // File entry of the file to be deleted + DWORD dwFileIndex = 0; int nError = ERROR_SUCCESS; + // Keep compiler happy + dwSearchScope = dwSearchScope; + // Check the parameters if(nError == ERROR_SUCCESS) { if(!IsValidMpqHandle(ha)) nError = ERROR_INVALID_HANDLE; - if(dwSearchScope != SFILE_OPEN_BY_INDEX && *szFileName == 0) + if(szFileName == NULL || *szFileName == 0) nError = ERROR_INVALID_PARAMETER; + if(IsInternalMpqFileName(szFileName)) + nError = ERROR_INTERNAL_FILE; } if(nError == ERROR_SUCCESS) @@ -947,26 +1045,19 @@ bool WINAPI SFileRemoveFile(HANDLE hMpq, const char * szFileName, DWORD dwSearch // Do not allow to remove files from MPQ open for read only if(ha->dwFlags & MPQ_FLAG_READ_ONLY) nError = ERROR_ACCESS_DENIED; - - // Do not allow to remove internal files - if(dwSearchScope != SFILE_OPEN_BY_INDEX) - { - if(IsInternalMpqFileName(szFileName)) - nError = ERROR_INTERNAL_FILE; - } } // Get hash entry belonging to this file if(nError == ERROR_SUCCESS) { - if(dwSearchScope == SFILE_OPEN_FROM_MPQ) + if(!IsPseudoFileName(szFileName, &dwFileIndex)) { if((pFileEntry = GetFileEntryExact(ha, (char *)szFileName, lcFileLocale)) == NULL) nError = ERROR_FILE_NOT_FOUND; } else { - if((pFileEntry = GetFileEntryByIndex(ha, (DWORD)(DWORD_PTR)szFileName)) == NULL) + if((pFileEntry = GetFileEntryByIndex(ha, dwFileIndex)) == NULL) nError = ERROR_FILE_NOT_FOUND; } } @@ -974,17 +1065,18 @@ bool WINAPI SFileRemoveFile(HANDLE hMpq, const char * szFileName, DWORD dwSearch // Test if the file is not already deleted if(nError == ERROR_SUCCESS) { - if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0) + if(!(pFileEntry->dwFlags & MPQ_FILE_EXISTS)) nError = ERROR_FILE_NOT_FOUND; } if(nError == ERROR_SUCCESS) { - // Mark the file entry as free - FreeFileEntry(ha, pFileEntry); + // Invalidate the entries for (listfile) and (attributes) + // After we are done with MPQ changes, we need to re-create them anyway + InvalidateInternalFiles(ha); - // Update MPQ archive - ha->dwFlags |= MPQ_FLAG_CHANGED; + // Mark the file entry as free + nError = FreeFileEntry(ha, pFileEntry); } // Resolve error and exit @@ -997,9 +1089,7 @@ bool WINAPI SFileRemoveFile(HANDLE hMpq, const char * szFileName, DWORD dwSearch bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szFileName, const char * szNewFileName) { TMPQArchive * ha = (TMPQArchive *)hMpq; - TFileEntry * pOldFileEntry = NULL; - TFileEntry * pNewFileEntry = NULL; - TFileEntry TempEntry = {0}; + TFileEntry * pFileEntry = NULL; ULONGLONG RawDataOffs; TMPQFile * hf; int nError = ERROR_SUCCESS; @@ -1019,6 +1109,10 @@ bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szFileName, const char * s if(ha->dwFlags & MPQ_FLAG_READ_ONLY) nError = ERROR_ACCESS_DENIED; + // Do not allow renaming anything to a pseudo-file name + if(IsPseudoFileName(szFileName, NULL) || IsPseudoFileName(szNewFileName, NULL)) + nError = ERROR_INVALID_PARAMETER; + // Do not allow to rename any of the internal files // Also do not allow to rename any of files to an internal file if(IsInternalMpqFileName(szFileName) || IsInternalMpqFileName(szNewFileName)) @@ -1028,73 +1122,53 @@ bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szFileName, const char * s // Find the current file entry. if(nError == ERROR_SUCCESS) { - pOldFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale); - if(pOldFileEntry == NULL) + // Get the file entry + pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale); + if(pFileEntry == NULL) nError = ERROR_FILE_NOT_FOUND; } - + // Also try to find file entry for the new file. // This verifies if we are not overwriting an existing file // (whose name we perhaps don't know) if(nError == ERROR_SUCCESS) { - pNewFileEntry = GetFileEntryLocale(ha, szNewFileName, pOldFileEntry->lcLocale); - if(pNewFileEntry != NULL) + if(GetFileEntryLocale(ha, szNewFileName, pFileEntry->lcLocale) != NULL) nError = ERROR_ALREADY_EXISTS; } - // Now we free the existing file entry and allocate new one. - // Before we do that, we save the content of the existing file entry - // in order to preserve it + // Now we rename the existing file entry. if(nError == ERROR_SUCCESS) { - // Save the file entry and free it - memcpy(&TempEntry, pOldFileEntry, sizeof(TFileEntry)); - TempEntry.szFileName = NULL; - FreeFileEntry(ha, pOldFileEntry); - - // Note: if this step fails, we leave the MPQ in a corrupt state. - pNewFileEntry = AllocateFileEntry(ha, szNewFileName, TempEntry.lcLocale); - if(pNewFileEntry == NULL) - nError = ERROR_FILE_CORRUPT; + // Rename the file entry + nError = RenameFileEntry(ha, pFileEntry, szNewFileName); } // Now we copy the existing file entry to the new one if(nError == ERROR_SUCCESS) { - // Copy all members that are not related to hash tables - assert(pNewFileEntry->lcLocale == TempEntry.lcLocale); - pNewFileEntry->ByteOffset = TempEntry.ByteOffset; - pNewFileEntry->FileTime = TempEntry.FileTime; - pNewFileEntry->dwFileSize = TempEntry.dwFileSize; - pNewFileEntry->dwCmpSize = TempEntry.dwCmpSize; - pNewFileEntry->dwFlags = TempEntry.dwFlags; - pNewFileEntry->wPlatform = TempEntry.wPlatform; - pNewFileEntry->dwCrc32 = TempEntry.dwCrc32; - memcpy(pNewFileEntry->md5, TempEntry.md5, MD5_DIGEST_SIZE); - // If the file is encrypted, we have to re-crypt the file content // with the new decryption key - if(pNewFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) + if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) { hf = CreateMpqFile(ha); if(hf != NULL) { // Recrypt the file data in the MPQ - hf->pFileEntry = pNewFileEntry; - hf->dwDataSize = pNewFileEntry->dwFileSize; + hf->pFileEntry = pFileEntry; + hf->dwDataSize = pFileEntry->dwFileSize; nError = RecryptFileData(ha, hf, szFileName, szNewFileName); - + // Update the MD5 if(ha->pHeader->dwRawChunkSize != 0) { - RawDataOffs = ha->MpqPos + pNewFileEntry->ByteOffset; + RawDataOffs = ha->MpqPos + pFileEntry->ByteOffset; WriteMpqDataMD5(ha->pStream, RawDataOffs, - pNewFileEntry->dwCmpSize, + pFileEntry->dwCmpSize, ha->pHeader->dwRawChunkSize); } - + FreeMPQFile(hf); } else @@ -1104,9 +1178,9 @@ bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szFileName, const char * s } } - // Now we mark the MPQ as changed, because MPQ tables need to be saved - if(nError == ERROR_SUCCESS) - ha->dwFlags |= MPQ_FLAG_CHANGED; + // + // Note: MPQ_FLAG_CHANGED is set by RenameFileEntry + // // Resolve error and return if(nError != ERROR_SUCCESS) diff --git a/dep/StormLib/src/SFileAttributes.cpp b/dep/StormLib/src/SFileAttributes.cpp index 6ac11ea06d9..e3659bfdc9a 100644 --- a/dep/StormLib/src/SFileAttributes.cpp +++ b/dep/StormLib/src/SFileAttributes.cpp @@ -9,8 +9,6 @@ /*****************************************************************************/ #define __STORMLIB_SELF__ -#define __INCLUDE_COMPRESSION__ -#define __INCLUDE_CRYPTOGRAPHY__ #include "StormLib.h" #include "StormCommon.h" @@ -25,6 +23,7 @@ typedef struct _MPQ_ATTRIBUTES_HEADER // Followed by an array of CRC32 // Followed by an array of file times // Followed by an array of MD5 + // Followed by an array of patch bits } MPQ_ATTRIBUTES_HEADER, *PMPQ_ATTRIBUTES_HEADER; //----------------------------------------------------------------------------- @@ -33,7 +32,6 @@ typedef struct _MPQ_ATTRIBUTES_HEADER int SAttrLoadAttributes(TMPQArchive * ha) { MPQ_ATTRIBUTES_HEADER AttrHeader; - TMPQFile * hf; HANDLE hFile = NULL; DWORD dwBlockTableSize = ha->pHeader->dwBlockTableSize; DWORD dwArraySize; @@ -48,18 +46,21 @@ int SAttrLoadAttributes(TMPQArchive * ha) // If it's not there, then the archive doesn't support attributes if(SFileOpenFileEx((HANDLE)ha, ATTRIBUTES_NAME, SFILE_OPEN_ANY_LOCALE, &hFile)) { - // Remember the flags for (attributes) - hf = (TMPQFile *)hFile; - ha->dwFileFlags2 = hf->pFileEntry->dwFlags; - // Load the content of the attributes file SFileReadFile(hFile, &AttrHeader, sizeof(MPQ_ATTRIBUTES_HEADER), &dwBytesRead, NULL); - AttrHeader.dwVersion = BSWAP_INT32_UNSIGNED(AttrHeader.dwVersion); - AttrHeader.dwFlags = BSWAP_INT32_UNSIGNED(AttrHeader.dwFlags); - ha->dwAttrFlags = AttrHeader.dwFlags; if(dwBytesRead != sizeof(MPQ_ATTRIBUTES_HEADER)) nError = ERROR_FILE_CORRUPT; + // Verify the header of the (attributes) file + if(nError == ERROR_SUCCESS) + { + AttrHeader.dwVersion = BSWAP_INT32_UNSIGNED(AttrHeader.dwVersion); + AttrHeader.dwFlags = BSWAP_INT32_UNSIGNED(AttrHeader.dwFlags); + ha->dwAttrFlags = AttrHeader.dwFlags; + if(dwBytesRead != sizeof(MPQ_ATTRIBUTES_HEADER)) + nError = ERROR_FILE_CORRUPT; + } + // Verify format of the attributes if(nError == ERROR_SUCCESS) { @@ -70,7 +71,7 @@ int SAttrLoadAttributes(TMPQArchive * ha) // Load the CRC32 (if any) if(nError == ERROR_SUCCESS && (AttrHeader.dwFlags & MPQ_ATTRIBUTE_CRC32)) { - LPDWORD pArrayCRC32 = ALLOCMEM(DWORD, dwBlockTableSize); + LPDWORD pArrayCRC32 = STORM_ALLOC(DWORD, dwBlockTableSize); if(pArrayCRC32 != NULL) { @@ -84,7 +85,7 @@ int SAttrLoadAttributes(TMPQArchive * ha) else nError = ERROR_FILE_CORRUPT; - FREEMEM(pArrayCRC32); + STORM_FREE(pArrayCRC32); } else nError = ERROR_NOT_ENOUGH_MEMORY; @@ -93,7 +94,7 @@ int SAttrLoadAttributes(TMPQArchive * ha) // Read the array of file times if(nError == ERROR_SUCCESS && (AttrHeader.dwFlags & MPQ_ATTRIBUTE_FILETIME)) { - ULONGLONG * pArrayFileTime = ALLOCMEM(ULONGLONG, dwBlockTableSize); + ULONGLONG * pArrayFileTime = STORM_ALLOC(ULONGLONG, dwBlockTableSize); if(pArrayFileTime != NULL) { @@ -107,7 +108,7 @@ int SAttrLoadAttributes(TMPQArchive * ha) else nError = ERROR_FILE_CORRUPT; - FREEMEM(pArrayFileTime); + STORM_FREE(pArrayFileTime); } else nError = ERROR_NOT_ENOUGH_MEMORY; @@ -117,7 +118,7 @@ int SAttrLoadAttributes(TMPQArchive * ha) // Note: MD5 array can be incomplete, if it's the last array in the (attributes) if(nError == ERROR_SUCCESS && (AttrHeader.dwFlags & MPQ_ATTRIBUTE_MD5)) { - unsigned char * pArrayMD5 = ALLOCMEM(unsigned char, (dwBlockTableSize * MD5_DIGEST_SIZE)); + unsigned char * pArrayMD5 = STORM_ALLOC(unsigned char, (dwBlockTableSize * MD5_DIGEST_SIZE)); unsigned char * md5; if(pArrayMD5 != NULL) @@ -136,16 +137,50 @@ int SAttrLoadAttributes(TMPQArchive * ha) else nError = ERROR_FILE_CORRUPT; - FREEMEM(pArrayMD5); + STORM_FREE(pArrayMD5); } else nError = ERROR_NOT_ENOUGH_MEMORY; } - // - // Note: Version 7.00 of StormLib saved the (attributes) incorrectly. + // Read the patch bit for each file + if(nError == ERROR_SUCCESS && (AttrHeader.dwFlags & MPQ_ATTRIBUTE_PATCH_BIT)) + { + LPBYTE pbBitArray; + DWORD dwByteSize = ((dwBlockTableSize - 1) / 8) + 1; + + pbBitArray = STORM_ALLOC(BYTE, dwByteSize); + if(pbBitArray != NULL) + { + SFileReadFile(hFile, pbBitArray, dwByteSize, &dwBytesRead, NULL); + if(dwBytesRead == dwByteSize) + { + for(i = 0; i < dwBlockTableSize; i++) + { + DWORD dwByteIndex = i / 8; + DWORD dwBitMask = 0x80 >> (i & 7); + + // Is the appropriate bit set? + if(pbBitArray[dwByteIndex] & dwBitMask) + { + // At the moment, we assume that the patch bit is present + // in both file table and (attributes) + assert((ha->pFileTable[i].dwFlags & MPQ_FILE_PATCH_FILE) != 0); + ha->pFileTable[i].dwFlags |= MPQ_FILE_PATCH_FILE; + } + } + } + else + nError = ERROR_FILE_CORRUPT; + + STORM_FREE(pbBitArray); + } + } + + // + // Note: Version 7.00 of StormLib saved the (attributes) incorrectly. // Sometimes, number of entries in the (attributes) was 1 item less - // than block table size. + // than block table size. // If we encounter such table, we will zero all three arrays // @@ -169,6 +204,19 @@ int SAttrFileSaveToMpq(TMPQArchive * ha) DWORD i; int nError = ERROR_SUCCESS; + // Now we have to check if we need patch bits in the (attributes) + if(nError == ERROR_SUCCESS) + { + for(i = 0; i < ha->dwFileTableSize; i++) + { + if(ha->pFileTable[i].dwFlags & MPQ_FILE_PATCH_FILE) + { + ha->dwAttrFlags |= MPQ_ATTRIBUTE_PATCH_BIT; + break; + } + } + } + // If the (attributes) is not in the file table yet, // we have to increase the final block table size pFileEntry = GetFileEntryExact(ha, ATTRIBUTES_NAME, LANG_NEUTRAL); @@ -205,24 +253,30 @@ int SAttrFileSaveToMpq(TMPQArchive * ha) dwFileSize += dwFinalBlockTableSize * sizeof(ULONGLONG); if(ha->dwAttrFlags & MPQ_ATTRIBUTE_MD5) dwFileSize += dwFinalBlockTableSize * MD5_DIGEST_SIZE; + if(ha->dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT) + dwFileSize += ((dwFinalBlockTableSize - 1)) / 8 + 1; } + // Determine the flags for (attributes) + if(ha->dwFileFlags2 == 0) + ha->dwFileFlags2 = GetDefaultSpecialFileFlags(ha, dwFileSize); + // Create the attributes file in the MPQ - assert(ha->dwFileFlags2 != 0); nError = SFileAddFile_Init(ha, ATTRIBUTES_NAME, - NULL, - dwFileSize, - LANG_NEUTRAL, - ha->dwFileFlags2, - &hf); + 0, + dwFileSize, + LANG_NEUTRAL, + ha->dwFileFlags2 | MPQ_FILE_REPLACEEXISTING, + &hf); // Write all parts of the (attributes) file if(nError == ERROR_SUCCESS) { assert(ha->dwFileTableSize == dwFinalBlockTableSize); + // Note that we don't know what the new bit (0x08) means. AttrHeader.dwVersion = BSWAP_INT32_UNSIGNED(100); - AttrHeader.dwFlags = BSWAP_INT32_UNSIGNED(ha->dwAttrFlags); + AttrHeader.dwFlags = BSWAP_INT32_UNSIGNED((ha->dwAttrFlags & MPQ_ATTRIBUTE_ALL)); dwToWrite = sizeof(MPQ_ATTRIBUTES_HEADER); nError = SFileAddFile_Write(hf, &AttrHeader, dwToWrite, MPQ_COMPRESSION_ZLIB); } @@ -230,7 +284,7 @@ int SAttrFileSaveToMpq(TMPQArchive * ha) // Write the array of CRC32 if(nError == ERROR_SUCCESS && (ha->dwAttrFlags & MPQ_ATTRIBUTE_CRC32)) { - LPDWORD pArrayCRC32 = ALLOCMEM(DWORD, dwFinalBlockTableSize); + LPDWORD pArrayCRC32 = STORM_ALLOC(DWORD, dwFinalBlockTableSize); if(pArrayCRC32 != NULL) { @@ -240,14 +294,14 @@ int SAttrFileSaveToMpq(TMPQArchive * ha) dwToWrite = ha->dwFileTableSize * sizeof(DWORD); nError = SFileAddFile_Write(hf, pArrayCRC32, dwToWrite, MPQ_COMPRESSION_ZLIB); - FREEMEM(pArrayCRC32); + STORM_FREE(pArrayCRC32); } } // Write the array of file time if(nError == ERROR_SUCCESS && (ha->dwAttrFlags & MPQ_ATTRIBUTE_FILETIME)) { - ULONGLONG * pArrayFileTime = ALLOCMEM(ULONGLONG, ha->dwFileTableSize); + ULONGLONG * pArrayFileTime = STORM_ALLOC(ULONGLONG, ha->dwFileTableSize); if(pArrayFileTime != NULL) { @@ -257,14 +311,14 @@ int SAttrFileSaveToMpq(TMPQArchive * ha) dwToWrite = ha->dwFileTableSize * sizeof(ULONGLONG); nError = SFileAddFile_Write(hf, pArrayFileTime, dwToWrite, MPQ_COMPRESSION_ZLIB); - FREEMEM(pArrayFileTime); + STORM_FREE(pArrayFileTime); } } // Write the array of MD5s if(nError == ERROR_SUCCESS && (ha->dwAttrFlags & MPQ_ATTRIBUTE_MD5)) { - char * pArrayMD5 = ALLOCMEM(char, ha->dwFileTableSize * MD5_DIGEST_SIZE); + char * pArrayMD5 = STORM_ALLOC(char, ha->dwFileTableSize * MD5_DIGEST_SIZE); if(pArrayMD5 != NULL) { @@ -274,7 +328,31 @@ int SAttrFileSaveToMpq(TMPQArchive * ha) dwToWrite = ha->dwFileTableSize * MD5_DIGEST_SIZE; nError = SFileAddFile_Write(hf, pArrayMD5, dwToWrite, MPQ_COMPRESSION_ZLIB); - FREEMEM(pArrayMD5); + STORM_FREE(pArrayMD5); + } + } + + // Write the array of patch bits + if(nError == ERROR_SUCCESS && (ha->dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT)) + { + LPBYTE pbBitArray; + DWORD dwByteSize = ((ha->dwFileTableSize - 1) / 8) + 1; + + pbBitArray = STORM_ALLOC(BYTE, dwByteSize); + if(pbBitArray != NULL) + { + memset(pbBitArray, 0, dwByteSize); + for(i = 0; i < ha->dwFileTableSize; i++) + { + DWORD dwByteIndex = i / 8; + DWORD dwBitMask = 0x80 >> (i & 7); + + if(ha->pFileTable[i].dwFlags & MPQ_FILE_PATCH_FILE) + pbBitArray[dwByteIndex] |= dwBitMask; + } + + nError = SFileAddFile_Write(hf, pbBitArray, dwByteSize, MPQ_COMPRESSION_ZLIB); + STORM_FREE(pbBitArray); } } @@ -282,9 +360,10 @@ int SAttrFileSaveToMpq(TMPQArchive * ha) if(hf != NULL) { SFileAddFile_Finish(hf); - ha->dwFlags |= MPQ_FLAG_ATTRIBS_VALID; } + if(nError == ERROR_SUCCESS) + ha->dwFlags &= ~MPQ_FLAG_INV_ATTRIBUTES; return nError; } @@ -324,8 +403,8 @@ bool WINAPI SFileSetAttributes(HANDLE hMpq, DWORD dwFlags) } // Set the attributes + InvalidateInternalFiles(ha); ha->dwAttrFlags = (dwFlags & MPQ_ATTRIBUTE_ALL); - ha->dwFlags |= MPQ_FLAG_CHANGED; return true; } @@ -360,7 +439,7 @@ bool WINAPI SFileUpdateFileAttributes(HANDLE hMpq, const char * szFileName) // Get the file size hf = (TMPQFile *)hFile; - SFileGetFileInfo(hFile, SFILE_INFO_FILE_SIZE, &dwTotalBytes, sizeof(DWORD)); + SFileGetFileInfo(hFile, SFILE_INFO_FILE_SIZE, &dwTotalBytes, sizeof(DWORD), NULL); // Initialize the CRC32 and MD5 contexts md5_init(&md5_state); @@ -387,7 +466,7 @@ bool WINAPI SFileUpdateFileAttributes(HANDLE hMpq, const char * szFileName) md5_done(&md5_state, hf->pFileEntry->md5); // Remember that we need to save the MPQ tables - ha->dwFlags |= MPQ_FLAG_CHANGED; + InvalidateInternalFiles(ha); SFileCloseFile(hFile); return true; } diff --git a/dep/StormLib/src/SFileCompactArchive.cpp b/dep/StormLib/src/SFileCompactArchive.cpp index 6c80837e939..46ae8be8326 100644 --- a/dep/StormLib/src/SFileCompactArchive.cpp +++ b/dep/StormLib/src/SFileCompactArchive.cpp @@ -81,10 +81,10 @@ static int CheckIfAllFilesKnown(TMPQArchive * ha, const char * szListFile, LPDWO } static int CopyNonMpqData( - TFileStream * pSrcStream, - TFileStream * pTrgStream, - ULONGLONG & ByteOffset, - ULONGLONG & ByteCount) + TFileStream * pSrcStream, + TFileStream * pTrgStream, + ULONGLONG & ByteOffset, + ULONGLONG & ByteCount) { ULONGLONG DataSize = ByteCount; DWORD dwToRead; @@ -130,9 +130,9 @@ static int CopyNonMpqData( // Copies all file sectors into another archive. static int CopyMpqFileSectors( - TMPQArchive * ha, - TMPQFile * hf, - TFileStream * pNewStream) + TMPQArchive * ha, + TMPQFile * hf, + TFileStream * pNewStream) { TFileEntry * pFileEntry = hf->pFileEntry; ULONGLONG RawFilePos; // Used for calculating sector offset in the old MPQ archive @@ -145,10 +145,10 @@ static int CopyMpqFileSectors( int nError = ERROR_SUCCESS; // Remember the position in the destination file - FileStream_GetPos(pNewStream, MpqFilePos); + FileStream_GetPos(pNewStream, &MpqFilePos); MpqFilePos -= ha->MpqPos; - // Resolve decryption keys. Note that the file key given + // Resolve decryption keys. Note that the file key given // in the TMPQFile structure also includes the key adjustment if(nError == ERROR_SUCCESS && (pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)) { @@ -174,7 +174,7 @@ static int CopyMpqFileSectors( // If we have to save sector offset table, do it. if(nError == ERROR_SUCCESS && hf->SectorOffsets != NULL) { - DWORD * SectorOffsetsCopy = (DWORD *)ALLOCMEM(BYTE, hf->SectorOffsets[0]); + DWORD * SectorOffsetsCopy = (DWORD *)STORM_ALLOC(BYTE, hf->SectorOffsets[0]); DWORD dwSectorOffsLen = hf->SectorOffsets[0]; assert((pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT) == 0); @@ -194,6 +194,7 @@ static int CopyMpqFileSectors( if(!FileStream_Write(pNewStream, NULL, SectorOffsetsCopy, dwSectorOffsLen)) nError = GetLastError(); + dwBytesToCopy -= dwSectorOffsLen; dwCmpSize += dwSectorOffsLen; } @@ -204,7 +205,7 @@ static int CopyMpqFileSectors( CompactCB(pvUserData, CCB_COMPACTING_FILES, CompactBytesProcessed, CompactTotalBytes); } - FREEMEM(SectorOffsetsCopy); + STORM_FREE(SectorOffsetsCopy); } // Now we have to copy all file sectors. We do it without @@ -216,10 +217,6 @@ static int CopyMpqFileSectors( DWORD dwRawDataInSector = hf->dwSectorSize; DWORD dwRawByteOffset = dwSector * hf->dwSectorSize; - // Last sector: If there is not enough bytes remaining in the file, cut the raw size - if(dwRawDataInSector > dwBytesToCopy) - dwRawDataInSector = dwBytesToCopy; - // Fix the raw data length if the file is compressed if(hf->SectorOffsets != NULL) { @@ -227,9 +224,13 @@ static int CopyMpqFileSectors( dwRawByteOffset = hf->SectorOffsets[dwSector]; } + // Last sector: If there is not enough bytes remaining in the file, cut the raw size + if(dwRawDataInSector > dwBytesToCopy) + dwRawDataInSector = dwBytesToCopy; + // Calculate the raw file offset of the file sector CalculateRawSectorOffset(RawFilePos, hf, dwRawByteOffset); - + // Read the file sector if(!FileStream_Read(ha->pStream, &RawFilePos, hf->pbFileSector, dwRawDataInSector)) { @@ -238,7 +239,7 @@ static int CopyMpqFileSectors( } // If necessary, re-encrypt the sector - // Note: Recompression is not necessary here. Unlike encryption, + // Note: Recompression is not necessary here. Unlike encryption, // the compression does not depend on the position of the file in MPQ. if((pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) && dwFileKey1 != dwFileKey2) { @@ -263,7 +264,7 @@ static int CopyMpqFileSectors( } // Adjust byte counts - dwBytesToCopy -= hf->dwSectorSize; + dwBytesToCopy -= dwRawDataInSector; dwCmpSize += dwRawDataInSector; } } @@ -291,14 +292,43 @@ static int CopyMpqFileSectors( } // Size of the CRC block is also included in the compressed file size + dwBytesToCopy -= dwCrcLength; dwCmpSize += dwCrcLength; } } + // There might be extra data beyond sector checksum table + // Sometimes, these data are even part of sector offset table + // Examples: + // 2012 - WoW\15354\locale-enGB.MPQ:DBFilesClient\SpellLevels.dbc + // 2012 - WoW\15354\locale-enGB.MPQ:Interface\AddOns\Blizzard_AuctionUI\Blizzard_AuctionUI.xml + if(nError == ERROR_SUCCESS && dwBytesToCopy != 0) + { + LPBYTE pbExtraData; + + // Allocate space for the extra data + pbExtraData = STORM_ALLOC(BYTE, dwBytesToCopy); + if(pbExtraData != NULL) + { + if(!FileStream_Read(ha->pStream, NULL, pbExtraData, dwBytesToCopy)) + nError = GetLastError(); + + if(!FileStream_Write(pNewStream, NULL, pbExtraData, dwBytesToCopy)) + nError = GetLastError(); + + // Include these extra data in the compressed size + dwCmpSize += dwBytesToCopy; + dwBytesToCopy = 0; + STORM_FREE(pbExtraData); + } + else + nError = ERROR_NOT_ENOUGH_MEMORY; + } + // Write the MD5's of the raw file data, if needed if(nError == ERROR_SUCCESS && ha->pHeader->dwRawChunkSize != 0) { - nError = WriteMpqDataMD5(pNewStream, + nError = WriteMpqDataMD5(pNewStream, ha->MpqPos + MpqFilePos, pFileEntry->dwCmpSize, ha->pHeader->dwRawChunkSize); @@ -310,7 +340,7 @@ static int CopyMpqFileSectors( // At this point, number of bytes written should be exactly // the same like the compressed file size. If it isn't, // there's something wrong (an unknown archive version, MPQ protection, ...) - // + // // Note: Diablo savegames have very weird layout, and the file "hero" // seems to have improper compressed size. Instead of real compressed size, // the "dwCmpSize" member of the block table entry contains @@ -431,8 +461,8 @@ bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bR ULONGLONG ByteOffset; ULONGLONG ByteCount; LPDWORD pFileKeys = NULL; - char szTempFile[MAX_PATH] = ""; - char * szTemp = NULL; + TCHAR szTempFile[MAX_PATH] = _T(""); + TCHAR * szTemp = NULL; int nError = ERROR_SUCCESS; // Test the valid parameters @@ -450,7 +480,7 @@ bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bR // Create the table with file keys if(nError == ERROR_SUCCESS) { - if((pFileKeys = ALLOCMEM(DWORD, ha->dwFileTableSize)) != NULL) + if((pFileKeys = STORM_ALLOC(DWORD, ha->dwFileTableSize)) != NULL) memset(pFileKeys, 0, sizeof(DWORD) * ha->dwFileTableSize); else nError = ERROR_NOT_ENOUGH_MEMORY; @@ -461,7 +491,7 @@ bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bR if(nError == ERROR_SUCCESS) { // Initialize the progress variables for compact callback - FileStream_GetSize(ha->pStream, CompactTotalBytes); + FileStream_GetSize(ha->pStream, &CompactTotalBytes); CompactBytesProcessed = 0; nError = CheckIfAllFilesKnown(ha, szListFile, pFileKeys); } @@ -469,13 +499,13 @@ bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bR // Get the temporary file name and create it if(nError == ERROR_SUCCESS) { - strcpy(szTempFile, ha->pStream->szFileName); - if((szTemp = strrchr(szTempFile, '.')) != NULL) - strcpy(szTemp + 1, "mp_"); + _tcscpy(szTempFile, FileStream_GetFileName(ha->pStream)); + if((szTemp = _tcsrchr(szTempFile, '.')) != NULL) + _tcscpy(szTemp + 1, _T("mp_")); else - strcat(szTempFile, "_"); + _tcscat(szTempFile, _T("_")); - pTempStream = FileStream_CreateFile(szTempFile); + pTempStream = FileStream_CreateFile(szTempFile, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); if(pTempStream == NULL) nError = GetLastError(); } @@ -525,13 +555,13 @@ bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bR if(nError == ERROR_SUCCESS) { nError = CopyMpqFiles(ha, pFileKeys, pTempStream); - ha->dwFlags |= MPQ_FLAG_CHANGED | MPQ_FLAG_LISTFILE_VALID | MPQ_FLAG_ATTRIBS_VALID; + ha->dwFlags |= MPQ_FLAG_CHANGED; } // If succeeded, switch the streams if(nError == ERROR_SUCCESS) { - if(FileStream_MoveFile(ha->pStream, pTempStream)) + if(FileStream_Switch(ha->pStream, pTempStream)) pTempStream = NULL; else nError = ERROR_CAN_NOT_COMPLETE; @@ -543,7 +573,7 @@ bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bR // // Note: We don't recalculate position of the MPQ tables at this point. // SaveMPQTables does it automatically. - // + // nError = SaveMPQTables(ha); if(nError == ERROR_SUCCESS && CompactCB != NULL) @@ -562,7 +592,7 @@ bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bR if(pTempStream != NULL) FileStream_Close(pTempStream); if(pFileKeys != NULL) - FREEMEM(pFileKeys); + STORM_FREE(pFileKeys); if(nError != ERROR_SUCCESS) SetLastError(nError); return (nError == ERROR_SUCCESS); @@ -571,13 +601,24 @@ bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bR //----------------------------------------------------------------------------- // Changing hash table size +DWORD WINAPI SFileGetMaxFileCount(HANDLE hMpq) +{ + TMPQArchive * ha = (TMPQArchive *)hMpq; + + return ha->dwMaxFileCount; +} + bool WINAPI SFileSetMaxFileCount(HANDLE hMpq, DWORD dwMaxFileCount) { TMPQHetTable * pOldHetTable = NULL; TMPQArchive * ha = (TMPQArchive *)hMpq; + TFileEntry * pOldFileTableEnd = ha->pFileTable + ha->dwFileTableSize; + TFileEntry * pOldFileTable = NULL; + TFileEntry * pOldFileEntry; + TFileEntry * pFileEntry; TMPQHash * pOldHashTable = NULL; DWORD dwOldHashTableSize = 0; - DWORD dwOldMaxFileCount = ha->dwMaxFileCount; + DWORD dwOldFileTableSize = 0; int nError = ERROR_SUCCESS; // Test the valid parameters @@ -585,11 +626,17 @@ bool WINAPI SFileSetMaxFileCount(HANDLE hMpq, DWORD dwMaxFileCount) nError = ERROR_INVALID_HANDLE; if(ha->dwFlags & MPQ_FLAG_READ_ONLY) nError = ERROR_ACCESS_DENIED; - + + // The new limit must not be lower than the index of the last file entry in the table + if(nError == ERROR_SUCCESS && ha->dwFileTableSize > dwMaxFileCount) + nError = ERROR_DISK_FULL; + // ALL file names must be known in order to be able // to rebuild hash table size if(nError == ERROR_SUCCESS) + { nError = CheckIfAllFilesKnown(ha, NULL, NULL); + } // If the MPQ has a hash table, then we relocate the hash table if(nError == ERROR_SUCCESS && ha->pHashTable != NULL) @@ -600,7 +647,7 @@ bool WINAPI SFileSetMaxFileCount(HANDLE hMpq, DWORD dwMaxFileCount) // Allocate new hash table ha->pHeader->dwHashTableSize = GetHashTableSizeForFileCount(dwMaxFileCount); - ha->pHashTable = ALLOCMEM(TMPQHash, ha->pHeader->dwHashTableSize); + ha->pHashTable = STORM_ALLOC(TMPQHash, ha->pHeader->dwHashTableSize); if(ha->pHashTable != NULL) memset(ha->pHashTable, 0xFF, ha->pHeader->dwHashTableSize * sizeof(TMPQHash)); else @@ -622,38 +669,34 @@ bool WINAPI SFileSetMaxFileCount(HANDLE hMpq, DWORD dwMaxFileCount) // Now reallocate the file table if(nError == ERROR_SUCCESS) { - TFileEntry * pOldFileTable = ha->pFileTable; + // Save the current file table + dwOldFileTableSize = ha->dwFileTableSize; + pOldFileTable = ha->pFileTable; - ha->pFileTable = ALLOCMEM(TFileEntry, dwMaxFileCount); + // Create new one + ha->pFileTable = STORM_ALLOC(TFileEntry, dwMaxFileCount); if(ha->pFileTable != NULL) - { memset(ha->pFileTable, 0, dwMaxFileCount * sizeof(TFileEntry)); - memcpy(ha->pFileTable, pOldFileTable, ha->dwFileTableSize * sizeof(TFileEntry)); - ha->dwMaxFileCount = dwMaxFileCount; - } else - { nError = ERROR_NOT_ENOUGH_MEMORY; - ha->pFileTable = pOldFileTable; - } } // Now we have to build both classic hash table and HET table. if(nError == ERROR_SUCCESS) { - TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; - TFileEntry * pFileEntry; DWORD dwFileIndex = 0; DWORD dwHashIndex = 0; - // Make new hash table entry for each file - for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++, dwFileIndex++) + // Create new hash and HET entry for each file + pFileEntry = ha->pFileTable; + for(pOldFileEntry = pOldFileTable; pOldFileEntry < pOldFileTableEnd; pOldFileEntry++) { - if(pFileEntry->dwFlags & MPQ_FILE_EXISTS) + if(pOldFileEntry->dwFlags & MPQ_FILE_EXISTS) { - // The file name must be known + // Copy the old file entry to the new one + memcpy(pFileEntry, pOldFileEntry, sizeof(TFileEntry)); assert(pFileEntry->szFileName != NULL); - + // Create new entry in the hash table if(ha->pHashTable != NULL) { @@ -675,36 +718,45 @@ bool WINAPI SFileSetMaxFileCount(HANDLE hMpq, DWORD dwMaxFileCount) break; } } + + // Move to the next file entry in the new table + pFileEntry++; + dwFileIndex++; } } } // Mark the archive as changed - // Keep the (listfile) and (attributes) as-is + // Note: We always have to rebuild the (attributes) file due to file table change if(nError == ERROR_SUCCESS) { - ha->dwFlags |= MPQ_FLAG_CHANGED | MPQ_FLAG_LISTFILE_VALID | MPQ_FLAG_ATTRIBS_VALID; - SaveMPQTables(ha); + ha->dwMaxFileCount = dwMaxFileCount; + InvalidateInternalFiles(ha); } else { // Revert the hash table if(ha->pHashTable != NULL && pOldHashTable != NULL) { - FREEMEM(ha->pHashTable); + STORM_FREE(ha->pHashTable); ha->pHeader->dwHashTableSize = dwOldHashTableSize; ha->pHashTable = pOldHashTable; } - // Revert HET table + // Revert the HET table if(ha->pHetTable != NULL && pOldHetTable != NULL) { FreeHetTable(ha->pHetTable); ha->pHetTable = pOldHetTable; } - // Revert maximum file count - ha->dwMaxFileCount = dwOldMaxFileCount; + // Revert the file table + if(pOldFileTable != NULL) + { + STORM_FREE(ha->pFileTable); + ha->pFileTable = pOldFileTable; + } + SetLastError(nError); } diff --git a/dep/StormLib/src/SFileCreateArchive.cpp b/dep/StormLib/src/SFileCreateArchive.cpp index 240efeab7ab..3ce0c02b5af 100644 --- a/dep/StormLib/src/SFileCreateArchive.cpp +++ b/dep/StormLib/src/SFileCreateArchive.cpp @@ -66,19 +66,48 @@ static int WriteNakedMPQHeader(TMPQArchive * ha) //----------------------------------------------------------------------------- // Creates a new MPQ archive. -bool WINAPI SFileCreateArchive(const char * szMpqName, DWORD dwFlags, DWORD dwMaxFileCount, HANDLE * phMpq) +bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwFlags, DWORD dwMaxFileCount, HANDLE * phMpq) +{ + SFILE_CREATE_MPQ CreateInfo; + + // Fill the create structure + memset(&CreateInfo, 0, sizeof(SFILE_CREATE_MPQ)); + CreateInfo.cbSize = sizeof(SFILE_CREATE_MPQ); + CreateInfo.dwMpqVersion = (dwFlags & MPQ_CREATE_ARCHIVE_VMASK) >> FLAGS_TO_FORMAT_SHIFT; + CreateInfo.dwStreamFlags = STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE; + CreateInfo.dwAttrFlags = (dwFlags & MPQ_CREATE_ATTRIBUTES) ? MPQ_ATTRIBUTE_ALL : 0; + CreateInfo.dwSectorSize = (CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_3) ? 0x4000 : 0x1000; + CreateInfo.dwRawChunkSize = (CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_4) ? 0x4000 : 0; + CreateInfo.dwMaxFileCount = dwMaxFileCount; + return SFileCreateArchive2(szMpqName, &CreateInfo, phMpq); +} + +bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCreateInfo, HANDLE * phMpq) { TFileStream * pStream = NULL; // File stream TMPQArchive * ha = NULL; // MPQ archive handle ULONGLONG MpqPos = 0; // Position of MPQ header in the file HANDLE hMpq = NULL; - USHORT wFormatVersion = MPQ_FORMAT_VERSION_1; DWORD dwBlockTableSize = 0; // Initial block table size DWORD dwHashTableSize = 0; + DWORD dwMaxFileCount; int nError = ERROR_SUCCESS; // Check the parameters, if they are valid - if(szMpqName == NULL || *szMpqName == 0 || phMpq == NULL) + if(szMpqName == NULL || *szMpqName == 0 || pCreateInfo == NULL || phMpq == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return false; + } + + // Verify if all variables in SFILE_CREATE_MPQ are correct + if((pCreateInfo->cbSize == 0 || pCreateInfo->cbSize > sizeof(SFILE_CREATE_MPQ)) || + (pCreateInfo->dwMpqVersion > MPQ_FORMAT_VERSION_4) || + (pCreateInfo->pvUserData != NULL || pCreateInfo->cbUserData != 0) || + (pCreateInfo->dwAttrFlags & ~MPQ_ATTRIBUTE_ALL) || + (pCreateInfo->dwSectorSize & (pCreateInfo->dwSectorSize - 1)) || + (pCreateInfo->dwRawChunkSize & (pCreateInfo->dwRawChunkSize - 1)) || + (pCreateInfo->dwMaxFileCount < 4)) { SetLastError(ERROR_INVALID_PARAMETER); return false; @@ -89,7 +118,7 @@ bool WINAPI SFileCreateArchive(const char * szMpqName, DWORD dwFlags, DWORD dwMa // We verify if the file already exists and if it's a MPQ archive. // If yes, we won't allow to overwrite it. - if(SFileOpenArchive(szMpqName, 0, dwFlags, &hMpq)) + if(SFileOpenArchive(szMpqName, 0, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE | MPQ_OPEN_NO_ATTRIBUTES | MPQ_OPEN_NO_LISTFILE, &hMpq)) { SFileCloseArchive(hMpq); SetLastError(ERROR_ALREADY_EXISTS); @@ -102,25 +131,18 @@ bool WINAPI SFileCreateArchive(const char * szMpqName, DWORD dwFlags, DWORD dwMa // - If the file doesn't exist, create new empty file // - pStream = FileStream_OpenFile(szMpqName, true); + pStream = FileStream_OpenFile(szMpqName, pCreateInfo->dwStreamFlags); if(pStream == NULL) { - pStream = FileStream_CreateFile(szMpqName); + pStream = FileStream_CreateFile(szMpqName, pCreateInfo->dwStreamFlags); if(pStream == NULL) return false; } - // Decide what format to use - wFormatVersion = (USHORT)((dwFlags & MPQ_CREATE_ARCHIVE_VMASK) >> 16); - if(wFormatVersion > MPQ_FORMAT_VERSION_4) - { - SetLastError(ERROR_INVALID_PARAMETER); - return false; - } - // Increment the maximum amount of files to have space // for listfile and attributes file - if(dwFlags & MPQ_CREATE_ATTRIBUTES) + dwMaxFileCount = pCreateInfo->dwMaxFileCount; + if(pCreateInfo->dwAttrFlags != 0) dwMaxFileCount++; dwMaxFileCount++; @@ -128,20 +150,20 @@ bool WINAPI SFileCreateArchive(const char * szMpqName, DWORD dwFlags, DWORD dwMa dwHashTableSize = GetHashTableSizeForFileCount(dwMaxFileCount); // Retrieve the file size and round it up to 0x200 bytes - FileStream_GetSize(pStream, MpqPos); + FileStream_GetSize(pStream, &MpqPos); MpqPos = (MpqPos + 0x1FF) & (ULONGLONG)0xFFFFFFFFFFFFFE00ULL; if(!FileStream_SetSize(pStream, MpqPos)) nError = GetLastError(); -#ifdef _DEBUG +#ifdef _DEBUG // Debug code, used for testing StormLib -// dwBlockTableSize = dwHashTableSize * 2; + // dwBlockTableSize = dwHashTableSize * 2; #endif // Create the archive handle if(nError == ERROR_SUCCESS) { - if((ha = ALLOCMEM(TMPQArchive, 1)) == NULL) + if((ha = STORM_ALLOC(TMPQArchive, 1)) == NULL) nError = ERROR_NOT_ENOUGH_MEMORY; } @@ -150,19 +172,18 @@ bool WINAPI SFileCreateArchive(const char * szMpqName, DWORD dwFlags, DWORD dwMa { memset(ha, 0, sizeof(TMPQArchive)); ha->pStream = pStream; - ha->dwSectorSize = (wFormatVersion >= MPQ_FORMAT_VERSION_3) ? 0x4000 : 0x1000; + ha->dwSectorSize = pCreateInfo->dwSectorSize; ha->UserDataPos = MpqPos; ha->MpqPos = MpqPos; ha->pHeader = (TMPQHeader *)ha->HeaderData; ha->dwMaxFileCount = dwMaxFileCount; ha->dwFileTableSize = 0; - ha->dwFileFlags1 = MPQ_FILE_ENCRYPTED | MPQ_FILE_COMPRESS | MPQ_FILE_REPLACEEXISTING; - ha->dwFileFlags2 = MPQ_FILE_ENCRYPTED | MPQ_FILE_COMPRESS | MPQ_FILE_REPLACEEXISTING; + ha->dwFileFlags1 = pCreateInfo->dwFileFlags1; + ha->dwFileFlags2 = pCreateInfo->dwFileFlags2; ha->dwFlags = 0; // Setup the attributes - if(dwFlags & MPQ_CREATE_ATTRIBUTES) - ha->dwAttrFlags = MPQ_ATTRIBUTE_CRC32 | MPQ_ATTRIBUTE_FILETIME | MPQ_ATTRIBUTE_MD5; + ha->dwAttrFlags = pCreateInfo->dwAttrFlags; pStream = NULL; } @@ -174,9 +195,9 @@ bool WINAPI SFileCreateArchive(const char * szMpqName, DWORD dwFlags, DWORD dwMa // Fill the MPQ header memset(pHeader, 0, sizeof(ha->HeaderData)); pHeader->dwID = ID_MPQ; - pHeader->dwHeaderSize = MpqHeaderSizes[wFormatVersion]; + pHeader->dwHeaderSize = MpqHeaderSizes[pCreateInfo->dwMpqVersion]; pHeader->dwArchiveSize = pHeader->dwHeaderSize + dwHashTableSize * sizeof(TMPQHash); - pHeader->wFormatVersion = wFormatVersion; + pHeader->wFormatVersion = (USHORT)pCreateInfo->dwMpqVersion; pHeader->wSectorSize = GetSectorSizeShift(ha->dwSectorSize); pHeader->dwHashTablePos = pHeader->dwHeaderSize; pHeader->dwHashTableSize = dwHashTableSize; @@ -185,39 +206,34 @@ bool WINAPI SFileCreateArchive(const char * szMpqName, DWORD dwFlags, DWORD dwMa // For MPQs version 4 and higher, we set the size of raw data block // for calculating MD5 - if(wFormatVersion >= MPQ_FORMAT_VERSION_4) - pHeader->dwRawChunkSize = 0x4000; + if(pCreateInfo->dwMpqVersion >= MPQ_FORMAT_VERSION_4) + pHeader->dwRawChunkSize = pCreateInfo->dwRawChunkSize; // Write the naked MPQ header nError = WriteNakedMPQHeader(ha); - // - // Note: Don't recalculate position of MPQ tables at this point. - // We merely set a flag that indicates that the MPQ tables - // have been changed, and SaveMpqTables will do the work when closing the archive. - // - - ha->dwFlags |= MPQ_FLAG_CHANGED; - } - - // Create initial hash table - if(nError == ERROR_SUCCESS) - { - nError = CreateHashTable(ha, dwHashTableSize); + // Remember that the (listfile) and (attributes) need to be saved + ha->dwFlags |= MPQ_FLAG_CHANGED | MPQ_FLAG_INV_LISTFILE | MPQ_FLAG_INV_ATTRIBUTES; } // Create initial HET table, if the caller required an MPQ format 3.0 or newer - if(nError == ERROR_SUCCESS && wFormatVersion >= MPQ_FORMAT_VERSION_3) + if(nError == ERROR_SUCCESS && pCreateInfo->dwMpqVersion >= MPQ_FORMAT_VERSION_3) { ha->pHetTable = CreateHetTable(ha->dwMaxFileCount, 0x40, true); if(ha->pHetTable == NULL) nError = ERROR_NOT_ENOUGH_MEMORY; } + // Create initial hash table + if(nError == ERROR_SUCCESS) + { + nError = CreateHashTable(ha, dwHashTableSize); + } + // Create initial file table if(nError == ERROR_SUCCESS) { - ha->pFileTable = ALLOCMEM(TFileEntry, ha->dwMaxFileCount); + ha->pFileTable = STORM_ALLOC(TFileEntry, ha->dwMaxFileCount); if(ha->pFileTable != NULL) memset(ha->pFileTable, 0x00, sizeof(TFileEntry) * ha->dwMaxFileCount); else @@ -232,7 +248,7 @@ bool WINAPI SFileCreateArchive(const char * szMpqName, DWORD dwFlags, DWORD dwMa SetLastError(nError); ha = NULL; } - + // Return the values *phMpq = (HANDLE)ha; return (nError == ERROR_SUCCESS); diff --git a/dep/StormLib/src/SFileExtractFile.cpp b/dep/StormLib/src/SFileExtractFile.cpp index 8fb7dc57c5b..c8053ed4e62 100644 --- a/dep/StormLib/src/SFileExtractFile.cpp +++ b/dep/StormLib/src/SFileExtractFile.cpp @@ -12,7 +12,7 @@ #include "StormLib.h" #include "StormCommon.h" -bool WINAPI SFileExtractFile(HANDLE hMpq, const char * szToExtract, const char * szExtracted, DWORD dwSearchScope) +bool WINAPI SFileExtractFile(HANDLE hMpq, const char * szToExtract, const TCHAR * szExtracted, DWORD dwSearchScope) { TFileStream * pLocalFile = NULL; HANDLE hMpqFile = NULL; @@ -28,7 +28,7 @@ bool WINAPI SFileExtractFile(HANDLE hMpq, const char * szToExtract, const char * // Create the local file if(nError == ERROR_SUCCESS) { - pLocalFile = FileStream_CreateFile(szExtracted); + pLocalFile = FileStream_CreateFile(szExtracted, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); if(pLocalFile == NULL) nError = GetLastError(); } diff --git a/dep/StormLib/src/SFileFindFile.cpp b/dep/StormLib/src/SFileFindFile.cpp index cfab3173048..952d4d016f9 100644 --- a/dep/StormLib/src/SFileFindFile.cpp +++ b/dep/StormLib/src/SFileFindFile.cpp @@ -69,7 +69,7 @@ bool CheckWildCard(const char * szString, const char * szWildCard) szString++; } - // If there is '*', means zero or more chars. We have to + // If there is '*', means zero or more chars. We have to // find the sequence after '*' if(*szWildCard == '*') { @@ -151,9 +151,9 @@ static DWORD GetSearchTableItems(TMPQArchive * ha) } static bool FileWasFoundBefore( - TMPQArchive * ha, - TMPQSearch * hs, - TFileEntry * pFileEntry) + TMPQArchive * ha, + TMPQSearch * hs, + TFileEntry * pFileEntry) { TFileEntry * pEntry; char * szRealFileName = pFileEntry->szFileName; @@ -258,7 +258,7 @@ static int DoMPQSearch(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData) pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; pFileEntry = ha->pFileTable + hs->dwNextIndex; - // Get the start and end of the hash table + // Get the length of the patch prefix (0 if none) nPrefixLength = strlen(ha->szPatchPrefix); // Parse the file table @@ -285,21 +285,18 @@ static int DoMPQSearch(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData) szFileName = pFileEntry->szFileName; if(szFileName == NULL) { - // Open the file by index in order to check if the file exists - if(SFileOpenFileEx((HANDLE)hs->ha, (char *)(DWORD_PTR)dwBlockIndex, SFILE_OPEN_BY_INDEX, &hFile)) - SFileCloseFile(hFile); - - // If the name was retrieved, use that one. Otherwise, just use generic pseudo-name - szFileName = pFileEntry->szFileName; - if(szFileName == NULL) + // Open the file by its pseudo-name. + // This also generates the file name with a proper extension + sprintf(szPseudoName, "File%08u.xxx", dwBlockIndex); + if(SFileOpenFileEx((HANDLE)hs->ha, szPseudoName, SFILE_OPEN_FROM_MPQ, &hFile)) { - sprintf(szPseudoName, "File%08u.xxx", dwBlockIndex); - szFileName = szPseudoName; + szFileName = (pFileEntry->szFileName != NULL) ? pFileEntry->szFileName : szPseudoName; + SFileCloseFile(hFile); } } // Check the file name against the wildcard - if(CheckWildCard(szFileName, hs->szSearchMask)) + if(CheckWildCard(szFileName + nPrefixLength, hs->szSearchMask)) { // Fill the found entry lpFindFileData->dwHashIndex = pPatchEntry->dwHashIndex; @@ -315,7 +312,7 @@ static int DoMPQSearch(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData) // Fill the file name and plain file name strcpy(lpFindFileData->cFileName, szFileName + nPrefixLength); - lpFindFileData->szPlainName = (char *)GetPlainFileName(lpFindFileData->cFileName); + lpFindFileData->szPlainName = (char *)GetPlainFileNameA(lpFindFileData->cFileName); return ERROR_SUCCESS; } @@ -339,8 +336,8 @@ static void FreeMPQSearch(TMPQSearch *& hs) if(hs != NULL) { if(hs->pSearchTable != NULL) - FREEMEM(hs->pSearchTable); - FREEMEM(hs); + STORM_FREE(hs->pSearchTable); + STORM_FREE(hs); hs = NULL; } } @@ -371,7 +368,7 @@ HANDLE WINAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_DA if(nError == ERROR_SUCCESS) { nSize = sizeof(TMPQSearch) + strlen(szMask) + 1; - if((hs = (TMPQSearch *)ALLOCMEM(char, nSize)) == NULL) + if((hs = (TMPQSearch *)STORM_ALLOC(char, nSize)) == NULL) nError = ERROR_NOT_ENOUGH_MEMORY; } @@ -388,7 +385,7 @@ HANDLE WINAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_DA if(ha->haPatch != NULL) { hs->dwSearchTableItems = GetSearchTableItems(ha); - hs->pSearchTable = ALLOCMEM(TFileEntry *, hs->dwSearchTableItems); + hs->pSearchTable = STORM_ALLOC(TFileEntry *, hs->dwSearchTableItems); hs->dwFlagMask = MPQ_FILE_EXISTS | MPQ_FILE_PATCH_FILE; if(hs->pSearchTable != NULL) memset(hs->pSearchTable, 0, hs->dwSearchTableItems * sizeof(TFileEntry *)); @@ -409,7 +406,7 @@ HANDLE WINAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_DA FreeMPQSearch(hs); SetLastError(nError); } - + // Return the result value return (HANDLE)hs; } diff --git a/dep/StormLib/src/SFileListFile.cpp b/dep/StormLib/src/SFileListFile.cpp index eda1e1e9b60..a575c581a57 100644 --- a/dep/StormLib/src/SFileListFile.cpp +++ b/dep/StormLib/src/SFileListFile.cpp @@ -34,67 +34,70 @@ struct TListFileCache //----------------------------------------------------------------------------- // Local functions (cache) -static TListFileCache * CreateListFileCache(HANDLE hMpq, const char * szListFile) +static bool FreeListFileCache(TListFileCache * pCache) +{ + // Valid parameter check + if(pCache == NULL) + return false; + + // Free all allocated buffers + if(pCache->hFile != NULL) + SFileCloseFile(pCache->hFile); + if(pCache->szMask != NULL) + STORM_FREE(pCache->szMask); + STORM_FREE(pCache); + return true; +} + +static TListFileCache * CreateListFileCache(HANDLE hListFile, const char * szMask) { TListFileCache * pCache = NULL; - HANDLE hListFile = NULL; - DWORD dwSearchScope = SFILE_OPEN_LOCAL_FILE; DWORD dwBytesRead = 0; int nError = ERROR_SUCCESS; - // If the szListFile is NULL, it means we have to open internal listfile - if(szListFile == NULL) - { - // Use SFILE_OPEN_ANY_LOCALE for listfile. This will allow us to load - // the listfile even if there is only non-neutral version of the listfile in the MPQ - dwSearchScope = SFILE_OPEN_ANY_LOCALE; - szListFile = LISTFILE_NAME; - } - - // Open the local/internal listfile - if(SFileOpenFileEx(hMpq, szListFile, dwSearchScope, &hListFile)) - { - TMPQArchive * ha = (TMPQArchive *)hMpq; - TMPQFile * hf = (TMPQFile *)hListFile; - - // Remember flags for (listfile) - if(hf->pFileEntry != NULL) - ha->dwFileFlags1 = hf->pFileEntry->dwFlags; - } - else - nError = GetLastError(); - // Allocate cache for one file block + pCache = (TListFileCache *)STORM_ALLOC(TListFileCache, 1); + if(pCache == NULL) + nError = ERROR_NOT_ENOUGH_MEMORY; + + // Clear the entire structure if(nError == ERROR_SUCCESS) { - pCache = (TListFileCache *)ALLOCMEM(TListFileCache, 1); - if(pCache == NULL) - nError = ERROR_NOT_ENOUGH_MEMORY; + memset(pCache, 0, sizeof(TListFileCache)); + pCache->hFile = hListFile; + + // Shall we allocate a mask? + if(szMask != NULL) + { + pCache->szMask = STORM_ALLOC(char, strlen(szMask) + 1); + if(pCache->szMask != NULL) + strcpy(pCache->szMask, szMask); + else + nError = ERROR_NOT_ENOUGH_MEMORY; + } } + // Initialize the file cache if(nError == ERROR_SUCCESS) { - // Initialize the file cache - memset(pCache, 0, sizeof(TListFileCache)); - pCache->dwFileSize = SFileGetFileSize(hListFile, NULL); - pCache->hFile = hListFile; + pCache->dwFileSize = SFileGetFileSize(pCache->hFile, NULL); // Fill the cache - SFileReadFile(hListFile, pCache->Buffer, CACHE_BUFFER_SIZE, &dwBytesRead, NULL); + SFileReadFile(pCache->hFile, pCache->Buffer, CACHE_BUFFER_SIZE, &dwBytesRead, NULL); if(dwBytesRead == 0) nError = GetLastError(); } - // Initialize the pointers + // Allocate pointers if(nError == ERROR_SUCCESS) { pCache->pBegin = - pCache->pPos = &pCache->Buffer[0]; + pCache->pPos = &pCache->Buffer[0]; pCache->pEnd = pCache->pBegin + dwBytesRead; } else { - SListFileFindClose((HANDLE)pCache); + FreeListFileCache(pCache); SetLastError(nError); pCache = NULL; } @@ -113,7 +116,7 @@ static DWORD ReloadListFileCache(TListFileCache * pCache) // Only do something if the cache is empty if(pCache->pPos >= pCache->pEnd) { - __TryReadBlock: + // __TryReadBlock: // Move the file position forward pCache->dwFilePos += CACHE_BUFFER_SIZE; @@ -131,14 +134,14 @@ static DWORD ReloadListFileCache(TListFileCache * pCache) // If we didn't read anything, it might mean that the block // of the file is not available (in case of partial MPQs). - // We sill skip it and try to read next block, until we reach - // the end of the file. + // We stop reading the file at this point, because the rest + // of the listfile is unreliable if(dwBytesRead == 0) - goto __TryReadBlock; + return 0; // Set the buffer pointers pCache->pBegin = - pCache->pPos = &pCache->Buffer[0]; + pCache->pPos = &pCache->Buffer[0]; pCache->pEnd = pCache->pBegin + dwBytesRead; } @@ -150,7 +153,7 @@ static size_t ReadListFileLine(TListFileCache * pCache, char * szLine, int nMaxC char * szLineBegin = szLine; char * szLineEnd = szLine + nMaxChars - 1; char * szExtraString = NULL; - + // Skip newlines, spaces, tabs and another non-printable stuff for(;;) { @@ -209,7 +212,7 @@ static size_t ReadListFileLine(TListFileCache * pCache, char * szLine, int nMaxC return (szLine - szLineBegin); } -static int CompareFileNodes(const void * p1, const void * p2) +static int CompareFileNodes(const void * p1, const void * p2) { char * szFileName1 = *(char **)p1; char * szFileName2 = *(char **)p2; @@ -218,8 +221,8 @@ static int CompareFileNodes(const void * p1, const void * p2) } static int WriteListFileLine( - TMPQFile * hf, - const char * szLine) + TMPQFile * hf, + const char * szLine) { char szNewLine[2] = {0x0D, 0x0A}; size_t nLength = strlen(szLine); @@ -238,15 +241,30 @@ static int WriteListFileLine( // Adds a name into the list of all names. For each locale in the MPQ, // one entry will be created // If the file name is already there, does nothing. -int SListFileCreateNodeForAllLocales(TMPQArchive * ha, const char * szFileName) +static int SListFileCreateNodeForAllLocales(TMPQArchive * ha, const char * szFileName) { TMPQHeader * pHeader = ha->pHeader; TFileEntry * pFileEntry; TMPQHash * pFirstHash; TMPQHash * pHash; + bool bNameEntryCreated = false; + + // If we have HET table, use that one + if(ha->pHetTable != NULL) + { + pFileEntry = GetFileEntryAny(ha, szFileName); + if(pFileEntry != NULL) + { + // Allocate file name for the file entry + AllocateFileName(pFileEntry, szFileName); + bNameEntryCreated = true; + } + + return ERROR_SUCCESS; + } // If we have hash table, we use it - if(ha->pHashTable != NULL) + if(bNameEntryCreated == false && ha->pHashTable != NULL) { // Look for the first hash table entry for the file pFirstHash = pHash = GetFirstHashEntry(ha, szFileName); @@ -259,26 +277,12 @@ int SListFileCreateNodeForAllLocales(TMPQArchive * ha, const char * szFileName) { // Allocate file name for the file entry AllocateFileName(ha->pFileTable + pHash->dwBlockIndex, szFileName); + bNameEntryCreated = true; } // Now find the next language version of the file pHash = GetNextHashEntry(ha, pFirstHash, pHash); } - - return ERROR_SUCCESS; - } - - // If we have HET table, use that one - if(ha->pHetTable != NULL) - { - pFileEntry = GetFileEntryAny(ha, szFileName); - if(pFileEntry != NULL) - { - // Allocate file name for the file entry - AllocateFileName(pFileEntry, szFileName); - } - - return ERROR_SUCCESS; } return ERROR_CAN_NOT_COMPLETE; @@ -298,7 +302,7 @@ int SListFileSaveToMpq(TMPQArchive * ha) int nError = ERROR_SUCCESS; // Allocate the table for sorting listfile - SortTable = ALLOCMEM(char*, ha->dwFileTableSize); + SortTable = STORM_ALLOC(char*, ha->dwFileTableSize); if(SortTable == NULL) return ERROR_NOT_ENOUGH_MEMORY; @@ -330,7 +334,7 @@ int SListFileSaveToMpq(TMPQArchive * ha) // Count the 0-th item dwFileSize += (DWORD)strlen(SortTable[0]) + 2; szPrevItem = SortTable[0]; - + // Count all next items for(i = 1; i < nFileNodes; i++) { @@ -342,14 +346,17 @@ int SListFileSaveToMpq(TMPQArchive * ha) } } + // Determine the flags for (listfile) + if(ha->dwFileFlags1 == 0) + ha->dwFileFlags1 = GetDefaultSpecialFileFlags(ha, dwFileSize); + // Create the listfile in the MPQ - assert(ha->dwFileFlags1 != 0); nError = SFileAddFile_Init(ha, LISTFILE_NAME, - NULL, - dwFileSize, - LANG_NEUTRAL, - ha->dwFileFlags1, - &hf); + 0, + dwFileSize, + LANG_NEUTRAL, + ha->dwFileFlags1 | MPQ_FILE_REPLACEEXISTING, + &hf); // Add all file names if(nError == ERROR_SUCCESS) { @@ -374,27 +381,126 @@ int SListFileSaveToMpq(TMPQArchive * ha) // Create the listfile in the MPQ dwFileSize = (DWORD)strlen(LISTFILE_NAME) + 2; nError = SFileAddFile_Init(ha, LISTFILE_NAME, - NULL, - dwFileSize, - LANG_NEUTRAL, - MPQ_FILE_ENCRYPTED | MPQ_FILE_COMPRESS | MPQ_FILE_REPLACEEXISTING, - &hf); + 0, + dwFileSize, + LANG_NEUTRAL, + MPQ_FILE_ENCRYPTED | MPQ_FILE_COMPRESS | MPQ_FILE_REPLACEEXISTING, + &hf); // Just add "(listfile)" there if(nError == ERROR_SUCCESS) + { WriteListFileLine(hf, LISTFILE_NAME); + } } // Finalize the file in the MPQ if(hf != NULL) { SFileAddFile_Finish(hf); - ha->dwFlags |= MPQ_FLAG_LISTFILE_VALID; } - + // Free buffers + if(nError == ERROR_SUCCESS) + ha->dwFlags &= ~MPQ_FLAG_INV_LISTFILE; if(SortTable != NULL) - FREEMEM(SortTable); + STORM_FREE(SortTable); + return nError; +} + +static int SFileAddArbitraryListFile( + TMPQArchive * ha, + HANDLE hListFile) +{ + TListFileCache * pCache = NULL; + size_t nLength; + char szFileName[MAX_PATH]; + int nError = ERROR_SUCCESS; + + // Create the listfile cache for that file + pCache = CreateListFileCache(hListFile, NULL); + if(pCache == NULL) + nError = GetLastError(); + + // Load the node list. Add the node for every locale in the archive + if(nError == ERROR_SUCCESS) + { + while((nLength = ReadListFileLine(pCache, szFileName, sizeof(szFileName))) > 0) + SListFileCreateNodeForAllLocales(ha, szFileName); + pCache->hFile = NULL; + } + + // Delete the cache + if(pCache != NULL) + FreeListFileCache(pCache); + return nError; +} + +static int SFileAddExternalListFile( + TMPQArchive * ha, + HANDLE hMpq, + const char * szListFile) +{ + HANDLE hListFile; + int nError = ERROR_SUCCESS; + + // Open the external list file + if(SFileOpenFileEx(hMpq, szListFile, SFILE_OPEN_LOCAL_FILE, &hListFile)) + { + // Add the data from the listfile to MPQ + nError = SFileAddArbitraryListFile(ha, hListFile); + SFileCloseFile(hListFile); + } + return nError; +} + +static int SFileAddInternalListFile( + TMPQArchive * ha, + HANDLE hMpq) +{ + TMPQArchive * haMpq = (TMPQArchive *)hMpq; + TMPQHash * pFirstHash; + TMPQHash * pHash; + HANDLE hListFile; + LCID lcSaveLocale = lcFileLocale; + int nError = ERROR_SUCCESS; + + // If there is hash table, we need to support multiple listfiles + // with different locales (BrooDat.mpq) + if(haMpq->pHashTable != NULL) + { + pFirstHash = pHash = GetFirstHashEntry(haMpq, LISTFILE_NAME); + while(nError == ERROR_SUCCESS && pHash != NULL) + { + // Set the prefered locale to that from list file + SFileSetLocale(pHash->lcLocale); + if(SFileOpenFileEx(hMpq, LISTFILE_NAME, 0, &hListFile)) + { + // Add the data from the listfile to MPQ + nError = SFileAddArbitraryListFile(ha, hListFile); + SFileCloseFile(hListFile); + } + + // Restore the original locale + SFileSetLocale(lcSaveLocale); + + // Move to the next hash + pHash = GetNextHashEntry(haMpq, pFirstHash, pHash); + } + } + else + { + // Open the external list file + if(SFileOpenFileEx(hMpq, LISTFILE_NAME, 0, &hListFile)) + { + // Add the data from the listfile to MPQ + // The function also closes the listfile handle + nError = SFileAddArbitraryListFile(ha, hListFile); + SFileCloseFile(hListFile); + } + } + + // Return the result of the operation return nError; } @@ -402,29 +508,18 @@ int SListFileSaveToMpq(TMPQArchive * ha) // File functions // Adds a listfile into the MPQ archive. -// Note that the function does not remove the int WINAPI SFileAddListFile(HANDLE hMpq, const char * szListFile) { - TListFileCache * pCache = NULL; TMPQArchive * ha = (TMPQArchive *)hMpq; - char szFileName[MAX_PATH]; - size_t nLength = 0; int nError = ERROR_SUCCESS; // Add the listfile for each MPQ in the patch chain while(ha != NULL) { - // Load the listfile to cache - pCache = CreateListFileCache(hMpq, szListFile); - if(pCache == NULL) - { - nError = GetLastError(); - break; - } - - // Load the node list. Add the node for every locale in the archive - while((nLength = ReadListFileLine(pCache, szFileName, sizeof(szFileName))) > 0) - SListFileCreateNodeForAllLocales(ha, szFileName); + if(szListFile != NULL) + SFileAddExternalListFile(ha, hMpq, szListFile); + else + SFileAddInternalListFile(ha, hMpq); // Also, add three special files to the listfile: // (listfile) itself, (attributes) and (signature) @@ -432,9 +527,6 @@ int WINAPI SFileAddListFile(HANDLE hMpq, const char * szListFile) SListFileCreateNodeForAllLocales(ha, SIGNATURE_NAME); SListFileCreateNodeForAllLocales(ha, ATTRIBUTES_NAME); - // Delete the cache - SListFileFindClose((HANDLE)pCache); - // Move to the next archive in the chain ha = ha->haPatch; } @@ -443,30 +535,38 @@ int WINAPI SFileAddListFile(HANDLE hMpq, const char * szListFile) } //----------------------------------------------------------------------------- -// Passing through the listfile +// Enumerating files in listfile HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const char * szMask, SFILE_FIND_DATA * lpFindFileData) { TListFileCache * pCache = NULL; + HANDLE hListFile; size_t nLength = 0; + DWORD dwSearchScope = SFILE_OPEN_LOCAL_FILE; int nError = ERROR_SUCCESS; // Initialize the structure with zeros memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA)); - // Load the listfile to cache - pCache = CreateListFileCache(hMpq, szListFile); - if(pCache == NULL) + // If the szListFile is NULL, it means we have to open internal listfile + if(szListFile == NULL) + { + // Use SFILE_OPEN_ANY_LOCALE for listfile. This will allow us to load + // the listfile even if there is only non-neutral version of the listfile in the MPQ + dwSearchScope = SFILE_OPEN_ANY_LOCALE; + szListFile = LISTFILE_NAME; + } + + // Open the local/internal listfile + if(!SFileOpenFileEx(hMpq, szListFile, dwSearchScope, &hListFile)) nError = GetLastError(); - // Allocate file mask - if(nError == ERROR_SUCCESS && szMask != NULL) + // Load the listfile to cache + if(nError == ERROR_SUCCESS) { - pCache->szMask = ALLOCMEM(char, strlen(szMask) + 1); - if(pCache->szMask != NULL) - strcpy(pCache->szMask, szMask); - else - nError = ERROR_NOT_ENOUGH_MEMORY; + pCache = CreateListFileCache(hListFile, szMask); + if(pCache == NULL) + nError = GetLastError(); } // Perform file search @@ -484,7 +584,7 @@ HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const // If some mask entered, check it if(CheckWildCard(lpFindFileData->cFileName, pCache->szMask)) - break; + break; } } @@ -492,10 +592,10 @@ HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const if(nError != ERROR_SUCCESS) { memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA)); - SListFileFindClose((HANDLE)pCache); - pCache = NULL; + FreeListFileCache(pCache); SetLastError(nError); + pCache = NULL; } return (HANDLE)pCache; } @@ -532,19 +632,6 @@ bool WINAPI SListFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData bool WINAPI SListFileFindClose(HANDLE hFind) { - TListFileCache * pCache = (TListFileCache *)hFind; - - if(pCache != NULL) - { - if(pCache->hFile != NULL) - SFileCloseFile(pCache->hFile); - if(pCache->szMask != NULL) - FREEMEM(pCache->szMask); - - FREEMEM(pCache); - return true; - } - - return false; + return FreeListFileCache((TListFileCache *)hFind); } diff --git a/dep/StormLib/src/SFileOpenArchive.cpp b/dep/StormLib/src/SFileOpenArchive.cpp index b38beec0019..b44c9909c2f 100644 --- a/dep/StormLib/src/SFileOpenArchive.cpp +++ b/dep/StormLib/src/SFileOpenArchive.cpp @@ -14,7 +14,6 @@ /*****************************************************************************/ #define __STORMLIB_SELF__ -#define __INCLUDE_CRYPTOGRAPHY__ #include "StormLib.h" #include "StormCommon.h" @@ -33,6 +32,34 @@ static bool IsAviFile(void * pvFileBegin) return (DwordValue0 == 0x46464952 && DwordValue2 == 0x20495641 && DwordValue3 == 0x5453494C); } +static TFileBitmap * CreateFileBitmap(TMPQArchive * ha, TMPQBitmap * pMpqBitmap, bool bFileIsComplete) +{ + TFileBitmap * pBitmap; + size_t nLength; + + // Calculate the length of the bitmap in blocks and in bytes + nLength = (size_t)(((ha->pHeader->ArchiveSize64 - 1) / pMpqBitmap->dwBlockSize) + 1); + nLength = (size_t)(((nLength - 1) / 8) + 1); + + // Allocate the file bitmap + pBitmap = (TFileBitmap *)STORM_ALLOC(BYTE, sizeof(TFileBitmap) + nLength); + if(pBitmap != NULL) + { + // Fill the structure + pBitmap->StartOffset = ha->MpqPos; + pBitmap->EndOffset = ha->MpqPos + ha->pHeader->ArchiveSize64; + pBitmap->IsComplete = bFileIsComplete ? 1 : 0; + pBitmap->BitmapSize = (DWORD)nLength; + pBitmap->BlockSize = pMpqBitmap->dwBlockSize; + pBitmap->Reserved = 0; + + // Copy the file bitmap + memcpy((pBitmap + 1), (pMpqBitmap + 1), nLength); + } + + return pBitmap; +} + // This function gets the right positions of the hash table and the block table. static int VerifyMpqTablePositions(TMPQArchive * ha, ULONGLONG FileSize) { @@ -92,19 +119,6 @@ static int VerifyMpqTablePositions(TMPQArchive * ha, ULONGLONG FileSize) // SFileGetLocale and SFileSetLocale // Set the locale for all newly opened files -DWORD WINAPI SFileGetGlobalFlags() -{ - return dwGlobalFlags; -} - -DWORD WINAPI SFileSetGlobalFlags(DWORD dwNewFlags) -{ - DWORD dwOldFlags = dwGlobalFlags; - - dwGlobalFlags = dwNewFlags; - return dwOldFlags; -} - LCID WINAPI SFileGetLocale() { return lcFileLocale; @@ -124,18 +138,17 @@ LCID WINAPI SFileSetLocale(LCID lcNewLocale) // dwFlags - See MPQ_OPEN_XXX in StormLib.h // phMpq - Pointer to store open archive handle -//extern "C" void wow_SFileVerifyMpqHeaderMD5(TMPQHeader * ha); - bool WINAPI SFileOpenArchive( - const char * szMpqName, - DWORD dwPriority, - DWORD dwFlags, - HANDLE * phMpq) + const TCHAR * szMpqName, + DWORD dwPriority, + DWORD dwFlags, + HANDLE * phMpq) { TFileStream * pStream = NULL; // Open file stream TMPQArchive * ha = NULL; // Archive handle + TFileEntry * pFileEntry; ULONGLONG FileSize = 0; // Size of the file - int nError = ERROR_SUCCESS; + int nError = ERROR_SUCCESS; // Verify the parameters if(szMpqName == NULL || *szMpqName == 0 || phMpq == NULL) @@ -148,25 +161,17 @@ bool WINAPI SFileOpenArchive( // Open the MPQ archive file if(nError == ERROR_SUCCESS) { - if(!(dwFlags & MPQ_OPEN_ENCRYPTED)) - { - pStream = FileStream_OpenFile(szMpqName, (dwFlags & MPQ_OPEN_READ_ONLY) ? false : true); - if(pStream == NULL) - nError = GetLastError(); - } - else - { - pStream = FileStream_OpenEncrypted(szMpqName); - if(pStream == NULL) - nError = GetLastError(); - } + // Initialize the stream + pStream = FileStream_OpenFile(szMpqName, (dwFlags & STREAM_OPTIONS_MASK)); + if(pStream == NULL) + nError = GetLastError(); } - + // Allocate the MPQhandle if(nError == ERROR_SUCCESS) { - FileStream_GetSize(pStream, FileSize); - if((ha = ALLOCMEM(TMPQArchive, 1)) == NULL) + FileStream_GetSize(pStream, &FileSize); + if((ha = STORM_ALLOC(TMPQArchive, 1)) == NULL) nError = ERROR_NOT_ENOUGH_MEMORY; } @@ -178,7 +183,7 @@ bool WINAPI SFileOpenArchive( pStream = NULL; // Remember if the archive is open for write - if(ha->pStream->StreamFlags & (STREAM_FLAG_READ_ONLY | STREAM_FLAG_ENCRYPTED_FILE)) + if(FileStream_IsReadOnly(ha->pStream)) ha->dwFlags |= MPQ_FLAG_READ_ONLY; // Also remember if we shall check sector CRCs when reading file @@ -244,7 +249,7 @@ bool WINAPI SFileOpenArchive( // Now convert the header to version 4 BSWAP_TMPQHEADER(ha->pHeader); - ConvertMpqHeaderToFormat4(ha, FileSize, dwFlags); + nError = ConvertMpqHeaderToFormat4(ha, FileSize, dwFlags); break; } @@ -261,7 +266,7 @@ bool WINAPI SFileOpenArchive( if(nError == ERROR_SUCCESS) { // Dump the header -// DumpMpqHeader(ha->pHeader); + // DumpMpqHeader(ha->pHeader); // W3x Map Protectors use the fact that War3's Storm.dll ignores the MPQ user data, // and probably ignores the MPQ format version as well. The trick is to @@ -279,10 +284,6 @@ bool WINAPI SFileOpenArchive( if(dwFlags & (MPQ_OPEN_NO_LISTFILE | MPQ_OPEN_NO_ATTRIBUTES)) ha->dwFlags |= MPQ_FLAG_READ_ONLY; - // Set the default file flags for (listfile) and (attributes) - ha->dwFileFlags1 = - ha->dwFileFlags2 = MPQ_FILE_ENCRYPTED | MPQ_FILE_COMPRESS | MPQ_FILE_REPLACEEXISTING; - // Set the size of file sector ha->dwSectorSize = (0x200 << ha->pHeader->wSectorSize); @@ -290,6 +291,24 @@ bool WINAPI SFileOpenArchive( nError = VerifyMpqTablePositions(ha, FileSize); } + // Check if the MPQ has data bitmap. If yes, we can verify if the MPQ is complete + if(nError == ERROR_SUCCESS && ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_4) + { + TFileBitmap * pBitmap; + bool bFileIsComplete = true; + + LoadMpqDataBitmap(ha, FileSize, &bFileIsComplete); + if(ha->pBitmap != NULL && bFileIsComplete == false) + { + // Convert the MPQ bitmap to the file bitmap + pBitmap = CreateFileBitmap(ha, ha->pBitmap, bFileIsComplete); + + // Set the data bitmap into the file stream for additional checks + FileStream_SetBitmap(ha->pStream, pBitmap); + ha->dwFlags |= MPQ_FLAG_READ_ONLY; + } + } + // Read the hash table. Ignore the result, as hash table is no longer required // Read HET table. Ignore the result, as HET table is no longer required if(nError == ERROR_SUCCESS) @@ -308,7 +327,6 @@ bool WINAPI SFileOpenArchive( if(nError == ERROR_SUCCESS && (ha->dwFlags & MPQ_FLAG_PROTECTED) == 0) { TFileEntry * pFileTableEnd = ha->pFileTable + ha->pHeader->dwBlockTableSize; - TFileEntry * pFileEntry = ha->pFileTable; // ULONGLONG ArchiveSize = 0; ULONGLONG RawFilePos; @@ -338,8 +356,8 @@ bool WINAPI SFileOpenArchive( } // Also, we remember end of the file -// if(RawFilePos > ArchiveSize) -// ArchiveSize = RawFilePos; + // if(RawFilePos > ArchiveSize) + // ArchiveSize = RawFilePos; } } } @@ -347,6 +365,11 @@ bool WINAPI SFileOpenArchive( // Load the internal listfile and include it to the file table if(nError == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_LISTFILE) == 0) { + // Save the flags for (listfile) + pFileEntry = GetFileEntryLocale(ha, LISTFILE_NAME, LANG_NEUTRAL); + if(pFileEntry != NULL) + ha->dwFileFlags1 = pFileEntry->dwFlags; + // Ignore result of the operation. (listfile) is optional. SFileAddListFile((HANDLE)ha, NULL); } @@ -354,6 +377,11 @@ bool WINAPI SFileOpenArchive( // Load the "(attributes)" file and merge it to the file table if(nError == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_ATTRIBUTES) == 0) { + // Save the flags for (attributes) + pFileEntry = GetFileEntryLocale(ha, ATTRIBUTES_NAME, LANG_NEUTRAL); + if(pFileEntry != NULL) + ha->dwFileFlags2 = pFileEntry->dwFlags; + // Ignore result of the operation. (attributes) is optional. SAttrLoadAttributes(ha); } @@ -371,6 +399,16 @@ bool WINAPI SFileOpenArchive( return (nError == ERROR_SUCCESS); } +//----------------------------------------------------------------------------- +// SFileGetArchiveBitmap + +bool WINAPI SFileGetArchiveBitmap(HANDLE hMpq, TFileBitmap * pBitmap, DWORD Length, LPDWORD LengthNeeded) +{ + TMPQArchive * ha = (TMPQArchive *)hMpq; + + return FileStream_GetBitmap(ha->pStream, pBitmap, Length, LengthNeeded); +} + //----------------------------------------------------------------------------- // bool SFileFlushArchive(HANDLE hMpq) // @@ -393,21 +431,25 @@ bool WINAPI SFileFlushArchive(HANDLE hMpq) return false; } - // If the archive has been changed, update the changes on the disk drive. - // Save listfile (if created), attributes (if created) and also save MPQ tables. - if(ha->dwFlags & MPQ_FLAG_CHANGED) + // If the (listfile) has been invalidated, save it + if(ha->dwFlags & MPQ_FLAG_INV_LISTFILE) { - // Save the (listfile) nError = SListFileSaveToMpq(ha); if(nError != ERROR_SUCCESS) nResultError = nError; + } - // Save the (attributes) + // If the (attributes) has been invalidated, save it + if(ha->dwFlags & MPQ_FLAG_INV_ATTRIBUTES) + { nError = SAttrFileSaveToMpq(ha); if(nError != ERROR_SUCCESS) nResultError = nError; + } - // Save HET table, BET table, hash table, block table, hi-block table + // Save HET table, BET table, hash table, block table, hi-block table + if(ha->dwFlags & MPQ_FLAG_CHANGED) + { nError = SaveMPQTables(ha); if(nError != ERROR_SUCCESS) nResultError = nError; diff --git a/dep/StormLib/src/SFileOpenFileEx.cpp b/dep/StormLib/src/SFileOpenFileEx.cpp index f3118ad4584..4d2516af436 100644 --- a/dep/StormLib/src/SFileOpenFileEx.cpp +++ b/dep/StormLib/src/SFileOpenFileEx.cpp @@ -20,8 +20,21 @@ static bool OpenLocalFile(const char * szFileName, HANDLE * phFile) { TFileStream * pStream; TMPQFile * hf = NULL; - - pStream = FileStream_OpenFile(szFileName, false); + + // We have to convert the local file name to UNICODE, if needed +#ifdef _UNICODE + TCHAR szFileNameT[MAX_PATH]; + int i; + + for(i = 0; szFileName[i] != 0; i++) + szFileNameT[i] = szFileName[i]; + szFileNameT[i] = 0; + pStream = FileStream_OpenFile(szFileNameT, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); + +#else + pStream = FileStream_OpenFile(szFileName, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); +#endif + if(pStream != NULL) { // Allocate and initialize file handle @@ -47,8 +60,8 @@ bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, DWORD dwReserved, HAN TMPQArchive * ha = (TMPQArchive *)hMpq; TMPQFile * hfPatch; // Pointer to patch file TMPQFile * hfBase = NULL; // Pointer to base open file - TMPQFile * hfLast; // The highest file in the chain that is not patch file - TMPQFile * hf; + TMPQFile * hfLast = NULL; // The highest file in the chain that is not patch file + TMPQFile * hf = NULL; HANDLE hPatchFile; char szPatchFileName[MAX_PATH]; @@ -60,17 +73,17 @@ bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, DWORD dwReserved, HAN { // Construct the name of the patch file strcpy(szPatchFileName, ha->szPatchPrefix); - strcat(szPatchFileName, szFileName); + strcpy(&szPatchFileName[ha->cchPatchPrefix], szFileName); if(SFileOpenFileEx((HANDLE)ha, szPatchFileName, SFILE_OPEN_FROM_MPQ, (HANDLE *)&hfBase)) { - // - // The following scenario is possible: - // 1) Base MPQ file doesn't contain the desired file - // 2) First patch MPQ contains the file with MPQ_FILE_PATCH_FILE - // 3) Second patch contains full version of the file - // + // The file must be a base file, i.e. without MPQ_FILE_PATCH_FILE + if((hfBase->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0) + { + hf = hfLast = hfBase; + break; + } - break; + SFileCloseFile((HANDLE)hfBase); } // Move to the next file in the patch chain @@ -78,36 +91,24 @@ bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, DWORD dwReserved, HAN } // If we couldn't find the file in any of the patches, it doesn't exist - hf = hfLast = hfBase; if(hf == NULL) { SetLastError(ERROR_FILE_NOT_FOUND); return false; } - // At this point, we require that the open file is not a patch - if(hf->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) - { - FreeMPQFile(hf); - SetLastError(ERROR_BASE_FILE_MISSING); - return false; - } - - // Move to the patch MPQ - ha = ha->haPatch; - // Now keep going in the patch chain and open every patch file that is there - while(ha != NULL) + for(ha = ha->haPatch; ha != NULL; ha = ha->haPatch) { // Construct patch file name strcpy(szPatchFileName, ha->szPatchPrefix); - strcat(szPatchFileName, szFileName); + strcpy(&szPatchFileName[ha->cchPatchPrefix], szFileName); if(SFileOpenFileEx((HANDLE)ha, szPatchFileName, SFILE_OPEN_FROM_MPQ, &hPatchFile)) { // Remember the new version hfPatch = (TMPQFile *)hPatchFile; - // If we encountered a full replacement of the file, + // If we encountered a full replacement of the file, // we have to remember the highest full file if((hfPatch->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0) hfLast = hfPatch; @@ -116,9 +117,6 @@ bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, DWORD dwReserved, HAN hf->hfPatchFile = hfPatch; hf = hfPatch; } - - // Move to the next patch in the chain - ha = ha->haPatch; } // Now we need to free all files that are below the highest unpatched version @@ -145,61 +143,55 @@ bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, DWORD dwReserved, HAN /*****************************************************************************/ //----------------------------------------------------------------------------- -// SFileEnumLocales enums all locale versions within MPQ. +// SFileEnumLocales enums all locale versions within MPQ. // Functions fills all available language identifiers on a file into the buffer // pointed by plcLocales. There must be enough entries to copy the localed, // otherwise the function returns ERROR_INSUFFICIENT_BUFFER. int WINAPI SFileEnumLocales( - HANDLE hMpq, - const char * szFileName, - LCID * plcLocales, - LPDWORD pdwMaxLocales, - DWORD dwSearchScope) + HANDLE hMpq, + const char * szFileName, + LCID * plcLocales, + LPDWORD pdwMaxLocales, + DWORD dwSearchScope) { TMPQArchive * ha = (TMPQArchive *)hMpq; + TFileEntry * pFileEntry; TMPQHash * pFirstHash; TMPQHash * pHash; + DWORD dwFileIndex = 0; DWORD dwLocales = 0; // Test the parameters if(!IsValidMpqHandle(ha)) return ERROR_INVALID_HANDLE; - if(pdwMaxLocales == NULL) - return ERROR_INVALID_PARAMETER; - if(dwSearchScope == SFILE_OPEN_BY_INDEX && (DWORD_PTR)szFileName > ha->pHeader->dwBlockTableSize) + if(szFileName == NULL || *szFileName == 0) return ERROR_INVALID_PARAMETER; - if(dwSearchScope != SFILE_OPEN_BY_INDEX && *szFileName == 0) + if(pdwMaxLocales == NULL) return ERROR_INVALID_PARAMETER; + // Keep compiler happy + dwSearchScope = dwSearchScope; + // Parse hash table entries for all locales - if(dwSearchScope == SFILE_OPEN_FROM_MPQ) + if(!IsPseudoFileName(szFileName, &dwFileIndex)) { + // Calculate the number of locales pFirstHash = pHash = GetFirstHashEntry(ha, szFileName); while(pHash != NULL) { dwLocales++; pHash = GetNextHashEntry(ha, pFirstHash, pHash); } - } - else - { - // For nameless access, always return 1 locale -// pHash = GetFileEntryByIndex(ha, (DWORD)(DWORD_PTR)szFileName); -// if(pHash != NULL) -// dwLocales++; - } - // Test if there is enough space to copy the locales - if(*pdwMaxLocales < dwLocales) - { - *pdwMaxLocales = dwLocales; - return ERROR_INSUFFICIENT_BUFFER; - } + // Test if there is enough space to copy the locales + if(*pdwMaxLocales < dwLocales) + { + *pdwMaxLocales = dwLocales; + return ERROR_INSUFFICIENT_BUFFER; + } - // Now find all locales - if(dwSearchScope == SFILE_OPEN_FROM_MPQ) - { + // Enum the locales pFirstHash = pHash = GetFirstHashEntry(ha, szFileName); while(pHash != NULL) { @@ -209,10 +201,18 @@ int WINAPI SFileEnumLocales( } else { + // There must be space for 1 locale + if(*pdwMaxLocales < 1) + { + *pdwMaxLocales = 1; + return ERROR_INSUFFICIENT_BUFFER; + } + // For nameless access, always return 1 locale -// pHash = GetFileEntryByIndex(ha, (DWORD)(DWORD_PTR)szFileName); -// if(pHash != NULL) -// *plcLocales++ = pHash->lcLocale; + pFileEntry = GetFileEntryByIndex(ha, dwFileIndex); + pHash = ha->pHashTable + pFileEntry->dwHashIndex; + *plcLocales = pHash->lcLocale; + dwLocales = 1; } // Give the caller the total number of found locales @@ -229,26 +229,54 @@ int WINAPI SFileEnumLocales( bool WINAPI SFileHasFile(HANDLE hMpq, const char * szFileName) { TMPQArchive * ha = (TMPQArchive *)hMpq; + TFileEntry * pFileEntry; + DWORD dwFlagsToCheck = MPQ_FILE_EXISTS; + DWORD dwFileIndex = 0; + char szPatchFileName[MAX_PATH]; + bool bIsPseudoName; int nError = ERROR_SUCCESS; if(!IsValidMpqHandle(ha)) nError = ERROR_INVALID_HANDLE; - if(*szFileName == 0) + if(szFileName == NULL || *szFileName == 0) nError = ERROR_INVALID_PARAMETER; // Prepare the file opening if(nError == ERROR_SUCCESS) { - if(GetFileEntryLocale(ha, szFileName, lcFileLocale) == NULL) + // Different processing for pseudo-names + bIsPseudoName = IsPseudoFileName(szFileName, &dwFileIndex); + + // Walk through the MPQ and all patches + while(ha != NULL) { - nError = ERROR_FILE_NOT_FOUND; + // Verify presence of the file + pFileEntry = (bIsPseudoName == false) ? GetFileEntryLocale(ha, szFileName, lcFileLocale) + : GetFileEntryByIndex(ha, dwFileIndex); + // Verify the file flags + if(pFileEntry != NULL && (pFileEntry->dwFlags & dwFlagsToCheck) == MPQ_FILE_EXISTS) + return true; + + // If this is patched archive, go to the patch + dwFlagsToCheck = MPQ_FILE_EXISTS | MPQ_FILE_PATCH_FILE; + ha = ha->haPatch; + + // Prepare the patched file name + if(ha != NULL) + { + strcpy(szPatchFileName, ha->szPatchPrefix); + strcat(szPatchFileName, szFileName); + szFileName = szPatchFileName; + } } + + // Not found, sorry + nError = ERROR_FILE_NOT_FOUND; } // Cleanup - if(nError != ERROR_SUCCESS) - SetLastError(nError); - return (nError == ERROR_SUCCESS); + SetLastError(nError); + return false; } @@ -265,7 +293,8 @@ bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearch TMPQArchive * ha = (TMPQArchive *)hMpq; TFileEntry * pFileEntry = NULL; TMPQFile * hf = NULL; - DWORD dwBlockIndex = 0; // Found table index + DWORD dwFileIndex = 0; + bool bOpenByIndex = false; int nError = ERROR_SUCCESS; // Don't accept NULL pointer to file handle @@ -277,82 +306,67 @@ bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearch { switch(dwSearchScope) { - case SFILE_OPEN_PATCHED_FILE: - - // We want to open the updated version of the file - return OpenPatchedFile(hMpq, szFileName, 0, phFile); - - case SFILE_OPEN_FROM_MPQ: - - if(!IsValidMpqHandle(ha)) - { - nError = ERROR_INVALID_HANDLE; - break; - } - - if(szFileName == NULL || *szFileName == 0) - { - nError = ERROR_INVALID_PARAMETER; - break; - } - - // First of all, check the name as-is - pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale); - if(pFileEntry != NULL) - break; + case SFILE_OPEN_PATCHED_FILE: - // If the file doesn't exist in the MPQ, check file pseudo-name ("FileXXXXXXXX.ext") - if(!IsPseudoFileName(szFileName, &dwBlockIndex)) - { - nError = ERROR_FILE_NOT_FOUND; - break; - } + // We want to open the updated version of the file + return OpenPatchedFile(hMpq, szFileName, 0, phFile); - // Set the file name to the file index and fall through - szFileName = (const char *)(DWORD_PTR)dwBlockIndex; - dwSearchScope = SFILE_OPEN_BY_INDEX; - // No break here, fall through. + case SFILE_OPEN_FROM_MPQ: - case SFILE_OPEN_BY_INDEX: + if(!IsValidMpqHandle(ha)) + { + nError = ERROR_INVALID_HANDLE; + break; + } - if(!IsValidMpqHandle(ha)) - { - nError = ERROR_INVALID_HANDLE; - break; - } + if(szFileName == NULL || *szFileName == 0) + { + nError = ERROR_INVALID_PARAMETER; + break; + } - // Set handle size to be sizeof(TMPQFile) + length of FileXXXXXXXX.xxx - pFileEntry = GetFileEntryByIndex(ha, (DWORD)(DWORD_PTR)szFileName); + // First of all, check the name as-is + if(!IsPseudoFileName(szFileName, &dwFileIndex)) + { + pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale); if(pFileEntry == NULL) nError = ERROR_FILE_NOT_FOUND; - break; - - case SFILE_OPEN_ANY_LOCALE: - - // This open option is reserved for opening MPQ internal listfile. - // No argument validation. Tries to open file with neutral locale first, - // then any other available. - dwSearchScope = SFILE_OPEN_FROM_MPQ; - pFileEntry = GetFileEntryAny(ha, szFileName); + } + else + { + bOpenByIndex = true; + pFileEntry = GetFileEntryByIndex(ha, dwFileIndex); if(pFileEntry == NULL) nError = ERROR_FILE_NOT_FOUND; - break; - - case SFILE_OPEN_LOCAL_FILE: + } + break; - if(szFileName == NULL || *szFileName == 0) - { - nError = ERROR_INVALID_PARAMETER; - break; - } + case SFILE_OPEN_ANY_LOCALE: - return OpenLocalFile(szFileName, phFile); + // This open option is reserved for opening MPQ internal listfile. + // No argument validation. Tries to open file with neutral locale first, + // then any other available. + dwSearchScope = SFILE_OPEN_FROM_MPQ; + pFileEntry = GetFileEntryAny(ha, szFileName); + if(pFileEntry == NULL) + nError = ERROR_FILE_NOT_FOUND; + break; - default: + case SFILE_OPEN_LOCAL_FILE: - // Don't accept any other value + if(szFileName == NULL || *szFileName == 0) + { nError = ERROR_INVALID_PARAMETER; break; + } + + return OpenLocalFile(szFileName, phFile); + + default: + + // Don't accept any other value + nError = ERROR_INVALID_PARAMETER; + break; } // Quick return if something failed @@ -375,7 +389,7 @@ bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearch // Allocate file handle if(nError == ERROR_SUCCESS) { - if((hf = ALLOCMEM(TMPQFile, 1)) == NULL) + if((hf = STORM_ALLOC(TMPQFile, 1)) == NULL) nError = ERROR_NOT_ENOUGH_MEMORY; } @@ -395,9 +409,13 @@ bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearch if(ha->dwFlags & MPQ_FLAG_CHECK_SECTOR_CRC) hf->bCheckSectorCRCs = true; - // Decrypt file key. Cannot be used if the file is given by index - if(dwSearchScope == SFILE_OPEN_FROM_MPQ) + // If we know the real file name, copy it to the file entry + if(bOpenByIndex == false) { + // If there is no file name yet, allocate it + AllocateFileName(pFileEntry, szFileName); + + // If the file is encrypted, we should detect the file key if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) { hf->dwFileKey = DecryptFileKey(szFileName, @@ -408,7 +426,7 @@ bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearch } else { - // If the file is encrypted and not compressed, we cannot detect the file key + // Try to auto-detect the file name if(!SFileGetFileName(hf, NULL)) nError = GetLastError(); } @@ -438,7 +456,7 @@ bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearch bool WINAPI SFileCloseFile(HANDLE hFile) { TMPQFile * hf = (TMPQFile *)hFile; - + if(!IsValidFileHandle(hf)) { SetLastError(ERROR_INVALID_HANDLE); diff --git a/dep/StormLib/src/SFilePatchArchives.cpp b/dep/StormLib/src/SFilePatchArchives.cpp index 2bca9ae17e6..fe900486ce7 100644 --- a/dep/StormLib/src/SFilePatchArchives.cpp +++ b/dep/StormLib/src/SFilePatchArchives.cpp @@ -9,7 +9,6 @@ /*****************************************************************************/ #define __STORMLIB_SELF__ -#define __INCLUDE_CRYPTOGRAPHY__ #include "StormLib.h" #include "StormCommon.h" @@ -27,75 +26,41 @@ typedef struct _BLIZZARD_BSDIFF40_FILE //----------------------------------------------------------------------------- // Local functions -static bool CompareNameMask(const char * szMpqName, const char * szNameMask) -{ - for(;;) - { - // Compare character - switch(*szNameMask) - { - case 0: // End of the mask - return (*szMpqName == 0) ? true : false; - - case '#': // We are expecting a number - while('0' <= *szMpqName && *szMpqName <= '9') - szMpqName++; - szNameMask++; - break; - - default: - if(toupper(*szMpqName++) != toupper(*szNameMask++)) - return false; - break; - } - } -} - static bool GetDefaultPatchPrefix( - const char * szBaseMpqName, - const char * szPatchMpqName, - char * szBuffer) + const TCHAR * szBaseMpqName, + char * szBuffer) { - const char * szExtension; - const char * szDash; + const TCHAR * szExtension; + const TCHAR * szDash; - // Get the plain name of the patch MPQ - szPatchMpqName = GetPlainFileName(szPatchMpqName); + // Ensure that both names are plain names + szBaseMpqName = GetPlainFileNameT(szBaseMpqName); - // For files like "wow-update-13164.MPQ", the patch prefix - // is based on the base MPQ name - if(CompareNameMask(szPatchMpqName, "wow-update-#.mpq")) - { - // Patch prefix is for the Cataclysm MPQs, whose names - // are like "locale-enGB.MPQ" or "speech-enGB.MPQ" - szExtension = strrchr(szBaseMpqName, '.'); - szDash = strrchr(szBaseMpqName, '-'); - strcpy(szBuffer, "Base"); - - // If the length of the prefix doesn't match, use default one - if(szExtension != NULL && szDash != NULL && (szExtension - szDash) == 5) - { - // Copy the prefix - szBuffer[0] = szDash[1]; - szBuffer[1] = szDash[2]; - szBuffer[2] = szDash[3]; - szBuffer[3] = szDash[4]; - szBuffer[4] = 0; - } + // Patch prefix is for the Cataclysm MPQs, whose names + // are like "locale-enGB.MPQ" or "speech-enGB.MPQ" + szExtension = _tcsrchr(szBaseMpqName, _T('.')); + szDash = _tcsrchr(szBaseMpqName, _T('-')); + strcpy(szBuffer, "Base"); - return true; + // If the length of the prefix doesn't match, use default one + if(szExtension != NULL && szDash != NULL && (szExtension - szDash) == 5) + { + // Copy the prefix + szBuffer[0] = (char)szDash[1]; + szBuffer[1] = (char)szDash[2]; + szBuffer[2] = (char)szDash[3]; + szBuffer[3] = (char)szDash[4]; + szBuffer[4] = 0; } - // No patch prefix - *szBuffer = 0; - return false; + return true; } static void Decompress_RLE(LPBYTE pbDecompressed, DWORD cbDecompressed, LPBYTE pbCompressed, DWORD cbCompressed) { LPBYTE pbDecompressedEnd = pbDecompressed + cbDecompressed; LPBYTE pbCompressedEnd = pbCompressed + cbCompressed; - BYTE RepeatCount; + BYTE RepeatCount; BYTE OneByte; // Cut the initial DWORD from the compressed chunk @@ -109,7 +74,7 @@ static void Decompress_RLE(LPBYTE pbDecompressed, DWORD cbDecompressed, LPBYTE p while(pbCompressed < pbCompressedEnd && pbDecompressed < pbDecompressedEnd) { OneByte = *pbCompressed++; - + // Is it a repetition byte ? if(OneByte & 0x80) { @@ -134,7 +99,7 @@ static int LoadMpqPatch_COPY(TMPQFile * hf, TPatchHeader * pPatchHeader) int nError = ERROR_SUCCESS; // Allocate space for patch header and compressed data - hf->pPatchHeader = (TPatchHeader *)ALLOCMEM(BYTE, pPatchHeader->dwSizeOfPatchData); + hf->pPatchHeader = (TPatchHeader *)STORM_ALLOC(BYTE, pPatchHeader->dwSizeOfPatchData); if(hf->pPatchHeader == NULL) nError = ERROR_NOT_ENOUGH_MEMORY; @@ -148,7 +113,7 @@ static int LoadMpqPatch_COPY(TMPQFile * hf, TPatchHeader * pPatchHeader) pbPatchFile += sizeof(TPatchHeader); // Load the rest of the patch - if(!SFileReadFile((HANDLE)hf, pbPatchFile, pPatchHeader->dwSizeOfPatchData - sizeof(TPatchHeader))) + if(!SFileReadFile((HANDLE)hf, pbPatchFile, pPatchHeader->dwSizeOfPatchData - sizeof(TPatchHeader), NULL, NULL)) nError = GetLastError(); } @@ -166,7 +131,7 @@ static int LoadMpqPatch_BSD0(TMPQFile * hf, TPatchHeader * pPatchHeader) // Allocate space for compressed data cbCompressed = pPatchHeader->dwXfrmBlockSize - SIZE_OF_XFRM_HEADER; - pbCompressed = ALLOCMEM(BYTE, cbCompressed); + pbCompressed = STORM_ALLOC(BYTE, cbCompressed); if(pbCompressed == NULL) nError = ERROR_SUCCESS; @@ -174,7 +139,7 @@ static int LoadMpqPatch_BSD0(TMPQFile * hf, TPatchHeader * pPatchHeader) if(nError == ERROR_SUCCESS) { // Load the rest of the header - SFileReadFile((HANDLE)hf, pbCompressed, cbCompressed, &dwBytesRead); + SFileReadFile((HANDLE)hf, pbCompressed, cbCompressed, &dwBytesRead, NULL); if(dwBytesRead != cbCompressed) nError = ERROR_FILE_CORRUPT; } @@ -183,7 +148,7 @@ static int LoadMpqPatch_BSD0(TMPQFile * hf, TPatchHeader * pPatchHeader) if(nError == ERROR_SUCCESS) { cbDecompressed = pPatchHeader->dwSizeOfPatchData - sizeof(TPatchHeader); - hf->pPatchHeader = (TPatchHeader *)ALLOCMEM(BYTE, pPatchHeader->dwSizeOfPatchData); + hf->pPatchHeader = (TPatchHeader *)STORM_ALLOC(BYTE, pPatchHeader->dwSizeOfPatchData); if(hf->pPatchHeader == NULL) nError = ERROR_NOT_ENOUGH_MEMORY; } @@ -209,20 +174,20 @@ static int LoadMpqPatch_BSD0(TMPQFile * hf, TPatchHeader * pPatchHeader) // Free buffers and exit if(pbCompressed != NULL) - FREEMEM(pbCompressed); + STORM_FREE(pbCompressed); return nError; } static int ApplyMpqPatch_COPY( - TMPQFile * hf, - TPatchHeader * pPatchHeader) + TMPQFile * hf, + TPatchHeader * pPatchHeader) { LPBYTE pbNewFileData; DWORD cbNewFileData; // Allocate space for new file data cbNewFileData = pPatchHeader->dwXfrmBlockSize - SIZE_OF_XFRM_HEADER; - pbNewFileData = ALLOCMEM(BYTE, cbNewFileData); + pbNewFileData = STORM_ALLOC(BYTE, cbNewFileData); if(pbNewFileData == NULL) return ERROR_NOT_ENOUGH_MEMORY; @@ -230,7 +195,7 @@ static int ApplyMpqPatch_COPY( memcpy(pbNewFileData, (LPBYTE)pPatchHeader + sizeof(TPatchHeader), cbNewFileData); // Free the old file data - FREEMEM(hf->pbFileData); + STORM_FREE(hf->pbFileData); // Put the new file data there hf->pbFileData = pbNewFileData; @@ -239,8 +204,8 @@ static int ApplyMpqPatch_COPY( } static int ApplyMpqPatch_BSD0( - TMPQFile * hf, - TPatchHeader * pPatchHeader) + TMPQFile * hf, + TPatchHeader * pPatchHeader) { PBLIZZARD_BSDIFF40_FILE pBsdiff; LPDWORD pCtrlBlock; @@ -281,7 +246,7 @@ static int ApplyMpqPatch_BSD0( dwNewSize = (DWORD)BSWAP_INT64_UNSIGNED(pBsdiff->NewFileSize); // Allocate new buffer - pbNewData = ALLOCMEM(BYTE, dwNewSize); + pbNewData = STORM_ALLOC(BYTE, dwNewSize); if(pbNewData == NULL) return ERROR_NOT_ENOUGH_MEMORY; @@ -296,7 +261,7 @@ static int ApplyMpqPatch_BSD0( // Sanity check if((dwNewOffset + dwAddDataLength) > dwNewSize) { - FREEMEM(pbNewData); + STORM_FREE(pbNewData); return ERROR_FILE_CORRUPT; } @@ -317,7 +282,7 @@ static int ApplyMpqPatch_BSD0( // Sanity check if((dwNewOffset + dwMovDataLength) > dwNewSize) { - FREEMEM(pbNewData); + STORM_FREE(pbNewData); return ERROR_FILE_CORRUPT; } @@ -334,7 +299,7 @@ static int ApplyMpqPatch_BSD0( } // Free the old file data - FREEMEM(hf->pbFileData); + STORM_FREE(hf->pbFileData); // Put the new data to the fil structure hf->pbFileData = pbNewData; @@ -350,7 +315,7 @@ static int LoadMpqPatch(TMPQFile * hf) int nError = ERROR_SUCCESS; // Read the patch header - SFileReadFile((HANDLE)hf, &PatchHeader, sizeof(TPatchHeader), &dwBytesRead); + SFileReadFile((HANDLE)hf, &PatchHeader, sizeof(TPatchHeader), &dwBytesRead, NULL); if(dwBytesRead != sizeof(TPatchHeader)) nError = ERROR_FILE_CORRUPT; @@ -372,17 +337,17 @@ static int LoadMpqPatch(TMPQFile * hf) { switch(PatchHeader.dwPatchType) { - case 0x59504f43: // 'COPY' - nError = LoadMpqPatch_COPY(hf, &PatchHeader); - break; + case 0x59504f43: // 'COPY' + nError = LoadMpqPatch_COPY(hf, &PatchHeader); + break; - case 0x30445342: // 'BSD0' - nError = LoadMpqPatch_BSD0(hf, &PatchHeader); - break; + case 0x30445342: // 'BSD0' + nError = LoadMpqPatch_BSD0(hf, &PatchHeader); + break; - default: - nError = ERROR_FILE_CORRUPT; - break; + default: + nError = ERROR_FILE_CORRUPT; + break; } } @@ -390,20 +355,15 @@ static int LoadMpqPatch(TMPQFile * hf) } static int ApplyMpqPatch( - TMPQFile * hf, - TPatchHeader * pPatchHeader) + TMPQFile * hf, + TPatchHeader * pPatchHeader) { - unsigned char md5_digest[MD5_DIGEST_SIZE]; - hash_state md5_state; int nError = ERROR_SUCCESS; // Verify the original file before patching if(pPatchHeader->dwSizeBeforePatch != 0) { - md5_init(&md5_state); - md5_process(&md5_state, hf->pbFileData, hf->cbFileData); - md5_done(&md5_state, md5_digest); - if(memcmp(pPatchHeader->md5_before_patch, md5_digest, MD5_DIGEST_SIZE)) + if(!VerifyDataBlockHash(hf->pbFileData, hf->cbFileData, pPatchHeader->md5_before_patch)) nError = ERROR_FILE_CORRUPT; } @@ -412,17 +372,17 @@ static int ApplyMpqPatch( { switch(pPatchHeader->dwPatchType) { - case 0x59504f43: // 'COPY' - nError = ApplyMpqPatch_COPY(hf, pPatchHeader); - break; + case 0x59504f43: // 'COPY' + nError = ApplyMpqPatch_COPY(hf, pPatchHeader); + break; - case 0x30445342: // 'BSD0' - nError = ApplyMpqPatch_BSD0(hf, pPatchHeader); - break; + case 0x30445342: // 'BSD0' + nError = ApplyMpqPatch_BSD0(hf, pPatchHeader); + break; - default: - nError = ERROR_FILE_CORRUPT; - break; + default: + nError = ERROR_FILE_CORRUPT; + break; } } @@ -430,10 +390,7 @@ static int ApplyMpqPatch( if(nError == ERROR_SUCCESS && pPatchHeader->dwSizeAfterPatch != 0) { // Verify the patched file - md5_init(&md5_state); - md5_process(&md5_state, hf->pbFileData, hf->cbFileData); - md5_done(&md5_state, md5_digest); - if(memcmp(pPatchHeader->md5_after_patch, md5_digest, MD5_DIGEST_SIZE)) + if(!VerifyDataBlockHash(hf->pbFileData, hf->cbFileData, pPatchHeader->md5_after_patch)) nError = ERROR_FILE_CORRUPT; } @@ -443,7 +400,7 @@ static int ApplyMpqPatch( //----------------------------------------------------------------------------- // Public functions (StormLib internals) -bool IsPatchData(const void * pvData, DWORD cbData, LPDWORD pdwPatchedFileSize) +bool IsIncrementalPatchFile(const void * pvData, DWORD cbData, LPDWORD pdwPatchedFileSize) { TPatchHeader * pPatchHeader = (TPatchHeader *)pvData; BLIZZARD_BSDIFF40_FILE DiffFile; @@ -521,15 +478,14 @@ int PatchFileData(TMPQFile * hf) // bool WINAPI SFileOpenPatchArchive( - HANDLE hMpq, - const char * szPatchMpqName, - const char * szPatchPathPrefix, - DWORD dwFlags) + HANDLE hMpq, + const TCHAR * szPatchMpqName, + const char * szPatchPathPrefix, + DWORD dwFlags) { TMPQArchive * haPatch; TMPQArchive * ha = (TMPQArchive *)hMpq; HANDLE hPatchMpq = NULL; - size_t nLength = 0; char szPatchPrefixBuff[MPQ_PATCH_PREFIX_LEN]; int nError = ERROR_SUCCESS; @@ -543,18 +499,13 @@ bool WINAPI SFileOpenPatchArchive( nError = ERROR_INVALID_PARAMETER; // If the user didn't give the patch prefix, get default one - if(szPatchPathPrefix == NULL) + if(szPatchPathPrefix != NULL) { - // Get the default patch prefix from the base MPQ - GetDefaultPatchPrefix(ha->pStream->szFileName, szPatchMpqName, szPatchPrefixBuff); - szPatchPathPrefix = szPatchPrefixBuff; + // Save length of the patch prefix + if(strlen(szPatchPathPrefix) > MPQ_PATCH_PREFIX_LEN - 2) + nError = ERROR_INVALID_PARAMETER; } - // Save length of the patch prefix - nLength = strlen(szPatchPathPrefix); - if(nLength > MPQ_PATCH_PREFIX_LEN - 2) - nError = ERROR_INVALID_PARAMETER; - // // We don't allow adding patches to archives that have been open for write // @@ -569,7 +520,7 @@ bool WINAPI SFileOpenPatchArchive( if(nError == ERROR_SUCCESS) { - if((ha->pStream->StreamFlags & STREAM_FLAG_READ_ONLY) == 0) + if(!FileStream_IsReadOnly(ha->pStream)) nError = ERROR_ACCESS_DENIED; } @@ -580,17 +531,26 @@ bool WINAPI SFileOpenPatchArchive( return false; haPatch = (TMPQArchive *)hPatchMpq; + // Older WoW patches (build 13914) used to have + // several language versions in one patch file + // Those patches needed to have a path prefix + // We can distinguish such patches by not having the (patch_metadata) file + if(szPatchPathPrefix == NULL) + { + if(!SFileHasFile(hPatchMpq, PATCH_METADATA_NAME)) + { + GetDefaultPatchPrefix(FileStream_GetFileName(ha->pStream), szPatchPrefixBuff); + szPatchPathPrefix = szPatchPrefixBuff; + } + } + // Save the prefix for patch file names. // Make sure that there is backslash after it - if(nLength > 0) + if(szPatchPathPrefix != NULL && *szPatchPathPrefix != 0) { strcpy(haPatch->szPatchPrefix, szPatchPathPrefix); - if(haPatch->szPatchPrefix[nLength - 1] != '\\') - { - haPatch->szPatchPrefix[nLength++] = '\\'; - haPatch->szPatchPrefix[nLength] = 0; - } - haPatch->cchPatchPrefix = nLength; + strcat(haPatch->szPatchPrefix, "\\"); + haPatch->cchPatchPrefix = strlen(haPatch->szPatchPrefix); } // Now add the patch archive to the list of patches to the original MPQ diff --git a/dep/StormLib/src/SFileReadFile.cpp b/dep/StormLib/src/SFileReadFile.cpp index e7caeb494bb..741352c2c07 100644 --- a/dep/StormLib/src/SFileReadFile.cpp +++ b/dep/StormLib/src/SFileReadFile.cpp @@ -10,7 +10,6 @@ /*****************************************************************************/ #define __STORMLIB_SELF__ -#define __INCLUDE_COMPRESSION__ #include "StormLib.h" #include "StormCommon.h" @@ -29,40 +28,31 @@ struct TFileHeader2Ext //----------------------------------------------------------------------------- // Local functions +static void CopyFileName(char * szTarget, const TCHAR * szSource) +{ + while(*szSource != 0) + *szTarget++ = (char)*szSource++; + *szTarget = 0; +} + static DWORD GetMpqFileCount(TMPQArchive * ha) { TFileEntry * pFileTableEnd; TFileEntry * pFileEntry; DWORD dwFileCount = 0; - bool bPatchMode = (ha->haPatch != NULL) ? true : false; // Go through all open MPQs, including patches while(ha != NULL) { - // Go through the entire hash table + // Only count files that are not patch files pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; - - if(bPatchMode) - { - // If we are in patch mode, only count files that - // are not patch files - for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) - { - // If the file is patch file and this is not primary archive, skip it - // BUGBUG: This errorneously counts non-patch files that are - // in both main MPQ and in patches. - if((pFileEntry->dwFlags & (MPQ_FILE_EXISTS | MPQ_FILE_PATCH_FILE)) == MPQ_FILE_EXISTS) - dwFileCount++; - } - } - else + for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) { - // When we are not in patch mode, count all files, no matter what. - for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) - { - if(pFileEntry->dwFlags & MPQ_FILE_EXISTS) - dwFileCount++; - } + // If the file is patch file and this is not primary archive, skip it + // BUGBUG: This errorneously counts non-patch files that are in both + // base MPQ and in patches, and increases the number of files by cca 50% + if((pFileEntry->dwFlags & (MPQ_FILE_EXISTS | MPQ_FILE_PATCH_FILE)) == MPQ_FILE_EXISTS) + dwFileCount++; } // Move to the next patch archive @@ -72,6 +62,61 @@ static DWORD GetMpqFileCount(TMPQArchive * ha) return dwFileCount; } +static bool GetFilePatchChain(TMPQFile * hf, void * pvFileInfo, DWORD cbFileInfo, LPDWORD pcbLengthNeeded) +{ + TMPQFile * hfTemp; + TCHAR * szPatchChain = (TCHAR *)pvFileInfo; + TCHAR * szFileName; + size_t cchCharsNeeded = 1; + size_t nLength; + DWORD cbLengthNeeded; + + // Check if the "hf" is a MPQ file + if(hf->pStream != NULL) + { + // Calculate the length needed + szFileName = FileStream_GetFileName(hf->pStream); + cchCharsNeeded += _tcslen(szFileName) + 1; + cbLengthNeeded = (DWORD)(cchCharsNeeded * sizeof(TCHAR)); + + // If we have enough space, copy the file name + if(cbFileInfo >= cbLengthNeeded) + { + nLength = _tcslen(szFileName) + 1; + memcpy(szPatchChain, szFileName, nLength * sizeof(TCHAR)); + szPatchChain += nLength; + + // Terminate the multi-string + *szPatchChain = 0; + } + } + else + { + // Calculate number of characters needed + for(hfTemp = hf; hfTemp != NULL; hfTemp = hfTemp->hfPatchFile) + cchCharsNeeded += _tcslen(FileStream_GetFileName(hfTemp->ha->pStream)) + 1; + cbLengthNeeded = (DWORD)(cchCharsNeeded * sizeof(TCHAR)); + + // If we have enough space, the copy the patch chain + if(cbFileInfo >= cbLengthNeeded) + { + for(hfTemp = hf; hfTemp != NULL; hfTemp = hfTemp->hfPatchFile) + { + szFileName = FileStream_GetFileName(hfTemp->ha->pStream); + nLength = _tcslen(szFileName) + 1; + memcpy(szPatchChain, szFileName, nLength * sizeof(TCHAR)); + szPatchChain += nLength; + } + + // Terminate the multi-string + *szPatchChain = 0; + } + } + + // Give result length, terminate multi-string and return + *pcbLengthNeeded = cbLengthNeeded; + return true; +} // hf - MPQ File handle. // pbBuffer - Pointer to target buffer to store sectors. @@ -116,15 +161,30 @@ static int ReadMpqSectors(TMPQFile * hf, LPBYTE pbBuffer, DWORD dwByteOffset, DW } // If the sector checksums are not loaded yet, load them now. - if(hf->SectorChksums == NULL && (pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC)) + if(hf->SectorChksums == NULL && (pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC) && hf->bLoadedSectorCRCs == false) { - nError = AllocateSectorChecksums(hf, true); - if(nError != ERROR_SUCCESS) - return nError; + // + // Sector CRCs is plain crap feature. It is almost never present, + // often it's empty, or the end offset of sector CRCs is zero. + // We only try to load sector CRCs once, and regardless if it fails + // or not, we won't try that again for the given file. + // + + AllocateSectorChecksums(hf, true); + hf->bLoadedSectorCRCs = true; } + // TODO: If the raw data MD5s are not loaded yet, load them now + // Only do it if the MPQ is of format 4.0 + // if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_4 && ha->pHeader->dwRawChunkSize != 0) + // { + // nError = AllocateRawMD5s(hf, true); + // if(nError != ERROR_SUCCESS) + // return nError; + // } + // If the file is compressed, also allocate secondary buffer - pbInSector = pbRawSector = ALLOCMEM(BYTE, dwBytesToRead); + pbInSector = pbRawSector = STORM_ALLOC(BYTE, dwBytesToRead); if(pbRawSector == NULL) return ERROR_NOT_ENOUGH_MEMORY; @@ -207,13 +267,20 @@ static int ReadMpqSectors(TMPQFile * hf, LPBYTE pbBuffer, DWORD dwByteOffset, DW int cbInSector = dwRawBytesInThisSector; int nResult = 0; - // Is the file compressed by PKWARE Data Compression Library ? - if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE) - nResult = SCompExplode((char *)pbOutSector, &cbOutSector, (char *)pbInSector, cbInSector); - // Is the file compressed by Blizzard's multiple compression ? if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS) - nResult = SCompDecompress((char *)pbOutSector, &cbOutSector, (char *)pbInSector, cbInSector); + { + if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2) + nResult = SCompDecompress2((char *)pbOutSector, &cbOutSector, (char *)pbInSector, cbInSector); + else + nResult = SCompDecompress((char *)pbOutSector, &cbOutSector, (char *)pbInSector, cbInSector); + } + + // Is the file compressed by PKWARE Data Compression Library ? + else if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE) + { + nResult = SCompExplode((char *)pbOutSector, &cbOutSector, (char *)pbInSector, cbInSector); + } // Did the decompression fail ? if(nResult == 0) @@ -239,21 +306,21 @@ static int ReadMpqSectors(TMPQFile * hf, LPBYTE pbBuffer, DWORD dwByteOffset, DW // Free all used buffers if(pbRawSector != NULL) - FREEMEM(pbRawSector); - + STORM_FREE(pbRawSector); + // Give the caller thenumber of bytes read *pdwBytesRead = dwBytesRead; - return nError; + return nError; } -static int ReadMpqFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwToRead, LPDWORD pdwBytesRead) +static int ReadMpqFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead) { ULONGLONG RawFilePos = hf->RawFilePos; TMPQArchive * ha = hf->ha; TFileEntry * pFileEntry = hf->pFileEntry; LPBYTE pbCompressed = NULL; LPBYTE pbRawData = NULL; - int nError; + int nError = ERROR_SUCCESS; // If the file buffer is not allocated yet, do it. if(hf->pbFileSector == NULL) @@ -268,128 +335,103 @@ static int ReadMpqFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwToRead, if(hf->pPatchInfo != NULL) RawFilePos += hf->pPatchInfo->dwLength; - // If the file buffer is not loaded yet, do it + // If the file sector is not loaded yet, do it if(hf->dwSectorOffs != 0) { - // - // In "wow-update-12694.MPQ" from Wow-Cataclysm BETA: - // - // File CmpSize FileSize Data - // -------------------------------------- ------- -------- --------------- - // esES\DBFilesClient\LightSkyBox.dbc 0xBE 0xBC Is compressed - // deDE\DBFilesClient\MountCapability.dbc 0x93 0x77 Is uncompressed - // - // Now tell me how to deal with this mess. Apparently - // someone made a mistake at Blizzard ... - // - - if(hf->pPatchInfo != NULL) + // Is the file compressed? + if(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED) { - // Allocate space for - pbCompressed = ALLOCMEM(BYTE, pFileEntry->dwCmpSize); + // Allocate space for compressed data + pbCompressed = STORM_ALLOC(BYTE, pFileEntry->dwCmpSize); if(pbCompressed == NULL) return ERROR_NOT_ENOUGH_MEMORY; + pbRawData = pbCompressed; + } - // Read the entire file - if(!FileStream_Read(ha->pStream, &RawFilePos, pbCompressed, pFileEntry->dwCmpSize)) - { - FREEMEM(pbCompressed); - return GetLastError(); - } - - // We assume that patch files are not encrypted - assert((pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) == 0); - assert((pFileEntry->dwFlags & MPQ_FILE_IMPLODE) == 0); - - // Check the 'PTCH' signature to find out if it's compressed or not - if(pbCompressed[0] != 'P' || pbCompressed[1] != 'T' || pbCompressed[2] != 'C' || pbCompressed[3] != 'H') - { - int cbOutBuffer = (int)hf->dwDataSize; - int nResult = SCompDecompress((char *)hf->pbFileSector, - &cbOutBuffer, - (char *)pbCompressed, - (int)pFileEntry->dwCmpSize); - if(nResult == 0) - { - FREEMEM(pbCompressed); - return ERROR_FILE_CORRUPT; - } - } - else - { - memcpy(hf->pbFileSector, pbCompressed, hf->dwDataSize); - } + // Load the raw (compressed, encrypted) data + if(!FileStream_Read(ha->pStream, &RawFilePos, pbRawData, pFileEntry->dwCmpSize)) + { + STORM_FREE(pbCompressed); + return GetLastError(); + } - // Free the decompression buffer. - FREEMEM(pbCompressed); + // If the file is encrypted, we have to decrypt the data first + if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) + { + BSWAP_ARRAY32_UNSIGNED(pbRawData, pFileEntry->dwCmpSize); + DecryptMpqBlock(pbRawData, pFileEntry->dwCmpSize, hf->dwFileKey); + BSWAP_ARRAY32_UNSIGNED(pbRawData, pFileEntry->dwCmpSize); } - else + + // If the file is compressed, we have to decompress it now + if(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED) { - // If the file is compressed, we have to allocate buffer for compressed data - if(pFileEntry->dwCmpSize < hf->dwDataSize) - { - pbCompressed = ALLOCMEM(BYTE, pFileEntry->dwCmpSize); - if(pbCompressed == NULL) - return ERROR_NOT_ENOUGH_MEMORY; - pbRawData = pbCompressed; - } + int cbOutBuffer = (int)hf->dwDataSize; + int cbInBuffer = (int)pFileEntry->dwCmpSize; + int nResult = 0; - // Read the entire file - if(!FileStream_Read(ha->pStream, &RawFilePos, pbRawData, pFileEntry->dwCmpSize)) - { - FREEMEM(pbCompressed); - return GetLastError(); - } + // + // If the file is an incremental patch, the size of compressed data + // is determined as pFileEntry->dwCmpSize - sizeof(TPatchInfo) + // + // In "wow-update-12694.MPQ" from Wow-Cataclysm BETA: + // + // File CmprSize DcmpSize DataSize Compressed? + // -------------------------------------- ---------- -------- -------- --------------- + // esES\DBFilesClient\LightSkyBox.dbc 0xBE->0xA2 0xBC 0xBC Yes + // deDE\DBFilesClient\MountCapability.dbc 0x93->0x77 0x77 0x77 No + // + + if(pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) + cbInBuffer = cbInBuffer - sizeof(TPatchInfo); - // If the file is encrypted, we have to decrypt the data first - if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) + // Is the file compressed by Blizzard's multiple compression ? + if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS) { - BSWAP_ARRAY32_UNSIGNED(pbRawData, pFileEntry->dwCmpSize); - DecryptMpqBlock(pbRawData, pFileEntry->dwCmpSize, hf->dwFileKey); - BSWAP_ARRAY32_UNSIGNED(pbRawData, pFileEntry->dwCmpSize); + if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2) + nResult = SCompDecompress2((char *)hf->pbFileSector, &cbOutBuffer, (char *)pbRawData, cbInBuffer); + else + nResult = SCompDecompress((char *)hf->pbFileSector, &cbOutBuffer, (char *)pbRawData, cbInBuffer); } - // If the file is compressed, we have to decompress it now - if(pFileEntry->dwCmpSize < hf->dwDataSize) - { - int cbOutBuffer = (int)hf->dwDataSize; - int nResult = 0; - - // Note: Single unit files compressed with IMPLODE are not supported by Blizzard - if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE) - nResult = SCompExplode((char *)hf->pbFileSector, &cbOutBuffer, (char *)pbRawData, (int)pFileEntry->dwCmpSize); - if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS) - nResult = SCompDecompress((char *)hf->pbFileSector, &cbOutBuffer, (char *)pbRawData, (int)pFileEntry->dwCmpSize); - - // Free the decompression buffer. - FREEMEM(pbCompressed); - if(nResult == 0) - return ERROR_FILE_CORRUPT; - } + // Is the file compressed by PKWARE Data Compression Library ? + // Note: Single unit files compressed with IMPLODE are not supported by Blizzard + else if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE) + nResult = SCompExplode((char *)hf->pbFileSector, &cbOutBuffer, (char *)pbRawData, cbInBuffer); + + nError = (nResult != 0) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT; + } + else + { + if(pbRawData != hf->pbFileSector) + memcpy(hf->pbFileSector, pbRawData, hf->dwDataSize); } + // Free the decompression buffer. + if(pbCompressed != NULL) + STORM_FREE(pbCompressed); + // The file sector is now properly loaded hf->dwSectorOffs = 0; } // At this moment, we have the file loaded into the file buffer. // Copy as much as the caller wants - if(hf->dwSectorOffs == 0) + if(nError == ERROR_SUCCESS && hf->dwSectorOffs == 0) { // File position is greater or equal to file size ? - if(hf->dwFilePos >= hf->dwDataSize) + if(dwFilePos >= hf->dwDataSize) { *pdwBytesRead = 0; return ERROR_SUCCESS; } // If not enough bytes remaining in the file, cut them - if((hf->dwDataSize - hf->dwFilePos) < dwToRead) - dwToRead = (hf->dwDataSize - hf->dwFilePos); + if((hf->dwDataSize - dwFilePos) < dwToRead) + dwToRead = (hf->dwDataSize - dwFilePos); // Copy the bytes - memcpy(pvBuffer, hf->pbFileSector + hf->dwFilePos, dwToRead); - hf->dwFilePos += dwToRead; + memcpy(pvBuffer, hf->pbFileSector + dwFilePos, dwToRead); // Give the number of bytes read *pdwBytesRead = dwToRead; @@ -400,7 +442,7 @@ static int ReadMpqFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwToRead, return ERROR_CAN_NOT_COMPLETE; } -static int ReadMpqFile(TMPQFile * hf, void * pvBuffer, DWORD dwBytesToRead, LPDWORD pdwBytesRead) +static int ReadMpqFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwBytesToRead, LPDWORD pdwBytesRead) { TMPQArchive * ha = hf->ha; LPBYTE pbBuffer = (BYTE *)pvBuffer; @@ -411,18 +453,18 @@ static int ReadMpqFile(TMPQFile * hf, void * pvBuffer, DWORD dwBytesToRead, LPDW int nError; // If the file position is at or beyond end of file, do nothing - if(hf->dwFilePos >= hf->dwDataSize) + if(dwFilePos >= hf->dwDataSize) { *pdwBytesRead = 0; return ERROR_SUCCESS; } // If not enough bytes in the file remaining, cut them - if(dwBytesToRead > (hf->dwDataSize - hf->dwFilePos)) - dwBytesToRead = (hf->dwDataSize - hf->dwFilePos); + if(dwBytesToRead > (hf->dwDataSize - dwFilePos)) + dwBytesToRead = (hf->dwDataSize - dwFilePos); // Compute sector position in the file - dwFileSectorPos = hf->dwFilePos & ~dwSectorSizeMask; // Position in the block + dwFileSectorPos = dwFilePos & ~dwSectorSizeMask; // Position in the block // If the file sector buffer is not allocated yet, do it now if(hf->pbFileSector == NULL) @@ -433,11 +475,11 @@ static int ReadMpqFile(TMPQFile * hf, void * pvBuffer, DWORD dwBytesToRead, LPDW } // Load the first (incomplete) file sector - if(hf->dwFilePos & dwSectorSizeMask) + if(dwFilePos & dwSectorSizeMask) { DWORD dwBytesInSector = ha->dwSectorSize; - DWORD dwBufferOffs = hf->dwFilePos & dwSectorSizeMask; - DWORD dwToCopy; + DWORD dwBufferOffs = dwFilePos & dwSectorSizeMask; + DWORD dwToCopy; // Is the file sector already loaded ? if(hf->dwSectorOffs != dwFileSectorPos) @@ -511,20 +553,17 @@ static int ReadMpqFile(TMPQFile * hf, void * pvBuffer, DWORD dwBytesToRead, LPDW // Copy the data from the cached last sector to the caller's buffer memcpy(pbBuffer, hf->pbFileSector, dwToCopy); - + // Update pointers dwTotalBytesRead += dwToCopy; } - // Update the file position - hf->dwFilePos += dwTotalBytesRead; - // Store total number of bytes read to the caller *pdwBytesRead = dwTotalBytesRead; return ERROR_SUCCESS; } -static int ReadMpqFilePatchFile(TMPQFile * hf, void * pvBuffer, DWORD dwToRead, LPDWORD pdwBytesRead) +static int ReadMpqFilePatchFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead) { DWORD dwBytesToRead = dwToRead; DWORD dwBytesRead = 0; @@ -534,16 +573,16 @@ static int ReadMpqFilePatchFile(TMPQFile * hf, void * pvBuffer, DWORD dwToRead, if(hf->pbFileData == NULL) { // Load the original file and store its content to "pbOldData" - hf->pbFileData = ALLOCMEM(BYTE, hf->pFileEntry->dwFileSize); + hf->pbFileData = STORM_ALLOC(BYTE, hf->pFileEntry->dwFileSize); hf->cbFileData = hf->pFileEntry->dwFileSize; if(hf->pbFileData == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Read the file data if(hf->pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT) - nError = ReadMpqFileSingleUnit(hf, hf->pbFileData, hf->cbFileData, &dwBytesRead); + nError = ReadMpqFileSingleUnit(hf, hf->pbFileData, 0, hf->cbFileData, &dwBytesRead); else - nError = ReadMpqFile(hf, hf->pbFileData, hf->cbFileData, &dwBytesRead); + nError = ReadMpqFile(hf, hf->pbFileData, 0, hf->cbFileData, &dwBytesRead); // Fix error code if(nError == ERROR_SUCCESS && dwBytesRead != hf->cbFileData) @@ -553,23 +592,21 @@ static int ReadMpqFilePatchFile(TMPQFile * hf, void * pvBuffer, DWORD dwToRead, if(nError == ERROR_SUCCESS) nError = PatchFileData(hf); - // Reset position to zero - hf->dwFilePos = 0; + // Reset number of bytes read to zero dwBytesRead = 0; } // If there is something to read, do it if(nError == ERROR_SUCCESS) { - if(hf->dwFilePos < hf->cbFileData) + if(dwFilePos < hf->cbFileData) { // Make sure we don't copy more than file size - if((hf->dwFilePos + dwToRead) > hf->cbFileData) - dwToRead = hf->cbFileData - hf->dwFilePos; + if((dwFilePos + dwToRead) > hf->cbFileData) + dwToRead = hf->cbFileData - dwFilePos; // Copy the appropriate amount of the file data to the caller's buffer - memcpy(pvBuffer, hf->pbFileData + hf->dwFilePos, dwToRead); - hf->dwFilePos += dwToRead; + memcpy(pvBuffer, hf->pbFileData + dwFilePos, dwToRead); dwBytesRead = dwToRead; } @@ -619,14 +656,14 @@ bool WINAPI SFileReadFile(HANDLE hFile, void * pvBuffer, DWORD dwToRead, LPDWORD // and if they differ, we assume that number of bytes read // is the difference between them - FileStream_GetPos(hf->pStream, FilePosition1); + FileStream_GetPos(hf->pStream, &FilePosition1); if(!FileStream_Read(hf->pStream, NULL, pvBuffer, dwToRead)) { // If not all bytes have been read, then return the number // of bytes read if((nError = GetLastError()) == ERROR_HANDLE_EOF) { - FileStream_GetPos(hf->pStream, FilePosition2); + FileStream_GetPos(hf->pStream, &FilePosition2); dwBytesRead = (DWORD)(FilePosition2 - FilePosition1); } else @@ -644,20 +681,23 @@ bool WINAPI SFileReadFile(HANDLE hFile, void * pvBuffer, DWORD dwToRead, LPDWORD // If the file is a patch file, we have to read it special way if(hf->hfPatchFile != NULL && (hf->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0) { - nError = ReadMpqFilePatchFile(hf, pvBuffer, dwToRead, &dwBytesRead); + nError = ReadMpqFilePatchFile(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead); } - // If the file is single unit file, redirect it to read file + // If the file is single unit file, redirect it to read file else if(hf->pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT) { - nError = ReadMpqFileSingleUnit(hf, pvBuffer, dwToRead, &dwBytesRead); + nError = ReadMpqFileSingleUnit(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead); } // Otherwise read it as sector based MPQ file else { - nError = ReadMpqFile(hf, pvBuffer, dwToRead, &dwBytesRead); + nError = ReadMpqFile(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead); } + + // Increment the file position + hf->dwFilePos += dwBytesRead; } // Give the caller the number of bytes read @@ -707,7 +747,7 @@ DWORD WINAPI SFileGetFileSize(HANDLE hFile, LPDWORD pdwFileSizeHigh) // Is it a local file ? if(hf->pStream != NULL) { - FileStream_GetSize(hf->pStream, FileSize); + FileStream_GetSize(hf->pStream, &FileSize); } else { @@ -730,7 +770,6 @@ DWORD WINAPI SFileSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHi TMPQFile * hf = (TMPQFile *)hFile; ULONGLONG FilePosition; ULONGLONG MoveOffset; - ULONGLONG FileSize; DWORD dwFilePosHi; // If the hFile is not a valid file handle, return an error. @@ -743,48 +782,37 @@ DWORD WINAPI SFileSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHi // Get the relative point where to move from switch(dwMoveMethod) { - case FILE_BEGIN: - FilePosition = 0; - break; - - case FILE_CURRENT: - if(hf->pStream != NULL) - { - FileStream_GetPos(hf->pStream, FilePosition); - } - else - { - FilePosition = hf->dwFilePos; - } - break; + case FILE_BEGIN: + FilePosition = 0; + break; - case FILE_END: - if(hf->pStream != NULL) - { - FileStream_GetSize(hf->pStream, FilePosition); - } - else - { - FilePosition = hf->dwDataSize; - } - break; + case FILE_CURRENT: + if(hf->pStream != NULL) + { + FileStream_GetPos(hf->pStream, &FilePosition); + } + else + { + FilePosition = hf->dwFilePos; + } + break; - default: - SetLastError(ERROR_INVALID_PARAMETER); - return SFILE_INVALID_POS; - } + case FILE_END: + if(hf->pStream != NULL) + { + FileStream_GetSize(hf->pStream, &FilePosition); + } + else + { + FilePosition = SFileGetFileSize(hFile, NULL); + } + break; - // Get the current file size - if(hf->pStream != NULL) - { - FileStream_GetSize(hf->pStream, FileSize); - } - else - { - FileSize = hf->dwDataSize; + default: + SetLastError(ERROR_INVALID_PARAMETER); + return SFILE_INVALID_POS; } - // Now get the move offset. Note that both values form // a signed 64-bit value (a file pointer can be moved backwards) if(plFilePosHigh != NULL) @@ -834,7 +862,7 @@ DWORD WINAPI SFileSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHi //----------------------------------------------------------------------------- // Tries to retrieve the file name -static TFileHeader2Ext data2ext[] = +static TFileHeader2Ext data2ext[] = { {0x00005A4D, 0x0000FFFF, 0x00000000, 0x00000000, "exe"}, // EXE files {0x00000006, 0xFFFFFFFF, 0x00000001, 0xFFFFFFFF, "dc6"}, // EXE files @@ -857,14 +885,14 @@ static TFileHeader2Ext data2ext[] = {0x47585053, 0xFFFFFFFF, 0x00000000, 0x00000000, "bls"}, // WoW pixel shaders {0xE0FFD8FF, 0xFFFFFFFF, 0x00000000, 0x00000000, "jpg"}, // JPEG image {0x00000000, 0x00000000, 0x00000000, 0x00000000, "xxx"}, // Default extension - {0, 0, 0, 0, NULL} // Terminator + {0, 0, 0, 0, NULL} // Terminator }; bool WINAPI SFileGetFileName(HANDLE hFile, char * szFileName) { TFileEntry * pFileEntry; TMPQFile * hf = (TMPQFile *)hFile; // MPQ File handle - char szPseudoName[20]; + char szPseudoName[20]; DWORD FirstBytes[2]; // The first 4 bytes of the file DWORD dwFilePos; // Saved file position int nError = ERROR_SUCCESS; @@ -878,14 +906,14 @@ bool WINAPI SFileGetFileName(HANDLE hFile, char * szFileName) if(!IsValidFileHandle(hf)) nError = ERROR_INVALID_HANDLE; pFileEntry = hf->pFileEntry; - + // Only do something if the file name is not filled - if(nError == ERROR_SUCCESS && pFileEntry->szFileName == NULL) + if(nError == ERROR_SUCCESS && pFileEntry != NULL && pFileEntry->szFileName == NULL) { // Read the first 2 DWORDs bytes from the file FirstBytes[0] = FirstBytes[1] = 0; - dwFilePos = SFileSetFilePointer(hf, 0, NULL, FILE_CURRENT); - SFileReadFile(hFile, FirstBytes, sizeof(FirstBytes), NULL); + dwFilePos = SFileSetFilePointer(hf, 0, NULL, FILE_CURRENT); + SFileReadFile(hFile, FirstBytes, sizeof(FirstBytes), NULL, NULL); BSWAP_ARRAY32_UNSIGNED(FirstBytes, sizeof(FirstBytes)); SFileSetFilePointer(hf, dwFilePos, NULL, FILE_BEGIN); @@ -893,7 +921,7 @@ bool WINAPI SFileGetFileName(HANDLE hFile, char * szFileName) for(i = 0; data2ext[i].szExt != NULL; i++) { if((FirstBytes[0] & data2ext[i].dwOffset00Mask) == data2ext[i].dwOffset00Data && - (FirstBytes[1] & data2ext[i].dwOffset04Mask) == data2ext[i].dwOffset04Data) + (FirstBytes[1] & data2ext[i].dwOffset04Mask) == data2ext[i].dwOffset04Data) { sprintf(szPseudoName, "File%08u.%s", (unsigned int)(pFileEntry - hf->ha->pFileTable), data2ext[i].szExt); break; @@ -905,8 +933,13 @@ bool WINAPI SFileGetFileName(HANDLE hFile, char * szFileName) } // Now put the file name to the file structure - if(nError == ERROR_SUCCESS && szFileName != NULL && pFileEntry->szFileName != NULL) - strcpy(szFileName, pFileEntry->szFileName); + if(nError == ERROR_SUCCESS && szFileName != NULL) + { + if(pFileEntry != NULL && pFileEntry->szFileName != NULL) + strcpy(szFileName, pFileEntry->szFileName); + else if(hf->pStream != NULL) + CopyFileName(szFileName, FileStream_GetFileName(hf->pStream)); + } return (nError == ERROR_SUCCESS); } @@ -918,219 +951,233 @@ bool WINAPI SFileGetFileName(HANDLE hFile, char * szFileName) #define VERIFY_MPQ_HANDLE(h) \ if(!IsValidMpqHandle(h)) \ - { \ - nError = ERROR_INVALID_HANDLE; \ - break; \ +{ \ + nError = ERROR_INVALID_HANDLE; \ + break; \ } #define VERIFY_FILE_HANDLE(h) \ if(!IsValidFileHandle(h)) \ - { \ - nError = ERROR_INVALID_HANDLE; \ - break; \ +{ \ + nError = ERROR_INVALID_HANDLE; \ + break; \ } -#define RESULT_IS_64BIT_VALUE(val) \ - cbLengthNeeded = sizeof(ULONGLONG); \ - ResultValue = val; - -#define RESULT_IS_32BIT_VALUE(val) \ - cbLengthNeeded = sizeof(DWORD); \ - ResultValue = val; - bool WINAPI SFileGetFileInfo( - HANDLE hMpqOrFile, - DWORD dwInfoType, - void * pvFileInfo, - DWORD cbFileInfo, - LPDWORD pcbLengthNeeded) + HANDLE hMpqOrFile, + DWORD dwInfoType, + void * pvFileInfo, + DWORD cbFileInfo, + LPDWORD pcbLengthNeeded) { TMPQArchive * ha = (TMPQArchive *)hMpqOrFile; - ULONGLONG ResultValue = 0; TMPQBlock * pBlock; TMPQFile * hf = (TMPQFile *)hMpqOrFile; + void * pvSrcFileInfo = NULL; DWORD cbLengthNeeded = 0; DWORD dwIsReadOnly; DWORD dwFileCount = 0; + DWORD dwFileIndex; DWORD dwFileKey; DWORD i; int nError = ERROR_SUCCESS; switch(dwInfoType) { - case SFILE_INFO_ARCHIVE_NAME: - VERIFY_MPQ_HANDLE(ha); - cbLengthNeeded = (DWORD)strlen(ha->pStream->szFileName) + 1; - if(cbFileInfo < cbLengthNeeded) - { - nError = ERROR_INSUFFICIENT_BUFFER; - break; - } - strcpy((char *)pvFileInfo, ha->pStream->szFileName); - break; - - case SFILE_INFO_ARCHIVE_SIZE: // Size of the archive - VERIFY_MPQ_HANDLE(ha); - RESULT_IS_32BIT_VALUE(ha->pHeader->dwArchiveSize); - break; - - case SFILE_INFO_MAX_FILE_COUNT: // Max. number of files in the MPQ - VERIFY_MPQ_HANDLE(ha); - RESULT_IS_32BIT_VALUE(ha->dwMaxFileCount); - break; - - case SFILE_INFO_HASH_TABLE_SIZE: // Size of the hash table - VERIFY_MPQ_HANDLE(ha); - RESULT_IS_32BIT_VALUE(ha->pHeader->dwHashTableSize); - break; - - case SFILE_INFO_BLOCK_TABLE_SIZE: // Size of the block table - VERIFY_MPQ_HANDLE(ha); - RESULT_IS_32BIT_VALUE(ha->pHeader->dwBlockTableSize); - break; - - case SFILE_INFO_SECTOR_SIZE: - VERIFY_MPQ_HANDLE(ha); - RESULT_IS_32BIT_VALUE(ha->dwSectorSize); - break; - - case SFILE_INFO_HASH_TABLE: - VERIFY_MPQ_HANDLE(ha); - cbLengthNeeded = ha->pHeader->dwHashTableSize * sizeof(TMPQHash); - if(cbFileInfo < cbLengthNeeded) - { - nError = ERROR_INSUFFICIENT_BUFFER; - break; - } - memcpy(pvFileInfo, ha->pHashTable, cbLengthNeeded); - break; - - case SFILE_INFO_BLOCK_TABLE: - VERIFY_MPQ_HANDLE(ha); - cbLengthNeeded = ha->dwFileTableSize * sizeof(TMPQBlock); - if(cbFileInfo < cbLengthNeeded) - { - nError = ERROR_INSUFFICIENT_BUFFER; - break; - } - - // Construct block table from file table size - pBlock = (TMPQBlock *)pvFileInfo; - for(i = 0; i < ha->dwFileTableSize; i++) - { - pBlock->dwFilePos = (DWORD)ha->pFileTable[i].ByteOffset; - pBlock->dwFSize = ha->pFileTable[i].dwFileSize; - pBlock->dwCSize = ha->pFileTable[i].dwCmpSize; - pBlock->dwFlags = ha->pFileTable[i].dwFlags; - pBlock++; - } + case SFILE_INFO_ARCHIVE_NAME: + VERIFY_MPQ_HANDLE(ha); + + // pvFileInfo receives the name of the archive, terminated by 0 + pvSrcFileInfo = FileStream_GetFileName(ha->pStream); + cbLengthNeeded = (DWORD)(_tcslen((TCHAR *)pvSrcFileInfo) + 1) * sizeof(TCHAR); + break; + + case SFILE_INFO_ARCHIVE_SIZE: // Size of the archive + VERIFY_MPQ_HANDLE(ha); + cbLengthNeeded = sizeof(DWORD); + pvSrcFileInfo = &ha->pHeader->dwArchiveSize; + break; + + case SFILE_INFO_MAX_FILE_COUNT: // Max. number of files in the MPQ + VERIFY_MPQ_HANDLE(ha); + cbLengthNeeded = sizeof(DWORD); + pvSrcFileInfo = &ha->dwMaxFileCount; + break; + + case SFILE_INFO_HASH_TABLE_SIZE: // Size of the hash table + VERIFY_MPQ_HANDLE(ha); + cbLengthNeeded = sizeof(DWORD); + pvSrcFileInfo = &ha->pHeader->dwHashTableSize; + break; + + case SFILE_INFO_BLOCK_TABLE_SIZE: // Size of the block table + VERIFY_MPQ_HANDLE(ha); + cbLengthNeeded = sizeof(DWORD); + pvSrcFileInfo = &ha->pHeader->dwBlockTableSize; + break; + + case SFILE_INFO_SECTOR_SIZE: + VERIFY_MPQ_HANDLE(ha); + cbLengthNeeded = sizeof(DWORD); + pvSrcFileInfo = &ha->dwSectorSize; + break; + + case SFILE_INFO_HASH_TABLE: + VERIFY_MPQ_HANDLE(ha); + cbLengthNeeded = ha->pHeader->dwHashTableSize * sizeof(TMPQHash); + pvSrcFileInfo = ha->pHashTable; + break; + + case SFILE_INFO_BLOCK_TABLE: + VERIFY_MPQ_HANDLE(ha); + cbLengthNeeded = ha->dwFileTableSize * sizeof(TMPQBlock); + if(cbFileInfo < cbLengthNeeded) + { + nError = ERROR_INSUFFICIENT_BUFFER; break; + } - case SFILE_INFO_NUM_FILES: - VERIFY_MPQ_HANDLE(ha); - dwFileCount = GetMpqFileCount(ha); - RESULT_IS_32BIT_VALUE(dwFileCount); - break; + // Construct block table from file table size + pBlock = (TMPQBlock *)pvFileInfo; + for(i = 0; i < ha->dwFileTableSize; i++) + { + pBlock->dwFilePos = (DWORD)ha->pFileTable[i].ByteOffset; + pBlock->dwFSize = ha->pFileTable[i].dwFileSize; + pBlock->dwCSize = ha->pFileTable[i].dwCmpSize; + pBlock->dwFlags = ha->pFileTable[i].dwFlags; + pBlock++; + } + break; - case SFILE_INFO_STREAM_FLAGS: // Stream flags for the MPQ. See STREAM_FLAG_XXX - VERIFY_MPQ_HANDLE(ha); - RESULT_IS_32BIT_VALUE(ha->pStream->StreamFlags); - break; + case SFILE_INFO_NUM_FILES: + VERIFY_MPQ_HANDLE(ha); + dwFileCount = GetMpqFileCount(ha); + cbLengthNeeded = sizeof(DWORD); + pvSrcFileInfo = &dwFileCount; + break; - case SFILE_INFO_IS_READ_ONLY: + case SFILE_INFO_STREAM_FLAGS: VERIFY_MPQ_HANDLE(ha); - - dwIsReadOnly = ((ha->pStream->StreamFlags & STREAM_FLAG_READ_ONLY) || (ha->dwFlags & MPQ_FLAG_READ_ONLY)); - RESULT_IS_32BIT_VALUE(dwIsReadOnly); - break; - - case SFILE_INFO_HASH_INDEX: - VERIFY_FILE_HANDLE(hf); - RESULT_IS_32BIT_VALUE(hf->pFileEntry->dwHashIndex); - break; - - case SFILE_INFO_CODENAME1: - VERIFY_FILE_HANDLE(hf); - if(ha->pHashTable != NULL) - { - RESULT_IS_32BIT_VALUE(ha->pHashTable[hf->pFileEntry->dwHashIndex].dwName1); - } - break; - - case SFILE_INFO_CODENAME2: - VERIFY_FILE_HANDLE(hf); - if(ha->pHashTable != NULL) - { - RESULT_IS_32BIT_VALUE(ha->pHashTable[hf->pFileEntry->dwHashIndex].dwName2); - } - break; - - case SFILE_INFO_LOCALEID: - VERIFY_FILE_HANDLE(hf); - RESULT_IS_32BIT_VALUE(hf->pFileEntry->lcLocale); - break; - - case SFILE_INFO_BLOCKINDEX: - VERIFY_FILE_HANDLE(hf); - RESULT_IS_32BIT_VALUE((DWORD)(hf->pFileEntry - hf->ha->pFileTable)); - break; - - case SFILE_INFO_FILE_SIZE: - VERIFY_FILE_HANDLE(hf); - RESULT_IS_32BIT_VALUE(hf->pFileEntry->dwFileSize); - break; - - case SFILE_INFO_COMPRESSED_SIZE: - VERIFY_FILE_HANDLE(hf); - RESULT_IS_32BIT_VALUE(hf->pFileEntry->dwCmpSize); - break; - - case SFILE_INFO_FLAGS: - VERIFY_FILE_HANDLE(hf); - RESULT_IS_32BIT_VALUE(hf->pFileEntry->dwFlags); - break; - - case SFILE_INFO_POSITION: - VERIFY_FILE_HANDLE(hf); - RESULT_IS_32BIT_VALUE((DWORD)hf->pFileEntry->ByteOffset); - break; - - case SFILE_INFO_KEY: - VERIFY_FILE_HANDLE(hf); - RESULT_IS_32BIT_VALUE(hf->dwFileKey); - break; - - case SFILE_INFO_KEY_UNFIXED: - VERIFY_FILE_HANDLE(hf); - dwFileKey = hf->dwFileKey; - if(hf->pFileEntry->dwFlags & MPQ_FILE_FIX_KEY) - dwFileKey = (dwFileKey ^ hf->pFileEntry->dwFileSize) - (DWORD)hf->MpqFilePos; - RESULT_IS_32BIT_VALUE(dwFileKey); - break; - - case SFILE_INFO_FILETIME: - VERIFY_FILE_HANDLE(hf); - RESULT_IS_64BIT_VALUE(hf->pFileEntry->FileTime); - break; - - default: - nError = ERROR_INVALID_PARAMETER; - break; + FileStream_GetFlags(ha->pStream, &dwFileKey); + cbLengthNeeded = sizeof(DWORD); + pvSrcFileInfo = &dwFileKey; + break; + + case SFILE_INFO_IS_READ_ONLY: + VERIFY_MPQ_HANDLE(ha); + dwIsReadOnly = (FileStream_IsReadOnly(ha->pStream) || (ha->dwFlags & MPQ_FLAG_READ_ONLY)); + cbLengthNeeded = sizeof(DWORD); + pvSrcFileInfo = &dwIsReadOnly; + break; + + case SFILE_INFO_HASH_INDEX: + VERIFY_FILE_HANDLE(hf); + cbLengthNeeded = sizeof(DWORD); + pvSrcFileInfo = &hf->pFileEntry->dwHashIndex; + break; + + case SFILE_INFO_CODENAME1: + VERIFY_FILE_HANDLE(hf); + cbLengthNeeded = sizeof(DWORD); + pvSrcFileInfo = &hf->pFileEntry->dwHashIndex; + if(ha->pHashTable != NULL) + pvSrcFileInfo = &ha->pHashTable[hf->pFileEntry->dwHashIndex].dwName1; + break; + + case SFILE_INFO_CODENAME2: + VERIFY_FILE_HANDLE(hf); + cbLengthNeeded = sizeof(DWORD); + if(ha->pHashTable != NULL) + pvSrcFileInfo = &ha->pHashTable[hf->pFileEntry->dwHashIndex].dwName2; + break; + + case SFILE_INFO_LOCALEID: + VERIFY_FILE_HANDLE(hf); + cbLengthNeeded = sizeof(DWORD); + pvSrcFileInfo = &hf->pFileEntry->lcLocale; + break; + + case SFILE_INFO_BLOCKINDEX: + VERIFY_FILE_HANDLE(hf); + dwFileIndex = (DWORD)(hf->pFileEntry - hf->ha->pFileTable); + cbLengthNeeded = sizeof(DWORD); + pvSrcFileInfo = &dwFileIndex; + break; + + case SFILE_INFO_FILE_SIZE: + VERIFY_FILE_HANDLE(hf); + cbLengthNeeded = sizeof(DWORD); + pvSrcFileInfo = &hf->pFileEntry->dwFileSize; + break; + + case SFILE_INFO_COMPRESSED_SIZE: + VERIFY_FILE_HANDLE(hf); + cbLengthNeeded = sizeof(DWORD); + pvSrcFileInfo = &hf->pFileEntry->dwCmpSize; + break; + + case SFILE_INFO_FLAGS: + VERIFY_FILE_HANDLE(hf); + cbLengthNeeded = sizeof(DWORD); + pvSrcFileInfo = &hf->pFileEntry->dwFlags; + break; + + case SFILE_INFO_POSITION: + VERIFY_FILE_HANDLE(hf); + cbLengthNeeded = sizeof(ULONGLONG); + pvSrcFileInfo = &hf->pFileEntry->ByteOffset; + break; + + case SFILE_INFO_KEY: + VERIFY_FILE_HANDLE(hf); + cbLengthNeeded = sizeof(DWORD); + pvSrcFileInfo = &hf->dwFileKey; + break; + + case SFILE_INFO_KEY_UNFIXED: + VERIFY_FILE_HANDLE(hf); + dwFileKey = hf->dwFileKey; + if(hf->pFileEntry->dwFlags & MPQ_FILE_FIX_KEY) + dwFileKey = (dwFileKey ^ hf->pFileEntry->dwFileSize) - (DWORD)hf->MpqFilePos; + cbLengthNeeded = sizeof(DWORD); + pvSrcFileInfo = &dwFileKey; + break; + + case SFILE_INFO_FILETIME: + VERIFY_FILE_HANDLE(hf); + cbLengthNeeded = sizeof(ULONGLONG); + pvSrcFileInfo = &hf->pFileEntry->FileTime; + break; + + case SFILE_INFO_PATCH_CHAIN: + VERIFY_FILE_HANDLE(hf); + GetFilePatchChain(hf, pvFileInfo, cbFileInfo, &cbLengthNeeded); + break; + + default: + nError = ERROR_INVALID_PARAMETER; + break; } - // Check the size - if(cbLengthNeeded < cbFileInfo) - nError = ERROR_INSUFFICIENT_BUFFER; - - // Give the size to the caller - if(pcbLengthNeeded != NULL) - *pcbLengthNeeded = cbLengthNeeded; + // If everything is OK so far, copy the information + if(nError == ERROR_SUCCESS) + { + // Is the output buffer large enough? + if(cbFileInfo >= cbLengthNeeded) + { + // Copy the data + if(pvSrcFileInfo != NULL) + memcpy(pvFileInfo, pvSrcFileInfo, cbLengthNeeded); + } + else + { + nError = ERROR_INSUFFICIENT_BUFFER; + } - // Give the result to the caller - if(cbLengthNeeded == 8) - *(ULONGLONG *)pvFileInfo = ResultValue; - if(cbLengthNeeded == 4) - *(DWORD *)pvFileInfo = (DWORD)ResultValue; + // Give the size to the caller + if(pcbLengthNeeded != NULL) + *pcbLengthNeeded = cbLengthNeeded; + } // Set the last error value, if needed if(nError != ERROR_SUCCESS) diff --git a/dep/StormLib/src/SFileVerify.cpp b/dep/StormLib/src/SFileVerify.cpp index 7272b05665e..8344480efcc 100644 --- a/dep/StormLib/src/SFileVerify.cpp +++ b/dep/StormLib/src/SFileVerify.cpp @@ -14,8 +14,6 @@ /*****************************************************************************/ #define __STORMLIB_SELF__ -#define __INCLUDE_COMPRESSION__ -#define __INCLUDE_CRYPTOGRAPHY__ #include "StormLib.h" #include "StormCommon.h" @@ -46,65 +44,65 @@ typedef struct _MPQ_SIGNATURE_INFO // Created by Jean-Francois Roy using OpenSSL static const char * szBlizzardWeakPublicKey = - "-----BEGIN PUBLIC KEY-----" - "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJJidwS/uILMBSO5DLGsBFknIXWWjQJe" - "2kfdfEk3G/j66w4KkhZ1V61Rt4zLaMVCYpDun7FLwRjkMDSepO1q2DcCAwEAAQ==" - "-----END PUBLIC KEY-----"; + "-----BEGIN PUBLIC KEY-----" + "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJJidwS/uILMBSO5DLGsBFknIXWWjQJe" + "2kfdfEk3G/j66w4KkhZ1V61Rt4zLaMVCYpDun7FLwRjkMDSepO1q2DcCAwEAAQ==" + "-----END PUBLIC KEY-----"; static const char * szBlizzardStrongPublicKey = - "-----BEGIN PUBLIC KEY-----" - "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsQZ+ziT2h8h+J/iMQpgd" - "tH1HaJzOBE3agjU4yMPcrixaPOZoA4t8bwfey7qczfWywocYo3pleytFF+IuD4HD" - "Fl9OXN1SFyupSgMx1EGZlgbFAomnbq9MQJyMqQtMhRAjFgg4TndS7YNb+JMSAEKp" - "kXNqY28n/EVBHD5TsMuVCL579gIenbr61dI92DDEdy790IzIG0VKWLh/KOTcTJfm" - "Ds/7HQTkGouVW+WUsfekuqNQo7ND9DBnhLjLjptxeFE2AZqYcA1ao3S9LN3GL1tW" - "lVXFIX9c7fWqaVTQlZ2oNsI/ARVApOK3grNgqvwH6YoVYVXjNJEo5sQJsPsdV/hk" - "dwIDAQAB" - "-----END PUBLIC KEY-----"; + "-----BEGIN PUBLIC KEY-----" + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsQZ+ziT2h8h+J/iMQpgd" + "tH1HaJzOBE3agjU4yMPcrixaPOZoA4t8bwfey7qczfWywocYo3pleytFF+IuD4HD" + "Fl9OXN1SFyupSgMx1EGZlgbFAomnbq9MQJyMqQtMhRAjFgg4TndS7YNb+JMSAEKp" + "kXNqY28n/EVBHD5TsMuVCL579gIenbr61dI92DDEdy790IzIG0VKWLh/KOTcTJfm" + "Ds/7HQTkGouVW+WUsfekuqNQo7ND9DBnhLjLjptxeFE2AZqYcA1ao3S9LN3GL1tW" + "lVXFIX9c7fWqaVTQlZ2oNsI/ARVApOK3grNgqvwH6YoVYVXjNJEo5sQJsPsdV/hk" + "dwIDAQAB" + "-----END PUBLIC KEY-----"; static const char * szWarcraft3MapPublicKey = - "-----BEGIN PUBLIC KEY-----" - "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1BwklUUQ3UvjizOBRoF5" - "yyOVc7KD+oGOQH5i6eUk1yfs0luCC70kNucNrfqhmviywVtahRse1JtXCPrx2bd3" - "iN8Dx91fbkxjYIOGTsjYoHKTp0BbaFkJih776fcHgnFSb+7mJcDuJVvJOXxEH6w0" - "1vo6VtujCqj1arqbyoal+xtAaczF3us5cOEp45sR1zAWTn1+7omN7VWV4QqJPaDS" - "gBSESc0l1grO0i1VUSumayk7yBKIkb+LBvcG6WnYZHCi7VdLmaxER5m8oZfER66b" - "heHoiSQIZf9PAY6Guw2DT5BTc54j/AaLQAKf2qcRSgQLVo5kQaddF3rCpsXoB/74" - "6QIDAQAB" - "-----END PUBLIC KEY-----"; + "-----BEGIN PUBLIC KEY-----" + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1BwklUUQ3UvjizOBRoF5" + "yyOVc7KD+oGOQH5i6eUk1yfs0luCC70kNucNrfqhmviywVtahRse1JtXCPrx2bd3" + "iN8Dx91fbkxjYIOGTsjYoHKTp0BbaFkJih776fcHgnFSb+7mJcDuJVvJOXxEH6w0" + "1vo6VtujCqj1arqbyoal+xtAaczF3us5cOEp45sR1zAWTn1+7omN7VWV4QqJPaDS" + "gBSESc0l1grO0i1VUSumayk7yBKIkb+LBvcG6WnYZHCi7VdLmaxER5m8oZfER66b" + "heHoiSQIZf9PAY6Guw2DT5BTc54j/AaLQAKf2qcRSgQLVo5kQaddF3rCpsXoB/74" + "6QIDAQAB" + "-----END PUBLIC KEY-----"; static const char * szWowPatchPublicKey = - "-----BEGIN PUBLIC KEY-----" - "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwOsMV0LagAWPEtEQM6b9" - "6FHFkUyGbbyda2/Dfc9dyl21E9QvX+Yw7qKRMAKPzA2TlQQLZKvXpnKXF/YIK5xa" - "5uwg9CEHCEAYolLG4xn0FUOE0E/0PuuytI0p0ICe6rk00PifZzTr8na2wI/l/GnQ" - "bvnIVF1ck6cslATpQJ5JJVMXzoFlUABS19WESw4MXuJAS3AbMhxNWdEhVv7eO51c" - "yGjRLy9QjogZODZTY0fSEksgBqQxNCoYVJYI/sF5K2flDsGqrIp0OdJ6teJlzg1Y" - "UjYnb6bKjlidXoHEXI2TgA/mD6O3XFIt08I9s3crOCTgICq7cgX35qrZiIVWZdRv" - "TwIDAQAB" - "-----END PUBLIC KEY-----"; + "-----BEGIN PUBLIC KEY-----" + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwOsMV0LagAWPEtEQM6b9" + "6FHFkUyGbbyda2/Dfc9dyl21E9QvX+Yw7qKRMAKPzA2TlQQLZKvXpnKXF/YIK5xa" + "5uwg9CEHCEAYolLG4xn0FUOE0E/0PuuytI0p0ICe6rk00PifZzTr8na2wI/l/GnQ" + "bvnIVF1ck6cslATpQJ5JJVMXzoFlUABS19WESw4MXuJAS3AbMhxNWdEhVv7eO51c" + "yGjRLy9QjogZODZTY0fSEksgBqQxNCoYVJYI/sF5K2flDsGqrIp0OdJ6teJlzg1Y" + "UjYnb6bKjlidXoHEXI2TgA/mD6O3XFIt08I9s3crOCTgICq7cgX35qrZiIVWZdRv" + "TwIDAQAB" + "-----END PUBLIC KEY-----"; static const char * szWowSurveyPublicKey = - "-----BEGIN PUBLIC KEY-----" - "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnIt1DR6nRyyKsy2qahHe" - "MKLtacatn/KxieHcwH87wLBxKy+jZ0gycTmJ7SaTdBAEMDs/V5IPIXEtoqYnid2c" - "63TmfGDU92oc3Ph1PWUZ2PWxBhT06HYxRdbrgHw9/I29pNPi/607x+lzPORITOgU" - "BR6MR8au8HsQP4bn4vkJNgnSgojh48/XQOB/cAln7As1neP61NmVimoLR4Bwi3zt" - "zfgrZaUpyeNCUrOYJmH09YIjbBySTtXOUidoPHjFrMsCWpr6xs8xbETbs7MJFL6a" - "vcUfTT67qfIZ9RsuKfnXJTIrV0kwDSjjuNXiPTmWAehSsiHIsrUXX5RNcwsSjClr" - "nQIDAQAB" - "-----END PUBLIC KEY-----"; + "-----BEGIN PUBLIC KEY-----" + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnIt1DR6nRyyKsy2qahHe" + "MKLtacatn/KxieHcwH87wLBxKy+jZ0gycTmJ7SaTdBAEMDs/V5IPIXEtoqYnid2c" + "63TmfGDU92oc3Ph1PWUZ2PWxBhT06HYxRdbrgHw9/I29pNPi/607x+lzPORITOgU" + "BR6MR8au8HsQP4bn4vkJNgnSgojh48/XQOB/cAln7As1neP61NmVimoLR4Bwi3zt" + "zfgrZaUpyeNCUrOYJmH09YIjbBySTtXOUidoPHjFrMsCWpr6xs8xbETbs7MJFL6a" + "vcUfTT67qfIZ9RsuKfnXJTIrV0kwDSjjuNXiPTmWAehSsiHIsrUXX5RNcwsSjClr" + "nQIDAQAB" + "-----END PUBLIC KEY-----"; static const char * szStarcraft2MapPublicKey = - "-----BEGIN PUBLIC KEY-----" - "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmk4GT8zb+ICC25a17KZB" - "q/ygKGJ2VSO6IT5PGHJlm1KfnHBA4B6SH3xMlJ4c6eG2k7QevZv+FOhjsAHubyWq" - "2VKqWbrIFKv2ILc2RfMn8J9EDVRxvcxh6slRrVL69D0w1tfVGjMiKq2Fym5yGoRT" - "E7CRgDqbAbXP9LBsCNWHiJLwfxMGzHbk8pIl9oia5pvM7ofZamSHchxlpy6xa4GJ" - "7xKN01YCNvklTL1D7uol3wkwcHc7vrF8QwuJizuA5bSg4poEGtH62BZOYi+UL/z0" - "31YK+k9CbQyM0X0pJoJoYz1TK+Y5J7vBnXCZtfcTYQ/ZzN6UcxTa57dJaiOlCh9z" - "nQIDAQAB" - "-----END PUBLIC KEY-----"; + "-----BEGIN PUBLIC KEY-----" + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmk4GT8zb+ICC25a17KZB" + "q/ygKGJ2VSO6IT5PGHJlm1KfnHBA4B6SH3xMlJ4c6eG2k7QevZv+FOhjsAHubyWq" + "2VKqWbrIFKv2ILc2RfMn8J9EDVRxvcxh6slRrVL69D0w1tfVGjMiKq2Fym5yGoRT" + "E7CRgDqbAbXP9LBsCNWHiJLwfxMGzHbk8pIl9oia5pvM7ofZamSHchxlpy6xa4GJ" + "7xKN01YCNvklTL1D7uol3wkwcHc7vrF8QwuJizuA5bSg4poEGtH62BZOYi+UL/z0" + "31YK+k9CbQyM0X0pJoJoYz1TK+Y5J7vBnXCZtfcTYQ/ZzN6UcxTa57dJaiOlCh9z" + "nQIDAQAB" + "-----END PUBLIC KEY-----"; //----------------------------------------------------------------------------- // Local functions @@ -123,14 +121,9 @@ static void memrev(unsigned char *buf, size_t count) static bool is_valid_md5(void * pvMd5) { - unsigned char * pbMd5 = (unsigned char *)pvMd5; - unsigned char ByteSum = 0; - int i; + LPDWORD Md5 = (LPDWORD)pvMd5; - for(i = 0; i < MD5_DIGEST_SIZE; i++) - ByteSum |= pbMd5[i]; - - return ByteSum ? true : false; + return (Md5[0] | Md5[1] | Md5[2] | Md5[3]) ? true : false; } static bool decode_base64_key(const char * szKeyBase64, rsa_key * key) @@ -159,14 +152,24 @@ static bool decode_base64_key(const char * szKeyBase64, rsa_key * key) return true; } +static void GetPlainAnsiFileName( + const TCHAR * szFileName, + char * szPlainName) +{ + const TCHAR * szPlainNameT = GetPlainFileNameT(szFileName); + + // Convert the plain name to ANSI + while(*szPlainNameT != 0) + *szPlainName++ = (char)*szPlainNameT++; + *szPlainName = 0; +} + // Calculate begin and end of the MPQ archive static void CalculateArchiveRange( - TMPQArchive * ha, - PMPQ_SIGNATURE_INFO pSI) + TMPQArchive * ha, + PMPQ_SIGNATURE_INFO pSI) { - TMPQHeader * pHeader = ha->pHeader; ULONGLONG TempPos = 0; - ULONGLONG MaxPos; char szMapHeader[0x200]; // Get the MPQ begin @@ -183,39 +186,17 @@ static void CalculateArchiveRange( } } - // Get the MPQ data end. The end is calculated as the biggest - // value of (end of the last file), (end of block table), - // (end of ext block table), (end of hash table) - FindFreeMpqSpace(ha, &MaxPos); - - // Check if hash table is beyond - TempPos = ha->MpqPos + MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos) + pHeader->HashTableSize64; - if(TempPos > MaxPos) - MaxPos = TempPos; - - // Check if block table is beyond - TempPos = ha->MpqPos + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos) + pHeader->BlockTableSize64; - if(TempPos > MaxPos) - MaxPos = TempPos; - - // Check if ext block table is beyond - if(pHeader->HiBlockTablePos64 != 0) - { - TempPos = ha->MpqPos + pHeader->HiBlockTablePos64 + pHeader->HiBlockTableSize64; - if(TempPos > MaxPos) - MaxPos = TempPos; - } - - // Give the end - pSI->EndMpqData = MaxPos; + // Get the MPQ data end. This is stored in our MPQ header, + // and it's been already prepared by SFileOpenArchive, + pSI->EndMpqData = ha->MpqPos + ha->pHeader->ArchiveSize64; // Get the size of the entire file - FileStream_GetSize(ha->pStream, pSI->EndOfFile); + FileStream_GetSize(ha->pStream, &pSI->EndOfFile); } static bool QueryMpqSignatureInfo( - TMPQArchive * ha, - PMPQ_SIGNATURE_INFO pSI) + TMPQArchive * ha, + PMPQ_SIGNATURE_INFO pSI) { ULONGLONG ExtraBytes; TMPQFile * hf; @@ -229,7 +210,7 @@ static bool QueryMpqSignatureInfo( if(SFileOpenFileEx((HANDLE)ha, SIGNATURE_NAME, SFILE_OPEN_FROM_MPQ, &hFile)) { // Get the content of the signature - SFileReadFile(hFile, pSI->Signature, sizeof(pSI->Signature), &pSI->cbSignatureSize); + SFileReadFile(hFile, pSI->Signature, sizeof(pSI->Signature), &pSI->cbSignatureSize, NULL); // Verify the size of the signature hf = (TMPQFile *)hFile; @@ -267,9 +248,9 @@ static bool QueryMpqSignatureInfo( } static bool CalculateMpqHashMd5( - TMPQArchive * ha, - PMPQ_SIGNATURE_INFO pSI, - LPBYTE pMd5Digest) + TMPQArchive * ha, + PMPQ_SIGNATURE_INFO pSI, + LPBYTE pMd5Digest) { hash_state md5_state; ULONGLONG BeginBuffer; @@ -277,7 +258,7 @@ static bool CalculateMpqHashMd5( LPBYTE pbDigestBuffer = NULL; // Allocate buffer for creating the MPQ digest. - pbDigestBuffer = ALLOCMEM(BYTE, MPQ_DIGEST_UNIT_SIZE); + pbDigestBuffer = STORM_ALLOC(BYTE, MPQ_DIGEST_UNIT_SIZE); if(pbDigestBuffer == NULL) return false; @@ -302,10 +283,10 @@ static bool CalculateMpqHashMd5( if(dwToRead == 0) break; - // Read the next chunk + // Read the next chunk if(!FileStream_Read(ha->pStream, &BeginBuffer, pbDigestBuffer, dwToRead)) { - FREEMEM(pbDigestBuffer); + STORM_FREE(pbDigestBuffer); return false; } @@ -338,13 +319,13 @@ static bool CalculateMpqHashMd5( // Finalize the MD5 hash md5_done(&md5_state, pMd5Digest); - FREEMEM(pbDigestBuffer); + STORM_FREE(pbDigestBuffer); return true; } static void AddTailToSha1( - hash_state * psha1_state, - const char * szTail) + hash_state * psha1_state, + const char * szTail) { unsigned char szUpperCase[0x200]; unsigned long nLength = 0; @@ -361,19 +342,20 @@ static void AddTailToSha1( } static bool CalculateMpqHashSha1( - TMPQArchive * ha, - PMPQ_SIGNATURE_INFO pSI, - unsigned char * sha1_tail0, - unsigned char * sha1_tail1, - unsigned char * sha1_tail2) + TMPQArchive * ha, + PMPQ_SIGNATURE_INFO pSI, + unsigned char * sha1_tail0, + unsigned char * sha1_tail1, + unsigned char * sha1_tail2) { ULONGLONG BeginBuffer; hash_state sha1_state_temp; hash_state sha1_state; LPBYTE pbDigestBuffer = NULL; + char szPlainName[MAX_PATH]; // Allocate buffer for creating the MPQ digest. - pbDigestBuffer = ALLOCMEM(BYTE, MPQ_DIGEST_UNIT_SIZE); + pbDigestBuffer = STORM_ALLOC(BYTE, MPQ_DIGEST_UNIT_SIZE); if(pbDigestBuffer == NULL) return false; @@ -396,10 +378,10 @@ static bool CalculateMpqHashSha1( if(dwToRead == 0) break; - // Read the next chunk + // Read the next chunk if(!FileStream_Read(ha->pStream, &BeginBuffer, pbDigestBuffer, dwToRead)) { - FREEMEM(pbDigestBuffer); + STORM_FREE(pbDigestBuffer); return false; } @@ -415,7 +397,8 @@ static bool CalculateMpqHashSha1( sha1_done(&sha1_state_temp, sha1_tail0); memcpy(&sha1_state_temp, &sha1_state, sizeof(hash_state)); - AddTailToSha1(&sha1_state_temp, GetPlainFileName(ha->pStream->szFileName)); + GetPlainAnsiFileName(FileStream_GetFileName(ha->pStream), szPlainName); + AddTailToSha1(&sha1_state_temp, szPlainName); sha1_done(&sha1_state_temp, sha1_tail1); memcpy(&sha1_state_temp, &sha1_state, sizeof(hash_state)); @@ -423,16 +406,15 @@ static bool CalculateMpqHashSha1( sha1_done(&sha1_state_temp, sha1_tail2); // Finalize the MD5 hash - FREEMEM(pbDigestBuffer); + STORM_FREE(pbDigestBuffer); return true; } static int VerifyRawMpqData( - TMPQArchive * ha, - ULONGLONG ByteOffset, - DWORD dwDataSize) + TMPQArchive * ha, + ULONGLONG ByteOffset, + DWORD dwDataSize) { - hash_state md5_state; ULONGLONG DataOffset = ha->MpqPos + ByteOffset; LPBYTE pbDataChunk; LPBYTE pbMD5Array1; // Calculated MD5 array @@ -443,21 +425,23 @@ static int VerifyRawMpqData( DWORD dwMD5Size; int nError = ERROR_SUCCESS; + // Don't verify zero-sized blocks + if(dwDataSize == 0) + return ERROR_SUCCESS; + // Get the number of data chunks to calculate MD5 assert(dwChunkSize != 0); - dwChunkCount = dwDataSize / dwChunkSize; - if(dwDataSize % dwChunkSize) - dwChunkCount++; + dwChunkCount = ((dwDataSize - 1) / dwChunkSize) + 1; dwMD5Size = dwChunkCount * MD5_DIGEST_SIZE; // Allocate space for data chunk and for the MD5 array - pbDataChunk = ALLOCMEM(BYTE, dwChunkSize); + pbDataChunk = STORM_ALLOC(BYTE, dwChunkSize); if(pbDataChunk == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Allocate space for MD5 array - pbMD5Array1 = ALLOCMEM(BYTE, dwMD5Size); - pbMD5Array2 = ALLOCMEM(BYTE, dwMD5Size); + pbMD5Array1 = STORM_ALLOC(BYTE, dwMD5Size); + pbMD5Array2 = STORM_ALLOC(BYTE, dwMD5Size); if(pbMD5Array1 == NULL || pbMD5Array2 == NULL) nError = ERROR_NOT_ENOUGH_MEMORY; @@ -479,9 +463,7 @@ static int VerifyRawMpqData( } // Calculate MD5 - md5_init(&md5_state); - md5_process(&md5_state, pbDataChunk, dwBytesInChunk); - md5_done(&md5_state, pbMD5); + CalculateDataBlockHash(pbDataChunk, dwBytesInChunk, pbMD5); // Move pointers and offsets DataOffset += dwBytesInChunk; @@ -508,17 +490,17 @@ static int VerifyRawMpqData( // Free memory and return result if(pbMD5Array2 != NULL) - FREEMEM(pbMD5Array2); + STORM_FREE(pbMD5Array2); if(pbMD5Array1 != NULL) - FREEMEM(pbMD5Array1); + STORM_FREE(pbMD5Array1); if(pbDataChunk != NULL) - FREEMEM(pbDataChunk); + STORM_FREE(pbDataChunk); return nError; } static DWORD VerifyWeakSignature( - TMPQArchive * ha, - PMPQ_SIGNATURE_INFO pSI) + TMPQArchive * ha, + PMPQ_SIGNATURE_INFO pSI) { BYTE RevSignature[MPQ_WEAK_SIGNATURE_SIZE]; BYTE Md5Digest[MD5_DIGEST_SIZE]; @@ -545,9 +527,9 @@ static DWORD VerifyWeakSignature( } static DWORD VerifyStrongSignatureWithKey( - unsigned char * reversed_signature, - unsigned char * padded_digest, - const char * szPublicKey) + unsigned char * reversed_signature, + unsigned char * padded_digest, + const char * szPublicKey) { rsa_key key; int result = 0; @@ -562,15 +544,15 @@ static DWORD VerifyStrongSignatureWithKey( // Verify the signature if(rsa_verify_simple(reversed_signature, MPQ_STRONG_SIGNATURE_SIZE, padded_digest, MPQ_STRONG_SIGNATURE_SIZE, &result, &key) != CRYPT_OK) return ERROR_VERIFY_FAILED; - + // Free the key and return result rsa_free(&key); return result ? ERROR_STRONG_SIGNATURE_OK : ERROR_STRONG_SIGNATURE_ERROR; } static DWORD VerifyStrongSignature( - TMPQArchive * ha, - PMPQ_SIGNATURE_INFO pSI) + TMPQArchive * ha, + PMPQ_SIGNATURE_INFO pSI) { unsigned char reversed_signature[MPQ_STRONG_SIGNATURE_SIZE]; unsigned char Sha1Digest_tail0[SHA1_DIGEST_SIZE]; @@ -631,10 +613,12 @@ static DWORD VerifyStrongSignature( return ERROR_STRONG_SIGNATURE_ERROR; } -//----------------------------------------------------------------------------- -// Public (exported) functions - -DWORD WINAPI SFileVerifyFile(HANDLE hMpq, const char * szFileName, DWORD dwFlags) +static DWORD VerifyFile( + HANDLE hMpq, + const char * szFileName, + LPDWORD pdwCrc32, + char * pMD5, + DWORD dwFlags) { hash_state md5_state; unsigned char * pFileMd5; @@ -647,12 +631,41 @@ DWORD WINAPI SFileVerifyFile(HANDLE hMpq, const char * szFileName, DWORD dwFlags DWORD dwSearchScope = SFILE_OPEN_FROM_MPQ; DWORD dwTotalBytes = 0; DWORD dwBytesRead; - DWORD dwCrc32; + DWORD dwCrc32 = 0; // Fix the open type for patched archives if(SFileIsPatchedArchive(hMpq)) dwSearchScope = SFILE_OPEN_PATCHED_FILE; + // If we have to verify raw data MD5, do it before file open + if(dwFlags & SFILE_VERIFY_RAW_MD5) + { + TMPQArchive * ha = (TMPQArchive *)hMpq; + + // Parse the base MPQ and all patches + while(ha != NULL) + { + // Does the archive have support for raw MD5? + if(ha->pHeader->dwRawChunkSize != 0) + { + // The file has raw MD5 if the archive supports it + dwVerifyResult |= VERIFY_FILE_HAS_RAW_MD5; + + // Find file entry for the file + pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale); + if(pFileEntry != NULL) + { + // If the file's raw MD5 doesn't match, don't bother with more checks + if(VerifyRawMpqData(ha, pFileEntry->ByteOffset, pFileEntry->dwCmpSize) != ERROR_SUCCESS) + return dwVerifyResult | VERIFY_FILE_RAW_MD5_ERROR; + } + } + + // Move to the next patch + ha = ha->haPatch; + } + } + // Attempt to open the file if(SFileOpenFileEx(hMpq, szFileName, dwSearchScope, &hFile)) { @@ -665,17 +678,6 @@ DWORD WINAPI SFileVerifyFile(HANDLE hMpq, const char * szFileName, DWORD dwFlags md5_init(&md5_state); dwCrc32 = crc32(0, Z_NULL, 0); - // If we have to verify raw data MD5, do it - if(dwFlags & SFILE_VERIFY_RAW_MD5) - { - if(hf->ha->pHeader->dwRawChunkSize != 0) - { - dwVerifyResult |= VERIFY_FILE_HAS_RAW_MD5; - if(SFileVerifyRawData(hMpq, SFILE_VERIFY_FILE, szFileName) != ERROR_SUCCESS) - dwVerifyResult |= VERIFY_FILE_RAW_MD5_ERROR; - } - } - // Also turn on sector checksum verification if(dwFlags & SFILE_VERIFY_SECTOR_CRC) hf->bCheckSectorCRCs = true; @@ -695,7 +697,7 @@ DWORD WINAPI SFileVerifyFile(HANDLE hMpq, const char * szFileName, DWORD dwFlags // Update CRC32 value if(dwFlags & SFILE_VERIFY_FILE_CRC) dwCrc32 = crc32(dwCrc32, Buffer, dwBytesRead); - + // Update MD5 value if(dwFlags & SFILE_VERIFY_FILE_MD5) md5_process(&md5_state, Buffer, dwBytesRead); @@ -766,9 +768,54 @@ DWORD WINAPI SFileVerifyFile(HANDLE hMpq, const char * szFileName, DWORD dwFlags dwVerifyResult |= VERIFY_OPEN_ERROR; } + // If the caller required CRC32 and/or MD5, give it to him + if(pdwCrc32 != NULL) + *pdwCrc32 = dwCrc32; + if(pMD5 != NULL) + memcpy(pMD5, md5, MD5_DIGEST_SIZE); + return dwVerifyResult; } +//----------------------------------------------------------------------------- +// Public (exported) functions + +bool WINAPI SFileGetFileChecksums(HANDLE hMpq, const char * szFileName, LPDWORD pdwCrc32, char * pMD5) +{ + DWORD dwVerifyResult; + DWORD dwVerifyFlags = 0; + + if(pdwCrc32 != NULL) + dwVerifyFlags |= SFILE_VERIFY_FILE_CRC; + if(pMD5 != NULL) + dwVerifyFlags |= SFILE_VERIFY_FILE_MD5; + + dwVerifyResult = VerifyFile(hMpq, + szFileName, + pdwCrc32, + pMD5, + dwVerifyFlags); + + // If verification failed, return zero + if(dwVerifyResult & VERIFY_FILE_ERROR_MASK) + { + SetLastError(ERROR_FILE_CORRUPT); + return false; + } + + return true; +} + + +DWORD WINAPI SFileVerifyFile(HANDLE hMpq, const char * szFileName, DWORD dwFlags) +{ + return VerifyFile(hMpq, + szFileName, + NULL, + NULL, + dwFlags); +} + // Verifies raw data of the archive Only works for MPQs version 4 or newer int WINAPI SFileVerifyRawData(HANDLE hMpq, DWORD dwWhatToVerify, const char * szFileName) { @@ -788,54 +835,54 @@ int WINAPI SFileVerifyRawData(HANDLE hMpq, DWORD dwWhatToVerify, const char * sz // If we have to verify MPQ header, do it switch(dwWhatToVerify) { - case SFILE_VERIFY_MPQ_HEADER: - - // Only if the header is of version 4 or newer - if(pHeader->dwHeaderSize >= (MPQ_HEADER_SIZE_V4 - MD5_DIGEST_SIZE)) - return VerifyRawMpqData(ha, 0, MPQ_HEADER_SIZE_V4 - MD5_DIGEST_SIZE); - return ERROR_SUCCESS; + case SFILE_VERIFY_MPQ_HEADER: - case SFILE_VERIFY_HET_TABLE: + // Only if the header is of version 4 or newer + if(pHeader->dwHeaderSize >= (MPQ_HEADER_SIZE_V4 - MD5_DIGEST_SIZE)) + return VerifyRawMpqData(ha, 0, MPQ_HEADER_SIZE_V4 - MD5_DIGEST_SIZE); + return ERROR_SUCCESS; - // Only if we have HET table - if(pHeader->HetTablePos64 && pHeader->HetTableSize64) - return VerifyRawMpqData(ha, pHeader->HetTablePos64, (DWORD)pHeader->HetTableSize64); - return ERROR_SUCCESS; + case SFILE_VERIFY_HET_TABLE: - case SFILE_VERIFY_BET_TABLE: + // Only if we have HET table + if(pHeader->HetTablePos64 && pHeader->HetTableSize64) + return VerifyRawMpqData(ha, pHeader->HetTablePos64, (DWORD)pHeader->HetTableSize64); + return ERROR_SUCCESS; - // Only if we have BET table - if(pHeader->BetTablePos64 && pHeader->BetTableSize64) - return VerifyRawMpqData(ha, pHeader->BetTablePos64, (DWORD)pHeader->BetTableSize64); - return ERROR_SUCCESS; + case SFILE_VERIFY_BET_TABLE: - case SFILE_VERIFY_HASH_TABLE: + // Only if we have BET table + if(pHeader->BetTablePos64 && pHeader->BetTableSize64) + return VerifyRawMpqData(ha, pHeader->BetTablePos64, (DWORD)pHeader->BetTableSize64); + return ERROR_SUCCESS; - // Hash table is not protected by MD5 - return ERROR_SUCCESS; + case SFILE_VERIFY_HASH_TABLE: - case SFILE_VERIFY_BLOCK_TABLE: + // Hash table is not protected by MD5 + return ERROR_SUCCESS; - // Block table is not protected by MD5 - return ERROR_SUCCESS; + case SFILE_VERIFY_BLOCK_TABLE: - case SFILE_VERIFY_HIBLOCK_TABLE: + // Block table is not protected by MD5 + return ERROR_SUCCESS; + + case SFILE_VERIFY_HIBLOCK_TABLE: - // It is unknown if the hi-block table is protected my MD5 or not. - return ERROR_SUCCESS; + // It is unknown if the hi-block table is protected my MD5 or not. + return ERROR_SUCCESS; - case SFILE_VERIFY_FILE: + case SFILE_VERIFY_FILE: - // Verify parameters - if(szFileName == NULL || *szFileName == 0) - return ERROR_INVALID_PARAMETER; + // Verify parameters + if(szFileName == NULL || *szFileName == 0) + return ERROR_INVALID_PARAMETER; - // Get the offset of a file - pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale); - if(pFileEntry == NULL) - return ERROR_FILE_NOT_FOUND; + // Get the offset of a file + pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale); + if(pFileEntry == NULL) + return ERROR_FILE_NOT_FOUND; - return VerifyRawMpqData(ha, pFileEntry->ByteOffset, pFileEntry->dwCmpSize); + return VerifyRawMpqData(ha, pFileEntry->ByteOffset, pFileEntry->dwCmpSize); } return ERROR_INVALID_PARAMETER; @@ -860,14 +907,14 @@ DWORD WINAPI SFileVerifyArchive(HANDLE hMpq) // Verify the signature switch(si.nSignatureType) { - case SIGNATURE_TYPE_NONE: - return ERROR_NO_SIGNATURE; + case SIGNATURE_TYPE_NONE: + return ERROR_NO_SIGNATURE; - case SIGNATURE_TYPE_WEAK: - return VerifyWeakSignature(ha, &si); + case SIGNATURE_TYPE_WEAK: + return VerifyWeakSignature(ha, &si); - case SIGNATURE_TYPE_STRONG: - return VerifyStrongSignature(ha, &si); + case SIGNATURE_TYPE_STRONG: + return VerifyStrongSignature(ha, &si); } return ERROR_VERIFY_FAILED; diff --git a/dep/StormLib/src/StormCommon.h b/dep/StormLib/src/StormCommon.h index 2f87656471e..57a89066711 100644 --- a/dep/StormLib/src/StormCommon.h +++ b/dep/StormLib/src/StormCommon.h @@ -15,52 +15,54 @@ #define __STORMCOMMON_H__ //----------------------------------------------------------------------------- -// Make sure that we include the compression library headers, -// when a source needs the functions +// Compression support -#ifdef __INCLUDE_COMPRESSION__ +// Include functions from Pkware Data Compression Library +#include "pklib/pklib.h" - #include "pklib/pklib.h" // Include functions from Pkware Data Compression Library +// Include functions from Huffmann compression +#include "huffman/huff.h" - #include "huffman/huff.h" // Include functions from Huffmann compression +// Include functions from IMA ADPCM compression +#include "adpcm/adpcm.h" - #include "adpcm/adpcm.h" // Include functions from IMA ADPCM compression +// Include functions from SPARSE compression +#include "sparse/sparse.h" - #include "sparse/sparse.h" // Include functions from SPARSE compression +// Include functions from LZMA compression +#include "lzma/C/LzmaEnc.h" +#include "lzma/C/LzmaDec.h" - #include "lzma/C/LzmaEnc.h" // Include functions from LZMA compression - #include "lzma/C/LzmaDec.h" - - #ifndef __SYS_ZLIB - #include "zlib/zlib.h" // Include functions from zlib - #else - #include // If zlib is available on system, use this instead - #endif - - #ifndef __SYS_BZLIB - #include "bzip2/bzlib.h" // Include functions from bzlib - #else - #include // If bzlib is available on system, use this instead - #endif +// Include functions from zlib +#ifndef __SYS_ZLIB +#include "zlib/zlib.h" +#else +#include +#endif +// Include functions from bzlib +#ifndef __SYS_BZLIB +#include "bzip2/bzlib.h" +#else +#include #endif //----------------------------------------------------------------------------- -// Make sure that we include the cryptography headers, -// when a source needs the functions +// Cryptography support -#ifdef __INCLUDE_CRYPTOGRAPHY__ +// Headers from LibTomCrypt #include "libtomcrypt/src/headers/tomcrypt.h" + +// For HashStringJenkins #include "jenkins/lookup.h" -#endif //----------------------------------------------------------------------------- // StormLib private defines -#define ID_MPQ_FILE 0x46494c45 // Used internally for checking TMPQFile ('FILE') +#define ID_MPQ_FILE 0x46494c45 // Used internally for checking TMPQFile ('FILE') #define MPQ_WEAK_SIGNATURE_SIZE 64 -#define MPQ_STRONG_SIGNATURE_SIZE 256 +#define MPQ_STRONG_SIGNATURE_SIZE 256 // Prevent problems with CRT "min" and "max" functions, // as they are not defined on all platforms @@ -70,18 +72,48 @@ // Macro for building 64-bit file offset from two 32-bit #define MAKE_OFFSET64(hi, lo) (((ULONGLONG)hi << 32) | lo) +//----------------------------------------------------------------------------- +// Memory management +// +// We use our own macros for allocating/freeing memory. If you want +// to redefine them, please keep the following rules +// +// - The memory allocation must return NULL if not enough memory +// (i.e not to throw exception) +// - It is not necessary to fill the allocated buffer with zeros +// - Memory freeing function doesn't have to test the pointer to NULL. +// + +#if defined(_MSC_VER) && defined(_DEBUG) +__inline void * DebugMalloc(char * /* szFile */, int /* nLine */, size_t nSize) +{ + // return new BYTE[nSize]; + return HeapAlloc(GetProcessHeap(), 0, nSize); +} + +__inline void DebugFree(void * ptr) +{ + // delete [] ptr; + HeapFree(GetProcessHeap(), 0, ptr); +} + +#define STORM_ALLOC(type, nitems) (type *)DebugMalloc(__FILE__, __LINE__, (nitems) * sizeof(type)) +#define STORM_FREE(ptr) DebugFree(ptr) +#else + +#define STORM_ALLOC(type, nitems) (type *)malloc((nitems) * sizeof(type)) +#define STORM_FREE(ptr) free(ptr) + +#endif + //----------------------------------------------------------------------------- // StormLib internal global variables -extern DWORD dwGlobalFlags; // Global StormLib flags -extern LCID lcFileLocale; // Preferred file locale +extern LCID lcFileLocale; // Preferred file locale //----------------------------------------------------------------------------- // Encryption and decryption functions -#define MPQ_KEY_HASH_TABLE 0xC3AF3770 // Obtained by HashString("(hash table)", MPQ_HASH_FILE_KEY) -#define MPQ_KEY_BLOCK_TABLE 0xEC83B3A3 // Obtained by HashString("(block table)", MPQ_HASH_FILE_KEY) - #define MPQ_HASH_TABLE_INDEX 0x000 #define MPQ_HASH_NAME_A 0x100 #define MPQ_HASH_NAME_B 0x200 @@ -89,14 +121,16 @@ extern LCID lcFileLocale; // Preferred file locale DWORD HashString(const char * szFileName, DWORD dwHashType); -void InitializeMpqCryptography(); +void InitializeMpqCryptography(); DWORD GetHashTableSizeForFileCount(DWORD dwFileCount); bool IsPseudoFileName(const char * szFileName, LPDWORD pdwFileIndex); ULONGLONG HashStringJenkins(const char * szFileName); -void ConvertMpqHeaderToFormat4(TMPQArchive * ha, ULONGLONG FileSize, DWORD dwFlags); +int ConvertMpqHeaderToFormat4(TMPQArchive * ha, ULONGLONG FileSize, DWORD dwFlags); + +DWORD GetDefaultSpecialFileFlags(TMPQArchive * ha, DWORD dwFileSize); void EncryptMpqBlock(void * pvFileBlock, DWORD dwLength, DWORD dwKey); void DecryptMpqBlock(void * pvFileBlock, DWORD dwLength, DWORD dwKey); @@ -105,6 +139,10 @@ DWORD DetectFileKeyBySectorSize(LPDWORD SectorOffsets, DWORD decrypted); DWORD DetectFileKeyByContent(void * pvFileContent, DWORD dwFileSize); DWORD DecryptFileKey(const char * szFileName, ULONGLONG MpqPos, DWORD dwFileSize, DWORD dwFlags); +bool IsValidMD5(LPBYTE pbMd5); +bool VerifyDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE expected_md5); +void CalculateDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE md5_hash); + //----------------------------------------------------------------------------- // Handle validation functions @@ -119,9 +157,12 @@ TMPQHash * GetNextHashEntry(TMPQArchive * ha, TMPQHash * pFirstHash, TMPQHash * DWORD AllocateHashEntry(TMPQArchive * ha, TFileEntry * pFileEntry); DWORD AllocateHetEntry(TMPQArchive * ha, TFileEntry * pFileEntry); -void FindFreeMpqSpace(TMPQArchive * ha, ULONGLONG * pMpqPos); +void FindFreeMpqSpace(TMPQArchive * ha, ULONGLONG * pFreeSpacePos); -// Functions that load the HET abd BET tables +// Functions that loads and verifies MPQ data bitmap +int LoadMpqDataBitmap(TMPQArchive * ha, ULONGLONG FileSize, bool * pbFileIsComplete); + +// Functions that load the HET and BET tables int CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize); int LoadAnyHashTable(TMPQArchive * ha); int BuildFileTable(TMPQArchive * ha, ULONGLONG FileSize); @@ -145,7 +186,12 @@ void AllocateFileName(TFileEntry * pFileEntry, const char * szFileName); // Allocates new file entry in the MPQ tables. Reuses existing, if possible TFileEntry * FindFreeFileEntry(TMPQArchive * ha); TFileEntry * AllocateFileEntry(TMPQArchive * ha, const char * szFileName, LCID lcLocale); -void FreeFileEntry(TMPQArchive * ha, TFileEntry * pFileEntry); +int RenameFileEntry(TMPQArchive * ha, TFileEntry * pFileEntry, const char * szNewFileName); +void ClearFileEntry(TMPQArchive * ha, TFileEntry * pFileEntry); +int FreeFileEntry(TMPQArchive * ha, TFileEntry * pFileEntry); + +// Invalidates entries for (listfile) and (attributes) +void InvalidateInternalFiles(TMPQArchive * ha); //----------------------------------------------------------------------------- // Common functions - MPQ File @@ -164,7 +210,7 @@ int WriteMemDataMD5(TFileStream * pStream, ULONGLONG RawDataOffs, void * pvRawD int WriteMpqDataMD5(TFileStream * pStream, ULONGLONG RawDataOffs, DWORD dwRawDataSize, DWORD dwChunkSize); void FreeMPQFile(TMPQFile *& hf); -bool IsPatchData(const void * pvData, DWORD cbData, LPDWORD pdwPatchedFileSize); +bool IsIncrementalPatchFile(const void * pvData, DWORD cbData, LPDWORD pdwPatchedFileSize); int PatchFileData(TMPQFile * hf); void FreeMPQArchive(TMPQArchive *& ha); @@ -173,32 +219,33 @@ void FreeMPQArchive(TMPQArchive *& ha); // Utility functions bool CheckWildCard(const char * szString, const char * szWildCard); -const char * GetPlainFileName(const char * szFileName); +const char * GetPlainFileNameA(const char * szFileName); +const TCHAR * GetPlainFileNameT(const TCHAR * szFileName); bool IsInternalMpqFileName(const char * szFileName); //----------------------------------------------------------------------------- // Support for adding files to the MPQ int SFileAddFile_Init( - TMPQArchive * ha, - const char * szArchivedName, - ULONGLONG ft, - DWORD dwFileSize, - LCID lcLocale, - DWORD dwFlags, - TMPQFile ** phf - ); + TMPQArchive * ha, + const char * szArchivedName, + ULONGLONG ft, + DWORD dwFileSize, + LCID lcLocale, + DWORD dwFlags, + TMPQFile ** phf + ); int SFileAddFile_Write( - TMPQFile * hf, - const void * pvData, - DWORD dwSize, - DWORD dwCompression - ); + TMPQFile * hf, + const void * pvData, + DWORD dwSize, + DWORD dwCompression + ); int SFileAddFile_Finish( - TMPQFile * hf - ); + TMPQFile * hf + ); //----------------------------------------------------------------------------- // Attributes support diff --git a/dep/StormLib/src/StormLib.h b/dep/StormLib/src/StormLib.h index ac963cf16f2..e5e15369771 100644 --- a/dep/StormLib/src/StormLib.h +++ b/dep/StormLib/src/StormLib.h @@ -63,338 +63,346 @@ /* 20.08.10 7.20 Lad Support for opening multiple MPQs in patch mode */ /* 20.09.10 8.00 Lad MPQs v 4, HET and BET tables */ /* 07.01.11 8.01 Lad Write support for MPQs v 3 and 4 */ +/* 15.09.11 8.04 Lad Bug fixes, testing for Diablo III MPQs */ +/* 26.04.12 8.10 Lad Support for data map, added SFileGetArchiveBitmap */ +/* 29.05.12 8.20 Lad C-only interface */ /*****************************************************************************/ #ifndef __STORMLIB_H__ #define __STORMLIB_H__ #ifdef _MSC_VER -#pragma warning(disable:4668) // 'XXX' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' +#pragma warning(disable:4668) // 'XXX' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' #pragma warning(disable:4820) // 'XXX' : '2' bytes padding added after data member 'XXX::yyy' #endif +#include "StormPort.h" + #ifdef __cplusplus extern "C" { #endif -#include "StormPort.h" - //----------------------------------------------------------------------------- // Use the apropriate library // // The library type is encoded in the library name as the following // StormLibXYZ.lib -// +// // X - D for Debug version, R for Release version -// Y - A for ANSI version, U for Unicode version (Unicode version does not exist yet) -// Z - S for static C library, D for multithreaded DLL C-library +// Y - A for ANSI version, U for Unicode version +// Z - S for static-linked CRT library, D for multithreaded DLL CRT library // -#if defined(_MSC_VER) && !defined (__STORMLIB_SELF__) - #ifdef _DEBUG // DEBUG VERSIONS - #ifdef _DLL - #pragma comment(lib, "StormLibDAD.lib") // Debug Ansi Dynamic version - #else - #pragma comment(lib, "StormLibDAS.lib") // Debug Ansi Static version - #endif - #else // RELEASE VERSIONS - #ifdef _DLL - #pragma comment(lib, "StormLibRAD.lib") // Release Ansi Dynamic version - #else - #pragma comment(lib, "StormLibRAS.lib") // Release Ansi Static version - #endif - #endif +#if defined(_MSC_VER) && !defined(__STORMLIB_SELF__) + +#ifdef _DEBUG // DEBUG VERSIONS +#ifndef _UNICODE +#ifdef _DLL +#pragma comment(lib, "StormLibDAD.lib") // Debug Ansi CRT-DLL version +#else +#pragma comment(lib, "StormLibDAS.lib") // Debug Ansi CRT-LIB version +#endif +#else +#ifdef _DLL +#pragma comment(lib, "StormLibDUD.lib") // Debug Unicode CRT-DLL version +#else +#pragma comment(lib, "StormLibDUS.lib") // Debug Unicode CRT-LIB version +#endif +#endif +#else // RELEASE VERSIONS +#ifndef _UNICODE +#ifdef _DLL +#pragma comment(lib, "StormLibRAD.lib") // Release Ansi CRT-DLL version +#else +#pragma comment(lib, "StormLibRAS.lib") // Release Ansi CRT-LIB version +#endif +#else +#ifdef _DLL +#pragma comment(lib, "StormLibRUD.lib") // Release Unicode CRT-DLL version +#else +#pragma comment(lib, "StormLibRUS.lib") // Release Unicode CRT-LIB version +#endif +#endif +#endif + #endif //----------------------------------------------------------------------------- // Defines -#define ID_MPQ 0x1A51504D // MPQ archive header ID ('MPQ\x1A') -#define ID_MPQ_USERDATA 0x1B51504D // MPQ userdata entry ('MPQ\x1B') +#define ID_MPQ 0x1A51504D // MPQ archive header ID ('MPQ\x1A') +#define ID_MPQ_USERDATA 0x1B51504D // MPQ userdata entry ('MPQ\x1B') -#define ERROR_AVI_FILE 10000 // No MPQ file, but AVI file. -#define ERROR_UNKNOWN_FILE_KEY 10001 // Returned by SFileReadFile when can't find file key -#define ERROR_CHECKSUM_ERROR 10002 // Returned by SFileReadFile when sector CRC doesn't match -#define ERROR_INTERNAL_FILE 10003 // The given operation is not allowed on internal file -#define ERROR_BASE_FILE_MISSING 10004 // The file is present as incremental patch file, but base file is missing +#define ERROR_AVI_FILE 10000 // No MPQ file, but AVI file. +#define ERROR_UNKNOWN_FILE_KEY 10001 // Returned by SFileReadFile when can't find file key +#define ERROR_CHECKSUM_ERROR 10002 // Returned by SFileReadFile when sector CRC doesn't match +#define ERROR_INTERNAL_FILE 10003 // The given operation is not allowed on internal file +#define ERROR_BASE_FILE_MISSING 10004 // The file is present as incremental patch file, but base file is missing +#define ERROR_MARKED_FOR_DELETE 10005 // The file was marked as "deleted" in the MPQ // Values for SFileCreateArchive -#define HASH_TABLE_SIZE_MIN 0x00000004 // Minimum acceptable hash table size -#define HASH_TABLE_SIZE_DEFAULT 0x00001000 // Default hash table size for empty MPQs -#define HASH_TABLE_SIZE_MAX 0x00080000 // Maximum acceptable hash table size - -#define HASH_ENTRY_DELETED 0xFFFFFFFE // Block index for deleted entry in the hash table -#define HASH_ENTRY_FREE 0xFFFFFFFF // Block index for free entry in the hash table +#define HASH_TABLE_SIZE_MIN 0x00000004 // Minimum acceptable hash table size +#define HASH_TABLE_SIZE_DEFAULT 0x00001000 // Default hash table size for empty MPQs +#define HASH_TABLE_SIZE_MAX 0x00080000 // Maximum acceptable hash table size -#define HET_ENTRY_DELETED 0x80 // HET hash value for a deleted entry -#define HET_ENTRY_FREE 0x00 // HET hash value for free entry +#define HASH_ENTRY_DELETED 0xFFFFFFFE // Block index for deleted entry in the hash table +#define HASH_ENTRY_FREE 0xFFFFFFFF // Block index for free entry in the hash table -#define HASH_STATE_SIZE 0x60 // Size of LibTomCrypt's hash_state structure +#define HET_ENTRY_DELETED 0x80 // HET hash value for a deleted entry +#define HET_ENTRY_FREE 0x00 // HET hash value for free entry -#define MPQ_PATCH_PREFIX_LEN 0x20 // Maximum length of the patch prefix +#define HASH_STATE_SIZE 0x60 // Size of LibTomCrypt's hash_state structure -// Values for TFileStream::Flags -#define STREAM_FLAG_READ_ONLY 0x01 // The stream is read only -#define STREAM_FLAG_PART_FILE 0x02 // The stream is a PART file. -#define STREAM_FLAG_ENCRYPTED_FILE 0x04 // The stream is an encrypted MPQ (MPQE). +#define MPQ_PATCH_PREFIX_LEN 0x20 // Maximum length of the patch prefix // Values for SFileOpenArchive -#define SFILE_OPEN_HARD_DISK_FILE 2 // Open the archive on HDD -#define SFILE_OPEN_CDROM_FILE 3 // Open the archive only if it is on CDROM +#define SFILE_OPEN_HARD_DISK_FILE 2 // Open the archive on HDD +#define SFILE_OPEN_CDROM_FILE 3 // Open the archive only if it is on CDROM // Values for SFileOpenFile -#define SFILE_OPEN_FROM_MPQ 0x00000000 // Open the file from the MPQ archive -#define SFILE_OPEN_PATCHED_FILE 0x00000001 // Open the file from the MPQ archive -#define SFILE_OPEN_BY_INDEX 0x00000002 // The 'szFileName' parameter is actually the file index -#define SFILE_OPEN_ANY_LOCALE 0xFFFFFFFE // Reserved for StormLib internal use -#define SFILE_OPEN_LOCAL_FILE 0xFFFFFFFF // Open the file from the MPQ archive +#define SFILE_OPEN_FROM_MPQ 0x00000000 // Open the file from the MPQ archive +#define SFILE_OPEN_PATCHED_FILE 0x00000001 // Open the file from the MPQ archive +#define SFILE_OPEN_ANY_LOCALE 0xFFFFFFFE // Reserved for StormLib internal use +#define SFILE_OPEN_LOCAL_FILE 0xFFFFFFFF // Open a local file // Flags for TMPQArchive::dwFlags -#define MPQ_FLAG_READ_ONLY 0x00000001 // If set, the MPQ has been open for read-only access -#define MPQ_FLAG_CHANGED 0x00000002 // If set, the MPQ tables have been changed -#define MPQ_FLAG_PROTECTED 0x00000004 // Set on protected MPQs (like W3M maps) -#define MPQ_FLAG_CHECK_SECTOR_CRC 0x0000008 // Checking sector CRC when reading files -#define MPQ_FLAG_NEED_FIX_SIZE 0x00000010 // Used during opening the archive -#define MPQ_FLAG_LISTFILE_VALID 0x00000020 // Used when (listfile) has already been saved -#define MPQ_FLAG_ATTRIBS_VALID 0x00000040 // Used when (attributes) has already been saved - -// Return value for SFilGetFileSize and SFileSetFilePointer -#define SFILE_INVALID_SIZE 0xFFFFFFFF -#define SFILE_INVALID_POS 0xFFFFFFFF -#define SFILE_INVALID_ATTRIBUTES 0xFFFFFFFF +#define MPQ_FLAG_READ_ONLY 0x00000001 // If set, the MPQ has been open for read-only access +#define MPQ_FLAG_CHANGED 0x00000002 // If set, the MPQ tables have been changed +#define MPQ_FLAG_PROTECTED 0x00000004 // Set on protected MPQs (like W3M maps) +#define MPQ_FLAG_CHECK_SECTOR_CRC 0x00000008 // Checking sector CRC when reading files +#define MPQ_FLAG_NEED_FIX_SIZE 0x00000010 // Used during opening the archive +#define MPQ_FLAG_INV_LISTFILE 0x00000020 // If set, it means that the (listfile) has been invalidated +#define MPQ_FLAG_INV_ATTRIBUTES 0x00000040 // If set, it means that the (attributes) has been invalidated + +// Return value for SFileGetFileSize and SFileSetFilePointer +#define SFILE_INVALID_SIZE 0xFFFFFFFF +#define SFILE_INVALID_POS 0xFFFFFFFF +#define SFILE_INVALID_ATTRIBUTES 0xFFFFFFFF // Flags for SFileAddFile -#define MPQ_FILE_IMPLODE 0x00000100 // Implode method (By PKWARE Data Compression Library) -#define MPQ_FILE_COMPRESS 0x00000200 // Compress methods (By multiple methods) -#define MPQ_FILE_COMPRESSED 0x0000FF00 // File is compressed -#define MPQ_FILE_ENCRYPTED 0x00010000 // Indicates whether file is encrypted -#define MPQ_FILE_FIX_KEY 0x00020000 // File decryption key has to be fixed -#define MPQ_FILE_PATCH_FILE 0x00100000 // The file is a patch file. Raw file data begin with TPatchInfo structure -#define MPQ_FILE_SINGLE_UNIT 0x01000000 // File is stored as a single unit, rather than split into sectors (Thx, Quantam) -#define MPQ_FILE_DELETE_MARKER 0x02000000 // File is a deletion marker, indicating that the file no longer exists. - // The file is only 1 byte long and its name is a hash -#define MPQ_FILE_SECTOR_CRC 0x04000000 // File has checksums for each sector. - // Ignored if file is not compressed or imploded. -#define MPQ_FILE_EXISTS 0x80000000 // Set if file exists, reset when the file was deleted -#define MPQ_FILE_REPLACEEXISTING 0x80000000 // Replace when the file exist (SFileAddFile) +#define MPQ_FILE_IMPLODE 0x00000100 // Implode method (By PKWARE Data Compression Library) +#define MPQ_FILE_COMPRESS 0x00000200 // Compress methods (By multiple methods) +#define MPQ_FILE_COMPRESSED 0x0000FF00 // File is compressed +#define MPQ_FILE_ENCRYPTED 0x00010000 // Indicates whether file is encrypted +#define MPQ_FILE_FIX_KEY 0x00020000 // File decryption key has to be fixed +#define MPQ_FILE_PATCH_FILE 0x00100000 // The file is a patch file. Raw file data begin with TPatchInfo structure +#define MPQ_FILE_SINGLE_UNIT 0x01000000 // File is stored as a single unit, rather than split into sectors (Thx, Quantam) +#define MPQ_FILE_DELETE_MARKER 0x02000000 // File is a deletion marker. Used in MPQ patches, indicating that the file no longer exists. +#define MPQ_FILE_SECTOR_CRC 0x04000000 // File has checksums for each sector. +// Ignored if file is not compressed or imploded. +#define MPQ_FILE_EXISTS 0x80000000 // Set if file exists, reset when the file was deleted +#define MPQ_FILE_REPLACEEXISTING 0x80000000 // Replace when the file exist (SFileAddFile) #define MPQ_FILE_VALID_FLAGS (MPQ_FILE_IMPLODE | \ - MPQ_FILE_COMPRESS | \ - MPQ_FILE_ENCRYPTED | \ - MPQ_FILE_FIX_KEY | \ - MPQ_FILE_PATCH_FILE | \ - MPQ_FILE_SINGLE_UNIT | \ - MPQ_FILE_DELETE_MARKER | \ - MPQ_FILE_SECTOR_CRC | \ - MPQ_FILE_EXISTS) + MPQ_FILE_COMPRESS | \ + MPQ_FILE_ENCRYPTED | \ + MPQ_FILE_FIX_KEY | \ + MPQ_FILE_PATCH_FILE | \ + MPQ_FILE_SINGLE_UNIT | \ + MPQ_FILE_DELETE_MARKER | \ + MPQ_FILE_SECTOR_CRC | \ + MPQ_FILE_EXISTS) // Compression types for multiple compressions -#define MPQ_COMPRESSION_HUFFMANN 0x01 // Huffmann compression (used on WAVE files only) -#define MPQ_COMPRESSION_ZLIB 0x02 // ZLIB compression -#define MPQ_COMPRESSION_PKWARE 0x08 // PKWARE DCL compression -#define MPQ_COMPRESSION_BZIP2 0x10 // BZIP2 compression (added in Warcraft III) -#define MPQ_COMPRESSION_SPARSE 0x20 // Sparse compression (added in Starcraft 2) -#define MPQ_COMPRESSION_ADPCM_MONO 0x40 // IMA ADPCM compression (mono) -#define MPQ_COMPRESSION_ADPCM_STEREO 0x80 // IMA ADPCM compression (stereo) +#define MPQ_COMPRESSION_HUFFMANN 0x01 // Huffmann compression (used on WAVE files only) +#define MPQ_COMPRESSION_ZLIB 0x02 // ZLIB compression +#define MPQ_COMPRESSION_PKWARE 0x08 // PKWARE DCL compression +#define MPQ_COMPRESSION_BZIP2 0x10 // BZIP2 compression (added in Warcraft III) +#define MPQ_COMPRESSION_SPARSE 0x20 // Sparse compression (added in Starcraft 2) +#define MPQ_COMPRESSION_ADPCM_MONO 0x40 // IMA ADPCM compression (mono) +#define MPQ_COMPRESSION_ADPCM_STEREO 0x80 // IMA ADPCM compression (stereo) +#define MPQ_COMPRESSION_LZMA 0x12 // LZMA compression. Added in Starcraft 2. This value is NOT a combination of flags. +#define MPQ_COMPRESSION_NEXT_SAME 0xFFFFFFFF // Same compression -// For backward compatibility -#define MPQ_COMPRESSION_WAVE_MONO 0x40 // IMA ADPCM compression (mono) -#define MPQ_COMPRESSION_WAVE_STEREO 0x80 // IMA ADPCM compression (stereo) +// Constants for SFileAddWave +#define MPQ_WAVE_QUALITY_HIGH 0 // Best quality, the worst compression +#define MPQ_WAVE_QUALITY_MEDIUM 1 // Medium quality, medium compression +#define MPQ_WAVE_QUALITY_LOW 2 // Low quality, the best compression -// LZMA compression. Added in Starcraft 2. This value is NOT a combination of flags. -#define MPQ_COMPRESSION_LZMA 0x12 +// Signatures for HET and BET table +#define HET_TABLE_SIGNATURE 0x1A544548 // 'HET\x1a' +#define BET_TABLE_SIGNATURE 0x1A544542 // 'BET\x1a' +// Decryption keys for MPQ tables +#define MPQ_KEY_HASH_TABLE 0xC3AF3770 // Obtained by HashString("(hash table)", MPQ_HASH_FILE_KEY) +#define MPQ_KEY_BLOCK_TABLE 0xEC83B3A3 // Obtained by HashString("(block table)", MPQ_HASH_FILE_KEY) -// Constants for SFileAddWave -#define MPQ_WAVE_QUALITY_HIGH 0 // Best quality, the worst compression -#define MPQ_WAVE_QUALITY_MEDIUM 1 // Medium quality, medium compression -#define MPQ_WAVE_QUALITY_LOW 2 // Low quality, the best compression +// Block map defines +#define MPQ_DATA_BITMAP_SIGNATURE 0x33767470 // Signature of the MPQ data bitmap ('ptv3') // Constants for SFileGetFileInfo -#define SFILE_INFO_ARCHIVE_NAME 1 // MPQ size (value from header) -#define SFILE_INFO_ARCHIVE_SIZE 2 // MPQ size (value from header) -#define SFILE_INFO_MAX_FILE_COUNT 3 // Max number of files in the MPQ -#define SFILE_INFO_HASH_TABLE_SIZE 4 // Size of hash table, in entries -#define SFILE_INFO_BLOCK_TABLE_SIZE 5 // Number of entries in the block table -#define SFILE_INFO_SECTOR_SIZE 6 // Size of file sector (in bytes) -#define SFILE_INFO_HASH_TABLE 7 // Pointer to Hash table (TMPQHash *) -#define SFILE_INFO_BLOCK_TABLE 8 // Pointer to Block Table (TMPQBlock *) -#define SFILE_INFO_NUM_FILES 9 // Real number of files within archive -#define SFILE_INFO_STREAM_FLAGS 10 // Stream flags for the MPQ. See STREAM_FLAG_XXX -#define SFILE_INFO_IS_READ_ONLY 11 // TRUE of the MPQ was open as read only +#define SFILE_INFO_ARCHIVE_NAME 1 // MPQ size (value from header) +#define SFILE_INFO_ARCHIVE_SIZE 2 // MPQ size (value from header) +#define SFILE_INFO_MAX_FILE_COUNT 3 // Max number of files in the MPQ +#define SFILE_INFO_HASH_TABLE_SIZE 4 // Size of hash table, in entries +#define SFILE_INFO_BLOCK_TABLE_SIZE 5 // Number of entries in the block table +#define SFILE_INFO_SECTOR_SIZE 6 // Size of file sector (in bytes) +#define SFILE_INFO_HASH_TABLE 7 // Pointer to Hash table (TMPQHash *) +#define SFILE_INFO_BLOCK_TABLE 8 // Pointer to Block Table (TMPQBlock *) +#define SFILE_INFO_NUM_FILES 9 // Real number of files within archive +#define SFILE_INFO_STREAM_FLAGS 10 // Stream flags for the MPQ. See STREAM_FLAG_XXX +#define SFILE_INFO_IS_READ_ONLY 11 // TRUE of the MPQ was open as read only //------ -#define SFILE_INFO_HASH_INDEX 100 // Hash index of file in MPQ -#define SFILE_INFO_CODENAME1 101 // The first codename of the file -#define SFILE_INFO_CODENAME2 102 // The second codename of the file -#define SFILE_INFO_LOCALEID 103 // Locale ID of file in MPQ -#define SFILE_INFO_BLOCKINDEX 104 // Index to Block Table -#define SFILE_INFO_FILE_SIZE 105 // Original file size (from the block table) -#define SFILE_INFO_COMPRESSED_SIZE 106 // Compressed file size (from the block table) -#define SFILE_INFO_FLAGS 107 // File flags -#define SFILE_INFO_POSITION 108 // File position within archive -#define SFILE_INFO_KEY 109 // File decryption key -#define SFILE_INFO_KEY_UNFIXED 110 // Decryption key not fixed to file pos and size -#define SFILE_INFO_FILETIME 111 // TMPQFileTime - -#define LISTFILE_NAME "(listfile)" // Name of internal listfile -#define SIGNATURE_NAME "(signature)" // Name of internal signature -#define ATTRIBUTES_NAME "(attributes)" // Name of internal attributes file - -#define STORMLIB_VERSION 0x0801 // Current version of StormLib (8.00) -#define STORMLIB_VERSION_STRING "8.01" - -#define MPQ_FORMAT_VERSION_1 0 // Up to The Burning Crusade -#define MPQ_FORMAT_VERSION_2 1 // The Burning Crusade and newer -#define MPQ_FORMAT_VERSION_3 2 // WoW Cataclysm Beta -#define MPQ_FORMAT_VERSION_4 3 // WoW Cataclysm and newer +#define SFILE_INFO_HASH_INDEX 100 // Hash index of file in MPQ +#define SFILE_INFO_CODENAME1 101 // The first codename of the file +#define SFILE_INFO_CODENAME2 102 // The second codename of the file +#define SFILE_INFO_LOCALEID 103 // Locale ID of file in MPQ +#define SFILE_INFO_BLOCKINDEX 104 // Index to Block Table +#define SFILE_INFO_FILE_SIZE 105 // Original file size (from the block table) +#define SFILE_INFO_COMPRESSED_SIZE 106 // Compressed file size (from the block table) +#define SFILE_INFO_FLAGS 107 // File flags +#define SFILE_INFO_POSITION 108 // File position within archive +#define SFILE_INFO_KEY 109 // File decryption key +#define SFILE_INFO_KEY_UNFIXED 110 // Decryption key not fixed to file pos and size +#define SFILE_INFO_FILETIME 111 // TMPQFileTime +#define SFILE_INFO_PATCH_CHAIN 112 // Chain of patches + +#define LISTFILE_NAME "(listfile)" // Name of internal listfile +#define SIGNATURE_NAME "(signature)" // Name of internal signature +#define ATTRIBUTES_NAME "(attributes)" // Name of internal attributes file +#define PATCH_METADATA_NAME "(patch_metadata)" + +#define STORMLIB_VERSION 0x0814 // Current version of StormLib (8.10) +#define STORMLIB_VERSION_STRING "8.20" + +#define MPQ_FORMAT_VERSION_1 0 // Up to The Burning Crusade +#define MPQ_FORMAT_VERSION_2 1 // The Burning Crusade and newer +#define MPQ_FORMAT_VERSION_3 2 // WoW Cataclysm Beta +#define MPQ_FORMAT_VERSION_4 3 // WoW Cataclysm and newer // Flags for MPQ attributes -#define MPQ_ATTRIBUTE_CRC32 0x00000001 // The "(attributes)" contains CRC32 for each file -#define MPQ_ATTRIBUTE_FILETIME 0x00000002 // The "(attributes)" contains file time for each file -#define MPQ_ATTRIBUTE_MD5 0x00000004 // The "(attributes)" contains MD5 for each file -#define MPQ_ATTRIBUTE_ALL 0x00000007 // Summary mask +#define MPQ_ATTRIBUTE_CRC32 0x00000001 // The "(attributes)" contains CRC32 for each file +#define MPQ_ATTRIBUTE_FILETIME 0x00000002 // The "(attributes)" contains file time for each file +#define MPQ_ATTRIBUTE_MD5 0x00000004 // The "(attributes)" contains MD5 for each file +#define MPQ_ATTRIBUTE_PATCH_BIT 0x00000008 // The "(attributes)" contains a patch bit for each file +#define MPQ_ATTRIBUTE_ALL 0x0000000F // Summary mask -#define MPQ_ATTRIBUTES_V1 100 // (attributes) format version 1.00 +#define MPQ_ATTRIBUTES_V1 100 // (attributes) format version 1.00 // Flags for SFileOpenArchive -#define MPQ_OPEN_NO_LISTFILE 0x0010 // Don't load the internal listfile -#define MPQ_OPEN_NO_ATTRIBUTES 0x0020 // Don't open the attributes -#define MPQ_OPEN_FORCE_MPQ_V1 0x0040 // Always open the archive as MPQ v 1.00, ignore the "wFormatVersion" variable in the header -#define MPQ_OPEN_CHECK_SECTOR_CRC 0x0080 // On files with MPQ_FILE_SECTOR_CRC, the CRC will be checked when reading file -#define MPQ_OPEN_READ_ONLY 0x0100 // Open the archive for read-only access -#define MPQ_OPEN_ENCRYPTED 0x0200 // Opens an encrypted MPQ archive (Example: Starcraft II installation) +#define BASE_PROVIDER_FILE 0x00000000 // Base data source is a file +#define BASE_PROVIDER_MAP 0x00000001 // Base data source is memory-mapped file +#define BASE_PROVIDER_HTTP 0x00000002 // Base data source is a file on web server +#define BASE_PROVIDER_MASK 0x0000000F // Mask for base provider value + +#define STREAM_PROVIDER_LINEAR 0x00000000 // Stream is linear with no offset mapping +#define STREAM_PROVIDER_PARTIAL 0x00000010 // Stream is partial file (.part) +#define STREAM_PROVIDER_ENCRYPTED 0x00000020 // Stream is an encrypted MPQ +#define STREAM_PROVIDER_MASK 0x000000F0 // Mask for stream provider value + +#define STREAM_FLAG_READ_ONLY 0x00000100 // Stream is read only +#define STREAM_FLAG_WRITE_SHARE 0x00000200 // Allow write sharing when open for write +#define STREAM_FLAG_MASK 0x0000FF00 // Mask for stream flags +#define STREAM_OPTIONS_MASK 0x0000FFFF // Mask for all stream options + +#define MPQ_OPEN_NO_LISTFILE 0x00010000 // Don't load the internal listfile +#define MPQ_OPEN_NO_ATTRIBUTES 0x00020000 // Don't open the attributes +#define MPQ_OPEN_FORCE_MPQ_V1 0x00040000 // Always open the archive as MPQ v 1.00, ignore the "wFormatVersion" variable in the header +#define MPQ_OPEN_CHECK_SECTOR_CRC 0x00080000 // On files with MPQ_FILE_SECTOR_CRC, the CRC will be checked when reading file + +// Deprecated +#define MPQ_OPEN_READ_ONLY STREAM_FLAG_READ_ONLY +#define MPQ_OPEN_ENCRYPTED STREAM_PROVIDER_ENCRYPTED // Flags for SFileCreateArchive -#define MPQ_CREATE_ATTRIBUTES 0x00000001 // Also add the (attributes) file -#define MPQ_CREATE_ARCHIVE_V1 0x00000000 // Creates archive of version 1 (size up to 4GB) -#define MPQ_CREATE_ARCHIVE_V2 0x00010000 // Creates archive of version 2 (larger than 4 GB) -#define MPQ_CREATE_ARCHIVE_V3 0x00020000 // Creates archive of version 3 -#define MPQ_CREATE_ARCHIVE_V4 0x00030000 // Creates archive of version 4 -#define MPQ_CREATE_ARCHIVE_VMASK 0x000F0000 // Mask for archive version +#define MPQ_CREATE_ATTRIBUTES 0x00100000 // Also add the (attributes) file +#define MPQ_CREATE_ARCHIVE_V1 0x00000000 // Creates archive of version 1 (size up to 4GB) +#define MPQ_CREATE_ARCHIVE_V2 0x01000000 // Creates archive of version 2 (larger than 4 GB) +#define MPQ_CREATE_ARCHIVE_V3 0x02000000 // Creates archive of version 3 +#define MPQ_CREATE_ARCHIVE_V4 0x03000000 // Creates archive of version 4 +#define MPQ_CREATE_ARCHIVE_VMASK 0x0F000000 // Mask for archive version + +#define FLAGS_TO_FORMAT_SHIFT 24 // (MPQ_CREATE_ARCHIVE_V4 >> FLAGS_TO_FORMAT_SHIFT) => MPQ_FORMAT_VERSION_4 // Flags for SFileVerifyFile -#define SFILE_VERIFY_SECTOR_CRC 0x0001 // Verify sector checksum for the file, if available -#define SFILE_VERIFY_FILE_CRC 0x0002 // Verify file CRC, if available -#define SFILE_VERIFY_FILE_MD5 0x0004 // Verify file MD5, if available -#define SFILE_VERIFY_RAW_MD5 0x0008 // Verify raw file MD5, if available -#define SFILE_VERIFY_ALL 0x000F // Verify every checksum possible +#define SFILE_VERIFY_SECTOR_CRC 0x00000001 // Verify sector checksum for the file, if available +#define SFILE_VERIFY_FILE_CRC 0x00000002 // Verify file CRC, if available +#define SFILE_VERIFY_FILE_MD5 0x00000004 // Verify file MD5, if available +#define SFILE_VERIFY_RAW_MD5 0x00000008 // Verify raw file MD5, if available +#define SFILE_VERIFY_ALL 0x0000000F // Verify every checksum possible // Return values for SFileVerifyFile -#define VERIFY_OPEN_ERROR 0x0001 // Failed to open the file -#define VERIFY_READ_ERROR 0x0002 // Failed to read all data from the file -#define VERIFY_FILE_HAS_SECTOR_CRC 0x0004 // File has sector CRC -#define VERIFY_FILE_SECTOR_CRC_ERROR 0x0008 // Sector CRC check failed -#define VERIFY_FILE_HAS_CHECKSUM 0x0010 // File has CRC32 -#define VERIFY_FILE_CHECKSUM_ERROR 0x0020 // CRC32 check failed -#define VERIFY_FILE_HAS_MD5 0x0040 // File has data MD5 -#define VERIFY_FILE_MD5_ERROR 0x0080 // MD5 check failed -#define VERIFY_FILE_HAS_RAW_MD5 0x0100 // File has raw data MD5 -#define VERIFY_FILE_RAW_MD5_ERROR 0x0200 // Raw MD5 check failed +#define VERIFY_OPEN_ERROR 0x0001 // Failed to open the file +#define VERIFY_READ_ERROR 0x0002 // Failed to read all data from the file +#define VERIFY_FILE_HAS_SECTOR_CRC 0x0004 // File has sector CRC +#define VERIFY_FILE_SECTOR_CRC_ERROR 0x0008 // Sector CRC check failed +#define VERIFY_FILE_HAS_CHECKSUM 0x0010 // File has CRC32 +#define VERIFY_FILE_CHECKSUM_ERROR 0x0020 // CRC32 check failed +#define VERIFY_FILE_HAS_MD5 0x0040 // File has data MD5 +#define VERIFY_FILE_MD5_ERROR 0x0080 // MD5 check failed +#define VERIFY_FILE_HAS_RAW_MD5 0x0100 // File has raw data MD5 +#define VERIFY_FILE_RAW_MD5_ERROR 0x0200 // Raw MD5 check failed +#define VERIFY_FILE_ERROR_MASK (VERIFY_OPEN_ERROR | VERIFY_READ_ERROR | VERIFY_FILE_SECTOR_CRC_ERROR | VERIFY_FILE_CHECKSUM_ERROR | VERIFY_FILE_MD5_ERROR | VERIFY_FILE_RAW_MD5_ERROR) // Flags for SFileVerifyRawData (for MPQs version 4.0 or higher) -#define SFILE_VERIFY_MPQ_HEADER 0x0001 // Verify raw MPQ header -#define SFILE_VERIFY_HET_TABLE 0x0002 // Verify raw data of the HET table -#define SFILE_VERIFY_BET_TABLE 0x0003 // Verify raw data of the BET table -#define SFILE_VERIFY_HASH_TABLE 0x0004 // Verify raw data of the hash table -#define SFILE_VERIFY_BLOCK_TABLE 0x0005 // Verify raw data of the block table -#define SFILE_VERIFY_HIBLOCK_TABLE 0x0006 // Verify raw data of the hi-block table -#define SFILE_VERIFY_FILE 0x0007 // Verify raw data of a file +#define SFILE_VERIFY_MPQ_HEADER 0x0001 // Verify raw MPQ header +#define SFILE_VERIFY_HET_TABLE 0x0002 // Verify raw data of the HET table +#define SFILE_VERIFY_BET_TABLE 0x0003 // Verify raw data of the BET table +#define SFILE_VERIFY_HASH_TABLE 0x0004 // Verify raw data of the hash table +#define SFILE_VERIFY_BLOCK_TABLE 0x0005 // Verify raw data of the block table +#define SFILE_VERIFY_HIBLOCK_TABLE 0x0006 // Verify raw data of the hi-block table +#define SFILE_VERIFY_FILE 0x0007 // Verify raw data of a file // Return values for SFileVerifyArchive -#define ERROR_NO_SIGNATURE 0 // There is no signature in the MPQ -#define ERROR_VERIFY_FAILED 1 // There was an error during verifying signature (like no memory) -#define ERROR_WEAK_SIGNATURE_OK 2 // There is a weak signature and sign check passed -#define ERROR_WEAK_SIGNATURE_ERROR 3 // There is a weak signature but sign check failed -#define ERROR_STRONG_SIGNATURE_OK 4 // There is a strong signature and sign check passed -#define ERROR_STRONG_SIGNATURE_ERROR 5 // There is a strong signature but sign check failed +#define ERROR_NO_SIGNATURE 0 // There is no signature in the MPQ +#define ERROR_VERIFY_FAILED 1 // There was an error during verifying signature (like no memory) +#define ERROR_WEAK_SIGNATURE_OK 2 // There is a weak signature and sign check passed +#define ERROR_WEAK_SIGNATURE_ERROR 3 // There is a weak signature but sign check failed +#define ERROR_STRONG_SIGNATURE_OK 4 // There is a strong signature and sign check passed +#define ERROR_STRONG_SIGNATURE_ERROR 5 // There is a strong signature but sign check failed #ifndef MD5_DIGEST_SIZE -#define MD5_DIGEST_SIZE 0x10 +#define MD5_DIGEST_SIZE 0x10 #endif #ifndef SHA1_DIGEST_SIZE -#define SHA1_DIGEST_SIZE 0x14 // 160 bits +#define SHA1_DIGEST_SIZE 0x14 // 160 bits #endif #ifndef LANG_NEUTRAL -#define LANG_NEUTRAL 0x00 // Neutral locale +#define LANG_NEUTRAL 0x00 // Neutral locale #endif //----------------------------------------------------------------------------- // Callback functions // Values for compact callback -#define CCB_CHECKING_FILES 1 // Checking archive (dwParam1 = current, dwParam2 = total) -#define CCB_CHECKING_HASH_TABLE 2 // Checking hash table (dwParam1 = current, dwParam2 = total) -#define CCB_COPYING_NON_MPQ_DATA 3 // Copying non-MPQ data: No params used -#define CCB_COMPACTING_FILES 4 // Compacting archive (dwParam1 = current, dwParam2 = total) -#define CCB_CLOSING_ARCHIVE 5 // Closing archive: No params used - +#define CCB_CHECKING_FILES 1 // Checking archive (dwParam1 = current, dwParam2 = total) +#define CCB_CHECKING_HASH_TABLE 2 // Checking hash table (dwParam1 = current, dwParam2 = total) +#define CCB_COPYING_NON_MPQ_DATA 3 // Copying non-MPQ data: No params used +#define CCB_COMPACTING_FILES 4 // Compacting archive (dwParam1 = current, dwParam2 = total) +#define CCB_CLOSING_ARCHIVE 5 // Closing archive: No params used + typedef void (WINAPI * SFILE_ADDFILE_CALLBACK)(void * pvUserData, DWORD dwBytesWritten, DWORD dwTotalBytes, bool bFinalCall); typedef void (WINAPI * SFILE_COMPACT_CALLBACK)(void * pvUserData, DWORD dwWorkType, ULONGLONG BytesProcessed, ULONGLONG TotalBytes); +typedef struct TFileStream TFileStream; + //----------------------------------------------------------------------------- -// Stream support - structures - -struct TFileStream; - -typedef bool (*STREAM_GETPOS)( - TFileStream * pStream, // Pointer to an open stream - ULONGLONG & ByteOffset // Pointer to store current file position - ); - -typedef bool (*STREAM_READ)( - TFileStream * pStream, // Pointer to an open stream - ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position - void * pvBuffer, // Pointer to data to be read - DWORD dwBytesToRead // Number of bytes to read from the file - ); - -typedef bool (*STREAM_WRITE)( - TFileStream * pStream, // Pointer to an open stream - ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it writes to the current position - const void * pvBuffer, // Pointer to data to be written - DWORD dwBytesToWrite // Number of bytes to read from the file - ); - -typedef bool (*STREAM_GETSIZE)( - TFileStream * pStream, // Pointer to an open stream - ULONGLONG & FileSize // Receives the file size, in bytes - ); - -typedef bool (*STREAM_SETSIZE)( - TFileStream * pStream, // Pointer to an open stream - ULONGLONG FileSize // New size for the file, in bytes - ); - -// Common stream structure. Can be variable length -struct TFileStream -{ - ULONGLONG RawFilePos; // Current position in raw file - HANDLE hFile; // File handle. Do not use directly. - char szFileName[MAX_PATH];// Name of the file - BYTE StreamFlags; // See STREAM_FLAG_XXXX +// Structure for bit arrays used for HET and BET tables - STREAM_GETPOS StreamGetPos; // Pointer to function that returns current file position - STREAM_READ StreamRead; // Pointer to stream read function for this archive. Do not use directly. - STREAM_WRITE StreamWrite; // Pointer to stream write function for this archive. Do not use directly. - STREAM_GETSIZE StreamGetSize; // Pointer to function returning file size - STREAM_SETSIZE StreamSetSize; // Pointer to function changing file size +typedef struct _TBitArray +{ - // Extra members may follow -}; + DWORD NumberOfBits; // Total number of bits that are available + BYTE Elements[1]; // Array of elements (variable length) +} TBitArray; -//----------------------------------------------------------------------------- -// Structure for bit arrays used for HET and BET tables +void GetBits(TBitArray * array, unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, int nResultSize); +void SetBits(TBitArray * array, unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, int nResultSize); -struct TBitArray +// Structure for file bitmap. Used by SFileGetArchiveBitmap +typedef struct _TFileBitmap { - void GetBits(unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, int nResultSize); - void SetBits(unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, int nResultSize); + ULONGLONG StartOffset; // Starting offset of the file, covered by bitmap + ULONGLONG EndOffset; // Ending offset of the file, covered by bitmap + DWORD IsComplete; // If nonzero, no blocks are missing + DWORD BitmapSize; // Size of the file bitmap (in bytes) + DWORD BlockSize; // Size of one block, in bytes + DWORD Reserved; // Alignment - DWORD NumberOfBits; // Total number of bits that are available - BYTE Elements[1]; // Array of elements (variable length) -}; + // Followed by file bitmap (variable length), array of BYTEs) +} TFileBitmap; //----------------------------------------------------------------------------- // Structures related to MPQ format @@ -411,7 +419,7 @@ struct TBitArray #define MPQ_HEADER_SIZE_V3 0x44 #define MPQ_HEADER_SIZE_V4 0xD0 -struct TMPQUserData +typedef struct _TMPQUserData { // The ID_MPQ_USERDATA ('MPQ\x1B') signature DWORD dwID; @@ -424,7 +432,7 @@ struct TMPQUserData // Appears to be size of user data header (Starcraft II maps) DWORD cbUserDataHeader; -}; +} TMPQUserData; // MPQ file header // @@ -432,13 +440,13 @@ struct TMPQUserData // Reason: A 64-bit integer at the beginning of 3.0 part, // which is offset 0x2C #pragma pack(push, 1) -struct TMPQHeader +typedef struct _TMPQHeader { // The ID_MPQ ('MPQ\x1A') signature - DWORD dwID; + DWORD dwID; // Size of the archive header - DWORD dwHeaderSize; + DWORD dwHeaderSize; // 32-bit size of MPQ archive // This field is deprecated in the Burning Crusade MoPaQ format, and the size of the archive @@ -458,14 +466,14 @@ struct TMPQHeader // Offset to the beginning of the hash table, relative to the beginning of the archive. DWORD dwHashTablePos; - + // Offset to the beginning of the block table, relative to the beginning of the archive. DWORD dwBlockTablePos; - + // Number of entries in the hash table. Must be a power of two, and must be less than 2^16 for // the original MoPaQ format, or less than 2^20 for the Burning Crusade format. DWORD dwHashTableSize; - + // Number of entries in the block table DWORD dwBlockTableSize; @@ -510,7 +518,7 @@ struct TMPQHeader // Size of raw data chunk to calculate MD5. // MD5 of each data chunk follows the raw file data. - DWORD dwRawChunkSize; + DWORD dwRawChunkSize; // MD5 of MPQ tables unsigned char MD5_BlockTable[MD5_DIGEST_SIZE]; // MD5 of the block table before decryption @@ -519,16 +527,16 @@ struct TMPQHeader unsigned char MD5_BetTable[MD5_DIGEST_SIZE]; // MD5 of the BET table before decryption unsigned char MD5_HetTable[MD5_DIGEST_SIZE]; // MD5 of the HET table before decryption unsigned char MD5_MpqHeader[MD5_DIGEST_SIZE]; // MD5 of the MPQ header from signature to (including) MD5_HetTable -}; +} TMPQHeader; #pragma pack(pop) // Hash entry. All files in the archive are searched by their hashes. -struct TMPQHash +typedef struct _TMPQHash { // The hash of the file path, using method A. DWORD dwName1; - + // The hash of the file path, using method B. DWORD dwName2; @@ -557,46 +565,46 @@ struct TMPQHash // - FFFFFFFEh: Hash table entry is empty, but was valid at some point (a deleted file). // Does not terminate searches for a given file. DWORD dwBlockIndex; -}; +} TMPQHash; // File description block contains informations about the file -struct TMPQBlock +typedef struct _TMPQBlock { // Offset of the beginning of the file, relative to the beginning of the archive. DWORD dwFilePos; - + // Compressed file size DWORD dwCSize; - + // Only valid if the block is a file; otherwise meaningless, and should be 0. // If the file is compressed, this is the size of the uncompressed file data. - DWORD dwFSize; - + DWORD dwFSize; + // Flags for the file. See MPQ_FILE_XXXX constants - DWORD dwFlags; -}; + DWORD dwFlags; +} TMPQBlock; // Patch file information, preceding the sector offset table -struct TPatchInfo +typedef struct _TPatchInfo { - DWORD dwLength; // Length of patch info header, in bytes - DWORD dwFlags; // Flags. 0x80000000 = MD5 (?) - DWORD dwDataSize; // Uncompressed size of the patch file - BYTE md5[0x10]; // MD5 of the entire patch file after decompression + DWORD dwLength; // Length of patch info header, in bytes + DWORD dwFlags; // Flags. 0x80000000 = MD5 (?) + DWORD dwDataSize; // Uncompressed size of the patch file + BYTE md5[0x10]; // MD5 of the entire patch file after decompression // Followed by the sector table (variable length) -}; +} TPatchInfo; -// Header for PTCH files -struct TPatchHeader +// Header for PTCH files +typedef struct _TPatchHeader { //-- PATCH header ----------------------------------- DWORD dwSignature; // 'PTCH' DWORD dwSizeOfPatchData; // Size of the entire patch (decompressed) DWORD dwSizeBeforePatch; // Size of the file before patch DWORD dwSizeAfterPatch; // Size of file after patch - + //-- MD5 block -------------------------------------- DWORD dwMD5; // 'MD5_' DWORD dwMd5BlockSize; // Size of the MD5 block, including the signature and size itself @@ -609,14 +617,14 @@ struct TPatchHeader DWORD dwPatchType; // Type of patch ('BSD0' or 'COPY') // Followed by the patch data -}; +} TPatchHeader; #define SIZE_OF_XFRM_HEADER 0x0C // This is the combined file entry for maintaining file list in the MPQ. // This structure is combined from block table, hi-block table, // (attributes) file and from (listfile). -struct TFileEntry +typedef struct _TFileEntry { ULONGLONG ByteOffset; // Position of the file content in the MPQ, relative to the MPQ header ULONGLONG FileTime; // FileTime from the (attributes) file. 0 if not present. @@ -631,68 +639,97 @@ struct TFileEntry DWORD dwCrc32; // CRC32 from (attributes) file. 0 if not present. unsigned char md5[MD5_DIGEST_SIZE]; // File MD5 from the (attributes) file. 0 if not present. char * szFileName; // File name. NULL if not known. -}; +} TFileEntry; + +// Common header for HET and BET tables +typedef struct _TMPQExtTable +{ + DWORD dwSignature; // 'HET\x1A' or 'BET\x1A' + DWORD dwVersion; // Version. Seems to be always 1 + DWORD dwDataSize; // Size of the contained table + + // Followed by the table header + // Followed by the table data + +} TMPQExtTable; + +// +// MPQ data bitmap, can be found at (FileSize - sizeof(TMPQBlockMap)) +// +// There is bit map of the entire MPQ before TMPQBitmap. Each 0x4000-byte +// block is represented by one bit (including the last, eventually incomplete block). +// +typedef struct _TMPQBitmap +{ + DWORD dwSignature; // 'ptv3' (MPQ_BLOCK_MAP_SIGNATURE) + DWORD dwAlways3; // Unknown, seems to always have value of 3 + DWORD dwBuildNumber; // Game build number for that MPQ + DWORD dwMapOffsetLo; // Low 32-bits of the offset of the bit map + DWORD dwMapOffsetHi; // High 32-bits of the offset of the bit map + DWORD dwBlockSize; // Size of one block (usually 0x4000 bytes) +} TMPQBitmap; // Structure for parsed HET table -struct TMPQHetTable +typedef struct _TMPQHetTable { - TBitArray * pBetIndexes; // Bit array of indexes to BET tables - LPBYTE pHetHashes; // Array of HET hashes. Each entry has size of 1 byte - ULONGLONG AndMask64; // AND mask used for calculating file name hash - ULONGLONG OrMask64; // OR mask used for setting the highest bit of the file name hash - - DWORD dwIndexSizeTotal; // Total size of one entry in pBetIndexes (in bits) - DWORD dwIndexSizeExtra; // Extra bits in the entry in pBetIndexes - DWORD dwIndexSize; // Effective size of one entry in pBetIndexes (in bits) - DWORD dwMaxFileCount; // Maximum number of files in the MPQ - DWORD dwHashTableSize; // Number of entries in pBetHashes - DWORD dwHashBitSize; // Effective number of bits in the hash -}; + TBitArray * pBetIndexes; // Bit array of indexes to BET tables + LPBYTE pHetHashes; // Array of HET hashes. Each entry has size of 1 byte + ULONGLONG AndMask64; // AND mask used for calculating file name hash + ULONGLONG OrMask64; // OR mask used for setting the highest bit of the file name hash + + DWORD dwIndexSizeTotal; // Total size of one entry in pBetIndexes (in bits) + DWORD dwIndexSizeExtra; // Extra bits in the entry in pBetIndexes + DWORD dwIndexSize; // Effective size of one entry in pBetIndexes (in bits) + DWORD dwMaxFileCount; // Maximum number of files in the MPQ + DWORD dwHashTableSize; // Number of entries in pBetHashes + DWORD dwHashBitSize; // Effective number of bits in the hash +} TMPQHetTable; // Structure for parsed BET table -struct TMPQBetTable +typedef struct _TMPQBetTable { - TBitArray * pBetHashes; // Array of BET hashes - TBitArray * pFileTable; // Bit-based file table - LPDWORD pFileFlags; // Array of file flags - - DWORD dwTableEntrySize; // Size of one table entry, in bits - DWORD dwBitIndex_FilePos; // Bit index of the file position in the table entry - DWORD dwBitIndex_FileSize; // Bit index of the file size in the table entry - DWORD dwBitIndex_CmpSize; // Bit index of the compressed size in the table entry - DWORD dwBitIndex_FlagIndex; // Bit index of the flag index in the table entry - DWORD dwBitIndex_Unknown; // Bit index of ??? in the table entry - DWORD dwBitCount_FilePos; // Size of file offset (in bits) within table entry - DWORD dwBitCount_FileSize; // Size of file size (in bits) within table entry - DWORD dwBitCount_CmpSize; // Size of compressed file size (in bits) within table entry - DWORD dwBitCount_FlagIndex; // Size of flag index (in bits) within table entry - DWORD dwBitCount_Unknown; // Size of ??? (in bits) within table entry - DWORD dwBetHashSizeTotal; // Total size of bet hash - DWORD dwBetHashSizeExtra; // Extra bits in the bet hash - DWORD dwBetHashSize; // Effective size of the bet hash - DWORD dwMaxFileCount; // Number of files (usually equal to maximum number of files) - DWORD dwFlagCount; // Number of entries in pFileFlags -}; + TBitArray * pBetHashes; // Array of BET hashes + TBitArray * pFileTable; // Bit-based file table + LPDWORD pFileFlags; // Array of file flags + + DWORD dwTableEntrySize; // Size of one table entry, in bits + DWORD dwBitIndex_FilePos; // Bit index of the file position in the table entry + DWORD dwBitIndex_FileSize; // Bit index of the file size in the table entry + DWORD dwBitIndex_CmpSize; // Bit index of the compressed size in the table entry + DWORD dwBitIndex_FlagIndex; // Bit index of the flag index in the table entry + DWORD dwBitIndex_Unknown; // Bit index of ??? in the table entry + DWORD dwBitCount_FilePos; // Size of file offset (in bits) within table entry + DWORD dwBitCount_FileSize; // Size of file size (in bits) within table entry + DWORD dwBitCount_CmpSize; // Size of compressed file size (in bits) within table entry + DWORD dwBitCount_FlagIndex; // Size of flag index (in bits) within table entry + DWORD dwBitCount_Unknown; // Size of ??? (in bits) within table entry + DWORD dwBetHashSizeTotal; // Total size of bet hash + DWORD dwBetHashSizeExtra; // Extra bits in the bet hash + DWORD dwBetHashSize; // Effective size of the bet hash + DWORD dwFileCount; // Number of files (usually equal to maximum number of files) + DWORD dwFlagCount; // Number of entries in pFileFlags +} TMPQBetTable; // Archive handle structure -struct TMPQArchive +typedef struct _TMPQArchive { TFileStream * pStream; // Open stream for the MPQ ULONGLONG UserDataPos; // Position of user data (relative to the begin of the file) ULONGLONG MpqPos; // MPQ header offset (relative to the begin of the file) - TMPQArchive * haPatch; // Pointer to patch archive, if any - TMPQArchive * haBase; // Pointer to base ("previous version") archive, if any + struct _TMPQArchive * haPatch; // Pointer to patch archive, if any + struct _TMPQArchive * haBase; // Pointer to base ("previous version") archive, if any char szPatchPrefix[MPQ_PATCH_PREFIX_LEN]; // Prefix for file names in patch MPQs size_t cchPatchPrefix; // Length of the patch prefix, in characters TMPQUserData * pUserData; // MPQ user data (NULL if not present in the file) TMPQHeader * pHeader; // MPQ file header + TMPQBitmap * pBitmap; // MPQ bitmap TMPQHash * pHashTable; // Hash table TMPQHetTable * pHetTable; // Het table TFileEntry * pFileTable; // File table - + TMPQUserData UserData; // MPQ user data. Valid only when ID_MPQ_USERDATA has been found BYTE HeaderData[MPQ_HEADER_SIZE_V4]; // Storage for MPQ header @@ -705,10 +742,10 @@ struct TMPQArchive DWORD dwFileFlags2; // Flags for (attributes) DWORD dwAttrFlags; // Flags for the (attributes) file, see MPQ_ATTRIBUTE_XXX DWORD dwFlags; // See MPQ_FLAG_XXXXX -}; +} TMPQArchive; // File handle structure -struct TMPQFile +typedef struct _TMPQFile { TFileStream * pStream; // File stream. Only used on local files TMPQArchive * ha; // Archive handle @@ -719,14 +756,14 @@ struct TMPQFile ULONGLONG MpqFilePos; // Offset in MPQ archive (relative to MPQ header) DWORD dwMagic; // 'FILE' - TMPQFile * hfPatchFile; // Pointer to opened patch file + struct _TMPQFile * hfPatchFile; // Pointer to opened patch file TPatchHeader * pPatchHeader; // Patch header. Only used if the file is a patch file LPBYTE pbFileData; // Loaded and patched file data. Only used if the file is a patch file DWORD cbFileData; // Size of loaded patched data TPatchInfo * pPatchInfo; // Patch info block, preceding the sector table - DWORD * SectorOffsets; // Position of each file sector, relative to the begin of the file. Only for compressed files. - DWORD * SectorChksums; // Array of ADLER32 values for each sector + DWORD * SectorOffsets; // Position of each file sector, relative to the begin of the file. Only for compressed files. + DWORD * SectorChksums; // Array of sector checksums (either ADLER32 or MD5) values for each file sector DWORD dwSectorCount; // Number of sectors in the file DWORD dwPatchedFileSize; // Size of patched file. Used when saving patch file to the MPQ DWORD dwDataSize; // Size of data in the file (on patch files, this differs from file size in block table entry) @@ -738,11 +775,13 @@ struct TMPQFile unsigned char hctx[HASH_STATE_SIZE];// Hash state for MD5. Used when saving file to MPQ DWORD dwCrc32; // CRC32 value, used when saving file to MPQ + bool bLoadedSectorCRCs; // If true, we already tried to load sector CRCs bool bCheckSectorCRCs; // If true, then SFileReadFile will check sector CRCs when reading the file bool bIsWriteHandle; // If true, this handle has been created by SFileCreateFile bool bErrorOccured; // If true, then at least one error occured during saving the file to the archive -}; +} TMPQFile; +// Structure for SFileFindFirstFile and SFileFindNextFile typedef struct _SFILE_FIND_DATA { char cFileName[MAX_PATH]; // Full name of the found file @@ -758,53 +797,40 @@ typedef struct _SFILE_FIND_DATA } SFILE_FIND_DATA, *PSFILE_FIND_DATA; -//----------------------------------------------------------------------------- -// Memory management -// -// We use our own macros for allocating/freeing memory. If you want -// to redefine them, please keep the following rules -// -// - The memory allocation must return NULL if not enough memory -// (i.e not to throw exception) -// - It is not necessary to fill the allocated buffer with zeros -// - Memory freeing function doesn't have to test the pointer to NULL. -// - -#if defined(_MSC_VER) && defined(_DEBUG) -__inline void * DebugMalloc(char * /* szFile */, int /* nLine */, size_t nSize) -{ -// return new BYTE[nSize]; - return HeapAlloc(GetProcessHeap(), 0, nSize); -} - -__inline void DebugFree(void * ptr) +typedef struct _SFILE_CREATE_MPQ { -// delete [] ptr; - HeapFree(GetProcessHeap(), 0, ptr); -} - -#define ALLOCMEM(type, nitems) (type *)DebugMalloc(__FILE__, __LINE__, (nitems) * sizeof(type)) -#define FREEMEM(ptr) DebugFree(ptr) -#else - -#define ALLOCMEM(type, nitems) (type *)malloc((nitems) * sizeof(type)) -#define FREEMEM(ptr) free(ptr) - -#endif + DWORD cbSize; // Size of this structure, in bytes + DWORD dwMpqVersion; // Version of the MPQ to be created + void *pvUserData; // Reserved, must be NULL + DWORD cbUserData; // Reserved, must be 0 + DWORD dwStreamFlags; // Stream flags for creating the MPQ + DWORD dwFileFlags1; // File flags for (listfile). 0 = default + DWORD dwFileFlags2; // File flags for (attributes). 0 = default + DWORD dwAttrFlags; // Flags for the (attributes) file. If 0, no attributes will be created + DWORD dwSectorSize; // Sector size for compressed files + DWORD dwRawChunkSize; // Size of raw data chunk + DWORD dwMaxFileCount; // File limit for the MPQ + +} SFILE_CREATE_MPQ, *PSFILE_CREATE_MPQ; //----------------------------------------------------------------------------- // Stream support - functions -TFileStream * FileStream_CreateFile(const char * szFileName); -TFileStream * FileStream_OpenFile(const char * szFileName, bool bWriteAccess); -TFileStream * FileStream_OpenEncrypted(const char * szFileName); -bool FileStream_GetPos(TFileStream * pStream, ULONGLONG & ByteOffset); +TFileStream * FileStream_CreateFile(const TCHAR * szFileName, DWORD dwStreamFlags); +TFileStream * FileStream_OpenFile(const TCHAR * szFileName, DWORD dwStreamFlags); +TCHAR * FileStream_GetFileName(TFileStream * pStream); +bool FileStream_IsReadOnly(TFileStream * pStream); bool FileStream_Read(TFileStream * pStream, ULONGLONG * pByteOffset, void * pvBuffer, DWORD dwBytesToRead); bool FileStream_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const void * pvBuffer, DWORD dwBytesToWrite); -bool FileStream_GetLastWriteTime(TFileStream * pStream, ULONGLONG * pFT); -bool FileStream_GetSize(TFileStream * pStream, ULONGLONG & FileSize); +bool FileStream_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset); +bool FileStream_SetPos(TFileStream * pStream, ULONGLONG ByteOffset); +bool FileStream_GetSize(TFileStream * pStream, ULONGLONG * pFileSize); bool FileStream_SetSize(TFileStream * pStream, ULONGLONG NewFileSize); -bool FileStream_MoveFile(TFileStream * pStream, TFileStream * pTempStream); +bool FileStream_GetTime(TFileStream * pStream, ULONGLONG * pFT); +bool FileStream_GetFlags(TFileStream * pStream, LPDWORD pdwStreamFlags); +bool FileStream_Switch(TFileStream * pStream, TFileStream * pTempStream); +bool FileStream_SetBitmap(TFileStream * pStream, TFileBitmap * pBitmap); +bool FileStream_GetBitmap(TFileStream * pStream, TFileBitmap * pBitmap, DWORD Length, LPDWORD LengthNeeded); void FileStream_Close(TFileStream * pStream); //----------------------------------------------------------------------------- @@ -823,21 +849,17 @@ typedef bool (WINAPI * SFILEREADFILE)(HANDLE, void *, DWORD, LPDWORD, LPOVERLAP //----------------------------------------------------------------------------- // Functions for manipulation with StormLib global flags -#define SFILE_FLAG_ALLOW_WRITE_SHARE 0x00000001 // When a MPQ is open for write by StorLib, - // it is allowed to open it for write with another application. - -DWORD WINAPI SFileGetGlobalFlags(); -DWORD WINAPI SFileSetGlobalFlags(DWORD dwNewFlags); - LCID WINAPI SFileGetLocale(); LCID WINAPI SFileSetLocale(LCID lcNewLocale); //----------------------------------------------------------------------------- // Functions for archive manipulation -bool WINAPI SFileOpenArchive(const char * szMpqName, DWORD dwPriority, DWORD dwFlags, HANDLE * phMpq); -bool WINAPI SFileCreateArchive(const char * szMpqName, DWORD dwFlags, DWORD dwMaxFileCount, HANDLE * phMpq); +bool WINAPI SFileOpenArchive(const TCHAR * szMpqName, DWORD dwPriority, DWORD dwFlags, HANDLE * phMpq); +bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwFlags, DWORD dwMaxFileCount, HANDLE * phMpq); +bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCreateInfo, HANDLE * phMpq); +bool WINAPI SFileGetArchiveBitmap(HANDLE hMpq, TFileBitmap * pBitmap, DWORD Length, LPDWORD LengthNeeded); bool WINAPI SFileFlushArchive(HANDLE hMpq); bool WINAPI SFileCloseArchive(HANDLE hMpq); @@ -848,9 +870,10 @@ int WINAPI SFileAddListFile(HANDLE hMpq, const char * szListFile); // Archive compacting bool WINAPI SFileSetCompactCallback(HANDLE hMpq, SFILE_COMPACT_CALLBACK CompactCB, void * pvData); -bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile = NULL, bool bReserved = 0); +bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool bReserved); // Changing the maximum file count +DWORD WINAPI SFileGetMaxFileCount(HANDLE hMpq); bool WINAPI SFileSetMaxFileCount(HANDLE hMpq, DWORD dwMaxFileCount); // Changing (attributes) file @@ -861,7 +884,7 @@ bool WINAPI SFileUpdateFileAttributes(HANDLE hMpq, const char * szFileName); //----------------------------------------------------------------------------- // Functions for manipulation with patch archives -bool WINAPI SFileOpenPatchArchive(HANDLE hMpq, const char * szPatchMpqName, const char * szPatchPathPrefix, DWORD dwFlags); +bool WINAPI SFileOpenPatchArchive(HANDLE hMpq, const TCHAR * szPatchMpqName, const char * szPatchPathPrefix, DWORD dwFlags); bool WINAPI SFileIsPatchedArchive(HANDLE hMpq); //----------------------------------------------------------------------------- @@ -869,22 +892,25 @@ bool WINAPI SFileIsPatchedArchive(HANDLE hMpq); // Reading from MPQ file bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope, HANDLE * phFile); -DWORD WINAPI SFileGetFileSize(HANDLE hFile, LPDWORD pdwFileSizeHigh = NULL); +DWORD WINAPI SFileGetFileSize(HANDLE hFile, LPDWORD pdwFileSizeHigh); DWORD WINAPI SFileSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHigh, DWORD dwMoveMethod); -bool WINAPI SFileReadFile(HANDLE hFile, void * lpBuffer, DWORD dwToRead, LPDWORD pdwRead = NULL, LPOVERLAPPED lpOverlapped = NULL); +bool WINAPI SFileReadFile(HANDLE hFile, void * lpBuffer, DWORD dwToRead, LPDWORD pdwRead, LPOVERLAPPED lpOverlapped); bool WINAPI SFileCloseFile(HANDLE hFile); // Retrieving info about the file bool WINAPI SFileHasFile(HANDLE hMpq, const char * szFileName); bool WINAPI SFileGetFileName(HANDLE hFile, char * szFileName); -bool WINAPI SFileGetFileInfo(HANDLE hMpqOrFile, DWORD dwInfoType, void * pvFileInfo, DWORD cbFileInfo, LPDWORD pcbLengthNeeded = NULL); +bool WINAPI SFileGetFileInfo(HANDLE hMpqOrFile, DWORD dwInfoType, void * pvFileInfo, DWORD cbFileInfo, LPDWORD pcbLengthNeeded); // High-level extract function -bool WINAPI SFileExtractFile(HANDLE hMpq, const char * szToExtract, const char * szExtracted, DWORD dwSearchScope = SFILE_OPEN_FROM_MPQ); +bool WINAPI SFileExtractFile(HANDLE hMpq, const char * szToExtract, const TCHAR * szExtracted, DWORD dwSearchScope); //----------------------------------------------------------------------------- // Functions for file and archive verification +// Generates file CRC32 +bool WINAPI SFileGetFileChecksums(HANDLE hMpq, const char * szFileName, LPDWORD pdwCrc32, char * pMD5); + // Verifies file against its checksums stored in (attributes) attributes (depending on dwFlags). // For dwFlags, use one or more of MPQ_ATTRIBUTE_MD5 DWORD WINAPI SFileVerifyFile(HANDLE hMpq, const char * szFileName, DWORD dwFlags); @@ -915,9 +941,10 @@ int WINAPI SFileEnumLocales(HANDLE hMpq, const char * szFileName, LCID * plcL bool WINAPI SFileCreateFile(HANDLE hMpq, const char * szArchivedName, ULONGLONG FileTime, DWORD dwFileSize, LCID lcLocale, DWORD dwFlags, HANDLE * phFile); bool WINAPI SFileWriteFile(HANDLE hFile, const void * pvData, DWORD dwSize, DWORD dwCompression); bool WINAPI SFileFinishFile(HANDLE hFile); -bool WINAPI SFileAddFileEx(HANDLE hMpq, const char * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwCompression, DWORD dwCompressionNext = 0xFFFFFFFF); -bool WINAPI SFileAddFile(HANDLE hMpq, const char * szFileName, const char * szArchivedName, DWORD dwFlags); -bool WINAPI SFileAddWave(HANDLE hMpq, const char * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwQuality); + +bool WINAPI SFileAddFileEx(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwCompression, DWORD dwCompressionNext); +bool WINAPI SFileAddFile(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags); +bool WINAPI SFileAddWave(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwQuality); bool WINAPI SFileRemoveFile(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope); bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szOldFileName, const char * szNewFileName); bool WINAPI SFileSetFileLocale(HANDLE hFile, LCID lcNewLocale); @@ -932,14 +959,25 @@ int WINAPI SCompImplode (char * pbOutBuffer, int * pcbOutBuffer, char * pb int WINAPI SCompExplode (char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer); int WINAPI SCompCompress (char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer, unsigned uCompressionMask, int nCmpType, int nCmpLevel); int WINAPI SCompDecompress (char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer); +int WINAPI SCompDecompress2(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer); + +//----------------------------------------------------------------------------- +// Non-Windows support for SetLastError/GetLastError + +#ifndef PLATFORM_WINDOWS + +void SetLastError(int err); +int GetLastError(); + +#endif //----------------------------------------------------------------------------- // Functions from Storm.dll. They use slightly different names for keeping // possibility to use them together with StormLib (StormXXX instead of SFileXXX) #ifdef __LINK_STORM_DLL__ - #define STORM_ALTERNATE_NAMES // Force storm_dll.h to use alternate fnc names - #include "..\storm_dll\storm_dll.h" +#define STORM_ALTERNATE_NAMES // Force storm_dll.h to use alternate fnc names +#include "..\storm_dll\storm_dll.h" #endif // __LINK_STORM_DLL__ #ifdef __cplusplus diff --git a/dep/StormLib/src/StormPort.h b/dep/StormLib/src/StormPort.h index 0ecb3e0c598..5673516c193 100644 --- a/dep/StormLib/src/StormPort.h +++ b/dep/StormLib/src/StormPort.h @@ -9,7 +9,7 @@ /* Computer: whiplash.flachland-chemnitz.de */ /* System: Linux 2.4.0 on i686 */ /* */ -/* Author: Sam Wilkins */ +/* Author: Sam Wilkins */ /* System: Mac OS X and port to big endian processor */ /* */ /*---------------------------------------------------------------------------*/ @@ -26,198 +26,216 @@ #ifndef __STORMPORT_H__ #define __STORMPORT_H__ +#ifndef __cplusplus +#define bool char +#define true 1 +#define false 0 +#endif + // Defines for Windows #if !defined(PLATFORM_DEFINED) && (defined(WIN32) || defined(WIN64)) - // In MSVC 8.0, there are some functions declared as deprecated. - #if _MSC_VER >= 1400 - #define _CRT_SECURE_NO_DEPRECATE - #define _CRT_NON_CONFORMING_SWPRINTFS - #endif +// In MSVC 8.0, there are some functions declared as deprecated. +#if _MSC_VER >= 1400 +#define _CRT_SECURE_NO_DEPRECATE +#define _CRT_NON_CONFORMING_SWPRINTFS +#endif - #include - #include - #include - #include - #define PLATFORM_LITTLE_ENDIAN +#include +#include +#include +#include +#include +#include +#define PLATFORM_LITTLE_ENDIAN - #ifdef WIN64 - #define PLATFORM_64BIT - #else - #define PLATFORM_32BIT - #endif +#ifdef WIN64 +#define PLATFORM_64BIT +#else +#define PLATFORM_32BIT +#endif - #define PLATFORM_WINDOWS - #define PLATFORM_DEFINED // The platform is known now +#define PLATFORM_WINDOWS +#define PLATFORM_DEFINED // The platform is known now #endif -// Defines for Mac Carbon -#if !defined(PLATFORM_DEFINED) && defined(__APPLE__) // Mac Carbon API +// Defines for Mac +#if !defined(PLATFORM_DEFINED) && defined(__APPLE__) // Mac BSD API - // Macintosh using Carbon - #include // Mac OS X - - #define PKEXPORT - #define __SYS_ZLIB - #define __SYS_BZLIB +// Macintosh +#include +#include +#include +#include +#include +#include +#include - #ifndef __BIG_ENDIAN__ - #define PLATFORM_LITTLE_ENDIAN // Apple is now making Macs with Intel CPUs - #endif - - #define PLATFORM_MAC - #define PLATFORM_DEFINED // The platform is known now +#define PKEXPORT +#define __SYS_ZLIB +#define __SYS_BZLIB + +#ifndef __BIG_ENDIAN__ +#define PLATFORM_LITTLE_ENDIAN +#endif + +#define PLATFORM_MAC +#define PLATFORM_DEFINED // The platform is known now #endif // Assumption: we are not on Windows nor Macintosh, so this must be linux *grin* #if !defined(PLATFORM_DEFINED) - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - - #define PLATFORM_LITTLE_ENDIAN - #define PLATFORM_LINUX - #define PLATFORM_DEFINED +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PLATFORM_LITTLE_ENDIAN +#define PLATFORM_LINUX +#define PLATFORM_DEFINED #endif // Definition of Windows-specific structures for non-Windows platforms #ifndef PLATFORM_WINDOWS - #if __LP64__ - #define PLATFORM_64BIT - #else - #define PLATFORM_32BIT - #endif - - // Typedefs for ANSI C - typedef unsigned char BYTE; - typedef unsigned short USHORT; - typedef int LONG; - typedef unsigned int DWORD; - typedef unsigned long DWORD_PTR; - typedef long LONG_PTR; - typedef long INT_PTR; - typedef long long LONGLONG; - typedef unsigned long long ULONGLONG; - typedef void * HANDLE; - typedef void * LPOVERLAPPED; // Unsupported on Linux and Mac - typedef char TCHAR; - typedef unsigned int LCID; - typedef LONG * PLONG; - typedef DWORD * LPDWORD; - typedef BYTE * LPBYTE; - - #ifdef PLATFORM_32BIT - #define _LZMA_UINT32_IS_ULONG - #endif - - // Some Windows-specific defines - #ifndef MAX_PATH - #define MAX_PATH 1024 - #endif - - #define WINAPI - - #define FILE_BEGIN SEEK_SET - #define FILE_CURRENT SEEK_CUR - #define FILE_END SEEK_END - - #define _stricmp strcasecmp - #define _strnicmp strncasecmp - - void SetLastError(int err); - int GetLastError(); +#if __LP64__ +#define PLATFORM_64BIT +#else +#define PLATFORM_32BIT +#endif +// Typedefs for ANSI C +typedef unsigned char BYTE; +typedef unsigned short USHORT; +typedef int LONG; +typedef unsigned int DWORD; +typedef unsigned long DWORD_PTR; +typedef long LONG_PTR; +typedef long INT_PTR; +typedef long long LONGLONG; +typedef unsigned long long ULONGLONG; +typedef void * HANDLE; +typedef void * LPOVERLAPPED; // Unsupported on Linux and Mac +typedef char TCHAR; +typedef unsigned int LCID; +typedef LONG * PLONG; +typedef DWORD * LPDWORD; +typedef BYTE * LPBYTE; + +#ifdef PLATFORM_32BIT +#define _LZMA_UINT32_IS_ULONG +#endif + +// Some Windows-specific defines +#ifndef MAX_PATH +#define MAX_PATH 1024 +#endif + +#define WINAPI + +#define FILE_BEGIN SEEK_SET +#define FILE_CURRENT SEEK_CUR +#define FILE_END SEEK_END + +#define _T(x) x +#define _tcslen strlen +#define _tcscpy strcpy +#define _tcscat strcat +#define _tcsrchr strrchr +#define _tprintf printf +#define _stprintf sprintf +#define _tremove remove + +#define _stricmp strcasecmp +#define _strnicmp strncasecmp +#define _tcsnicmp strncasecmp #endif // !WIN32 -// Platform-specific error codes -#ifdef PLATFORM_MAC - #define ERROR_SUCCESS noErr - #define ERROR_FILE_NOT_FOUND fnfErr - #define ERROR_ACCESS_DENIED permErr - #define ERROR_INVALID_HANDLE rfNumErr - #define ERROR_NOT_ENOUGH_MEMORY mFulErr - #define ERROR_BAD_FORMAT 200 // Returned when the opened file is in format that is not recognized by StormLib - #define ERROR_NO_MORE_FILES errFSNoMoreItems - #define ERROR_HANDLE_EOF eofErr - #define ERROR_NOT_SUPPORTED 201 - #define ERROR_INVALID_PARAMETER paramErr - #define ERROR_DISK_FULL dskFulErr - #define ERROR_ALREADY_EXISTS dupFNErr - #define ERROR_CAN_NOT_COMPLETE 202 // A generic error, when any operation fails from an unknown reason - #define ERROR_FILE_CORRUPT 203 // At any point when there is bad data format in the file - #define ERROR_INSUFFICIENT_BUFFER errFSBadBuffer +// 64-bit calls are supplied by "normal" calls on Mac +#if defined(PLATFORM_MAC) +#define stat64 stat +#define fstat64 fstat +#define lseek64 lseek +#define off64_t off_t +#define O_LARGEFILE 0 #endif -#ifdef PLATFORM_LINUX - #define ERROR_SUCCESS 0 - #define ERROR_FILE_NOT_FOUND ENOENT - #define ERROR_ACCESS_DENIED EPERM - #define ERROR_INVALID_HANDLE EBADF - #define ERROR_NOT_ENOUGH_MEMORY ENOMEM - #define ERROR_BAD_FORMAT 105 // No such error code under Linux - #define ERROR_NO_MORE_FILES 106 - #define ERROR_HANDLE_EOF 107 // No such error code under Linux - #define ERROR_NOT_SUPPORTED ENOTSUP - #define ERROR_INVALID_PARAMETER EINVAL - #define ERROR_DISK_FULL ENOSPC - #define ERROR_ALREADY_EXISTS EEXIST - #define ERROR_CAN_NOT_COMPLETE 108 // No such error code under Linux - #define ERROR_FILE_CORRUPT 109 // No such error code under Linux - #define ERROR_INSUFFICIENT_BUFFER ENOBUFS +// Platform-specific error codes for UNIX-based platforms +#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) +#define ERROR_SUCCESS 0 +#define ERROR_FILE_NOT_FOUND ENOENT +#define ERROR_ACCESS_DENIED EPERM +#define ERROR_INVALID_HANDLE EBADF +#define ERROR_NOT_ENOUGH_MEMORY ENOMEM +#define ERROR_BAD_FORMAT 105 // No such error code under Linux +#define ERROR_NO_MORE_FILES 106 +#define ERROR_HANDLE_EOF 107 // No such error code under Linux +#define ERROR_NOT_SUPPORTED ENOTSUP +#define ERROR_INVALID_PARAMETER EINVAL +#define ERROR_DISK_FULL ENOSPC +#define ERROR_ALREADY_EXISTS EEXIST +#define ERROR_CAN_NOT_COMPLETE 108 // No such error code under Linux +#define ERROR_FILE_CORRUPT 109 // No such error code under Linux +#define ERROR_INSUFFICIENT_BUFFER ENOBUFS #endif #ifdef PLATFORM_LITTLE_ENDIAN - #define BSWAP_INT16_UNSIGNED(a) (a) - #define BSWAP_INT16_SIGNED(a) (a) - #define BSWAP_INT32_UNSIGNED(a) (a) - #define BSWAP_INT32_SIGNED(a) (a) - #define BSWAP_INT64_SIGNED(a) (a) - #define BSWAP_INT64_UNSIGNED(a) (a) - #define BSWAP_ARRAY16_UNSIGNED(a,b) {} - #define BSWAP_ARRAY32_UNSIGNED(a,b) {} - #define BSWAP_ARRAY64_UNSIGNED(a,b) {} - #define BSWAP_PART_HEADER(a) {} - #define BSWAP_TMPQUSERDATA(a) {} - #define BSWAP_TMPQHEADER(a) {} +#define BSWAP_INT16_UNSIGNED(a) (a) +#define BSWAP_INT16_SIGNED(a) (a) +#define BSWAP_INT32_UNSIGNED(a) (a) +#define BSWAP_INT32_SIGNED(a) (a) +#define BSWAP_INT64_SIGNED(a) (a) +#define BSWAP_INT64_UNSIGNED(a) (a) +#define BSWAP_ARRAY16_UNSIGNED(a,b) {} +#define BSWAP_ARRAY32_UNSIGNED(a,b) {} +#define BSWAP_ARRAY64_UNSIGNED(a,b) {} +#define BSWAP_PART_HEADER(a) {} +#define BSWAP_TMPQUSERDATA(a) {} +#define BSWAP_TMPQHEADER(a) {} #else - int16_t SwapInt16(uint16_t); - uint16_t SwapUInt16(uint16_t); - int32_t SwapInt32(uint32_t); - uint32_t SwapUInt32(uint32_t); - int64_t SwapInt64(uint64_t); - uint64_t SwapUInt64(uint64_t); - void ConvertUInt16Buffer(void * ptr, size_t length); - void ConvertUInt32Buffer(void * ptr, size_t length); - void ConvertUInt64Buffer(void * ptr, size_t length); - void ConvertPartHeader(void * partHeader); - void ConvertTMPQUserData(void *userData); - void ConvertTMPQHeader(void *header); - #define BSWAP_INT16_SIGNED(a) SwapInt16((a)) - #define BSWAP_INT16_UNSIGNED(a) SwapUInt16((a)) - #define BSWAP_INT32_SIGNED(a) SwapInt32((a)) - #define BSWAP_INT32_UNSIGNED(a) SwapUInt32((a)) - #define BSWAP_INT64_SIGNED(a) SwapInt64((a)) - #define BSWAP_INT64_UNSIGNED(a) SwapUInt64((a)) - #define BSWAP_ARRAY16_UNSIGNED(a,b) ConvertUInt16Buffer((a),(b)) - #define BSWAP_ARRAY32_UNSIGNED(a,b) ConvertUInt32Buffer((a),(b)) - #define BSWAP_ARRAY64_UNSIGNED(a,b) ConvertUInt64Buffer((a),(b)) - #define BSWAP_PART_HEADER(a) ConvertPartHeader(a) - #define BSWAP_TMPQUSERDATA(a) ConvertTMPQUserData((a)) - #define BSWAP_TMPQHEADER(a) ConvertTMPQHeader((a)) +#ifdef __cplusplus + extern "C" { +#endif +int16_t SwapInt16(uint16_t); +uint16_t SwapUInt16(uint16_t); +int32_t SwapInt32(uint32_t); +uint32_t SwapUInt32(uint32_t); +int64_t SwapInt64(uint64_t); +uint64_t SwapUInt64(uint64_t); +void ConvertUInt16Buffer(void * ptr, size_t length); +void ConvertUInt32Buffer(void * ptr, size_t length); +void ConvertUInt64Buffer(void * ptr, size_t length); +void ConvertPartHeader(void * partHeader); +void ConvertTMPQUserData(void *userData); +void ConvertTMPQHeader(void *header); +#ifdef __cplusplus + } +#endif +#define BSWAP_INT16_SIGNED(a) SwapInt16((a)) +#define BSWAP_INT16_UNSIGNED(a) SwapUInt16((a)) +#define BSWAP_INT32_SIGNED(a) SwapInt32((a)) +#define BSWAP_INT32_UNSIGNED(a) SwapUInt32((a)) +#define BSWAP_INT64_SIGNED(a) SwapInt64((a)) +#define BSWAP_INT64_UNSIGNED(a) SwapUInt64((a)) +#define BSWAP_ARRAY16_UNSIGNED(a,b) ConvertUInt16Buffer((a),(b)) +#define BSWAP_ARRAY32_UNSIGNED(a,b) ConvertUInt32Buffer((a),(b)) +#define BSWAP_ARRAY64_UNSIGNED(a,b) ConvertUInt64Buffer((a),(b)) +#define BSWAP_PART_HEADER(a) ConvertPartHeader(a) +#define BSWAP_TMPQUSERDATA(a) ConvertTMPQUserData((a)) +#define BSWAP_TMPQHEADER(a) ConvertTMPQHeader((a)) #endif #endif // __STORMPORT_H__ diff --git a/dep/StormLib/src/adpcm/adpcm.cpp b/dep/StormLib/src/adpcm/adpcm.cpp index c43d2234c18..783cf10bf5b 100644 --- a/dep/StormLib/src/adpcm/adpcm.cpp +++ b/dep/StormLib/src/adpcm/adpcm.cpp @@ -17,17 +17,17 @@ //------------------------------------------------------------------------------ // Structures -union TByteAndWordPtr +typedef union _BYTE_AND_WORD_PTR { short * pw; unsigned char * pb; -}; +} BYTE_AND_WORD_PTR; -union TWordAndByteArray +typedef union _WORD_AND_BYTE_ARRAY { short w; unsigned char b[2]; -}; +} WORD_AND_BYTE_ARRAY; //----------------------------------------------------------------------------- // Tables necessary dor decompression @@ -36,8 +36,8 @@ static long Table1503F120[] = { 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000004, 0xFFFFFFFF, 0x00000002, 0xFFFFFFFF, 0x00000006, 0xFFFFFFFF, 0x00000001, 0xFFFFFFFF, 0x00000005, 0xFFFFFFFF, 0x00000003, 0xFFFFFFFF, 0x00000007, - 0xFFFFFFFF, 0x00000001, 0xFFFFFFFF, 0x00000005, 0xFFFFFFFF, 0x00000003, 0xFFFFFFFF, 0x00000007, - 0xFFFFFFFF, 0x00000002, 0xFFFFFFFF, 0x00000004, 0xFFFFFFFF, 0x00000006, 0xFFFFFFFF, 0x00000008 + 0xFFFFFFFF, 0x00000001, 0xFFFFFFFF, 0x00000005, 0xFFFFFFFF, 0x00000003, 0xFFFFFFFF, 0x00000007, + 0xFFFFFFFF, 0x00000002, 0xFFFFFFFF, 0x00000004, 0xFFFFFFFF, 0x00000006, 0xFFFFFFFF, 0x00000008 }; static long step_table[] = @@ -63,8 +63,8 @@ static long step_table[] = int CompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuffer, int dwInLength, int nChannels, int nCmpLevel) // ECX EDX { - TWordAndByteArray Wcmp; - TByteAndWordPtr out; // Pointer to the output buffer + WORD_AND_BYTE_ARRAY Wcmp; + BYTE_AND_WORD_PTR out; // Pointer to the output buffer long SInt32Array1[2]; long SInt32Array2[2]; long SInt32Array3[2]; @@ -83,6 +83,7 @@ int CompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuff int nLength; int nIndex; int nValue; + int i, chnl; // If less than 2 bytes remain, don't decompress anything // pbSaveOutBuffer = pbOutBuffer; @@ -99,7 +100,7 @@ int CompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuff SInt32Array1[0] = SInt32Array1[1] = 0x2C; - for(int i = 0; i < nChannels; i++) + for(i = 0; i < nChannels; i++) { nOneWord = BSWAP_INT16_SIGNED(*pwInBuffer++); *out.pw++ = BSWAP_INT16_SIGNED((short)nOneWord); @@ -113,13 +114,13 @@ int CompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuff nLength = (nLength / 2) - (int)(out.pb - pbOutBuffer); nLength = (nLength < 0) ? 0 : nLength; - + nIndex = nChannels - 1; // edi nWordsRemains = dwInLength / 2; // eax - + // ebx - nChannels // ecx - pwOutPos - for(int chnl = nChannels; chnl < nWordsRemains; chnl++) + for(chnl = nChannels; chnl < nWordsRemains; chnl++) { // 1500F030 if((out.pb - pbOutBuffer + 2) > nBytesRemains) @@ -185,7 +186,7 @@ int CompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuff } if(dwBit == dwStopBit) break; - + nTableValue >>= 1; } @@ -206,7 +207,7 @@ int CompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuff SInt32Array2[nIndex] = nValue; *out.pb++ = (unsigned char)(dwBitBuff | ebx); nTableValue = Table1503F120[dwBitBuff & 0x1F]; - SInt32Array1[nIndex] = SInt32Array1[nIndex] + nTableValue; + SInt32Array1[nIndex] = SInt32Array1[nIndex] + nTableValue; if(SInt32Array1[nIndex] < 0) SInt32Array1[nIndex] = 0; else if(SInt32Array1[nIndex] > 0x58) @@ -223,14 +224,14 @@ int CompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuff // 1500F230 int DecompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, unsigned char * pbInBuffer, int dwInLength, int nChannels) { - TByteAndWordPtr out; // Output buffer - TByteAndWordPtr in; + BYTE_AND_WORD_PTR out; // Output buffer + BYTE_AND_WORD_PTR in; unsigned char * pbInBufferEnd = (pbInBuffer + dwInLength); long SInt32Array1[2]; long SInt32Array2[2]; long nOneWord; - int dwOutLengthCopy = dwOutLength; int nIndex; + int i; SInt32Array1[0] = SInt32Array1[1] = 0x2C; out.pb = pbOutBuffer; @@ -238,15 +239,15 @@ int DecompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, unsigned char in.pw++; // Fill the Uint32Array2 array by channel values. - for(int i = 0; i < nChannels; i++) + for(i = 0; i < nChannels; i++) { nOneWord = BSWAP_INT16_SIGNED(*in.pw++); SInt32Array2[i] = nOneWord; - if(dwOutLengthCopy < 2) + if(dwOutLength < 2) return (int)(out.pb - pbOutBuffer); *out.pw++ = BSWAP_INT16_SIGNED((short)nOneWord); - dwOutLengthCopy -= sizeof(short); + dwOutLength -= sizeof(short); } // Get the initial index @@ -270,7 +271,7 @@ int DecompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, unsigned char if(SInt32Array1[nIndex] != 0) SInt32Array1[nIndex]--; - if(dwOutLengthCopy < 2) + if(dwOutLength < 2) return (int)(out.pb - pbOutBuffer); *out.pw++ = BSWAP_INT16_SIGNED((unsigned short)SInt32Array2[nIndex]); @@ -281,7 +282,7 @@ int DecompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, unsigned char SInt32Array1[nIndex] += 8; if(SInt32Array1[nIndex] > 0x58) SInt32Array1[nIndex] = 0x58; - + if(nChannels == 2) nIndex = (nIndex == 0) ? 1 : 0; break; diff --git a/dep/StormLib/src/adpcm/adpcm.h b/dep/StormLib/src/adpcm/adpcm.h index fddc346a288..beb96159720 100644 --- a/dep/StormLib/src/adpcm/adpcm.h +++ b/dep/StormLib/src/adpcm/adpcm.h @@ -11,10 +11,6 @@ #ifndef __ADPCM_H__ #define __ADPCM_H__ -#ifdef __cplusplus -extern "C" { -#endif - //----------------------------------------------------------------------------- // Functions @@ -23,8 +19,4 @@ extern "C" { int CompressADPCM (unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuffer, int dwInLength, int nCmpType, int nChannels); int DecompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, unsigned char * pbInBuffer, int dwInLength, int nChannels); -#ifdef __cplusplus -} // extern "C" -#endif - #endif // __ADPCM_H__ diff --git a/dep/StormLib/src/huffman/huff.cpp b/dep/StormLib/src/huffman/huff.cpp index 997fe46e928..e12dd7df06e 100644 --- a/dep/StormLib/src/huffman/huff.cpp +++ b/dep/StormLib/src/huffman/huff.cpp @@ -14,10 +14,10 @@ /* 19.11.03 1.01 Dan Big endian handling */ /* 08.12.03 2.01 Dan High-memory handling (> 0x80000000) */ /*****************************************************************************/ - + #include #include - + #include "huff.h" // Special for Mac - we have to know if normal pointer greater or less @@ -29,10 +29,10 @@ static long mul = 1; #define PTR_INVALID(ptr) (((LONG_PTR)(ptr) * mul) < 0) #define PTR_INVALID_OR_NULL(ptr) (((LONG_PTR)(ptr) * mul) <= 0) - + //----------------------------------------------------------------------------- // Methods of the THTreeItem struct - + // 1501DB70 THTreeItem * THTreeItem::Call1501DB70(THTreeItem * pLast) { @@ -40,83 +40,83 @@ THTreeItem * THTreeItem::Call1501DB70(THTreeItem * pLast) pLast = this + 1; return pLast; } - + // Gets previous Huffman tree item (?) THTreeItem * THTreeItem::GetPrevItem(LONG_PTR value) { if(PTR_INVALID(prev)) return PTR_NOT(prev); - + if(value == -1 || PTR_INVALID(value)) - value = (long)(this - next->prev); + value = (LONG_PTR)(this - next->prev); return prev + value; - + // OLD VERSION // if(PTR_INT(value) < 0) // value = PTR_INT((item - item->next->prev)); // return (THTreeItem *)((char *)prev + value); } - + // 1500F5E0 void THTreeItem::ClearItemLinks() { next = prev = NULL; } - + // 1500BC90 void THTreeItem::RemoveItem() { THTreeItem * pTemp; // EDX - + if(next != NULL) { pTemp = prev; - + if(PTR_INVALID_OR_NULL(pTemp)) pTemp = PTR_NOT(pTemp); else pTemp += (this - next->prev); - + pTemp->next = next; next->prev = prev; next = prev = NULL; } } - + /* // OLD VERSION : Removes item from the tree (?) static void RemoveItem(THTreeItem * item) { THTreeItem * next = item->next; // ESI THTreeItem * prev = item->prev; // EDX - + if(next == NULL) return; - + if(PTR_INT(prev) < 0) prev = PTR_NOT(prev); else // ??? usually item == next->prev, so what is it ? prev = (THTreeItem *)((unsigned char *)prev + (unsigned long)((unsigned char *)item - (unsigned char *)(next->prev))); - + // Remove HTree item from the chain prev->next = next; // Sets the 'first' pointer next->prev = item->prev; - + // Invalidate pointers item->next = NULL; item->prev = NULL; } */ - + //----------------------------------------------------------------------------- // TOutputStream functions - + void TOutputStream::PutBits(unsigned long dwBuff, unsigned int nPutBits) { dwBitBuff |= (dwBuff << nBits); nBits += nPutBits; - + // Flush completed bytes while(nBits >= 8) { @@ -125,12 +125,12 @@ void TOutputStream::PutBits(unsigned long dwBuff, unsigned int nPutBits) *pbOutPos++ = (unsigned char)dwBitBuff; cbOutSize--; } - + dwBitBuff >>= 8; nBits -= 8; } } - + //----------------------------------------------------------------------------- // TInputStream functions @@ -153,8 +153,8 @@ unsigned long TInputStream::GetBit() BitCount--; return dwOneBit; -} - +} + // Gets 7 bits from the stream. DOES NOT remove the bits from input stream unsigned long TInputStream::Get7Bits() { @@ -172,7 +172,7 @@ unsigned long TInputStream::Get7Bits() // Return the first available 7 bits. DO NOT remove them from the input stream return (BitBuffer & 0x7F); } - + // Gets the whole byte from the input stream. unsigned long TInputStream::Get8Bits() { @@ -215,15 +215,15 @@ void TInputStream::SkipBits(unsigned int dwBitsToSkip) //----------------------------------------------------------------------------- // Functions for huffmann tree items - + // Inserts item into the tree (?) -static void InsertItem(THTreeItem ** itemPtr, THTreeItem * item, unsigned long where, THTreeItem * item2) +static void InsertItem(THTreeItem ** itemPtr, THTreeItem * item, unsigned long nWhere, THTreeItem * item2) { THTreeItem * next = item->next; // EDI - next to the first item THTreeItem * prev = item->prev; // ESI - prev to the first item THTreeItem * prev2; // Pointer to previous item LONG_PTR next2; // Pointer to the next item - + // The same code like in RemoveItem(item); if(next != 0) // If the first item already has next one { @@ -231,21 +231,21 @@ static void InsertItem(THTreeItem ** itemPtr, THTreeItem * item, unsigned long w prev = PTR_NOT(prev); else prev += (item - next->prev); - + // 150083C1 // Remove the item from the tree prev->next = next; next->prev = prev; - + // Invalidate 'prev' and 'next' pointer item->next = 0; item->prev = 0; } - + if(item2 == NULL) // EDX - If the second item is not entered, item2 = PTR_PTR(&itemPtr[1]); // take the first tree item - - switch(where) + + switch(nWhere) { case SWITCH_ITEMS : // Switch the two items item->next = item2->next; // item2->next (Pointer to pointer to first) @@ -253,68 +253,73 @@ static void InsertItem(THTreeItem ** itemPtr, THTreeItem * item, unsigned long w item2->next->prev = item; item2->next = item; // Set the first item return; - + case INSERT_ITEM: // Insert as the last item item->next = item2; // Set next item (or pointer to pointer to first item) item->prev = item2->prev; // Set prev item (or last item in the tree) - + next2 = PTR_INT(itemPtr[0]);// Usually NULL prev2 = item2->prev; // Prev item to the second (or last tree item) - + if(PTR_INVALID(prev2)) { - prev2 = PTR_NOT(prev); - - prev2->next = item; - item2->prev = item; // Next after last item + if(prev != NULL) + { + prev2 = PTR_NOT(prev); + if(prev2 != NULL) + { + prev2->next = item; + item2->prev = item; // Next after last item + } + } return; } - + if(PTR_INVALID(next2)) - next2 = (long)(item2 - item2->next->prev); + next2 = (LONG_PTR)(item2 - item2->next->prev); // next2 = (THTreeItem *)(unsigned long)((unsigned char *)item2 - (unsigned char *)(item2->next->prev)); - + // prev2 = (THTreeItem *)((char *)prev2 + (unsigned long)next2);// ??? prev2 += next2; prev2->next = item; item2->prev = item; // Set the next/last item return; - + default: return; } } - + //----------------------------------------------------------------------------- // THuffmannTree class functions - + THuffmannTree::THuffmannTree() { // We have to check if the "this" pointer is less than zero if((LONG_PTR)this < 0) mul = -1; } - + void THuffmannTree::InitTree(bool bCompression) { THTreeItem * pItem; unsigned int nCount; - + // Clear links for all the items in the tree for(pItem = items0008, nCount = 0x203; nCount != 0; pItem++, nCount--) pItem->ClearItemLinks(); - + pItem3050 = NULL; pItem3054 = PTR_PTR(&pItem3054); pItem3058 = PTR_NOT(pItem3054); - + pItem305C = NULL; pFirst = PTR_PTR(&pFirst); pLast = PTR_NOT(pFirst); - + offs0004 = 1; nItems = 0; - + // Clear all TQDecompress items. Do this only if preparing for decompression if(bCompression == false) { @@ -322,7 +327,7 @@ void THuffmannTree::InitTree(bool bCompression) qd3474[nCount].offs00 = 0; } } - + // Builds Huffman tree. Called with the first 8 bits loaded from input stream void THuffmannTree::BuildTree(unsigned int nCmpType) { @@ -331,55 +336,55 @@ void THuffmannTree::BuildTree(unsigned int nCmpType) unsigned char * byteArray; // [ESP+1C] - Pointer to unsigned char in Table1502A630 THTreeItem * child1; unsigned long i; // egcs in linux doesn't like multiple for loops without an explicit i - + // Loop while pointer has a valid value while(PTR_VALID(pLast)) // ESI - Last entry { THTreeItem * temp; // EAX - + if(pLast->next != NULL) // ESI->next pLast->RemoveItem(); // EDI = &offs3054 pItem3058 = PTR_PTR(&pItem3054); // [EDI+4] pLast->prev = pItem3058; // EAX - + temp = PTR_PTR(&pItem3054)->GetPrevItem(PTR_INT(&pItem3050)); - + temp->next = pLast; pItem3054 = pLast; } - + // Clear all pointers in HTree item array memset(items306C, 0, sizeof(items306C)); - + maxByte = 0; // Greatest character found init to zero. itemPtr = (THTreeItem **)&items306C; // Pointer to current entry in HTree item pointer array - + // Ensure we have low 8 bits only nCmpType &= 0xFF; byteArray = Table1502A630 + nCmpType * 258; // EDI also - + for(i = 0; i < 0x100; i++, itemPtr++) { THTreeItem * item = pItem3058; // Item to be created THTreeItem * pItem3 = pItem3058; unsigned char oneByte = byteArray[i]; - + // Skip all the bytes which are zero. if(byteArray[i] == 0) continue; - + // If not valid pointer, take the first available item in the array if(PTR_INVALID_OR_NULL(item)) item = &items0008[nItems++]; - + // Insert this item as the top of the tree InsertItem(&pItem305C, item, SWITCH_ITEMS, NULL); - + item->parent = NULL; // Invalidate child and parent item->child = NULL; *itemPtr = item; // Store pointer into pointer array - + item->dcmpByte = i; // Store counter item->byteValue = oneByte; // Store byte value if(oneByte >= maxByte) @@ -387,7 +392,7 @@ void THuffmannTree::BuildTree(unsigned int nCmpType) maxByte = oneByte; continue; } - + // Find the first item which has byte value greater than current one byte if(PTR_VALID(pItem3 = pLast)) // EDI - Pointer to the last item { @@ -404,35 +409,35 @@ void THuffmannTree::BuildTree(unsigned int nCmpType) } } pItem3 = NULL; - + // 15006B09 _15006B09: if(item->next != NULL) item->RemoveItem(); - + // 15006B15 if(pItem3 == NULL) pItem3 = PTR_PTR(&pFirst); - + // 15006B1F item->next = pItem3->next; item->prev = pItem3->next->prev; pItem3->next->prev = item; pItem3->next = item; } - + // 15006B4A for(; i < 0x102; i++) { THTreeItem ** itemPtr = &items306C[i]; // EDI - + // 15006B59 THTreeItem * item = pItem3058; // ESI if(PTR_INVALID_OR_NULL(item)) item = &items0008[nItems++]; - + InsertItem(&pItem305C, item, INSERT_ITEM, NULL); - + // 15006B89 item->dcmpByte = i; item->byteValue = 1; @@ -440,42 +445,42 @@ void THuffmannTree::BuildTree(unsigned int nCmpType) item->child = NULL; *itemPtr++ = item; } - + // 15006BAA if(PTR_VALID(child1 = pLast)) // EDI - last item (first child to item { THTreeItem * child2; // EBP THTreeItem * item; // ESI - + // 15006BB8 while(PTR_VALID(child2 = child1->prev)) { if(PTR_INVALID_OR_NULL(item = pItem3058)) item = &items0008[nItems++]; - + // 15006BE3 InsertItem(&pItem305C, item, SWITCH_ITEMS, NULL); - + // 15006BF3 item->parent = NULL; item->child = NULL; - + //EDX = child2->byteValue + child1->byteValue; //EAX = child1->byteValue; //ECX = maxByte; // The greatest character (0xFF usually) - + item->byteValue = child1->byteValue + child2->byteValue; // 0x02 item->child = child1; // Prev item in the child1->parent = item; child2->parent = item; - + // EAX = item->byteValue; if(item->byteValue >= maxByte) maxByte = item->byteValue; else { THTreeItem * pItem2 = child2->prev; // EDI - + // 15006C2D while(PTR_VALID(pItem2)) { @@ -484,22 +489,22 @@ void THuffmannTree::BuildTree(unsigned int nCmpType) pItem2 = pItem2->prev; } pItem2 = NULL; - + _15006C3B: if(item->next != 0) { THTreeItem * temp4 = item->GetPrevItem(-1); - + temp4->next = item->next; // The first item changed item->next->prev = item->prev; // First->prev changed to negative value item->next = NULL; item->prev = NULL; } - + // 15006C62 if(pItem2 == NULL) pItem2 = PTR_PTR(&pFirst); - + item->next = pItem2->next; // Set item with 0x100 byte value item->prev = pItem2->next->prev; // Set item with 0x17 byte value pItem2->next->prev = item; // Changed prev of item with @@ -521,48 +526,48 @@ void THuffmannTree::ModifyTree(unsigned long dwIndex) THTreeItem * pItem1 = pItem3058; // ESI THTreeItem * pSaveLast = (PTR_INT(pLast) <= 0) ? NULL : pLast; // EBX THTreeItem * temp; // EAX - + // Prepare the first item to insert to the tree if(PTR_INT(pItem1) <= 0) pItem1 = &items0008[nItems++]; - + // If item has any next item, remove it from the chain if(pItem1->next != NULL) { THTreeItem * temp = pItem1->GetPrevItem(-1); // EAX - + temp->next = pItem1->next; pItem1->next->prev = pItem1->prev; pItem1->next = NULL; pItem1->prev = NULL; } - + pItem1->next = PTR_PTR(&pFirst); pItem1->prev = pLast; temp = pItem1->next->GetPrevItem(PTR_INT(pItem305C)); - + // 150068E9 temp->next = pItem1; pLast = pItem1; - + pItem1->parent = NULL; pItem1->child = NULL; - + // 150068F6 pItem1->dcmpByte = pSaveLast->dcmpByte; // Copy item index pItem1->byteValue = pSaveLast->byteValue; // Copy item byte value pItem1->parent = pSaveLast; // Set parent to last item items306C[pSaveLast->dcmpByte] = pItem1; // Insert item into item pointer array - + // Prepare the second item to insert into the tree if(PTR_INT((pItem1 = pItem3058)) <= 0) pItem1 = &items0008[nItems++]; - + // 1500692E if(pItem1->next != NULL) { temp = pItem1->GetPrevItem(-1); // EAX - + temp->next = pItem1->next; pItem1->next->prev = pItem1->prev; pItem1->next = NULL; @@ -572,11 +577,11 @@ void THuffmannTree::ModifyTree(unsigned long dwIndex) pItem1->next = PTR_PTR(&pFirst); pItem1->prev = pLast; temp = pItem1->next->GetPrevItem(PTR_INT(pItem305C)); - + // 15006968 temp->next = pItem1; pLast = pItem1; - + // 1500696E pItem1->child = NULL; pItem1->dcmpByte = dwIndex; @@ -584,45 +589,45 @@ void THuffmannTree::ModifyTree(unsigned long dwIndex) pItem1->parent = pSaveLast; pSaveLast->child = pItem1; items306C[dwIndex] = pItem1; - + do { THTreeItem * pItem2 = pItem1; THTreeItem * pItem3; unsigned long byteValue; - + // 15006993 byteValue = ++pItem1->byteValue; - + // Pass through all previous which have its value greater than byteValue while(PTR_INT((pItem3 = pItem2->prev)) > 0) // EBX { if(pItem3->byteValue >= byteValue) goto _150069AE; - + pItem2 = pItem2->prev; } // 150069AC pItem3 = NULL; - + _150069AE: if(pItem2 == pItem1) continue; - + // 150069B2 // Switch pItem2 with item InsertItem(&pItem305C, pItem2, SWITCH_ITEMS, pItem1); InsertItem(&pItem305C, pItem1, SWITCH_ITEMS, pItem3); - + // 150069D0 // Switch parents of pItem1 and pItem2 temp = pItem2->parent->child; if(pItem1 == pItem1->parent->child) pItem1->parent->child = pItem2; - + if(pItem2 == temp) pItem2->parent->child = pItem1; - + // 150069ED // Switch parents of pItem1 and pItem3 temp = pItem1->parent; @@ -632,7 +637,7 @@ void THuffmannTree::ModifyTree(unsigned long dwIndex) } while(PTR_INT((pItem1 = pItem1->parent)) > 0); } - + void THuffmannTree::UninitTree() { while(PTR_INT(pLast) > 0) @@ -640,11 +645,11 @@ void THuffmannTree::UninitTree() pItem = pItem305C->Call1501DB70(pLast); pItem->RemoveItem(); } - + for(pItem = pFirst; PTR_INT(pItem3058) > 0; pItem = pItem3058) pItem->RemoveItem(); PTR_PTR(&pItem3054)->RemoveItem(); - + for(pItem = items0008 + 0x203, nCount = 0x203; nCount != 0; nCount--) { pItem--; @@ -653,7 +658,7 @@ void THuffmannTree::UninitTree() } } */ - + THTreeItem * THuffmannTree::Call1500E740(unsigned int nValue) { THTreeItem * pItem1 = pItem3058; // EDX @@ -661,7 +666,7 @@ THTreeItem * THuffmannTree::Call1500E740(unsigned int nValue) THTreeItem * pNext; THTreeItem * pPrev; THTreeItem ** ppItem; - + if(PTR_INVALID_OR_NULL(pItem1) || (pItem2 = pItem1) == NULL) { if((pItem2 = &items0008[nItems++]) != NULL) @@ -671,7 +676,7 @@ THTreeItem * THuffmannTree::Call1500E740(unsigned int nValue) } else pItem1 = pItem2; - + pNext = pItem1->next; if(pNext != NULL) { @@ -680,23 +685,23 @@ THTreeItem * THuffmannTree::Call1500E740(unsigned int nValue) pPrev = PTR_NOT(pPrev); else pPrev += (pItem1 - pItem1->next->prev); - + pPrev->next = pNext; pNext->prev = pPrev; pItem1->next = NULL; pItem1->prev = NULL; } - + ppItem = &pFirst; // esi if(nValue > 1) { // ecx = pFirst->next; pItem1->next = *ppItem; pItem1->prev = (*ppItem)->prev; - + (*ppItem)->prev = pItem2; *ppItem = pItem1; - + pItem2->parent = NULL; pItem2->child = NULL; } @@ -711,7 +716,7 @@ THTreeItem * THuffmannTree::Call1500E740(unsigned int nValue) pPrev = PTR_NOT(pPrev); pPrev->next = pItem1; pPrev->prev = pItem2; - + pItem2->parent = NULL; pItem2->child = NULL; } @@ -721,7 +726,7 @@ THTreeItem * THuffmannTree::Call1500E740(unsigned int nValue) pPrev += (THTreeItem *)ppItem - (*ppItem)->prev; else pPrev += PTR_INT(pItem305C); - + pPrev->next = pItem1; ppItem[1] = pItem2; pItem2->parent = NULL; @@ -730,18 +735,18 @@ THTreeItem * THuffmannTree::Call1500E740(unsigned int nValue) } return pItem2; } - + void THuffmannTree::Call1500E820(THTreeItem * pItem) { THTreeItem * pItem1; // edi THTreeItem * pItem2 = NULL; // eax THTreeItem * pItem3; // edx THTreeItem * pPrev; // ebx - + for(; pItem != NULL; pItem = pItem->parent) { pItem->byteValue++; - + for(pItem1 = pItem; ; pItem1 = pPrev) { pPrev = pItem1->prev; @@ -750,14 +755,14 @@ void THuffmannTree::Call1500E820(THTreeItem * pItem) pPrev = NULL; break; } - + if(pPrev->byteValue >= pItem->byteValue) break; } - + if(pItem1 == pItem) continue; - + if(pItem1->next != NULL) { pItem2 = pItem1->GetPrevItem(-1); @@ -766,7 +771,7 @@ void THuffmannTree::Call1500E820(THTreeItem * pItem) pItem1->next = NULL; pItem1->prev = NULL; } - + pItem2 = pItem->next; pItem1->next = pItem2; pItem1->prev = pItem2->prev; @@ -780,30 +785,30 @@ void THuffmannTree::Call1500E820(THTreeItem * pItem) pItem->next = NULL; pItem->prev = NULL; } - + if(pPrev == NULL) pPrev = PTR_PTR(&pFirst); - + pItem2 = pPrev->next; pItem->next = pItem2; pItem->prev = pItem2->prev; pItem2->prev = pItem; pPrev->next = pItem; - + pItem3 = pItem1->parent->child; pItem2 = pItem->parent; if(pItem2->child == pItem) pItem2->child = pItem1; if(pItem3 == pItem1) pItem1->parent->child = pItem; - + pItem2 = pItem->parent; pItem->parent = pItem1->parent; pItem1->parent = pItem2; offs0004++; } } - + // 1500E920 unsigned int THuffmannTree::DoCompression(TOutputStream * os, unsigned char * pbInBuffer, int nInLength, int nCmpType) { @@ -814,14 +819,14 @@ unsigned int THuffmannTree::DoCompression(TOutputStream * os, unsigned char * pb unsigned long dwBitBuff; unsigned int nBits; unsigned int nBit; - + BuildTree(nCmpType); bIsCmp0 = (nCmpType == 0); - + // Store the compression type into output buffer os->dwBitBuff |= (nCmpType << os->nBits); os->nBits += 8; - + // Flush completed bytes while(os->nBits >= 8) { @@ -830,22 +835,22 @@ unsigned int THuffmannTree::DoCompression(TOutputStream * os, unsigned char * pb *os->pbOutPos++ = (unsigned char)os->dwBitBuff; os->cbOutSize--; } - + os->dwBitBuff >>= 8; os->nBits -= 8; } - + for(; nInLength != 0; nInLength--) { unsigned char bOneByte = *pbInBuffer++; - + if((pItem1 = items306C[bOneByte]) == NULL) { pItem2 = items306C[0x101]; // ecx pItem3 = pItem2->parent; // eax dwBitBuff = 0; nBits = 0; - + for(; pItem3 != NULL; pItem3 = pItem3->parent) { nBit = (pItem3->child != pItem2) ? 1 : 0; @@ -854,11 +859,11 @@ unsigned int THuffmannTree::DoCompression(TOutputStream * os, unsigned char * pb pItem2 = pItem3; } os->PutBits(dwBitBuff, nBits); - + // Store the loaded byte into output stream os->dwBitBuff |= (bOneByte << os->nBits); os->nBits += 8; - + // Flush the whole byte(s) while(os->nBits >= 8) { @@ -870,34 +875,34 @@ unsigned int THuffmannTree::DoCompression(TOutputStream * os, unsigned char * pb os->dwBitBuff >>= 8; os->nBits -= 8; } - + pItem1 = (PTR_INVALID_OR_NULL(pLast)) ? NULL : pLast; pItem2 = Call1500E740(1); pItem2->dcmpByte = pItem1->dcmpByte; pItem2->byteValue = pItem1->byteValue; pItem2->parent = pItem1; items306C[pItem2->dcmpByte] = pItem2; - + pItem2 = Call1500E740(1); pItem2->dcmpByte = bOneByte; pItem2->byteValue = 0; pItem2->parent = pItem1; items306C[pItem2->dcmpByte] = pItem2; pItem1->child = pItem2; - + Call1500E820(pItem2); - + if(bIsCmp0 != 0) { Call1500E820(items306C[bOneByte]); continue; } - + for(pItem1 = items306C[bOneByte]; pItem1 != NULL; pItem1 = pItem1->parent) { pItem1->byteValue++; pItem2 = pItem1; - + for(;;) { pItem3 = pItem2->prev; @@ -910,19 +915,19 @@ unsigned int THuffmannTree::DoCompression(TOutputStream * os, unsigned char * pb break; pItem2 = pItem3; } - + if(pItem2 != pItem1) { InsertItem(&pItem305C, pItem2, SWITCH_ITEMS, pItem1); InsertItem(&pItem305C, pItem1, SWITCH_ITEMS, pItem3); - + pItem3 = pItem2->parent->child; if(pItem1->parent->child == pItem1) pItem1->parent->child = pItem2; - + if(pItem3 == pItem2) pItem2->parent->child = pItem1; - + pTemp = pItem1->parent; pItem1->parent = pItem2->parent; pItem2->parent = pTemp; @@ -944,13 +949,13 @@ unsigned int THuffmannTree::DoCompression(TOutputStream * os, unsigned char * pb } os->PutBits(dwBitBuff, nBits); } - + // 1500EB98 if(bIsCmp0 != 0) Call1500E820(items306C[bOneByte]); // 1500EB9D // 1500EBAF } // for(; nInLength != 0; nInLength--) - + // 1500EBB8 pItem1 = items306C[0x100]; dwBitBuff = 0; @@ -962,10 +967,10 @@ unsigned int THuffmannTree::DoCompression(TOutputStream * os, unsigned char * pb nBits++; pItem1 = pItem2; } - + // 1500EBE6 os->PutBits(dwBitBuff, nBits); - + // 1500EBEF // Flush the remaining bits while(os->nBits != 0) @@ -978,10 +983,10 @@ unsigned int THuffmannTree::DoCompression(TOutputStream * os, unsigned char * pb os->dwBitBuff >>= 8; os->nBits -= ((os->nBits > 8) ? 8 : os->nBits); } - + return (unsigned int)(os->pbOutPos - os->pbOutBuffer); } - + // Decompression using Huffman tree (1500E450) unsigned int THuffmannTree::DoDecompression(unsigned char * pbOutBuffer, unsigned int dwOutLength, TInputStream * is) { @@ -994,35 +999,35 @@ unsigned int THuffmannTree::DoDecompression(unsigned char * pbOutBuffer, unsigne unsigned int n8Bits; // 8 bits loaded from input stream unsigned int n7Bits; // 7 bits loaded from input stream bool bHasQdEntry; - + // Test the output length. Must not be NULL. if(dwOutLength == 0) return 0; - + // Get the compression type from the input stream n8Bits = is->Get8Bits(); - + // Build the Huffman tree - BuildTree(n8Bits); + BuildTree(n8Bits); bIsCmp0 = (n8Bits == 0) ? 1 : 0; - + for(;;) { // Security check: If we are at the end of the input buffer, - // it means that the data are corrupt. - if(is->pbInBuffer > is->pbInBufferEnd) + // it means that the data is corrupt + if(is->BitCount == 0 && is->pbInBuffer >= is->pbInBufferEnd) return 0; // Get 7 bits from input stream n7Bits = is->Get7Bits(); - + // Try to use quick decompression. Check TQDecompress array for corresponding item. // If found, ise the result byte instead. qd = &qd3474[n7Bits]; - + // If there is a quick-pass possible (ebx) bHasQdEntry = (qd->offs00 >= offs0004) ? true : false; - + // If we can use quick decompress, use it. if(bHasQdEntry) { @@ -1040,21 +1045,24 @@ unsigned int THuffmannTree::DoDecompression(unsigned char * pbOutBuffer, unsigne pItem1 = pFirst->next->prev; if(PTR_INVALID_OR_NULL(pItem1)) pItem1 = NULL; -_1500E549: +_1500E549: nBitCount = 0; pItem2 = NULL; - + do { + if(pItem1 == NULL) + return 0; + pItem1 = pItem1->child; // Move down by one level if(is->GetBit()) // If current bit is set, move to previous pItem1 = pItem1->prev; - + if(++nBitCount == 7) // If we are at 7th bit, save current HTree item. pItem2 = pItem1; } while(pItem1->child != NULL); // Walk until tree has no deeper level - + if(bHasQdEntry == false) { if(nBitCount > 7) @@ -1067,7 +1075,7 @@ unsigned int THuffmannTree::DoDecompression(unsigned char * pbOutBuffer, unsigne { unsigned long nIndex = n7Bits & (0xFFFFFFFF >> (32 - nBitCount)); unsigned long nAdd = (1 << nBitCount); - + for(qd = &qd3474[nIndex]; nIndex <= 0x7F; nIndex += nAdd, qd += nAdd) { qd->offs00 = offs0004; @@ -1078,43 +1086,43 @@ unsigned int THuffmannTree::DoDecompression(unsigned char * pbOutBuffer, unsigne } nDcmpByte = pItem1->dcmpByte; } - + if(nDcmpByte == 0x101) // Huffman tree needs to be modified { n8Bits = is->Get8Bits(); pItem1 = (PTR_INVALID_OR_NULL(pLast)) ? NULL : pLast; - + pItem2 = Call1500E740(1); pItem2->parent = pItem1; pItem2->dcmpByte = pItem1->dcmpByte; pItem2->byteValue = pItem1->byteValue; items306C[pItem2->dcmpByte] = pItem2; - + pItem2 = Call1500E740(1); pItem2->parent = pItem1; pItem2->dcmpByte = n8Bits; pItem2->byteValue = 0; items306C[pItem2->dcmpByte] = pItem2; - + pItem1->child = pItem2; Call1500E820(pItem2); if(bIsCmp0 == 0) Call1500E820(items306C[n8Bits]); - + nDcmpByte = n8Bits; } - + if(nDcmpByte == 0x100) break; - + *pbOutPos++ = (unsigned char)nDcmpByte; if(--dwOutLength == 0) break; - + if(bIsCmp0) Call1500E820(items306C[nDcmpByte]); } - + return (unsigned int)(pbOutPos - pbOutBuffer); } @@ -1140,7 +1148,7 @@ unsigned char THuffmannTree::Table1502A630[] = 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, - + // Data for compression type 0x01 0x54, 0x16, 0x16, 0x0D, 0x0C, 0x08, 0x06, 0x05, 0x06, 0x05, 0x06, 0x03, 0x04, 0x04, 0x03, 0x05, 0x0E, 0x0B, 0x14, 0x13, 0x13, 0x09, 0x0B, 0x06, 0x05, 0x04, 0x03, 0x02, 0x03, 0x02, 0x02, 0x02, @@ -1159,7 +1167,7 @@ unsigned char THuffmannTree::Table1502A630[] = 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x06, 0x4B, 0x00, 0x00, - + // Data for compression type 0x02 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x27, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1178,7 +1186,7 @@ unsigned char THuffmannTree::Table1502A630[] = 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - + // Data for compression type 0x03 0xFF, 0x0B, 0x07, 0x05, 0x0B, 0x02, 0x02, 0x02, 0x06, 0x02, 0x02, 0x01, 0x04, 0x02, 0x01, 0x03, 0x09, 0x01, 0x01, 0x01, 0x03, 0x04, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, @@ -1197,7 +1205,7 @@ unsigned char THuffmannTree::Table1502A630[] = 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x07, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x11, 0x00, 0x00, - + // Data for compression type 0x04 0xFF, 0xFB, 0x98, 0x9A, 0x84, 0x85, 0x63, 0x64, 0x3E, 0x3E, 0x22, 0x22, 0x13, 0x13, 0x18, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1216,7 +1224,7 @@ unsigned char THuffmannTree::Table1502A630[] = 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - + // Data for compression type 0x05 0xFF, 0xF1, 0x9D, 0x9E, 0x9A, 0x9B, 0x9A, 0x97, 0x93, 0x93, 0x8C, 0x8E, 0x86, 0x88, 0x80, 0x82, 0x7C, 0x7C, 0x72, 0x73, 0x69, 0x6B, 0x5F, 0x60, 0x55, 0x56, 0x4A, 0x4B, 0x40, 0x41, 0x37, 0x37, @@ -1235,7 +1243,7 @@ unsigned char THuffmannTree::Table1502A630[] = 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - + // Data for compression type 0x06 0xC3, 0xCB, 0xF5, 0x41, 0xFF, 0x7B, 0xF7, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1254,7 +1262,7 @@ unsigned char THuffmannTree::Table1502A630[] = 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - + // Data for compression type 0x07 0xC3, 0xD9, 0xEF, 0x3D, 0xF9, 0x7C, 0xE9, 0x1E, 0xFD, 0xAB, 0xF1, 0x2C, 0xFC, 0x5B, 0xFE, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1273,7 +1281,7 @@ unsigned char THuffmannTree::Table1502A630[] = 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - + // Data for compression type 0x08 0xBA, 0xC5, 0xDA, 0x33, 0xE3, 0x6D, 0xD8, 0x18, 0xE5, 0x94, 0xDA, 0x23, 0xDF, 0x4A, 0xD1, 0x10, 0xEE, 0xAF, 0xE4, 0x2C, 0xEA, 0x5A, 0xDE, 0x15, 0xF4, 0x87, 0xE9, 0x21, 0xF6, 0x43, 0xFC, 0x12, diff --git a/dep/StormLib/src/huffman/huff.h b/dep/StormLib/src/huffman/huff.h index 6af4ac638bb..d1c06e0f479 100644 --- a/dep/StormLib/src/huffman/huff.h +++ b/dep/StormLib/src/huffman/huff.h @@ -9,69 +9,67 @@ /* 03.05.03 2.00 Lad Added compression */ /* 08.12.03 2.01 Dan High-memory handling (> 0x80000000) */ /*****************************************************************************/ - + #ifndef __HUFFMAN_H__ #define __HUFFMAN_H__ #include "../StormPort.h" - + //----------------------------------------------------------------------------- // Defines - -#define INSERT_ITEM 1 + +#define INSERT_ITEM 1 #define SWITCH_ITEMS 2 // Switch the item1 and item2 - + #define PTR_NOT(ptr) (THTreeItem *)(~(DWORD_PTR)(ptr)) #define PTR_PTR(ptr) ((THTreeItem *)(ptr)) #define PTR_INT(ptr) (INT_PTR)(ptr) - + #ifndef NULL #define NULL 0 #endif - + //----------------------------------------------------------------------------- // Structures and classes - + // Input stream for Huffmann decompression class TInputStream { public: - + unsigned long GetBit(); unsigned long Get7Bits(); unsigned long Get8Bits(); void SkipBits(unsigned int BitCount); - + unsigned char * pbInBuffer; // Input data unsigned char * pbInBufferEnd; // End of the input buffer unsigned long BitBuffer; // Input bit buffer unsigned int BitCount; // Number of bits remaining in 'dwBitBuff' }; - + // Output stream for Huffmann compression class TOutputStream { public: - + void PutBits(unsigned long dwBuff, unsigned int nPutBits); - + unsigned char * pbOutBuffer; // 00 : Output buffer unsigned long cbOutSize; // 04 : Size of output buffer unsigned char * pbOutPos; // 08 : Current output position unsigned long dwBitBuff; // 0C : Bit buffer unsigned long nBits; // 10 : Number of bits in the bit buffer }; - + // Huffmann tree item (?) struct THTreeItem { - public: - THTreeItem * Call1501DB70(THTreeItem * pLast); THTreeItem * GetPrevItem(LONG_PTR value); void ClearItemLinks(); void RemoveItem(); - + THTreeItem * next; // 00 - Pointer to next THTreeItem THTreeItem * prev; // 04 - Pointer to prev THTreeItem (< 0 if none) unsigned long dcmpByte; // 08 - Index of this item in item pointer array, decompressed byte value @@ -80,7 +78,7 @@ struct THTreeItem THTreeItem * child; // 14 - Pointer to child THTreeItem int addressMultiplier; // -1 if object on negative address (>0x80000000), +1 if positive }; - + // Structure used for quick decompress. The 'bitCount' contains number of bits // and byte value contains result decompressed byte value. // After each walk through Huffman tree are filled all entries which are @@ -98,47 +96,47 @@ struct TQDecompress THTreeItem * pItem; // 08 - THTreeItem (if number of bits is greater than 7 }; }; - + // Structure for Huffman tree (Size 0x3674 bytes). Because I'm not expert // for the decompression, I do not know actually if the class is really a Hufmann // tree. If someone knows the decompression details, please let me know class THuffmannTree { public: - + THuffmannTree(); void InitTree(bool bCompression); void BuildTree(unsigned int nCmpType); // void ModifyTree(unsigned long dwIndex); // void UninitTree(); - + // void Call15007010(Bit32 dwInLength, THTreeItem * item); THTreeItem * Call1500E740(unsigned int nValue); void Call1500E820(THTreeItem * pItem); unsigned int DoCompression(TOutputStream * os, unsigned char * pbInBuffer, int nInLength, int nCmpType); unsigned int DoDecompression(unsigned char * pbOutBuffer, unsigned int dwOutLength, TInputStream * is); - + unsigned long bIsCmp0; // 0000 - 1 if compression type 0 unsigned long offs0004; // 0004 - Some flag THTreeItem items0008[0x203]; // 0008 - HTree items - + //- Sometimes used as HTree item ----------- THTreeItem * pItem3050; // 3050 - Always NULL (?) THTreeItem * pItem3054; // 3054 - Pointer to Huffman tree item THTreeItem * pItem3058; // 3058 - Pointer to Huffman tree item (< 0 if invalid) - + //- Sometimes used as HTree item ----------- THTreeItem * pItem305C; // 305C - Usually NULL THTreeItem * pFirst; // 3060 - Pointer to top (first) Huffman tree item THTreeItem * pLast; // 3064 - Pointer to bottom (last) Huffman tree item (< 0 if invalid) unsigned long nItems; // 3068 - Number of used HTree items - + //------------------------------------------- THTreeItem * items306C[0x102]; // 306C - THTreeItem pointer array TQDecompress qd3474[0x80]; // 3474 - Array for quick decompression int addressMultiplier; // -1 if object on negative address (>0x80000000), +1 if positive - + static unsigned char Table1502A630[];// Some table }; - + #endif // __HUFFMAN_H__ diff --git a/dep/StormLib/src/huffman/huff_patch.cpp b/dep/StormLib/src/huffman/huff_patch.cpp new file mode 100644 index 00000000000..6639107c554 --- /dev/null +++ b/dep/StormLib/src/huffman/huff_patch.cpp @@ -0,0 +1,1120 @@ +/*****************************************************************************/ +/* huffman.cpp Copyright (c) Ladislav Zezula 1998-2003 */ +/*---------------------------------------------------------------------------*/ +/* This module contains Huffmann (de)compression methods */ +/* */ +/* Authors : Ladislav Zezula (ladik@zezula.net) */ +/* ShadowFlare (BlakFlare@hotmail.com) */ +/* */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* xx.xx.xx 1.00 Lad The first version of dcmp.cpp */ +/* 03.05.03 1.00 Lad Added compression methods */ +/* 19.11.03 1.01 Dan Big endian handling */ +/* 08.12.03 2.01 Dan High-memory handling (> 0x80000000) */ +/*****************************************************************************/ + +#include +#include + +#include "huff.h" + +THTreeItem * gcpFirst, * gpFirst, * gcpItem3054, * gpItem3054; + +#define PTR_VALID(ptr) ((ptr) != gcpFirst && (ptr) != gcpItem3054) +#define PTR_INVALID(ptr) (!PTR_VALID(ptr)) +#define PTR_INVALID_OR_NULL(ptr) (0 == (ptr) || PTR_INVALID(ptr)) + +//----------------------------------------------------------------------------- +// Methods of the THTreeItem struct + +// 1501DB70 +THTreeItem * THTreeItem::Call1501DB70(THTreeItem * pLast) +{ + if(pLast == NULL) + pLast = this + 1; + return pLast; +} + +// Gets previous Huffman tree item (?) +THTreeItem * THTreeItem::GetPrevItem(SIntPtr value) +{ + if(PTR_INVALID(prev)) + return PTR_NOT(prev); + + if(value == -1 || PTR_INVALID((THTreeItem *) value)) + value = (SIntPtr)(this - next->prev); + return prev + value; +} + +// 1500F5E0 +void THTreeItem::ClearItemLinks() +{ + next = prev = NULL; +} + +// 1500BC90 +void THTreeItem::RemoveItem() +{ + THTreeItem * pTemp; // EDX + + if(next != NULL) + { + pTemp = prev; + + if(PTR_INVALID(pTemp)) + pTemp = PTR_NOT(pTemp); + else + pTemp += (this - next->prev); + + pTemp->next = next; + next->prev = prev; + next = prev = NULL; + } +} + +//----------------------------------------------------------------------------- +// TOutputStream functions + +void TOutputStream::PutBits(unsigned long dwBuff, unsigned int nPutBits) +{ + dwBitBuff |= (dwBuff << nBits); + nBits += nPutBits; + + // Flush completed bytes + while(nBits >= 8) + { + if(cbOutSize != 0) + { + *pbOutPos++ = (unsigned char)dwBitBuff; + cbOutSize--; + } + + dwBitBuff >>= 8; + nBits -= 8; + } +} + +//----------------------------------------------------------------------------- +// TInputStream functions + +// Gets one bit from input stream +unsigned long TInputStream::GetBit() +{ + unsigned long dwOneBit = 0; + + // Ensure that the input stream is reloaded, if there are no bits left + if(BitCount == 0) + { + // Refill the bit buffer + BitBuffer = *pbInBuffer++; + BitCount = 8; + } + + // Copy the bit from bit buffer to the variable + dwOneBit = (BitBuffer & 0x01); + BitBuffer >>= 1; + BitCount--; + + return dwOneBit; +} + +// Gets 7 bits from the stream. DOES NOT remove the bits from input stream +unsigned long TInputStream::Get7Bits() +{ + unsigned long dwReloadByte = 0; + + // If there is not enough bits to get the value, + // we have to add 8 more bits from the input buffer + if(BitCount < 7) + { + dwReloadByte = *pbInBuffer++; + BitBuffer |= dwReloadByte << BitCount; + BitCount += 8; + } + + // Return the first available 7 bits. DO NOT remove them from the input stream + return (BitBuffer & 0x7F); +} + +// Gets the whole byte from the input stream. +unsigned long TInputStream::Get8Bits() +{ + unsigned long dwReloadByte = 0; + unsigned long dwOneByte = 0; + + // If there is not enough bits to get the value, + // we have to add 8 more bits from the input buffer + if(BitCount < 8) + { + dwReloadByte = *pbInBuffer++; + BitBuffer |= dwReloadByte << BitCount; + BitCount += 8; + } + + // Return the lowest 8 its + dwOneByte = (BitBuffer & 0xFF); + BitBuffer >>= 8; + BitCount -= 8; + return dwOneByte; +} + +void TInputStream::SkipBits(unsigned int dwBitsToSkip) +{ + unsigned long dwReloadByte = 0; + + // If there is not enough bits in the buffer, + // we have to add 8 more bits from the input buffer + if(BitCount < dwBitsToSkip) + { + dwReloadByte = *pbInBuffer++; + BitBuffer |= dwReloadByte << BitCount; + BitCount += 8; + } + + // Skip the remaining bits + BitBuffer >>= dwBitsToSkip; + BitCount -= dwBitsToSkip; +} + +//----------------------------------------------------------------------------- +// Functions for huffmann tree items + +// Inserts item into the tree (?) +static void InsertItem(THTreeItem ** itemPtr, THTreeItem * item, unsigned long where, THTreeItem * item2) +{ + THTreeItem * next = item->next; // EDI - next to the first item + THTreeItem * prev = item->prev; // ESI - prev to the first item + THTreeItem * prev2; // Pointer to previous item + THTreeItem * next2; // Pointer to the next item + + // The same code like in RemoveItem(item); + if(next != 0) // If the first item already has next one + { + if(PTR_INVALID(prev)) + prev = PTR_NOT(prev); + else + prev += (item - next->prev); + + // 150083C1 + // Remove the item from the tree + prev->next = next; + next->prev = prev; + + // Invalidate 'prev' and 'next' pointer + item->next = 0; + item->prev = 0; + } + + if(item2 == NULL) // EDX - If the second item is not entered, + item2 = PTR_PTR(&itemPtr[1]); // take the first tree item + + switch(where) + { + case SWITCH_ITEMS : // Switch the two items + item->next = item2->next; // item2->next (Pointer to pointer to first) + item->prev = item2->next->prev; + item2->next->prev = item; + item2->next = item; // Set the first item + return; + + case INSERT_ITEM: // Insert as the last item + item->next = item2; // Set next item (or pointer to pointer to first item) + item->prev = item2->prev; // Set prev item (or last item in the tree) + + next2 = itemPtr[0];// Usually NULL + prev2 = item2->prev; // Prev item to the second (or last tree item) + + if(PTR_INVALID(prev2)) + { + prev2 = PTR_NOT(prev); + + prev2->next = item; + item2->prev = item; // Next after last item + return; + } + + if(PTR_INVALID(next2)) + next2 = (THTreeItem *)(item2 - item2->next->prev); + + prev2 += (long) next2; + prev2->next = item; + item2->prev = item; // Set the next/last item + return; + + default: + return; + } +} + +//----------------------------------------------------------------------------- +// THuffmannTree class functions + +THuffmannTree::THuffmannTree() +{ +} + +void THuffmannTree::InitTree(bool bCompression) +{ + THTreeItem * pItem; + unsigned int nCount; + + // Clear links for all the items in the tree + for(pItem = items0008, nCount = 0x203; nCount != 0; pItem++, nCount--) + pItem->ClearItemLinks(); + + gcpItem3054 = (THTreeItem *) &gcpItem3054; + pItem3050 = NULL; + pItem3054 = PTR_PTR(&pItem3054); + pItem3058 = gcpItem3054; + gpItem3054 = pItem3054; + + gcpFirst = (THTreeItem *) &gcpFirst; + pItem305C = NULL; + pFirst = PTR_PTR(&pFirst); + pLast = gcpFirst; + gpFirst = pFirst; + + offs0004 = 1; + nItems = 0; + + // Clear all TQDecompress items. Do this only if preparing for decompression + if(bCompression == false) + { + for(nCount = 0; nCount < sizeof(qd3474) / sizeof(TQDecompress); nCount++) + qd3474[nCount].offs00 = 0; + } +} + +// Builds Huffman tree. Called with the first 8 bits loaded from input stream +void THuffmannTree::BuildTree(unsigned int nCmpType) +{ + unsigned long maxByte; // [ESP+10] - The greatest character found in table + THTreeItem ** itemPtr; // [ESP+14] - Pointer to Huffman tree item pointer array + unsigned char * byteArray; // [ESP+1C] - Pointer to unsigned char in Table1502A630 + THTreeItem * child1; + unsigned long i; // egcs in linux doesn't like multiple for loops without an explicit i + + // Loop while pointer has a valid value + while(PTR_VALID(pLast)) // ESI - Last entry + { + THTreeItem * temp; // EAX + + if(pLast->next != NULL) // ESI->next + pLast->RemoveItem(); + // EDI = &offs3054 + pItem3058 = PTR_PTR(&pItem3054); // [EDI+4] + pLast->prev = pItem3058; // EAX + + temp = PTR_PTR(&pItem3054)->GetPrevItem((SIntPtr)(&pItem3050)); + + temp->next = pLast; + pItem3054 = pLast; + } + + // Clear all pointers in HTree item array + memset(items306C, 0, sizeof(items306C)); + + maxByte = 0; // Greatest character found init to zero. + itemPtr = (THTreeItem **)&items306C; // Pointer to current entry in HTree item pointer array + + // Ensure we have low 8 bits only + nCmpType &= 0xFF; + byteArray = Table1502A630 + nCmpType * 258; // EDI also + + for(i = 0; i < 0x100; i++, itemPtr++) + { + THTreeItem * item = pItem3058; // Item to be created + THTreeItem * pItem3 = pItem3058; + unsigned char oneByte = byteArray[i]; + + // Skip all the bytes which are zero. + if(byteArray[i] == 0) + continue; + + // If not valid pointer, take the first available item in the array + if(PTR_INVALID_OR_NULL(item)) + item = &items0008[nItems++]; + + // Insert this item as the top of the tree + InsertItem(&pItem305C, item, SWITCH_ITEMS, NULL); + + item->parent = NULL; // Invalidate child and parent + item->child = NULL; + *itemPtr = item; // Store pointer into pointer array + + item->dcmpByte = i; // Store counter + item->byteValue = oneByte; // Store byte value + if(oneByte >= maxByte) + { + maxByte = oneByte; + continue; + } + + // Find the first item which has byte value greater than current one byte + if(PTR_VALID(pItem3 = pLast)) // EDI - Pointer to the last item + { + // 15006AF7 + if(pItem3 != NULL) + { + do // 15006AFB + { + if(pItem3->byteValue >= oneByte) + goto _15006B09; + pItem3 = pItem3->prev; + } + while(PTR_VALID(pItem3)); + } + } + pItem3 = NULL; + + // 15006B09 + _15006B09: + if(item->next != NULL) + item->RemoveItem(); + + // 15006B15 + if(pItem3 == NULL) + pItem3 = PTR_PTR(&pFirst); + + // 15006B1F + item->next = pItem3->next; + item->prev = pItem3->next->prev; + pItem3->next->prev = item; + pItem3->next = item; + } + + // 15006B4A + for(; i < 0x102; i++) + { + THTreeItem ** itemPtr = &items306C[i]; // EDI + + // 15006B59 + THTreeItem * item = pItem3058; // ESI + if(PTR_INVALID_OR_NULL(item)) + item = &items0008[nItems++]; + + InsertItem(&pItem305C, item, INSERT_ITEM, NULL); + + // 15006B89 + item->dcmpByte = i; + item->byteValue = 1; + item->parent = NULL; + item->child = NULL; + *itemPtr++ = item; + } + + // 15006BAA + if(PTR_VALID(child1 = pLast)) // EDI - last item (first child to item + { + THTreeItem * child2; // EBP + THTreeItem * item; // ESI + + // 15006BB8 + while(PTR_VALID(child2 = child1->prev)) + { + if(PTR_INVALID_OR_NULL(item = pItem3058)) + item = &items0008[nItems++]; + + // 15006BE3 + InsertItem(&pItem305C, item, SWITCH_ITEMS, NULL); + + // 15006BF3 + item->parent = NULL; + item->child = NULL; + + //EDX = child2->byteValue + child1->byteValue; + //EAX = child1->byteValue; + //ECX = maxByte; // The greatest character (0xFF usually) + + item->byteValue = child1->byteValue + child2->byteValue; // 0x02 + item->child = child1; // Prev item in the + child1->parent = item; + child2->parent = item; + + // EAX = item->byteValue; + if(item->byteValue >= maxByte) + maxByte = item->byteValue; + else + { + THTreeItem * pItem2 = child2->prev; // EDI + + // 15006C2D + while(PTR_VALID(pItem2)) + { + if(pItem2->byteValue >= item->byteValue) + goto _15006C3B; + pItem2 = pItem2->prev; + } + pItem2 = NULL; + + _15006C3B: + if(item->next != 0) + { + THTreeItem * temp4 = item->GetPrevItem(-1); + + temp4->next = item->next; // The first item changed + item->next->prev = item->prev; // First->prev changed to negative value + item->next = NULL; + item->prev = NULL; + } + + // 15006C62 + if(pItem2 == NULL) + pItem2 = PTR_PTR(&pFirst); + + item->next = pItem2->next; // Set item with 0x100 byte value + item->prev = pItem2->next->prev; // Set item with 0x17 byte value + pItem2->next->prev = item; // Changed prev of item with + pItem2->next = item; + } + + // 15006C7B + if(PTR_INVALID_OR_NULL(child1 = child2->prev)) + break; + } + } + // 15006C88 + offs0004 = 1; +} + +THTreeItem * THuffmannTree::Call1500E740(unsigned int nValue) +{ + THTreeItem * pItem1 = pItem3058; // EDX + THTreeItem * pItem2; // EAX + THTreeItem * pNext; + THTreeItem * pPrev; + THTreeItem ** ppItem; + + if(PTR_INVALID_OR_NULL(pItem1) || (pItem2 = pItem1) == NULL) + { + if((pItem2 = &items0008[nItems++]) != NULL) + pItem1 = pItem2; + else + pItem1 = pFirst; + } + else + pItem1 = pItem2; + + pNext = pItem1->next; + if(pNext != NULL) + { + pPrev = pItem1->prev; + if(PTR_INVALID(pPrev)) + pPrev = PTR_NOT(pPrev); + else + pPrev += (pItem1 - pItem1->next->prev); + + pPrev->next = pNext; + pNext->prev = pPrev; + pItem1->next = NULL; + pItem1->prev = NULL; + } + + ppItem = &pFirst; // esi + if(nValue > 1) + { + // ecx = pFirst->next; + pItem1->next = *ppItem; + pItem1->prev = (*ppItem)->prev; + + (*ppItem)->prev = pItem2; + *ppItem = pItem1; + + pItem2->parent = NULL; + pItem2->child = NULL; + } + else + { + pItem1->next = (THTreeItem *)ppItem; + pItem1->prev = ppItem[1]; + // edi = pItem305C; + pPrev = ppItem[1]; // ecx + if(PTR_INVALID(pPrev)) + { + pPrev = PTR_NOT(pPrev); + pPrev->next = pItem1; + pPrev->prev = pItem2; + + pItem2->parent = NULL; + pItem2->child = NULL; + } + else + { + if(PTR_INVALID(pItem305C)) + pPrev += (THTreeItem *)ppItem - (*ppItem)->prev; + else + pPrev += (long)pItem305C; + + pPrev->next = pItem1; + ppItem[1] = pItem2; + pItem2->parent = NULL; + pItem2->child = NULL; + } + } + return pItem2; +} + +void THuffmannTree::Call1500E820(THTreeItem * pItem) +{ + THTreeItem * pItem1; // edi + THTreeItem * pItem2 = NULL; // eax + THTreeItem * pItem3; // edx + THTreeItem * pPrev; // ebx + + for(; pItem != NULL; pItem = pItem->parent) + { + pItem->byteValue++; + + for(pItem1 = pItem; ; pItem1 = pPrev) + { + pPrev = pItem1->prev; + if(PTR_INVALID_OR_NULL(pPrev)) + { + pPrev = NULL; + break; + } + + if(pPrev->byteValue >= pItem->byteValue) + break; + } + + if(pItem1 == pItem) + continue; + + if(pItem1->next != NULL) + { + pItem2 = pItem1->GetPrevItem(-1); + pItem2->next = pItem1->next; + pItem1->next->prev = pItem1->prev; + pItem1->next = NULL; + pItem1->prev = NULL; + } + + pItem2 = pItem->next; + pItem1->next = pItem2; + pItem1->prev = pItem2->prev; + pItem2->prev = pItem1; + pItem->next = pItem1; + if((pItem2 = pItem1) != NULL) + { + pItem2 = pItem->GetPrevItem(-1); + pItem2->next = pItem->next; + pItem->next->prev = pItem->prev; + pItem->next = NULL; + pItem->prev = NULL; + } + + if(pPrev == NULL) + pPrev = PTR_PTR(&pFirst); + + pItem2 = pPrev->next; + pItem->next = pItem2; + pItem->prev = pItem2->prev; + pItem2->prev = pItem; + pPrev->next = pItem; + + pItem3 = pItem1->parent->child; + pItem2 = pItem->parent; + if(pItem2->child == pItem) + pItem2->child = pItem1; + if(pItem3 == pItem1) + pItem1->parent->child = pItem; + + pItem2 = pItem->parent; + pItem->parent = pItem1->parent; + pItem1->parent = pItem2; + offs0004++; + } +} + +// 1500E920 +unsigned int THuffmannTree::DoCompression(TOutputStream * os, unsigned char * pbInBuffer, int nInLength, int nCmpType) +{ + THTreeItem * pItem1; + THTreeItem * pItem2; + THTreeItem * pItem3; + THTreeItem * pTemp; + unsigned long dwBitBuff; + unsigned int nBits; + unsigned int nBit; + + BuildTree(nCmpType); + bIsCmp0 = (nCmpType == 0); + + // Store the compression type into output buffer + os->dwBitBuff |= (nCmpType << os->nBits); + os->nBits += 8; + + // Flush completed bytes + while(os->nBits >= 8) + { + if(os->cbOutSize != 0) + { + *os->pbOutPos++ = (unsigned char)os->dwBitBuff; + os->cbOutSize--; + } + + os->dwBitBuff >>= 8; + os->nBits -= 8; + } + + for(; nInLength != 0; nInLength--) + { + unsigned char bOneByte = *pbInBuffer++; + + if((pItem1 = items306C[bOneByte]) == NULL) + { + pItem2 = items306C[0x101]; // ecx + pItem3 = pItem2->parent; // eax + dwBitBuff = 0; + nBits = 0; + + for(; pItem3 != NULL; pItem3 = pItem3->parent) + { + nBit = (pItem3->child != pItem2) ? 1 : 0; + dwBitBuff = (dwBitBuff << 1) | nBit; + nBits++; + pItem2 = pItem3; + } + os->PutBits(dwBitBuff, nBits); + + // Store the loaded byte into output stream + os->dwBitBuff |= (bOneByte << os->nBits); + os->nBits += 8; + + // Flush the whole byte(s) + while(os->nBits >= 8) + { + if(os->cbOutSize != 0) + { + *os->pbOutPos++ = (unsigned char)os->dwBitBuff; + os->cbOutSize--; + } + os->dwBitBuff >>= 8; + os->nBits -= 8; + } + + pItem1 = (PTR_INVALID_OR_NULL(pLast)) ? NULL : pLast; + pItem2 = Call1500E740(1); + pItem2->dcmpByte = pItem1->dcmpByte; + pItem2->byteValue = pItem1->byteValue; + pItem2->parent = pItem1; + items306C[pItem2->dcmpByte] = pItem2; + + pItem2 = Call1500E740(1); + pItem2->dcmpByte = bOneByte; + pItem2->byteValue = 0; + pItem2->parent = pItem1; + items306C[pItem2->dcmpByte] = pItem2; + pItem1->child = pItem2; + + Call1500E820(pItem2); + + if(bIsCmp0 != 0) + { + Call1500E820(items306C[bOneByte]); + continue; + } + + for(pItem1 = items306C[bOneByte]; pItem1 != NULL; pItem1 = pItem1->parent) + { + pItem1->byteValue++; + pItem2 = pItem1; + + for(;;) + { + pItem3 = pItem2->prev; + if(PTR_INVALID_OR_NULL(pItem3)) + { + pItem3 = NULL; + break; + } + if(pItem3->byteValue >= pItem1->byteValue) + break; + pItem2 = pItem3; + } + + if(pItem2 != pItem1) + { + InsertItem(&pItem305C, pItem2, SWITCH_ITEMS, pItem1); + InsertItem(&pItem305C, pItem1, SWITCH_ITEMS, pItem3); + + pItem3 = pItem2->parent->child; + if(pItem1->parent->child == pItem1) + pItem1->parent->child = pItem2; + + if(pItem3 == pItem2) + pItem2->parent->child = pItem1; + + pTemp = pItem1->parent; + pItem1->parent = pItem2->parent; + pItem2->parent = pTemp; + offs0004++; + } + } + } +// 1500EB62 + else + { + dwBitBuff = 0; + nBits = 0; + for(pItem2 = pItem1->parent; pItem2 != NULL; pItem2 = pItem2->parent) + { + nBit = (pItem2->child != pItem1) ? 1 : 0; + dwBitBuff = (dwBitBuff << 1) | nBit; + nBits++; + pItem1 = pItem2; + } + os->PutBits(dwBitBuff, nBits); + } + +// 1500EB98 + if(bIsCmp0 != 0) + Call1500E820(items306C[bOneByte]); // 1500EB9D +// 1500EBAF + } // for(; nInLength != 0; nInLength--) + +// 1500EBB8 + pItem1 = items306C[0x100]; + dwBitBuff = 0; + nBits = 0; + for(pItem2 = pItem1->parent; pItem2 != NULL; pItem2 = pItem2->parent) + { + nBit = (pItem2->child != pItem1) ? 1 : 0; + dwBitBuff = (dwBitBuff << 1) | nBit; + nBits++; + pItem1 = pItem2; + } + +// 1500EBE6 + os->PutBits(dwBitBuff, nBits); + +// 1500EBEF + // Flush the remaining bits + while(os->nBits != 0) + { + if(os->cbOutSize != 0) + { + *os->pbOutPos++ = (unsigned char)os->dwBitBuff; + os->cbOutSize--; + } + os->dwBitBuff >>= 8; + os->nBits -= ((os->nBits > 8) ? 8 : os->nBits); + } + + return (unsigned int)(os->pbOutPos - os->pbOutBuffer); +} + +// Decompression using Huffman tree (1500E450) +unsigned int THuffmannTree::DoDecompression(unsigned char * pbOutBuffer, unsigned int dwOutLength, TInputStream * is) +{ + TQDecompress * qd; + THTreeItem * pItem1; + THTreeItem * pItem2; + unsigned char * pbOutPos = pbOutBuffer; + unsigned long nBitCount; + unsigned int nDcmpByte = 0; + unsigned int n8Bits; // 8 bits loaded from input stream + unsigned int n7Bits; // 7 bits loaded from input stream + bool bHasQdEntry; + + // Test the output length. Must not be NULL. + if(dwOutLength == 0) + return 0; + + // Get the compression type from the input stream + n8Bits = is->Get8Bits(); + + // Build the Huffman tree + BuildTree(n8Bits); + bIsCmp0 = (n8Bits == 0) ? 1 : 0; + + for(;;) + { + // Security check: If we are at the end of the input buffer, + // it means that the data are corrupt. + if(is->pbInBuffer > is->pbInBufferEnd) + return 0; + + // Get 7 bits from input stream + n7Bits = is->Get7Bits(); + + // Try to use quick decompression. Check TQDecompress array for corresponding item. + // If found, ise the result byte instead. + qd = &qd3474[n7Bits]; + + // If there is a quick-pass possible (ebx) + bHasQdEntry = (qd->offs00 >= offs0004) ? true : false; + + // If we can use quick decompress, use it. + if(bHasQdEntry) + { + if(qd->nBits > 7) + { + is->SkipBits(7); + pItem1 = qd->pItem; + goto _1500E549; + } + is->SkipBits(qd->nBits); + nDcmpByte = qd->dcmpByte; + } + else + { + pItem1 = pFirst->next->prev; + if(PTR_INVALID_OR_NULL(pItem1)) + pItem1 = NULL; +_1500E549: + nBitCount = 0; + pItem2 = NULL; + + do + { + pItem1 = pItem1->child; // Move down by one level + if(is->GetBit()) // If current bit is set, move to previous + pItem1 = pItem1->prev; + + if(++nBitCount == 7) // If we are at 7th bit, save current HTree item. + pItem2 = pItem1; + } + while(pItem1->child != NULL); // Walk until tree has no deeper level + + if(bHasQdEntry == false) + { + if(nBitCount > 7) + { + qd->offs00 = offs0004; + qd->nBits = nBitCount; + qd->pItem = pItem2; + } + else + { + unsigned long nIndex = n7Bits & (0xFFFFFFFF >> (32 - nBitCount)); + unsigned long nAdd = (1 << nBitCount); + + for(qd = &qd3474[nIndex]; nIndex <= 0x7F; nIndex += nAdd, qd += nAdd) + { + qd->offs00 = offs0004; + qd->nBits = nBitCount; + qd->dcmpByte = pItem1->dcmpByte; + } + } + } + nDcmpByte = pItem1->dcmpByte; + } + + if(nDcmpByte == 0x101) // Huffman tree needs to be modified + { + n8Bits = is->Get8Bits(); + pItem1 = (PTR_INVALID_OR_NULL(pLast)) ? NULL : pLast; + + pItem2 = Call1500E740(1); + pItem2->parent = pItem1; + pItem2->dcmpByte = pItem1->dcmpByte; + pItem2->byteValue = pItem1->byteValue; + items306C[pItem2->dcmpByte] = pItem2; + + pItem2 = Call1500E740(1); + pItem2->parent = pItem1; + pItem2->dcmpByte = n8Bits; + pItem2->byteValue = 0; + items306C[pItem2->dcmpByte] = pItem2; + + pItem1->child = pItem2; + Call1500E820(pItem2); + if(bIsCmp0 == 0) + Call1500E820(items306C[n8Bits]); + + nDcmpByte = n8Bits; + } + + if(nDcmpByte == 0x100) + break; + + *pbOutPos++ = (unsigned char)nDcmpByte; + if(--dwOutLength == 0) + break; + + if(bIsCmp0) + Call1500E820(items306C[nDcmpByte]); + } + + return (unsigned int)(pbOutPos - pbOutBuffer); +} + + +// Table for (de)compression. Every compression type has 258 entries +unsigned char THuffmannTree::Table1502A630[] = +{ + // Data for compression type 0x00 + 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, + + // Data for compression type 0x01 + 0x54, 0x16, 0x16, 0x0D, 0x0C, 0x08, 0x06, 0x05, 0x06, 0x05, 0x06, 0x03, 0x04, 0x04, 0x03, 0x05, + 0x0E, 0x0B, 0x14, 0x13, 0x13, 0x09, 0x0B, 0x06, 0x05, 0x04, 0x03, 0x02, 0x03, 0x02, 0x02, 0x02, + 0x0D, 0x07, 0x09, 0x06, 0x06, 0x04, 0x03, 0x02, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, + 0x09, 0x06, 0x04, 0x04, 0x04, 0x04, 0x03, 0x02, 0x03, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x04, + 0x08, 0x03, 0x04, 0x07, 0x09, 0x05, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, + 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, + 0x06, 0x0A, 0x08, 0x08, 0x06, 0x07, 0x04, 0x03, 0x04, 0x04, 0x02, 0x02, 0x04, 0x02, 0x03, 0x03, + 0x04, 0x03, 0x07, 0x07, 0x09, 0x06, 0x04, 0x03, 0x03, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x0A, 0x02, 0x02, 0x03, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x06, 0x03, 0x05, 0x02, 0x03, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x03, 0x01, 0x01, 0x01, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x04, 0x04, 0x04, 0x07, 0x09, 0x08, 0x0C, 0x02, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x03, + 0x04, 0x01, 0x02, 0x04, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, + 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x06, 0x4B, + 0x00, 0x00, + + // Data for compression type 0x02 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x27, 0x00, 0x00, 0x23, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x06, 0x0E, 0x10, 0x04, + 0x06, 0x08, 0x05, 0x04, 0x04, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, 0x01, 0x01, 0x02, 0x01, 0x01, + 0x01, 0x04, 0x02, 0x04, 0x02, 0x02, 0x02, 0x01, 0x01, 0x04, 0x01, 0x01, 0x02, 0x03, 0x03, 0x02, + 0x03, 0x01, 0x03, 0x06, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x01, 0x01, + 0x01, 0x29, 0x07, 0x16, 0x12, 0x40, 0x0A, 0x0A, 0x11, 0x25, 0x01, 0x03, 0x17, 0x10, 0x26, 0x2A, + 0x10, 0x01, 0x23, 0x23, 0x2F, 0x10, 0x06, 0x07, 0x02, 0x09, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + + // Data for compression type 0x03 + 0xFF, 0x0B, 0x07, 0x05, 0x0B, 0x02, 0x02, 0x02, 0x06, 0x02, 0x02, 0x01, 0x04, 0x02, 0x01, 0x03, + 0x09, 0x01, 0x01, 0x01, 0x03, 0x04, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, + 0x05, 0x01, 0x01, 0x01, 0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x0A, 0x04, 0x02, 0x01, 0x06, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x01, + 0x05, 0x02, 0x03, 0x04, 0x03, 0x03, 0x03, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, 0x03, 0x03, + 0x01, 0x03, 0x01, 0x01, 0x02, 0x05, 0x01, 0x01, 0x04, 0x03, 0x05, 0x01, 0x03, 0x01, 0x03, 0x03, + 0x02, 0x01, 0x04, 0x03, 0x0A, 0x06, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x01, 0x0A, 0x02, 0x05, 0x01, 0x01, 0x02, 0x07, 0x02, 0x17, 0x01, 0x05, 0x01, 0x01, + 0x0E, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x06, 0x02, 0x01, 0x04, 0x05, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x07, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x11, + 0x00, 0x00, + + // Data for compression type 0x04 + 0xFF, 0xFB, 0x98, 0x9A, 0x84, 0x85, 0x63, 0x64, 0x3E, 0x3E, 0x22, 0x22, 0x13, 0x13, 0x18, 0x17, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + + // Data for compression type 0x05 + 0xFF, 0xF1, 0x9D, 0x9E, 0x9A, 0x9B, 0x9A, 0x97, 0x93, 0x93, 0x8C, 0x8E, 0x86, 0x88, 0x80, 0x82, + 0x7C, 0x7C, 0x72, 0x73, 0x69, 0x6B, 0x5F, 0x60, 0x55, 0x56, 0x4A, 0x4B, 0x40, 0x41, 0x37, 0x37, + 0x2F, 0x2F, 0x27, 0x27, 0x21, 0x21, 0x1B, 0x1C, 0x17, 0x17, 0x13, 0x13, 0x10, 0x10, 0x0D, 0x0D, + 0x0B, 0x0B, 0x09, 0x09, 0x08, 0x08, 0x07, 0x07, 0x06, 0x05, 0x05, 0x04, 0x04, 0x04, 0x19, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + + // Data for compression type 0x06 + 0xC3, 0xCB, 0xF5, 0x41, 0xFF, 0x7B, 0xF7, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xBF, 0xCC, 0xF2, 0x40, 0xFD, 0x7C, 0xF7, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7A, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + + // Data for compression type 0x07 + 0xC3, 0xD9, 0xEF, 0x3D, 0xF9, 0x7C, 0xE9, 0x1E, 0xFD, 0xAB, 0xF1, 0x2C, 0xFC, 0x5B, 0xFE, 0x17, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xBD, 0xD9, 0xEC, 0x3D, 0xF5, 0x7D, 0xE8, 0x1D, 0xFB, 0xAE, 0xF0, 0x2C, 0xFB, 0x5C, 0xFF, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + + // Data for compression type 0x08 + 0xBA, 0xC5, 0xDA, 0x33, 0xE3, 0x6D, 0xD8, 0x18, 0xE5, 0x94, 0xDA, 0x23, 0xDF, 0x4A, 0xD1, 0x10, + 0xEE, 0xAF, 0xE4, 0x2C, 0xEA, 0x5A, 0xDE, 0x15, 0xF4, 0x87, 0xE9, 0x21, 0xF6, 0x43, 0xFC, 0x12, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xB0, 0xC7, 0xD8, 0x33, 0xE3, 0x6B, 0xD6, 0x18, 0xE7, 0x95, 0xD8, 0x23, 0xDB, 0x49, 0xD0, 0x11, + 0xE9, 0xB2, 0xE2, 0x2B, 0xE8, 0x5C, 0xDD, 0x15, 0xF1, 0x87, 0xE7, 0x20, 0xF7, 0x44, 0xFF, 0x13, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5F, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 +}; diff --git a/dep/StormLib/src/huffman/huff_patch.h b/dep/StormLib/src/huffman/huff_patch.h new file mode 100644 index 00000000000..ca2390c1831 --- /dev/null +++ b/dep/StormLib/src/huffman/huff_patch.h @@ -0,0 +1,145 @@ +/*****************************************************************************/ +/* huffman.h Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* Description : */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* xx.xx.xx 1.00 Lad The first version of huffman.h */ +/* 03.05.03 2.00 Lad Added compression */ +/* 08.12.03 2.01 Dan High-memory handling (> 0x80000000) */ +/*****************************************************************************/ + +#ifndef __HUFFMAN_H__ +#define __HUFFMAN_H__ + +#include + +#define SIntPtr intptr_t + +//----------------------------------------------------------------------------- +// Defines + +#define INSERT_ITEM 1 +#define SWITCH_ITEMS 2 // Switch the item1 and item2 + +#define PTR_NOT(ptr) ((ptr) == gcpFirst ? gpFirst : gpItem3054) +#define PTR_PTR(ptr) ((THTreeItem *)(ptr)) + +#ifndef NULL +#define NULL 0 +#endif + +//----------------------------------------------------------------------------- +// Structures and classes + +// Input stream for Huffmann decompression +class TInputStream +{ + public: + + unsigned long GetBit(); + unsigned long Get7Bits(); + unsigned long Get8Bits(); + void SkipBits(unsigned int BitCount); + + unsigned char * pbInBuffer; // Input data + unsigned char * pbInBufferEnd; // End of the input buffer + unsigned long BitBuffer; // Input bit buffer + unsigned int BitCount; // Number of bits remaining in 'dwBitBuff' +}; + +// Output stream for Huffmann compression +class TOutputStream +{ + public: + + void PutBits(unsigned long dwBuff, unsigned int nPutBits); + + unsigned char * pbOutBuffer; // 00 : Output buffer + unsigned long cbOutSize; // 04 : Size of output buffer + unsigned char * pbOutPos; // 08 : Current output position + unsigned long dwBitBuff; // 0C : Bit buffer + unsigned long nBits; // 10 : Number of bits in the bit buffer +}; + +// Huffmann tree item (?) +struct THTreeItem +{ + public: + + THTreeItem * Call1501DB70(THTreeItem * pLast); + THTreeItem * GetPrevItem(SIntPtr value); + void ClearItemLinks(); + void RemoveItem(); + + THTreeItem * next; // 00 - Pointer to next THTreeItem + THTreeItem * prev; // 04 - Pointer to prev THTreeItem (< 0 if none) + unsigned long dcmpByte; // 08 - Index of this item in item pointer array, decompressed byte value + unsigned long byteValue; // 0C - Some byte value + THTreeItem * parent; // 10 - Pointer to parent THTreeItem (NULL if none) + THTreeItem * child; // 14 - Pointer to child THTreeItem + int addressMultiplier; // -1 if object on negative address (>0x80000000), +1 if positive +}; + +// Structure used for quick decompress. The 'bitCount' contains number of bits +// and byte value contains result decompressed byte value. +// After each walk through Huffman tree are filled all entries which are +// multiplies of number of bits loaded from input stream. These entries +// contain number of bits and result value. At the next 7 bits is tested this +// structure first. If corresponding entry found, decompression routine will +// not walk through Huffman tree and directly stores output byte to output stream. +struct TQDecompress +{ + unsigned long offs00; // 00 - 1 if resolved + unsigned long nBits; // 04 - Bit count + union + { + unsigned long dcmpByte; // 08 - Byte value for decompress (if bitCount <= 7) + THTreeItem * pItem; // 08 - THTreeItem (if number of bits is greater than 7 + }; +}; + +// Structure for Huffman tree (Size 0x3674 bytes). Because I'm not expert +// for the decompression, I do not know actually if the class is really a Hufmann +// tree. If someone knows the decompression details, please let me know +class THuffmannTree +{ + public: + + THuffmannTree(); + void InitTree(bool bCompression); + void BuildTree(unsigned int nCmpType); +// void ModifyTree(unsigned long dwIndex); +// void UninitTree(); + +// void Call15007010(Bit32 dwInLength, THTreeItem * item); + THTreeItem * Call1500E740(unsigned int nValue); + void Call1500E820(THTreeItem * pItem); + unsigned int DoCompression(TOutputStream * os, unsigned char * pbInBuffer, int nInLength, int nCmpType); + unsigned int DoDecompression(unsigned char * pbOutBuffer, unsigned int dwOutLength, TInputStream * is); + + unsigned long bIsCmp0; // 0000 - 1 if compression type 0 + unsigned long offs0004; // 0004 - Some flag + THTreeItem items0008[0x203]; // 0008 - HTree items + + //- Sometimes used as HTree item ----------- + THTreeItem * pItem3050; // 3050 - Always NULL (?) + THTreeItem * pItem3054; // 3054 - Pointer to Huffman tree item + THTreeItem * pItem3058; // 3058 - Pointer to Huffman tree item (< 0 if invalid) + + //- Sometimes used as HTree item ----------- + THTreeItem * pItem305C; // 305C - Usually NULL + THTreeItem * pFirst; // 3060 - Pointer to top (first) Huffman tree item + THTreeItem * pLast; // 3064 - Pointer to bottom (last) Huffman tree item (< 0 if invalid) + unsigned long nItems; // 3068 - Number of used HTree items + + //------------------------------------------- + THTreeItem * items306C[0x102]; // 306C - THTreeItem pointer array + TQDecompress qd3474[0x80]; // 3474 - Array for quick decompression + int addressMultiplier; // -1 if object on negative address (>0x80000000), +1 if positive + + static unsigned char Table1502A630[];// Some table +}; + +#endif // __HUFFMAN_H__ diff --git a/dep/StormLib/src/lzma/info.txt b/dep/StormLib/src/lzma/info.txt new file mode 100644 index 00000000000..4cee86e3542 --- /dev/null +++ b/dep/StormLib/src/lzma/info.txt @@ -0,0 +1 @@ +Taken from LZMA SDK v 9.11 \ No newline at end of file diff --git a/dep/StormLib/src/pklib/explode.c b/dep/StormLib/src/pklib/explode.c index 0d327a5da84..510845c4210 100644 --- a/dep/StormLib/src/pklib/explode.c +++ b/dep/StormLib/src/pklib/explode.c @@ -16,7 +16,7 @@ #include "pklib.h" -#define PKDCL_OK 0 +#define PKDCL_OK 0 #define PKDCL_STREAM_END 1 // All data from the input stream is read #define PKDCL_NEED_DICT 2 // Need more data (dictionary) #define PKDCL_CONTINUE 10 // Internal flag, not returned to user @@ -31,7 +31,7 @@ char CopyrightPkware[] = "PKWARE Data Compression Library for Win32\r\n" //----------------------------------------------------------------------------- // Tables -static unsigned char DistBits[] = +static unsigned char DistBits[] = { 0x02, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, @@ -39,7 +39,7 @@ static unsigned char DistBits[] = 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 }; -static unsigned char DistCode[] = +static unsigned char DistCode[] = { 0x03, 0x0D, 0x05, 0x19, 0x09, 0x11, 0x01, 0x3E, 0x1E, 0x2E, 0x0E, 0x36, 0x16, 0x26, 0x06, 0x3A, 0x1A, 0x2A, 0x0A, 0x32, 0x12, 0x22, 0x42, 0x02, 0x7C, 0x3C, 0x5C, 0x1C, 0x6C, 0x2C, 0x4C, 0x0C, @@ -88,7 +88,7 @@ static unsigned char ChBitsAsc[] = 0x0D, 0x0D, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D }; -static unsigned short ChCodeAsc[] = +static unsigned short ChCodeAsc[] = { 0x0490, 0x0FE0, 0x07E0, 0x0BE0, 0x03E0, 0x0DE0, 0x05E0, 0x09E0, 0x01E0, 0x00B8, 0x0062, 0x0EE0, 0x06E0, 0x0022, 0x0AE0, 0x02E0, @@ -121,7 +121,7 @@ static unsigned short ChCodeAsc[] = 0x0300, 0x0D40, 0x1D00, 0x0D00, 0x1500, 0x0540, 0x0500, 0x1900, 0x0900, 0x0940, 0x1100, 0x0100, 0x1E00, 0x0E00, 0x0140, 0x1600, 0x0600, 0x1A00, 0x0E40, 0x0640, 0x0A40, 0x0A00, 0x1200, 0x0200, - 0x1C00, 0x0C00, 0x1400, 0x0400, 0x1800, 0x0800, 0x1000, 0x0000 + 0x1C00, 0x0C00, 0x1400, 0x0400, 0x1800, 0x0800, 0x1000, 0x0000 }; //----------------------------------------------------------------------------- @@ -278,11 +278,11 @@ static unsigned long DecodeLit(TDcmpStruct * pWork) { // Remove one bit from the input data if(WasteBits(pWork, 1)) - return 0x306; - + return 0x306; + // The next 8 bits hold the index to the length code table length_code = pWork->LengthCodes[pWork->bit_buff & 0xFF]; - + // Remove the apropriate number of bits if(WasteBits(pWork, pWork->LenBits[length_code])) return 0x306; @@ -442,7 +442,7 @@ static unsigned long Expand(TDcmpStruct * pWork) { pWork->out_buff[pWork->outputPos++] = (unsigned char)next_literal; } - + // Flush the output buffer, if number of extracted bytes has reached the end if(pWork->outputPos >= 0x2000) { @@ -495,7 +495,7 @@ unsigned int explode( pWork->in_pos = 3; // Position in input buffer // Test for the valid dictionary size - if(4 > pWork->dsize_bits || pWork->dsize_bits > 6) + if(4 > pWork->dsize_bits || pWork->dsize_bits > 6) return CMP_INVALID_DICTSIZE; pWork->dsize_mask = 0xFFFF >> (0x10 - pWork->dsize_bits); // Shifted by 'sar' instruction @@ -517,6 +517,6 @@ unsigned int explode( GenDecodeTabs(pWork->DistPosCodes, DistCode, pWork->DistBits, sizeof(pWork->DistBits)); if(Expand(pWork) != 0x306) return CMP_NO_ERROR; - + return CMP_ABORT; } diff --git a/dep/StormLib/src/pklib/implode.c b/dep/StormLib/src/pklib/implode.c index 1771b1862a0..fbbc6d8da30 100644 --- a/dep/StormLib/src/pklib/implode.c +++ b/dep/StormLib/src/pklib/implode.c @@ -27,7 +27,7 @@ //----------------------------------------------------------------------------- // Tables -static unsigned char DistBits[] = +static unsigned char DistBits[] = { 0x02, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, @@ -35,7 +35,7 @@ static unsigned char DistBits[] = 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 }; -static unsigned char DistCode[] = +static unsigned char DistCode[] = { 0x03, 0x0D, 0x05, 0x19, 0x09, 0x11, 0x01, 0x3E, 0x1E, 0x2E, 0x0E, 0x36, 0x16, 0x26, 0x06, 0x3A, 0x1A, 0x2A, 0x0A, 0x32, 0x12, 0x22, 0x42, 0x02, 0x7C, 0x3C, 0x5C, 0x1C, 0x6C, 0x2C, 0x4C, 0x0C, @@ -78,7 +78,7 @@ static unsigned char ChBitsAsc[] = 0x0D, 0x0D, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D }; -static unsigned short ChCodeAsc[] = +static unsigned short ChCodeAsc[] = { 0x0490, 0x0FE0, 0x07E0, 0x0BE0, 0x03E0, 0x0DE0, 0x05E0, 0x09E0, 0x01E0, 0x00B8, 0x0062, 0x0EE0, 0x06E0, 0x0022, 0x0AE0, 0x02E0, @@ -111,7 +111,7 @@ static unsigned short ChCodeAsc[] = 0x0300, 0x0D40, 0x1D00, 0x0D00, 0x1500, 0x0540, 0x0500, 0x1900, 0x0900, 0x0940, 0x1100, 0x0100, 0x1E00, 0x0E00, 0x0140, 0x1600, 0x0600, 0x1A00, 0x0E40, 0x0640, 0x0A40, 0x0A00, 0x1200, 0x0200, - 0x1C00, 0x0C00, 0x1400, 0x0400, 0x1800, 0x0800, 0x1000, 0x0000 + 0x1C00, 0x0C00, 0x1400, 0x0400, 0x1800, 0x0800, 0x1000, 0x0000 }; //----------------------------------------------------------------------------- @@ -140,7 +140,7 @@ static void SortBuffer(TCmpStruct * pWork, unsigned char * buffer_begin, unsigne // Zero the entire "phash_to_index" table memset(pWork->phash_to_index, 0, sizeof(pWork->phash_to_index)); - + // Step 1: Count amount of each PAIR_HASH in the input buffer // The table will look like this: // offs 0x000: Number of occurences of PAIR_HASH 0 @@ -150,7 +150,7 @@ static void SortBuffer(TCmpStruct * pWork, unsigned char * buffer_begin, unsigne for(buffer_ptr = buffer_begin; buffer_ptr < buffer_end; buffer_ptr++) pWork->phash_to_index[BYTE_PAIR_HASH(buffer_ptr)]++; - // Step 2: Convert the table to the array of PAIR_HASH amounts. + // Step 2: Convert the table to the array of PAIR_HASH amounts. // Each element contains count of PAIR_HASHes that is less or equal // to element index // The table will look like this: @@ -218,7 +218,7 @@ static void OutputBits(TCmpStruct * pWork, unsigned int nbits, unsigned long bit { pWork->out_bytes++; bit_buff >>= (8 - out_bits); - + pWork->out_buff[pWork->out_bytes] = (unsigned char)bit_buff; pWork->out_bits &= 7; } @@ -236,7 +236,7 @@ static void OutputBits(TCmpStruct * pWork, unsigned int nbits, unsigned long bit // This function searches for a repetition // (a previous occurence of the current byte sequence) -// Returns length of the repetition, and stores the backward distance +// Returns length of the repetition, and stores the backward distance // to pWork structure. static unsigned int FindRep(TCmpStruct * pWork, unsigned char * input_data) { @@ -277,7 +277,7 @@ static unsigned int FindRep(TCmpStruct * pWork, unsigned char * input_data) phash_offs = pWork->phash_offs + phash_offs_index; prev_repetition = pWork->work_buff + phash_offs[0]; repetition_limit = input_data - 1; - + // If the current PAIR_HASH was not encountered before, // we haven't found a repetition. if(prev_repetition >= repetition_limit) @@ -303,7 +303,7 @@ static unsigned int FindRep(TCmpStruct * pWork, unsigned char * input_data) { prev_repetition++; input_data_ptr++; - + // Are the bytes different ? if(*prev_repetition != *input_data_ptr) break; @@ -399,7 +399,7 @@ static unsigned int FindRep(TCmpStruct * pWork, unsigned char * input_data) pWork->offs09BC[++offs_in_rep] = ++di_val; } - // + // // Now go through all the repetitions from the first found one // to the current input data, and check if any of them migh be // a start of a greater sequence match. @@ -408,7 +408,7 @@ static unsigned int FindRep(TCmpStruct * pWork, unsigned char * input_data) prev_repetition = pWork->work_buff + phash_offs[0]; prev_rep_end = prev_repetition + rep_length; rep_length2 = rep_length; - + for(;;) { rep_length2 = pWork->offs09BC[rep_length2]; @@ -502,7 +502,7 @@ static void WriteCmpData(TCmpStruct * pWork) unsigned int save_rep_length; // Saved length of current repetition unsigned int save_distance = 0; // Saved distance of current repetition unsigned int rep_length; // Length of the found repetition - unsigned int phase = 0; // + unsigned int phase = 0; // // Store the compression type and dictionary size pWork->out_buff[0] = (char)pWork->ctype; @@ -542,12 +542,12 @@ static void WriteCmpData(TCmpStruct * pWork) input_data_end = pWork->work_buff + pWork->dsize_bytes + total_loaded; if(input_data_ended) input_data_end += 0x204; - + // // Warning: The end of the buffer passed to "SortBuffer" is actually 2 bytes beyond // valid data. It is questionable if this is actually a bug or not, // but it might cause the compressed data output to be dependent on random bytes - // that are in the buffer. + // that are in the buffer. // To prevent that, the calling application must always zero the compression // buffer before passing it to "implode" // @@ -556,7 +556,7 @@ static void WriteCmpData(TCmpStruct * pWork) // previously compressed data, if any. switch(phase) { - case 0: + case 0: SortBuffer(pWork, input_data, input_data_end + 1); phase++; if(pWork->dsize_bytes != 0x1000) diff --git a/dep/StormLib/src/pklib/pklib.h b/dep/StormLib/src/pklib/pklib.h index f43da153be6..eb18049abdf 100644 --- a/dep/StormLib/src/pklib/pklib.h +++ b/dep/StormLib/src/pklib/pklib.h @@ -34,7 +34,7 @@ #ifndef PKEXPORT #ifdef WIN32 -#define PKEXPORT __cdecl // Use for normal __cdecl calling +#define PKEXPORT __cdecl // Use for normal __cdecl calling #else #define PKEXPORT #endif @@ -47,7 +47,7 @@ typedef struct { unsigned int distance; // 0000: Backward distance of the currently found repetition, decreased by 1 - unsigned int out_bytes; // 0004: # bytes available in out_buff + unsigned int out_bytes; // 0004: # bytes available in out_buff unsigned int out_bits; // 0008: # of bits available in the last out byte unsigned int dsize_bits; // 000C: Number of bits needed for dictionary size. 4 = 0x400, 5 = 0x800, 6 = 0x1000 unsigned int dsize_mask; // 0010: Bit mask for dictionary. 0x0F = 0x400, 0x1F = 0x800, 0x3F = 0x1000 @@ -57,14 +57,14 @@ typedef struct unsigned char dist_codes[0x40]; // 005C: Distance codes unsigned char nChBits[0x306]; // 009C: Table of literal bit lengths to be put to the output stream unsigned short nChCodes[0x306]; // 03A2: Table of literal codes to be put to the output stream - unsigned short offs09AE; // 09AE: + unsigned short offs09AE; // 09AE: void * param; // 09B0: User parameter unsigned int (*read_buf)(char *buf, unsigned int *size, void *param); // 9B4 void (*write_buf)(char *buf, unsigned int *size, void *param); // 9B8 unsigned short offs09BC[0x204]; // 09BC: - unsigned long offs0DC4; // 0DC4: + unsigned long offs0DC4; // 0DC4: unsigned short phash_to_index[0x900]; // 0DC8: Array of indexes (one for each PAIR_HASH) to the "pair_hash_offsets" table unsigned short phash_to_index_end; // 1FC8: End marker for "phash_to_index" table char out_buff[0x802]; // 1FCA: Compressed data @@ -101,15 +101,15 @@ typedef struct unsigned char in_buff[0x800]; // 2234: Buffer for data to be decompressed unsigned char DistPosCodes[0x100]; // 2A34: Table of distance position codes unsigned char LengthCodes[0x100]; // 2B34: Table of length codes - unsigned char offs2C34[0x100]; // 2C34: Buffer for - unsigned char offs2D34[0x100]; // 2D34: Buffer for - unsigned char offs2E34[0x80]; // 2EB4: Buffer for - unsigned char offs2EB4[0x100]; // 2EB4: Buffer for - unsigned char ChBitsAsc[0x100]; // 2FB4: Buffer for + unsigned char offs2C34[0x100]; // 2C34: Buffer for + unsigned char offs2D34[0x100]; // 2D34: Buffer for + unsigned char offs2E34[0x80]; // 2EB4: Buffer for + unsigned char offs2EB4[0x100]; // 2EB4: Buffer for + unsigned char ChBitsAsc[0x100]; // 2FB4: Buffer for unsigned char DistBits[0x40]; // 30B4: Numbers of bytes to skip copied block length unsigned char LenBits[0x10]; // 30F4: Numbers of bits for skip copied block length unsigned char ExLenBits[0x10]; // 3104: Number of valid bits for copied block - unsigned short LenBase[0x10]; // 3114: Buffer for + unsigned short LenBase[0x10]; // 3114: Buffer for } TDcmpStruct; #define EXP_BUFFER_SIZE sizeof(TDcmpStruct) // Size of decompression structure diff --git a/dep/StormLib/src/sparse/sparse.cpp b/dep/StormLib/src/sparse/sparse.cpp index 5b37f6d0f8e..96f2efd70e1 100644 --- a/dep/StormLib/src/sparse/sparse.cpp +++ b/dep/StormLib/src/sparse/sparse.cpp @@ -14,10 +14,10 @@ /* 19.11.03 1.01 Dan Big endian handling */ /* 08.12.03 2.01 Dan High-memory handling (> 0x80000000) */ /*****************************************************************************/ - + #include #include - + #include "sparse.h" //----------------------------------------------------------------------------- @@ -86,7 +86,7 @@ void CompressSparse(unsigned char * pbOutBuffer, int * pcbOutBuffer, unsigned ch // Put marker that means "0x80 of nonzeros" *pbOutBuffer++ = 0xFF; memcpy(pbOutBuffer, pbInBuffer, 0x80); - + // Adjust counter of nonzeros and both pointers NumberOfNonZeros -= 0x80; pbOutBuffer += 0x80; @@ -106,7 +106,7 @@ void CompressSparse(unsigned char * pbOutBuffer, int * pcbOutBuffer, unsigned ch // Put marker that means "1 nonzero byte" *pbOutBuffer++ = 0x80; memcpy(pbOutBuffer, pbInBuffer, 1); - + // Adjust counter of nonzeros and both pointers NumberOfNonZeros--; pbOutBuffer++; @@ -183,7 +183,7 @@ void CompressSparse(unsigned char * pbOutBuffer, int * pcbOutBuffer, unsigned ch // Put marker that means "Several zeros" *pbOutBuffer++ = (unsigned char)(NumberOfZeros - 3); - + // Adjust pointer pbInBuffer += NumberOfZeros; } @@ -208,7 +208,7 @@ void CompressSparse(unsigned char * pbOutBuffer, int * pcbOutBuffer, unsigned ch // Terminate with a marker that means "0x80 of nonzeros" *pbOutBuffer++ = 0xFF; memcpy(pbOutBuffer, pbInBuffer, NumberOfNonZeros); - + // Adjust pointer pbOutBuffer += NumberOfNonZeros; break; @@ -255,6 +255,10 @@ int DecompressSparse(unsigned char * pbOutBuffer, int * pcbOutBuffer, unsigned c OneByte = *pbInBuffer++; cbOutBuffer |= (OneByte << 0x00); + // Verify the size of the stream against the output buffer size + if(cbOutBuffer > *pcbOutBuffer) + return 0; + // Put the output size to the buffer *pcbOutBuffer = cbOutBuffer; diff --git a/dep/StormLib/storm_dll/storm_dll.def b/dep/StormLib/storm_dll/storm_dll.def index 8de88f5db3d..bdc7ce76ed4 100644 --- a/dep/StormLib/storm_dll/storm_dll.def +++ b/dep/StormLib/storm_dll/storm_dll.def @@ -22,4 +22,3 @@ EXPORTS StormCompress @551 ; 0x227 StormDecompress @552 ; 0x228 - \ No newline at end of file diff --git a/dep/StormLib/stormlib_dll/DllMain.c b/dep/StormLib/stormlib_dll/DllMain.c index cbfa84a08a8..d5ef37fc106 100644 --- a/dep/StormLib/stormlib_dll/DllMain.c +++ b/dep/StormLib/stormlib_dll/DllMain.c @@ -20,5 +20,5 @@ DWORD WINAPI DllMain(HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved) UNREFERENCED_PARAMETER(dwReason); UNREFERENCED_PARAMETER(lpReserved); - return TRUE; + return TRUE; } diff --git a/dep/StormLib/stormlib_dll/StormLib.def b/dep/StormLib/stormlib_dll/StormLib.def index 3c833612408..61da13dc08a 100644 --- a/dep/StormLib/stormlib_dll/StormLib.def +++ b/dep/StormLib/stormlib_dll/StormLib.def @@ -8,13 +8,12 @@ LIBRARY StormLib.dll EXPORTS - SFileGetGlobalFlags - SFileSetGlobalFlags SFileSetLocale SFileGetLocale SFileOpenArchive SFileCreateArchive + SFileGetArchiveBitmap SFileFlushArchive SFileCloseArchive @@ -22,22 +21,23 @@ EXPORTS SFileSetCompactCallback SFileCompactArchive - - SFileSetMaxFileCount - + + SFileGetMaxFileCount + SFileSetMaxFileCount + SFileGetAttributes SFileSetAttributes SFileUpdateFileAttributes SFileOpenPatchArchive SFileIsPatchedArchive - + SFileOpenFileEx SFileGetFileSize SFileSetFilePointer SFileReadFile SFileCloseFile - + SFileHasFile SFileGetFileName SFileGetFileInfo @@ -45,7 +45,7 @@ EXPORTS SFileExtractFile SFileVerifyFile - + SFileVerifyRawData SFileVerifyArchive SFileFindFirstFile @@ -57,7 +57,7 @@ EXPORTS SListFileFindClose SFileEnumLocales - + SFileCreateFile SFileWriteFile SFileFinishFile @@ -72,5 +72,5 @@ EXPORTS SCompImplode SCompExplode - SCompCompress - SCompDecompress + SCompCompress + SCompDecompress diff --git a/dep/StormLib/stormlib_dll/StormLib.exp b/dep/StormLib/stormlib_dll/StormLib.exp index 5097c28a96f..eea00023a48 100644 --- a/dep/StormLib/stormlib_dll/StormLib.exp +++ b/dep/StormLib/stormlib_dll/StormLib.exp @@ -1,54 +1,75 @@ # # Export file for Mac OS X -# Copyright (c) 2009 Sam Wilkins +# Copyright (c) 2009 Sam Wilkins # swilkins1337@gmail.com # -_GetLastError -_SCompExplode -_SCompImplode -_SFileAddFile -_SFileAddWave -_SFileHasFile -_SCompCompress +_SFileSetLocale +_SFileGetLocale + +_SFileOpenArchive +_SFileCreateArchive +_SFileGetArchiveBitmap +_SFileFlushArchive +_SFileCloseArchive + +_SFileAddListFile + +_SFileSetCompactCallback +_SFileCompactArchive + +_SFileGetMaxFileCount +_SFileSetMaxFileCount + +_SFileGetAttributes +_SFileSetAttributes +_SFileUpdateFileAttributes + +_SFileOpenPatchArchive +_SFileIsPatchedArchive + +_SFileOpenFileEx +_SFileGetFileSize +_SFileSetFilePointer _SFileReadFile -_SFileAddFile _SFileCloseFile + +_SFileHasFile +_SFileGetFileName +_SFileGetFileInfo + +_SFileExtractFile + +_SFileVerifyFile +_SFileVerifyRawData +_SFileVerifyArchive + +_SFileFindFirstFile +_SFileFindNextFile _SFileFindClose -_SFileGetLocale -_SFileSetLocale -_SFileWriteFile -_SCompDecompress + +_SListFileFindFirstFile +_SListFileFindNextFile +_SListFileFindClose + +_SFileEnumLocales + _SFileCreateFile +_SFileWriteFile _SFileFinishFile -_SFileOpenFileEx +_SFileAddFileEx +_SFileAddFile +_SFileAddWave _SFileRemoveFile _SFileRenameFile -_SFileVerifyFile -_SFileAddListFile -_SFileEnumLocales -_SFileExtractFile -_SFileGetFileInfo -_SFileGetFileName -_SFileGetFileSize -_SFileOpenArchive -_SFileCloseArchive -_SFileFindNextFile -_SFileFlushArchive -_SFileCreateArchive -_SFileFindFirstFile -_SFileGetAttributes -_SFileSetAttributes _SFileSetFileLocale -_SFileVerifyArchive -_SListFileFindClose -_SFileCompactArchive -_SFileSetFilePointer -_SFileIsPatchedArchive -_SFileOpenPatchArchive -_SListFileFindNextFile -_SListFileFindFirstFile -_SFileSetAddFileCallback -_SFileSetCompactCallback _SFileSetDataCompression -_SFileUpdateFileAttributes +_SFileSetAddFileCallback + +_SCompImplode +_SCompExplode +_SCompCompress +_SCompDecompress + +_SetLastError +_GetLastError diff --git a/dep/StormLib/test/Test.cpp b/dep/StormLib/test/Test.cpp index f1c719a76cc..353ab73373d 100644 --- a/dep/StormLib/test/Test.cpp +++ b/dep/StormLib/test/Test.cpp @@ -28,7 +28,7 @@ // Defines #ifdef PLATFORM_WINDOWS -#define WORK_PATH_ROOT "E:\\Multimedia\\MPQs\\" +#define WORK_PATH_ROOT "C:\\Multimedia\\MPQs\\" #endif #ifdef PLATFORM_LINUX @@ -36,7 +36,7 @@ #endif #ifdef PLATFORM_MAC -#define WORK_PATH_ROOT "/Users/sam/Downloads/" +#define WORK_PATH_ROOT "/Users/sam/StormLib/test" #endif #ifndef LANG_CZECH @@ -45,14 +45,22 @@ #define MPQ_SECTOR_SIZE 0x1000 -#define MAKE_PATH(path) (WORK_PATH_ROOT path) +#define MAKE_PATH(path) _T(WORK_PATH_ROOT) _T(path) + +// Unicode MPQ names +/* Czech */ static const wchar_t szUnicodeName1[] = {0x010C, 0x0065, 0x0073, 0x006B, 0x00FD, _T('.'), _T('m'), _T('p'), _T('q'), 0}; +/* Russian */ static const wchar_t szUnicodeName2[] = {0x0420, 0x0443, 0x0441, 0x0441, 0x043A, 0x0438, 0x0439, _T('.'), _T('m'), _T('p'), _T('q'), 0}; +/* Greece */ static const wchar_t szUnicodeName3[] = {0x03B5, 0x03BB, 0x03BB, 0x03B7, 0x03BD, 0x03B9, 0x03BA, 0x03AC, _T('.'), _T('m'), _T('p'), _T('q'), 0}; +/* Chinese */ static const wchar_t szUnicodeName4[] = {0x65E5, 0x672C, 0x8A9E, _T('.'), _T('m'), _T('p'), _T('q'), 0}; +/* Japanese */ static const wchar_t szUnicodeName5[] = {0x7B80, 0x4F53, 0x4E2D, 0x6587, _T('.'), _T('m'), _T('p'), _T('q'), 0}; +/* Arabic */ static const wchar_t szUnicodeName6[] = {0x0627, 0x0644, 0x0639, 0x0639, 0x0631, 0x0628, 0x064A, 0x0629, _T('.'), _T('m'), _T('p'), _T('q'), 0}; //----------------------------------------------------------------------------- // Constants -static const char * szWorkDir = MAKE_PATH("Work"); +static const TCHAR * szWorkDir = MAKE_PATH("Work"); -static unsigned int AddFlags[] = +static unsigned int AddFlags[] = { // Compression Encryption Fixed key Single Unit Sector CRC 0 | 0 | 0 | 0 | 0, @@ -107,20 +115,41 @@ static void clreol() szConsoleLine[i++] = '\r'; szConsoleLine[i] = 0; - printf(szConsoleLine); + _tprintf(szConsoleLine); delete [] szConsoleLine; } } #endif // PLATFORM_WINDOWS } -static const char * GetPlainName(const char * szFileName) +static void PrintfTA(const TCHAR * szFormat, const TCHAR * szStrT, const char * szStrA, int lcLocale = 0) +{ + TCHAR * szTemp; + TCHAR szBuffer[MAX_PATH]; + + // Convert ANSI string to TCHAR + for(szTemp = szBuffer; *szStrA != 0; szTemp++, szStrA++) + szTemp[0] = szStrA[0]; + szTemp[0] = 0; + + _tprintf(szFormat, szStrT, szBuffer, lcLocale); +} + +static void MergeLocalPath(TCHAR * szBuffer, const TCHAR * szPart1, const char * szPart2) { - const char * szTemp; + // Copy directory name + while(*szPart1 != 0) + *szBuffer++ = *szPart1++; + + // Add separator + *szBuffer++ = _T('/'); + + // Copy file name + while(*szPart2 != 0) + *szBuffer++ = *szPart2++; - if((szTemp = strrchr(szFileName, '\\')) != NULL) - szFileName = szTemp + 1; - return szFileName; + // Terminate the string + *szBuffer = 0; } int GetFirstDiffer(void * ptr1, void * ptr2, int nSize) @@ -141,27 +170,27 @@ static void WINAPI CompactCB(void * /* lpParam */, DWORD dwWork, ULONGLONG Bytes { clreol(); - printf("%u of %u ", (DWORD)BytesDone, (DWORD)TotalBytes); + _tprintf(_T("%u of %u "), (DWORD)BytesDone, (DWORD)TotalBytes); switch(dwWork) { case CCB_CHECKING_FILES: - printf("Checking files in archive ...\r"); + _tprintf(_T("Checking files in archive ...\r")); break; case CCB_CHECKING_HASH_TABLE: - printf("Checking hash table ...\r"); + _tprintf(_T("Checking hash table ...\r")); break; case CCB_COPYING_NON_MPQ_DATA: - printf("Copying non-MPQ data ...\r"); + _tprintf(_T("Copying non-MPQ data ...\r")); break; case CCB_COMPACTING_FILES: - printf("Compacting archive ...\r"); + _tprintf(_T("Compacting archive ...\r")); break; case CCB_CLOSING_ARCHIVE: - printf("Closing archive ...\r"); + _tprintf(_T("Closing archive ...\r")); break; } } @@ -255,7 +284,7 @@ static bool CompareArchivedFiles(const char * szFileName, HANDLE hFile1, HANDLE bResult2 = SFileReadFile(hFile2, pbBuffer2, dwBlockSize, &dwRead2, NULL); if(bResult1 != bResult2) { - printf("Different results from SFileReadFile, Mpq1 %u, Mpq2 %u\n", bResult1, bResult2); + _tprintf(_T("Different results from SFileReadFile, Mpq1 %u, Mpq2 %u\n"), bResult1, bResult2); bResult = false; break; } @@ -263,7 +292,7 @@ static bool CompareArchivedFiles(const char * szFileName, HANDLE hFile1, HANDLE // Test the number of bytes read if(dwRead1 != dwRead2) { - printf("Different bytes read from SFileReadFile, Mpq1 %u, Mpq2 %u\n", dwRead1, dwRead2); + _tprintf(_T("Different bytes read from SFileReadFile, Mpq1 %u, Mpq2 %u\n"), dwRead1, dwRead2); bResult = false; break; } @@ -271,7 +300,7 @@ static bool CompareArchivedFiles(const char * szFileName, HANDLE hFile1, HANDLE // No more bytes ==> OK if(dwRead1 == 0) break; - + // Test the content if((nDiff = GetFirstDiffer(pbBuffer1, pbBuffer2, dwRead1)) != -1) { @@ -304,7 +333,7 @@ static bool CompareArchivedFilesRR(const char * /* szFileName */, HANDLE hFile1, dwFileSize2 = SFileGetFileSize(hFile2, NULL); if(dwFileSize1 != dwFileSize2) { - printf("Different size from SFileGetFileSize (file1: %u, file2: %u)\n", dwFileSize1, dwFileSize2); + _tprintf(_T("Different size from SFileGetFileSize (file1: %u, file2: %u)\n"), dwFileSize1, dwFileSize2); return false; } @@ -315,7 +344,7 @@ static bool CompareArchivedFilesRR(const char * /* szFileName */, HANDLE hFile1, DWORD dwRandom = rand() * rand(); DWORD dwMoveMethod = dwRandom % 3; DWORD dwPosition = dwRandom % dwFileSize1; - DWORD dwToRead = dwRandom % dwBlockSize; + DWORD dwToRead = dwRandom % dwBlockSize; // Also test negative seek if(rand() & 1) @@ -329,12 +358,12 @@ static bool CompareArchivedFilesRR(const char * /* szFileName */, HANDLE hFile1, pbBuffer2 = new BYTE[dwToRead]; // Set the file pointer - printf("RndRead (%u): pos %8i from %s, size %u ...\r", i, dwPosition, szPositions[dwMoveMethod], dwToRead); + _tprintf(_T("RndRead (%u): pos %8i from %s, size %u ...\r"), i, dwPosition, szPositions[dwMoveMethod], dwToRead); dwRead1 = SFileSetFilePointer(hFile1, dwPosition, NULL, dwMoveMethod); dwRead2 = SFileSetFilePointer(hFile2, dwPosition, NULL, dwMoveMethod); if(dwRead1 != dwRead2) { - printf("Difference returned by SFileSetFilePointer (file1: %u, file2: %u)\n", dwRead1, dwRead2); + _tprintf(_T("Difference returned by SFileSetFilePointer (file1: %u, file2: %u)\n"), dwRead1, dwRead2); nError = ERROR_CAN_NOT_COMPLETE; break; } @@ -344,7 +373,7 @@ static bool CompareArchivedFilesRR(const char * /* szFileName */, HANDLE hFile1, bResult2 = SFileReadFile(hFile2, pbBuffer2, dwToRead, &dwRead2, NULL); if(bResult1 != bResult2) { - printf("Different results from SFileReadFile (file1: %u, file2: %u)\n\n", bResult1, bResult2); + _tprintf(_T("Different results from SFileReadFile (file1: %u, file2: %u)\n\n"), bResult1, bResult2); nError = ERROR_CAN_NOT_COMPLETE; break; } @@ -352,15 +381,15 @@ static bool CompareArchivedFilesRR(const char * /* szFileName */, HANDLE hFile1, // Test the number of bytes read if(dwRead1 != dwRead2) { - printf("Different bytes read from SFileReadFile (file1: %u, file2: %u)\n\n", dwRead1, dwRead2); + _tprintf(_T("Different bytes read from SFileReadFile (file1: %u, file2: %u)\n\n"), dwRead1, dwRead2); nError = ERROR_CAN_NOT_COMPLETE; break; } - + // Test the content if(dwRead1 != 0 && memcmp(pbBuffer1, pbBuffer2, dwRead1)) { - printf("Different data content from SFileReadFile\n"); + _tprintf(_T("Different data content from SFileReadFile\n")); nError = ERROR_CAN_NOT_COMPLETE; break; } @@ -373,10 +402,27 @@ static bool CompareArchivedFilesRR(const char * /* szFileName */, HANDLE hFile1, return (nError == ERROR_SUCCESS) ? true : false; } +//----------------------------------------------------------------------------- +// Opening local file + +static int TestOpenLocalFile(const char * szFileName) +{ + HANDLE hFile; + char szRetrievedName[MAX_PATH]; + + if(SFileOpenFileEx(NULL, szFileName, SFILE_OPEN_LOCAL_FILE, &hFile)) + { + SFileGetFileName(hFile, szRetrievedName); + SFileCloseFile(hFile); + } + + return ERROR_SUCCESS; +} + //----------------------------------------------------------------------------- // Partial file reading -static int TestPartFileRead(const char * szFileName) +static int TestPartFileRead(const TCHAR * szFileName) { ULONGLONG ByteOffset; ULONGLONG FileSize = 0; @@ -393,7 +439,7 @@ static int TestPartFileRead(const char * szFileName) // Get the size of the stream if(nError == ERROR_SUCCESS) { - if(!FileStream_GetSize(pStream, FileSize)) + if(!FileStream_GetSize(pStream, &FileSize)) nError = GetLastError(); } @@ -425,6 +471,29 @@ static int TestPartFileRead(const char * szFileName) return nError; } +//----------------------------------------------------------------------------- +// Compare PKLIB decompression + +BYTE pbCompressed1[] = {0x00, 0x04, 0x00, 0x00, 0x04, 0xF0, 0x1F, 0x7B, 0x01, 0xFF}; +BYTE pbCompressed2[] = {0x00, 0x04, 0x00, 0x00, 0x04, 0xF0, 0x1F, 0x00, 0x00, 0x04, 0xFC, 0x03}; + +static int ComparePklibCompressions() +{ + char Decompressed[0x1000]; + char Compressed[0x1000]; + int cbDecompressed = 0x208; + int cbCompressed = sizeof(Compressed); + + memset(Decompressed, 0, cbDecompressed); + SCompImplode(Compressed, &cbCompressed, Decompressed, cbDecompressed); + + cbDecompressed = sizeof(Decompressed); + SCompExplode(Decompressed, &cbDecompressed, Compressed, cbCompressed); + + + return ERROR_SUCCESS; +} + //----------------------------------------------------------------------------- // Compare LZMA decompression @@ -516,7 +585,7 @@ static int CompareLzmaCompressions(int nSectorSize) int nDiff; clreol(); - printf("Testing compression of sector %u\r", i + 1); + _tprintf(_T("Testing compression of sector %u\r"), i + 1); // Generate random data sector GenerateRandomDataBlock(pbOriginalData, nSectorSize); @@ -539,28 +608,28 @@ static int CompareLzmaCompressions(int nSectorSize) // Compare the length of the output data if(nDcmpLength1 != nDcmpLength2) { - printf("Difference in compressed blocks lengths (%u vs %u)\n", nDcmpLength1, nDcmpLength2); - goto __TryToDecompress; + _tprintf(_T("Difference in compressed blocks lengths (%u vs %u)\n"), nDcmpLength1, nDcmpLength2); + goto __TryToDecompress; } // Compare the output if((nDiff = GetFirstDiffer(pbDecompressed1, pbDecompressed2, nDcmpLength1)) != -1) { - printf("Difference in decompressed blocks (offset 0x%08X)\n", nDiff); + _tprintf(_T("Difference in decompressed blocks (offset 0x%08X)\n"), nDiff); goto __TryToDecompress; } // Check for data overflow if(pbDecompressed1[nSectorSize] != 0xFD || pbDecompressed1[nSectorSize] != 0xFD) { - printf("Damage after decompressed sector !!!\n"); + _tprintf(_T("Damage after decompressed sector !!!\n")); goto __TryToDecompress; } // Compare the decompressed data against original data if((nDiff = GetFirstDiffer(pbDecompressed1, pbOriginalData, nDcmpLength1)) != -1) { - printf("Difference between original data and decompressed data (offset 0x%08X)\n", nDiff); + _tprintf(_T("Difference between original data and decompressed data (offset 0x%08X)\n"), nDiff); goto __TryToDecompress; } } @@ -611,7 +680,7 @@ static int TestSectorCompress(int nSectorSize) int nDiff; clreol(); - printf("Testing compression of sector %u\r", i + 1); + _tprintf(_T("Testing compression of sector %u\r"), i + 1); // Generate random data sector GenerateRandomDataBlock(pbOriginal, nOriginalLength); @@ -631,7 +700,7 @@ static int TestSectorCompress(int nSectorSize) { if((nDiff = GetFirstDiffer(pbCompressed, pbOriginal, nOriginalLength)) != -1) { - printf("Compression error: Fail when unable to compress the data (Offset 0x%08X).\n", nDiff); + _tprintf(_T("Compression error: Fail when unable to compress the data (Offset 0x%08X).\n"), nDiff); goto __TryAgain; } } @@ -644,14 +713,14 @@ static int TestSectorCompress(int nSectorSize) // Check the decompressed length against original length if(nDecompressedLength != nOriginalLength) { - printf("Length of uncompressed data does not agree with original data length !!!\n"); + _tprintf(_T("Length of uncompressed data does not agree with original data length !!!\n")); goto __TryAgain; } - + // Check decompressed block against original block if((nDiff = GetFirstDiffer(pbDecompressed, pbOriginal, nOriginalLength)) != -1) { - printf("Decompressed sector does not agree with the original data !!! (Offset 0x%08X)\n", nDiff); + _tprintf(_T("Decompressed sector does not agree with the original data !!! (Offset 0x%08X)\n"), nDiff); goto __TryAgain; } } @@ -665,9 +734,9 @@ static int TestSectorCompress(int nSectorSize) return nError; } -static int TestArchiveOpenAndClose(const char * szMpqName) +static int TestArchiveOpenAndClose(const TCHAR * szMpqName) { - const char * szFileName1 = "ITEM\\TEXTURECOMPONENTS\\LegLowerTexture\\MAIL_DUNGEONSHAMAN_B_01BLUE_PANT_LL_U.BLP"; + const char * szFileName1 = "StringList\\Powers.stl"; // const char * szFileName2 = "items\\map\\mapz_deleted.cel"; TMPQArchive * ha = NULL; HANDLE hFile1 = NULL; @@ -677,11 +746,17 @@ static int TestArchiveOpenAndClose(const char * szMpqName) if(nError == ERROR_SUCCESS) { - printf("Opening archive %s ...\n", szMpqName); - if(!SFileOpenArchive(szMpqName, 0, 0, /* MPQ_OPEN_ENCRYPTED,*/ &hMpq)) + _tprintf(_T("Opening archive %s ...\n"), szMpqName); + if(!SFileOpenArchive(szMpqName, 0, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE, &hMpq)) nError = GetLastError(); ha = (TMPQArchive *)hMpq; } + + if(nError == ERROR_SUCCESS) + { + SFileAddListFile(hMpq, "c:\\Tools32\\Listfiles\\ListFile.txt"); + } + /* // Test for TBitArray if(nError == ERROR_SUCCESS && ha->pHetTable != NULL) @@ -699,10 +774,10 @@ static int TestArchiveOpenAndClose(const char * szMpqName) memcpy(SaveBits, pBitArray->Elements, sizeof(SaveBits)); // Load the index to the BET table - pBitArray->GetBits(nBitPosition, nBitCount, LoadedBits, sizeof(LoadedBits)); + GetBits(pBitArray, nBitPosition, nBitCount, LoadedBits, sizeof(LoadedBits)); // Load the index to the BET table - pBitArray->SetBits(nBitPosition, nBitCount, LoadedBits, sizeof(LoadedBits)); + SetBits(pBitArray, nBitPosition, nBitCount, LoadedBits, sizeof(LoadedBits)); // Verify the bits if(memcmp(SaveBits, pBitArray->Elements, sizeof(SaveBits))) @@ -726,17 +801,18 @@ static int TestArchiveOpenAndClose(const char * szMpqName) // Dummy read from the file if(nError == ERROR_SUCCESS) - { + { DWORD dwBytesRead = 0; - BYTE Buffer[0x1000]; + BYTE Buffer[0x1000]; - SFileReadFile(hFile1, Buffer, sizeof(Buffer), &dwBytesRead); - } + SFileSetFileLocale(hFile1, 0x405); + SFileReadFile(hFile1, Buffer, sizeof(Buffer), &dwBytesRead, NULL); + } /* // Verify the MPQ listfile if(nError == ERROR_SUCCESS) { - SFileVerifyFile(hMpq, szFileName1, 0xFFFFFFFF); + SFileVerifyFile(hMpq, szFileName1, 0xFFFFFFFF); if(!CompareArchivedFilesRR(szFileName1, hFile1, hFile2, 0x100000)) nError = ERROR_CAN_NOT_COMPLETE; } @@ -748,7 +824,7 @@ static int TestArchiveOpenAndClose(const char * szMpqName) return nError; } -static int TestFindFiles(const char * szMpqName) +static int TestFindFiles(const TCHAR * szMpqName) { TMPQFile * hf; HANDLE hFile; @@ -761,7 +837,7 @@ static int TestFindFiles(const char * szMpqName) // Open the archive if(nError == ERROR_SUCCESS) { - printf("Opening \"%s\" for finding files ...\n", szMpqName); + _tprintf(_T("Opening \"%s\" for finding files ...\n"), szMpqName); if(!SFileOpenArchive(szMpqName, 0, 0, &hMpq)) nError = GetLastError(); } @@ -780,7 +856,7 @@ static int TestFindFiles(const char * szMpqName) if(SFileOpenFileEx(hMpq, sf.cFileName, 0, &hFile)) { hf = (TMPQFile *)hFile; - SFileReadFile(hFile, Buffer, sizeof(Buffer)); + SFileReadFile(hFile, Buffer, sizeof(Buffer), NULL, NULL); nFiles++; if(sf.dwFileFlags & MPQ_FILE_SECTOR_CRC) @@ -800,11 +876,11 @@ static int TestFindFiles(const char * szMpqName) if(hMpq != NULL) SFileCloseArchive(hMpq); if(nError == ERROR_SUCCESS) - printf("Search complete\n"); + _tprintf(_T("Search complete\n")); return nError; } -static int TestMpqCompacting(const char * szMpqName) +static int TestMpqCompacting(const TCHAR * szMpqName) { HANDLE hMpq = NULL; int nError = ERROR_SUCCESS; @@ -812,51 +888,52 @@ static int TestMpqCompacting(const char * szMpqName) // Open the archive if(nError == ERROR_SUCCESS) { - printf("Opening \"%s\" for compacting ...\n", szMpqName); + _tprintf(_T("Opening \"%s\" for compacting ...\n"), szMpqName); if(!SFileOpenArchive(szMpqName, 0, 0, &hMpq)) nError = GetLastError(); } -/* if(nError == ERROR_SUCCESS) { - char * szFileName = "Campaign\\Human\\Human01.pud"; + const char * szFileName = "Shaders\\Effects\\shadowmap.wfx"; printf("Deleting file %s ...\r", szFileName); - if(!SFileRemoveFile(hMpq, szFileName)) + if(!SFileRemoveFile(hMpq, szFileName, SFILE_OPEN_FROM_MPQ)) nError = GetLastError(); } -*/ +/* // Compact the archive if(nError == ERROR_SUCCESS) { - printf("Compacting archive ...\r"); + _tprintf(_T("Compacting archive ...\r")); SFileSetCompactCallback(hMpq, CompactCB, NULL); if(!SFileCompactArchive(hMpq, "c:\\Tools32\\ListFiles\\ListFile.txt")) nError = GetLastError(); } - +*/ if(hMpq != NULL) SFileCloseArchive(hMpq); if(nError == ERROR_SUCCESS) - printf("Compacting complete (No errors)\n"); + _tprintf(_T("Compacting complete (No errors)\n")); return nError; } -static int TestCreateArchive(const char * szMpqName) +static int TestCreateArchive(const TCHAR * szMpqName) { TFileStream * pStream; - const char * szFileName1 = MAKE_PATH("FileTest.exe"); - const char * szFileName2 = MAKE_PATH("ZeroSize.txt"); - HANDLE hMpq = NULL; // Handle of created archive + const TCHAR * szFileName1 = MAKE_PATH("FileTest.exe"); + const TCHAR * szFileName2 = MAKE_PATH("ZeroSize.txt"); + HANDLE hMpq = NULL; // Handle of created archive DWORD dwVerifyResult; + DWORD dwFileCount = 0; LCID LocaleIDs[] = {0x000, 0x405, 0x406, 0x407, 0xFFFF}; char szMpqFileName[MAX_PATH]; int nError = ERROR_SUCCESS; + int i; // Create the new file - printf("Creating %s ...\n", szMpqName); - pStream = FileStream_CreateFile(szMpqName); + _tprintf(_T("Creating %s ...\n"), szMpqName); + pStream = FileStream_CreateFile(szMpqName, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); if(pStream == NULL) nError = GetLastError(); @@ -864,7 +941,7 @@ static int TestCreateArchive(const char * szMpqName) if(nError == ERROR_SUCCESS) { ULONGLONG FileSize = 0x100000; - + FileStream_SetSize(pStream, FileSize); FileStream_Close(pStream); } @@ -874,7 +951,7 @@ static int TestCreateArchive(const char * szMpqName) { if(!SFileCreateArchive(szMpqName, MPQ_CREATE_ARCHIVE_V4 | MPQ_CREATE_ATTRIBUTES, - 7, + 17, &hMpq)) { nError = GetLastError(); @@ -884,26 +961,17 @@ static int TestCreateArchive(const char * szMpqName) // Add the same file multiple times if(nError == ERROR_SUCCESS) { -// SFileCompactArchive(hMpq); -// if(!SFileAddFileEx(hMpq, -// "e:\\Multimedia\\MPQs\\Achievement.dbc", -// "enGB\\DBFilesClient\\Achievement.dbc", -// MPQ_FILE_COMPRESS, -// MPQ_COMPRESSION_ZLIB)) -// { -// printf("Failed to add the file \"%s\".\n", szMpqFileName); -// } - // Add FileTest.exe - for(int i = 0; AddFlags[i] != 0xFFFFFFFF; i++) + for(i = 0; AddFlags[i] != 0xFFFFFFFF; i++) { sprintf(szMpqFileName, "FileTest_%02u.exe", i); - printf("Adding %s as %s ...\n", szFileName1, szMpqFileName); - if(SFileAddFileEx(hMpq, szFileName1, szMpqFileName, AddFlags[i], MPQ_COMPRESSION_ZLIB)) + PrintfTA(_T("Adding %s as %s ...\n"), szFileName1, szMpqFileName); + if(SFileAddFileEx(hMpq, szFileName1, szMpqFileName, AddFlags[i], MPQ_COMPRESSION_ZLIB, MPQ_COMPRESSION_NEXT_SAME)) { dwVerifyResult = SFileVerifyFile(hMpq, szMpqFileName, MPQ_ATTRIBUTE_CRC32 | MPQ_ATTRIBUTE_MD5); if(dwVerifyResult & (VERIFY_OPEN_ERROR | VERIFY_READ_ERROR | VERIFY_FILE_SECTOR_CRC_ERROR | VERIFY_FILE_CHECKSUM_ERROR | VERIFY_FILE_MD5_ERROR)) printf("CRC error on \"%s\"\n", szMpqFileName); + dwFileCount++; } else { @@ -911,13 +979,22 @@ static int TestCreateArchive(const char * szMpqName) } } + + // Delete a file in the middle of the file table + SFileRemoveFile(hMpq, "FileTest_10.exe", SFILE_OPEN_FROM_MPQ); + SFileAddFileEx(hMpq, szFileName1, "FileTest_xx.exe", MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED, MPQ_COMPRESSION_ZLIB, MPQ_COMPRESSION_NEXT_SAME); + + // Try to decrement max file count + dwFileCount = SFileGetMaxFileCount(hMpq); + SFileSetMaxFileCount(hMpq, dwFileCount - 1); + // Add ZeroSize.txt (1) sprintf(szMpqFileName, "ZeroSize_1.txt"); - for(int i = 0; LocaleIDs[i] != 0xFFFF; i++) + for(i = 0; LocaleIDs[i] != 0xFFFF; i++) { - printf("Adding %s as %s (locale %04x) ...\n", szFileName2, szMpqFileName, LocaleIDs[i]); + PrintfTA(_T("Adding %s as %s (locale %04x) ...\n"), szFileName2, szMpqFileName, LocaleIDs[i]); SFileSetLocale(LocaleIDs[i]); - if(!SFileAddFileEx(hMpq, szFileName2, szMpqFileName, MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED, MPQ_COMPRESSION_ZLIB)) + if(!SFileAddFileEx(hMpq, szFileName2, szMpqFileName, MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED, MPQ_COMPRESSION_ZLIB, MPQ_COMPRESSION_NEXT_SAME)) printf("Cannot add the file\n"); } @@ -925,9 +1002,9 @@ static int TestCreateArchive(const char * szMpqName) sprintf(szMpqFileName, "ZeroSize_2.txt"); for(int i = 0; LocaleIDs[i] != 0xFFFF; i++) { - printf("Adding %s as %s (locale %04x) ...\n", szFileName2, szMpqFileName, LocaleIDs[i]); + PrintfTA(_T("Adding %s as %s (locale %04x) ...\n"), szFileName2, szMpqFileName, LocaleIDs[i]); SFileSetLocale(LocaleIDs[i]); - if(!SFileAddFileEx(hMpq, szFileName2, szMpqFileName, MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED, MPQ_COMPRESSION_ZLIB)) + if(!SFileAddFileEx(hMpq, szFileName2, szMpqFileName, MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED, MPQ_COMPRESSION_ZLIB, MPQ_COMPRESSION_NEXT_SAME)) printf("Cannot add the file\n"); } } @@ -935,36 +1012,36 @@ static int TestCreateArchive(const char * szMpqName) // Test rename function if(nError == ERROR_SUCCESS) { - printf("Testing rename files ...\n"); + _tprintf(_T("Testing rename files ...\n")); SFileSetLocale(LANG_NEUTRAL); if(!SFileRenameFile(hMpq, "FileTest_08.exe", "FileTest_08a.exe")) { nError = GetLastError(); - printf("Failed to rename the file\n"); + _tprintf(_T("Failed to rename the file\n")); } if(!SFileRenameFile(hMpq, "FileTest_08a.exe", "FileTest_08.exe")) { nError = GetLastError(); - printf("Failed to rename the file\n"); + _tprintf(_T("Failed to rename the file\n")); } if(!SFileRenameFile(hMpq, "FileTest_10.exe", "FileTest_10a.exe")) { nError = GetLastError(); - printf("Failed to rename the file\n"); + _tprintf(_T("Failed to rename the file\n")); } if(!SFileRenameFile(hMpq, "FileTest_10a.exe", "FileTest_10.exe")) { nError = GetLastError(); - printf("Failed to rename the file\n"); + _tprintf(_T("Failed to rename the file\n")); } - + if(nError == ERROR_SUCCESS) - printf("Rename test succeeded.\n\n"); + _tprintf(_T("Rename test succeeded.\n\n")); else - printf("Rename test failed.\n\n"); + _tprintf(_T("Rename test failed.\n\n")); } // Compact the archive @@ -972,8 +1049,8 @@ static int TestCreateArchive(const char * szMpqName) // SFileCompactArchive(hMpq); // Test changing hash table size -// if(nError == ERROR_SUCCESS) -// SFileSetMaxFileCount(hMpq, 0x95); + if(nError == ERROR_SUCCESS) + SFileSetMaxFileCount(hMpq, 0x95); if(hMpq != NULL) SFileCloseArchive(hMpq); @@ -982,30 +1059,92 @@ static int TestCreateArchive(const char * szMpqName) if(SFileOpenArchive(szMpqName, 0, 0, &hMpq)) SFileCloseArchive(hMpq); - printf("\n"); + _tprintf(_T("\n")); return nError; } +static int TestCreateArchive_PaliRoharBug(const TCHAR * szMpqName) +{ + const TCHAR * szFileName = MAKE_PATH("FileTest.exe"); + HANDLE hMpq = NULL; // Handle of created archive + DWORD dwMaxFileCount = 0; + DWORD dwMpqFlags = MPQ_FILE_ENCRYPTED | MPQ_FILE_COMPRESS; + char szMpqFileName[MAX_PATH]; + int nError = ERROR_SUCCESS; + int i; + + _tremove(szMpqName); + if(SFileCreateArchive(szMpqName, + MPQ_CREATE_ARCHIVE_V4 | MPQ_CREATE_ATTRIBUTES, + 1, + &hMpq)) + { + // Add the file there + SFileAddFileEx(hMpq, szFileName, "FileTest_base.exe", dwMpqFlags, MPQ_COMPRESSION_ZLIB, MPQ_COMPRESSION_NEXT_SAME); + SFileFlushArchive(hMpq); + SFileCloseArchive(hMpq); + + // Add the same file 10 times + for(i = 0; i < 10; i++) + { + if(SFileOpenArchive(szMpqName, 0, 0, &hMpq)) + { + dwMaxFileCount = SFileGetMaxFileCount(hMpq) + 1; + _tprintf(_T("Increasing max file count to %u ...\n"), dwMaxFileCount); + SFileSetMaxFileCount(hMpq, dwMaxFileCount); + + sprintf(szMpqFileName, "FileTest_%02u.exe", dwMaxFileCount); + PrintfTA(_T("Adding %s as %s\n"), szFileName, szMpqFileName); + if(!SFileAddFileEx(hMpq, szFileName, szMpqFileName, dwMpqFlags, MPQ_COMPRESSION_ZLIB, MPQ_COMPRESSION_NEXT_SAME)) + { + printf("Failed to add the file \"%s\".\n", szMpqFileName); + break; + } + + SFileFlushArchive(hMpq); + SFileCompactArchive(hMpq, NULL, false); + SFileCloseArchive(hMpq); + } + } + } + + _tprintf(_T("\n")); + return nError; +} + + static int TestAddFilesToMpq( - const char * szMpqName, + const TCHAR * szMpqName, ... ) { - const char * szFileName; + const TCHAR * szFileName; + const TCHAR * szSrc; + char * szTrg; HANDLE hMpq; va_list argList; + char szMpqFileName[MAX_PATH]; int nError = ERROR_SUCCESS; if(!SFileOpenArchive(szMpqName, 0, 0, &hMpq)) return GetLastError(); va_start(argList, szMpqName); - while((szFileName = va_arg(argList, const char *)) != NULL) + while((szFileName = va_arg(argList, const TCHAR *)) != NULL) { + // Convert the plain name to ANSI + szSrc = GetPlainFileNameT(szFileName); + szTrg = szMpqFileName; + while(*szSrc != 0) + *szTrg++ = (char)*szSrc++; + *szTrg = 0; + + // Add the file to MPQ if(!SFileAddFileEx(hMpq, szFileName, - GetPlainFileName(szFileName), + szMpqFileName, MPQ_FILE_COMPRESS, - MPQ_COMPRESSION_ZLIB)) + MPQ_COMPRESSION_ZLIB, + MPQ_COMPRESSION_NEXT_SAME)) { nError = GetLastError(); printf("Failed to add the file \"%s\"\n", szFileName); @@ -1016,7 +1155,7 @@ static int TestAddFilesToMpq( return nError; } -static int TestCreateArchiveFromMemory(const char * szMpqName) +static int TestCreateArchiveFromMemory(const TCHAR * szMpqName) { #define FILE_SIZE 65535 @@ -1025,7 +1164,7 @@ static int TestCreateArchiveFromMemory(const char * szMpqName) char* data = new char [FILE_SIZE]; // random memory data char szFileName[100]; int i; - + // Create an mpq file for testing if(SFileCreateArchive(szMpqName, MPQ_CREATE_ARCHIVE_V2|MPQ_CREATE_ATTRIBUTES, 0x100000, &hMPQ)) { @@ -1034,7 +1173,7 @@ static int TestCreateArchiveFromMemory(const char * szMpqName) sprintf(szFileName, "File%03u.bin", i); printf("Adding file %s\r", szFileName); - if(SFileCreateFile(hMPQ, szFileName, NULL, FILE_SIZE, NULL, MPQ_FILE_COMPRESS, &hFile)) + if(SFileCreateFile(hMPQ, szFileName, 0, FILE_SIZE, 0, MPQ_FILE_COMPRESS, &hFile)) { SFileWriteFile(hFile, data, FILE_SIZE, MPQ_COMPRESSION_ZLIB); SFileFinishFile(hFile); @@ -1047,7 +1186,7 @@ static int TestCreateArchiveFromMemory(const char * szMpqName) } static int TestFileReadAndWrite( - const char * szMpqName, + const TCHAR * szMpqName, const char * szFileName) { LPBYTE pvFile = NULL; @@ -1060,7 +1199,7 @@ static int TestFileReadAndWrite( if(!SFileOpenArchive(szMpqName, 0, 0, &hMpq)) { nError = GetLastError(); - printf("Failed to open the archive %s (%u).\n", szMpqName, nError); + _tprintf(_T("Failed to open the archive %s (%u).\n"), szMpqName, nError); } if(nError == ERROR_SUCCESS) @@ -1074,10 +1213,10 @@ static int TestFileReadAndWrite( if(nError == ERROR_SUCCESS) { - if(!SFileGetFileInfo(hFile, SFILE_INFO_FILE_SIZE, &dwFileSize, sizeof(DWORD))) + if(!SFileGetFileInfo(hFile, SFILE_INFO_FILE_SIZE, &dwFileSize, sizeof(DWORD), NULL)) { nError = GetLastError(); - printf("Failed to get the file size (%u).\n", nError); + _tprintf(_T("Failed to get the file size (%u).\n"), nError); } } @@ -1093,7 +1232,7 @@ static int TestFileReadAndWrite( if(nError == ERROR_SUCCESS) { - if(!SFileReadFile(hFile, pvFile, dwFileSize, &dwBytesRead)) + if(!SFileReadFile(hFile, pvFile, dwFileSize, &dwBytesRead, NULL)) { nError = GetLastError(); printf("Failed to read file (%u).\n", nError); @@ -1108,7 +1247,7 @@ static int TestFileReadAndWrite( if(nError == ERROR_SUCCESS) { - if(!SFileCreateFile(hMpq, szFileName, NULL, dwFileSize, 0, MPQ_FILE_REPLACEEXISTING, &hFile)) + if(!SFileCreateFile(hMpq, szFileName, 0, dwFileSize, 0, MPQ_FILE_REPLACEEXISTING, &hFile)) { nError = GetLastError(); printf("Failed to create %s in the archive (%u).\n", szFileName, nError); @@ -1140,54 +1279,54 @@ static int TestFileReadAndWrite( return nError; } -static int TestSignatureVerify(const char * szMpqName) +static int TestSignatureVerify(const TCHAR * szMpqName) { HANDLE hMpq; - + if(SFileOpenArchive(szMpqName, 0, 0, &hMpq)) { - printf("Verifying digital signature in %s:\n", szMpqName); + _tprintf(_T("Verifying digital signature in %s:\n"), szMpqName); switch(SFileVerifyArchive(hMpq)) { case ERROR_NO_SIGNATURE: - printf("No digital signature present.\n"); + _tprintf(_T("No digital signature present.\n")); break; - + case ERROR_VERIFY_FAILED: - printf("Failed to verify signature.\n"); + _tprintf(_T("Failed to verify signature.\n")); break; - + case ERROR_WEAK_SIGNATURE_OK: - printf("Weak signature is OK.\n"); + _tprintf(_T("Weak signature is OK.\n")); break; case ERROR_WEAK_SIGNATURE_ERROR: - printf("Weak signature mismatch.\n"); + _tprintf(_T("Weak signature mismatch.\n")); break; case ERROR_STRONG_SIGNATURE_OK: - printf("Strong signature is OK.\n"); + _tprintf(_T("Strong signature is OK.\n")); break; case ERROR_STRONG_SIGNATURE_ERROR: - printf("Strong signature mismatch.\n"); + _tprintf(_T("Strong signature mismatch.\n")); break; } - + SFileCloseArchive(hMpq); - printf("\n"); + _tprintf(_T("\n")); } return 0; } -static int TestCreateArchiveCopy(const char * szMpqName, const char * szMpqCopyName, const char * szListFile) +static int TestCreateArchiveCopy(const TCHAR * szMpqName, const TCHAR * szMpqCopyName, const char * szListFile) { TFileStream * pStream; - char szLocalFile[MAX_PATH] = ""; + TCHAR szLocalFile[MAX_PATH]; HANDLE hMpq1 = NULL; // Handle of existing archive - HANDLE hMpq2 = NULL; // Handle of created archive + HANDLE hMpq2 = NULL; // Handle of created archive DWORD dwHashTableSize = 0; int nError = ERROR_SUCCESS; @@ -1196,7 +1335,7 @@ static int TestCreateArchiveCopy(const char * szMpqName, const char * szMpqCopyN szListFile = NULL; // Create the new file - pStream = FileStream_CreateFile(szMpqCopyName); + pStream = FileStream_CreateFile(szMpqCopyName, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); if(pStream == NULL) nError = GetLastError(); @@ -1204,7 +1343,7 @@ static int TestCreateArchiveCopy(const char * szMpqName, const char * szMpqCopyN if(nError == ERROR_SUCCESS) { ULONGLONG FileSize = 0x100000; - + FileStream_SetSize(pStream, FileSize); FileStream_Close(pStream); } @@ -1212,7 +1351,7 @@ static int TestCreateArchiveCopy(const char * szMpqName, const char * szMpqCopyN // Open the existing MPQ archive if(nError == ERROR_SUCCESS) { - printf("Opening %s ...\n", szMpqName); + _tprintf(_T("Opening %s ...\n"), szMpqName); if(!SFileOpenArchive(szMpqName, 0, 0, &hMpq1)) nError = GetLastError(); } @@ -1220,8 +1359,8 @@ static int TestCreateArchiveCopy(const char * szMpqName, const char * szMpqCopyN // Well, now create the MPQ archive if(nError == ERROR_SUCCESS) { - printf("Creating %s ...\n", szMpqCopyName); - SFileGetFileInfo(hMpq1, SFILE_INFO_HASH_TABLE_SIZE, &dwHashTableSize, 4); + _tprintf(_T("Creating %s ...\n"), szMpqCopyName); + SFileGetFileInfo(hMpq1, SFILE_INFO_HASH_TABLE_SIZE, &dwHashTableSize, 4, NULL); if(!SFileCreateArchive(szMpqCopyName, 0, dwHashTableSize, &hMpq2)) nError = GetLastError(); } @@ -1233,7 +1372,7 @@ static int TestCreateArchiveCopy(const char * szMpqName, const char * szMpqCopyN HANDLE hFind = SFileFindFirstFile(hMpq1, "*", &sf, szListFile); bool bResult = true; - printf("Copying files ...\n"); + _tprintf(_T("Copying files ...\n")); if(hFind != NULL) { @@ -1244,15 +1383,15 @@ static int TestCreateArchiveCopy(const char * szMpqName, const char * szMpqCopyN SFileSetLocale(sf.lcLocale); // Create the local file name - sprintf(szLocalFile, "%s/%s", szWorkDir, sf.szPlainName); - if(SFileExtractFile(hMpq1, sf.cFileName, szLocalFile)) + MergeLocalPath(szLocalFile, szWorkDir, sf.szPlainName); + if(SFileExtractFile(hMpq1, sf.cFileName, szLocalFile, SFILE_OPEN_FROM_MPQ)) { printf("Extracting %s ... OK\n", sf.cFileName); if(!SFileAddFile(hMpq2, szLocalFile, sf.cFileName, sf.dwFileFlags)) { nError = GetLastError(); printf("Adding %s ... Failed\n\n", sf.cFileName); - remove(szLocalFile); + _tremove(szLocalFile); break; } else @@ -1266,7 +1405,7 @@ static int TestCreateArchiveCopy(const char * szMpqName, const char * szMpqCopyN } // Delete the added file - remove(szLocalFile); + _tremove(szLocalFile); } // Find the next file @@ -1287,16 +1426,139 @@ static int TestCreateArchiveCopy(const char * szMpqName, const char * szMpqCopyN return nError; } +static int TestOpenPatchedArchive(const TCHAR * szMpqName, ...) +{ + TFileStream * pStream; + HANDLE hFile = NULL; + HANDLE hMpq = NULL; + va_list argList; + const char * szFileName = "World\\Minimaps\\Azeroth\\noLiquid_map20_44.blp"; + TCHAR szLocFileName[MAX_PATH]; + LPBYTE pbFullFile = NULL; + DWORD dwFileSize; + int nError = ERROR_SUCCESS; + + // Open the primary MPQ + _tprintf(_T("Opening %s ...\n"), szMpqName); + if(!SFileOpenArchive(szMpqName, 0, MPQ_OPEN_READ_ONLY, &hMpq)) + { + nError = GetLastError(); + _tprintf(_T("Failed to open the archive %s ...\n"), szMpqName); + } + + // Add all patches + if(nError == ERROR_SUCCESS) + { + va_start(argList, szMpqName); + while((szMpqName = va_arg(argList, const TCHAR *)) != NULL) + { + _tprintf(_T("Adding patch %s ...\n"), szMpqName); + if(!SFileOpenPatchArchive(hMpq, szMpqName, NULL, 0)) + { + nError = GetLastError(); + printf("Failed to add patch %s ...\n", szMpqName); + } + } + va_end(argList); + } + + // Now search all files + if(nError == ERROR_SUCCESS) + { + SFILE_FIND_DATA sf; + HANDLE hFind; + bool bResult = true; + + hFind = SFileFindFirstFile(hMpq, "World\\Minimaps\\Azeroth\\noLiquid_map20_44.*", &sf, NULL); + while(hFind && bResult) + { + printf("%s\n", sf.cFileName); + bResult = SFileFindNextFile(hFind, &sf); + } + } + + // Now try to open patched version of a file + if(nError == ERROR_SUCCESS) + { + SFileExtractFile(hMpq, szFileName, _T("E:\\noLiquid_map20_44.blp"), SFILE_OPEN_FROM_MPQ); + } + + // Now try to open patched version of "Achievement.dbc" + if(nError == ERROR_SUCCESS) + { + printf("Opening patched file \"%s\" ...\n", szFileName); + SFileVerifyFile(hMpq, szFileName, SFILE_VERIFY_RAW_MD5); + if(!SFileOpenFileEx(hMpq, szFileName, SFILE_OPEN_PATCHED_FILE, &hFile)) + { + nError = GetLastError(); + printf("Failed to open patched file \"%s\"\n", szFileName); + } + } + + // Verify of the patched version is correct + if(nError == ERROR_SUCCESS) + { + TCHAR * szPatchChain = NULL; + DWORD cbPatchChain = 0; + + // Get the patch chain + SFileGetFileInfo(hFile, SFILE_INFO_PATCH_CHAIN, szPatchChain, cbPatchChain, &cbPatchChain); + szPatchChain = (TCHAR *)(new BYTE[cbPatchChain]); + SFileGetFileInfo(hFile, SFILE_INFO_PATCH_CHAIN, szPatchChain, cbPatchChain, &cbPatchChain); + delete [] szPatchChain; + + // Get the size of the full patched file + dwFileSize = SFileGetFileSize(hFile, NULL); + if(dwFileSize != 0) + { + DWORD dwBytesRead = 0; + BYTE TempData[0x100]; + + SFileReadFile(hFile, TempData, sizeof(TempData), &dwBytesRead, NULL); + SFileSetFilePointer(hFile, 0, NULL, FILE_BEGIN); + + // Allocate space for the full file + pbFullFile = new BYTE[dwFileSize]; + if(pbFullFile != NULL) + { + if(!SFileReadFile(hFile, pbFullFile, dwFileSize, NULL, NULL)) + { + nError = GetLastError(); + printf("Failed to read full patched file data \"%s\"\n", szFileName); + } + + if(nError == ERROR_SUCCESS) + { + MergeLocalPath(szLocFileName, MAKE_PATH("Work//"), GetPlainFileNameA(szFileName)); + pStream = FileStream_CreateFile(szLocFileName, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); + if(pStream != NULL) + { + FileStream_Write(pStream, NULL, pbFullFile, dwFileSize); + FileStream_Close(pStream); + } + } + + delete [] pbFullFile; + } + } + } + + // Close handles + if(hFile != NULL) + SFileCloseFile(hFile); + if(hMpq != NULL) + SFileCloseArchive(hMpq); + return nError; +} + static int TestCompareTwoArchives( - const char * szMpqName1, - const char * szMpqName2, + const TCHAR * szMpqName1, + const TCHAR * szMpqName2, const char * szListFile, DWORD dwBlockSize) { TMPQArchive * ha1 = NULL; TMPQArchive * ha2 = NULL; - LPBYTE pbBuffer1 = NULL; - LPBYTE pbBuffer2 = NULL; HANDLE hMpq1 = NULL; // Handle of the first archive HANDLE hMpq2 = NULL; // Handle of the second archive HANDLE hFile1 = NULL; @@ -1307,18 +1569,12 @@ static int TestCompareTwoArchives( if(szListFile == NULL || *szListFile == 0) szListFile = NULL; - // Allocate both buffers - pbBuffer1 = new BYTE[dwBlockSize]; - pbBuffer2 = new BYTE[dwBlockSize]; - if(pbBuffer1 == NULL || pbBuffer2 == NULL) - nError = ERROR_NOT_ENOUGH_MEMORY; - - printf("=============== Comparing MPQ archives ===============\n"); + _tprintf(_T("=============== Comparing MPQ archives ===============\n")); // Open the first MPQ archive if(nError == ERROR_SUCCESS && szMpqName1 != NULL) { - printf("Opening %s ...\n", szMpqName1); + _tprintf(_T("Opening %s ...\n"), szMpqName1); if(!SFileOpenArchive(szMpqName1, 0, 0, &hMpq1)) nError = GetLastError(); ha1 = (TMPQArchive *)hMpq1; @@ -1327,7 +1583,7 @@ static int TestCompareTwoArchives( // Open the second MPQ archive if(nError == ERROR_SUCCESS && szMpqName2 != NULL) { - printf("Opening %s ...\n", szMpqName2); + _tprintf(_T("Opening %s ...\n"), szMpqName2); if(!SFileOpenArchive(szMpqName2, 0, 0, &hMpq2)) nError = GetLastError(); ha2 = (TMPQArchive *)hMpq2; @@ -1356,67 +1612,58 @@ static int TestCompareTwoArchives( if(nError == ERROR_SUCCESS) { SFILE_FIND_DATA sf; + TMPQFile * hf1; + TMPQFile * hf2; HANDLE hFind = SFileFindFirstFile(hMpq1, "*", &sf, szListFile); - DWORD dwSearchScope1 = SFILE_OPEN_FROM_MPQ; - DWORD dwSearchScope2 = SFILE_OPEN_FROM_MPQ; bool bResult = true; - if(hMpq1 == NULL) - dwSearchScope1 = SFILE_OPEN_LOCAL_FILE; - if(hMpq2 == NULL) - dwSearchScope2 = SFILE_OPEN_LOCAL_FILE; - while(hFind != NULL && bResult == true) { - printf("%s\n", sf.cFileName); +// printf("%s \r", sf.cFileName); SFileSetLocale(sf.lcLocale); // Open the first file - if(!SFileOpenFileEx(hMpq1, sf.cFileName, dwSearchScope1, &hFile1)) - { + if(!SFileOpenFileEx(hMpq1, sf.cFileName, 0, &hFile1)) printf("Failed to open the file %s in the first archive\n", sf.cFileName); - continue; - } - if(!SFileOpenFileEx(hMpq2, sf.cFileName, dwSearchScope2, &hFile2)) - { + if(!SFileOpenFileEx(hMpq2, sf.cFileName, 0, &hFile2)) printf("Failed to open the file %s in the second archive\n", sf.cFileName); - continue; - } - if(dwSearchScope1 == SFILE_OPEN_FROM_MPQ && dwSearchScope2 == SFILE_OPEN_FROM_MPQ) + if(hFile1 != NULL && hFile2 != NULL) { - TMPQFile * hf1 = (TMPQFile *)hFile1; - TMPQFile * hf2 = (TMPQFile *)hFile2; + // Get the TMPQFile pointers + hf1 = (TMPQFile *)hFile1; + hf2 = (TMPQFile *)hFile2; // Compare the file sizes if(hf1->pFileEntry->dwFileSize != hf2->pFileEntry->dwFileSize) - printf(" - %s different size (%u x %u)\n", sf.cFileName, hf1->pFileEntry->dwFileSize, hf2->pFileEntry->dwFileSize); + printf("Different file size: %s (%u x %u)\n", sf.cFileName, hf1->pFileEntry->dwFileSize, hf2->pFileEntry->dwFileSize); + + if(hf1->pFileEntry->dwCmpSize != hf2->pFileEntry->dwCmpSize) + printf("Different cmpr size: %s (%u x %u)\n", sf.cFileName, hf1->pFileEntry->dwCmpSize, hf2->pFileEntry->dwCmpSize); if(hf1->pFileEntry->dwFlags != hf2->pFileEntry->dwFlags) - printf(" - %s different flags (%08X x %08X)\n", sf.cFileName, hf1->pFileEntry->dwFlags, hf2->pFileEntry->dwFlags); - } + printf("Different mpq flags: %s (%08X x %08X)\n", sf.cFileName, hf1->pFileEntry->dwFlags, hf2->pFileEntry->dwFlags); - if(!CompareArchivedFiles(sf.cFileName, hFile1, hFile2, 0x1001)) - printf(" - %s different content\n", sf.cFileName); + if(!CompareArchivedFiles(sf.cFileName, hFile1, hFile2, dwBlockSize)) + printf("Different file data: %s\n", sf.cFileName); - if(!CompareArchivedFilesRR(sf.cFileName, hFile1, hFile2, 0x100000)) - printf(" - %s different content\n", sf.cFileName); +// if(!CompareArchivedFilesRR(sf.cFileName, hFile1, hFile2, dwBlockSize)) +// printf("Different file data: %s\n", sf.cFileName); + } // Close both files - SFileCloseFile(hFile2); - SFileCloseFile(hFile1); + if(hFile2 != NULL) + SFileCloseFile(hFile2); + if(hFile1 != NULL) + SFileCloseFile(hFile1); hFile2 = hFile1 = NULL; // Find the next file bResult = SFileFindNextFile(hFind, &sf); } - // Close all handles - if(hFile2 != NULL) - SFileCloseFile(hFile2); - if(hFile1 != NULL) - SFileCloseFile(hFile1); + // Close the find handle if(hFind != NULL) SFileFindClose(hFind); } @@ -1428,130 +1675,101 @@ static int TestCompareTwoArchives( SFileCloseArchive(hMpq2); if(hMpq1 != NULL) SFileCloseArchive(hMpq1); - if(pbBuffer2 != NULL) - delete [] pbBuffer2; - if(pbBuffer1 != NULL) - delete [] pbBuffer1; return nError; } -static int TestOpenPatchedArchive(const char * szMpqName, ...) +//----------------------------------------------------------------------------- +// Searching all known MPQs + +#ifdef _WIN32 +static int TestSearchOneArchive(const TCHAR * szMpqName) { - TFileStream * pStream; - HANDLE hFile = NULL; - HANDLE hMpq = NULL; - va_list argList; -// const char * szFileName = "DBFilesClient\\Achievement.dbc"; - const char * szFileName = "ruRU/DBFilesClient/Spell.dbc"; - const char * szExtension; - const char * szLocale; - char szLocFileName[MAX_PATH]; - LPBYTE pbFullFile = NULL; - DWORD dwFileSize; - int nError = ERROR_SUCCESS; + SFILE_FIND_DATA sf; + HANDLE hFind; + HANDLE hMpq; + const TCHAR * szExtension; + bool bFound = true; - // Open the primary MPQ - printf("Opening %s ...\n", szMpqName); - if(!SFileOpenArchive(szMpqName, 0, MPQ_OPEN_READ_ONLY, &hMpq)) - { - nError = GetLastError(); - printf("Failed to open the archive %s ...\n", szMpqName); - } + // Get the file extension + szExtension = _tcsrchr(szMpqName, _T('.')); + if(szExtension == NULL) + return ERROR_SUCCESS; - // Add all patches - if(nError == ERROR_SUCCESS) - { - va_start(argList, szMpqName); - while((szMpqName = va_arg(argList, const char *)) != NULL) - { - printf("Adding patch %s ...\n", szMpqName); - if(!SFileOpenPatchArchive(hMpq, szMpqName, NULL, 0)) - { - nError = GetLastError(); - printf("Failed to add patch %s ...\n", szMpqName); - } - } - va_end(argList); - } + // Only search defined extensions + if(_tcsicmp(szExtension, _T(".mpq")) && _tcsnicmp(szExtension, _T(".SC2"), 4)) + return ERROR_SUCCESS; - // Now search all files - if(nError == ERROR_SUCCESS) - { - SFILE_FIND_DATA sf; - HANDLE hFind; - BOOL bResult = TRUE; + _tprintf(_T("Searching %s ...\n"), szMpqName); + // Try to open the MPQ + if(SFileOpenArchive(szMpqName, 0, MPQ_OPEN_READ_ONLY, &hMpq)) + { hFind = SFileFindFirstFile(hMpq, "*", &sf, NULL); - while(hFind && bResult) + if(hFind != NULL) { - printf("%s\n", sf.cFileName); - bResult = SFileFindNextFile(hFind, &sf); + while(bFound) + { + if(sf.dwFileFlags & MPQ_FILE_DELETE_MARKER) + _tprintf(_T("Delete marker: %s:%hs\n"), szMpqName, sf.cFileName); + + bFound = SFileFindNextFile(hFind, &sf); + } } - } - // Now try to open patched version of "Achievement.dbc" - if(nError == ERROR_SUCCESS) - { - SFileExtractFile(hMpq, szFileName, "E:\\Spell.dbc"); + SFileCloseArchive(hMpq); } - // Now try to open patched version of "Achievement.dbc" - if(nError == ERROR_SUCCESS) - { - printf("Opening patched file \"%s\" ...\n", szFileName); - if(!SFileOpenFileEx(hMpq, szFileName, SFILE_OPEN_PATCHED_FILE, &hFile)) - { - nError = GetLastError(); - printf("Failed to open patched file \"%s\"\n", szFileName); - } - } + return ERROR_SUCCESS; +} - // Verify of the patched version is correct - if(nError == ERROR_SUCCESS) +static int TestSearchAllArchives(const TCHAR * szSearchMask) +{ + WIN32_FIND_DATA wf; + LPTSTR szFilePart; + HANDLE hFind; + TCHAR szFullPath[MAX_PATH]; + BOOL bFound = TRUE; + + // Initiate search + _tcscpy(szFullPath, szSearchMask); + szFilePart = _tcschr(szFullPath, _T('*')); + assert(szFilePart != NULL); + + // Begin search + hFind = FindFirstFile(szSearchMask, &wf); + if(hFind != INVALID_HANDLE_VALUE) { - // Get the size of the full patched file - dwFileSize = SFileGetFileSize(hFile, NULL); - if(dwFileSize != 0) + while(bFound) { - // Allocate space for the full file - pbFullFile = new BYTE[dwFileSize]; - if(pbFullFile != NULL) + // Eliminate "." and ".." + if(wf.cFileName[0] != _T('.')) { - if(!SFileReadFile(hFile, pbFullFile, dwFileSize)) - { - nError = GetLastError(); - printf("Failed to read full patched file data \"%s\"\n", szFileName); + // Construct the full path + _tcscpy(szFilePart, wf.cFileName); + + // If it a directory? + if(wf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + _tcscat(szFullPath, _T("\\*")); + TestSearchAllArchives(szFullPath); } - - if(nError == ERROR_SUCCESS) + else { - strcpy(szLocFileName, MAKE_PATH("Work//")); - strcat(szLocFileName, GetPlainName(szFileName)); - - pStream = FileStream_CreateFile(szLocFileName); - if(pStream != NULL) - { - FileStream_Write(pStream, NULL, pbFullFile, dwFileSize); - FileStream_Close(pStream); - } + TestSearchOneArchive(szFullPath); } - - delete [] pbFullFile; } + bFound = FindNextFile(hFind, &wf); } + FindClose(hFind); } - // Close handles - if(hFile != NULL) - SFileCloseFile(hFile); - if(hMpq != NULL) - SFileCloseArchive(hMpq); - return nError; + return ERROR_SUCCESS; } +#endif //----------------------------------------------------------------------------- // Main -// +// int main(void) { @@ -1561,6 +1779,8 @@ int main(void) _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); #endif // defined(_MSC_VER) && defined(_DEBUG) +// FileStream_OpenEncrypted(_T("e:\\Multimedia\\MPQs\\2010 - Starcraft II\\Installer UI 2 deDE.MPQE")); + // Mix the random number generator // srand(GetTickCount()); @@ -1568,10 +1788,16 @@ int main(void) // if(nError == ERROR_SUCCESS) // nError = TestStructureSizes(); +// if(nError == ERROR_SUCCESS) +// nError = TestOpenLocalFile("C:\\autoexec.bat"); + // Test reading partial file // if(nError == ERROR_SUCCESS) // nError = TestPartFileRead(MAKE_PATH("2009 - PartialMPQs/patch.MPQ.part")); +// if(nError == ERROR_SUCCESS) +// nError = ComparePklibCompressions(); + // Test LZMA compression method against the code ripped from Starcraft II // if(nError == ERROR_SUCCESS) // nError = CompareLzmaCompressions(MPQ_SECTOR_SIZE); @@ -1579,23 +1805,30 @@ int main(void) // Test compression methods // if(nError == ERROR_SUCCESS) // nError = TestSectorCompress(MPQ_SECTOR_SIZE); - + // Test the archive open and close -// if(nError == ERROR_SUCCESS) -// nError = TestArchiveOpenAndClose(MAKE_PATH("2011 - WoW-Cataclysm2/expansion1.MPQ")); -// nError = TestArchiveOpenAndClose(MAKE_PATH("2011 - WoW-Cataclysm/wow-update-13202.MPQ")); + if(nError == ERROR_SUCCESS) + nError = TestArchiveOpenAndClose(MAKE_PATH("BrooDat.mpq")); +// nError = TestArchiveOpenAndClose(MAKE_PATH("2011 - WoW BETA/wow-update-13202.MPQ")); // nError = TestArchiveOpenAndClose(MAKE_PATH("2002 - Warcraft III/ProtectedMap_HashTable_FakeValid.w3x")); // nError = TestArchiveOpenAndClose(MAKE_PATH("2010 - Starcraft II/Installer Tome 1 enGB.MPQE")); // nError = TestArchiveOpenAndClose(MAKE_PATH("1997 - Diablo I/DIABDAT_orig.MPQ")); // nError = TestArchiveOpenAndClose(MAKE_PATH("2004 - World of Warcraft/SoundCache-enUS.MPQ")); -// nError = TestArchiveOpenAndClose(MAKE_PATH("DIABDAT_orig.MPQ")); +// nError = TestArchiveOpenAndClose(MAKE_PATH("smpq.mpq ")); // if(nError == ERROR_SUCCESS) // nError = TestFindFiles(MAKE_PATH("2002 - Warcraft III/HumanEd.mpq")); // Create a big MPQ archive // if(nError == ERROR_SUCCESS) +// nError = TestCreateArchive_PaliRoharBug(MAKE_PATH("Test.mpq")); // nError = TestCreateArchive(MAKE_PATH("Test.mpq")); +// nError = TestCreateArchive((const TCHAR*)szUnicodeName1); +// nError = TestCreateArchive((const TCHAR*)szUnicodeName2); +// nError = TestCreateArchive((const TCHAR*)szUnicodeName3); +// nError = TestCreateArchive((const TCHAR*)szUnicodeName4); +// nError = TestCreateArchive((const TCHAR*)szUnicodeName5); +// nError = TestCreateArchive((const TCHAR*)szUnicodeName6); // if(nError == ERROR_SUCCESS) // nError = TestAddFilesToMpq(MAKE_PATH("wow-update-13202.MPQ"), @@ -1624,45 +1857,43 @@ int main(void) // nError = TestSignatureVerify(MAKE_PATH("2004 - World of Warcraft/WoW-2.3.3.7799-to-2.4.0.8089-enUS-patch.exe")); // nError = TestSignatureVerify(MAKE_PATH("2004 - World of Warcraft/standalone.MPQ")); - // Compact the archive + // Compact the archive // if(nError == ERROR_SUCCESS) -// nError = TestMpqCompacting(MAKE_PATH("DIABDAT_orig.MPQ")); - +// nError = TestMpqCompacting(MAKE_PATH("wow-update-base-14333.MPQ")); + // Create copy of the archive, appending some bytes before the MPQ header // if(nError == ERROR_SUCCESS) // nError = TestCreateArchiveCopy(MAKE_PATH("PartialMPQs/interface.MPQ.part"), MAKE_PATH("PartialMPQs/interface-copy.MPQ.part"), NULL); -// if(nError == ERROR_SUCCESS) -// { -// nError = TestCompareTwoArchives(MAKE_PATH("2011 - WoW-Cataclysm/wow-update-13189.MPQ"), -// MAKE_PATH("wow-update-13189.MPQ"), -// NULL, -// 0x1001); -// } - +/* if(nError == ERROR_SUCCESS) { - nError = TestOpenPatchedArchive(MAKE_PATH("2011 - WoW-Cataclysm2/art.MPQ"), - MAKE_PATH("2011 - WoW-Cataclysm2/wow-update-13164.MPQ"), - MAKE_PATH("2011 - WoW-Cataclysm2/wow-update-13205.MPQ"), - MAKE_PATH("2011 - WoW-Cataclysm2/wow-update-13287.MPQ"), - MAKE_PATH("2011 - WoW-Cataclysm2/wow-update-13329.MPQ"), - MAKE_PATH("2011 - WoW-Cataclysm2/wow-update-base-13417.MPQ"), - MAKE_PATH("2011 - WoW-Cataclysm2/wow-update-base-13449.MPQ"), - MAKE_PATH("2011 - WoW-Cataclysm2/wow-update-base-13482.MPQ"), - MAKE_PATH("2011 - WoW-Cataclysm2/wow-update-base-13529.MPQ"), - MAKE_PATH("2011 - WoW-Cataclysm2/wow-update-base-13561.MPQ"), - MAKE_PATH("2011 - WoW-Cataclysm2/wow-update-base-13596.MPQ"), - MAKE_PATH("2011 - WoW-Cataclysm2/wow-update-base-13682.MPQ"), + nError = TestOpenPatchedArchive(MAKE_PATH("2011 - WoW 4.x/locale-enGB.MPQ"), + MAKE_PATH("2011 - WoW 4.x/wow-update-13164.MPQ"), + MAKE_PATH("2011 - WoW 4.x/wow-update-13205.MPQ"), + MAKE_PATH("2011 - WoW 4.x/wow-update-13287.MPQ"), + MAKE_PATH("2011 - WoW 4.x/wow-update-13329.MPQ"), + MAKE_PATH("2011 - WoW 4.x/wow-update-13596.MPQ"), + MAKE_PATH("2011 - WoW 4.x/wow-update-13623.MPQ"), + MAKE_PATH("2011 - WoW 4.x/wow-update-enGB-13914.MPQ"), + MAKE_PATH("2011 - WoW 4.x/wow-update-enGB-14007.MPQ"), + MAKE_PATH("2011 - WoW 4.x/wow-update-enGB-14333.MPQ"), + MAKE_PATH("2011 - WoW 4.x/wow-update-enGB-14480.MPQ"), NULL); } +*/ + if(nError == ERROR_SUCCESS) + { + nError = TestCompareTwoArchives(MAKE_PATH("Sound-copy.mpq"), + MAKE_PATH("Sound.mpq"), + NULL, + 0x1001); + } - // Remove the working directory - clreol(); - if(nError != ERROR_SUCCESS) - printf("One or more errors occurred when testing StormLib\n"); - printf("Work complete.\n"); +// if(nError == ERROR_SUCCESS) +// nError = TestSearchAllArchives(MAKE_PATH("*")); + return nError; }