diff --git a/dep/CMakeLists.txt b/dep/CMakeLists.txt index d6843aa6e7..b4ccedeca8 100644 --- a/dep/CMakeLists.txt +++ b/dep/CMakeLists.txt @@ -11,12 +11,14 @@ add_subdirectory(threads) if(SERVERS OR TOOLS) + add_subdirectory(argon2) add_subdirectory(boost) add_subdirectory(zlib) add_subdirectory(g3dlite) add_subdirectory(recastnavigation) add_subdirectory(fmt) add_subdirectory(PCG) + add_subdirectory(short_alloc) add_subdirectory(utf8cpp) add_subdirectory(valgrind) add_subdirectory(openssl) @@ -31,8 +33,6 @@ if(SERVERS) add_subdirectory(gsoap) add_subdirectory(rapidjson) add_subdirectory(efsw) - add_subdirectory(short_alloc) - add_subdirectory(argon2) endif() if(TOOLS) diff --git a/dep/PackageList.txt b/dep/PackageList.txt index d6ac996b64..7483c22968 100644 --- a/dep/PackageList.txt +++ b/dep/PackageList.txt @@ -62,8 +62,8 @@ recastnavigation (Recast is state of the art navigation mesh construction toolse Custom changes: https://github.com/TrinityCore/recastnavigation/tree/3.3.5 StormLib (a pack of modules, written in C++, which are able to read and also to write files from/to the MPQ archives) - http://www.zezula.net/en/mpq/stormlib.html - Version: 8.04 + https://github.com/ladislav-zezula/StormLib + Version: 1df38c1f9cba98d2b6169ad04a84cd4d956fea38 utf8-cpp (UTF-8 with C++ in a Portable Way) https://github.com/nemtrif/utfcpp diff --git a/dep/StormLib/CMakeLists.txt b/dep/StormLib/CMakeLists.txt index a5478ebcd3..daeefabf2d 100644 --- a/dep/StormLib/CMakeLists.txt +++ b/dep/StormLib/CMakeLists.txt @@ -11,6 +11,7 @@ set(SRC_FILES src/FileStream.cpp src/SBaseCommon.cpp src/SBaseFileTable.cpp + src/SBaseSubTypes.cpp src/SCompression.cpp src/SFileAddFile.cpp src/SFileAttributes.cpp @@ -18,12 +19,14 @@ set(SRC_FILES src/SFileCreateArchive.cpp src/SFileExtractFile.cpp src/SFileFindFile.cpp + src/SFileGetFileInfo.cpp src/SFileListFile.cpp src/SFileOpenArchive.cpp src/SFileOpenFileEx.cpp src/SFilePatchArchives.cpp src/SFileReadFile.cpp src/SFileVerify.cpp + src/SMemUtf8.cpp ) set(TOMCRYPT_FILES @@ -57,9 +60,22 @@ set(TOMCRYPT_FILES src/libtomcrypt/src/pk/asn1/der_decode_sequence_ex.c src/libtomcrypt/src/pk/asn1/der_decode_sequence_flexi.c src/libtomcrypt/src/pk/asn1/der_decode_sequence_multi.c + src/libtomcrypt/src/pk/asn1/der_encode_set.c + src/libtomcrypt/src/pk/asn1/der_encode_utctime.c + src/libtomcrypt/src/pk/asn1/der_encode_utf8_string.c + src/libtomcrypt/src/pk/asn1/der_encode_printable_string.c + src/libtomcrypt/src/pk/asn1/der_encode_ia5_string.c + src/libtomcrypt/src/pk/asn1/der_encode_object_identifier.c + src/libtomcrypt/src/pk/asn1/der_encode_octet_string.c + src/libtomcrypt/src/pk/asn1/der_encode_bit_string.c + src/libtomcrypt/src/pk/asn1/der_encode_short_integer.c + src/libtomcrypt/src/pk/asn1/der_encode_integer.c + src/libtomcrypt/src/pk/asn1/der_encode_boolean.c + src/libtomcrypt/src/pk/asn1/der_encode_setof.c src/libtomcrypt/src/pk/asn1/der_decode_short_integer.c src/libtomcrypt/src/pk/asn1/der_decode_utctime.c src/libtomcrypt/src/pk/asn1/der_decode_utf8_string.c + src/libtomcrypt/src/pk/asn1/der_encode_sequence_ex.c src/libtomcrypt/src/pk/asn1/der_length_bit_string.c src/libtomcrypt/src/pk/asn1/der_length_boolean.c src/libtomcrypt/src/pk/asn1/der_length_ia5_string.c @@ -78,14 +94,17 @@ set(TOMCRYPT_FILES src/libtomcrypt/src/pk/ecc/ltc_ecc_points.c src/libtomcrypt/src/pk/ecc/ltc_ecc_projective_add_point.c src/libtomcrypt/src/pk/ecc/ltc_ecc_projective_dbl_point.c + src/libtomcrypt/src/pk/pkcs1/pkcs_1_v1_5_encode.c src/libtomcrypt/src/pk/pkcs1/pkcs_1_mgf1.c src/libtomcrypt/src/pk/pkcs1/pkcs_1_oaep_decode.c src/libtomcrypt/src/pk/pkcs1/pkcs_1_pss_decode.c + src/libtomcrypt/src/pk/pkcs1/pkcs_1_pss_encode.c src/libtomcrypt/src/pk/pkcs1/pkcs_1_v1_5_decode.c src/libtomcrypt/src/pk/rsa/rsa_exptmod.c src/libtomcrypt/src/pk/rsa/rsa_free.c src/libtomcrypt/src/pk/rsa/rsa_import.c src/libtomcrypt/src/pk/rsa/rsa_make_key.c + src/libtomcrypt/src/pk/rsa/rsa_sign_hash.c src/libtomcrypt/src/pk/rsa/rsa_verify_hash.c src/libtomcrypt/src/pk/rsa/rsa_verify_simple.c ) diff --git a/dep/StormLib/StormLib-v1.0.hotfix1.diff b/dep/StormLib/StormLib-v1.0.hotfix1.diff deleted file mode 100644 index 37cd6a2fdf..0000000000 --- a/dep/StormLib/StormLib-v1.0.hotfix1.diff +++ /dev/null @@ -1,22 +0,0 @@ - dep/StormLib/src/SFileFindFile.cpp | 6 +++++- - 1 file changed, 5 insertions(+), 1 deletion(-) - -diff --git a/dep/StormLib/src/SFileFindFile.cpp b/dep/StormLib/src/SFileFindFile.cpp -index 337be7d..ca745b9 100644 ---- a/dep/StormLib/src/SFileFindFile.cpp -+++ b/dep/StormLib/src/SFileFindFile.cpp -@@ -225,8 +225,12 @@ static TFileEntry * FindPatchEntry(TMPQArchive * ha, TFileEntry * pFileEntry) - ha = ha->haPatch; - - // Prepare the prefix for the file name -+ char* fileName = pFileEntry->szFileName; -+ if (fileName == NULL) -+ continue; -+ - strcpy(szFileName, ha->szPatchPrefix); -- strcat(szFileName, pFileEntry->szFileName); -+ strcat(szFileName, fileName); - - // Try to find the file there - pTempEntry = GetFileEntryExact(ha, szFileName, lcLocale); - diff --git a/dep/StormLib/doc/History.txt b/dep/StormLib/doc/History.txt index 5e32894d7b..77e438a767 100644 --- a/dep/StormLib/doc/History.txt +++ b/dep/StormLib/doc/History.txt @@ -2,6 +2,26 @@ StormLib history ================ + Version 9.30 + + - Added functions for conversions between file names and safe operating system file names + + Version 9.11 + + - Fixed bug in processing HET table. + + Version 9.10 + + - Support for weak-signing + - Anti-protector: New Spazzler + + Version 9.00 + + - Support for streaming (master-mirror) + - Support for multi-file MPQs used by some WoW versions + - Opening maps protected by Spazzler protector + - Opening maps protected by BOBA protector + Version 8.02 - Support for UNICODE encoding for on-disk files @@ -18,21 +38,21 @@ - Updated support for protected maps from Warcraft III Version 7.11 - + - Support for MPQs v 3.0 (WOW-Cataclysm BETA) - StormLib now deals properly with files that have MPQ_SECTOR_CHECKSUM missing, but have sector checksum entry present in the sector offset table Version 7.10 - + - Support for partial MPQs ("interface.MPQ.part") - The only operation that is externally allowed to do with internal files ("(listfile)", "(attributes)" and "(signature)") is reading. Attempt to modify any of the file fails and GetLastError returns ERROR_INTERNAL_FILE - - Fixed memory leak that has occured when writing more than one sector to the file at once + - Fixed memory leak that has occurred when writing more than one sector to the file at once Version 7.01 - + - Support for adding files from memory - Fixed improper validation of handles to MPQ file and MPQ archive - Fixed bug where StormLib didn't save CRC32 of the file when added to archive @@ -57,6 +77,6 @@ Version 6.20 - Fixed lots of bugs when processing files with same names but different locales - - Fixed bugs when repeately extracts the same file with MPQ_FILE_SINGLE_UNIT flag + - Fixed bugs when repeatedly extracts the same file with MPQ_FILE_SINGLE_UNIT flag - Added SFileFlushArchive - Fixed issue opening AVI files renamed to MPQ using SFileCreateArchiveEx diff --git a/dep/StormLib/doc/Sector Offset MD5.txt b/dep/StormLib/doc/Sector Offset MD5.txt deleted file mode 100644 index a22506ac70..0000000000 --- a/dep/StormLib/doc/Sector Offset MD5.txt +++ /dev/null @@ -1,25 +0,0 @@ - - After sector offset table - ========================= - -FileSize CmpSize DWORDs -00007588 000075A4 0x01 -0000A9EA 000095EC 0x01 -0000E51D 0000E20D 0x02 -00015C00 00015C40 0x02 -0001C578 000186C4 0x02 -0002A9D4 0002A9EA 0x04 -00037BAC 00037A42 0x06 -0003C3C1 00034084 0x06 -0003D224 0003B30F 0x06 -00045105 0004195A 0x07 -00045D9C 0003D87D 0x07 -0004AAB8 0004860A 0x08 -0004D18E 00048E0C 0x07 -00056B4C 00056BDD 0x09 -0005DC08 00059426 0x09 -00061EC0 00057711 0x0A -0006CEC4 00062561 0x0B -000778EE 00066736 0x0C -000AD0CB 0007F32E 0x11 -00327EAC 00303395 0x53 diff --git a/dep/StormLib/doc/d3-authenticationcode-deDE.txt b/dep/StormLib/doc/d3-authenticationcode/d3-authenticationcode-deDE.txt similarity index 100% rename from dep/StormLib/doc/d3-authenticationcode-deDE.txt rename to dep/StormLib/doc/d3-authenticationcode/d3-authenticationcode-deDE.txt diff --git a/dep/StormLib/doc/d3-authenticationcode-enGB.txt b/dep/StormLib/doc/d3-authenticationcode/d3-authenticationcode-enGB.txt similarity index 100% rename from dep/StormLib/doc/d3-authenticationcode-enGB.txt rename to dep/StormLib/doc/d3-authenticationcode/d3-authenticationcode-enGB.txt diff --git a/dep/StormLib/doc/d3-authenticationcode-enSG.txt b/dep/StormLib/doc/d3-authenticationcode/d3-authenticationcode-enSG.txt similarity index 100% rename from dep/StormLib/doc/d3-authenticationcode-enSG.txt rename to dep/StormLib/doc/d3-authenticationcode/d3-authenticationcode-enSG.txt diff --git a/dep/StormLib/doc/d3-authenticationcode-enUS.txt b/dep/StormLib/doc/d3-authenticationcode/d3-authenticationcode-enUS.txt similarity index 100% rename from dep/StormLib/doc/d3-authenticationcode-enUS.txt rename to dep/StormLib/doc/d3-authenticationcode/d3-authenticationcode-enUS.txt diff --git a/dep/StormLib/doc/d3-authenticationcode-esES.txt b/dep/StormLib/doc/d3-authenticationcode/d3-authenticationcode-esES.txt similarity index 100% rename from dep/StormLib/doc/d3-authenticationcode-esES.txt rename to dep/StormLib/doc/d3-authenticationcode/d3-authenticationcode-esES.txt diff --git a/dep/StormLib/doc/d3-authenticationcode-esMX.txt b/dep/StormLib/doc/d3-authenticationcode/d3-authenticationcode-esMX.txt similarity index 100% rename from dep/StormLib/doc/d3-authenticationcode-esMX.txt rename to dep/StormLib/doc/d3-authenticationcode/d3-authenticationcode-esMX.txt diff --git a/dep/StormLib/doc/d3-authenticationcode-frFR.txt b/dep/StormLib/doc/d3-authenticationcode/d3-authenticationcode-frFR.txt similarity index 100% rename from dep/StormLib/doc/d3-authenticationcode-frFR.txt rename to dep/StormLib/doc/d3-authenticationcode/d3-authenticationcode-frFR.txt diff --git a/dep/StormLib/doc/d3-authenticationcode-itIT.txt b/dep/StormLib/doc/d3-authenticationcode/d3-authenticationcode-itIT.txt similarity index 100% rename from dep/StormLib/doc/d3-authenticationcode-itIT.txt rename to dep/StormLib/doc/d3-authenticationcode/d3-authenticationcode-itIT.txt diff --git a/dep/StormLib/doc/d3-authenticationcode-koKR.txt b/dep/StormLib/doc/d3-authenticationcode/d3-authenticationcode-koKR.txt similarity index 100% rename from dep/StormLib/doc/d3-authenticationcode-koKR.txt rename to dep/StormLib/doc/d3-authenticationcode/d3-authenticationcode-koKR.txt diff --git a/dep/StormLib/doc/d3-authenticationcode-plPL.txt b/dep/StormLib/doc/d3-authenticationcode/d3-authenticationcode-plPL.txt similarity index 100% rename from dep/StormLib/doc/d3-authenticationcode-plPL.txt rename to dep/StormLib/doc/d3-authenticationcode/d3-authenticationcode-plPL.txt diff --git a/dep/StormLib/doc/d3-authenticationcode-ptBR.txt b/dep/StormLib/doc/d3-authenticationcode/d3-authenticationcode-ptBR.txt similarity index 100% rename from dep/StormLib/doc/d3-authenticationcode-ptBR.txt rename to dep/StormLib/doc/d3-authenticationcode/d3-authenticationcode-ptBR.txt diff --git a/dep/StormLib/doc/d3-authenticationcode-zhTW.txt b/dep/StormLib/doc/d3-authenticationcode/d3-authenticationcode-zhTW.txt similarity index 100% rename from dep/StormLib/doc/d3-authenticationcode-zhTW.txt rename to dep/StormLib/doc/d3-authenticationcode/d3-authenticationcode-zhTW.txt diff --git a/dep/StormLib/doc/diablo3_ruru_disk_encrypted_win.blob b/dep/StormLib/doc/diablo3_ruru_disk_encrypted_win.blob deleted file mode 100644 index 7972b2d763..0000000000 --- a/dep/StormLib/doc/diablo3_ruru_disk_encrypted_win.blob +++ /dev/null @@ -1,49 +0,0 @@ -{ -"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 deleted file mode 100644 index d89f489952..0000000000 --- a/dep/StormLib/doc/diablo3_urls.txt +++ /dev/null @@ -1,14 +0,0 @@ -http://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/doc/hots-authenticationcode/hots-authenticationcode-bgdl.txt b/dep/StormLib/doc/hots-authenticationcode/hots-authenticationcode-bgdl.txt new file mode 100644 index 0000000000..6dfe0ee601 --- /dev/null +++ b/dep/StormLib/doc/hots-authenticationcode/hots-authenticationcode-bgdl.txt @@ -0,0 +1 @@ +S48B6CDTN5XEQAKQDJNDLJBJ73FDFM3U \ No newline at end of file diff --git a/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-deDE.txt b/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-deDE.txt new file mode 100644 index 0000000000..029d733eaa --- /dev/null +++ b/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-deDE.txt @@ -0,0 +1 @@ +Y45MD3CAK4KXSSXHYD9VY64Z8EKJ4XFX \ No newline at end of file diff --git a/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-enGB.txt b/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-enGB.txt new file mode 100644 index 0000000000..7f66f8bba1 --- /dev/null +++ b/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-enGB.txt @@ -0,0 +1 @@ +G8MN8UDG6NA2ANGY6A3DNY82HRGF29ZH \ No newline at end of file diff --git a/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-enUS.txt b/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-enUS.txt new file mode 100644 index 0000000000..0a4f5b8f60 --- /dev/null +++ b/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-enUS.txt @@ -0,0 +1 @@ +3DH5RE5NVM5GTFD85LXGWT6FK859ETR5 \ No newline at end of file diff --git a/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-esES.txt b/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-esES.txt new file mode 100644 index 0000000000..fba8c8dc86 --- /dev/null +++ b/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-esES.txt @@ -0,0 +1 @@ +8WLKUAXE94PFQU4Y249PAZ24N4R4XKTQ \ No newline at end of file diff --git a/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-esMX.txt b/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-esMX.txt new file mode 100644 index 0000000000..bb020c65e1 --- /dev/null +++ b/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-esMX.txt @@ -0,0 +1 @@ +A34DXX3VHGGXSQBRFE5UFFDXMF9G4G54 \ No newline at end of file diff --git a/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-frFR.txt b/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-frFR.txt new file mode 100644 index 0000000000..37bcc4a41c --- /dev/null +++ b/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-frFR.txt @@ -0,0 +1 @@ +ZG7J9K938HJEFWPQUA768MA2PFER6EAJ \ No newline at end of file diff --git a/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-itIT.txt b/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-itIT.txt new file mode 100644 index 0000000000..a665f69133 --- /dev/null +++ b/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-itIT.txt @@ -0,0 +1 @@ +NE7CUNNNTVAPXV7E3G2BSVBWGVMW8BL2 \ No newline at end of file diff --git a/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-koKR.txt b/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-koKR.txt new file mode 100644 index 0000000000..e6346df309 --- /dev/null +++ b/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-koKR.txt @@ -0,0 +1 @@ +3V9E2FTMBM9QQWK7U6MAMWAZWQDB838F \ No newline at end of file diff --git a/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-plPL.txt b/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-plPL.txt new file mode 100644 index 0000000000..564efc4896 --- /dev/null +++ b/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-plPL.txt @@ -0,0 +1 @@ +2NSFB8MELULJ83U6YHA3UP6K4MQD48L6 \ No newline at end of file diff --git a/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-ptBR.txt b/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-ptBR.txt new file mode 100644 index 0000000000..e8f8172d1c --- /dev/null +++ b/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-ptBR.txt @@ -0,0 +1 @@ +QA2TZ9EWZ4CUU8BMB5WXCTY65F9CSW4E \ No newline at end of file diff --git a/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-ruRU.txt b/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-ruRU.txt new file mode 100644 index 0000000000..1b93b5be24 --- /dev/null +++ b/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-ruRU.txt @@ -0,0 +1 @@ +VHB378W64BAT9SH7D68VV9NLQDK9YEGT \ No newline at end of file diff --git a/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-zhTW.txt b/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-zhTW.txt new file mode 100644 index 0000000000..409a8c1424 --- /dev/null +++ b/dep/StormLib/doc/sc2-authenticationcode/sc2-authenticationcode-zhTW.txt @@ -0,0 +1 @@ +U3NFQJV4M6GC7KBN9XQJ3BRDN3PLD9NE \ No newline at end of file diff --git a/dep/StormLib/stormlib_dll/DllMain.c b/dep/StormLib/src/DllMain.c similarity index 91% rename from dep/StormLib/stormlib_dll/DllMain.c rename to dep/StormLib/src/DllMain.c index cbfa84a08a..98eb0038a9 100644 --- a/dep/StormLib/stormlib_dll/DllMain.c +++ b/dep/StormLib/src/DllMain.c @@ -14,11 +14,11 @@ //----------------------------------------------------------------------------- // DllMain -DWORD WINAPI DllMain(HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved) +BOOL WINAPI DllMain(HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved) { UNREFERENCED_PARAMETER(hInst); UNREFERENCED_PARAMETER(dwReason); UNREFERENCED_PARAMETER(lpReserved); - return TRUE; + return TRUE; } diff --git a/dep/StormLib/stormlib_dll/StormLib.def b/dep/StormLib/src/DllMain.def similarity index 82% rename from dep/StormLib/stormlib_dll/StormLib.def rename to dep/StormLib/src/DllMain.def index 0ba9188431..38d1156565 100644 --- a/dep/StormLib/stormlib_dll/StormLib.def +++ b/dep/StormLib/src/DllMain.def @@ -13,31 +13,32 @@ EXPORTS SFileOpenArchive SFileCreateArchive - SFileGetArchiveBitmap + SFileCreateArchive2 SFileFlushArchive SFileCloseArchive SFileAddListFile + SFileAddListFileEntries SFileSetCompactCallback SFileCompactArchive - + SFileGetMaxFileCount - SFileSetMaxFileCount - + SFileSetMaxFileCount + SFileGetAttributes SFileSetAttributes SFileUpdateFileAttributes SFileOpenPatchArchive SFileIsPatchedArchive - + SFileOpenFileEx SFileGetFileSize SFileSetFilePointer SFileReadFile SFileCloseFile - + SFileHasFile SFileGetFileName SFileGetFileInfo @@ -57,7 +58,7 @@ EXPORTS SListFileFindClose SFileEnumLocales - + SFileCreateFile SFileWriteFile SFileFinishFile @@ -70,7 +71,13 @@ EXPORTS SFileSetDataCompression SFileSetAddFileCallback + SMemUTF8ToFileName + SMemFileNameToUTF8 + SCompImplode SCompExplode - SCompCompress - SCompDecompress + SCompCompress + SCompDecompress + + GetLastError=Kernel32.GetLastError + SetLastError=Kernel32.SetLastError diff --git a/dep/StormLib/src/DllMain.rc b/dep/StormLib/src/DllMain.rc new file mode 100644 index 0000000000..e21ecef64f --- /dev/null +++ b/dep/StormLib/src/DllMain.rc @@ -0,0 +1,110 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Neutral resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU) +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL +#pragma code_page(1250) + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 9,26,0,3 + PRODUCTVERSION 9,26,0,3 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x0L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040504b0" + BEGIN + VALUE "Comments", "http://www.zezula.net/mpq.html" + VALUE "FileDescription", "StormLib library for reading Blizzard MPQ archives" + VALUE "FileVersion", "9.26.0.3" + VALUE "InternalName", "StormLib" + VALUE "LegalCopyright", "Copyright (c) 2014 - 2024 Ladislav Zezula" + VALUE "OriginalFilename", "StormLib.dll" + VALUE "ProductName", "StormLib" + VALUE "ProductVersion", "9.26.0.3" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x405, 1200 + END +END + +#endif // Neutral resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// Czech (Czech Republic) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CSY) +LANGUAGE LANG_CZECH, SUBLANG_DEFAULT +#pragma code_page(1250) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // Czech (Czech Republic) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/dep/StormLib/src/FileStream.cpp b/dep/StormLib/src/FileStream.cpp index 413f2acb3a..202d6e3e99 100644 --- a/dep/StormLib/src/FileStream.cpp +++ b/dep/StormLib/src/FileStream.cpp @@ -19,7 +19,8 @@ #include "FileStream.h" #ifdef _MSC_VER -#pragma comment(lib, "wininet.lib") +#pragma comment(lib, "wininet.lib") // Internet functions for HTTP stream +#pragma warning(disable: 4800) // 'BOOL' : forcing value to bool 'true' or 'false' (performance warning) #endif //----------------------------------------------------------------------------- @@ -29,103 +30,155 @@ #define INVALID_HANDLE_VALUE ((HANDLE)-1) #endif -#ifdef _MSC_VER -#pragma warning(disable: 4800) // 'BOOL' : forcing value to bool 'true' or 'false' (performance warning) -#endif - //----------------------------------------------------------------------------- // Local functions - platform-specific functions -#ifndef PLATFORM_WINDOWS -static int nLastError = ERROR_SUCCESS; - -int GetLastError() +static DWORD StringToInt(const char * szString) { - return nLastError; + DWORD dwValue = 0; + + while('0' <= szString[0] && szString[0] <= '9') + { + dwValue = (dwValue * 10) + (szString[0] - '0'); + szString++; + } + + return dwValue; } -void SetLastError(int nError) +static void CreateNameWithSuffix(LPTSTR szBuffer, size_t cchMaxChars, LPCTSTR szName, unsigned int nValue) { - nLastError = nError; -} -#endif + LPTSTR szBufferEnd = szBuffer + cchMaxChars - 1; -#ifndef PLATFORM_LITTLE_ENDIAN -void ConvertPartHeader(void * partHeader) -{ - PPART_FILE_HEADER theHeader = (PPART_FILE_HEADER)partHeader; + // Copy the name + while(szBuffer < szBufferEnd && szName[0] != 0) + *szBuffer++ = *szName++; - theHeader->PartialVersion = SwapUInt32(theHeader->PartialVersion); - theHeader->Flags = SwapUInt32(theHeader->Flags); - theHeader->FileSizeLo = SwapUInt32(theHeader->FileSizeLo); - theHeader->FileSizeHi = SwapUInt32(theHeader->FileSizeHi); - theHeader->BlockSize = SwapUInt32(theHeader->BlockSize); + // Append "." + if(szBuffer < szBufferEnd) + *szBuffer++ = '.'; + + // Append the number + SMemIntToStr(szBuffer, szBufferEnd - szBuffer + 1, nValue); } -#endif //----------------------------------------------------------------------------- -// Preparing file bitmap for a complete file of a given size +// Dummy init function -#define DEFAULT_BLOCK_SIZE 0x4000 - -static bool Dummy_GetBitmap( - TFileStream * pStream, - TFileBitmap * pBitmap, - DWORD Length, - LPDWORD LengthNeeded) +static void BaseNone_Init(TFileStream *) { - ULONGLONG FileSize = 0; - DWORD TotalLength; - DWORD BlockCount; - DWORD BitmapSize; - DWORD LastByte; - bool bResult = false; - - // 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); - - // Calculate and give the total length - TotalLength = sizeof(TFileBitmap) + BitmapSize; - if(LengthNeeded != NULL) - *LengthNeeded = TotalLength; - - // Has the caller given enough space for storing the structure? - if(Length >= sizeof(TFileBitmap)) - { - memset(pBitmap, 0, sizeof(TFileBitmap)); - pBitmap->EndOffset = FileSize; - pBitmap->IsComplete = 1; - pBitmap->BitmapSize = BitmapSize; - pBitmap->BlockSize = DEFAULT_BLOCK_SIZE; - bResult = true; - } - - // Do we have enough space to fill the bitmap as well? - if(Length >= TotalLength) - { - LPBYTE pbBitmap = (LPBYTE)(pBitmap + 1); - - // Fill the full blocks - memset(pbBitmap, 0xFF, (BlockCount / 8)); - pbBitmap += (BlockCount / 8); - bResult = true; - - // Supply the last block - if(BlockCount & 7) - { - LastByte = (1 << (BlockCount & 7)) - 1; - pbBitmap[0] = (BYTE)LastByte; - } - } - - return bResult; + // Nothing here } //----------------------------------------------------------------------------- // Local functions - base file support +static bool BaseFile_Create(TFileStream * pStream) +{ +#ifdef STORMLIB_WINDOWS + { + DWORD dwWriteShare = (pStream->dwFlags & STREAM_FLAG_WRITE_SHARE) ? FILE_SHARE_WRITE : 0; + + pStream->Base.File.hFile = CreateFile(pStream->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 defined(STORMLIB_MAC) || defined(STORMLIB_LINUX) + { + intptr_t handle; + + handle = open(pStream->szFileName, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if(handle == -1) + { + pStream->Base.File.hFile = INVALID_HANDLE_VALUE; + SErrSetLastError(errno); + return false; + } + + pStream->Base.File.hFile = (HANDLE)handle; + } +#endif + + // Reset the file size and position + pStream->Base.File.FileSize = 0; + pStream->Base.File.FilePos = 0; + return true; +} + +static bool BaseFile_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD dwStreamFlags) +{ +#ifdef STORMLIB_WINDOWS + { + ULARGE_INTEGER FileSize; + DWORD dwWriteAccess = (dwStreamFlags & STREAM_FLAG_READ_ONLY) ? 0 : FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES; + DWORD dwWriteShare = (dwStreamFlags & STREAM_FLAG_WRITE_SHARE) ? FILE_SHARE_WRITE : 0; + + // Open the file + pStream->Base.File.hFile = CreateFile(szFileName, + FILE_READ_DATA | FILE_READ_ATTRIBUTES | dwWriteAccess, + FILE_SHARE_READ | dwWriteShare, + 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(STORMLIB_MAC) || defined(STORMLIB_LINUX) + { + struct stat64 fileinfo; + int oflag = (dwStreamFlags & STREAM_FLAG_READ_ONLY) ? O_RDONLY : O_RDWR; + intptr_t handle; + + // Open the file + handle = open(szFileName, oflag | O_LARGEFILE); + if(handle == -1) + { + pStream->Base.File.hFile = INVALID_HANDLE_VALUE; + SErrSetLastError(errno); + return false; + } + + // Get the file size + if(fstat64(handle, &fileinfo) == -1) + { + pStream->Base.File.hFile = INVALID_HANDLE_VALUE; + SErrSetLastError(errno); + close(handle); + 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 + + // Reset the file position + pStream->Base.File.FilePos = 0; + return true; +} + 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 @@ -135,7 +188,7 @@ static bool BaseFile_Read( ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.File.FilePos; DWORD dwBytesRead = 0; // Must be set by platform-specific code -#ifdef PLATFORM_WINDOWS +#ifdef STORMLIB_WINDOWS { // Note: StormLib no longer supports Windows 9x. // Thus, we can use the OVERLAPPED structure to specify @@ -156,36 +209,18 @@ static bool BaseFile_Read( if(!ReadFile(pStream->Base.File.hFile, pvBuffer, dwBytesToRead, &dwBytesRead, &Overlapped)) return false; } -/* - // If the byte offset is different from the current file position, - // we have to update the file position - if(ByteOffset != pStream->Base.File.FilePos) - { - LONG ByteOffsetHi = (LONG)(ByteOffset >> 32); - - SetFilePointer(pStream->Base.File.hFile, (LONG)ByteOffset, &ByteOffsetHi, FILE_BEGIN); - pStream->Base.File.FilePos = ByteOffset; - } - - // Read the data - if(dwBytesToRead != 0) - { - if(!ReadFile(pStream->Base.File.hFile, pvBuffer, dwBytesToRead, &dwBytesRead, NULL)) - return false; - } -*/ } #endif -#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) +#if defined(STORMLIB_MAC) || defined(STORMLIB_LINUX) { ssize_t bytes_read; // If the byte offset is different from the current file position, - // we have to update the file position + // we have to update the file position xxx if(ByteOffset != pStream->Base.File.FilePos) { - lseek((intptr_t)pStream->Base.File.hFile, (off_t)(ByteOffset), SEEK_SET); + lseek64((intptr_t)pStream->Base.File.hFile, (off64_t)(ByteOffset), SEEK_SET); pStream->Base.File.FilePos = ByteOffset; } @@ -195,10 +230,10 @@ static bool BaseFile_Read( bytes_read = read((intptr_t)pStream->Base.File.hFile, pvBuffer, (size_t)dwBytesToRead); if(bytes_read == -1) { - nLastError = errno; + SErrSetLastError(errno); return false; } - + dwBytesRead = (DWORD)(size_t)bytes_read; } } @@ -208,7 +243,7 @@ static bool BaseFile_Read( // If the number of bytes read doesn't match to required amount, return false pStream->Base.File.FilePos = ByteOffset + dwBytesRead; if(dwBytesRead != dwBytesToRead) - SetLastError(ERROR_HANDLE_EOF); + SErrSetLastError(ERROR_HANDLE_EOF); return (dwBytesRead == dwBytesToRead); } @@ -224,7 +259,7 @@ static bool BaseFile_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.File.FilePos; DWORD dwBytesWritten = 0; // Must be set by platform-specific code -#ifdef PLATFORM_WINDOWS +#ifdef STORMLIB_WINDOWS { // Note: StormLib no longer supports Windows 9x. // Thus, we can use the OVERLAPPED structure to specify @@ -245,28 +280,10 @@ static bool BaseFile_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const 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(ByteOffset != pStream->Base.File.FilePos) - { - LONG ByteOffsetHi = (LONG)(ByteOffset >> 32); - - SetFilePointer(pStream->Base.File.hFile, (LONG)ByteOffset, &ByteOffsetHi, FILE_BEGIN); - pStream->Base.File.FilePos = ByteOffset; - } - - // Read the data - if(dwBytesToWrite != 0) - { - if(!WriteFile(pStream->Base.File.hFile, pvBuffer, dwBytesToWrite, &dwBytesWritten, NULL)) - return false; - } -*/ } #endif -#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) +#if defined(STORMLIB_MAC) || defined(STORMLIB_LINUX) { ssize_t bytes_written; @@ -274,7 +291,7 @@ static bool BaseFile_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const // we have to update the file position if(ByteOffset != pStream->Base.File.FilePos) { - lseek((intptr_t)pStream->Base.File.hFile, (off_t)(ByteOffset), SEEK_SET); + lseek64((intptr_t)pStream->Base.File.hFile, (off64_t)(ByteOffset), SEEK_SET); pStream->Base.File.FilePos = ByteOffset; } @@ -282,10 +299,10 @@ static bool BaseFile_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const bytes_written = write((intptr_t)pStream->Base.File.hFile, pvBuffer, (size_t)dwBytesToWrite); if(bytes_written == -1) { - nLastError = errno; + SErrSetLastError(errno); return false; } - + dwBytesWritten = (DWORD)(size_t)bytes_written; } #endif @@ -298,33 +315,17 @@ static bool BaseFile_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const pStream->Base.File.FileSize = pStream->Base.File.FilePos; if(dwBytesWritten != dwBytesToWrite) - SetLastError(ERROR_DISK_FULL); + SErrSetLastError(ERROR_DISK_FULL); return (dwBytesWritten == dwBytesToWrite); } -static bool BaseFile_GetPos( - TFileStream * pStream, // Pointer to an open stream - ULONGLONG & ByteOffset) // Pointer to file byte offset -{ - ByteOffset = pStream->Base.File.FilePos; - return true; -} - -static bool BaseFile_GetSize( - TFileStream * pStream, // Pointer to an open stream - ULONGLONG & FileSize) // Pointer where to store file size -{ - FileSize = pStream->Base.File.FileSize; - return true; -} - /** * \a pStream Pointer to an open stream * \a NewFileSize New size of the file */ -static bool BaseFile_SetSize(TFileStream * pStream, ULONGLONG NewFileSize) +static bool BaseFile_Resize(TFileStream * pStream, ULONGLONG NewFileSize) { -#ifdef PLATFORM_WINDOWS +#ifdef STORMLIB_WINDOWS { LONG FileSizeHi = (LONG)(NewFileSize >> 32); LONG FileSizeLo; @@ -333,11 +334,13 @@ static bool BaseFile_SetSize(TFileStream * pStream, ULONGLONG NewFileSize) // Set the position at the new file size dwNewPos = SetFilePointer(pStream->Base.File.hFile, (LONG)NewFileSize, &FileSizeHi, FILE_BEGIN); - if(dwNewPos == INVALID_SET_FILE_POINTER && GetLastError() != ERROR_SUCCESS) + if(dwNewPos == INVALID_SET_FILE_POINTER && SErrGetLastError() != ERROR_SUCCESS) return false; // Set the current file pointer as the end of the file bResult = (bool)SetEndOfFile(pStream->Base.File.hFile); + if(bResult) + pStream->Base.File.FileSize = NewFileSize; // Restore the file position FileSizeHi = (LONG)(pStream->Base.File.FilePos >> 32); @@ -346,30 +349,43 @@ static bool BaseFile_SetSize(TFileStream * pStream, ULONGLONG NewFileSize) return bResult; } #endif - -#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) + +#if defined(STORMLIB_MAC) || defined(STORMLIB_LINUX) { - if(ftruncate((intptr_t)pStream->Base.File.hFile, (off_t)NewFileSize) == -1) + if(ftruncate64((intptr_t)pStream->Base.File.hFile, (off64_t)NewFileSize) == -1) { - nLastError = errno; + SErrSetLastError(errno); return false; } - + + pStream->Base.File.FileSize = NewFileSize; return true; } #endif } -static bool BaseFile_GetTime(TFileStream * pStream, ULONGLONG * pFileTime) +// Gives the current file size +static bool BaseFile_GetSize(TFileStream * pStream, ULONGLONG * pFileSize) { - *pFileTime = pStream->Base.File.FileTime; + // Note: Used by all thre base providers. + // Requires the TBaseData union to have the same layout for all three base providers + *pFileSize = pStream->Base.File.FileSize; + return true; +} + +// Gives the current file position +static bool BaseFile_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset) +{ + // Note: Used by all thre base providers. + // Requires the TBaseData union to have the same layout for all three base providers + *pByteOffset = pStream->Base.File.FilePos; return true; } // Renames the file pointed by pStream so that it contains data from pNewStream -static bool BaseFile_Switch(TFileStream * pStream, TFileStream * pNewStream) +static bool BaseFile_Replace(TFileStream * pStream, TFileStream * pNewStream) { -#ifdef PLATFORM_WINDOWS +#ifdef STORMLIB_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); @@ -378,14 +394,14 @@ static bool BaseFile_Switch(TFileStream * pStream, TFileStream * pNewStream) return (bool)MoveFile(pNewStream->szFileName, pStream->szFileName); #endif -#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) +#if defined(STORMLIB_MAC) || defined(STORMLIB_LINUX) // "rename" on Linux also works if the target file exists if(rename(pNewStream->szFileName, pStream->szFileName) == -1) { - nLastError = errno; + SErrSetLastError(errno); return false; } - + return true; #endif } @@ -394,11 +410,11 @@ static void BaseFile_Close(TFileStream * pStream) { if(pStream->Base.File.hFile != INVALID_HANDLE_VALUE) { -#ifdef PLATFORM_WINDOWS +#ifdef STORMLIB_WINDOWS CloseHandle(pStream->Base.File.hFile); #endif -#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) +#if defined(STORMLIB_MAC) || defined(STORMLIB_LINUX) close((intptr_t)pStream->Base.File.hFile); #endif } @@ -407,138 +423,174 @@ static void BaseFile_Close(TFileStream * pStream) pStream->Base.File.hFile = INVALID_HANDLE_VALUE; } -static bool BaseFile_Create( - TFileStream * pStream, - const TCHAR * szFileName, - DWORD dwStreamFlags) +// Initializes base functions for the disk file +static void BaseFile_Init(TFileStream * pStream) { -#ifdef PLATFORM_WINDOWS - { - 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 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) - { - nLastError = errno; - return false; - } - - pStream->Base.File.hFile = (HANDLE)handle; - } -#endif - - // Fill-in the entry points + pStream->BaseCreate = BaseFile_Create; + pStream->BaseOpen = BaseFile_Open; pStream->BaseRead = BaseFile_Read; pStream->BaseWrite = BaseFile_Write; - pStream->BaseGetPos = BaseFile_GetPos; + pStream->BaseResize = BaseFile_Resize; 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 +#ifdef STORMLIB_WINDOWS + +typedef struct _SECTION_BASIC_INFORMATION +{ + PVOID BaseAddress; + ULONG Attributes; + LARGE_INTEGER Size; +} SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION; + +typedef ULONG (WINAPI * NTQUERYSECTION)( + IN HANDLE SectionHandle, + IN ULONG SectionInformationClass, + OUT PVOID SectionInformation, + IN SIZE_T Length, + OUT PSIZE_T ResultLength); + +static bool RetrieveFileMappingSize(HANDLE hSection, ULARGE_INTEGER & RefFileSize) +{ + SECTION_BASIC_INFORMATION BasicInfo = {0}; + NTQUERYSECTION PfnQuerySection; + HMODULE hNtdll; + SIZE_T ReturnLength = 0; + + if((hNtdll = GetModuleHandle(_T("ntdll.dll"))) != NULL) + { + PfnQuerySection = (NTQUERYSECTION)GetProcAddress(hNtdll, "NtQuerySection"); + if(PfnQuerySection != NULL) + { + if(PfnQuerySection(hSection, 0, &BasicInfo, sizeof(SECTION_BASIC_INFORMATION), &ReturnLength) == 0) + { + RefFileSize.HighPart = BasicInfo.Size.HighPart; + RefFileSize.LowPart = BasicInfo.Size.LowPart; + return true; + } + } + } + + return false; +} +#endif + +static bool BaseMap_Open(TFileStream * pStream, LPCTSTR szFileName, DWORD dwStreamFlags) +{ +#ifdef STORMLIB_WINDOWS + + ULARGE_INTEGER FileSize = {0}; + HANDLE hFile = INVALID_HANDLE_VALUE; + HANDLE hMap = NULL; + bool bResult = false; + + // Keep compilers happy + STORMLIB_UNUSED(dwStreamFlags); + + // 1) Try to treat "szFileName" as a section name + hMap = OpenFileMapping(SECTION_QUERY | FILE_MAP_READ, FALSE, szFileName); + if(hMap != NULL) + { + // Try to retrieve the size of the mapping + if(!RetrieveFileMappingSize(hMap, FileSize)) + { + CloseHandle(hMap); + hMap = NULL; + } + } + + // 2) Treat the name as file name + else + { + hFile = CreateFile(szFileName, FILE_READ_DATA, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if(hFile != INVALID_HANDLE_VALUE) + { + // Retrieve file size. Don't allow mapping file of a zero size. + FileSize.LowPart = GetFileSize(hFile, &FileSize.HighPart); + if(FileSize.QuadPart != 0) + { + // Now create file mapping over the file + hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); + } + } + } + + // Did it succeed? + 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) + { + // Retrieve file time. If it's named section, put 0 + if(hFile != INVALID_HANDLE_VALUE) + GetFileTime(hFile, NULL, NULL, (LPFILETIME)&pStream->Base.Map.FileTime); + + // Retrieve file size and position + pStream->Base.Map.FileSize = FileSize.QuadPart; + pStream->Base.Map.FilePos = 0; + bResult = true; + } + + // Close the map handle + CloseHandle(hMap); + } + + // Close the file handle + if(hFile != INVALID_HANDLE_VALUE) + CloseHandle(hFile); + + // Return the result of the operation + return bResult; + +#elif defined(STORMLIB_HAS_MMAP) + + struct stat64 fileinfo; + intptr_t handle; + bool bResult = false; + + // Open the file + handle = open(szFileName, O_RDONLY); + if(handle != -1) + { + // Get the file size + if(fstat64(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) + SErrSetLastError(errno); + return bResult; + +#else + + // File mapping is not supported + return false; + +#endif +} + 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 @@ -563,139 +615,36 @@ static bool BaseMap_Read( return true; } -static bool BaseMap_GetPos( - TFileStream * pStream, // Pointer to an open stream - ULONGLONG & ByteOffset) // Pointer to file byte offset -{ - ByteOffset = pStream->Base.Map.FilePos; - return true; -} - -static bool BaseMap_GetSize( - TFileStream * pStream, // Pointer to an open stream - ULONGLONG & FileSize) // Pointer where to store file size -{ - FileSize = 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 + +#ifdef STORMLIB_WINDOWS + if(pStream->Base.Map.pbFile != NULL) UnmapViewOfFile(pStream->Base.Map.pbFile); -#endif -#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) +#elif defined(STORMLIB_HAS_MMAP) + 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) +// Initializes base functions for the mapped file +static void BaseMap_Init(TFileStream * pStream) { -#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 + // Supply the file stream functions + pStream->BaseOpen = BaseMap_Open; pStream->BaseRead = BaseMap_Read; - pStream->BaseGetPos = BaseMap_GetPos; - pStream->BaseGetSize = BaseMap_GetSize; - pStream->BaseGetTime = BaseMap_GetTime; + pStream->BaseGetSize = BaseFile_GetSize; // Reuse BaseFile function + pStream->BaseGetPos = BaseFile_GetPos; // Reuse BaseFile function pStream->BaseClose = BaseMap_Close; - pStream->dwFlags = dwStreamFlags; - return true; + + // Mapped files are read-only + pStream->dwFlags |= STREAM_FLAG_READ_ONLY; } //----------------------------------------------------------------------------- @@ -717,20 +666,128 @@ static const TCHAR * BaseHttp_ExtractServerName(const TCHAR * szFileName, TCHAR else { while(szFileName[0] != 0 && szFileName[0] != _T('/')) - *szFileName++; + szFileName++; } // Return the remainder return szFileName; } +static bool BaseHttp_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD dwStreamFlags) +{ +#ifdef STORMLIB_WINDOWS + + HINTERNET hRequest; + DWORD dwTemp = 0; + + // Keep compilers happy + STORMLIB_UNUSED(dwStreamFlags); + + // Don't connect to the internet + if(!InternetGetConnectedState(&dwTemp, 0)) + return false; + + // Initiate the connection to the internet + pStream->Base.Http.hInternet = InternetOpen(_T("StormLib HTTP MPQ reader"), + INTERNET_OPEN_TYPE_PRECONFIG, + NULL, + NULL, + 0); + if(pStream->Base.Http.hInternet != NULL) + { + 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) + { + // 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; + TCHAR StatusCode[0x08]; + + // Check if the file succeeded to open + dwDataSize = sizeof(StatusCode); + if(HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_CODE, StatusCode, &dwDataSize, &dwIndex)) + { + if(_tcscmp(StatusCode, _T("200"))) + { + InternetCloseHandle(hRequest); + SErrSetLastError(ERROR_FILE_NOT_FOUND); + return false; + } + } + + // 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) + { + InternetCloseHandle(hRequest); + pStream->Base.Http.FileSize = dwFileSize; + pStream->Base.Http.FilePos = 0; + return true; + } + } + } + + // Close the request + InternetCloseHandle(hRequest); + } + + // Close the connection handle + InternetCloseHandle(pStream->Base.Http.hConnect); + pStream->Base.Http.hConnect = NULL; + } + + // Close the internet handle + InternetCloseHandle(pStream->Base.Http.hInternet); + pStream->Base.Http.hInternet = NULL; + } + + // If the file is not there or is not available for random access, report error + pStream->BaseClose(pStream); + return false; + +#else + + // Not supported + SErrSetLastError(ERROR_NOT_SUPPORTED); + pStream = pStream; + return false; + +#endif +} + 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 +#ifdef STORMLIB_WINDOWS ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.Http.FilePos; DWORD dwTotalBytesRead = 0; @@ -743,7 +800,6 @@ static bool BaseHttp_Read( 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); @@ -752,8 +808,8 @@ static bool BaseHttp_Read( { // 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); + wsprintf(szRangeRequest, _T("Range: bytes=%u-%u"), (unsigned int)dwStartOffset, (unsigned int)dwEndOffset); + HttpAddRequestHeaders(hRequest, szRangeRequest, 0xFFFFFFFF, HTTP_ADDREQ_FLAG_ADD_IF_NEW); // Send the request to the server if(HttpSendRequest(hRequest, NULL, 0, NULL, 0)) @@ -764,8 +820,8 @@ static bool BaseHttp_Read( DWORD dwBlockBytesRead = 0; // Read the block from the file - if(dwBlockBytesToRead > sizeof(Buffer)) - dwBlockBytesToRead = sizeof(Buffer); + if(dwBlockBytesToRead > 0x200) + dwBlockBytesToRead = 0x200; InternetReadFile(hRequest, pbBuffer, dwBlockBytesToRead, &dwBlockBytesRead); // Check for end @@ -786,7 +842,7 @@ static bool BaseHttp_Read( // If the number of bytes read doesn't match the required amount, return false if(dwTotalBytesRead != dwBytesToRead) - SetLastError(ERROR_HANDLE_EOF); + SErrSetLastError(ERROR_HANDLE_EOF); return (dwTotalBytesRead == dwBytesToRead); #else @@ -796,37 +852,15 @@ static bool BaseHttp_Read( pByteOffset = pByteOffset; pvBuffer = pvBuffer; dwBytesToRead = dwBytesToRead; - SetLastError(ERROR_NOT_SUPPORTED); + SErrSetLastError(ERROR_NOT_SUPPORTED); return false; #endif } -static bool BaseHttp_GetPos( - TFileStream * pStream, // Pointer to an open stream - ULONGLONG & ByteOffset) // Pointer to file byte offset -{ - ByteOffset = pStream->Base.Http.FilePos; - return true; -} - -static bool BaseHttp_GetSize( - TFileStream * pStream, // Pointer to an open stream - ULONGLONG & FileSize) // Pointer where to store file size -{ - FileSize = 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 +#ifdef STORMLIB_WINDOWS if(pStream->Base.Http.hConnect != NULL) InternetCloseHandle(pStream->Base.Http.hConnect); pStream->Base.Http.hConnect = NULL; @@ -839,273 +873,628 @@ static void BaseHttp_Close(TFileStream * pStream) #endif } -static bool BaseHttp_Open( - TFileStream * pStream, - const TCHAR * szFileName, - DWORD dwStreamFlags) +// Initializes base functions for the mapped file +static void BaseHttp_Init(TFileStream * pStream) { -#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 + // Supply the stream functions + pStream->BaseOpen = BaseHttp_Open; pStream->BaseRead = BaseHttp_Read; - pStream->BaseGetPos = BaseHttp_GetPos; - pStream->BaseGetSize = BaseHttp_GetSize; - pStream->BaseGetTime = BaseHttp_GetTime; + pStream->BaseGetSize = BaseFile_GetSize; // Reuse BaseFile function + pStream->BaseGetPos = BaseFile_GetPos; // Reuse BaseFile function pStream->BaseClose = BaseHttp_Close; - pStream->dwFlags = dwStreamFlags; - return true; -#else - - // Not supported - pStream = pStream; - szFileName = szFileName; - SetLastError(ERROR_NOT_SUPPORTED); - return false; - -#endif + // HTTP files are read-only + pStream->dwFlags |= STREAM_FLAG_READ_ONLY; } //----------------------------------------------------------------------------- -// Local functions - linear stream support +// Local functions - base block-based support -static bool LinearStream_Read( - TLinearStream * pStream, // Pointer to an open stream +// Generic function that loads blocks from the file +// The function groups the block with the same availability, +// so the called BlockRead can finish the request in a single system call +static bool BlockStream_Read( + TBlockStream * 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 BlockOffset0; + ULONGLONG BlockOffset; ULONGLONG ByteOffset; ULONGLONG EndOffset; - LPBYTE pbBitmap; - DWORD BlockIndex; - DWORD ByteIndex; - DWORD BitMask; + LPBYTE TransferBuffer; + LPBYTE BlockBuffer; + DWORD BlockBufferOffset; // Offset of the desired data in the block buffer + DWORD BytesNeeded; // Number of bytes that really need to be read + DWORD BlockSize = pStream->BlockSize; + DWORD BlockCount; + bool bPrevBlockAvailable; + bool bCallbackCalled = false; + bool bBlockAvailable; + bool bResult = true; - // At this point, we must have a bitmap set - assert(pStream->pBitmap != NULL); + // The base block read function must be present + assert(pStream->BlockRead != NULL); - // If we have data map, we must check if the data block is present in the MPQ - if(dwBytesToRead != 0) + // NOP reading of zero bytes + if(dwBytesToRead == 0) + return true; + + // Get the current position in the stream + ByteOffset = (pByteOffset != NULL) ? pByteOffset[0] : pStream->StreamPos; + EndOffset = ByteOffset + dwBytesToRead; + if(EndOffset > pStream->StreamSize) { - DWORD BlockSize = pStream->pBitmap->BlockSize; + SErrSetLastError(ERROR_HANDLE_EOF); + return false; + } - // Get the offset where we read it from - if(pByteOffset == NULL) - pStream->BaseGetPos(pStream, ByteOffset); - else - ByteOffset = *pByteOffset; - EndOffset = ByteOffset + dwBytesToRead; + // Calculate the block parameters + BlockOffset0 = BlockOffset = ByteOffset & ~((ULONGLONG)BlockSize - 1); + BlockCount = (DWORD)(((EndOffset - BlockOffset) + (BlockSize - 1)) / BlockSize); + BytesNeeded = (DWORD)(EndOffset - BlockOffset); - // If the start of the area is within the region - // protected by data map, check each block - if(ByteOffset < pStream->pBitmap->EndOffset) + // Remember where we have our data + assert((BlockSize & (BlockSize - 1)) == 0); + BlockBufferOffset = (DWORD)(ByteOffset & (BlockSize - 1)); + + // Allocate buffer for reading blocks + TransferBuffer = BlockBuffer = STORM_ALLOC(BYTE, (BlockCount * BlockSize)); + if(TransferBuffer == NULL) + { + SErrSetLastError(ERROR_NOT_ENOUGH_MEMORY); + return false; + } + + // If all blocks are available, just read all blocks at once + if(pStream->IsComplete == 0) + { + // Now parse the blocks and send the block read request + // to all blocks with the same availability + assert(pStream->BlockCheck != NULL); + bPrevBlockAvailable = pStream->BlockCheck(pStream, BlockOffset); + + // Loop as long as we have something to read + while(BlockOffset < EndOffset) { - // Cut the end of the stream protected by the data map - EndOffset = STORMLIB_MIN(EndOffset, pStream->pBitmap->EndOffset); + // Determine availability of the next block + bBlockAvailable = pStream->BlockCheck(pStream, BlockOffset); - // Calculate the initial block index - BlockIndex = (DWORD)(ByteOffset / BlockSize); - pbBitmap = (LPBYTE)(pStream->pBitmap + 1); - - // Parse each block - while(ByteOffset < EndOffset) + // If the availability has changed, read all blocks up to this one + if(bBlockAvailable != bPrevBlockAvailable) { - // 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) + // Call the file stream callback, if the block is not available + if(pStream->pMaster && pStream->pfnCallback && bPrevBlockAvailable == false) { - SetLastError(ERROR_FILE_CORRUPT); - return false; + pStream->pfnCallback(pStream->UserData, BlockOffset0, (DWORD)(BlockOffset - BlockOffset0)); + bCallbackCalled = true; } - // Move to tne next block - ByteOffset += BlockSize; - BlockIndex++; + // Load the continuous blocks with the same availability + assert(BlockOffset > BlockOffset0); + bResult = pStream->BlockRead(pStream, BlockOffset0, BlockOffset, BlockBuffer, BytesNeeded, bPrevBlockAvailable); + if(!bResult) + break; + + // Move the block offset + BlockBuffer += (DWORD)(BlockOffset - BlockOffset0); + BytesNeeded -= (DWORD)(BlockOffset - BlockOffset0); + bPrevBlockAvailable = bBlockAvailable; + BlockOffset0 = BlockOffset; + } + + // Move to the block offset in the stream + BlockOffset += BlockSize; + } + + // If there is a block(s) remaining to be read, do it + if(BlockOffset > BlockOffset0) + { + // Call the file stream callback, if the block is not available + if(pStream->pMaster && pStream->pfnCallback && bPrevBlockAvailable == false) + { + pStream->pfnCallback(pStream->UserData, BlockOffset0, (DWORD)(BlockOffset - BlockOffset0)); + bCallbackCalled = true; + } + + // Read the complete blocks from the file + if(BlockOffset > pStream->StreamSize) + BlockOffset = pStream->StreamSize; + bResult = pStream->BlockRead(pStream, BlockOffset0, BlockOffset, BlockBuffer, BytesNeeded, bPrevBlockAvailable); + } + } + else + { + // Read the complete blocks from the file + if(EndOffset > pStream->StreamSize) + EndOffset = pStream->StreamSize; + bResult = pStream->BlockRead(pStream, BlockOffset, EndOffset, BlockBuffer, BytesNeeded, true); + } + + // Now copy the data to the user buffer + if(bResult) + { + memcpy(pvBuffer, TransferBuffer + BlockBufferOffset, dwBytesToRead); + pStream->StreamPos = ByteOffset + dwBytesToRead; + } + else + { + // If the block read failed, set the last error + SErrSetLastError(ERROR_FILE_INCOMPLETE); + } + + // Call the callback to indicate we are done + if(bCallbackCalled) + pStream->pfnCallback(pStream->UserData, 0, 0); + + // Free the block buffer and return + STORM_FREE(TransferBuffer); + return bResult; +} + +static bool BlockStream_GetSize(TFileStream * pStream, ULONGLONG * pFileSize) +{ + *pFileSize = pStream->StreamSize; + return true; +} + +static bool BlockStream_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset) +{ + *pByteOffset = pStream->StreamPos; + return true; +} + +static void BlockStream_Close(TBlockStream * pStream) +{ + // Free the data map, if any + if(pStream->FileBitmap != NULL) + STORM_FREE(pStream->FileBitmap); + pStream->FileBitmap = NULL; + + // Call the base class for closing the stream + pStream->BaseClose(pStream); +} + +//----------------------------------------------------------------------------- +// File stream allocation function + +static STREAM_INIT StreamBaseInit[4] = +{ + BaseFile_Init, + BaseMap_Init, + BaseHttp_Init, + BaseNone_Init +}; + +// This function allocates an empty structure for the file stream +// The stream structure is created as flat block, variable length +// The file name is placed after the end of the stream structure data +static TFileStream * AllocateFileStream( + const TCHAR * szFileName, + size_t StreamSize, + DWORD dwStreamFlags) +{ + TFileStream * pMaster = NULL; + TFileStream * pStream; + const TCHAR * szNextFile = szFileName; + size_t FileNameSize; + + // Sanity check + assert(StreamSize != 0); + + // The caller can specify chain of files in the following form: + // C:\archive.MPQ*http://www.server.com/MPQs/archive-server.MPQ + // In that case, we use the part after "*" as master file name + while(szNextFile[0] != 0 && szNextFile[0] != _T('*')) + szNextFile++; + FileNameSize = (size_t)((szNextFile - szFileName) * sizeof(TCHAR)); + + // If we have a next file, we need to open it as master stream + // Note that we don't care if the master stream exists or not, + // If it doesn't, later attempts to read missing file block will fail + if(szNextFile[0] == _T('*')) + { + // Don't allow another master file in the string + if(_tcschr(szNextFile + 1, _T('*')) != NULL) + { + SErrSetLastError(ERROR_INVALID_PARAMETER); + return NULL; + } + + // Open the master file + pMaster = FileStream_OpenFile(szNextFile + 1, STREAM_FLAG_READ_ONLY); + } + + // Allocate the stream structure for the given stream type + pStream = (TFileStream *)STORM_ALLOC(BYTE, StreamSize + FileNameSize + sizeof(TCHAR)); + if(pStream != NULL) + { + // Zero the entire structure + memset(pStream, 0, StreamSize); + pStream->pMaster = pMaster; + pStream->dwFlags = dwStreamFlags; + + // Initialize the file name + pStream->szFileName = (TCHAR *)((BYTE *)pStream + StreamSize); + memcpy(pStream->szFileName, szFileName, FileNameSize); + pStream->szFileName[FileNameSize / sizeof(TCHAR)] = 0; + + // Initialize the stream functions + StreamBaseInit[dwStreamFlags & 0x03](pStream); + } + + return pStream; +} + +//----------------------------------------------------------------------------- +// Local functions - flat stream support + +static DWORD FlatStream_CheckFile(TBlockStream * pStream) +{ + LPBYTE FileBitmap = (LPBYTE)pStream->FileBitmap; + DWORD WholeByteCount = (pStream->BlockCount / 8); + DWORD ExtraBitsCount = (pStream->BlockCount & 7); + BYTE ExpectedValue; + + // Verify the whole bytes - their value must be 0xFF + for(DWORD i = 0; i < WholeByteCount; i++) + { + if(FileBitmap[i] != 0xFF) + return 0; + } + + // If there are extra bits, calculate the mask + if(ExtraBitsCount != 0) + { + ExpectedValue = (BYTE)((1 << ExtraBitsCount) - 1); + if(FileBitmap[WholeByteCount] != ExpectedValue) + return 0; + } + + // Yes, the file is complete + return 1; +} + +static bool FlatStream_LoadBitmap(TBlockStream * pStream) +{ + FILE_BITMAP_FOOTER Footer; + ULONGLONG ByteOffset; + LPBYTE FileBitmap; + DWORD BlockCount; + DWORD BitmapSize; + + // Do not load the bitmap if we should not have to + if(!(pStream->dwFlags & STREAM_FLAG_USE_BITMAP)) + return false; + + // Only if the size is greater than size of bitmap footer + if(pStream->Base.File.FileSize > sizeof(FILE_BITMAP_FOOTER)) + { + // Load the bitmap footer + ByteOffset = pStream->Base.File.FileSize - sizeof(FILE_BITMAP_FOOTER); + if(pStream->BaseRead(pStream, &ByteOffset, &Footer, sizeof(FILE_BITMAP_FOOTER))) + { + // Make sure that the array is properly BSWAP-ed + BSWAP_ARRAY32_UNSIGNED((LPDWORD)(&Footer), sizeof(FILE_BITMAP_FOOTER)); + + // Verify if there is actually a footer + if(Footer.Signature == ID_FILE_BITMAP_FOOTER && Footer.Version == 0x03 && Footer.BlockSize != 0) + { + // Get the offset of the bitmap, number of blocks and size of the bitmap + ByteOffset = MAKE_OFFSET64(Footer.MapOffsetHi, Footer.MapOffsetLo); + BlockCount = (DWORD)(((ByteOffset - 1) / Footer.BlockSize) + 1); + BitmapSize = ((BlockCount + 7) / 8); + + // Check if the sizes match + if(ByteOffset + BitmapSize + sizeof(FILE_BITMAP_FOOTER) == pStream->Base.File.FileSize) + { + // Allocate space for the bitmap + FileBitmap = STORM_ALLOC(BYTE, BitmapSize); + if(FileBitmap != NULL) + { + // Load the bitmap bits + if(!pStream->BaseRead(pStream, &ByteOffset, FileBitmap, BitmapSize)) + { + STORM_FREE(FileBitmap); + return false; + } + + // Update the stream size + pStream->BuildNumber = Footer.BuildNumber; + pStream->StreamSize = ByteOffset; + + // Fill the bitmap information + pStream->FileBitmap = FileBitmap; + pStream->BitmapSize = BitmapSize; + pStream->BlockSize = Footer.BlockSize; + pStream->BlockCount = BlockCount; + pStream->IsComplete = FlatStream_CheckFile(pStream); + return true; + } + } } } } - // Now if all tests passed, we can call the base read function - return pStream->BaseRead(pStream, pByteOffset, pvBuffer, dwBytesToRead); + return false; } -static bool LinearStream_Switch(TLinearStream * pStream, TLinearStream * pNewStream) +static void FlatStream_UpdateBitmap( + TBlockStream * pStream, // Pointer to an open stream + ULONGLONG StartOffset, + ULONGLONG EndOffset) { + LPBYTE FileBitmap = (LPBYTE)pStream->FileBitmap; + DWORD BlockIndex; + DWORD BlockSize = pStream->BlockSize; + DWORD ByteIndex; + BYTE BitMask; + // 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); + assert((StartOffset & (BlockSize - 1)) == 0); + assert(FileBitmap != NULL); - // Close the new stream - pNewStream->BaseClose(pNewStream); + // Calculate the index of the block + BlockIndex = (DWORD)(StartOffset / BlockSize); + ByteIndex = (BlockIndex / 0x08); + BitMask = (BYTE)(1 << (BlockIndex & 0x07)); - // 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)) + // Set all bits for the specified range + while(StartOffset < EndOffset) { - // Enough space for complete bitmap? - if(Length >= TotalLength) - { - memcpy(pBitmap, pStream->pBitmap, TotalLength); - bResult = true; - } - else - { - memcpy(pBitmap, pStream->pBitmap, sizeof(TFileBitmap)); - bResult = true; - } + // Set the bit + FileBitmap[ByteIndex] |= BitMask; + + // Move all + StartOffset += BlockSize; + ByteIndex += (BitMask >> 0x07); + BitMask = (BitMask >> 0x07) | (BitMask << 0x01); } - return bResult; + // Increment the bitmap update count + pStream->IsModified = 1; } -static void LinearStream_Close(TLinearStream * pStream) +static bool FlatStream_BlockCheck( + TBlockStream * pStream, // Pointer to an open stream + ULONGLONG BlockOffset) { - // 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); + LPBYTE FileBitmap = (LPBYTE)pStream->FileBitmap; + DWORD BlockIndex; + BYTE BitMask; + + // Sanity checks + assert((BlockOffset & (pStream->BlockSize - 1)) == 0); + assert(FileBitmap != NULL); + + // Calculate the index of the block + BlockIndex = (DWORD)(BlockOffset / pStream->BlockSize); + BitMask = (BYTE)(1 << (BlockIndex & 0x07)); + + // Check if the bit is present + return (FileBitmap[BlockIndex / 0x08] & BitMask) ? true : false; } -static bool LinearStream_Open(TLinearStream * pStream) +static bool FlatStream_BlockRead( + TBlockStream * pStream, // Pointer to an open stream + ULONGLONG StartOffset, + ULONGLONG EndOffset, + LPBYTE BlockBuffer, + DWORD BytesNeeded, + bool bAvailable) { - // 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; + DWORD BytesToRead = (DWORD)(EndOffset - StartOffset); + + // The starting offset must be aligned to size of the block + assert(pStream->FileBitmap != NULL); + assert((StartOffset & (pStream->BlockSize - 1)) == 0); + assert(StartOffset < EndOffset); + + // If the blocks are not available, we need to load them from the master + // and then save to the mirror + if(bAvailable == false) + { + // If we have no master, we cannot satisfy read request + if(pStream->pMaster == NULL) + return false; + + // Load the blocks from the master stream + // Note that we always have to read complete blocks + // so they get properly stored to the mirror stream + if(!FileStream_Read(pStream->pMaster, &StartOffset, BlockBuffer, BytesToRead)) + return false; + + // Store the loaded blocks to the mirror file. + // Note that this operation is not required to succeed + if(pStream->BaseWrite(pStream, &StartOffset, BlockBuffer, BytesToRead)) + FlatStream_UpdateBitmap(pStream, StartOffset, EndOffset); + + return true; + } + else + { + if(BytesToRead > BytesNeeded) + BytesToRead = BytesNeeded; + return pStream->BaseRead(pStream, &StartOffset, BlockBuffer, BytesToRead); + } +} + +static void FlatStream_Close(TBlockStream * pStream) +{ + FILE_BITMAP_FOOTER Footer; + + if(pStream->FileBitmap && pStream->IsModified) + { + // Write the file bitmap + pStream->BaseWrite(pStream, &pStream->StreamSize, pStream->FileBitmap, pStream->BitmapSize); + + // Prepare and write the file footer + Footer.Signature = ID_FILE_BITMAP_FOOTER; + Footer.Version = 3; + Footer.BuildNumber = pStream->BuildNumber; + Footer.MapOffsetLo = (DWORD)(pStream->StreamSize & 0xFFFFFFFF); + Footer.MapOffsetHi = (DWORD)(pStream->StreamSize >> 0x20); + Footer.BlockSize = pStream->BlockSize; + BSWAP_ARRAY32_UNSIGNED(&Footer, sizeof(FILE_BITMAP_FOOTER)); + pStream->BaseWrite(pStream, NULL, &Footer, sizeof(FILE_BITMAP_FOOTER)); + } + + // Close the base class + BlockStream_Close(pStream); +} + +static bool FlatStream_CreateMirror(TBlockStream * pStream) +{ + ULONGLONG MasterSize = 0; + ULONGLONG MirrorSize = 0; + LPBYTE FileBitmap = NULL; + DWORD dwBitmapSize; + DWORD dwBlockCount; + bool bNeedCreateMirrorStream = true; + bool bNeedResizeMirrorStream = true; + + // Do we have master function and base creation function? + if(pStream->pMaster == NULL || pStream->BaseCreate == NULL) + return false; + + // Retrieve the master file size, block count and bitmap size + FileStream_GetSize(pStream->pMaster, &MasterSize); + dwBlockCount = (DWORD)((MasterSize + DEFAULT_BLOCK_SIZE - 1) / DEFAULT_BLOCK_SIZE); + dwBitmapSize = (DWORD)((dwBlockCount + 7) / 8); + + // Setup stream size and position + pStream->BuildNumber = DEFAULT_BUILD_NUMBER; // BUGBUG: Really??? + pStream->StreamSize = MasterSize; + pStream->StreamPos = 0; + + // Open the base stream for write access + if(pStream->BaseOpen(pStream, pStream->szFileName, 0)) + { + // If the file open succeeded, check if the file size matches required size + pStream->BaseGetSize(pStream, &MirrorSize); + if(MirrorSize == MasterSize + dwBitmapSize + sizeof(FILE_BITMAP_FOOTER)) + { + // Attempt to load an existing file bitmap + if(FlatStream_LoadBitmap(pStream)) + return true; + + // We need to create new file bitmap + bNeedResizeMirrorStream = false; + } + + // We need to create mirror stream + bNeedCreateMirrorStream = false; + } + + // Create a new stream, if needed + if(bNeedCreateMirrorStream) + { + if(!pStream->BaseCreate(pStream)) + return false; + } + + // If we need to, then resize the mirror stream + if(bNeedResizeMirrorStream) + { + if(!pStream->BaseResize(pStream, MasterSize + dwBitmapSize + sizeof(FILE_BITMAP_FOOTER))) + return false; + } + + // Allocate the bitmap array + FileBitmap = STORM_ALLOC(BYTE, dwBitmapSize); + if(FileBitmap == NULL) + return false; + + // Initialize the bitmap + memset(FileBitmap, 0, dwBitmapSize); + pStream->FileBitmap = FileBitmap; + pStream->BitmapSize = dwBitmapSize; + pStream->BlockSize = DEFAULT_BLOCK_SIZE; + pStream->BlockCount = dwBlockCount; + pStream->IsComplete = 0; + pStream->IsModified = 1; + + // Note: Don't write the stream bitmap right away. + // Doing so would cause sparse file resize on NTFS, + // which would take long time on larger files. return true; } +static TFileStream * FlatStream_Open(const TCHAR * szFileName, DWORD dwStreamFlags) +{ + TBlockStream * pStream; + ULONGLONG ByteOffset = 0; + + // Create new empty stream + pStream = (TBlockStream *)AllocateFileStream(szFileName, sizeof(TBlockStream), dwStreamFlags); + if(pStream == NULL) + return NULL; + + // Do we have a master stream? + if(pStream->pMaster != NULL) + { + if(!FlatStream_CreateMirror(pStream)) + { + FileStream_Close(pStream); + SErrSetLastError(ERROR_FILE_NOT_FOUND); + return NULL; + } + } + else + { + // Attempt to open the base stream + if(!pStream->BaseOpen(pStream, pStream->szFileName, dwStreamFlags)) + { + FileStream_Close(pStream); + return NULL; + } + + // Load the bitmap, if required to + if(dwStreamFlags & STREAM_FLAG_USE_BITMAP) + FlatStream_LoadBitmap(pStream); + } + + // If we have a stream bitmap, set the reading functions + // which check presence of each file block + if(pStream->FileBitmap != NULL) + { + // Set the stream position to zero. Stream size is already set + assert(pStream->StreamSize != 0); + pStream->StreamPos = 0; + pStream->dwFlags |= STREAM_FLAG_READ_ONLY; + + // Supply the stream functions + pStream->StreamRead = (STREAM_READ)BlockStream_Read; + pStream->StreamGetSize = BlockStream_GetSize; + pStream->StreamGetPos = BlockStream_GetPos; + pStream->StreamClose = (STREAM_CLOSE)FlatStream_Close; + + // Supply the block functions + pStream->BlockCheck = (BLOCK_CHECK)FlatStream_BlockCheck; + pStream->BlockRead = (BLOCK_READ)FlatStream_BlockRead; + } + else + { + // Reset the base position to zero + pStream->BaseRead(pStream, &ByteOffset, NULL, 0); + + // Setup stream size and position + pStream->StreamSize = pStream->Base.File.FileSize; + pStream->StreamPos = 0; + + // Set the base functions + pStream->StreamRead = pStream->BaseRead; + pStream->StreamWrite = pStream->BaseWrite; + pStream->StreamResize = pStream->BaseResize; + pStream->StreamGetSize = pStream->BaseGetSize; + pStream->StreamGetPos = pStream->BaseGetPos; + pStream->StreamClose = pStream->BaseClose; + } + + return pStream; +} + //----------------------------------------------------------------------------- // Local functions - partial stream support @@ -1126,337 +1515,438 @@ static bool IsPartHeader(PPART_FILE_HEADER pPartHdr) return false; } -static bool PartialStream_Read( - TPartialStream * pStream, - ULONGLONG * pByteOffset, - void * pvBuffer, - DWORD dwBytesToRead) +static DWORD PartStream_CheckFile(TBlockStream * pStream) { - 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 + PPART_FILE_MAP_ENTRY FileBitmap = (PPART_FILE_MAP_ENTRY)pStream->FileBitmap; + DWORD dwBlockCount; - // If the byte offset is not entered, use the current position - if(pByteOffset == NULL) - pByteOffset = &pStream->VirtualPos; + // Get the number of blocks + dwBlockCount = (DWORD)((pStream->StreamSize + pStream->BlockSize - 1) / pStream->BlockSize); - // Check if the file position is not at or beyond end of the file - if(*pByteOffset >= pStream->VirtualSize) + // Check all blocks + for(DWORD i = 0; i < dwBlockCount; i++, FileBitmap++) { - SetLastError(ERROR_HANDLE_EOF); - return false; + // Few sanity checks + assert(FileBitmap->LargeValueHi == 0); + assert(FileBitmap->LargeValueLo == 0); + assert(FileBitmap->Flags == 0 || FileBitmap->Flags == 3); + + // Check if this block is present + if(FileBitmap->Flags != 3) + return 0; } - // 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->BlockCount - 1) - dwBlockSize = (DWORD)pStream->VirtualSize & (pStream->BlockSize - 1); - - // Get the number of bytes reamining in the current part - dwBytesInPart = dwBlockSize - dwPartOffset; - - // Compute the raw file offset of the file part - RawByteOffset = MAKE_OFFSET64(PartMap->BlockOffsHi, PartMap->BlockOffsLo); - if(RawByteOffset == 0) - { - nFailReason = ERROR_FILE_CORRUPT; - bResult = false; - break; - } - - // If the number of bytes in part is too big, cut it - if(dwBytesInPart > dwBytesRemaining) - dwBytesInPart = dwBytesRemaining; - - // Append the offset within the part - RawByteOffset += dwPartOffset; - if(!pStream->BaseRead(pStream, &RawByteOffset, pbBuffer, dwBytesInPart)) - { - nFailReason = ERROR_FILE_CORRUPT; - bResult = false; - break; - } - - // Increment the file position - dwBytesRemaining -= dwBytesInPart; - dwBytesRead += dwBytesInPart; - pbBuffer += dwBytesInPart; - - // Move to the next file part - dwPartOffset = 0; - dwPartIndex++; - } - - // Move the file position by the number of bytes read - pStream->VirtualPos = *pByteOffset + dwBytesRead; - if(dwBytesRead != dwBytesToRead) - SetLastError(nFailReason); - return (dwBytesRead == dwBytesToRead); + // Yes, the file is complete + return 1; } -static bool PartialStream_GetPos( - TPartialStream * pStream, - ULONGLONG & ByteOffset) -{ - ByteOffset = pStream->VirtualPos; - return true; -} - -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 PartialStream_Open(TPartialStream * pStream) +static bool PartStream_LoadBitmap(TBlockStream * pStream) { + PPART_FILE_MAP_ENTRY FileBitmap; PART_FILE_HEADER PartHdr; - ULONGLONG VirtualSize; // Size of the file stored in part file - ULONGLONG ByteOffset = {0}; + ULONGLONG ByteOffset = 0; + ULONGLONG StreamSize = 0; DWORD BlockCount; + DWORD BitmapSize; - // Sanity check - assert(pStream->BaseRead != NULL); - - // Attempt to read PART file header - if(pStream->BaseRead(pStream, &ByteOffset, &PartHdr, sizeof(PART_FILE_HEADER))) + // Only if the size is greater than size of the bitmap header + if(pStream->Base.File.FileSize > 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)) + // Attempt to read PART file header + if(pStream->BaseRead(pStream, &ByteOffset, &PartHdr, sizeof(PART_FILE_HEADER))) { - // 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); + // We need to swap PART file header on big-endian platforms + BSWAP_ARRAY32_UNSIGNED(&PartHdr, sizeof(PART_FILE_HEADER)); - // Allocate the map entry array - pStream->PartMap = STORM_ALLOC(PART_FILE_MAP_ENTRY, BlockCount); - if(pStream->PartMap != NULL) + // Verify the PART file header + if(IsPartHeader(&PartHdr)) { - // Load the block map - if(pStream->BaseRead(pStream, NULL, pStream->PartMap, BlockCount * sizeof(PART_FILE_MAP_ENTRY))) + // Get the number of blocks and size of one block + StreamSize = MAKE_OFFSET64(PartHdr.FileSizeHi, PartHdr.FileSizeLo); + ByteOffset = sizeof(PART_FILE_HEADER); + BlockCount = (DWORD)((StreamSize + PartHdr.BlockSize - 1) / PartHdr.BlockSize); + BitmapSize = BlockCount * sizeof(PART_FILE_MAP_ENTRY); + + // Check if sizes match + if((ByteOffset + BitmapSize) < pStream->Base.File.FileSize) { - // Swap the array of file map entries - BSWAP_ARRAY32_UNSIGNED(pStream->PartMap, BlockCount * sizeof(PART_FILE_MAP_ENTRY)); + // Allocate space for the array of PART_FILE_MAP_ENTRY + FileBitmap = STORM_ALLOC(PART_FILE_MAP_ENTRY, BlockCount); + if(FileBitmap != NULL) + { + // Load the block map + if(!pStream->BaseRead(pStream, &ByteOffset, FileBitmap, BitmapSize)) + { + STORM_FREE(FileBitmap); + return false; + } - // Fill the members of PART file stream - pStream->VirtualSize = ((ULONGLONG)PartHdr.FileSizeHi) + PartHdr.FileSizeLo; - pStream->VirtualPos = 0; - pStream->BlockCount = BlockCount; - pStream->BlockSize = PartHdr.BlockSize; + // Make sure that the byte order is correct + BSWAP_ARRAY32_UNSIGNED(FileBitmap, BitmapSize); - // 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; + // Update the stream size + pStream->BuildNumber = StringToInt(PartHdr.GameBuildNumber); + pStream->StreamSize = StreamSize; + + // Fill the bitmap information + pStream->FileBitmap = FileBitmap; + pStream->BitmapSize = BitmapSize; + pStream->BlockSize = PartHdr.BlockSize; + pStream->BlockCount = BlockCount; + pStream->IsComplete = PartStream_CheckFile(pStream); + return true; + } } - - // Free the part map - STORM_FREE(pStream->PartMap); - pStream->PartMap = NULL; } } } - SetLastError(ERROR_BAD_FORMAT); return false; } -//----------------------------------------------------------------------------- -// Local functions - encrypted stream support - -// Note: Starcraft II - Installer.exe (4.1.1.4219): 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 (follows in comment) -// Pointer to key: ECX+0x5C -0C- -1C--08- -18--04- -14--00- -10- -static const char * MpqeKey_Starcraft2_Install_deDE = "expand 32-byte kSSXH00004XFXK4KX00008EKJD3CA0000Y64ZY45M0000YD9V"; // Y45MD3CAK4KXSSXHYD9VY64Z8EKJ4XFX -static const char * MpqeKey_Starcraft2_Install_enGB = "expand 32-byte kANGY000029ZH6NA20000HRGF8UDG0000NY82G8MN00006A3D"; // G8MN8UDG6NA2ANGY6A3DNY82HRGF29ZH -static const char * MpqeKey_Starcraft2_Install_enSG = "expand 32-byte kWW5B0000F7HWFDU90000FWZSHLB20000BLRSW9RR00003ECE"; // W9RRHLB2FDU9WW5B3ECEBLRSFWZSF7HW -static const char * MpqeKey_Starcraft2_Install_enUS = "expand 32-byte kTFD80000ETR5VM5G0000K859RE5N0000WT6F3DH500005LXG"; // 3DH5RE5NVM5GTFD85LXGWT6FK859ETR5 -static const char * MpqeKey_Starcraft2_Install_esES = "expand 32-byte kQU4Y0000XKTQ94PF0000N4R4UAXE0000AZ248WLK0000249P"; // 8WLKUAXE94PFQU4Y249PAZ24N4R4XKTQ -static const char * MpqeKey_Starcraft2_Install_esMX = "expand 32-byte kSQBR00004G54HGGX0000MF9GXX3V0000FFDXA34D0000FE5U"; // A34DXX3VHGGXSQBRFE5UFFDXMF9G4G54 -static const char * MpqeKey_Starcraft2_Install_frFR = "expand 32-byte kFWPQ00006EAJ8HJE0000PFER9K9300008MA2ZG7J0000UA76"; // ZG7J9K938HJEFWPQUA768MA2PFER6EAJ -static const char * MpqeKey_Starcraft2_Install_itIT = "expand 32-byte kXV7E00008BL2TVAP0000GVMWUNNN0000SVBWNE7C00003G2B"; // NE7CUNNNTVAPXV7E3G2BSVBWGVMW8BL2 -static const char * MpqeKey_Starcraft2_Install_koKR = "expand 32-byte kQWK70000838FBM9Q0000WQDB2FTM0000MWAZ3V9E0000U6MA"; // 3V9E2FTMBM9QQWK7U6MAMWAZWQDB838F -static const char * MpqeKey_Starcraft2_Install_plPL = "expand 32-byte k83U6000048L6LULJ00004MQDB8ME0000UP6K2NSF0000YHA3"; // 2NSFB8MELULJ83U6YHA3UP6K4MQD48L6 -static const char * MpqeKey_Starcraft2_Install_ptBR = "expand 32-byte kU8BM0000SW4EZ4CU00005F9CZ9EW0000CTY6QA2T0000B5WX"; // QA2TZ9EWZ4CUU8BMB5WXCTY65F9CSW4E -static const char * MpqeKey_Starcraft2_Install_ruRU = "expand 32-byte k9SH70000YEGT4BAT0000QDK978W60000V9NLVHB30000D68V"; // VHB378W64BAT9SH7D68VV9NLQDK9YEGT -static const char * MpqeKey_Starcraft2_Install_zhTW = "expand 32-byte k7KBN0000D9NEM6GC0000N3PLQJV400003BRDU3NF00009XQJ"; // U3NFQJV4M6GC7KBN9XQJ3BRDN3PLD9NE - -// Note: Diablo III: Agent.exe (1.0.0.954): Suffix derived from battle.net auth. code -// Address of decryption routine: 00502b00 http://dist.blizzard.com/mediakey/d3-authenticationcode-enGB.txt -// Pointer to decryptor object: ECX Numbers mean offset of 4-char group of auth code (follows in comment) -// Pointer to key: ECX+0x5C -0C- -1C--08- -18--04- -14--00- -10- -static const char * MpqeKey_Diablo3_Install_deDE = "expand 32-byte kEFH40000QRZKY3520000XC9MF6EJ0000CFH2UCMX0000XFRX"; // UCMXF6EJY352EFH4XFRXCFH2XC9MQRZK -static const char * MpqeKey_Diablo3_Install_enGB = "expand 32-byte kXP4G0000PHBPRP7W0000J9UNHY4800007SL9MMKV0000HYBQ"; // MMKVHY48RP7WXP4GHYBQ7SL9J9UNPHBP -static const char * MpqeKey_Diablo3_Install_enSG = "expand 32-byte kTZ9M00003CPPVGGL0000JYETWHQ70000FDCL8MXL0000QZQS"; // 8MXLWHQ7VGGLTZ9MQZQSFDCLJYET3CPP -static const char * MpqeKey_Diablo3_Install_enUS = "expand 32-byte kGUNG0000WZSZXFE20000UAKP5TM60000HKQ9EJ2R00005QDG"; // EJ2R5TM6XFE2GUNG5QDGHKQ9UAKPWZSZ -static const char * MpqeKey_Diablo3_Install_esES = "expand 32-byte kK65U0000HQQTZ6LN0000CLP4BE420000WZVMPBGF0000GJQ3"; // PBGFBE42Z6LNK65UGJQ3WZVMCLP4HQQT -static const char * MpqeKey_Diablo3_Install_esMX = "expand 32-byte kW5P200008VU2TSGC0000JPEYJJS90000C47AX7SE00008EBS"; // X7SEJJS9TSGCW5P28EBSC47AJPEY8VU2 -static const char * MpqeKey_Diablo3_Install_frFR = "expand 32-byte kRY3D00007YA2YE6X0000XS4PQA8V0000ZDE45KVB0000LGC5"; // 5KVBQA8VYE6XRY3DLGC5ZDE4XS4P7YA2 -static const char * MpqeKey_Diablo3_Install_itIT = "expand 32-byte kVVY40000B2546EVN00005B8KD2K50000DWYT478J0000XX8T"; // 478JD2K56EVNVVY4XX8TDWYT5B8KB254 -static const char * MpqeKey_Diablo3_Install_koKR = "expand 32-byte k6YWH0000474ARZTN0000NVWDVNFQ0000VDH98TS40000E9CH"; // 8TS4VNFQRZTN6YWHE9CHVDH9NVWD474A -static const char * MpqeKey_Diablo3_Install_plPL = "expand 32-byte k4ZJJ0000BLJBF4LZ0000A6GAZ32D00003AZQLJ520000XVKK"; // LJ52Z32DF4LZ4ZJJXVKK3AZQA6GABLJB -static const char * MpqeKey_Diablo3_Install_ptBR = "expand 32-byte k545Y0000XYAGCUE20000WHE7HY2E0000JPVYK6BD0000KNLB"; // K6BDHY2ECUE2545YKNLBJPVYWHE7XYAG -static const char * MpqeKey_Diablo3_Install_ruRU = "expand 32-byte kXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; // -static const char * MpqeKey_Diablo3_Install_zhTW = "expand 32-byte kMRUC0000AA8HV3ZZ0000UX2TQTN80000A8CG6VWC0000ZXV8"; // 6VWCQTN8V3ZZMRUCZXV8A8CGUX2TAA8H -static const char * MpqeKey_Diablo3_Install_zhCN = "expand 32-byte kXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; // - -static const char * MpqKeyArray[] = +static void PartStream_UpdateBitmap( + TBlockStream * pStream, // Pointer to an open stream + ULONGLONG StartOffset, + ULONGLONG EndOffset, + ULONGLONG RealOffset) { - MpqeKey_Starcraft2_Install_deDE, - MpqeKey_Starcraft2_Install_enGB, - MpqeKey_Starcraft2_Install_enSG, - MpqeKey_Starcraft2_Install_enUS, - MpqeKey_Starcraft2_Install_esES, - MpqeKey_Starcraft2_Install_esMX, - MpqeKey_Starcraft2_Install_frFR, - MpqeKey_Starcraft2_Install_itIT, - MpqeKey_Starcraft2_Install_koKR, - MpqeKey_Starcraft2_Install_plPL, - MpqeKey_Starcraft2_Install_ptBR, - MpqeKey_Starcraft2_Install_ruRU, - MpqeKey_Starcraft2_Install_zhTW, + PPART_FILE_MAP_ENTRY FileBitmap; + DWORD BlockSize = pStream->BlockSize; - MpqeKey_Diablo3_Install_deDE, - MpqeKey_Diablo3_Install_enGB, - MpqeKey_Diablo3_Install_enSG, - MpqeKey_Diablo3_Install_enUS, - MpqeKey_Diablo3_Install_esES, - MpqeKey_Diablo3_Install_esMX, - MpqeKey_Diablo3_Install_frFR, - MpqeKey_Diablo3_Install_itIT, - MpqeKey_Diablo3_Install_koKR, - MpqeKey_Diablo3_Install_plPL, - MpqeKey_Diablo3_Install_ptBR, -// MpqeKey_Diablo3_Install_ruRU, - MpqeKey_Diablo3_Install_zhTW, -// MpqeKey_Diablo3_Install_zhCN, + // Sanity checks + assert((StartOffset & (BlockSize - 1)) == 0); + assert(pStream->FileBitmap != NULL); + + // Calculate the first entry in the block map + FileBitmap = (PPART_FILE_MAP_ENTRY)pStream->FileBitmap + (StartOffset / BlockSize); + + // Set all bits for the specified range + while(StartOffset < EndOffset) + { + // Set the bit + FileBitmap->BlockOffsHi = (DWORD)(RealOffset >> 0x20); + FileBitmap->BlockOffsLo = (DWORD)(RealOffset & 0xFFFFFFFF); + FileBitmap->Flags = 3; + + // Move all + StartOffset += BlockSize; + RealOffset += BlockSize; + FileBitmap++; + } + + // Increment the bitmap update count + pStream->IsModified = 1; +} + +static bool PartStream_BlockCheck( + TBlockStream * pStream, // Pointer to an open stream + ULONGLONG BlockOffset) +{ + PPART_FILE_MAP_ENTRY FileBitmap; + + // Sanity checks + assert((BlockOffset & (pStream->BlockSize - 1)) == 0); + assert(pStream->FileBitmap != NULL); + + // Calculate the block map entry + FileBitmap = (PPART_FILE_MAP_ENTRY)pStream->FileBitmap + (BlockOffset / pStream->BlockSize); + + // Check if the flags are present + return (FileBitmap->Flags & 0x03) ? true : false; +} + +static bool PartStream_BlockRead( + TBlockStream * pStream, + ULONGLONG StartOffset, + ULONGLONG EndOffset, + LPBYTE BlockBuffer, + DWORD BytesNeeded, + bool bAvailable) +{ + PPART_FILE_MAP_ENTRY FileBitmap; + ULONGLONG ByteOffset; + DWORD BytesToRead; + DWORD BlockIndex = (DWORD)(StartOffset / pStream->BlockSize); + + // The starting offset must be aligned to size of the block + assert(pStream->FileBitmap != NULL); + assert((StartOffset & (pStream->BlockSize - 1)) == 0); + assert(StartOffset < EndOffset); + + // If the blocks are not available, we need to load them from the master + // and then save to the mirror + if(bAvailable == false) + { + // If we have no master, we cannot satisfy read request + if(pStream->pMaster == NULL) + return false; + + // Load the blocks from the master stream + // Note that we always have to read complete blocks + // so they get properly stored to the mirror stream + BytesToRead = (DWORD)(EndOffset - StartOffset); + if(!FileStream_Read(pStream->pMaster, &StartOffset, BlockBuffer, BytesToRead)) + return false; + + // The loaded blocks are going to be stored to the end of the file + // Note that this operation is not required to succeed + if(pStream->BaseGetSize(pStream, &ByteOffset)) + { + // Store the loaded blocks to the mirror file. + if(pStream->BaseWrite(pStream, &ByteOffset, BlockBuffer, BytesToRead)) + { + PartStream_UpdateBitmap(pStream, StartOffset, EndOffset, ByteOffset); + } + } + } + else + { + // Get the file map entry + FileBitmap = (PPART_FILE_MAP_ENTRY)pStream->FileBitmap + BlockIndex; + + // Read all blocks + while(StartOffset < EndOffset) + { + // Get the number of bytes to be read + BytesToRead = (DWORD)(EndOffset - StartOffset); + if(BytesToRead > pStream->BlockSize) + BytesToRead = pStream->BlockSize; + if(BytesToRead > BytesNeeded) + BytesToRead = BytesNeeded; + + // Read the block + ByteOffset = MAKE_OFFSET64(FileBitmap->BlockOffsHi, FileBitmap->BlockOffsLo); + if(!pStream->BaseRead(pStream, &ByteOffset, BlockBuffer, BytesToRead)) + return false; + + // Move the pointers + StartOffset += pStream->BlockSize; + BlockBuffer += pStream->BlockSize; + BytesNeeded -= pStream->BlockSize; + FileBitmap++; + } + } + + return true; +} + +static void PartStream_Close(TBlockStream * pStream) +{ + PART_FILE_HEADER PartHeader; + ULONGLONG ByteOffset = 0; + + if(pStream->FileBitmap && pStream->IsModified) + { + // Prepare the part file header + memset(&PartHeader, 0, sizeof(PART_FILE_HEADER)); + PartHeader.PartialVersion = 2; + PartHeader.FileSizeHi = (DWORD)(pStream->StreamSize >> 0x20); + PartHeader.FileSizeLo = (DWORD)(pStream->StreamSize & 0xFFFFFFFF); + PartHeader.BlockSize = pStream->BlockSize; + + // Make sure that the header is properly BSWAPed + BSWAP_ARRAY32_UNSIGNED(&PartHeader, sizeof(PART_FILE_HEADER)); + SMemIntToStr(PartHeader.GameBuildNumber, _countof(PartHeader.GameBuildNumber), pStream->BuildNumber); + + // Write the part header + pStream->BaseWrite(pStream, &ByteOffset, &PartHeader, sizeof(PART_FILE_HEADER)); + + // Write the block bitmap + BSWAP_ARRAY32_UNSIGNED(pStream->FileBitmap, pStream->BitmapSize); + pStream->BaseWrite(pStream, NULL, pStream->FileBitmap, pStream->BitmapSize); + } + + // Close the base class + BlockStream_Close(pStream); +} + +static bool PartStream_CreateMirror(TBlockStream * pStream) +{ + ULONGLONG RemainingSize; + ULONGLONG MasterSize = 0; + ULONGLONG MirrorSize = 0; + LPBYTE FileBitmap = NULL; + DWORD dwBitmapSize; + DWORD dwBlockCount; + bool bNeedCreateMirrorStream = true; + bool bNeedResizeMirrorStream = true; + + // Do we have master function and base creation function? + if(pStream->pMaster == NULL || pStream->BaseCreate == NULL) + return false; + + // Retrieve the master file size, block count and bitmap size + FileStream_GetSize(pStream->pMaster, &MasterSize); + dwBlockCount = (DWORD)((MasterSize + DEFAULT_BLOCK_SIZE - 1) / DEFAULT_BLOCK_SIZE); + dwBitmapSize = (DWORD)(dwBlockCount * sizeof(PART_FILE_MAP_ENTRY)); + + // Setup stream size and position + pStream->BuildNumber = DEFAULT_BUILD_NUMBER; // BUGBUG: Really??? + pStream->StreamSize = MasterSize; + pStream->StreamPos = 0; + + // Open the base stream for write access + if(pStream->BaseOpen(pStream, pStream->szFileName, 0)) + { + // If the file open succeeded, check if the file size matches required size + pStream->BaseGetSize(pStream, &MirrorSize); + if(MirrorSize >= sizeof(PART_FILE_HEADER) + dwBitmapSize) + { + // Check if the remaining size is aligned to block + RemainingSize = MirrorSize - sizeof(PART_FILE_HEADER) - dwBitmapSize; + if((RemainingSize & (DEFAULT_BLOCK_SIZE - 1)) == 0 || RemainingSize == MasterSize) + { + // Attempt to load an existing file bitmap + if(PartStream_LoadBitmap(pStream)) + return true; + } + } + + // We need to create mirror stream + bNeedCreateMirrorStream = false; + } + + // Create a new stream, if needed + if(bNeedCreateMirrorStream) + { + if(!pStream->BaseCreate(pStream)) + return false; + } + + // If we need to, then resize the mirror stream + if(bNeedResizeMirrorStream) + { + if(!pStream->BaseResize(pStream, sizeof(PART_FILE_HEADER) + dwBitmapSize)) + return false; + } + + // Allocate the bitmap array + FileBitmap = STORM_ALLOC(BYTE, dwBitmapSize); + if(FileBitmap == NULL) + return false; + + // Initialize the bitmap + memset(FileBitmap, 0, dwBitmapSize); + pStream->FileBitmap = FileBitmap; + pStream->BitmapSize = dwBitmapSize; + pStream->BlockSize = DEFAULT_BLOCK_SIZE; + pStream->BlockCount = dwBlockCount; + pStream->IsComplete = 0; + pStream->IsModified = 1; + + // Note: Don't write the stream bitmap right away. + // Doing so would cause sparse file resize on NTFS, + // which would take long time on larger files. + return true; +} + + +static TFileStream * PartStream_Open(const TCHAR * szFileName, DWORD dwStreamFlags) +{ + TBlockStream * pStream; + + // Create new empty stream + pStream = (TBlockStream *)AllocateFileStream(szFileName, sizeof(TBlockStream), dwStreamFlags); + if(pStream == NULL) + return NULL; + + // Do we have a master stream? + if(pStream->pMaster != NULL) + { + if(!PartStream_CreateMirror(pStream)) + { + FileStream_Close(pStream); + SErrSetLastError(ERROR_FILE_NOT_FOUND); + return NULL; + } + } + else + { + // Attempt to open the base stream + if(!pStream->BaseOpen(pStream, pStream->szFileName, dwStreamFlags)) + { + FileStream_Close(pStream); + return NULL; + } + + // Load the part stream block map + if(!PartStream_LoadBitmap(pStream)) + { + FileStream_Close(pStream); + SErrSetLastError(ERROR_BAD_FORMAT); + return NULL; + } + } + + // Set the stream position to zero. Stream size is already set + assert(pStream->StreamSize != 0); + pStream->StreamPos = 0; + pStream->dwFlags |= STREAM_FLAG_READ_ONLY; + + // Set new function pointers + pStream->StreamRead = (STREAM_READ)BlockStream_Read; + pStream->StreamGetPos = BlockStream_GetPos; + pStream->StreamGetSize = BlockStream_GetSize; + pStream->StreamClose = (STREAM_CLOSE)PartStream_Close; + + // Supply the block functions + pStream->BlockCheck = (BLOCK_CHECK)PartStream_BlockCheck; + pStream->BlockRead = (BLOCK_READ)PartStream_BlockRead; + return pStream; +} + +//----------------------------------------------------------------------------- +// Local functions - MPQE stream support + +static const char * szKeyTemplate = "expand 32-byte k000000000000000000000000000000000000000000000000"; + +static const char * AuthCodeArray[] = +{ + // Starcraft II (Heart of the Swarm) + // Authentication code URL: http://dist.blizzard.com/mediakey/hots-authenticationcode-bgdl.txt + // -0C- -1C--08- -18--04- -14--00- -10- + "S48B6CDTN5XEQAKQDJNDLJBJ73FDFM3U", // SC2 Heart of the Swarm-all : "expand 32-byte kQAKQ0000FM3UN5XE000073FD6CDT0000LJBJS48B0000DJND" + + // 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" + + // 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 }; @@ -1468,6 +1958,25 @@ 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, @@ -1505,7 +2014,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) { @@ -1581,131 +2090,315 @@ static void DecryptFileChunk( } } -static const char * DetectFileKey(LPBYTE pbEncryptedHeader) -{ - ULONGLONG ByteOffset = 0; - BYTE FileHeader[MPQE_CHUNK_SIZE]; - BYTE Key[MPQE_CHUNK_SIZE]; - - // We just try all known keys one by one - for(int i = 0; MpqKeyArray[i] != NULL; i++) - { - // Copy the key there - memcpy(Key, MpqKeyArray[i], MPQE_CHUNK_SIZE); - BSWAP_ARRAY32_UNSIGNED(Key, MPQE_CHUNK_SIZE); - - // Try to decrypt with the given key - memcpy(FileHeader, pbEncryptedHeader, MPQE_CHUNK_SIZE); - DecryptFileChunk((LPDWORD)FileHeader, Key, ByteOffset, MPQE_CHUNK_SIZE); - - // 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') - return MpqKeyArray[i]; - } - - // Key not found, sorry - return NULL; -} - -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 - ULONGLONG EndOffset; // End offset that is to be read from the file - DWORD dwBytesToAllocate; - DWORD dwBytesToDecrypt; - DWORD dwOffsetInCache; - LPBYTE pbMpqData = NULL; - bool bResult = false; - - // Get the byte offset - if(pByteOffset == NULL) - pStream->BaseGetPos(pStream, ByteOffset); - else - ByteOffset = *pByteOffset; - - // Cut it down to MPQE chunk size - StartOffset = ByteOffset; - StartOffset = StartOffset & ~(MPQE_CHUNK_SIZE - 1); - EndOffset = ByteOffset + dwBytesToRead; - - // Calculate number of bytes to decrypt - dwBytesToDecrypt = (DWORD)(EndOffset - StartOffset); - dwBytesToAllocate = (dwBytesToDecrypt + (MPQE_CHUNK_SIZE - 1)) & ~(MPQE_CHUNK_SIZE - 1); - - // Allocate buffers for encrypted and decrypted data - 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(pStream->BaseRead(pStream, &StartOffset, pbMpqData, dwBytesToDecrypt)) - { - // Decrypt the data - DecryptFileChunk((LPDWORD)pbMpqData, pStream->Key, StartOffset, dwBytesToAllocate); - - // Copy the decrypted data - memcpy(pvBuffer, pbMpqData + dwOffsetInCache, dwBytesToRead); - bResult = true; - } - else - { - assert(false); - } - - // Free decryption buffer - STORM_FREE(pbMpqData); - } - - // Free buffers and exit - return bResult; -} - -static bool EncryptedStream_Open(TEncryptedStream * pStream) +static bool MpqeStream_DetectFileKey(TEncryptedStream * pStream) { ULONGLONG ByteOffset = 0; BYTE EncryptedHeader[MPQE_CHUNK_SIZE]; - const char * szKey; + BYTE FileHeader[MPQE_CHUNK_SIZE]; - // Sanity check - assert(pStream->BaseRead != NULL); - - // Load one MPQE chunk and try to detect the file key + // Read the first file chunk if(pStream->BaseRead(pStream, &ByteOffset, EncryptedHeader, sizeof(EncryptedHeader))) { - // Attempt to decrypt the MPQ header with all known keys - szKey = DetectFileKey(EncryptedHeader); - if(szKey != NULL) + // We just try all known keys one by one + for(int i = 0; AuthCodeArray[i] != NULL; i++) { - // Copy the key for the file - memcpy(pStream->Key, szKey, MPQE_CHUNK_SIZE); - BSWAP_ARRAY32_UNSIGNED(pStream->Key, MPQE_CHUNK_SIZE); + // Prepare they decryption key from game serial number + CreateKeyFromAuthCode(pStream->Key, AuthCodeArray[i]); - // 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; + // Try to decrypt with the given key + memcpy(FileHeader, EncryptedHeader, MPQE_CHUNK_SIZE); + DecryptFileChunk((LPDWORD)FileHeader, pStream->Key, ByteOffset, MPQE_CHUNK_SIZE); - // We need to reset the position back to the begin of the file - pStream->BaseRead(pStream, &ByteOffset, EncryptedHeader, 0); - return true; + // 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') + { + // Update the stream size + pStream->StreamSize = pStream->Base.File.FileSize; + + // Fill the block information + pStream->BlockSize = MPQE_CHUNK_SIZE; + pStream->BlockCount = (DWORD)(pStream->Base.File.FileSize + MPQE_CHUNK_SIZE - 1) / MPQE_CHUNK_SIZE; + pStream->IsComplete = 1; + return true; + } + } + } + + // Key not found, sorry + return false; +} + +static bool MpqeStream_BlockRead( + TEncryptedStream * pStream, + ULONGLONG StartOffset, + ULONGLONG EndOffset, + LPBYTE BlockBuffer, + DWORD BytesNeeded, + bool bAvailable) +{ + DWORD dwBytesToRead; + + assert((StartOffset & (pStream->BlockSize - 1)) == 0); + assert(StartOffset < EndOffset); + assert(bAvailable != false); + BytesNeeded = BytesNeeded; + bAvailable = bAvailable; + + // Read the file from the stream as-is + // Limit the reading to number of blocks really needed + dwBytesToRead = (DWORD)(EndOffset - StartOffset); + if(!pStream->BaseRead(pStream, &StartOffset, BlockBuffer, dwBytesToRead)) + return false; + + // Decrypt the data + dwBytesToRead = (dwBytesToRead + MPQE_CHUNK_SIZE - 1) & ~(MPQE_CHUNK_SIZE - 1); + DecryptFileChunk((LPDWORD)BlockBuffer, pStream->Key, StartOffset, dwBytesToRead); + return true; +} + +static TFileStream * MpqeStream_Open(const TCHAR * szFileName, DWORD dwStreamFlags) +{ + TEncryptedStream * pStream; + + // Create new empty stream + pStream = (TEncryptedStream *)AllocateFileStream(szFileName, sizeof(TEncryptedStream), dwStreamFlags); + if(pStream == NULL) + return NULL; + + // Attempt to open the base stream + assert(pStream->BaseOpen != NULL); + if(!pStream->BaseOpen(pStream, pStream->szFileName, dwStreamFlags)) + return NULL; + + // Determine the encryption key for the MPQ + if(MpqeStream_DetectFileKey(pStream)) + { + // Set the stream position and size + assert(pStream->StreamSize != 0); + pStream->StreamPos = 0; + pStream->dwFlags |= STREAM_FLAG_READ_ONLY; + + // Set new function pointers + pStream->StreamRead = (STREAM_READ)BlockStream_Read; + pStream->StreamGetPos = BlockStream_GetPos; + pStream->StreamGetSize = BlockStream_GetSize; + pStream->StreamClose = pStream->BaseClose; + + // Supply the block functions + pStream->BlockRead = (BLOCK_READ)MpqeStream_BlockRead; + return pStream; + } + + // Cleanup the stream and return + FileStream_Close(pStream); + SErrSetLastError(ERROR_UNKNOWN_FILE_KEY); + return NULL; +} + +//----------------------------------------------------------------------------- +// Local functions - Block4 stream support + +#define BLOCK4_BLOCK_SIZE 0x4000 // Size of one block +#define BLOCK4_HASH_SIZE 0x20 // Size of MD5 hash that is after each block +#define BLOCK4_MAX_BLOCKS 0x00002000 // Maximum amount of blocks per file +#define BLOCK4_MAX_FSIZE 0x08040000 // Max size of one file + +static bool Block4Stream_BlockRead( + TBlockStream * pStream, // Pointer to an open stream + ULONGLONG StartOffset, + ULONGLONG EndOffset, + LPBYTE BlockBuffer, + DWORD BytesNeeded, + bool bAvailable) +{ + TBaseProviderData * BaseArray = (TBaseProviderData *)pStream->FileBitmap; + ULONGLONG ByteOffset; + DWORD BytesToRead; + DWORD StreamIndex; + DWORD BlockIndex; + bool bResult; + + // The starting offset must be aligned to size of the block + assert(pStream->FileBitmap != NULL); + assert((StartOffset & (pStream->BlockSize - 1)) == 0); + assert(StartOffset < EndOffset); + assert(bAvailable == true); + + // Keep compilers happy + STORMLIB_UNUSED(bAvailable); + STORMLIB_UNUSED(EndOffset); + + while(BytesNeeded != 0) + { + // Calculate the block index and the file index + StreamIndex = (DWORD)((StartOffset / pStream->BlockSize) / BLOCK4_MAX_BLOCKS); + BlockIndex = (DWORD)((StartOffset / pStream->BlockSize) % BLOCK4_MAX_BLOCKS); + if(StreamIndex > pStream->BitmapSize) + return false; + + // Calculate the block offset + ByteOffset = ((ULONGLONG)BlockIndex * (BLOCK4_BLOCK_SIZE + BLOCK4_HASH_SIZE)); + BytesToRead = STORMLIB_MIN(BytesNeeded, BLOCK4_BLOCK_SIZE); + + // Read from the base stream + pStream->Base = BaseArray[StreamIndex]; + bResult = pStream->BaseRead(pStream, &ByteOffset, BlockBuffer, BytesToRead); + BaseArray[StreamIndex] = pStream->Base; + + // Did the result succeed? + if(bResult == false) + return false; + + // Move pointers + StartOffset += BytesToRead; + BlockBuffer += BytesToRead; + BytesNeeded -= BytesToRead; + } + + return true; +} + + +static void Block4Stream_Close(TBlockStream * pStream) +{ + TBaseProviderData * BaseArray = (TBaseProviderData *)pStream->FileBitmap; + + // If we have a non-zero count of base streams, + // we have to close them all + if(BaseArray != NULL) + { + // Close all base streams + for(DWORD i = 0; i < pStream->BitmapSize; i++) + { + memcpy(&pStream->Base, BaseArray + i, sizeof(TBaseProviderData)); + pStream->BaseClose(pStream); + } + } + + // Free the data map, if any + if(pStream->FileBitmap != NULL) + STORM_FREE(pStream->FileBitmap); + pStream->FileBitmap = NULL; + + // Do not call the BaseClose function, + // we closed all handles already + return; +} + +static TFileStream * Block4Stream_Open(const TCHAR * szFileName, DWORD dwStreamFlags) +{ + TBaseProviderData * NewBaseArray = NULL; + ULONGLONG RemainderBlock; + ULONGLONG BlockCount; + ULONGLONG FileSize; + TBlockStream * pStream; + TCHAR * szNameBuff; + size_t nNameLength; + DWORD dwBaseFiles = 0; + DWORD dwBaseFlags; + + // Create new empty stream + pStream = (TBlockStream *)AllocateFileStream(szFileName, sizeof(TBlockStream), dwStreamFlags); + if(pStream == NULL) + return NULL; + + // Sanity check + assert(pStream->BaseOpen != NULL); + + // Get the length of the file name without numeric suffix + nNameLength = _tcslen(pStream->szFileName); + if(pStream->szFileName[nNameLength - 2] == '.' && pStream->szFileName[nNameLength - 1] == '0') + nNameLength -= 2; + pStream->szFileName[nNameLength] = 0; + + // Supply the stream functions + pStream->StreamRead = (STREAM_READ)BlockStream_Read; + pStream->StreamGetSize = BlockStream_GetSize; + pStream->StreamGetPos = BlockStream_GetPos; + pStream->StreamClose = (STREAM_CLOSE)Block4Stream_Close; + pStream->BlockRead = (BLOCK_READ)Block4Stream_BlockRead; + + // Allocate work space for numeric names + szNameBuff = STORM_ALLOC(TCHAR, nNameLength + 4); + if(szNameBuff != NULL) + { + // Set the base flags + dwBaseFlags = (dwStreamFlags & STREAM_PROVIDERS_MASK) | STREAM_FLAG_READ_ONLY; + + // Go all suffixes from 0 to 30 + for(int nSuffix = 0; nSuffix < 30; nSuffix++) + { + // Open the n-th file + CreateNameWithSuffix(szNameBuff, nNameLength + 4, pStream->szFileName, nSuffix); + if(!pStream->BaseOpen(pStream, szNameBuff, dwBaseFlags)) + break; + + // If the open succeeded, we re-allocate the base provider array + NewBaseArray = STORM_ALLOC(TBaseProviderData, dwBaseFiles + 1); + if(NewBaseArray == NULL) + { + SErrSetLastError(ERROR_NOT_ENOUGH_MEMORY); + return NULL; + } + + // Copy the old base data array to the new base data array + if(pStream->FileBitmap != NULL) + { + memcpy(NewBaseArray, pStream->FileBitmap, sizeof(TBaseProviderData) * dwBaseFiles); + STORM_FREE(pStream->FileBitmap); + } + + // Also copy the opened base array + memcpy(NewBaseArray + dwBaseFiles, &pStream->Base, sizeof(TBaseProviderData)); + pStream->FileBitmap = NewBaseArray; + dwBaseFiles++; + + // Get the size of the base stream + pStream->BaseGetSize(pStream, &FileSize); + assert(FileSize <= BLOCK4_MAX_FSIZE); + RemainderBlock = FileSize % (BLOCK4_BLOCK_SIZE + BLOCK4_HASH_SIZE); + BlockCount = FileSize / (BLOCK4_BLOCK_SIZE + BLOCK4_HASH_SIZE); + + // Increment the stream size and number of blocks + pStream->StreamSize += (BlockCount * BLOCK4_BLOCK_SIZE); + pStream->BlockCount += (DWORD)BlockCount; + + // Is this the last file? + if(FileSize < BLOCK4_MAX_FSIZE) + { + if(RemainderBlock) + { + pStream->StreamSize += (RemainderBlock - BLOCK4_HASH_SIZE); + pStream->BlockCount++; + } + break; + } } - // An unknown key - SetLastError(ERROR_UNKNOWN_FILE_KEY); + // Fill the remainining block stream variables + pStream->BitmapSize = dwBaseFiles; + pStream->BlockSize = BLOCK4_BLOCK_SIZE; + pStream->IsComplete = 1; + pStream->IsModified = 0; + + // Fill the remaining stream variables + pStream->StreamPos = 0; + pStream->dwFlags |= STREAM_FLAG_READ_ONLY; + + STORM_FREE(szNameBuff); } - return false; + + // If we opened something, return success + if(dwBaseFiles == 0) + { + FileStream_Close(pStream); + SErrSetLastError(ERROR_FILE_NOT_FOUND); + pStream = NULL; + } + + return pStream; } //----------------------------------------------------------------------------- @@ -1734,33 +2427,26 @@ TFileStream * FileStream_CreateFile( { TFileStream * pStream; - // We only support creation of linear, local file - if((dwStreamFlags & (STREAM_PROVIDER_MASK | BASE_PROVIDER_MASK)) != (STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE)) + // We only support creation of flat, local file + if((dwStreamFlags & (STREAM_PROVIDERS_MASK)) != (STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE)) { - SetLastError(ERROR_NOT_SUPPORTED); + SErrSetLastError(ERROR_NOT_SUPPORTED); return NULL; } - // Allocate file stream structure for linear stream - pStream = STORM_ALLOC(TFileStream, 1); + // Allocate file stream structure for flat stream + pStream = AllocateFileStream(szFileName, sizeof(TBlockStream), dwStreamFlags); 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)) + if(BaseFile_Create(pStream)) { // Fill the stream provider functions pStream->StreamRead = pStream->BaseRead; pStream->StreamWrite = pStream->BaseWrite; - pStream->StreamGetPos = pStream->BaseGetPos; + pStream->StreamResize = pStream->BaseResize; 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->StreamGetPos = pStream->BaseGetPos; pStream->StreamClose = pStream->BaseClose; return pStream; } @@ -1782,8 +2468,6 @@ TFileStream * FileStream_CreateFile( * - 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, @@ -1797,92 +2481,241 @@ TFileStream * FileStream_OpenFile( const TCHAR * szFileName, DWORD dwStreamFlags) { - TFileStream * pStream = NULL; - size_t StreamSize = 0; - bool bStreamResult = false; - bool bBaseResult = false; + DWORD dwProvider = dwStreamFlags & STREAM_PROVIDERS_MASK; + size_t nPrefixLength = FileStream_Prefix(szFileName, &dwProvider); - // Allocate file stream for each stream provider + // Re-assemble the stream flags + dwStreamFlags = (dwStreamFlags & STREAM_OPTIONS_MASK) | dwProvider; + szFileName += nPrefixLength; + + // Perform provider-specific open switch(dwStreamFlags & STREAM_PROVIDER_MASK) { - case STREAM_PROVIDER_LINEAR: // Allocate structure for linear stream - StreamSize = sizeof(TLinearStream); - break; + case STREAM_PROVIDER_FLAT: + return FlatStream_Open(szFileName, dwStreamFlags); case STREAM_PROVIDER_PARTIAL: - dwStreamFlags |= STREAM_FLAG_READ_ONLY; - StreamSize = sizeof(TPartialStream); - break; + return PartStream_Open(szFileName, dwStreamFlags); - case STREAM_PROVIDER_ENCRYPTED: - dwStreamFlags |= STREAM_FLAG_READ_ONLY; - StreamSize = sizeof(TEncryptedStream); - break; + case STREAM_PROVIDER_MPQE: + return MpqeStream_Open(szFileName, dwStreamFlags); + + case STREAM_PROVIDER_BLOCK4: + return Block4Stream_Open(szFileName, dwStreamFlags); default: + SErrSetLastError(ERROR_INVALID_PARAMETER); return NULL; } +} - // Allocate the stream for each type - pStream = (TFileStream *)STORM_ALLOC(BYTE, StreamSize); - if(pStream == NULL) - return NULL; +/** + * Returns the file name of the stream + * + * \a pStream Pointer to an open stream + */ +const TCHAR * FileStream_GetFileName(TFileStream * pStream) +{ + assert(pStream != NULL); + return pStream->szFileName; +} - // Fill the stream structure with zeros - memset(pStream, 0, StreamSize); - _tcscpy(pStream->szFileName, szFileName); +/** + * Returns the length of the provider prefix. Returns zero if no prefix + * + * \a szFileName Pointer to a stream name (file, mapped file, URL) + * \a pdwStreamProvider Pointer to a DWORD variable that receives stream provider (STREAM_PROVIDER_XXX) + */ - // Now initialize the respective base provider - switch(dwStreamFlags & BASE_PROVIDER_MASK) +size_t FileStream_Prefix(const TCHAR * szFileName, DWORD * pdwProvider) +{ + size_t nPrefixLength1 = 0; + size_t nPrefixLength2 = 0; + DWORD dwProvider = 0; + + if(szFileName != NULL) { - case BASE_PROVIDER_FILE: - bBaseResult = BaseFile_Open(pStream, szFileName, dwStreamFlags); - break; + // + // Determine the stream provider + // - case BASE_PROVIDER_MAP: - dwStreamFlags |= STREAM_FLAG_READ_ONLY; - bBaseResult = BaseMap_Open(pStream, szFileName, dwStreamFlags); - break; + if(!_tcsnicmp(szFileName, _T("flat-"), 5)) + { + dwProvider |= STREAM_PROVIDER_FLAT; + nPrefixLength1 = 5; + } - case BASE_PROVIDER_HTTP: - dwStreamFlags |= STREAM_FLAG_READ_ONLY; - bBaseResult = BaseHttp_Open(pStream, szFileName, dwStreamFlags); - break; + else if(!_tcsnicmp(szFileName, _T("part-"), 5)) + { + dwProvider |= STREAM_PROVIDER_PARTIAL; + nPrefixLength1 = 5; + } + + else if(!_tcsnicmp(szFileName, _T("mpqe-"), 5)) + { + dwProvider |= STREAM_PROVIDER_MPQE; + nPrefixLength1 = 5; + } + + else if(!_tcsnicmp(szFileName, _T("blk4-"), 5)) + { + dwProvider |= STREAM_PROVIDER_BLOCK4; + nPrefixLength1 = 5; + } + + // + // Determine the base provider + // + + if(!_tcsnicmp(szFileName+nPrefixLength1, _T("file:"), 5)) + { + dwProvider |= BASE_PROVIDER_FILE; + nPrefixLength2 = 5; + } + + else if(!_tcsnicmp(szFileName+nPrefixLength1, _T("map:"), 4)) + { + dwProvider |= BASE_PROVIDER_MAP; + nPrefixLength2 = 4; + } + + else if(!_tcsnicmp(szFileName+nPrefixLength1, _T("http:"), 5)) + { + dwProvider |= BASE_PROVIDER_HTTP; + nPrefixLength2 = 5; + } + + // Only accept stream provider if we recognized the base provider + if(nPrefixLength2 != 0) + { + // It is also allowed to put "//" after the base provider, e.g. "file://", "http://" + if(szFileName[nPrefixLength1+nPrefixLength2] == '/' && szFileName[nPrefixLength1+nPrefixLength2+1] == '/') + nPrefixLength2 += 2; + + if(pdwProvider != NULL) + *pdwProvider = dwProvider; + return nPrefixLength1 + nPrefixLength2; + } } - // If we failed to open the base storage, fail the operation - if(bBaseResult == false) + return 0; +} + +/** + * Sets a download callback. Whenever the stream needs to download one or more blocks + * from the server, the callback is called + * + * \a pStream Pointer to an open stream + * \a pfnCallback Pointer to callback function + * \a pvUserData Arbitrary user pointer passed to the download callback + */ + +bool FileStream_SetCallback(TFileStream * pStream, SFILE_DOWNLOAD_CALLBACK pfnCallback, void * pvUserData) +{ + TBlockStream * pBlockStream = (TBlockStream *)pStream; + + if(pStream->BlockRead == NULL) { - STORM_FREE(pStream); - return NULL; + SErrSetLastError(ERROR_NOT_SUPPORTED); + return false; } - // Now initialize the stream provider - switch(dwStreamFlags & STREAM_PROVIDER_MASK) + pBlockStream->pfnCallback = pfnCallback; + pBlockStream->UserData = pvUserData; + return true; +} + +/** + * This function gives the block map. The 'pvBitmap' pointer must point to a buffer + * of at least sizeof(STREAM_BLOCK_MAP) size. It can also have size of the complete + * block map (i.e. sizeof(STREAM_BLOCK_MAP) + BitmapSize). In that case, the function + * also copies the bit-based block map. + * + * \a pStream Pointer to an open stream + * \a pvBitmap Pointer to buffer where the block map will be stored + * \a cbBitmap Length of the buffer, of the block map + * \a cbLengthNeeded Length of the bitmap, in bytes + */ + +bool FileStream_GetBitmap(TFileStream * pStream, void * pvBitmap, DWORD cbBitmap, DWORD * pcbLengthNeeded) +{ + TStreamBitmap * pBitmap = (TStreamBitmap *)pvBitmap; + TBlockStream * pBlockStream = (TBlockStream *)pStream; + ULONGLONG BlockOffset; + LPBYTE Bitmap = (LPBYTE)(pBitmap + 1); + DWORD BitmapSize; + DWORD BlockCount; + DWORD BlockSize; + bool bResult = false; + + // Retrieve the size of one block + if(pStream->BlockCheck != NULL) { - case STREAM_PROVIDER_LINEAR: - bStreamResult = LinearStream_Open((TLinearStream *)pStream); - break; - - case STREAM_PROVIDER_PARTIAL: - bStreamResult = PartialStream_Open((TPartialStream *)pStream); - break; - - case STREAM_PROVIDER_ENCRYPTED: - bStreamResult = EncryptedStream_Open((TEncryptedStream *)pStream); - break; + BlockCount = pBlockStream->BlockCount; + BlockSize = pBlockStream->BlockSize; + } + else + { + BlockCount = (DWORD)((pStream->StreamSize + DEFAULT_BLOCK_SIZE - 1) / DEFAULT_BLOCK_SIZE); + BlockSize = DEFAULT_BLOCK_SIZE; } - // If the operation failed, free the stream and set it to NULL - if(bStreamResult == false) + // Fill-in the variables + BitmapSize = (BlockCount + 7) / 8; + + // Give the number of blocks + if(pcbLengthNeeded != NULL) + pcbLengthNeeded[0] = sizeof(TStreamBitmap) + BitmapSize; + + // If the length of the buffer is not enough + if(pvBitmap != NULL && cbBitmap != 0) { - // Only close the base stream - pStream->BaseClose(pStream); - STORM_FREE(pStream); - pStream = NULL; + // Give the STREAM_BLOCK_MAP structure + if(cbBitmap >= sizeof(TStreamBitmap)) + { + pBitmap->StreamSize = pStream->StreamSize; + pBitmap->BitmapSize = BitmapSize; + pBitmap->BlockCount = BlockCount; + pBitmap->BlockSize = BlockSize; + pBitmap->IsComplete = (pStream->BlockCheck != NULL) ? pBlockStream->IsComplete : 1; + bResult = true; + } + + // Give the block bitmap, if enough space + if(cbBitmap >= sizeof(TStreamBitmap) + BitmapSize) + { + // Version with bitmap present + if(pStream->BlockCheck != NULL) + { + DWORD ByteIndex = 0; + BYTE BitMask = 0x01; + + // Initialize the map with zeros + memset(Bitmap, 0, BitmapSize); + + // Fill the map + for(BlockOffset = 0; BlockOffset < pStream->StreamSize; BlockOffset += BlockSize) + { + // Set the bit if the block is present + if(pBlockStream->BlockCheck(pStream, BlockOffset)) + Bitmap[ByteIndex] |= BitMask; + + // Move bit position + ByteIndex += (BitMask >> 0x07); + BitMask = (BitMask >> 0x07) | (BitMask << 0x01); + } + } + else + { + memset(Bitmap, 0xFF, BitmapSize); + } + } } - return pStream; + // Set last error value and return + if(bResult == false) + SErrSetLastError(ERROR_INSUFFICIENT_BUFFER); + return bResult; } /** @@ -1901,8 +2734,8 @@ TFileStream * FileStream_OpenFile( * * \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 + * - If the function reads less than required bytes, it returns false and SErrGetLastError() returns ERROR_HANDLE_EOF + * - If the function fails, it reads false and SErrGetLastError() returns an error code different from ERROR_HANDLE_EOF */ bool FileStream_Read(TFileStream * pStream, ULONGLONG * pByteOffset, void * pvBuffer, DWORD dwBytesToRead) { @@ -1925,33 +2758,25 @@ bool FileStream_Read(TFileStream * pStream, ULONGLONG * pByteOffset, void * pvBu bool FileStream_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const void * pvBuffer, DWORD dwBytesToWrite) { if(pStream->dwFlags & STREAM_FLAG_READ_ONLY) + { + SErrSetLastError(ERROR_ACCESS_DENIED); return false; + } assert(pStream->StreamWrite != NULL); return pStream->StreamWrite(pStream, pByteOffset, pvBuffer, dwBytesToWrite); } -/** - * This function returns the current file position - * \a pStream - * \a ByteOffset - */ -bool FileStream_GetPos(TFileStream * pStream, ULONGLONG & ByteOffset) -{ - assert(pStream->StreamGetPos != NULL); - return pStream->StreamGetPos(pStream, ByteOffset); -} - /** * 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 & FileSize) +bool FileStream_GetSize(TFileStream * pStream, ULONGLONG * pFileSize) { assert(pStream->StreamGetSize != NULL); - return pStream->StreamGetSize(pStream, FileSize); + return pStream->StreamGetSize(pStream, pFileSize); } /** @@ -1961,12 +2786,26 @@ bool FileStream_GetSize(TFileStream * pStream, ULONGLONG & FileSize) * \a NewFileSize File size to set */ bool FileStream_SetSize(TFileStream * pStream, ULONGLONG NewFileSize) -{ +{ if(pStream->dwFlags & STREAM_FLAG_READ_ONLY) + { + SErrSetLastError(ERROR_ACCESS_DENIED); return false; + } - assert(pStream->StreamSetSize != NULL); - return pStream->StreamSetSize(pStream, NewFileSize); + assert(pStream->StreamResize != NULL); + return pStream->StreamResize(pStream, NewFileSize); +} + +/** + * This function returns the current file position + * \a pStream + * \a pByteOffset + */ +bool FileStream_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset) +{ + assert(pStream->StreamGetPos != NULL); + return pStream->StreamGetPos(pStream, pByteOffset); } /** @@ -1977,8 +2816,21 @@ bool FileStream_SetSize(TFileStream * pStream, ULONGLONG NewFileSize) */ bool FileStream_GetTime(TFileStream * pStream, ULONGLONG * pFileTime) { - assert(pStream->StreamGetTime != NULL); - return pStream->StreamGetTime(pStream, pFileTime); + // Just use the saved filetime value + *pFileTime = pStream->Base.File.FileTime; + return true; +} + +/** + * 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; } /** @@ -1990,87 +2842,41 @@ bool FileStream_GetTime(TFileStream * pStream, ULONGLONG * pFileTime) * 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) + * \a pNewStream Temporary ("working") stream (created during archive compacting) */ -bool FileStream_Switch(TFileStream * pStream, TFileStream * pNewStream) +bool FileStream_Replace(TFileStream * pStream, TFileStream * pNewStream) { + // Only supported on flat files + if((pStream->dwFlags & STREAM_PROVIDERS_MASK) != (STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE)) + { + SErrSetLastError(ERROR_NOT_SUPPORTED); + return false; + } + + // Not supported on read-only streams if(pStream->dwFlags & STREAM_FLAG_READ_ONLY) + { + SErrSetLastError(ERROR_ACCESS_DENIED); + return false; + } + + // Close both stream's base providers + pNewStream->BaseClose(pNewStream); + pStream->BaseClose(pStream); + + // Now we have to delete the (now closed) old file and rename the new file + if(!BaseFile_Replace(pStream, pNewStream)) return false; - assert(pStream->StreamSwitch != NULL); - return pStream->StreamSwitch(pStream, pNewStream); -} - -/** - * Returns the file name of the stream - * - * \a pStream Pointer to an open stream - */ -TCHAR * FileStream_GetFileName(TFileStream * pStream) -{ - assert(pStream != NULL); - return pStream->szFileName; -} - -/** - * Returns true if the stream is read-only - * - * \a pStream Pointer to an open stream - */ -bool FileStream_IsReadOnly(TFileStream * pStream) -{ - return (pStream->dwFlags & STREAM_FLAG_READ_ONLY) ? true : false; -} - -/** - * 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 - */ - -bool FileStream_SetBitmap(TFileStream * pStream, TFileBitmap * pBitmap) -{ - TLinearStream * pLinearStream; - - // It must be a linear stream. - if((pStream->dwFlags & STREAM_PROVIDER_MASK) != STREAM_PROVIDER_LINEAR) - return false; - pLinearStream = (TLinearStream *)pStream; - - // Two bitmaps are not allowed - if(pLinearStream->pBitmap != NULL) + // Now open the base file again + if(!BaseFile_Open(pStream, pStream->szFileName, pStream->dwFlags)) return false; - // We need to change some entry points - pLinearStream->StreamRead = (STREAM_READ)LinearStream_Read; - pLinearStream->StreamGetBmp = (STREAM_GETBMP)LinearStream_GetBitmap; - - // Using data bitmap renders the stream to be read only. - pLinearStream->dwFlags |= STREAM_FLAG_READ_ONLY; - pLinearStream->pBitmap = pBitmap; + // Cleanup the new stream + FileStream_Close(pNewStream); return true; } -/** - * 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); -} - /** * This function closes an archive file and frees any data buffers * that have been allocated for stream management. The function must also @@ -2084,211 +2890,20 @@ void FileStream_Close(TFileStream * pStream) // Check if the stream structure is allocated at all if(pStream != NULL) { - // Close the stream provider. - // This will also close the base stream - assert(pStream->StreamClose != NULL); - pStream->StreamClose(pStream); + // Free the master stream, if any + if(pStream->pMaster != NULL) + FileStream_Close(pStream->pMaster); + pStream->pMaster = NULL; + + // Close the stream provider + if(pStream->StreamClose != NULL) + pStream->StreamClose(pStream); + + // ... or close base stream, if any + else if(pStream->BaseClose != NULL) + pStream->BaseClose(pStream); // Free the stream itself STORM_FREE(pStream); } } - -//----------------------------------------------------------------------------- -// main - for testing purposes - -#ifdef __STORMLIB_TEST__ -int FileStream_Test(const TCHAR * szFileName, DWORD dwStreamFlags) -{ - TFileStream * pStream; - TMPQHeader MpqHeader; - ULONGLONG FilePos; - TMPQBlock * pBlock; - TMPQHash * pHash; - - InitializeMpqCryptography(); - - 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) - { - 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); - } - - // Read the block table - pBlock = STORM_ALLOC(TMPQBlock, MpqHeader.dwBlockTableSize); - if(pBlock != NULL) - { - 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(); - - // - // Test 1: Write to a stream - // - - pStream = FileStream_CreateFile("E:\\Stream.bin", 0); - if(pStream != NULL) - { - char szString1[100] = "This is a single line\n\r"; - DWORD dwLength = strlen(szString1); - - 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); - } - - // - // Test2: Read from the stream - // - - pStream = FileStream_OpenFile("E:\\Stream.bin", STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); - if(pStream != NULL) - { - 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("Write succeeded while it should fail\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; - } - - szString2[dwLength] = 0; - if(strcmp(szString1, szString2)) - { - printf("Data read from file are different from data written\n"); - return -1; - } - } - FileStream_Close(pStream); - } - - // - // Test3: Open the temp stream, write some data and switch it to the original stream - // - - pStream = FileStream_OpenFile("E:\\Stream.bin", STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); - if(pStream != NULL) - { - TFileStream * pTempStream; - ULONGLONG FileSize; - - pTempStream = FileStream_CreateFile("E:\\TempStream.bin", 0); - 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 != 0) - { - DWORD dwBytesToRead = (DWORD)FileSize; - char Buffer[0x80]; - - if(dwBytesToRead > sizeof(Buffer)) - dwBytesToRead = sizeof(Buffer); - - if(!FileStream_Read(pStream, NULL, Buffer, dwBytesToRead)) - { - printf("CopyStream: Read source file failed\n"); - return -1; - } - - if(!FileStream_Write(pTempStream, NULL, Buffer, dwBytesToRead)) - { - printf("CopyStream: Write target file failed\n"); - return -1; - } - - FileSize -= dwBytesToRead; - } - - // Switch the streams - // Note that the pTempStream is closed by the operation - FileStream_Switch(pStream, pTempStream); - FileStream_Close(pStream); - } - - // - // Test4: Read from the stream again - // - - pStream = FileStream_OpenFile("E:\\Stream.bin", STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); - if(pStream != NULL) - { - char szString1[100] = "This is a single line\n\r"; - char szString2[100]; - DWORD dwLength = strlen(szString1); - - 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 index 31fe757514..2bc118b28e 100644 --- a/dep/StormLib/src/FileStream.h +++ b/dep/StormLib/src/FileStream.h @@ -14,6 +14,20 @@ //----------------------------------------------------------------------------- // Function prototypes +typedef void (*STREAM_INIT)( + struct TFileStream * pStream // Pointer to an unopened stream +); + +typedef bool (*STREAM_CREATE)( + struct TFileStream * pStream // Pointer to an unopened stream + ); + +typedef bool (*STREAM_OPEN)( + struct TFileStream * pStream, // Pointer to an unopened stream + const TCHAR * szFileName, // Pointer to file name to be open + DWORD dwStreamFlags // Stream flags + ); + 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 @@ -28,44 +42,49 @@ typedef bool (*STREAM_WRITE)( DWORD dwBytesToWrite // Number of bytes to read from the file ); -typedef bool (*STREAM_GETPOS)( - struct TFileStream * pStream, // Pointer to an open stream - ULONGLONG & ByteOffset // Pointer to store current file position - ); - -typedef bool (*STREAM_GETSIZE)( - struct TFileStream * pStream, // Pointer to an open stream - ULONGLONG & FileSize // Receives the file size, in bytes - ); - -typedef bool (*STREAM_SETSIZE)( +typedef bool (*STREAM_RESIZE)( 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_GETSIZE)( + struct TFileStream * pStream, // Pointer to an open stream + ULONGLONG * pFileSize // Receives the file size, in bytes ); -typedef bool (*STREAM_SWITCH)( - struct TFileStream * pStream, - struct TFileStream * pNewStream - ); - -typedef bool (*STREAM_GETBMP)( - TFileStream * pStream, - TFileBitmap * pBitmap, - DWORD Length, - LPDWORD LengthNeeded +typedef bool (*STREAM_GETPOS)( + struct TFileStream * pStream, // Pointer to an open stream + ULONGLONG * pByteOffset // Pointer to store current file position ); typedef void (*STREAM_CLOSE)( - struct TFileStream * pStream + struct TFileStream * pStream // Pointer to an open stream + ); + +typedef bool (*BLOCK_READ)( + struct TFileStream * pStream, // Pointer to a block-oriented stream + ULONGLONG StartOffset, // Byte offset of start of the block array + ULONGLONG EndOffset, // End offset (either end of the block or end of the file) + LPBYTE BlockBuffer, // Pointer to block-aligned buffer + DWORD BytesNeeded, // Number of bytes that are really needed + bool bAvailable // true if the block is available + ); + +typedef bool (*BLOCK_CHECK)( + struct TFileStream * pStream, // Pointer to a block-oriented stream + ULONGLONG BlockOffset // Offset of the file to check + ); + +typedef void (*BLOCK_SAVEMAP)( + struct TFileStream * pStream // Pointer to a block-oriented stream ); //----------------------------------------------------------------------------- -// Local structures - part file structure +// Local structures - partial file structure and bitmap footer + +#define ID_FILE_BITMAP_FOOTER 0x33767470 // Signature of the file bitmap footer ('ptv3') +#define DEFAULT_BLOCK_SIZE 0x00004000 // Default size of the stream block +#define DEFAULT_BUILD_NUMBER 10958 // Build number for newly created partial MPQs typedef struct _PART_FILE_HEADER { @@ -89,91 +108,100 @@ typedef struct _PART_FILE_MAP_ENTRY } PART_FILE_MAP_ENTRY, *PPART_FILE_MAP_ENTRY; -//----------------------------------------------------------------------------- -// Local structures +typedef struct _FILE_BITMAP_FOOTER +{ + DWORD Signature; // 'ptv3' (ID_FILE_BITMAP_FOOTER) + DWORD Version; // Unknown, seems to always have value of 3 (version?) + DWORD BuildNumber; // Game build number for that MPQ + DWORD MapOffsetLo; // Low 32-bits of the offset of the bit map + DWORD MapOffsetHi; // High 32-bits of the offset of the bit map + DWORD BlockSize; // Size of one block (usually 0x4000 bytes) -union TBaseData +} FILE_BITMAP_FOOTER, *PFILE_BITMAP_FOOTER; + +//----------------------------------------------------------------------------- +// Structure for file stream + +union TBaseProviderData { struct { ULONGLONG FileSize; // Size of the file ULONGLONG FilePos; // Current file position - ULONGLONG FileTime; // Date/time of last modification of the file + ULONGLONG FileTime; // Last write time 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 + ULONGLONG FileSize; // Size of the file + ULONGLONG FilePos; // Current file position + ULONGLONG FileTime; // Last write time 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 + ULONGLONG FileSize; // Size of the file + ULONGLONG FilePos; // Current file position + ULONGLONG FileTime; // Last write time 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_RESIZE StreamResize; // Pointer to function changing file size 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_GETPOS StreamGetPos; // Pointer to function that returns current file position STREAM_CLOSE StreamClose; // Pointer to function closing the stream - // Stream provider data members - TCHAR szFileName[MAX_PATH]; // File name - DWORD dwFlags; // Stream flags + // Block-oriented functions + BLOCK_READ BlockRead; // Pointer to function reading one or more blocks + BLOCK_CHECK BlockCheck; // Pointer to function checking whether the block is present // Base provider functions - STREAM_READ BaseRead; - STREAM_WRITE BaseWrite; - STREAM_GETPOS BaseGetPos; // Pointer to function that returns current file position + STREAM_CREATE BaseCreate; // Pointer to base create function + STREAM_OPEN BaseOpen; // Pointer to base open function + STREAM_READ BaseRead; // Read from the stream + STREAM_WRITE BaseWrite; // Write to the stream + STREAM_RESIZE BaseResize; // Pointer to function changing file size 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_GETPOS BaseGetPos; // Pointer to function that returns current file position STREAM_CLOSE BaseClose; // Pointer to function closing the stream - // Base provider data members - TBaseData Base; // Base provider data + // Base provider data (file size, file position) + TBaseProviderData Base; + + // Stream provider data + TFileStream * pMaster; // Master stream (e.g. MPQ on a web server) + TCHAR * szFileName; // File name (self-relative pointer) + + ULONGLONG StreamSize; // Stream size (can be less than file size) + ULONGLONG StreamPos; // Stream position + DWORD BuildNumber; // Game build number + DWORD dwFlags; // Stream flags // Followed by stream provider data, with variable length }; //----------------------------------------------------------------------------- -// Structure for linear stream +// Structures for block-oriented stream -struct TLinearStream : public TFileStream +struct TBlockStream : 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 + SFILE_DOWNLOAD_CALLBACK pfnCallback; // Callback for downloading + void * FileBitmap; // Array of bits for file blocks + void * UserData; // User data to be passed to the download callback + DWORD BitmapSize; // Size of the file bitmap (in bytes) + DWORD BlockSize; // Size of one block, in bytes + DWORD BlockCount; // Number of data blocks in the file + DWORD IsComplete; // If nonzero, no blocks are missing + DWORD IsModified; // nonzero if the bitmap has been modified }; //----------------------------------------------------------------------------- @@ -181,7 +209,7 @@ struct TPartialStream : public TFileStream #define MPQE_CHUNK_SIZE 0x40 // Size of one chunk to be decrypted -struct TEncryptedStream : public TFileStream +struct TEncryptedStream : public TBlockStream { BYTE Key[MPQE_CHUNK_SIZE]; // File key }; diff --git a/dep/StormLib/src/LibTomCrypt.c b/dep/StormLib/src/LibTomCrypt.c new file mode 100644 index 0000000000..f77b604641 --- /dev/null +++ b/dep/StormLib/src/LibTomCrypt.c @@ -0,0 +1,85 @@ +// +// This compound source is here for reducing the long build time +// of the entire StormLib library +// + +#pragma warning(disable:4242) // der_decode_ia5_string.c(84) : warning C4242: '=' : conversion from 'int' to 'unsigned char', possible loss of data + +#include "libtomcrypt/src/hashes/hash_memory.c" +#include "libtomcrypt/src/hashes/md5.c" +#include "libtomcrypt/src/hashes/sha1.c" +#include "libtomcrypt/src/hashes/sha256.c" +#include "libtomcrypt/src/math/multi.c" +#include "libtomcrypt/src/math/rand_prime.c" +#include "libtomcrypt/src/misc/base64_decode.c" +#include "libtomcrypt/src/misc/crypt_argchk.c" +#include "libtomcrypt/src/misc/crypt_find_hash.c" +#include "libtomcrypt/src/misc/crypt_find_prng.c" +#include "libtomcrypt/src/misc/crypt_hash_descriptor.c" +#include "libtomcrypt/src/misc/crypt_hash_is_valid.c" +#include "libtomcrypt/src/misc/crypt_libc.c" +#include "libtomcrypt/src/misc/crypt_ltc_mp_descriptor.c" +#include "libtomcrypt/src/misc/crypt_prng_descriptor.c" +#include "libtomcrypt/src/misc/crypt_prng_is_valid.c" +#include "libtomcrypt/src/misc/crypt_register_hash.c" +#include "libtomcrypt/src/misc/crypt_register_prng.c" +#include "libtomcrypt/src/misc/zeromem.c" +#include "libtomcrypt/src/pk/asn1/der_decode_bit_string.c" +#include "libtomcrypt/src/pk/asn1/der_decode_boolean.c" +#include "libtomcrypt/src/pk/asn1/der_decode_choice.c" +#include "libtomcrypt/src/pk/asn1/der_decode_ia5_string.c" +#include "libtomcrypt/src/pk/asn1/der_decode_integer.c" +#include "libtomcrypt/src/pk/asn1/der_decode_object_identifier.c" +#include "libtomcrypt/src/pk/asn1/der_decode_octet_string.c" +#include "libtomcrypt/src/pk/asn1/der_decode_printable_string.c" +#include "libtomcrypt/src/pk/asn1/der_decode_sequence_ex.c" +#include "libtomcrypt/src/pk/asn1/der_decode_sequence_flexi.c" +#include "libtomcrypt/src/pk/asn1/der_decode_sequence_multi.c" +#include "libtomcrypt/src/pk/asn1/der_decode_short_integer.c" +#include "libtomcrypt/src/pk/asn1/der_decode_utctime.c" +#include "libtomcrypt/src/pk/asn1/der_decode_utf8_string.c" +#include "libtomcrypt/src/pk/asn1/der_encode_bit_string.c" +#include "libtomcrypt/src/pk/asn1/der_encode_boolean.c" +#include "libtomcrypt/src/pk/asn1/der_encode_ia5_string.c" +#include "libtomcrypt/src/pk/asn1/der_encode_integer.c" +#include "libtomcrypt/src/pk/asn1/der_encode_object_identifier.c" +#include "libtomcrypt/src/pk/asn1/der_encode_octet_string.c" +#include "libtomcrypt/src/pk/asn1/der_encode_printable_string.c" +#include "libtomcrypt/src/pk/asn1/der_encode_sequence_ex.c" +#include "libtomcrypt/src/pk/asn1/der_encode_sequence_multi.c" +#include "libtomcrypt/src/pk/asn1/der_encode_set.c" +#include "libtomcrypt/src/pk/asn1/der_encode_setof.c" +#include "libtomcrypt/src/pk/asn1/der_encode_short_integer.c" +#include "libtomcrypt/src/pk/asn1/der_encode_utctime.c" +#include "libtomcrypt/src/pk/asn1/der_encode_utf8_string.c" +#include "libtomcrypt/src/pk/asn1/der_length_bit_string.c" +#include "libtomcrypt/src/pk/asn1/der_length_boolean.c" +#include "libtomcrypt/src/pk/asn1/der_length_ia5_string.c" +#include "libtomcrypt/src/pk/asn1/der_length_integer.c" +#include "libtomcrypt/src/pk/asn1/der_length_object_identifier.c" +#include "libtomcrypt/src/pk/asn1/der_length_octet_string.c" +#include "libtomcrypt/src/pk/asn1/der_length_printable_string.c" +#include "libtomcrypt/src/pk/asn1/der_length_sequence.c" +#include "libtomcrypt/src/pk/asn1/der_length_short_integer.c" +#include "libtomcrypt/src/pk/asn1/der_length_utctime.c" +#include "libtomcrypt/src/pk/asn1/der_length_utf8_string.c" +#include "libtomcrypt/src/pk/asn1/der_sequence_free.c" +#include "libtomcrypt/src/pk/ecc/ltc_ecc_map.c" +#include "libtomcrypt/src/pk/ecc/ltc_ecc_mul2add.c" +#include "libtomcrypt/src/pk/ecc/ltc_ecc_mulmod.c" +#include "libtomcrypt/src/pk/ecc/ltc_ecc_points.c" +#include "libtomcrypt/src/pk/ecc/ltc_ecc_projective_add_point.c" +#include "libtomcrypt/src/pk/ecc/ltc_ecc_projective_dbl_point.c" +#include "libtomcrypt/src/pk/pkcs1/pkcs_1_mgf1.c" +#include "libtomcrypt/src/pk/pkcs1/pkcs_1_oaep_decode.c" +#include "libtomcrypt/src/pk/pkcs1/pkcs_1_pss_decode.c" +#include "libtomcrypt/src/pk/pkcs1/pkcs_1_pss_encode.c" +#include "libtomcrypt/src/pk/pkcs1/pkcs_1_v1_5_decode.c" +#include "libtomcrypt/src/pk/pkcs1/pkcs_1_v1_5_encode.c" +#include "libtomcrypt/src/pk/rsa/rsa_exptmod.c" +#include "libtomcrypt/src/pk/rsa/rsa_free.c" +#include "libtomcrypt/src/pk/rsa/rsa_import.c" +#include "libtomcrypt/src/pk/rsa/rsa_make_key.c" +#include "libtomcrypt/src/pk/rsa/rsa_sign_hash.c" +#include "libtomcrypt/src/pk/rsa/rsa_verify_hash.c" +#include "libtomcrypt/src/pk/rsa/rsa_verify_simple.c" diff --git a/dep/StormLib/src/LibTomMath.c b/dep/StormLib/src/LibTomMath.c new file mode 100644 index 0000000000..5f6639f1b7 --- /dev/null +++ b/dep/StormLib/src/LibTomMath.c @@ -0,0 +1,125 @@ +// +// This compound source is here for reducing the long build time +// of the entire StormLib library +// + +#pragma warning(disable:4334) // warning C4334: '<<' : result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?) + +#include "libtommath/bn_fast_mp_invmod.c" +#include "libtommath/bn_fast_mp_montgomery_reduce.c" +#include "libtommath/bn_fast_s_mp_mul_digs.c" +#include "libtommath/bn_fast_s_mp_mul_high_digs.c" +#include "libtommath/bn_fast_s_mp_sqr.c" +#include "libtommath/bn_mp_2expt.c" +#include "libtommath/bn_mp_abs.c" +#include "libtommath/bn_mp_add.c" +#include "libtommath/bn_mp_add_d.c" +#include "libtommath/bn_mp_addmod.c" +#include "libtommath/bn_mp_and.c" +#include "libtommath/bn_mp_clamp.c" +#include "libtommath/bn_mp_clear.c" +#include "libtommath/bn_mp_clear_multi.c" +#include "libtommath/bn_mp_cmp.c" +#include "libtommath/bn_mp_cmp_d.c" +#include "libtommath/bn_mp_cmp_mag.c" +#include "libtommath/bn_mp_cnt_lsb.c" +#include "libtommath/bn_mp_copy.c" +#include "libtommath/bn_mp_count_bits.c" +#include "libtommath/bn_mp_div.c" +#include "libtommath/bn_mp_div_2.c" +#include "libtommath/bn_mp_div_2d.c" +#include "libtommath/bn_mp_div_3.c" +#include "libtommath/bn_mp_div_d.c" +#include "libtommath/bn_mp_dr_is_modulus.c" +#include "libtommath/bn_mp_dr_reduce.c" +#include "libtommath/bn_mp_dr_setup.c" +#include "libtommath/bn_mp_exch.c" +#include "libtommath/bn_mp_expt_d.c" +#include "libtommath/bn_mp_exptmod.c" +#include "libtommath/bn_mp_exptmod_fast.c" +#include "libtommath/bn_mp_exteuclid.c" +#include "libtommath/bn_mp_fread.c" +#include "libtommath/bn_mp_fwrite.c" +#include "libtommath/bn_mp_gcd.c" +#include "libtommath/bn_mp_get_int.c" +#include "libtommath/bn_mp_grow.c" +#include "libtommath/bn_mp_init.c" +#include "libtommath/bn_mp_init_copy.c" +#include "libtommath/bn_mp_init_multi.c" +#include "libtommath/bn_mp_init_set.c" +#include "libtommath/bn_mp_init_set_int.c" +#include "libtommath/bn_mp_init_size.c" +#include "libtommath/bn_mp_invmod.c" +#include "libtommath/bn_mp_invmod_slow.c" +#include "libtommath/bn_mp_is_square.c" +#include "libtommath/bn_mp_jacobi.c" +#include "libtommath/bn_mp_karatsuba_mul.c" +#include "libtommath/bn_mp_karatsuba_sqr.c" +#include "libtommath/bn_mp_lcm.c" +#include "libtommath/bn_mp_lshd.c" +#include "libtommath/bn_mp_mod.c" +#include "libtommath/bn_mp_mod_2d.c" +#include "libtommath/bn_mp_mod_d.c" +#include "libtommath/bn_mp_montgomery_calc_normalization.c" +#include "libtommath/bn_mp_montgomery_reduce.c" +#include "libtommath/bn_mp_montgomery_setup.c" +#include "libtommath/bn_mp_mul.c" +#include "libtommath/bn_mp_mul_2.c" +#include "libtommath/bn_mp_mul_2d.c" +#include "libtommath/bn_mp_mul_d.c" +#include "libtommath/bn_mp_mulmod.c" +#include "libtommath/bn_mp_n_root.c" +#include "libtommath/bn_mp_neg.c" +#include "libtommath/bn_mp_or.c" +#include "libtommath/bn_mp_prime_fermat.c" +#include "libtommath/bn_mp_prime_is_divisible.c" +#include "libtommath/bn_mp_prime_is_prime.c" +#include "libtommath/bn_mp_prime_miller_rabin.c" +#include "libtommath/bn_mp_prime_next_prime.c" +#include "libtommath/bn_mp_prime_rabin_miller_trials.c" +#include "libtommath/bn_mp_prime_random_ex.c" +#include "libtommath/bn_mp_radix_size.c" +#include "libtommath/bn_mp_radix_smap.c" +#include "libtommath/bn_mp_rand.c" +#include "libtommath/bn_mp_read_radix.c" +#include "libtommath/bn_mp_read_signed_bin.c" +#include "libtommath/bn_mp_read_unsigned_bin.c" +#include "libtommath/bn_mp_reduce.c" +#include "libtommath/bn_mp_reduce_2k.c" +#include "libtommath/bn_mp_reduce_2k_l.c" +#include "libtommath/bn_mp_reduce_2k_setup.c" +#include "libtommath/bn_mp_reduce_2k_setup_l.c" +#include "libtommath/bn_mp_reduce_is_2k.c" +#include "libtommath/bn_mp_reduce_is_2k_l.c" +#include "libtommath/bn_mp_reduce_setup.c" +#include "libtommath/bn_mp_rshd.c" +#include "libtommath/bn_mp_set.c" +#include "libtommath/bn_mp_set_int.c" +#include "libtommath/bn_mp_shrink.c" +#include "libtommath/bn_mp_signed_bin_size.c" +#include "libtommath/bn_mp_sqr.c" +#include "libtommath/bn_mp_sqrmod.c" +#include "libtommath/bn_mp_sqrt.c" +#include "libtommath/bn_mp_sub.c" +#include "libtommath/bn_mp_sub_d.c" +#include "libtommath/bn_mp_submod.c" +#include "libtommath/bn_mp_to_signed_bin.c" +#include "libtommath/bn_mp_to_signed_bin_n.c" +#include "libtommath/bn_mp_to_unsigned_bin.c" +#include "libtommath/bn_mp_to_unsigned_bin_n.c" +#include "libtommath/bn_mp_toom_mul.c" +#include "libtommath/bn_mp_toom_sqr.c" +#include "libtommath/bn_mp_toradix.c" +#include "libtommath/bn_mp_toradix_n.c" +#include "libtommath/bn_mp_unsigned_bin_size.c" +#include "libtommath/bn_mp_xor.c" +#include "libtommath/bn_mp_zero.c" +#include "libtommath/bn_prime_tab.c" +#include "libtommath/bn_reverse.c" +#include "libtommath/bn_s_mp_add.c" +#include "libtommath/bn_s_mp_exptmod.c" +#include "libtommath/bn_s_mp_mul_digs.c" +#include "libtommath/bn_s_mp_mul_high_digs.c" +#include "libtommath/bn_s_mp_sqr.c" +#include "libtommath/bn_s_mp_sub.c" +#include "libtommath/bncore.c" diff --git a/dep/StormLib/src/LibTomMathDesc.c b/dep/StormLib/src/LibTomMathDesc.c new file mode 100644 index 0000000000..0d2fb3afa2 --- /dev/null +++ b/dep/StormLib/src/LibTomMathDesc.c @@ -0,0 +1,4 @@ +// This is a source file for WDK build of StormLib +// It is copied to the root folder during the build process + +#include "libtomcrypt/src/math/ltm_desc.c" diff --git a/dep/StormLib/src/SBaseCommon.cpp b/dep/StormLib/src/SBaseCommon.cpp index 6581878452..5c6e70681a 100644 --- a/dep/StormLib/src/SBaseCommon.cpp +++ b/dep/StormLib/src/SBaseCommon.cpp @@ -15,40 +15,207 @@ #include "StormLib.h" #include "StormCommon.h" -char StormLibCopyright[] = "StormLib v " STORMLIB_VERSION_STRING " Copyright Ladislav Zezula 1998-2012"; +char StormLibCopyright[] = "StormLib v " STORMLIB_VERSION_STRING " Copyright Ladislav Zezula 1998-2023"; //----------------------------------------------------------------------------- -// The buffer for decryption engine. +// Local variables -LCID lcFileLocale = LANG_NEUTRAL; // File locale -USHORT wPlatform = 0; // File platform +DWORD g_dwMpqSignature = ID_MPQ; // Marker for MPQ header +DWORD g_dwHashTableKey = MPQ_KEY_HASH_TABLE; // Key for hash table +DWORD g_dwBlockTableKey = MPQ_KEY_BLOCK_TABLE; // Key for block table +LCID g_lcFileLocale = 0; // Compound of file locale and platform //----------------------------------------------------------------------------- -// Storm buffer functions +// Conversion to uppercase/lowercase + +// Converts ASCII characters to lowercase +// Converts slash (0x2F) to backslash (0x5C) +const unsigned char AsciiToLowerTable[256] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x5C, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, + 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, + 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, + 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, + 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF +}; + +// Converts ASCII characters to uppercase +// Converts slash (0x2F) to backslash (0x5C) +const unsigned char AsciiToUpperTable[256] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x5C, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, + 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, + 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, + 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, + 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, + 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF +}; + +// Converts ASCII characters to uppercase +// Does NOT convert slash (0x2F) to backslash (0x5C) +unsigned char AsciiToUpperTable_Slash[256] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, + 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, + 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, + 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, + 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, + 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF +}; + +//----------------------------------------------------------------------------- +// Safe string functions (for ANSI builds) + +char * StringCopy(char * szTarget, size_t cchTarget, const char * szSource) +{ + size_t cchSource = 0; + + if(cchTarget > 0) + { + cchSource = strlen(szSource); + + if(cchSource >= cchTarget) + cchSource = cchTarget - 1; + + memcpy(szTarget, szSource, cchSource); + szTarget[cchSource] = 0; + } + + return szTarget + cchSource; +} + +void StringCat(char * szTarget, size_t cchTargetMax, const char * szSource) +{ + // Get the current length of the target + size_t cchTarget = strlen(szTarget); + + // Copy the string to the target + if(cchTarget < cchTargetMax) + { + StringCopy(szTarget + cchTarget, (cchTargetMax - cchTarget), szSource); + } +} + +void StringCreatePseudoFileName(char * szBuffer, size_t cchMaxChars, unsigned int nIndex, const char * szExtension) +{ + char * szBufferEnd = szBuffer + cchMaxChars; + + // "File" + szBuffer = StringCopy(szBuffer, (szBufferEnd - szBuffer), "File"); + + // Number + szBuffer = SMemIntToStr(szBuffer, szBufferEnd - szBuffer + 1, nIndex, 8); + + // Dot + if(szBuffer < szBufferEnd) + *szBuffer++ = '.'; + + // Extension + while(szExtension[0] == '.') + szExtension++; + StringCopy(szBuffer, (szBufferEnd - szBuffer), szExtension); +} + +//----------------------------------------------------------------------------- +// Utility functions (UNICODE) only exist in the ANSI version of the library +// In ANSI builds, TCHAR = char, so we don't need these functions implemented + +#ifdef _UNICODE +void StringCopy(TCHAR * szTarget, size_t cchTarget, const char * szSource) +{ + int ccResult; + + ccResult = MultiByteToWideChar(CP_UTF8, 0, szSource, -1, szTarget, (int)(cchTarget)); + szTarget[ccResult] = 0; +} + +void StringCopy(char * szTarget, size_t cchTarget, const TCHAR * szSource) +{ + int ccResult; + + ccResult = WideCharToMultiByte(CP_UTF8, 0, szSource, -1, szTarget, (int)(cchTarget), NULL, NULL); + szTarget[ccResult] = 0; +} + +void StringCopy(TCHAR * szTarget, size_t cchTarget, const TCHAR * szSource) +{ + if(cchTarget > 0) + { + size_t cchSource = _tcslen(szSource); + + if(cchSource >= cchTarget) + cchSource = cchTarget - 1; + + memcpy(szTarget, szSource, cchSource * sizeof(TCHAR)); + szTarget[cchSource] = 0; + } +} + +void StringCat(TCHAR * szTarget, size_t cchTargetMax, const TCHAR * szSource) +{ + // Get the current length of the target + size_t cchTarget = _tcslen(szTarget); + + // Copy the string to the target + if(cchTarget < cchTargetMax) + { + StringCopy(szTarget + cchTarget, (cchTargetMax - cchTarget), szSource); + } +} + +void StringCat(TCHAR * szTarget, size_t cchTargetMax, const char * szSource) +{ + // Get the current length of the target + size_t cchTarget = _tcslen(szTarget); + + // Copy the string to the target + if(cchTarget < cchTargetMax) + { + StringCopy(szTarget + cchTarget, (cchTargetMax - cchTarget), szSource); + } +} +#endif + +//----------------------------------------------------------------------------- +// Storm hashing functions #define STORM_BUFFER_SIZE 0x500 +#define HASH_INDEX_MASK(ha) (ha->pHeader->dwHashTableSize ? (ha->pHeader->dwHashTableSize - 1) : 0) static DWORD StormBuffer[STORM_BUFFER_SIZE]; // Buffer for the decryption engine static bool bMpqCryptographyInitialized = false; -DWORD HashString(const char * szFileName, DWORD dwHashType) -{ - LPBYTE pbKey = (BYTE *)szFileName; - DWORD dwSeed1 = 0x7FED7FED; - DWORD dwSeed2 = 0xEEEEEEEE; - DWORD ch; - - while(*pbKey != 0) - { - ch = toupper(*pbKey++); - - dwSeed1 = StormBuffer[dwHashType + ch] ^ (dwSeed1 + dwSeed2); - dwSeed2 = ch + dwSeed1 + dwSeed2 + (dwSeed2 << 5) + 3; - } - - return dwSeed1; -} - void InitializeMpqCryptography() { DWORD dwSeed = 0x00100001; @@ -77,254 +244,166 @@ void InitializeMpqCryptography() } // Also register both MD5 and SHA1 hash algorithms - register_hash(&md5_desc); register_hash(&sha1_desc); + register_hash(&md5_desc); // Use LibTomMath as support math library for LibTomCrypt - ltc_mp = ltm_desc; + ltc_mp = ltm_desc; // Don't do that again bMpqCryptographyInitialized = true; } } +// +// Note: Implementation of this function in WorldEdit.exe and storm.dll +// incorrectly treats the character as signed, which leads to the +// a buffer underflow if the character in the file name >= 0x80: +// The following steps happen when *pbKey == 0xBF and dwHashType == 0x0000 +// (calculating hash index) +// +// 1) Result of AsciiToUpperTable_Slash[*pbKey++] is sign-extended to 0xffffffbf +// 2) The "ch" is added to dwHashType (0xffffffbf + 0x0000 => 0xffffffbf) +// 3) The result is used as index to the StormBuffer table, +// thus dereferences a random value BEFORE the begin of StormBuffer. +// +// As result, MPQs containing files with non-ANSI characters will not work between +// various game versions and localizations. Even WorldEdit, after importing a file +// with Korean characters in the name, cannot open the file back. +// +DWORD HashString(const char * szFileName, DWORD dwHashType) +{ + LPBYTE pbKey = (BYTE *)szFileName; + DWORD dwSeed1 = 0x7FED7FED; + DWORD dwSeed2 = 0xEEEEEEEE; + DWORD ch; + + while(*pbKey != 0) + { + // Convert the input character to uppercase + // Convert slash (0x2F) to backslash (0x5C) + ch = AsciiToUpperTable[*pbKey++]; + + dwSeed1 = StormBuffer[dwHashType + ch] ^ (dwSeed1 + dwSeed2); + dwSeed2 = ch + dwSeed1 + dwSeed2 + (dwSeed2 << 5) + 3; + } + + return dwSeed1; +} + +DWORD HashStringSlash(const char * szFileName, DWORD dwHashType) +{ + LPBYTE pbKey = (BYTE *)szFileName; + DWORD dwSeed1 = 0x7FED7FED; + DWORD dwSeed2 = 0xEEEEEEEE; + DWORD ch; + + while(*pbKey != 0) + { + // Convert the input character to uppercase + // DON'T convert slash (0x2F) to backslash (0x5C) + ch = AsciiToUpperTable_Slash[*pbKey++]; + + dwSeed1 = StormBuffer[dwHashType + ch] ^ (dwSeed1 + dwSeed2); + dwSeed2 = ch + dwSeed1 + dwSeed2 + (dwSeed2 << 5) + 3; + } + + return dwSeed1; +} + +DWORD HashStringLower(const char * szFileName, DWORD dwHashType) +{ + LPBYTE pbKey = (BYTE *)szFileName; + DWORD dwSeed1 = 0x7FED7FED; + DWORD dwSeed2 = 0xEEEEEEEE; + DWORD ch; + + while(*pbKey != 0) + { + // Convert the input character to lower + // DON'T convert slash (0x2F) to backslash (0x5C) + ch = AsciiToLowerTable[*pbKey++]; + + dwSeed1 = StormBuffer[dwHashType + ch] ^ (dwSeed1 + dwSeed2); + dwSeed2 = ch + dwSeed1 + dwSeed2 + (dwSeed2 << 5) + 3; + } + + return dwSeed1; +} + //----------------------------------------------------------------------------- // Calculates the hash table size for a given amount of files -DWORD GetHashTableSizeForFileCount(DWORD dwFileCount) +// Returns the nearest higher power of two. +// If the value is already a power of two, returns the same value +DWORD GetNearestPowerOfTwo(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) - { - if(dwPowerOfTwo >= dwFileCount) - { - return dwPowerOfTwo; - } - } + dwFileCount --; - // Don't allow the hash table size go over allowed maximum - return HASH_TABLE_SIZE_MAX; + dwFileCount |= dwFileCount >> 1; + dwFileCount |= dwFileCount >> 2; + dwFileCount |= dwFileCount >> 4; + dwFileCount |= dwFileCount >> 8; + dwFileCount |= dwFileCount >> 16; + + return dwFileCount + 1; } +/* +DWORD GetNearestPowerOfTwo(DWORD dwFileCount) +{ + DWORD dwPowerOfTwo = HASH_TABLE_SIZE_MIN; + // For zero files, there is no hash table needed + if(dwFileCount == 0) + return 0; + + // Round the hash table size up to the nearest power of two + // Don't allow the hash table size go over allowed maximum + while(dwPowerOfTwo < HASH_TABLE_SIZE_MAX && dwPowerOfTwo < dwFileCount) + dwPowerOfTwo <<= 1; + return dwPowerOfTwo; +} +*/ //----------------------------------------------------------------------------- // Calculates a Jenkin's Encrypting and decrypting MPQ file data ULONGLONG HashStringJenkins(const char * szFileName) { - char * szTemp; - char szLocFileName[0x108]; - char chOneChar; + LPBYTE pbFileName = (LPBYTE)szFileName; + char szNameBuff[0x108]; size_t nLength = 0; unsigned int primary_hash = 1; unsigned int secondary_hash = 2; - // This is required to produce defined results - assert(szFileName != NULL); - // Normalize the file name - convert to uppercase, and convert "/" to "\\". - if(szFileName != NULL) + if(pbFileName != NULL) { - szTemp = szLocFileName; - while(*szFileName != 0) - { - chOneChar = (char)tolower(*szFileName++); - if(chOneChar == '/') - chOneChar = '\\'; + char * szNamePtr = szNameBuff; + char * szNameEnd = szNamePtr + sizeof(szNameBuff); - *szTemp++ = chOneChar; - } - - nLength = szTemp - szLocFileName; - *szTemp = 0; + // Normalize the file name. Doesn't have to be zero terminated for hashing + while(szNamePtr < szNameEnd && pbFileName[0] != 0) + *szNamePtr++ = (char)AsciiToLowerTable[*pbFileName++]; + nLength = szNamePtr - szNameBuff; } // Thanks Quantam for finding out what the algorithm is. // I am really getting old for reversing large chunks of assembly // that does hashing :-) - hashlittle2(szLocFileName, nLength, &secondary_hash, &primary_hash); + hashlittle2(szNameBuff, nLength, &secondary_hash, &primary_hash); // Combine those 2 together - return (ULONGLONG)primary_hash * (ULONGLONG)0x100000000ULL + (ULONGLONG)secondary_hash; -} - -//----------------------------------------------------------------------------- -// This function converts the MPQ header so it always looks like version 4 - -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 - if(dwFlags & MPQ_OPEN_FORCE_MPQ_V1) - wFormatVersion = MPQ_FORMAT_VERSION_1; - - // Format-specific fixes - switch(wFormatVersion) - { - 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; - } - - // - // 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; - } - - // 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: - - // 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 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; - - // 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; - } - - // - // 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; - } - - // 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); - - // 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; - } - } - 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; - - // 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; - } - - return nError; + return ((ULONGLONG)primary_hash << 0x20) | (ULONGLONG)secondary_hash; } //----------------------------------------------------------------------------- // Default flags for (attributes) and (listfile) -DWORD GetDefaultSpecialFileFlags(TMPQArchive * ha, DWORD dwFileSize) +DWORD GetDefaultSpecialFileFlags(DWORD dwFileSize, USHORT wFormatVersion) { // Fixed for format 1.0 - if(ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1) - return MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED | MPQ_FILE_FIX_KEY; + if(wFormatVersion == MPQ_FORMAT_VERSION_1) + return MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED | MPQ_FILE_KEY_V2; // 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); @@ -332,186 +411,269 @@ DWORD GetDefaultSpecialFileFlags(TMPQArchive * ha, DWORD dwFileSize) //----------------------------------------------------------------------------- -// Encrypting and decrypting MPQ file data +// Encrypting/Decrypting MPQ data block -void EncryptMpqBlock(void * pvFileBlock, DWORD dwLength, DWORD dwSeed1) +static DWORD EncryptUInt32Unaligned(LPDWORD DataPointer, DWORD i, DWORD dwXorKey) { - LPDWORD block = (LPDWORD)pvFileBlock; - DWORD dwSeed2 = 0xEEEEEEEE; - DWORD ch; + LPBYTE pbDataPointer = (LPBYTE)(DataPointer + i); + LPBYTE pbXorKey = (LPBYTE)(&dwXorKey); + DWORD dwValue32; + + // Retrieve the value + dwValue32 = ((DWORD)pbDataPointer[0] << 0x00) | + ((DWORD)pbDataPointer[1] << 0x08) | + ((DWORD)pbDataPointer[2] << 0x10) | + ((DWORD)pbDataPointer[3] << 0x18); + + // Perform unaligned XOR + pbDataPointer[0] = (pbDataPointer[0] ^ pbXorKey[0]); + pbDataPointer[1] = (pbDataPointer[1] ^ pbXorKey[1]); + pbDataPointer[2] = (pbDataPointer[2] ^ pbXorKey[2]); + pbDataPointer[3] = (pbDataPointer[3] ^ pbXorKey[3]); + return dwValue32; +} + +void EncryptMpqBlock(void * pvDataBlock, DWORD dwLength, DWORD dwKey1) +{ + LPDWORD DataPointer = (LPDWORD)pvDataBlock; + DWORD dwValue32; + DWORD dwKey2 = 0xEEEEEEEE; // Round to DWORDs dwLength >>= 2; - while(dwLength-- > 0) + // We need different approach on non-aligned buffers + if(STORMLIB_DWORD_ALIGNED(DataPointer)) { - dwSeed2 += StormBuffer[0x400 + (dwSeed1 & 0xFF)]; - ch = *block; - *block++ = ch ^ (dwSeed1 + dwSeed2); - - dwSeed1 = ((~dwSeed1 << 0x15) + 0x11111111) | (dwSeed1 >> 0x0B); - dwSeed2 = ch + dwSeed2 + (dwSeed2 << 5) + 3; - } -} - -void DecryptMpqBlock(void * pvFileBlock, DWORD dwLength, DWORD dwSeed1) -{ - LPDWORD block = (LPDWORD)pvFileBlock; - DWORD dwSeed2 = 0xEEEEEEEE; - DWORD ch; - - // Round to DWORDs - dwLength >>= 2; - - while(dwLength-- > 0) - { - dwSeed2 += StormBuffer[0x400 + (dwSeed1 & 0xFF)]; - ch = *block ^ (dwSeed1 + dwSeed2); - - dwSeed1 = ((~dwSeed1 << 0x15) + 0x11111111) | (dwSeed1 >> 0x0B); - dwSeed2 = ch + dwSeed2 + (dwSeed2 << 5) + 3; - *block++ = ch; - } -} - -/* -void EncryptMpqTable(void * pvMpqTable, DWORD dwLength, const char * szKey) -{ - EncryptMpqBlock(pvMpqTable, dwLength, HashString(szKey, MPQ_HASH_FILE_KEY)); -} - -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 - */ - -DWORD DetectFileKeyBySectorSize(LPDWORD SectorOffsets, DWORD decrypted) -{ - DWORD saveKey1; - DWORD temp = *SectorOffsets ^ decrypted; // temp = seed1 + seed2 - temp -= 0xEEEEEEEE; // temp = seed1 + StormBuffer[0x400 + (seed1 & 0xFF)] - - for(int i = 0; i < 0x100; i++) // Try all 255 possibilities - { - DWORD seed1; - DWORD seed2 = 0xEEEEEEEE; - DWORD ch; - - // Try the first DWORD (We exactly know the value) - seed1 = temp - StormBuffer[0x400 + i]; - seed2 += StormBuffer[0x400 + (seed1 & 0xFF)]; - ch = SectorOffsets[0] ^ (seed1 + seed2); - - if(ch != decrypted) - continue; - - // Add 1 because we are decrypting sector positions - saveKey1 = seed1 + 1; - - // If OK, continue and test the second value. We don't know exactly the value, - // but we know that the second one has lower 16 bits set to zero - // (no compressed sector is larger than 0xFFFF bytes) - seed1 = ((~seed1 << 0x15) + 0x11111111) | (seed1 >> 0x0B); - seed2 = ch + seed2 + (seed2 << 5) + 3; - - seed2 += StormBuffer[0x400 + (seed1 & 0xFF)]; - ch = SectorOffsets[1] ^ (seed1 + seed2); - - if((ch & 0xFFFF0000) == 0) - return saveKey1; - } - return 0; -} - -// Function tries to detect file encryption key. It expectes at least two uncompressed bytes -DWORD DetectFileKeyByKnownContent(void * pvFileContent, DWORD nDwords, ...) -{ - LPDWORD pdwContent = (LPDWORD)pvFileContent; - va_list argList; - DWORD dwDecrypted[0x10]; - 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 - { - DWORD seed1; - DWORD seed2 = 0xEEEEEEEE; - DWORD ch; - - // Try the first DWORD - seed1 = dwTemp - StormBuffer[0x400 + i]; - seed2 += StormBuffer[0x400 + (seed1 & 0xFF)]; - ch = pdwContent[0] ^ (seed1 + seed2); - - if(ch != dwDecrypted[0]) - continue; - - saveKey1 = seed1; - - // If OK, continue and test all bytes. - for(j = 1; j < nDwords; j++) + for(DWORD i = 0; i < dwLength; i++) { - seed1 = ((~seed1 << 0x15) + 0x11111111) | (seed1 >> 0x0B); - seed2 = ch + seed2 + (seed2 << 5) + 3; + // Modify the second key + dwKey2 += StormBuffer[MPQ_HASH_KEY2_MIX + (dwKey1 & 0xFF)]; - seed2 += StormBuffer[0x400 + (seed1 & 0xFF)]; - ch = pdwContent[j] ^ (seed1 + seed2); + // We can use 32-bit approach, when the buffer is aligned + DataPointer[i] = (dwValue32 = DataPointer[i]) ^ (dwKey1 + dwKey2); - if(ch == dwDecrypted[j] && j == nDwords - 1) - return saveKey1; + dwKey1 = ((~dwKey1 << 0x15) + 0x11111111) | (dwKey1 >> 0x0B); + dwKey2 = dwValue32 + dwKey2 + (dwKey2 << 5) + 3; } } + else + { + for(DWORD i = 0; i < dwLength; i++) + { + // Modify the second key + dwKey2 += StormBuffer[MPQ_HASH_KEY2_MIX + (dwKey1 & 0xFF)]; + + // The data are unaligned. Make sure we don't cause data misalignment error + dwValue32 = EncryptUInt32Unaligned(DataPointer, i, (dwKey1 + dwKey2)); + + dwKey1 = ((~dwKey1 << 0x15) + 0x11111111) | (dwKey1 >> 0x0B); + dwKey2 = dwValue32 + dwKey2 + (dwKey2 << 5) + 3; + } + } +} + +static DWORD DecryptUInt32Unaligned(LPDWORD DataPointer, DWORD i, DWORD dwXorKey) +{ + LPBYTE pbDataPointer = (LPBYTE)(DataPointer + i); + LPBYTE pbXorKey = (LPBYTE)(&dwXorKey); + + // Perform unaligned XOR + pbDataPointer[0] = (pbDataPointer[0] ^ pbXorKey[0]); + pbDataPointer[1] = (pbDataPointer[1] ^ pbXorKey[1]); + pbDataPointer[2] = (pbDataPointer[2] ^ pbXorKey[2]); + pbDataPointer[3] = (pbDataPointer[3] ^ pbXorKey[3]); + + // Retrieve the value + return ((DWORD)pbDataPointer[0] << 0x00) | + ((DWORD)pbDataPointer[1] << 0x08) | + ((DWORD)pbDataPointer[2] << 0x10) | + ((DWORD)pbDataPointer[3] << 0x18); +} + +void DecryptMpqBlock(void * pvDataBlock, DWORD dwLength, DWORD dwKey1) +{ + LPDWORD DataPointer = (LPDWORD)pvDataBlock; + DWORD dwValue32; + DWORD dwKey2 = 0xEEEEEEEE; + + // Round to DWORDs + dwLength >>= 2; + + // We need different approach on non-aligned buffers + if(STORMLIB_DWORD_ALIGNED(DataPointer)) + { + for(DWORD i = 0; i < dwLength; i++) + { + // Modify the second key + dwKey2 += StormBuffer[MPQ_HASH_KEY2_MIX + (dwKey1 & 0xFF)]; + + // We can use 32-bit approach, when the buffer is aligned + DataPointer[i] = dwValue32 = DataPointer[i] ^ (dwKey1 + dwKey2); + + dwKey1 = ((~dwKey1 << 0x15) + 0x11111111) | (dwKey1 >> 0x0B); + dwKey2 = dwValue32 + dwKey2 + (dwKey2 << 5) + 3; + } + } + else + { + for(DWORD i = 0; i < dwLength; i++) + { + // Modify the second key + dwKey2 += StormBuffer[MPQ_HASH_KEY2_MIX + (dwKey1 & 0xFF)]; + + // The data are unaligned. Make sure we don't cause data misalignment error + dwValue32 = DecryptUInt32Unaligned(DataPointer, i, (dwKey1 + dwKey2)); + + dwKey1 = ((~dwKey1 << 0x15) + 0x11111111) | (dwKey1 >> 0x0B); + dwKey2 = dwValue32 + dwKey2 + (dwKey2 << 5) + 3; + } + } +} + +/** + * Functions tries to get file decryption key. This comes from these facts + * + * - We know the decrypted value of the first DWORD in the encrypted data + * - We know the decrypted value of the second DWORD (at least aproximately) + * - There is only 256 variants of how the second key is modified + * + * The first iteration of dwKey1 and dwKey2 is this: + * + * dwKey2 = 0xEEEEEEEE + StormBuffer[MPQ_HASH_KEY2_MIX + (dwKey1 & 0xFF)] + * dwDecrypted0 = DataBlock[0] ^ (dwKey1 + dwKey2); + * + * This means: + * + * (dwKey1 + dwKey2) = DataBlock[0] ^ dwDecrypted0; + * + */ + +DWORD DetectFileKeyBySectorSize(LPDWORD EncryptedData, DWORD dwSectorSize, DWORD dwDecrypted0) +{ + // We must have at least 2 DWORDs there to be able to decrypt something + if(dwSectorSize >= 0x08) + { + // Also try subsequent three values. This is because the value of the sector offset[0] + // could be higher than the total size of the sector table. + // Example MPQ: MPQ_2021_v1_CantExtractCHK.scx + for(DWORD dwDecrypted4 = dwDecrypted0 + 4; dwDecrypted0 < dwDecrypted4; dwDecrypted0++) + { + DWORD dwDecrypted1Max = dwSectorSize + dwDecrypted0; + DWORD dwKey1PlusKey2; + DWORD DataBlock[2]; + + // Get the value of the combined encryption key + dwKey1PlusKey2 = (EncryptedData[0] ^ dwDecrypted0) - 0xEEEEEEEE; + + // Try all 256 combinations of dwKey1 + for(DWORD i = 0; i < 0x100; i++) + { + DWORD dwSaveKey1; + DWORD dwKey1 = dwKey1PlusKey2 - StormBuffer[MPQ_HASH_KEY2_MIX + i]; + DWORD dwKey2 = 0xEEEEEEEE; + + // Modify the second key and decrypt the first DWORD + dwKey2 += StormBuffer[MPQ_HASH_KEY2_MIX + (dwKey1 & 0xFF)]; + DataBlock[0] = EncryptedData[0] ^ (dwKey1 + dwKey2); + + // Did we obtain the same value like dwDecrypted0? + if(DataBlock[0] == dwDecrypted0) + { + // Save this key value. Increment by one because + // we are decrypting sector offset table + dwSaveKey1 = dwKey1 + 1; + + // Rotate both keys + dwKey1 = ((~dwKey1 << 0x15) + 0x11111111) | (dwKey1 >> 0x0B); + dwKey2 = DataBlock[0] + dwKey2 + (dwKey2 << 5) + 3; + + // Modify the second key again and decrypt the second DWORD + dwKey2 += StormBuffer[MPQ_HASH_KEY2_MIX + (dwKey1 & 0xFF)]; + DataBlock[1] = EncryptedData[1] ^ (dwKey1 + dwKey2); + + // Now compare the results + if(DataBlock[1] <= dwDecrypted1Max) + return dwSaveKey1; + } + } + } + } + + // Key not found return 0; } -DWORD DetectFileKeyByContent(void * pvFileContent, DWORD dwFileSize) +// Function tries to detect file encryption key based on expected file content +// It is the same function like before, except that we know the value of the second DWORD +DWORD DetectFileKeyByKnownContent(void * pvEncryptedData, DWORD dwDecrypted0, DWORD dwDecrypted1) +{ + LPDWORD EncryptedData = (LPDWORD)pvEncryptedData; + DWORD dwKey1PlusKey2; + DWORD DataBlock[2]; + + // Get the value of the combined encryption key + dwKey1PlusKey2 = (EncryptedData[0] ^ dwDecrypted0) - 0xEEEEEEEE; + + // Try all 256 combinations of dwKey1 + for(DWORD i = 0; i < 0x100; i++) + { + DWORD dwSaveKey1; + DWORD dwKey1 = dwKey1PlusKey2 - StormBuffer[MPQ_HASH_KEY2_MIX + i]; + DWORD dwKey2 = 0xEEEEEEEE; + + // Modify the second key and decrypt the first DWORD + dwKey2 += StormBuffer[MPQ_HASH_KEY2_MIX + (dwKey1 & 0xFF)]; + DataBlock[0] = EncryptedData[0] ^ (dwKey1 + dwKey2); + + // Did we obtain the same value like dwDecrypted0? + if(DataBlock[0] == dwDecrypted0) + { + // Save this key value + dwSaveKey1 = dwKey1; + + // Rotate both keys + dwKey1 = ((~dwKey1 << 0x15) + 0x11111111) | (dwKey1 >> 0x0B); + dwKey2 = DataBlock[0] + dwKey2 + (dwKey2 << 5) + 3; + + // Modify the second key again and decrypt the second DWORD + dwKey2 += StormBuffer[MPQ_HASH_KEY2_MIX + (dwKey1 & 0xFF)]; + DataBlock[1] = EncryptedData[1] ^ (dwKey1 + dwKey2); + + // Now compare the results + if(DataBlock[1] == dwDecrypted1) + return dwSaveKey1; + } + } + + // Key not found + return 0; +} + +DWORD DetectFileKeyByContent(void * pvEncryptedData, DWORD dwSectorSize, DWORD dwFileSize) { DWORD dwFileKey; // Try to break the file encryption key as if it was a WAVE file - if(dwFileSize >= 0x0C) + if(dwSectorSize >= 0x0C) { - dwFileKey = DetectFileKeyByKnownContent(pvFileContent, 3, 0x46464952, dwFileSize - 8, 0x45564157); + dwFileKey = DetectFileKeyByKnownContent(pvEncryptedData, 0x46464952, dwFileSize - 8); if(dwFileKey != 0) return dwFileKey; } // Try to break the encryption key as if it was an EXE file - if(dwFileSize > 0x40) + if(dwSectorSize > 0x40) { - dwFileKey = DetectFileKeyByKnownContent(pvFileContent, 2, 0x00905A4D, 0x00000003); + dwFileKey = DetectFileKeyByKnownContent(pvEncryptedData, 0x00905A4D, 0x00000003); if(dwFileKey != 0) return dwFileKey; } // Try to break the encryption key as if it was a XML file - if(dwFileSize > 0x04) + if(dwSectorSize > 0x04) { - dwFileKey = DetectFileKeyByKnownContent(pvFileContent, 2, 0x6D783F3C, 0x6576206C); + dwFileKey = DetectFileKeyByKnownContent(pvEncryptedData, 0x6D783F3C, 0x6576206C); if(dwFileKey != 0) return dwFileKey; } @@ -530,11 +692,11 @@ DWORD DecryptFileKey( DWORD dwMpqPos = (DWORD)MpqPos; // File key is calculated from plain name - szFileName = GetPlainFileNameA(szFileName); + szFileName = GetPlainFileName(szFileName); dwFileKey = HashString(szFileName, MPQ_HASH_FILE_KEY); // Fix the key, if needed - if(dwFlags & MPQ_FILE_FIX_KEY) + if(dwFlags & MPQ_FILE_KEY_V2) dwFileKey = (dwFileKey + dwMpqPos) ^ dwFileSize; // Return the key @@ -544,160 +706,194 @@ DWORD DecryptFileKey( //----------------------------------------------------------------------------- // Handle validation functions -bool IsValidMpqHandle(TMPQArchive * ha) +TMPQArchive * IsValidMpqHandle(HANDLE hMpq) { - if(ha == NULL) - return false; - if(ha->pHeader == NULL || ha->pHeader->dwID != ID_MPQ) - return false; - - return (bool)(ha->pHeader->dwID == ID_MPQ); + TMPQArchive * ha = (TMPQArchive *)hMpq; + + return (ha != NULL && ha->pHeader != NULL && ha->pHeader->dwID == g_dwMpqSignature) ? ha : NULL; } -bool IsValidFileHandle(TMPQFile * hf) +TMPQFile * IsValidFileHandle(HANDLE hFile) { - if(hf == NULL) - return false; + TMPQFile * hf = (TMPQFile *)hFile; - if(hf->dwMagic != ID_MPQ_FILE) - return false; + // Must not be NULL + if(hf != NULL && hf->dwMagic == ID_MPQ_FILE) + { + // Local file handle? + if(hf->pStream != NULL) + return hf; - if(hf->pStream != NULL) - return true; + // Also verify the MPQ handle within the file handle + if(IsValidMpqHandle(hf->ha)) + return hf; + } - return IsValidMpqHandle(hf->ha); + return NULL; } //----------------------------------------------------------------------------- // Hash table and block table manipulation +// Attempts to search a free hash entry, or an entry whose names and locale matches +TMPQHash * FindFreeHashEntry(TMPQArchive * ha, DWORD dwStartIndex, DWORD dwHashCheck1, DWORD dwHashCheck2, LCID lcFileLocale) +{ + TMPQHash * pDeletedEntry = NULL; // If a deleted entry was found in the continuous hash range + TMPQHash * pFreeEntry = NULL; // If a free entry was found in the continuous hash range + DWORD dwHashIndexMask = HASH_INDEX_MASK(ha); + DWORD dwIndex; + USHORT Locale = SFILE_LOCALE(lcFileLocale); + + // Set the initial index + dwStartIndex = dwIndex = (dwStartIndex & dwHashIndexMask); + + // Search the hash table and return the found entries in the following priority: + // 1) + // 2) + // 3) + // 4) NULL + for(;;) + { + TMPQHash * pHash = ha->pHashTable + dwIndex; + + // If we found a matching entry, return that one + if(pHash->dwHashCheck1 == dwHashCheck1 && pHash->dwHashCheck2 == dwHashCheck2 && pHash->Locale == Locale) + return pHash; + + // If we found a deleted entry, remember it but keep searching + if(pHash->dwBlockIndex == HASH_ENTRY_DELETED && pDeletedEntry == NULL) + pDeletedEntry = pHash; + + // If we found a free entry, we need to stop searching + if(pHash->dwBlockIndex == HASH_ENTRY_FREE) + { + pFreeEntry = pHash; + break; + } + + // Move to the next hash entry. + // If we reached the starting entry, it's failure. + dwIndex = (dwIndex + 1) & dwHashIndexMask; + if(dwIndex == dwStartIndex) + break; + } + + // If we found a deleted entry, return that one preferentially + return (pDeletedEntry != NULL) ? pDeletedEntry : pFreeEntry; +} + // Retrieves the first hash entry for the given file. // Every locale version of a file has its own hash entry TMPQHash * GetFirstHashEntry(TMPQArchive * ha, const char * szFileName) { - TMPQHash * pStartHash; // File hash entry (start) - TMPQHash * pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize; - TMPQHash * pHash; // File hash entry (current) - DWORD dwHashTableSizeMask; - DWORD dwIndex = HashString(szFileName, MPQ_HASH_TABLE_INDEX); - DWORD dwName1 = HashString(szFileName, MPQ_HASH_NAME_A); - DWORD dwName2 = HashString(szFileName, MPQ_HASH_NAME_B); + DWORD dwHashIndexMask = HASH_INDEX_MASK(ha); + DWORD dwStartIndex = ha->pfnHashString(szFileName, MPQ_HASH_TABLE_INDEX); + DWORD dwHashCheck1 = ha->pfnHashString(szFileName, MPQ_HASH_NAME_A); + DWORD dwHashCheck2 = ha->pfnHashString(szFileName, MPQ_HASH_NAME_B); + DWORD dwIndex; - // Get the first possible has entry that might be the one - dwHashTableSizeMask = ha->pHeader->dwHashTableSize ? (ha->pHeader->dwHashTableSize - 1) : 0; - pStartHash = pHash = ha->pHashTable + (dwIndex & dwHashTableSizeMask); + // Set the initial index + dwStartIndex = dwIndex = (dwStartIndex & dwHashIndexMask); - // There might be deleted entries in the hash table prior to our desired entry. - while(pHash->dwBlockIndex != HASH_ENTRY_FREE) + // Search the hash table + for(;;) { - // If the entry agrees, we found it. - if(pHash->dwName1 == dwName1 && pHash->dwName2 == dwName2 && pHash->dwBlockIndex < ha->dwFileTableSize) + TMPQHash * pHash = ha->pHashTable + dwIndex; + + // If the entry matches, we found it. + if(pHash->dwHashCheck1 == dwHashCheck1 && pHash->dwHashCheck2 == dwHashCheck2 && MPQ_BLOCK_INDEX(pHash) < ha->dwFileTableSize) return pHash; + // If that hash entry is a free entry, it means we haven't found the file + if(pHash->dwBlockIndex == HASH_ENTRY_FREE) + return NULL; + // Move to the next hash entry. Stop searching // if we got reached the original hash entry - if(++pHash >= pHashEnd) - pHash = ha->pHashTable; - if(pHash == pStartHash) - break; + dwIndex = (dwIndex + 1) & dwHashIndexMask; + if(dwIndex == dwStartIndex) + return NULL; } - - // The apropriate hash entry was not found - return NULL; } -TMPQHash * GetNextHashEntry(TMPQArchive * ha, TMPQHash * pFirstHash, TMPQHash * pPrevHash) +TMPQHash * GetNextHashEntry(TMPQArchive * ha, TMPQHash * pFirstHash, TMPQHash * pHash) { - TMPQHash * pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize; - TMPQHash * pHash = pPrevHash; - DWORD dwName1 = pPrevHash->dwName1; - DWORD dwName2 = pPrevHash->dwName2; + DWORD dwHashIndexMask = HASH_INDEX_MASK(ha); + DWORD dwStartIndex = (DWORD)(pFirstHash - ha->pHashTable); + DWORD dwHashCheck1 = pHash->dwHashCheck1; + DWORD dwHashCheck2 = pHash->dwHashCheck2; + DWORD dwIndex = (DWORD)(pHash - ha->pHashTable); - // Now go for any next entry that follows the pPrevHash, + // Now go for any next entry that follows the pHash, // until either free hash entry was found, or the start entry was reached for(;;) { // Move to the next hash entry. Stop searching // if we got reached the original hash entry - if(++pHash >= pHashEnd) - pHash = ha->pHashTable; - if(pHash == pFirstHash) - break; + dwIndex = (dwIndex + 1) & dwHashIndexMask; + if(dwIndex == dwStartIndex) + return NULL; + pHash = ha->pHashTable + dwIndex; - // If the entry is a free entry, stop search - if(pHash->dwBlockIndex == HASH_ENTRY_FREE) - break; - - // If the entry is not free and the name agrees, we found it - if(pHash->dwName1 == dwName1 && pHash->dwName2 == dwName2 && pHash->dwBlockIndex < ha->pHeader->dwBlockTableSize) + // If the entry matches, we found it. + if(pHash->dwHashCheck1 == dwHashCheck1 && pHash->dwHashCheck2 == dwHashCheck2 && MPQ_BLOCK_INDEX(pHash) < ha->dwFileTableSize) return pHash; - } - // No next entry - return NULL; + // If that hash entry is a free entry, it means we haven't found the file + if(pHash->dwBlockIndex == HASH_ENTRY_FREE) + return NULL; + } } // Allocates an entry in the hash table -DWORD AllocateHashEntry( +TMPQHash * AllocateHashEntry( TMPQArchive * ha, - TFileEntry * pFileEntry) + TFileEntry * pFileEntry, + LCID lcFileLocale) { - TMPQHash * pStartHash; // File hash entry (start) - TMPQHash * pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize; - TMPQHash * pHash; // File hash entry (current) - DWORD dwHashTableSizeMask; - DWORD dwIndex = HashString(pFileEntry->szFileName, MPQ_HASH_TABLE_INDEX); - DWORD dwName1 = HashString(pFileEntry->szFileName, MPQ_HASH_NAME_A); - DWORD dwName2 = HashString(pFileEntry->szFileName, MPQ_HASH_NAME_B); + TMPQHash * pHash; + DWORD dwStartIndex = ha->pfnHashString(pFileEntry->szFileName, MPQ_HASH_TABLE_INDEX); + DWORD dwHashCheck1 = ha->pfnHashString(pFileEntry->szFileName, MPQ_HASH_NAME_A); + DWORD dwHashCheck2 = ha->pfnHashString(pFileEntry->szFileName, MPQ_HASH_NAME_B); - // Get the first possible has entry that might be the one - dwHashTableSizeMask = ha->pHeader->dwHashTableSize ? (ha->pHeader->dwHashTableSize - 1) : 0; - pStartHash = pHash = ha->pHashTable + (dwIndex & dwHashTableSizeMask); - - // There might be deleted entries in the hash table prior to our desired entry. - while(pHash->dwBlockIndex < HASH_ENTRY_DELETED) + // Attempt to find a free hash entry + pHash = FindFreeHashEntry(ha, dwStartIndex, dwHashCheck1, dwHashCheck2, lcFileLocale); + if(pHash != NULL) { - // If there already is an existing entry, reuse it. - if(pHash->dwName1 == dwName1 && pHash->dwName2 == dwName2 && pHash->lcLocale == pFileEntry->lcLocale) - break; - - // Move to the next hash entry. - // If we reached the starting entry, it's failure. - if(++pHash >= pHashEnd) - pHash = ha->pHashTable; - if(pHash == pStartHash) - return HASH_ENTRY_FREE; + // Fill the free hash entry + pHash->dwHashCheck1 = dwHashCheck1; + pHash->dwHashCheck2 = dwHashCheck2; + pHash->Locale = SFILE_LOCALE(lcFileLocale); + pHash->Platform = SFILE_PLATFORM(lcFileLocale); + pHash->Flags = 0; + pHash->dwBlockIndex = (DWORD)(pFileEntry - ha->pFileTable); } - // Fill the free hash entry - pHash->dwName1 = dwName1; - pHash->dwName2 = dwName2; - pHash->lcLocale = pFileEntry->lcLocale; - pHash->wPlatform = pFileEntry->wPlatform; - pHash->dwBlockIndex = (DWORD)(pFileEntry - ha->pFileTable); - - // Fill the hash index in the file entry - pFileEntry->dwHashIndex = (DWORD)(pHash - ha->pHashTable); - return pFileEntry->dwHashIndex; + return pHash; } // 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 * pFreeSpacePos) +// position in the MPQ. (listfile), (attributes) and (signature) are ignored, +// unless the MPQ is being flushed. +ULONGLONG FindFreeMpqSpace(TMPQArchive * ha) { TMPQHeader * pHeader = ha->pHeader; TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; - TFileEntry * pFileEntry = ha->pFileTable; + TFileEntry * pFileEntry; ULONGLONG FreeSpacePos = ha->pHeader->dwHeaderSize; DWORD dwChunkCount; // Parse the entire block table for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) { - // Only take existing files - if(pFileEntry->dwFlags & MPQ_FILE_EXISTS) + // Only take existing files with nonzero size + if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) && (pFileEntry->dwCmpSize != 0)) { + // If we are not saving MPQ tables, ignore internal MPQ files + if((ha->dwFlags & MPQ_FLAG_SAVING_TABLES) == 0 && IsInternalMpqFileName(pFileEntry->szFileName)) + continue; + // If the end of the file is bigger than current MPQ table pos, update it if((pFileEntry->ByteOffset + pFileEntry->dwCmpSize) > FreeSpacePos) { @@ -715,14 +911,13 @@ void FindFreeMpqSpace(TMPQArchive * ha, ULONGLONG * pFreeSpacePos) } // Give the free space position to the caller - if(pFreeSpacePos != NULL) - *pFreeSpacePos = FreeSpacePos; + return FreeSpacePos; } //----------------------------------------------------------------------------- // Common functions - MPQ File -TMPQFile * CreateMpqFile(TMPQArchive * ha) +TMPQFile * CreateFileHandle(TMPQArchive * ha, TFileEntry * pFileEntry) { TMPQFile * hf; @@ -732,102 +927,195 @@ TMPQFile * CreateMpqFile(TMPQArchive * ha) { // Fill the file structure memset(hf, 0, sizeof(TMPQFile)); - hf->ha = ha; - hf->pStream = NULL; hf->dwMagic = ID_MPQ_FILE; + hf->pStream = NULL; + hf->ha = ha; + + // If the caller entered a file entry, we also copy information from the file entry + if(ha != NULL) + { + // Increment number of open files in the archive handle + ha->dwFileCount++; + + // Add the file entry to the handle + if(pFileEntry != NULL) + { + // Set the raw position and MPQ position + hf->RawFilePos = FileOffsetFromMpqOffset(ha, pFileEntry->ByteOffset); + hf->MpqFilePos = pFileEntry->ByteOffset; + + // Set the data size + hf->dwDataSize = pFileEntry->dwFileSize; + hf->pFileEntry = pFileEntry; + } + } } return hf; } -// 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) +TMPQFile * CreateWritableHandle(TMPQArchive * ha, DWORD dwFileSize) { - LPBYTE pbCompressed = NULL; - LPBYTE pbToRead = (LPBYTE)pvTable; - int nError = ERROR_SUCCESS; + ULONGLONG FreeMpqSpace; + ULONGLONG TempPos; + TMPQFile * hf; - // "interface.MPQ.part" in trial version of World of Warcraft - // has block table and hash table compressed. - if(dwCompressedSize < dwRealSize) + // We need to find the position in the MPQ where we save the file data + FreeMpqSpace = FindFreeMpqSpace(ha); + + // When format V1, the size of the archive cannot exceed 4 GB + if(ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1) { - // Allocate temporary buffer for holding compressed data - pbCompressed = STORM_ALLOC(BYTE, dwCompressedSize); - if(pbCompressed == NULL) - return ERROR_NOT_ENOUGH_MEMORY; - - // Assign the temporary buffer as target for read operation - pbToRead = pbCompressed; - } - - // Read the table - if(FileStream_Read(ha->pStream, &ByteOffset, pbToRead, dwCompressedSize)) - { - // First of all, decrypt the table - if(dwKey != 0) + TempPos = FreeMpqSpace + + dwFileSize + + (ha->pHeader->dwHashTableSize * sizeof(TMPQHash)) + + (ha->dwFileTableSize * sizeof(TMPQBlock)); + if((TempPos >> 32) != 0) { - BSWAP_ARRAY32_UNSIGNED(pbToRead, dwCompressedSize); - DecryptMpqBlock(pbToRead, dwCompressedSize, dwKey); - BSWAP_ARRAY32_UNSIGNED(pbToRead, dwCompressedSize); - } - - // If the table is compressed, decompress it - if(dwCompressedSize < dwRealSize) - { - int cbOutBuffer = (int)dwRealSize; - int cbInBuffer = (int)dwCompressedSize; - - if(!SCompDecompress2((char *)pvTable, &cbOutBuffer, (char *)pbCompressed, cbInBuffer)) - nError = GetLastError(); - - // Free the temporary buffer - STORM_FREE(pbCompressed); + SErrSetLastError(ERROR_DISK_FULL); + return NULL; } } - else + + // Allocate the file handle + hf = CreateFileHandle(ha, NULL); + if(hf == NULL) { - nError = GetLastError(); + SErrSetLastError(ERROR_NOT_ENOUGH_MEMORY); + return NULL; } - BSWAP_ARRAY32_UNSIGNED(pvTable, dwRealSize); - return nError; + // We need to find the position in the MPQ where we save the file data + hf->MpqFilePos = FreeMpqSpace; + hf->bIsWriteHandle = true; + return hf; } -void CalculateRawSectorOffset( - ULONGLONG & RawFilePos, - TMPQFile * hf, - DWORD dwSectorOffset) +// Loads a table from MPQ. +// Can be used for hash table, block table, sector offset table or sector checksum table +void * LoadMpqTable( + TMPQArchive * ha, + ULONGLONG ByteOffset, + LPBYTE pbTableHash, + DWORD dwCompressedSize, + DWORD dwTableSize, + DWORD dwKey, + DWORD * PtrRealTableSize) { - // - // Some MPQ protectors place the sector offset table after the actual file data. - // Sector offsets in the sector offset table are negative. When added - // to MPQ file offset from the block table entry, the result is a correct - // position of the file data in the MPQ. - // - // The position of sector table must be always within the MPQ, however. - // When a negative sector offset is found, we make sure that we make the addition - // just in 32-bits, and then add the MPQ offset. - // + ULONGLONG FileSize = 0; + LPBYTE pbCompressed = NULL; + LPBYTE pbMpqTable; + LPBYTE pbToRead; + DWORD dwBytesToRead = dwTableSize; + DWORD dwErrCode = ERROR_SUCCESS; - if(dwSectorOffset & 0x80000000) + // Allocate the MPQ table + pbMpqTable = pbToRead = STORM_ALLOC(BYTE, dwTableSize); + if(pbMpqTable != NULL) { - RawFilePos = hf->ha->MpqPos + ((DWORD)hf->pFileEntry->ByteOffset + dwSectorOffset); - } - else - { - RawFilePos = hf->RawFilePos + dwSectorOffset; + // Check if the MPQ table is compressed + if(dwCompressedSize < dwTableSize) + { + // Allocate temporary buffer for holding compressed data + pbCompressed = pbToRead = STORM_ALLOC(BYTE, dwCompressedSize); + dwBytesToRead = dwCompressedSize; + + if(pbCompressed == NULL) + { + STORM_FREE(pbMpqTable); + return NULL; + } + } + else + { + // pocs/MPQ_2025_06_BadHashTableSize.mpq + dwCompressedSize = dwTableSize; + } + + // Get the file offset from which we will read the table + // Note: According to Storm.dll from Warcraft III (version 2002), + // if the hash table position is 0xFFFFFFFF, no SetFilePointer call is done + // and the table is loaded from the current file offset + if(ByteOffset == SFILE_INVALID_POS) + FileStream_GetPos(ha->pStream, &ByteOffset); + FileStream_GetSize(ha->pStream, &FileSize); + + // Is the sector table within the file? + if(ByteOffset >= FileSize) + { + STORM_FREE(pbMpqTable); + return NULL; + } + + // The hash table and block table can go beyond EOF. + // Storm.dll reads as much as possible, then fills the missing part with zeros. + // Abused by Spazzler map protector which sets hash table size to 0x00100000 + // Abused by NP_Protect in MPQs v4 as well + if((ByteOffset + dwBytesToRead) > FileSize) + { + // Fill the extra data with zeros + dwBytesToRead = (DWORD)(FileSize - ByteOffset); + memset(pbMpqTable + dwBytesToRead, 0, (dwTableSize - dwBytesToRead)); + } + + // Give the caller information that the table was cut + if(PtrRealTableSize != NULL) + { + PtrRealTableSize[0] = dwBytesToRead; + } + + // If everything succeeded, read the raw table from the MPQ + if(FileStream_Read(ha->pStream, &ByteOffset, pbToRead, dwBytesToRead)) + { + // Verify the MD5 of the table, if present + if(!VerifyDataBlockHash(pbToRead, dwBytesToRead, pbTableHash)) + { + dwErrCode = ERROR_FILE_CORRUPT; + } + } + else + { + dwErrCode = SErrGetLastError(); + } + + if(dwErrCode == ERROR_SUCCESS) + { + // First of all, decrypt the table + if(dwKey != 0) + { + BSWAP_ARRAY32_UNSIGNED(pbToRead, dwCompressedSize); + DecryptMpqBlock(pbToRead, dwCompressedSize, dwKey); + BSWAP_ARRAY32_UNSIGNED(pbToRead, dwCompressedSize); + } + + // If the table is compressed, decompress it + if(dwCompressedSize < dwTableSize) + { + int cbOutBuffer = (int)dwTableSize; + int cbInBuffer = (int)dwCompressedSize; + + if(!SCompDecompress2(pbMpqTable, &cbOutBuffer, pbCompressed, cbInBuffer)) + dwErrCode = SErrGetLastError(); + } + + // Make sure that the table is properly byte-swapped + BSWAP_ARRAY32_UNSIGNED(pbMpqTable, dwTableSize); + } + + // If read failed, free the table and return + if(dwErrCode != ERROR_SUCCESS) + { + STORM_FREE(pbMpqTable); + pbMpqTable = NULL; + } + + // Free the compression buffer, if any + if(pbCompressed != NULL) + STORM_FREE(pbCompressed); } - // We also have to add patch header size, if patch header is present - if(hf->pPatchInfo != NULL) - RawFilePos += hf->pPatchInfo->dwLength; + // Return the MPQ table + return pbMpqTable; } unsigned char * AllocateMd5Buffer( @@ -855,7 +1143,7 @@ unsigned char * AllocateMd5Buffer( } // Allocates sector buffer and sector offset table -int AllocateSectorBuffer(TMPQFile * hf) +DWORD AllocateSectorBuffer(TMPQFile * hf) { TMPQArchive * ha = hf->ha; @@ -874,13 +1162,14 @@ int AllocateSectorBuffer(TMPQFile * hf) hf->dwSectorOffs = SFILE_INVALID_POS; // Return result - return (hf->pbFileSector != NULL) ? (int)ERROR_SUCCESS : (int)ERROR_NOT_ENOUGH_MEMORY; + return (hf->pbFileSector != NULL) ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY; } // Allocates sector offset table -int AllocatePatchInfo(TMPQFile * hf, bool bLoadFromFile) +DWORD AllocatePatchInfo(TMPQFile * hf, bool bLoadFromFile) { TMPQArchive * ha = hf->ha; + TPatchInfo * pPatchInfo; DWORD dwLength = sizeof(TPatchInfo); // The following conditions must be true @@ -891,35 +1180,39 @@ __AllocateAndLoadPatchInfo: // Allocate space for patch header. Start with default size, // and if its size if bigger, then we reload them - hf->pPatchInfo = (TPatchInfo *)STORM_ALLOC(BYTE, dwLength); - if(hf->pPatchInfo == NULL) + pPatchInfo = (TPatchInfo *)(STORM_ALLOC(BYTE, dwLength)); + if(pPatchInfo == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Do we have to load the patch header from the file ? if(bLoadFromFile) { // Load the patch header - if(!FileStream_Read(ha->pStream, &hf->RawFilePos, hf->pPatchInfo, dwLength)) + if(!FileStream_Read(ha->pStream, &hf->RawFilePos, pPatchInfo, dwLength)) { - // Free the patch info - STORM_FREE(hf->pPatchInfo); - hf->pPatchInfo = NULL; - return GetLastError(); + STORM_FREE(pPatchInfo); + return SErrGetLastError(); } // Perform necessary swapping - hf->pPatchInfo->dwLength = BSWAP_INT32_UNSIGNED(hf->pPatchInfo->dwLength); - hf->pPatchInfo->dwFlags = BSWAP_INT32_UNSIGNED(hf->pPatchInfo->dwFlags); - hf->pPatchInfo->dwDataSize = BSWAP_INT32_UNSIGNED(hf->pPatchInfo->dwDataSize); + pPatchInfo->dwLength = BSWAP_INT32_UNSIGNED(pPatchInfo->dwLength); + pPatchInfo->dwFlags = BSWAP_INT32_UNSIGNED(pPatchInfo->dwFlags); + pPatchInfo->dwDataSize = BSWAP_INT32_UNSIGNED(pPatchInfo->dwDataSize); + + // Do nothing if the patch info is not valid + if(!(pPatchInfo->dwFlags & MPQ_PATCH_INFO_VALID)) + { + STORM_FREE(pPatchInfo); + return ERROR_FILE_CORRUPT; + } // Verify the size of the patch header // If it's not default size, we have to reload them - if(hf->pPatchInfo->dwLength > dwLength) + if(pPatchInfo->dwLength > dwLength) { // Free the patch info - dwLength = hf->pPatchInfo->dwLength; - STORM_FREE(hf->pPatchInfo); - hf->pPatchInfo = NULL; + dwLength = pPatchInfo->dwLength; + STORM_FREE(pPatchInfo); // If the length is out of all possible ranges, fail the operation if(dwLength > 0x400) @@ -928,21 +1221,22 @@ __AllocateAndLoadPatchInfo: } // Patch file data size according to the patch header - hf->dwDataSize = hf->pPatchInfo->dwDataSize; + hf->dwDataSize = pPatchInfo->dwDataSize; } else { - memset(hf->pPatchInfo, 0, dwLength); + memset(pPatchInfo, 0, dwLength); + pPatchInfo->dwLength = dwLength; + pPatchInfo->dwFlags = MPQ_PATCH_INFO_VALID; } // Save the final length to the patch header - hf->pPatchInfo->dwLength = dwLength; - hf->pPatchInfo->dwFlags = 0x80000000; + hf->pPatchInfo = pPatchInfo; return ERROR_SUCCESS; } // Allocates sector offset table -int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile) +DWORD AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile) { TMPQArchive * ha = hf->ha; TFileEntry * pFileEntry = hf->pFileEntry; @@ -968,7 +1262,7 @@ int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile) // Calculate the number of file sectors 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. @@ -976,12 +1270,12 @@ int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile) dwSectorOffsLen += sizeof(DWORD); // Only allocate and load the table if the file is compressed - if(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED) + if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) { __LoadSectorOffsets: // Allocate the sector offset table - hf->SectorOffsets = (DWORD *)STORM_ALLOC(BYTE, dwSectorOffsLen); + hf->SectorOffsets = STORM_ALLOC(DWORD, (dwSectorOffsLen / sizeof(DWORD))); if(hf->SectorOffsets == NULL) return ERROR_NOT_ENOUGH_MEMORY; @@ -990,8 +1284,16 @@ int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile) { ULONGLONG RawFilePos = hf->RawFilePos; + // Append the length of the patch info, if any if(hf->pPatchInfo != NULL) + { + if((RawFilePos + hf->pPatchInfo->dwLength) < RawFilePos) { + STORM_FREE(hf->SectorOffsets); + hf->SectorOffsets = NULL; + return ERROR_FILE_CORRUPT; + } RawFilePos += hf->pPatchInfo->dwLength; + } // Load the sector offsets from the file if(!FileStream_Read(ha->pStream, &RawFilePos, hf->SectorOffsets, dwSectorOffsLen)) @@ -999,7 +1301,7 @@ int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile) // Free the sector offsets STORM_FREE(hf->SectorOffsets); hf->SectorOffsets = NULL; - return GetLastError(); + return SErrGetLastError(); } // Swap the sector positions @@ -1011,7 +1313,7 @@ int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile) // If we don't know the file key, try to find it. if(hf->dwFileKey == 0) { - hf->dwFileKey = DetectFileKeyBySectorSize(hf->SectorOffsets, dwSectorOffsLen); + hf->dwFileKey = DetectFileKeyBySectorSize(hf->SectorOffsets, ha->dwSectorSize, dwSectorOffsLen); if(hf->dwFileKey == 0) { STORM_FREE(hf->SectorOffsets); @@ -1037,14 +1339,17 @@ int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile) DWORD dwSectorOffset0 = hf->SectorOffsets[i]; // Every following sector offset must be bigger than the previous one - if(dwSectorOffset1 <= dwSectorOffset0) + if(dwSectorOffset1 < dwSectorOffset0) { bSectorOffsetTableCorrupt = true; break; } // The sector size must not be bigger than compressed file size - if((dwSectorOffset1 - dwSectorOffset0) > pFileEntry->dwCmpSize) + // Edit: Yes, but apparently, in original Storm.dll, the compressed + // size is not checked anywhere. However, we need to do this check + // in order to sector offset table malformed by MPQ protectors + if((dwSectorOffset1 - dwSectorOffset0) > ha->dwSectorSize) { bSectorOffsetTableCorrupt = true; break; @@ -1064,15 +1369,26 @@ int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile) // 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 + // These extra values are, however, included in the dwCmpSize in the file // table. We cannot ignore them, because compacting archive would fail - // + // - if(hf->SectorOffsets[0] > dwSectorOffsLen) + // Clear the lower 2 bits in order to make sure that the value is aligned to 4 bytes + if((hf->SectorOffsets[0] & 0xFFFFFFFC) > dwSectorOffsLen) { - dwSectorOffsLen = hf->SectorOffsets[0]; + // MPQ protectors put some ridiculous values there. We must limit the extra bytes + if(hf->SectorOffsets[0] > (dwSectorOffsLen + 0x400)) + { + STORM_FREE(hf->SectorOffsets); + hf->SectorOffsets = NULL; + return ERROR_FILE_CORRUPT; + } + + // The new length of the sector offset must be aligned to DWORD + dwSectorOffsLen = (hf->SectorOffsets[0] & 0xFFFFFFFC); + + // Free the old sector offset table STORM_FREE(hf->SectorOffsets); - hf->SectorOffsets = NULL; goto __LoadSectorOffsets; } } @@ -1086,7 +1402,7 @@ int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile) return ERROR_SUCCESS; } -int AllocateSectorChecksums(TMPQFile * hf, bool bLoadFromFile) +DWORD AllocateSectorChecksums(TMPQFile * hf, bool bLoadFromFile) { TMPQArchive * ha = hf->ha; TFileEntry * pFileEntry = hf->pFileEntry; @@ -1109,7 +1425,7 @@ 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); - // + // // Older MPQs store an array of CRC32's after // the raw file data in the MPQ. // @@ -1122,35 +1438,40 @@ int AllocateSectorChecksums(TMPQFile * hf, bool bLoadFromFile) // Does the size of the file table match with the CRC32-based checksums? dwExpectedSize = (hf->dwSectorCount + 2) * sizeof(DWORD); - if(hf->SectorOffsets[0] == dwExpectedSize) + if(hf->SectorOffsets[0] != 0 && hf->SectorOffsets[0] == dwExpectedSize) { - // 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]; - - // Ignore cases when the length is too small or too big. - if(dwCompressedSize < sizeof(DWORD) || dwCompressedSize > hf->dwSectorSize) - return ERROR_SUCCESS; - - // Allocate the array for the sector checksums - hf->SectorChksums = STORM_ALLOC(DWORD, hf->dwSectorCount); - if(hf->SectorChksums == NULL) - return ERROR_NOT_ENOUGH_MEMORY; - - // If we are not supposed to load it from the file, allocate empty buffer + // If we are not loading from the MPQ file, we just allocate the sector table + // In that case, do not check any sizes if(bLoadFromFile == false) { + hf->SectorChksums = STORM_ALLOC(DWORD, hf->dwSectorCount); + if(hf->SectorChksums == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + // Fill the checksum table with zeros memset(hf->SectorChksums, 0, hf->dwSectorCount * sizeof(DWORD)); return ERROR_SUCCESS; } + else + { + // 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]; - // Calculate offset of the CRC table - dwCrcSize = hf->dwSectorCount * sizeof(DWORD); - dwCrcOffset = hf->SectorOffsets[hf->dwSectorCount]; - CalculateRawSectorOffset(RawFilePos, hf, dwCrcOffset); + // Ignore cases when the length is too small or too big. + if(dwCompressedSize < sizeof(DWORD) || dwCompressedSize > hf->dwSectorSize) + return ERROR_SUCCESS; - // Now read the table from the MPQ - return LoadMpqTable(ha, RawFilePos, hf->SectorChksums, dwCompressedSize, dwCrcSize, 0); + // Calculate offset of the CRC table + dwCrcSize = hf->dwSectorCount * sizeof(DWORD); + dwCrcOffset = hf->SectorOffsets[hf->dwSectorCount]; + RawFilePos = CalculateRawSectorOffset(hf, dwCrcOffset); + + // Now read the table from the MPQ + hf->SectorChksums = (DWORD *)LoadMpqTable(ha, RawFilePos, NULL, dwCompressedSize, dwCrcSize, 0, NULL); + if(hf->SectorChksums == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + } } // If the size doesn't match, we ignore sector checksums @@ -1158,7 +1479,7 @@ int AllocateSectorChecksums(TMPQFile * hf, bool bLoadFromFile) return ERROR_SUCCESS; } -int WritePatchInfo(TMPQFile * hf) +DWORD WritePatchInfo(TMPQFile * hf) { TMPQArchive * ha = hf->ha; TPatchInfo * pPatchInfo = hf->pPatchInfo; @@ -1170,12 +1491,12 @@ int WritePatchInfo(TMPQFile * hf) BSWAP_ARRAY32_UNSIGNED(pPatchInfo, 3 * sizeof(DWORD)); if(!FileStream_Write(ha->pStream, &hf->RawFilePos, pPatchInfo, sizeof(TPatchInfo))) - return GetLastError(); + return SErrGetLastError(); return ERROR_SUCCESS; } -int WriteSectorOffsets(TMPQFile * hf) +DWORD WriteSectorOffsets(TMPQFile * hf) { TMPQArchive * ha = hf->ha; TFileEntry * pFileEntry = hf->pFileEntry; @@ -1184,7 +1505,7 @@ int WriteSectorOffsets(TMPQFile * hf) // The caller must make sure that this function is only called // when the following is true. - assert(hf->pFileEntry->dwFlags & MPQ_FILE_COMPRESSED); + assert(hf->pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK); assert(hf->SectorOffsets != NULL); dwSectorOffsLen = hf->SectorOffsets[0]; @@ -1199,25 +1520,24 @@ int WriteSectorOffsets(TMPQFile * hf) // Write sector offsets to the archive if(!FileStream_Write(ha->pStream, &RawFilePos, hf->SectorOffsets, dwSectorOffsLen)) - return GetLastError(); - + return SErrGetLastError(); + // Not necessary, as the sector checksums // are going to be freed when this is done. // BSWAP_ARRAY32_UNSIGNED(hf->SectorOffsets, dwSectorOffsLen); return ERROR_SUCCESS; } - -int WriteSectorChecksums(TMPQFile * hf) +DWORD WriteSectorChecksums(TMPQFile * hf) { TMPQArchive * ha = hf->ha; ULONGLONG RawFilePos; TFileEntry * pFileEntry = hf->pFileEntry; LPBYTE pbCompressed; DWORD dwCompressedSize = 0; + DWORD dwErrCode = ERROR_SUCCESS; DWORD dwCrcSize; int nOutSize; - int nError = ERROR_SUCCESS; // The caller must make sure that this function is only called // when the following is true. @@ -1245,7 +1565,7 @@ int WriteSectorChecksums(TMPQFile * hf) BSWAP_ARRAY32_UNSIGNED(hf->SectorChksums, dwCrcSize); nOutSize = (int)dwCrcSize; - SCompCompress((char *)pbCompressed, &nOutSize, (char *)hf->SectorChksums, (int)dwCrcSize, MPQ_COMPRESSION_ZLIB, 0, 0); + SCompCompress(pbCompressed, &nOutSize, hf->SectorChksums, (int)dwCrcSize, MPQ_COMPRESSION_ZLIB, 0, 0); dwCompressedSize = (DWORD)nOutSize; // Write the sector CRCs to the archive @@ -1253,20 +1573,20 @@ int WriteSectorChecksums(TMPQFile * hf) if(hf->pPatchInfo != NULL) RawFilePos += hf->pPatchInfo->dwLength; if(!FileStream_Write(ha->pStream, &RawFilePos, pbCompressed, dwCompressedSize)) - nError = GetLastError(); + dwErrCode = SErrGetLastError(); // Not necessary, as the sector checksums // are going to be freed when this is done. // 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; STORM_FREE(pbCompressed); - return nError; + return dwErrCode; } -int WriteMemDataMD5( +DWORD WriteMemDataMD5( TFileStream * pStream, ULONGLONG RawDataOffs, void * pvRawData, @@ -1279,7 +1599,7 @@ int WriteMemDataMD5( LPBYTE pbRawData = (LPBYTE)pvRawData; DWORD dwBytesRemaining = dwRawDataSize; DWORD dwMd5ArraySize = 0; - int nError = ERROR_SUCCESS; + DWORD dwErrCode = ERROR_SUCCESS; // Allocate buffer for array of MD5 md5_array = md5 = AllocateMd5Buffer(dwRawDataSize, dwChunkSize, &dwMd5ArraySize); @@ -1304,7 +1624,7 @@ int WriteMemDataMD5( // Write the array od MD5's to the file RawDataOffs += dwRawDataSize; if(!FileStream_Write(pStream, &RawDataOffs, md5_array, dwMd5ArraySize)) - nError = GetLastError(); + dwErrCode = SErrGetLastError(); // Give the caller the size of the MD5 array if(pcbTotalSize != NULL) @@ -1312,12 +1632,12 @@ int WriteMemDataMD5( // Free buffers and exit STORM_FREE(md5_array); - return nError; + return dwErrCode; } // Writes the MD5 for each chunk of the raw file data -int WriteMpqDataMD5( +DWORD WriteMpqDataMD5( TFileStream * pStream, ULONGLONG RawDataOffs, DWORD dwRawDataSize, @@ -1328,7 +1648,7 @@ int WriteMpqDataMD5( LPBYTE pbFileChunk; DWORD dwMd5ArraySize = 0; DWORD dwToRead = dwRawDataSize; - int nError = ERROR_SUCCESS; + DWORD dwErrCode = ERROR_SUCCESS; // Allocate buffer for array of MD5 md5_array = md5 = AllocateMd5Buffer(dwRawDataSize, dwChunkSize, &dwMd5ArraySize); @@ -1352,7 +1672,7 @@ int WriteMpqDataMD5( // Read the chunk if(!FileStream_Read(pStream, &RawDataOffs, pbFileChunk, dwToRead)) { - nError = GetLastError(); + dwErrCode = SErrGetLastError(); break; } @@ -1366,110 +1686,16 @@ int WriteMpqDataMD5( } // Write the array od MD5's to the file - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { if(!FileStream_Write(pStream, NULL, md5_array, dwMd5ArraySize)) - nError = GetLastError(); + dwErrCode = SErrGetLastError(); } // Free buffers and exit STORM_FREE(pbFileChunk); STORM_FREE(md5_array); - return nError; -} - -// Frees the structure for MPQ file -void FreeMPQFile(TMPQFile *& hf) -{ - if(hf != NULL) - { - // If we have patch file attached to this one, free it first - if(hf->hfPatchFile != NULL) - FreeMPQFile(hf->hfPatchFile); - - // Then free all buffers allocated in the file structure - if(hf->pPatchHeader != NULL) - STORM_FREE(hf->pPatchHeader); - if(hf->pbFileData != NULL) - STORM_FREE(hf->pbFileData); - if(hf->pPatchInfo != NULL) - STORM_FREE(hf->pPatchInfo); - if(hf->SectorOffsets != NULL) - STORM_FREE(hf->SectorOffsets); - if(hf->SectorChksums != NULL) - STORM_FREE(hf->SectorChksums); - if(hf->pbFileSector != NULL) - STORM_FREE(hf->pbFileSector); - FileStream_Close(hf->pStream); - STORM_FREE(hf); - hf = NULL; - } -} - -// Frees the MPQ archive -void FreeMPQArchive(TMPQArchive *& ha) -{ - if(ha != NULL) - { - // First of all, free the patch archive, if any - 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) - STORM_FREE(ha->pFileTable[i].szFileName); - ha->pFileTable[i].szFileName = NULL; - } - - // Then free all buffers allocated in the archive structure - STORM_FREE(ha->pFileTable); - } - - if(ha->pBitmap != NULL) - STORM_FREE(ha->pBitmap); - if(ha->pHashTable != NULL) - STORM_FREE(ha->pHashTable); - if(ha->pHetTable != NULL) - FreeHetTable(ha->pHetTable); - STORM_FREE(ha); - ha = NULL; - } -} - -const char * GetPlainFileNameA(const char * szFileName) -{ - const char * szPlainName = szFileName; - - while(*szFileName != 0) - { - 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; + return dwErrCode; } bool IsInternalMpqFileName(const char * szFileName) @@ -1524,30 +1750,42 @@ bool IsPseudoFileName(const char * szFileName, DWORD * pdwFileIndex) 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); + LPDWORD Md5 = (LPDWORD)pbMd5; + + return ((Md5 != NULL) && (Md5[0] | Md5[1] | Md5[2] | Md5[3])) ? true : false; } +bool IsValidSignature(LPBYTE pbSignature) +{ + LPDWORD Signature = (LPDWORD)pbSignature; + DWORD SigValid = 0; + + for(int i = 0; i < MPQ_WEAK_SIGNATURE_SIZE / sizeof(DWORD); i++) + SigValid |= Signature[i]; + + return (SigValid != 0) ? true : false; +} + + bool VerifyDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE expected_md5) { hash_state md5_state; BYTE md5_digest[MD5_DIGEST_SIZE]; + bool bResult = true; // Don't verify the block if the MD5 is not valid. - if(!IsValidMD5(expected_md5)) - return true; + if(IsValidMD5(expected_md5)) + { + // 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); - // 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? + bResult = (memcmp(md5_digest, expected_md5, MD5_DIGEST_SIZE) == 0); + } - // Does the MD5's match? - return (memcmp(md5_digest, expected_md5, MD5_DIGEST_SIZE) == 0); + return bResult; } void CalculateDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE md5_hash) @@ -1559,51 +1797,168 @@ void CalculateDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE md5_ha md5_done(&md5_state, md5_hash); } +//----------------------------------------------------------------------------- +// Free the handle structures + +static void DeleteArchiveHandle(TMPQArchive * ha) +{ + // Sanity check + assert(ha->dwFileCount == 0 && ha->dwRefCount == 0); + + // Invalidate the add file callback so it won't be called + // when saving (listfile) and (attributes) + ha->pfnAddFileCB = NULL; + ha->pvAddFileUserData = NULL; + + // Flush all unsaved data to the storage + SFileFlushArchive((HANDLE)(ha)); + assert(ha->dwFileCount == 0 && ha->dwRefCount == 0); + + // First of all, free the patch archive, if any + if(ha->haPatch != NULL) + DereferenceArchive(ha->haPatch); + ha->haPatch = NULL; + + // Free the patch prefix, if any + if(ha->pPatchPrefix != NULL) + STORM_FREE(ha->pPatchPrefix); + + // 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) + STORM_FREE(ha->pFileTable[i].szFileName); + ha->pFileTable[i].szFileName = NULL; + } + + // Then free all buffers allocated in the archive structure + STORM_FREE(ha->pFileTable); + } + + if(ha->pHashTable != NULL) + STORM_FREE(ha->pHashTable); + if(ha->pHetTable != NULL) + FreeHetTable(ha->pHetTable); + STORM_FREE(ha); +} + +void FreeFileHandle(TMPQFile *& hf) +{ + TMPQArchive * ha; + + if(hf != NULL) + { + // If we have patch file attached to this one, free it first + if(hf->hfPatch != NULL) + FreeFileHandle(hf->hfPatch); + + // Then free all buffers allocated in the file structure + if(hf->pbFileData != NULL) + STORM_FREE(hf->pbFileData); + if(hf->pPatchInfo != NULL) + STORM_FREE(hf->pPatchInfo); + if(hf->SectorOffsets != NULL) + STORM_FREE(hf->SectorOffsets); + if(hf->SectorChksums != NULL) + STORM_FREE(hf->SectorChksums); + if(hf->hctx != NULL) + STORM_FREE(hf->hctx); + if(hf->pbFileSector != NULL) + STORM_FREE(hf->pbFileSector); + if(hf->pStream != NULL) + FileStream_Close(hf->pStream); + + // Dereference file count in the archive handle + if((ha = hf->ha) != NULL) + DereferenceArchiveFiles(ha); + + // Free the file handle + STORM_FREE(hf); + hf = NULL; + } +} + +bool DereferenceArchiveFiles(TMPQArchive * ha) +{ + // There must be at least one reference + if(ha == NULL || ha->dwFileCount == 0) + return false; + + // Decrement the file count + ha->dwFileCount--; + + // If we reached zero, free the archive + if(ha->dwRefCount == 0 && ha->dwFileCount == 0) + DeleteArchiveHandle(ha); + return true; +} + +bool DereferenceArchive(TMPQArchive * ha) +{ + // There must be at least one reference + if(ha == NULL || ha->dwRefCount == 0) + return false; + + // Decrement the file count + ha->dwRefCount--; + + // If we reached zero, free the archive + if(ha->dwRefCount == 0 && ha->dwFileCount == 0) + DeleteArchiveHandle(ha); + return true; +} //----------------------------------------------------------------------------- // Swapping functions -#ifndef PLATFORM_LITTLE_ENDIAN - -// -// Note that those functions are implemented for Mac operating system, -// as this is the only supported platform that uses big endian. -// +#ifndef STORMLIB_LITTLE_ENDIAN // Swaps a signed 16-bit integer -int16_t SwapInt16(uint16_t data) +int16_t SwapInt16(uint16_t val) { - return (int16_t)CFSwapInt16(data); + return (val << 8) | ((val >> 8) & 0xFF); } // Swaps an unsigned 16-bit integer -uint16_t SwapUInt16(uint16_t data) +uint16_t SwapUInt16(uint16_t val) { - return CFSwapInt16(data); + return (val << 8) | (val >> 8 ); } -// Swaps signed 32-bit integer -int32_t SwapInt32(uint32_t data) +// Swaps a signed 32-bit integer +int32_t SwapInt32(uint32_t val) { - return (int32_t)CFSwapInt32(data); + val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF ); + return (val << 16) | ((val >> 16) & 0xFFFF); } // Swaps an unsigned 32-bit integer -uint32_t SwapUInt32(uint32_t data) +uint32_t SwapUInt32(uint32_t val) { - return CFSwapInt32(data); + val = ((val << 8) & 0xFF00FF00 ) | ((val >> 8) & 0xFF00FF ); + return (val << 16) | (val >> 16); } -// Swaps signed 64-bit integer -int64_t SwapInt64(int64_t data) +// Swaps a signed 64-bit integer +int64_t SwapInt64(uint64_t val) { - return (int64_t)CFSwapInt64(data); + val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL ); + val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL ); + return (val << 32) | ((val >> 32) & 0xFFFFFFFFULL); } // Swaps an unsigned 64-bit integer -uint64_t SwapUInt64(uint64_t data) +uint64_t SwapUInt64(uint64_t val) { - return CFSwapInt64(data); + val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL ); + val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL ); + return (val << 32) | (val >> 32); } // Swaps array of unsigned 16-bit integers @@ -1613,10 +1968,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 @@ -1625,11 +1980,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 @@ -1638,63 +1993,80 @@ 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++; - } -} - -// Swaps the TMPQUserData structure -void ConvertTMPQUserData(void *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); + while(nElements-- > 0) + { + *buffer = SwapUInt64(*buffer); + buffer++; + } } // Swaps the TMPQHeader structure -void ConvertTMPQHeader(void *header) +void ConvertTMPQHeader(void *header, uint16_t version) { - 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); + TMPQHeader * theHeader = (TMPQHeader *)header; - if(theHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2) - { - // Swap the hi-block table position - theHeader->HiBlockTablePos64 = SwapUInt64(theHeader->HiBlockTablePos64); - + // Swap header part version 1 + if(version >= MPQ_FORMAT_VERSION_1) + { + 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(version >= MPQ_FORMAT_VERSION_2) + { + 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(version >= 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); - theHeader->HetTableSize64 = SwapUInt64(theHeader->HetTableSize64); - theHeader->BetTableSize64 = SwapUInt64(theHeader->BetTableSize64); - } - } - } + if(version >= MPQ_FORMAT_VERSION_4) + { + theHeader->HashTableSize64 = SwapUInt64(theHeader->HashTableSize64); + theHeader->BlockTableSize64 = SwapUInt64(theHeader->BlockTableSize64); + theHeader->HiBlockTableSize64 = SwapUInt64(theHeader->HiBlockTableSize64); + theHeader->HetTableSize64 = SwapUInt64(theHeader->HetTableSize64); + theHeader->BetTableSize64 = SwapUInt64(theHeader->BetTableSize64); + } } -#endif // PLATFORM_LITTLE_ENDIAN +#endif // STORMLIB_LITTLE_ENDIAN + +//----------------------------------------------------------------------------- +// Debug support + +/* +#include + +#if defined(STORMLIB_WINDOWS) && defined(_DEBUG) +void SFileLog(const char * format, ...) +{ + va_list argList; + char * szBuffer; + size_t nLength = 0x1000; + + if((szBuffer = STORM_ALLOC(char, 0x1000)) != NULL) + { + va_start(argList, format); + StringCchVPrintfA(szBuffer, nLength, format, argList); + va_end(argList); + + OutputDebugStringA(szBuffer); + STORM_FREE(szBuffer); + } +} +#endif +*/ \ No newline at end of file diff --git a/dep/StormLib/src/SBaseDumpData.cpp b/dep/StormLib/src/SBaseDumpData.cpp index c9dcf0a336..f52b927c30 100644 --- a/dep/StormLib/src/SBaseDumpData.cpp +++ b/dep/StormLib/src/SBaseDumpData.cpp @@ -41,6 +41,26 @@ void DumpMpqHeader(TMPQHeader * pHeader) printf("-----------------------------------------------\n\n"); } +void DumpHashTable(TMPQHash * pHashTable, DWORD dwHashTableSize) +{ + DWORD i; + + if(pHashTable == NULL || dwHashTableSize == 0) + return; + + printf("== Hash Table =================================\n"); + for(i = 0; i < dwHashTableSize; i++) + { + printf("[%08x] %08X %08X %04X %02X %08X\n", i, + pHashTable[i].dwHashCheck1, + pHashTable[i].dwHashCheck2, + pHashTable[i].Locale, + pHashTable[i].Platform, + pHashTable[i].dwBlockIndex); + } + printf("-----------------------------------------------\n\n"); +} + void DumpHetAndBetTable(TMPQHetTable * pHetTable, TMPQBetTable * pBetTable) { DWORD i; @@ -49,14 +69,14 @@ 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("ULONGLONG AndMask64 = %016llX\n", pHetTable->AndMask64); + printf("ULONGLONG OrMask64 = %016llX\n", pHetTable->OrMask64); + printf("DWORD dwEntryCount = %08X\n", pHetTable->dwEntryCount); + printf("DWORD dwTotalCount = %08X\n", pHetTable->dwTotalCount); + printf("DWORD dwNameHashBitSize = %08X\n", pHetTable->dwNameHashBitSize); 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("DWORD dwIndexSize = %08X\n", pHetTable->dwIndexSize); printf("-----------------------------------------------\n\n"); printf("== BET Header =================================\n"); @@ -69,19 +89,19 @@ void DumpHetAndBetTable(TMPQHetTable * pHetTable, TMPQBetTable * pBetTable) 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_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 dwBitTotal_NameHash2 = %08X\n", pBetTable->dwBitTotal_NameHash2); + printf("DWORD dwBitExtra_NameHash2 = %08X\n", pBetTable->dwBitExtra_NameHash2); + printf("DWORD dwBitCount_NameHash2 = %08X\n", pBetTable->dwBitCount_NameHash2); + printf("DWORD dwEntryCount = %08X\n", pBetTable->dwEntryCount); printf("DWORD dwFlagCount = %08X\n", pBetTable->dwFlagCount); printf("-----------------------------------------------\n\n"); printf("== HET & Bet Table ======================================================================\n\n"); printf("HetIdx HetHash BetIdx BetHash ByteOffset FileSize CmpSize FlgIdx Flags \n"); printf("------ ------- ------ ---------------- ---------------- -------- -------- ------ --------\n"); - for(i = 0; i < pHetTable->dwHashTableSize; i++) + for(i = 0; i < pHetTable->dwTotalCount; i++) { ULONGLONG ByteOffset = 0; ULONGLONG BetHash = 0; @@ -91,45 +111,39 @@ void DumpHetAndBetTable(TMPQHetTable * pHetTable, TMPQBetTable * pBetTable) DWORD dwFlags = 0; DWORD dwBetIndex = 0; - pHetTable->pBetIndexes->GetBits(i * pHetTable->dwIndexSizeTotal, - pHetTable->dwIndexSize, - &dwBetIndex, - 4); - - if(dwBetIndex < pHetTable->dwMaxFileCount) + GetMPQBits(pHetTable->pBetIndexes, i * pHetTable->dwIndexSizeTotal, + pHetTable->dwIndexSize, + &dwBetIndex, 4); + + if(dwBetIndex < pHetTable->dwTotalCount) { DWORD dwEntryIndex = pBetTable->dwTableEntrySize * dwBetIndex; - pBetTable->pBetHashes->GetBits(dwBetIndex * pBetTable->dwBetHashSizeTotal, - pBetTable->dwBetHashSize, - &BetHash, - 8); + GetMPQBits(pBetTable->pNameHashes, dwBetIndex * pBetTable->dwBitTotal_NameHash2, + pBetTable->dwBitCount_NameHash2, + &BetHash, 8); - pBetTable->pFileTable->GetBits(dwEntryIndex + pBetTable->dwBitIndex_FilePos, - pBetTable->dwBitCount_FilePos, - &ByteOffset, - 8); + GetMPQBits(pBetTable->pFileTable, dwEntryIndex + pBetTable->dwBitIndex_FilePos, + pBetTable->dwBitCount_FilePos, + &ByteOffset, 8); - pBetTable->pFileTable->GetBits(dwEntryIndex + pBetTable->dwBitIndex_FileSize, - pBetTable->dwBitCount_FileSize, - &dwFileSize, - 4); + GetMPQBits(pBetTable->pFileTable, dwEntryIndex + pBetTable->dwBitIndex_FileSize, + pBetTable->dwBitCount_FileSize, + &dwFileSize, 4); - pBetTable->pFileTable->GetBits(dwEntryIndex + pBetTable->dwBitIndex_CmpSize, - pBetTable->dwBitCount_CmpSize, - &dwCmpSize, - 4); + GetMPQBits(pBetTable->pFileTable, dwEntryIndex + pBetTable->dwBitIndex_CmpSize, + pBetTable->dwBitCount_CmpSize, + &dwCmpSize, 4); - pBetTable->pFileTable->GetBits(dwEntryIndex + pBetTable->dwBitIndex_FlagIndex, - pBetTable->dwBitCount_FlagIndex, - &dwFlagIndex, - 4); + GetMPQBits(pBetTable->pFileTable, dwEntryIndex + pBetTable->dwBitIndex_FlagIndex, + pBetTable->dwBitCount_FlagIndex, + &dwFlagIndex, 4); dwFlags = pBetTable->pFileFlags[dwFlagIndex]; } printf(" %04X %02lX %04X %016llX %016llX %08X %08X %04X %08X\n", i, - pHetTable->pHetHashes[i], + pHetTable->pNameHashes[i], dwBetIndex, BetHash, ByteOffset, @@ -141,4 +155,29 @@ void DumpHetAndBetTable(TMPQHetTable * pHetTable, TMPQBetTable * pBetTable) printf("-----------------------------------------------------------------------------------------\n"); } +void DumpFileTable(TFileEntry * pFileTable, DWORD dwFileTableSize) +{ + DWORD i; + + if(pFileTable == NULL || dwFileTableSize == 0) + return; + + printf("== File Table =================================\n"); + for(i = 0; i < dwFileTableSize; i++, pFileTable++) + { + printf("[%04u] %08X-%08X %08X-%08X %08X-%08X 0x%08X 0x%08X 0x%08X %s\n", i, + (DWORD)(pFileTable->FileNameHash >> 0x20), + (DWORD)(pFileTable->FileNameHash & 0xFFFFFFFF), + (DWORD)(pFileTable->ByteOffset >> 0x20), + (DWORD)(pFileTable->ByteOffset & 0xFFFFFFFF), + (DWORD)(pFileTable->FileTime >> 0x20), + (DWORD)(pFileTable->FileTime & 0xFFFFFFFF), + pFileTable->dwFileSize, + pFileTable->dwCmpSize, + pFileTable->dwFlags, + pFileTable->szFileName != NULL ? pFileTable->szFileName : ""); + } + printf("-----------------------------------------------\n\n"); +} + #endif // __STORMLIB_DUMP_DATA__ diff --git a/dep/StormLib/src/SBaseFileTable.cpp b/dep/StormLib/src/SBaseFileTable.cpp index 09379ca5da..40d4d25cf5 100644 --- a/dep/StormLib/src/SBaseFileTable.cpp +++ b/dep/StormLib/src/SBaseFileTable.cpp @@ -18,55 +18,12 @@ #define INVALID_FLAG_VALUE 0xCCCCCCCC #define MAX_FLAG_INDEX 512 -//----------------------------------------------------------------------------- -// Local structures - -// Structure for HET table header -typedef struct _HET_TABLE_HEADER -{ - DWORD dwTableSize; // Size of the entire HET table, including HET_TABLE_HEADER (in bytes) - DWORD dwMaxFileCount; // Maximum number of files in the MPQ - DWORD dwHashTableSize; // Size of the hash table (in bytes) - DWORD dwHashEntrySize; // Effective size of the hash entry (in bits) - DWORD dwIndexSizeTotal; // Total size of file index (in bits) - DWORD dwIndexSizeExtra; // Extra bits in the file index - DWORD dwIndexSize; // Effective size of the file index (in bits) - DWORD dwIndexTableSize; // Size of the block index subtable (in bytes) - -} HET_TABLE_HEADER, *PHET_TABLE_HEADER; - -// Structure for BET table header -typedef struct _BET_TABLE_HEADER -{ - DWORD dwTableSize; // Size of the entire BET table, including the header (in bytes) - DWORD dwFileCount; // Number of files in the BET table - DWORD dwUnknown08; - DWORD dwTableEntrySize; // Size of one table entry (in bits) - DWORD dwBitIndex_FilePos; // Bit index of the file position (within the entry record) - DWORD dwBitIndex_FileSize; // Bit index of the file size (within the entry record) - DWORD dwBitIndex_CmpSize; // Bit index of the compressed size (within the entry record) - DWORD dwBitIndex_FlagIndex; // Bit index of the flag index (within the entry record) - DWORD dwBitIndex_Unknown; // Bit index of the ??? (within the entry record) - DWORD dwBitCount_FilePos; // Bit size of file position (in the entry record) - DWORD dwBitCount_FileSize; // Bit size of file size (in the entry record) - DWORD dwBitCount_CmpSize; // Bit size of compressed file size (in the entry record) - DWORD dwBitCount_FlagIndex; // Bit size of flags index (in the entry record) - DWORD dwBitCount_Unknown; // Bit size of ??? (in the entry record) - DWORD dwBetHashSizeTotal; // Total size of the BET hash - DWORD dwBetHashSizeExtra; // Extra bits in the BET hash - DWORD dwBetHashSize; // Effective size of BET hash (in bits) - DWORD dwBetHashArraySize; // Size of BET hashes array, in bytes - DWORD dwFlagCount; // Number of flags in the following array - -} BET_TABLE_HEADER, *PBET_TABLE_HEADER; - //----------------------------------------------------------------------------- // Support for calculating bit sizes static void InitFileFlagArray(LPDWORD FlagArray) { - for(DWORD dwFlagIndex = 0; dwFlagIndex < MAX_FLAG_INDEX; dwFlagIndex++) - FlagArray[dwFlagIndex] = INVALID_FLAG_VALUE; + memset(FlagArray, 0xCC, MAX_FLAG_INDEX * sizeof(DWORD)); } static DWORD GetFileFlagIndex(LPDWORD FlagArray, DWORD dwFlags) @@ -95,38 +52,53 @@ static DWORD GetNecessaryBitCount(ULONGLONG MaxValue) MaxValue >>= 1; dwBitCount++; } - + return dwBitCount; } //----------------------------------------------------------------------------- -// Support functions for BIT_ARRAY +// Implementation of the TMPQBits struct -static USHORT SetBitsMask[] = {0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF}; +struct TMPQBits +{ + static TMPQBits * Create(DWORD NumberOfBits, BYTE FillValue); -static TBitArray * CreateBitArray( + DWORD GetBits(unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, unsigned int nResultSize); + DWORD SetBits(unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, unsigned int nResultSize); + + static const USHORT SetBitsMask[]; // Bit mask for each number of bits (0-8) + + DWORD NumberOfBytes; // Total number of bytes in "Elements" + DWORD NumberOfBits; // Total number of bits that are available + BYTE Elements[1]; // Array of elements (variable length) +}; + +const USHORT TMPQBits::SetBitsMask[] = {0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF}; + +TMPQBits * TMPQBits::Create( DWORD NumberOfBits, BYTE FillValue) { - TBitArray * pBitArray; - size_t nSize = sizeof(TBitArray) + (NumberOfBits + 7) / 8; + TMPQBits * pBitArray; + size_t nSize = sizeof(TMPQBits) + (NumberOfBits + 7) / 8; // Allocate the bit array - pBitArray = (TBitArray *)STORM_ALLOC(BYTE, nSize); + pBitArray = (TMPQBits *)STORM_ALLOC(BYTE, nSize); if(pBitArray != NULL) { memset(pBitArray, FillValue, nSize); + pBitArray->NumberOfBytes = (NumberOfBits + 7) / 8; pBitArray->NumberOfBits = NumberOfBits; } return pBitArray; } -void TBitArray::GetBits( +DWORD TMPQBits::GetBits( unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, - int nResultByteSize) + unsigned int nResultByteSize) { unsigned char * pbBuffer = (unsigned char *)pvBuffer; unsigned int nBytePosition0 = (nBitPosition / 8); @@ -135,19 +107,24 @@ void TBitArray::GetBits( unsigned int nBitOffset = (nBitPosition & 0x07); unsigned char BitBuffer; - // Keep compiler happy for platforms where nResultByteSize is not used - nResultByteSize = nResultByteSize; + // Check for bit overflow + if(nBitPosition + nBitLength < nBitPosition) + return ERROR_BUFFER_OVERFLOW; + if(nBitPosition + nBitLength > NumberOfBits) + return ERROR_BUFFER_OVERFLOW; + if(nByteLength > nResultByteSize) + return ERROR_BUFFER_OVERFLOW; #ifdef _DEBUG // Check if the target is properly zeroed - for(int i = 0; i < nResultByteSize; i++) + for(unsigned int i = 0; i < nResultByteSize; i++) assert(pbBuffer[i] == 0); #endif -#ifndef PLATFORM_LITTLE_ENDIAN +#ifndef STORMLIB_LITTLE_ENDIAN // Adjust the buffer pointer for big endian platforms pbBuffer += (nResultByteSize - 1); -#endif +#endif // Copy whole bytes, if any while(nByteLength > 0) @@ -162,7 +139,7 @@ void TBitArray::GetBits( BitBuffer = Elements[nBytePosition0]; } -#ifdef PLATFORM_LITTLE_ENDIAN +#ifdef STORMLIB_LITTLE_ENDIAN *pbBuffer++ = BitBuffer; #else *pbBuffer-- = BitBuffer; @@ -185,13 +162,14 @@ void TBitArray::GetBits( *pbBuffer &= (0x01 << nBitLength) - 1; } + return ERROR_SUCCESS; } -void TBitArray::SetBits( +DWORD TMPQBits::SetBits( unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, - int nResultByteSize) + unsigned int nResultByteSize) { unsigned char * pbBuffer = (unsigned char *)pvBuffer; unsigned int nBytePosition = (nBitPosition / 8); @@ -200,19 +178,27 @@ void TBitArray::SetBits( unsigned short AndMask = 0; unsigned short OneByte = 0; - // Keep compiler happy for platforms where nResultByteSize is not used - nResultByteSize = nResultByteSize; + // Keep compilers happy for platforms where nResultByteSize is not used + STORMLIB_UNUSED(nResultByteSize); -#ifndef PLATFORM_LITTLE_ENDIAN + // Check for bit overflow + if(nBitPosition + nBitLength < nBitPosition) + return ERROR_BUFFER_OVERFLOW; + if(nBitPosition + nBitLength > NumberOfBits) + return ERROR_BUFFER_OVERFLOW; + if(nBitLength / 8 > nResultByteSize) + return ERROR_BUFFER_OVERFLOW; + +#ifndef STORMLIB_LITTLE_ENDIAN // Adjust the buffer pointer for big endian platforms pbBuffer += (nResultByteSize - 1); -#endif +#endif // Copy whole bytes, if any while(nBitLength > 8) { // Reload the bit buffer -#ifdef PLATFORM_LITTLE_ENDIAN +#ifdef STORMLIB_LITTLE_ENDIAN OneByte = *pbBuffer++; #else OneByte = *pbBuffer--; @@ -251,90 +237,850 @@ void TBitArray::SetBits( Elements[nBytePosition] = (BYTE)((Elements[nBytePosition] & ~AndMask) | BitBuffer); } } + return ERROR_SUCCESS; } +void GetMPQBits(TMPQBits * pBits, unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, int nResultByteSize) +{ + pBits->GetBits(nBitPosition, nBitLength, pvBuffer, nResultByteSize); +} + +//----------------------------------------------------------------------------- +// Support for MPQ header + +static bool VerifyTablePosition64( + ULONGLONG MpqOffset, // Position of the MPQ header + ULONGLONG TableOffset, // Position of the MPQ table, relative to MPQ header + ULONGLONG TableSize, // Size of the MPQ table, in bytes + ULONGLONG FileSize) // Size of the entire file, in bytes +{ + if(TableOffset != 0) + { + // Verify overflows + if((MpqOffset + TableOffset) < MpqOffset) + return false; + if((MpqOffset + TableOffset + TableSize) < MpqOffset) + return false; + + // Verify sizes + if(TableOffset >= FileSize || TableSize >= FileSize) + return false; + if((MpqOffset + TableOffset) >= FileSize) + return false; + if((MpqOffset + TableOffset + TableSize) >= FileSize) + return false; + } + return true; +} + +static bool VerifyTableTandemPositions( + ULONGLONG MpqOffset, // Position of the MPQ header + ULONGLONG TableOffset1, // 1st table: Position, relative to MPQ header + ULONGLONG TableSize1, // 1st table: Size in bytes + ULONGLONG TableOffset2, // 2nd table: Position, relative to MPQ header + ULONGLONG TableSize2, // 2nd table: Size in bytes + ULONGLONG FileSize) // Size of the entire file, in bytes +{ + return VerifyTablePosition64(MpqOffset, TableOffset1, TableSize1, FileSize) && + VerifyTablePosition64(MpqOffset, TableOffset2, TableSize2, FileSize); +} + +static ULONGLONG DetermineArchiveSize_V1( + TMPQArchive * ha, + TMPQHeader * pHeader, + ULONGLONG MpqOffset, + ULONGLONG FileSize) +{ + ULONGLONG ByteOffset; + ULONGLONG EndOfMpq = FileSize; + DWORD SignatureHeader = 0; + DWORD dwArchiveSize32; + + // This could only be called for MPQs version 1.0 + assert(pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1); + + // Check if we can rely on the archive size in the header + if(pHeader->dwBlockTablePos < pHeader->dwArchiveSize) + { + // The block table cannot be compressed, so the sizes must match + if((pHeader->dwArchiveSize - pHeader->dwBlockTablePos) == (pHeader->dwBlockTableSize * sizeof(TMPQBlock))) + return pHeader->dwArchiveSize; + + // If the archive size in the header is less than real file size + dwArchiveSize32 = (DWORD)(FileSize - MpqOffset); + if(pHeader->dwArchiveSize == dwArchiveSize32) + return pHeader->dwArchiveSize; + } + + // Check if there is a signature header + if((EndOfMpq - MpqOffset) > (MPQ_STRONG_SIGNATURE_SIZE + 4)) + { + ByteOffset = EndOfMpq - MPQ_STRONG_SIGNATURE_SIZE - 4; + if(FileStream_Read(ha->pStream, &ByteOffset, &SignatureHeader, sizeof(DWORD))) + { + if(BSWAP_INT32_UNSIGNED(SignatureHeader) == MPQ_STRONG_SIGNATURE_ID) + EndOfMpq = EndOfMpq - MPQ_STRONG_SIGNATURE_SIZE - 4; + } + } + + // Return the returned archive size + return (EndOfMpq - MpqOffset); +} + +static ULONGLONG DetermineBlockTableSize_V2(TMPQHeader * pHeader, ULONGLONG MpqHeaderPos, ULONGLONG FileSize) +{ + ULONGLONG BlockTablePos = MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); + ULONGLONG ArchiveSize = FileSize - MpqHeaderPos; + + // If there is a hi-block table and it is beyond the block table, + // we can determine the block table size from it + if(pHeader->HiBlockTablePos64 != 0) + { + if(pHeader->HiBlockTablePos64 > BlockTablePos) + { + return (pHeader->HiBlockTablePos64 - BlockTablePos); + } + } + + // If we have valid archive size, we can determine the block table size from the archive size + else + { + if((BlockTablePos >> 0x20) == 0 && (ArchiveSize >> 0x20) == 0) + { + DWORD dwBlockTablePos32 = (DWORD)(BlockTablePos); + DWORD dwArchiveSize32 = (DWORD)(ArchiveSize); + + if(pHeader->dwArchiveSize == dwArchiveSize32) + { + return (dwArchiveSize32 - dwBlockTablePos32); + } + } + } + + // Default is the block table size from MPQ header + return (ULONGLONG)(pHeader->dwBlockTableSize) * sizeof(TMPQBlock); +} + +static ULONGLONG DetermineArchiveSize_V4( + TMPQHeader * pHeader, + ULONGLONG /* MpqOffset */, + ULONGLONG /* FileSize */) +{ + ULONGLONG ArchiveSize = 0; + ULONGLONG EndOfTable; + + // This could only be called for MPQs version 4 + assert(pHeader->wFormatVersion == MPQ_FORMAT_VERSION_4); + + // Check position of BET table, if correct + if((pHeader->BetTablePos64 >> 0x20) == 0 && (pHeader->BetTableSize64 >> 0x20) == 0) + { + EndOfTable = pHeader->BetTablePos64 + pHeader->BetTableSize64; + if(EndOfTable > ArchiveSize) + ArchiveSize = EndOfTable; + } + + // Check position of HET table, if correct + if((pHeader->HetTablePos64 >> 0x20) == 0 && (pHeader->HetTableSize64 >> 0x20) == 0) + { + EndOfTable = pHeader->HetTablePos64 + pHeader->HetTableSize64; + if(EndOfTable > ArchiveSize) + ArchiveSize = EndOfTable; + } + + EndOfTable = pHeader->dwHashTablePos + pHeader->dwHashTableSize * sizeof(TMPQHash); + if(EndOfTable > ArchiveSize) + ArchiveSize = EndOfTable; + + EndOfTable = pHeader->dwBlockTablePos + pHeader->dwBlockTableSize * sizeof(TMPQBlock); + if(EndOfTable > ArchiveSize) + ArchiveSize = EndOfTable; + + // Return the calculated archive size + return ArchiveSize; +} + +ULONGLONG GetFileOffsetMask(TMPQArchive * ha) +{ + ULONGLONG FileOffsetMask = (ULONGLONG)(-1); + + // Sanity checks + assert(ha != NULL); + assert(ha->pHeader != NULL); + + // MPQs of format 1 are 32-bit only + if(ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1) + FileOffsetMask = (ULONGLONG)(DWORD)(-1); + return FileOffsetMask; +} + +ULONGLONG FileOffsetFromMpqOffset(TMPQArchive * ha, ULONGLONG MpqOffset) +{ + return (ha->MpqPos + MpqOffset) & ha->FileOffsetMask; +} + +//ULONGLONG FileOffsetFromMpqOffset(TMPQArchive * ha, ULONGLONG MpqOffset) +//{ +// if(ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1) +// { +// // For MPQ archive v1, any file offset is only 32-bit +// return (ULONGLONG)((DWORD)ha->MpqPos + (DWORD)MpqOffset); +// } +// else +// { +// // For MPQ archive v2+, file offsets are full 64-bit +// return ha->MpqPos + MpqOffset; +// } +//} + +ULONGLONG CalculateRawSectorOffset( + TMPQFile * hf, + DWORD dwSectorOffset) +{ + ULONGLONG RawFilePos; + + // Must be used for files within a MPQ + assert(hf->ha != NULL); + assert(hf->ha->pHeader != NULL); + + // + // Some MPQ protectors place the sector offset table after the actual file data. + // Sector offsets in the sector offset table are negative. When added + // to MPQ file offset from the block table entry, the result is a correct + // position of the file data in the MPQ. + // + // For MPQs version 1.0, the offset is purely 32-bit + // + + RawFilePos = (hf->RawFilePos + dwSectorOffset) & hf->ha->FileOffsetMask; + + // We also have to add patch header size, if patch header is present + if(hf->pPatchInfo != NULL) + RawFilePos += hf->pPatchInfo->dwLength; + + // Return the result offset + return RawFilePos; +} + +// This function converts the MPQ header so it always looks like version 4 +DWORD ConvertMpqHeaderToFormat4( + TMPQArchive * ha, + ULONGLONG ByteOffset, + ULONGLONG FileSize, + DWORD dwFlags, + MTYPE MapType) +{ + TMPQHeader * pHeader = (TMPQHeader *)ha->HeaderData; + ULONGLONG BlockTablePos64 = 0; + ULONGLONG HashTablePos64 = 0; + ULONGLONG BlockTableMask = (ULONGLONG)-1; + ULONGLONG MaxOffset; + USHORT wFormatVersion = BSWAP_INT16_UNSIGNED(pHeader->wFormatVersion); + bool bHashBlockOffsetOK = false; + bool bHetBetOffsetOK = false; + DWORD dwErrCode = 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 + if((MapType == MapTypeStarcraft) || (MapType == MapTypeWarcraft3) || (dwFlags & MPQ_OPEN_FORCE_MPQ_V1)) + wFormatVersion = MPQ_FORMAT_VERSION_1; + + // Don't accept format 3 for Starcraft II maps + if((MapType == MapTypeStarcraft2) && (pHeader->wFormatVersion > MPQ_FORMAT_VERSION_2)) + wFormatVersion = MPQ_FORMAT_VERSION_4; + + // Format-specific fixes + switch(wFormatVersion) + { + case MPQ_FORMAT_VERSION_1: + + // Make sure that the MPQ Header is properly swapped + BSWAP_TMPQHEADER(pHeader, MPQ_FORMAT_VERSION_1); + + // Check for blatantly wrong MPQ header by the tables position + if(((ByteOffset + pHeader->dwHashTablePos) & 0xFFFFFFFF) > FileSize) + return ERROR_FAKE_MPQ_HEADER; + if(((ByteOffset + pHeader->dwBlockTablePos) & 0xFFFFFFFF) > FileSize) + return ERROR_FAKE_MPQ_HEADER; + + // Check for blatantly wrong MPQ header by the tables size + if(MapType == MapTypeStarcraft) + { + if((pHeader->dwHashTableSize * sizeof(TMPQHash)) & 0xF0000000) + return ERROR_FAKE_MPQ_HEADER; + if((pHeader->dwBlockTableSize * sizeof(TMPQBlock)) & 0xF0000000) + return ERROR_FAKE_MPQ_HEADER; + } + + // Check for malformed MPQ header version 1.0 + if(pHeader->wFormatVersion != MPQ_FORMAT_VERSION_1 || pHeader->dwHeaderSize != MPQ_HEADER_SIZE_V1) + { + pHeader->wFormatVersion = MPQ_FORMAT_VERSION_1; + pHeader->dwHeaderSize = MPQ_HEADER_SIZE_V1; + ha->dwFlags |= MPQ_FLAG_MALFORMED; + } + + // + // Note: The value of "dwArchiveSize" member in the MPQ header + // is ignored by Storm.dll and can contain garbage value + // ("w3xmaster" protector). + // + + Label_ArchiveVersion1: + if(pHeader->dwBlockTableSize > 1) // Prevent empty MPQs being marked as malformed + { + if(pHeader->dwHashTablePos <= pHeader->dwHeaderSize || (pHeader->dwHashTablePos & 0x80000000)) + ha->dwFlags |= MPQ_FLAG_MALFORMED; + if(pHeader->dwBlockTablePos <= pHeader->dwHeaderSize || (pHeader->dwBlockTablePos & 0x80000000)) + ha->dwFlags |= MPQ_FLAG_MALFORMED; + } + + // Only low byte of sector size is really used + if(pHeader->wSectorSize & 0xFF00) + ha->dwFlags |= MPQ_FLAG_MALFORMED; + pHeader->wSectorSize = pHeader->wSectorSize & 0xFF; + + // Fill the rest of the header + memset((LPBYTE)pHeader + MPQ_HEADER_SIZE_V1, 0, sizeof(TMPQHeader) - MPQ_HEADER_SIZE_V1); + pHeader->BlockTableSize64 = pHeader->dwBlockTableSize * sizeof(TMPQBlock); + pHeader->HashTableSize64 = pHeader->dwHashTableSize * sizeof(TMPQHash); + pHeader->ArchiveSize64 = pHeader->dwArchiveSize; + + // Block table position must be calculated as 32-bit value + // Note: BOBA protector puts block table before the MPQ header, so it is negative + BlockTablePos64 = (ULONGLONG)((DWORD)ByteOffset + pHeader->dwBlockTablePos); + BlockTableMask = 0xFFFFFFF0; + + // Determine the archive size on malformed MPQs + if(ha->dwFlags & MPQ_FLAG_MALFORMED) + { + // Calculate the archive size + pHeader->ArchiveSize64 = DetermineArchiveSize_V1(ha, pHeader, ByteOffset, FileSize); + pHeader->dwArchiveSize = (DWORD)pHeader->ArchiveSize64; + } + + // EWIX_v8_7.w3x: TMPQHeader::dwBlockTableSize = 0x00319601 + // Size of TFileTable goes to ~200MB, so we artificially cut it + if(BlockTablePos64 + (pHeader->dwBlockTableSize * sizeof(TMPQBlock)) > FileSize) + { + pHeader->dwBlockTableSize = (DWORD)((FileSize - BlockTablePos64) / sizeof(TMPQBlock)); + pHeader->BlockTableSize64 = pHeader->dwBlockTableSize * sizeof(TMPQBlock); + } + break; + + case MPQ_FORMAT_VERSION_2: + + // Check for malformed MPQ header version 1.0 + BSWAP_TMPQHEADER(pHeader, MPQ_FORMAT_VERSION_2); + if(pHeader->wFormatVersion != MPQ_FORMAT_VERSION_2 || pHeader->dwHeaderSize != MPQ_HEADER_SIZE_V2) + { + pHeader->wFormatVersion = MPQ_FORMAT_VERSION_1; + pHeader->dwHeaderSize = MPQ_HEADER_SIZE_V1; + ha->dwFlags |= MPQ_FLAG_MALFORMED; + goto Label_ArchiveVersion1; + } + + // Fill the rest of the header with zeros + memset((LPBYTE)pHeader + MPQ_HEADER_SIZE_V2, 0, sizeof(TMPQHeader) - MPQ_HEADER_SIZE_V2); + + // Check position of the hi-block table + if(pHeader->HiBlockTablePos64 > FileSize) + return ERROR_FILE_CORRUPT; + + // Calculate the expected hash table size + pHeader->HashTableSize64 = (pHeader->dwHashTableSize * sizeof(TMPQHash)); + HashTablePos64 = MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos); + + // Calculate the expected block table size + pHeader->BlockTableSize64 = (pHeader->dwBlockTableSize * sizeof(TMPQBlock)); + BlockTablePos64 = MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); + + // Supply the 64-bit archive size for signature verification + pHeader->ArchiveSize64 = pHeader->dwArchiveSize; + + // We require the block table to follow hash table + if(BlockTablePos64 >= HashTablePos64) + { + // Determine whether the hash table is compressed. This can be detected + // by subtracting hash table position from the block table position. + pHeader->HashTableSize64 = BlockTablePos64 - HashTablePos64; + + // Also, block table may be compressed. We check whether the HiBlockTable is there. + // If not, we try to use the archive size. Note that ArchiveSize may have + // an arbitrary value, because it is not tested by Blizzard games anymore + pHeader->BlockTableSize64 = DetermineBlockTableSize_V2(pHeader, ByteOffset, FileSize); + } + else + { + ha->dwFlags |= MPQ_FLAG_MALFORMED; + } + + // Add the MPQ Offset + BlockTablePos64 += ByteOffset; + break; + + 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 + BSWAP_TMPQHEADER(pHeader, MPQ_FORMAT_VERSION_3); + if(pHeader->dwHeaderSize < MPQ_HEADER_SIZE_V3) + { + pHeader->ArchiveSize64 = pHeader->dwArchiveSize; + pHeader->HetTablePos64 = 0; + pHeader->BetTablePos64 = 0; + } + + // Fixup malformed MPQ header sizes + pHeader->dwHeaderSize = STORMLIB_MIN(pHeader->dwHeaderSize, MPQ_HEADER_SIZE_V3); + + // + // We need to calculate the 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 + // + + // Fill the rest of the header with zeros + memset((LPBYTE)pHeader + MPQ_HEADER_SIZE_V3, 0, sizeof(TMPQHeader) - MPQ_HEADER_SIZE_V3); + BlockTablePos64 = MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); + HashTablePos64 = MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos); + MaxOffset = pHeader->ArchiveSize64; + + // Size of the hi-block table + if(pHeader->HiBlockTablePos64) + { + if(pHeader->HiBlockTablePos64 > FileSize) + return ERROR_FILE_CORRUPT; + pHeader->HiBlockTableSize64 = MaxOffset - pHeader->HiBlockTablePos64; + MaxOffset = pHeader->HiBlockTablePos64; + } + + // Size of the block table + if(BlockTablePos64) + { + if(BlockTablePos64 > FileSize || BlockTablePos64 >= MaxOffset) + return ERROR_FILE_CORRUPT; + pHeader->BlockTableSize64 = MaxOffset - BlockTablePos64; + MaxOffset = BlockTablePos64; + } + + // Size of the hash table + if(HashTablePos64) + { + if(HashTablePos64 > FileSize || HashTablePos64 >= MaxOffset) + return ERROR_FILE_CORRUPT; + pHeader->HashTableSize64 = MaxOffset - HashTablePos64; + MaxOffset = HashTablePos64; + } + + // Size of the BET table + if(pHeader->BetTablePos64) + { + if(pHeader->BetTablePos64 > FileSize) + return ERROR_FILE_CORRUPT; + pHeader->BetTableSize64 = MaxOffset - pHeader->BetTablePos64; + MaxOffset = pHeader->BetTablePos64; + } + + // Size of the HET table + if(pHeader->HetTablePos64) + { + if(pHeader->HetTablePos64 > FileSize) + return ERROR_FILE_CORRUPT; + pHeader->HetTableSize64 = MaxOffset - pHeader->HetTablePos64; +// MaxOffset = pHeader->HetTablePos64; + } + + // Add the MPQ Offset + BlockTablePos64 += ByteOffset; + break; + + 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 + // Apparently, Starcraft II only accepts MPQ headers where the MPQ header hash matches + // If MD5 doesn't match, we ignore this offset. We also ignore it if there's no MD5 at all + if(!IsValidMD5(pHeader->MD5_MpqHeader)) + return ERROR_FAKE_MPQ_HEADER; + if(!VerifyDataBlockHash(pHeader, MPQ_HEADER_SIZE_V4 - MD5_DIGEST_SIZE, pHeader->MD5_MpqHeader)) + return ERROR_FAKE_MPQ_HEADER; + + // Byteswap after header MD5 is verified + BSWAP_TMPQHEADER(pHeader, MPQ_FORMAT_VERSION_4); + + // Fixup malformed MPQ header sizes + pHeader->dwHeaderSize = MPQ_HEADER_SIZE_V4; + + // HiBlockTable must be 0 for archives under 4GB + if((pHeader->ArchiveSize64 >> 0x20) == 0 && pHeader->HiBlockTablePos64 != 0) + return ERROR_FAKE_MPQ_HEADER; + + // Is the "HET&BET" table tandem OK? + bHetBetOffsetOK = VerifyTableTandemPositions(ByteOffset, + pHeader->HetTablePos64, pHeader->HetTableSize64, + pHeader->BetTablePos64, pHeader->BetTableSize64, + FileSize); + + // Is the "Hash&Block" table tandem OK? + bHashBlockOffsetOK = VerifyTableTandemPositions(ByteOffset, + pHeader->dwHashTablePos, pHeader->HashTableSize64, + pHeader->dwBlockTablePos, pHeader->BlockTableSize64, + FileSize); + + // At least one pair must be OK + if(bHetBetOffsetOK == false && bHashBlockOffsetOK == false) + return ERROR_FAKE_MPQ_HEADER; + + // Check for malformed MPQs + if(pHeader->wFormatVersion != MPQ_FORMAT_VERSION_4 || (ByteOffset + pHeader->ArchiveSize64) > FileSize || (ByteOffset + pHeader->HiBlockTablePos64) >= FileSize) + { + pHeader->wFormatVersion = MPQ_FORMAT_VERSION_4; + pHeader->dwHeaderSize = MPQ_HEADER_SIZE_V4; + ha->dwFlags |= MPQ_FLAG_MALFORMED; + } + + // Recalculate archive size + if(ha->dwFlags & MPQ_FLAG_MALFORMED) + { + // Calculate the archive size + pHeader->ArchiveSize64 = DetermineArchiveSize_V4(pHeader, ByteOffset, FileSize); + pHeader->dwArchiveSize = (DWORD)pHeader->ArchiveSize64; + } + + // Calculate the block table position + BlockTablePos64 = ByteOffset + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); + break; + + default: + + // Check if it's a War of the Immortal data file (SQP) + // If not, we treat it as malformed MPQ version 1.0 + if(ConvertSqpHeaderToFormat4(ha, FileSize, dwFlags) != ERROR_SUCCESS) + { + pHeader->wFormatVersion = MPQ_FORMAT_VERSION_1; + pHeader->dwHeaderSize = MPQ_HEADER_SIZE_V1; + ha->dwFlags |= MPQ_FLAG_MALFORMED; + goto Label_ArchiveVersion1; + } + + // Calculate the block table position + BlockTablePos64 = ByteOffset + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); + break; + } + + // Handle case when block table is placed before the MPQ header + // Used by BOBA protector + if(BlockTablePos64 < ByteOffset) + ha->dwFlags |= MPQ_FLAG_MALFORMED; + return dwErrCode; +} //----------------------------------------------------------------------------- // Support for hash table -// Returns a hash table entry in the following order: -// 1) A hash table entry with the neutral locale -// 2) A hash table entry with any other locale -// 3) NULL -static TMPQHash * GetHashEntryAny(TMPQArchive * ha, const char * szFileName) +// Hash entry verification when the file table does not exist yet +bool IsValidHashEntry(TMPQArchive * ha, TMPQHash * pHash) { - TMPQHash * pHashNeutral = NULL; - TMPQHash * pFirstHash = GetFirstHashEntry(ha, szFileName); - TMPQHash * pHashAny = NULL; - TMPQHash * pHash = pFirstHash; + TFileEntry * pFileEntry = ha->pFileTable + MPQ_BLOCK_INDEX(pHash); - // Parse the found hashes - while(pHash != NULL) + return ((MPQ_BLOCK_INDEX(pHash) < ha->dwFileTableSize) && (pFileEntry->dwFlags & MPQ_FILE_EXISTS)) ? true : false; +} + +// Hash entry verification when the file table does not exist yet +static bool IsValidHashEntry1(TMPQArchive * ha, TMPQHash * pHash, TMPQBlock * pBlockTable) +{ + ULONGLONG ByteOffset; + TMPQBlock * pBlock; + + // The block index is considered valid if it's less than block table size + if(MPQ_BLOCK_INDEX(pHash) < ha->pHeader->dwBlockTableSize) { - // If we found neutral hash, remember it - if(pHash->lcLocale == 0) - pHashNeutral = pHash; - if(pHashAny == NULL) - pHashAny = pHash; + // Calculate the block table position + pBlock = pBlockTable + MPQ_BLOCK_INDEX(pHash); - // Get the next hash entry for that file - pHash = GetNextHashEntry(ha, pFirstHash, pHash); + // Check whether this is an existing file + if(pBlock->dwFlags & MPQ_FILE_EXISTS) + { + // We don't allow to be file size greater than 2GB in malformed archives + if((ha->dwFlags & MPQ_FLAG_MALFORMED) && (pBlock->dwFSize >= 0x80000000)) + return false; + + // The begin of the file must be within the archive + ByteOffset = FileOffsetFromMpqOffset(ha, pBlock->dwFilePos); + return (ByteOffset < ha->FileSize); + } } - // At the end, return neutral hash (if found), otherwise NULL - return (pHashNeutral != NULL) ? pHashNeutral : pHashAny; + return false; } // Returns a hash table entry in the following order: -// 1) A hash table entry with the preferred locale -// 2) A hash table entry with the neutral locale +// 1) A hash table entry with the preferred locale and platform +// 2) A hash table entry with the neutral|matching locale and neutral|matching platform // 3) NULL -static TMPQHash * GetHashEntryLocale(TMPQArchive * ha, const char * szFileName, LCID lcLocale) +// Storm_2016.dll: 15020940 +static TMPQHash * GetHashEntryLocale(TMPQArchive * ha, const char * szFileName, LCID lcFileLocale) { - TMPQHash * pHashNeutral = NULL; TMPQHash * pFirstHash = GetFirstHashEntry(ha, szFileName); + TMPQHash * pBestEntry = NULL; TMPQHash * pHash = pFirstHash; + USHORT Locale = SFILE_LOCALE(lcFileLocale); + BYTE Platform = SFILE_PLATFORM(lcFileLocale); // Parse the found hashes while(pHash != NULL) { - // If the locales match, return it - if(pHash->lcLocale == lcLocale) + // Storm_2016.dll: 150209CB + // If the hash entry matches both locale and platform, return it immediately + // Only do that for non-0 locale&platform, because for loc&plat=0, there's different + // processing in Warcraft III vs. Starcraft, which is abused by some protectors. + if((Locale || Platform) && pHash->Locale == Locale && pHash->Platform == Platform) return pHash; - - // If we found neutral hash, remember it - if(pHash->lcLocale == 0) - pHashNeutral = pHash; + + // Storm_2016.dll: 150209D9 + // If (locale matches or is neutral) AND (platform matches or is neutral), remember this as the best entry + // Also remember the first matching entry for Starcraft maps + if(pHash->Locale == 0 || pHash->Locale == Locale) + { + if(pHash->Platform == 0 || pHash->Platform == Platform) + { + pBestEntry = 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 - return pHashNeutral; + // Return the best entry that we found + return pBestEntry; } // Returns a hash table entry in the following order: -// 1) A hash table entry with the preferred locale +// 1) A hash table entry with the preferred locale&platform // 2) NULL -static TMPQHash * GetHashEntryExact(TMPQArchive * ha, const char * szFileName, LCID lcLocale) +// In case there are multiple items with the same locale&platform, +// we need to return the last one. This is because it must correspond to SFileOpenFileEx +static TMPQHash * GetHashEntryExact(TMPQArchive * ha, const char * szFileName, LCID lcFileLocale) { TMPQHash * pFirstHash = GetFirstHashEntry(ha, szFileName); + TMPQHash * pBestHash = NULL; TMPQHash * pHash = pFirstHash; + USHORT Locale = SFILE_LOCALE(lcFileLocale); + BYTE Platform = SFILE_PLATFORM(lcFileLocale); // Parse the found hashes while(pHash != NULL) { - // If the locales match, return it - if(pHash->lcLocale == lcLocale) - return pHash; - + // If the locales match, we remember this one as the best one + if(pHash->Locale == Locale && pHash->Platform == Platform) + pBestHash = pHash; + // Get the next hash entry for that file - pHash = GetNextHashEntry(ha, pFirstHash, pHash); + pHash = GetNextHashEntry(ha, pFirstHash, pHash); } - // Not found - return NULL; + // Return the best hash or NULL + return pBestHash; +} + +// Defragment the file table so it does not contain any gaps +// Note: As long as all values of all TMPQHash::dwBlockIndex +// are not HASH_ENTRY_FREE, the startup search index does not matter. +// Hash table is circular, so as long as there is no terminator, +// all entries will be found. +/* +static TMPQHash * DefragmentHashTable( + TMPQArchive * ha, + TMPQHash * pHashTable, + TMPQBlock * pBlockTable) +{ + TMPQHeader * pHeader = ha->pHeader; + TMPQHash * pHashTableEnd = pHashTable + pHeader->dwHashTableSize; + TMPQHash * pSource = pHashTable; + TMPQHash * pTarget = pHashTable; + DWORD dwFirstFreeEntry; + DWORD dwNewTableSize; + + // Sanity checks + assert(pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1); + assert(pHeader->HiBlockTablePos64 == 0); + + // Parse the hash table and move the entries to the begin of it + for(pSource = pHashTable; pSource < pHashTableEnd; pSource++) + { + // Check whether this is a valid hash table entry + if(IsValidHashEntry1(ha, pSource, pBlockTable)) + { + // Copy the hash table entry back + if(pSource > pTarget) + pTarget[0] = pSource[0]; + + // Move the target + pTarget++; + } + } + + // Calculate how many entries in the hash table we really need + dwFirstFreeEntry = (DWORD)(pTarget - pHashTable); + dwNewTableSize = GetNearestPowerOfTwo(dwFirstFreeEntry); + + // Fill the rest with entries that look like deleted + pHashTableEnd = pHashTable + dwNewTableSize; + pSource = pHashTable + dwFirstFreeEntry; + memset(pSource, 0xFF, (dwNewTableSize - dwFirstFreeEntry) * sizeof(TMPQHash)); + + // Mark the block indexes as deleted + for(; pSource < pHashTableEnd; pSource++) + pSource->dwBlockIndex = HASH_ENTRY_DELETED; + + // Free some of the space occupied by the hash table + if(dwNewTableSize < pHeader->dwHashTableSize) + { + pHashTable = STORM_REALLOC(TMPQHash, pHashTable, dwNewTableSize); + ha->pHeader->BlockTableSize64 = dwNewTableSize * sizeof(TMPQHash); + ha->pHeader->dwHashTableSize = dwNewTableSize; + } + + return pHashTable; +} +*/ + +static DWORD BuildFileTableFromBlockTable( + TMPQArchive * ha, + TMPQBlock * pBlockTable) +{ + TFileEntry * pFileEntry; + TMPQHeader * pHeader = ha->pHeader; + TMPQBlock * pBlock; + TMPQHash * pHashTableEnd; + TMPQHash * pHash; + LPDWORD DefragmentTable = NULL; + DWORD dwItemCount = 0; + + // Sanity checks + assert(ha->pFileTable != NULL); + assert(ha->dwFileTableSize >= ha->dwMaxFileCount); + + // + // Defragmentation of the hash table was removed. The reason is a MPQ protector, + // two hash entries with the same name, where only the second one is valid. + // The index of the first entry (HashString(szFileName, 0)) points to the second one: + // + // NameA NameB BlkIdx Name + // B701656E FCFB1EED 0000001C staredit\scenario.chk (correct one) + // --> B701656E FCFB1EED 0000001D staredit\scenario.chk (corrupt one) + // + // Defragmenting the hash table corrupts the order and "staredit\scenario.chk" can't be read + // Example MPQ: MPQ_2022_v1_Sniper.scx + // + + //if(ha->dwFlags & MPQ_FLAG_HASH_TABLE_CUT) + //{ + // ha->pHashTable = DefragmentHashTable(ha, ha->pHashTable, pBlockTable); + // ha->dwMaxFileCount = pHeader->dwHashTableSize; + //} + + // If the hash table or block table is cut, + // we will defragment the block table + if(ha->dwFlags & (MPQ_FLAG_HASH_TABLE_CUT | MPQ_FLAG_BLOCK_TABLE_CUT)) + { + // Sanity checks + assert(pHeader->HiBlockTablePos64 == 0); + + // Allocate the translation table + DefragmentTable = STORM_ALLOC(DWORD, pHeader->dwBlockTableSize); + if(DefragmentTable == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + // Fill the translation table + memset(DefragmentTable, 0xFF, pHeader->dwBlockTableSize * sizeof(DWORD)); + } + + // Parse the entire hash table + pHashTableEnd = ha->pHashTable + pHeader->dwHashTableSize; + for(pHash = ha->pHashTable; pHash < pHashTableEnd; pHash++) + { + // + // We need to properly handle these cases: + // - Multiple hash entries (same file name) point to the same block entry + // - Multiple hash entries (different file name) point to the same block entry + // + // Ignore all hash table entries where: + // - Block Index >= BlockTableSize + // - Flags of the appropriate block table entry + // + + if(IsValidHashEntry1(ha, pHash, pBlockTable)) + { + DWORD dwOldIndex = MPQ_BLOCK_INDEX(pHash); + DWORD dwNewIndex = MPQ_BLOCK_INDEX(pHash); + + // Determine the new block index + if(DefragmentTable != NULL) + { + // Need to handle case when multiple hash + // entries point to the same block entry + if(DefragmentTable[dwOldIndex] == HASH_ENTRY_FREE) + { + DefragmentTable[dwOldIndex] = dwItemCount; + dwNewIndex = dwItemCount++; + } + else + { + dwNewIndex = DefragmentTable[dwOldIndex]; + } + + // Fix the pointer in the hash entry + pHash->dwBlockIndex = dwNewIndex; + + // Dump the relocation entry +// printf("Relocating hash entry %08X-%08X: %08X -> %08X\n", pHash->dwHashCheck1, pHash->dwHashCheck2, dwBlockIndex, dwNewIndex); + } + + // Get the pointer to the file entry and the block entry + pFileEntry = ha->pFileTable + dwNewIndex; + pBlock = pBlockTable + dwOldIndex; + + // ByteOffset is only valid if file size is not zero + pFileEntry->ByteOffset = pBlock->dwFilePos; + if(pFileEntry->ByteOffset == 0 && pBlock->dwFSize == 0) + pFileEntry->ByteOffset = ha->pHeader->dwHeaderSize; + + // Clear file flags that are unknown to this type of map. + pFileEntry->dwFlags = pBlock->dwFlags & ha->dwValidFileFlags; + + // Fill the rest of the file entry + pFileEntry->dwFileSize = pBlock->dwFSize; + pFileEntry->dwCmpSize = pBlock->dwCSize; + } + } + + // Free the translation table + if(DefragmentTable != NULL) + { + // If we defragmented the block table in the process, + // free some memory by shrinking the file table + if(ha->dwFileTableSize > ha->dwMaxFileCount) + { + ha->pFileTable = STORM_REALLOC(TFileEntry, ha->pFileTable, ha->dwMaxFileCount); + ha->pHeader->BlockTableSize64 = ha->dwMaxFileCount * sizeof(TMPQBlock); + ha->pHeader->dwBlockTableSize = ha->dwMaxFileCount; + ha->dwFileTableSize = ha->dwMaxFileCount; + } + +// DumpFileTable(ha->pFileTable, ha->dwFileTableSize); + + // Free the translation table + STORM_FREE(DefragmentTable); + } + + return ERROR_SUCCESS; } static TMPQHash * TranslateHashTable( @@ -362,7 +1108,8 @@ static TMPQHash * TranslateHashTable( return pHashTable; } -static TMPQBlock * TranslateBlockTable( +// Also used in SFileGetFileInfo +TMPQBlock * TranslateBlockTable( TMPQArchive * ha, ULONGLONG * pcbTableSize, bool * pbNeedHiBlockTable) @@ -370,18 +1117,17 @@ static TMPQBlock * TranslateBlockTable( TFileEntry * pFileEntry = ha->pFileTable; TMPQBlock * pBlockTable; TMPQBlock * pBlock; - size_t BlockTableSize; - bool bNeedHiBlockTable = false; + DWORD NeedHiBlockTable = 0; + DWORD dwBlockTableSize = ha->pHeader->dwBlockTableSize; // Allocate copy of the hash table - pBlockTable = pBlock = STORM_ALLOC(TMPQBlock, ha->dwFileTableSize); + pBlockTable = pBlock = STORM_ALLOC(TMPQBlock, dwBlockTableSize); if(pBlockTable != NULL) { - // Copy the block table - BlockTableSize = sizeof(TMPQBlock) * ha->dwFileTableSize; - for(DWORD i = 0; i < ha->dwFileTableSize; i++) + // Convert the block table + for(DWORD i = 0; i < dwBlockTableSize; i++) { - bNeedHiBlockTable = (pFileEntry->ByteOffset >> 32) ? true : false; + NeedHiBlockTable |= (DWORD)(pFileEntry->ByteOffset >> 32); pBlock->dwFilePos = (DWORD)pFileEntry->ByteOffset; pBlock->dwFSize = pFileEntry->dwFileSize; pBlock->dwCSize = pFileEntry->dwCmpSize; @@ -393,10 +1139,10 @@ static TMPQBlock * TranslateBlockTable( // Give the size to the caller if(pcbTableSize != NULL) - *pcbTableSize = (ULONGLONG)BlockTableSize; + *pcbTableSize = (ULONGLONG)dwBlockTableSize * sizeof(TMPQBlock); if(pbNeedHiBlockTable != NULL) - *pbNeedHiBlockTable = bNeedHiBlockTable; + *pbNeedHiBlockTable = NeedHiBlockTable ? true : false; } return pBlockTable; @@ -409,20 +1155,19 @@ static USHORT * TranslateHiBlockTable( TFileEntry * pFileEntry = ha->pFileTable; USHORT * pHiBlockTable; USHORT * pHiBlock; - size_t HiBlockTableSize; + DWORD dwBlockTableSize = ha->pHeader->dwBlockTableSize; // Allocate copy of the hash table - pHiBlockTable = pHiBlock = STORM_ALLOC(USHORT, ha->dwFileTableSize); + pHiBlockTable = pHiBlock = STORM_ALLOC(USHORT, dwBlockTableSize); if(pHiBlockTable != NULL) { // Copy the block table - HiBlockTableSize = sizeof(USHORT) * ha->dwFileTableSize; - for(DWORD i = 0; i < ha->dwFileTableSize; i++) + for(DWORD i = 0; i < dwBlockTableSize; i++) pHiBlock[i] = (USHORT)(pFileEntry[i].ByteOffset >> 0x20); // Give the size to the caller if(pcbTableSize != NULL) - *pcbTableSize = (ULONGLONG)HiBlockTableSize; + *pcbTableSize = (ULONGLONG)dwBlockTableSize * sizeof(USHORT); } return pHiBlockTable; @@ -431,21 +1176,21 @@ static USHORT * TranslateHiBlockTable( //----------------------------------------------------------------------------- // General EXT table functions -TMPQExtTable * LoadExtTable( +TMPQExtHeader * LoadExtTable( TMPQArchive * ha, ULONGLONG ByteOffset, size_t Size, DWORD dwSignature, DWORD dwKey) { - TMPQExtTable * pCompressed = NULL; // Compressed table - TMPQExtTable * pExtTable = NULL; // Uncompressed table + TMPQExtHeader * pCompressed = NULL; // Compressed table + TMPQExtHeader * pExtTable = NULL; // Uncompressed table // Do nothing if the size is zero if(ByteOffset != 0 && Size != 0) { // Allocate size for the compressed table - pExtTable = (TMPQExtTable *)STORM_ALLOC(BYTE, Size); + pExtTable = (TMPQExtHeader *)STORM_ALLOC(BYTE, Size); if(pExtTable != NULL) { // Load the table from the MPQ @@ -457,7 +1202,7 @@ TMPQExtTable * LoadExtTable( } // Swap the ext table header - BSWAP_ARRAY32_UNSIGNED(pExtTable, sizeof(TMPQExtTable)); + BSWAP_ARRAY32_UNSIGNED(pExtTable, sizeof(TMPQExtHeader)); if(pExtTable->dwSignature != dwSignature) { STORM_FREE(pExtTable); @@ -466,14 +1211,14 @@ TMPQExtTable * LoadExtTable( // Decrypt the block BSWAP_ARRAY32_UNSIGNED(pExtTable + 1, pExtTable->dwDataSize); - DecryptMpqBlock(pExtTable + 1, (DWORD)(Size - sizeof(TMPQExtTable)), dwKey); + DecryptMpqBlock(pExtTable + 1, (DWORD)(Size - sizeof(TMPQExtHeader)), dwKey); BSWAP_ARRAY32_UNSIGNED(pExtTable + 1, pExtTable->dwDataSize); // If the table is compressed, decompress it - if((pExtTable->dwDataSize + sizeof(TMPQExtTable)) > Size) + if((pExtTable->dwDataSize + sizeof(TMPQExtHeader)) > Size) { pCompressed = pExtTable; - pExtTable = (TMPQExtTable *)STORM_ALLOC(BYTE, sizeof(TMPQExtTable) + pCompressed->dwDataSize); + pExtTable = (TMPQExtHeader *)STORM_ALLOC(BYTE, sizeof(TMPQExtHeader) + pCompressed->dwDataSize); if(pExtTable != NULL) { int cbOutBuffer = (int)pCompressed->dwDataSize; @@ -483,7 +1228,7 @@ TMPQExtTable * LoadExtTable( pExtTable->dwSignature = pCompressed->dwSignature; pExtTable->dwVersion = pCompressed->dwVersion; pExtTable->dwDataSize = pCompressed->dwDataSize; - if(!SCompDecompress2((char *)(pExtTable + 1), &cbOutBuffer, (char *)(pCompressed + 1), cbInBuffer)) + if(!SCompDecompress2(pExtTable + 1, &cbOutBuffer, pCompressed + 1, cbInBuffer)) { STORM_FREE(pExtTable); pExtTable = NULL; @@ -500,13 +1245,7 @@ TMPQExtTable * LoadExtTable( return pExtTable; } -// Used in MPQ Editor -void FreeMpqBuffer(void * pvBuffer) -{ - STORM_FREE(pvBuffer); -} - -static int SaveMpqTable( +static DWORD SaveMpqTable( TMPQArchive * ha, void * pMpqTable, ULONGLONG ByteOffset, @@ -517,7 +1256,7 @@ static int SaveMpqTable( { ULONGLONG FileOffset; void * pCompressed = NULL; - int nError = ERROR_SUCCESS; + DWORD dwErrCode = ERROR_SUCCESS; // Do we have to compress the table? if(bCompress) @@ -531,7 +1270,7 @@ static int SaveMpqTable( return ERROR_NOT_ENOUGH_MEMORY; // Compress the table - SCompCompress((char *)pCompressed, &cbOutBuffer, (char *)pMpqTable, cbInBuffer, MPQ_COMPRESSION_ZLIB, 0, 0); + SCompCompress(pCompressed, &cbOutBuffer, pMpqTable, cbInBuffer, MPQ_COMPRESSION_ZLIB, 0, 0); // If the compression failed, revert it. Otherwise, swap the tables if(cbOutBuffer >= cbInBuffer) @@ -563,17 +1302,17 @@ static int SaveMpqTable( BSWAP_ARRAY32_UNSIGNED(pMpqTable, Size); FileOffset = ha->MpqPos + ByteOffset; if(!FileStream_Write(ha->pStream, &FileOffset, pMpqTable, (DWORD)Size)) - nError = GetLastError(); + dwErrCode = SErrGetLastError(); // Free the compressed table, if any if(pCompressed != NULL) STORM_FREE(pCompressed); - return nError; + return dwErrCode; } -static int SaveExtTable( +static DWORD SaveExtTable( TMPQArchive * ha, - TMPQExtTable * pExtTable, + TMPQExtHeader * pExtTable, ULONGLONG ByteOffset, DWORD dwTableSize, unsigned char * md5, @@ -582,9 +1321,9 @@ static int SaveExtTable( LPDWORD pcbTotalSize) { ULONGLONG FileOffset; - TMPQExtTable * pCompressed = NULL; + TMPQExtHeader * pCompressed = NULL; DWORD cbTotalSize = 0; - int nError = ERROR_SUCCESS; + DWORD dwErrCode = ERROR_SUCCESS; // Do we have to compress the table? if(bCompress) @@ -593,7 +1332,7 @@ static int SaveExtTable( int cbInBuffer = (int)dwTableSize; // Allocate extra space for compressed table - pCompressed = (TMPQExtTable *)STORM_ALLOC(BYTE, dwTableSize); + pCompressed = (TMPQExtHeader *)STORM_ALLOC(BYTE, dwTableSize); if(pCompressed == NULL) return ERROR_NOT_ENOUGH_MEMORY; @@ -601,7 +1340,7 @@ static int SaveExtTable( pCompressed->dwSignature = pExtTable->dwSignature; pCompressed->dwVersion = pExtTable->dwVersion; pCompressed->dwDataSize = pExtTable->dwDataSize; - SCompCompress((char *)(pCompressed + 1), &cbOutBuffer, (char *)(pExtTable + 1), cbInBuffer, MPQ_COMPRESSION_ZLIB, 0, 0); + SCompCompress((pCompressed + 1), &cbOutBuffer, (pExtTable + 1), cbInBuffer, MPQ_COMPRESSION_ZLIB, 0, 0); // If the compression failed, revert it. Otherwise, swap the tables if(cbOutBuffer >= cbInBuffer) @@ -619,7 +1358,7 @@ static int SaveExtTable( if(dwKey != 0) { BSWAP_ARRAY32_UNSIGNED(pExtTable + 1, pExtTable->dwDataSize); - EncryptMpqBlock(pExtTable + 1, (DWORD)(dwTableSize - sizeof(TMPQExtTable)), dwKey); + EncryptMpqBlock(pExtTable + 1, (DWORD)(dwTableSize - sizeof(TMPQExtHeader)), dwKey); BSWAP_ARRAY32_UNSIGNED(pExtTable + 1, pExtTable->dwDataSize); } @@ -634,12 +1373,12 @@ static int SaveExtTable( if(FileStream_Write(ha->pStream, &FileOffset, pExtTable, dwTableSize)) cbTotalSize += dwTableSize; else - nError = GetLastError(); + dwErrCode = SErrGetLastError(); // We have to write raw data MD5 - if(nError == ERROR_SUCCESS && ha->pHeader->dwRawChunkSize != 0) + if(dwErrCode == ERROR_SUCCESS && ha->pHeader->dwRawChunkSize != 0) { - nError = WriteMemDataMD5(ha->pStream, + dwErrCode = WriteMemDataMD5(ha->pStream, FileOffset, pExtTable, dwTableSize, @@ -654,7 +1393,7 @@ static int SaveExtTable( // Free the compressed table, if any if(pCompressed != NULL) STORM_FREE(pCompressed); - return nError; + return dwErrCode; } //----------------------------------------------------------------------------- @@ -662,102 +1401,176 @@ static int SaveExtTable( static void CreateHetHeader( TMPQHetTable * pHetTable, - PHET_TABLE_HEADER pHetHeader) + TMPQHetHeader * pHetHeader) { - // Fill the BET header - pHetHeader->dwMaxFileCount = pHetTable->dwMaxFileCount; - pHetHeader->dwHashTableSize = pHetTable->dwHashTableSize; - pHetHeader->dwHashEntrySize = pHetTable->dwHashBitSize; - pHetHeader->dwIndexSizeTotal = GetNecessaryBitCount(pHetTable->dwMaxFileCount); - pHetHeader->dwIndexSizeExtra = 0; - pHetHeader->dwIndexSize = pHetHeader->dwIndexSizeTotal; - pHetHeader->dwIndexTableSize = ((pHetHeader->dwIndexSizeTotal * pHetTable->dwHashTableSize) + 7) / 8; + // Fill the common header + pHetHeader->ExtHdr.dwSignature = HET_TABLE_SIGNATURE; + pHetHeader->ExtHdr.dwVersion = 1; + pHetHeader->ExtHdr.dwDataSize = 0; + + // Fill the HET header + pHetHeader->dwEntryCount = pHetTable->dwEntryCount; + pHetHeader->dwTotalCount = pHetTable->dwTotalCount; + pHetHeader->dwNameHashBitSize = pHetTable->dwNameHashBitSize; + pHetHeader->dwIndexSizeTotal = pHetTable->dwIndexSizeTotal; + pHetHeader->dwIndexSizeExtra = pHetTable->dwIndexSizeExtra; + pHetHeader->dwIndexSize = pHetTable->dwIndexSize; + pHetHeader->dwIndexTableSize = ((pHetHeader->dwIndexSizeTotal * pHetTable->dwTotalCount) + 7) / 8; // Calculate the total size needed for holding HET table - pHetHeader->dwTableSize = sizeof(HET_TABLE_HEADER) + - pHetHeader->dwHashTableSize + + pHetHeader->ExtHdr.dwDataSize = + pHetHeader->dwTableSize = sizeof(TMPQHetHeader) - sizeof(TMPQExtHeader) + + pHetHeader->dwTotalCount + pHetHeader->dwIndexTableSize; } -TMPQHetTable * CreateHetTable(DWORD dwMaxFileCount, DWORD dwHashBitSize, bool bCreateEmpty) +TMPQHetTable * CreateHetTable(DWORD dwEntryCount, DWORD dwTotalCount, DWORD dwNameHashBitSize, LPBYTE pbSrcData) { TMPQHetTable * pHetTable; pHetTable = STORM_ALLOC(TMPQHetTable, 1); if(pHetTable != NULL) { - pHetTable->dwIndexSizeTotal = 0; - pHetTable->dwIndexSizeExtra = 0; - pHetTable->dwIndexSize = pHetTable->dwIndexSizeTotal; - pHetTable->dwMaxFileCount = dwMaxFileCount; - pHetTable->dwHashTableSize = (dwMaxFileCount * 4 / 3); - pHetTable->dwHashBitSize = dwHashBitSize; + // Zero the HET table + memset(pHetTable, 0, sizeof(TMPQHetTable)); - // Size of one index is calculated from max file count - pHetTable->dwIndexSizeTotal = GetNecessaryBitCount(dwMaxFileCount); - pHetTable->dwIndexSizeExtra = 0; - pHetTable->dwIndexSize = pHetTable->dwIndexSizeTotal; - - // Allocate hash table - 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 - if(bCreateEmpty) - pHetTable->pBetIndexes = CreateBitArray(pHetTable->dwHashTableSize * pHetTable->dwIndexSizeTotal, 0xFF); + // Hash sizes less than 0x40 bits are not tested + assert(dwNameHashBitSize == 0x40); // Calculate masks - pHetTable->AndMask64 = 0; - if(dwHashBitSize != 0x40) - pHetTable->AndMask64 = (ULONGLONG)1 << dwHashBitSize; - pHetTable->AndMask64--; + pHetTable->AndMask64 = ((dwNameHashBitSize != 0x40) ? ((ULONGLONG)1 << dwNameHashBitSize) : 0) - 1; + pHetTable->OrMask64 = (ULONGLONG)1 << (dwNameHashBitSize - 1); - pHetTable->OrMask64 = (ULONGLONG)1 << (dwHashBitSize - 1); + // If the total count is not entered, use default + if(dwTotalCount == 0) + dwTotalCount = (dwEntryCount * 4) / 3; + + // Store the HET table parameters + pHetTable->dwEntryCount = dwEntryCount; + pHetTable->dwTotalCount = dwTotalCount; + pHetTable->dwNameHashBitSize = dwNameHashBitSize; + pHetTable->dwIndexSizeTotal = GetNecessaryBitCount(dwEntryCount); + pHetTable->dwIndexSizeExtra = 0; + pHetTable->dwIndexSize = pHetTable->dwIndexSizeTotal; + + // Allocate array of hashes + pHetTable->pNameHashes = STORM_ALLOC(BYTE, dwTotalCount); + if(pHetTable->pNameHashes != NULL) + { + // Make sure the data are initialized + memset(pHetTable->pNameHashes, 0, dwTotalCount); + + // Allocate the bit array for file indexes + pHetTable->pBetIndexes = TMPQBits::Create(dwTotalCount * pHetTable->dwIndexSizeTotal, 0xFF); + if(pHetTable->pBetIndexes != NULL) + { + // Initialize the HET table from the source data (if given) + if(pbSrcData != NULL) + { + // Copy the name hashes + memcpy(pHetTable->pNameHashes, pbSrcData, dwTotalCount); + + // Copy the file indexes + memcpy(pHetTable->pBetIndexes->Elements, pbSrcData + dwTotalCount, pHetTable->pBetIndexes->NumberOfBytes); + } + + // Return the result HET table + return pHetTable; + } + + // Free the name hashes + STORM_FREE(pHetTable->pNameHashes); + } + + STORM_FREE(pHetTable); } - return pHetTable; + // Failed + return NULL; } -static TMPQHetTable * TranslateHetTable(TMPQExtTable * pExtTable) +static DWORD InsertHetEntry(TMPQHetTable * pHetTable, ULONGLONG FileNameHash, DWORD dwFileIndex) +{ + DWORD StartIndex; + DWORD Index; + BYTE NameHash1; + + // Get the start index and the high 8 bits of the name hash + StartIndex = Index = (DWORD)(FileNameHash % pHetTable->dwTotalCount); + NameHash1 = (BYTE)(FileNameHash >> (pHetTable->dwNameHashBitSize - 8)); + + // Find a place where to put it + for(;;) + { + // Did we find a free HET entry? + if(pHetTable->pNameHashes[Index] == HET_ENTRY_FREE) + { + // Set the entry in the name hash table + pHetTable->pNameHashes[Index] = NameHash1; + + // Set the entry in the file index table + pHetTable->pBetIndexes->SetBits(pHetTable->dwIndexSizeTotal * Index, + pHetTable->dwIndexSize, + &dwFileIndex, + 4); + return ERROR_SUCCESS; + } + + // Move to the next entry in the HET table + // If we came to the start index again, we are done + Index = (Index + 1) % pHetTable->dwTotalCount; + if(Index == StartIndex) + break; + } + + // No space in the HET table. Should never happen, + // because the HET table is created according to the number of files + assert(false); + return ERROR_DISK_FULL; +} + +static TMPQHetTable * TranslateHetTable(TMPQHetHeader * pHetHeader) { - HET_TABLE_HEADER HetHeader; TMPQHetTable * pHetTable = NULL; - LPBYTE pbSrcData = (LPBYTE)(pExtTable + 1); + LPBYTE pbSrcData = (LPBYTE)(pHetHeader + 1); // Sanity check - assert(pExtTable->dwSignature == HET_TABLE_SIGNATURE); - assert(pExtTable->dwVersion == 1); + assert(pHetHeader->ExtHdr.dwSignature == HET_TABLE_SIGNATURE); + assert(pHetHeader->ExtHdr.dwVersion == 1); // Verify size of the HET table - if(pExtTable != NULL && pExtTable->dwDataSize >= sizeof(HET_TABLE_HEADER)) + if(pHetHeader->ExtHdr.dwDataSize >= (sizeof(TMPQHetHeader) - sizeof(TMPQExtHeader))) { - // Copy the table header in order to have it aligned and swapped - memcpy(&HetHeader, pbSrcData, sizeof(HET_TABLE_HEADER)); - BSWAP_ARRAY32_UNSIGNED(&HetHeader, sizeof(HET_TABLE_HEADER)); - pbSrcData += sizeof(HET_TABLE_HEADER); - // Verify the size of the table in the header - if(HetHeader.dwTableSize == pExtTable->dwDataSize) + if(pHetHeader->ExtHdr.dwDataSize >= pHetHeader->dwTableSize) { - // Create translated table - pHetTable = CreateHetTable(HetHeader.dwMaxFileCount, HetHeader.dwHashEntrySize, false); - if(pHetTable != NULL) + // The size of the HET table must be sum of header, hash and index table size + if((sizeof(TMPQHetHeader) - sizeof(TMPQExtHeader) + pHetHeader->dwTotalCount + pHetHeader->dwIndexTableSize) == pHetHeader->dwTableSize) { - // Copy the hash table size, index size and extra bits from the HET header - pHetTable->dwHashTableSize = HetHeader.dwHashTableSize; - pHetTable->dwIndexSizeTotal = HetHeader.dwIndexSizeTotal; - pHetTable->dwIndexSizeExtra = HetHeader.dwIndexSizeExtra; + // So far, all MPQs with HET Table have had total number of entries equal to 4/3 of file count + // Exception: "2010 - Starcraft II\!maps\Tya's Zerg Defense (unprotected).SC2Map" +// assert(((pHetHeader->dwEntryCount * 4) / 3) == pHetHeader->dwTotalCount); - // Fill the hash table - if(pHetTable->pHetHashes != NULL) - memcpy(pHetTable->pHetHashes, pbSrcData, pHetTable->dwHashTableSize); - pbSrcData += pHetTable->dwHashTableSize; + // The size of one index is predictable as well + assert(GetNecessaryBitCount(pHetHeader->dwEntryCount) == pHetHeader->dwIndexSizeTotal); - // Copy the block index table - pHetTable->pBetIndexes = CreateBitArray(HetHeader.dwIndexTableSize * 8, 0xFF); - if(pHetTable->pBetIndexes != NULL) - memcpy(pHetTable->pBetIndexes->Elements, pbSrcData, HetHeader.dwIndexTableSize); - pbSrcData += HetHeader.dwIndexTableSize; + // The size of index table (in entries) is expected + // to be the same like the hash table size (in bytes) + assert(((pHetHeader->dwTotalCount * pHetHeader->dwIndexSizeTotal) + 7) / 8 == pHetHeader->dwIndexTableSize); + + // Create translated table + pHetTable = CreateHetTable(pHetHeader->dwEntryCount, pHetHeader->dwTotalCount, pHetHeader->dwNameHashBitSize, pbSrcData); + if(pHetTable != NULL) + { + // Now the sizes in the hash table should be already set + assert(pHetTable->dwEntryCount == pHetHeader->dwEntryCount); + assert(pHetTable->dwTotalCount == pHetHeader->dwTotalCount); + assert(pHetTable->dwIndexSizeTotal == pHetHeader->dwIndexSizeTotal); + + // Copy the missing variables + pHetTable->dwIndexSizeExtra = pHetHeader->dwIndexSizeExtra; + pHetTable->dwIndexSize = pHetHeader->dwIndexSize; + } } } } @@ -765,110 +1578,96 @@ static TMPQHetTable * TranslateHetTable(TMPQExtTable * pExtTable) return pHetTable; } -static TMPQExtTable * TranslateHetTable(TMPQHetTable * pHetTable, ULONGLONG * pcbHetTable) +static TMPQExtHeader * TranslateHetTable(TMPQHetTable * pHetTable, ULONGLONG * pcbHetTable) { - TMPQExtTable * pExtTable = NULL; - HET_TABLE_HEADER HetHeader; + TMPQHetHeader * pHetHeader = NULL; + TMPQHetHeader HetHeader; LPBYTE pbLinearTable = NULL; LPBYTE pbTrgData; - size_t HetTableSize; // Prepare header of the HET table CreateHetHeader(pHetTable, &HetHeader); - // Calculate the total size needed for holding the encrypted HET table - HetTableSize = HetHeader.dwTableSize; - // Allocate space for the linear table - pbLinearTable = STORM_ALLOC(BYTE, sizeof(TMPQExtTable) + HetTableSize); + pbLinearTable = STORM_ALLOC(BYTE, sizeof(TMPQExtHeader) + HetHeader.dwTableSize); if(pbLinearTable != NULL) { - // Create the common ext table header - pExtTable = (TMPQExtTable *)pbLinearTable; - pExtTable->dwSignature = HET_TABLE_SIGNATURE; - pExtTable->dwVersion = 1; - pExtTable->dwDataSize = (DWORD)HetTableSize; - pbTrgData = (LPBYTE)(pExtTable + 1); + // Copy the table header + pHetHeader = (TMPQHetHeader *)pbLinearTable; + memcpy(pHetHeader, &HetHeader, sizeof(TMPQHetHeader)); + pbTrgData = (LPBYTE)(pHetHeader + 1); - // Copy the HET table header - memcpy(pbTrgData, &HetHeader, sizeof(HET_TABLE_HEADER)); - BSWAP_ARRAY32_UNSIGNED(pbTrgData, sizeof(HET_TABLE_HEADER)); - pbTrgData += sizeof(HET_TABLE_HEADER); - - // Copy the array of HET hashes - memcpy(pbTrgData, pHetTable->pHetHashes, pHetTable->dwHashTableSize); - pbTrgData += pHetTable->dwHashTableSize; + // Copy the array of name hashes + memcpy(pbTrgData, pHetTable->pNameHashes, pHetTable->dwTotalCount); + pbTrgData += pHetTable->dwTotalCount; // Copy the bit array of BET indexes memcpy(pbTrgData, pHetTable->pBetIndexes->Elements, HetHeader.dwIndexTableSize); - // Calculate the total size of the table, including the TMPQExtTable + // Calculate the total size of the table, including the TMPQExtHeader if(pcbHetTable != NULL) { - *pcbHetTable = (ULONGLONG)(sizeof(TMPQExtTable) + HetTableSize); + *pcbHetTable = (ULONGLONG)(sizeof(TMPQExtHeader) + HetHeader.dwTableSize); } } - return pExtTable; + // Keep Coverity happy + assert((TMPQExtHeader *)&pHetHeader->ExtHdr == (TMPQExtHeader *)pbLinearTable); + return (TMPQExtHeader *)pbLinearTable; } -DWORD GetFileIndex_Het(TMPQArchive * ha, const char * szFileName) +static DWORD GetFileIndex_Het(TMPQArchive * ha, const char * szFileName) { TMPQHetTable * pHetTable = ha->pHetTable; ULONGLONG FileNameHash; - ULONGLONG AndMask64; - ULONGLONG OrMask64; - ULONGLONG BetHash; DWORD StartIndex; DWORD Index; - BYTE HetHash; // Upper 8 bits of the masked file name hash + BYTE NameHash1; // Upper 8 bits of the masked file name hash + + // If there are no entries in the HET table, do nothing + if(pHetTable->dwEntryCount == 0) + return HASH_ENTRY_FREE; // Do nothing if the MPQ has no HET table assert(ha->pHetTable != NULL); // Calculate 64-bit hash of the file name - AndMask64 = pHetTable->AndMask64; - OrMask64 = pHetTable->OrMask64; - FileNameHash = (HashStringJenkins(szFileName) & AndMask64) | OrMask64; + FileNameHash = (HashStringJenkins(szFileName) & pHetTable->AndMask64) | pHetTable->OrMask64; // Split the file name hash into two parts: - // Part 1: The highest 8 bits of the name hash - // Part 2: The rest of the name hash (without the highest 8 bits) - HetHash = (BYTE)(FileNameHash >> (pHetTable->dwHashBitSize - 8)); - BetHash = FileNameHash & (AndMask64 >> 0x08); + // NameHash1: The highest 8 bits of the name hash + // NameHash2: File name hash limited to hash size + // Note: Our file table contains full name hash, no need to cut the high 8 bits before comparison + NameHash1 = (BYTE)(FileNameHash >> (pHetTable->dwNameHashBitSize - 8)); // Calculate the starting index to the hash table - StartIndex = Index = (DWORD)(FileNameHash % pHetTable->dwHashTableSize); + StartIndex = Index = (DWORD)(FileNameHash % pHetTable->dwTotalCount); // Go through HET table until we find a terminator - while(pHetTable->pHetHashes[Index] != HET_ENTRY_FREE) + while(pHetTable->pNameHashes[Index] != HET_ENTRY_FREE) { - // Did we find match ? - if(pHetTable->pHetHashes[Index] == HetHash) + // Did we find a match ? + if(pHetTable->pNameHashes[Index] == NameHash1) { DWORD dwFileIndex = 0; - // Get the index of the BetHash - pHetTable->pBetIndexes->GetBits(pHetTable->dwIndexSizeTotal * Index, - pHetTable->dwIndexSize, - &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(dwFileIndex <= ha->dwFileTableSize && ha->pFileTable[dwFileIndex].BetHash == BetHash) - return dwFileIndex; + // Get the file index + if(pHetTable->pBetIndexes->GetBits(pHetTable->dwIndexSizeTotal * Index, + pHetTable->dwIndexSize, + &dwFileIndex, + sizeof(DWORD)) == ERROR_SUCCESS) + { + // Verify the FileNameHash against the entry in the table of name hashes + if(dwFileIndex <= ha->dwFileTableSize && ha->pFileTable[dwFileIndex].FileNameHash == FileNameHash) + { + return dwFileIndex; + } + } } - // Move to the next entry in the primary search table + // Move to the next entry in the HET table // If we came to the start index again, we are done - Index = (Index + 1) % pHetTable->dwHashTableSize; + Index = (Index + 1) % pHetTable->dwTotalCount; if(Index == StartIndex) break; } @@ -877,94 +1676,12 @@ DWORD GetFileIndex_Het(TMPQArchive * ha, const char * szFileName) return HASH_ENTRY_FREE; } -DWORD AllocateHetEntry( - TMPQArchive * ha, - TFileEntry * pFileEntry) -{ - TMPQHetTable * pHetTable = ha->pHetTable; - ULONGLONG FileNameHash; - ULONGLONG AndMask64; - ULONGLONG OrMask64; - ULONGLONG BetHash; - DWORD FreeHetIndex = HASH_ENTRY_FREE; - DWORD dwFileIndex; - DWORD StartIndex; - DWORD Index; - BYTE HetHash; // Upper 8 bits of the masked file name hash - - // Do nothing if the MPQ has no HET table - assert(ha->pHetTable != NULL); - - // Calculate 64-bit hash of the file name - AndMask64 = pHetTable->AndMask64; - OrMask64 = pHetTable->OrMask64; - FileNameHash = (HashStringJenkins(pFileEntry->szFileName) & AndMask64) | OrMask64; - - // Calculate the starting index to the hash table - StartIndex = Index = (DWORD)(FileNameHash % pHetTable->dwHashTableSize); - - // Split the file name hash into two parts: - // Part 1: The highest 8 bits of the name hash - // Part 2: The rest of the name hash (without the highest 8 bits) - HetHash = (BYTE)(FileNameHash >> (pHetTable->dwHashBitSize - 8)); - BetHash = FileNameHash & (AndMask64 >> 0x08); - - // Go through HET table until we find a terminator - for(;;) - { - // Check for entries that might have been deleted - if(pHetTable->pHetHashes[Index] == HET_ENTRY_DELETED) - { - DWORD dwInvalidBetIndex = (1 << pHetTable->dwIndexSizeTotal) - 1; - DWORD dwBetIndex = 0; - - // Verify the BET index. If it's really free, we can use it - dwFileIndex = (DWORD)(pFileEntry - ha->pFileTable); - pHetTable->pBetIndexes->GetBits(pHetTable->dwIndexSizeTotal * Index, - pHetTable->dwIndexSize, - &dwBetIndex, - 4); - - if(dwBetIndex == dwInvalidBetIndex) - { - FreeHetIndex = Index; - break; - } - } - - // Is that entry free ? - if(pHetTable->pHetHashes[Index] == HET_ENTRY_FREE) - { - FreeHetIndex = Index; - break; - } - - // Move to the next entry in the primary search table - // If we came to the start index again, we are done - Index = (Index + 1) % pHetTable->dwHashTableSize; - if(Index == StartIndex) - return HASH_ENTRY_FREE; - } - - // Fill the HET table entry - dwFileIndex = (DWORD)(pFileEntry - ha->pFileTable); - pHetTable->pHetHashes[FreeHetIndex] = HetHash; - pHetTable->pBetIndexes->SetBits(pHetTable->dwIndexSizeTotal * FreeHetIndex, - pHetTable->dwIndexSize, - &dwFileIndex, - 4); - // Fill the file entry - pFileEntry->BetHash = BetHash; - pFileEntry->dwHetIndex = FreeHetIndex; - return FreeHetIndex; -} - void FreeHetTable(TMPQHetTable * pHetTable) { if(pHetTable != NULL) { - if(pHetTable->pHetHashes != NULL) - STORM_FREE(pHetTable->pHetHashes); + if(pHetTable->pNameHashes != NULL) + STORM_FREE(pHetTable->pNameHashes); if(pHetTable->pBetIndexes != NULL) STORM_FREE(pHetTable->pBetIndexes); @@ -975,9 +1692,19 @@ void FreeHetTable(TMPQHetTable * pHetTable) //----------------------------------------------------------------------------- // Support for BET table +static bool VerifyBetHeaderSize(TMPQArchive * /* ha */, TMPQBetHeader * pBetHeader) +{ + LPBYTE pbSrcData = (LPBYTE)(pBetHeader + 1); + LPBYTE pbSrcEnd = (LPBYTE)(pBetHeader) + pBetHeader->dwTableSize; + + // Move past the flags + pbSrcData = pbSrcData + (pBetHeader->dwFlagCount * sizeof(DWORD)) + (pBetHeader->dwEntryCount * pBetHeader->dwTableEntrySize) / 8; + return (pbSrcData <= pbSrcEnd); +} + static void CreateBetHeader( TMPQArchive * ha, - PBET_TABLE_HEADER pBetHeader) + TMPQBetHeader * pBetHeader) { TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; TFileEntry * pFileEntry; @@ -991,9 +1718,19 @@ static void CreateBetHeader( // Initialize array of flag combinations InitFileFlagArray(FlagArray); + // Fill the common header + pBetHeader->ExtHdr.dwSignature = BET_TABLE_SIGNATURE; + pBetHeader->ExtHdr.dwVersion = 1; + pBetHeader->ExtHdr.dwDataSize = 0; + // Get the maximum values for the BET table + pFileTableEnd = ha->pFileTable + ha->pHeader->dwBlockTableSize; for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) { + // + // Note: Deleted files must be counted as well + // + // Highest file position in the MPQ if(pFileEntry->ByteOffset > MaxByteOffset) MaxByteOffset = pFileEntry->ByteOffset; @@ -1036,24 +1773,25 @@ static void CreateBetHeader( pBetHeader->dwBitCount_Unknown; // Save the file count and flag count - pBetHeader->dwFileCount = ha->dwFileTableSize; + pBetHeader->dwEntryCount = ha->pHeader->dwBlockTableSize; pBetHeader->dwFlagCount = dwMaxFlagIndex + 1; pBetHeader->dwUnknown08 = 0x10; // Save the total size of the BET hash - pBetHeader->dwBetHashSizeTotal = ha->pHetTable->dwHashBitSize - 0x08; - pBetHeader->dwBetHashSizeExtra = 0; - pBetHeader->dwBetHashSize = pBetHeader->dwBetHashSizeTotal; - pBetHeader->dwBetHashArraySize = ((pBetHeader->dwBetHashSizeTotal * pBetHeader->dwFileCount) + 7) / 8; + pBetHeader->dwBitTotal_NameHash2 = ha->pHetTable->dwNameHashBitSize - 0x08; + pBetHeader->dwBitExtra_NameHash2 = 0; + pBetHeader->dwBitCount_NameHash2 = pBetHeader->dwBitTotal_NameHash2; + pBetHeader->dwNameHashArraySize = ((pBetHeader->dwBitTotal_NameHash2 * pBetHeader->dwEntryCount) + 7) / 8; // Save the total table size - pBetHeader->dwTableSize = sizeof(BET_TABLE_HEADER) + + pBetHeader->ExtHdr.dwDataSize = + pBetHeader->dwTableSize = sizeof(TMPQBetHeader) - sizeof(TMPQExtHeader) + pBetHeader->dwFlagCount * sizeof(DWORD) + - ((pBetHeader->dwTableEntrySize * pBetHeader->dwFileCount) + 7) / 8 + - pBetHeader->dwBetHashArraySize; + ((pBetHeader->dwTableEntrySize * pBetHeader->dwEntryCount) + 7) / 8 + + pBetHeader->dwNameHashArraySize; } -TMPQBetTable * CreateBetTable(DWORD dwFileCount) +TMPQBetTable * CreateBetTable(DWORD dwEntryCount) { TMPQBetTable * pBetTable; @@ -1062,7 +1800,7 @@ TMPQBetTable * CreateBetTable(DWORD dwFileCount) if(pBetTable != NULL) { memset(pBetTable, 0, sizeof(TMPQBetTable)); - pBetTable->dwFileCount = dwFileCount; + pBetTable->dwEntryCount = dwEntryCount; } return pBetTable; @@ -1070,93 +1808,99 @@ TMPQBetTable * CreateBetTable(DWORD dwFileCount) static TMPQBetTable * TranslateBetTable( TMPQArchive * ha, - TMPQExtTable * pExtTable) + TMPQBetHeader * pBetHeader) { - BET_TABLE_HEADER BetHeader; TMPQBetTable * pBetTable = NULL; - LPBYTE pbSrcData = (LPBYTE)(pExtTable + 1); - DWORD LengthInBytes; + LPBYTE pbSrcData = (LPBYTE)(pBetHeader + 1); + DWORD LengthInBytes = 0; // Sanity check - assert(pExtTable->dwSignature == BET_TABLE_SIGNATURE); - assert(pExtTable->dwVersion == 1); + assert(pBetHeader->ExtHdr.dwSignature == BET_TABLE_SIGNATURE); + assert(pBetHeader->ExtHdr.dwVersion == 1); assert(ha->pHetTable != NULL); ha = ha; // Verify size of the HET table - if(pExtTable != NULL && pExtTable->dwDataSize >= sizeof(BET_TABLE_HEADER)) + if(pBetHeader->ExtHdr.dwDataSize >= (sizeof(TMPQBetHeader) - sizeof(TMPQExtHeader))) { - // Copy the table header in order to have it aligned and swapped - memcpy(&BetHeader, pbSrcData, sizeof(BET_TABLE_HEADER)); - 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) + if(pBetHeader->ExtHdr.dwDataSize >= pBetHeader->dwTableSize) { - // Create translated table - pBetTable = CreateBetTable(BetHeader.dwFileCount); - if(pBetTable != NULL) + // The number of entries in the BET table must be the same like number of entries in the block table + // Note: Ignored if there is no block table + //assert(pBetHeader->dwEntryCount == ha->pHeader->dwBlockTableSize); + assert(pBetHeader->dwEntryCount <= ha->dwMaxFileCount); + + // The number of entries in the BET table must be the same like number of entries in the HET table + // Note that if it's not, it is not a problem + //assert(pBetHeader->dwEntryCount == ha->pHetTable->dwEntryCount); + + // Verify an obviously-wrong values + if(VerifyBetHeaderSize(ha, pBetHeader)) { - // Copy the variables from the header to the BetTable - pBetTable->dwTableEntrySize = BetHeader.dwTableEntrySize; - pBetTable->dwBitIndex_FilePos = BetHeader.dwBitIndex_FilePos; - pBetTable->dwBitIndex_FileSize = BetHeader.dwBitIndex_FileSize; - pBetTable->dwBitIndex_CmpSize = BetHeader.dwBitIndex_CmpSize; - pBetTable->dwBitIndex_FlagIndex = BetHeader.dwBitIndex_FlagIndex; - pBetTable->dwBitIndex_Unknown = BetHeader.dwBitIndex_Unknown; - pBetTable->dwBitCount_FilePos = BetHeader.dwBitCount_FilePos; - pBetTable->dwBitCount_FileSize = BetHeader.dwBitCount_FileSize; - pBetTable->dwBitCount_CmpSize = BetHeader.dwBitCount_CmpSize; - 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) + // Create translated table + pBetTable = CreateBetTable(pBetHeader->dwEntryCount); + if(pBetTable != NULL) { - // Allocate array for file flags and load it - pBetTable->pFileFlags = STORM_ALLOC(DWORD, BetHeader.dwFlagCount); - if(pBetTable->pFileFlags != NULL) + // Copy the variables from the header to the BetTable + pBetTable->dwTableEntrySize = pBetHeader->dwTableEntrySize; + pBetTable->dwBitIndex_FilePos = pBetHeader->dwBitIndex_FilePos; + pBetTable->dwBitIndex_FileSize = pBetHeader->dwBitIndex_FileSize; + pBetTable->dwBitIndex_CmpSize = pBetHeader->dwBitIndex_CmpSize; + pBetTable->dwBitIndex_FlagIndex = pBetHeader->dwBitIndex_FlagIndex; + pBetTable->dwBitIndex_Unknown = pBetHeader->dwBitIndex_Unknown; + pBetTable->dwBitCount_FilePos = pBetHeader->dwBitCount_FilePos; + pBetTable->dwBitCount_FileSize = pBetHeader->dwBitCount_FileSize; + pBetTable->dwBitCount_CmpSize = pBetHeader->dwBitCount_CmpSize; + pBetTable->dwBitCount_FlagIndex = pBetHeader->dwBitCount_FlagIndex; + pBetTable->dwBitCount_Unknown = pBetHeader->dwBitCount_Unknown; + + // Since we don't know what the "unknown" is, we'll assert when it's zero + assert(pBetTable->dwBitCount_Unknown == 0); + + // Allocate array for flags + if(pBetHeader->dwFlagCount != 0) { - LengthInBytes = BetHeader.dwFlagCount * sizeof(DWORD); - memcpy(pBetTable->pFileFlags, pbSrcData, LengthInBytes); - BSWAP_ARRAY32_UNSIGNED(pBetTable->pFileFlags, LengthInBytes); + // Allocate array for file flags and load it + pBetTable->pFileFlags = STORM_ALLOC(DWORD, pBetHeader->dwFlagCount); + if(pBetTable->pFileFlags != NULL) + { + LengthInBytes = pBetHeader->dwFlagCount * sizeof(DWORD); + memcpy(pBetTable->pFileFlags, pbSrcData, LengthInBytes); + BSWAP_ARRAY32_UNSIGNED(pBetTable->pFileFlags, LengthInBytes); + pbSrcData += LengthInBytes; + } + + // Save the number of flags + pBetTable->dwFlagCount = pBetHeader->dwFlagCount; + } + + // Load the bit-based file table + pBetTable->pFileTable = TMPQBits::Create(pBetTable->dwTableEntrySize * pBetHeader->dwEntryCount, 0); + if(pBetTable->pFileTable != NULL) + { + LengthInBytes = (pBetTable->pFileTable->NumberOfBits + 7) / 8; + memcpy(pBetTable->pFileTable->Elements, pbSrcData, LengthInBytes); pbSrcData += LengthInBytes; } - // Save the number of flags - pBetTable->dwFlagCount = BetHeader.dwFlagCount; + // Fill the sizes of BET hash + pBetTable->dwBitTotal_NameHash2 = pBetHeader->dwBitTotal_NameHash2; + pBetTable->dwBitExtra_NameHash2 = pBetHeader->dwBitExtra_NameHash2; + pBetTable->dwBitCount_NameHash2 = pBetHeader->dwBitCount_NameHash2; + + // Create and load the array of BET hashes + pBetTable->pNameHashes = TMPQBits::Create(pBetTable->dwBitTotal_NameHash2 * pBetHeader->dwEntryCount, 0); + if(pBetTable->pNameHashes != NULL) + { + LengthInBytes = (pBetTable->pNameHashes->NumberOfBits + 7) / 8; + memcpy(pBetTable->pNameHashes->Elements, pbSrcData, LengthInBytes); + // pbSrcData += LengthInBytes; + } + + // Dump both tables +// DumpHetAndBetTable(ha->pHetTable, pBetTable); } - - // Load the bit-based file table - pBetTable->pFileTable = CreateBitArray(pBetTable->dwTableEntrySize * BetHeader.dwFileCount, 0); - LengthInBytes = (pBetTable->pFileTable->NumberOfBits + 7) / 8; - if(pBetTable->pFileTable != NULL) - memcpy(pBetTable->pFileTable->Elements, pbSrcData, LengthInBytes); - pbSrcData += LengthInBytes; - - // Fill the sizes of BET hash - 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; - if(pBetTable->pBetHashes != NULL) - memcpy(pBetTable->pBetHashes->Elements, pbSrcData, LengthInBytes); - pbSrcData += BetHeader.dwBetHashArraySize; - - // Dump both tables -// DumpHetAndBetTable(ha->pHetTable, pBetTable); } } } @@ -1164,63 +1908,48 @@ static TMPQBetTable * TranslateBetTable( return pBetTable; } -TMPQExtTable * TranslateBetTable( +TMPQExtHeader * TranslateBetTable( TMPQArchive * ha, ULONGLONG * pcbBetTable) { - TMPQExtTable * pExtTable = NULL; - BET_TABLE_HEADER BetHeader; - TBitArray * pBitArray = NULL; + TMPQBetHeader * pBetHeader = NULL; + TMPQBetHeader BetHeader; + TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; + TFileEntry * pFileEntry; + TMPQBits * pBitArray = NULL; LPBYTE pbLinearTable = NULL; LPBYTE pbTrgData; - size_t BetTableSize; DWORD LengthInBytes; DWORD FlagArray[MAX_FLAG_INDEX]; - DWORD i; // Calculate the bit sizes of various entries InitFileFlagArray(FlagArray); CreateBetHeader(ha, &BetHeader); - // Calculate the size of the BET table - BetTableSize = sizeof(BET_TABLE_HEADER) + - BetHeader.dwFlagCount * sizeof(DWORD) + - ((BetHeader.dwTableEntrySize * BetHeader.dwFileCount) + 7) / 8 + - BetHeader.dwBetHashArraySize; - // Allocate space - pbLinearTable = STORM_ALLOC(BYTE, sizeof(TMPQExtTable) + BetTableSize); + pbLinearTable = STORM_ALLOC(BYTE, sizeof(TMPQExtHeader) + BetHeader.dwTableSize); if(pbLinearTable != NULL) { - // Create the common ext table header - pExtTable = (TMPQExtTable *)pbLinearTable; - pExtTable->dwSignature = BET_TABLE_SIGNATURE; - pExtTable->dwVersion = 1; - pExtTable->dwDataSize = (DWORD)BetTableSize; - pbTrgData = (LPBYTE)(pExtTable + 1); - - // Copy the BET table header - memcpy(pbTrgData, &BetHeader, sizeof(BET_TABLE_HEADER)); - BSWAP_ARRAY32_UNSIGNED(pbTrgData, sizeof(BET_TABLE_HEADER)); - pbTrgData += sizeof(BET_TABLE_HEADER); + // Copy the BET header to the linear buffer + pBetHeader = (TMPQBetHeader *)pbLinearTable; + memcpy(pBetHeader, &BetHeader, sizeof(TMPQBetHeader)); + pbTrgData = (LPBYTE)(pBetHeader + 1); // Save the bit-based block table - pBitArray = CreateBitArray(BetHeader.dwFileCount * BetHeader.dwTableEntrySize, 0); + pBitArray = TMPQBits::Create(BetHeader.dwEntryCount * BetHeader.dwTableEntrySize, 0); if(pBitArray != NULL) { - TFileEntry * pFileEntry = ha->pFileTable; DWORD dwFlagIndex = 0; DWORD nBitOffset = 0; - // Construct the array of flag values and bit-based file table - for(i = 0; i < BetHeader.dwFileCount; i++, pFileEntry++) + // Construct the bit-based file table + pFileTableEnd = ha->pFileTable + BetHeader.dwEntryCount; + for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) { // - // 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 !!! + // Note: Missing files must be included as well // - + // Save the byte offset pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_FilePos, BetHeader.dwBitCount_FilePos, @@ -1261,39 +1990,28 @@ TMPQExtTable * TranslateBetTable( STORM_FREE(pBitArray); } - // Create bit array for BET hashes - pBitArray = CreateBitArray(BetHeader.dwBetHashSizeTotal * BetHeader.dwFileCount, 0); + // Create bit array for name hashes + pBitArray = TMPQBits::Create(BetHeader.dwBitTotal_NameHash2 * BetHeader.dwEntryCount, 0); if(pBitArray != NULL) { - TFileEntry * pFileEntry = ha->pFileTable; - ULONGLONG AndMask64 = ha->pHetTable->AndMask64; - ULONGLONG OrMask64 = ha->pHetTable->OrMask64; + DWORD dwFileIndex = 0; - for(i = 0; i < BetHeader.dwFileCount; i++) + for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) { - ULONGLONG FileNameHash = 0; - - // Calculate 64-bit hash of the file name - if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) && pFileEntry->szFileName != NULL) - { - FileNameHash = (HashStringJenkins(pFileEntry->szFileName) & AndMask64) | OrMask64; - FileNameHash = FileNameHash & (AndMask64 >> 0x08); - } - // Insert the name hash to the bit array - pBitArray->SetBits(BetHeader.dwBetHashSizeTotal * i, - BetHeader.dwBetHashSize, - &FileNameHash, + pBitArray->SetBits(BetHeader.dwBitTotal_NameHash2 * dwFileIndex, + BetHeader.dwBitCount_NameHash2, + &pFileEntry->FileNameHash, 8); - // Move to the next file entry - pFileEntry++; + assert(dwFileIndex < BetHeader.dwEntryCount); + dwFileIndex++; } // Write the array of BET hashes LengthInBytes = (pBitArray->NumberOfBits + 7) / 8; memcpy(pbTrgData, pBitArray->Elements, LengthInBytes); - pbTrgData += LengthInBytes; +// pbTrgData += LengthInBytes; // Free the bit array STORM_FREE(pBitArray); @@ -1302,11 +2020,13 @@ TMPQExtTable * TranslateBetTable( // Write the size of the BET table in the MPQ if(pcbBetTable != NULL) { - *pcbBetTable = (ULONGLONG)(sizeof(TMPQExtTable) + BetTableSize); + *pcbBetTable = (ULONGLONG)(sizeof(TMPQExtHeader) + BetHeader.dwTableSize); } } - return pExtTable; + // Keep Coverity happy + assert((TMPQExtHeader *)&pBetHeader->ExtHdr == (TMPQExtHeader *)pbLinearTable); + return (TMPQExtHeader *)pbLinearTable; } void FreeBetTable(TMPQBetTable * pBetTable) @@ -1317,8 +2037,8 @@ void FreeBetTable(TMPQBetTable * pBetTable) STORM_FREE(pBetTable->pFileTable); if(pBetTable->pFileFlags != NULL) STORM_FREE(pBetTable->pFileFlags); - if(pBetTable->pBetHashes != NULL) - STORM_FREE(pBetTable->pBetHashes); + if(pBetTable->pNameHashes != NULL) + STORM_FREE(pBetTable->pNameHashes); STORM_FREE(pBetTable); } @@ -1327,11 +2047,25 @@ void FreeBetTable(TMPQBetTable * pBetTable) //----------------------------------------------------------------------------- // Support for file table -TFileEntry * GetFileEntryAny(TMPQArchive * ha, const char * szFileName) +TFileEntry * GetFileEntryLocale(TMPQArchive * ha, const char * szFileName, LCID lcFileLocale, LPDWORD PtrHashIndex) { TMPQHash * pHash; DWORD dwFileIndex; + // First, we have to search the classic hash table + // This is because on renaming, deleting, or changing locale, + // we will need the pointer to hash table entry + if(ha->pHashTable != NULL) + { + pHash = GetHashEntryLocale(ha, szFileName, lcFileLocale); + if(pHash != NULL && MPQ_BLOCK_INDEX(pHash) < ha->dwFileTableSize) + { + if(PtrHashIndex != NULL) + PtrHashIndex[0] = (DWORD)(pHash - ha->pHashTable); + return ha->pFileTable + MPQ_BLOCK_INDEX(pHash); + } + } + // If we have HET table in the MPQ, try to find the file in HET table if(ha->pHetTable != NULL) { @@ -1340,77 +2074,44 @@ TFileEntry * GetFileEntryAny(TMPQArchive * ha, const char * szFileName) return ha->pFileTable + dwFileIndex; } - // Otherwise, perform the file search in the classic hash table - if(ha->pHashTable != NULL) - { - pHash = GetHashEntryAny(ha, szFileName); - if(pHash != NULL && pHash->dwBlockIndex < ha->dwFileTableSize) - return ha->pFileTable + pHash->dwBlockIndex; - } - // Not found return NULL; } -TFileEntry * GetFileEntryLocale(TMPQArchive * ha, const char * szFileName, LCID lcLocale) +TFileEntry * GetFileEntryExact(TMPQArchive * ha, const char * szFileName, LCID lcFileLocale, LPDWORD PtrHashIndex) { TMPQHash * pHash; DWORD dwFileIndex; + // If the hash table is present, find the entry from hash table + if(ha->pHashTable != NULL) + { + pHash = GetHashEntryExact(ha, szFileName, lcFileLocale); + if(pHash != NULL && MPQ_BLOCK_INDEX(pHash) < ha->dwFileTableSize) + { + if(PtrHashIndex != NULL) + PtrHashIndex[0] = (DWORD)(pHash - ha->pHashTable); + return ha->pFileTable + MPQ_BLOCK_INDEX(pHash); + } + } + // If we have HET table in the MPQ, try to find the file in HET table if(ha->pHetTable != NULL) { dwFileIndex = GetFileIndex_Het(ha, szFileName); if(dwFileIndex != HASH_ENTRY_FREE) + { + if(PtrHashIndex != NULL) + PtrHashIndex[0] = HASH_ENTRY_FREE; return ha->pFileTable + dwFileIndex; + } } - // Otherwise, perform the file search in the classic hash table - if(ha->pHashTable != NULL) - { - pHash = GetHashEntryLocale(ha, szFileName, lcLocale); - if(pHash != NULL && pHash->dwBlockIndex < ha->dwFileTableSize) - return ha->pFileTable + pHash->dwBlockIndex; - } - // Not found return NULL; } -TFileEntry * GetFileEntryExact(TMPQArchive * ha, const char * szFileName, LCID lcLocale) -{ - TMPQHash * pHash; - DWORD dwFileIndex; - - // If we have HET table in the MPQ, try to find the file in HET table - if(ha->pHetTable != NULL) - { - dwFileIndex = GetFileIndex_Het(ha, szFileName); - if(dwFileIndex != HASH_ENTRY_FREE) - return ha->pFileTable + dwFileIndex; - } - - // Otherwise, perform the file search in the classic hash table - if(ha->pHashTable != NULL) - { - pHash = GetHashEntryExact(ha, szFileName, lcLocale); - if(pHash != NULL && pHash->dwBlockIndex < ha->dwFileTableSize) - return ha->pFileTable + pHash->dwBlockIndex; - } - - // Not found - return NULL; -} - -TFileEntry * GetFileEntryByIndex(TMPQArchive * ha, DWORD dwIndex) -{ - // For MPQs with classic hash table - if(dwIndex < ha->dwFileTableSize) - return ha->pFileTable + dwIndex; - return NULL; -} - -void AllocateFileName(TFileEntry * pFileEntry, const char * szFileName) +void AllocateFileName(TMPQArchive * ha, TFileEntry * pFileEntry, const char * szFileName) { // Sanity check assert(pFileEntry != NULL); @@ -1430,170 +2131,114 @@ void AllocateFileName(TFileEntry * pFileEntry, const char * szFileName) if(pFileEntry->szFileName != NULL) strcpy(pFileEntry->szFileName, szFileName); } + + // We also need to create the file name hash + if(ha->pHetTable != NULL) + { + ULONGLONG AndMask64 = ha->pHetTable->AndMask64; + ULONGLONG OrMask64 = ha->pHetTable->OrMask64; + + pFileEntry->FileNameHash = (HashStringJenkins(szFileName) & AndMask64) | OrMask64; + } } - -// Finds a free file entry. Does NOT increment table size. -TFileEntry * FindFreeFileEntry(TMPQArchive * ha) +TFileEntry * AllocateFileEntry(TMPQArchive * ha, const char * szFileName, LCID lcFileLocale, LPDWORD PtrHashIndex) { TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; TFileEntry * pFreeEntry = NULL; TFileEntry * pFileEntry; + TMPQHash * pHash = NULL; + DWORD dwReservedFiles = ha->dwReservedFiles; + DWORD dwFreeCount = 0; - // Try to find a free entry + // Sanity check: File table size must be greater or equal to max file count + assert(ha->dwFileTableSize >= ha->dwMaxFileCount); + + // If we are saving MPQ tables, we don't tale number of reserved files into account + dwReservedFiles = (ha->dwFlags & MPQ_FLAG_SAVING_TABLES) ? 0 : ha->dwReservedFiles; + + // Now find a free entry in the file table. + // Note that in the case when free entries are in the middle, + // we need to use these for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) { - // If that entry is free, we reuse it if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0) { - pFreeEntry = pFileEntry; - break; + // Remember the first free entry + if(pFreeEntry == NULL) + pFreeEntry = pFileEntry; + dwFreeCount++; + + // If the number of free items is greater than number + // of reserved items, We can add the file + if(dwFreeCount > dwReservedFiles) + 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 the total number of free entries is less than number of reserved files, + // we cannot add the file to the archive + if(pFreeEntry == NULL || dwFreeCount <= dwReservedFiles) + return NULL; - // If no file entry within the existing file table is free, - // we try the reserve space after current file table - if(ha->dwFileTableSize < ha->dwMaxFileCount) - return ha->pFileTable + ha->dwFileTableSize; + // Initialize the file entry and set its file name + memset(pFreeEntry, 0, sizeof(TFileEntry)); + AllocateFileName(ha, pFreeEntry, szFileName); - // If we reached maximum file count, we cannot add more files to the MPQ - assert(ha->dwFileTableSize == ha->dwMaxFileCount); - return NULL; -} - - -TFileEntry * AllocateFileEntry(TMPQArchive * ha, const char * szFileName, LCID lcLocale) -{ - TFileEntry * pFileEntry = NULL; - TMPQHash * pHash; - DWORD dwHashIndex; - DWORD dwFileIndex; - bool bHashEntryExists = false; - bool bHetEntryExists = false; - - // If the archive has classic hash table, we try to - // find the file in the hash table + // If the archive has a hash table, we need to first free entry there if(ha->pHashTable != NULL) { - // If the hash entry is already there, we reuse the file entry - pHash = GetHashEntryExact(ha, szFileName, lcLocale); - if(pHash != NULL) - { - pFileEntry = ha->pFileTable + pHash->dwBlockIndex; - bHashEntryExists = true; - } + // Make sure that the entry is not there yet + assert(GetHashEntryExact(ha, szFileName, lcFileLocale) == NULL); + + // Find a free hash table entry for the name + pHash = AllocateHashEntry(ha, pFreeEntry, lcFileLocale); + if(pHash == NULL) + return NULL; + + // Set the file index to the hash table + pHash->dwBlockIndex = (DWORD)(pFreeEntry - ha->pFileTable); + PtrHashIndex[0] = (DWORD)(pHash - ha->pHashTable); } - // If the archive has HET table, try to use it for - // finding the file + // If the archive has a HET table, just do some checks + // Note: Don't bother modifying the HET table. It will be rebuilt from scratch after, anyway if(ha->pHetTable != NULL) { - dwFileIndex = GetFileIndex_Het(ha, szFileName); - if(dwFileIndex != HASH_ENTRY_FREE) - { - pFileEntry = ha->pFileTable + dwFileIndex; - bHetEntryExists = true; - } + assert(GetFileIndex_Het(ha, szFileName) == HASH_ENTRY_FREE); } - // If still haven't found the file entry, we allocate new one - if(pFileEntry == NULL) - { - pFileEntry = FindFreeFileEntry(ha); - if(pFileEntry == NULL) - return NULL; - } - - // Fill the rest of the file entry - pFileEntry->ByteOffset = 0; - pFileEntry->FileTime = 0; - pFileEntry->dwFileSize = 0; - pFileEntry->dwCmpSize = 0; - pFileEntry->dwFlags = 0; - pFileEntry->lcLocale = 0; - pFileEntry->wPlatform = 0; - pFileEntry->dwCrc32 = 0; - memset(pFileEntry->md5, 0, MD5_DIGEST_SIZE); - - // Allocate space for file name, if it's not there yet - AllocateFileName(pFileEntry, szFileName); - - // If the free file entry is at the end of the file table, - // we have to increment file table size - if(pFileEntry == ha->pFileTable + ha->dwFileTableSize) - { - assert(ha->dwFileTableSize < ha->dwMaxFileCount); - ha->pHeader->dwBlockTableSize++; - ha->dwFileTableSize++; - } - - // If the MPQ has hash table, we have to insert the new entry into the hash table - if(ha->pHashTable != NULL && bHashEntryExists == false) - { - dwHashIndex = AllocateHashEntry(ha, pFileEntry); - assert(dwHashIndex != HASH_ENTRY_FREE); - } - - // If the MPQ has HET table, we have to insert it to the HET table as well - if(ha->pHetTable != NULL && bHetEntryExists == false) - { - // TODO: Does HET table even support locales? - // Most probably, Blizzard gave up that silly idea long ago. - dwHashIndex = AllocateHetEntry(ha, pFileEntry); - assert(dwHashIndex != HASH_ENTRY_FREE); - } - - // Return the file entry - return pFileEntry; + // Return the free table entry + return pFreeEntry; } -int RenameFileEntry( +DWORD RenameFileEntry( TMPQArchive * ha, - TFileEntry * pFileEntry, + TMPQFile * hf, const char * szNewFileName) { - TMPQHash * pHash; - DWORD dwFileIndex; - int nError = ERROR_SUCCESS; + TFileEntry * pFileEntry = hf->pFileEntry; + TMPQHash * pHashEntry = hf->pHashEntry; + LCID lcFileLocale = 0; - // If the MPQ has classic hash table, clear the entry there + // If the archive hash hash table, we need to free the hash table entry if(ha->pHashTable != NULL) { - assert(pFileEntry->dwHashIndex < ha->pHeader->dwHashTableSize); + // The file must have hash table entry assigned + // Will exit if there are multiple HASH entries pointing to the same file entry + if(pHashEntry == NULL) + return ERROR_NOT_SUPPORTED; - pHash = ha->pHashTable + pFileEntry->dwHashIndex; - memset(pHash, 0xFF, sizeof(TMPQHash)); - pHash->dwBlockIndex = HASH_ENTRY_DELETED; - } + // Save the locale + lcFileLocale = SFILE_MAKE_LCID(pHashEntry->Locale, pHashEntry->Platform); - // 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 - pHetTable->pBetIndexes->SetBits(pHetTable->dwIndexSizeTotal * pFileEntry->dwHetIndex, - pHetTable->dwIndexSize, - &dwInvalidFileIndex, - 4); + // Mark the hash table entry as deleted + pHashEntry->dwHashCheck1 = 0xFFFFFFFF; + pHashEntry->dwHashCheck2 = 0xFFFFFFFF; + pHashEntry->Locale = 0xFFFF; + pHashEntry->Platform = 0xFF; + pHashEntry->Flags = 0xFF; + pHashEntry->dwBlockIndex = HASH_ENTRY_DELETED; } // Free the old file name @@ -1602,247 +2247,133 @@ int RenameFileEntry( pFileEntry->szFileName = NULL; // Allocate new file name - AllocateFileName(pFileEntry, szNewFileName); + AllocateFileName(ha, pFileEntry, szNewFileName); - // Now find a hash entry for the new file name + // Allocate new hash entry 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; + // Since we freed one hash entry before, this must succeed + hf->pHashEntry = AllocateHashEntry(ha, pFileEntry, lcFileLocale); + assert(hf->pHashEntry != NULL); } - // 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; - } - - // Invalidate the entries for (listfile) and (attributes) - // After we are done with MPQ changes, we need to re-create them - InvalidateInternalFiles(ha); - return nError; + return ERROR_SUCCESS; } -void ClearFileEntry( - TMPQArchive * ha, - TFileEntry * pFileEntry) +DWORD DeleteFileEntry(TMPQArchive * ha, TMPQFile * hf) { - TMPQHash * pHash = NULL; + TFileEntry * pFileEntry = hf->pFileEntry; + TMPQHash * pHashEntry = hf->pHashEntry; - // If the MPQ has classic hash table, clear the entry there + // If the archive hash hash table, we need to free the hash table entry if(ha->pHashTable != NULL) { - assert(pFileEntry->dwHashIndex < ha->pHeader->dwHashTableSize); + // The file must have hash table entry assigned + // Will exit if there are multiple HASH entries pointing to the same file entry + if(pHashEntry == NULL) + return ERROR_NOT_SUPPORTED; - pHash = ha->pHashTable + pFileEntry->dwHashIndex; - memset(pHash, 0xFF, sizeof(TMPQHash)); - pHash->dwBlockIndex = HASH_ENTRY_DELETED; - } - - // 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 - pHetTable->pBetIndexes->SetBits(pHetTable->dwIndexSizeTotal * pFileEntry->dwHetIndex, - pHetTable->dwIndexSize, - &dwInvalidFileIndex, - 4); + // Mark the hash table entry as deleted + pHashEntry->dwHashCheck1 = 0xFFFFFFFF; + pHashEntry->dwHashCheck2 = 0xFFFFFFFF; + pHashEntry->Locale = 0xFFFF; + pHashEntry->Platform = 0xFF; + pHashEntry->Flags = 0xFF; + pHashEntry->dwBlockIndex = HASH_ENTRY_DELETED; } // Free the file name, and set the file entry as deleted if(pFileEntry->szFileName != NULL) STORM_FREE(pFileEntry->szFileName); + pFileEntry->szFileName = NULL; - // Invalidate the file entry - memset(pFileEntry, 0, sizeof(TFileEntry)); + // + // Don't modify the HET table, because it gets recreated by the caller + // Don't decrement the number of entries in the file table + // Keep Byte Offset, file size, compressed size, CRC32 and MD5 + // Clear the file name hash and the MPQ_FILE_EXISTS bit + // + + pFileEntry->dwFlags &= ~MPQ_FILE_EXISTS; + pFileEntry->FileNameHash = 0; + return ERROR_SUCCESS; } -int FreeFileEntry( - TMPQArchive * ha, - TFileEntry * pFileEntry) +DWORD InvalidateInternalFile(TMPQArchive * ha, const char * szFileName, DWORD dwFlagNone, DWORD dwFlagNew, DWORD dwForceAddTheFile = 0) { - TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; - TFileEntry * pTempEntry; - int nError = ERROR_SUCCESS; + TMPQFile * hf = NULL; + DWORD dwFileFlags = MPQ_FILE_DEFAULT_INTERNAL; + DWORD dwErrCode = ERROR_FILE_NOT_FOUND; - // - // 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 - // - - if(ha->pHetTable == NULL) + // Open the file from the MPQ + if(SFileOpenFileEx((HANDLE)ha, szFileName, SFILE_OPEN_BASE_FILE, (HANDLE *)&hf)) { - TFileEntry * pLastFileEntry = ha->pFileTable + ha->dwFileTableSize - 1; - TFileEntry * pLastUsedEntry = pLastFileEntry; + // Remember the file flags + dwFileFlags = hf->pFileEntry->dwFlags; - // Zero the file entry - ClearFileEntry(ha, pFileEntry); + // Delete the file entry + dwErrCode = DeleteFileEntry(ha, hf); + if(dwErrCode == ERROR_SUCCESS) + dwForceAddTheFile = 1; - // 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; - } + // Close the file + FreeFileHandle(hf); + } - // Can we free some entries at the end? - if(pLastUsedEntry < pLastFileEntry) - { - // Fix the size of the file table entry - ha->dwFileTableSize = (DWORD)(pLastUsedEntry - ha->pFileTable) + 1; - ha->pHeader->dwBlockTableSize = ha->dwFileTableSize; - } + // Are we going to add the file? + if(dwForceAddTheFile) + { + ha->dwFlags |= dwFlagNew; + ha->dwReservedFiles++; } else { - // Note: Deleted entries in Blizzard MPQs version 4.0 - // normally contain valid byte offset and length - pFileEntry->dwFlags &= ~MPQ_FILE_EXISTS; - nError = ERROR_SUCCESS; + ha->dwFlags |= dwFlagNone; + dwFileFlags = 0; } - return nError; + // Return the intended file flags + return dwFileFlags; } void InvalidateInternalFiles(TMPQArchive * ha) { - TFileEntry * pFileEntry; - - // Invalidate the (listfile), if not done yet - if(!(ha->dwFlags & MPQ_FLAG_INV_LISTFILE)) + // Do nothing if we are in the middle of saving internal files + if(!(ha->dwFlags & MPQ_FLAG_SAVING_TABLES)) { - pFileEntry = GetFileEntryExact(ha, LISTFILE_NAME, LANG_NEUTRAL); - if(pFileEntry != NULL) - FreeFileEntry(ha, pFileEntry); - ha->dwFlags |= MPQ_FLAG_INV_LISTFILE; - } + // + // We clear the file entries for (listfile), (attributes) and (signature) + // For each internal file cleared, we increment the number + // of reserved entries in the file table. + // - // 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; - } - - // Remember that the MPQ has been changed and it will be necessary - // to update the tables - ha->dwFlags |= MPQ_FLAG_CHANGED; -} - -//----------------------------------------------------------------------------- -// Functions that loads and verify MPQ data bitmap - -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))) + // Invalidate the (listfile), if not done yet + if((ha->dwFlags & (MPQ_FLAG_LISTFILE_NONE | MPQ_FLAG_LISTFILE_NEW)) == 0) { - // Is it a valid data bitmap? - BSWAP_ARRAY32_UNSIGNED((LPDWORD)(&DataBitmap), sizeof(TMPQBitmap)); - if(DataBitmap.dwSignature == MPQ_DATA_BITMAP_SIGNATURE) - { - // We assume that MPQs with data bitmap begin at position 0 - assert(ha->MpqPos == 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) - { - // Copy the bitmap header - memcpy(pBitmap, &DataBitmap, sizeof(TMPQBitmap)); - - // Read the remaining part - if(!FileStream_Read(ha->pStream, &BitmapOffset, (pBitmap + 1), BitmapByteSize)) - { - STORM_FREE(pBitmap); - pBitmap = NULL; - } - } - } - } - } - - // If the caller asks for file completeness, check it - if(pBitmap != NULL && pbFileIsComplete != NULL) - { - LPBYTE pbBitmap = (LPBYTE)(pBitmap + 1); - DWORD i; - bool bFileIsComplete = true; - - // Calculate the number of whole bytes and extra bits of the bitmap - WholeByteCount = (DataBlockCount / 8); - ExtraBitsCount = (DataBlockCount & 7); - - // Verify the whole bytes - their value must be 0xFF - for(i = 0; i < WholeByteCount; i++) - { - if(pbBitmap[i] != 0xFF) - bFileIsComplete = false; + ha->dwFileFlags1 = InvalidateInternalFile(ha, LISTFILE_NAME, MPQ_FLAG_LISTFILE_NONE, MPQ_FLAG_LISTFILE_NEW, (ha->dwFlags & MPQ_FLAG_LISTFILE_FORCE)); } - // If there are extra bits, calculate the mask - if(ExtraBitsCount != 0) + // Invalidate the (attributes), if not done yet + if((ha->dwFlags & (MPQ_FLAG_ATTRIBUTES_NONE | MPQ_FLAG_ATTRIBUTES_NEW)) == 0) { - BYTE ExpectedValue = (BYTE)((1 << ExtraBitsCount) - 1); - - if(pbBitmap[i] != ExpectedValue) - bFileIsComplete = false; + ha->dwFileFlags2 = InvalidateInternalFile(ha, ATTRIBUTES_NAME, MPQ_FLAG_ATTRIBUTES_NONE, MPQ_FLAG_ATTRIBUTES_NEW); } - // Give the result to the caller - *pbFileIsComplete = bFileIsComplete; - } + // Invalidate the (signature), if not done yet + if((ha->dwFlags & (MPQ_FLAG_SIGNATURE_NONE | MPQ_FLAG_SIGNATURE_NEW)) == 0) + { + ha->dwFileFlags3 = InvalidateInternalFile(ha, SIGNATURE_NAME, MPQ_FLAG_SIGNATURE_NONE, MPQ_FLAG_SIGNATURE_NEW); + } - ha->pBitmap = pBitmap; - return ERROR_SUCCESS; + // Remember that the MPQ has been changed + ha->dwFlags |= MPQ_FLAG_CHANGED; + } } //----------------------------------------------------------------------------- // Support for file tables - hash table, block table, hi-block table -int CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize) +DWORD CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize) { TMPQHash * pHashTable; @@ -1850,6 +2381,10 @@ int CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize) assert((dwHashTableSize & (dwHashTableSize - 1)) == 0); assert(ha->pHashTable == NULL); + // If the required hash table size is zero, don't create anything + if(dwHashTableSize == 0) + dwHashTableSize = HASH_TABLE_SIZE_DEFAULT; + // Create the hash table pHashTable = STORM_ALLOC(TMPQHash, dwHashTableSize); if(pHashTable == NULL) @@ -1857,199 +2392,155 @@ int CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize) // Fill it memset(pHashTable, 0xFF, dwHashTableSize * sizeof(TMPQHash)); + ha->pHeader->dwHashTableSize = dwHashTableSize; + ha->dwMaxFileCount = dwHashTableSize; ha->pHashTable = pHashTable; - - // Set the max file count, if needed - if(ha->pHetTable == NULL) - ha->dwMaxFileCount = dwHashTableSize; return ERROR_SUCCESS; } -TMPQHash * LoadHashTable(TMPQArchive * ha) +static TMPQHash * LoadHashTable(TMPQArchive * ha) { TMPQHeader * pHeader = ha->pHeader; ULONGLONG ByteOffset; - TMPQHash * pHashTable; + TMPQHash * pHashTable = NULL; DWORD dwTableSize; DWORD dwCmpSize; - int nError; + DWORD dwRealTableSize = 0; - // If the MPQ has no hash table, do nothing - if(pHeader->dwHashTablePos == 0 && pHeader->wHashTablePosHi == 0) - return NULL; + // Note: It is allowed to load hash table if it is at offset 0. + // Example: MPQ_2016_v1_ProtectedMap_HashOffsIsZero.w3x +// if(pHeader->dwHashTablePos == 0 && pHeader->wHashTablePosHi == 0) +// return NULL; // If the hash table size is zero, do nothing if(pHeader->dwHashTableSize == 0) return NULL; - // Allocate buffer for the hash table - dwTableSize = pHeader->dwHashTableSize * sizeof(TMPQHash); - pHashTable = STORM_ALLOC(TMPQHash, pHeader->dwHashTableSize); - if(pHashTable == NULL) - 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) + // Load the hash table for MPQ variations + switch(ha->dwSubType) { - STORM_FREE(pHashTable); - pHashTable = NULL; + case MPQ_SUBTYPE_MPQ: + + // Calculate the position and size of the hash table + ByteOffset = FileOffsetFromMpqOffset(ha, MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos)); + dwTableSize = pHeader->dwHashTableSize * sizeof(TMPQHash); + dwCmpSize = (DWORD)pHeader->HashTableSize64; + + // Read, decrypt and uncompress the hash table + pHashTable = (TMPQHash *)LoadMpqTable(ha, ByteOffset, pHeader->MD5_HashTable, dwCmpSize, dwTableSize, g_dwHashTableKey, &dwRealTableSize); +// DumpHashTable(pHashTable, pHeader->dwHashTableSize); + + // If the hash table was cut, we can/have to defragment it + if(pHashTable != NULL && dwRealTableSize != 0 && dwRealTableSize < dwTableSize) + { + ha->dwRealHashTableSize = dwRealTableSize; + ha->dwFlags |= (MPQ_FLAG_MALFORMED | MPQ_FLAG_HASH_TABLE_CUT); + } + break; + + case MPQ_SUBTYPE_SQP: + pHashTable = LoadSqpHashTable(ha); + break; + + case MPQ_SUBTYPE_MPK: + pHashTable = LoadMpkHashTable(ha); + break; } - // Return the hash table + // Return the loaded hash table return pHashTable; } -static void FixBlockTableSize( - TMPQArchive * ha, - TMPQBlock * pBlockTable, - DWORD dwClaimedSize) +DWORD CreateFileTable(TMPQArchive * ha, DWORD dwFileTableSize) { - TMPQHeader * pHeader = ha->pHeader; - ULONGLONG BlockTableStart; - ULONGLONG BlockTableEnd; - ULONGLONG FileDataStart; + ha->pFileTable = STORM_ALLOC(TFileEntry, dwFileTableSize); + if(ha->pFileTable == NULL) + return ERROR_NOT_ENOUGH_MEMORY; - // 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; + memset(ha->pFileTable, 0x00, sizeof(TFileEntry) * dwFileTableSize); + ha->dwFileTableSize = dwFileTableSize; + return ERROR_SUCCESS; } -TMPQBlock * LoadBlockTable(TMPQArchive * ha, ULONGLONG FileSize) +TMPQBlock * LoadBlockTable(TMPQArchive * ha, bool /* bDontFixEntries */) { TMPQHeader * pHeader = ha->pHeader; - TMPQBlock * pBlockTable; + TMPQBlock * pBlockTable = NULL; ULONGLONG ByteOffset; DWORD dwTableSize; DWORD dwCmpSize; - int nError; + DWORD dwRealTableSize; - // Do nothing if the block table position is zero - if(pHeader->dwBlockTablePos == 0 && pHeader->wBlockTablePosHi == 0) - return NULL; + // Note: It is possible that the block table starts at offset 0 + // Example: MPQ_2016_v1_ProtectedMap_HashOffsIsZero.w3x +// 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) + // Load the block table for MPQ variations + switch(ha->dwSubType) { - pHeader->dwBlockTableSize = (DWORD)((FileSize - ByteOffset) / sizeof(TMPQBlock)); - pHeader->BlockTableSize64 = pHeader->dwBlockTableSize * sizeof(TMPQBlock); - dwTableSize = dwCmpSize = pHeader->dwBlockTableSize * sizeof(TMPQBlock); + case MPQ_SUBTYPE_MPQ: + + // Calculate byte position of the block table + ByteOffset = FileOffsetFromMpqOffset(ha, MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos)); + dwTableSize = pHeader->dwBlockTableSize * sizeof(TMPQBlock); + dwCmpSize = (DWORD)pHeader->BlockTableSize64; + + // Read, decrypt and uncompress the block table + pBlockTable = (TMPQBlock * )LoadMpqTable(ha, ByteOffset, NULL, dwCmpSize, dwTableSize, g_dwBlockTableKey, &dwRealTableSize); + + // If the block table was cut, we need to remember it + if(pBlockTable != NULL && dwRealTableSize && dwRealTableSize < dwTableSize) + ha->dwFlags |= (MPQ_FLAG_MALFORMED | MPQ_FLAG_BLOCK_TABLE_CUT); + break; + + case MPQ_SUBTYPE_SQP: + pBlockTable = LoadSqpBlockTable(ha); + break; + + case MPQ_SUBTYPE_MPK: + pBlockTable = LoadMpkBlockTable(ha); + break; } - // - // 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) +TMPQHetTable * LoadHetTable(TMPQArchive * ha) { - TMPQExtTable * pExtTable; + TMPQExtHeader * pExtTable; + TMPQHetTable * pHetTable = NULL; TMPQHeader * pHeader = ha->pHeader; - int nError = ERROR_SUCCESS; - // If the HET table position is not NULL, we expect - // both HET and BET tables to be present. - if(pHeader->HetTablePos64 != 0) + // If the HET table position is not 0, we expect the table to be present + // Alsom the HET table must have a reasonable size + if(pHeader->HetTablePos64 && pHeader->HetTableSize64 && pHeader->HetTableSize64 < BET_TABLE_MAX_SIZE) { // Attempt to load the HET table (Hash Extended Table) pExtTable = LoadExtTable(ha, pHeader->HetTablePos64, (size_t)pHeader->HetTableSize64, HET_TABLE_SIGNATURE, MPQ_KEY_HASH_TABLE); if(pExtTable != NULL) { - // If succeeded, we have to limit the maximum file count - // to the values saved in the HET table - // If loading HET table fails, we ignore the result. - ha->pHetTable = TranslateHetTable(pExtTable); - if(ha->pHetTable != NULL) - ha->dwMaxFileCount = ha->pHetTable->dwMaxFileCount; - + // Translate the loaded table into HET table. + pHetTable = TranslateHetTable((TMPQHetHeader *)pExtTable); STORM_FREE(pExtTable); } - - // If the HET hable failed to load, it's corrupt. - if(ha->pHetTable == NULL) - nError = ERROR_FILE_CORRUPT; } - return nError; + return pHetTable; } TMPQBetTable * LoadBetTable(TMPQArchive * ha) { - TMPQExtTable * pExtTable; + TMPQExtHeader * 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) + // If the BET table position is not 0, we expect the table to be present + if(pHeader->BetTablePos64 && pHeader->BetTableSize64 && pHeader->BetTableSize64 < BET_TABLE_MAX_SIZE) { // 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); @@ -2057,7 +2548,7 @@ TMPQBetTable * LoadBetTable(TMPQArchive * ha) { // If succeeded, we translate the BET table // to more readable form - pBetTable = TranslateBetTable(ha, pExtTable); + pBetTable = TranslateBetTable(ha, (TMPQBetHeader *)pExtTable); STORM_FREE(pExtTable); } } @@ -2065,7 +2556,7 @@ TMPQBetTable * LoadBetTable(TMPQArchive * ha) return pBetTable; } -int LoadAnyHashTable(TMPQArchive * ha) +DWORD LoadAnyHashTable(TMPQArchive * ha) { TMPQHeader * pHeader = ha->pHeader; @@ -2073,123 +2564,57 @@ int LoadAnyHashTable(TMPQArchive * ha) if(pHeader->dwHashTableSize == 0 && pHeader->HetTableSize64 == 0) return CreateHashTable(ha, HASH_TABLE_SIZE_DEFAULT); - // Try to load HET and/or classic hash table - LoadHetTable(ha); + // Try to load HET table + if(pHeader->HetTablePos64 != 0) + ha->pHetTable = LoadHetTable(ha); - // Load the HASH table - ha->pHashTable = LoadHashTable(ha); + // Try to load classic hash table + // Note that we load the classic hash table even when HET table exists, + // because if the MPQ gets modified and saved, hash table must be there + if(pHeader->dwHashTableSize) + 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? + // At least one of the tables must be present if(ha->pHetTable == NULL && ha->pHashTable == NULL) return 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; - } - + // Set the maximum file count to the size of the hash table. + // Note: We don't care about HET table limits, because HET table is rebuilt + // after each file add/rename/delete. + ha->dwMaxFileCount = (ha->pHashTable != NULL) ? pHeader->dwHashTableSize : HASH_TABLE_SIZE_MAX; return ERROR_SUCCESS; } -int BuildFileTable_Classic( - TMPQArchive * ha, - TFileEntry * pFileTable, - ULONGLONG FileSize) +static DWORD BuildFileTable_Classic(TMPQArchive * ha) { - TFileEntry * pFileEntry; TMPQHeader * pHeader = ha->pHeader; TMPQBlock * pBlockTable; - TMPQBlock * pBlock; - int nError = ERROR_SUCCESS; + DWORD dwErrCode = ERROR_SUCCESS; // Sanity checks assert(ha->pHashTable != NULL); + assert(ha->pFileTable != NULL); + + // If the MPQ has no block table, do nothing + if(pHeader->dwBlockTableSize == 0) + return ERROR_SUCCESS; + assert(ha->dwFileTableSize >= pHeader->dwBlockTableSize); // Load the block table - pBlockTable = LoadBlockTable(ha, FileSize); + // WARNING! ha->pFileTable can change in the process!! + pBlockTable = (TMPQBlock *)LoadBlockTable(ha); 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 + dwErrCode = BuildFileTableFromBlockTable(ha, pBlockTable); STORM_FREE(pBlockTable); } else { - nError = ERROR_NOT_ENOUGH_MEMORY; + dwErrCode = ERROR_NOT_ENOUGH_MEMORY; } // Load the hi-block table - if(nError == ERROR_SUCCESS && pHeader->HiBlockTablePos64 != 0) + if(dwErrCode == ERROR_SUCCESS && pHeader->HiBlockTablePos64 != 0) { ULONGLONG ByteOffset; USHORT * pHiBlockTable = NULL; @@ -2203,20 +2628,19 @@ int BuildFileTable_Classic( // 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(); + dwErrCode = SErrGetLastError(); // Now merge the hi-block table to the file table - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { - pFileEntry = pFileTable; + TFileEntry * pFileEntry = ha->pFileTable; + + // Swap the hi-block table + BSWAP_ARRAY16_UNSIGNED(pHiBlockTable, dwTableSize); // 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++; - } + for(DWORD i = 0; i < pHeader->dwBlockTableSize; i++, pFileEntry++) + pFileEntry->ByteOffset = MAKE_OFFSET64(pHiBlockTable[i], pFileEntry->ByteOffset); } // Free the hi-block table @@ -2224,93 +2648,114 @@ int BuildFileTable_Classic( } else { - nError = ERROR_NOT_ENOUGH_MEMORY; + dwErrCode = ERROR_NOT_ENOUGH_MEMORY; } } - // Set the current size of the file table - ha->dwFileTableSize = pHeader->dwBlockTableSize; - return nError; + return dwErrCode; } -int BuildFileTable_HetBet( - TMPQArchive * ha, - TFileEntry * pFileTable) +static DWORD BuildFileTable_HetBet(TMPQArchive * ha) { TMPQHetTable * pHetTable = ha->pHetTable; TMPQBetTable * pBetTable; - TFileEntry * pFileEntry = pFileTable; - TBitArray * pBitArray; + TFileEntry * pFileEntry = ha->pFileTable; + TMPQBits * pBitArray; DWORD dwBitPosition = 0; DWORD i; - int nError = ERROR_FILE_CORRUPT; + DWORD dwErrCode = ERROR_SUCCESS; // 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++) + // Verify the size of NameHash2 in the BET table. + // It has to be 8 bits less than the information in HET table + if((pBetTable->dwBitCount_NameHash2 + 8) != pHetTable->dwNameHashBitSize) + { + FreeBetTable(pBetTable); + return ERROR_FILE_CORRUPT; + } + + // Step one: Fill the name indexes + for(i = 0; i < pHetTable->dwTotalCount; i++) { DWORD dwFileIndex = 0; // Is the entry in the HET table occupied? - if(pHetTable->pHetHashes[i] != 0) + if(pHetTable->pNameHashes[i] != HET_ENTRY_FREE) { // Load the index to the BET table - pHetTable->pBetIndexes->GetBits(pHetTable->dwIndexSizeTotal * i, - pHetTable->dwIndexSize, - &dwFileIndex, - 4); - // Overflow test - if(dwFileIndex < pBetTable->dwFileCount) + dwErrCode = pHetTable->pBetIndexes->GetBits(pHetTable->dwIndexSizeTotal * i, + pHetTable->dwIndexSize, + &dwFileIndex, + 4); + if(dwErrCode != ERROR_SUCCESS) { - // Get the file entry and save HET index - pFileEntry = pFileTable + dwFileIndex; - pFileEntry->dwHetIndex = i; + FreeBetTable(pBetTable); + return ERROR_FILE_CORRUPT; + } + + // Overflow test + if(dwFileIndex < pBetTable->dwEntryCount) + { + ULONGLONG NameHash1 = pHetTable->pNameHashes[i]; + ULONGLONG NameHash2 = 0; // Load the BET hash - pBetTable->pBetHashes->GetBits(pBetTable->dwBetHashSizeTotal * dwFileIndex, - pBetTable->dwBetHashSize, - &pFileEntry->BetHash, - 8); + dwErrCode = pBetTable->pNameHashes->GetBits(pBetTable->dwBitTotal_NameHash2 * dwFileIndex, + pBetTable->dwBitCount_NameHash2, + &NameHash2, + 8); + if(dwErrCode != ERROR_SUCCESS) + { + FreeBetTable(pBetTable); + return ERROR_FILE_CORRUPT; + } + + // Combine both part of the name hash and put it to the file table + pFileEntry = ha->pFileTable + dwFileIndex; + pFileEntry->FileNameHash = (NameHash1 << pBetTable->dwBitCount_NameHash2) | NameHash2; } } } // 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++) + pFileEntry = ha->pFileTable; + pBitArray = pBetTable->pFileTable; + for(i = 0; i < pBetTable->dwEntryCount; i++) { DWORD dwFlagIndex = 0; // Read the file position - pBitArray->GetBits(dwBitPosition + pBetTable->dwBitIndex_FilePos, - pBetTable->dwBitCount_FilePos, - &pFileEntry->ByteOffset, - 8); + if((dwErrCode = pBitArray->GetBits(dwBitPosition + pBetTable->dwBitIndex_FilePos, + pBetTable->dwBitCount_FilePos, + &pFileEntry->ByteOffset, + 8)) != ERROR_SUCCESS) + break; // Read the file size - pBitArray->GetBits(dwBitPosition + pBetTable->dwBitIndex_FileSize, - pBetTable->dwBitCount_FileSize, - &pFileEntry->dwFileSize, - 4); + if((dwErrCode = pBitArray->GetBits(dwBitPosition + pBetTable->dwBitIndex_FileSize, + pBetTable->dwBitCount_FileSize, + &pFileEntry->dwFileSize, + 4)) != ERROR_SUCCESS) + break; // Read the compressed size - pBitArray->GetBits(dwBitPosition + pBetTable->dwBitIndex_CmpSize, - pBetTable->dwBitCount_CmpSize, - &pFileEntry->dwCmpSize, - 4); - + if((dwErrCode = pBitArray->GetBits(dwBitPosition + pBetTable->dwBitIndex_CmpSize, + pBetTable->dwBitCount_CmpSize, + &pFileEntry->dwCmpSize, + 4)) != ERROR_SUCCESS) + break; // Read the flag index if(pBetTable->dwFlagCount != 0) { - pBitArray->GetBits(dwBitPosition + pBetTable->dwBitIndex_FlagIndex, - pBetTable->dwBitCount_FlagIndex, - &dwFlagIndex, - 4); + if((dwErrCode = pBitArray->GetBits(dwBitPosition + pBetTable->dwBitIndex_FlagIndex, + pBetTable->dwBitCount_FlagIndex, + &dwFlagIndex, + 4)) != ERROR_SUCCESS) + break; pFileEntry->dwFlags = pBetTable->pFileFlags[dwFlagIndex]; } @@ -2325,40 +2770,42 @@ int BuildFileTable_HetBet( } // Set the current size of the file table - ha->dwFileTableSize = pBetTable->dwFileCount; FreeBetTable(pBetTable); - nError = ERROR_SUCCESS; } else { - nError = ERROR_FILE_CORRUPT; + dwErrCode = ERROR_FILE_CORRUPT; } - - return nError; + return dwErrCode; } -int BuildFileTable(TMPQArchive * ha, ULONGLONG FileSize) +DWORD BuildFileTable(TMPQArchive * ha) { - TFileEntry * pFileTable; + DWORD dwFileTableSize; bool bFileTableCreated = false; // Sanity checks + assert(ha->pFileTable == NULL); assert(ha->dwFileTableSize == 0); assert(ha->dwMaxFileCount != 0); + // Determine the allocation size for the file table + dwFileTableSize = STORMLIB_MAX(ha->pHeader->dwBlockTableSize, ha->dwMaxFileCount); + // Allocate the file table with size determined before - pFileTable = STORM_ALLOC(TFileEntry, ha->dwMaxFileCount); - if(pFileTable == NULL) + ha->pFileTable = STORM_ALLOC(TFileEntry, dwFileTableSize); + if(ha->pFileTable == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Fill the table with zeros - memset(pFileTable, 0, ha->dwMaxFileCount * sizeof(TFileEntry)); + memset(ha->pFileTable, 0, dwFileTableSize * sizeof(TFileEntry)); + ha->dwFileTableSize = dwFileTableSize; // If we have HET table, we load file table from the BET table // Note: If BET table is corrupt or missing, we set the archive as read only if(ha->pHetTable != NULL) { - if(BuildFileTable_HetBet(ha, pFileTable) != ERROR_SUCCESS) + if(BuildFileTable_HetBet(ha) != ERROR_SUCCESS) ha->dwFlags |= MPQ_FLAG_READ_ONLY; else bFileTableCreated = true; @@ -2368,29 +2815,232 @@ int BuildFileTable(TMPQArchive * ha, ULONGLONG FileSize) // Note: If block table is corrupt or missing, we set the archive as read only if(ha->pHashTable != NULL) { - if(BuildFileTable_Classic(ha, pFileTable, FileSize) != ERROR_SUCCESS) + if(BuildFileTable_Classic(ha) != ERROR_SUCCESS) ha->dwFlags |= MPQ_FLAG_READ_ONLY; else bFileTableCreated = true; } - - // If something failed, we free the file table entry - if(bFileTableCreated == false) + + // Return result + return bFileTableCreated ? ERROR_SUCCESS : ERROR_FILE_CORRUPT; +} + +/* +void UpdateBlockTableSize(TMPQArchive * ha) +{ + TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; + TFileEntry * pFileEntry; + DWORD dwBlockTableSize = 0; + + // Calculate the number of files + for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) { - STORM_FREE(pFileTable); - return ERROR_FILE_CORRUPT; + // If the source table entry is valid, + if(pFileEntry->dwFlags & MPQ_FILE_EXISTS) + dwBlockTableSize = (DWORD)(pFileEntry - ha->pFileTable) + 1; + } + + // Save the block table size to the MPQ header + ha->pHeader->dwBlockTableSize = ha->dwReservedFiles + dwBlockTableSize; +} +*/ + +// Defragment the file table so it does not contain any gaps +DWORD DefragmentFileTable(TMPQArchive * ha) +{ + TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; + TFileEntry * pSource = ha->pFileTable; + TFileEntry * pTarget = ha->pFileTable; + LPDWORD DefragmentTable; + DWORD dwBlockTableSize = 0; + DWORD dwSrcIndex; + DWORD dwTrgIndex; + + // Allocate brand new file table + DefragmentTable = STORM_ALLOC(DWORD, ha->dwFileTableSize); + if(DefragmentTable != NULL) + { + // Clear the file table + memset(DefragmentTable, 0xFF, sizeof(DWORD) * ha->dwFileTableSize); + + // Parse the entire file table and defragment it + for(; pSource < pFileTableEnd; pSource++) + { + // If the source table entry is valid, + if(pSource->dwFlags & MPQ_FILE_EXISTS) + { + // Remember the index conversion + dwSrcIndex = (DWORD)(pSource - ha->pFileTable); + dwTrgIndex = (DWORD)(pTarget - ha->pFileTable); + DefragmentTable[dwSrcIndex] = dwTrgIndex; + + // Move the entry, if needed + if(pTarget != pSource) + pTarget[0] = pSource[0]; + pTarget++; + + // Update the block table size + dwBlockTableSize = (DWORD)(pTarget - ha->pFileTable); + } + else + { + // If there is file name left, free it + if(pSource->szFileName != NULL) + STORM_FREE(pSource->szFileName); + pSource->szFileName = NULL; + } + } + + // Did we defragment something? + if(pTarget < pFileTableEnd) + { + // Clear the remaining file entries + memset(pTarget, 0, (pFileTableEnd - pTarget) * sizeof(TFileEntry)); + + // Go through the hash table and relocate the block indexes + if(ha->pHashTable != NULL) + { + TMPQHash * pHashTableEnd = ha->pHashTable + ha->pHeader->dwHashTableSize; + TMPQHash * pHash; + DWORD dwNewBlockIndex; + + for(pHash = ha->pHashTable; pHash < pHashTableEnd; pHash++) + { + if(MPQ_BLOCK_INDEX(pHash) < ha->dwFileTableSize) + { + // If that block entry is there, set it to the hash entry + // If not, set it as DELETED + dwNewBlockIndex = DefragmentTable[MPQ_BLOCK_INDEX(pHash)]; + pHash->dwBlockIndex = (dwNewBlockIndex != HASH_ENTRY_FREE) ? dwNewBlockIndex : HASH_ENTRY_DELETED; + } + } + } + } + + // Save the block table size + ha->pHeader->dwBlockTableSize = ha->dwReservedFiles + dwBlockTableSize; + + // Free the defragment table + STORM_FREE(DefragmentTable); } - // Assign it to the archive structure - ha->pFileTable = pFileTable; return ERROR_SUCCESS; } -// Saves MPQ header, hash table, block table and hi-block table. -int SaveMPQTables(TMPQArchive * ha) +// Rebuilds the HET table from scratch based on the file table +// Used after a modifying operation (add, rename, delete) +DWORD RebuildHetTable(TMPQArchive * ha) { - TMPQExtTable * pHetTable = NULL; - TMPQExtTable * pBetTable = NULL; + TMPQHetTable * pOldHetTable = ha->pHetTable; + TFileEntry * pFileTableEnd; + TFileEntry * pFileEntry; + DWORD dwBlockTableSize = ha->dwFileTableSize; + DWORD dwErrCode = ERROR_SUCCESS; + + // If we are in the state of saving MPQ tables, the real size of block table + // must already have been calculated. Use that value instead + if(ha->dwFlags & MPQ_FLAG_SAVING_TABLES) + { + assert(ha->pHeader->dwBlockTableSize != 0); + dwBlockTableSize = ha->pHeader->dwBlockTableSize; + } + + // Create new HET table based on the total number of entries in the file table + // Note that if we fail to create it, we just stop using HET table + ha->pHetTable = CreateHetTable(dwBlockTableSize, 0, 0x40, NULL); + if(ha->pHetTable != NULL) + { + // Go through the file table again and insert all existing files + pFileTableEnd = ha->pFileTable + dwBlockTableSize; + for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) + { + if(pFileEntry->dwFlags & MPQ_FILE_EXISTS) + { + // Get the high + dwErrCode = InsertHetEntry(ha->pHetTable, pFileEntry->FileNameHash, (DWORD)(pFileEntry - ha->pFileTable)); + if(dwErrCode != ERROR_SUCCESS) + break; + } + } + } + + // Free the old HET table + FreeHetTable(pOldHetTable); + return dwErrCode; +} + +// Rebuilds the file table, removing all deleted file entries. +// Used when compacting the archive +DWORD RebuildFileTable(TMPQArchive * ha, DWORD dwNewHashTableSize) +{ + TFileEntry * pFileEntry; + TMPQHash * pHashTableEnd = ha->pHashTable + ha->pHeader->dwHashTableSize; + TMPQHash * pOldHashTable = ha->pHashTable; + TMPQHash * pHashTable = NULL; + TMPQHash * pHash; + DWORD dwErrCode = ERROR_SUCCESS; + + // The new hash table size must be greater or equal to the current hash table size + assert(dwNewHashTableSize >= ha->pHeader->dwHashTableSize); + assert(dwNewHashTableSize >= ha->dwMaxFileCount); + assert((dwNewHashTableSize & (dwNewHashTableSize - 1)) == 0); + assert(ha->pHashTable != NULL); + + // Reallocate the new file table, if needed + if(dwNewHashTableSize > ha->dwFileTableSize) + { + ha->pFileTable = STORM_REALLOC(TFileEntry, ha->pFileTable, dwNewHashTableSize); + if(ha->pFileTable == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + memset(ha->pFileTable + ha->dwFileTableSize, 0, (dwNewHashTableSize - ha->dwFileTableSize) * sizeof(TFileEntry)); + } + + // Allocate new hash table + if(dwErrCode == ERROR_SUCCESS) + { + pHashTable = STORM_ALLOC(TMPQHash, dwNewHashTableSize); + if(pHashTable == NULL) + dwErrCode = ERROR_NOT_ENOUGH_MEMORY; + } + + // If both succeeded, we need to rebuild the file table + if(dwErrCode == ERROR_SUCCESS) + { + // Make sure that the hash table is properly filled + memset(pHashTable, 0xFF, sizeof(TMPQHash) * dwNewHashTableSize); + ha->pHashTable = pHashTable; + + // Set the new limits to the MPQ archive + ha->pHeader->dwHashTableSize = dwNewHashTableSize; + + // Parse the old hash table and copy all entries to the new table + for(pHash = pOldHashTable; pHash < pHashTableEnd; pHash++) + { + if(IsValidHashEntry(ha, pHash)) + { + pFileEntry = ha->pFileTable + MPQ_BLOCK_INDEX(pHash); + AllocateHashEntry(ha, pFileEntry, SFILE_MAKE_LCID(pHash->Locale, pHash->Platform)); + } + } + + // Increment the max file count for the file + ha->dwFileTableSize = dwNewHashTableSize; + ha->dwMaxFileCount = dwNewHashTableSize; + ha->dwFlags |= MPQ_FLAG_CHANGED; + } + + // Now free the remaining entries + if(pOldHashTable != NULL) + STORM_FREE(pOldHashTable); + return dwErrCode; +} + +// Saves MPQ header, hash table, block table and hi-block table. +DWORD SaveMPQTables(TMPQArchive * ha) +{ + TMPQExtHeader * pHetTable = NULL; + TMPQExtHeader * pBetTable = NULL; TMPQHeader * pHeader = ha->pHeader; TMPQBlock * pBlockTable = NULL; TMPQHash * pHashTable = NULL; @@ -2403,136 +3053,142 @@ int SaveMPQTables(TMPQArchive * ha) USHORT * pHiBlockTable = NULL; DWORD cbTotalSize; bool bNeedHiBlockTable = false; - int nError = ERROR_SUCCESS; + DWORD dwErrCode = ERROR_SUCCESS; // 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 - FindFreeMpqSpace(ha, &TablePos); + // Find the space where the MPQ tables will be saved + TablePos = FindFreeMpqSpace(ha); // If the MPQ has HET table, we prepare a ready-to-save version - if(nError == ERROR_SUCCESS && ha->pHetTable != NULL) + if(dwErrCode == ERROR_SUCCESS && ha->pHetTable != NULL) { pHetTable = TranslateHetTable(ha->pHetTable, &HetTableSize64); if(pHetTable == NULL) - nError = ERROR_NOT_ENOUGH_MEMORY; + dwErrCode = ERROR_NOT_ENOUGH_MEMORY; } // If the MPQ has HET table, we also must create BET table to be saved - if(nError == ERROR_SUCCESS && ha->pHetTable != NULL) + if(dwErrCode == ERROR_SUCCESS && ha->pHetTable != NULL) { pBetTable = TranslateBetTable(ha, &BetTableSize64); if(pBetTable == NULL) - nError = ERROR_NOT_ENOUGH_MEMORY; + dwErrCode = ERROR_NOT_ENOUGH_MEMORY; } // Now create hash table - if(nError == ERROR_SUCCESS && ha->pHashTable != NULL) + if(dwErrCode == ERROR_SUCCESS && ha->pHashTable != NULL) { pHashTable = TranslateHashTable(ha, &HashTableSize64); if(pHashTable == NULL) - nError = ERROR_NOT_ENOUGH_MEMORY; + dwErrCode = ERROR_NOT_ENOUGH_MEMORY; } // Create block table - if(nError == ERROR_SUCCESS && ha->pHashTable != NULL) + if(dwErrCode == ERROR_SUCCESS && ha->pFileTable != NULL) { pBlockTable = TranslateBlockTable(ha, &BlockTableSize64, &bNeedHiBlockTable); if(pBlockTable == NULL) - nError = ERROR_NOT_ENOUGH_MEMORY; + dwErrCode = ERROR_NOT_ENOUGH_MEMORY; } // Create hi-block table, if needed - if(nError == ERROR_SUCCESS && bNeedHiBlockTable) + if(dwErrCode == ERROR_SUCCESS && bNeedHiBlockTable) { pHiBlockTable = TranslateHiBlockTable(ha, &HiBlockTableSize64); if(pHiBlockTable == NULL) - nError = ERROR_NOT_ENOUGH_MEMORY; + dwErrCode = ERROR_NOT_ENOUGH_MEMORY; } // Write the HET table, if any - if(nError == ERROR_SUCCESS && pHetTable != NULL) + if(dwErrCode == ERROR_SUCCESS && pHetTable != NULL) { pHeader->HetTableSize64 = HetTableSize64; pHeader->HetTablePos64 = TablePos; - nError = SaveExtTable(ha, pHetTable, TablePos, (DWORD)HetTableSize64, pHeader->MD5_HetTable, MPQ_KEY_HASH_TABLE, false, &cbTotalSize); + dwErrCode = SaveExtTable(ha, pHetTable, TablePos, (DWORD)HetTableSize64, pHeader->MD5_HetTable, MPQ_KEY_HASH_TABLE, false, &cbTotalSize); TablePos += cbTotalSize; } // Write the BET table, if any - if(nError == ERROR_SUCCESS && pBetTable != NULL) + if(dwErrCode == ERROR_SUCCESS && pBetTable != NULL) { pHeader->BetTableSize64 = BetTableSize64; pHeader->BetTablePos64 = TablePos; - nError = SaveExtTable(ha, pBetTable, TablePos, (DWORD)BetTableSize64, pHeader->MD5_BetTable, MPQ_KEY_BLOCK_TABLE, false, &cbTotalSize); + dwErrCode = SaveExtTable(ha, pBetTable, TablePos, (DWORD)BetTableSize64, pHeader->MD5_BetTable, MPQ_KEY_BLOCK_TABLE, false, &cbTotalSize); TablePos += cbTotalSize; } // Write the hash table, if we have any - if(nError == ERROR_SUCCESS && pHashTable != NULL) + if(dwErrCode == ERROR_SUCCESS && pHashTable != NULL) { pHeader->HashTableSize64 = HashTableSize64; pHeader->wHashTablePosHi = (USHORT)(TablePos >> 32); pHeader->dwHashTableSize = (DWORD)(HashTableSize64 / sizeof(TMPQHash)); pHeader->dwHashTablePos = (DWORD)TablePos; - nError = SaveMpqTable(ha, pHashTable, TablePos, (size_t)HashTableSize64, pHeader->MD5_HashTable, MPQ_KEY_HASH_TABLE, false); + dwErrCode = SaveMpqTable(ha, pHashTable, TablePos, (size_t)HashTableSize64, pHeader->MD5_HashTable, MPQ_KEY_HASH_TABLE, false); TablePos += HashTableSize64; } // Write the block table, if we have any - if(nError == ERROR_SUCCESS && pBlockTable != NULL) + if(dwErrCode == ERROR_SUCCESS && pBlockTable != NULL) { pHeader->BlockTableSize64 = BlockTableSize64; pHeader->wBlockTablePosHi = (USHORT)(TablePos >> 32); pHeader->dwBlockTableSize = (DWORD)(BlockTableSize64 / sizeof(TMPQBlock)); pHeader->dwBlockTablePos = (DWORD)TablePos; - nError = SaveMpqTable(ha, pBlockTable, TablePos, (size_t)BlockTableSize64, pHeader->MD5_BlockTable, MPQ_KEY_BLOCK_TABLE, false); + dwErrCode = SaveMpqTable(ha, pBlockTable, TablePos, (size_t)BlockTableSize64, pHeader->MD5_BlockTable, MPQ_KEY_BLOCK_TABLE, false); TablePos += BlockTableSize64; } // Write the hi-block table, if we have any - if(nError == ERROR_SUCCESS && pHiBlockTable != NULL) + if(dwErrCode == ERROR_SUCCESS && pHiBlockTable != NULL) { ULONGLONG ByteOffset = ha->MpqPos + TablePos; pHeader->HiBlockTableSize64 = HiBlockTableSize64; pHeader->HiBlockTablePos64 = TablePos; BSWAP_ARRAY16_UNSIGNED(pHiBlockTable, HiBlockTableSize64); - + if(!FileStream_Write(ha->pStream, &ByteOffset, pHiBlockTable, (DWORD)HiBlockTableSize64)) - nError = GetLastError(); + dwErrCode = SErrGetLastError(); TablePos += HiBlockTableSize64; } // Cut the MPQ - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { ULONGLONG FileSize = ha->MpqPos + TablePos; if(!FileStream_SetSize(ha->pStream, FileSize)) - nError = GetLastError(); + dwErrCode = SErrGetLastError(); } // Write the MPQ header - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { + TMPQHeader SaveMpqHeader; + // Update the size of the archive pHeader->ArchiveSize64 = TablePos; pHeader->dwArchiveSize = (DWORD)TablePos; - + // Update the MD5 of the archive header CalculateDataBlockHash(pHeader, MPQ_HEADER_SIZE_V4 - MD5_DIGEST_SIZE, pHeader->MD5_MpqHeader); // Write the MPQ header to the file - BSWAP_TMPQHEADER(pHeader); - if(!FileStream_Write(ha->pStream, &ha->MpqPos, pHeader, pHeader->dwHeaderSize)) - nError = GetLastError(); - BSWAP_TMPQHEADER(pHeader); + assert(pHeader->dwHeaderSize <= sizeof(SaveMpqHeader)); + memcpy(&SaveMpqHeader, pHeader, pHeader->dwHeaderSize); + BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_1); + BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_2); + BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_3); + BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_4); + if(!FileStream_Write(ha->pStream, &ha->MpqPos, &SaveMpqHeader, pHeader->dwHeaderSize)) + dwErrCode = SErrGetLastError(); } // Clear the changed flag - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) ha->dwFlags &= ~MPQ_FLAG_CHANGED; // Cleanup and exit @@ -2546,5 +3202,5 @@ int SaveMPQTables(TMPQArchive * ha) STORM_FREE(pBlockTable); if(pHiBlockTable != NULL) STORM_FREE(pHiBlockTable); - return nError; + return dwErrCode; } diff --git a/dep/StormLib/src/SBaseSubTypes.cpp b/dep/StormLib/src/SBaseSubTypes.cpp new file mode 100644 index 0000000000..dd08b6feda --- /dev/null +++ b/dep/StormLib/src/SBaseSubTypes.cpp @@ -0,0 +1,688 @@ +/*****************************************************************************/ +/* SBaseSubTypes.cpp Copyright (c) Ladislav Zezula 2013 */ +/*---------------------------------------------------------------------------*/ +/* Conversion routines for archive formats that are similar to MPQ format */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 02.11.11 1.00 Lad The first version of SBaseSubTypes.cpp */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "StormCommon.h" + +/*****************************************************************************/ +/* */ +/* Support for SQP file format (War of the Immortals) */ +/* */ +/*****************************************************************************/ + +typedef struct _TSQPHeader +{ + // The ID_MPQ ('MPQ\x1A') signature + DWORD dwID; + + // Size of the archive header + DWORD dwHeaderSize; + + // 32-bit size of MPQ archive + DWORD dwArchiveSize; + + // 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; + + // Must be zero for SQP files + USHORT wFormatVersion; + + // Power of two exponent specifying the number of 512-byte disk sectors in each file sector + // in the archive. The size of each file sector in the archive is 512 * 2 ^ wSectorSize. + USHORT wSectorSize; + +} TSQPHeader; + +typedef struct _TSQPHash +{ + // Most likely the Locale + Platform + DWORD dwAlwaysZero; + + // If the hash table entry is valid, this is the index into the block table of the file. + // Otherwise, one of the following two values: + // - FFFFFFFFh: Hash table entry is empty, and has always been empty. + // Terminates searches for a given file. + // - 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; + + // The hash of the file path, using method A. + DWORD dwHashCheck1; + + // The hash of the file path, using method B. + DWORD dwHashCheck2; + +} TSQPHash; + +typedef struct _TSQPBlock +{ + // Offset of the beginning of the file, relative to the beginning of the archive. + DWORD dwFilePos; + + // Flags for the file. See MPQ_FILE_XXXX constants + DWORD dwFlags; + + // Compressed file size + DWORD dwCSize; + + // Uncompressed file size + DWORD dwFSize; + +} TSQPBlock; + +//----------------------------------------------------------------------------- +// Functions - SQP file format + +// This function converts SQP file header into MPQ file header +DWORD ConvertSqpHeaderToFormat4( + TMPQArchive * ha, + ULONGLONG FileSize, + DWORD dwFlags) +{ + TSQPHeader * pSqpHeader = (TSQPHeader *)ha->HeaderData; + TMPQHeader Header; + + // SQP files from War of the Immortal use MPQ file format with slightly + // modified structure. These fields have different position: + // + // Offset TMPQHeader TSQPHeader + // ------ ---------- ----------- + // 000C wFormatVersion dwHashTablePos (lo) + // 000E wSectorSize dwHashTablePos (hi) + // 001C dwBlockTableSize (lo) wBlockSize + // 001E dwHashTableSize (hi) wFormatVersion + + // Can't open the archive with certain flags + if(dwFlags & MPQ_OPEN_FORCE_MPQ_V1) + return ERROR_FILE_CORRUPT; + + // The file must not be greater than 4 GB + if((FileSize >> 0x20) != 0) + return ERROR_FILE_CORRUPT; + + // Translate the SQP header into a MPQ header + memset(&Header, 0, sizeof(TMPQHeader)); + Header.dwID = BSWAP_INT32_UNSIGNED(pSqpHeader->dwID); + Header.dwHeaderSize = BSWAP_INT32_UNSIGNED(pSqpHeader->dwHeaderSize); + Header.dwArchiveSize = BSWAP_INT32_UNSIGNED(pSqpHeader->dwArchiveSize); + Header.dwHashTablePos = BSWAP_INT32_UNSIGNED(pSqpHeader->dwHashTablePos); + Header.dwBlockTablePos = BSWAP_INT32_UNSIGNED(pSqpHeader->dwBlockTablePos); + Header.dwHashTableSize = BSWAP_INT32_UNSIGNED(pSqpHeader->dwHashTableSize); + Header.dwBlockTableSize = BSWAP_INT32_UNSIGNED(pSqpHeader->dwBlockTableSize); + Header.wFormatVersion = BSWAP_INT16_UNSIGNED(pSqpHeader->wFormatVersion); + Header.wSectorSize = BSWAP_INT16_UNSIGNED(pSqpHeader->wSectorSize); + + // Verify the SQP header + if(Header.dwID == g_dwMpqSignature && Header.dwHeaderSize == sizeof(TSQPHeader) && Header.dwArchiveSize == FileSize) + { + // Check for fixed values of version and sector size + if(Header.wFormatVersion == MPQ_FORMAT_VERSION_1 && Header.wSectorSize == 3) + { + // Initialize the fields of 3.0 header + Header.ArchiveSize64 = Header.dwArchiveSize; + Header.HashTableSize64 = Header.dwHashTableSize * sizeof(TMPQHash); + Header.BlockTableSize64 = Header.dwBlockTableSize * sizeof(TMPQBlock); + + // Copy the converted MPQ header back + memcpy(ha->HeaderData, &Header, sizeof(TMPQHeader)); + + // Mark this file as SQP file + ha->pfnHashString = HashStringSlash; + ha->dwFlags |= MPQ_FLAG_READ_ONLY; + ha->dwSubType = MPQ_SUBTYPE_SQP; + return ERROR_SUCCESS; + } + } + + return ERROR_FILE_CORRUPT; +} + +void * LoadSqpTable(TMPQArchive * ha, DWORD dwByteOffset, DWORD cbTableSize, DWORD dwKey) +{ + ULONGLONG ByteOffset; + LPBYTE pbSqpTable; + + // Allocate buffer for the table + pbSqpTable = STORM_ALLOC(BYTE, cbTableSize); + if(pbSqpTable != NULL) + { + // Load the table + ByteOffset = ha->MpqPos + dwByteOffset; + if(FileStream_Read(ha->pStream, &ByteOffset, pbSqpTable, cbTableSize)) + { + // Decrypt the SQP table + DecryptMpqBlock(pbSqpTable, cbTableSize, dwKey); + return pbSqpTable; + } + + // Free the table + STORM_FREE(pbSqpTable); + } + + return NULL; +} + +TMPQHash * LoadSqpHashTable(TMPQArchive * ha) +{ + TMPQHeader * pHeader = ha->pHeader; + TSQPHash * pSqpHashTable; + TSQPHash * pSqpHashEnd; + TSQPHash * pSqpHash; + TMPQHash * pMpqHash; + DWORD dwErrCode = ERROR_SUCCESS; + + // Load the hash table + pSqpHashTable = (TSQPHash *)LoadSqpTable(ha, pHeader->dwHashTablePos, pHeader->dwHashTableSize * sizeof(TSQPHash), MPQ_KEY_HASH_TABLE); + if(pSqpHashTable != NULL) + { + // Parse the entire hash table and convert it to MPQ hash table + pSqpHashEnd = pSqpHashTable + pHeader->dwHashTableSize; + pMpqHash = (TMPQHash *)pSqpHashTable; + for(pSqpHash = pSqpHashTable; pSqpHash < pSqpHashEnd; pSqpHash++, pMpqHash++) + { + // Ignore free entries + if(pSqpHash->dwBlockIndex != HASH_ENTRY_FREE) + { + // Store the hash entry to a temporary variable + TSQPHash TempEntry = *pSqpHash; + + // Check block index against the size of the block table + if(pHeader->dwBlockTableSize <= MPQ_BLOCK_INDEX(pSqpHash) && pSqpHash->dwBlockIndex < HASH_ENTRY_DELETED) + dwErrCode = ERROR_FILE_CORRUPT; + + // We do not support nonzero locale and platform ID + if(pSqpHash->dwAlwaysZero != 0 && pSqpHash->dwAlwaysZero != HASH_ENTRY_FREE) + dwErrCode = ERROR_FILE_CORRUPT; + + // Copy the entry to the MPQ hash entry + pMpqHash->dwHashCheck1 = TempEntry.dwHashCheck1; + pMpqHash->dwHashCheck2 = TempEntry.dwHashCheck2; + pMpqHash->dwBlockIndex = MPQ_BLOCK_INDEX(&TempEntry); + pMpqHash->Locale = 0; + pMpqHash->Platform = 0; + pMpqHash->Flags = 0; + } + } + + // If an error occurred, we need to free the hash table + if(dwErrCode != ERROR_SUCCESS) + { + STORM_FREE(pSqpHashTable); + pSqpHashTable = NULL; + } + } + + // Return the converted hash table (or NULL on failure) + return (TMPQHash *)pSqpHashTable; +} + +// Loads the SQP Block table and converts it to a MPQ block table +TMPQBlock * LoadSqpBlockTable(TMPQArchive * ha) +{ + TMPQHeader * pHeader = ha->pHeader; + TSQPBlock * pSqpBlockTable; + TSQPBlock * pSqpBlockEnd; + TSQPBlock * pSqpBlock; + TMPQBlock * pMpqBlock; + DWORD dwErrCode = ERROR_SUCCESS; + + // Load the hash table + pSqpBlockTable = (TSQPBlock *)LoadSqpTable(ha, pHeader->dwBlockTablePos, pHeader->dwBlockTableSize * sizeof(TSQPBlock), MPQ_KEY_BLOCK_TABLE); + if(pSqpBlockTable != NULL) + { + // Parse the entire hash table and convert it to MPQ hash table + pSqpBlockEnd = pSqpBlockTable + pHeader->dwBlockTableSize; + pMpqBlock = (TMPQBlock *)pSqpBlockTable; + for(pSqpBlock = pSqpBlockTable; pSqpBlock < pSqpBlockEnd; pSqpBlock++, pMpqBlock++) + { + // Store the block entry to a temporary variable + TSQPBlock TempEntry = *pSqpBlock; + + // Check for valid flags + if(pSqpBlock->dwFlags & ~MPQ_FILE_VALID_FLAGS) + dwErrCode = ERROR_FILE_CORRUPT; + + // Convert SQP block table entry to MPQ block table entry + pMpqBlock->dwFilePos = TempEntry.dwFilePos; + pMpqBlock->dwCSize = TempEntry.dwCSize; + pMpqBlock->dwFSize = TempEntry.dwFSize; + pMpqBlock->dwFlags = TempEntry.dwFlags; + } + + // If an error occurred, we need to free the hash table + if(dwErrCode != ERROR_SUCCESS) + { + STORM_FREE(pSqpBlockTable); + pSqpBlockTable = NULL; + } + } + + // Return the converted hash table (or NULL on failure) + return (TMPQBlock *)pSqpBlockTable; +} + +/*****************************************************************************/ +/* */ +/* Support for MPK file format (Longwu Online) */ +/* */ +/*****************************************************************************/ + +#define MPK_BLOCK_TABLE_LONGWU 0x86364E6D // MPK table ID for Longwu Online +#define MPK_BLOCK_TABLE_WOTGV 0x6d4e3686 // MPK table ID for Warriors of the Ghost Valley + +#define MPK_FILE_UNKNOWN_0001 0x00000001 // Seems to be always present +#define MPK_FILE_UNKNOWN_0010 0x00000010 // Seems to be always present +#define MPK_FILE_COMPRESSED 0x00000100 // Indicates a compressed file +#define MPK_FILE_ENCRYPTED 0x00002000 // Indicates an encrypted file +#define MPK_FILE_EXISTS 0x01000000 // Seems to be always present + +typedef struct _TMPKHeader +{ + // The ID_MPK ('MPK\x1A') signature + DWORD dwID; + + // Contains '2000' + DWORD dwVersion; + + // 32-bit size of the archive + DWORD dwArchiveSize; + + // Size of the archive header + DWORD dwHeaderSize; + + DWORD dwHashTablePos; + DWORD dwHashTableSize; + DWORD dwBlockTablePos; + DWORD dwBlockTableSize; + DWORD dwUnknownPos; + DWORD dwUnknownSize; +} TMPKHeader; + + +typedef struct _TMPKHash +{ + // The hash of the file path, using method A. + DWORD dwHashCheck1; + + // The hash of the file path, using method B. + DWORD dwHashCheck2; + + // The hash of the file path, using method C. + DWORD dwHashCheck3; + + // If the hash table entry is valid, this is the index into the block table of the file. + // Otherwise, one of the following two values: + // - FFFFFFFFh: Hash table entry is empty, and has always been empty. + // Terminates searches for a given file. + // - 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; + +} TMPKHash; + +// MPK block for Longwu Online +struct TMPKBlock1 +{ + DWORD dwFlags; // 0x1121 - Compressed , 0x1120 - Not compressed + DWORD dwFilePos; // Offset of the beginning of the file, relative to the beginning of the archive. + DWORD dwFSize; // Uncompressed file size + DWORD dwCSize; // Compressed file size + DWORD dwMagic; // 0x86364E6D for Longwu Online +}; + +// MPK block for Warriors of the Ghost Valley +struct TMPKBlock2 +{ + DWORD dwFlags; // 0x1121 - Compressed , 0x1120 - Not compressed + DWORD dwFilePos; // Offset of the beginning of the file, relative to the beginning of the archive. + DWORD dwFSize; // Uncompressed file size + DWORD dwCSize; // Compressed file size + DWORD dwMagic; // 0x6d4e3686 + DWORD dwFlags1; // Unknown +}; + +//----------------------------------------------------------------------------- +// Local variables - MPK file format + +static const unsigned char MpkDecryptionKey[512] = +{ + 0x60, 0x20, 0x29, 0xE1, 0x01, 0xCE, 0xAA, 0xFE, 0xA3, 0xAB, 0x8E, 0x30, 0xAF, 0x02, 0xD1, 0x7D, + 0x41, 0x24, 0x06, 0xBD, 0xAE, 0xBE, 0x43, 0xC3, 0xBA, 0xB7, 0x08, 0x13, 0x51, 0xCF, 0xF8, 0xF7, + 0x25, 0x42, 0xA5, 0x4A, 0xDA, 0x0F, 0x52, 0x1C, 0x90, 0x3B, 0x63, 0x49, 0x36, 0xF6, 0xDD, 0x1B, + 0xEA, 0x58, 0xD4, 0x40, 0x70, 0x61, 0x55, 0x09, 0xCD, 0x0B, 0xA2, 0x4B, 0x68, 0x2C, 0x8A, 0xF1, + 0x3C, 0x3A, 0x65, 0xBB, 0xA1, 0xA8, 0x23, 0x97, 0xFD, 0x15, 0x00, 0x94, 0x88, 0x33, 0x59, 0xE9, + 0xFB, 0x69, 0x21, 0xEF, 0x85, 0x5B, 0x57, 0x6C, 0xFA, 0xB5, 0xEE, 0xB8, 0x71, 0xDC, 0xB1, 0x38, + 0x0C, 0x0A, 0x5C, 0x56, 0xC9, 0xB4, 0x84, 0x17, 0x1E, 0xE5, 0xD3, 0x5A, 0xCC, 0xFC, 0x11, 0x86, + 0x7F, 0x45, 0x4F, 0x54, 0xC8, 0x8D, 0x73, 0x89, 0x79, 0x5D, 0xB3, 0xBF, 0xB9, 0xE3, 0x93, 0xE4, + 0x6F, 0x35, 0x2D, 0x46, 0xF2, 0x76, 0xC5, 0x7E, 0xE2, 0xA4, 0xE6, 0xD9, 0x6E, 0x48, 0x34, 0x2B, + 0xC6, 0x5F, 0xBC, 0xA0, 0x6D, 0x0D, 0x47, 0x6B, 0x95, 0x96, 0x92, 0x91, 0xB2, 0x27, 0xEB, 0x9E, + 0xEC, 0x8F, 0xDF, 0x9C, 0x74, 0x99, 0x64, 0xF5, 0xFF, 0x28, 0xB6, 0x37, 0xF3, 0x7C, 0x81, 0x03, + 0x44, 0x62, 0x1F, 0xDB, 0x04, 0x7B, 0xB0, 0x9B, 0x31, 0xA7, 0xDE, 0x78, 0x9F, 0xAD, 0x0E, 0x3F, + 0x3E, 0x4D, 0xC7, 0xD7, 0x39, 0x19, 0x5E, 0xC2, 0xD0, 0xAC, 0xE8, 0x1A, 0x87, 0x8B, 0x07, 0x05, + 0x22, 0xED, 0x72, 0x2E, 0x1D, 0xC1, 0xA9, 0xD6, 0xE0, 0x83, 0xD5, 0xD8, 0xCB, 0x80, 0xF0, 0x66, + 0x7A, 0x9D, 0x50, 0xF9, 0x10, 0x4E, 0x16, 0x14, 0x77, 0x75, 0x6A, 0x67, 0xD2, 0xC0, 0xA6, 0xC4, + 0x53, 0x8C, 0x32, 0xCA, 0x82, 0x2A, 0x18, 0x9A, 0xF4, 0x4C, 0x3D, 0x26, 0x12, 0xE7, 0x98, 0x2F, + 0x4A, 0x04, 0x0D, 0xAF, 0xB4, 0xCF, 0x12, 0xCE, 0x1A, 0x37, 0x61, 0x39, 0x60, 0x95, 0xBE, 0x25, + 0xE4, 0x6E, 0xFC, 0x1B, 0xE7, 0x49, 0xE6, 0x67, 0xF6, 0xC5, 0xCB, 0x2F, 0x27, 0xD4, 0x68, 0xB2, + 0x01, 0x52, 0xD0, 0x46, 0x11, 0x20, 0xFB, 0x9D, 0xA9, 0x02, 0xF5, 0x8F, 0x3D, 0x82, 0xD3, 0xFF, + 0x0B, 0xB8, 0xF2, 0x4D, 0x8E, 0x81, 0x2C, 0xAB, 0x5F, 0xC4, 0x41, 0x29, 0x40, 0xFA, 0xC0, 0xBF, + 0x33, 0x10, 0x21, 0x16, 0xB0, 0x71, 0x83, 0x96, 0x8D, 0x2B, 0x23, 0x3B, 0xF9, 0xC1, 0xE5, 0x72, + 0xE2, 0x1C, 0x26, 0xF0, 0x73, 0x36, 0x63, 0x56, 0x31, 0x4E, 0x6B, 0x55, 0x62, 0x79, 0xC6, 0x91, + 0x00, 0x35, 0xB1, 0x2A, 0xA6, 0x42, 0xDF, 0xEB, 0x3C, 0x51, 0xEA, 0x97, 0x57, 0x94, 0x8C, 0x80, + 0x34, 0x5C, 0xD2, 0x76, 0xA4, 0xE9, 0x85, 0xE8, 0xBB, 0x78, 0xE0, 0xB5, 0xAD, 0x0F, 0x87, 0x70, + 0xDD, 0xAE, 0xF4, 0xD9, 0x66, 0x54, 0x6F, 0xCC, 0x4C, 0x77, 0x3E, 0xCD, 0xF1, 0x75, 0x0A, 0xA1, + 0x28, 0x9B, 0x9A, 0x7E, 0x4B, 0x98, 0x99, 0x47, 0xFE, 0xA5, 0xF7, 0xB7, 0xA3, 0xE1, 0x9F, 0xBC, + 0x93, 0x44, 0x3A, 0x08, 0x89, 0x22, 0xEE, 0xB9, 0x45, 0xD6, 0x06, 0x09, 0xC9, 0xBD, 0x14, 0x0C, + 0xB6, 0x5E, 0x9C, 0x7A, 0x65, 0x59, 0xAA, 0x19, 0x5B, 0x7C, 0x18, 0x43, 0x92, 0x13, 0x15, 0x7B, + 0xED, 0xD5, 0xC7, 0x17, 0xEF, 0x86, 0x90, 0xC2, 0x74, 0x64, 0xF3, 0xDC, 0x6C, 0x38, 0x05, 0x1D, + 0xC8, 0x0E, 0xEC, 0x6A, 0x32, 0xDA, 0xD7, 0xC3, 0xDB, 0x8B, 0x24, 0xB3, 0x5D, 0x2E, 0xBA, 0xA2, + 0xD8, 0x03, 0x88, 0x7D, 0x7F, 0x69, 0x8A, 0xFD, 0xCA, 0x4F, 0x30, 0x9E, 0xA0, 0xD1, 0x5A, 0x53, + 0xDE, 0x3F, 0x84, 0xAC, 0xF8, 0xA7, 0x2D, 0x1F, 0x1E, 0xE3, 0x58, 0x50, 0x6D, 0x48, 0x07, 0xA8 +}; + +//----------------------------------------------------------------------------- +// Functions - MPK file format + +// This function converts MPK file header into MPQ file header +DWORD ConvertMpkHeaderToFormat4( + TMPQArchive * ha, + ULONGLONG FileSize, + DWORD dwFlags) +{ + TMPKHeader * pMpkHeader = (TMPKHeader *)ha->HeaderData; + TMPQHeader Header; + + // Can't open the archive with certain flags + if(dwFlags & MPQ_OPEN_FORCE_MPQ_V1) + return ERROR_FILE_CORRUPT; + if(pMpkHeader->dwVersion != ID_MPK_VERSION_2000) + return ERROR_FILE_CORRUPT; + + // Translate the MPK header into a MPQ header + // Note: Hash table size and block table size are in bytes, not in entries + memset(&Header, 0, sizeof(TMPQHeader)); + Header.dwID = BSWAP_INT32_UNSIGNED(pMpkHeader->dwID); + Header.dwArchiveSize = BSWAP_INT32_UNSIGNED(pMpkHeader->dwArchiveSize); + Header.dwHeaderSize = BSWAP_INT32_UNSIGNED(pMpkHeader->dwHeaderSize); + Header.dwHashTablePos = BSWAP_INT32_UNSIGNED(pMpkHeader->dwHashTablePos); + Header.dwHashTableSize = BSWAP_INT32_UNSIGNED(pMpkHeader->dwHashTableSize) / sizeof(TMPKHash); + Header.dwBlockTablePos = BSWAP_INT32_UNSIGNED(pMpkHeader->dwBlockTablePos); + Header.dwBlockTableSize = BSWAP_INT32_UNSIGNED(pMpkHeader->dwBlockTableSize); +// Header.dwUnknownPos = BSWAP_INT32_UNSIGNED(pMpkHeader->dwUnknownPos); +// Header.dwUnknownSize = BSWAP_INT32_UNSIGNED(pMpkHeader->dwUnknownSize); + assert(Header.dwHeaderSize == sizeof(TMPKHeader)); + + // Verify the MPK header + if(Header.dwID == ID_MPK && Header.dwHeaderSize == sizeof(TMPKHeader) && Header.dwArchiveSize == (DWORD)FileSize) + { + // The header ID must be ID_MPQ + Header.dwID = g_dwMpqSignature; + Header.wFormatVersion = MPQ_FORMAT_VERSION_1; + Header.wSectorSize = 3; + + // Initialize the fields of 3.0 header + Header.ArchiveSize64 = Header.dwArchiveSize; + Header.HashTableSize64 = Header.dwHashTableSize * sizeof(TMPQHash); + Header.BlockTableSize64 = Header.dwBlockTableSize * sizeof(TMPQBlock); + + // Copy the converted MPQ header back + memcpy(ha->HeaderData, &Header, sizeof(TMPQHeader)); + + // Mark this file as MPK file + ha->pfnHashString = HashStringLower; + ha->dwFlags |= MPQ_FLAG_READ_ONLY; + ha->dwSubType = MPQ_SUBTYPE_MPK; + return ERROR_SUCCESS; + } + return ERROR_FILE_CORRUPT; +} + +// Attempts to search a free hash entry in the hash table being converted. +// The created hash table must always be of nonzero size, +// should have no duplicated items and no deleted entries +TMPQHash * FindFreeHashEntry(TMPQHash * pHashTable, DWORD dwHashTableSize, DWORD dwStartIndex) +{ + TMPQHash * pHash; + DWORD dwIndex; + + // Set the initial index + dwStartIndex = dwIndex = (dwStartIndex & (dwHashTableSize - 1)); + assert(dwHashTableSize != 0); + + // Search the hash table and return the found entries in the following priority: + for(;;) + { + // We are not expecting to find matching entry in the hash table being built + // We are not expecting to find deleted entry either + pHash = pHashTable + dwIndex; + + // If we found a free entry, we need to stop searching + if(pHash->dwBlockIndex == HASH_ENTRY_FREE) + return pHash; + + // Move to the next hash entry. + // If we reached the starting entry, it's failure. + dwIndex = (dwIndex + 1) & (dwHashTableSize - 1); + if(dwIndex == dwStartIndex) + break; + } + + // We haven't found anything + assert(false); + return NULL; +} + +DWORD GetMpkBlockTableItemLength(void * pvMpkBlockTable, size_t cbMpkBlockTable) +{ + TMPKBlock1 * pBlockItem1 = (TMPKBlock1 *)(pvMpkBlockTable); + TMPKBlock2 * pBlockItem2 = (TMPKBlock2 *)(pvMpkBlockTable); + + // + // We have no information as to what's the type of the table item + // So we just compare the magic numbers of all supported item sizes + // + + if(cbMpkBlockTable >= sizeof(TMPKBlock1) * 2) + { + if(pBlockItem1[0].dwMagic == pBlockItem1[1].dwMagic) + { + return sizeof(TMPKBlock1); + } + } + + if(cbMpkBlockTable >= sizeof(TMPKBlock2) * 2) + { + if(pBlockItem2[0].dwMagic == pBlockItem2[1].dwMagic) + { + return sizeof(TMPKBlock2); + } + } + + // Unknown item size + assert(false); + return 0; +} + +void DecryptMpkTable(void * pvMpkTable, size_t cbSize) +{ + LPBYTE pbMpkTable = (LPBYTE)pvMpkTable; + + for(size_t i = 0; i < cbSize; i++) + pbMpkTable[i] = MpkDecryptionKey[pbMpkTable[i]]; +} + +void * LoadMpkTable(TMPQArchive * ha, DWORD dwByteOffset, DWORD cbTableSize) +{ + ULONGLONG ByteOffset; + LPBYTE pbMpkTable = NULL; + + // Allocate space for the table + pbMpkTable = STORM_ALLOC(BYTE, cbTableSize); + if(pbMpkTable != NULL) + { + // Load and the MPK hash table + ByteOffset = ha->MpqPos + dwByteOffset; + if(FileStream_Read(ha->pStream, &ByteOffset, pbMpkTable, cbTableSize)) + { + // Decrypt the table + DecryptMpkTable(pbMpkTable, cbTableSize); + return pbMpkTable; + } + + // Free the MPK table + STORM_FREE(pbMpkTable); + pbMpkTable = NULL; + } + + // Return the table + return pbMpkTable; +} + +TMPQHash * LoadMpkHashTable(TMPQArchive * ha) +{ + TMPQHeader * pHeader = ha->pHeader; + TMPQHash * pHashTable = NULL; + TMPKHash * pMpkHash; + TMPQHash * pHash = NULL; + DWORD dwHashTableSize = pHeader->dwHashTableSize; + + // MPKs use different hash table searching. + // Instead of using MPQ_HASH_TABLE_INDEX hash as index, + // they store the value directly in the hash table. + // Also for faster searching, the hash table is sorted ascending by the value + + // Load and decrypt the MPK hash table. + pMpkHash = (TMPKHash *)LoadMpkTable(ha, pHeader->dwHashTablePos, pHeader->dwHashTableSize * sizeof(TMPKHash)); + if(pMpkHash != NULL) + { + // Calculate the hash table size as if it was real MPQ hash table + pHeader->dwHashTableSize = GetNearestPowerOfTwo(pHeader->dwHashTableSize); + pHeader->HashTableSize64 = pHeader->dwHashTableSize * sizeof(TMPQHash); + + // Now allocate table that will serve like a true MPQ hash table, + // so we translate the MPK hash table to MPQ hash table + pHashTable = STORM_ALLOC(TMPQHash, pHeader->dwHashTableSize); + if(pHashTable != NULL) + { + // Set the entire hash table to free + memset(pHashTable, 0xFF, (size_t)pHeader->HashTableSize64); + + // Copy the MPK hash table into MPQ hash table + for(DWORD i = 0; i < dwHashTableSize; i++) + { + // Finds the free hash entry in the hash table + // We don't expect any errors here, because we are putting files to empty hash table + pHash = FindFreeHashEntry(pHashTable, pHeader->dwHashTableSize, pMpkHash[i].dwHashCheck1); + assert(pHash->dwBlockIndex == HASH_ENTRY_FREE); + + // Copy the MPK hash entry to the hash table + pHash->dwBlockIndex = pMpkHash[i].dwBlockIndex; + pHash->Locale = 0; + pHash->Platform = 0; + pHash->Flags = 0; + pHash->dwHashCheck1 = pMpkHash[i].dwHashCheck2; + pHash->dwHashCheck2 = pMpkHash[i].dwHashCheck3; + } + } + + // Free the temporary hash table + STORM_FREE(pMpkHash); + } + + return pHashTable; +} + +static DWORD ConvertMpkFlagsToMpqFlags(DWORD dwMpkFlags) +{ + DWORD dwMpqFlags = MPQ_FILE_EXISTS; + + // Check for flags that are always present + assert((dwMpkFlags & MPK_FILE_UNKNOWN_0001) != 0); + assert((dwMpkFlags & MPK_FILE_UNKNOWN_0010) != 0); + assert((dwMpkFlags & MPK_FILE_EXISTS) != 0); + + // Append the compressed flag + dwMpqFlags |= (dwMpkFlags & MPK_FILE_COMPRESSED) ? MPQ_FILE_COMPRESS : 0; + dwMpqFlags |= (dwMpkFlags & MPK_FILE_ENCRYPTED) ? MPQ_FILE_ENCRYPTED : 0; + + // All files in the MPQ seem to be single unit files + dwMpqFlags |= MPQ_FILE_SINGLE_UNIT; + + return dwMpqFlags; +} + +static TMPQBlock * LoadMpkBlockTable(TMPQArchive * ha, void * pMpkBlockTable, DWORD nItemSize) +{ + TMPQHeader * pHeader = ha->pHeader; + TMPQBlock * pBlockTable; + TMPQBlock * pMpqBlock; + LPBYTE pbMpkBlockPtr = (LPBYTE)(pMpkBlockTable); + LPBYTE pbMpkBlockEnd = pbMpkBlockPtr + pHeader->dwBlockTableSize; + + // Fixup the number of items in the MPK block table + pHeader->dwBlockTableSize = pHeader->dwBlockTableSize / nItemSize; + + // Allocate buffer for MPQ-like block table + pBlockTable = pMpqBlock = STORM_ALLOC(TMPQBlock, pHeader->dwBlockTableSize); + if(pBlockTable != NULL) + { + while(pbMpkBlockPtr < pbMpkBlockEnd) + { + TMPKBlock1 * pMpkBlock = (TMPKBlock1 *)(pbMpkBlockPtr); + + // Translate the MPK block table entry to MPQ block table entry + pMpqBlock->dwFilePos = pMpkBlock->dwFilePos; + pMpqBlock->dwCSize = pMpkBlock->dwCSize; + pMpqBlock->dwFSize = pMpkBlock->dwFSize; + pMpqBlock->dwFlags = ConvertMpkFlagsToMpqFlags(pMpkBlock->dwFlags); + + // Move both + pbMpkBlockPtr += nItemSize; + pMpqBlock++; + } + } + + return pBlockTable; +} + +TMPQBlock * LoadMpkBlockTable(TMPQArchive * ha) +{ + TMPQHeader * pHeader = ha->pHeader; + TMPQBlock * pBlockTable = NULL; + void * pMpkBlockTable; + DWORD nItemLength; + + // Load the MPK block table. At this moment, we don't know the version of the blobk table + pMpkBlockTable = LoadMpkTable(ha, pHeader->dwBlockTablePos, pHeader->dwBlockTableSize); + if(pMpkBlockTable != NULL) + { + // Based on the item length, load the table and convert it to the MPQ table + if((nItemLength = GetMpkBlockTableItemLength(pMpkBlockTable, pHeader->dwBlockTableSize)) != 0) + { + pBlockTable = LoadMpkBlockTable(ha, pMpkBlockTable, nItemLength); + } + + // Free the MPK block table + STORM_FREE(pMpkBlockTable); + } + + return pBlockTable; +} diff --git a/dep/StormLib/src/SCompression.cpp b/dep/StormLib/src/SCompression.cpp index 5c7432248d..1768452a20 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. */ /*---------------------------------------------------------------------------*/ @@ -22,35 +22,34 @@ // Information about the input and output buffers for pklib typedef struct { - char * pbInBuff; // Pointer to input data buffer - char * pbInBuffEnd; // End of the input buffer - char * pbOutBuff; // Pointer to output data buffer - char * pbOutBuffEnd; // Pointer to output data buffer + unsigned char * pbInBuff; // Pointer to input data buffer + unsigned char * pbInBuffEnd; // End of the input buffer + unsigned char * pbOutBuff; // Pointer to output data buffer + unsigned char * pbOutBuffEnd; // Pointer to output data buffer } TDataInfo; // Prototype of the compression function // 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 + void * pvOutBuffer, // [out] Pointer to the buffer where the compressed data will be stored + int * pcbOutBuffer, // [in] Pointer to length of the buffer pointed by pvOutBuffer + void * pvInBuffer, // [in] Pointer to the buffer with data to compress + int cbInBuffer, // [in] Length of the buffer pointer by pvInBuffer 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 + void * pvOutBuffer, // [out] Pointer to the buffer where to store decompressed data + int * pcbOutBuffer, // [in] Pointer to total size of the buffer pointed by pvOutBuffer // [out] Contains length of the decompressed data - char * pbInBuffer, // [in] Pointer to data to be decompressed + void * pvInBuffer, // [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 @@ -70,59 +69,22 @@ typedef struct /* */ /*****************************************************************************/ -// 1500F4C0 -void Compress_huff( - char * pbOutBuffer, - int * pcbOutBuffer, - char * pbInBuffer, - int cbInBuffer, - int * pCmpType, - int /* nCmpLevel */) +void Compress_huff(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) { - THuffmannTree ht; // Huffmann tree for compression - TOutputStream os; // Output stream + THuffmannTree ht(true); + TOutputStream os(pvOutBuffer, *pcbOutBuffer); - // Initialize output stream - os.pbOutBuffer = (unsigned char *)pbOutBuffer; - os.cbOutSize = *pcbOutBuffer; - os.pbOutPos = (unsigned char *)pbOutBuffer; - os.dwBitBuff = 0; - os.nBits = 0; - - // Initialize the Huffmann tree for compression - ht.InitTree(true); - - *pcbOutBuffer = ht.DoCompression(&os, (unsigned char *)pbInBuffer, cbInBuffer, *pCmpType); - - // 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(); + STORMLIB_UNUSED(nCmpLevel); + *pcbOutBuffer = ht.Compress(&os, pvInBuffer, cbInBuffer, *pCmpType); } -// 1500F5F0 -int Decompress_huff(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer) +int Decompress_huff(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) { - THuffmannTree ht; - TInputStream is; + THuffmannTree ht(false); + TInputStream is(pvInBuffer, cbInBuffer); - // Initialize input stream - is.pbInBufferEnd = (unsigned char *)pbInBuffer + cbInBuffer; - is.pbInBuffer = (unsigned char *)pbInBuffer; - is.BitBuffer = 0; - is.BitCount = 0; - - // Initialize the Huffmann tree for compression - ht.InitTree(false); - *pcbOutBuffer = ht.DoDecompression((unsigned char *)pbOutBuffer, *pcbOutBuffer, &is); - if(*pcbOutBuffer == 0) - return 0; - - // 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(); - return 1; + *pcbOutBuffer = ht.Decompress(pvOutBuffer, *pcbOutBuffer, &is); + return (*pcbOutBuffer == 0) ? 0 : 1; } /******************************************************************************/ @@ -131,23 +93,21 @@ 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 */) +void Compress_ZLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) { z_stream z; // Stream information for zlib int windowBits; int nResult; + // Keep compilers happy + STORMLIB_UNUSED(pCmpType); + STORMLIB_UNUSED(nCmpLevel); + // Fill the stream structure for zlib - z.next_in = (Bytef *)pbInBuffer; + z.next_in = (Bytef *)pvInBuffer; z.avail_in = (uInt)cbInBuffer; z.total_in = cbInBuffer; - z.next_out = (Bytef *)pbOutBuffer; + z.next_out = (Bytef *)pvOutBuffer; z.avail_out = *pcbOutBuffer; z.total_out = 0; z.zalloc = NULL; @@ -184,7 +144,7 @@ void Compress_ZLIB( { // Call zlib to compress the data nResult = deflate(&z, Z_FINISH); - + if(nResult == Z_OK || nResult == Z_STREAM_END) *pcbOutBuffer = z.total_out; @@ -192,30 +152,31 @@ void Compress_ZLIB( } } -int Decompress_ZLIB(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer) +int Decompress_ZLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) { z_stream z; // Stream information for zlib int nResult; // Fill the stream structure for zlib - z.next_in = (Bytef *)pbInBuffer; + z.next_in = (Bytef *)pvInBuffer; z.avail_in = (uInt)cbInBuffer; z.total_in = cbInBuffer; - z.next_out = (Bytef *)pbOutBuffer; + z.next_out = (Bytef *)pvOutBuffer; z.avail_out = *pcbOutBuffer; z.total_out = 0; z.zalloc = NULL; z.zfree = NULL; // Initialize the decompression structure. Storm.dll uses zlib version 1.1.3 - if((nResult = inflateInit(&z)) == 0) + if((nResult = inflateInit(&z)) == Z_OK) { // Call zlib to decompress the data nResult = inflate(&z, Z_FINISH); *pcbOutBuffer = z.total_out; inflateEnd(&z); } - return nResult; + + return (nResult >= Z_OK); } /******************************************************************************/ @@ -227,7 +188,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 @@ -241,7 +202,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; @@ -251,7 +212,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 @@ -272,70 +233,74 @@ static void WriteOutputData(char * buf, unsigned int * size, void * param) assert(pInfo->pbOutBuff <= pInfo->pbOutBuffEnd); } -static void Compress_PKLIB( - char * pbOutBuffer, - int * pcbOutBuffer, - char * pbInBuffer, - int cbInBuffer, - int * /* pCmpType */, - int /* nCmpLevel */) +static void Compress_PKLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) { 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 + unsigned int ctype = (pCmpType && *pCmpType == DATA_TYPE_TEXT) ? CMP_ASCII : CMP_BINARY; // Compression type - // Fill data information structure - memset(work_buf, 0, CMP_BUFFER_SIZE); - Info.pbInBuff = pbInBuffer; - Info.pbInBuffEnd = pbInBuffer + cbInBuffer; - Info.pbOutBuff = pbOutBuffer; - Info.pbOutBuffEnd = pbOutBuffer + *pcbOutBuffer; + // Keep compilers happy + STORMLIB_UNUSED(nCmpLevel); - // - // 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 - // + // Handle no-memory condition + if(work_buf != NULL) + { + // Fill data information structure + memset(work_buf, 0, CMP_BUFFER_SIZE); + Info.pbInBuff = (unsigned char *)pvInBuffer; + Info.pbInBuffEnd = (unsigned char *)pvInBuffer + cbInBuffer; + Info.pbOutBuff = (unsigned char *)pvOutBuffer; + Info.pbOutBuffEnd = (unsigned char *)pvOutBuffer + *pcbOutBuffer; - if (cbInBuffer < 0x600) - dict_size = CMP_IMPLODE_DICT_SIZE1; - else if(0x600 <= cbInBuffer && cbInBuffer < 0xC00) - dict_size = CMP_IMPLODE_DICT_SIZE2; - else - dict_size = CMP_IMPLODE_DICT_SIZE3; + // + // Set the dictionary size + // + // Diablo I uses fixed dictionary size of CMP_IMPLODE_DICT_SIZE3 + // Starcraft I uses the variable dictionary size based on algorithm below + // - // Do the compression - if(implode(ReadInputData, WriteOutputData, work_buf, &Info, &ctype, &dict_size) == CMP_NO_ERROR) - *pcbOutBuffer = (int)(Info.pbOutBuff - pbOutBuffer); + if (cbInBuffer < 0x600) + dict_size = CMP_IMPLODE_DICT_SIZE1; + else if(0x600 <= cbInBuffer && cbInBuffer < 0xC00) + dict_size = CMP_IMPLODE_DICT_SIZE2; + else + dict_size = CMP_IMPLODE_DICT_SIZE3; - STORM_FREE(work_buf); + // Do the compression + if(implode(ReadInputData, WriteOutputData, work_buf, &Info, &ctype, &dict_size) == CMP_NO_ERROR) + *pcbOutBuffer = (int)(Info.pbOutBuff - (unsigned char *)pvOutBuffer); + + STORM_FREE(work_buf); + } } -static int Decompress_PKLIB(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer) +static int Decompress_PKLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) { TDataInfo Info; // Data information - char * work_buf = STORM_ALLOC(char, EXP_BUFFER_SIZE);// Pklib's work buffer + char * work_buf; + int nResult = 0; - // Fill data information structure - memset(work_buf, 0, EXP_BUFFER_SIZE); - Info.pbInBuff = pbInBuffer; - Info.pbInBuffEnd = pbInBuffer + cbInBuffer; - Info.pbOutBuff = pbOutBuffer; - Info.pbOutBuffEnd = pbOutBuffer + *pcbOutBuffer; + // Allocate Pklib's work buffer + if((work_buf = STORM_ALLOC(char, EXP_BUFFER_SIZE)) != NULL) + { + // Fill data information structure + memset(work_buf, 0, EXP_BUFFER_SIZE); + Info.pbInBuff = (unsigned char *)pvInBuffer; + Info.pbInBuffEnd = (unsigned char *)pvInBuffer + cbInBuffer; + Info.pbOutBuff = (unsigned char *)pvOutBuffer; + Info.pbOutBuffEnd = (unsigned char *)pvOutBuffer + *pcbOutBuffer; + + // Do the decompression + if(explode(ReadInputData, WriteOutputData, work_buf, &Info) == CMP_NO_ERROR) + nResult = 1; + + // Give away the number of decompressed bytes + *pcbOutBuffer = (int)(Info.pbOutBuff - (unsigned char *)pvOutBuffer); + STORM_FREE(work_buf); + } - // 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); - STORM_FREE(work_buf); - return 1; + return nResult; } /******************************************************************************/ @@ -344,30 +309,29 @@ 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 */) +static void Compress_BZIP2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) { bz_stream strm; int blockSize100k = 9; int workFactor = 30; int bzError; + // Keep compilers happy + STORMLIB_UNUSED(pCmpType); + STORMLIB_UNUSED(nCmpLevel); + // Initialize the BZIP2 compression strm.bzalloc = NULL; strm.bzfree = NULL; + strm.opaque = NULL; // Blizzard uses 9 as blockSize100k, (0x30 as workFactor) // Last checked on Starcraft II if(BZ2_bzCompressInit(&strm, blockSize100k, 0, workFactor) == BZ_OK) { - strm.next_in = pbInBuffer; + strm.next_in = (char *)pvInBuffer; strm.avail_in = cbInBuffer; - strm.next_out = pbOutBuffer; + strm.next_out = (char *)pvOutBuffer; strm.avail_out = *pcbOutBuffer; // Perform the compression @@ -386,45 +350,30 @@ static void Compress_BZIP2( } } -static int Decompress_BZIP2(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer) +static int Decompress_BZIP2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) { bz_stream strm; - int nResult = BZ_OK; + int nResult; // Initialize the BZIP2 decompression - strm.bzalloc = NULL; - strm.bzfree = NULL; - if(BZ2_bzDecompressInit(&strm, 0, 0) == BZ_OK) + strm.next_in = (char *)pvInBuffer; + strm.avail_in = cbInBuffer; + strm.next_out = (char *)pvOutBuffer; + strm.avail_out = *pcbOutBuffer; + strm.bzalloc = NULL; + strm.bzfree = NULL; + strm.opaque = NULL; + + // Initialize decompression + if((nResult = BZ2_bzDecompressInit(&strm, 0, 0)) == BZ_OK) { - strm.next_in = pbInBuffer; - strm.avail_in = cbInBuffer; - strm.next_out = pbOutBuffer; - strm.avail_out = *pcbOutBuffer; - // Perform the decompression - while(nResult != BZ_STREAM_END) - { - nResult = BZ2_bzDecompress(&strm); - - // If any error there, break the loop - if(nResult < BZ_OK) - break; - } - - // Put the stream into idle state + nResult = BZ2_bzDecompress(&strm); + *pcbOutBuffer = strm.total_out_lo32; BZ2_bzDecompressEnd(&strm); - - // If all succeeded, set the number of output bytes - if(nResult >= BZ_OK) - { - *pcbOutBuffer = strm.total_out_lo32; - return 1; - } } - // Something failed, so set number of output bytes to zero - *pcbOutBuffer = 0; - return 1; + return (nResult >= BZ_OK); } /******************************************************************************/ @@ -461,17 +410,12 @@ 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(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) { ICompressProgress Progress; CLzmaEncProps props; ISzAlloc SzAlloc; + Byte * pbOutBuffer = (Byte *)pvOutBuffer; Byte * destBuffer; SizeT destLen = *pcbOutBuffer; SizeT srcLen = cbInBuffer; @@ -479,6 +423,10 @@ static void LZMA_Callback_Free(void *p, void *address) size_t encodedPropsSize = LZMA_PROPS_SIZE; SRes nResult; + // Keep compilers happy + STORMLIB_UNUSED(pCmpType); + STORMLIB_UNUSED(nCmpLevel); + // Fill the callbacks in structures Progress.Progress = LZMA_Callback_Progress; SzAlloc.Alloc = LZMA_Callback_Alloc; @@ -488,11 +436,11 @@ static void LZMA_Callback_Free(void *p, void *address) LzmaEncProps_Init(&props); // Perform compression - destBuffer = (Byte *)pbOutBuffer + LZMA_HEADER_SIZE; + destBuffer = (Byte *)pvOutBuffer + LZMA_HEADER_SIZE; destLen = *pcbOutBuffer - LZMA_HEADER_SIZE; nResult = LzmaEncode(destBuffer, &destLen, - (Byte *)pbInBuffer, + (Byte *)pvInBuffer, srcLen, &props, encodedProps, @@ -529,18 +477,18 @@ static void LZMA_Callback_Free(void *p, void *address) *pcbOutBuffer = (unsigned int)(destLen + LZMA_HEADER_SIZE); } -static int Decompress_LZMA(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer) +static int Decompress_LZMA(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) { ELzmaStatus LzmaStatus; ISzAlloc SzAlloc; - Byte * destBuffer = (Byte *)pbOutBuffer; - Byte * srcBuffer = (Byte *)pbInBuffer; + Byte * destBuffer = (Byte *)pvOutBuffer; + Byte * srcBuffer = (Byte *)pvInBuffer; SizeT destLen = *pcbOutBuffer; SizeT srcLen = cbInBuffer; 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 @@ -557,7 +505,7 @@ static int Decompress_LZMA(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBu &destLen, srcBuffer + LZMA_HEADER_SIZE, &srcLen, - srcBuffer + 1, + srcBuffer + 1, LZMA_PROPS_SIZE, LZMA_FINISH_END, &LzmaStatus, @@ -569,26 +517,65 @@ static int Decompress_LZMA(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBu return 1; } +static int Decompress_LZMA_MPK(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ + ELzmaStatus LzmaStatus; + ISzAlloc SzAlloc; + Byte * destBuffer = (Byte *)pvOutBuffer; + Byte * srcBuffer = (Byte *)pvInBuffer; + SizeT destLen = *pcbOutBuffer; + SizeT srcLen = cbInBuffer; + SRes nResult; + BYTE LZMA_Props[] = {0x5D, 0x00, 0x00, 0x00, 0x01}; + + // There must be at least 0x0E bytes in the buffer + if(srcLen <= sizeof(LZMA_Props)) + return 0; + + // Verify the props header + if(memcmp(pvInBuffer, LZMA_Props, sizeof(LZMA_Props))) + return 0; + + // Fill the callbacks in structures + SzAlloc.Alloc = LZMA_Callback_Alloc; + SzAlloc.Free = LZMA_Callback_Free; + + // Perform compression + srcLen = cbInBuffer - sizeof(LZMA_Props); + nResult = LzmaDecode(destBuffer, + &destLen, + srcBuffer + sizeof(LZMA_Props), + &srcLen, + srcBuffer, + sizeof(LZMA_Props), + LZMA_FINISH_END, + &LzmaStatus, + &SzAlloc); + if(nResult != SZ_OK) + return 0; + + *pcbOutBuffer = (unsigned int)destLen; + return 1; +} + /******************************************************************************/ /* */ /* Support functions for SPARSE compression (0x20) */ /* */ /******************************************************************************/ -void Compress_SPARSE( - char * pbOutBuffer, - int * pcbOutBuffer, - char * pbInBuffer, - int cbInBuffer, - int * /* pCmpType */, - int /* nCmpLevel */) +void Compress_SPARSE(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) { - CompressSparse((unsigned char *)pbOutBuffer, pcbOutBuffer, (unsigned char *)pbInBuffer, cbInBuffer); + // Keep compilers happy + STORMLIB_UNUSED(pCmpType); + STORMLIB_UNUSED(nCmpLevel); + + CompressSparse(pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer); } -int Decompress_SPARSE(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer) +int Decompress_SPARSE(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) { - return DecompressSparse((unsigned char *)pbOutBuffer, pcbOutBuffer, (unsigned char *)pbInBuffer, cbInBuffer); + return DecompressSparse(pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer); } /******************************************************************************/ @@ -597,13 +584,7 @@ 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) +static void Compress_ADPCM_mono(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) { // Prepare the compression level for Huffmann compression, // which will be called as next step @@ -622,12 +603,12 @@ static void Compress_ADPCM_mono( nCmpLevel = 5; *pCmpType = 7; } - *pcbOutBuffer = CompressADPCM((unsigned char *)pbOutBuffer, *pcbOutBuffer, (short *)pbInBuffer, cbInBuffer, 1, nCmpLevel); + *pcbOutBuffer = CompressADPCM(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 1, nCmpLevel); } -static int Decompress_ADPCM_mono(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer) +static int Decompress_ADPCM_mono(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) { - *pcbOutBuffer = DecompressADPCM((unsigned char *)pbOutBuffer, *pcbOutBuffer, (unsigned char *)pbInBuffer, cbInBuffer, 1); + *pcbOutBuffer = DecompressADPCM(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 1); return 1; } @@ -637,13 +618,7 @@ 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) +static void Compress_ADPCM_stereo(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) { // Prepare the compression level for Huffmann compression, // which will be called as next step @@ -662,12 +637,30 @@ static void Compress_ADPCM_stereo( nCmpLevel = 5; *pCmpType = 7; } - *pcbOutBuffer = CompressADPCM((unsigned char *)pbOutBuffer, *pcbOutBuffer, (short *)pbInBuffer, cbInBuffer, 2, nCmpLevel); + *pcbOutBuffer = CompressADPCM(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 2, nCmpLevel); } -static int Decompress_ADPCM_stereo(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer) +static int Decompress_ADPCM_stereo(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) { - *pcbOutBuffer = DecompressADPCM((unsigned char *)pbOutBuffer, *pcbOutBuffer, (unsigned char *)pbInBuffer, cbInBuffer, 2); + *pcbOutBuffer = DecompressADPCM(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 2); + return 1; +} + +/******************************************************************************/ +/* */ +/* Support for ADPCM mono & stereo (Starcraft I BETA - like) */ +/* */ +/******************************************************************************/ + +static int Decompress_ADPCM1_sc1b(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ + *pcbOutBuffer = DecompressADPCM_SC1B(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 1); + return 1; +} + +static int Decompress_ADPCM2_sc1b(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ + *pcbOutBuffer = DecompressADPCM_SC1B(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 2); return 1; } @@ -677,24 +670,25 @@ static int Decompress_ADPCM_stereo(char * pbOutBuffer, int * pcbOutBuffer, char /* */ /*****************************************************************************/ -int WINAPI SCompImplode(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer) +int WINAPI SCompImplode(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) { - int cbOutBuffer = *pcbOutBuffer; + int cbOutBuffer; // Check for valid parameters - if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pbOutBuffer || !pbInBuffer) + if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pvOutBuffer || !pvInBuffer) { - SetLastError(ERROR_INVALID_PARAMETER); + SErrSetLastError(ERROR_INVALID_PARAMETER); return 0; } // Perform the compression - Compress_PKLIB(pbOutBuffer, &cbOutBuffer, pbInBuffer, cbInBuffer, NULL, 0); + cbOutBuffer = *pcbOutBuffer; + Compress_PKLIB(pvOutBuffer, &cbOutBuffer, pvInBuffer, cbInBuffer, NULL, 0); // If the compression was unsuccessful, copy the data as-is if(cbOutBuffer >= *pcbOutBuffer) { - memcpy(pbOutBuffer, pbInBuffer, cbInBuffer); + memcpy(pvOutBuffer, pvInBuffer, cbInBuffer); cbOutBuffer = *pcbOutBuffer; } @@ -708,33 +702,34 @@ int WINAPI SCompImplode(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffe /* */ /*****************************************************************************/ -int WINAPI SCompExplode(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer) +int WINAPI SCompExplode(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) { - int cbOutBuffer = *pcbOutBuffer; + int cbOutBuffer; // Check for valid parameters - if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pbOutBuffer || !pbInBuffer) + if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pvOutBuffer || !pvInBuffer) { - SetLastError(ERROR_INVALID_PARAMETER); + SErrSetLastError(ERROR_INVALID_PARAMETER); return 0; } // If the input length is the same as output length, do nothing. + cbOutBuffer = *pcbOutBuffer; if(cbInBuffer == cbOutBuffer) { // If the buffers are equal, don't copy anything. - if(pbInBuffer == pbOutBuffer) + if(pvInBuffer == pvOutBuffer) return 1; - memcpy(pbOutBuffer, pbInBuffer, cbInBuffer); + memcpy(pvOutBuffer, pvInBuffer, cbInBuffer); return 1; } - + // Perform decompression - if(!Decompress_PKLIB(pbOutBuffer, &cbOutBuffer, pbInBuffer, cbInBuffer)) + if(!Decompress_PKLIB(pvOutBuffer, &cbOutBuffer, pvInBuffer, cbInBuffer)) { - SetLastError(ERROR_FILE_CORRUPT); - return false; + SErrSetLastError(ERROR_FILE_CORRUPT); + return 0; } *pcbOutBuffer = cbOutBuffer; @@ -758,29 +753,23 @@ int WINAPI SCompExplode(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffe static TCompressTable cmp_table[] = { - {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 + {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) +int WINAPI SCompCompress(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, 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 - char * pbWorkBuffer = NULL; // Temporary storage for decompressed data - char * pbOutput = pbOutBuffer; // Current output buffer - char * pbInput = pbInBuffer; // Current input buffer + COMPRESS CompressFuncArray[0x10]; // Array of compression functions, applied sequentially + unsigned char CompressByte[0x10]; // CompressByte for each method in the CompressFuncArray array + unsigned char * pbWorkBuffer = NULL; // Temporary storage for decompressed data + unsigned char * pbOutBuffer = (unsigned char *)pvOutBuffer; + unsigned char * pbOutput = (unsigned char *)pvOutBuffer;// Current output buffer + unsigned char * pbInput = (unsigned char *)pvInBuffer; // Current input buffer int nCompressCount = 0; int nCompressIndex = 0; int nAtLeastOneCompressionDone = 0; @@ -789,9 +778,9 @@ int WINAPI SCompCompress( int nResult = 1; // Check for valid parameters - if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pbOutBuffer || !pbInBuffer) + if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pvOutBuffer || !pvInBuffer) { - SetLastError(ERROR_INVALID_PARAMETER); + SErrSetLastError(ERROR_INVALID_PARAMETER); return 0; } @@ -812,7 +801,7 @@ int WINAPI SCompCompress( else { // Fill the compressions array - for(size_t i = 0; i < (sizeof(cmp_table) / sizeof(TCompressTable)); i++) + for(size_t i = 0; i < _countof(cmp_table); i++) { // If the mask agrees, insert the compression function to the array if(uCompressionMask & cmp_table[i].uMask) @@ -827,7 +816,7 @@ int WINAPI SCompCompress( // If at least one of the compressions remaing unknown, return an error if(uCompressionMask != 0) { - SetLastError(ERROR_NOT_SUPPORTED); + SErrSetLastError(ERROR_NOT_SUPPORTED); return 0; } } @@ -838,10 +827,10 @@ int WINAPI SCompCompress( // If we need to do more than 1 compression, allocate intermediate buffer if(nCompressCount > 1) { - pbWorkBuffer = STORM_ALLOC(char, *pcbOutBuffer); + pbWorkBuffer = STORM_ALLOC(unsigned char, *pcbOutBuffer); if(pbWorkBuffer == NULL) { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); + SErrSetLastError(ERROR_NOT_ENOUGH_MEMORY); return 0; } } @@ -884,12 +873,12 @@ int WINAPI SCompCompress( // If at least one compression succeeded, put the compression // mask to the begin of the output buffer if(nAtLeastOneCompressionDone) - *pbOutBuffer = (char)uCompressionMask; + *pbOutBuffer = (unsigned char)uCompressionMask; *pcbOutBuffer = cbOutBuffer + nAtLeastOneCompressionDone; } else { - memcpy(pbOutBuffer, pbInBuffer, cbInBuffer); + memcpy(pvOutBuffer, pvInBuffer, cbInBuffer); *pcbOutBuffer = cbInBuffer; } @@ -905,6 +894,16 @@ int WINAPI SCompCompress( /* */ /*****************************************************************************/ +// Decompression table specific for Starcraft I BETA +// WAVE files are compressed by different ADPCM compression +static TDecompressTable dcmp_table_sc_beta[] = +{ + {MPQ_COMPRESSION_PKWARE, Decompress_PKLIB}, // Decompression with Pkware Data Compression Library + {MPQ_COMPRESSION_HUFFMANN, Decompress_huff}, // Huffmann decompression + {0x10, Decompress_ADPCM1_sc1b}, // IMA ADPCM mono decompression + {0x20, Decompress_ADPCM2_sc1b}, // IMA ADPCM stereo decompression +}; + // This table contains decompress functions which can be applied to // uncompressed data. The compression mask is stored in the first byte // of compressed data @@ -919,17 +918,22 @@ static TDecompressTable dcmp_table[] = {MPQ_COMPRESSION_SPARSE, Decompress_SPARSE} // Sparse decompression }; -int WINAPI SCompDecompress( - char * pbOutBuffer, +static int SCompDecompressInternal( + TDecompressTable * table, + size_t table_length, + void * pvOutBuffer, int * pcbOutBuffer, - char * pbInBuffer, - int cbInBuffer) + void * pvInBuffer, + int cbInBuffer, + unsigned uValidMask = 0xFF) { - 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 + unsigned char * pbWorkBuffer = NULL; + unsigned char * pbOutBuffer = (unsigned char *)pvOutBuffer; + unsigned char * pbInBuffer = (unsigned char *)pvInBuffer; + unsigned char * pbOutput = (unsigned char *)pvOutBuffer; + unsigned char * pbInput; + unsigned uCompressionMask1; // Decompressions applied to the data + unsigned uCompressionMask2; // 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 @@ -944,13 +948,14 @@ int WINAPI SCompDecompress( if(cbOutBuffer == cbInBuffer) { // If the buffers are equal, don't copy anything. - if(pbInBuffer != pbOutBuffer) - memcpy(pbOutBuffer, pbInBuffer, cbInBuffer); + if(pvInBuffer != pvOutBuffer) + memcpy(pvOutBuffer, pvInBuffer, cbInBuffer); return 1; } // Get applied compression types and decrement data length - uCompressionMask = uCompressionCopy = (unsigned char)*pbInBuffer++; + uCompressionMask1 = ((unsigned char)(*pbInBuffer++) & (uValidMask)); + uCompressionMask2 = uCompressionMask1; cbInBuffer--; // Get current compressed data and length of it @@ -958,33 +963,33 @@ int WINAPI SCompDecompress( cbInLength = cbInBuffer; // This compression function doesn't support LZMA - assert(uCompressionMask != MPQ_COMPRESSION_LZMA); + assert(uCompressionMask1 != MPQ_COMPRESSION_LZMA); // Parse the compression mask - for(size_t i = 0; i < (sizeof(dcmp_table) / sizeof(TDecompressTable)); i++) + for(size_t i = 0; i < table_length; i++) { // If the mask agrees, insert the compression function to the array - if(uCompressionMask & dcmp_table[i].uMask) + if(uCompressionMask1 & table[i].uMask) { - uCompressionCopy &= ~dcmp_table[i].uMask; + uCompressionMask2 &= ~table[i].uMask; nCompressCount++; } } // If at least one of the compressions remaing unknown, return an error - if(nCompressCount == 0 || uCompressionCopy != 0) + if(nCompressCount == 0 || uCompressionMask2 != 0) { - SetLastError(ERROR_NOT_SUPPORTED); + SErrSetLastError(ERROR_NOT_SUPPORTED); return 0; } // If there is more than one compression, we have to allocate extra buffer if(nCompressCount > 1) { - pbWorkBuffer = STORM_ALLOC(char, cbOutBuffer); + pbWorkBuffer = STORM_ALLOC(unsigned char, cbOutBuffer); if(pbWorkBuffer == NULL) { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); + SErrSetLastError(ERROR_NOT_ENOUGH_MEMORY); return 0; } } @@ -993,21 +998,21 @@ int WINAPI SCompDecompress( nCompressIndex = nCompressCount - 1; // Apply all decompressions - for(size_t i = 0; i < (sizeof(dcmp_table) / sizeof(TDecompressTable)); i++) + for(size_t i = 0; i < table_length; i++) { // Perform the (next) decompression - if(uCompressionMask & dcmp_table[i].uMask) + if(uCompressionMask1 & table[i].uMask) { // Get the correct output buffer pbOutput = (nCompressIndex & 1) ? pbWorkBuffer : pbOutBuffer; nCompressIndex--; - + // Perform the decompression cbOutBuffer = *pcbOutBuffer; - nResult = dcmp_table[i].Decompress(pbOutput, &cbOutBuffer, pbInput, cbInLength); + nResult = table[i].Decompress(pbOutput, &cbOutBuffer, pbInput, cbInLength); if(nResult == 0 || cbOutBuffer == 0) { - SetLastError(ERROR_FILE_CORRUPT); + SErrSetLastError(ERROR_FILE_CORRUPT); nResult = 0; break; } @@ -1027,28 +1032,33 @@ int WINAPI SCompDecompress( return nResult; } -int WINAPI SCompDecompress2( - char * pbOutBuffer, - int * pcbOutBuffer, - char * pbInBuffer, - int cbInBuffer) +int WINAPI SCompDecompress(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ + return SCompDecompressInternal(dcmp_table, _countof(dcmp_table), pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer); +} + +int WINAPI SCompDecompress2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) { DECOMPRESS pfnDecompress1 = NULL; DECOMPRESS pfnDecompress2 = NULL; - char * pbWorkBuffer = pbOutBuffer; + unsigned char * pbWorkBuffer = (unsigned char *)pvOutBuffer; + unsigned char * pbInBuffer = (unsigned char *)pvInBuffer; int cbWorkBuffer = *pcbOutBuffer; int nResult; char CompressionMethod; // Verify buffer sizes if(*pcbOutBuffer < cbInBuffer || cbInBuffer < 1) + { + SErrSetLastError(ERROR_INVALID_PARAMETER); 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); + if(pvOutBuffer != pvInBuffer) + memcpy(pvOutBuffer, pvInBuffer, cbInBuffer); return 1; } @@ -1095,18 +1105,28 @@ int WINAPI SCompDecompress2( // is not supported by newer MPQs. // + case (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_HUFFMANN): + pfnDecompress1 = Decompress_huff; + pfnDecompress2 = Decompress_ADPCM_mono; + break; + + case (MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN): + pfnDecompress1 = Decompress_huff; + pfnDecompress2 = Decompress_ADPCM_stereo; + break; + default: - SetLastError(ERROR_FILE_CORRUPT); + SErrSetLastError(ERROR_FILE_CORRUPT); return 0; } // If we have to use two decompressions, allocate temporary buffer if(pfnDecompress2 != NULL) { - pbWorkBuffer = STORM_ALLOC(char, *pcbOutBuffer); + pbWorkBuffer = STORM_ALLOC(unsigned char, *pcbOutBuffer); if(pbWorkBuffer == NULL) { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); + SErrSetLastError(ERROR_NOT_ENOUGH_MEMORY); return 0; } } @@ -1119,17 +1139,47 @@ int WINAPI SCompDecompress2( { cbInBuffer = cbWorkBuffer; cbWorkBuffer = *pcbOutBuffer; - nResult = pfnDecompress2(pbOutBuffer, &cbWorkBuffer, pbWorkBuffer, cbInBuffer); + nResult = pfnDecompress2(pvOutBuffer, &cbWorkBuffer, pbWorkBuffer, cbInBuffer); } // Supply the output buffer size *pcbOutBuffer = cbWorkBuffer; // Free temporary buffer - if(pbWorkBuffer != pbOutBuffer) + if(pbWorkBuffer != pvOutBuffer) STORM_FREE(pbWorkBuffer); if(nResult == 0) - SetLastError(ERROR_FILE_CORRUPT); + SErrSetLastError(ERROR_FILE_CORRUPT); return nResult; } + +int WINAPI SCompDecompressX(TMPQArchive * ha, void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ + // MPQs version 2 use their own fixed list of compression flags. + if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2) + { + return SCompDecompress2(pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer); + } + + // Starcraft BETA has specific decompression table. + if(ha->dwFlags & MPQ_FLAG_STARCRAFT_BETA) + { + return SCompDecompressInternal(dcmp_table_sc_beta, _countof(dcmp_table_sc_beta), pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer); + } + + // Default: Use the common MPQ v1 decompression routine + return SCompDecompressInternal(dcmp_table, _countof(dcmp_table), pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer); +} + +/*****************************************************************************/ +/* */ +/* File decompression for MPK archives */ +/* */ +/*****************************************************************************/ + +int SCompDecompressMpk(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ + return Decompress_LZMA_MPK(pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer); +} + diff --git a/dep/StormLib/src/SFileAddFile.cpp b/dep/StormLib/src/SFileAddFile.cpp index dda47370bd..55d300827f 100644 --- a/dep/StormLib/src/SFileAddFile.cpp +++ b/dep/StormLib/src/SFileAddFile.cpp @@ -6,6 +6,7 @@ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 27.03.10 1.00 Lad Splitted from SFileCreateArchiveEx.cpp */ +/* 21.04.13 1.01 Dea AddFile callback now part of TMPQArchive */ /*****************************************************************************/ #define __STORMLIB_SELF__ @@ -13,7 +14,17 @@ #include "StormCommon.h" //----------------------------------------------------------------------------- -// Local structures +// Local variables + +// Mask for lossy compressions +#define MPQ_LOSSY_COMPRESSION_MASK (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN) + +// Data compression for SFileAddFile +// Kept here for compatibility with code that was created with StormLib version < 6.50 +static DWORD DefaultDataCompression = MPQ_COMPRESSION_PKWARE; + +//----------------------------------------------------------------------------- +// WAVE verification #define FILE_SIGNATURE_RIFF 0x46464952 #define FILE_SIGNATURE_WAVE 0x45564157 @@ -25,8 +36,8 @@ 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 + + // Format sub-chunk DWORD dwSubChunk1Id; // 0x666d7420 ("fmt ") DWORD dwSubChunk1Size; // 0x16 for PCM USHORT wAudioFormat; // 1 = PCM. Other value means some sort of compression @@ -39,36 +50,29 @@ typedef struct _WAVE_FILE_HEADER // Followed by "data" sub-chunk (we don't care) } WAVE_FILE_HEADER, *PWAVE_FILE_HEADER; -//----------------------------------------------------------------------------- -// Local variables - -// Data compression for SFileAddFile -// Kept here for compatibility with code that was created with StormLib version < 6.50 -static DWORD DefaultDataCompression = MPQ_COMPRESSION_PKWARE; - -static SFILE_ADDFILE_CALLBACK AddFileCB = NULL; -static void * pvUserData = NULL; - -//----------------------------------------------------------------------------- -// MPQ write data functions - -#define LOSSY_COMPRESSION_MASK (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN) - -static int IsWaveFile( +static bool IsWaveFile_16BitsPerAdpcmSample( LPBYTE pbFileData, DWORD cbFileData, LPDWORD pdwChannels) { PWAVE_FILE_HEADER pWaveHdr = (PWAVE_FILE_HEADER)pbFileData; + // The amount of file data must be at least size of WAVE header if(cbFileData > sizeof(WAVE_FILE_HEADER)) { + // Check for the RIFF header if(pWaveHdr->dwChunkId == FILE_SIGNATURE_RIFF && pWaveHdr->dwFormat == FILE_SIGNATURE_WAVE) { + // Check for ADPCM format if(pWaveHdr->dwSubChunk1Id == FILE_SIGNATURE_FMT && pWaveHdr->wAudioFormat == AUDIO_FORMAT_PCM) { - *pdwChannels = pWaveHdr->wChannels; - return true; + // Now the number of bits per sample must be at least 16. + // If not, the WAVE file gets corrupted by the ADPCM compression + if(pWaveHdr->wBitsPerSample >= 0x10) + { + *pdwChannels = pWaveHdr->wChannels; + return true; + } } } } @@ -76,8 +80,48 @@ static int IsWaveFile( return false; } +static DWORD FillWritableHandle( + TMPQArchive * ha, + TMPQFile * hf, + ULONGLONG FileTime, + DWORD dwFileSize, + DWORD dwFlags) +{ + TFileEntry * pFileEntry = hf->pFileEntry; -static int WriteDataToMpqFile( + // Initialize the hash entry for the file + hf->RawFilePos = ha->MpqPos + hf->MpqFilePos; + hf->dwDataSize = dwFileSize; + + // Initialize the block table entry for the file + pFileEntry->ByteOffset = hf->MpqFilePos; + pFileEntry->dwFileSize = dwFileSize; + pFileEntry->dwCmpSize = 0; + pFileEntry->dwFlags = dwFlags | MPQ_FILE_EXISTS; + + // Initialize hashing of the file + if((hf->hctx = STORM_ALLOC(hash_state, 1)) != NULL) + md5_init((hash_state *)hf->hctx); + + // Fill-in file time and CRC + pFileEntry->FileTime = FileTime; + pFileEntry->dwCrc32 = crc32(0, Z_NULL, 0); + + // Mark the archive as modified + ha->dwFlags |= MPQ_FLAG_CHANGED; + + // Call the callback, if needed + if(ha->pfnAddFileCB != NULL) + ha->pfnAddFileCB(ha->pvAddFileUserData, 0, hf->dwDataSize, false); + hf->dwAddFileError = ERROR_SUCCESS; + + return ERROR_SUCCESS; +} + +//----------------------------------------------------------------------------- +// MPQ write data functions + +static DWORD WriteDataToMpqFile( TMPQArchive * ha, TMPQFile * hf, LPBYTE pbFileData, @@ -86,15 +130,10 @@ static int WriteDataToMpqFile( { TFileEntry * pFileEntry = hf->pFileEntry; ULONGLONG ByteOffset; - LPBYTE pbCompressed = NULL; // Compressed (target) data - LPBYTE pbToWrite = NULL; // Data to write to the file - int nCompressionLevel = -1; // ADPCM compression level (only used for wave files) - int nError = ERROR_SUCCESS; - - // If the caller wants ADPCM compression, we will set wave compression level to 4, - // which corresponds to medium quality - if(dwCompression & LOSSY_COMPRESSION_MASK) - nCompressionLevel = 4; + LPBYTE pbCompressed = NULL; // Compressed (target) data + LPBYTE pbToWrite = hf->pbFileSector; // Data to write to the file + DWORD dwErrCode = ERROR_SUCCESS; + int nCompressionLevel; // ADPCM compression level (only used for wave files) // Make sure that the caller won't overrun the previously initiated file size assert(hf->dwFilePos + dwDataSize <= pFileEntry->dwFileSize); @@ -102,20 +141,19 @@ static int WriteDataToMpqFile( assert(hf->pbFileSector != NULL); if((hf->dwFilePos + dwDataSize) > pFileEntry->dwFileSize) return ERROR_DISK_FULL; - pbToWrite = hf->pbFileSector; // Now write all data to the file sector buffer - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { DWORD dwBytesInSector = hf->dwFilePos % hf->dwSectorSize; 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); @@ -136,12 +174,13 @@ static int WriteDataToMpqFile( // Set the position in the file ByteOffset = hf->RawFilePos + pFileEntry->dwCmpSize; - // Update CRC32 and MD5 of the file - md5_process((hash_state *)hf->hctx, hf->pbFileSector, dwBytesInSector); + // Update MD5 and CRC32 of the file + if(hf->hctx != NULL) + md5_process((hash_state *)hf->hctx, hf->pbFileSector, dwBytesInSector); hf->dwCrc32 = crc32(hf->dwCrc32, hf->pbFileSector, dwBytesInSector); // Compress the file sector, if needed - if(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED) + if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) { int nOutBuffer = (int)dwBytesInSector; int nInBuffer = (int)dwBytesInSector; @@ -154,33 +193,39 @@ static int WriteDataToMpqFile( pbToWrite = pbCompressed = STORM_ALLOC(BYTE, hf->dwSectorSize + 0x100); if(pbCompressed == NULL) { - nError = ERROR_NOT_ENOUGH_MEMORY; + dwErrCode = ERROR_NOT_ENOUGH_MEMORY; break; } } // - // Note that both SCompImplode and SCompCompress give original buffer, - // if they are unable to comperss the data. + // Note that both SCompImplode and SCompCompress copy data as-is, + // if they are unable to compress the data. // if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE) { - SCompImplode((char *)pbCompressed, - &nOutBuffer, - (char *)hf->pbFileSector, - nInBuffer); + SCompImplode(pbCompressed, &nOutBuffer, hf->pbFileSector, nInBuffer); } if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS) { - SCompCompress((char *)pbCompressed, - &nOutBuffer, - (char *)hf->pbFileSector, - nInBuffer, - (unsigned)dwCompression, - 0, - nCompressionLevel); + // If this is the first sector, we need to override the given compression + // by the first sector compression. This is because the entire sector must + // be compressed by the same compression. + // + // Test case: + // + // WRITE_FILE(hFile, pvBuffer, 0x10, MPQ_COMPRESSION_PKWARE) // Write 0x10 bytes (sector 0) + // WRITE_FILE(hFile, pvBuffer, 0x10, MPQ_COMPRESSION_ADPCM_MONO) // Write 0x10 bytes (still sector 0) + // WRITE_FILE(hFile, pvBuffer, 0x10, MPQ_COMPRESSION_ADPCM_MONO) // Write 0x10 bytes (still sector 0) + // WRITE_FILE(hFile, pvBuffer, 0x10, MPQ_COMPRESSION_ADPCM_MONO) // Write 0x10 bytes (still sector 0) + dwCompression = (dwSectorIndex == 0) ? hf->dwCompression0 : dwCompression; + + // If the caller wants ADPCM compression, we will set wave compression level to 4, + // which corresponds to medium quality + nCompressionLevel = (dwCompression & MPQ_LOSSY_COMPRESSION_MASK) ? 4 : -1; + SCompCompress(pbCompressed, &nOutBuffer, hf->pbFileSector, nInBuffer, (unsigned)dwCompression, 0, nCompressionLevel); } // Update sector positions @@ -191,7 +236,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) @@ -201,16 +246,24 @@ static int WriteDataToMpqFile( BSWAP_ARRAY32_UNSIGNED(pbToWrite, dwBytesInSector); } + // Do not allow Warcraft III maps to go over 2GB. + // https://github.com/ladislav-zezula/StormLib/issues/306 + if((ha->dwFlags & MPQ_FLAG_WAR3_MAP) && (ByteOffset + dwBytesInSector) > 0x7FFFFFFF) + { + dwErrCode = ERROR_DISK_FULL; + break; + } + // Write the file sector if(!FileStream_Write(ha->pStream, &ByteOffset, pbToWrite, dwBytesInSector)) { - nError = GetLastError(); + dwErrCode = SErrGetLastError(); break; } // Call the compact callback, if any - if(AddFileCB != NULL) - AddFileCB(pvUserData, hf->dwFilePos, hf->dwDataSize, false); + if(ha->pfnAddFileCB != NULL) + ha->pfnAddFileCB(ha->pvAddFileUserData, hf->dwFilePos, hf->dwDataSize, false); // Update the compressed file size pFileEntry->dwCmpSize += dwBytesInSector; @@ -223,13 +276,13 @@ static int WriteDataToMpqFile( // Cleanup if(pbCompressed != NULL) STORM_FREE(pbCompressed); - return nError; + return dwErrCode; } //----------------------------------------------------------------------------- // Recrypts file data for file renaming -static int RecryptFileData( +static DWORD RecryptFileData( TMPQArchive * ha, TMPQFile * hf, const char * szFileName, @@ -240,14 +293,14 @@ static int RecryptFileData( DWORD dwBytesToRecrypt = pFileEntry->dwCmpSize; DWORD dwOldKey; DWORD dwNewKey; - int nError = ERROR_SUCCESS; + DWORD dwErrCode = ERROR_SUCCESS; // The file must be encrypted assert(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED); // File decryption key is calculated from the plain name - szNewFileName = GetPlainFileNameA(szNewFileName); - szFileName = GetPlainFileNameA(szFileName); + szNewFileName = GetPlainFileName(szNewFileName); + szFileName = GetPlainFileName(szFileName); // Calculate both file keys dwOldKey = DecryptFileKey(szFileName, pFileEntry->ByteOffset, pFileEntry->dwFileSize, pFileEntry->dwFlags); @@ -263,21 +316,21 @@ static int RecryptFileData( hf->RawFilePos = ha->MpqPos + hf->MpqFilePos; // Allocate buffer for file transfer - nError = AllocateSectorBuffer(hf); - if(nError != ERROR_SUCCESS) - return nError; + dwErrCode = AllocateSectorBuffer(hf); + if(dwErrCode != ERROR_SUCCESS) + return dwErrCode; // Also allocate buffer for sector offsets // Note: Don't load sector checksums, we don't need to recrypt them - nError = AllocateSectorOffsets(hf, true); - if(nError != ERROR_SUCCESS) - return nError; + dwErrCode = AllocateSectorOffsets(hf, true); + if(dwErrCode != ERROR_SUCCESS) + return dwErrCode; // If we have sector offsets, recrypt these as well if(hf->SectorOffsets != NULL) { // Allocate secondary buffer for sectors copy - DWORD * SectorOffsetsCopy = (DWORD *)STORM_ALLOC(BYTE, hf->SectorOffsets[0]); + DWORD * SectorOffsetsCopy = STORM_ALLOC(DWORD, hf->SectorOffsets[0] / sizeof(DWORD)); DWORD dwSectorOffsLen = hf->SectorOffsets[0]; if(SectorOffsetsCopy == NULL) @@ -290,13 +343,13 @@ static int RecryptFileData( // Write the recrypted array back if(!FileStream_Write(ha->pStream, &hf->RawFilePos, SectorOffsetsCopy, dwSectorOffsLen)) - nError = GetLastError(); + dwErrCode = SErrGetLastError(); STORM_FREE(SectorOffsetsCopy); } // Now we have to recrypt all file sectors. We do it without // recompression, because recompression is not necessary in this case - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { for(DWORD dwSector = 0; dwSector < hf->dwSectorCount; dwSector++) { @@ -315,17 +368,17 @@ static int RecryptFileData( } // Calculate the raw file offset of the file sector - CalculateRawSectorOffset(RawFilePos, hf, dwRawByteOffset); + RawFilePos = CalculateRawSectorOffset(hf, dwRawByteOffset); // Read the file sector if(!FileStream_Read(ha->pStream, &RawFilePos, hf->pbFileSector, dwRawDataInSector)) { - nError = GetLastError(); + dwErrCode = SErrGetLastError(); break; } // 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); @@ -335,7 +388,7 @@ static int RecryptFileData( // Write the sector back if(!FileStream_Write(ha->pStream, &RawFilePos, hf->pbFileSector, dwRawDataInSector)) { - nError = GetLastError(); + dwErrCode = SErrGetLastError(); break; } @@ -344,25 +397,25 @@ static int RecryptFileData( } } - return nError; + return dwErrCode; } //----------------------------------------------------------------------------- -// Support functions for adding files to the MPQ +// Internal support for MPQ modifications -int SFileAddFile_Init( +DWORD SFileAddFile_Init( TMPQArchive * ha, const char * szFileName, ULONGLONG FileTime, DWORD dwFileSize, - LCID lcLocale, + LCID lcFileLocale, DWORD dwFlags, TMPQFile ** phf) { TFileEntry * pFileEntry = NULL; - ULONGLONG TempPos; // For various file offset calculations TMPQFile * hf = NULL; // File structure for newly added file - int nError = ERROR_SUCCESS; + DWORD dwHashIndex = HASH_ENTRY_FREE; + DWORD dwErrCode = ERROR_SUCCESS; // // Note: This is an internal function so no validity checks are done. @@ -375,121 +428,157 @@ int SFileAddFile_Init( dwFlags &= ~MPQ_FILE_SECTOR_CRC; // Sector CRC is not allowed if the file is not compressed - if(!(dwFlags & MPQ_FILE_COMPRESSED)) + if(!(dwFlags & MPQ_FILE_COMPRESS_MASK)) dwFlags &= ~MPQ_FILE_SECTOR_CRC; - - // Fix Key is not allowed if the file is not enrypted + + // Fix Key is not allowed if the file is not encrypted if(!(dwFlags & MPQ_FILE_ENCRYPTED)) - dwFlags &= ~MPQ_FILE_FIX_KEY; + dwFlags &= ~MPQ_FILE_KEY_V2; // 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 if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_3) - lcLocale = 0; + lcFileLocale = 0; // Allocate the TMPQFile entry for newly added file - hf = CreateMpqFile(ha); + hf = CreateWritableHandle(ha, dwFileSize); if(hf == NULL) - nError = ERROR_NOT_ENOUGH_MEMORY; - - // Find a free space in the MPQ, as well as free block table entry - if(nError == ERROR_SUCCESS) - { - // Find the position where the file will be stored - FindFreeMpqSpace(ha, &hf->MpqFilePos); - 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) - { - TempPos = hf->MpqFilePos + dwFileSize; - TempPos += ha->pHeader->dwHashTableSize * sizeof(TMPQHash); - TempPos += ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock); - TempPos += ha->pHeader->dwBlockTableSize * sizeof(USHORT); - if((TempPos >> 32) != 0) - nError = ERROR_DISK_FULL; - } - } + return SErrGetLastError(); // Allocate file entry in the MPQ - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { // Check if the file already exists in the archive - pFileEntry = GetFileEntryExact(ha, szFileName, lcLocale); - if(pFileEntry == NULL) + pFileEntry = GetFileEntryExact(ha, szFileName, lcFileLocale, &dwHashIndex); + if(pFileEntry != NULL) { - pFileEntry = AllocateFileEntry(ha, szFileName, lcLocale); - if(pFileEntry == NULL) - nError = ERROR_DISK_FULL; + if(dwFlags & MPQ_FILE_REPLACEEXISTING) + InvalidateInternalFiles(ha); + else + dwErrCode = ERROR_ALREADY_EXISTS; } 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); - } + // Attempt to allocate new file entry + pFileEntry = AllocateFileEntry(ha, szFileName, lcFileLocale, &dwHashIndex); + if(pFileEntry != NULL) + InvalidateInternalFiles(ha); + else + dwErrCode = ERROR_DISK_FULL; } + + // Set the file entry to the file structure + hf->pFileEntry = pFileEntry; } - // - // At this point, the file name in file entry must be non-NULL - // + // Prepare the pointer to hash table entry + if(dwErrCode == ERROR_SUCCESS && ha->pHashTable != NULL && dwHashIndex < ha->pHeader->dwHashTableSize) + { + hf->pHashEntry = ha->pHashTable + dwHashIndex; + hf->pHashEntry->Locale = SFILE_LOCALE(lcFileLocale); + hf->pHashEntry->Platform = SFILE_PLATFORM(lcFileLocale); + hf->pHashEntry->Flags = 0; + } - // Create key for file encryption - if(nError == ERROR_SUCCESS && (dwFlags & MPQ_FILE_ENCRYPTED)) + // Prepare the file key + if(dwErrCode == ERROR_SUCCESS && (dwFlags & MPQ_FILE_ENCRYPTED)) { hf->dwFileKey = DecryptFileKey(szFileName, hf->MpqFilePos, dwFileSize, dwFlags); + if(hf->dwFileKey == 0) + dwErrCode = ERROR_UNKNOWN_FILE_KEY; } - if(nError == ERROR_SUCCESS) + // Fill the file entry and TMPQFile structure + if(dwErrCode == ERROR_SUCCESS) { - // Initialize the hash entry for the file - hf->pFileEntry = pFileEntry; - hf->dwDataSize = dwFileSize; + // At this point, the file name in the file entry must be set + assert(pFileEntry->szFileName != NULL); + assert(_stricmp(pFileEntry->szFileName, szFileName) == 0); - // Initialize the block table entry for the file - pFileEntry->ByteOffset = hf->MpqFilePos; - pFileEntry->dwFileSize = dwFileSize; - pFileEntry->dwCmpSize = 0; - pFileEntry->dwFlags = dwFlags | MPQ_FILE_EXISTS; - pFileEntry->lcLocale = (USHORT)lcLocale; - - // Initialize the file time, CRC32 and MD5 - assert(sizeof(hf->hctx) >= sizeof(hash_state)); - memset(pFileEntry->md5, 0, MD5_DIGEST_SIZE); - md5_init((hash_state *)hf->hctx); - pFileEntry->dwCrc32 = crc32(0, Z_NULL, 0); - - // If the caller gave us a file time, use it. - pFileEntry->FileTime = FileTime; - - // Call the callback, if needed - if(AddFileCB != NULL) - AddFileCB(pvUserData, 0, hf->dwDataSize, false); + dwErrCode = FillWritableHandle(ha, hf, FileTime, dwFileSize, dwFlags); } - // If an error occured, remember it - if(nError != ERROR_SUCCESS) - hf->bErrorOccured = true; + // Free the file handle if failed + if(dwErrCode != ERROR_SUCCESS && hf != NULL) + FreeFileHandle(hf); + + // Give the handle to the caller *phf = hf; - return nError; + return dwErrCode; } -int SFileAddFile_Write(TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD dwCompression) +DWORD SFileAddFile_Init( + TMPQArchive * ha, + TMPQFile * hfSrc, + TMPQFile ** phf) +{ + TFileEntry * pFileEntry = NULL; + TMPQFile * hf = NULL; // File structure for newly added file + ULONGLONG FileTime = hfSrc->pFileEntry->FileTime; + DWORD dwFileSize = hfSrc->pFileEntry->dwFileSize; + DWORD dwFlags = hfSrc->pFileEntry->dwFlags; + DWORD dwErrCode = ERROR_SUCCESS; + + // Allocate the TMPQFile entry for newly added file + hf = CreateWritableHandle(ha, dwFileSize); + if(hf == NULL) + dwErrCode = ERROR_NOT_ENOUGH_MEMORY; + + // We need to keep the file entry index the same like in the source archive + // This is because multiple hash table entries can point to the same file entry + if(dwErrCode == ERROR_SUCCESS) + { + // Retrieve the file entry for the target file + pFileEntry = ha->pFileTable + (hfSrc->pFileEntry - hfSrc->ha->pFileTable); + + // Copy all variables except file name + if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0) + { + pFileEntry[0] = hfSrc->pFileEntry[0]; + pFileEntry->szFileName = NULL; + } + else + dwErrCode = ERROR_ALREADY_EXISTS; + + // Set the file entry to the file structure + hf->pFileEntry = pFileEntry; + } + + // Prepare the pointer to hash table entry + if(dwErrCode == ERROR_SUCCESS && ha->pHashTable != NULL && hfSrc->pHashEntry != NULL) + { + hf->dwHashIndex = (DWORD)(hfSrc->pHashEntry - hfSrc->ha->pHashTable); + hf->pHashEntry = ha->pHashTable + hf->dwHashIndex; + } + + // Prepare the file key (copy from source file) + if(dwErrCode == ERROR_SUCCESS && (dwFlags & MPQ_FILE_ENCRYPTED)) + { + hf->dwFileKey = hfSrc->dwFileKey; + if(hf->dwFileKey == 0) + dwErrCode = ERROR_UNKNOWN_FILE_KEY; + } + + // Fill the file entry and TMPQFile structure + if(dwErrCode == ERROR_SUCCESS) + { + dwErrCode = FillWritableHandle(ha, hf, FileTime, dwFileSize, dwFlags); + } + + // Free the file handle if failed + if(dwErrCode != ERROR_SUCCESS && hf != NULL) + FreeFileHandle(hf); + + // Give the handle to the caller + *phf = hf; + return dwErrCode; +} + +DWORD SFileAddFile_Write(TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD dwCompression) { TMPQArchive * ha; TFileEntry * pFileEntry; - int nError = ERROR_SUCCESS; + DWORD dwErrCode = ERROR_SUCCESS; // Don't bother if the caller gave us zero size if(pvData == NULL || dwSize == 0) @@ -505,56 +594,44 @@ int SFileAddFile_Write(TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD d ULONGLONG RawFilePos = hf->RawFilePos; // Allocate buffer for file sector - nError = AllocateSectorBuffer(hf); - if(nError != ERROR_SUCCESS) - { - hf->bErrorOccured = true; - return nError; - } + hf->dwAddFileError = dwErrCode = AllocateSectorBuffer(hf); + if(dwErrCode != ERROR_SUCCESS) + return dwErrCode; // Allocate patch info, if the data is patch if(hf->pPatchInfo == NULL && IsIncrementalPatchFile(pvData, dwSize, &hf->dwPatchedFileSize)) { // Set the MPQ_FILE_PATCH_FILE flag - hf->pFileEntry->dwFlags |= MPQ_FILE_PATCH_FILE; + pFileEntry->dwFlags |= MPQ_FILE_PATCH_FILE; // Allocate the patch info - nError = AllocatePatchInfo(hf, false); - if(nError != ERROR_SUCCESS) - { - hf->bErrorOccured = true; - return nError; - } + hf->dwAddFileError = dwErrCode = AllocatePatchInfo(hf, false); + if(dwErrCode != ERROR_SUCCESS) + return dwErrCode; } // Allocate sector offsets if(hf->SectorOffsets == NULL) { - nError = AllocateSectorOffsets(hf, false); - if(nError != ERROR_SUCCESS) - { - hf->bErrorOccured = true; - return nError; - } + hf->dwAddFileError = dwErrCode = AllocateSectorOffsets(hf, false); + if(dwErrCode != ERROR_SUCCESS) + return dwErrCode; } // Create array of sector checksums if(hf->SectorChksums == NULL && (pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC)) { - nError = AllocateSectorChecksums(hf, false); - if(nError != ERROR_SUCCESS) - { - hf->bErrorOccured = true; - return nError; - } + hf->dwAddFileError = dwErrCode = AllocateSectorChecksums(hf, false); + if(dwErrCode != ERROR_SUCCESS) + return dwErrCode; } // Pre-save the patch info, if any if(hf->pPatchInfo != NULL) { if(!FileStream_Write(ha->pStream, &RawFilePos, hf->pPatchInfo, hf->pPatchInfo->dwLength)) - nError = GetLastError(); - + dwErrCode = SErrGetLastError(); + pFileEntry->dwCmpSize += hf->pPatchInfo->dwLength; RawFilePos += hf->pPatchInfo->dwLength; } @@ -565,7 +642,7 @@ int SFileAddFile_Write(TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD d if(hf->SectorOffsets != NULL) { if(!FileStream_Write(ha->pStream, &RawFilePos, hf->SectorOffsets, hf->SectorOffsets[0])) - nError = GetLastError(); + dwErrCode = SErrGetLastError(); pFileEntry->dwCmpSize += hf->SectorOffsets[0]; RawFilePos += hf->SectorOffsets[0]; @@ -573,181 +650,177 @@ int SFileAddFile_Write(TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD d } // Write the MPQ data to the file - if(nError == ERROR_SUCCESS) - nError = WriteDataToMpqFile(ha, hf, (LPBYTE)pvData, dwSize, dwCompression); + if(dwErrCode == ERROR_SUCCESS) + { + // Save the first sector compression to the file structure + // Note that the entire first file sector will be compressed + // by compression that was passed to the first call of SFileAddFile_Write + if(hf->dwFilePos == 0) + hf->dwCompression0 = dwCompression; + + // Write the data to the MPQ + dwErrCode = WriteDataToMpqFile(ha, hf, (LPBYTE)pvData, dwSize, dwCompression); + } // If it succeeded and we wrote all the file data, // we need to re-save sector offset table - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { if(hf->dwFilePos >= pFileEntry->dwFileSize) { // Finish calculating CRC32 - hf->pFileEntry->dwCrc32 = hf->dwCrc32; + pFileEntry->dwCrc32 = hf->dwCrc32; // Finish calculating MD5 - md5_done((hash_state *)hf->hctx, hf->pFileEntry->md5); + if(hf->hctx != NULL) + md5_done((hash_state *)hf->hctx, pFileEntry->md5); // If we also have sector checksums, write them to the file if(hf->SectorChksums != NULL) { - nError = WriteSectorChecksums(hf); - if(nError != ERROR_SUCCESS) - hf->bErrorOccured = true; + dwErrCode = WriteSectorChecksums(hf); } // Now write patch info if(hf->pPatchInfo != NULL) { - memcpy(hf->pPatchInfo->md5, hf->pFileEntry->md5, MD5_DIGEST_SIZE); - hf->pPatchInfo->dwDataSize = hf->pFileEntry->dwFileSize; - hf->pFileEntry->dwFileSize = hf->dwPatchedFileSize; - nError = WritePatchInfo(hf); - if(nError != ERROR_SUCCESS) - hf->bErrorOccured = true; + memcpy(hf->pPatchInfo->md5, pFileEntry->md5, MD5_DIGEST_SIZE); + hf->pPatchInfo->dwDataSize = pFileEntry->dwFileSize; + pFileEntry->dwFileSize = hf->dwPatchedFileSize; + dwErrCode = WritePatchInfo(hf); } // Now write sector offsets to the file if(hf->SectorOffsets != NULL) { - nError = WriteSectorOffsets(hf); - if(nError != ERROR_SUCCESS) - hf->bErrorOccured = true; + dwErrCode = WriteSectorOffsets(hf); } // Write the MD5 hashes of each file chunk, if required if(ha->pHeader->dwRawChunkSize != 0) { - nError = WriteMpqDataMD5(ha->pStream, - ha->MpqPos + hf->pFileEntry->ByteOffset, - hf->pFileEntry->dwCmpSize, - ha->pHeader->dwRawChunkSize); - if(nError != ERROR_SUCCESS) - hf->bErrorOccured = true; + dwErrCode = WriteMpqDataMD5(ha->pStream, + ha->MpqPos + pFileEntry->ByteOffset, + hf->pFileEntry->dwCmpSize, + ha->pHeader->dwRawChunkSize); } } } - else - { - hf->bErrorOccured = true; - } - return nError; + // Update the archive size + if((ha->MpqPos + pFileEntry->ByteOffset + pFileEntry->dwCmpSize) > ha->FileSize) + ha->FileSize = ha->MpqPos + pFileEntry->ByteOffset + pFileEntry->dwCmpSize; + + // Store the error code from the Write File operation + hf->dwAddFileError = dwErrCode; + return dwErrCode; } -int SFileAddFile_Finish(TMPQFile * hf) +DWORD SFileAddFile_Finish(TMPQFile * hf) { TMPQArchive * ha = hf->ha; TFileEntry * pFileEntry = hf->pFileEntry; - int nError = ERROR_SUCCESS; + DWORD dwErrCode = hf->dwAddFileError; // If all previous operations succeeded, we can update the MPQ - if(!hf->bErrorOccured) + if(dwErrCode == ERROR_SUCCESS) { // Verify if the caller wrote the file properly if(hf->pPatchInfo == NULL) { assert(pFileEntry != NULL); if(hf->dwFilePos != pFileEntry->dwFileSize) - { - nError = ERROR_CAN_NOT_COMPLETE; - hf->bErrorOccured = true; - } + dwErrCode = ERROR_CAN_NOT_COMPLETE; } else { if(hf->dwFilePos != hf->pPatchInfo->dwDataSize) - { - nError = ERROR_CAN_NOT_COMPLETE; - hf->bErrorOccured = true; - } + dwErrCode = ERROR_CAN_NOT_COMPLETE; } } - if(!hf->bErrorOccured) + // Now we need to recreate the HET table, if exists + if(dwErrCode == ERROR_SUCCESS && ha->pHetTable != NULL) + { + dwErrCode = RebuildHetTable(ha); + } + + // Update the block table size + if(dwErrCode == ERROR_SUCCESS) { // Call the user callback, if any - if(AddFileCB != NULL) - AddFileCB(pvUserData, hf->dwDataSize, hf->dwDataSize, true); - - // Update the size of the block table - ha->pHeader->dwBlockTableSize = ha->dwFileTableSize; + if(ha->pfnAddFileCB != NULL) + ha->pfnAddFileCB(ha->pvAddFileUserData, hf->dwDataSize, hf->dwDataSize, true); } else { // Free the file entry in MPQ tables if(pFileEntry != NULL) - FreeFileEntry(ha, pFileEntry); + DeleteFileEntry(ha, hf); } // Clear the add file callback - FreeMPQFile(hf); - pvUserData = NULL; - AddFileCB = NULL; - return nError; + FreeFileHandle(hf); + return dwErrCode; } //----------------------------------------------------------------------------- -// 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, + LCID lcFileLocale, DWORD dwFlags, HANDLE * phFile) { TMPQArchive * ha = (TMPQArchive *)hMpq; - int nError = ERROR_SUCCESS; + DWORD dwErrCode = ERROR_SUCCESS; // Check valid parameters - if(!IsValidMpqHandle(ha)) - nError = ERROR_INVALID_HANDLE; + if(!IsValidMpqHandle(hMpq)) + dwErrCode = ERROR_INVALID_HANDLE; if(szArchivedName == NULL || *szArchivedName == 0) - nError = ERROR_INVALID_PARAMETER; + dwErrCode = ERROR_INVALID_PARAMETER; if(phFile == NULL) - nError = ERROR_INVALID_PARAMETER; - + dwErrCode = 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; + if(dwErrCode == ERROR_SUCCESS) + { + if(ha->dwFlags & MPQ_FLAG_READ_ONLY) + dwErrCode = 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 a file under pseudo-file name + if(IsPseudoFileName(szArchivedName, NULL)) + dwErrCode = ERROR_INVALID_PARAMETER; - // Don't allow to add any of the internal files - if(IsInternalMpqFileName(szArchivedName)) - nError = ERROR_INTERNAL_FILE; + // Don't allow to add any of the internal files + if(IsInternalMpqFileName(szArchivedName)) + dwErrCode = ERROR_INTERNAL_FILE; + } // Perform validity check of the MPQ flags - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { // Mask all unsupported flags out - dwFlags &= MPQ_FILE_VALID_FLAGS; + dwFlags &= ha->dwValidFileFlags; // Check for valid flag combinations if((dwFlags & (MPQ_FILE_IMPLODE | MPQ_FILE_COMPRESS)) == (MPQ_FILE_IMPLODE | MPQ_FILE_COMPRESS)) - nError = ERROR_INVALID_PARAMETER; + dwErrCode = ERROR_INVALID_PARAMETER; } - // 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); - } + // Initiate the add file operation + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = SFileAddFile_Init(ha, szArchivedName, FileTime, dwFileSize, lcFileLocale, dwFlags, (TMPQFile **)phFile); // Deal with the errors - if(nError != ERROR_SUCCESS) - SetLastError(nError); - return (nError == ERROR_SUCCESS); + if(dwErrCode != ERROR_SUCCESS) + SErrSetLastError(dwErrCode); + return (dwErrCode == ERROR_SUCCESS); } bool WINAPI SFileWriteFile( @@ -757,16 +830,16 @@ bool WINAPI SFileWriteFile( DWORD dwCompression) { TMPQFile * hf = (TMPQFile *)hFile; - int nError = ERROR_SUCCESS; + DWORD dwErrCode = ERROR_SUCCESS; // Check the proper parameters - if(!IsValidFileHandle(hf)) - nError = ERROR_INVALID_HANDLE; + if(!IsValidFileHandle(hFile)) + dwErrCode = ERROR_INVALID_HANDLE; if(hf->bIsWriteHandle == false) - nError = ERROR_INVALID_HANDLE; + dwErrCode = ERROR_INVALID_HANDLE; // Special checks for single unit files - if(nError == ERROR_SUCCESS && (hf->pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT)) + if(dwErrCode == ERROR_SUCCESS && (hf->pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT)) { // // Note: Blizzard doesn't support single unit files @@ -775,50 +848,50 @@ bool WINAPI SFileWriteFile( // // if(dwFlags & MPQ_FILE_IMPLODE) -// nError = ERROR_INVALID_PARAMETER; +// dwErrCode = ERROR_INVALID_PARAMETER; // // if(dwFlags & MPQ_FILE_ENCRYPTED) -// nError = ERROR_INVALID_PARAMETER; - +// dwErrCode = ERROR_INVALID_PARAMETER; + // Lossy compression is not allowed on single unit files - if(dwCompression & LOSSY_COMPRESSION_MASK) - nError = ERROR_INVALID_PARAMETER; + if(dwCompression & MPQ_LOSSY_COMPRESSION_MASK) + dwErrCode = ERROR_INVALID_PARAMETER; } // Write the data to the file - if(nError == ERROR_SUCCESS) - nError = SFileAddFile_Write(hf, pvData, dwSize, dwCompression); - + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = SFileAddFile_Write(hf, pvData, dwSize, dwCompression); + // Deal with errors - if(nError != ERROR_SUCCESS) - SetLastError(nError); - return (nError == ERROR_SUCCESS); + if(dwErrCode != ERROR_SUCCESS) + SErrSetLastError(dwErrCode); + return (dwErrCode == ERROR_SUCCESS); } bool WINAPI SFileFinishFile(HANDLE hFile) { TMPQFile * hf = (TMPQFile *)hFile; - int nError = ERROR_SUCCESS; + DWORD dwErrCode = ERROR_SUCCESS; // Check the proper parameters - if(!IsValidFileHandle(hf)) - nError = ERROR_INVALID_HANDLE; + if(!IsValidFileHandle(hFile)) + dwErrCode = ERROR_INVALID_HANDLE; if(hf->bIsWriteHandle == false) - nError = ERROR_INVALID_HANDLE; + dwErrCode = ERROR_INVALID_HANDLE; // Finish the file - if(nError == ERROR_SUCCESS) - nError = SFileAddFile_Finish(hf); - + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = SFileAddFile_Finish(hf); + // Deal with errors - if(nError != ERROR_SUCCESS) - SetLastError(nError); - return (nError == ERROR_SUCCESS); + if(dwErrCode != ERROR_SUCCESS) + SErrSetLastError(dwErrCode); + return (dwErrCode == ERROR_SUCCESS); } //----------------------------------------------------------------------------- -// Adds a file to the archive +// Adds a file to the archive bool WINAPI SFileAddFileEx( HANDLE hMpq, @@ -831,6 +904,7 @@ bool WINAPI SFileAddFileEx( ULONGLONG FileSize = 0; ULONGLONG FileTime = 0; TFileStream * pStream = NULL; + TMPQArchive * ha; HANDLE hMpqFile = NULL; LPBYTE pbFileData = NULL; DWORD dwBytesRemaining = 0; @@ -839,67 +913,72 @@ bool WINAPI SFileAddFileEx( DWORD dwChannels = 0; bool bIsAdpcmCompression = false; bool bIsFirstSector = true; - int nError = ERROR_SUCCESS; + DWORD dwErrCode = ERROR_SUCCESS; // Check parameters - if(szFileName == NULL || *szFileName == 0) - nError = ERROR_INVALID_PARAMETER; + if(hMpq == NULL || szFileName == NULL || *szFileName == 0 || (ha = IsValidMpqHandle(hMpq)) == NULL) + { + SErrSetLastError(ERROR_INVALID_PARAMETER); + return false; + } // Open added file - if(nError == ERROR_SUCCESS) - { - pStream = FileStream_OpenFile(szFileName, STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); - if(pStream == NULL) - nError = GetLastError(); - } + pStream = FileStream_OpenFile(szFileName, STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE); + if(pStream == NULL) + return false; - // Get the file size and file time - if(nError == ERROR_SUCCESS) - { - FileStream_GetTime(pStream, &FileTime); - FileStream_GetSize(pStream, FileSize); - - // Files bigger than 4GB cannot be added to MPQ - if(FileSize >> 32) - nError = ERROR_DISK_FULL; - } + // Files bigger than 4GB cannot be added to MPQ + FileStream_GetTime(pStream, &FileTime); + FileStream_GetSize(pStream, &FileSize); + if(FileSize >> 32) + dwErrCode = ERROR_DISK_FULL; // Allocate data buffer for reading from the source file - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { dwBytesRemaining = (DWORD)FileSize; pbFileData = STORM_ALLOC(BYTE, dwSectorSize); if(pbFileData == NULL) - nError = ERROR_NOT_ENOUGH_MEMORY; + dwErrCode = ERROR_NOT_ENOUGH_MEMORY; + } + + // LZMA compression can only be present in MPQ version 2 or higher + if(dwErrCode == ERROR_SUCCESS) + { + if(dwCompression == MPQ_COMPRESSION_LZMA && ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1) + dwErrCode = ERROR_INVALID_PARAMETER; } // Deal with various combination of compressions - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { // When the compression for next blocks is set to default, // we will copy the compression for the first sector 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_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO)) { - // The first compression must not be WAVE + // The compression of the first file sector must not be ADPCM + // in order not to corrupt the headers if(dwCompression & (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO)) dwCompression = MPQ_COMPRESSION_PKWARE; - + + // Remove both flag mono and stereo flags. + // They will be re-added according to WAVE type dwCompressionNext &= ~(MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO); bIsAdpcmCompression = true; } // Initiate adding file to the MPQ - if(!SFileCreateFile(hMpq, szArchivedName, FileTime, (DWORD)FileSize, lcFileLocale, dwFlags, &hMpqFile)) - nError = GetLastError(); + if(!SFileCreateFile(hMpq, szArchivedName, FileTime, (DWORD)FileSize, g_lcFileLocale, dwFlags, &hMpqFile)) + dwErrCode = SErrGetLastError(); } // Write the file data to the MPQ - while(nError == ERROR_SUCCESS && dwBytesRemaining != 0) + while(dwErrCode == ERROR_SUCCESS && dwBytesRemaining != 0) { // Get the number of bytes remaining in the source file dwBytesToRead = dwBytesRemaining; @@ -909,29 +988,33 @@ bool WINAPI SFileAddFileEx( // Read data from the local file if(!FileStream_Read(pStream, NULL, pbFileData, dwBytesToRead)) { - nError = GetLastError(); + dwErrCode = SErrGetLastError(); 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)) + // The file must really be a WAVE file with at least 16 bits per sample, + // otherwise the ADPCM compression will corrupt it + if(IsWaveFile_16BitsPerAdpcmSample(pbFileData, dwBytesToRead, &dwChannels)) { - nError = ERROR_BAD_FORMAT; - break; + // Setup the compression of next sectors according to number of channels + dwCompressionNext |= (dwChannels == 1) ? MPQ_COMPRESSION_ADPCM_MONO : MPQ_COMPRESSION_ADPCM_STEREO; + } + else + { + // Setup the compression of next sectors to a lossless compression + dwCompressionNext = (dwCompression & MPQ_LOSSY_COMPRESSION_MASK) ? MPQ_COMPRESSION_PKWARE : dwCompression; } - // 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)) { - nError = GetLastError(); + dwErrCode = SErrGetLastError(); break; } @@ -944,7 +1027,7 @@ bool WINAPI SFileAddFileEx( if(hMpqFile != NULL) { if(!SFileFinishFile(hMpqFile)) - nError = GetLastError(); + dwErrCode = SErrGetLastError(); } // Cleanup and exit @@ -952,11 +1035,11 @@ bool WINAPI SFileAddFileEx( STORM_FREE(pbFileData); if(pStream != NULL) FileStream_Close(pStream); - if(nError != ERROR_SUCCESS) - SetLastError(nError); - return (nError == ERROR_SUCCESS); + if(dwErrCode != ERROR_SUCCESS) + SErrSetLastError(dwErrCode); + return (dwErrCode == ERROR_SUCCESS); } - + // Adds a data file into the archive bool WINAPI SFileAddFile(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags) { @@ -983,7 +1066,7 @@ bool WINAPI SFileAddWave(HANDLE hMpq, const TCHAR * szFileName, const char * szA // 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) @@ -1015,152 +1098,137 @@ bool WINAPI SFileAddWave(HANDLE hMpq, const TCHAR * szFileName, const char * szA //----------------------------------------------------------------------------- // bool SFileRemoveFile(HANDLE hMpq, char * szFileName) // -// 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. +// This function removes a file from the archive. +// 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; + TMPQArchive * ha = IsValidMpqHandle(hMpq); + TMPQFile * hf = NULL; + DWORD dwErrCode = ERROR_SUCCESS; - // Keep compiler happy - dwSearchScope = dwSearchScope; + // Keep compilers happy + STORMLIB_UNUSED(dwSearchScope); // Check the parameters - if(nError == ERROR_SUCCESS) + if(ha == NULL) + dwErrCode = ERROR_INVALID_HANDLE; + if(szFileName == NULL || *szFileName == 0) + dwErrCode = ERROR_INVALID_PARAMETER; + if(IsInternalMpqFileName(szFileName)) + dwErrCode = ERROR_INTERNAL_FILE; + + // Do not allow to remove files from read-only or patched MPQs + if(dwErrCode == ERROR_SUCCESS) { - if(!IsValidMpqHandle(ha)) - nError = ERROR_INVALID_HANDLE; - if(szFileName == NULL || *szFileName == 0) - nError = ERROR_INVALID_PARAMETER; - if(IsInternalMpqFileName(szFileName)) - nError = ERROR_INTERNAL_FILE; + if((ha->dwFlags & MPQ_FLAG_READ_ONLY) || (ha->haPatch != NULL)) + dwErrCode = ERROR_ACCESS_DENIED; } - if(nError == ERROR_SUCCESS) + // If all checks have passed, we can delete the file from the MPQ + if(dwErrCode == ERROR_SUCCESS) { - // Do not allow to remove files from MPQ open for read only - if(ha->dwFlags & MPQ_FLAG_READ_ONLY) - nError = ERROR_ACCESS_DENIED; - } - - // Get hash entry belonging to this file - if(nError == ERROR_SUCCESS) - { - if(!IsPseudoFileName(szFileName, &dwFileIndex)) + // Open the file from the MPQ + if(SFileOpenFileEx(hMpq, szFileName, SFILE_OPEN_BASE_FILE, (HANDLE *)&hf)) { - if((pFileEntry = GetFileEntryExact(ha, (char *)szFileName, lcFileLocale)) == NULL) - nError = ERROR_FILE_NOT_FOUND; + // Delete the file entry + dwErrCode = DeleteFileEntry(ha, hf); + FreeFileHandle(hf); } else - { - if((pFileEntry = GetFileEntryByIndex(ha, dwFileIndex)) == NULL) - nError = ERROR_FILE_NOT_FOUND; - } + dwErrCode = SErrGetLastError(); } - // Test if the file is not already deleted - if(nError == ERROR_SUCCESS) + // If the file has been deleted, we need to invalidate + // the internal files and recreate HET table + if(dwErrCode == ERROR_SUCCESS) { - if(!(pFileEntry->dwFlags & MPQ_FILE_EXISTS)) - nError = ERROR_FILE_NOT_FOUND; - } - - if(nError == ERROR_SUCCESS) - { - // Invalidate the entries for (listfile) and (attributes) + // Invalidate the entries for internal files // After we are done with MPQ changes, we need to re-create them anyway InvalidateInternalFiles(ha); - // Mark the file entry as free - nError = FreeFileEntry(ha, pFileEntry); + // + // Don't rebuild HET table now; the file's flags indicate + // that it's been deleted, which is enough + // } // Resolve error and exit - if(nError != ERROR_SUCCESS) - SetLastError(nError); - return (nError == ERROR_SUCCESS); + if(dwErrCode != ERROR_SUCCESS) + SErrSetLastError(dwErrCode); + return (dwErrCode == ERROR_SUCCESS); } // Renames the file within the archive. bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szFileName, const char * szNewFileName) { - TMPQArchive * ha = (TMPQArchive *)hMpq; - TFileEntry * pFileEntry = NULL; - ULONGLONG RawDataOffs; + TMPQArchive * ha = IsValidMpqHandle(hMpq); + TFileEntry * pFileEntry; TMPQFile * hf; - int nError = ERROR_SUCCESS; + DWORD dwHashIndex = 0; + DWORD dwErrCode = ERROR_SUCCESS; + LCID lcFileLocale = 0; // Test the valid parameters - if(nError == ERROR_SUCCESS) - { - if(!IsValidMpqHandle(ha)) - nError = ERROR_INVALID_HANDLE; - if(szFileName == NULL || *szFileName == 0 || szNewFileName == NULL || *szNewFileName == 0) - nError = ERROR_INVALID_PARAMETER; - } + if(ha == NULL) + dwErrCode = ERROR_INVALID_HANDLE; + if(szFileName == NULL || *szFileName == 0 || szNewFileName == NULL || *szNewFileName == 0) + dwErrCode = ERROR_INVALID_PARAMETER; + if(IsInternalMpqFileName(szFileName) || IsInternalMpqFileName(szNewFileName)) + dwErrCode = ERROR_INTERNAL_FILE; - if(nError == ERROR_SUCCESS) + // Do not allow to rename files in MPQ open for read only + if(dwErrCode == ERROR_SUCCESS) { - // Do not allow to rename files in MPQ open for read only 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)) - nError = ERROR_INTERNAL_FILE; + dwErrCode = ERROR_ACCESS_DENIED; } - // Find the current file entry. - if(nError == ERROR_SUCCESS) + // Retrieve the locale of the existing file + // Could be the preferred one or neutral one + if(dwErrCode == ERROR_SUCCESS && ha->pHashTable != 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) - { - if(GetFileEntryLocale(ha, szNewFileName, pFileEntry->lcLocale) != NULL) - nError = ERROR_ALREADY_EXISTS; - } - - // Now we rename the existing file entry. - if(nError == ERROR_SUCCESS) - { - // Rename the file entry - nError = RenameFileEntry(ha, pFileEntry, szNewFileName); - } - - // Now we copy the existing file entry to the new one - if(nError == ERROR_SUCCESS) - { - // If the file is encrypted, we have to re-crypt the file content - // with the new decryption key - if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) + if((pFileEntry = GetFileEntryLocale(ha, szFileName, g_lcFileLocale, &dwHashIndex)) != NULL) { - hf = CreateMpqFile(ha); - if(hf != NULL) + lcFileLocale = ha->pHashTable[dwHashIndex].Locale; + } + else + dwErrCode = ERROR_FILE_NOT_FOUND; + } + + // The target file entry must not be there + if(dwErrCode == ERROR_SUCCESS) + { + if(GetFileEntryExact(ha, szNewFileName, lcFileLocale) != NULL) + { + dwErrCode = ERROR_ALREADY_EXISTS; + } + } + + // Open the file from the MPQ + if(dwErrCode == ERROR_SUCCESS) + { + // Attempt to open the file + if(SFileOpenFileEx(hMpq, szFileName, SFILE_OPEN_BASE_FILE, (HANDLE *)&hf)) + { + ULONGLONG RawDataOffs; + + // Invalidate the entries for internal files + pFileEntry = hf->pFileEntry; + InvalidateInternalFiles(ha); + + // Rename the file entry in the table + dwErrCode = RenameFileEntry(ha, hf, szNewFileName); + + // If the file is encrypted, we have to re-crypt the file content + // with the new decryption key + if((dwErrCode == ERROR_SUCCESS) && (pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)) { // Recrypt the file data in the MPQ - hf->pFileEntry = pFileEntry; - hf->dwDataSize = pFileEntry->dwFileSize; - nError = RecryptFileData(ha, hf, szFileName, szNewFileName); - - // Update the MD5 - if(ha->pHeader->dwRawChunkSize != 0) + dwErrCode = RecryptFileData(ha, hf, szFileName, szNewFileName); + + // Update the MD5 of the raw block + if(dwErrCode == ERROR_SUCCESS && ha->pHeader->dwRawChunkSize != 0) { RawDataOffs = ha->MpqPos + pFileEntry->ByteOffset; WriteMpqDataMD5(ha->pStream, @@ -1168,24 +1236,25 @@ bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szFileName, const char * s pFileEntry->dwCmpSize, ha->pHeader->dwRawChunkSize); } - - FreeMPQFile(hf); - } - else - { - nError = ERROR_NOT_ENOUGH_MEMORY; } + + // Free the file handle + FreeFileHandle(hf); + } + else + { + dwErrCode = SErrGetLastError(); } } - // - // Note: MPQ_FLAG_CHANGED is set by RenameFileEntry - // + // We also need to rebuild the HET table, if present + if(dwErrCode == ERROR_SUCCESS && ha->pHetTable != NULL) + dwErrCode = RebuildHetTable(ha); - // Resolve error and return - if(nError != ERROR_SUCCESS) - SetLastError(nError); - return (nError == ERROR_SUCCESS); + // Resolve error and exit + if(dwErrCode != ERROR_SUCCESS) + SErrSetLastError(dwErrCode); + return (dwErrCode == ERROR_SUCCESS); } //----------------------------------------------------------------------------- @@ -1197,7 +1266,7 @@ bool WINAPI SFileSetDataCompression(DWORD DataCompression) if((DataCompression & uValidMask) != DataCompression) { - SetLastError(ERROR_INVALID_PARAMETER); + SErrSetLastError(ERROR_INVALID_PARAMETER); return false; } @@ -1212,65 +1281,62 @@ bool WINAPI SFileSetFileLocale(HANDLE hFile, LCID lcNewLocale) { TMPQArchive * ha; TFileEntry * pFileEntry; - TMPQFile * hf = (TMPQFile *)hFile; + TMPQFile * hf = IsValidFileHandle(hFile); - // Invalid handle => do nothing - if(!IsValidFileHandle(hf)) + // Invalid file handle => return error + if(hf == NULL) { - SetLastError(ERROR_INVALID_HANDLE); + SErrSetLastError(ERROR_INVALID_HANDLE); return false; } - // Do not allow unnamed access - if(hf->pFileEntry->szFileName == NULL) + // Invalid archive handle => return error + if((ha = IsValidMpqHandle(hf->ha)) == NULL) { - SetLastError(ERROR_CAN_NOT_COMPLETE); - return false; - } - - // Do not allow to change locale of any internal file - if(IsInternalMpqFileName(hf->pFileEntry->szFileName)) - { - SetLastError(ERROR_INTERNAL_FILE); - return false; - } - - // Do not allow changing file locales in MPQs version 3 or higher - ha = hf->ha; - if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_3) - { - SetLastError(ERROR_NOT_SUPPORTED); + SErrSetLastError(ERROR_INVALID_HANDLE); return false; } // Do not allow to rename files in MPQ open for read only if(ha->dwFlags & MPQ_FLAG_READ_ONLY) { - SetLastError(ERROR_ACCESS_DENIED); + SErrSetLastError(ERROR_ACCESS_DENIED); return false; } - // If the file already has that locale, return OK - if(hf->pFileEntry->lcLocale == lcNewLocale) - return true; + // Do not allow unnamed access + if(hf->pFileEntry->szFileName == NULL) + { + SErrSetLastError(ERROR_CAN_NOT_COMPLETE); + return false; + } + + // Do not allow to change locale of any internal file + if(IsInternalMpqFileName(hf->pFileEntry->szFileName)) + { + SErrSetLastError(ERROR_INTERNAL_FILE); + return false; + } + + // Do not allow changing file locales if there is no hash table + if(hf->pHashEntry == NULL) + { + SErrSetLastError(ERROR_NOT_SUPPORTED); + return false; + } // We have to check if the file+locale is not already there - pFileEntry = GetFileEntryExact(ha, hf->pFileEntry->szFileName, lcNewLocale); + pFileEntry = GetFileEntryExact(ha, hf->pFileEntry->szFileName, lcNewLocale, NULL); if(pFileEntry != NULL) { - SetLastError(ERROR_ALREADY_EXISTS); + SErrSetLastError(ERROR_ALREADY_EXISTS); return false; } - // Set the locale and return success - pFileEntry = hf->pFileEntry; - pFileEntry->lcLocale = (USHORT)lcNewLocale; - - // Save the new locale to the hash table, if any - if(ha->pHashTable != NULL) - ha->pHashTable[pFileEntry->dwHashIndex].lcLocale = (USHORT)lcNewLocale; - - // Remember that the MPQ tables have been changed + // Update the locale in the hash table entry + hf->pHashEntry->Locale = SFILE_LOCALE(lcNewLocale); + hf->pHashEntry->Platform = SFILE_PLATFORM(lcNewLocale); + hf->pHashEntry->Flags = 0; ha->dwFlags |= MPQ_FLAG_CHANGED; return true; } @@ -1278,9 +1344,17 @@ bool WINAPI SFileSetFileLocale(HANDLE hFile, LCID lcNewLocale) //----------------------------------------------------------------------------- // Sets add file callback -bool WINAPI SFileSetAddFileCallback(HANDLE /* hMpq */, SFILE_ADDFILE_CALLBACK aAddFileCB, void * pvData) +bool WINAPI SFileSetAddFileCallback(HANDLE hMpq, SFILE_ADDFILE_CALLBACK AddFileCB, void * pvUserData) { - pvUserData = pvData; - AddFileCB = aAddFileCB; + TMPQArchive * ha = (TMPQArchive *) hMpq; + + if(!IsValidMpqHandle(hMpq)) + { + SErrSetLastError(ERROR_INVALID_HANDLE); + return false; + } + + ha->pvAddFileUserData = pvUserData; + ha->pfnAddFileCB = AddFileCB; return true; } diff --git a/dep/StormLib/src/SFileAttributes.cpp b/dep/StormLib/src/SFileAttributes.cpp index 0f056088fa..fa2dacf874 100644 --- a/dep/StormLib/src/SFileAttributes.cpp +++ b/dep/StormLib/src/SFileAttributes.cpp @@ -24,352 +24,448 @@ typedef struct _MPQ_ATTRIBUTES_HEADER // Followed by an array of file times // Followed by an array of MD5 // Followed by an array of patch bits + + // Note: The MD5 in (attributes), if present, is a hash of the entire file. + // In case the file is an incremental patch, it contains MD5 of the file + // after being patched. + } MPQ_ATTRIBUTES_HEADER, *PMPQ_ATTRIBUTES_HEADER; +//----------------------------------------------------------------------------- +// Local functions + +static DWORD GetSizeOfAttributesFile(DWORD dwAttrFlags, DWORD dwBlockTableSize) +{ + DWORD cbAttrFile = sizeof(MPQ_ATTRIBUTES_HEADER); + + // Calculate size of the (attributes) file + if(dwAttrFlags & MPQ_ATTRIBUTE_CRC32) + cbAttrFile += dwBlockTableSize * sizeof(DWORD); + if(dwAttrFlags & MPQ_ATTRIBUTE_FILETIME) + cbAttrFile += dwBlockTableSize * sizeof(ULONGLONG); + if(dwAttrFlags & MPQ_ATTRIBUTE_MD5) + cbAttrFile += dwBlockTableSize * MD5_DIGEST_SIZE; + + // The bit array has been created without the last bit belonging to (attributes) + // When the number of files is a multiplier of 8 plus one, then the size of (attributes) + // if 1 byte less than expected. + // Example: wow-update-13164.MPQ: BlockTableSize = 0x62E1, but there's only 0xC5C bytes + if(dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT) + cbAttrFile += (dwBlockTableSize + 6) / 8; + + return cbAttrFile; +} + +static DWORD CheckSizeOfAttributesFile(DWORD cbAttrFile, DWORD dwAttrFlags, DWORD dwBlockTableSize) +{ + DWORD cbHeaderSize = sizeof(MPQ_ATTRIBUTES_HEADER); + DWORD cbChecksumSize1 = 0; + DWORD cbChecksumSize2 = 0; + DWORD cbFileTimeSize1 = 0; + DWORD cbFileTimeSize2 = 0; + DWORD cbFileHashSize1 = 0; + DWORD cbFileHashSize2 = 0; + DWORD cbPatchBitSize1 = 0; + DWORD cbPatchBitSize2 = 0; + DWORD cbPatchBitSize3 = 0; + + // + // Various variants with the patch bit + // + // interface.MPQ.part from WoW build 10958 has + // the MPQ_ATTRIBUTE_PATCH_BIT set, but there's an array of DWORDs instead. + // The array is filled with zeros, so we don't know what it should contain + // + // Zenith.SC2MAP has the MPQ_ATTRIBUTE_PATCH_BIT set, but the bit array is missing + // + // Elimination Tournament 2.w3x's (attributes) have one entry less + // + // There may be two variants: Either the (attributes) file has full + // number of entries, or has one entry less + // + + // Get the expected size of CRC32 array + if(dwAttrFlags & MPQ_ATTRIBUTE_CRC32) + { + cbChecksumSize1 += dwBlockTableSize * sizeof(DWORD); + cbChecksumSize2 += cbChecksumSize1 - sizeof(DWORD); + } + + // Get the expected size of FILETIME array + if(dwAttrFlags & MPQ_ATTRIBUTE_FILETIME) + { + cbFileTimeSize1 += dwBlockTableSize * sizeof(ULONGLONG); + cbFileTimeSize2 += cbFileTimeSize1 - sizeof(ULONGLONG); + } + + // Get the expected size of MD5 array + if(dwAttrFlags & MPQ_ATTRIBUTE_MD5) + { + cbFileHashSize1 += dwBlockTableSize * MD5_DIGEST_SIZE; + cbFileHashSize2 += cbFileHashSize1 - MD5_DIGEST_SIZE; + } + + // Get the expected size of patch bit array + if(dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT) + { + cbPatchBitSize1 = + cbPatchBitSize2 = ((dwBlockTableSize + 6) / 8); + cbPatchBitSize3 = dwBlockTableSize * sizeof(DWORD); + } + + // Check if the (attributes) file entry count is equal to our file table size + if(cbAttrFile == (cbHeaderSize + cbChecksumSize1 + cbFileTimeSize1 + cbFileHashSize1 + cbPatchBitSize1)) + return dwBlockTableSize; + + // Check if the (attributes) file entry count is equal to our file table size minus one + if(cbAttrFile == (cbHeaderSize + cbChecksumSize2 + cbFileTimeSize2 + cbFileHashSize2 + cbPatchBitSize2)) + return dwBlockTableSize - 1; + + // Zenith.SC2MAP has the MPQ_ATTRIBUTE_PATCH_BIT set, but the bit array is missing + if(cbAttrFile == (cbHeaderSize + cbChecksumSize1 + cbFileTimeSize1 + cbFileHashSize1)) + return dwBlockTableSize; + + // interface.MPQ.part (WoW build 10958) has the MPQ_ATTRIBUTE_PATCH_BIT set + // but there's an array of DWORDs (filled with zeros) instead of array of bits + if(cbAttrFile == (cbHeaderSize + cbChecksumSize1 + cbFileTimeSize1 + cbFileHashSize1 + cbPatchBitSize3)) + return dwBlockTableSize; + +#ifdef __STORMLIB_TEST__ + // Invalid size of the (attributes) file + // Note that many MPQs, especially Warcraft III maps have the size of (attributes) invalid. + // We only perform this check if this is the STORMLIB testprogram itself +// assert(false); +#endif + + return 0; +} + +static DWORD LoadAttributesFile(TMPQArchive * ha, LPBYTE pbAttrFile, DWORD cbAttrFile) +{ + LPBYTE pbAttrFileEnd = pbAttrFile + cbAttrFile; + LPBYTE pbAttrPtr = pbAttrFile; + DWORD dwAttributesEntries = 0; + DWORD i; + + // Load and verify the header + if((pbAttrPtr + sizeof(MPQ_ATTRIBUTES_HEADER)) <= pbAttrFileEnd) + { + PMPQ_ATTRIBUTES_HEADER pAttrHeader = (PMPQ_ATTRIBUTES_HEADER)pbAttrPtr; + + // Verify the header version + BSWAP_ARRAY32_UNSIGNED(pAttrHeader, sizeof(MPQ_ATTRIBUTES_HEADER)); + if(pAttrHeader->dwVersion != MPQ_ATTRIBUTES_V1) + return ERROR_BAD_FORMAT; + + // Verify the flags + if(pAttrHeader->dwFlags & ~MPQ_ATTRIBUTE_ALL) + return ERROR_BAD_FORMAT; + + // Verify whether file size of (attributes) is expected + dwAttributesEntries = CheckSizeOfAttributesFile(cbAttrFile, pAttrHeader->dwFlags, ha->pHeader->dwBlockTableSize); + if(dwAttributesEntries == 0) + return ERROR_BAD_FORMAT; + + ha->dwAttrFlags = pAttrHeader->dwFlags; + pbAttrPtr = (LPBYTE)(pAttrHeader + 1); + } + + // Load the CRC32 (if present) + if(ha->dwAttrFlags & MPQ_ATTRIBUTE_CRC32) + { + LPDWORD ArrayCRC32 = (LPDWORD)pbAttrPtr; + DWORD cbArraySize = dwAttributesEntries * sizeof(DWORD); + + // Verify if there's enough data + if((pbAttrPtr + cbArraySize) > pbAttrFileEnd) + return ERROR_FILE_CORRUPT; + + BSWAP_ARRAY32_UNSIGNED(ArrayCRC32, cbArraySize); + for(i = 0; i < dwAttributesEntries; i++) + ha->pFileTable[i].dwCrc32 = ArrayCRC32[i]; + pbAttrPtr += cbArraySize; + } + + // Load the FILETIME (if present) + if(ha->dwAttrFlags & MPQ_ATTRIBUTE_FILETIME) + { + ULONGLONG * ArrayFileTime = (ULONGLONG *)pbAttrPtr; + DWORD cbArraySize = dwAttributesEntries * sizeof(ULONGLONG); + + // Verify if there's enough data + if((pbAttrPtr + cbArraySize) > pbAttrFileEnd) + return ERROR_FILE_CORRUPT; + + BSWAP_ARRAY64_UNSIGNED(ArrayFileTime, cbArraySize); + for(i = 0; i < dwAttributesEntries; i++) + ha->pFileTable[i].FileTime = ArrayFileTime[i]; + pbAttrPtr += cbArraySize; + } + + // Load the MD5 (if present) + if(ha->dwAttrFlags & MPQ_ATTRIBUTE_MD5) + { + LPBYTE ArrayMd5 = pbAttrPtr; + DWORD cbArraySize = dwAttributesEntries * MD5_DIGEST_SIZE; + + // Verify if there's enough data + if((pbAttrPtr + cbArraySize) > pbAttrFileEnd) + return ERROR_FILE_CORRUPT; + + for(i = 0; i < dwAttributesEntries; i++) + { + memcpy(ha->pFileTable[i].md5, ArrayMd5, MD5_DIGEST_SIZE); + ArrayMd5 += MD5_DIGEST_SIZE; + } + pbAttrPtr += cbArraySize; + } + + // Read the patch bit for each file (if present) + if(ha->dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT) + { + LPBYTE pbBitArray = pbAttrPtr; + DWORD cbArraySize = (dwAttributesEntries + 7) / 8; + DWORD dwByteIndex = 0; + DWORD dwBitMask = 0x80; + + // Verify if there's enough data + if((pbAttrPtr + cbArraySize) == pbAttrFileEnd) + { + for(i = 0; i < dwAttributesEntries; i++) + { + ha->pFileTable[i].dwFlags |= (pbBitArray[dwByteIndex] & dwBitMask) ? MPQ_FILE_PATCH_FILE : 0; + dwByteIndex += (dwBitMask & 0x01); + dwBitMask = (dwBitMask << 0x07) | (dwBitMask >> 0x01); + } + } + } + + return ERROR_SUCCESS; +} + +static LPBYTE CreateAttributesFile(TMPQArchive * ha, DWORD * pcbAttrFile) +{ + PMPQ_ATTRIBUTES_HEADER pAttrHeader; + TFileEntry * pFileTableEnd = ha->pFileTable + ha->pHeader->dwBlockTableSize; + TFileEntry * pFileEntry; + LPBYTE pbAttrFile; + LPBYTE pbAttrPtr; + size_t cbAttrFile; + + // Check if we need patch bits in the (attributes) file + for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) + { + if(pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) + { + ha->dwAttrFlags |= MPQ_ATTRIBUTE_PATCH_BIT; + break; + } + } + + // Allocate the buffer for holding the entire (attributes) + // Allocate 1 byte more (See GetSizeOfAttributesFile for more info) + cbAttrFile = GetSizeOfAttributesFile(ha->dwAttrFlags, ha->pHeader->dwBlockTableSize); + pbAttrFile = pbAttrPtr = STORM_ALLOC(BYTE, cbAttrFile + 1); + if(pbAttrFile != NULL) + { + // Make sure it's all zeroed + memset(pbAttrFile, 0, cbAttrFile + 1); + + // Write the header of the (attributes) file + pAttrHeader = (PMPQ_ATTRIBUTES_HEADER)pbAttrPtr; + pAttrHeader->dwVersion = BSWAP_INT32_UNSIGNED(100); + pAttrHeader->dwFlags = BSWAP_INT32_UNSIGNED((ha->dwAttrFlags & MPQ_ATTRIBUTE_ALL)); + pbAttrPtr = (LPBYTE)(pAttrHeader + 1); + + // Write the array of CRC32, if present + if(ha->dwAttrFlags & MPQ_ATTRIBUTE_CRC32) + { + LPDWORD pArrayCRC32 = (LPDWORD)pbAttrPtr; + + // Copy from file table + for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) + *pArrayCRC32++ = BSWAP_INT32_UNSIGNED(pFileEntry->dwCrc32); + + // Update pointer + pbAttrPtr = (LPBYTE)pArrayCRC32; + } + + // Write the array of file time + if(ha->dwAttrFlags & MPQ_ATTRIBUTE_FILETIME) + { + ULONGLONG * pArrayFileTime = (ULONGLONG *)pbAttrPtr; + + // Copy from file table + for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) + *pArrayFileTime++ = BSWAP_INT64_UNSIGNED(pFileEntry->FileTime); + + // Update pointer + pbAttrPtr = (LPBYTE)pArrayFileTime; + } + + // Write the array of MD5s + if(ha->dwAttrFlags & MPQ_ATTRIBUTE_MD5) + { + LPBYTE pbArrayMD5 = pbAttrPtr; + + // Copy from file table + for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) + { + memcpy(pbArrayMD5, pFileEntry->md5, MD5_DIGEST_SIZE); + pbArrayMD5 += MD5_DIGEST_SIZE; + } + + // Update pointer + pbAttrPtr = pbArrayMD5; + } + + // Write the array of patch bits + if(ha->dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT) + { + LPBYTE pbBitArray = pbAttrPtr; + DWORD dwByteIndex = 0; + BYTE dwBitMask = 0x80; + + // Copy from file table + for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) + { + // Set the bit, if needed + if(pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) + pbBitArray[dwByteIndex] |= dwBitMask; + + // Update bit index and bit mask + dwByteIndex += (dwBitMask & 0x01); + dwBitMask = (dwBitMask << 0x07) | (dwBitMask >> 0x01); + } + + // Move past the bit array + pbAttrPtr += (ha->pHeader->dwBlockTableSize + 6) / 8; + } + + // Now we expect that current position matches the estimated size + // Note that if there is 1 extra bit above the byte size, + // the table is actually 1 byte shorter in Blizzard MPQs. See GetSizeOfAttributesFile + assert((size_t)(pbAttrPtr - pbAttrFile) == cbAttrFile); + } + + // Give away the attributes file + if(pcbAttrFile != NULL) + *pcbAttrFile = (DWORD)cbAttrFile; + return pbAttrFile; +} + //----------------------------------------------------------------------------- // Public functions (internal use by StormLib) -int SAttrLoadAttributes(TMPQArchive * ha) +DWORD SAttrLoadAttributes(TMPQArchive * ha) { - MPQ_ATTRIBUTES_HEADER AttrHeader; - TMPQFile * hf; HANDLE hFile = NULL; - DWORD dwBlockTableSize = ha->pHeader->dwBlockTableSize; - DWORD dwArraySize; + LPBYTE pbAttrFile; DWORD dwBytesRead; - DWORD i; - int nError = ERROR_SUCCESS; + DWORD cbAttrFile = 0; + DWORD dwErrCode = ERROR_FILE_CORRUPT; // File table must be initialized assert(ha->pFileTable != NULL); + assert((ha->dwFlags & MPQ_FLAG_BLOCK_TABLE_CUT) == 0); + + // Don't load the attributes file from malformed Warcraft III maps + if(ha->dwFlags & MPQ_FLAG_MALFORMED) + return ERROR_FILE_CORRUPT; // Attempt to open the "(attributes)" file. - // 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; + // Retrieve and check size of the (attributes) file + cbAttrFile = SFileGetFileSize(hFile, NULL); - // Load the content of the attributes file - SFileReadFile(hFile, &AttrHeader, sizeof(MPQ_ATTRIBUTES_HEADER), &dwBytesRead, NULL); - if(dwBytesRead != sizeof(MPQ_ATTRIBUTES_HEADER)) - nError = ERROR_FILE_CORRUPT; - - // Verify the header of the (attributes) file - if(nError == ERROR_SUCCESS) + // Integer overflow check + if((cbAttrFile + 1) > cbAttrFile) { - 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) - { - if(AttrHeader.dwVersion > MPQ_ATTRIBUTES_V1) - nError = ERROR_BAD_FORMAT; - } - - // Load the CRC32 (if any) - if(nError == ERROR_SUCCESS && (AttrHeader.dwFlags & MPQ_ATTRIBUTE_CRC32)) - { - LPDWORD pArrayCRC32 = STORM_ALLOC(DWORD, dwBlockTableSize); - - if(pArrayCRC32 != NULL) + // Size of the (attributes) might be 1 byte less than expected + // See GetSizeOfAttributesFile for more info + pbAttrFile = STORM_ALLOC(BYTE, cbAttrFile + 1); + if(pbAttrFile != NULL) { - dwArraySize = dwBlockTableSize * sizeof(DWORD); - SFileReadFile(hFile, pArrayCRC32, dwArraySize, &dwBytesRead, NULL); - if(dwBytesRead == dwArraySize) - { - for(i = 0; i < dwBlockTableSize; i++) - ha->pFileTable[i].dwCrc32 = BSWAP_INT32_UNSIGNED(pArrayCRC32[i]); - } - else - nError = ERROR_FILE_CORRUPT; + // Set the last byte to 0 in case the size should be 1 byte greater + pbAttrFile[cbAttrFile] = 0; - STORM_FREE(pArrayCRC32); - } - else - nError = ERROR_NOT_ENOUGH_MEMORY; - } + // Load the entire file to memory + if(!SFileReadFile(hFile, pbAttrFile, cbAttrFile, &dwBytesRead, NULL)) + ha->dwFlags |= (SErrGetLastError() == ERROR_FILE_CORRUPT) ? MPQ_FLAG_MALFORMED : 0; - // Read the array of file times - if(nError == ERROR_SUCCESS && (AttrHeader.dwFlags & MPQ_ATTRIBUTE_FILETIME)) - { - ULONGLONG * pArrayFileTime = STORM_ALLOC(ULONGLONG, dwBlockTableSize); + // Parse the (attributes) + if(dwBytesRead == cbAttrFile) + dwErrCode = LoadAttributesFile(ha, pbAttrFile, cbAttrFile); - if(pArrayFileTime != NULL) - { - dwArraySize = dwBlockTableSize * sizeof(ULONGLONG); - SFileReadFile(hFile, pArrayFileTime, dwArraySize, &dwBytesRead, NULL); - if(dwBytesRead == dwArraySize) - { - for(i = 0; i < dwBlockTableSize; i++) - ha->pFileTable[i].FileTime = BSWAP_INT64_UNSIGNED(pArrayFileTime[i]); - } - else - nError = ERROR_FILE_CORRUPT; - - STORM_FREE(pArrayFileTime); - } - else - nError = ERROR_NOT_ENOUGH_MEMORY; - } - - // Read the MD5 (if any) - // 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 = STORM_ALLOC(unsigned char, (dwBlockTableSize * MD5_DIGEST_SIZE)); - unsigned char * md5; - - if(pArrayMD5 != NULL) - { - dwArraySize = dwBlockTableSize * MD5_DIGEST_SIZE; - SFileReadFile(hFile, pArrayMD5, dwArraySize, &dwBytesRead, NULL); - if(dwBytesRead == dwArraySize) - { - md5 = pArrayMD5; - for(i = 0; i < dwBlockTableSize; i++) - { - memcpy(ha->pFileTable[i].md5, md5, MD5_DIGEST_SIZE); - md5 += MD5_DIGEST_SIZE; - } - } - else - nError = ERROR_FILE_CORRUPT; - - STORM_FREE(pArrayMD5); - } - else - nError = ERROR_NOT_ENOUGH_MEMORY; - } - - // 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); + // Free the buffer + STORM_FREE(pbAttrFile); } } - // - // 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. - // If we encounter such table, we will zero all three arrays - // - - if(nError != ERROR_SUCCESS) - ha->dwAttrFlags = 0; - - // Cleanup & exit + // Close the attributes file SFileCloseFile(hFile); } - return nError; + + return dwErrCode; } -int SAttrFileSaveToMpq(TMPQArchive * ha) +// Saves the (attributes) to the MPQ +DWORD SAttrFileSaveToMpq(TMPQArchive * ha) { - MPQ_ATTRIBUTES_HEADER AttrHeader; - TFileEntry * pFileEntry; TMPQFile * hf = NULL; - DWORD dwFinalBlockTableSize = ha->dwFileTableSize; - DWORD dwFileSize = 0; - DWORD dwToWrite; - DWORD i; - int nError = ERROR_SUCCESS; + LPBYTE pbAttrFile; + DWORD cbAttrFile = 0; + DWORD dwErrCode = ERROR_SUCCESS; - // Now we have to check if we need patch bits in the (attributes) - if(nError == ERROR_SUCCESS) + // Only save the attributes if we should do so + if(ha->dwFileFlags2 != 0) { - for(i = 0; i < ha->dwFileTableSize; i++) + // At this point, we expect to have at least one reserved entry in the file table + assert(ha->dwFlags & MPQ_FLAG_ATTRIBUTES_NEW); + assert(ha->dwReservedFiles > 0); + + // Create the raw data that is to be written to (attributes) + // Note: Blizzard MPQs have entries for (listfile) and (attributes), + // but they are filled empty + pbAttrFile = CreateAttributesFile(ha, &cbAttrFile); + if(pbAttrFile != NULL) { - if(ha->pFileTable[i].dwFlags & MPQ_FILE_PATCH_FILE) + // Determine the real flags for (attributes) + if(ha->dwFileFlags2 == MPQ_FILE_DEFAULT_INTERNAL) + ha->dwFileFlags2 = GetDefaultSpecialFileFlags(cbAttrFile, ha->pHeader->wFormatVersion); + + // Create the attributes file in the MPQ + dwErrCode = SFileAddFile_Init(ha, ATTRIBUTES_NAME, + 0, + cbAttrFile, + LANG_NEUTRAL, + ha->dwFileFlags2 | MPQ_FILE_REPLACEEXISTING, + &hf); + + // Write the attributes file raw data to it + if(dwErrCode == ERROR_SUCCESS) { - 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); - if(pFileEntry != NULL) - { - // If "(attributes)" file exists, and it's set to 0, then remove it - if(ha->dwAttrFlags == 0) - { - FreeFileEntry(ha, pFileEntry); - return ERROR_SUCCESS; - } - } - else - { - // If we don't want to create file atributes, do nothing - if(ha->dwAttrFlags == 0) - return ERROR_SUCCESS; - - // Check where the file entry is going to be allocated. - // If at the end of the file table, we have to increment - // the expected size of the (attributes) file. - pFileEntry = FindFreeFileEntry(ha); - if(pFileEntry == ha->pFileTable + ha->dwFileTableSize) - dwFinalBlockTableSize++; - } - - // Calculate the size of the attributes file - if(nError == ERROR_SUCCESS) - { - dwFileSize = sizeof(MPQ_ATTRIBUTES_HEADER); // Header - if(ha->dwAttrFlags & MPQ_ATTRIBUTE_CRC32) - dwFileSize += dwFinalBlockTableSize * sizeof(DWORD); - if(ha->dwAttrFlags & MPQ_ATTRIBUTE_FILETIME) - 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 - nError = SFileAddFile_Init(ha, ATTRIBUTES_NAME, - 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 & MPQ_ATTRIBUTE_ALL)); - dwToWrite = sizeof(MPQ_ATTRIBUTES_HEADER); - nError = SFileAddFile_Write(hf, &AttrHeader, dwToWrite, MPQ_COMPRESSION_ZLIB); - } - - // Write the array of CRC32 - if(nError == ERROR_SUCCESS && (ha->dwAttrFlags & MPQ_ATTRIBUTE_CRC32)) - { - LPDWORD pArrayCRC32 = STORM_ALLOC(DWORD, dwFinalBlockTableSize); - - if(pArrayCRC32 != NULL) - { - // Copy from file table - for(i = 0; i < ha->dwFileTableSize; i++) - pArrayCRC32[i] = BSWAP_INT32_UNSIGNED(ha->pFileTable[i].dwCrc32); - - dwToWrite = ha->dwFileTableSize * sizeof(DWORD); - nError = SFileAddFile_Write(hf, pArrayCRC32, dwToWrite, MPQ_COMPRESSION_ZLIB); - STORM_FREE(pArrayCRC32); - } - } - - // Write the array of file time - if(nError == ERROR_SUCCESS && (ha->dwAttrFlags & MPQ_ATTRIBUTE_FILETIME)) - { - ULONGLONG * pArrayFileTime = STORM_ALLOC(ULONGLONG, ha->dwFileTableSize); - - if(pArrayFileTime != NULL) - { - // Copy from file table - for(i = 0; i < ha->dwFileTableSize; i++) - pArrayFileTime[i] = BSWAP_INT64_UNSIGNED(ha->pFileTable[i].FileTime); - - dwToWrite = ha->dwFileTableSize * sizeof(ULONGLONG); - nError = SFileAddFile_Write(hf, pArrayFileTime, dwToWrite, MPQ_COMPRESSION_ZLIB); - STORM_FREE(pArrayFileTime); - } - } - - // Write the array of MD5s - if(nError == ERROR_SUCCESS && (ha->dwAttrFlags & MPQ_ATTRIBUTE_MD5)) - { - char * pArrayMD5 = STORM_ALLOC(char, ha->dwFileTableSize * MD5_DIGEST_SIZE); - - if(pArrayMD5 != NULL) - { - // Copy from file table - for(i = 0; i < ha->dwFileTableSize; i++) - memcpy(&pArrayMD5[i * MD5_DIGEST_SIZE], ha->pFileTable[i].md5, MD5_DIGEST_SIZE); - - dwToWrite = ha->dwFileTableSize * MD5_DIGEST_SIZE; - nError = SFileAddFile_Write(hf, pArrayMD5, dwToWrite, MPQ_COMPRESSION_ZLIB); - 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; + // Write the content of the attributes file to the MPQ + dwErrCode = SFileAddFile_Write(hf, pbAttrFile, cbAttrFile, MPQ_COMPRESSION_ZLIB); + SFileAddFile_Finish(hf); } - nError = SFileAddFile_Write(hf, pbBitArray, dwByteSize, MPQ_COMPRESSION_ZLIB); - STORM_FREE(pbBitArray); + // Clear the number of reserved files + ha->dwFlags &= ~(MPQ_FLAG_ATTRIBUTES_NEW | MPQ_FLAG_ATTRIBUTES_NONE); + ha->dwReservedFiles--; + + // Free the attributes buffer + STORM_FREE(pbAttrFile); + } + else + { + // If the (attributes) file would be empty, its OK + dwErrCode = (cbAttrFile == 0) ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY; } } - // Finalize the file in the archive - if(hf != NULL) - { - SFileAddFile_Finish(hf); - } - - if(nError == ERROR_SUCCESS) - ha->dwFlags &= ~MPQ_FLAG_INV_ATTRIBUTES; - return nError; + return dwErrCode; } //----------------------------------------------------------------------------- @@ -380,9 +476,9 @@ DWORD WINAPI SFileGetAttributes(HANDLE hMpq) TMPQArchive * ha = (TMPQArchive *)hMpq; // Verify the parameters - if(!IsValidMpqHandle(ha)) + if(!IsValidMpqHandle(hMpq)) { - SetLastError(ERROR_INVALID_PARAMETER); + SErrSetLastError(ERROR_INVALID_PARAMETER); return SFILE_INVALID_ATTRIBUTES; } @@ -394,16 +490,16 @@ bool WINAPI SFileSetAttributes(HANDLE hMpq, DWORD dwFlags) TMPQArchive * ha = (TMPQArchive *)hMpq; // Verify the parameters - if(!IsValidMpqHandle(ha)) + if(!IsValidMpqHandle(hMpq)) { - SetLastError(ERROR_INVALID_PARAMETER); + SErrSetLastError(ERROR_INVALID_PARAMETER); return false; } // Not allowed when the archive is read-only if(ha->dwFlags & MPQ_FLAG_READ_ONLY) { - SetLastError(ERROR_ACCESS_DENIED); + SErrSetLastError(ERROR_ACCESS_DENIED); return false; } @@ -427,24 +523,24 @@ bool WINAPI SFileUpdateFileAttributes(HANDLE hMpq, const char * szFileName) // Verify the parameters if(!IsValidMpqHandle(ha)) { - SetLastError(ERROR_INVALID_PARAMETER); + SErrSetLastError(ERROR_INVALID_PARAMETER); return false; } // Not allowed when the archive is read-only if(ha->dwFlags & MPQ_FLAG_READ_ONLY) { - SetLastError(ERROR_ACCESS_DENIED); + SErrSetLastError(ERROR_ACCESS_DENIED); return false; } // Attempt to open the file - if(!SFileOpenFileEx(hMpq, szFileName, SFILE_OPEN_FROM_MPQ, &hFile)) + if(!SFileOpenFileEx(hMpq, szFileName, SFILE_OPEN_BASE_FILE, &hFile)) return false; // Get the file size hf = (TMPQFile *)hFile; - SFileGetFileInfo(hFile, SFILE_INFO_FILE_SIZE, &dwTotalBytes, sizeof(DWORD)); + dwTotalBytes = hf->pFileEntry->dwFileSize; // Initialize the CRC32 and MD5 contexts md5_init(&md5_state); diff --git a/dep/StormLib/src/SFileCompactArchive.cpp b/dep/StormLib/src/SFileCompactArchive.cpp index 319108b08f..b2f57f4208 100644 --- a/dep/StormLib/src/SFileCompactArchive.cpp +++ b/dep/StormLib/src/SFileCompactArchive.cpp @@ -7,80 +7,94 @@ /* -------- ---- --- ------- */ /* 14.04.03 1.00 Lad Splitted from SFileCreateArchiveEx.cpp */ /* 19.11.03 1.01 Dan Big endian handling */ +/* 21.04.13 1.02 Dea Compact callback now part of TMPQArchive */ /*****************************************************************************/ #define __STORMLIB_SELF__ #include "StormLib.h" #include "StormCommon.h" -/*****************************************************************************/ -/* Local variables */ -/*****************************************************************************/ - -static SFILE_COMPACT_CALLBACK CompactCB = NULL; -static ULONGLONG CompactBytesProcessed = 0; -static ULONGLONG CompactTotalBytes = 0; -static void * pvUserData = NULL; - /*****************************************************************************/ /* Local functions */ /*****************************************************************************/ -static int CheckIfAllFilesKnown(TMPQArchive * ha, const char * szListFile, LPDWORD pFileKeys) +static DWORD CheckIfAllFilesKnown(TMPQArchive * ha) { - TFileEntry * pFileTableEnd; + TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; TFileEntry * pFileEntry; DWORD dwBlockIndex = 0; - int nError = ERROR_SUCCESS; - - // Add the listfile to the MPQ - if(nError == ERROR_SUCCESS && szListFile != NULL) - { - // Notify the user - if(CompactCB != NULL) - CompactCB(pvUserData, CCB_CHECKING_FILES, CompactBytesProcessed, CompactTotalBytes); - - nError = SFileAddListFile((HANDLE)ha, szListFile); - } + DWORD dwErrCode = ERROR_SUCCESS; // Verify the file table - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { - pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++, dwBlockIndex++) { + // If there is an existing entry in the file table, check its name if(pFileEntry->dwFlags & MPQ_FILE_EXISTS) { - if(pFileEntry->szFileName != NULL && !IsPseudoFileName(pFileEntry->szFileName, NULL)) + // The name must be valid and must not be a pseudo-name + if(pFileEntry->szFileName == NULL || IsPseudoFileName(pFileEntry->szFileName, NULL)) { - DWORD dwFileKey = 0; - - // Resolve the file key. Use plain file name for it - if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) - { - dwFileKey = DecryptFileKey(pFileEntry->szFileName, - pFileEntry->ByteOffset, - pFileEntry->dwFileSize, - pFileEntry->dwFlags); - } - - // Give the key to the caller - if(pFileKeys != NULL) - pFileKeys[dwBlockIndex] = dwFileKey; - } - else - { - nError = ERROR_CAN_NOT_COMPLETE; + dwErrCode = ERROR_UNKNOWN_FILE_NAMES; break; } } } } - return nError; + return dwErrCode; } -static int CopyNonMpqData( +static DWORD CheckIfAllKeysKnown(TMPQArchive * ha, const TCHAR * szListFile, LPDWORD pFileKeys) +{ + TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; + TFileEntry * pFileEntry; + DWORD dwBlockIndex = 0; + DWORD dwErrCode = ERROR_SUCCESS; + + // Add the listfile to the MPQ + if(szListFile != NULL) + { + // Notify the user + if(ha->pfnCompactCB != NULL) + ha->pfnCompactCB(ha->pvCompactUserData, CCB_CHECKING_FILES, ha->CompactBytesProcessed, ha->CompactTotalBytes); + + dwErrCode = SFileAddListFile((HANDLE)ha, szListFile); + } + + // Verify the file table + if(dwErrCode == ERROR_SUCCESS) + { + for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++, dwBlockIndex++) + { + // If the file exists and it's encrypted + if(pFileEntry->dwFlags & MPQ_FILE_EXISTS) + { + // If we know the name, we decrypt the file key from the file name + if(pFileEntry->szFileName != NULL && !IsPseudoFileName(pFileEntry->szFileName, NULL)) + { + // Give the key to the caller + pFileKeys[dwBlockIndex] = DecryptFileKey(pFileEntry->szFileName, + pFileEntry->ByteOffset, + pFileEntry->dwFileSize, + pFileEntry->dwFlags); + continue; + } + + // We don't know the encryption key of this file, + // thus we cannot compact the file + dwErrCode = ERROR_UNKNOWN_FILE_NAMES; + break; + } + } + } + + return dwErrCode; +} + +static DWORD CopyNonMpqData( + TMPQArchive * ha, TFileStream * pSrcStream, TFileStream * pTrgStream, ULONGLONG & ByteOffset, @@ -89,7 +103,7 @@ static int CopyNonMpqData( ULONGLONG DataSize = ByteCount; DWORD dwToRead; char DataBuffer[0x1000]; - int nError = ERROR_SUCCESS; + DWORD dwErrCode = ERROR_SUCCESS; // Copy the data while(DataSize > 0) @@ -102,22 +116,22 @@ static int CopyNonMpqData( // Read from the source stream if(!FileStream_Read(pSrcStream, &ByteOffset, DataBuffer, dwToRead)) { - nError = GetLastError(); + dwErrCode = SErrGetLastError(); break; } // Write to the target stream if(!FileStream_Write(pTrgStream, NULL, DataBuffer, dwToRead)) { - nError = GetLastError(); + dwErrCode = SErrGetLastError(); break; } // Update the progress - if(CompactCB != NULL) + if(ha->pfnCompactCB != NULL) { - CompactBytesProcessed += dwToRead; - CompactCB(pvUserData, CCB_COPYING_NON_MPQ_DATA, CompactBytesProcessed, CompactTotalBytes); + ha->CompactBytesProcessed += dwToRead; + ha->pfnCompactCB(ha->pvCompactUserData, CCB_COPYING_NON_MPQ_DATA, ha->CompactBytesProcessed, ha->CompactTotalBytes); } // Decrement the number of data to be copied @@ -125,35 +139,31 @@ static int CopyNonMpqData( DataSize -= dwToRead; } - return ERROR_SUCCESS; + return dwErrCode; } // Copies all file sectors into another archive. -static int CopyMpqFileSectors( +static DWORD CopyMpqFileSectors( TMPQArchive * ha, TMPQFile * hf, - TFileStream * pNewStream) + TFileStream * pNewStream, + ULONGLONG MpqFilePos) // MPQ file position in the new archive { TFileEntry * pFileEntry = hf->pFileEntry; ULONGLONG RawFilePos; // Used for calculating sector offset in the old MPQ archive - ULONGLONG MpqFilePos; // MPQ file position in the new archive DWORD dwBytesToCopy = pFileEntry->dwCmpSize; DWORD dwPatchSize = 0; // Size of patch header DWORD dwFileKey1 = 0; // File key used for decryption DWORD dwFileKey2 = 0; // File key used for encryption DWORD dwCmpSize = 0; // Compressed file size, including patch header - int nError = ERROR_SUCCESS; + DWORD dwErrCode = ERROR_SUCCESS; - // Remember the position in the destination file - 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)) + if(dwErrCode == ERROR_SUCCESS && (pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)) { dwFileKey2 = dwFileKey1 = hf->dwFileKey; - if(pFileEntry->dwFlags & MPQ_FILE_FIX_KEY) + if(pFileEntry->dwFlags & MPQ_FILE_KEY_V2) { dwFileKey2 = (dwFileKey1 ^ pFileEntry->dwFileSize) - (DWORD)pFileEntry->ByteOffset; dwFileKey2 = (dwFileKey2 + (DWORD)MpqFilePos) ^ pFileEntry->dwFileSize; @@ -161,30 +171,30 @@ static int CopyMpqFileSectors( } // If we have to save patch header, do it - if(nError == ERROR_SUCCESS && hf->pPatchInfo != NULL) + if(dwErrCode == ERROR_SUCCESS && hf->pPatchInfo != NULL) { BSWAP_ARRAY32_UNSIGNED(hf->pPatchInfo, sizeof(DWORD) * 3); if(!FileStream_Write(pNewStream, NULL, hf->pPatchInfo, hf->pPatchInfo->dwLength)) - nError = GetLastError(); + dwErrCode = SErrGetLastError(); // Save the size of the patch info dwPatchSize = hf->pPatchInfo->dwLength; } // If we have to save sector offset table, do it. - if(nError == ERROR_SUCCESS && hf->SectorOffsets != NULL) + if(dwErrCode == ERROR_SUCCESS && hf->SectorOffsets != NULL) { - DWORD * SectorOffsetsCopy = (DWORD *)STORM_ALLOC(BYTE, hf->SectorOffsets[0]); + DWORD * SectorOffsetsCopy = STORM_ALLOC(DWORD, hf->SectorOffsets[0] / sizeof(DWORD)); DWORD dwSectorOffsLen = hf->SectorOffsets[0]; assert((pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT) == 0); - assert(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED); + assert(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK); if(SectorOffsetsCopy == NULL) - nError = ERROR_NOT_ENOUGH_MEMORY; + dwErrCode = ERROR_NOT_ENOUGH_MEMORY; // Encrypt the secondary sector offset table and write it to the target file - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { memcpy(SectorOffsetsCopy, hf->SectorOffsets, dwSectorOffsLen); if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) @@ -192,17 +202,17 @@ static int CopyMpqFileSectors( BSWAP_ARRAY32_UNSIGNED(SectorOffsetsCopy, dwSectorOffsLen); if(!FileStream_Write(pNewStream, NULL, SectorOffsetsCopy, dwSectorOffsLen)) - nError = GetLastError(); + dwErrCode = SErrGetLastError(); dwBytesToCopy -= dwSectorOffsLen; dwCmpSize += dwSectorOffsLen; } // Update compact progress - if(CompactCB != NULL) + if(ha->pfnCompactCB != NULL) { - CompactBytesProcessed += dwSectorOffsLen; - CompactCB(pvUserData, CCB_COMPACTING_FILES, CompactBytesProcessed, CompactTotalBytes); + ha->CompactBytesProcessed += dwSectorOffsLen; + ha->pfnCompactCB(ha->pvCompactUserData, CCB_COMPACTING_FILES, ha->CompactBytesProcessed, ha->CompactTotalBytes); } STORM_FREE(SectorOffsetsCopy); @@ -210,7 +220,7 @@ static int CopyMpqFileSectors( // Now we have to copy all file sectors. We do it without // recompression, because recompression is not necessary in this case - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { for(DWORD dwSector = 0; dwSector < hf->dwSectorCount; dwSector++) { @@ -229,17 +239,17 @@ static int CopyMpqFileSectors( dwRawDataInSector = dwBytesToCopy; // Calculate the raw file offset of the file sector - CalculateRawSectorOffset(RawFilePos, hf, dwRawByteOffset); - + RawFilePos = CalculateRawSectorOffset(hf, dwRawByteOffset); + // Read the file sector if(!FileStream_Read(ha->pStream, &RawFilePos, hf->pbFileSector, dwRawDataInSector)) { - nError = GetLastError(); + dwErrCode = SErrGetLastError(); break; } // 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) { @@ -252,15 +262,15 @@ static int CopyMpqFileSectors( // Now write the sector back to the file if(!FileStream_Write(pNewStream, NULL, hf->pbFileSector, dwRawDataInSector)) { - nError = GetLastError(); + dwErrCode = SErrGetLastError(); break; } // Update compact progress - if(CompactCB != NULL) + if(ha->pfnCompactCB != NULL) { - CompactBytesProcessed += dwRawDataInSector; - CompactCB(pvUserData, CCB_COMPACTING_FILES, CompactBytesProcessed, CompactTotalBytes); + ha->CompactBytesProcessed += dwRawDataInSector; + ha->pfnCompactCB(ha->pvCompactUserData, CCB_COMPACTING_FILES, ha->CompactBytesProcessed, ha->CompactTotalBytes); } // Adjust byte counts @@ -271,7 +281,7 @@ static int CopyMpqFileSectors( // Copy the sector CRCs, if any // Sector CRCs are always compressed (not imploded) and unencrypted - if(nError == ERROR_SUCCESS && hf->SectorOffsets != NULL && hf->SectorChksums != NULL) + if(dwErrCode == ERROR_SUCCESS && hf->SectorOffsets != NULL && hf->SectorChksums != NULL) { DWORD dwCrcLength; @@ -279,16 +289,16 @@ static int CopyMpqFileSectors( if(dwCrcLength != 0) { if(!FileStream_Read(ha->pStream, NULL, hf->SectorChksums, dwCrcLength)) - nError = GetLastError(); + dwErrCode = SErrGetLastError(); if(!FileStream_Write(pNewStream, NULL, hf->SectorChksums, dwCrcLength)) - nError = GetLastError(); + dwErrCode = SErrGetLastError(); // Update compact progress - if(CompactCB != NULL) + if(ha->pfnCompactCB != NULL) { - CompactBytesProcessed += dwCrcLength; - CompactCB(pvUserData, CCB_COMPACTING_FILES, CompactBytesProcessed, CompactTotalBytes); + ha->CompactBytesProcessed += dwCrcLength; + ha->pfnCompactCB(ha->pvCompactUserData, CCB_COMPACTING_FILES, ha->CompactBytesProcessed, ha->CompactTotalBytes); } // Size of the CRC block is also included in the compressed file size @@ -302,7 +312,7 @@ static int CopyMpqFileSectors( // 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) + if(dwErrCode == ERROR_SUCCESS && dwBytesToCopy != 0) { LPBYTE pbExtraData; @@ -311,293 +321,135 @@ static int CopyMpqFileSectors( if(pbExtraData != NULL) { if(!FileStream_Read(ha->pStream, NULL, pbExtraData, dwBytesToCopy)) - nError = GetLastError(); + dwErrCode = SErrGetLastError(); if(!FileStream_Write(pNewStream, NULL, pbExtraData, dwBytesToCopy)) - nError = GetLastError(); + dwErrCode = SErrGetLastError(); // Include these extra data in the compressed size dwCmpSize += dwBytesToCopy; - dwBytesToCopy = 0; STORM_FREE(pbExtraData); } else - nError = ERROR_NOT_ENOUGH_MEMORY; + dwErrCode = ERROR_NOT_ENOUGH_MEMORY; } // Write the MD5's of the raw file data, if needed - if(nError == ERROR_SUCCESS && ha->pHeader->dwRawChunkSize != 0) + if(dwErrCode == ERROR_SUCCESS && ha->pHeader->dwRawChunkSize != 0) { - nError = WriteMpqDataMD5(pNewStream, + dwErrCode = WriteMpqDataMD5(pNewStream, ha->MpqPos + MpqFilePos, pFileEntry->dwCmpSize, ha->pHeader->dwRawChunkSize); } - // Update file position in the block table - if(nError == ERROR_SUCCESS) + // Verify the number of bytes written + if(dwErrCode == ERROR_SUCCESS) { // 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, ...) - // + // there's something wrong (an unknown archive version, MPQ malformation, ...) + // // 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 // uncompressed size of file data + size of the sector table. // If we compact the archive, Diablo will refuse to load the game - // Seems like some sort of protection to me. // // Note: Some patch files in WOW patches don't count the patch header // into compressed size // - if(dwCmpSize <= pFileEntry->dwCmpSize && pFileEntry->dwCmpSize <= dwCmpSize + dwPatchSize) + if(!(dwCmpSize <= pFileEntry->dwCmpSize && pFileEntry->dwCmpSize <= dwCmpSize + dwPatchSize)) { - // Note: DO NOT update the compressed size in the file entry, no matter how bad it is. - pFileEntry->ByteOffset = MpqFilePos; - } - else - { - nError = ERROR_FILE_CORRUPT; + dwErrCode = ERROR_FILE_CORRUPT; assert(false); } } - return nError; + return dwErrCode; } -static int CopyMpqFiles(TMPQArchive * ha, LPDWORD pFileKeys, TFileStream * pNewStream) +static DWORD CopyMpqFiles(TMPQArchive * ha, LPDWORD pFileKeys, TFileStream * pNewStream) { TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; TFileEntry * pFileEntry; TMPQFile * hf = NULL; - int nError = ERROR_SUCCESS; + ULONGLONG MpqFilePos; + DWORD dwErrCode = ERROR_SUCCESS; // Walk through all files and write them to the destination MPQ archive for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) { // Copy all the file sectors // Only do that when the file has nonzero size - if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) && pFileEntry->dwFileSize != 0) + if((pFileEntry->dwFlags & MPQ_FILE_EXISTS)) { - // Allocate structure for the MPQ file - hf = CreateMpqFile(ha); - if(hf == NULL) - return ERROR_NOT_ENOUGH_MEMORY; + // Query the position where the destination file will be + FileStream_GetPos(pNewStream, &MpqFilePos); + MpqFilePos = MpqFilePos - ha->MpqPos; - // Store file entry - hf->pFileEntry = pFileEntry; - - // Set the raw file position - hf->MpqFilePos = pFileEntry->ByteOffset; - hf->RawFilePos = ha->MpqPos + hf->MpqFilePos; - - // Set the file decryption key - hf->dwFileKey = pFileKeys[pFileEntry - ha->pFileTable]; - hf->dwDataSize = pFileEntry->dwFileSize; - - // If the file is a patch file, load the patch header - if(pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) + // Perform file copy ONLY if the file has nonzero size + if(pFileEntry->dwFileSize != 0) { - nError = AllocatePatchInfo(hf, true); - if(nError != ERROR_SUCCESS) + // Allocate structure for the MPQ file + hf = CreateFileHandle(ha, pFileEntry); + if(hf == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + + // Set the file decryption key + hf->dwFileKey = pFileKeys[pFileEntry - ha->pFileTable]; + + // If the file is a patch file, load the patch header + if(pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) + { + dwErrCode = AllocatePatchInfo(hf, true); + if(dwErrCode != ERROR_SUCCESS) + break; + } + + // Allocate buffers for file sector and sector offset table + dwErrCode = AllocateSectorBuffer(hf); + if(dwErrCode != ERROR_SUCCESS) break; + + // Also allocate sector offset table and sector checksum table + dwErrCode = AllocateSectorOffsets(hf, true); + if(dwErrCode != ERROR_SUCCESS) + break; + + // Also load sector checksums, if any + if(pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC) + { + dwErrCode = AllocateSectorChecksums(hf, false); + if(dwErrCode != ERROR_SUCCESS) + break; + } + + // Copy all file sectors + dwErrCode = CopyMpqFileSectors(ha, hf, pNewStream, MpqFilePos); + if(dwErrCode != ERROR_SUCCESS) + break; + + // Free buffers. This also sets "hf" to NULL. + FreeFileHandle(hf); } - // Allocate buffers for file sector and sector offset table - nError = AllocateSectorBuffer(hf); - if(nError != ERROR_SUCCESS) - break; - - // Also allocate sector offset table and sector checksum table - nError = AllocateSectorOffsets(hf, true); - if(nError != ERROR_SUCCESS) - break; - - // Also load sector checksums, if any - if(pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC) - { - nError = AllocateSectorChecksums(hf, false); - if(nError != ERROR_SUCCESS) - break; - } - - // Copy all file sectors - nError = CopyMpqFileSectors(ha, hf, pNewStream); - if(nError != ERROR_SUCCESS) - break; - - // Free buffers. This also sets "hf" to NULL. - FreeMPQFile(hf); + // Note: DO NOT update the compressed size in the file entry, no matter how bad it is. + pFileEntry->ByteOffset = MpqFilePos; } } // Cleanup and exit if(hf != NULL) - FreeMPQFile(hf); - return nError; + FreeFileHandle(hf); + return dwErrCode; } - /*****************************************************************************/ /* Public functions */ /*****************************************************************************/ -bool WINAPI SFileSetCompactCallback(HANDLE /* hMpq */, SFILE_COMPACT_CALLBACK aCompactCB, void * pvData) -{ - CompactCB = aCompactCB; - pvUserData = pvData; - return true; -} - -//----------------------------------------------------------------------------- -// Archive compacting - -bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bReserved */) -{ - TFileStream * pTempStream = NULL; - TMPQArchive * ha = (TMPQArchive *)hMpq; - ULONGLONG ByteOffset; - ULONGLONG ByteCount; - LPDWORD pFileKeys = NULL; - TCHAR szTempFile[MAX_PATH] = _T(""); - TCHAR * szTemp = NULL; - int nError = ERROR_SUCCESS; - - // Test the valid parameters - if(!IsValidMpqHandle(ha)) - nError = ERROR_INVALID_HANDLE; - if(ha->dwFlags & MPQ_FLAG_READ_ONLY) - nError = ERROR_ACCESS_DENIED; - - // If the MPQ is changed at this moment, we have to flush the archive - if(nError == ERROR_SUCCESS && (ha->dwFlags & MPQ_FLAG_CHANGED)) - { - SFileFlushArchive(hMpq); - } - - // Create the table with file keys - if(nError == ERROR_SUCCESS) - { - if((pFileKeys = STORM_ALLOC(DWORD, ha->dwFileTableSize)) != NULL) - memset(pFileKeys, 0, sizeof(DWORD) * ha->dwFileTableSize); - else - nError = ERROR_NOT_ENOUGH_MEMORY; - } - - // First of all, we have to check of we are able to decrypt all files. - // If not, sorry, but the archive cannot be compacted. - if(nError == ERROR_SUCCESS) - { - // Initialize the progress variables for compact callback - FileStream_GetSize(ha->pStream, CompactTotalBytes); - CompactBytesProcessed = 0; - nError = CheckIfAllFilesKnown(ha, szListFile, pFileKeys); - } - - // Get the temporary file name and create it - if(nError == ERROR_SUCCESS) - { - _tcscpy(szTempFile, FileStream_GetFileName(ha->pStream)); - if((szTemp = _tcsrchr(szTempFile, '.')) != NULL) - _tcscpy(szTemp + 1, _T("mp_")); - else - _tcscat(szTempFile, _T("_")); - - pTempStream = FileStream_CreateFile(szTempFile, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); - if(pTempStream == NULL) - nError = GetLastError(); - } - - // Write the data before MPQ user data (if any) - if(nError == ERROR_SUCCESS && ha->UserDataPos != 0) - { - // Inform the application about the progress - if(CompactCB != NULL) - CompactCB(pvUserData, CCB_COPYING_NON_MPQ_DATA, CompactBytesProcessed, CompactTotalBytes); - - ByteOffset = 0; - ByteCount = ha->UserDataPos; - nError = CopyNonMpqData(ha->pStream, pTempStream, ByteOffset, ByteCount); - } - - // Write the MPQ user data (if any) - if(nError == ERROR_SUCCESS && ha->MpqPos > ha->UserDataPos) - { - // At this point, we assume that the user data size is equal - // to pUserData->dwHeaderOffs. - // If this assumption doesn't work, then we have an unknown version of MPQ - ByteOffset = ha->UserDataPos; - ByteCount = ha->MpqPos - ha->UserDataPos; - - assert(ha->pUserData != NULL); - assert(ha->pUserData->dwHeaderOffs == ByteCount); - nError = CopyNonMpqData(ha->pStream, pTempStream, ByteOffset, ByteCount); - } - - // Write the MPQ header - if(nError == ERROR_SUCCESS) - { - // Remember the header size before swapping - DWORD dwBytesToWrite = ha->pHeader->dwHeaderSize; - - BSWAP_TMPQHEADER(ha->pHeader); - if(!FileStream_Write(pTempStream, NULL, ha->pHeader, dwBytesToWrite)) - nError = GetLastError(); - BSWAP_TMPQHEADER(ha->pHeader); - - // Update the progress - CompactBytesProcessed += ha->pHeader->dwHeaderSize; - } - - // Now copy all files - if(nError == ERROR_SUCCESS) - { - nError = CopyMpqFiles(ha, pFileKeys, pTempStream); - ha->dwFlags |= MPQ_FLAG_CHANGED; - } - - // If succeeded, switch the streams - if(nError == ERROR_SUCCESS) - { - if(FileStream_Switch(ha->pStream, pTempStream)) - pTempStream = NULL; - else - nError = ERROR_CAN_NOT_COMPLETE; - } - - // If all succeeded, save the MPQ tables - if(nError == ERROR_SUCCESS) - { - // - // 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) - { - CompactBytesProcessed += (ha->pHeader->dwHashTableSize * sizeof(TMPQHash)); - CompactBytesProcessed += (ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock)); - CompactCB(pvUserData, CCB_CLOSING_ARCHIVE, CompactBytesProcessed, CompactTotalBytes); - } - } - - // Invalidate the compact callback - pvUserData = NULL; - CompactCB = NULL; - - // Cleanup and return - if(pTempStream != NULL) - FileStream_Close(pTempStream); - if(pFileKeys != NULL) - STORM_FREE(pFileKeys); - if(nError != ERROR_SUCCESS) - SetLastError(nError); - return (nError == ERROR_SUCCESS); -} - //----------------------------------------------------------------------------- // Changing hash table size @@ -610,156 +462,200 @@ DWORD WINAPI SFileGetMaxFileCount(HANDLE hMpq) 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 dwOldFileTableSize = 0; - int nError = ERROR_SUCCESS; + TMPQArchive * ha; + DWORD dwErrCode = ERROR_SUCCESS; + + // Calculate the hash table size for the new file limit + DWORD dwNewHashTableSize = GetNearestPowerOfTwo(dwMaxFileCount); // Test the valid parameters - if(!IsValidMpqHandle(ha)) - nError = ERROR_INVALID_HANDLE; + if((ha = IsValidMpqHandle(hMpq)) == NULL) + dwErrCode = ERROR_INVALID_HANDLE; if(ha->dwFlags & MPQ_FLAG_READ_ONLY) - nError = ERROR_ACCESS_DENIED; + dwErrCode = ERROR_ACCESS_DENIED; + if(dwNewHashTableSize < ha->dwFileTableSize) + dwErrCode = ERROR_DISK_FULL; - // 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; + // Are there some files open? + if(ha->dwFileCount) + dwErrCode = ERROR_ACCESS_DENIED; - // ALL file names must be known in order to be able - // to rebuild hash table size - if(nError == ERROR_SUCCESS) + // ALL file names must be known in order to be able to rebuild hash table + if(dwErrCode == ERROR_SUCCESS && ha->pHashTable != NULL) { - 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) - { - // Save parameters for the current hash table - dwOldHashTableSize = ha->pHeader->dwHashTableSize; - pOldHashTable = ha->pHashTable; - - // Allocate new hash table - ha->pHeader->dwHashTableSize = GetHashTableSizeForFileCount(dwMaxFileCount); - ha->pHashTable = STORM_ALLOC(TMPQHash, ha->pHeader->dwHashTableSize); - if(ha->pHashTable != NULL) - memset(ha->pHashTable, 0xFF, ha->pHeader->dwHashTableSize * sizeof(TMPQHash)); - else - nError = ERROR_NOT_ENOUGH_MEMORY; - } - - // If the MPQ has HET table, allocate new one as well - if(nError == ERROR_SUCCESS && ha->pHetTable != NULL) - { - // Save the original HET table - pOldHetTable = ha->pHetTable; - - // Create new one - ha->pHetTable = CreateHetTable(dwMaxFileCount, 0x40, true); - if(ha->pHetTable == NULL) - nError = ERROR_NOT_ENOUGH_MEMORY; - } - - // Now reallocate the file table - if(nError == ERROR_SUCCESS) - { - // Save the current file table - dwOldFileTableSize = ha->dwFileTableSize; - pOldFileTable = ha->pFileTable; - - // Create new one - ha->pFileTable = STORM_ALLOC(TFileEntry, dwMaxFileCount); - if(ha->pFileTable != NULL) - memset(ha->pFileTable, 0, dwMaxFileCount * sizeof(TFileEntry)); - else - nError = ERROR_NOT_ENOUGH_MEMORY; - } - - // Now we have to build both classic hash table and HET table. - if(nError == ERROR_SUCCESS) - { - DWORD dwFileIndex = 0; - DWORD dwHashIndex = 0; - - // Create new hash and HET entry for each file - pFileEntry = ha->pFileTable; - for(pOldFileEntry = pOldFileTable; pOldFileEntry < pOldFileTableEnd; pOldFileEntry++) + dwErrCode = CheckIfAllFilesKnown(ha); + if(dwErrCode == ERROR_SUCCESS) { - if(pOldFileEntry->dwFlags & MPQ_FILE_EXISTS) - { - // 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) - { - dwHashIndex = AllocateHashEntry(ha, pFileEntry); - if(dwHashIndex == HASH_ENTRY_FREE) - { - nError = ERROR_CAN_NOT_COMPLETE; - break; - } - } - - // Create new entry in the HET table, if needed - if(ha->pHetTable != NULL) - { - dwHashIndex = AllocateHetEntry(ha, pFileEntry); - if(dwHashIndex == HASH_ENTRY_FREE) - { - nError = ERROR_CAN_NOT_COMPLETE; - break; - } - } - - // Move to the next file entry in the new table - pFileEntry++; - dwFileIndex++; - } + // Rebuild both file tables + dwErrCode = RebuildFileTable(ha, dwNewHashTableSize); } } - // Mark the archive as changed - // Note: We always have to rebuild the (attributes) file due to file table change - if(nError == ERROR_SUCCESS) + // We always have to rebuild the (attributes) file due to file table change + if(dwErrCode == ERROR_SUCCESS) { - ha->dwMaxFileCount = dwMaxFileCount; + // Invalidate (listfile) and (attributes) InvalidateInternalFiles(ha); - } - else - { - // Revert the hash table - if(ha->pHashTable != NULL && pOldHashTable != NULL) - { - STORM_FREE(ha->pHashTable); - ha->pHeader->dwHashTableSize = dwOldHashTableSize; - ha->pHashTable = pOldHashTable; - } - // Revert the HET table - if(ha->pHetTable != NULL && pOldHetTable != NULL) - { - FreeHetTable(ha->pHetTable); - ha->pHetTable = pOldHetTable; - } - - // Revert the file table - if(pOldFileTable != NULL) - { - STORM_FREE(ha->pFileTable); - ha->pFileTable = pOldFileTable; - } - - SetLastError(nError); + // Rebuild the HET table, if we have any + if(ha->pHetTable != NULL) + dwErrCode = RebuildHetTable(ha); } - // Return the result - return (nError == ERROR_SUCCESS); + // Return the error + if(dwErrCode != ERROR_SUCCESS) + SErrSetLastError(dwErrCode); + return (dwErrCode == ERROR_SUCCESS); +} + +//----------------------------------------------------------------------------- +// Archive compacting + +bool WINAPI SFileSetCompactCallback(HANDLE hMpq, SFILE_COMPACT_CALLBACK pfnCompactCB, void * pvUserData) +{ + TMPQArchive * ha = (TMPQArchive *) hMpq; + + if (!IsValidMpqHandle(hMpq)) + { + SErrSetLastError(ERROR_INVALID_HANDLE); + return false; + } + + ha->pfnCompactCB = pfnCompactCB; + ha->pvCompactUserData = pvUserData; + return true; +} + +bool WINAPI SFileCompactArchive(HANDLE hMpq, const TCHAR * szListFile, bool /* bReserved */) +{ + TFileStream * pTempStream = NULL; + TMPQArchive * ha = (TMPQArchive *)hMpq; + ULONGLONG ByteOffset; + ULONGLONG ByteCount; + LPDWORD pFileKeys = NULL; + TCHAR szTempFile[MAX_PATH+1] = _T(""); + DWORD dwErrCode = ERROR_SUCCESS; + + // Test the valid parameters + if(!IsValidMpqHandle(hMpq)) + dwErrCode = ERROR_INVALID_HANDLE; + if(ha->dwFlags & MPQ_FLAG_READ_ONLY) + dwErrCode = ERROR_ACCESS_DENIED; + + // Are there some files open? + if(ha->dwFileCount) + dwErrCode = ERROR_ACCESS_DENIED; + + // If the MPQ is changed at this moment, we have to flush the archive + if(dwErrCode == ERROR_SUCCESS && (ha->dwFlags & MPQ_FLAG_CHANGED)) + { + SFileFlushArchive(hMpq); + } + + // Create the table with file keys + if(dwErrCode == ERROR_SUCCESS) + { + if((pFileKeys = STORM_ALLOC(DWORD, ha->dwFileTableSize)) != NULL) + memset(pFileKeys, 0, sizeof(DWORD) * ha->dwFileTableSize); + else + dwErrCode = ERROR_NOT_ENOUGH_MEMORY; + } + + // First of all, we have to check of we are able to decrypt all files. + // If not, sorry, but the archive cannot be compacted. + if(dwErrCode == ERROR_SUCCESS) + { + // Initialize the progress variables for compact callback + FileStream_GetSize(ha->pStream, &(ha->CompactTotalBytes)); + ha->CompactBytesProcessed = 0; + dwErrCode = CheckIfAllKeysKnown(ha, szListFile, pFileKeys); + } + + // Get the temporary file name and create it + if(dwErrCode == ERROR_SUCCESS) + { + // Create temporary file name. Prevent buffer overflow + StringCopy(szTempFile, _countof(szTempFile), FileStream_GetFileName(ha->pStream)); + StringCat(szTempFile, _countof(szTempFile), _T(".tmp")); + + // Create temporary file + pTempStream = FileStream_CreateFile(szTempFile, STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE); + if(pTempStream == NULL) + dwErrCode = SErrGetLastError(); + } + + // Write the data before MPQ user data (if any) + if(dwErrCode == ERROR_SUCCESS && ha->UserDataPos != 0) + { + // Inform the application about the progress + if(ha->pfnCompactCB != NULL) + ha->pfnCompactCB(ha->pvCompactUserData, CCB_COPYING_NON_MPQ_DATA, ha->CompactBytesProcessed, ha->CompactTotalBytes); + + ByteOffset = 0; + ByteCount = ha->UserDataPos; + dwErrCode = CopyNonMpqData(ha, ha->pStream, pTempStream, ByteOffset, ByteCount); + } + + // Write the MPQ user data (if any) + if(dwErrCode == ERROR_SUCCESS && ha->MpqPos > ha->UserDataPos) + { + // At this point, we assume that the user data size is equal + // to pUserData->dwHeaderOffs. + // If this assumption doesn't work, then we have an unknown version of MPQ + ByteOffset = ha->UserDataPos; + ByteCount = ha->MpqPos - ha->UserDataPos; + + assert(ha->pUserData != NULL); + assert(ha->pUserData->dwHeaderOffs == ByteCount); + dwErrCode = CopyNonMpqData(ha, ha->pStream, pTempStream, ByteOffset, ByteCount); + } + + // Write the MPQ header + if(dwErrCode == ERROR_SUCCESS) + { + TMPQHeader SaveMpqHeader; + + // Write the MPQ header to the file + memcpy(&SaveMpqHeader, ha->pHeader, ha->pHeader->dwHeaderSize); + BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_1); + BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_2); + BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_3); + BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_4); + if(!FileStream_Write(pTempStream, NULL, &SaveMpqHeader, ha->pHeader->dwHeaderSize)) + dwErrCode = SErrGetLastError(); + + // Update the progress + ha->CompactBytesProcessed += ha->pHeader->dwHeaderSize; + } + + // Now copy all files + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = CopyMpqFiles(ha, pFileKeys, pTempStream); + + // If succeeded, switch the streams + if(dwErrCode == ERROR_SUCCESS) + { + ha->dwFlags |= MPQ_FLAG_CHANGED; + if(FileStream_Replace(ha->pStream, pTempStream)) + pTempStream = NULL; + else + dwErrCode = ERROR_CAN_NOT_COMPLETE; + } + + // Final user notification + if(dwErrCode == ERROR_SUCCESS && ha->pfnCompactCB != NULL) + { + ha->CompactBytesProcessed += (ha->pHeader->dwHashTableSize * sizeof(TMPQHash)); + ha->CompactBytesProcessed += (ha->dwFileTableSize * sizeof(TMPQBlock)); + ha->pfnCompactCB(ha->pvCompactUserData, CCB_CLOSING_ARCHIVE, ha->CompactBytesProcessed, ha->CompactTotalBytes); + } + + // Cleanup and return + if(pTempStream != NULL) + FileStream_Close(pTempStream); + if(pFileKeys != NULL) + STORM_FREE(pFileKeys); + if(dwErrCode != ERROR_SUCCESS) + SErrSetLastError(dwErrCode); + return (dwErrCode == ERROR_SUCCESS); } diff --git a/dep/StormLib/src/SFileCreateArchive.cpp b/dep/StormLib/src/SFileCreateArchive.cpp index 59f3d3fa9e..13d57137ca 100644 --- a/dep/StormLib/src/SFileCreateArchive.cpp +++ b/dep/StormLib/src/SFileCreateArchive.cpp @@ -27,6 +27,13 @@ static const DWORD MpqHeaderSizes[] = //----------------------------------------------------------------------------- // Local functions +static DWORD GetValidFileFlags(DWORD dwMpqVersion) +{ + if(dwMpqVersion > MPQ_FORMAT_VERSION_1) + return MPQ_FILE_VALID_FLAGS; + return MPQ_FILE_VALID_FLAGS_W3X; +} + static USHORT GetSectorSizeShift(DWORD dwSectorSize) { USHORT wSectorSizeShift = 0; @@ -40,12 +47,12 @@ static USHORT GetSectorSizeShift(DWORD dwSectorSize) return wSectorSizeShift; } -static int WriteNakedMPQHeader(TMPQArchive * ha) +static DWORD WriteNakedMPQHeader(TMPQArchive * ha) { TMPQHeader * pHeader = ha->pHeader; TMPQHeader Header; DWORD dwBytesToWrite = pHeader->dwHeaderSize; - int nError = ERROR_SUCCESS; + DWORD dwErrCode = ERROR_SUCCESS; // Prepare the naked MPQ header memset(&Header, 0, sizeof(TMPQHeader)); @@ -56,29 +63,45 @@ static int WriteNakedMPQHeader(TMPQArchive * ha) Header.wSectorSize = pHeader->wSectorSize; // Write it to the file - BSWAP_TMPQHEADER(&Header); + BSWAP_TMPQHEADER(&Header, MPQ_FORMAT_VERSION_1); + BSWAP_TMPQHEADER(&Header, MPQ_FORMAT_VERSION_2); + BSWAP_TMPQHEADER(&Header, MPQ_FORMAT_VERSION_3); + BSWAP_TMPQHEADER(&Header, MPQ_FORMAT_VERSION_4); if(!FileStream_Write(ha->pStream, &ha->MpqPos, &Header, dwBytesToWrite)) - nError = GetLastError(); + dwErrCode = SErrGetLastError(); - return nError; + return dwErrCode; } //----------------------------------------------------------------------------- // Creates a new MPQ archive. -bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwFlags, DWORD dwMaxFileCount, HANDLE * phMpq) +bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwCreateFlags, 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.dwMpqVersion = (dwCreateFlags & MPQ_CREATE_ARCHIVE_VMASK) >> FLAGS_TO_FORMAT_SHIFT; + CreateInfo.dwStreamFlags = STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE; + CreateInfo.dwFileFlags1 = (dwCreateFlags & MPQ_CREATE_LISTFILE) ? MPQ_FILE_DEFAULT_INTERNAL : 0; + CreateInfo.dwFileFlags2 = (dwCreateFlags & MPQ_CREATE_ATTRIBUTES) ? MPQ_FILE_DEFAULT_INTERNAL : 0; + CreateInfo.dwFileFlags3 = (dwCreateFlags & MPQ_CREATE_SIGNATURE) ? MPQ_FILE_DEFAULT_INTERNAL : 0; + CreateInfo.dwAttrFlags = (dwCreateFlags & MPQ_CREATE_ATTRIBUTES) ? (MPQ_ATTRIBUTE_CRC32 | MPQ_ATTRIBUTE_FILETIME | MPQ_ATTRIBUTE_MD5) : 0; CreateInfo.dwSectorSize = (CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_3) ? 0x4000 : 0x1000; CreateInfo.dwRawChunkSize = (CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_4) ? 0x4000 : 0; CreateInfo.dwMaxFileCount = dwMaxFileCount; + + // Set the proper attribute parts + if((CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_3) && (dwCreateFlags & MPQ_CREATE_ATTRIBUTES)) + CreateInfo.dwAttrFlags |= MPQ_ATTRIBUTE_PATCH_BIT; + + // Backward compatibility: SFileCreateArchive always used to add (listfile) + // We would break loads of applications if we change that + CreateInfo.dwFileFlags1 = MPQ_FILE_DEFAULT_INTERNAL; + + // Let the main function create the archive return SFileCreateArchive2(szMpqName, &CreateInfo, phMpq); } @@ -86,30 +109,31 @@ bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCrea { TFileStream * pStream = NULL; // File stream TMPQArchive * ha = NULL; // MPQ archive handle + TMPQHeader * pHeader; ULONGLONG MpqPos = 0; // Position of MPQ header in the file HANDLE hMpq = NULL; DWORD dwBlockTableSize = 0; // Initial block table size DWORD dwHashTableSize = 0; - DWORD dwMaxFileCount; - int nError = ERROR_SUCCESS; + DWORD dwReservedFiles = 0; // Number of reserved file entries + DWORD dwMpqFlags = 0; + DWORD dwErrCode = ERROR_SUCCESS; // Check the parameters, if they are valid if(szMpqName == NULL || *szMpqName == 0 || pCreateInfo == NULL || phMpq == NULL) { - SetLastError(ERROR_INVALID_PARAMETER); + SErrSetLastError(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)) + 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))) { - SetLastError(ERROR_INVALID_PARAMETER); + SErrSetLastError(ERROR_INVALID_PARAMETER); return false; } @@ -118,10 +142,10 @@ bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCrea // 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, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE | MPQ_OPEN_NO_ATTRIBUTES | MPQ_OPEN_NO_LISTFILE, &hMpq)) + if(SFileOpenArchive(szMpqName, 0, STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE | MPQ_OPEN_NO_ATTRIBUTES | MPQ_OPEN_NO_LISTFILE, &hMpq)) { SFileCloseArchive(hMpq); - SetLastError(ERROR_ALREADY_EXISTS); + SErrSetLastError(ERROR_ALREADY_EXISTS); return false; } @@ -139,62 +163,73 @@ bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCrea return false; } - // Increment the maximum amount of files to have space - // for listfile and attributes file - dwMaxFileCount = pCreateInfo->dwMaxFileCount; - if(pCreateInfo->dwAttrFlags != 0) - dwMaxFileCount++; - dwMaxFileCount++; + // Increment the maximum amount of files to have space for (listfile) + if(pCreateInfo->dwMaxFileCount && pCreateInfo->dwFileFlags1) + { + dwMpqFlags |= MPQ_FLAG_LISTFILE_NEW; + dwReservedFiles++; + } + + // Increment the maximum amount of files to have space for (attributes) + if(pCreateInfo->dwMaxFileCount && pCreateInfo->dwFileFlags2 && pCreateInfo->dwAttrFlags) + { + dwMpqFlags |= MPQ_FLAG_ATTRIBUTES_NEW; + dwReservedFiles++; + } + + // Increment the maximum amount of files to have space for (signature) + if(pCreateInfo->dwMaxFileCount && pCreateInfo->dwFileFlags3) + { + dwMpqFlags |= MPQ_FLAG_SIGNATURE_NEW; + dwReservedFiles++; + } // If file count is not zero, initialize the hash table size - dwHashTableSize = GetHashTableSizeForFileCount(dwMaxFileCount); + dwHashTableSize = GetNearestPowerOfTwo(pCreateInfo->dwMaxFileCount + dwReservedFiles); // 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(); + dwErrCode = SErrGetLastError(); -#ifdef _DEBUG +#ifdef _DEBUG // Debug code, used for testing StormLib // dwBlockTableSize = dwHashTableSize * 2; #endif // Create the archive handle - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { if((ha = STORM_ALLOC(TMPQArchive, 1)) == NULL) - nError = ERROR_NOT_ENOUGH_MEMORY; + dwErrCode = ERROR_NOT_ENOUGH_MEMORY; } // Fill the MPQ archive handle structure - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { memset(ha, 0, sizeof(TMPQArchive)); - ha->pStream = pStream; - ha->dwSectorSize = pCreateInfo->dwSectorSize; - ha->UserDataPos = MpqPos; - ha->MpqPos = MpqPos; - ha->pHeader = (TMPQHeader *)ha->HeaderData; - ha->dwMaxFileCount = dwMaxFileCount; - ha->dwFileTableSize = 0; - ha->dwFileFlags1 = pCreateInfo->dwFileFlags1; - ha->dwFileFlags2 = pCreateInfo->dwFileFlags2; - ha->dwFlags = 0; - - // Setup the attributes - ha->dwAttrFlags = pCreateInfo->dwAttrFlags; + ha->pfnHashString = HashStringSlash; + ha->pStream = pStream; + ha->dwSectorSize = pCreateInfo->dwSectorSize; + ha->UserDataPos = MpqPos; + ha->MpqPos = MpqPos; + ha->pHeader = pHeader = (TMPQHeader *)ha->HeaderData; + ha->dwMaxFileCount = dwHashTableSize; + ha->dwFileTableSize = 0; + ha->dwReservedFiles = dwReservedFiles; + ha->dwValidFileFlags = GetValidFileFlags(pCreateInfo->dwMpqVersion); + ha->dwFileFlags1 = pCreateInfo->dwFileFlags1; + ha->dwFileFlags2 = pCreateInfo->dwFileFlags2; + ha->dwFileFlags3 = pCreateInfo->dwFileFlags3 ? MPQ_FILE_EXISTS : 0; + ha->dwAttrFlags = pCreateInfo->dwAttrFlags; + ha->dwFlags = dwMpqFlags | MPQ_FLAG_CHANGED; + ha->dwRefCount = 1; pStream = NULL; - } - - // Fill the MPQ header - if(nError == ERROR_SUCCESS) - { - TMPQHeader * pHeader = ha->pHeader; // Fill the MPQ header memset(pHeader, 0, sizeof(ha->HeaderData)); - pHeader->dwID = ID_MPQ; + pHeader->dwID = g_dwMpqSignature; pHeader->dwHeaderSize = MpqHeaderSizes[pCreateInfo->dwMpqVersion]; pHeader->dwArchiveSize = pHeader->dwHeaderSize + dwHashTableSize * sizeof(TMPQHash); pHeader->wFormatVersion = (USHORT)pCreateInfo->dwMpqVersion; @@ -204,52 +239,48 @@ bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCrea pHeader->dwBlockTablePos = pHeader->dwHashTablePos + dwHashTableSize * sizeof(TMPQHash); pHeader->dwBlockTableSize = dwBlockTableSize; + // Set the mask for MPQ byte offset + ha->FileOffsetMask = GetFileOffsetMask(ha); + // For MPQs version 4 and higher, we set the size of raw data block // for calculating MD5 if(pCreateInfo->dwMpqVersion >= MPQ_FORMAT_VERSION_4) pHeader->dwRawChunkSize = pCreateInfo->dwRawChunkSize; // Write the naked MPQ header - nError = WriteNakedMPQHeader(ha); - - // Remember that the (listfile) and (attributes) need to be saved - ha->dwFlags |= MPQ_FLAG_CHANGED | MPQ_FLAG_INV_LISTFILE | MPQ_FLAG_INV_ATTRIBUTES; + dwErrCode = WriteNakedMPQHeader(ha); } // Create initial HET table, if the caller required an MPQ format 3.0 or newer - if(nError == ERROR_SUCCESS && pCreateInfo->dwMpqVersion >= MPQ_FORMAT_VERSION_3) + if(dwErrCode == ERROR_SUCCESS && pCreateInfo->dwMpqVersion >= MPQ_FORMAT_VERSION_3 && pCreateInfo->dwMaxFileCount != 0) { - ha->pHetTable = CreateHetTable(ha->dwMaxFileCount, 0x40, true); + ha->pHetTable = CreateHetTable(ha->dwFileTableSize, 0, 0x40, NULL); if(ha->pHetTable == NULL) - nError = ERROR_NOT_ENOUGH_MEMORY; + dwErrCode = ERROR_NOT_ENOUGH_MEMORY; } // Create initial hash table - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS && dwHashTableSize != 0) { - nError = CreateHashTable(ha, dwHashTableSize); + dwErrCode = CreateHashTable(ha, dwHashTableSize); } // Create initial file table - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS && ha->dwMaxFileCount != 0) { - ha->pFileTable = STORM_ALLOC(TFileEntry, ha->dwMaxFileCount); - if(ha->pFileTable != NULL) - memset(ha->pFileTable, 0x00, sizeof(TFileEntry) * ha->dwMaxFileCount); - else - nError = ERROR_NOT_ENOUGH_MEMORY; + dwErrCode = CreateFileTable(ha, ha->dwMaxFileCount); } // Cleanup : If an error, delete all buffers and return - if(nError != ERROR_SUCCESS) + if(dwErrCode != ERROR_SUCCESS) { FileStream_Close(pStream); - FreeMPQArchive(ha); - SetLastError(nError); + DereferenceArchive(ha); + SErrSetLastError(dwErrCode); ha = NULL; } - + // Return the values *phMpq = (HANDLE)ha; - return (nError == ERROR_SUCCESS); + return (dwErrCode == ERROR_SUCCESS); } diff --git a/dep/StormLib/src/SFileExtractFile.cpp b/dep/StormLib/src/SFileExtractFile.cpp index c8053ed4e6..b65410c483 100644 --- a/dep/StormLib/src/SFileExtractFile.cpp +++ b/dep/StormLib/src/SFileExtractFile.cpp @@ -16,44 +16,41 @@ bool WINAPI SFileExtractFile(HANDLE hMpq, const char * szToExtract, const TCHAR { TFileStream * pLocalFile = NULL; HANDLE hMpqFile = NULL; - int nError = ERROR_SUCCESS; + DWORD dwErrCode = ERROR_SUCCESS; // Open the MPQ file - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { if(!SFileOpenFileEx(hMpq, szToExtract, dwSearchScope, &hMpqFile)) - nError = GetLastError(); + dwErrCode = SErrGetLastError(); } // Create the local file - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { - pLocalFile = FileStream_CreateFile(szExtracted, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); + pLocalFile = FileStream_CreateFile(szExtracted, 0); if(pLocalFile == NULL) - nError = GetLastError(); + dwErrCode = SErrGetLastError(); } // Copy the file's content - if(nError == ERROR_SUCCESS) + while(dwErrCode == ERROR_SUCCESS) { char szBuffer[0x1000]; - DWORD dwTransferred; + DWORD dwTransferred = 0; - for(;;) - { - // dwTransferred is only set to nonzero if something has been read. - // nError can be ERROR_SUCCESS or ERROR_HANDLE_EOF - if(!SFileReadFile(hMpqFile, szBuffer, sizeof(szBuffer), &dwTransferred, NULL)) - nError = GetLastError(); - if(nError == ERROR_HANDLE_EOF) - nError = ERROR_SUCCESS; - if(dwTransferred == 0) - break; + // dwTransferred is only set to nonzero if something has been read. + // dwErrCode can be ERROR_SUCCESS or ERROR_HANDLE_EOF + if(!SFileReadFile(hMpqFile, szBuffer, sizeof(szBuffer), &dwTransferred, NULL)) + dwErrCode = SErrGetLastError(); + if(dwErrCode == ERROR_HANDLE_EOF) + dwErrCode = ERROR_SUCCESS; + if(dwTransferred == 0) + break; - // If something has been actually read, write it - if(!FileStream_Write(pLocalFile, NULL, szBuffer, dwTransferred)) - nError = GetLastError(); - } + // If something has been actually read, write it + if(!FileStream_Write(pLocalFile, NULL, szBuffer, dwTransferred)) + dwErrCode = SErrGetLastError(); } // Close the files @@ -61,7 +58,7 @@ bool WINAPI SFileExtractFile(HANDLE hMpq, const char * szToExtract, const TCHAR SFileCloseFile(hMpqFile); if(pLocalFile != NULL) FileStream_Close(pLocalFile); - if(nError != ERROR_SUCCESS) - SetLastError(nError); - return (nError == ERROR_SUCCESS); + if(dwErrCode != ERROR_SUCCESS) + SErrSetLastError(dwErrCode); + return (dwErrCode == ERROR_SUCCESS); } diff --git a/dep/StormLib/src/SFileFindFile.cpp b/dep/StormLib/src/SFileFindFile.cpp index ca745b9134..7db680cd19 100644 --- a/dep/StormLib/src/SFileFindFile.cpp +++ b/dep/StormLib/src/SFileFindFile.cpp @@ -12,17 +12,9 @@ #include "StormLib.h" #include "StormCommon.h" -//----------------------------------------------------------------------------- -// Defines - -#define LISTFILE_CACHE_SIZE 0x1000 - //----------------------------------------------------------------------------- // Private structure used for file search (search handle) -struct TMPQSearch; -typedef int (*MPQSEARCH)(TMPQSearch *, SFILE_FIND_DATA *); - // Used by searching in MPQ archives struct TMPQSearch { @@ -37,97 +29,65 @@ struct TMPQSearch //----------------------------------------------------------------------------- // Local functions -static bool IsValidSearchHandle(TMPQSearch * hs) +static TMPQSearch * IsValidSearchHandle(HANDLE hFind) { - if(hs == NULL) - return false; + TMPQSearch * hs = (TMPQSearch *)hFind; - return IsValidMpqHandle(hs->ha); + if(hs != NULL && IsValidMpqHandle(hs->ha)) + return hs; + + return NULL; } -bool CheckWildCard(const char * szString, const char * szWildCard) +bool SFileCheckWildCard(const char * szString, const char * szWildCard) { - const char * szSubString; - int nSubStringLength; - int nMatchCount = 0; + const char * szWildCardPtr; - // When the mask is empty, it never matches - if(szWildCard == NULL || *szWildCard == 0) - return false; - - // If the wildcard contains just "*", then it always matches - if(szWildCard[0] == '*' && szWildCard[1] == 0) - return true; - - // Do normal test for(;;) { // If there is '?' in the wildcard, we skip one char - while(*szWildCard == '?') + while(szWildCard[0] == '?') { + if(szString[0] == 0) + return false; + szWildCard++; szString++; } - // If there is '*', means zero or more chars. We have to - // find the sequence after '*' - if(*szWildCard == '*') + // Handle '*' + szWildCardPtr = szWildCard; + if(szWildCardPtr[0] != 0) { - // More stars is equal to one star - while(*szWildCard == '*' || *szWildCard == '?') - szWildCard++; - - // If we found end of the wildcard, it's a match - if(*szWildCard == 0) - return true; - - // Determine the length of the substring in szWildCard - szSubString = szWildCard; - while(*szSubString != 0 && *szSubString != '?' && *szSubString != '*') - szSubString++; - nSubStringLength = (int)(szSubString - szWildCard); - nMatchCount = 0; - - // Now we have to find a substring in szString, - // that matches the substring in szWildCard - while(*szString != 0) + if(szWildCardPtr[0] == '*') { - // Calculate match count - while(nMatchCount < nSubStringLength) - { - if(toupper(szString[nMatchCount]) != toupper(szWildCard[nMatchCount])) - break; - if(szString[nMatchCount] == 0) - break; - nMatchCount++; - } + while(szWildCardPtr[0] == '*') + szWildCardPtr++; - // If the match count has reached substring length, we found a match - if(nMatchCount == nSubStringLength) - { - szWildCard += nMatchCount; - szString += nMatchCount; - break; - } + if(szWildCardPtr[0] == 0) + return true; - // No match, move to the next char in szString - nMatchCount = 0; - szString++; + if(AsciiToUpperTable[szWildCardPtr[0]] == AsciiToUpperTable[szString[0]]) + { + if(SFileCheckWildCard(szString, szWildCardPtr)) + return true; + } } + else + { + if(AsciiToUpperTable[szWildCardPtr[0]] != AsciiToUpperTable[szString[0]]) + return false; + + szWildCard = szWildCardPtr + 1; + } + + if(szString[0] == 0) + return false; + szString++; } else { - // If we came to the end of the string, compare it to the wildcard - if(toupper(*szString) != toupper(*szWildCard)) - return false; - - // If we arrived to the end of the string, it's a match - if(*szString == 0) - return true; - - // Otherwise, continue in comparing - szWildCard++; - szString++; + return (szString[0] == 0) ? true : false; } } } @@ -140,7 +100,7 @@ static DWORD GetSearchTableItems(TMPQArchive * ha) while(ha != NULL) { // Append the number of files - dwMergeItems += (ha->pHetTable != NULL) ? ha->pHetTable->dwMaxFileCount + dwMergeItems += (ha->pHetTable != NULL) ? ha->pHetTable->dwEntryCount : ha->pHeader->dwBlockTableSize; // Move to the patched archive ha = ha->haPatch; @@ -165,18 +125,18 @@ static bool FileWasFoundBefore( { // If we are in patch MPQ, we check if patch prefix matches // and then trim the patch prefix - if(ha->cchPatchPrefix != 0) + if(ha->pPatchPrefix != NULL) { // If the patch prefix doesn't fit, we pretend that the file // was there before and it will be skipped - if(_strnicmp(szRealFileName, ha->szPatchPrefix, ha->cchPatchPrefix)) + if(_strnicmp(szRealFileName, ha->pPatchPrefix->szPatchPrefix, ha->pPatchPrefix->nLength)) return true; - szRealFileName += ha->cchPatchPrefix; + szRealFileName += ha->pPatchPrefix->nLength; } // Calculate the hash to the table - dwNameHash = HashString(szRealFileName, MPQ_HASH_NAME_A); + dwNameHash = ha->pfnHashString(szRealFileName, MPQ_HASH_NAME_A); dwStartIndex = dwIndex = (dwNameHash % hs->dwSearchTableItems); // The file might have been found before @@ -213,119 +173,195 @@ static bool FileWasFoundBefore( static TFileEntry * FindPatchEntry(TMPQArchive * ha, TFileEntry * pFileEntry) { - TFileEntry * pPatchEntry = NULL; + TFileEntry * pPatchEntry = pFileEntry; TFileEntry * pTempEntry; - char szFileName[MAX_PATH]; - LCID lcLocale = pFileEntry->lcLocale; + char szFileName[MAX_PATH+1]; - // Go while there are patches - while(ha->haPatch != NULL) + // Can't find patch entry for a file that doesn't have name + if(pFileEntry->szFileName != NULL && pFileEntry->szFileName[0] != 0) { - // Move to the patch archive - ha = ha->haPatch; + // Go while there are patches + while(ha->haPatch != NULL) + { + // Move to the patch archive + ha = ha->haPatch; + szFileName[0] = 0; - // Prepare the prefix for the file name - char* fileName = pFileEntry->szFileName; - if (fileName == NULL) - continue; + // Prepare the prefix for the file name + if(ha->pPatchPrefix && ha->pPatchPrefix->nLength) + StringCopy(szFileName, _countof(szFileName), ha->pPatchPrefix->szPatchPrefix); + StringCat(szFileName, _countof(szFileName), pFileEntry->szFileName); - strcpy(szFileName, ha->szPatchPrefix); - strcat(szFileName, fileName); - - // Try to find the file there - pTempEntry = GetFileEntryExact(ha, szFileName, lcLocale); - if(pTempEntry != NULL) - pPatchEntry = pTempEntry; + // Try to find the file there + pTempEntry = GetFileEntryExact(ha, szFileName, 0, NULL); + if(pTempEntry != NULL) + pPatchEntry = pTempEntry; + } } // Return the found patch entry return pPatchEntry; } +static bool DoMPQSearch_FileEntry( + TMPQSearch * hs, + SFILE_FIND_DATA * lpFindFileData, + TMPQArchive * ha, + TMPQHash * pHashEntry, + TFileEntry * pFileEntry) +{ + TFileEntry * pPatchEntry; + HANDLE hFile = NULL; + const char * szFileName; + size_t nGlobalPrefixLength = (ha->pPatchPrefix != NULL) ? ha->pPatchPrefix->nLength : 0; + DWORD dwBlockIndex; + char szNameBuff[MAX_PATH]; + + // Is it a file but not a patch file? + if((pFileEntry->dwFlags & hs->dwFlagMask) == MPQ_FILE_EXISTS) + { + // Ignore fake files which are not compressed but have size higher than the archive + if((pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) == 0 && (pFileEntry->dwFileSize > ha->FileSize)) + return false; + + // Now we have to check if this file was not enumerated before + if(!FileWasFoundBefore(ha, hs, pFileEntry)) + { + size_t nPrefixLength = nGlobalPrefixLength; + +// if(pFileEntry != NULL && !_stricmp(pFileEntry->szFileName, "TriggerLibs\\NativeLib.galaxy")) +// DebugBreak(); + + // Find a patch to this file + // Note: This either succeeds or returns pFileEntry + pPatchEntry = FindPatchEntry(ha, pFileEntry); + + // Prepare the block index + dwBlockIndex = (DWORD)(pFileEntry - ha->pFileTable); + + // Get the file name. If it's not known, we will create pseudo-name + szFileName = pFileEntry->szFileName; + if(szFileName == NULL) + { + // Open the file by its pseudo-name. + StringCreatePseudoFileName(szNameBuff, _countof(szNameBuff), dwBlockIndex, "xxx"); + if(SFileOpenFileEx((HANDLE)hs->ha, szNameBuff, SFILE_OPEN_BASE_FILE, &hFile)) + { + SFileGetFileName(hFile, szNameBuff); + SFileCloseFile(hFile); + szFileName = szNameBuff; + nPrefixLength = 0; + } + } + + // If the file name is still NULL, we cannot include the file to search results + if(szFileName != NULL) + { + // Check the file name against the wildcard + if(SFileCheckWildCard(szFileName + nPrefixLength, hs->szSearchMask)) + { + // Fill the found entry. hash entry and block index are taken from the base MPQ + lpFindFileData->dwHashIndex = HASH_ENTRY_FREE; + lpFindFileData->dwBlockIndex = dwBlockIndex; + lpFindFileData->dwFileSize = pPatchEntry->dwFileSize; + lpFindFileData->dwFileFlags = pPatchEntry->dwFlags; + lpFindFileData->dwCompSize = pPatchEntry->dwCmpSize; + lpFindFileData->lcLocale = 0; // pPatchEntry->lcFileLocale; + + // Fill the filetime + lpFindFileData->dwFileTimeHi = (DWORD)(pPatchEntry->FileTime >> 32); + lpFindFileData->dwFileTimeLo = (DWORD)(pPatchEntry->FileTime); + + // Fill-in the entries from hash table entry, if given + if(pHashEntry != NULL) + { + lpFindFileData->dwHashIndex = (DWORD)(pHashEntry - ha->pHashTable); + lpFindFileData->lcLocale = SFILE_MAKE_LCID(pHashEntry->Locale, pHashEntry->Platform); + } + + // Fill the file name and plain file name + StringCopy(lpFindFileData->cFileName, _countof(lpFindFileData->cFileName), szFileName + nPrefixLength); + lpFindFileData->szPlainName = (char *)GetPlainFileName(lpFindFileData->cFileName); + return true; + } + } + } + } + + // Either not a valid item or was found before + return false; +} + +static DWORD DoMPQSearch_HashTable(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData, TMPQArchive * ha) +{ + TMPQHash * pHashTableEnd = ha->pHashTable + ha->pHeader->dwHashTableSize; + TMPQHash * pHash; + + // Parse the file table + for(pHash = ha->pHashTable + hs->dwNextIndex; pHash < pHashTableEnd; pHash++) + { + // Increment the next index for subsequent search + hs->dwNextIndex++; + + // Does this hash table entry point to a proper block table entry? + if(IsValidHashEntry(ha, pHash)) + { + // Check if this file entry should be included in the search result + if(DoMPQSearch_FileEntry(hs, lpFindFileData, ha, pHash, ha->pFileTable + MPQ_BLOCK_INDEX(pHash))) + return ERROR_SUCCESS; + } + } + + // No more files + return ERROR_NO_MORE_FILES; +} + +static DWORD DoMPQSearch_FileTable(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData, TMPQArchive * ha) +{ + TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; + TFileEntry * pFileEntry; + + // Parse the file table + for(pFileEntry = ha->pFileTable + hs->dwNextIndex; pFileEntry < pFileTableEnd; pFileEntry++) + { + // Increment the next index for subsequent search + hs->dwNextIndex++; + + // Check if this file entry should be included in the search result + if(DoMPQSearch_FileEntry(hs, lpFindFileData, ha, NULL, pFileEntry)) + return ERROR_SUCCESS; + } + + // No more files + return ERROR_NO_MORE_FILES; +} + // Performs one MPQ search -static int DoMPQSearch(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData) +static DWORD DoMPQSearch(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData) { TMPQArchive * ha = hs->ha; - TFileEntry * pFileTableEnd; - TFileEntry * pPatchEntry; - TFileEntry * pFileEntry; - const char * szFileName; - HANDLE hFile; - char szPseudoName[20]; - DWORD dwBlockIndex; - size_t nPrefixLength; + DWORD dwErrCode; // Start searching with base MPQ while(ha != NULL) { - // Now parse the file entry table in order to get all files. - pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; - pFileEntry = ha->pFileTable + hs->dwNextIndex; - - // Get the length of the patch prefix (0 if none) - nPrefixLength = strlen(ha->szPatchPrefix); - - // Parse the file table - while(pFileEntry < pFileTableEnd) + // If the archive has hash table, we need to use hash table + // in order to catch hash table index and file locale. + // Note: If multiple hash table entries, point to the same block entry, + // we need, to report them all + dwErrCode = (ha->pHashTable != NULL) ? DoMPQSearch_HashTable(hs, lpFindFileData, ha) + : DoMPQSearch_FileTable(hs, lpFindFileData, ha); + if(dwErrCode == ERROR_SUCCESS) { - // Increment the next index for subsequent search - hs->dwNextIndex++; - - // Is it a file and not a patch file? - if((pFileEntry->dwFlags & hs->dwFlagMask) == MPQ_FILE_EXISTS) - { - // Now we have to check if this file was not enumerated before - if(!FileWasFoundBefore(ha, hs, pFileEntry)) - { - // Find a patch to this file - pPatchEntry = FindPatchEntry(ha, pFileEntry); - if(pPatchEntry == NULL) - pPatchEntry = pFileEntry; - - // Prepare the block index - dwBlockIndex = (DWORD)(pFileEntry - ha->pFileTable); - - // Get the file name. If it's not known, we will create 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)) - { - szFileName = (pFileEntry->szFileName != NULL) ? pFileEntry->szFileName : szPseudoName; - SFileCloseFile(hFile); - } - } - - // Check the file name against the wildcard - if(CheckWildCard(szFileName + nPrefixLength, hs->szSearchMask)) - { - // Fill the found entry - lpFindFileData->dwHashIndex = pPatchEntry->dwHashIndex; - lpFindFileData->dwBlockIndex = dwBlockIndex; - lpFindFileData->dwFileSize = pPatchEntry->dwFileSize; - lpFindFileData->dwFileFlags = pPatchEntry->dwFlags; - lpFindFileData->dwCompSize = pPatchEntry->dwCmpSize; - lpFindFileData->lcLocale = pPatchEntry->lcLocale; - - // Fill the filetime - lpFindFileData->dwFileTimeHi = (DWORD)(pPatchEntry->FileTime >> 32); - lpFindFileData->dwFileTimeLo = (DWORD)(pPatchEntry->FileTime); - - // Fill the file name and plain file name - strcpy(lpFindFileData->cFileName, szFileName + nPrefixLength); - lpFindFileData->szPlainName = (char *)GetPlainFileNameA(lpFindFileData->cFileName); - return ERROR_SUCCESS; - } - - } - } - - pFileEntry++; + return dwErrCode; } + // If there is no more patches in the chain, stop it. + // This also keeps hs->ha non-NULL, which is required + // for freeing the handle later + if(ha->haPatch == NULL) + break; + // Move to the next patch in the patch chain hs->ha = ha = ha->haPatch; hs->dwNextIndex = 0; @@ -349,38 +385,38 @@ static void FreeMPQSearch(TMPQSearch *& hs) //----------------------------------------------------------------------------- // Public functions -HANDLE WINAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_DATA * lpFindFileData, const char * szListFile) +HANDLE WINAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_DATA * lpFindFileData, const TCHAR * szListFile) { TMPQArchive * ha = (TMPQArchive *)hMpq; TMPQSearch * hs = NULL; size_t nSize = 0; - int nError = ERROR_SUCCESS; + DWORD dwErrCode = ERROR_SUCCESS; // Check for the valid parameters - if(!IsValidMpqHandle(ha)) - nError = ERROR_INVALID_HANDLE; + if(!IsValidMpqHandle(hMpq)) + dwErrCode = ERROR_INVALID_HANDLE; if(szMask == NULL || lpFindFileData == NULL) - nError = ERROR_INVALID_PARAMETER; + dwErrCode = ERROR_INVALID_PARAMETER; // Include the listfile into the MPQ's internal listfile // Note that if the listfile name is NULL, do nothing because the // internal listfile is always included. - if(nError == ERROR_SUCCESS && szListFile != NULL && *szListFile != 0) - nError = SFileAddListFile((HANDLE)ha, szListFile); + if(dwErrCode == ERROR_SUCCESS && szListFile != NULL && *szListFile != 0) + dwErrCode = SFileAddListFile((HANDLE)ha, szListFile); // Allocate the structure for MPQ search - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { nSize = sizeof(TMPQSearch) + strlen(szMask) + 1; if((hs = (TMPQSearch *)STORM_ALLOC(char, nSize)) == NULL) - nError = ERROR_NOT_ENOUGH_MEMORY; + dwErrCode = ERROR_NOT_ENOUGH_MEMORY; } // Perform the first search - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { memset(hs, 0, sizeof(TMPQSearch)); - strcpy(&hs->szSearchMask[0], szMask); + strcpy(hs->szSearchMask, szMask); hs->dwFlagMask = MPQ_FILE_EXISTS; hs->ha = ha; @@ -394,54 +430,54 @@ HANDLE WINAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_DA if(hs->pSearchTable != NULL) memset(hs->pSearchTable, 0, hs->dwSearchTableItems * sizeof(TFileEntry *)); else - nError = ERROR_NOT_ENOUGH_MEMORY; + dwErrCode = ERROR_NOT_ENOUGH_MEMORY; } } // Perform first item searching - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { - nError = DoMPQSearch(hs, lpFindFileData); + dwErrCode = DoMPQSearch(hs, lpFindFileData); } // Cleanup - if(nError != ERROR_SUCCESS) + if(dwErrCode != ERROR_SUCCESS) { FreeMPQSearch(hs); - SetLastError(nError); + SErrSetLastError(dwErrCode); } - + // Return the result value return (HANDLE)hs; } bool WINAPI SFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData) { - TMPQSearch * hs = (TMPQSearch *)hFind; - int nError = ERROR_SUCCESS; + TMPQSearch * hs = IsValidSearchHandle(hFind); + DWORD dwErrCode = ERROR_SUCCESS; // Check the parameters - if(!IsValidSearchHandle(hs)) - nError = ERROR_INVALID_HANDLE; + if(hs == NULL) + dwErrCode = ERROR_INVALID_HANDLE; if(lpFindFileData == NULL) - nError = ERROR_INVALID_PARAMETER; + dwErrCode = ERROR_INVALID_PARAMETER; - if(nError == ERROR_SUCCESS) - nError = DoMPQSearch(hs, lpFindFileData); + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = DoMPQSearch(hs, lpFindFileData); - if(nError != ERROR_SUCCESS) - SetLastError(nError); - return (nError == ERROR_SUCCESS); + if(dwErrCode != ERROR_SUCCESS) + SErrSetLastError(dwErrCode); + return (dwErrCode == ERROR_SUCCESS); } bool WINAPI SFileFindClose(HANDLE hFind) { - TMPQSearch * hs = (TMPQSearch *)hFind; + TMPQSearch * hs = IsValidSearchHandle(hFind); // Check the parameters - if(!IsValidSearchHandle(hs)) + if(hs == NULL) { - SetLastError(ERROR_INVALID_HANDLE); + SErrSetLastError(ERROR_INVALID_HANDLE); return false; } diff --git a/dep/StormLib/src/SFileGetFileInfo.cpp b/dep/StormLib/src/SFileGetFileInfo.cpp new file mode 100644 index 0000000000..f245387e17 --- /dev/null +++ b/dep/StormLib/src/SFileGetFileInfo.cpp @@ -0,0 +1,627 @@ +/*****************************************************************************/ +/* SFileGetFileInfo.cpp Copyright (c) Ladislav Zezula 2013 */ +/*---------------------------------------------------------------------------*/ +/* Description: */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 30.11.13 1.00 Lad The first version of SFileGetFileInfo.cpp */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "StormCommon.h" + +//----------------------------------------------------------------------------- +// Local functions + +static DWORD GetMpqFileCount(TMPQArchive * ha) +{ + TFileEntry * pFileTableEnd; + TFileEntry * pFileEntry; + DWORD dwFileCount = 0; + + // Go through all open MPQs, including patches + while(ha != NULL) + { + // Only count files that are not patch files + pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; + 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 + // 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 + ha = ha->haPatch; + } + + return dwFileCount; +} + +static bool GetInfo_ReturnError(DWORD dwErrCode) +{ + SErrSetLastError(dwErrCode); + return false; +} + +static bool GetInfo_BufferCheck(void * pvFileInfo, DWORD cbFileInfo, DWORD cbData, LPDWORD pcbLengthNeeded) +{ + // Give the length needed to store the info + if(pcbLengthNeeded != NULL) + pcbLengthNeeded[0] = cbData; + + // Check for sufficient buffer + if(cbData > cbFileInfo) + return GetInfo_ReturnError(ERROR_INSUFFICIENT_BUFFER); + + // If the buffer size is sufficient, check for valid user buffer + if(pvFileInfo == NULL) + return GetInfo_ReturnError(ERROR_INVALID_PARAMETER); + + // Buffers and sizes are OK, we are ready to proceed file copying + return true; +} + +static bool GetInfo(void * pvFileInfo, DWORD cbFileInfo, const void * pvData, DWORD cbData, LPDWORD pcbLengthNeeded) +{ + // Verify the input parameter + if(pvData == NULL) + return GetInfo_ReturnError(ERROR_INVALID_PARAMETER); + + // Verify buffer pointer and buffer size + if(!GetInfo_BufferCheck(pvFileInfo, cbFileInfo, cbData, pcbLengthNeeded)) + return false; + + // Copy the data to the caller-supplied buffer + memcpy(pvFileInfo, pvData, cbData); + return true; +} + +static bool GetInfo_Allocated(void * pvFileInfo, DWORD cbFileInfo, void * pvData, DWORD cbData, LPDWORD pcbLengthNeeded) +{ + bool bResult; + + // Verify the input parameter + if(pvData == NULL) + return GetInfo_ReturnError(ERROR_INVALID_PARAMETER); + + // Verify buffer pointer and buffer size + if((bResult = GetInfo_BufferCheck(pvFileInfo, cbFileInfo, cbData, pcbLengthNeeded)) != false) + memcpy(pvFileInfo, pvData, cbData); + + // Copy the data to the user buffer + STORM_FREE(pvData); + return bResult; +} + +static bool GetInfo_TablePointer(void * pvFileInfo, DWORD cbFileInfo, void * pvTablePointer, SFileInfoClass InfoClass, LPDWORD pcbLengthNeeded) +{ + // Verify buffer pointer and buffer size + if(!GetInfo_BufferCheck(pvFileInfo, cbFileInfo, sizeof(void *), pcbLengthNeeded)) + { + SFileFreeFileInfo(pvTablePointer, InfoClass); + return false; + } + + // The user buffer receives pointer to the table. + // When done, the caller needs to call SFileFreeFileInfo on it + *(void **)pvFileInfo = pvTablePointer; + return true; +} + +static bool GetInfo_ReadFromFile(void * pvFileInfo, DWORD cbFileInfo, TFileStream * pStream, ULONGLONG ByteOffset, DWORD cbData, LPDWORD pcbLengthNeeded) +{ + // Verify buffer pointer and buffer size + if(!GetInfo_BufferCheck(pvFileInfo, cbFileInfo, cbData, pcbLengthNeeded)) + return false; + + return FileStream_Read(pStream, &ByteOffset, pvFileInfo, cbData); +} + +static bool GetInfo_FileEntry(void * pvFileInfo, DWORD cbFileInfo, TFileEntry * pFileEntry, LPDWORD pcbLengthNeeded) +{ + LPBYTE pbFileInfo = (LPBYTE)pvFileInfo; + DWORD cbSrcFileInfo = sizeof(TFileEntry); + DWORD cbFileName = 1; + + // The file name belongs to the file entry + if(pFileEntry->szFileName) + cbFileName = (DWORD)strlen(pFileEntry->szFileName) + 1; + cbSrcFileInfo += cbFileName; + + // Verify buffer pointer and buffer size + if(!GetInfo_BufferCheck(pvFileInfo, cbFileInfo, cbSrcFileInfo, pcbLengthNeeded)) + return false; + + // Copy the file entry + memcpy(pbFileInfo, pFileEntry, sizeof(TFileEntry)); + pbFileInfo += sizeof(TFileEntry); + pbFileInfo[0] = 0; + + // Copy the file name + if(pFileEntry->szFileName) + memcpy(pbFileInfo, pFileEntry->szFileName, cbFileName); + return true; +} + +static bool GetInfo_PatchChain(TMPQFile * hf, void * pvFileInfo, DWORD cbFileInfo, LPDWORD pcbLengthNeeded) +{ + TMPQFile * hfTemp; + LPCTSTR szPatchName; + LPTSTR szFileInfo = (LPTSTR)pvFileInfo; + size_t cchCharsNeeded = 1; + size_t nLength; + + // Patch chain is only supported on MPQ files. Local files are not supported. + if(hf->pStream != NULL) + return GetInfo_ReturnError(ERROR_INVALID_PARAMETER); + + // Calculate the necessary length of the multi-string + for(hfTemp = hf; hfTemp != NULL; hfTemp = hfTemp->hfPatch) + cchCharsNeeded += _tcslen(FileStream_GetFileName(hfTemp->ha->pStream)) + 1; + + // Verify whether the caller gave us valid buffer with enough size + if(!GetInfo_BufferCheck(pvFileInfo, cbFileInfo, (DWORD)(cchCharsNeeded * sizeof(TCHAR)), pcbLengthNeeded)) + return false; + + // Copy each patch name + for(hfTemp = hf; hfTemp != NULL; hfTemp = hfTemp->hfPatch) + { + // Get the file name and its length + szPatchName = FileStream_GetFileName(hfTemp->ha->pStream); + nLength = _tcslen(szPatchName) + 1; + + // Copy the file name + memcpy(szFileInfo, szPatchName, nLength * sizeof(TCHAR)); + szFileInfo += nLength; + } + + // Make it multi-string + szFileInfo[0] = 0; + return true; +} + +//----------------------------------------------------------------------------- +// Retrieves an information about an archive or about a file within the archive +// +// hMpqOrFile - Handle to an MPQ archive or to a file +// InfoClass - Information to obtain +// pvFileInfo - Pointer to buffer to store the information +// cbFileInfo - Size of the buffer pointed by pvFileInfo +// pcbLengthNeeded - Receives number of bytes necessary to store the information + +bool WINAPI SFileGetFileInfo( + HANDLE hMpqOrFile, + SFileInfoClass InfoClass, + void * pvFileInfo, + DWORD cbFileInfo, + LPDWORD pcbLengthNeeded) +{ + MPQ_SIGNATURE_INFO SignatureInfo; + const TCHAR * szSrcFileInfo; + TMPQArchive * ha = NULL; + TFileEntry * pFileEntry = NULL; + TMPQHeader * pHeader = NULL; + ULONGLONG Int64Value = 0; + ULONGLONG ByteOffset; + TMPQFile * hf = NULL; + void * pvSrcFileInfo = NULL; + DWORD cbSrcFileInfo = 0; + DWORD dwInt32Value = 0; + + // Validate archive/file handle + if((int)InfoClass <= (int)SFileMpqFlags) + { + if((ha = IsValidMpqHandle(hMpqOrFile)) == NULL) + return GetInfo_ReturnError(ERROR_INVALID_HANDLE); + pHeader = ha->pHeader; + } + else + { + if((hf = IsValidFileHandle(hMpqOrFile)) == NULL) + return GetInfo_ReturnError(ERROR_INVALID_HANDLE); + pFileEntry = hf->pFileEntry; + } + + // Return info-class-specific data + switch(InfoClass) + { + case SFileMpqFileName: + szSrcFileInfo = FileStream_GetFileName(ha->pStream); + cbSrcFileInfo = (DWORD)((_tcslen(szSrcFileInfo) + 1) * sizeof(TCHAR)); + return GetInfo(pvFileInfo, cbFileInfo, szSrcFileInfo, cbSrcFileInfo, pcbLengthNeeded); + + case SFileMpqStreamBitmap: + return FileStream_GetBitmap(ha->pStream, pvFileInfo, cbFileInfo, pcbLengthNeeded); + + case SFileMpqUserDataOffset: + return GetInfo(pvFileInfo, cbFileInfo, &ha->UserDataPos, sizeof(ULONGLONG), pcbLengthNeeded); + + case SFileMpqUserDataHeader: + if(ha->pUserData == NULL) + return GetInfo_ReturnError(ERROR_INVALID_PARAMETER); + return GetInfo_ReadFromFile(pvFileInfo, cbFileInfo, ha->pStream, ha->UserDataPos, sizeof(TMPQUserData), pcbLengthNeeded); + + case SFileMpqUserData: + if(ha->pUserData == NULL) + return GetInfo_ReturnError(ERROR_INVALID_PARAMETER); + return GetInfo_ReadFromFile(pvFileInfo, cbFileInfo, ha->pStream, ha->UserDataPos + sizeof(TMPQUserData), ha->pUserData->dwHeaderOffs - sizeof(TMPQUserData), pcbLengthNeeded); + + case SFileMpqHeaderOffset: + return GetInfo(pvFileInfo, cbFileInfo, &ha->MpqPos, sizeof(ULONGLONG), pcbLengthNeeded); + + case SFileMpqHeaderSize: + return GetInfo(pvFileInfo, cbFileInfo, &pHeader->dwHeaderSize, sizeof(DWORD), pcbLengthNeeded); + + case SFileMpqHeader: + return GetInfo_ReadFromFile(pvFileInfo, cbFileInfo, ha->pStream, ha->MpqPos, pHeader->dwHeaderSize, pcbLengthNeeded); + + case SFileMpqHetTableOffset: + return GetInfo(pvFileInfo, cbFileInfo, &pHeader->HetTablePos64, sizeof(ULONGLONG), pcbLengthNeeded); + + case SFileMpqHetTableSize: + return GetInfo(pvFileInfo, cbFileInfo, &pHeader->HetTableSize64, sizeof(ULONGLONG), pcbLengthNeeded); + + case SFileMpqHetHeader: + pvSrcFileInfo = LoadExtTable(ha, pHeader->HetTablePos64, (size_t)pHeader->HetTableSize64, HET_TABLE_SIGNATURE, MPQ_KEY_HASH_TABLE); + if(pvSrcFileInfo == NULL) + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); + return GetInfo_Allocated(pvFileInfo, cbFileInfo, pvSrcFileInfo, sizeof(TMPQHetHeader), pcbLengthNeeded); + + case SFileMpqHetTable: + if((pvSrcFileInfo = LoadHetTable(ha)) == NULL) + return GetInfo_ReturnError(ERROR_NOT_ENOUGH_MEMORY); + return GetInfo_TablePointer(pvFileInfo, cbFileInfo, pvSrcFileInfo, InfoClass, pcbLengthNeeded); + + case SFileMpqBetTableOffset: + return GetInfo(pvFileInfo, cbFileInfo, &pHeader->BetTablePos64, sizeof(ULONGLONG), pcbLengthNeeded); + + case SFileMpqBetTableSize: + return GetInfo(pvFileInfo, cbFileInfo, &pHeader->BetTableSize64, sizeof(ULONGLONG), pcbLengthNeeded); + + case SFileMpqBetHeader: + + // Retrieve the table and its size + pvSrcFileInfo = LoadExtTable(ha, pHeader->BetTablePos64, (size_t)pHeader->BetTableSize64, BET_TABLE_SIGNATURE, MPQ_KEY_BLOCK_TABLE); + if(pvSrcFileInfo == NULL) + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); + cbSrcFileInfo = sizeof(TMPQBetHeader) + ((TMPQBetHeader *)pvSrcFileInfo)->dwFlagCount * sizeof(DWORD); + + // It is allowed for the caller to only require BET header + if(cbFileInfo == sizeof(TMPQBetHeader)) + cbSrcFileInfo = sizeof(TMPQBetHeader); + return GetInfo_Allocated(pvFileInfo, cbFileInfo, pvSrcFileInfo, cbSrcFileInfo, pcbLengthNeeded); + + case SFileMpqBetTable: + if((pvSrcFileInfo = LoadBetTable(ha)) == NULL) + return GetInfo_ReturnError(ERROR_NOT_ENOUGH_MEMORY); + return GetInfo_TablePointer(pvFileInfo, cbFileInfo, pvSrcFileInfo, InfoClass, pcbLengthNeeded); + + case SFileMpqHashTableOffset: + Int64Value = MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos); + return GetInfo(pvFileInfo, cbFileInfo, &Int64Value, sizeof(ULONGLONG), pcbLengthNeeded); + + case SFileMpqHashTableSize64: + return GetInfo(pvFileInfo, cbFileInfo, &pHeader->HashTableSize64, sizeof(ULONGLONG), pcbLengthNeeded); + + case SFileMpqHashTableSize: + return GetInfo(pvFileInfo, cbFileInfo, &pHeader->dwHashTableSize, sizeof(DWORD), pcbLengthNeeded); + + case SFileMpqHashTable: + cbSrcFileInfo = pHeader->dwHashTableSize * sizeof(TMPQHash); + return GetInfo(pvFileInfo, cbFileInfo, ha->pHashTable, cbSrcFileInfo, pcbLengthNeeded); + + case SFileMpqBlockTableOffset: + Int64Value = MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); + return GetInfo(pvFileInfo, cbFileInfo, &Int64Value, sizeof(ULONGLONG), pcbLengthNeeded); + + case SFileMpqBlockTableSize64: + return GetInfo(pvFileInfo, cbFileInfo, &pHeader->BlockTableSize64, sizeof(ULONGLONG), pcbLengthNeeded); + + case SFileMpqBlockTableSize: + return GetInfo(pvFileInfo, cbFileInfo, &pHeader->dwBlockTableSize, sizeof(DWORD), pcbLengthNeeded); + + case SFileMpqBlockTable: + ByteOffset = FileOffsetFromMpqOffset(ha, MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos)); + if(ByteOffset >= ha->FileSize) + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); + cbSrcFileInfo = pHeader->dwBlockTableSize * sizeof(TMPQBlock); + pvSrcFileInfo = LoadBlockTable(ha, true); + return GetInfo_Allocated(pvFileInfo, cbFileInfo, pvSrcFileInfo, cbSrcFileInfo, pcbLengthNeeded); + + case SFileMpqHiBlockTableOffset: + return GetInfo(pvFileInfo, cbFileInfo, &pHeader->HiBlockTablePos64, sizeof(ULONGLONG), pcbLengthNeeded); + + case SFileMpqHiBlockTableSize64: + return GetInfo(pvFileInfo, cbFileInfo, &pHeader->HiBlockTableSize64, sizeof(ULONGLONG), pcbLengthNeeded); + + case SFileMpqHiBlockTable: + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); + + case SFileMpqSignatures: + if(!QueryMpqSignatureInfo(ha, &SignatureInfo)) + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); + return GetInfo(pvFileInfo, cbFileInfo, &SignatureInfo.SignatureTypes, sizeof(DWORD), pcbLengthNeeded); + + case SFileMpqStrongSignatureOffset: + if(QueryMpqSignatureInfo(ha, &SignatureInfo) == false || (SignatureInfo.SignatureTypes & SIGNATURE_TYPE_STRONG) == 0) + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); + return GetInfo(pvFileInfo, cbFileInfo, &SignatureInfo.EndMpqData, sizeof(ULONGLONG), pcbLengthNeeded); + + case SFileMpqStrongSignatureSize: + if(QueryMpqSignatureInfo(ha, &SignatureInfo) == false || (SignatureInfo.SignatureTypes & SIGNATURE_TYPE_STRONG) == 0) + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); + dwInt32Value = MPQ_STRONG_SIGNATURE_SIZE + 4; + return GetInfo(pvFileInfo, cbFileInfo, &dwInt32Value, sizeof(DWORD), pcbLengthNeeded); + + case SFileMpqStrongSignature: + if(QueryMpqSignatureInfo(ha, &SignatureInfo) == false || (SignatureInfo.SignatureTypes & SIGNATURE_TYPE_STRONG) == 0) + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); + return GetInfo(pvFileInfo, cbFileInfo, SignatureInfo.Signature, MPQ_STRONG_SIGNATURE_SIZE + 4, pcbLengthNeeded); + + case SFileMpqArchiveSize64: + return GetInfo(pvFileInfo, cbFileInfo, &pHeader->ArchiveSize64, sizeof(ULONGLONG), pcbLengthNeeded); + + case SFileMpqArchiveSize: + return GetInfo(pvFileInfo, cbFileInfo, &pHeader->dwArchiveSize, sizeof(DWORD), pcbLengthNeeded); + + case SFileMpqMaxFileCount: + return GetInfo(pvFileInfo, cbFileInfo, &ha->dwMaxFileCount, sizeof(DWORD), pcbLengthNeeded); + + case SFileMpqFileTableSize: + return GetInfo(pvFileInfo, cbFileInfo, &ha->dwFileTableSize, sizeof(DWORD), pcbLengthNeeded); + + case SFileMpqSectorSize: + return GetInfo(pvFileInfo, cbFileInfo, &ha->dwSectorSize, sizeof(DWORD), pcbLengthNeeded); + + case SFileMpqNumberOfFiles: + dwInt32Value = GetMpqFileCount(ha); + return GetInfo(pvFileInfo, cbFileInfo, &dwInt32Value, sizeof(DWORD), pcbLengthNeeded); + + case SFileMpqRawChunkSize: + if(pHeader->dwRawChunkSize == 0) + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); + return GetInfo(pvFileInfo, cbFileInfo, &pHeader->dwRawChunkSize, sizeof(DWORD), pcbLengthNeeded); + + case SFileMpqStreamFlags: + FileStream_GetFlags(ha->pStream, &dwInt32Value); + return GetInfo(pvFileInfo, cbFileInfo, &dwInt32Value, sizeof(DWORD), pcbLengthNeeded); + + case SFileMpqFlags: + return GetInfo(pvFileInfo, cbFileInfo, &ha->dwFlags, sizeof(DWORD), pcbLengthNeeded); + + case SFileInfoPatchChain: + return GetInfo_PatchChain(hf, pvFileInfo, cbFileInfo, pcbLengthNeeded); + + case SFileInfoFileEntry: + if(pFileEntry == NULL) + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); + return GetInfo_FileEntry(pvFileInfo, cbFileInfo, pFileEntry, pcbLengthNeeded); + + case SFileInfoHashEntry: + return GetInfo(pvFileInfo, cbFileInfo, hf->pHashEntry, sizeof(TMPQHash), pcbLengthNeeded); + + case SFileInfoHashIndex: + return GetInfo(pvFileInfo, cbFileInfo, &hf->dwHashIndex, sizeof(DWORD), pcbLengthNeeded); + + case SFileInfoNameHash1: + if(hf->pHashEntry == NULL) + return GetInfo_ReturnError(ERROR_INVALID_PARAMETER); + return GetInfo(pvFileInfo, cbFileInfo, &hf->pHashEntry->dwHashCheck1, sizeof(DWORD), pcbLengthNeeded); + + case SFileInfoNameHash2: + if(hf->pHashEntry == NULL) + return GetInfo_ReturnError(ERROR_INVALID_PARAMETER); + return GetInfo(pvFileInfo, cbFileInfo, &hf->pHashEntry->dwHashCheck2, sizeof(DWORD), pcbLengthNeeded); + + case SFileInfoNameHash3: + return GetInfo(pvFileInfo, cbFileInfo, &pFileEntry->FileNameHash, sizeof(ULONGLONG), pcbLengthNeeded); + + case SFileInfoLocale: + if(hf->pHashEntry == NULL) + return GetInfo_ReturnError(ERROR_INVALID_PARAMETER); + dwInt32Value = SFILE_MAKE_LCID(hf->pHashEntry->Locale, hf->pHashEntry->Platform); + return GetInfo(pvFileInfo, cbFileInfo, &dwInt32Value, sizeof(DWORD), pcbLengthNeeded); + + case SFileInfoFileIndex: + if(hf->ha == NULL) + return GetInfo_ReturnError(ERROR_INVALID_PARAMETER); + dwInt32Value = (DWORD)(pFileEntry - hf->ha->pFileTable); + return GetInfo(pvFileInfo, cbFileInfo, &dwInt32Value, sizeof(DWORD), pcbLengthNeeded); + + case SFileInfoByteOffset: + return GetInfo(pvFileInfo, cbFileInfo, &pFileEntry->ByteOffset, sizeof(ULONGLONG), pcbLengthNeeded); + + case SFileInfoFileTime: + return GetInfo(pvFileInfo, cbFileInfo, &pFileEntry->FileTime, sizeof(ULONGLONG), pcbLengthNeeded); + + case SFileInfoFileSize: + return GetInfo(pvFileInfo, cbFileInfo, &pFileEntry->dwFileSize, sizeof(DWORD), pcbLengthNeeded); + + case SFileInfoCompressedSize: + return GetInfo(pvFileInfo, cbFileInfo, &pFileEntry->dwCmpSize, sizeof(DWORD), pcbLengthNeeded); + + case SFileInfoFlags: + return GetInfo(pvFileInfo, cbFileInfo, &pFileEntry->dwFlags, sizeof(DWORD), pcbLengthNeeded); + + case SFileInfoEncryptionKey: + return GetInfo(pvFileInfo, cbFileInfo, &hf->dwFileKey, sizeof(DWORD), pcbLengthNeeded); + + case SFileInfoEncryptionKeyRaw: + if(pFileEntry == NULL) + return GetInfo_ReturnError(ERROR_INVALID_PARAMETER); + dwInt32Value = hf->dwFileKey; + if(pFileEntry->dwFlags & MPQ_FILE_KEY_V2) + dwInt32Value = (dwInt32Value ^ pFileEntry->dwFileSize) - (DWORD)hf->MpqFilePos; + return GetInfo(pvFileInfo, cbFileInfo, &dwInt32Value, sizeof(DWORD), pcbLengthNeeded); + + case SFileInfoCRC32: + return GetInfo(pvFileInfo, cbFileInfo, &hf->pFileEntry->dwCrc32, sizeof(DWORD), pcbLengthNeeded); + default: + // Invalid info class + return GetInfo_ReturnError(ERROR_INVALID_PARAMETER); + } +} + +bool WINAPI SFileFreeFileInfo(void * pvFileInfo, SFileInfoClass InfoClass) +{ + switch(InfoClass) + { + case SFileMpqHetTable: + FreeHetTable((TMPQHetTable *)pvFileInfo); + return true; + + case SFileMpqBetTable: + FreeBetTable((TMPQBetTable *)pvFileInfo); + return true; + + default: + break; + } + + SErrSetLastError(ERROR_INVALID_PARAMETER); + return false; +} + +//----------------------------------------------------------------------------- +// Tries to retrieve the file name + +struct TFileHeader2Ext +{ + DWORD dwOffset00Data; // Required data at offset 00 (32-bits) + DWORD dwOffset00Mask; // Mask for data at offset 00 (32 bits). 0 = data are ignored + DWORD dwOffset04Data; // Required data at offset 04 (32-bits) + DWORD dwOffset04Mask; // Mask for data at offset 04 (32 bits). 0 = data are ignored + const char * szExt; // Supplied extension, if the condition is true +}; + +static TFileHeader2Ext data2ext[] = +{ + {0x00005A4D, 0x0000FFFF, 0x00000000, 0x00000000, "exe"}, // EXE files + {0x00000006, 0xFFFFFFFF, 0x00000001, 0xFFFFFFFF, "dc6"}, // EXE files + {0x1A51504D, 0xFFFFFFFF, 0x00000000, 0x00000000, "mpq"}, // MPQ archive header ID ('MPQ\x1A') + {0x46464952, 0xFFFFFFFF, 0x00000000, 0x00000000, "wav"}, // WAVE header 'RIFF' + {0x324B4D53, 0xFFFFFFFF, 0x00000000, 0x00000000, "smk"}, // Old "Smacker Video" files 'SMK2' + {0x694B4942, 0xFFFFFFFF, 0x00000000, 0x00000000, "bik"}, // Bink video files (new) + {0x0801050A, 0xFFFFFFFF, 0x00000000, 0x00000000, "pcx"}, // PCX images used in Diablo I + {0x544E4F46, 0xFFFFFFFF, 0x00000000, 0x00000000, "fnt"}, // Font files used in Diablo II + {0x6D74683C, 0xFFFFFFFF, 0x00000000, 0x00000000, "html"}, // HTML 'ha->pFileTable), data2ext[i].szExt); + + // Save the pseudo-name in the file entry as well + AllocateFileName(hf->ha, pFileEntry, szPseudoName); + + // If the caller wants to copy the file name, do it + if(szFileName != NULL) + strcpy(szFileName, szPseudoName); + return ERROR_SUCCESS; + } + } + } + + return ERROR_CAN_NOT_COMPLETE; +} + +bool WINAPI SFileGetFileName(HANDLE hFile, char * szFileName) +{ + TMPQFile * hf; + DWORD dwErrCode = ERROR_INVALID_HANDLE; + + // Check valid parameters + if((hf = IsValidFileHandle(hFile)) != NULL) + { + TFileEntry * pFileEntry = hf->pFileEntry; + + // For MPQ files, retrieve the file name from the file entry + if(hf->pStream == NULL) + { + if(pFileEntry != NULL) + { + // If the file name is not there yet, create a pseudo name + if(pFileEntry->szFileName == NULL) + dwErrCode = CreatePseudoFileName(hFile, pFileEntry, szFileName); + + // Copy the file name to the output buffer, if any + if(pFileEntry->szFileName && szFileName) + { + StringCopy(szFileName, MAX_PATH, pFileEntry->szFileName); + dwErrCode = ERROR_SUCCESS; + } + } + } + + // For local files, copy the file name from the stream + else + { + if(szFileName != NULL) + { + const TCHAR * szStreamName = FileStream_GetFileName(hf->pStream); + StringCopy(szFileName, MAX_PATH, szStreamName); + } + dwErrCode = ERROR_SUCCESS; + } + } + + if(dwErrCode != ERROR_SUCCESS) + SErrSetLastError(dwErrCode); + return (dwErrCode == ERROR_SUCCESS); +} + diff --git a/dep/StormLib/src/SFileListFile.cpp b/dep/StormLib/src/SFileListFile.cpp index 4604206ed0..a7f813bd5b 100644 --- a/dep/StormLib/src/SFileListFile.cpp +++ b/dep/StormLib/src/SFileListFile.cpp @@ -17,199 +17,290 @@ // Listfile entry structure #define CACHE_BUFFER_SIZE 0x1000 // Size of the cache buffer +#define MAX_LISTFILE_SIZE 0xC000000 // Maximum accepted listfile size is 192 MB + +union TListFileHandle +{ + TFileStream * pStream; // Opened local file + HANDLE hFile; // Opened MPQ file +}; struct TListFileCache { - HANDLE hFile; // Stormlib file handle - char * szMask; // File mask - DWORD dwFileSize; // Total size of the cached file - DWORD dwFilePos; // Position of the cache in the file - BYTE * pBegin; // The begin of the listfile cache - BYTE * pPos; - BYTE * pEnd; // The last character in the file cache + char * szWildCard; // Self-relative pointer to file mask + LPBYTE pBegin; // The begin of the listfile cache + LPBYTE pPos; // Current position in the cache + LPBYTE pEnd; // The last character in the file cache + DWORD dwFlags; // Flags from TMPQArchive - BYTE Buffer[CACHE_BUFFER_SIZE]; // Listfile cache itself +// char szWildCard[wildcard_length]; // Followed by the name mask (if any) +// char szListFile[listfile_length]; // Followed by the listfile (if any) }; +typedef bool (*LOAD_LISTFILE)(TListFileHandle * pHandle, void * pvBuffer, DWORD cbBuffer, LPDWORD pdwBytesRead); + //----------------------------------------------------------------------------- // Local functions (cache) -static TListFileCache * CreateListFileCache(HANDLE hMpq, const char * szListFile) +// In SFileFindFile.cll +bool SFileCheckWildCard(const char * szString, const char * szWildCard); + +static char * CopyListLine(char * szListLine, const char * szFileName) +{ + // Copy the string + while(szFileName[0] != 0) + *szListLine++ = *szFileName++; + + // Append the end-of-line + *szListLine++ = 0x0D; + *szListLine++ = 0x0A; + return szListLine; +} + +static bool LoadListFile_Stream(TListFileHandle * pHandle, void * pvBuffer, DWORD cbBuffer, LPDWORD pdwBytesRead) +{ + ULONGLONG ByteOffset = 0; + bool bResult; + + bResult = FileStream_Read(pHandle->pStream, &ByteOffset, pvBuffer, cbBuffer); + if(bResult) + *pdwBytesRead = cbBuffer; + return bResult; +} + +static bool LoadListFile_MPQ(TListFileHandle * pHandle, void * pvBuffer, DWORD cbBuffer, LPDWORD pdwBytesRead) +{ + return SFileReadFile(pHandle->hFile, pvBuffer, cbBuffer, pdwBytesRead, NULL); +} + +static bool FreeListFileCache(TListFileCache * pCache) +{ + // Valid parameter check + if(pCache != NULL) + STORM_FREE(pCache); + return true; +} + +static TListFileCache * CreateListFileCache( + LOAD_LISTFILE PfnLoadFile, + TListFileHandle * pHandle, + const char * szWildCard, + DWORD dwFileSize, + DWORD dwMaxSize, + DWORD dwFlags) { TListFileCache * pCache = NULL; - HANDLE hListFile = NULL; - DWORD dwSearchScope = SFILE_OPEN_LOCAL_FILE; + size_t cchWildCardAligned = 0; + size_t cchWildCard = 0; DWORD dwBytesRead = 0; - int nError = ERROR_SUCCESS; - // If the szListFile is NULL, it means we have to open internal listfile - if(szListFile == NULL) + // Get the amount of bytes that need to be allocated + if(dwFileSize == 0 || dwFileSize > dwMaxSize) + return NULL; + + // Append buffer for name mask, if any + if(szWildCard != 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; + cchWildCard = strlen(szWildCard) + 1; + cchWildCardAligned = (cchWildCard + 3) & 0xFFFFFFFC; } - // 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 - if(nError == ERROR_SUCCESS) + pCache = (TListFileCache *)STORM_ALLOC(BYTE, sizeof(TListFileCache) + cchWildCardAligned + dwFileSize + 1); + if(pCache != NULL) { - pCache = (TListFileCache *)STORM_ALLOC(TListFileCache, 1); - if(pCache == NULL) - nError = ERROR_NOT_ENOUGH_MEMORY; - } + // Clear the entire structure + memset(pCache, 0, sizeof(TListFileCache) + cchWildCard); + pCache->dwFlags = dwFlags; - if(nError == ERROR_SUCCESS) - { - // Initialize the file cache - memset(pCache, 0, sizeof(TListFileCache)); - pCache->dwFileSize = SFileGetFileSize(hListFile, NULL); - pCache->hFile = hListFile; + // Shall we copy the mask? + if(cchWildCard != 0) + { + pCache->szWildCard = (char *)(pCache + 1); + memcpy(pCache->szWildCard, szWildCard, cchWildCard); + } - // Fill the cache - SFileReadFile(hListFile, pCache->Buffer, CACHE_BUFFER_SIZE, &dwBytesRead, NULL); - if(dwBytesRead == 0) - nError = GetLastError(); - } + // Fill-in the rest of the cache pointers + pCache->pBegin = (LPBYTE)(pCache + 1) + cchWildCardAligned; - // Initialize the pointers - if(nError == ERROR_SUCCESS) - { - pCache->pBegin = - pCache->pPos = &pCache->Buffer[0]; - pCache->pEnd = pCache->pBegin + dwBytesRead; - } - else - { - SListFileFindClose((HANDLE)pCache); - SetLastError(nError); - pCache = NULL; + // Load the entire listfile to the cache + PfnLoadFile(pHandle, pCache->pBegin, dwFileSize, &dwBytesRead); + if(dwBytesRead != 0) + { + // Allocate pointers + pCache->pPos = pCache->pBegin; + pCache->pEnd = pCache->pBegin + dwBytesRead; + } + else + { + FreeListFileCache(pCache); + pCache = NULL; + } } // Return the cache return pCache; } -// Reloads the cache. Returns number of characters -// that has been loaded into the cache. -static DWORD ReloadListFileCache(TListFileCache * pCache) +static TListFileCache * CreateListFileCache( + HANDLE hMpq, + const TCHAR * szListFile, + const char * szWildCard, + DWORD dwMaxSize, + DWORD dwFlags) { - DWORD dwBytesToRead; + TListFileCache * pCache = NULL; + TListFileHandle ListHandle = {NULL}; + + // Put default value to dwMaxSize + if(dwMaxSize == 0) + dwMaxSize = MAX_LISTFILE_SIZE; + + // Internal listfile: hMPQ must be non NULL and szListFile must be NULL. + // We load the MPQ::(listfile) file + if(hMpq != NULL && szListFile == NULL) + { + DWORD dwFileSize = 0; + + // Open the file from the MPQ + if(SFileOpenFileEx(hMpq, LISTFILE_NAME, 0, &ListHandle.hFile)) + { + // Get the file size and create the listfile cache + dwFileSize = SFileGetFileSize(ListHandle.hFile, NULL); + pCache = CreateListFileCache(LoadListFile_MPQ, &ListHandle, szWildCard, dwFileSize, dwMaxSize, dwFlags); + + // Close the MPQ file + SFileCloseFile(ListHandle.hFile); + } + + // Return the loaded cache + return pCache; + } + + // External listfile: hMpq must be NULL and szListFile must be non-NULL. + // We load the file using TFileStream + if(hMpq == NULL && szListFile != NULL) + { + ULONGLONG FileSize = 0; + + // Open the local file + ListHandle.pStream = FileStream_OpenFile(szListFile, STREAM_FLAG_READ_ONLY); + if(ListHandle.pStream != NULL) + { + // Verify the file size + FileStream_GetSize(ListHandle.pStream, &FileSize); + if(0 < FileSize && FileSize < dwMaxSize) + { + pCache = CreateListFileCache(LoadListFile_Stream, &ListHandle, szWildCard, (DWORD)FileSize, dwMaxSize, dwFlags); + } + + // Close the stream + FileStream_Close(ListHandle.pStream); + } + + // Return the loaded cache + return pCache; + } + + // This combination should never happen + SErrSetLastError(ERROR_INVALID_PARAMETER); + assert(false); + return NULL; +} + +#ifdef _DEBUG +/* +TMPQNameCache * CreateNameCache(HANDLE hListFile, const char * szSearchMask) +{ + TMPQNameCache * pNameCache; + char * szCachePointer; + size_t cbToAllocate; + size_t nMaskLength = 1; DWORD dwBytesRead = 0; + DWORD dwFileSize; - // Only do something if the cache is empty - if(pCache->pPos >= pCache->pEnd) + // Get the size of the listfile. Ignore zero or too long ones + dwFileSize = SFileGetFileSize(hListFile, NULL); + if(dwFileSize == 0 || dwFileSize > MAX_LISTFILE_SIZE) + return NULL; + + // Get the length of the search mask + if(szSearchMask == NULL) + szSearchMask = "*"; + nMaskLength = strlen(szSearchMask) + 1; + + // Allocate the name cache + cbToAllocate = sizeof(TMPQNameCache) + nMaskLength + dwFileSize + 1; + pNameCache = (TMPQNameCache *)STORM_ALLOC(BYTE, cbToAllocate); + if(pNameCache != NULL) { -// __TryReadBlock: + // Initialize the name cache + memset(pNameCache, 0, sizeof(TMPQNameCache)); + pNameCache->TotalCacheSize = (DWORD)(nMaskLength + dwFileSize + 1); + szCachePointer = (char *)(pNameCache + 1); - // Move the file position forward - pCache->dwFilePos += CACHE_BUFFER_SIZE; - if(pCache->dwFilePos >= pCache->dwFileSize) - return 0; + // Copy the search mask, if any + memcpy(szCachePointer, szSearchMask, nMaskLength); + pNameCache->FirstNameOffset = (DWORD)nMaskLength; + pNameCache->FreeSpaceOffset = (DWORD)nMaskLength; - // Get the number of bytes remaining - dwBytesToRead = pCache->dwFileSize - pCache->dwFilePos; - if(dwBytesToRead > CACHE_BUFFER_SIZE) - dwBytesToRead = CACHE_BUFFER_SIZE; + // Read the listfile itself + SFileSetFilePointer(hListFile, 0, NULL, FILE_BEGIN); + SFileReadFile(hListFile, szCachePointer + nMaskLength, dwFileSize, &dwBytesRead, NULL); - // Load the next data chunk to the cache - SFileSetFilePointer(pCache->hFile, pCache->dwFilePos, NULL, FILE_BEGIN); - SFileReadFile(pCache->hFile, pCache->Buffer, CACHE_BUFFER_SIZE, &dwBytesRead, NULL); - - // If we didn't read anything, it might mean that the block - // of the file is not available (in case of partial MPQs). - // We stop reading the file at this point, because the rest - // of the listfile is unreliable + // If nothing has been read from the listfile, clear the cache if(dwBytesRead == 0) - return 0; + { + STORM_FREE(pNameCache); + return NULL; + } - // Set the buffer pointers - pCache->pBegin = - pCache->pPos = &pCache->Buffer[0]; - pCache->pEnd = pCache->pBegin + dwBytesRead; + // Move the free space offset + pNameCache->FreeSpaceOffset = pNameCache->FirstNameOffset + dwBytesRead + 1; + szCachePointer[nMaskLength + dwBytesRead] = 0; } - return dwBytesRead; + return pNameCache; } -static size_t ReadListFileLine(TListFileCache * pCache, char * szLine, int nMaxChars) +static void FreeNameCache(TMPQNameCache * pNameCache) { - char * szLineBegin = szLine; - char * szLineEnd = szLine + nMaxChars - 1; - char * szExtraString = NULL; - - // Skip newlines, spaces, tabs and another non-printable stuff - for(;;) - { - // If we need to reload the cache, do it - if(pCache->pPos == pCache->pEnd) - { - if(ReloadListFileCache(pCache) == 0) - break; - } + if(pNameCache != NULL) + STORM_FREE(pNameCache); + pNameCache = NULL; +} +*/ +#endif // _DEBUG - // If we found a non-whitespace character, stop - if(*pCache->pPos > 0x20) - break; +static char * ReadListFileLine(TListFileCache * pCache, size_t * PtrLength) +{ + LPBYTE pbLineBegin; + LPBYTE pbLineEnd; - // Skip the character + // Skip newlines. Keep spaces and tabs, as they can be a legal part of the file name + while(pCache->pPos < pCache->pEnd && (pCache->pPos[0] == 0x0A || pCache->pPos[0] == 0x0D)) pCache->pPos++; - } - // Copy the remaining characters - while(szLine < szLineEnd) - { - // If we need to reload the cache, do it now and resume copying - if(pCache->pPos == pCache->pEnd) - { - if(ReloadListFileCache(pCache) == 0) - break; - } + // Set the line begin and end + if(pCache->pPos >= pCache->pEnd) + return NULL; + pbLineBegin = pbLineEnd = pCache->pPos; - // If we have found a newline, stop loading - if(*pCache->pPos == 0x0D || *pCache->pPos == 0x0A) - break; + // Find the end of the line + while(pCache->pPos < pCache->pEnd && pCache->pPos[0] != 0x0A && pCache->pPos[0] != 0x0D) + pCache->pPos++; - // Blizzard listfiles can also contain information about patch: - // Pass1\Files\MacOS\unconditional\user\Background Downloader.app\Contents\Info.plist~Patch(Data#frFR#base-frFR,1326) - if(*pCache->pPos == '~') - szExtraString = szLine; + // Remember the end of the line + pbLineEnd = pCache->pPos++; + pbLineEnd[0] = 0; - // Copy the character - *szLine++ = *pCache->pPos++; - } - - // Terminate line with zero - *szLine = 0; - - // If there was extra string after the file name, clear it - if(szExtraString != NULL) - { - if(szExtraString[0] == '~' && szExtraString[1] == 'P') - { - szLine = szExtraString; - *szExtraString = 0; - } - } - - // Return the length of the line - return (szLine - szLineBegin); + // Give the line to the caller + if(PtrLength != NULL) + PtrLength[0] = (size_t)(pbLineEnd - pbLineBegin); + return (char *)pbLineBegin; } -static int CompareFileNodes(const void * p1, const void * p2) +static int STORMLIB_CDECL CompareFileNodes(const void * p1, const void * p2) { char * szFileName1 = *(char **)p1; char * szFileName2 = *(char **)p2; @@ -217,19 +308,96 @@ static int CompareFileNodes(const void * p1, const void * p2) return _stricmp(szFileName1, szFileName2); } -static int WriteListFileLine( - TMPQFile * hf, - const char * szLine) +static LPBYTE CreateListFile(TMPQArchive * ha, DWORD * pcbListFile) { - char szNewLine[2] = {0x0D, 0x0A}; - size_t nLength = strlen(szLine); - int nError; + TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; + TFileEntry * pFileEntry; + char ** SortTable = NULL; + char * szListFile = NULL; + char * szListLine; + size_t nFileNodes = 0; + size_t cbListFile = 0; + size_t nIndex0; + size_t nIndex1; - nError = SFileAddFile_Write(hf, szLine, (DWORD)nLength, MPQ_COMPRESSION_ZLIB); - if(nError != ERROR_SUCCESS) - return nError; + // Allocate the table for sorting listfile + SortTable = STORM_ALLOC(char*, ha->dwFileTableSize); + if(SortTable == NULL) + return NULL; - return SFileAddFile_Write(hf, szNewLine, sizeof(szNewLine), MPQ_COMPRESSION_ZLIB); + // Construct the sort table + // Note: in MPQs with multiple locale versions of the same file, + // this code causes adding multiple listfile entries. + // They will get removed after the listfile sorting + for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) + { + // Only take existing items + if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) && pFileEntry->szFileName != NULL) + { + // Ignore pseudo-names and internal names + if(!IsPseudoFileName(pFileEntry->szFileName, NULL) && !IsInternalMpqFileName(pFileEntry->szFileName)) + { + SortTable[nFileNodes++] = pFileEntry->szFileName; + } + } + } + + // Remove duplicities + if(nFileNodes > 0) + { + // Sort the table + qsort(SortTable, nFileNodes, sizeof(char *), CompareFileNodes); + + // Count the 0-th item + cbListFile += strlen(SortTable[0]) + 2; + + // Walk through the items and only use the ones that are not duplicated + for(nIndex0 = 0, nIndex1 = 1; nIndex1 < nFileNodes; nIndex1++) + { + // If the next file node is different, we will include it to the result listfile + if(_stricmp(SortTable[nIndex1], SortTable[nIndex0]) != 0) + { + cbListFile += strlen(SortTable[nIndex1]) + 2; + nIndex0 = nIndex1; + } + } + + // Now allocate buffer for the entire listfile + szListFile = szListLine = STORM_ALLOC(char, cbListFile + 1); + if(szListFile != NULL) + { + // Copy the 0-th item + szListLine = CopyListLine(szListLine, SortTable[0]); + + // Walk through the items and only use the ones that are not duplicated + for(nIndex0 = 0, nIndex1 = 1; nIndex1 < nFileNodes; nIndex1++) + { + // If the next file node is different, we will include it to the result listfile + if(_stricmp(SortTable[nIndex1], SortTable[nIndex0]) != 0) + { + // Copy the listfile line + szListLine = CopyListLine(szListLine, SortTable[nIndex1]); + nIndex0 = nIndex1; + } + } + + // Sanity check - does the size match? + assert((size_t)(szListLine - szListFile) == cbListFile); + } + } + else + { + szListFile = STORM_ALLOC(char, 1); + cbListFile = 0; + } + + // Free the sort table + STORM_FREE(SortTable); + + // Give away the listfile + if(pcbListFile != NULL) + *pcbListFile = (DWORD)cbListFile; + return (LPBYTE)szListFile; } //----------------------------------------------------------------------------- @@ -238,200 +406,269 @@ 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 DWORD SListFileCreateNodeForAllLocales(TMPQArchive * ha, const char * szFileName) { - TMPQHeader * pHeader = ha->pHeader; TFileEntry * pFileEntry; - TMPQHash * pFirstHash; + TMPQHash * pHashEnd; TMPQHash * pHash; - bool bNameEntryCreated = false; + DWORD dwHashCheck1; + DWORD dwHashCheck2; // If we have HET table, use that one if(ha->pHetTable != NULL) { - pFileEntry = GetFileEntryAny(ha, szFileName); + pFileEntry = GetFileEntryLocale(ha, szFileName, 0); if(pFileEntry != NULL) { // Allocate file name for the file entry - AllocateFileName(pFileEntry, szFileName); - bNameEntryCreated = true; + AllocateFileName(ha, pFileEntry, szFileName); } return ERROR_SUCCESS; } // If we have hash table, we use it - if(bNameEntryCreated == false && ha->pHashTable != NULL) + if(ha->pHashTable != NULL) { - // Look for the first hash table entry for the file - pFirstHash = pHash = GetFirstHashEntry(ha, szFileName); + // Get the end of the hash table and both names + pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize; + dwHashCheck1 = ha->pfnHashString(szFileName, MPQ_HASH_NAME_A); + dwHashCheck2 = ha->pfnHashString(szFileName, MPQ_HASH_NAME_B); - // Go while we found something - while(pHash != NULL) + // Some protectors set very high hash table size (0x00400000 items or more) + // in order to make this process very slow. We will ignore items + // in the hash table that would be beyond the end of the file. + // Example MPQ: MPQ_2022_v1_Sniper.scx + if(ha->dwFlags & MPQ_FLAG_HASH_TABLE_CUT) + pHashEnd = ha->pHashTable + (ha->dwRealHashTableSize / sizeof(TMPQHash)); + + // Go through the hash table and put the name in each item that has the same name pair + for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++) { - // Is it a valid file table index ? - if(pHash->dwBlockIndex < pHeader->dwBlockTableSize) + if(pHash->dwHashCheck1 == dwHashCheck1 && pHash->dwHashCheck2 == dwHashCheck2 && MPQ_BLOCK_INDEX(pHash) < ha->dwFileTableSize) { // Allocate file name for the file entry - AllocateFileName(ha->pFileTable + pHash->dwBlockIndex, szFileName); - bNameEntryCreated = true; + AllocateFileName(ha, ha->pFileTable + MPQ_BLOCK_INDEX(pHash), szFileName); } - - // Now find the next language version of the file - pHash = GetNextHashEntry(ha, pFirstHash, pHash); } + + // Go while we found something + //pFirstHash = pHash = GetFirstHashEntry(ha, szFileName); + //while(pHash != NULL) + //{ + // // Allocate file name for the file entry + // AllocateFileName(ha, ha->pFileTable + MPQ_BLOCK_INDEX(pHash), szFileName); + + // // Now find the next language version of the file + // pHash = GetNextHashEntry(ha, pFirstHash, pHash); + //} + + return ERROR_SUCCESS; } return ERROR_CAN_NOT_COMPLETE; } -// Saves the whole listfile into the MPQ. -int SListFileSaveToMpq(TMPQArchive * ha) +// Saves the whole listfile to the MPQ +DWORD SListFileSaveToMpq(TMPQArchive * ha) { - TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; - TFileEntry * pFileEntry; TMPQFile * hf = NULL; - char * szPrevItem; - char ** SortTable = NULL; - DWORD dwFileSize = 0; - size_t nFileNodes = 0; - size_t i; - int nError = ERROR_SUCCESS; + LPBYTE pbListFile; + DWORD cbListFile = 0; + DWORD dwErrCode = ERROR_SUCCESS; - // Allocate the table for sorting listfile - SortTable = STORM_ALLOC(char*, ha->dwFileTableSize); - if(SortTable == NULL) - return ERROR_NOT_ENOUGH_MEMORY; - - // Construct the sort table - // Note: in MPQs with multiple locale versions of the same file, - // this code causes adding multiple listfile entries. - // Since those MPQs were last time used in Starcraft, - // we leave it as it is. - for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) + // Only save the listfile if we should do so + if(ha->dwFileFlags1 != 0) { - // Only take existing items - if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) && pFileEntry->szFileName != NULL) + // At this point, we expect to have at least one reserved entry in the file table + assert(ha->dwFlags & MPQ_FLAG_LISTFILE_NEW); + assert(ha->dwReservedFiles > 0); + + // Create the raw data that is to be written to (listfile) + // Note: Creating the raw data before the (listfile) has been created in the MPQ + // causes that the name of the listfile will not be included in the listfile itself. + // That is OK, because (listfile) in Blizzard MPQs does not contain it either. + pbListFile = CreateListFile(ha, &cbListFile); + if(pbListFile != NULL) { - // Ignore pseudo-names - if(!IsPseudoFileName(pFileEntry->szFileName, NULL) && !IsInternalMpqFileName(pFileEntry->szFileName)) + // Determine the real flags for (listfile) + if(ha->dwFileFlags1 == MPQ_FILE_DEFAULT_INTERNAL) + ha->dwFileFlags1 = GetDefaultSpecialFileFlags(cbListFile, ha->pHeader->wFormatVersion); + + // Create the listfile in the MPQ + dwErrCode = SFileAddFile_Init(ha, LISTFILE_NAME, + 0, + cbListFile, + LANG_NEUTRAL, + ha->dwFileFlags1 | MPQ_FILE_REPLACEEXISTING, + &hf); + + // Write the listfile raw data to it + if(dwErrCode == ERROR_SUCCESS) { - SortTable[nFileNodes++] = pFileEntry->szFileName; + // Write the content of the listfile to the MPQ + dwErrCode = SFileAddFile_Write(hf, pbListFile, cbListFile, MPQ_COMPRESSION_ZLIB); + SFileAddFile_Finish(hf); + } + + // Clear the listfile flags + ha->dwFlags &= ~(MPQ_FLAG_LISTFILE_NEW | MPQ_FLAG_LISTFILE_NONE); + ha->dwReservedFiles--; + + // Free the listfile buffer + STORM_FREE(pbListFile); + } + else + { + // If the (listfile) file would be empty, its OK + dwErrCode = (cbListFile == 0) ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY; + } + } + + return dwErrCode; +} + +static DWORD SFileAddArbitraryListFile( + TMPQArchive * ha, + HANDLE hMpq, + const TCHAR * szListFile, + DWORD dwMaxSize) +{ + TListFileCache * pCache = NULL; + + // Create the listfile cache for that file + pCache = CreateListFileCache(hMpq, szListFile, NULL, dwMaxSize, ha->dwFlags); + if(pCache != NULL) + { + char * szFileName; + size_t nLength = 0; + + // Get the next line + while((szFileName = ReadListFileLine(pCache, &nLength)) != NULL) + { + // Add the line to the MPQ + if(nLength != 0) + SListFileCreateNodeForAllLocales(ha, szFileName); + } + + // Delete the cache + FreeListFileCache(pCache); + } + + return (pCache != NULL) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT; +} + +static DWORD SFileAddArbitraryListFile( + TMPQArchive * ha, + const char ** listFileEntries, + DWORD dwEntryCount) +{ + if(listFileEntries != NULL && dwEntryCount > 0) + { + // Get the next line + for(DWORD dwListFileNum = 0; dwListFileNum < dwEntryCount; dwListFileNum++) + { + const char * listFileEntry = listFileEntries[dwListFileNum]; + if(listFileEntry != NULL) + { + SListFileCreateNodeForAllLocales(ha, listFileEntry); } } } - // Sort the table - qsort(SortTable, nFileNodes, sizeof(char *), CompareFileNodes); + return (listFileEntries != NULL && dwEntryCount > 0) ? ERROR_SUCCESS : ERROR_INVALID_PARAMETER; +} - // Now parse the table of file names again - remove duplicates - // and count file size. - if(nFileNodes != 0) +static DWORD SFileAddInternalListFile( + TMPQArchive * ha, + HANDLE hMpq) +{ + TMPQHash * pFirstHash; + TMPQHash * pHash; + LCID lcSaveLocale = g_lcFileLocale; + DWORD dwMaxSize = MAX_LISTFILE_SIZE; + DWORD dwErrCode = ERROR_SUCCESS; + + // If there is hash table, we need to support multiple listfiles + // with different locales (BrooDat.mpq) + if(ha->pHashTable != NULL) { - // Count the 0-th item - dwFileSize += (DWORD)strlen(SortTable[0]) + 2; - szPrevItem = SortTable[0]; - - // Count all next items - for(i = 1; i < nFileNodes; i++) + // If the archive is a malformed map, ignore too large listfiles + if(STORMLIB_TEST_FLAGS(ha->dwFlags, MPQ_FLAG_MALFORMED | MPQ_FLAG_PATCH, MPQ_FLAG_MALFORMED)) + dwMaxSize = 0x40000; + + pFirstHash = pHash = GetFirstHashEntry(ha, LISTFILE_NAME); + while(dwErrCode == ERROR_SUCCESS && pHash != NULL) { - // If the item is the same like the last one, skip it - if(_stricmp(SortTable[i], szPrevItem)) - { - dwFileSize += (DWORD)strlen(SortTable[i]) + 2; - szPrevItem = SortTable[i]; - } + // Set the prefered locale to that from list file + SFileSetLocale(SFILE_MAKE_LCID(pHash->Locale, pHash->Platform)); + + // Add that listfile + dwErrCode = SFileAddArbitraryListFile(ha, hMpq, NULL, dwMaxSize); + + // Move to the next hash + pHash = GetNextHashEntry(ha, pFirstHash, pHash); } - // Determine the flags for (listfile) - if(ha->dwFileFlags1 == 0) - ha->dwFileFlags1 = GetDefaultSpecialFileFlags(ha, dwFileSize); - - // Create the listfile in the MPQ - nError = SFileAddFile_Init(ha, LISTFILE_NAME, - 0, - dwFileSize, - LANG_NEUTRAL, - ha->dwFileFlags1 | MPQ_FILE_REPLACEEXISTING, - &hf); - // Add all file names - if(nError == ERROR_SUCCESS) - { - // Each name is followed by newline ("\x0D\x0A") - szPrevItem = SortTable[0]; - nError = WriteListFileLine(hf, SortTable[0]); - - // Count all next items - for(i = 1; i < nFileNodes; i++) - { - // If the item is the same like the last one, skip it - if(_stricmp(SortTable[i], szPrevItem)) - { - WriteListFileLine(hf, SortTable[i]); - szPrevItem = SortTable[i]; - } - } - } + // Restore the original locale + SFileSetLocale(lcSaveLocale); } else { - // Create the listfile in the MPQ - dwFileSize = (DWORD)strlen(LISTFILE_NAME) + 2; - nError = SFileAddFile_Init(ha, LISTFILE_NAME, - 0, - dwFileSize, - LANG_NEUTRAL, - MPQ_FILE_ENCRYPTED | MPQ_FILE_COMPRESS | MPQ_FILE_REPLACEEXISTING, - &hf); + // Add the single listfile + dwErrCode = SFileAddArbitraryListFile(ha, hMpq, NULL, dwMaxSize); + } - // Just add "(listfile)" there - if(nError == ERROR_SUCCESS) + // Return the result of the operation + return dwErrCode; +} + +static bool DoListFileSearch(TListFileCache * pCache, SFILE_FIND_DATA * lpFindFileData) +{ + // Check for the valid search handle + if(pCache != NULL) + { + char * szFileName; + size_t nLength = 0; + + // Get the next line + while((szFileName = ReadListFileLine(pCache, &nLength)) != NULL) { - WriteListFileLine(hf, LISTFILE_NAME); + // Check search mask + if(nLength != 0 && SFileCheckWildCard(szFileName, pCache->szWildCard)) + { + if(nLength >= sizeof(lpFindFileData->cFileName)) + nLength = sizeof(lpFindFileData->cFileName) - 1; + + memcpy(lpFindFileData->cFileName, szFileName, nLength); + lpFindFileData->cFileName[nLength] = 0; + return true; + } } } - // Finalize the file in the MPQ - if(hf != NULL) - { - SFileAddFile_Finish(hf); - } - - // Free buffers - if(nError == ERROR_SUCCESS) - ha->dwFlags &= ~MPQ_FLAG_INV_LISTFILE; - if(SortTable != NULL) - STORM_FREE(SortTable); - return nError; + // No more files + memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA)); + SErrSetLastError(ERROR_NO_MORE_FILES); + return false; } //----------------------------------------------------------------------------- // 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) +DWORD WINAPI SFileAddListFile(HANDLE hMpq, const TCHAR * szListFile) { - TListFileCache * pCache = NULL; TMPQArchive * ha = (TMPQArchive *)hMpq; - char szFileName[MAX_PATH]; - size_t nLength = 0; - int nError = ERROR_SUCCESS; + DWORD dwErrCode = 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) + dwErrCode = SFileAddArbitraryListFile(ha, NULL, szListFile, MAX_LISTFILE_SIZE); + else + dwErrCode = SFileAddInternalListFile(ha, hMpq); // Also, add three special files to the listfile: // (listfile) itself, (attributes) and (signature) @@ -439,119 +676,75 @@ 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; } - return nError; + return dwErrCode; +} + +DWORD WINAPI SFileAddListFileEntries(HANDLE hMpq, const char ** listFileEntries, DWORD dwEntryCount) +{ + TMPQArchive * ha = (TMPQArchive *)hMpq; + DWORD dwErrCode = ERROR_SUCCESS; + + // Add the listfile for each MPQ in the patch chain + while(ha != NULL) + { + if(listFileEntries != NULL && dwEntryCount > 0) + dwErrCode = SFileAddArbitraryListFile(ha, listFileEntries, dwEntryCount); + else + dwErrCode = SFileAddInternalListFile(ha, hMpq); + + // Also, add three special files to the listfile: + // (listfile) itself, (attributes) and (signature) + SListFileCreateNodeForAllLocales(ha, LISTFILE_NAME); + SListFileCreateNodeForAllLocales(ha, SIGNATURE_NAME); + SListFileCreateNodeForAllLocales(ha, ATTRIBUTES_NAME); + + // Move to the next archive in the chain + ha = ha->haPatch; + } + + return dwErrCode; } //----------------------------------------------------------------------------- -// Passing through the listfile +// Enumerating files in listfile -HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const char * szMask, SFILE_FIND_DATA * lpFindFileData) +HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const TCHAR * szListFile, const char * szMask, SFILE_FIND_DATA * lpFindFileData) { TListFileCache * pCache = NULL; - size_t nLength = 0; - 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) - nError = GetLastError(); - - // Allocate file mask - if(nError == ERROR_SUCCESS && szMask != NULL) + // Open the local/internal listfile + pCache = CreateListFileCache(hMpq, szListFile, szMask, 0, 0); + if(pCache != NULL) { - pCache->szMask = STORM_ALLOC(char, strlen(szMask) + 1); - if(pCache->szMask != NULL) - strcpy(pCache->szMask, szMask); - else - nError = ERROR_NOT_ENOUGH_MEMORY; - } - - // Perform file search - if(nError == ERROR_SUCCESS) - { - for(;;) + if(!DoListFileSearch(pCache, lpFindFileData)) { - // Read the (next) line - nLength = ReadListFileLine(pCache, lpFindFileData->cFileName, sizeof(lpFindFileData->cFileName)); - if(nLength == 0) - { - nError = ERROR_NO_MORE_FILES; - break; - } - - // If some mask entered, check it - if(CheckWildCard(lpFindFileData->cFileName, pCache->szMask)) - break; + memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA)); + SErrSetLastError(ERROR_NO_MORE_FILES); + FreeListFileCache(pCache); + pCache = NULL; } } - // Cleanup & exit - if(nError != ERROR_SUCCESS) - { - memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA)); - SListFileFindClose((HANDLE)pCache); - pCache = NULL; - - SetLastError(nError); - } + // Return the listfile cache as handle return (HANDLE)pCache; } bool WINAPI SListFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData) { - TListFileCache * pCache = (TListFileCache *)hFind; - size_t nLength; - bool bResult = false; - int nError = ERROR_SUCCESS; - - for(;;) - { - // Read the (next) line - nLength = ReadListFileLine(pCache, lpFindFileData->cFileName, sizeof(lpFindFileData->cFileName)); - if(nLength == 0) - { - nError = ERROR_NO_MORE_FILES; - break; - } - - // If some mask entered, check it - if(CheckWildCard(lpFindFileData->cFileName, pCache->szMask)) - { - bResult = true; - break; - } - } - - if(nError != ERROR_SUCCESS) - SetLastError(nError); - return bResult; + return DoListFileSearch((TListFileCache *)hFind, lpFindFileData); } bool WINAPI SListFileFindClose(HANDLE hFind) { TListFileCache * pCache = (TListFileCache *)hFind; - if(pCache != NULL) - { - if(pCache->hFile != NULL) - SFileCloseFile(pCache->hFile); - if(pCache->szMask != NULL) - STORM_FREE(pCache->szMask); - - STORM_FREE(pCache); - return true; - } - - return false; + return FreeListFileCache(pCache); } diff --git a/dep/StormLib/src/SFileOpenArchive.cpp b/dep/StormLib/src/SFileOpenArchive.cpp index b36b7d35ea..efa5821639 100644 --- a/dep/StormLib/src/SFileOpenArchive.cpp +++ b/dep/StormLib/src/SFileOpenArchive.cpp @@ -5,11 +5,11 @@ /* E-mail : ladik@zezula.net */ /* WWW : www.zezula.net */ /*---------------------------------------------------------------------------*/ -/* Archive functions of Storm.dll */ +/* Implementation of archive functions */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ -/* xx.xx.xx 1.00 Lad The first version of SFileOpenArchive.cpp */ +/* xx.xx.xx 1.00 Lad Created */ /* 19.11.03 1.01 Dan Big endian handling */ /*****************************************************************************/ @@ -17,54 +17,158 @@ #include "StormLib.h" #include "StormCommon.h" -/*****************************************************************************/ -/* Local functions */ -/*****************************************************************************/ +#define HEADER_SEARCH_BUFFER_SIZE 0x1000 -static bool IsAviFile(void * pvFileBegin) +//----------------------------------------------------------------------------- +// Local functions + +#ifndef IMAGE_DOS_SIGNATURE + +#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ +#define IMAGE_FILE_DLL 0x2000 // File is a DLL. + +typedef struct _IMAGE_DOS_HEADER { - LPDWORD AviHeader = (DWORD *)pvFileBegin; - DWORD DwordValue0 = BSWAP_INT32_UNSIGNED(AviHeader[0]); - DWORD DwordValue2 = BSWAP_INT32_UNSIGNED(AviHeader[2]); - DWORD DwordValue3 = BSWAP_INT32_UNSIGNED(AviHeader[3]); + USHORT e_magic; + USHORT dummy[0x1D]; + DWORD e_lfanew; +} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; - // Test for 'RIFF', 'AVI ' or 'LIST' - return (DwordValue0 == 0x46464952 && DwordValue2 == 0x20495641 && DwordValue3 == 0x5453494C); -} - -static TFileBitmap * CreateFileBitmap(TMPQArchive * ha, TMPQBitmap * pMpqBitmap, bool bFileIsComplete) +typedef struct _IMAGE_FILE_HEADER { - TFileBitmap * pBitmap; - size_t nLength; + USHORT Machine; + USHORT NumberOfSections; + DWORD TimeDateStamp; + DWORD PointerToSymbolTable; + DWORD NumberOfSymbols; + USHORT SizeOfOptionalHeader; + USHORT Characteristics; +} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; +#endif - // 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) +static MTYPE CheckMapType(LPCTSTR szFileName, LPBYTE pbHeaderBuffer, size_t cbHeaderBuffer) +{ + LPDWORD HeaderInt32 = (LPDWORD)pbHeaderBuffer; + LPCTSTR szExtension; + MTYPE TypeByExtension = MapTypeNotRecognized; + + // Don't do any checks if there is not at least 16 bytes + if(cbHeaderBuffer > 0x10) { - // 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; + DWORD DwordValue0 = BSWAP_INT32_UNSIGNED(HeaderInt32[0]); + DWORD DwordValue1 = BSWAP_INT32_UNSIGNED(HeaderInt32[1]); + DWORD DwordValue2 = BSWAP_INT32_UNSIGNED(HeaderInt32[2]); + DWORD DwordValue3 = BSWAP_INT32_UNSIGNED(HeaderInt32[3]); - // Copy the file bitmap - memcpy((pBitmap + 1), (pMpqBitmap + 1), nLength); + // Check maps by extension (Starcraft, Starcraft II). We must do this before + // checking actual data, because the "NP_Protect" protector places + // fake Warcraft III header into the Starcraft II maps + if((szExtension = _tcsrchr(szFileName, _T('.'))) != NULL) + { + // Check for Starcraft I maps by extension + if(!_tcsicmp(szExtension, _T(".scm")) || !_tcsicmp(szExtension, _T(".scx"))) + { + return MapTypeStarcraft; + } + + // Check for Starcraft II maps by extension + if(!_tcsicmp(szExtension, _T(".s2ma")) || !_tcsicmp(szExtension, _T(".SC2Map")) || !_tcsicmp(szExtension, _T(".SC2Mod"))) + { + return MapTypeStarcraft2; + } + + // Check for Warcraft III maps by extension + if(!_tcsicmp(szExtension, _T(".w3m")) || !_tcsicmp(szExtension, _T(".w3x"))) + { + TypeByExtension = MapTypeWarcraft3; + } + } + + // Test for AVI files (Warcraft III cinematics) - 'RIFF', 'AVI ' or 'LIST' + if(DwordValue0 == 0x46464952 && DwordValue2 == 0x20495641 && DwordValue3 == 0x5453494C) + return MapTypeAviFile; + + // Check for Warcraft III maps + if(DwordValue0 == 0x57334D48 && DwordValue1 == 0x00000000) + return MapTypeWarcraft3; } - return pBitmap; + // MIX files are DLL files that contain MPQ in overlay. + // Only Warcraft III is able to load them, so we consider them Warcraft III maps + // Do not include EXE files, because they may be World of Warcraft patches + if(cbHeaderBuffer > sizeof(IMAGE_DOS_HEADER)) + { + PIMAGE_FILE_HEADER pFileHeader; + PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)(pbHeaderBuffer); + size_t dwMaxAllowedSize = cbHeaderBuffer - sizeof(DWORD) - sizeof(IMAGE_FILE_HEADER); + + // Verify the header of EXE/DLL files + if((pDosHeader->e_magic == IMAGE_DOS_SIGNATURE) && (0 < pDosHeader->e_lfanew && pDosHeader->e_lfanew < 0x10000)) + { + // Is the file an EXE? + if((size_t)pDosHeader->e_lfanew <= dwMaxAllowedSize) + { + pFileHeader = (PIMAGE_FILE_HEADER)(pbHeaderBuffer + pDosHeader->e_lfanew + sizeof(DWORD)); + if(pFileHeader->Characteristics & IMAGE_FILE_DLL) + { + return MapTypeWarcraft3; + } + } + } + } + + // No special map type recognized + return TypeByExtension; +} + +static bool IsStarcraftBetaArchive(TMPQHeader * pHeader) +{ + // The archive must be version 1, with a standard header size + if(pHeader->dwID == ID_MPQ && pHeader->dwHeaderSize == MPQ_HEADER_SIZE_V1) + { + // Check for known archive sizes + return (pHeader->dwArchiveSize == 0x00028FB3 || // patch_rt.mpq + pHeader->dwArchiveSize == 0x0351853D || // StarDat.mpq + pHeader->dwArchiveSize == 0x0AEC8960); // INSTALL.exe + + } + return false; +} + +static TMPQUserData * IsValidMpqUserData(ULONGLONG ByteOffset, ULONGLONG FileSize, void * pvUserData) +{ + TMPQUserData * pUserData; + + // BSWAP the source data and copy them to our buffer + BSWAP_ARRAY32_UNSIGNED(pvUserData, sizeof(TMPQUserData)); + pUserData = (TMPQUserData *)pvUserData; + + // Check the sizes + if(pUserData->cbUserDataHeader <= pUserData->cbUserDataSize && pUserData->cbUserDataSize <= pUserData->dwHeaderOffs) + { + // Move to the position given by the userdata + ByteOffset += pUserData->dwHeaderOffs; + + // The MPQ header should be within range of the file size + if((ByteOffset + MPQ_HEADER_SIZE_V1) < FileSize) + { + // Note: We should verify if there is the MPQ header. + // However, the header could be at any position below that + // that is multiplier of 0x200 + return (TMPQUserData *)pvUserData; + } + } + + return NULL; } // This function gets the right positions of the hash table and the block table. -static int VerifyMpqTablePositions(TMPQArchive * ha, ULONGLONG FileSize) +static DWORD VerifyMpqTablePositions(TMPQArchive * ha, ULONGLONG FileSize) { TMPQHeader * pHeader = ha->pHeader; ULONGLONG ByteOffset; + //bool bMalformed = (ha->dwFlags & MPQ_FLAG_MALFORMED) ? true : false; // Check the begin of HET table if(pHeader->HetTablePos64) @@ -85,7 +189,7 @@ static int VerifyMpqTablePositions(TMPQArchive * ha, ULONGLONG FileSize) // Check the begin of hash table if(pHeader->wHashTablePosHi || pHeader->dwHashTablePos) { - ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos); + ByteOffset = FileOffsetFromMpqOffset(ha, MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos)); if(ByteOffset > FileSize) return ERROR_BAD_FORMAT; } @@ -93,27 +197,55 @@ static int VerifyMpqTablePositions(TMPQArchive * ha, ULONGLONG FileSize) // Check the begin of block table if(pHeader->wBlockTablePosHi || pHeader->dwBlockTablePos) { - ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); + ByteOffset = FileOffsetFromMpqOffset(ha, MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos)); if(ByteOffset > FileSize) return ERROR_BAD_FORMAT; } // Check the begin of hi-block table - if(pHeader->HiBlockTablePos64 != 0) - { - ByteOffset = ha->MpqPos + pHeader->HiBlockTablePos64; - if(ByteOffset > FileSize) - return ERROR_BAD_FORMAT; - } + //if(pHeader->HiBlockTablePos64 != 0) + //{ + // ByteOffset = ha->MpqPos + pHeader->HiBlockTablePos64; + // if(ByteOffset > FileSize) + // return ERROR_BAD_FORMAT; + //} // All OK. return ERROR_SUCCESS; } +//----------------------------------------------------------------------------- +// Support for alternate markers. Call before opening an archive -/*****************************************************************************/ -/* Public functions */ -/*****************************************************************************/ +#define SFILE_MARKERS_MIN_SIZE (sizeof(DWORD) + sizeof(DWORD) + sizeof(const char *) + sizeof(const char *)) + +bool WINAPI SFileSetArchiveMarkers(PSFILE_MARKERS pMarkers) +{ + // Check structure minimum size + if(pMarkers == NULL || pMarkers->dwSize < SFILE_MARKERS_MIN_SIZE) + { + SErrSetLastError(ERROR_INVALID_PARAMETER); + return false; + } + + // Make sure that the MPQ cryptography is initialized at this time + InitializeMpqCryptography(); + + // Remember the marker for MPQ header + if(pMarkers->dwSignature != 0) + g_dwMpqSignature = pMarkers->dwSignature; + + // Remember the encryption key for hash table + if(pMarkers->szHashTableKey != NULL) + g_dwHashTableKey = HashString(pMarkers->szHashTableKey, MPQ_HASH_FILE_KEY); + + // Remember the encryption key for block table + if(pMarkers->szBlockTableKey != NULL) + g_dwBlockTableKey = HashString(pMarkers->szBlockTableKey, MPQ_HASH_FILE_KEY); + + // Succeeded + return true; +} //----------------------------------------------------------------------------- // SFileGetLocale and SFileSetLocale @@ -121,13 +253,12 @@ static int VerifyMpqTablePositions(TMPQArchive * ha, ULONGLONG FileSize) LCID WINAPI SFileGetLocale() { - return lcFileLocale; + return g_lcFileLocale; } -LCID WINAPI SFileSetLocale(LCID lcNewLocale) +LCID WINAPI SFileSetLocale(LCID lcFileLocale) { - lcFileLocale = lcNewLocale; - return lcFileLocale; + return (g_lcFileLocale = lcFileLocale); } //----------------------------------------------------------------------------- @@ -144,131 +275,235 @@ bool WINAPI SFileOpenArchive( DWORD dwFlags, HANDLE * phMpq) { + TMPQUserData * pUserData = NULL; TFileStream * pStream = NULL; // Open file stream TMPQArchive * ha = NULL; // Archive handle + TFileEntry * pFileEntry; ULONGLONG FileSize = 0; // Size of the file - int nError = ERROR_SUCCESS; + LPBYTE pbHeaderBuffer = NULL; // Buffer for searching MPQ header + DWORD dwStreamFlags = (dwFlags & STREAM_FLAGS_MASK); + MTYPE MapType = MapTypeNotChecked; + DWORD dwErrCode = ERROR_SUCCESS; // Verify the parameters if(szMpqName == NULL || *szMpqName == 0 || phMpq == NULL) - nError = ERROR_INVALID_PARAMETER; + { + SErrSetLastError(ERROR_INVALID_PARAMETER); + return false; + } // One time initialization of MPQ cryptography InitializeMpqCryptography(); dwPriority = dwPriority; + // If not forcing MPQ v 1.0, also use file bitmap + dwStreamFlags |= (dwFlags & MPQ_OPEN_FORCE_MPQ_V1) ? 0 : STREAM_FLAG_USE_BITMAP; + // Open the MPQ archive file - if(nError == ERROR_SUCCESS) + pStream = FileStream_OpenFile(szMpqName, dwStreamFlags); + if(pStream == NULL) + return false; + + // Check the file size. There must be at least 0x20 bytes + if(dwErrCode == ERROR_SUCCESS) { - // 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 = STORM_ALLOC(TMPQArchive, 1)) == NULL) - nError = ERROR_NOT_ENOUGH_MEMORY; + FileStream_GetSize(pStream, &FileSize); + if(FileSize < MPQ_HEADER_SIZE_V1) + dwErrCode = ERROR_BAD_FORMAT; } - // Initialize handle structure and allocate structure for MPQ header - if(nError == ERROR_SUCCESS) + // Allocate the MPQhandle + if(dwErrCode == ERROR_SUCCESS) { - memset(ha, 0, sizeof(TMPQArchive)); + if((ha = STORM_ALLOC(TMPQArchive, 1)) != NULL) + memset(ha, 0, sizeof(TMPQArchive)); + else + dwErrCode = ERROR_NOT_ENOUGH_MEMORY; + } + + // Allocate buffer for searching MPQ header + if(dwErrCode == ERROR_SUCCESS) + { + pbHeaderBuffer = STORM_ALLOC(BYTE, HEADER_SEARCH_BUFFER_SIZE); + if(pbHeaderBuffer == NULL) + dwErrCode = ERROR_NOT_ENOUGH_MEMORY; + } + + // Find the position of MPQ header + if(dwErrCode == ERROR_SUCCESS) + { + ULONGLONG ByteOffset = 0; + ULONGLONG EndOfSearch = FileSize; + DWORD dwStrmFlags = 0; + DWORD dwHeaderSize; + DWORD dwHeaderID; + bool bSearchComplete = false; + + ha->dwValidFileFlags = MPQ_FILE_VALID_FLAGS; + ha->pfnHashString = HashStringSlash; ha->pStream = pStream; pStream = NULL; - // Remember if the archive is open for write - if(FileStream_IsReadOnly(ha->pStream)) - ha->dwFlags |= MPQ_FLAG_READ_ONLY; + // Set the archive read only if the stream is read-only + FileStream_GetFlags(ha->pStream, &dwStrmFlags); + ha->dwFlags |= (dwStrmFlags & STREAM_FLAG_READ_ONLY) ? MPQ_FLAG_READ_ONLY : 0; // Also remember if we shall check sector CRCs when reading file - if(dwFlags & MPQ_OPEN_CHECK_SECTOR_CRC) - ha->dwFlags |= MPQ_FLAG_CHECK_SECTOR_CRC; - } + ha->dwFlags |= (dwFlags & MPQ_OPEN_CHECK_SECTOR_CRC) ? MPQ_FLAG_CHECK_SECTOR_CRC : 0; - // Find the offset of MPQ header within the file - if(nError == ERROR_SUCCESS) - { - ULONGLONG SearchPos = 0; - DWORD dwHeaderID; + // Also remember if this MPQ is a patch + ha->dwFlags |= (dwFlags & MPQ_OPEN_PATCH) ? MPQ_FLAG_PATCH : 0; - while(SearchPos < FileSize) + // Set the reference count to 1 + ha->dwRefCount = 1; + + // Limit the header searching to about 130 MB of data + if(EndOfSearch > 0x08000000) + EndOfSearch = 0x08000000; + if(FileSize < HEADER_SEARCH_BUFFER_SIZE) + memset(pbHeaderBuffer, 0, HEADER_SEARCH_BUFFER_SIZE); + + // Find the offset of MPQ header within the file + while(bSearchComplete == false && ByteOffset < EndOfSearch) { - DWORD dwBytesAvailable = MPQ_HEADER_SIZE_V4; + // Always read at least 0x1000 bytes for performance. + // This is what Storm.dll (2002) does. + DWORD dwBytesAvailable = HEADER_SEARCH_BUFFER_SIZE; // Cut the bytes available, if needed - if((FileSize - SearchPos) < MPQ_HEADER_SIZE_V4) - dwBytesAvailable = (DWORD)(FileSize - SearchPos); + if((FileSize - ByteOffset) < HEADER_SEARCH_BUFFER_SIZE) + dwBytesAvailable = (DWORD)(FileSize - ByteOffset); // Read the eventual MPQ header - if(!FileStream_Read(ha->pStream, &SearchPos, ha->HeaderData, dwBytesAvailable)) + if(!FileStream_Read(ha->pStream, &ByteOffset, pbHeaderBuffer, dwBytesAvailable)) { - nError = GetLastError(); + dwErrCode = SErrGetLastError(); break; } - // There are AVI files from Warcraft III with 'MPQ' extension. - if(SearchPos == 0 && IsAviFile(ha->HeaderData)) + // Check whether the file is AVI file or a Warcraft III/Starcraft II map + if(MapType == MapTypeNotChecked) { - nError = ERROR_AVI_FILE; - break; - } - - // If there is the MPQ user data signature, process it - dwHeaderID = BSWAP_INT32_UNSIGNED(*(LPDWORD)ha->HeaderData); - if(dwHeaderID == ID_MPQ_USERDATA && ha->pUserData == NULL) - { - // Ignore the MPQ user data completely if the caller wants to open the MPQ as V1.0 - if((dwFlags & MPQ_OPEN_FORCE_MPQ_V1) == 0) + // Do nothing if the file is an AVI file + if((MapType = CheckMapType(szMpqName, pbHeaderBuffer, dwBytesAvailable)) == MapTypeAviFile) { - // Fill the user data header - ha->pUserData = &ha->UserData; - memcpy(ha->pUserData, ha->HeaderData, sizeof(TMPQUserData)); - BSWAP_TMPQUSERDATA(ha->pUserData); - - // Remember the position of the user data and continue search - ha->UserDataPos = SearchPos; - SearchPos += ha->pUserData->dwHeaderOffs; - continue; + dwErrCode = ERROR_AVI_FILE; + break; } } - // There must be MPQ header signature - if(dwHeaderID == ID_MPQ) + // Search the header buffer + for(DWORD dwInBufferOffset = 0; dwInBufferOffset < dwBytesAvailable; dwInBufferOffset += 0x200) { - // Save the position where the MPQ header has been found - if(ha->pUserData == NULL) - ha->UserDataPos = SearchPos; - ha->pHeader = (TMPQHeader *)ha->HeaderData; - ha->MpqPos = SearchPos; + // Copy the data from the potential header buffer to the MPQ header + memcpy(ha->HeaderData, pbHeaderBuffer + dwInBufferOffset, sizeof(ha->HeaderData)); - // Now convert the header to version 4 - BSWAP_TMPQHEADER(ha->pHeader); - nError = ConvertMpqHeaderToFormat4(ha, FileSize, dwFlags); - break; + // If there is the MPQ user data, process it + // Note that Warcraft III does not check for user data, which is abused by many map protectors + dwHeaderID = BSWAP_INT32_UNSIGNED(ha->HeaderData[0]); + if(MapType != MapTypeWarcraft3 && (dwFlags & MPQ_OPEN_FORCE_MPQ_V1) == 0) + { + if(ha->pUserData == NULL && dwHeaderID == ID_MPQ_USERDATA) + { + // Copy the eventual user data to the separate buffer + memcpy(&ha->UserData, ha->HeaderData, sizeof(TMPQUserData)); + + // Verify if this looks like a valid user data + pUserData = IsValidMpqUserData(ByteOffset, FileSize, &ha->UserData); + if(pUserData != NULL) + { + // Set the byte offset to the loaded user data + ULONGLONG TempByteOffset = ByteOffset + pUserData->dwHeaderOffs; + + // Read the eventual MPQ header from the position where the user data points + if(!FileStream_Read(ha->pStream, &TempByteOffset, ha->HeaderData, sizeof(ha->HeaderData))) + { + dwErrCode = SErrGetLastError(); + break; + } + + // Re-initialize the header ID + dwHeaderID = BSWAP_INT32_UNSIGNED(ha->HeaderData[0]); + } + } + } + + // There must be MPQ header signature. Note that STORM.dll from Warcraft III actually + // tests the MPQ header size. It must be at least 0x20 bytes in order to load it + // Abused by Spazzler Map protector. Note that the size check is not present + // in Storm.dll v 1.00, so Diablo I code would load the MPQ anyway. + dwHeaderSize = BSWAP_INT32_UNSIGNED(ha->HeaderData[1]); + if(dwHeaderID == g_dwMpqSignature && dwHeaderSize >= MPQ_HEADER_SIZE_V1) + { + // Now convert the header to version 4 + dwErrCode = ConvertMpqHeaderToFormat4(ha, ByteOffset, FileSize, dwFlags, MapType); + if(dwErrCode != ERROR_FAKE_MPQ_HEADER) + { + bSearchComplete = true; + break; + } + } + + // Check for MPK archives (Longwu Online - MPQ fork) + if(MapType == MapTypeNotRecognized && (dwFlags & MPQ_OPEN_FORCE_MPQ_V1) == 0 && dwHeaderID == ID_MPK) + { + // Now convert the MPK header to MPQ Header version 4 + dwErrCode = ConvertMpkHeaderToFormat4(ha, FileSize, dwFlags); + bSearchComplete = true; + break; + } + + // If searching for the MPQ header is disabled, return an error + if(dwFlags & MPQ_OPEN_NO_HEADER_SEARCH) + { + dwErrCode = ERROR_NOT_SUPPORTED; + bSearchComplete = true; + break; + } + + // Move the pointers + ByteOffset += 0x200; + pUserData = NULL; } - - // Move to the next possible offset - SearchPos += 0x200; } - // If we haven't found MPQ header in the file, it's an error - if(ha->pHeader == NULL) - nError = ERROR_BAD_FORMAT; + // Did we identify one of the supported headers? + if(dwErrCode == ERROR_SUCCESS) + { + // If we retrieved the offset from the user data offset, initialize the user data + if(pUserData != NULL) + { + // Fill the user data header + ha->pUserData = &ha->UserData; + ha->UserDataPos = ByteOffset; + + // Set the real byte offset + ByteOffset = ByteOffset + pUserData->dwHeaderOffs; + } + + // Set the user data position to the MPQ header, if none + if(ha->pUserData == NULL) + ha->UserDataPos = ByteOffset; + + // Set the position of the MPQ header + ha->pHeader = (TMPQHeader *)ha->HeaderData; + ha->MpqPos = ByteOffset; + ha->FileSize = FileSize; + + // Sector size must be nonzero. + if(ByteOffset >= FileSize || ha->pHeader->wSectorSize == 0) + dwErrCode = ERROR_BAD_FORMAT; + } } // Fix table positions according to format - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { // Dump the header // 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 + // and ignores the MPQ format version as well. The trick is to // fake MPQ format 2, with an improper hi-word position of hash table and block table // We can overcome such protectors by forcing opening the archive as MPQ v 1.0 if(dwFlags & MPQ_OPEN_FORCE_MPQ_V1) @@ -279,124 +514,152 @@ bool WINAPI SFileOpenArchive( ha->pUserData = NULL; } + // Anti-overflow. If the hash table size in the header is + // higher than 0x10000000, it would overflow in 32-bit version + // Observed in the malformed Warcraft III maps + // Example map: MPQ_2016_v1_ProtectedMap_TableSizeOverflow.w3x + ha->pHeader->dwBlockTableSize = (ha->pHeader->dwBlockTableSize & BLOCK_INDEX_MASK); + ha->pHeader->dwHashTableSize = (ha->pHeader->dwHashTableSize & BLOCK_INDEX_MASK); + // Both MPQ_OPEN_NO_LISTFILE or MPQ_OPEN_NO_ATTRIBUTES trigger read only mode if(dwFlags & (MPQ_OPEN_NO_LISTFILE | MPQ_OPEN_NO_ATTRIBUTES)) ha->dwFlags |= MPQ_FLAG_READ_ONLY; - // Set the size of file sector - ha->dwSectorSize = (0x200 << ha->pHeader->wSectorSize); + // Check if the caller wants to force adding listfile + if(dwFlags & MPQ_OPEN_FORCE_LISTFILE) + ha->dwFlags |= MPQ_FLAG_LISTFILE_FORCE; - // Verify if any of the tables doesn't start beyond the end of the file - nError = VerifyMpqTablePositions(ha, FileSize); + // StarDat.mpq from Starcraft I BETA: Enable special compression types + if(IsStarcraftBetaArchive(ha->pHeader)) + ha->dwFlags |= MPQ_FLAG_STARCRAFT_BETA; + + // Set the mask for the file offset. In MPQs version 1, + // all offsets are 32-bit and overflow is allowed. + // For MPQs v2+, file offset if 64-bit. + ha->FileOffsetMask = GetFileOffsetMask(ha); + + // Maps from StarCraft and Warcraft III need special treatment + switch(MapType) + { + case MapTypeStarcraft: + ha->dwValidFileFlags = MPQ_FILE_VALID_FLAGS_SCX; + ha->dwFlags |= MPQ_FLAG_STARCRAFT; + break; + + case MapTypeWarcraft3: + ha->dwValidFileFlags = MPQ_FILE_VALID_FLAGS_W3X; + ha->dwFlags |= MPQ_FLAG_WAR3_MAP; + break; + case MapTypeNotChecked: + case MapTypeNotRecognized: + case MapTypeAviFile: + case MapTypeStarcraft2: + // silence -Wswitch-enum + break; + } + + // Set the size of file sector. Be sure to check for integer overflow + if((ha->dwSectorSize = (0x200 << ha->pHeader->wSectorSize)) == 0) + dwErrCode = ERROR_FILE_CORRUPT; } - // 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) + // Verify if any of the tables doesn't start beyond the end of the file + if(dwErrCode == ERROR_SUCCESS) { - 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; - } + dwErrCode = VerifyMpqTablePositions(ha, FileSize); } // 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) + if(dwErrCode == ERROR_SUCCESS) { - nError = LoadAnyHashTable(ha); + dwErrCode = LoadAnyHashTable(ha); } // Now, build the file table. It will be built by combining // the block table, BET table, hi-block table, (attributes) and (listfile). - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { - nError = BuildFileTable(ha, FileSize); - } - - // Verify the file table, if no kind of protection was detected - 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; - - // Parse all file entries - for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) - { - // If that file entry is valid, check the file position - if(pFileEntry->dwFlags & MPQ_FILE_EXISTS) - { - // Get the 64-bit file position, - // relative to the begin of the file - RawFilePos = ha->MpqPos + pFileEntry->ByteOffset; - - // Begin of the file must be within range - if(RawFilePos > FileSize) - { - nError = ERROR_FILE_CORRUPT; - break; - } - - // End of the file must be within range - RawFilePos += pFileEntry->dwCmpSize; - if(RawFilePos > FileSize) - { - nError = ERROR_FILE_CORRUPT; - break; - } - - // Also, we remember end of the file -// if(RawFilePos > ArchiveSize) -// ArchiveSize = RawFilePos; - } - } + dwErrCode = BuildFileTable(ha); } // Load the internal listfile and include it to the file table - if(nError == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_LISTFILE) == 0) + if(dwErrCode == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_LISTFILE) == 0) { - // Ignore result of the operation. (listfile) is optional. - SFileAddListFile((HANDLE)ha, NULL); + // Quick check for (listfile) + pFileEntry = GetFileEntryLocale(ha, LISTFILE_NAME, LANG_NEUTRAL); + if(pFileEntry != NULL) + { + // Ignore result of the operation. (listfile) is optional. + SFileAddListFile((HANDLE)ha, NULL); + ha->dwFileFlags1 = pFileEntry->dwFlags; + } } // Load the "(attributes)" file and merge it to the file table - if(nError == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_ATTRIBUTES) == 0) + if(dwErrCode == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_ATTRIBUTES) == 0 && (ha->dwFlags & MPQ_FLAG_BLOCK_TABLE_CUT) == 0) { - // Ignore result of the operation. (attributes) is optional. - SAttrLoadAttributes(ha); + // Quick check for (attributes) + pFileEntry = GetFileEntryLocale(ha, ATTRIBUTES_NAME, LANG_NEUTRAL); + if(pFileEntry != NULL) + { + // Ignore result of the operation. (attributes) is optional. + SAttrLoadAttributes(ha); + ha->dwFileFlags2 = pFileEntry->dwFlags; + } + } + + // Remember whether the archive has weak signature. Only for MPQs format 1.0. + if(dwErrCode == ERROR_SUCCESS) + { + // Quick check for (signature) + pFileEntry = GetFileEntryLocale(ha, SIGNATURE_NAME, LANG_NEUTRAL); + if(pFileEntry != NULL) + { + // Just remember that the archive is weak-signed + assert((pFileEntry->dwFlags & MPQ_FILE_EXISTS) != 0); + ha->dwFileFlags3 = pFileEntry->dwFlags; + } + + // Finally, set the MPQ_FLAG_READ_ONLY if the MPQ was found malformed + ha->dwFlags |= (ha->dwFlags & MPQ_FLAG_MALFORMED) ? MPQ_FLAG_READ_ONLY : 0; } // Cleanup and exit - if(nError != ERROR_SUCCESS) + if(dwErrCode != ERROR_SUCCESS) { FileStream_Close(pStream); - FreeMPQArchive(ha); - SetLastError(nError); + DereferenceArchive(ha); + SErrSetLastError(dwErrCode); ha = NULL; } - *phMpq = ha; - return (nError == ERROR_SUCCESS); + // Free the header buffer + if(pbHeaderBuffer != NULL) + STORM_FREE(pbHeaderBuffer); + if(phMpq != NULL) + *phMpq = ha; + return (dwErrCode == ERROR_SUCCESS); } //----------------------------------------------------------------------------- -// SFileGetArchiveBitmap +// bool WINAPI SFileSetDownloadCallback(HANDLE, SFILE_DOWNLOAD_CALLBACK, void *); +// +// Sets a callback that is called when content is downloaded from the master MPQ +// -bool WINAPI SFileGetArchiveBitmap(HANDLE hMpq, TFileBitmap * pBitmap, DWORD Length, LPDWORD LengthNeeded) +bool WINAPI SFileSetDownloadCallback(HANDLE hMpq, SFILE_DOWNLOAD_CALLBACK DownloadCB, void * pvUserData) { TMPQArchive * ha = (TMPQArchive *)hMpq; - return FileStream_GetBitmap(ha->pStream, pBitmap, Length, LengthNeeded); + // Do nothing if 'hMpq' is bad parameter + if(!IsValidMpqHandle(hMpq)) + { + SErrSetLastError(ERROR_INVALID_HANDLE); + return false; + } + + return FileStream_SetCallback(ha->pStream, DownloadCB, pvUserData); } //----------------------------------------------------------------------------- @@ -410,45 +673,91 @@ bool WINAPI SFileGetArchiveBitmap(HANDLE hMpq, TFileBitmap * pBitmap, DWORD Leng bool WINAPI SFileFlushArchive(HANDLE hMpq) { - TMPQArchive * ha = (TMPQArchive *)hMpq; - int nResultError = ERROR_SUCCESS; - int nError; + TMPQArchive * ha; + DWORD dwResultError = ERROR_SUCCESS; + DWORD dwErrCode; // Do nothing if 'hMpq' is bad parameter - if(!IsValidMpqHandle(ha)) + if((ha = IsValidMpqHandle(hMpq)) == NULL) { - SetLastError(ERROR_INVALID_HANDLE); + SErrSetLastError(ERROR_INVALID_HANDLE); return false; } - // If the (listfile) has been invalidated, save it - if(ha->dwFlags & MPQ_FLAG_INV_LISTFILE) - { - nError = SListFileSaveToMpq(ha); - if(nError != ERROR_SUCCESS) - nResultError = nError; - } - - // 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 + // Only if the MPQ was changed if(ha->dwFlags & MPQ_FLAG_CHANGED) { - nError = SaveMPQTables(ha); - if(nError != ERROR_SUCCESS) - nResultError = nError; + // Indicate that we are saving MPQ internal structures + ha->dwFlags |= MPQ_FLAG_SAVING_TABLES; + + // Defragment the file table. This will allow us to put the internal files to the end + DefragmentFileTable(ha); + + // + // Create each internal file + // Note that the (signature) file is usually before (listfile) in the file table + // + + // Up the number of references to prevent it from going away during archive cleanup + ha->dwRefCount++; + + // Add the (signature) file + if(ha->dwFlags & MPQ_FLAG_SIGNATURE_NEW) + { + dwErrCode = SSignFileCreate(ha); + if(dwErrCode != ERROR_SUCCESS) + dwResultError = dwErrCode; + } + + // Add the (listfile) file + if(ha->dwFlags & (MPQ_FLAG_LISTFILE_NEW | MPQ_FLAG_LISTFILE_FORCE)) + { + dwErrCode = SListFileSaveToMpq(ha); + if(dwErrCode != ERROR_SUCCESS) + dwResultError = dwErrCode; + } + + // Add the (attributes) file + if(ha->dwFlags & MPQ_FLAG_ATTRIBUTES_NEW) + { + dwErrCode = SAttrFileSaveToMpq(ha); + if(dwErrCode != ERROR_SUCCESS) + dwResultError = dwErrCode; + } + + // Save HET table, BET table, hash table, block table, hi-block table + if(ha->dwFlags & MPQ_FLAG_CHANGED) + { + // Rebuild the HET table + if(ha->pHetTable != NULL) + RebuildHetTable(ha); + + // Save all MPQ tables first + dwErrCode = SaveMPQTables(ha); + if(dwErrCode != ERROR_SUCCESS) + dwResultError = dwErrCode; + + // If the archive has weak signature, we need to finish it + if(ha->dwFileFlags3 != 0) + { + dwErrCode = SSignFileFinish(ha); + if(dwErrCode != ERROR_SUCCESS) + dwResultError = dwErrCode; + } + } + + // Drop the reference count. Do NOT call DereferenceHandle() to prevent recursion + assert(ha->dwRefCount > 0); + ha->dwRefCount--; + + // We are no longer saving internal MPQ structures + ha->dwFlags &= ~MPQ_FLAG_SAVING_TABLES; } // Return the error - if(nResultError != ERROR_SUCCESS) - SetLastError(nResultError); - return (nResultError == ERROR_SUCCESS); + if(dwResultError != ERROR_SUCCESS) + SErrSetLastError(dwResultError); + return (dwResultError == ERROR_SUCCESS); } //----------------------------------------------------------------------------- @@ -457,14 +766,21 @@ bool WINAPI SFileFlushArchive(HANDLE hMpq) bool WINAPI SFileCloseArchive(HANDLE hMpq) { - TMPQArchive * ha = (TMPQArchive *)hMpq; - bool bResult; + TMPQArchive * ha = IsValidMpqHandle(hMpq); - // Flush all unsaved data to the storage - bResult = SFileFlushArchive(hMpq); + // Only if the handle is valid + if(ha == NULL) + { + SErrSetLastError(ERROR_INVALID_HANDLE); + return false; + } - // Free all memory used by MPQ archive - FreeMPQArchive(ha); - return bResult; + // Dereference the archive + if(!DereferenceArchive(ha)) + { + SErrSetLastError(ERROR_ACCESS_DENIED); + return false; + } + + return true; } - diff --git a/dep/StormLib/src/SFileOpenFileEx.cpp b/dep/StormLib/src/SFileOpenFileEx.cpp index e03a8b1799..e555303dd5 100644 --- a/dep/StormLib/src/SFileOpenFileEx.cpp +++ b/dep/StormLib/src/SFileOpenFileEx.cpp @@ -5,7 +5,7 @@ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ -/* xx.xx.99 1.00 Lad The first version of SFileOpenFileEx.cpp */ +/* xx.xx.99 1.00 Lad Created */ /*****************************************************************************/ #define __STORMLIB_SELF__ @@ -16,126 +16,164 @@ /* Local functions */ /*****************************************************************************/ -static bool OpenLocalFile(const char * szFileName, HANDLE * phFile) +// Finds hash index of the entry that was open by pseudo-name +static DWORD FindHashIndex(TMPQArchive * ha, DWORD dwFileIndex) +{ + TMPQHash * pHashTableEnd; + TMPQHash * pHash; + DWORD dwHashIndex = HASH_ENTRY_FREE; + DWORD dwCount = 0; + + // Should only be called if the archive has hash table + assert(ha->pHashTable != NULL); + + // Multiple hash table entries can point to the file table entry. + // We need to search all of them + pHashTableEnd = ha->pHashTable + ha->pHeader->dwHashTableSize; + for(pHash = ha->pHashTable; pHash < pHashTableEnd; pHash++) + { + if(MPQ_BLOCK_INDEX(pHash) == dwFileIndex) + { + // Example: MPQ_2023_v1_Lusin2Rpg1.28.w3x, file index 24483 + // ReplaceableTextures\CommandButtons\BTNHaboss79.blp + // Hash Table Index #1 = 18 + // Hash Table Index #2 = 8446 + if(dwCount++ > 0) + return HASH_ENTRY_FREE; + dwHashIndex = (DWORD)(pHash - ha->pHashTable); + } + } + + // Return the found hash index, if there are no duplicities + return dwHashIndex; +} + +static const char * GetPatchFileName(TMPQArchive * ha, const char * szFileName, char * szBuffer) +{ + TMPQNamePrefix * pPrefix; + + // Are there patches in the current MPQ? + if(ha->dwFlags & MPQ_FLAG_PATCH) + { + // The patch prefix must be already known here + assert(ha->pPatchPrefix != NULL); + pPrefix = ha->pPatchPrefix; + + // The patch name for "OldWorld\\XXX\\YYY" is "Base\\XXX\YYY" + // We need to remove the "OldWorld\\" prefix + if(!_strnicmp(szFileName, "OldWorld\\", 9)) + szFileName += 9; + + // Create the file name from the known patch entry + memcpy(szBuffer, pPrefix->szPatchPrefix, pPrefix->nLength); + strcpy(szBuffer + pPrefix->nLength, szFileName); + szFileName = szBuffer; + } + + return szFileName; +} + +static bool OpenLocalFile(const char * szFileName, HANDLE * PtrFile) { TFileStream * pStream; TMPQFile * hf = NULL; - - // 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 + // Convert the file name to UNICODE (if needed) + StringCopy(szFileNameT, _countof(szFileNameT), szFileName); + // Open the file and create the TMPQFile structure + pStream = FileStream_OpenFile(szFileNameT, STREAM_FLAG_READ_ONLY); if(pStream != NULL) { // Allocate and initialize file handle - hf = CreateMpqFile(NULL); + hf = CreateFileHandle(NULL, NULL); if(hf != NULL) { hf->pStream = pStream; - *phFile = hf; + *PtrFile = hf; return true; } else { FileStream_Close(pStream); - SetLastError(ERROR_NOT_ENOUGH_MEMORY); + SErrSetLastError(ERROR_NOT_ENOUGH_MEMORY); } } - *phFile = NULL; + *PtrFile = NULL; return false; } -bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, DWORD dwReserved, HANDLE * phFile) +bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, HANDLE * PtrFile) { + TMPQArchive * haBase = NULL; TMPQArchive * ha = (TMPQArchive *)hMpq; + TFileEntry * pFileEntry; TMPQFile * hfPatch; // Pointer to patch file TMPQFile * hfBase = NULL; // Pointer to base open file - TMPQFile * hfLast = NULL; // The highest file in the chain that is not patch file TMPQFile * hf = NULL; HANDLE hPatchFile; - char szPatchFileName[MAX_PATH]; + char szNameBuffer[MAX_PATH]; - // Keep this flag here for future updates - dwReserved = dwReserved; - - // First of all, try to open the original version of the file in any of the patch chain + // First of all, find the latest archive where the file is in base version + // (i.e. where the original, unpatched version of the file exists) while(ha != NULL) { - // Construct the name of the patch file - strcpy(szPatchFileName, ha->szPatchPrefix); - strcpy(&szPatchFileName[ha->cchPatchPrefix], szFileName); - if(SFileOpenFileEx((HANDLE)ha, szPatchFileName, SFILE_OPEN_FROM_MPQ, (HANDLE *)&hfBase)) - { - // 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; - } + // If the file is there, then we remember the archive + pFileEntry = GetFileEntryExact(ha, GetPatchFileName(ha, szFileName, szNameBuffer), 0, NULL); + if(pFileEntry != NULL && (pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0) + haBase = ha; - SFileCloseFile((HANDLE)hfBase); - } - - // Move to the next file in the patch chain + // Move to the patch archive ha = ha->haPatch; } - // If we couldn't find the file in any of the patches, it doesn't exist - if(hf == NULL) + // If we couldn't find the base file in any of the patches, it doesn't exist + if((ha = haBase) != NULL) { - SetLastError(ERROR_FILE_NOT_FOUND); - return false; - } - - // Now keep going in the patch chain and open every patch file that is there - for(ha = ha->haPatch; ha != NULL; ha = ha->haPatch) - { - // Construct patch file name - strcpy(szPatchFileName, ha->szPatchPrefix); - strcpy(&szPatchFileName[ha->cchPatchPrefix], szFileName); - if(SFileOpenFileEx((HANDLE)ha, szPatchFileName, SFILE_OPEN_FROM_MPQ, &hPatchFile)) + // Now open the base file + if(SFileOpenFileEx((HANDLE)ha, GetPatchFileName(ha, szFileName, szNameBuffer), SFILE_OPEN_BASE_FILE, (HANDLE *)&hfBase)) { - // Remember the new version - hfPatch = (TMPQFile *)hPatchFile; + // The file must be a base file, i.e. without MPQ_FILE_PATCH_FILE + assert((hfBase->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0); + hf = hfBase; - // 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; + // Now open all patches and attach them on top of the base file + for(ha = ha->haPatch; ha != NULL; ha = ha->haPatch) + { + // Prepare the file name with a correct prefix + if(SFileOpenFileEx((HANDLE)ha, GetPatchFileName(ha, szFileName, szNameBuffer), SFILE_OPEN_BASE_FILE, &hPatchFile)) + { + // Remember the new version + hfPatch = (TMPQFile *)hPatchFile; - // Set current patch to base file and move on - hf->hfPatchFile = hfPatch; - hf = hfPatch; + // We should not find patch file + assert((hfPatch->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) != 0); + + // Attach the patch to the base file + hf->hfPatch = hfPatch; + hf = hfPatch; + } + } + + // If the opened file has the delete marker, we don't open the file + if(hfBase && hf && hf->pFileEntry->dwFlags & MPQ_FILE_DELETE_MARKER) + { + SFileCloseFile((HANDLE)(hfBase)); + SErrSetLastError(ERROR_FILE_DELETED); + hfBase = NULL; + } } } - - // Now we need to free all files that are below the highest unpatched version - while(hfBase != hfLast) + else { - TMPQFile * hfNext = hfBase->hfPatchFile; - - // Free the file below - hfBase->hfPatchFile = NULL; - FreeMPQFile(hfBase); - - // Move the base to the next file - hfBase = hfNext; + SErrSetLastError(ERROR_FILE_NOT_FOUND); } // Give the updated base MPQ - if(phFile != NULL) - *phFile = (HANDLE)hfBase; - return true; + if(PtrFile != NULL) + *PtrFile = (HANDLE)hfBase; + return (hfBase != NULL); } /*****************************************************************************/ @@ -143,81 +181,224 @@ 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, +// pointed by PtrFileLocales. There must be enough entries to copy the localed, // otherwise the function returns ERROR_INSUFFICIENT_BUFFER. -int WINAPI SFileEnumLocales( +DWORD WINAPI SFileEnumLocales( HANDLE hMpq, const char * szFileName, - LCID * plcLocales, - LPDWORD pdwMaxLocales, + LCID * PtrFileLocales, + LPDWORD PtrMaxLocales, DWORD dwSearchScope) { TMPQArchive * ha = (TMPQArchive *)hMpq; - TFileEntry * pFileEntry; TMPQHash * pFirstHash; TMPQHash * pHash; DWORD dwFileIndex = 0; + DWORD dwMaxLocales; DWORD dwLocales = 0; // Test the parameters - if(!IsValidMpqHandle(ha)) + if(!IsValidMpqHandle(hMpq)) return ERROR_INVALID_HANDLE; if(szFileName == NULL || *szFileName == 0) return ERROR_INVALID_PARAMETER; - if(pdwMaxLocales == NULL) + if(ha->pHashTable == NULL) + return ERROR_NOT_SUPPORTED; + if(PtrMaxLocales == NULL) + return ERROR_INVALID_PARAMETER; + if(IsPseudoFileName(szFileName, &dwFileIndex)) return ERROR_INVALID_PARAMETER; - - // Keep compiler happy - dwSearchScope = dwSearchScope; - // Parse hash table entries for all locales - if(!IsPseudoFileName(szFileName, &dwFileIndex)) + // Keep compilers happy + dwMaxLocales = PtrMaxLocales[0]; + STORMLIB_UNUSED(dwSearchScope); + + // Parse all files with that name + pFirstHash = pHash = GetFirstHashEntry(ha, szFileName); + while(pHash != NULL) { - // Calculate the number of locales - pFirstHash = pHash = GetFirstHashEntry(ha, szFileName); - while(pHash != NULL) - { - dwLocales++; - pHash = GetNextHashEntry(ha, pFirstHash, pHash); - } + // Put the locales to the buffer + if(PtrFileLocales != NULL && dwLocales < dwMaxLocales) + *PtrFileLocales++ = SFILE_MAKE_LCID(pHash->Locale, pHash->Platform); + dwLocales++; - // Test if there is enough space to copy the locales - if(*pdwMaxLocales < dwLocales) - { - *pdwMaxLocales = dwLocales; - return ERROR_INSUFFICIENT_BUFFER; - } - - // Enum the locales - pFirstHash = pHash = GetFirstHashEntry(ha, szFileName); - while(pHash != NULL) - { - *plcLocales++ = pHash->lcLocale; - pHash = GetNextHashEntry(ha, pFirstHash, pHash); - } - } - else - { - // There must be space for 1 locale - if(*pdwMaxLocales < 1) - { - *pdwMaxLocales = 1; - return ERROR_INSUFFICIENT_BUFFER; - } - - // For nameless access, always return 1 locale - pFileEntry = GetFileEntryByIndex(ha, dwFileIndex); - pHash = ha->pHashTable + pFileEntry->dwHashIndex; - *plcLocales = pHash->lcLocale; - dwLocales = 1; + // Get the next locale + pHash = GetNextHashEntry(ha, pFirstHash, pHash); } - // Give the caller the total number of found locales - *pdwMaxLocales = dwLocales; - return ERROR_SUCCESS; + // Give the caller the number of locales and return + PtrMaxLocales[0] = dwLocales; + return (dwLocales <= dwMaxLocales) ? ERROR_SUCCESS : ERROR_INSUFFICIENT_BUFFER; +} + +//----------------------------------------------------------------------------- +// SFileOpenFileEx +// +// hMpq - Handle of opened MPQ archive +// szFileName - Name of file to open +// dwSearchScope - Where to search +// PtrFile - Pointer to store opened file handle + +bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope, HANDLE * PtrFile) +{ + TMPQArchive * ha = IsValidMpqHandle(hMpq); + TFileEntry * pFileEntry = NULL; + TMPQFile * hf = NULL; + DWORD dwHashIndex = HASH_ENTRY_FREE; + DWORD dwFileIndex = 0; + DWORD dwErrCode = ERROR_SUCCESS; + bool bOpenByIndex = false; + + // Don't accept NULL pointer to file handle + if(szFileName == NULL || *szFileName == 0) + dwErrCode = ERROR_INVALID_PARAMETER; + + // When opening a file from MPQ, the handle must be valid + if(dwSearchScope != SFILE_OPEN_LOCAL_FILE && ha == NULL) + dwErrCode = ERROR_INVALID_HANDLE; + + // When not checking for existence, the pointer to file handle must be valid + if(dwSearchScope != SFILE_OPEN_CHECK_EXISTS && PtrFile == NULL) + dwErrCode = ERROR_INVALID_PARAMETER; + + // Prepare the file opening + if(dwErrCode == ERROR_SUCCESS) + { + switch(dwSearchScope) + { + case SFILE_OPEN_FROM_MPQ: + case SFILE_OPEN_BASE_FILE: + case SFILE_OPEN_CHECK_EXISTS: + + // If this MPQ has no patches, open the file from this MPQ directly + if(ha->haPatch == NULL || dwSearchScope == SFILE_OPEN_BASE_FILE) + { + pFileEntry = GetFileEntryLocale(ha, szFileName, g_lcFileLocale, &dwHashIndex); + } + + // If this MPQ is a patched archive, open the file as patched + else + { + return OpenPatchedFile(hMpq, szFileName, PtrFile); + } + 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. + pFileEntry = GetFileEntryLocale(ha, szFileName, 0, &dwHashIndex); + break; + + case SFILE_OPEN_LOCAL_FILE: + + // Open a local file + return OpenLocalFile(szFileName, PtrFile); + + default: + + // Don't accept any other value + dwErrCode = ERROR_INVALID_PARAMETER; + break; + } + } + + // Check whether the file really exists in the MPQ + if(dwErrCode == ERROR_SUCCESS) + { + // If we didn't find the file, try to open it using pseudo file name ("File + if(pFileEntry == NULL || (pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0) + { + // Check the pseudo-file name ("File00000001.ext") + if((bOpenByIndex = IsPseudoFileName(szFileName, &dwFileIndex)) == true) + { + // Get the file entry for the file + if(dwFileIndex < ha->dwFileTableSize) + { + pFileEntry = ha->pFileTable + dwFileIndex; + } + } + + // Still not found? + if(pFileEntry == NULL || (pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0) + { + dwErrCode = ERROR_FILE_NOT_FOUND; + } + } + + // Perform some checks of invalid files + if(pFileEntry != NULL) + { + // MPQ protectors use insanely amount of fake files, often with very high size. + // We won't open any files whose compressed size is bigger than archive size + // If the file is not compressed, its size cannot be bigger than archive size + if((pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) == 0 && (pFileEntry->dwFileSize > ha->FileSize)) + { + dwErrCode = ERROR_FILE_CORRUPT; + pFileEntry = NULL; + } + + // Ignore unknown loading flags (example: MPQ_2016_v1_WME4_4.w3x) +// if(pFileEntry->dwFlags & ~MPQ_FILE_VALID_FLAGS) +// { +// dwErrCode = ERROR_NOT_SUPPORTED; +// pFileEntry = NULL; +// } + } + } + + // Did the caller just wanted to know if the file exists? + if(dwErrCode == ERROR_SUCCESS && dwSearchScope != SFILE_OPEN_CHECK_EXISTS) + { + // Allocate file handle + hf = CreateFileHandle(ha, pFileEntry); + if(hf != NULL) + { + // Get the hash index for the file + if(ha->pHashTable != NULL && dwHashIndex == HASH_ENTRY_FREE) + dwHashIndex = FindHashIndex(ha, dwFileIndex); + if(dwHashIndex != HASH_ENTRY_FREE) + hf->pHashEntry = ha->pHashTable + dwHashIndex; + hf->dwHashIndex = dwHashIndex; + + // If the MPQ has sector CRC enabled, enable if for the file + if(ha->dwFlags & MPQ_FLAG_CHECK_SECTOR_CRC) + hf->bCheckSectorCRCs = true; + + // 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(ha, pFileEntry, szFileName); + + // If the file is encrypted, we should detect the file key + if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) + { + hf->dwFileKey = DecryptFileKey(szFileName, + pFileEntry->ByteOffset, + pFileEntry->dwFileSize, + pFileEntry->dwFlags); + } + } + } + else + { + dwErrCode = ERROR_NOT_ENOUGH_MEMORY; + } + } + + // Give the file entry + if(PtrFile != NULL) + PtrFile[0] = hf; + + // Return error code + if(dwErrCode != ERROR_SUCCESS) + SErrSetLastError(dwErrCode); + return (dwErrCode == ERROR_SUCCESS); } //----------------------------------------------------------------------------- @@ -228,226 +409,7 @@ 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 == NULL || *szFileName == 0) - nError = ERROR_INVALID_PARAMETER; - - // Prepare the file opening - if(nError == ERROR_SUCCESS) - { - // Different processing for pseudo-names - bIsPseudoName = IsPseudoFileName(szFileName, &dwFileIndex); - - // Walk through the MPQ and all patches - while(ha != NULL) - { - // 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 - SetLastError(nError); - return false; -} - - -//----------------------------------------------------------------------------- -// SFileOpenFileEx -// -// hMpq - Handle of opened MPQ archive -// szFileName - Name of file to open -// dwSearchScope - Where to search -// phFile - Pointer to store opened file handle - -bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope, HANDLE * phFile) -{ - TMPQArchive * ha = (TMPQArchive *)hMpq; - TFileEntry * pFileEntry = NULL; - TMPQFile * hf = NULL; - DWORD dwFileIndex = 0; - bool bOpenByIndex = false; - int nError = ERROR_SUCCESS; - - // Don't accept NULL pointer to file handle - if(phFile == NULL) - nError = ERROR_INVALID_PARAMETER; - - // Prepare the file opening - if(nError == ERROR_SUCCESS) - { - 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 - if(!IsPseudoFileName(szFileName, &dwFileIndex)) - { - pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale); - if(pFileEntry == NULL) - nError = ERROR_FILE_NOT_FOUND; - } - else - { - bOpenByIndex = true; - pFileEntry = GetFileEntryByIndex(ha, dwFileIndex); - 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); - if(pFileEntry == NULL) - nError = ERROR_FILE_NOT_FOUND; - break; - - case SFILE_OPEN_LOCAL_FILE: - - 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 - if(nError != ERROR_SUCCESS) - { - SetLastError(nError); - return false; - } - } - - // Test if the file was not already deleted. - if(nError == ERROR_SUCCESS) - { - if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0) - nError = ERROR_FILE_NOT_FOUND; - if(pFileEntry->dwFlags & ~MPQ_FILE_VALID_FLAGS) - nError = ERROR_NOT_SUPPORTED; - } - - // Allocate file handle - if(nError == ERROR_SUCCESS) - { - if((hf = STORM_ALLOC(TMPQFile, 1)) == NULL) - nError = ERROR_NOT_ENOUGH_MEMORY; - } - - // Initialize file handle - if(nError == ERROR_SUCCESS) - { - memset(hf, 0, sizeof(TMPQFile)); - hf->pFileEntry = pFileEntry; - hf->dwMagic = ID_MPQ_FILE; - hf->ha = ha; - - hf->MpqFilePos = pFileEntry->ByteOffset; - hf->RawFilePos = ha->MpqPos + hf->MpqFilePos; - hf->dwDataSize = pFileEntry->dwFileSize; - - // If the MPQ has sector CRC enabled, enable if for the file - if(ha->dwFlags & MPQ_FLAG_CHECK_SECTOR_CRC) - hf->bCheckSectorCRCs = true; - - // 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, - pFileEntry->ByteOffset, - pFileEntry->dwFileSize, - pFileEntry->dwFlags); - } - } - else - { - // Try to auto-detect the file name - if(!SFileGetFileName(hf, NULL)) - nError = GetLastError(); - } - } - - // If the file is actually a patch file, we have to load the patch file header - if(nError == ERROR_SUCCESS && pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) - { - assert(hf->pPatchInfo == NULL); - nError = AllocatePatchInfo(hf, true); - } - - // Cleanup - if(nError != ERROR_SUCCESS) - { - SetLastError(nError); - FreeMPQFile(hf); - } - - *phFile = hf; - return (nError == ERROR_SUCCESS); + return SFileOpenFileEx(hMpq, szFileName, SFILE_OPEN_CHECK_EXISTS, NULL); } //----------------------------------------------------------------------------- @@ -456,14 +418,14 @@ bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearch bool WINAPI SFileCloseFile(HANDLE hFile) { TMPQFile * hf = (TMPQFile *)hFile; - - if(!IsValidFileHandle(hf)) + + if(!IsValidFileHandle(hFile)) { - SetLastError(ERROR_INVALID_HANDLE); + SErrSetLastError(ERROR_INVALID_HANDLE); return false; } // Free the structure - FreeMPQFile(hf); + FreeFileHandle(hf); return true; } diff --git a/dep/StormLib/src/SFilePatchArchives.cpp b/dep/StormLib/src/SFilePatchArchives.cpp index 24ae2c52c3..de98d4f5ba 100644 --- a/dep/StormLib/src/SFilePatchArchives.cpp +++ b/dep/StormLib/src/SFilePatchArchives.cpp @@ -15,6 +15,37 @@ //----------------------------------------------------------------------------- // Local structures +#define MAX_SC2_PATCH_PREFIX 0x80 + +#define PATCH_SIGNATURE_HEADER 0x48435450 +#define PATCH_SIGNATURE_MD5 0x5f35444d +#define PATCH_SIGNATURE_XFRM 0x4d524658 + +#define SIZE_OF_XFRM_HEADER 0x0C + +// Header for incremental patch files +typedef struct _MPQ_PATCH_HEADER +{ + //-- 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 + BYTE md5_before_patch[0x10]; // MD5 of the original (unpached) file + BYTE md5_after_patch[0x10]; // MD5 of the patched file + + //-- XFRM block ------------------------------------- + DWORD dwXFRM; // 'XFRM' + DWORD dwXfrmBlockSize; // Size of the XFRM block, includes XFRM header and patch data + DWORD dwPatchType; // Type of patch ('BSD0' or 'COPY') + + // Followed by the patch data +} MPQ_PATCH_HEADER, *PMPQ_PATCH_HEADER; + typedef struct _BLIZZARD_BSDIFF40_FILE { ULONGLONG Signature; @@ -23,49 +54,70 @@ typedef struct _BLIZZARD_BSDIFF40_FILE ULONGLONG NewFileSize; } BLIZZARD_BSDIFF40_FILE, *PBLIZZARD_BSDIFF40_FILE; +typedef struct _BSDIFF_CTRL_BLOCK +{ + DWORD dwAddDataLength; + DWORD dwMovDataLength; + DWORD dwOldMoveLength; + +} BSDIFF_CTRL_BLOCK, *PBSDIFF_CTRL_BLOCK; + +typedef struct _LOCALIZED_MPQ_INFO +{ + const char * szNameTemplate; // Name template + size_t nLangOffset; // Offset of the language + size_t nLength; // Length of the name template +} LOCALIZED_MPQ_INFO, *PLOCALIZED_MPQ_INFO; + +//----------------------------------------------------------------------------- +// Local variables + +// 4-byte groups for all languages +static const char * LanguageList = "baseteenenUSenGBenCNenTWdeDEesESesMXfrFRitITkoKRptBRptPTruRUzhCNzhTW"; + +// List of localized MPQs for World of Warcraft +static LOCALIZED_MPQ_INFO LocaleMpqs_WoW[] = +{ + {"expansion1-locale-####", 18, 22}, + {"expansion1-speech-####", 18, 22}, + {"expansion2-locale-####", 18, 22}, + {"expansion2-speech-####", 18, 22}, + {"expansion3-locale-####", 18, 22}, + {"expansion3-speech-####", 18, 22}, + {"locale-####", 7, 11}, + {"speech-####", 7, 11}, + {NULL, 0, 0} +}; + //----------------------------------------------------------------------------- // Local functions -static bool GetDefaultPatchPrefix( - const TCHAR * szBaseMpqName, - char * szBuffer) +static inline bool IsPatchMetadataFile(TFileEntry * pFileEntry) { - const TCHAR * szExtension; - const TCHAR * szDash; - - // Ensure that both names are plain names - szBaseMpqName = GetPlainFileNameT(szBaseMpqName); - - // 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"); - - // If the length of the prefix doesn't match, use default one - if(szExtension != NULL && szDash != NULL && (szExtension - szDash) == 5) + // The file must ave a name + if(pFileEntry->szFileName != NULL && (pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0) { - // 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; + // The file must be small + if(0 < pFileEntry->dwFileSize && pFileEntry->dwFileSize < 0x40) + { + // Compare the plain name + return (_stricmp(GetPlainFileName(pFileEntry->szFileName), PATCH_METADATA_NAME) == 0); + } } - return true; + // Not a patch_metadata + return false; } 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 pbCompressed += sizeof(DWORD); - cbCompressed -= sizeof(DWORD); // Pre-fill decompressed buffer with zeros memset(pbDecompressed, 0, cbDecompressed); @@ -74,7 +126,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) { @@ -94,130 +146,94 @@ static void Decompress_RLE(LPBYTE pbDecompressed, DWORD cbDecompressed, LPBYTE p } } -static int LoadMpqPatch_COPY(TMPQFile * hf, TPatchHeader * pPatchHeader) +static DWORD LoadFilePatch_COPY(TMPQFile * hf, PMPQ_PATCH_HEADER pFullPatch) { - int nError = ERROR_SUCCESS; + DWORD cbBytesToRead = pFullPatch->dwSizeOfPatchData - sizeof(MPQ_PATCH_HEADER); + DWORD cbBytesRead = 0; - // Allocate space for patch header and compressed data - hf->pPatchHeader = (TPatchHeader *)STORM_ALLOC(BYTE, pPatchHeader->dwSizeOfPatchData); - if(hf->pPatchHeader == NULL) - nError = ERROR_NOT_ENOUGH_MEMORY; - - // Load the patch data and decide if they are compressed or not - if(nError == ERROR_SUCCESS) - { - LPBYTE pbPatchFile = (LPBYTE)hf->pPatchHeader; - - // Copy the patch header itself - memcpy(pbPatchFile, pPatchHeader, sizeof(TPatchHeader)); - pbPatchFile += sizeof(TPatchHeader); - - // Load the rest of the patch - if(!SFileReadFile((HANDLE)hf, pbPatchFile, pPatchHeader->dwSizeOfPatchData - sizeof(TPatchHeader))) - nError = GetLastError(); - } - - return nError; + // Simply load the rest of the patch + SFileReadFile((HANDLE)hf, (pFullPatch + 1), cbBytesToRead, &cbBytesRead, NULL); + return (cbBytesRead == cbBytesToRead) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT; } -static int LoadMpqPatch_BSD0(TMPQFile * hf, TPatchHeader * pPatchHeader) +static DWORD LoadFilePatch_BSD0(TMPQFile * hf, PMPQ_PATCH_HEADER pFullPatch) { - LPBYTE pbDecompressed = NULL; + LPBYTE pbDecompressed = (LPBYTE)(pFullPatch + 1); LPBYTE pbCompressed = NULL; DWORD cbDecompressed = 0; DWORD cbCompressed = 0; DWORD dwBytesRead = 0; - int nError = ERROR_SUCCESS; + DWORD dwErrCode = ERROR_SUCCESS; - // Allocate space for compressed data - cbCompressed = pPatchHeader->dwXfrmBlockSize - SIZE_OF_XFRM_HEADER; - pbCompressed = STORM_ALLOC(BYTE, cbCompressed); - if(pbCompressed == NULL) - nError = ERROR_SUCCESS; + // Calculate the size of compressed data + cbDecompressed = pFullPatch->dwSizeOfPatchData - sizeof(MPQ_PATCH_HEADER); + cbCompressed = pFullPatch->dwXfrmBlockSize - SIZE_OF_XFRM_HEADER; - // Read the compressed patch data - if(nError == ERROR_SUCCESS) + // Is that file compressed? + if(cbCompressed < cbDecompressed) { - // Load the rest of the header - SFileReadFile((HANDLE)hf, pbCompressed, cbCompressed, &dwBytesRead); - if(dwBytesRead != cbCompressed) - nError = ERROR_FILE_CORRUPT; - } + pbCompressed = STORM_ALLOC(BYTE, cbCompressed); + if(pbCompressed == NULL) + dwErrCode = ERROR_NOT_ENOUGH_MEMORY; - // Get the uncompressed size of the patch - if(nError == ERROR_SUCCESS) - { - cbDecompressed = pPatchHeader->dwSizeOfPatchData - sizeof(TPatchHeader); - hf->pPatchHeader = (TPatchHeader *)STORM_ALLOC(BYTE, pPatchHeader->dwSizeOfPatchData); - if(hf->pPatchHeader == NULL) - nError = ERROR_NOT_ENOUGH_MEMORY; - } - - // Now decompress the patch data - if(nError == ERROR_SUCCESS) - { - // Copy the patch header - memcpy(hf->pPatchHeader, pPatchHeader, sizeof(TPatchHeader)); - pbDecompressed = (LPBYTE)hf->pPatchHeader + sizeof(TPatchHeader); - - // Uncompress or copy the patch data - if(cbCompressed < cbDecompressed) + // Read the compressed patch data + if(dwErrCode == ERROR_SUCCESS) { + SFileReadFile((HANDLE)hf, pbCompressed, cbCompressed, &dwBytesRead, NULL); + if(dwBytesRead != cbCompressed) + dwErrCode = ERROR_FILE_CORRUPT; + } + + // Decompress the data + if(dwErrCode == ERROR_SUCCESS) Decompress_RLE(pbDecompressed, cbDecompressed, pbCompressed, cbCompressed); - } - else - { - assert(cbCompressed == cbDecompressed); - memcpy(pbDecompressed, pbCompressed, cbCompressed); - } + + if(pbCompressed != NULL) + STORM_FREE(pbCompressed); + } + else + { + SFileReadFile((HANDLE)hf, pbDecompressed, cbDecompressed, &dwBytesRead, NULL); + if(dwBytesRead != cbDecompressed) + dwErrCode = ERROR_FILE_CORRUPT; } - // Free buffers and exit - if(pbCompressed != NULL) - STORM_FREE(pbCompressed); - return nError; + return dwErrCode; } -static int ApplyMpqPatch_COPY( - TMPQFile * hf, - TPatchHeader * pPatchHeader) +static DWORD ApplyFilePatch_COPY( + TMPQPatcher * pPatcher, + PMPQ_PATCH_HEADER pFullPatch, + LPBYTE pbTarget, + LPBYTE pbSource) { - LPBYTE pbNewFileData; - DWORD cbNewFileData; - - // Allocate space for new file data - cbNewFileData = pPatchHeader->dwXfrmBlockSize - SIZE_OF_XFRM_HEADER; - pbNewFileData = STORM_ALLOC(BYTE, cbNewFileData); - if(pbNewFileData == NULL) - return ERROR_NOT_ENOUGH_MEMORY; + // Sanity checks + assert(pPatcher->cbMaxFileData >= pPatcher->cbFileData); + pFullPatch = pFullPatch; // Copy the patch data as-is - memcpy(pbNewFileData, (LPBYTE)pPatchHeader + sizeof(TPatchHeader), cbNewFileData); - - // Free the old file data - STORM_FREE(hf->pbFileData); - - // Put the new file data there - hf->pbFileData = pbNewFileData; - hf->cbFileData = cbNewFileData; + memcpy(pbTarget, pbSource, pPatcher->cbFileData); return ERROR_SUCCESS; } -static int ApplyMpqPatch_BSD0( - TMPQFile * hf, - TPatchHeader * pPatchHeader) +static DWORD ApplyFilePatch_BSD0( + TMPQPatcher * pPatcher, + PMPQ_PATCH_HEADER pFullPatch, + LPBYTE pbTarget, + LPBYTE pbSource) { PBLIZZARD_BSDIFF40_FILE pBsdiff; - LPDWORD pCtrlBlock; - LPBYTE pbPatchData = (LPBYTE)pPatchHeader + sizeof(TPatchHeader); + PBSDIFF_CTRL_BLOCK pCtrlBlock; + LPBYTE pbPatchData = (LPBYTE)(pFullPatch + 1); LPBYTE pDataBlock; LPBYTE pExtraBlock; - LPBYTE pbNewData = NULL; - LPBYTE pbOldData = (LPBYTE)hf->pbFileData; + LPBYTE pbOldData = pbSource; + LPBYTE pbNewData = pbTarget; + DWORD dwCombineSize; DWORD dwNewOffset = 0; // Current position to patch DWORD dwOldOffset = 0; // Current source position DWORD dwNewSize; // Patched file size - DWORD dwOldSize = hf->cbFileData; // File size before patch + DWORD dwOldSize = pPatcher->cbFileData; // File size before patch // Get pointer to the patch header // Format of BSDIFF header corresponds to original BSDIFF, which is: @@ -234,7 +250,7 @@ static int ApplyMpqPatch_BSD0( // 0000 4 bytes Length to copy from the BSDIFF data block the new file // 0004 4 bytes Length to copy from the BSDIFF extra block // 0008 4 bytes Size to increment source file offset - pCtrlBlock = (LPDWORD)pbPatchData; + pCtrlBlock = (PBSDIFF_CTRL_BLOCK)pbPatchData; pbPatchData += (size_t)BSWAP_INT64_UNSIGNED(pBsdiff->CtrlBlockSize); // Get the pointer to the data block @@ -245,46 +261,38 @@ static int ApplyMpqPatch_BSD0( pExtraBlock = (LPBYTE)pbPatchData; dwNewSize = (DWORD)BSWAP_INT64_UNSIGNED(pBsdiff->NewFileSize); - // Allocate new buffer - pbNewData = STORM_ALLOC(BYTE, dwNewSize); - if(pbNewData == NULL) - return ERROR_NOT_ENOUGH_MEMORY; - // Now patch the file while(dwNewOffset < dwNewSize) { - DWORD dwAddDataLength = BSWAP_INT32_UNSIGNED(pCtrlBlock[0]); - DWORD dwMovDataLength = BSWAP_INT32_UNSIGNED(pCtrlBlock[1]); - DWORD dwOldMoveLength = BSWAP_INT32_UNSIGNED(pCtrlBlock[2]); + DWORD dwAddDataLength = BSWAP_INT32_UNSIGNED(pCtrlBlock->dwAddDataLength); + DWORD dwMovDataLength = BSWAP_INT32_UNSIGNED(pCtrlBlock->dwMovDataLength); + DWORD dwOldMoveLength = BSWAP_INT32_UNSIGNED(pCtrlBlock->dwOldMoveLength); DWORD i; // Sanity check if((dwNewOffset + dwAddDataLength) > dwNewSize) - { - STORM_FREE(pbNewData); return ERROR_FILE_CORRUPT; - } // Read the diff string to the target buffer memcpy(pbNewData + dwNewOffset, pDataBlock, dwAddDataLength); pDataBlock += dwAddDataLength; - // Now combine the patch data with the original file - for(i = 0; i < dwAddDataLength; i++) - { - if(dwOldOffset < dwOldSize) - pbNewData[dwNewOffset] = pbNewData[dwNewOffset] + pbOldData[dwOldOffset]; + // Get the longest block that we can combine + dwCombineSize = ((dwOldOffset + dwAddDataLength) >= dwOldSize) ? (dwOldSize - dwOldOffset) : dwAddDataLength; + if((dwNewOffset + dwCombineSize) > dwNewSize || (dwNewOffset + dwCombineSize) < dwNewOffset) + return ERROR_FILE_CORRUPT; - dwNewOffset++; - dwOldOffset++; - } + // Now combine the patch data with the original file + for(i = 0; i < dwCombineSize; i++) + pbNewData[dwNewOffset + i] = pbNewData[dwNewOffset + i] + pbOldData[dwOldOffset + i]; + + // Move the offsets + dwNewOffset += dwAddDataLength; + dwOldOffset += dwAddDataLength; // Sanity check if((dwNewOffset + dwMovDataLength) > dwNewSize) - { - STORM_FREE(pbNewData); return ERROR_FILE_CORRUPT; - } // Copy the data from the extra block in BSDIFF patch memcpy(pbNewData + dwNewOffset, pExtraBlock, dwMovDataLength); @@ -295,169 +303,549 @@ static int ApplyMpqPatch_BSD0( if(dwOldMoveLength & 0x80000000) dwOldMoveLength = 0x80000000 - dwOldMoveLength; dwOldOffset += dwOldMoveLength; - pCtrlBlock += 3; + pCtrlBlock++; } - // Free the old file data - STORM_FREE(hf->pbFileData); + // The size after patch must match + if(dwNewOffset != pFullPatch->dwSizeAfterPatch) + return ERROR_FILE_CORRUPT; - // Put the new data to the fil structure - hf->pbFileData = pbNewData; - hf->cbFileData = dwNewSize; + // Update the new data size + pPatcher->cbFileData = dwNewOffset; return ERROR_SUCCESS; } - -static int LoadMpqPatch(TMPQFile * hf) +static PMPQ_PATCH_HEADER LoadFullFilePatch(TMPQFile * hf, MPQ_PATCH_HEADER & PatchHeader) { - TPatchHeader PatchHeader; - DWORD dwBytesRead; - int nError = ERROR_SUCCESS; + PMPQ_PATCH_HEADER pFullPatch; + DWORD dwErrCode = ERROR_SUCCESS; - // Read the patch header - SFileReadFile((HANDLE)hf, &PatchHeader, sizeof(TPatchHeader), &dwBytesRead); - if(dwBytesRead != sizeof(TPatchHeader)) - nError = ERROR_FILE_CORRUPT; + // BSWAP the entire header, if needed + BSWAP_ARRAY32_UNSIGNED(&PatchHeader, sizeof(DWORD) * 6); + BSWAP_ARRAY32_UNSIGNED(&PatchHeader.dwXFRM, sizeof(DWORD) * 3); // Verify the signatures in the patch header - if(nError == ERROR_SUCCESS) - { - // BSWAP the entire header, if needed - BSWAP_ARRAY32_UNSIGNED(&PatchHeader, sizeof(DWORD) * 6); - PatchHeader.dwXFRM = BSWAP_INT32_UNSIGNED(PatchHeader.dwXFRM); - PatchHeader.dwXfrmBlockSize = BSWAP_INT32_UNSIGNED(PatchHeader.dwXfrmBlockSize); - PatchHeader.dwPatchType = BSWAP_INT32_UNSIGNED(PatchHeader.dwPatchType); + if(PatchHeader.dwSignature != PATCH_SIGNATURE_HEADER || PatchHeader.dwMD5 != PATCH_SIGNATURE_MD5 || PatchHeader.dwXFRM != PATCH_SIGNATURE_XFRM) + return NULL; - if(PatchHeader.dwSignature != 0x48435450 || PatchHeader.dwMD5 != 0x5f35444d || PatchHeader.dwXFRM != 0x4d524658) - nError = ERROR_FILE_CORRUPT; - } - - // Read the patch, depending on patch type - if(nError == ERROR_SUCCESS) + // Allocate space for patch header and compressed data + pFullPatch = (PMPQ_PATCH_HEADER)STORM_ALLOC(BYTE, PatchHeader.dwSizeOfPatchData); + if(pFullPatch != NULL) { - switch(PatchHeader.dwPatchType) + // Copy the patch header + memcpy(pFullPatch, &PatchHeader, sizeof(MPQ_PATCH_HEADER)); + + // Read the patch, depending on patch type + if(dwErrCode == ERROR_SUCCESS) { - case 0x59504f43: // 'COPY' - nError = LoadMpqPatch_COPY(hf, &PatchHeader); - break; + switch(PatchHeader.dwPatchType) + { + case 0x59504f43: // 'COPY' + dwErrCode = LoadFilePatch_COPY(hf, pFullPatch); + break; - case 0x30445342: // 'BSD0' - nError = LoadMpqPatch_BSD0(hf, &PatchHeader); - break; + case 0x30445342: // 'BSD0' + dwErrCode = LoadFilePatch_BSD0(hf, pFullPatch); + break; - default: - nError = ERROR_FILE_CORRUPT; - break; + default: + dwErrCode = ERROR_FILE_CORRUPT; + break; + } + } + + // If something failed, free the patch buffer + if(dwErrCode != ERROR_SUCCESS) + { + STORM_FREE(pFullPatch); + pFullPatch = NULL; } } - return nError; + // Give the result to the caller + return pFullPatch; } -static int ApplyMpqPatch( - TMPQFile * hf, - TPatchHeader * pPatchHeader) +static DWORD ApplyFilePatch( + TMPQPatcher * pPatcher, + PMPQ_PATCH_HEADER pFullPatch) { - int nError = ERROR_SUCCESS; + LPBYTE pbSource = (pPatcher->nCounter & 0x1) ? pPatcher->pbFileData2 : pPatcher->pbFileData1; + LPBYTE pbTarget = (pPatcher->nCounter & 0x1) ? pPatcher->pbFileData1 : pPatcher->pbFileData2; + DWORD dwErrCode; - // Verify the original file before patching - if(pPatchHeader->dwSizeBeforePatch != 0) + // Sanity checks + assert(pFullPatch->dwSizeAfterPatch <= pPatcher->cbMaxFileData); + + // Apply the patch according to the type + switch(pFullPatch->dwPatchType) { - if(!VerifyDataBlockHash(hf->pbFileData, hf->cbFileData, pPatchHeader->md5_before_patch)) - nError = ERROR_FILE_CORRUPT; - } + case 0x59504f43: // 'COPY' + dwErrCode = ApplyFilePatch_COPY(pPatcher, pFullPatch, pbTarget, pbSource); + break; - // Apply the patch - if(nError == ERROR_SUCCESS) - { - switch(pPatchHeader->dwPatchType) - { - case 0x59504f43: // 'COPY' - nError = ApplyMpqPatch_COPY(hf, pPatchHeader); - break; + case 0x30445342: // 'BSD0' + dwErrCode = ApplyFilePatch_BSD0(pPatcher, pFullPatch, pbTarget, pbSource); + break; - case 0x30445342: // 'BSD0' - nError = ApplyMpqPatch_BSD0(hf, pPatchHeader); - break; - - default: - nError = ERROR_FILE_CORRUPT; - break; - } + default: + dwErrCode = ERROR_FILE_CORRUPT; + break; } // Verify MD5 after patch - if(nError == ERROR_SUCCESS && pPatchHeader->dwSizeAfterPatch != 0) + if(dwErrCode == ERROR_SUCCESS && pFullPatch->dwSizeAfterPatch != 0) { // Verify the patched file - if(!VerifyDataBlockHash(hf->pbFileData, hf->cbFileData, pPatchHeader->md5_after_patch)) - nError = ERROR_FILE_CORRUPT; + if(!VerifyDataBlockHash(pbTarget, pFullPatch->dwSizeAfterPatch, pFullPatch->md5_after_patch)) + dwErrCode = ERROR_FILE_CORRUPT; + + // Copy the MD5 of the new block + memcpy(pPatcher->this_md5, pFullPatch->md5_after_patch, MD5_DIGEST_SIZE); } - return nError; + return dwErrCode; } //----------------------------------------------------------------------------- -// Public functions (StormLib internals) +// Local functions (patch prefix matching) -bool IsIncrementalPatchFile(const void * pvData, DWORD cbData, LPDWORD pdwPatchedFileSize) +static bool CreatePatchPrefix(TMPQArchive * ha, const char * szFileName, size_t nLength) { - TPatchHeader * pPatchHeader = (TPatchHeader *)pvData; - BLIZZARD_BSDIFF40_FILE DiffFile; - DWORD dwPatchType; + TMPQNamePrefix * pNewPrefix; - if(cbData >= sizeof(TPatchHeader) + sizeof(BLIZZARD_BSDIFF40_FILE)) + // If the length of the patch prefix was not entered, find it + // Not that the patch prefix must always begin with backslash + if(szFileName != NULL && nLength == 0) + nLength = strlen(szFileName); + + // Create the patch prefix + pNewPrefix = (TMPQNamePrefix *)STORM_ALLOC(BYTE, sizeof(TMPQNamePrefix) + nLength + 1); + if(pNewPrefix != NULL) { - dwPatchType = BSWAP_INT32_UNSIGNED(pPatchHeader->dwPatchType); - if(dwPatchType == 0x30445342) + // Fill the name prefix. Also add the backslash + if(szFileName && nLength) { - // Give the caller the patch file size - if(pdwPatchedFileSize != NULL) + memcpy(pNewPrefix->szPatchPrefix, szFileName, nLength); + if(pNewPrefix->szPatchPrefix[nLength - 1] != '\\') + pNewPrefix->szPatchPrefix[nLength++] = '\\'; + } + + // Terminate the string and fill the length + pNewPrefix->szPatchPrefix[nLength] = 0; + pNewPrefix->nLength = nLength; + } + + ha->pPatchPrefix = pNewPrefix; + return (pNewPrefix != NULL); +} + +static bool CheckAndCreatePatchPrefix(TMPQArchive * ha, const char * szPatchPrefix, size_t nLength) +{ + char szTempName[MAX_SC2_PATCH_PREFIX + 0x41]; + bool bResult = false; + + // Prepare the patch file name + if(nLength > MAX_SC2_PATCH_PREFIX) + return false; + + // Prepare the patched file name + memcpy(szTempName, szPatchPrefix, nLength); + memcpy(&szTempName[nLength], "\\(patch_metadata)", 18); + + // Verifywhether that file exists + if(GetFileEntryLocale(ha, szTempName, 0) != NULL) + bResult = CreatePatchPrefix(ha, szPatchPrefix, nLength); + + return bResult; +} + +static bool IsMatchingPatchFile( + TMPQArchive * ha, + const char * szFileName, + LPBYTE pbBaseFileMd5) +{ + MPQ_PATCH_HEADER PatchHeader = {0}; + HANDLE hFile = NULL; + DWORD dwTransferred = 0; + DWORD dwFlags = 0; + bool bResult = false; + + // Open the file and load the patch header + if(SFileOpenFileEx((HANDLE)ha, szFileName, SFILE_OPEN_BASE_FILE, &hFile)) + { + // Retrieve the flags. We need to know whether the file is a patch or not + SFileGetFileInfo(hFile, SFileInfoFlags, &dwFlags, sizeof(DWORD), &dwTransferred); + if(dwFlags & MPQ_FILE_PATCH_FILE) + { + // Load the patch header + SFileReadFile(hFile, &PatchHeader, sizeof(MPQ_PATCH_HEADER), &dwTransferred, NULL); + BSWAP_ARRAY32_UNSIGNED(&PatchHeader, sizeof(DWORD) * 6); + + // If the file contains an incremental patch, + // compare the "MD5 before patching" with the base file MD5 + if(dwTransferred == sizeof(MPQ_PATCH_HEADER) && PatchHeader.dwSignature == PATCH_SIGNATURE_HEADER) + bResult = (!memcmp(PatchHeader.md5_before_patch, pbBaseFileMd5, MD5_DIGEST_SIZE)); + } + else + { + // TODO: How to match it if it's not an incremental patch? + // Example: StarCraft II\Updates\enGB\s2-update-enGB-23258.MPQ: + // Mods\Core.SC2Mod\enGB.SC2Assets\StreamingBuckets.txt" + bResult = false; + } + + // Close the file + SFileCloseFile(hFile); + } + + return bResult; +} + +static const char * FindArchiveLanguage(TMPQArchive * ha, PLOCALIZED_MPQ_INFO pMpqInfo) +{ + TFileEntry * pFileEntry; + const char * szLanguage = LanguageList; + char szFileName[0x40]; + + // Iterate through all localized languages + while(pMpqInfo->szNameTemplate != NULL) + { + // Iterate through all languages + for(szLanguage = LanguageList; szLanguage[0] != 0; szLanguage += 4) + { + // Construct the file name + memcpy(szFileName, pMpqInfo->szNameTemplate, pMpqInfo->nLength); + szFileName[pMpqInfo->nLangOffset + 0] = szLanguage[0]; + szFileName[pMpqInfo->nLangOffset + 1] = szLanguage[1]; + szFileName[pMpqInfo->nLangOffset + 2] = szLanguage[2]; + szFileName[pMpqInfo->nLangOffset + 3] = szLanguage[3]; + + // Append the suffix + memcpy(szFileName + pMpqInfo->nLength, "-md5.lst", 9); + + // Check whether the name exists + pFileEntry = GetFileEntryLocale(ha, szFileName, 0); + if(pFileEntry != NULL) + return szLanguage; + } + + // Move to the next language name + pMpqInfo++; + } + + // Not found + return NULL; +} + +//----------------------------------------------------------------------------- +// Finding ratch prefix for an temporary build of WoW (Pre-Cataclysm) + +static bool FindPatchPrefix_WoW_13164_13623(TMPQArchive * haBase, TMPQArchive * haPatch) +{ + const char * szPatchPrefix; + char szNamePrefix[0x08]; + + // Try to find the language of the MPQ archive + szPatchPrefix = FindArchiveLanguage(haBase, LocaleMpqs_WoW); + if(szPatchPrefix == NULL) + szPatchPrefix = "Base"; + + // Format the patch prefix + szNamePrefix[0] = szPatchPrefix[0]; + szNamePrefix[1] = szPatchPrefix[1]; + szNamePrefix[2] = szPatchPrefix[2]; + szNamePrefix[3] = szPatchPrefix[3]; + szNamePrefix[4] = '\\'; + szNamePrefix[5] = 0; + return CreatePatchPrefix(haPatch, szNamePrefix, 5); +} + +//----------------------------------------------------------------------------- +// Finding patch prefix for Starcraft II (Pre-Legacy of the Void) + +// +// This method tries to match the patch by placement of the archive (in the game subdirectory) +// +// Archive Path: %GAME_DIR%\Mods\SwarmMulti.SC2Mod\Base.SC2Data +// Patch Prefix: Mods\SwarmMulti.SC2Mod\Base.SC2Data +// +// Archive Path: %ANY_DIR%\MPQ_2013_v4_Mods#Liberty.SC2Mod#enGB.SC2Data +// Patch Prefix: Mods\Liberty.SC2Mod\enGB.SC2Data +// + +static bool CheckPatchPrefix_SC2_ArchiveName( + TMPQArchive * haPatch, + const TCHAR * szPathPtr, + const TCHAR * szSeparator, + const TCHAR * szPathEnd, + const TCHAR * szExpectedString, + size_t cchExpectedString) +{ + char szPatchPrefix[MAX_SC2_PATCH_PREFIX+0x41]; + size_t nLength = 0; + bool bResult = false; + + // Check whether the length is equal to the length of the expected string + if((size_t)(szSeparator - szPathPtr) == cchExpectedString) + { + // Now check the string itself + if(!_tcsnicmp(szPathPtr, szExpectedString, szSeparator - szPathPtr)) + { + // Copy the name string + for(; szPathPtr < szPathEnd; szPathPtr++) { - Decompress_RLE((LPBYTE)&DiffFile, sizeof(BLIZZARD_BSDIFF40_FILE), (LPBYTE)(pPatchHeader + 1), sizeof(BLIZZARD_BSDIFF40_FILE)); - DiffFile.NewFileSize = BSWAP_INT64_UNSIGNED(DiffFile.NewFileSize); - *pdwPatchedFileSize = (DWORD)DiffFile.NewFileSize; - return true; + if(szPathPtr[0] != _T('/') && szPathPtr[0] != _T('#')) + szPatchPrefix[nLength++] = (char)szPathPtr[0]; + else + szPatchPrefix[nLength++] = '\\'; } + + // Check and create the patch prefix + bResult = CheckAndCreatePatchPrefix(haPatch, szPatchPrefix, nLength); } } + return bResult; +} + +static bool FindPatchPrefix_SC2_ArchiveName(TMPQArchive * haBase, TMPQArchive * haPatch) +{ + const TCHAR * szPathBegin = FileStream_GetFileName(haBase->pStream); + const TCHAR * szSeparator = NULL; + const TCHAR * szPathEnd = szPathBegin + _tcslen(szPathBegin); + const TCHAR * szPathPtr; + int nSlashCount = 0; + int nDotCount = 0; + + // Skip the part where the patch prefix would be too long + if((szPathEnd - szPathBegin) > MAX_SC2_PATCH_PREFIX) + szPathBegin = szPathEnd - MAX_SC2_PATCH_PREFIX; + + // Search for the file extension + for(szPathPtr = szPathEnd; szPathPtr > szPathBegin; szPathPtr--) + { + if(szPathPtr[0] == _T('.')) + { + nDotCount++; + break; + } + } + + // Search for the possible begin of the prefix name + for(/* NOTHING */; szPathPtr > szPathBegin; szPathPtr--) + { + // Check the slashes, backslashes and hashes + if(szPathPtr[0] == _T('\\') || szPathPtr[0] == _T('/') || szPathPtr[0] == _T('#')) + { + if(nDotCount == 0) + return false; + szSeparator = szPathPtr; + nSlashCount++; + } + + // Check the path parts + if(szSeparator != NULL && nSlashCount >= nDotCount) + { + if(CheckPatchPrefix_SC2_ArchiveName(haPatch, szPathPtr, szSeparator, szPathEnd, _T("Battle.net"), 10)) + return true; + if(CheckPatchPrefix_SC2_ArchiveName(haPatch, szPathPtr, szSeparator, szPathEnd, _T("Campaigns"), 9)) + return true; + if(CheckPatchPrefix_SC2_ArchiveName(haPatch, szPathPtr, szSeparator, szPathEnd, _T("Mods"), 4)) + return true; + } + } + + // Not matched, sorry return false; } -int PatchFileData(TMPQFile * hf) +// +// This method tries to read the patch prefix from a helper file +// +// Example +// ========================================================= +// MPQ File Name: MPQ_2013_v4_Base1.SC2Data +// Helper File : MPQ_2013_v4_Base1.SC2Data-PATCH +// File Contains: PatchPrefix=Mods\Core.SC2Mod\Base.SC2Data +// Patch Prefix : Mods\Core.SC2Mod\Base.SC2Data +// + +static bool ExtractPatchPrefixFromFile(const TCHAR * szHelperFile, char * szPatchPrefix, size_t nMaxChars, size_t * PtrLength) { - TMPQFile * hfBase = hf; - int nError = ERROR_SUCCESS; + TFileStream * pStream; + ULONGLONG FileSize = 0; + size_t nLength; + char szFileData[MAX_PATH+1]; + bool bResult = false; - // Move to the first patch - hf = hf->hfPatchFile; - - // Now go through all patches and patch the original data - while(hf != NULL) + pStream = FileStream_OpenFile(szHelperFile, STREAM_FLAG_READ_ONLY); + if(pStream != NULL) { - // This must be true - assert(hf->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE); + // Retrieve and check the file size + FileStream_GetSize(pStream, &FileSize); + if(12 <= FileSize && FileSize < MAX_PATH) + { + // Read the entire file to memory + if(FileStream_Read(pStream, NULL, szFileData, (DWORD)FileSize)) + { + // Terminate the buffer with zero + szFileData[(DWORD)FileSize] = 0; - // Make sure that the patch data is loaded - nError = LoadMpqPatch(hf); - if(nError != ERROR_SUCCESS) - break; + // The file data must begin with the "PatchPrefix" variable + if(!_strnicmp(szFileData, "PatchPrefix", 11)) + { + char * szLinePtr = szFileData + 11; + char * szLineEnd; - // Apply the patch - nError = ApplyMpqPatch(hfBase, hf->pPatchHeader); - if(nError != ERROR_SUCCESS) - break; + // Skip spaces or '=' + while(szLinePtr[0] == ' ' || szLinePtr[0] == '=') + szLinePtr++; + szLineEnd = szLinePtr; - // Move to the next patch - hf = hf->hfPatchFile; + // Find the end + while(szLineEnd[0] != 0 && szLineEnd[0] != 0x0A && szLineEnd[0] != 0x0D) + szLineEnd++; + nLength = (size_t)(szLineEnd - szLinePtr); + + // Copy the variable + if(szLineEnd > szLinePtr && nLength <= nMaxChars) + { + memcpy(szPatchPrefix, szLinePtr, nLength); + szPatchPrefix[nLength] = 0; + PtrLength[0] = nLength; + bResult = true; + } + } + } + } + + // Close the stream + FileStream_Close(pStream); } - return nError; + return bResult; } -//----------------------------------------------------------------------------- -// Public functions + +static bool FindPatchPrefix_SC2_HelperFile(TMPQArchive * haBase, TMPQArchive * haPatch) +{ + TCHAR szHelperFile[MAX_PATH+1]; + char szPatchPrefix[MAX_SC2_PATCH_PREFIX+0x41]; + size_t nLength = 0; + bool bResult = false; + + // Create the name of the patch helper file + _tcscpy(szHelperFile, FileStream_GetFileName(haBase->pStream)); + if(_tcslen(szHelperFile) + 6 > MAX_PATH) + return false; + _tcscat(szHelperFile, _T("-PATCH")); + + // Open the patch helper file and read the line + if(ExtractPatchPrefixFromFile(szHelperFile, szPatchPrefix, MAX_SC2_PATCH_PREFIX, &nLength)) + bResult = CheckAndCreatePatchPrefix(haPatch, szPatchPrefix, nLength); + + return bResult; +} + +// +// Find match in Starcraft II patch MPQs +// Match a LST file in the root directory if the MPQ with any of the file in subdirectories +// +// The problem: +// File in the base MPQ: enGB-md5.lst +// File in the patch MPQ: Campaigns\Liberty.SC2Campaign\enGB.SC2Assets\enGB-md5.lst +// Campaigns\Liberty.SC2Campaign\enGB.SC2Data\enGB-md5.lst +// Campaigns\LibertyStory.SC2Campaign\enGB.SC2Data\enGB-md5.lst +// Campaigns\LibertyStory.SC2Campaign\enGB.SC2Data\enGB-md5.lst Mods\Core.SC2Mod\enGB.SC2Assets\enGB-md5.lst +// Mods\Core.SC2Mod\enGB.SC2Data\enGB-md5.lst +// Mods\Liberty.SC2Mod\enGB.SC2Assets\enGB-md5.lst +// Mods\Liberty.SC2Mod\enGB.SC2Data\enGB-md5.lst +// Mods\LibertyMulti.SC2Mod\enGB.SC2Data\enGB-md5.lst +// +// Solution: +// We need to match the file by its MD5 +// + +static bool FindPatchPrefix_SC2_MatchFiles(TMPQArchive * haBase, TMPQArchive * haPatch, TFileEntry * pBaseEntry) +{ + TMPQNamePrefix * pPatchPrefix; + char * szPatchFileName; + char * szPlainName; + size_t cchWorkBuffer = 0x400; + bool bResult = false; + + // First-level patches: Find the same file within the patch archive + // and verify by MD5-before-patch + if(haBase->haPatch == NULL) + { + TFileEntry * pFileTableEnd = haPatch->pFileTable + haPatch->dwFileTableSize; + TFileEntry * pFileEntry; + + // Allocate working buffer for merging LST file + szPatchFileName = STORM_ALLOC(char, cchWorkBuffer); + if(szPatchFileName != NULL) + { + // Parse the entire file table + for(pFileEntry = haPatch->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) + { + // Look for "patch_metadata" file + if(IsPatchMetadataFile(pFileEntry)) + { + // Construct the name of the MD5 file + strcpy(szPatchFileName, pFileEntry->szFileName); + szPlainName = (char *)GetPlainFileName(szPatchFileName); + strcpy(szPlainName, pBaseEntry->szFileName); + + // Check for matching MD5 file + if(IsMatchingPatchFile(haPatch, szPatchFileName, pBaseEntry->md5)) + { + bResult = CreatePatchPrefix(haPatch, szPatchFileName, (size_t)(szPlainName - szPatchFileName)); + break; + } + } + } + + // Delete the merge buffer + STORM_FREE(szPatchFileName); + } + } + + // For second-level patches, just take the patch prefix from the lower level patch + else + { + // There must be at least two patches in the chain + assert(haBase->haPatch->pPatchPrefix != NULL); + pPatchPrefix = haBase->haPatch->pPatchPrefix; + + // Copy the patch prefix + bResult = CreatePatchPrefix(haPatch, + pPatchPrefix->szPatchPrefix, + pPatchPrefix->nLength); + } + + return bResult; +} + +// Note: pBaseEntry is the file entry of the base version of "StreamingBuckets.txt" +static bool FindPatchPrefix_SC2(TMPQArchive * haBase, TMPQArchive * haPatch, TFileEntry * pBaseEntry) +{ + // Method 1: Try it by the placement of the archive. + // Works when someone is opening an archive in the game (sub)directory + if(FindPatchPrefix_SC2_ArchiveName(haBase, haPatch)) + return true; + + // Method 2: Try to locate the Name.Ext-PATCH file and read the patch prefix from it + if(FindPatchPrefix_SC2_HelperFile(haBase, haPatch)) + return true; + + // Method 3: Try to pair any version of "StreamingBuckets.txt" from the patch MPQ + // with the "StreamingBuckets.txt" in the base MPQ. Does not always work + if(FindPatchPrefix_SC2_MatchFiles(haBase, haPatch, pBaseEntry)) + return true; + + return false; +} // // Patch prefix is the path subdirectory where the patched files are within MPQ. @@ -476,6 +864,225 @@ int PatchFileData(TMPQFile * hf) // File in patch MPQ: Base\DBFilesClient\Achievement.dbc // Path prefix: Base // +// Example 3: +// Main MPQ: %GAME%\Battle.net\Battle.net.MPQ +// Patch MPQ: s2-update-base-26147.MPQ +// File in main MPQ: Battle.net\i18n\deDE\String\CLIENT_ACHIEVEMENTS.xml +// File in patch MPQ: Battle.net\Battle.net.MPQ\Battle.net\i18n\deDE\String\CLIENT_ACHIEVEMENTS.xml +// Path prefix: Battle.net\Battle.net.MPQ +// +// Example 4: +// Main MPQ: %GAME%\Campaigns\Liberty.SC2Campaign\enGB.SC2Data +// *OR* %ANY_DIR%\%ANY_NAME%Campaigns#Liberty.SC2Campaign#enGB.SC2Data +// Patch MPQ: s2-update-enGB-23258.MPQ +// File in main MPQ: LocalizedData\GameHotkeys.txt +// File in patch MPQ: Campaigns\Liberty.SC2Campaign\enGB.SC2Data\LocalizedData\GameHotkeys.txt +// Patch Prefix: Campaigns\Liberty.SC2Campaign\enGB.SC2Data +// + +static bool FindPatchPrefix(TMPQArchive * haBase, TMPQArchive * haPatch, const char * szPatchPathPrefix) +{ + TFileEntry * pFileEntry; + + // If the patch prefix was explicitly entered, we use that one + if(szPatchPathPrefix != NULL) + return CreatePatchPrefix(haPatch, szPatchPathPrefix, 0); + + // Patches for World of Warcraft - they mostly do not use prefix. + // All patches that use patch prefix have the "base\\(patch_metadata) file present + if(GetFileEntryLocale(haPatch, "base\\" PATCH_METADATA_NAME, 0)) + return FindPatchPrefix_WoW_13164_13623(haBase, haPatch); + + // Updates for Starcraft II + // Match: LocalizedData\GameHotkeys.txt <==> Campaigns\Liberty.SC2Campaign\enGB.SC2Data\LocalizedData\GameHotkeys.txt + // All Starcraft II base archives seem to have the file "StreamingBuckets.txt" present + pFileEntry = GetFileEntryLocale(haBase, "StreamingBuckets.txt", 0); + if(pFileEntry != NULL) + return FindPatchPrefix_SC2(haBase, haPatch, pFileEntry); + + // Diablo III patch MPQs don't use patch prefix + // Hearthstone MPQs don't use patch prefix + CreatePatchPrefix(haPatch, NULL, 0); + return true; +} + +//----------------------------------------------------------------------------- +// Public functions (StormLib internals) + +bool IsIncrementalPatchFile(const void * pvData, DWORD cbData, LPDWORD pdwPatchedFileSize) +{ + PMPQ_PATCH_HEADER pPatchHeader = (PMPQ_PATCH_HEADER)pvData; + BLIZZARD_BSDIFF40_FILE DiffFile; + DWORD dwPatchType; + + if(cbData >= sizeof(MPQ_PATCH_HEADER) + sizeof(BLIZZARD_BSDIFF40_FILE)) + { + dwPatchType = BSWAP_INT32_UNSIGNED(pPatchHeader->dwPatchType); + if(dwPatchType == 0x30445342) + { + // Give the caller the patch file size + if(pdwPatchedFileSize != NULL) + { + Decompress_RLE((LPBYTE)&DiffFile, sizeof(BLIZZARD_BSDIFF40_FILE), (LPBYTE)(pPatchHeader + 1), sizeof(BLIZZARD_BSDIFF40_FILE)); + DiffFile.NewFileSize = BSWAP_INT64_UNSIGNED(DiffFile.NewFileSize); + *pdwPatchedFileSize = (DWORD)DiffFile.NewFileSize; + return true; + } + } + } + + return false; +} + +DWORD Patch_InitPatcher(TMPQPatcher * pPatcher, TMPQFile * hf) +{ + DWORD cbMaxFileData = 0; + + // Overflow check + if((cbMaxFileData + (DWORD)sizeof(MPQ_PATCH_HEADER)) < cbMaxFileData) + return ERROR_NOT_ENOUGH_MEMORY; + if(hf->hfPatch == NULL) + return ERROR_INVALID_PARAMETER; + + // Initialize the entire structure with zeros + memset(pPatcher, 0, sizeof(TMPQPatcher)); + + // Copy the MD5 of the current file + memcpy(pPatcher->this_md5, hf->pFileEntry->md5, MD5_DIGEST_SIZE); + + // Find out the biggest data size needed during the patching process + while(hf != NULL) + { + if(hf->pFileEntry->dwFileSize > cbMaxFileData) + cbMaxFileData = hf->pFileEntry->dwFileSize; + hf = hf->hfPatch; + } + + // Allocate primary and secondary buffer + pPatcher->pbFileData1 = STORM_ALLOC(BYTE, cbMaxFileData); + pPatcher->pbFileData2 = STORM_ALLOC(BYTE, cbMaxFileData); + if(!pPatcher->pbFileData1 || !pPatcher->pbFileData2) + return ERROR_NOT_ENOUGH_MEMORY; + + pPatcher->cbMaxFileData = cbMaxFileData; + return ERROR_SUCCESS; +} + +// +// Note: The patch may either be applied to the base file or to the previous version +// In Starcraft II, Mods\Core.SC2Mod\Base.SC2Data, file StreamingBuckets.txt: +// +// Base file MD5: 31376b0344b6df59ad009d4296125539 +// +// s2-update-base-23258: from 31376b0344b6df59ad009d4296125539 to 941a82683452e54bf024a8d491501824 +// s2-update-base-24540: from 31376b0344b6df59ad009d4296125539 to 941a82683452e54bf024a8d491501824 +// s2-update-base-26147: from 31376b0344b6df59ad009d4296125539 to d5d5253c762fac6b9761240288a0771a +// s2-update-base-28522: from 31376b0344b6df59ad009d4296125539 to 5a76c4b356920aab7afd22e0e1913d7a +// s2-update-base-30508: from 31376b0344b6df59ad009d4296125539 to 8cb0d4799893fe801cc78ae4488a3671 +// s2-update-base-32283: from 31376b0344b6df59ad009d4296125539 to 8cb0d4799893fe801cc78ae4488a3671 +// +// We don't keep all intermediate versions in memory, as it would cause massive +// memory usage during patching process. A prime example is the file +// DBFilesClient\\Item-Sparse.db2 from locale-enGB.MPQ (WoW 16965), which has +// 9 patches in a row, each requiring 70 MB memory (35 MB patch data + 35 MB work buffer) +// + +DWORD Patch_Process(TMPQPatcher * pPatcher, TMPQFile * hf) +{ + PMPQ_PATCH_HEADER pFullPatch; + MPQ_PATCH_HEADER PatchHeader1; + MPQ_PATCH_HEADER PatchHeader2 = {0}; + TMPQFile * hfBase = hf; + DWORD cbBytesRead = 0; + DWORD dwErrCode = ERROR_SUCCESS; + + // Move to the first patch + assert(hfBase->pbFileData == NULL); + assert(hfBase->cbFileData == 0); + hf = hf->hfPatch; + + // Read the header of the current patch + SFileReadFile((HANDLE)hf, &PatchHeader1, sizeof(MPQ_PATCH_HEADER), &cbBytesRead, NULL); + if(cbBytesRead != sizeof(MPQ_PATCH_HEADER)) + return ERROR_FILE_CORRUPT; + + // Perform the patching process + while(dwErrCode == ERROR_SUCCESS && hf != NULL) + { + // Try to read the next patch header. If the md5_before_patch + // still matches we go directly to the next one and repeat + while(hf->hfPatch != NULL) + { + // Attempt to read the patch header + SFileReadFile((HANDLE)hf->hfPatch, &PatchHeader2, sizeof(MPQ_PATCH_HEADER), &cbBytesRead, NULL); + if(cbBytesRead != sizeof(MPQ_PATCH_HEADER)) + return ERROR_FILE_CORRUPT; + + // Compare the md5_before_patch + if(memcmp(PatchHeader2.md5_before_patch, pPatcher->this_md5, MD5_DIGEST_SIZE)) + break; + + // Move one patch fuhrter + PatchHeader1 = PatchHeader2; + hf = hf->hfPatch; + } + + // Allocate memory for the patch data + pFullPatch = LoadFullFilePatch(hf, PatchHeader1); + if(pFullPatch != NULL) + { + // Apply the patch + dwErrCode = ApplyFilePatch(pPatcher, pFullPatch); + STORM_FREE(pFullPatch); + } + else + { + dwErrCode = ERROR_FILE_CORRUPT; + } + + // Move to the next patch + PatchHeader1 = PatchHeader2; + pPatcher->nCounter++; + hf = hf->hfPatch; + } + + // Put the result data to the file structure + if(dwErrCode == ERROR_SUCCESS) + { + // Swap the pointer to the file data structure + if(pPatcher->nCounter & 0x01) + { + hfBase->pbFileData = pPatcher->pbFileData2; + pPatcher->pbFileData2 = NULL; + } + else + { + hfBase->pbFileData = pPatcher->pbFileData1; + pPatcher->pbFileData1 = NULL; + } + + // Also supply the data size + hfBase->cbFileData = pPatcher->cbFileData; + } + + return ERROR_SUCCESS; +} + +void Patch_Finalize(TMPQPatcher * pPatcher) +{ + if(pPatcher != NULL) + { + if(pPatcher->pbFileData1 != NULL) + STORM_FREE(pPatcher->pbFileData1); + if(pPatcher->pbFileData2 != NULL) + STORM_FREE(pPatcher->pbFileData2); + + memset(pPatcher, 0, sizeof(TMPQPatcher)); + } +} + +//----------------------------------------------------------------------------- +// Public functions bool WINAPI SFileOpenPatchArchive( HANDLE hMpq, @@ -486,25 +1093,13 @@ bool WINAPI SFileOpenPatchArchive( TMPQArchive * haPatch; TMPQArchive * ha = (TMPQArchive *)hMpq; HANDLE hPatchMpq = NULL; - char szPatchPrefixBuff[MPQ_PATCH_PREFIX_LEN]; - int nError = ERROR_SUCCESS; - - // Keep compiler happy - dwFlags = dwFlags; + DWORD dwErrCode = ERROR_SUCCESS; // Verify input parameters - if(!IsValidMpqHandle(ha)) - nError = ERROR_INVALID_HANDLE; + if(!IsValidMpqHandle(hMpq)) + dwErrCode = ERROR_INVALID_HANDLE; if(szPatchMpqName == NULL || *szPatchMpqName == 0) - nError = ERROR_INVALID_PARAMETER; - - // If the user didn't give the patch prefix, get default one - if(szPatchPathPrefix != NULL) - { - // Save length of the patch prefix - if(strlen(szPatchPathPrefix) > MPQ_PATCH_PREFIX_LEN - 2) - nError = ERROR_INVALID_PARAMETER; - } + dwErrCode = ERROR_INVALID_PARAMETER; // // We don't allow adding patches to archives that have been open for write @@ -515,63 +1110,56 @@ bool WINAPI SFileOpenPatchArchive( // 2) Modify or replace a file // 3) Add patch archive to the opened MPQ // 4) Read patched file - // 5) Now what ? + // 5) Now what? // - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { - if(!FileStream_IsReadOnly(ha->pStream)) - nError = ERROR_ACCESS_DENIED; + if(!(ha->dwFlags & MPQ_FLAG_READ_ONLY)) + dwErrCode = ERROR_ACCESS_DENIED; } // Open the archive like it is normal archive - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { - if(!SFileOpenArchive(szPatchMpqName, 0, MPQ_OPEN_READ_ONLY, &hPatchMpq)) - return false; - haPatch = (TMPQArchive *)hPatchMpq; + // These flags will be propagated to SFileOpenArchive + dwFlags = (dwFlags & MPQ_OPEN_NO_LISTFILE) | MPQ_OPEN_READ_ONLY | MPQ_OPEN_PATCH; - // 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) + // Open the patch as MPQ + if(SFileOpenArchive(szPatchMpqName, 0, dwFlags, &hPatchMpq)) { - if(!SFileHasFile(hPatchMpq, PATCH_METADATA_NAME)) + // Cast the archive handle to structure pointer + haPatch = (TMPQArchive *)hPatchMpq; + + // We need to remember the proper patch prefix to match names of patched files + if(FindPatchPrefix(ha, (TMPQArchive *)hPatchMpq, szPatchPathPrefix)) { - GetDefaultPatchPrefix(FileStream_GetFileName(ha->pStream), szPatchPrefixBuff); - szPatchPathPrefix = szPatchPrefixBuff; - } - } + // Now add the patch archive to the list of patches to the original MPQ + while(ha != NULL) + { + if(ha->haPatch == NULL) + { + haPatch->haBase = ha; + ha->haPatch = haPatch; + return true; + } - // Save the prefix for patch file names. - // Make sure that there is backslash after it - if(szPatchPathPrefix != NULL && *szPatchPathPrefix != 0) - { - strcpy(haPatch->szPatchPrefix, szPatchPathPrefix); - strcat(haPatch->szPatchPrefix, "\\"); - haPatch->cchPatchPrefix = strlen(haPatch->szPatchPrefix); - } - - // Now add the patch archive to the list of patches to the original MPQ - while(ha != NULL) - { - if(ha->haPatch == NULL) - { - haPatch->haBase = ha; - ha->haPatch = haPatch; - return true; + // Move to the next archive + ha = ha->haPatch; + } } - // Move to the next archive - ha = ha->haPatch; + // Close the archive + SFileCloseArchive(hPatchMpq); + dwErrCode = ERROR_CANT_FIND_PATCH_PREFIX; + } + else + { + dwErrCode = SErrGetLastError(); } - - // Should never happen - nError = ERROR_CAN_NOT_COMPLETE; } - SetLastError(nError); + SErrSetLastError(dwErrCode); return false; } @@ -580,7 +1168,7 @@ bool WINAPI SFileIsPatchedArchive(HANDLE hMpq) TMPQArchive * ha = (TMPQArchive *)hMpq; // Verify input parameters - if(!IsValidMpqHandle(ha)) + if(!IsValidMpqHandle(hMpq)) return false; return (ha->haPatch != NULL); diff --git a/dep/StormLib/src/SFileReadFile.cpp b/dep/StormLib/src/SFileReadFile.cpp index 5570fd466c..ee91cac4ef 100644 --- a/dep/StormLib/src/SFileReadFile.cpp +++ b/dep/StormLib/src/SFileReadFile.cpp @@ -14,116 +14,19 @@ #include "StormCommon.h" //----------------------------------------------------------------------------- -// Local structures +// External references (not public functions) -struct TFileHeader2Ext -{ - DWORD dwOffset00Data; // Required data at offset 00 (32-bits) - DWORD dwOffset00Mask; // Mask for data at offset 00 (32 bits). 0 = data are ignored - DWORD dwOffset04Data; // Required data at offset 04 (32-bits) - DWORD dwOffset04Mask; // Mask for data at offset 04 (32 bits). 0 = data are ignored - const char * szExt; // Supplied extension, if the condition is true -}; +int WINAPI SCompDecompressX(TMPQArchive * ha, void * pvOutBuffer, int * pcbOutBuffer, void * pbInBuffer, int cbInBuffer); //----------------------------------------------------------------------------- // 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; - - // Go through all open MPQs, including patches - while(ha != NULL) - { - // Only count files that are not patch files - pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; - 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 - // 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 - ha = ha->haPatch; - } - - 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. // dwByteOffset - Position of sector in the file (relative to file begin) // dwBytesToRead - Number of bytes to read. Must be multiplier of sector size. // pdwBytesRead - Stored number of bytes loaded -static int ReadMpqSectors(TMPQFile * hf, LPBYTE pbBuffer, DWORD dwByteOffset, DWORD dwBytesToRead, LPDWORD pdwBytesRead) +static DWORD ReadMpqSectors(TMPQFile * hf, LPBYTE pbBuffer, DWORD dwByteOffset, DWORD dwBytesToRead, LPDWORD pdwBytesRead) { ULONGLONG RawFilePos; TMPQArchive * ha = hf->ha; @@ -137,7 +40,7 @@ static int ReadMpqSectors(TMPQFile * hf, LPBYTE pbBuffer, DWORD dwByteOffset, DW DWORD dwSectorIndex = dwByteOffset / ha->dwSectorSize; DWORD dwSectorsDone = 0; DWORD dwBytesRead = 0; - int nError = ERROR_SUCCESS; + DWORD dwErrCode = ERROR_SUCCESS; // Note that dwByteOffset must be aligned to size of one sector // Note that dwBytesToRead must be a multiplier of one sector size @@ -150,14 +53,14 @@ static int ReadMpqSectors(TMPQFile * hf, LPBYTE pbBuffer, DWORD dwByteOffset, DW dwRawBytesToRead = dwBytesToRead; // Perform all necessary work to do with compressed files - if(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED) + if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) { // If the sector positions are not loaded yet, do it if(hf->SectorOffsets == NULL) { - nError = AllocateSectorOffsets(hf, true); - if(nError != ERROR_SUCCESS) - return nError; + dwErrCode = AllocateSectorOffsets(hf, true); + if(dwErrCode != ERROR_SUCCESS || hf->SectorOffsets == NULL) + return dwErrCode; } // If the sector checksums are not loaded yet, load them now. @@ -170,189 +73,214 @@ static int ReadMpqSectors(TMPQFile * hf, LPBYTE pbBuffer, DWORD dwByteOffset, DW // or not, we won't try that again for the given file. // - AllocateSectorChecksums(hf, true); - hf->bLoadedSectorCRCs = true; + if(AllocateSectorChecksums(hf, true) == ERROR_SUCCESS) + 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; +// dwErrCode = AllocateRawMD5s(hf, true); +// if(dwErrCode != ERROR_SUCCESS) +// return dwErrCode; // } - // If the file is compressed, also allocate secondary buffer - pbInSector = pbRawSector = STORM_ALLOC(BYTE, dwBytesToRead); - if(pbRawSector == NULL) - return ERROR_NOT_ENOUGH_MEMORY; - // Assign the temporary buffer as target for read operation dwRawSectorOffset = hf->SectorOffsets[dwSectorIndex]; dwRawBytesToRead = hf->SectorOffsets[dwSectorIndex + dwSectorsToRead] - dwRawSectorOffset; + + // If the file is compressed, also allocate secondary buffer + pbInSector = pbRawSector = STORM_ALLOC(BYTE, dwRawBytesToRead); + if(pbRawSector == NULL) + return ERROR_NOT_ENOUGH_MEMORY; } // Calculate raw file offset where the sector(s) are stored. - CalculateRawSectorOffset(RawFilePos, hf, dwRawSectorOffset); + RawFilePos = CalculateRawSectorOffset(hf, dwRawSectorOffset); // Set file pointer and read all required sectors - if(!FileStream_Read(ha->pStream, &RawFilePos, pbInSector, dwRawBytesToRead)) - return GetLastError(); - dwBytesRead = 0; - - // Now we have to decrypt and decompress all file sectors that have been loaded - for(DWORD i = 0; i < dwSectorsToRead; i++) + if(FileStream_Read(ha->pStream, &RawFilePos, pbInSector, dwRawBytesToRead)) { - DWORD dwRawBytesInThisSector = ha->dwSectorSize; - DWORD dwBytesInThisSector = ha->dwSectorSize; - DWORD dwIndex = dwSectorIndex + i; - - // If there is not enough bytes in the last sector, - // cut the number of bytes in this sector - if(dwRawBytesInThisSector > dwBytesToRead) - dwRawBytesInThisSector = dwBytesToRead; - if(dwBytesInThisSector > dwBytesToRead) - dwBytesInThisSector = dwBytesToRead; - - // If the file is compressed, we have to adjust the raw sector size - if(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED) - dwRawBytesInThisSector = hf->SectorOffsets[dwIndex + 1] - hf->SectorOffsets[dwIndex]; - - // If the file is encrypted, we have to decrypt the sector - if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) + // Now we have to decrypt and decompress all file sectors that have been loaded + for(DWORD i = 0; i < dwSectorsToRead; i++) { - BSWAP_ARRAY32_UNSIGNED(pbInSector, dwRawBytesInThisSector); + DWORD dwRawBytesInThisSector = ha->dwSectorSize; + DWORD dwBytesInThisSector = ha->dwSectorSize; + DWORD dwIndex = dwSectorIndex + i; - // If we don't know the key, try to detect it by file content - if(hf->dwFileKey == 0) + // If there is not enough bytes in the last sector, + // cut the number of bytes in this sector + if(dwRawBytesInThisSector > dwBytesToRead) + dwRawBytesInThisSector = dwBytesToRead; + if(dwBytesInThisSector > dwBytesToRead) + dwBytesInThisSector = dwBytesToRead; + + // If the file is compressed, we have to adjust the raw sector size + if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) + dwRawBytesInThisSector = hf->SectorOffsets[dwIndex + 1] - hf->SectorOffsets[dwIndex]; + + // If the file is encrypted, we have to decrypt the sector + if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) { - hf->dwFileKey = DetectFileKeyByContent(pbInSector, dwBytesInThisSector); + BSWAP_ARRAY32_UNSIGNED(pbInSector, dwRawBytesInThisSector); + + // If we don't know the key, try to detect it by file content if(hf->dwFileKey == 0) { - nError = ERROR_UNKNOWN_FILE_KEY; - break; + hf->dwFileKey = DetectFileKeyByContent(pbInSector, dwBytesInThisSector, hf->dwDataSize); + if(hf->dwFileKey == 0) + { + dwErrCode = ERROR_UNKNOWN_FILE_KEY; + break; + } } + + DecryptMpqBlock(pbInSector, dwRawBytesInThisSector, hf->dwFileKey + dwIndex); + BSWAP_ARRAY32_UNSIGNED(pbInSector, dwRawBytesInThisSector); } - DecryptMpqBlock(pbInSector, dwRawBytesInThisSector, hf->dwFileKey + dwIndex); - BSWAP_ARRAY32_UNSIGNED(pbInSector, dwRawBytesInThisSector); - } - - // If the file has sector CRC check turned on, perform it - if(hf->bCheckSectorCRCs && hf->SectorChksums != NULL) - { - DWORD dwAdlerExpected = hf->SectorChksums[dwIndex]; - DWORD dwAdlerValue = 0; - - // We can only check sector CRC when it's not zero - // Neither can we check it if it's 0xFFFFFFFF. - if(dwAdlerExpected != 0 && dwAdlerExpected != 0xFFFFFFFF) + // If the file has sector CRC check turned on, perform it + if(hf->bCheckSectorCRCs && hf->SectorChksums != NULL) { - dwAdlerValue = adler32(0, pbInSector, dwRawBytesInThisSector); - if(dwAdlerValue != dwAdlerExpected) + DWORD dwAdlerExpected = hf->SectorChksums[dwIndex]; + DWORD dwAdlerValue = 0; + + // We can only check sector CRC when it's not zero + // Neither can we check it if it's 0xFFFFFFFF. + if(dwAdlerExpected != 0 && dwAdlerExpected != 0xFFFFFFFF) { - nError = ERROR_CHECKSUM_ERROR; - break; + dwAdlerValue = adler32(0, pbInSector, dwRawBytesInThisSector); + if(dwAdlerValue != dwAdlerExpected) + { + dwErrCode = ERROR_CHECKSUM_ERROR; + break; + } } } - } - // If the sector is really compressed, decompress it. - // WARNING : Some sectors may not be compressed, it can be determined only - // by comparing uncompressed and compressed size !!! - if(dwRawBytesInThisSector < dwBytesInThisSector) - { - int cbOutSector = dwBytesInThisSector; - int cbInSector = dwRawBytesInThisSector; - int nResult = 0; - - // Is the file compressed by Blizzard's multiple compression ? - if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS) + // If the sector is really compressed, decompress it. + // WARNING : Some sectors may not be compressed, it can be determined only + // by comparing uncompressed and compressed size !!! + if(dwRawBytesInThisSector < dwBytesInThisSector) { - if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2) - nResult = SCompDecompress2((char *)pbOutSector, &cbOutSector, (char *)pbInSector, cbInSector); + if(dwRawBytesInThisSector != 0) + { + int cbOutSector = dwBytesInThisSector; + int cbInSector = dwRawBytesInThisSector; + int nResult = 0; + + // Is the file compressed by Blizzard's multiple compression ? + if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS) + { + // Remember the last used compression + hf->dwCompression0 = pbInSector[0]; + + // Decompress the data. We need to perform MPQ-specific decompression, + // as multiple Blizzard games may have their own decompression tables + // and even decompression methods. + nResult = SCompDecompressX(ha, pbOutSector, &cbOutSector, pbInSector, cbInSector); + } + + // Is the file compressed by PKWARE Data Compression Library ? + else if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE) + { + nResult = SCompExplode(pbOutSector, &cbOutSector, pbInSector, cbInSector); + } + + // Did the decompression fail ? + if(nResult == 0) + { + dwErrCode = ERROR_FILE_CORRUPT; + break; + } + + // Special case (MPQ_2024_v1_300TK2.09p.w3x, file File00010254.blp): + // Extracted less than required. Fill the rest with zeros + if((DWORD)(cbOutSector) < dwBytesInThisSector) + { + memset(pbOutSector + cbOutSector, 0, dwBytesInThisSector - cbOutSector); + } + } else - nResult = SCompDecompress((char *)pbOutSector, &cbOutSector, (char *)pbInSector, cbInSector); + { + memset(pbOutSector, 0, dwBytesInThisSector); + } } - - // Is the file compressed by PKWARE Data Compression Library ? - else if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE) + else { - nResult = SCompExplode((char *)pbOutSector, &cbOutSector, (char *)pbInSector, cbInSector); + if(pbOutSector != pbInSector) + memcpy(pbOutSector, pbInSector, dwBytesInThisSector); } - // Did the decompression fail ? - if(nResult == 0) - { - nError = ERROR_FILE_CORRUPT; - break; - } + // Move pointers + dwBytesToRead -= dwBytesInThisSector; + dwByteOffset += dwBytesInThisSector; + dwBytesRead += dwBytesInThisSector; + pbOutSector += dwBytesInThisSector; + pbInSector += dwRawBytesInThisSector; + dwSectorsDone++; } - else - { - if(pbOutSector != pbInSector) - memcpy(pbOutSector, pbInSector, dwBytesInThisSector); - } - - // Move pointers - dwBytesToRead -= dwBytesInThisSector; - dwByteOffset += dwBytesInThisSector; - dwBytesRead += dwBytesInThisSector; - pbOutSector += dwBytesInThisSector; - pbInSector += dwRawBytesInThisSector; - dwSectorsDone++; + } + else + { + dwErrCode = SErrGetLastError(); } // Free all used buffers if(pbRawSector != NULL) STORM_FREE(pbRawSector); - + // Give the caller thenumber of bytes read *pdwBytesRead = dwBytesRead; - return nError; + return dwErrCode; } -static int ReadMpqFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead) +static DWORD 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 = ERROR_SUCCESS; + LPBYTE pbRawData; + DWORD dwErrCode = ERROR_SUCCESS; // If the file buffer is not allocated yet, do it. if(hf->pbFileSector == NULL) { - nError = AllocateSectorBuffer(hf); - if(nError != ERROR_SUCCESS) - return nError; - pbRawData = hf->pbFileSector; + dwErrCode = AllocateSectorBuffer(hf); + if(dwErrCode != ERROR_SUCCESS || hf->pbFileSector == NULL) + return dwErrCode; } // If the file is a patch file, adjust raw data offset if(hf->pPatchInfo != NULL) RawFilePos += hf->pPatchInfo->dwLength; + pbRawData = hf->pbFileSector; // If the file sector is not loaded yet, do it if(hf->dwSectorOffs != 0) { + DWORD cbRawData = hf->dwDataSize; + // Is the file compressed? - if(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED) + if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) { // Allocate space for compressed data pbCompressed = STORM_ALLOC(BYTE, pFileEntry->dwCmpSize); if(pbCompressed == NULL) return ERROR_NOT_ENOUGH_MEMORY; + + // Redirect reading pbRawData = pbCompressed; + cbRawData = pFileEntry->dwCmpSize; } - + // Load the raw (compressed, encrypted) data - if(!FileStream_Read(ha->pStream, &RawFilePos, pbRawData, pFileEntry->dwCmpSize)) + if(!FileStream_Read(ha->pStream, &RawFilePos, pbRawData, cbRawData)) { STORM_FREE(pbCompressed); - return GetLastError(); + return SErrGetLastError(); } // If the file is encrypted, we have to decrypt the data first @@ -364,7 +292,7 @@ static int ReadMpqFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos } // If the file is compressed, we have to decompress it now - if(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED) + if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) { int cbOutBuffer = (int)hf->dwDataSize; int cbInBuffer = (int)pFileEntry->dwCmpSize; @@ -380,26 +308,126 @@ static int ReadMpqFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos // -------------------------------------- ---------- -------- -------- --------------- // 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) + if(pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE && cbInBuffer > sizeof(TPatchInfo)) cbInBuffer = cbInBuffer - sizeof(TPatchInfo); // Is the file compressed by Blizzard's multiple compression ? if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS) { + // Remember the last used compression + hf->dwCompression0 = pbRawData[0]; + + // Decompress the file if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2) - nResult = SCompDecompress2((char *)hf->pbFileSector, &cbOutBuffer, (char *)pbRawData, cbInBuffer); + nResult = SCompDecompress2(hf->pbFileSector, &cbOutBuffer, pbRawData, cbInBuffer); else - nResult = SCompDecompress((char *)hf->pbFileSector, &cbOutBuffer, (char *)pbRawData, cbInBuffer); + nResult = SCompDecompress(hf->pbFileSector, &cbOutBuffer, pbRawData, cbInBuffer); } // 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); + nResult = SCompExplode(hf->pbFileSector, &cbOutBuffer, pbRawData, cbInBuffer); - nError = (nResult != 0) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT; + dwErrCode = (nResult != 0) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT; + } + else + { + if(hf->pbFileSector != NULL && 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(dwErrCode == ERROR_SUCCESS && hf->dwSectorOffs == 0) + { + // File position is greater or equal to file size ? + if(dwFilePos >= hf->dwDataSize) + { + *pdwBytesRead = 0; + return ERROR_SUCCESS; + } + + // If not enough bytes remaining in the file, cut them + if((hf->dwDataSize - dwFilePos) < dwToRead) + dwToRead = (hf->dwDataSize - dwFilePos); + + // Copy the bytes + memcpy(pvBuffer, hf->pbFileSector + dwFilePos, dwToRead); + + // Give the number of bytes read + *pdwBytesRead = dwToRead; + } + + // An error, sorry + return dwErrCode; +} + +static DWORD ReadMpkFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead) +{ + ULONGLONG RawFilePos = hf->RawFilePos + 0x0C; // For some reason, MPK files start at position (hf->RawFilePos + 0x0C) + TMPQArchive * ha = hf->ha; + TFileEntry * pFileEntry = hf->pFileEntry; + LPBYTE pbCompressed = NULL; + LPBYTE pbRawData = hf->pbFileSector; + DWORD dwErrCode = ERROR_SUCCESS; + + // We do not support patch files in MPK archives + assert(hf->pPatchInfo == NULL); + + // If the file buffer is not allocated yet, do it. + if(hf->pbFileSector == NULL) + { + dwErrCode = AllocateSectorBuffer(hf); + if(dwErrCode != ERROR_SUCCESS || hf->pbFileSector == NULL) + return dwErrCode; + pbRawData = hf->pbFileSector; + } + + // If the file sector is not loaded yet, do it + if(hf->dwSectorOffs != 0) + { + // Is the file compressed? + if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) + { + // Allocate space for compressed data + pbCompressed = STORM_ALLOC(BYTE, pFileEntry->dwCmpSize); + if(pbCompressed == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + pbRawData = pbCompressed; + } + + // Load the raw (compressed, encrypted) data + if(!FileStream_Read(ha->pStream, &RawFilePos, pbRawData, pFileEntry->dwCmpSize)) + { + STORM_FREE(pbCompressed); + return SErrGetLastError(); + } + + // If the file is encrypted, we have to decrypt the data first + if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) + { + DecryptMpkTable(pbRawData, pFileEntry->dwCmpSize); + } + + // If the file is compressed, we have to decompress it now on + if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) + { + int cbOutBuffer = (int)hf->dwDataSize; + + hf->dwCompression0 = pbRawData[0]; + if(!SCompDecompressMpk(hf->pbFileSector, &cbOutBuffer, pbRawData, (int)pFileEntry->dwCmpSize)) + dwErrCode = ERROR_FILE_CORRUPT; } else { @@ -417,7 +445,7 @@ static int ReadMpqFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos // At this moment, we have the file loaded into the file buffer. // Copy as much as the caller wants - if(nError == ERROR_SUCCESS && hf->dwSectorOffs == 0) + if(dwErrCode == ERROR_SUCCESS && hf->dwSectorOffs == 0) { // File position is greater or equal to file size ? if(dwFilePos >= hf->dwDataSize) @@ -442,7 +470,8 @@ static int ReadMpqFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos return ERROR_CAN_NOT_COMPLETE; } -static int ReadMpqFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwBytesToRead, LPDWORD pdwBytesRead) + +static DWORD ReadMpqFileSectorFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwBytesToRead, LPDWORD pdwBytesRead) { TMPQArchive * ha = hf->ha; LPBYTE pbBuffer = (BYTE *)pvBuffer; @@ -450,7 +479,7 @@ static int ReadMpqFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dw DWORD dwSectorSizeMask = ha->dwSectorSize - 1; // Mask for block size, usually 0x0FFF DWORD dwFileSectorPos; // File offset of the loaded sector DWORD dwBytesRead; // Number of bytes read (temporary variable) - int nError; + DWORD dwErrCode; // If the file position is at or beyond end of file, do nothing if(dwFilePos >= hf->dwDataSize) @@ -469,9 +498,9 @@ static int ReadMpqFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dw // If the file sector buffer is not allocated yet, do it now if(hf->pbFileSector == NULL) { - nError = AllocateSectorBuffer(hf); - if(nError != ERROR_SUCCESS) - return nError; + dwErrCode = AllocateSectorBuffer(hf); + if(dwErrCode != ERROR_SUCCESS || hf->pbFileSector == NULL) + return dwErrCode; } // Load the first (incomplete) file sector @@ -479,15 +508,15 @@ static int ReadMpqFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dw { DWORD dwBytesInSector = ha->dwSectorSize; DWORD dwBufferOffs = dwFilePos & dwSectorSizeMask; - DWORD dwToCopy; + DWORD dwToCopy; // Is the file sector already loaded ? if(hf->dwSectorOffs != dwFileSectorPos) { // Load one MPQ sector into archive buffer - nError = ReadMpqSectors(hf, hf->pbFileSector, dwFileSectorPos, ha->dwSectorSize, &dwBytesInSector); - if(nError != ERROR_SUCCESS) - return nError; + dwErrCode = ReadMpqSectors(hf, hf->pbFileSector, dwFileSectorPos, ha->dwSectorSize, &dwBytesInSector); + if(dwErrCode != ERROR_SUCCESS) + return dwErrCode; // Remember that the data loaded to the sector have new file offset hf->dwSectorOffs = dwFileSectorPos; @@ -519,9 +548,9 @@ static int ReadMpqFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dw DWORD dwBlockBytes = dwBytesToRead & ~dwSectorSizeMask; // Load all sectors to the output buffer - nError = ReadMpqSectors(hf, pbBuffer, dwFileSectorPos, dwBlockBytes, &dwBytesRead); - if(nError != ERROR_SUCCESS) - return nError; + dwErrCode = ReadMpqSectors(hf, pbBuffer, dwFileSectorPos, dwBlockBytes, &dwBytesRead); + if(dwErrCode != ERROR_SUCCESS) + return dwErrCode; // Update pointers dwTotalBytesRead += dwBytesRead; @@ -539,9 +568,9 @@ static int ReadMpqFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dw if(hf->dwSectorOffs != dwFileSectorPos) { // Load one MPQ sector into archive buffer - nError = ReadMpqSectors(hf, hf->pbFileSector, dwFileSectorPos, ha->dwSectorSize, &dwBytesRead); - if(nError != ERROR_SUCCESS) - return nError; + dwErrCode = ReadMpqSectors(hf, hf->pbFileSector, dwFileSectorPos, ha->dwSectorSize, &dwBytesRead); + if(dwErrCode != ERROR_SUCCESS) + return dwErrCode; // Remember that the data loaded to the sector have new file offset hf->dwSectorOffs = dwFileSectorPos; @@ -553,7 +582,7 @@ static int ReadMpqFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dw // Copy the data from the cached last sector to the caller's buffer memcpy(pbBuffer, hf->pbFileSector, dwToCopy); - + // Update pointers dwTotalBytesRead += dwToCopy; } @@ -563,41 +592,41 @@ static int ReadMpqFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dw return ERROR_SUCCESS; } -static int ReadMpqFilePatchFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead) +static DWORD ReadMpqFilePatchFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead) { + TMPQPatcher Patcher; DWORD dwBytesToRead = dwToRead; DWORD dwBytesRead = 0; - int nError = ERROR_SUCCESS; + DWORD dwErrCode = ERROR_SUCCESS; // Make sure that the patch file is loaded completely - if(hf->pbFileData == NULL) + if(dwErrCode == ERROR_SUCCESS && hf->pbFileData == NULL) { - // Load the original file and store its content to "pbOldData" - hf->pbFileData = STORM_ALLOC(BYTE, hf->pFileEntry->dwFileSize); - hf->cbFileData = hf->pFileEntry->dwFileSize; - if(hf->pbFileData == NULL) - return ERROR_NOT_ENOUGH_MEMORY; + // Initialize patching process and allocate data + dwErrCode = Patch_InitPatcher(&Patcher, hf); + if(dwErrCode != ERROR_SUCCESS) + return dwErrCode; - // Read the file data + // Set the current data size + Patcher.cbFileData = hf->pFileEntry->dwFileSize; + + // Initialize the patcher object with initial file data if(hf->pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT) - nError = ReadMpqFileSingleUnit(hf, hf->pbFileData, 0, hf->cbFileData, &dwBytesRead); + dwErrCode = ReadMpqFileSingleUnit(hf, Patcher.pbFileData1, 0, Patcher.cbFileData, &dwBytesRead); else - nError = ReadMpqFile(hf, hf->pbFileData, 0, hf->cbFileData, &dwBytesRead); + dwErrCode = ReadMpqFileSectorFile(hf, Patcher.pbFileData1, 0, Patcher.cbFileData, &dwBytesRead); - // Fix error code - if(nError == ERROR_SUCCESS && dwBytesRead != hf->cbFileData) - nError = ERROR_FILE_CORRUPT; + // Perform the patching process + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = Patch_Process(&Patcher, hf); - // Patch the file data - if(nError == ERROR_SUCCESS) - nError = PatchFileData(hf); - - // Reset number of bytes read to zero + // Finalize the patcher structure + Patch_Finalize(&Patcher); dwBytesRead = 0; } // If there is something to read, do it - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { if(dwFilePos < hf->cbFileData) { @@ -611,13 +640,45 @@ static int ReadMpqFilePatchFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, } // Set the proper error code - nError = (dwBytesRead == dwBytesToRead) ? ERROR_SUCCESS : ERROR_HANDLE_EOF; + dwErrCode = (dwBytesRead == dwBytesToRead) ? ERROR_SUCCESS : ERROR_HANDLE_EOF; } // Give the result to the caller if(pdwBytesRead != NULL) *pdwBytesRead = dwBytesRead; - return nError; + return dwErrCode; +} + +static DWORD ReadMpqFileLocalFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead) +{ + ULONGLONG FilePosition1 = dwFilePos; + ULONGLONG FilePosition2; + DWORD dwBytesRead = 0; + DWORD dwErrCode = ERROR_SUCCESS; + + assert(hf->pStream != NULL); + + // Because stream I/O functions are designed to read + // "all or nothing", we compare file position before and after, + // and if they differ, we assume that number of bytes read + // is the difference between them + + if(!FileStream_Read(hf->pStream, &FilePosition1, pvBuffer, dwToRead)) + { + // If not all bytes have been read, then return the number of bytes read + if((dwErrCode = SErrGetLastError()) == ERROR_HANDLE_EOF) + { + FileStream_GetPos(hf->pStream, &FilePosition2); + dwBytesRead = (DWORD)(FilePosition2 - FilePosition1); + } + } + else + { + dwBytesRead = dwToRead; + } + + *pdwBytesRead = dwBytesRead; + return dwErrCode; } //----------------------------------------------------------------------------- @@ -625,94 +686,90 @@ static int ReadMpqFilePatchFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, bool WINAPI SFileReadFile(HANDLE hFile, void * pvBuffer, DWORD dwToRead, LPDWORD pdwRead, LPOVERLAPPED lpOverlapped) { - TMPQFile * hf = (TMPQFile *)hFile; + TFileEntry * pFileEntry; + TMPQFile * hf; DWORD dwBytesRead = 0; // Number of bytes read - int nError = ERROR_SUCCESS; + DWORD dwErrCode = ERROR_SUCCESS; - // Keep compilers happy + // Always zero the result + if(pdwRead != NULL) + *pdwRead = 0; lpOverlapped = lpOverlapped; // Check valid parameters - if(!IsValidFileHandle(hf)) + if((hf = IsValidFileHandle(hFile)) == NULL) { - SetLastError(ERROR_INVALID_HANDLE); + SErrSetLastError(ERROR_INVALID_HANDLE); return false; } if(pvBuffer == NULL) { - SetLastError(ERROR_INVALID_PARAMETER); + SErrSetLastError(ERROR_INVALID_PARAMETER); return false; } + // If we didn't load the patch info yet, do it now + if(hf->pFileEntry != NULL && (hf->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) && hf->pPatchInfo == NULL) + { + dwErrCode = AllocatePatchInfo(hf, true); + if(dwErrCode != ERROR_SUCCESS || hf->pPatchInfo == NULL) + { + SErrSetLastError(dwErrCode); + return false; + } + } + + // Clear the last used compression + pFileEntry = hf->pFileEntry; + hf->dwCompression0 = 0; + // If the file is local file, read the data directly from the stream if(hf->pStream != NULL) { - ULONGLONG FilePosition1; - ULONGLONG FilePosition2; - - // Because stream I/O functions are designed to read - // "all or nothing", we compare file position before and after, - // and if they differ, we assume that number of bytes read - // is the difference between them - - 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); - dwBytesRead = (DWORD)(FilePosition2 - FilePosition1); - } - else - { - nError = GetLastError(); - } - } - else - { - dwBytesRead = dwToRead; - } + dwErrCode = ReadMpqFileLocalFile(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead); } + + // If the file is a patch file, we have to read it special way + else if(hf->hfPatch != NULL && (pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0) + { + dwErrCode = ReadMpqFilePatchFile(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead); + } + + // If the archive is a MPK archive, we need special way to read the file + else if(hf->ha->dwSubType == MPQ_SUBTYPE_MPK) + { + dwErrCode = ReadMpkFileSingleUnit(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead); + } + + // If the file is single unit file, redirect it to read file + else if(pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT) + { + dwErrCode = ReadMpqFileSingleUnit(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead); + } + + // Otherwise read it as sector based MPQ file else { - // 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, hf->dwFilePos, dwToRead, &dwBytesRead); - } - - // 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, hf->dwFilePos, dwToRead, &dwBytesRead); - } - - // Otherwise read it as sector based MPQ file - else - { - nError = ReadMpqFile(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead); - } - - // Increment the file position - hf->dwFilePos += dwBytesRead; + dwErrCode = ReadMpqFileSectorFile(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead); } + // Increment the file position + hf->dwFilePos += dwBytesRead; + // Give the caller the number of bytes read if(pdwRead != NULL) *pdwRead = dwBytesRead; // If the read operation succeeded, but not full number of bytes was read, // set the last error to ERROR_HANDLE_EOF - if(nError == ERROR_SUCCESS && (dwBytesRead < dwToRead)) - nError = ERROR_HANDLE_EOF; + if(dwErrCode == ERROR_SUCCESS && (dwBytesRead < dwToRead)) + dwErrCode = ERROR_HANDLE_EOF; // If something failed, set the last error value - if(nError != ERROR_SUCCESS) - SetLastError(nError); - return (nError == ERROR_SUCCESS); + if(dwErrCode != ERROR_SUCCESS) + SErrSetLastError(dwErrCode); + return (dwErrCode == ERROR_SUCCESS); } //----------------------------------------------------------------------------- @@ -724,13 +781,13 @@ DWORD WINAPI SFileGetFileSize(HANDLE hFile, LPDWORD pdwFileSizeHigh) TMPQFile * hf = (TMPQFile *)hFile; // Validate the file handle before we go on - if(IsValidFileHandle(hf)) + if(IsValidFileHandle(hFile)) { // Make sure that the variable is initialized FileSize = 0; // If the file is patched file, we have to get the size of the last version - if(hf->hfPatchFile != NULL) + if(hf->hfPatch != NULL) { // Walk through the entire patch chain, take the last version while(hf != NULL) @@ -739,7 +796,7 @@ DWORD WINAPI SFileGetFileSize(HANDLE hFile, LPDWORD pdwFileSizeHigh) FileSize = hf->pFileEntry->dwFileSize; // Move to the next patch file in the hierarchy - hf = hf->hfPatchFile; + hf = hf->hfPatch; } } else @@ -747,7 +804,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 { @@ -761,423 +818,105 @@ DWORD WINAPI SFileGetFileSize(HANDLE hFile, LPDWORD pdwFileSizeHigh) return (DWORD)FileSize; } - SetLastError(ERROR_INVALID_HANDLE); + SErrSetLastError(ERROR_INVALID_HANDLE); return SFILE_INVALID_SIZE; } DWORD WINAPI SFileSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHigh, DWORD dwMoveMethod) { TMPQFile * hf = (TMPQFile *)hFile; - ULONGLONG FilePosition; - ULONGLONG MoveOffset; - DWORD dwFilePosHi; + ULONGLONG OldPosition; + ULONGLONG NewPosition; + ULONGLONG FileSize; + ULONGLONG DeltaPos; // If the hFile is not a valid file handle, return an error. - if(!IsValidFileHandle(hf)) + if(!IsValidFileHandle(hFile)) { - SetLastError(ERROR_INVALID_HANDLE); + SErrSetLastError(ERROR_INVALID_HANDLE); return SFILE_INVALID_POS; } + // Retrieve the file size for handling the limits + if(hf->pStream != NULL) + { + FileStream_GetSize(hf->pStream, &FileSize); + } + else + { + FileSize = SFileGetFileSize(hFile, NULL); + } + + // Handle the NULL and non-NULL values of plFilePosHigh + // Non-NULL: The DeltaPos is combined from lFilePos and *lpFilePosHigh + // NULL: The DeltaPos is sign-extended value of lFilePos + DeltaPos = (plFilePosHigh != NULL) ? MAKE_OFFSET64(plFilePosHigh[0], lFilePos) : (ULONGLONG)(LONGLONG)lFilePos; + // Get the relative point where to move from switch(dwMoveMethod) { case FILE_BEGIN: - FilePosition = 0; + + // Move relative to the file begin. + OldPosition = 0; break; case FILE_CURRENT: + + // Retrieve the current file position if(hf->pStream != NULL) { - FileStream_GetPos(hf->pStream, FilePosition); + FileStream_GetPos(hf->pStream, &OldPosition); } else { - FilePosition = hf->dwFilePos; + OldPosition = hf->dwFilePos; } break; case FILE_END: - if(hf->pStream != NULL) - { - FileStream_GetSize(hf->pStream, FilePosition); - } - else - { - FilePosition = SFileGetFileSize(hFile, NULL); - } + + // Move relative to the end of the file + OldPosition = FileSize; break; default: - SetLastError(ERROR_INVALID_PARAMETER); + SErrSetLastError(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) - dwFilePosHi = *plFilePosHigh; - else - dwFilePosHi = (lFilePos & 0x80000000) ? 0xFFFFFFFF : 0; - MoveOffset = MAKE_OFFSET64(dwFilePosHi, lFilePos); + // Calculate the new position + NewPosition = OldPosition + DeltaPos; - // Now calculate the new file pointer - // Do not allow the file pointer to go before the begin of the file - FilePosition += MoveOffset; - if(FilePosition < 0) - FilePosition = 0; + // If moving backward, don't allow the new position go negative + if((LONGLONG)DeltaPos < 0) + { + if(NewPosition > FileSize) // Position is negative + { + SErrSetLastError(ERROR_NEGATIVE_SEEK); + return SFILE_INVALID_POS; + } + } + + // If moving forward, don't allow the new position go past the end of the file + else + { + if(NewPosition > FileSize) + NewPosition = FileSize; + } // Now apply the file pointer to the file if(hf->pStream != NULL) { - // Apply the new file position - if(!FileStream_Read(hf->pStream, &FilePosition, NULL, 0)) + if(!FileStream_Read(hf->pStream, &NewPosition, NULL, 0)) return SFILE_INVALID_POS; - - // Return the new file position - if(plFilePosHigh != NULL) - *plFilePosHigh = (LONG)(FilePosition >> 32); - return (DWORD)FilePosition; } - else - { - // Files in MPQ can't be bigger than 4 GB. - // We don't allow to go past 4 GB - if(FilePosition >> 32) - { - SetLastError(ERROR_INVALID_PARAMETER); - return SFILE_INVALID_POS; - } - // Change the file position - hf->dwFilePos = (DWORD)FilePosition; + // Also, store the new file position to the TMPQFile struct + hf->dwFilePos = (DWORD)NewPosition; - // Return the new file position - if(plFilePosHigh != NULL) - *plFilePosHigh = 0; - return (DWORD)FilePosition; - } -} - -//----------------------------------------------------------------------------- -// Tries to retrieve the file name - -static TFileHeader2Ext data2ext[] = -{ - {0x00005A4D, 0x0000FFFF, 0x00000000, 0x00000000, "exe"}, // EXE files - {0x00000006, 0xFFFFFFFF, 0x00000001, 0xFFFFFFFF, "dc6"}, // EXE files - {0x1A51504D, 0xFFFFFFFF, 0x00000000, 0x00000000, "mpq"}, // MPQ archive header ID ('MPQ\x1A') - {0x46464952, 0xFFFFFFFF, 0x00000000, 0x00000000, "wav"}, // WAVE header 'RIFF' - {0x324B4D53, 0xFFFFFFFF, 0x00000000, 0x00000000, "smk"}, // Old "Smacker Video" files 'SMK2' - {0x694B4942, 0xFFFFFFFF, 0x00000000, 0x00000000, "bik"}, // Bink video files (new) - {0x0801050A, 0xFFFFFFFF, 0x00000000, 0x00000000, "pcx"}, // PCX images used in Diablo I - {0x544E4F46, 0xFFFFFFFF, 0x00000000, 0x00000000, "fnt"}, // Font files used in Diablo II - {0x6D74683C, 0xFFFFFFFF, 0x00000000, 0x00000000, "html"}, // HTML 'pFileEntry; - - // Only do something if the file name is not filled - 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); - BSWAP_ARRAY32_UNSIGNED(FirstBytes, sizeof(FirstBytes)); - SFileSetFilePointer(hf, dwFilePos, NULL, FILE_BEGIN); - - // Try to guess file extension from those 2 DWORDs - 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) - { - sprintf(szPseudoName, "File%08u.%s", (unsigned int)(pFileEntry - hf->ha->pFileTable), data2ext[i].szExt); - break; - } - } - - // Put the file name to the file table - AllocateFileName(pFileEntry, szPseudoName); - } - - // Now put the file name to the file structure - 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); -} - -//----------------------------------------------------------------------------- -// Retrieves an information about an archive or about a file within the archive -// -// hMpqOrFile - Handle to an MPQ archive or to a file -// dwInfoType - Information to obtain - -#define VERIFY_MPQ_HANDLE(h) \ - if(!IsValidMpqHandle(h)) \ - { \ - nError = ERROR_INVALID_HANDLE; \ - break; \ - } - -#define VERIFY_FILE_HANDLE(h) \ - if(!IsValidFileHandle(h)) \ - { \ - nError = ERROR_INVALID_HANDLE; \ - break; \ - } - -bool WINAPI SFileGetFileInfo( - HANDLE hMpqOrFile, - DWORD dwInfoType, - void * pvFileInfo, - DWORD cbFileInfo, - LPDWORD pcbLengthNeeded) -{ - TMPQArchive * ha = (TMPQArchive *)hMpqOrFile; - 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); - - // 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; - } - - // 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_NUM_FILES: - VERIFY_MPQ_HANDLE(ha); - dwFileCount = GetMpqFileCount(ha); - cbLengthNeeded = sizeof(DWORD); - pvSrcFileInfo = &dwFileCount; - break; - - case SFILE_INFO_STREAM_FLAGS: // Deprecated - nError = ERROR_INVALID_PARAMETER; - 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; - } - - // 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 size to the caller - if(pcbLengthNeeded != NULL) - *pcbLengthNeeded = cbLengthNeeded; - } - - // Set the last error value, if needed - if(nError != ERROR_SUCCESS) - SetLastError(nError); - return (nError == ERROR_SUCCESS); + // Return the new file position + if(plFilePosHigh != NULL) + *plFilePosHigh = (LONG)(NewPosition >> 32); + return (DWORD)NewPosition; } diff --git a/dep/StormLib/src/SFileVerify.cpp b/dep/StormLib/src/SFileVerify.cpp index 7457171d88..da1cf91f26 100644 --- a/dep/StormLib/src/SFileVerify.cpp +++ b/dep/StormLib/src/SFileVerify.cpp @@ -20,29 +20,23 @@ //----------------------------------------------------------------------------- // Local defines -#define SIGNATURE_TYPE_NONE 0 -#define SIGNATURE_TYPE_WEAK 1 -#define SIGNATURE_TYPE_STRONG 2 - #define MPQ_DIGEST_UNIT_SIZE 0x10000 -typedef struct _MPQ_SIGNATURE_INFO -{ - ULONGLONG BeginMpqData; // File offset where the hashing starts - ULONGLONG BeginExclude; // Begin of the excluded area (used for (signature) file) - ULONGLONG EndExclude; // End of the excluded area (used for (signature) file) - ULONGLONG EndMpqData; // File offset where the hashing ends - ULONGLONG EndOfFile; // Size of the entire file - BYTE Signature[MPQ_STRONG_SIGNATURE_SIZE + 0x10]; - DWORD cbSignatureSize; // Length of the signature - int nSignatureType; // See SIGNATURE_TYPE_XXX - -} MPQ_SIGNATURE_INFO, *PMPQ_SIGNATURE_INFO; - //----------------------------------------------------------------------------- // Known Blizzard public keys // Created by Jean-Francois Roy using OpenSSL +static const char * szBlizzardWeakPrivateKey = + "-----BEGIN PRIVATE KEY-----" + "MIIBOQIBAAJBAJJidwS/uILMBSO5DLGsBFknIXWWjQJe2kfdfEk3G/j66w4KkhZ1" + "V61Rt4zLaMVCYpDun7FLwRjkMDSepO1q2DcCAwEAAQJANtiztVDMJh2hE1hjPDKy" + "UmEJ9U/aN3gomuKOjbQbQ/bWWcM/WfhSVHmPqtqh/bQI2UXFr0rnXngeteZHLr/b" + "8QIhAMuWriSKGMACw18/rVVfUrThs915odKBH1Alr3vMVVzZAiEAuBHPSQkgwcb6" + "L4MWaiKuOzq08mSyNqPeN8oSy18q848CIHeMn+3s+eOmu7su1UYQl6yH7OrdBd1q" + "3UxfFNEJiAbhAiAqxdCyOxHGlbM7aS3DOg3cq5ayoN2cvtV7h1R4t8OmVwIgF+5z" + "/6vkzBUsZhd8Nwyis+MeQYH0rpFpMKdTlqmPF2Q=" + "-----END PRIVATE KEY-----"; + static const char * szBlizzardWeakPublicKey = "-----BEGIN PUBLIC KEY-----" "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJJidwS/uILMBSO5DLGsBFknIXWWjQJe" @@ -119,13 +113,6 @@ static void memrev(unsigned char *buf, size_t count) } } -static bool is_valid_md5(void * pvMd5) -{ - LPDWORD Md5 = (LPDWORD)pvMd5; - - return (Md5[0] | Md5[1] | Md5[2] | Md5[3]) ? true : false; -} - static bool decode_base64_key(const char * szKeyBase64, rsa_key * key) { unsigned char decoded_key[0x200]; @@ -156,7 +143,7 @@ static void GetPlainAnsiFileName( const TCHAR * szFileName, char * szPlainName) { - const TCHAR * szPlainNameT = GetPlainFileNameT(szFileName); + const TCHAR * szPlainNameT = GetPlainFileName(szFileName); // Convert the plain name to ANSI while(*szPlainNameT != 0) @@ -186,65 +173,11 @@ static void CalculateArchiveRange( } } - // Get the MPQ data end. This is stored in our MPQ header, - // and it's been already prepared by SFileOpenArchive, + // Get the MPQ data end. This is stored in the MPQ header pSI->EndMpqData = ha->MpqPos + ha->pHeader->ArchiveSize64; // Get the size of the entire file - FileStream_GetSize(ha->pStream, pSI->EndOfFile); -} - -static bool QueryMpqSignatureInfo( - TMPQArchive * ha, - PMPQ_SIGNATURE_INFO pSI) -{ - ULONGLONG ExtraBytes; - TMPQFile * hf; - HANDLE hFile; - DWORD dwFileSize; - - // Calculate the range of the MPQ - CalculateArchiveRange(ha, pSI); - - // If there is "(signature)" file in the MPQ, it has a weak signature - 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); - - // Verify the size of the signature - hf = (TMPQFile *)hFile; - - // We have to exclude the signature file from the digest - pSI->BeginExclude = ha->MpqPos + hf->pFileEntry->ByteOffset; - pSI->EndExclude = pSI->BeginExclude + hf->pFileEntry->dwCmpSize; - dwFileSize = hf->dwDataSize; - - // Close the file - SFileCloseFile(hFile); - pSI->nSignatureType = SIGNATURE_TYPE_WEAK; - return (dwFileSize == (MPQ_WEAK_SIGNATURE_SIZE + 8)) ? true : false; - } - - // If there is extra bytes beyond the end of the archive, - // it's the strong signature - ExtraBytes = pSI->EndOfFile - pSI->EndMpqData; - if(ExtraBytes >= (MPQ_STRONG_SIGNATURE_SIZE + 4)) - { - // Read the strong signature - if(!FileStream_Read(ha->pStream, &pSI->EndMpqData, pSI->Signature, (MPQ_STRONG_SIGNATURE_SIZE + 4))) - return false; - - // Check the signature header "NGIS" - if(pSI->Signature[0] != 'N' || pSI->Signature[1] != 'G' || pSI->Signature[2] != 'I' || pSI->Signature[3] != 'S') - return false; - - pSI->nSignatureType = SIGNATURE_TYPE_STRONG; - return true; - } - - // Succeeded, but no known signature found - return true; + FileStream_GetSize(ha->pStream, &pSI->EndOfFile); } static bool CalculateMpqHashMd5( @@ -283,7 +216,7 @@ static bool CalculateMpqHashMd5( if(dwToRead == 0) break; - // Read the next chunk + // Read the next chunk if(!FileStream_Read(ha->pStream, &BeginBuffer, pbDigestBuffer, dwToRead)) { STORM_FREE(pbDigestBuffer); @@ -327,14 +260,15 @@ static void AddTailToSha1( hash_state * psha1_state, const char * szTail) { + unsigned char * pbTail = (unsigned char *)szTail; unsigned char szUpperCase[0x200]; unsigned long nLength = 0; // Convert the tail to uppercase // Note that we don't need to terminate the string with zero - while(*szTail != 0) + while(*pbTail != 0) { - szUpperCase[nLength++] = (unsigned char)toupper(*szTail++); + szUpperCase[nLength++] = AsciiToUpperTable[*pbTail++]; } // Append the tail to the SHA1 @@ -378,7 +312,7 @@ static bool CalculateMpqHashSha1( if(dwToRead == 0) break; - // Read the next chunk + // Read the next chunk if(!FileStream_Read(ha->pStream, &BeginBuffer, pbDigestBuffer, dwToRead)) { STORM_FREE(pbDigestBuffer); @@ -410,7 +344,7 @@ static bool CalculateMpqHashSha1( return true; } -static int VerifyRawMpqData( +static DWORD VerifyRawMpqData( TMPQArchive * ha, ULONGLONG ByteOffset, DWORD dwDataSize) @@ -423,7 +357,7 @@ static int VerifyRawMpqData( DWORD dwChunkCount; DWORD dwChunkSize = ha->pHeader->dwRawChunkSize; DWORD dwMD5Size; - int nError = ERROR_SUCCESS; + DWORD dwErrCode = ERROR_SUCCESS; // Don't verify zero-sized blocks if(dwDataSize == 0) @@ -443,10 +377,10 @@ static int VerifyRawMpqData( pbMD5Array1 = STORM_ALLOC(BYTE, dwMD5Size); pbMD5Array2 = STORM_ALLOC(BYTE, dwMD5Size); if(pbMD5Array1 == NULL || pbMD5Array2 == NULL) - nError = ERROR_NOT_ENOUGH_MEMORY; + dwErrCode = ERROR_NOT_ENOUGH_MEMORY; // Calculate MD5 of each data chunk - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { LPBYTE pbMD5 = pbMD5Array1; @@ -458,7 +392,7 @@ static int VerifyRawMpqData( // Read the data chunk if(!FileStream_Read(ha->pStream, &DataOffset, pbDataChunk, dwBytesInChunk)) { - nError = ERROR_FILE_CORRUPT; + dwErrCode = ERROR_FILE_CORRUPT; break; } @@ -473,19 +407,19 @@ static int VerifyRawMpqData( } // Read the MD5 array - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { // Read the array of MD5 if(!FileStream_Read(ha->pStream, &DataOffset, pbMD5Array2, dwMD5Size)) - nError = GetLastError(); + dwErrCode = SErrGetLastError(); } // Compare the array of MD5 - if(nError == ERROR_SUCCESS) + if(dwErrCode == ERROR_SUCCESS) { // Compare the MD5 if(memcmp(pbMD5Array1, pbMD5Array2, dwMD5Size)) - nError = ERROR_FILE_CORRUPT; + dwErrCode = ERROR_FILE_CORRUPT; } // Free memory and return result @@ -495,7 +429,7 @@ static int VerifyRawMpqData( STORM_FREE(pbMD5Array1); if(pbDataChunk != NULL) STORM_FREE(pbDataChunk); - return nError; + return dwErrCode; } static DWORD VerifyWeakSignature( @@ -508,6 +442,10 @@ static DWORD VerifyWeakSignature( int hash_idx = find_hash("md5"); int result = 0; + // The signature might be zeroed out. In that case, we ignore it + if(!IsValidSignature(pSI->Signature)) + return ERROR_WEAK_SIGNATURE_OK; + // Calculate hash of the entire archive, skipping the (signature) file if(!CalculateMpqHashMd5(ha, pSI, Md5Digest)) return ERROR_VERIFY_FAILED; @@ -544,7 +482,7 @@ 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; @@ -628,14 +566,16 @@ static DWORD VerifyFile( BYTE Buffer[0x1000]; HANDLE hFile = NULL; DWORD dwVerifyResult = 0; - DWORD dwSearchScope = SFILE_OPEN_FROM_MPQ; DWORD dwTotalBytes = 0; - DWORD dwBytesRead; DWORD dwCrc32 = 0; - // Fix the open type for patched archives - if(SFileIsPatchedArchive(hMpq)) - dwSearchScope = SFILE_OPEN_PATCHED_FILE; + // + // Note: When the MPQ is patched, it will + // automatically check the patched version of the file + // + + // Make sure the md5 is initialized + memset(md5, 0, sizeof(md5)); // If we have to verify raw data MD5, do it before file open if(dwFlags & SFILE_VERIFY_RAW_MD5) @@ -652,7 +592,7 @@ static DWORD VerifyFile( dwVerifyResult |= VERIFY_FILE_HAS_RAW_MD5; // Find file entry for the file - pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale); + pFileEntry = GetFileEntryLocale(ha, szFileName, g_lcFileLocale); if(pFileEntry != NULL) { // If the file's raw MD5 doesn't match, don't bother with more checks @@ -667,7 +607,7 @@ static DWORD VerifyFile( } // Attempt to open the file - if(SFileOpenFileEx(hMpq, szFileName, dwSearchScope, &hFile)) + if(SFileOpenFileEx(hMpq, szFileName, SFILE_OPEN_FROM_MPQ, &hFile)) { // Get the file size hf = (TMPQFile *)hFile; @@ -685,11 +625,13 @@ static DWORD VerifyFile( // Go through entire file and update both CRC32 and MD5 for(;;) { + DWORD dwBytesRead = 0; + // Read data from file SFileReadFile(hFile, Buffer, sizeof(Buffer), &dwBytesRead, NULL); if(dwBytesRead == 0) { - if(GetLastError() == ERROR_CHECKSUM_ERROR) + if(SErrGetLastError() == ERROR_CHECKSUM_ERROR) dwVerifyResult |= VERIFY_FILE_SECTOR_CRC_ERROR; break; } @@ -697,7 +639,7 @@ static DWORD VerifyFile( // 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); @@ -719,7 +661,7 @@ static DWORD VerifyFile( if(dwTotalBytes == 0) { // Check CRC32 and MD5 only if there is no patches - if(hf->hfPatchFile == NULL) + if(hf->hfPatch == NULL) { // Check if the CRC32 matches. if(dwFlags & SFILE_VERIFY_FILE_CRC) @@ -741,7 +683,7 @@ static DWORD VerifyFile( md5_done(&md5_state, md5); // Only check the MD5 if it is valid - if(is_valid_md5(pFileMd5)) + if(IsValidMD5(pFileMd5)) { dwVerifyResult |= VERIFY_FILE_HAS_MD5; if(memcmp(md5, pFileMd5, MD5_DIGEST_SIZE)) @@ -772,11 +714,165 @@ static DWORD VerifyFile( if(pdwCrc32 != NULL) *pdwCrc32 = dwCrc32; if(pMD5 != NULL) - memcpy(pMD5, md5, MD5_DIGEST_SIZE); + memcpy(pMD5, md5, MD5_DIGEST_SIZE); return dwVerifyResult; } +// Used in SFileGetFileInfo +bool QueryMpqSignatureInfo( + TMPQArchive * ha, + PMPQ_SIGNATURE_INFO pSI) +{ + TFileEntry * pFileEntry; + ULONGLONG ExtraBytes; + DWORD dwFileSize; + + // Make sure it's all zeroed + memset(pSI, 0, sizeof(MPQ_SIGNATURE_INFO)); + + // Flush the archive, if it was modified + if(ha->dwFlags & MPQ_FLAG_CHANGED) + SFileFlushArchive((HANDLE)(ha)); + + // Calculate the range of the MPQ + CalculateArchiveRange(ha, pSI); + + // If there is "(signature)" file in the MPQ, it has a weak signature + pFileEntry = GetFileEntryLocale(ha, SIGNATURE_NAME, LANG_NEUTRAL); + if(pFileEntry != NULL) + { + // Calculate the begin and end of the signature file itself + pSI->BeginExclude = ha->MpqPos + pFileEntry->ByteOffset; + pSI->EndExclude = pSI->BeginExclude + pFileEntry->dwCmpSize; + dwFileSize = (DWORD)(pSI->EndExclude - pSI->BeginExclude); + + // Does the signature have proper size? + if(dwFileSize == MPQ_SIGNATURE_FILE_SIZE) + { + // Read the weak signature + if(!FileStream_Read(ha->pStream, &pSI->BeginExclude, pSI->Signature, dwFileSize)) + return false; + + pSI->SignatureTypes |= SIGNATURE_TYPE_WEAK; + pSI->cbSignatureSize = dwFileSize; + return true; + } + } + + // If there is extra bytes beyond the end of the archive, + // it's the strong signature + ExtraBytes = pSI->EndOfFile - pSI->EndMpqData; + if(ExtraBytes >= (MPQ_STRONG_SIGNATURE_SIZE + 4)) + { + // Read the strong signature + if(!FileStream_Read(ha->pStream, &pSI->EndMpqData, pSI->Signature, (MPQ_STRONG_SIGNATURE_SIZE + 4))) + return false; + + // Check the signature header "NGIS" + if(pSI->Signature[0] != 'N' || pSI->Signature[1] != 'G' || pSI->Signature[2] != 'I' || pSI->Signature[3] != 'S') + return true; //Not a valid signature, but another filetype could've been appended so not always an error. + + pSI->SignatureTypes |= SIGNATURE_TYPE_STRONG; + return true; + } + + // Succeeded, but no known signature found + return true; +} + +//----------------------------------------------------------------------------- +// Support for weak signature + +DWORD SSignFileCreate(TMPQArchive * ha) +{ + TMPQFile * hf = NULL; + BYTE EmptySignature[MPQ_SIGNATURE_FILE_SIZE]; + DWORD dwErrCode = ERROR_SUCCESS; + + // Only save the signature if we should do so + if(ha->dwFileFlags3 != 0) + { + // The (signature) file must be non-encrypted and non-compressed + assert(ha->dwFlags & MPQ_FLAG_SIGNATURE_NEW); + assert(ha->dwFileFlags3 == MPQ_FILE_EXISTS); + assert(ha->dwReservedFiles > 0); + + // Create the (signature) file file in the MPQ + // Note that the file must not be compressed or encrypted + dwErrCode = SFileAddFile_Init(ha, SIGNATURE_NAME, + 0, + sizeof(EmptySignature), + LANG_NEUTRAL, + ha->dwFileFlags3 | MPQ_FILE_REPLACEEXISTING, + &hf); + + // Write the empty signature file to the archive + if(dwErrCode == ERROR_SUCCESS) + { + // Write the empty zeroed file to the MPQ + memset(EmptySignature, 0, sizeof(EmptySignature)); + dwErrCode = SFileAddFile_Write(hf, EmptySignature, (DWORD)sizeof(EmptySignature), 0); + } + + // Finalize the signature + if(dwErrCode == ERROR_SUCCESS) + { + // Clear the CRC as it will not be valid + hf->pFileEntry->dwCrc32 = hf->dwCrc32 = 0; + SFileAddFile_Finish(hf); + + // Clear the invalid mark + ha->dwFlags &= ~(MPQ_FLAG_SIGNATURE_NEW | MPQ_FLAG_SIGNATURE_NONE); + ha->dwReservedFiles--; + } + } + + return dwErrCode; +} + +DWORD SSignFileFinish(TMPQArchive * ha) +{ + MPQ_SIGNATURE_INFO si = {0}; + unsigned long signature_len = MPQ_WEAK_SIGNATURE_SIZE; + BYTE WeakSignature[MPQ_SIGNATURE_FILE_SIZE]; + BYTE Md5Digest[MD5_DIGEST_SIZE]; + rsa_key key; + int hash_idx = find_hash("md5"); + + // Sanity checks + assert((ha->dwFlags & MPQ_FLAG_CHANGED) == 0); + assert(ha->dwFileFlags3 == MPQ_FILE_EXISTS); + + // Query the weak signature info + if(!QueryMpqSignatureInfo(ha, &si)) + return ERROR_FILE_CORRUPT; + + // There must be exactly one signature + if(si.SignatureTypes != SIGNATURE_TYPE_WEAK) + return ERROR_FILE_CORRUPT; + + // Calculate MD5 of the entire archive + if(!CalculateMpqHashMd5(ha, &si, Md5Digest)) + return ERROR_VERIFY_FAILED; + + // Decode the private key + if(!decode_base64_key(szBlizzardWeakPrivateKey, &key)) + return ERROR_VERIFY_FAILED; + + // Sign the hash + memset(WeakSignature, 0, sizeof(WeakSignature)); + rsa_sign_hash_ex(Md5Digest, sizeof(Md5Digest), WeakSignature + 8, &signature_len, LTC_LTC_PKCS_1_V1_5, 0, 0, hash_idx, 0, &key); + memrev(WeakSignature + 8, MPQ_WEAK_SIGNATURE_SIZE); + rsa_free(&key); + + // Write the signature to the MPQ. Don't use SFile* functions, but write the hash directly + if(!FileStream_Write(ha->pStream, &si.BeginExclude, WeakSignature, MPQ_SIGNATURE_FILE_SIZE)) + return SErrGetLastError(); + + return ERROR_SUCCESS; +} + //----------------------------------------------------------------------------- // Public (exported) functions @@ -799,7 +895,7 @@ bool WINAPI SFileGetFileChecksums(HANDLE hMpq, const char * szFileName, LPDWORD // If verification failed, return zero if(dwVerifyResult & VERIFY_FILE_ERROR_MASK) { - SetLastError(ERROR_FILE_CORRUPT); + SErrSetLastError(ERROR_FILE_CORRUPT); return false; } @@ -817,14 +913,14 @@ DWORD WINAPI SFileVerifyFile(HANDLE hMpq, const char * szFileName, DWORD 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) +DWORD WINAPI SFileVerifyRawData(HANDLE hMpq, DWORD dwWhatToVerify, const char * szFileName) { TMPQArchive * ha = (TMPQArchive *)hMpq; TFileEntry * pFileEntry; TMPQHeader * pHeader; // Verify input parameters - if(!IsValidMpqHandle(ha)) + if(!IsValidMpqHandle(hMpq)) return ERROR_INVALID_PARAMETER; pHeader = ha->pHeader; @@ -836,7 +932,7 @@ int WINAPI SFileVerifyRawData(HANDLE hMpq, DWORD dwWhatToVerify, const char * sz 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); @@ -878,7 +974,7 @@ int WINAPI SFileVerifyRawData(HANDLE hMpq, DWORD dwWhatToVerify, const char * sz return ERROR_INVALID_PARAMETER; // Get the offset of a file - pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale); + pFileEntry = GetFileEntryLocale(ha, szFileName, g_lcFileLocale); if(pFileEntry == NULL) return ERROR_FILE_NOT_FOUND; @@ -892,30 +988,72 @@ int WINAPI SFileVerifyRawData(HANDLE hMpq, DWORD dwWhatToVerify, const char * sz // Verifies the archive against the signature DWORD WINAPI SFileVerifyArchive(HANDLE hMpq) { - MPQ_SIGNATURE_INFO si; + MPQ_SIGNATURE_INFO si = {0}; TMPQArchive * ha = (TMPQArchive *)hMpq; // Verify input parameters - if(!IsValidMpqHandle(ha)) + if(!IsValidMpqHandle(hMpq)) return ERROR_VERIFY_FAILED; // Get the MPQ signature and signature type - memset(&si, 0, sizeof(MPQ_SIGNATURE_INFO)); if(!QueryMpqSignatureInfo(ha, &si)) return ERROR_VERIFY_FAILED; - // Verify the signature - switch(si.nSignatureType) + // If there is no signature + if(si.SignatureTypes == 0) + return ERROR_NO_SIGNATURE; + + // We haven't seen a MPQ with both signatures + assert(si.SignatureTypes == SIGNATURE_TYPE_WEAK || si.SignatureTypes == SIGNATURE_TYPE_STRONG); + + // Verify the strong signature, if present + if(si.SignatureTypes & SIGNATURE_TYPE_STRONG) + return VerifyStrongSignature(ha, &si); + + // Verify the weak signature, if present + if(si.SignatureTypes & SIGNATURE_TYPE_WEAK) + return VerifyWeakSignature(ha, &si); + + return ERROR_NO_SIGNATURE; +} + +// Verifies the archive against the signature +bool WINAPI SFileSignArchive(HANDLE hMpq, DWORD dwSignatureType) +{ + TMPQArchive * ha; + + // Verify the archive handle + ha = IsValidMpqHandle(hMpq); + if(ha == NULL) { - case SIGNATURE_TYPE_NONE: - return ERROR_NO_SIGNATURE; - - case SIGNATURE_TYPE_WEAK: - return VerifyWeakSignature(ha, &si); - - case SIGNATURE_TYPE_STRONG: - return VerifyStrongSignature(ha, &si); + SErrSetLastError(ERROR_INVALID_PARAMETER); + return false; } - return ERROR_VERIFY_FAILED; + // We only support weak signature, and only for MPQs version 1.0 + if(dwSignatureType != SIGNATURE_TYPE_WEAK) + { + SErrSetLastError(ERROR_INVALID_PARAMETER); + return false; + } + + // The archive must not be malformed and must not be read-only + if(ha->dwFlags & (MPQ_FLAG_READ_ONLY | MPQ_FLAG_MALFORMED)) + { + SErrSetLastError(ERROR_ACCESS_DENIED); + return false; + } + + // If the signature is not there yet + if(ha->dwFileFlags3 == 0) + { + // Turn the signature on. The signature will + // be applied when the archive is closed + ha->dwFlags |= MPQ_FLAG_SIGNATURE_NEW | MPQ_FLAG_CHANGED; + ha->dwFileFlags3 = MPQ_FILE_EXISTS; + ha->dwReservedFiles++; + } + + return true; } + diff --git a/dep/StormLib/src/SMemUtf8.cpp b/dep/StormLib/src/SMemUtf8.cpp new file mode 100644 index 0000000000..a38ffc6010 --- /dev/null +++ b/dep/StormLib/src/SMemUtf8.cpp @@ -0,0 +1,582 @@ +/*****************************************************************************/ +/* SFileVerify.cpp Copyright (c) Ladislav Zezula 2010 */ +/*---------------------------------------------------------------------------*/ +/* Support for conversion of UTF-8 <-> File name */ +/* */ +/* File names in the MPQs are assumed to be UTF-8. However, bad sequences */ +/* or filename unsafe characters are allowed in the list files, but won't */ +/* work in unpacking files from MPQ to a local file. */ +/* */ +/* This module contains cross-platform comparable conversion between UTF-8 */ +/* and file names that will produce identical file names across platforms. */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 31.10.24 1.00 Lad Created */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "StormCommon.h" + +//----------------------------------------------------------------------------- +// Local defines + +#define MAX_INVALID_CHARS 128 // Maximum number of invalid characters in a row + +//----------------------------------------------------------------------------- +// Conversion tables + +const unsigned char SMemCharToByte[0x80] = +{ + // 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xFF + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x10 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x20 + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x30 + 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x40 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x50 + 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x60 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF // 0x70 +}; + +static TCHAR szInvalidCharPrefix[] = _T("%u["); + +//----------------------------------------------------------------------------- +// Local functions + +// Bit mask of characters that are file name safe. We will maintain +// the same charset even on non-Windows in order to keep the file names equal +static unsigned int FileNameSafeChars[4] = +{ + 0x00000000, 0x2BFF7BFB, 0xFFFFFFFF, 0xEFFFFFFF // Windows: [0x20-0x7F], except 0x22, 0x2A, 0x2F, 0x3A, 0x3C, 0x3E, 0x3F, 0x7C +// 0xfffffffe, 0xffff7fff, 0xffffffff, 0xffffffff // Linux: [0x01-0x7F], except 0x2F +}; + +static bool UTF8_IsBadFileNameCharacter(DWORD ch) +{ + // It is guaranteed that the character is in range of 0x00 - 0x7F + assert(ch < 0x80); + + // Use the bit from the table + return (FileNameSafeChars[ch / 32] & (1 << (ch % 32))) ? false : true; +} + +static DWORD UTF8_DecodeSequence(const BYTE * pbString, BYTE BitsMask, size_t ccFollowBytes, DWORD dwMinValue, DWORD dwMaxValue, DWORD & dwCodePoint, size_t & ccBytesEaten) +{ + const BYTE * pbSaveString = pbString; + DWORD dwAccumulator; + + // Extract the low bits from the leading byte + dwAccumulator = pbString[0] & BitsMask; + ccBytesEaten = 1; + pbString++; + + // Process the follow-up bytes + for(size_t i = 0; i < ccFollowBytes; i++) + { + // Every follow-up byte in the UTF-8 sequence must start with 10xxxxxx + if((pbString[0] & 0xC0) != 0x80) + return ERROR_NO_UNICODE_TRANSLATION; + + // Add 6 bits to the accumulator + dwAccumulator = (dwAccumulator << 6) | (*pbString++ & 0x3F); + } + + // Check whether the code point is in the given range + if(!(dwMinValue <= dwAccumulator && dwAccumulator <= dwMaxValue)) + return ERROR_INVALID_DATA; + + // Give the number of bytes eaten and the decoded code point + ccBytesEaten = (pbString - pbSaveString); + dwCodePoint = dwAccumulator; + return ERROR_SUCCESS; +} + +// https://en.wikipedia.org/wiki/UTF-8 +DWORD UTF8_DecodeCodePoint(const BYTE * pbString, const BYTE * pbStringEnd, DWORD & dwCodePoint, size_t & ccBytesEaten, DWORD dwFlags) +{ + // Reset the number of bytes eaten + dwCodePoint = SFILE_UTF8_INVALID_CHARACTER; + ccBytesEaten = 0; + + if(pbString < pbStringEnd) + { + // At least one byte will be eaten + ccBytesEaten = 1; + + // 1st code point (0x00 - 0x7F, 1 byte) + if(pbString[0] <= 0x7F) + { + // This is the perfect spot to check for filename-unsafe characters + if((dwFlags & SFILE_UTF8_KEEP_INVALID_FCH) == 0 && UTF8_IsBadFileNameCharacter(pbString[0])) + return ERROR_NO_UNICODE_TRANSLATION; + + // Decode the 1-byte sequence + dwCodePoint = pbString[0]; + return ERROR_SUCCESS; + } + + // 2nd code point (0x80 - 0x7FF, 2 bytes) + if((pbString[0] & 0xE0) == 0xC0 && (pbString + 2) <= pbStringEnd) + { + // Decode the 2-byte sequence + return UTF8_DecodeSequence(pbString, 0x1F, 1, 0x80, 0x7FF, dwCodePoint, ccBytesEaten); + } + + // 3rd code point (0x800 - 0xFFFF, 3 bytes) + // Note: MultiByteToWideChar will not decode 0xE0 0xBF 0xBF (--> 0x0FFF), + if((pbString[0] & 0xF0) == 0xE0 && (pbString + 3) <= pbStringEnd) + { + // Decode the 3-byte sequence + return UTF8_DecodeSequence(pbString, 0x0F, 2, 0x800, 0xFFFF, dwCodePoint, ccBytesEaten); + } + + // 4th code point (0x10000 - 0x10FFFF, 4 bytes) + if((pbString[0] & 0xF8) == 0xF0 && (pbString + 4) <= pbStringEnd) + { + // Try to decode 4-byte sequence + return UTF8_DecodeSequence(pbString, 0x07, 3, 0x10000, SFILE_UNICODE_MAX, dwCodePoint, ccBytesEaten); + } + + // An invalid UTF-8 sequence encountered + return ERROR_NO_UNICODE_TRANSLATION; + } + + // No bytes available. Should never happen + assert(false); + return ERROR_BUFFER_OVERFLOW; +} + +static size_t UTF8_EncodeSequence(DWORD dwCodePoint, BYTE LeadingByte, DWORD dwFollowByteCount, LPBYTE Utf8Buffer) +{ + DWORD dwByteShift = dwFollowByteCount * 6; + + // Encode the highest byte + Utf8Buffer[0] = (BYTE)(LeadingByte | (dwCodePoint >> dwByteShift)); + dwByteShift -= 6; + + // Encode the follow bytes + for(DWORD i = 0; i < dwFollowByteCount; i++) + { + // The follow byte must be 10xxxxxx + Utf8Buffer[i + 1] = (BYTE)(0x80 | ((dwCodePoint >> dwByteShift) & 0x3F)); + dwByteShift -= 6; + } + + return dwFollowByteCount + 1; +} + +size_t UTF8_EncodeCodePoint(DWORD dwCodePoint, LPBYTE Utf8Buffer) +{ + // 0x00 - 0x7F, 1 byte + if(dwCodePoint < 0x80) + return UTF8_EncodeSequence(dwCodePoint, 0x00, 0, Utf8Buffer); + + // 0x80 - 0x7FF + if(dwCodePoint < 0x800) + return UTF8_EncodeSequence(dwCodePoint, 0xC0, 1, Utf8Buffer); + + // 0x800 - 0xFFFF + if(dwCodePoint < 0x10000) + return UTF8_EncodeSequence(dwCodePoint, 0xE0, 2, Utf8Buffer); + + // 0x800 - 0xFFFF + if(dwCodePoint < 0x110000) + return UTF8_EncodeSequence(dwCodePoint, 0xF0, 3, Utf8Buffer); + + // Should never happen + assert(false); + return 0; +} + +static size_t UTF8_FlushInvalidChars(LPTSTR szBuffer, size_t ccBuffer, size_t nOutLength, LPBYTE InvalidChars, size_t nInvalidChars) +{ + // Case 0: No invalid char -> do nothing + if(nInvalidChars == 0) + { + return nOutLength; + } + + // Case 1: One invalid char -> %xx (compatible with previous versions of MPQ Editor) + if(nInvalidChars == 1) + { + // Space for 3 characters needed + if(szBuffer != NULL && (nOutLength + 3) <= ccBuffer) + { + szBuffer[nOutLength] = '%'; + SMemBinToStr(szBuffer + nOutLength + 1, ccBuffer - 1, InvalidChars, 1); + } + return nOutLength + 3; + } + + // Case 1: More than one invalid char -> %u[xxyyzz] + else + { + // Enough space for %u[xxyyzz] + size_t nLengthNeeded = nInvalidChars * 2 + 4; + + // Space for 4 characters needed + if(szBuffer != NULL && (nOutLength + nLengthNeeded) <= ccBuffer) + { + memcpy(szBuffer + nOutLength, szInvalidCharPrefix, sizeof(szInvalidCharPrefix) - sizeof(TCHAR)); + + SMemBinToStr(szBuffer + nOutLength + 3, ccBuffer - 3, InvalidChars, nInvalidChars); + + szBuffer[nOutLength + nLengthNeeded - 1] = ']'; + szBuffer[nOutLength + nLengthNeeded] = 0; + } + return nOutLength + nLengthNeeded; + } +} + +size_t UTF8_FlushBinBuffer(LPBYTE pbBuffer, size_t ccBuffer, size_t nOutLength, LPBYTE BinBuffer, size_t nByteCount) +{ + if(pbBuffer != NULL && (nOutLength + nByteCount) < ccBuffer) + memcpy(pbBuffer + nOutLength, BinBuffer, nByteCount); + return nOutLength + nByteCount; +} + +#ifdef STORMLIB_WIDE_CHAR +static size_t UTF16_EncodeCodePoint(DWORD dwCodePoint, unsigned short * Utf16Buffer) +{ + // https://en.wikipedia.org/wiki/UTF-16 + if(dwCodePoint <= 0xFFFF) + { + Utf16Buffer[0] = (unsigned short)(dwCodePoint); + return 1; + } + + if(dwCodePoint <= SFILE_UNICODE_MAX) + { + // Fix the code point + dwCodePoint -= 0x10000; + + // Split the code point to two 10-bit values + Utf16Buffer[0] = (unsigned short)(0xD800 + (dwCodePoint >> 10)); // High 6 bytes + Utf16Buffer[1] = (unsigned short)(0xDC00 + (dwCodePoint & 0x3FF)); // Low 10 bytes + return 2; + } + + // Should never happen + assert(false); + return 0; +} + +static DWORD UTF16_DecodeCodePoint(LPCTSTR szString, LPCTSTR szStringEnd, DWORD & dwCodePoint, size_t & ccCharsEaten) +{ + // Reset the number of bytes eaten + dwCodePoint = SFILE_UTF8_INVALID_CHARACTER; + ccCharsEaten = 0; + + if(szString < szStringEnd) + { + // At least one char will be eaten + ccCharsEaten = 1; + + // Check for an invalid surrogate pair + if(0xDC00 <= szString[0] && szString[0] <= 0xDFFF) + { + dwCodePoint = SFILE_UTF8_INVALID_CHARACTER; + return ERROR_NO_UNICODE_TRANSLATION; + } + + // Check for a valid surrogate pair + if(0xD800 <= szString[0] && szString[0] <= 0xDBFF && (szString + 1) < szStringEnd) + { + dwCodePoint = ((szString[0] - 0xD800) << 10) | (szString[1] - 0xDC00) + 0x10000; + ccCharsEaten = 2; + return ERROR_SUCCESS; + } + + // Direct encoding + dwCodePoint = szString[0]; + ccCharsEaten = 1; + return ERROR_SUCCESS; + } + + // No bytes available. Should never happen + assert(false); + return ERROR_BUFFER_OVERFLOW; +} +#endif + +size_t UTF16_IsEncodedCharSequence(LPCTSTR szString, LPCTSTR szStringEnd, LPBYTE BinBuffer) +{ + size_t nEncodedChars = 0; + + if((szString + 1) < szStringEnd && *szString++ == '%') + { + if((szString + 1) < szStringEnd && *szString++ == 'u') + { + if((szString + 1) < szStringEnd && *szString++ == '[') + { + // Keep going as long as we can convert + for(size_t i = 0; i < MAX_INVALID_CHARS; i++) + { + if(szString + (i * 2) >= szStringEnd) + break; + if(szString[i * 2] == ']') + break; + nEncodedChars++; + } + + // Did we encounter the end of the string? + if(szString + (nEncodedChars * 2) + 1 <= szStringEnd && szString[nEncodedChars * 2] == ']') + { + TCHAR HexaString[MAX_INVALID_CHARS * 2 + 1]; + + // Copy the hexadecimal string + memcpy(HexaString, szString, (nEncodedChars * 2) * sizeof(TCHAR)); + HexaString[nEncodedChars * 2] = 0; + + // Try to decode the hexa string + if(SMemStrToBin(HexaString, BinBuffer, nEncodedChars) == ERROR_SUCCESS) + { + return nEncodedChars; + } + } + } + } + } + return 0; +} + +//----------------------------------------------------------------------------- +// Public (exported) functions + +// Conversion of MPQ file name to file-name-safe string +DWORD WINAPI SMemUTF8ToFileName( + LPTSTR szBuffer, // Pointer to the output buffer. If NULL, the function will calculate the needed length + size_t ccBuffer, // Length of the output buffer (must include EOS) + const void * lpString, // Pointer to the begin of the string + const void * lpStringEnd, // Pointer to the end of string. If NULL, it's assumed to be zero-terminated + DWORD dwFlags, // Additional flags + size_t * pOutLength = NULL) // Pointer to a variable that receives the needed length (optional) +{ + const BYTE * pbStringEnd = (const BYTE *)lpStringEnd; + const BYTE * pbString = (const BYTE *)lpString; + DWORD dwErrCode = ERROR_SUCCESS; + size_t nInvalidChars = 0; + size_t nOutLength = 0; + BYTE InvalidChars[MAX_INVALID_CHARS]; + + // Set the end of the input if not specified + if(pbStringEnd == NULL) + pbStringEnd = pbString + strlen((char *)pbString); + + // Keep conversion as long + while(pbString < pbStringEnd) + { + size_t ccBytesEaten = 0; + size_t nCharLength; + DWORD dwCodePoint = 0; + + // Decode the single UTF-8 char + if((dwErrCode = UTF8_DecodeCodePoint(pbString, pbStringEnd, dwCodePoint, ccBytesEaten, dwFlags)) != ERROR_SUCCESS) + { + // Exactly one byte should be eaten on error + assert(ccBytesEaten == 1); + + // If invalid chars are allowed, we replace the result with 0xFFFD + if(dwFlags & SFILE_UTF8_REPLACE_INVALID) + { + // Replace the code point with invalid marker and continue on the next character + dwCodePoint = SFILE_UTF8_INVALID_CHARACTER; + dwErrCode = ERROR_SUCCESS; + } + + // If the invalid chars are not allowed, we put the invalid char to the stack + else + { + // Flush the invalid characters, if full + if(nInvalidChars >= _countof(InvalidChars)) + { + nOutLength = UTF8_FlushInvalidChars(szBuffer, ccBuffer, nOutLength, InvalidChars, nInvalidChars); + nInvalidChars = 0; + } + + // Put the invalid char to the stack + InvalidChars[nInvalidChars++] = pbString[0]; + pbString++; + continue; + } + } + + // Check whether the unicode char is not out of range + assert(dwCodePoint <= SFILE_UNICODE_MAX); + + // Move the source pointer by the number of bytes eaten + pbString = pbString + ccBytesEaten; + + // Flush the invalid characters, if any + nOutLength = UTF8_FlushInvalidChars(szBuffer, ccBuffer, nOutLength, InvalidChars, nInvalidChars); + nInvalidChars = 0; + +#ifdef STORMLIB_WIDE_CHAR + { + unsigned short Utf16Buffer[2]; + + // Encode the code point into UTF-16 + nCharLength = UTF16_EncodeCodePoint(dwCodePoint, Utf16Buffer); + + // Write the encoded UTF-16 to the output buffer, if present + if(szBuffer != NULL && (nOutLength + nCharLength) < ccBuffer) + { + memcpy(szBuffer + nOutLength, Utf16Buffer, nCharLength * sizeof(unsigned short)); + } + } +#else + { + BYTE Utf8Buffer[4]; + + // Encode the code point into UTF-8 + nCharLength = UTF8_EncodeCodePoint(dwCodePoint, Utf8Buffer); + + // Write the encoded UTF-16 to the output buffer, if present + if(szBuffer != NULL && (nOutLength + nCharLength) < ccBuffer) + { + memcpy(szBuffer + nOutLength, Utf8Buffer, nCharLength); + } + } +#endif + + // Increment the output length + nOutLength = nOutLength + nCharLength; + } + + // Flush the invalid characters, if any + nOutLength = UTF8_FlushInvalidChars(szBuffer, ccBuffer, nOutLength, InvalidChars, nInvalidChars); + nInvalidChars = 0; + + // Terminate the string with zero, if we still have space + if(szBuffer != NULL && nOutLength < ccBuffer) + szBuffer[nOutLength] = 0; + nOutLength++; + + // Give the output length, if required + if(pOutLength != NULL) + pOutLength[0] = nOutLength; + return dwErrCode; +} + +DWORD WINAPI SMemFileNameToUTF8( + void * lpBuffer, // Pointer to the output buffer. If NULL, the function will calculate the needed length + size_t ccBuffer, // Length of the output buffer (must include EOS) + const TCHAR * szString, // Pointer to the begin of the string + const TCHAR * szStringEnd, // Pointer to the end of string. If NULL, it's assumed to be zero-terminated + DWORD /* dwFlags */, // Additional flags + size_t * pOutLength = NULL) // Pointer to a variable that receives the needed length in bytes (optional) +{ + LPBYTE pbBuffer = (LPBYTE)lpBuffer; + size_t nOutLength = 0; + DWORD dwErrCode = ERROR_SUCCESS; + + // Set the end of the input if not specified + if(szStringEnd == NULL) + szStringEnd = szString + _tcslen(szString); + + // Keep conversion as long + while(szString < szStringEnd) + { + size_t ccCharsEaten = 0; + size_t nUtf8Length; + DWORD dwCodePoint = 0; + BYTE Utf8Buffer[MAX_INVALID_CHARS]; + + // Check for encoded sequence of bytes + if(szString[0] == '%') + { + // If there is a single hexa number ("%c7"), decode that number + if((szString + 3) <= szStringEnd) + { + TCHAR HexaString[3] = {0}; + + HexaString[0] = szString[1]; + HexaString[1] = szString[2]; + if(SMemStrToBin(HexaString, Utf8Buffer, 1) == ERROR_SUCCESS) + { + nOutLength = UTF8_FlushBinBuffer(pbBuffer, ccBuffer, nOutLength, Utf8Buffer, 1); + szString += 3; + continue; + } + } + + // If there is an escaped sequence ("%u[aabbcc]"), decode that sequence + if((nUtf8Length = UTF16_IsEncodedCharSequence(szString, szStringEnd, Utf8Buffer)) != 0) + { + nOutLength = UTF8_FlushBinBuffer(pbBuffer, ccBuffer, nOutLength, Utf8Buffer, nUtf8Length); + szString += (nUtf8Length * 2) + 4; + continue; + } + } + +#ifdef STORMLIB_WIDE_CHAR + // Try to decode the code point from UTF-16 + if((dwErrCode = UTF16_DecodeCodePoint(szString, szStringEnd, dwCodePoint, ccCharsEaten)) != ERROR_SUCCESS) + return dwErrCode; +#else + // Try to decode the code point from UTF-16 + if((dwErrCode = UTF8_DecodeCodePoint((const BYTE *)szString, (const BYTE *)szStringEnd, dwCodePoint, ccCharsEaten)) != ERROR_SUCCESS) + return dwErrCode; +#endif + + // Check whether the unicode char is not out of range + assert(dwCodePoint <= SFILE_UNICODE_MAX); + + // Move the source pointer by the number of bytes eaten + szString = szString + ccCharsEaten; + + // Encode the UNICODE char + nUtf8Length = UTF8_EncodeCodePoint(dwCodePoint, Utf8Buffer); + + // Do we have enough space in the buffer? + if(pbBuffer != NULL && (nOutLength + nUtf8Length) < ccBuffer) + { + // Write the encoded UTF-16 to the output + memcpy(pbBuffer + nOutLength, Utf8Buffer, nUtf8Length); + } + + // Increment the output length + nOutLength = nOutLength + nUtf8Length; + } + + // Terminate the string with zero, if we still have space + if(pbBuffer != NULL && nOutLength < ccBuffer) + pbBuffer[nOutLength] = 0; + nOutLength++; + + // Give the output length, if required + if(pOutLength != NULL) + pOutLength[0] = nOutLength; + return dwErrCode; +} + +//----------------------------------------------------------------------------- +// (Set/Get)LastError wrapper + +#ifndef STORMLIB_WINDOWS +#ifndef STORMLIB_WIIU +static thread_local DWORD dwLastError = ERROR_SUCCESS; +#else +static DWORD dwLastError = ERROR_SUCCESS; +#endif +#endif + +void SErrSetLastError(DWORD dwErrCode) +{ +#ifdef STORMLIB_WINDOWS + SetLastError(dwErrCode); +#else + dwLastError = dwErrCode; +#endif +} + +DWORD SErrGetLastError() +{ +#ifdef STORMLIB_WINDOWS + return GetLastError(); +#else + return dwLastError; +#endif +} diff --git a/dep/StormLib/src/StormCommon.h b/dep/StormLib/src/StormCommon.h index 8bd3b3e862..8c15a55c37 100644 --- a/dep/StormLib/src/StormCommon.h +++ b/dep/StormLib/src/StormCommon.h @@ -61,55 +61,226 @@ #define ID_MPQ_FILE 0x46494c45 // Used internally for checking TMPQFile ('FILE') -#define MPQ_WEAK_SIGNATURE_SIZE 64 -#define MPQ_STRONG_SIGNATURE_SIZE 256 - // Prevent problems with CRT "min" and "max" functions, // as they are not defined on all platforms #define STORMLIB_MIN(a, b) ((a < b) ? a : b) #define STORMLIB_MAX(a, b) ((a > b) ? a : b) +#define STORMLIB_UNUSED(p) ((void)(p)) + +// Checks for data pointers aligned to 4-byte boundary +#define STORMLIB_DWORD_ALIGNED(ptr) (((size_t)(ptr) & 0x03) == 0) + +// Check for masked flags +#define STORMLIB_TEST_FLAGS(dwFlags, dwMask, dwValue) ((dwFlags & (dwMask)) == (dwValue)) // Macro for building 64-bit file offset from two 32-bit -#define MAKE_OFFSET64(hi, lo) (((ULONGLONG)hi << 32) | lo) +#define MAKE_OFFSET64(hi, lo) (((ULONGLONG)hi << 32) | (ULONGLONG)lo) + +// Macro for checking a valid, non-empty ASCIIZ string +#ifndef IS_VALID_STRING +#define IS_VALID_STRING(str) (str && str[0]) +#endif + +//----------------------------------------------------------------------------- +// MTYPE definition - specifies what kind of MPQ is the file + +typedef enum _MTYPE +{ + MapTypeNotChecked, // The map type was not checked yet + MapTypeNotRecognized, // The file does not seems to be a map + MapTypeAviFile, // The file is actually an AVI file (Warcraft III cinematics) + MapTypeStarcraft, // The file is a Starcraft map + MapTypeWarcraft3, // The file is a Warcraft III map + MapTypeStarcraft2 // The file is a Starcraft II map +} MTYPE, *PMTYPE; + +//----------------------------------------------------------------------------- +// MPQ signature information + +// Size of each signature type +#define MPQ_WEAK_SIGNATURE_SIZE 64 +#define MPQ_STRONG_SIGNATURE_SIZE 256 +#define MPQ_STRONG_SIGNATURE_ID 0x5349474E // ID of the strong signature ("NGIS") +#define MPQ_SIGNATURE_FILE_SIZE (MPQ_WEAK_SIGNATURE_SIZE + 8) + +// MPQ signature info +typedef struct _MPQ_SIGNATURE_INFO +{ + ULONGLONG BeginMpqData; // File offset where the hashing starts + ULONGLONG BeginExclude; // Begin of the excluded area (used for (signature) file) + ULONGLONG EndExclude; // End of the excluded area (used for (signature) file) + ULONGLONG EndMpqData; // File offset where the hashing ends + ULONGLONG EndOfFile; // Size of the entire file + BYTE Signature[MPQ_STRONG_SIGNATURE_SIZE + 0x10]; + DWORD cbSignatureSize; // Length of the signature + DWORD SignatureTypes; // See SIGNATURE_TYPE_XXX + +} MPQ_SIGNATURE_INFO, *PMPQ_SIGNATURE_INFO; //----------------------------------------------------------------------------- // Memory management // // We use our own macros for allocating/freeing memory. If you want -// to redefine them, please keep the following rules +// 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. +// - The allocating function does not need 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 const* /* szFile */, int /* nLine */, size_t nSize) -{ -// return new BYTE[nSize]; - return HeapAlloc(GetProcessHeap(), 0, nSize); -} +//#if defined(_MSC_VER) && defined(_DEBUG) +// +//#define STORM_ALLOC(type, nitems) (type *)HeapAlloc(GetProcessHeap(), 0, ((nitems) * sizeof(type))) +//#define STORM_REALLOC(type, ptr, nitems) (type *)HeapReAlloc(GetProcessHeap(), 0, ptr, ((nitems) * sizeof(type))) +//#define STORM_FREE(ptr) HeapFree(GetProcessHeap(), 0, ptr) +// +//#else -__inline void DebugFree(void * ptr) -{ -// delete [] ptr; - HeapFree(GetProcessHeap(), 0, ptr); -} +#define STORM_ALLOC(type, nitems) (type *)malloc((nitems) * sizeof(type)) +#define STORM_REALLOC(type, ptr, nitems) (type *)realloc(ptr, ((nitems) * sizeof(type))) +#define STORM_FREE(ptr) free(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 +//#endif //----------------------------------------------------------------------------- // StormLib internal global variables -extern LCID lcFileLocale; // Preferred file locale +extern DWORD g_dwMpqSignature; // Marker for MPQ header +extern DWORD g_dwHashTableKey; // Key for hash table +extern DWORD g_dwBlockTableKey; // Key for block table +extern LCID g_lcFileLocale; // Preferred file locale and platform + +//----------------------------------------------------------------------------- +// Conversion to uppercase/lowercase (and "/" to "\") + +extern const unsigned char AsciiToLowerTable[256]; +extern const unsigned char AsciiToUpperTable[256]; +extern const unsigned char SMemCharToByte[0x80]; + +//----------------------------------------------------------------------------- +// Safe string functions + +template +XCHAR * SMemIntToStr(XCHAR * szBuffer, size_t cchMaxChars, XINT nValue, size_t nDigitCount = 0) +{ + XCHAR * szBufferEnd = szBuffer + cchMaxChars - 1; + XCHAR szNumberRev[0x20]; + size_t nLength = 0; + + // Always put the first digit + szNumberRev[nLength++] = (XCHAR)(nValue % 10) + '0'; + nValue /= 10; + + // Continue as long as we have non-zero + while(nValue != 0) + { + szNumberRev[nLength++] = (XCHAR)(nValue % 10) + '0'; + nValue /= 10; + } + + // Fill zeros, if needed + while(szBuffer < szBufferEnd && nLength < nDigitCount) + { + *szBuffer++ = '0'; + nDigitCount--; + } + + // Fill the buffer + while(szBuffer < szBufferEnd && nLength > 0) + { + nLength--; + *szBuffer++ = szNumberRev[nLength]; + } + + // Terminate the number with zeros + szBuffer[0] = 0; + return szBuffer; +} + +template +DWORD SMemBinToStr(XCHAR * szBuffer, size_t cchBuffer, const void * pvBinary, size_t cbBinary) +{ + const unsigned char * pbBinary = (const unsigned char *)pvBinary; + const char * SMemIntToHex = "0123456789abcdef"; + + // The size of the string must be enough to hold the binary + EOS + if(cchBuffer < ((cbBinary * 2) + 1)) + return ERROR_INSUFFICIENT_BUFFER; + + // Convert the string to the array of MD5 + // Copy the blob data as text + for(size_t i = 0; i < cbBinary; i++) + { + *szBuffer++ = SMemIntToHex[pbBinary[0] >> 0x04]; + *szBuffer++ = SMemIntToHex[pbBinary[0] & 0x0F]; + pbBinary++; + } + + // Terminate the string + *szBuffer = 0; + return ERROR_SUCCESS; +} + +template +DWORD SMemStrToBin(const XCHAR * szString, void * pvBinary, size_t cbBinary, size_t * PtrBinary = NULL) +{ + LPBYTE pbBinary = (LPBYTE)pvBinary; + LPBYTE pbBinaryEnd = pbBinary + cbBinary; + LPBYTE pbSaveBinary = pbBinary; + + // Verify parameter + if(szString != NULL && szString[0] != 0) + { + // Work as long as we have at least 2 characters ready + while(szString[0] != 0 && szString[1] != 0) + { + // Convert both to unsigned char to get rid of negative indexes produced by szString[x] + BYTE StringByte0 = (BYTE)szString[0]; + BYTE StringByte1 = (BYTE)szString[1]; + + // Each character must be within the range of 0x00-0x7F + if(StringByte0 >= 0x80 || StringByte1 >= 0x80) + return ERROR_INVALID_PARAMETER; + if(SMemCharToByte[StringByte0] == 0xFF || SMemCharToByte[StringByte1] == 0xFF) + return ERROR_INVALID_PARAMETER; + + // Overflow check + if(pbBinary >= pbBinaryEnd) + return ERROR_INSUFFICIENT_BUFFER; + + *pbBinary++ = (SMemCharToByte[StringByte0] << 0x04) | SMemCharToByte[StringByte1]; + szString += 2; + } + + // Odd number of chars? + if(szString[0] != 0 && szString[1] == 0) + return ERROR_INVALID_PARAMETER; + } + + // Give the length + if(PtrBinary != NULL) + PtrBinary[0] = pbBinary - pbSaveBinary; + return ERROR_SUCCESS; +} + +char * StringCopy(char * szTarget, size_t cchTarget, const char * szSource); +void StringCat(char * szTarget, size_t cchTargetMax, const char * szSource); +void StringCreatePseudoFileName(char * szBuffer, size_t cchMaxChars, unsigned int nIndex, const char * szExtension); + +#ifdef _UNICODE +void StringCopy(TCHAR * szTarget, size_t cchTarget, const char * szSource); +void StringCopy(char * szTarget, size_t cchTarget, const TCHAR * szSource); +void StringCopy(TCHAR * szTarget, size_t cchTarget, const TCHAR * szSource); +void StringCat(TCHAR * szTarget, size_t cchTargetMax, const TCHAR * szSource); +void StringCat(TCHAR * szTarget, size_t cchTargetMax, const char * szSource); +#endif + +//----------------------------------------------------------------------------- +// UTF-8 support + +DWORD UTF8_DecodeCodePoint(const BYTE * pbString, const BYTE * pbStringEnd, DWORD & dwCodePoint, size_t & ccBytesEaten, DWORD dwFlags = 0); +size_t UTF8_EncodeCodePoint(DWORD dwCodePoint, LPBYTE Utf8Buffer); //----------------------------------------------------------------------------- // Encryption and decryption functions @@ -118,157 +289,245 @@ extern LCID lcFileLocale; // Preferred file locale #define MPQ_HASH_NAME_A 0x100 #define MPQ_HASH_NAME_B 0x200 #define MPQ_HASH_FILE_KEY 0x300 +#define MPQ_HASH_KEY2_MIX 0x400 DWORD HashString(const char * szFileName, DWORD dwHashType); +DWORD HashStringSlash(const char * szFileName, DWORD dwHashType); +DWORD HashStringLower(const char * szFileName, DWORD dwHashType); void InitializeMpqCryptography(); -DWORD GetHashTableSizeForFileCount(DWORD dwFileCount); +DWORD GetNearestPowerOfTwo(DWORD dwFileCount); bool IsPseudoFileName(const char * szFileName, LPDWORD pdwFileIndex); ULONGLONG HashStringJenkins(const char * szFileName); -int ConvertMpqHeaderToFormat4(TMPQArchive * ha, ULONGLONG FileSize, DWORD dwFlags); +DWORD GetDefaultSpecialFileFlags(DWORD dwFileSize, USHORT wFormatVersion); -DWORD GetDefaultSpecialFileFlags(TMPQArchive * ha, DWORD dwFileSize); +void EncryptMpqBlock(void * pvDataBlock, DWORD dwLength, DWORD dwKey); +void DecryptMpqBlock(void * pvDataBlock, DWORD dwLength, DWORD dwKey); -void EncryptMpqBlock(void * pvFileBlock, DWORD dwLength, DWORD dwKey); -void DecryptMpqBlock(void * pvFileBlock, DWORD dwLength, DWORD dwKey); - -DWORD DetectFileKeyBySectorSize(LPDWORD SectorOffsets, DWORD decrypted); -DWORD DetectFileKeyByContent(void * pvFileContent, DWORD dwFileSize); +DWORD DetectFileKeyBySectorSize(LPDWORD EncryptedData, DWORD dwSectorSize, DWORD dwSectorOffsLen); +DWORD DetectFileKeyByContent(void * pvEncryptedData, DWORD dwSectorSize, DWORD dwFileSize); DWORD DecryptFileKey(const char * szFileName, ULONGLONG MpqPos, DWORD dwFileSize, DWORD dwFlags); bool IsValidMD5(LPBYTE pbMd5); +bool IsValidSignature(LPBYTE pbSignature); bool VerifyDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE expected_md5); void CalculateDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE md5_hash); //----------------------------------------------------------------------------- // Handle validation functions -bool IsValidMpqHandle(TMPQArchive * ha); -bool IsValidFileHandle(TMPQFile * hf); +TMPQArchive * IsValidMpqHandle(HANDLE hMpq); +TMPQFile * IsValidFileHandle(HANDLE hFile); //----------------------------------------------------------------------------- -// Hash table and block table manipulation +// Support for MPQ file tables +ULONGLONG GetFileOffsetMask(TMPQArchive * ha); +ULONGLONG FileOffsetFromMpqOffset(TMPQArchive * ha, ULONGLONG MpqOffset); +ULONGLONG CalculateRawSectorOffset(TMPQFile * hf, DWORD dwSectorOffset); + +DWORD ConvertMpqHeaderToFormat4(TMPQArchive * ha, ULONGLONG MpqOffset, ULONGLONG FileSize, DWORD dwFlags, MTYPE MapType); + +bool IsValidHashEntry(TMPQArchive * ha, TMPQHash * pHash); + +TMPQHash * FindFreeHashEntry(TMPQArchive * ha, DWORD dwStartIndex, DWORD dwHashCheck1, DWORD dwHashCheck2, LCID lcFileLocale); TMPQHash * GetFirstHashEntry(TMPQArchive * ha, const char * szFileName); TMPQHash * GetNextHashEntry(TMPQArchive * ha, TMPQHash * pFirstHash, TMPQHash * pPrevHash); -DWORD AllocateHashEntry(TMPQArchive * ha, TFileEntry * pFileEntry); -DWORD AllocateHetEntry(TMPQArchive * ha, TFileEntry * pFileEntry); +TMPQHash * AllocateHashEntry(TMPQArchive * ha, TFileEntry * pFileEntry, LCID lcFileLocale); -void FindFreeMpqSpace(TMPQArchive * ha, ULONGLONG * pFreeSpacePos); +TMPQExtHeader * LoadExtTable(TMPQArchive * ha, ULONGLONG ByteOffset, size_t Size, DWORD dwSignature, DWORD dwKey); +TMPQHetTable * LoadHetTable(TMPQArchive * ha); +TMPQBetTable * LoadBetTable(TMPQArchive * ha); -// Functions that loads and verifies MPQ data bitmap -int LoadMpqDataBitmap(TMPQArchive * ha, ULONGLONG FileSize, bool * pbFileIsComplete); +TMPQBlock * LoadBlockTable(TMPQArchive * ha, bool bDontFixEntries = false); +TMPQBlock * TranslateBlockTable(TMPQArchive * ha, ULONGLONG * pcbTableSize, bool * pbNeedHiBlockTable); + +ULONGLONG FindFreeMpqSpace(TMPQArchive * ha); // Functions that load the HET and BET tables -int CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize); -int LoadAnyHashTable(TMPQArchive * ha); -int BuildFileTable(TMPQArchive * ha, ULONGLONG FileSize); -int SaveMPQTables(TMPQArchive * ha); +DWORD CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize); +DWORD LoadAnyHashTable(TMPQArchive * ha); +DWORD BuildFileTable(TMPQArchive * ha); +DWORD DefragmentFileTable(TMPQArchive * ha); -TMPQHetTable * CreateHetTable(DWORD dwMaxFileCount, DWORD dwHashBitSize, bool bCreateEmpty); +DWORD CreateFileTable(TMPQArchive * ha, DWORD dwFileTableSize); +DWORD RebuildHetTable(TMPQArchive * ha); +DWORD RebuildFileTable(TMPQArchive * ha, DWORD dwNewHashTableSize); +DWORD SaveMPQTables(TMPQArchive * ha); + +TMPQHetTable * CreateHetTable(DWORD dwEntryCount, DWORD dwTotalCount, DWORD dwHashBitSize, LPBYTE pbSrcData); void FreeHetTable(TMPQHetTable * pHetTable); TMPQBetTable * CreateBetTable(DWORD dwMaxFileCount); void FreeBetTable(TMPQBetTable * pBetTable); // Functions for finding files in the file table -TFileEntry * GetFileEntryAny(TMPQArchive * ha, const char * szFileName); -TFileEntry * GetFileEntryLocale(TMPQArchive * ha, const char * szFileName, LCID lcLocale); -TFileEntry * GetFileEntryExact(TMPQArchive * ha, const char * szFileName, LCID lcLocale); -TFileEntry * GetFileEntryByIndex(TMPQArchive * ha, DWORD dwIndex); +TFileEntry * GetFileEntryLocale(TMPQArchive * ha, const char * szFileName, LCID lcFileLocale, LPDWORD PtrHashIndex = NULL); +TFileEntry * GetFileEntryExact(TMPQArchive * ha, const char * szFileName, LCID lcFileLocale, LPDWORD PtrHashIndex = NULL); // Allocates file name in the file entry -void AllocateFileName(TFileEntry * pFileEntry, const char * szFileName); +void AllocateFileName(TMPQArchive * ha, 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); -int RenameFileEntry(TMPQArchive * ha, TFileEntry * pFileEntry, const char * szNewFileName); -void ClearFileEntry(TMPQArchive * ha, TFileEntry * pFileEntry); -int FreeFileEntry(TMPQArchive * ha, TFileEntry * pFileEntry); +TFileEntry * AllocateFileEntry(TMPQArchive * ha, const char * szFileName, LCID lcFileLocale, LPDWORD PtrHashIndex); +DWORD RenameFileEntry(TMPQArchive * ha, TMPQFile * hf, const char * szNewFileName); +DWORD DeleteFileEntry(TMPQArchive * ha, TMPQFile * hf); // Invalidates entries for (listfile) and (attributes) void InvalidateInternalFiles(TMPQArchive * ha); +// Retrieves information about the strong signature +bool QueryMpqSignatureInfo(TMPQArchive * ha, PMPQ_SIGNATURE_INFO pSignatureInfo); + +//----------------------------------------------------------------------------- +// Support for alternate file formats (SBaseSubTypes.cpp) + +DWORD ConvertSqpHeaderToFormat4(TMPQArchive * ha, ULONGLONG FileSize, DWORD dwFlags); +TMPQHash * LoadSqpHashTable(TMPQArchive * ha); +TMPQBlock * LoadSqpBlockTable(TMPQArchive * ha); + +DWORD ConvertMpkHeaderToFormat4(TMPQArchive * ha, ULONGLONG FileSize, DWORD dwFlags); +void DecryptMpkTable(void * pvMpkTable, size_t cbSize); +TMPQHash * LoadMpkHashTable(TMPQArchive * ha); +TMPQBlock * LoadMpkBlockTable(TMPQArchive * ha); +int SCompDecompressMpk(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer); + //----------------------------------------------------------------------------- // Common functions - MPQ File -TMPQFile * CreateMpqFile(TMPQArchive * ha); -int LoadMpqTable(TMPQArchive * ha, ULONGLONG ByteOffset, void * pvTable, DWORD dwCompressedSize, DWORD dwRealSize, DWORD dwKey); -int AllocateSectorBuffer(TMPQFile * hf); -int AllocatePatchInfo(TMPQFile * hf, bool bLoadFromFile); -int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile); -int AllocateSectorChecksums(TMPQFile * hf, bool bLoadFromFile); -void CalculateRawSectorOffset(ULONGLONG & RawFilePos, TMPQFile * hf, DWORD dwSectorOffset); -int WritePatchInfo(TMPQFile * hf); -int WriteSectorOffsets(TMPQFile * hf); -int WriteSectorChecksums(TMPQFile * hf); -int WriteMemDataMD5(TFileStream * pStream, ULONGLONG RawDataOffs, void * pvRawData, DWORD dwRawDataSize, DWORD dwChunkSize, LPDWORD pcbTotalSize); -int WriteMpqDataMD5(TFileStream * pStream, ULONGLONG RawDataOffs, DWORD dwRawDataSize, DWORD dwChunkSize); -void FreeMPQFile(TMPQFile *& hf); +TMPQFile * CreateFileHandle(TMPQArchive * ha, TFileEntry * pFileEntry); +TMPQFile * CreateWritableHandle(TMPQArchive * ha, DWORD dwFileSize); +void * LoadMpqTable(TMPQArchive * ha, ULONGLONG ByteOffset, LPBYTE pbTableHash, DWORD dwCompressedSize, DWORD dwRealSize, DWORD dwKey, DWORD * PtrRealTableSize); +DWORD AllocateSectorBuffer(TMPQFile * hf); +DWORD AllocatePatchInfo(TMPQFile * hf, bool bLoadFromFile); +DWORD AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile); +DWORD AllocateSectorChecksums(TMPQFile * hf, bool bLoadFromFile); +DWORD WritePatchInfo(TMPQFile * hf); +DWORD WriteSectorOffsets(TMPQFile * hf); +DWORD WriteSectorChecksums(TMPQFile * hf); +DWORD WriteMemDataMD5(TFileStream * pStream, ULONGLONG RawDataOffs, void * pvRawData, DWORD dwRawDataSize, DWORD dwChunkSize, LPDWORD pcbTotalSize); +DWORD WriteMpqDataMD5(TFileStream * pStream, ULONGLONG RawDataOffs, DWORD dwRawDataSize, DWORD dwChunkSize); + +bool DereferenceArchiveFiles(TMPQArchive * ha); +bool DereferenceArchive(TMPQArchive * ha); +void FreeFileHandle(TMPQFile *& hf); + +//----------------------------------------------------------------------------- +// Patch functions + +// Structure used for the patching process +typedef struct _TMPQPatcher +{ + BYTE this_md5[MD5_DIGEST_SIZE]; // MD5 of the current file state + LPBYTE pbFileData1; // Primary working buffer + LPBYTE pbFileData2; // Secondary working buffer + DWORD cbMaxFileData; // Maximum allowed size of the patch data + DWORD cbFileData; // Current size of the result data + DWORD nCounter; // Counter of the patch process + +} TMPQPatcher; bool IsIncrementalPatchFile(const void * pvData, DWORD cbData, LPDWORD pdwPatchedFileSize); -int PatchFileData(TMPQFile * hf); - -void FreeMPQArchive(TMPQArchive *& ha); +DWORD Patch_InitPatcher(TMPQPatcher * pPatcher, TMPQFile * hf); +DWORD Patch_Process(TMPQPatcher * pPatcher, TMPQFile * hf); +void Patch_Finalize(TMPQPatcher * pPatcher); //----------------------------------------------------------------------------- // Utility functions -bool CheckWildCard(const char * szString, const char * szWildCard); -const char * GetPlainFileNameA(const char * szFileName); -const TCHAR * GetPlainFileNameT(const TCHAR * szFileName); bool IsInternalMpqFileName(const char * szFileName); -//----------------------------------------------------------------------------- -// Support for adding files to the MPQ +template +const XCHAR * GetPlainFileName(const XCHAR * szFileName) +{ + const XCHAR * szPlainName = szFileName; -int SFileAddFile_Init( + while(*szFileName != 0) + { + if(*szFileName == '\\' || *szFileName == '/') + szPlainName = szFileName + 1; + szFileName++; + } + + return szPlainName; +} + +//----------------------------------------------------------------------------- +// Internal support for MPQ modifications + +DWORD SFileAddFile_Init( TMPQArchive * ha, const char * szArchivedName, ULONGLONG ft, DWORD dwFileSize, - LCID lcLocale, + LCID lcFileLocale, DWORD dwFlags, TMPQFile ** phf ); -int SFileAddFile_Write( +DWORD SFileAddFile_Init( + TMPQArchive * ha, + TMPQFile * hfSrc, + TMPQFile ** phf + ); + +DWORD SFileAddFile_Write( TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD dwCompression ); -int SFileAddFile_Finish( +DWORD SFileAddFile_Finish( TMPQFile * hf ); //----------------------------------------------------------------------------- // Attributes support -int SAttrLoadAttributes(TMPQArchive * ha); -int SAttrFileSaveToMpq(TMPQArchive * ha); +DWORD SAttrLoadAttributes(TMPQArchive * ha); +DWORD SAttrFileSaveToMpq(TMPQArchive * ha); //----------------------------------------------------------------------------- // Listfile functions -int SListFileSaveToMpq(TMPQArchive * ha); +DWORD SListFileSaveToMpq(TMPQArchive * ha); + +//----------------------------------------------------------------------------- +// Weak signature support + +DWORD SSignFileCreate(TMPQArchive * ha); +DWORD SSignFileFinish(TMPQArchive * ha); //----------------------------------------------------------------------------- // Dump data support #ifdef __STORMLIB_DUMP_DATA__ + void DumpMpqHeader(TMPQHeader * pHeader); +void DumpHashTable(TMPQHash * pHashTable, DWORD dwHashTableSize); void DumpHetAndBetTable(TMPQHetTable * pHetTable, TMPQBetTable * pBetTable); +void DumpFileTable(TFileEntry * pFileTable, DWORD dwFileTableSize); #else -#define DumpMpqHeader(h) /* */ -#define DumpHetAndBetTable(h, b) /* */ + +#define DumpMpqHeader(h) /* */ +#define DumpHashTable(t, s) /* */ +#define DumpHetAndBetTable(t, s) /* */ +#define DumpFileTable(t, s) /* */ + #endif +/* +#ifdef _DEBUG +void SFileLog(const char * format, ...); +#else +inline void SFileLog(...) {} +#endif +*/ + #endif // __STORMCOMMON_H__ diff --git a/dep/StormLib/stormlib_dll/StormLib.exp b/dep/StormLib/src/StormLib.exp similarity index 97% rename from dep/StormLib/stormlib_dll/StormLib.exp rename to dep/StormLib/src/StormLib.exp index aa18edb7aa..188958ea01 100644 --- a/dep/StormLib/stormlib_dll/StormLib.exp +++ b/dep/StormLib/src/StormLib.exp @@ -9,11 +9,11 @@ _SFileGetLocale _SFileOpenArchive _SFileCreateArchive -_SFileGetArchiveBitmap _SFileFlushArchive _SFileCloseArchive _SFileAddListFile +_SFileAddListFileEntries _SFileSetCompactCallback _SFileCompactArchive diff --git a/dep/StormLib/src/StormLib.h b/dep/StormLib/src/StormLib.h index a07ae46ca4..6ca51c33d0 100644 --- a/dep/StormLib/src/StormLib.h +++ b/dep/StormLib/src/StormLib.h @@ -1,7 +1,7 @@ /*****************************************************************************/ -/* StormLib.h Copyright (c) Ladislav Zezula 1999-2010 */ +/* StormLib.h Copyright (c) Ladislav Zezula 1999-2025 */ /*---------------------------------------------------------------------------*/ -/* StormLib library v 7.02 */ +/* StormLib library v 9.31 */ /* */ /* Author : Ladislav Zezula */ /* E-mail : ladik@zezula.net */ @@ -25,7 +25,7 @@ /* hash table */ /* 08.12.03 4.11 DCH Fixed bug in reading file sector larger than 0x1000 */ /* on certain files. */ -/* Fixed bug in AddFile with MPQ_FILE_REPLACE_EXISTING */ +/* Fixed bug in AddFile with MPQ_FILE_REPLACEEXISTING */ /* (Thanx Daniel Chiamarello, dchiamarello@madvawes.com)*/ /* 21.12.03 4.50 Lad Completed port for Mac */ /* Fixed bug in compacting (if fsize is mul of 0x1000) */ @@ -37,7 +37,7 @@ /* Correctly works if HashTableSize > BlockTableSize */ /* 29.12.04 4.70 Lad Fixed compatibility problem with MPQs from WoW */ /* 14.07.05 5.00 Lad Added the BZLIB compression support */ -/* Added suport of files stored as single unit */ +/* Added support of files stored as single unit */ /* 17.04.06 5.01 Lad Converted to MS Visual Studio 8.0 */ /* Fixed issue with protected Warcraft 3 protected maps */ /* 15.05.06 5.02 Lad Fixed issue with WoW 1.10+ */ @@ -65,15 +65,25 @@ /* 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 */ +/* 14.01.13 8.21 Lad ADPCM and Huffmann (de)compression refactored */ +/* 04.12.13 9.00 Lad Unit tests, bug fixes */ +/* 27.08.14 9.10 Lad Signing archives with weak digital signature */ +/* 25.11.14 9.11 Lad Fixed bug reading & creating HET table */ +/* 18.09.15 9.20 Lad Release 9.20 */ +/* 12.12.16 9.21 Lad Release 9.21 */ +/* 10.11.17 9.22 Lad Release 9.22 */ +/* 28.09.22 9.24 Lad lcLocale -> lcFileLocale, also contains platform */ +/* 01.11.24 9.30 Lad Added conversion from UTF-8 to file name and back */ /*****************************************************************************/ #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 +#endif #include "StormPort.h" @@ -82,44 +92,49 @@ extern "C" { #endif //----------------------------------------------------------------------------- -// Use the apropriate library +// Use the appropriate 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 // Z - S for static-linked CRT library, D for multithreaded DLL CRT library // -#if 0 && 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 +#if defined(__STORMLIB_SELF__) && !defined(__STORMLIB_NO_STATIC_LINK__) +#define __STORMLIB_NO_STATIC_LINK__ // Define this if you don't want to link using pragmas when using msvc +#endif + +#if defined(_MSC_VER) && !defined(__STORMLIB_NO_STATIC_LINK__) + #ifndef WDK_BUILD + #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 - #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 + #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 @@ -129,30 +144,37 @@ extern "C" { //----------------------------------------------------------------------------- // Defines +#define STORMLIB_VERSION 0x091F // Current version of StormLib +#define STORMLIB_VERSION_STRING "9.31" // Current version of StormLib as string + #define ID_MPQ 0x1A51504D // MPQ archive header ID ('MPQ\x1A') #define ID_MPQ_USERDATA 0x1B51504D // MPQ userdata entry ('MPQ\x1B') +#define ID_MPK 0x1A4B504D // MPK archive header ID ('MPK\x1A') -#define ERROR_AVI_FILE 10000 // No MPQ file, but AVI file. +#define ID_MPK_VERSION_2000 0x30303032 // MPK version ("2000") + +#define ERROR_AVI_FILE 10000 // Not a MPQ file, but an 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 +#define ERROR_FILE_INCOMPLETE 10006 // The required file part is missing +#define ERROR_UNKNOWN_FILE_NAMES 10007 // A name of at least one file is unknown +#define ERROR_CANT_FIND_PATCH_PREFIX 10008 // StormLib was unable to find patch prefix for the patches +#define ERROR_FAKE_MPQ_HEADER 10009 // The header at this position is fake header +#define ERROR_FILE_DELETED 10010 // The file is present but contains delete marker // Values for SFileCreateArchive -#define HASH_TABLE_SIZE_MIN 0x00000004 // Minimum acceptable hash table size +#define HASH_TABLE_SIZE_MIN 0x00000004 // Verified: If there is 1 file, hash table size is 4 #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 HET_ENTRY_DELETED 0x80 // HET hash value for a deleted entry -#define HET_ENTRY_FREE 0x00 // HET hash value for free entry - -#define HASH_STATE_SIZE 0x60 // Size of LibTomCrypt's hash_state structure - -#define MPQ_PATCH_PREFIX_LEN 0x20 // Maximum length of the patch prefix +#define HET_ENTRY_DELETED 0x80 // NameHash1 value for a deleted entry +#define HET_ENTRY_FREE 0x00 // NameHash1 value for free entry // Values for SFileOpenArchive #define SFILE_OPEN_HARD_DISK_FILE 2 // Open the archive on HDD @@ -160,54 +182,103 @@ extern "C" { // 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_CHECK_EXISTS 0xFFFFFFFC // Only check whether the file exists +#define SFILE_OPEN_BASE_FILE 0xFFFFFFFD // Reserved for StormLib internal use #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 +// Flags for TMPQArchive::dwFlags. Used internally #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 +#define MPQ_FLAG_MALFORMED 0x00000004 // Malformed data structure detected (W3M map protectors) +#define MPQ_FLAG_HASH_TABLE_CUT 0x00000008 // The hash table goes beyond EOF +#define MPQ_FLAG_BLOCK_TABLE_CUT 0x00000010 // The hash table goes beyond EOF +#define MPQ_FLAG_CHECK_SECTOR_CRC 0x00000020 // Checking sector CRC when reading files +#define MPQ_FLAG_SAVING_TABLES 0x00000040 // If set, we are saving MPQ internal files and MPQ tables +#define MPQ_FLAG_PATCH 0x00000080 // If set, this MPQ is a patch archive +#define MPQ_FLAG_STARCRAFT_BETA 0x00000100 // If set, this MPQ is from Starcraft I BETA +#define MPQ_FLAG_STARCRAFT 0x00000200 // If set, this MPQ is from Starcraft I +#define MPQ_FLAG_WAR3_MAP 0x00000400 // If set, this MPQ is a Warcraft III map +#define MPQ_FLAG_LISTFILE_NONE 0x00000800 // Set when no (listfile) was found in InvalidateInternalFiles +#define MPQ_FLAG_LISTFILE_NEW 0x00001000 // Set when (listfile) invalidated by InvalidateInternalFiles +#define MPQ_FLAG_LISTFILE_FORCE 0x00002000 // Save updated listfile on exit +#define MPQ_FLAG_ATTRIBUTES_NONE 0x00004000 // Set when no (attributes) was found in InvalidateInternalFiles +#define MPQ_FLAG_ATTRIBUTES_NEW 0x00008000 // Set when (attributes) invalidated by InvalidateInternalFiles +#define MPQ_FLAG_SIGNATURE_NONE 0x00010000 // Set when no (signature) was found in InvalidateInternalFiles +#define MPQ_FLAG_SIGNATURE_NEW 0x00020000 // Set when (signature) invalidated by InvalidateInternalFiles + +// Values for TMPQArchive::dwSubType +#define MPQ_SUBTYPE_MPQ 0x00000000 // The file is a MPQ file (Blizzard games) +#define MPQ_SUBTYPE_SQP 0x00000001 // The file is a SQP file (War of the Immortals) +#define MPQ_SUBTYPE_MPK 0x00000002 // The file is a MPK file (Longwu Online) // Return value for SFileGetFileSize and SFileSetFilePointer #define SFILE_INVALID_SIZE 0xFFFFFFFF #define SFILE_INVALID_POS 0xFFFFFFFF #define SFILE_INVALID_ATTRIBUTES 0xFFFFFFFF -// Flags for SFileAddFile +// Flags for TMPQBlock::dwFlags #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_ENCRYPTED 0x00010000 // Indicates an encrypted file +#define MPQ_FILE_KEY_V2 0x00020000 // Indicates an encrypted file with key v2 #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_SIGNATURE 0x10000000 // Present on STANDARD.SNP\(signature). The only occurrence ever observed #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_COMPRESS_MASK 0x0000FF00 // Mask for a file being compressed + +#define MPQ_FILE_DEFAULT_INTERNAL 0xFFFFFFFF // Use default flags for internal files + +#define MPQ_FILE_FIX_KEY 0x00020000 // Obsolete, do not use + #define MPQ_FILE_VALID_FLAGS (MPQ_FILE_IMPLODE | \ MPQ_FILE_COMPRESS | \ MPQ_FILE_ENCRYPTED | \ - MPQ_FILE_FIX_KEY | \ + MPQ_FILE_KEY_V2 | \ MPQ_FILE_PATCH_FILE | \ MPQ_FILE_SINGLE_UNIT | \ MPQ_FILE_DELETE_MARKER | \ MPQ_FILE_SECTOR_CRC | \ + MPQ_FILE_SIGNATURE | \ MPQ_FILE_EXISTS) +#define MPQ_FILE_VALID_FLAGS_W3X (MPQ_FILE_IMPLODE | \ + MPQ_FILE_COMPRESS | \ + MPQ_FILE_ENCRYPTED | \ + MPQ_FILE_KEY_V2 | \ + MPQ_FILE_DELETE_MARKER | \ + MPQ_FILE_SECTOR_CRC | \ + MPQ_FILE_SIGNATURE | \ + MPQ_FILE_EXISTS) + +#define MPQ_FILE_VALID_FLAGS_SCX (MPQ_FILE_IMPLODE | \ + MPQ_FILE_COMPRESS | \ + MPQ_FILE_ENCRYPTED | \ + MPQ_FILE_KEY_V2 | \ + MPQ_FILE_EXISTS) + +// Flags for TPatchInfo::dwFlags +#define MPQ_PATCH_INFO_VALID 0x80000000 // Set if the patch info is valid + +// We need to mask out the upper 4 bits of the block table index. +// This is because it gets shifted out when calculating block table offset +// BlockTableOffset = pHash->dwBlockIndex << 0x04 +// Malformed MPQ maps may contain block indexes like 0x40000001 or 0xF0000023 +#define BLOCK_INDEX_MASK 0x0FFFFFFF +#define MPQ_BLOCK_INDEX(pHash) ((pHash)->dwBlockIndex & BLOCK_INDEX_MASK) + // 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_SPARSE 0x20 // Run-length (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. @@ -221,51 +292,19 @@ extern "C" { // Signatures for HET and BET table #define HET_TABLE_SIGNATURE 0x1A544548 // 'HET\x1a' #define BET_TABLE_SIGNATURE 0x1A544542 // 'BET\x1a' +#define BET_TABLE_MAX_SIZE 0x00100000 // Maximum acceptable size of HET&BET tables // 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) -// 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_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 0x080A // Current version of StormLib (8.10) -#define STORMLIB_VERSION_STRING "8.10" - #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_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 @@ -284,27 +323,33 @@ extern "C" { #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_FLAT 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_MPQE 0x00000020 // Stream is an encrypted MPQ +#define STREAM_PROVIDER_BLOCK4 0x00000030 // 0x4000 per block, text MD5 after each block, max 0x2000 blocks per file #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 STREAM_FLAG_USE_BITMAP 0x00000400 // If the file has a file bitmap, load it and use it +#define STREAM_OPTIONS_MASK 0x0000FF00 // Mask for stream options + +#define STREAM_PROVIDERS_MASK 0x000000FF // Mask to get stream providers +#define STREAM_FLAGS_MASK 0x0000FFFF // Mask for all stream flags (providers+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_NO_HEADER_SEARCH 0x00040000 // Don't search for the MPQ header past the begin of the file +#define MPQ_OPEN_FORCE_MPQ_V1 0x00080000 // Always open the archive as MPQ v 1.00, ignore the "wFormatVersion" variable in the header +#define MPQ_OPEN_CHECK_SECTOR_CRC 0x00100000 // On files with MPQ_FILE_SECTOR_CRC, the CRC will be checked when reading file +#define MPQ_OPEN_PATCH 0x00200000 // This archive is a patch MPQ. Used internally. +#define MPQ_OPEN_FORCE_LISTFILE 0x00400000 // Force add listfile even if there is none at the moment of opening #define MPQ_OPEN_READ_ONLY STREAM_FLAG_READ_ONLY -#define MPQ_OPEN_ENCRYPTED STREAM_PROVIDER_ENCRYPTED // Flags for SFileCreateArchive -#define MPQ_CREATE_ATTRIBUTES 0x00100000 // Also add the (attributes) file +#define MPQ_CREATE_LISTFILE 0x00100000 // Also add the (listfile) file +#define MPQ_CREATE_ATTRIBUTES 0x00200000 // Also add the (attributes) file +#define MPQ_CREATE_SIGNATURE 0x00400000 // Also add the (signature) 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 @@ -342,6 +387,11 @@ extern "C" { #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 +// Signature types +#define SIGNATURE_TYPE_NONE 0x0000 // The archive has no signature in it +#define SIGNATURE_TYPE_WEAK 0x0001 // The archive has weak signature +#define SIGNATURE_TYPE_STRONG 0x0002 // The archive has strong signature + // 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) @@ -349,7 +399,7 @@ extern "C" { #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 #endif @@ -362,45 +412,94 @@ extern "C" { #define LANG_NEUTRAL 0x00 // Neutral locale #endif +// Pointer to hashing function +typedef DWORD (*HASH_STRING)(const char * szFileName, DWORD dwHashType); + +//----------------------------------------------------------------------------- +// File information classes for SFileGetFileInfo and SFileFreeFileInfo + +typedef enum _SFileInfoClass +{ + // Info classes for archives + SFileMpqFileName, // Name of the archive file (TCHAR []) + SFileMpqStreamBitmap, // Array of bits, each bit means availability of one block (BYTE []) + SFileMpqUserDataOffset, // Offset of the user data header (ULONGLONG) + SFileMpqUserDataHeader, // Raw (unfixed) user data header (TMPQUserData) + SFileMpqUserData, // MPQ User data, without the header (BYTE []) + SFileMpqHeaderOffset, // Offset of the MPQ header (ULONGLONG) + SFileMpqHeaderSize, // Fixed size of the MPQ header + SFileMpqHeader, // Raw (unfixed) archive header (TMPQHeader) + SFileMpqHetTableOffset, // Offset of the HET table, relative to MPQ header (ULONGLONG) + SFileMpqHetTableSize, // Compressed size of the HET table (ULONGLONG) + SFileMpqHetHeader, // HET table header (TMPQHetHeader) + SFileMpqHetTable, // HET table as pointer. Must be freed using SFileFreeFileInfo + SFileMpqBetTableOffset, // Offset of the BET table, relative to MPQ header (ULONGLONG) + SFileMpqBetTableSize, // Compressed size of the BET table (ULONGLONG) + SFileMpqBetHeader, // BET table header, followed by the flags (TMPQBetHeader + DWORD[]) + SFileMpqBetTable, // BET table as pointer. Must be freed using SFileFreeFileInfo + SFileMpqHashTableOffset, // Hash table offset, relative to MPQ header (ULONGLONG) + SFileMpqHashTableSize64, // Compressed size of the hash table (ULONGLONG) + SFileMpqHashTableSize, // Size of the hash table, in entries (DWORD) + SFileMpqHashTable, // Raw (unfixed) hash table (TMPQBlock []) + SFileMpqBlockTableOffset, // Block table offset, relative to MPQ header (ULONGLONG) + SFileMpqBlockTableSize64, // Compressed size of the block table (ULONGLONG) + SFileMpqBlockTableSize, // Size of the block table, in entries (DWORD) + SFileMpqBlockTable, // Raw (unfixed) block table (TMPQBlock []) + SFileMpqHiBlockTableOffset, // Hi-block table offset, relative to MPQ header (ULONGLONG) + SFileMpqHiBlockTableSize64, // Compressed size of the hi-block table (ULONGLONG) + SFileMpqHiBlockTable, // The hi-block table (USHORT []) + SFileMpqSignatures, // Signatures present in the MPQ (DWORD) + SFileMpqStrongSignatureOffset, // Byte offset of the strong signature, relative to begin of the file (ULONGLONG) + SFileMpqStrongSignatureSize, // Size of the strong signature (DWORD) + SFileMpqStrongSignature, // The strong signature (BYTE []) + SFileMpqArchiveSize64, // Archive size from the header (ULONGLONG) + SFileMpqArchiveSize, // Archive size from the header (DWORD) + SFileMpqMaxFileCount, // Max number of files in the archive (DWORD) + SFileMpqFileTableSize, // Number of entries in the file table (DWORD) + SFileMpqSectorSize, // Sector size (DWORD) + SFileMpqNumberOfFiles, // Number of files (DWORD) + SFileMpqRawChunkSize, // Size of the raw data chunk for MD5 + SFileMpqStreamFlags, // Stream flags (DWORD) + SFileMpqFlags, // Nonzero if the MPQ is read only (DWORD) + + // Info classes for files + SFileInfoPatchChain, // Chain of patches where the file is (TCHAR []) + SFileInfoFileEntry, // The file entry for the file (TFileEntry) + SFileInfoHashEntry, // Hash table entry for the file (TMPQHash) + SFileInfoHashIndex, // Index of the hash table entry (DWORD) + SFileInfoNameHash1, // The first name hash in the hash table (DWORD) + SFileInfoNameHash2, // The second name hash in the hash table (DWORD) + SFileInfoNameHash3, // 64-bit file name hash for the HET/BET tables (ULONGLONG) + SFileInfoLocale, // File locale (DWORD) + SFileInfoFileIndex, // Block index (DWORD) + SFileInfoByteOffset, // File position in the archive (ULONGLONG) + SFileInfoFileTime, // File time (ULONGLONG) + SFileInfoFileSize, // Size of the file (DWORD) + SFileInfoCompressedSize, // Compressed file size (DWORD) + SFileInfoFlags, // File flags from (DWORD) + SFileInfoEncryptionKey, // File encryption key + SFileInfoEncryptionKeyRaw, // Unfixed value of the file key + SFileInfoCRC32, // CRC32 of the file + + SFileInfoInvalid = 0xFFF, // Invalid file info class +} SFileInfoClass; + //----------------------------------------------------------------------------- // 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_DOWNLOAD_CALLBACK)(void * pvUserData, ULONGLONG ByteOffset, DWORD dwTotalBytes); 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); -struct TFileStream; - -//----------------------------------------------------------------------------- -// Structure for bit arrays used for HET and BET tables - -struct TBitArray -{ - void GetBits(unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, int nResultSize); - void SetBits(unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, int nResultSize); - - DWORD NumberOfBits; // Total number of bits that are available - BYTE Elements[1]; // Array of elements (variable length) -}; - -// Structure for file bitmap. Used by SFileGetArchiveBitmap -struct TFileBitmap -{ - 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 - - // Followed by file bitmap (variable length), array of BYTEs) -}; +typedef struct TFileStream TFileStream; +typedef struct TMPQBits TMPQBits; //----------------------------------------------------------------------------- // Structures related to MPQ format @@ -416,8 +515,9 @@ struct TFileBitmap #define MPQ_HEADER_SIZE_V2 0x2C #define MPQ_HEADER_SIZE_V3 0x44 #define MPQ_HEADER_SIZE_V4 0xD0 +#define MPQ_HEADER_DWORDS (MPQ_HEADER_SIZE_V4 / 0x04) -struct TMPQUserData +typedef struct _TMPQUserData { // The ID_MPQ_USERDATA ('MPQ\x1B') signature DWORD dwID; @@ -430,7 +530,7 @@ struct TMPQUserData // Appears to be size of user data header (Starcraft II maps) DWORD cbUserDataHeader; -}; +} TMPQUserData; // MPQ file header // @@ -438,13 +538,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 @@ -464,14 +564,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; @@ -516,7 +616,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 @@ -525,34 +625,34 @@ 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 +// Hash table entry. All files in the archive are searched by their hashes. +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; + DWORD dwHashCheck1; -#ifdef PLATFORM_LITTLE_ENDIAN + // The hash of the file path, using method B. + DWORD dwHashCheck2; + +#ifdef STORMLIB_LITTLE_ENDIAN // The language of the file. This is a Windows LANGID data type, and uses the same values. // 0 indicates the default language (American English), or that the file is language-neutral. - USHORT lcLocale; + USHORT Locale; // The platform the file is used for. 0 indicates the default platform. // No other values have been observed. - // Note: wPlatform is actually just BYTE, but since it has never been used, we don't care. - USHORT wPlatform; + BYTE Platform; + BYTE Flags; #else - USHORT wPlatform; - USHORT lcLocale; + BYTE Flags; + BYTE Platform; + USHORT Locale; #endif @@ -563,271 +663,337 @@ 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 +// File description block contains information about the file +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 = valid (?) + 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) -}; - -// Header for PTCH files -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 - BYTE md5_before_patch[0x10]; // MD5 of the original (unpached) file - BYTE md5_after_patch[0x10]; // MD5 of the patched file - - //-- XFRM block ------------------------------------- - DWORD dwXFRM; // 'XFRM' - DWORD dwXfrmBlockSize; // Size of the XFRM block, includes XFRM header and patch data - DWORD dwPatchType; // Type of patch ('BSD0' or 'COPY') - - // Followed by the patch data -}; - -#define SIZE_OF_XFRM_HEADER 0x0C +} TPatchInfo; // 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. - ULONGLONG BetHash; // Lower part of the file name hash. Only used when the MPQ has BET table. - DWORD dwHashIndex; // Index to the hash table. Only used when the MPQ has classic hash table - DWORD dwHetIndex; // Index to the HET table. Only used when the MPQ has HET table - DWORD dwFileSize; // Decompressed size of the file - DWORD dwCmpSize; // Compressed size of the file (i.e., size of the file data in the MPQ) - DWORD dwFlags; // File flags (from block table) - USHORT lcLocale; // Locale ID for the file - USHORT wPlatform; // Platform ID for the file - 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. -}; + ULONGLONG FileNameHash; // Jenkins hash of the file name. Only used when the MPQ has BET table. + 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. + DWORD dwFileSize; // Decompressed size of the file + DWORD dwCmpSize; // Compressed size of the file (i.e., size of the file data in the MPQ) + DWORD dwFlags; // File flags (from block table) + DWORD dwCrc32; // CRC32 from (attributes) file. 0 if not present. + BYTE 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 -struct TMPQExtTable +typedef struct _TMPQExtHeader { - DWORD dwSignature; // 'HET\x1A' or 'BET\x1A' - DWORD dwVersion; // Version. Seems to be always 1 - DWORD dwDataSize; // Size of the contained table + 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 -}; +} TMPQExtHeader; -// -// 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). -// -struct TMPQBitmap +// Structure for HET table header +typedef struct _TMPQHetHeader { - 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) -}; + TMPQExtHeader ExtHdr; + + DWORD dwTableSize; // Size of the entire HET table, including HET_TABLE_HEADER (in bytes) + DWORD dwEntryCount; // Number of occupied entries in the HET table + DWORD dwTotalCount; // Total number of entries in the HET table + DWORD dwNameHashBitSize; // Size of the name hash entry (in bits) + DWORD dwIndexSizeTotal; // Total size of file index (in bits) + DWORD dwIndexSizeExtra; // Extra bits in the file index + DWORD dwIndexSize; // Effective size of the file index (in bits) + DWORD dwIndexTableSize; // Size of the block index subtable (in bytes) + +} TMPQHetHeader; + +// Structure for BET table header +typedef struct _TMPQBetHeader +{ + TMPQExtHeader ExtHdr; + + DWORD dwTableSize; // Size of the entire BET table, including the header (in bytes) + DWORD dwEntryCount; // Number of entries in the BET table. Must match HET_TABLE_HEADER::dwEntryCount + DWORD dwUnknown08; + DWORD dwTableEntrySize; // Size of one table entry (in bits) + DWORD dwBitIndex_FilePos; // Bit index of the file position (within the entry record) + DWORD dwBitIndex_FileSize; // Bit index of the file size (within the entry record) + DWORD dwBitIndex_CmpSize; // Bit index of the compressed size (within the entry record) + DWORD dwBitIndex_FlagIndex; // Bit index of the flag index (within the entry record) + DWORD dwBitIndex_Unknown; // Bit index of the ??? (within the entry record) + DWORD dwBitCount_FilePos; // Bit size of file position (in the entry record) + DWORD dwBitCount_FileSize; // Bit size of file size (in the entry record) + DWORD dwBitCount_CmpSize; // Bit size of compressed file size (in the entry record) + DWORD dwBitCount_FlagIndex; // Bit size of flags index (in the entry record) + DWORD dwBitCount_Unknown; // Bit size of ??? (in the entry record) + DWORD dwBitTotal_NameHash2; // Total bit size of the NameHash2 + DWORD dwBitExtra_NameHash2; // Extra bits in the NameHash2 + DWORD dwBitCount_NameHash2; // Effective size of NameHash2 (in bits) + DWORD dwNameHashArraySize; // Size of NameHash2 table, in bytes + DWORD dwFlagCount; // Number of flags in the following array + +} TMPQBetHeader; // 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 + TMPQBits * pBetIndexes; // Bit array of FileIndex values + LPBYTE pNameHashes; // Array of NameHash1 values (NameHash1 = upper 8 bits of FileName hashe) + 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 -}; + DWORD dwEntryCount; // Number of occupied entries in the HET table + DWORD dwTotalCount; // Number of entries in both NameHash and FileIndex table + DWORD dwNameHashBitSize; // Size of the name hash entry (in bits) + 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) +} 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 + TMPQBits * pNameHashes; // Array of NameHash2 entries (lower 24 bits of FileName hash) + TMPQBits * 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 -}; + 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 dwBitTotal_NameHash2; // Total size of the NameHash2 + DWORD dwBitExtra_NameHash2; // Extra bits in the NameHash2 + DWORD dwBitCount_NameHash2; // Effective size of the NameHash2 + DWORD dwEntryCount; // Number of entries + DWORD dwFlagCount; // Number of file flags in pFileFlags +} TMPQBetTable; + +// Structure for patch prefix +typedef struct _TMPQNamePrefix +{ + size_t nLength; // Length of this patch prefix. Can be 0 + char szPatchPrefix[1]; // Patch name prefix (variable length). If not empty, it always starts with backslash. +} TMPQNamePrefix; + +// Structure for name cache +typedef struct _TMPQNameCache +{ + DWORD FirstNameOffset; // Offset of the first name in the name list (in bytes) + DWORD FreeSpaceOffset; // Offset of the first free byte in the name cache (in bytes) + DWORD TotalCacheSize; // Size, in bytes, of the cache. Includes wildcard + DWORD SearchOffset; // Used by SListFileFindFirstFile + + // Followed by search mask (ASCIIZ, '\0' if none) + // Followed by name cache (ANSI multistring) + +} TMPQNameCache; // Archive handle structure -struct TMPQArchive +typedef struct _TMPQArchive { - TFileStream * pStream; // Open stream for the MPQ + 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) + 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) + ULONGLONG FileSize; // Size of the file at the moment of file open + ULONGLONG FileOffsetMask; // 0xFFFFFFFF for MPQ v 1, otherwise 0xFFFFFFFFFFFFFFFFull - TMPQArchive * haPatch; // Pointer to patch archive, if any - 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 + struct _TMPQArchive * haPatch; // Pointer to patch archive, if any + struct _TMPQArchive * haBase; // Pointer to base ("previous version") archive, if any + TMPQNamePrefix * pPatchPrefix; // Patch prefix to precede names of patch files - 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 + TMPQUserData * pUserData; // MPQ user data (NULL if not present in the file) + TMPQHeader * pHeader; // MPQ file header + TMPQHash * pHashTable; // Hash table + TMPQHetTable * pHetTable; // HET table + TFileEntry * pFileTable; // File table + HASH_STRING pfnHashString; // Hashing function that will convert the file name into hash + + TMPQUserData UserData; // MPQ user data. Valid only when ID_MPQ_USERDATA has been found + DWORD HeaderData[MPQ_HEADER_DWORDS]; // Storage for MPQ header DWORD dwHETBlockSize; DWORD dwBETBlockSize; - DWORD dwFileTableSize; // Current size of the file table, e.g. index of the entry past the last occupied one - DWORD dwMaxFileCount; // Maximum number of files in the MPQ - DWORD dwSectorSize; // Default size of one file sector - DWORD dwFileFlags1; // Flags for (listfile) - DWORD dwFileFlags2; // Flags for (attributes) - DWORD dwAttrFlags; // Flags for the (attributes) file, see MPQ_ATTRIBUTE_XXX - DWORD dwFlags; // See MPQ_FLAG_XXXXX -}; + DWORD dwMaxFileCount; // Maximum number of files in the MPQ. Also total size of the file table. + DWORD dwFileTableSize; // Current size of the file table, e.g. index of the entry past the last occupied one + DWORD dwReservedFiles; // Number of entries reserved for internal MPQ files (listfile, attributes) + DWORD dwSectorSize; // Default size of one file sector + DWORD dwFileFlags1; // Flags for (listfile) + DWORD dwFileFlags2; // Flags for (attributes) + DWORD dwFileFlags3; // Flags for (signature) + DWORD dwAttrFlags; // Flags for the (attributes) file, see MPQ_ATTRIBUTE_XXX + DWORD dwValidFileFlags; // Valid flags for the current MPQ + DWORD dwRealHashTableSize; // Real size of the hash table, if MPQ_FLAG_HASH_TABLE_CUT is set in dwFlags + DWORD dwFlags; // See MPQ_FLAG_XXXXX + DWORD dwSubType; // See MPQ_SUBTYPE_XXX + + DWORD dwFileCount; // Number of open files + DWORD dwRefCount; // Number of references + + SFILE_ADDFILE_CALLBACK pfnAddFileCB; // Callback function for adding files + void * pvAddFileUserData; // User data thats passed to the callback + + SFILE_COMPACT_CALLBACK pfnCompactCB; // Callback function for compacting the archive + ULONGLONG CompactBytesProcessed; // Amount of bytes that have been processed during a particular compact call + ULONGLONG CompactTotalBytes; // Total amount of bytes to be compacted + void * pvCompactUserData; // User data thats passed to the callback +} TMPQArchive; // File handle structure -struct TMPQFile +typedef struct _TMPQFile { - TFileStream * pStream; // File stream. Only used on local files - TMPQArchive * ha; // Archive handle - TFileEntry * pFileEntry; // File entry for the file - DWORD dwFileKey; // Decryption key - DWORD dwFilePos; // Current file position - ULONGLONG RawFilePos; // Offset in MPQ archive (relative to file begin) - ULONGLONG MpqFilePos; // Offset in MPQ archive (relative to MPQ header) - DWORD dwMagic; // 'FILE' + TFileStream * pStream; // File stream. Only used on local files + TMPQArchive * ha; // Archive handle + TMPQHash * pHashEntry; // Pointer to hash table entry, if the file was open using hash table + TFileEntry * pFileEntry; // File entry for the file + ULONGLONG RawFilePos; // Offset in MPQ archive (relative to file begin) + ULONGLONG MpqFilePos; // Offset in MPQ archive (relative to MPQ header) + DWORD dwHashIndex; // Hash table index (0xFFFFFFFF if not used) + DWORD dwFileKey; // Decryption key + DWORD dwFilePos; // Current file position + DWORD dwMagic; // 'FILE' - 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 + struct _TMPQFile * hfPatch; // Pointer to opened patch file - 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 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) + TPatchInfo * pPatchInfo; // Patch info block, preceding the sector table + LPDWORD SectorOffsets; // Position of each file sector, relative to the begin of the file. Only for compressed files. + LPDWORD SectorChksums; // Array of sector checksums (either ADLER32 or MD5) values for each file sector + LPBYTE pbFileData; // Data of the file (single unit files, patched files) + DWORD cbFileData; // Size of file data + DWORD dwCompression0; // Compression that will be used on the first 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) - LPBYTE pbFileSector; // Last loaded file sector. For single unit files, entire file content - DWORD dwSectorOffs; // File position of currently loaded file sector - DWORD dwSectorSize; // Size of the file sector. For single unit files, this is equal to the file size + LPBYTE pbFileSector; // Last loaded file sector. For single unit files, entire file content + DWORD dwSectorOffs; // File position of currently loaded file sector + DWORD dwSectorSize; // Size of the file sector. For single unit files, this is equal to the file size - 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 + void * hctx; // 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 -}; + DWORD dwAddFileError; // Result of the "Add File" operations + + 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 +} TMPQFile; // Structure for SFileFindFirstFile and SFileFindNextFile typedef struct _SFILE_FIND_DATA { - char cFileName[MAX_PATH]; // Full name of the found file - char * szPlainName; // Plain name of the found file - DWORD dwHashIndex; // Hash table index for the file - DWORD dwBlockIndex; // Block table index for the file - DWORD dwFileSize; // File size in bytes - DWORD dwFileFlags; // MPQ file flags - DWORD dwCompSize; // Compressed file size - DWORD dwFileTimeLo; // Low 32-bits of the file time (0 if not present) - DWORD dwFileTimeHi; // High 32-bits of the file time (0 if not present) - LCID lcLocale; // Locale version + char cFileName[MAX_PATH]; // Full name of the found file + char * szPlainName; // Plain name of the found file + DWORD dwHashIndex; // Hash table index for the file (HAH_ENTRY_FREE if no hash table) + DWORD dwBlockIndex; // Block table index for the file + DWORD dwFileSize; // File size in bytes + DWORD dwFileFlags; // MPQ file flags + DWORD dwCompSize; // Compressed file size + DWORD dwFileTimeLo; // Low 32-bits of the file time (0 if not present) + DWORD dwFileTimeHi; // High 32-bits of the file time (0 if not present) + LCID lcLocale; // Compound of file locale (16 bits) and platform (8 bits) } SFILE_FIND_DATA, *PSFILE_FIND_DATA; typedef struct _SFILE_CREATE_MPQ { - 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 + 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). Use MPQ_FILE_DEFAULT_INTERNAL to set default flags + DWORD dwFileFlags2; // File flags for (attributes). Use MPQ_FILE_DEFAULT_INTERNAL to set default flags + DWORD dwFileFlags3; // File flags for (signature). Use MPQ_FILE_DEFAULT_INTERNAL to set default flags + 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; +typedef struct _SFILE_MARKERS +{ + DWORD dwSize; // Size of this structure, in bytes + DWORD dwSignature; // Alternate MPQ header marker + const char * szHashTableKey; // Replacement for "(hash table)" + const char * szBlockTableKey; // Replacement for "(block table)" +} SFILE_MARKERS, *PSFILE_MARKERS; + +//----------------------------------------------------------------------------- +// TMPQBits support - functions + +void GetMPQBits(TMPQBits * pBits, unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, int nResultByteSize); + //----------------------------------------------------------------------------- // Stream support - functions +// Structure used by FileStream_GetBitmap +struct TStreamBitmap +{ + ULONGLONG StreamSize; // Size of the stream, in bytes + DWORD BitmapSize; // Size of the block map, in bytes + DWORD BlockCount; // Number of blocks in the stream + DWORD BlockSize; // Size of one block + DWORD IsComplete; // Nonzero if the file is complete + + // Followed by the BYTE array, each bit means availability of one block +}; + +// UNICODE versions of the file access functions 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); +const TCHAR * FileStream_GetFileName(TFileStream * pStream); +size_t FileStream_Prefix(const TCHAR * szFileName, DWORD * pdwProvider); + +bool FileStream_SetCallback(TFileStream * pStream, SFILE_DOWNLOAD_CALLBACK pfnCallback, void * pvUserData); + +bool FileStream_GetBitmap(TFileStream * pStream, void * pvBitmap, DWORD cbBitmap, DWORD * pcbLengthNeeded); 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_GetPos(TFileStream * pStream, ULONGLONG & ByteOffset); -bool FileStream_SetPos(TFileStream * pStream, ULONGLONG ByteOffset); -bool FileStream_GetSize(TFileStream * pStream, ULONGLONG & FileSize); bool FileStream_SetSize(TFileStream * pStream, ULONGLONG NewFileSize); +bool FileStream_GetSize(TFileStream * pStream, ULONGLONG * pFileSize); +bool FileStream_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset); bool FileStream_GetTime(TFileStream * pStream, ULONGLONG * pFT); -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); +bool FileStream_GetFlags(TFileStream * pStream, LPDWORD pdwStreamFlags); +bool FileStream_Replace(TFileStream * pStream, TFileStream * pNewStream); void FileStream_Close(TFileStream * pStream); //----------------------------------------------------------------------------- @@ -846,28 +1012,39 @@ typedef bool (WINAPI * SFILEREADFILE)(HANDLE, void *, DWORD, LPDWORD, LPOVERLAP //----------------------------------------------------------------------------- // Functions for manipulation with StormLib global flags +// Macros for making LCID from Locale and Platform +#define SFILE_MAKE_LCID(locale, platform) ((LCID)(USHORT)locale | (LCID)(BYTE)platform << 0x10) +#define SFILE_LOCALE(lcFileLocale) (USHORT)(lcFileLocale & 0xFFFF) +#define SFILE_PLATFORM(lcFileLocale) (BYTE)(lcFileLocale >> 0x10) + +// Alternate marker support. This is for MPQs masked as DLLs (*.asi), which +// patch Storm.dll at runtime. Call before SFileOpenArchive +bool WINAPI SFileSetArchiveMarkers(PSFILE_MARKERS pMarkers); + +// Call before SFileOpenFileEx LCID WINAPI SFileGetLocale(); -LCID WINAPI SFileSetLocale(LCID lcNewLocale); +LCID WINAPI SFileSetLocale(LCID lcFileLocale); //----------------------------------------------------------------------------- // Functions for archive manipulation 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 SFileCreateArchive(const TCHAR * szMpqName, DWORD dwCreateFlags, 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 SFileSetDownloadCallback(HANDLE hMpq, SFILE_DOWNLOAD_CALLBACK DownloadCB, void * pvUserData); bool WINAPI SFileFlushArchive(HANDLE hMpq); bool WINAPI SFileCloseArchive(HANDLE hMpq); // Adds another listfile into MPQ. The currently added listfile(s) remain, // so you can use this API to combining more listfiles. // Note that this function is internally called by SFileFindFirstFile -int WINAPI SFileAddListFile(HANDLE hMpq, const char * szListFile); +DWORD WINAPI SFileAddListFile(HANDLE hMpq, const TCHAR * szListFile); +DWORD WINAPI SFileAddListFileEntries(HANDLE hMpq, const char ** listFileEntries, DWORD dwEntryCount); // 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 SFileSetCompactCallback(HANDLE hMpq, SFILE_COMPACT_CALLBACK CompactCB, void * pvUserData); +bool WINAPI SFileCompactArchive(HANDLE hMpq, const TCHAR * szListFile, bool bReserved); // Changing the maximum file count DWORD WINAPI SFileGetMaxFileCount(HANDLE hMpq); @@ -888,19 +1065,20 @@ bool WINAPI SFileIsPatchedArchive(HANDLE hMpq); // Functions for file manipulation // Reading from MPQ file +bool WINAPI SFileHasFile(HANDLE hMpq, const char * szFileName); 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); +// Retrieving info about a file in the archive +bool WINAPI SFileGetFileInfo(HANDLE hMpqOrFile, SFileInfoClass InfoClass, void * pvFileInfo, DWORD cbFileInfo, LPDWORD pcbLengthNeeded); +bool WINAPI SFileGetFileName(HANDLE hFile, char * szFileName); // szFileName must be at least MAX_PATH chars +bool WINAPI SFileFreeFileInfo(void * pvFileInfo, SFileInfoClass InfoClass); // High-level extract function -bool WINAPI SFileExtractFile(HANDLE hMpq, const char * szToExtract, const TCHAR * 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 @@ -913,60 +1091,83 @@ bool WINAPI SFileGetFileChecksums(HANDLE hMpq, const char * szFileName, LPDWOR DWORD WINAPI SFileVerifyFile(HANDLE hMpq, const char * szFileName, DWORD 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); +DWORD WINAPI SFileVerifyRawData(HANDLE hMpq, DWORD dwWhatToVerify, const char * szFileName); // Verifies the signature, if present +bool WINAPI SFileSignArchive(HANDLE hMpq, DWORD dwSignatureType); DWORD WINAPI SFileVerifyArchive(HANDLE hMpq); //----------------------------------------------------------------------------- // Functions for file searching -HANDLE WINAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_DATA * lpFindFileData, const char * szListFile); +HANDLE WINAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_DATA * lpFindFileData, const TCHAR * szListFile); bool WINAPI SFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData); bool WINAPI SFileFindClose(HANDLE hFind); -HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const char * szMask, SFILE_FIND_DATA * lpFindFileData); +HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const TCHAR * szListFile, const char * szMask, SFILE_FIND_DATA * lpFindFileData); bool WINAPI SListFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData); bool WINAPI SListFileFindClose(HANDLE hFind); // Locale support -int WINAPI SFileEnumLocales(HANDLE hMpq, const char * szFileName, LCID * plcLocales, LPDWORD pdwMaxLocales, DWORD dwSearchScope); +DWORD WINAPI SFileEnumLocales(HANDLE hMpq, const char * szFileName, LCID * PtrFileLocales, LPDWORD PtrMaxLocales, DWORD dwSearchScope); //----------------------------------------------------------------------------- // Support for adding files to the MPQ -bool WINAPI SFileCreateFile(HANDLE hMpq, const char * szArchivedName, ULONGLONG FileTime, DWORD dwFileSize, LCID lcLocale, DWORD dwFlags, HANDLE * phFile); +bool WINAPI SFileCreateFile(HANDLE hMpq, const char * szArchivedName, ULONGLONG FileTime, DWORD dwFileSize, LCID lcFileLocale, 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 TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwCompression, DWORD dwCompressionNext = MPQ_COMPRESSION_NEXT_SAME); -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 = SFILE_OPEN_FROM_MPQ); +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); bool WINAPI SFileSetDataCompression(DWORD DataCompression); -bool WINAPI SFileSetAddFileCallback(HANDLE hMpq, SFILE_ADDFILE_CALLBACK AddFileCB, void * pvData); +bool WINAPI SFileSetAddFileCallback(HANDLE hMpq, SFILE_ADDFILE_CALLBACK AddFileCB, void * pvUserData); //----------------------------------------------------------------------------- // Compression and decompression -int WINAPI SCompImplode (char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer); -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); +int WINAPI SCompImplode (void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer); +int WINAPI SCompExplode (void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer); +int WINAPI SCompCompress (void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, unsigned uCompressionMask, int nCmpType, int nCmpLevel); +int WINAPI SCompDecompress (void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer); +int WINAPI SCompDecompress2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer); //----------------------------------------------------------------------------- -// Non-Windows support for SetLastError/GetLastError +// Conversion of UTF-8 (MPQ listfiles) into file name safe strings -#ifndef PLATFORM_WINDOWS +#define SFILE_UTF8_REPLACE_INVALID 0x01 // If set, invalid UTF8 combinations will be replaced by 0xFFFD (like MultiByteToWideChar) +#define SFILE_UTF8_KEEP_INVALID_FCH 0x02 // If set, filename-invalid chars will be kept unescaped -void SetLastError(int err); -int GetLastError(); +#define SFILE_UTF8_INVALID_CHARACTER 0xFFFD // Marker of an invalid character +#define SFILE_UNICODE_MAX 0x10FFFF // The highest valid UNICODE char -#endif +// Conversion of MPQ file name to file-name-safe string +DWORD WINAPI SMemUTF8ToFileName( + TCHAR * szBuffer, // Pointer to the output buffer. If NULL, the function will calculate the needed length + size_t ccBuffer, // Length of the output buffer (must include EOS) + const void * lpString, // Pointer to the begin of the string + const void * lpStringEnd, // Pointer to the end of string. If NULL, it's assumed to be zero-terminated + DWORD dwFlags, // Additional flags + size_t * pOutLength); // Pointer to a variable that receives the needed length (optional) + +DWORD WINAPI SMemFileNameToUTF8( + void * lpBuffer, // Pointer to the output buffer. If NULL, the function will calculate the needed length + size_t ccBuffer, // Length of the output buffer (must include EOS) + const TCHAR * szString, // Pointer to the begin of the string + const TCHAR * szStringEnd, // Pointer to the end of string. If NULL, it's assumed to be zero-terminated + DWORD dwFlags, // Reserved + size_t * pOutLength); // Pointer to a variable that receives the needed length in bytes (optional) + +//----------------------------------------------------------------------------- +// Stormlib-specific support for SetLastError/GetLastError + +void SErrSetLastError(DWORD dwErrCode); +DWORD SErrGetLastError(); //----------------------------------------------------------------------------- // Functions from Storm.dll. They use slightly different names for keeping diff --git a/dep/StormLib/src/StormPort.h b/dep/StormLib/src/StormPort.h index 0914654b9a..25bbb554e6 100644 --- a/dep/StormLib/src/StormPort.h +++ b/dep/StormLib/src/StormPort.h @@ -20,20 +20,21 @@ /* 12.11.03 1.02 Dan Macintosh compatibility */ /* 24.07.04 1.03 Sam Mac OS X compatibility */ /* 22.11.06 1.04 Sam Mac OS X compatibility (for StormLib 6.0) */ -/* 31.12.06 1.05 XPinguin Full GNU/Linux compatibility */ +/* 31.12.06 1.05 XPinguin Full GNU/Linux compatibility */ +/* 17.10.12 1.05 Lad Moved error codes so they don't overlap with errno.h */ /*****************************************************************************/ #ifndef __STORMPORT_H__ #define __STORMPORT_H__ -#ifndef __cplusplus - #define bool char - #define true 1 - #define false 0 +#if !defined(__cplusplus) && !defined(_MSC_VER) + #include #endif +//----------------------------------------------------------------------------- // Defines for Windows -#if !defined(PLATFORM_DEFINED) && (defined(WIN32) || defined(WIN64)) + +#if !defined(STORMLIB_PLATFORM_DEFINED) && defined(_WIN32) // In MSVC 8.0, there are some functions declared as deprecated. #if _MSC_VER >= 1400 @@ -41,52 +42,78 @@ #define _CRT_NON_CONFORMING_SWPRINTFS #endif + #if defined(UNICODE) || defined(_UNICODE) + #define STORMLIB_WIDE_CHAR + #endif + #include #include #include #include + + // Suppress definitions of `min` and `max` macros by : + #ifndef NOMINMAX + #define NOMINMAX 1 + #endif #include #include - #define PLATFORM_LITTLE_ENDIAN - #ifdef WIN64 - #define PLATFORM_64BIT - #else - #define PLATFORM_32BIT - #endif + #define STORMLIB_LITTLE_ENDIAN - #define PLATFORM_WINDOWS - #define PLATFORM_DEFINED // The platform is known now + #define STORMLIB_CDECL __cdecl + + #define STORMLIB_WINDOWS + #define STORMLIB_PLATFORM_DEFINED // The platform is known now #endif -// Defines for Mac -#if !defined(PLATFORM_DEFINED) && defined(__APPLE__) // Mac BSD API +//----------------------------------------------------------------------------- +// Defines for Mac + +#if !defined(STORMLIB_PLATFORM_DEFINED) && defined(__APPLE__) // Mac BSD API - // Macintosh #include #include #include #include #include #include + #include + #include + #include + #include #include - - #define PKEXPORT - #define __SYS_ZLIB - #define __SYS_BZLIB - #ifndef __BIG_ENDIAN__ - #define PLATFORM_LITTLE_ENDIAN + // Support for PowerPC on Max OS X + #if (__ppc__ == 1) || (__POWERPC__ == 1) || (_ARCH_PPC == 1) + #include + #include #endif - #define PLATFORM_MAC - #define PLATFORM_DEFINED // The platform is known now + #define PKEXPORT + + #ifndef __SYS_ZLIB + #define __SYS_ZLIB + #endif + + #ifndef __SYS_BZLIB + #define __SYS_BZLIB + #endif + + #ifndef __BIG_ENDIAN__ + #define STORMLIB_LITTLE_ENDIAN + #endif + + #define STORMLIB_MAC + #define STORMLIB_HAS_MMAP // Indicate that we have mmap support + #define STORMLIB_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) +//----------------------------------------------------------------------------- +// Defines for HAIKU platform + +#if !defined(STORMLIB_PLATFORM_DEFINED) && defined(__HAIKU__) #include #include @@ -102,20 +129,198 @@ #include #include - #define PLATFORM_LITTLE_ENDIAN - #define PLATFORM_LINUX - #define PLATFORM_DEFINED + #ifndef __BIG_ENDIAN__ + #define STORMLIB_LITTLE_ENDIAN + #endif + + #define STORMLIB_MAC // Use Mac compatible code + #define STORMLIB_HAIKU + #define STORMLIB_PLATFORM_DEFINED // The platform is known now #endif -// Definition of Windows-specific structures for non-Windows platforms -#ifndef PLATFORM_WINDOWS - #if __LP64__ - #define PLATFORM_64BIT - #else - #define PLATFORM_32BIT +//----------------------------------------------------------------------------- +// Defines for AMIGA platform + +#if !defined(STORMLIB_PLATFORM_DEFINED) && defined(__AMIGA__) + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #ifndef __BIG_ENDIAN__ + #define STORMLIB_LITTLE_ENDIAN #endif + #define STORMLIB_MAC // Use Mac compatible code + #define STORMLIB_AMIGA + #define STORMLIB_PLATFORM_DEFINED + +#endif + +//----------------------------------------------------------------------------- +// Defines for Switch platform + +#if !defined(STORMLIB_PLATFORM_DEFINED) && defined(__SWITCH__) + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #ifndef __BIG_ENDIAN__ + #define STORMLIB_LITTLE_ENDIAN + #endif + + #define STORMLIB_MAC // Use Mac compatible code + #define STORMLIB_SWITCH + #define STORMLIB_PLATFORM_DEFINED + +#endif + +//----------------------------------------------------------------------------- +// Defines for 3DS platform + +#if !defined(STORMLIB_PLATFORM_DEFINED) && defined(__3DS__) + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #define STORMLIB_LITTLE_ENDIAN + + #define STORMLIB_MAC // Use Mac compatible code + #define STORMLIB_CTR + #define STORMLIB_PLATFORM_DEFINED + +#endif + +//----------------------------------------------------------------------------- +// Defines for Vita platform + +#if !defined(STORMLIB_PLATFORM_DEFINED) && defined(__vita__) + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #ifndef __BIG_ENDIAN__ + #define STORMLIB_LITTLE_ENDIAN + #endif + + #define STORMLIB_MAC // Use Mac compatible code + #define STORMLIB_VITA + #define STORMLIB_PLATFORM_DEFINED + +#endif + +//----------------------------------------------------------------------------- +// Defines for Wii U platform + +#if !defined(STORMLIB_PLATFORM_DEFINED) && defined(__WIIU__) + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #undef STORMLIB_LITTLE_ENDIAN // Wii U is always big endian + + #define STORMLIB_MAC // Use Mac compatible code + #define STORMLIB_WIIU + #define STORMLIB_PLATFORM_DEFINED + +#endif + +//----------------------------------------------------------------------------- +// Assumption: If the platform is not defined, assume a Linux-like platform + +#if !defined(STORMLIB_PLATFORM_DEFINED) + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #ifndef __BIG_ENDIAN__ + #define STORMLIB_LITTLE_ENDIAN + #endif + + // Platforms with mmap support + #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) + #include + #define STORMLIB_HAS_MMAP + #endif + + #define STORMLIB_LINUX + #define STORMLIB_PLATFORM_DEFINED + +#endif + +//----------------------------------------------------------------------------- +// Definition of Windows-specific types for non-Windows platforms + +#ifndef STORMLIB_WINDOWS + + // __cdecl means nothing on non-Windows + #define STORMLIB_CDECL /* */ + // Typedefs for ANSI C typedef unsigned char BYTE; typedef unsigned short USHORT; @@ -127,14 +332,18 @@ typedef long long LONGLONG; typedef unsigned long long ULONGLONG; typedef void * HANDLE; - typedef void * LPOVERLAPPED; // Unsupported on Linux and Mac + typedef void * LPOVERLAPPED; typedef char TCHAR; typedef unsigned int LCID; typedef LONG * PLONG; typedef DWORD * LPDWORD; typedef BYTE * LPBYTE; + typedef const char * LPCTSTR; + typedef const char * LPCSTR; + typedef char * LPTSTR; + typedef char * LPSTR; - #ifdef PLATFORM_32BIT + #ifndef __LP64__ #define _LZMA_UINT32_IS_ULONG #endif @@ -143,7 +352,7 @@ #define MAX_PATH 1024 #endif - #define WINAPI + #define WINAPI #define FILE_BEGIN SEEK_SET #define FILE_CURRENT SEEK_CUR @@ -153,46 +362,76 @@ #define _tcslen strlen #define _tcscpy strcpy #define _tcscat strcat + #define _tcschr strchr #define _tcsrchr strrchr + #define _tcsstr strstr + #define _tcsnicmp strncasecmp #define _tprintf printf #define _stprintf sprintf #define _tremove remove + #define _tmain main #define _stricmp strcasecmp #define _strnicmp strncasecmp + #define _tcsicmp strcasecmp #define _tcsnicmp strncasecmp -#endif // !WIN32 +#endif // !STORMLIB_WINDOWS // 64-bit calls are supplied by "normal" calls on Mac -#if defined(PLATFORM_MAC) +#if defined(STORMLIB_MAC) #define stat64 stat #define fstat64 fstat #define lseek64 lseek + #define ftruncate64 ftruncate #define off64_t off_t #define O_LARGEFILE 0 #endif -// Platform-specific error codes for UNIX-based platforms -#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) +// 64-bit calls are supplied by "normal" calls on musl (like Mac) +#if defined(__linux__) && !defined(__GLIBC__) + #define stat64 stat + #define fstat64 fstat + #define lseek64 lseek + #define ftruncate64 ftruncate + #define off64_t off_t + #ifndef O_LARGEFILE + #define O_LARGEFILE 0 + #endif +#endif + +// Platform-specific error codes for non-Windows platforms +#ifndef ERROR_SUCCESS #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_NEGATIVE_SEEK ESPIPE #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 + #define ERROR_BAD_FORMAT 1000 // No such error codes under Linux + #define ERROR_NO_MORE_FILES 1001 + #define ERROR_HANDLE_EOF 1002 + #define ERROR_CAN_NOT_COMPLETE 1003 + #define ERROR_FILE_CORRUPT 1004 + #define ERROR_BUFFER_OVERFLOW 1005 + #define ERROR_INVALID_DATA 1006 + #define ERROR_NO_UNICODE_TRANSLATION 1007 #endif -#ifdef PLATFORM_LITTLE_ENDIAN +// Macros that can sometimes be missing +#ifndef _countof + #define _countof(x) (sizeof(x) / sizeof(x[0])) +#endif + +//----------------------------------------------------------------------------- +// Swapping functions + +#ifdef STORMLIB_LITTLE_ENDIAN #define BSWAP_INT16_UNSIGNED(a) (a) #define BSWAP_INT16_SIGNED(a) (a) #define BSWAP_INT32_UNSIGNED(a) (a) @@ -203,9 +442,13 @@ #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_TMPQHEADER(a,b) {} + #define BSWAP_TMPKHEADER(a) {} #else + +#ifdef __cplusplus + extern "C" { +#endif int16_t SwapInt16(uint16_t); uint16_t SwapUInt16(uint16_t); int32_t SwapInt32(uint32_t); @@ -215,9 +458,12 @@ 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); + void ConvertTMPQHeader(void *header, uint16_t wPart); + void ConvertTMPKHeader(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)) @@ -227,9 +473,8 @@ #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)) + #define BSWAP_TMPQHEADER(a,b) ConvertTMPQHeader((a),(b)) + #define BSWAP_TMPKHEADER(a) ConvertTMPKHeader((a)) #endif #endif // __STORMPORT_H__ diff --git a/dep/StormLib/src/adpcm/adpcm.cpp b/dep/StormLib/src/adpcm/adpcm.cpp index 916fa3811a..abf49da0bc 100644 --- a/dep/StormLib/src/adpcm/adpcm.cpp +++ b/dep/StormLib/src/adpcm/adpcm.cpp @@ -10,349 +10,534 @@ /* 11.03.03 1.00 Lad Splitted from Pkware.cpp */ /* 20.05.03 2.00 Lad Added compression */ /* 19.11.03 2.01 Dan Big endian handling */ +/* 10.01.13 3.00 Lad Refactored, beautified, documented :-) */ /*****************************************************************************/ +#include +#include + #include "adpcm.h" -//------------------------------------------------------------------------------ -// Structures - -typedef union _BYTE_AND_WORD_PTR -{ - short * pw; - unsigned char * pb; -} BYTE_AND_WORD_PTR; - -typedef union _WORD_AND_BYTE_ARRAY -{ - short w; - unsigned char b[2]; -} WORD_AND_BYTE_ARRAY; - //----------------------------------------------------------------------------- // Tables necessary dor decompression -static long Table1503F120[] = +static const int NextStepTable[] = { - 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 + -1, 0, -1, 4, -1, 2, -1, 6, + -1, 1, -1, 5, -1, 3, -1, 7, + -1, 1, -1, 5, -1, 3, -1, 7, + -1, 2, -1, 4, -1, 6, -1, 8 }; -static long step_table[] = +static const int StepSizeTable[] = { - 0x00000007, 0x00000008, 0x00000009, 0x0000000A, 0x0000000B, 0x0000000C, 0x0000000D, 0x0000000E, - 0x00000010, 0x00000011, 0x00000013, 0x00000015, 0x00000017, 0x00000019, 0x0000001C, 0x0000001F, - 0x00000022, 0x00000025, 0x00000029, 0x0000002D, 0x00000032, 0x00000037, 0x0000003C, 0x00000042, - 0x00000049, 0x00000050, 0x00000058, 0x00000061, 0x0000006B, 0x00000076, 0x00000082, 0x0000008F, - 0x0000009D, 0x000000AD, 0x000000BE, 0x000000D1, 0x000000E6, 0x000000FD, 0x00000117, 0x00000133, - 0x00000151, 0x00000173, 0x00000198, 0x000001C1, 0x000001EE, 0x00000220, 0x00000256, 0x00000292, - 0x000002D4, 0x0000031C, 0x0000036C, 0x000003C3, 0x00000424, 0x0000048E, 0x00000502, 0x00000583, - 0x00000610, 0x000006AB, 0x00000756, 0x00000812, 0x000008E0, 0x000009C3, 0x00000ABD, 0x00000BD0, - 0x00000CFF, 0x00000E4C, 0x00000FBA, 0x0000114C, 0x00001307, 0x000014EE, 0x00001706, 0x00001954, - 0x00001BDC, 0x00001EA5, 0x000021B6, 0x00002515, 0x000028CA, 0x00002CDF, 0x0000315B, 0x0000364B, - 0x00003BB9, 0x000041B2, 0x00004844, 0x00004F7E, 0x00005771, 0x0000602F, 0x000069CE, 0x00007462, - 0x00007FFF + 7, 8, 9, 10, 11, 12, 13, 14, + 16, 17, 19, 21, 23, 25, 28, 31, + 34, 37, 41, 45, 50, 55, 60, 66, + 73, 80, 88, 97, 107, 118, 130, 143, + 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, + 724, 796, 876, 963, 1060, 1166, 1282, 1411, + 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, + 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, + 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, + 32767 +}; + +//----------------------------------------------------------------------------- +// Helper class for writing output ADPCM data + +class TADPCMStream +{ + public: + + TADPCMStream(void * pvBuffer, size_t cbBuffer) + { + pbBufferEnd = (unsigned char *)pvBuffer + cbBuffer; + pbBuffer = (unsigned char *)pvBuffer; + } + + bool ReadByteSample(unsigned char & ByteSample) + { + // Check if there is enough space in the buffer + if(pbBuffer >= pbBufferEnd) + return false; + + ByteSample = *pbBuffer++; + return true; + } + + bool WriteByteSample(unsigned char ByteSample) + { + // Check if there is enough space in the buffer + if(pbBuffer >= pbBufferEnd) + return false; + + *pbBuffer++ = ByteSample; + return true; + } + + bool ReadWordSample(short & OneSample) + { + // Check if we have enough space in the output buffer + if((size_t)(pbBufferEnd - pbBuffer) < sizeof(short)) + return false; + + // Write the sample + OneSample = pbBuffer[0] + (((short)pbBuffer[1]) << 0x08); + pbBuffer += sizeof(short); + return true; + } + + bool WriteWordSample(short OneSample) + { + // Check if we have enough space in the output buffer + if((size_t)(pbBufferEnd - pbBuffer) < sizeof(short)) + return false; + + // Write the sample + *pbBuffer++ = (unsigned char)(OneSample & 0xFF); + *pbBuffer++ = (unsigned char)(OneSample >> 0x08); + return true; + } + + int LengthProcessed(void * pvOutBuffer) + { + return (int)((unsigned char *)pbBuffer - (unsigned char *)pvOutBuffer); + } + + unsigned char * pbBufferEnd; + unsigned char * pbBuffer; }; //---------------------------------------------------------------------------- -// CompressWave +// Local functions -// 1500EF70 -int CompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuffer, int dwInLength, int nChannels, int nCmpLevel) -// ECX EDX +static inline short GetNextStepIndex(int StepIndex, unsigned int EncodedSample) { - WORD_AND_BYTE_ARRAY Wcmp; - BYTE_AND_WORD_PTR out; // Pointer to the output buffer - long SInt32Array1[2]; - long SInt32Array2[2]; - long SInt32Array3[2]; - long nBytesRemains = dwOutLength; // Number of bytes remaining - long nWordsRemains; // Number of words remaining -// unsigned char * pbSaveOutBuffer; // Copy of output buffer (actually not used) - unsigned long dwBitBuff; - unsigned long dwStopBit; - unsigned long dwBit; - unsigned long ebx; - unsigned long esi; - long nTableValue; - long nOneWord; - long var_1C; - long var_2C; - int nLength; - int nIndex; - int nValue; - int i, chnl; + // Get the next step index + StepIndex = StepIndex + NextStepTable[EncodedSample & 0x1F]; - // If less than 2 bytes remain, don't decompress anything -// pbSaveOutBuffer = pbOutBuffer; - out.pb = pbOutBuffer; - if(nBytesRemains < 2) - return 2; + // Don't make the step index overflow + if(StepIndex < 0) + StepIndex = 0; + else if(StepIndex > 88) + StepIndex = 88; - Wcmp.b[1] = (unsigned char)(nCmpLevel - 1); - Wcmp.b[0] = (unsigned char)0; + return (short)StepIndex; +} - *out.pw++ = BSWAP_INT16_SIGNED(Wcmp.w); - if((out.pb - pbOutBuffer + (nChannels * 2)) > nBytesRemains) - return (int)(out.pb - pbOutBuffer + (nChannels * 2)); - - SInt32Array1[0] = SInt32Array1[1] = 0x2C; - - for(i = 0; i < nChannels; i++) +static inline int UpdatePredictedSample(int PredictedSample, int EncodedSample, int Difference, int BitMask = 0x40) +{ + // Is the sign bit set? + if(EncodedSample & BitMask) { - nOneWord = BSWAP_INT16_SIGNED(*pwInBuffer++); - *out.pw++ = BSWAP_INT16_SIGNED((short)nOneWord); - SInt32Array2[i] = nOneWord; + PredictedSample -= Difference; + if(PredictedSample <= -32768) + PredictedSample = -32768; + } + else + { + PredictedSample += Difference; + if(PredictedSample >= 32767) + PredictedSample = 32767; } - // Weird. But it's there - nLength = dwInLength; - if(nLength < 0) // mov eax, dwInLength; cdq; sub eax, edx; - nLength++; + return PredictedSample; +} - 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(chnl = nChannels; chnl < nWordsRemains; chnl++) - { - // 1500F030 - if((out.pb - pbOutBuffer + 2) > nBytesRemains) - return (int)(out.pb - pbOutBuffer + 2); +static inline int DecodeSample(int PredictedSample, int EncodedSample, int StepSize, int Difference) +{ + if(EncodedSample & 0x01) + Difference += (StepSize >> 0); - // Switch index - if(nChannels == 2) - nIndex = (nIndex == 0) ? 1 : 0; + if(EncodedSample & 0x02) + Difference += (StepSize >> 1); - // Load one word from the input stream - nOneWord = BSWAP_INT16_SIGNED(*pwInBuffer++); // ecx - nOneWord - SInt32Array3[nIndex] = nOneWord; + if(EncodedSample & 0x04) + Difference += (StepSize >> 2); - // esi - SInt32Array2[nIndex] - // eax - nValue - nValue = nOneWord - SInt32Array2[nIndex]; - nValue = (nValue < 0) ? ((nValue ^ 0xFFFFFFFF) + 1) : nValue; + if(EncodedSample & 0x08) + Difference += (StepSize >> 3); - ebx = (nOneWord >= SInt32Array2[nIndex]) ? 0 : 0x40; + if(EncodedSample & 0x10) + Difference += (StepSize >> 4); - // esi - SInt32Array2[nIndex] - // edx - step_table[SInt32Array2[nIndex]] - // edi - (step_table[SInt32Array1[nIndex]] >> nCmpLevel) - nTableValue = step_table[SInt32Array1[nIndex]]; - dwStopBit = (unsigned long)nCmpLevel; + if(EncodedSample & 0x20) + Difference += (StepSize >> 5); - // edi - nIndex; - if(nValue < (nTableValue >> nCmpLevel)) - { - if(SInt32Array1[nIndex] != 0) - SInt32Array1[nIndex]--; - *out.pb++ = 0x80; - } - else - { - while(nValue > nTableValue * 2) - { - if(SInt32Array1[nIndex] >= 0x58 || nLength == 0) - break; - - SInt32Array1[nIndex] += 8; - if(SInt32Array1[nIndex] > 0x58) - SInt32Array1[nIndex] = 0x58; - - nTableValue = step_table[SInt32Array1[nIndex]]; - *out.pb++ = 0x81; - nLength--; - } - - var_2C = nTableValue >> Wcmp.b[1]; - dwBitBuff = 0; - - esi = (1 << (dwStopBit - 2)); - dwStopBit = (esi <= 0x20) ? esi : 0x20; - - for(var_1C = 0, dwBit = 1; ; dwBit <<= 1) - { -// esi = var_1C + nTableValue; - if((var_1C + nTableValue) <= nValue) - { - var_1C += nTableValue; - dwBitBuff |= dwBit; - } - if(dwBit == dwStopBit) - break; - - nTableValue >>= 1; - } - - nValue = SInt32Array2[nIndex]; - if(ebx != 0) - { - nValue -= (var_1C + var_2C); - if(nValue < -32768) - nValue = -32768; - } - else - { - nValue += (var_1C + var_2C); - if(nValue > 32767) - nValue = 32767; - } - - SInt32Array2[nIndex] = nValue; - *out.pb++ = (unsigned char)(dwBitBuff | ebx); - nTableValue = Table1503F120[dwBitBuff & 0x1F]; - SInt32Array1[nIndex] = SInt32Array1[nIndex] + nTableValue; - if(SInt32Array1[nIndex] < 0) - SInt32Array1[nIndex] = 0; - else if(SInt32Array1[nIndex] > 0x58) - SInt32Array1[nIndex] = 0x58; - } - } - - return (int)(out.pb - pbOutBuffer); + return UpdatePredictedSample(PredictedSample, EncodedSample, Difference); } //---------------------------------------------------------------------------- -// DecompressADPCM +// Compression routine -// 1500F230 -int DecompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, unsigned char * pbInBuffer, int dwInLength, int nChannels) +int CompressADPCM(void * pvOutBuffer, int cbOutBuffer, void * pvInBuffer, int cbInBuffer, int ChannelCount, int CompressionLevel) { - 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 nIndex; - int i; + TADPCMStream os(pvOutBuffer, cbOutBuffer); // The output stream + TADPCMStream is(pvInBuffer, cbInBuffer); // The input stream + unsigned char BitShift = (unsigned char)(CompressionLevel - 1); + short PredictedSamples[MAX_ADPCM_CHANNEL_COUNT];// Predicted samples for each channel + short StepIndexes[MAX_ADPCM_CHANNEL_COUNT]; // Step indexes for each channel + short InputSample; // Input sample for the current channel + int TotalStepSize; + int ChannelIndex; + int AbsDifference; + int Difference; + int MaxBitMask; + int StepSize; - SInt32Array1[0] = SInt32Array1[1] = 0x2C; - out.pb = pbOutBuffer; - in.pb = pbInBuffer; - in.pw++; + // Don't compress when buffer size is too small + if(2 * ChannelCount + 2 > cbOutBuffer) + return 2 * ChannelCount + 2; - // Fill the Uint32Array2 array by channel values. - for(i = 0; i < nChannels; i++) + // First byte in the output stream contains zero. The second one contains the compression level + os.WriteByteSample(0); + if(!os.WriteByteSample(BitShift)) + return 2; + + // Set the initial step index for each channel + PredictedSamples[0] = PredictedSamples[1] = 0; + StepIndexes[0] = StepIndexes[1] = INITIAL_ADPCM_STEP_INDEX; + + // Next, InitialSample value for each channel follows + for(int i = 0; i < ChannelCount; i++) { - nOneWord = BSWAP_INT16_SIGNED(*in.pw++); - SInt32Array2[i] = nOneWord; - if(dwOutLength < 2) - return (int)(out.pb - pbOutBuffer); + // Get the initial sample from the input stream + if(!is.ReadWordSample(InputSample)) + return os.LengthProcessed(pvOutBuffer); - *out.pw++ = BSWAP_INT16_SIGNED((short)nOneWord); - dwOutLength -= sizeof(short); + // Store the initial sample to our sample array + PredictedSamples[i] = InputSample; + + // Also store the loaded sample to the output stream + if(!os.WriteWordSample(InputSample)) + return os.LengthProcessed(pvOutBuffer); } // Get the initial index - nIndex = nChannels - 1; + ChannelIndex = ChannelCount - 1; - // Perform the decompression - while(in.pb < pbInBufferEnd) + // Now keep reading the input data as long as there is something in the input buffer + while(is.ReadWordSample(InputSample)) { - unsigned char nOneByte = *in.pb++; + int EncodedSample = 0; - // Switch index - if(nChannels == 2) - nIndex = (nIndex == 0) ? 1 : 0; + // If we have two channels, we need to flip the channel index + ChannelIndex = (ChannelIndex + 1) % ChannelCount; - // 1500F2A2: Get one byte from input buffer - if(nOneByte & 0x80) + // Get the difference from the previous sample. + // If the difference is negative, set the sign bit to the encoded sample + AbsDifference = InputSample - PredictedSamples[ChannelIndex]; + if(AbsDifference < 0) { - switch(nOneByte & 0x7F) - { - case 0: // 1500F315 - if(SInt32Array1[nIndex] != 0) - SInt32Array1[nIndex]--; + AbsDifference = -AbsDifference; + EncodedSample |= 0x40; + } - if(dwOutLength < 2) - return (int)(out.pb - pbOutBuffer); + // If the difference is too low (higher that difference treshold), + // write a step index modifier marker + StepSize = StepSizeTable[StepIndexes[ChannelIndex]]; + if(AbsDifference < (StepSize >> CompressionLevel)) + { + if(StepIndexes[ChannelIndex] != 0) + StepIndexes[ChannelIndex]--; - *out.pw++ = BSWAP_INT16_SIGNED((unsigned short)SInt32Array2[nIndex]); - dwOutLength -= sizeof(unsigned short); - break; - - case 1: // 1500F2E8 - SInt32Array1[nIndex] += 8; - if(SInt32Array1[nIndex] > 0x58) - SInt32Array1[nIndex] = 0x58; - - if(nChannels == 2) - nIndex = (nIndex == 0) ? 1 : 0; - break; - - case 2: // 1500F41E - break; - - default: // 1500F2C4 - SInt32Array1[nIndex] -= 8; - if(SInt32Array1[nIndex] < 0) - SInt32Array1[nIndex] = 0; - - if(nChannels == 2) - nIndex = (nIndex == 0) ? 1 : 0; - break; - } + os.WriteByteSample(0x80); } else { - // 1500F349 - long temp1 = step_table[SInt32Array1[nIndex]]; // EDI - long temp2 = temp1 >> pbInBuffer[1]; // ESI - long temp3 = SInt32Array2[nIndex]; // ECX - - if(nOneByte & 0x01) // EBX = nOneByte - temp2 += (temp1 >> 0); - - if(nOneByte & 0x02) - temp2 += (temp1 >> 1); - - if(nOneByte & 0x04) - temp2 += (temp1 >> 2); - - if(nOneByte & 0x08) - temp2 += (temp1 >> 3); - - if(nOneByte & 0x10) - temp2 += (temp1 >> 4); - - if(nOneByte & 0x20) - temp2 += (temp1 >> 5); - - if(nOneByte & 0x40) + // If the difference is too high, write marker that + // indicates increase in step size + while(AbsDifference > (StepSize << 1)) { - temp3 = temp3 - temp2; - if(temp3 <= -32768) - temp3 = -32768; + if(StepIndexes[ChannelIndex] >= 0x58) + break; + + // Modify the step index + StepIndexes[ChannelIndex] += 8; + if(StepIndexes[ChannelIndex] > 0x58) + StepIndexes[ChannelIndex] = 0x58; + + // Write the "modify step index" marker + StepSize = StepSizeTable[StepIndexes[ChannelIndex]]; + os.WriteByteSample(0x81); + } + + // Get the limit bit value + MaxBitMask = (1 << (BitShift - 1)); + MaxBitMask = (MaxBitMask > 0x20) ? 0x20 : MaxBitMask; + Difference = StepSize >> BitShift; + TotalStepSize = 0; + + for(int BitVal = 0x01; BitVal <= MaxBitMask; BitVal <<= 1) + { + if((TotalStepSize + StepSize) <= AbsDifference) + { + TotalStepSize += StepSize; + EncodedSample |= BitVal; + } + StepSize >>= 1; + } + + PredictedSamples[ChannelIndex] = (short)UpdatePredictedSample(PredictedSamples[ChannelIndex], + EncodedSample, + Difference + TotalStepSize); + // Write the encoded sample to the output stream + if(!os.WriteByteSample((unsigned char)EncodedSample)) + break; + + // Calculates the step index to use for the next encode + StepIndexes[ChannelIndex] = GetNextStepIndex(StepIndexes[ChannelIndex], EncodedSample); + } + } + + return os.LengthProcessed(pvOutBuffer); +} + +//---------------------------------------------------------------------------- +// Decompression routine + +int DecompressADPCM(void * pvOutBuffer, int cbOutBuffer, void * pvInBuffer, int cbInBuffer, int ChannelCount) +{ + TADPCMStream os(pvOutBuffer, cbOutBuffer); // Output stream + TADPCMStream is(pvInBuffer, cbInBuffer); // Input stream + unsigned char EncodedSample; + unsigned char BitShift; + short PredictedSamples[MAX_ADPCM_CHANNEL_COUNT]; // Predicted sample for each channel + short StepIndexes[MAX_ADPCM_CHANNEL_COUNT]; // Predicted step index for each channel + int ChannelIndex; // Current channel index + + // Initialize the StepIndex for each channel + PredictedSamples[0] = PredictedSamples[1] = 0; + StepIndexes[0] = StepIndexes[1] = INITIAL_ADPCM_STEP_INDEX; + + // The first byte is always zero, the second one contains bit shift (compression level - 1) + is.ReadByteSample(BitShift); + is.ReadByteSample(BitShift); + + // Next, InitialSample value for each channel follows + for(int i = 0; i < ChannelCount; i++) + { + // Get the initial sample from the input stream + short InitialSample; + + // Attempt to read the initial sample + if(!is.ReadWordSample(InitialSample)) + return os.LengthProcessed(pvOutBuffer); + + // Store the initial sample to our sample array + PredictedSamples[i] = InitialSample; + + // Also store the loaded sample to the output stream + if(!os.WriteWordSample(InitialSample)) + return os.LengthProcessed(pvOutBuffer); + } + + // Get the initial index + ChannelIndex = ChannelCount - 1; + + // Keep reading as long as there is something in the input buffer + while(is.ReadByteSample(EncodedSample)) + { + // If we have two channels, we need to flip the channel index + ChannelIndex = (ChannelIndex + 1) % ChannelCount; + + if(EncodedSample == 0x80) + { + if(StepIndexes[ChannelIndex] != 0) + StepIndexes[ChannelIndex]--; + + if(!os.WriteWordSample(PredictedSamples[ChannelIndex])) + return os.LengthProcessed(pvOutBuffer); + } + else if(EncodedSample == 0x81) + { + // Modify the step index + StepIndexes[ChannelIndex] += 8; + if(StepIndexes[ChannelIndex] > 0x58) + StepIndexes[ChannelIndex] = 0x58; + + // Next pass, keep going on the same channel + ChannelIndex = (ChannelIndex + 1) % ChannelCount; + } + else + { + int StepIndex = StepIndexes[ChannelIndex]; + int StepSize = StepSizeTable[StepIndex]; + + // Encode one sample + PredictedSamples[ChannelIndex] = (short)DecodeSample(PredictedSamples[ChannelIndex], + EncodedSample, + StepSize, + StepSize >> BitShift); + + // Write the decoded sample to the output stream + if(!os.WriteWordSample(PredictedSamples[ChannelIndex])) + break; + + // Calculates the step index to use for the next encode + StepIndexes[ChannelIndex] = GetNextStepIndex(StepIndex, EncodedSample); + } + } + + // Return total bytes written since beginning of the output buffer + return os.LengthProcessed(pvOutBuffer); +} + +//----------------------------------------------------------------------------- +// ADPCM decompression present in Starcraft I BETA + +typedef struct _ADPCM_DATA +{ + const unsigned int * pValues; + int BitCount; + int field_8; + int field_C; + int field_10; + +} ADPCM_DATA, *PADPCM_DATA; + +static const unsigned int adpcm_values_2[] = {0x33, 0x66}; +static const unsigned int adpcm_values_3[] = {0x3A, 0x3A, 0x50, 0x70}; +static const unsigned int adpcm_values_4[] = {0x3A, 0x3A, 0x3A, 0x3A, 0x4D, 0x66, 0x80, 0x9A}; +static const unsigned int adpcm_values_6[] = +{ + 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, + 0x46, 0x53, 0x60, 0x6D, 0x7A, 0x86, 0x93, 0xA0, 0xAD, 0xBA, 0xC6, 0xD3, 0xE0, 0xED, 0xFA, 0x106 +}; + +static const unsigned int * InitAdpcmData(PADPCM_DATA pData, unsigned char BitCount) +{ + switch(BitCount) + { + case 2: + pData->pValues = adpcm_values_2; + break; + + case 3: + pData->pValues = adpcm_values_3; + break; + + case 4: + pData->pValues = adpcm_values_4; + break; + + default: + pData->pValues = NULL; + break; + + case 6: + pData->pValues = adpcm_values_6; + break; + } + + pData->BitCount = BitCount; + pData->field_C = 0x20000; + pData->field_8 = 1 << BitCount; + pData->field_10 = (1 << BitCount) / 2; + return pData->pValues; +} + +int DecompressADPCM_SC1B(void * pvOutBuffer, int cbOutBuffer, void * pvInBuffer, int cbInBuffer, int ChannelCount) +{ + TADPCMStream os(pvOutBuffer, cbOutBuffer); // Output stream + TADPCMStream is(pvInBuffer, cbInBuffer); // Input stream + ADPCM_DATA AdpcmData; + int LowBitValues[MAX_ADPCM_CHANNEL_COUNT]; + int UpperBits[MAX_ADPCM_CHANNEL_COUNT]; + int BitMasks[MAX_ADPCM_CHANNEL_COUNT]; + int PredictedSamples[MAX_ADPCM_CHANNEL_COUNT]; + int ChannelIndex; + int ChannelIndexMax; + int OutputSample; + unsigned char BitCount; + unsigned char EncodedSample; + short InputValue16; + int reg_eax; + int Difference; + + // The first byte contains number of bits + if(!is.ReadByteSample(BitCount)) + return os.LengthProcessed(pvOutBuffer); + if(!InitAdpcmData(&AdpcmData, BitCount)) + return os.LengthProcessed(pvOutBuffer); + assert(AdpcmData.pValues != NULL); + + // Init bit values + for(int i = 0; i < ChannelCount; i++) + { + unsigned char OneByte; + + if(!is.ReadByteSample(OneByte)) + return os.LengthProcessed(pvOutBuffer); + LowBitValues[i] = OneByte & 0x01; + UpperBits[i] = OneByte >> 1; + } + + // + for(int i = 0; i < ChannelCount; i++) + { + if(!is.ReadWordSample(InputValue16)) + return os.LengthProcessed(pvOutBuffer); + BitMasks[i] = InputValue16 << AdpcmData.BitCount; + } + + // Next, InitialSample value for each channel follows + for(int i = 0; i < ChannelCount; i++) + { + if(!is.ReadWordSample(InputValue16)) + return os.LengthProcessed(pvOutBuffer); + + PredictedSamples[i] = InputValue16; + os.WriteWordSample(InputValue16); + } + + // Get the initial index + ChannelIndexMax = ChannelCount - 1; + ChannelIndex = 0; + + // Keep reading as long as there is something in the input buffer + while(is.ReadByteSample(EncodedSample)) + { + reg_eax = ((PredictedSamples[ChannelIndex] * 3) << 3) - PredictedSamples[ChannelIndex]; + PredictedSamples[ChannelIndex] = ((reg_eax * 10) + 0x80) >> 8; + + Difference = (((EncodedSample >> 1) + 1) * BitMasks[ChannelIndex] + AdpcmData.field_10) >> AdpcmData.BitCount; + + PredictedSamples[ChannelIndex] = UpdatePredictedSample(PredictedSamples[ChannelIndex], EncodedSample, Difference, 0x01); + + BitMasks[ChannelIndex] = (AdpcmData.pValues[EncodedSample >> 1] * BitMasks[ChannelIndex] + 0x80) >> 6; + if(BitMasks[ChannelIndex] < AdpcmData.field_8) + BitMasks[ChannelIndex] = AdpcmData.field_8; + + if(BitMasks[ChannelIndex] > AdpcmData.field_C) + BitMasks[ChannelIndex] = AdpcmData.field_C; + + reg_eax = (cbInBuffer - is.LengthProcessed(pvInBuffer)) >> ChannelIndexMax; + OutputSample = PredictedSamples[ChannelIndex]; + if(reg_eax < UpperBits[ChannelIndex]) + { + if(LowBitValues[ChannelIndex]) + { + OutputSample += (UpperBits[ChannelIndex] - reg_eax); + if(OutputSample > 32767) + OutputSample = 32767; } else { - temp3 = temp3 + temp2; - if(temp3 >= 32767) - temp3 = 32767; + OutputSample += (reg_eax - UpperBits[ChannelIndex]); + if(OutputSample < -32768) + OutputSample = -32768; } - - SInt32Array2[nIndex] = temp3; - if(dwOutLength < 2) - break; - - // Store the output 16-bit value - *out.pw++ = BSWAP_INT16_SIGNED((short)SInt32Array2[nIndex]); - dwOutLength -= 2; - - SInt32Array1[nIndex] += Table1503F120[nOneByte & 0x1F]; - - if(SInt32Array1[nIndex] < 0) - SInt32Array1[nIndex] = 0; - else if(SInt32Array1[nIndex] > 0x58) - SInt32Array1[nIndex] = 0x58; } + + // Write the word sample and swap channel + os.WriteWordSample((short)(OutputSample)); + ChannelIndex = (ChannelIndex + 1) % ChannelCount; } - return (int)(out.pb - pbOutBuffer); + + return os.LengthProcessed(pvOutBuffer); } + diff --git a/dep/StormLib/src/adpcm/adpcm.h b/dep/StormLib/src/adpcm/adpcm.h index beb9615972..169d4658c1 100644 --- a/dep/StormLib/src/adpcm/adpcm.h +++ b/dep/StormLib/src/adpcm/adpcm.h @@ -12,11 +12,16 @@ #define __ADPCM_H__ //----------------------------------------------------------------------------- -// Functions +// Defines -#include "../StormPort.h" +#define MAX_ADPCM_CHANNEL_COUNT 2 +#define INITIAL_ADPCM_STEP_INDEX 0x2C -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); +//----------------------------------------------------------------------------- +// Public functions + +int CompressADPCM (void * pvOutBuffer, int dwOutLength, void * pvInBuffer, int dwInLength, int ChannelCount, int nCmpType); +int DecompressADPCM (void * pvOutBuffer, int dwOutLength, void * pvInBuffer, int dwInLength, int ChannelCount); +int DecompressADPCM_SC1B(void * pvOutBuffer, int cbOutBuffer, void * pvInBuffer, int cbInBuffer, int ChannelCount); #endif // __ADPCM_H__ diff --git a/dep/StormLib/src/bzip2/blocksort.c b/dep/StormLib/src/bzip2/blocksort.c new file mode 100644 index 0000000000..bd2dec157f --- /dev/null +++ b/dep/StormLib/src/bzip2/blocksort.c @@ -0,0 +1,1094 @@ + +/*-------------------------------------------------------------*/ +/*--- Block sorting machinery ---*/ +/*--- blocksort.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.5 of 10 December 2007 + Copyright (C) 1996-2007 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + +/*---------------------------------------------*/ +/*--- Fallback O(N log(N)^2) sorting ---*/ +/*--- algorithm, for repetitive blocks ---*/ +/*---------------------------------------------*/ + +/*---------------------------------------------*/ +static +__inline__ +void fallbackSimpleSort ( UInt32* fmap, + UInt32* eclass, + Int32 lo, + Int32 hi ) +{ + Int32 i, j, tmp; + UInt32 ec_tmp; + + if (lo == hi) return; + + if (hi - lo > 3) { + for ( i = hi-4; i >= lo; i-- ) { + tmp = fmap[i]; + ec_tmp = eclass[tmp]; + for ( j = i+4; j <= hi && ec_tmp > eclass[fmap[j]]; j += 4 ) + fmap[j-4] = fmap[j]; + fmap[j-4] = tmp; + } + } + + for ( i = hi-1; i >= lo; i-- ) { + tmp = fmap[i]; + ec_tmp = eclass[tmp]; + for ( j = i+1; j <= hi && ec_tmp > eclass[fmap[j]]; j++ ) + fmap[j-1] = fmap[j]; + fmap[j-1] = tmp; + } +} + + +/*---------------------------------------------*/ +#define fswap(zz1, zz2) \ + { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; } + +#define fvswap(zzp1, zzp2, zzn) \ +{ \ + Int32 yyp1 = (zzp1); \ + Int32 yyp2 = (zzp2); \ + Int32 yyn = (zzn); \ + while (yyn > 0) { \ + fswap(fmap[yyp1], fmap[yyp2]); \ + yyp1++; yyp2++; yyn--; \ + } \ +} + + +#define fmin(a,b) ((a) < (b)) ? (a) : (b) + +#define fpush(lz,hz) { stackLo[sp] = lz; \ + stackHi[sp] = hz; \ + sp++; } + +#define fpop(lz,hz) { sp--; \ + lz = stackLo[sp]; \ + hz = stackHi[sp]; } + +#define FALLBACK_QSORT_SMALL_THRESH 10 +#define FALLBACK_QSORT_STACK_SIZE 100 + + +static +void fallbackQSort3 ( UInt32* fmap, + UInt32* eclass, + Int32 loSt, + Int32 hiSt ) +{ + Int32 unLo, unHi, ltLo, gtHi, n, m; + Int32 sp, lo, hi; + UInt32 med, r, r3; + Int32 stackLo[FALLBACK_QSORT_STACK_SIZE]; + Int32 stackHi[FALLBACK_QSORT_STACK_SIZE]; + + r = 0; + + sp = 0; + fpush ( loSt, hiSt ); + + while (sp > 0) { + + AssertH ( sp < FALLBACK_QSORT_STACK_SIZE - 1, 1004 ); + + fpop ( lo, hi ); + if (hi - lo < FALLBACK_QSORT_SMALL_THRESH) { + fallbackSimpleSort ( fmap, eclass, lo, hi ); + continue; + } + + /* Random partitioning. Median of 3 sometimes fails to + avoid bad cases. Median of 9 seems to help but + looks rather expensive. This too seems to work but + is cheaper. Guidance for the magic constants + 7621 and 32768 is taken from Sedgewick's algorithms + book, chapter 35. + */ + r = ((r * 7621) + 1) % 32768; + r3 = r % 3; + if (r3 == 0) med = eclass[fmap[lo]]; else + if (r3 == 1) med = eclass[fmap[(lo+hi)>>1]]; else + med = eclass[fmap[hi]]; + + unLo = ltLo = lo; + unHi = gtHi = hi; + + while (1) { + while (1) { + if (unLo > unHi) break; + n = (Int32)eclass[fmap[unLo]] - (Int32)med; + if (n == 0) { + fswap(fmap[unLo], fmap[ltLo]); + ltLo++; unLo++; + continue; + }; + if (n > 0) break; + unLo++; + } + while (1) { + if (unLo > unHi) break; + n = (Int32)eclass[fmap[unHi]] - (Int32)med; + if (n == 0) { + fswap(fmap[unHi], fmap[gtHi]); + gtHi--; unHi--; + continue; + }; + if (n < 0) break; + unHi--; + } + if (unLo > unHi) break; + fswap(fmap[unLo], fmap[unHi]); unLo++; unHi--; + } + + AssertD ( unHi == unLo-1, "fallbackQSort3(2)" ); + + if (gtHi < ltLo) continue; + + n = fmin(ltLo-lo, unLo-ltLo); fvswap(lo, unLo-n, n); + m = fmin(hi-gtHi, gtHi-unHi); fvswap(unLo, hi-m+1, m); + + n = lo + unLo - ltLo - 1; + m = hi - (gtHi - unHi) + 1; + + if (n - lo > hi - m) { + fpush ( lo, n ); + fpush ( m, hi ); + } else { + fpush ( m, hi ); + fpush ( lo, n ); + } + } +} + +#undef fmin +#undef fpush +#undef fpop +#undef fswap +#undef fvswap +#undef FALLBACK_QSORT_SMALL_THRESH +#undef FALLBACK_QSORT_STACK_SIZE + + +/*---------------------------------------------*/ +/* Pre: + nblock > 0 + eclass exists for [0 .. nblock-1] + ((UChar*)eclass) [0 .. nblock-1] holds block + ptr exists for [0 .. nblock-1] + + Post: + ((UChar*)eclass) [0 .. nblock-1] holds block + All other areas of eclass destroyed + fmap [0 .. nblock-1] holds sorted order + bhtab [ 0 .. 2+(nblock/32) ] destroyed +*/ + +#define SET_BH(zz) bhtab[(zz) >> 5] |= (1 << ((zz) & 31)) +#define CLEAR_BH(zz) bhtab[(zz) >> 5] &= ~(1 << ((zz) & 31)) +#define ISSET_BH(zz) (bhtab[(zz) >> 5] & (1 << ((zz) & 31))) +#define WORD_BH(zz) bhtab[(zz) >> 5] +#define UNALIGNED_BH(zz) ((zz) & 0x01f) + +static +void fallbackSort ( UInt32* fmap, + UInt32* eclass, + UInt32* bhtab, + Int32 nblock, + Int32 verb ) +{ + Int32 ftab[257]; + Int32 ftabCopy[256]; + Int32 H, i, j, k, l, r, cc, cc1; + Int32 nNotDone; + Int32 nBhtab; + UChar* eclass8 = (UChar*)eclass; + + /*-- + Initial 1-char radix sort to generate + initial fmap and initial BH bits. + --*/ + if (verb >= 4) + VPrintf0 ( " bucket sorting ...\n" ); + for (i = 0; i < 257; i++) ftab[i] = 0; + for (i = 0; i < nblock; i++) ftab[eclass8[i]]++; + for (i = 0; i < 256; i++) ftabCopy[i] = ftab[i]; + for (i = 1; i < 257; i++) ftab[i] += ftab[i-1]; + + for (i = 0; i < nblock; i++) { + j = eclass8[i]; + k = ftab[j] - 1; + ftab[j] = k; + fmap[k] = i; + } + + nBhtab = 2 + (nblock / 32); + for (i = 0; i < nBhtab; i++) bhtab[i] = 0; + for (i = 0; i < 256; i++) SET_BH(ftab[i]); + + /*-- + Inductively refine the buckets. Kind-of an + "exponential radix sort" (!), inspired by the + Manber-Myers suffix array construction algorithm. + --*/ + + /*-- set sentinel bits for block-end detection --*/ + for (i = 0; i < 32; i++) { + SET_BH(nblock + 2*i); + CLEAR_BH(nblock + 2*i + 1); + } + + /*-- the log(N) loop --*/ + H = 1; + while (1) { + + if (verb >= 4) + VPrintf1 ( " depth %6d has ", H ); + + j = 0; + for (i = 0; i < nblock; i++) { + if (ISSET_BH(i)) j = i; + k = fmap[i] - H; if (k < 0) k += nblock; + eclass[k] = j; + } + + nNotDone = 0; + r = -1; + while (1) { + + /*-- find the next non-singleton bucket --*/ + k = r + 1; + while (ISSET_BH(k) && UNALIGNED_BH(k)) k++; + if (ISSET_BH(k)) { + while (WORD_BH(k) == 0xffffffff) k += 32; + while (ISSET_BH(k)) k++; + } + l = k - 1; + if (l >= nblock) break; + while (!ISSET_BH(k) && UNALIGNED_BH(k)) k++; + if (!ISSET_BH(k)) { + while (WORD_BH(k) == 0x00000000) k += 32; + while (!ISSET_BH(k)) k++; + } + r = k - 1; + if (r >= nblock) break; + + /*-- now [l, r] bracket current bucket --*/ + if (r > l) { + nNotDone += (r - l + 1); + fallbackQSort3 ( fmap, eclass, l, r ); + + /*-- scan bucket and generate header bits-- */ + cc = -1; + for (i = l; i <= r; i++) { + cc1 = eclass[fmap[i]]; + if (cc != cc1) { SET_BH(i); cc = cc1; }; + } + } + } + + if (verb >= 4) + VPrintf1 ( "%6d unresolved strings\n", nNotDone ); + + H *= 2; + if (H > nblock || nNotDone == 0) break; + } + + /*-- + Reconstruct the original block in + eclass8 [0 .. nblock-1], since the + previous phase destroyed it. + --*/ + if (verb >= 4) + VPrintf0 ( " reconstructing block ...\n" ); + j = 0; + for (i = 0; i < nblock; i++) { + while (ftabCopy[j] == 0) j++; + ftabCopy[j]--; + eclass8[fmap[i]] = (UChar)j; + } + AssertH ( j < 256, 1005 ); +} + +#undef SET_BH +#undef CLEAR_BH +#undef ISSET_BH +#undef WORD_BH +#undef UNALIGNED_BH + + +/*---------------------------------------------*/ +/*--- The main, O(N^2 log(N)) sorting ---*/ +/*--- algorithm. Faster for "normal" ---*/ +/*--- non-repetitive blocks. ---*/ +/*---------------------------------------------*/ + +/*---------------------------------------------*/ +static +__inline__ +Bool mainGtU ( UInt32 i1, + UInt32 i2, + UChar* block, + UInt16* quadrant, + UInt32 nblock, + Int32* budget ) +{ + Int32 k; + UChar c1, c2; + UInt16 s1, s2; + + AssertD ( i1 != i2, "mainGtU" ); + /* 1 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 2 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 3 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 4 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 5 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 6 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 7 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 8 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 9 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 10 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 11 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 12 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + + k = nblock + 8; + + do { + /* 1 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 2 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 3 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 4 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 5 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 6 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 7 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 8 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + + if (i1 >= nblock) i1 -= nblock; + if (i2 >= nblock) i2 -= nblock; + + k -= 8; + (*budget)--; + } + while (k >= 0); + + return False; +} + + +/*---------------------------------------------*/ +/*-- + Knuth's increments seem to work better + than Incerpi-Sedgewick here. Possibly + because the number of elems to sort is + usually small, typically <= 20. +--*/ +static +Int32 incs[14] = { 1, 4, 13, 40, 121, 364, 1093, 3280, + 9841, 29524, 88573, 265720, + 797161, 2391484 }; + +static +void mainSimpleSort ( UInt32* ptr, + UChar* block, + UInt16* quadrant, + Int32 nblock, + Int32 lo, + Int32 hi, + Int32 d, + Int32* budget ) +{ + Int32 i, j, h, bigN, hp; + UInt32 v; + + bigN = hi - lo + 1; + if (bigN < 2) return; + + hp = 0; + while (incs[hp] < bigN) hp++; + hp--; + + for (; hp >= 0; hp--) { + h = incs[hp]; + + i = lo + h; + while (True) { + + /*-- copy 1 --*/ + if (i > hi) break; + v = ptr[i]; + j = i; + while ( mainGtU ( + ptr[j-h]+d, v+d, block, quadrant, nblock, budget + ) ) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; + + /*-- copy 2 --*/ + if (i > hi) break; + v = ptr[i]; + j = i; + while ( mainGtU ( + ptr[j-h]+d, v+d, block, quadrant, nblock, budget + ) ) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; + + /*-- copy 3 --*/ + if (i > hi) break; + v = ptr[i]; + j = i; + while ( mainGtU ( + ptr[j-h]+d, v+d, block, quadrant, nblock, budget + ) ) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; + + if (*budget < 0) return; + } + } +} + + +/*---------------------------------------------*/ +/*-- + The following is an implementation of + an elegant 3-way quicksort for strings, + described in a paper "Fast Algorithms for + Sorting and Searching Strings", by Robert + Sedgewick and Jon L. Bentley. +--*/ + +#define mswap(zz1, zz2) \ + { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; } + +#define mvswap(zzp1, zzp2, zzn) \ +{ \ + Int32 yyp1 = (zzp1); \ + Int32 yyp2 = (zzp2); \ + Int32 yyn = (zzn); \ + while (yyn > 0) { \ + mswap(ptr[yyp1], ptr[yyp2]); \ + yyp1++; yyp2++; yyn--; \ + } \ +} + +static +__inline__ +UChar mmed3 ( UChar a, UChar b, UChar c ) +{ + UChar t; + if (a > b) { t = a; a = b; b = t; }; + if (b > c) { + b = c; + if (a > b) b = a; + } + return b; +} + +#define mmin(a,b) ((a) < (b)) ? (a) : (b) + +#define mpush(lz,hz,dz) { stackLo[sp] = lz; \ + stackHi[sp] = hz; \ + stackD [sp] = dz; \ + sp++; } + +#define mpop(lz,hz,dz) { sp--; \ + lz = stackLo[sp]; \ + hz = stackHi[sp]; \ + dz = stackD [sp]; } + + +#define mnextsize(az) (nextHi[az]-nextLo[az]) + +#define mnextswap(az,bz) \ + { Int32 tz; \ + tz = nextLo[az]; nextLo[az] = nextLo[bz]; nextLo[bz] = tz; \ + tz = nextHi[az]; nextHi[az] = nextHi[bz]; nextHi[bz] = tz; \ + tz = nextD [az]; nextD [az] = nextD [bz]; nextD [bz] = tz; } + + +#define MAIN_QSORT_SMALL_THRESH 20 +#define MAIN_QSORT_DEPTH_THRESH (BZ_N_RADIX + BZ_N_QSORT) +#define MAIN_QSORT_STACK_SIZE 100 + +static +void mainQSort3 ( UInt32* ptr, + UChar* block, + UInt16* quadrant, + Int32 nblock, + Int32 loSt, + Int32 hiSt, + Int32 dSt, + Int32* budget ) +{ + Int32 unLo, unHi, ltLo, gtHi, n, m, med; + Int32 sp, lo, hi, d; + + Int32 stackLo[MAIN_QSORT_STACK_SIZE]; + Int32 stackHi[MAIN_QSORT_STACK_SIZE]; + Int32 stackD [MAIN_QSORT_STACK_SIZE]; + + Int32 nextLo[3]; + Int32 nextHi[3]; + Int32 nextD [3]; + + sp = 0; + mpush ( loSt, hiSt, dSt ); + + while (sp > 0) { + + AssertH ( sp < MAIN_QSORT_STACK_SIZE - 2, 1001 ); + + mpop ( lo, hi, d ); + if (hi - lo < MAIN_QSORT_SMALL_THRESH || + d > MAIN_QSORT_DEPTH_THRESH) { + mainSimpleSort ( ptr, block, quadrant, nblock, lo, hi, d, budget ); + if (*budget < 0) return; + continue; + } + + med = (Int32) + mmed3 ( block[ptr[ lo ]+d], + block[ptr[ hi ]+d], + block[ptr[ (lo+hi)>>1 ]+d] ); + + unLo = ltLo = lo; + unHi = gtHi = hi; + + while (True) { + while (True) { + if (unLo > unHi) break; + n = ((Int32)block[ptr[unLo]+d]) - med; + if (n == 0) { + mswap(ptr[unLo], ptr[ltLo]); + ltLo++; unLo++; continue; + }; + if (n > 0) break; + unLo++; + } + while (True) { + if (unLo > unHi) break; + n = ((Int32)block[ptr[unHi]+d]) - med; + if (n == 0) { + mswap(ptr[unHi], ptr[gtHi]); + gtHi--; unHi--; continue; + }; + if (n < 0) break; + unHi--; + } + if (unLo > unHi) break; + mswap(ptr[unLo], ptr[unHi]); unLo++; unHi--; + } + + AssertD ( unHi == unLo-1, "mainQSort3(2)" ); + + if (gtHi < ltLo) { + mpush(lo, hi, d+1 ); + continue; + } + + n = mmin(ltLo-lo, unLo-ltLo); mvswap(lo, unLo-n, n); + m = mmin(hi-gtHi, gtHi-unHi); mvswap(unLo, hi-m+1, m); + + n = lo + unLo - ltLo - 1; + m = hi - (gtHi - unHi) + 1; + + nextLo[0] = lo; nextHi[0] = n; nextD[0] = d; + nextLo[1] = m; nextHi[1] = hi; nextD[1] = d; + nextLo[2] = n+1; nextHi[2] = m-1; nextD[2] = d+1; + + if (mnextsize(0) < mnextsize(1)) mnextswap(0,1); + if (mnextsize(1) < mnextsize(2)) mnextswap(1,2); + if (mnextsize(0) < mnextsize(1)) mnextswap(0,1); + + AssertD (mnextsize(0) >= mnextsize(1), "mainQSort3(8)" ); + AssertD (mnextsize(1) >= mnextsize(2), "mainQSort3(9)" ); + + mpush (nextLo[0], nextHi[0], nextD[0]); + mpush (nextLo[1], nextHi[1], nextD[1]); + mpush (nextLo[2], nextHi[2], nextD[2]); + } +} + +#undef mswap +#undef mvswap +#undef mpush +#undef mpop +#undef mmin +#undef mnextsize +#undef mnextswap +#undef MAIN_QSORT_SMALL_THRESH +#undef MAIN_QSORT_DEPTH_THRESH +#undef MAIN_QSORT_STACK_SIZE + + +/*---------------------------------------------*/ +/* Pre: + nblock > N_OVERSHOOT + block32 exists for [0 .. nblock-1 +N_OVERSHOOT] + ((UChar*)block32) [0 .. nblock-1] holds block + ptr exists for [0 .. nblock-1] + + Post: + ((UChar*)block32) [0 .. nblock-1] holds block + All other areas of block32 destroyed + ftab [0 .. 65536 ] destroyed + ptr [0 .. nblock-1] holds sorted order + if (*budget < 0), sorting was abandoned +*/ + +#define BIGFREQ(b) (ftab[((b)+1) << 8] - ftab[(b) << 8]) +#define SETMASK (1 << 21) +#define CLEARMASK (~(SETMASK)) + +static +void mainSort ( UInt32* ptr, + UChar* block, + UInt16* quadrant, + UInt32* ftab, + Int32 nblock, + Int32 verb, + Int32* budget ) +{ + Int32 i, j, k, ss, sb; + Int32 runningOrder[256]; + Bool bigDone[256]; + Int32 copyStart[256]; + Int32 copyEnd [256]; + UChar c1; + Int32 numQSorted; + UInt16 s; + if (verb >= 4) VPrintf0 ( " main sort initialise ...\n" ); + + /*-- set up the 2-byte frequency table --*/ + for (i = 65536; i >= 0; i--) ftab[i] = 0; + + j = block[0] << 8; + i = nblock-1; + for (; i >= 3; i -= 4) { + quadrant[i] = 0; + j = (j >> 8) | ( ((UInt16)block[i]) << 8); + ftab[j]++; + quadrant[i-1] = 0; + j = (j >> 8) | ( ((UInt16)block[i-1]) << 8); + ftab[j]++; + quadrant[i-2] = 0; + j = (j >> 8) | ( ((UInt16)block[i-2]) << 8); + ftab[j]++; + quadrant[i-3] = 0; + j = (j >> 8) | ( ((UInt16)block[i-3]) << 8); + ftab[j]++; + } + for (; i >= 0; i--) { + quadrant[i] = 0; + j = (j >> 8) | ( ((UInt16)block[i]) << 8); + ftab[j]++; + } + + /*-- (emphasises close relationship of block & quadrant) --*/ + for (i = 0; i < BZ_N_OVERSHOOT; i++) { + block [nblock+i] = block[i]; + quadrant[nblock+i] = 0; + } + + if (verb >= 4) VPrintf0 ( " bucket sorting ...\n" ); + + /*-- Complete the initial radix sort --*/ + for (i = 1; i <= 65536; i++) ftab[i] += ftab[i-1]; + + s = block[0] << 8; + i = nblock-1; + for (; i >= 3; i -= 4) { + s = (s >> 8) | (block[i] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i; + s = (s >> 8) | (block[i-1] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i-1; + s = (s >> 8) | (block[i-2] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i-2; + s = (s >> 8) | (block[i-3] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i-3; + } + for (; i >= 0; i--) { + s = (s >> 8) | (block[i] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i; + } + + /*-- + Now ftab contains the first loc of every small bucket. + Calculate the running order, from smallest to largest + big bucket. + --*/ + for (i = 0; i <= 255; i++) { + bigDone [i] = False; + runningOrder[i] = i; + } + + { + Int32 vv; + Int32 h = 1; + do h = 3 * h + 1; while (h <= 256); + do { + h = h / 3; + for (i = h; i <= 255; i++) { + vv = runningOrder[i]; + j = i; + while ( BIGFREQ(runningOrder[j-h]) > BIGFREQ(vv) ) { + runningOrder[j] = runningOrder[j-h]; + j = j - h; + if (j <= (h - 1)) goto zero; + } + zero: + runningOrder[j] = vv; + } + } while (h != 1); + } + + /*-- + The main sorting loop. + --*/ + + numQSorted = 0; + + for (i = 0; i <= 255; i++) { + + /*-- + Process big buckets, starting with the least full. + Basically this is a 3-step process in which we call + mainQSort3 to sort the small buckets [ss, j], but + also make a big effort to avoid the calls if we can. + --*/ + ss = runningOrder[i]; + + /*-- + Step 1: + Complete the big bucket [ss] by quicksorting + any unsorted small buckets [ss, j], for j != ss. + Hopefully previous pointer-scanning phases have already + completed many of the small buckets [ss, j], so + we don't have to sort them at all. + --*/ + for (j = 0; j <= 255; j++) { + if (j != ss) { + sb = (ss << 8) + j; + if ( ! (ftab[sb] & SETMASK) ) { + Int32 lo = ftab[sb] & CLEARMASK; + Int32 hi = (ftab[sb+1] & CLEARMASK) - 1; + if (hi > lo) { + if (verb >= 4) + VPrintf4 ( " qsort [0x%x, 0x%x] " + "done %d this %d\n", + ss, j, numQSorted, hi - lo + 1 ); + mainQSort3 ( + ptr, block, quadrant, nblock, + lo, hi, BZ_N_RADIX, budget + ); + numQSorted += (hi - lo + 1); + if (*budget < 0) return; + } + } + ftab[sb] |= SETMASK; + } + } + + AssertH ( !bigDone[ss], 1006 ); + + /*-- + Step 2: + Now scan this big bucket [ss] so as to synthesise the + sorted order for small buckets [t, ss] for all t, + including, magically, the bucket [ss,ss] too. + This will avoid doing Real Work in subsequent Step 1's. + --*/ + { + for (j = 0; j <= 255; j++) { + copyStart[j] = ftab[(j << 8) + ss] & CLEARMASK; + copyEnd [j] = (ftab[(j << 8) + ss + 1] & CLEARMASK) - 1; + } + for (j = ftab[ss << 8] & CLEARMASK; j < copyStart[ss]; j++) { + k = ptr[j]-1; if (k < 0) k += nblock; + c1 = block[k]; + if (!bigDone[c1]) + ptr[ copyStart[c1]++ ] = k; + } + for (j = (ftab[(ss+1) << 8] & CLEARMASK) - 1; j > copyEnd[ss]; j--) { + k = ptr[j]-1; if (k < 0) k += nblock; + c1 = block[k]; + if (!bigDone[c1]) + ptr[ copyEnd[c1]-- ] = k; + } + } + + AssertH ( (copyStart[ss]-1 == copyEnd[ss]) + || + /* Extremely rare case missing in bzip2-1.0.0 and 1.0.1. + Necessity for this case is demonstrated by compressing + a sequence of approximately 48.5 million of character + 251; 1.0.0/1.0.1 will then die here. */ + (copyStart[ss] == 0 && copyEnd[ss] == nblock-1), + 1007 ) + + for (j = 0; j <= 255; j++) ftab[(j << 8) + ss] |= SETMASK; + + /*-- + Step 3: + The [ss] big bucket is now done. Record this fact, + and update the quadrant descriptors. Remember to + update quadrants in the overshoot area too, if + necessary. The "if (i < 255)" test merely skips + this updating for the last bucket processed, since + updating for the last bucket is pointless. + + The quadrant array provides a way to incrementally + cache sort orderings, as they appear, so as to + make subsequent comparisons in fullGtU() complete + faster. For repetitive blocks this makes a big + difference (but not big enough to be able to avoid + the fallback sorting mechanism, exponential radix sort). + + The precise meaning is: at all times: + + for 0 <= i < nblock and 0 <= j <= nblock + + if block[i] != block[j], + + then the relative values of quadrant[i] and + quadrant[j] are meaningless. + + else { + if quadrant[i] < quadrant[j] + then the string starting at i lexicographically + precedes the string starting at j + + else if quadrant[i] > quadrant[j] + then the string starting at j lexicographically + precedes the string starting at i + + else + the relative ordering of the strings starting + at i and j has not yet been determined. + } + --*/ + bigDone[ss] = True; + + if (i < 255) { + Int32 bbStart = ftab[ss << 8] & CLEARMASK; + Int32 bbSize = (ftab[(ss+1) << 8] & CLEARMASK) - bbStart; + Int32 shifts = 0; + + while ((bbSize >> shifts) > 65534) shifts++; + + for (j = bbSize-1; j >= 0; j--) { + Int32 a2update = ptr[bbStart + j]; + UInt16 qVal = (UInt16)(j >> shifts); + quadrant[a2update] = qVal; + if (a2update < BZ_N_OVERSHOOT) + quadrant[a2update + nblock] = qVal; + } + AssertH ( ((bbSize-1) >> shifts) <= 65535, 1002 ); + } + + } + + if (verb >= 4) + VPrintf3 ( " %d pointers, %d sorted, %d scanned\n", + nblock, numQSorted, nblock - numQSorted ); +} + +#undef BIGFREQ +#undef SETMASK +#undef CLEARMASK + + +/*---------------------------------------------*/ +/* Pre: + nblock > 0 + arr2 exists for [0 .. nblock-1 +N_OVERSHOOT] + ((UChar*)arr2) [0 .. nblock-1] holds block + arr1 exists for [0 .. nblock-1] + + Post: + ((UChar*)arr2) [0 .. nblock-1] holds block + All other areas of block destroyed + ftab [ 0 .. 65536 ] destroyed + arr1 [0 .. nblock-1] holds sorted order +*/ +void BZ2_blockSort ( EState* s ) +{ + UInt32* ptr = s->ptr; + UChar* block = s->block; + UInt32* ftab = s->ftab; + Int32 nblock = s->nblock; + Int32 verb = s->verbosity; + Int32 wfact = s->workFactor; + UInt16* quadrant; + Int32 budget; + Int32 budgetInit; + Int32 i; + + if (nblock < 10000) { + fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb ); + } else { + /* Calculate the location for quadrant, remembering to get + the alignment right. Assumes that &(block[0]) is at least + 2-byte aligned -- this should be ok since block is really + the first section of arr2. + */ + i = nblock+BZ_N_OVERSHOOT; + if (i & 1) i++; + quadrant = (UInt16*)(&(block[i])); + + /* (wfact-1) / 3 puts the default-factor-30 + transition point at very roughly the same place as + with v0.1 and v0.9.0. + Not that it particularly matters any more, since the + resulting compressed stream is now the same regardless + of whether or not we use the main sort or fallback sort. + */ + if (wfact < 1 ) wfact = 1; + if (wfact > 100) wfact = 100; + budgetInit = nblock * ((wfact-1) / 3); + budget = budgetInit; + + mainSort ( ptr, block, quadrant, ftab, nblock, verb, &budget ); + if (verb >= 3) + VPrintf3 ( " %d work, %d block, ratio %5.2f\n", + budgetInit - budget, + nblock, + (float)(budgetInit - budget) / + (float)(nblock==0 ? 1 : nblock) ); + if (budget < 0) { + if (verb >= 2) + VPrintf0 ( " too repetitive; using fallback" + " sorting algorithm\n" ); + fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb ); + } + } + + s->origPtr = -1; + for (i = 0; i < s->nblock; i++) + if (ptr[i] == 0) + { s->origPtr = i; break; }; + + AssertH( s->origPtr != -1, 1003 ); +} + + +/*-------------------------------------------------------------*/ +/*--- end blocksort.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/dep/StormLib/src/bzip2/bzlib.c b/dep/StormLib/src/bzip2/bzlib.c new file mode 100644 index 0000000000..b98f3e586b --- /dev/null +++ b/dep/StormLib/src/bzip2/bzlib.c @@ -0,0 +1,1573 @@ + +/*-------------------------------------------------------------*/ +/*--- Library top-level functions. ---*/ +/*--- bzlib.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.5 of 10 December 2007 + Copyright (C) 1996-2007 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + +/* CHANGES + 0.9.0 -- original version. + 0.9.0a/b -- no changes in this file. + 0.9.0c -- made zero-length BZ_FLUSH work correctly in bzCompress(). + fixed bzWrite/bzRead to ignore zero-length requests. + fixed bzread to correctly handle read requests after EOF. + wrong parameter order in call to bzDecompressInit in + bzBuffToBuffDecompress. Fixed. +*/ + +#define _CRT_SECURE_NO_WARNINGS +#include "bzlib_private.h" + + +/*---------------------------------------------------*/ +/*--- Compression stuff ---*/ +/*---------------------------------------------------*/ + + +/*---------------------------------------------------*/ +#ifndef BZ_NO_STDIO +void BZ2_bz__AssertH__fail ( int errcode ) +{ + fprintf(stderr, + "\n\nbzip2/libbzip2: internal error number %d.\n" + "This is a bug in bzip2/libbzip2, %s.\n" + "Please report it to me at: jseward@bzip.org. If this happened\n" + "when you were using some program which uses libbzip2 as a\n" + "component, you should also report this bug to the author(s)\n" + "of that program. Please make an effort to report this bug;\n" + "timely and accurate bug reports eventually lead to higher\n" + "quality software. Thanks. Julian Seward, 10 December 2007.\n\n", + errcode, + BZ2_bzlibVersion() + ); + + if (errcode == 1007) { + fprintf(stderr, + "\n*** A special note about internal error number 1007 ***\n" + "\n" + "Experience suggests that a common cause of i.e. 1007\n" + "is unreliable memory or other hardware. The 1007 assertion\n" + "just happens to cross-check the results of huge numbers of\n" + "memory reads/writes, and so acts (unintendedly) as a stress\n" + "test of your memory system.\n" + "\n" + "I suggest the following: try compressing the file again,\n" + "possibly monitoring progress in detail with the -vv flag.\n" + "\n" + "* If the error cannot be reproduced, and/or happens at different\n" + " points in compression, you may have a flaky memory system.\n" + " Try a memory-test program. I have used Memtest86\n" + " (www.memtest86.com). At the time of writing it is free (GPLd).\n" + " Memtest86 tests memory much more thorougly than your BIOSs\n" + " power-on test, and may find failures that the BIOS doesn't.\n" + "\n" + "* If the error can be repeatably reproduced, this is a bug in\n" + " bzip2, and I would very much like to hear about it. Please\n" + " let me know, and, ideally, save a copy of the file causing the\n" + " problem -- without which I will be unable to investigate it.\n" + "\n" + ); + } + + exit(3); +} +#endif + + +/*---------------------------------------------------*/ +static +int bz_config_ok ( void ) +{ + if (sizeof(int) != 4) return 0; + if (sizeof(short) != 2) return 0; + if (sizeof(char) != 1) return 0; + return 1; +} + + +/*---------------------------------------------------*/ +static +void* default_bzalloc ( void* opaque, Int32 items, Int32 size ) +{ + void* v = malloc ( items * size ); + return v; +} + +static +void default_bzfree ( void* opaque, void* addr ) +{ + if (addr != NULL) free ( addr ); +} + + +/*---------------------------------------------------*/ +static +void prepare_new_block ( EState* s ) +{ + Int32 i; + s->nblock = 0; + s->numZ = 0; + s->state_out_pos = 0; + BZ_INITIALISE_CRC ( s->blockCRC ); + for (i = 0; i < 256; i++) s->inUse[i] = False; + s->blockNo++; +} + + +/*---------------------------------------------------*/ +static +void init_RL ( EState* s ) +{ + s->state_in_ch = 256; + s->state_in_len = 0; +} + + +static +Bool isempty_RL ( EState* s ) +{ + if (s->state_in_ch < 256 && s->state_in_len > 0) + return False; else + return True; +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzCompressInit) + ( bz_stream* strm, + int blockSize100k, + int verbosity, + int workFactor ) +{ + Int32 n; + EState* s; + + if (!bz_config_ok()) return BZ_CONFIG_ERROR; + + if (strm == NULL || + blockSize100k < 1 || blockSize100k > 9 || + workFactor < 0 || workFactor > 250) + return BZ_PARAM_ERROR; + + if (workFactor == 0) workFactor = 30; + if (strm->bzalloc == NULL) strm->bzalloc = default_bzalloc; + if (strm->bzfree == NULL) strm->bzfree = default_bzfree; + + s = BZALLOC( sizeof(EState) ); + if (s == NULL) return BZ_MEM_ERROR; + s->strm = strm; + + s->arr1 = NULL; + s->arr2 = NULL; + s->ftab = NULL; + + n = 100000 * blockSize100k; + s->arr1 = BZALLOC( n * sizeof(UInt32) ); + s->arr2 = BZALLOC( (n+BZ_N_OVERSHOOT) * sizeof(UInt32) ); + s->ftab = BZALLOC( 65537 * sizeof(UInt32) ); + + if (s->arr1 == NULL || s->arr2 == NULL || s->ftab == NULL) { + if (s->arr1 != NULL) BZFREE(s->arr1); + if (s->arr2 != NULL) BZFREE(s->arr2); + if (s->ftab != NULL) BZFREE(s->ftab); + if (s != NULL) BZFREE(s); + return BZ_MEM_ERROR; + } + + s->blockNo = 0; + s->state = BZ_S_INPUT; + s->mode = BZ_M_RUNNING; + s->combinedCRC = 0; + s->blockSize100k = blockSize100k; + s->nblockMAX = 100000 * blockSize100k - 19; + s->verbosity = verbosity; + s->workFactor = workFactor; + + s->block = (UChar*)s->arr2; + s->mtfv = (UInt16*)s->arr1; + s->zbits = NULL; + s->ptr = (UInt32*)s->arr1; + + strm->state = s; + strm->total_in_lo32 = 0; + strm->total_in_hi32 = 0; + strm->total_out_lo32 = 0; + strm->total_out_hi32 = 0; + init_RL ( s ); + prepare_new_block ( s ); + return BZ_OK; +} + + +/*---------------------------------------------------*/ +static +void add_pair_to_block ( EState* s ) +{ + Int32 i; + UChar ch = (UChar)(s->state_in_ch); + for (i = 0; i < s->state_in_len; i++) { + BZ_UPDATE_CRC( s->blockCRC, ch ); + } + s->inUse[s->state_in_ch] = True; + switch (s->state_in_len) { + case 1: + s->block[s->nblock] = (UChar)ch; s->nblock++; + break; + case 2: + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + break; + case 3: + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + break; + default: + s->inUse[s->state_in_len-4] = True; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = ((UChar)(s->state_in_len-4)); + s->nblock++; + break; + } +} + + +/*---------------------------------------------------*/ +static +void flush_RL ( EState* s ) +{ + if (s->state_in_ch < 256) add_pair_to_block ( s ); + init_RL ( s ); +} + + +/*---------------------------------------------------*/ +#define ADD_CHAR_TO_BLOCK(zs,zchh0) \ +{ \ + UInt32 zchh = (UInt32)(zchh0); \ + /*-- fast track the common case --*/ \ + if (zchh != zs->state_in_ch && \ + zs->state_in_len == 1) { \ + UChar ch = (UChar)(zs->state_in_ch); \ + BZ_UPDATE_CRC( zs->blockCRC, ch ); \ + zs->inUse[zs->state_in_ch] = True; \ + zs->block[zs->nblock] = (UChar)ch; \ + zs->nblock++; \ + zs->state_in_ch = zchh; \ + } \ + else \ + /*-- general, uncommon cases --*/ \ + if (zchh != zs->state_in_ch || \ + zs->state_in_len == 255) { \ + if (zs->state_in_ch < 256) \ + add_pair_to_block ( zs ); \ + zs->state_in_ch = zchh; \ + zs->state_in_len = 1; \ + } else { \ + zs->state_in_len++; \ + } \ +} + + +/*---------------------------------------------------*/ +static +Bool copy_input_until_stop ( EState* s ) +{ + Bool progress_in = False; + + if (s->mode == BZ_M_RUNNING) { + + /*-- fast track the common case --*/ + while (True) { + /*-- block full? --*/ + if (s->nblock >= s->nblockMAX) break; + /*-- no input? --*/ + if (s->strm->avail_in == 0) break; + progress_in = True; + ADD_CHAR_TO_BLOCK ( s, (UInt32)(*((UChar*)(s->strm->next_in))) ); + s->strm->next_in++; + s->strm->avail_in--; + s->strm->total_in_lo32++; + if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++; + } + + } else { + + /*-- general, uncommon case --*/ + while (True) { + /*-- block full? --*/ + if (s->nblock >= s->nblockMAX) break; + /*-- no input? --*/ + if (s->strm->avail_in == 0) break; + /*-- flush/finish end? --*/ + if (s->avail_in_expect == 0) break; + progress_in = True; + ADD_CHAR_TO_BLOCK ( s, (UInt32)(*((UChar*)(s->strm->next_in))) ); + s->strm->next_in++; + s->strm->avail_in--; + s->strm->total_in_lo32++; + if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++; + s->avail_in_expect--; + } + } + return progress_in; +} + + +/*---------------------------------------------------*/ +static +Bool copy_output_until_stop ( EState* s ) +{ + Bool progress_out = False; + + while (True) { + + /*-- no output space? --*/ + if (s->strm->avail_out == 0) break; + + /*-- block done? --*/ + if (s->state_out_pos >= s->numZ) break; + + progress_out = True; + *(s->strm->next_out) = s->zbits[s->state_out_pos]; + s->state_out_pos++; + s->strm->avail_out--; + s->strm->next_out++; + s->strm->total_out_lo32++; + if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; + } + + return progress_out; +} + + +/*---------------------------------------------------*/ +static +Bool handle_compress ( bz_stream* strm ) +{ + Bool progress_in = False; + Bool progress_out = False; + EState* s = strm->state; + + while (True) { + + if (s->state == BZ_S_OUTPUT) { + progress_out |= copy_output_until_stop ( s ); + if (s->state_out_pos < s->numZ) break; + if (s->mode == BZ_M_FINISHING && + s->avail_in_expect == 0 && + isempty_RL(s)) break; + prepare_new_block ( s ); + s->state = BZ_S_INPUT; + if (s->mode == BZ_M_FLUSHING && + s->avail_in_expect == 0 && + isempty_RL(s)) break; + } + + if (s->state == BZ_S_INPUT) { + progress_in |= copy_input_until_stop ( s ); + if (s->mode != BZ_M_RUNNING && s->avail_in_expect == 0) { + flush_RL ( s ); + BZ2_compressBlock ( s, (Bool)(s->mode == BZ_M_FINISHING) ); + s->state = BZ_S_OUTPUT; + } + else + if (s->nblock >= s->nblockMAX) { + BZ2_compressBlock ( s, False ); + s->state = BZ_S_OUTPUT; + } + else + if (s->strm->avail_in == 0) { + break; + } + } + + } + + return progress_in || progress_out; +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzCompress) ( bz_stream *strm, int action ) +{ + Bool progress; + EState* s; + if (strm == NULL) return BZ_PARAM_ERROR; + s = strm->state; + if (s == NULL) return BZ_PARAM_ERROR; + if (s->strm != strm) return BZ_PARAM_ERROR; + + preswitch: + switch (s->mode) { + + case BZ_M_IDLE: + return BZ_SEQUENCE_ERROR; + + case BZ_M_RUNNING: + if (action == BZ_RUN) { + progress = handle_compress ( strm ); + return progress ? BZ_RUN_OK : BZ_PARAM_ERROR; + } + else + if (action == BZ_FLUSH) { + s->avail_in_expect = strm->avail_in; + s->mode = BZ_M_FLUSHING; + goto preswitch; + } + else + if (action == BZ_FINISH) { + s->avail_in_expect = strm->avail_in; + s->mode = BZ_M_FINISHING; + goto preswitch; + } + else + return BZ_PARAM_ERROR; + + case BZ_M_FLUSHING: + if (action != BZ_FLUSH) return BZ_SEQUENCE_ERROR; + if (s->avail_in_expect != s->strm->avail_in) + return BZ_SEQUENCE_ERROR; + progress = handle_compress ( strm ); + if (s->avail_in_expect > 0 || !isempty_RL(s) || + s->state_out_pos < s->numZ) return BZ_FLUSH_OK; + s->mode = BZ_M_RUNNING; + return BZ_RUN_OK; + + case BZ_M_FINISHING: + if (action != BZ_FINISH) return BZ_SEQUENCE_ERROR; + if (s->avail_in_expect != s->strm->avail_in) + return BZ_SEQUENCE_ERROR; + progress = handle_compress ( strm ); + if (!progress) return BZ_SEQUENCE_ERROR; + if (s->avail_in_expect > 0 || !isempty_RL(s) || + s->state_out_pos < s->numZ) return BZ_FINISH_OK; + s->mode = BZ_M_IDLE; + return BZ_STREAM_END; + } + return BZ_OK; /*--not reached--*/ +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzCompressEnd) ( bz_stream *strm ) +{ + EState* s; + if (strm == NULL) return BZ_PARAM_ERROR; + s = strm->state; + if (s == NULL) return BZ_PARAM_ERROR; + if (s->strm != strm) return BZ_PARAM_ERROR; + + if (s->arr1 != NULL) BZFREE(s->arr1); + if (s->arr2 != NULL) BZFREE(s->arr2); + if (s->ftab != NULL) BZFREE(s->ftab); + BZFREE(strm->state); + + strm->state = NULL; + + return BZ_OK; +} + + +/*---------------------------------------------------*/ +/*--- Decompression stuff ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzDecompressInit) + ( bz_stream* strm, + int verbosity, + int small ) +{ + DState* s; + + if (!bz_config_ok()) return BZ_CONFIG_ERROR; + + if (strm == NULL) return BZ_PARAM_ERROR; + if (small != 0 && small != 1) return BZ_PARAM_ERROR; + if (verbosity < 0 || verbosity > 4) return BZ_PARAM_ERROR; + + if (strm->bzalloc == NULL) strm->bzalloc = default_bzalloc; + if (strm->bzfree == NULL) strm->bzfree = default_bzfree; + + s = BZALLOC( sizeof(DState) ); + if (s == NULL) return BZ_MEM_ERROR; + s->strm = strm; + strm->state = s; + s->state = BZ_X_MAGIC_1; + s->bsLive = 0; + s->bsBuff = 0; + s->calculatedCombinedCRC = 0; + strm->total_in_lo32 = 0; + strm->total_in_hi32 = 0; + strm->total_out_lo32 = 0; + strm->total_out_hi32 = 0; + s->smallDecompress = (Bool)small; + s->ll4 = NULL; + s->ll16 = NULL; + s->tt = NULL; + s->currBlockNo = 0; + s->verbosity = verbosity; + + return BZ_OK; +} + + +/*---------------------------------------------------*/ +/* Return True iff data corruption is discovered. + Returns False if there is no problem. +*/ +static +Bool unRLE_obuf_to_output_FAST ( DState* s ) +{ + UChar k1; + + if (s->blockRandomised) { + + while (True) { + /* try to finish existing run */ + while (True) { + if (s->strm->avail_out == 0) return False; + if (s->state_out_len == 0) break; + *( (UChar*)(s->strm->next_out) ) = s->state_out_ch; + BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch ); + s->state_out_len--; + s->strm->next_out++; + s->strm->avail_out--; + s->strm->total_out_lo32++; + if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; + } + + /* can a new run be started? */ + if (s->nblock_used == s->save_nblock+1) return False; + + /* Only caused by corrupt data stream? */ + if (s->nblock_used > s->save_nblock+1) + return True; + + s->state_out_len = 1; + s->state_out_ch = s->k0; + BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 2; + BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 3; + BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + s->state_out_len = ((Int32)k1) + 4; + BZ_GET_FAST(s->k0); BZ_RAND_UPD_MASK; + s->k0 ^= BZ_RAND_MASK; s->nblock_used++; + } + + } else { + + /* restore */ + UInt32 c_calculatedBlockCRC = s->calculatedBlockCRC; + UChar c_state_out_ch = s->state_out_ch; + Int32 c_state_out_len = s->state_out_len; + Int32 c_nblock_used = s->nblock_used; + Int32 c_k0 = s->k0; + UInt32* c_tt = s->tt; + UInt32 c_tPos = s->tPos; + char* cs_next_out = s->strm->next_out; + unsigned int cs_avail_out = s->strm->avail_out; + Int32 ro_blockSize100k = s->blockSize100k; + /* end restore */ + + UInt32 avail_out_INIT = cs_avail_out; + Int32 s_save_nblockPP = s->save_nblock+1; + unsigned int total_out_lo32_old; + + while (True) { + + /* try to finish existing run */ + if (c_state_out_len > 0) { + while (True) { + if (cs_avail_out == 0) goto return_notr; + if (c_state_out_len == 1) break; + *( (UChar*)(cs_next_out) ) = c_state_out_ch; + BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch ); + c_state_out_len--; + cs_next_out++; + cs_avail_out--; + } + s_state_out_len_eq_one: + { + if (cs_avail_out == 0) { + c_state_out_len = 1; goto return_notr; + }; + *( (UChar*)(cs_next_out) ) = c_state_out_ch; + BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch ); + cs_next_out++; + cs_avail_out--; + } + } + /* Only caused by corrupt data stream? */ + if (c_nblock_used > s_save_nblockPP) + return True; + + /* can a new run be started? */ + if (c_nblock_used == s_save_nblockPP) { + c_state_out_len = 0; goto return_notr; + }; + c_state_out_ch = c_k0; + BZ_GET_FAST_C(k1); c_nblock_used++; + if (k1 != c_k0) { + c_k0 = k1; goto s_state_out_len_eq_one; + }; + if (c_nblock_used == s_save_nblockPP) + goto s_state_out_len_eq_one; + + c_state_out_len = 2; + BZ_GET_FAST_C(k1); c_nblock_used++; + if (c_nblock_used == s_save_nblockPP) continue; + if (k1 != c_k0) { c_k0 = k1; continue; }; + + c_state_out_len = 3; + BZ_GET_FAST_C(k1); c_nblock_used++; + if (c_nblock_used == s_save_nblockPP) continue; + if (k1 != c_k0) { c_k0 = k1; continue; }; + + BZ_GET_FAST_C(k1); c_nblock_used++; + c_state_out_len = ((Int32)k1) + 4; + BZ_GET_FAST_C(c_k0); c_nblock_used++; + } + + return_notr: + total_out_lo32_old = s->strm->total_out_lo32; + s->strm->total_out_lo32 += (avail_out_INIT - cs_avail_out); + if (s->strm->total_out_lo32 < total_out_lo32_old) + s->strm->total_out_hi32++; + + /* save */ + s->calculatedBlockCRC = c_calculatedBlockCRC; + s->state_out_ch = c_state_out_ch; + s->state_out_len = c_state_out_len; + s->nblock_used = c_nblock_used; + s->k0 = c_k0; + s->tt = c_tt; + s->tPos = c_tPos; + s->strm->next_out = cs_next_out; + s->strm->avail_out = cs_avail_out; + /* end save */ + } + return False; +} + + + +/*---------------------------------------------------*/ +__inline__ Int32 BZ2_indexIntoF ( Int32 indx, Int32 *cftab ) +{ + Int32 nb, na, mid; + nb = 0; + na = 256; + do { + mid = (nb + na) >> 1; + if (indx >= cftab[mid]) nb = mid; else na = mid; + } + while (na - nb != 1); + return nb; +} + + +/*---------------------------------------------------*/ +/* Return True iff data corruption is discovered. + Returns False if there is no problem. +*/ +static +Bool unRLE_obuf_to_output_SMALL ( DState* s ) +{ + UChar k1; + + if (s->blockRandomised) { + + while (True) { + /* try to finish existing run */ + while (True) { + if (s->strm->avail_out == 0) return False; + if (s->state_out_len == 0) break; + *( (UChar*)(s->strm->next_out) ) = s->state_out_ch; + BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch ); + s->state_out_len--; + s->strm->next_out++; + s->strm->avail_out--; + s->strm->total_out_lo32++; + if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; + } + + /* can a new run be started? */ + if (s->nblock_used == s->save_nblock+1) return False; + + /* Only caused by corrupt data stream? */ + if (s->nblock_used > s->save_nblock+1) + return True; + + s->state_out_len = 1; + s->state_out_ch = s->k0; + BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 2; + BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 3; + BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + s->state_out_len = ((Int32)k1) + 4; + BZ_GET_SMALL(s->k0); BZ_RAND_UPD_MASK; + s->k0 ^= BZ_RAND_MASK; s->nblock_used++; + } + + } else { + + while (True) { + /* try to finish existing run */ + while (True) { + if (s->strm->avail_out == 0) return False; + if (s->state_out_len == 0) break; + *( (UChar*)(s->strm->next_out) ) = s->state_out_ch; + BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch ); + s->state_out_len--; + s->strm->next_out++; + s->strm->avail_out--; + s->strm->total_out_lo32++; + if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; + } + + /* can a new run be started? */ + if (s->nblock_used == s->save_nblock+1) return False; + + /* Only caused by corrupt data stream? */ + if (s->nblock_used > s->save_nblock+1) + return True; + + s->state_out_len = 1; + s->state_out_ch = s->k0; + BZ_GET_SMALL(k1); s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 2; + BZ_GET_SMALL(k1); s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 3; + BZ_GET_SMALL(k1); s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + BZ_GET_SMALL(k1); s->nblock_used++; + s->state_out_len = ((Int32)k1) + 4; + BZ_GET_SMALL(s->k0); s->nblock_used++; + } + + } +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzDecompress) ( bz_stream *strm ) +{ + Bool corrupt; + DState* s; + if (strm == NULL) return BZ_PARAM_ERROR; + s = strm->state; + if (s == NULL) return BZ_PARAM_ERROR; + if (s->strm != strm) return BZ_PARAM_ERROR; + + while (True) { + if (s->state == BZ_X_IDLE) return BZ_SEQUENCE_ERROR; + if (s->state == BZ_X_OUTPUT) { + if (s->smallDecompress) + corrupt = unRLE_obuf_to_output_SMALL ( s ); else + corrupt = unRLE_obuf_to_output_FAST ( s ); + if (corrupt) return BZ_DATA_ERROR; + if (s->nblock_used == s->save_nblock+1 && s->state_out_len == 0) { + BZ_FINALISE_CRC ( s->calculatedBlockCRC ); + if (s->verbosity >= 3) + VPrintf2 ( " {0x%08x, 0x%08x}", s->storedBlockCRC, + s->calculatedBlockCRC ); + if (s->verbosity >= 2) VPrintf0 ( "]" ); + if (s->calculatedBlockCRC != s->storedBlockCRC) + return BZ_DATA_ERROR; + s->calculatedCombinedCRC + = (s->calculatedCombinedCRC << 1) | + (s->calculatedCombinedCRC >> 31); + s->calculatedCombinedCRC ^= s->calculatedBlockCRC; + s->state = BZ_X_BLKHDR_1; + } else { + return BZ_OK; + } + } + if (s->state >= BZ_X_MAGIC_1) { + Int32 r = BZ2_decompress ( s ); + if (r == BZ_STREAM_END) { + if (s->verbosity >= 3) + VPrintf2 ( "\n combined CRCs: stored = 0x%08x, computed = 0x%08x", + s->storedCombinedCRC, s->calculatedCombinedCRC ); + if (s->calculatedCombinedCRC != s->storedCombinedCRC) + return BZ_DATA_ERROR; + return r; + } + if (s->state != BZ_X_OUTPUT) return r; + } + } + + AssertH ( 0, 6001 ); + + return 0; /*NOTREACHED*/ +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzDecompressEnd) ( bz_stream *strm ) +{ + DState* s; + if (strm == NULL) return BZ_PARAM_ERROR; + s = strm->state; + if (s == NULL) return BZ_PARAM_ERROR; + if (s->strm != strm) return BZ_PARAM_ERROR; + + if (s->tt != NULL) BZFREE(s->tt); + if (s->ll16 != NULL) BZFREE(s->ll16); + if (s->ll4 != NULL) BZFREE(s->ll4); + + BZFREE(strm->state); + strm->state = NULL; + + return BZ_OK; +} + + +#ifndef BZ_NO_STDIO +/*---------------------------------------------------*/ +/*--- File I/O stuff ---*/ +/*---------------------------------------------------*/ + +#define BZ_SETERR(eee) \ +{ \ + if (bzerror != NULL) *bzerror = eee; \ + if (bzf != NULL) bzf->lastErr = eee; \ +} + +typedef + struct { + FILE* handle; + Char buf[BZ_MAX_UNUSED]; + Int32 bufN; + Bool writing; + bz_stream strm; + Int32 lastErr; + Bool initialisedOk; + } + bzFile; + + +/*---------------------------------------------*/ +static Bool myfeof ( FILE* f ) +{ + Int32 c = fgetc ( f ); + if (c == EOF) return True; + ungetc ( c, f ); + return False; +} + + +/*---------------------------------------------------*/ +BZFILE* BZ_API(BZ2_bzWriteOpen) + ( int* bzerror, + FILE* f, + int blockSize100k, + int verbosity, + int workFactor ) +{ + Int32 ret; + bzFile* bzf = NULL; + + BZ_SETERR(BZ_OK); + + if (f == NULL || + (blockSize100k < 1 || blockSize100k > 9) || + (workFactor < 0 || workFactor > 250) || + (verbosity < 0 || verbosity > 4)) + { BZ_SETERR(BZ_PARAM_ERROR); return NULL; }; + + if (ferror(f)) + { BZ_SETERR(BZ_IO_ERROR); return NULL; }; + + bzf = malloc ( sizeof(bzFile) ); + if (bzf == NULL) + { BZ_SETERR(BZ_MEM_ERROR); return NULL; }; + + BZ_SETERR(BZ_OK); + bzf->initialisedOk = False; + bzf->bufN = 0; + bzf->handle = f; + bzf->writing = True; + bzf->strm.bzalloc = NULL; + bzf->strm.bzfree = NULL; + bzf->strm.opaque = NULL; + + if (workFactor == 0) workFactor = 30; + ret = BZ2_bzCompressInit ( &(bzf->strm), blockSize100k, + verbosity, workFactor ); + if (ret != BZ_OK) + { BZ_SETERR(ret); free(bzf); return NULL; }; + + bzf->strm.avail_in = 0; + bzf->initialisedOk = True; + return bzf; +} + + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzWrite) + ( int* bzerror, + BZFILE* b, + void* buf, + int len ) +{ + Int32 n, n2, ret; + bzFile* bzf = (bzFile*)b; + + BZ_SETERR(BZ_OK); + if (bzf == NULL || buf == NULL || len < 0) + { BZ_SETERR(BZ_PARAM_ERROR); return; }; + if (!(bzf->writing)) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + + if (len == 0) + { BZ_SETERR(BZ_OK); return; }; + + bzf->strm.avail_in = len; + bzf->strm.next_in = buf; + + while (True) { + bzf->strm.avail_out = BZ_MAX_UNUSED; + bzf->strm.next_out = bzf->buf; + ret = BZ2_bzCompress ( &(bzf->strm), BZ_RUN ); + if (ret != BZ_RUN_OK) + { BZ_SETERR(ret); return; }; + + if (bzf->strm.avail_out < BZ_MAX_UNUSED) { + n = BZ_MAX_UNUSED - bzf->strm.avail_out; + n2 = fwrite ( (void*)(bzf->buf), sizeof(UChar), + n, bzf->handle ); + if (n != n2 || ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + } + + if (bzf->strm.avail_in == 0) + { BZ_SETERR(BZ_OK); return; }; + } +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzWriteClose) + ( int* bzerror, + BZFILE* b, + int abandon, + unsigned int* nbytes_in, + unsigned int* nbytes_out ) +{ + BZ2_bzWriteClose64 ( bzerror, b, abandon, + nbytes_in, NULL, nbytes_out, NULL ); +} + + +void BZ_API(BZ2_bzWriteClose64) + ( int* bzerror, + BZFILE* b, + int abandon, + unsigned int* nbytes_in_lo32, + unsigned int* nbytes_in_hi32, + unsigned int* nbytes_out_lo32, + unsigned int* nbytes_out_hi32 ) +{ + Int32 n, n2, ret; + bzFile* bzf = (bzFile*)b; + + if (bzf == NULL) + { BZ_SETERR(BZ_OK); return; }; + if (!(bzf->writing)) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + + if (nbytes_in_lo32 != NULL) *nbytes_in_lo32 = 0; + if (nbytes_in_hi32 != NULL) *nbytes_in_hi32 = 0; + if (nbytes_out_lo32 != NULL) *nbytes_out_lo32 = 0; + if (nbytes_out_hi32 != NULL) *nbytes_out_hi32 = 0; + + if ((!abandon) && bzf->lastErr == BZ_OK) { + while (True) { + bzf->strm.avail_out = BZ_MAX_UNUSED; + bzf->strm.next_out = bzf->buf; + ret = BZ2_bzCompress ( &(bzf->strm), BZ_FINISH ); + if (ret != BZ_FINISH_OK && ret != BZ_STREAM_END) + { BZ_SETERR(ret); return; }; + + if (bzf->strm.avail_out < BZ_MAX_UNUSED) { + n = BZ_MAX_UNUSED - bzf->strm.avail_out; + n2 = fwrite ( (void*)(bzf->buf), sizeof(UChar), + n, bzf->handle ); + if (n != n2 || ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + } + + if (ret == BZ_STREAM_END) break; + } + } + + if ( !abandon && !ferror ( bzf->handle ) ) { + fflush ( bzf->handle ); + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + } + + if (nbytes_in_lo32 != NULL) + *nbytes_in_lo32 = bzf->strm.total_in_lo32; + if (nbytes_in_hi32 != NULL) + *nbytes_in_hi32 = bzf->strm.total_in_hi32; + if (nbytes_out_lo32 != NULL) + *nbytes_out_lo32 = bzf->strm.total_out_lo32; + if (nbytes_out_hi32 != NULL) + *nbytes_out_hi32 = bzf->strm.total_out_hi32; + + BZ_SETERR(BZ_OK); + BZ2_bzCompressEnd ( &(bzf->strm) ); + free ( bzf ); +} + + +/*---------------------------------------------------*/ +BZFILE* BZ_API(BZ2_bzReadOpen) + ( int* bzerror, + FILE* f, + int verbosity, + int small, + void* unused, + int nUnused ) +{ + bzFile* bzf = NULL; + int ret; + + BZ_SETERR(BZ_OK); + + if (f == NULL || + (small != 0 && small != 1) || + (verbosity < 0 || verbosity > 4) || + (unused == NULL && nUnused != 0) || + (unused != NULL && (nUnused < 0 || nUnused > BZ_MAX_UNUSED))) + { BZ_SETERR(BZ_PARAM_ERROR); return NULL; }; + + if (ferror(f)) + { BZ_SETERR(BZ_IO_ERROR); return NULL; }; + + bzf = malloc ( sizeof(bzFile) ); + if (bzf == NULL) + { BZ_SETERR(BZ_MEM_ERROR); return NULL; }; + + BZ_SETERR(BZ_OK); + + bzf->initialisedOk = False; + bzf->handle = f; + bzf->bufN = 0; + bzf->writing = False; + bzf->strm.bzalloc = NULL; + bzf->strm.bzfree = NULL; + bzf->strm.opaque = NULL; + + while (nUnused > 0) { + bzf->buf[bzf->bufN] = *((UChar*)(unused)); bzf->bufN++; + unused = ((void*)( 1 + ((UChar*)(unused)) )); + nUnused--; + } + + ret = BZ2_bzDecompressInit ( &(bzf->strm), verbosity, small ); + if (ret != BZ_OK) + { BZ_SETERR(ret); free(bzf); return NULL; }; + + bzf->strm.avail_in = bzf->bufN; + bzf->strm.next_in = bzf->buf; + + bzf->initialisedOk = True; + return bzf; +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzReadClose) ( int *bzerror, BZFILE *b ) +{ + bzFile* bzf = (bzFile*)b; + + BZ_SETERR(BZ_OK); + if (bzf == NULL) + { BZ_SETERR(BZ_OK); return; }; + + if (bzf->writing) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; + + if (bzf->initialisedOk) + (void)BZ2_bzDecompressEnd ( &(bzf->strm) ); + free ( bzf ); +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzRead) + ( int* bzerror, + BZFILE* b, + void* buf, + int len ) +{ + Int32 n, ret; + bzFile* bzf = (bzFile*)b; + + BZ_SETERR(BZ_OK); + + if (bzf == NULL || buf == NULL || len < 0) + { BZ_SETERR(BZ_PARAM_ERROR); return 0; }; + + if (bzf->writing) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return 0; }; + + if (len == 0) + { BZ_SETERR(BZ_OK); return 0; }; + + bzf->strm.avail_out = len; + bzf->strm.next_out = buf; + + while (True) { + + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return 0; }; + + if (bzf->strm.avail_in == 0 && !myfeof(bzf->handle)) { + n = fread ( bzf->buf, sizeof(UChar), + BZ_MAX_UNUSED, bzf->handle ); + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return 0; }; + bzf->bufN = n; + bzf->strm.avail_in = bzf->bufN; + bzf->strm.next_in = bzf->buf; + } + + ret = BZ2_bzDecompress ( &(bzf->strm) ); + + if (ret != BZ_OK && ret != BZ_STREAM_END) + { BZ_SETERR(ret); return 0; }; + + if (ret == BZ_OK && myfeof(bzf->handle) && + bzf->strm.avail_in == 0 && bzf->strm.avail_out > 0) + { BZ_SETERR(BZ_UNEXPECTED_EOF); return 0; }; + + if (ret == BZ_STREAM_END) + { BZ_SETERR(BZ_STREAM_END); + return len - bzf->strm.avail_out; }; + if (bzf->strm.avail_out == 0) + { BZ_SETERR(BZ_OK); return len; }; + + } + + return 0; /*not reached*/ +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzReadGetUnused) + ( int* bzerror, + BZFILE* b, + void** unused, + int* nUnused ) +{ + bzFile* bzf = (bzFile*)b; + if (bzf == NULL) + { BZ_SETERR(BZ_PARAM_ERROR); return; }; + if (bzf->lastErr != BZ_STREAM_END) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; + if (unused == NULL || nUnused == NULL) + { BZ_SETERR(BZ_PARAM_ERROR); return; }; + + BZ_SETERR(BZ_OK); + *nUnused = bzf->strm.avail_in; + *unused = bzf->strm.next_in; +} +#endif + + +/*---------------------------------------------------*/ +/*--- Misc convenience stuff ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzBuffToBuffCompress) + ( char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int blockSize100k, + int verbosity, + int workFactor ) +{ + bz_stream strm; + int ret; + + if (dest == NULL || destLen == NULL || + source == NULL || + blockSize100k < 1 || blockSize100k > 9 || + verbosity < 0 || verbosity > 4 || + workFactor < 0 || workFactor > 250) + return BZ_PARAM_ERROR; + + if (workFactor == 0) workFactor = 30; + strm.bzalloc = NULL; + strm.bzfree = NULL; + strm.opaque = NULL; + ret = BZ2_bzCompressInit ( &strm, blockSize100k, + verbosity, workFactor ); + if (ret != BZ_OK) return ret; + + strm.next_in = source; + strm.next_out = dest; + strm.avail_in = sourceLen; + strm.avail_out = *destLen; + + ret = BZ2_bzCompress ( &strm, BZ_FINISH ); + if (ret == BZ_FINISH_OK) goto output_overflow; + if (ret != BZ_STREAM_END) goto errhandler; + + /* normal termination */ + *destLen -= strm.avail_out; + BZ2_bzCompressEnd ( &strm ); + return BZ_OK; + + output_overflow: + BZ2_bzCompressEnd ( &strm ); + return BZ_OUTBUFF_FULL; + + errhandler: + BZ2_bzCompressEnd ( &strm ); + return ret; +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzBuffToBuffDecompress) + ( char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int small, + int verbosity ) +{ + bz_stream strm; + int ret; + + if (dest == NULL || destLen == NULL || + source == NULL || + (small != 0 && small != 1) || + verbosity < 0 || verbosity > 4) + return BZ_PARAM_ERROR; + + strm.bzalloc = NULL; + strm.bzfree = NULL; + strm.opaque = NULL; + ret = BZ2_bzDecompressInit ( &strm, verbosity, small ); + if (ret != BZ_OK) return ret; + + strm.next_in = source; + strm.next_out = dest; + strm.avail_in = sourceLen; + strm.avail_out = *destLen; + + ret = BZ2_bzDecompress ( &strm ); + if (ret == BZ_OK) goto output_overflow_or_eof; + if (ret != BZ_STREAM_END) goto errhandler; + + /* normal termination */ + *destLen -= strm.avail_out; + BZ2_bzDecompressEnd ( &strm ); + return BZ_OK; + + output_overflow_or_eof: + if (strm.avail_out > 0) { + BZ2_bzDecompressEnd ( &strm ); + return BZ_UNEXPECTED_EOF; + } else { + BZ2_bzDecompressEnd ( &strm ); + return BZ_OUTBUFF_FULL; + }; + + errhandler: + BZ2_bzDecompressEnd ( &strm ); + return ret; +} + + +/*---------------------------------------------------*/ +/*-- + Code contributed by Yoshioka Tsuneo (tsuneo@rr.iij4u.or.jp) + to support better zlib compatibility. + This code is not _officially_ part of libbzip2 (yet); + I haven't tested it, documented it, or considered the + threading-safeness of it. + If this code breaks, please contact both Yoshioka and me. +--*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +/*-- + return version like "0.9.5d, 4-Sept-1999". +--*/ +const char * BZ_API(BZ2_bzlibVersion)(void) +{ + return BZ_VERSION; +} + + +#ifndef BZ_NO_STDIO +/*---------------------------------------------------*/ + +#if defined(_WIN32) || defined(OS2) || defined(MSDOS) +# include +# include +# define SET_BINARY_MODE(file) _setmode(_fileno(file),O_BINARY) +#else +# define SET_BINARY_MODE(file) +#endif +static +BZFILE * bzopen_or_bzdopen + ( const char *path, /* no use when bzdopen */ + int fd, /* no use when bzdopen */ + const char *mode, + int open_mode) /* bzopen: 0, bzdopen:1 */ +{ + int bzerr; + char unused[BZ_MAX_UNUSED]; + int blockSize100k = 9; + int writing = 0; + char mode2[10] = ""; + FILE *fp = NULL; + BZFILE *bzfp = NULL; + int verbosity = 0; + int workFactor = 30; + int smallMode = 0; + int nUnused = 0; + + if (mode == NULL) return NULL; + while (*mode) { + switch (*mode) { + case 'r': + writing = 0; break; + case 'w': + writing = 1; break; + case 's': + smallMode = 1; break; + default: + if (isdigit((int)(*mode))) { + blockSize100k = *mode-BZ_HDR_0; + } + } + mode++; + } + strcat(mode2, writing ? "w" : "r" ); + strcat(mode2,"b"); /* binary mode */ + + if (open_mode==0) { + if (path==NULL || strcmp(path,"")==0) { + fp = (writing ? stdout : stdin); + SET_BINARY_MODE(fp); + } else { + fp = fopen(path,mode2); + } + } else { +#ifdef BZ_STRICT_ANSI + fp = NULL; +#else + fp = _fdopen(fd,mode2); +#endif + } + if (fp == NULL) return NULL; + + if (writing) { + /* Guard against total chaos and anarchy -- JRS */ + if (blockSize100k < 1) blockSize100k = 1; + if (blockSize100k > 9) blockSize100k = 9; + bzfp = BZ2_bzWriteOpen(&bzerr,fp,blockSize100k, + verbosity,workFactor); + } else { + bzfp = BZ2_bzReadOpen(&bzerr,fp,verbosity,smallMode, + unused,nUnused); + } + if (bzfp == NULL) { + if (fp != stdin && fp != stdout) fclose(fp); + return NULL; + } + return bzfp; +} + + +/*---------------------------------------------------*/ +/*-- + open file for read or write. + ex) bzopen("file","w9") + case path="" or NULL => use stdin or stdout. +--*/ +BZFILE * BZ_API(BZ2_bzopen) + ( const char *path, + const char *mode ) +{ + return bzopen_or_bzdopen(path,-1,mode,/*bzopen*/0); +} + + +/*---------------------------------------------------*/ +BZFILE * BZ_API(BZ2_bzdopen) + ( int fd, + const char *mode ) +{ + return bzopen_or_bzdopen(NULL,fd,mode,/*bzdopen*/1); +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzread) (BZFILE* b, void* buf, int len ) +{ + int bzerr, nread; + if (((bzFile*)b)->lastErr == BZ_STREAM_END) return 0; + nread = BZ2_bzRead(&bzerr,b,buf,len); + if (bzerr == BZ_OK || bzerr == BZ_STREAM_END) { + return nread; + } else { + return -1; + } +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzwrite) (BZFILE* b, void* buf, int len ) +{ + int bzerr; + + BZ2_bzWrite(&bzerr,b,buf,len); + if(bzerr == BZ_OK){ + return len; + }else{ + return -1; + } +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzflush) (BZFILE *b) +{ + /* do nothing now... */ + return 0; +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzclose) (BZFILE* b) +{ + int bzerr; + FILE *fp; + + if (b==NULL) {return;} + fp = ((bzFile *)b)->handle; + if(((bzFile*)b)->writing){ + BZ2_bzWriteClose(&bzerr,b,0,NULL,NULL); + if(bzerr != BZ_OK){ + BZ2_bzWriteClose(NULL,b,1,NULL,NULL); + } + }else{ + BZ2_bzReadClose(&bzerr,b); + } + if(fp!=stdin && fp!=stdout){ + fclose(fp); + } +} + + +/*---------------------------------------------------*/ +/*-- + return last error code +--*/ +static const char *bzerrorstrings[] = { + "OK" + ,"SEQUENCE_ERROR" + ,"PARAM_ERROR" + ,"MEM_ERROR" + ,"DATA_ERROR" + ,"DATA_ERROR_MAGIC" + ,"IO_ERROR" + ,"UNEXPECTED_EOF" + ,"OUTBUFF_FULL" + ,"CONFIG_ERROR" + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ +}; + + +const char * BZ_API(BZ2_bzerror) (BZFILE *b, int *errnum) +{ + int err = ((bzFile *)b)->lastErr; + + if(err>0) err = 0; + *errnum = err; + return bzerrorstrings[err*-1]; +} +#endif + + +/*-------------------------------------------------------------*/ +/*--- end bzlib.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/dep/StormLib/src/bzip2/bzlib.h b/dep/StormLib/src/bzip2/bzlib.h new file mode 100644 index 0000000000..c5b75d6d8f --- /dev/null +++ b/dep/StormLib/src/bzip2/bzlib.h @@ -0,0 +1,282 @@ + +/*-------------------------------------------------------------*/ +/*--- Public header file for the library. ---*/ +/*--- bzlib.h ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.5 of 10 December 2007 + Copyright (C) 1996-2007 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#ifndef _BZLIB_H +#define _BZLIB_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define BZ_RUN 0 +#define BZ_FLUSH 1 +#define BZ_FINISH 2 + +#define BZ_OK 0 +#define BZ_RUN_OK 1 +#define BZ_FLUSH_OK 2 +#define BZ_FINISH_OK 3 +#define BZ_STREAM_END 4 +#define BZ_SEQUENCE_ERROR (-1) +#define BZ_PARAM_ERROR (-2) +#define BZ_MEM_ERROR (-3) +#define BZ_DATA_ERROR (-4) +#define BZ_DATA_ERROR_MAGIC (-5) +#define BZ_IO_ERROR (-6) +#define BZ_UNEXPECTED_EOF (-7) +#define BZ_OUTBUFF_FULL (-8) +#define BZ_CONFIG_ERROR (-9) + +typedef + struct { + char *next_in; + unsigned int avail_in; + unsigned int total_in_lo32; + unsigned int total_in_hi32; + + char *next_out; + unsigned int avail_out; + unsigned int total_out_lo32; + unsigned int total_out_hi32; + + void *state; + + void *(*bzalloc)(void *,int,int); + void (*bzfree)(void *,void *); + void *opaque; + } + bz_stream; + + +#ifndef BZ_IMPORT +#define BZ_EXPORT +#endif + +#ifndef BZ_NO_STDIO +/* Need a definitition for FILE */ +#include +#endif + +#ifdef _WIN32 +# include +# ifdef small + /* windows.h define small to char */ +# undef small +# endif +# ifdef BZ_EXPORT +# define BZ_API(func) WINAPI func +# define BZ_EXTERN extern +# else + /* import windows dll dynamically */ +# define BZ_API(func) (WINAPI * func) +# define BZ_EXTERN +# endif +#else +# define BZ_API(func) func +# define BZ_EXTERN extern +#endif + + +/*-- Core (low-level) library functions --*/ + +BZ_EXTERN int BZ_API(BZ2_bzCompressInit) ( + bz_stream* strm, + int blockSize100k, + int verbosity, + int workFactor + ); + +BZ_EXTERN int BZ_API(BZ2_bzCompress) ( + bz_stream* strm, + int action + ); + +BZ_EXTERN int BZ_API(BZ2_bzCompressEnd) ( + bz_stream* strm + ); + +BZ_EXTERN int BZ_API(BZ2_bzDecompressInit) ( + bz_stream *strm, + int verbosity, + int small + ); + +BZ_EXTERN int BZ_API(BZ2_bzDecompress) ( + bz_stream* strm + ); + +BZ_EXTERN int BZ_API(BZ2_bzDecompressEnd) ( + bz_stream *strm + ); + + + +/*-- High(er) level library functions --*/ + +#ifndef BZ_NO_STDIO +#define BZ_MAX_UNUSED 5000 + +typedef void BZFILE; + +BZ_EXTERN BZFILE* BZ_API(BZ2_bzReadOpen) ( + int* bzerror, + FILE* f, + int verbosity, + int small, + void* unused, + int nUnused + ); + +BZ_EXTERN void BZ_API(BZ2_bzReadClose) ( + int* bzerror, + BZFILE* b + ); + +BZ_EXTERN void BZ_API(BZ2_bzReadGetUnused) ( + int* bzerror, + BZFILE* b, + void** unused, + int* nUnused + ); + +BZ_EXTERN int BZ_API(BZ2_bzRead) ( + int* bzerror, + BZFILE* b, + void* buf, + int len + ); + +BZ_EXTERN BZFILE* BZ_API(BZ2_bzWriteOpen) ( + int* bzerror, + FILE* f, + int blockSize100k, + int verbosity, + int workFactor + ); + +BZ_EXTERN void BZ_API(BZ2_bzWrite) ( + int* bzerror, + BZFILE* b, + void* buf, + int len + ); + +BZ_EXTERN void BZ_API(BZ2_bzWriteClose) ( + int* bzerror, + BZFILE* b, + int abandon, + unsigned int* nbytes_in, + unsigned int* nbytes_out + ); + +BZ_EXTERN void BZ_API(BZ2_bzWriteClose64) ( + int* bzerror, + BZFILE* b, + int abandon, + unsigned int* nbytes_in_lo32, + unsigned int* nbytes_in_hi32, + unsigned int* nbytes_out_lo32, + unsigned int* nbytes_out_hi32 + ); +#endif + + +/*-- Utility functions --*/ + +BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffCompress) ( + char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int blockSize100k, + int verbosity, + int workFactor + ); + +BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffDecompress) ( + char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int small, + int verbosity + ); + + +/*-- + Code contributed by Yoshioka Tsuneo (tsuneo@rr.iij4u.or.jp) + to support better zlib compatibility. + This code is not _officially_ part of libbzip2 (yet); + I haven't tested it, documented it, or considered the + threading-safeness of it. + If this code breaks, please contact both Yoshioka and me. +--*/ + +BZ_EXTERN const char * BZ_API(BZ2_bzlibVersion) ( + void + ); + +#ifndef BZ_NO_STDIO +BZ_EXTERN BZFILE * BZ_API(BZ2_bzopen) ( + const char *path, + const char *mode + ); + +BZ_EXTERN BZFILE * BZ_API(BZ2_bzdopen) ( + int fd, + const char *mode + ); + +BZ_EXTERN int BZ_API(BZ2_bzread) ( + BZFILE* b, + void* buf, + int len + ); + +BZ_EXTERN int BZ_API(BZ2_bzwrite) ( + BZFILE* b, + void* buf, + int len + ); + +BZ_EXTERN int BZ_API(BZ2_bzflush) ( + BZFILE* b + ); + +BZ_EXTERN void BZ_API(BZ2_bzclose) ( + BZFILE* b + ); + +BZ_EXTERN const char * BZ_API(BZ2_bzerror) ( + BZFILE *b, + int *errnum + ); +#endif + +#ifdef __cplusplus +} +#endif + +#endif + +/*-------------------------------------------------------------*/ +/*--- end bzlib.h ---*/ +/*-------------------------------------------------------------*/ diff --git a/dep/StormLib/src/bzip2/bzlib_private.h b/dep/StormLib/src/bzip2/bzlib_private.h new file mode 100644 index 0000000000..23427879b1 --- /dev/null +++ b/dep/StormLib/src/bzip2/bzlib_private.h @@ -0,0 +1,509 @@ + +/*-------------------------------------------------------------*/ +/*--- Private header file for the library. ---*/ +/*--- bzlib_private.h ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.5 of 10 December 2007 + Copyright (C) 1996-2007 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#ifndef _BZLIB_PRIVATE_H +#define _BZLIB_PRIVATE_H + +#include + +#ifndef BZ_NO_STDIO +#include +#include +#include +#endif + +#include "bzlib.h" + + + +/*-- General stuff. --*/ + +#define BZ_VERSION "1.0.5, 10-Dec-2007" + +typedef char Char; +typedef unsigned char Bool; +typedef unsigned char UChar; +typedef int Int32; +typedef unsigned int UInt32; +typedef short Int16; +typedef unsigned short UInt16; + +#define True ((Bool)1) +#define False ((Bool)0) + +#ifndef __GNUC__ +#define __inline__ /* */ +#endif + +#ifndef BZ_NO_STDIO + +extern void BZ2_bz__AssertH__fail ( int errcode ); +#define AssertH(cond,errcode) \ + { if (!(cond)) BZ2_bz__AssertH__fail ( errcode ); } + +#if BZ_DEBUG +#define AssertD(cond,msg) \ + { if (!(cond)) { \ + fprintf ( stderr, \ + "\n\nlibbzip2(debug build): internal error\n\t%s\n", msg );\ + exit(1); \ + }} +#else +#define AssertD(cond,msg) /* */ +#endif + +#define VPrintf0(zf) \ + fprintf(stderr,zf) +#define VPrintf1(zf,za1) \ + fprintf(stderr,zf,za1) +#define VPrintf2(zf,za1,za2) \ + fprintf(stderr,zf,za1,za2) +#define VPrintf3(zf,za1,za2,za3) \ + fprintf(stderr,zf,za1,za2,za3) +#define VPrintf4(zf,za1,za2,za3,za4) \ + fprintf(stderr,zf,za1,za2,za3,za4) +#define VPrintf5(zf,za1,za2,za3,za4,za5) \ + fprintf(stderr,zf,za1,za2,za3,za4,za5) + +#else + +extern void bz_internal_error ( int errcode ); +#define AssertH(cond,errcode) \ + { if (!(cond)) bz_internal_error ( errcode ); } +#define AssertD(cond,msg) do { } while (0) +#define VPrintf0(zf) do { } while (0) +#define VPrintf1(zf,za1) do { } while (0) +#define VPrintf2(zf,za1,za2) do { } while (0) +#define VPrintf3(zf,za1,za2,za3) do { } while (0) +#define VPrintf4(zf,za1,za2,za3,za4) do { } while (0) +#define VPrintf5(zf,za1,za2,za3,za4,za5) do { } while (0) + +#endif + + +#define BZALLOC(nnn) (strm->bzalloc)(strm->opaque,(nnn),1) +#define BZFREE(ppp) (strm->bzfree)(strm->opaque,(ppp)) + + +/*-- Header bytes. --*/ + +#define BZ_HDR_B 0x42 /* 'B' */ +#define BZ_HDR_Z 0x5a /* 'Z' */ +#define BZ_HDR_h 0x68 /* 'h' */ +#define BZ_HDR_0 0x30 /* '0' */ + +/*-- Constants for the back end. --*/ + +#define BZ_MAX_ALPHA_SIZE 258 +#define BZ_MAX_CODE_LEN 23 + +#define BZ_RUNA 0 +#define BZ_RUNB 1 + +#define BZ_N_GROUPS 6 +#define BZ_G_SIZE 50 +#define BZ_N_ITERS 4 + +#define BZ_MAX_SELECTORS (2 + (900000 / BZ_G_SIZE)) + + + +/*-- Stuff for randomising repetitive blocks. --*/ + +extern Int32 BZ2_rNums[512]; + +#define BZ_RAND_DECLS \ + Int32 rNToGo; \ + Int32 rTPos \ + +#define BZ_RAND_INIT_MASK \ + s->rNToGo = 0; \ + s->rTPos = 0 \ + +#define BZ_RAND_MASK ((s->rNToGo == 1) ? 1 : 0) + +#define BZ_RAND_UPD_MASK \ + if (s->rNToGo == 0) { \ + s->rNToGo = BZ2_rNums[s->rTPos]; \ + s->rTPos++; \ + if (s->rTPos == 512) s->rTPos = 0; \ + } \ + s->rNToGo--; + + + +/*-- Stuff for doing CRCs. --*/ + +extern UInt32 BZ2_crc32Table[256]; + +#define BZ_INITIALISE_CRC(crcVar) \ +{ \ + crcVar = 0xffffffffL; \ +} + +#define BZ_FINALISE_CRC(crcVar) \ +{ \ + crcVar = ~(crcVar); \ +} + +#define BZ_UPDATE_CRC(crcVar,cha) \ +{ \ + crcVar = (crcVar << 8) ^ \ + BZ2_crc32Table[(crcVar >> 24) ^ \ + ((UChar)cha)]; \ +} + + + +/*-- States and modes for compression. --*/ + +#define BZ_M_IDLE 1 +#define BZ_M_RUNNING 2 +#define BZ_M_FLUSHING 3 +#define BZ_M_FINISHING 4 + +#define BZ_S_OUTPUT 1 +#define BZ_S_INPUT 2 + +#define BZ_N_RADIX 2 +#define BZ_N_QSORT 12 +#define BZ_N_SHELL 18 +#define BZ_N_OVERSHOOT (BZ_N_RADIX + BZ_N_QSORT + BZ_N_SHELL + 2) + + + + +/*-- Structure holding all the compression-side stuff. --*/ + +typedef + struct { + /* pointer back to the struct bz_stream */ + bz_stream* strm; + + /* mode this stream is in, and whether inputting */ + /* or outputting data */ + Int32 mode; + Int32 state; + + /* remembers avail_in when flush/finish requested */ + UInt32 avail_in_expect; + + /* for doing the block sorting */ + UInt32* arr1; + UInt32* arr2; + UInt32* ftab; + Int32 origPtr; + + /* aliases for arr1 and arr2 */ + UInt32* ptr; + UChar* block; + UInt16* mtfv; + UChar* zbits; + + /* for deciding when to use the fallback sorting algorithm */ + Int32 workFactor; + + /* run-length-encoding of the input */ + UInt32 state_in_ch; + Int32 state_in_len; + BZ_RAND_DECLS; + + /* input and output limits and current posns */ + Int32 nblock; + Int32 nblockMAX; + Int32 numZ; + Int32 state_out_pos; + + /* map of bytes used in block */ + Int32 nInUse; + Bool inUse[256]; + UChar unseqToSeq[256]; + + /* the buffer for bit stream creation */ + UInt32 bsBuff; + Int32 bsLive; + + /* block and combined CRCs */ + UInt32 blockCRC; + UInt32 combinedCRC; + + /* misc administratium */ + Int32 verbosity; + Int32 blockNo; + Int32 blockSize100k; + + /* stuff for coding the MTF values */ + Int32 nMTF; + Int32 mtfFreq [BZ_MAX_ALPHA_SIZE]; + UChar selector [BZ_MAX_SELECTORS]; + UChar selectorMtf[BZ_MAX_SELECTORS]; + + UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 code [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 rfreq [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + /* second dimension: only 3 needed; 4 makes index calculations faster */ + UInt32 len_pack[BZ_MAX_ALPHA_SIZE][4]; + + } + EState; + + + +/*-- externs for compression. --*/ + +extern void +BZ2_blockSort ( EState* ); + +extern void +BZ2_compressBlock ( EState*, Bool ); + +extern void +BZ2_bsInitWrite ( EState* ); + +extern void +BZ2_hbAssignCodes ( Int32*, UChar*, Int32, Int32, Int32 ); + +extern void +BZ2_hbMakeCodeLengths ( UChar*, Int32*, Int32, Int32 ); + + + +/*-- states for decompression. --*/ + +#define BZ_X_IDLE 1 +#define BZ_X_OUTPUT 2 + +#define BZ_X_MAGIC_1 10 +#define BZ_X_MAGIC_2 11 +#define BZ_X_MAGIC_3 12 +#define BZ_X_MAGIC_4 13 +#define BZ_X_BLKHDR_1 14 +#define BZ_X_BLKHDR_2 15 +#define BZ_X_BLKHDR_3 16 +#define BZ_X_BLKHDR_4 17 +#define BZ_X_BLKHDR_5 18 +#define BZ_X_BLKHDR_6 19 +#define BZ_X_BCRC_1 20 +#define BZ_X_BCRC_2 21 +#define BZ_X_BCRC_3 22 +#define BZ_X_BCRC_4 23 +#define BZ_X_RANDBIT 24 +#define BZ_X_ORIGPTR_1 25 +#define BZ_X_ORIGPTR_2 26 +#define BZ_X_ORIGPTR_3 27 +#define BZ_X_MAPPING_1 28 +#define BZ_X_MAPPING_2 29 +#define BZ_X_SELECTOR_1 30 +#define BZ_X_SELECTOR_2 31 +#define BZ_X_SELECTOR_3 32 +#define BZ_X_CODING_1 33 +#define BZ_X_CODING_2 34 +#define BZ_X_CODING_3 35 +#define BZ_X_MTF_1 36 +#define BZ_X_MTF_2 37 +#define BZ_X_MTF_3 38 +#define BZ_X_MTF_4 39 +#define BZ_X_MTF_5 40 +#define BZ_X_MTF_6 41 +#define BZ_X_ENDHDR_2 42 +#define BZ_X_ENDHDR_3 43 +#define BZ_X_ENDHDR_4 44 +#define BZ_X_ENDHDR_5 45 +#define BZ_X_ENDHDR_6 46 +#define BZ_X_CCRC_1 47 +#define BZ_X_CCRC_2 48 +#define BZ_X_CCRC_3 49 +#define BZ_X_CCRC_4 50 + + + +/*-- Constants for the fast MTF decoder. --*/ + +#define MTFA_SIZE 4096 +#define MTFL_SIZE 16 + + + +/*-- Structure holding all the decompression-side stuff. --*/ + +typedef + struct { + /* pointer back to the struct bz_stream */ + bz_stream* strm; + + /* state indicator for this stream */ + Int32 state; + + /* for doing the final run-length decoding */ + UChar state_out_ch; + Int32 state_out_len; + Bool blockRandomised; + BZ_RAND_DECLS; + + /* the buffer for bit stream reading */ + UInt32 bsBuff; + Int32 bsLive; + + /* misc administratium */ + Int32 blockSize100k; + Bool smallDecompress; + Int32 currBlockNo; + Int32 verbosity; + + /* for undoing the Burrows-Wheeler transform */ + Int32 origPtr; + UInt32 tPos; + Int32 k0; + Int32 unzftab[256]; + Int32 nblock_used; + Int32 cftab[257]; + Int32 cftabCopy[257]; + + /* for undoing the Burrows-Wheeler transform (FAST) */ + UInt32 *tt; + + /* for undoing the Burrows-Wheeler transform (SMALL) */ + UInt16 *ll16; + UChar *ll4; + + /* stored and calculated CRCs */ + UInt32 storedBlockCRC; + UInt32 storedCombinedCRC; + UInt32 calculatedBlockCRC; + UInt32 calculatedCombinedCRC; + + /* map of bytes used in block */ + Int32 nInUse; + Bool inUse[256]; + Bool inUse16[16]; + UChar seqToUnseq[256]; + + /* for decoding the MTF values */ + UChar mtfa [MTFA_SIZE]; + Int32 mtfbase[256 / MTFL_SIZE]; + UChar selector [BZ_MAX_SELECTORS]; + UChar selectorMtf[BZ_MAX_SELECTORS]; + UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + + Int32 limit [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 base [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 perm [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 minLens[BZ_N_GROUPS]; + + /* save area for scalars in the main decompress code */ + Int32 save_i; + Int32 save_j; + Int32 save_t; + Int32 save_alphaSize; + Int32 save_nGroups; + Int32 save_nSelectors; + Int32 save_EOB; + Int32 save_groupNo; + Int32 save_groupPos; + Int32 save_nextSym; + Int32 save_nblockMAX; + Int32 save_nblock; + Int32 save_es; + Int32 save_N; + Int32 save_curr; + Int32 save_zt; + Int32 save_zn; + Int32 save_zvec; + Int32 save_zj; + Int32 save_gSel; + Int32 save_gMinlen; + Int32* save_gLimit; + Int32* save_gBase; + Int32* save_gPerm; + + } + DState; + + + +/*-- Macros for decompression. --*/ + +#define BZ_GET_FAST(cccc) \ + /* c_tPos is unsigned, hence test < 0 is pointless. */ \ + if (s->tPos >= (UInt32)100000 * (UInt32)s->blockSize100k) return True; \ + s->tPos = s->tt[s->tPos]; \ + cccc = (UChar)(s->tPos & 0xff); \ + s->tPos >>= 8; + +#define BZ_GET_FAST_C(cccc) \ + /* c_tPos is unsigned, hence test < 0 is pointless. */ \ + if (c_tPos >= (UInt32)100000 * (UInt32)ro_blockSize100k) return True; \ + c_tPos = c_tt[c_tPos]; \ + cccc = (UChar)(c_tPos & 0xff); \ + c_tPos >>= 8; + +#define SET_LL4(i,n) \ + { if (((i) & 0x1) == 0) \ + s->ll4[(i) >> 1] = (s->ll4[(i) >> 1] & 0xf0) | (n); else \ + s->ll4[(i) >> 1] = (s->ll4[(i) >> 1] & 0x0f) | ((n) << 4); \ + } + +#define GET_LL4(i) \ + ((((UInt32)(s->ll4[(i) >> 1])) >> (((i) << 2) & 0x4)) & 0xF) + +#define SET_LL(i,n) \ + { s->ll16[i] = (UInt16)(n & 0x0000ffff); \ + SET_LL4(i, n >> 16); \ + } + +#define GET_LL(i) \ + (((UInt32)s->ll16[i]) | (GET_LL4(i) << 16)) + +#define BZ_GET_SMALL(cccc) \ + /* c_tPos is unsigned, hence test < 0 is pointless. */ \ + if (s->tPos >= (UInt32)100000 * (UInt32)s->blockSize100k) return True; \ + cccc = BZ2_indexIntoF ( s->tPos, s->cftab ); \ + s->tPos = GET_LL(s->tPos); + + +/*-- externs for decompression. --*/ + +extern Int32 +BZ2_indexIntoF ( Int32, Int32* ); + +extern Int32 +BZ2_decompress ( DState* ); + +extern void +BZ2_hbCreateDecodeTables ( Int32*, Int32*, Int32*, UChar*, + Int32, Int32, Int32 ); + + +#endif + + +/*-- BZ_NO_STDIO seems to make NULL disappear on some platforms. --*/ + +#ifdef BZ_NO_STDIO +#ifndef NULL +#define NULL 0 +#endif +#endif + + +/*-------------------------------------------------------------*/ +/*--- end bzlib_private.h ---*/ +/*-------------------------------------------------------------*/ diff --git a/dep/StormLib/src/bzip2/compress.c b/dep/StormLib/src/bzip2/compress.c new file mode 100644 index 0000000000..8c80a07970 --- /dev/null +++ b/dep/StormLib/src/bzip2/compress.c @@ -0,0 +1,672 @@ + +/*-------------------------------------------------------------*/ +/*--- Compression machinery (not incl block sorting) ---*/ +/*--- compress.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.5 of 10 December 2007 + Copyright (C) 1996-2007 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +/* CHANGES + 0.9.0 -- original version. + 0.9.0a/b -- no changes in this file. + 0.9.0c -- changed setting of nGroups in sendMTFValues() + so as to do a bit better on small files +*/ + +#include "bzlib_private.h" + + +/*---------------------------------------------------*/ +/*--- Bit stream I/O ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +void BZ2_bsInitWrite ( EState* s ) +{ + s->bsLive = 0; + s->bsBuff = 0; +} + + +/*---------------------------------------------------*/ +static +void bsFinishWrite ( EState* s ) +{ + while (s->bsLive > 0) { + s->zbits[s->numZ] = (UChar)(s->bsBuff >> 24); + s->numZ++; + s->bsBuff <<= 8; + s->bsLive -= 8; + } +} + + +/*---------------------------------------------------*/ +#define bsNEEDW(nz) \ +{ \ + while (s->bsLive >= 8) { \ + s->zbits[s->numZ] \ + = (UChar)(s->bsBuff >> 24); \ + s->numZ++; \ + s->bsBuff <<= 8; \ + s->bsLive -= 8; \ + } \ +} + + +/*---------------------------------------------------*/ +static +__inline__ +void bsW ( EState* s, Int32 n, UInt32 v ) +{ + bsNEEDW ( n ); + s->bsBuff |= (v << (32 - s->bsLive - n)); + s->bsLive += n; +} + + +/*---------------------------------------------------*/ +static +void bsPutUInt32 ( EState* s, UInt32 u ) +{ + bsW ( s, 8, (u >> 24) & 0xffL ); + bsW ( s, 8, (u >> 16) & 0xffL ); + bsW ( s, 8, (u >> 8) & 0xffL ); + bsW ( s, 8, u & 0xffL ); +} + + +/*---------------------------------------------------*/ +static +void bsPutUChar ( EState* s, UChar c ) +{ + bsW( s, 8, (UInt32)c ); +} + + +/*---------------------------------------------------*/ +/*--- The back end proper ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +static +void makeMaps_e ( EState* s ) +{ + Int32 i; + s->nInUse = 0; + for (i = 0; i < 256; i++) + if (s->inUse[i]) { + s->unseqToSeq[i] = s->nInUse; + s->nInUse++; + } +} + + +/*---------------------------------------------------*/ +static +void generateMTFValues ( EState* s ) +{ + UChar yy[256]; + Int32 i, j; + Int32 zPend; + Int32 wr; + Int32 EOB; + + /* + After sorting (eg, here), + s->arr1 [ 0 .. s->nblock-1 ] holds sorted order, + and + ((UChar*)s->arr2) [ 0 .. s->nblock-1 ] + holds the original block data. + + The first thing to do is generate the MTF values, + and put them in + ((UInt16*)s->arr1) [ 0 .. s->nblock-1 ]. + Because there are strictly fewer or equal MTF values + than block values, ptr values in this area are overwritten + with MTF values only when they are no longer needed. + + The final compressed bitstream is generated into the + area starting at + (UChar*) (&((UChar*)s->arr2)[s->nblock]) + + These storage aliases are set up in bzCompressInit(), + except for the last one, which is arranged in + compressBlock(). + */ + UInt32* ptr = s->ptr; + UChar* block = s->block; + UInt16* mtfv = s->mtfv; + + makeMaps_e ( s ); + EOB = s->nInUse+1; + + for (i = 0; i <= EOB; i++) s->mtfFreq[i] = 0; + + wr = 0; + zPend = 0; + for (i = 0; i < s->nInUse; i++) yy[i] = (UChar) i; + + for (i = 0; i < s->nblock; i++) { + UChar ll_i; + AssertD ( wr <= i, "generateMTFValues(1)" ); + j = ptr[i]-1; if (j < 0) j += s->nblock; + ll_i = s->unseqToSeq[block[j]]; + AssertD ( ll_i < s->nInUse, "generateMTFValues(2a)" ); + + if (yy[0] == ll_i) { + zPend++; + } else { + + if (zPend > 0) { + zPend--; + while (True) { + if (zPend & 1) { + mtfv[wr] = BZ_RUNB; wr++; + s->mtfFreq[BZ_RUNB]++; + } else { + mtfv[wr] = BZ_RUNA; wr++; + s->mtfFreq[BZ_RUNA]++; + } + if (zPend < 2) break; + zPend = (zPend - 2) / 2; + }; + zPend = 0; + } + { + register UChar rtmp; + register UChar* ryy_j; + register UChar rll_i; + rtmp = yy[1]; + yy[1] = yy[0]; + ryy_j = &(yy[1]); + rll_i = ll_i; + while ( rll_i != rtmp ) { + register UChar rtmp2; + ryy_j++; + rtmp2 = rtmp; + rtmp = *ryy_j; + *ryy_j = rtmp2; + }; + yy[0] = rtmp; + j = ryy_j - &(yy[0]); + mtfv[wr] = j+1; wr++; s->mtfFreq[j+1]++; + } + + } + } + + if (zPend > 0) { + zPend--; + while (True) { + if (zPend & 1) { + mtfv[wr] = BZ_RUNB; wr++; + s->mtfFreq[BZ_RUNB]++; + } else { + mtfv[wr] = BZ_RUNA; wr++; + s->mtfFreq[BZ_RUNA]++; + } + if (zPend < 2) break; + zPend = (zPend - 2) / 2; + }; + zPend = 0; + } + + mtfv[wr] = EOB; wr++; s->mtfFreq[EOB]++; + + s->nMTF = wr; +} + + +/*---------------------------------------------------*/ +#define BZ_LESSER_ICOST 0 +#define BZ_GREATER_ICOST 15 + +static +void sendMTFValues ( EState* s ) +{ + Int32 v, t, i, j, gs, ge, totc, bt, bc, iter; + Int32 nSelectors, alphaSize, minLen, maxLen, selCtr; + Int32 nGroups, nBytes; + + /*-- + UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + is a global since the decoder also needs it. + + Int32 code[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 rfreq[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + are also globals only used in this proc. + Made global to keep stack frame size small. + --*/ + + + UInt16 cost[BZ_N_GROUPS]; + Int32 fave[BZ_N_GROUPS]; + + UInt16* mtfv = s->mtfv; + + if (s->verbosity >= 3) + VPrintf3( " %d in block, %d after MTF & 1-2 coding, " + "%d+2 syms in use\n", + s->nblock, s->nMTF, s->nInUse ); + + alphaSize = s->nInUse+2; + for (t = 0; t < BZ_N_GROUPS; t++) + for (v = 0; v < alphaSize; v++) + s->len[t][v] = BZ_GREATER_ICOST; + + /*--- Decide how many coding tables to use ---*/ + AssertH ( s->nMTF > 0, 3001 ); + if (s->nMTF < 200) nGroups = 2; else + if (s->nMTF < 600) nGroups = 3; else + if (s->nMTF < 1200) nGroups = 4; else + if (s->nMTF < 2400) nGroups = 5; else + nGroups = 6; + + /*--- Generate an initial set of coding tables ---*/ + { + Int32 nPart, remF, tFreq, aFreq; + + nPart = nGroups; + remF = s->nMTF; + gs = 0; + while (nPart > 0) { + tFreq = remF / nPart; + ge = gs-1; + aFreq = 0; + while (aFreq < tFreq && ge < alphaSize-1) { + ge++; + aFreq += s->mtfFreq[ge]; + } + + if (ge > gs + && nPart != nGroups && nPart != 1 + && ((nGroups-nPart) % 2 == 1)) { + aFreq -= s->mtfFreq[ge]; + ge--; + } + + if (s->verbosity >= 3) + VPrintf5( " initial group %d, [%d .. %d], " + "has %d syms (%4.1f%%)\n", + nPart, gs, ge, aFreq, + (100.0 * (float)aFreq) / (float)(s->nMTF) ); + + for (v = 0; v < alphaSize; v++) + if (v >= gs && v <= ge) + s->len[nPart-1][v] = BZ_LESSER_ICOST; else + s->len[nPart-1][v] = BZ_GREATER_ICOST; + + nPart--; + gs = ge+1; + remF -= aFreq; + } + } + + /*--- + Iterate up to BZ_N_ITERS times to improve the tables. + ---*/ + for (iter = 0; iter < BZ_N_ITERS; iter++) { + + for (t = 0; t < nGroups; t++) fave[t] = 0; + + for (t = 0; t < nGroups; t++) + for (v = 0; v < alphaSize; v++) + s->rfreq[t][v] = 0; + + /*--- + Set up an auxiliary length table which is used to fast-track + the common case (nGroups == 6). + ---*/ + if (nGroups == 6) { + for (v = 0; v < alphaSize; v++) { + s->len_pack[v][0] = (s->len[1][v] << 16) | s->len[0][v]; + s->len_pack[v][1] = (s->len[3][v] << 16) | s->len[2][v]; + s->len_pack[v][2] = (s->len[5][v] << 16) | s->len[4][v]; + } + } + + nSelectors = 0; + totc = 0; + gs = 0; + while (True) { + + /*--- Set group start & end marks. --*/ + if (gs >= s->nMTF) break; + ge = gs + BZ_G_SIZE - 1; + if (ge >= s->nMTF) ge = s->nMTF-1; + + /*-- + Calculate the cost of this group as coded + by each of the coding tables. + --*/ + for (t = 0; t < nGroups; t++) cost[t] = 0; + + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ + register UInt32 cost01, cost23, cost45; + register UInt16 icv; + cost01 = cost23 = cost45 = 0; + +# define BZ_ITER(nn) \ + icv = mtfv[gs+(nn)]; \ + cost01 += s->len_pack[icv][0]; \ + cost23 += s->len_pack[icv][1]; \ + cost45 += s->len_pack[icv][2]; \ + + BZ_ITER(0); BZ_ITER(1); BZ_ITER(2); BZ_ITER(3); BZ_ITER(4); + BZ_ITER(5); BZ_ITER(6); BZ_ITER(7); BZ_ITER(8); BZ_ITER(9); + BZ_ITER(10); BZ_ITER(11); BZ_ITER(12); BZ_ITER(13); BZ_ITER(14); + BZ_ITER(15); BZ_ITER(16); BZ_ITER(17); BZ_ITER(18); BZ_ITER(19); + BZ_ITER(20); BZ_ITER(21); BZ_ITER(22); BZ_ITER(23); BZ_ITER(24); + BZ_ITER(25); BZ_ITER(26); BZ_ITER(27); BZ_ITER(28); BZ_ITER(29); + BZ_ITER(30); BZ_ITER(31); BZ_ITER(32); BZ_ITER(33); BZ_ITER(34); + BZ_ITER(35); BZ_ITER(36); BZ_ITER(37); BZ_ITER(38); BZ_ITER(39); + BZ_ITER(40); BZ_ITER(41); BZ_ITER(42); BZ_ITER(43); BZ_ITER(44); + BZ_ITER(45); BZ_ITER(46); BZ_ITER(47); BZ_ITER(48); BZ_ITER(49); + +# undef BZ_ITER + + cost[0] = cost01 & 0xffff; cost[1] = cost01 >> 16; + cost[2] = cost23 & 0xffff; cost[3] = cost23 >> 16; + cost[4] = cost45 & 0xffff; cost[5] = cost45 >> 16; + + } else { + /*--- slow version which correctly handles all situations ---*/ + for (i = gs; i <= ge; i++) { + UInt16 icv = mtfv[i]; + for (t = 0; t < nGroups; t++) cost[t] += s->len[t][icv]; + } + } + + /*-- + Find the coding table which is best for this group, + and record its identity in the selector table. + --*/ + bc = 999999999; bt = -1; + for (t = 0; t < nGroups; t++) + if (cost[t] < bc) { bc = cost[t]; bt = t; }; + totc += bc; + fave[bt]++; + s->selector[nSelectors] = bt; + nSelectors++; + + /*-- + Increment the symbol frequencies for the selected table. + --*/ + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ + +# define BZ_ITUR(nn) s->rfreq[bt][ mtfv[gs+(nn)] ]++ + + BZ_ITUR(0); BZ_ITUR(1); BZ_ITUR(2); BZ_ITUR(3); BZ_ITUR(4); + BZ_ITUR(5); BZ_ITUR(6); BZ_ITUR(7); BZ_ITUR(8); BZ_ITUR(9); + BZ_ITUR(10); BZ_ITUR(11); BZ_ITUR(12); BZ_ITUR(13); BZ_ITUR(14); + BZ_ITUR(15); BZ_ITUR(16); BZ_ITUR(17); BZ_ITUR(18); BZ_ITUR(19); + BZ_ITUR(20); BZ_ITUR(21); BZ_ITUR(22); BZ_ITUR(23); BZ_ITUR(24); + BZ_ITUR(25); BZ_ITUR(26); BZ_ITUR(27); BZ_ITUR(28); BZ_ITUR(29); + BZ_ITUR(30); BZ_ITUR(31); BZ_ITUR(32); BZ_ITUR(33); BZ_ITUR(34); + BZ_ITUR(35); BZ_ITUR(36); BZ_ITUR(37); BZ_ITUR(38); BZ_ITUR(39); + BZ_ITUR(40); BZ_ITUR(41); BZ_ITUR(42); BZ_ITUR(43); BZ_ITUR(44); + BZ_ITUR(45); BZ_ITUR(46); BZ_ITUR(47); BZ_ITUR(48); BZ_ITUR(49); + +# undef BZ_ITUR + + } else { + /*--- slow version which correctly handles all situations ---*/ + for (i = gs; i <= ge; i++) + s->rfreq[bt][ mtfv[i] ]++; + } + + gs = ge+1; + } + if (s->verbosity >= 3) { + VPrintf2 ( " pass %d: size is %d, grp uses are ", + iter+1, totc/8 ); + for (t = 0; t < nGroups; t++) + VPrintf1 ( "%d ", fave[t] ); + VPrintf0 ( "\n" ); + } + + /*-- + Recompute the tables based on the accumulated frequencies. + --*/ + /* maxLen was changed from 20 to 17 in bzip2-1.0.3. See + comment in huffman.c for details. */ + for (t = 0; t < nGroups; t++) + BZ2_hbMakeCodeLengths ( &(s->len[t][0]), &(s->rfreq[t][0]), + alphaSize, 17 /*20*/ ); + } + + + AssertH( nGroups < 8, 3002 ); + AssertH( nSelectors < 32768 && + nSelectors <= (2 + (900000 / BZ_G_SIZE)), + 3003 ); + + + /*--- Compute MTF values for the selectors. ---*/ + { + UChar pos[BZ_N_GROUPS], ll_i, tmp2, tmp; + for (i = 0; i < nGroups; i++) pos[i] = i; + for (i = 0; i < nSelectors; i++) { + ll_i = s->selector[i]; + j = 0; + tmp = pos[j]; + while ( ll_i != tmp ) { + j++; + tmp2 = tmp; + tmp = pos[j]; + pos[j] = tmp2; + }; + pos[0] = tmp; + s->selectorMtf[i] = j; + } + }; + + /*--- Assign actual codes for the tables. --*/ + for (t = 0; t < nGroups; t++) { + minLen = 32; + maxLen = 0; + for (i = 0; i < alphaSize; i++) { + if (s->len[t][i] > maxLen) maxLen = s->len[t][i]; + if (s->len[t][i] < minLen) minLen = s->len[t][i]; + } + AssertH ( !(maxLen > 17 /*20*/ ), 3004 ); + AssertH ( !(minLen < 1), 3005 ); + BZ2_hbAssignCodes ( &(s->code[t][0]), &(s->len[t][0]), + minLen, maxLen, alphaSize ); + } + + /*--- Transmit the mapping table. ---*/ + { + Bool inUse16[16]; + for (i = 0; i < 16; i++) { + inUse16[i] = False; + for (j = 0; j < 16; j++) + if (s->inUse[i * 16 + j]) inUse16[i] = True; + } + + nBytes = s->numZ; + for (i = 0; i < 16; i++) + if (inUse16[i]) bsW(s,1,1); else bsW(s,1,0); + + for (i = 0; i < 16; i++) + if (inUse16[i]) + for (j = 0; j < 16; j++) { + if (s->inUse[i * 16 + j]) bsW(s,1,1); else bsW(s,1,0); + } + + if (s->verbosity >= 3) + VPrintf1( " bytes: mapping %d, ", s->numZ-nBytes ); + } + + /*--- Now the selectors. ---*/ + nBytes = s->numZ; + bsW ( s, 3, nGroups ); + bsW ( s, 15, nSelectors ); + for (i = 0; i < nSelectors; i++) { + for (j = 0; j < s->selectorMtf[i]; j++) bsW(s,1,1); + bsW(s,1,0); + } + if (s->verbosity >= 3) + VPrintf1( "selectors %d, ", s->numZ-nBytes ); + + /*--- Now the coding tables. ---*/ + nBytes = s->numZ; + + for (t = 0; t < nGroups; t++) { + Int32 curr = s->len[t][0]; + bsW ( s, 5, curr ); + for (i = 0; i < alphaSize; i++) { + while (curr < s->len[t][i]) { bsW(s,2,2); curr++; /* 10 */ }; + while (curr > s->len[t][i]) { bsW(s,2,3); curr--; /* 11 */ }; + bsW ( s, 1, 0 ); + } + } + + if (s->verbosity >= 3) + VPrintf1 ( "code lengths %d, ", s->numZ-nBytes ); + + /*--- And finally, the block data proper ---*/ + nBytes = s->numZ; + selCtr = 0; + gs = 0; + while (True) { + if (gs >= s->nMTF) break; + ge = gs + BZ_G_SIZE - 1; + if (ge >= s->nMTF) ge = s->nMTF-1; + AssertH ( s->selector[selCtr] < nGroups, 3006 ); + + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ + UInt16 mtfv_i; + UChar* s_len_sel_selCtr + = &(s->len[s->selector[selCtr]][0]); + Int32* s_code_sel_selCtr + = &(s->code[s->selector[selCtr]][0]); + +# define BZ_ITAH(nn) \ + mtfv_i = mtfv[gs+(nn)]; \ + bsW ( s, \ + s_len_sel_selCtr[mtfv_i], \ + s_code_sel_selCtr[mtfv_i] ) + + BZ_ITAH(0); BZ_ITAH(1); BZ_ITAH(2); BZ_ITAH(3); BZ_ITAH(4); + BZ_ITAH(5); BZ_ITAH(6); BZ_ITAH(7); BZ_ITAH(8); BZ_ITAH(9); + BZ_ITAH(10); BZ_ITAH(11); BZ_ITAH(12); BZ_ITAH(13); BZ_ITAH(14); + BZ_ITAH(15); BZ_ITAH(16); BZ_ITAH(17); BZ_ITAH(18); BZ_ITAH(19); + BZ_ITAH(20); BZ_ITAH(21); BZ_ITAH(22); BZ_ITAH(23); BZ_ITAH(24); + BZ_ITAH(25); BZ_ITAH(26); BZ_ITAH(27); BZ_ITAH(28); BZ_ITAH(29); + BZ_ITAH(30); BZ_ITAH(31); BZ_ITAH(32); BZ_ITAH(33); BZ_ITAH(34); + BZ_ITAH(35); BZ_ITAH(36); BZ_ITAH(37); BZ_ITAH(38); BZ_ITAH(39); + BZ_ITAH(40); BZ_ITAH(41); BZ_ITAH(42); BZ_ITAH(43); BZ_ITAH(44); + BZ_ITAH(45); BZ_ITAH(46); BZ_ITAH(47); BZ_ITAH(48); BZ_ITAH(49); + +# undef BZ_ITAH + + } else { + /*--- slow version which correctly handles all situations ---*/ + for (i = gs; i <= ge; i++) { + bsW ( s, + s->len [s->selector[selCtr]] [mtfv[i]], + s->code [s->selector[selCtr]] [mtfv[i]] ); + } + } + + + gs = ge+1; + selCtr++; + } + AssertH( selCtr == nSelectors, 3007 ); + + if (s->verbosity >= 3) + VPrintf1( "codes %d\n", s->numZ-nBytes ); +} + + +/*---------------------------------------------------*/ +void BZ2_compressBlock ( EState* s, Bool is_last_block ) +{ + if (s->nblock > 0) { + + BZ_FINALISE_CRC ( s->blockCRC ); + s->combinedCRC = (s->combinedCRC << 1) | (s->combinedCRC >> 31); + s->combinedCRC ^= s->blockCRC; + if (s->blockNo > 1) s->numZ = 0; + + if (s->verbosity >= 2) + VPrintf4( " block %d: crc = 0x%08x, " + "combined CRC = 0x%08x, size = %d\n", + s->blockNo, s->blockCRC, s->combinedCRC, s->nblock ); + + BZ2_blockSort ( s ); + } + + s->zbits = (UChar*) (&((UChar*)s->arr2)[s->nblock]); + + /*-- If this is the first block, create the stream header. --*/ + if (s->blockNo == 1) { + BZ2_bsInitWrite ( s ); + bsPutUChar ( s, BZ_HDR_B ); + bsPutUChar ( s, BZ_HDR_Z ); + bsPutUChar ( s, BZ_HDR_h ); + bsPutUChar ( s, (UChar)(BZ_HDR_0 + s->blockSize100k) ); + } + + if (s->nblock > 0) { + + bsPutUChar ( s, 0x31 ); bsPutUChar ( s, 0x41 ); + bsPutUChar ( s, 0x59 ); bsPutUChar ( s, 0x26 ); + bsPutUChar ( s, 0x53 ); bsPutUChar ( s, 0x59 ); + + /*-- Now the block's CRC, so it is in a known place. --*/ + bsPutUInt32 ( s, s->blockCRC ); + + /*-- + Now a single bit indicating (non-)randomisation. + As of version 0.9.5, we use a better sorting algorithm + which makes randomisation unnecessary. So always set + the randomised bit to 'no'. Of course, the decoder + still needs to be able to handle randomised blocks + so as to maintain backwards compatibility with + older versions of bzip2. + --*/ + bsW(s,1,0); + + bsW ( s, 24, s->origPtr ); + generateMTFValues ( s ); + sendMTFValues ( s ); + } + + + /*-- If this is the last block, add the stream trailer. --*/ + if (is_last_block) { + + bsPutUChar ( s, 0x17 ); bsPutUChar ( s, 0x72 ); + bsPutUChar ( s, 0x45 ); bsPutUChar ( s, 0x38 ); + bsPutUChar ( s, 0x50 ); bsPutUChar ( s, 0x90 ); + bsPutUInt32 ( s, s->combinedCRC ); + if (s->verbosity >= 2) + VPrintf1( " final combined CRC = 0x%08x\n ", s->combinedCRC ); + bsFinishWrite ( s ); + } +} + + +/*-------------------------------------------------------------*/ +/*--- end compress.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/dep/StormLib/src/bzip2/crctable.c b/dep/StormLib/src/bzip2/crctable.c new file mode 100644 index 0000000000..215687b2c0 --- /dev/null +++ b/dep/StormLib/src/bzip2/crctable.c @@ -0,0 +1,104 @@ + +/*-------------------------------------------------------------*/ +/*--- Table for doing CRCs ---*/ +/*--- crctable.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.5 of 10 December 2007 + Copyright (C) 1996-2007 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + +/*-- + I think this is an implementation of the AUTODIN-II, + Ethernet & FDDI 32-bit CRC standard. Vaguely derived + from code by Rob Warnock, in Section 51 of the + comp.compression FAQ. +--*/ + +UInt32 BZ2_crc32Table[256] = { + + /*-- Ugly, innit? --*/ + + 0x00000000L, 0x04c11db7L, 0x09823b6eL, 0x0d4326d9L, + 0x130476dcL, 0x17c56b6bL, 0x1a864db2L, 0x1e475005L, + 0x2608edb8L, 0x22c9f00fL, 0x2f8ad6d6L, 0x2b4bcb61L, + 0x350c9b64L, 0x31cd86d3L, 0x3c8ea00aL, 0x384fbdbdL, + 0x4c11db70L, 0x48d0c6c7L, 0x4593e01eL, 0x4152fda9L, + 0x5f15adacL, 0x5bd4b01bL, 0x569796c2L, 0x52568b75L, + 0x6a1936c8L, 0x6ed82b7fL, 0x639b0da6L, 0x675a1011L, + 0x791d4014L, 0x7ddc5da3L, 0x709f7b7aL, 0x745e66cdL, + 0x9823b6e0L, 0x9ce2ab57L, 0x91a18d8eL, 0x95609039L, + 0x8b27c03cL, 0x8fe6dd8bL, 0x82a5fb52L, 0x8664e6e5L, + 0xbe2b5b58L, 0xbaea46efL, 0xb7a96036L, 0xb3687d81L, + 0xad2f2d84L, 0xa9ee3033L, 0xa4ad16eaL, 0xa06c0b5dL, + 0xd4326d90L, 0xd0f37027L, 0xddb056feL, 0xd9714b49L, + 0xc7361b4cL, 0xc3f706fbL, 0xceb42022L, 0xca753d95L, + 0xf23a8028L, 0xf6fb9d9fL, 0xfbb8bb46L, 0xff79a6f1L, + 0xe13ef6f4L, 0xe5ffeb43L, 0xe8bccd9aL, 0xec7dd02dL, + 0x34867077L, 0x30476dc0L, 0x3d044b19L, 0x39c556aeL, + 0x278206abL, 0x23431b1cL, 0x2e003dc5L, 0x2ac12072L, + 0x128e9dcfL, 0x164f8078L, 0x1b0ca6a1L, 0x1fcdbb16L, + 0x018aeb13L, 0x054bf6a4L, 0x0808d07dL, 0x0cc9cdcaL, + 0x7897ab07L, 0x7c56b6b0L, 0x71159069L, 0x75d48ddeL, + 0x6b93dddbL, 0x6f52c06cL, 0x6211e6b5L, 0x66d0fb02L, + 0x5e9f46bfL, 0x5a5e5b08L, 0x571d7dd1L, 0x53dc6066L, + 0x4d9b3063L, 0x495a2dd4L, 0x44190b0dL, 0x40d816baL, + 0xaca5c697L, 0xa864db20L, 0xa527fdf9L, 0xa1e6e04eL, + 0xbfa1b04bL, 0xbb60adfcL, 0xb6238b25L, 0xb2e29692L, + 0x8aad2b2fL, 0x8e6c3698L, 0x832f1041L, 0x87ee0df6L, + 0x99a95df3L, 0x9d684044L, 0x902b669dL, 0x94ea7b2aL, + 0xe0b41de7L, 0xe4750050L, 0xe9362689L, 0xedf73b3eL, + 0xf3b06b3bL, 0xf771768cL, 0xfa325055L, 0xfef34de2L, + 0xc6bcf05fL, 0xc27dede8L, 0xcf3ecb31L, 0xcbffd686L, + 0xd5b88683L, 0xd1799b34L, 0xdc3abdedL, 0xd8fba05aL, + 0x690ce0eeL, 0x6dcdfd59L, 0x608edb80L, 0x644fc637L, + 0x7a089632L, 0x7ec98b85L, 0x738aad5cL, 0x774bb0ebL, + 0x4f040d56L, 0x4bc510e1L, 0x46863638L, 0x42472b8fL, + 0x5c007b8aL, 0x58c1663dL, 0x558240e4L, 0x51435d53L, + 0x251d3b9eL, 0x21dc2629L, 0x2c9f00f0L, 0x285e1d47L, + 0x36194d42L, 0x32d850f5L, 0x3f9b762cL, 0x3b5a6b9bL, + 0x0315d626L, 0x07d4cb91L, 0x0a97ed48L, 0x0e56f0ffL, + 0x1011a0faL, 0x14d0bd4dL, 0x19939b94L, 0x1d528623L, + 0xf12f560eL, 0xf5ee4bb9L, 0xf8ad6d60L, 0xfc6c70d7L, + 0xe22b20d2L, 0xe6ea3d65L, 0xeba91bbcL, 0xef68060bL, + 0xd727bbb6L, 0xd3e6a601L, 0xdea580d8L, 0xda649d6fL, + 0xc423cd6aL, 0xc0e2d0ddL, 0xcda1f604L, 0xc960ebb3L, + 0xbd3e8d7eL, 0xb9ff90c9L, 0xb4bcb610L, 0xb07daba7L, + 0xae3afba2L, 0xaafbe615L, 0xa7b8c0ccL, 0xa379dd7bL, + 0x9b3660c6L, 0x9ff77d71L, 0x92b45ba8L, 0x9675461fL, + 0x8832161aL, 0x8cf30badL, 0x81b02d74L, 0x857130c3L, + 0x5d8a9099L, 0x594b8d2eL, 0x5408abf7L, 0x50c9b640L, + 0x4e8ee645L, 0x4a4ffbf2L, 0x470cdd2bL, 0x43cdc09cL, + 0x7b827d21L, 0x7f436096L, 0x7200464fL, 0x76c15bf8L, + 0x68860bfdL, 0x6c47164aL, 0x61043093L, 0x65c52d24L, + 0x119b4be9L, 0x155a565eL, 0x18197087L, 0x1cd86d30L, + 0x029f3d35L, 0x065e2082L, 0x0b1d065bL, 0x0fdc1becL, + 0x3793a651L, 0x3352bbe6L, 0x3e119d3fL, 0x3ad08088L, + 0x2497d08dL, 0x2056cd3aL, 0x2d15ebe3L, 0x29d4f654L, + 0xc5a92679L, 0xc1683bceL, 0xcc2b1d17L, 0xc8ea00a0L, + 0xd6ad50a5L, 0xd26c4d12L, 0xdf2f6bcbL, 0xdbee767cL, + 0xe3a1cbc1L, 0xe760d676L, 0xea23f0afL, 0xeee2ed18L, + 0xf0a5bd1dL, 0xf464a0aaL, 0xf9278673L, 0xfde69bc4L, + 0x89b8fd09L, 0x8d79e0beL, 0x803ac667L, 0x84fbdbd0L, + 0x9abc8bd5L, 0x9e7d9662L, 0x933eb0bbL, 0x97ffad0cL, + 0xafb010b1L, 0xab710d06L, 0xa6322bdfL, 0xa2f33668L, + 0xbcb4666dL, 0xb8757bdaL, 0xb5365d03L, 0xb1f740b4L +}; + + +/*-------------------------------------------------------------*/ +/*--- end crctable.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/dep/StormLib/src/bzip2/decompress.c b/dep/StormLib/src/bzip2/decompress.c new file mode 100644 index 0000000000..bba5e0fa36 --- /dev/null +++ b/dep/StormLib/src/bzip2/decompress.c @@ -0,0 +1,626 @@ + +/*-------------------------------------------------------------*/ +/*--- Decompression machinery ---*/ +/*--- decompress.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.5 of 10 December 2007 + Copyright (C) 1996-2007 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + + +/*---------------------------------------------------*/ +static +void makeMaps_d ( DState* s ) +{ + Int32 i; + s->nInUse = 0; + for (i = 0; i < 256; i++) + if (s->inUse[i]) { + s->seqToUnseq[s->nInUse] = i; + s->nInUse++; + } +} + + +/*---------------------------------------------------*/ +#define RETURN(rrr) \ + { retVal = rrr; goto save_state_and_return; }; + +#define GET_BITS(lll,vvv,nnn) \ + case lll: s->state = lll; \ + while (True) { \ + if (s->bsLive >= nnn) { \ + UInt32 v; \ + v = (s->bsBuff >> \ + (s->bsLive-nnn)) & ((1 << nnn)-1); \ + s->bsLive -= nnn; \ + vvv = v; \ + break; \ + } \ + if (s->strm->avail_in == 0) RETURN(BZ_OK); \ + s->bsBuff \ + = (s->bsBuff << 8) | \ + ((UInt32) \ + (*((UChar*)(s->strm->next_in)))); \ + s->bsLive += 8; \ + s->strm->next_in++; \ + s->strm->avail_in--; \ + s->strm->total_in_lo32++; \ + if (s->strm->total_in_lo32 == 0) \ + s->strm->total_in_hi32++; \ + } + +#define GET_UCHAR(lll,uuu) \ + GET_BITS(lll,uuu,8) + +#define GET_BIT(lll,uuu) \ + GET_BITS(lll,uuu,1) + +/*---------------------------------------------------*/ +#define GET_MTF_VAL(label1,label2,lval) \ +{ \ + if (groupPos == 0) { \ + groupNo++; \ + if (groupNo >= nSelectors) \ + RETURN(BZ_DATA_ERROR); \ + groupPos = BZ_G_SIZE; \ + gSel = s->selector[groupNo]; \ + gMinlen = s->minLens[gSel]; \ + gLimit = &(s->limit[gSel][0]); \ + gPerm = &(s->perm[gSel][0]); \ + gBase = &(s->base[gSel][0]); \ + } \ + groupPos--; \ + zn = gMinlen; \ + GET_BITS(label1, zvec, zn); \ + while (1) { \ + if (zn > 20 /* the longest code */) \ + RETURN(BZ_DATA_ERROR); \ + if (zvec <= gLimit[zn]) break; \ + zn++; \ + GET_BIT(label2, zj); \ + zvec = (zvec << 1) | zj; \ + }; \ + if (zvec - gBase[zn] < 0 \ + || zvec - gBase[zn] >= BZ_MAX_ALPHA_SIZE) \ + RETURN(BZ_DATA_ERROR); \ + lval = gPerm[zvec - gBase[zn]]; \ +} + + +/*---------------------------------------------------*/ +Int32 BZ2_decompress ( DState* s ) +{ + UChar uc; + Int32 retVal; + Int32 minLen, maxLen; + bz_stream* strm = s->strm; + + /* stuff that needs to be saved/restored */ + Int32 i; + Int32 j; + Int32 t; + Int32 alphaSize; + Int32 nGroups; + Int32 nSelectors; + Int32 EOB; + Int32 groupNo; + Int32 groupPos; + Int32 nextSym; + Int32 nblockMAX; + Int32 nblock; + Int32 es; + Int32 N; + Int32 curr; + Int32 zt; + Int32 zn; + Int32 zvec; + Int32 zj; + Int32 gSel; + Int32 gMinlen; + Int32* gLimit; + Int32* gBase; + Int32* gPerm; + + if (s->state == BZ_X_MAGIC_1) { + /*initialise the save area*/ + s->save_i = 0; + s->save_j = 0; + s->save_t = 0; + s->save_alphaSize = 0; + s->save_nGroups = 0; + s->save_nSelectors = 0; + s->save_EOB = 0; + s->save_groupNo = 0; + s->save_groupPos = 0; + s->save_nextSym = 0; + s->save_nblockMAX = 0; + s->save_nblock = 0; + s->save_es = 0; + s->save_N = 0; + s->save_curr = 0; + s->save_zt = 0; + s->save_zn = 0; + s->save_zvec = 0; + s->save_zj = 0; + s->save_gSel = 0; + s->save_gMinlen = 0; + s->save_gLimit = NULL; + s->save_gBase = NULL; + s->save_gPerm = NULL; + } + + /*restore from the save area*/ + i = s->save_i; + j = s->save_j; + t = s->save_t; + alphaSize = s->save_alphaSize; + nGroups = s->save_nGroups; + nSelectors = s->save_nSelectors; + EOB = s->save_EOB; + groupNo = s->save_groupNo; + groupPos = s->save_groupPos; + nextSym = s->save_nextSym; + nblockMAX = s->save_nblockMAX; + nblock = s->save_nblock; + es = s->save_es; + N = s->save_N; + curr = s->save_curr; + zt = s->save_zt; + zn = s->save_zn; + zvec = s->save_zvec; + zj = s->save_zj; + gSel = s->save_gSel; + gMinlen = s->save_gMinlen; + gLimit = s->save_gLimit; + gBase = s->save_gBase; + gPerm = s->save_gPerm; + + retVal = BZ_OK; + + switch (s->state) { + + GET_UCHAR(BZ_X_MAGIC_1, uc); + if (uc != BZ_HDR_B) RETURN(BZ_DATA_ERROR_MAGIC); + + GET_UCHAR(BZ_X_MAGIC_2, uc); + if (uc != BZ_HDR_Z) RETURN(BZ_DATA_ERROR_MAGIC); + + GET_UCHAR(BZ_X_MAGIC_3, uc) + if (uc != BZ_HDR_h) RETURN(BZ_DATA_ERROR_MAGIC); + + GET_BITS(BZ_X_MAGIC_4, s->blockSize100k, 8) + if (s->blockSize100k < (BZ_HDR_0 + 1) || + s->blockSize100k > (BZ_HDR_0 + 9)) RETURN(BZ_DATA_ERROR_MAGIC); + s->blockSize100k -= BZ_HDR_0; + + if (s->smallDecompress) { + s->ll16 = BZALLOC( s->blockSize100k * 100000 * sizeof(UInt16) ); + s->ll4 = BZALLOC( + ((1 + s->blockSize100k * 100000) >> 1) * sizeof(UChar) + ); + if (s->ll16 == NULL || s->ll4 == NULL) RETURN(BZ_MEM_ERROR); + } else { + s->tt = BZALLOC( s->blockSize100k * 100000 * sizeof(Int32) ); + if (s->tt == NULL) RETURN(BZ_MEM_ERROR); + } + + GET_UCHAR(BZ_X_BLKHDR_1, uc); + + if (uc == 0x17) goto endhdr_2; + if (uc != 0x31) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_2, uc); + if (uc != 0x41) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_3, uc); + if (uc != 0x59) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_4, uc); + if (uc != 0x26) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_5, uc); + if (uc != 0x53) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_6, uc); + if (uc != 0x59) RETURN(BZ_DATA_ERROR); + + s->currBlockNo++; + if (s->verbosity >= 2) + VPrintf1 ( "\n [%d: huff+mtf ", s->currBlockNo ); + + s->storedBlockCRC = 0; + GET_UCHAR(BZ_X_BCRC_1, uc); + s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_BCRC_2, uc); + s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_BCRC_3, uc); + s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_BCRC_4, uc); + s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); + + GET_BITS(BZ_X_RANDBIT, s->blockRandomised, 1); + + s->origPtr = 0; + GET_UCHAR(BZ_X_ORIGPTR_1, uc); + s->origPtr = (s->origPtr << 8) | ((Int32)uc); + GET_UCHAR(BZ_X_ORIGPTR_2, uc); + s->origPtr = (s->origPtr << 8) | ((Int32)uc); + GET_UCHAR(BZ_X_ORIGPTR_3, uc); + s->origPtr = (s->origPtr << 8) | ((Int32)uc); + + if (s->origPtr < 0) + RETURN(BZ_DATA_ERROR); + if (s->origPtr > 10 + 100000*s->blockSize100k) + RETURN(BZ_DATA_ERROR); + + /*--- Receive the mapping table ---*/ + for (i = 0; i < 16; i++) { + GET_BIT(BZ_X_MAPPING_1, uc); + if (uc == 1) + s->inUse16[i] = True; else + s->inUse16[i] = False; + } + + for (i = 0; i < 256; i++) s->inUse[i] = False; + + for (i = 0; i < 16; i++) + if (s->inUse16[i]) + for (j = 0; j < 16; j++) { + GET_BIT(BZ_X_MAPPING_2, uc); + if (uc == 1) s->inUse[i * 16 + j] = True; + } + makeMaps_d ( s ); + if (s->nInUse == 0) RETURN(BZ_DATA_ERROR); + alphaSize = s->nInUse+2; + + /*--- Now the selectors ---*/ + GET_BITS(BZ_X_SELECTOR_1, nGroups, 3); + if (nGroups < 2 || nGroups > 6) RETURN(BZ_DATA_ERROR); + GET_BITS(BZ_X_SELECTOR_2, nSelectors, 15); + if (nSelectors < 1) RETURN(BZ_DATA_ERROR); + for (i = 0; i < nSelectors; i++) { + j = 0; + while (True) { + GET_BIT(BZ_X_SELECTOR_3, uc); + if (uc == 0) break; + j++; + if (j >= nGroups) RETURN(BZ_DATA_ERROR); + } + s->selectorMtf[i] = j; + } + + /*--- Undo the MTF values for the selectors. ---*/ + { + UChar pos[BZ_N_GROUPS], tmp, v; + for (v = 0; v < nGroups; v++) pos[v] = v; + + for (i = 0; i < nSelectors; i++) { + v = s->selectorMtf[i]; + tmp = pos[v]; + while (v > 0) { pos[v] = pos[v-1]; v--; } + pos[0] = tmp; + s->selector[i] = tmp; + } + } + + /*--- Now the coding tables ---*/ + for (t = 0; t < nGroups; t++) { + GET_BITS(BZ_X_CODING_1, curr, 5); + for (i = 0; i < alphaSize; i++) { + while (True) { + if (curr < 1 || curr > 20) RETURN(BZ_DATA_ERROR); + GET_BIT(BZ_X_CODING_2, uc); + if (uc == 0) break; + GET_BIT(BZ_X_CODING_3, uc); + if (uc == 0) curr++; else curr--; + } + s->len[t][i] = curr; + } + } + + /*--- Create the Huffman decoding tables ---*/ + for (t = 0; t < nGroups; t++) { + minLen = 32; + maxLen = 0; + for (i = 0; i < alphaSize; i++) { + if (s->len[t][i] > maxLen) maxLen = s->len[t][i]; + if (s->len[t][i] < minLen) minLen = s->len[t][i]; + } + BZ2_hbCreateDecodeTables ( + &(s->limit[t][0]), + &(s->base[t][0]), + &(s->perm[t][0]), + &(s->len[t][0]), + minLen, maxLen, alphaSize + ); + s->minLens[t] = minLen; + } + + /*--- Now the MTF values ---*/ + + EOB = s->nInUse+1; + nblockMAX = 100000 * s->blockSize100k; + groupNo = -1; + groupPos = 0; + + for (i = 0; i <= 255; i++) s->unzftab[i] = 0; + + /*-- MTF init --*/ + { + Int32 ii, jj, kk; + kk = MTFA_SIZE-1; + for (ii = 256 / MTFL_SIZE - 1; ii >= 0; ii--) { + for (jj = MTFL_SIZE-1; jj >= 0; jj--) { + s->mtfa[kk] = (UChar)(ii * MTFL_SIZE + jj); + kk--; + } + s->mtfbase[ii] = kk + 1; + } + } + /*-- end MTF init --*/ + + nblock = 0; + GET_MTF_VAL(BZ_X_MTF_1, BZ_X_MTF_2, nextSym); + + while (True) { + + if (nextSym == EOB) break; + + if (nextSym == BZ_RUNA || nextSym == BZ_RUNB) { + + es = -1; + N = 1; + do { + if (nextSym == BZ_RUNA) es = es + (0+1) * N; else + if (nextSym == BZ_RUNB) es = es + (1+1) * N; + N = N * 2; + GET_MTF_VAL(BZ_X_MTF_3, BZ_X_MTF_4, nextSym); + } + while (nextSym == BZ_RUNA || nextSym == BZ_RUNB); + + es++; + uc = s->seqToUnseq[ s->mtfa[s->mtfbase[0]] ]; + s->unzftab[uc] += es; + + if (s->smallDecompress) + while (es > 0) { + if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR); + s->ll16[nblock] = (UInt16)uc; + nblock++; + es--; + } + else + while (es > 0) { + if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR); + s->tt[nblock] = (UInt32)uc; + nblock++; + es--; + }; + + continue; + + } else { + + if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR); + + /*-- uc = MTF ( nextSym-1 ) --*/ + { + Int32 ii, jj, kk, pp, lno, off; + UInt32 nn; + nn = (UInt32)(nextSym - 1); + + if (nn < MTFL_SIZE) { + /* avoid general-case expense */ + pp = s->mtfbase[0]; + uc = s->mtfa[pp+nn]; + while (nn > 3) { + Int32 z = pp+nn; + s->mtfa[(z) ] = s->mtfa[(z)-1]; + s->mtfa[(z)-1] = s->mtfa[(z)-2]; + s->mtfa[(z)-2] = s->mtfa[(z)-3]; + s->mtfa[(z)-3] = s->mtfa[(z)-4]; + nn -= 4; + } + while (nn > 0) { + s->mtfa[(pp+nn)] = s->mtfa[(pp+nn)-1]; nn--; + }; + s->mtfa[pp] = uc; + } else { + /* general case */ + lno = nn / MTFL_SIZE; + off = nn % MTFL_SIZE; + pp = s->mtfbase[lno] + off; + uc = s->mtfa[pp]; + while (pp > s->mtfbase[lno]) { + s->mtfa[pp] = s->mtfa[pp-1]; pp--; + }; + s->mtfbase[lno]++; + while (lno > 0) { + s->mtfbase[lno]--; + s->mtfa[s->mtfbase[lno]] + = s->mtfa[s->mtfbase[lno-1] + MTFL_SIZE - 1]; + lno--; + } + s->mtfbase[0]--; + s->mtfa[s->mtfbase[0]] = uc; + if (s->mtfbase[0] == 0) { + kk = MTFA_SIZE-1; + for (ii = 256 / MTFL_SIZE-1; ii >= 0; ii--) { + for (jj = MTFL_SIZE-1; jj >= 0; jj--) { + s->mtfa[kk] = s->mtfa[s->mtfbase[ii] + jj]; + kk--; + } + s->mtfbase[ii] = kk + 1; + } + } + } + } + /*-- end uc = MTF ( nextSym-1 ) --*/ + + s->unzftab[s->seqToUnseq[uc]]++; + if (s->smallDecompress) + s->ll16[nblock] = (UInt16)(s->seqToUnseq[uc]); else + s->tt[nblock] = (UInt32)(s->seqToUnseq[uc]); + nblock++; + + GET_MTF_VAL(BZ_X_MTF_5, BZ_X_MTF_6, nextSym); + continue; + } + } + + /* Now we know what nblock is, we can do a better sanity + check on s->origPtr. + */ + if (s->origPtr < 0 || s->origPtr >= nblock) + RETURN(BZ_DATA_ERROR); + + /*-- Set up cftab to facilitate generation of T^(-1) --*/ + s->cftab[0] = 0; + for (i = 1; i <= 256; i++) s->cftab[i] = s->unzftab[i-1]; + for (i = 1; i <= 256; i++) s->cftab[i] += s->cftab[i-1]; + for (i = 0; i <= 256; i++) { + if (s->cftab[i] < 0 || s->cftab[i] > nblock) { + /* s->cftab[i] can legitimately be == nblock */ + RETURN(BZ_DATA_ERROR); + } + } + + s->state_out_len = 0; + s->state_out_ch = 0; + BZ_INITIALISE_CRC ( s->calculatedBlockCRC ); + s->state = BZ_X_OUTPUT; + if (s->verbosity >= 2) VPrintf0 ( "rt+rld" ); + + if (s->smallDecompress) { + + /*-- Make a copy of cftab, used in generation of T --*/ + for (i = 0; i <= 256; i++) s->cftabCopy[i] = s->cftab[i]; + + /*-- compute the T vector --*/ + for (i = 0; i < nblock; i++) { + uc = (UChar)(s->ll16[i]); + SET_LL(i, s->cftabCopy[uc]); + s->cftabCopy[uc]++; + } + + /*-- Compute T^(-1) by pointer reversal on T --*/ + i = s->origPtr; + j = GET_LL(i); + do { + Int32 tmp = GET_LL(j); + SET_LL(j, i); + i = j; + j = tmp; + } + while (i != s->origPtr); + + s->tPos = s->origPtr; + s->nblock_used = 0; + if (s->blockRandomised) { + BZ_RAND_INIT_MASK; + BZ_GET_SMALL(s->k0); s->nblock_used++; + BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK; + } else { + BZ_GET_SMALL(s->k0); s->nblock_used++; + } + + } else { + + /*-- compute the T^(-1) vector --*/ + for (i = 0; i < nblock; i++) { + uc = (UChar)(s->tt[i] & 0xff); + s->tt[s->cftab[uc]] |= (i << 8); + s->cftab[uc]++; + } + + s->tPos = s->tt[s->origPtr] >> 8; + s->nblock_used = 0; + if (s->blockRandomised) { + BZ_RAND_INIT_MASK; + BZ_GET_FAST(s->k0); s->nblock_used++; + BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK; + } else { + BZ_GET_FAST(s->k0); s->nblock_used++; + } + + } + + RETURN(BZ_OK); + + + + endhdr_2: + + GET_UCHAR(BZ_X_ENDHDR_2, uc); + if (uc != 0x72) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_ENDHDR_3, uc); + if (uc != 0x45) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_ENDHDR_4, uc); + if (uc != 0x38) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_ENDHDR_5, uc); + if (uc != 0x50) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_ENDHDR_6, uc); + if (uc != 0x90) RETURN(BZ_DATA_ERROR); + + s->storedCombinedCRC = 0; + GET_UCHAR(BZ_X_CCRC_1, uc); + s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_CCRC_2, uc); + s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_CCRC_3, uc); + s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_CCRC_4, uc); + s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); + + s->state = BZ_X_IDLE; + RETURN(BZ_STREAM_END); + + default: AssertH ( False, 4001 ); + } + + AssertH ( False, 4002 ); + + save_state_and_return: + + s->save_i = i; + s->save_j = j; + s->save_t = t; + s->save_alphaSize = alphaSize; + s->save_nGroups = nGroups; + s->save_nSelectors = nSelectors; + s->save_EOB = EOB; + s->save_groupNo = groupNo; + s->save_groupPos = groupPos; + s->save_nextSym = nextSym; + s->save_nblockMAX = nblockMAX; + s->save_nblock = nblock; + s->save_es = es; + s->save_N = N; + s->save_curr = curr; + s->save_zt = zt; + s->save_zn = zn; + s->save_zvec = zvec; + s->save_zj = zj; + s->save_gSel = gSel; + s->save_gMinlen = gMinlen; + s->save_gLimit = gLimit; + s->save_gBase = gBase; + s->save_gPerm = gPerm; + + return retVal; +} + + +/*-------------------------------------------------------------*/ +/*--- end decompress.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/dep/StormLib/src/bzip2/huffman.c b/dep/StormLib/src/bzip2/huffman.c new file mode 100644 index 0000000000..87e79e38af --- /dev/null +++ b/dep/StormLib/src/bzip2/huffman.c @@ -0,0 +1,205 @@ + +/*-------------------------------------------------------------*/ +/*--- Huffman coding low-level stuff ---*/ +/*--- huffman.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.5 of 10 December 2007 + Copyright (C) 1996-2007 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + +/*---------------------------------------------------*/ +#define WEIGHTOF(zz0) ((zz0) & 0xffffff00) +#define DEPTHOF(zz1) ((zz1) & 0x000000ff) +#define MYMAX(zz2,zz3) ((zz2) > (zz3) ? (zz2) : (zz3)) + +#define ADDWEIGHTS(zw1,zw2) \ + (WEIGHTOF(zw1)+WEIGHTOF(zw2)) | \ + (1 + MYMAX(DEPTHOF(zw1),DEPTHOF(zw2))) + +#define UPHEAP(z) \ +{ \ + Int32 zz, tmp; \ + zz = z; tmp = heap[zz]; \ + while (weight[tmp] < weight[heap[zz >> 1]]) { \ + heap[zz] = heap[zz >> 1]; \ + zz >>= 1; \ + } \ + heap[zz] = tmp; \ +} + +#define DOWNHEAP(z) \ +{ \ + Int32 zz, yy, tmp; \ + zz = z; tmp = heap[zz]; \ + while (True) { \ + yy = zz << 1; \ + if (yy > nHeap) break; \ + if (yy < nHeap && \ + weight[heap[yy+1]] < weight[heap[yy]]) \ + yy++; \ + if (weight[tmp] < weight[heap[yy]]) break; \ + heap[zz] = heap[yy]; \ + zz = yy; \ + } \ + heap[zz] = tmp; \ +} + + +/*---------------------------------------------------*/ +void BZ2_hbMakeCodeLengths ( UChar *len, + Int32 *freq, + Int32 alphaSize, + Int32 maxLen ) +{ + /*-- + Nodes and heap entries run from 1. Entry 0 + for both the heap and nodes is a sentinel. + --*/ + Int32 nNodes, nHeap, n1, n2, i, j, k; + Bool tooLong; + + Int32 heap [ BZ_MAX_ALPHA_SIZE + 2 ]; + Int32 weight [ BZ_MAX_ALPHA_SIZE * 2 ]; + Int32 parent [ BZ_MAX_ALPHA_SIZE * 2 ]; + + for (i = 0; i < alphaSize; i++) + weight[i+1] = (freq[i] == 0 ? 1 : freq[i]) << 8; + + while (True) { + + nNodes = alphaSize; + nHeap = 0; + + heap[0] = 0; + weight[0] = 0; + parent[0] = -2; + + for (i = 1; i <= alphaSize; i++) { + parent[i] = -1; + nHeap++; + heap[nHeap] = i; + UPHEAP(nHeap); + } + + AssertH( nHeap < (BZ_MAX_ALPHA_SIZE+2), 2001 ); + + while (nHeap > 1) { + n1 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP(1); + n2 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP(1); + nNodes++; + parent[n1] = parent[n2] = nNodes; + weight[nNodes] = ADDWEIGHTS(weight[n1], weight[n2]); + parent[nNodes] = -1; + nHeap++; + heap[nHeap] = nNodes; + UPHEAP(nHeap); + } + + AssertH( nNodes < (BZ_MAX_ALPHA_SIZE * 2), 2002 ); + + tooLong = False; + for (i = 1; i <= alphaSize; i++) { + j = 0; + k = i; + while (parent[k] >= 0) { k = parent[k]; j++; } + len[i-1] = j; + if (j > maxLen) tooLong = True; + } + + if (! tooLong) break; + + /* 17 Oct 04: keep-going condition for the following loop used + to be 'i < alphaSize', which missed the last element, + theoretically leading to the possibility of the compressor + looping. However, this count-scaling step is only needed if + one of the generated Huffman code words is longer than + maxLen, which up to and including version 1.0.2 was 20 bits, + which is extremely unlikely. In version 1.0.3 maxLen was + changed to 17 bits, which has minimal effect on compression + ratio, but does mean this scaling step is used from time to + time, enough to verify that it works. + + This means that bzip2-1.0.3 and later will only produce + Huffman codes with a maximum length of 17 bits. However, in + order to preserve backwards compatibility with bitstreams + produced by versions pre-1.0.3, the decompressor must still + handle lengths of up to 20. */ + + for (i = 1; i <= alphaSize; i++) { + j = weight[i] >> 8; + j = 1 + (j / 2); + weight[i] = j << 8; + } + } +} + + +/*---------------------------------------------------*/ +void BZ2_hbAssignCodes ( Int32 *code, + UChar *length, + Int32 minLen, + Int32 maxLen, + Int32 alphaSize ) +{ + Int32 n, vec, i; + + vec = 0; + for (n = minLen; n <= maxLen; n++) { + for (i = 0; i < alphaSize; i++) + if (length[i] == n) { code[i] = vec; vec++; }; + vec <<= 1; + } +} + + +/*---------------------------------------------------*/ +void BZ2_hbCreateDecodeTables ( Int32 *limit, + Int32 *base, + Int32 *perm, + UChar *length, + Int32 minLen, + Int32 maxLen, + Int32 alphaSize ) +{ + Int32 pp, i, j, vec; + + pp = 0; + for (i = minLen; i <= maxLen; i++) + for (j = 0; j < alphaSize; j++) + if (length[j] == i) { perm[pp] = j; pp++; }; + + for (i = 0; i < BZ_MAX_CODE_LEN; i++) base[i] = 0; + for (i = 0; i < alphaSize; i++) base[length[i]+1]++; + + for (i = 1; i < BZ_MAX_CODE_LEN; i++) base[i] += base[i-1]; + + for (i = 0; i < BZ_MAX_CODE_LEN; i++) limit[i] = 0; + vec = 0; + + for (i = minLen; i <= maxLen; i++) { + vec += (base[i+1] - base[i]); + limit[i] = vec-1; + vec <<= 1; + } + for (i = minLen + 1; i <= maxLen; i++) + base[i] = ((limit[i-1] + 1) << 1) - base[i]; +} + + +/*-------------------------------------------------------------*/ +/*--- end huffman.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/dep/StormLib/src/bzip2/randtable.c b/dep/StormLib/src/bzip2/randtable.c new file mode 100644 index 0000000000..068b76367b --- /dev/null +++ b/dep/StormLib/src/bzip2/randtable.c @@ -0,0 +1,84 @@ + +/*-------------------------------------------------------------*/ +/*--- Table for randomising repetitive blocks ---*/ +/*--- randtable.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.5 of 10 December 2007 + Copyright (C) 1996-2007 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + + +/*---------------------------------------------*/ +Int32 BZ2_rNums[512] = { + 619, 720, 127, 481, 931, 816, 813, 233, 566, 247, + 985, 724, 205, 454, 863, 491, 741, 242, 949, 214, + 733, 859, 335, 708, 621, 574, 73, 654, 730, 472, + 419, 436, 278, 496, 867, 210, 399, 680, 480, 51, + 878, 465, 811, 169, 869, 675, 611, 697, 867, 561, + 862, 687, 507, 283, 482, 129, 807, 591, 733, 623, + 150, 238, 59, 379, 684, 877, 625, 169, 643, 105, + 170, 607, 520, 932, 727, 476, 693, 425, 174, 647, + 73, 122, 335, 530, 442, 853, 695, 249, 445, 515, + 909, 545, 703, 919, 874, 474, 882, 500, 594, 612, + 641, 801, 220, 162, 819, 984, 589, 513, 495, 799, + 161, 604, 958, 533, 221, 400, 386, 867, 600, 782, + 382, 596, 414, 171, 516, 375, 682, 485, 911, 276, + 98, 553, 163, 354, 666, 933, 424, 341, 533, 870, + 227, 730, 475, 186, 263, 647, 537, 686, 600, 224, + 469, 68, 770, 919, 190, 373, 294, 822, 808, 206, + 184, 943, 795, 384, 383, 461, 404, 758, 839, 887, + 715, 67, 618, 276, 204, 918, 873, 777, 604, 560, + 951, 160, 578, 722, 79, 804, 96, 409, 713, 940, + 652, 934, 970, 447, 318, 353, 859, 672, 112, 785, + 645, 863, 803, 350, 139, 93, 354, 99, 820, 908, + 609, 772, 154, 274, 580, 184, 79, 626, 630, 742, + 653, 282, 762, 623, 680, 81, 927, 626, 789, 125, + 411, 521, 938, 300, 821, 78, 343, 175, 128, 250, + 170, 774, 972, 275, 999, 639, 495, 78, 352, 126, + 857, 956, 358, 619, 580, 124, 737, 594, 701, 612, + 669, 112, 134, 694, 363, 992, 809, 743, 168, 974, + 944, 375, 748, 52, 600, 747, 642, 182, 862, 81, + 344, 805, 988, 739, 511, 655, 814, 334, 249, 515, + 897, 955, 664, 981, 649, 113, 974, 459, 893, 228, + 433, 837, 553, 268, 926, 240, 102, 654, 459, 51, + 686, 754, 806, 760, 493, 403, 415, 394, 687, 700, + 946, 670, 656, 610, 738, 392, 760, 799, 887, 653, + 978, 321, 576, 617, 626, 502, 894, 679, 243, 440, + 680, 879, 194, 572, 640, 724, 926, 56, 204, 700, + 707, 151, 457, 449, 797, 195, 791, 558, 945, 679, + 297, 59, 87, 824, 713, 663, 412, 693, 342, 606, + 134, 108, 571, 364, 631, 212, 174, 643, 304, 329, + 343, 97, 430, 751, 497, 314, 983, 374, 822, 928, + 140, 206, 73, 263, 980, 736, 876, 478, 430, 305, + 170, 514, 364, 692, 829, 82, 855, 953, 676, 246, + 369, 970, 294, 750, 807, 827, 150, 790, 288, 923, + 804, 378, 215, 828, 592, 281, 565, 555, 710, 82, + 896, 831, 547, 261, 524, 462, 293, 465, 502, 56, + 661, 821, 976, 991, 658, 869, 905, 758, 745, 193, + 768, 550, 608, 933, 378, 286, 215, 979, 792, 961, + 61, 688, 793, 644, 986, 403, 106, 366, 905, 644, + 372, 567, 466, 434, 645, 210, 389, 550, 919, 135, + 780, 773, 635, 389, 707, 100, 626, 958, 165, 504, + 920, 176, 193, 713, 857, 265, 203, 50, 668, 108, + 645, 990, 626, 197, 510, 357, 358, 850, 858, 364, + 936, 638 +}; + + +/*-------------------------------------------------------------*/ +/*--- end randtable.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/dep/StormLib/src/huffman/huff.cpp b/dep/StormLib/src/huffman/huff.cpp index 66a46b3fa5..ba0e4b7220 100644 --- a/dep/StormLib/src/huffman/huff.cpp +++ b/dep/StormLib/src/huffman/huff.cpp @@ -1,11 +1,11 @@ /*****************************************************************************/ -/* huffman.cpp Copyright (c) Ladislav Zezula 1998-2003 */ +/* huff.cpp Copyright (c) Ladislav Zezula 1998-2026 */ /*---------------------------------------------------------------------------*/ -/* This module contains Huffmann (de)compression methods */ +/* This module contains Adaptive (Dynamic) Huffmann coding, following the */ +/* Fahler-Gallager-Knuth variant */ /* */ /* Authors : Ladislav Zezula (ladik@zezula.net) */ /* ShadowFlare (BlakFlare@hotmail.com) */ -/* */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ @@ -13,1124 +13,26 @@ /* 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) */ +/* 09.01.13 3.00 Lad Refactored, beautified, documented :-) */ +/* 07.04.26 3.01 Lad Explained a bit more */ /*****************************************************************************/ - + #include #include - + #include "huff.h" -// Special for Mac - we have to know if normal pointer greater or less -// than 0x80000000. This variable is used in the PTR_VALID and PTR_INVALID -// macros -static long mul = 1; - -#define PTR_VALID(ptr) (((LONG_PTR)(ptr) * mul) > 0) -#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) -{ - if(pLast == NULL) - 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_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) - { - if(cbOutSize != 0) - { - *pbOutPos++ = (unsigned char)dwBitBuff; - cbOutSize--; - } - - dwBitBuff >>= 8; - nBits -= 8; - } -} - -//----------------------------------------------------------------------------- -// TInputStream functions +// Local defines -// 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; -} +#define HUFF_DECOMPRESS_ERROR 0x1FF //----------------------------------------------------------------------------- -// Functions for huffmann tree items - -// Inserts item into the tree (?) -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 - { - 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(nWhere) - { - 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 = PTR_INT(itemPtr[0]);// Usually NULL - prev2 = item2->prev; // Prev item to the second (or last tree item) - - if(PTR_INVALID(prev2)) - { - 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_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) - { - 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(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) - { - 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; - } +// Table of byte-to-weight values - // 15006C7B - if(PTR_INVALID_OR_NULL(child1 = child2->prev)) - break; - } - } - // 15006C88 - offs0004 = 1; -} -/* -// Modifies Huffman tree. Adds new item and changes -void THuffmannTree::ModifyTree(unsigned long dwIndex) +// Table generic/sparse data distribution (DATA_TYPE_SPARSE) +static const unsigned char DataDistribution_Sparse[] = { - 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; - pItem1->prev = NULL; - } - // 1500694C - 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; - pItem1->byteValue = 0; - 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; - pItem1 ->parent = pItem2->parent; - pItem2->parent = temp; - offs0004++; - } - while(PTR_INT((pItem1 = pItem1->parent)) > 0); -} - -void THuffmannTree::UninitTree() -{ - while(PTR_INT(pLast) > 0) - { - 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--; - pItem->RemoveItem(); - pItem->RemoveItem(); - } -} -*/ - -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_OR_NULL(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_OR_NULL(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 += PTR_INT(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 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) - { - 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 - { - 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) - { - 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, @@ -1147,9 +49,12 @@ 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, - - // Data for compression type 0x01 + 0x00, 0x00 +}; + +// Table for general binary data distribution (DATA_TYPE_BINARY) +static const unsigned char DataDistribution_Binary[] = +{ 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, @@ -1166,9 +71,12 @@ unsigned char THuffmannTree::Table1502A630[] = 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 +}; + +// Table for text distribution (DATA_TYPE_TEXT) +static const unsigned char DataDistribution_Text[] = +{ 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, @@ -1185,9 +93,12 @@ 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, - - // Data for compression type 0x03 + 0x00, 0x00 +}; + +// Table for general purpose distribution (DATA_TYPE_GENERAL) +static const unsigned char DataDistribution_General[] = +{ 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, @@ -1204,9 +115,12 @@ unsigned char THuffmannTree::Table1502A630[] = 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 + 0x00, 0x00 +}; + +// Table for 4-bit ADPCM audio samples (DATA_TYPE_ADPCM_4) +static const unsigned char DataDistribution_ADPCM_4[] = +{ 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, @@ -1223,9 +137,12 @@ 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, - - // Data for compression type 0x05 + 0x00, 0x00 +}; + +// Table for 6-bit ADPCM audio samples (DATA_TYPE_ADPCM_6) +static const unsigned char DataDistribution_ADPCM_6[] = +{ 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, @@ -1242,9 +159,12 @@ 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, - - // Data for compression type 0x06 + 0x00, 0x00 +}; + +// Table for 3-bit stereo audio (DATA_TYPE_STEREO_3) +static const unsigned char DataDistribution_Stereo_3[] = +{ 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, @@ -1261,9 +181,12 @@ 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, - - // Data for compression type 0x07 + 0x00, 0x00 +}; + +// Table for 4-bit stereo audio (DATA_TYPE_STEREO_4) +static const unsigned char DataDistribution_Stereo_4[] = +{ 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, @@ -1280,9 +203,12 @@ 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, - - // Data for compression type 0x08 + 0x00, 0x00 +}; + +// Table for 5-bit stereo audio (DATA_TYPE_STEREO_5) +static const unsigned char DataDistribution_Stereo_5[] = +{ 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, @@ -1301,3 +227,690 @@ unsigned char THuffmannTree::Table1502A630[] = 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const unsigned char * DataDistributions[0x09] = +{ + DataDistribution_Sparse, // Generic / sparse + DataDistribution_Binary, // Generic binary data + DataDistribution_Text, // Text data + DataDistribution_General, // General purpose + DataDistribution_ADPCM_4, // 4-bit ADPCM audio samples + DataDistribution_ADPCM_6, // 6-bit ADPCM audio samples + DataDistribution_Stereo_3, // 3-bit stereo audio + DataDistribution_Stereo_4, // 4-bit stereo audio + DataDistribution_Stereo_5 // 5-bit stereo audio +}; + +#define MAX_DATA_TYPE (sizeof(DataDistributions) / sizeof(DataDistributions[0])) + +//----------------------------------------------------------------------------- +// Debug/diagnostics + +#ifdef _DEBUG +void DumpHuffmannTree(THTreeItem * pItem) +{ + THTreeItem * pChildLo; // Item with the lower weight + THTreeItem * pChildHi; // Item with the higher weight + + // Get the lower-weight branch + pChildLo = pItem->pChildLo; + if(pChildLo != NULL) + { + // Get the higher-weight branch + pChildHi = pChildLo->pPrev; + + // Parse the lower-weight branch + DumpHuffmannTree(pChildHi); + DumpHuffmannTree(pChildLo); + } +} +#endif + +//----------------------------------------------------------------------------- +// TInputStream functions + +TInputStream::TInputStream(void * pvInBuffer, size_t cbInBuffer) +{ + pbInBufferEnd = (unsigned char *)pvInBuffer + cbInBuffer; + pbInBuffer = (unsigned char *)pvInBuffer; + BitBuffer = 0; + BitCount = 0; +} + +// Gets one bit from input stream +bool TInputStream::Get1Bit(unsigned int & BitValue) +{ + // Ensure that the input stream is reloaded, if there are no bits left + if(BitCount == 0) + { + // Buffer overflow check + if(pbInBuffer >= pbInBufferEnd) + return false; + + // Refill the bit buffer + BitBuffer = *pbInBuffer++; + BitCount = 8; + } + + // Copy the bit from bit buffer to the variable + BitValue = (BitBuffer & 0x01); + BitBuffer >>= 1; + BitCount--; + return true; +} + +// Gets the whole byte from the input stream. +bool TInputStream::Get8Bits(unsigned int & ByteValue) +{ + unsigned int 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 < 8) + { + // Buffer overflow check + if(pbInBuffer >= pbInBufferEnd) + return false; + + dwReloadByte = *pbInBuffer++; + BitBuffer |= dwReloadByte << BitCount; + BitCount += 8; + } + + // Return the lowest 8 bits + ByteValue = (BitBuffer & 0xFF); + BitBuffer >>= 8; + BitCount -= 8; + return true; +} + +// Gets 7 bits from the stream. DOES NOT remove the bits from input stream +bool TInputStream::Peek7Bits(unsigned int & Value) +{ + unsigned int Value8Bits = 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) + { + // Load additional 8 bits. Be careful if we have no more data + if(pbInBuffer >= pbInBufferEnd) + return false; + Value8Bits = *pbInBuffer++; + + // Add these 8 bits to the bit buffer + BitBuffer |= Value8Bits << BitCount; + BitCount += 8; + } + + // Return 7 bits of data. DO NOT remove them from the input stream + Value = (BitBuffer & 0x7F); + return true; +} + +void TInputStream::SkipBits(unsigned int dwBitsToSkip) +{ + unsigned int 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) + { + // Buffer overflow check + if(pbInBuffer >= pbInBufferEnd) + return; + + dwReloadByte = *pbInBuffer++; + BitBuffer |= dwReloadByte << BitCount; + BitCount += 8; + } + + // Skip the remaining bits + BitBuffer >>= dwBitsToSkip; + BitCount -= dwBitsToSkip; +} + +//----------------------------------------------------------------------------- +// TOutputStream functions + +TOutputStream::TOutputStream(void * pvOutBuffer, size_t cbOutLength) +{ + pbOutBufferEnd = (unsigned char *)pvOutBuffer + cbOutLength; + pbOutBuffer = (unsigned char *)pvOutBuffer; + BitBuffer = 0; + BitCount = 0; +} + +void TOutputStream::PutBits(unsigned int dwValue, unsigned int nBitCount) +{ + BitBuffer |= (dwValue << BitCount); + BitCount += nBitCount; + + // Flush completed bytes + while(BitCount >= 8) + { + if(pbOutBuffer < pbOutBufferEnd) + *pbOutBuffer++ = (unsigned char)BitBuffer; + + BitBuffer >>= 8; + BitCount -= 8; + } +} + +void TOutputStream::Flush() +{ + while(BitCount != 0) + { + if(pbOutBuffer < pbOutBufferEnd) + *pbOutBuffer++ = (unsigned char)BitBuffer; + + BitBuffer >>= 8; + BitCount -= ((BitCount > 8) ? 8 : BitCount); + } +} + +//----------------------------------------------------------------------------- +// Methods of the THTreeItem struct + +void THTreeItem::RemoveItem() +{ + if(pNext != NULL) + { + pPrev->pNext = pNext; + pNext->pPrev = pPrev; + pNext = pPrev = NULL; + } +} + +//----------------------------------------------------------------------------- +// THuffmannTree class functions + +THuffmannTree::THuffmannTree(bool bCompression) +{ + ListHead.pNext = ListHead.pPrev = LIST_HEAD(); + MinValidValue = 1; + ItemsUsed = 0; + bIsSparseData = false; + + memset(ItemsByByte, 0, sizeof(ItemsByByte)); + + // If we are going to decompress data, we need to invalidate all item links + // We do so by zeroing their ValidValue, so it becomes lower MinValidValue + if(bCompression == false) + { + memset(QuickLinks, 0, sizeof(QuickLinks)); + } +} + +THuffmannTree::~THuffmannTree() +{ + // Our Huffmann tree does not use any memory allocations, + // so we don't need to do any code in the destructor +} + +void THuffmannTree::LinkTwoItems(THTreeItem * pItem1, THTreeItem * pItem2) +{ + pItem2->pNext = pItem1->pNext; + pItem2->pPrev = pItem1->pNext->pPrev; + pItem1->pNext->pPrev = pItem2; + pItem1->pNext = pItem2; +} + +// Inserts item into the weight-sorted linked list +void THuffmannTree::InsertItem(THTreeItem * pNewItem, TInsertPoint InsertPoint, THTreeItem * pInsertPoint) +{ + // Remove the item from the linked list + pNewItem->RemoveItem(); + + if(pInsertPoint == NULL) + pInsertPoint = LIST_HEAD(); + + switch(InsertPoint) + { + case InsertAfter: + LinkTwoItems(pInsertPoint, pNewItem); + return; + + case InsertBefore: + LinkTwoItems(pInsertPoint->pPrev, pNewItem); + return; + } +} + +THTreeItem * THuffmannTree::FindHigherOrEqualItem(THTreeItem * pItem, unsigned int Weight) +{ + // Parse all existing items + if(pItem != NULL) + { + while(pItem != LIST_HEAD()) + { + if(pItem->Weight >= Weight) + return pItem; + + pItem = pItem->pPrev; + } + } + + // If not found, we just get the first item + return LIST_HEAD(); +} + +THTreeItem * THuffmannTree::CreateNewItem(unsigned int DecompressedValue, unsigned int Weight, TInsertPoint InsertPoint) +{ + THTreeItem * pNewItem = NULL; + + // Don't let the item buffer run out of space + if(ItemsUsed < HUFF_ITEM_COUNT) + { + // Allocate new item from the item pool + pNewItem = &ItemBuffer[ItemsUsed++]; + + // Insert this item into the weight-sorted list + InsertItem(pNewItem, InsertPoint, NULL); + + // Fill the rest of the item + pNewItem->DecompressedValue = DecompressedValue; + pNewItem->Weight = Weight; + pNewItem->pParent = NULL; + pNewItem->pChildLo = NULL; + } + + return pNewItem; +} + +unsigned int THuffmannTree::FixupItemPosByWeight(THTreeItem * pNewItem, unsigned int MaxWeight) +{ + THTreeItem * pHigherItem; + + if(pNewItem->Weight < MaxWeight) + { + // Find an item that has higher weight than this one + pHigherItem = FindHigherOrEqualItem(ListHead.pPrev, pNewItem->Weight); + + // Remove the item and put it to the new position + pNewItem->RemoveItem(); + LinkTwoItems(pHigherItem, pNewItem); + } + else + { + MaxWeight = pNewItem->Weight; + } + + // Return the (updated) maximum weight + return MaxWeight; +} + +// Builds Huffman tree from pre-initialized weight table for the given data type +bool THuffmannTree::BuildTree(unsigned int DataType) +{ + const unsigned char * DataDistTable; + THTreeItem * pNewItem; + THTreeItem * pChildLo; + THTreeItem * pChildHi; + unsigned int MaxWeight; + + // Clear all pointers in HTree item array + memset(ItemsByByte, 0, sizeof(ItemsByByte)); + MaxWeight = 0; + + // The check confirms that the lower 4 bits of DataType do not exceed the bounds of DataDistributions + // DataDistributions has _countof(DataDistributions) entries, indexed by (DataType & 0x0F) + if((DataType = (DataType & 0x0F)) >= MAX_DATA_TYPE) + return false; + DataDistTable = DataDistributions[DataType]; + + // Build the linear list of entries that is sorted by byte weight + for(unsigned int i = 0; i < 0x100; i++) + { + // Skip all the bytes which are zero + if(DataDistTable[i] != 0) + { + // Create new tree item + ItemsByByte[i] = pNewItem = CreateNewItem(i, DataDistTable[i], InsertAfter); + + // We need to put the item to the right place in the list + MaxWeight = FixupItemPosByWeight(pNewItem, MaxWeight); + } + } + + // Insert termination entries at the end of the list + ItemsByByte[0x100] = CreateNewItem(0x100, 1, InsertBefore); + ItemsByByte[0x101] = CreateNewItem(0x101, 1, InsertBefore); + + // Now we need to build the tree. We start at the last entry + // and go backwards to the first one + pChildLo = ListHead.pPrev; + + // Work as long as both children are valid + // pChildHi is child with higher weight, pChildLo is the one with lower weight + while(pChildLo != LIST_HEAD()) + { + // Also get and verify the higher-weight child + pChildHi = pChildLo->pPrev; + if(pChildHi == LIST_HEAD()) + break; + + // Create new parent item for the children + pNewItem = CreateNewItem(0, pChildHi->Weight + pChildLo->Weight, InsertAfter); + if(pNewItem == NULL) + return false; + + // Link both child items to their new parent + pChildLo->pParent = pNewItem; + pChildHi->pParent = pNewItem; + pNewItem->pChildLo = pChildLo; + + // Fixup the item's position by its weight + MaxWeight = FixupItemPosByWeight(pNewItem, MaxWeight); + + // Get the previous lower-weight child + pChildLo = pChildHi->pPrev; + } + + // Initialize the MinValidValue to 1, which invalidates all quick-link items + MinValidValue = 1; + return true; +} + +void THuffmannTree::IncWeightsAndRebalance(THTreeItem * pItem) +{ + THTreeItem * pHigherItem; // A previous item with greater or equal weight + THTreeItem * pChildHi; // The higher-weight child + THTreeItem * pChildLo; // The lower-weight child + THTreeItem * pParent; + + // Climb up the tree and increment weight of each tree item + for(; pItem != NULL; pItem = pItem->pParent) + { + // Increment the item's weight + pItem->Weight++; + + // Find a previous item with equal or greater weight, which is not equal to this item + pHigherItem = FindHigherOrEqualItem(pItem->pPrev, pItem->Weight); + pChildHi = pHigherItem->pNext; + + // If the item is not equal to the tree item, we need to rebalance the tree + if(pChildHi != pItem) + { + // Move the previous item to the RIGHT from the given item + pChildHi->RemoveItem(); + LinkTwoItems(pItem, pChildHi); + + // Move the given item AFTER the greater-weight tree item + pItem->RemoveItem(); + LinkTwoItems(pHigherItem, pItem); + + // We need to maintain the tree so that pChildHi->Weight is >= pChildLo->Weight. + // Rebalance the tree accordingly. + pChildLo = pChildHi->pParent->pChildLo; + pParent = pItem->pParent; + if(pParent->pChildLo == pItem) + pParent->pChildLo = pChildHi; + if(pChildLo == pChildHi) + pChildHi->pParent->pChildLo = pItem; + pParent = pItem->pParent; + pItem->pParent = pChildHi->pParent; + pChildHi->pParent = pParent; + + // Increment the global valid value. This invalidates all quick-link items. + MinValidValue++; + } + } +} + +bool THuffmannTree::InsertNewBranchAndRebalance(unsigned int Value1, unsigned int Value2) +{ + THTreeItem * pLastItem = ListHead.pPrev; + THTreeItem * pChildHi; + THTreeItem * pChildLo; + + // Create higher-weight child + pChildHi = CreateNewItem(Value1, pLastItem->Weight, InsertBefore); + if(pChildHi != NULL) + { + pChildHi->pParent = pLastItem; + ItemsByByte[Value1] = pChildHi; + + // Create lower-weight child + pChildLo = CreateNewItem(Value2, 0, InsertBefore); + if(pChildLo != NULL) + { + pChildLo->pParent = pLastItem; + pLastItem->pChildLo = pChildLo; + ItemsByByte[Value2] = pChildLo; + + IncWeightsAndRebalance(pChildLo); + return true; + } + } + + // No more space in the tree buffer + return false; +} + +void THuffmannTree::EncodeOneByte(TOutputStream * os, THTreeItem * pItem) +{ + THTreeItem * pParent = pItem->pParent; + unsigned int BitBuffer = 0; + unsigned int BitCount = 0; + + // Walk from leaf to root, collecting 0/1 bits based on child position + while(pParent != NULL) + { + // Fill the bit buffer + BitBuffer = (BitBuffer << 1) | ((pParent->pChildLo != pItem) ? 1 : 0); + BitCount++; + + // Move to the parent + pItem = pParent; + pParent = pParent->pParent; + } + + // Write the bits to the output stream + os->PutBits(BitBuffer, BitCount); +} + +unsigned int THuffmannTree::DecodeOneByte(TInputStream * is) +{ + THTreeItem * pItemLink = NULL; + THTreeItem * pItem; + unsigned int ItemLinkIndex; + unsigned int BitCount = 0; + bool bHasItemLinkIndex; + + // Try to retrieve quick link index + bHasItemLinkIndex = is->Peek7Bits(ItemLinkIndex); + + // Is the quick-link item valid? + if(bHasItemLinkIndex && QuickLinks[ItemLinkIndex].ValidValue > MinValidValue) + { + // If that item needs less than 7 bits, we can get decompressed value directly + if(QuickLinks[ItemLinkIndex].ValidBits <= 7) + { + is->SkipBits(QuickLinks[ItemLinkIndex].ValidBits); + return QuickLinks[ItemLinkIndex].DecompressedValue; + } + + // Otherwise we cannot get decompressed value directly + // but we can skip 7 levels of tree parsing + pItem = QuickLinks[ItemLinkIndex].pItem; + is->SkipBits(7); + } + else + { + // Just a sanity check + if(ListHead.pNext == LIST_HEAD()) + return HUFF_DECOMPRESS_ERROR; + + // We don't have the quick-link item, we need to parse the tree from its root + pItem = ListHead.pNext; + } + + // Step down the tree until we find a terminal item + while(pItem->pChildLo != NULL) + { + unsigned int BitValue = 0; + + // If the next bit in the compressed stream is set, we get the higher-weight + // child. Otherwise, get the lower-weight child + if(!is->Get1Bit(BitValue)) + return HUFF_DECOMPRESS_ERROR; + + pItem = BitValue ? pItem->pChildLo->pPrev : pItem->pChildLo; + BitCount++; + + // If the number of loaded bits reached 7, + // remember the current item for storing into quick-link item array + if(BitCount == 7) + pItemLink = pItem; + } + + // If we didn't get the item from the quick-link array, + // set the entry in it + if(bHasItemLinkIndex && QuickLinks[ItemLinkIndex].ValidValue < MinValidValue) + { + // If the current compressed byte was more than 7 bits, + // set a quick-link item with pointer to tree item + if(BitCount > 7) + { + QuickLinks[ItemLinkIndex].ValidValue = MinValidValue; + QuickLinks[ItemLinkIndex].ValidBits = BitCount; + QuickLinks[ItemLinkIndex].pItem = pItemLink; + } + else + { + // Limit the quick-decompress item to lower amount of bits + // Coverity fix 84457: (x >> 32) has undefined behavior + ItemLinkIndex = (BitCount != 0) ? ItemLinkIndex & (0xFFFFFFFF >> (32 - BitCount)) : 0; + while(ItemLinkIndex < LINK_ITEM_COUNT) + { + // Fill the quick-decompress item + QuickLinks[ItemLinkIndex].ValidValue = MinValidValue; + QuickLinks[ItemLinkIndex].ValidBits = BitCount; + QuickLinks[ItemLinkIndex].DecompressedValue = pItem->DecompressedValue; + + // Increment the index + ItemLinkIndex += (1 << BitCount); + } + } + } + + // Return the decompressed value from the found item + return pItem->DecompressedValue; +} + +unsigned int THuffmannTree::Compress(TOutputStream * os, void * pvInBuffer, int cbInBuffer, int DataType) +{ + unsigned char * pbInBufferEnd = (unsigned char *)pvInBuffer + cbInBuffer; + unsigned char * pbInBuffer = (unsigned char *)pvInBuffer; + unsigned char * pbOutBuff = os->pbOutBuffer; + unsigned char InputByte; + + if(!BuildTree(DataType)) + return 0; + bIsSparseData = (DataType == DATA_TYPE_SPARSE); + + // Store the compression type into output buffer + os->PutBits(DataType, 8); + + // Process the entire input buffer + while(pbInBuffer < pbInBufferEnd) + { + // Get the (next) byte from the input buffer + InputByte = *pbInBuffer++; + + // Do we have an item for such input value? + if(ItemsByByte[InputByte] == NULL) + { + // Encode the NYT (Not Yet Transmitted) escape symbol + EncodeOneByte(os, ItemsByByte[0x101]); + + // Store the loaded byte into output stream + os->PutBits(InputByte, 8); + + if(!InsertNewBranchAndRebalance(ListHead.pPrev->DecompressedValue, InputByte)) + return 0; + + if(bIsSparseData) + { + IncWeightsAndRebalance(ItemsByByte[InputByte]); + continue; + } + + IncWeightsAndRebalance(ItemsByByte[InputByte]); + } + else + { + EncodeOneByte(os, ItemsByByte[InputByte]); + } + + if(bIsSparseData) + { + IncWeightsAndRebalance(ItemsByByte[InputByte]); + } + } + + // Put the termination mark to the compressed stream + EncodeOneByte(os, ItemsByByte[0x100]); + + // Flush the remaining bits + os->Flush(); + return (unsigned int)(os->pbOutBuffer - pbOutBuff); +} + +// Decompression using adaptive Huffman tree +unsigned int THuffmannTree::Decompress(void * pvOutBuffer, unsigned int cbOutLength, TInputStream * is) +{ + unsigned char * pbOutBufferEnd = (unsigned char *)pvOutBuffer + cbOutLength; + unsigned char * pbOutBuffer = (unsigned char *)pvOutBuffer; + unsigned int DecompressedValue = 0; + unsigned int DataType = 0; + + // Test the output length. Must not be zero + if(cbOutLength == 0) + return 0; + + // Get the compression type from the input stream + if(!is->Get8Bits(DataType)) + return 0; + bIsSparseData = (DataType == DATA_TYPE_SPARSE); + + // Build the Huffman tree + if(!BuildTree(DataType)) + return 0; + + // Process the entire input buffer until end of the stream + while((DecompressedValue = DecodeOneByte(is)) != 0x100) + { + // Did an error occur? + if(DecompressedValue == HUFF_DECOMPRESS_ERROR) + return 0; + + // NYT escape: the next 8 bits contain a new, previously unseen byte + if(DecompressedValue == 0x101) + { + // The decompressed byte is stored in the next 8 bits + if(!is->Get8Bits(DecompressedValue)) + return 0; + + if(!InsertNewBranchAndRebalance(ListHead.pPrev->DecompressedValue, DecompressedValue)) + return 0; + + if(!bIsSparseData) + IncWeightsAndRebalance(ItemsByByte[DecompressedValue]); + } + + // Store the byte to the output stream + if(pbOutBuffer >= pbOutBufferEnd) + break; + *pbOutBuffer++ = (unsigned char)DecompressedValue; + + if(bIsSparseData) + { + IncWeightsAndRebalance(ItemsByByte[DecompressedValue]); + } + } + + return (unsigned int)(pbOutBuffer - (unsigned char *)pvOutBuffer); +} + diff --git a/dep/StormLib/src/huffman/huff.h b/dep/StormLib/src/huffman/huff.h index 83e9b2cdad..2760dd2342 100644 --- a/dep/StormLib/src/huffman/huff.h +++ b/dep/StormLib/src/huffman/huff.h @@ -1,7 +1,7 @@ /*****************************************************************************/ /* huffman.h Copyright (c) Ladislav Zezula 2003 */ /*---------------------------------------------------------------------------*/ -/* Description : */ +/* Adaptive Huffmann coding (FGK variant) for MPQ archives */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ @@ -9,134 +9,147 @@ /* 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 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 - + +#define DATA_TYPE_SPARSE 0x00 +#define DATA_TYPE_BINARY 0x01 +#define DATA_TYPE_TEXT 0x02 +#define DATA_TYPE_GENERAL 0x03 +#define DATA_TYPE_ADPCM_4 0x04 +#define DATA_TYPE_ADPCM_6 0x05 +#define DATA_TYPE_STEREO_3 0x06 +#define DATA_TYPE_STEREO_4 0x07 +#define DATA_TYPE_STEREO_5 0x08 + +#define LINK_ITEM_COUNT 128 // Maximum number of quick-link items +#define BYTE_ITEM_COUNT 258 // Number of items-by-byte +#define HUFF_ITEM_COUNT 515 // Number of items in the item pool + //----------------------------------------------------------------------------- // Structures and classes - + // Input stream for Huffmann decompression class TInputStream { public: - - unsigned long GetBit(); - unsigned long Get7Bits(); - unsigned long Get8Bits(); + + TInputStream(void * pvInBuffer, size_t cbInBuffer); + bool Get1Bit(unsigned int & BitValue); + bool Get8Bits(unsigned int & ByteValue); + bool Peek7Bits(unsigned int & Value); 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' + + unsigned char * pbInBufferEnd; // End position in the input buffer + unsigned char * pbInBuffer; // Current position in the input buffer + unsigned int BitBuffer; // Input bit buffer + unsigned int BitCount; // Number of bits remaining in 'BitBuffer' }; - + + // 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 + + TOutputStream(void * pvOutBuffer, size_t cbOutLength); + void PutBits(unsigned int dwValue, unsigned int nBitCount); + void Flush(); + + unsigned char * pbOutBufferEnd; // End position in the output buffer + unsigned char * pbOutBuffer; // Current position in the output buffer + unsigned int BitBuffer; // Bit buffer + unsigned int BitCount; // Number of bits in the bit buffer }; - -// Huffmann tree item (?) + +// The list head sentinel - an actual THTreeItem embedded in THuffmannTree. +// Its pNext serves as "pFirst" (highest weight) and pPrev as "pLast" (lowest weight). +#define LIST_HEAD() (&ListHead) + +enum TInsertPoint +{ + InsertAfter = 1, + InsertBefore = 2 +}; + +// Huffmann tree item struct THTreeItem { - THTreeItem * Call1501DB70(THTreeItem * pLast); - THTreeItem * GetPrevItem(LONG_PTR value); - void ClearItemLinks(); + THTreeItem() { pPrev = pNext = NULL; DecompressedValue = 0; Weight = 0; pParent = pChildLo = NULL; } +// ~THTreeItem() { RemoveItem(); } + 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 +// void RemoveEntry(); + + THTreeItem * pNext; // Pointer to lower-weight tree item + THTreeItem * pPrev; // Pointer to higher-weight item + unsigned int DecompressedValue; // Decompressed byte value (also index in the array) + unsigned int Weight; // Weight + THTreeItem * pParent; // Pointer to parent item (NULL if none) + THTreeItem * pChildLo; // Pointer to the lower-weight child ("left child") }; - -// 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 + + +// Structure used for quick navigating in the huffmann tree. +// Allows skipping up to 7 bits in the compressed stream, thus +// decompressing a bit faster. Sometimes it can even get the decompressed +// byte directly. +struct TQuickLink { - unsigned long offs00; // 00 - 1 if resolved - unsigned long nBits; // 04 - Bit count + unsigned int ValidValue; // If greater than THuffmannTree::MinValidValue, the entry is valid + unsigned int ValidBits; // Number of bits that are valid for this item link union { - unsigned long dcmpByte; // 08 - Byte value for decompress (if bitCount <= 7) - THTreeItem * pItem; // 08 - THTreeItem (if number of bits is greater than 7 + THTreeItem * pItem; // Pointer to the item within the Huffmann tree + unsigned int DecompressedValue; // Value for direct decompression }; }; - -// 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 + + +// Adaptive Huffmann tree (FGK variant). The tree is initialized with +// pre-computed weight tables and rebalanced after every symbol to +// maintain the sibling property 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 + + THuffmannTree(bool bCompression); + ~THuffmannTree(); + + void LinkTwoItems(THTreeItem * pItem1, THTreeItem * pItem2); + void InsertItem(THTreeItem * item, TInsertPoint InsertPoint, THTreeItem * item2); + + THTreeItem * FindHigherOrEqualItem(THTreeItem * pItem, unsigned int Weight); + THTreeItem * CreateNewItem(unsigned int DecompressedValue, unsigned int Weight, TInsertPoint InsertPoint); + + unsigned int FixupItemPosByWeight(THTreeItem * pItem, unsigned int MaxWeight); + bool BuildTree(unsigned int DataType); + + void IncWeightsAndRebalance(THTreeItem * pItem); + bool InsertNewBranchAndRebalance(unsigned int Value1, unsigned int Value2); + + void EncodeOneByte(TOutputStream * os, THTreeItem * pItem); + unsigned int DecodeOneByte(TInputStream * is); + + unsigned int Compress(TOutputStream * os, void * pvInBuffer, int cbInBuffer, int DataType); + unsigned int Decompress(void * pvOutBuffer, unsigned int cbOutLength, TInputStream * is); + + THTreeItem ItemBuffer[HUFF_ITEM_COUNT]; // Buffer for tree items. No memory allocation is needed + unsigned int ItemsUsed; // Number of tree items used from ItemBuffer + + // Sentinel node for the linear item list (doubly-linked, circular). + // ListHead.pNext = highest weight item, ListHead.pPrev = lowest weight item + THTreeItem ListHead; + + THTreeItem * ItemsByByte[BYTE_ITEM_COUNT]; // Array of item pointers, one for each possible byte value + TQuickLink QuickLinks[LINK_ITEM_COUNT]; // Array of quick-link items + + unsigned int MinValidValue; // A minimum value of TQDecompress::ValidValue to be considered valid + bool bIsSparseData; // True if sparse data }; - + #endif // __HUFFMAN_H__ diff --git a/dep/StormLib/src/huffman/huff_patch.cpp b/dep/StormLib/src/huffman/huff_patch.cpp deleted file mode 100644 index 6176a9b1f7..0000000000 --- a/dep/StormLib/src/huffman/huff_patch.cpp +++ /dev/null @@ -1,1120 +0,0 @@ -/*****************************************************************************/ -/* 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 deleted file mode 100644 index c1c03e6786..0000000000 --- a/dep/StormLib/src/huffman/huff_patch.h +++ /dev/null @@ -1,145 +0,0 @@ -/*****************************************************************************/ -/* 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/libtomcrypt/src/hashes/sha256.c b/dep/StormLib/src/libtomcrypt/src/hashes/sha256.c new file mode 100644 index 0000000000..edd3d44afd --- /dev/null +++ b/dep/StormLib/src/libtomcrypt/src/hashes/sha256.c @@ -0,0 +1,340 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../headers/tomcrypt.h" + +/** + @file sha256.c + LTC_SHA256 by Tom St Denis +*/ + +#ifdef LTC_SHA256 + +const struct ltc_hash_descriptor sha256_desc = +{ + "sha256", + 0, + 32, + 64, + + /* OID */ + { 2, 16, 840, 1, 101, 3, 4, 2, 1, }, + 9, + + &sha256_init, + &sha256_process, + &sha256_done, + &sha256_test, + NULL +}; + +#ifdef LTC_SMALL_CODE +/* the K array */ +static const ulong32 K[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, + 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL, + 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, + 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL, + 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL, + 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, + 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, + 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL, + 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, + 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL +}; +#endif + +/* Various logical functions */ +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define S(x, n) RORc((x),(n)) +#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) +#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) +#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) +#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) +#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) + +/* compress 512-bits */ +#ifdef LTC_CLEAN_STACK +static int _sha256_compress(hash_state * md, unsigned char *buf) +#else +static int sha256_compress(hash_state * md, unsigned char *buf) +#endif +{ + ulong32 S[8], W[64], t0, t1; +#ifdef LTC_SMALL_CODE + ulong32 t; +#endif + int i; + + /* copy state into S */ + for (i = 0; i < 8; i++) { + S[i] = md->sha256.state[i]; + } + + /* copy the state into 512-bits into W[0..15] */ + for (i = 0; i < 16; i++) { + LOAD32H(W[i], buf + (4*i)); + } + + /* fill W[16..63] */ + for (i = 16; i < 64; i++) { + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; + } + + /* Compress */ +#ifdef LTC_SMALL_CODE +#define RND(a,b,c,d,e,f,g,h,i) \ + t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ + t1 = Sigma0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + + for (i = 0; i < 64; ++i) { + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i); + t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; + S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t; + } +#else +#define RND(a,b,c,d,e,f,g,h,i,ki) \ + t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i]; \ + t1 = Sigma0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],0,0x428a2f98); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],1,0x71374491); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],2,0xb5c0fbcf); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],3,0xe9b5dba5); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],4,0x3956c25b); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],5,0x59f111f1); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],6,0x923f82a4); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],7,0xab1c5ed5); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],8,0xd807aa98); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],9,0x12835b01); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],10,0x243185be); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],11,0x550c7dc3); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],12,0x72be5d74); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],13,0x80deb1fe); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],14,0x9bdc06a7); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],15,0xc19bf174); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],16,0xe49b69c1); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],17,0xefbe4786); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],18,0x0fc19dc6); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],19,0x240ca1cc); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],20,0x2de92c6f); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],21,0x4a7484aa); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],22,0x5cb0a9dc); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],23,0x76f988da); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],24,0x983e5152); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],25,0xa831c66d); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],26,0xb00327c8); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],27,0xbf597fc7); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],28,0xc6e00bf3); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],29,0xd5a79147); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],30,0x06ca6351); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],31,0x14292967); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],32,0x27b70a85); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],33,0x2e1b2138); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],34,0x4d2c6dfc); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],35,0x53380d13); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],36,0x650a7354); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],37,0x766a0abb); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],38,0x81c2c92e); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],39,0x92722c85); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],40,0xa2bfe8a1); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],41,0xa81a664b); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],42,0xc24b8b70); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],43,0xc76c51a3); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],44,0xd192e819); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],45,0xd6990624); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],46,0xf40e3585); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],47,0x106aa070); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],48,0x19a4c116); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],49,0x1e376c08); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],50,0x2748774c); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],51,0x34b0bcb5); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],52,0x391c0cb3); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],53,0x4ed8aa4a); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],54,0x5b9cca4f); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],55,0x682e6ff3); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],56,0x748f82ee); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],57,0x78a5636f); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],58,0x84c87814); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],59,0x8cc70208); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],60,0x90befffa); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],61,0xa4506ceb); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],62,0xbef9a3f7); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],63,0xc67178f2); + +#undef RND + +#endif + + /* feedback */ + for (i = 0; i < 8; i++) { + md->sha256.state[i] = md->sha256.state[i] + S[i]; + } + return CRYPT_OK; +} + +#ifdef LTC_CLEAN_STACK +static int sha256_compress(hash_state * md, unsigned char *buf) +{ + int err; + err = _sha256_compress(md, buf); + burn_stack(sizeof(ulong32) * 74); + return err; +} +#endif + +/** + Initialize the hash state + @param md The hash state you wish to initialize + @return CRYPT_OK if successful +*/ +int sha256_init(hash_state * md) +{ + LTC_ARGCHK(md != NULL); + + md->sha256.curlen = 0; + md->sha256.length = 0; + md->sha256.state[0] = 0x6A09E667UL; + md->sha256.state[1] = 0xBB67AE85UL; + md->sha256.state[2] = 0x3C6EF372UL; + md->sha256.state[3] = 0xA54FF53AUL; + md->sha256.state[4] = 0x510E527FUL; + md->sha256.state[5] = 0x9B05688CUL; + md->sha256.state[6] = 0x1F83D9ABUL; + md->sha256.state[7] = 0x5BE0CD19UL; + return CRYPT_OK; +} + +/** + Process a block of memory though the hash + @param md The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @return CRYPT_OK if successful +*/ +HASH_PROCESS(sha256_process, sha256_compress, sha256, 64) + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (32 bytes) + @return CRYPT_OK if successful +*/ +int sha256_done(hash_state * md, unsigned char *out) +{ + int i; + + LTC_ARGCHK(md != NULL); + LTC_ARGCHK(out != NULL); + + if (md->sha256.curlen >= sizeof(md->sha256.buf)) { + return CRYPT_INVALID_ARG; + } + + + /* increase the length of the message */ + md->sha256.length += md->sha256.curlen * 8; + + /* append the '1' bit */ + md->sha256.buf[md->sha256.curlen++] = (unsigned char)0x80; + + /* if the length is currently above 56 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->sha256.curlen > 56) { + while (md->sha256.curlen < 64) { + md->sha256.buf[md->sha256.curlen++] = (unsigned char)0; + } + sha256_compress(md, md->sha256.buf); + md->sha256.curlen = 0; + } + + /* pad upto 56 bytes of zeroes */ + while (md->sha256.curlen < 56) { + md->sha256.buf[md->sha256.curlen++] = (unsigned char)0; + } + + /* store length */ + STORE64H(md->sha256.length, md->sha256.buf+56); + sha256_compress(md, md->sha256.buf); + + /* copy output */ + for (i = 0; i < 8; i++) { + STORE32H(md->sha256.state[i], out+(4*i)); + } +#ifdef LTC_CLEAN_STACK + zeromem(md, sizeof(hash_state)); +#endif + return CRYPT_OK; +} + +/** + Self-test the hash + @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled +*/ +int sha256_test(void) +{ + #ifndef LTC_TEST + return CRYPT_NOP; + #else + static const struct { + char *msg; + unsigned char hash[32]; + } tests[] = { + { "abc", + { 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, + 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, + 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, + 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad } + }, + { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + { 0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, + 0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39, + 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67, + 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1 } + }, + }; + + int i; + unsigned char tmp[32]; + hash_state md; + + for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) { + sha256_init(&md); + sha256_process(&md, (unsigned char*)tests[i].msg, (unsigned long)strlen(tests[i].msg)); + sha256_done(&md, tmp); + if (XMEMCMP(tmp, tests[i].hash, 32) != 0) { + return CRYPT_FAIL_TESTVECTOR; + } + } + return CRYPT_OK; + #endif +} + +#ifdef LTC_SHA224 +#include "sha224.c" +#endif + +#endif + + + +/* $Source: /cvs/libtom/libtomcrypt/src/hashes/sha2/sha256.c,v $ */ +/* $Revision: 1.11 $ */ +/* $Date: 2007/05/12 14:25:28 $ */ diff --git a/dep/StormLib/src/libtomcrypt/src/headers/tomcrypt.h b/dep/StormLib/src/libtomcrypt/src/headers/tomcrypt.h index 74cdff4754..7df3f5a8a7 100644 --- a/dep/StormLib/src/libtomcrypt/src/headers/tomcrypt.h +++ b/dep/StormLib/src/libtomcrypt/src/headers/tomcrypt.h @@ -25,6 +25,10 @@ extern "C" { /* descriptor table size */ #define TAB_SIZE 32 +#ifdef _MSC_VER +#pragma warning(disable: 4333) // der_encode_utf8_string.c(91) : warning C4333: '>>' : right shift by too large amount, data loss +#endif + /* error codes [will be expanded in future releases] */ enum { CRYPT_OK=0, /* Result OK */ diff --git a/dep/StormLib/src/libtomcrypt/src/headers/tomcrypt_cfg.h b/dep/StormLib/src/libtomcrypt/src/headers/tomcrypt_cfg.h index 7feae6e8bd..335d55f06a 100644 --- a/dep/StormLib/src/libtomcrypt/src/headers/tomcrypt_cfg.h +++ b/dep/StormLib/src/libtomcrypt/src/headers/tomcrypt_cfg.h @@ -28,19 +28,27 @@ LTC_EXPORT void * LTC_CALL XREALLOC(void *p, size_t n); LTC_EXPORT void * LTC_CALL XCALLOC(size_t n, size_t s); LTC_EXPORT void LTC_CALL XFREE(void *p); -LTC_EXPORT void LTC_CALL XQSORT(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *)); - +LTC_EXPORT void LTC_CALL XQSORT(void *base, size_t nmemb, size_t size, int(LTC_CALL * compar)(const void *, const void *)); /* change the clock function too */ LTC_EXPORT clock_t LTC_CALL XCLOCK(void); +#endif // LTC_NO_PROTOTYPES /* various other functions */ +#ifndef LTC_NO_PROTOTYPES_MEMCPY LTC_EXPORT void * LTC_CALL XMEMCPY(void *dest, const void *src, size_t n); +#endif + +#ifndef LTC_NO_PROTOTYPES_MEMCMP LTC_EXPORT int LTC_CALL XMEMCMP(const void *s1, const void *s2, size_t n); +#endif + +#ifndef LTC_NO_PROTOTYPES_MEMSET LTC_EXPORT void * LTC_CALL XMEMSET(void *s, int c, size_t n); +#endif +#ifndef LTC_NO_PROTOTYPES_STRCMP LTC_EXPORT int LTC_CALL XSTRCMP(const char *s1, const char *s2); - #endif /* type of argument checking, 0=default, 1=fatal and 2=error+continue, 3=nothing */ diff --git a/dep/StormLib/src/libtomcrypt/src/headers/tomcrypt_custom.h b/dep/StormLib/src/libtomcrypt/src/headers/tomcrypt_custom.h index 88ec8f984a..d53eef7543 100644 --- a/dep/StormLib/src/libtomcrypt/src/headers/tomcrypt_custom.h +++ b/dep/StormLib/src/libtomcrypt/src/headers/tomcrypt_custom.h @@ -11,29 +11,29 @@ #define LTC_NO_ROLC #define LTC_SOURCE +#define LTC_SHA256 #define LTC_SHA1 #define LTC_MD5 #define LTC_DER -#define LTC_RC4 -#define USE_LTM #define LTM_DESC +#define USE_LTM /* macros for various libc functions you can change for embedded targets */ #ifndef XMALLOC - #ifdef malloc + #ifdef malloc #define LTC_NO_PROTOTYPES #endif #define XMALLOC LibTomMalloc #endif #ifndef XREALLOC - #ifdef realloc + #ifdef realloc #define LTC_NO_PROTOTYPES #endif #define XREALLOC LibTomRealloc #endif #ifndef XCALLOC - #ifdef calloc + #ifdef calloc #define LTC_NO_PROTOTYPES #endif #define XCALLOC LibTomCalloc @@ -47,25 +47,25 @@ #ifndef XMEMSET #ifdef memset - #define LTC_NO_PROTOTYPES + #define LTC_NO_PROTOTYPES_MEMSET #endif #define XMEMSET memset #endif #ifndef XMEMCPY #ifdef memcpy - #define LTC_NO_PROTOTYPES + #define LTC_NO_PROTOTYPES_MEMCPY #endif #define XMEMCPY memcpy #endif #ifndef XMEMCMP - #ifdef memcmp - #define LTC_NO_PROTOTYPES + #ifdef memcmp + #define LTC_NO_PROTOTYPES_MEMCMP #endif #define XMEMCMP memcmp #endif #ifndef XSTRCMP #ifdef strcmp - #define LTC_NO_PROTOTYPES + #define LTC_NO_PROTOTYPES_STRCMP #endif #define XSTRCMP strcmp #endif @@ -91,19 +91,19 @@ #define LTC_BLOWFISH #define LTC_DES #define LTC_CAST5 - + #define LTC_NO_MODES #define LTC_ECB_MODE #define LTC_CBC_MODE #define LTC_CTR_MODE - + #define LTC_NO_HASHES #define LTC_SHA1 #define LTC_SHA512 #define LTC_SHA384 #define LTC_SHA256 #define LTC_SHA224 - + #define LTC_NO_MACS #define LTC_HMAC #define LTC_OMAC @@ -114,11 +114,11 @@ #define LTC_YARROW #define LTC_DEVRANDOM #define TRY_URANDOM_FIRST - + #define LTC_NO_PK #define LTC_MRSA #define LTC_MECC -#endif +#endif /* Use small code where possible */ /* #define LTC_SMALL_CODE */ @@ -194,7 +194,7 @@ #define LTC_LRW_MODE #ifndef LTC_NO_TABLES /* like GCM mode this will enable 16 8x128 tables [64KB] that make - * seeking very fast. + * seeking very fast. */ #define LRW_TABLES #endif @@ -205,7 +205,7 @@ #endif /* LTC_NO_MODES */ /* ---> One-Way Hash Functions <--- */ -#ifndef LTC_NO_HASHES +#ifndef LTC_NO_HASHES #define LTC_CHC_HASH #define LTC_WHIRLPOOL @@ -252,7 +252,7 @@ /* Use 64KiB tables */ #ifndef LTC_NO_TABLES - #define LTC_GCM_TABLES + #define LTC_GCM_TABLES #endif /* USE SSE2? requires GCC works on x86_32 and x86_64*/ @@ -319,7 +319,7 @@ #define LTC_MRSA /* Include Katja (a Rabin variant like RSA) */ -/* #define MKAT */ +/* #define MKAT */ /* Digital Signature Algorithm */ #define LTC_MDSA @@ -332,7 +332,7 @@ #if defined(TFM_LTC_DESC) && defined(LTC_MECC) #define LTC_MECC_ACCEL -#endif +#endif /* do we want fixed point ECC */ /* #define LTC_MECC_FP */ @@ -376,9 +376,9 @@ #ifdef LTC_MRSA #define LTC_PKCS_1 -#endif +#endif -#if defined(LTC_DER) && !defined(MPI) +#if defined(LTC_DER) && !defined(MPI) #error ASN.1 DER requires MPI functionality #endif diff --git a/dep/StormLib/src/libtomcrypt/src/misc/crypt_libc.c b/dep/StormLib/src/libtomcrypt/src/misc/crypt_libc.c index bcc89f4f94..3457a52094 100644 --- a/dep/StormLib/src/libtomcrypt/src/misc/crypt_libc.c +++ b/dep/StormLib/src/libtomcrypt/src/misc/crypt_libc.c @@ -12,32 +12,32 @@ #include #include "../headers/tomcrypt.h" -void * LibTomMalloc(size_t n) +LTC_EXPORT void * LTC_CALL LibTomMalloc(size_t n) { return malloc(n); } -void * LibTomCalloc(size_t n, size_t s) +LTC_EXPORT void * LTC_CALL LibTomCalloc(size_t n, size_t s) { return calloc(n, s); } -void * LibTomRealloc(void *p, size_t n) +LTC_EXPORT void * LTC_CALL LibTomRealloc(void *p, size_t n) { return realloc(p, n); } -void LibTomFree(void * p) +LTC_EXPORT void LTC_CALL LibTomFree(void * p) { free(p); } -clock_t LibTomClock(void) +LTC_EXPORT clock_t LTC_CALL LibTomClock(void) { return clock(); } -void LibTomQsort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *)) +LTC_EXPORT void LTC_CALL LibTomQsort(void *base, size_t nmemb, size_t size, int(LTC_CALL * compar)(const void *, const void *)) { qsort(base, nmemb, size, compar); } diff --git a/dep/StormLib/src/libtomcrypt/src/misc/crypt_ltc_mp_descriptor.c b/dep/StormLib/src/libtomcrypt/src/misc/crypt_ltc_mp_descriptor.c index 91ba9d162d..c02a96f33f 100644 --- a/dep/StormLib/src/libtomcrypt/src/misc/crypt_ltc_mp_descriptor.c +++ b/dep/StormLib/src/libtomcrypt/src/misc/crypt_ltc_mp_descriptor.c @@ -10,4 +10,4 @@ */ #include "../headers/tomcrypt.h" -ltc_math_descriptor ltc_mp; +ltc_math_descriptor ltc_mp = {0}; diff --git a/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_bit_string.c b/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_bit_string.c new file mode 100644 index 0000000000..ca29c58a76 --- /dev/null +++ b/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_bit_string.c @@ -0,0 +1,89 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** + @file der_encode_bit_string.c + ASN.1 DER, encode a BIT STRING, Tom St Denis +*/ + + +#ifdef LTC_DER + +/** + Store a BIT STRING + @param in The array of bits to store (one per char) + @param inlen The number of bits tostore + @param out [out] The destination for the DER encoded BIT STRING + @param outlen [in/out] The max size and resulting size of the DER BIT STRING + @return CRYPT_OK if successful +*/ +int der_encode_bit_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen) +{ + unsigned long len, x, y; + unsigned char buf; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* avoid overflows */ + if ((err = der_length_bit_string(inlen, &len)) != CRYPT_OK) { + return err; + } + + if (len > *outlen) { + *outlen = len; + return CRYPT_BUFFER_OVERFLOW; + } + + /* store header (include bit padding count in length) */ + x = 0; + y = (inlen >> 3) + ((inlen&7) ? 1 : 0) + 1; + + out[x++] = 0x03; + if (y < 128) { + out[x++] = (unsigned char)y; + } else if (y < 256) { + out[x++] = 0x81; + out[x++] = (unsigned char)y; + } else if (y < 65536) { + out[x++] = 0x82; + out[x++] = (unsigned char)((y>>8)&255); + out[x++] = (unsigned char)(y&255); + } + + /* store number of zero padding bits */ + out[x++] = (unsigned char)((8 - inlen) & 7); + + /* store the bits in big endian format */ + for (y = buf = 0; y < inlen; y++) { + buf |= (in[y] ? 1 : 0) << (7 - (y & 7)); + if ((y & 7) == 7) { + out[x++] = buf; + buf = 0; + } + } + /* store last byte */ + if (inlen & 7) { + out[x++] = buf; + } + *outlen = x; + return CRYPT_OK; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/bit/der_encode_bit_string.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_boolean.c b/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_boolean.c new file mode 100644 index 0000000000..ded2731f9b --- /dev/null +++ b/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_boolean.c @@ -0,0 +1,51 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** + @file der_encode_boolean.c + ASN.1 DER, encode a BOOLEAN, Tom St Denis +*/ + + +#ifdef LTC_DER + +/** + Store a BOOLEAN + @param in The boolean to encode + @param out [out] The destination for the DER encoded BOOLEAN + @param outlen [in/out] The max size and resulting size of the DER BOOLEAN + @return CRYPT_OK if successful +*/ +int der_encode_boolean(int in, + unsigned char *out, unsigned long *outlen) +{ + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(out != NULL); + + if (*outlen < 3) { + *outlen = 3; + return CRYPT_BUFFER_OVERFLOW; + } + + *outlen = 3; + out[0] = 0x01; + out[1] = 0x01; + out[2] = in ? 0xFF : 0x00; + + return CRYPT_OK; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/boolean/der_encode_boolean.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_ia5_string.c b/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_ia5_string.c new file mode 100644 index 0000000000..30d3f43748 --- /dev/null +++ b/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_ia5_string.c @@ -0,0 +1,85 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** + @file der_encode_ia5_string.c + ASN.1 DER, encode a IA5 STRING, Tom St Denis +*/ + +#ifdef LTC_DER + +/** + Store an IA5 STRING + @param in The array of IA5 to store (one per char) + @param inlen The number of IA5 to store + @param out [out] The destination for the DER encoded IA5 STRING + @param outlen [in/out] The max size and resulting size of the DER IA5 STRING + @return CRYPT_OK if successful +*/ +int der_encode_ia5_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen) +{ + unsigned long x, y, len; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* get the size */ + if ((err = der_length_ia5_string(in, inlen, &len)) != CRYPT_OK) { + return err; + } + + /* too big? */ + if (len > *outlen) { + *outlen = len; + return CRYPT_BUFFER_OVERFLOW; + } + + /* encode the header+len */ + x = 0; + out[x++] = 0x16; + if (inlen < 128) { + out[x++] = (unsigned char)inlen; + } else if (inlen < 256) { + out[x++] = 0x81; + out[x++] = (unsigned char)inlen; + } else if (inlen < 65536UL) { + out[x++] = 0x82; + out[x++] = (unsigned char)((inlen>>8)&255); + out[x++] = (unsigned char)(inlen&255); + } else if (inlen < 16777216UL) { + out[x++] = 0x83; + out[x++] = (unsigned char)((inlen>>16)&255); + out[x++] = (unsigned char)((inlen>>8)&255); + out[x++] = (unsigned char)(inlen&255); + } else { + return CRYPT_INVALID_ARG; + } + + /* store octets */ + for (y = 0; y < inlen; y++) { + out[x++] = der_ia5_char_encode(in[y]); + } + + /* retun length */ + *outlen = x; + + return CRYPT_OK; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/ia5/der_encode_ia5_string.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_integer.c b/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_integer.c new file mode 100644 index 0000000000..4137a94bb1 --- /dev/null +++ b/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_integer.c @@ -0,0 +1,130 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** + @file der_encode_integer.c + ASN.1 DER, encode an integer, Tom St Denis +*/ + + +#ifdef LTC_DER + +/* Exports a positive bignum as DER format (upto 2^32 bytes in size) */ +/** + Store a mp_int integer + @param num The first mp_int to encode + @param out [out] The destination for the DER encoded integers + @param outlen [in/out] The max size and resulting size of the DER encoded integers + @return CRYPT_OK if successful +*/ +int der_encode_integer(void *num, unsigned char *out, unsigned long *outlen) +{ + unsigned long tmplen, y; + int err, leading_zero; + + LTC_ARGCHK(num != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* find out how big this will be */ + if ((err = der_length_integer(num, &tmplen)) != CRYPT_OK) { + return err; + } + + if (*outlen < tmplen) { + *outlen = tmplen; + return CRYPT_BUFFER_OVERFLOW; + } + + if (mp_cmp_d(num, 0) != LTC_MP_LT) { + /* we only need a leading zero if the msb of the first byte is one */ + if ((mp_count_bits(num) & 7) == 0 || mp_iszero(num) == LTC_MP_YES) { + leading_zero = 1; + } else { + leading_zero = 0; + } + + /* get length of num in bytes (plus 1 since we force the msbyte to zero) */ + y = mp_unsigned_bin_size(num) + leading_zero; + } else { + leading_zero = 0; + y = mp_count_bits(num); + y = y + (8 - (y & 7)); + y = y >> 3; + if (((mp_cnt_lsb(num)+1)==mp_count_bits(num)) && ((mp_count_bits(num)&7)==0)) --y; + } + + /* now store initial data */ + *out++ = 0x02; + if (y < 128) { + /* short form */ + *out++ = (unsigned char)y; + } else if (y < 256) { + *out++ = 0x81; + *out++ = (unsigned char)y; + } else if (y < 65536UL) { + *out++ = 0x82; + *out++ = (unsigned char)((y>>8)&255); + *out++ = (unsigned char)y; + } else if (y < 16777216UL) { + *out++ = 0x83; + *out++ = (unsigned char)((y>>16)&255); + *out++ = (unsigned char)((y>>8)&255); + *out++ = (unsigned char)y; + } else { + return CRYPT_INVALID_ARG; + } + + /* now store msbyte of zero if num is non-zero */ + if (leading_zero) { + *out++ = 0x00; + } + + /* if it's not zero store it as big endian */ + if (mp_cmp_d(num, 0) == LTC_MP_GT) { + /* now store the mpint */ + if ((err = mp_to_unsigned_bin(num, out)) != CRYPT_OK) { + return err; + } + } else if (mp_iszero(num) != LTC_MP_YES) { + void *tmp; + + /* negative */ + if (mp_init(&tmp) != CRYPT_OK) { + return CRYPT_MEM; + } + + /* 2^roundup and subtract */ + y = mp_count_bits(num); + y = y + (8 - (y & 7)); + if (((mp_cnt_lsb(num)+1)==mp_count_bits(num)) && ((mp_count_bits(num)&7)==0)) y -= 8; + if (mp_2expt(tmp, y) != CRYPT_OK || mp_add(tmp, num, tmp) != CRYPT_OK) { + mp_clear(tmp); + return CRYPT_MEM; + } + if ((err = mp_to_unsigned_bin(tmp, out)) != CRYPT_OK) { + mp_clear(tmp); + return err; + } + mp_clear(tmp); + } + + /* we good */ + *outlen = tmplen; + return CRYPT_OK; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/integer/der_encode_integer.c,v $ */ +/* $Revision: 1.9 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_object_identifier.c b/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_object_identifier.c new file mode 100644 index 0000000000..68e216276e --- /dev/null +++ b/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_object_identifier.c @@ -0,0 +1,111 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** + @file der_encode_object_identifier.c + ASN.1 DER, Encode Object Identifier, Tom St Denis +*/ + +#ifdef LTC_DER +/** + Encode an OID + @param words The words to encode (upto 32-bits each) + @param nwords The number of words in the OID + @param out [out] Destination of OID data + @param outlen [in/out] The max and resulting size of the OID + @return CRYPT_OK if successful +*/ +int der_encode_object_identifier(unsigned long *words, unsigned long nwords, + unsigned char *out, unsigned long *outlen) +{ + unsigned long i, x, y, z, t, mask, wordbuf; + int err; + + LTC_ARGCHK(words != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* check length */ + if ((err = der_length_object_identifier(words, nwords, &x)) != CRYPT_OK) { + return err; + } + if (x > *outlen) { + *outlen = x; + return CRYPT_BUFFER_OVERFLOW; + } + + /* compute length to store OID data */ + z = 0; + wordbuf = words[0] * 40 + words[1]; + for (y = 1; y < nwords; y++) { + t = der_object_identifier_bits(wordbuf); + z += t/7 + ((t%7) ? 1 : 0) + (wordbuf == 0 ? 1 : 0); + if (y < nwords - 1) { + wordbuf = words[y + 1]; + } + } + + /* store header + length */ + x = 0; + out[x++] = 0x06; + if (z < 128) { + out[x++] = (unsigned char)z; + } else if (z < 256) { + out[x++] = 0x81; + out[x++] = (unsigned char)z; + } else if (z < 65536UL) { + out[x++] = 0x82; + out[x++] = (unsigned char)((z>>8)&255); + out[x++] = (unsigned char)(z&255); + } else { + return CRYPT_INVALID_ARG; + } + + /* store first byte */ + wordbuf = words[0] * 40 + words[1]; + for (i = 1; i < nwords; i++) { + /* store 7 bit words in little endian */ + t = wordbuf & 0xFFFFFFFF; + if (t) { + y = x; + mask = 0; + while (t) { + out[x++] = (unsigned char)((t & 0x7F) | mask); + t >>= 7; + mask |= 0x80; /* upper bit is set on all but the last byte */ + } + /* now swap bytes y...x-1 */ + z = x - 1; + while (y < z) { + t = out[y]; out[y] = out[z]; out[z] = (unsigned char)t; + ++y; + --z; + } + } else { + /* zero word */ + out[x++] = 0x00; + } + + if (i < nwords - 1) { + wordbuf = words[i + 1]; + } + } + + *outlen = x; + return CRYPT_OK; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/object_identifier/der_encode_object_identifier.c,v $ */ +/* $Revision: 1.7 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_octet_string.c b/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_octet_string.c new file mode 100644 index 0000000000..b3ee7f4a42 --- /dev/null +++ b/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_octet_string.c @@ -0,0 +1,86 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** + @file der_encode_octet_string.c + ASN.1 DER, encode a OCTET STRING, Tom St Denis +*/ + + +#ifdef LTC_DER + +/** + Store an OCTET STRING + @param in The array of OCTETS to store (one per char) + @param inlen The number of OCTETS to store + @param out [out] The destination for the DER encoded OCTET STRING + @param outlen [in/out] The max size and resulting size of the DER OCTET STRING + @return CRYPT_OK if successful +*/ +int der_encode_octet_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen) +{ + unsigned long x, y, len; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* get the size */ + if ((err = der_length_octet_string(inlen, &len)) != CRYPT_OK) { + return err; + } + + /* too big? */ + if (len > *outlen) { + *outlen = len; + return CRYPT_BUFFER_OVERFLOW; + } + + /* encode the header+len */ + x = 0; + out[x++] = 0x04; + if (inlen < 128) { + out[x++] = (unsigned char)inlen; + } else if (inlen < 256) { + out[x++] = 0x81; + out[x++] = (unsigned char)inlen; + } else if (inlen < 65536UL) { + out[x++] = 0x82; + out[x++] = (unsigned char)((inlen>>8)&255); + out[x++] = (unsigned char)(inlen&255); + } else if (inlen < 16777216UL) { + out[x++] = 0x83; + out[x++] = (unsigned char)((inlen>>16)&255); + out[x++] = (unsigned char)((inlen>>8)&255); + out[x++] = (unsigned char)(inlen&255); + } else { + return CRYPT_INVALID_ARG; + } + + /* store octets */ + for (y = 0; y < inlen; y++) { + out[x++] = in[y]; + } + + /* retun length */ + *outlen = x; + + return CRYPT_OK; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/octet/der_encode_octet_string.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_printable_string.c b/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_printable_string.c new file mode 100644 index 0000000000..a1dab5f400 --- /dev/null +++ b/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_printable_string.c @@ -0,0 +1,85 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** + @file der_encode_printable_string.c + ASN.1 DER, encode a printable STRING, Tom St Denis +*/ + +#ifdef LTC_DER + +/** + Store an printable STRING + @param in The array of printable to store (one per char) + @param inlen The number of printable to store + @param out [out] The destination for the DER encoded printable STRING + @param outlen [in/out] The max size and resulting size of the DER printable STRING + @return CRYPT_OK if successful +*/ +int der_encode_printable_string(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen) +{ + unsigned long x, y, len; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* get the size */ + if ((err = der_length_printable_string(in, inlen, &len)) != CRYPT_OK) { + return err; + } + + /* too big? */ + if (len > *outlen) { + *outlen = len; + return CRYPT_BUFFER_OVERFLOW; + } + + /* encode the header+len */ + x = 0; + out[x++] = 0x13; + if (inlen < 128) { + out[x++] = (unsigned char)inlen; + } else if (inlen < 256) { + out[x++] = 0x81; + out[x++] = (unsigned char)inlen; + } else if (inlen < 65536UL) { + out[x++] = 0x82; + out[x++] = (unsigned char)((inlen>>8)&255); + out[x++] = (unsigned char)(inlen&255); + } else if (inlen < 16777216UL) { + out[x++] = 0x83; + out[x++] = (unsigned char)((inlen>>16)&255); + out[x++] = (unsigned char)((inlen>>8)&255); + out[x++] = (unsigned char)(inlen&255); + } else { + return CRYPT_INVALID_ARG; + } + + /* store octets */ + for (y = 0; y < inlen; y++) { + out[x++] = der_printable_char_encode(in[y]); + } + + /* retun length */ + *outlen = x; + + return CRYPT_OK; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/printable_string/der_encode_printable_string.c,v $ */ +/* $Revision: 1.5 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_sequence_ex.c b/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_sequence_ex.c new file mode 100644 index 0000000000..3df19cf4ad --- /dev/null +++ b/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_sequence_ex.c @@ -0,0 +1,335 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" +#include + + +/** + @file der_encode_sequence_ex.c + ASN.1 DER, encode a SEQUENCE, Tom St Denis +*/ + +#ifdef LTC_DER + +/** + Encode a SEQUENCE + @param list The list of items to encode + @param inlen The number of items in the list + @param out [out] The destination + @param outlen [in/out] The size of the output + @param type_of LTC_ASN1_SEQUENCE or LTC_ASN1_SET/LTC_ASN1_SETOF + @return CRYPT_OK on success +*/ +int der_encode_sequence_ex(ltc_asn1_list *list, unsigned long inlen, + unsigned char *out, unsigned long *outlen, int type_of) +{ + int err, type; + unsigned long size, x, y, z, i; + void *data; + + LTC_ARGCHK(list != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* get size of output that will be required */ + y = 0; + for (i = 0; i < inlen; i++) { + type = list[i].type; + size = list[i].size; + data = list[i].data; + + if (type == LTC_ASN1_EOL) { + break; + } + + switch (type) { + case LTC_ASN1_BOOLEAN: + if ((err = der_length_boolean(&x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_INTEGER: + if ((err = der_length_integer(data, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_SHORT_INTEGER: + if ((err = der_length_short_integer(*((unsigned long*)data), &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_BIT_STRING: + if ((err = der_length_bit_string(size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_OCTET_STRING: + if ((err = der_length_octet_string(size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_NULL: + y += 2; + break; + + case LTC_ASN1_OBJECT_IDENTIFIER: + if ((err = der_length_object_identifier(data, size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_IA5_STRING: + if ((err = der_length_ia5_string(data, size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_PRINTABLE_STRING: + if ((err = der_length_printable_string(data, size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_UTF8_STRING: + if ((err = der_length_utf8_string(data, size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_UTCTIME: + if ((err = der_length_utctime(data, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + case LTC_ASN1_SET: + case LTC_ASN1_SETOF: + case LTC_ASN1_SEQUENCE: + if ((err = der_length_sequence(data, size, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + + default: + err = CRYPT_INVALID_ARG; + goto LBL_ERR; + } + } + + /* calc header size */ + z = y; + if (y < 128) { + y += 2; + } else if (y < 256) { + /* 0x30 0x81 LL */ + y += 3; + } else if (y < 65536UL) { + /* 0x30 0x82 LL LL */ + y += 4; + } else if (y < 16777216UL) { + /* 0x30 0x83 LL LL LL */ + y += 5; + } else { + err = CRYPT_INVALID_ARG; + goto LBL_ERR; + } + + /* too big ? */ + if (*outlen < y) { + *outlen = y; + err = CRYPT_BUFFER_OVERFLOW; + goto LBL_ERR; + } + + /* store header */ + x = 0; + out[x++] = (type_of == LTC_ASN1_SEQUENCE) ? 0x30 : 0x31; + + if (z < 128) { + out[x++] = (unsigned char)z; + } else if (z < 256) { + out[x++] = 0x81; + out[x++] = (unsigned char)z; + } else if (z < 65536UL) { + out[x++] = 0x82; + out[x++] = (unsigned char)((z>>8UL)&255); + out[x++] = (unsigned char)(z&255); + } else if (z < 16777216UL) { + out[x++] = 0x83; + out[x++] = (unsigned char)((z>>16UL)&255); + out[x++] = (unsigned char)((z>>8UL)&255); + out[x++] = (unsigned char)(z&255); + } + + /* store data */ + *outlen -= x; + for (i = 0; i < inlen; i++) { + type = list[i].type; + size = list[i].size; + data = list[i].data; + + if (type == LTC_ASN1_EOL) { + break; + } + + switch (type) { + case LTC_ASN1_BOOLEAN: + z = *outlen; + if ((err = der_encode_boolean(*((int *)data), out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_INTEGER: + z = *outlen; + if ((err = der_encode_integer(data, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_SHORT_INTEGER: + z = *outlen; + if ((err = der_encode_short_integer(*((unsigned long*)data), out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_BIT_STRING: + z = *outlen; + if ((err = der_encode_bit_string(data, size, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_OCTET_STRING: + z = *outlen; + if ((err = der_encode_octet_string(data, size, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_NULL: + out[x++] = 0x05; + out[x++] = 0x00; + *outlen -= 2; + break; + + case LTC_ASN1_OBJECT_IDENTIFIER: + z = *outlen; + if ((err = der_encode_object_identifier(data, size, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_IA5_STRING: + z = *outlen; + if ((err = der_encode_ia5_string(data, size, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_PRINTABLE_STRING: + z = *outlen; + if ((err = der_encode_printable_string(data, size, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_UTF8_STRING: + z = *outlen; + if ((err = der_encode_utf8_string(data, size, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_UTCTIME: + z = *outlen; + if ((err = der_encode_utctime(data, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_SET: + z = *outlen; + if ((err = der_encode_set(data, size, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_SETOF: + z = *outlen; + if ((err = der_encode_setof(data, size, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + case LTC_ASN1_SEQUENCE: + z = *outlen; + if ((err = der_encode_sequence_ex(data, size, out + x, &z, type)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + + default: + err = CRYPT_INVALID_ARG; + goto LBL_ERR; + } + } + *outlen = x; + err = CRYPT_OK; + +LBL_ERR: + return err; +} + +#endif diff --git a/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_sequence_multi.c b/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_sequence_multi.c new file mode 100644 index 0000000000..782f042e19 --- /dev/null +++ b/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_sequence_multi.c @@ -0,0 +1,138 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" +#include + + +/** + @file der_encode_sequence_multi.c + ASN.1 DER, encode a SEQUENCE, Tom St Denis +*/ + +#ifdef LTC_DER + +/** + Encode a SEQUENCE type using a VA list + @param out [out] Destination for data + @param outlen [in/out] Length of buffer and resulting length of output + @remark <...> is of the form (int, unsigned long, void*) + @return CRYPT_OK on success +*/ +int der_encode_sequence_multi(unsigned char *out, unsigned long *outlen, ...) +{ + int err, type; + unsigned long size, x; + void *data; + va_list args; + ltc_asn1_list *list; + + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* get size of output that will be required */ + va_start(args, outlen); + x = 0; + for (;;) { + type = va_arg(args, int); + size = va_arg(args, unsigned long); + data = va_arg(args, void*); + + if (type == LTC_ASN1_EOL) { + break; + } + + switch (type) { + case LTC_ASN1_BOOLEAN: + case LTC_ASN1_INTEGER: + case LTC_ASN1_SHORT_INTEGER: + case LTC_ASN1_BIT_STRING: + case LTC_ASN1_OCTET_STRING: + case LTC_ASN1_NULL: + case LTC_ASN1_OBJECT_IDENTIFIER: + case LTC_ASN1_IA5_STRING: + case LTC_ASN1_PRINTABLE_STRING: + case LTC_ASN1_UTF8_STRING: + case LTC_ASN1_UTCTIME: + case LTC_ASN1_SEQUENCE: + case LTC_ASN1_SET: + case LTC_ASN1_SETOF: + ++x; + break; + + default: + va_end(args); + return CRYPT_INVALID_ARG; + } + } + va_end(args); + + /* allocate structure for x elements */ + if (x == 0) { + return CRYPT_NOP; + } + + list = XCALLOC(sizeof(*list), x); + if (list == NULL) { + return CRYPT_MEM; + } + + /* fill in the structure */ + va_start(args, outlen); + x = 0; + for (;;) { + type = va_arg(args, int); + size = va_arg(args, unsigned long); + data = va_arg(args, void*); + + if (type == LTC_ASN1_EOL) { + break; + } + + switch (type) { + case LTC_ASN1_BOOLEAN: + case LTC_ASN1_INTEGER: + case LTC_ASN1_SHORT_INTEGER: + case LTC_ASN1_BIT_STRING: + case LTC_ASN1_OCTET_STRING: + case LTC_ASN1_NULL: + case LTC_ASN1_OBJECT_IDENTIFIER: + case LTC_ASN1_IA5_STRING: + case LTC_ASN1_PRINTABLE_STRING: + case LTC_ASN1_UTF8_STRING: + case LTC_ASN1_UTCTIME: + case LTC_ASN1_SEQUENCE: + case LTC_ASN1_SET: + case LTC_ASN1_SETOF: + list[x].type = type; + list[x].size = size; + list[x++].data = data; + break; + + default: + va_end(args); + err = CRYPT_INVALID_ARG; + goto LBL_ERR; + } + } + va_end(args); + + err = der_encode_sequence(list, x, out, outlen); +LBL_ERR: + XFREE(list); + return err; +} + +#endif + + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_encode_sequence_multi.c,v $ */ +/* $Revision: 1.12 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_set.c b/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_set.c new file mode 100644 index 0000000000..25d247d184 --- /dev/null +++ b/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_set.c @@ -0,0 +1,103 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** + @file der_encode_set.c + ASN.1 DER, Encode a SET, Tom St Denis +*/ + +#ifdef LTC_DER + +/* LTC define to ASN.1 TAG */ +static int ltc_to_asn1(int v) +{ + switch (v) { + case LTC_ASN1_BOOLEAN: return 0x01; + case LTC_ASN1_INTEGER: + case LTC_ASN1_SHORT_INTEGER: return 0x02; + case LTC_ASN1_BIT_STRING: return 0x03; + case LTC_ASN1_OCTET_STRING: return 0x04; + case LTC_ASN1_NULL: return 0x05; + case LTC_ASN1_OBJECT_IDENTIFIER: return 0x06; + case LTC_ASN1_UTF8_STRING: return 0x0C; + case LTC_ASN1_PRINTABLE_STRING: return 0x13; + case LTC_ASN1_IA5_STRING: return 0x16; + case LTC_ASN1_UTCTIME: return 0x17; + case LTC_ASN1_SEQUENCE: return 0x30; + case LTC_ASN1_SET: + case LTC_ASN1_SETOF: return 0x31; + default: return -1; + } +} + + +static int LTC_CALL qsort_helper1(const void *a, const void *b) +{ + ltc_asn1_list *A = (ltc_asn1_list *)a, *B = (ltc_asn1_list *)b; + int r; + + r = ltc_to_asn1(A->type) - ltc_to_asn1(B->type); + + /* for QSORT the order is UNDEFINED if they are "equal" which means it is NOT DETERMINISTIC. So we force it to be :-) */ + if (r == 0) { + /* their order in the original list now determines the position */ + return A->used - B->used; + } else { + return r; + } +} + +/* + Encode a SET type + @param list The list of items to encode + @param inlen The number of items in the list + @param out [out] The destination + @param outlen [in/out] The size of the output + @return CRYPT_OK on success +*/ +int der_encode_set(ltc_asn1_list *list, unsigned long inlen, + unsigned char *out, unsigned long *outlen) +{ + ltc_asn1_list *copy; + unsigned long x; + int err; + + /* make copy of list */ + copy = XCALLOC(inlen, sizeof(*copy)); + if (copy == NULL) { + return CRYPT_MEM; + } + + /* fill in used member with index so we can fully sort it */ + for (x = 0; x < inlen; x++) { + copy[x] = list[x]; + copy[x].used = x; + } + + /* sort it by the "type" field */ + XQSORT(copy, inlen, sizeof(*copy), &qsort_helper1); + + /* call der_encode_sequence_ex() */ + err = der_encode_sequence_ex(copy, inlen, out, outlen, LTC_ASN1_SET); + + /* free list */ + XFREE(copy); + + return err; +} + + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/set/der_encode_set.c,v $ */ +/* $Revision: 1.12 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_setof.c b/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_setof.c new file mode 100644 index 0000000000..0987cabe8d --- /dev/null +++ b/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_setof.c @@ -0,0 +1,162 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** + @file der_encode_setof.c + ASN.1 DER, Encode SET OF, Tom St Denis +*/ + +#ifdef LTC_DER + +struct edge { + unsigned char *start; + unsigned long size; +}; + +static int LTC_CALL qsort_helper2(const void *a, const void *b) +{ + struct edge *A = (struct edge *)a, *B = (struct edge *)b; + int r; + unsigned long x; + + /* compare min length */ + r = XMEMCMP(A->start, B->start, MIN(A->size, B->size)); + + if (r == 0 && A->size != B->size) { + if (A->size > B->size) { + for (x = B->size; x < A->size; x++) { + if (A->start[x]) { + return 1; + } + } + } else { + for (x = A->size; x < B->size; x++) { + if (B->start[x]) { + return -1; + } + } + } + } + + return r; +} + +/** + Encode a SETOF stucture + @param list The list of items to encode + @param inlen The number of items in the list + @param out [out] The destination + @param outlen [in/out] The size of the output + @return CRYPT_OK on success +*/ +int der_encode_setof(ltc_asn1_list *list, unsigned long inlen, + unsigned char *out, unsigned long *outlen) +{ + unsigned long x, y, z, hdrlen; + int err; + struct edge *edges; + unsigned char *ptr, *buf; + + /* check that they're all the same type */ + for (x = 1; x < inlen; x++) { + if (list[x].type != list[x-1].type) { + return CRYPT_INVALID_ARG; + } + } + + /* alloc buffer to store copy of output */ + buf = XCALLOC(1, *outlen); + if (buf == NULL) { + return CRYPT_MEM; + } + + /* encode list */ + if ((err = der_encode_sequence_ex(list, inlen, buf, outlen, LTC_ASN1_SETOF)) != CRYPT_OK) { + XFREE(buf); + return err; + } + + /* allocate edges */ + edges = XCALLOC(inlen, sizeof(*edges)); + if (edges == NULL) { + XFREE(buf); + return CRYPT_MEM; + } + + /* skip header */ + ptr = buf + 1; + + /* now skip length data */ + x = *ptr++; + if (x >= 0x80) { + ptr += (x & 0x7F); + } + + /* get the size of the static header */ + hdrlen = (unsigned long)((size_t)ptr - (size_t)buf); + + + /* scan for edges */ + x = 0; + while (ptr < (buf + *outlen)) { + /* store start */ + edges[x].start = ptr; + + /* skip type */ + z = 1; + + /* parse length */ + y = ptr[z++]; + if (y < 128) { + edges[x].size = y; + } else { + y &= 0x7F; + edges[x].size = 0; + while (y--) { + edges[x].size = (edges[x].size << 8) | ((unsigned long)ptr[z++]); + } + } + + /* skip content */ + edges[x].size += z; + ptr += edges[x].size; + ++x; + } + + /* sort based on contents (using edges) */ + XQSORT(edges, inlen, sizeof(*edges), &qsort_helper2); + + /* copy static header */ + XMEMCPY(out, buf, hdrlen); + + /* copy+sort using edges+indecies to output from buffer */ + for (y = hdrlen, x = 0; x < inlen; x++) { + XMEMCPY(out+y, edges[x].start, edges[x].size); + y += edges[x].size; + } + +#ifdef LTC_CLEAN_STACK + zeromem(buf, *outlen); +#endif + + /* free buffers */ + XFREE(edges); + XFREE(buf); + + return CRYPT_OK; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/set/der_encode_setof.c,v $ */ +/* $Revision: 1.12 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_short_integer.c b/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_short_integer.c new file mode 100644 index 0000000000..9b167d0847 --- /dev/null +++ b/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_short_integer.c @@ -0,0 +1,97 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** + @file der_encode_short_integer.c + ASN.1 DER, encode an integer, Tom St Denis +*/ + + +#ifdef LTC_DER + +/** + Store a short integer in the range (0,2^32-1) + @param num The integer to encode + @param out [out] The destination for the DER encoded integers + @param outlen [in/out] The max size and resulting size of the DER encoded integers + @return CRYPT_OK if successful +*/ +int der_encode_short_integer(unsigned long num, unsigned char *out, unsigned long *outlen) +{ + unsigned long len, x, y, z; + int err; + + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* force to 32 bits */ + num &= 0xFFFFFFFFUL; + + /* find out how big this will be */ + if ((err = der_length_short_integer(num, &len)) != CRYPT_OK) { + return err; + } + + if (*outlen < len) { + *outlen = len; + return CRYPT_BUFFER_OVERFLOW; + } + + /* get len of output */ + z = 0; + y = num; + while (y) { + ++z; + y >>= 8; + } + + /* handle zero */ + if (z == 0) { + z = 1; + } + + /* see if msb is set */ + z += (num&(1UL<<((z<<3) - 1))) ? 1 : 0; + + /* adjust the number so the msB is non-zero */ + for (x = 0; (z <= 4) && (x < (4 - z)); x++) { + num <<= 8; + } + + /* store header */ + x = 0; + out[x++] = 0x02; + out[x++] = (unsigned char)z; + + /* if 31st bit is set output a leading zero and decrement count */ + if (z == 5) { + out[x++] = 0; + --z; + } + + /* store values */ + for (y = 0; y < z; y++) { + out[x++] = (unsigned char)((num >> 24) & 0xFF); + num <<= 8; + } + + /* we good */ + *outlen = x; + + return CRYPT_OK; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/short_integer/der_encode_short_integer.c,v $ */ +/* $Revision: 1.8 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_utctime.c b/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_utctime.c new file mode 100644 index 0000000000..167a2b4906 --- /dev/null +++ b/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_utctime.c @@ -0,0 +1,83 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** + @file der_encode_utctime.c + ASN.1 DER, encode a UTCTIME, Tom St Denis +*/ + +#ifdef LTC_DER + +static const char *baseten = "0123456789"; + +#define STORE_V(y) \ + out[x++] = der_ia5_char_encode(baseten[(y/10) % 10]); \ + out[x++] = der_ia5_char_encode(baseten[y % 10]); + +/** + Encodes a UTC time structure in DER format + @param utctime The UTC time structure to encode + @param out The destination of the DER encoding of the UTC time structure + @param outlen [in/out] The length of the DER encoding + @return CRYPT_OK if successful +*/ +int der_encode_utctime(ltc_utctime *utctime, + unsigned char *out, unsigned long *outlen) +{ + unsigned long x, tmplen; + int err; + + LTC_ARGCHK(utctime != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + if ((err = der_length_utctime(utctime, &tmplen)) != CRYPT_OK) { + return err; + } + if (tmplen > *outlen) { + *outlen = tmplen; + return CRYPT_BUFFER_OVERFLOW; + } + + /* store header */ + out[0] = 0x17; + + /* store values */ + x = 2; + STORE_V(utctime->YY); + STORE_V(utctime->MM); + STORE_V(utctime->DD); + STORE_V(utctime->hh); + STORE_V(utctime->mm); + STORE_V(utctime->ss); + + if (utctime->off_mm || utctime->off_hh) { + out[x++] = der_ia5_char_encode(utctime->off_dir ? '-' : '+'); + STORE_V(utctime->off_hh); + STORE_V(utctime->off_mm); + } else { + out[x++] = der_ia5_char_encode('Z'); + } + + /* store length */ + out[1] = (unsigned char)(x - 2); + + /* all good let's return */ + *outlen = x; + return CRYPT_OK; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utctime/der_encode_utctime.c,v $ */ +/* $Revision: 1.10 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_utf8_string.c b/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_utf8_string.c new file mode 100644 index 0000000000..84d8d06211 --- /dev/null +++ b/dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_utf8_string.c @@ -0,0 +1,105 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** + @file der_encode_utf8_string.c + ASN.1 DER, encode a UTF8 STRING, Tom St Denis +*/ + + +#ifdef LTC_DER + +/** + Store an UTF8 STRING + @param in The array of UTF8 to store (one per wchar_t) + @param inlen The number of UTF8 to store + @param out [out] The destination for the DER encoded UTF8 STRING + @param outlen [in/out] The max size and resulting size of the DER UTF8 STRING + @return CRYPT_OK if successful +*/ +int der_encode_utf8_string(const wchar_t *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen) +{ + unsigned long x, y, len; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* get the size */ + for (x = len = 0; x < inlen; x++) { + if (in[x] < 0 || in[x] > 0x1FFFF) { + return CRYPT_INVALID_ARG; + } + len += der_utf8_charsize(in[x]); + } + + if (len < 128) { + y = 2 + len; + } else if (len < 256) { + y = 3 + len; + } else if (len < 65536UL) { + y = 4 + len; + } else if (len < 16777216UL) { + y = 5 + len; + } else { + return CRYPT_INVALID_ARG; + } + + /* too big? */ + if (y > *outlen) { + *outlen = len; + return CRYPT_BUFFER_OVERFLOW; + } + + /* encode the header+len */ + x = 0; + out[x++] = 0x0C; + if (len < 128) { + out[x++] = (unsigned char)len; + } else if (len < 256) { + out[x++] = 0x81; + out[x++] = (unsigned char)len; + } else if (len < 65536UL) { + out[x++] = 0x82; + out[x++] = (unsigned char)((len>>8)&255); + out[x++] = (unsigned char)(len&255); + } else if (len < 16777216UL) { + out[x++] = 0x83; + out[x++] = (unsigned char)((len>>16)&255); + out[x++] = (unsigned char)((len>>8)&255); + out[x++] = (unsigned char)(len&255); + } else { + return CRYPT_INVALID_ARG; + } + + /* store UTF8 */ + for (y = 0; y < inlen; y++) { + switch (der_utf8_charsize(in[y])) { + case 1: out[x++] = (unsigned char)in[y]; break; + case 2: out[x++] = 0xC0 | ((in[y] >> 6) & 0x1F); out[x++] = 0x80 | (in[y] & 0x3F); break; + case 3: out[x++] = 0xE0 | ((in[y] >> 12) & 0x0F); out[x++] = 0x80 | ((in[y] >> 6) & 0x3F); out[x++] = 0x80 | (in[y] & 0x3F); break; + case 4: out[x++] = 0xF0 | ((in[y] >> 18) & 0x07); out[x++] = 0x80 | ((in[y] >> 12) & 0x3F); out[x++] = 0x80 | ((in[y] >> 6) & 0x3F); out[x++] = 0x80 | (in[y] & 0x3F); break; + } + } + + /* retun length */ + *outlen = x; + + return CRYPT_OK; +} + +#endif + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utf8/der_encode_utf8_string.c,v $ */ +/* $Revision: 1.9 $ */ +/* $Date: 2006/12/28 01:27:24 $ */ diff --git a/dep/StormLib/src/libtomcrypt/src/pk/pkcs1/pkcs_1_pss_encode.c b/dep/StormLib/src/libtomcrypt/src/pk/pkcs1/pkcs_1_pss_encode.c new file mode 100644 index 0000000000..68d5e86159 --- /dev/null +++ b/dep/StormLib/src/libtomcrypt/src/pk/pkcs1/pkcs_1_pss_encode.c @@ -0,0 +1,175 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** + @file pkcs_1_pss_encode.c + LTC_PKCS #1 PSS Signature Padding, Tom St Denis +*/ + +#ifdef LTC_PKCS_1 + +/** + LTC_PKCS #1 v2.00 Signature Encoding + @param msghash The hash to encode + @param msghashlen The length of the hash (octets) + @param saltlen The length of the salt desired (octets) + @param prng An active PRNG context + @param prng_idx The index of the PRNG desired + @param hash_idx The index of the hash desired + @param modulus_bitlen The bit length of the RSA modulus + @param out [out] The destination of the encoding + @param outlen [in/out] The max size and resulting size of the encoded data + @return CRYPT_OK if successful +*/ +int pkcs_1_pss_encode(const unsigned char *msghash, unsigned long msghashlen, + unsigned long saltlen, prng_state *prng, + int prng_idx, int hash_idx, + unsigned long modulus_bitlen, + unsigned char *out, unsigned long *outlen) +{ + unsigned char *DB, *mask, *salt, *hash; + unsigned long x, y, hLen, modulus_len; + int err; + hash_state md; + + LTC_ARGCHK(msghash != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + /* ensure hash and PRNG are valid */ + if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { + return err; + } + if ((err = prng_is_valid(prng_idx)) != CRYPT_OK) { + return err; + } + + hLen = hash_descriptor[hash_idx].hashsize; + modulus_len = (modulus_bitlen>>3) + (modulus_bitlen & 7 ? 1 : 0); + + /* check sizes */ + if ((saltlen > modulus_len) || (modulus_len < hLen + saltlen + 2)) { + return CRYPT_PK_INVALID_SIZE; + } + + /* allocate ram for DB/mask/salt/hash of size modulus_len */ + DB = XMALLOC(modulus_len); + mask = XMALLOC(modulus_len); + salt = XMALLOC(modulus_len); + hash = XMALLOC(modulus_len); + if (DB == NULL || mask == NULL || salt == NULL || hash == NULL) { + if (DB != NULL) { + XFREE(DB); + } + if (mask != NULL) { + XFREE(mask); + } + if (salt != NULL) { + XFREE(salt); + } + if (hash != NULL) { + XFREE(hash); + } + return CRYPT_MEM; + } + + + /* generate random salt */ + if (saltlen > 0) { + if (prng_descriptor[prng_idx].read(salt, saltlen, prng) != saltlen) { + err = CRYPT_ERROR_READPRNG; + goto LBL_ERR; + } + } + + /* M = (eight) 0x00 || msghash || salt, hash = H(M) */ + if ((err = hash_descriptor[hash_idx].init(&md)) != CRYPT_OK) { + goto LBL_ERR; + } + zeromem(DB, 8); + if ((err = hash_descriptor[hash_idx].process(&md, DB, 8)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash_idx].process(&md, msghash, msghashlen)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash_idx].process(&md, salt, saltlen)) != CRYPT_OK) { + goto LBL_ERR; + } + if ((err = hash_descriptor[hash_idx].done(&md, hash)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* generate DB = PS || 0x01 || salt, PS == modulus_len - saltlen - hLen - 2 zero bytes */ + x = 0; + XMEMSET(DB + x, 0, modulus_len - saltlen - hLen - 2); + x += modulus_len - saltlen - hLen - 2; + DB[x++] = 0x01; + XMEMCPY(DB + x, salt, saltlen); + x += saltlen; + + /* generate mask of length modulus_len - hLen - 1 from hash */ + if ((err = pkcs_1_mgf1(hash_idx, hash, hLen, mask, modulus_len - hLen - 1)) != CRYPT_OK) { + goto LBL_ERR; + } + + /* xor against DB */ + for (y = 0; y < (modulus_len - hLen - 1); y++) { + DB[y] ^= mask[y]; + } + + /* output is DB || hash || 0xBC */ + if (*outlen < modulus_len) { + *outlen = modulus_len; + err = CRYPT_BUFFER_OVERFLOW; + goto LBL_ERR; + } + + /* DB len = modulus_len - hLen - 1 */ + y = 0; + XMEMCPY(out + y, DB, modulus_len - hLen - 1); + y += modulus_len - hLen - 1; + + /* hash */ + XMEMCPY(out + y, hash, hLen); + y += hLen; + + /* 0xBC */ + out[y] = 0xBC; + + /* now clear the 8*modulus_len - modulus_bitlen most significant bits */ + out[0] &= 0xFF >> ((modulus_len<<3) - (modulus_bitlen-1)); + + /* store output size */ + *outlen = modulus_len; + err = CRYPT_OK; +LBL_ERR: +#ifdef LTC_CLEAN_STACK + zeromem(DB, modulus_len); + zeromem(mask, modulus_len); + zeromem(salt, modulus_len); + zeromem(hash, modulus_len); +#endif + + XFREE(hash); + XFREE(salt); + XFREE(mask); + XFREE(DB); + + return err; +} + +#endif /* LTC_PKCS_1 */ + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_pss_encode.c,v $ */ +/* $Revision: 1.9 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ diff --git a/dep/StormLib/src/libtomcrypt/src/pk/pkcs1/pkcs_1_v1_5_encode.c b/dep/StormLib/src/libtomcrypt/src/pk/pkcs1/pkcs_1_v1_5_encode.c new file mode 100644 index 0000000000..4342919edf --- /dev/null +++ b/dep/StormLib/src/libtomcrypt/src/pk/pkcs1/pkcs_1_v1_5_encode.c @@ -0,0 +1,111 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/*! \file pkcs_1_v1_5_encode.c + * + * LTC_PKCS #1 v1.5 Padding (Andreas Lange) + */ + +#ifdef LTC_PKCS_1 + +/*! \brief LTC_PKCS #1 v1.5 encode. + * + * \param msg The data to encode + * \param msglen The length of the data to encode (octets) + * \param block_type Block type to use in padding (\sa ltc_pkcs_1_v1_5_blocks) + * \param modulus_bitlen The bit length of the RSA modulus + * \param prng An active PRNG state (only for LTC_LTC_PKCS_1_EME) + * \param prng_idx The index of the PRNG desired (only for LTC_LTC_PKCS_1_EME) + * \param out [out] The destination for the encoded data + * \param outlen [in/out] The max size and resulting size of the encoded data + * + * \return CRYPT_OK if successful + */ +int pkcs_1_v1_5_encode(const unsigned char *msg, + unsigned long msglen, + int block_type, + unsigned long modulus_bitlen, + prng_state *prng, + int prng_idx, + unsigned char *out, + unsigned long *outlen) +{ + unsigned long modulus_len, ps_len, i; + unsigned char *ps; + int result; + + /* valid block_type? */ + if ((block_type != LTC_LTC_PKCS_1_EMSA) && + (block_type != LTC_LTC_PKCS_1_EME)) { + return CRYPT_PK_INVALID_PADDING; + } + + if (block_type == LTC_LTC_PKCS_1_EME) { /* encryption padding, we need a valid PRNG */ + if ((result = prng_is_valid(prng_idx)) != CRYPT_OK) { + return result; + } + } + + modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0); + + /* test message size */ + if ((msglen + 11) > modulus_len) { + return CRYPT_PK_INVALID_SIZE; + } + + if (*outlen < modulus_len) { + *outlen = modulus_len; + result = CRYPT_BUFFER_OVERFLOW; + goto bail; + } + + /* generate an octets string PS */ + ps = &out[2]; + ps_len = modulus_len - msglen - 3; + + if (block_type == LTC_LTC_PKCS_1_EME) { + /* now choose a random ps */ + if (prng_descriptor[prng_idx].read(ps, ps_len, prng) != ps_len) { + result = CRYPT_ERROR_READPRNG; + goto bail; + } + + /* transform zero bytes (if any) to non-zero random bytes */ + for (i = 0; i < ps_len; i++) { + while (ps[i] == 0) { + if (prng_descriptor[prng_idx].read(&ps[i], 1, prng) != 1) { + result = CRYPT_ERROR_READPRNG; + goto bail; + } + } + } + } else { + XMEMSET(ps, 0xFF, ps_len); + } + + /* create string of length modulus_len */ + out[0] = 0x00; + out[1] = (unsigned char)block_type; /* block_type 1 or 2 */ + out[2 + ps_len] = 0x00; + XMEMCPY(&out[2 + ps_len + 1], msg, msglen); + *outlen = modulus_len; + + result = CRYPT_OK; +bail: + return result; +} /* pkcs_1_v1_5_encode */ + +#endif /* #ifdef LTC_PKCS_1 */ + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_v1_5_encode.c,v $ */ +/* $Revision: 1.4 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ diff --git a/dep/StormLib/src/libtomcrypt/src/pk/rsa/rsa_sign_hash.c b/dep/StormLib/src/libtomcrypt/src/pk/rsa/rsa_sign_hash.c new file mode 100644 index 0000000000..49fb858754 --- /dev/null +++ b/dep/StormLib/src/libtomcrypt/src/pk/rsa/rsa_sign_hash.c @@ -0,0 +1,134 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "../../headers/tomcrypt.h" + +/** + @file rsa_sign_hash.c + RSA LTC_PKCS #1 v1.5 and v2 PSS sign hash, Tom St Denis and Andreas Lange +*/ + +#ifdef LTC_MRSA + +/** + LTC_PKCS #1 pad then sign + @param in The hash to sign + @param inlen The length of the hash to sign (octets) + @param out [out] The signature + @param outlen [in/out] The max size and resulting size of the signature + @param padding Type of padding (LTC_LTC_PKCS_1_PSS or LTC_LTC_PKCS_1_V1_5) + @param prng An active PRNG state + @param prng_idx The index of the PRNG desired + @param hash_idx The index of the hash desired + @param saltlen The length of the salt desired (octets) + @param key The private RSA key to use + @return CRYPT_OK if successful +*/ +int rsa_sign_hash_ex(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen, + int padding, + prng_state *prng, int prng_idx, + int hash_idx, unsigned long saltlen, + rsa_key *key) +{ + unsigned long modulus_bitlen, modulus_bytelen, x, y; + int err; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(key != NULL); + + /* valid padding? */ + if ((padding != LTC_LTC_PKCS_1_V1_5) && (padding != LTC_LTC_PKCS_1_PSS)) { + return CRYPT_PK_INVALID_PADDING; + } + + if (padding == LTC_LTC_PKCS_1_PSS) { + /* valid prng and hash ? */ + if ((err = prng_is_valid(prng_idx)) != CRYPT_OK) { + return err; + } + if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { + return err; + } + } + + /* get modulus len in bits */ + modulus_bitlen = mp_count_bits((key->N)); + + /* outlen must be at least the size of the modulus */ + modulus_bytelen = mp_unsigned_bin_size((key->N)); + if (modulus_bytelen > *outlen) { + *outlen = modulus_bytelen; + return CRYPT_BUFFER_OVERFLOW; + } + + if (padding == LTC_LTC_PKCS_1_PSS) { + /* PSS pad the key */ + x = *outlen; + if ((err = pkcs_1_pss_encode(in, inlen, saltlen, prng, prng_idx, + hash_idx, modulus_bitlen, out, &x)) != CRYPT_OK) { + return err; + } + } else { + /* LTC_PKCS #1 v1.5 pad the hash */ + unsigned char *tmpin; + ltc_asn1_list digestinfo[2], siginfo[2]; + + /* not all hashes have OIDs... so sad */ + if (hash_descriptor[hash_idx].OIDlen == 0) { + return CRYPT_INVALID_ARG; + } + + /* construct the SEQUENCE + SEQUENCE { + SEQUENCE {hashoid OID + blah NULL + } + hash OCTET STRING + } + */ + LTC_SET_ASN1(digestinfo, 0, LTC_ASN1_OBJECT_IDENTIFIER, hash_descriptor[hash_idx].OID, hash_descriptor[hash_idx].OIDlen); + LTC_SET_ASN1(digestinfo, 1, LTC_ASN1_NULL, NULL, 0); + LTC_SET_ASN1(siginfo, 0, LTC_ASN1_SEQUENCE, digestinfo, 2); + LTC_SET_ASN1(siginfo, 1, LTC_ASN1_OCTET_STRING, in, inlen); + + /* allocate memory for the encoding */ + y = mp_unsigned_bin_size(key->N); + tmpin = XMALLOC(y); + if (tmpin == NULL) { + return CRYPT_MEM; + } + + if ((err = der_encode_sequence(siginfo, 2, tmpin, &y)) != CRYPT_OK) { + XFREE(tmpin); + return err; + } + + x = *outlen; + if ((err = pkcs_1_v1_5_encode(tmpin, y, LTC_LTC_PKCS_1_EMSA, + modulus_bitlen, NULL, 0, + out, &x)) != CRYPT_OK) { + XFREE(tmpin); + return err; + } + XFREE(tmpin); + } + + /* RSA encode it */ + return ltc_mp.rsa_me(out, x, out, outlen, PK_PRIVATE, key); +} + +#endif /* LTC_MRSA */ + +/* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_sign_hash.c,v $ */ +/* $Revision: 1.11 $ */ +/* $Date: 2007/05/12 14:32:35 $ */ diff --git a/dep/StormLib/src/pklib/explode.c b/dep/StormLib/src/pklib/explode.c index 0d327a5da8..c211768fff 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[] = +const unsigned char DistBits[0x40] = { 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[] = +const unsigned char DistCode[0x40] = { 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, @@ -47,28 +47,28 @@ static unsigned char DistCode[] = 0xF0, 0x70, 0xB0, 0x30, 0xD0, 0x50, 0x90, 0x10, 0xE0, 0x60, 0xA0, 0x20, 0xC0, 0x40, 0x80, 0x00 }; -static unsigned char ExLenBits[] = +const unsigned char ExLenBits[0x10] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; -static unsigned short LenBase[] = +const unsigned short LenBase[0x10] = { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x000A, 0x000E, 0x0016, 0x0026, 0x0046, 0x0086, 0x0106 }; -static unsigned char LenBits[] = +const unsigned char LenBits[0x10] = { 0x03, 0x02, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07, 0x07 }; -static unsigned char LenCode[] = +const unsigned char LenCode[0x10] = { 0x05, 0x03, 0x01, 0x06, 0x0A, 0x02, 0x0C, 0x14, 0x04, 0x18, 0x08, 0x30, 0x10, 0x20, 0x40, 0x00 }; -static unsigned char ChBitsAsc[] = +const unsigned char ChBitsAsc[0x100] = { 0x0B, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x08, 0x07, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0C, 0x0C, 0x0C, 0x0C, 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[] = +const unsigned short ChCodeAsc[0x100] = { 0x0490, 0x0FE0, 0x07E0, 0x0BE0, 0x03E0, 0x0DE0, 0x05E0, 0x09E0, 0x01E0, 0x00B8, 0x0062, 0x0EE0, 0x06E0, 0x0022, 0x0AE0, 0x02E0, @@ -121,20 +121,20 @@ 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 }; //----------------------------------------------------------------------------- // Local functions static void GenDecodeTabs( - unsigned char * positions, // [out] Table of positions - unsigned char * start_indexes, // [in] Table of start indexes - unsigned char * length_bits, // [in] Table of lengths. Each length is stored as number of bits - size_t elements) // [in] Number of elements in start_indexes and length_bits + unsigned char * positions, // [out] Table of positions + const unsigned char * start_indexes, // [in] Table of start indexes + const unsigned char * length_bits, // [in] Table of lengths. Each length is stored as number of bits + size_t elements) // [in] Number of elements in start_indexes and length_bits { - unsigned long index; - unsigned long length; + unsigned int index; + unsigned int length; size_t i; for(i = 0; i < elements; i++) @@ -150,8 +150,8 @@ static void GenDecodeTabs( static void GenAscTabs(TDcmpStruct * pWork) { - unsigned short * pChCodeAsc = &ChCodeAsc[0xFF]; - unsigned long acc, add; + const unsigned short * pChCodeAsc = &ChCodeAsc[0xFF]; + unsigned int acc, add; unsigned short count; for(count = 0x00FF; pChCodeAsc >= ChCodeAsc; pChCodeAsc--, count--) @@ -227,7 +227,7 @@ static void GenAscTabs(TDcmpStruct * pWork) // Returns: PKDCL_OK: Operation was successful // PKDCL_STREAM_END: There are no more bits in the input buffer -static int WasteBits(TDcmpStruct * pWork, unsigned long nBits) +static int WasteBits(TDcmpStruct * pWork, unsigned int nBits) { // If number of bits required is less than number of (bits in the buffer) ? if(nBits <= pWork->extra_bits) @@ -267,30 +267,30 @@ static int WasteBits(TDcmpStruct * pWork, unsigned long nBits) // 0x305: End of stream // 0x306: Error -static unsigned long DecodeLit(TDcmpStruct * pWork) +static unsigned int DecodeLit(TDcmpStruct * pWork) { - unsigned long extra_length_bits; // Number of bits of extra literal length - unsigned long length_code; // Length code - unsigned long value; + unsigned int extra_length_bits; // Number of bits of extra literal length + unsigned int length_code; // Length code + unsigned int value; // Test the current bit in byte buffer. If is not set, simply return the next 8 bits. if(pWork->bit_buff & 1) { // 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 + + // Remove the appropriate number of bits if(WasteBits(pWork, pWork->LenBits[length_code])) return 0x306; // Are there some extra bits for the obtained length code ? if((extra_length_bits = pWork->ExLenBits[length_code]) != 0) { - unsigned long extra_length = pWork->bit_buff & ((1 << extra_length_bits) - 1); + unsigned int extra_length = pWork->bit_buff & ((1 << extra_length_bits) - 1); if(WasteBits(pWork, extra_length_bits)) { @@ -312,7 +312,7 @@ static unsigned long DecodeLit(TDcmpStruct * pWork) // If the binary compression type, read 8 bits and return them as one byte. if(pWork->ctype == CMP_BINARY) { - unsigned long uncompressed_byte = pWork->bit_buff & 0xFF; + unsigned int uncompressed_byte = pWork->bit_buff & 0xFF; if(WasteBits(pWork, 8)) return 0x306; @@ -357,11 +357,11 @@ static unsigned long DecodeLit(TDcmpStruct * pWork) // Decodes the distance of the repetition, backwards relative to the // current output buffer position -static unsigned long DecodeDist(TDcmpStruct * pWork, unsigned long rep_length) +static unsigned int DecodeDist(TDcmpStruct * pWork, unsigned int rep_length) { - unsigned long dist_pos_code; // Distance position code - unsigned long dist_pos_bits; // Number of bits of distance position - unsigned long distance; // Distance position + unsigned int dist_pos_code; // Distance position code + unsigned int dist_pos_bits; // Number of bits of distance position + unsigned int distance; // Distance position // Next 2-8 bits in the input buffer is the distance position code dist_pos_code = pWork->DistPosCodes[pWork->bit_buff & 0xFF]; @@ -388,10 +388,10 @@ static unsigned long DecodeDist(TDcmpStruct * pWork, unsigned long rep_length) return distance + 1; } -static unsigned long Expand(TDcmpStruct * pWork) +static unsigned int Expand(TDcmpStruct * pWork) { - unsigned long next_literal; // Literal decoded from the compressed data - unsigned long result; // Value to be returned + unsigned int next_literal; // Literal decoded from the compressed data + unsigned int result; // Value to be returned unsigned int copyBytes; // Number of bytes to copy to the output buffer pWork->outputPos = 0x1000; // Initialize output buffer position @@ -412,8 +412,8 @@ static unsigned long Expand(TDcmpStruct * pWork) { unsigned char * source; unsigned char * target; - unsigned long rep_length; // Length of the repetition, in bytes - unsigned long minus_dist; // Backward distance to the repetition, relative to the current buffer position + unsigned int rep_length; // Length of the repetition, in bytes + unsigned int minus_dist; // Backward distance to the repetition, relative to the current buffer position // Get the length of the repeating sequence. // Note that the repeating block may overlap the current output position, @@ -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) { @@ -455,7 +455,7 @@ static unsigned long Expand(TDcmpStruct * pWork) // Note that if the output buffer overflowed previously, the extra decompressed bytes // are stored in "out_buff_overflow", and they will now be // within decompressed part of the output buffer. - memcpy(pWork->out_buff, &pWork->out_buff[0x1000], pWork->outputPos - 0x1000); + memmove(pWork->out_buff, &pWork->out_buff[0x1000], pWork->outputPos - 0x1000); pWork->outputPos -= 0x1000; } } @@ -470,7 +470,7 @@ static unsigned long Expand(TDcmpStruct * pWork) //----------------------------------------------------------------------------- // Main exploding function. -unsigned int explode( +unsigned int PKEXPORT explode( unsigned int (*read_buf)(char *buf, unsigned int *size, void *param), void (*write_buf)(char *buf, unsigned int *size, void *param), char *work_buf, @@ -479,7 +479,6 @@ unsigned int explode( TDcmpStruct * pWork = (TDcmpStruct *)work_buf; // Initialize work struct and load compressed data - // Note: The caller must zero the "work_buff" before passing it to explode pWork->read_buf = read_buf; pWork->write_buf = write_buf; pWork->param = param; @@ -495,7 +494,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 @@ -510,13 +509,13 @@ unsigned int explode( } memcpy(pWork->LenBits, LenBits, sizeof(pWork->LenBits)); - GenDecodeTabs(pWork->LengthCodes, LenCode, pWork->LenBits, sizeof(pWork->LenBits)); + GenDecodeTabs(pWork->LengthCodes, LenCode, LenBits, sizeof(LenBits)); memcpy(pWork->ExLenBits, ExLenBits, sizeof(pWork->ExLenBits)); memcpy(pWork->LenBase, LenBase, sizeof(pWork->LenBase)); memcpy(pWork->DistBits, DistBits, sizeof(pWork->DistBits)); - GenDecodeTabs(pWork->DistPosCodes, DistCode, pWork->DistBits, sizeof(pWork->DistBits)); + GenDecodeTabs(pWork->DistPosCodes, DistCode, DistBits, sizeof(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 1771b1862a..9a569f1641 100644 --- a/dep/StormLib/src/pklib/implode.c +++ b/dep/StormLib/src/pklib/implode.c @@ -12,6 +12,7 @@ #include #include +#include #include "pklib.h" @@ -24,96 +25,6 @@ #define MAX_REP_LENGTH 0x204 // The longest allowed repetition -//----------------------------------------------------------------------------- -// Tables - -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, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 -}; - -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, - 0x74, 0x34, 0x54, 0x14, 0x64, 0x24, 0x44, 0x04, 0x78, 0x38, 0x58, 0x18, 0x68, 0x28, 0x48, 0x08, - 0xF0, 0x70, 0xB0, 0x30, 0xD0, 0x50, 0x90, 0x10, 0xE0, 0x60, 0xA0, 0x20, 0xC0, 0x40, 0x80, 0x00 -}; - -static unsigned char ExLenBits[] = -{ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 -}; - -static unsigned char LenBits[] = -{ - 0x03, 0x02, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07, 0x07 -}; - -static unsigned char LenCode[] = -{ - 0x05, 0x03, 0x01, 0x06, 0x0A, 0x02, 0x0C, 0x14, 0x04, 0x18, 0x08, 0x30, 0x10, 0x20, 0x40, 0x00 -}; - -static unsigned char ChBitsAsc[] = -{ - 0x0B, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x08, 0x07, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x04, 0x0A, 0x08, 0x0C, 0x0A, 0x0C, 0x0A, 0x08, 0x07, 0x07, 0x08, 0x09, 0x07, 0x06, 0x07, 0x08, - 0x07, 0x06, 0x07, 0x07, 0x07, 0x07, 0x08, 0x07, 0x07, 0x08, 0x08, 0x0C, 0x0B, 0x07, 0x09, 0x0B, - 0x0C, 0x06, 0x07, 0x06, 0x06, 0x05, 0x07, 0x08, 0x08, 0x06, 0x0B, 0x09, 0x06, 0x07, 0x06, 0x06, - 0x07, 0x0B, 0x06, 0x06, 0x06, 0x07, 0x09, 0x08, 0x09, 0x09, 0x0B, 0x08, 0x0B, 0x09, 0x0C, 0x08, - 0x0C, 0x05, 0x06, 0x06, 0x06, 0x05, 0x06, 0x06, 0x06, 0x05, 0x0B, 0x07, 0x05, 0x06, 0x05, 0x05, - 0x06, 0x0A, 0x05, 0x05, 0x05, 0x05, 0x08, 0x07, 0x08, 0x08, 0x0A, 0x0B, 0x0B, 0x0C, 0x0C, 0x0C, - 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, - 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, - 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, - 0x0D, 0x0D, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D -}; - -static unsigned short ChCodeAsc[] = -{ - 0x0490, 0x0FE0, 0x07E0, 0x0BE0, 0x03E0, 0x0DE0, 0x05E0, 0x09E0, - 0x01E0, 0x00B8, 0x0062, 0x0EE0, 0x06E0, 0x0022, 0x0AE0, 0x02E0, - 0x0CE0, 0x04E0, 0x08E0, 0x00E0, 0x0F60, 0x0760, 0x0B60, 0x0360, - 0x0D60, 0x0560, 0x1240, 0x0960, 0x0160, 0x0E60, 0x0660, 0x0A60, - 0x000F, 0x0250, 0x0038, 0x0260, 0x0050, 0x0C60, 0x0390, 0x00D8, - 0x0042, 0x0002, 0x0058, 0x01B0, 0x007C, 0x0029, 0x003C, 0x0098, - 0x005C, 0x0009, 0x001C, 0x006C, 0x002C, 0x004C, 0x0018, 0x000C, - 0x0074, 0x00E8, 0x0068, 0x0460, 0x0090, 0x0034, 0x00B0, 0x0710, - 0x0860, 0x0031, 0x0054, 0x0011, 0x0021, 0x0017, 0x0014, 0x00A8, - 0x0028, 0x0001, 0x0310, 0x0130, 0x003E, 0x0064, 0x001E, 0x002E, - 0x0024, 0x0510, 0x000E, 0x0036, 0x0016, 0x0044, 0x0030, 0x00C8, - 0x01D0, 0x00D0, 0x0110, 0x0048, 0x0610, 0x0150, 0x0060, 0x0088, - 0x0FA0, 0x0007, 0x0026, 0x0006, 0x003A, 0x001B, 0x001A, 0x002A, - 0x000A, 0x000B, 0x0210, 0x0004, 0x0013, 0x0032, 0x0003, 0x001D, - 0x0012, 0x0190, 0x000D, 0x0015, 0x0005, 0x0019, 0x0008, 0x0078, - 0x00F0, 0x0070, 0x0290, 0x0410, 0x0010, 0x07A0, 0x0BA0, 0x03A0, - 0x0240, 0x1C40, 0x0C40, 0x1440, 0x0440, 0x1840, 0x0840, 0x1040, - 0x0040, 0x1F80, 0x0F80, 0x1780, 0x0780, 0x1B80, 0x0B80, 0x1380, - 0x0380, 0x1D80, 0x0D80, 0x1580, 0x0580, 0x1980, 0x0980, 0x1180, - 0x0180, 0x1E80, 0x0E80, 0x1680, 0x0680, 0x1A80, 0x0A80, 0x1280, - 0x0280, 0x1C80, 0x0C80, 0x1480, 0x0480, 0x1880, 0x0880, 0x1080, - 0x0080, 0x1F00, 0x0F00, 0x1700, 0x0700, 0x1B00, 0x0B00, 0x1300, - 0x0DA0, 0x05A0, 0x09A0, 0x01A0, 0x0EA0, 0x06A0, 0x0AA0, 0x02A0, - 0x0CA0, 0x04A0, 0x08A0, 0x00A0, 0x0F20, 0x0720, 0x0B20, 0x0320, - 0x0D20, 0x0520, 0x0920, 0x0120, 0x0E20, 0x0620, 0x0A20, 0x0220, - 0x0C20, 0x0420, 0x0820, 0x0020, 0x0FC0, 0x07C0, 0x0BC0, 0x03C0, - 0x0DC0, 0x05C0, 0x09C0, 0x01C0, 0x0EC0, 0x06C0, 0x0AC0, 0x02C0, - 0x0CC0, 0x04C0, 0x08C0, 0x00C0, 0x0F40, 0x0740, 0x0B40, 0x0340, - 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 -}; - //----------------------------------------------------------------------------- // Macros @@ -129,7 +40,7 @@ static unsigned short ChCodeAsc[] = // Builds the "hash_to_index" table and "pair_hash_offsets" table. // Every element of "hash_to_index" will contain lowest index to the // "pair_hash_offsets" table, effectively giving offset of the first -// occurence of the given PAIR_HASH in the input data. +// occurrence of the given PAIR_HASH in the input data. static void SortBuffer(TCmpStruct * pWork, unsigned char * buffer_begin, unsigned char * buffer_end) { unsigned short * phash_to_index; @@ -140,7 +51,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 +61,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: @@ -165,7 +76,7 @@ static void SortBuffer(TCmpStruct * pWork, unsigned char * buffer_begin, unsigne } // Step 3: Convert the table to the array of indexes. - // Now, each element contains index to the first occurence of given PAIR_HASH + // Now, each element contains index to the first occurrence of given PAIR_HASH for(buffer_end--; buffer_end >= buffer_begin; buffer_end--) { byte_pair_hash = BYTE_PAIR_HASH(buffer_end); @@ -218,7 +129,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; } @@ -235,21 +146,21 @@ 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 + // (a previous occurrence of the current byte sequence) +// Returns length of the repetition, and stores the backward distance // to pWork structure. static unsigned int FindRep(TCmpStruct * pWork, unsigned char * input_data) { unsigned short * phash_to_index; // Pointer into pWork->phash_to_index table unsigned short * phash_offs; // Pointer to the table containing offsets of each PAIR_HASH unsigned char * repetition_limit; // An eventual repetition must be at position below this pointer - unsigned char * prev_repetition; // Pointer to the previous occurence of the current PAIR_HASH + unsigned char * prev_repetition; // Pointer to the previous occurrence of the current PAIR_HASH unsigned char * prev_rep_end; // End of the previous repetition unsigned char * input_data_ptr; unsigned short phash_offs_index; // Index to the table with PAIR_HASH positions unsigned short min_phash_offs; // The lowest allowed hash offset - unsigned short offs_in_rep; // Offset within found repetition - unsigned int equal_byte_count; // Number of bytes that are equal to the previous occurence + unsigned int offs_in_rep; // Offset within found repetition + unsigned int equal_byte_count; // Number of bytes that are equal to the previous occurrence unsigned int rep_length = 1; // Length of the found repetition unsigned int rep_length2; // Secondary repetition unsigned char pre_last_byte; // Last but one byte from a repetion @@ -277,7 +188,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 +214,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; @@ -330,7 +241,7 @@ static unsigned int FindRep(TCmpStruct * pWork, unsigned char * input_data) } // Move forward in the table of PAIR_HASH repetitions. - // There might be a more recent occurence of the same repetition. + // There might be a more recent occurrence of the same repetition. phash_offs_index++; phash_offs++; prev_repetition = pWork->work_buff + phash_offs[0]; @@ -343,7 +254,7 @@ static unsigned int FindRep(TCmpStruct * pWork, unsigned char * input_data) } } - // If the repetition has max length of 0x204 bytes, we can't go any fuhrter + // If the repetition has max length of 0x204 bytes, we can't go any further if(equal_byte_count == MAX_REP_LENGTH) { pWork->distance--; @@ -382,7 +293,7 @@ static unsigned int FindRep(TCmpStruct * pWork, unsigned char * input_data) // The last repetition is the best one. // - pWork->offs09BC[0] = 0xFFFF; + pWork->offs09BC[0] = USHRT_MAX; pWork->offs09BC[1] = 0x0000; di_val = 0; @@ -393,13 +304,13 @@ static unsigned int FindRep(TCmpStruct * pWork, unsigned char * input_data) if(input_data[offs_in_rep] != input_data[di_val]) { di_val = pWork->offs09BC[di_val]; - if(di_val != 0xFFFF) + if(di_val != USHRT_MAX) continue; } 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,11 +319,11 @@ 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]; - if(rep_length2 == 0xFFFF) + if(rep_length2 == USHRT_MAX) rep_length2 = 0; // Get the pointer to the previous repetition @@ -465,7 +376,7 @@ static unsigned int FindRep(TCmpStruct * pWork, unsigned char * input_data) // Find out how many more characters are equal to the first repetition. while(*prev_rep_end == input_data[rep_length2]) { - if(++rep_length2 >= 0x204) + if(++rep_length2 >= MAX_REP_LENGTH) break; prev_rep_end++; } @@ -475,7 +386,7 @@ static unsigned int FindRep(TCmpStruct * pWork, unsigned char * input_data) { // Calculate the distance of the new repetition pWork->distance = (unsigned int)(input_data - prev_repetition - 1); - if((rep_length = rep_length2) == 0x204) + if((rep_length = rep_length2) == MAX_REP_LENGTH) return rep_length; // Update the additional elements in the "offs09BC" table @@ -485,7 +396,7 @@ static unsigned int FindRep(TCmpStruct * pWork, unsigned char * input_data) if(input_data[offs_in_rep] != input_data[di_val]) { di_val = pWork->offs09BC[di_val]; - if(di_val != 0xFFFF) + if(di_val != USHRT_MAX) continue; } pWork->offs09BC[++offs_in_rep] = ++di_val; @@ -497,12 +408,12 @@ static unsigned int FindRep(TCmpStruct * pWork, unsigned char * input_data) static void WriteCmpData(TCmpStruct * pWork) { unsigned char * input_data_end; // Pointer to the end of the input data - unsigned char * input_data = pWork->work_buff + pWork->dsize_bytes + 0x204; + unsigned char * input_data = pWork->work_buff + pWork->dsize_bytes + MAX_REP_LENGTH; unsigned int input_data_ended = 0; // If 1, then all data from the input stream have been already loaded 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; @@ -522,7 +433,7 @@ static void WriteCmpData(TCmpStruct * pWork) // Load the bytes from the input stream, up to 0x1000 bytes while(bytes_to_load != 0) { - bytes_loaded = pWork->read_buf((char *)pWork->work_buff + pWork->dsize_bytes + 0x204 + total_loaded, + bytes_loaded = pWork->read_buf((char *)pWork->work_buff + pWork->dsize_bytes + MAX_REP_LENGTH + total_loaded, &bytes_to_load, pWork->param); if(bytes_loaded == 0) @@ -541,13 +452,13 @@ static void WriteCmpData(TCmpStruct * pWork) input_data_end = pWork->work_buff + pWork->dsize_bytes + total_loaded; if(input_data_ended) - input_data_end += 0x204; - + input_data_end += MAX_REP_LENGTH; + // // 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 +467,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) @@ -564,7 +475,7 @@ static void WriteCmpData(TCmpStruct * pWork) break; case 1: - SortBuffer(pWork, input_data - pWork->dsize_bytes + 0x204, input_data_end + 1); + SortBuffer(pWork, input_data - pWork->dsize_bytes + MAX_REP_LENGTH, input_data_end + 1); phase++; break; @@ -607,7 +518,7 @@ static void WriteCmpData(TCmpStruct * pWork) // Try to find better repetition 1 byte later. // Example: "ARROCKFORT" "AROCKFORT" // When "input_data" points to the second string, FindRep - // returns the occurence of "AR". But there is longer repetition "ROCKFORT", + // returns the occurrence of "AR". But there is longer repetition "ROCKFORT", // beginning 1 byte after. save_rep_length = rep_length; save_distance = pWork->distance; @@ -662,7 +573,7 @@ _00402252:; if(input_data_ended == 0) { input_data -= 0x1000; - memcpy(pWork->work_buff, pWork->work_buff + 0x1000, pWork->dsize_bytes + 0x204); + memmove(pWork->work_buff, pWork->work_buff + 0x1000, pWork->dsize_bytes + MAX_REP_LENGTH); } } @@ -688,13 +599,11 @@ unsigned int PKEXPORT implode( unsigned int *dsize) { TCmpStruct * pWork = (TCmpStruct *)work_buf; - unsigned int nChCode; unsigned int nCount; unsigned int i; int nCount2; // Fill the work buffer information - // Note: The caller must zero the "work_buff" before passing it to implode pWork->read_buf = read_buf; pWork->write_buf = write_buf; pWork->dsize_bytes = *dsize; @@ -727,11 +636,10 @@ unsigned int PKEXPORT implode( switch(*type) { case CMP_BINARY: // We will compress data with binary compression type - for(nChCode = 0, nCount = 0; nCount < 0x100; nCount++) + for(nCount = 0; nCount < 0x100; nCount++) { pWork->nChBits[nCount] = 9; - pWork->nChCodes[nCount] = (unsigned short)nChCode; - nChCode = (nChCode & 0x0000FFFF) + 2; + pWork->nChCodes[nCount] = nCount * 2; } break; @@ -750,14 +658,11 @@ unsigned int PKEXPORT implode( for(i = 0; i < 0x10; i++) { - if(1 << ExLenBits[i]) + for(nCount2 = 0; nCount2 < (1 << ExLenBits[i]); nCount2++) { - for(nCount2 = 0; nCount2 < (1 << ExLenBits[i]); nCount2++) - { - pWork->nChBits[nCount] = (unsigned char)(ExLenBits[i] + LenBits[i] + 1); - pWork->nChCodes[nCount] = (unsigned short)((nCount2 << (LenBits[i] + 1)) | ((LenCode[i] & 0xFFFF00FF) * 2) | 1); - nCount++; - } + pWork->nChBits[nCount] = (unsigned char)(ExLenBits[i] + LenBits[i] + 1); + pWork->nChCodes[nCount] = (unsigned short)((nCount2 << (LenBits[i] + 1)) | ((LenCode[i] & 0xFFFF00FF) * 2) | 1); + nCount++; } } diff --git a/dep/StormLib/src/pklib/pklib.h b/dep/StormLib/src/pklib/pklib.h index f43da153be..95c7b3b72b 100644 --- a/dep/StormLib/src/pklib/pklib.h +++ b/dep/StormLib/src/pklib/pklib.h @@ -5,13 +5,13 @@ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ -/* 31.03.03 1.00 Lad The first version of pkware.h */ +/* 31.03.03 1.00 Lad Created */ /*****************************************************************************/ #ifndef __PKLIB_H__ #define __PKLIB_H__ -#include "../StormPort.h" +#pragma once //----------------------------------------------------------------------------- // Defines @@ -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,20 +101,32 @@ 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]; // 2E34: 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 // Defined as 12596 in pkware headers +//----------------------------------------------------------------------------- +// Tables (in explode.c) + +extern const unsigned char DistBits[0x40]; +extern const unsigned char DistCode[0x40]; +extern const unsigned char ExLenBits[0x10]; +extern const unsigned short LenBase[0x10]; +extern const unsigned char LenBits[0x10]; +extern const unsigned char LenCode[0x10]; +extern const unsigned char ChBitsAsc[0x100]; +extern const unsigned short ChCodeAsc[0x100]; + //----------------------------------------------------------------------------- // Public functions @@ -137,7 +149,7 @@ unsigned int PKEXPORT explode( char *work_buf, void *param); -// The original name "crc32" was changed to "crc32pk" due +// The original name "crc32" was changed to "crc32_pklib" due // to compatibility with zlib unsigned long PKEXPORT crc32_pklib(char *buffer, unsigned int *size, unsigned long *old_crc); diff --git a/dep/StormLib/src/resource.h b/dep/StormLib/src/resource.h new file mode 100644 index 0000000000..8821947c12 --- /dev/null +++ b/dep/StormLib/src/resource.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by DllMain.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/dep/StormLib/src/sparse/sparse.cpp b/dep/StormLib/src/sparse/sparse.cpp index 6df7021fc8..6cf2df28e1 100644 --- a/dep/StormLib/src/sparse/sparse.cpp +++ b/dep/StormLib/src/sparse/sparse.cpp @@ -14,22 +14,24 @@ /* 19.11.03 1.01 Dan Big endian handling */ /* 08.12.03 2.01 Dan High-memory handling (> 0x80000000) */ /*****************************************************************************/ - + #include #include - + #include "sparse.h" //----------------------------------------------------------------------------- // Public functions -void CompressSparse(unsigned char * pbOutBuffer, int * pcbOutBuffer, unsigned char * pbInBuffer, int cbInBuffer) +void CompressSparse(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) { - unsigned char * pbOutBufferEnd = pbOutBuffer + *pcbOutBuffer; - unsigned char * pbInBufferEnd = pbInBuffer + cbInBuffer; - unsigned char * pbLastNonZero = pbInBuffer; - unsigned char * pbOutBuffer0 = pbOutBuffer; - unsigned char * pbInBuffPtr = pbInBuffer; + unsigned char * pbOutBufferEnd = (unsigned char *)pvOutBuffer + *pcbOutBuffer; + unsigned char * pbInBufferEnd = (unsigned char *)pvInBuffer + cbInBuffer; + unsigned char * pbLastNonZero = (unsigned char *)pvInBuffer; + unsigned char * pbOutBuffer0 = (unsigned char *)pvOutBuffer; + unsigned char * pbInBuffPtr = (unsigned char *)pvInBuffer; + unsigned char * pbOutBuffer = (unsigned char *)pvOutBuffer; + unsigned char * pbInBuffer = (unsigned char *)pvInBuffer; size_t NumberOfNonZeros; size_t NumberOfZeros; @@ -86,7 +88,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 +108,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++; @@ -128,20 +130,6 @@ void CompressSparse(unsigned char * pbOutBuffer, int * pcbOutBuffer, unsigned ch pbOutBuffer += NumberOfNonZeros; pbInBuffer += NumberOfNonZeros; } - else - { - // Verify if we still have enough space in output buffer - if((pbOutBuffer + 2) >= pbOutBufferEnd) - return; - - // Put marker that means "1 nonzero byte" - *pbOutBuffer++ = 0x80; - memcpy(pbOutBuffer, pbInBuffer, 1); - - // Adjust pointers - pbOutBuffer++; - pbInBuffer++; - } } // Now flush all zero bytes @@ -183,7 +171,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 +196,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; @@ -234,9 +222,11 @@ void CompressSparse(unsigned char * pbOutBuffer, int * pcbOutBuffer, unsigned ch *pcbOutBuffer = (int)(pbOutBuffer - pbOutBuffer0); } -int DecompressSparse(unsigned char * pbOutBuffer, int * pcbOutBuffer, unsigned char * pbInBuffer, int cbInBuffer) +int DecompressSparse(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) { - unsigned char * pbInBufferEnd = pbInBuffer + cbInBuffer; + unsigned char * pbInBufferEnd = (unsigned char *)pvInBuffer + cbInBuffer; + unsigned char * pbOutBuffer = (unsigned char *)pvOutBuffer; + unsigned char * pbInBuffer = (unsigned char *)pvInBuffer; unsigned int cbChunkSize; unsigned int cbOutBuffer = 0; unsigned int OneByte; @@ -256,7 +246,7 @@ int DecompressSparse(unsigned char * pbOutBuffer, int * pcbOutBuffer, unsigned c cbOutBuffer |= (OneByte << 0x00); // Verify the size of the stream against the output buffer size - if(cbOutBuffer > *pcbOutBuffer) + if(cbOutBuffer > (unsigned int)*pcbOutBuffer) return 0; // Put the output size to the buffer @@ -271,7 +261,12 @@ int DecompressSparse(unsigned char * pbOutBuffer, int * pcbOutBuffer, unsigned c // If highest bit, it means that that normal data follow if(OneByte & 0x80) { + // Check the length of one chunk. Check for overflows cbChunkSize = (OneByte & 0x7F) + 1; + if((pbInBuffer + cbChunkSize) > pbInBufferEnd) + return 0; + + // Copy the chunk. Make sure that the buffer won't overflow cbChunkSize = (cbChunkSize < cbOutBuffer) ? cbChunkSize : cbOutBuffer; memcpy(pbOutBuffer, pbInBuffer, cbChunkSize); pbInBuffer += cbChunkSize; diff --git a/dep/StormLib/src/sparse/sparse.h b/dep/StormLib/src/sparse/sparse.h index 032395f0b9..12f62b526d 100644 --- a/dep/StormLib/src/sparse/sparse.h +++ b/dep/StormLib/src/sparse/sparse.h @@ -11,9 +11,7 @@ #ifndef __SPARSE_H__ #define __SPARSE_H__ -#include "../StormPort.h" - -void CompressSparse(unsigned char * pbOutBuffer, int * pcbOutLength, unsigned char * pbInBuffer, int cbInLength); -int DecompressSparse(unsigned char * pbOutBuffer, int * pcbOutLength, unsigned char * pbInBuffer, int cbInLength); +void CompressSparse(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer); +int DecompressSparse(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer); #endif // __SPARSE_H__ diff --git a/dep/StormLib/src/wdk/sources-cpp.cpp b/dep/StormLib/src/wdk/sources-cpp.cpp new file mode 100644 index 0000000000..a9d7ba3f50 --- /dev/null +++ b/dep/StormLib/src/wdk/sources-cpp.cpp @@ -0,0 +1,27 @@ +// This is a source file for WDK build of StormLib +// It is copied to the root folder during the build process + +#include "src\adpcm\adpcm.cpp" +#include "src\huffman\huff.cpp" +#include "src\sparse\sparse.cpp" + +#include "src\FileStream.cpp" +#include "src\SBaseCommon.cpp" +#include "src\SBaseDumpData.cpp" +#include "src\SBaseFileTable.cpp" +#include "src\SBaseSubTypes.cpp" +#include "src\SCompression.cpp" +#include "src\SFileAddFile.cpp" +#include "src\SFileAttributes.cpp" +#include "src\SFileCompactArchive.cpp" +#include "src\SFileCreateArchive.cpp" +#include "src\SFileExtractFile.cpp" +#include "src\SFileFindFile.cpp" +#include "src\SFileGetFileInfo.cpp" +#include "src\SFileListFile.cpp" +#include "src\SFileOpenArchive.cpp" +#include "src\SFileOpenFileEx.cpp" +#include "src\SFilePatchArchives.cpp" +#include "src\SFileReadFile.cpp" +#include "src\SFileVerify.cpp" +#include "src\SMemUtf8.cpp" diff --git a/dep/StormLib/src/wdk/sources-wdk-bzip2.c b/dep/StormLib/src/wdk/sources-wdk-bzip2.c new file mode 100644 index 0000000000..74cf92a1b5 --- /dev/null +++ b/dep/StormLib/src/wdk/sources-wdk-bzip2.c @@ -0,0 +1,13 @@ +// This is a source file for WDK build of StormLib +// It is copied to the root folder during the build process + +#pragma warning(disable: 4242) // '=' : conversion from 'Int32' to 'UChar', possible loss of data +#pragma warning(disable: 4244) // '=' : conversion from '__int64' to 'Int32', possible loss of data + +#include "src\bzip2\blocksort.c" +#include "src\bzip2\bzlib.c" +#include "src\bzip2\compress.c" +#include "src\bzip2\crctable.c" +#include "src\bzip2\decompress.c" +#include "src\bzip2\huffman.c" +#include "src\bzip2\randtable.c" diff --git a/dep/StormLib/src/wdk/sources-wdk-ltc.c b/dep/StormLib/src/wdk/sources-wdk-ltc.c new file mode 100644 index 0000000000..7a7f90cc95 --- /dev/null +++ b/dep/StormLib/src/wdk/sources-wdk-ltc.c @@ -0,0 +1,4 @@ +// This is a source file for WDK build of StormLib +// It is copied to the root folder during the build process + +#include "src\libtomcrypt\src\math\ltm_desc.c" diff --git a/dep/StormLib/src/wdk/sources-wdk-lzma.c b/dep/StormLib/src/wdk/sources-wdk-lzma.c new file mode 100644 index 0000000000..1fe46ee54f --- /dev/null +++ b/dep/StormLib/src/wdk/sources-wdk-lzma.c @@ -0,0 +1,8 @@ +// This is a source file for WDK build of StormLib +// It is copied to the root folder during the build process + +#include "src\lzma\C\LzFind.c" +#include "src\lzma\C\LzFindMt.c" +#include "src\lzma\C\LzmaDec.c" +#include "src\lzma\C\LzmaEnc.c" +#include "src\lzma\C\Threads.c" diff --git a/dep/StormLib/src/wdk/sources-wdk-misc.c b/dep/StormLib/src/wdk/sources-wdk-misc.c new file mode 100644 index 0000000000..d1c36f0ba4 --- /dev/null +++ b/dep/StormLib/src/wdk/sources-wdk-misc.c @@ -0,0 +1,6 @@ +// This is a source file for WDK build of StormLib +// It is copied to the root folder during the build process + +#include "src\jenkins\lookup3.c" +#include "src\pklib\explode.c" +#include "src\pklib\implode.c" diff --git a/dep/StormLib/src/wdk/sources-wdk-tomcrypt.c b/dep/StormLib/src/wdk/sources-wdk-tomcrypt.c new file mode 100644 index 0000000000..60453d6e46 --- /dev/null +++ b/dep/StormLib/src/wdk/sources-wdk-tomcrypt.c @@ -0,0 +1,82 @@ +// This is a source file for WDK build of StormLib +// It is copied to the root folder during the build process + +#pragma warning(disable:4242) // der_decode_ia5_string.c(84) : warning C4242: '=' : conversion from 'int' to 'unsigned char', possible loss of data + +#include "src\libtomcrypt\src\hashes\hash_memory.c" +#include "src\libtomcrypt\src\hashes\md5.c" +#include "src\libtomcrypt\src\hashes\sha1.c" +#include "src\libtomcrypt\src\math\multi.c" +#include "src\libtomcrypt\src\math\rand_prime.c" +#include "src\libtomcrypt\src\misc\base64_decode.c" +#include "src\libtomcrypt\src\misc\crypt_argchk.c" +#include "src\libtomcrypt\src\misc\crypt_find_hash.c" +#include "src\libtomcrypt\src\misc\crypt_find_prng.c" +#include "src\libtomcrypt\src\misc\crypt_hash_descriptor.c" +#include "src\libtomcrypt\src\misc\crypt_hash_is_valid.c" +#include "src\libtomcrypt\src\misc\crypt_libc.c" +#include "src\libtomcrypt\src\misc\crypt_ltc_mp_descriptor.c" +#include "src\libtomcrypt\src\misc\crypt_prng_descriptor.c" +#include "src\libtomcrypt\src\misc\crypt_prng_is_valid.c" +#include "src\libtomcrypt\src\misc\crypt_register_hash.c" +#include "src\libtomcrypt\src\misc\crypt_register_prng.c" +#include "src\libtomcrypt\src\misc\zeromem.c" +#include "src\libtomcrypt\src\pk\asn1\der_decode_bit_string.c" +#include "src\libtomcrypt\src\pk\asn1\der_decode_boolean.c" +#include "src\libtomcrypt\src\pk\asn1\der_decode_choice.c" +#include "src\libtomcrypt\src\pk\asn1\der_decode_ia5_string.c" +#include "src\libtomcrypt\src\pk\asn1\der_decode_integer.c" +#include "src\libtomcrypt\src\pk\asn1\der_decode_object_identifier.c" +#include "src\libtomcrypt\src\pk\asn1\der_decode_octet_string.c" +#include "src\libtomcrypt\src\pk\asn1\der_decode_printable_string.c" +#include "src\libtomcrypt\src\pk\asn1\der_decode_sequence_ex.c" +#include "src\libtomcrypt\src\pk\asn1\der_decode_sequence_flexi.c" +#include "src\libtomcrypt\src\pk\asn1\der_decode_sequence_multi.c" +#include "src\libtomcrypt\src\pk\asn1\der_decode_short_integer.c" +#include "src\libtomcrypt\src\pk\asn1\der_decode_utctime.c" +#include "src\libtomcrypt\src\pk\asn1\der_decode_utf8_string.c" +#include "src\libtomcrypt\src\pk\asn1\der_encode_bit_string.c" +#include "src\libtomcrypt\src\pk\asn1\der_encode_boolean.c" +#include "src\libtomcrypt\src\pk\asn1\der_encode_ia5_string.c" +#include "src\libtomcrypt\src\pk\asn1\der_encode_integer.c" +#include "src\libtomcrypt\src\pk\asn1\der_encode_object_identifier.c" +#include "src\libtomcrypt\src\pk\asn1\der_encode_octet_string.c" +#include "src\libtomcrypt\src\pk\asn1\der_encode_printable_string.c" +#include "src\libtomcrypt\src\pk\asn1\der_encode_sequence_ex.c" +#include "src\libtomcrypt\src\pk\asn1\der_encode_sequence_multi.c" +#include "src\libtomcrypt\src\pk\asn1\der_encode_set.c" +#include "src\libtomcrypt\src\pk\asn1\der_encode_setof.c" +#include "src\libtomcrypt\src\pk\asn1\der_encode_short_integer.c" +#include "src\libtomcrypt\src\pk\asn1\der_encode_utctime.c" +#include "src\libtomcrypt\src\pk\asn1\der_encode_utf8_string.c" +#include "src\libtomcrypt\src\pk\asn1\der_length_bit_string.c" +#include "src\libtomcrypt\src\pk\asn1\der_length_boolean.c" +#include "src\libtomcrypt\src\pk\asn1\der_length_ia5_string.c" +#include "src\libtomcrypt\src\pk\asn1\der_length_integer.c" +#include "src\libtomcrypt\src\pk\asn1\der_length_object_identifier.c" +#include "src\libtomcrypt\src\pk\asn1\der_length_octet_string.c" +#include "src\libtomcrypt\src\pk\asn1\der_length_printable_string.c" +#include "src\libtomcrypt\src\pk\asn1\der_length_sequence.c" +#include "src\libtomcrypt\src\pk\asn1\der_length_short_integer.c" +#include "src\libtomcrypt\src\pk\asn1\der_length_utctime.c" +#include "src\libtomcrypt\src\pk\asn1\der_length_utf8_string.c" +#include "src\libtomcrypt\src\pk\asn1\der_sequence_free.c" +#include "src\libtomcrypt\src\pk\ecc\ltc_ecc_map.c" +#include "src\libtomcrypt\src\pk\ecc\ltc_ecc_mul2add.c" +#include "src\libtomcrypt\src\pk\ecc\ltc_ecc_mulmod.c" +#include "src\libtomcrypt\src\pk\ecc\ltc_ecc_points.c" +#include "src\libtomcrypt\src\pk\ecc\ltc_ecc_projective_add_point.c" +#include "src\libtomcrypt\src\pk\ecc\ltc_ecc_projective_dbl_point.c" +#include "src\libtomcrypt\src\pk\pkcs1\pkcs_1_mgf1.c" +#include "src\libtomcrypt\src\pk\pkcs1\pkcs_1_oaep_decode.c" +#include "src\libtomcrypt\src\pk\pkcs1\pkcs_1_pss_decode.c" +#include "src\libtomcrypt\src\pk\pkcs1\pkcs_1_pss_encode.c" +#include "src\libtomcrypt\src\pk\pkcs1\pkcs_1_v1_5_decode.c" +#include "src\libtomcrypt\src\pk\pkcs1\pkcs_1_v1_5_encode.c" +#include "src\libtomcrypt\src\pk\rsa\rsa_exptmod.c" +#include "src\libtomcrypt\src\pk\rsa\rsa_free.c" +#include "src\libtomcrypt\src\pk\rsa\rsa_import.c" +#include "src\libtomcrypt\src\pk\rsa\rsa_make_key.c" +#include "src\libtomcrypt\src\pk\rsa\rsa_sign_hash.c" +#include "src\libtomcrypt\src\pk\rsa\rsa_verify_hash.c" +#include "src\libtomcrypt\src\pk\rsa\rsa_verify_simple.c" diff --git a/dep/StormLib/src/wdk/sources-wdk-tommath.c b/dep/StormLib/src/wdk/sources-wdk-tommath.c new file mode 100644 index 0000000000..b394d27513 --- /dev/null +++ b/dep/StormLib/src/wdk/sources-wdk-tommath.c @@ -0,0 +1,123 @@ +// This is a source file for WDK build of StormLib +// It is copied to the root folder during the build process + +#pragma warning(disable:4334) // warning C4334: '<<' : result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?) + +#include "src\libtommath\bn_fast_mp_invmod.c" +#include "src\libtommath\bn_fast_mp_montgomery_reduce.c" +#include "src\libtommath\bn_fast_s_mp_mul_digs.c" +#include "src\libtommath\bn_fast_s_mp_mul_high_digs.c" +#include "src\libtommath\bn_fast_s_mp_sqr.c" +#include "src\libtommath\bn_mp_2expt.c" +#include "src\libtommath\bn_mp_abs.c" +#include "src\libtommath\bn_mp_add.c" +#include "src\libtommath\bn_mp_add_d.c" +#include "src\libtommath\bn_mp_addmod.c" +#include "src\libtommath\bn_mp_and.c" +#include "src\libtommath\bn_mp_clamp.c" +#include "src\libtommath\bn_mp_clear.c" +#include "src\libtommath\bn_mp_clear_multi.c" +#include "src\libtommath\bn_mp_cmp.c" +#include "src\libtommath\bn_mp_cmp_d.c" +#include "src\libtommath\bn_mp_cmp_mag.c" +#include "src\libtommath\bn_mp_cnt_lsb.c" +#include "src\libtommath\bn_mp_copy.c" +#include "src\libtommath\bn_mp_count_bits.c" +#include "src\libtommath\bn_mp_div.c" +#include "src\libtommath\bn_mp_div_2.c" +#include "src\libtommath\bn_mp_div_2d.c" +#include "src\libtommath\bn_mp_div_3.c" +#include "src\libtommath\bn_mp_div_d.c" +#include "src\libtommath\bn_mp_dr_is_modulus.c" +#include "src\libtommath\bn_mp_dr_reduce.c" +#include "src\libtommath\bn_mp_dr_setup.c" +#include "src\libtommath\bn_mp_exch.c" +#include "src\libtommath\bn_mp_expt_d.c" +#include "src\libtommath\bn_mp_exptmod.c" +#include "src\libtommath\bn_mp_exptmod_fast.c" +#include "src\libtommath\bn_mp_exteuclid.c" +#include "src\libtommath\bn_mp_fread.c" +#include "src\libtommath\bn_mp_fwrite.c" +#include "src\libtommath\bn_mp_gcd.c" +#include "src\libtommath\bn_mp_get_int.c" +#include "src\libtommath\bn_mp_grow.c" +#include "src\libtommath\bn_mp_init.c" +#include "src\libtommath\bn_mp_init_copy.c" +#include "src\libtommath\bn_mp_init_multi.c" +#include "src\libtommath\bn_mp_init_set.c" +#include "src\libtommath\bn_mp_init_set_int.c" +#include "src\libtommath\bn_mp_init_size.c" +#include "src\libtommath\bn_mp_invmod.c" +#include "src\libtommath\bn_mp_invmod_slow.c" +#include "src\libtommath\bn_mp_is_square.c" +#include "src\libtommath\bn_mp_jacobi.c" +#include "src\libtommath\bn_mp_karatsuba_mul.c" +#include "src\libtommath\bn_mp_karatsuba_sqr.c" +#include "src\libtommath\bn_mp_lcm.c" +#include "src\libtommath\bn_mp_lshd.c" +#include "src\libtommath\bn_mp_mod.c" +#include "src\libtommath\bn_mp_mod_2d.c" +#include "src\libtommath\bn_mp_mod_d.c" +#include "src\libtommath\bn_mp_montgomery_calc_normalization.c" +#include "src\libtommath\bn_mp_montgomery_reduce.c" +#include "src\libtommath\bn_mp_montgomery_setup.c" +#include "src\libtommath\bn_mp_mul.c" +#include "src\libtommath\bn_mp_mul_2.c" +#include "src\libtommath\bn_mp_mul_2d.c" +#include "src\libtommath\bn_mp_mul_d.c" +#include "src\libtommath\bn_mp_mulmod.c" +#include "src\libtommath\bn_mp_n_root.c" +#include "src\libtommath\bn_mp_neg.c" +#include "src\libtommath\bn_mp_or.c" +#include "src\libtommath\bn_mp_prime_fermat.c" +#include "src\libtommath\bn_mp_prime_is_divisible.c" +#include "src\libtommath\bn_mp_prime_is_prime.c" +#include "src\libtommath\bn_mp_prime_miller_rabin.c" +#include "src\libtommath\bn_mp_prime_next_prime.c" +#include "src\libtommath\bn_mp_prime_rabin_miller_trials.c" +#include "src\libtommath\bn_mp_prime_random_ex.c" +#include "src\libtommath\bn_mp_radix_size.c" +#include "src\libtommath\bn_mp_radix_smap.c" +#include "src\libtommath\bn_mp_rand.c" +#include "src\libtommath\bn_mp_read_radix.c" +#include "src\libtommath\bn_mp_read_signed_bin.c" +#include "src\libtommath\bn_mp_read_unsigned_bin.c" +#include "src\libtommath\bn_mp_reduce.c" +#include "src\libtommath\bn_mp_reduce_2k.c" +#include "src\libtommath\bn_mp_reduce_2k_l.c" +#include "src\libtommath\bn_mp_reduce_2k_setup.c" +#include "src\libtommath\bn_mp_reduce_2k_setup_l.c" +#include "src\libtommath\bn_mp_reduce_is_2k.c" +#include "src\libtommath\bn_mp_reduce_is_2k_l.c" +#include "src\libtommath\bn_mp_reduce_setup.c" +#include "src\libtommath\bn_mp_rshd.c" +#include "src\libtommath\bn_mp_set.c" +#include "src\libtommath\bn_mp_set_int.c" +#include "src\libtommath\bn_mp_shrink.c" +#include "src\libtommath\bn_mp_signed_bin_size.c" +#include "src\libtommath\bn_mp_sqr.c" +#include "src\libtommath\bn_mp_sqrmod.c" +#include "src\libtommath\bn_mp_sqrt.c" +#include "src\libtommath\bn_mp_sub.c" +#include "src\libtommath\bn_mp_sub_d.c" +#include "src\libtommath\bn_mp_submod.c" +#include "src\libtommath\bn_mp_to_signed_bin.c" +#include "src\libtommath\bn_mp_to_signed_bin_n.c" +#include "src\libtommath\bn_mp_to_unsigned_bin.c" +#include "src\libtommath\bn_mp_to_unsigned_bin_n.c" +#include "src\libtommath\bn_mp_toom_mul.c" +#include "src\libtommath\bn_mp_toom_sqr.c" +#include "src\libtommath\bn_mp_toradix.c" +#include "src\libtommath\bn_mp_toradix_n.c" +#include "src\libtommath\bn_mp_unsigned_bin_size.c" +#include "src\libtommath\bn_mp_xor.c" +#include "src\libtommath\bn_mp_zero.c" +#include "src\libtommath\bn_prime_tab.c" +#include "src\libtommath\bn_reverse.c" +#include "src\libtommath\bn_s_mp_add.c" +#include "src\libtommath\bn_s_mp_exptmod.c" +#include "src\libtommath\bn_s_mp_mul_digs.c" +#include "src\libtommath\bn_s_mp_mul_high_digs.c" +#include "src\libtommath\bn_s_mp_sqr.c" +#include "src\libtommath\bn_s_mp_sub.c" +#include "src\libtommath\bncore.c" diff --git a/dep/StormLib/src/wdk/sources-wdk-zlib.c b/dep/StormLib/src/wdk/sources-wdk-zlib.c new file mode 100644 index 0000000000..2455a9a673 --- /dev/null +++ b/dep/StormLib/src/wdk/sources-wdk-zlib.c @@ -0,0 +1,21 @@ +// This is a source file for WDK build of StormLib +// It is copied to the root folder during the build process + +#pragma warning(disable:4242) // deflate.c(1693) : warning C4242: '=' : conversion from 'unsigned int' to 'Bytef', possible loss of data + +#define NO_DUMMY_DECL +#define NO_GZIP +#include "src\zlib\adler32.c" +#undef DO1 +#undef DO8 +#undef MIN +#include "src\zlib\compress.c" +#include "src\zlib\crc32.c" +#include "src\zlib\deflate.c" +#include "src\zlib\trees.c" +#include "src\zlib\zutil.c" + +#undef COPY // Conflicting definition +#include "src\zlib\inflate.c" +#include "src\zlib\inffast.c" +#include "src\zlib\inftrees.c" diff --git a/dep/StormLib/src/zlib/adler32.c b/dep/StormLib/src/zlib/adler32.c new file mode 100644 index 0000000000..65ad6a5adc --- /dev/null +++ b/dep/StormLib/src/zlib/adler32.c @@ -0,0 +1,169 @@ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995-2007 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zutil.h" + +#define local static + +local uLong adler32_combine_(uLong adler1, uLong adler2, z_off64_t len2); + +#define BASE 65521UL /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +/* use NO_DIVIDE if your processor does not do division in hardware */ +#ifdef NO_DIVIDE +# define MOD(a) \ + do { \ + if (a >= (BASE << 16)) a -= (BASE << 16); \ + if (a >= (BASE << 15)) a -= (BASE << 15); \ + if (a >= (BASE << 14)) a -= (BASE << 14); \ + if (a >= (BASE << 13)) a -= (BASE << 13); \ + if (a >= (BASE << 12)) a -= (BASE << 12); \ + if (a >= (BASE << 11)) a -= (BASE << 11); \ + if (a >= (BASE << 10)) a -= (BASE << 10); \ + if (a >= (BASE << 9)) a -= (BASE << 9); \ + if (a >= (BASE << 8)) a -= (BASE << 8); \ + if (a >= (BASE << 7)) a -= (BASE << 7); \ + if (a >= (BASE << 6)) a -= (BASE << 6); \ + if (a >= (BASE << 5)) a -= (BASE << 5); \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +# define MOD4(a) \ + do { \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +#else +# define MOD(a) a %= BASE +# define MOD4(a) a %= BASE +#endif + +/* ========================================================================= */ +uLong ZEXPORT adler32(adler, buf, len) + uLong adler; + const Bytef *buf; + uInt len; +{ + unsigned long sum2; + unsigned n; + + /* split Adler-32 into component sums */ + sum2 = (adler >> 16) & 0xffff; + adler &= 0xffff; + + /* in case user likes doing a byte at a time, keep it fast */ + if (len == 1) { + adler += buf[0]; + if (adler >= BASE) + adler -= BASE; + sum2 += adler; + if (sum2 >= BASE) + sum2 -= BASE; + return adler | (sum2 << 16); + } + + /* initial Adler-32 value (deferred check for len == 1 speed) */ + if (buf == Z_NULL) + return 1L; + + /* in case short lengths are provided, keep it somewhat fast */ + if (len < 16) { + while (len--) { + adler += *buf++; + sum2 += adler; + } + if (adler >= BASE) + adler -= BASE; + MOD4(sum2); /* only added so many BASE's */ + return adler | (sum2 << 16); + } + + /* do length NMAX blocks -- requires just one modulo operation */ + while (len >= NMAX) { + len -= NMAX; + n = NMAX / 16; /* NMAX is divisible by 16 */ + do { + DO16(buf); /* 16 sums unrolled */ + buf += 16; + } while (--n); + MOD(adler); + MOD(sum2); + } + + /* do remaining bytes (less than NMAX, still just one modulo) */ + if (len) { /* avoid modulos if none remaining */ + while (len >= 16) { + len -= 16; + DO16(buf); + buf += 16; + } + while (len--) { + adler += *buf++; + sum2 += adler; + } + MOD(adler); + MOD(sum2); + } + + /* return recombined sums */ + return adler | (sum2 << 16); +} + +/* ========================================================================= */ +local uLong adler32_combine_(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off64_t len2; +{ + unsigned long sum1; + unsigned long sum2; + unsigned rem; + + /* the derivation of this formula is left as an exercise for the reader */ + rem = (unsigned)(len2 % BASE); + sum1 = adler1 & 0xffff; + sum2 = rem * sum1; + MOD(sum2); + sum1 += (adler2 & 0xffff) + BASE - 1; + sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; + if (sum1 >= BASE) sum1 -= BASE; + if (sum1 >= BASE) sum1 -= BASE; + if (sum2 >= (BASE << 1)) sum2 -= (BASE << 1); + if (sum2 >= BASE) sum2 -= BASE; + return sum1 | (sum2 << 16); +} + +/* ========================================================================= */ +uLong ZEXPORT adler32_combine(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off_t len2; +{ + return adler32_combine_(adler1, adler2, len2); +} + +uLong ZEXPORT adler32_combine64(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off64_t len2; +{ + return adler32_combine_(adler1, adler2, len2); +} diff --git a/dep/StormLib/src/zlib/compress.c b/dep/StormLib/src/zlib/compress.c new file mode 100644 index 0000000000..ea4dfbe9d7 --- /dev/null +++ b/dep/StormLib/src/zlib/compress.c @@ -0,0 +1,80 @@ +/* compress.c -- compress a memory buffer + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ +int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; + int level; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; +#ifdef MAXSEG_64K + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; +#endif + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = deflateInit(&stream, level); + if (err != Z_OK) return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = deflateEnd(&stream); + return err; +} + +/* =========================================================================== + */ +int ZEXPORT compress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); +} + +/* =========================================================================== + If the default memLevel or windowBits for deflateInit() is changed, then + this function needs to be updated. + */ +uLong ZEXPORT compressBound (sourceLen) + uLong sourceLen; +{ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + + (sourceLen >> 25) + 13; +} diff --git a/dep/StormLib/src/zlib/compress_zlib.c b/dep/StormLib/src/zlib/compress_zlib.c new file mode 100644 index 0000000000..46e5061583 --- /dev/null +++ b/dep/StormLib/src/zlib/compress_zlib.c @@ -0,0 +1,5 @@ +// Some compilers (e.g. Visual Studio 2012) don't like the name conflict between +// zlib\compress.c and bzip2\compress.c. This file is plain wrapper for compress.c +// in order to create obj file with a different name. + +#include "compress.c" diff --git a/dep/StormLib/src/zlib/crc32.c b/dep/StormLib/src/zlib/crc32.c new file mode 100644 index 0000000000..91be372d22 --- /dev/null +++ b/dep/StormLib/src/zlib/crc32.c @@ -0,0 +1,442 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-2006, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Thanks to Rodney Brown for his contribution of faster + * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing + * tables for updating the shift register in one step with three exclusive-ors + * instead of four steps with four exclusive-ors. This results in about a + * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. + */ + +/* @(#) $Id$ */ + +/* + Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore + protection on the static variables used to control the first-use generation + of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should + first call get_crc_table() to initialize the tables before allowing more than + one thread to use crc32(). + */ + +#ifdef MAKECRCH +# include +# ifndef DYNAMIC_CRC_TABLE +# define DYNAMIC_CRC_TABLE +# endif /* !DYNAMIC_CRC_TABLE */ +#endif /* MAKECRCH */ + +#include "zutil.h" /* for STDC and FAR definitions */ + +#define local static + +/* Find a four-byte integer type for crc32_little() and crc32_big(). */ +#ifndef NOBYFOUR +# ifdef STDC /* need ANSI C limits.h to determine sizes */ +# include +# define BYFOUR +# if (UINT_MAX == 0xffffffffUL) + typedef unsigned int u4; +# else +# if (ULONG_MAX == 0xffffffffUL) + typedef unsigned long u4; +# else +# if (USHRT_MAX == 0xffffffffUL) + typedef unsigned short u4; +# else +# undef BYFOUR /* can't find a four-byte integer type! */ +# endif +# endif +# endif +# endif /* STDC */ +#endif /* !NOBYFOUR */ + +/* Definitions for doing the crc four data bytes at a time. */ +#ifdef BYFOUR +# define REV(w) ((((w)>>24)&0xff)+(((w)>>8)&0xff00)+ \ + (((w)&0xff00)<<8)+(((w)&0xff)<<24)) + local unsigned long crc32_little OF((unsigned long, + const unsigned char FAR *, unsigned)); + local unsigned long crc32_big OF((unsigned long, + const unsigned char FAR *, unsigned)); +# define TBLS 8 +#else +# define TBLS 1 +#endif /* BYFOUR */ + +/* Local functions for crc concatenation */ +local unsigned long gf2_matrix_times OF((unsigned long *mat, + unsigned long vec)); +local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); +local uLong crc32_combine_(uLong crc1, uLong crc2, z_off64_t len2); + + +#ifdef DYNAMIC_CRC_TABLE + +local volatile int crc_table_empty = 1; +local unsigned long FAR crc_table[TBLS][256]; +local void make_crc_table OF((void)); +#ifdef MAKECRCH + local void write_table OF((FILE *, const unsigned long FAR *)); +#endif /* MAKECRCH */ +/* + Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The first table is simply the CRC of all possible eight bit values. This is + all the information needed to generate CRCs on data a byte at a time for all + combinations of CRC register values and incoming bytes. The remaining tables + allow for word-at-a-time CRC calculation for both big-endian and little- + endian machines, where a word is four bytes. +*/ +local void make_crc_table() +{ + unsigned long c; + int n, k; + unsigned long poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static volatile int first = 1; /* flag to limit concurrent making */ + static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* See if another task is already doing this (not thread-safe, but better + than nothing -- significantly reduces duration of vulnerability in + case the advice about DYNAMIC_CRC_TABLE is ignored) */ + if (first) { + first = 0; + + /* make exclusive-or pattern from polynomial (0xedb88320UL) */ + poly = 0UL; + for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++) + poly |= 1UL << (31 - p[n]); + + /* generate a crc for every 8-bit value */ + for (n = 0; n < 256; n++) { + c = (unsigned long)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[0][n] = c; + } + +#ifdef BYFOUR + /* generate crc for each value followed by one, two, and three zeros, + and then the byte reversal of those as well as the first table */ + for (n = 0; n < 256; n++) { + c = crc_table[0][n]; + crc_table[4][n] = REV(c); + for (k = 1; k < 4; k++) { + c = crc_table[0][c & 0xff] ^ (c >> 8); + crc_table[k][n] = c; + crc_table[k + 4][n] = REV(c); + } + } +#endif /* BYFOUR */ + + crc_table_empty = 0; + } + else { /* not first */ + /* wait for the other guy to finish (not efficient, but rare) */ + while (crc_table_empty) + ; + } + +#ifdef MAKECRCH + /* write out CRC tables to crc32.h */ + { + FILE *out; + + out = fopen("crc32.h", "w"); + if (out == NULL) return; + fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); + fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); + fprintf(out, "local const unsigned long FAR "); + fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); + write_table(out, crc_table[0]); +# ifdef BYFOUR + fprintf(out, "#ifdef BYFOUR\n"); + for (k = 1; k < 8; k++) { + fprintf(out, " },\n {\n"); + write_table(out, crc_table[k]); + } + fprintf(out, "#endif\n"); +# endif /* BYFOUR */ + fprintf(out, " }\n};\n"); + fclose(out); + } +#endif /* MAKECRCH */ +} + +#ifdef MAKECRCH +local void write_table(out, table) + FILE *out; + const unsigned long FAR *table; +{ + int n; + + for (n = 0; n < 256; n++) + fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", table[n], + n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); +} +#endif /* MAKECRCH */ + +#else /* !DYNAMIC_CRC_TABLE */ +/* ======================================================================== + * Tables of CRC-32s of all single-byte values, made by make_crc_table(). + */ +#include "crc32.h" +#endif /* DYNAMIC_CRC_TABLE */ + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const unsigned long FAR * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + return (const unsigned long FAR *)crc_table; +} + +/* ========================================================================= */ +#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) +#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 + +/* ========================================================================= */ +unsigned long ZEXPORT crc32(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + uInt len; +{ + if (buf == Z_NULL) return 0UL; + +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + +#ifdef BYFOUR + if (sizeof(void *) == sizeof(ptrdiff_t)) { + u4 endian; + + endian = 1; + if (*((unsigned char *)(&endian))) + return crc32_little(crc, buf, len); + else + return crc32_big(crc, buf, len); + } +#endif /* BYFOUR */ + crc = crc ^ 0xffffffffUL; + while (len >= 8) { + DO8; + len -= 8; + } + if (len) do { + DO1; + } while (--len); + return crc ^ 0xffffffffUL; +} + +#ifdef BYFOUR + +/* ========================================================================= */ +#define DOLIT4 c ^= *buf4++; \ + c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ + crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] +#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 + +/* ========================================================================= */ +local unsigned long crc32_little(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = (u4)crc; + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + while (len >= 32) { + DOLIT32; + len -= 32; + } + while (len >= 4) { + DOLIT4; + len -= 4; + } + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + } while (--len); + c = ~c; + return (unsigned long)c; +} + +/* ========================================================================= */ +#define DOBIG4 c ^= *++buf4; \ + c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ + crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] +#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 + +/* ========================================================================= */ +local unsigned long crc32_big(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = REV((u4)crc); + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + buf4--; + while (len >= 32) { + DOBIG32; + len -= 32; + } + while (len >= 4) { + DOBIG4; + len -= 4; + } + buf4++; + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + } while (--len); + c = ~c; + return (unsigned long)(REV(c)); +} + +#endif /* BYFOUR */ + +#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ + +/* ========================================================================= */ +local unsigned long gf2_matrix_times(mat, vec) + unsigned long *mat; + unsigned long vec; +{ + unsigned long sum; + + sum = 0; + while (vec) { + if (vec & 1) + sum ^= *mat; + vec >>= 1; + mat++; + } + return sum; +} + +/* ========================================================================= */ +local void gf2_matrix_square(square, mat) + unsigned long *square; + unsigned long *mat; +{ + int n; + + for (n = 0; n < GF2_DIM; n++) + square[n] = gf2_matrix_times(mat, mat[n]); +} + +/* ========================================================================= */ +local uLong crc32_combine_(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off64_t len2; +{ + int n; + unsigned long row; + unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ + unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ + + /* degenerate case (also disallow negative lengths) */ + if (len2 <= 0) + return crc1; + + /* put operator for one zero bit in odd */ + odd[0] = 0xedb88320UL; /* CRC-32 polynomial */ + row = 1; + for (n = 1; n < GF2_DIM; n++) { + odd[n] = row; + row <<= 1; + } + + /* put operator for two zero bits in even */ + gf2_matrix_square(even, odd); + + /* put operator for four zero bits in odd */ + gf2_matrix_square(odd, even); + + /* apply len2 zeros to crc1 (first square will put the operator for one + zero byte, eight zero bits, in even) */ + do { + /* apply zeros operator for this bit of len2 */ + gf2_matrix_square(even, odd); + if (len2 & 1) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + if (len2 == 0) + break; + + /* another iteration of the loop with odd and even swapped */ + gf2_matrix_square(odd, even); + if (len2 & 1) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + } while (len2 != 0); + + /* return combined crc */ + crc1 ^= crc2; + return crc1; +} + +/* ========================================================================= */ +uLong ZEXPORT crc32_combine(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off_t len2; +{ + return crc32_combine_(crc1, crc2, len2); +} + +uLong ZEXPORT crc32_combine64(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off64_t len2; +{ + return crc32_combine_(crc1, crc2, len2); +} diff --git a/dep/StormLib/src/zlib/crc32.h b/dep/StormLib/src/zlib/crc32.h new file mode 100644 index 0000000000..8053b6117c --- /dev/null +++ b/dep/StormLib/src/zlib/crc32.h @@ -0,0 +1,441 @@ +/* crc32.h -- tables for rapid CRC calculation + * Generated automatically by crc32.c + */ + +local const unsigned long FAR crc_table[TBLS][256] = +{ + { + 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, + 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, + 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, + 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, + 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, + 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, + 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, + 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, + 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, + 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, + 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, + 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, + 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, + 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, + 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, + 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, + 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, + 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, + 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, + 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, + 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, + 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, + 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, + 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, + 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, + 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, + 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, + 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, + 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, + 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, + 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, + 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, + 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, + 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, + 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, + 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, + 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, + 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, + 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, + 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, + 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, + 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, + 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, + 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, + 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, + 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, + 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, + 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, + 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, + 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, + 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, + 0x2d02ef8dUL +#ifdef BYFOUR + }, + { + 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, + 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, + 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, + 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, + 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, + 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, + 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, + 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, + 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, + 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, + 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, + 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, + 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, + 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, + 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, + 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, + 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, + 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, + 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, + 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, + 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, + 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, + 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, + 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, + 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, + 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, + 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, + 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, + 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, + 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, + 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, + 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, + 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, + 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, + 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, + 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, + 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, + 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, + 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, + 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, + 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, + 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, + 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, + 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, + 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, + 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, + 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, + 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, + 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, + 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, + 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, + 0x9324fd72UL + }, + { + 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, + 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, + 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, + 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, + 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, + 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, + 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, + 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, + 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, + 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, + 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, + 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, + 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, + 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, + 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, + 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, + 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, + 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, + 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, + 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, + 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, + 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, + 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, + 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, + 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, + 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, + 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, + 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, + 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, + 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, + 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, + 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, + 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, + 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, + 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, + 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, + 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, + 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, + 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, + 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, + 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, + 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, + 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, + 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, + 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, + 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, + 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, + 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, + 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, + 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, + 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, + 0xbe9834edUL + }, + { + 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, + 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, + 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, + 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, + 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, + 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, + 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, + 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, + 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, + 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, + 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, + 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, + 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, + 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, + 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, + 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, + 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, + 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, + 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, + 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, + 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, + 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, + 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, + 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, + 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, + 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, + 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, + 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, + 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, + 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, + 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, + 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, + 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, + 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, + 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, + 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, + 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, + 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, + 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, + 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, + 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, + 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, + 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, + 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, + 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, + 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, + 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, + 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, + 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, + 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, + 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, + 0xde0506f1UL + }, + { + 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, + 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, + 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, + 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, + 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, + 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, + 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, + 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, + 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, + 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, + 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, + 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, + 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, + 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, + 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, + 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, + 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, + 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, + 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, + 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, + 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, + 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, + 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, + 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, + 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, + 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, + 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, + 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, + 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, + 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, + 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, + 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, + 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, + 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, + 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, + 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, + 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, + 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, + 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, + 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, + 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, + 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, + 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, + 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, + 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, + 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, + 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, + 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, + 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, + 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, + 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, + 0x8def022dUL + }, + { + 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, + 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, + 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, + 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, + 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, + 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, + 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, + 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, + 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, + 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, + 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, + 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, + 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, + 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, + 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, + 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, + 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, + 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, + 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, + 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, + 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, + 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, + 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, + 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, + 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, + 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, + 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, + 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, + 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, + 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, + 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, + 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, + 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, + 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, + 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, + 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, + 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, + 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, + 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, + 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, + 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, + 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, + 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, + 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, + 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, + 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, + 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, + 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, + 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, + 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, + 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, + 0x72fd2493UL + }, + { + 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, + 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, + 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, + 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, + 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, + 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, + 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, + 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, + 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, + 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, + 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, + 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, + 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, + 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, + 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, + 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, + 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, + 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, + 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, + 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, + 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, + 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, + 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, + 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, + 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, + 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, + 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, + 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, + 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, + 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, + 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, + 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, + 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, + 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, + 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, + 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, + 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, + 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, + 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, + 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, + 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, + 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, + 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, + 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, + 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, + 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, + 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, + 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, + 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, + 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, + 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, + 0xed3498beUL + }, + { + 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, + 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, + 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, + 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, + 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, + 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, + 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, + 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, + 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, + 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, + 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, + 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, + 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, + 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, + 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, + 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, + 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, + 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, + 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, + 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, + 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, + 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, + 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, + 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, + 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, + 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, + 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, + 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, + 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, + 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, + 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, + 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, + 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, + 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, + 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, + 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, + 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, + 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, + 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, + 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, + 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, + 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, + 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, + 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, + 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, + 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, + 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, + 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, + 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, + 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, + 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, + 0xf10605deUL +#endif + } +}; diff --git a/dep/StormLib/src/zlib/deflate.c b/dep/StormLib/src/zlib/deflate.c new file mode 100644 index 0000000000..5c4022f3d4 --- /dev/null +++ b/dep/StormLib/src/zlib/deflate.c @@ -0,0 +1,1834 @@ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in http://www.ietf.org/rfc/rfc1951.txt + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* @(#) $Id$ */ + +#include "deflate.h" + +const char deflate_copyright[] = + " deflate 1.2.5 Copyright 1995-2010 Jean-loup Gailly and Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* =========================================================================== + * Function prototypes. + */ +typedef enum { + need_more, /* block not completed, need more input or more output */ + block_done, /* block flush performed */ + finish_started, /* finish started, need only more output at next deflate */ + finish_done /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local void fill_window OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast OF((deflate_state *s, int flush)); +#ifndef FASTEST +local block_state deflate_slow OF((deflate_state *s, int flush)); +#endif +local block_state deflate_rle OF((deflate_state *s, int flush)); +local block_state deflate_huff OF((deflate_state *s, int flush)); +local void lm_init OF((deflate_state *s)); +local void putShortMSB OF((deflate_state *s, uInt b)); +local void flush_pending OF((z_streamp strm)); +local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ + uInt longest_match OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match OF((deflate_state *s, IPos cur_match)); +#endif + +#ifdef DEBUG +local void check_match OF((deflate_state *s, IPos start, IPos match, + int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; + compress_func func; +} config; + +#ifdef FASTEST +local const config configuration_table[2] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ +#else +local const config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8, deflate_fast}, +/* 3 */ {4, 6, 32, 32, deflate_fast}, + +/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32, deflate_slow}, +/* 6 */ {8, 16, 128, 128, deflate_slow}, +/* 7 */ {8, 32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ +#endif + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +#ifndef NO_DUMMY_DECL +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ +#endif + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) + + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * If this file is compiled with -DFASTEST, the compression level is forced + * to 1, and no hash chains are maintained. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of str are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#ifdef FASTEST +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#else +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#endif + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* ========================================================================= */ +int ZEXPORT deflateInit_(strm, level, version, stream_size) + z_streamp strm; + int level; + const char *version; + int stream_size; +{ + return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, version, stream_size); + /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + version, stream_size) + z_streamp strm; + int level; + int method; + int windowBits; + int memLevel; + int strategy; + const char *version; + int stream_size; +{ + deflate_state *s; + int wrap = 1; + static const char my_version[] = ZLIB_VERSION; + + ushf *overlay; + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 24 bits. + */ + + if (version == Z_NULL || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return Z_VERSION_ERROR; + } + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + + if (windowBits < 0) { /* suppress zlib wrapper */ + wrap = 0; + windowBits = -windowBits; + } +#ifdef GZIP + else if (windowBits > 15) { + wrap = 2; /* write gzip wrapper instead */ + windowBits -= 16; + } +#endif + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ + s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); + if (s == Z_NULL) return Z_MEM_ERROR; + strm->state = (struct internal_state FAR *)s; + s->strm = strm; + + s->wrap = wrap; + s->gzhead = Z_NULL; + s->w_bits = windowBits; + s->w_size = 1 << s->w_bits; + s->w_mask = s->w_size - 1; + + s->hash_bits = memLevel + 7; + s->hash_size = 1 << s->hash_bits; + s->hash_mask = s->hash_size - 1; + s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + + s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->high_water = 0; /* nothing written to s->window yet */ + + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + s->pending_buf = (uchf *) overlay; + s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); + + if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || + s->pending_buf == Z_NULL) { + s->status = FINISH_STATE; + strm->msg = (char*)ERR_MSG(Z_MEM_ERROR); + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +/* ========================================================================= */ +int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) + z_streamp strm; + const Bytef *dictionary; + uInt dictLength; +{ + deflate_state *s; + uInt length = dictLength; + uInt n; + IPos hash_head = 0; + + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL || + strm->state->wrap == 2 || + (strm->state->wrap == 1 && strm->state->status != INIT_STATE)) + return Z_STREAM_ERROR; + + s = strm->state; + if (s->wrap) + strm->adler = adler32(strm->adler, dictionary, dictLength); + + if (length < MIN_MATCH) return Z_OK; + if (length > s->w_size) { + length = s->w_size; + dictionary += dictLength - length; /* use the tail of the dictionary */ + } + zmemcpy(s->window, dictionary, length); + s->strstart = length; + s->block_start = (long)length; + + /* Insert all strings in the hash table (except for the last two bytes). + * s->lookahead stays null, so s->ins_h will be recomputed at the next + * call of fill_window. + */ + s->ins_h = s->window[0]; + UPDATE_HASH(s, s->ins_h, s->window[1]); + for (n = 0; n <= length - MIN_MATCH; n++) { + INSERT_STRING(s, n, hash_head); + } + if (hash_head) hash_head = 0; /* to make compiler happy */ + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateReset (strm) + z_streamp strm; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { + return Z_STREAM_ERROR; + } + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + if (s->wrap < 0) { + s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ + } + s->status = s->wrap ? INIT_STATE : BUSY_STATE; + strm->adler = +#ifdef GZIP + s->wrap == 2 ? crc32(0L, Z_NULL, 0) : +#endif + adler32(0L, Z_NULL, 0); + s->last_flush = Z_NO_FLUSH; + + _tr_init(s); + lm_init(s); + + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateSetHeader (strm, head) + z_streamp strm; + gz_headerp head; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (strm->state->wrap != 2) return Z_STREAM_ERROR; + strm->state->gzhead = head; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePrime (strm, bits, value) + z_streamp strm; + int bits; + int value; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + strm->state->bi_valid = bits; + strm->state->bi_buf = (ush)(value & ((1 << bits) - 1)); + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateParams(strm, level, strategy) + z_streamp strm; + int level; + int strategy; +{ + deflate_state *s; + compress_func func; + int err = Z_OK; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + func = configuration_table[s->level].func; + + if ((strategy != s->strategy || func != configuration_table[level].func) && + strm->total_in != 0) { + /* Flush the last buffer: */ + err = deflate(strm, Z_BLOCK); + } + if (s->level != level) { + s->level = level; + s->max_lazy_match = configuration_table[level].max_lazy; + s->good_match = configuration_table[level].good_length; + s->nice_match = configuration_table[level].nice_length; + s->max_chain_length = configuration_table[level].max_chain; + } + s->strategy = strategy; + return err; +} + +/* ========================================================================= */ +int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) + z_streamp strm; + int good_length; + int max_lazy; + int nice_length; + int max_chain; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + s->good_match = good_length; + s->max_lazy_match = max_lazy; + s->nice_match = nice_length; + s->max_chain_length = max_chain; + return Z_OK; +} + +/* ========================================================================= + * For the default windowBits of 15 and memLevel of 8, this function returns + * a close to exact, as well as small, upper bound on the compressed size. + * They are coded as constants here for a reason--if the #define's are + * changed, then this function needs to be changed as well. The return + * value for 15 and 8 only works for those exact settings. + * + * For any setting other than those defaults for windowBits and memLevel, + * the value returned is a conservative worst case for the maximum expansion + * resulting from using fixed blocks instead of stored blocks, which deflate + * can emit on compressed data for some combinations of the parameters. + * + * This function could be more sophisticated to provide closer upper bounds for + * every combination of windowBits and memLevel. But even the conservative + * upper bound of about 14% expansion does not seem onerous for output buffer + * allocation. + */ +uLong ZEXPORT deflateBound(strm, sourceLen) + z_streamp strm; + uLong sourceLen; +{ + deflate_state *s; + uLong complen, wraplen; + Bytef *str; + + /* conservative upper bound for compressed data */ + complen = sourceLen + + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5; + + /* if can't get parameters, return conservative bound plus zlib wrapper */ + if (strm == Z_NULL || strm->state == Z_NULL) + return complen + 6; + + /* compute wrapper length */ + s = strm->state; + switch (s->wrap) { + case 0: /* raw deflate */ + wraplen = 0; + break; + case 1: /* zlib wrapper */ + wraplen = 6 + (s->strstart ? 4 : 0); + break; + case 2: /* gzip wrapper */ + wraplen = 18; + if (s->gzhead != Z_NULL) { /* user-supplied gzip header */ + if (s->gzhead->extra != Z_NULL) + wraplen += 2 + s->gzhead->extra_len; + str = s->gzhead->name; + if (str != Z_NULL) + do { + wraplen++; + } while (*str++); + str = s->gzhead->comment; + if (str != Z_NULL) + do { + wraplen++; + } while (*str++); + if (s->gzhead->hcrc) + wraplen += 2; + } + break; + default: /* for compiler happiness */ + wraplen = 6; + } + + /* if not default parameters, return conservative bound */ + if (s->w_bits != 15 || s->hash_bits != 8 + 7) + return complen + wraplen; + + /* default settings: return tight bound for that case */ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + + (sourceLen >> 25) + 13 - 6 + wraplen; +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (s, b) + deflate_state *s; + uInt b; +{ + put_byte(s, (Byte)(b >> 8)); + put_byte(s, (Byte)(b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->next_out buffer and copying into it. + * (See also read_buf()). + */ +local void flush_pending(strm) + z_streamp strm; +{ + unsigned len = strm->state->pending; + + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + zmemcpy(strm->next_out, strm->state->pending_out, len); + strm->next_out += len; + strm->state->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + strm->state->pending -= len; + if (strm->state->pending == 0) { + strm->state->pending_out = strm->state->pending_buf; + } +} + +/* ========================================================================= */ +int ZEXPORT deflate (strm, flush) + z_streamp strm; + int flush; +{ + int old_flush; /* value of flush param for previous deflate call */ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + flush > Z_BLOCK || flush < 0) { + return Z_STREAM_ERROR; + } + s = strm->state; + + if (strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0) || + (s->status == FINISH_STATE && flush != Z_FINISH)) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + s->strm = strm; /* just in case */ + old_flush = s->last_flush; + s->last_flush = flush; + + /* Write the header */ + if (s->status == INIT_STATE) { +#ifdef GZIP + if (s->wrap == 2) { + strm->adler = crc32(0L, Z_NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (s->gzhead == Z_NULL) { + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, OS_CODE); + s->status = BUSY_STATE; + } + else { + put_byte(s, (s->gzhead->text ? 1 : 0) + + (s->gzhead->hcrc ? 2 : 0) + + (s->gzhead->extra == Z_NULL ? 0 : 4) + + (s->gzhead->name == Z_NULL ? 0 : 8) + + (s->gzhead->comment == Z_NULL ? 0 : 16) + ); + put_byte(s, (Byte)(s->gzhead->time & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, s->gzhead->os & 0xff); + if (s->gzhead->extra != Z_NULL) { + put_byte(s, s->gzhead->extra_len & 0xff); + put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); + } + if (s->gzhead->hcrc) + strm->adler = crc32(strm->adler, s->pending_buf, + s->pending); + s->gzindex = 0; + s->status = EXTRA_STATE; + } + } + else +#endif + { + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags; + + if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) + level_flags = 0; + else if (s->level < 6) + level_flags = 1; + else if (s->level == 6) + level_flags = 2; + else + level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + s->status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = adler32(0L, Z_NULL, 0); + } + } +#ifdef GZIP + if (s->status == EXTRA_STATE) { + if (s->gzhead->extra != Z_NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + + while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) + break; + } + put_byte(s, s->gzhead->extra[s->gzindex]); + s->gzindex++; + } + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (s->gzindex == s->gzhead->extra_len) { + s->gzindex = 0; + s->status = NAME_STATE; + } + } + else + s->status = NAME_STATE; + } + if (s->status == NAME_STATE) { + if (s->gzhead->name != Z_NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->name[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) { + s->gzindex = 0; + s->status = COMMENT_STATE; + } + } + else + s->status = COMMENT_STATE; + } + if (s->status == COMMENT_STATE) { + if (s->gzhead->comment != Z_NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->comment[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) + s->status = HCRC_STATE; + } + else + s->status = HCRC_STATE; + } + if (s->status == HCRC_STATE) { + if (s->gzhead->hcrc) { + if (s->pending + 2 > s->pending_buf_size) + flush_pending(strm); + if (s->pending + 2 <= s->pending_buf_size) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + strm->adler = crc32(0L, Z_NULL, 0); + s->status = BUSY_STATE; + } + } + else + s->status = BUSY_STATE; + } +#endif + + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm->avail_in == 0 && flush <= old_flush && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || s->lookahead != 0 || + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + + bstate = s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) : + (s->strategy == Z_RLE ? deflate_rle(s, flush) : + (*(configuration_table[s->level].func))(s, flush)); + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; + } + if (bstate == need_more || bstate == finish_started) { + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == block_done) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(s); + } else if (flush != Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */ + _tr_stored_block(s, (char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Z_FULL_FLUSH) { + CLEAR_HASH(s); /* forget history */ + if (s->lookahead == 0) { + s->strstart = 0; + s->block_start = 0L; + } + } + } + flush_pending(strm); + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + Assert(strm->avail_out > 0, "bug2"); + + if (flush != Z_FINISH) return Z_OK; + if (s->wrap <= 0) return Z_STREAM_END; + + /* Write the trailer */ +#ifdef GZIP + if (s->wrap == 2) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); + put_byte(s, (Byte)(strm->total_in & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); + } + else +#endif + { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ + return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int ZEXPORT deflateEnd (strm) + z_streamp strm; +{ + int status; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + + status = strm->state->status; + if (status != INIT_STATE && + status != EXTRA_STATE && + status != NAME_STATE && + status != COMMENT_STATE && + status != HCRC_STATE && + status != BUSY_STATE && + status != FINISH_STATE) { + return Z_STREAM_ERROR; + } + + /* Deallocate in reverse order of allocations: */ + TRY_FREE(strm, strm->state->pending_buf); + TRY_FREE(strm, strm->state->head); + TRY_FREE(strm, strm->state->prev); + TRY_FREE(strm, strm->state->window); + + ZFREE(strm, strm->state); + strm->state = Z_NULL; + + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + * To simplify the source, this is not supported for 16-bit MSDOS (which + * doesn't have enough memory anyway to duplicate compression states). + */ +int ZEXPORT deflateCopy (dest, source) + z_streamp dest; + z_streamp source; +{ +#ifdef MAXSEG_64K + return Z_STREAM_ERROR; +#else + deflate_state *ds; + deflate_state *ss; + ushf *overlay; + + + if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { + return Z_STREAM_ERROR; + } + + ss = source->state; + + zmemcpy(dest, source, sizeof(z_stream)); + + ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); + if (ds == Z_NULL) return Z_MEM_ERROR; + dest->state = (struct internal_state FAR *) ds; + zmemcpy(ds, ss, sizeof(deflate_state)); + ds->strm = dest; + + ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); + ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); + ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); + overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); + ds->pending_buf = (uchf *) overlay; + + if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || + ds->pending_buf == Z_NULL) { + deflateEnd (dest); + return Z_MEM_ERROR; + } + /* following zmemcpy do not work for 16-bit MSDOS */ + zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); + zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + + ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); + ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); + ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + + ds->l_desc.dyn_tree = ds->dyn_ltree; + ds->d_desc.dyn_tree = ds->dyn_dtree; + ds->bl_desc.dyn_tree = ds->bl_tree; + + return Z_OK; +#endif /* MAXSEG_64K */ +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local int read_buf(strm, buf, size) + z_streamp strm; + Bytef *buf; + unsigned size; +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + if (strm->state->wrap == 1) { + strm->adler = adler32(strm->adler, strm->next_in, len); + } +#ifdef GZIP + else if (strm->state->wrap == 2) { + strm->adler = crc32(strm->adler, strm->next_in, len); + } +#endif + zmemcpy(buf, strm->next_in, len); + strm->next_in += len; + strm->total_in += len; + + return (int)len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (s) + deflate_state *s; +{ + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +#ifndef FASTEST +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif +#endif +} + +#ifndef FASTEST +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + int best_len = s->prev_length; /* best match length so far */ + int nice_match = s->nice_match; /* stop if match long enough */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + Posf *prev = s->prev; + uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ushf*)scan; + register ush scan_end = *(ushf*)(scan+best_len-1); +#else + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2. Note that the checks below + * for insufficient lookahead only occur occasionally for performance + * reasons. Therefore uninitialized memory will be accessed, and + * conditional jumps will be made that depend on those values. + * However the length of the match is limited to the lookahead, so + * the output of deflate is not affected by the uninitialized values. + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ushf*)(match+best_len-1) != scan_end || + *(ushf*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + Assert(scan[2] == match[2], "scan[2]?"); + scan++, match++; + do { + } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ushf*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if ((uInt)best_len <= s->lookahead) return (uInt)best_len; + return s->lookahead; +} +#endif /* ASMV */ + +#else /* FASTEST */ + +/* --------------------------------------------------------------------------- + * Optimized version for FASTEST only + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + Assert(cur_match < s->strstart, "no future"); + + match = s->window + cur_match; + + /* Return failure if the match length is less than 2: + */ + if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match += 2; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + + if (len < MIN_MATCH) return MIN_MATCH - 1; + + s->match_start = cur_match; + return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; +} + +#endif /* FASTEST */ + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(s, start, match, length) + deflate_state *s; + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (zmemcmp(s->window + match, + s->window + start, length) != EQUAL) { + fprintf(stderr, " start %u, match %u, length %d\n", + start, match, length); + do { + fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); + } while (--length != 0); + z_error("invalid match"); + } + if (z_verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(s->window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(s, start, match, length) +#endif /* DEBUG */ + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(s) + deflate_state *s; +{ + register unsigned n, m; + register Posf *p; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (sizeof(int) <= 2) { + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if + * strstart == 0 && lookahead == 1 (input done a byte at time) + */ + more--; + } + } + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s->strstart >= wsize+MAX_DIST(s)) { + + zmemcpy(s->window, s->window+wsize, (unsigned)wsize); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + } while (--n); + + n = wsize; +#ifndef FASTEST + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); +#endif + more += wsize; + } + if (s->strm->avail_in == 0) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead >= MIN_MATCH) { + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); + + /* If the WIN_INIT bytes after the end of the current data have never been + * written, then zero those bytes in order to avoid memory check reports of + * the use of uninitialized (or uninitialised as Julian writes) bytes by + * the longest match routines. Update the high water mark for the next + * time through here. WIN_INIT is set to MAX_MATCH since the longest match + * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. + */ + if (s->high_water < s->window_size) { + ulg curr = s->strstart + (ulg)(s->lookahead); + ulg init; + + if (s->high_water < curr) { + /* Previous high water mark below current data -- zero WIN_INIT + * bytes or up to end of window, whichever is less. + */ + init = s->window_size - curr; + if (init > WIN_INIT) + init = WIN_INIT; + zmemzero(s->window + curr, (unsigned)init); + s->high_water = curr + init; + } + else if (s->high_water < (ulg)curr + WIN_INIT) { + /* High water mark at or above current data, but below current data + * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up + * to end of window, whichever is less. + */ + init = (ulg)curr + WIN_INIT - s->high_water; + if (init > s->window_size - s->high_water) + init = s->window_size - s->high_water; + zmemzero(s->window + s->high_water, (unsigned)init); + s->high_water += init; + } + } +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, last) { \ + _tr_flush_block(s, (s->block_start >= 0L ? \ + (charf *)&s->window[(unsigned)s->block_start] : \ + (charf *)Z_NULL), \ + (ulg)((long)s->strstart - s->block_start), \ + (last)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ + Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, last) { \ + FLUSH_BLOCK_ONLY(s, last); \ + if (s->strm->avail_out == 0) return (last) ? finish_started : need_more; \ +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +local block_state deflate_stored(s, flush) + deflate_state *s; + int flush; +{ + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + ulg max_block_size = 0xffff; + ulg max_start; + + if (max_block_size > s->pending_buf_size - 5) { + max_block_size = s->pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s->lookahead <= 1) { + + Assert(s->strstart < s->w_size+MAX_DIST(s) || + s->block_start >= (long)s->w_size, "slide too late"); + + fill_window(s); + if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + + if (s->lookahead == 0) break; /* flush the current block */ + } + Assert(s->block_start >= 0L, "block gone"); + + s->strstart += s->lookahead; + s->lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + max_start = s->block_start + max_block_size; + if (s->strstart == 0 || (ulg)s->strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s->lookahead = (uInt)(s->strstart - max_start); + s->strstart = (uInt)max_start; + FLUSH_BLOCK(s, 0); + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { + FLUSH_BLOCK(s, 0); + } + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + hash_head = NIL; + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + s->match_length = longest_match (s, hash_head); + /* longest_match() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + _tr_tally_dist(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ +#ifndef FASTEST + if (s->match_length <= s->max_insert_length && + s->lookahead >= MIN_MATCH) { + s->match_length--; /* string at strstart already in table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s->match_length != 0); + s->strstart++; + } else +#endif + { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +#ifndef FASTEST +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + hash_head = NIL; + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + s->match_length = longest_match (s, hash_head); + /* longest_match() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED +#if TOO_FAR <= 32767 + || (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR) +#endif + )) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + _tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + if (++s->strstart <= max_insert) { + INSERT_STRING(s, s->strstart, hash_head); + } + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + if (bflush) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return need_more; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s->match_available) { + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + s->match_available = 0; + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif /* FASTEST */ + +/* =========================================================================== + * For Z_RLE, simply look for runs of bytes, generate matches only of distance + * one. Do not maintain a hash table. (It will be regenerated if this run of + * deflate switches away from Z_RLE.) + */ +local block_state deflate_rle(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + uInt prev; /* byte at distance one to match */ + Bytef *scan, *strend; /* scan goes up to strend for length of run */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the longest encodable run. + */ + if (s->lookahead < MAX_MATCH) { + fill_window(s); + if (s->lookahead < MAX_MATCH && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* See how many times the previous byte repeats */ + s->match_length = 0; + if (s->lookahead >= MIN_MATCH && s->strstart > 0) { + scan = s->window + s->strstart - 1; + prev = *scan; + if (prev == *++scan && prev == *++scan && prev == *++scan) { + strend = s->window + s->strstart + MAX_MATCH; + do { + } while (prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + scan < strend); + s->match_length = MAX_MATCH - (int)(strend - scan); + if (s->match_length > s->lookahead) + s->match_length = s->lookahead; + } + } + + /* Emit match if have run of MIN_MATCH or longer, else emit literal */ + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->strstart - 1, s->match_length); + + _tr_tally_dist(s, 1, s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + s->strstart += s->match_length; + s->match_length = 0; + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. + * (It will be regenerated if this run of deflate switches away from Huffman.) + */ +local block_state deflate_huff(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we have a literal to write. */ + if (s->lookahead == 0) { + fill_window(s); + if (s->lookahead == 0) { + if (flush == Z_NO_FLUSH) + return need_more; + break; /* flush the current block */ + } + } + + /* Output a literal byte */ + s->match_length = 0; + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} diff --git a/dep/StormLib/src/zlib/deflate.h b/dep/StormLib/src/zlib/deflate.h new file mode 100644 index 0000000000..cbf0d1ea5d --- /dev/null +++ b/dep/StormLib/src/zlib/deflate.h @@ -0,0 +1,342 @@ +/* deflate.h -- internal compression state + * Copyright (C) 1995-2010 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef DEFLATE_H +#define DEFLATE_H + +#include "zutil.h" + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer creation by deflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip encoding + should be left enabled. */ +#ifndef NO_GZIP +# define GZIP +#endif + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define INIT_STATE 42 +#define EXTRA_STATE 69 +#define NAME_STATE 73 +#define COMMENT_STATE 91 +#define HCRC_STATE 103 +#define BUSY_STATE 113 +#define FINISH_STATE 666 +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + uInt pending; /* nb of bytes in the pending buffer */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + gz_headerp gzhead; /* gzip header information to write */ + uInt gzindex; /* where in extra, name, or comment */ + Byte method; /* STORED (for zip only) or DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to supress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + uInt matches; /* number of string matches in current block */ + int last_eob_len; /* bit length of EOB code for last block */ + +#ifdef DEBUG + ulg compressed_len; /* total bit length of compressed file mod 2^32 */ + ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + + ulg high_water; + /* High water mark offset in window for initialized bytes -- bytes above + * this are set to zero in order to avoid memory check warnings when + * longest match routines access bytes past the input. This is then + * updated to the new high water mark. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + +#define WIN_INIT MAX_MATCH +/* Number of bytes after end of data in window to initialize in order to avoid + memory checker errors from longest match routines */ + + /* in trees.c */ +void ZLIB_INTERNAL _tr_init OF((deflate_state *s)); +int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf, + ulg stored_len, int last)); +void ZLIB_INTERNAL _tr_align OF((deflate_state *s)); +void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf, + ulg stored_len, int last)); + +#define d_code(dist) \ + ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch ZLIB_INTERNAL _length_code[]; + extern uch ZLIB_INTERNAL _dist_code[]; +#else + extern const uch ZLIB_INTERNAL _length_code[]; + extern const uch ZLIB_INTERNAL _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->last_lit] = 0; \ + s->l_buf[s->last_lit++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (length); \ + ush dist = (distance); \ + s->d_buf[s->last_lit] = dist; \ + s->l_buf[s->last_lit++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ + flush = _tr_tally(s, distance, length) +#endif + +#endif /* DEFLATE_H */ diff --git a/dep/StormLib/src/zlib/gzguts.h b/dep/StormLib/src/zlib/gzguts.h new file mode 100644 index 0000000000..990a4d2514 --- /dev/null +++ b/dep/StormLib/src/zlib/gzguts.h @@ -0,0 +1,218 @@ +/* gzguts.h -- zlib internal header definitions for gz* operations + * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#ifdef _LARGEFILE64_SOURCE +# ifndef _LARGEFILE_SOURCE +# define _LARGEFILE_SOURCE 1 +# endif +# ifdef _FILE_OFFSET_BITS +# undef _FILE_OFFSET_BITS +# endif +#endif + +#ifdef HAVE_HIDDEN +# define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) +#else +# define ZLIB_INTERNAL +#endif + +#include +#include "zlib.h" +#ifdef STDC +# include +# include +# include +#endif + +#ifndef _POSIX_SOURCE +# define _POSIX_SOURCE +#endif +#include + +#ifdef _WIN32 +# include +#endif + +#if defined(__TURBOC__) || defined(_MSC_VER) || defined(_WIN32) +# include +#endif + +#if defined(_WIN32) || defined(__CYGWIN__) +# define WIDECHAR +#endif + +#ifdef WINAPI_FAMILY +# define open _open +# define read _read +# define write _write +# define close _close +#endif + +#ifdef NO_DEFLATE /* for compatibility with old definition */ +# define NO_GZCOMPRESS +#endif + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif + +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif + +#if defined(MSDOS) && defined(__BORLANDC__) && (BORLANDC > 0x410) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif + +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS +/* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 +/* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 ) +# define vsnprintf _vsnprintf +# endif +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +# ifdef VMS +# define NO_vsnprintf +# endif +# ifdef __OS400__ +# define NO_vsnprintf +# endif +# ifdef __MVS__ +# define NO_vsnprintf +# endif +#endif + +/* unlike snprintf (which is required in C99), _snprintf does not guarantee + null termination of the result -- however this is only used in gzlib.c where + the result is assured to fit in the space provided */ +#if defined(_MSC_VER) && _MSC_VER < 1900 +# define snprintf _snprintf +#endif + +#ifndef local +# define local static +#endif +/* since "static" is used to mean two completely different things in C, we + define "local" for the non-static meaning of "static", for readability + (compile with -Dlocal if your debugger can't find static symbols) */ + +/* gz* functions always use library allocation functions */ +#ifndef STDC + extern voidp malloc OF((uInt size)); + extern void free OF((voidpf ptr)); +#endif + +/* get errno and strerror definition */ +#if defined UNDER_CE +# include +# define zstrerror() gz_strwinerror((DWORD)GetLastError()) +#else +# ifndef NO_STRERROR +# include +# define zstrerror() strerror(errno) +# else +# define zstrerror() "stdio error (consult errno)" +# endif +#endif + +/* provide prototypes for these when building zlib without LFS */ +#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); +#endif + +/* default memLevel */ +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif + +/* default i/o buffer size -- double this for output when reading (this and + twice this must be able to fit in an unsigned type) */ +#define GZBUFSIZE 8192 + +/* gzip modes, also provide a little integrity check on the passed structure */ +#define GZ_NONE 0 +#define GZ_READ 7247 +#define GZ_WRITE 31153 +#define GZ_APPEND 1 /* mode set to GZ_WRITE after the file is opened */ + +/* values for gz_state how */ +#define LOOK 0 /* look for a gzip header */ +#define COPY 1 /* copy input directly */ +#define GZIP 2 /* decompress a gzip stream */ + +/* internal gzip file state data structure */ +typedef struct { + /* exposed contents for gzgetc() macro */ + struct gzFile_s x; /* "x" for exposed */ + /* x.have: number of bytes available at x.next */ + /* x.next: next output data to deliver or write */ + /* x.pos: current position in uncompressed data */ + /* used for both reading and writing */ + int mode; /* see gzip modes above */ + int fd; /* file descriptor */ + char *path; /* path or fd for error messages */ + unsigned size; /* buffer size, zero if not allocated yet */ + unsigned want; /* requested buffer size, default is GZBUFSIZE */ + unsigned char *in; /* input buffer (double-sized when writing) */ + unsigned char *out; /* output buffer (double-sized when reading) */ + int direct; /* 0 if processing gzip, 1 if transparent */ + /* just for reading */ + int how; /* 0: get header, 1: copy, 2: decompress */ + z_off64_t start; /* where the gzip data started, for rewinding */ + int eof; /* true if end of input file reached */ + int past; /* true if read requested past end */ + /* just for writing */ + int level; /* compression level */ + int strategy; /* compression strategy */ + /* seek request */ + z_off64_t skip; /* amount to skip (already rewound if backwards) */ + int seek; /* true if seek request pending */ + /* error information */ + int err; /* error code */ + char *msg; /* error message */ + /* zlib inflate or deflate stream */ + z_stream strm; /* stream structure in-place (not a pointer) */ +} gz_state; +typedef gz_state FAR *gz_statep; + +/* shared functions */ +void ZLIB_INTERNAL gz_error OF((gz_statep, int, const char *)); +#if defined UNDER_CE +char ZLIB_INTERNAL *gz_strwinerror OF((DWORD error)); +#endif + +/* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t + value -- needed when comparing unsigned to z_off64_t, which is signed + (possible z_off64_t types off_t, off64_t, and long are all signed) */ +#ifdef INT_MAX +# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX) +#else +unsigned ZLIB_INTERNAL gz_intmax OF((void)); +# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax()) +#endif diff --git a/dep/StormLib/src/zlib/inffast.c b/dep/StormLib/src/zlib/inffast.c new file mode 100644 index 0000000000..2f1d60b43b --- /dev/null +++ b/dep/StormLib/src/zlib/inffast.c @@ -0,0 +1,340 @@ +/* inffast.c -- fast decoding + * Copyright (C) 1995-2008, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifndef ASMINF + +/* Allow machine dependent optimization for post-increment or pre-increment. + Based on testing to date, + Pre-increment preferred for: + - PowerPC G3 (Adler) + - MIPS R5000 (Randers-Pehrson) + Post-increment preferred for: + - none + No measurable difference: + - Pentium III (Anderson) + - M68060 (Nikl) + */ +#ifdef POSTINC +# define OFF 0 +# define PUP(a) *(a)++ +#else +# define OFF 1 +# define PUP(a) *++(a) +#endif + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode == LEN + strm->avail_in >= 6 + strm->avail_out >= 258 + start >= strm->avail_out + state->bits < 8 + + On return, state->mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm->avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm->avail_out >= 258 for each loop to avoid checking for + output space. + */ +void ZLIB_INTERNAL inflate_fast(strm, start) +z_streamp strm; +unsigned start; /* inflate()'s starting value for strm->avail_out */ +{ + struct inflate_state FAR *state; + unsigned char FAR *in; /* local strm->next_in */ + unsigned char FAR *last; /* while in < last, enough input available */ + unsigned char FAR *out; /* local strm->next_out */ + unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ + unsigned char FAR *end; /* while out < end, enough space available */ +#ifdef INFLATE_STRICT + unsigned dmax; /* maximum distance from zlib header */ +#endif + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned wnext; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ + unsigned long hold; /* local strm->hold */ + unsigned bits; /* local strm->bits */ + code const FAR *lcode; /* local strm->lencode */ + code const FAR *dcode; /* local strm->distcode */ + unsigned lmask; /* mask for first level of length codes */ + unsigned dmask; /* mask for first level of distance codes */ + code here; /* retrieved table entry */ + unsigned op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + unsigned len; /* match length, unused bytes */ + unsigned dist; /* match distance */ + unsigned char FAR *from; /* where to copy match from */ + + /* copy state to local variables */ + state = (struct inflate_state FAR *)strm->state; + in = strm->next_in - OFF; + last = in + (strm->avail_in - 5); + out = strm->next_out - OFF; + beg = out - (start - strm->avail_out); + end = out + (strm->avail_out - 257); +#ifdef INFLATE_STRICT + dmax = state->dmax; +#endif + wsize = state->wsize; + whave = state->whave; + wnext = state->wnext; + window = state->window; + hold = state->hold; + bits = state->bits; + lcode = state->lencode; + dcode = state->distcode; + lmask = (1U << state->lenbits) - 1; + dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + do { + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + here = lcode[hold & lmask]; + dolen: + op = (unsigned)(here.bits); + hold >>= op; + bits -= op; + op = (unsigned)(here.op); + if (op == 0) { /* literal */ + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", here.val)); + PUP(out) = (unsigned char)(here.val); + } + else if (op & 16) { /* length base */ + len = (unsigned)(here.val); + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + len += (unsigned)hold & ((1U << op) - 1); + hold >>= op; + bits -= op; + } + Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + here = dcode[hold & dmask]; + dodist: + op = (unsigned)(here.bits); + hold >>= op; + bits -= op; + op = (unsigned)(here.op); + if (op & 16) { /* distance base */ + dist = (unsigned)(here.val); + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + } + dist += (unsigned)hold & ((1U << op) - 1); +#ifdef INFLATE_STRICT + if (dist > dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + hold >>= op; + bits -= op; + Tracevv((stderr, "inflate: distance %u\n", dist)); + op = (unsigned)(out - beg); /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + if (state->sane) { + strm->msg = + (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + if (len <= op - whave) { + do { + PUP(out) = 0; + } while (--len); + continue; + } + len -= op - whave; + do { + PUP(out) = 0; + } while (--op > whave); + if (op == 0) { + from = out - dist; + do { + PUP(out) = PUP(from); + } while (--len); + continue; + } +#endif + } + from = window - OFF; + if (wnext == 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + else if (wnext < op) { /* wrap around window */ + from += wsize + wnext - op; + op -= wnext; + if (op < len) { /* some from end of window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = window - OFF; + if (wnext < len) { /* some from start of window */ + op = wnext; + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + } + else { /* contiguous in window */ + from += wnext - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + while (len > 2) { + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + else { + from = out - dist; /* copy direct from output */ + do { /* minimum length is three */ + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } while (len > 2); + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + } + else if ((op & 64) == 0) { /* 2nd level distance code */ + here = dcode[here.val + (hold & ((1U << op) - 1))]; + goto dodist; + } + else { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + } + else if ((op & 64) == 0) { /* 2nd level length code */ + here = lcode[here.val + (hold & ((1U << op) - 1))]; + goto dolen; + } + else if (op & 32) { /* end-of-block */ + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + else { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + } while (in < last && out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + in -= len; + bits -= len << 3; + hold &= (1U << bits) - 1; + + /* update state and return */ + strm->next_in = in + OFF; + strm->next_out = out + OFF; + strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); + strm->avail_out = (unsigned)(out < end ? + 257 + (end - out) : 257 - (out - end)); + state->hold = hold; + state->bits = bits; + return; +} + +/* + inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): + - Using bit fields for code structure + - Different op definition to avoid & for extra bits (do & for table bits) + - Three separate decoding do-loops for direct, window, and wnext == 0 + - Special case for distance > 1 copies to do overlapped load and store copy + - Explicit branch predictions (based on measured branch probabilities) + - Deferring match copy and interspersed it with decoding subsequent codes + - Swapping literal/length else + - Swapping window/direct else + - Larger unrolled copy loops (three is about right) + - Moving len -= 3 statement into middle of loop + */ + +#endif /* !ASMINF */ diff --git a/dep/StormLib/src/zlib/inffast.h b/dep/StormLib/src/zlib/inffast.h new file mode 100644 index 0000000000..e5c1aa4ca8 --- /dev/null +++ b/dep/StormLib/src/zlib/inffast.h @@ -0,0 +1,11 @@ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-2003, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +void ZLIB_INTERNAL inflate_fast OF((z_streamp strm, unsigned start)); diff --git a/dep/StormLib/src/zlib/inffixed.h b/dep/StormLib/src/zlib/inffixed.h new file mode 100644 index 0000000000..75ed4b5978 --- /dev/null +++ b/dep/StormLib/src/zlib/inffixed.h @@ -0,0 +1,94 @@ + /* inffixed.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + + /* WARNING: this file should *not* be used by applications. It + is part of the implementation of the compression library and + is subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} + }; + + static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} + }; diff --git a/dep/StormLib/src/zlib/inflate.c b/dep/StormLib/src/zlib/inflate.c new file mode 100644 index 0000000000..a8431abeac --- /dev/null +++ b/dep/StormLib/src/zlib/inflate.c @@ -0,0 +1,1480 @@ +/* inflate.c -- zlib decompression + * Copyright (C) 1995-2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * Change history: + * + * 1.2.beta0 24 Nov 2002 + * - First version -- complete rewrite of inflate to simplify code, avoid + * creation of window when not needed, minimize use of window when it is + * needed, make inffast.c even faster, implement gzip decoding, and to + * improve code readability and style over the previous zlib inflate code + * + * 1.2.beta1 25 Nov 2002 + * - Use pointers for available input and output checking in inffast.c + * - Remove input and output counters in inffast.c + * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 + * - Remove unnecessary second byte pull from length extra in inffast.c + * - Unroll direct copy to three copies per loop in inffast.c + * + * 1.2.beta2 4 Dec 2002 + * - Change external routine names to reduce potential conflicts + * - Correct filename to inffixed.h for fixed tables in inflate.c + * - Make hbuf[] unsigned char to match parameter type in inflate.c + * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) + * to avoid negation problem on Alphas (64 bit) in inflate.c + * + * 1.2.beta3 22 Dec 2002 + * - Add comments on state->bits assertion in inffast.c + * - Add comments on op field in inftrees.h + * - Fix bug in reuse of allocated window after inflateReset() + * - Remove bit fields--back to byte structure for speed + * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths + * - Change post-increments to pre-increments in inflate_fast(), PPC biased? + * - Add compile time option, POSTINC, to use post-increments instead (Intel?) + * - Make MATCH copy in inflate() much faster for when inflate_fast() not used + * - Use local copies of stream next and avail values, as well as local bit + * buffer and bit count in inflate()--for speed when inflate_fast() not used + * + * 1.2.beta4 1 Jan 2003 + * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings + * - Move a comment on output buffer sizes from inffast.c to inflate.c + * - Add comments in inffast.c to introduce the inflate_fast() routine + * - Rearrange window copies in inflate_fast() for speed and simplification + * - Unroll last copy for window match in inflate_fast() + * - Use local copies of window variables in inflate_fast() for speed + * - Pull out common wnext == 0 case for speed in inflate_fast() + * - Make op and len in inflate_fast() unsigned for consistency + * - Add FAR to lcode and dcode declarations in inflate_fast() + * - Simplified bad distance check in inflate_fast() + * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new + * source file infback.c to provide a call-back interface to inflate for + * programs like gzip and unzip -- uses window as output buffer to avoid + * window copying + * + * 1.2.beta5 1 Jan 2003 + * - Improved inflateBack() interface to allow the caller to provide initial + * input in strm. + * - Fixed stored blocks bug in inflateBack() + * + * 1.2.beta6 4 Jan 2003 + * - Added comments in inffast.c on effectiveness of POSTINC + * - Typecasting all around to reduce compiler warnings + * - Changed loops from while (1) or do {} while (1) to for (;;), again to + * make compilers happy + * - Changed type of window in inflateBackInit() to unsigned char * + * + * 1.2.beta7 27 Jan 2003 + * - Changed many types to unsigned or unsigned short to avoid warnings + * - Added inflateCopy() function + * + * 1.2.0 9 Mar 2003 + * - Changed inflateBack() interface to provide separate opaque descriptors + * for the in() and out() functions + * - Changed inflateBack() argument and in_func typedef to swap the length + * and buffer address return values for the input function + * - Check next_in and next_out for Z_NULL on entry to inflate() + * + * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifdef MAKEFIXED +# ifndef BUILDFIXED +# define BUILDFIXED +# endif +#endif + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); +local int updatewindow OF((z_streamp strm, unsigned out)); +#ifdef BUILDFIXED + void makefixed OF((void)); +#endif +local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf, + unsigned len)); + +int ZEXPORT inflateReset(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + strm->total_in = strm->total_out = state->total = 0; + strm->msg = Z_NULL; + strm->adler = 1; /* to support ill-conceived Java test suite */ + state->mode = HEAD; + state->last = 0; + state->havedict = 0; + state->dmax = 32768U; + state->head = Z_NULL; + state->wsize = 0; + state->whave = 0; + state->wnext = 0; + state->hold = 0; + state->bits = 0; + state->lencode = state->distcode = state->next = state->codes; + state->sane = 1; + state->back = -1; + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int ZEXPORT inflateReset2(strm, windowBits) +z_streamp strm; +int windowBits; +{ + int wrap; + struct inflate_state FAR *state; + + /* get the state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* extract wrap request from windowBits parameter */ + if (windowBits < 0) { + wrap = 0; + windowBits = -windowBits; + } + else { + wrap = (windowBits >> 4) + 1; +#ifdef GUNZIP + if (windowBits < 48) + windowBits &= 15; +#endif + } + + /* set number of window bits, free window if different */ + if (windowBits && (windowBits < 8 || windowBits > 15)) + return Z_STREAM_ERROR; + if (state->window != Z_NULL && state->wbits != (unsigned)windowBits) { + ZFREE(strm, state->window); + state->window = Z_NULL; + } + + /* update state and reset the rest of it */ + state->wrap = wrap; + state->wbits = (unsigned)windowBits; + return inflateReset(strm); +} + +int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) +z_streamp strm; +int windowBits; +const char *version; +int stream_size; +{ + int ret; + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL) return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *) + ZALLOC(strm, 1, sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + state->window = Z_NULL; + ret = inflateReset2(strm, windowBits); + if (ret != Z_OK) { + ZFREE(strm, state); + strm->state = Z_NULL; + } + return ret; +} + +int ZEXPORT inflateInit_(strm, version, stream_size) +z_streamp strm; +const char *version; +int stream_size; +{ + return inflateInit2_(strm, DEF_WBITS, version, stream_size); +} + +int ZEXPORT inflatePrime(strm, bits, value) +z_streamp strm; +int bits; +int value; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (bits < 0) { + state->hold = 0; + state->bits = 0; + return Z_OK; + } + if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; + value &= (1L << bits) - 1; + state->hold += value << state->bits; + state->bits += bits; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +#ifdef MAKEFIXED +#include + +/* + Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also + defines BUILDFIXED, so the tables are built on the fly. makefixed() writes + those tables to stdout, which would be piped to inffixed.h. A small program + can simply call makefixed to do this: + + void makefixed(void); + + int main(void) + { + makefixed(); + return 0; + } + + Then that can be linked with zlib built with MAKEFIXED defined and run: + + a.out > inffixed.h + */ +void makefixed() +{ + unsigned low, size; + struct inflate_state state; + + fixedtables(&state); + puts(" /* inffixed.h -- table for decoding fixed codes"); + puts(" * Generated automatically by makefixed()."); + puts(" */"); + puts(""); + puts(" /* WARNING: this file should *not* be used by applications."); + puts(" It is part of the implementation of this library and is"); + puts(" subject to change. Applications should only use zlib.h."); + puts(" */"); + puts(""); + size = 1U << 9; + printf(" static const code lenfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 7) == 0) printf("\n "); + printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits, + state.lencode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); + size = 1U << 5; + printf("\n static const code distfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 6) == 0) printf("\n "); + printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, + state.distcode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); +} +#endif /* MAKEFIXED */ + +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +local int updatewindow(strm, out) +z_streamp strm; +unsigned out; +{ + struct inflate_state FAR *state; + unsigned copy, dist; + + state = (struct inflate_state FAR *)strm->state; + + /* if it hasn't been done already, allocate space for the window */ + if (state->window == Z_NULL) { + state->window = (unsigned char FAR *) + ZALLOC(strm, 1U << state->wbits, + sizeof(unsigned char)); + if (state->window == Z_NULL) return 1; + } + + /* if window not in use yet, initialize */ + if (state->wsize == 0) { + state->wsize = 1U << state->wbits; + state->wnext = 0; + state->whave = 0; + } + + /* copy state->wsize or less output bytes into the circular window */ + copy = out - strm->avail_out; + if (copy >= state->wsize) { + zmemcpy(state->window, strm->next_out - state->wsize, state->wsize); + state->wnext = 0; + state->whave = state->wsize; + } + else { + dist = state->wsize - state->wnext; + if (dist > copy) dist = copy; + zmemcpy(state->window + state->wnext, strm->next_out - copy, dist); + copy -= dist; + if (copy) { + zmemcpy(state->window, strm->next_out - copy, copy); + state->wnext = copy; + state->whave = state->wsize; + } + else { + state->wnext += dist; + if (state->wnext == state->wsize) state->wnext = 0; + if (state->whave < state->wsize) state->whave += dist; + } + } + return 0; +} + +/* Macros for inflate(): */ + +/* check function to use adler32() for zlib or crc32() for gzip */ +#ifdef GUNZIP +# define UPDATE(check, buf, len) \ + (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) +#else +# define UPDATE(check, buf, len) adler32(check, buf, len) +#endif + +/* check macros for header crc */ +#ifdef GUNZIP +# define CRC2(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + check = crc32(check, hbuf, 2); \ + } while (0) + +# define CRC4(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + hbuf[2] = (unsigned char)((word) >> 16); \ + hbuf[3] = (unsigned char)((word) >> 24); \ + check = crc32(check, hbuf, 4); \ + } while (0) +#endif + +/* Load registers with state in inflate() for speed */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Restore state from registers in inflate() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflate() + if there is no input available. */ +#define PULLBYTE() \ + do { \ + if (have == 0) goto inf_leave; \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflate(). */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Reverse the bytes in a 32-bit value */ +#define REVERSE(q) \ + ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +/* + inflate() uses a state machine to process as much input data and generate as + much output data as possible before returning. The state machine is + structured roughly as follows: + + for (;;) switch (state) { + ... + case STATEn: + if (not enough input data or output space to make progress) + return; + ... make progress ... + state = STATEm; + break; + ... + } + + so when inflate() is called again, the same case is attempted again, and + if the appropriate resources are provided, the machine proceeds to the + next state. The NEEDBITS() macro is usually the way the state evaluates + whether it can proceed or should return. NEEDBITS() does the return if + the requested bits are not available. The typical use of the BITS macros + is: + + NEEDBITS(n); + ... do something with BITS(n) ... + DROPBITS(n); + + where NEEDBITS(n) either returns from inflate() if there isn't enough + input left to load n bits into the accumulator, or it continues. BITS(n) + gives the low n bits in the accumulator. When done, DROPBITS(n) drops + the low n bits off the accumulator. INITBITS() clears the accumulator + and sets the number of available bits to zero. BYTEBITS() discards just + enough bits to put the accumulator on a byte boundary. After BYTEBITS() + and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. + + NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return + if there is no input available. The decoding of variable length codes uses + PULLBYTE() directly in order to pull just enough bytes to decode the next + code, and no more. + + Some states loop until they get enough input, making sure that enough + state information is maintained to continue the loop where it left off + if NEEDBITS() returns in the loop. For example, want, need, and keep + would all have to actually be part of the saved state in case NEEDBITS() + returns: + + case STATEw: + while (want < need) { + NEEDBITS(n); + keep[want++] = BITS(n); + DROPBITS(n); + } + state = STATEx; + case STATEx: + + As shown above, if the next state is also the next case, then the break + is omitted. + + A state may also return if there is not enough output space available to + complete that state. Those states are copying stored data, writing a + literal byte, and copying a matching string. + + When returning, a "goto inf_leave" is used to update the total counters, + update the check value, and determine whether any progress has been made + during that inflate() call in order to return the proper return code. + Progress is defined as a change in either strm->avail_in or strm->avail_out. + When there is a window, goto inf_leave will update the window with the last + output written. If a goto inf_leave occurs in the middle of decompression + and there is no window currently, goto inf_leave will create one and copy + output to the window for the next call of inflate(). + + In this implementation, the flush parameter of inflate() only affects the + return code (per zlib.h). inflate() always writes as much as possible to + strm->next_out, given the space available and the provided input--the effect + documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers + the allocation of and copying into a sliding window until necessary, which + provides the effect documented in zlib.h for Z_FINISH when the entire input + stream available. So the only thing the flush parameter actually does is: + when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it + will return Z_BUF_ERROR if it has not reached the end of the stream. + */ + +int ZEXPORT inflate(strm, flush) +z_streamp strm; +int flush; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned in, out; /* save starting available input and output */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code here; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ +#ifdef GUNZIP + unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ +#endif + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0)) + return Z_STREAM_ERROR; + + state = (struct inflate_state FAR *)strm->state; + if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ + LOAD(); + in = have; + out = left; + ret = Z_OK; + for (;;) + switch (state->mode) { + case HEAD: + if (state->wrap == 0) { + state->mode = TYPEDO; + break; + } + NEEDBITS(16); +#ifdef GUNZIP + if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ + state->check = crc32(0L, Z_NULL, 0); + CRC2(state->check, hold); + INITBITS(); + state->mode = FLAGS; + break; + } + state->flags = 0; /* expect zlib header */ + if (state->head != Z_NULL) + state->head->done = -1; + if (!(state->wrap & 1) || /* check if zlib header allowed */ +#else + if ( +#endif + ((BITS(8) << 8) + (hold >> 8)) % 31) { + strm->msg = (char *)"incorrect header check"; + state->mode = BAD; + break; + } + if (BITS(4) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + DROPBITS(4); + len = BITS(4) + 8; + if (state->wbits == 0) + state->wbits = len; + else if (len > state->wbits) { + strm->msg = (char *)"invalid window size"; + state->mode = BAD; + break; + } + state->dmax = 1U << len; + Tracev((stderr, "inflate: zlib header ok\n")); + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = hold & 0x200 ? DICTID : TYPE; + INITBITS(); + break; +#ifdef GUNZIP + case FLAGS: + NEEDBITS(16); + state->flags = (int)(hold); + if ((state->flags & 0xff) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + if (state->flags & 0xe000) { + strm->msg = (char *)"unknown header flags set"; + state->mode = BAD; + break; + } + if (state->head != Z_NULL) + state->head->text = (int)((hold >> 8) & 1); + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = TIME; + case TIME: + NEEDBITS(32); + if (state->head != Z_NULL) + state->head->time = hold; + if (state->flags & 0x0200) CRC4(state->check, hold); + INITBITS(); + state->mode = OS; + case OS: + NEEDBITS(16); + if (state->head != Z_NULL) { + state->head->xflags = (int)(hold & 0xff); + state->head->os = (int)(hold >> 8); + } + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = EXLEN; + case EXLEN: + if (state->flags & 0x0400) { + NEEDBITS(16); + state->length = (unsigned)(hold); + if (state->head != Z_NULL) + state->head->extra_len = (unsigned)hold; + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + } + else if (state->head != Z_NULL) + state->head->extra = Z_NULL; + state->mode = EXTRA; + case EXTRA: + if (state->flags & 0x0400) { + copy = state->length; + if (copy > have) copy = have; + if (copy) { + if (state->head != Z_NULL && + state->head->extra != Z_NULL) { + len = state->head->extra_len - state->length; + zmemcpy(state->head->extra + len, next, + len + copy > state->head->extra_max ? + state->head->extra_max - len : copy); + } + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + state->length -= copy; + } + if (state->length) goto inf_leave; + } + state->length = 0; + state->mode = NAME; + case NAME: + if (state->flags & 0x0800) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->name != Z_NULL && + state->length < state->head->name_max) + state->head->name[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->name = Z_NULL; + state->length = 0; + state->mode = COMMENT; + case COMMENT: + if (state->flags & 0x1000) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->comment != Z_NULL && + state->length < state->head->comm_max) + state->head->comment[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->comment = Z_NULL; + state->mode = HCRC; + case HCRC: + if (state->flags & 0x0200) { + NEEDBITS(16); + if (hold != (state->check & 0xffff)) { + strm->msg = (char *)"header crc mismatch"; + state->mode = BAD; + break; + } + INITBITS(); + } + if (state->head != Z_NULL) { + state->head->hcrc = (int)((state->flags >> 9) & 1); + state->head->done = 1; + } + strm->adler = state->check = crc32(0L, Z_NULL, 0); + state->mode = TYPE; + break; +#endif + case DICTID: + NEEDBITS(32); + strm->adler = state->check = REVERSE(hold); + INITBITS(); + state->mode = DICT; + case DICT: + if (state->havedict == 0) { + RESTORE(); + return Z_NEED_DICT; + } + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = TYPE; + case TYPE: + if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave; + case TYPEDO: + if (state->last) { + BYTEBITS(); + state->mode = CHECK; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN_; /* decode codes */ + if (flush == Z_TREES) { + DROPBITS(2); + goto inf_leave; + } + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + case STORED: + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + state->mode = COPY_; + if (flush == Z_TREES) goto inf_leave; + case COPY_: + state->mode = COPY; + case COPY: + copy = state->length; + if (copy) { + if (copy > have) copy = have; + if (copy > left) copy = left; + if (copy == 0) goto inf_leave; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + break; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + case TABLE: + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + state->have = 0; + state->mode = LENLENS; + case LENLENS: + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + state->have = 0; + state->mode = CODELENS; + case CODELENS: + while (state->have < state->nlen + state->ndist) { + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.val < 16) { + NEEDBITS(here.bits); + DROPBITS(here.bits); + state->lens[state->have++] = here.val; + } + else { + if (here.val == 16) { + NEEDBITS(here.bits + 2); + DROPBITS(here.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = state->lens[state->have - 1]; + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (here.val == 17) { + NEEDBITS(here.bits + 3); + DROPBITS(here.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(here.bits + 7); + DROPBITS(here.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* check for end-of-block code (better have one) */ + if (state->lens[256] == 0) { + strm->msg = (char *)"invalid code -- missing end-of-block"; + state->mode = BAD; + break; + } + + /* build code tables -- note: do not change the lenbits or distbits + values here (9 and 6) without reading the comments in inftrees.h + concerning the ENOUGH constants, which depend on those values */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN_; + if (flush == Z_TREES) goto inf_leave; + case LEN_: + state->mode = LEN; + case LEN: + if (have >= 6 && left >= 258) { + RESTORE(); + inflate_fast(strm, out); + LOAD(); + if (state->mode == TYPE) + state->back = -1; + break; + } + state->back = 0; + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.op && (here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + state->back += last.bits; + } + DROPBITS(here.bits); + state->back += here.bits; + state->length = (unsigned)here.val; + if ((int)(here.op) == 0) { + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", here.val)); + state->mode = LIT; + break; + } + if (here.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->back = -1; + state->mode = TYPE; + break; + } + if (here.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + state->extra = (unsigned)(here.op) & 15; + state->mode = LENEXT; + case LENEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + state->back += state->extra; + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + state->was = state->length; + state->mode = DIST; + case DIST: + for (;;) { + here = state->distcode[BITS(state->distbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if ((here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + state->back += last.bits; + } + DROPBITS(here.bits); + state->back += here.bits; + if (here.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)here.val; + state->extra = (unsigned)(here.op) & 15; + state->mode = DISTEXT; + case DISTEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + state->back += state->extra; + } +#ifdef INFLATE_STRICT + if (state->offset > state->dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + state->mode = MATCH; + case MATCH: + if (left == 0) goto inf_leave; + copy = out - left; + if (state->offset > copy) { /* copy from window */ + copy = state->offset - copy; + if (copy > state->whave) { + if (state->sane) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + Trace((stderr, "inflate.c too far\n")); + copy -= state->whave; + if (copy > state->length) copy = state->length; + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = 0; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; +#endif + } + if (copy > state->wnext) { + copy -= state->wnext; + from = state->window + (state->wsize - copy); + } + else + from = state->window + (state->wnext - copy); + if (copy > state->length) copy = state->length; + } + else { /* copy from output */ + from = put - state->offset; + copy = state->length; + } + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = *from++; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; + case LIT: + if (left == 0) goto inf_leave; + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + case CHECK: + if (state->wrap) { + NEEDBITS(32); + out -= left; + strm->total_out += out; + state->total += out; + if (out) + strm->adler = state->check = + UPDATE(state->check, put - out, out); + out = left; + if (( +#ifdef GUNZIP + state->flags ? hold : +#endif + REVERSE(hold)) != state->check) { + strm->msg = (char *)"incorrect data check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: check matches trailer\n")); + } +#ifdef GUNZIP + state->mode = LENGTH; + case LENGTH: + if (state->wrap && state->flags) { + NEEDBITS(32); + if (hold != (state->total & 0xffffffffUL)) { + strm->msg = (char *)"incorrect length check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: length matches trailer\n")); + } +#endif + state->mode = DONE; + case DONE: + ret = Z_STREAM_END; + goto inf_leave; + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + default: + return Z_STREAM_ERROR; + } + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + inf_leave: + RESTORE(); + if (state->wsize || (state->mode < CHECK && out != strm->avail_out)) + if (updatewindow(strm, out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + in -= strm->avail_in; + out -= strm->avail_out; + strm->total_in += in; + strm->total_out += out; + state->total += out; + if (state->wrap && out) + strm->adler = state->check = + UPDATE(state->check, strm->next_out - out, out); + strm->data_type = state->bits + (state->last ? 64 : 0) + + (state->mode == TYPE ? 128 : 0) + + (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0); + if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) + ret = Z_BUF_ERROR; + return ret; +} + +int ZEXPORT inflateEnd(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->window != Z_NULL) ZFREE(strm, state->window); + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + +int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) +z_streamp strm; +const Bytef *dictionary; +uInt dictLength; +{ + struct inflate_state FAR *state; + unsigned long id; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->wrap != 0 && state->mode != DICT) + return Z_STREAM_ERROR; + + /* check for correct dictionary id */ + if (state->mode == DICT) { + id = adler32(0L, Z_NULL, 0); + id = adler32(id, dictionary, dictLength); + if (id != state->check) + return Z_DATA_ERROR; + } + + /* copy dictionary to window */ + if (updatewindow(strm, strm->avail_out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + if (dictLength > state->wsize) { + zmemcpy(state->window, dictionary + dictLength - state->wsize, + state->wsize); + state->whave = state->wsize; + } + else { + zmemcpy(state->window + state->wsize - dictLength, dictionary, + dictLength); + state->whave = dictLength; + } + state->havedict = 1; + Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK; +} + +int ZEXPORT inflateGetHeader(strm, head) +z_streamp strm; +gz_headerp head; +{ + struct inflate_state FAR *state; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; + + /* save header structure */ + state->head = head; + head->done = 0; + return Z_OK; +} + +/* + Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found + or when out of input. When called, *have is the number of pattern bytes + found in order so far, in 0..3. On return *have is updated to the new + state. If on return *have equals four, then the pattern was found and the + return value is how many bytes were read including the last byte of the + pattern. If *have is less than four, then the pattern has not been found + yet and the return value is len. In the latter case, syncsearch() can be + called again with more data and the *have state. *have is initialized to + zero for the first call. + */ +local unsigned syncsearch(have, buf, len) +unsigned FAR *have; +unsigned char FAR *buf; +unsigned len; +{ + unsigned got; + unsigned next; + + got = *have; + next = 0; + while (next < len && got < 4) { + if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) + got++; + else if (buf[next]) + got = 0; + else + got = 4 - got; + next++; + } + *have = got; + return next; +} + +int ZEXPORT inflateSync(strm) +z_streamp strm; +{ + unsigned len; /* number of bytes to look at or looked at */ + unsigned long in, out; /* temporary to save total_in and total_out */ + unsigned char buf[4]; /* to restore bit buffer to byte string */ + struct inflate_state FAR *state; + + /* check parameters */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; + + /* if first time, start search in bit buffer */ + if (state->mode != SYNC) { + state->mode = SYNC; + state->hold <<= state->bits & 7; + state->bits -= state->bits & 7; + len = 0; + while (state->bits >= 8) { + buf[len++] = (unsigned char)(state->hold); + state->hold >>= 8; + state->bits -= 8; + } + state->have = 0; + syncsearch(&(state->have), buf, len); + } + + /* search available input */ + len = syncsearch(&(state->have), strm->next_in, strm->avail_in); + strm->avail_in -= len; + strm->next_in += len; + strm->total_in += len; + + /* return no joy or set up to restart inflate() on a new block */ + if (state->have != 4) return Z_DATA_ERROR; + in = strm->total_in; out = strm->total_out; + inflateReset(strm); + strm->total_in = in; strm->total_out = out; + state->mode = TYPE; + return Z_OK; +} + +/* + Returns true if inflate is currently at the end of a block generated by + Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + implementation to provide an additional safety check. PPP uses + Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored + block. When decompressing, PPP checks that at the end of input packet, + inflate is waiting for these length bytes. + */ +int ZEXPORT inflateSyncPoint(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + return state->mode == STORED && state->bits == 0; +} + +int ZEXPORT inflateCopy(dest, source) +z_streamp dest; +z_streamp source; +{ + struct inflate_state FAR *state; + struct inflate_state FAR *copy; + unsigned char FAR *window; + unsigned wsize; + + /* check input */ + if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL || + source->zalloc == (alloc_func)0 || source->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)source->state; + + /* allocate space */ + copy = (struct inflate_state FAR *) + ZALLOC(source, 1, sizeof(struct inflate_state)); + if (copy == Z_NULL) return Z_MEM_ERROR; + window = Z_NULL; + if (state->window != Z_NULL) { + window = (unsigned char FAR *) + ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); + if (window == Z_NULL) { + ZFREE(source, copy); + return Z_MEM_ERROR; + } + } + + /* copy state */ + zmemcpy(dest, source, sizeof(z_stream)); + zmemcpy(copy, state, sizeof(struct inflate_state)); + if (state->lencode >= state->codes && + state->lencode <= state->codes + ENOUGH - 1) { + copy->lencode = copy->codes + (state->lencode - state->codes); + copy->distcode = copy->codes + (state->distcode - state->codes); + } + copy->next = copy->codes + (state->next - state->codes); + if (window != Z_NULL) { + wsize = 1U << state->wbits; + zmemcpy(window, state->window, wsize); + } + copy->window = window; + dest->state = (struct internal_state FAR *)copy; + return Z_OK; +} + +int ZEXPORT inflateUndermine(strm, subvert) +z_streamp strm; +int subvert; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + state->sane = !subvert; +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + return Z_OK; +#else + state->sane = 1; + return Z_DATA_ERROR; +#endif +} + +long ZEXPORT inflateMark(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return -1L << 16; + state = (struct inflate_state FAR *)strm->state; + return ((long)(state->back) << 16) + + (state->mode == COPY ? state->length : + (state->mode == MATCH ? state->was - state->length : 0)); +} diff --git a/dep/StormLib/src/zlib/inflate.h b/dep/StormLib/src/zlib/inflate.h new file mode 100644 index 0000000000..eaef3dc087 --- /dev/null +++ b/dep/StormLib/src/zlib/inflate.h @@ -0,0 +1,130 @@ +/* inflate.h -- internal inflate state definition + * Copyright (C) 1995-2016 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +#ifndef __INFLATE_H_ +#define __INFLATE_H_ + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer decoding by inflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip decoding + should be left enabled. */ +#ifndef NO_GZIP +# define GUNZIP +#endif + +/* Possible inflate modes between inflate() calls */ +typedef enum { + HEAD = 16180, /* i: waiting for magic header */ + FLAGS, /* i: waiting for method and flags (gzip) */ + TIME, /* i: waiting for modification time (gzip) */ + OS, /* i: waiting for extra flags and operating system (gzip) */ + EXLEN, /* i: waiting for extra length (gzip) */ + EXTRA, /* i: waiting for extra bytes (gzip) */ + NAME, /* i: waiting for end of file name (gzip) */ + COMMENT, /* i: waiting for end of comment (gzip) */ + HCRC, /* i: waiting for header crc (gzip) */ + DICTID, /* i: waiting for dictionary check value */ + DICT, /* waiting for inflateSetDictionary() call */ + TYPE, /* i: waiting for type bits, including last-flag bit */ + TYPEDO, /* i: same, but skip check to exit inflate on new block */ + STORED, /* i: waiting for stored size (length and complement) */ + COPY_, /* i/o: same as COPY below, but only first time in */ + COPY, /* i/o: waiting for input or output to copy stored block */ + TABLE, /* i: waiting for dynamic block table lengths */ + LENLENS, /* i: waiting for code length code lengths */ + CODELENS, /* i: waiting for length/lit and distance code lengths */ + LEN_, /* i: same as LEN below, but only first time in */ + LEN, /* i: waiting for length/lit/eob code */ + LENEXT, /* i: waiting for length extra bits */ + DIST, /* i: waiting for distance code */ + DISTEXT, /* i: waiting for distance extra bits */ + MATCH, /* o: waiting for output space to copy string */ + LIT, /* o: waiting for output space to write literal */ + CHECK, /* i: waiting for 32-bit check value */ + LENGTH, /* i: waiting for 32-bit length (gzip) */ + DONE, /* finished check, done -- remain here until reset */ + BAD, /* got a data error -- remain here until reset */ + MEM, /* got an inflate() memory error -- remain here until reset */ + SYNC /* looking for synchronization bytes to restart inflate() */ +} inflate_mode; + +/* + State transitions between above modes - + + (most modes can go to BAD or MEM on error -- not shown for clarity) + + Process header: + HEAD -> (gzip) or (zlib) or (raw) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME -> COMMENT -> + HCRC -> TYPE + (zlib) -> DICTID or TYPE + DICTID -> DICT -> TYPE + (raw) -> TYPEDO + Read deflate blocks: + TYPE -> TYPEDO -> STORED or TABLE or LEN_ or CHECK + STORED -> COPY_ -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN_ + LEN_ -> LEN + Read deflate codes in fixed or dynamic block: + LEN -> LENEXT or LIT or TYPE + LENEXT -> DIST -> DISTEXT -> MATCH -> LEN + LIT -> LEN + Process trailer: + CHECK -> LENGTH -> DONE + */ + +/* State maintained between inflate() calls -- approximately 7K bytes, not + including the allocated sliding window, which is up to 32K bytes. */ +struct inflate_state { + z_streamp strm; /* pointer back to this zlib stream */ + inflate_mode mode; /* current inflate mode */ + int last; /* true if processing last block */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip, + bit 2 true to validate check value */ + int havedict; /* true if dictionary provided */ + int flags; /* gzip header method and flags (0 if zlib) */ + unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ + unsigned long check; /* protected copy of check value */ + unsigned long total; /* protected copy of output count */ + gz_headerp head; /* where to save gzip header information */ + /* sliding window */ + unsigned wbits; /* log base 2 of requested window size */ + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned wnext; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if needed */ + /* bit accumulator */ + unsigned long hold; /* input bit accumulator */ + unsigned bits; /* number of bits in "in" */ + /* for string and stored block copying */ + unsigned length; /* literal or length of data to copy */ + unsigned offset; /* distance back to copy string from */ + /* for table and code decoding */ + unsigned extra; /* extra bits needed */ + /* fixed and dynamic code tables */ + code const FAR *lencode; /* starting table for length/literal codes */ + code const FAR *distcode; /* starting table for distance codes */ + unsigned lenbits; /* index bits for lencode */ + unsigned distbits; /* index bits for distcode */ + /* dynamic table building */ + unsigned ncode; /* number of code length code lengths */ + unsigned nlen; /* number of length code lengths */ + unsigned ndist; /* number of distance code lengths */ + unsigned have; /* number of code lengths in lens[] */ + code FAR *next; /* next available space in codes[] */ + unsigned short lens[320]; /* temporary storage for code lengths */ + unsigned short work[288]; /* work area for code table building */ + code codes[ENOUGH]; /* space for code tables */ + int sane; /* if false, allow invalid distance too far */ + int back; /* bits back of last unprocessed length/lit */ + unsigned was; /* initial length of match */ +}; + +#endif __INFLATE_H_ diff --git a/dep/StormLib/src/zlib/inftrees.c b/dep/StormLib/src/zlib/inftrees.c new file mode 100644 index 0000000000..11e9c52acc --- /dev/null +++ b/dep/StormLib/src/zlib/inftrees.c @@ -0,0 +1,330 @@ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" + +#define MAXBITS 15 + +const char inflate_copyright[] = + " inflate 1.2.5 Copyright 1995-2010 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* + Build a set of tables to decode the provided canonical Huffman code. + The code lengths are lens[0..codes-1]. The result starts at *table, + whose indices are 0..2^bits-1. work is a writable array of at least + lens shorts, which is used as a work area. type is the type of code + to be generated, CODES, LENS, or DISTS. On return, zero is success, + -1 is an invalid code, and +1 means that ENOUGH isn't enough. table + on return points to the next available entry's address. bits is the + requested root table index bits, and on return it is the actual root + table index bits. It will differ if the request is greater than the + longest code or if it is less than the shortest code. + */ +int ZLIB_INTERNAL inflate_table(type, lens, codes, table, bits, work) +codetype type; +unsigned short FAR *lens; +unsigned codes; +code FAR * FAR *table; +unsigned FAR *bits; +unsigned short FAR *work; +{ + unsigned len; /* a code's length in bits */ + unsigned sym; /* index of code symbols */ + unsigned min, max; /* minimum and maximum code lengths */ + unsigned root; /* number of index bits for root table */ + unsigned curr; /* number of index bits for current table */ + unsigned drop; /* code bits to drop for sub-table */ + int left; /* number of prefix codes available */ + unsigned used; /* code entries in table used */ + unsigned huff; /* Huffman code */ + unsigned incr; /* for incrementing code, index */ + unsigned fill; /* index for replicating entries */ + unsigned low; /* low bits for current root entry */ + unsigned mask; /* mask for low root bits */ + code here; /* table entry for duplication */ + code FAR *next; /* next available space in table */ + const unsigned short FAR *base; /* base value table to use */ + const unsigned short FAR *extra; /* extra bits table to use */ + int end; /* use base and extra for symbol > end */ + unsigned short count[MAXBITS+1]; /* number of codes of each length */ + unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ + static const unsigned short lbase[31] = { /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + static const unsigned short lext[31] = { /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 73, 195}; + static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0}; + static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64}; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) + count[len] = 0; + for (sym = 0; sym < codes; sym++) + count[lens[sym]]++; + + /* bound code lengths, force root to be within code lengths */ + root = *bits; + for (max = MAXBITS; max >= 1; max--) + if (count[max] != 0) break; + if (root > max) root = max; + if (max == 0) { /* no symbols to code at all */ + here.op = (unsigned char)64; /* invalid code marker */ + here.bits = (unsigned char)1; + here.val = (unsigned short)0; + *(*table)++ = here; /* make a table to force an error */ + *(*table)++ = here; + *bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min < max; min++) + if (count[min] != 0) break; + if (root < min) root = min; + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) return -1; /* over-subscribed */ + } + if (left > 0 && (type == CODES || max != 1)) + return -1; /* incomplete set */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + count[len]; + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) + if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked for LENS and DIST tables against + the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in + the initial root table size constants. See the comments in inftrees.h + for more information. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + switch (type) { + case CODES: + base = extra = work; /* dummy value--not used */ + end = 19; + break; + case LENS: + base = lbase; + base -= 257; + extra = lext; + extra -= 257; + end = 256; + break; + default: /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize state for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = *table; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = (unsigned)(-1); /* trigger new sub-table when len > root */ + used = 1U << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if ((type == LENS && used >= ENOUGH_LENS) || + (type == DISTS && used >= ENOUGH_DISTS)) + return 1; + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + here.bits = (unsigned char)(len - drop); + if ((int)(work[sym]) < end) { + here.op = (unsigned char)0; + here.val = work[sym]; + } + else if ((int)(work[sym]) > end) { + here.op = (unsigned char)(extra[work[sym]]); + here.val = base[work[sym]]; + } + else { + here.op = (unsigned char)(32 + 64); /* end of block */ + here.val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1U << (len - drop); + fill = 1U << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + next[(huff >> drop) + fill] = here; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + + /* go to next symbol, update count, len */ + sym++; + if (--(count[len]) == 0) { + if (len == max) break; + len = lens[work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) { + /* if first time, transition to sub-tables */ + if (drop == 0) + drop = root; + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = (int)(1 << curr); + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1U << curr; + if ((type == LENS && used >= ENOUGH_LENS) || + (type == DISTS && used >= ENOUGH_DISTS)) + return 1; + + /* point entry in root table to sub-table */ + low = huff & mask; + (*table)[low].op = (unsigned char)curr; + (*table)[low].bits = (unsigned char)root; + (*table)[low].val = (unsigned short)(next - *table); + } + } + + /* + Fill in rest of table for incomplete codes. This loop is similar to the + loop above in incrementing huff for table indices. It is assumed that + len is equal to curr + drop, so there is no loop needed to increment + through high index bits. When the current sub-table is filled, the loop + drops back to the root table to fill in any remaining entries there. + */ + here.op = (unsigned char)64; /* invalid code marker */ + here.bits = (unsigned char)(len - drop); + here.val = (unsigned short)0; + while (huff != 0) { + /* when done with sub-table, drop back to root table */ + if (drop != 0 && (huff & mask) != low) { + drop = 0; + len = root; + next = *table; + here.bits = (unsigned char)len; + } + + /* put invalid code marker in table */ + next[huff >> drop] = here; + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + } + + /* set return parameters */ + *table += used; + *bits = root; + return 0; +} diff --git a/dep/StormLib/src/zlib/inftrees.h b/dep/StormLib/src/zlib/inftrees.h new file mode 100644 index 0000000000..14429f37f9 --- /dev/null +++ b/dep/StormLib/src/zlib/inftrees.h @@ -0,0 +1,67 @@ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2005, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +#ifndef __INFTREES_H_ +#define __INFTREES_H_ + +/* Structure for decoding tables. Each entry provides either the + information needed to do the operation requested by the code that + indexed that table entry, or it provides a pointer to another + table that indexes more bits of the code. op indicates whether + the entry is a pointer to another table, a literal, a length or + distance, an end-of-block, or an invalid code. For a table + pointer, the low four bits of op is the number of index bits of + that table. For a length or distance, the low four bits of op + is the number of extra bits to get after the code. bits is + the number of bits in this code or part of the code to drop off + of the bit buffer. val is the actual byte to output in the case + of a literal, the base length or distance, or the offset from + the current table to the next table. Each entry is four bytes. */ +typedef struct { + unsigned char op; /* operation, extra bits, table bits */ + unsigned char bits; /* bits in this part of the code */ + unsigned short val; /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): + 00000000 - literal + 0000tttt - table link, tttt != 0 is the number of table index bits + 0001eeee - length or distance, eeee is the number of extra bits + 01100000 - end of block + 01000000 - invalid code + */ + +/* Maximum size of the dynamic table. The maximum number of code structures is + 1444, which is the sum of 852 for literal/length codes and 592 for distance + codes. These values were found by exhaustive searches using the program + examples/enough.c found in the zlib distribtution. The arguments to that + program are the number of symbols, the initial root table size, and the + maximum bit length of a code. "enough 286 9 15" for literal/length codes + returns returns 852, and "enough 30 6 15" for distance codes returns 592. + The initial root table size (9 or 6) is found in the fifth argument of the + inflate_table() calls in inflate.c and infback.c. If the root table size is + changed, then these maximum sizes would be need to be recalculated and + updated. */ +#define ENOUGH_LENS 852 +#define ENOUGH_DISTS 592 +#define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS) + +/* Type of code to build for inflate_table() */ +typedef enum { + CODES, + LENS, + DISTS +} codetype; + +int ZLIB_INTERNAL inflate_table OF((codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work)); + +#endif // __INFTREES_H_ diff --git a/dep/StormLib/src/zlib/trees.c b/dep/StormLib/src/zlib/trees.c new file mode 100644 index 0000000000..56e9bb1c11 --- /dev/null +++ b/dep/StormLib/src/zlib/trees.c @@ -0,0 +1,1244 @@ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1995-2010 Jean-loup Gailly + * detect_data_type() function provided freely by Cosmin Truta, 2006 + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + +/* @(#) $Id$ */ + +/* #define GEN_TREES_H */ + +#include "deflate.h" + +#ifdef DEBUG +# include +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local const int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local const uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +#define Buf_size (8 * 2*sizeof(char)) +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ + +#if defined(GEN_TREES_H) || !defined(STDC) +/* non ANSI compilers may not accept trees.h */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +uch _dist_code[DIST_CODE_LEN]; +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +uch _length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#else +# include "trees.h" +#endif /* GEN_TREES_H */ + +struct static_tree_desc_s { + const ct_data *static_tree; /* static tree or NULL */ + const intf *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +local static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local static_tree_desc static_bl_desc = +{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block OF((deflate_state *s)); +local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree OF((deflate_state *s, tree_desc *desc)); +local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local int build_bl_tree OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, + int blcodes)); +local void compress_block OF((deflate_state *s, ct_data *ltree, + ct_data *dtree)); +local int detect_data_type OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup OF((deflate_state *s)); +local void bi_flush OF((deflate_state *s)); +local void copy_block OF((deflate_state *s, charf *buf, unsigned len, + int header)); + +#ifdef GEN_TREES_H +local void gen_trees_header OF((void)); +#endif + +#ifndef DEBUG +# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(s, c, tree) \ + { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef DEBUG +local void send_bits OF((deflate_state *s, int value, int length)); + +local void send_bits(s, value, length) + deflate_state *s; + int value; /* value to send */ + int length; /* number of bits */ +{ + Tracevv((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; + + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (ush)value << s->bi_valid; + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= (ush)value << s->bi_valid; + s->bi_valid += length; + } +} +#else /* !DEBUG */ + +#define send_bits(s, value, length) \ +{ int len = length;\ + if (s->bi_valid > (int)Buf_size - len) {\ + int val = value;\ + s->bi_buf |= (ush)val << s->bi_valid;\ + put_short(s, s->bi_buf);\ + s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ + s->bi_valid += len - Buf_size;\ + } else {\ + s->bi_buf |= (ush)(value) << s->bi_valid;\ + s->bi_valid += len;\ + }\ +} +#endif /* DEBUG */ + + +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. + */ +local void tr_static_init() +{ +#if defined(GEN_TREES_H) || !defined(STDC) + static int static_init_done = 0; + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + if (static_init_done) return; + + /* For some embedded targets, global variables are not initialized: */ +#ifdef NO_INIT_GLOBAL_POINTERS + static_l_desc.static_tree = static_ltree; + static_l_desc.extra_bits = extra_lbits; + static_d_desc.static_tree = static_dtree; + static_d_desc.extra_bits = extra_dbits; + static_bl_desc.extra_bits = extra_blbits; +#endif + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + _dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse((unsigned)n, 5); + } + static_init_done = 1; + +# ifdef GEN_TREES_H + gen_trees_header(); +# endif +#endif /* defined(GEN_TREES_H) || !defined(STDC) */ +} + +/* =========================================================================== + * Genererate the file trees.h describing the static trees. + */ +#ifdef GEN_TREES_H +# ifndef DEBUG +# include +# endif + +# define SEPARATOR(i, last, width) \ + ((i) == (last)? "\n};\n\n" : \ + ((i) % (width) == (width)-1 ? ",\n" : ", ")) + +void gen_trees_header() +{ + FILE *header = fopen("trees.h", "w"); + int i; + + Assert (header != NULL, "Can't open trees.h"); + fprintf(header, + "/* header created automatically with -DGEN_TREES_H */\n\n"); + + fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); + for (i = 0; i < L_CODES+2; i++) { + fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, + static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); + } + + fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, + static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); + } + + fprintf(header, "const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {\n"); + for (i = 0; i < DIST_CODE_LEN; i++) { + fprintf(header, "%2u%s", _dist_code[i], + SEPARATOR(i, DIST_CODE_LEN-1, 20)); + } + + fprintf(header, + "const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); + for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { + fprintf(header, "%2u%s", _length_code[i], + SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); + } + + fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); + for (i = 0; i < LENGTH_CODES; i++) { + fprintf(header, "%1u%s", base_length[i], + SEPARATOR(i, LENGTH_CODES-1, 20)); + } + + fprintf(header, "local const int base_dist[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "%5u%s", base_dist[i], + SEPARATOR(i, D_CODES-1, 10)); + } + + fclose(header); +} +#endif /* GEN_TREES_H */ + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void ZLIB_INTERNAL _tr_init(s) + deflate_state *s; +{ + tr_static_init(); + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; + s->last_eob_len = 8; /* enough lookahead for inflate */ +#ifdef DEBUG + s->compressed_len = 0L; + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(s) + deflate_state *s; +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(s, tree, k) + deflate_state *s; + ct_data *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + const ct_data *stree = desc->stat_desc->static_tree; + const intf *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (bits + xbits); + if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if ((unsigned) tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((long)bits - (long)tree[m].Len) + *(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code, bl_count) + ct_data *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ + ushf *bl_count; /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; + const ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[node].Freq = 1; + s->depth[node] = 0; + s->opt_len--; if (stree) s->static_len -= stree[node].Len; + /* node is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? + s->depth[n] : s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree(s) + deflate_state *s; +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + s->opt_len, s->static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(s, lcodes, dcodes, blcodes) + deflate_state *s; + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last) + deflate_state *s; + charf *buf; /* input block */ + ulg stored_len; /* length of input block */ + int last; /* one if this is the last block for a file */ +{ + send_bits(s, (STORED_BLOCK<<1)+last, 3); /* send block type */ +#ifdef DEBUG + s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; + s->compressed_len += (stored_len + 4) << 3; +#endif + copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ +} + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + * The current inflate code requires 9 bits of lookahead. If the + * last two codes for the previous block (real code plus EOB) were coded + * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + * the last real code. In this case we send two empty static blocks instead + * of one. (There are no problems if the previous block is stored or fixed.) + * To simplify the code, we assume the worst case of last real code encoded + * on one bit only. + */ +void ZLIB_INTERNAL _tr_align(s) + deflate_state *s; +{ + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ +#endif + bi_flush(s); + /* Of the 10 bits for the empty block, we have already sent + * (10 - bi_valid) bits. The lookahead for the last real code (before + * the EOB of the previous block) was thus at least one plus the length + * of the EOB plus what we have just sent of the empty static block. + */ + if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; +#endif + bi_flush(s); + } + s->last_eob_len = 7; +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) + deflate_state *s; + charf *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int last; /* one if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s->level > 0) { + + /* Check if the file is binary or text */ + if (s->strm->data_type == Z_UNKNOWN) + s->strm->data_type = detect_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + + build_tree(s, (tree_desc *)(&(s->d_desc))); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + } else { + Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + +#ifdef FORCE_STORED + if (buf != (char*)0) { /* force stored block */ +#else + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, last); + +#ifdef FORCE_STATIC + } else if (static_lenb >= 0) { /* force static trees */ +#else + } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { +#endif + send_bits(s, (STATIC_TREES<<1)+last, 3); + compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->static_len; +#endif + } else { + send_bits(s, (DYN_TREES<<1)+last, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->opt_len; +#endif + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (last) { + bi_windup(s); +#ifdef DEBUG + s->compressed_len += 7; /* align on byte boundary */ +#endif + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + s->compressed_len-7*last)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int ZLIB_INTERNAL _tr_tally (s, dist, lc) + deflate_state *s; + unsigned dist; /* distance of matched string */ + unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + +#ifdef TRUNCATE_BLOCK + /* Try to guess if it is profitable to stop the current block here */ + if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)((long)s->strstart - s->block_start); + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + s->last_lit, in_length, out_length, + 100L - out_length*100L/in_length)); + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } +#endif + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(s, ltree, dtree) + deflate_state *s; + ct_data *ltree; /* literal tree */ + ct_data *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, + "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); + s->last_eob_len = ltree[END_BLOCK].Len; +} + +/* =========================================================================== + * Check if the data type is TEXT or BINARY, using the following algorithm: + * - TEXT if the two conditions below are satisfied: + * a) There are no non-portable control characters belonging to the + * "black list" (0..6, 14..25, 28..31). + * b) There is at least one printable character belonging to the + * "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). + * - BINARY otherwise. + * - The following partially-portable control characters form a + * "gray list" that is ignored in this detection algorithm: + * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). + * IN assertion: the fields Freq of dyn_ltree are set. + */ +local int detect_data_type(s) + deflate_state *s; +{ + /* black_mask is the bit mask of black-listed bytes + * set bits 0..6, 14..25, and 28..31 + * 0xf3ffc07f = binary 11110011111111111100000001111111 + */ + unsigned long black_mask = 0xf3ffc07fUL; + int n; + + /* Check for non-textual ("black-listed") bytes. */ + for (n = 0; n <= 31; n++, black_mask >>= 1) + if ((black_mask & 1) && (s->dyn_ltree[n].Freq != 0)) + return Z_BINARY; + + /* Check for textual ("white-listed") bytes. */ + if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0 + || s->dyn_ltree[13].Freq != 0) + return Z_TEXT; + for (n = 32; n < LITERALS; n++) + if (s->dyn_ltree[n].Freq != 0) + return Z_TEXT; + + /* There are no "black-listed" or "white-listed" bytes: + * this stream either is empty or has tolerated ("gray-listed") bytes only. + */ + return Z_BINARY; +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(s) + deflate_state *s; +{ + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(s) + deflate_state *s; +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +local void copy_block(s, buf, len, header) + deflate_state *s; + charf *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + s->last_eob_len = 8; /* enough lookahead for inflate */ + + if (header) { + put_short(s, (ush)len); + put_short(s, (ush)~len); +#ifdef DEBUG + s->bits_sent += 2*16; +#endif + } +#ifdef DEBUG + s->bits_sent += (ulg)len<<3; +#endif + while (len--) { + put_byte(s, *buf++); + } +} diff --git a/dep/StormLib/src/zlib/trees.h b/dep/StormLib/src/zlib/trees.h new file mode 100644 index 0000000000..d35639d82a --- /dev/null +++ b/dep/StormLib/src/zlib/trees.h @@ -0,0 +1,128 @@ +/* header created automatically with -DGEN_TREES_H */ + +local const ct_data static_ltree[L_CODES+2] = { +{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, +{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, +{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, +{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, +{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, +{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, +{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, +{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, +{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, +{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, +{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, +{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, +{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, +{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, +{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, +{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, +{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, +{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, +{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, +{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, +{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, +{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, +{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, +{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, +{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, +{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, +{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, +{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, +{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, +{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, +{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, +{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, +{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, +{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, +{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, +{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, +{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, +{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, +{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, +{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, +{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, +{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, +{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, +{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, +{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, +{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, +{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, +{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, +{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, +{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, +{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, +{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, +{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, +{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, +{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, +{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, +{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, +{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} +}; + +local const ct_data static_dtree[D_CODES] = { +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} +}; + +const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +}; + +const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +local const int base_length[LENGTH_CODES] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +}; + +local const int base_dist[D_CODES] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 +}; + diff --git a/dep/StormLib/src/zlib/zconf.h b/dep/StormLib/src/zlib/zconf.h new file mode 100644 index 0000000000..02ce56c431 --- /dev/null +++ b/dep/StormLib/src/zlib/zconf.h @@ -0,0 +1,428 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2010 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + * Even better than compiling with -DZ_PREFIX would be to use configure to set + * this permanently in zconf.h using "./configure --zprefix". + */ +#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ + +/* all linked symbols */ +# define _dist_code z__dist_code +# define _length_code z__length_code +# define _tr_align z__tr_align +# define _tr_flush_block z__tr_flush_block +# define _tr_init z__tr_init +# define _tr_stored_block z__tr_stored_block +# define _tr_tally z__tr_tally +# define adler32 z_adler32 +# define adler32_combine z_adler32_combine +# define adler32_combine64 z_adler32_combine64 +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define crc32 z_crc32 +# define crc32_combine z_crc32_combine +# define crc32_combine64 z_crc32_combine64 +# define deflate z_deflate +# define deflateBound z_deflateBound +# define deflateCopy z_deflateCopy +# define deflateEnd z_deflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateInit_ z_deflateInit_ +# define deflateParams z_deflateParams +# define deflatePrime z_deflatePrime +# define deflateReset z_deflateReset +# define deflateSetDictionary z_deflateSetDictionary +# define deflateSetHeader z_deflateSetHeader +# define deflateTune z_deflateTune +# define deflate_copyright z_deflate_copyright +# define get_crc_table z_get_crc_table +# define gz_error z_gz_error +# define gz_intmax z_gz_intmax +# define gz_strwinerror z_gz_strwinerror +# define gzbuffer z_gzbuffer +# define gzclearerr z_gzclearerr +# define gzclose z_gzclose +# define gzclose_r z_gzclose_r +# define gzclose_w z_gzclose_w +# define gzdirect z_gzdirect +# define gzdopen z_gzdopen +# define gzeof z_gzeof +# define gzerror z_gzerror +# define gzflush z_gzflush +# define gzgetc z_gzgetc +# define gzgets z_gzgets +# define gzoffset z_gzoffset +# define gzoffset64 z_gzoffset64 +# define gzopen z_gzopen +# define gzopen64 z_gzopen64 +# define gzprintf z_gzprintf +# define gzputc z_gzputc +# define gzputs z_gzputs +# define gzread z_gzread +# define gzrewind z_gzrewind +# define gzseek z_gzseek +# define gzseek64 z_gzseek64 +# define gzsetparams z_gzsetparams +# define gztell z_gztell +# define gztell64 z_gztell64 +# define gzungetc z_gzungetc +# define gzwrite z_gzwrite +# define inflate z_inflate +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define inflateBackInit_ z_inflateBackInit_ +# define inflateCopy z_inflateCopy +# define inflateEnd z_inflateEnd +# define inflateGetHeader z_inflateGetHeader +# define inflateInit2_ z_inflateInit2_ +# define inflateInit_ z_inflateInit_ +# define inflateMark z_inflateMark +# define inflatePrime z_inflatePrime +# define inflateReset z_inflateReset +# define inflateReset2 z_inflateReset2 +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateUndermine z_inflateUndermine +# define inflate_copyright z_inflate_copyright +# define inflate_fast z_inflate_fast +# define inflate_table z_inflate_table +# define uncompress z_uncompress +# define zError z_zError +# define zcalloc z_zcalloc +# define zcfree z_zcfree +# define zlibCompileFlags z_zlibCompileFlags +# define zlibVersion z_zlibVersion + +/* all zlib typedefs in zlib.h and zconf.h */ +# define Byte z_Byte +# define Bytef z_Bytef +# define alloc_func z_alloc_func +# define charf z_charf +# define free_func z_free_func +# define gzFile z_gzFile +# define gz_header z_gz_header +# define gz_headerp z_gz_headerp +# define in_func z_in_func +# define intf z_intf +# define out_func z_out_func +# define uInt z_uInt +# define uIntf z_uIntf +# define uLong z_uLong +# define uLongf z_uLongf +# define voidp z_voidp +# define voidpc z_voidpc +# define voidpf z_voidpf + +/* all zlib structs in zlib.h and zconf.h */ +# define gz_header_s z_gz_header_s +# define internal_state z_internal_state + +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_UNISTD_H +#endif + +#ifdef STDC +# include /* for off_t */ +#endif + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# include /* for SEEK_* and off_t */ +# ifdef VMS +# include /* for off_t */ +# endif +# ifndef z_off_t +# define z_off_t off_t +# endif +#endif + +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 +# define z_off64_t off64_t +#else +# define z_off64_t z_off_t +#endif + +#if defined(__OS400__) +# define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) + #pragma map(deflateInit_,"DEIN") + #pragma map(deflateInit2_,"DEIN2") + #pragma map(deflateEnd,"DEEND") + #pragma map(deflateBound,"DEBND") + #pragma map(inflateInit_,"ININ") + #pragma map(inflateInit2_,"ININ2") + #pragma map(inflateEnd,"INEND") + #pragma map(inflateSync,"INSY") + #pragma map(inflateSetDictionary,"INSEDI") + #pragma map(compressBound,"CMBND") + #pragma map(inflate_table,"INTABL") + #pragma map(inflate_fast,"INFA") + #pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/dep/StormLib/src/zlib/zlib.h b/dep/StormLib/src/zlib/zlib.h new file mode 100644 index 0000000000..bfbba83e8e --- /dev/null +++ b/dep/StormLib/src/zlib/zlib.h @@ -0,0 +1,1613 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.5, April 19th, 2010 + + Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.5" +#define ZLIB_VERNUM 0x1250 +#define ZLIB_VER_MAJOR 1 +#define ZLIB_VER_MINOR 2 +#define ZLIB_VER_REVISION 5 +#define ZLIB_VER_SUBREVISION 0 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed data. + This version of the library supports only one compression method (deflation) + but other algorithms will be added later and will have the same stream + interface. + + Compression can be done in a single step if the buffers are large enough, + or can be done by repeated calls of the compression function. In the latter + case, the application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip streams in memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never crash + even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has dropped + to zero. It must update next_out and avail_out when avail_out has dropped + to zero. The application must initialize zalloc, zfree and opaque before + calling the init function. All other fields are set by the compression + library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this if + the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers + returned by zalloc for objects of exactly 65536 bytes *must* have their + offset normalized to zero. The default allocation function provided by this + library ensures this (see zutil.c). To reduce memory requirements and avoid + any allocation of 64K objects, at the expense of compression ratio, compile + the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or progress + reports. After compression, total_in holds the total size of the + uncompressed data and may be saved for use in the decompressor (particularly + if the decompressor wants to decompress everything in a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +#define Z_TREES 6 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is not + compatible with the zlib.h header file used by the application. This check + is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. If + zalloc and zfree are set to Z_NULL, deflateInit updates them to use default + allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at all + (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION + requests a default compromise between speed and compression (currently + equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if level is not a valid compression level, or + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). msg is set to null + if there is no error message. deflateInit does not perform any compression: + this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). Some + output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating avail_in or avail_out accordingly; avail_out should + never be zero before the call. The application can consume the compressed + output when it wants, for example when the output buffer is full (avail_out + == 0), or after each call of deflate(). If deflate returns Z_OK and with + zero avail_out, it must be called again after making room in the output + buffer because there might be more output pending. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumulate before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In + particular avail_in is zero after the call if enough output space has been + provided before the call.) Flushing may degrade compression for some + compression algorithms and so it should be used only when necessary. This + completes the current deflate block and follows it with an empty stored block + that is three bits plus filler bits to the next byte, followed by four bytes + (00 00 ff ff). + + If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the + output buffer, but the output is not aligned to a byte boundary. All of the + input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. + This completes the current deflate block and follows it with an empty fixed + codes block that is 10 bits long. This assures that enough bytes are output + in order for the decompressor to finish the block before the empty fixed code + block. + + If flush is set to Z_BLOCK, a deflate block is completed and emitted, as + for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to + seven bits of the current block are held to be written as the next byte after + the next deflate block is completed. In this case, the decompressor may not + be provided enough bits at this point in order to complete decompression of + the data provided so far to the compressor. It may need to wait for the next + block to be emitted. This is for advanced applications that need to control + the emission of deflate blocks. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there was + enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the stream + are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least the + value returned by deflateBound (see below). If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect the + compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was Z_NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, msg + may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the + exact value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit() does not process any header information -- that is deferred + until inflate() is called. +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing will + resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there is + no more input data or no more space in the output buffer (see below about + the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating the next_* and avail_* values accordingly. The + application can consume the uncompressed output when it wants, for example + when the output buffer is full (avail_out == 0), or after each call of + inflate(). If inflate returns Z_OK and with zero avail_out, it must be + called again after making room in the output buffer because there might be + more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, + Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() + stop if and when it gets to the next deflate block boundary. When decoding + the zlib or gzip format, this will cause inflate() to return immediately + after the header and before the first block. When doing a raw inflate, + inflate() will go ahead and process the first block, and will return when it + gets to the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 if + inflate() is currently decoding the last block in the deflate stream, plus + 128 if inflate() returned immediately after decoding an end-of-block code or + decoding the complete header up to just before the first byte of the deflate + stream. The end-of-block will not be indicated until all of the uncompressed + data from that block has been written to strm->next_out. The number of + unused bits may in general be greater than seven, except when bit 7 of + data_type is set, in which case the number of unused bits will be less than + eight. data_type is set as noted here every time inflate() returns for all + flush options, and so can be used to determine the amount of currently + consumed input in bits. + + The Z_TREES option behaves as Z_BLOCK does, but it also returns when the + end of each deflate block header is reached, before any actual data in that + block is decoded. This allows the caller to determine the length of the + deflate block header for later use in random access within a deflate block. + 256 is added to the value of strm->data_type when inflate() returns + immediately after reaching the end of the deflate block header. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step (a + single call of inflate), the parameter flush should be set to Z_FINISH. In + this case all pending input is processed and all pending output is flushed; + avail_out must be large enough to hold all the uncompressed data. (The size + of the uncompressed data may have been saved by the compressor for this + purpose.) The next operation on this stream must be inflateEnd to deallocate + the decompression state. The use of Z_FINISH is never required, but can be + used to inform inflate that a faster approach may be used for the single + inflate() call. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the only effect of the flush parameter in this implementation + is on the return value of inflate(), as noted below, or when it returns early + because Z_BLOCK or Z_TREES is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the adler32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the adler32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() can decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically, if requested when + initializing with inflateInit2(). Any information contained in the gzip + header is not retained, so applications that need that information should + instead use raw inflate, see inflateInit2() below, or inflateBack() and + perform their own processing of the gzip header and trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + next_in or next_out was Z_NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may + then call inflateSync() to look for a good compression block if a partial + recovery of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by the + caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), no + header crc, and the operating system will be set to 255 (unknown). If a + gzip stream is being written, strm->adler is a crc32 instead of an adler32. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but is + slow and reduces compression ratio; memLevel=9 uses maximum memory for + optimal speed. The default value is 8. See zconf.h for total memory usage + as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as + fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The + strategy parameter only affects the compression ratio but not the + correctness of the compressed output even if it is not set appropriately. + Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler + decoder for special applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid + method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is + incompatible with the version assumed by the caller (ZLIB_VERSION). msg is + set to null if there is no error message. deflateInit2 does not perform any + compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any call + of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size + provided in deflateInit or deflateInit2. Thus the strings most likely to be + useful should be put at the end of the dictionary, not at the front. In + addition, the current implementation of deflate will use at most the window + size minus 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and can + consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. The + stream will keep the same compression level and any other attributes that + may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different strategy. + If the compression level is changed, the input available so far is + compressed with the old level (and may be flushed); the new level will take + effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to be + compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR if + strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() or + deflateInit2(), and after deflateSetHeader(), if used. This would be used + to allocate an output buffer for deflation in a single pass, and so would be + called before deflate(). +*/ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the bits + leftover from a previous deflate stream when appending to it. As such, this + function can only be used for raw deflate, and must be used before the first + deflate() call after a deflateInit2() or deflateReset(). bits must be less + than or equal to 16, and that many of the least significant bits of value + will be inserted in the output. + + deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be zero to request that inflate use the window size in + the zlib header of the compressed stream. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a + crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit2 does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit2() does not process any header information -- that is + deferred until inflate() is called. +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called + immediately after inflateInit2() or inflateReset() and before any call of + inflate() to set the dictionary. The application must insure that the + dictionary that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been + found, or Z_STREAM_ERROR if the stream structure was inconsistent. In the + success case, the application may save the current current value of total_in + which indicates where valid compressed data was found. In the error case, + the application may repeatedly call inflateSync, providing more input each + time, until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. The + stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, + int windowBits)); +/* + This function is the same as inflateReset, but it also permits changing + the wrap and window size requests. The windowBits parameter is interpreted + the same as it is for inflateInit2. + + inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL), or if + the windowBits parameter is invalid. +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + If bits is negative, then the input stream bit buffer is emptied. Then + inflatePrime() can be called again to put bits in the buffer. This is used + to clear out bits leftover after feeding inflate a block description prior + to feeding inflate codes. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); +/* + This function returns two values, one in the lower 16 bits of the return + value, and the other in the remaining upper bits, obtained by shifting the + return value down 16 bits. If the upper value is -1 and the lower value is + zero, then inflate() is currently decoding information outside of a block. + If the upper value is -1 and the lower value is non-zero, then inflate is in + the middle of a stored block, with the lower value equaling the number of + bytes from the input remaining to copy. If the upper value is not -1, then + it is the number of bits back from the current bit position in the input of + the code (literal or length/distance pair) currently being processed. In + that case the lower value is the number of bytes already emitted for that + code. + + A code is being processed if inflate is waiting for more input to complete + decoding of the code, or if it has completed decoding but is waiting for + more output space to write the literal or match data. + + inflateMark() is used to mark locations in the input data for random + access, which may be at bit positions, and to note those cases where the + output of a code may span boundaries of random access blocks. The current + location in the input stream can be determined from avail_in and data_type + as noted in the description for the Z_BLOCK flush parameter for inflate. + + inflateMark returns the value noted above or -1 << 16 if the provided + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be + used to force inflate() to return immediately after header processing is + complete and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When any + of extra, name, or comment are not Z_NULL and the respective field is not + present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the paramaters are invalid, Z_MEM_ERROR if the internal state could not be + allocated, or Z_VERSION_ERROR if the version of the library does not match + the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is more efficient than inflate() for + file i/o applications in that it avoids copying between the output and the + sliding window by simply making the window itself the output buffer. This + function trusts the application to not change the output buffer passed by + the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free the + allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects only + the raw deflate stream to decompress. This is different from the normal + behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format error + in the deflate stream (in which case strm->msg is set to indicate the nature + of the error), or Z_STREAM_ERROR if the stream was not properly initialized. + In the case of Z_BUF_ERROR, an input or output error can be distinguished + using strm->next_in which will be Z_NULL only if in() returned an error. If + strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning + non-zero. (in() will always be called before out(), so strm->next_in is + assured to be defined if out() returns non-zero.) Note that inflateBack() + cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + + + /* utility functions */ + +/* + The following utility functions are implemented on top of the basic + stream-oriented functions. To simplify the interface, some default options + are assumed (compression level and memory usage, standard memory allocation + functions). The source code of these utility functions can be modified if + you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before a + compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be large enough to hold the entire + uncompressed data. (The size of the uncompressed data must have been saved + previously by the compressor and transmitted to the decompressor by some + mechanism outside the scope of this compression library.) Upon exit, destLen + is the actual size of the uncompressed buffer. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. +*/ + + + /* gzip file access functions */ + +/* + This library supports reading and writing files in gzip (.gz) format with + an interface similar to that of stdio, using the functions that start with + "gz". The gzip format is different from the zlib format. gzip is a gzip + wrapper, documented in RFC 1952, wrapped around a deflate stream. +*/ + +typedef voidp gzFile; /* opaque gzip file descriptor */ + +/* +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); + + Opens a gzip (.gz) file for reading or writing. The mode parameter is as + in fopen ("rb" or "wb") but can also include a compression level ("wb9") or + a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only + compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' + for fixed code compression as in "wb9F". (See the description of + deflateInit2 for more information about the strategy parameter.) Also "a" + can be used instead of "w" to request that the gzip stream that will be + written be appended to the file. "+" will result in an error, since reading + and writing to the same gzip file is not supported. + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened, if there was + insufficient memory to allocate the gzFile state, or if an invalid mode was + specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). + errno can be checked to determine if the reason gzopen failed was that the + file could not be opened. +*/ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen associates a gzFile with the file descriptor fd. File descriptors + are obtained from calls like open, dup, creat, pipe or fileno (if the file + has been previously opened with fopen). The mode parameter is as in gzopen. + + The next call of gzclose on the returned gzFile will also close the file + descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor + fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, + mode);. The duplicated descriptor should be saved to avoid a leak, since + gzdopen does not close fd if it fails. + + gzdopen returns NULL if there was insufficient memory to allocate the + gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not + provided, or '+' was provided), or if fd is -1. The file descriptor is not + used until the next gz* read, write, seek, or close operation, so gzdopen + will not detect if fd is invalid (unless fd is -1). +*/ + +ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); +/* + Set the internal buffer size used by this library's functions. The + default buffer size is 8192 bytes. This function must be called after + gzopen() or gzdopen(), and before any other calls that read or write the + file. The buffer memory allocation is always deferred to the first read or + write. Two buffers are allocated, either both of the specified size when + writing, or one of the specified size and the other twice that size when + reading. A larger buffer size of, for example, 64K or 128K bytes will + noticeably increase the speed of decompression (reading). + + The new buffer size also affects the maximum length for gzprintf(). + + gzbuffer() returns 0 on success, or -1 on failure, such as being called + too late. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. If + the input file was not in gzip format, gzread copies the given number of + bytes into the buffer. + + After reaching the end of a gzip stream in the input, gzread will continue + to read, looking for another gzip stream, or failing that, reading the rest + of the input file directly without decompression. The entire input file + will be read if gzread is called until it returns less than the requested + len. + + gzread returns the number of uncompressed bytes actually read, less than + len for end of file, or -1 for error. +*/ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes written or 0 in case of + error. +*/ + +ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the arguments to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written, or 0 in case of error. The number of + uncompressed bytes written is limited to 8191, or one less than the buffer + size given to gzbuffer(). The caller should assure that this limit is not + exceeded. If it is exceeded, then gzprintf() will return an error (0) with + nothing written. In this case, there may also be a buffer overflow with + unpredictable consequences, which is possible only if zlib was compiled with + the insecure functions sprintf() or vsprintf() because the secure snprintf() + or vsnprintf() functions were not available. This can be determined using + zlibCompileFlags(). +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or a + newline character is read and transferred to buf, or an end-of-file + condition is encountered. If any characters are read or if len == 1, the + string is terminated with a null character. If no characters are read due + to an end-of-file or len < 1, then the buffer is left untouched. + + gzgets returns buf which is a null-terminated string, or it returns NULL + for end-of-file or in case of error. If there was an error, the contents at + buf are indeterminate. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. gzputc + returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte or -1 + in case of end of file or error. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read as the first character + on the next read. At least one character of push-back is allowed. + gzungetc() returns the character pushed, or -1 on failure. gzungetc() will + fail if c is -1, and may fail if a character has been pushed but not read + yet. If gzungetc is used immediately after gzopen or gzdopen, at least the + output buffer size of pushed characters is allowed. (See gzbuffer above.) + The pushed character will be discarded if the stream is repositioned with + gzseek() or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter flush + is as in the deflate() function. The return value is the zlib error number + (see function gzerror below). gzflush is only permitted when writing. + + If the flush parameter is Z_FINISH, the remaining data is written and the + gzip stream is completed in the output. If gzwrite() is called again, a new + gzip stream will be started in the output. gzread() is able to read such + concatented gzip streams. + + gzflush should be called only when strictly necessary because it will + degrade compression if called too often. +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); + + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); + + Returns the starting position for the next gzread or gzwrite on the given + compressed file. This position represents a number of bytes in the + uncompressed data stream, and is zero when starting, even if appending or + reading a gzip stream from the middle of a file using gzdopen(). + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); + + Returns the current offset in the file being read or written. This offset + includes the count of bytes that precede the gzip stream, for example when + appending or when using gzdopen() for reading. When reading, the offset + does not include as yet unused buffered input. This information can be used + for a progress indicator. On error, gzoffset() returns -1. +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns true (1) if the end-of-file indicator has been set while reading, + false (0) otherwise. Note that the end-of-file indicator is set only if the + read tried to go past the end of the input, but came up short. Therefore, + just like feof(), gzeof() may return false even if there is no more data to + read, in the event that the last read request was for the exact number of + bytes remaining in the input file. This will happen if the input file size + is an exact multiple of the buffer size. + + If gzeof() returns true, then the read functions will return no more data, + unless the end-of-file indicator is reset by gzclearerr() and the input file + has grown since the previous end of file was detected. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns true (1) if file is being copied directly while reading, or false + (0) if file is a gzip stream being decompressed. This state can change from + false to true while reading the input file if the end of a gzip stream is + reached, but is followed by data that is not another gzip stream. + + If the input file is empty, gzdirect() will return true, since the input + does not contain a gzip stream. + + If gzdirect() is used immediately after gzopen() or gzdopen() it will + cause buffers to be allocated to allow reading the file to determine if it + is a gzip file. Therefore if gzbuffer() is used, it should be called before + gzdirect(). +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file and + deallocates the (de)compression state. Note that once file is closed, you + cannot call gzerror with file, since its structures have been deallocated. + gzclose must not be called more than once on the same file, just as free + must not be called more than once on the same allocation. + + gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a + file operation error, or Z_OK on success. +*/ + +ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); +ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); +/* + Same as gzclose(), but gzclose_r() is only for use when reading, and + gzclose_w() is only for use when writing or appending. The advantage to + using these instead of gzclose() is that they avoid linking in zlib + compression or decompression code that is not used when only reading or only + writing respectively. If gzclose() is used, then both compression and + decompression code will be included the application when linking to a static + zlib library. +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the given + compressed file. errnum is set to zlib error number. If an error occurred + in the file system and not in the compression library, errnum is set to + Z_ERRNO and the application may consult errno to get the exact error code. + + The application must not modify the returned string. Future calls to + this function may invalidate the previously returned string. If file is + closed, then the string previously returned by gzerror will no longer be + available. + + gzerror() should be used to distinguish errors from end-of-file for those + functions above that do not distinguish those cases in their return values. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the compression + library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is Z_NULL, this function returns the + required initial value for the checksum. + + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. + + Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +/* +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); + + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is Z_NULL, this function returns the required + initial value for the for the crc. Pre- and post-conditioning (one's + complement) is performed within this function so it shouldn't be done by the + application. + + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +/* +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, sizeof(z_stream)) + +/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or + * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if + * both are true, the application gets the *64 functions, and the regular + * functions are changed to 64 bits) -- in case these are set on systems + * without large file support, _LFS64_LARGEFILE must also be true + */ +#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); +#endif + +#if !defined(ZLIB_INTERNAL) && _FILE_OFFSET_BITS-0 == 64 && _LFS64_LARGEFILE-0 +# define gzopen gzopen64 +# define gzseek gzseek64 +# define gztell gztell64 +# define gzoffset gzoffset64 +# define adler32_combine adler32_combine64 +# define crc32_combine crc32_combine64 +# ifdef _LARGEFILE64_SOURCE + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +# endif +#else + ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); +#endif + +/* hack for buggy compilers */ +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; +#endif + +/* undocumented functions */ +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); +ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); +ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/dep/StormLib/src/zlib/zutil.c b/dep/StormLib/src/zlib/zutil.c new file mode 100644 index 0000000000..898ed345b0 --- /dev/null +++ b/dep/StormLib/src/zlib/zutil.c @@ -0,0 +1,318 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2005, 2010 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zutil.h" + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +const char * const z_errmsg[10] = { +"need dictionary", /* Z_NEED_DICT 2 */ +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + + +const char * ZEXPORT zlibVersion() +{ + return ZLIB_VERSION; +} + +uLong ZEXPORT zlibCompileFlags() +{ + uLong flags; + + flags = 0; + switch ((int)(sizeof(uInt))) { + case 2: break; + case 4: flags += 1; break; + case 8: flags += 2; break; + default: flags += 3; + } + switch ((int)(sizeof(uLong))) { + case 2: break; + case 4: flags += 1 << 2; break; + case 8: flags += 2 << 2; break; + default: flags += 3 << 2; + } + switch ((int)(sizeof(voidpf))) { + case 2: break; + case 4: flags += 1 << 4; break; + case 8: flags += 2 << 4; break; + default: flags += 3 << 4; + } + switch ((int)(sizeof(z_off_t))) { + case 2: break; + case 4: flags += 1 << 6; break; + case 8: flags += 2 << 6; break; + default: flags += 3 << 6; + } +#ifdef DEBUG + flags += 1 << 8; +#endif +#if defined(ASMV) || defined(ASMINF) + flags += 1 << 9; +#endif +#ifdef ZLIB_WINAPI + flags += 1 << 10; +#endif +#ifdef BUILDFIXED + flags += 1 << 12; +#endif +#ifdef DYNAMIC_CRC_TABLE + flags += 1 << 13; +#endif +#ifdef NO_GZCOMPRESS + flags += 1L << 16; +#endif +#ifdef NO_GZIP + flags += 1L << 17; +#endif +#ifdef PKZIP_BUG_WORKAROUND + flags += 1L << 20; +#endif +#ifdef FASTEST + flags += 1L << 21; +#endif +#ifdef STDC +# ifdef NO_vsnprintf + flags += 1L << 25; +# ifdef HAS_vsprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_vsnprintf_void + flags += 1L << 26; +# endif +# endif +#else + flags += 1L << 24; +# ifdef NO_snprintf + flags += 1L << 25; +# ifdef HAS_sprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_snprintf_void + flags += 1L << 26; +# endif +# endif +#endif + return flags; +} + +#ifdef DEBUG + +# ifndef verbose +# define verbose 0 +# endif +int ZLIB_INTERNAL z_verbose = verbose; + +void ZLIB_INTERNAL z_error (m) + char *m; +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(err) + int err; +{ + return ERR_MSG(err); +} + +#if defined(_WIN32_WCE) + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. + */ + int errno = 0; +#endif + +#ifndef HAVE_MEMCPY + +void ZLIB_INTERNAL zmemcpy(dest, source, len) + Bytef* dest; + const Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int ZLIB_INTERNAL zmemcmp(s1, s2, len) + const Bytef* s1; + const Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void ZLIB_INTERNAL zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + + +#ifdef SYS16BIT + +#ifdef __TURBOC__ +/* Turbo C in 16-bit mode */ + +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf = opaque; /* just to make some compilers happy */ + ulg bsize = (ulg)items*size; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) +{ + int n; + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + ptr = opaque; /* just to make some compilers happy */ + Assert(0, "zcfree: ptr not found"); +} + +#endif /* __TURBOC__ */ + + +#ifdef M_I86 +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, uInt items, uInt size) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + return _halloc((long)items, size); +} + +void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + _hfree(ptr); +} + +#endif /* M_I86 */ + +#endif /* SYS16BIT */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf ZLIB_INTERNAL zcalloc (opaque, items, size) + voidpf opaque; + unsigned items; + unsigned size; +{ + if (opaque) items += size - size; /* make compiler happy */ + return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : + (voidpf)calloc(items, size); +} + +void ZLIB_INTERNAL zcfree (opaque, ptr) + voidpf opaque; + voidpf ptr; +{ + free(ptr); + if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ diff --git a/dep/StormLib/src/zlib/zutil.h b/dep/StormLib/src/zlib/zutil.h new file mode 100644 index 0000000000..258fa88799 --- /dev/null +++ b/dep/StormLib/src/zlib/zutil.h @@ -0,0 +1,274 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2010 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#if ((__GNUC__-0) * 10 + __GNUC_MINOR__-0 >= 33) && !defined(NO_VIZ) +# define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) +#else +# define ZLIB_INTERNAL +#endif + +#include "zlib.h" + +#ifdef STDC +# if !(defined(_WIN32_WCE) && defined(_MSC_VER)) +# include +# endif +# include +# include +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +# define OS_CODE 0x00 +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +# ifdef M_I86 +# include +# endif +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#ifdef WIN32 +# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ +# define OS_CODE 0x0b +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0f +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + +#if defined(__BORLANDC__) + #pragma warn -8004 + #pragma warn -8008 + #pragma warn -8066 +#endif + +/* provide prototypes for these when building zlib without LFS */ +#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0 + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS + /* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 + /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 ) +# define vsnprintf _vsnprintf +# endif +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +#endif +#ifdef VMS +# define NO_vsnprintf +#endif + +#if defined(pyr) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include + extern int ZLIB_INTERNAL z_verbose; + extern void ZLIB_INTERNAL z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items, + unsigned size)); +void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr)); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#endif /* ZUTIL_H */ diff --git a/dep/StormLib/storm_dll/storm_dll.cpp b/dep/StormLib/storm_dll/storm.cpp similarity index 99% rename from dep/StormLib/storm_dll/storm_dll.cpp rename to dep/StormLib/storm_dll/storm.cpp index 2941f2a37f..814f7c8ad3 100644 --- a/dep/StormLib/storm_dll/storm_dll.cpp +++ b/dep/StormLib/storm_dll/storm.cpp @@ -12,7 +12,7 @@ #define BUILDING_STORM_CPP #define STORM_ALTERNATE_NAMES -#include "storm_dll.h" +#include "storm.h" BOOL WINAPI SFILE(OpenArchive)(LPCSTR lpFileName, DWORD dwPriority, DWORD dwFlags, HANDLE *hMPQ) { diff --git a/dep/StormLib/storm_dll/storm_dll.def b/dep/StormLib/storm_dll/storm.def similarity index 94% rename from dep/StormLib/storm_dll/storm_dll.def rename to dep/StormLib/storm_dll/storm.def index 8de88f5db3..2b8b08c422 100644 --- a/dep/StormLib/storm_dll/storm_dll.def +++ b/dep/StormLib/storm_dll/storm.def @@ -18,8 +18,7 @@ EXPORTS StormGetArchiveName @275 ; 0x113 StormGetFileName @276 ; 0x114 -; StormSetLastError @465 ; 0x +; StormSetLastError @465 ; 0x1d1 StormCompress @551 ; 0x227 StormDecompress @552 ; 0x228 - \ No newline at end of file diff --git a/dep/StormLib/storm_dll/storm_dll.h b/dep/StormLib/storm_dll/storm.h similarity index 81% rename from dep/StormLib/storm_dll/storm_dll.h rename to dep/StormLib/storm_dll/storm.h index 6d67820a22..9dafffd00e 100644 --- a/dep/StormLib/storm_dll/storm_dll.h +++ b/dep/StormLib/storm_dll/storm.h @@ -13,24 +13,25 @@ /* 11.04.03 1.00 Lad Added some functions */ /*****************************************************************************/ -// We need the Windows data types for the Storm prototypes -#include - #ifndef __STORM_H__ #define __STORM_H__ -// Somethimes is necessary to change the function names so they +// We need the Windows data types for the Storm prototypes +#include + +// Sometimes is necessary to change the function names so they // will not conflict with other MPQ tools. #ifdef STORM_ALTERNATE_NAMES #define SFILE(Name) Storm##Name + #define SMEM(Name) Storm##Name #define SCOMP(Name) Storm##Name #else #define SFILE(Name) SFile##Name + #define SMEM(Name) SMem##Name #define SCOMP(Name) SComp##Name #endif - -// Just in case anyone is still using C out there +// Make sure the functions are exported as C functions #ifdef __cplusplus extern "C" { #endif @@ -46,22 +47,25 @@ DWORD WINAPI SFILE(GetFileSize)(HANDLE hFile, LPDWORD lpFileSizeHigh); BOOL WINAPI SFILE(GetFileArchive)(HANDLE hFile, HANDLE *hMPQ); BOOL WINAPI SFILE(GetFileName)(HANDLE hFile, LPCSTR lpBuffer, DWORD dwBufferLength); DWORD WINAPI SFILE(SetFilePointer)(HANDLE hFile, long lDistanceToMove, PLONG lplDistanceToMoveHigh, DWORD dwMoveMethod); -BOOL WINAPI SFILE(ReadFile)(HANDLE hFile,LPVOID lpBuffer,DWORD nNumberOfBytesToRead,LPDWORD lpNumberOfBytesRead,LPOVERLAPPED lpOverlapped); +BOOL WINAPI SFILE(ReadFile)(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped); LCID WINAPI SFILE(SetLocale)(LCID nNewLocale); BOOL WINAPI SFILE(GetBasePath)(LPCSTR lpBuffer, DWORD dwBufferLength); BOOL WINAPI SFILE(SetBasePath)(LPCSTR lpNewBasePath); +// File name safe functions +BOOL WINAPI SMEM(UTF8ToFileName)(LPTSTR szBuffer, size_t ccBuffer, const void * lpString, const void * lpStringEnd, DWORD dwFlags, size_t * pOutLength); +BOOL WINAPI SMEM(FileNameToUTF8)(void * lpBuffer, size_t ccBuffer, const TCHAR * szString, const TCHAR * szStringEnd, DWORD dwFlags, size_t * pOutLength); + // Storm (de)compression functions BOOL WINAPI SCOMP(Compress) (char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength, int uCmp, int uCmpType, int nCmpLevel); BOOL WINAPI SCOMP(Decompress)(char * pbOutBuffer, int * pdwOutLength, char * pbInBuffer, int dwInLength); +#ifdef __cplusplus +} +#endif #if defined(_MSC_VER) && !defined(BUILDING_STORM_CPP) #pragma comment(lib, "Storm.lib") // Force linking Storm.lib and thus Storm.dll #endif -#ifdef __cplusplus -} -#endif - #endif // __STORM_H__ diff --git a/dep/StormLib/storm_dll/storm.vcxproj b/dep/StormLib/storm_dll/storm.vcxproj new file mode 100644 index 0000000000..a8d4849f54 --- /dev/null +++ b/dep/StormLib/storm_dll/storm.vcxproj @@ -0,0 +1,209 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {BD600973-C6FA-4CE3-8821-67F6418B7F9C} + storm + Win32Proj + 10.0.17134.0 + + + + DynamicLibrary + v143 + Unicode + true + + + DynamicLibrary + v143 + Unicode + + + DynamicLibrary + v143 + Unicode + true + + + DynamicLibrary + v143 + Unicode + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>16.0.31829.152 + + + .\bin\dll\$(Platform)\$(Configuration)\ + .\bin\dll\$(Platform)\$(Configuration)\ + true + false + + + .\bin\dll\$(Platform)\$(Configuration)\ + .\bin\dll\$(Platform)\$(Configuration)\ + true + false + + + .\bin\dll\$(Platform)\$(Configuration)\ + .\bin\dll\$(Platform)\$(Configuration)\ + false + false + + + .\bin\dll\$(Platform)\$(Configuration)\ + .\bin\dll\$(Platform)\$(Configuration)\ + false + false + + + + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;storm_EXPORTS;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreaded + + Level3 + EditAndContinue + + + $(OutDir)storm.dll + .\storm.def + true + Windows + 0x15000000 + + MachineX86 + false + + + copy .\bin\dll\$(Platform)\$(Configuration)\storm.lib .\storm.lib + + + + + X64 + + + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;storm_EXPORTS;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + Level3 + ProgramDatabase + + + $(OutDir)storm.dll + .\storm.def + true + Windows + + + + MachineX64 + false + + + copy .\bin\dll\$(Platform)\$(Configuration)\storm.lib .\storm.lib + + + + + WIN32;NDEBUG;_WINDOWS;_USRDLL;storm_EXPORTS;%(PreprocessorDefinitions) + MultiThreaded + + Level3 + ProgramDatabase + + + $(OutDir)storm.dll + .\storm.def + true + Windows + true + true + 0x15000000 + + MachineX86 + false + + + copy .\bin\dll\$(Platform)\$(Configuration)\storm.lib .\storm.lib + + + + + X64 + + + WIN32;NDEBUG;_WINDOWS;_USRDLL;storm_EXPORTS;%(PreprocessorDefinitions) + MultiThreadedDLL + + Level3 + ProgramDatabase + + + $(OutDir)storm.dll + .\storm.def + true + Windows + true + true + + + + MachineX64 + false + + + copy .\bin\dll\$(Platform)\$(Configuration)\storm.lib .\storm.lib + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dep/StormLib/storm_dll/storm.vcxproj.filters b/dep/StormLib/storm_dll/storm.vcxproj.filters new file mode 100644 index 0000000000..c10e805f07 --- /dev/null +++ b/dep/StormLib/storm_dll/storm.vcxproj.filters @@ -0,0 +1,28 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + + + Source Files + + + + + Header Files + + + \ No newline at end of file diff --git a/dep/StormLib/storm_dll/storm_test.cpp b/dep/StormLib/storm_dll/storm_test.cpp new file mode 100644 index 0000000000..58417f3fdc --- /dev/null +++ b/dep/StormLib/storm_dll/storm_test.cpp @@ -0,0 +1,183 @@ +/*****************************************************************************/ +/* Storm_test.cpp Copyright (c) Ladislav Zezula 2014 */ +/*---------------------------------------------------------------------------*/ +/* Test module for storm.dll (original Blizzard MPQ dynalic library */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 24.08.14 1.00 Lad The first version of Storm_test.cpp */ +/*****************************************************************************/ + +#define _CRT_NON_CONFORMING_SWPRINTFS +#define _CRT_SECURE_NO_DEPRECATE +#include +#include +#include + +#ifdef _MSC_VER +#include +#endif + +#define STORM_ALTERNATE_NAMES // Use Storm* prefix for functions +#include "storm.h" // Header file for Storm.dll + +#pragma comment(lib, "storm.lib") + +//----------------------------------------------------------------------------- +// List of files + +const char * IntToHexChar = "0123456789ABCDEF"; + +LPCSTR DefFilesToOpen[] = +{ + "music\\tdefeat.wav", + "setupdat\\inst.vis", + "setupdat\\audio\\mouseover.wav", + "files\\font\\font12.fnt", + "music\\zvict.wav", + "files\\readme.txt", + "music\\zerg2.wav", + "music\\zerg1.wav", + "setupdat\\inst_regs.ins", + "files\\font\\font50.fnt", + "files\\msv2all.vxp", + "setupdat\\scr_options.vis", + "setupdat\\font\\font16x.fnt", + "setupdat\\setup.vis", + "maps\\128x128_wasteland4.scm", + "setupdat\\scr_main.vis", + "music\\pvict.wav", + "music\\zdefeat.wav", +}; + +//----------------------------------------------------------------------------- +// Main + +static void CalculateMD5(LPBYTE md5_digest, LPBYTE pbData, DWORD cbData) +{ + HCRYPTPROV hCryptProv = NULL; + HCRYPTHASH hCryptHash = NULL; + + if(CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) + { + if(CryptCreateHash(hCryptProv, CALG_MD5, NULL, 0, &hCryptHash)) + { + DWORD dwHashLen = 0x10; + + CryptHashData(hCryptHash, pbData, cbData, 0); + CryptGetHashParam(hCryptHash, HP_HASHVAL, md5_digest, &dwHashLen, 0); + CryptDestroyHash(hCryptHash); + } + + CryptReleaseContext(hCryptProv, 0); + } +} + +template +DWORD BinaryToString(XCHAR * szBuffer, size_t cchBuffer, LPCVOID pvBinary, size_t cbBinary) +{ + LPCBYTE pbBinary = (LPCBYTE)pvBinary; + + // The size of the string must be enough to hold the binary + EOS + if(cchBuffer < ((cbBinary * 2) + 1)) + return ERROR_INSUFFICIENT_BUFFER; + + // Convert the string to the array of MD5 + // Copy the blob data as text + for(size_t i = 0; i < cbBinary; i++) + { + *szBuffer++ = IntToHexChar[pbBinary[0] >> 0x04]; + *szBuffer++ = IntToHexChar[pbBinary[0] & 0x0F]; + pbBinary++; + } + + // Terminate the string + *szBuffer = 0; + return ERROR_SUCCESS; +} + +int main(int argc, char * argv[]) +{ + LPCSTR szArchiveName; + LPCSTR szFormat; + HANDLE hMpq = NULL; + HANDLE hFile = NULL; + LPBYTE pbBuffer = NULL; + DWORD dwBytesRead = 0; + DWORD dwFileSize = 0; + BOOL bResult; + BYTE md5_digest[0x10]; + char md5_string[0x40]; + + // Check parameters + if(argc == 1) + { + printf("Error: Missing MPQ name\nUsage: storm_test.exe MpqName [FileName [FileName]]\n"); + return 3; + } + + // Get both arguments + SetLastError(ERROR_SUCCESS); + szArchiveName = argv[1]; + + // Break for kernel debugger + //__debugbreak(); + + // Put Storm.dll to the current folder before running this + //printf("[*] Opening archive '%s' ...\n", szArchiveName); + __debugbreak(); + if(StormOpenArchive(szArchiveName, 0, 0, &hMpq)) + { + LPCSTR * FilesToOpen = DefFilesToOpen; + size_t nFilesToOpen = _countof(DefFilesToOpen); + + // Set the list of files + if(argc > 2) + { + FilesToOpen = (LPCSTR *)(&argv[2]); + nFilesToOpen = argc - 2; + } + + // Attempt to open all files + for(size_t i = 0; i < nFilesToOpen; i++) + { + //printf("[*] Opening file '%s' ...\n", FilesToOpen[i]); + if(StormOpenFileEx(hMpq, FilesToOpen[i], 0, &hFile)) + { + //printf("[*] Retrieving file size ... "); + dwFileSize = StormGetFileSize(hFile, NULL); + szFormat = (dwFileSize == INVALID_FILE_SIZE) ? ("(invalid)\n") : ("(%u bytes)\n"); + //printf(szFormat, dwFileSize); + + // Allocate the buffer for the entire file + //printf("[*] Allocating buffer ...\n"); + if((pbBuffer = new BYTE[dwFileSize]) != NULL) + { + //printf("[*] Moving to begin of the file ...\n"); + StormSetFilePointer(hFile, 0, NULL, FILE_BEGIN); + + //printf("[*] Reading file ... "); + bResult = StormReadFile(hFile, pbBuffer, dwFileSize, &dwBytesRead, NULL); + szFormat = (bResult != FALSE && dwBytesRead == dwFileSize) ? ("(OK)\n") : ("(error %u)\n"); + //printf(szFormat, GetLastError()); + + //printf("[*] Calculating MD5 ... "); + CalculateMD5(md5_digest, pbBuffer, dwFileSize); + BinaryToString(md5_string, _countof(md5_string), md5_digest, sizeof(md5_digest)); + printf("%s *%s\n", md5_string, FilesToOpen[i]); + + delete[] pbBuffer; + } + + //printf("[*] Closing file ...\n"); + StormCloseFile(hFile); + } + } + + //printf("[*] Closing archive ...\n"); + StormCloseArchive(hMpq); + } + + //printf("Done.\n\n"); + return 0; +} diff --git a/dep/StormLib/storm_dll/storm_test.vcxproj b/dep/StormLib/storm_dll/storm_test.vcxproj new file mode 100644 index 0000000000..fccd989cef --- /dev/null +++ b/dep/StormLib/storm_dll/storm_test.vcxproj @@ -0,0 +1,202 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {5B0E680A-EC4E-402E-AE0D-18ADD2DC6EBE} + storm_test + Win32Proj + 10.0.17134.0 + + + + Application + v143 + MultiByte + + + Application + v143 + MultiByte + + + Application + v143 + MultiByte + + + Application + v143 + MultiByte + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>16.0.31829.152 + + + .\bin\exe\$(Platform)\$(Configuration)\ + .\bin\exe\$(Platform)\$(Configuration)\ + true + + + .\bin\exe\$(Platform)\$(Configuration)\ + .\bin\exe\$(Platform)\$(Configuration)\ + true + + + .\bin\exe\$(Platform)\$(Configuration)\ + .\bin\exe\$(Platform)\$(Configuration)\ + false + + + .\bin\exe\$(Platform)\$(Configuration)\ + .\bin\exe\$(Platform)\$(Configuration)\ + false + + + + Disabled + ../aaa/inc;exif;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + + Level4 + EditAndContinue + + + $(OutDir)storm_test.exe + .\bin\dll\Win32\Release;%(AdditionalLibraryDirectories) + true + $(OutDir)storm_test.pdb + Console + false + + MachineX86 + false + + + + + X64 + + + Disabled + ../aaa/inc;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + + Level4 + ProgramDatabase + + + $(OutDir)storm_test.exe + .\bin\dll\x64\Release;%(AdditionalLibraryDirectories) + true + $(OutDir)storm_test.pdb + Console + false + + MachineX64 + false + + + + + ../aaa/inc;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreaded + + Level4 + ProgramDatabase + + + $(OutDir)storm_test.exe + .\bin\dll\Win32\Release;%(AdditionalLibraryDirectories) + true + Console + true + true + false + + MachineX86 + false + + + + + + + + X64 + + + ../aaa/inc;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreaded + + Level4 + ProgramDatabase + + + $(OutDir)storm_test.exe + .\bin\dll\x64\Release;%(AdditionalLibraryDirectories) + true + Console + true + true + false + + MachineX64 + false + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dep/StormLib/storm_dll/storm_test.vcxproj.filters b/dep/StormLib/storm_dll/storm_test.vcxproj.filters new file mode 100644 index 0000000000..3345f5245e --- /dev/null +++ b/dep/StormLib/storm_dll/storm_test.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {10b55df7-950e-4099-ab74-ee928278fc62} + + + + + Source Files + + + + + Header Files + + + \ No newline at end of file diff --git a/dep/StormLib/test/CMakeLists.txt b/dep/StormLib/test/CMakeLists.txt new file mode 100644 index 0000000000..80c64c4663 --- /dev/null +++ b/dep/StormLib/test/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.10) + +set(TEST_SRC_FILES + StormTest.cpp +) + +# Option to use the legacy hardcoded paths +option(STORMTEST_USE_OLD_PATHS "Use hardcoded OS-specific default paths for WORK_PATH_ROOT" ON) + +if(NOT STORMTEST_USE_OLD_PATHS) + set(WORK_PATH_ROOT "${CMAKE_CURRENT_BINARY_DIR}/work") + add_compile_definitions(WORK_PATH_ROOT="${WORK_PATH_ROOT}") +endif() + +find_package(ALSA REQUIRED) +add_executable(StormLib_test ${TEST_SRC_FILES}) +target_link_libraries(StormLib_test storm ALSA::ALSA) +install(TARGETS StormLib_test RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + +add_test(NAME StormLib_test COMMAND StormLib_test) diff --git a/dep/StormLib/test/StormTest.cpp b/dep/StormLib/test/StormTest.cpp new file mode 100755 index 0000000000..16eef96e57 --- /dev/null +++ b/dep/StormLib/test/StormTest.cpp @@ -0,0 +1,4697 @@ +/*****************************************************************************/ +/* StormTest.cpp Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* Test module for StormLib */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 25.03.03 1.00 Lad The first version of StormTest.cpp */ +/*****************************************************************************/ + +#define _CRT_NON_CONFORMING_SWPRINTFS +#define _CRT_SECURE_NO_DEPRECATE +#define __INCLUDE_CRYPTOGRAPHY__ +#define __STORMLIB_SELF__ // Don't use StormLib.lib +#include + +#ifdef _MSC_VER +#include +#endif + +#include "../src/StormLib.h" +#include "../src/StormCommon.h" + +#include "TLogHelper.cpp" // Helper class for showing test results + +#ifdef _MSC_VER +#pragma warning(disable: 4505) // 'XXX' : unreferenced local function has been removed +#include +#pragma comment(lib, "winmm.lib") +#endif + +#ifndef STORMLIB_WINDOWS +#include +#include // sudo apt-get install libasound2-dev +#endif + +//------------------------------------------------------------------------------ +// Local structures + +#define ID_XHSC 0x58485343 // 'XHSC' + +// Artificial error code for situations where we don't know the result +#define ERROR_UNDETERMINED_RESULT 0xC000FFFF + +// Artificial flag for not reporting open failure +#define MPQ_OPEN_DONT_REPORT_FAILURE 0x80000000 + +// Size of SHA256, in bytes +#define SHA256_DIGEST_SIZE 0x20 + +typedef DWORD (*FS_SEARCH_CALLBACK)(LPCTSTR szFullPath, void * lpContext); + +typedef enum _EXTRA_TYPE +{ + NoExtraType = 0, + ListFile, + Utf8File, + TwoFiles, + PatchList, + HashValues, +} EXTRA_TYPE, *PEXTRA_TYPE; + +typedef struct _FILE_DATA +{ + DWORD dwBlockIndex; + DWORD dwFileSize; + DWORD dwFlags; + DWORD dwCrc32; + BYTE FileData[1]; +} FILE_DATA, *PFILE_DATA; + +typedef struct _TEST_EXTRA_ONEFILE +{ + EXTRA_TYPE Type; // Must be ListFile + LPCTSTR szFile; // The name of the (list)file +} TEST_EXTRA_ONEFILE, *PTEST_EXTRA_ONEFILE; + +typedef struct _TEST_EXTRA_UTF8 +{ + EXTRA_TYPE Type; // Must be Utf8File + const BYTE * szMpqFile; // (UTF-8) The name of the MPQ file + const BYTE * szListFile; // (UTF-8) The name of the listfile +} TEST_EXTRA_UTF8, *PTEST_EXTRA_UTF8; + +typedef struct _TEST_EXTRA_TWOFILES +{ + EXTRA_TYPE Type; // Must be TwoFiles + LPCSTR szFile1; // The first referenced file name + LPCSTR szFile2; // The second referenced file name +} TEST_EXTRA_TWOFILES, *PTEST_EXTRA_TWOFILES; + +typedef struct _TEST_EXTRA_PATCHES +{ + EXTRA_TYPE Type; // Must be PatchList + LPCTSTR szPatchList; // Multi-SZ list of patches + LPCSTR szFileName; // Example of patched file + DWORD dwPatchCount; // Number of patches +} TEST_EXTRA_PATCHES, *PTEST_EXTRA_PATCHES; + +typedef struct _TEST_EXTRA_HASHVAL +{ + DWORD dwHash1; // Hash A of the file name + DWORD dwHash2; // Hash B of the file name + LPCSTR szFileName; // File name +} TEST_EXTRA_HASHVAL, *PTEST_EXTRA_HASHVAL; + +typedef struct _TEST_EXTRA_HASHVALS +{ + EXTRA_TYPE Type; // Must be HashValues + TEST_EXTRA_HASHVAL Items[2]; +} TEST_EXTRA_HASHVALS, *PTEST_EXTRA_HASHVALS; + +typedef struct _TEST_INFO1 +{ + LPCTSTR szName1; // MPQ name + LPCTSTR szName2; // ListFile name or NULL if none + LPCSTR szDataHash; // Compound name+data hash + DWORD dwFlags; // Flags for testing the file. Low 16 bits contains number of files + const void * pExtra; +} TEST_INFO1, *PTEST_INFO1; + +typedef struct _TEST_INFO2 +{ + LPCSTR szName1; // (UTF-8) MPQ name + LPCSTR szName2; // (UTF-8) Added file name or NULL if none + LPCSTR szDataHash; // Compound name+data hash + DWORD dwFlags; // Flags for testing the file. Low 16 bits contains number of files + const void * pExtra; +} TEST_INFO2, *PTEST_INFO2; + +typedef struct _LINE_INFO +{ + LONG nLinePos; + DWORD nLineLen; + const char * szLine; +} LINE_INFO, *PLINE_INFO; + +#define ID_WAVE_MAGIC1 0x46464952 // 'RIFF' +#define ID_WAVE_MAGIC2 0x45564157 // 'WAVE' +#define ID_WAVE_CHUNK_FMT 0x20746d66 // 'fmt ' +#define ID_WAVE_CHUNK_DATA 0x61746164 // 'data' + +typedef struct _WAVE_FILE_HEADER +{ + DWORD dwRiffSignature; // ID_WAVE_MAGIC1 ('RIFF') + DWORD dwFileSize; + DWORD dwTypeSignature; // ID_WAVE_MAGIC2 ('WAVE') + DWORD dwChunkMarker; // ID_WAVE_CHUNK_FMT ('fmt\0') + DWORD dwLength; // Length of the format chunk (16 bytes) + USHORT wFormat; // 1 = PCM + USHORT wChannels; // Number of channels + DWORD dwSamplesPerSec; // Bitrate + DWORD dwAvgSamplesPerSec; + USHORT wBlockAlign; + USHORT wBitsPerSample; + DWORD dwDataSignature; // ID_WAVE_CHUNK_DATA ('data') + DWORD dwDataSize; // Size of the data chunk +} WAVE_FILE_HEADER, *PWAVE_FILE_HEADER; + +//------------------------------------------------------------------------------ +// Local variables + +#ifndef WORK_PATH_ROOT + #ifdef STORMLIB_WINDOWS + #define WORK_PATH_ROOT _T("\\Multimedia\\MPQs") + #endif + + #ifdef STORMLIB_LINUX + #define WORK_PATH_ROOT "/media/ladik/MPQs" + #endif + + #ifdef STORMLIB_HAIKU + #define WORK_PATH_ROOT "~/StormLib/test" + #endif +#endif + +// Definition of the path separator +#ifdef STORMLIB_WINDOWS +static LPCTSTR g_szPathSeparator = _T("\\"); +static const TCHAR PATH_SEPARATOR = _T('\\'); // Path separator for Windows platforms +#else +static LPCSTR g_szPathSeparator = "/"; +static const TCHAR PATH_SEPARATOR = '/'; // Path separator for Non-Windows platforms +#endif + +// Global for the work MPQ +static LPCTSTR szMpqSubDir = _T("1995 - Test MPQs"); +static TCHAR szDataFileDir[MAX_PATH] = {0}; +static TCHAR szListFileDir[MAX_PATH] = {0}; +static TCHAR szMpqPatchDir[MAX_PATH] = {0}; + +//----------------------------------------------------------------------------- +// Testing data + +// Flags for TestOpenArchive +#define TFLG_VALUE_MASK 0x000FFFFF // Mask for file count +#define TFLG_READ_ONLY 0x00100000 // Open the archive read-only +#define TFLG_SIGCHECK_BEFORE 0x00200000 // Verify signature before modification +#define TFLG_MODIFY 0x00400000 // Modify the archive +#define TFLG_SIGN_ARCHIVE 0x00800000 // Sign an archive that is not signed +#define TFLG_SIGCHECK_AFTER 0x01000000 // Verify signature after modification +#define TFLG_GET_FILE_INFO 0x02000000 // Test the GetFileInfo function +#define TFLG_ADD_USER_DATA 0x04000000 // Add user data during MPQ copying +#define TFLG_HAS_LISTFILE 0x08000000 // The MPQ must have (listfile) +#define TFLG_HAS_ATTRIBUTES 0x10000000 // The MPQ must have (attributes) +#define TFLG_BIGFILE 0x20000000 // Add a big file to the MPQ +#define TFLG_COMPACT 0x40000000 // Perform archive compacting between two opens +#define TFLG_WILL_FAIL 0x80000000 // The process is expected to fail + +// Flags for TestCreateArchive +#define CFLG_V2 0x00010000 // Create archive version 2 +#define CFLG_V4 0x00020000 // Create archive version 4 +#define CFLG_EMPTY 0x00040000 // The archive will be empty +#define CFLG_NONSTD_NAMES 0x00080000 // Create archive containing non-standard names +#define CFLG_MPQEDITOR 0x00100000 // Create archive like MPQEditor would do + +// Flags for TestOpenArchive_SignatureTest +#define SFLAG_VERIFY_BEFORE 0x00000001 // Verify signature before modification +#define SFLAG_CREATE_ARCHIVE 0x00000002 // Create new archive +#define SFLAG_MODIFY_ARCHIVE 0x00000004 // Modify the archive before signing +#define SFLAG_SIGN_AT_CREATE 0x00000008 // Sign the archive at the creation +#define SFLAG_SIGN_ARCHIVE 0x00000010 // Sign the archive +#define SFLAG_VERIFY_AFTER 0x00000020 // Verify the signature after modification + +static DWORD AddFlags[] = +{ +// Compression Encryption Fixed key Single Unit Sector CRC + 0 | 0 | 0 | 0 | 0, + 0 | MPQ_FILE_ENCRYPTED | 0 | 0 | 0, + 0 | MPQ_FILE_ENCRYPTED | MPQ_FILE_KEY_V2 | 0 | 0, + 0 | 0 | 0 | MPQ_FILE_SINGLE_UNIT | 0, + 0 | MPQ_FILE_ENCRYPTED | 0 | MPQ_FILE_SINGLE_UNIT | 0, + 0 | MPQ_FILE_ENCRYPTED | MPQ_FILE_KEY_V2 | MPQ_FILE_SINGLE_UNIT | 0, + MPQ_FILE_IMPLODE | 0 | 0 | 0 | 0, + MPQ_FILE_IMPLODE | MPQ_FILE_ENCRYPTED | 0 | 0 | 0, + MPQ_FILE_IMPLODE | MPQ_FILE_ENCRYPTED | MPQ_FILE_KEY_V2 | 0 | 0, + MPQ_FILE_IMPLODE | 0 | 0 | MPQ_FILE_SINGLE_UNIT | 0, + MPQ_FILE_IMPLODE | MPQ_FILE_ENCRYPTED | 0 | MPQ_FILE_SINGLE_UNIT | 0, + MPQ_FILE_IMPLODE | MPQ_FILE_ENCRYPTED | MPQ_FILE_KEY_V2 | MPQ_FILE_SINGLE_UNIT | 0, + MPQ_FILE_IMPLODE | 0 | 0 | 0 | MPQ_FILE_SECTOR_CRC, + MPQ_FILE_IMPLODE | MPQ_FILE_ENCRYPTED | 0 | 0 | MPQ_FILE_SECTOR_CRC, + MPQ_FILE_IMPLODE | MPQ_FILE_ENCRYPTED | MPQ_FILE_KEY_V2 | 0 | MPQ_FILE_SECTOR_CRC, + MPQ_FILE_COMPRESS | 0 | 0 | 0 | 0, + MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED | 0 | 0 | 0, + MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED | MPQ_FILE_KEY_V2 | 0 | 0, + MPQ_FILE_COMPRESS | 0 | 0 | MPQ_FILE_SINGLE_UNIT | 0, + MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED | 0 | MPQ_FILE_SINGLE_UNIT | 0, + MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED | MPQ_FILE_KEY_V2 | MPQ_FILE_SINGLE_UNIT | 0, + MPQ_FILE_COMPRESS | 0 | 0 | 0 | MPQ_FILE_SECTOR_CRC, + MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED | 0 | 0 | MPQ_FILE_SECTOR_CRC, + MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED | MPQ_FILE_KEY_V2 | 0 | MPQ_FILE_SECTOR_CRC, + 0xFFFFFFFF +}; + +static DWORD WaveCompressions[] = +{ + MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_HUFFMANN, + MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN, + MPQ_COMPRESSION_PKWARE, + MPQ_COMPRESSION_ZLIB, + MPQ_COMPRESSION_BZIP2 +}; + +static const BYTE szPlainName_CZE[] = { 0xc4, 0x8c, 0x65, 0x73, 0x6b, 0xc3, 0xbd, 0x2e, 0x6d, 0x70, 0x71, 0x00}; // (UTF-8) Czech +static const BYTE szPlainName_RUS[] = { 0xd0, 0xa0, 0xd1, 0x83, 0xd1, 0x81, 0xd1, 0x81, 0xd0, 0xba, 0xd0, 0xb8, 0xd0, 0xb9, 0x2e, 0x6d, 0x70, 0x71, 0x00}; // (UTF-8) Russian +static const BYTE szPlainName_GRC[] = {0xce, 0xb5, 0xce, 0xbb, 0xce, 0xbb, 0xce, 0xb7, 0xce, 0xbd, 0xce, 0xb9, 0xce, 0xba, 0xce, 0xac, 0x2e, 0x6d, 0x70, 0x71, 0x00}; // (UTF-8) Greek +static const BYTE szPlainName_CHN[] = { 0xe6, 0x97, 0xa5, 0xe6, 0x9c, 0xac, 0xe8, 0xaa, 0x9e, 0x2e, 0x6d, 0x70, 0x71, 0x00}; // (UTF-8) Chinese +static const BYTE szPlainName_JPN[] = { 0xe7, 0xae, 0x80, 0xe4, 0xbd, 0x93, 0xe4, 0xb8, 0xad, 0xe6, 0x96, 0x87, 0x2e, 0x6d, 0x70, 0x71, 0x00}; // (UTF-8) Japanese +static const BYTE szPlainName_SAU[] = {0xd8, 0xa7, 0xd9, 0x84, 0xd8, 0xb9, 0xd8, 0xb9, 0xd8, 0xb1, 0xd8, 0xa8, 0xd9, 0x8a, 0xd8, 0xa9, 0x2e, 0x6d, 0x70, 0x71, 0x00}; // (UTF-8) Arabic + +static SFILE_MARKERS MpqMarkers[] = +{ + {sizeof(SFILE_MARKERS), ID_MPQ, "(hash table)", "(block table)"}, + {sizeof(SFILE_MARKERS), ID_XHSC, "(cash table)", "(clock table)"} +}; + +//----------------------------------------------------------------------------- +// Local file functions + +// This must be the directory where our test MPQs are stored. +// We also expect a subdirectory named +static TCHAR szMpqDirectory[MAX_PATH+1]; +size_t cchMpqDirectory = 0; + +inline bool AssertTrue(bool bCondition) +{ + if(!bCondition) + { +#ifdef STORMLIB_WINDOWS + __debugbreak(); +#else + assert(false); +#endif + } + return bCondition; +} + +#define ASSERT_TRUE(condition) { if(!AssertTrue(condition)) { return false; } } + +static EXTRA_TYPE GetExtraType(const void * pExtra) +{ + if(pExtra != NULL) + { + return *(PEXTRA_TYPE)(pExtra); + } + else + { + return NoExtraType; + } +} + +LPCTSTR SwapMpqName(LPTSTR szBuffer, size_t ccBuffer, LPCTSTR szMpqName, PTEST_EXTRA_UTF8 pExtra) +{ + if((pExtra != NULL) && (pExtra->Type == Utf8File)) + { + StringCopy(szBuffer, ccBuffer, (LPCSTR)(pExtra->szMpqFile)); + return szBuffer; + } + else + { + return szMpqName; + } +} + +template +static bool IsFullPath(const XCHAR * szFileName) +{ +#ifdef STORMLIB_WINDOWS + if(('A' <= szFileName[0] && szFileName[0] <= 'Z') || ('a' <= szFileName[0] && szFileName[0] <= 'z')) + { + return (szFileName[1] == ':' && szFileName[2] == PATH_SEPARATOR); + } +#endif + + szFileName = szFileName; + return false; +} + +LPCTSTR GetRelativePath(LPCTSTR szFullPath) +{ + LPCTSTR szRelativePath; + + if(szFullPath && szFullPath[0]) + { + if((szRelativePath = _tcsstr(szFullPath, szMpqSubDir)) != NULL) + { + return szRelativePath; + } + } + return _T(""); +} + +const char * GetFileText(PFILE_DATA pFileData) +{ + const char * szFileText = (const char *)(pFileData->FileData); + + // Skip UTF-8 marker + if(pFileData->dwFileSize > 3 && !memcmp(szFileText, "\xEF\xBB\xBF", 3)) + szFileText += 3; + return szFileText; +} + +#ifdef STORMLIB_LINUX +static void alsa_silent_error_handler(const char * file, int line, const char * function, int err, const char * fmt, ...) +{ + // Suppress ALSA error output, so do nothing +} +#endif + +static void PlayWaveSound(PFILE_DATA pFileData) +{ +#ifdef STORMLIB_WINDOWS + PlaySound((LPCTSTR)pFileData->FileData, NULL, SND_MEMORY); +#endif + +#ifdef STORMLIB_LINUX + PWAVE_FILE_HEADER pWaveHeader = (PWAVE_FILE_HEADER)pFileData->FileData; + snd_pcm_hw_params_t *params; + snd_pcm_t *pcm_handle; + + // Suppress ALSA error printing + snd_lib_error_set_handler(alsa_silent_error_handler); + + // Capture the WAVE file header + if(pFileData->dwFileSize >= sizeof(WAVE_FILE_HEADER) && pWaveHeader->dwRiffSignature == ID_WAVE_MAGIC1 && pWaveHeader->dwTypeSignature == ID_WAVE_MAGIC2) + { + // Scan for "fmt " and "data" + if(pWaveHeader->dwChunkMarker == ID_WAVE_CHUNK_FMT && pWaveHeader->dwDataSignature == ID_WAVE_CHUNK_DATA) + { + if(pWaveHeader->wBlockAlign != 0) + { + // Open the default sound device and play the sound + if(snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, 0) >= 0) + { + snd_pcm_format_t format = (pWaveHeader->wBitsPerSample == 16) ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_S8; + unsigned int rate = pWaveHeader->dwSamplesPerSec; + + snd_pcm_hw_params_alloca(¶ms); + snd_pcm_hw_params_any(pcm_handle, params); + snd_pcm_hw_params_set_access(pcm_handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); + snd_pcm_hw_params_set_format(pcm_handle, params, format); + snd_pcm_hw_params_set_channels(pcm_handle, params, pWaveHeader->wChannels); + snd_pcm_hw_params_set_rate_near(pcm_handle, params, &rate, 0); + snd_pcm_hw_params(pcm_handle, params); + + snd_pcm_writei(pcm_handle, (pWaveHeader + 1), pWaveHeader->dwDataSize / pWaveHeader->wBlockAlign); + snd_pcm_drain(pcm_handle); + snd_pcm_close(pcm_handle); + } + } + } + } + #endif +} + +static bool CompareBlocks(LPBYTE pbBlock1, LPBYTE pbBlock2, DWORD dwLength, DWORD * pdwDifference) +{ + for(DWORD i = 0; i < dwLength; i++) + { + if(pbBlock1[i] != pbBlock2[i]) + { + pdwDifference[0] = i; + return false; + } + } + + return true; +} + +static DWORD CompareTwoFiles(TLogHelper & Logger, PFILE_DATA pFileData1, PFILE_DATA pFileData2) +{ + DWORD dwDifference = 0; + + // Compare the file sizes + if(pFileData1->dwFileSize != pFileData2->dwFileSize) + { + Logger.PrintMessage("File size mismatch: %u vs %u", pFileData1->dwFileSize, pFileData2->dwFileSize); + return ERROR_FILE_CORRUPT; + } + + // Compare the file data + if(memcmp(pFileData1->FileData, pFileData2->FileData, pFileData1->dwFileSize)) + { + CompareBlocks(pFileData1->FileData, pFileData2->FileData, pFileData1->dwFileSize, &dwDifference); + Logger.PrintMessage("File data mismatch at offset %08x", dwDifference); + return ERROR_FILE_CORRUPT; + } + + return ERROR_SUCCESS; +} + +template +static const XCHAR * FindNextPathPart(const XCHAR * szPath, size_t nPartCount) +{ + const XCHAR * szPathPart = szPath; + + while(szPath[0] != 0 && nPartCount > 0) + { + // Is there path separator? + if(szPath[0] == '\\' || szPath[0] == '/') + { + szPathPart = szPath + 1; + nPartCount--; + } + + // Move to the next letter + szPath++; + } + + return szPathPart; +} + +template +size_t StringLength(const XCHAR * szString) +{ + size_t nLength; + + for(nLength = 0; szString[nLength] != 0; nLength++); + + return nLength; +} + +template +static const XCHAR * GetShortPlainName(const XCHAR * szFileName) +{ + const XCHAR * szPlainName = FindNextPathPart(szFileName, 1000); + const XCHAR * szPlainEnd = szFileName + StringLength(szFileName); + + // If the name is still too long, cut it + if((szPlainEnd - szPlainName) > 50) + szPlainName = szPlainEnd - 50; + + return szPlainName; +} + +static size_t ConvertSha256ToText(const unsigned char * sha_digest, LPTSTR szBuffer) +{ + LPCSTR szTable = "0123456789abcdef"; + + for(size_t i = 0; i < SHA256_DIGEST_SIZE; i++) + { + *szBuffer++ = szTable[(sha_digest[0] >> 0x04)]; + *szBuffer++ = szTable[(sha_digest[0] & 0x0F)]; + sha_digest++; + } + + *szBuffer = 0; + return (SHA256_DIGEST_SIZE * 2); +} + +static void CreateFullPathName(TCHAR * szBuffer, size_t cchBuffer, LPCTSTR szSubDir, LPCTSTR szNamePart1, LPCTSTR szNamePart2 = NULL) +{ + TCHAR * szSaveBuffer = szBuffer; + size_t nPrefixLength = 0; + size_t nLength; + DWORD dwProvider = 0; + bool bIsFullPath = false; + char chSeparator = PATH_SEPARATOR; + + // Pre-initialize the buffer + szBuffer[0] = 0; + + // Determine the path prefix + if(szNamePart1 != NULL) + { + nPrefixLength = FileStream_Prefix(szNamePart1, &dwProvider); + if((dwProvider & BASE_PROVIDER_MASK) == BASE_PROVIDER_HTTP) + { + bIsFullPath = true; + chSeparator = '/'; + } + else + bIsFullPath = IsFullPath(szNamePart1 + nPrefixLength); + } + + // Copy the MPQ prefix, if any + if(nPrefixLength > 0) + { + StringCat(szBuffer, cchBuffer, szNamePart1); + szBuffer[nPrefixLength] = 0; + szSaveBuffer += nPrefixLength; + szNamePart1 += nPrefixLength; + } + + // If the given name is not a full path, copy the MPQ directory + if(bIsFullPath == false) + { + // Copy the master MPQ directory + StringCat(szBuffer, cchBuffer, szMpqDirectory); + + // Append the subdirectory, if any + if(szSubDir != NULL && (nLength = _tcslen(szSubDir)) != 0) + { + // No leading or trailing separator are allowed + assert(szSubDir[0] != '/' && szSubDir[0] != '\\'); + assert(szSubDir[nLength - 1] != '/' && szSubDir[nLength - 1] != '\\'); + + // Append the subdirectory + StringCat(szBuffer, cchBuffer, g_szPathSeparator); + StringCat(szBuffer, cchBuffer, szSubDir); + } + } + + // Copy the file name, if any + if(szNamePart1 != NULL && (nLength = _tcslen(szNamePart1)) != 0) + { + // Path separators are not allowed in the name part + assert(szNamePart1[0] != '\\' && szNamePart1[0] != '/'); + assert(szNamePart1[nLength - 1] != '/' && szNamePart1[nLength - 1] != '\\'); + + // Append file path separator and the name part + if(bIsFullPath == false) + StringCat(szBuffer, cchBuffer, g_szPathSeparator); + StringCat(szBuffer, cchBuffer, szNamePart1); + } + + // Append the second part of the name + if(szNamePart2 != NULL && (nLength = _tcslen(szNamePart2)) != 0) + { + // Copy the file name + StringCat(szBuffer, cchBuffer, szNamePart2); + } + + // Normalize the path separators + for(; szSaveBuffer[0] != 0; szSaveBuffer++) + { + szSaveBuffer[0] = (szSaveBuffer[0] != '/' && szSaveBuffer[0] != '\\') ? szSaveBuffer[0] : chSeparator; + } +} + +#ifdef _UNICODE +static void CreateFullPathName(char * szBuffer, size_t cchBuffer, LPCTSTR szSubDir, LPCTSTR szNamePart1, LPCTSTR szNamePart2 = NULL) +{ + TCHAR szFullPathT[MAX_PATH]; + + CreateFullPathName(szFullPathT, _countof(szFullPathT), szSubDir, szNamePart1, szNamePart2); + StringCopy(szBuffer, cchBuffer, szFullPathT); +} +#endif + +static DWORD CalculateFileHash(TLogHelper * pLogger, LPCTSTR szFullPath, LPTSTR szFileHash) +{ + TFileStream * pStream; + hash_state sha256_state; + ULONGLONG ByteOffset = 0; + ULONGLONG FileSize = 0; + LPCTSTR szShortPlainName = GetShortPlainName(szFullPath); + LPCTSTR szHashingFormat = _T("Hashing %s ") fmt_X_of_Y_t; + LPBYTE pbFileBlock; + DWORD cbBytesToRead; + DWORD cbFileBlock = 0x100000; + DWORD dwErrCode = ERROR_SUCCESS; + BYTE file_hash[SHA256_DIGEST_SIZE]; + + // Notify the user + pLogger->PrintProgress(_T("Hashing %s ..."), szShortPlainName); + szFileHash[0] = 0; + + // Open the file to be verified + pStream = FileStream_OpenFile(szFullPath, STREAM_FLAG_READ_ONLY); + if(pStream != NULL) + { + // Retrieve the size of the file + FileStream_GetSize(pStream, &FileSize); + + // Allocate the buffer for loading file parts + pbFileBlock = STORM_ALLOC(BYTE, cbFileBlock); + if(pbFileBlock != NULL) + { + // Initialize SHA256 calculation + sha256_init(&sha256_state); + + // Calculate the SHA256 of the file + while(ByteOffset < FileSize) + { + // Notify the user + pLogger->PrintProgress(szHashingFormat, szShortPlainName, ByteOffset, FileSize); + + // Load the file block + cbBytesToRead = ((FileSize - ByteOffset) > cbFileBlock) ? cbFileBlock : (DWORD)(FileSize - ByteOffset); + if(!FileStream_Read(pStream, &ByteOffset, pbFileBlock, cbBytesToRead)) + { + dwErrCode = SErrGetLastError(); + break; + } + + // Add to SHA256 + sha256_process(&sha256_state, pbFileBlock, cbBytesToRead); + ByteOffset += cbBytesToRead; + } + + // Notify the user + pLogger->PrintProgress(szHashingFormat, szShortPlainName, ByteOffset, FileSize); + + // Finalize SHA256 + sha256_done(&sha256_state, file_hash); + + // Convert the SHA256 to ANSI text + ConvertSha256ToText(file_hash, szFileHash); + STORM_FREE(pbFileBlock); + } + + FileStream_Close(pStream); + } + + // If we calculated something, return OK + if(dwErrCode == ERROR_SUCCESS && szFileHash[0] == 0) + dwErrCode = ERROR_CAN_NOT_COMPLETE; + return dwErrCode; +} + +//----------------------------------------------------------------------------- +// Directory search + +static HANDLE InitDirectorySearch(LPCTSTR szDirectory) +{ +#ifdef STORMLIB_WINDOWS + + WIN32_FIND_DATA wf; + HANDLE hFind; + TCHAR szSearchMask[MAX_PATH]; + + // Construct the directory mask + _stprintf(szSearchMask, _T("%s\\*"), szDirectory); + + // Perform the search + hFind = FindFirstFile(szSearchMask, &wf); + return (hFind != INVALID_HANDLE_VALUE) ? hFind : NULL; + +#endif + +#if defined(STORMLIB_LINUX) || defined(STORMLIB_MAC) + + // Open the directory + return (HANDLE)opendir(szDirectory); + +#endif +} + +static bool SearchDirectory(HANDLE hFind, LPTSTR szDirEntry, size_t cchDirEntry, bool & IsDirectory) +{ +#ifdef STORMLIB_WINDOWS + + WIN32_FIND_DATA wf; + + // Search for the hnext entry. + if(FindNextFile(hFind, &wf)) + { + IsDirectory = (wf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? true : false; + StringCopy(szDirEntry, cchDirEntry, wf.cFileName); + return true; + } + + return false; + +#endif + +#if defined(STORMLIB_LINUX) || defined(STORMLIB_MAC) + + struct dirent * directory_entry; + + directory_entry = readdir((DIR *)hFind); + if(directory_entry != NULL) + { + IsDirectory = (directory_entry->d_type == DT_DIR) ? true : false; + strcpy(szDirEntry, directory_entry->d_name); + return true; + } + + return false; + +#endif +} + +static void FreeDirectorySearch(HANDLE hFind) +{ +#ifdef STORMLIB_WINDOWS + FindClose(hFind); +#endif + +#if defined(STORMLIB_LINUX) || defined(STORMLIB_MAC) + closedir((DIR *)hFind); +#endif +} + +static PFILE_DATA LoadLocalFile(TLogHelper * pLogger, LPCTSTR szFileName, bool bMustSucceed) +{ + TFileStream * pStream; + PFILE_DATA pFileData = NULL; + ULONGLONG FileSize = 0; + size_t nAllocateBytes; + + // Notify the user + if(pLogger != NULL) + pLogger->PrintProgress(_T("Loading %s ..."), GetPlainFileName(szFileName)); + + // Attempt to open the file + if((pStream = FileStream_OpenFile(szFileName, STREAM_FLAG_READ_ONLY)) != NULL) + { + // Get the file size + FileStream_GetSize(pStream, &FileSize); + + // Check for too big files + if((FileSize >> 0x20) == 0) + { + // Allocate space for the file + nAllocateBytes = sizeof(FILE_DATA) + (size_t)FileSize; + pFileData = (PFILE_DATA)STORM_ALLOC(BYTE, nAllocateBytes); + if(pFileData != NULL) + { + // Make sure it;s properly zeroed + memset(pFileData, 0, nAllocateBytes); + pFileData->dwFileSize = (DWORD)FileSize; + + // Load to memory + if(!FileStream_Read(pStream, NULL, pFileData->FileData, pFileData->dwFileSize)) + { + STORM_FREE(pFileData); + pFileData = NULL; + } + } + } + + // Close the stream + FileStream_Close(pStream); + } + else + { + if(pLogger != NULL && bMustSucceed == true) + pLogger->PrintError(_T("Open failed: %s"), szFileName); + } + + // Return the loaded file data or NULL + return pFileData; +} + +static DWORD FindFilesInternal(FS_SEARCH_CALLBACK PfnFolderCallback, FS_SEARCH_CALLBACK PfnFileCallback, LPTSTR szDirectory, void * lpContext = NULL) +{ + LPTSTR szPlainName; + HANDLE hFind; + size_t nLength; + TCHAR szDirEntry[MAX_PATH]; + bool IsDirectory = false; + DWORD dwErrCode = ERROR_SUCCESS; + + if(szDirectory != NULL) + { + // Initiate directory search + hFind = InitDirectorySearch(szDirectory); + if(hFind != NULL) + { + // Append slash at the end of the directory name + nLength = _tcslen(szDirectory); + szDirectory[nLength++] = PATH_SEPARATOR; + szPlainName = szDirectory + nLength; + + // Skip the first entry, since it's always "." or ".." + while(SearchDirectory(hFind, szDirEntry, _countof(szDirEntry), IsDirectory) && dwErrCode == ERROR_SUCCESS) + { + // Copy the directory entry name to both names + _tcscpy(szPlainName, szDirEntry); + + // Found a directory? + if(IsDirectory) + { + if(szDirEntry[0] != '.') + { + if(PfnFolderCallback != NULL) + PfnFolderCallback(szDirectory, lpContext); + dwErrCode = FindFilesInternal(PfnFolderCallback, PfnFileCallback, szDirectory, lpContext); + } + } + else + { + if(PfnFileCallback != NULL) + { + dwErrCode = PfnFileCallback(szDirectory, lpContext); + } + } + } + + FreeDirectorySearch(hFind); + } + } + + // Free the path buffer, if any + return dwErrCode; +} + +static DWORD ForEachFile_VerifyFileHash(LPCTSTR szFullPath, void * lpContext) +{ + TLogHelper * pLogger = (TLogHelper *)(lpContext); + PFILE_DATA pFileData; + LPCTSTR szHashExtension = _T(".sha256"); + LPTSTR szExtension; + TCHAR szShaFileName[MAX_PATH + 1]; + TCHAR szHashText[0x80]; + char szHashTextA[0x80]; + DWORD dwErrCode = ERROR_SUCCESS; + + // Try to load the file with the SHA extension + StringCopy(szShaFileName, _countof(szShaFileName), szFullPath); + szExtension = _tcsrchr(szShaFileName, '.'); + if(szExtension == NULL) + return ERROR_SUCCESS; + + // Skip .sha and .sha256 files + if(!_tcsicmp(szExtension, _T(".txt")) || !_tcsicmp(szExtension, _T(".sha")) || !_tcsicmp(szExtension, szHashExtension)) + return ERROR_SUCCESS; + + // Load the local file to memory + _tcscpy(szExtension, szHashExtension); + pFileData = LoadLocalFile(pLogger, szShaFileName, false); + if(pFileData != NULL) + { + // Calculate SHA256 of the entire file + dwErrCode = CalculateFileHash(pLogger, szFullPath, szHashText); + if(dwErrCode == ERROR_SUCCESS) + { + // Compare with what we loaded from the file + if(pFileData->dwFileSize >= (SHA256_DIGEST_SIZE * 2)) + { + // Compare the SHA256 + StringCopy(szHashTextA, _countof(szHashTextA), szHashText); + if(_strnicmp(szHashTextA, GetFileText(pFileData), (SHA256_DIGEST_SIZE * 2))) + { + SErrSetLastError(dwErrCode = ERROR_FILE_CORRUPT); + pLogger->PrintError(_T("File hash check failed: %s"), szFullPath); + } + } + } + + // Clear the line + if(dwErrCode == ERROR_SUCCESS) + pLogger->PrintProgress("OK"); + STORM_FREE(pFileData); + } + return dwErrCode; +} + +// Verify SHA256 of each MPQ that we have in the collection +static DWORD VerifyFileHashes(LPCTSTR szSubDirectory) +{ + TLogHelper Logger("TestVerifyHash"); + TCHAR szWorkBuff[MAX_PATH]; + + // Construct the full directory name + CreateFullPathName(szWorkBuff, _countof(szWorkBuff), szSubDirectory, NULL); + + // Find each file and check its hash + return FindFilesInternal(NULL, ForEachFile_VerifyFileHash, szWorkBuff, &Logger); +} + +static DWORD FindListFileFolder(LPCTSTR szFullPath, void * /* lpContext */) +{ + LPCTSTR szPlainName = GetPlainFileName(szFullPath); + + // Check data file dir + if(szDataFileDir[0] == 0) + { + if(!_tcsnicmp(szPlainName, _T("addfiles-"), 9)) + { + StringCopy(szDataFileDir, _countof(szDataFileDir), GetRelativePath(szFullPath)); + return ERROR_SUCCESS; + } + } + + // Check listfile directory + if(szListFileDir[0] == 0) + { + if(!_tcsnicmp(szPlainName, _T("listfiles-"), 10)) + { + StringCopy(szListFileDir, _countof(szListFileDir), GetRelativePath(szFullPath)); + return ERROR_SUCCESS; + } + } + + if(szMpqPatchDir[0] == 0) + { + if(!_tcsnicmp(szPlainName, _T("patches-"), 8)) + { + StringCopy(szMpqPatchDir, _countof(szMpqPatchDir), GetRelativePath(szFullPath)); + return ERROR_SUCCESS; + } + } + + // Check patch directory + return ERROR_SUCCESS; +} + +static DWORD InitializeMpqDirectory(TCHAR * argv[], int argc) +{ + TLogHelper Logger("InitWorkFolder"); + TFileStream * pStream; + TCHAR szFullPath[MAX_PATH] = {0}; + LPCTSTR szWhereFrom = _T("default"); + LPCTSTR szDirName = WORK_PATH_ROOT; + + // Make sure SHA256 works in test program + register_hash(&sha256_desc); + + // Retrieve the first argument + if(argc > 1 && argv[1] != NULL) + { + // Check if it's a directory + pStream = FileStream_OpenFile(argv[1], STREAM_FLAG_READ_ONLY); + if(pStream == NULL) + { + szWhereFrom = _T("command line"); + szDirName = argv[1]; + } + else + { + FileStream_Close(pStream); + } + } + + // Copy the name of the MPQ directory. + StringCopy(szMpqDirectory, _countof(szMpqDirectory), szDirName); + cchMpqDirectory = _tcslen(szMpqDirectory); + + // Cut trailing slashes and/or backslashes + while((cchMpqDirectory > 0) && (szMpqDirectory[cchMpqDirectory - 1] == '/' || szMpqDirectory[cchMpqDirectory - 1] == '\\')) + cchMpqDirectory--; + szMpqDirectory[cchMpqDirectory] = 0; + + // Print the work directory info + Logger.PrintMessage(_T("Work directory %s (%s)"), szMpqDirectory, szWhereFrom); + + // Find the listfile directory within the MPQ directory + CreateFullPathName(szFullPath, _countof(szFullPath), NULL, szMpqSubDir); + FindFilesInternal(FindListFileFolder, NULL, szFullPath, NULL); + if(szDataFileDir[0] == 0) + return Logger.PrintError(_T("Data files folder was not found in the MPQ directory")); + if(szListFileDir[0] == 0) + return Logger.PrintError(_T("Listfile folder was not found in the MPQ directory")); + if(szMpqPatchDir[0] == 0) + return Logger.PrintError(_T("Patches folder was not found in the MPQ directory")); + + // Verify if the work MPQ directory is writable + CreateFullPathName(szFullPath, _countof(szFullPath), szMpqSubDir, _T("new-file.bin")); + pStream = FileStream_CreateFile(szFullPath, 0); + if(pStream == NULL) + return Logger.PrintError(_T("MPQ subdirectory doesn't exist or is not writable")); + + // Close the stream + FileStream_Close(pStream); + _tremove(szFullPath); + + // Verify if the working directory exists and if there is a subdirectory with the file name + CreateFullPathName(szFullPath, _countof(szFullPath), szListFileDir, _T("ListFile_Blizzard.txt")); + pStream = FileStream_OpenFile(szFullPath, STREAM_FLAG_READ_ONLY); + if(pStream == NULL) + return Logger.PrintError(_T("The main listfile (%s) was not found. Check your paths"), GetShortPlainName(szFullPath)); + + // Close the stream + FileStream_Close(pStream); + return ERROR_SUCCESS; +} + +static DWORD GetFilePatchCount(TLogHelper * pLogger, HANDLE hMpq, LPCSTR szFileName) +{ + TCHAR * szPatchName; + HANDLE hFile; + TCHAR szPatchChain[0x400]; + DWORD dwErrCode = ERROR_SUCCESS; + int nPatchCount = 0; + + // Open the MPQ file + if(SFileOpenFileEx(hMpq, szFileName, 0, &hFile)) + { + // Notify the user + pLogger->PrintProgress("Verifying patch chain for %s ...", GetShortPlainName(szFileName)); + + // Query the patch chain + if(!SFileGetFileInfo(hFile, SFileInfoPatchChain, szPatchChain, sizeof(szPatchChain), NULL)) + dwErrCode = pLogger->PrintError("Failed to retrieve the patch chain on %s", szFileName); + + // Is there anything at all in the patch chain? + if(dwErrCode == ERROR_SUCCESS && szPatchChain[0] == 0) + { + pLogger->PrintError("The patch chain for %s is empty", szFileName); + dwErrCode = ERROR_FILE_CORRUPT; + } + + // Now calculate the number of patches + if(dwErrCode == ERROR_SUCCESS) + { + // Get the pointer to the patch + szPatchName = szPatchChain; + + // Skip the base name + for(;;) + { + // Skip the current name + szPatchName = szPatchName + _tcslen(szPatchName) + 1; + if(szPatchName[0] == 0) + break; + + // Increment number of patches + nPatchCount++; + } + } + + SFileCloseFile(hFile); + } + else + { + if(SErrGetLastError() != ERROR_FILE_DELETED) + { + pLogger->PrintError("Open failed: %s", szFileName); + } + } + + return nPatchCount; +} + +static DWORD SetLocaleForFileOperations(HANDLE hMpq, LPCSTR szFileName, LCID lcLocale) +{ + HANDLE hFile = NULL; + DWORD dwErrCode = ERROR_SUCCESS; + + if(SFileOpenFileEx(hMpq, szFileName, 0, &hFile)) + { + if(!SFileSetFileLocale(hFile, lcLocale)) + dwErrCode = SErrGetLastError(); + SFileSetLocale(lcLocale); + SFileCloseFile(hFile); + } + else + { + dwErrCode = SErrGetLastError(); + } + return dwErrCode; +} + +static DWORD VerifyFilePatchCount(TLogHelper * pLogger, HANDLE hMpq, LPCSTR szFileName, DWORD dwExpectedPatchCount) +{ + DWORD dwPatchCount = 0; + + // Retrieve the patch count + pLogger->PrintProgress("Verifying patch count for %s ...", szFileName); + dwPatchCount = GetFilePatchCount(pLogger, hMpq, szFileName); + + // Check if there are any patches at all + if(dwExpectedPatchCount != 0 && dwPatchCount == 0) + { + pLogger->PrintMessage("There are no patches for %s", szFileName); + return ERROR_FILE_CORRUPT; + } + + // Check if the number of patches fits + if(dwPatchCount != dwExpectedPatchCount) + { + pLogger->PrintMessage("Unexpected number of patches for %s", szFileName); + return ERROR_FILE_CORRUPT; + } + + return ERROR_SUCCESS; +} + +static DWORD CreateEmptyFile(TLogHelper * pLogger, LPCTSTR szPlainName, ULONGLONG FileSize, TCHAR * szBuffer) +{ + TFileStream * pStream; + TCHAR szFullPath[MAX_PATH]; + + // Notify the user + pLogger->PrintProgress(_T("Creating empty file %s ..."), szPlainName); + + // Construct the full path and crete the file + CreateFullPathName(szFullPath, _countof(szFullPath), NULL, szPlainName); + pStream = FileStream_CreateFile(szFullPath, STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE); + if(pStream == NULL) + return pLogger->PrintError(_T("Failed to create file %s"), szBuffer); + + // Write the required size + FileStream_SetSize(pStream, FileSize); + FileStream_Close(pStream); + + // Give the caller the full file name + if(szBuffer != NULL) + _tcscpy(szBuffer, szFullPath); + return ERROR_SUCCESS; +} + +static DWORD VerifyFilePosition( + TLogHelper * pLogger, + TFileStream * pStream, + ULONGLONG ExpectedPosition) +{ + ULONGLONG ByteOffset = 0; + DWORD dwErrCode = ERROR_SUCCESS; + + // Retrieve the file position + if(FileStream_GetPos(pStream, &ByteOffset)) + { + if(ByteOffset != ExpectedPosition) + { + pLogger->PrintMessage(_T("The file position is different than expected (expected: ") fmt_I64u_t _T(", current: ") fmt_I64u_t, ExpectedPosition, ByteOffset); + dwErrCode = ERROR_FILE_CORRUPT; + } + } + else + { + dwErrCode = pLogger->PrintError(_T("Failed to retrieve the file offset")); + } + + return dwErrCode; +} + +static DWORD VerifyFileMpqHeader(TLogHelper * pLogger, TFileStream * pStream, ULONGLONG * pByteOffset) +{ + TMPQHeader Header; + DWORD dwErrCode = ERROR_SUCCESS; + + memset(&Header, 0xFE, sizeof(TMPQHeader)); + if(FileStream_Read(pStream, pByteOffset, &Header, sizeof(TMPQHeader))) + { + if(Header.dwID != g_dwMpqSignature) + { + pLogger->PrintMessage(_T("Read error - the data is not a MPQ header")); + dwErrCode = ERROR_FILE_CORRUPT; + } + } + else + { + dwErrCode = pLogger->PrintError(_T("Failed to read the MPQ header")); + } + + return dwErrCode; +} + +static DWORD WriteMpqUserDataHeader( + TLogHelper * pLogger, + TFileStream * pStream, + ULONGLONG ByteOffset, + DWORD dwByteCount) +{ + TMPQUserData UserData; + DWORD dwErrCode = ERROR_SUCCESS; + + // Notify the user + pLogger->PrintProgress("Writing user data header..."); + + // Fill the user data header + UserData.dwID = ID_MPQ_USERDATA; + UserData.cbUserDataSize = dwByteCount; + UserData.dwHeaderOffs = (dwByteCount + sizeof(TMPQUserData)); + UserData.cbUserDataHeader = dwByteCount / 2; + if(!FileStream_Write(pStream, &ByteOffset, &UserData, sizeof(TMPQUserData))) + dwErrCode = SErrGetLastError(); + return dwErrCode; +} + +static DWORD WriteFileData( + TLogHelper * pLogger, + TFileStream * pStream, + ULONGLONG ByteOffset, + ULONGLONG ByteCount) +{ + ULONGLONG SaveByteCount = ByteCount; + ULONGLONG BytesWritten = 0; + LPBYTE pbDataBuffer; + DWORD cbDataBuffer = 0x10000; + DWORD dwErrCode = ERROR_SUCCESS; + + // Write some data + pbDataBuffer = new BYTE[cbDataBuffer]; + if(pbDataBuffer != NULL) + { + memset(pbDataBuffer, 0, cbDataBuffer); + strcpy((char *)pbDataBuffer, "This is a test data written to a file."); + + // Perform the write + while(ByteCount > 0) + { + DWORD cbToWrite = (ByteCount > cbDataBuffer) ? cbDataBuffer : (DWORD)ByteCount; + + // Notify the user + pLogger->PrintProgress("Writing file data " fmt_X_of_Y_a " ...", BytesWritten, SaveByteCount); + + // Write the data + if(!FileStream_Write(pStream, &ByteOffset, pbDataBuffer, cbToWrite)) + { + dwErrCode = SErrGetLastError(); + break; + } + + BytesWritten += cbToWrite; + ByteOffset += cbToWrite; + ByteCount -= cbToWrite; + } + + delete [] pbDataBuffer; + } + return dwErrCode; +} + +static DWORD CopyFileData( + TLogHelper * pLogger, + TFileStream * pStream1, + TFileStream * pStream2, + ULONGLONG ByteOffset, + ULONGLONG ByteCount) +{ + ULONGLONG BytesCopied = 0; + ULONGLONG EndOffset = ByteOffset + ByteCount; + LPBYTE pbCopyBuffer; + DWORD BytesToRead; + DWORD BlockLength = 0x100000; + DWORD dwErrCode = ERROR_SUCCESS; + + // Allocate copy buffer + pbCopyBuffer = STORM_ALLOC(BYTE, BlockLength); + if(pbCopyBuffer != NULL) + { + while(ByteOffset < EndOffset) + { + // Read source + BytesToRead = ((EndOffset - ByteOffset) > BlockLength) ? BlockLength : (DWORD)(EndOffset - ByteOffset); + if(!FileStream_Read(pStream1, &ByteOffset, pbCopyBuffer, BytesToRead)) + { + dwErrCode = SErrGetLastError(); + break; + } + + // Write to the destination file + if(!FileStream_Write(pStream2, NULL, pbCopyBuffer, BytesToRead)) + { + dwErrCode = SErrGetLastError(); + break; + } + + // Increment the byte counts + BytesCopied += BytesToRead; + ByteOffset += BytesToRead; + + // Notify the user + pLogger->PrintProgress("Copying " fmt_X_of_Y_a " ...", BytesCopied, ByteCount); + } + + STORM_FREE(pbCopyBuffer); + } + + return dwErrCode; +} + +// Support function for copying file +static DWORD CreateFileCopy( + TLogHelper * pLogger, + LPCTSTR szPlainName, + LPCTSTR szFileCopy, + TCHAR * szBuffer = NULL, + size_t cchBuffer = 0, + ULONGLONG PreMpqDataSize = 0, + ULONGLONG UserDataSize = 0) +{ + TFileStream * pStream1; // Source file + TFileStream * pStream2; // Target file + ULONGLONG ByteOffset = 0; + ULONGLONG FileSize = 0; + TCHAR szFileName1[MAX_PATH]; + TCHAR szFileName2[MAX_PATH]; + DWORD dwErrCode = ERROR_SUCCESS; + + // Notify the user + szPlainName += FileStream_Prefix(szPlainName, NULL); + pLogger->PrintProgress(_T("Creating copy of %s ..."), szPlainName); + + // Construct both file names. Check if they are not the same + CreateFullPathName(szFileName1, _countof(szFileName1), szMpqSubDir, szPlainName); + CreateFullPathName(szFileName2, _countof(szFileName2), NULL, szFileCopy + FileStream_Prefix(szFileCopy, NULL)); + if(!_tcsicmp(szFileName1, szFileName2)) + { + pLogger->PrintError("Failed to create copy of MPQ (the copy name is the same like the original name)"); + return ERROR_CAN_NOT_COMPLETE; + } + + // Open the source file + pStream1 = FileStream_OpenFile(szFileName1, STREAM_FLAG_READ_ONLY); + if(pStream1 == NULL) + { + pLogger->PrintError(_T("Failed to open the source file %s"), szFileName1); + return ERROR_CAN_NOT_COMPLETE; + } + + // Create the destination file + pStream2 = FileStream_CreateFile(szFileName2, 0); + if(pStream2 != NULL) + { + // If we should write some pre-MPQ data to the target file, do it + if(PreMpqDataSize != 0) + { + dwErrCode = WriteFileData(pLogger, pStream2, ByteOffset, PreMpqDataSize); + ByteOffset += PreMpqDataSize; + } + + // If we should write some MPQ user data, write the header first + if(UserDataSize != 0) + { + dwErrCode = WriteMpqUserDataHeader(pLogger, pStream2, ByteOffset, (DWORD)UserDataSize); + ByteOffset += sizeof(TMPQUserData); + + dwErrCode = WriteFileData(pLogger, pStream2, ByteOffset, UserDataSize); + ByteOffset += UserDataSize; + } + + // Copy the file data from the source file to the destination file + FileStream_GetSize(pStream1, &FileSize); + if(FileSize != 0) + { + dwErrCode = CopyFileData(pLogger, pStream1, pStream2, 0, FileSize); + ByteOffset += FileSize; + } + FileStream_Close(pStream2); + } + + // Close the source file + FileStream_Close(pStream1); + + // Create the full file name of the target file, including prefix + if(szBuffer && cchBuffer) + CreateFullPathName(szBuffer, cchBuffer, NULL, szFileCopy); + + // Report error, if any + if(dwErrCode != ERROR_SUCCESS) + pLogger->PrintError("Failed to create copy of MPQ"); + return dwErrCode; +} + +static DWORD CreateMasterAndMirrorPaths( + TLogHelper * pLogger, + TCHAR * szMirrorPath, + TCHAR * szMasterPath, + LPCTSTR szMirrorName, + LPCTSTR szMasterName, + bool bCopyMirrorFile) +{ + TCHAR szCopyPath[MAX_PATH]; + DWORD dwErrCode = ERROR_SUCCESS; + + // Always delete the mirror file + CreateFullPathName(szMasterPath, MAX_PATH, szMpqSubDir, szMasterName); + CreateFullPathName(szCopyPath, _countof(szCopyPath), NULL, szMirrorName); + _tremove(szCopyPath + FileStream_Prefix(szCopyPath, NULL)); + + // Copy the mirrored file from the source to the work directory + if(bCopyMirrorFile) + dwErrCode = CreateFileCopy(pLogger, szMirrorName, szMirrorName); + + // Create the mirror*master path + if(dwErrCode == ERROR_SUCCESS) + _stprintf(szMirrorPath, _T("%s*%s"), szCopyPath, szMasterPath); + + return dwErrCode; +} + +static void WINAPI AddFileCallback(void * pvUserData, DWORD dwBytesWritten, DWORD dwTotalBytes, bool bFinalCall) +{ + TLogHelper * pLogger = (TLogHelper *)pvUserData; + + // Keep compilers happy + STORMLIB_UNUSED(bFinalCall); + + pLogger->PrintProgress("Adding file (%s) (%u of %u) (%u of %u) ...", pLogger->UserString, + pLogger->UserCount, + pLogger->UserTotal, + dwBytesWritten, + dwTotalBytes); +} + +//----------------------------------------------------------------------------- +// MPQ file utilities + +#define SEARCH_FLAG_LOAD_FILES 0x00000001 // Test function should load all files in the MPQ +#define SEARCH_FLAG_HASH_FILES 0x00000002 // Test function should load all files in the MPQ +#define SEARCH_FLAG_PLAY_WAVES 0x00000004 // Play extracted WAVE files +#define SEARCH_FLAG_IGNORE_ERRORS 0x00000008 // Ignore files that failed to open + +static bool CheckIfFileIsPresent(TLogHelper * pLogger, HANDLE hMpq, LPCSTR szFileName, DWORD bShouldExist) +{ + HANDLE hFile = NULL; + + if(SFileOpenFileEx(hMpq, szFileName, 0, &hFile)) + { + if(!bShouldExist) + pLogger->PrintMessage("The file \"%s\" is present, but it should not be", szFileName); + SFileCloseFile(hFile); + return true; + } + else + { + if(bShouldExist) + pLogger->PrintMessage("The file \"%s\" is not present, but it should be", szFileName); + return false; + } +} + +static DWORD LoadLocalFileMD5(TLogHelper * pLogger, LPCTSTR szFileFullName, LPBYTE md5_file_local) +{ + PFILE_DATA pFileData; + + // Load the local file to memory + if((pFileData = LoadLocalFile(pLogger, szFileFullName, true)) == NULL) + { + return pLogger->PrintError(_T("The file \"%s\" could not be loaded"), szFileFullName); + } + + // Calculate the hash + CalculateDataBlockHash(pFileData->FileData, pFileData->dwFileSize, md5_file_local); + STORM_FREE(pFileData); + return ERROR_SUCCESS; +} + +static DWORD LoadMpqFile(TLogHelper & Logger, HANDLE hMpq, LPCSTR szFileName, LCID lcFileLocale, DWORD dwSearchFlags, PFILE_DATA * ppFileData) +{ + PFILE_DATA pFileData = NULL; + HANDLE hFile; + DWORD dwFileSizeHi = 0xCCCCCCCC; + DWORD dwFileSizeLo = 0; + DWORD dwBytesRead; + DWORD dwCrc32 = 0; + DWORD dwErrCode = ERROR_SUCCESS; + TCHAR szSafeName[1024]; + + // Print the file name to the console. + // Prevent bad UTF-8 sequences to go through + SMemUTF8ToFileName(szSafeName, _countof(szSafeName), szFileName, NULL, 0, NULL); + Logger.PrintProgress(_T("Loading file %s ..."), GetShortPlainName(szSafeName)); + +#if defined(_MSC_VER) && defined(_DEBUG) + //if(!_stricmp(szFileName, "(signature)")) + // __debugbreak(); +#endif + + // Make sure that we open the proper locale file + SFileSetLocale(lcFileLocale); + *ppFileData = NULL; + + // Open the file from MPQ + if(SFileOpenFileEx(hMpq, szFileName, 0, &hFile)) + { + // Get the CRC32 of the file + SFileGetFileInfo(hFile, SFileInfoCRC32, &dwCrc32, sizeof(dwCrc32), NULL); + + // Get the size of the file + if(dwErrCode == ERROR_SUCCESS) + { + dwFileSizeLo = SFileGetFileSize(hFile, &dwFileSizeHi); + if(dwFileSizeLo == SFILE_INVALID_SIZE || dwFileSizeHi != 0) + dwErrCode = Logger.PrintError("Failed to query the file size"); + } + + // Spazzler protector: Creates fake files with size of 0x7FFFE7CA + if(dwErrCode == ERROR_SUCCESS && dwFileSizeLo > 0x1FFFFFFF) + { + dwErrCode = ERROR_FILE_CORRUPT; + } + + // Allocate buffer for the file content + if(dwErrCode == ERROR_SUCCESS) + { + pFileData = (PFILE_DATA)STORM_ALLOC(BYTE, sizeof(FILE_DATA) + dwFileSizeLo); + if(pFileData == NULL) + { + Logger.PrintError("Failed to allocate buffer for the file content"); + dwErrCode = ERROR_NOT_ENOUGH_MEMORY; + } + } + + // Get the file index of the MPQ file + if(dwErrCode == ERROR_SUCCESS) + { + // Store the file size + memset(pFileData, 0, sizeof(FILE_DATA) + dwFileSizeLo); + pFileData->dwFileSize = dwFileSizeLo; + pFileData->dwCrc32 = dwCrc32; + + // Retrieve the block index and file flags + if(!SFileGetFileInfo(hFile, SFileInfoFileIndex, &pFileData->dwBlockIndex, sizeof(DWORD), NULL)) + dwErrCode = Logger.PrintError("Failed retrieve the file index of %s", szFileName); + if(!SFileGetFileInfo(hFile, SFileInfoFlags, &pFileData->dwFlags, sizeof(DWORD), NULL)) + dwErrCode = Logger.PrintError("Failed retrieve the file flags of %s", szFileName); + } + + // Load the entire file + if(dwErrCode == ERROR_SUCCESS) + { + // Read the file data + SFileReadFile(hFile, pFileData->FileData, dwFileSizeLo, &dwBytesRead, NULL); + if(dwBytesRead != dwFileSizeLo) + dwErrCode = ERROR_FILE_CORRUPT; + } + + // If succeeded, check the CRC + if(dwErrCode == ERROR_SUCCESS && dwCrc32 != 0 && (dwSearchFlags & SEARCH_FLAG_IGNORE_ERRORS) == 0) + { + dwCrc32 = crc32(0, (Bytef *)pFileData->FileData, (uInt)pFileData->dwFileSize); + if(dwCrc32 != pFileData->dwCrc32) + Logger.PrintMessage("Warning: CRC32 error on %s", szFileName); + } + + SFileCloseFile(hFile); + } + else + { + if((dwSearchFlags & SEARCH_FLAG_IGNORE_ERRORS) == 0 && SErrGetLastError() != ERROR_FILE_DELETED) + { + dwErrCode = Logger.PrintError("Open failed: %s", szFileName); + } + } + + // If something failed, free the file data + if((dwErrCode != ERROR_SUCCESS) && (pFileData != NULL)) + { + STORM_FREE(pFileData); + pFileData = NULL; + } + + // Return what we got + if(ppFileData != NULL) + ppFileData[0] = pFileData; + return dwErrCode; +} + +static DWORD LoadMpqFileMD5(TLogHelper & Logger, HANDLE hMpq, LPCSTR szArchivedName, LPBYTE md5_file_in_mpq1) +{ + PFILE_DATA pFileData = NULL; + DWORD dwErrCode; + + // Load the MPQ to memory + dwErrCode = LoadMpqFile(Logger, hMpq, szArchivedName, 0, 0, &pFileData); + if(dwErrCode == ERROR_SUCCESS && pFileData != NULL) + { + CalculateDataBlockHash(pFileData->FileData, pFileData->dwFileSize, md5_file_in_mpq1); + STORM_FREE(pFileData); + return ERROR_SUCCESS; + } + + Logger.PrintError("The file \"%s\" is not in the archive", szArchivedName); + return dwErrCode; +} + +static DWORD CompareTwoLocalFilesRR( + TLogHelper * pLogger, + TFileStream * pStream1, // Master file + TFileStream * pStream2, // Mirror file + int nIterations) // Number of iterations +{ + ULONGLONG RandomNumber = 0x12345678; // We need pseudo-random number that will repeat each run of the program + ULONGLONG RandomSeed; + ULONGLONG ByteOffset; + ULONGLONG FileSize1 = 1; + ULONGLONG FileSize2 = 2; + DWORD BytesToRead; + DWORD Difference; + LPBYTE pbBuffer1; + LPBYTE pbBuffer2; + DWORD cbBuffer = 0x100000; + DWORD dwErrCode = ERROR_SUCCESS; + + // Compare file sizes + FileStream_GetSize(pStream1, &FileSize1); + FileStream_GetSize(pStream2, &FileSize2); + if(FileSize1 != FileSize2) + { + pLogger->PrintMessage("The files have different size"); + return ERROR_CAN_NOT_COMPLETE; + } + + // Allocate both buffers + pbBuffer1 = STORM_ALLOC(BYTE, cbBuffer); + pbBuffer2 = STORM_ALLOC(BYTE, cbBuffer); + if(pbBuffer1 && pbBuffer2) + { + // Perform many random reads + for(int i = 0; i < nIterations; i++) + { + // Generate psudo-random offsrt and data size + ByteOffset = (RandomNumber % FileSize1); + BytesToRead = (DWORD)(RandomNumber % cbBuffer); + + // Show the progress message + pLogger->PrintProgress("Comparing file: Offset: " fmt_I64u_a ", Length: %u", ByteOffset, BytesToRead); + + // Only perform read if the byte offset is below + if(ByteOffset < FileSize1) + { + if((ByteOffset + BytesToRead) > FileSize1) + BytesToRead = (DWORD)(FileSize1 - ByteOffset); + + memset(pbBuffer1, 0xEE, cbBuffer); + memset(pbBuffer2, 0xAA, cbBuffer); + + FileStream_Read(pStream1, &ByteOffset, pbBuffer1, BytesToRead); + FileStream_Read(pStream2, &ByteOffset, pbBuffer2, BytesToRead); + + if(!CompareBlocks(pbBuffer1, pbBuffer2, BytesToRead, &Difference)) + { + pLogger->PrintMessage("Difference at %u (Offset " fmt_I64X_a ", Length %X)", Difference, ByteOffset, BytesToRead); + dwErrCode = ERROR_FILE_CORRUPT; + break; + } + + // Shuffle the random number + memcpy(&RandomSeed, pbBuffer1, sizeof(RandomSeed)); + RandomNumber = ((RandomNumber >> 0x11) | (RandomNumber << 0x29)) ^ (RandomNumber + RandomSeed); + } + } + } + + // Free both buffers + if(pbBuffer2 != NULL) + STORM_FREE(pbBuffer2); + if(pbBuffer1 != NULL) + STORM_FREE(pbBuffer1); + return dwErrCode; +} + +static DWORD SearchArchive( + TLogHelper * pLogger, + HANDLE hMpq, + DWORD dwSearchFlags = 0, + DWORD * pdwFileCount = NULL, + LPBYTE pbFileHash = NULL) +{ + SFILE_FIND_DATA sf; + PFILE_DATA pFileData; + HANDLE hFind; + FILE * fp = NULL; + DWORD dwFileCount = 0; + hash_state md5state; + TCHAR szListFile[MAX_PATH] = _T(""); + DWORD dwErrCode = ERROR_SUCCESS; + bool bFound = true; + + // Construct the full name of the listfile + CreateFullPathName(szListFile, _countof(szListFile), szListFileDir, _T("ListFile_Blizzard.txt")); + + // Create the log file with file sizes and CRCs + //fp = fopen("C:\\mpq-listing.txt", "wt"); + + // Prepare hashing + md5_init(&md5state); + + // Initiate the MPQ search + pLogger->PrintProgress("Searching the archive (initializing) ..."); + hFind = SFileFindFirstFile(hMpq, "*", &sf, szListFile); + if(hFind == NULL) + { + dwErrCode = SErrGetLastError(); + dwErrCode = (dwErrCode == ERROR_NO_MORE_FILES) ? ERROR_SUCCESS : dwErrCode; + return dwErrCode; + } + + // Perform the search + pLogger->PrintProgress("Searching the archive ..."); + while(bFound == true) + { + // Increment number of files + dwFileCount++; + + // Load the file to memory, if required + if(dwSearchFlags & SEARCH_FLAG_LOAD_FILES) + { + // Load the entire file to the MPQ + if(LoadMpqFile(*pLogger, hMpq, sf.cFileName, sf.lcLocale, dwSearchFlags, &pFileData) == ERROR_SUCCESS && pFileData != NULL) + { + // Hash the file data, if needed + if((dwSearchFlags & SEARCH_FLAG_HASH_FILES) && !IsInternalMpqFileName(sf.cFileName)) + md5_process(&md5state, pFileData->FileData, pFileData->dwFileSize); + + // Play sound files, if required + if((dwSearchFlags & SEARCH_FLAG_PLAY_WAVES) && strstr(sf.cFileName, ".wav") != NULL) + { + pLogger->PrintProgress("Playing sound %s", sf.cFileName); + PlayWaveSound(pFileData); + } + + // Debug: Show CRC32 of each file in order to debug differences + if(fp != NULL) + { + pFileData->dwCrc32 = crc32(0, pFileData->FileData, pFileData->dwFileSize); + fprintf(fp, "%08x:%08x: %s \n", pFileData->dwFileSize, pFileData->dwCrc32, sf.cFileName); + } + + // Also write the content of the file to the test directory + //if(fp != NULL) + //{ + // FILE * fp2; + // char szFullPath[MAX_PATH] = "C:\\test\\"; + + // strcat(szFullPath, sf.cFileName); + // if((fp2 = fopen(szFullPath, "wb")) != NULL) + // { + // fwrite(pFileData->FileData, 1, pFileData->dwFileSize, fp2); + // fclose(fp2); + // } + //} + + // Free the loaded file data + STORM_FREE(pFileData); + } + } + + // The last file that was OK + bFound = SFileFindNextFile(hFind, &sf); + } + SFileFindClose(hFind); + + // Give the file count, if required + if(pdwFileCount != NULL) + pdwFileCount[0] = dwFileCount; + + // Give the hash, if required + if(pbFileHash != NULL && (dwSearchFlags & SEARCH_FLAG_HASH_FILES)) + md5_done(&md5state, pbFileHash); + + // Close the log file, if any + if(fp != NULL) + fclose(fp); + return dwErrCode; +} + +static DWORD VerifyDataChecksum(TLogHelper & Logger, HANDLE hMpq, DWORD dwSearchFlags, LPCSTR szExpectedHash, DWORD dwExpectedFileCount) +{ + DWORD dwErrCode = ERROR_SUCCESS; + DWORD dwFileCount = 0; + BYTE NameHash[MD5_DIGEST_SIZE] = {0}; + char szNameHash[0x40]; + + // Do nothing if no name hash and no known file count + if(IS_VALID_STRING(szExpectedHash) || (dwExpectedFileCount != 0)) + { + // Search the archive, obtain file count and name hash + if((dwErrCode = SearchArchive(&Logger, hMpq, dwSearchFlags, &dwFileCount, NameHash)) != ERROR_SUCCESS) + { + Logger.PrintMessage("Failed to search the archive"); + return dwErrCode; + } + + // Check the file count, if given + if((dwExpectedFileCount != 0) && (dwExpectedFileCount != dwFileCount)) + { + Logger.PrintMessage("File count mismatch(expected: %u, found: %u)", dwExpectedFileCount, dwFileCount); + dwErrCode = ERROR_CAN_NOT_COMPLETE; + } + + // Check the MD5 hash, if given + if(IS_VALID_STRING(szNameHash)) + { + SMemBinToStr(szNameHash, _countof(szNameHash), NameHash, MD5_DIGEST_SIZE); + if(_stricmp(szNameHash, szExpectedHash)) + { + Logger.PrintMessage("Extracted files MD5 mismatch (expected: %s, obtained: %s)", szExpectedHash, szNameHash); + dwErrCode = ERROR_CAN_NOT_COMPLETE; + } + } + } + return dwErrCode; +} + +static DWORD CreateNewArchive(TLogHelper * pLogger, LPCTSTR szPlainName, DWORD dwCreateFlags, DWORD dwMaxFileCount, HANDLE * phMpq) +{ + HANDLE hMpq = NULL; + TCHAR szMpqName[MAX_PATH]; + TCHAR szFullPath[MAX_PATH]; + + // Make sure that the MPQ is deleted + CreateFullPathName(szFullPath, _countof(szFullPath), NULL, szPlainName); + _tremove(szFullPath); + + // Create the new MPQ + StringCopy(szMpqName, _countof(szMpqName), szFullPath); + if(!SFileCreateArchive(szMpqName, dwCreateFlags, dwMaxFileCount, &hMpq)) + return pLogger->PrintError(_T("Failed to create archive %s"), szMpqName); + + // Shall we close it right away? + if(phMpq == NULL) + SFileCloseArchive(hMpq); + else + *phMpq = hMpq; + + return ERROR_SUCCESS; +} + +static DWORD CreateNewArchive_V2(TLogHelper * pLogger, LPCTSTR szPlainName, DWORD dwCreateFlags, DWORD dwMaxFileCount, HANDLE * phMpq) +{ + SFILE_CREATE_MPQ CreateInfo; + HANDLE hMpq = NULL; + TCHAR szMpqName[MAX_PATH]; + TCHAR szFullPath[MAX_PATH]; + + // Make sure that the MPQ is deleted + CreateFullPathName(szFullPath, _countof(szFullPath), NULL, szPlainName); + StringCopy(szMpqName, _countof(szMpqName), szFullPath); + _tremove(szFullPath); + + // Fill the create structure + memset(&CreateInfo, 0, sizeof(SFILE_CREATE_MPQ)); + CreateInfo.cbSize = sizeof(SFILE_CREATE_MPQ); + CreateInfo.dwMpqVersion = (dwCreateFlags & MPQ_CREATE_ARCHIVE_VMASK) >> FLAGS_TO_FORMAT_SHIFT; + CreateInfo.dwStreamFlags = STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE; +// CreateInfo.dwFileFlags1 = (dwCreateFlags & MPQ_CREATE_LISTFILE) ? MPQ_FILE_EXISTS : 0; +// CreateInfo.dwFileFlags2 = (dwCreateFlags & MPQ_CREATE_ATTRIBUTES) ? MPQ_FILE_EXISTS : 0; + CreateInfo.dwFileFlags1 = (dwCreateFlags & MPQ_CREATE_LISTFILE) ? MPQ_FILE_DEFAULT_INTERNAL : 0; + CreateInfo.dwFileFlags2 = (dwCreateFlags & MPQ_CREATE_ATTRIBUTES) ? MPQ_FILE_DEFAULT_INTERNAL : 0; + CreateInfo.dwFileFlags3 = (dwCreateFlags & MPQ_CREATE_SIGNATURE) ? MPQ_FILE_DEFAULT_INTERNAL : 0; + CreateInfo.dwAttrFlags = (dwCreateFlags & MPQ_CREATE_ATTRIBUTES) ? (MPQ_ATTRIBUTE_CRC32 | MPQ_ATTRIBUTE_FILETIME | MPQ_ATTRIBUTE_MD5) : 0; + CreateInfo.dwSectorSize = (CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_3) ? 0x4000 : 0x1000; + CreateInfo.dwRawChunkSize = (CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_4) ? 0x4000 : 0; + CreateInfo.dwMaxFileCount = dwMaxFileCount; + + // Create the new MPQ + if(!SFileCreateArchive2(szMpqName, &CreateInfo, &hMpq)) + return pLogger->PrintError(_T("Failed to create archive %s"), szMpqName); + + // Shall we close it right away? + if(phMpq == NULL) + SFileCloseArchive(hMpq); + else + *phMpq = hMpq; + + return ERROR_SUCCESS; +} + +static DWORD OpenExistingArchive(TLogHelper * pLogger, LPCTSTR szFullPath, DWORD dwOpenFlags, HANDLE * phMpq) +{ + HANDLE hMpq = NULL; + size_t nMarkerIndex; + DWORD dwErrCode = ERROR_SUCCESS; + + // Get the stream provider from the MPQ prefix or MPQ name + if(_tcsnicmp(szFullPath, _T("flat-file://"), 11)) + { + if(_tcsstr(szFullPath, _T(".MPQE")) != NULL) + dwOpenFlags |= STREAM_PROVIDER_MPQE; + if(_tcsstr(szFullPath, _T(".MPQ.part")) != NULL) + dwOpenFlags |= STREAM_PROVIDER_PARTIAL; + if(_tcsstr(szFullPath, _T(".mpq.part")) != NULL) + dwOpenFlags |= STREAM_PROVIDER_PARTIAL; + if(_tcsstr(szFullPath, _T(".MPQ.0")) != NULL) + dwOpenFlags |= STREAM_PROVIDER_BLOCK4; + } + + // Handle ASI files properly + nMarkerIndex = (_tcsstr(szFullPath, _T(".asi")) != NULL) ? 1 : 0; + SFileSetArchiveMarkers(&MpqMarkers[nMarkerIndex]); + + // Open the copied archive + pLogger->PrintProgress(_T("Opening archive %s ..."), GetShortPlainName(szFullPath)); + if(!SFileOpenArchive(szFullPath, 0, dwOpenFlags, &hMpq)) + { + switch(dwErrCode = SErrGetLastError()) + { +// case ERROR_BAD_FORMAT: // If the error is ERROR_BAD_FORMAT, try to open with MPQ_OPEN_FORCE_MPQ_V1 +// bReopenResult = SFileOpenArchive(szMpqName, 0, dwFlags | MPQ_OPEN_FORCE_MPQ_V1, &hMpq); +// dwErrCode = (bReopenResult == false) ? SErrGetLastError() : ERROR_SUCCESS; +// break; + + case ERROR_AVI_FILE: // Ignore the error if it's an AVI file or if the file is incomplete + case ERROR_FILE_INCOMPLETE: + return dwErrCode; + } + + // Show the open error to the user + if((dwOpenFlags & MPQ_OPEN_DONT_REPORT_FAILURE) == 0) + dwErrCode = pLogger->PrintError(_T("Failed to open archive %s"), szFullPath); + return dwErrCode; + } + + // Store the archive handle or close the archive + if(phMpq == NULL) + SFileCloseArchive(hMpq); + else + *phMpq = hMpq; + return dwErrCode; +} + +static DWORD OpenExistingArchiveWithCopy(TLogHelper * pLogger, LPCTSTR szFileName, LPCTSTR szCopyName, HANDLE * phMpq, DWORD dwOpenFlags = 0) +{ + DWORD dwErrCode = ERROR_SUCCESS; + TCHAR szFullPath[MAX_PATH]; + + // We expect MPQ directory to be already prepared by InitializeMpqDirectory + assert(szMpqDirectory[0] != 0); + + // At least one name must be entered + assert(szFileName != NULL || szCopyName != NULL); + + // If both names entered, create a copy + if(szFileName != NULL && szCopyName != NULL) + { + dwErrCode = CreateFileCopy(pLogger, szFileName, szCopyName, szFullPath, _countof(szFullPath)); + if(dwErrCode != ERROR_SUCCESS) + return dwErrCode; + } + + // If only source name entered, open it for read-only access + else if(szFileName != NULL && szCopyName == NULL) + { + CreateFullPathName(szFullPath, _countof(szFullPath), szMpqSubDir, szFileName); + dwOpenFlags |= MPQ_OPEN_READ_ONLY; + } + + // If only target name entered, open it directly + else if(szFileName == NULL && szCopyName != NULL) + { + CreateFullPathName(szFullPath, _countof(szFullPath), NULL, szCopyName); + } + + // Open the archive + return OpenExistingArchive(pLogger, szFullPath, dwOpenFlags, phMpq); +} + +static DWORD AddFileToMpq( + TLogHelper * pLogger, + HANDLE hMpq, + LPCSTR szFileName, + LPCSTR szFileData, + DWORD dwFlags = 0, + DWORD dwCompression = 0, + DWORD dwExpectedError = ERROR_SUCCESS) +{ + HANDLE hFile = NULL; + DWORD dwFileSize = (DWORD)strlen(szFileData); + DWORD dwErrCode = ERROR_SUCCESS; + + // Notify the user + pLogger->PrintProgress("Adding file %s ...", szFileName); + + // Get the default flags + if(dwFlags == 0) + dwFlags = MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED; + if(dwCompression == 0) + dwCompression = MPQ_COMPRESSION_ZLIB; + + // Create the file within the MPQ + if(SFileCreateFile(hMpq, szFileName, 0, dwFileSize, 0, dwFlags, &hFile)) + { + // Write the file + if(!SFileWriteFile(hFile, szFileData, dwFileSize, dwCompression)) + dwErrCode = pLogger->PrintError("Failed to write data to the MPQ"); + SFileCloseFile(hFile); + } + else + { + dwErrCode = SErrGetLastError(); + } + + // Check the expected error code + if(dwExpectedError != ERROR_UNDETERMINED_RESULT) + { + if(dwErrCode != dwExpectedError) + { + pLogger->PrintError("Unexpected result from SFileCreateFile(%s)", szFileName); + dwErrCode = ERROR_CAN_NOT_COMPLETE; + } + } + return dwErrCode; +} + +static DWORD AddLocalFileToMpq( + TLogHelper * pLogger, + HANDLE hMpq, + LPCSTR szArchivedName, + LPCTSTR szFileFullName, + DWORD dwFlags = 0, + DWORD dwCompression = 0, + bool bMustSucceed = false) +{ + TCHAR szFileName[MAX_PATH]; + DWORD dwVerifyResult; + + // Notify the user + pLogger->PrintProgress("Adding file %s (%u of %u)...", GetShortPlainName(szFileFullName), pLogger->UserCount, pLogger->UserTotal); + pLogger->UserString = szArchivedName; + + // Get the default flags + if(dwFlags == 0) + dwFlags = MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED; + if(dwCompression == 0) + dwCompression = MPQ_COMPRESSION_ZLIB; + + // Set the notification callback + SFileSetAddFileCallback(hMpq, AddFileCallback, pLogger); + + // Add the file to the MPQ + StringCopy(szFileName, _countof(szFileName), szFileFullName); + if(!SFileAddFileEx(hMpq, szFileName, szArchivedName, dwFlags, dwCompression, MPQ_COMPRESSION_NEXT_SAME)) + { + if(bMustSucceed) + return pLogger->PrintError("Failed to add the file %s", szArchivedName); + return SErrGetLastError(); + } + + // Verify the file unless it was lossy compression + if((dwCompression & (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO)) == 0) + { + // Notify the user + pLogger->PrintProgress("Verifying file %s (%u of %u) ...", szArchivedName, pLogger->UserCount, pLogger->UserTotal); + + // Perform the verification + dwVerifyResult = SFileVerifyFile(hMpq, szArchivedName, 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)) + return pLogger->PrintError("CRC error on %s", szArchivedName); + } + + return ERROR_SUCCESS; +} + +static DWORD RemoveMpqFile(TLogHelper * pLogger, HANDLE hMpq, LPCSTR szFileName, DWORD dwExpectedError) +{ + DWORD dwErrCode = ERROR_SUCCESS; + + // Notify the user + pLogger->PrintProgress("Removing file %s ...", szFileName); + + // Perform the deletion + if(!SFileRemoveFile(hMpq, szFileName, 0)) + dwErrCode = SErrGetLastError(); + + if(dwErrCode != dwExpectedError) + return pLogger->PrintError("Unexpected result from SFileRemoveFile(%s)", szFileName); + return ERROR_SUCCESS; +} + +//----------------------------------------------------------------------------- +// Tests + +static void TestGetFileInfo( + TLogHelper * pLogger, + HANDLE hMpqOrFile, + SFileInfoClass InfoClass, + void * pvFileInfo, + DWORD cbFileInfo, + DWORD * pcbLengthNeeded, + bool bExpectedResult, + DWORD dwExpectedErrCode) +{ + DWORD dwErrCode = ERROR_SUCCESS; + bool bResult; + + // Call the get file info + bResult = SFileGetFileInfo(hMpqOrFile, InfoClass, pvFileInfo, cbFileInfo, pcbLengthNeeded); + if(!bResult) + dwErrCode = SErrGetLastError(); + + // Check the expected results + if(bResult != bExpectedResult) + pLogger->PrintMessage("Different result of SFileGetFileInfo."); + if(dwErrCode != dwExpectedErrCode) + pLogger->PrintMessage("Different error from SFileGetFileInfo (expected %u, returned %u)", dwExpectedErrCode, dwErrCode); +} + +// StormLib is able to open local files (as well as the original Storm.dll) +// I want to keep this for occasional use + +static LINE_INFO Lines[] = +{ + {0x000, 18, "accountbilling.url"}, + {0x013, 45, "alternate/character/goblin/male/goblinmale.m2"}, + {0x9ab, 54, "alternate/character/goblin/male/goblinmale0186-00.anim"} +}; + +static DWORD TestOnLocalListFile_Read(TLogHelper & Logger, HANDLE hFile) +{ + for(size_t i = 0; i < _countof(Lines); i++) + { + DWORD dwBytesRead = 0; + char szFileLine[0x100] = {0}; + + SFileSetFilePointer(hFile, Lines[i].nLinePos, NULL, FILE_BEGIN); + SFileReadFile(hFile, szFileLine, Lines[i].nLineLen, &dwBytesRead, NULL); + + if(dwBytesRead != Lines[i].nLineLen) + { + Logger.PrintMessage("Line %u length mismatch", i); + return false; + } + + if(strcmp(szFileLine, Lines[i].szLine)) + { + Logger.PrintMessage("Line %u content mismatch", i); + return false; + } + } + + return true; +} + +static DWORD TestOnLocalListFile(LPCTSTR szPlainName) +{ + TLogHelper Logger("TestLiFiSearch", szPlainName); + SFILE_FIND_DATA sf; + HANDLE hFile; + HANDLE hFind; + DWORD dwFileSizeHi = 0; + DWORD dwFileSizeLo = 0; + TCHAR szFullPath[MAX_PATH]; + char szFileName1[MAX_PATH]; + char szFileName2[MAX_PATH]; + int nFileCount = 0; + + // Get the full name of the local file + CreateFullPathName(szFileName1, _countof(szFileName1), szDataFileDir, szPlainName); + + // Test opening the local file + if(SFileOpenFileEx(NULL, szFileName1, SFILE_OPEN_LOCAL_FILE, &hFile)) + { + // Retrieve the file name. It must match the name under which the file was open + if(FileStream_Prefix(szPlainName, NULL) == 0) + { + SFileGetFileName(hFile, szFileName2); + if(strcmp(szFileName2, szFileName1)) + Logger.PrintMessage("The retrieved name does not match the open name"); + } + + // Retrieve the file size + dwFileSizeLo = SFileGetFileSize(hFile, &dwFileSizeHi); + if(dwFileSizeHi != 0 || dwFileSizeLo != 0x04385a4e) + Logger.PrintMessage("Local file size mismatch"); + + // Read few lines, check their content + TestOnLocalListFile_Read(Logger, hFile); + SFileCloseFile(hFile); + } + else + return Logger.PrintError("Failed to open local listfile"); + + // We need unicode listfile name + StringCopy(szFullPath, _countof(szFullPath), szFileName1); + + // Start searching in the listfile + hFind = SListFileFindFirstFile(NULL, szFullPath, "*", &sf); + if(hFind != NULL) + { + for(;;) + { + Logger.PrintProgress("Found file (%04u): %s", nFileCount++, GetShortPlainName(sf.cFileName)); + if(!SListFileFindNextFile(hFind, &sf)) + break; + } + + SListFileFindClose(hFind); + } + else + return Logger.PrintError("Failed to search local listfile"); + + return ERROR_SUCCESS; +} + +static void WINAPI TestReadFile_DownloadCallback( + void * UserData, + ULONGLONG ByteOffset, + DWORD DataLength) +{ + TLogHelper * pLogger = (TLogHelper *)UserData; + + if(ByteOffset != 0 && DataLength != 0) + pLogger->PrintProgress("Downloading data (offset: " fmt_I64X_a ", length: %X)", ByteOffset, DataLength); + else + pLogger->PrintProgress("Download complete."); +} + +// Open a file stream with mirroring a master file +static DWORD TestReadFile_MasterMirror(LPCTSTR szMirrorName, LPCTSTR szMasterName, bool bCopyMirrorFile) +{ + TFileStream * pStream1; // Master file + TFileStream * pStream2; // Mirror file + TLogHelper Logger("TestFileMirror", szMirrorName); + TCHAR szMirrorPath[MAX_PATH + MAX_PATH]; + TCHAR szMasterPath[MAX_PATH]; + DWORD dwProvider = 0; + int nIterations = 0x10000; + DWORD dwErrCode; + + // Retrieve the provider + FileStream_Prefix(szMasterName, &dwProvider); + +#ifndef STORMLIB_WINDOWS + if((dwProvider & BASE_PROVIDER_MASK) == BASE_PROVIDER_HTTP) + return ERROR_SUCCESS; +#endif + + // Create copy of the file to serve as mirror, keep master there + dwErrCode = CreateMasterAndMirrorPaths(&Logger, szMirrorPath, szMasterPath, szMirrorName, szMasterName, bCopyMirrorFile); + if(dwErrCode == ERROR_SUCCESS) + { + // Open both master and mirror file + pStream1 = FileStream_OpenFile(szMasterPath, STREAM_FLAG_READ_ONLY); + pStream2 = FileStream_OpenFile(szMirrorPath, STREAM_FLAG_READ_ONLY | STREAM_FLAG_USE_BITMAP); + if(pStream1 && pStream2) + { + // For internet based files, we limit the number of operations + if((dwProvider & BASE_PROVIDER_MASK) == BASE_PROVIDER_HTTP) + nIterations = 0x80; + + FileStream_SetCallback(pStream2, TestReadFile_DownloadCallback, &Logger); + dwErrCode = CompareTwoLocalFilesRR(&Logger, pStream1, pStream2, nIterations); + } + + if(pStream2 != NULL) + FileStream_Close(pStream2); + if(pStream1 != NULL) + FileStream_Close(pStream1); + } + + return dwErrCode; +} + +// Test of the TFileStream object +static DWORD TestFileStreamOperations(LPCTSTR szPlainName, DWORD dwStreamFlags) +{ + TFileStream * pStream = NULL; + TLogHelper Logger("TestFileStream", szPlainName); + ULONGLONG ByteOffset; + ULONGLONG FileSize = 0; + TCHAR szFullPath[MAX_PATH]; + DWORD dwRequiredFlags = 0; + BYTE Buffer[0x10]; + DWORD dwErrCode = ERROR_SUCCESS; + + // Copy the file so we won't screw up + if((dwStreamFlags & STREAM_PROVIDER_MASK) == STREAM_PROVIDER_BLOCK4) + CreateFullPathName(szFullPath, _countof(szFullPath), szMpqSubDir, szPlainName); + else + dwErrCode = CreateFileCopy(&Logger, szPlainName, szPlainName, szFullPath, _countof(szFullPath)); + + // Open the file stream + if(dwErrCode == ERROR_SUCCESS) + { + pStream = FileStream_OpenFile(szFullPath, dwStreamFlags); + if(pStream == NULL) + return Logger.PrintError(_T("Open failed: %s"), szFullPath); + } + + // Get the size of the file stream + if(dwErrCode == ERROR_SUCCESS) + { + if(!FileStream_GetFlags(pStream, &dwStreamFlags)) + dwErrCode = Logger.PrintError("Failed to retrieve the stream flags"); + + if(!FileStream_GetSize(pStream, &FileSize)) + dwErrCode = Logger.PrintError("Failed to retrieve the file size"); + + // Any other stream except STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE should be read-only + if((dwStreamFlags & STREAM_PROVIDERS_MASK) != (STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE)) + dwRequiredFlags |= STREAM_FLAG_READ_ONLY; +// if(pStream->BlockPresent) +// dwRequiredFlags |= STREAM_FLAG_READ_ONLY; + + // Check the flags there + if((dwStreamFlags & dwRequiredFlags) != dwRequiredFlags) + { + Logger.PrintMessage("The stream should be read-only but it isn't"); + dwErrCode = ERROR_FILE_CORRUPT; + } + } + + // After successful open, the stream position must be zero + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = VerifyFilePosition(&Logger, pStream, 0); + + // Read the MPQ header from the current file offset. + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = VerifyFileMpqHeader(&Logger, pStream, NULL); + + // After successful open, the stream position must sizeof(TMPQHeader) + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = VerifyFilePosition(&Logger, pStream, sizeof(TMPQHeader)); + + // Now try to read the MPQ header from the offset 0 + if(dwErrCode == ERROR_SUCCESS) + { + ByteOffset = 0; + dwErrCode = VerifyFileMpqHeader(&Logger, pStream, &ByteOffset); + } + + // After successful open, the stream position must sizeof(TMPQHeader) + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = VerifyFilePosition(&Logger, pStream, sizeof(TMPQHeader)); + + // Try a write operation + if(dwErrCode == ERROR_SUCCESS) + { + bool bExpectedResult = (dwStreamFlags & STREAM_FLAG_READ_ONLY) ? false : true; + bool bResult; + + // Attempt to write to the file + ByteOffset = 0; + bResult = FileStream_Write(pStream, &ByteOffset, Buffer, sizeof(Buffer)); + + // If the result is not expected + if(bResult != bExpectedResult) + { + Logger.PrintMessage("FileStream_Write result is different than expected"); + dwErrCode = ERROR_FILE_CORRUPT; + } + } + + // Move the position 9 bytes from the end and try to read 10 bytes. + // This must fail, because stream reading functions are "all or nothing" + if(dwErrCode == ERROR_SUCCESS) + { + ByteOffset = FileSize - 9; + if(FileStream_Read(pStream, &ByteOffset, Buffer, 10)) + { + Logger.PrintMessage("FileStream_Read succeeded, but it shouldn't"); + dwErrCode = ERROR_FILE_CORRUPT; + } + } + + // Try again with 9 bytes. This must succeed, unless the file block is not available + if(dwErrCode == ERROR_SUCCESS) + { + ByteOffset = FileSize - 9; + if(!FileStream_Read(pStream, &ByteOffset, Buffer, 9)) + { + Logger.PrintMessage("FileStream_Read from the end of the file failed"); + dwErrCode = ERROR_FILE_CORRUPT; + } + } + + // Verify file position - it must be at the end of the file + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = VerifyFilePosition(&Logger, pStream, FileSize); + + // Close the stream + if(pStream != NULL) + FileStream_Close(pStream); + return dwErrCode; +} + +static DWORD TestArchive_LoadFiles(TLogHelper * pLogger, HANDLE hMpq, DWORD bIgnoreOpenErrors, ...) +{ + PFILE_DATA pFileData; + const char * szFileName; + va_list argList; + DWORD dwSearchFlags = (bIgnoreOpenErrors) ? SEARCH_FLAG_IGNORE_ERRORS : 0; + DWORD dwErrCode = ERROR_SUCCESS; + + va_start(argList, bIgnoreOpenErrors); + while((szFileName = va_arg(argList, const char *)) != NULL) + { + if(SFileHasFile(hMpq, szFileName)) + { + dwErrCode = LoadMpqFile(*pLogger, hMpq, szFileName, 0, dwSearchFlags, &pFileData); + if(dwErrCode != ERROR_SUCCESS && bIgnoreOpenErrors == 0) + { + pLogger->PrintError("Error loading the file %s", szFileName); + break; + } + else + { + dwErrCode = ERROR_SUCCESS; + STORM_FREE(pFileData); + pFileData = NULL; + } + } + } + va_end(argList); + + return dwErrCode; +} + +//----------------------------------------------------------------------------- +// Testing archive operations: Single archive + +static DWORD TestOpenArchive_VerifySignature(TLogHelper & Logger, HANDLE hMpq, DWORD dwDoItIfNonZero) +{ + DWORD dwSignatures = 0; + DWORD dwVerifyError; + + // Only do it if we asked to + if(dwDoItIfNonZero) + { + // Query the signature types + Logger.PrintProgress("Retrieving signatures ..."); + TestGetFileInfo(&Logger, hMpq, SFileMpqSignatures, &dwSignatures, sizeof(DWORD), NULL, true, ERROR_SUCCESS); + + // Are there some signatures at all? + if(dwSignatures == 0) + { + Logger.PrintMessage("No signatures present in the file"); + return ERROR_FILE_CORRUPT; + } + + // Verify any of the present signatures + Logger.PrintProgress("Verifying archive signature ..."); + dwVerifyError = SFileVerifyArchive(hMpq); + + // Verify the result + if((dwSignatures & SIGNATURE_TYPE_STRONG) && (dwVerifyError != ERROR_STRONG_SIGNATURE_OK)) + { + Logger.PrintMessage("Strong signature verification error"); + return ERROR_FILE_CORRUPT; + } + + // Verify the result + if((dwSignatures & SIGNATURE_TYPE_WEAK) && (dwVerifyError != ERROR_WEAK_SIGNATURE_OK)) + { + Logger.PrintMessage("Weak signature verification error"); + return ERROR_FILE_CORRUPT; + } + } + return ERROR_SUCCESS; +} + +static DWORD TestOpenArchive_Extra_ListFile(TLogHelper & Logger, HANDLE hMpq, PTEST_EXTRA_ONEFILE pExtra) +{ + DWORD dwErrCode = ERROR_SUCCESS; + TCHAR szFullName[MAX_PATH]; + + if(IS_VALID_STRING(pExtra->szFile)) + { + Logger.PrintProgress(_T("Adding listfile %s ..."), pExtra->szFile); + CreateFullPathName(szFullName, _countof(szFullName), szListFileDir, pExtra->szFile); + if((dwErrCode = SFileAddListFile(hMpq, szFullName)) != ERROR_SUCCESS) + Logger.PrintMessage("Failed to add the listfile to the MPQ"); + } + return dwErrCode; +} + +static DWORD TestOpenArchive_Extra_Utf8File(TLogHelper & Logger, HANDLE hMpq, PTEST_EXTRA_UTF8 pExtra) +{ + DWORD dwErrCode = ERROR_SUCCESS; + TCHAR szFullName[MAX_PATH]; + TCHAR szListName[MAX_PATH]; + + if(IS_VALID_STRING(pExtra->szListFile)) + { + StringCopy(szListName, _countof(szListName), (const char *)pExtra->szListFile); + Logger.PrintProgress(_T("Adding listfile %s ..."), szListName); + CreateFullPathName(szFullName, _countof(szFullName), szListFileDir, szListName); + if((dwErrCode = SFileAddListFile(hMpq, szFullName)) != ERROR_SUCCESS) + Logger.PrintMessage("Failed to add the listfile to the MPQ"); + } + return dwErrCode; +} + +static DWORD TestOpenArchive_Extra_TwoFiles(TLogHelper & Logger, HANDLE hMpq, DWORD dwSearchFlags, PTEST_EXTRA_TWOFILES pExtra) +{ + PFILE_DATA pFileData1 = NULL; + PFILE_DATA pFileData2 = NULL; + DWORD dwErrCode = ERROR_SUCCESS; + + // Perform actions + for(;;) + { + // Load and verify the first file, if any + if(IS_VALID_STRING(pExtra->szFile1)) + { + if((dwErrCode = LoadMpqFile(Logger, hMpq, pExtra->szFile1, 0, dwSearchFlags, &pFileData1)) != ERROR_SUCCESS) + break; + } + + // Load and verify the second file, if any + if(IS_VALID_STRING(pExtra->szFile2)) + { + if((dwErrCode = LoadMpqFile(Logger, hMpq, pExtra->szFile2, 0, dwSearchFlags, &pFileData2)) != ERROR_SUCCESS) + break; + } + + // If two files were given, they must be equal + if(pFileData1 && pFileData2) + { + dwErrCode = CompareTwoFiles(Logger, pFileData1, pFileData2); + } + break; + } + + // Free buffers and exit + if(pFileData2 != NULL) + STORM_FREE(pFileData2); + if(pFileData1 != NULL) + STORM_FREE(pFileData1); + return dwErrCode; +} + +static DWORD TestOpenArchive_Extra_Patches(TLogHelper & Logger, HANDLE hMpq, PTEST_EXTRA_PATCHES pExtra) +{ + LPCTSTR szPatch; + TCHAR szFullPath[MAX_PATH]; + DWORD dwErrCode = ERROR_SUCCESS; + + // Open all patches that are in the multi-SZ list + if(IS_VALID_STRING(pExtra->szPatchList)) + { + for(szPatch = pExtra->szPatchList; szPatch[0] != 0; szPatch = szPatch + _tcslen(szPatch) + 1) + { + Logger.PrintProgress(_T("Adding patch %s ..."), GetShortPlainName(szPatch)); + CreateFullPathName(szFullPath, _countof(szFullPath), szMpqPatchDir, szPatch); + if(!SFileOpenPatchArchive(hMpq, szFullPath, NULL, 0)) + return Logger.PrintError(_T("Failed to add patch %s ..."), szFullPath); + } + } + + // Verify the patch count of the given file + if(IS_VALID_STRING(pExtra->szFileName)) + dwErrCode = VerifyFilePatchCount(&Logger, hMpq, pExtra->szFileName, pExtra->dwPatchCount); + return dwErrCode; +} + +static DWORD TestOpenArchive_Extra_HashValues(TLogHelper & Logger, HANDLE hMpq, PTEST_EXTRA_HASHVALS pExtra) +{ + HANDLE hFile = NULL; + DWORD dwErrCode = ERROR_SUCCESS; + DWORD dwHash1 = 0; + DWORD dwHash2 = 0; + DWORD cbHash = 0; + + for(size_t i = 0; i < _countof(pExtra->Items); i++) + { + PTEST_EXTRA_HASHVAL pItem = &pExtra->Items[i]; + + if(SFileOpenFileEx(hMpq, pItem->szFileName, 0, &hFile)) + { + if(SFileGetFileInfo(hFile, SFileInfoNameHash1, &dwHash1, sizeof(dwHash1), &cbHash)) + { + assert(cbHash == sizeof(DWORD)); + } + + if(SFileGetFileInfo(hFile, SFileInfoNameHash2, &dwHash2, sizeof(dwHash2), &cbHash)) + { + assert(cbHash == sizeof(DWORD)); + } + + if(dwHash1 != pItem->dwHash1 || dwHash2 != pItem->dwHash2) + { + dwErrCode = Logger.PrintError("Name hash values mismatch on %s", pItem->szFileName); + } + + SFileCloseFile(hFile); + } + } + return dwErrCode; +} + +static DWORD TestOpenArchive_ExtraType(TLogHelper & Logger, HANDLE hMpq, DWORD dwSearchFlags, const void * pExtra) +{ + switch(GetExtraType(pExtra)) + { + case ListFile: + return TestOpenArchive_Extra_ListFile(Logger, hMpq, (PTEST_EXTRA_ONEFILE)(pExtra)); + + case Utf8File: + return TestOpenArchive_Extra_Utf8File(Logger, hMpq, (PTEST_EXTRA_UTF8)(pExtra)); + + case TwoFiles: + return TestOpenArchive_Extra_TwoFiles(Logger, hMpq, dwSearchFlags, (PTEST_EXTRA_TWOFILES)(pExtra)); + + case PatchList: + return TestOpenArchive_Extra_Patches(Logger, hMpq, (PTEST_EXTRA_PATCHES)(pExtra)); + + case HashValues: + return TestOpenArchive_Extra_HashValues(Logger, hMpq, (PTEST_EXTRA_HASHVALS)(pExtra)); + + default: + return ERROR_SUCCESS; + } +} + +static DWORD TestOpenArchive_ModifyArchive(TLogHelper & Logger, HANDLE hMpq, DWORD dwFlags) +{ + DWORD dwExpectedError; + DWORD dwErrCode = ERROR_SUCCESS; + TCHAR szFullPath[MAX_PATH]; + + // Modify the archive, if required + if(dwFlags & TFLG_MODIFY) + { + Logger.PrintProgress("Modifying archive ..."); + + if(dwFlags & TFLG_BIGFILE) + { + dwExpectedError = (dwFlags & TFLG_WILL_FAIL) ? ERROR_DISK_FULL : ERROR_SUCCESS; + + CreateFullPathName(szFullPath, _countof(szFullPath), szDataFileDir, _T("new-file-big.mp4")); + dwErrCode = AddLocalFileToMpq(&Logger, hMpq, "added-extra-file.mp4", szFullPath); + dwErrCode = (dwErrCode == dwExpectedError) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT; + } + else + { + dwExpectedError = (dwFlags & TFLG_READ_ONLY) ? ERROR_ACCESS_DENIED : ERROR_SUCCESS; + dwErrCode = AddFileToMpq(&Logger, hMpq, "AddedFile01.txt", "This is a file added to signed MPQ", MPQ_FILE_COMPRESS, 0, dwExpectedError); + } + } + return dwErrCode; +} + +static DWORD TestOpenArchive_SignArchive(TLogHelper & Logger, HANDLE hMpq, DWORD dwDoItIfNonZero) +{ + // Sign the MPQ archive, if required + if(dwDoItIfNonZero) + { + Logger.PrintProgress("Signing the MPQ ..."); + if(!SFileSignArchive(hMpq, SIGNATURE_TYPE_WEAK)) + { + Logger.PrintMessage("Failed to create archive signature"); + return ERROR_FILE_CORRUPT; + } + } + return ERROR_SUCCESS; +} + +static DWORD TestOpenArchive_GetFileInfo(TLogHelper & Logger, HANDLE hMpq, DWORD dwFlags) +{ + if(dwFlags & TFLG_GET_FILE_INFO) + { + TMPQHeader Header = {0}; + HANDLE hFile = NULL; + DWORD dwExpectedError; + DWORD cbLength; + BYTE DataBuff[0x400]; + + // Retrieve the version of the MPQ + Logger.PrintProgress("Checking SFileGetFileInfo"); + SFileGetFileInfo(hMpq, SFileMpqHeader, &Header, sizeof(TMPQHeader), NULL); + + // Test on invalid archive/file handle + TestGetFileInfo(&Logger, NULL, SFileMpqBetHeader, NULL, 0, NULL, false, ERROR_INVALID_HANDLE); + TestGetFileInfo(&Logger, NULL, SFileInfoInvalid, NULL, 0, NULL, false, ERROR_INVALID_HANDLE); + TestGetFileInfo(&Logger, NULL, SFileInfoNameHash1, NULL, 0, NULL, false, ERROR_INVALID_HANDLE); + + // Valid handle but all parameters NULL + dwExpectedError = (Header.wFormatVersion == MPQ_FORMAT_VERSION_4) ? ERROR_INSUFFICIENT_BUFFER : ERROR_FILE_NOT_FOUND; + TestGetFileInfo(&Logger, hMpq, SFileMpqBetHeader, NULL, 0, NULL, false, dwExpectedError); + TestGetFileInfo(&Logger, hMpq, SFileMpqBetHeader, NULL, 0, &cbLength, false, dwExpectedError); + + // When we call SFileInfo with buffer = NULL and nonzero buffer size, it is ignored + TestGetFileInfo(&Logger, hMpq, SFileMpqBetHeader, NULL, 3, &cbLength, false, dwExpectedError); + + // When we call SFileInfo with buffer != NULL and nonzero buffer size, it should return error + TestGetFileInfo(&Logger, hMpq, SFileMpqBetHeader, DataBuff, 3, &cbLength, false, dwExpectedError); + + // Request for bet table header should also succeed if we want header only + dwExpectedError = (Header.wFormatVersion == MPQ_FORMAT_VERSION_4) ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND; + TestGetFileInfo(&Logger, hMpq, SFileMpqBetHeader, DataBuff, sizeof(TMPQBetHeader), &cbLength, (dwExpectedError == ERROR_SUCCESS), dwExpectedError); + TestGetFileInfo(&Logger, hMpq, SFileMpqBetHeader, DataBuff, sizeof(DataBuff), &cbLength, (dwExpectedError == ERROR_SUCCESS), dwExpectedError); + + // Try to retrieve strong signature from the MPQ + dwExpectedError = (Header.wFormatVersion == MPQ_FORMAT_VERSION_4) ? ERROR_FILE_NOT_FOUND : ERROR_INSUFFICIENT_BUFFER; + TestGetFileInfo(&Logger, hMpq, SFileMpqStrongSignature, NULL, 0, NULL, false, dwExpectedError); + TestGetFileInfo(&Logger, hMpq, SFileMpqStrongSignature, NULL, 0, &cbLength, false, dwExpectedError); + if(Header.wFormatVersion == MPQ_FORMAT_VERSION_1) + assert(cbLength == MPQ_STRONG_SIGNATURE_SIZE + 4); + + // Retrieve the signature + dwExpectedError = (Header.wFormatVersion == MPQ_FORMAT_VERSION_4) ? ERROR_FILE_NOT_FOUND : ERROR_SUCCESS; + TestGetFileInfo(&Logger, hMpq, SFileMpqStrongSignature, DataBuff, sizeof(DataBuff), &cbLength, (dwExpectedError == ERROR_SUCCESS), dwExpectedError); + if(Header.wFormatVersion == MPQ_FORMAT_VERSION_1) + assert(memcmp(DataBuff, "NGIS", 4) == 0); + + // Check SFileGetFileInfo on a listfile + if(SFileOpenFileEx(hMpq, LISTFILE_NAME, 0, &hFile)) + { + TestGetFileInfo(&Logger, hMpq, SFileInfoFileTime, DataBuff, sizeof(DataBuff), &cbLength, false, ERROR_INVALID_HANDLE); + TestGetFileInfo(&Logger, hFile, SFileInfoFileTime, DataBuff, sizeof(DataBuff), &cbLength, true, ERROR_SUCCESS); + SFileCloseFile(hFile); + } + } + return ERROR_SUCCESS; +} + +static DWORD TestOpenArchive( + LPCTSTR szMpqName1, // (UTF-8) Name of the MPQ + LPCTSTR szMpqName2, // (UTF-8) Name of the MPQ (original name or name of a copy) + LPCSTR szExpectedHash, // Expected name+data hash + DWORD dwFlags, // Test flags. Lower bits contains the number of files + const void * pExtra) // Extra parameter +{ + TLogHelper Logger("TestReadingMpq", szMpqName1); + HANDLE hMpq = NULL; + DWORD dwExpectedFileCount = 0; + DWORD dwSearchFlags = 0; + DWORD dwOpenFlags = 0; + DWORD dwMpqFlags = 0; + DWORD dwErrCode; + TCHAR szMpqNameBuff[MAX_PATH]; + + // Propagate the open MPQ flags from the input + dwOpenFlags |= (dwFlags & TFLG_WILL_FAIL) ? MPQ_OPEN_DONT_REPORT_FAILURE : 0; + dwOpenFlags |= (dwFlags & TFLG_READ_ONLY) ? STREAM_FLAG_READ_ONLY : 0; + + // Shall we switch the name of the MPQ? + szMpqName1 = SwapMpqName(szMpqNameBuff, _countof(szMpqNameBuff), szMpqName1, (PTEST_EXTRA_UTF8)(pExtra)); + + // If the file is a partial MPQ, don't load all files + if(_tcsstr(szMpqName1, _T(".MPQ.part")) == NULL) + dwSearchFlags |= SEARCH_FLAG_LOAD_FILES; + + // If we shall hash the files, do it + if(IS_VALID_STRING(szExpectedHash)) + { + dwExpectedFileCount = (dwFlags & TFLG_VALUE_MASK); + dwSearchFlags |= SEARCH_FLAG_HASH_FILES; + } + + // Copy the archive so we won't fuck up the original one + dwErrCode = OpenExistingArchiveWithCopy(&Logger, szMpqName1, szMpqName2, &hMpq, dwOpenFlags); + while(dwErrCode == ERROR_SUCCESS) + { + // Check for malformed MPQs + SFileGetFileInfo(hMpq, SFileMpqFlags, &dwMpqFlags, sizeof(dwMpqFlags), NULL); + dwSearchFlags |= (dwMpqFlags & MPQ_FLAG_MALFORMED) ? SEARCH_FLAG_IGNORE_ERRORS : 0; + dwSearchFlags |= (GetExtraType(pExtra) == PatchList) ? SEARCH_FLAG_IGNORE_ERRORS : 0; + + // Verify signature before any changes + if((dwErrCode = TestOpenArchive_VerifySignature(Logger, hMpq, (dwFlags & TFLG_SIGCHECK_BEFORE))) != ERROR_SUCCESS) + break; + + // Perform extra action, dependent on the data passed + if((dwErrCode = TestOpenArchive_ExtraType(Logger, hMpq, dwSearchFlags, pExtra)) != ERROR_SUCCESS) + break; + + // Modify the archive, if required + if((dwErrCode = TestOpenArchive_ModifyArchive(Logger, hMpq, dwFlags)) != ERROR_SUCCESS) + break; + + // Sign the archive, if needed + if((dwErrCode = TestOpenArchive_SignArchive(Logger, hMpq, (dwFlags & TFLG_SIGN_ARCHIVE))) != ERROR_SUCCESS) + break; + + // Test the SFileGetFileInfo, if required + if((dwErrCode = TestOpenArchive_GetFileInfo(Logger, hMpq, dwFlags)) != ERROR_SUCCESS) + break; + + // Verify signature after any changes + if((dwErrCode = TestOpenArchive_VerifySignature(Logger, hMpq, (dwFlags & TFLG_SIGCHECK_AFTER))) != ERROR_SUCCESS) + break; + + // Attempt to open the (listfile), (attributes), (signature) + if((TestArchive_LoadFiles(&Logger, hMpq, (dwMpqFlags & MPQ_FLAG_MALFORMED), LISTFILE_NAME, ATTRIBUTES_NAME, SIGNATURE_NAME, NULL)) != ERROR_SUCCESS) + break; + + // If required, we search the archive and compare file cound and name hash + if((dwErrCode = VerifyDataChecksum(Logger, hMpq, dwSearchFlags, szExpectedHash, dwExpectedFileCount)) != ERROR_SUCCESS) + break; + + break; + } + + // Reset error code, if the failure is expected + if((dwErrCode != ERROR_SUCCESS || hMpq == NULL) && (dwFlags & TFLG_WILL_FAIL)) + SErrSetLastError(dwErrCode = ERROR_SUCCESS); + + // Cleanup and exit + if(hMpq != NULL) + SFileCloseArchive(hMpq); + return Logger.PrintVerdict(dwErrCode); +} + +static DWORD TestOpenArchive(const TEST_INFO1 & TestInfo) +{ + return TestOpenArchive(TestInfo.szName1, // Name of the MPQ + TestInfo.szName2, // Name of the listfile or NULL + TestInfo.szDataHash, // Compound name+data hash + TestInfo.dwFlags, // Test flags + TestInfo.pExtra); // Extra parameter +} + +//----------------------------------------------------------------------------- +// Reopening archives + +static void WINAPI CompactCallback(void * pvUserData, DWORD dwWork, ULONGLONG BytesDone, ULONGLONG TotalBytes) +{ + TLogHelper * pLogger = (TLogHelper *)pvUserData; + LPCSTR szWork = NULL; + + switch(dwWork) + { + case CCB_CHECKING_FILES: + szWork = "Checking files in archive"; + break; + + case CCB_CHECKING_HASH_TABLE: + szWork = "Checking hash table"; + break; + + case CCB_COPYING_NON_MPQ_DATA: + szWork = "Copying non-MPQ data"; + break; + + case CCB_COMPACTING_FILES: + szWork = "Compacting files"; + break; + + case CCB_CLOSING_ARCHIVE: + szWork = "Closing archive"; + break; + } + + if(szWork != NULL) + { + if(pLogger != NULL) + pLogger->PrintProgress("%s " fmt_X_of_Y_a " ...", szWork, BytesDone, TotalBytes); + else + printf("%s " fmt_X_of_Y_a " ... \r", szWork, BytesDone, TotalBytes); + } +} + +static DWORD TestReopenArchive_CompactArchive(TLogHelper & Logger, HANDLE hMpq, DWORD dwFlags) +{ + if(dwFlags & TFLG_COMPACT) + { + // Set the compact callback + Logger.PrintProgress("Compacting archive ..."); + if(!SFileSetCompactCallback(hMpq, CompactCallback, &Logger)) + return Logger.PrintError("Failed to set the compact callback"); + + // Compact the archive + if(!SFileCompactArchive(hMpq, NULL, false)) + return Logger.PrintError("Failed to compact archive"); + } + return ERROR_SUCCESS; +} + +static DWORD TestReopenArchive( + LPCTSTR szMpqName1, // Name of the MPQ + LPCSTR szExpectedHash, // Expected name+data hash + DWORD dwFlags) // Test flags. Lower bits contains the number of files +{ + TLogHelper Logger("Test_ReopenMpq", szMpqName1); + ULONGLONG PreMpqDataSize = (dwFlags & TFLG_ADD_USER_DATA) ? 0x400 : 0; + ULONGLONG UserDataSize = (dwFlags & TFLG_ADD_USER_DATA) ? 0x531 : 0; + LPCTSTR szCopyName = _T("StormLibTest_Reopened.mpq"); + HANDLE hMpq; + DWORD dwExpectedFileCount = 0; + DWORD dwSearchFlags = SEARCH_FLAG_LOAD_FILES; + TCHAR szFullPath[MAX_PATH]; + DWORD dwErrCode; + + // If we shall hash the files, do it + if(IS_VALID_STRING(szExpectedHash)) + { + dwExpectedFileCount = (dwFlags & TFLG_VALUE_MASK); + dwSearchFlags |= SEARCH_FLAG_HASH_FILES; + } + + // Create copy of the archive, with interleaving some user data + dwErrCode = CreateFileCopy(&Logger, szMpqName1, szCopyName, szFullPath, _countof(szFullPath), PreMpqDataSize, UserDataSize); + + // Open the archive and read the hash of the files + if(dwErrCode == ERROR_SUCCESS) + { + if((dwErrCode = OpenExistingArchive(&Logger, szFullPath, 0, &hMpq)) == ERROR_SUCCESS) + { + // Verify presence of (listfile) and (attributes) + CheckIfFileIsPresent(&Logger, hMpq, LISTFILE_NAME, (dwFlags & TFLG_HAS_LISTFILE)); + CheckIfFileIsPresent(&Logger, hMpq, ATTRIBUTES_NAME, (dwFlags & TFLG_HAS_ATTRIBUTES)); + + // If required, we search the archive and compare file cound and name hash + dwErrCode = VerifyDataChecksum(Logger, hMpq, dwSearchFlags, szExpectedHash, dwExpectedFileCount); + SFileCloseArchive(hMpq); + } + } + + // Try to modify and/or compact the MPQ + if(dwErrCode == ERROR_SUCCESS) + { + // Open the archive again + if((dwErrCode = OpenExistingArchive(&Logger, szFullPath, 0, &hMpq)) == ERROR_SUCCESS) + { + // Modify the archive, if required + if((dwErrCode = TestOpenArchive_ModifyArchive(Logger, hMpq, dwFlags)) == ERROR_SUCCESS) + { + dwErrCode = TestReopenArchive_CompactArchive(Logger, hMpq, dwFlags); + } + SFileCloseArchive(hMpq); + } + } + + // Open the archive and load some files + if((dwErrCode == ERROR_SUCCESS) && STORMLIB_TEST_FLAGS(dwFlags, TFLG_COMPACT | TFLG_MODIFY | TFLG_BIGFILE, TFLG_COMPACT)) + { + // Open the archive + if((dwErrCode = OpenExistingArchive(&Logger, szFullPath, 0, &hMpq)) == ERROR_SUCCESS) + { + // Verify presence of (listfile) and (attributes) + CheckIfFileIsPresent(&Logger, hMpq, LISTFILE_NAME, (dwFlags & TFLG_HAS_LISTFILE)); + CheckIfFileIsPresent(&Logger, hMpq, ATTRIBUTES_NAME, (dwFlags & TFLG_HAS_ATTRIBUTES)); + + // Search the archive and load every file + dwErrCode = VerifyDataChecksum(Logger, hMpq, dwSearchFlags, szExpectedHash, dwExpectedFileCount); + } + SFileCloseArchive(hMpq); + } + return dwErrCode; +} + +static DWORD TestOpenArchive_SignatureTest(LPCTSTR szPlainName, LPCTSTR szOriginalName, DWORD dwFlags) +{ + TLogHelper Logger("Test_Signature", szPlainName); + HANDLE hMpq; + DWORD dwCreateFlags = MPQ_CREATE_LISTFILE | MPQ_CREATE_ATTRIBUTES | MPQ_FORMAT_VERSION_1; + DWORD dwErrCode = ERROR_SUCCESS; + + // Create a new archive or copy existing + if(dwFlags & SFLAG_CREATE_ARCHIVE) + { + dwCreateFlags |= (dwFlags & SFLAG_SIGN_AT_CREATE) ? MPQ_CREATE_SIGNATURE : 0; + dwErrCode = CreateNewArchive_V2(&Logger, szPlainName, dwCreateFlags, 4000, &hMpq); + } + else + { + szOriginalName = (szOriginalName) ? szOriginalName : szPlainName; + dwErrCode = OpenExistingArchiveWithCopy(&Logger, szPlainName, szOriginalName, &hMpq); + } + + // Continue with archive signature tests + if(dwErrCode == ERROR_SUCCESS) + { + // Shall we check the signatures before modifications? + if(dwErrCode == ERROR_SUCCESS) + { + dwErrCode = TestOpenArchive_VerifySignature(Logger, hMpq, (dwFlags & SFLAG_VERIFY_BEFORE)); + } + + // Shall we modify the archive? + if(dwErrCode == ERROR_SUCCESS) + { + dwErrCode = TestOpenArchive_ModifyArchive(Logger, hMpq, (dwFlags & SFLAG_MODIFY_ARCHIVE)); + } + + // Shall we sign the archive? + if(dwErrCode == ERROR_SUCCESS) + { + dwErrCode = TestOpenArchive_SignArchive(Logger, hMpq, (dwFlags & SFLAG_SIGN_ARCHIVE)); + } + + // Shall we check the signatures after modifications? + if(dwErrCode == ERROR_SUCCESS) + { + dwErrCode = TestOpenArchive_VerifySignature(Logger, hMpq, (dwFlags & SFLAG_VERIFY_AFTER)); + } + + SFileCloseArchive(hMpq); + } + return dwErrCode; +} + +static DWORD TestCreateArchive(LPCTSTR szPlainName, LPCSTR szFileName, DWORD dwFlags) +{ + TLogHelper Logger("CreateNewMpq", szPlainName); + HANDLE hMpq = NULL; + DWORD dwMaxFileCount = dwFlags & 0x0000FFFF; + DWORD dwCreateFlags = 0; + DWORD dwFileCount = 0; + DWORD dwErrCode; + + // Fixup the MPQ format + dwCreateFlags |= (dwFlags & CFLG_EMPTY) ? 0 : MPQ_CREATE_LISTFILE | MPQ_CREATE_ATTRIBUTES; + dwCreateFlags |= (dwFlags & CFLG_V2) ? MPQ_CREATE_ARCHIVE_V2 : 0; + dwCreateFlags |= (dwFlags & CFLG_V4) ? MPQ_CREATE_ARCHIVE_V4 : 0; + + // Create the full path name + dwErrCode = CreateNewArchive(&Logger, szPlainName, dwCreateFlags, dwMaxFileCount, &hMpq); + if(dwErrCode == ERROR_SUCCESS) + { + // Add non-standard names, if needed + if(dwFlags & CFLG_NONSTD_NAMES) + { + // Add few files and close the archive + AddFileToMpq(&Logger, hMpq, "AddedFile000.txt", "This is the file data 000.", MPQ_FILE_COMPRESS); + AddFileToMpq(&Logger, hMpq, "\\/\\/\\/\\AddedFile001.txt", "This is the file data 001.", MPQ_FILE_COMPRESS); + AddFileToMpq(&Logger, hMpq, "\\\\\\\\\\\\\\\\", "This is the file data 002.", MPQ_FILE_COMPRESS); + AddFileToMpq(&Logger, hMpq, "////////////////", "This is the file data 003.", MPQ_FILE_COMPRESS); + AddFileToMpq(&Logger, hMpq, "//\\//\\//\\//\\", "This is the file data 004.", MPQ_FILE_COMPRESS); + AddFileToMpq(&Logger, hMpq, "................", "This is the file data 005.", MPQ_FILE_COMPRESS); + AddFileToMpq(&Logger, hMpq, "//****//****//****//****.***", "This is the file data 006.", MPQ_FILE_COMPRESS); + AddFileToMpq(&Logger, hMpq, "//*??*//*??*//*??*//?**?.?*?", "This is the file data 007.", MPQ_FILE_COMPRESS); + AddFileToMpq(&Logger, hMpq, "\\/\\/File.txt", "This is the file data 008.", MPQ_FILE_COMPRESS); + AddFileToMpq(&Logger, hMpq, "\\/\\/File.txt..", "This is the file data 009.", MPQ_FILE_COMPRESS); + AddFileToMpq(&Logger, hMpq, "Dir1\\Dir2\\Dir3\\File.txt..", "This is the file data 010.", MPQ_FILE_COMPRESS); + AddFileToMpq(&Logger, hMpq, "\\Dir1\\Dir2\\Dir3\\File.txt..", "This is the file data 011.", MPQ_FILE_COMPRESS); + AddFileToMpq(&Logger, hMpq, "\\\\Dir1\\\\Dir2\\\\Dir3\\\\File.txt..", "This is the file data 012.", MPQ_FILE_COMPRESS); + AddFileToMpq(&Logger, hMpq, "/Dir1/Dir2/Dir3/File.txt..", "This is the file data 013.", MPQ_FILE_COMPRESS); + AddFileToMpq(&Logger, hMpq, "////Dir1////Dir2////Dir3////File.txt..", "This is the file data 014.", MPQ_FILE_COMPRESS); + AddFileToMpq(&Logger, hMpq, "\\//\\Dir1\\//\\Dir2\\//\\File.txt..", "This is the file data 015.", MPQ_FILE_COMPRESS); + AddFileToMpq(&Logger, hMpq, "\x10\x11\x12\x13\\\x14\x15\x16\x17\\\x18\x19\x1a\x1b\\\x1c\x1D\x1E\x1F.txt", "This is the file data 016.", MPQ_FILE_COMPRESS); + AddFileToMpq(&Logger, hMpq, "\x09\x20\x09\x20\\\x20\x09\x20\x09\\\x09\x20\x09\x20\\\x20\x09\x20\x09.txt", "This is the file data 017.", MPQ_FILE_COMPRESS); + AddFileToMpq(&Logger, hMpq, "\x80\x91\xA2\xB3\\\xC4\xD5\xE6\xF7\\\x80\x91\xA2\xB3.txt", "This is the file data 018.", MPQ_FILE_COMPRESS); + AddFileToMpq(&Logger, hMpq, "Dir1\x20\x09\x20\\Dir2\x20\x09\x20\\File.txt\x09\x09\x20\x2e", "This is the file data 019.", MPQ_FILE_COMPRESS); + AddFileToMpq(&Logger, hMpq, "Dir1\x20\x09\x20\\Dir2\x20\x09\x20\\\x09\x20\x2e\x09\x20\x2e", "This is the file data 020.", MPQ_FILE_COMPRESS); + } + + // Like MPQEditor: Flush archive, add one file, flush again + if(dwFlags & CFLG_MPQEDITOR) + { + SFileFlushArchive(hMpq); + dwErrCode = AddFileToMpq(&Logger, hMpq, szFileName, "This is the file data.", MPQ_FILE_COMPRESS); + SFileFlushArchive(hMpq); + } + + // Search the archive + SearchArchive(&Logger, hMpq); + SFileCloseArchive(hMpq); + } + + // Reopen the empty MPQ + if(dwErrCode == ERROR_SUCCESS) + { + dwErrCode = OpenExistingArchiveWithCopy(&Logger, NULL, szPlainName, &hMpq); + if(dwErrCode == ERROR_SUCCESS) + { + // Retrieve the number of files + SFileGetFileInfo(hMpq, SFileMpqNumberOfFiles, &dwFileCount, sizeof(dwFileCount), NULL); + + // Special check for empty MPQs + if(dwFlags & CFLG_EMPTY) + { + if(dwFileCount != 0) + dwErrCode = ERROR_FILE_CORRUPT; + CheckIfFileIsPresent(&Logger, hMpq, "File00000000.xxx", false); + CheckIfFileIsPresent(&Logger, hMpq, LISTFILE_NAME, false); + SearchArchive(&Logger, hMpq); + } + else + { + if(dwFileCount == 0) + dwErrCode = ERROR_FILE_CORRUPT; + } + + // Close the MPQ + SFileCloseArchive(hMpq); + } + } + + return dwErrCode; +} + +static DWORD TestCreateArchive(const TEST_INFO2 & TestInfo) +{ + TCHAR szPlainNameT[MAX_PATH]; + + // Always prefix the archive name with "StormLibTest_" + StringCopy(szPlainNameT, _countof(szPlainNameT), "StormLibTest_"); + StringCat(szPlainNameT, _countof(szPlainNameT), TestInfo.szName1); + + // Perform creation of the archive + return TestCreateArchive(szPlainNameT, TestInfo.szName2, TestInfo.dwFlags); +} + +static DWORD TestRenameFile(LPCTSTR szPlainName) +{ + TLogHelper Logger("TestRenameFile", szPlainName); + LPCSTR szSourceFile = "war3map.mmp"; + LPCSTR szTargetFile = "war3map.wts"; + HANDLE hMpq = NULL; + DWORD dwErrCode; + DWORD dwFailed = 0; + LCID LCID_RURU = 0x0419; + LCID LCID_ESES = 0x040A; + + // Create copy of the archive and open it + dwErrCode = OpenExistingArchiveWithCopy(&Logger, szPlainName, szPlainName, &hMpq); + if(dwErrCode == ERROR_SUCCESS) + { + // Try to rename an existing file to "war3map.wts". + // This must fail because the file already exists. + if(SFileRenameFile(hMpq, szSourceFile, szTargetFile)) + dwFailed++; + + // Now change locale of an existing file to Russian + if(SetLocaleForFileOperations(hMpq, szSourceFile, LCID_RURU) != ERROR_SUCCESS) + dwFailed++; + + // The rename should work now + if(!SFileRenameFile(hMpq, szSourceFile, szTargetFile)) + dwFailed++; + + // Changing the file locale to Neutral should fail now, + // because such file already exists + if(SetLocaleForFileOperations(hMpq, szTargetFile, 0) != ERROR_ALREADY_EXISTS) + dwFailed++; + + // Pick another source file + szSourceFile = "war3map.shd"; + + // Change the file locale to Spain + if(SetLocaleForFileOperations(hMpq, szSourceFile, LCID_ESES) != ERROR_SUCCESS) + dwFailed++; + + // This rename should also work, because there is no target file with Spanish locale + if(!SFileRenameFile(hMpq, szSourceFile, szTargetFile)) + dwFailed++; + + // Evaluate the result + if(dwFailed != 0) + dwErrCode = ERROR_CAN_NOT_COMPLETE; + SFileCloseArchive(hMpq); + } + + // Restore the original file locale + SFileSetLocale(LANG_NEUTRAL); + return dwErrCode; +} + +static DWORD TestCreateArchive_TestGaps(LPCTSTR szPlainName) +{ + TLogHelper Logger("TestCreateGaps", szPlainName); + ULONGLONG ByteOffset1 = 0xFFFFFFFF; + ULONGLONG ByteOffset2 = 0xEEEEEEEE; + HANDLE hMpq = NULL; + HANDLE hFile = NULL; + TCHAR szFullPath[MAX_PATH]; + DWORD dwErrCode = ERROR_SUCCESS; + + // Create new MPQ + dwErrCode = CreateNewArchive_V2(&Logger, szPlainName, MPQ_CREATE_LISTFILE | MPQ_CREATE_ATTRIBUTES | MPQ_FORMAT_VERSION_4, 4000, &hMpq); + if(dwErrCode == ERROR_SUCCESS) + { + // Add one file and flush the archive + dwErrCode = AddFileToMpq(&Logger, hMpq, "AddedFile01.txt", "This is the file data.", MPQ_FILE_COMPRESS); + SFileCloseArchive(hMpq); + hMpq = NULL; + } + + // Reopen the MPQ and add another file. + // The new file must be added to the position of the (listfile) + if(dwErrCode == ERROR_SUCCESS) + { + CreateFullPathName(szFullPath, _countof(szFullPath), NULL, szPlainName); + dwErrCode = OpenExistingArchive(&Logger, szFullPath, 0, &hMpq); + if(dwErrCode == ERROR_SUCCESS) + { + // Retrieve the position of the (listfile) + if(SFileOpenFileEx(hMpq, LISTFILE_NAME, 0, &hFile)) + { + SFileGetFileInfo(hFile, SFileInfoByteOffset, &ByteOffset1, sizeof(ULONGLONG), NULL); + SFileCloseFile(hFile); + } + else + dwErrCode = SErrGetLastError(); + } + } + + // Add another file and check its position. It must be at the position of the former listfile + if(dwErrCode == ERROR_SUCCESS) + { + LPCSTR szAddedFile = "AddedFile02.txt"; + + // Add another file + dwErrCode = AddFileToMpq(&Logger, hMpq, szAddedFile, "This is the second added file.", MPQ_FILE_COMPRESS); + + // Retrieve the position of the (listfile) + if(SFileOpenFileEx(hMpq, szAddedFile, 0, &hFile)) + { + SFileGetFileInfo(hFile, SFileInfoByteOffset, &ByteOffset2, sizeof(ULONGLONG), NULL); + SFileCloseFile(hFile); + } + else + dwErrCode = SErrGetLastError(); + } + + // Now check the positions + if(dwErrCode == ERROR_SUCCESS) + { + if(ByteOffset1 != ByteOffset2) + { + Logger.PrintError("The added file was not written to the position of (listfile)"); + dwErrCode = ERROR_FILE_CORRUPT; + } + } + + // Close the archive if needed + if(hMpq != NULL) + SFileCloseArchive(hMpq); + return dwErrCode; +} + +static DWORD TestCreateArchive_FillArchive(LPCTSTR szPlainName, DWORD dwCreateFlags) +{ + TLogHelper Logger("TestCreateFull", szPlainName); + LPCSTR szFileData = "TestCreateArchive_FillArchive: Testing file data"; + char szFileName[MAX_PATH]; + HANDLE hMpq = NULL; + DWORD dwMaxFileCount = 6; + DWORD dwCompression = MPQ_COMPRESSION_ZLIB; + DWORD dwFlags = MPQ_FILE_ENCRYPTED | MPQ_FILE_COMPRESS; + DWORD dwErrCode; + + // + // Note that StormLib will round the maxfile count + // up to hash table size (nearest power of two) + // + if((dwCreateFlags & MPQ_CREATE_LISTFILE) == 0) + dwMaxFileCount++; + if((dwCreateFlags & MPQ_CREATE_ATTRIBUTES) == 0) + dwMaxFileCount++; + + // Create the new MPQ archive + dwErrCode = CreateNewArchive_V2(&Logger, szPlainName, dwCreateFlags, dwMaxFileCount, &hMpq); + if(dwErrCode == ERROR_SUCCESS) + { + // Flush the archive first + SFileFlushArchive(hMpq); + + // Add all files + for(unsigned int i = 0; i < dwMaxFileCount; i++) + { + sprintf(szFileName, "AddedFile%03u.txt", i); + dwErrCode = AddFileToMpq(&Logger, hMpq, szFileName, szFileData, dwFlags, dwCompression); + if(dwErrCode != ERROR_SUCCESS) + break; + } + + // Flush the archive again + SFileFlushArchive(hMpq); + } + + // Now the MPQ should be full. It must not be possible to add another file + if(dwErrCode == ERROR_SUCCESS) + { + dwErrCode = AddFileToMpq(&Logger, hMpq, "ShouldNotBeHere.txt", szFileData, MPQ_FILE_COMPRESS, MPQ_COMPRESSION_ZLIB, ERROR_DISK_FULL); + assert(dwErrCode != ERROR_SUCCESS); + dwErrCode = ERROR_SUCCESS; + } + + // Close the archive to enforce saving all tables + if(hMpq != NULL) + SFileCloseArchive(hMpq); + hMpq = NULL; + + // Reopen the archive again + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = OpenExistingArchiveWithCopy(&Logger, NULL, szPlainName, &hMpq); + + // The archive should still be full + if(dwErrCode == ERROR_SUCCESS) + { + CheckIfFileIsPresent(&Logger, hMpq, LISTFILE_NAME, (dwCreateFlags & MPQ_CREATE_LISTFILE) ? true : false); + CheckIfFileIsPresent(&Logger, hMpq, ATTRIBUTES_NAME, (dwCreateFlags & MPQ_CREATE_ATTRIBUTES) ? true : false); + dwErrCode = AddFileToMpq(&Logger, hMpq, "ShouldNotBeHere.txt", szFileData, MPQ_FILE_COMPRESS, MPQ_COMPRESSION_ZLIB, ERROR_DISK_FULL); + assert(dwErrCode != ERROR_SUCCESS); + dwErrCode = ERROR_SUCCESS; + } + + // The (listfile) and (attributes) must be present + if(dwErrCode == ERROR_SUCCESS) + { + CheckIfFileIsPresent(&Logger, hMpq, LISTFILE_NAME, (dwCreateFlags & MPQ_CREATE_LISTFILE) ? true : false); + CheckIfFileIsPresent(&Logger, hMpq, ATTRIBUTES_NAME, (dwCreateFlags & MPQ_CREATE_ATTRIBUTES) ? true : false); + dwErrCode = RemoveMpqFile(&Logger, hMpq, szFileName, ERROR_SUCCESS); + } + + // Now add the file again. This time, it should be possible OK + if(dwErrCode == ERROR_SUCCESS) + { + dwErrCode = AddFileToMpq(&Logger, hMpq, szFileName, szFileData, dwFlags, dwCompression, ERROR_SUCCESS); + assert(dwErrCode == ERROR_SUCCESS); + } + + // Now add the file again. This time, it should fail + if(dwErrCode == ERROR_SUCCESS) + { + dwErrCode = AddFileToMpq(&Logger, hMpq, szFileName, szFileData, dwFlags, dwCompression, ERROR_ALREADY_EXISTS); + assert(dwErrCode != ERROR_SUCCESS); + dwErrCode = ERROR_SUCCESS; + } + + // Now add the file again. This time, it should fail + if(dwErrCode == ERROR_SUCCESS) + { + dwErrCode = AddFileToMpq(&Logger, hMpq, "ShouldNotBeHere.txt", szFileData, dwFlags, dwCompression, ERROR_DISK_FULL); + assert(dwErrCode != ERROR_SUCCESS); + dwErrCode = ERROR_SUCCESS; + } + + // Close the archive and return + if(hMpq != NULL) + SFileCloseArchive(hMpq); + hMpq = NULL; + + // Reopen the archive for the third time to verify that both internal files are there + if(dwErrCode == ERROR_SUCCESS) + { + dwErrCode = OpenExistingArchiveWithCopy(&Logger, NULL, szPlainName, &hMpq); + if(dwErrCode == ERROR_SUCCESS) + { + CheckIfFileIsPresent(&Logger, hMpq, LISTFILE_NAME, (dwCreateFlags & MPQ_CREATE_LISTFILE) ? true : false); + CheckIfFileIsPresent(&Logger, hMpq, ATTRIBUTES_NAME, (dwCreateFlags & MPQ_CREATE_ATTRIBUTES) ? true : false); + SFileCloseArchive(hMpq); + } + } + + return dwErrCode; +} + +static DWORD TestCreateArchive_IncMaxFileCount(LPCTSTR szPlainName) +{ + TLogHelper Logger("IncMaxFileCount", szPlainName); + LPCSTR szFileData = "TestCreateArchive_IncMaxFileCount: Testing file data"; + char szFileName[MAX_PATH]; + HANDLE hMpq = NULL; + DWORD dwMaxFileCount = 1; + DWORD dwErrCode; + + // Create the new MPQ + dwErrCode = CreateNewArchive(&Logger, szPlainName, MPQ_CREATE_ARCHIVE_V4 | MPQ_CREATE_LISTFILE | MPQ_CREATE_ATTRIBUTES, dwMaxFileCount, &hMpq); + + // Now add exactly one file + if(dwErrCode == ERROR_SUCCESS) + { + dwErrCode = AddFileToMpq(&Logger, hMpq, "AddFile_base.txt", szFileData); + SFileFlushArchive(hMpq); + SFileCloseArchive(hMpq); + } + + // Now add 10 files. Each time we cannot add the file due to archive being full, + // we increment the max file count + if(dwErrCode == ERROR_SUCCESS) + { + for(unsigned int i = 0; i < 10; i++) + { + // Open the archive again + dwErrCode = OpenExistingArchiveWithCopy(&Logger, NULL, szPlainName, &hMpq); + if(dwErrCode != ERROR_SUCCESS) + break; + + // Add one file + sprintf(szFileName, "AddFile_%04u.txt", i); + dwErrCode = AddFileToMpq(&Logger, hMpq, szFileName, szFileData, 0, 0, ERROR_UNDETERMINED_RESULT); + if(dwErrCode != ERROR_SUCCESS) + { + // Increment the max file count by one + dwMaxFileCount = SFileGetMaxFileCount(hMpq) + 1; + Logger.PrintProgress("Increasing max file count to %u ...", dwMaxFileCount); + SFileSetMaxFileCount(hMpq, dwMaxFileCount); + + // Attempt to create the file again + dwErrCode = AddFileToMpq(&Logger, hMpq, szFileName, szFileData, 0, 0, ERROR_SUCCESS); + } + + // Compact the archive and close it + SFileSetCompactCallback(hMpq, CompactCallback, &Logger); + SFileCompactArchive(hMpq, NULL, false); + SFileCloseArchive(hMpq); + if(dwErrCode != ERROR_SUCCESS) + break; + } + } + + return dwErrCode; +} + +static DWORD TestCreateArchive_FileFlagTest(LPCTSTR szPlainName) +{ + TLogHelper Logger("TestFileFlag", szPlainName); + HANDLE hMpq = NULL; // Handle of created archive + TCHAR szFileName1[MAX_PATH]; + TCHAR szFileName2[MAX_PATH]; + TCHAR szFullPath[MAX_PATH]; + LPCSTR szMiddleFile = "FileTest_10.exe"; + LCID LocaleIDs[] = {0x000, 0x405, 0x406, 0x407}; + char szArchivedName[MAX_PATH]; + DWORD dwMaxFileCount = 0; + DWORD dwFileCount = 0; + DWORD dwErrCode; + + // Create paths for local file to be added + CreateFullPathName(szFileName1, _countof(szFileName1), szDataFileDir, _T("new-file.exe")); + CreateFullPathName(szFileName2, _countof(szFileName2), szDataFileDir, _T("new-file.bin")); + SFileSetLocale(LANG_NEUTRAL); + + // Create an empty file that will serve as holder for the MPQ + dwErrCode = CreateEmptyFile(&Logger, szPlainName, 0x100000, szFullPath); + + // Create new MPQ archive over that file + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = CreateNewArchive(&Logger, szPlainName, MPQ_CREATE_ARCHIVE_V1 | MPQ_CREATE_LISTFILE | MPQ_CREATE_ATTRIBUTES, 17, &hMpq); + + // Add the same file multiple times + if(dwErrCode == ERROR_SUCCESS) + { + dwMaxFileCount = SFileGetMaxFileCount(hMpq); + for(size_t i = 0; AddFlags[i] != 0xFFFFFFFF; i++) + { + sprintf(szArchivedName, "FileTest_%02u.exe", (unsigned int)i); + dwErrCode = AddLocalFileToMpq(&Logger, hMpq, szArchivedName, szFileName1, AddFlags[i], 0); + if(dwErrCode != ERROR_SUCCESS) + break; + + dwFileCount++; + } + } + + // Delete a file in the middle of the file table + if(dwErrCode == ERROR_SUCCESS) + { + Logger.PrintProgress("Removing file %s ...", szMiddleFile); + dwErrCode = RemoveMpqFile(&Logger, hMpq, szMiddleFile, ERROR_SUCCESS); + dwFileCount--; + } + + // Add one more file + if(dwErrCode == ERROR_SUCCESS) + { + dwErrCode = AddLocalFileToMpq(&Logger, hMpq, "FileTest_xx.exe", szFileName1); + dwFileCount++; + } + + // Try to decrement max file count. This must succeed + if(dwErrCode == ERROR_SUCCESS) + { + Logger.PrintProgress("Attempting to decrement max file count ..."); + if(SFileSetMaxFileCount(hMpq, 5)) + dwErrCode = Logger.PrintError("Max file count decremented, even if it should fail"); + } + + // Add ZeroSize.txt several times under a different locale + if(dwErrCode == ERROR_SUCCESS) + { + for(size_t i = 0; i < _countof(LocaleIDs); i++) + { + bool bMustSucceed = ((dwFileCount + 2) < dwMaxFileCount); + + SFileSetLocale(LocaleIDs[i]); + dwErrCode = AddLocalFileToMpq(&Logger, hMpq, "ZeroSize_1.txt", szFileName2); + if(dwErrCode != ERROR_SUCCESS) + { + if(bMustSucceed == false) + dwErrCode = ERROR_SUCCESS; + break; + } + + dwFileCount++; + } + } + + // Add ZeroSize.txt again several times under a different locale + if(dwErrCode == ERROR_SUCCESS) + { + for(size_t i = 0; LocaleIDs[i] != 0xFFFF; i++) + { + bool bMustSucceed = ((dwFileCount + 2) < dwMaxFileCount); + + SFileSetLocale(LocaleIDs[i]); + dwErrCode = AddLocalFileToMpq(&Logger, hMpq, "ZeroSize_2.txt", szFileName2, 0, 0, bMustSucceed); + if(dwErrCode != ERROR_SUCCESS) + { + if(bMustSucceed == false) + dwErrCode = ERROR_SUCCESS; + break; + } + + dwFileCount++; + } + } + + // Verify how many files did we add to the MPQ + if(dwErrCode == ERROR_SUCCESS) + { + if(dwFileCount + 2 != dwMaxFileCount) + { + Logger.PrintErrorVa("Number of files added to MPQ was unexpected (expected %u, added %u)", dwFileCount, dwMaxFileCount - 2); + dwErrCode = ERROR_FILE_CORRUPT; + } + } + + // Test rename function + if(dwErrCode == ERROR_SUCCESS) + { + Logger.PrintProgress("Testing rename files ..."); + SFileSetLocale(LANG_NEUTRAL); + if(!SFileRenameFile(hMpq, "FileTest_08.exe", "FileTest_08a.exe")) + dwErrCode = Logger.PrintError("Failed to rename the file"); + } + + if(dwErrCode == ERROR_SUCCESS) + { + if(!SFileRenameFile(hMpq, "FileTest_08a.exe", "FileTest_08.exe")) + dwErrCode = Logger.PrintError("Failed to rename the file"); + } + + if(dwErrCode == ERROR_SUCCESS) + { + if(SFileRenameFile(hMpq, "FileTest_10.exe", "FileTest_10a.exe")) + { + Logger.PrintError("Rename test succeeded even if it shouldn't"); + dwErrCode = ERROR_FILE_CORRUPT; + } + } + + if(dwErrCode == ERROR_SUCCESS) + { + if(SFileRenameFile(hMpq, "FileTest_10a.exe", "FileTest_10.exe")) + { + Logger.PrintError("Rename test succeeded even if it shouldn't"); + dwErrCode = ERROR_FILE_CORRUPT; + } + } + + // Close the archive + if(hMpq != NULL) + SFileCloseArchive(hMpq); + hMpq = NULL; + + // Try to reopen the archive + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = OpenExistingArchive(&Logger, szFullPath, 0, NULL); + return dwErrCode; +} + +static DWORD TestCreateArchive_WaveCompressionsTest(LPCTSTR szPlainName, LPCTSTR szWaveFile) +{ + TLogHelper Logger("TestCompressions", szPlainName); + HANDLE hMpq = NULL; // Handle of created archive + TCHAR szFileName[MAX_PATH]; // Source file to be added + char szArchivedName[MAX_PATH]; + DWORD dwCmprCount = sizeof(WaveCompressions) / sizeof(DWORD); + DWORD dwAddedFiles = 0; + DWORD dwFoundFiles = 0; + DWORD dwErrCode; + + // Create paths for local file to be added + CreateFullPathName(szFileName, _countof(szFileName), szDataFileDir, szWaveFile); + + // Create new archive + dwErrCode = CreateNewArchive(&Logger, szPlainName, MPQ_CREATE_ARCHIVE_V1 | MPQ_CREATE_LISTFILE | MPQ_CREATE_ATTRIBUTES, 0x40, &hMpq); + + // Add the same file multiple times + if(dwErrCode == ERROR_SUCCESS) + { + Logger.UserTotal = dwCmprCount; + for(unsigned int i = 0; i < dwCmprCount; i++) + { + sprintf(szArchivedName, "WaveFile_%02u.wav", i + 1); + dwErrCode = AddLocalFileToMpq(&Logger, hMpq, szArchivedName, szFileName, MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED | MPQ_FILE_SECTOR_CRC, WaveCompressions[i]); + if(dwErrCode != ERROR_SUCCESS) + break; + + Logger.UserCount++; + dwAddedFiles++; + } + + SFileCloseArchive(hMpq); + } + + // Reopen the archive extract each WAVE file and try to play it + if(dwErrCode == ERROR_SUCCESS) + { + dwErrCode = OpenExistingArchiveWithCopy(&Logger, NULL, szPlainName, &hMpq); + if(dwErrCode == ERROR_SUCCESS) + { + SearchArchive(&Logger, hMpq, SEARCH_FLAG_LOAD_FILES | SEARCH_FLAG_PLAY_WAVES, &dwFoundFiles, NULL); + SFileCloseArchive(hMpq); + } + + // Check if the number of found files is the same like the number of added files + // DOn;t forget that there will be (listfile) and (attributes) + if(dwFoundFiles != (dwAddedFiles + 2)) + { + Logger.PrintError("Number of found files does not match number of added files."); + dwErrCode = ERROR_FILE_CORRUPT; + } + } + + return dwErrCode; +} + +static DWORD TestCreateArchive_ListFilePos(LPCTSTR szPlainName) +{ + PFILE_DATA pFileData; + LPCSTR szReaddedFile = "AddedFile_##.txt"; + LPCSTR szFileMask = "AddedFile_%02u.txt"; + TLogHelper Logger("ListFilePos", szPlainName); + HANDLE hMpq = NULL; // Handle of created archive + char szArchivedName[MAX_PATH]; + DWORD dwMaxFileCount = 0x0E; + DWORD dwFileCount = 0; + size_t i; + DWORD dwErrCode; + + // Create a new archive with the limit of 0x20 files + dwErrCode = CreateNewArchive(&Logger, szPlainName, MPQ_CREATE_ARCHIVE_V4 | MPQ_CREATE_LISTFILE | MPQ_CREATE_ATTRIBUTES, dwMaxFileCount, &hMpq); + + // Add maximum files files + if(dwErrCode == ERROR_SUCCESS) + { + for(i = 0; i < dwMaxFileCount; i++) + { + sprintf(szArchivedName, szFileMask, i); + dwErrCode = AddFileToMpq(&Logger, hMpq, szArchivedName, "This is a text data.", 0, 0, ERROR_SUCCESS); + if(dwErrCode != ERROR_SUCCESS) + break; + + dwFileCount++; + } + } + + // Delete few middle files + if(dwErrCode == ERROR_SUCCESS) + { + for(i = 0; i < (dwMaxFileCount / 2); i++) + { + sprintf(szArchivedName, szFileMask, i); + dwErrCode = RemoveMpqFile(&Logger, hMpq, szArchivedName, ERROR_SUCCESS); + if(dwErrCode != ERROR_SUCCESS) + break; + + dwFileCount--; + } + } + + // Close the archive + if(hMpq != NULL) + SFileCloseArchive(hMpq); + hMpq = NULL; + + // Reopen the archive to catch any asserts + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = OpenExistingArchiveWithCopy(&Logger, NULL, szPlainName, &hMpq); + + // Check that (listfile) is at the end + if(dwErrCode == ERROR_SUCCESS) + { + if(LoadMpqFile(Logger, hMpq, LISTFILE_NAME, 0, 0, &pFileData) == ERROR_SUCCESS) + { + if(pFileData->dwBlockIndex < dwFileCount) + Logger.PrintMessage("Unexpected file index of %s", LISTFILE_NAME); + STORM_FREE(pFileData); + } + + if(LoadMpqFile(Logger, hMpq, ATTRIBUTES_NAME, 0, 0, &pFileData) == ERROR_SUCCESS) + { + if(pFileData->dwBlockIndex <= dwFileCount) + Logger.PrintMessage("Unexpected file index of %s", ATTRIBUTES_NAME); + STORM_FREE(pFileData); + } + + // Add new file to the archive. It should be added to the last position + dwErrCode = AddFileToMpq(&Logger, hMpq, szReaddedFile, "This is a re-added file.", 0, 0, ERROR_SUCCESS); + if(dwErrCode == ERROR_SUCCESS) + { + // Force update of the tables + SFileFlushArchive(hMpq); + + // Load the file + if(LoadMpqFile(Logger, hMpq, szReaddedFile, 0, 0, &pFileData) == ERROR_SUCCESS) + { + if(pFileData->dwBlockIndex != dwFileCount) + Logger.PrintMessage("Unexpected file index of %s", szReaddedFile); + STORM_FREE(pFileData); + } + } + + SFileCloseArchive(hMpq); + } + + return dwErrCode; +} + +static DWORD TestCreateArchive_BigArchive(LPCTSTR szPlainName) +{ + TLogHelper Logger("TestBigArchive", szPlainName); + HANDLE hMpq = NULL; // Handle of created archive + TCHAR szLocalFileName[MAX_PATH]; + char szArchivedName[MAX_PATH]; + DWORD dwMaxFileCount = 0x20; + DWORD dwAddedCount = 0; + size_t i; + DWORD dwErrCode; + + // Create a new archive with the limit of 0x20 files + dwErrCode = CreateNewArchive(&Logger, szPlainName, MPQ_CREATE_ARCHIVE_V3 | MPQ_CREATE_LISTFILE | MPQ_CREATE_ATTRIBUTES, dwMaxFileCount, &hMpq); + if(dwErrCode == ERROR_SUCCESS) + { + LPCSTR szFileMask = "AddedFile_%02u.txt"; + + // Now add few really big files + CreateFullPathName(szLocalFileName, _countof(szLocalFileName), szMpqSubDir, _T("MPQ_1997_v1_Diablo1_DIABDAT.MPQ")); + Logger.UserTotal = (dwMaxFileCount / 2); + + for(i = 0; i < dwMaxFileCount / 2; i++) + { + sprintf(szArchivedName, szFileMask, i + 1); + dwErrCode = AddLocalFileToMpq(&Logger, hMpq, szArchivedName, szLocalFileName, 0, 0, true); + if(dwErrCode != ERROR_SUCCESS) + break; + + Logger.UserCount++; + dwAddedCount++; + } + } + + // Close the archive + if(hMpq != NULL) + SFileCloseArchive(hMpq); + hMpq = NULL; + + // Reopen the archive to catch any asserts + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = OpenExistingArchiveWithCopy(&Logger, NULL, szPlainName, &hMpq); + + // Check that (listfile) is at the end + if(dwErrCode == ERROR_SUCCESS) + { + CheckIfFileIsPresent(&Logger, hMpq, LISTFILE_NAME, true); + CheckIfFileIsPresent(&Logger, hMpq, ATTRIBUTES_NAME, true); + + SFileCloseArchive(hMpq); + } + + return dwErrCode; +} + +// Test replacing a file in an archive +static DWORD TestReplaceFile(LPCTSTR szMpqPlainName, LPCTSTR szFilePlainName, LPCSTR szFileFlags, DWORD dwCompression) +{ + TLogHelper Logger("TestModifyMpq", szMpqPlainName); + HANDLE hMpq = NULL; + TCHAR szFileFullName[MAX_PATH]; + TCHAR szMpqFullName[MAX_PATH]; + char szArchivedName[MAX_PATH]; + DWORD dwErrCode; + DWORD dwFileFlags = (DWORD)(DWORD_PTR)(szFileFlags); // szFileFlags is a file flags cast to LPCSTR + BYTE md5_file_in_mpq1[MD5_DIGEST_SIZE]; + BYTE md5_file_in_mpq2[MD5_DIGEST_SIZE]; + BYTE md5_file_in_mpq3[MD5_DIGEST_SIZE]; + BYTE md5_file_local[MD5_DIGEST_SIZE]; + + // Get the name of archived file as plain text. If the file shall be in a subfolder, + // its name must contain a hashtag, e.g. "staredit#scenario.chk" + StringCopy(szArchivedName, _countof(szArchivedName), szFilePlainName); + for(size_t i = 0; szArchivedName[i] != 0; i++) + szArchivedName[i] = (szArchivedName[i] == '#') ? '\\' : szArchivedName[i]; + + // Get the full path of the archive and local file + CreateFullPathName(szFileFullName, _countof(szFileFullName), szDataFileDir, szFilePlainName); + CreateFullPathName(szMpqFullName, _countof(szMpqFullName), NULL, szMpqPlainName); + dwFileFlags |= MPQ_FILE_REPLACEEXISTING | MPQ_FILE_COMPRESS; + + // Open an existing archive + dwErrCode = OpenExistingArchiveWithCopy(&Logger, szMpqPlainName, szMpqPlainName, &hMpq); + + // Open the file, load to memory, calculate hash + if(dwErrCode == ERROR_SUCCESS) + { + dwErrCode = LoadMpqFileMD5(Logger, hMpq, szArchivedName, md5_file_in_mpq1); + } + + // Open the local file, calculate hash + if(dwErrCode == ERROR_SUCCESS) + { + dwErrCode = LoadLocalFileMD5(&Logger, szFileFullName, md5_file_local); + } + + // Add the given file + if(dwErrCode == ERROR_SUCCESS) + { + // Add the file to MPQ + dwErrCode = AddLocalFileToMpq(&Logger, hMpq, + szArchivedName, + szFileFullName, + dwFileFlags, + dwCompression, + true); + } + + // Load the file from the MPQ again and compare both MD5's + if(dwErrCode == ERROR_SUCCESS) + { + // Load the file from the MPQ again + dwErrCode = LoadMpqFileMD5(Logger, hMpq, szArchivedName, md5_file_in_mpq2); + if(dwErrCode == ERROR_SUCCESS) + { + // New MPQ file must be different from the old one + if(!memcmp(md5_file_in_mpq2, md5_file_in_mpq1, MD5_DIGEST_SIZE)) + { + Logger.PrintError("Data mismatch after adding the file \"%s\"", szArchivedName); + dwErrCode = ERROR_CHECKSUM_ERROR; + } + + // New MPQ file must be identical to the local one + if(memcmp(md5_file_in_mpq2, md5_file_local, MD5_DIGEST_SIZE)) + { + Logger.PrintError("Data mismatch after adding the file \"%s\"", szArchivedName); + dwErrCode = ERROR_CHECKSUM_ERROR; + } + } + } + + // Compact the MPQ + if(dwErrCode == ERROR_SUCCESS) + { + // Compact the archive + Logger.PrintProgress("Compacting archive %s ...", szMpqPlainName); + if(!SFileSetCompactCallback(hMpq, CompactCallback, &Logger)) + dwErrCode = Logger.PrintError(_T("Failed to compact archive %s"), szMpqPlainName); + + // Some test archives (like MPQ_2022_v1_v4.329.w3x) can't be compacted. + // For that reason, we ignore the result of SFileCompactArchive(). + SFileCompactArchive(hMpq, NULL, 0); + SFileCloseArchive(hMpq); + hMpq = NULL; + } + + // Try to open the archive again. Ignore the previous errors + if(dwErrCode == ERROR_SUCCESS) + { + dwErrCode = OpenExistingArchive(&Logger, szMpqFullName, 0, &hMpq); + if(dwErrCode == ERROR_SUCCESS) + { + // Load the file from the MPQ again + dwErrCode = LoadMpqFileMD5(Logger, hMpq, szArchivedName, md5_file_in_mpq3); + if(dwErrCode == ERROR_SUCCESS) + { + // New MPQ file must be the same like the local one + if(memcmp(md5_file_in_mpq3, md5_file_local, MD5_DIGEST_SIZE)) + { + Logger.PrintError("Data mismatch after adding the file \"%s\"", szArchivedName); + dwErrCode = ERROR_CHECKSUM_ERROR; + } + } + + SFileCloseArchive(hMpq); + hMpq = NULL; + } + } + + // Finally, close the archive + if(hMpq != NULL) + SFileCloseArchive(hMpq); + return dwErrCode; +} + +static bool TestUtfConversion(const void * lpString) +{ + LPTSTR szBuffer; + LPBYTE pbBuffer; + size_t nLength1 = 0; + size_t nLength2 = 0; + DWORD dwErrCode1; + DWORD dwErrCode2; + TCHAR szWideBuffer[1]; + BYTE szByteBuffer[1]; + int nResult; + + // Get the number of bytes of the buffer while the output buffer is 0 + dwErrCode1 = SMemUTF8ToFileName(NULL, 0, lpString, NULL, 0, &nLength1); + + // Check the number of bytes when the buffer is non-NULL, but buffer length is insufficient + dwErrCode2 = SMemUTF8ToFileName(szWideBuffer, _countof(szWideBuffer), lpString, NULL, 0, &nLength2); + ASSERT_TRUE(dwErrCode2 == dwErrCode1); + ASSERT_TRUE(nLength2 == nLength1); + + // Check the number of bytes when the buffer is non-NULL, and buffer length is sufficient + if((szBuffer = STORM_ALLOC(TCHAR, nLength1)) != NULL) + { + dwErrCode2 = SMemUTF8ToFileName(szBuffer, nLength1, lpString, NULL, 0, &nLength2); + ASSERT_TRUE(dwErrCode2 == dwErrCode1); + ASSERT_TRUE(nLength2 == nLength1); + + // Get the number of bytes of the buffer while the output buffer is 0 + dwErrCode1 = SMemFileNameToUTF8(NULL, 0, szBuffer, NULL, 0, &nLength1); + + // Check the number of bytes when the buffer is non-NULL, but buffer length is insufficient + dwErrCode2 = SMemFileNameToUTF8(szByteBuffer, _countof(szByteBuffer), szBuffer, NULL, 0, &nLength2); + ASSERT_TRUE(dwErrCode2 == dwErrCode1); + ASSERT_TRUE(nLength2 == nLength1); + + // Check the conversion into a buffer large enough + if((pbBuffer = STORM_ALLOC(BYTE, nLength1)) != NULL) + { + dwErrCode2 = SMemFileNameToUTF8(pbBuffer, nLength1, szBuffer, NULL, 0, &nLength2); + ASSERT_TRUE(dwErrCode2 == dwErrCode1); + ASSERT_TRUE(nLength2 == nLength1); + + nResult = memcmp(pbBuffer, lpString, nLength1); + ASSERT_TRUE(nResult == 0); + + STORM_FREE(pbBuffer); + } + + STORM_FREE(szBuffer); + } + return true; +} + +static DWORD TestUtf8Conversions(const BYTE * szTestString, const TCHAR * szListFile) +{ + SFILE_FIND_DATA sf; + HANDLE hFind; + TCHAR szFullPath[MAX_PATH]; + + // Check conversion of the invalid UTF8 string + TestUtfConversion(szTestString); + + // Create full path of the listfile + CreateFullPathName(szFullPath, _countof(szFullPath), szListFileDir, szListFile); + + // Test all file names in the Chinese listfile + if(szListFile && szListFile[0]) + { + hFind = SListFileFindFirstFile(NULL, szFullPath, "*", &sf); + if(hFind != NULL) + { + while(SListFileFindNextFile(hFind, &sf)) + { + if(!TestUtfConversion(sf.cFileName)) + { + return ERROR_INVALID_DATA; + } + } + SListFileFindClose(hFind); + } + } + return ERROR_SUCCESS; +} + +static DWORD TestUseAfterFree(LPCTSTR szPlainName) +{ + TLogHelper Logger("TestUseAfterFree", szPlainName); + HANDLE hMpq; + HANDLE hFile; + DWORD dwErrCode; + + // Create new archive + dwErrCode = CreateNewArchive(&Logger, szPlainName, MPQ_CREATE_ARCHIVE_V1 | MPQ_CREATE_LISTFILE | MPQ_CREATE_ATTRIBUTES, 32, &hMpq); + if(dwErrCode == ERROR_SUCCESS) + { + SFileCreateFile(hMpq, "foo", 0ULL, 8, 0, 0, &hFile); + SFileCloseArchive(hMpq); + SFileCloseFile(hFile); + } + + dwErrCode = CreateNewArchive(&Logger, szPlainName, MPQ_CREATE_ARCHIVE_V1 | MPQ_CREATE_LISTFILE | MPQ_CREATE_ATTRIBUTES, 32, &hMpq); + if(dwErrCode == ERROR_SUCCESS) + { + if(SFileCreateFile(hMpq, "foo", 0ULL, 8, 0, 0, &hFile)) + { + char nameBuf[260]; + + SFileSetMaxFileCount(hMpq, 64); + SFileGetFileName(hFile, nameBuf); + SFileCloseFile(hFile); + } + SFileCloseArchive(hMpq); + } + return dwErrCode; +} + +//----------------------------------------------------------------------------- +// Tables + +static LPCTSTR szSigned1 = _T("STANDARD.SNP"); +static LPCTSTR szSigned2 = _T("StarDat.mpq"); +static LPCTSTR szSigned3 = _T("War2Patch_202.exe"); +static LPCTSTR szSigned4 = _T("(10)DustwallowKeys.w3m"); +static LPCTSTR szSigned5 = _T("WoW-1.2.3.4211-enUS-patch.exe"); +static LPCTSTR szSigned6 = _T("WoW-1.11.2.5464-to-1.12.0.5595-enUS-patch.exe"); +static LPCTSTR szSigned7 = _T("WoW-3.0.1.8337-to-3.0.1.8392-enGB-patch.exe"); +static LPCTSTR szSigned8 = _T("wow-final.MPQ"); + +static LPCTSTR szDiabdatMPQ = _T("MPQ_1997_v1_Diablo1_DIABDAT.MPQ"); + +static const TEST_EXTRA_ONEFILE LfBliz = {ListFile, _T("ListFile_Blizzard.txt")}; +static const TEST_EXTRA_ONEFILE LfWotI = {ListFile, _T("ListFile_WarOfTheImmortals.txt")}; +static const TEST_EXTRA_ONEFILE LfBad1 = {ListFile, _T("ListFile_UTF8_Bad.txt")}; + +static const BYTE szMpqFileNameUTF8[] = {0x4D, 0x50, 0x51, 0x5F, 0x32, 0x30, 0x32, 0x34, 0x5F, 0x76, 0x31, 0x5F, 0xE6, 0x9D, 0x82, 0xE9, 0xB1, 0xBC, 0xE5, 0x9C, 0xB0, 0xE7, 0x89, 0xA2, 0x5F, 0x30, 0x2E, 0x30, 0x38, 0x34, 0x62, 0x65, 0x74, 0x61, 0x34, 0x36, 0x2E, 0x77, 0x33, 0x78, 0x00}; +static const BYTE szLstFileNameUTF8[] = {0x4C, 0x69, 0x73, 0x74, 0x46, 0x69, 0x6C, 0x65, 0x5F, 0xE6, 0x9D, 0x82, 0xE9, 0xB1, 0xBC, 0xE5, 0x9C, 0xB0, 0xE7, 0x89, 0xA2, 0x5F, 0x30, 0x2E, 0x30, 0x38, 0x34, 0x62, 0x65, 0x74, 0x61, 0x34, 0x36, 0x2E, 0x74, 0x78, 0x74, 0x00}; + +static const BYTE FileNameInvalidUTF8[] = +{ +// Hexadecimal Binary UTF-16 String +// ---- --------------------------------- ------ ------ + 0x7c, // --> 01111100 --> 0x007c %u[7cb7] + 0xb7, // --> 10110111(bad) --> 0xfffd + 0xc9, 0xb7, // --> 11001001 10110111 --> 0x0277 \x0277 + 0xc9, /* ca */ // --> 11001001 11001010(bad) --> 0xfffd %u[c9cac0bde7] + 0xca, /* c0 */ // --> 11001010 11000000(bad) --> 0xfffd + 0xc0, /* bd */ // --> 11000000 10111101(bad) --> 0x003d(bad) + 0xbd, // --> 10111101(bad) --> 0xfffd + 0xe7, /* c4 */ // --> 11100111 11000100(bad) --> 0xfffd + 0xc4, 0xa7, // --> 11000100 10100111 --> 0x0127 \x0127 + 0xca, /* de */ // --> 11001010 11011110(bad) --> 0xfffd %ca + 0xde, 0xbb, // --> 11011110 10111011 --> 0x07bb \x07bb + 0xb6, // --> 10110110(bad) --> 0xfffd %b6 + 0xd3, 0xad, // --> 11010011 10101101 --> 0x04ed \x04ed + 0xc4, /* fa */ // --> 11000100 11111010(bad) --> 0xfffd %u[c4fa] + 0xfa, /* 5f */ // --> 11111010 01011111(bad) --> 0xfffd + 0x5f, // --> 01011111 --> 0x005f _ + 0xa1, // --> 10100001(bad) --> 0xfffd %u[a1eea1f0a1ef] + 0xee, /* a1 f0 */ // --> 11101110 10100001 11110000(bad) --> 0xfffd + 0xa1, // --> 10100001(bad) --> 0xfffd + 0xf0, /* a1 ef */ // --> 11110000 10100001 11101111(bad) --> 0xfffd + 0xa1, // --> 10100001(bad) --> 0xfffd + 0xef, /* 5f */ // --> 11101111 01011111(bad) --> 0xfffd + 0x5f, // --> 01011111 --> 0x005f _ + 0xf0, /* 80 80 80 */ // --> 11110000 10000000 10000000 10000000 --> 0x0000(bad) %u[f0808080] + 0x80, // --> 10000000(bad) --> 0xfffd + 0x80, // --> 10000000(bad) --> 0xfffd + 0x80, // --> 10000000(bad) --> 0xfffd + 0xe9, 0xa3, 0x9e, // --> 11101001 10100011 10011110 --> 0x98de \x98de + 0xe4, 0xb8, 0x96, // --> 11100100 10111000 10010110 --> 0x4e16 \x4e16 + 0xe7, 0x95, 0x8c, // --> 11100111 10010101 10001100 --> 0x754c \x754c + 0xe9, 0xad, 0x94, // --> 11101001 10101101 10010100 --> 0x9b54 \x9b54 + 0xe5, 0x85, 0xbd, // --> 11100101 10000101 10111101 --> 0x517d \x517d + 0xe6, 0xac, 0xa2, // --> 11100110 10101100 10100010 --> 0x6b22 \x6b22 + 0xe8, 0xbf, 0x8e, // --> 11101000 10111111 10001110 --> 0x8fce \x8fce + 0xe6, 0x82, 0xa8, // --> 11100110 10000010 10101000 --> 0x60a8 \x60a8 + 0x2e, // --> 00101110 --> 0x002e \x002e + 0x6d, 0x64, 0x78, // --> 01101101 01100100 01111000 --> ".mdx" + 0x00 // --> 00000000 --> EOS +}; + +static const BYTE FileNameInvalidChar[] = "units/Orc/HealingWard/Rune2.blp"; + +static const TEST_EXTRA_UTF8 MpqUtf8 = {Utf8File, szMpqFileNameUTF8, szLstFileNameUTF8}; + +static const TEST_EXTRA_TWOFILES TwoFilesD1 = {TwoFiles, "music\\dintro.wav", "File00000023.xxx"}; +static const TEST_EXTRA_TWOFILES TwoFilesD2 = {TwoFiles, "waitingroombkgd.dc6"}; +static const TEST_EXTRA_TWOFILES TwoFilesW3M = {TwoFiles, "file00000002.blp"}; +static const TEST_EXTRA_TWOFILES TwoFilesW3X = {TwoFiles, "BlueCrystal.mdx"}; + +static const TEST_EXTRA_HASHVALS HashVals = +{ + HashValues, + { + {0x00000000, 0x00000000, "File00024483.blp"}, + {0x8bd6929a, 0xfd55129b, "ReplaceableTextures\\CommandButtons\\BTNHaboss79.blp"} + } +}; + +static const TEST_EXTRA_PATCHES PatchSC1 = +{ + PatchList, + _T("s1-1998-BroodWar.mpq\0"), + "music\\terran1.wav", + 0 +}; + +static const TEST_EXTRA_PATCHES PatchBETA = +{ + PatchList, + _T("wow-08-beta-patch.mpq\0"), + "Creature\\GnomeSpidertank\\FlameLickSmallBlue.blp", + 0 +}; + + +static const TEST_EXTRA_PATCHES Patch13286 = +{ + PatchList, + _T("wow-update-oldworld-13154.MPQ\0") + _T("wow-update-oldworld-13286.MPQ\0"), + "OldWorld\\World\\Model.blob", + 2 +}; + +static const TEST_EXTRA_PATCHES Patch15050 = +{ + PatchList, + _T("wow-update-13164.MPQ\0") + _T("wow-update-13205.MPQ\0") + _T("wow-update-13287.MPQ\0") + _T("wow-update-13329.MPQ\0") + _T("wow-update-13596.MPQ\0") + _T("wow-update-13623.MPQ\0") + _T("wow-update-base-13914.MPQ\0") + _T("wow-update-base-14007.MPQ\0") + _T("wow-update-base-14333.MPQ\0") + _T("wow-update-base-14480.MPQ\0") + _T("wow-update-base-14545.MPQ\0") + _T("wow-update-base-14946.MPQ\0") + _T("wow-update-base-15005.MPQ\0") + _T("wow-update-base-15050.MPQ\0"), + "World\\Model.blob", + 8 +}; + +static const TEST_EXTRA_PATCHES Patch16965 = +{ + PatchList, + _T("wow-update-enGB-16016.MPQ\0") + _T("wow-update-enGB-16048.MPQ\0") + _T("wow-update-enGB-16057.MPQ\0") + _T("wow-update-enGB-16309.MPQ\0") + _T("wow-update-enGB-16357.MPQ\0") + _T("wow-update-enGB-16516.MPQ\0") + _T("wow-update-enGB-16650.MPQ\0") + _T("wow-update-enGB-16844.MPQ\0") + _T("wow-update-enGB-16965.MPQ\0"), + "DBFilesClient\\BattlePetNPCTeamMember.db2", + 0 +}; + +static const TEST_EXTRA_PATCHES Patch32283 = +{ + PatchList, + _T("s2-update-base-23258.MPQ\0") + _T("s2-update-base-24540.MPQ\0") + _T("s2-update-base-26147.MPQ\0") + _T("s2-update-base-28522.MPQ\0") + _T("s2-update-base-30508.MPQ\0") + _T("s2-update-base-32283.MPQ\0"), + "TriggerLibs\\natives.galaxy", + 6 +}; + +static const TEST_EXTRA_PATCHES Patch32283a = +{ + PatchList, + _T("s2-update-enGB-23258.MPQ\0") + _T("s2-update-enGB-24540.MPQ\0") + _T("s2-update-enGB-26147.MPQ\0") + _T("s2-update-enGB-28522.MPQ\0") + _T("s2-update-enGB-30508.MPQ\0") + _T("s2-update-enGB-32283.MPQ\0"), + "Assets\\Textures\\startupimage.dds", + 0 +}; + +static const TEST_EXTRA_PATCHES Patch34644 = +{ + PatchList, + _T("s2-update-base-23258.MPQ\0") + _T("s2-update-base-24540.MPQ\0") + _T("s2-update-base-26147.MPQ\0") + _T("s2-update-base-28522.MPQ\0") + _T("s2-update-base-32384.MPQ\0") + _T("s2-update-base-34644.MPQ\0"), + "TriggerLibs\\GameData\\GameData.galaxy", + 2 +}; + +static const TEST_EXTRA_PATCHES Patch34644m = +{ + PatchList, + _T("s2-update-base-23258.MPQ\0") + _T("s2-update-base-24540.MPQ\0") + _T("s2-update-base-26147.MPQ\0") + _T("s2-update-base-28522.MPQ\0") + _T("s2-update-base-32384.MPQ\0") + _T("s2-update-base-34644.MPQ\0"), + "Maps\\Campaign\\THorner03.SC2Map\\BankList.xml", + 3 +}; + +static const TEST_EXTRA_PATCHES Patch36281 = +{ + PatchList, + _T("s2-update-enGB-23258.MPQ\0") + _T("s2-update-enGB-24540.MPQ\0") + _T("s2-update-enGB-26147.MPQ\0") + _T("s2-update-enGB-28522.MPQ\0") + _T("s2-update-enGB-32384.MPQ\0") + _T("s2-update-enGB-34644.MPQ\0") + _T("s2-update-enGB-36281.MPQ\0"), + "LocalizedData\\GameHotkeys.txt", + 6 +}; + +static const TEST_EXTRA_PATCHES PatchH3604 = +{ + PatchList, + _T("hs-0-3604-Win-final.MPQ\0"), + "Hearthstone.exe", + 1 +}; + +static const TEST_EXTRA_PATCHES PatchH6898 = +{ + PatchList, + _T("hs-0-5314-Win-final.MPQ\0") + _T("hs-5314-5435-Win-final.MPQ\0") + _T("hs-5435-5506-Win-final.MPQ\0") + _T("hs-5506-5834-Win-final.MPQ\0") + _T("hs-5834-6024-Win-final.MPQ\0") + _T("hs-6024-6141-Win-final.MPQ\0") + _T("hs-6141-6187-Win-final.MPQ\0") + _T("hs-6187-6284-Win-final.MPQ\0") + _T("hs-6284-6485-Win-final.MPQ\0") + _T("hs-6485-6898-Win-final.MPQ\0"), + "Hearthstone_Data\\Managed\\Assembly-Csharp.dll", + 10 +}; + +static const TEST_INFO1 TestList_StreamOps[] = +{ + {_T("MPQ_2013_v4_alternate-original.MPQ"), NULL, NULL, 0}, + {_T("MPQ_2013_v4_alternate-original.MPQ"), NULL, NULL, STREAM_FLAG_READ_ONLY}, + {_T("MPQ_2013_v4_alternate-complete.MPQ"), NULL, NULL, STREAM_FLAG_USE_BITMAP}, + {_T("part-file://MPQ_2009_v2_WoW_patch.MPQ.part"), NULL, NULL, 0}, + {_T("blk4-file://streaming/model.MPQ.0"), NULL, NULL, STREAM_PROVIDER_BLOCK4}, + {_T("mpqe-file://MPQ_2011_v2_EncryptedMpq.MPQE"), NULL, NULL, STREAM_PROVIDER_MPQE} +}; + +static const TEST_INFO1 TestList_MasterMirror[] = +{ + {_T("part-file://MPQ_2009_v1_patch-created.MPQ.part"), _T("MPQ_2009_v1_patch-original.MPQ"), NULL, 0}, + {_T("part-file://MPQ_2009_v1_patch-partial.MPQ.part"), _T("MPQ_2009_v1_patch-original.MPQ"), NULL, 1}, + {_T("part-file://MPQ_2009_v1_patch-complete.MPQ.part"), _T("MPQ_2009_v1_patch-original.MPQ"), NULL, 1}, + {_T("MPQ_2013_v4_alternate-created.MPQ"), _T("MPQ_2013_v4_alternate-original.MPQ"), NULL, 0}, + {_T("MPQ_2013_v4_alternate-incomplete.MPQ"), _T("MPQ_2013_v4_alternate-incomplete.MPQ"), NULL, 1}, + {_T("MPQ_2013_v4_alternate-complete.MPQ"), _T("MPQ_2013_v4_alternate-original.MPQ"), NULL, 1}, + + // Takes hell a lot of time!!! +// {_T("MPQ_2013_v4_alternate-downloaded.MPQ"), _T("http://www.zezula.net\\mpqs\\alternate.zip"), NULL, 0} +}; + +static const TEST_INFO1 Test_OpenMpqs[] = +{ + + // PoC's by Gabe Sherman, tinh0, Zao Yang + {_T("pocs/MPQ_2024_01_HeapOverrun.mpq"), NULL, "7008f95dcbc4e5d840830c176dec6969", 14}, + {_T("pocs/MPQ_2024_02_StackOverflow.mpq"), NULL, "7093fcbcc9674b3e152e74e8e8a937bb", 4}, + {_T("pocs/MPQ_2024_03_TooBigAlloc.mpq"), NULL, "--------------------------------", TFLG_WILL_FAIL}, + {_T("pocs/MPQ_2024_04_HeapOverflow.mpq"), NULL, "--------------------------------", TFLG_WILL_FAIL}, + {_T("pocs/MPQ_2024_05_HeapOverflow.mpq"), NULL, "0539ae020719654a0ea6e2627a8195f8", 14}, + {_T("pocs/MPQ_2024_06_HeapOverflowReadFile.mpq"), NULL, "d41d8cd98f00b204e9800998ecf8427e", 1}, + {_T("pocs/MPQ_2024_07_InvalidBitmapFooter.mpq"), NULL, "--------------------------------", TFLG_WILL_FAIL}, + {_T("pocs/MPQ_2024_08_InvalidSectorSize.mpq"), NULL, "--------------------------------", TFLG_WILL_FAIL}, + {_T("pocs/MPQ_2024_09_InvalidSectorSize.mpq"), NULL, "--------------------------------", TFLG_WILL_FAIL}, + {_T("pocs/MPQ_2024_10_HuffDecompressError.mpq"), NULL, "--------------------------------", TFLG_WILL_FAIL}, + {_T("pocs/MPQ_2024_10_SparseDecompressError.mpq"), NULL, "--------------------------------", TFLG_WILL_FAIL}, + {_T("pocs/MPQ_2024_11_HiBlockTablePosInvalid.mpq"), NULL, "--------------------------------", TFLG_WILL_FAIL}, + {_T("pocs/MPQ_2025_01_SectorTableBeyondEOF.mpq"), NULL, "--------------------------------", TFLG_WILL_FAIL}, + {_T("pocs/MPQ_2025_02_SectorOffsetSizeNotAligned.mpq"), NULL, "0cc175b9c0f1b6a831c399e269772661", TFLG_WILL_FAIL}, + {_T("pocs/MPQ_2025_03_InvalidPatchInfo.mpq"), NULL, "93b885adfe0da089cdf634904fd59f71", TFLG_WILL_FAIL}, + {_T("pocs/MPQ_2025_04_InvalidArchiveSize64.mpq"), NULL, "--------------------------------", TFLG_WILL_FAIL}, + {_T("pocs/MPQ_2025_05_AddFileError.mpq"), NULL, "ce9b8afed4221a53663d391f10691ba6", TFLG_WILL_FAIL}, + {_T("pocs/MPQ_2025_06_BadHashTableSize.mpq"), NULL, "00000000000000000000000000000000", TFLG_WILL_FAIL}, + {_T("pocs/MPQ_2025_07_BadHetTableSize.mpq"), NULL, "00000000000000000000000000000000", TFLG_WILL_FAIL}, + + // Correct or damaged archives + {_T("MPQ_1997_v1_Diablo1_DIABDAT.MPQ"), NULL, "554b538541e42170ed41cb236483489e", 2910, &TwoFilesD1}, // Base MPQ from Diablo 1 + {_T("MPQ_1997_v1_patch_rt_SC1B.mpq"), NULL, "43fe7d362955be68a708486e399576a7", 10}, // From Starcraft 1 BETA + {_T("MPQ_1997_v1_StarDat_SC1B.mpq"), NULL, "0094b23f28cfff7386071ef3bd19a577", 2468}, // From Starcraft 1 BETA + {_T("MPQ_1997_v1_INSTALL_SC1B.EXE_"), NULL, "3248460c89bb6f8e3b8fc3e08de7ffbb", 79}, // From Starcraft 1 BETA + {_T("MPQ_2016_v1_D2XP_IX86_1xx_114a.mpq"), NULL, "255d87a62f3c9518f72cf723a1818946", 221, &TwoFilesD2}, // Update MPQ from Diablo II (patch 2016) + {_T("MPQ_2018_v1_icon_error.w3m"), NULL, "fcefa25fb50c391e8714f2562d1e10ff", 19, &TwoFilesW3M}, + {_T("MPQ_1997_v1_Diablo1_STANDARD.SNP"), NULL, "5ef18ef9a26b5704d8d46a344d976c89", 2, &LfBliz}, // File whose archive's (signature) file has flags = 0x90000000 + {_T("MPQ_2012_v2_EmptyMpq.MPQ"), NULL, "00000000000000000000000000000000", 0}, // Empty archive (found in WoW cache - it's just a header) + {_T("MPQ_2013_v4_EmptyMpq.MPQ"), NULL, "00000000000000000000000000000000", 0}, // Empty archive (created artificially - it's just a header) + {_T("MPQ_2013_v4_patch-base-16357.MPQ"), NULL, "d41d8cd98f00b204e9800998ecf8427e", 1}, // Empty archive (found in WoW cache - it's just a header) + {_T("MPQ_2011_v4_InvalidHetEntryCount.MPQ"), NULL, "be4b49ecc3942d1957249f9da0021659", 6}, // Empty archive (with invalid HET entry count) + {_T("MPQ_2002_v1_BlockTableCut.MPQ"), NULL, "a9499ab74d939303d8cda7c397c36275", 287}, // Truncated archive + {_T("MPQ_2010_v2_HasUserData.s2ma"), NULL, "feff9e2c86db716b6ff5ffc906181200", 52}, // MPQ that actually has user data + {_T("MPQ_2014_v1_AttributesOneEntryLess.w3x"), NULL, "90451b7052eb0f1d6f4bf69b2daff7f5", 116}, // Warcraft III map whose "(attributes)" file has (BlockTableSize-1) entries + {_T("MPQ_2010_v3_expansion-locale-frFR.MPQ"), NULL, "0c8fc921466f07421a281a05fad08b01", 53}, // MPQ archive v 3.0 (the only one I know) + {_T("mpqe-file://MPQ_2011_v2_EncryptedMpq.MPQE"), NULL, "10e4dcdbe95b7ad731c563ec6b71bc16", 82}, // Encrypted archive from Starcraft II installer + {_T("part-file://MPQ_2010_v2_HashTableCompressed.MPQ.part"),NULL, "d41d8cd98f00b204e9800998ecf8427e", 14263}, // Partial MPQ with compressed hash table + {_T("blk4-file://streaming/model.MPQ.0"), NULL, "e06b00efb2fc7e7469dd8b3b859ae15d", 39914}, // Archive that is merged with multiple files + {_T("MPQ_2023_v2_MemoryCorruption.SC2Replay"), NULL, "4cf5021aa272298e64712a378a50df44", 10}, // MPQ archive v 2.0, archive size is wrong + {_T("MPQ_2023_v1_StarcraftMap.scm"), NULL, "7830c51700697dd3c175f086a3157b29", 4}, // StarCraft map from StarCraft: Brood War 1.16 + {_T("MPQ_2023_v1_BroodWarMap.scx"), NULL, "dd3afa3c2f5e562ce3ca91c0c605a71f", 3}, // Brood War map from StarCraft: Brood War 1.16 + {_T("MPQ_2023_v1_Volcanis.scm"), NULL, "522c89ca96d6736427b01f7c80dd626f", 3}, // Map modified with unusual file compression: ZLIB+Huffman + {_T("MPQ_2023_v4_UTF8.s2ma"), NULL, "97b7a686650f3307d135e1d1b017a36a", 67}, // Map contaning files with Chinese names (UTF8-encoded) + {_T("MPQ_2023_v1_GreenTD.w3x"), NULL, "a8d91fc4e52d7c21ff7feb498c74781a", 2004}, // Corrupt sector checksum table in file #A0 + + {_T("MPQ_2023_v4_1F644C5A.SC2Replay"), NULL, "b225828ffbf5037553e6a1290187caab", 17}, // Corrupt patch info of the "(attributes)" file + {_T(""), NULL, "67faeffd0c0aece205ac8b7282d8ad8e", 4697, &MpqUtf8}, // Chinese name of the MPQ + {_T("MPQ_2024_v1_BadUtf8_5.0.2.w3x"), NULL, "be34f9862758f021a1c6c77df3cd4f05", 6393, &LfBad1}, // Bad UTF-8 sequences in file names + + // Protected archives + {_T("MPQ_2002_v1_ProtectedMap_InvalidUserData.w3x"), NULL, "b900364cc134a51ddeca21a13697c3ca", 79}, + {_T("MPQ_2002_v1_ProtectedMap_InvalidMpqFormat.w3x"), NULL, "db67e894da9de618a1cdf86d02d315ff", 117}, + {_T("MPQ_2002_v1_ProtectedMap_Spazzler.w3x"), NULL, "72d7963aa799a7fb4117c55b7beabaf9", 470}, // Warcraft III map locked by the Spazzler protector + {_T("MPQ_2014_v1_ProtectedMap_Spazzler2.w3x"), NULL, "72d7963aa799a7fb4117c55b7beabaf9", 470}, // Warcraft III map locked by the Spazzler protector + {_T("MPQ_2014_v1_ProtectedMap_Spazzler3.w3x"), NULL, "e55aad2dd33cf68b372ca8e30dcb78a7", 130}, // Warcraft III map locked by the Spazzler protector + {_T("MPQ_2002_v1_ProtectedMap_BOBA.w3m"), NULL, "7b725d87e07a2173c42fe2314b95fa6c", 17}, // Warcraft III map locked by the BOBA protector + {_T("MPQ_2015_v1_ProtectedMap_KangTooJee.w3x"), NULL, "44111a3edf7645bc44bb1afd3a813576", 1715}, + {_T("MPQ_2015_v1_ProtectedMap_Somj2hM16.w3x"), NULL, "b411f9a51a6e9a9a509150c8d66ba359", 92}, + {_T("MPQ_2015_v1_ProtectedMap_Spazy.w3x"), NULL, "6e491bd055511435dcb4d9c8baed0516", 4089}, // Warcraft III map locked by Spazy protector + {_T("MPQ_2015_v1_MessListFile.mpq"), NULL, "15e25d5be124d8ad71519f967997efc2", 8}, + {_T("MPQ_2016_v1_ProtectedMap_TableSizeOverflow.w3x"), NULL, "ad81b43cbd37bbfa27e4bed4c17e6a81", 176}, + {_T("MPQ_2016_v1_ProtectedMap_HashOffsIsZero.w3x"), NULL, "d6e712c275a26dc51f16b3a02f6187df", 228}, + {_T("MPQ_2016_v1_ProtectedMap_Somj2.w3x"), NULL, "457cdbf97a9ca41cfe8ea130dafaa0bb", 21}, // Something like Somj 2.0 + {_T("MPQ_2016_v1_WME4_4.w3x"), NULL, "7ec2f4d0f3982d8b12d88bc08ef0c1fb", 640}, // Protector from China (2016-05-27) + {_T("MPQ_2016_v1_SP_(4)Adrenaline.w3x"), NULL, "b6f6d56f4f8aaef04c2c4b1f08881a8b", 16}, + {_T("MPQ_2016_v1_ProtectedMap_1.4.w3x"), NULL, "3c7908b29d3feac9ec952282390a242d", 5027}, + {_T("MPQ_2016_v1_KoreanFile.w3m"), NULL, "805d1f75712472a81c6df27b2a71f946", 18}, + {_T("MPQ_2017_v1_Eden_RPG_S2_2.5J.w3x"), NULL, "cbe1fd7ed5ed2fc005fba9beafcefe40", 16334}, // Protected by PG1.11.973 + {_T("MPQ_2017_v1_BigDummyFiles.w3x"), NULL, "f4d2ee9d85d2c4107e0b2d00ff302dd7", 9086}, + {_T("MPQ_2017_v1_TildeInFileName.mpq"), NULL, "f203e3979247a4dbf7f3828695ac810c", 5}, + {_T("MPQ_2018_v1_EWIX_v8_7.w3x"), NULL, "12c0f4e15c7361b7c13acd37a181d83b", 857, &TwoFilesW3X}, + {_T("MPQ_2020_v4_FakeMpqHeaders.SC2Mod"), NULL, "f45392f6523250c943990a017c230b41", 24}, // Archive that has two fake headers before the real one + {_T("MPQ_2020_v4_NP_Protect_1.s2ma"), NULL, "1a1ea40ac1165bcdb4f2e434edfc7636", 21}, // SC2 map that is protected by the NP_Protect + {_T("MPQ_2020_v4_NP_Protect_2.s2ma"), NULL, "7d1a379da8bd966da1f4fa6e4646049b", 55}, // SC2 map that is protected by the NP_Protect + {_T("MPQ_2015_v1_flem1.w3x"), NULL, "1c4c13e627658c473e84d94371e31f37", 20}, + {_T("MPQ_2002_v1_ProtectedMap_HashTable_FakeValid.w3x"), NULL, "5250975ed917375fc6540d7be436d4de", 114}, + {_T("MPQ_2021_v1_CantExtractCHK.scx"), NULL, "055fd548a789c910d9dd37472ecc1e66", 28}, + {_T("MPQ_2022_v1_Sniper.scx"), NULL, "2e955271b70b79344ad85b698f6ce9d8", 64}, // Multiple items in hash table for staredit\scenario.chk (locale=0, platform=0) + {_T("MPQ_2022_v1_OcOc_Bound_2.scx"), NULL, "25cad16a2fb4e883767a1f512fc1dce7", 16}, + {_T("MPQ_2023_v1_Lusin2Rpg1.28.w3x"), NULL, "9c21352f06cf763fcf05e8a2691e6194", 10305, &HashVals}, + {_T("MPQ_2024_v1_300TK2.09p.w3x"), NULL, "e442e3d2e7d457b9ba544544013b791f", 32588}, // Fake MPQ User data, fake MPQ header at offset 0x200 + {_T("MPQ_2025_v1_Legion_TD_11_2d-BETA_2_TeamOZE.w3x"), NULL, "08efaaa11cafe5e8921a6f112b2fa458", 626}, + {_T("MPQ_2026_v1_The Art of Defense v4.11 G0A.w3x"), NULL, "892ec8421b34e35899624fc63b451327", 952}, // Some files have slash characters in their names + {_T("MPQ_2026_v1_BadTablesSize.scx"), NULL, "2dd05809bdcb466bbe35778086790caf", 3}, // Fake MPQ header at offset 0 + + // ASI plugins + {_T("mix-mpq/AHF04patch.mix"), NULL, "d3c6aac48bc12813ef5ce4ad113e58bf", 2891}, // MIX file + {_T("mix-mpq/hs0.1.asi"), NULL, "50cba7460a6e6d270804fb9776a7ec4f", 6022}, + {_T("mix-mpq/hs0.8.asi"), NULL, "6a40f733428001805bfe6e107ca9aec1", 11352}, // Items in hash table have platform = 0xFF + {_T("mix-mpq/MoeMoeMod.asi"), NULL, "89b923c7cde06de48815844a5bbb0ec4", 2578}, + + // MPQ modifications from Chinese games + {_T("MPx_2013_v1_LongwuOnline.mpk"), NULL, "548f7db88284097f7e94c95a08c5bc24", 469}, // MPK archive from Longwu online + {_T("MPx_2013_v1_WarOfTheImmortals.sqp"), NULL, "a048f37f7c6162a96253d8081722b6d9", 9396, &LfWotI}, // SQP archive from War of the Immortals + {_T("MPx_2022_v1_Music.mpk"), NULL, "fc369cff4ff4b573dd024de963e4cdd5", 650}, // MPK archive from Warriors of the Ghost Valley + {_T("MPx_2022_v1_Scp.mpk"), NULL, "9cb453dc159f2e667c14f48957fd9e77", 113}, // MPK archive from Warriors of the Ghost Valley + {_T("MPx_2022_v1_UI.mpk"), NULL, "677a36b458d528a3158ced3dfb711e49", 3086}, // MPK archive from Warriors of the Ghost Valley + + // Patched MPQs + {_T("MPQ_1998_v1_StarCraft.mpq"), NULL, "5ecef2f41c5fd44c264e269416de9495", 1943, &PatchSC1}, // Patched MPQ from StarCraft I + {_T("MPQ_2005_v1_texture.MPQ"), NULL, "f95f44bfd8e6dde30508bfec2d4cb842", 45533, &PatchBETA}, // WoW BETA: Patched "texture.MPQ" + {_T("MPQ_2012_v4_OldWorld.MPQ"), NULL, "07643ec62864b4dd4fc8f8a6a16ce006", 71439, &Patch13286}, // WoW 13286: Patched "OldWorld.MPQ" + {_T("MPQ_2013_v4_world.MPQ"), NULL, "af9baeceab20139bbf94d03f99170ae0", 48930, &Patch15050}, // WoW 15050: Patched "world.MPQ" + {_T("MPQ_2013_v4_locale-enGB.MPQ"), NULL, "d39e743aaf6dad51d643d65e6e564804", 14349, &Patch16965}, // WoW 16965: Patched "locale-enGB.MPQ" + {_T("MPQ_2013_v4_Base1.SC2Data"), NULL, "28a0f3cff1f400feb268ddae0efb2985", 1459, &Patch32283}, // SC2 32283: Patched "Base1.SC2Data" + {_T("MPQ_2013_v4_Mods#Core.SC2Mod#enGB.SC2Assets"), NULL, "89df7ddac15700721b3f4c37d2673c1f", 11, &Patch32283a},// SC2 32283: Patched "Mods#Core.SC2Mod#enGB.SC2Assets" + {_T("MPQ_2013_v4_Base1.SC2Data"), NULL, "7b65d0a3c3c0e67e4c61f53f277e5ae7", 1459, &Patch34644}, // SC2 34644: Patched "Base1.SC2Data" + {_T("MPQ_2013_v4_Base3.SC2Maps"), NULL, "831ed96de221b4018d4bc9e09593c540", 2080, &Patch34644m},// SC2 34644: Patched "Base3.SC2Maps" + {_T("MPQ_2013_v4_Mods#Liberty.SC2Mod#enGB.SC2Data"), NULL, "fde3842552c1a9cd5ceee0e571227d18", 17, &Patch36281}, // SC2 36281: Patched "Mods#Liberty.SC2Mod#enGB.SC2Data" + {_T("MPQ_2014_v4_base-Win.MPQ"), NULL, "337b609b2469a6732f2837eae8f730a4", 207, &PatchH3604}, // HSTN 3604: Patched "base-Win.MPQ" + {_T("MPQ_2014_v4_base-Win.MPQ"), NULL, "28c3447bdc6b221b5cb346123ea03a94", 234, &PatchH6898}, // HSTN 6898: Patched "base-Win.MPQ" + + // Signed archives + {_T("MPQ_1997_v1_Diablo1_STANDARD.SNP"), szSigned1, "5ef18ef9a26b5704d8d46a344d976c89", 2 | TFLG_SIGCHECK_BEFORE}, + {_T("MPQ_1998_v1_StarDat.mpq"), szSigned2, "2530cb937565fd41b1dc0443697096a2", 2925 | TFLG_SIGN_ARCHIVE | TFLG_SIGCHECK_AFTER}, + {_T("MPQ_1999_v1_WeakSignature.exe"), szSigned3, "c1084033d0bd5f7e2b9b78b600c0bba8", 24 | TFLG_SIGCHECK_BEFORE}, + {_T("MPQ_1999_v1_WeakSignature.exe"), szSigned3, "807fe2e4d38eccf5ee6bc88f5ee5940d", 25 | TFLG_SIGCHECK_BEFORE | TFLG_MODIFY | TFLG_SIGCHECK_AFTER}, + {_T("MPQ_2002_v1_StrongSignature.w3m"), szSigned4, "7b725d87e07a2173c42fe2314b95fa6c", 17 | TFLG_SIGCHECK_BEFORE}, + {_T("MPQ_2003_v1_WeakSignatureEmpty.exe"), szSigned5, "1e24a80dafa5285a0aee9470263e5b7c", 12259 | TFLG_SIGCHECK_BEFORE}, + {_T("MPQ_2006_v1_WoW-1.11.2.5464-patch.exe_"), szSigned6, "6d1ccbfc344b6a2bc4ddf14867e45fea", 952 | TFLG_SIGCHECK_BEFORE}, + {_T("MPQ_2007_v2_StrongSignature1.exe"), szSigned7, "c553320a2f841ccb86c0643f58d8488a", 23 | TFLG_SIGCHECK_BEFORE}, + {_T("MPQ_2007_v2_StrongSignature2.MPQ"), szSigned8, "53cedaf7ba8c67b2e2ca95d5eb2b9380", 1622 | TFLG_SIGCHECK_BEFORE}, + + // Multi-file archive with wrong prefix to see how StormLib deals with it + {_T("flat-file://streaming/model.MPQ.0"), _T("flat-file://model.MPQ.0"), NULL, 0 | TFLG_WILL_FAIL}, + + // An archive that has been invalidated by extending an old valid MPQ + {_T("MPQ_2013_vX_Battle.net.MPQ"), NULL, NULL, 0 | TFLG_WILL_FAIL}, + + // Check whether read-only archive fails with update + {_T("MPQ_1997_v1_Diablo1_DIABDAT.MPQ"), NULL, "554b538541e42170ed41cb236483489e", 2910 | TFLG_READ_ONLY | TFLG_MODIFY | TFLG_WILL_FAIL}, + {_T("MPQ_1997_v1_Diablo1_DIABDAT.MPQ"), szDiabdatMPQ, "84fcb4aefbd0deac5b5159ec11922dbf", 2911 | TFLG_MODIFY}, + + // Check the GetFileInfo operations + {_T("MPQ_2002_v1_StrongSignature.w3m"), NULL, "7b725d87e07a2173c42fe2314b95fa6c", 17 | TFLG_GET_FILE_INFO}, + {_T("MPQ_2013_v4_SC2_EmptyMap.SC2Map"), NULL, "88e1b9a88d56688c9c24037782b7bb68", 33 | TFLG_GET_FILE_INFO}, + +}; + +static const TEST_INFO1 Test_ReopenMpqs[] = +{ + // Test the archive compacting feature + {_T("MPQ_2010_v3_expansion-locale-frFR.MPQ"), NULL, "0c8fc921466f07421a281a05fad08b01", 53 | TFLG_COMPACT | TFLG_ADD_USER_DATA | TFLG_HAS_LISTFILE | TFLG_HAS_ATTRIBUTES}, + {_T("MPQ_2016_v1_00000.pak"), NULL, "76c5c4dffee8a9e3568e22216b5f0b94", 2072 | TFLG_COMPACT | TFLG_HAS_LISTFILE}, + {_T("MPQ_2013_v4_SC2_EmptyMap.SC2Map"), NULL, "88e1b9a88d56688c9c24037782b7bb68", 33 | TFLG_COMPACT | TFLG_ADD_USER_DATA | TFLG_HAS_LISTFILE | TFLG_HAS_ATTRIBUTES}, + {_T("MPQ_2013_v4_expansion1.MPQ"), NULL, "c97d2b4e2561d3eb3a728d72a74d86c2", 15633 | TFLG_COMPACT | TFLG_ADD_USER_DATA | TFLG_HAS_LISTFILE | TFLG_HAS_ATTRIBUTES}, + + // Adding a file to MPQ that had size of the file table equal + // or greater than hash table, but has free entries + {_T("MPQ_2014_v1_out1.w3x"), NULL, "222e685bd76e1af6d267ea1e0c27371f", 39 | TFLG_MODIFY | TFLG_HAS_LISTFILE}, + {_T("MPQ_2014_v1_out2.w3x"), NULL, "514ab40dc72fc29965cc30b2e0d63674", 34 | TFLG_MODIFY | TFLG_HAS_LISTFILE}, + + // Adding a file to MPQ and testing that (listfile) and (attributes) has the same state like before + {_T("MPQ_1997_v1_Diablo1_DIABDAT.MPQ"), NULL, "554b538541e42170ed41cb236483489e", 2910 | TFLG_MODIFY}, + {_T("MPQ_2013_v4_SC2_EmptyMap.SC2Map"), NULL, "88e1b9a88d56688c9c24037782b7bb68", 33 | TFLG_MODIFY | TFLG_HAS_LISTFILE | TFLG_HAS_ATTRIBUTES}, + + // Adding a large file to archive version 1. This must fail + {_T("MPQ_2002_v1_LargeMapFile.w3m"), NULL, "2f302705280f0cf9da50ac10e3d71522", 22 | TFLG_MODIFY | TFLG_HAS_LISTFILE | TFLG_HAS_ATTRIBUTES | TFLG_BIGFILE | TFLG_WILL_FAIL} +}; + +// Tests for signature file +static const TEST_INFO1 Test_Signature[] = +{ + {_T("MPQ_1999_v1_WeakSigned1.mpq"), NULL, NULL, SFLAG_CREATE_ARCHIVE | SFLAG_SIGN_AT_CREATE | SFLAG_MODIFY_ARCHIVE | SFLAG_SIGN_ARCHIVE | SFLAG_VERIFY_AFTER}, + {_T("MPQ_1999_v1_WeakSigned1.mpq"), NULL, NULL, SFLAG_CREATE_ARCHIVE | SFLAG_MODIFY_ARCHIVE | SFLAG_SIGN_ARCHIVE | SFLAG_VERIFY_AFTER}, +}; + +static const TEST_INFO1 Test_ReplaceFile[] = +{ + {_T("MPQ_2014_v4_Base.StormReplay"), _T("replay.message.events"), (LPCSTR)(MPQ_FILE_SINGLE_UNIT), MPQ_COMPRESSION_ZLIB}, + {_T("MPQ_2022_v1_v4.329.w3x"), _T("war3map.j"), (LPCSTR)(MPQ_FILE_SINGLE_UNIT), MPQ_COMPRESSION_ZLIB}, + {_T("MPQ_2023_v1_StarcraftMap.scm"), _T("staredit#scenario.chk"), NULL, MPQ_COMPRESSION_ZLIB | MPQ_COMPRESSION_HUFFMANN}, +}; + +static const TEST_INFO2 Test_CreateMpqs[] = +{ + // Create empty MPQs containing nothing + {"EmptyMpq_v2.mpq", NULL, NULL, CFLG_V2 | CFLG_EMPTY}, + {"EmptyMpq_v4.mpq", NULL, NULL, CFLG_V4 | CFLG_EMPTY}, + + // Create empty MPQs with localized names + {(LPCSTR)szPlainName_CZE, NULL, NULL, CFLG_V2 | 15}, // (UTF-8) Czech + {(LPCSTR)szPlainName_RUS, NULL, NULL, CFLG_V2 | 58}, // (UTF-8) Russian + {(LPCSTR)szPlainName_GRC, NULL, NULL, CFLG_V2 | 15874}, // (UTF-8) Greek + {(LPCSTR)szPlainName_CHN, NULL, NULL, CFLG_V4 | 87541}, // (UTF-8) Chinese + {(LPCSTR)szPlainName_JPN, NULL, NULL, CFLG_V4 | 87541}, // (UTF-8) Japanese + {(LPCSTR)szPlainName_SAU, NULL, NULL, CFLG_V4 | 87541}, // (UTF-8) Arabic + + // Create archive containing files with non-std names + {"NonStdNames.mpq", NULL, NULL, CFLG_NONSTD_NAMES | 4000}, + + // Test creating of an archive the same way like MPQ Editor does + {"MpqEditorTest.mpq", "AddedFile.exe", NULL, CFLG_V2 | CFLG_MPQEDITOR | 4000}, +}; + +static const LPCSTR Test_CreateMpq_Localized[] = +{ + (LPCSTR)szPlainName_CZE, // (UTF-8) Czech + (LPCSTR)szPlainName_RUS, // (UTF-8) Russian + (LPCSTR)szPlainName_GRC, // (UTF-8) Greek + (LPCSTR)szPlainName_CHN, // (UTF-8) Chinese + (LPCSTR)szPlainName_JPN, // (UTF-8) Japanese + (LPCSTR)szPlainName_SAU // (UTF-8) Arabic +}; + +static void Test_PlayingSpace() +{ + size_t nOutLength = 0; + TCHAR szBuffer[128]; + + SMemUTF8ToFileName(szBuffer, _countof(szBuffer), FileNameInvalidChar, NULL, SFILE_UTF8_KEEP_INVALID_FCH, &nOutLength); + _tprintf(_T("%s\n"), szBuffer); +} + +//----------------------------------------------------------------------------- +// Main + +//#define TEST_COMMAND_LINE +#define TEST_LOCAL_LISTFILE +#define TEST_STREAM_OPERATIONS +#define TEST_MASTER_MIRROR +#define TEST_OPEN_MPQ +#define TEST_REOPEN_MPQ +#define TEST_VERIFY_SIGNATURE +#define TEST_REPLACE_FILE +#define TEST_VERIFY_HASHES +#define TEST_CREATE_MPQS +#define TEST_MISC_MPQS + +int _tmain(int argc, TCHAR * argv[]) +{ + DWORD dwErrCode = ERROR_SUCCESS; + +#if defined(_MSC_VER) && defined(_DEBUG) + _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); +#endif // defined(_MSC_VER) && defined(_DEBUG) + + // Initialize storage and mix the random number generator + printf("==== Test Suite for StormLib version %s ====\n", STORMLIB_VERSION_STRING); + dwErrCode = InitializeMpqDirectory(argv, argc); + + // Placeholder function for various testing purposes + Test_PlayingSpace(); + + // Test the UTF-8 conversions + TestUtf8Conversions(FileNameInvalidUTF8, LfBad1.szFile); + TestUtf8Conversions(FileNameInvalidChar, NULL); + + // Test the use-after-free scenarios + TestUseAfterFree(_T("Test-UAF.mpq")); + +#ifdef TEST_COMMAND_LINE + // Test-open MPQs from the command line. They must be plain name + // and must be placed in the Test-MPQs folder + for(int i = 2; i < argc; i++) + { + TestOpenArchive(argv[i], NULL, NULL, 0, &LfBliz); + } +#endif // TEST_COMMAND_LINE + +#ifdef TEST_LOCAL_LISTFILE // Tests on a local listfile + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = TestOnLocalListFile(_T("FLAT-MAP:listfile-test.txt")); + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = TestOnLocalListFile(_T("listfile-test.txt")); +#endif // TEST_LOCAL_LISTFILE + +#ifdef TEST_STREAM_OPERATIONS // Test file stream operations + if(dwErrCode == ERROR_SUCCESS) + { + for(size_t i = 0; i < _countof(TestList_StreamOps); i++) + { + dwErrCode = TestFileStreamOperations(TestList_StreamOps[i].szName1, TestList_StreamOps[i].dwFlags); + if(dwErrCode != ERROR_SUCCESS) + break; + } + } +#endif // TEST_STREAM_OPERATIONS + +#ifdef TEST_MASTER_MIRROR // Test master-mirror reading operations + if(dwErrCode == ERROR_SUCCESS) + { + for(size_t i = 0; i < _countof(TestList_MasterMirror); i++) + { + dwErrCode = TestReadFile_MasterMirror(TestList_MasterMirror[i].szName1, + TestList_MasterMirror[i].szName2, + TestList_MasterMirror[i].dwFlags != 0); + if(dwErrCode != ERROR_SUCCESS) + break; + } + } +#endif // TEST_MASTER_MIRROR + +#ifdef TEST_OPEN_MPQ // Test opening various archives - correct, damaged, protected + if(dwErrCode == ERROR_SUCCESS) + { + for(size_t i = 0; i < _countof(Test_OpenMpqs); i++) + { + dwErrCode = TestOpenArchive(Test_OpenMpqs[i]); + dwErrCode = ERROR_SUCCESS; + } + } +#endif // TEST_OPEN_MPQ + +#ifdef TEST_REOPEN_MPQ // Test operations involving reopening the archive + if(dwErrCode == ERROR_SUCCESS) + { + for(size_t i = 0; i < _countof(Test_ReopenMpqs); i++) + { + // Ignore the error code here; we want to see results of all opens + dwErrCode = TestReopenArchive(Test_ReopenMpqs[i].szName1, + Test_ReopenMpqs[i].szDataHash, + Test_ReopenMpqs[i].dwFlags); + dwErrCode = ERROR_SUCCESS; + } + } +#endif + +#ifdef TEST_VERIFY_SIGNATURE // Verify digital signatures of the archives + if(dwErrCode == ERROR_SUCCESS) + { + for(size_t i = 0; i < _countof(Test_Signature); i++) + { + // Ignore the error code here; we want to see results of all opens + dwErrCode = TestOpenArchive_SignatureTest(Test_Signature[i].szName1, + Test_Signature[i].szName1, + Test_Signature[i].dwFlags); + if(dwErrCode != ERROR_SUCCESS) + break; + } + } +#endif + +#ifdef TEST_REPLACE_FILE // Replace a file in archives + if(dwErrCode == ERROR_SUCCESS) + { + for(size_t i = 0; i < _countof(Test_ReplaceFile); i++) + { + // Ignore the error code here; we want to see results of all opens + dwErrCode = TestReplaceFile(Test_ReplaceFile[i].szName1, + Test_ReplaceFile[i].szName2, + Test_ReplaceFile[i].szDataHash, + Test_ReplaceFile[i].dwFlags); + if(dwErrCode != ERROR_SUCCESS) + break; + } + } +#endif + +#ifdef TEST_VERIFY_HASHES + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = VerifyFileHashes(szMpqSubDir); +#endif + +#ifdef TEST_CREATE_MPQS + if(dwErrCode == ERROR_SUCCESS) + { + for(size_t i = 0; i < _countof(Test_CreateMpqs); i++) + { + // Ignore the error code here; we want to see results of all opens + dwErrCode = TestCreateArchive(Test_CreateMpqs[i]); + if(dwErrCode != ERROR_SUCCESS) + break; + } + } +#endif + +#ifdef TEST_MISC_MPQS + + // Test creating of an archive the same way like MPQ Editor does + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = TestRenameFile(_T("MPQ_2002_v1_StrongSignature.w3m")); + + // Test creating of an archive the same way like MPQ Editor does + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = TestCreateArchive_TestGaps(_T("StormLibTest_GapsTest.mpq")); + + // Create an archive and fill it with files up to the max file count + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = TestCreateArchive_FillArchive(_T("StormLibTest_FileTableFull.mpq"), 0); + + // Create an archive and fill it with files up to the max file count + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = TestCreateArchive_FillArchive(_T("StormLibTest_FileTableFull.mpq"), MPQ_CREATE_LISTFILE); + + // Create an archive and fill it with files up to the max file count + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = TestCreateArchive_FillArchive(_T("StormLibTest_FileTableFull.mpq"), MPQ_CREATE_ATTRIBUTES); + + // Create an archive and fill it with files up to the max file count + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = TestCreateArchive_FillArchive(_T("StormLibTest_FileTableFull.mpq"), MPQ_CREATE_ATTRIBUTES | MPQ_CREATE_LISTFILE); + + // Create an archive, and increment max file count several times + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = TestCreateArchive_IncMaxFileCount(_T("StormLibTest_IncMaxFileCount.mpq")); + + // Create a MPQ file, add files with various flags + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = TestCreateArchive_FileFlagTest(_T("StormLibTest_FileFlagTest.mpq")); + + // Create a MPQ file, add a mono-WAVE file with various compressions + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = TestCreateArchive_WaveCompressionsTest(_T("StormLibTest_AddWaveMonoTest.mpq"), _T("wave-mono.wav")); + + // Create a MPQ file, add a mono-WAVE with 8 bits per sample file with various compressions + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = TestCreateArchive_WaveCompressionsTest(_T("StormLibTest_AddWaveMonoBadTest.mpq"), _T("wave-mono-bad.wav")); + + // Create a MPQ file, add a stereo-WAVE file with various compressions + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = TestCreateArchive_WaveCompressionsTest(_T("StormLibTest_AddWaveStereoTest.mpq"), _T("wave-stereo.wav")); + + // Check if the listfile is always created at the end of the file table in the archive + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = TestCreateArchive_ListFilePos(_T("StormLibTest_ListFilePos.mpq")); + + // Open a MPQ (add custom user data to it) + if(dwErrCode == ERROR_SUCCESS) + dwErrCode = TestCreateArchive_BigArchive(_T("StormLibTest_BigArchive_v4.mpq")); +#endif // TEST_MISC_MPQS + +#ifdef _MSC_VER + _CrtDumpMemoryLeaks(); +#endif // _MSC_VER + + return dwErrCode; +} diff --git a/dep/StormLib/test/TLogHelper.cpp b/dep/StormLib/test/TLogHelper.cpp new file mode 100644 index 0000000000..7c128f32c5 --- /dev/null +++ b/dep/StormLib/test/TLogHelper.cpp @@ -0,0 +1,663 @@ +/*****************************************************************************/ +/* TLogHelper.cpp Copyright (c) Ladislav Zezula 2013 */ +/*---------------------------------------------------------------------------*/ +/* Helper class for reporting StormLib tests */ +/* This file should be included directly from StormTest.cpp using #include */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 26.11.13 1.00 Lad Created */ +/*****************************************************************************/ + +//----------------------------------------------------------------------------- +// String replacements for format strings + +#if defined(STORMLIB_WINDOWS) || defined(CASCLIB_PLATFORM_WINDOWS) +#define TEST_PLATFORM_WINDOWS +#endif + +#ifdef _MSC_VER +#define fmt_I64u_w L"%I64u" +#define fmt_I64u_t _T("%I64u") +#define fmt_I64u_a "%I64u" +#define fmt_I64X_t _T("%I64X") +#define fmt_I64X_a "%I64X" +#else +#define fmt_I64u_t _T("%llu") +#define fmt_I64u_a "%llu" +#define fmt_I64X_t _T("%llX") +#define fmt_I64X_a "%llX" +#endif + +#define fmt_X_of_Y_a "(" fmt_I64u_a " of " fmt_I64u_a ")" +#define fmt_X_of_Y_t _T(fmt_X_of_Y_a) + +#ifdef __CASCLIB_SELF__ +#define TEST_MIN CASCLIB_MIN +#define TEST_PRINT_PREFIX false +#else +#define TEST_MIN STORMLIB_MIN +#define TEST_PRINT_PREFIX true +#endif + +//----------------------------------------------------------------------------- +// Local functions + +template +XCHAR * StringEnd(XCHAR * sz) +{ + while(sz[0] != 0) + sz++; + return sz; +} + +// ANSI version of the function - expects UTF-8 encoding +size_t ConsoleLength(const char * ptr, const char * end) +{ + size_t ccBytesEaten; + size_t nLength = 0; + DWORD dwErrCode; + + while(ptr < end) + { + DWORD dwCodePoint = 0; + + // Decode a single UTF-8 character + dwErrCode = UTF8_DecodeCodePoint((BYTE *)(ptr), (BYTE *)(end), dwCodePoint, ccBytesEaten); + if(dwErrCode != ERROR_SUCCESS && dwErrCode != ERROR_NO_UNICODE_TRANSLATION) + break; + + // Chinese chars occupy 1 extra char slot on console + if(0x5000 <= ptr[0] && ptr[0] <= 0xA000) + nLength++; + ptr += ccBytesEaten; + nLength++; + } + return nLength; +} + +#ifdef TEST_PLATFORM_WINDOWS +size_t ConsoleLength(const wchar_t * ptr, const wchar_t * end) +{ + size_t nLength = 0; + + while(ptr < end) + { + DWORD dwCodePoint = ptr[0]; + + // Chinese chars occupy more space + if(0x5000 <= dwCodePoint && dwCodePoint <= 0xA000) + nLength++; + ptr += 1; + nLength++; + } + return nLength; +} +#endif + +inline DWORD TestInterlockedIncrement(DWORD * PtrValue) +{ +#ifdef TEST_PLATFORM_WINDOWS + return (DWORD)InterlockedIncrement((LONG *)(PtrValue)); +#elif defined(__GNUC__) + return __sync_add_and_fetch(PtrValue, 1); +#else + return ++(*PtrValue); +#endif +} + +inline DWORD Test_GetLastError() +{ + return SErrGetLastError(); +} + +#ifdef TEST_PLATFORM_WINDOWS +wchar_t * CopyFormatCharacter(wchar_t * szBuffer, const wchar_t *& szFormat) +{ + static const wchar_t * szStringFormat = L"%s"; + static const wchar_t * szUint64Format = fmt_I64u_w; + + // String format + if(szFormat[0] == '%') + { + if(szFormat[1] == 's') + { + wcscpy(szBuffer, szStringFormat); + szFormat += 2; + return szBuffer + wcslen(szStringFormat); + } + + // Replace %I64u with the proper platform-dependent suffix + if(szFormat[1] == 'I' && szFormat[2] == '6' && szFormat[3] == '4' && szFormat[4] == 'u') + { + wcscpy(szBuffer, szUint64Format); + szFormat += 5; + return szBuffer + wcslen(szUint64Format); + } + } + + // Copy the character as-is + *szBuffer++ = *szFormat++; + return szBuffer; +} +#endif // TEST_PLATFORM_WINDOWS + +char * CopyFormatCharacter(char * szBuffer, const char *& szFormat) +{ + static const char * szStringFormat = "\"%s\""; + static const char * szUint64Format = fmt_I64u_a; + + // String format + if(szFormat[0] == '%') + { + if(szFormat[1] == 's') + { + StringCopy(szBuffer, 32, szStringFormat); + szFormat += 2; + return szBuffer + strlen(szStringFormat); + } + + // Replace %I64u with the proper platform-dependent suffix + if(szFormat[1] == 'I' && szFormat[2] == '6' && szFormat[3] == '4' && szFormat[4] == 'u') + { + StringCopy(szBuffer, 32, szUint64Format); + szFormat += 5; + return szBuffer + strlen(szUint64Format); + } + } + + // Copy the character as-is + *szBuffer++ = *szFormat++; + return szBuffer; +} + +size_t TestStrPrintfV(char * buffer, size_t nCount, const char * format, va_list argList) +{ + return vsnprintf(buffer, nCount, format, argList); +} + +size_t TestStrPrintf(char * buffer, size_t nCount, const char * format, ...) +{ + va_list argList; + size_t length; + + // Start the argument list + va_start(argList, format); + length = TestStrPrintfV(buffer, nCount, format, argList); + va_end(argList); + + return length; +} + +size_t TestStrPrintfV(wchar_t * buffer, size_t nCount, const wchar_t * format, va_list argList) +{ +#ifdef TEST_PLATFORM_WINDOWS + return _vsnwprintf(buffer, nCount, format, argList); +#else + return vswprintf(buffer, nCount, format, argList); +#endif +} + +size_t TestStrPrintf(wchar_t * buffer, size_t nCount, const wchar_t * format, ...) +{ + va_list argList; + size_t length; + + // Start the argument list + va_start(argList, format); + length = TestStrPrintfV(buffer, nCount, format, argList); + va_end(argList); + + return length; +} + +//----------------------------------------------------------------------------- +// Definition of the TLogHelper class + +class TLogHelper +{ + public: + + // + // Constructor and destructor + // + + TLogHelper(const char * szNewMainTitle = NULL, const TCHAR * szNewSubTitle1 = NULL, const TCHAR * szNewSubTitle2 = NULL) + { + // Fill the variables + memset(this, 0, sizeof(TLogHelper)); + UserString = ""; + TotalFiles = 1; + UserCount = 1; + UserTotal = 1; + +#ifdef TEST_PLATFORM_WINDOWS + InitializeCriticalSection(&Locker); + TickCount = GetTickCount(); + + SetConsoleOutputCP(CP_UTF8); // Set the UTF-8 code page to handle national-specific names + SetConsoleCP(CP_UTF8); +#endif + + // Remember the startup time + SetStartTime(); + + // Save all three titles + szMainTitle = szNewMainTitle; + szSubTitle1 = szNewSubTitle1; + szSubTitle2 = szNewSubTitle2; + + // Print the initial information + if(szNewMainTitle != NULL) + { +#ifdef __CASCLIB_SELF__ + char szMainTitleT[0x100] = {0}; + size_t nLength; + + nLength = TestStrPrintf(szMainTitleT, _countof(szMainTitleT), "-- \"%s\" --", szNewMainTitle); + while(nLength < 90) + szMainTitleT[nLength++] = '-'; + if(nLength < sizeof(szMainTitleT)) + szMainTitleT[nLength++] = 0; + + printf_console("%s\n", szMainTitleT); +#endif + +#ifdef __STORMLIB_SELF__ + TCHAR szMainTitleT[0x100] = {0}; + + // Copy the UNICODE main title + StringCopy(szMainTitleT, _countof(szMainTitleT), szMainTitle); + + if(szSubTitle1 != NULL && szSubTitle2 != NULL) + nPrevPrinted = printf_console(_T("\rRunning %s (%s+%s) ..."), szMainTitleT, szSubTitle1, szSubTitle2); + else if(szSubTitle1 != NULL) + nPrevPrinted = printf_console(_T("\rRunning %s (%s) ..."), szMainTitleT, szSubTitle1); + else + nPrevPrinted = printf_console(_T("\rRunning %s ..."), szMainTitleT); +#endif + } + } + + ~TLogHelper() + { + // Print a verdict, if no verdict was printed yet + if(bMessagePrinted == false) + { + PrintVerdict(ERROR_SUCCESS); + } + +#ifdef TEST_PLATFORM_WINDOWS + DeleteCriticalSection(&Locker); +#endif + +#ifdef __CASCLIB_SELF__ + printf_console("\n"); +#endif + } + + // + // Measurement of elapsed time + // + + bool TimeElapsed(DWORD Milliseconds) + { + bool bTimeElapsed = false; + +#ifdef TEST_PLATFORM_WINDOWS + if(GetTickCount() > (TickCount + Milliseconds)) + { + TickCount = GetTickCount(); + if(TestInterlockedIncrement(&TimeTrigger) == 1) + { + bTimeElapsed = true; + } + } + +#endif + return bTimeElapsed; + } + + // + // Printing functions + // + + int printf_console(const char * format, ...) + { + va_list argList; + va_start(argList, format); + int nLength = 0; + +#ifdef TEST_PLATFORM_WINDOWS + char * szBuffer; + int ccBuffer = 0x1000; + + if((szBuffer = new char[ccBuffer]) != NULL) + { + // Prepare the string + TestStrPrintfV(szBuffer, ccBuffer, format, argList); + nLength = (int)strlen(szBuffer); + + // Unlike wprintf, WriteConsole supports UTF-8 much better + WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), szBuffer, nLength, NULL, NULL); + delete[] szBuffer; + } +#else + nLength = vprintf(format, argList); +#endif + + return nLength; + } + +#ifdef TEST_PLATFORM_WINDOWS + int printf_console(const wchar_t * format, ...) + { + va_list argList; + va_start(argList, format); + int nLength = 0; + + wchar_t * szBuffer; + int ccBuffer = 0x1000; + + if((szBuffer = new wchar_t[ccBuffer]) != NULL) + { + // Prepare the string + TestStrPrintfV(szBuffer, ccBuffer, format, argList); + nLength = (int)wcslen(szBuffer); + + // Unlike wprintf, WriteConsole supports UTF-8 much better + WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), szBuffer, nLength, NULL, NULL); + delete[] szBuffer; + } + return nLength; + } +#endif + + template + DWORD PrintWithClreol(const XCHAR * szFormat, va_list argList, bool bPrintLastError, bool bPrintEndOfLine) + { + XCHAR * szBufferPtr; + XCHAR * szBufferEnd; + size_t nLength = 0; + DWORD dwErrCode = Test_GetLastError(); + XCHAR szPercentS[] = {'%', 's', 0}; + XCHAR szMessage[0x200] = {0}; + XCHAR szBuffer[0x200] = {0}; + bool bPrintPrefix = TEST_PRINT_PREFIX; + + // Always start the buffer with '\r' + szBufferEnd = szBuffer + _countof(szBuffer); + szBufferPtr = szBuffer + 1; + szBuffer[0] = '\r'; + + // Print the prefix, if needed + if(szMainTitle != NULL && bPrintPrefix) + { + while(szMainTitle[nLength] != 0) + *szBufferPtr++ = szMainTitle[nLength++]; + + *szBufferPtr++ = ':'; + *szBufferPtr++ = ' '; + } + + // Construct the message + TestStrPrintfV(szMessage, _countof(szMessage), szFormat, argList); + StringCopy(szBufferPtr, (szBufferEnd - szBufferPtr), szMessage); + szBufferPtr = StringEnd(szBufferPtr); + + // Append the last error + if(bPrintLastError) + { + XCHAR szErrMsg[] = {' ', '(', 'e', 'r', 'r', 'o', 'r', ' ', 'c', 'o', 'd', 'e', ':', ' ', '%', 'u', ')', 0, 0}; + nLength = TestStrPrintf(szBufferPtr, (szBufferEnd - szBufferPtr), szErrMsg, dwErrCode); + szBufferPtr += nLength; + } + + // Pad the string with zeros, if needed + while(szBufferPtr < (szBuffer + nPrevPrinted)) + *szBufferPtr++ = ' '; + + // Remember how much did we print + nPrevPrinted = ConsoleLength(szBuffer, szBufferPtr); + + // Always add one extra space *AFTER* calculating length + if(szBufferPtr < szBufferEnd) + *szBufferPtr++ = ' '; + *szBufferPtr = 0; + + // Print the message to the console + printf_console(szPercentS, szBuffer); + nMessageCounter++; + + // If we shall print the newline, do it + if(bPrintEndOfLine) + { + bMessagePrinted = true; + nPrevPrinted = 0; + printf("\n"); + } + + // Finally print the message + return dwErrCode; + } + + template + void PrintProgress(const XCHAR * szFormat, ...) + { + va_list argList; + + // Always reset the time trigger + TimeTrigger = 0; + + // Only print progress when the cooldown is ready + if(ProgressReady()) + { + va_start(argList, szFormat); + PrintWithClreol(szFormat, argList, false, false); + va_end(argList); + } + } + + template + void PrintMessage(const XCHAR * szFormat, ...) + { + va_list argList; + + va_start(argList, szFormat); + PrintWithClreol(szFormat, argList, false, true); + va_end(argList); + } + + void PrintTotalTime() + { + DWORD TotalTime = SetEndTime(); + + if(TotalTime != 0) + PrintMessage("TotalTime: %u.%u second(s)", (TotalTime / 1000), (TotalTime % 1000)); + PrintMessage("Work complete."); + } + + template + int PrintErrorVa(const XCHAR * szFormat, ...) + { + va_list argList; + int nResult; + + va_start(argList, szFormat); + nResult = PrintWithClreol(szFormat, argList, true, true); + va_end(argList); + + return nResult; + } + + template + int PrintError(const XCHAR * szFormat, const XCHAR * szFileName = NULL) + { + return PrintErrorVa(szFormat, szFileName); + } + + // Print final verdict + DWORD PrintVerdict(DWORD dwErrCode = ERROR_SUCCESS) + { + LPCTSTR szSaveSubTitle1 = szSubTitle1; + LPCTSTR szSaveSubTitle2 = szSubTitle2; + TCHAR szSaveMainTitle[0x80]; + + // Set both to NULL so they won't be printed + StringCopy(szSaveMainTitle, _countof(szSaveMainTitle), szMainTitle); + szMainTitle = NULL; + szSubTitle1 = NULL; + szSubTitle2 = NULL; + + // Print the final information + if(szSaveMainTitle[0] != 0) + { + if(DontPrintResult == false) + { + const TCHAR * szVerdict = (dwErrCode == ERROR_SUCCESS) ? _T("succeeded") : _T("failed"); + + if(szSaveSubTitle1 != NULL && szSaveSubTitle2 != NULL) + PrintMessage(_T("%s (%s+%s) %s."), szSaveMainTitle, szSaveSubTitle1, szSaveSubTitle2, szVerdict); + else if(szSaveSubTitle1 != NULL) + PrintMessage(_T("%s (%s) %s."), szSaveMainTitle, szSaveSubTitle1, szVerdict); + else + PrintMessage(_T("%s %s."), szSaveMainTitle, szVerdict); + } + else + { + PrintProgress(" "); + printf_console("\r"); + } + } + + // Return the error code so the caller can pass it fuhrter + return dwErrCode; + } + + // + // Locking functions (Windows only) + // + + void Lock() + { +#ifdef TEST_PLATFORM_WINDOWS + EnterCriticalSection(&Locker); +#endif + } + + void Unlock() + { +#ifdef TEST_PLATFORM_WINDOWS + LeaveCriticalSection(&Locker); +#endif + } + + // + // Time functions + // + + ULONGLONG GetCurrentThreadTime() + { +#ifdef _WIN32 + ULONGLONG TempTime = 0; + + GetSystemTimeAsFileTime((LPFILETIME)(&TempTime)); + return ((TempTime) / 10 / 1000); + + //ULONGLONG KernelTime = 0; + //ULONGLONG UserTime = 0; + //ULONGLONG TempTime = 0; + + //GetThreadTimes(GetCurrentThread(), (LPFILETIME)&TempTime, (LPFILETIME)&TempTime, (LPFILETIME)&KernelTime, (LPFILETIME)&UserTime); + //return ((KernelTime + UserTime) / 10 / 1000); +#else + return time(NULL) * 1000; +#endif + } + + bool ProgressReady() + { + time_t dwTickCount = time(NULL); + bool bResult = false; + + if(dwTickCount > dwPrevTickCount) + { + dwPrevTickCount = dwTickCount; + bResult = true; + } + + return bResult; + } + + ULONGLONG SetStartTime() + { + StartTime = GetCurrentThreadTime(); + return StartTime; + } + + DWORD SetEndTime() + { + EndTime = GetCurrentThreadTime(); + return (DWORD)(EndTime - StartTime); + } + + void IncrementTotalBytes(ULONGLONG IncrementValue) + { + // For some weird reason, this is measurably faster then InterlockedAdd64 + Lock(); + TotalBytes = TotalBytes + IncrementValue; + Unlock(); + } + + void FormatTotalBytes(char * szBuffer, size_t ccBuffer) + { + ULONGLONG Bytes = TotalBytes; + ULONGLONG Divider = 1000000000; + char * szBufferEnd = szBuffer + ccBuffer; + bool bDividingOn = false; + + while((szBuffer + 4) < szBufferEnd && Divider > 0) + { + // Are we already dividing? + if(bDividingOn) + { + szBuffer += TestStrPrintf(szBuffer, ccBuffer, " %03u", (DWORD)(Bytes / Divider)); + Bytes = Bytes % Divider; + } + else if(Bytes > Divider) + { + szBuffer += TestStrPrintf(szBuffer, ccBuffer, "%u", (DWORD)(Bytes / Divider)); + Bytes = Bytes % Divider; + bDividingOn = true; + } + Divider /= 1000; + } + } + +#ifdef TEST_PLATFORM_WINDOWS + CRITICAL_SECTION Locker; +#endif + + ULONGLONG TotalBytes; // For user's convenience: Total number of bytes + ULONGLONG ByteCount; // For user's convenience: Current number of bytes + ULONGLONG StartTime; // Start time of an operation, in milliseconds + ULONGLONG EndTime; // End time of an operation, in milliseconds + const char * UserString; + DWORD UserCount; + DWORD UserTotal; + DWORD TickCount; + DWORD TimeTrigger; // For triggering elapsed timers + DWORD TotalFiles; // For user's convenience: Total number of files + DWORD FileCount; // For user's convenience: Curernt number of files + DWORD DontPrintResult:1; // If true, supress printing result from the destructor + + protected: + + const char * szMainTitle; // Title of the text (usually name) + const TCHAR * szSubTitle1; // Title of the text (can be name of the tested file) + const TCHAR * szSubTitle2; // Title of the text (can be name of the tested file) + size_t nMessageCounter; + size_t nPrevPrinted; + time_t dwPrevTickCount; + bool bMessagePrinted; +}; diff --git a/dep/StormLib/test/Test.cpp b/dep/StormLib/test/Test.cpp deleted file mode 100644 index e31c15fee2..0000000000 --- a/dep/StormLib/test/Test.cpp +++ /dev/null @@ -1,1892 +0,0 @@ -/*****************************************************************************/ -/* StormLibTest.cpp Copyright (c) Ladislav Zezula 2003 */ -/*---------------------------------------------------------------------------*/ -/* Test module for StormLib */ -/*---------------------------------------------------------------------------*/ -/* Date Ver Who Comment */ -/* -------- ---- --- ------- */ -/* 25.03.03 1.00 Lad The first version of StormLibTest.cpp */ -/*****************************************************************************/ - -#define _CRT_SECURE_NO_DEPRECATE -#define __INCLUDE_CRYPTOGRAPHY__ -#define __STORMLIB_SELF__ // Don't use StormLib.lib -#include - -#ifdef _MSC_VER -#include -#endif - -#include "../src/StormLib.h" -#include "../src/StormCommon.h" - -#ifdef _MSC_VER -#pragma warning(disable: 4505) // 'XXX' : unreferenced local function has been removed -#endif - -//------------------------------------------------------------------------------ -// Defines - -#ifdef PLATFORM_WINDOWS -#define WORK_PATH_ROOT "E:\\Multimedia\\MPQs\\" -#endif - -#ifdef PLATFORM_LINUX -#define WORK_PATH_ROOT "/home/user/MPQs/" -#endif - -#ifdef PLATFORM_MAC -#define WORK_PATH_ROOT "/Users/sam/StormLib/test" -#endif - -#ifndef LANG_CZECH -#define LANG_CZECH 0x0405 -#endif - -#define MPQ_SECTOR_SIZE 0x1000 - -#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 TCHAR * szWorkDir = MAKE_PATH("Work"); - -static unsigned int AddFlags[] = -{ -// Compression Encryption Fixed key Single Unit Sector CRC - 0 | 0 | 0 | 0 | 0, - 0 | MPQ_FILE_ENCRYPTED | 0 | 0 | 0, - 0 | MPQ_FILE_ENCRYPTED | MPQ_FILE_FIX_KEY | 0 | 0, - 0 | 0 | 0 | MPQ_FILE_SINGLE_UNIT | 0, - 0 | MPQ_FILE_ENCRYPTED | 0 | MPQ_FILE_SINGLE_UNIT | 0, - 0 | MPQ_FILE_ENCRYPTED | MPQ_FILE_FIX_KEY | MPQ_FILE_SINGLE_UNIT | 0, - MPQ_FILE_IMPLODE | 0 | 0 | 0 | 0, - MPQ_FILE_IMPLODE | MPQ_FILE_ENCRYPTED | 0 | 0 | 0, - MPQ_FILE_IMPLODE | MPQ_FILE_ENCRYPTED | MPQ_FILE_FIX_KEY | 0 | 0, - MPQ_FILE_IMPLODE | 0 | 0 | MPQ_FILE_SINGLE_UNIT | 0, - MPQ_FILE_IMPLODE | MPQ_FILE_ENCRYPTED | 0 | MPQ_FILE_SINGLE_UNIT | 0, - MPQ_FILE_IMPLODE | MPQ_FILE_ENCRYPTED | MPQ_FILE_FIX_KEY | MPQ_FILE_SINGLE_UNIT | 0, - MPQ_FILE_IMPLODE | 0 | 0 | 0 | MPQ_FILE_SECTOR_CRC, - MPQ_FILE_IMPLODE | MPQ_FILE_ENCRYPTED | 0 | 0 | MPQ_FILE_SECTOR_CRC, - MPQ_FILE_IMPLODE | MPQ_FILE_ENCRYPTED | MPQ_FILE_FIX_KEY | 0 | MPQ_FILE_SECTOR_CRC, - MPQ_FILE_COMPRESS | 0 | 0 | 0 | 0, - MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED | 0 | 0 | 0, - MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED | MPQ_FILE_FIX_KEY | 0 | 0, - MPQ_FILE_COMPRESS | 0 | 0 | MPQ_FILE_SINGLE_UNIT | 0, - MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED | 0 | MPQ_FILE_SINGLE_UNIT | 0, - MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED | MPQ_FILE_FIX_KEY | MPQ_FILE_SINGLE_UNIT | 0, - MPQ_FILE_COMPRESS | 0 | 0 | 0 | MPQ_FILE_SECTOR_CRC, - MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED | 0 | 0 | MPQ_FILE_SECTOR_CRC, - MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED | MPQ_FILE_FIX_KEY | 0 | MPQ_FILE_SECTOR_CRC, - 0xFFFFFFFF -}; - -//----------------------------------------------------------------------------- -// Local testing functions - -static void clreol() -{ -#ifdef PLATFORM_WINDOWS - CONSOLE_SCREEN_BUFFER_INFO ScreenInfo; - HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); - LPTSTR szConsoleLine; - int nConsoleChars; - int i = 0; - - GetConsoleScreenBufferInfo(hConsole, &ScreenInfo); - nConsoleChars = (ScreenInfo.srWindow.Right - ScreenInfo.srWindow.Left); - if(nConsoleChars > 0) - { - szConsoleLine = new TCHAR[nConsoleChars + 3]; - if(szConsoleLine != NULL) - { - szConsoleLine[i++] = '\r'; - for(; i < nConsoleChars; i++) - szConsoleLine[i] = ' '; - szConsoleLine[i++] = '\r'; - szConsoleLine[i] = 0; - - _tprintf(szConsoleLine); - delete [] szConsoleLine; - } - } -#endif // PLATFORM_WINDOWS -} - -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) -{ - // Copy directory name - while(*szPart1 != 0) - *szBuffer++ = *szPart1++; - - // Add separator - *szBuffer++ = _T('/'); - - // Copy file name - while(*szPart2 != 0) - *szBuffer++ = *szPart2++; - - // Terminate the string - *szBuffer = 0; -} - -int GetFirstDiffer(void * ptr1, void * ptr2, int nSize) -{ - char * buff1 = (char *)ptr1; - char * buff2 = (char *)ptr2; - int nDiffer; - - for(nDiffer = 0; nDiffer < nSize; nDiffer++) - { - if(*buff1++ != *buff2++) - return nDiffer; - } - return -1; -} - -static void WINAPI CompactCB(void * /* lpParam */, DWORD dwWork, ULONGLONG BytesDone, ULONGLONG TotalBytes) -{ - clreol(); - - _tprintf(_T("%u of %u "), (DWORD)BytesDone, (DWORD)TotalBytes); - switch(dwWork) - { - case CCB_CHECKING_FILES: - _tprintf(_T("Checking files in archive ...\r")); - break; - - case CCB_CHECKING_HASH_TABLE: - _tprintf(_T("Checking hash table ...\r")); - break; - - case CCB_COPYING_NON_MPQ_DATA: - _tprintf(_T("Copying non-MPQ data ...\r")); - break; - - case CCB_COMPACTING_FILES: - _tprintf(_T("Compacting archive ...\r")); - break; - - case CCB_CLOSING_ARCHIVE: - _tprintf(_T("Closing archive ...\r")); - break; - } -} - -static void GenerateRandomDataBlock(LPBYTE pbBuffer, DWORD cbBuffer) -{ - LPBYTE pbBufferEnd = pbBuffer + cbBuffer; - LPBYTE pbPtr = pbBuffer; - DWORD cbBytesToPut = 0; - BYTE ByteToPut = 0; - bool bRandomData = false; - - while(pbPtr < pbBufferEnd) - { - // If there are no bytes to put, we will generate new byte and length - if(cbBytesToPut == 0) - { - bRandomData = false; - switch(rand() % 10) - { - case 0: // A short sequence of zeros - cbBytesToPut = rand() % 0x08; - ByteToPut = 0; - break; - - case 1: // A long sequence of zeros - cbBytesToPut = rand() % 0x80; - ByteToPut = 0; - break; - - case 2: // A short sequence of non-zeros - cbBytesToPut = rand() % 0x08; - ByteToPut = (BYTE)(rand() % 0x100); - break; - - case 3: // A long sequence of non-zeros - cbBytesToPut = rand() % 0x80; - ByteToPut = (BYTE)(rand() % 0x100); - break; - - case 4: // A short random data - cbBytesToPut = rand() % 0x08; - bRandomData = true; - break; - - case 5: // A long random data - cbBytesToPut = rand() % 0x80; - bRandomData = true; - break; - - default: // A single random byte - cbBytesToPut = 1; - ByteToPut = (BYTE)(rand() % 0x100); - break; - } - } - - // Generate random byte, if needed - if(bRandomData) - ByteToPut = (BYTE)(rand() % 0x100); - - // Put next byte to the output buffer - *pbPtr++ = ByteToPut; - cbBytesToPut--; - } -} - -static bool CompareArchivedFiles(const char * szFileName, HANDLE hFile1, HANDLE hFile2, DWORD dwBlockSize) -{ - LPBYTE pbBuffer1 = NULL; - LPBYTE pbBuffer2 = NULL; - DWORD dwRead1; // Number of bytes read (Storm.dll) - DWORD dwRead2; // Number of bytes read (StormLib) - bool bResult1 = false; // Result from Storm.dll - bool bResult2 = false; // Result from StormLib - bool bResult = true; - int nDiff; - - szFileName = szFileName; - - // Allocate buffers - pbBuffer1 = new BYTE[dwBlockSize]; - pbBuffer2 = new BYTE[dwBlockSize]; - - for(;;) - { - // Read the file's content by both methods and compare the result - memset(pbBuffer1, 0, dwBlockSize); - memset(pbBuffer2, 0, dwBlockSize); - bResult1 = SFileReadFile(hFile1, pbBuffer1, dwBlockSize, &dwRead1, NULL); - bResult2 = SFileReadFile(hFile2, pbBuffer2, dwBlockSize, &dwRead2, NULL); - if(bResult1 != bResult2) - { - _tprintf(_T("Different results from SFileReadFile, Mpq1 %u, Mpq2 %u\n"), bResult1, bResult2); - bResult = false; - break; - } - - // Test the number of bytes read - if(dwRead1 != dwRead2) - { - _tprintf(_T("Different bytes read from SFileReadFile, Mpq1 %u, Mpq2 %u\n"), dwRead1, dwRead2); - bResult = false; - break; - } - - // No more bytes ==> OK - if(dwRead1 == 0) - break; - - // Test the content - if((nDiff = GetFirstDiffer(pbBuffer1, pbBuffer2, dwRead1)) != -1) - { - bResult = false; - break; - } - } - - delete [] pbBuffer2; - delete [] pbBuffer1; - return bResult; -} - -// Random read version -static bool CompareArchivedFilesRR(const char * /* szFileName */, HANDLE hFile1, HANDLE hFile2, DWORD dwBlockSize) -{ - const char * szPositions[3] = {"FILE_BEGIN ", "FILE_CURRENT", "FILE_END "}; - LPBYTE pbBuffer1 = NULL; - LPBYTE pbBuffer2 = NULL; - DWORD dwFileSize1; // File size (Storm.dll) - DWORD dwFileSize2; // File size (StormLib) - DWORD dwRead1; // Number of bytes read (Storm.dll) - DWORD dwRead2; // Number of bytes read (StormLib) - bool bResult1 = false; // Result from Storm.dll - bool bResult2 = false; // Result from StormLib - int nError = ERROR_SUCCESS; - - // Test the file size - dwFileSize1 = SFileGetFileSize(hFile1, NULL); - dwFileSize2 = SFileGetFileSize(hFile2, NULL); - if(dwFileSize1 != dwFileSize2) - { - _tprintf(_T("Different size from SFileGetFileSize (file1: %u, file2: %u)\n"), dwFileSize1, dwFileSize2); - return false; - } - - if(dwFileSize1 != 0) - { - for(int i = 0; i < 10000; i++) - { - DWORD dwRandom = rand() * rand(); - DWORD dwMoveMethod = dwRandom % 3; - DWORD dwPosition = dwRandom % dwFileSize1; - DWORD dwToRead = dwRandom % dwBlockSize; - - // Also test negative seek - if(rand() & 1) - { - int nPosition = (int)dwPosition; - dwPosition = (DWORD)(-nPosition); - } - - // Allocate buffers - pbBuffer1 = new BYTE[dwToRead]; - pbBuffer2 = new BYTE[dwToRead]; - - // Set the file pointer - _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) - { - _tprintf(_T("Difference returned by SFileSetFilePointer (file1: %u, file2: %u)\n"), dwRead1, dwRead2); - nError = ERROR_CAN_NOT_COMPLETE; - break; - } - - // Read the file's content by both methods and compare the result - bResult1 = SFileReadFile(hFile1, pbBuffer1, dwToRead, &dwRead1, NULL); - bResult2 = SFileReadFile(hFile2, pbBuffer2, dwToRead, &dwRead2, NULL); - if(bResult1 != bResult2) - { - _tprintf(_T("Different results from SFileReadFile (file1: %u, file2: %u)\n\n"), bResult1, bResult2); - nError = ERROR_CAN_NOT_COMPLETE; - break; - } - - // Test the number of bytes read - if(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)) - { - _tprintf(_T("Different data content from SFileReadFile\n")); - nError = ERROR_CAN_NOT_COMPLETE; - break; - } - - delete [] pbBuffer2; - delete [] pbBuffer1; - } - } - clreol(); - 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 TCHAR * szFileName) -{ - ULONGLONG ByteOffset; - ULONGLONG FileSize = 0; - TFileStream * pStream; - BYTE BigBuffer[0x7000]; - BYTE Buffer[0x100]; - int nError = ERROR_SUCCESS; - - // Open the partial file - pStream = FileStream_OpenFile(szFileName, false); - if(pStream == NULL) - nError = GetLastError(); - - // Get the size of the stream - if(nError == ERROR_SUCCESS) - { - if(!FileStream_GetSize(pStream, FileSize)) - nError = GetLastError(); - } - - // Read the last 0x7000 bytes - if(nError == ERROR_SUCCESS) - { - ByteOffset = FileSize - sizeof(BigBuffer); - if(!FileStream_Read(pStream, &ByteOffset, BigBuffer, sizeof(BigBuffer))) - nError = GetLastError(); - } - - // Read the last 0x100 bytes - if(nError == ERROR_SUCCESS) - { - ByteOffset = FileSize - sizeof(Buffer); - if(!FileStream_Read(pStream, &ByteOffset, Buffer, sizeof(Buffer))) - nError = GetLastError(); - } - - // Read 0x100 bytes from position (FileSize - 0xFF) - if(nError == ERROR_SUCCESS) - { - ByteOffset = FileSize - sizeof(Buffer) + 1; - if(!FileStream_Read(pStream, &ByteOffset, Buffer, sizeof(Buffer))) - nError = GetLastError(); - } - - FileStream_Close(pStream); - 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 - -#ifdef PLATFORM_WINDOWS -typedef void * (*ALLOC_MEMORY)(size_t); -typedef void (*FREE_MEMORY)(void *); -typedef int (GIVE_DATA)(void *); - -extern "C" int starcraft_decompress_lzma(char * pbInBuffer, int cbInBuffer, char * pbOutBuffer, int cbOutBuffer, int * pcbOutBuffer, ALLOC_MEMORY pfnAllocMemory, FREE_MEMORY pfnFreeMemory); -extern "C" int starcraft_compress_lzma(char * pbInBuffer, int cbInBuffer, int dummy1, char * pbOutBuffer, int cbOutBuffer, int dummy2, int * pcbOutBuffer, ALLOC_MEMORY pfnAllocMemory, FREE_MEMORY pfnFreeMemory, GIVE_DATA pfnGiveData); -void Compress_LZMA(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer, int *, int); -int Decompress_LZMA(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer); - -extern "C" void * operator_new(size_t sz) -{ - return malloc(sz); -} - -void * Memory_Allocate(size_t byte_size) -{ - return malloc(byte_size); -} - -void Memory_Free(void * address) -{ - if(address != NULL) - free(address); -} - -int GiveData(void *) -{ - return 0; -} - -static int StarcraftCompress_LZMA(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer) -{ - return starcraft_compress_lzma(pbInBuffer, - cbInBuffer, - 0, - pbOutBuffer, - *pcbOutBuffer, - 0, - pcbOutBuffer, - Memory_Allocate, - Memory_Free, - GiveData); -} - -static int StarcraftDecompress_LZMA(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer) -{ - return starcraft_decompress_lzma(pbInBuffer, - cbInBuffer, - pbOutBuffer, - *pcbOutBuffer, - pcbOutBuffer, - Memory_Allocate, - Memory_Free); -} - -static int CompareLzmaCompressions(int nSectorSize) -{ - LPBYTE pbCompressed1 = NULL; // Compressed by our code - LPBYTE pbCompressed2 = NULL; // Compressed by Blizzard's code - LPBYTE pbDecompressed1 = NULL; // Decompressed by our code - LPBYTE pbDecompressed2 = NULL; // Decompressed by Blizzard's code - LPBYTE pbOriginalData = NULL; - int nError = ERROR_SUCCESS; - - // Allocate buffers - // Must allocate twice blocks due to probable bug in Storm.dll. - // Storm.dll corrupts stack when uncompresses data with PKWARE DCL - // and no compression occurs. - pbDecompressed1 = new BYTE [nSectorSize]; - pbDecompressed2 = new BYTE [nSectorSize]; - pbCompressed1 = new BYTE [nSectorSize]; - pbCompressed2 = new BYTE [nSectorSize]; - pbOriginalData = new BYTE[nSectorSize]; - if(!pbDecompressed1 || !pbDecompressed2 || !pbCompressed1 || !pbCompressed2 || !pbOriginalData) - nError = ERROR_NOT_ENOUGH_MEMORY; - - if(nError == ERROR_SUCCESS) - { - for(int i = 0; i < 100000; i++) - { - int nDcmpLength1; - int nDcmpLength2; - int nCmpLength1; - int nCmpLength2; - int nDiff; - - clreol(); - _tprintf(_T("Testing compression of sector %u\r"), i + 1); - - // Generate random data sector - GenerateRandomDataBlock(pbOriginalData, nSectorSize); - - // Compress the sector by both methods - nCmpLength1 = nCmpLength2 = nSectorSize; -// Compress_LZMA((char *)pbCompressed1, &nCmpLength1, (char *)pbOriginalData, nSectorSize, 0, 0); - StarcraftCompress_LZMA((char *)pbCompressed1, &nCmpLength2, (char *)pbOriginalData, nSectorSize); - -__TryToDecompress: - - // Only test decompression when the compression actually succeeded - if(nCmpLength1 < nSectorSize) - { - // Decompress both data - nDcmpLength2 = nDcmpLength1 = nSectorSize; -// Decompress_LZMA((char *)pbDecompressed1, &nDcmpLength1, (char *)pbCompressed1, nCmpLength1); - StarcraftDecompress_LZMA((char *)pbDecompressed2, &nDcmpLength2, (char *)pbCompressed1, nCmpLength1); - - // Compare the length of the output data - if(nDcmpLength1 != nDcmpLength2) - { - _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) - { - _tprintf(_T("Difference in decompressed blocks (offset 0x%08X)\n"), nDiff); - goto __TryToDecompress; - } - - // Check for data overflow - if(pbDecompressed1[nSectorSize] != 0xFD || pbDecompressed1[nSectorSize] != 0xFD) - { - _tprintf(_T("Damage after decompressed sector !!!\n")); - goto __TryToDecompress; - } - - // Compare the decompressed data against original data - if((nDiff = GetFirstDiffer(pbDecompressed1, pbOriginalData, nDcmpLength1)) != -1) - { - _tprintf(_T("Difference between original data and decompressed data (offset 0x%08X)\n"), nDiff); - goto __TryToDecompress; - } - } - } - } - - // Cleanup - if(pbOriginalData != NULL) - delete [] pbOriginalData; - if(pbCompressed2 != NULL) - delete [] pbCompressed2; - if(pbCompressed1 != NULL) - delete [] pbCompressed1; - if(pbDecompressed2 != NULL) - delete [] pbDecompressed2; - if(pbDecompressed1 != NULL) - delete [] pbDecompressed1; - clreol(); - return nError; -} -#endif // PLATFORM_WINDOWS - -//----------------------------------------------------------------------------- -// Compression method test - -static int TestSectorCompress(int nSectorSize) -{ - LPBYTE pbDecompressed = NULL; - LPBYTE pbCompressed = NULL; - LPBYTE pbOriginal = NULL; - int nError = ERROR_SUCCESS; - - // Allocate buffers - pbDecompressed = new BYTE[nSectorSize]; - pbCompressed = new BYTE[nSectorSize]; - pbOriginal = new BYTE[nSectorSize]; - if(!pbDecompressed || !pbCompressed || !pbOriginal) - nError = ERROR_NOT_ENOUGH_MEMORY; - - if(nError == ERROR_SUCCESS) - { - for(int i = 0; i < 100000; i++) - { - int nOriginalLength = nSectorSize % (rand() + 1); - int nCompressedLength; - int nDecompressedLength; - int nCmp = MPQ_COMPRESSION_SPARSE | MPQ_COMPRESSION_ZLIB | MPQ_COMPRESSION_BZIP2 | MPQ_COMPRESSION_PKWARE; - int nDiff; - - clreol(); - _tprintf(_T("Testing compression of sector %u\r"), i + 1); - - // Generate random data sector - GenerateRandomDataBlock(pbOriginal, nOriginalLength); - if(nOriginalLength == 0x123) - nOriginalLength = 0; - -__TryAgain: - - // Compress the sector - nCompressedLength = nOriginalLength; - SCompCompress((char *)pbCompressed, &nCompressedLength, (char *)pbOriginal, nOriginalLength, nCmp, 0, -1); -// SCompImplode((char *)pbCompressed, &nCompressedLength, (char *)pbOriginal, nOriginalLength); - - // When the method was unable to compress data, - // the compressed data must be identical to original data - if(nCompressedLength == nOriginalLength) - { - if((nDiff = GetFirstDiffer(pbCompressed, pbOriginal, nOriginalLength)) != -1) - { - _tprintf(_T("Compression error: Fail when unable to compress the data (Offset 0x%08X).\n"), nDiff); - goto __TryAgain; - } - } - - // Uncompress the sector - nDecompressedLength = nOriginalLength; - SCompDecompress((char *)pbDecompressed, &nDecompressedLength, (char *)pbCompressed, nCompressedLength); -// SCompExplode((char *)pbDecompressed, &nDecompressedLength, (char *)pbCompressed, nCompressedLength); - - // Check the decompressed length against original length - if(nDecompressedLength != nOriginalLength) - { - _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) - { - _tprintf(_T("Decompressed sector does not agree with the original data !!! (Offset 0x%08X)\n"), nDiff); - goto __TryAgain; - } - } - } - - // Cleanup - delete [] pbOriginal; - delete [] pbCompressed; - delete [] pbDecompressed; - clreol(); - return nError; -} - -static int TestArchiveOpenAndClose(const TCHAR * szMpqName) -{ - const char * szFileName1 = "DBFilesClient\\Item.dbc"; -// const char * szFileName2 = "items\\map\\mapz_deleted.cel"; - TMPQArchive * ha = NULL; - HANDLE hFile1 = NULL; -// HANDLE hFile2 = NULL; - HANDLE hMpq = NULL; - int nError = ERROR_SUCCESS; - - if(nError == ERROR_SUCCESS) - { - _tprintf(_T("Opening archive %s ...\n"), szMpqName); - if(!SFileOpenArchive(szMpqName, 0, STREAM_PROVIDER_ENCRYPTED | BASE_PROVIDER_FILE, &hMpq)) - nError = GetLastError(); - ha = (TMPQArchive *)hMpq; - } -/* - // Test for TBitArray - if(nError == ERROR_SUCCESS && ha->pHetTable != NULL) - { - TBitArray * pBitArray = ha->pHetTable->pBetIndexes; - - for(ULONG i = 0; i < 0x10000; i++) - { - BYTE LoadedBits[0x20]; - BYTE SaveBits[0x40]; - unsigned int nBitPosition = (i >> 0x08); - unsigned int nBitCount = (i & 0xFF); - - memset(LoadedBits, 0, sizeof(LoadedBits)); - memcpy(SaveBits, pBitArray->Elements, sizeof(SaveBits)); - - // Load the index to the BET table - pBitArray->GetBits(nBitPosition, nBitCount, LoadedBits, sizeof(LoadedBits)); - - // Load the index to the BET table - pBitArray->SetBits(nBitPosition, nBitCount, LoadedBits, sizeof(LoadedBits)); - - // Verify the bits - if(memcmp(SaveBits, pBitArray->Elements, sizeof(SaveBits))) - assert(false); - } - } -*/ - // Verify the raw data in the archive - if(nError == ERROR_SUCCESS) - { - // Verify the archive - SFileVerifyRawData(hMpq, SFILE_VERIFY_FILE, szFileName1); - - // Try to open a file - if(!SFileOpenFileEx(hMpq, szFileName1, SFILE_OPEN_FROM_MPQ, &hFile1)) - { - nError = GetLastError(); - printf("%s - file not found in the MPQ\n", szFileName1); - } - } - - // Dummy read from the file - if(nError == ERROR_SUCCESS) - { - DWORD dwBytesRead = 0; - BYTE Buffer[0x1000]; - - SFileSetFileLocale(hFile1, 0x405); - SFileReadFile(hFile1, Buffer, sizeof(Buffer), &dwBytesRead); - } -/* - // Verify the MPQ listfile - if(nError == ERROR_SUCCESS) - { - SFileVerifyFile(hMpq, szFileName1, 0xFFFFFFFF); - if(!CompareArchivedFilesRR(szFileName1, hFile1, hFile2, 0x100000)) - nError = ERROR_CAN_NOT_COMPLETE; - } -*/ - if(hFile1 != NULL) - SFileCloseFile(hFile1); - if(hMpq != NULL) - SFileCloseArchive(hMpq); - return nError; -} - -static int TestFindFiles(const TCHAR * szMpqName) -{ - TMPQFile * hf; - HANDLE hFile; - HANDLE hMpq = NULL; - BYTE Buffer[100]; - int nError = ERROR_SUCCESS; - int nFiles = 0; - int nFound = 0; - - // Open the archive - if(nError == ERROR_SUCCESS) - { - _tprintf(_T("Opening \"%s\" for finding files ...\n"), szMpqName); - if(!SFileOpenArchive(szMpqName, 0, 0, &hMpq)) - nError = GetLastError(); - } - - // Compact the archive - if(nError == ERROR_SUCCESS) - { - SFILE_FIND_DATA sf; - HANDLE hFind; - DWORD dwExtraDataSize; - bool bFound = true; - - hFind = SFileFindFirstFile(hMpq, "*", &sf, "c:\\Tools32\\ListFiles\\ListFile.txt"); - while(hFind != NULL && bFound != false) - { - if(SFileOpenFileEx(hMpq, sf.cFileName, 0, &hFile)) - { - hf = (TMPQFile *)hFile; - SFileReadFile(hFile, Buffer, sizeof(Buffer)); - nFiles++; - - if(sf.dwFileFlags & MPQ_FILE_SECTOR_CRC) - { - dwExtraDataSize = hf->SectorOffsets[hf->dwSectorCount + 1] - hf->SectorOffsets[hf->dwSectorCount]; - if(dwExtraDataSize != 0) - nFound++; - } - - SFileCloseFile(hFile); - } - - bFound = SFileFindNextFile(hFind, &sf); - } - } - - if(hMpq != NULL) - SFileCloseArchive(hMpq); - if(nError == ERROR_SUCCESS) - _tprintf(_T("Search complete\n")); - return nError; -} - -static int TestMpqCompacting(const TCHAR * szMpqName) -{ - HANDLE hMpq = NULL; - int nError = ERROR_SUCCESS; - - // Open the archive - if(nError == ERROR_SUCCESS) - { - _tprintf(_T("Opening \"%s\" for compacting ...\n"), szMpqName); - if(!SFileOpenArchive(szMpqName, 0, 0, &hMpq)) - nError = GetLastError(); - } - - if(nError == ERROR_SUCCESS) - { - const char * szFileName = "Shaders\\Effects\\shadowmap.wfx"; - - printf("Deleting file %s ...\r", szFileName); - if(!SFileRemoveFile(hMpq, szFileName)) - nError = GetLastError(); - } -/* - // Compact the archive - if(nError == ERROR_SUCCESS) - { - _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) - _tprintf(_T("Compacting complete (No errors)\n")); - return nError; -} - -static int TestCreateArchive(const TCHAR * szMpqName) -{ - TFileStream * pStream; - 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 - _tprintf(_T("Creating %s ...\n"), szMpqName); - pStream = FileStream_CreateFile(szMpqName, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); - if(pStream == NULL) - nError = GetLastError(); - - // Write some data - if(nError == ERROR_SUCCESS) - { - ULONGLONG FileSize = 0x100000; - - FileStream_SetSize(pStream, FileSize); - FileStream_Close(pStream); - } - - // Well, now create the MPQ archive - if(nError == ERROR_SUCCESS) - { - if(!SFileCreateArchive(szMpqName, - MPQ_CREATE_ARCHIVE_V4 | MPQ_CREATE_ATTRIBUTES, - 17, - &hMpq)) - { - nError = GetLastError(); - } - } - - // Add the same file multiple times - if(nError == ERROR_SUCCESS) - { - // Add FileTest.exe - for(i = 0; AddFlags[i] != 0xFFFFFFFF; i++) - { - sprintf(szMpqFileName, "FileTest_%02u.exe", i); - PrintfTA(_T("Adding %s as %s ...\n"), szFileName1, szMpqFileName); - if(SFileAddFileEx(hMpq, szFileName1, szMpqFileName, AddFlags[i], MPQ_COMPRESSION_ZLIB)) - { - 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 - { - printf("Failed to add the file \"%s\".\n", szMpqFileName); - } - } - - - // Delete a file in the middle of the file table - SFileRemoveFile(hMpq, "FileTest_10.exe"); - SFileAddFileEx(hMpq, szFileName1, "FileTest_xx.exe", MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED, MPQ_COMPRESSION_ZLIB); - - // Try to decrement max file count - dwFileCount = SFileGetMaxFileCount(hMpq); - SFileSetMaxFileCount(hMpq, dwFileCount - 1); - - // Add ZeroSize.txt (1) - sprintf(szMpqFileName, "ZeroSize_1.txt"); - for(i = 0; LocaleIDs[i] != 0xFFFF; 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)) - printf("Cannot add the file\n"); - } - - // Add ZeroSize.txt (1) - sprintf(szMpqFileName, "ZeroSize_2.txt"); - for(int i = 0; LocaleIDs[i] != 0xFFFF; 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)) - printf("Cannot add the file\n"); - } - } - - // Test rename function - if(nError == ERROR_SUCCESS) - { - _tprintf(_T("Testing rename files ...\n")); - SFileSetLocale(LANG_NEUTRAL); - if(!SFileRenameFile(hMpq, "FileTest_08.exe", "FileTest_08a.exe")) - { - nError = GetLastError(); - _tprintf(_T("Failed to rename the file\n")); - } - - if(!SFileRenameFile(hMpq, "FileTest_08a.exe", "FileTest_08.exe")) - { - nError = GetLastError(); - _tprintf(_T("Failed to rename the file\n")); - } - - if(!SFileRenameFile(hMpq, "FileTest_10.exe", "FileTest_10a.exe")) - { - nError = GetLastError(); - _tprintf(_T("Failed to rename the file\n")); - } - - if(!SFileRenameFile(hMpq, "FileTest_10a.exe", "FileTest_10.exe")) - { - nError = GetLastError(); - _tprintf(_T("Failed to rename the file\n")); - } - - if(nError == ERROR_SUCCESS) - _tprintf(_T("Rename test succeeded.\n\n")); - else - _tprintf(_T("Rename test failed.\n\n")); - } - - // Compact the archive -// if(nError == ERROR_SUCCESS) -// SFileCompactArchive(hMpq); - - // Test changing hash table size - if(nError == ERROR_SUCCESS) - SFileSetMaxFileCount(hMpq, 0x95); - - if(hMpq != NULL) - SFileCloseArchive(hMpq); - - // Try to reopen the archive - if(SFileOpenArchive(szMpqName, 0, 0, &hMpq)) - SFileCloseArchive(hMpq); - - _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); - 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)) - { - printf("Failed to add the file \"%s\".\n", szMpqFileName); - break; - } - - SFileFlushArchive(hMpq); - SFileCompactArchive(hMpq); - SFileCloseArchive(hMpq); - } - } - } - - _tprintf(_T("\n")); - return nError; -} - - -static int TestAddFilesToMpq( - const TCHAR * szMpqName, - ... - ) -{ - 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 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, - szMpqFileName, - MPQ_FILE_COMPRESS, - MPQ_COMPRESSION_ZLIB)) - { - nError = GetLastError(); - printf("Failed to add the file \"%s\"\n", szFileName); - } - } - - SFileCloseArchive(hMpq); - return nError; -} - -static int TestCreateArchiveFromMemory(const TCHAR * szMpqName) -{ -#define FILE_SIZE 65535 - - HANDLE hFile; - HANDLE hMPQ; - 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)) - { - for(i = 0; i < 1000; i++) - { - sprintf(szFileName, "File%03u.bin", i); - printf("Adding file %s\r", szFileName); - - if(SFileCreateFile(hMPQ, szFileName, 0, FILE_SIZE, 0, MPQ_FILE_COMPRESS, &hFile)) - { - SFileWriteFile(hFile, data, FILE_SIZE, MPQ_COMPRESSION_ZLIB); - SFileFinishFile(hFile); - } - } - } - SFileCloseArchive(hMPQ); - delete [] data; - return ERROR_SUCCESS; -} - -static int TestFileReadAndWrite( - const TCHAR * szMpqName, - const char * szFileName) -{ - LPBYTE pvFile = NULL; - HANDLE hFile = NULL; - HANDLE hMpq = NULL; - DWORD dwBytesRead; - DWORD dwFileSize = 0; - int nError = ERROR_SUCCESS; - - if(!SFileOpenArchive(szMpqName, 0, 0, &hMpq)) - { - nError = GetLastError(); - _tprintf(_T("Failed to open the archive %s (%u).\n"), szMpqName, nError); - } - - if(nError == ERROR_SUCCESS) - { - if(!SFileOpenFileEx(hMpq, szFileName, 0, &hFile)) - { - nError = GetLastError(); - printf("Failed to open the file %s (%u).\n", szFileName, nError); - } - } - - if(nError == ERROR_SUCCESS) - { - if(!SFileGetFileInfo(hFile, SFILE_INFO_FILE_SIZE, &dwFileSize, sizeof(DWORD))) - { - nError = GetLastError(); - _tprintf(_T("Failed to get the file size (%u).\n"), nError); - } - } - - if(nError == ERROR_SUCCESS) - { - pvFile = new BYTE[dwFileSize]; - if(pvFile == NULL) - { - nError = ERROR_NOT_ENOUGH_MEMORY; - printf("Failed to allocate buffer for the file (%u).\n", nError); - } - } - - if(nError == ERROR_SUCCESS) - { - if(!SFileReadFile(hFile, pvFile, dwFileSize, &dwBytesRead)) - { - nError = GetLastError(); - printf("Failed to read file (%u).\n", nError); - } - } - - if(hFile != NULL) - { - SFileCloseFile(hFile); - hFile = NULL; - } - - if(nError == ERROR_SUCCESS) - { - 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); - } - } - - if(nError == ERROR_SUCCESS) - { - if(!SFileWriteFile(hFile, pvFile, dwFileSize, 0)) - { - nError = GetLastError(); - printf("Failed to write the data to the MPQ (%u).\n", nError); - } - } - - if(hFile != NULL) - { - if(!SFileFinishFile(hFile)) - { - nError = GetLastError(); - printf("Failed to finalize file creation (%u).\n", nError); - } - } - - if(pvFile != NULL) - delete [] pvFile; - if(hMpq != NULL) - SFileCloseArchive(hMpq); - return nError; -} - -static int TestSignatureVerify(const TCHAR * szMpqName) -{ - HANDLE hMpq; - - if(SFileOpenArchive(szMpqName, 0, 0, &hMpq)) - { - _tprintf(_T("Verifying digital signature in %s:\n"), szMpqName); - switch(SFileVerifyArchive(hMpq)) - { - case ERROR_NO_SIGNATURE: - _tprintf(_T("No digital signature present.\n")); - break; - - case ERROR_VERIFY_FAILED: - _tprintf(_T("Failed to verify signature.\n")); - break; - - case ERROR_WEAK_SIGNATURE_OK: - _tprintf(_T("Weak signature is OK.\n")); - break; - - case ERROR_WEAK_SIGNATURE_ERROR: - _tprintf(_T("Weak signature mismatch.\n")); - break; - - case ERROR_STRONG_SIGNATURE_OK: - _tprintf(_T("Strong signature is OK.\n")); - break; - - case ERROR_STRONG_SIGNATURE_ERROR: - _tprintf(_T("Strong signature mismatch.\n")); - break; - } - - SFileCloseArchive(hMpq); - _tprintf(_T("\n")); - } - - return 0; -} - - -static int TestCreateArchiveCopy(const TCHAR * szMpqName, const TCHAR * szMpqCopyName, const char * szListFile) -{ - TFileStream * pStream; - TCHAR szLocalFile[MAX_PATH]; - HANDLE hMpq1 = NULL; // Handle of existing archive - HANDLE hMpq2 = NULL; // Handle of created archive - DWORD dwHashTableSize = 0; - int nError = ERROR_SUCCESS; - - // If no listfile or an empty one, use NULL - if(szListFile == NULL || *szListFile == 0) - szListFile = NULL; - - // Create the new file - pStream = FileStream_CreateFile(szMpqCopyName, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); - if(pStream == NULL) - nError = GetLastError(); - - // Write some data - if(nError == ERROR_SUCCESS) - { - ULONGLONG FileSize = 0x100000; - - FileStream_SetSize(pStream, FileSize); - FileStream_Close(pStream); - } - - // Open the existing MPQ archive - if(nError == ERROR_SUCCESS) - { - _tprintf(_T("Opening %s ...\n"), szMpqName); - if(!SFileOpenArchive(szMpqName, 0, 0, &hMpq1)) - nError = GetLastError(); - } - - // Well, now create the MPQ archive - if(nError == ERROR_SUCCESS) - { - _tprintf(_T("Creating %s ...\n"), szMpqCopyName); - SFileGetFileInfo(hMpq1, SFILE_INFO_HASH_TABLE_SIZE, &dwHashTableSize, 4); - if(!SFileCreateArchive(szMpqCopyName, 0, dwHashTableSize, &hMpq2)) - nError = GetLastError(); - } - - // Copy all files from one archive to another - if(nError == ERROR_SUCCESS) - { - SFILE_FIND_DATA sf; - HANDLE hFind = SFileFindFirstFile(hMpq1, "*", &sf, szListFile); - bool bResult = true; - - _tprintf(_T("Copying files ...\n")); - - if(hFind != NULL) - { - while(bResult) - { - if(strcmp(sf.cFileName, LISTFILE_NAME) && strcmp(sf.cFileName, ATTRIBUTES_NAME)) - { - SFileSetLocale(sf.lcLocale); - - // Create the local file name - MergeLocalPath(szLocalFile, szWorkDir, sf.szPlainName); - if(SFileExtractFile(hMpq1, sf.cFileName, szLocalFile)) - { - 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); - _tremove(szLocalFile); - break; - } - else - { - printf("Adding %s ... OK\n", sf.cFileName); - } - } - else - { - printf("Extracting %s ... Failed\n", sf.cFileName); - } - - // Delete the added file - _tremove(szLocalFile); - } - - // Find the next file - bResult = SFileFindNextFile(hFind, &sf); - } - - // Close the search handle - SFileFindClose(hFind); - printf("\n"); - } - } - - // Close both archives - if(hMpq2 != NULL) - SFileCloseArchive(hMpq2); - if(hMpq1 != NULL) - SFileCloseArchive(hMpq1); - 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")); - } - - // 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); - SFileSetFilePointer(hFile, 0, NULL, FILE_BEGIN); - - // Allocate space for the full file - pbFullFile = new BYTE[dwFileSize]; - if(pbFullFile != NULL) - { - if(!SFileReadFile(hFile, pbFullFile, dwFileSize)) - { - 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 TCHAR * szMpqName1, - const TCHAR * szMpqName2, - const char * szListFile, - DWORD dwBlockSize) -{ - TMPQArchive * ha1 = NULL; - TMPQArchive * ha2 = NULL; - HANDLE hMpq1 = NULL; // Handle of the first archive - HANDLE hMpq2 = NULL; // Handle of the second archive - HANDLE hFile1 = NULL; - HANDLE hFile2 = NULL; - int nError = ERROR_SUCCESS; - - // If no listfile or an empty one, use NULL - if(szListFile == NULL || *szListFile == 0) - szListFile = NULL; - - _tprintf(_T("=============== Comparing MPQ archives ===============\n")); - - // Open the first MPQ archive - if(nError == ERROR_SUCCESS && szMpqName1 != NULL) - { - _tprintf(_T("Opening %s ...\n"), szMpqName1); - if(!SFileOpenArchive(szMpqName1, 0, 0, &hMpq1)) - nError = GetLastError(); - ha1 = (TMPQArchive *)hMpq1; - } - - // Open the second MPQ archive - if(nError == ERROR_SUCCESS && szMpqName2 != NULL) - { - _tprintf(_T("Opening %s ...\n"), szMpqName2); - if(!SFileOpenArchive(szMpqName2, 0, 0, &hMpq2)) - nError = GetLastError(); - ha2 = (TMPQArchive *)hMpq2; - } - - // Compare the header - if(nError == ERROR_SUCCESS && (ha1 != NULL && ha2 != NULL)) - { - if(ha1->pHeader->dwHeaderSize != ha2->pHeader->dwHeaderSize) - printf(" - Header size is different\n"); - if(ha1->pHeader->wFormatVersion != ha2->pHeader->wFormatVersion) - printf(" - Format version is different\n"); - if(ha1->pHeader->wSectorSize != ha2->pHeader->wSectorSize) - printf(" - Sector size is different\n"); - if(ha1->pHeader->HetTableSize64 != ha2->pHeader->HetTableSize64) - printf(" - HET table size is different\n"); - if(ha1->pHeader->BetTableSize64 != ha2->pHeader->BetTableSize64) - printf(" - BET table size is different\n"); - if(ha1->pHeader->dwHashTableSize != ha2->pHeader->dwHashTableSize) - printf(" - Hash table size is different\n"); - if(ha1->pHeader->dwBlockTableSize != ha2->pHeader->dwBlockTableSize) - printf(" - Block table size is different\n"); - } - - // Find all files in the first archive and compare them - if(nError == ERROR_SUCCESS) - { - SFILE_FIND_DATA sf; - TMPQFile * hf1; - TMPQFile * hf2; - HANDLE hFind = SFileFindFirstFile(hMpq1, "*", &sf, szListFile); - bool bResult = true; - - while(hFind != NULL && bResult == true) - { -// printf("%s \r", sf.cFileName); - SFileSetLocale(sf.lcLocale); - - // Open the first file - if(!SFileOpenFileEx(hMpq1, sf.cFileName, 0, &hFile1)) - printf("Failed to open the file %s in the first archive\n", sf.cFileName); - - if(!SFileOpenFileEx(hMpq2, sf.cFileName, 0, &hFile2)) - printf("Failed to open the file %s in the second archive\n", sf.cFileName); - - if(hFile1 != NULL && hFile2 != NULL) - { - // Get the TMPQFile pointers - hf1 = (TMPQFile *)hFile1; - hf2 = (TMPQFile *)hFile2; - - // Compare the file sizes - if(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("Different mpq flags: %s (%08X x %08X)\n", sf.cFileName, hf1->pFileEntry->dwFlags, hf2->pFileEntry->dwFlags); - - if(!CompareArchivedFiles(sf.cFileName, hFile1, hFile2, dwBlockSize)) - printf("Different file data: %s\n", sf.cFileName); - -// if(!CompareArchivedFilesRR(sf.cFileName, hFile1, hFile2, dwBlockSize)) -// printf("Different file data: %s\n", sf.cFileName); - } - - // Close both files - if(hFile2 != NULL) - SFileCloseFile(hFile2); - if(hFile1 != NULL) - SFileCloseFile(hFile1); - hFile2 = hFile1 = NULL; - - // Find the next file - bResult = SFileFindNextFile(hFind, &sf); - } - - // Close the find handle - if(hFind != NULL) - SFileFindClose(hFind); - } - - // Close both archives - clreol(); - printf("================ MPQ compare complete ================\n"); - if(hMpq2 != NULL) - SFileCloseArchive(hMpq2); - if(hMpq1 != NULL) - SFileCloseArchive(hMpq1); - return nError; -} - -//----------------------------------------------------------------------------- -// Searching all known MPQs - -#ifdef _WIN32 -static int TestSearchOneArchive(const TCHAR * szMpqName) -{ - SFILE_FIND_DATA sf; - HANDLE hFind; - HANDLE hMpq; - const TCHAR * szExtension; - bool bFound = true; - - // Get the file extension - szExtension = _tcsrchr(szMpqName, _T('.')); - if(szExtension == NULL) - return ERROR_SUCCESS; - - // Only search defined extensions - if(_tcsicmp(szExtension, _T(".mpq")) && _tcsnicmp(szExtension, _T(".SC2"), 4)) - return ERROR_SUCCESS; - - _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); - if(hFind != NULL) - { - while(bFound) - { - if(sf.dwFileFlags & MPQ_FILE_DELETE_MARKER) - _tprintf(_T("Delete marker: %s:%hs\n"), szMpqName, sf.cFileName); - - bFound = SFileFindNextFile(hFind, &sf); - } - } - - SFileCloseArchive(hMpq); - } - - return 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) - { - while(bFound) - { - // Eliminate "." and ".." - if(wf.cFileName[0] != _T('.')) - { - // Construct the full path - _tcscpy(szFilePart, wf.cFileName); - - // If it a directory? - if(wf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - _tcscat(szFullPath, _T("\\*")); - TestSearchAllArchives(szFullPath); - } - else - { - TestSearchOneArchive(szFullPath); - } - } - bFound = FindNextFile(hFind, &wf); - } - FindClose(hFind); - } - - return ERROR_SUCCESS; -} -#endif - -//----------------------------------------------------------------------------- -// Main -// - -int main(void) -{ - int nError = ERROR_SUCCESS; - -#if defined(_MSC_VER) && defined(_DEBUG) - _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()); - - // Test structure sizes -// 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); - - // Test compression methods -// if(nError == ERROR_SUCCESS) -// nError = TestSectorCompress(MPQ_SECTOR_SIZE); - - // Test the archive open and close -// if(nError == ERROR_SUCCESS) -// nError = TestArchiveOpenAndClose(_T("d:\\Install\\Blizzard\\Diablo III\\Diablo-III-8370-enGB-Installer\\Installer Tome 1.MPQE")); -// 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("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"), -// "c:\\Tools32\\Arj32.exe", -// "c:\\Tools32\\autoruns.chm", -// "c:\\Tools32\\CPUEater.exe", -// "c:\\Tools32\\dumpbin.exe", -// "c:\\Tools32\\editbin.exe", -// "c:\\Tools32\\fsg.ini", -// "c:\\Tools32\\hiew8.ini", -// "c:\\Tools32\\ida.bat", -// "c:\\Tools32\\mp3.ini", -// NULL); - -// if(nError == ERROR_SUCCESS) -// nError = TestCreateArchiveFromMemory(MAKE_PATH("Test-leak.mpq")); - -// if(nError == ERROR_SUCCESS) -// nError = TestFileReadAndWrite(MAKE_PATH("2002 - Warcraft III/(10)DustwallowKeys.w3m"), "war3map.j"); - - // Verify the archive signature -// if(nError == ERROR_SUCCESS) -// nError = TestSignatureVerify(MAKE_PATH("1998 - Starcraft/BW-1152.exe")); -// nError = TestSignatureVerify(MAKE_PATH("2002 - Warcraft III/(10)DustwallowKeys.w3m")); -// nError = TestSignatureVerify(MAKE_PATH("2002 - Warcraft III/War3TFT_121b_English.exe")); -// 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 -// if(nError == ERROR_SUCCESS) -// 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 = 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); - } - - -// if(nError == ERROR_SUCCESS) -// nError = TestSearchAllArchives(MAKE_PATH("*")); - - return nError; -} diff --git a/dep/StormLib/test/stormlib-test-001.txt b/dep/StormLib/test/stormlib-test-001.txt new file mode 100644 index 0000000000..8f6e508ccd --- /dev/null +++ b/dep/StormLib/test/stormlib-test-001.txt @@ -0,0 +1,187 @@ +==== Test Suite for StormLib version 9.31 ==== +InitWorkFolder: Work directory \Multimedia\MPQs (default) +units/Orc/HealingWard/Rune2.blp +TestUseAfterFree (Test-UAF.mpq) succeeded. +TestLiFiSearch (FLAT-MAP:listfile-test.txt) succeeded. +TestLiFiSearch (listfile-test.txt) succeeded. +TestFileStream (MPQ_2013_v4_alternate-original.MPQ) succeeded. +TestFileStream (MPQ_2013_v4_alternate-original.MPQ) succeeded. +TestFileStream (MPQ_2013_v4_alternate-complete.MPQ) succeeded. +TestFileStream (part-file://MPQ_2009_v2_WoW_patch.MPQ.part) succeeded. +TestFileStream (blk4-file://streaming/model.MPQ.0) succeeded. +TestFileStream (mpqe-file://MPQ_2011_v2_EncryptedMpq.MPQE) succeeded. +TestFileMirror (part-file://MPQ_2009_v1_patch-created.MPQ.part) succeeded. +TestFileMirror (part-file://MPQ_2009_v1_patch-partial.MPQ.part) succeeded. +TestFileMirror (part-file://MPQ_2009_v1_patch-complete.MPQ.part) succeeded. +TestFileMirror (MPQ_2013_v4_alternate-created.MPQ) succeeded. +TestFileMirror (MPQ_2013_v4_alternate-incomplete.MPQ) succeeded. +TestFileMirror (MPQ_2013_v4_alternate-complete.MPQ) succeeded. +TestReadingMpq: Warning: CRC32 error on replay.game.events +TestReadingMpq (pocs/MPQ_2024_01_HeapOverrun.mpq) succeeded. +TestReadingMpq (pocs/MPQ_2024_02_StackOverflow.mpq) succeeded. +TestReadingMpq (pocs/MPQ_2024_03_TooBigAlloc.mpq) succeeded. +TestReadingMpq (pocs/MPQ_2024_04_HeapOverflow.mpq) succeeded. +TestReadingMpq: Warning: CRC32 error on replay.tracker.events +TestReadingMpq (pocs/MPQ_2024_05_HeapOverflow.mpq) succeeded. +TestReadingMpq (pocs/MPQ_2024_06_HeapOverflowReadFile.mpq) succeeded. +TestReadingMpq (pocs/MPQ_2024_07_InvalidBitmapFooter.mpq) succeeded. +TestReadingMpq (pocs/MPQ_2024_08_InvalidSectorSize.mpq) succeeded. +TestReadingMpq (pocs/MPQ_2024_09_InvalidSectorSize.mpq) succeeded. +TestReadingMpq (pocs/MPQ_2024_10_HuffDecompressError.mpq) succeeded. +TestReadingMpq (pocs/MPQ_2024_10_SparseDecompressError.mpq) succeeded. +TestReadingMpq (pocs/MPQ_2024_11_HiBlockTablePosInvalid.mpq) succeeded. +TestReadingMpq: Error loading the file (listfile) (error code: 38) +TestReadingMpq (pocs/MPQ_2025_01_SectorTableBeyondEOF.mpq) succeeded. +TestReadingMpq: Warning: CRC32 error on (listfile) +TestReadingMpq: Warning: CRC32 error on (listfile) +TestReadingMpq (pocs/MPQ_2025_02_SectorOffsetSizeNotAligned.mpq) succeeded. +TestReadingMpq (pocs/MPQ_2025_03_InvalidPatchInfo.mpq) succeeded. +TestReadingMpq (pocs/MPQ_2025_04_InvalidArchiveSize64.mpq) succeeded. +TestReadingMpq (pocs/MPQ_2025_05_AddFileError.mpq) succeeded. +TestReadingMpq (pocs/MPQ_2025_06_BadHashTableSize.mpq) succeeded. +TestReadingMpq (pocs/MPQ_2025_07_BadHetTableSize.mpq) succeeded. +TestReadingMpq (MPQ_1997_v1_Diablo1_DIABDAT.MPQ) succeeded. +TestReadingMpq (MPQ_1997_v1_patch_rt_SC1B.mpq) succeeded. +TestReadingMpq (MPQ_1997_v1_StarDat_SC1B.mpq) succeeded. +TestReadingMpq (MPQ_1997_v1_INSTALL_SC1B.EXE_) succeeded. +TestReadingMpq: Warning: CRC32 error on (signature) +TestReadingMpq: Warning: CRC32 error on (signature) +TestReadingMpq (MPQ_2016_v1_D2XP_IX86_1xx_114a.mpq) succeeded. +TestReadingMpq (MPQ_2018_v1_icon_error.w3m) succeeded. +TestReadingMpq (MPQ_1997_v1_Diablo1_STANDARD.SNP) succeeded. +TestReadingMpq (MPQ_2012_v2_EmptyMpq.MPQ) succeeded. +TestReadingMpq (MPQ_2013_v4_EmptyMpq.MPQ) succeeded. +TestReadingMpq (MPQ_2013_v4_patch-base-16357.MPQ) succeeded. +TestReadingMpq (MPQ_2011_v4_InvalidHetEntryCount.MPQ) succeeded. +TestReadingMpq (MPQ_2002_v1_BlockTableCut.MPQ) succeeded. +TestReadingMpq (MPQ_2010_v2_HasUserData.s2ma) succeeded. +TestReadingMpq: Warning: CRC32 error on (listfile) +TestReadingMpq: Warning: CRC32 error on (listfile) +TestReadingMpq: Warning: CRC32 error on File00000003.xxx +TestReadingMpq (MPQ_2014_v1_AttributesOneEntryLess.w3x) succeeded. +TestReadingMpq (MPQ_2010_v3_expansion-locale-frFR.MPQ) succeeded. +TestReadingMpq (mpqe-file://MPQ_2011_v2_EncryptedMpq.MPQE) succeeded. +TestReadingMpq (part-file://MPQ_2010_v2_HashTableCompressed.MPQ.part) succeeded. +TestReadingMpq (blk4-file://streaming/model.MPQ.0) succeeded. +TestReadingMpq: Warning: CRC32 error on replay.message.events +TestReadingMpq (MPQ_2023_v2_MemoryCorruption.SC2Replay) succeeded. +TestReadingMpq (MPQ_2023_v1_StarcraftMap.scm) succeeded. +TestReadingMpq (MPQ_2023_v1_BroodWarMap.scx) succeeded. +TestReadingMpq (MPQ_2023_v1_Volcanis.scm) succeeded. +TestReadingMpq (MPQ_2023_v4_UTF8.s2ma) succeeded. +TestReadingMpq (MPQ_2023_v1_GreenTD.w3x) succeeded. +TestReadingMpq (MPQ_2023_v4_1F644C5A.SC2Replay) succeeded. +TestReadingMpq () succeeded. +TestReadingMpq (MPQ_2024_v1_BadUtf8_5.0.2.w3x) succeeded. +TestReadingMpq (MPQ_2002_v1_ProtectedMap_InvalidUserData.w3x) succeeded. +TestReadingMpq (MPQ_2002_v1_ProtectedMap_InvalidMpqFormat.w3x) succeeded. +TestReadingMpq (MPQ_2002_v1_ProtectedMap_Spazzler.w3x) succeeded. +TestReadingMpq (MPQ_2014_v1_ProtectedMap_Spazzler2.w3x) succeeded. +TestReadingMpq (MPQ_2014_v1_ProtectedMap_Spazzler3.w3x) succeeded. +TestReadingMpq (MPQ_2002_v1_ProtectedMap_BOBA.w3m) succeeded. +TestReadingMpq (MPQ_2015_v1_ProtectedMap_KangTooJee.w3x) succeeded. +TestReadingMpq (MPQ_2015_v1_ProtectedMap_Somj2hM16.w3x) succeeded. +TestReadingMpq (MPQ_2015_v1_ProtectedMap_Spazy.w3x) succeeded. +TestReadingMpq (MPQ_2015_v1_MessListFile.mpq) succeeded. +TestReadingMpq (MPQ_2016_v1_ProtectedMap_TableSizeOverflow.w3x) succeeded. +TestReadingMpq (MPQ_2016_v1_ProtectedMap_HashOffsIsZero.w3x) succeeded. +TestReadingMpq (MPQ_2016_v1_ProtectedMap_Somj2.w3x) succeeded. +TestReadingMpq (MPQ_2016_v1_WME4_4.w3x) succeeded. +TestReadingMpq (MPQ_2016_v1_SP_(4)Adrenaline.w3x) succeeded. +TestReadingMpq (MPQ_2016_v1_ProtectedMap_1.4.w3x) succeeded. +TestReadingMpq (MPQ_2016_v1_KoreanFile.w3m) succeeded. +TestReadingMpq (MPQ_2017_v1_Eden_RPG_S2_2.5J.w3x) succeeded. +TestReadingMpq (MPQ_2017_v1_BigDummyFiles.w3x) succeeded. +TestReadingMpq (MPQ_2017_v1_TildeInFileName.mpq) succeeded. +TestReadingMpq (MPQ_2018_v1_EWIX_v8_7.w3x) succeeded. +TestReadingMpq (MPQ_2020_v4_FakeMpqHeaders.SC2Mod) succeeded. +TestReadingMpq (MPQ_2020_v4_NP_Protect_1.s2ma) succeeded. +TestReadingMpq (MPQ_2020_v4_NP_Protect_2.s2ma) succeeded. +TestReadingMpq (MPQ_2015_v1_flem1.w3x) succeeded. +TestReadingMpq (MPQ_2002_v1_ProtectedMap_HashTable_FakeValid.w3x) succeeded. +TestReadingMpq (MPQ_2021_v1_CantExtractCHK.scx) succeeded. +TestReadingMpq (MPQ_2022_v1_Sniper.scx) succeeded. +TestReadingMpq (MPQ_2022_v1_OcOc_Bound_2.scx) succeeded. +TestReadingMpq (MPQ_2023_v1_Lusin2Rpg1.28.w3x) succeeded. +TestReadingMpq (MPQ_2024_v1_300TK2.09p.w3x) succeeded. +TestReadingMpq (MPQ_2025_v1_Legion_TD_11_2d-BETA_2_TeamOZE.w3x) succeeded. +TestReadingMpq (MPQ_2026_v1_The Art of Defense v4.11 G0A.w3x) succeeded. +TestReadingMpq (MPQ_2026_v1_BadTablesSize.scx) succeeded. +TestReadingMpq (mix-mpq/AHF04patch.mix) succeeded. +TestReadingMpq (mix-mpq/hs0.1.asi) succeeded. +TestReadingMpq (mix-mpq/hs0.8.asi) succeeded. +TestReadingMpq (mix-mpq/MoeMoeMod.asi) succeeded. +TestReadingMpq (MPx_2013_v1_LongwuOnline.mpk) succeeded. +TestReadingMpq (MPx_2013_v1_WarOfTheImmortals.sqp) succeeded. +TestReadingMpq (MPx_2022_v1_Music.mpk) succeeded. +TestReadingMpq (MPx_2022_v1_Scp.mpk) succeeded. +TestReadingMpq (MPx_2022_v1_UI.mpk) succeeded. +TestReadingMpq (MPQ_1998_v1_StarCraft.mpq) succeeded. +TestReadingMpq (MPQ_2005_v1_texture.MPQ) succeeded. +TestReadingMpq (MPQ_2012_v4_OldWorld.MPQ) succeeded. +TestReadingMpq (MPQ_2013_v4_world.MPQ) succeeded. +TestReadingMpq (MPQ_2013_v4_locale-enGB.MPQ) succeeded. +TestReadingMpq (MPQ_2013_v4_Base1.SC2Data) succeeded. +TestReadingMpq (MPQ_2013_v4_Mods#Core.SC2Mod#enGB.SC2Assets) succeeded. +TestReadingMpq (MPQ_2013_v4_Base1.SC2Data) succeeded. +TestReadingMpq (MPQ_2013_v4_Base3.SC2Maps) succeeded. +TestReadingMpq (MPQ_2013_v4_Mods#Liberty.SC2Mod#enGB.SC2Data) succeeded. +TestReadingMpq (MPQ_2014_v4_base-Win.MPQ) succeeded. +TestReadingMpq (MPQ_2014_v4_base-Win.MPQ) succeeded. +TestReadingMpq (MPQ_1997_v1_Diablo1_STANDARD.SNP) succeeded. +TestReadingMpq (MPQ_1998_v1_StarDat.mpq) succeeded. +TestReadingMpq (MPQ_1999_v1_WeakSignature.exe) succeeded. +TestReadingMpq (MPQ_1999_v1_WeakSignature.exe) succeeded. +TestReadingMpq (MPQ_2002_v1_StrongSignature.w3m) succeeded. +TestReadingMpq (MPQ_2003_v1_WeakSignatureEmpty.exe) succeeded. +TestReadingMpq: Warning: CRC32 error on (signature) +TestReadingMpq: Warning: CRC32 error on (signature) +TestReadingMpq (MPQ_2006_v1_WoW-1.11.2.5464-patch.exe_) succeeded. +TestReadingMpq (MPQ_2007_v2_StrongSignature1.exe) succeeded. +TestReadingMpq (MPQ_2007_v2_StrongSignature2.MPQ) succeeded. +TestReadingMpq (flat-file://streaming/model.MPQ.0) succeeded. +TestReadingMpq (MPQ_2013_vX_Battle.net.MPQ) succeeded. +TestReadingMpq (MPQ_1997_v1_Diablo1_DIABDAT.MPQ) succeeded. +TestReadingMpq (MPQ_1997_v1_Diablo1_DIABDAT.MPQ) succeeded. +TestReadingMpq (MPQ_2002_v1_StrongSignature.w3m) succeeded. +TestReadingMpq (MPQ_2013_v4_SC2_EmptyMap.SC2Map) succeeded. +Test_ReopenMpq (MPQ_2010_v3_expansion-locale-frFR.MPQ) succeeded. +Test_ReopenMpq (MPQ_2016_v1_00000.pak) succeeded. +Test_ReopenMpq (MPQ_2013_v4_SC2_EmptyMap.SC2Map) succeeded. +Test_ReopenMpq (MPQ_2013_v4_expansion1.MPQ) succeeded. +Test_ReopenMpq (MPQ_2014_v1_out1.w3x) succeeded. +Test_ReopenMpq (MPQ_2014_v1_out2.w3x) succeeded. +Test_ReopenMpq (MPQ_1997_v1_Diablo1_DIABDAT.MPQ) succeeded. +Test_ReopenMpq (MPQ_2013_v4_SC2_EmptyMap.SC2Map) succeeded. +Test_ReopenMpq (MPQ_2002_v1_LargeMapFile.w3m) succeeded. +Test_Signature (MPQ_1999_v1_WeakSigned1.mpq) succeeded. +Test_Signature (MPQ_1999_v1_WeakSigned1.mpq) succeeded. +TestModifyMpq (MPQ_2014_v4_Base.StormReplay) succeeded. +TestModifyMpq (MPQ_2022_v1_v4.329.w3x) succeeded. +TestModifyMpq (MPQ_2023_v1_StarcraftMap.scm) succeeded. +TestVerifyHash succeeded. +CreateNewMpq (StormLibTest_EmptyMpq_v2.mpq) succeeded. +CreateNewMpq (StormLibTest_EmptyMpq_v4.mpq) succeeded. +CreateNewMpq (StormLibTest_Český.mpq) succeeded. +CreateNewMpq (StormLibTest_Русский.mpq) succeeded. +CreateNewMpq (StormLibTest_ελληνικά.mpq) succeeded. +CreateNewMpq (StormLibTest_日本語.mpq) succeeded. +CreateNewMpq (StormLibTest_简体中文.mpq) succeeded. +CreateNewMpq (StormLibTest_الععربية.mpq) succeeded. +CreateNewMpq (StormLibTest_NonStdNames.mpq) succeeded. +CreateNewMpq (StormLibTest_MpqEditorTest.mpq) succeeded. +TestRenameFile (MPQ_2002_v1_StrongSignature.w3m) succeeded. +TestCreateGaps (StormLibTest_GapsTest.mpq) succeeded. +TestCreateFull (StormLibTest_FileTableFull.mpq) succeeded. +TestCreateFull (StormLibTest_FileTableFull.mpq) succeeded. +TestCreateFull (StormLibTest_FileTableFull.mpq) succeeded. +TestCreateFull (StormLibTest_FileTableFull.mpq) succeeded. +IncMaxFileCount (StormLibTest_IncMaxFileCount.mpq) succeeded. +TestFileFlag (StormLibTest_FileFlagTest.mpq) succeeded. +TestCompressions: Warning: CRC32 error on WaveFile_01.wav +TestCompressions: Warning: CRC32 error on WaveFile_02.wav +TestCompressions (StormLibTest_AddWaveMonoBadTest.mpq) succeeded. +TestCompressions: Warning: CRC32 error on WaveFile_01.wav +TestCompressions: Warning: CRC32 error on WaveFile_02.wav +ListFilePos (StormLibTest_ListFilePos.mpq) succeeded. +TestBigArchive (StormLibTest_BigArchive_v4.mpq) succeeded. diff --git a/dep/StormLib/test/x86_ripped_code.asm b/dep/StormLib/test/x86_ripped_code.asm deleted file mode 100644 index 4b4be328a6..0000000000 --- a/dep/StormLib/test/x86_ripped_code.asm +++ /dev/null @@ -1,1231 +0,0 @@ -.686P -.MODEL FLAT -ASSUME FS: NOTHING -.CODE - -extrn _memset:PROC -extrn _memcpy:PROC -extrn _memmove:PROC - -;------------------------------------------------------------------------------ -; Structures -; - -;------------------------------------------------------------------------------ -; Functions -; - -_aullrem proc near ; CODE XREF: sub_6CC140+3Ap - ; sub_6CC1E0+57p ... - -DividendLo = dword ptr 8 -DividendHi = dword ptr 0Ch -DivisorLo = dword ptr 10h -DivisorHi = dword ptr 14h - - push ebx - mov eax, [esp+DivisorHi] - or eax, eax - jnz short loc_8F8FE1 - mov ecx, [esp+DivisorLo] - mov eax, [esp+DividendHi] - xor edx, edx - div ecx - mov eax, [esp+DividendLo] - div ecx - mov eax, edx - xor edx, edx - jmp short loc_8F9031 -; --------------------------------------------------------------------------- - -loc_8F8FE1: ; CODE XREF: _aullrem+7j - mov ecx, eax - mov ebx, [esp+DivisorLo] - mov edx, [esp+DividendHi] - mov eax, [esp+DividendLo] - -loc_8F8FEF: ; CODE XREF: _aullrem+39j - shr ecx, 1 - rcr ebx, 1 - shr edx, 1 - rcr eax, 1 - or ecx, ecx - jnz short loc_8F8FEF - div ebx - mov ecx, eax - mul [esp+DivisorHi] - xchg eax, ecx - mul [esp+DivisorLo] - add edx, ecx - jb short loc_8F901A - cmp edx, [esp+DividendHi] - ja short loc_8F901A - jb short loc_8F9022 - cmp eax, [esp+DividendLo] - jbe short loc_8F9022 - -loc_8F901A: ; CODE XREF: _aullrem+4Aj - ; _aullrem+50j - sub eax, [esp+DivisorLo] - sbb edx, [esp+DivisorHi] - -loc_8F9022: ; CODE XREF: _aullrem+52j - ; _aullrem+58j - sub eax, [esp+DividendLo] - sbb edx, [esp+DividendHi] - neg edx - neg eax - sbb edx, 0 - -loc_8F9031: ; CODE XREF: _aullrem+1Fj - pop ebx - retn 10h -_aullrem endp - -_aullshr proc near ; CODE XREF: sub_40E2B6+1ECp - ; sub_40E2B6+240p ... - cmp cl, 40h - jnb short loc_414BDA - cmp cl, 20h - jnb short loc_414BD0 - shrd eax, edx, cl - shr edx, cl - retn -; --------------------------------------------------------------------------- - -loc_414BD0: ; CODE XREF: _aullshr+8j - mov eax, edx - xor edx, edx - and cl, 1Fh - shr eax, cl - retn -; --------------------------------------------------------------------------- - -loc_414BDA: ; CODE XREF: _aullshr+3j - xor eax, eax - xor edx, edx - retn -_aullshr endp - - -; =============== S U B R O U T I N E ======================================= - -; Attributes: bp-based frame - -SFileDecryptMpqHeader proc near ; CODE XREF: sub_6D00E0+AEp - ; sub_6D00E0+D3p - -EncryptedDataAligned= dword ptr -88h -var_48 = dword ptr -48h -DecryptBuffer = dword ptr -44h -var_40 = dword ptr -40h -var_3C = dword ptr -3Ch -var_38 = dword ptr -38h -var_34 = dword ptr -34h -var_30 = dword ptr -30h -var_2C = dword ptr -2Ch -var_28 = dword ptr -28h -var_24 = dword ptr -24h -var_20 = dword ptr -20h -var_1C = dword ptr -1Ch -var_18 = dword ptr -18h -var_14 = dword ptr -14h -var_10 = dword ptr -10h -var_0C = dword ptr -0Ch -var_8 = dword ptr -8 -var_4 = dword ptr -4 -arg_FFFFFFF8 = dword ptr 0 -arg_FFFFFFFC = dword ptr 4 -arg_0 = dword ptr 8 - - push ebp - mov ebp, esp - sub esp, 88h - mov eax, [ecx+8] - mov edx, [ecx+14h] - push ebx - mov ebx, [ecx+10h] - mov [ebp+var_0C], eax - mov eax, [ebp+arg_0] - push esi - push edi - mov edi, [ecx+0Ch] - mov [ebp+DecryptBuffer], ecx - mov [ebp+var_10], edx - test al, 3 - jz short loc_6C9F03 - mov ecx, [eax] - mov [ebp+EncryptedDataAligned], ecx - mov ecx, [eax+4] - mov [ebp+EncryptedDataAligned+4], ecx - mov ecx, [eax+8] - mov [ebp+EncryptedDataAligned+8], ecx - mov ecx, [eax+0Ch] - mov [ebp+EncryptedDataAligned+0Ch], ecx - mov ecx, [eax+10h] - mov [ebp+EncryptedDataAligned+10h], ecx - mov ecx, [eax+14h] - mov [ebp+EncryptedDataAligned+14h], ecx - mov ecx, [eax+18h] - mov [ebp+EncryptedDataAligned+18h], ecx - mov ecx, [eax+1Ch] - mov [ebp+EncryptedDataAligned+1Ch], ecx - mov ecx, [eax+20h] - mov [ebp+EncryptedDataAligned+20h], ecx - mov ecx, [eax+24h] - mov [ebp+EncryptedDataAligned+24h], ecx - mov ecx, [eax+28h] - mov [ebp+EncryptedDataAligned+28h], ecx - mov ecx, [eax+2Ch] - mov [ebp+EncryptedDataAligned+2Ch], ecx - mov ecx, [eax+30h] - mov [ebp+EncryptedDataAligned+30h], ecx - mov ecx, [eax+34h] - mov [ebp+EncryptedDataAligned+34h], ecx - mov ecx, [eax+38h] - mov eax, [eax+3Ch] - mov [ebp+EncryptedDataAligned+3Ch], eax - mov [ebp+EncryptedDataAligned+38h], ecx - lea eax, [ebp+EncryptedDataAligned] - -loc_6C9F03: ; CODE XREF: SFileDecryptMpqHeader+26j - mov ecx, [eax] - mov [ebp+var_30], ecx - mov esi, edi - not esi - and esi, edx - mov edx, ebx - and edx, edi - or esi, edx - add esi, ecx - mov ecx, [ebp+var_0C] - lea edx, [esi+ecx-28955B88h] - mov ecx, [eax+4] - rol edx, 7 - add edx, edi - mov [ebp+var_2C], ecx - mov ecx, edx - not ecx - and ecx, ebx - mov esi, edi - and esi, edx - or ecx, esi - add ecx, [ebp+var_2C] - mov esi, [ebp+var_10] - lea esi, [ecx+esi-173848AAh] - mov ecx, [eax+8] - mov [ebp+var_40], ecx - rol esi, 0Ch - add esi, edx - mov ecx, esi - not ecx - and ecx, edi - mov edi, esi - and edi, edx - or ecx, edi - add ecx, [ebp+var_40] - lea edi, [ecx+ebx+242070DBh] - mov ecx, [eax+0Ch] - ror edi, 0Fh - add edi, esi - mov [ebp+var_1C], ecx - mov ebx, edi - not ebx - and ebx, edx - mov ecx, esi - and ecx, edi - or ebx, ecx - add ebx, [ebp+var_1C] - mov ecx, [ebp+DecryptBuffer] - mov ecx, [ecx+0Ch] - lea ecx, [ebx+ecx-3E423112h] - ror ecx, 0Ah - add ecx, edi - mov [ebp+var_4], edi - and edi, ecx - mov ebx, ecx - not ebx - and ebx, esi - or ebx, edi - mov edi, [eax+10h] - add ebx, edi - lea edx, [ebx+edx-0A83F051h] - rol edx, 7 - add edx, ecx - mov [ebp+arg_0], edx - not edx - and edx, [ebp+var_4] - mov ebx, ecx - and ebx, [ebp+arg_0] - mov [ebp+var_10], edi - mov edi, [eax+14h] - or edx, ebx - add edx, edi - lea esi, [edx+esi+4787C62Ah] - mov edx, [eax+18h] - mov [ebp+var_34], edi - mov edi, [ebp+arg_0] - rol esi, 0Ch - add esi, edi - mov [ebp+var_20], edx - mov edx, esi - not edx - and edx, ecx - mov ebx, esi - and ebx, edi - or edx, ebx - add edx, [ebp+var_20] - mov ebx, [ebp+var_4] - lea edx, [edx+ebx-57CFB9EDh] - ror edx, 0Fh - add edx, esi - mov ebx, edx - not ebx - and ebx, edi - mov edi, esi - and edi, edx - or ebx, edi - mov edi, [eax+1Ch] - add ebx, edi - lea ecx, [ebx+ecx-2B96AFFh] - ror ecx, 0Ah - add ecx, edx - mov [ebp+var_3C], edi - mov edi, [eax+20h] - mov [ebp+var_14], edi - mov edi, ecx - not edi - and edi, esi - mov ebx, edx - and ebx, ecx - or edi, ebx - add edi, [ebp+var_14] - mov ebx, [ebp+arg_0] - lea edi, [edi+ebx+698098D8h] - mov [ebp+var_8], ecx - rol edi, 7 - add edi, ecx - and ecx, edi - mov ebx, edi - not ebx - and ebx, edx - or ebx, ecx - mov ecx, [eax+24h] - add ebx, ecx - mov [ebp+var_0C], ecx - mov ecx, [eax+28h] - lea esi, [ebx+esi-74BB0851h] - mov [ebp+var_24], ecx - rol esi, 0Ch - add esi, edi - mov ecx, esi - not ecx - and ecx, [ebp+var_8] - mov ebx, esi - and ebx, edi - or ecx, ebx - add ecx, [ebp+var_24] - mov ebx, esi - lea edx, [ecx+edx-0A44Fh] - mov ecx, [eax+2Ch] - ror edx, 0Fh - add edx, esi - mov [ebp+var_18], ecx - and ebx, edx - mov ecx, edx - not ecx - and ecx, edi - or ecx, ebx - add ecx, [ebp+var_18] - mov ebx, [ebp+var_8] - lea ecx, [ecx+ebx-76A32842h] - ror ecx, 0Ah - add ecx, edx - mov ebx, ecx - not ebx - mov [ebp+var_4], edx - and edx, ecx - and ebx, esi - or ebx, edx - mov edx, [eax+30h] - add ebx, edx - mov [ebp+var_38], edx - lea edi, [ebx+edi+6B901122h] - mov edx, [eax+34h] - rol edi, 7 - add edi, ecx - mov [ebp+arg_0], edi - not edi - and edi, [ebp+var_4] - mov ebx, ecx - and ebx, [ebp+arg_0] - mov [ebp+var_8], edx - or edi, ebx - add edi, edx - mov edx, [eax+38h] - mov eax, [eax+3Ch] - lea esi, [edi+esi-2678E6Dh] - rol esi, 0Ch - add esi, [ebp+arg_0] - mov [ebp+var_28], edx - mov edi, esi - not edi - mov edx, edi - and edx, ecx - mov ebx, esi - and ebx, [ebp+arg_0] - or edx, ebx - add edx, [ebp+var_28] - mov ebx, [ebp+var_4] - lea edx, [edx+ebx-5986BC72h] - mov [ebp+var_4], eax - ror edx, 0Fh - add edx, esi - mov ebx, edx - not ebx - mov [ebp+var_48], ebx - and ebx, [ebp+arg_0] - mov eax, esi - and eax, edx - or ebx, eax - add ebx, [ebp+var_4] - and edi, edx - lea ecx, [ebx+ecx+49B40821h] - ror ecx, 0Ah - add ecx, edx - mov eax, esi - and eax, ecx - or edi, eax - add edi, [ebp+var_2C] - mov eax, [ebp+arg_0] - lea edi, [edi+eax-9E1DA9Eh] - mov eax, [ebp+var_48] - and eax, ecx - rol edi, 5 - add edi, ecx - mov ebx, edx - and ebx, edi - or eax, ebx - add eax, [ebp+var_20] - lea esi, [eax+esi-3FBF4CC0h] - rol esi, 9 - add esi, edi - mov eax, ecx - not eax - and eax, edi - mov ebx, esi - and ebx, ecx - or eax, ebx - add eax, [ebp+var_18] - lea edx, [eax+edx+265E5A51h] - rol edx, 0Eh - add edx, esi - mov eax, edi - not eax - and eax, esi - mov ebx, edx - and ebx, edi - or eax, ebx - add eax, [ebp+var_30] - lea ecx, [eax+ecx-16493856h] - ror ecx, 0Ch - mov eax, esi - add ecx, edx - not eax - and eax, edx - mov ebx, esi - and ebx, ecx - or eax, ebx - add eax, [ebp+var_34] - lea edi, [eax+edi-29D0EFA3h] - rol edi, 5 - add edi, ecx - mov [ebp+arg_0], edi - mov eax, edx - not eax - and eax, ecx - mov edi, edx - and edi, [ebp+arg_0] - or eax, edi - add eax, [ebp+var_24] - mov edi, ecx - lea esi, [eax+esi+2441453h] - mov eax, [ebp+arg_0] - not edi - and edi, eax - rol esi, 9 - add esi, eax - not eax - and eax, esi - mov ebx, esi - and ebx, ecx - or edi, ebx - add edi, [ebp+var_4] - lea edx, [edi+edx-275E197Fh] - rol edx, 0Eh - add edx, esi - mov edi, edx - and edi, [ebp+arg_0] - or eax, edi - add eax, [ebp+var_10] - mov edi, esi - lea ecx, [eax+ecx-182C0438h] - ror ecx, 0Ch - add ecx, edx - and edi, ecx - mov eax, esi - not eax - and eax, edx - or eax, edi - add eax, [ebp+var_0C] - mov edi, [ebp+arg_0] - lea eax, [eax+edi+21E1CDE6h] - rol eax, 5 - add eax, ecx - mov [ebp+arg_0], eax - mov eax, edx - not eax - and eax, ecx - mov edi, edx - and edi, [ebp+arg_0] - or eax, edi - add eax, [ebp+var_28] - mov edi, ecx - lea esi, [eax+esi-3CC8F82Ah] - mov eax, [ebp+arg_0] - rol esi, 9 - add esi, eax - not edi - and edi, eax - mov ebx, esi - and ebx, ecx - or edi, ebx - add edi, [ebp+var_1C] - not eax - lea edx, [edi+edx-0B2AF279h] - rol edx, 0Eh - add edx, esi - and eax, esi - mov edi, edx - and edi, [ebp+arg_0] - or eax, edi - add eax, [ebp+var_14] - lea ecx, [eax+ecx+455A14EDh] - ror ecx, 0Ch - add ecx, edx - mov eax, esi - not eax - and eax, edx - mov edi, esi - and edi, ecx - or eax, edi - add eax, [ebp+var_8] - mov edi, [ebp+arg_0] - lea eax, [eax+edi-561C16FBh] - rol eax, 5 - add eax, ecx - mov [ebp+arg_0], eax - mov eax, edx - not eax - and eax, ecx - mov edi, edx - and edi, [ebp+arg_0] - or eax, edi - add eax, [ebp+var_40] - mov edi, ecx - lea esi, [eax+esi-3105C08h] - mov eax, [ebp+arg_0] - not edi - and edi, eax - rol esi, 9 - add esi, eax - mov ebx, esi - and ebx, ecx - or edi, ebx - add edi, [ebp+var_3C] - not eax - lea edx, [edi+edx+676F02D9h] - and eax, esi - rol edx, 0Eh - add edx, esi - mov edi, edx - and edi, [ebp+arg_0] - or eax, edi - add eax, [ebp+var_38] - mov edi, [ebp+arg_0] - lea ecx, [eax+ecx-72D5B376h] - ror ecx, 0Ch - add ecx, edx - mov eax, esi - xor eax, edx - xor eax, ecx - add eax, [ebp+var_34] - lea eax, [eax+edi-5C6BEh] - rol eax, 4 - add eax, ecx - mov edi, edx - xor edi, ecx - xor edi, eax - add edi, [ebp+var_14] - lea esi, [edi+esi-788E097Fh] - rol esi, 0Bh - add esi, eax - mov edi, esi - xor edi, ecx - xor edi, eax - add edi, [ebp+var_18] - lea edx, [edi+edx+6D9D6122h] - rol edx, 10h - add edx, esi - mov edi, esi - xor edi, edx - mov ebx, edi - xor ebx, eax - add ebx, [ebp+var_28] - lea ecx, [ebx+ecx-21AC7F4h] - ror ecx, 9 - add ecx, edx - xor edi, ecx - add edi, [ebp+var_2C] - lea eax, [edi+eax-5B4115BCh] - rol eax, 4 - mov edi, edx - add eax, ecx - xor edi, ecx - xor edi, eax - add edi, [ebp+var_10] - lea esi, [edi+esi+4BDECFA9h] - rol esi, 0Bh - add esi, eax - mov edi, esi - xor edi, ecx - xor edi, eax - add edi, [ebp+var_3C] - lea edx, [edi+edx-944B4A0h] - rol edx, 10h - add edx, esi - mov edi, esi - xor edi, edx - mov ebx, edi - xor ebx, eax - add ebx, [ebp+var_24] - lea ecx, [ebx+ecx-41404390h] - ror ecx, 9 - add ecx, edx - xor edi, ecx - add edi, [ebp+var_8] - lea eax, [edi+eax+289B7EC6h] - rol eax, 4 - add eax, ecx - mov edi, edx - xor edi, ecx - xor edi, eax - add edi, [ebp+var_30] - lea esi, [edi+esi-155ED806h] - rol esi, 0Bh - add esi, eax - mov edi, esi - xor edi, ecx - xor edi, eax - add edi, [ebp+var_1C] - lea edi, [edi+edx-2B10CF7Bh] - rol edi, 10h - add edi, esi - mov edx, esi - xor edx, edi - mov ebx, edx - xor ebx, eax - add ebx, [ebp+var_20] - lea ecx, [ebx+ecx+4881D05h] - ror ecx, 9 - add ecx, edi - xor edx, ecx - add edx, [ebp+var_0C] - lea eax, [edx+eax-262B2FC7h] - rol eax, 4 - add eax, ecx - mov edx, edi - xor edx, ecx - xor edx, eax - add edx, [ebp+var_38] - lea edx, [edx+esi-1924661Bh] - rol edx, 0Bh - add edx, eax - mov esi, edx - xor esi, ecx - xor esi, eax - add esi, [ebp+var_4] - mov ebx, edx - lea esi, [esi+edi+1FA27CF8h] - mov edi, [ebp+var_40] - rol esi, 10h - add esi, edx - xor ebx, esi - xor ebx, eax - add ebx, edi - lea ecx, [ebx+ecx-3B53A99Bh] - ror ecx, 9 - add ecx, esi - mov ebx, edx - not ebx - or ebx, ecx - xor ebx, esi - add ebx, [ebp+var_30] - lea eax, [ebx+eax-0BD6DDBCh] - rol eax, 6 - add eax, ecx - mov ebx, esi - not ebx - or ebx, eax - xor ebx, ecx - add ebx, [ebp+var_3C] - lea edx, [ebx+edx+432AFF97h] - rol edx, 0Ah - add edx, eax - mov ebx, ecx - not ebx - or ebx, edx - xor ebx, eax - add ebx, [ebp+var_28] - lea esi, [ebx+esi-546BDC59h] - rol esi, 0Fh - add esi, edx - mov ebx, eax - not ebx - or ebx, esi - xor ebx, edx - add ebx, [ebp+var_34] - lea ecx, [ebx+ecx-36C5FC7h] - ror ecx, 0Bh - add ecx, esi - mov ebx, edx - not ebx - or ebx, ecx - xor ebx, esi - add ebx, [ebp+var_38] - lea eax, [ebx+eax+655B59C3h] - rol eax, 6 - add eax, ecx - mov ebx, esi - not ebx - or ebx, eax - xor ebx, ecx - add ebx, [ebp+var_1C] - lea edx, [ebx+edx-70F3336Eh] - rol edx, 0Ah - add edx, eax - mov ebx, ecx - not ebx - or ebx, edx - xor ebx, eax - add ebx, [ebp+var_24] - lea esi, [ebx+esi-100B83h] - rol esi, 0Fh - add esi, edx - mov ebx, eax - not ebx - or ebx, esi - xor ebx, edx - add ebx, [ebp+var_2C] - lea ecx, [ebx+ecx-7A7BA22Fh] - ror ecx, 0Bh - add ecx, esi - mov ebx, edx - not ebx - or ebx, ecx - xor ebx, esi - add ebx, [ebp+var_14] - lea eax, [ebx+eax+6FA87E4Fh] - rol eax, 6 - add eax, ecx - mov ebx, esi - not ebx - or ebx, eax - xor ebx, ecx - add ebx, [ebp+var_4] - lea edx, [ebx+edx-1D31920h] - rol edx, 0Ah - add edx, eax - mov ebx, ecx - not ebx - or ebx, edx - xor ebx, eax - add ebx, [ebp+var_20] - lea esi, [ebx+esi-5CFEBCECh] - rol esi, 0Fh - mov ebx, eax - add esi, edx - not ebx - or ebx, esi - xor ebx, edx - add ebx, [ebp+var_8] - lea ecx, [ebx+ecx+4E0811A1h] - ror ecx, 0Bh - add ecx, esi - mov ebx, edx - not ebx - or ebx, ecx - xor ebx, esi - add ebx, [ebp+var_10] - lea eax, [ebx+eax-8AC817Eh] - rol eax, 6 - add eax, ecx - mov ebx, esi - not ebx - or ebx, eax - xor ebx, ecx - add ebx, [ebp+var_18] - lea edx, [ebx+edx-42C50DCBh] - rol edx, 0Ah - add edx, eax - mov ebx, ecx - not ebx - or ebx, edx - xor ebx, eax - add ebx, edi - lea esi, [ebx+esi+2AD7D2BBh] - mov edi, eax - not edi - rol esi, 0Fh - add esi, edx - or edi, esi - xor edi, edx - add edi, [ebp+var_0C] - lea edi, [edi+ecx-14792C6Fh] - mov ecx, [ebp+DecryptBuffer] - mov ebx, [ecx+8] - add ebx, eax - mov eax, [ecx+10h] - ror edi, 0Bh - add edi, [ecx+0Ch] - add eax, esi - add edi, esi - mov [ecx+10h], eax - mov eax, [ecx+14h] - mov [ecx+0Ch], edi - pop edi - add eax, edx - pop esi - mov [ecx+8], ebx - mov [ecx+14h], eax - pop ebx - mov esp, ebp - pop ebp - retn 4 -SFileDecryptMpqHeader endp - -; --------------------------------------------------------------------------- - -; --------------------------------------------------------------------------- - -sub_6D00E0 proc near ; CODE XREF: sub_6D0210+59p - ; sub_6D0210+66p ... - -var_10 = dword ptr -10h -pbMpqHeader = dword ptr -4 -pMpqHeader = dword ptr 8 -dwSize = dword ptr 0Ch - - push ebp - mov ebp, esp - push ecx - mov eax, [ebp+pMpqHeader] ; EAX = MPQ Header - push ebx - mov ebx, [ebp+dwSize] ; EBX - size of MPQ Header - push esi - push edi - mov esi, ecx ; ESI - decryption buffer (6 DWORDs) - mov edi, [esi] - shr edi, 3 - and edi, 3Fh - mov [ebp+pbMpqHeader], eax - lea ecx, ds:0[ebx*8] ; ECX = sizeof header * 8 - test ebx, ebx - jbe loc_6D01F8 - add [esi], ecx - mov edx, ebx ; EDX = size of header - shr edx, 1Dh - add [esi+4], edx - mov edx, [esi+4] - cmp [esi], ecx - jnb short loc_6D011E - inc edx - mov [esi+4], edx - -loc_6D011E: ; CODE XREF: sub_6D00E0+38j - test edi, edi - jz short loc_6D0196 - lea eax, [edi+ebx] - cmp eax, 40h - jbe short loc_6D0136 - mov eax, 40h - sub eax, edi - mov [ebp+dwSize], eax - jmp short loc_6D013B -; --------------------------------------------------------------------------- - -loc_6D0136: ; CODE XREF: sub_6D00E0+48j - mov [ebp+dwSize], ebx - mov eax, ebx - -loc_6D013B: ; CODE XREF: sub_6D00E0+54j - mov edx, [ebp+pMpqHeader] - add edx, eax - lea ecx, [edi+esi+18h] - mov [ebp+pbMpqHeader], edx - cmp ecx, edx - jnb short loc_6D0163 - lea edx, [ecx+eax] - mov ecx, [ebp+pMpqHeader] - cmp edx, ecx - jbe short loc_6D0166 - push eax - push ecx - lea eax, [edi+esi+18h] - push eax - call _memmove - jmp short loc_6D0172 -; --------------------------------------------------------------------------- - -loc_6D0163: ; CODE XREF: sub_6D00E0+69j - mov ecx, [ebp+pMpqHeader] - -loc_6D0166: ; CODE XREF: sub_6D00E0+73j - push eax - push ecx - lea eax, [edi+esi+18h] - push eax - call _memcpy - -loc_6D0172: ; CODE XREF: sub_6D00E0+81j - mov eax, [ebp+dwSize] - lea ecx, [eax+edi] - add esp, 0Ch - cmp ecx, 40h - jl short loc_6D01F8 - mov edx, [ebp+pbMpqHeader] - sub ebx, eax - lea eax, [esi+18h] - push eax - mov ecx, esi - mov [ebp+pbMpqHeader], edx - call SFileDecryptMpqHeader - mov eax, [ebp+pbMpqHeader] - -loc_6D0196: ; CODE XREF: sub_6D00E0+40j - cmp ebx, 40h - jl short loc_6D01C4 - mov edi, ebx - shr edi, 6 - mov ecx, edi - neg ecx - shl ecx, 6 - add ebx, ecx - lea esp, [esp+0] - -loc_6D01B0: ; CODE XREF: sub_6D00E0+E2j - push eax - mov ecx, esi - call SFileDecryptMpqHeader - add [ebp+pbMpqHeader], 40h - sub edi, 1 - mov eax, [ebp+pbMpqHeader] - jnz short loc_6D01B0 - -loc_6D01C4: ; CODE XREF: sub_6D00E0+B9j - test ebx, ebx - jz short loc_6D01F8 - add esi, 18h - lea edx, [ebx+eax] - cmp esi, edx - jnb short loc_6D01ED - lea ecx, [esi+ebx] - cmp ecx, eax - jbe short loc_6D01ED - push ebx - push eax - push esi - call _memmove - add esp, 0Ch - pop edi - pop esi - pop ebx - mov esp, ebp - pop ebp - retn 8 -; --------------------------------------------------------------------------- - -loc_6D01ED: ; CODE XREF: sub_6D00E0+F0j - ; sub_6D00E0+F7j - push ebx - push eax - push esi - call _memcpy - add esp, 0Ch - -loc_6D01F8: ; CODE XREF: sub_6D00E0+23j - ; sub_6D00E0+9Ej ... - pop edi - pop esi - pop ebx - mov esp, ebp - pop ebp - retn 8 -sub_6D00E0 endp - -aA_1: ; DATA XREF: sub_6249D0+68o - ; sub_6D0210+4Fo ... - dw 80h, 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - db 0 - -sub_6D0210 proc near ; CODE XREF: SFileVerifyMpqHeaderMD5+42p - ; sub_6D2D60+A9p ... - -var_8 = byte ptr -8 -var_6 = byte ptr -6 -var_5 = byte ptr -5 -var_4 = byte ptr -4 -var_3 = byte ptr -3 -var_2 = byte ptr -2 -var_1 = byte ptr -1 -arg_0 = dword ptr 8 - - push ebp - mov ebp, esp - sub esp, 8 - push esi - mov esi, ecx ; Source address - mov eax, [esi] - mov ecx, eax - shr ecx, 8 - mov [ebp-7], cl - mov ecx, eax - shr ecx, 18h - mov [ebp+var_5], cl - mov ecx, [esi+4] - mov edx, eax - shr edx, 10h - mov [ebp+var_6], dl - mov edx, ecx - mov [ebp+var_4], cl - shr edx, 8 - mov [ebp+var_3], dl - mov edx, ecx - shr ecx, 18h - mov [ebp+var_1], cl - mov [ebp+var_8], al - shr eax, 3 - mov ecx, 0FFFFFFF7h - sub ecx, eax - and ecx, 3Fh - push edi - inc ecx - push ecx - shr edx, 10h - push offset aA_1 ; "" - mov ecx, esi - mov [ebp+var_2], dl - call sub_6D00E0 - push 8 - lea edx, [ebp+var_8] - push edx - mov ecx, esi - call sub_6D00E0 - mov edi, [ebp+arg_0] - xor eax, eax - -loc_6D0280: ; CODE XREF: sub_6D0210+8Ej - mov ecx, eax - and ecx, 3 - add ecx, ecx - mov edx, eax - sar edx, 2 - mov edx, [esi+edx*4+8] - add ecx, ecx - add ecx, ecx - shr edx, cl - inc eax - cmp eax, 10h - mov [eax+edi-1], dl - jl short loc_6D0280 - pop edi - pop esi - mov esp, ebp - pop ebp - retn 4 -sub_6D0210 endp - - -sub_6CEBE0 proc near ; CODE XREF: sub_4A72D0+49p - ; SFileVerifyMpqHeaderMD5+51p ... - -var_4 = dword ptr -4 -arg_0 = dword ptr 8 - - push ebp - mov ebp, esp - mov edx, [ebp+arg_0] - mov eax, 10h - push esi - lea esp, [esp+0] - -loc_6CEBF0: ; CODE XREF: sub_6CEBE0+22j - mov esi, [ecx] - cmp esi, [edx] - jnz short loc_6CEC14 - sub eax, 4 - add edx, 4 - add ecx, 4 - cmp eax, 4 - jnb short loc_6CEBF0 - xor eax, eax - xor edx, edx - test eax, eax - setz dl - mov al, dl - pop esi - pop ebp - retn 4 -; --------------------------------------------------------------------------- - -loc_6CEC14: ; CODE XREF: sub_6CEBE0+14j - movzx eax, byte ptr [ecx] - movzx esi, byte ptr [edx] - sub eax, esi - jnz short loc_6CEC40 - movzx eax, byte ptr [ecx+1] - movzx esi, byte ptr [edx+1] - sub eax, esi - jnz short loc_6CEC40 - movzx eax, byte ptr [ecx+2] - movzx esi, byte ptr [edx+2] - sub eax, esi - jnz short loc_6CEC40 - movzx eax, byte ptr [ecx+3] - movzx ecx, byte ptr [edx+3] - sub eax, ecx - -loc_6CEC40: ; CODE XREF: sub_6CEBE0+3Cj - ; sub_6CEBE0+48j ... - sar eax, 1Fh - or eax, 1 - xor edx, edx - test eax, eax - setz dl - mov al, dl - pop esi - pop ebp - retn 4 -sub_6CEBE0 endp - -SFileVerifyMpqHeaderMD5 proc near ; CODE XREF: SFileVerifyMpqHeader+6Bp - -var_68 = dword ptr -68h -var_64 = dword ptr -64h -var_60 = dword ptr -60h -var_5C = dword ptr -5Ch -var_58 = dword ptr -58h -var_54 = dword ptr -54h -var_10 = dword ptr -10h - - push ebp - mov ebp, esp - sub esp, 68h - push esi - mov esi, eax ; ESI = pointer to MPQ Header - xor eax, eax - push 0C0h - push esi - lea ecx, [ebp+var_68] - mov [ebp+var_64], eax - mov [ebp+var_68], eax - mov [ebp+var_60], 67452301h - mov [ebp+var_5C], 0EFCDAB89h - mov [ebp+var_58], 98BADCFEh - mov [ebp+var_54], 10325476h - call sub_6D00E0 - lea eax, [ebp+var_10] - push eax - lea ecx, [ebp+var_68] - call sub_6D0210 - add esi, 0C0h - push esi - lea ecx, [ebp+var_10] - call sub_6CEBE0 - pop esi - mov esp, ebp - pop ebp - retn -SFileVerifyMpqHeaderMD5 endp - - -_wow_SFileVerifyMpqHeaderMD5 proc - push ebp - mov ebp, esp - mov eax, [ebp+8] - call SFileVerifyMpqHeaderMD5 - mov esp, ebp - pop ebp - retn -_wow_SFileVerifyMpqHeaderMD5 endp - -END diff --git a/dep/StormLib/test/x86_starcraft_lzma.asm b/dep/StormLib/test/x86_starcraft_lzma.asm deleted file mode 100644 index bb13200b43..0000000000 --- a/dep/StormLib/test/x86_starcraft_lzma.asm +++ /dev/null @@ -1,11066 +0,0 @@ -; -; LZMA compression code ripped from Starctaft II BEta -; Used while StormLib's LZMA implementation was tested against Starcraft compression -; Not used in StormLib. -; - -.686P -.MODEL FLAT -ASSUME FS: NOTHING - -.DATA - -;--------------------------------------------------------------------------- -; Data - -byte_4CB248 db 0, 0Bh, 0Bh, 0Bh ; indirect table for switch statement - db 0Bh, 0Bh, 0Bh, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 1, 2, 3, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 4, 5, 6, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 7, 0Bh, 0Bh, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 8, 9, 0Bh, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 0Bh, 0Bh, 0Bh, 0Bh - db 0Ah - align 10h - -dword_544960 dd 0BB40E64Eh ; DATA XREF: sub_401C72+B -dword_553598 dd ? ; DATA XREF: sub_4CB8A0+9 - -off_50CD3C dd offset sub_4A0605 ; DATA XREF: sub_4A05F7+1o - ; .data:off_52A004o ... -dword_50CD40 dd 0E06D7363h ; DATA XREF: _CxxThrowException(x,x)+Eo - dd 1 - dd 0 - dd 0 - dd 3 - dd 19930520h - dd 0 - dd 0 - -; --------------------------------------------------------------------------- - -off_546E20 dd offset sub_4CDD70 ; DATA XREF: sub_4D0270:loc_4D0324o -off_546E24 dd offset sub_4CDD90 ; DATA XREF: sub_4CF810+53r -off_546E28 dd offset off_50CD3C ; DATA XREF: .rdata:off_51B888o - dd 0 - db '.?AUISequentialInStream@@',0 - db 0 - db 0 -off_546E4C dd offset off_50CD3C ; DATA XREF: .rdata:off_51B8C0o - dd 0 - db '.?AUISequentialOutStream@@',0 - db 0 -off_546E70 dd offset off_50CD3C ; DATA XREF: .rdata:off_51B8F8o - dd 0 - db '.?AUICompressCoder@@',0 - db 0 - db 0 - db 0 -off_546E90 dd offset off_50CD3C ; DATA XREF: .rdata:off_51B944o - dd 0 - db '.?AUICompressSetOutStream@@',0 -off_546EB4 dd offset off_50CD3C ; DATA XREF: .rdata:0051B96Co - dd 0 - db '.?AUICompressSetCoderProperties@@',0 - db 0 - db 0 -off_546EE0 dd offset off_50CD3C ; DATA XREF: .rdata:0051B9B8o - dd 0 - db '.?AUICompressWriteCoderProperties@@',0 - dd offset off_50CD3C - dd 0 - db '.?AUCSystemException@@',0 - db 0 - dd offset off_50CD3C - dd 0 - db '.?AUCOutBufferException@@',0 - db 0 - db 0 -off_546F50 dd offset off_50CD3C ; DATA XREF: .rdata:0051BA04o - dd 0 - db '.?AVCInStreamMemory@@',0 - db 0 - db 0 -off_546F70 dd offset off_50CD3C ; DATA XREF: .rdata:off_51BA4Co - dd 0 - db '.?AVCMyUnknownImp@@',0 -off_546F8C dd offset off_50CD3C ; DATA XREF: .rdata:0051BAA8o - dd 0 - db '.?AVCOutStreamMemory@@',0 - db 0 -off_546FAC dd offset off_50CD3C ; DATA XREF: .rdata:0051BAFCo - dd 0 - db '.?AVCEncoder@NLZMA@NCompress@@',0 - db 0 -off_546FD4 dd offset off_50CD3C ; DATA XREF: .rdata:off_51BBECo - dd 0 - db '.?AVCBaseState@NLZMA@NCompress@@',0 - db 0 - db 0 - db 0 - -; --------------------------------------------------------------------------- - -dword_51B960 dd 0 ; DATA XREF: .rdata:00517A64o - dd 0 - dd 0 - dd offset off_546EB4 - dd 0 ; offset dword_51B974 - -dword_51B9AC dd 0 ; DATA XREF: .rdata:00517A78o - dd 0 - dd 0 - dd offset off_546EE0 - dd 0 ;offset dword_51B9C0 - -dword_51B9F8 dd 0 ; DATA XREF: .rdata:00517A8Co - dd 0 - dd 0 - dd offset off_546F50 - dd 0 ; offset dword_51BA0C - -dword_51BA9C dd 0 ; DATA XREF: .rdata:00517AA0o - dd 0 - dd 0 - dd offset off_546F8C - dd 0 ; offset dword_51BAB0 - -dword_51BAF0 dd 0 ; DATA XREF: .rdata:00517AF8o - dd 0 - dd 0 - dd offset off_546FAC - dd 0 ; offset dword_51BB04 - -dword_51BC58 dd 0 ; DATA XREF: .rdata:00517AE0o - dd 4 - dd 0 - dd offset off_546FAC - dd 0 ; offset dword_51BB04 - -dword_51BC6C dd 0 ; DATA XREF: .rdata:00517ACCo - dd 8 - dd 0 - dd offset off_546FAC - dd 0 ; offset dword_51BB04 - -dword_51BC80 dd 0 ; DATA XREF: .rdata:00517AB8o - dd 0Ch - dd 0 - dd offset off_546FAC - dd 0 ; offset dword_51BB04 - -; --------------------------------------------------------------------------- - -dword_512730 dd 0 ; DATA XREF: sub_48DB4D+B9o -dword_512734 dd 0 ; DATA XREF: CCmdTarget::GetInterface(void const *)+2Dr -dword_512738 dd 0C0h ; DATA XREF: CCmdTarget::GetInterface(void const *)+38r -dword_51273C dd 46000000h ; DATA XREF: CCmdTarget::GetInterface(void const *)+43r - -dword_5535A0 dd ? ; DATA XREF: sub_4CF900:loc_4CF94Co -dword_5535A4 dd ? ; DATA XREF: .text:004DAF37w -dword_5535A8 dd ? ; DATA XREF: .text:004DAF3Cw -dword_5535AC dd ? ; DATA XREF: .text:004DAF41w - -dword_5535B0 dd ? ; DATA XREF: sub_4CF900+5Eo -dword_5535B4 dd ? ; DATA XREF: .text:004DAF17w -dword_5535B8 dd ? ; DATA XREF: .text:004DAF1Cw -dword_5535BC dd ? ; DATA XREF: .text:004DAF21w - -dword_5535C0 dd ? ; DATA XREF: sub_4CF900:loc_4CF98Fo -dword_5535C4 dd ? ; DATA XREF: .text:004DAEF7w -dword_5535C8 dd ? ; DATA XREF: .text:004DAEFCw -dword_5535CC dd ? ; DATA XREF: .text:004DAF01w - - -dword_526DD0 dd 0 ; DATA XREF: .text:loc_4CF6CBo - dd 0 - dd 0 - dd 0 ; offset dword_526DE0 - -kLiteralNextStates db 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5 -kMatchNextStates db 7, 7, 7, 7, 7, 7, 7, 0Ah, 0Ah, 0Ah, 0Ah, 0Ah -kRepNextStates db 8, 8, 8, 8, 8, 8, 8, 0Bh, 0Bh, 0Bh, 0Bh, 0Bh -kShortRepNextStates db 9, 9, 9, 9, 9, 9, 9, 0Bh, 0Bh, 0Bh, 0Bh, 0Bh - -dword_550998 dd 200h dup(?) -dword_551198 dd 800h dup(?) -dword_553198 dd 100h dup(?) - -ICompressSetOutStream_vftable dd offset __purecall ; DATA XREF: NCompress_NLZMA_CEncoder_CEncoder+2o - dd offset __purecall - dd offset __purecall - dd offset __purecall - dd offset __purecall - dd offset dword_51B960 -ICompressSetCoderProperties_vftable dd offset __purecall ; DATA XREF: NCompress_NLZMA_CEncoder_CEncoder+9o - dd offset __purecall - dd offset __purecall - dd offset __purecall - dd offset dword_51B9AC -ICompressWriteCoderProperties_vftable dd offset __purecall ; DATA XREF: NCompress_NLZMA_CEncoder_CEncoder+10o - dd offset __purecall - dd offset __purecall - dd offset __purecall - dd offset dword_51B9F8 -off_517A90 dd offset Interface1_QueryInterface - dd offset Interface1_AddRef - dd offset Interface1_Release - dd offset sub_4CCB20 - dd offset dword_51BA9C -off_517AA4 dd offset Interface1_QueryInterface ; DATA XREF: sub_4CF610+Eo - dd offset Interface1_AddRef - dd offset Interface2_Release - dd offset sub_4CCB90 - dd offset sub_4CF590 - dd offset dword_51BC80 -NCompress_NLZMA_CEncoder_vftable_ICompressWriteCoderProperties dd offset sub_4CFA50 ; DATA XREF: sub_4CF810+3Do - dd offset sub_4CFA10 - dd offset sub_4CFA70 - dd offset sub_4CDDB0 - dd offset dword_51BC6C -NCompress_NLZMA_CEncoder_vftable_ICompressSetCoderProperties dd offset ICompressSetCoderProperties_QueryInterface ; DATA XREF: sub_4CF810+36o - dd offset ICompressSetCoderProperties_AddRef - dd offset ICompressSetCoderProperties_Release - dd offset ICompressSetCoderProperties_SetCoderProperties - dd offset dword_51BC58 -NCompress_NLZMA_CEncoder_vftable_ICompressSetOutStream dd offset sub_4CFA20 ; DATA XREF: sub_4CF810+2Fo - dd offset sub_4CFA80 - dd offset sub_4CFA40 - dd offset sub_4CFA90 - dd offset sub_4CDE70 - dd offset dword_51BAF0 -NCompress_NLZMA_CEncoder_vftable dd offset sub_4CF900 ; DATA XREF: sub_4CF810+29o - dd offset sub_4CF9D0 - dd offset sub_4CF9E0 - dd offset CEncoder_Code - dd offset sub_4D0250 - -.CODE - -extrn _operator_new:PROC -extrn __allshr: PROC -extrn _free: PROC -extrn _memcmp: PROC -extrn _memcpy: PROC -extrn _memset: PROC -extrn ___report_gsfailure: PROC -extrn __purecall: PROC -VirtualAlloc PROTO STDCALL :DWORD,:DWORD,:DWORD,:DWORD -VirtualFree PROTO STDCALL :DWORD,:DWORD,:DWORD - -; --------------------------------------------------------------------------- - -; void __cdecl j__free(void *Memory) -; 004843B8 -j__free proc near ; CODE XREF: sub_401048+16p - ; .text:004010CFp ... - jmp _free -j__free endp - - -; --------------------------------------------------------------------------- - -; int __cdecl unknown_libname_324(void *Buf1, void *Buf2) -unknown_libname_324 proc near -; sub_48B7BA - -Buf1 = dword ptr 4 -Buf2 = dword ptr 8 - - push 10h ; Size - push [esp+4+Buf2] ; Buf2 - push [esp+8+Buf1] ; Buf1 - call _memcmp - add esp, 0Ch - neg eax - sbb eax, eax - inc eax - retn -unknown_libname_324 endp - -; --------------------------------------------------------------------------- - -sub_4A05F7 proc near ; CODE XREF: sub_4A0605+3p - push ecx - mov dword ptr [ecx], offset off_50CD3C - call sub_4A98B8 - pop ecx - retn -sub_4A05F7 endp - -; --------------------------------------------------------------------------- - -; int __thiscall sub_4A0605(void *Memory, char) -sub_4A0605 proc near ; DATA XREF: .rdata:off_50CD3Co - -arg_0 = byte ptr 4 - - push esi - mov esi, ecx - call sub_4A05F7 - test [esp+4+arg_0], 1 - jz short loc_4A061B - push esi ; Memory - call j__free - pop ecx - -loc_4A061B: ; CODE XREF: sub_4A0605+Dj - mov eax, esi - pop esi - retn 4 -sub_4A0605 endp - -; --------------------------------------------------------------------------- - -sub_4A0686 proc near ; CODE XREF: sub_401C72+49p - ; sub_40222F+15Cp ... - cmp ecx, dword_544960 - jnz short loc_4A0690 - retn -loc_4A0690: ; CODE XREF: sub_4A0686+6j - jmp ___report_gsfailure -sub_4A0686 endp - -; --------------------------------------------------------------------------- - -sub_4A98B8 proc near ; CODE XREF: sub_4A05F7+7p - - int 3 - retn -sub_4A98B8 endp - -; =============== S U B R O U T I N E ======================================= - - -sub_4CAEC0 proc near ; CODE XREF: CEncoder_GetOptimum+514p - -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 -arg_8 = dword ptr 0Ch - - push esi - push edi - mov edi, [esp+8+arg_4] - lea eax, [edi-2] - cmp eax, 4 - mov esi, ecx - jb short loc_4CAED5 - mov eax, 3 - -loc_4CAED5: ; CODE XREF: sub_4CAEC0+Ej - mov edx, [esp+8+arg_0] - cmp edx, 80h - jnb short loc_4CAEEF - shl eax, 7 - add eax, edx - mov ecx, [esi+eax*4+33314h] - jmp short loc_4CAF25 -; --------------------------------------------------------------------------- - -loc_4CAEEF: ; CODE XREF: sub_4CAEC0+1Fj - mov ecx, 7FFFFh - sub ecx, edx - sar ecx, 1Fh - push ebx - and ecx, 0Ch - add ecx, 6 - mov ebx, edx - shr ebx, cl - shl eax, 5 - add eax, ecx - and edx, 0Fh - movzx ebx, byte ptr dword_551198[ebx] - lea eax, [ebx+eax*2] - mov ecx, [esi+eax*4+32F14h] - add ecx, [esi+edx*4+33B14h] - pop ebx - -loc_4CAF25: ; CODE XREF: sub_4CAEC0+2Dj - mov edx, [esp+8+arg_8] - imul edx, 110h - add edx, edi - mov eax, [esi+edx*4+295B8h] - pop edi - add eax, ecx - pop esi - retn 0Ch -sub_4CAEC0 endp - -; --------------------------------------------------------------------------- - align 10h - -loc_4CAF40: ; CODE XREF: .text:004DAED5j - push ecx - push ebx - push ebp - push esi - mov ebp, 2 - mov bl, 2 - push edi - mov byte ptr dword_551198, 0 - mov byte ptr dword_551198+1, 1 - mov [esp+10h], bl - mov edi, ebp - -loc_4CAF60: ; CODE XREF: .text:004CAF96j - mov ecx, edi - shr ecx, 1 - sub ecx, 1 - mov esi, 1 - shl esi, cl - test esi, esi - jbe short loc_4CAF89 - mov ecx, [esp+10h] - push esi - lea eax, dword_551198[ebp] - push ecx - push eax - call _memset - add esp, 0Ch - add ebp, esi - -loc_4CAF89: ; CODE XREF: .text:004CAF70j - add bl, 1 - add edi, 1 - cmp bl, 1Ah - mov [esp+10h], bl - jb short loc_4CAF60 - pop edi - pop esi - pop ebp - pop ebx - pop ecx - retn -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -sub_4CAFA0 proc near ; CODE XREF: sub_4CAFC0+90p - -arg_0 = dword ptr 4 - - mov eax, [esp+arg_0] - lea ecx, [eax-61h] - cmp cx, 19h - ja short locret_4CAFB2 - add eax, 0FFE0h - -locret_4CAFB2: ; CODE XREF: sub_4CAFA0+Bj - retn -sub_4CAFA0 endp - -; --------------------------------------------------------------------------- - align 10h - -; =============== S U B R O U T I N E ======================================= - -sub_4CAFC0 proc near ; CODE XREF: ICompressSetCoderProperties_SetCoderProperties+A5p - movzx ecx, word ptr [eax] - lea edx, [ecx-61h] - cmp dx, 19h - ja short loc_4CAFD2 - add ecx, 0FFE0h - -loc_4CAFD2: ; CODE XREF: sub_4CAFC0+Aj - add eax, 2 - cmp cx, 48h - jnz short loc_4CB01B - movzx ecx, word ptr [eax] - lea edx, [ecx-61h] - cmp dx, 19h - ja short loc_4CAFED - add ecx, 0FFE0h - -loc_4CAFED: ; CODE XREF: sub_4CAFC0+25j - add eax, 2 - cmp cx, 43h - jz short loc_4CAFF9 - -loc_4CAFF6: ; CODE XREF: sub_4CAFC0+44j - ; sub_4CAFC0+4Bj ... - xor eax, eax - retn -; --------------------------------------------------------------------------- - -loc_4CAFF9: ; CODE XREF: sub_4CAFC0+34j - movzx ecx, word ptr [eax] - sub ecx, 30h - lea edx, [ecx-4] - test edx, edx - ja short loc_4CAFF6 - cmp word ptr [eax+2], 0 - jnz short loc_4CAFF6 - mov dword ptr [ebx], 0 - mov [edi], ecx - mov eax, 1 - retn -; --------------------------------------------------------------------------- - -loc_4CB01B: ; CODE XREF: sub_4CAFC0+19j - cmp cx, 42h - jnz short loc_4CAFF6 - movzx ecx, word ptr [eax] - lea edx, [ecx-61h] - cmp dx, 19h - ja short loc_4CB033 - add ecx, 0FFE0h - -loc_4CB033: ; CODE XREF: sub_4CAFC0+6Bj - add eax, 2 - cmp cx, 54h - jnz short loc_4CAFF6 - push esi - movzx esi, word ptr [eax] - sub esi, 30h - lea ecx, [esi-2] - cmp ecx, 2 - ja short loc_4CB06C - movzx eax, word ptr [eax+2] - push eax - call sub_4CAFA0 - add esp, 4 - test ax, ax - jnz short loc_4CB06C - mov dword ptr [ebx], 1 - mov [edi], esi - mov eax, 1 - pop esi - retn -; --------------------------------------------------------------------------- - -loc_4CB06C: ; CODE XREF: sub_4CAFC0+89j - ; sub_4CAFC0+9Bj - xor eax, eax - pop esi - retn -sub_4CAFC0 endp - - -ICompressSetCoderProperties_SetCoderProperties proc near ; DATA XREF: .rdata:00517ADCo - -var_4 = dword ptr -4 -pThis = dword ptr 4 -propIDs = dword ptr 8 -properties = dword ptr 0Ch -numProperties = dword ptr 10h - - push ecx - push ebx - push ebp - xor edx, edx - cmp [esp+0Ch+numProperties], edx - push esi - push edi - mov [esp+14h+var_4], edx - jbe loc_4CB20D - mov esi, [esp+14h+properties] - mov ebp, [esp+14h+pThis] - lea ecx, [ecx+0] - -loc_4CB090: ; CODE XREF: sub_4CB070+197j - mov eax, [esp+14h+propIDs] - mov eax, [eax+edx*4] - add eax, 0FFFFFC00h - cmp eax, 90h ; switch 145 cases - ja loc_4CB131 ; default - ; jumptable 004CB0AE cases 1-63,67-79,83-111,113-127,130-143 - movzx ecx, ds:byte_4CB248[eax] - jmp ds:off_4CB218[ecx*4] ; switch jump - -loc_4CB0B5: ; DATA XREF: .text:off_4CB218o - cmp word ptr [esi], 13h ; jumptable 004CB0AE case 80 - jnz short loc_4CB131 ; default - ; jumptable 004CB0AE cases 1-63,67-79,83-111,113-127,130-143 - mov eax, [esi+8] - lea ecx, [eax-5] - cmp ecx, 10Ch - ja short loc_4CB131 ; default - ; jumptable 004CB0AE cases 1-63,67-79,83-111,113-127,130-143 - mov [ebp+32EF0h], eax - jmp loc_4CB1F9 -; --------------------------------------------------------------------------- - -loc_4CB0D4: ; CODE XREF: sub_4CB070+3Ej - ; DATA XREF: .text:off_4CB218o - cmp word ptr [esi], 13h ; jumptable 004CB0AE case 82 - jnz short loc_4CB131 ; default - ; jumptable 004CB0AE cases 1-63,67-79,83-111,113-127,130-143 - mov eax, [esi+8] - mov [ebp+33B88h], eax - jmp loc_4CB1F9 -; --------------------------------------------------------------------------- - -loc_4CB0E8: ; CODE XREF: sub_4CB070+3Ej - ; DATA XREF: .text:off_4CB218o - cmp word ptr [esi], 13h ; jumptable 004CB0AE case 112 - jnz short loc_4CB131 ; default - ; jumptable 004CB0AE cases 1-63,67-79,83-111,113-127,130-143 - cmp dword ptr [esi+8], 0 - setz cl - mov [ebp+32EECh], cl - jmp loc_4CB1F9 -; --------------------------------------------------------------------------- - -loc_4CB100: ; CODE XREF: sub_4CB070+3Ej - ; DATA XREF: .text:off_4CB218o - cmp word ptr [esi], 8 ; jumptable 004CB0AE case 81 - jnz short loc_4CB131 ; default - ; jumptable 004CB0AE cases 1-63,67-79,83-111,113-127,130-143 - mov eax, [esi+8] - lea edi, [ebp+0C4h] - lea ebx, [ebp+0CCh] - call sub_4CAFC0 - test eax, eax - jz short loc_4CB131 ; default - ; jumptable 004CB0AE cases 1-63,67-79,83-111,113-127,130-143 - mov edx, [esp+14h+var_4] - jmp loc_4CB1F9 -; --------------------------------------------------------------------------- - -loc_4CB127: ; CODE XREF: sub_4CB070+3Ej - ; DATA XREF: .text:off_4CB218o - cmp word ptr [esi], 0Bh ; jumptable 004CB0AE case 128 - jz loc_4CB1F9 - -loc_4CB131: ; CODE XREF: sub_4CB070+31j - ; sub_4CB070+3Ej ... - pop edi ; default - ; jumptable 004CB0AE cases 1-63,67-79,83-111,113-127,130-143 - pop esi - pop ebp - mov eax, 80070057h - pop ebx - pop ecx - retn 10h -; --------------------------------------------------------------------------- - -loc_4CB13E: ; CODE XREF: sub_4CB070+3Ej - ; DATA XREF: .text:off_4CB218o - cmp word ptr [esi], 13h ; jumptable 004CB0AE case 129 - jnz short loc_4CB131 ; default - ; jumptable 004CB0AE cases 1-63,67-79,83-111,113-127,130-143 - jmp loc_4CB1F9 -; --------------------------------------------------------------------------- - -loc_4CB149: ; CODE XREF: sub_4CB070+3Ej - ; DATA XREF: .text:off_4CB218o - cmp word ptr [esi], 13h ; jumptable 004CB0AE case 0 - jnz short loc_4CB131 ; default - ; jumptable 004CB0AE cases 1-63,67-79,83-111,113-127,130-143 - mov eax, [esi+8] - lea ecx, [eax-1] - cmp ecx, 3FFFFFFFh - ja short loc_4CB131 ; default - ; jumptable 004CB0AE cases 1-63,67-79,83-111,113-127,130-143 - mov [ebp+33B64h], eax - xor ecx, ecx - -loc_4CB165: ; CODE XREF: sub_4CB070+106j - mov edi, 1 - shl edi, cl - cmp eax, edi - jbe short loc_4CB178 - add ecx, 1 - cmp ecx, 1Eh - jb short loc_4CB165 - -loc_4CB178: ; CODE XREF: sub_4CB070+FEj - lea eax, [ecx+ecx] - mov [ebp+33B50h], eax - jmp short loc_4CB1F9 -; --------------------------------------------------------------------------- - -loc_4CB183: ; CODE XREF: sub_4CB070+3Ej - ; DATA XREF: .text:off_4CB218o - cmp word ptr [esi], 13h ; jumptable 004CB0AE case 64 - jnz short loc_4CB131 ; default - ; jumptable 004CB0AE cases 1-63,67-79,83-111,113-127,130-143 - mov ecx, [esi+8] - cmp ecx, 4 - ja short loc_4CB131 ; default - ; jumptable 004CB0AE cases 1-63,67-79,83-111,113-127,130-143 - mov eax, 1 - shl eax, cl - mov [ebp+33B54h], ecx - sub eax, 1 - mov [ebp+33B58h], eax - jmp short loc_4CB1F9 -; --------------------------------------------------------------------------- - -loc_4CB1A9: ; CODE XREF: sub_4CB070+3Ej - ; DATA XREF: .text:off_4CB218o - cmp word ptr [esi], 13h ; jumptable 004CB0AE case 66 - jnz short loc_4CB131 ; default - ; jumptable 004CB0AE cases 1-63,67-79,83-111,113-127,130-143 - mov eax, [esi+8] - cmp eax, 4 - ja loc_4CB131 ; default - ; jumptable 004CB0AE cases 1-63,67-79,83-111,113-127,130-143 - mov [ebp+33B5Ch], eax - jmp short loc_4CB1F9 -; --------------------------------------------------------------------------- - -loc_4CB1C3: ; CODE XREF: sub_4CB070+3Ej - ; DATA XREF: .text:off_4CB218o - cmp word ptr [esi], 13h ; jumptable 004CB0AE case 65 - jnz loc_4CB131 ; default - ; jumptable 004CB0AE cases 1-63,67-79,83-111,113-127,130-143 - mov eax, [esi+8] - cmp eax, 8 - ja loc_4CB131 ; default - ; jumptable 004CB0AE cases 1-63,67-79,83-111,113-127,130-143 - mov [ebp+33B60h], eax - jmp short loc_4CB1F9 -; --------------------------------------------------------------------------- - -loc_4CB1E1: ; CODE XREF: sub_4CB070+3Ej - ; DATA XREF: .text:off_4CB218o - cmp word ptr [esi], 0Bh ; jumptable 004CB0AE case 144 - jnz loc_4CB131 ; default - ; jumptable 004CB0AE cases 1-63,67-79,83-111,113-127,130-143 - cmp word ptr [esi+8], 0FFFFh - setz cl - mov [ebp+33B8Ch], cl - -loc_4CB1F9: ; CODE XREF: sub_4CB070+5Fj - ; sub_4CB070+73j ... - add edx, 1 - add esi, 10h - cmp edx, [esp+14h+numProperties] - mov [esp+14h+var_4], edx - jb loc_4CB090 - -loc_4CB20D: ; CODE XREF: sub_4CB070+Fj - pop edi - pop esi - pop ebp - xor eax, eax - pop ebx - pop ecx - retn 10h - -off_4CB218 dd offset loc_4CB149, offset loc_4CB183, offset loc_4CB1C3 - dd offset loc_4CB1A9, offset loc_4CB0B5, offset loc_4CB100 ; jump table for switch statement - dd offset loc_4CB0D4, offset loc_4CB0E8, offset loc_4CB127 - dd offset loc_4CB13E, offset loc_4CB1E1, offset loc_4CB131 - -ICompressSetCoderProperties_SetCoderProperties endp - -; =============== S U B R O U T I N E ======================================= - -sub_4CB2E0 proc near ; CODE XREF: CEncoder_GetOptimum+1199p - -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 - - push ebx - push ebp - push esi - mov esi, [esp+0Ch+arg_4] - lea eax, [esi+esi*4] - lea edx, [ecx+eax*8] - mov [ecx+32F08h], esi - mov eax, [edx+100h] - mov ebx, [edx+104h] - push edi - -loc_4CB300: ; CODE XREF: sub_4CB2E0+A6j - lea edx, [esi+esi*4] - cmp byte ptr [ecx+edx*8+0F1h], 0 - lea edi, [ecx+edx*8] - jz short loc_4CB359 - lea edx, [eax+eax*4+1Eh] - lea edx, [ecx+edx*8] - mov dword ptr [edx+14h], 0FFFFFFFFh - mov byte ptr [edx+1], 0 - lea edx, [eax+eax*4] - lea edx, [ecx+edx*8] - lea ebp, [eax-1] - mov [edx+100h], ebp - cmp byte ptr [edi+0F2h], 0 - jz short loc_4CB359 - mov byte ptr [edx+0C9h], 0 - mov ebp, [edi+0F4h] - mov [edx+0D8h], ebp - mov edi, [edi+0F8h] - mov [edx+0DCh], edi - -loc_4CB359: ; CODE XREF: sub_4CB2E0+2Ej - ; sub_4CB2E0+58j - lea edx, [eax+eax*4] - add edx, edx - add edx, edx - add edx, edx - mov edi, eax - test edi, edi - mov eax, [edx+ecx+100h] - mov ebp, ebx - mov ebx, [edx+ecx+104h] - mov [edx+ecx+100h], esi - mov [edx+ecx+104h], ebp - mov esi, edi - jnz loc_4CB300 - mov eax, [ecx+104h] - mov edx, [esp+10h+arg_0] - pop edi - pop esi - mov [edx], eax - mov eax, [ecx+100h] - pop ebp - mov [ecx+32F0Ch], eax - pop ebx - retn 8 -sub_4CB2E0 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -sub_4CB3B0 proc near ; CODE XREF: CEncoder_GetOptimumFast+2Dp - ; CEncoder_GetOptimumFast+28Fp ... - -arg_0 = dword ptr 4 - - push ebx - push ebp - push esi - mov esi, ecx - mov ecx, [esi+80h] - mov edx, [esi+78h] - lea eax, [esi+32660h] - push eax - push ecx - xor ebx, ebx - call edx - mov ebp, [esp+14h+arg_0] - add esp, 8 - test eax, eax - mov [ebp+0], eax - jbe short loc_4CB446 - mov ebx, [esi+eax*4+32658h] - cmp ebx, [esi+32EF8h] - jnz short loc_4CB446 - mov eax, [esi+80h] - mov ecx, [esi+70h] - push edi - push eax - call ecx - mov edx, [esi+80h] - mov edi, eax - mov eax, [esi+74h] - push edx - add edi, 1 - call eax - mov ecx, [ebp+0] - mov ecx, [esi+ecx*4+3265Ch] - add esp, 8 - sub eax, 1 - add ecx, 1 - cmp edi, 111h - jbe short loc_4CB425 - mov edi, 111h - -loc_4CB425: ; CODE XREF: sub_4CB3B0+6Ej - mov edx, eax - sub edx, ecx - cmp ebx, edi - jnb short loc_4CB445 - mov ebp, edx - lea ecx, [eax+ebx] - sub ebp, eax - -loc_4CB434: ; CODE XREF: sub_4CB3B0+93j - mov dl, [ecx] - cmp dl, [ecx+ebp] - jnz short loc_4CB445 - add ebx, 1 - add ecx, 1 - cmp ebx, edi - jb short loc_4CB434 - -loc_4CB445: ; CODE XREF: sub_4CB3B0+7Bj - ; sub_4CB3B0+89j - pop edi - -loc_4CB446: ; CODE XREF: sub_4CB3B0+26j - ; sub_4CB3B0+35j - add dword ptr [esi+32F04h], 1 - pop esi - pop ebp - mov eax, ebx - pop ebx - retn 4 -sub_4CB3B0 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -CEncoder_GetOptimumFast proc near ; CODE XREF: CEncoder_CodeOneBlock+1F4p - -var_28 = dword ptr -28h -var_24 = dword ptr -24h -var_20 = dword ptr -20h -var_1C = dword ptr -1Ch -var_18 = dword ptr -18h -var_14 = dword ptr -14h -var_10 = dword ptr -10h -arg_0 = dword ptr 4 - - sub esp, 28h - push ebx - push esi - push edi - mov edi, ecx - mov eax, [edi+80h] - mov ecx, [edi+70h] - push eax - call ecx - add esp, 4 - cmp byte ptr [edi+32F10h], 0 - mov esi, eax - mov [esp+34h+var_24], esi - jnz short loc_4CB498 - lea edx, [esp+34h+var_20] - push edx - mov ecx, edi - call sub_4CB3B0 - mov ebx, [esp+34h+var_20] - jmp short loc_4CB4AF -; --------------------------------------------------------------------------- - -loc_4CB498: ; CODE XREF: CEncoder_GetOptimumFast+24j - mov ebx, [edi+32F00h] - mov eax, [edi+32EFCh] - mov [esp+34h+var_20], ebx - mov byte ptr [edi+32F10h], 0 - -loc_4CB4AF: ; CODE XREF: CEncoder_GetOptimumFast+36j - mov ecx, [edi+80h] - mov edx, [edi+74h] - push ecx - mov [esp+38h+var_28], eax - call edx - add esp, 4 - sub eax, 1 - cmp esi, 111h - jbe loc_4CB55E - mov [esp+34h+var_24], 111h - -loc_4CB4D9: ; CODE XREF: CEncoder_GetOptimumFast+101j - push ebp - xor ebp, ebp - lea ecx, [edi+14h] - mov [esp+38h+var_18], ebp - mov [esp+38h+var_14], ebp - mov [esp+38h+var_1C], ecx - jmp short loc_4CB4F0 -; --------------------------------------------------------------------------- - align 10h - -loc_4CB4F0: ; CODE XREF: CEncoder_GetOptimumFast+8Bj - ; CEncoder_GetOptimumFast+132j - mov edx, [esp+38h+var_1C] - mov ecx, eax - sub ecx, [edx] - mov dl, [eax] - sub ecx, 1 - cmp dl, [ecx] - jnz short loc_4CB57F - mov dl, [eax+1] - cmp dl, [ecx+1] - jnz short loc_4CB57F - mov esi, 2 - cmp [esp+38h+var_24], esi - jbe short loc_4CB537 - lea edx, [eax+2] - sub ecx, eax - lea esp, [esp+0] - -loc_4CB520: ; CODE XREF: CEncoder_GetOptimumFast+D1j - mov bl, [edx] - cmp bl, [ecx+edx] - jnz short loc_4CB533 - add esi, 1 - add edx, 1 - cmp esi, [esp+38h+var_24] - jb short loc_4CB520 - -loc_4CB533: ; CODE XREF: CEncoder_GetOptimumFast+C5j - mov ebx, [esp+38h+var_20] - -loc_4CB537: ; CODE XREF: CEncoder_GetOptimumFast+B2j - cmp esi, [edi+32EF8h] - jnb short loc_4CB5B9 - mov edx, [esp+38h+var_14] - lea ecx, ds:0[ebp*4] - mov [esp+ecx+38h+var_10], esi - cmp esi, [esp+edx+38h+var_10] - jbe short loc_4CB587 - mov [esp+38h+var_18], ebp - mov [esp+38h+var_14], ecx - jmp short loc_4CB587 -; --------------------------------------------------------------------------- - -loc_4CB55E: ; CODE XREF: CEncoder_GetOptimumFast+6Bj - cmp esi, 2 - jnb loc_4CB4D9 - mov eax, [esp+34h+arg_0] - pop edi - pop esi - mov dword ptr [eax], 0FFFFFFFFh - mov eax, 1 - pop ebx - add esp, 28h - retn 4 -; --------------------------------------------------------------------------- - -loc_4CB57F: ; CODE XREF: CEncoder_GetOptimumFast+9Fj - ; CEncoder_GetOptimumFast+A7j - mov [esp+ebp*4+38h+var_10], 0 - -loc_4CB587: ; CODE XREF: CEncoder_GetOptimumFast+F2j - ; CEncoder_GetOptimumFast+FCj - add [esp+38h+var_1C], 4 - add ebp, 1 - cmp ebp, 4 - jb loc_4CB4F0 - mov esi, [esp+38h+var_28] - cmp esi, [edi+32EF8h] - jb short loc_4CB5EC - mov eax, [edi+ebx*4+3265Ch] - mov ecx, [esp+38h+arg_0] - add eax, 4 - mov [ecx], eax - jmp loc_4CB69A -; --------------------------------------------------------------------------- - -loc_4CB5B9: ; CODE XREF: CEncoder_GetOptimumFast+DDj - mov eax, [esp+38h+arg_0] - mov [eax], ebp - lea eax, [esi-1] - test eax, eax - jz loc_4CB82E - mov ecx, [edi+80h] - mov edx, [edi+7Ch] - add [edi+32F04h], eax - push eax - push ecx - call edx - add esp, 8 - pop ebp - pop edi - mov eax, esi - pop esi - pop ebx - add esp, 28h - retn 4 -; --------------------------------------------------------------------------- - -loc_4CB5EC: ; CODE XREF: CEncoder_GetOptimumFast+142j - xor ebp, ebp - cmp esi, 2 - mov [esp+38h+var_20], ebp - jb short loc_4CB65A - cmp ebx, 2 - mov ecx, [edi+ebx*4+3265Ch] - mov [esp+38h+var_20], ecx - jbe short loc_4CB63F - lea eax, [edi+ebx*4+32654h] - mov edi, edi - -loc_4CB610: ; CODE XREF: CEncoder_GetOptimumFast+1DDj - mov edx, [eax-4] - add edx, 1 - cmp esi, edx - jnz short loc_4CB63F - mov ecx, [esp+38h+var_20] - shr ecx, 7 - cmp ecx, [eax] - jbe short loc_4CB63F - mov edx, [eax-4] - mov ecx, [eax] - sub eax, 8 - sub ebx, 2 - cmp ebx, 2 - mov [esp+38h+var_28], edx - mov [esp+38h+var_20], ecx - mov esi, edx - ja short loc_4CB610 - -loc_4CB63F: ; CODE XREF: CEncoder_GetOptimumFast+1A5j - ; CEncoder_GetOptimumFast+1B8j ... - cmp esi, 2 - jnz short loc_4CB656 - cmp [esp+38h+var_20], 80h - jb short loc_4CB656 - mov [esp+38h+var_28], 1 - -loc_4CB656: ; CODE XREF: CEncoder_GetOptimumFast+1E2j - ; CEncoder_GetOptimumFast+1ECj - mov ebp, [esp+38h+var_20] - -loc_4CB65A: ; CODE XREF: CEncoder_GetOptimumFast+195j - mov edx, [esp+38h+var_18] - mov esi, [esp+edx*4+38h+var_10] - cmp esi, 2 - mov ebx, [esp+38h+var_28] - jb short loc_4CB6C7 - lea eax, [esi+1] - cmp eax, ebx - jnb short loc_4CB690 - lea ecx, [esi+2] - cmp ecx, ebx - jb short loc_4CB681 - cmp ebp, 200h - ja short loc_4CB690 - -loc_4CB681: ; CODE XREF: CEncoder_GetOptimumFast+217j - lea edx, [esi+3] - cmp edx, ebx - jb short loc_4CB6C7 - cmp ebp, 8000h - jbe short loc_4CB6C7 - -loc_4CB690: ; CODE XREF: CEncoder_GetOptimumFast+210j - ; CEncoder_GetOptimumFast+21Fj - mov eax, [esp+38h+arg_0] - mov ecx, [esp+38h+var_18] - mov [eax], ecx - -loc_4CB69A: ; CODE XREF: CEncoder_GetOptimumFast+154j - lea eax, [esi-1] - test eax, eax - jz loc_4CB82E - mov edx, [edi+80h] - add [edi+32F04h], eax - push eax - mov eax, [edi+7Ch] - push edx - call eax - add esp, 8 - pop ebp - pop edi - mov eax, esi - pop esi - pop ebx - add esp, 28h - retn 4 -; --------------------------------------------------------------------------- - -loc_4CB6C7: ; CODE XREF: CEncoder_GetOptimumFast+209j - ; CEncoder_GetOptimumFast+226j ... - cmp ebx, 2 - jb short loc_4CB73E - cmp [esp+38h+var_24], 2 - jbe short loc_4CB73E - mov ecx, [edi+80h] - mov edx, [edi+70h] - push ecx - call edx - add esp, 4 - lea esi, [edi+32F00h] - push esi - mov ecx, edi - mov [esp+3Ch+var_24], eax - call sub_4CB3B0 - cmp eax, 2 - mov [edi+32EFCh], eax - jb short loc_4CB757 - cmp eax, ebx - mov ecx, [esi] - mov edx, [edi+ecx*4+3265Ch] - jb short loc_4CB710 - cmp edx, ebp - jb short loc_4CB737 - -loc_4CB710: ; CODE XREF: CEncoder_GetOptimumFast+2AAj - lea ecx, [ebx+1] - cmp eax, ecx - jnz short loc_4CB722 - mov esi, edx - shr esi, 7 - cmp esi, ebp - jbe short loc_4CB737 - cmp eax, ecx - -loc_4CB722: ; CODE XREF: CEncoder_GetOptimumFast+2B5j - ja short loc_4CB737 - add eax, 1 - cmp eax, ebx - jb short loc_4CB757 - cmp ebx, 3 - jb short loc_4CB757 - shr ebp, 7 - cmp ebp, edx - jbe short loc_4CB757 - -loc_4CB737: ; CODE XREF: CEncoder_GetOptimumFast+2AEj - ; CEncoder_GetOptimumFast+2BEj ... - mov byte ptr [edi+32F10h], 1 - -loc_4CB73E: ; CODE XREF: CEncoder_GetOptimumFast+26Aj - ; CEncoder_GetOptimumFast+271j - mov edx, [esp+38h+arg_0] - pop ebp - pop edi - pop esi - mov dword ptr [edx], 0FFFFFFFFh - mov eax, 1 - pop ebx - add esp, 28h - retn 4 -; --------------------------------------------------------------------------- - -loc_4CB757: ; CODE XREF: CEncoder_GetOptimumFast+29Dj - ; CEncoder_GetOptimumFast+2C9j ... - mov eax, [edi+80h] - mov ecx, [edi+74h] - push eax - call ecx - lea ecx, [edi+14h] - add esp, 4 - sub eax, 1 - mov [esp+38h+var_18], 0 - mov ebx, ecx - -loc_4CB776: ; CODE XREF: CEncoder_GetOptimumFast+39Aj - mov dl, [eax+1] - mov ecx, eax - sub ecx, [ebx] - sub ecx, 1 - cmp dl, [ecx+1] - jnz short loc_4CB7DD - mov dl, [eax+2] - cmp dl, [ecx+2] - lea ebp, [eax+2] - jnz short loc_4CB7DD - mov esi, 2 - cmp [esp+38h+var_24], esi - jbe short loc_4CB7B4 - mov edx, ebp - mov ebp, ecx - sub ebp, eax - -loc_4CB7A1: ; CODE XREF: CEncoder_GetOptimumFast+352j - mov cl, [edx] - cmp cl, [edx+ebp] - jnz short loc_4CB7B4 - add esi, 1 - add edx, 1 - cmp esi, [esp+38h+var_24] - jb short loc_4CB7A1 - -loc_4CB7B4: ; CODE XREF: CEncoder_GetOptimumFast+339j - ; CEncoder_GetOptimumFast+346j - add esi, 1 - cmp esi, [esp+38h+var_28] - jb short loc_4CB7E9 - mov eax, [esp+38h+arg_0] - pop ebp - mov byte ptr [edi+32F10h], 1 - pop edi - pop esi - mov dword ptr [eax], 0FFFFFFFFh - mov eax, 1 - pop ebx - add esp, 28h - retn 4 -; --------------------------------------------------------------------------- - -loc_4CB7DD: ; CODE XREF: CEncoder_GetOptimumFast+323j - ; CEncoder_GetOptimumFast+32Ej - mov edx, [esp+38h+var_18] - mov [esp+edx*4+38h+var_10], 0 - -loc_4CB7E9: ; CODE XREF: CEncoder_GetOptimumFast+35Bj - mov ecx, [esp+38h+var_18] - add ecx, 1 - add ebx, 4 - cmp ecx, 4 - mov [esp+38h+var_18], ecx - jb loc_4CB776 - mov esi, [esp+38h+var_28] - mov ecx, [esp+38h+var_20] - mov edx, [esp+38h+arg_0] - add ecx, 4 - lea eax, [esi-2] - test eax, eax - mov [edx], ecx - jz short loc_4CB82E - add [edi+32F04h], eax - mov ecx, [edi+7Ch] - push eax - mov eax, [edi+80h] - push eax - call ecx - add esp, 8 - -loc_4CB82E: ; CODE XREF: CEncoder_GetOptimumFast+164j - ; CEncoder_GetOptimumFast+23Fj ... - pop ebp - pop edi - mov eax, esi - pop esi - pop ebx - add esp, 28h - retn 4 -CEncoder_GetOptimumFast endp - -; =============== S U B R O U T I N E ======================================= - -sub_4CB8A0 proc near ; CODE XREF: sub_4CD940+37p - -arg_0 = dword ptr 4 - - push edi - mov edi, [esp+4+arg_0] - test edi, edi - jz short loc_4CB8EB - mov eax, dword_553598 - test eax, eax - jnz short loc_4CB8DD - push 0Ch ; Size - call _operator_new ; operator new(uint) - add esp, 4 - test eax, eax - jz short loc_4CB8D6 - mov dword ptr [eax], 0 - mov dword ptr [eax+4], 0 - mov dword ptr [eax+8], 0 - jmp short loc_4CB8D8 -; --------------------------------------------------------------------------- - -loc_4CB8D6: ; CODE XREF: sub_4CB8A0+1Ej - xor eax, eax - -loc_4CB8D8: ; CODE XREF: sub_4CB8A0+34j - mov dword_553598, eax - -loc_4CB8DD: ; CODE XREF: sub_4CB8A0+10j - mov eax, [eax] - test eax, eax - jz short loc_4CB8EB - push edi - call eax - add esp, 4 - pop edi - retn -; --------------------------------------------------------------------------- - -loc_4CB8EB: ; CODE XREF: sub_4CB8A0+7j - ; sub_4CB8A0+41j - xor eax, eax - pop edi - retn -sub_4CB8A0 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -sub_4CB8F0 proc near ; CODE XREF: sub_4CBA60+85p - -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 -arg_8 = dword ptr 0Ch - - push esi - mov esi, [esp+4+arg_0] - push edi - mov edi, [esi+44h] - add edi, [esi+40h] - add edi, [esp+8+arg_4] - cmp dword ptr [esi+4Ch], 0 - jz short loc_4CB911 - mov [esi+3Ch], edi - pop edi - mov eax, 1 - pop esi - retn -; --------------------------------------------------------------------------- - -loc_4CB911: ; CODE XREF: sub_4CB8F0+14j - mov eax, [esi+30h] - test eax, eax - jz short loc_4CB91D - cmp [esi+3Ch], edi - jz short loc_4CB93E - -loc_4CB91D: ; CODE XREF: sub_4CB8F0+26j - push ebx - mov ebx, [esp+0Ch+arg_8] - push eax - mov eax, [ebx+4] - call eax - mov dword ptr [esi+30h], 0 - mov [esi+3Ch], edi - mov ecx, [ebx] - push edi - call ecx - add esp, 8 - mov [esi+30h], eax - pop ebx - -loc_4CB93E: ; CODE XREF: sub_4CB8F0+2Bj - xor eax, eax - cmp [esi+30h], eax - pop edi - setnz al - pop esi - retn -sub_4CB8F0 endp - -; --------------------------------------------------------------------------- - align 10h - -loc_4CB950: ; DATA XREF: sub_4CCAA0+1Co - mov eax, [esp+4] - mov eax, [eax] - retn -; --------------------------------------------------------------------------- - align 10h - -loc_4CB960: ; DATA XREF: sub_4CCAA0+Eo - mov eax, [esp+4] - mov ecx, [eax] - mov edx, [esp+8] - mov al, [edx+ecx] - retn -; --------------------------------------------------------------------------- - align 10h -loc_4CB970: ; DATA XREF: sub_4CCAA0+15o - mov ecx, [esp+4] - -; =============== S U B R O U T I N E ======================================= - -; Attributes: library function - -; public: int __thiscall CRect::Height(void)const -?Height@CRect@@QBEHXZ proc near - mov eax, [ecx+0Ch] - sub eax, [ecx+4] - retn -?Height@CRect@@QBEHXZ endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -sub_4CB980 proc near ; CODE XREF: sub_4CBC80+46p - ; sub_4CBD50+61p - -arg_0 = dword ptr 4 - - push esi - mov esi, [esp+4+arg_0] - cmp dword ptr [esi+38h], 0 - jnz short loc_4CB9F0 - cmp dword ptr [esi+68h], 0 - jnz short loc_4CB9F0 - mov eax, [esi] - sub eax, [esi+4] - mov ecx, [esi+30h] - add eax, [esi+0Ch] - sub ecx, eax - add ecx, [esi+3Ch] - jz short loc_4CB9F0 - push edi - -loc_4CB9A4: ; CODE XREF: sub_4CB980+63j - mov edx, [esi+34h] - lea edi, [esp+8+arg_0] - push edi - push ecx - push eax - mov eax, [edx] - push edx - call eax - add esp, 10h - test eax, eax - mov [esi+68h], eax - jnz short loc_4CB9EF - mov eax, [esp+8+arg_0] - test eax, eax - jz short loc_4CB9E8 - add [esi+0Ch], eax - mov eax, [esi+0Ch] - sub eax, [esi+4] - cmp eax, [esi+44h] - ja short loc_4CB9EF - mov eax, [esi] - sub eax, [esi+4] - mov ecx, [esi+30h] - add eax, [esi+0Ch] - sub ecx, eax - add ecx, [esi+3Ch] - jnz short loc_4CB9A4 - pop edi - pop esi - retn -; --------------------------------------------------------------------------- - -loc_4CB9E8: ; CODE XREF: sub_4CB980+43j - mov dword ptr [esi+38h], 1 - -loc_4CB9EF: ; CODE XREF: sub_4CB980+3Bj - ; sub_4CB980+51j - pop edi - -loc_4CB9F0: ; CODE XREF: sub_4CB980+9j - ; sub_4CB980+Fj ... - pop esi - retn -sub_4CB980 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -sub_4CBA00 proc near ; CODE XREF: sub_4CBD50+58p - -arg_0 = dword ptr 4 - - push ebx - push ebp - push esi - push edi - mov edi, [esp+10h+arg_0] - mov esi, [edi+0Ch] - mov eax, [edi+40h] - sub esi, [edi+4] - mov ebx, [edi] - mov ebp, [edi+30h] - add esi, eax - sub ebx, eax - mov eax, dword_553598 - test eax, eax - jnz short loc_4CBA44 - push 0Ch ; Size - call _operator_new ; operator new(uint) - xor ecx, ecx - add esp, 4 - cmp eax, ecx - jz short loc_4CBA3D - mov [eax], ecx - mov [eax+4], ecx - mov [eax+8], ecx - jmp short loc_4CBA3F -; --------------------------------------------------------------------------- - -loc_4CBA3D: ; CODE XREF: sub_4CBA00+31j - xor eax, eax - -loc_4CBA3F: ; CODE XREF: sub_4CBA00+3Bj - mov dword_553598, eax - -loc_4CBA44: ; CODE XREF: sub_4CBA00+21j - mov eax, [eax+8] - test eax, eax - jz short loc_4CBA53 - push esi - push ebx - push ebp - call eax - add esp, 0Ch - -loc_4CBA53: ; CODE XREF: sub_4CBA00+49j - mov eax, [edi+30h] - add eax, [edi+40h] - mov [edi], eax - pop edi - pop esi - pop ebp - pop ebx - retn -sub_4CBA00 endp - -; =============== S U B R O U T I N E ======================================= - -sub_4CBA60 proc near ; CODE XREF: sub_4D0270+D2p - -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 -arg_8 = dword ptr 0Ch -arg_C = dword ptr 10h -arg_10 = dword ptr 14h -arg_14 = dword ptr 18h - - push ebx - push esi - push edi - mov edi, [esp+0Ch+arg_4] - cmp edi, 0C0000000h - jbe short loc_4CBAA2 - mov esi, [esp+0Ch+arg_0] - mov eax, [esi+20h] - mov edi, [esp+0Ch+arg_14] - mov ecx, [edi+4] - push eax - call ecx - xor ebx, ebx - add esp, 4 - cmp [esi+4Ch], ebx - mov [esi+20h], ebx - jnz short loc_4CBA9C - mov edx, [esi+30h] - mov eax, [edi+4] - push edx - call eax - add esp, 4 - mov [esi+30h], ebx - -loc_4CBA9C: ; CODE XREF: sub_4CBA60+2Bj - pop edi - pop esi - xor eax, eax - pop ebx - retn -; --------------------------------------------------------------------------- - -loc_4CBAA2: ; CODE XREF: sub_4CBA60+Dj - mov eax, edi - shr eax, 1 - cmp edi, 80000000h - jbe short loc_4CBAB3 - mov eax, edi - shr eax, 2 - -loc_4CBAB3: ; CODE XREF: sub_4CBA60+4Cj - mov ecx, [esp+0Ch+arg_8] - mov esi, [esp+0Ch+arg_0] - mov ebx, [esp+0Ch+arg_C] - lea edx, [edi+ecx+1] - push ebp - mov [esi+40h], edx - mov edx, [esp+10h+arg_10] - add ecx, ebx - lea ebp, [ebx+edx] - add ecx, edx - mov [esi+44h], ebp - mov ebp, [esp+10h+arg_14] - shr ecx, 1 - push ebp - lea eax, [ecx+eax+80000h] - push eax - push esi - call sub_4CB8F0 - add esp, 0Ch - test eax, eax - jz loc_4CBBEA - mov edx, [esi+48h] - mov [esi+1Ch], ebx - xor ebx, ebx - cmp edx, 2 - lea ebp, [edi+1] - mov [esi+5Ch], ebx - jnz short loc_4CBB0F - mov eax, 0FFFFh - jmp short loc_4CBB49 -; --------------------------------------------------------------------------- - -loc_4CBB0F: ; CODE XREF: sub_4CBA60+A6j - lea ecx, [edi-1] - mov eax, ecx - shr eax, 1 - or ecx, eax - mov eax, ecx - shr eax, 2 - or ecx, eax - mov eax, ecx - shr eax, 4 - or ecx, eax - mov eax, ecx - or eax, 1FFFE00h - shr eax, 8 - or eax, ecx - shr eax, 1 - cmp eax, 1000000h - jbe short loc_4CBB49 - cmp edx, 3 - jnz short loc_4CBB47 - mov eax, 0FFFFFFh - jmp short loc_4CBB49 -; --------------------------------------------------------------------------- - -loc_4CBB47: ; CODE XREF: sub_4CBA60+DEj - shr eax, 1 - -loc_4CBB49: ; CODE XREF: sub_4CBA60+ADj - ; sub_4CBA60+D9j ... - mov [esi+28h], eax - add eax, 1 - cmp edx, 2 - jbe short loc_4CBB5B - mov dword ptr [esi+5Ch], 400h - -loc_4CBB5B: ; CODE XREF: sub_4CBA60+F2j - cmp edx, 3 - jbe short loc_4CBB67 - add dword ptr [esi+5Ch], 10000h - -loc_4CBB67: ; CODE XREF: sub_4CBA60+FEj - cmp edx, 4 - jbe short loc_4CBB73 - add dword ptr [esi+5Ch], 100000h - -loc_4CBB73: ; CODE XREF: sub_4CBA60+10Aj - mov ecx, [esi+60h] - mov edx, [esi+5Ch] - add ecx, [esi+64h] - add eax, edx - cmp [esi+50h], ebx - mov [esi+58h], edi - mov [esi+60h], eax - mov [esi+18h], ebp - lea edx, [ebp+ebp+0] - jnz short loc_4CBB92 - mov edx, ebp - -loc_4CBB92: ; CODE XREF: sub_4CBA60+12Ej - lea edi, [eax+edx] - mov eax, [esi+20h] - cmp eax, ebx - mov [esi+64h], edx - jz short loc_4CBBA3 - cmp ecx, edi - jz short loc_4CBBE0 - -loc_4CBBA3: ; CODE XREF: sub_4CBA60+13Dj - mov ebp, [esp+10h+arg_14] - push eax - mov eax, [ebp+4] - call eax - lea eax, ds:0[edi*4] - mov ecx, eax - shr ecx, 2 - add esp, 4 - cmp ecx, edi - mov [esi+20h], ebx - jz short loc_4CBBC7 - xor eax, eax - jmp short loc_4CBBD0 -; --------------------------------------------------------------------------- - -loc_4CBBC7: ; CODE XREF: sub_4CBA60+161j - mov edx, [ebp+0] - push eax - call edx - add esp, 4 - -loc_4CBBD0: ; CODE XREF: sub_4CBA60+165j - cmp eax, ebx - mov [esi+20h], eax - jz short loc_4CBBEC - mov ecx, [esi+60h] - lea edx, [eax+ecx*4] - mov [esi+24h], edx - -loc_4CBBE0: ; CODE XREF: sub_4CBA60+141j - pop ebp - pop edi - pop esi - mov eax, 1 - pop ebx - retn -; --------------------------------------------------------------------------- - -loc_4CBBEA: ; CODE XREF: sub_4CBA60+8Fj - xor ebx, ebx - -loc_4CBBEC: ; CODE XREF: sub_4CBA60+175j - mov eax, [esi+20h] - mov ecx, [ebp+4] - push eax - call ecx - add esp, 4 - cmp [esi+4Ch], ebx - mov [esi+20h], ebx - jnz short loc_4CBC0F - mov edx, [esi+30h] - mov eax, [ebp+4] - push edx - call eax - add esp, 4 - mov [esi+30h], ebx - -loc_4CBC0F: ; CODE XREF: sub_4CBA60+19Ej - pop ebp - pop edi - pop esi - xor eax, eax - pop ebx - retn -sub_4CBA60 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -sub_4CBC20 proc near ; CODE XREF: sub_4CBD50+79p - -arg_0 = dword ptr 4 - - mov eax, [esp+arg_0] - mov edx, [eax+18h] - sub edx, [eax+14h] - push ebx - mov ebx, [eax+4] - or ecx, 0FFFFFFFFh - sub ecx, ebx - cmp edx, ecx - push esi - jnb short loc_4CBC3A - mov ecx, edx - -loc_4CBC3A: ; CODE XREF: sub_4CBC20+16j - mov esi, [eax+0Ch] - sub esi, ebx - push edi - mov edi, [eax+44h] - mov edx, esi - cmp edx, edi - ja short loc_4CBC54 - test edx, edx - jbe short loc_4CBC56 - mov edx, 1 - jmp short loc_4CBC56 -; --------------------------------------------------------------------------- - -loc_4CBC54: ; CODE XREF: sub_4CBC20+27j - sub edx, edi - -loc_4CBC56: ; CODE XREF: sub_4CBC20+2Bj - ; sub_4CBC20+32j - cmp edx, ecx - pop edi - jnb short loc_4CBC5D - mov ecx, edx - -loc_4CBC5D: ; CODE XREF: sub_4CBC20+39j - mov edx, [eax+1Ch] - cmp esi, edx - jbe short loc_4CBC66 - mov esi, edx - -loc_4CBC66: ; CODE XREF: sub_4CBC20+42j - add ebx, ecx - mov [eax+10h], esi - pop esi - mov [eax+8], ebx - pop ebx - retn -sub_4CBC20 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -sub_4CBC80 proc near ; DATA XREF: sub_4CCAA0+8o - -arg_0 = dword ptr 4 - - push ebx - push esi - mov esi, [esp+8+arg_0] - xor eax, eax - cmp [esi+60h], eax - jbe short loc_4CBCA2 - lea ecx, [ecx+0] - -loc_4CBC90: ; CODE XREF: sub_4CBC80+20j - mov ecx, [esi+20h] - mov dword ptr [ecx+eax*4], 0 - add eax, 1 - cmp eax, [esi+60h] - jb short loc_4CBC90 - -loc_4CBCA2: ; CODE XREF: sub_4CBC80+Bj - mov eax, [esi+18h] - mov edx, [esi+30h] - push esi - mov dword ptr [esi+14h], 0 - mov [esi], edx - mov [esi+0Ch], eax - mov [esi+4], eax - mov dword ptr [esi+68h], 0 - mov dword ptr [esi+38h], 0 - call sub_4CB980 - mov eax, [esi+18h] - mov ebx, [esi+4] - sub eax, [esi+14h] - or ecx, 0FFFFFFFFh - sub ecx, ebx - add esp, 4 - cmp eax, ecx - jnb short loc_4CBCE2 - mov ecx, eax - -loc_4CBCE2: ; CODE XREF: sub_4CBC80+5Ej - mov edx, [esi+0Ch] - sub edx, ebx - push edi - mov edi, [esi+44h] - mov eax, edx - cmp eax, edi - ja short loc_4CBCFC - test eax, eax - jbe short loc_4CBCFE - mov eax, 1 - jmp short loc_4CBCFE -; --------------------------------------------------------------------------- - -loc_4CBCFC: ; CODE XREF: sub_4CBC80+6Fj - sub eax, edi - -loc_4CBCFE: ; CODE XREF: sub_4CBC80+73j - ; sub_4CBC80+7Aj - cmp eax, ecx - pop edi - jnb short loc_4CBD05 - mov ecx, eax - -loc_4CBD05: ; CODE XREF: sub_4CBC80+81j - mov eax, edx - mov edx, [esi+1Ch] - cmp eax, edx - jbe short loc_4CBD10 - mov eax, edx - -loc_4CBD10: ; CODE XREF: sub_4CBC80+8Cj - add ebx, ecx - mov [esi+8], ebx - mov [esi+10h], eax - pop esi - pop ebx - retn -sub_4CBC80 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - - -sub_4CBD20 proc near ; CODE XREF: sub_4CBD50+26p - -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 -arg_8 = dword ptr 0Ch - - push edi - mov edi, [esp+4+arg_8] - xor eax, eax - test edi, edi - jbe short loc_4CBD4C - mov edx, [esp+4+arg_4] - push esi - mov esi, [esp+8+arg_0] - -loc_4CBD34: ; CODE XREF: sub_4CBD20+29j - mov ecx, [edx+eax*4] - cmp ecx, esi - ja short loc_4CBD3F - xor ecx, ecx - jmp short loc_4CBD41 -; --------------------------------------------------------------------------- - -loc_4CBD3F: ; CODE XREF: sub_4CBD20+19j - sub ecx, esi - -loc_4CBD41: ; CODE XREF: sub_4CBD20+1Dj - mov [edx+eax*4], ecx - add eax, 1 - cmp eax, edi - jb short loc_4CBD34 - pop esi - -loc_4CBD4C: ; CODE XREF: sub_4CBD20+9j - pop edi - retn -sub_4CBD20 endp - -; --------------------------------------------------------------------------- - align 10h - -; =============== S U B R O U T I N E ======================================= - -sub_4CBD50 proc near ; CODE XREF: .text:004CC1A1p - ; .text:004CC20Ap ... - -arg_0 = dword ptr 4 - - push esi - mov esi, [esp+4+arg_0] - cmp dword ptr [esi+4], 0FFFFFFFFh - jnz short loc_4CBD88 - mov eax, [esi+64h] - add eax, [esi+60h] - mov ecx, [esi+20h] - push edi - mov edi, 0FFFFFFFEh - sub edi, [esi+58h] - push eax - push ecx - and edi, 0FFFFFC00h - push edi - call sub_4CBD20 - sub [esi+8], edi - sub [esi+4], edi - add esp, 0Ch - sub [esi+0Ch], edi - pop edi - -loc_4CBD88: ; CODE XREF: sub_4CBD50+9j - cmp dword ptr [esi+38h], 0 - jnz short loc_4CBDB9 - mov edx, [esi+0Ch] - sub edx, [esi+4] - mov eax, [esi+44h] - cmp eax, edx - jnz short loc_4CBDB9 - mov ecx, [esi+3Ch] - add ecx, [esi+30h] - sub ecx, [esi] - cmp ecx, eax - ja short loc_4CBDB0 - push esi - call sub_4CBA00 - add esp, 4 - -loc_4CBDB0: ; CODE XREF: sub_4CBD50+55j - push esi - call sub_4CB980 - add esp, 4 - -loc_4CBDB9: ; CODE XREF: sub_4CBD50+3Cj - ; sub_4CBD50+49j - mov edx, [esi+14h] - cmp edx, [esi+18h] - jnz short loc_4CBDC8 - mov dword ptr [esi+14h], 0 - -loc_4CBDC8: ; CODE XREF: sub_4CBD50+6Fj - push esi - call sub_4CBC20 - add esp, 4 - pop esi - retn -sub_4CBD50 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -sub_4CBDE0 proc near ; CODE XREF: sub_4CC5C0+1B7p - -var_4 = dword ptr -4 -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 -arg_8 = dword ptr 0Ch -arg_C = dword ptr 10h -arg_10 = dword ptr 14h -arg_14 = dword ptr 18h -arg_18 = dword ptr 1Ch -arg_1C = dword ptr 20h -arg_20 = dword ptr 24h -arg_24 = dword ptr 28h - - push ecx - mov edx, [esp+4+arg_4] - mov ecx, [esp+4+arg_10] - mov eax, [esp+4+arg_14] - push ebx - mov [ecx+eax*4], edx - mov ecx, [esp+8+arg_8] - push ebp - push esi - sub ecx, edx - cmp [esp+10h+arg_1C], 0 - push edi - jz loc_4CBEAC - mov ebp, [esp+14h+arg_C] - mov ebx, [esp+14h+arg_24] - jmp short loc_4CBE14 -; --------------------------------------------------------------------------- - align 10h - -loc_4CBE10: ; CODE XREF: sub_4CBDE0+C6j - mov eax, [esp+14h+arg_14] - -loc_4CBE14: ; CODE XREF: sub_4CBDE0+2Dj - mov esi, [esp+14h+arg_18] - sub [esp+14h+arg_1C], 1 - cmp ecx, esi - jnb loc_4CBEAC - mov edx, ebp - sub edx, ecx - cmp eax, ecx - sbb edi, edi - and edi, esi - sub edi, ecx - add edi, eax - mov eax, [esp+14h+arg_10] - mov eax, [eax+edi*4] - mov [esp+14h+arg_4], eax - mov al, [edx+ebx] - cmp al, [ebx+ebp] - jnz short loc_4CBE99 - mov al, [edx] - cmp al, [ebp+0] - jnz short loc_4CBE99 - mov eax, [esp+14h+arg_0] - mov esi, 1 - cmp eax, esi - jz short loc_4CBE7A - sub edx, ebp - lea edi, [ebp+1] - mov [esp+14h+var_4], edx - jmp short loc_4CBE69 -; --------------------------------------------------------------------------- - -loc_4CBE65: ; CODE XREF: sub_4CBDE0+98j - mov edx, [esp+14h+var_4] - -loc_4CBE69: ; CODE XREF: sub_4CBDE0+83j - mov dl, [edx+edi] - cmp dl, [edi] - jnz short loc_4CBE7A - add esi, 1 - add edi, 1 - cmp esi, eax - jnz short loc_4CBE65 - -loc_4CBE7A: ; CODE XREF: sub_4CBDE0+78j - ; sub_4CBDE0+8Ej - cmp ebx, esi - jnb short loc_4CBE99 - mov edx, [esp+14h+arg_20] - mov [edx], esi - add edx, 4 - add ecx, 0FFFFFFFFh - mov [edx], ecx - add edx, 4 - cmp esi, eax - mov ebx, esi - mov [esp+14h+arg_20], edx - jz short loc_4CBEB6 - -loc_4CBE99: ; CODE XREF: sub_4CBDE0+64j - ; sub_4CBDE0+6Bj ... - mov ecx, [esp+14h+arg_8] - sub ecx, [esp+14h+arg_4] - cmp [esp+14h+arg_1C], 0 - jnz loc_4CBE10 - -loc_4CBEAC: ; CODE XREF: sub_4CBDE0+1Fj - ; sub_4CBDE0+3Fj - mov eax, [esp+14h+arg_20] - pop edi - pop esi - pop ebp - pop ebx - pop ecx - retn -; --------------------------------------------------------------------------- - -loc_4CBEB6: ; CODE XREF: sub_4CBDE0+B7j - pop edi - pop esi - pop ebp - mov eax, edx - pop ebx - pop ecx - retn -sub_4CBDE0 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -sub_4CBEC0 proc near ; CODE XREF: .text:004CC1E7p - ; sub_4CC220+14Ep ... - -var_10 = dword ptr -10h -var_C = dword ptr -0Ch -var_8 = dword ptr -8 -var_4 = dword ptr -4 -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 -arg_8 = dword ptr 0Ch -arg_C = dword ptr 10h -arg_10 = dword ptr 14h -arg_14 = dword ptr 18h -arg_18 = dword ptr 1Ch -arg_1C = dword ptr 20h -arg_20 = dword ptr 24h -arg_24 = dword ptr 28h - - sub esp, 10h - mov eax, [esp+10h+arg_10] - mov edx, [esp+10h+arg_8] - sub edx, [esp+10h+arg_4] - push ebx - push ebp - push esi - push edi - mov edi, [esp+20h+arg_14] - lea eax, [eax+edi*8] - lea ecx, [eax+4] - mov [esp+20h+var_C], ecx - xor ecx, ecx - xor ebx, ebx - cmp [esp+20h+arg_1C], ecx - mov ebp, eax - mov [esp+20h+var_10], ebp - mov [esp+20h+var_8], ecx - mov [esp+20h+var_4], ebx - jnz short loc_4CBF24 - -loc_4CBEF9: ; CODE XREF: sub_4CBEC0+6Fj - ; sub_4CBEC0+151j - mov edx, [esp+20h+var_C] - mov eax, [esp+20h+arg_20] - pop edi - pop esi - mov dword ptr [ebp+0], 0 - pop ebp - mov dword ptr [edx], 0 - pop ebx - add esp, 10h - retn -; --------------------------------------------------------------------------- - jmp short loc_4CBF20 -; --------------------------------------------------------------------------- - align 10h - -loc_4CBF20: ; CODE XREF: sub_4CBEC0+56j - ; sub_4CBEC0+14Bj - mov edi, [esp+20h+arg_14] - -loc_4CBF24: ; CODE XREF: sub_4CBEC0+37j - mov eax, [esp+20h+arg_18] - sub [esp+20h+arg_1C], 1 - cmp edx, eax - jnb short loc_4CBEF9 - cmp edi, edx - sbb esi, esi - and esi, eax - mov eax, [esp+20h+arg_10] - sub esi, edx - add esi, edi - lea eax, [eax+esi*8] - mov esi, [esp+20h+arg_C] - sub esi, edx - cmp ecx, ebx - jb short loc_4CBF4E - mov ecx, ebx - -loc_4CBF4E: ; CODE XREF: sub_4CBEC0+8Aj - mov bl, [ecx+esi] - mov edi, [esp+20h+arg_C] - cmp bl, [ecx+edi] - jnz short loc_4CBFB8 - add ecx, 1 - cmp ecx, [esp+20h+arg_0] - jz short loc_4CBF93 - mov bl, [ecx+esi] - cmp bl, [ecx+edi] - jnz short loc_4CBF93 - add ecx, 1 - cmp ecx, [esp+20h+arg_0] - jz short loc_4CBF93 - mov ebx, edi - mov ebp, esi - lea edi, [ecx+ebx] - sub ebp, ebx - lea ecx, [ecx+0] - -loc_4CBF80: ; CODE XREF: sub_4CBEC0+D1j - mov bl, [edi+ebp] - cmp bl, [edi] - jnz short loc_4CBF93 - add ecx, 1 - add edi, 1 - cmp ecx, [esp+20h+arg_0] - jnz short loc_4CBF80 - -loc_4CBF93: ; CODE XREF: sub_4CBEC0+A1j - ; sub_4CBEC0+A9j ... - cmp [esp+20h+arg_24], ecx - jnb short loc_4CBFB8 - mov edi, [esp+20h+arg_20] - mov [edi], ecx - add edi, 4 - add edx, 0FFFFFFFFh - mov [edi], edx - add edi, 4 - cmp ecx, [esp+20h+arg_0] - mov [esp+20h+arg_24], ecx - mov [esp+20h+arg_20], edi - jz short loc_4CC016 - -loc_4CBFB8: ; CODE XREF: sub_4CBEC0+98j - ; sub_4CBEC0+D7j - mov dl, [ecx+esi] - mov esi, [esp+20h+arg_C] - cmp dl, [ecx+esi] - mov edx, [esp+20h+arg_4] - jnb short loc_4CBFE4 - mov esi, [esp+20h+var_10] - lea ebp, [eax+4] - mov ebx, ecx - mov ecx, [esp+20h+var_8] - mov [esi], edx - mov eax, [ebp+0] - mov [esp+20h+var_10], ebp - mov [esp+20h+var_4], ebx - jmp short loc_4CBFFC -; --------------------------------------------------------------------------- - -loc_4CBFE4: ; CODE XREF: sub_4CBEC0+106j - mov esi, [esp+20h+var_C] - mov ebp, [esp+20h+var_10] - mov ebx, [esp+20h+var_4] - mov [esi], edx - mov [esp+20h+var_C], eax - mov eax, [eax] - mov [esp+20h+var_8], ecx - -loc_4CBFFC: ; CODE XREF: sub_4CBEC0+122j - mov edx, [esp+20h+arg_8] - sub edx, eax - cmp [esp+20h+arg_1C], 0 - mov [esp+20h+arg_4], eax - jnz loc_4CBF20 - jmp loc_4CBEF9 -; --------------------------------------------------------------------------- - -loc_4CC016: ; CODE XREF: sub_4CBEC0+F6j - mov ecx, [eax] - mov edx, [esp+20h+var_10] - mov [edx], ecx - mov eax, [eax+4] - mov ecx, [esp+20h+var_C] - mov [ecx], eax - mov eax, edi - pop edi - pop esi - pop ebp - pop ebx - add esp, 10h - retn -sub_4CBEC0 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - - -sub_4CC040 proc near ; CODE XREF: sub_4CC220+F8p - ; sub_4CC3B0+173p ... - -var_10 = dword ptr -10h -var_C = dword ptr -0Ch -var_8 = dword ptr -8 -var_4 = dword ptr -4 -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 -arg_8 = dword ptr 0Ch -arg_C = dword ptr 10h -arg_10 = dword ptr 14h -arg_14 = dword ptr 18h -arg_18 = dword ptr 1Ch -arg_1C = dword ptr 20h - - sub esp, 10h - mov eax, [esp+10h+arg_10] - mov edx, [esp+10h+arg_4] - push ebx - push ebp - push esi - mov esi, [esp+1Ch+arg_14] - lea eax, [eax+esi*8] - lea ecx, [eax+4] - push edi - mov edi, eax - mov eax, [esp+20h+arg_8] - xor ebx, ebx - xor ebp, ebp - sub eax, edx - cmp [esp+20h+arg_1C], ebx - mov [esp+20h+var_C], ecx - mov [esp+20h+var_10], edi - mov [esp+20h+var_8], ebx - mov [esp+20h+var_4], ebp - jz loc_4CC159 - nop - -loc_4CC080: ; CODE XREF: sub_4CC040+114j - sub [esp+20h+arg_1C], 1 - cmp eax, [esp+20h+arg_18] - jnb loc_4CC159 - cmp esi, eax - sbb ecx, ecx - and ecx, [esp+20h+arg_18] - sub ecx, eax - add ecx, esi - mov esi, [esp+20h+arg_10] - lea edi, [esi+ecx*8] - mov ecx, [esp+20h+arg_C] - sub ecx, eax - cmp ebx, ebp - mov eax, ebx - jb short loc_4CC0B0 - mov eax, ebp - -loc_4CC0B0: ; CODE XREF: sub_4CC040+6Cj - mov bl, [eax+ecx] - mov esi, [esp+20h+arg_C] - cmp bl, [eax+esi] - jnz short loc_4CC102 - add eax, 1 - cmp eax, [esp+20h+arg_0] - jz short loc_4CC0E3 - mov ebx, esi - mov ebp, ecx - lea esi, [eax+ebx] - sub ebp, ebx - mov edi, edi - -loc_4CC0D0: ; CODE XREF: sub_4CC040+A1j - mov bl, [esi+ebp] - cmp bl, [esi] - jnz short loc_4CC0FC - add eax, 1 - add esi, 1 - cmp eax, [esp+20h+arg_0] - jnz short loc_4CC0D0 - -loc_4CC0E3: ; CODE XREF: sub_4CC040+83j - ; sub_4CC040+C0j - mov edx, [edi] - mov eax, [esp+20h+var_10] - mov [eax], edx - mov ecx, [edi+4] - mov edx, [esp+20h+var_C] - pop edi - pop esi - pop ebp - mov [edx], ecx - pop ebx - add esp, 10h - retn -; --------------------------------------------------------------------------- - -loc_4CC0FC: ; CODE XREF: sub_4CC040+95j - cmp eax, [esp+20h+arg_0] - jz short loc_4CC0E3 - -loc_4CC102: ; CODE XREF: sub_4CC040+7Aj - mov cl, [eax+ecx] - mov esi, [esp+20h+arg_C] - cmp cl, [eax+esi] - jnb short loc_4CC129 - mov ecx, [esp+20h+var_10] - mov ebx, [esp+20h+var_8] - add edi, 4 - mov ebp, eax - mov [ecx], edx - mov edx, [edi] - mov [esp+20h+var_10], edi - mov [esp+20h+var_4], ebp - jmp short loc_4CC143 -; --------------------------------------------------------------------------- - -loc_4CC129: ; CODE XREF: sub_4CC040+CCj - mov ecx, [esp+20h+var_C] - mov ebp, [esp+20h+var_4] - mov [ecx], edx - mov edx, [edi] - mov ebx, eax - mov [esp+20h+var_C], edi - mov edi, [esp+20h+var_10] - mov [esp+20h+var_8], ebx - -loc_4CC143: ; CODE XREF: sub_4CC040+E7j - mov eax, [esp+20h+arg_8] - sub eax, edx - cmp [esp+20h+arg_1C], 0 - jz short loc_4CC159 - mov esi, [esp+20h+arg_14] - jmp loc_4CC080 -; --------------------------------------------------------------------------- - -loc_4CC159: ; CODE XREF: sub_4CC040+39j - ; sub_4CC040+49j ... - mov eax, [esp+20h+var_C] - mov dword ptr [edi], 0 - pop edi - pop esi - pop ebp - mov dword ptr [eax], 0 - pop ebx - add esp, 10h - retn -sub_4CC040 endp - -; --------------------------------------------------------------------------- - align 10h - -loc_4CC180: ; DATA XREF: sub_4CCAA0+40o - push esi - mov esi, [esp+8] - mov edx, [esi+10h] - cmp edx, 2 - jnb short loc_4CC1AD - add dword ptr [esi+4], 1 - mov eax, [esi+4] - add dword ptr [esi+14h], 1 - add dword ptr [esi], 1 - cmp eax, [esi+8] - jnz short loc_4CC1A9 - push esi - call sub_4CBD50 - add esp, 4 - -loc_4CC1A9: ; CODE XREF: .text:004CC19Ej - xor eax, eax - pop esi - retn -; --------------------------------------------------------------------------- - -loc_4CC1AD: ; CODE XREF: .text:004CC18Bj - mov eax, [esi] - xor ecx, ecx - mov ch, [eax+1] - push ebx - mov ebx, [esp+10h] - push edi - mov edi, [esi+4] - push 1 - push ebx - mov cl, [eax] - mov eax, ecx - mov ecx, [esi+20h] - lea eax, [ecx+eax*4] - mov ecx, [eax] - mov [eax], edi - mov eax, [esi+2Ch] - push eax - mov eax, [esi+18h] - push eax - mov eax, [esi+14h] - push eax - mov eax, [esi+24h] - push eax - mov eax, [esi] - push eax - mov eax, [esi+4] - push eax - push ecx - push edx - call sub_4CBEC0 - add dword ptr [esi+4], 1 - add dword ptr [esi+14h], 1 - add dword ptr [esi], 1 - mov edi, eax - mov eax, [esi+4] - sub edi, ebx - add esp, 28h - sar edi, 2 - cmp eax, [esi+8] - jnz short loc_4CC212 - push esi - call sub_4CBD50 - add esp, 4 - -loc_4CC212: ; CODE XREF: .text:004CC207j - mov eax, edi - pop edi - pop ebx - pop esi - retn - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -sub_4CC220 proc near ; DATA XREF: sub_4CCAA0+54o - -var_8 = dword ptr -8 -var_4 = dword ptr -4 -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 - - sub esp, 8 - push esi - mov esi, [esp+0Ch+arg_0] - mov eax, [esi+10h] - cmp eax, 3 - mov [esp+0Ch+arg_0], eax - jnb short loc_4CC259 - mov eax, 1 - add [esi+14h], eax - add [esi], eax - add [esi+4], eax - mov eax, [esi+4] - cmp eax, [esi+8] - jnz short loc_4CC252 - push esi - call sub_4CBD50 - add esp, 4 - -loc_4CC252: ; CODE XREF: sub_4CC220+27j - xor eax, eax - pop esi - add esp, 8 - retn -; --------------------------------------------------------------------------- - -loc_4CC259: ; CODE XREF: sub_4CC220+12j - push ebx - mov ebx, [esi+20h] - push ebp - xor edx, edx - push edi - mov edi, [esi] - mov dh, [edi+2] - movzx ecx, byte ptr [edi] - movzx eax, byte ptr [edi+1] - xor eax, dword_553198[ecx*4] - xor edx, eax - and edx, [esi+28h] - mov ecx, eax - mov ebp, [ebx+edx*4+1000h] - mov eax, [esi+4] - and ecx, 3FFh - sub eax, [ebx+ecx*4] - mov [esp+18h+var_8], ebp - mov ebp, [esi+4] - mov [ebx+edx*4+1000h], ebp - mov ebx, [esi+20h] - mov edx, [ebx+edx*4+1000h] - mov [ebx+ecx*4], edx - mov ebx, [esp+18h+arg_4] - xor ebp, ebp - cmp eax, [esi+18h] - mov ecx, 2 - jnb loc_4CC348 - mov edx, edi - sub edx, eax - mov [esp+18h+var_4], edx - mov dl, [edx] - cmp dl, [edi] - jnz short loc_4CC348 - mov edx, [esp+18h+arg_0] - cmp edx, ecx - jz short loc_4CC2EA - -loc_4CC2D3: ; CODE XREF: sub_4CC220+C4j - mov ebx, [esp+18h+var_4] - mov bl, [ebx+ecx] - cmp bl, [ecx+edi] - jnz short loc_4CC2E6 - add ecx, 1 - cmp ecx, edx - jnz short loc_4CC2D3 - -loc_4CC2E6: ; CODE XREF: sub_4CC220+BDj - mov ebx, [esp+18h+arg_4] - -loc_4CC2EA: ; CODE XREF: sub_4CC220+B1j - add eax, 0FFFFFFFFh - cmp ecx, edx - mov [ebx], ecx - mov [ebx+4], eax - mov ebp, 2 - jnz short loc_4CC34C - mov eax, [esi+2Ch] - mov ecx, [esi+18h] - push eax - mov eax, [esi+14h] - push ecx - mov ecx, [esi+24h] - push eax - mov eax, [esi] - push ecx - mov ecx, [esi+4] - push eax - mov eax, [esp+2Ch+var_8] - push ecx - push eax - push edx - call sub_4CC040 - mov eax, 1 - add [esi+14h], eax - add [esi], eax - add [esi+4], eax - mov eax, [esi+4] - add esp, 20h - cmp eax, [esi+8] - jnz short loc_4CC33E - push esi - call sub_4CBD50 - add esp, 4 - -loc_4CC33E: ; CODE XREF: sub_4CC220+113j - pop edi - mov eax, ebp - pop ebp - pop ebx - pop esi - add esp, 8 - retn -; --------------------------------------------------------------------------- - -loc_4CC348: ; CODE XREF: sub_4CC220+97j - ; sub_4CC220+A9j - mov edx, [esp+18h+arg_0] - -loc_4CC34C: ; CODE XREF: sub_4CC220+D9j - mov eax, [esi+2Ch] - push ecx - lea ecx, [ebx+ebp*4] - push ecx - mov ecx, [esi+18h] - push eax - mov eax, [esi+14h] - push ecx - mov ecx, [esi+24h] - push eax - mov eax, [esi] - push ecx - mov ecx, [esi+4] - push eax - mov eax, [esp+34h+var_8] - push ecx - push eax - push edx - call sub_4CBEC0 - mov edi, eax - mov eax, 1 - add [esi+14h], eax - add [esi], eax - add [esi+4], eax - mov eax, [esi+4] - sub edi, ebx - add esp, 28h - sar edi, 2 - cmp eax, [esi+8] - jnz short loc_4CC39B - push esi - call sub_4CBD50 - add esp, 4 - -loc_4CC39B: ; CODE XREF: sub_4CC220+170j - mov eax, edi - pop edi - pop ebp - pop ebx - pop esi - add esp, 8 - retn -sub_4CC220 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -sub_4CC3B0 proc near ; DATA XREF: sub_4CCAA0:loc_4CCB03o - -var_10 = dword ptr -10h -var_8 = dword ptr -8 -var_4 = dword ptr -4 -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 - - sub esp, 10h - push esi - mov esi, [esp+14h+arg_0] - mov eax, [esi+10h] - cmp eax, 4 - mov [esp+14h+arg_0], eax - jnb short loc_4CC3E9 - mov eax, 1 - add [esi+14h], eax - add [esi], eax - add [esi+4], eax - mov eax, [esi+4] - cmp eax, [esi+8] - jnz short loc_4CC3E2 - push esi - call sub_4CBD50 - add esp, 4 - -loc_4CC3E2: ; CODE XREF: sub_4CC3B0+27j - xor eax, eax - pop esi - add esp, 10h - retn -; --------------------------------------------------------------------------- - -loc_4CC3E9: ; CODE XREF: sub_4CC3B0+12j - push ebx - push ebp - push edi - mov edi, [esi] - movzx eax, byte ptr [edi] - movzx edx, byte ptr [edi+1] - xor edx, dword_553198[eax*4] - movzx eax, byte ptr [edi+2] - movzx ebp, byte ptr [edi+3] - mov ecx, eax - add eax, eax - add eax, eax - add eax, eax - xor eax, dword_553198[ebp*4] - mov ebp, [esi+4] - shl eax, 5 - shl ecx, 8 - xor eax, edx - and eax, [esi+28h] - xor ecx, edx - mov ebx, edx - mov edx, [esi+20h] - and ebx, 3FFh - sub ebp, [edx+ebx*4] - and ecx, 0FFFFh - mov [esp+20h+var_4], ebp - mov ebp, [esi+4] - sub ebp, [edx+ecx*4+1000h] - mov [esp+20h+var_10], ebp - mov ebp, [edx+eax*4+41000h] - mov [esp+20h+var_8], ebp - mov ebp, [esi+4] - mov [edx+eax*4+41000h], ebp - mov edx, [esi+20h] - mov eax, [edx+eax*4+41000h] - mov [edx+ecx*4+1000h], eax - mov eax, [esi+20h] - mov ecx, [eax+ecx*4+1000h] - mov [eax+ebx*4], ecx - mov ecx, [esp+20h+var_4] - mov ebx, [esp+20h+arg_4] - xor ebp, ebp - cmp ecx, [esi+18h] - mov eax, 1 - jnb short loc_4CC4A9 - mov edx, edi - sub edx, ecx - mov dl, [edx] - cmp dl, [edi] - jnz short loc_4CC4A9 - mov eax, 2 - lea edx, [ecx-1] - mov [ebx], eax - mov [ebx+4], edx - mov ebp, eax - -loc_4CC4A9: ; CODE XREF: sub_4CC3B0+DEj - ; sub_4CC3B0+E8j - mov edx, [esp+20h+var_10] - cmp ecx, edx - jz short loc_4CC4D5 - cmp edx, [esi+18h] - jnb short loc_4CC4D5 - mov edx, edi - sub edx, [esp+20h+var_10] - mov dl, [edx] - cmp dl, [edi] - jnz short loc_4CC4D5 - mov ecx, [esp+20h+var_10] - lea edx, [ecx-1] - mov [ebx+ebp*4+4], edx - mov eax, 3 - add ebp, 2 - -loc_4CC4D5: ; CODE XREF: sub_4CC3B0+FFj - ; sub_4CC3B0+104j ... - test ebp, ebp - mov edx, [esp+20h+arg_0] - jz short loc_4CC553 - cmp eax, edx - jz short loc_4CC4FE - mov edx, eax - sub edx, ecx - add edx, edi - -loc_4CC4E7: ; CODE XREF: sub_4CC3B0+148j - mov cl, [edx] - cmp cl, [eax+edi] - jnz short loc_4CC4FA - add eax, 1 - add edx, 1 - cmp eax, [esp+20h+arg_0] - jnz short loc_4CC4E7 - -loc_4CC4FA: ; CODE XREF: sub_4CC3B0+13Cj - mov edx, [esp+20h+arg_0] - -loc_4CC4FE: ; CODE XREF: sub_4CC3B0+12Fj - cmp eax, edx - mov [ebx+ebp*4-8], eax - jnz short loc_4CC553 - mov eax, [esi+2Ch] - mov ecx, [esi+18h] - push eax - mov eax, [esi+14h] - push ecx - mov ecx, [esi+24h] - push eax - mov eax, [esi] - push ecx - mov ecx, [esi+4] - push eax - mov eax, [esp+34h+var_8] - push ecx - push eax - push edx - call sub_4CC040 - mov eax, 1 - add [esi+14h], eax - add [esi], eax - add [esi+4], eax - mov eax, [esi+4] - add esp, 20h - cmp eax, [esi+8] - jnz short loc_4CC549 - push esi - call sub_4CBD50 - add esp, 4 - -loc_4CC549: ; CODE XREF: sub_4CC3B0+18Ej - pop edi - mov eax, ebp - pop ebp - pop ebx - pop esi - add esp, 10h - retn -; --------------------------------------------------------------------------- - -loc_4CC553: ; CODE XREF: sub_4CC3B0+12Bj - ; sub_4CC3B0+154j - cmp eax, 3 - jnb short loc_4CC55D - mov eax, 3 - -loc_4CC55D: ; CODE XREF: sub_4CC3B0+1A6j - push eax - mov eax, [esi+2Ch] - lea ecx, [ebx+ebp*4] - push ecx - mov ecx, [esi+18h] - push eax - mov eax, [esi+14h] - push ecx - mov ecx, [esi+24h] - push eax - mov eax, [esi] - push ecx - mov ecx, [esi+4] - push eax - mov eax, [esp+3Ch+var_8] - push ecx - push eax - push edx - call sub_4CBEC0 - mov edi, eax - mov eax, 1 - add [esi+14h], eax - add [esi], eax - add [esi+4], eax - mov eax, [esi+4] - sub edi, ebx - add esp, 28h - sar edi, 2 - cmp eax, [esi+8] - jnz short loc_4CC5AC - push esi - call sub_4CBD50 - add esp, 4 - -loc_4CC5AC: ; CODE XREF: sub_4CC3B0+1F1j - mov eax, edi - pop edi - pop ebp - pop ebx - pop esi - add esp, 10h - retn -sub_4CC3B0 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -sub_4CC5C0 proc near ; DATA XREF: sub_4CCAA0+29o - -var_10 = dword ptr -10h -var_8 = dword ptr -8 -var_4 = dword ptr -4 -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 - - sub esp, 10h - push esi - mov esi, [esp+14h+arg_0] - mov eax, [esi+10h] - cmp eax, 4 - mov [esp+14h+arg_0], eax - jnb short loc_4CC5F9 - mov eax, 1 - add [esi+14h], eax - add [esi], eax - add [esi+4], eax - mov eax, [esi+4] - cmp eax, [esi+8] - jnz short loc_4CC5F2 - push esi - call sub_4CBD50 - add esp, 4 - -loc_4CC5F2: ; CODE XREF: sub_4CC5C0+27j - xor eax, eax - pop esi - add esp, 10h - retn -; --------------------------------------------------------------------------- - -loc_4CC5F9: ; CODE XREF: sub_4CC5C0+12j - push ebx - push ebp - push edi - mov edi, [esi] - movzx eax, byte ptr [edi] - movzx edx, byte ptr [edi+1] - xor edx, dword_553198[eax*4] - movzx eax, byte ptr [edi+2] - movzx ebp, byte ptr [edi+3] - mov ecx, eax - add eax, eax - add eax, eax - add eax, eax - xor eax, dword_553198[ebp*4] - mov ebp, [esi+4] - shl eax, 5 - shl ecx, 8 - xor eax, edx - and eax, [esi+28h] - xor ecx, edx - mov ebx, edx - mov edx, [esi+20h] - and ebx, 3FFh - sub ebp, [edx+ebx*4] - and ecx, 0FFFFh - mov [esp+20h+var_4], ebp - mov ebp, [esi+4] - sub ebp, [edx+ecx*4+1000h] - mov [esp+20h+var_10], ebp - mov ebp, [edx+eax*4+41000h] - mov [esp+20h+var_8], ebp - mov ebp, [esi+4] - mov [edx+eax*4+41000h], ebp - mov edx, [esi+20h] - mov eax, [edx+eax*4+41000h] - mov [edx+ecx*4+1000h], eax - mov eax, [esi+20h] - mov ecx, [eax+ecx*4+1000h] - mov [eax+ebx*4], ecx - mov ecx, [esp+20h+var_4] - mov ebx, [esp+20h+arg_4] - xor ebp, ebp - cmp ecx, [esi+18h] - mov eax, 1 - jnb short loc_4CC6B9 - mov edx, edi - sub edx, ecx - mov dl, [edx] - cmp dl, [edi] - jnz short loc_4CC6B9 - mov eax, 2 - lea edx, [ecx-1] - mov [ebx], eax - mov [ebx+4], edx - mov ebp, eax - -loc_4CC6B9: ; CODE XREF: sub_4CC5C0+DEj - ; sub_4CC5C0+E8j - mov edx, [esp+20h+var_10] - cmp ecx, edx - jz short loc_4CC6E5 - cmp edx, [esi+18h] - jnb short loc_4CC6E5 - mov edx, edi - sub edx, [esp+20h+var_10] - mov dl, [edx] - cmp dl, [edi] - jnz short loc_4CC6E5 - mov ecx, [esp+20h+var_10] - lea edx, [ecx-1] - mov [ebx+ebp*4+4], edx - mov eax, 3 - add ebp, 2 - -loc_4CC6E5: ; CODE XREF: sub_4CC5C0+FFj - ; sub_4CC5C0+104j ... - test ebp, ebp - jz short loc_4CC747 - cmp eax, [esp+20h+arg_0] - jz short loc_4CC708 - mov edx, eax - sub edx, ecx - add edx, edi - -loc_4CC6F5: ; CODE XREF: sub_4CC5C0+146j - mov cl, [edx] - cmp cl, [eax+edi] - jnz short loc_4CC708 - add eax, 1 - add edx, 1 - cmp eax, [esp+20h+arg_0] - jnz short loc_4CC6F5 - -loc_4CC708: ; CODE XREF: sub_4CC5C0+12Dj - ; sub_4CC5C0+13Aj - cmp eax, [esp+20h+arg_0] - mov [ebx+ebp*4-8], eax - jnz short loc_4CC747 - mov edx, [esi+14h] - mov eax, [esi+24h] - mov ecx, [esp+20h+var_8] - mov [eax+edx*4], ecx - mov eax, 1 - add [esi+14h], eax - add [esi], eax - add [esi+4], eax - mov eax, [esi+4] - cmp eax, [esi+8] - jnz short loc_4CC73D - push esi - call sub_4CBD50 - add esp, 4 - -loc_4CC73D: ; CODE XREF: sub_4CC5C0+172j - pop edi - mov eax, ebp - pop ebp - pop ebx - pop esi - add esp, 10h - retn -; --------------------------------------------------------------------------- - -loc_4CC747: ; CODE XREF: sub_4CC5C0+127j - ; sub_4CC5C0+150j - cmp eax, 3 - jnb short loc_4CC751 - mov eax, 3 - -loc_4CC751: ; CODE XREF: sub_4CC5C0+18Aj - mov ecx, [esi+18h] - push eax - mov eax, [esi+2Ch] - lea edx, [ebx+ebp*4] - push edx - mov edx, [esi+14h] - push eax - mov eax, [esi+24h] - push ecx - mov ecx, [esi] - push edx - mov edx, [esi+4] - push eax - mov eax, [esp+38h+var_8] - push ecx - mov ecx, [esp+3Ch+arg_0] - push edx - push eax - push ecx - call sub_4CBDE0 - mov edi, eax - mov eax, 1 - add [esi+14h], eax - add [esi], eax - add [esi+4], eax - mov eax, [esi+4] - sub edi, ebx - add esp, 28h - sar edi, 2 - cmp eax, [esi+8] - jnz short loc_4CC7A4 - push esi - call sub_4CBD50 - add esp, 4 - -loc_4CC7A4: ; CODE XREF: sub_4CC5C0+1D9j - mov eax, edi - pop edi - pop ebp - pop ebx - pop esi - add esp, 10h - retn -sub_4CC5C0 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -sub_4CC7B0 proc near ; DATA XREF: sub_4CCAA0+47o - -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 - - push ebx - push ebp - mov ebp, [esp+8+arg_4] - push esi - mov esi, [esp+0Ch+arg_0] - push edi - mov edi, 1 - -loc_4CC7C1: ; CODE XREF: sub_4CC7B0+6Dj - mov edx, [esi+10h] - cmp edx, 2 - jb short loc_4CC802 - mov eax, [esi] - mov ebx, [esi+4] - xor ecx, ecx - mov ch, [eax+1] - mov cl, [eax] - mov eax, ecx - mov ecx, [esi+20h] - lea eax, [ecx+eax*4] - mov ecx, [eax] - mov [eax], ebx - mov eax, [esi+2Ch] - push eax - mov eax, [esi+18h] - push eax - mov eax, [esi+14h] - push eax - mov eax, [esi+24h] - push eax - mov eax, [esi] - push eax - mov eax, [esi+4] - push eax - push ecx - push edx - call sub_4CC040 - add esp, 20h - -loc_4CC802: ; CODE XREF: sub_4CC7B0+17j - add [esi+4], edi - mov eax, [esi+4] - add [esi+14h], edi - add [esi], edi - cmp eax, [esi+8] - jnz short loc_4CC81B - push esi - call sub_4CBD50 - add esp, 4 - -loc_4CC81B: ; CODE XREF: sub_4CC7B0+60j - sub ebp, edi - jnz short loc_4CC7C1 - pop edi - pop esi - pop ebp - pop ebx - retn -sub_4CC7B0 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -sub_4CC830 proc near ; DATA XREF: sub_4CCAA0+5Bo - -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 - - push ebx - push ebp - push esi - mov esi, [esp+0Ch+arg_0] - push edi - mov edi, 1 - lea ecx, [ecx+0] - -loc_4CC840: ; CODE XREF: sub_4CC830+9Fj - mov ebp, [esi+10h] - cmp ebp, 3 - jb short loc_4CC8B2 - mov ecx, [esi] - movzx edx, byte ptr [ecx] - movzx eax, byte ptr [ecx+1] - xor eax, dword_553198[edx*4] - mov ebx, [esi+4] - xor edx, edx - mov dh, [ecx+2] - xor edx, eax - and edx, [esi+28h] - and eax, 3FFh - mov ecx, edx - mov edx, [esi+20h] - mov edi, [edx+ecx*4+1000h] - lea edx, [edx+ecx*4+1000h] - mov [edx], ebx - mov edx, [esi+20h] - mov ecx, [edx+ecx*4+1000h] - mov [edx+eax*4], ecx - mov edx, [esi+2Ch] - mov eax, [esi+18h] - mov ecx, [esi+14h] - push edx - mov edx, [esi+24h] - push eax - mov eax, [esi] - push ecx - mov ecx, [esi+4] - push edx - push eax - push ecx - push edi - push ebp - call sub_4CC040 - add esp, 20h - mov edi, 1 - -loc_4CC8B2: ; CODE XREF: sub_4CC830+16j - add [esi+4], edi - mov eax, [esi+4] - add [esi+14h], edi - add [esi], edi - cmp eax, [esi+8] - jnz short loc_4CC8CB - push esi - call sub_4CBD50 - add esp, 4 - -loc_4CC8CB: ; CODE XREF: sub_4CC830+90j - sub [esp+10h+arg_4], edi - jnz loc_4CC840 - pop edi - pop esi - pop ebp - pop ebx - retn -sub_4CC830 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -sub_4CC8E0 proc near ; DATA XREF: sub_4CCAA0+6Ao - -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 - - push ebx - push ebp - push esi - mov esi, [esp+0Ch+arg_0] - push edi - mov edi, 1 - lea ecx, [ecx+0] - -loc_4CC8F0: ; CODE XREF: sub_4CC8E0+D6j - mov eax, [esi+10h] - cmp eax, 4 - mov [esp+10h+arg_0], eax - jb loc_4CC999 - mov eax, [esi] - movzx edi, byte ptr [eax+2] - movzx edx, byte ptr [eax] - movzx ebx, byte ptr [eax+3] - movzx ecx, byte ptr [eax+1] - xor ecx, dword_553198[edx*4] - mov ebp, [esi+4] - lea eax, ds:0[edi*8] - xor eax, dword_553198[ebx*4] - mov edx, edi - mov edi, [esi+20h] - shl eax, 5 - xor eax, ecx - and eax, [esi+28h] - shl edx, 8 - mov ebx, [edi+eax*4+41000h] - xor edx, ecx - and edx, 0FFFFh - mov [edi+edx*4+1000h], ebp - mov edi, [esi+20h] - mov edx, [edi+edx*4+1000h] - and ecx, 3FFh - mov [edi+ecx*4], edx - mov ecx, [esi+20h] - mov edx, [esi+4] - mov [ecx+eax*4+41000h], edx - mov eax, [esi+2Ch] - mov ecx, [esi+18h] - mov edx, [esi+14h] - push eax - mov eax, [esi+24h] - push ecx - mov ecx, [esi] - push edx - mov edx, [esi+4] - push eax - mov eax, [esp+20h+arg_0] - push ecx - push edx - push ebx - push eax - call sub_4CC040 - add esp, 20h - mov edi, 1 - -loc_4CC999: ; CODE XREF: sub_4CC8E0+1Aj - add [esi+4], edi - mov eax, [esi+4] - add [esi+14h], edi - add [esi], edi - cmp eax, [esi+8] - jnz short loc_4CC9B2 - push esi - call sub_4CBD50 - add esp, 4 - -loc_4CC9B2: ; CODE XREF: sub_4CC8E0+C7j - sub [esp+10h+arg_4], edi - jnz loc_4CC8F0 - pop edi - pop esi - pop ebp - pop ebx - retn -sub_4CC8E0 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -sub_4CC9D0 proc near ; DATA XREF: sub_4CCAA0+30o - -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 - - push ebx - push ebp - push esi - mov esi, [esp+0Ch+arg_0] - push edi - mov edi, 1 - lea ecx, [ecx+0] - -loc_4CC9E0: ; CODE XREF: sub_4CC9D0+BAj - cmp dword ptr [esi+10h], 4 - jb loc_4CCA6D - mov eax, [esi] - movzx edi, byte ptr [eax+2] - movzx edx, byte ptr [eax] - movzx ebx, byte ptr [eax+3] - movzx ecx, byte ptr [eax+1] - xor ecx, dword_553198[edx*4] - mov ebp, [esi+4] - lea eax, ds:0[edi*8] - xor eax, dword_553198[ebx*4] - mov edx, edi - mov edi, [esi+20h] - shl eax, 5 - xor eax, ecx - and eax, [esi+28h] - mov ebx, [edi+eax*4+41000h] - lea edi, [edi+eax*4+41000h] - mov [edi], ebp - mov edi, [esi+20h] - mov eax, [edi+eax*4+41000h] - shl edx, 8 - xor edx, ecx - and edx, 0FFFFh - mov [edi+edx*4+1000h], eax - mov eax, [esi+20h] - mov edx, [eax+edx*4+1000h] - and ecx, 3FFh - mov [eax+ecx*4], edx - mov eax, [esi+14h] - mov ecx, [esi+24h] - mov [ecx+eax*4], ebx - mov edi, 1 - -loc_4CCA6D: ; CODE XREF: sub_4CC9D0+14j - add [esi+4], edi - mov eax, [esi+4] - add [esi+14h], edi - add [esi], edi - cmp eax, [esi+8] - jnz short loc_4CCA86 - push esi - call sub_4CBD50 - add esp, 4 - -loc_4CCA86: ; CODE XREF: sub_4CC9D0+ABj - sub [esp+10h+arg_4], edi - jnz loc_4CC9E0 - pop edi - pop esi - pop ebp - pop ebx - retn -sub_4CC9D0 endp - -; =============== S U B R O U T I N E ======================================= - -sub_4CCAA0 proc near ; CODE XREF: sub_4D0270+F3p - -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 - - mov eax, [esp+arg_4] - mov ecx, [esp+arg_0] - mov dword ptr [eax], offset sub_4CBC80 - mov dword ptr [eax+4], offset loc_4CB960 - mov dword ptr [eax+8], offset loc_4CB970 - mov dword ptr [eax+0Ch], offset loc_4CB950 - cmp dword ptr [ecx+50h], 0 - jnz short loc_4CCAD8 - mov dword ptr [eax+10h], offset sub_4CC5C0 - mov dword ptr [eax+14h], offset sub_4CC9D0 - retn -; --------------------------------------------------------------------------- - -loc_4CCAD8: ; CODE XREF: sub_4CCAA0+27j - mov ecx, [ecx+48h] - cmp ecx, 2 - jnz short loc_4CCAEF - mov dword ptr [eax+10h], offset loc_4CC180 - mov dword ptr [eax+14h], offset sub_4CC7B0 - retn -; --------------------------------------------------------------------------- - -loc_4CCAEF: ; CODE XREF: sub_4CCAA0+3Ej - cmp ecx, 3 - jnz short loc_4CCB03 - mov dword ptr [eax+10h], offset sub_4CC220 - mov dword ptr [eax+14h], offset sub_4CC830 - retn -; --------------------------------------------------------------------------- - -loc_4CCB03: ; CODE XREF: sub_4CCAA0+52j - mov dword ptr [eax+10h], offset sub_4CC3B0 - mov dword ptr [eax+14h], offset sub_4CC8E0 - retn -sub_4CCAA0 endp - -; --------------------------------------------------------------------------- - -Interface1_AddRef proc near ; DATA XREF: .rdata:00517A94o -;sub_4CCB80 - -arg_0 = dword ptr 4 - mov eax, [esp+arg_0] - add dword ptr [eax+4], 1 - mov eax, [eax+4] - retn 4 -Interface1_AddRef endp - -; --------------------------------------------------------------------------- - -sub_4CCB20 proc near ; DATA XREF: .rdata:00517A9Co - -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 -arg_8 = dword ptr 0Ch -arg_C = dword ptr 10h - - push esi - mov esi, [esp+4+arg_0] - mov ecx, [esi+10h] - sub ecx, [esi+18h] - mov eax, [esi+14h] - sbb eax, [esi+1Ch] - xor edx, edx - cmp edx, eax - push edi - mov edi, [esp+8+arg_8] - jb short loc_4CCB48 - ja short loc_4CCB42 - cmp edi, ecx - jbe short loc_4CCB48 - -loc_4CCB42: ; CODE XREF: sub_4CCB20+1Cj - mov edi, [esi+10h] - sub edi, [esi+18h] - -loc_4CCB48: ; CODE XREF: sub_4CCB20+1Aj - ; sub_4CCB20+20j - mov eax, [esp+8+arg_C] - test eax, eax - jz short loc_4CCB52 - mov [eax], edi - -loc_4CCB52: ; CODE XREF: sub_4CCB20+2Ej - test edi, edi - jz short loc_4CCB72 - mov eax, [esi+8] - add eax, [esi+18h] - mov ecx, [esp+8+arg_4] - push edi ; size_t - push eax ; void * - push ecx ; void * - call _memcpy ; Microsoft VisualC 2-8/net runtime - add esp, 0Ch - add [esi+18h], edi - adc dword ptr [esi+1Ch], 0 - -loc_4CCB72: ; CODE XREF: sub_4CCB20+34j - pop edi - xor eax, eax - pop esi - retn 10h -sub_4CCB20 endp - -; --------------------------------------------------------------------------- - -sub_4CCB90 proc near ; DATA XREF: .rdata:00517AB0o - -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 -arg_8 = dword ptr 0Ch -arg_C = dword ptr 10h - - push ebx - mov ebx, [esp+4+arg_8] - push esi - mov esi, [esp+8+arg_0] - mov ecx, [esi+10h] - mov eax, [esi+14h] - push edi - xor edi, edi - sub ecx, [esi+18h] - sbb eax, [esi+1Ch] - cmp edi, eax - jb short loc_4CCBC2 - ja short loc_4CCBB3 - cmp ebx, ecx - jbe short loc_4CCBC2 - -loc_4CCBB3: ; CODE XREF: sub_4CCB90+1Dj - pop edi - mov byte ptr [esi+20h], 1 - pop esi - mov eax, 80004005h - pop ebx - retn 10h - -loc_4CCBC2: ; CODE XREF: sub_4CCB90+1Bj - ; sub_4CCB90+21j - mov ecx, [esi+8] - mov eax, [esp+0Ch+arg_4] - add ecx, [esi+18h] - push ebx ; size_t - push eax ; void * - push ecx ; void * - call _memcpy ; Microsoft VisualC 2-8/net runtime - mov eax, [esp+18h+arg_C] - add esp, 0Ch - add [esi+18h], ebx - adc [esi+1Ch], edi - test eax, eax - jz short loc_4CCBE7 - mov [eax], ebx - -loc_4CCBE7: ; CODE XREF: sub_4CCB90+53j - pop edi - pop esi - xor eax, eax - pop ebx - retn 10h -sub_4CCB90 endp - -; --------------------------------------------------------------------------- - align 10h -; --------------------------------------------------------------------------- - -LzmaProps_Decode proc near ; CODE XREF: Decompress_lzma_internal+35p -;sub_4CCBF0 - -propsRes = dword ptr 4 -propsData = dword ptr 8 -propsSize = dword ptr 0Ch - - cmp [esp+propsSize], 5 ; LZMA_PROPS_SIZE - jge short loc_4CCBFD - -loc_4CCBF7: ; CODE XREF: LzmaProps_Decode+16j - mov eax, 1 - retn -; --------------------------------------------------------------------------- - -loc_4CCBFD: ; CODE XREF: LzmaProps_Decode+5j - mov eax, [esp+propsData] - mov cl, [eax] - cmp cl, 0E1h ; 9 * 5 * 5 - jnb short loc_4CCBF7 - cmp cl, 2Dh - push edi - mov edi, [esp+4+propsRes] - mov dword ptr [edi+8], 0 - jb short loc_4CCC40 - push esi - movzx esi, cl - mov eax, 6C16C16Dh - mul esi - mov eax, esi - sub eax, edx - shr eax, 1 - add eax, edx - shr eax, 5 - movzx eax, al - mov edx, eax - pop esi - -loc_4CCC35: ; CODE XREF: LzmaProps_Decode+4Bj - add cl, 0D3h - sub eax, 1 - jnz short loc_4CCC35 - mov [edi+8], edx - -loc_4CCC40: ; CODE XREF: LzmaProps_Decode+27j - cmp cl, 9 - mov dword ptr [edi+4], 0 - jb short loc_4CCC6B - movzx edx, cl - mov eax, 38E38E39h - mul edx - shr edx, 1 - movzx eax, dl - mov edx, eax - lea ecx, [ecx+0] - -loc_4CCC60: ; CODE XREF: LzmaProps_Decode+76j - add cl, 0F7h - sub eax, 1 - jnz short loc_4CCC60 - mov [edi+4], edx - -loc_4CCC6B: ; CODE XREF: LzmaProps_Decode+5Aj - movzx eax, cl - mov [edi], eax - xor eax, eax - pop edi - retn -LzmaProps_Decode endp - -sub_4CCC80 proc near ; CODE XREF: Decompress_lzma_internal+97p - -var_3C = dword ptr -3Ch -var_38 = dword ptr -38h -var_34 = dword ptr -34h -var_30 = dword ptr -30h -var_2C = dword ptr -2Ch -var_28 = dword ptr -28h -var_24 = dword ptr -24h -var_20 = dword ptr -20h -var_1C = dword ptr -1Ch -var_18 = dword ptr -18h -var_14 = dword ptr -14h -var_10 = dword ptr -10h -var_C = dword ptr -0Ch -var_8 = dword ptr -8 -var_4 = dword ptr -4 -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 -arg_8 = dword ptr 0Ch -arg_C = dword ptr 10h -arg_10 = dword ptr 14h -arg_14 = dword ptr 18h -arg_18 = dword ptr 1Ch - - sub esp, 3Ch - mov eax, [esp+3Ch+arg_0] - mov ecx, [eax+8] - push ebx - push ebp - mov ebp, 1 - mov edx, ebp - shl edx, cl - mov ecx, [eax+4] - xor ebx, ebx - push esi - sub edx, ebp - mov [esp+48h+var_C], edx - mov edx, ebp - shl edx, cl - mov ecx, [esp+48h+arg_C] - push edi - mov edi, [eax+0Ch] - sub edx, ebp - mov [esp+4Ch+var_8], edx - mov edx, [eax] - mov [ecx], ebx - mov ecx, [esp+4Ch+arg_18] - mov [ecx], ebx - mov ecx, [eax+4] - add ecx, edx - mov eax, 300h - shl eax, cl - mov [esp+4Ch+var_20], edi - mov [esp+4Ch+var_2C], ebx - mov byte ptr [esp+4Ch+arg_0], bl - add eax, 736h - mov [esp+4Ch+var_14], edx - mov [esp+4Ch+var_34], ebx - mov [esp+4Ch+var_38], ebp - mov [esp+4Ch+var_24], ebp - mov [esp+4Ch+var_28], ebp - mov [esp+4Ch+var_18], ebp - jz short loc_4CCD04 - mov ecx, eax - shr ecx, 1 - mov eax, 4000400h - rep stosd - adc ecx, ecx - rep stosw - -loc_4CCD04: ; CODE XREF: sub_4CCC80+72j - mov edx, [esp+4Ch+arg_8] - mov ecx, [esp+4Ch+arg_4] - lea edi, [ecx+edx] - xor esi, esi - or eax, 0FFFFFFFFh - mov [esp+4Ch+var_3C], edi - xor edx, edx - lea ebx, [ebx+0] - -loc_4CCD20: ; CODE XREF: sub_4CCC80+BCj - cmp ecx, edi - jz loc_4CCF0B - movzx ebp, byte ptr [ecx] - shl esi, 8 - or esi, ebp - mov ebp, 1 - add edx, ebp - add ecx, ebp - cmp edx, 5 - jl short loc_4CCD20 - cmp [esp+4Ch+arg_14], ebx - mov [esp+4Ch+arg_8], ecx - jbe loc_4CCFCD - lea esp, [esp+0] - -loc_4CCD50: ; CODE XREF: sub_4CCC80+32Dj - mov edx, [esp+4Ch+var_34] - mov ecx, [esp+4Ch+var_C] - and ecx, [esp+4Ch+var_2C] - mov edi, [esp+4Ch+var_20] - mov ebx, edx - shl ebx, 4 - add ebx, ecx - cmp eax, 1000000h - lea ebx, [edi+ebx*2] - mov [esp+4Ch+var_1C], ecx - mov [esp+4Ch+var_30], ebx - jnb short loc_4CCD99 - mov ecx, [esp+4Ch+arg_8] - cmp ecx, [esp+4Ch+var_3C] - jz loc_4CD6E2 - movzx ebp, byte ptr [ecx] - shl esi, 8 - shl eax, 8 - or esi, ebp - add ecx, 1 - mov [esp+4Ch+arg_8], ecx - -loc_4CCD99: ; CODE XREF: sub_4CCC80+F7j - movzx ebx, word ptr [ebx] - movzx ebp, bx - mov ecx, eax - shr ecx, 0Bh - imul ecx, ebp - cmp esi, ecx - jnb loc_4CCFF4 - mov edi, [esp+4Ch+var_30] - mov eax, ecx - mov ecx, 800h - sub ecx, ebp - mov ebp, [esp+4Ch+var_8] - sar ecx, 5 - add ecx, ebx - movzx ebx, byte ptr [esp+4Ch+arg_0] - mov [edi], cx - mov edi, [esp+4Ch+var_2C] - mov ecx, 8 - sub cl, byte ptr [esp+4Ch+var_14] - and ebp, edi - shr ebx, cl - mov ecx, [esp+4Ch+var_14] - shl ebp, cl - mov ecx, [esp+4Ch+var_20] - mov edx, 1 - add ebx, ebp - imul ebx, 600h - cmp [esp+4Ch+var_34], 7 - lea ebp, [ebx+ecx+0E6Ch] - mov [esp+4Ch+var_30], ebp - jl loc_4CCEB0 - sub edi, [esp+4Ch+var_38] - mov ecx, [esp+4Ch+arg_10] - movzx ebx, byte ptr [edi+ecx] - jmp short loc_4CCE20 -; --------------------------------------------------------------------------- - align 10h - -loc_4CCE20: ; CODE XREF: sub_4CCC80+197j - ; sub_4CCC80+2C6j - add ebx, ebx - mov [esp+4Ch+var_10], ebx - and ebx, 100h - cmp eax, 1000000h - lea ecx, [ebx+edx] - lea ebp, [ebp+ecx*2+200h] - mov [esp+4Ch+arg_0], ebp - jnb short loc_4CCE61 - mov ecx, [esp+4Ch+arg_8] - cmp ecx, [esp+4Ch+var_3C] - jz loc_4CD6E2 - movzx edi, byte ptr [ecx] - shl esi, 8 - shl eax, 8 - or esi, edi - add ecx, 1 - mov [esp+4Ch+arg_8], ecx - -loc_4CCE61: ; CODE XREF: sub_4CCC80+1BFj - movzx edi, word ptr [ebp+0] - movzx ebp, di - mov ecx, eax - shr ecx, 0Bh - imul ecx, ebp - cmp esi, ecx - jnb loc_4CCF15 - mov eax, ecx - mov ecx, 800h - sub ecx, ebp - sar ecx, 5 - add ecx, edi - mov edi, [esp+4Ch+arg_0] - add edx, edx - test ebx, ebx - mov [edi], cx - jz loc_4CCF36 - -loc_4CCE97: ; CODE XREF: sub_4CCC80+2B0j - cmp edx, 100h - jge loc_4CCF70 - mov ebp, [esp+4Ch+var_30] - jmp short loc_4CCEB0 -; --------------------------------------------------------------------------- - align 10h - -loc_4CCEB0: ; CODE XREF: sub_4CCC80+185j - ; sub_4CCC80+227j ... - cmp eax, 1000000h - lea ecx, [edx+edx] - mov [esp+4Ch+arg_0], ecx - jnb short loc_4CCEDE - mov edi, [esp+4Ch+arg_8] - cmp edi, [esp+4Ch+var_3C] - jz loc_4CD6E2 - movzx ebx, byte ptr [edi] - shl esi, 8 - shl eax, 8 - or esi, ebx - add edi, 1 - mov [esp+4Ch+arg_8], edi - -loc_4CCEDE: ; CODE XREF: sub_4CCC80+23Cj - movzx edi, word ptr [ecx+ebp] - movzx ebx, di - mov ecx, eax - shr ecx, 0Bh - imul ecx, ebx - cmp esi, ecx - jnb short loc_4CCF4B - mov eax, ecx - mov ecx, 800h - sub ecx, ebx - sar ecx, 5 - add ecx, edi - mov edi, [esp+4Ch+arg_0] - mov [edi+ebp], cx - add edx, edx - jmp short loc_4CCF64 -; --------------------------------------------------------------------------- - -loc_4CCF0B: ; CODE XREF: sub_4CCC80+A2j - pop edi - pop esi - mov eax, ebp - pop ebp - pop ebx - add esp, 3Ch - retn -; --------------------------------------------------------------------------- - -loc_4CCF15: ; CODE XREF: sub_4CCC80+1F2j - sub eax, ecx - sub esi, ecx - mov cx, di - shr cx, 5 - sub di, cx - test ebx, ebx - mov ecx, [esp+4Ch+arg_0] - mov [ecx], di - lea edx, [edx+edx+1] - jz loc_4CCE97 - -loc_4CCF36: ; CODE XREF: sub_4CCC80+211j - cmp edx, 100h - jge short loc_4CCF70 - mov ebp, [esp+4Ch+var_30] - mov ebx, [esp+4Ch+var_10] - jmp loc_4CCE20 -; --------------------------------------------------------------------------- - -loc_4CCF4B: ; CODE XREF: sub_4CCC80+26Fj - mov dx, di - shr dx, 5 - sub di, dx - mov edx, [esp+4Ch+arg_0] - sub eax, ecx - sub esi, ecx - mov [edx+ebp], di - add edx, 1 - -loc_4CCF64: ; CODE XREF: sub_4CCC80+289j - cmp edx, 100h - jl loc_4CCEB0 - -loc_4CCF70: ; CODE XREF: sub_4CCC80+21Dj - ; sub_4CCC80+2BCj - mov ecx, [esp+4Ch+var_2C] - mov edi, [esp+4Ch+arg_10] - mov [ecx+edi], dl - add ecx, 1 - mov [esp+4Ch+var_2C], ecx - mov ecx, [esp+4Ch+var_34] - cmp ecx, 4 - mov byte ptr [esp+4Ch+arg_0], dl - jge short loc_4CCF99 - mov [esp+4Ch+var_34], 0 - jmp short loc_4CCFA5 -; --------------------------------------------------------------------------- - -loc_4CCF99: ; CODE XREF: sub_4CCC80+30Dj - cmp ecx, 0Ah - jge short loc_4CCFEF - sub ecx, 3 - -loc_4CCFA1: ; CODE XREF: sub_4CCC80+372j - mov [esp+4Ch+var_34], ecx - -loc_4CCFA5: ; CODE XREF: sub_4CCC80+317j - ; sub_4CCC80+530j ... - mov ecx, [esp+4Ch+var_2C] - cmp ecx, [esp+4Ch+arg_14] - jb loc_4CCD50 - -loc_4CCFB3: ; CODE XREF: sub_4CCC80+A13j - ; sub_4CCC80+A5Aj - cmp eax, 1000000h - jnb short loc_4CCFCD - mov edx, [esp+4Ch+arg_8] - cmp edx, [esp+4Ch+var_3C] - jz loc_4CD6E2 - add [esp+4Ch+arg_8], 1 - -loc_4CCFCD: ; CODE XREF: sub_4CCC80+C6j - ; sub_4CCC80+338j - mov eax, [esp+4Ch+arg_8] - sub eax, [esp+4Ch+arg_4] - mov ecx, [esp+4Ch+arg_C] - mov edx, [esp+4Ch+var_2C] - pop edi - mov [ecx], eax - mov eax, [esp+48h+arg_18] - pop esi - pop ebp - mov [eax], edx - xor eax, eax - pop ebx - add esp, 3Ch - retn -; --------------------------------------------------------------------------- - -loc_4CCFEF: ; CODE XREF: sub_4CCC80+31Cj - sub ecx, 6 - jmp short loc_4CCFA1 -; --------------------------------------------------------------------------- - -loc_4CCFF4: ; CODE XREF: sub_4CCC80+129j - sub eax, ecx - sub esi, ecx - mov cx, bx - shr cx, 5 - sub bx, cx - cmp eax, 1000000h - mov ecx, [esp+4Ch+var_30] - mov [ecx], bx - jnb short loc_4CD030 - mov ecx, [esp+4Ch+arg_8] - cmp ecx, [esp+4Ch+var_3C] - jz loc_4CD6E2 - movzx ebx, byte ptr [ecx] - shl esi, 8 - shl eax, 8 - or esi, ebx - add ecx, 1 - mov [esp+4Ch+arg_8], ecx - -loc_4CD030: ; CODE XREF: sub_4CCC80+38Ej - movzx ebx, word ptr [edi+edx*2+180h] - movzx ebp, bx - mov ecx, eax - shr ecx, 0Bh - imul ecx, ebp - cmp esi, ecx - jnb short loc_4CD096 - mov eax, ecx - mov ecx, 800h - sub ecx, ebp - sar ecx, 5 - add ecx, ebx - mov [edi+edx*2+180h], cx - mov ecx, [esp+4Ch+var_28] - mov ebp, [esp+4Ch+var_1C] - mov [esp+4Ch+var_18], ecx - mov ecx, [esp+4Ch+var_24] - mov [esp+4Ch+var_28], ecx - mov ecx, [esp+4Ch+var_38] - mov [esp+4Ch+var_24], ecx - xor ecx, ecx - cmp edx, 7 - setl cl - sub ecx, 1 - and ecx, 3 - mov [esp+4Ch+var_34], ecx - lea ecx, [edi+664h] - jmp loc_4CD30B -; --------------------------------------------------------------------------- - -loc_4CD096: ; CODE XREF: sub_4CCC80+3C5j - sub eax, ecx - sub esi, ecx - mov cx, bx - shr cx, 5 - sub bx, cx - cmp eax, 1000000h - mov [edi+edx*2+180h], bx - jnb short loc_4CD0D3 - mov ecx, [esp+4Ch+arg_8] - cmp ecx, [esp+4Ch+var_3C] - jz loc_4CD6E2 - movzx ebx, byte ptr [ecx] - shl esi, 8 - shl eax, 8 - or esi, ebx - add ecx, 1 - mov [esp+4Ch+arg_8], ecx - -loc_4CD0D3: ; CODE XREF: sub_4CCC80+431j - movzx ebx, word ptr [edi+edx*2+198h] - movzx ebp, bx - mov ecx, eax - shr ecx, 0Bh - imul ecx, ebp - cmp esi, ecx - mov [esp+4Ch+arg_0], ebx - jnb loc_4CD1D3 - mov ebx, 800h - sub ebx, ebp - mov ebp, [esp+4Ch+var_1C] - sar ebx, 5 - add ebx, [esp+4Ch+arg_0] - mov eax, ecx - mov [edi+edx*2+198h], bx - lea ebx, [edx+0Fh] - shl ebx, 4 - add ebx, ebp - cmp ecx, 1000000h - lea ebx, [edi+ebx*2] - mov [esp+4Ch+var_30], ebx - jnb short loc_4CD147 - mov ebx, [esp+4Ch+arg_8] - cmp ebx, [esp+4Ch+var_3C] - jz loc_4CD6E2 - shl ecx, 8 - mov eax, ecx - movzx ecx, byte ptr [ebx] - shl esi, 8 - or esi, ecx - add ebx, 1 - mov [esp+4Ch+arg_8], ebx - -loc_4CD147: ; CODE XREF: sub_4CCC80+4A3j - mov ecx, [esp+4Ch+var_30] - movzx ecx, word ptr [ecx] - mov [esp+4Ch+arg_0], ecx - movzx ebx, cx - mov ecx, eax - shr ecx, 0Bh - imul ecx, ebx - cmp esi, ecx - jnb short loc_4CD1B5 - mov edi, [esp+4Ch+var_30] - mov eax, ecx - mov ecx, 800h - sub ecx, ebx - sar ecx, 5 - add ecx, [esp+4Ch+arg_0] - mov [edi], cx - mov edi, [esp+4Ch+var_2C] - test edi, edi - jz loc_4CD6E2 - xor ecx, ecx - cmp edx, 7 - mov edx, [esp+4Ch+arg_10] - setnl cl - lea ecx, [ecx+ecx+9] - mov [esp+4Ch+var_34], ecx - mov ecx, edi - sub ecx, [esp+4Ch+var_38] - add edi, 1 - mov cl, [ecx+edx] - mov [edi+edx-1], cl - mov byte ptr [esp+4Ch+arg_0], cl - mov [esp+4Ch+var_2C], edi - jmp loc_4CCFA5 -; --------------------------------------------------------------------------- - -loc_4CD1B5: ; CODE XREF: sub_4CCC80+4DFj - sub eax, ecx - sub esi, ecx - mov ecx, [esp+4Ch+arg_0] - mov bx, cx - shr bx, 5 - sub cx, bx - mov ebx, [esp+4Ch+var_30] - mov [ebx], cx - jmp loc_4CD2F0 -; --------------------------------------------------------------------------- - -loc_4CD1D3: ; CODE XREF: sub_4CCC80+46Cj - sub eax, ecx - sub esi, ecx - mov cx, bx - shr cx, 5 - sub bx, cx - cmp eax, 1000000h - mov [edi+edx*2+198h], bx - jnb short loc_4CD210 - mov ecx, [esp+4Ch+arg_8] - cmp ecx, [esp+4Ch+var_3C] - jz loc_4CD6E2 - movzx ebx, byte ptr [ecx] - shl esi, 8 - shl eax, 8 - or esi, ebx - add ecx, 1 - mov [esp+4Ch+arg_8], ecx - -loc_4CD210: ; CODE XREF: sub_4CCC80+56Ej - movzx ebx, word ptr [edi+edx*2+1B0h] - movzx ebp, bx - mov ecx, eax - shr ecx, 0Bh - imul ecx, ebp - cmp esi, ecx - jnb short loc_4CD246 - mov eax, ecx - mov ecx, 800h - sub ecx, ebp - sar ecx, 5 - add ecx, ebx - mov [edi+edx*2+1B0h], cx - mov ecx, [esp+4Ch+var_24] - jmp loc_4CD2E0 -; --------------------------------------------------------------------------- - -loc_4CD246: ; CODE XREF: sub_4CCC80+5A5j - sub eax, ecx - sub esi, ecx - mov cx, bx - shr cx, 5 - sub bx, cx - cmp eax, 1000000h - mov [edi+edx*2+1B0h], bx - jnb short loc_4CD283 - mov ecx, [esp+4Ch+arg_8] - cmp ecx, [esp+4Ch+var_3C] - jz loc_4CD6E2 - movzx ebx, byte ptr [ecx] - shl esi, 8 - shl eax, 8 - or esi, ebx - add ecx, 1 - mov [esp+4Ch+arg_8], ecx - -loc_4CD283: ; CODE XREF: sub_4CCC80+5E1j - movzx ebx, word ptr [edi+edx*2+1C8h] - movzx ebp, bx - mov ecx, eax - shr ecx, 0Bh - imul ecx, ebp - cmp esi, ecx - jnb short loc_4CD2B6 - mov eax, ecx - mov ecx, 800h - sub ecx, ebp - sar ecx, 5 - add ecx, ebx - mov [edi+edx*2+1C8h], cx - mov ecx, [esp+4Ch+var_28] - jmp short loc_4CD2D8 -; --------------------------------------------------------------------------- - -loc_4CD2B6: ; CODE XREF: sub_4CCC80+618j - sub eax, ecx - sub esi, ecx - mov cx, bx - shr cx, 5 - sub bx, cx - mov ecx, [esp+4Ch+var_18] - mov [edi+edx*2+1C8h], bx - mov ebx, [esp+4Ch+var_28] - mov [esp+4Ch+var_18], ebx - -loc_4CD2D8: ; CODE XREF: sub_4CCC80+634j - mov ebx, [esp+4Ch+var_24] - mov [esp+4Ch+var_28], ebx - -loc_4CD2E0: ; CODE XREF: sub_4CCC80+5C1j - mov ebx, [esp+4Ch+var_38] - mov ebp, [esp+4Ch+var_1C] - mov [esp+4Ch+var_24], ebx - mov [esp+4Ch+var_38], ecx - -loc_4CD2F0: ; CODE XREF: sub_4CCC80+54Ej - xor ecx, ecx - cmp edx, 7 - setnl cl - sub ecx, 1 - and ecx, 0FFFFFFFDh - add ecx, 0Bh - mov [esp+4Ch+var_34], ecx - lea ecx, [edi+0A68h] - -loc_4CD30B: ; CODE XREF: sub_4CCC80+411j - cmp eax, 1000000h - jnb short loc_4CD332 - mov edx, [esp+4Ch+arg_8] - cmp edx, [esp+4Ch+var_3C] - jz loc_4CD6E2 - movzx edi, byte ptr [edx] - shl esi, 8 - shl eax, 8 - or esi, edi - add edx, 1 - mov [esp+4Ch+arg_8], edx - -loc_4CD332: ; CODE XREF: sub_4CCC80+690j - movzx edi, word ptr [ecx] - movzx ebx, di - mov edx, eax - shr edx, 0Bh - imul edx, ebx - cmp esi, edx - jnb short loc_4CD36E - mov eax, edx - mov edx, 800h - sub edx, ebx - sar edx, 5 - add edx, edi - mov [ecx], dx - shl ebp, 4 - lea ecx, [ecx+ebp+4] - mov [esp+4Ch+var_1C], 0 - mov edx, 3 - jmp loc_4CD409 -; --------------------------------------------------------------------------- - -loc_4CD36E: ; CODE XREF: sub_4CCC80+6C2j - sub eax, edx - sub esi, edx - mov dx, di - shr dx, 5 - sub di, dx - cmp eax, 1000000h - mov [ecx], di - jnb short loc_4CD3A6 - mov edx, [esp+4Ch+arg_8] - cmp edx, [esp+4Ch+var_3C] - jz loc_4CD6E2 - movzx edi, byte ptr [edx] - shl esi, 8 - shl eax, 8 - or esi, edi - add edx, 1 - mov [esp+4Ch+arg_8], edx - -loc_4CD3A6: ; CODE XREF: sub_4CCC80+704j - movzx edx, word ptr [ecx+2] - movzx ebx, dx - mov edi, eax - shr edi, 0Bh - imul edi, ebx - cmp esi, edi - jnb short loc_4CD3E4 - mov eax, edi - mov edi, 800h - sub edi, ebx - sar edi, 5 - add edi, edx - shl ebp, 4 - mov [ecx+2], di - lea ecx, [ecx+ebp+104h] - mov [esp+4Ch+var_1C], 8 - mov edx, 3 - jmp short loc_4CD409 -; --------------------------------------------------------------------------- - -loc_4CD3E4: ; CODE XREF: sub_4CCC80+737j - sub eax, edi - sub esi, edi - mov di, dx - shr di, 5 - sub dx, di - mov [ecx+2], dx - add ecx, 204h - mov [esp+4Ch+var_1C], 10h - mov edx, 8 - -loc_4CD409: ; CODE XREF: sub_4CCC80+6E9j - ; sub_4CCC80+762j - mov [esp+4Ch+arg_0], edx - mov [esp+4Ch+var_10], edx - mov ebx, 1 - jmp short loc_4CD420 -; --------------------------------------------------------------------------- - align 10h - -loc_4CD420: ; CODE XREF: sub_4CCC80+796j - ; sub_4CCC80+819j - cmp eax, 1000000h - mov edx, [esp+4Ch+arg_8] - lea edi, [ebx+ebx] - mov [esp+4Ch+var_30], edi - jnb short loc_4CD44E - cmp edx, [esp+4Ch+var_3C] - jz loc_4CD6E2 - movzx ebp, byte ptr [edx] - shl esi, 8 - shl eax, 8 - or esi, ebp - add edx, 1 - mov [esp+4Ch+arg_8], edx - -loc_4CD44E: ; CODE XREF: sub_4CCC80+7B0j - movzx edi, word ptr [edi+ecx] - movzx ebp, di - mov edx, eax - shr edx, 0Bh - imul edx, ebp - cmp esi, edx - jnb short loc_4CD47B - mov eax, edx - mov edx, 800h - sub edx, ebp - sar edx, 5 - add edx, edi - mov edi, [esp+4Ch+var_30] - mov [edi+ecx], dx - add ebx, ebx - jmp short loc_4CD494 -; --------------------------------------------------------------------------- - -loc_4CD47B: ; CODE XREF: sub_4CCC80+7DFj - mov ebx, [esp+4Ch+var_30] - sub eax, edx - sub esi, edx - mov dx, di - shr dx, 5 - sub di, dx - mov [ebx+ecx], di - add ebx, 1 - -loc_4CD494: ; CODE XREF: sub_4CCC80+7F9j - sub [esp+4Ch+var_10], 1 - jnz short loc_4CD420 - mov ecx, [esp+4Ch+arg_0] - mov edx, 1 - shl edx, cl - mov ecx, [esp+4Ch+var_1C] - sub ecx, edx - add ebx, ecx - cmp [esp+4Ch+var_34], 4 - mov [esp+4Ch+var_4], ebx - jge loc_4CD69D - add [esp+4Ch+var_34], 7 - cmp ebx, 4 - jl short loc_4CD4CC - mov ebx, 3 - -loc_4CD4CC: ; CODE XREF: sub_4CCC80+845j - mov ecx, [esp+4Ch+var_20] - shl ebx, 7 - lea ecx, [ebx+ecx+360h] - mov [esp+4Ch+var_30], ecx - mov [esp+4Ch+arg_0], 6 - mov edi, 1 - jmp short loc_4CD4F0 -; --------------------------------------------------------------------------- - align 10h - -loc_4CD4F0: ; CODE XREF: sub_4CCC80+86Bj - ; sub_4CCC80+8E3j - cmp eax, 1000000h - mov edx, [esp+4Ch+arg_8] - lea ebx, [edi+edi] - jnb short loc_4CD51A - cmp edx, [esp+4Ch+var_3C] - jz loc_4CD6E2 - movzx ebp, byte ptr [edx] - shl esi, 8 - shl eax, 8 - or esi, ebp - add edx, 1 - mov [esp+4Ch+arg_8], edx - -loc_4CD51A: ; CODE XREF: sub_4CCC80+87Cj - movzx edx, word ptr [ebx+ecx] - movzx ebp, dx - mov ecx, eax - shr ecx, 0Bh - imul ecx, ebp - cmp esi, ecx - jnb short loc_4CD545 - mov eax, ecx - mov ecx, 800h - sub ecx, ebp - sar ecx, 5 - add ecx, edx - mov [esp+4Ch+var_10], ecx - mov edx, ecx - add edi, edi - jmp short loc_4CD556 -; --------------------------------------------------------------------------- - -loc_4CD545: ; CODE XREF: sub_4CCC80+8ABj - sub eax, ecx - sub esi, ecx - mov cx, dx - shr cx, 5 - sub dx, cx - lea edi, [ebx+1] - -loc_4CD556: ; CODE XREF: sub_4CCC80+8C3j - sub [esp+4Ch+arg_0], 1 - mov ecx, [esp+4Ch+var_30] - mov [ebx+ecx], dx - jnz short loc_4CD4F0 - sub edi, 40h - cmp edi, 4 - jl loc_4CD68A - mov ebx, 1 - mov ecx, edi - mov edx, edi - sar ecx, 1 - and edx, ebx - sub ecx, ebx - or edx, 2 - cmp edi, 0Eh - mov [esp+4Ch+var_30], ecx - jge short loc_4CD5A1 - shl edx, cl - mov ecx, [esp+4Ch+var_20] - mov [esp+4Ch+var_38], edx - sub edx, edi - lea ebp, [ecx+edx*2+55Eh] - jmp short loc_4CD5F3 -; --------------------------------------------------------------------------- - -loc_4CD5A1: ; CODE XREF: sub_4CCC80+90Aj - mov edi, [esp+4Ch+arg_8] - sub ecx, 4 - -loc_4CD5A8: ; CODE XREF: sub_4CCC80+958j - cmp eax, 1000000h - jnb short loc_4CD5CA - cmp edi, [esp+4Ch+var_3C] - jz loc_4CD6EF - movzx ebp, byte ptr [edi] - shl esi, 8 - shl eax, 8 - or esi, ebp - add edi, ebx - mov [esp+4Ch+arg_8], edi - -loc_4CD5CA: ; CODE XREF: sub_4CCC80+92Dj - shr eax, 1 - add edx, edx - cmp esi, eax - jb short loc_4CD5D6 - sub esi, eax - or edx, ebx - -loc_4CD5D6: ; CODE XREF: sub_4CCC80+950j - sub ecx, ebx - jnz short loc_4CD5A8 - mov ebp, [esp+4Ch+var_20] - add ebp, 644h - shl edx, 4 - mov [esp+4Ch+var_38], edx - mov [esp+4Ch+var_30], 4 - -loc_4CD5F3: ; CODE XREF: sub_4CCC80+91Fj - mov [esp+4Ch+var_1C], ebx - mov [esp+4Ch+arg_0], ebx - jmp short loc_4CD600 -; --------------------------------------------------------------------------- - align 10h - -loc_4CD600: ; CODE XREF: sub_4CCC80+97Bj - ; sub_4CCC80+A02j - mov ebx, [esp+4Ch+arg_0] - add ebx, ebx - cmp eax, 1000000h - jnb short loc_4CD62D - mov ecx, [esp+4Ch+arg_8] - cmp ecx, [esp+4Ch+var_3C] - jz loc_4CD6E2 - movzx edx, byte ptr [ecx] - shl esi, 8 - shl eax, 8 - or esi, edx - add ecx, 1 - mov [esp+4Ch+arg_8], ecx - -loc_4CD62D: ; CODE XREF: sub_4CCC80+98Bj - movzx edi, word ptr [ebx+ebp] - movzx ecx, di - mov edx, eax - shr edx, 0Bh - imul edx, ecx - cmp esi, edx - jnb short loc_4CD658 - mov eax, edx - mov edx, 800h - sub edx, ecx - sar edx, 5 - add edx, edi - shl [esp+4Ch+arg_0], 1 - mov [ebx+ebp], dx - jmp short loc_4CD679 -; --------------------------------------------------------------------------- - -loc_4CD658: ; CODE XREF: sub_4CCC80+9BEj - mov cx, di - shr cx, 5 - sub di, cx - sub eax, edx - sub esi, edx - mov edx, [esp+4Ch+var_1C] - mov [ebx+ebp], di - add ebx, 1 - or [esp+4Ch+var_38], edx - mov [esp+4Ch+arg_0], ebx - -loc_4CD679: ; CODE XREF: sub_4CCC80+9D6j - shl [esp+4Ch+var_1C], 1 - sub [esp+4Ch+var_30], 1 - jnz loc_4CD600 - jmp short loc_4CD68E -; --------------------------------------------------------------------------- - -loc_4CD68A: ; CODE XREF: sub_4CCC80+8EBj - mov [esp+4Ch+var_38], edi - -loc_4CD68E: ; CODE XREF: sub_4CCC80+A08j - add [esp+4Ch+var_38], 1 - jz loc_4CCFB3 - mov ebx, [esp+4Ch+var_4] - -loc_4CD69D: ; CODE XREF: sub_4CCC80+837j - mov ebp, [esp+4Ch+var_2C] - mov ecx, [esp+4Ch+var_38] - add ebx, 2 - cmp ecx, ebp - ja short loc_4CD6E2 - mov edi, ebp - sub edi, ecx - add edi, [esp+4Ch+arg_10] - -loc_4CD6B4: ; CODE XREF: sub_4CCC80+A60j - mov cl, [edi] - mov edx, [esp+4Ch+arg_10] - sub ebx, 1 - mov [edx+ebp], cl - add ebp, 1 - add edi, 1 - test ebx, ebx - mov byte ptr [esp+4Ch+arg_0], cl - mov [esp+4Ch+var_2C], ebp - jz loc_4CCFA5 - cmp ebp, [esp+4Ch+arg_14] - jnb loc_4CCFB3 - jmp short loc_4CD6B4 -; --------------------------------------------------------------------------- - -loc_4CD6E2: ; CODE XREF: sub_4CCC80+101j - ; sub_4CCC80+1C9j ... - pop edi - pop esi - pop ebp - mov eax, 1 - pop ebx - add esp, 3Ch - retn -; --------------------------------------------------------------------------- - -loc_4CD6EF: ; CODE XREF: sub_4CCC80+933j - pop edi - pop esi - pop ebp - mov eax, ebx - pop ebx - add esp, 3Ch - retn -sub_4CCC80 endp - -_starcraft_decompress_lzma PROC -;sub_4CD700 - -var_14 = byte ptr -14h -var_10 = dword ptr -10h -var_C = dword ptr -0Ch -var_4 = dword ptr -4 -pbInBuffer = dword ptr 4 -cbInBuffer = dword ptr 8 -pbOutBuffer = dword ptr 0Ch -cbOutBuffer = dword ptr 10h -pcbOutBuffer = dword ptr 14h -pfnAllocateMemory= dword ptr 18h -pfnFreeMemory = dword ptr 1Ch - - sub esp, 14h - push ebx - mov ebx, [esp+18h+cbInBuffer] - cmp ebx, 0Eh ; LZMA_PROPS_SIZE + 8 - jnb short loc_4CD714 - xor al, al - pop ebx - add esp, 14h - retn -; --------------------------------------------------------------------------- - -loc_4CD714: ; CODE XREF: Decompress_lzma_internal+Bj - push ebp - mov ebp, [esp+1Ch+pcbOutBuffer] - push edi - mov edi, [esp+20h+pbInBuffer] - mov dword ptr [ebp+0], 0 - cmp byte ptr [edi], 0 - jnz short loc_4CD741 - push 5 - lea eax, [edi+1] - push eax - lea ecx, [esp+28h+var_10] - push ecx - call LzmaProps_Decode - add esp, 0Ch - test eax, eax - jz short loc_4CD74A - -loc_4CD741: ; CODE XREF: Decompress_lzma_internal+28j - pop edi - pop ebp - xor al, al - pop ebx - add esp, 14h - retn -; --------------------------------------------------------------------------- - -loc_4CD74A: ; CODE XREF: Decompress_lzma_internal+3Fj - mov edx, [esp+20h+var_10] - mov eax, [esp+20h+var_C] - lea ecx, [eax+edx] - mov edx, 300h - shl edx, cl - push esi - lea eax, [edx+edx+0E6Ch] - push eax - call [esp+28h+pfnAllocateMemory] - mov esi, eax - add esp, 4 - test esi, esi - mov [esp+24h+var_4], esi - jz short loc_4CD7AA - mov edx, [esp+24h+cbOutBuffer] - mov eax, [esp+24h+pbOutBuffer] - lea ecx, [esp+24h+cbInBuffer] - push ecx ; cbInBuffer - push edx ; cbOutBuffer - push eax ; pbOutBuffer - lea ecx, [esp+30h+var_14] - push ecx ; &var_14 - add ebx, 0FFFFFFF2h - push ebx ; cbInBuffer - LZMA86_HEADER_SIZE - add edi, 0Eh - lea edx, [esp+38h+var_10] - push edi ; pbInBuffer + LZMA86_HEADER_SIZE - push edx ; dest - call sub_4CCC80 - push esi - mov edi, eax - call [esp+44h+pfnFreeMemory] - add esp, 20h - test edi, edi - jz short loc_4CD7B4 - -loc_4CD7AA: ; CODE XREF: Decompress_lzma_internal+74j - pop esi - pop edi - pop ebp - xor al, al - pop ebx - add esp, 14h - retn -; --------------------------------------------------------------------------- - -loc_4CD7B4: ; CODE XREF: Decompress_lzma_internal+A8j - mov eax, [esp+24h+cbInBuffer] - pop esi - pop edi - mov [ebp+0], eax - pop ebp - mov al, 1 - pop ebx - add esp, 14h - retn -_starcraft_decompress_lzma ENDP - -; =============== S U B R O U T I N E ======================================= - -sub_4CD7D0 proc near ; CODE XREF: sub_4CDC00+74p - -arg_0 = dword ptr 4 - - push esi - mov esi, [esp+4+arg_0] - or esi, 8 - xor eax, eax - cmp esi, 1 - jz short loc_4CD806 - push edi - -loc_4CD7E0: ; CODE XREF: sub_4CD7D0+33j - mov edx, esi - shr esi, 1 - mov edi, [ecx+esi*4] - and edx, 1 - sub edi, edx - neg edx - xor edi, edx - shr edi, 2 - and edi, 1FFh - add eax, dword_550998[edi*4] - cmp esi, 1 - jnz short loc_4CD7E0 - pop edi - -loc_4CD806: ; CODE XREF: sub_4CD7D0+Dj - pop esi - retn 4 -sub_4CD7D0 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -sub_4CD810 proc near ; CODE XREF: CEncoder_FillAlignPrices+17p - -var_4 = dword ptr -4 -arg_0 = dword ptr 4 - - push ecx - push ebx - push ebp - push esi - xor eax, eax - push edi - mov edi, [esp+14h+arg_0] - mov [esp+14h+var_4], ecx - lea esi, [eax+1] - lea ebx, [eax+4] - -loc_4CD825: ; CODE XREF: sub_4CD810+42j - mov ecx, [esp+14h+var_4] - mov ecx, [ecx+esi*4] - mov edx, edi - and edx, 1 - sub ecx, edx - mov ebp, edx - neg ebp - xor ecx, ebp - shr ecx, 2 - and ecx, 1FFh - add eax, dword_550998[ecx*4] - add esi, esi - shr edi, 1 - or esi, edx - sub ebx, 1 - jnz short loc_4CD825 - pop edi - pop esi - pop ebp - pop ebx - pop ecx - retn 4 -sub_4CD810 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -sub_4CD860 proc near ; CODE XREF: CEncoder_FillDistancesPrices+6Bp - -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 -arg_8 = dword ptr 0Ch - - push edi - mov edi, [esp+4+arg_4] - xor eax, eax - test edi, edi - lea edx, [eax+1] - jz short loc_4CD8A7 - push ebx - push ebp - push esi - mov esi, [esp+10h+arg_8] - -loc_4CD875: ; CODE XREF: sub_4CD860+42j - mov ebx, [esp+10h+arg_0] - mov ebx, [ebx+edx*4] - mov ecx, esi - and ecx, 1 - sub ebx, ecx - mov ebp, ecx - neg ebp - xor ebx, ebp - shr ebx, 2 - and ebx, 1FFh - add eax, dword_550998[ebx*4] - add edx, edx - shr esi, 1 - or edx, ecx - sub edi, 1 - jnz short loc_4CD875 - pop esi - pop ebp - pop ebx - -loc_4CD8A7: ; CODE XREF: sub_4CD860+Cj - pop edi - retn -sub_4CD860 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -sub_4CD8B0 proc near ; CODE XREF: CEncoder_CodeOneBlock+7B8p - mov eax, [ecx+24h] - mov edx, [ecx+1Ch] - push ebx - push esi - mov esi, [ecx+30h] - xor ebx, ebx - push edi - mov edi, [ecx+34h] - sub esi, eax - sbb edi, ebx - add esi, edx - adc edi, ebx - cmp eax, edx - jbe short loc_4CD8D4 - mov eax, [ecx+28h] - add esi, eax - adc edi, ebx - -loc_4CD8D4: ; CODE XREF: sub_4CD8B0+1Bj - mov eax, [ecx] - xor edx, edx - add eax, esi - adc edx, edi - pop edi - add eax, 4 - pop esi - adc edx, ebx - pop ebx - retn -sub_4CD8B0 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -sub_4CD8F0 proc near ; CODE XREF: sub_4CD940+21p - ; .text:loc_4CF730j ... - mov eax, dword_553598 - test eax, eax - push esi - mov esi, ecx - push edi - mov edi, [esi] - jnz short loc_4CD92A - push 0Ch ; Size - call _operator_new ; operator new(uint) - add esp, 4 - test eax, eax - jz short loc_4CD923 - mov dword ptr [eax], 0 - mov dword ptr [eax+4], 0 - mov dword ptr [eax+8], 0 - jmp short loc_4CD925 -; --------------------------------------------------------------------------- - -loc_4CD923: ; CODE XREF: sub_4CD8F0+1Bj - xor eax, eax - -loc_4CD925: ; CODE XREF: sub_4CD8F0+31j - mov dword_553598, eax - -loc_4CD92A: ; CODE XREF: sub_4CD8F0+Dj - mov eax, [eax+4] - test eax, eax - jz short loc_4CD937 - push edi - call eax - add esp, 4 - -loc_4CD937: ; CODE XREF: sub_4CD8F0+3Fj - pop edi - mov dword ptr [esi], 0 - pop esi - retn -sub_4CD8F0 endp - -; =============== S U B R O U T I N E ======================================= - -sub_4CD940 proc near ; CODE XREF: sub_4D0270+74p - -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 - - push ebx - mov ebx, [esp+4+arg_4] - push esi - mov esi, ecx - cmp dword ptr [esi], 0 - push edi - mov edi, [esp+0Ch+arg_0] - jz short loc_4CD95F - mov eax, [esi+8] - add eax, [esi+4] - lea ecx, [edi+ebx] - cmp ecx, eax - jz short loc_4CD981 - -loc_4CD95F: ; CODE XREF: sub_4CD940+10j - mov ecx, esi - call sub_4CD8F0 - lea ecx, [edi+ebx] - mov edx, 1 - shl edx, cl - imul edx, 0C00h - push edx - call sub_4CB8A0 - add esp, 4 - mov [esi], eax - -loc_4CD981: ; CODE XREF: sub_4CD940+1Dj - mov ecx, edi - mov eax, 1 - shl eax, cl - mov [esi+8], edi - pop edi - mov [esi+4], ebx - sub eax, 1 - mov [esi+0Ch], eax - xor eax, eax - cmp [esi], eax - pop esi - setnz al - pop ebx - retn 8 -sub_4CD940 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - - -sub_4CD9B0 proc near ; CODE XREF: CEncoder_GetOptimum+436p - ; CEncoder_GetOptimum+AE1p - -arg_0 = dword ptr 4 -arg_4 = byte ptr 8 -arg_8 = dword ptr 0Ch - - mov edx, [esp+arg_0] - test edx, edx - push esi - jnz short loc_4CD9F2 - movzx eax, [esp+4+arg_4] - mov edx, eax - shl edx, 4 - add edx, [esp+4+arg_8] - mov esi, 800h - sub esi, [ecx+edx*4+284B0h] - mov ecx, [ecx+eax*4+28420h] - shr esi, 2 - mov eax, dword_550998[esi*4] - shr ecx, 2 - add eax, dword_550998[ecx*4] - pop esi - retn 0Ch -; --------------------------------------------------------------------------- - -loc_4CD9F2: ; CODE XREF: sub_4CD9B0+7j - movzx esi, [esp+4+arg_4] - mov eax, 800h - sub eax, [ecx+esi*4+28420h] - shr eax, 2 - cmp edx, 1 - mov eax, dword_550998[eax*4] - jnz short loc_4CDA27 - mov ecx, [ecx+esi*4+28450h] - shr ecx, 2 - add eax, dword_550998[ecx*4] - pop esi - retn 0Ch -; --------------------------------------------------------------------------- - -loc_4CDA27: ; CODE XREF: sub_4CD9B0+60j - push edi - add edx, 0FFFFFFFEh - mov edi, 800h - sub edi, [ecx+esi*4+28450h] - mov ecx, [ecx+esi*4+28480h] - sub ecx, edx - neg edx - xor ecx, edx - shr edi, 2 - mov edx, dword_550998[edi*4] - shr ecx, 2 - and ecx, 1FFh - add edx, dword_550998[ecx*4] - pop edi - add eax, edx - pop esi - retn 0Ch -sub_4CD9B0 endp - -; --------------------------------------------------------------------------- - align 10h - -; =============== S U B R O U T I N E ======================================= - - -sub_4CDA70 proc near ; CODE XREF: CEncoder_CodeReal+10Cp - ; CEncoder_CodeReal+134p ... - push esi - mov esi, [ecx] - cmp dword ptr [esi+80h], 0 - jz short loc_4CDA8C - cmp byte ptr [esi+33B95h], 0 - jz short loc_4CDA8C - mov byte ptr [esi+33B95h], 0 - -loc_4CDA8C: ; CODE XREF: sub_4CDA70+Aj - ; sub_4CDA70+13j - mov eax, [esi+33B8Ch] - test eax, eax - jz short loc_4CDAA8 - mov ecx, [eax] - mov edx, [ecx+8] - push eax - call edx - mov dword ptr [esi+33B8Ch], 0 - -loc_4CDAA8: ; CODE XREF: sub_4CDA70+24j - mov ecx, [esi+4] - mov edx, [ecx+10h] - lea eax, [esi+4] - push eax - call edx - pop esi - retn -sub_4CDA70 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - - -sub_4CDAC0 proc near ; CODE XREF: CEncoder_GetOptimum+2C4p - ; CEncoder_GetOptimum+7ACp ... - -var_4 = dword ptr -4 -arg_0 = dword ptr 4 -arg_4 = byte ptr 8 -arg_8 = byte ptr 0Ch - - push ecx - push ebx - push ebp - xor ebp, ebp - cmp byte ptr [esp+0Ch+arg_0], 0 - push esi - mov eax, ecx - push edi - mov [esp+14h+var_4], eax - lea edi, [ebp+1] - lea ebx, [ebp+8] - jz short loc_4CDB3B - movzx ecx, [esp+14h+arg_8] - mov [esp+14h+arg_0], ecx - -loc_4CDAE3: ; CODE XREF: sub_4CDAC0+71j - movzx esi, [esp+14h+arg_4] - mov edx, [esp+14h+arg_0] - sub ebx, 1 - movzx ecx, bl - shr esi, cl - shr edx, cl - and esi, 1 - mov ecx, esi - shl ecx, 8 - add ecx, edi - mov eax, [eax+ecx*4+400h] - and edx, 1 - sub eax, edx - mov ecx, edx - neg ecx - xor eax, ecx - shr eax, 2 - and eax, 1FFh - add ebp, dword_550998[eax*4] - add edi, edi - or edi, edx - cmp esi, edx - jnz short loc_4CDB33 - test ebx, ebx - jz short loc_4CDB75 - mov eax, [esp+14h+var_4] - jmp short loc_4CDAE3 -; --------------------------------------------------------------------------- - -loc_4CDB33: ; CODE XREF: sub_4CDAC0+67j - test ebx, ebx - jz short loc_4CDB75 - mov eax, [esp+14h+var_4] - -loc_4CDB3B: ; CODE XREF: sub_4CDAC0+18j - movzx edx, [esp+14h+arg_8] - mov [esp+14h+arg_0], edx - -loc_4CDB44: ; CODE XREF: sub_4CDAC0+B3j - mov edx, [esp+14h+arg_0] - sub ebx, 1 - mov cl, bl - shr edx, cl - mov ecx, [eax+edi*4] - add edi, edi - and edx, 1 - sub ecx, edx - mov esi, edx - neg esi - xor ecx, esi - shr ecx, 2 - and ecx, 1FFh - add ebp, dword_550998[ecx*4] - or edi, edx - test ebx, ebx - jnz short loc_4CDB44 - -loc_4CDB75: ; CODE XREF: sub_4CDAC0+6Bj - ; sub_4CDAC0+75j - pop edi - pop esi - mov eax, ebp - pop ebp - pop ebx - pop ecx - retn 0Ch -sub_4CDAC0 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -sub_4CDB80 proc near ; CODE XREF: sub_4CDEA0+11Ep - ; sub_4CDEA0+137p - -arg_0 = dword ptr 4 - - push esi - mov esi, [esp+4+arg_0] - test esi, esi - mov edx, 400h - push edi - mov [ecx], edx - mov [ecx+4], edx - jbe short loc_4CDBE6 - lea eax, [ecx+20Ch] - lea ebx, [ebx+0] - -loc_4CDBA0: ; CODE XREF: sub_4CDB80+64j - mov [eax-200h], edx - mov [eax-1FCh], edx - mov [eax-1F8h], edx - mov [eax-1F4h], edx - mov [eax-1F0h], edx - mov [eax-1ECh], edx - mov [eax-1E8h], edx - mov [eax], edx - mov [eax+4], edx - mov [eax+8], edx - mov [eax+0Ch], edx - mov [eax+10h], edx - mov [eax+14h], edx - mov [eax+18h], edx - add eax, 20h - sub esi, 1 - jnz short loc_4CDBA0 - -loc_4CDBE6: ; CODE XREF: sub_4CDB80+12j - lea edi, [ecx+40Ch] - mov ecx, 0FFh - mov eax, edx - rep stosd - pop edi - pop esi - retn 4 -sub_4CDB80 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -sub_4CDC00 proc near ; CODE XREF: sub_4CF740+22p - ; sub_4CF780+2Bp - -var_C = dword ptr -0Ch -var_8 = dword ptr -8 -var_4 = dword ptr -4 -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 -arg_8 = dword ptr 0Ch - - sub esp, 0Ch - push ebx - push ebp - push esi - push edi - mov edi, ecx - mov eax, [edi] - mov ebx, [esp+1Ch+arg_0] - mov ecx, eax - shr ecx, 2 - mov edx, dword_550998[ecx*4] - mov ecx, 800h - sub ecx, eax - shr ecx, 2 - mov eax, dword_550998[ecx*4] - mov ecx, [edi+4] - mov [esp+1Ch+var_C], edx - mov edx, ecx - shr edx, 2 - mov ebp, dword_550998[edx*4] - mov edx, 800h - sub edx, ecx - shr edx, 2 - mov ecx, dword_550998[edx*4] - add ebp, eax - add ecx, eax - mov [esp+1Ch+var_8], ebp - mov [esp+1Ch+var_4], ecx - xor esi, esi - mov edi, edi - -loc_4CDC60: ; CODE XREF: sub_4CDC00+8Aj - cmp esi, [esp+1Ch+arg_4] - jnb loc_4CDD62 - mov eax, ebx - shl eax, 5 - push esi - lea ecx, [eax+edi+8] - call sub_4CD7D0 - add eax, [esp+1Ch+var_C] - mov ecx, [esp+1Ch+arg_8] - mov [ecx+esi*4], eax - add esi, 1 - cmp esi, 8 - jb short loc_4CDC60 - cmp esi, 10h - jnb short loc_4CDD00 - lea ecx, [esi-8] - mov [esp+1Ch+arg_0], ecx - jmp short loc_4CDCA0 -; --------------------------------------------------------------------------- - align 10h - -loc_4CDCA0: ; CODE XREF: sub_4CDC00+98j - ; sub_4CDC00+FEj - cmp esi, [esp+1Ch+arg_4] - jnb loc_4CDD62 - or ecx, 8 - xor edx, edx - cmp ecx, 1 - jz short loc_4CDCE4 - -loc_4CDCB4: ; CODE XREF: sub_4CDC00+DEj - mov eax, ecx - shr ecx, 1 - and eax, 1 - lea ebp, [ecx+ebx*8] - mov ebp, [edi+ebp*4+208h] - sub ebp, eax - neg eax - xor ebp, eax - shr ebp, 2 - and ebp, 1FFh - add edx, dword_550998[ebp*4] - cmp ecx, 1 - jnz short loc_4CDCB4 - mov ebp, [esp+1Ch+var_8] - -loc_4CDCE4: ; CODE XREF: sub_4CDC00+B2j - mov eax, [esp+1Ch+arg_8] - mov ecx, [esp+1Ch+arg_0] - add edx, ebp - mov [eax+esi*4], edx - add esi, 1 - add ecx, 1 - cmp esi, 10h - mov [esp+1Ch+arg_0], ecx - jb short loc_4CDCA0 - -loc_4CDD00: ; CODE XREF: sub_4CDC00+8Fj - cmp esi, [esp+1Ch+arg_4] - jnb short loc_4CDD62 - lea ebx, [esi-10h] - lea esp, [esp+0] - -loc_4CDD10: ; CODE XREF: sub_4CDC00+160j - mov ecx, ebx - or ecx, 100h - xor edx, edx - cmp ecx, 1 - jz short loc_4CDD49 - nop - -loc_4CDD20: ; CODE XREF: sub_4CDC00+147j - mov eax, ecx - shr ecx, 1 - mov ebp, [edi+ecx*4+408h] - and eax, 1 - sub ebp, eax - neg eax - xor ebp, eax - shr ebp, 2 - and ebp, 1FFh - add edx, dword_550998[ebp*4] - cmp ecx, 1 - jnz short loc_4CDD20 - -loc_4CDD49: ; CODE XREF: sub_4CDC00+11Dj - mov ecx, [esp+1Ch+var_4] - mov eax, [esp+1Ch+arg_8] - add edx, ecx - mov [eax+esi*4], edx - add esi, 1 - add ebx, 1 - cmp esi, [esp+1Ch+arg_4] - jb short loc_4CDD10 - -loc_4CDD62: ; CODE XREF: sub_4CDC00+64j - ; sub_4CDC00+A4j ... - pop edi - pop esi - pop ebp - pop ebx - add esp, 0Ch - retn 0Ch -sub_4CDC00 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -; int __cdecl sub_4CDD70(SIZE_T dwSize) -sub_4CDD70 proc near ; DATA XREF: .data:off_546E20o - -dwSize = dword ptr 4 - - mov eax, [esp+dwSize] - test eax, eax - jnz short loc_4CDD79 - retn -; --------------------------------------------------------------------------- - -loc_4CDD79: ; CODE XREF: sub_4CDD70+6j - push 4 ; flProtect - push 1000h ; flAllocationType - push eax ; dwSize - push 0 ; lpAddress - call ds:VirtualAlloc - retn -sub_4CDD70 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - - -; int __cdecl sub_4CDD90(void * lpAddress) -sub_4CDD90 proc near ; CODE XREF: sub_4CF810+53p - ; sub_4CF810+73p - ; DATA XREF: ... - -lpAddress = dword ptr 4 - - mov eax, [esp+lpAddress] - test eax, eax - jz short locret_4CDDA6 - push 8000h ; dwFreeType - push 0 ; dwSize - push eax ; lpAddress - call ds:VirtualFree - -locret_4CDDA6: ; CODE XREF: sub_4CDD90+6j - retn -sub_4CDD90 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - - -sub_4CDDB0 proc near ; DATA XREF: .rdata:00517AC8o - -var_10 = dword ptr -10h -var_C = byte ptr -0Ch -var_B = byte ptr -0Bh -var_A = byte ptr -0Ah -var_9 = byte ptr -9 -var_8 = byte ptr -8 -var_4 = dword ptr -4 -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 - - sub esp, 10h - mov eax, dword_544960 - xor eax, esp - mov [esp+10h+var_4], eax - mov ecx, [esp+10h+arg_0] - mov al, [ecx+33B50h] - mov dl, 5 - imul dl - add al, [ecx+33B58h] - mov dl, 9 - imul dl - add al, [ecx+33B5Ch] - push ebp - mov ebp, [esp+14h+arg_4] - mov [esp+14h+var_C], al - mov eax, [ecx+33B60h] - mov ecx, eax - mov edx, eax - push esi - shr ecx, 8 - shr edx, 10h - mov [esp+18h+var_B], al - shr eax, 18h - push edi - mov [esp+1Ch+var_A], cl - mov [esp+1Ch+var_9], dl - mov [esp+1Ch+var_8], al - mov esi, 5 - lea edi, [esp+1Ch+var_C] - -loc_4CDE13: ; CODE XREF: sub_4CDDB0+85j - mov eax, [ebp+0] - mov edx, [eax+0Ch] - lea ecx, [esp+1Ch+var_10] - push ecx - push esi - push edi - push ebp - call edx - mov ecx, [esp+1Ch+var_10] - add edi, ecx - sub esi, ecx - test eax, eax - jnz short loc_4CDE37 - test ecx, ecx - jz short loc_4CDE4B - test esi, esi - jnz short loc_4CDE13 - -loc_4CDE37: ; CODE XREF: sub_4CDDB0+7Dj - pop edi - pop esi - pop ebp - mov ecx, [esp+10h+var_4] - xor ecx, esp - call sub_4A0686 - add esp, 10h - retn 8 -; --------------------------------------------------------------------------- - -loc_4CDE4B: ; CODE XREF: sub_4CDDB0+81j - mov ecx, [esp+1Ch+var_4] - pop edi - pop esi - pop ebp - xor ecx, esp - mov eax, 80004005h - call sub_4A0686 - add esp, 10h - retn 8 -sub_4CDDB0 endp - - align 10h - -; =============== S U B R O U T I N E ======================================= - -sub_4CDE70 proc near ; DATA XREF: .rdata:00517AF4o - -arg_0 = dword ptr 4 - - push esi - mov esi, [esp+4+arg_0] - mov eax, [esi+50h] - test eax, eax - jz short loc_4CDE8B - mov ecx, [eax] - mov edx, [ecx+8] - push eax - call edx - mov dword ptr [esi+50h], 0 - -loc_4CDE8B: ; CODE XREF: sub_4CDE70+Aj - xor eax, eax - pop esi - retn 4 -sub_4CDE70 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - - -sub_4CDEA0 proc near ; CODE XREF: CEncoder_SetStreams+3Cp - -var_4 = dword ptr -4 - - push ecx - push ebx - xor ebx, ebx - push ebp - push esi - mov esi, ecx - mov byte ptr [esi+10h], 0 - mov byte ptr [esi+11h], 0 - mov [esi+14h], ebx - mov [esi+18h], ebx - mov [esi+1Ch], ebx - mov [esi+20h], ebx - mov eax, [esi+50h] - mov [esi+4Ch], ebx - mov [esi+48h], eax - mov [esi+44h], ebx - mov [esi+58h], ebx - mov [esi+5Ch], ebx - mov [esi+64h], bl - push edi - mov [esi+30h], ebx - mov [esi+34h], ebx - mov dword ptr [esi+38h], 0FFFFFFFFh - mov dword ptr [esi+28h], 1 - mov [esi+2Ch], bl - lea ecx, [esi+28420h] - lea edi, [esi+284B0h] - mov [esp+14h+var_4], 0Ch - mov ebp, 400h - -loc_4CDF02: ; CODE XREF: sub_4CDEA0+9Cj - xor edx, edx - mov eax, edi - jmp short loc_4CDF10 -; --------------------------------------------------------------------------- - align 10h - -loc_4CDF10: ; CODE XREF: sub_4CDEA0+66j - ; sub_4CDEA0+84j - mov [eax-3C0h], ebp - mov [eax], ebp - add edx, 1 - add eax, 4 - cmp edx, [esi+33B60h] - jbe short loc_4CDF10 - mov [ecx-30h], ebp - mov [ecx], ebp - mov [ecx+30h], ebp - mov [ecx+60h], ebp - add ecx, 4 - add edi, 40h - sub [esp+14h+var_4], 1 - jnz short loc_4CDF02 - mov ecx, [esi+32658h] - add ecx, [esi+32654h] - mov eax, 1 - shl eax, cl - cmp eax, ebx - jbe short loc_4CDF7C - xor edx, edx - mov ebx, eax - lea esp, [esp+0] - -loc_4CDF60: ; CODE XREF: sub_4CDEA0+DAj - mov edi, [esi+32650h] - add edi, edx - mov ecx, 300h - mov eax, ebp - add edx, 0C00h - sub ebx, 1 - rep stosd - jnz short loc_4CDF60 - -loc_4CDF7C: ; CODE XREF: sub_4CDEA0+B3j - lea edx, [esi+287B4h] - mov ebx, 4 - -loc_4CDF87: ; CODE XREF: sub_4CDEA0+FBj - mov edi, edx - mov ecx, 3Fh - mov eax, ebp - add edx, 100h - sub ebx, 1 - rep stosd - jnz short loc_4CDF87 - lea edi, [esi+28BB0h] - mov ecx, 72h - rep stosd - mov ecx, [esi+33B5Ch] - mov edx, 1 - shl edx, cl - lea ecx, [esi+28DB8h] - push edx - call sub_4CDB80 - mov ecx, [esi+33B5Ch] - mov eax, 1 - shl eax, cl - lea ecx, [esi+2DA04h] - push eax - call sub_4CDB80 - mov eax, ebp - mov [esi+28D7Ch], eax - mov [esi+28D80h], eax - mov [esi+28D84h], eax - mov [esi+28D88h], eax - mov [esi+28D8Ch], eax - mov [esi+28D90h], eax - mov [esi+28D94h], eax - mov [esi+28D98h], eax - mov [esi+28D9Ch], eax - mov [esi+28DA0h], eax - mov [esi+28DA4h], eax - mov [esi+28DA8h], eax - mov [esi+28DACh], eax - mov [esi+28DB0h], eax - mov [esi+28DB4h], eax - xor eax, eax - pop edi - mov [esi+32F10h], bl - mov [esi+32F08h], eax - mov [esi+32F0Ch], eax - mov [esi+32F04h], eax - pop esi - pop ebp - pop ebx - pop ecx - retn -sub_4CDEA0 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -sub_4CF450 proc near ; CODE XREF: .text:004CF692p - ; CEncoder_Flush+42p - -var_8 = dword ptr -8 -var_4 = dword ptr -4 - - sub esp, 8 - push esi - mov esi, ecx - mov eax, [esi+0Ch] - push edi - mov edi, [esi+4] - cmp eax, edi - jb short loc_4CF464 - mov edi, [esi+10h] - -loc_4CF464: ; CODE XREF: sub_4CF450+Fj - push ebp - mov ebp, [esi+20h] - xor edx, edx - sub edi, eax - cmp ebp, edx - mov [esp+14h+var_4], edx - jz short loc_4CF4BA - push ebx - mov ebx, [esi] - add ebx, eax - mov eax, dword_553598 - cmp eax, edx - jnz short loc_4CF4A5 - push 0Ch ; Size - call _operator_new ; operator new(uint) - xor ecx, ecx - add esp, 4 - cmp eax, ecx - jz short loc_4CF49C - mov [eax], ecx - mov [eax+4], ecx - mov [eax+8], ecx - jmp short loc_4CF49E -; --------------------------------------------------------------------------- - -loc_4CF49C: ; CODE XREF: sub_4CF450+40j - xor eax, eax - -loc_4CF49E: ; CODE XREF: sub_4CF450+4Aj - mov dword_553598, eax - xor edx, edx - -loc_4CF4A5: ; CODE XREF: sub_4CF450+30j - mov eax, [eax+8] - cmp eax, edx - jz short loc_4CF4B6 - push edi - push ebx - push ebp - call eax - add esp, 0Ch - xor edx, edx - -loc_4CF4B6: ; CODE XREF: sub_4CF450+5Aj - add [esi+20h], edi - pop ebx - -loc_4CF4BA: ; CODE XREF: sub_4CF450+22j - mov eax, [esi+14h] - cmp eax, edx - pop ebp - jz short loc_4CF4E4 - mov [esp+10h+var_8], edx - mov ecx, [eax] - lea edx, [esp+10h+var_8] - push edx - mov edx, [esi] - add edx, [esi+0Ch] - push edi - push edx - push eax - mov eax, [ecx+0Ch] - call eax - mov edi, [esp+10h+var_8] - mov [esp+10h+var_4], eax - xor edx, edx - -loc_4CF4E4: ; CODE XREF: sub_4CF450+70j - add [esi+0Ch], edi - mov ecx, [esi+0Ch] - mov eax, [esi+10h] - cmp ecx, eax - jnz short loc_4CF4F4 - mov [esi+0Ch], edx - -loc_4CF4F4: ; CODE XREF: sub_4CF450+9Fj - cmp [esi+4], eax - jnz short loc_4CF500 - mov byte ptr [esi+24h], 1 - mov [esi+4], edx - -loc_4CF500: ; CODE XREF: sub_4CF450+A7j - mov ecx, [esi+0Ch] - cmp ecx, [esi+4] - jbe short loc_4CF50A - mov eax, ecx - -loc_4CF50A: ; CODE XREF: sub_4CF450+B6j - add [esi+18h], edi - mov [esi+8], eax - mov eax, [esp+10h+var_4] - adc [esi+1Ch], edx - pop edi - pop esi - add esp, 8 - retn -sub_4CF450 endp - -; --------------------------------------------------------------------------- - align 10h - - -; --------------------------------------------------------------------------- - -; int __stdcall Interface1_QueryInterface(int, void *Buf1, int) -Interface1_QueryInterface proc near ; DATA XREF: .rdata:off_517A90o -; sub_4CF520 - -arg_0 = dword ptr 4 -Buf1 = dword ptr 8 -arg_8 = dword ptr 0Ch - - mov eax, [esp+Buf1] - push offset dword_512730 ; Buf2 - push eax ; Buf1 - call unknown_libname_324 ; MFC 3.1/4.0/4.2/8.0 32bit - add esp, 8 - test eax, eax - jz short loc_4CF54D - mov eax, [esp+arg_0] - mov ecx, [esp+arg_8] - mov [ecx], eax - mov edx, [eax] - push eax - mov eax, [edx+4] - call eax - xor eax, eax - retn 0Ch -loc_4CF54D: ; CODE XREF: Interface1_QueryInterface+14j - mov eax, 80004002h - retn 0Ch -Interface1_QueryInterface endp - -; --------------------------------------------------------------------------- - -; int __stdcall Interface1_Release(void *pUnknown) -Interface1_Release proc near ; DATA XREF: .rdata:00517A98o - -pUnknown = dword ptr 4 - - mov ecx, [esp+pUnknown] - add dword ptr [ecx+4], 0FFFFFFFFh - mov eax, [ecx+4] - jnz short locret_4CF585 - push ecx ; Memory - mov dword ptr [ecx], offset off_517A90 - mov dword ptr [ecx+8], 0 - call j__free - add esp, 4 - xor eax, eax - -locret_4CF585: ; CODE XREF: Interface1_Release+Bj - retn 4 -Interface1_Release endp - -; --------------------------------------------------------------------------- - -sub_4CF590 proc near ; DATA XREF: .rdata:00517AB4o - -arg_0 = dword ptr 4 -arg_4 = byte ptr 8 -arg_8 = dword ptr 0Ch -arg_C = dword ptr 10h - - mov eax, [esp+arg_0] - mov edx, [eax+10h] - sub edx, [eax+18h] - mov ecx, [eax+14h] - sbb ecx, [eax+1Ch] - push ebx - mov ebx, [esp+4+arg_8] - push edi - mov edi, [esp+8+arg_C] - cmp edi, ecx - jb short loc_4CF5C2 - ja short loc_4CF5B4 - cmp ebx, edx - jbe short loc_4CF5C2 - -loc_4CF5B4: ; CODE XREF: sub_4CF590+1Ej - pop edi - mov byte ptr [eax+20h], 1 - mov eax, 80004005h - pop ebx - retn 10h -; --------------------------------------------------------------------------- - -loc_4CF5C2: ; CODE XREF: sub_4CF590+1Cj - ; sub_4CF590+22j - push esi - xor esi, esi - xor ecx, ecx - test edi, edi - jb short loc_4CF5FA - ja short loc_4CF5D1 - test ebx, ebx - jbe short loc_4CF5FA - -loc_4CF5D1: ; CODE XREF: sub_4CF590+3Bj - mov dl, [esp+0Ch+arg_4] - push ebp - -loc_4CF5D6: ; CODE XREF: sub_4CF590+5Fj - ; sub_4CF590+67j - mov ebp, [eax+18h] - mov ebx, [eax+8] - mov [ebx+ebp], dl - add dword ptr [eax+18h], 1 - adc dword ptr [eax+1Ch], 0 - add esi, 1 - adc ecx, 0 - cmp ecx, edi - jb short loc_4CF5D6 - ja short loc_4CF5F9 - cmp esi, [esp+10h+arg_8] - jb short loc_4CF5D6 - -loc_4CF5F9: ; CODE XREF: sub_4CF590+61j - pop ebp - -loc_4CF5FA: ; CODE XREF: sub_4CF590+39j - ; sub_4CF590+3Fj - pop esi - pop edi - xor eax, eax - pop ebx - retn 10h -sub_4CF590 endp - -; =============== S U B R O U T I N E ======================================= - -; int __stdcall Interface2_Release(void *pUnknown) -Interface2_Release proc near ; DATA XREF: .rdata:00517AACo -; sub_4CF610 - -pUnknown = dword ptr 4 - - mov ecx, [esp+pUnknown] - add dword ptr [ecx+4], 0FFFFFFFFh - mov eax, [ecx+4] - jnz short locret_4CF635 - push ecx ; Memory - mov dword ptr [ecx], offset off_517AA4 - mov dword ptr [ecx+8], 0 - call j__free - add esp, 4 - xor eax, eax - -locret_4CF635: ; CODE XREF: Interface2_Release+Bj - retn 4 -Interface2_Release endp - -; =============== S U B R O U T I N E ======================================= - -loc_4CF640: ; CODE XREF: sub_4CF6E0+39p - ; sub_4CFAC0+57p ... - push ecx - push ebx - push esi - push edi - mov edi, ecx - mov esi, [edi+8] - cmp esi, 0FF000000h - jb short loc_4CF661 - mov edx, [edi+0Ch] - mov eax, esi - mov cl, 20h - call __allshr ; Microsoft VisualC 2-8/net runtime - test eax, eax - jz short loc_4CF6B6 - -loc_4CF661: ; CODE XREF: .text:004CF64Fj - mov bl, [edi+4] - lea esi, [edi+18h] - -loc_4CF667: ; CODE XREF: .text:004CF6A9j - mov eax, [edi+8] - mov edx, [edi+0Ch] - mov cl, 20h - call __allshr ; Microsoft VisualC 2-8/net runtime - mov ecx, [esi+4] - mov edx, [esi] - add al, bl - mov [ecx+edx], al - add dword ptr [esi+4], 1 - mov eax, [esi+4] - cmp eax, [esi+8] - jnz short loc_4CF6A3 - cmp [esi+0Ch], eax - jz short loc_4CF6A3 - nop - -loc_4CF690: ; CODE XREF: .text:004CF6A1j - mov ecx, esi - call sub_4CF450 - test eax, eax - jnz short loc_4CF6CB - mov eax, [esi+0Ch] - cmp eax, [esi+4] - jnz short loc_4CF690 - -loc_4CF6A3: ; CODE XREF: .text:004CF688j - ; .text:004CF68Dj - or bl, 0FFh - add dword ptr [edi], 0FFFFFFFFh - jnz short loc_4CF667 - mov esi, [edi+8] - mov edx, esi - shr edx, 18h - mov [edi+4], dl - -loc_4CF6B6: ; CODE XREF: .text:004CF65Fj - add dword ptr [edi], 1 - shl esi, 8 - mov [edi+8], esi - mov dword ptr [edi+0Ch], 0 - pop edi - pop esi - pop ebx - pop ecx - retn - -; --------------------------------------------------------------------------- - -loc_4CF6CB: ; CODE XREF: .text:004CF699j - push offset dword_526DD0 - lea ecx, [esp+10h] - push ecx - mov [esp+14h], eax - int 3 -; call __CxxThrowException@8 ; _CxxThrowException(x,x) -; --------------------------------------------------------------------------- - - db 2 dup(0CCh) - -; =============== S U B R O U T I N E ======================================= - - -sub_4CF6E0 proc near ; CODE XREF: sub_4D0770+FBp - ; CEncoder_CodeOneBlock+6C5p - -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 - - push esi - push edi - mov edi, [esp+8+arg_4] - add edi, 0FFFFFFFFh - mov esi, ecx - js short loc_4CF724 - push ebp - mov ebp, [esp+0Ch+arg_0] - -loc_4CF6F2: ; CODE XREF: sub_4CF6E0+41j - shr dword ptr [esi+10h], 1 - mov eax, [esi+10h] - mov edx, ebp - mov ecx, edi - shr edx, cl - test dl, 1 - jz short loc_4CF70A - add [esi+8], eax - adc dword ptr [esi+0Ch], 0 - -loc_4CF70A: ; CODE XREF: sub_4CF6E0+21j - cmp eax, 1000000h - jnb short loc_4CF71E - shl eax, 8 - mov ecx, esi - mov [esi+10h], eax - call loc_4CF640 - -loc_4CF71E: ; CODE XREF: sub_4CF6E0+2Fj - sub edi, 1 - jns short loc_4CF6F2 - pop ebp - -loc_4CF724: ; CODE XREF: sub_4CF6E0+Bj - pop edi - pop esi - retn 8 -sub_4CF6E0 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -sub_4CF740 proc near ; CODE XREF: sub_4D0770+DFp - ; CEncoder_CodeOneBlock+58Ap ... - -arg_0 = dword ptr 4 - - push esi - push edi - mov edi, [esp+8+arg_0] - mov eax, edi - imul eax, 440h - mov esi, ecx - mov edx, [esi+4C08h] - lea ecx, [eax+esi+808h] - push ecx - push edx - push edi - mov ecx, esi - call sub_4CDC00 - mov eax, [esi+4C08h] - mov [esi+edi*4+4C0Ch], eax - pop edi - pop esi - retn 4 -sub_4CF740 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -sub_4CF780 proc near ; CODE XREF: CEncoder_SetStreams+7Ep - ; CEncoder_SetStreams+A6p - -arg_0 = dword ptr 4 - - push esi - push edi - xor edi, edi - cmp [esp+8+arg_0], edi - mov esi, ecx - jbe short loc_4CF7CC - push ebx - push ebp - lea ebx, [esi+4C0Ch] - lea ebp, [esi+808h] - lea ebx, [ebx+0] - -loc_4CF7A0: ; CODE XREF: sub_4CF780+48j - mov eax, [esi+4C08h] - push ebp - push eax - push edi - mov ecx, esi - call sub_4CDC00 - mov ecx, [esi+4C08h] - mov [ebx], ecx - add edi, 1 - add ebx, 4 - add ebp, 440h - cmp edi, [esp+10h+arg_0] - jb short loc_4CF7A0 - pop ebp - pop ebx - -loc_4CF7CC: ; CODE XREF: sub_4CF780+Aj - pop edi - pop esi - retn 4 -sub_4CF780 endp - -; --------------------------------------------------------------------------- - align 10h -CEncoder_GetOptimum proc near ; CODE XREF: CEncoder_CodeOneBlock+201p - -var_7D = byte ptr -7Dh -var_7C = dword ptr -7Ch -var_78 = dword ptr -78h -var_74 = dword ptr -74h -var_70 = dword ptr -70h -var_6C = dword ptr -6Ch -var_68 = dword ptr -68h -var_64 = dword ptr -64h -var_60 = dword ptr -60h -var_5C = dword ptr -5Ch -var_58 = dword ptr -58h -var_54 = dword ptr -54h -var_50 = dword ptr -50h -var_4C = dword ptr -4Ch -var_48 = dword ptr -48h -var_44 = dword ptr -44h -var_40 = dword ptr -40h -var_3C = dword ptr -3Ch -var_38 = dword ptr -38h -var_34 = dword ptr -34h -var_30 = dword ptr -30h -var_2C = dword ptr -2Ch -var_28 = dword ptr -28h -var_24 = dword ptr -24h -var_20 = dword ptr -20h -var_1C = dword ptr -1Ch -var_18 = dword ptr -18h -var_14 = dword ptr -14h -var_10 = dword ptr -10h -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 - - sub esp, 80h - push ebp - mov ebp, ecx - mov edx, [ebp+32F0Ch] - cmp [ebp+32F08h], edx - push esi - jz short loc_4CE0A5 - mov esi, [esp+88h+arg_4] - lea eax, [edx+edx*4+1Eh] - lea ecx, [ebp+eax*8+0] - mov eax, [ecx+10h] - sub eax, edx - mov edx, [ecx+14h] - mov [esi], edx - mov ecx, [ecx+10h] - pop esi - mov [ebp+32F0Ch], ecx - pop ebp - add esp, 80h - retn 8 -; --------------------------------------------------------------------------- - -loc_4CE0A5: ; CODE XREF: CEncoder_GetOptimum+16j - mov edx, [ebp+80h] - mov eax, [ebp+70h] - push ebx - xor esi, esi - push edx - mov [ebp+32F08h], esi - mov [ebp+32F0Ch], esi - call eax - add esp, 4 - cmp byte ptr [ebp+32F10h], 0 - mov ebx, eax - mov [esp+8Ch+var_60], eax - jnz short loc_4CE0E4 - lea ecx, [esp+8Ch+var_70] - push ecx - mov ecx, ebp - call sub_4CB3B0 - mov [esp+8Ch+var_68], eax - jmp short loc_4CE0FF -; --------------------------------------------------------------------------- - -loc_4CE0E4: ; CODE XREF: CEncoder_GetOptimum+70j - mov edx, [ebp+32EFCh] - mov eax, [ebp+32F00h] - mov [esp+8Ch+var_68], edx - mov [esp+8Ch+var_70], eax - mov byte ptr [ebp+32F10h], 0 - -loc_4CE0FF: ; CODE XREF: CEncoder_GetOptimum+82j - mov ecx, [ebp+80h] - mov edx, [ebp+74h] - push edi - push ecx - call edx - mov edi, eax - add esp, 4 - sub edi, 1 - cmp ebx, 2 - jnb short loc_4CE138 - mov eax, [esp+90h+arg_4] - pop edi - pop ebx - pop esi - mov dword ptr [eax], 0FFFFFFFFh - mov eax, 1 - pop ebp - add esp, 80h - retn 8 -; --------------------------------------------------------------------------- - -loc_4CE138: ; CODE XREF: CEncoder_GetOptimum+B7j - cmp ebx, 111h - jbe short loc_4CE149 - mov ebx, 111h - mov [esp+90h+var_60], ebx - -loc_4CE149: ; CODE XREF: CEncoder_GetOptimum+DEj - lea ecx, [ebp+14h] - mov [esp+90h+var_50], esi - mov [esp+90h+var_64], esi - xor edx, edx - mov [esp+90h+var_4C], esi - mov [esp+90h+var_6C], ecx - mov edi, edi - -loc_4CE160: ; CODE XREF: CEncoder_GetOptimum+180j - mov eax, [esp+90h+var_6C] - mov eax, [eax] - mov ecx, edi - sub ecx, eax - mov [esp+edx+90h+var_20], eax - mov al, [edi] - sub ecx, 1 - cmp al, [ecx] - jnz short loc_4CE1C9 - mov al, [edi+1] - cmp al, [ecx+1] - jnz short loc_4CE1C9 - mov eax, 2 - cmp ebx, eax - jbe short loc_4CE1A7 - lea esi, [edi+2] - sub ecx, edi - lea ecx, [ecx+0] - -loc_4CE190: ; CODE XREF: CEncoder_GetOptimum+143j - mov bl, [esi] - cmp bl, [ecx+esi] - mov ebx, [esp+90h+var_60] - jnz short loc_4CE1A5 - add eax, 1 - add esi, 1 - cmp eax, ebx - jb short loc_4CE190 - -loc_4CE1A5: ; CODE XREF: CEncoder_GetOptimum+139j - xor esi, esi - -loc_4CE1A7: ; CODE XREF: CEncoder_GetOptimum+126j - mov ecx, [esp+90h+var_4C] - mov [esp+edx+90h+var_10], eax - cmp eax, [esp+ecx+90h+var_10] - jbe short loc_4CE1D0 - mov eax, [esp+90h+var_64] - mov [esp+90h+var_50], eax - mov [esp+90h+var_4C], edx - jmp short loc_4CE1D0 -; --------------------------------------------------------------------------- - -loc_4CE1C9: ; CODE XREF: CEncoder_GetOptimum+115j - ; CEncoder_GetOptimum+11Dj - mov [esp+edx+90h+var_10], esi - -loc_4CE1D0: ; CODE XREF: CEncoder_GetOptimum+159j - ; CEncoder_GetOptimum+167j - add [esp+90h+var_64], 1 - add [esp+90h+var_6C], 4 - add edx, 4 - cmp edx, 10h - jb loc_4CE160 - mov ecx, [esp+90h+var_50] - mov ebx, [esp+ecx*4+90h+var_10] - mov eax, [ebp+32EF8h] - cmp ebx, eax - mov [esp+90h+var_40], ebx - jb short loc_4CE234 - mov edx, [esp+90h+arg_4] - lea eax, [ebx-1] - cmp eax, esi - mov [edx], ecx - jz short loc_4CE225 - add [ebp+32F04h], eax - mov ecx, [ebp+7Ch] - push eax - mov eax, [ebp+80h] - push eax - call ecx - add esp, 8 - -loc_4CE225: ; CODE XREF: CEncoder_GetOptimum+1ADj - pop edi - mov eax, ebx - pop ebx - pop esi - pop ebp - add esp, 80h - retn 8 -; --------------------------------------------------------------------------- - -loc_4CE234: ; CODE XREF: CEncoder_GetOptimum+19Dj - mov esi, [esp+90h+var_68] - cmp esi, eax - jb short loc_4CE27F - mov edx, [esp+90h+var_70] - mov eax, [ebp+edx*4+3265Ch] - mov ecx, [esp+90h+arg_4] - add eax, 4 - mov [ecx], eax - lea eax, [esi-1] - test eax, eax - jz short loc_4CE270 - mov edx, [ebp+80h] - add [ebp+32F04h], eax - push eax - mov eax, [ebp+7Ch] - push edx - call eax - add esp, 8 - -loc_4CE270: ; CODE XREF: CEncoder_GetOptimum+1F8j - pop edi - pop ebx - mov eax, esi - pop esi - pop ebp - add esp, 80h - retn 8 -; --------------------------------------------------------------------------- - -loc_4CE27F: ; CODE XREF: CEncoder_GetOptimum+1DAj - mov al, [edi] - mov esi, [esp+90h+var_20] - sub edi, esi - cmp [esp+90h+var_68], 2 - mov cl, [edi-1] - mov byte ptr [esp+90h+var_4C], al - mov byte ptr [esp+90h+var_50], cl - jnb short loc_4CE2C1 - cmp al, cl - jz short loc_4CE2C1 - cmp ebx, 2 - jnb short loc_4CE2C1 - mov ecx, [esp+90h+arg_4] - pop edi - pop ebx - pop esi - mov dword ptr [ecx], 0FFFFFFFFh - mov eax, 1 - pop ebp - add esp, 80h - retn 8 -; --------------------------------------------------------------------------- - -loc_4CE2C1: ; CODE XREF: CEncoder_GetOptimum+237j - ; CEncoder_GetOptimum+23Bj ... - mov edx, [esp+90h+var_4C] - mov bl, [ebp+10h] - mov edi, [ebp+33B60h] - and edi, [esp+90h+arg_0] - mov ecx, [esp+90h+var_50] - push edx - cmp bl, 7 - setnb dl - push ecx - mov [ebp+0F0h], bl - mov eax, [ebp+32654h] - mov ecx, 8 - sub cl, al - push edx - movzx edx, byte ptr [ebp+11h] - shr edx, cl - mov ecx, [ebp+3265Ch] - and ecx, [esp+9Ch+arg_0] - mov [esp+9Ch+var_3C], ecx - mov ecx, eax - mov eax, [esp+9Ch+var_3C] - shl eax, cl - add edx, eax - imul edx, 0C00h - add edx, [ebp+32650h] - mov ecx, edx - call sub_4CDAC0 - movzx ecx, bl - shl ecx, 4 - add ecx, edi - mov edx, [ebp+ecx*4+280F0h] - shr edx, 2 - add eax, dword_550998[edx*4] - mov ebx, 800h - mov [ebp+124h], eax - mov dword ptr [ebp+12Ch], 0FFFFFFFFh - mov byte ptr [ebp+119h], 0 - mov cl, [ebp+10h] - movzx eax, cl - mov edx, eax - shl edx, 4 - add edx, edi - sub ebx, [ebp+edx*4+280F0h] - mov edx, 800h - sub edx, [ebp+eax*4+283F0h] - mov al, byte ptr [esp+90h+var_4C] - shr edx, 2 - mov edx, dword_550998[edx*4] - shr ebx, 2 - mov ebx, dword_550998[ebx*4] - add edx, ebx - cmp byte ptr [esp+90h+var_50], al - mov [esp+90h+var_44], ebx - mov [esp+90h+var_3C], edx - jnz short loc_4CE3F3 - movzx eax, cl - mov ebx, [ebp+eax*4+28420h] - mov ecx, eax - shl ecx, 4 - add ecx, edi - mov ecx, [ebp+ecx*4+284B0h] - shr ecx, 2 - mov eax, dword_550998[ecx*4] - shr ebx, 2 - add eax, dword_550998[ebx*4] - add eax, edx - cmp eax, [ebp+124h] - jnb short loc_4CE3F3 - mov [ebp+124h], eax - mov dword ptr [ebp+12Ch], 0 - mov byte ptr [ebp+119h], 0 - -loc_4CE3F3: ; CODE XREF: CEncoder_GetOptimum+344j - ; CEncoder_GetOptimum+37Aj - mov eax, [esp+90h+var_68] - mov ecx, [esp+90h+var_40] - cmp eax, ecx - jb short loc_4CE405 - mov [esp+90h+var_7C], eax - jmp short loc_4CE40B -; --------------------------------------------------------------------------- - -loc_4CE405: ; CODE XREF: CEncoder_GetOptimum+39Dj - mov [esp+90h+var_7C], ecx - mov eax, ecx - -loc_4CE40B: ; CODE XREF: CEncoder_GetOptimum+3A3j - cmp eax, 2 - jnb short loc_4CE431 - mov edx, [ebp+12Ch] - mov eax, [esp+90h+arg_4] - pop edi - pop ebx - pop esi - mov [eax], edx - mov eax, 1 - pop ebp - add esp, 80h - retn 8 -; --------------------------------------------------------------------------- - -loc_4CE431: ; CODE XREF: CEncoder_GetOptimum+3AEj - mov ecx, [esp+90h+var_1C] - mov edx, [esp+90h+var_18] - xor ebx, ebx - mov [ebp+128h], ebx - mov [ebp+10Ch], ecx - mov ecx, [esp+90h+var_14] - mov [ebp+110h], edx - lea edx, [eax+eax*4] - mov [ebp+114h], ecx - mov [ebp+108h], esi - lea ecx, [ebp+edx*8+0FCh] - jmp short loc_4CE470 -; --------------------------------------------------------------------------- - align 10h - -loc_4CE470: ; CODE XREF: CEncoder_GetOptimum+407j - ; CEncoder_GetOptimum+41Fj - mov dword ptr [ecx], 0FFFFFFFh - sub eax, 1 - sub ecx, 28h - cmp eax, 2 - jnb short loc_4CE470 - -loc_4CE481: ; CODE XREF: CEncoder_GetOptimum+48Ej - mov esi, [esp+ebx*4+90h+var_10] - cmp esi, 2 - jb short loc_4CE4E8 - movzx eax, byte ptr [ebp+10h] - push edi - push eax - push ebx - mov ecx, ebp - call sub_4CD9B0 - add eax, [esp+90h+var_3C] - mov ecx, edi - imul ecx, 110h - add ecx, esi - mov [esp+90h+var_40], eax - lea edx, [esi+esi*4] - lea eax, [ebp+ecx*4+2E204h] - lea ecx, [ebp+edx*8+0FCh] - mov edi, edi - -loc_4CE4C0: ; CODE XREF: CEncoder_GetOptimum+486j - mov edx, [eax] - add edx, [esp+90h+var_40] - cmp edx, [ecx] - jnb short loc_4CE4DA - mov [ecx], edx - mov dword ptr [ecx+4], 0 - mov [ecx+8], ebx - mov byte ptr [ecx-0Bh], 0 - -loc_4CE4DA: ; CODE XREF: CEncoder_GetOptimum+468j - sub esi, 1 - sub eax, 4 - sub ecx, 28h - cmp esi, 2 - jnb short loc_4CE4C0 - -loc_4CE4E8: ; CODE XREF: CEncoder_GetOptimum+42Bj - add ebx, 1 - cmp ebx, 4 - jb short loc_4CE481 - movzx eax, byte ptr [ebp+10h] - mov ecx, [ebp+eax*4+283F0h] - shr ecx, 2 - mov eax, dword_550998[ecx*4] - add eax, [esp+90h+var_44] - mov [esp+90h+var_3C], eax - mov eax, [esp+90h+var_10] - cmp eax, 2 - lea ebx, [eax+1] - jnb short loc_4CE521 - mov ebx, 2 - -loc_4CE521: ; CODE XREF: CEncoder_GetOptimum+4BAj - cmp ebx, [esp+90h+var_68] - ja loc_4CE5BA - xor eax, eax - cmp ebx, [ebp+32660h] - mov [esp+90h+var_60], eax - jbe short loc_4CE550 - lea esp, [esp+0] - -loc_4CE540: ; CODE XREF: CEncoder_GetOptimum+4EAj - add eax, 2 - cmp ebx, [ebp+eax*4+32660h] - ja short loc_4CE540 - mov [esp+90h+var_60], eax - -loc_4CE550: ; CODE XREF: CEncoder_GetOptimum+4D7j - lea edx, [ebx+ebx*4] - lea esi, [ebp+edx*8+0FCh] - lea ebx, [ebx+0] - -loc_4CE560: ; CODE XREF: CEncoder_GetOptimum+558j - mov eax, [esp+90h+var_60] - mov eax, [ebp+eax*4+32664h] - push edi - push ebx - push eax - mov ecx, ebp - mov [esp+9Ch+var_40], eax - call sub_4CAEC0 - add eax, [esp+90h+var_3C] - cmp eax, [esi] - jnb short loc_4CE598 - mov ecx, [esp+90h+var_40] - add ecx, 4 - mov [esi], eax - mov dword ptr [esi+4], 0 - mov [esi+8], ecx - mov byte ptr [esi-0Bh], 0 - -loc_4CE598: ; CODE XREF: CEncoder_GetOptimum+51Fj - mov eax, [esp+90h+var_60] - cmp ebx, [ebp+eax*4+32660h] - jnz short loc_4CE5B2 - add eax, 2 - cmp eax, [esp+90h+var_70] - mov [esp+90h+var_60], eax - jz short loc_4CE5BA - -loc_4CE5B2: ; CODE XREF: CEncoder_GetOptimum+543j - add ebx, 1 - add esi, 28h - jmp short loc_4CE560 -; --------------------------------------------------------------------------- - -loc_4CE5BA: ; CODE XREF: CEncoder_GetOptimum+4C5j - ; CEncoder_GetOptimum+550j - mov eax, 1 - cmp [esp+90h+var_7C], eax - mov [esp+90h+var_78], eax - jz loc_4CF1D0 - mov esi, eax - jmp short loc_4CE5D5 -; --------------------------------------------------------------------------- - -loc_4CE5D1: ; CODE XREF: CEncoder_GetOptimum+116Aj - mov esi, [esp+90h+var_78] - -loc_4CE5D5: ; CODE XREF: CEncoder_GetOptimum+56Fj - mov edx, [ebp+80h] - mov eax, [ebp+70h] - push edx - call eax - add esp, 4 - lea ecx, [esp+90h+var_4C] - push ecx - mov ecx, ebp - mov [esp+94h+var_60], eax - call sub_4CB3B0 - cmp eax, [ebp+32EF8h] - mov [esp+90h+var_28], eax - jnb loc_4CF1D7 - add [esp+90h+arg_0], 1 - lea edx, [esi+esi*4+1Eh] - mov bl, [ebp+edx*8+1] - test bl, bl - lea eax, [ebp+edx*8+0] - mov edx, [eax+10h] - jz short loc_4CE673 - sub edx, 1 - cmp byte ptr [eax+2], 0 - jz short loc_4CE660 - mov ecx, [eax+4] - add ecx, 6 - cmp dword ptr [eax+8], 4 - lea ecx, [ecx+ecx*4] - mov cl, [ebp+ecx*8+0] - movzx ecx, cl - jnb short loc_4CE64F - mov cl, ds:kRepNextStates[ecx] - movzx ecx, cl - mov cl, ds:kLiteralNextStates[ecx] - jmp short loc_4CE67B -; --------------------------------------------------------------------------- - -loc_4CE64F: ; CODE XREF: CEncoder_GetOptimum+5DCj - mov cl, ds:kMatchNextStates[ecx] - movzx ecx, cl - mov cl, ds:kLiteralNextStates[ecx] - jmp short loc_4CE67B -; --------------------------------------------------------------------------- - -loc_4CE660: ; CODE XREF: CEncoder_GetOptimum+5C6j - lea ecx, [edx+edx*4+1Eh] - mov cl, [ebp+ecx*8+0] - movzx ecx, cl - mov cl, ds:kLiteralNextStates[ecx] - jmp short loc_4CE67B -; --------------------------------------------------------------------------- - -loc_4CE673: ; CODE XREF: CEncoder_GetOptimum+5BDj - lea ecx, [edx+edx*4+1Eh] - mov cl, [ebp+ecx*8+0] - -loc_4CE67B: ; CODE XREF: CEncoder_GetOptimum+5EDj - ; CEncoder_GetOptimum+5FEj ... - add esi, 0FFFFFFFFh - cmp edx, esi - jnz short loc_4CE6AC - cmp dword ptr [eax+14h], 0 - jnz short loc_4CE69A - movzx edx, cl - mov bl, ds:kShortRepNextStates[edx] - mov byte ptr [esp+90h+var_64], bl - jmp loc_4CE754 -; --------------------------------------------------------------------------- - -loc_4CE69A: ; CODE XREF: CEncoder_GetOptimum+626j - movzx ecx, cl - mov bl, ds:kLiteralNextStates[ecx] - mov byte ptr [esp+90h+var_64], bl - jmp loc_4CE754 -; --------------------------------------------------------------------------- - -loc_4CE6AC: ; CODE XREF: CEncoder_GetOptimum+620j - test bl, bl - jz short loc_4CE6CB - cmp byte ptr [eax+2], 0 - jz short loc_4CE6CB - mov edi, [eax+8] - mov edx, [eax+4] - movzx ecx, cl - mov bl, ds:kRepNextStates[ecx] - mov [esp+90h+var_68], edi - jmp short loc_4CE6EA -; --------------------------------------------------------------------------- - -loc_4CE6CB: ; CODE XREF: CEncoder_GetOptimum+64Ej - ; CEncoder_GetOptimum+654j - mov esi, [eax+14h] - cmp esi, 4 - mov [esp+90h+var_68], esi - movzx ecx, cl - mov edi, esi - jnb short loc_4CE6E4 - mov bl, ds:kRepNextStates[ecx] - jmp short loc_4CE6EA -; --------------------------------------------------------------------------- - -loc_4CE6E4: ; CODE XREF: CEncoder_GetOptimum+67Aj - mov bl, ds:kMatchNextStates[ecx] - -loc_4CE6EA: ; CODE XREF: CEncoder_GetOptimum+669j - ; CEncoder_GetOptimum+682j - cmp edi, 4 - lea edx, [edx+edx*4+1Eh] - mov byte ptr [esp+90h+var_64], bl - lea edx, [ebp+edx*8+0] - jnb short loc_4CE738 - mov ecx, [edx+edi*4+18h] - mov esi, 1 - cmp edi, esi - mov [esp+90h+var_20], ecx - jb short loc_4CE723 - mov ecx, edi - lea esi, [edx+18h] - lea edi, [esp+90h+var_1C] - rep movsd - mov esi, [esp+90h+var_68] - add esi, 1 - cmp esi, 4 - jnb short loc_4CE754 - -loc_4CE723: ; CODE XREF: CEncoder_GetOptimum+6AAj - lea edx, [edx+esi*4+18h] - mov ecx, 4 - lea edi, [esp+esi*4+90h+var_20] - sub ecx, esi - mov esi, edx - rep movsd - jmp short loc_4CE754 -; --------------------------------------------------------------------------- - -loc_4CE738: ; CODE XREF: CEncoder_GetOptimum+699j - mov ecx, [edx+18h] - mov [esp+90h+var_1C], ecx - mov ecx, [edx+1Ch] - mov edx, [edx+20h] - add edi, 0FFFFFFFCh - mov [esp+90h+var_20], edi - mov [esp+90h+var_18], ecx - mov [esp+90h+var_14], edx - -loc_4CE754: ; CODE XREF: CEncoder_GetOptimum+635j - ; CEncoder_GetOptimum+647j ... - mov ecx, [esp+90h+var_1C] - mov edx, [esp+90h+var_18] - mov edi, [esp+90h+var_20] - mov [eax], bl - mov [eax+1Ch], ecx - mov ecx, [esp+90h+var_14] - mov [eax+18h], edi - mov [eax+20h], edx - mov [eax+24h], ecx - mov esi, [eax+0Ch] - mov edx, [ebp+80h] - mov eax, [ebp+74h] - push edx - mov [esp+94h+var_3C], esi - call eax - movzx ecx, byte ptr [eax-1] - sub eax, 1 - mov edx, eax - mov byte ptr [esp+94h+var_5C], cl - sub edx, edi - movzx ecx, byte ptr [edx-1] - lea edi, [edx-1] - mov edx, [ebp+32654h] - mov byte ptr [esp+94h+var_58], cl - mov ecx, [ebp+33B60h] - and ecx, [esp+94h+arg_0] - mov [esp+94h+var_40], edi - movzx edi, bl - mov [esp+94h+var_54], ecx - mov [esp+94h+var_68], edi - add esp, 4 - shl edi, 4 - add edi, ecx - mov ecx, [esp+90h+var_5C] - push ecx - mov ecx, [esp+94h+var_58] - push ecx - cmp bl, 7 - mov ebx, [ebp+3265Ch] - setnb cl - and ebx, [esp+98h+arg_0] - mov [esp+98h+var_74], eax - movzx eax, byte ptr [eax-1] - push ecx - mov ecx, 8 - sub cl, dl - shr eax, cl - mov ecx, edx - shl ebx, cl - add eax, ebx - imul eax, 0C00h - add eax, [ebp+32650h] - mov ecx, eax - call sub_4CDAC0 - mov ecx, [ebp+edi*4+280F0h] - shr ecx, 2 - add eax, dword_550998[ecx*4] - mov ecx, [esp+90h+var_78] - add eax, esi - lea edx, [ecx+ecx*4+23h] - cmp eax, [ebp+edx*8+0Ch] - lea esi, [ebp+edx*8+0] - mov [esp+90h+var_7D], 0 - jnb short loc_4CE851 - mov [esi+0Ch], eax - mov [esi+10h], ecx - mov dword ptr [esi+14h], 0FFFFFFFFh - mov byte ptr [esi+1], 0 - mov [esp+90h+var_7D], 1 - -loc_4CE851: ; CODE XREF: CEncoder_GetOptimum+7D9j - mov ebx, [esp+90h+var_68] - mov edx, 800h - sub edx, [ebp+edi*4+280F0h] - shr edx, 2 - mov ecx, dword_550998[edx*4] - add ecx, [esp+90h+var_3C] - mov edx, 800h - sub edx, [ebp+ebx*4+283F0h] - mov ebx, [esp+90h+var_78] - shr edx, 2 - mov edx, dword_550998[edx*4] - add edx, ecx - mov [esp+90h+var_24], ecx - mov cl, byte ptr [esp+90h+var_58] - cmp cl, byte ptr [esp+90h+var_5C] - mov [esp+90h+var_38], edx - jnz short loc_4CE8EB - cmp [esi+10h], ebx - jnb short loc_4CE8A8 - cmp dword ptr [esi+14h], 0 - jz short loc_4CE8EB - -loc_4CE8A8: ; CODE XREF: CEncoder_GetOptimum+840j - mov ecx, [esp+90h+var_68] - mov ecx, [ebp+ecx*4+28420h] - mov edi, [ebp+edi*4+284B0h] - shr ecx, 2 - mov ecx, dword_550998[ecx*4] - shr edi, 2 - add ecx, dword_550998[edi*4] - add ecx, edx - cmp ecx, [esi+0Ch] - ja short loc_4CE8EB - mov [esi+0Ch], ecx - mov [esi+10h], ebx - mov dword ptr [esi+14h], 0 - mov byte ptr [esi+1], 0 - mov [esp+90h+var_7D], 1 - -loc_4CE8EB: ; CODE XREF: CEncoder_GetOptimum+83Bj - ; CEncoder_GetOptimum+846j ... - mov edx, [esp+90h+var_60] - mov ecx, 0FFFh - sub ecx, ebx - cmp ecx, edx - jnb short loc_4CE900 - mov edx, ecx - mov [esp+90h+var_60], edx - -loc_4CE900: ; CODE XREF: CEncoder_GetOptimum+898j - cmp edx, 2 - mov ebx, edx - mov [esp+90h+var_6C], ebx - jb loc_4CF1BB - mov esi, [ebp+32EF8h] - mov edi, [esp+90h+var_60] - cmp edi, esi - jbe short loc_4CE923 - mov ebx, esi - mov [esp+90h+var_6C], esi - -loc_4CE923: ; CODE XREF: CEncoder_GetOptimum+8BBj - cmp [esp+90h+var_7D], 0 - jnz loc_4CEA90 - mov dl, byte ptr [esp+90h+var_5C] - cmp byte ptr [esp+90h+var_58], dl - jz loc_4CEA90 - lea ecx, [esi+1] - cmp edi, ecx - jb short loc_4CE945 - mov edi, ecx - -loc_4CE945: ; CODE XREF: CEncoder_GetOptimum+8E1j - mov esi, 1 - cmp edi, esi - jbe short loc_4CE975 - mov ebx, [esp+90h+var_74] - mov edx, [esp+90h+var_40] - lea ecx, [ebx+1] - sub edx, ebx - jmp short loc_4CE960 -; --------------------------------------------------------------------------- - align 10h - -loc_4CE960: ; CODE XREF: CEncoder_GetOptimum+8FBj - ; CEncoder_GetOptimum+90Fj - mov bl, [ecx] - cmp bl, [edx+ecx] - jnz short loc_4CE971 - add esi, 1 - add ecx, 1 - cmp esi, edi - jb short loc_4CE960 - -loc_4CE971: ; CODE XREF: CEncoder_GetOptimum+905j - mov ebx, [esp+90h+var_6C] - -loc_4CE975: ; CODE XREF: CEncoder_GetOptimum+8ECj - add esi, 0FFFFFFFFh - cmp esi, 2 - mov [esp+90h+var_50], esi - jb loc_4CEA90 - mov ecx, [esp+90h+var_68] - mov dl, ds:kLiteralNextStates[ecx] - mov ecx, [esp+90h+arg_0] - movzx ebx, dl - add ecx, 1 - and ecx, [ebp+33B60h] - mov edx, ebx - shl edx, 4 - lea esi, [edx+ecx] - mov edx, 800h - sub edx, [ebp+ebx*4+283F0h] - mov edi, 800h - sub edi, [ebp+esi*4+280F0h] - shr edx, 2 - mov edx, dword_550998[edx*4] - shr edi, 2 - add edx, dword_550998[edi*4] - mov edi, [esp+90h+var_78] - add edx, eax - mov eax, [esp+90h+var_50] - lea edi, [eax+edi+1] - mov eax, [esp+90h+var_7C] - cmp eax, edi - mov [esp+90h+var_40], esi - jnb short loc_4CEA22 - mov esi, [esp+90h+var_7C] - lea eax, [eax+eax*4] - lea eax, [ebp+eax*8+0FCh] - mov [esp+90h+var_3C], eax - mov eax, edi - sub eax, esi - add esi, eax - mov [esp+90h+var_7C], esi - mov esi, [esp+90h+var_3C] - -loc_4CEA10: ; CODE XREF: CEncoder_GetOptimum+9BCj - add esi, 28h - sub eax, 1 - mov dword ptr [esi], 0FFFFFFFh - jnz short loc_4CEA10 - mov esi, [esp+90h+var_40] - -loc_4CEA22: ; CODE XREF: CEncoder_GetOptimum+98Ej - imul ecx, 110h - add ecx, [esp+90h+var_50] - mov eax, 800h - sub eax, [ebp+esi*4+284B0h] - mov esi, [ebp+ebx*4+28420h] - mov ebx, [esp+90h+var_6C] - shr eax, 2 - mov eax, dword_550998[eax*4] - shr esi, 2 - add eax, dword_550998[esi*4] - mov esi, [esp+90h+var_7C] - add eax, [ebp+ecx*4+2E204h] - lea ecx, [edi+edi*4+1Eh] - add eax, edx - cmp eax, [ebp+ecx*8+0Ch] - lea ecx, [ebp+ecx*8+0] - jnb short loc_4CEA94 - mov edx, [esp+90h+var_78] - add edx, 1 - mov [ecx+0Ch], eax - mov [ecx+10h], edx - mov dword ptr [ecx+14h], 0 - mov byte ptr [ecx+1], 1 - mov byte ptr [ecx+2], 0 - jmp short loc_4CEA94 -; --------------------------------------------------------------------------- - -loc_4CEA90: ; CODE XREF: CEncoder_GetOptimum+8C8j - ; CEncoder_GetOptimum+8D6j ... - mov esi, [esp+90h+var_7C] - -loc_4CEA94: ; CODE XREF: CEncoder_GetOptimum+A10j - ; CEncoder_GetOptimum+A2Ej - mov [esp+90h+var_70], 0 - mov ecx, [esp+90h+var_70] - mov [esp+90h+var_40], 2 - jmp short loc_4CEAB8 -; --------------------------------------------------------------------------- - align 10h - -loc_4CEAB0: ; CODE XREF: CEncoder_GetOptimum+D63j - mov ebx, [esp+90h+var_6C] - mov esi, [esp+90h+var_7C] - -loc_4CEAB8: ; CODE XREF: CEncoder_GetOptimum+A48j - mov edx, [esp+90h+var_74] - mov eax, edx - sub eax, [esp+ecx*4+90h+var_20] - mov cl, [edx] - sub eax, 1 - cmp cl, [eax] - mov [esp+90h+var_48], eax - jnz loc_4CEDB5 - mov cl, [edx+1] - cmp cl, [eax+1] - jnz loc_4CEDB5 - mov edi, 2 - cmp ebx, edi - jbe short loc_4CEB01 - lea ecx, [edx+2] - sub eax, edx - lea ecx, [ecx+0] - -loc_4CEAF0: ; CODE XREF: CEncoder_GetOptimum+A9Fj - mov dl, [ecx] - cmp dl, [eax+ecx] - jnz short loc_4CEB01 - add edi, 1 - add ecx, 1 - cmp edi, ebx - jb short loc_4CEAF0 - -loc_4CEB01: ; CODE XREF: CEncoder_GetOptimum+A86j - ; CEncoder_GetOptimum+A95j - mov eax, [esp+90h+var_78] - lea ebx, [edi+eax] - cmp esi, ebx - jnb short loc_4CEB2E - mov eax, ebx - lea ecx, [esi+esi*4] - sub eax, esi - add esi, eax - lea ecx, [ebp+ecx*8+0FCh] - mov [esp+90h+var_7C], esi - -loc_4CEB20: ; CODE XREF: CEncoder_GetOptimum+ACCj - add ecx, 28h - sub eax, 1 - mov dword ptr [ecx], 0FFFFFFFh - jnz short loc_4CEB20 - -loc_4CEB2E: ; CODE XREF: CEncoder_GetOptimum+AAAj - mov edx, [esp+90h+var_54] - mov eax, [esp+90h+var_64] - mov ecx, [esp+90h+var_70] - push edx - push eax - push ecx - mov ecx, ebp - mov esi, edi - call sub_4CD9B0 - add eax, [esp+90h+var_38] - mov ecx, [esp+90h+var_54] - imul ecx, 110h - lea edx, [edi+ecx] - mov [esp+90h+var_34], ecx - lea ecx, [ebp+edx*4+2E204h] - mov [esp+90h+var_50], ecx - lea edx, [ebx+ebx*4] - mov ebx, [esp+90h+var_50] - mov [esp+90h+var_2C], eax - lea ecx, [ebp+edx*8+0FCh] - -loc_4CEB78: ; CODE XREF: CEncoder_GetOptimum+B40j - mov edx, [ebx] - add edx, eax - cmp edx, [ecx] - jnb short loc_4CEB94 - mov [ecx], edx - mov edx, [esp+90h+var_78] - mov [ecx+4], edx - mov edx, [esp+90h+var_70] - mov [ecx+8], edx - mov byte ptr [ecx-0Bh], 0 - -loc_4CEB94: ; CODE XREF: CEncoder_GetOptimum+B1Ej - sub edi, 1 - sub ebx, 4 - sub ecx, 28h - cmp edi, 2 - jnb short loc_4CEB78 - cmp [esp+90h+var_70], 0 - jnz short loc_4CEBB0 - lea eax, [esi+1] - mov [esp+90h+var_40], eax - -loc_4CEBB0: ; CODE XREF: CEncoder_GetOptimum+B47j - mov ecx, [ebp+32EF8h] - mov edx, [esp+90h+var_60] - lea eax, [esi+1] - add ecx, eax - cmp edx, ecx - jnb short loc_4CEBC5 - mov ecx, edx - -loc_4CEBC5: ; CODE XREF: CEncoder_GetOptimum+B61j - cmp eax, ecx - jnb short loc_4CEBE7 - mov ebx, [esp+90h+var_74] - mov edi, [esp+90h+var_48] - lea edx, [eax+ebx] - sub edi, ebx - -loc_4CEBD6: ; CODE XREF: CEncoder_GetOptimum+B85j - mov bl, [edx] - cmp bl, [edx+edi] - jnz short loc_4CEBE7 - add eax, 1 - add edx, 1 - cmp eax, ecx - jb short loc_4CEBD6 - -loc_4CEBE7: ; CODE XREF: CEncoder_GetOptimum+B67j - ; CEncoder_GetOptimum+B7Bj - or ecx, 0FFFFFFFFh - sub ecx, esi - add eax, ecx - cmp eax, 2 - mov [esp+90h+var_44], eax - jb loc_4CEDB5 - mov edx, [esp+90h+var_68] - mov cl, ds:kRepNextStates[edx] - mov eax, [ebp+33B60h] - mov edi, [esp+90h+arg_0] - movzx ecx, cl - mov ebx, eax - lea edx, [esi+edi] - and ebx, edx - mov edx, ecx - mov cl, ds:kLiteralNextStates[ecx] - shl edx, 4 - add ebx, edx - mov edx, [ebp+ebx*4+280F0h] - movzx ebx, cl - shr edx, 2 - mov edx, dword_550998[edx*4] - mov [esp+90h+var_30], edx - lea edi, [esi+edi+1] - and edi, eax - mov eax, [ebp+32654h] - mov ecx, ebx - shl ecx, 4 - add ecx, edi - mov [esp+90h+var_50], ecx - mov ecx, [esp+90h+var_74] - movzx edx, byte ptr [esi+ecx] - push edx - mov edx, [esp+94h+var_48] - movzx edx, byte ptr [esi+edx] - push edx - movzx edx, byte ptr [ecx+esi-1] - mov ecx, 8 - sub cl, al - shr edx, cl - mov ecx, [esp+98h+arg_0] - lea eax, [esi+ecx] - mov ecx, [ebp+3265Ch] - and ecx, eax - mov eax, ecx - mov ecx, [ebp+32654h] - shl eax, cl - push 1 - add edx, eax - imul edx, 0C00h - add edx, [ebp+32650h] - mov ecx, edx - call sub_4CDAC0 - mov ecx, [esp+90h+var_50] - mov edx, 800h - sub edx, [ebp+ecx*4+280F0h] - mov ecx, [esp+90h+var_34] - shr edx, 2 - add eax, dword_550998[edx*4] - mov edx, 800h - sub edx, [ebp+ebx*4+283F0h] - shr edx, 2 - add eax, dword_550998[edx*4] - lea edx, [esi+ecx] - add eax, [ebp+edx*4+2E204h] - mov ecx, [esp+90h+var_44] - add eax, [esp+90h+var_30] - mov edx, [esp+90h+var_78] - add eax, [esp+90h+var_2C] - add ecx, esi - lea ecx, [ecx+edx+1] - cmp [esp+90h+var_7C], ecx - mov [esp+90h+var_30], ecx - jnb short loc_4CED3E - mov edx, [esp+90h+var_7C] - lea edx, [edx+edx*4] - lea edx, [ebp+edx*8+0FCh] - mov [esp+90h+var_2C], edx - mov edx, [esp+90h+var_7C] - sub ecx, edx - add edx, ecx - mov [esp+90h+var_7C], edx - mov edx, [esp+90h+var_2C] - jmp short loc_4CED30 -; --------------------------------------------------------------------------- - align 10h - -loc_4CED30: ; CODE XREF: CEncoder_GetOptimum+CCBj - ; CEncoder_GetOptimum+CDCj - add edx, 28h - sub ecx, 1 - mov dword ptr [edx], 0FFFFFFFh - jnz short loc_4CED30 - -loc_4CED3E: ; CODE XREF: CEncoder_GetOptimum+CA7j - mov edx, [esp+90h+var_50] - imul edi, 110h - add edi, [esp+90h+var_44] - mov ecx, 800h - sub ecx, [ebp+edx*4+284B0h] - mov edx, [ebp+ebx*4+28420h] - shr ecx, 2 - mov ecx, dword_550998[ecx*4] - shr edx, 2 - add ecx, dword_550998[edx*4] - add ecx, [ebp+edi*4+2E204h] - add ecx, eax - mov eax, [esp+90h+var_30] - lea eax, [eax+eax*4+1Eh] - cmp ecx, [ebp+eax*8+0Ch] - lea eax, [ebp+eax*8+0] - jnb short loc_4CEDB5 - mov [eax+0Ch], ecx - mov ecx, [esp+90h+var_78] - lea edx, [esi+ecx+1] - mov [eax+4], ecx - mov ecx, [esp+90h+var_70] - mov [eax+10h], edx - mov dword ptr [eax+14h], 0 - mov byte ptr [eax+1], 1 - mov byte ptr [eax+2], 1 - mov [eax+8], ecx - -loc_4CEDB5: ; CODE XREF: CEncoder_GetOptimum+A6Dj - ; CEncoder_GetOptimum+A79j ... - mov ecx, [esp+90h+var_70] - add ecx, 1 - cmp ecx, 4 - mov [esp+90h+var_70], ecx - jb loc_4CEAB0 - mov edx, [esp+90h+var_28] - mov ecx, [esp+90h+var_6C] - cmp edx, ecx - jbe short loc_4CEDFB - xor eax, eax - cmp ecx, [ebp+32660h] - mov edx, ecx - jbe short loc_4CEDED - -loc_4CEDE1: ; CODE XREF: CEncoder_GetOptimum+D8Bj - add eax, 2 - cmp ecx, [ebp+eax*4+32660h] - ja short loc_4CEDE1 - -loc_4CEDED: ; CODE XREF: CEncoder_GetOptimum+D7Fj - mov [ebp+eax*4+32660h], ecx - add eax, 2 - mov [esp+90h+var_4C], eax - -loc_4CEDFB: ; CODE XREF: CEncoder_GetOptimum+D73j - mov esi, [esp+90h+var_40] - cmp edx, esi - jb loc_4CF1BB - mov eax, [esp+90h+var_68] - mov ecx, [ebp+eax*4+283F0h] - shr ecx, 2 - mov eax, dword_550998[ecx*4] - add eax, [esp+90h+var_24] - mov ecx, [esp+90h+var_7C] - mov [esp+90h+var_40], eax - mov eax, [esp+90h+var_78] - add eax, edx - cmp ecx, eax - jnb short loc_4CEE52 - lea edx, [ecx+ecx*4] - sub eax, ecx - add ecx, eax - lea edx, [ebp+edx*8+0FCh] - mov [esp+90h+var_7C], ecx - -loc_4CEE44: ; CODE XREF: CEncoder_GetOptimum+DF0j - add edx, 28h - sub eax, 1 - mov dword ptr [edx], 0FFFFFFFh - jnz short loc_4CEE44 - -loc_4CEE52: ; CODE XREF: CEncoder_GetOptimum+DD0j - xor eax, eax - cmp esi, [ebp+32660h] - mov [esp+90h+var_70], eax - jbe short loc_4CEE70 - -loc_4CEE60: ; CODE XREF: CEncoder_GetOptimum+E0Aj - add eax, 2 - cmp esi, [ebp+eax*4+32660h] - ja short loc_4CEE60 - mov [esp+90h+var_70], eax - -loc_4CEE70: ; CODE XREF: CEncoder_GetOptimum+DFEj - lea ebx, ds:0[eax*4] - mov edx, [ebx+ebp+32664h] - mov ecx, 7FFFFh - sub ecx, edx - sar ecx, 1Fh - and ecx, 0Ch - add ecx, 6 - mov eax, edx - shr eax, cl - mov [esp+90h+var_6C], edx - lea edi, [esi+1] - movzx eax, byte ptr dword_551198[eax] - lea ecx, [eax+ecx*2] - mov eax, [esp+90h+var_54] - imul eax, 110h - add eax, esi - lea eax, [ebp+eax*4+295B8h] - mov [esp+90h+var_3C], ecx - mov ecx, [esp+90h+var_78] - mov [esp+90h+var_54], eax - lea eax, [esi+ecx] - lea eax, [eax+eax*4] - lea ecx, [ebp+eax*8+0FCh] - mov [esp+90h+var_50], ecx - -loc_4CEED3: ; CODE XREF: CEncoder_GetOptimum+1156j - lea eax, [edi-3] - cmp eax, 4 - jb short loc_4CEEE0 - mov eax, 3 - -loc_4CEEE0: ; CODE XREF: CEncoder_GetOptimum+E79j - cmp edx, 80h - jnb short loc_4CEEF6 - shl eax, 7 - add eax, edx - mov eax, [ebp+eax*4+33314h] - jmp short loc_4CEF10 -; --------------------------------------------------------------------------- - -loc_4CEEF6: ; CODE XREF: CEncoder_GetOptimum+E86j - shl eax, 6 - add eax, [esp+90h+var_3C] - mov esi, edx - mov eax, [ebp+eax*4+32F14h] - and esi, 0Fh - add eax, [ebp+esi*4+33B14h] - -loc_4CEF10: ; CODE XREF: CEncoder_GetOptimum+E94j - add eax, [esp+90h+var_40] - mov esi, [esp+90h+var_54] - add eax, [esi] - cmp eax, [ecx] - mov [esp+90h+var_2C], eax - jnb short loc_4CEF35 - mov [ecx], eax - mov eax, [esp+90h+var_78] - add edx, 4 - mov [ecx+4], eax - mov [ecx+8], edx - mov byte ptr [ecx-0Bh], 0 - -loc_4CEF35: ; CODE XREF: CEncoder_GetOptimum+EC0j - lea eax, [edi-1] - cmp eax, [ebx+ebp+32660h] - jnz loc_4CF1A1 - mov ebx, [esp+90h+var_74] - mov eax, [ebp+32EF8h] - mov esi, [esp+90h+var_60] - mov edx, ebx - sub edx, [esp+90h+var_6C] - add eax, edi - sub edx, 1 - cmp esi, eax - mov ecx, edi - jnb short loc_4CEF6C - mov eax, esi - mov [esp+90h+var_44], esi - jmp short loc_4CEF70 -; --------------------------------------------------------------------------- - -loc_4CEF6C: ; CODE XREF: CEncoder_GetOptimum+F02j - mov [esp+90h+var_44], eax - -loc_4CEF70: ; CODE XREF: CEncoder_GetOptimum+F0Aj - cmp edi, eax - jnb short loc_4CEF93 - lea esi, [edi+edx] - sub ebx, edx - lea esp, [esp+0] - -loc_4CEF80: ; CODE XREF: CEncoder_GetOptimum+F31j - mov al, [ebx+esi] - cmp al, [esi] - jnz short loc_4CEF93 - add ecx, 1 - add esi, 1 - cmp ecx, [esp+90h+var_44] - jb short loc_4CEF80 - -loc_4CEF93: ; CODE XREF: CEncoder_GetOptimum+F12j - ; CEncoder_GetOptimum+F25j - or esi, 0FFFFFFFFh - lea eax, [edi-1] - sub esi, eax - add ecx, esi - cmp ecx, 2 - mov [esp+90h+var_48], ecx - jb loc_4CF157 - mov ecx, [esp+90h+var_68] - mov bl, ds:kMatchNextStates[ecx] - mov ecx, [ebp+33B60h] - mov eax, [esp+90h+arg_0] - lea esi, [edi+eax-1] - mov eax, ecx - and eax, esi - movzx esi, bl - movzx edx, byte ptr [edi+edx-1] - mov ebx, esi - shl ebx, 4 - add ebx, eax - mov ebx, [ebp+ebx*4+280F0h] - shr ebx, 2 - mov ebx, dword_550998[ebx*4] - mov [esp+90h+var_28], ebx - mov bl, ds:kLiteralNextStates[esi] - lea esi, [eax+1] - mov eax, [ebp+32654h] - and esi, ecx - movzx ebx, bl - mov ecx, ebx - shl ecx, 4 - add ecx, esi - mov [esp+90h+var_44], ecx - mov ecx, [esp+90h+var_74] - movzx ecx, byte ptr [ecx+edi-1] - push ecx - mov ecx, [esp+94h+var_74] - push edx - movzx edx, byte ptr [edi+ecx-2] - mov ecx, 8 - sub cl, al - shr edx, cl - mov ecx, [esp+98h+arg_0] - lea eax, [edi+ecx-1] - mov ecx, [ebp+3265Ch] - and ecx, eax - mov eax, ecx - mov ecx, [ebp+32654h] - shl eax, cl - push 1 - add edx, eax - imul edx, 0C00h - add edx, [ebp+32650h] - mov ecx, edx - call sub_4CDAC0 - mov edx, 800h - sub edx, [ebp+ebx*4+283F0h] - mov ecx, 800h - shr edx, 2 - add eax, dword_550998[edx*4] - mov edx, [esp+90h+var_44] - sub ecx, [ebp+edx*4+280F0h] - mov edx, [esp+90h+var_78] - shr ecx, 2 - add eax, dword_550998[ecx*4] - mov ecx, [esp+90h+var_48] - add eax, [esp+90h+var_28] - add ecx, edx - add eax, [esp+90h+var_2C] - add ecx, edi - cmp [esp+90h+var_7C], ecx - mov [esp+90h+var_28], ecx - jnb short loc_4CF0DE - mov edx, [esp+90h+var_7C] - lea edx, [edx+edx*4] - lea edx, [ebp+edx*8+0FCh] - mov [esp+90h+var_24], edx - mov edx, [esp+90h+var_7C] - sub ecx, edx - add edx, ecx - mov [esp+90h+var_7C], edx - mov edx, [esp+90h+var_24] - lea ecx, [ecx+0] - -loc_4CF0D0: ; CODE XREF: CEncoder_GetOptimum+107Cj - add edx, 28h - sub ecx, 1 - mov dword ptr [edx], 0FFFFFFFh - jnz short loc_4CF0D0 - -loc_4CF0DE: ; CODE XREF: CEncoder_GetOptimum+1049j - mov edx, [esp+90h+var_44] - imul esi, 110h - add esi, [esp+90h+var_48] - mov ecx, 800h - sub ecx, [ebp+edx*4+284B0h] - mov edx, [ebp+ebx*4+28420h] - shr ecx, 2 - mov ecx, dword_550998[ecx*4] - shr edx, 2 - add ecx, dword_550998[edx*4] - add ecx, [ebp+esi*4+2E204h] - add ecx, eax - mov eax, [esp+90h+var_28] - lea eax, [eax+eax*4+1Eh] - cmp ecx, [ebp+eax*8+0Ch] - lea eax, [ebp+eax*8+0] - jnb short loc_4CF157 - mov [eax+0Ch], ecx - mov ecx, [esp+90h+var_78] - lea edx, [ecx+edi] - mov [eax+4], ecx - mov ecx, [esp+90h+var_6C] - add ecx, 4 - mov [eax+10h], edx - mov dword ptr [eax+14h], 0 - mov byte ptr [eax+1], 1 - mov byte ptr [eax+2], 1 - mov [eax+8], ecx - -loc_4CF157: ; CODE XREF: CEncoder_GetOptimum+F44j - ; CEncoder_GetOptimum+10CCj - mov eax, [esp+90h+var_70] - add eax, 2 - cmp eax, [esp+90h+var_4C] - mov [esp+90h+var_70], eax - jz short loc_4CF1BB - lea ebx, ds:0[eax*4] - mov eax, [ebx+ebp+32664h] - cmp eax, 80h - mov [esp+90h+var_6C], eax - jb short loc_4CF1A1 - mov ecx, 7FFFFh - sub ecx, eax - sar ecx, 1Fh - and ecx, 0Ch - add ecx, 6 - shr eax, cl - movzx edx, byte ptr dword_551198[eax] - lea eax, [edx+ecx*2] - mov [esp+90h+var_3C], eax - -loc_4CF1A1: ; CODE XREF: CEncoder_GetOptimum+EDFj - ; CEncoder_GetOptimum+111Fj - add [esp+90h+var_50], 28h - add [esp+90h+var_54], 4 - mov ecx, [esp+90h+var_50] - mov edx, [esp+90h+var_6C] - add edi, 1 - jmp loc_4CEED3 -; --------------------------------------------------------------------------- - -loc_4CF1BB: ; CODE XREF: CEncoder_GetOptimum+8A9j - ; CEncoder_GetOptimum+DA1j ... - mov eax, [esp+90h+var_78] - add eax, 1 - cmp eax, [esp+90h+var_7C] - mov [esp+90h+var_78], eax - jnz loc_4CE5D1 - -loc_4CF1D0: ; CODE XREF: CEncoder_GetOptimum+567j - mov ecx, [esp+90h+var_78] - push ecx - jmp short loc_4CF1EF -; --------------------------------------------------------------------------- - -loc_4CF1D7: ; CODE XREF: CEncoder_GetOptimum+59Ej - mov ecx, [esp+90h+var_4C] - mov [ebp+32F00h], ecx - mov [ebp+32EFCh], eax - mov byte ptr [ebp+32F10h], 1 - push esi - -loc_4CF1EF: ; CODE XREF: CEncoder_GetOptimum+1175j - mov edx, [esp+94h+arg_4] - push edx - mov ecx, ebp - call sub_4CB2E0 - pop edi - pop ebx - pop esi - pop ebp - add esp, 80h - retn 8 -CEncoder_GetOptimum endp - -; --------------------------------------------------------------------------- - align 10h - -loc_4CF210: ; DATA XREF: CEncoder_CodeOneBlock+44o - mov edx, [esp+10h] - mov eax, [esp+4] - mov eax, [eax+4] - mov ecx, [eax] - push edx - mov edx, [esp+10h] - push edx - mov edx, [esp+10h] - push edx - push eax - mov eax, [ecx+0Ch] - call eax - retn -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -CEncoder_FillDistancesPrices proc near ; CODE XREF: CEncoder_SetStreams+4Fp - ; CEncoder_CodeOneBlock+751p - -var_20C = dword ptr -20Ch -var_208 = dword ptr -208h -var_204 = dword ptr -204h -var_200 = dword ptr -200h - - sub esp, 20Ch - push ebx - push ebp - push esi - push edi - mov ebx, ecx - mov esi, 4 - -loc_4CF241: ; CODE XREF: CEncoder_FillDistancesPrices+80j - cmp esi, 2000h - jnb short loc_4CF252 - movzx edx, byte ptr dword_551198[esi] - jmp short loc_4CF27A -; --------------------------------------------------------------------------- - -loc_4CF252: ; CODE XREF: CEncoder_FillDistancesPrices+17j - cmp esi, 2000000h - jnb short loc_4CF26B - mov eax, esi - shr eax, 0Ch - movzx edx, byte ptr dword_551198[eax] - add edx, 18h - jmp short loc_4CF27A -; --------------------------------------------------------------------------- - -loc_4CF26B: ; CODE XREF: CEncoder_FillDistancesPrices+28j - mov ecx, esi - shr ecx, 18h - movzx edx, byte ptr dword_551198[ecx] - add edx, 30h - -loc_4CF27A: ; CODE XREF: CEncoder_FillDistancesPrices+20j - ; CEncoder_FillDistancesPrices+39j - mov ecx, edx - mov eax, edx - and eax, 1 - shr ecx, 1 - sub ecx, 1 - or eax, 2 - shl eax, cl - mov edi, esi - sub edi, eax - push edi - sub eax, edx - push ecx - lea edx, [ebx+eax*4+28BACh] - push edx - call sub_4CD860 - mov [esp+esi*4+228h+var_200], eax - add esi, 1 - add esp, 0Ch - cmp esi, 80h - jb short loc_4CF241 - lea ebp, [esp+21Ch+var_200] - sub ebp, ebx - lea esi, [ebx+3331Ch] - sub ebp, 33314h - lea edx, [ebx+32F14h] - mov [esp+21Ch+var_20C], esi - mov [esp+21Ch+var_208], ebp - mov [esp+21Ch+var_204], 4 - jmp short loc_4CF2E4 -; --------------------------------------------------------------------------- - align 10h - -loc_4CF2E0: ; CODE XREF: CEncoder_FillDistancesPrices+1BDj - mov esi, [esp+21Ch+var_20C] - -loc_4CF2E4: ; CODE XREF: CEncoder_FillDistancesPrices+AAj - xor edi, edi - cmp [ebx+33B58h], edi - jbe short loc_4CF33F - mov edi, edi - -loc_4CF2F0: ; CODE XREF: CEncoder_FillDistancesPrices+109j - mov ecx, edi - or ecx, 40h - xor esi, esi - cmp ecx, 1 - jz short loc_4CF32D - lea esp, [esp+0] - -loc_4CF300: ; CODE XREF: CEncoder_FillDistancesPrices+F7j - mov eax, ecx - shr ecx, 1 - mov ebp, [edx+ecx*4-0A764h] - and eax, 1 - sub ebp, eax - neg eax - xor ebp, eax - shr ebp, 2 - and ebp, 1FFh - add esi, dword_550998[ebp*4] - cmp ecx, 1 - jnz short loc_4CF300 - mov ebp, [esp+21Ch+var_208] - -loc_4CF32D: ; CODE XREF: CEncoder_FillDistancesPrices+CAj - mov [edx+edi*4], esi - add edi, 1 - cmp edi, [ebx+33B58h] - jb short loc_4CF2F0 - mov esi, [esp+21Ch+var_20C] - -loc_4CF33F: ; CODE XREF: CEncoder_FillDistancesPrices+BCj - mov eax, 0Eh - cmp [ebx+33B58h], eax - jbe short loc_4CF368 - lea esp, [esp+0] - -loc_4CF350: ; CODE XREF: CEncoder_FillDistancesPrices+136j - mov ecx, eax - shr ecx, 1 - sub ecx, 5 - shl ecx, 6 - add [edx+eax*4], ecx - add eax, 1 - cmp eax, [ebx+33B58h] - jb short loc_4CF350 - -loc_4CF368: ; CODE XREF: CEncoder_FillDistancesPrices+11Aj - mov eax, [edx] - mov [esi-8], eax - mov ecx, [edx+4] - mov [esi-4], ecx - mov eax, [edx+8] - mov [esi], eax - mov ecx, [edx+0Ch] - mov [esi+4], ecx - mov eax, 4 - add esi, 8 - -loc_4CF386: ; CODE XREF: CEncoder_FillDistancesPrices+19Ej - cmp eax, 2000h - jnb short loc_4CF396 - movzx ecx, byte ptr dword_551198[eax] - jmp short loc_4CF3BB -; --------------------------------------------------------------------------- - -loc_4CF396: ; CODE XREF: CEncoder_FillDistancesPrices+15Bj - cmp eax, 2000000h - mov ecx, eax - jnb short loc_4CF3AE - shr ecx, 0Ch - movzx ecx, byte ptr dword_551198[ecx] - add ecx, 18h - jmp short loc_4CF3BB -; --------------------------------------------------------------------------- - -loc_4CF3AE: ; CODE XREF: CEncoder_FillDistancesPrices+16Dj - shr ecx, 18h - movzx ecx, byte ptr dword_551198[ecx] - add ecx, 30h - -loc_4CF3BB: ; CODE XREF: CEncoder_FillDistancesPrices+164j - ; CEncoder_FillDistancesPrices+17Cj - mov ecx, [edx+ecx*4] - add ecx, [esi+ebp] - add eax, 1 - mov [esi], ecx - add esi, 4 - cmp eax, 80h - jb short loc_4CF386 - add [esp+21Ch+var_20C], 200h - sub ebp, 200h - add edx, 100h - sub [esp+21Ch+var_204], 1 - mov [esp+21Ch+var_208], ebp - jnz loc_4CF2E0 - pop edi - pop esi - pop ebp - mov dword ptr [ebx+33B70h], 0 - pop ebx - add esp, 20Ch - retn -CEncoder_FillDistancesPrices endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -CEncoder_FillAlignPrices proc near ; CODE XREF: CEncoder_SetStreams+56p - ; CEncoder_CodeOneBlock+761p - push ebx - push ebp - push esi - mov ebp, ecx - push edi - xor esi, esi - lea ebx, [ebp+28D78h] - lea edi, [ebp+33B14h] - -loc_4CF424: ; CODE XREF: CEncoder_FillAlignPrices+27j - push esi - mov ecx, ebx - call sub_4CD810 - mov [edi], eax - add esi, 1 - add edi, 4 - cmp esi, 10h - jb short loc_4CF424 - pop edi - pop esi - mov dword ptr [ebp+33B54h], 0 - pop ebp - pop ebx - retn -CEncoder_FillAlignPrices endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -sub_4CF810 proc near ; CODE XREF: sub_4D0250+3p - -var_10 = dword ptr -10h -var_C = dword ptr -0Ch -var_4 = dword ptr -4 - - push 0FFFFFFFFh - push offset loc_4DA0C9 - mov eax, dword ptr fs:[0] - push eax - push ecx - push ebx - push esi - mov eax, dword_544960 - xor eax, esp - push eax - lea eax, [esp+1Ch+var_C] - mov dword ptr fs:[0], eax - mov esi, ecx - mov [esp+1Ch+var_10], esi - mov dword ptr [esi], offset NCompress_NLZMA_CEncoder_vftable - mov dword ptr [esi+4], offset NCompress_NLZMA_CEncoder_vftable_ICompressSetOutStream - mov dword ptr [esi+8], offset NCompress_NLZMA_CEncoder_vftable_ICompressSetCoderProperties - mov dword ptr [esi+0Ch], offset NCompress_NLZMA_CEncoder_vftable_ICompressWriteCoderProperties - mov eax, [esi+0A4h] - push eax ; lpAddress - mov [esp+20h+var_4], 1 - call off_546E24 - xor ebx, ebx - add esp, 4 - cmp [esi+0D0h], ebx - mov [esi+0A4h], ebx - jnz short loc_4CF892 - mov ecx, [esi+0B4h] - push ecx ; lpAddress - call off_546E24 - add esp, 4 - mov [esi+0B4h], ebx - -loc_4CF892: ; CODE XREF: sub_4CF810+6Aj - mov eax, [esi+33B8Ch] - cmp eax, ebx - jz short loc_4CF8A4 - mov edx, [eax] - push eax - mov eax, [edx+8] - call eax - -loc_4CF8A4: ; CODE XREF: sub_4CF810+8Aj - lea ecx, [esi+32650h] - mov byte ptr [esp+1Ch+var_4], bl - call sub_4CD8F0 - mov eax, [esi+40h] - cmp eax, ebx - mov [esp+1Ch+var_4], 0FFFFFFFFh - jz short loc_4CF8CF - push 8000h ; dwFreeType - push ebx ; dwSize - push eax ; lpAddress - call ds:VirtualFree - -loc_4CF8CF: ; CODE XREF: sub_4CF810+B0j - mov [esi+40h], ebx - mov esi, [esi+54h] - cmp esi, ebx - jz short loc_4CF8E1 - mov ecx, [esi] - mov edx, [ecx+8] - push esi - call edx - -loc_4CF8E1: ; CODE XREF: sub_4CF810+C7j - mov ecx, [esp+1Ch+var_C] - mov dword ptr fs:[0], ecx - pop ecx - pop esi - pop ebx - add esp, 10h - retn -sub_4CF810 endp - -; =============== S U B R O U T I N E ======================================= - -; int __stdcall sub_4CF900(int, void *Buf1, int) -sub_4CF900 proc near ; CODE XREF: sub_4CFA20+5j - ; ICompressSetCoderProperties_QueryInterface+5j ... - -arg_0 = dword ptr 4 -Buf1 = dword ptr 8 -arg_8 = dword ptr 0Ch - - push esi - mov esi, [esp+4+Buf1] - push offset dword_512730 ; Buf2 - push esi ; Buf1 - call unknown_libname_324 ; MFC 3.1/4.0/4.2/8.0 32bit - add esp, 8 - test eax, eax - jz short loc_4CF94C - -loc_4CF917: ; CODE XREF: sub_4CF900+5Cj - mov eax, [esp+4+arg_0] - test eax, eax - jz short loc_4CF936 - mov edx, [esp+4+arg_8] - lea ecx, [eax+4] - mov [edx], ecx - mov ecx, [eax] - mov edx, [ecx+4] - push eax - call edx - xor eax, eax - pop esi - retn 0Ch -; --------------------------------------------------------------------------- - -loc_4CF936: ; CODE XREF: sub_4CF900+1Dj - ; sub_4CF900+76j ... - mov edx, [esp+4+arg_8] - xor ecx, ecx - mov [edx], ecx - mov ecx, [eax] - mov edx, [ecx+4] - push eax - call edx - xor eax, eax - pop esi - retn 0Ch -; --------------------------------------------------------------------------- - -loc_4CF94C: ; CODE XREF: sub_4CF900+15j - push offset dword_5535A0 ; Buf2 - push esi ; Buf1 - call unknown_libname_324 ; MFC 3.1/4.0/4.2/8.0 32bit - add esp, 8 - test eax, eax - jnz short loc_4CF917 - push offset dword_5535B0 ; Buf2 - push esi ; Buf1 - call unknown_libname_324 ; MFC 3.1/4.0/4.2/8.0 32bit - add esp, 8 - test eax, eax - jz short loc_4CF98F - mov eax, [esp+4+arg_0] - test eax, eax - jz short loc_4CF936 - mov edx, [esp+4+arg_8] - lea ecx, [eax+8] - mov [edx], ecx - mov ecx, [eax] - mov edx, [ecx+4] - push eax - call edx - xor eax, eax - pop esi - retn 0Ch -; --------------------------------------------------------------------------- - -loc_4CF98F: ; CODE XREF: sub_4CF900+6Ej - push offset dword_5535C0 ; Buf2 - push esi ; Buf1 - call unknown_libname_324 ; MFC 3.1/4.0/4.2/8.0 32bit - add esp, 8 - test eax, eax - jz short loc_4CF9C0 - mov eax, [esp+4+arg_0] - test eax, eax - jz short loc_4CF936 - mov edx, [esp+4+arg_8] - lea ecx, [eax+0Ch] - mov [edx], ecx - mov ecx, [eax] - mov edx, [ecx+4] - push eax - call edx - xor eax, eax - pop esi - retn 0Ch -; --------------------------------------------------------------------------- - -loc_4CF9C0: ; CODE XREF: sub_4CF900+9Fj - mov eax, 80004002h - pop esi - retn 0Ch -sub_4CF900 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - - -sub_4CF9D0 proc near ; CODE XREF: ICompressSetCoderProperties_AddRef+5j - ; sub_4CFA10+5j ... - -arg_0 = dword ptr 4 - - mov eax, [esp+arg_0] - add dword ptr [eax+24h], 1 - mov eax, [eax+24h] - retn 4 -sub_4CF9D0 endp - -; --------------------------------------------------------------------------- - align 10h - -; =============== S U B R O U T I N E ======================================= - - -sub_4CF9E0 proc near ; CODE XREF: sub_4CFA40+5j - ; ICompressSetCoderProperties_Release+5j ... - -arg_0 = dword ptr 4 - - mov ecx, [esp+arg_0] - add dword ptr [ecx+24h], 0FFFFFFFFh - mov eax, [ecx+24h] - jnz short locret_4CF9F8 - mov eax, [ecx] - mov edx, [eax+10h] - push 1 - call edx - xor eax, eax - -locret_4CF9F8: ; CODE XREF: sub_4CF9E0+Bj - retn 4 -sub_4CF9E0 endp - -; --------------------------------------------------------------------------- - align 10h - -; =============== S U B R O U T I N E ======================================= - - -ICompressSetCoderProperties_AddRef proc near ; DATA XREF: .rdata:00517AD4o - -arg_0 = dword ptr 4 - - sub [esp+arg_0], 8 - jmp sub_4CF9D0 -ICompressSetCoderProperties_AddRef endp - -; --------------------------------------------------------------------------- - align 10h - -; =============== S U B R O U T I N E ======================================= - - -sub_4CFA10 proc near ; DATA XREF: .rdata:00517AC0o - -arg_0 = dword ptr 4 - - sub [esp+arg_0], 0Ch - jmp sub_4CF9D0 -sub_4CFA10 endp - -; --------------------------------------------------------------------------- - align 10h - -; =============== S U B R O U T I N E ======================================= - - -sub_4CFA20 proc near ; DATA XREF: .rdata:NCompress_NLZMA_CEncoder_vftable_ICompressSetOutStreamo - -arg_0 = dword ptr 4 - - sub [esp+arg_0], 4 - jmp sub_4CF900 -sub_4CFA20 endp - -; --------------------------------------------------------------------------- - align 10h - -; =============== S U B R O U T I N E ======================================= - - -ICompressSetCoderProperties_QueryInterface proc near ; DATA XREF: .rdata:NCompress_NLZMA_CEncoder_vftable_ICompressSetCoderPropertieso - -arg_0 = dword ptr 4 - - sub [esp+arg_0], 8 - jmp sub_4CF900 -ICompressSetCoderProperties_QueryInterface endp - -; --------------------------------------------------------------------------- - align 10h - -; =============== S U B R O U T I N E ======================================= - - -sub_4CFA40 proc near ; DATA XREF: .rdata:00517AECo - -arg_0 = dword ptr 4 - - sub [esp+arg_0], 4 - jmp sub_4CF9E0 -sub_4CFA40 endp - -; --------------------------------------------------------------------------- - align 10h - -; =============== S U B R O U T I N E ======================================= - - -sub_4CFA50 proc near ; DATA XREF: .rdata:NCompress_NLZMA_CEncoder_vftable_ICompressWriteCoderPropertieso - -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 -arg_8 = dword ptr 0Ch - - sub [esp+arg_0], 0Ch - jmp sub_4CF900 -sub_4CFA50 endp - -; --------------------------------------------------------------------------- - align 10h - -; =============== S U B R O U T I N E ======================================= - - -ICompressSetCoderProperties_Release proc near ; DATA XREF: .rdata:00517AD8o - -arg_0 = dword ptr 4 - - sub [esp+arg_0], 8 - jmp sub_4CF9E0 -ICompressSetCoderProperties_Release endp - -; --------------------------------------------------------------------------- - align 10h - -; =============== S U B R O U T I N E ======================================= - - -sub_4CFA70 proc near ; DATA XREF: .rdata:00517AC4o - -arg_0 = dword ptr 4 - - sub [esp+arg_0], 0Ch - jmp sub_4CF9E0 -sub_4CFA70 endp - -; --------------------------------------------------------------------------- - align 10h - -; =============== S U B R O U T I N E ======================================= - - -sub_4CFA80 proc near ; DATA XREF: .rdata:00517AE8o - -arg_0 = dword ptr 4 - - sub [esp+arg_0], 4 - jmp sub_4CF9D0 -sub_4CFA80 endp - -; --------------------------------------------------------------------------- - align 10h - -; =============== S U B R O U T I N E ======================================= - - -sub_4CFA90 proc near ; DATA XREF: .rdata:00517AF0o - -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 - - push esi - mov esi, [esp+4+arg_4] - test esi, esi - push edi - mov edi, [esp+8+arg_0] - jz short loc_4CFAA6 - mov eax, [esi] - mov ecx, [eax+4] - push esi - call ecx - -loc_4CFAA6: ; CODE XREF: sub_4CFA90+Cj - mov eax, [edi+50h] - test eax, eax - jz short loc_4CFAB5 - mov edx, [eax] - push eax - mov eax, [edx+8] - call eax - -loc_4CFAB5: ; CODE XREF: sub_4CFA90+1Bj - mov [edi+50h], esi - pop edi - xor eax, eax - pop esi - retn 8 -sub_4CFA90 endp - -; =============== S U B R O U T I N E ======================================= - -sub_4CFAC0 proc near ; CODE XREF: CEncoder_CodeOneBlock+42Ap - ; CEncoder_CodeOneBlock+517p - -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 - - mov eax, [esp+arg_0] - push esi - mov esi, [eax+10h] - mov edx, esi - shr edx, 0Bh - imul edx, [ecx] - cmp [esp+4+arg_4], 0 - jnz short loc_4CFAEC - mov [eax+10h], edx - mov edx, [ecx] - mov esi, 800h - sub esi, edx - shr esi, 5 - add esi, edx - mov [ecx], esi - jmp short loc_4CFB03 -; --------------------------------------------------------------------------- - -loc_4CFAEC: ; CODE XREF: sub_4CFAC0+15j - add [eax+8], edx - adc dword ptr [eax+0Ch], 0 - sub esi, edx - mov [eax+10h], esi - mov edx, [ecx] - mov esi, edx - shr esi, 5 - sub edx, esi - mov [ecx], edx - -loc_4CFB03: ; CODE XREF: sub_4CFAC0+2Aj - mov ecx, [eax+10h] - cmp ecx, 1000000h - pop esi - jnb short locret_4CFB1C - shl ecx, 8 - mov [eax+10h], ecx - mov ecx, eax - call loc_4CF640 - -locret_4CFB1C: ; CODE XREF: sub_4CFAC0+4Dj - retn 8 -sub_4CFAC0 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - - -sub_4CFB20 proc near ; CODE XREF: sub_4D0020+52p - ; sub_4D0020+E4p - -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 - - push ebx - push ebp - push esi - mov esi, [esp+0Ch+arg_0] - push edi - mov ebp, ecx - mov ebx, 1 - mov edx, 3 - -loc_4CFB34: ; CODE XREF: sub_4CFB20+8Cj - mov edi, [esp+10h+arg_4] - sub edx, 1 - mov ecx, edx - shr edi, cl - mov ecx, [esi+10h] - mov eax, ecx - shr eax, 0Bh - imul eax, [ebp+ebx*4+0] - and edi, 1 - test edi, edi - mov [esp+10h+arg_0], edx - jnz short loc_4CFB70 - mov [esi+10h], eax - mov eax, [ebp+ebx*4+0] - mov ecx, 800h - sub ecx, eax - shr ecx, 5 - add ecx, eax - mov [ebp+ebx*4+0], ecx - jmp short loc_4CFB8B -; --------------------------------------------------------------------------- - -loc_4CFB70: ; CODE XREF: sub_4CFB20+35j - add [esi+8], eax - adc dword ptr [esi+0Ch], 0 - sub ecx, eax - mov [esi+10h], ecx - mov eax, [ebp+ebx*4+0] - mov ecx, eax - shr ecx, 5 - sub eax, ecx - mov [ebp+ebx*4+0], eax - -loc_4CFB8B: ; CODE XREF: sub_4CFB20+4Ej - mov eax, [esi+10h] - cmp eax, 1000000h - jnb short loc_4CFBA6 - shl eax, 8 - mov ecx, esi - mov [esi+10h], eax - call loc_4CF640 - mov edx, [esp+10h+arg_0] - -loc_4CFBA6: ; CODE XREF: sub_4CFB20+73j - add ebx, ebx - or ebx, edi - test edx, edx - jnz short loc_4CFB34 - pop edi - pop esi - pop ebp - pop ebx - retn 8 -sub_4CFB20 endp - -; --------------------------------------------------------------------------- - align 10h - -; =============== S U B R O U T I N E ======================================= - - -sub_4CFBC0 proc near ; CODE XREF: sub_4D0020+136p - -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 - - push ebx - push ebp - push esi - mov esi, [esp+0Ch+arg_0] - push edi - mov ebp, ecx - mov ebx, 1 - mov edx, 8 - -loc_4CFBD4: ; CODE XREF: sub_4CFBC0+8Cj - mov edi, [esp+10h+arg_4] - sub edx, 1 - mov ecx, edx - shr edi, cl - mov ecx, [esi+10h] - mov eax, ecx - shr eax, 0Bh - imul eax, [ebp+ebx*4+0] - and edi, 1 - test edi, edi - mov [esp+10h+arg_0], edx - jnz short loc_4CFC10 - mov [esi+10h], eax - mov eax, [ebp+ebx*4+0] - mov ecx, 800h - sub ecx, eax - shr ecx, 5 - add ecx, eax - mov [ebp+ebx*4+0], ecx - jmp short loc_4CFC2B -; --------------------------------------------------------------------------- - -loc_4CFC10: ; CODE XREF: sub_4CFBC0+35j - add [esi+8], eax - adc dword ptr [esi+0Ch], 0 - sub ecx, eax - mov [esi+10h], ecx - mov eax, [ebp+ebx*4+0] - mov ecx, eax - shr ecx, 5 - sub eax, ecx - mov [ebp+ebx*4+0], eax - -loc_4CFC2B: ; CODE XREF: sub_4CFBC0+4Ej - mov eax, [esi+10h] - cmp eax, 1000000h - jnb short loc_4CFC46 - shl eax, 8 - mov ecx, esi - mov [esi+10h], eax - call loc_4CF640 - mov edx, [esp+10h+arg_0] - -loc_4CFC46: ; CODE XREF: sub_4CFBC0+73j - add ebx, ebx - or ebx, edi - test edx, edx - jnz short loc_4CFBD4 - pop edi - pop esi - pop ebp - pop ebx - retn 8 -sub_4CFBC0 endp - -; --------------------------------------------------------------------------- - align 10h - -; =============== S U B R O U T I N E ======================================= - - -sub_4CFC60 proc near ; CODE XREF: sub_4D0770+EDp - ; CEncoder_CodeOneBlock+67Cp - -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 - - push ebx - push ebp - push esi - mov esi, [esp+0Ch+arg_0] - push edi - mov ebp, ecx - mov ebx, 1 - mov edx, 6 - -loc_4CFC74: ; CODE XREF: sub_4CFC60+8Cj - mov edi, [esp+10h+arg_4] - sub edx, 1 - mov ecx, edx - shr edi, cl - mov ecx, [esi+10h] - mov eax, ecx - shr eax, 0Bh - imul eax, [ebp+ebx*4+0] - and edi, 1 - test edi, edi - mov [esp+10h+arg_0], edx - jnz short loc_4CFCB0 - mov [esi+10h], eax - mov eax, [ebp+ebx*4+0] - mov ecx, 800h - sub ecx, eax - shr ecx, 5 - add ecx, eax - mov [ebp+ebx*4+0], ecx - jmp short loc_4CFCCB -; --------------------------------------------------------------------------- - -loc_4CFCB0: ; CODE XREF: sub_4CFC60+35j - add [esi+8], eax - adc dword ptr [esi+0Ch], 0 - sub ecx, eax - mov [esi+10h], ecx - mov eax, [ebp+ebx*4+0] - mov ecx, eax - shr ecx, 5 - sub eax, ecx - mov [ebp+ebx*4+0], eax - -loc_4CFCCB: ; CODE XREF: sub_4CFC60+4Ej - mov eax, [esi+10h] - cmp eax, 1000000h - jnb short loc_4CFCE6 - shl eax, 8 - mov ecx, esi - mov [esi+10h], eax - call loc_4CF640 - mov edx, [esp+10h+arg_0] - -loc_4CFCE6: ; CODE XREF: sub_4CFC60+73j - add ebx, ebx - or ebx, edi - test edx, edx - jnz short loc_4CFC74 - pop edi - pop esi - pop ebp - pop ebx - retn 8 -sub_4CFC60 endp - -; --------------------------------------------------------------------------- - align 10h - -; =============== S U B R O U T I N E ======================================= - - -sub_4CFD00 proc near ; CODE XREF: sub_4D0770+109p - ; CEncoder_CodeOneBlock+6D5p - -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 - - push ebx - push ebp - push esi - mov esi, [esp+0Ch+arg_0] - push edi - mov ebp, ecx - mov edi, 1 - mov [esp+10h+arg_0], 4 - -loc_4CFD17: ; CODE XREF: sub_4CFD00+87j - mov ecx, [esi+10h] - mov ebx, [esp+10h+arg_4] - mov eax, ecx - shr eax, 0Bh - imul eax, [ebp+edi*4+0] - and ebx, 1 - test ebx, ebx - jnz short loc_4CFD48 - mov [esi+10h], eax - mov eax, [ebp+edi*4+0] - mov ecx, 800h - sub ecx, eax - shr ecx, 5 - add ecx, eax - mov [ebp+edi*4+0], ecx - jmp short loc_4CFD63 -; --------------------------------------------------------------------------- - -loc_4CFD48: ; CODE XREF: sub_4CFD00+2Dj - add [esi+8], eax - adc dword ptr [esi+0Ch], 0 - sub ecx, eax - mov [esi+10h], ecx - mov eax, [ebp+edi*4+0] - mov edx, eax - shr edx, 5 - sub eax, edx - mov [ebp+edi*4+0], eax - -loc_4CFD63: ; CODE XREF: sub_4CFD00+46j - mov eax, [esi+10h] - cmp eax, 1000000h - jnb short loc_4CFD7A - shl eax, 8 - mov ecx, esi - mov [esi+10h], eax - call loc_4CF640 - -loc_4CFD7A: ; CODE XREF: sub_4CFD00+6Bj - shr [esp+10h+arg_4], 1 - add edi, edi - or edi, ebx - sub [esp+10h+arg_0], 1 - jnz short loc_4CFD17 - pop edi - pop esi - pop ebp - pop ebx - retn 8 -sub_4CFD00 endp - - -; =============== S U B R O U T I N E ======================================= - - -sub_4CFD90 proc near ; CODE XREF: CEncoder_CodeOneBlock+6AFp - -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 -arg_8 = dword ptr 0Ch -arg_C = dword ptr 10h - - mov eax, [esp+arg_8] - test eax, eax - push edi - mov edi, 1 - jle loc_4CFE26 - push ebx - push ebp - mov ebp, [esp+0Ch+arg_0] - push esi - mov esi, [esp+10h+arg_4] - mov [esp+10h+arg_8], eax - -loc_4CFDB1: ; CODE XREF: sub_4CFD90+91j - mov ecx, [esi+10h] - mov ebx, [esp+10h+arg_C] - mov eax, ecx - shr eax, 0Bh - imul eax, [ebp+edi*4+0] - and ebx, 1 - test ebx, ebx - jnz short loc_4CFDE2 - mov [esi+10h], eax - mov eax, [ebp+edi*4+0] - mov ecx, 800h - sub ecx, eax - shr ecx, 5 - add ecx, eax - mov [ebp+edi*4+0], ecx - jmp short loc_4CFDFD -; --------------------------------------------------------------------------- - -loc_4CFDE2: ; CODE XREF: sub_4CFD90+37j - add [esi+8], eax - adc dword ptr [esi+0Ch], 0 - sub ecx, eax - mov [esi+10h], ecx - mov eax, [ebp+edi*4+0] - mov edx, eax - shr edx, 5 - sub eax, edx - mov [ebp+edi*4+0], eax - -loc_4CFDFD: ; CODE XREF: sub_4CFD90+50j - mov eax, [esi+10h] - cmp eax, 1000000h - jnb short loc_4CFE14 - shl eax, 8 - mov ecx, esi - mov [esi+10h], eax - call loc_4CF640 - -loc_4CFE14: ; CODE XREF: sub_4CFD90+75j - shr [esp+10h+arg_C], 1 - add edi, edi - or edi, ebx - sub [esp+10h+arg_8], 1 - jnz short loc_4CFDB1 - pop esi - pop ebp - pop ebx - -loc_4CFE26: ; CODE XREF: sub_4CFD90+Cj - pop edi - retn -sub_4CFD90 endp - -; --------------------------------------------------------------------------- - align 10h - -; =============== S U B R O U T I N E ======================================= - - -sub_4CFE30 proc near ; CODE XREF: CEncoder_CodeOneBlock+190p - ; CEncoder_CodeOneBlock+2CCp - -arg_0 = dword ptr 4 -arg_4 = byte ptr 8 - - push ebx - push ebp - push esi - mov esi, [esp+0Ch+arg_0] - push edi - mov ebx, ecx - mov ebp, 1 - mov edx, 8 - -loc_4CFE44: ; CODE XREF: sub_4CFE30+88j - movzx edi, [esp+10h+arg_4] - sub edx, 1 - mov cl, dl - shr edi, cl - mov ecx, [esi+10h] - mov eax, ecx - shr eax, 0Bh - imul eax, [ebx+ebp*4] - and edi, 1 - test edi, edi - mov [esp+10h+arg_0], edx - jnz short loc_4CFE7E - mov [esi+10h], eax - mov eax, [ebx+ebp*4] - mov ecx, 800h - sub ecx, eax - shr ecx, 5 - add ecx, eax - mov [ebx+ebp*4], ecx - jmp short loc_4CFE97 -; --------------------------------------------------------------------------- - -loc_4CFE7E: ; CODE XREF: sub_4CFE30+35j - add [esi+8], eax - adc dword ptr [esi+0Ch], 0 - sub ecx, eax - mov [esi+10h], ecx - mov eax, [ebx+ebp*4] - mov ecx, eax - shr ecx, 5 - sub eax, ecx - mov [ebx+ebp*4], eax - -loc_4CFE97: ; CODE XREF: sub_4CFE30+4Cj - mov eax, [esi+10h] - cmp eax, 1000000h - jnb short loc_4CFEB2 - shl eax, 8 - mov ecx, esi - mov [esi+10h], eax - call loc_4CF640 - mov edx, [esp+10h+arg_0] - -loc_4CFEB2: ; CODE XREF: sub_4CFE30+6Fj - add ebp, ebp - or ebp, edi - test edx, edx - jnz short loc_4CFE44 - pop edi - pop esi - pop ebp - pop ebx - retn 8 -sub_4CFE30 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -sub_4CFED0 proc near ; CODE XREF: CEncoder_CodeOneBlock+300p - -var_4 = dword ptr -4 -arg_0 = dword ptr 4 -arg_4 = byte ptr 8 -arg_8 = byte ptr 0Ch - - push ecx - push ebx - push ebp - push esi - mov esi, [esp+10h+arg_0] - push edi - mov [esp+14h+var_4], ecx - mov ebx, 1 - mov edx, 8 - jmp short loc_4CFEF0 -; --------------------------------------------------------------------------- - align 10h - -loc_4CFEF0: ; CODE XREF: sub_4CFED0+17j - ; sub_4CFED0+ADj - movzx ebp, [esp+14h+arg_4] - movzx edi, [esp+14h+arg_8] - sub edx, 1 - movzx ecx, dl - shr ebp, cl - shr edi, cl - mov ecx, [esp+14h+var_4] - mov [esp+14h+arg_0], edx - mov edx, [esi+10h] - and ebp, 1 - lea eax, [ebp+1] - shl eax, 8 - add eax, ebx - lea ecx, [ecx+eax*4] - mov eax, edx - shr eax, 0Bh - imul eax, [ecx] - and edi, 1 - test edi, edi - jnz short loc_4CFF41 - mov [esi+10h], eax - mov eax, [ecx] - mov edx, 800h - sub edx, eax - shr edx, 5 - add edx, eax - mov [ecx], edx - jmp short loc_4CFF58 -; --------------------------------------------------------------------------- - -loc_4CFF41: ; CODE XREF: sub_4CFED0+5Aj - add [esi+8], eax - adc dword ptr [esi+0Ch], 0 - sub edx, eax - mov [esi+10h], edx - mov eax, [ecx] - mov edx, eax - shr edx, 5 - sub eax, edx - mov [ecx], eax - -loc_4CFF58: ; CODE XREF: sub_4CFED0+6Fj - mov eax, [esi+10h] - cmp eax, 1000000h - jnb short loc_4CFF6F - shl eax, 8 - mov ecx, esi - mov [esi+10h], eax - call loc_4CF640 - -loc_4CFF6F: ; CODE XREF: sub_4CFED0+90j - mov edx, [esp+14h+arg_0] - add ebx, ebx - or ebx, edi - cmp ebp, edi - jnz short loc_4CFF8B - test edx, edx - jnz loc_4CFEF0 - pop edi - pop esi - pop ebp - pop ebx - pop ecx - retn 0Ch -; --------------------------------------------------------------------------- - -loc_4CFF8B: ; CODE XREF: sub_4CFED0+A9j - test edx, edx - jz loc_4D0012 - -loc_4CFF93: ; CODE XREF: sub_4CFED0+140j - movzx edi, [esp+14h+arg_8] - mov ebp, [esp+14h+var_4] - sub edx, 1 - mov cl, dl - shr edi, cl - mov ecx, [esi+10h] - mov eax, ecx - shr eax, 0Bh - imul eax, [ebp+ebx*4+0] - and edi, 1 - test edi, edi - mov [esp+14h+arg_0], edx - jnz short loc_4CFFD4 - mov [esi+10h], eax - mov eax, [ebp+ebx*4+0] - mov ecx, 800h - sub ecx, eax - shr ecx, 5 - add ecx, eax - mov [ebp+ebx*4+0], ecx - jmp short loc_4CFFEF -; --------------------------------------------------------------------------- - -loc_4CFFD4: ; CODE XREF: sub_4CFED0+E9j - add [esi+8], eax - adc dword ptr [esi+0Ch], 0 - sub ecx, eax - mov [esi+10h], ecx - mov eax, [ebp+ebx*4+0] - mov ecx, eax - shr ecx, 5 - sub eax, ecx - mov [ebp+ebx*4+0], eax - -loc_4CFFEF: ; CODE XREF: sub_4CFED0+102j - mov eax, [esi+10h] - cmp eax, 1000000h - jnb short loc_4D000A - shl eax, 8 - mov ecx, esi - mov [esi+10h], eax - call loc_4CF640 - mov edx, [esp+14h+arg_0] - -loc_4D000A: ; CODE XREF: sub_4CFED0+127j - add ebx, ebx - or ebx, edi - test edx, edx - jnz short loc_4CFF93 - -loc_4D0012: ; CODE XREF: sub_4CFED0+BDj - pop edi - pop esi - pop ebp - pop ebx - pop ecx - retn 0Ch -sub_4CFED0 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -sub_4D0020 proc near ; CODE XREF: sub_4D0770+C6p - ; CEncoder_CodeOneBlock+56Dp ... - -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 -arg_8 = dword ptr 0Ch - - push ebp - mov ebp, [esp+4+arg_4] - cmp ebp, 8 - push esi - mov esi, [esp+8+arg_0] - push edi - mov edi, ecx - jnb short loc_4D007D - mov eax, [esi+10h] - shr eax, 0Bh - imul eax, [edi] - mov [esi+10h], eax - mov eax, [edi] - mov ecx, 800h - sub ecx, eax - -loc_4D0047: ; DATA XREF: .rdata:0050C3BCo - shr ecx, 5 - add ecx, eax - mov [edi], ecx - mov eax, [esi+10h] - cmp eax, 1000000h - jnb short loc_4D0065 - shl eax, 8 - -loc_4D005B: ; DATA XREF: .rdata:005064B0o - mov ecx, esi - mov [esi+10h], eax - call loc_4CF640 - -loc_4D0065: ; CODE XREF: sub_4D0020+36j - mov edx, [esp+0Ch+arg_8] - push ebp - shl edx, 5 - push esi - lea ecx, [edx+edi+8] - call sub_4CFB20 - pop edi - pop esi - pop ebp - retn 0Ch -; --------------------------------------------------------------------------- - -loc_4D007D: ; CODE XREF: sub_4D0020+10j - mov ecx, [esi+10h] - mov eax, ecx - shr eax, 0Bh - imul eax, [edi] - add [esi+8], eax - adc dword ptr [esi+0Ch], 0 - sub ecx, eax - mov [esi+10h], ecx - mov eax, [edi] - mov ecx, eax - shr ecx, 5 - sub eax, ecx - mov [edi], eax - mov eax, [esi+10h] - cmp eax, 1000000h - jnb short loc_4D00B6 - shl eax, 8 - mov ecx, esi - mov [esi+10h], eax - call loc_4CF640 - -loc_4D00B6: ; CODE XREF: sub_4D0020+87j - cmp ebp, 10h - jnb short loc_4D010F - mov edx, [esi+10h] - shr edx, 0Bh - imul edx, [edi+4] - mov [esi+10h], edx - mov eax, [edi+4] - mov ecx, 800h - sub ecx, eax - shr ecx, 5 - add ecx, eax - mov [edi+4], ecx - mov eax, [esi+10h] - cmp eax, 1000000h - jnb short loc_4D00F1 - shl eax, 8 - mov ecx, esi - mov [esi+10h], eax - call loc_4CF640 - -loc_4D00F1: ; CODE XREF: sub_4D0020+C2j - mov edx, [esp+0Ch+arg_8] - add ebp, 0FFFFFFF8h - push ebp - shl edx, 5 - push esi - lea ecx, [edx+edi+208h] - call sub_4CFB20 - pop edi - pop esi - pop ebp - retn 0Ch -; --------------------------------------------------------------------------- - -loc_4D010F: ; CODE XREF: sub_4D0020+99j - mov ecx, [esi+10h] - mov eax, ecx - shr eax, 0Bh - imul eax, [edi+4] - add [esi+8], eax - adc dword ptr [esi+0Ch], 0 - sub ecx, eax - mov [esi+10h], ecx - mov eax, [edi+4] - mov ecx, eax - shr ecx, 5 - sub eax, ecx - mov [edi+4], eax - mov eax, [esi+10h] - cmp eax, 1000000h - jnb short loc_4D014B - shl eax, 8 - mov ecx, esi - mov [esi+10h], eax - call loc_4CF640 - -loc_4D014B: ; CODE XREF: sub_4D0020+11Cj - add ebp, 0FFFFFFF0h - push ebp - push esi - lea ecx, [edi+408h] - call sub_4CFBC0 - pop edi - pop esi - pop ebp - retn 0Ch -sub_4D0020 endp - -; =============== S U B R O U T I N E ======================================= - -NCompress_NLZMA_CEncoder_CEncoder proc near ; CODE XREF: Compress_lzma_internal+149p - mov eax, ecx - mov dword ptr [eax+4], offset ICompressSetOutStream_vftable - mov dword ptr [eax+8], offset ICompressSetCoderProperties_vftable - mov dword ptr [eax+0Ch], offset ICompressWriteCoderProperties_vftable - xor ecx, ecx - mov [eax+24h], ecx - mov dword ptr [eax], offset NCompress_NLZMA_CEncoder_vftable - mov dword ptr [eax+4], offset NCompress_NLZMA_CEncoder_vftable_ICompressSetOutStream - mov dword ptr [eax+8], offset NCompress_NLZMA_CEncoder_vftable_ICompressSetCoderProperties - mov dword ptr [eax+0Ch], offset NCompress_NLZMA_CEncoder_vftable_ICompressWriteCoderProperties - mov [eax+40h], ecx - mov [eax+44h], ecx - mov [eax+54h], ecx - mov [eax+60h], ecx - mov [eax+32650h], ecx - mov dword ptr [eax+32EF8h], 20h - mov dword ptr [eax+33B58h], 2Ch - mov dword ptr [eax+33B5Ch], 2 - mov [eax+33B64h], ecx - mov dword ptr [eax+33B6Ch], 00400000h - mov edx, 3 - mov [eax+33B60h], edx - mov [eax+33B68h], edx - mov [eax+33B8Ch], ecx - mov [eax+33B90h], ecx - mov [eax+33B94h], cl - mov [eax+0B4h], ecx - mov [eax+0A4h], ecx - mov dword ptr [eax+0B0h], 20h - mov dword ptr [eax+0D4h], 1 - mov dword ptr [eax+0CCh], 4 - mov [eax+0D0h], ecx - mov [eax+0D8h], ecx - mov [eax+32EF4h], cl - retn -NCompress_NLZMA_CEncoder_CEncoder endp - -; =============== S U B R O U T I N E ======================================= - -; int __thiscall sub_4D0250(void *Memory, char) -sub_4D0250 proc near ; DATA XREF: .rdata:00517B0Co - -arg_0 = byte ptr 4 - - push esi - mov esi, ecx - call sub_4CF810 - test [esp+4+arg_0], 1 - jz short loc_4D0268 - push esi ; Memory - call j__free - add esp, 4 - -loc_4D0268: ; CODE XREF: sub_4D0250+Dj - mov eax, esi - pop esi - retn 4 -sub_4D0250 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -sub_4D0270 proc near ; CODE XREF: CEncoder_SetStreams+14p - push esi - mov esi, ecx - mov eax, [esi+40h] - test eax, eax - push edi - mov edi, 100000h - jz short loc_4D0297 - cmp [esi+50h], edi - jz short loc_4D02C5 - test eax, eax - jz short loc_4D0297 - push 8000h ; dwFreeType - push 0 ; dwSize - push eax ; lpAddress - call ds:VirtualFree - -loc_4D0297: ; CODE XREF: sub_4D0270+Ej - ; sub_4D0270+17j - push 4 ; flProtect - push 1000h ; flAllocationType - push edi ; dwSize - push 0 ; lpAddress - mov dword ptr [esi+40h], 0 - mov [esi+50h], edi - call ds:VirtualAlloc - test eax, eax - mov [esi+40h], eax - setnz al - test al, al - jnz short loc_4D02C5 - pop edi - mov eax, 8007000Eh - pop esi - retn -; --------------------------------------------------------------------------- - -loc_4D02C5: ; CODE XREF: sub_4D0270+13j - ; sub_4D0270+4Bj - mov eax, [esi+33B68h] - mov ecx, [esi+33B64h] - cmp dword ptr [esi+0D4h], 0 - push ebx - push eax - push ecx - lea ecx, [esi+32650h] - setnz bl - call sub_4CD940 - test al, al - jz short loc_4D034F - mov ecx, [esi+33B6Ch] - mov edx, 1000000h - cmp edx, ecx - sbb eax, eax - neg eax - push ebp - mov ebp, [esi+32EF8h] - mov [esi+0D8h], eax - mov eax, ebp - shr eax, 1 - add eax, 10h - test bl, bl - jnz short loc_4D0318 - shr eax, 1 - -loc_4D0318: ; CODE XREF: sub_4D0270+A4j - mov edx, [esi+33B90h] - test edx, edx - jz short loc_4D0324 - mov eax, edx - -loc_4D0324: ; CODE XREF: sub_4D0270+B0j - push offset off_546E20 - push 111h - push ebp - push 1000h - push ecx - lea edi, [esi+84h] - push edi - mov [esi+0B0h], eax - call sub_4CBA60 - add esp, 18h - test eax, eax - pop ebp - jnz short loc_4D0358 - -loc_4D034F: ; CODE XREF: sub_4D0270+7Bj - pop ebx - pop edi - mov eax, 8007000Eh - pop esi - retn -; --------------------------------------------------------------------------- - -loc_4D0358: ; CODE XREF: sub_4D0270+DDj - mov [esi+80h], edi - add esi, 68h - push esi - push edi - call sub_4CCAA0 - add esp, 8 - pop ebx - pop edi - xor eax, eax - pop esi - retn -sub_4D0270 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -CEncoder_SetStreams proc near ; CODE XREF: CEncoder_CodeReal+4Ap - -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 - - mov eax, [esp+arg_0] - push esi - mov esi, ecx - mov [esi+33B84h], eax - mov byte ptr [esi+33B80h], 0 - call sub_4D0270 - test eax, eax - jnz loc_4D0441 - mov edx, [esp+4+arg_4] - mov ecx, [esi+4] - lea eax, [esi+4] - push edx - push eax - mov eax, [ecx+0Ch] - call eax - test eax, eax - jnz loc_4D0441 - mov ecx, esi - call sub_4CDEA0 - test eax, eax - jnz short loc_4D0441 - cmp [esi+32EF4h], al - jnz short loc_4D03DB - mov ecx, esi - call CEncoder_FillDistancesPrices - mov ecx, esi - call CEncoder_FillAlignPrices - -loc_4D03DB: ; CODE XREF: CEncoder_SetStreams+4Bj - mov ecx, [esi+32EF8h] - sub ecx, 1 - mov [esi+2D9C0h], ecx - mov ecx, [esi+33B5Ch] - mov edx, 1 - shl edx, cl - lea ecx, [esi+28DB8h] - push edx - call sub_4CF780 - mov ecx, [esi+33B5Ch] - mov eax, [esi+32EF8h] - mov edx, 1 - shl edx, cl - sub eax, 1 - lea ecx, [esi+2DA04h] - mov [esi+3260Ch], eax - push edx - call sub_4CF780 - mov dword ptr [esi+33B78h], 0 - mov dword ptr [esi+33B7Ch], 0 - xor eax, eax - -loc_4D0441: ; CODE XREF: CEncoder_SetStreams+1Bj - ; CEncoder_SetStreams+34j ... - pop esi - retn 10h -CEncoder_SetStreams endp - -;Compress_lzma_internal proc near ; CODE XREF: Compress_lzma+34p -_starcraft_compress_lzma proc near ; CODE XREF: Compress_lzma+34p -; sub_4D0450 - -var_58 = dword ptr -58h -var_54 = dword ptr -54h -var_50 = dword ptr -50h -var_4C = dword ptr -4Ch -var_48 = dword ptr -48h -var_44 = dword ptr -44h -var_40 = dword ptr -40h -var_3C = word ptr -3Ch -var_34 = dword ptr -34h -var_2C = word ptr -2Ch -var_24 = dword ptr -24h -var_1C = dword ptr -1Ch -var_14 = dword ptr -14h -var_C = dword ptr -0Ch -var_4 = dword ptr -4 -pbInBuffer = dword ptr 4 -cbInBuffer = dword ptr 8 -pcbInBuffer = dword ptr 0Ch -pbOutBuffer = dword ptr 10h -cbOutBuffer = dword ptr 14h -dummy = dword ptr 18h -pcbOutBuffer = dword ptr 1Ch -pfnAllocateMemory= dword ptr 20h -pfnFreeMemory = dword ptr 24h -pfnGiveData = dword ptr 28h - - push 0FFFFFFFFh - push offset loc_4DA0FB - mov eax, dword ptr fs:[0] - push eax - sub esp, 4Ch - push ebx - push ebp - push esi - push edi - mov eax, dword_544960 - xor eax, esp - push eax - lea eax, [esp+6Ch+var_C] - mov dword ptr fs:[0], eax - mov eax, dword_553598 - xor ebx, ebx - cmp eax, ebx - jnz short loc_4D04A1 - push 0Ch ; Size - call _operator_new ; operator new(uint) - add esp, 4 - cmp eax, ebx - jz short loc_4D049A - mov [eax], ebx - mov [eax+4], ebx - mov [eax+8], ebx - jmp short loc_4D049C -; --------------------------------------------------------------------------- - -loc_4D049A: ; CODE XREF: Compress_lzma_internal+3Ej - xor eax, eax - -loc_4D049C: ; CODE XREF: Compress_lzma_internal+48j - mov dword ptr dword_553598, eax - -loc_4D04A1: ; CODE XREF: Compress_lzma_internal+30j - cmp [eax], ebx - jnz short loc_4D04B3 - mov ecx, [esp+6Ch+pfnAllocateMemory] - mov [eax], ecx - mov eax, dword_553598 - -loc_4D04B3: ; CODE XREF: Compress_lzma_internal+53j - cmp eax, ebx - jnz short loc_4D04D6 - push 0Ch ; Size - call _operator_new ; operator new(uint) - add esp, 4 - cmp eax, ebx - jz short loc_4D04CF - mov [eax], ebx - mov [eax+4], ebx - mov [eax+8], ebx - jmp short loc_4D04D1 -; --------------------------------------------------------------------------- - -loc_4D04CF: ; CODE XREF: Compress_lzma_internal+73j - xor eax, eax - -loc_4D04D1: ; CODE XREF: Compress_lzma_internal+7Dj - mov dword_553598, eax - -loc_4D04D6: ; CODE XREF: Compress_lzma_internal+65j - cmp [eax+4], ebx - jnz short loc_4D04EA - mov edx, [esp+6Ch+pfnFreeMemory] - mov [eax+4], edx - mov eax, dword_553598 - -loc_4D04EA: ; CODE XREF: Compress_lzma_internal+89j - cmp [eax+8], ebx - jnz short loc_4D04F9 - mov ecx, [esp+6Ch+pfnGiveData] - mov [eax+8], ecx - -loc_4D04F9: ; CODE XREF: Compress_lzma_internal+9Dj - mov edx, [esp+6Ch+pcbOutBuffer] - mov ebp, [esp+6Ch+pcbInBuffer] - cmp ebp, ebx - mov esi, [esp+6Ch+cbInBuffer] - mov [edx], ebx - mov edx, 1 - mov eax, esi - mov ecx, ebp - ja short loc_4D0520 - cmp esi, ebx - jbe short loc_4D053A - jmp short loc_4D0520 -; --------------------------------------------------------------------------- - align 10h - -loc_4D0520: ; CODE XREF: Compress_lzma_internal+C5j - ; Compress_lzma_internal+CBj ... - shrd eax, ecx, 1 - add edx, edx - shr ecx, 1 - cmp edx, 2000000h - ja short loc_4D053A - cmp ecx, ebx - ja short loc_4D0520 - jb short loc_4D053A - cmp eax, ebx - ja short loc_4D0520 - -loc_4D053A: ; CODE XREF: Compress_lzma_internal+C9j - ; Compress_lzma_internal+DEj ... - mov eax, 13h - push 33B98h ; Size - mov [esp+70h+var_48], 470h - mov [esp+70h+var_44], 400h - mov [esp+70h+var_40], 450h - mov [esp+70h+var_3C], ax - mov [esp+70h+var_2C], ax - mov word ptr [esp+70h+var_1C], ax - mov [esp+70h+var_34], 2 - mov [esp+70h+var_24], edx - mov [esp+70h+var_14], 40h - mov [esp+70h+var_58], ebx - call _operator_new ; operator new(uint) - add esp, 4 - mov [esp+6Ch+var_4C], eax - cmp eax, ebx - mov [esp+6Ch+var_4], ebx - jz short loc_4D05A4 - mov ecx, eax - call NCompress_NLZMA_CEncoder_CEncoder - mov [esp+6Ch+var_54], eax - jmp short loc_4D05AA -; --------------------------------------------------------------------------- - -loc_4D05A4: ; CODE XREF: Compress_lzma_internal+145j - mov [esp+6Ch+var_54], ebx - mov eax, ebx - -loc_4D05AA: ; CODE XREF: Compress_lzma_internal+152j - or edi, 0FFFFFFFFh - cmp eax, ebx - mov [esp+6Ch+var_4], edi - jz short loc_4D05D1 - mov ecx, [eax+8] - push 3 - lea edx, [esp+70h+var_3C] - push edx - add eax, 8 - lea edx, [esp+74h+var_48] - push edx - push eax - mov eax, [ecx+0Ch] - call eax ; ICompressSetCodeProperties::SetCoderProperties - test eax, eax - jz short loc_4D05D5 - -loc_4D05D1: ; CODE XREF: Compress_lzma_internal+163j - mov [esp+6Ch+var_58], edi - -loc_4D05D5: ; CODE XREF: Compress_lzma_internal+17Fj - push 28h ; Size - call _operator_new ; operator new(uint) - add esp, 4 - cmp eax, ebx - jz short loc_4D060E - mov ecx, [esp+6Ch+pbInBuffer] - mov [eax+4], ebx - add dword ptr [eax+4], 1 - mov dword ptr [eax], offset off_517A90 - mov [eax+8], ecx - mov [eax+10h], esi - mov [eax+14h], ebp - mov [eax+18h], ebx - mov [eax+1Ch], ebx - mov [eax+20h], bl - mov edi, eax ; EDI = pInStreamMemory - mov [esp+6Ch+var_50], eax - jmp short loc_4D0614 -; --------------------------------------------------------------------------- - -loc_4D060E: ; CODE XREF: Compress_lzma_internal+191j - xor edi, edi - mov [esp+6Ch+var_50], edi - -loc_4D0614: ; CODE XREF: Compress_lzma_internal+1BCj - push 28h ; Size - call _operator_new ; operator new(uint) - add esp, 4 - cmp eax, ebx - jz short loc_4D0657 - mov edx, [esp+6Ch+pbOutBuffer] - mov ecx, [esp+6Ch+cbOutBuffer] - mov [eax+4], ebx - add dword ptr [eax+4], 1 - mov [eax+8], edx - mov edx, [esp+6Ch+dummy] - mov dword ptr [eax], offset off_517AA4 - mov [eax+10h], ecx - mov [eax+14h], edx - mov [eax+18h], ebx - mov [eax+1Ch], ebx - mov [eax+20h], bl - mov esi, eax ; ESI = pOutStreamMemory - jmp short loc_4D0659 -; --------------------------------------------------------------------------- - -loc_4D0657: ; CODE XREF: Compress_lzma_internal+1D0j - xor esi, esi - -loc_4D0659: ; CODE XREF: Compress_lzma_internal+205j - cmp edi, ebx - jz short loc_4D0661 - cmp esi, ebx - jnz short loc_4D0669 - -loc_4D0661: ; CODE XREF: Compress_lzma_internal+20Bj - mov [esp+6Ch+var_58], 0FFFFFFFFh - -loc_4D0669: ; CODE XREF: Compress_lzma_internal+20Fj - mov eax, [esi] ; EAX = pOutStreamMemory->vftable - mov ecx, [eax+10h] ; ECX = - push ebx - mov edi, 1 - push edi - push ebx - push esi - call ecx - test eax, eax - jz short loc_4D0681 - mov [esp+6Ch+var_58], edi - -loc_4D0681: ; CODE XREF: Compress_lzma_internal+22Bj - mov eax, [esp+6Ch+var_54] - mov edx, [eax+0Ch] - add eax, 0Ch - push esi - push eax - mov eax, [edx+0Ch] - call eax - test eax, eax - jz short loc_4D069A - mov [esp+6Ch+var_58], edi - -loc_4D069A: ; CODE XREF: Compress_lzma_internal+244j - xor edi, edi - jmp short loc_4D06A4 -; --------------------------------------------------------------------------- - align 10h - -loc_4D06A0: ; CODE XREF: Compress_lzma_internal+27Fj - mov ebp, [esp+6Ch+pcbInBuffer] - -loc_4D06A4: ; CODE XREF: Compress_lzma_internal+24Cj - mov eax, [esp+6Ch+cbInBuffer] - push ebx - push 1 - mov edx, ebp - mov ecx, edi - call __allshr ; Microsoft VisualC 2-8/net runtime - push eax - mov eax, [esi] - mov ecx, [eax+10h] - push esi - call ecx - test eax, eax - jz short loc_4D06C9 - mov [esp+6Ch+var_58], 1 - -loc_4D06C9: ; CODE XREF: Compress_lzma_internal+26Fj - add edi, 8 - cmp edi, 40h - jl short loc_4D06A0 - mov ebp, [esp+6Ch+var_54] - xor edi, edi - cmp [esp+6Ch+var_58], ebx - jnz short loc_4D0723 - mov eax, [esp+6Ch+var_50] - mov edx, [ebp+0] - mov ecx, [edx+0Ch] - push ebx - push ebx - push ebx - push esi - push eax - push ebp - call ecx - mov ecx, [esi+18h] - mov edi, eax - mov eax, [esi+1Ch] - cmp eax, ebx - ja short loc_4D0700 - cmp ecx, 0FFFFFFFFh - jbe short loc_4D0709 - -loc_4D0700: ; CODE XREF: Compress_lzma_internal+2A9j - or ecx, 0FFFFFFFFh - mov [esp+6Ch+pcbInBuffer], ebx - jmp short loc_4D070D -; --------------------------------------------------------------------------- - -loc_4D0709: ; CODE XREF: Compress_lzma_internal+2AEj - mov [esp+6Ch+pcbInBuffer], eax - -loc_4D070D: ; CODE XREF: Compress_lzma_internal+2B7j - mov edx, [esp+6Ch+pcbOutBuffer] - mov [edx], ecx - cmp [esi+20h], bl - jz short loc_4D0723 - mov [esp+6Ch+var_58], 1 - -loc_4D0723: ; CODE XREF: Compress_lzma_internal+28Bj - ; Compress_lzma_internal+2C9j - mov eax, [esp+6Ch+var_50] - cmp eax, ebx - jz short loc_4D0733 - mov ecx, [eax] - mov edx, [ecx+8] - push eax - call edx - -loc_4D0733: ; CODE XREF: Compress_lzma_internal+2D9j - mov eax, [esi] - mov ecx, [eax+8] - push esi - call ecx - mov edx, [ebp+0] - mov eax, [edx+10h] - push 1 - mov ecx, ebp - call eax - mov eax, [esp+6Ch+var_58] - cmp eax, ebx - jnz short loc_4D0755 - neg edi - sbb edi, edi - mov eax, edi - -loc_4D0755: ; CODE XREF: Compress_lzma_internal+2FDj - mov ecx, [esp+6Ch+var_C] - mov dword ptr fs:[0], ecx - pop ecx - pop edi - pop esi - pop ebp - pop ebx - add esp, 58h - retn -_starcraft_compress_lzma endp -;Compress_lzma_internal endp - -; =============== S U B R O U T I N E ======================================= - -Compress_lzma proc near ; DATA XREF: .rdata:00509684o - -pbOutBuffer = dword ptr 4 -pcbOutBuffer = dword ptr 8 -pbInBuffer = dword ptr 0Ch -cbInBuffer = dword ptr 10h - - push esi - push edi -; call sub_47F198 - mov esi, [esp+8+pcbOutBuffer] ; ESI = pcbOutBuffer - mov edi, [esp+8+cbInBuffer] ; EDI = cbInBuffer -; push offset GiveDataToCompress -; push offset FreeMemory_47F3AB -; push offset AllocateMemory_47F396 - push esi ; pcbOutBuffer - push 0 - mov [eax+493E0h], eax - push dword ptr [esi] ; cbOutBuffer - push [esp+20h+pbOutBuffer] ; pbOutBuffer - push 0 - push edi ; cbInBuffer - push [esp+2Ch+pbInBuffer] ; pbInBuffer -; call Compress_lzma_internal - add esp, 28h - sub eax, 0 - jz short loc_47F401 - mov [esi], edi - dec eax - -loc_47F401: ; CODE XREF: Compress_lzma+3Fj - pop edi - pop esi - retn -Compress_lzma endp - -; =============== S U B R O U T I N E ======================================= - -sub_4D0770 proc near ; CODE XREF: CEncoder_Flush+1Ap - -arg_0 = dword ptr 4 - - push edi - mov edi, ecx - cmp byte ptr [edi+33B94h], 0 - jz loc_4D0881 - movzx eax, byte ptr [edi+10h] - mov ecx, [edi+38h] - shl eax, 4 - push ebx - mov ebx, [esp+8+arg_0] - add eax, ebx - lea edx, [edi+eax*4+280F0h] - push ebp - push esi - lea esi, [edi+28h] - mov eax, ecx - shr eax, 0Bh - imul eax, [edx] - add [esi+8], eax - adc dword ptr [esi+0Ch], 0 - sub ecx, eax - mov [esi+10h], ecx - mov eax, [edx] - mov ecx, eax - shr ecx, 5 - sub eax, ecx - mov [edx], eax - mov eax, [esi+10h] - cmp eax, 1000000h - jnb short loc_4D07D3 - shl eax, 8 - mov ecx, esi - mov [esi+10h], eax - call loc_4CF640 - -loc_4D07D3: ; CODE XREF: sub_4D0770+54j - movzx edx, byte ptr [edi+10h] - mov ecx, [esi+10h] - lea eax, [edi+edx*4+283F0h] - shr ecx, 0Bh - imul ecx, [eax] - mov [esi+10h], ecx - mov ecx, [eax] - mov edx, 800h - sub edx, ecx - shr edx, 5 - add edx, ecx - mov [eax], edx - mov eax, [esi+10h] - cmp eax, 1000000h - jnb short loc_4D0811 - shl eax, 8 - mov ecx, esi - mov [esi+10h], eax - call loc_4CF640 - -loc_4D0811: ; CODE XREF: sub_4D0770+92j - movzx eax, byte ptr [edi+10h] - mov cl, ds:kMatchNextStates[eax] - push ebx - mov [edi+10h], cl - cmp byte ptr [edi+32EF4h], 0 - push 0 - lea ebp, [edi+28DB8h] - push esi - mov ecx, ebp - setz byte ptr [esp+1Ch+arg_0] - call sub_4D0020 - cmp byte ptr [esp+10h+arg_0], 0 - jz short loc_4D0854 - add dword ptr [ebp+ebx*4+4C0Ch], 0FFFFFFFFh - jnz short loc_4D0854 - push ebx - mov ecx, ebp - call sub_4CF740 - -loc_4D0854: ; CODE XREF: sub_4D0770+D0j - ; sub_4D0770+DAj - push 3Fh - push esi - lea ecx, [edi+287B0h] - call sub_4CFC60 - push 1Ah - push 3FFFFFFh - mov ecx, esi - call sub_4CF6E0 - push 0Fh - push esi - lea ecx, [edi+28D78h] - call sub_4CFD00 - pop esi - pop ebp - pop ebx - -loc_4D0881: ; CODE XREF: sub_4D0770+Aj - pop edi - retn 4 -sub_4D0770 endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -CEncoder_Flush proc near ; CODE XREF: CEncoder_CodeOneBlock+BAp - ; CEncoder_CodeOneBlock+1D0p ... - -arg_0 = dword ptr 4 - - push esi - mov esi, ecx - mov eax, [esi+0ECh] - test eax, eax - jnz short loc_4D08E5 - mov eax, [esi+33B60h] - and eax, [esp+4+arg_0] - push ebx - push edi - push eax - call sub_4D0770 - lea ebx, [esi+28h] - mov edi, 5 - -loc_4D08B7: ; CODE XREF: CEncoder_Flush+31j - mov ecx, ebx - call loc_4CF640 - sub edi, 1 - jnz short loc_4D08B7 - mov ecx, [esi+4Ch] - add esi, 40h - cmp ecx, [esi+4] - pop edi - pop ebx - jz short loc_4D08E3 - -loc_4D08D0: ; CODE XREF: CEncoder_Flush+51j - mov ecx, esi - call sub_4CF450 - test eax, eax - jnz short loc_4D08E5 - mov edx, [esi+0Ch] - cmp edx, [esi+4] - jnz short loc_4D08D0 - -loc_4D08E3: ; CODE XREF: CEncoder_Flush+3Ej - xor eax, eax - -loc_4D08E5: ; CODE XREF: CEncoder_Flush+Bj - ; CEncoder_Flush+49j - pop esi - retn 4 -CEncoder_Flush endp - -; --------------------------------------------------------------------------- - align 10h -; =============== S U B R O U T I N E ======================================= - -CEncoder_CodeOneBlock proc near ; CODE XREF: CEncoder_CodeReal+B1p - ; CEncoder_CodeReal+F5p - -var_20 = byte ptr -20h -var_1C = dword ptr -1Ch -var_18 = dword ptr -18h -var_14 = dword ptr -14h -var_10 = dword ptr -10h -var_C = dword ptr -0Ch -var_8 = dword ptr -8 -var_4 = dword ptr -4 -arg_0 = dword ptr 4 -arg_4 = dword ptr 8 -arg_8 = dword ptr 0Ch - - sub esp, 20h - push ebp - push esi - mov esi, ecx - push edi - mov edi, [esi+33B84h] - xor ebp, ebp - cmp edi, ebp - jz short loc_4D0952 - mov eax, [edi] - mov ecx, [eax+4] - push edi - call ecx - mov eax, [esi+33B8Ch] - cmp eax, ebp - jz short loc_4D091E - mov edx, [eax] - push eax - mov eax, [edx+8] - call eax - -loc_4D091E: ; CODE XREF: CEncoder_CodeOneBlock+24j - mov [esi+33B8Ch], edi - mov ecx, [esi+80h] - mov edx, [esi+68h] - lea eax, [esi+33B88h] - push ecx - mov dword ptr [eax], offset loc_4CF210 - mov [esi+0B8h], eax - call edx - add esp, 4 - mov byte ptr [esi+33B95h], 1 - mov [esi+33B84h], ebp - -loc_4D0952: ; CODE XREF: CEncoder_CodeOneBlock+12j - mov eax, [esp+2Ch+arg_8] - mov dword ptr [eax], 1 - cmp byte ptr [esi+33B80h], 0 - jz short loc_4D0974 - mov eax, [esi+0ECh] - pop edi - pop esi - pop ebp - add esp, 20h - retn 0Ch -; --------------------------------------------------------------------------- - -loc_4D0974: ; CODE XREF: CEncoder_CodeOneBlock+73j - mov ecx, [esi+33B78h] - or ecx, [esi+33B7Ch] - push ebx - mov byte ptr [esi+33B80h], 1 - jnz loc_4D0A9C - mov edx, [esi+80h] - mov eax, [esi+70h] - push edx - call eax - add esp, 4 - test eax, eax - jnz short loc_4D09B9 - mov ecx, [esi+33B78h] - push ecx - mov ecx, esi - call CEncoder_Flush - pop ebx - pop edi - pop esi - pop ebp - add esp, 20h - retn 0Ch -; --------------------------------------------------------------------------- - -loc_4D09B9: ; CODE XREF: CEncoder_CodeOneBlock+AFj - lea edx, [esp+30h+var_4] - push edx - mov ecx, esi - call sub_4CB3B0 - movzx ecx, byte ptr [esi+10h] - mov eax, [esi+33B60h] - and eax, [esi+33B78h] - mov edx, [esi+38h] - lea edi, [esi+28h] - shl ecx, 4 - add eax, ecx - shr edx, 0Bh - imul edx, [esi+eax*4+280F0h] - lea eax, [esi+eax*4+280F0h] - mov [edi+10h], edx - mov ecx, [eax] - mov edx, 800h - sub edx, ecx - shr edx, 5 - add edx, ecx - mov [eax], edx - mov eax, [edi+10h] - cmp eax, 1000000h - jnb short loc_4D0A1C - shl eax, 8 - mov ecx, edi - mov [edi+10h], eax - call loc_4CF640 - -loc_4D0A1C: ; CODE XREF: CEncoder_CodeOneBlock+11Dj - movzx eax, byte ptr [esi+10h] - mov cl, ds:kLiteralNextStates[eax] - mov [esi+10h], cl - mov edx, [esi+32F04h] - mov eax, [esi+80h] - mov ecx, [esi+6Ch] - neg edx - push edx - push eax - call ecx - mov bl, al - mov eax, [esi+32654h] - add esp, 8 - mov byte ptr [esp+30h+var_10], bl - mov edx, [esp+30h+var_10] - push edx - movzx edx, byte ptr [esi+11h] - push edi - mov edi, [esi+3265Ch] - and edi, [esi+33B78h] - mov ecx, 8 - sub cl, al - shr edx, cl - mov ecx, eax - shl edi, cl - add edx, edi - imul edx, 0C00h - add edx, [esi+32650h] - mov ecx, edx - call sub_4CFE30 - add dword ptr [esi+32F04h], 0FFFFFFFFh - add dword ptr [esi+33B78h], 1 - mov [esi+11h], bl - adc [esi+33B7Ch], ebp - -loc_4D0A9C: ; CODE XREF: CEncoder_CodeOneBlock+98j - mov edi, [esi+33B78h] - mov eax, [esi+80h] - mov ecx, [esi+70h] - push eax - mov [esp+34h+var_14], edi - mov [esp+34h+var_4], edi - call ecx - add esp, 4 - test eax, eax - jnz short loc_4D0AD4 - push edi - mov ecx, esi - call CEncoder_Flush - pop ebx - pop edi - pop esi - pop ebp - add esp, 20h - retn 0Ch -; --------------------------------------------------------------------------- - align 10h - -loc_4D0AD0: ; CODE XREF: CEncoder_CodeOneBlock+735j - ; CEncoder_CodeOneBlock+78Bj - mov edi, [esp+30h+var_14] - -loc_4D0AD4: ; CODE XREF: CEncoder_CodeOneBlock+1CBj - cmp byte ptr [esi+32EF4h], 0 - mov ecx, esi - jz short loc_4D0AEB - lea edx, [esp+30h+var_1C] - push edx - call CEncoder_GetOptimumFast - jmp short loc_4D0AF6 -; --------------------------------------------------------------------------- - -loc_4D0AEB: ; CODE XREF: CEncoder_CodeOneBlock+1EDj - lea eax, [esp+30h+var_1C] - push eax - push edi - call CEncoder_GetOptimum - -loc_4D0AF6: ; CODE XREF: CEncoder_CodeOneBlock+1F9j - mov ebx, [esi+33B60h] - and ebx, edi - cmp eax, 1 - mov [esp+30h+var_18], eax - jnz loc_4D0C0E - cmp [esp+30h+var_1C], 0FFFFFFFFh - jnz loc_4D0C0E - movzx ecx, byte ptr [esi+10h] - mov edx, [esi+38h] - lea ebp, [esi+28h] - shl ecx, 4 - shr edx, 0Bh - add ecx, ebx - imul edx, [esi+ecx*4+280F0h] - lea eax, [esi+ecx*4+280F0h] - mov [ebp+10h], edx - mov ecx, [eax] - mov edx, 800h - sub edx, ecx - shr edx, 5 - add edx, ecx - mov [eax], edx - mov eax, [ebp+10h] - cmp eax, 1000000h - jnb short loc_4D0B61 - shl eax, 8 - mov ecx, ebp - mov [ebp+10h], eax - call loc_4CF640 - -loc_4D0B61: ; CODE XREF: CEncoder_CodeOneBlock+262j - mov eax, [esi+32F04h] - mov ecx, [esi+80h] - mov edx, [esi+6Ch] - neg eax - push eax - push ecx - call edx - movzx edi, byte ptr [esi+11h] - mov edx, [esi+3265Ch] - and edx, [esp+38h+var_14] - mov bl, al - mov eax, [esi+32654h] - mov ecx, 8 - sub cl, al - shr edi, cl - mov ecx, eax - shl edx, cl - add esp, 8 - mov byte ptr [esp+30h+var_10], bl - add edi, edx - imul edi, 0C00h - add edi, [esi+32650h] - cmp byte ptr [esi+10h], 7 - jnb short loc_4D0BC3 - mov eax, [esp+30h+var_10] - push eax - push ebp - mov ecx, edi - call sub_4CFE30 - jmp short loc_4D0BF5 -; --------------------------------------------------------------------------- - -loc_4D0BC3: ; CODE XREF: CEncoder_CodeOneBlock+2C2j - mov edx, [esi+80h] - mov eax, [esi+6Ch] - or ecx, 0FFFFFFFFh - sub ecx, [esi+14h] - sub ecx, [esi+32F04h] - push ecx - push edx - call eax - mov ecx, [esp+38h+var_10] - add esp, 8 - mov byte ptr [esp+30h+var_C], al - mov edx, [esp+30h+var_C] - push ecx - push edx - push ebp - mov ecx, edi - call sub_4CFED0 - -loc_4D0BF5: ; CODE XREF: CEncoder_CodeOneBlock+2D1j - movzx eax, byte ptr [esi+10h] - mov cl, ds:kLiteralNextStates[eax] - mov ebp, [esp+30h+var_18] - mov [esi+10h], cl - mov [esi+11h], bl - jmp loc_4D1013 -; --------------------------------------------------------------------------- - -loc_4D0C0E: ; CODE XREF: CEncoder_CodeOneBlock+215j - ; CEncoder_CodeOneBlock+220j - movzx edx, byte ptr [esi+10h] - mov ecx, [esi+38h] - lea edi, [esi+28h] - shl edx, 4 - mov eax, ecx - shr eax, 0Bh - add edx, ebx - imul eax, [esi+edx*4+280F0h] - add [edi+8], eax - lea edx, [esi+edx*4+280F0h] - mov ebp, 0 - adc [edi+0Ch], ebp - sub ecx, eax - mov [edi+10h], ecx - mov eax, [edx] - mov ecx, eax - shr ecx, 5 - sub eax, ecx - mov [edx], eax - mov eax, [edi+10h] - cmp eax, 1000000h - jnb short loc_4D0C63 - shl eax, 8 - mov ecx, edi - mov [edi+10h], eax - call loc_4CF640 - -loc_4D0C63: ; CODE XREF: CEncoder_CodeOneBlock+364j - cmp [esp+30h+var_1C], 4 - movzx edx, byte ptr [esi+10h] - mov ecx, [edi+10h] - jnb loc_4D0E91 - mov eax, ecx - shr eax, 0Bh - imul eax, [esi+edx*4+283F0h] - add [edi+8], eax - lea edx, [esi+edx*4+283F0h] - adc [edi+0Ch], ebp - sub ecx, eax - mov [edi+10h], ecx - mov eax, [edx] - mov ecx, eax - shr ecx, 5 - sub eax, ecx - mov [edx], eax - mov eax, [edi+10h] - cmp eax, 1000000h - jnb short loc_4D0CB6 - shl eax, 8 - mov ecx, edi - mov [edi+10h], eax - call loc_4CF640 - -loc_4D0CB6: ; CODE XREF: CEncoder_CodeOneBlock+3B7j - mov eax, [esp+30h+var_1C] - cmp eax, ebp - mov ecx, [edi+10h] - jnz short loc_4D0D24 - movzx edx, byte ptr [esi+10h] - lea eax, [esi+edx*4+28420h] - shr ecx, 0Bh - imul ecx, [eax] - mov [edi+10h], ecx - mov ecx, [eax] - mov edx, 800h - sub edx, ecx - shr edx, 5 - add edx, ecx - mov [eax], edx - mov eax, [edi+10h] - cmp eax, 1000000h - jnb short loc_4D0CFC - shl eax, 8 - mov ecx, edi - mov [edi+10h], eax - call loc_4CF640 - -loc_4D0CFC: ; CODE XREF: CEncoder_CodeOneBlock+3FDj - mov ebp, [esp+30h+var_18] - movzx ecx, byte ptr [esi+10h] - xor eax, eax - cmp ebp, 1 - setnz al - shl ecx, 4 - add ecx, ebx - lea ecx, [esi+ecx*4+284B0h] - push eax - push edi - call sub_4CFAC0 - jmp loc_4D0E2E -; --------------------------------------------------------------------------- - -loc_4D0D24: ; CODE XREF: CEncoder_CodeOneBlock+3CFj - mov edx, [esi+eax*4+14h] - movzx eax, byte ptr [esi+10h] - mov [esp+30h+var_8], edx - lea edx, [esi+eax*4+28420h] - mov eax, ecx - shr eax, 0Bh - imul eax, [edx] - add [edi+8], eax - adc [edi+0Ch], ebp - sub ecx, eax - mov [edi+10h], ecx - mov eax, [edx] - mov ecx, eax - shr ecx, 5 - sub eax, ecx - mov [edx], eax - mov eax, [edi+10h] - cmp eax, 1000000h - jnb short loc_4D0D6C - shl eax, 8 - mov ecx, edi - mov [edi+10h], eax - call loc_4CF640 - -loc_4D0D6C: ; CODE XREF: CEncoder_CodeOneBlock+46Dj - cmp [esp+30h+var_1C], 1 - mov ecx, [edi+10h] - jnz short loc_4D0DB3 - movzx edx, byte ptr [esi+10h] - lea eax, [esi+edx*4+28450h] - shr ecx, 0Bh - imul ecx, [eax] - mov [edi+10h], ecx - mov ecx, [eax] - mov edx, 800h - sub edx, ecx - shr edx, 5 - add edx, ecx - mov [eax], edx - mov eax, [edi+10h] - cmp eax, 1000000h - jnb short loc_4D0E1D - shl eax, 8 - mov ecx, edi - mov [edi+10h], eax - call loc_4CF640 - jmp short loc_4D0E1D -; --------------------------------------------------------------------------- - -loc_4D0DB3: ; CODE XREF: CEncoder_CodeOneBlock+484j - movzx eax, byte ptr [esi+10h] - lea edx, [esi+eax*4+28450h] - mov eax, ecx - shr eax, 0Bh - imul eax, [edx] - add [edi+8], eax - adc [edi+0Ch], ebp - sub ecx, eax - mov [edi+10h], ecx - mov eax, [edx] - mov ecx, eax - shr ecx, 5 - sub eax, ecx - mov [edx], eax - mov eax, [edi+10h] - cmp eax, 1000000h - jnb short loc_4D0DF3 - shl eax, 8 - mov ecx, edi - mov [edi+10h], eax - call loc_4CF640 - -loc_4D0DF3: ; CODE XREF: CEncoder_CodeOneBlock+4F4j - mov ebp, [esp+30h+var_1C] - movzx eax, byte ptr [esi+10h] - lea edx, [ebp-2] - push edx - push edi - lea ecx, [esi+eax*4+28480h] - call sub_4CFAC0 - cmp ebp, 3 - jnz short loc_4D0E17 - mov ecx, [esi+1Ch] - mov [esi+20h], ecx - -loc_4D0E17: ; CODE XREF: CEncoder_CodeOneBlock+51Fj - mov edx, [esi+18h] - mov [esi+1Ch], edx - -loc_4D0E1D: ; CODE XREF: CEncoder_CodeOneBlock+4B2j - ; CEncoder_CodeOneBlock+4C1j - mov eax, [esi+14h] - mov ecx, [esp+30h+var_8] - mov ebp, [esp+30h+var_18] - mov [esi+18h], eax - mov [esi+14h], ecx - -loc_4D0E2E: ; CODE XREF: CEncoder_CodeOneBlock+42Fj - cmp ebp, 1 - jnz short loc_4D0E45 - movzx edx, byte ptr [esi+10h] - mov al, ds:kShortRepNextStates[edx] - mov [esi+10h], al - jmp loc_4D0FF5 -; --------------------------------------------------------------------------- - -loc_4D0E45: ; CODE XREF: CEncoder_CodeOneBlock+541j - cmp byte ptr [esi+32EF4h], 0 - push ebx - lea edx, [ebp-2] - push edx - lea ecx, [esi+2DA04h] - push edi - setz [esp+3Ch+var_20] - call sub_4D0020 - cmp [esp+30h+var_20], 0 - jz short loc_4D0E7F - add dword ptr [esi+ebx*4+32610h], 0FFFFFFFFh - lea ecx, [esi+2DA04h] - jnz short loc_4D0E7F - push ebx - call sub_4CF740 - -loc_4D0E7F: ; CODE XREF: CEncoder_CodeOneBlock+577j - ; CEncoder_CodeOneBlock+587j - movzx eax, byte ptr [esi+10h] - mov cl, ds:kRepNextStates[eax] - mov [esi+10h], cl - jmp loc_4D0FF5 -; --------------------------------------------------------------------------- - -loc_4D0E91: ; CODE XREF: CEncoder_CodeOneBlock+37Fj - lea eax, [esi+edx*4+283F0h] - shr ecx, 0Bh - imul ecx, [eax] - mov [edi+10h], ecx - mov ecx, [eax] - mov edx, 800h - sub edx, ecx - shr edx, 5 - add edx, ecx - mov [eax], edx - mov eax, [edi+10h] - cmp eax, 1000000h - jnb short loc_4D0EC8 - shl eax, 8 - mov ecx, edi - mov [edi+10h], eax - call loc_4CF640 - -loc_4D0EC8: ; CODE XREF: CEncoder_CodeOneBlock+5C9j - movzx eax, byte ptr [esi+10h] - mov cl, ds:kMatchNextStates[eax] - mov eax, [esp+30h+var_18] - mov [esi+10h], cl - cmp byte ptr [esi+32EF4h], 0 - push ebx - setz [esp+34h+var_20] - add eax, 0FFFFFFFEh - push eax - lea ebp, [esi+28DB8h] - push edi - mov ecx, ebp - call sub_4D0020 - cmp [esp+30h+var_20], 0 - jz short loc_4D0F11 - add dword ptr [ebp+ebx*4+4C0Ch], 0FFFFFFFFh - jnz short loc_4D0F11 - push ebx - mov ecx, ebp - call sub_4CF740 - -loc_4D0F11: ; CODE XREF: CEncoder_CodeOneBlock+60Dj - ; CEncoder_CodeOneBlock+617j - mov eax, [esp+30h+var_1C] - sub eax, 4 - cmp eax, 2000h - mov [esp+30h+var_1C], eax - jnb short loc_4D0F2C - movzx ebp, byte ptr dword_551198[eax] - jmp short loc_4D0F4F -; --------------------------------------------------------------------------- - -loc_4D0F2C: ; CODE XREF: CEncoder_CodeOneBlock+631j - cmp eax, 2000000h - jnb short loc_4D0F42 - shr eax, 0Ch - movzx ebp, byte ptr dword_551198[eax] - add ebp, 18h - jmp short loc_4D0F4F -; --------------------------------------------------------------------------- - -loc_4D0F42: ; CODE XREF: CEncoder_CodeOneBlock+641j - shr eax, 18h - movzx ebp, byte ptr dword_551198[eax] - add ebp, 30h - -loc_4D0F4F: ; CODE XREF: CEncoder_CodeOneBlock+63Aj - ; CEncoder_CodeOneBlock+650j - mov eax, [esp+30h+var_18] - add eax, 0FFFFFFFEh - cmp eax, 4 - jb short loc_4D0F60 - mov eax, 3 - -loc_4D0F60: ; CODE XREF: CEncoder_CodeOneBlock+669j - push ebp - shl eax, 8 - push edi - lea ecx, [eax+esi+287B0h] - call sub_4CFC60 - cmp ebp, 4 - jb short loc_4D0FD1 - mov ebx, [esp+30h+var_1C] - mov ecx, ebp - mov eax, ebp - and eax, 1 - shr ecx, 1 - sub ecx, 1 - or eax, 2 - shl eax, cl - sub ebx, eax - cmp ebp, 0Eh - jnb short loc_4D0FA9 - push ebx - push ecx - sub eax, ebp - lea edx, [esi+eax*4+28BACh] - push edi - push edx - call sub_4CFD90 - add esp, 10h - jmp short loc_4D0FD1 -; --------------------------------------------------------------------------- - -loc_4D0FA9: ; CODE XREF: CEncoder_CodeOneBlock+6A0j - add ecx, 0FFFFFFFCh - push ecx - mov eax, ebx - shr eax, 4 - push eax - mov ecx, edi - call sub_4CF6E0 - and ebx, 0Fh - push ebx - push edi - lea ecx, [esi+28D78h] - call sub_4CFD00 - add dword ptr [esi+33B54h], 1 - -loc_4D0FD1: ; CODE XREF: CEncoder_CodeOneBlock+684j - ; CEncoder_CodeOneBlock+6B7j - mov ecx, [esi+1Ch] - mov edx, [esi+18h] - mov eax, [esi+14h] - add dword ptr [esi+33B70h], 1 - mov ebp, [esp+30h+var_18] - mov [esi+20h], ecx - mov ecx, [esp+30h+var_1C] - mov [esi+1Ch], edx - mov [esi+18h], eax - mov [esi+14h], ecx - -loc_4D0FF5: ; CODE XREF: CEncoder_CodeOneBlock+550j - ; CEncoder_CodeOneBlock+59Cj - mov eax, [esi+80h] - mov ecx, [esi+6Ch] - mov edx, ebp - sub edx, [esi+32F04h] - sub edx, 1 - push edx - push eax - call ecx - add esp, 8 - mov [esi+11h], al - -loc_4D1013: ; CODE XREF: CEncoder_CodeOneBlock+319j - sub [esi+32F04h], ebp - mov eax, [esi+32F04h] - add [esp+30h+var_14], ebp - test eax, eax - jnz loc_4D0AD0 - cmp [esi+32EF4h], al - jnz short loc_4D1056 - cmp dword ptr [esi+33B70h], 80h - jb short loc_4D1046 - mov ecx, esi - call CEncoder_FillDistancesPrices - -loc_4D1046: ; CODE XREF: CEncoder_CodeOneBlock+74Dj - cmp dword ptr [esi+33B54h], 10h - jb short loc_4D1056 - mov ecx, esi - call CEncoder_FillAlignPrices - -loc_4D1056: ; CODE XREF: CEncoder_CodeOneBlock+741j - ; CEncoder_CodeOneBlock+75Dj - mov edx, [esi+80h] - mov eax, [esi+70h] - push edx - call eax - add esp, 4 - test eax, eax - mov eax, [esp+30h+var_14] - jz short loc_4D10D7 - mov ecx, [esp+30h+var_4] - mov edx, eax - sub edx, ecx - cmp edx, 4000h - jb loc_4D0AD0 - sub eax, ecx - add [esi+33B78h], eax - mov ecx, [esi+33B78h] - mov eax, [esp+30h+arg_0] - adc dword ptr [esi+33B7Ch], 0 - mov [eax], ecx - mov edx, [esi+33B7Ch] - lea ecx, [esi+28h] - mov [eax+4], edx - call sub_4CD8B0 - mov ecx, [esp+30h+arg_4] - mov [ecx], eax - mov eax, [esp+30h+arg_8] - mov [ecx+4], edx - pop ebx - mov byte ptr [esi+33B80h], 0 - pop edi - mov dword ptr [eax], 0 - mov eax, [esi+0ECh] - pop esi - pop ebp - add esp, 20h - retn 0Ch -; --------------------------------------------------------------------------- - -loc_4D10D7: ; CODE XREF: CEncoder_CodeOneBlock+77Bj - push eax - mov ecx, esi - call CEncoder_Flush - pop ebx - pop edi - pop esi - pop ebp - add esp, 20h - retn 0Ch -CEncoder_CodeOneBlock endp - -; --------------------------------------------------------------------------- - align 10h -CEncoder_CodeReal proc near ; CODE XREF: CEncoder_Code+49p - -var_20 = dword ptr -20h -processedOutSize= byte ptr -1Ch -processedInSize = byte ptr -14h -var_C = dword ptr -0Ch -var_4 = dword ptr -4 -inStream = dword ptr 4 -outStream = dword ptr 8 -inSize = dword ptr 0Ch -outSize = dword ptr 10h -progress = dword ptr 14h - - push 0FFFFFFFFh - push offset loc_4DA128 - mov eax, dword ptr fs:[0] - push eax - sub esp, 14h - push ebp - push esi - push edi - mov eax, dword_544960 - xor eax, esp - push eax - lea eax, [esp+30h+var_C] - mov dword ptr fs:[0], eax - mov esi, ecx - mov [esp+30h+var_20], esi - mov eax, [esp+30h+outSize] - mov ecx, [esp+30h+inSize] - mov edx, [esp+30h+outStream] - push eax - mov eax, [esp+34h+inStream] - push ecx - push edx - push eax - mov ecx, esi - mov [esp+40h+var_4], 0 - call CEncoder_SetStreams - mov edi, eax - test edi, edi - jz short loc_4D1190 - cmp dword ptr [esi+80h], 0 - mov [esp+30h+var_4], 0FFFFFFFFh - jz short loc_4D1166 - cmp byte ptr [esi+33B95h], 0 - jz short loc_4D1166 - mov byte ptr [esi+33B95h], 0 - -loc_4D1166: ; CODE XREF: CEncoder_CodeReal+64j - ; CEncoder_CodeReal+6Dj - mov eax, [esi+33B8Ch] - test eax, eax - jz short loc_4D1182 - mov ecx, [eax] - mov edx, [ecx+8] - push eax - call edx - mov dword ptr [esi+33B8Ch], 0 - -loc_4D1182: ; CODE XREF: CEncoder_CodeReal+7Ej - mov ecx, [esi+4] - mov edx, [ecx+10h] - lea eax, [esi+4] - push eax - call edx - jmp short loc_4D1201 -; --------------------------------------------------------------------------- - -loc_4D1190: ; CODE XREF: CEncoder_CodeReal+53j - lea eax, [esp+30h+outSize] - push eax - lea ecx, [esp+34h+processedOutSize] - push ecx - lea edx, [esp+38h+processedInSize] - push edx - mov ecx, esi - call CEncoder_CodeOneBlock - mov edi, eax - test edi, edi - jnz short loc_4D11F0 - mov ebp, [esp+30h+progress] - -loc_4D11B0: ; CODE XREF: CEncoder_CodeReal+FEj - cmp [esp+30h+outSize], 0 - jnz short loc_4D1218 - test ebp, ebp - jz short loc_4D11D4 - mov eax, [ebp+0] - mov eax, [eax+0Ch] - lea ecx, [esp+30h+processedOutSize] - push ecx - lea edx, [esp+34h+processedInSize] - push edx - push ebp - call eax ; SetRatioInfo - mov edi, eax - test edi, edi - jnz short loc_4D11F0 - -loc_4D11D4: ; CODE XREF: CEncoder_CodeReal+C9j - lea ecx, [esp+30h+outSize] - push ecx - lea edx, [esp+34h+processedOutSize] - push edx - lea eax, [esp+38h+processedInSize] - push eax - mov ecx, esi - call CEncoder_CodeOneBlock - mov edi, eax - test edi, edi - jz short loc_4D11B0 - -loc_4D11F0: ; CODE XREF: CEncoder_CodeReal+BAj - ; CEncoder_CodeReal+E2j - lea ecx, [esp+30h+var_20] - mov [esp+30h+var_4], 0FFFFFFFFh - call sub_4CDA70 - -loc_4D1201: ; CODE XREF: CEncoder_CodeReal+9Ej - mov eax, edi - mov ecx, [esp+30h+var_C] - mov dword ptr fs:[0], ecx - pop ecx - pop edi - pop esi - pop ebp - add esp, 20h - retn 14h -; --------------------------------------------------------------------------- - -loc_4D1218: ; CODE XREF: CEncoder_CodeReal+C5j - lea ecx, [esp+30h+var_20] - mov [esp+30h+var_4], 0FFFFFFFFh - call sub_4CDA70 - xor eax, eax - mov ecx, [esp+30h+var_C] - mov dword ptr fs:[0], ecx - pop ecx - pop edi - pop esi - pop ebp - add esp, 20h - retn 14h -CEncoder_CodeReal endp - -; =============== S U B R O U T I N E ======================================= -; Attributes: bp-based frame - -CEncoder_Code proc near ; DATA XREF: .rdata:00517B08o -; sub_4D1240 - -var_10 = dword ptr -10h -var_C = dword ptr -0Ch -var_4 = dword ptr -4 -pThis = dword ptr 8 -pInStream = dword ptr 0Ch -pOutStream = dword ptr 10h -pInSize = dword ptr 14h -pOutSize = dword ptr 18h -progress = dword ptr 1Ch - - push ebp - mov ebp, esp - push 0FFFFFFFFh - push offset loc_4DA150 - mov eax, dword ptr fs:[0] - push eax - sub esp, 8 - push ebx - push esi - push edi - mov eax, dword_544960 - xor eax, ebp - push eax - lea eax, [ebp+var_C] - mov dword ptr fs:[0], eax - mov [ebp+var_10], esp - mov eax, [ebp+progress] - mov ecx, [ebp+pOutSize] - mov edx, [ebp+pInSize] - push eax - mov eax, [ebp+pOutStream] - push ecx - mov ecx, [ebp+pInStream] - push edx - push eax - push ecx - mov ecx, [ebp+pThis] - mov [ebp+var_4], 0 - call CEncoder_CodeReal - mov ecx, [ebp+var_C] - mov dword ptr fs:[0], ecx - pop ecx - pop edi - pop esi - pop ebx - mov esp, ebp - pop ebp - retn 18h -CEncoder_Code endp - -; --------------------------------------------------------------------------- - -loc_4DA0FB: ; DATA XREF: Compress_lzma_internal+2o - mov edx, [esp+8] - lea eax, [edx-5Ch] - mov ecx, [edx-60h] - xor ecx, eax - call sub_4A0686 - mov eax, 0 ; offset dword_526E44 - int 3; -; jmp ___CxxFrameHandler3 - -loc_4DA0C9: ; DATA XREF: sub_4CF810+2o - mov edx, [esp+8] - lea eax, [edx-0Ch] - mov ecx, [edx-10h] - xor ecx, eax - call sub_4A0686 - mov eax, 0 ; offset dword_526E18 - int 3; -; jmp ___CxxFrameHandler3 - -loc_4DA128: ; DATA XREF: CEncoder_CodeReal+2o - mov edx, [esp+8] - lea eax, [edx-20h] - mov ecx, [edx-24h] - xor ecx, eax - call sub_4A0686 - mov eax, 0 ; offset unk_526E70 - int 3; -; jmp ___CxxFrameHandler3 - -loc_4DA150: ; DATA XREF: CEncoder_Code+5o - mov edx, [esp+8] - lea eax, [edx+0Ch] - mov ecx, [edx-18h] - xor ecx, eax - call sub_4A0686 - mov eax, 0 ; offset unk_526ED8 - int 3; -; jmp ___CxxFrameHandler3 - -END diff --git a/src/tools/map_extractor/System.cpp b/src/tools/map_extractor/System.cpp index e50692820c..5685feb1d4 100644 --- a/src/tools/map_extractor/System.cpp +++ b/src/tools/map_extractor/System.cpp @@ -256,7 +256,7 @@ uint32 ReadBuild(int locale) //printf("Read %s file... ", filename.c_str()); HANDLE dbcFile; - if (!SFileOpenFileEx(LocaleMpq, filename.c_str(), SFILE_OPEN_PATCHED_FILE, &dbcFile)) + if (!SFileOpenFileEx(LocaleMpq, filename.c_str(), SFILE_OPEN_FROM_MPQ, &dbcFile)) { printf("Fatal error: Not found %s file!\n", filename.c_str()); exit(1); @@ -300,7 +300,7 @@ void ReadMapDBC() printf("Read Map.dbc file... "); HANDLE dbcFile; - if (!SFileOpenFileEx(LocaleMpq, "DBFilesClient\\Map.dbc", SFILE_OPEN_PATCHED_FILE, &dbcFile)) + if (!SFileOpenFileEx(LocaleMpq, "DBFilesClient\\Map.dbc", SFILE_OPEN_FROM_MPQ, &dbcFile)) { printf("Fatal error: Cannot find Map.dbc in archive!\n"); exit(1); @@ -330,7 +330,7 @@ void ReadLiquidMaterialTable() printf("Read LiquidMaterial.dbc file...\n"); HANDLE dbcFile; - if (!SFileOpenFileEx(LocaleMpq, "DBFilesClient\\LiquidMaterial.dbc", SFILE_OPEN_PATCHED_FILE, &dbcFile)) + if (!SFileOpenFileEx(LocaleMpq, "DBFilesClient\\LiquidMaterial.dbc", SFILE_OPEN_FROM_MPQ, &dbcFile)) { printf("Fatal error: Cannot find LiquidMaterial.dbc in archive!\n"); exit(1); @@ -357,7 +357,7 @@ void ReadLiquidObjectTable() printf("Read LiquidObject.dbc file...\n"); HANDLE dbcFile; - if (!SFileOpenFileEx(LocaleMpq, "DBFilesClient\\LiquidObject.dbc", SFILE_OPEN_PATCHED_FILE, &dbcFile)) + if (!SFileOpenFileEx(LocaleMpq, "DBFilesClient\\LiquidObject.dbc", SFILE_OPEN_FROM_MPQ, &dbcFile)) { printf("Fatal error: Cannot find LiquidObject.dbc in archive!\n"); exit(1); @@ -383,7 +383,7 @@ void ReadLiquidTypeTable() { printf("Read LiquidType.dbc file..."); HANDLE dbcFile; - if (!SFileOpenFileEx(LocaleMpq, "DBFilesClient\\LiquidType.dbc", SFILE_OPEN_PATCHED_FILE, &dbcFile)) + if (!SFileOpenFileEx(LocaleMpq, "DBFilesClient\\LiquidType.dbc", SFILE_OPEN_FROM_MPQ, &dbcFile)) { printf("Fatal error: Cannot find LiquidType.dbc in archive!\n"); exit(1); @@ -1129,7 +1129,7 @@ void ExtractDBCFiles(int locale) std::string fileName; do { - if (!SFileOpenFileEx(LocaleMpq, foundFile.cFileName, SFILE_OPEN_PATCHED_FILE, &dbcFile)) + if (!SFileOpenFileEx(LocaleMpq, foundFile.cFileName, SFILE_OPEN_FROM_MPQ, &dbcFile)) { printf("Unable to open file %s in the archive\n", foundFile.cFileName); continue; @@ -1171,7 +1171,7 @@ void ExtractDB2Files(int locale) std::string fileName; do { - if (!SFileOpenFileEx(LocaleMpq, foundFile.cFileName, SFILE_OPEN_PATCHED_FILE, &dbcFile)) + if (!SFileOpenFileEx(LocaleMpq, foundFile.cFileName, SFILE_OPEN_FROM_MPQ, &dbcFile)) { printf("Unable to open file %s in the archive\n", foundFile.cFileName); continue; @@ -1200,7 +1200,7 @@ void ExtractCameraFiles() printf("Extracting camera files...\n"); HANDLE dbcFile; - if (!SFileOpenFileEx(LocaleMpq, "DBFilesClient\\CinematicCamera.dbc", SFILE_OPEN_PATCHED_FILE, &dbcFile)) + if (!SFileOpenFileEx(LocaleMpq, "DBFilesClient\\CinematicCamera.dbc", SFILE_OPEN_FROM_MPQ, &dbcFile)) { printf("Fatal error: Cannot find Map.dbc in archive!\n"); exit(1); @@ -1246,7 +1246,7 @@ void ExtractCameraFiles() HANDLE dbcFile = nullptr; - if (!SFileOpenFileEx(WorldMpq, thisFile.c_str(), SFILE_OPEN_PATCHED_FILE, &dbcFile)) + if (!SFileOpenFileEx(WorldMpq, thisFile.c_str(), SFILE_OPEN_FROM_MPQ, &dbcFile)) { printf("Unable to open file %s in the archive\n", thisFile.c_str()); continue; @@ -1266,7 +1266,7 @@ bool LoadLocaleMPQFile(int locale) if (!SFileOpenArchive(fileName.c_str(), 0, MPQ_OPEN_READ_ONLY, &LocaleMpq)) { - if (GetLastError() != ERROR_PATH_NOT_FOUND) + if (SErrGetLastError() != ERROR_PATH_NOT_FOUND) { printf("\nLoading %s locale MPQs\n", Locales[locale]); printf("Cannot open archive %s\n", fileName.c_str()); @@ -1298,7 +1298,7 @@ bool LoadLocaleMPQFile(int locale) if (!SFileOpenPatchArchive(LocaleMpq, fileName.c_str(), prefix, 0)) { - if (GetLastError() != ERROR_FILE_NOT_FOUND) + if (SErrGetLastError() != ERROR_FILE_NOT_FOUND) printf("Cannot open patch archive %s\n", fileName.c_str()); continue; } @@ -1316,7 +1316,7 @@ void LoadCommonMPQFiles(uint32 build) printf("Loading common MPQ files\n"); if (!SFileOpenArchive(filename.c_str(), 0, MPQ_OPEN_READ_ONLY, &WorldMpq)) { - if (GetLastError() != ERROR_PATH_NOT_FOUND) + if (SErrGetLastError() != ERROR_PATH_NOT_FOUND) printf("Cannot open archive %s\n", filename.c_str()); return; } @@ -1330,7 +1330,7 @@ void LoadCommonMPQFiles(uint32 build) filename = Trinity::StringFormat("%s/Data/%s", input_path.string(), CONF_mpq_list[i]); if (!SFileOpenPatchArchive(WorldMpq, filename.c_str(), "", 0)) { - if (GetLastError() != ERROR_PATH_NOT_FOUND) + if (SErrGetLastError() != ERROR_PATH_NOT_FOUND) printf("Cannot open archive %s\n", filename.c_str()); else printf("Not found %s\n", filename.c_str()); @@ -1362,7 +1362,7 @@ void LoadCommonMPQFiles(uint32 build) if (!SFileOpenPatchArchive(WorldMpq, filename.c_str(), prefix, 0)) { - if (GetLastError() != ERROR_PATH_NOT_FOUND) + if (SErrGetLastError() != ERROR_PATH_NOT_FOUND) printf("Cannot open patch archive %s\n", filename.c_str()); else printf("Not found %s\n", filename.c_str()); @@ -1400,7 +1400,7 @@ int main(int argc, char * arg[]) //Open MPQs if (!LoadLocaleMPQFile(i)) { - if (GetLastError() != ERROR_PATH_NOT_FOUND) + if (SErrGetLastError() != ERROR_PATH_NOT_FOUND) printf("Unable to load %s locale archives!\n", Locales[i]); continue; } diff --git a/src/tools/map_extractor/loadlib.cpp b/src/tools/map_extractor/loadlib.cpp index e3a62420af..96787bc628 100644 --- a/src/tools/map_extractor/loadlib.cpp +++ b/src/tools/map_extractor/loadlib.cpp @@ -37,7 +37,7 @@ bool ChunkedFile::loadFile(HANDLE mpq, std::string const& fileName, bool log) { free(); HANDLE file; - if (!SFileOpenFileEx(mpq, fileName.c_str(), SFILE_OPEN_PATCHED_FILE, &file)) + if (!SFileOpenFileEx(mpq, fileName.c_str(), SFILE_OPEN_FROM_MPQ, &file)) { if (log) printf("No such file %s\n", fileName.c_str()); diff --git a/src/tools/vmap4_extractor/dbcfile.cpp b/src/tools/vmap4_extractor/dbcfile.cpp index 90c614b268..1b1da9d3a9 100644 --- a/src/tools/vmap4_extractor/dbcfile.cpp +++ b/src/tools/vmap4_extractor/dbcfile.cpp @@ -26,7 +26,7 @@ DBCFile::DBCFile(HANDLE mpq, char const* filename) : bool DBCFile::open() { - if (!SFileOpenFileEx(_mpq, _filename, SFILE_OPEN_PATCHED_FILE, &_file)) + if (!SFileOpenFileEx(_mpq, _filename, SFILE_OPEN_FROM_MPQ, &_file)) return false; char header[4]; diff --git a/src/tools/vmap4_extractor/mpqfile.cpp b/src/tools/vmap4_extractor/mpqfile.cpp index 73c4d191f0..fc974afd1f 100644 --- a/src/tools/vmap4_extractor/mpqfile.cpp +++ b/src/tools/vmap4_extractor/mpqfile.cpp @@ -9,10 +9,10 @@ MPQFile::MPQFile(HANDLE mpq, char const* filename, bool warnNoExist /*= true*/) size(0) { HANDLE file; - if (!SFileOpenFileEx(mpq, filename, SFILE_OPEN_PATCHED_FILE, &file)) + if (!SFileOpenFileEx(mpq, filename, SFILE_OPEN_FROM_MPQ, &file)) { - if (warnNoExist || GetLastError() != ERROR_FILE_NOT_FOUND) - fprintf(stderr, "Can't open %s, err=%u!\n", filename, GetLastError()); + if (warnNoExist || SErrGetLastError() != ERROR_FILE_NOT_FOUND) + fprintf(stderr, "Can't open %s, err=%u!\n", filename, SErrGetLastError()); eof = true; return; } @@ -38,7 +38,7 @@ MPQFile::MPQFile(HANDLE mpq, char const* filename, bool warnNoExist /*= true*/) DWORD read = 0; buffer = new char[size]; - if (!SFileReadFile(file, buffer, size, &read) || size != read) + if (!SFileReadFile(file, buffer, size, &read, nullptr) || size != read) { fprintf(stderr, "Can't read %s, size=%u read=%u!\n", filename, uint32(size), uint32(read)); SFileCloseFile(file); diff --git a/src/tools/vmap4_extractor/vmapexport.cpp b/src/tools/vmap4_extractor/vmapexport.cpp index 27e6f8b80d..f95e2af252 100644 --- a/src/tools/vmap4_extractor/vmapexport.cpp +++ b/src/tools/vmap4_extractor/vmapexport.cpp @@ -46,7 +46,7 @@ #include #include #include -#include +#include //------------------------------------------------------------------------------ // Defines @@ -126,7 +126,7 @@ bool LoadLocaleMPQFile(int locale) if (!SFileOpenArchive(fileName.c_str(), 0, MPQ_OPEN_READ_ONLY, &LocaleMpq)) { - if (GetLastError() != ERROR_PATH_NOT_FOUND) + if (SErrGetLastError() != ERROR_PATH_NOT_FOUND) { printf("\nLoading %s locale MPQs\n", Locales[locale]); printf("Cannot open archive %s\n", fileName.c_str()); @@ -158,7 +158,7 @@ bool LoadLocaleMPQFile(int locale) if (!SFileOpenPatchArchive(LocaleMpq, fileName.c_str(), prefix, 0)) { - if (GetLastError() != ERROR_FILE_NOT_FOUND) + if (SErrGetLastError() != ERROR_FILE_NOT_FOUND) printf("Cannot open patch archive %s\n", fileName.c_str()); continue; } @@ -176,7 +176,7 @@ void LoadCommonMPQFiles(uint32 build) printf("Loading common MPQ files\n"); if (!SFileOpenArchive(filename.c_str(), 0, MPQ_OPEN_READ_ONLY, &WorldMpq)) { - if (GetLastError() != ERROR_PATH_NOT_FOUND) + if (SErrGetLastError() != ERROR_PATH_NOT_FOUND) printf("Cannot open archive %s\n", filename.c_str()); return; } @@ -190,7 +190,7 @@ void LoadCommonMPQFiles(uint32 build) filename = Trinity::StringFormat("%s/Data/%s", input_path.string(), CONF_mpq_list[i]); if (!SFileOpenPatchArchive(WorldMpq, filename.c_str(), "", 0)) { - if (GetLastError() != ERROR_PATH_NOT_FOUND) + if (SErrGetLastError() != ERROR_PATH_NOT_FOUND) printf("Cannot open archive %s\n", filename.c_str()); else printf("Not found %s\n", filename.c_str()); @@ -222,7 +222,7 @@ void LoadCommonMPQFiles(uint32 build) if (!SFileOpenPatchArchive(WorldMpq, filename.c_str(), prefix, 0)) { - if (GetLastError() != ERROR_PATH_NOT_FOUND) + if (SErrGetLastError() != ERROR_PATH_NOT_FOUND) printf("Cannot open patch archive %s\n", filename.c_str()); else printf("Not found %s\n", filename.c_str()); @@ -521,7 +521,7 @@ int main(int argc, char ** argv) //Open MPQs if (!LoadLocaleMPQFile(i)) { - if (GetLastError() != ERROR_PATH_NOT_FOUND) + if (SErrGetLastError() != ERROR_PATH_NOT_FOUND) printf("Unable to load %s locale archives!\n", Locales[i]); continue; }