Ovahlord committed 2026-06-08 10:49:21 +02:00
1 parent f90d1d0161
commit 8075d11c5d
157 files changed
+39514 -25460

No files matched your search

+2 -2
View File
@@ -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)
+2 -2
View File
@@ -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
+19
View File
@@ -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
)
-22
View File
@@ -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);
+25 -5
View File
@@ -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
-25
View File
@@ -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
@@ -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": "(?P<prefix>d3-update-(?P<dataset>\\w+))-(?P<build>\\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"
]
}
}
}
}
-14
View File
@@ -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 <====
@@ -0,0 +1 @@
S48B6CDTN5XEQAKQDJNDLJBJ73FDFM3U
@@ -0,0 +1 @@
Y45MD3CAK4KXSSXHYD9VY64Z8EKJ4XFX
@@ -0,0 +1 @@
G8MN8UDG6NA2ANGY6A3DNY82HRGF29ZH
@@ -0,0 +1 @@
3DH5RE5NVM5GTFD85LXGWT6FK859ETR5
@@ -0,0 +1 @@
8WLKUAXE94PFQU4Y249PAZ24N4R4XKTQ
@@ -0,0 +1 @@
A34DXX3VHGGXSQBRFE5UFFDXMF9G4G54
@@ -0,0 +1 @@
ZG7J9K938HJEFWPQUA768MA2PFER6EAJ
@@ -0,0 +1 @@
NE7CUNNNTVAPXV7E3G2BSVBWGVMW8BL2
@@ -0,0 +1 @@
3V9E2FTMBM9QQWK7U6MAMWAZWQDB838F
@@ -0,0 +1 @@
2NSFB8MELULJ83U6YHA3UP6K4MQD48L6
@@ -0,0 +1 @@
QA2TZ9EWZ4CUU8BMB5WXCTY65F9CSW4E
@@ -0,0 +1 @@
VHB378W64BAT9SH7D68VV9NLQDK9YEGT
@@ -0,0 +1 @@
U3NFQJV4M6GC7KBN9XQJ3BRDN3PLD9NE
@@ -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;
}
@@ -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
+110
View File
@@ -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
File diff suppressed because it is too large. Load diff
+99 -71
View File
@@ -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
};
+85
View File
@@ -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"
+125
View File
@@ -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"
+4
View File
@@ -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"
File diff suppressed because it is too large. Load diff
+78 -39
View File
@@ -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__
File diff suppressed because it is too large. Load diff
+688
View File
@@ -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;
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+412 -316
View File
@@ -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);
File diff suppressed because it is too large. Load diff
+106 -75
View File
@@ -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);
}
+22 -25
View File
@@ -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);
}
+240 -204
View File
@@ -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;
}
+627
View File
@@ -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 '<htm'
{0x4D54483C, 0xFFFFFFFF, 0x00000000, 0x00000000, "html"}, // HTML '<HTM
{0x216F6F57, 0xFFFFFFFF, 0x00000000, 0x00000000, "tbl"}, // Table files
{0x31504C42, 0xFFFFFFFF, 0x00000000, 0x00000000, "blp"}, // BLP textures
{0x32504C42, 0xFFFFFFFF, 0x00000000, 0x00000000, "blp"}, // BLP textures (v2)
{0x584C444D, 0xFFFFFFFF, 0x00000000, 0x00000000, "mdx"}, // MDX files
{0x45505954, 0xFFFFFFFF, 0x00000000, 0x00000000, "pud"}, // Warcraft II maps
{0x38464947, 0xFFFFFFFF, 0x00000000, 0x00000000, "gif"}, // GIF images 'GIF8'
{0x3032444D, 0xFFFFFFFF, 0x00000000, 0x00000000, "m2"}, // WoW ??? .m2
{0x43424457, 0xFFFFFFFF, 0x00000000, 0x00000000, "dbc"}, // ??? .dbc
{0x47585053, 0xFFFFFFFF, 0x00000000, 0x00000000, "bls"}, // WoW pixel shaders
{0xE0FFD8FF, 0xFFFFFFFF, 0x00000000, 0x00000000, "jpg"}, // JPEG image
{0x503B4449, 0xFFFFFFFF, 0x3B4C5857, 0xFFFFFFFF, "slk"}, // SLK file (usually starts with "ID;PWXL;N;E")
{0x61754C1B, 0xFFFFFFFF, 0x00000000, 0x00000000, "lua"}, // Compiled LUA files
{0x20534444, 0xFFFFFFFF, 0x00000000, 0x00000000, "dds"}, // DDS textures
{0x43614C66, 0xFFFFFFFF, 0x00000000, 0x00000000, "flac"}, // FLAC sound files
{0x0000FBFF, 0x0000FFFF, 0x00000000, 0x00000000, "mp3"}, // MP3 sound files
{0x0000F3FF, 0x0000FFFF, 0x00000000, 0x00000000, "mp3"}, // MP3 sound files
{0x0000F2FF, 0x0000FFFF, 0x00000000, 0x00000000, "mp3"}, // MP3 sound files
{0x00334449, 0x00FFFFFF, 0x00000000, 0x00000000, "mp3"}, // MP3 sound files
{0x57334D48, 0xFFFFFFFF, 0x00000000, 0x00000000, "w3x"}, // Warcraft III map files, can also be w3m
{0x6F643357, 0xFFFFFFFF, 0x00000000, 0x00000000, "doo"}, // Warcraft III doodad files
{0x21453357, 0xFFFFFFFF, 0x00000000, 0x00000000, "w3e"}, // Warcraft III environment files
{0x5733504D, 0xFFFFFFFF, 0x00000000, 0x00000000, "wpm"}, // Warcraft III pathing map files
{0x21475457, 0xFFFFFFFF, 0x00000000, 0x00000000, "wtg"}, // Warcraft III trigger files
{0x00000000, 0x00000000, 0x00000000, 0x00000000, "xxx"}, // Default extension
{0, 0, 0, 0, NULL} // Terminator
};
static DWORD CreatePseudoFileName(HANDLE hFile, TFileEntry * pFileEntry, char * szFileName)
{
TMPQFile * hf = (TMPQFile *)hFile; // MPQ File handle
DWORD FirstBytes[2] = {0, 0}; // The first 4 bytes of the file
DWORD dwBytesRead = 0;
DWORD dwFilePos; // Saved file position
char szPseudoName[20];
// Read the first 2 DWORDs bytes from the file
dwFilePos = SFileSetFilePointer(hFile, 0, NULL, FILE_CURRENT);
SFileReadFile(hFile, FirstBytes, sizeof(FirstBytes), &dwBytesRead, NULL);
SFileSetFilePointer(hFile, dwFilePos, NULL, FILE_BEGIN);
// If we read at least 8 bytes
if(dwBytesRead == sizeof(FirstBytes))
{
// Make sure that the array is properly BSWAP-ed
BSWAP_ARRAY32_UNSIGNED(FirstBytes, sizeof(FirstBytes));
// Try to guess file extension from those 2 DWORDs
for(size_t i = 0; data2ext[i].szExt != NULL; i++)
{
if((FirstBytes[0] & data2ext[i].dwOffset00Mask) == data2ext[i].dwOffset00Data &&
(FirstBytes[1] & data2ext[i].dwOffset04Mask) == data2ext[i].dwOffset04Data)
{
// Format the pseudo-name
StringCreatePseudoFileName(szPseudoName, _countof(szPseudoName), (unsigned int)(pFileEntry - hf->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);
}
File diff suppressed because it is too large. Load diff
+553 -237
View File
@@ -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;
}
+314 -352
View File
@@ -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;
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+265 -127
View File
@@ -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;
}
+582
View File
@@ -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
}
+347 -88
View File
@@ -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 <typename XCHAR, typename XINT>
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 <typename XCHAR>
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 <typename XCHAR>
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 <typename XCHAR>
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__
@@ -9,11 +9,11 @@ _SFileGetLocale
_SFileOpenArchive
_SFileCreateArchive
_SFileGetArchiveBitmap
_SFileFlushArchive
_SFileCloseArchive
_SFileAddListFile
_SFileAddListFileEntries
_SFileSetCompactCallback
_SFileCompactArchive
File diff suppressed because it is too large. Load diff
+301 -56
View File
@@ -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 <stdbool.h>
#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 <tchar.h>
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
// Suppress definitions of `min` and `max` macros by <windows.h>:
#ifndef NOMINMAX
#define NOMINMAX 1
#endif
#include <windows.h>
#include <wininet.h>
#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 <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <errno.h>
#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 <stdint.h>
#include <CoreFoundation/CFByteOrder.h>
#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 <sys/types.h>
#include <sys/stat.h>
@@ -102,20 +129,198 @@
#include <assert.h>
#include <errno.h>
#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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <errno.h>
#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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <assert.h>
#include <errno.h>
#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 <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <assert.h>
#include <errno.h>
#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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <assert.h>
#include <errno.h>
#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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <assert.h>
#include <errno.h>
#include <malloc.h>
#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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <assert.h>
#include <errno.h>
#ifndef __BIG_ENDIAN__
#define STORMLIB_LITTLE_ENDIAN
#endif
// Platforms with mmap support
#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
#include <sys/mman.h>
#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__
+472 -287
View File
@@ -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 <assert.h>
#include <stddef.h>
#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);
}
+9 -4
View File
@@ -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__
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+282
View File
@@ -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 <jseward@bzip.org>
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 <stdio.h>
#endif
#ifdef _WIN32
# include <windows.h>
# 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 ---*/
/*-------------------------------------------------------------*/
+509
View File
@@ -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 <jseward@bzip.org>
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 <stdlib.h>
#ifndef BZ_NO_STDIO
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#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 ---*/
/*-------------------------------------------------------------*/
+672
View File
@@ -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 <jseward@bzip.org>
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 ---*/
/*-------------------------------------------------------------*/
+104
View File
@@ -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 <jseward@bzip.org>
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 ---*/
/*-------------------------------------------------------------*/
+626
View File
@@ -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 <jseward@bzip.org>
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 ---*/
/*-------------------------------------------------------------*/
+205
View File
@@ -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 <jseward@bzip.org>
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 ---*/
/*-------------------------------------------------------------*/
+84
View File
@@ -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 <jseward@bzip.org>
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 ---*/
/*-------------------------------------------------------------*/
File diff suppressed because it is too large. Load diff
+113 -100
View File
@@ -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__
File diff suppressed because it is too large. Load diff
-145
View File
@@ -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 <stdint.h>
#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__
@@ -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 $ */
@@ -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 */
@@ -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 */
@@ -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
@@ -12,32 +12,32 @@
#include <stdlib.h>
#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);
}
@@ -10,4 +10,4 @@
*/
#include "../headers/tomcrypt.h"
ltc_math_descriptor ltc_mp;
ltc_math_descriptor ltc_mp = {0};
@@ -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 $ */
@@ -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 $ */
@@ -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 $ */
@@ -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 $ */
@@ -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 $ */
@@ -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 $ */
@@ -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 $ */
@@ -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 <stdarg.h>
/**
@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
@@ -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 <stdarg.h>
/**
@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 <type, size, data> (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 $ */
@@ -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 $ */
@@ -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 $ */
@@ -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 $ */
@@ -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 $ */
@@ -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 $ */
@@ -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 $ */
Loaded 100 of 157 files, more files were not shown because too many files have changed in this diff. Show more