Update StormLib

This commit is contained in:
LordJZ authored and Shauren committed 2012-07-03 17:16:30 +02:00
1 parent 6fe1657fe9
commit 459257b056
44 files changed
+3539 -2276

No files matched your search

+28 -9
View File
@@ -1,3 +1,6 @@
project(StormLib)
cmake_minimum_required(VERSION 2.6)
set(SRC_FILES
src/adpcm/adpcm.cpp
src/huffman/huff.cpp
@@ -236,21 +239,27 @@ set(TEST_SRC_FILES
add_definitions(-D_7ZIP_ST -DBZ_STRICT_ANSI)
if( WIN32 )
if( MSVC )
if(WIN32)
if(MSVC)
message(STATUS "Using MSVC")
add_definitions(-D_7ZIP_ST -DWIN32)
else()
message(STATUS "Using mingw")
endif()
set(SRC_ADDITIONAL_FILES ${ZLIB_BZIP2_FILES} ${TOMCRYPT_FILES} ${TOMMATH_FILES})
set(LINK_LIBS wininet)
endif()
if( APPLE )
if(APPLE)
message(STATUS "Using Mac OS X port")
set(LINK_LIBS z bz2)
set(SRC_ADDITIONAL_FILES ${TOMCRYPT_FILES} ${TOMMATH_FILES})
endif()
if( ${CMAKE_SYSTEM_NAME} STREQUAL Linux )
if (${CMAKE_SYSTEM_NAME} STREQUAL Linux)
message(STATUS "Using Linux port")
option(WITH_LIBTOMCRYPT "Use system LibTomCrypt library" OFF)
if( WITH_LIBTOMCRYPT )
if(WITH_LIBTOMCRYPT)
set(LINK_LIBS z bz2 tomcrypt)
else()
set(LINK_LIBS z bz2)
@@ -258,20 +267,30 @@ if( ${CMAKE_SYSTEM_NAME} STREQUAL Linux )
endif()
endif()
add_library(storm STATIC ${SRC_FILES} ${SRC_ADDITIONAL_FILES})
add_library(storm SHARED ${SRC_FILES} ${SRC_ADDITIONAL_FILES})
target_link_libraries(storm ${LINK_LIBS})
if( APPLE )
option(WITH_TEST "Compile Test application" OFF)
if(WITH_TEST)
add_executable(storm_test ${TEST_SRC_FILES})
target_link_libraries(storm_test storm)
endif()
if(APPLE)
set_target_properties(storm PROPERTIES FRAMEWORK true)
set_target_properties(storm PROPERTIES PUBLIC_HEADER "src/StormLib.h src/StormPort.h")
set_target_properties(storm PROPERTIES LINK_FLAGS "-framework Carbon")
endif()
if( UNIX )
if(UNIX)
set_target_properties(storm PROPERTIES SOVERSION 0)
endif()
# On Win32, build StormLib.dll since we don't want to clash with Storm.dll
if( WIN32 )
if(WIN32)
set_target_properties(storm PROPERTIES OUTPUT_NAME StormLib)
endif()
install(TARGETS storm RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib FRAMEWORK DESTINATION /Library/Frameworks)
install(FILES src/StormLib.h src/StormPort.h DESTINATION include)
@@ -0,0 +1 @@
UCMXF6EJY352EFH4XFRXCFH2XC9MQRZK
@@ -0,0 +1 @@
MMKVHY48RP7WXP4GHYBQ7SL9J9UNPHBP
@@ -0,0 +1 @@
8MXLWHQ7VGGLTZ9MQZQSFDCLJYET3CPP
@@ -0,0 +1 @@
EJ2R5TM6XFE2GUNG5QDGHKQ9UAKPWZSZ
@@ -0,0 +1 @@
PBGFBE42Z6LNK65UGJQ3WZVMCLP4HQQT
@@ -0,0 +1 @@
X7SEJJS9TSGCW5P28EBSC47AJPEY8VU2
@@ -0,0 +1 @@
5KVBQA8VYE6XRY3DLGC5ZDE4XS4P7YA2
@@ -0,0 +1 @@
478JD2K56EVNVVY4XX8TDWYT5B8KB254
@@ -0,0 +1 @@
8TS4VNFQRZTN6YWHE9CHVDH9NVWD474A
@@ -0,0 +1 @@
LJ52Z32DF4LZ4ZJJXVKK3AZQA6GABLJB
@@ -0,0 +1 @@
K6BDHY2ECUE2545YKNLBJPVYWHE7XYAG
@@ -0,0 +1 @@
6VWCQTN8V3ZZMRUCZXV8A8CGUX2TAA8H
@@ -0,0 +1,49 @@
{
"config":{
"product": "D3",
"install_progress_percent": 70.0,
"install_progress_info": [8000000000.0, 0.0, 0.0],
"download_progress_info": [10000000.0, 500000000.0, 500000000.0],
"install_progress_speed": 5000000.0,
"tome_download_progress_percent": 0.0,
"updater_product": "d3_patch",
"expansion_level": 0,
"ptr": false,
"beta": false,
"update_method": "patch on demand",
"supports_multibox": false,
"data_dir": "Data_D3/PC/MPQs/",
"patch_url": "http://ruRU.patch.battle.net:1119/patch",
"update_regex": "(?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
@@ -0,0 +1,14 @@
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 <====
File diff suppressed because it is too large. Load diff
+189
View File
@@ -0,0 +1,189 @@
/*****************************************************************************/
/* FileStream.h Copyright (c) Ladislav Zezula 2012 */
/*---------------------------------------------------------------------------*/
/* Description: Definitions for FileStream object */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 14.04.12 1.00 Lad The first version of FileStream.h */
/*****************************************************************************/
#ifndef __FILESTREAM_H__
#define __FILESTREAM_H__
//-----------------------------------------------------------------------------
// Function prototypes
typedef bool (*STREAM_READ)(
struct TFileStream * pStream, // Pointer to an open stream
ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position
void * pvBuffer, // Pointer to data to be read
DWORD dwBytesToRead // Number of bytes to read from the file
);
typedef bool (*STREAM_WRITE)(
struct TFileStream * pStream, // Pointer to an open stream
ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it writes to the current position
const void * pvBuffer, // Pointer to data to be written
DWORD dwBytesToWrite // Number of bytes to read from the file
);
typedef bool (*STREAM_GETPOS)(
struct TFileStream * pStream, // Pointer to an open stream
ULONGLONG & 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)(
struct TFileStream * pStream, // Pointer to an open stream
ULONGLONG FileSize // New size for the file, in bytes
);
typedef bool (*STREAM_GETTIME)(
struct TFileStream * pStream,
ULONGLONG * pFT
);
typedef bool (*STREAM_SWITCH)(
struct TFileStream * pStream,
struct TFileStream * pNewStream
);
typedef bool (*STREAM_GETBMP)(
TFileStream * pStream,
TFileBitmap * pBitmap,
DWORD Length,
LPDWORD LengthNeeded
);
typedef void (*STREAM_CLOSE)(
struct TFileStream * pStream
);
//-----------------------------------------------------------------------------
// Local structures - part file structure
typedef struct _PART_FILE_HEADER
{
DWORD PartialVersion; // Always set to 2
char GameBuildNumber[0x20]; // Minimum build number of the game that can use this MPQ
DWORD Flags; // Flags (details unknown)
DWORD FileSizeLo; // Low 32 bits of the contained file size
DWORD FileSizeHi; // High 32 bits of the contained file size
DWORD BlockSize; // Size of one file block, in bytes
} PART_FILE_HEADER, *PPART_FILE_HEADER;
// Structure describing the block-to-file map entry
typedef struct _PART_FILE_MAP_ENTRY
{
DWORD Flags; // 3 = the block is present in the file
DWORD BlockOffsLo; // Low 32 bits of the block position in the file
DWORD BlockOffsHi; // High 32 bits of the block position in the file
DWORD LargeValueLo; // 64-bit value, meaning is unknown
DWORD LargeValueHi;
} PART_FILE_MAP_ENTRY, *PPART_FILE_MAP_ENTRY;
//-----------------------------------------------------------------------------
// Local structures
union TBaseData
{
struct
{
ULONGLONG FileSize; // Size of the file
ULONGLONG FilePos; // Current file position
ULONGLONG FileTime; // Date/time of last modification of the file
HANDLE hFile; // File handle
} File;
struct
{
ULONGLONG FileSize; // Mapped file size
ULONGLONG FilePos; // Current stream position
ULONGLONG FileTime; // Date/time of last modification of the file
LPBYTE pbFile; // Pointer to mapped view
} Map;
struct
{
ULONGLONG FileSize; // Size of the internet file
ULONGLONG FilePos; // Current position in the file
ULONGLONG FileTime; // Date/time of last modification of the file
HANDLE hInternet; // Internet handle
HANDLE hConnect; // Connection to the internet server
} Http;
};
//-----------------------------------------------------------------------------
// Structure for linear stream
struct TFileStream
{
// Stream provider functions
STREAM_READ StreamRead; // Pointer to stream read function for this archive. Do not use directly.
STREAM_WRITE StreamWrite; // Pointer to stream write function for this archive. Do not use directly.
STREAM_GETPOS StreamGetPos; // Pointer to function that returns current file position
STREAM_GETSIZE StreamGetSize; // Pointer to function returning file size
STREAM_SETSIZE StreamSetSize; // Pointer to function changing file size
STREAM_GETTIME StreamGetTime; // Pointer to function retrieving the file time
STREAM_GETBMP StreamGetBmp; // Pointer to function that retrieves the file bitmap
STREAM_SWITCH StreamSwitch; // Pointer to function changing the stream to another file
STREAM_CLOSE StreamClose; // Pointer to function closing the stream
// Stream provider data members
TCHAR szFileName[MAX_PATH]; // File name
DWORD dwFlags; // Stream flags
// Base provider functions
STREAM_READ BaseRead;
STREAM_WRITE BaseWrite;
STREAM_GETPOS BaseGetPos; // Pointer to function that returns current file position
STREAM_GETSIZE BaseGetSize; // Pointer to function returning file size
STREAM_SETSIZE BaseSetSize; // Pointer to function changing file size
STREAM_GETTIME BaseGetTime; // Pointer to function retrieving the file time
STREAM_CLOSE BaseClose; // Pointer to function closing the stream
// Base provider data members
TBaseData Base; // Base provider data
// Followed by stream provider data, with variable length
};
//-----------------------------------------------------------------------------
// Structure for linear stream
struct TLinearStream : public TFileStream
{
TFileBitmap * pBitmap; // Pointer to the stream bitmap
};
//-----------------------------------------------------------------------------
// Structure for partial stream
struct TPartialStream : public TFileStream
{
ULONGLONG VirtualSize; // Virtual size of the file
ULONGLONG VirtualPos; // Virtual position in the file
DWORD BlockCount; // Number of file blocks. Used by partial file stream
DWORD BlockSize; // Size of one block. Used by partial file stream
PPART_FILE_MAP_ENTRY PartMap; // File map, variable length
};
//-----------------------------------------------------------------------------
// Structure for encrypted stream
#define MPQE_CHUNK_SIZE 0x40 // Size of one chunk to be decrypted
struct TEncryptedStream : public TFileStream
{
BYTE Key[MPQE_CHUNK_SIZE]; // File key
};
#endif // __FILESTREAM_H__
+48 -16
View File
@@ -15,12 +15,11 @@
#include "StormLib.h"
#include "StormCommon.h"
char StormLibCopyright[] = "StormLib v " STORMLIB_VERSION_STRING " Copyright Ladislav Zezula 1998-2011";
char StormLibCopyright[] = "StormLib v " STORMLIB_VERSION_STRING " Copyright Ladislav Zezula 1998-2012";
//-----------------------------------------------------------------------------
// The buffer for decryption engine.
DWORD dwGlobalFlags = 0; // Global flags
LCID lcFileLocale = LANG_NEUTRAL; // File locale
USHORT wPlatform = 0; // File platform
@@ -318,6 +317,20 @@ int ConvertMpqHeaderToFormat4(
return nError;
}
//-----------------------------------------------------------------------------
// Default flags for (attributes) and (listfile)
DWORD GetDefaultSpecialFileFlags(TMPQArchive * ha, DWORD dwFileSize)
{
// Fixed for format 1.0
if(ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1)
return MPQ_FILE_COMPRESS | MPQ_FILE_ENCRYPTED | MPQ_FILE_FIX_KEY;
// Size-dependent for formats 2.0-4.0
return (dwFileSize > 0x4000) ? (MPQ_FILE_COMPRESS | MPQ_FILE_SECTOR_CRC) : (MPQ_FILE_COMPRESS | MPQ_FILE_SINGLE_UNIT);
}
//-----------------------------------------------------------------------------
// Encrypting and decrypting MPQ file data
@@ -692,11 +705,9 @@ void FindFreeMpqSpace(TMPQArchive * ha, ULONGLONG * pFreeSpacePos)
FreeSpacePos = pFileEntry->ByteOffset + pFileEntry->dwCmpSize;
// Add the MD5 chunks, if present
if(pHeader->dwRawChunkSize != 0)
if(pHeader->dwRawChunkSize != 0 && pFileEntry->dwCmpSize != 0)
{
dwChunkCount = pFileEntry->dwCmpSize / pHeader->dwRawChunkSize;
if(pFileEntry->dwCmpSize % pHeader->dwRawChunkSize)
dwChunkCount++;
dwChunkCount = ((pFileEntry->dwCmpSize - 1) / pHeader->dwRawChunkSize) + 1;
FreeSpacePos += dwChunkCount * MD5_DIGEST_SIZE;
}
}
@@ -773,7 +784,7 @@ int LoadMpqTable(
int cbOutBuffer = (int)dwRealSize;
int cbInBuffer = (int)dwCompressedSize;
if(!SCompDecompress((char *)pvTable, &cbOutBuffer, (char *)pbCompressed, cbInBuffer))
if(!SCompDecompress2((char *)pvTable, &cbOutBuffer, (char *)pbCompressed, cbInBuffer))
nError = GetLastError();
// Free the temporary buffer
@@ -828,13 +839,11 @@ unsigned char * AllocateMd5Buffer(
DWORD cbMd5Size;
// Sanity check
assert(dwRawDataSize != 0);
assert(dwChunkSize != 0);
// Calculate how many MD5's we will calculate
cbMd5Size = dwRawDataSize / dwChunkSize;
if(dwRawDataSize % dwChunkSize)
cbMd5Size++;
cbMd5Size *= MD5_DIGEST_SIZE;
cbMd5Size = (((dwRawDataSize - 1) / dwChunkSize) + 1) * MD5_DIGEST_SIZE;
// Allocate space for array or MD5s
md5_array = STORM_ALLOC(BYTE, cbMd5Size);
@@ -853,9 +862,12 @@ int AllocateSectorBuffer(TMPQFile * hf)
// Caller of AllocateSectorBuffer must ensure these
assert(hf->pbFileSector == NULL);
assert(hf->pFileEntry != NULL);
assert(hf->dwDataSize != 0);
assert(hf->ha != NULL);
// Don't allocate anything if the file has zero size
if(hf->pFileEntry->dwFileSize == 0 || hf->dwDataSize == 0)
return ERROR_SUCCESS;
// Determine the file sector size and allocate buffer for it
hf->dwSectorSize = (hf->pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT) ? hf->dwDataSize : ha->dwSectorSize;
hf->pbFileSector = STORM_ALLOC(BYTE, hf->dwSectorSize);
@@ -889,7 +901,7 @@ __AllocateAndLoadPatchInfo:
// Load the patch header
if(!FileStream_Read(ha->pStream, &hf->RawFilePos, hf->pPatchInfo, dwLength))
{
// Free the sector offsets
// Free the patch info
STORM_FREE(hf->pPatchInfo);
hf->pPatchInfo = NULL;
return GetLastError();
@@ -904,10 +916,14 @@ __AllocateAndLoadPatchInfo:
// If it's not default size, we have to reload them
if(hf->pPatchInfo->dwLength > dwLength)
{
// Free the patch info
dwLength = hf->pPatchInfo->dwLength;
STORM_FREE(hf->pPatchInfo);
hf->pPatchInfo = NULL;
// If the length is out of all possible ranges, fail the operation
if(dwLength > 0x400)
return ERROR_FILE_CORRUPT;
goto __AllocateAndLoadPatchInfo;
}
@@ -947,6 +963,7 @@ int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile)
}
// Calculate the number of data sectors
// Note that this doesn't work if the file size is zero
hf->dwSectorCount = ((hf->dwDataSize - 1) / hf->dwSectorSize) + 1;
// Calculate the number of file sectors
@@ -961,6 +978,8 @@ int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile)
// Only allocate and load the table if the file is compressed
if(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED)
{
__LoadSectorOffsets:
// Allocate the sector offset table
hf->SectorOffsets = (DWORD *)STORM_ALLOC(BYTE, dwSectorOffsLen);
if(hf->SectorOffsets == NULL)
@@ -1044,10 +1063,18 @@ int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile)
// There may be various extra DWORDs loaded after the sector offset table.
// They are mostly empty on WoW release MPQs, but on MPQs from PTR,
// they contain random non-zero data. Their meaning is unknown.
// At this point, we completely ignore them
//
// These extra values are, however, include in the dwCmpSize in the file
// table. We cannot ignore them, because compacting archive would fail
//
// assert(dwSectorOffsLen == hf->SectorOffsets[0]);
if(hf->SectorOffsets[0] > dwSectorOffsLen)
{
dwSectorOffsLen = hf->SectorOffsets[0];
STORM_FREE(hf->SectorOffsets);
hf->SectorOffsets = NULL;
goto __LoadSectorOffsets;
}
}
else
{
@@ -1388,6 +1415,10 @@ void FreeMPQArchive(TMPQArchive *& ha)
if(ha->haPatch != NULL)
FreeMPQArchive(ha->haPatch);
// Close the file stream
FileStream_Close(ha->pStream);
ha->pStream = NULL;
// Free the file names from the file table
if(ha->pFileTable != NULL)
{
@@ -1402,11 +1433,12 @@ void FreeMPQArchive(TMPQArchive *& ha)
STORM_FREE(ha->pFileTable);
}
if(ha->pBitmap != NULL)
STORM_FREE(ha->pBitmap);
if(ha->pHashTable != NULL)
STORM_FREE(ha->pHashTable);
if(ha->pHetTable != NULL)
FreeHetTable(ha->pHetTable);
FileStream_Close(ha->pStream);
STORM_FREE(ha);
ha = NULL;
}
+35 -35
View File
@@ -17,18 +17,18 @@
void DumpMpqHeader(TMPQHeader * pHeader)
{
printf("== MPQ Header =================================\n");
printf("DWORD dwID = %08lX\n", pHeader->dwID);
printf("DWORD dwHeaderSize = %08lX\n", pHeader->dwHeaderSize);
printf("DWORD dwArchiveSize = %08lX\n", pHeader->dwArchiveSize);
printf("USHORT wFormatVersion = %04lX\n", pHeader->wFormatVersion);
printf("USHORT wSectorSize = %04lX\n", pHeader->wSectorSize);
printf("DWORD dwHashTablePos = %08lX\n", pHeader->dwHashTablePos);
printf("DWORD dwBlockTablePos = %08lX\n", pHeader->dwBlockTablePos);
printf("DWORD dwHashTableSize = %08lX\n", pHeader->dwHashTableSize);
printf("DWORD dwBlockTableSize = %08lX\n", pHeader->dwBlockTableSize);
printf("DWORD dwID = %08X\n", pHeader->dwID);
printf("DWORD dwHeaderSize = %08X\n", pHeader->dwHeaderSize);
printf("DWORD dwArchiveSize = %08X\n", pHeader->dwArchiveSize);
printf("USHORT wFormatVersion = %04X\n", pHeader->wFormatVersion);
printf("USHORT wSectorSize = %04X\n", pHeader->wSectorSize);
printf("DWORD dwHashTablePos = %08X\n", pHeader->dwHashTablePos);
printf("DWORD dwBlockTablePos = %08X\n", pHeader->dwBlockTablePos);
printf("DWORD dwHashTableSize = %08X\n", pHeader->dwHashTableSize);
printf("DWORD dwBlockTableSize = %08X\n", pHeader->dwBlockTableSize);
printf("ULONGLONG HiBlockTablePos64 = %016llX\n", pHeader->HiBlockTablePos64);
printf("USHORT wHashTablePosHi = %04lX\n", pHeader->wHashTablePosHi);
printf("USHORT wBlockTablePosHi = %04lX\n", pHeader->wBlockTablePosHi);
printf("USHORT wHashTablePosHi = %04X\n", pHeader->wHashTablePosHi);
printf("USHORT wBlockTablePosHi = %04X\n", pHeader->wBlockTablePosHi);
printf("ULONGLONG ArchiveSize64 = %016llX\n", pHeader->ArchiveSize64);
printf("ULONGLONG BetTablePos64 = %016llX\n", pHeader->BetTablePos64);
printf("ULONGLONG HetTablePos64 = %016llX\n", pHeader->HetTablePos64);
@@ -37,7 +37,7 @@ void DumpMpqHeader(TMPQHeader * pHeader)
printf("ULONGLONG HiBlockTableSize64 = %016llX\n", pHeader->HiBlockTableSize64);
printf("ULONGLONG HetTableSize64 = %016llX\n", pHeader->HetTableSize64);
printf("ULONGLONG BetTableSize64 = %016llX\n", pHeader->BetTableSize64);
printf("DWORD dwRawChunkSize = %08lX\n", pHeader->dwRawChunkSize);
printf("DWORD dwRawChunkSize = %08X\n", pHeader->dwRawChunkSize);
printf("-----------------------------------------------\n\n");
}
@@ -51,31 +51,31 @@ void DumpHetAndBetTable(TMPQHetTable * pHetTable, TMPQBetTable * pBetTable)
printf("== HET Header =================================\n");
printf("ULONGLONG AndMask64 = %016llX\n", pHetTable->AndMask64);
printf("ULONGLONG OrMask64 = %016llX\n", pHetTable->OrMask64);
printf("DWORD dwIndexSizeTotal = %08lX\n", pHetTable->dwIndexSizeTotal);
printf("DWORD dwIndexSizeExtra = %08lX\n", pHetTable->dwIndexSizeExtra);
printf("DWORD dwIndexSize = %08lX\n", pHetTable->dwIndexSize);
printf("DWORD dwMaxFileCount = %08lX\n", pHetTable->dwMaxFileCount);
printf("DWORD dwHashTableSize = %08lX\n", pHetTable->dwHashTableSize);
printf("DWORD dwHashBitSize = %08lX\n", pHetTable->dwHashBitSize);
printf("DWORD dwIndexSizeTotal = %08X\n", pHetTable->dwIndexSizeTotal);
printf("DWORD dwIndexSizeExtra = %08X\n", pHetTable->dwIndexSizeExtra);
printf("DWORD dwIndexSize = %08X\n", pHetTable->dwIndexSize);
printf("DWORD dwMaxFileCount = %08X\n", pHetTable->dwMaxFileCount);
printf("DWORD dwHashTableSize = %08X\n", pHetTable->dwHashTableSize);
printf("DWORD dwHashBitSize = %08X\n", pHetTable->dwHashBitSize);
printf("-----------------------------------------------\n\n");
printf("== BET Header =================================\n");
printf("DWORD dwTableEntrySize = %08lX\n", pBetTable->dwTableEntrySize);
printf("DWORD dwBitIndex_FilePos = %08lX\n", pBetTable->dwBitIndex_FilePos);
printf("DWORD dwBitIndex_FileSize = %08lX\n", pBetTable->dwBitIndex_FileSize);
printf("DWORD dwBitIndex_CmpSize = %08lX\n", pBetTable->dwBitIndex_CmpSize);
printf("DWORD dwBitIndex_FlagIndex = %08lX\n", pBetTable->dwBitIndex_FlagIndex);
printf("DWORD dwBitIndex_Unknown = %08lX\n", pBetTable->dwBitIndex_Unknown);
printf("DWORD dwBitCount_FilePos = %08lX\n", pBetTable->dwBitCount_FilePos);
printf("DWORD dwBitCount_FileSize = %08lX\n", pBetTable->dwBitCount_FileSize);
printf("DWORD dwBitCount_CmpSize = %08lX\n", pBetTable->dwBitCount_CmpSize);
printf("DWORD dwBitCount_FlagIndex = %08lX\n", pBetTable->dwBitCount_FlagIndex);
printf("DWORD dwBitCount_Unknown = %08lX\n", pBetTable->dwBitCount_Unknown);
printf("DWORD dwBetHashSizeTotal = %08lX\n", pBetTable->dwBetHashSizeTotal);
printf("DWORD dwBetHashSizeExtra = %08lX\n", pBetTable->dwBetHashSizeExtra);
printf("DWORD dwBetHashSize = %08lX\n", pBetTable->dwBetHashSize);
printf("DWORD dwMaxFileCount = %08lX\n", pBetTable->dwMaxFileCount);
printf("DWORD dwFlagCount = %08lX\n", pBetTable->dwFlagCount);
printf("DWORD dwTableEntrySize = %08X\n", pBetTable->dwTableEntrySize);
printf("DWORD dwBitIndex_FilePos = %08X\n", pBetTable->dwBitIndex_FilePos);
printf("DWORD dwBitIndex_FileSize = %08X\n", pBetTable->dwBitIndex_FileSize);
printf("DWORD dwBitIndex_CmpSize = %08X\n", pBetTable->dwBitIndex_CmpSize);
printf("DWORD dwBitIndex_FlagIndex = %08X\n", pBetTable->dwBitIndex_FlagIndex);
printf("DWORD dwBitIndex_Unknown = %08X\n", pBetTable->dwBitIndex_Unknown);
printf("DWORD dwBitCount_FilePos = %08X\n", pBetTable->dwBitCount_FilePos);
printf("DWORD dwBitCount_FileSize = %08X\n", pBetTable->dwBitCount_FileSize);
printf("DWORD dwBitCount_CmpSize = %08X\n", pBetTable->dwBitCount_CmpSize);
printf("DWORD dwBitCount_FlagIndex = %08X\n", pBetTable->dwBitCount_FlagIndex);
printf("DWORD dwBitCount_Unknown = %08X\n", pBetTable->dwBitCount_Unknown);
printf("DWORD dwBetHashSizeTotal = %08X\n", pBetTable->dwBetHashSizeTotal);
printf("DWORD dwBetHashSizeExtra = %08X\n", pBetTable->dwBetHashSizeExtra);
printf("DWORD dwBetHashSize = %08X\n", pBetTable->dwBetHashSize);
printf("DWORD dwMaxFileCount = %08X\n", pBetTable->dwMaxFileCount);
printf("DWORD dwFlagCount = %08X\n", pBetTable->dwFlagCount);
printf("-----------------------------------------------\n\n");
printf("== HET & Bet Table ======================================================================\n\n");
@@ -128,7 +128,7 @@ void DumpHetAndBetTable(TMPQHetTable * pHetTable, TMPQBetTable * pBetTable)
dwFlags = pBetTable->pFileFlags[dwFlagIndex];
}
printf(" %04lX %02lX %04lX %016llX %016llX %08lX %08lX %04lX %08lX\n", i,
printf(" %04X %02lX %04X %016llX %016llX %08X %08X %04X %08X\n", i,
pHetTable->pHetHashes[i],
dwBetIndex,
BetHash,
+369 -296
View File
@@ -15,7 +15,8 @@
//-----------------------------------------------------------------------------
// Local defines
#define MAX_FLAG_INDEX 256
#define INVALID_FLAG_VALUE 0xCCCCCCCC
#define MAX_FLAG_INDEX 512
//-----------------------------------------------------------------------------
// Local structures
@@ -62,6 +63,29 @@ typedef struct _BET_TABLE_HEADER
//-----------------------------------------------------------------------------
// Support for calculating bit sizes
static void InitFileFlagArray(LPDWORD FlagArray)
{
for(DWORD dwFlagIndex = 0; dwFlagIndex < MAX_FLAG_INDEX; dwFlagIndex++)
FlagArray[dwFlagIndex] = INVALID_FLAG_VALUE;
}
static DWORD GetFileFlagIndex(LPDWORD FlagArray, DWORD dwFlags)
{
// Find free or equal entry in the flag array
for(DWORD dwFlagIndex = 0; dwFlagIndex < MAX_FLAG_INDEX; dwFlagIndex++)
{
if(FlagArray[dwFlagIndex] == INVALID_FLAG_VALUE || FlagArray[dwFlagIndex] == dwFlags)
{
FlagArray[dwFlagIndex] = dwFlags;
return dwFlagIndex;
}
}
// This should never happen
assert(false);
return 0xFFFFFFFF;
}
static DWORD GetNecessaryBitCount(ULONGLONG MaxValue)
{
DWORD dwBitCount = 0;
@@ -75,69 +99,6 @@ static DWORD GetNecessaryBitCount(ULONGLONG MaxValue)
return dwBitCount;
}
static BYTE GetFlagsComboIndex(DWORD dwFlags)
{
BYTE FlagComboIndex = 0;
//
// We only use 8 different bits. This allows us to
// construct 256 unique values for every possible combination of MPQ file flags.
//
if(dwFlags & MPQ_FILE_IMPLODE)
FlagComboIndex |= 0x01;
dwFlags &= ~MPQ_FILE_IMPLODE;
if(dwFlags & MPQ_FILE_COMPRESS)
FlagComboIndex |= 0x02;
dwFlags &= ~MPQ_FILE_COMPRESS;
if(dwFlags & MPQ_FILE_ENCRYPTED)
FlagComboIndex |= 0x04;
dwFlags &= ~MPQ_FILE_ENCRYPTED;
if(dwFlags & MPQ_FILE_FIX_KEY)
FlagComboIndex |= 0x08;
dwFlags &= ~MPQ_FILE_FIX_KEY;
if(dwFlags & MPQ_FILE_PATCH_FILE)
FlagComboIndex |= 0x10;
dwFlags &= ~MPQ_FILE_PATCH_FILE;
if(dwFlags & MPQ_FILE_SINGLE_UNIT)
FlagComboIndex |= 0x20;
dwFlags &= ~MPQ_FILE_SINGLE_UNIT;
if(dwFlags & MPQ_FILE_DELETE_MARKER)
FlagComboIndex |= 0x40;
dwFlags &= ~MPQ_FILE_DELETE_MARKER;
if(dwFlags & MPQ_FILE_SECTOR_CRC)
FlagComboIndex |= 0x80;
dwFlags &= ~MPQ_FILE_SECTOR_CRC;
// Sanity check - the flags must now be zero
assert((dwFlags & 0x7FFFFFFF) == 0);
return FlagComboIndex;
}
static DWORD GetFileFlagIndex(LPDWORD FlagArray, DWORD dwFlags)
{
// Find free or equal entry in the flag array
for(DWORD dwFlagIndex = 0; dwFlagIndex < MAX_FLAG_INDEX; dwFlagIndex++)
{
if(FlagArray[dwFlagIndex] == 0 || FlagArray[dwFlagIndex] == dwFlags)
{
FlagArray[dwFlagIndex] = dwFlags;
return dwFlagIndex;
}
}
// This should never happen
assert(false);
return 0xFFFFFFFF;
}
//-----------------------------------------------------------------------------
// Support functions for BIT_ARRAY
@@ -420,8 +381,7 @@ static TMPQBlock * TranslateBlockTable(
BlockTableSize = sizeof(TMPQBlock) * ha->dwFileTableSize;
for(DWORD i = 0; i < ha->dwFileTableSize; i++)
{
if(pFileEntry->ByteOffset >> 32)
bNeedHiBlockTable = true;
bNeedHiBlockTable = (pFileEntry->ByteOffset >> 32) ? true : false;
pBlock->dwFilePos = (DWORD)pFileEntry->ByteOffset;
pBlock->dwFSize = pFileEntry->dwFileSize;
pBlock->dwCSize = pFileEntry->dwCmpSize;
@@ -519,11 +479,15 @@ TMPQExtTable * LoadExtTable(
int cbOutBuffer = (int)pCompressed->dwDataSize;
int cbInBuffer = (int)Size;
// Decompress the XXX block
// Decompress the extended table
pExtTable->dwSignature = pCompressed->dwSignature;
pExtTable->dwVersion = pCompressed->dwVersion;
pExtTable->dwDataSize = pCompressed->dwDataSize;
SCompDecompress((char *)(pExtTable + 1), &cbOutBuffer, (char *)(pCompressed + 1), cbInBuffer);
if(!SCompDecompress2((char *)(pExtTable + 1), &cbOutBuffer, (char *)(pCompressed + 1), cbInBuffer))
{
STORM_FREE(pExtTable);
pExtTable = NULL;
}
}
// Free the compressed block
@@ -537,9 +501,9 @@ TMPQExtTable * LoadExtTable(
}
// Used in MPQ Editor
void FreeExtTable(TMPQExtTable * pExtTable)
void FreeMpqBuffer(void * pvBuffer)
{
STORM_FREE(pExtTable);
STORM_FREE(pvBuffer);
}
static int SaveMpqTable(
@@ -1018,26 +982,18 @@ static void CreateBetHeader(
TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
TFileEntry * pFileEntry;
ULONGLONG MaxByteOffset = 0;
DWORD FlagArray[MAX_FLAG_INDEX];
DWORD dwMaxFlagIndex = 0;
DWORD dwMaxFileSize = 0;
DWORD dwMaxCmpSize = 0;
DWORD dwFlagCount = 0;
BYTE FlagComboArray[MAX_FLAG_INDEX];
BYTE FlagComboIndex;
DWORD dwFlagIndex;
// Initialize array of flag combinations
memset(FlagComboArray, 0, sizeof(FlagComboArray));
InitFileFlagArray(FlagArray);
// Get the maximum values for the BET table
for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
{
// We don't allow the file table to have free entries in the middle
// This must be a bug in the library somewhere.
if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0)
{
pFileEntry->dwFlags = MPQ_FILE_EXISTS | MPQ_FILE_DELETE_MARKER;
assert(false);
}
// Highest file position in the MPQ
if(pFileEntry->ByteOffset > MaxByteOffset)
MaxByteOffset = pFileEntry->ByteOffset;
@@ -1051,10 +1007,9 @@ static void CreateBetHeader(
dwMaxCmpSize = pFileEntry->dwCmpSize;
// Check if this flag was there before
FlagComboIndex = GetFlagsComboIndex(pFileEntry->dwFlags);
if(FlagComboArray[FlagComboIndex] == 0)
dwFlagCount++;
FlagComboArray[FlagComboIndex] = 1;
dwFlagIndex = GetFileFlagIndex(FlagArray, pFileEntry->dwFlags);
if(dwFlagIndex > dwMaxFlagIndex)
dwMaxFlagIndex = dwFlagIndex;
}
// Now save bit count for every piece of file information
@@ -1068,7 +1023,7 @@ static void CreateBetHeader(
pBetHeader->dwBitCount_CmpSize = GetNecessaryBitCount(dwMaxCmpSize);
pBetHeader->dwBitIndex_FlagIndex = pBetHeader->dwBitIndex_CmpSize + pBetHeader->dwBitCount_CmpSize;
pBetHeader->dwBitCount_FlagIndex = GetNecessaryBitCount(dwFlagCount);
pBetHeader->dwBitCount_FlagIndex = GetNecessaryBitCount(dwMaxFlagIndex + 1);
pBetHeader->dwBitIndex_Unknown = pBetHeader->dwBitIndex_FlagIndex + pBetHeader->dwBitCount_FlagIndex;
pBetHeader->dwBitCount_Unknown = 0;
@@ -1082,7 +1037,7 @@ static void CreateBetHeader(
// Save the file count and flag count
pBetHeader->dwFileCount = ha->dwFileTableSize;
pBetHeader->dwFlagCount = dwFlagCount;
pBetHeader->dwFlagCount = dwMaxFlagIndex + 1;
pBetHeader->dwUnknown08 = 0x10;
// Save the total size of the BET hash
@@ -1224,8 +1179,8 @@ TMPQExtTable * TranslateBetTable(
DWORD i;
// Calculate the bit sizes of various entries
InitFileFlagArray(FlagArray);
CreateBetHeader(ha, &BetHeader);
memset(FlagArray, 0, sizeof(FlagArray));
// Calculate the size of the BET table
BetTableSize = sizeof(BET_TABLE_HEADER) +
@@ -1260,30 +1215,32 @@ TMPQExtTable * TranslateBetTable(
// Construct the array of flag values and bit-based file table
for(i = 0; i < BetHeader.dwFileCount; i++, pFileEntry++)
{
// Only count files that exist
if(pFileEntry->dwFlags & MPQ_FILE_EXISTS)
{
// Save the byte offset
pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_FilePos,
BetHeader.dwBitCount_FilePos,
&pFileEntry->ByteOffset,
8);
pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_FileSize,
BetHeader.dwBitCount_FileSize,
&pFileEntry->dwFileSize,
4);
pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_CmpSize,
BetHeader.dwBitCount_CmpSize,
&pFileEntry->dwCmpSize,
4);
//
// Note: Blizzard MPQs contain valid values even for non-existant files
// (FilePos, FileSize, CmpSize and FlagIndex)
// Note: If flags is zero, it must be in the flag table too !!!
//
// Save the byte offset
pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_FilePos,
BetHeader.dwBitCount_FilePos,
&pFileEntry->ByteOffset,
8);
pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_FileSize,
BetHeader.dwBitCount_FileSize,
&pFileEntry->dwFileSize,
4);
pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_CmpSize,
BetHeader.dwBitCount_CmpSize,
&pFileEntry->dwCmpSize,
4);
// Get the flag array for the file
dwFlagIndex = GetFileFlagIndex(FlagArray, pFileEntry->dwFlags);
pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_FlagIndex,
BetHeader.dwBitCount_FlagIndex,
&dwFlagIndex,
4);
}
// Save the flag index
dwFlagIndex = GetFileFlagIndex(FlagArray, pFileEntry->dwFlags);
pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_FlagIndex,
BetHeader.dwBitCount_FlagIndex,
&dwFlagIndex,
4);
// Move the bit offset
nBitOffset += BetHeader.dwTableEntrySize;
@@ -1480,7 +1437,7 @@ void AllocateFileName(TFileEntry * pFileEntry, const char * szFileName)
TFileEntry * FindFreeFileEntry(TMPQArchive * ha)
{
TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
TFileEntry * pDeletedEntry = NULL;
TFileEntry * pFreeEntry = NULL;
TFileEntry * pFileEntry;
// Try to find a free entry
@@ -1488,18 +1445,22 @@ TFileEntry * FindFreeFileEntry(TMPQArchive * ha)
{
// If that entry is free, we reuse it
if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0)
return pFileEntry;
{
pFreeEntry = pFileEntry;
break;
}
// If that entry is deleted, remember it
if(pFileEntry->dwFlags & MPQ_FILE_DELETE_MARKER)
pDeletedEntry = pFileEntry;
//
// Note: Files with "delete marker" are not deleted.
// Don't consider them free entries
//
}
// Do we have a deleted entry?
if(pDeletedEntry != NULL)
if(pFreeEntry != NULL)
{
ClearFileEntry(ha, pDeletedEntry);
return pDeletedEntry;
ClearFileEntry(ha, pFreeEntry);
return pFreeEntry;
}
// If no file entry within the existing file table is free,
@@ -1702,9 +1663,11 @@ void ClearFileEntry(
4);
}
// Free the file name, and zero the entire entry
// Free the file name, and set the file entry as deleted
if(pFileEntry->szFileName != NULL)
STORM_FREE(pFileEntry->szFileName);
// Invalidate the file entry
memset(pFileEntry, 0, sizeof(TFileEntry));
}
@@ -1718,7 +1681,7 @@ int FreeFileEntry(
//
// If we have HET table, we cannot just get rid of the file
// Doing so would lead to empty gaps in the HET and BET tables
// Doing so would lead to empty gaps in the HET table
// We have to keep BET hash, hash index, HET index, locale, platform and file name
//
@@ -1750,14 +1713,10 @@ int FreeFileEntry(
}
else
{
memset(pFileEntry->md5, 0, MD5_DIGEST_SIZE);
pFileEntry->ByteOffset = 0;
pFileEntry->FileTime = 0;
pFileEntry->dwFileSize = 0;
pFileEntry->dwCmpSize = 0;
pFileEntry->dwFlags = MPQ_FILE_EXISTS | MPQ_FILE_DELETE_MARKER;
pFileEntry->dwCrc32 = 0;
nError = ERROR_MARKED_FOR_DELETE;
// Note: Deleted entries in Blizzard MPQs version 4.0
// normally contain valid byte offset and length
pFileEntry->dwFlags &= ~MPQ_FILE_EXISTS;
nError = ERROR_SUCCESS;
}
return nError;
@@ -1785,14 +1744,172 @@ void InvalidateInternalFiles(TMPQArchive * ha)
ha->dwFlags |= MPQ_FLAG_INV_ATTRIBUTES;
}
// Remember that the MPQ has been changed and it will ne necessary
// Remember that the MPQ has been changed and it will be necessary
// to update the tables
ha->dwFlags |= MPQ_FLAG_CHANGED;
}
//-----------------------------------------------------------------------------
// Support for file tables - hash table, block table, hi-block table,
// (attributes) and (listfile)
// Functions that loads and verify MPQ data bitmap
int LoadMpqDataBitmap(TMPQArchive * ha, ULONGLONG FileSize, bool * pbFileIsComplete)
{
TMPQBitmap * pBitmap = NULL;
TMPQBitmap DataBitmap;
ULONGLONG BitmapOffset;
ULONGLONG EndOfMpq;
DWORD DataBlockCount = 0;
DWORD BitmapByteSize;
DWORD WholeByteCount;
DWORD ExtraBitsCount;
// Is there enough space for a MPQ bitmap?
EndOfMpq = ha->MpqPos + ha->pHeader->ArchiveSize64;
FileSize = FileSize - sizeof(TMPQBitmap);
if(FileSize > EndOfMpq)
{
// Try to load the data bitmap from the end of the file
if(FileStream_Read(ha->pStream, &FileSize, &DataBitmap, sizeof(TMPQBitmap)))
{
// Is it a valid data bitmap?
BSWAP_ARRAY32_UNSIGNED((LPDWORD)(&DataBitmap), sizeof(TMPQBitmap));
if(DataBitmap.dwSignature == MPQ_DATA_BITMAP_SIGNATURE)
{
// We assume that MPQs with data bitmap begin at position 0
assert(ha->MpqPos == 0);
// Calculate the number of extra bytes for data bitmap
DataBlockCount = (DWORD)(((ha->pHeader->ArchiveSize64 - 1) / DataBitmap.dwBlockSize) + 1);
BitmapByteSize = ((DataBlockCount - 1) / 8) + 1;
// Verify the data block size
BitmapOffset = ((ULONGLONG)DataBitmap.dwMapOffsetHi << 32) | DataBitmap.dwMapOffsetLo;
assert((DWORD)(FileSize - BitmapOffset) == BitmapByteSize);
// Allocate space for the data bitmap
pBitmap = (TMPQBitmap *)STORM_ALLOC(BYTE, sizeof(TMPQBitmap) + BitmapByteSize);
if(pBitmap != NULL)
{
// Copy the bitmap header
memcpy(pBitmap, &DataBitmap, sizeof(TMPQBitmap));
// Read the remaining part
if(!FileStream_Read(ha->pStream, &BitmapOffset, (pBitmap + 1), BitmapByteSize))
{
STORM_FREE(pBitmap);
pBitmap = NULL;
}
}
}
}
}
// If the caller asks for file completeness, check it
if(pBitmap != NULL && pbFileIsComplete != NULL)
{
LPBYTE pbBitmap = (LPBYTE)(pBitmap + 1);
DWORD i;
bool bFileIsComplete = true;
// Calculate the number of whole bytes and extra bits of the bitmap
WholeByteCount = (DataBlockCount / 8);
ExtraBitsCount = (DataBlockCount & 7);
// Verify the whole bytes - their value must be 0xFF
for(i = 0; i < WholeByteCount; i++)
{
if(pbBitmap[i] != 0xFF)
bFileIsComplete = false;
}
// If there are extra bits, calculate the mask
if(ExtraBitsCount != 0)
{
BYTE ExpectedValue = (BYTE)((1 << ExtraBitsCount) - 1);
if(pbBitmap[i] != ExpectedValue)
bFileIsComplete = false;
}
// Give the result to the caller
*pbFileIsComplete = bFileIsComplete;
}
ha->pBitmap = pBitmap;
return ERROR_SUCCESS;
}
//-----------------------------------------------------------------------------
// Support for file tables - hash table, block table, hi-block table
int CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize)
{
TMPQHash * pHashTable;
// Sanity checks
assert((dwHashTableSize & (dwHashTableSize - 1)) == 0);
assert(ha->pHashTable == NULL);
// Create the hash table
pHashTable = STORM_ALLOC(TMPQHash, dwHashTableSize);
if(pHashTable == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
// Fill it
memset(pHashTable, 0xFF, dwHashTableSize * sizeof(TMPQHash));
ha->pHashTable = pHashTable;
// Set the max file count, if needed
if(ha->pHetTable == NULL)
ha->dwMaxFileCount = dwHashTableSize;
return ERROR_SUCCESS;
}
TMPQHash * LoadHashTable(TMPQArchive * ha)
{
TMPQHeader * pHeader = ha->pHeader;
ULONGLONG ByteOffset;
TMPQHash * pHashTable;
DWORD dwTableSize;
DWORD dwCmpSize;
int nError;
// If the MPQ has no hash table, do nothing
if(pHeader->dwHashTablePos == 0 && pHeader->wHashTablePosHi == 0)
return NULL;
// If the hash table size is zero, do nothing
if(pHeader->dwHashTableSize == 0)
return NULL;
// Allocate buffer for the hash table
dwTableSize = pHeader->dwHashTableSize * sizeof(TMPQHash);
pHashTable = STORM_ALLOC(TMPQHash, pHeader->dwHashTableSize);
if(pHashTable == NULL)
return NULL;
// Compressed size of the hash table
dwCmpSize = (DWORD)pHeader->HashTableSize64;
//
// Load the table from the MPQ, with decompression
//
// Note: We will NOT check if the hash table is properly decrypted.
// Some MPQ protectors corrupt the hash table by rewriting part of it.
// Hash table, the way how it works, allows arbitrary values for unused entries.
//
ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos);
nError = LoadMpqTable(ha, ByteOffset, pHashTable, dwCmpSize, dwTableSize, MPQ_KEY_HASH_TABLE);
if(nError != ERROR_SUCCESS)
{
STORM_FREE(pHashTable);
pHashTable = NULL;
}
// Return the hash table
return pHashTable;
}
static void FixBlockTableSize(
TMPQArchive * ha,
@@ -1828,80 +1945,68 @@ static void FixBlockTableSize(
pHeader->dwBlockTableSize = dwClaimedSize;
}
int CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize)
{
TMPQHash * pHashTable;
// Sanity checks
assert((dwHashTableSize & (dwHashTableSize - 1)) == 0);
assert(ha->pHashTable == NULL);
// Create the hash table
pHashTable = STORM_ALLOC(TMPQHash, dwHashTableSize);
if(pHashTable == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
// Fill it
memset(pHashTable, 0xFF, dwHashTableSize * sizeof(TMPQHash));
ha->pHashTable = pHashTable;
// Set the max file count, if needed
if(ha->pHetTable == NULL)
ha->dwMaxFileCount = dwHashTableSize;
return ERROR_SUCCESS;
}
int LoadHashTable(TMPQArchive * ha)
TMPQBlock * LoadBlockTable(TMPQArchive * ha, ULONGLONG FileSize)
{
TMPQHeader * pHeader = ha->pHeader;
TMPQBlock * pBlockTable;
ULONGLONG ByteOffset;
TMPQHash * pHashTable;
DWORD dwTableSize;
DWORD dwCmpSize;
int nError;
// If the MPQ has no hash table, do nothing
if(pHeader->dwHashTablePos == 0 && pHeader->wHashTablePosHi == 0)
return ERROR_SUCCESS;
// Do nothing if the block table position is zero
if(pHeader->dwBlockTablePos == 0 && pHeader->wBlockTablePosHi == 0)
return NULL;
// If the hash table size is zero, do nothing
if(pHeader->dwHashTableSize == 0)
return ERROR_SUCCESS;
// Do nothing if the block table size is zero
if(pHeader->dwBlockTableSize == 0)
return NULL;
// Allocate buffer for the hash table
dwTableSize = pHeader->dwHashTableSize * sizeof(TMPQHash);
pHashTable = STORM_ALLOC(TMPQHash, pHeader->dwHashTableSize);
if(pHashTable == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
// Sanity check, enforced by LoadAnyHashTable
assert(ha->dwMaxFileCount >= pHeader->dwBlockTableSize);
// Compressed size of the hash table
dwCmpSize = (DWORD)pHeader->HashTableSize64;
// Calculate sizes of both tables
ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos);
dwTableSize = pHeader->dwBlockTableSize * sizeof(TMPQBlock);
dwCmpSize = (DWORD)pHeader->BlockTableSize64;
//
// Load the table from the MPQ, with decompression
//
// Note: We will NOT check if the hash table is properly decrypted.
// Some MPQ protectors corrupt the hash table by rewriting part of it.
// Hash table, the way how it works, allows arbitrary values for unused entries.
//
// Allocate space for the block table
// Note: pHeader->dwBlockTableSize can be zero !!!
pBlockTable = STORM_ALLOC(TMPQBlock, ha->dwMaxFileCount);
if(pBlockTable == NULL)
return NULL;
ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos);
nError = LoadMpqTable(ha, ByteOffset, pHashTable, dwCmpSize, dwTableSize, MPQ_KEY_HASH_TABLE);
if(nError != ERROR_SUCCESS)
// Fill the block table with zeros
memset(pBlockTable, 0, dwTableSize);
// I found a MPQ which claimed 0x200 entries in the block table,
// but the file was cut and there was only 0x1A0 entries.
// We will handle this case properly.
if(dwTableSize == dwCmpSize && (ByteOffset + dwTableSize) > FileSize)
{
STORM_FREE(pHashTable);
pHashTable = NULL;
return nError;
pHeader->dwBlockTableSize = (DWORD)((FileSize - ByteOffset) / sizeof(TMPQBlock));
pHeader->BlockTableSize64 = pHeader->dwBlockTableSize * sizeof(TMPQBlock);
dwTableSize = dwCmpSize = pHeader->dwBlockTableSize * sizeof(TMPQBlock);
}
// Set the maximum file count to the size of the hash table
// In case there is HET table, we have to keep the file limit
if(ha->pHetTable == NULL)
ha->dwMaxFileCount = pHeader->dwHashTableSize;
//
// One of the first cracked versions of Diablo I had block table unencrypted
// StormLib does NOT support such MPQs anymore, as they are incompatible
// with compressed block table feature
//
// Store the hash table to the MPQ
ha->pHashTable = pHashTable;
return ERROR_SUCCESS;
// Load the block table
nError = LoadMpqTable(ha, ByteOffset, pBlockTable, dwCmpSize, dwTableSize, MPQ_KEY_BLOCK_TABLE);
if(nError != ERROR_SUCCESS)
{
// Failed, sorry
STORM_FREE(pBlockTable);
return NULL;
}
// Defense against MPQs that claim block table to be bigger than it really is
FixBlockTableSize(ha, pBlockTable, pHeader->dwBlockTableSize);
return pBlockTable;
}
int LoadHetTable(TMPQArchive * ha)
@@ -1963,19 +2068,25 @@ TMPQBetTable * LoadBetTable(TMPQArchive * ha)
int LoadAnyHashTable(TMPQArchive * ha)
{
TMPQHeader * pHeader = ha->pHeader;
bool bHashTableLoaded = false;
// If the MPQ archive is empty, don't bother trying to load anything
if(pHeader->dwHashTableSize == 0 && pHeader->HetTableSize64 == 0)
return CreateHashTable(ha, HASH_TABLE_SIZE_DEFAULT);
// Try to load HET table
if(LoadHetTable(ha) == ERROR_SUCCESS)
bHashTableLoaded = true;
// Try to load HET and/or classic hash table
LoadHetTable(ha);
// Try to load the classic hash table
if(LoadHashTable(ha) == ERROR_SUCCESS)
bHashTableLoaded = true;
// Load the HASH table
ha->pHashTable = LoadHashTable(ha);
// Set the maximum file count to the size of the hash table
// In case there is HET table, we have to keep the file limit
if(ha->pHetTable == NULL)
ha->dwMaxFileCount = pHeader->dwHashTableSize;
// Did at least one succeed?
if(ha->pHetTable == NULL && ha->pHashTable == NULL)
return ERROR_FILE_CORRUPT;
// In theory, a MPQ could have bigger block table than hash table
if(ha->pHeader->dwBlockTableSize > ha->dwMaxFileCount)
@@ -1984,7 +2095,7 @@ int LoadAnyHashTable(TMPQArchive * ha)
ha->dwFlags |= MPQ_FLAG_READ_ONLY;
}
return bHashTableLoaded ? ERROR_SUCCESS : ERROR_FILE_CORRUPT;
return ERROR_SUCCESS;
}
int BuildFileTable_Classic(
@@ -2001,118 +2112,80 @@ int BuildFileTable_Classic(
// Sanity checks
assert(ha->pHashTable != NULL);
// Do nothing if the size of the block table is zero
if(pHeader->dwBlockTablePos != 0 && pHeader->dwBlockTableSize != 0)
// Load the block table
pBlockTable = LoadBlockTable(ha, FileSize);
if(pBlockTable != NULL)
{
// Sanity check, enforced by LoadAnyHashTable
assert(ha->dwMaxFileCount >= pHeader->dwBlockTableSize);
TMPQHash * pHashEnd = ha->pHashTable + pHeader->dwHashTableSize;
TMPQHash * pHash;
// Allocate space for the block table
// Note: pHeader->dwBlockTableSize can be zero !!!
pBlockTable = STORM_ALLOC(TMPQBlock, ha->dwMaxFileCount);
if(pBlockTable != NULL)
// If we don't have HET table, we build the file entries from the hash&block tables
if(ha->pHetTable == NULL)
{
ULONGLONG ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos);
TMPQHash * pHashEnd = ha->pHashTable + pHeader->dwHashTableSize;
TMPQHash * pHash;
DWORD dwTableSize = pHeader->dwBlockTableSize * sizeof(TMPQBlock);
DWORD dwCmpSize = (DWORD)pHeader->BlockTableSize64;
// Fill the block table with zeros
memset(pBlockTable, 0, dwTableSize);
// I have found a MPQ which claimed 0x200 entries in the block table,
// but the file was cut and there was only 0x1A0 entries.
// We will handle this case properly.
if(dwTableSize == dwCmpSize && (ByteOffset + dwTableSize) > FileSize)
for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++)
{
pHeader->dwBlockTableSize = (DWORD)((FileSize - ByteOffset) / sizeof(TMPQBlock));
pHeader->BlockTableSize64 = pHeader->dwBlockTableSize * sizeof(TMPQBlock);
dwTableSize = dwCmpSize = pHeader->dwBlockTableSize * sizeof(TMPQBlock);
}
//
// One of the first cracked versions of Diablo I had block table unencrypted
// StormLib does NOT support such MPQs anymore, as they are incompatible
// with compressed block table feature
//
// Load the block table
nError = LoadMpqTable(ha, ByteOffset, pBlockTable, dwCmpSize, dwTableSize, MPQ_KEY_BLOCK_TABLE);
if(nError == ERROR_SUCCESS)
{
// Defense against MPQs that claim block table to be bigger than it really is
FixBlockTableSize(ha, pBlockTable, pHeader->dwBlockTableSize);
// If we don't have HET table, we build the file entries from the hash&block tables
if(ha->pHetTable == NULL)
if(pHash->dwBlockIndex < pHeader->dwBlockTableSize)
{
for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++)
pFileEntry = pFileTable + pHash->dwBlockIndex;
pBlock = pBlockTable + pHash->dwBlockIndex;
//
// Yet another silly map protector: For each valid file,
// there are 4 items in the hash table, that appears to be valid:
//
// a6d79af0 e61a0932 001e0000 0000770b <== Fake valid
// a6d79af0 e61a0932 0000d761 0000dacb <== Fake valid
// a6d79af0 e61a0932 00000000 0000002f <== Real file entry
// a6d79af0 e61a0932 00005a4f 000093bc <== Fake valid
//
if(!(pBlock->dwFlags & ~MPQ_FILE_VALID_FLAGS) && (pBlock->dwFlags & MPQ_FILE_EXISTS))
{
if(pHash->dwBlockIndex < pHeader->dwBlockTableSize)
{
pFileEntry = pFileTable + pHash->dwBlockIndex;
pBlock = pBlockTable + pHash->dwBlockIndex;
//
// Yet another silly map protector: For each valid file,
// there are 4 items in the hash table, that appears to be valid:
//
// a6d79af0 e61a0932 001e0000 0000770b <== Fake valid
// a6d79af0 e61a0932 0000d761 0000dacb <== Fake valid
// a6d79af0 e61a0932 00000000 0000002f <== Real file entry
// a6d79af0 e61a0932 00005a4f 000093bc <== Fake valid
//
if(!(pBlock->dwFlags & ~MPQ_FILE_VALID_FLAGS) && (pBlock->dwFlags & MPQ_FILE_EXISTS))
{
// Fill the entry
pFileEntry->ByteOffset = pBlock->dwFilePos;
pFileEntry->dwHashIndex = (DWORD)(pHash - ha->pHashTable);
pFileEntry->dwFileSize = pBlock->dwFSize;
pFileEntry->dwCmpSize = pBlock->dwCSize;
pFileEntry->dwFlags = pBlock->dwFlags;
pFileEntry->lcLocale = pHash->lcLocale;
pFileEntry->wPlatform = pHash->wPlatform;
}
else
{
// If the hash table entry doesn't point to the valid file item,
// we invalidate the entire hash table entry
pHash->dwName1 = 0xFFFFFFFF;
pHash->dwName2 = 0xFFFFFFFF;
pHash->lcLocale = 0xFFFF;
pHash->wPlatform = 0xFFFF;
pHash->dwBlockIndex = HASH_ENTRY_DELETED;
}
}
// Fill the entry
pFileEntry->ByteOffset = pBlock->dwFilePos;
pFileEntry->dwHashIndex = (DWORD)(pHash - ha->pHashTable);
pFileEntry->dwFileSize = pBlock->dwFSize;
pFileEntry->dwCmpSize = pBlock->dwCSize;
pFileEntry->dwFlags = pBlock->dwFlags;
pFileEntry->lcLocale = pHash->lcLocale;
pFileEntry->wPlatform = pHash->wPlatform;
}
}
else
{
for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++)
else
{
if(pHash->dwBlockIndex < ha->dwFileTableSize)
{
pFileEntry = pFileTable + pHash->dwBlockIndex;
if(pFileEntry->dwFlags & MPQ_FILE_EXISTS)
{
pFileEntry->dwHashIndex = (DWORD)(pHash - ha->pHashTable);
pFileEntry->lcLocale = pHash->lcLocale;
pFileEntry->wPlatform = pHash->wPlatform;
}
}
// If the hash table entry doesn't point to the valid file item,
// we invalidate the entire hash table entry
pHash->dwName1 = 0xFFFFFFFF;
pHash->dwName2 = 0xFFFFFFFF;
pHash->lcLocale = 0xFFFF;
pHash->wPlatform = 0xFFFF;
pHash->dwBlockIndex = HASH_ENTRY_DELETED;
}
}
}
// Free the block table
STORM_FREE(pBlockTable);
}
else
{
nError = ERROR_NOT_ENOUGH_MEMORY;
for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++)
{
if(pHash->dwBlockIndex < ha->dwFileTableSize)
{
pFileEntry = pFileTable + pHash->dwBlockIndex;
if(pFileEntry->dwFlags & MPQ_FILE_EXISTS)
{
pFileEntry->dwHashIndex = (DWORD)(pHash - ha->pHashTable);
pFileEntry->lcLocale = pHash->lcLocale;
pFileEntry->wPlatform = pHash->wPlatform;
}
}
}
}
// Free the block table
STORM_FREE(pBlockTable);
}
else
{
nError = ERROR_NOT_ENOUGH_MEMORY;
}
// Load the hi-block table
+174 -89
View File
@@ -175,7 +175,7 @@ void Compress_ZLIB(
// Storm.dll uses zlib version 1.1.3
// Wow.exe uses zlib version 1.2.3
nResult = deflateInit2(&z,
Z_DEFAULT_COMPRESSION,
6, // Compression level used by WoW MPQs
Z_DEFLATED,
windowBits,
8,
@@ -292,7 +292,13 @@ static void Compress_PKLIB(
Info.pbOutBuff = pbOutBuffer;
Info.pbOutBuffEnd = pbOutBuffer + *pcbOutBuffer;
// Set the compression type and dictionary size
//
// Set the dictionary size
//
// Diablo I ues fixed dictionary size of CMP_IMPLODE_DICT_SIZE3
// Starcraft uses the variable dictionary size based on algorithm below
//
if (cbInBuffer < 0x600)
dict_size = CMP_IMPLODE_DICT_SIZE1;
else if(0x600 <= cbInBuffer && cbInBuffer < 0xC00)
@@ -455,7 +461,7 @@ static void LZMA_Callback_Free(void *p, void *address)
// the data compressed by StormLib.
//
static void Compress_LZMA(
/*static */ void Compress_LZMA(
char * pbOutBuffer,
int * pcbOutBuffer,
char * pbInBuffer,
@@ -752,13 +758,13 @@ int WINAPI SCompExplode(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffe
static TCompressTable cmp_table[] =
{
{MPQ_COMPRESSION_SPARSE, Compress_SPARSE}, // Sparse compression
{MPQ_COMPRESSION_WAVE_MONO, Compress_ADPCM_mono}, // IMA ADPCM mono compression
{MPQ_COMPRESSION_WAVE_STEREO, Compress_ADPCM_stereo}, // IMA ADPCM stereo compression
{MPQ_COMPRESSION_HUFFMANN, Compress_huff}, // Huffmann compression
{MPQ_COMPRESSION_ZLIB, Compress_ZLIB}, // Compression with the "zlib" library
{MPQ_COMPRESSION_PKWARE, Compress_PKLIB}, // Compression with Pkware DCL
{MPQ_COMPRESSION_BZIP2, Compress_BZIP2} // Compression Bzip2 library
{MPQ_COMPRESSION_SPARSE, Compress_SPARSE}, // Sparse compression
{MPQ_COMPRESSION_ADPCM_MONO, Compress_ADPCM_mono}, // IMA ADPCM mono compression
{MPQ_COMPRESSION_ADPCM_STEREO, Compress_ADPCM_stereo}, // IMA ADPCM stereo compression
{MPQ_COMPRESSION_HUFFMANN, Compress_huff}, // Huffmann compression
{MPQ_COMPRESSION_ZLIB, Compress_ZLIB}, // Compression with the "zlib" library
{MPQ_COMPRESSION_PKWARE, Compress_PKLIB}, // Compression with Pkware DCL
{MPQ_COMPRESSION_BZIP2, Compress_BZIP2} // Compression Bzip2 library
};
int WINAPI SCompCompress(
@@ -904,13 +910,13 @@ int WINAPI SCompCompress(
// of compressed data
static TDecompressTable dcmp_table[] =
{
{MPQ_COMPRESSION_BZIP2, Decompress_BZIP2}, // Decompression with Bzip2 library
{MPQ_COMPRESSION_PKWARE, Decompress_PKLIB}, // Decompression with Pkware Data Compression Library
{MPQ_COMPRESSION_ZLIB, Decompress_ZLIB}, // Decompression with the "zlib" library
{MPQ_COMPRESSION_HUFFMANN, Decompress_huff}, // Huffmann decompression
{MPQ_COMPRESSION_WAVE_STEREO, Decompress_ADPCM_stereo}, // IMA ADPCM stereo decompression
{MPQ_COMPRESSION_WAVE_MONO, Decompress_ADPCM_mono}, // IMA ADPCM mono decompression
{MPQ_COMPRESSION_SPARSE, Decompress_SPARSE} // Sparse decompression
{MPQ_COMPRESSION_BZIP2, Decompress_BZIP2}, // Decompression with Bzip2 library
{MPQ_COMPRESSION_PKWARE, Decompress_PKLIB}, // Decompression with Pkware Data Compression Library
{MPQ_COMPRESSION_ZLIB, Decompress_ZLIB}, // Decompression with the "zlib" library
{MPQ_COMPRESSION_HUFFMANN, Decompress_huff}, // Huffmann decompression
{MPQ_COMPRESSION_ADPCM_STEREO, Decompress_ADPCM_stereo}, // IMA ADPCM stereo decompression
{MPQ_COMPRESSION_ADPCM_MONO, Decompress_ADPCM_mono}, // IMA ADPCM mono decompression
{MPQ_COMPRESSION_SPARSE, Decompress_SPARSE} // Sparse decompression
};
int WINAPI SCompDecompress(
@@ -919,109 +925,86 @@ int WINAPI SCompDecompress(
char * pbInBuffer,
int cbInBuffer)
{
DECOMPRESS DecompressFuncArray[0x10]; // Array of compression functions, applied sequentially
char * pbWorkBuffer = NULL; // Temporary storage for decompressed data
char * pbOutput = pbOutBuffer; // Where to store decompressed data
char * pbInput; // Where to store decompressed data
unsigned uCompressionMask; // Decompressions applied to the data
unsigned uCompressionCopy; // Decompressions applied to the data
int cbOutBuffer = *pcbOutBuffer; // Current size of the output buffer
int cbInLength; // Current size of the input buffer
int nCompressCount = 0; // Number of compressions to be applied
int nCompressIndex = 0;
int nResult = 1;
// Zero input data bring zero output data
if(cbInBuffer == 0)
{
*pcbOutBuffer = 0;
return 1;
}
// Verify buffer sizes
if(cbOutBuffer < cbInBuffer || cbInBuffer < 1)
return 0;
/*
// If the input length is the same as output length, do nothing.
// Unfortunately, some data in WoW-Cataclysm BETA MPQs are like that ....
if(cbInBuffer == cbOutBuffer)
if(cbOutBuffer == cbInBuffer)
{
// If the buffers are equal, don't copy anything.
if(pbInBuffer == pbOutBuffer)
return 1;
memcpy(pbOutBuffer, pbInBuffer, cbInBuffer);
if(pbInBuffer != pbOutBuffer)
memcpy(pbOutBuffer, pbInBuffer, cbInBuffer);
return 1;
}
*/
// Get applied compression types and decrement data length
uCompressionMask = (unsigned char)*pbInBuffer++;
uCompressionMask = uCompressionCopy = (unsigned char)*pbInBuffer++;
cbInBuffer--;
// Get current compressed data and length of it
pbInput = pbInBuffer;
cbInLength = cbInBuffer;
//
// Beginning with Starcraft II, the decompression byte can no longer contain
// any arbitrary combination types. Instead, it can be one of the few
// pre-set values (0x02, 0x08, 0x10, 0x12, 0x20, 0x22, 0x30)
// Note that Starcraft II no longer uses WAVE files, so compressions 0x41, 0x48, 0x81, 0x88
// are no longer used.
//
// This compression function doesn't support LZMA
assert(uCompressionMask != MPQ_COMPRESSION_LZMA);
// Special case: LZMA decompression (added in Starcraft II)
if(uCompressionMask == MPQ_COMPRESSION_LZMA)
// Parse the compression mask
for(size_t i = 0; i < (sizeof(dcmp_table) / sizeof(TDecompressTable)); i++)
{
DecompressFuncArray[0] = Decompress_LZMA;
nCompressCount = 1;
}
else
{
// Fill the compressions array
for(size_t i = 0; i < (sizeof(dcmp_table) / sizeof(TDecompressTable)); i++)
// If the mask agrees, insert the compression function to the array
if(uCompressionMask & dcmp_table[i].uMask)
{
// If the mask agrees, insert the compression function to the array
if(uCompressionMask & dcmp_table[i].uMask)
{
DecompressFuncArray[nCompressCount] = dcmp_table[i].Decompress;
uCompressionMask &= ~dcmp_table[i].uMask;
nCompressCount++;
}
uCompressionCopy &= ~dcmp_table[i].uMask;
nCompressCount++;
}
}
// If at least one of the compressions remaing unknown, return an error
if(uCompressionMask != 0)
// If at least one of the compressions remaing unknown, return an error
if(nCompressCount == 0 || uCompressionCopy != 0)
{
SetLastError(ERROR_NOT_SUPPORTED);
return 0;
}
// If there is more than one compression, we have to allocate extra buffer
if(nCompressCount > 1)
{
pbWorkBuffer = STORM_ALLOC(char, cbOutBuffer);
if(pbWorkBuffer == NULL)
{
SetLastError(ERROR_NOT_SUPPORTED);
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return 0;
}
}
// If there is at least one decompression, do it
if(nCompressCount > 0)
// Get the current compression index
nCompressIndex = nCompressCount - 1;
// Apply all decompressions
for(size_t i = 0; i < (sizeof(dcmp_table) / sizeof(TDecompressTable)); i++)
{
// If there is more than one compression, we have to allocate extra buffer
if(nCompressCount > 1)
{
pbWorkBuffer = STORM_ALLOC(char, *pcbOutBuffer);
if(pbWorkBuffer == NULL)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return 0;
}
}
// Get the current compression index
nCompressIndex = nCompressCount - 1;
// Apply all decompressions
for(int i = 0; i < nCompressCount; i++)
// Perform the (next) decompression
if(uCompressionMask & dcmp_table[i].uMask)
{
// Get the correct output buffer
pbOutput = (nCompressIndex & 1) ? pbWorkBuffer : pbOutBuffer;
nCompressIndex--;
// Perform the (next) decompression
// Perform the decompression
cbOutBuffer = *pcbOutBuffer;
nResult = DecompressFuncArray[i](pbOutput, &cbOutBuffer, pbInput, cbInLength);
nResult = dcmp_table[i].Decompress(pbOutput, &cbOutBuffer, pbInput, cbInLength);
if(nResult == 0 || cbOutBuffer == 0)
{
SetLastError(ERROR_FILE_CORRUPT);
@@ -1030,21 +1013,123 @@ int WINAPI SCompDecompress(
}
// Switch buffers
pbInput = pbOutput;
cbInLength = cbOutBuffer;
pbInput = pbOutput;
}
}
// Put the length of the decompressed data to the output buffer
*pcbOutBuffer = cbOutBuffer;
}
else
{
memcpy(pbOutBuffer, pbInBuffer, cbInBuffer);
*pcbOutBuffer = cbInBuffer;
}
// Put the length of the decompressed data to the output buffer
*pcbOutBuffer = cbOutBuffer;
// Cleanup and return
if(pbWorkBuffer != NULL)
STORM_FREE(pbWorkBuffer);
return nResult;
}
int WINAPI SCompDecompress2(
char * pbOutBuffer,
int * pcbOutBuffer,
char * pbInBuffer,
int cbInBuffer)
{
DECOMPRESS pfnDecompress1 = NULL;
DECOMPRESS pfnDecompress2 = NULL;
char * pbWorkBuffer = pbOutBuffer;
int cbWorkBuffer = *pcbOutBuffer;
int nResult;
char CompressionMethod;
// Verify buffer sizes
if(*pcbOutBuffer < cbInBuffer || cbInBuffer < 1)
return 0;
// If the outputbuffer is as big as input buffer, just copy the block
if(*pcbOutBuffer == cbInBuffer)
{
if(pbOutBuffer != pbInBuffer)
memcpy(pbOutBuffer, pbInBuffer, cbInBuffer);
return 1;
}
// Get the compression methods
CompressionMethod = *pbInBuffer++;
cbInBuffer--;
// We only recognize a fixed set of compression methods
switch((unsigned char)CompressionMethod)
{
case MPQ_COMPRESSION_ZLIB:
pfnDecompress1 = Decompress_ZLIB;
break;
case MPQ_COMPRESSION_PKWARE:
pfnDecompress1 = Decompress_PKLIB;
break;
case MPQ_COMPRESSION_BZIP2:
pfnDecompress1 = Decompress_BZIP2;
break;
case MPQ_COMPRESSION_LZMA:
pfnDecompress1 = Decompress_LZMA;
break;
case MPQ_COMPRESSION_SPARSE:
pfnDecompress1 = Decompress_SPARSE;
break;
case (MPQ_COMPRESSION_SPARSE | MPQ_COMPRESSION_ZLIB):
pfnDecompress1 = Decompress_ZLIB;
pfnDecompress2 = Decompress_SPARSE;
break;
case (MPQ_COMPRESSION_SPARSE | MPQ_COMPRESSION_BZIP2):
pfnDecompress1 = Decompress_BZIP2;
pfnDecompress2 = Decompress_SPARSE;
break;
//
// Note: Any combination including MPQ_COMPRESSION_ADPCM_MONO,
// MPQ_COMPRESSION_ADPCM_STEREO or MPQ_COMPRESSION_HUFFMANN
// is not supported by newer MPQs.
//
default:
SetLastError(ERROR_FILE_CORRUPT);
return 0;
}
// If we have to use two decompressions, allocate temporary buffer
if(pfnDecompress2 != NULL)
{
pbWorkBuffer = STORM_ALLOC(char, *pcbOutBuffer);
if(pbWorkBuffer == NULL)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return 0;
}
}
// Apply the first decompression method
nResult = pfnDecompress1(pbWorkBuffer, &cbWorkBuffer, pbInBuffer, cbInBuffer);
// Apply the second decompression method, if any
if(pfnDecompress2 != NULL && nResult != 0)
{
cbInBuffer = cbWorkBuffer;
cbWorkBuffer = *pcbOutBuffer;
nResult = pfnDecompress2(pbOutBuffer, &cbWorkBuffer, pbWorkBuffer, cbInBuffer);
}
// Supply the output buffer size
*pcbOutBuffer = cbWorkBuffer;
// Free temporary buffer
if(pbWorkBuffer != pbOutBuffer)
STORM_FREE(pbWorkBuffer);
if(nResult == 0)
SetLastError(ERROR_FILE_CORRUPT);
return nResult;
}
+107 -32
View File
@@ -12,6 +12,33 @@
#include "StormLib.h"
#include "StormCommon.h"
//-----------------------------------------------------------------------------
// Local structures
#define FILE_SIGNATURE_RIFF 0x46464952
#define FILE_SIGNATURE_WAVE 0x45564157
#define FILE_SIGNATURE_FMT 0x20746D66
#define AUDIO_FORMAT_PCM 1
typedef struct _WAVE_FILE_HEADER
{
DWORD dwChunkId; // 0x52494646 ("RIFF")
DWORD dwChunkSize; // Size of that chunk, in bytes
DWORD dwFormat; // Must be 0x57415645 ("WAVE")
// Format sub-chunk
DWORD dwSubChunk1Id; // 0x666d7420 ("fmt ")
DWORD dwSubChunk1Size; // 0x16 for PCM
USHORT wAudioFormat; // 1 = PCM. Other value means some sort of compression
USHORT wChannels; // Number of channels
DWORD dwSampleRate; // 8000, 44100, etc.
DWORD dwBytesRate; // SampleRate * NumChannels * BitsPerSample/8
USHORT wBlockAlign; // NumChannels * BitsPerSample/8
USHORT wBitsPerSample; // 8 bits = 8, 16 bits = 16, etc.
// Followed by "data" sub-chunk (we don't care)
} WAVE_FILE_HEADER, *PWAVE_FILE_HEADER;
//-----------------------------------------------------------------------------
// Local variables
@@ -27,6 +54,29 @@ static void * pvUserData = NULL;
#define LOSSY_COMPRESSION_MASK (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN)
static int IsWaveFile(
LPBYTE pbFileData,
DWORD cbFileData,
LPDWORD pdwChannels)
{
PWAVE_FILE_HEADER pWaveHdr = (PWAVE_FILE_HEADER)pbFileData;
if(cbFileData > sizeof(WAVE_FILE_HEADER))
{
if(pWaveHdr->dwChunkId == FILE_SIGNATURE_RIFF && pWaveHdr->dwFormat == FILE_SIGNATURE_WAVE)
{
if(pWaveHdr->dwSubChunk1Id == FILE_SIGNATURE_FMT && pWaveHdr->wAudioFormat == AUDIO_FORMAT_PCM)
{
*pdwChannels = pWaveHdr->wChannels;
return true;
}
}
}
return false;
}
static int WriteDataToMpqFile(
TMPQArchive * ha,
TMPQFile * hf,
@@ -86,16 +136,6 @@ static int WriteDataToMpqFile(
// Set the position in the file
ByteOffset = hf->RawFilePos + pFileEntry->dwCmpSize;
// If the file is compressed, allocate buffer for the compressed data.
// Note that we allocate buffer that is a bit longer than sector size,
// for case if the compression method performs a buffer overrun
if((pFileEntry->dwFlags & MPQ_FILE_COMPRESSED) && pbCompressed == NULL)
{
pbToWrite = pbCompressed = STORM_ALLOC(BYTE, hf->dwSectorSize + 0x100);
if(pbCompressed == NULL)
nError = ERROR_NOT_ENOUGH_MEMORY;
}
// Update CRC32 and MD5 of the file
md5_process((hash_state *)hf->hctx, hf->pbFileSector, dwBytesInSector);
hf->dwCrc32 = crc32(hf->dwCrc32, hf->pbFileSector, dwBytesInSector);
@@ -106,7 +146,18 @@ static int WriteDataToMpqFile(
int nOutBuffer = (int)dwBytesInSector;
int nInBuffer = (int)dwBytesInSector;
assert(pbCompressed != NULL);
// If the file is compressed, allocate buffer for the compressed data.
// Note that we allocate buffer that is a bit longer than sector size,
// for case if the compression method performs a buffer overrun
if(pbCompressed == NULL)
{
pbToWrite = pbCompressed = STORM_ALLOC(BYTE, hf->dwSectorSize + 0x100);
if(pbCompressed == NULL)
{
nError = ERROR_NOT_ENOUGH_MEMORY;
break;
}
}
//
// Note that both SCompImplode and SCompCompress give original buffer,
@@ -319,11 +370,17 @@ int SFileAddFile_Init(
// flags get to this point
//
// Adjust file flags for too-small files
if(dwFileSize < 0x04)
dwFlags &= ~(MPQ_FILE_ENCRYPTED | MPQ_FILE_FIX_KEY);
if(dwFileSize < 0x20)
dwFlags &= ~(MPQ_FILE_COMPRESSED | MPQ_FILE_SECTOR_CRC);
// Sestor CRC is not allowed with single unit files
if(dwFlags & MPQ_FILE_SINGLE_UNIT)
dwFlags &= ~MPQ_FILE_SECTOR_CRC;
// Sector CRC is not allowed if the file is not compressed
if(!(dwFlags & MPQ_FILE_COMPRESSED))
dwFlags &= ~MPQ_FILE_SECTOR_CRC;
// Fix Key is not allowed if the file is not enrypted
if(!(dwFlags & MPQ_FILE_ENCRYPTED))
dwFlags &= ~MPQ_FILE_FIX_KEY;
// If the MPQ is of version 3.0 or higher, we ignore file locale.
// This is because HET and BET tables have no known support for it
@@ -371,12 +428,9 @@ int SFileAddFile_Init(
}
else
{
// If the file is marked as deleted, it's OK
if(!(pFileEntry->dwFlags & MPQ_FILE_DELETE_MARKER))
{
if((dwFlags & MPQ_FILE_REPLACEEXISTING) == 0)
nError = ERROR_ALREADY_EXISTS;
}
// If the file exists and "replace existing" is not set, fail it
if((dwFlags & MPQ_FILE_REPLACEEXISTING) == 0)
nError = ERROR_ALREADY_EXISTS;
// If the file entry already contains a file
// and it is a pseudo-name, replace it
@@ -459,7 +513,7 @@ int SFileAddFile_Write(TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD d
}
// Allocate patch info, if the data is patch
if(hf->pPatchInfo == NULL && IsPatchData(pvData, dwSize, &hf->dwPatchedFileSize))
if(hf->pPatchInfo == NULL && IsIncrementalPatchFile(pvData, dwSize, &hf->dwPatchedFileSize))
{
// Set the MPQ_FILE_PATCH_FILE flag
hf->pFileEntry->dwFlags |= MPQ_FILE_PATCH_FILE;
@@ -782,6 +836,9 @@ bool WINAPI SFileAddFileEx(
DWORD dwBytesRemaining = 0;
DWORD dwBytesToRead;
DWORD dwSectorSize = 0x1000;
DWORD dwChannels = 0;
bool bIsAdpcmCompression = false;
bool bIsFirstSector = true;
int nError = ERROR_SUCCESS;
// Check parameters
@@ -791,7 +848,7 @@ bool WINAPI SFileAddFileEx(
// Open added file
if(nError == ERROR_SUCCESS)
{
pStream = FileStream_OpenFile(szFileName, false);
pStream = FileStream_OpenFile(szFileName, STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
if(pStream == NULL)
nError = GetLastError();
}
@@ -799,7 +856,7 @@ bool WINAPI SFileAddFileEx(
// Get the file size and file time
if(nError == ERROR_SUCCESS)
{
FileStream_GetLastWriteTime(pStream, &FileTime);
FileStream_GetTime(pStream, &FileTime);
FileStream_GetSize(pStream, FileSize);
// Files bigger than 4GB cannot be added to MPQ
@@ -821,16 +878,19 @@ bool WINAPI SFileAddFileEx(
{
// When the compression for next blocks is set to default,
// we will copy the compression for the first sector
if(dwCompressionNext == 0xFFFFFFFF)
if(dwCompressionNext == MPQ_COMPRESSION_NEXT_SAME)
dwCompressionNext = dwCompression;
// If the caller wants ADPCM compression, we make sure that the first sector is not
// compressed with lossy compression
if(dwCompressionNext & (MPQ_COMPRESSION_WAVE_MONO | MPQ_COMPRESSION_WAVE_STEREO))
// If the caller wants ADPCM compression, we make sure
// that the first sector is not compressed with lossy compression
if(dwCompressionNext & (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO))
{
// The first compression must not be WAVE
if(dwCompression & (MPQ_COMPRESSION_WAVE_MONO | MPQ_COMPRESSION_WAVE_STEREO))
if(dwCompression & (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO))
dwCompression = MPQ_COMPRESSION_PKWARE;
dwCompressionNext &= ~(MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO);
bIsAdpcmCompression = true;
}
// Initiate adding file to the MPQ
@@ -853,6 +913,21 @@ bool WINAPI SFileAddFileEx(
break;
}
// If the file being added is a WAVE file, we check number of channels
if(bIsFirstSector && bIsAdpcmCompression)
{
// The file must really be a wave file, otherwise it's data corruption
if(!IsWaveFile(pbFileData, dwBytesToRead, &dwChannels))
{
nError = ERROR_BAD_FORMAT;
break;
}
// Setup the compression according to number of channels
dwCompressionNext |= (dwChannels == 1) ? MPQ_COMPRESSION_ADPCM_MONO : MPQ_COMPRESSION_ADPCM_STEREO;
bIsFirstSector = false;
}
// Add the file sectors to the MPQ
if(!SFileWriteFile(hMpqFile, pbFileData, dwBytesToRead, dwCompression))
{
@@ -920,12 +995,12 @@ bool WINAPI SFileAddWave(HANDLE hMpq, const TCHAR * szFileName, const char * szA
case MPQ_WAVE_QUALITY_MEDIUM:
// WaveCompressionLevel = 4;
dwCompression = MPQ_COMPRESSION_WAVE_STEREO | MPQ_COMPRESSION_HUFFMANN;
dwCompression = MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN;
break;
case MPQ_WAVE_QUALITY_LOW:
// WaveCompressionLevel = 2;
dwCompression = MPQ_COMPRESSION_WAVE_STEREO | MPQ_COMPRESSION_HUFFMANN;
dwCompression = MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN;
break;
}
+81 -3
View File
@@ -23,6 +23,7 @@ typedef struct _MPQ_ATTRIBUTES_HEADER
// Followed by an array of CRC32
// Followed by an array of file times
// Followed by an array of MD5
// Followed by an array of patch bits
} MPQ_ATTRIBUTES_HEADER, *PMPQ_ATTRIBUTES_HEADER;
//-----------------------------------------------------------------------------
@@ -147,6 +148,40 @@ int SAttrLoadAttributes(TMPQArchive * ha)
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);
}
}
//
// Note: Version 7.00 of StormLib saved the (attributes) incorrectly.
// Sometimes, number of entries in the (attributes) was 1 item less
@@ -174,6 +209,19 @@ int SAttrFileSaveToMpq(TMPQArchive * ha)
DWORD i;
int nError = ERROR_SUCCESS;
// Now we have to check if we need patch bits in the (attributes)
if(nError == ERROR_SUCCESS)
{
for(i = 0; i < ha->dwFileTableSize; i++)
{
if(ha->pFileTable[i].dwFlags & MPQ_FILE_PATCH_FILE)
{
ha->dwAttrFlags |= MPQ_ATTRIBUTE_PATCH_BIT;
break;
}
}
}
// If the (attributes) is not in the file table yet,
// we have to increase the final block table size
pFileEntry = GetFileEntryExact(ha, ATTRIBUTES_NAME, LANG_NEUTRAL);
@@ -210,15 +258,20 @@ int SAttrFileSaveToMpq(TMPQArchive * ha)
dwFileSize += dwFinalBlockTableSize * sizeof(ULONGLONG);
if(ha->dwAttrFlags & MPQ_ATTRIBUTE_MD5)
dwFileSize += dwFinalBlockTableSize * MD5_DIGEST_SIZE;
if(ha->dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT)
dwFileSize += ((dwFinalBlockTableSize - 1)) / 8 + 1;
}
// Determine the flags for (attributes)
if(ha->dwFileFlags2 == 0)
ha->dwFileFlags2 = GetDefaultSpecialFileFlags(ha, dwFileSize);
// Create the attributes file in the MPQ
assert(ha->dwFileFlags2 != 0);
nError = SFileAddFile_Init(ha, ATTRIBUTES_NAME,
0,
dwFileSize,
LANG_NEUTRAL,
ha->dwFileFlags2,
ha->dwFileFlags2 | MPQ_FILE_REPLACEEXISTING,
&hf);
// Write all parts of the (attributes) file
@@ -226,8 +279,9 @@ int SAttrFileSaveToMpq(TMPQArchive * ha)
{
assert(ha->dwFileTableSize == dwFinalBlockTableSize);
// Note that we don't know what the new bit (0x08) means.
AttrHeader.dwVersion = BSWAP_INT32_UNSIGNED(100);
AttrHeader.dwFlags = BSWAP_INT32_UNSIGNED(ha->dwAttrFlags);
AttrHeader.dwFlags = BSWAP_INT32_UNSIGNED((ha->dwAttrFlags & MPQ_ATTRIBUTE_ALL));
dwToWrite = sizeof(MPQ_ATTRIBUTES_HEADER);
nError = SFileAddFile_Write(hf, &AttrHeader, dwToWrite, MPQ_COMPRESSION_ZLIB);
}
@@ -283,6 +337,30 @@ int SAttrFileSaveToMpq(TMPQArchive * ha)
}
}
// Write the array of patch bits
if(nError == ERROR_SUCCESS && (ha->dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT))
{
LPBYTE pbBitArray;
DWORD dwByteSize = ((ha->dwFileTableSize - 1) / 8) + 1;
pbBitArray = STORM_ALLOC(BYTE, dwByteSize);
if(pbBitArray != NULL)
{
memset(pbBitArray, 0, dwByteSize);
for(i = 0; i < ha->dwFileTableSize; i++)
{
DWORD dwByteIndex = i / 8;
DWORD dwBitMask = 0x80 >> (i & 7);
if(ha->pFileTable[i].dwFlags & MPQ_FILE_PATCH_FILE)
pbBitArray[dwByteIndex] |= dwBitMask;
}
nError = SFileAddFile_Write(hf, pbBitArray, dwByteSize, MPQ_COMPRESSION_ZLIB);
STORM_FREE(pbBitArray);
}
}
// Finalize the file in the archive
if(hf != NULL)
{
+38 -8
View File
@@ -194,6 +194,7 @@ static int CopyMpqFileSectors(
if(!FileStream_Write(pNewStream, NULL, SectorOffsetsCopy, dwSectorOffsLen))
nError = GetLastError();
dwBytesToCopy -= dwSectorOffsLen;
dwCmpSize += dwSectorOffsLen;
}
@@ -216,10 +217,6 @@ static int CopyMpqFileSectors(
DWORD dwRawDataInSector = hf->dwSectorSize;
DWORD dwRawByteOffset = dwSector * hf->dwSectorSize;
// Last sector: If there is not enough bytes remaining in the file, cut the raw size
if(dwRawDataInSector > dwBytesToCopy)
dwRawDataInSector = dwBytesToCopy;
// Fix the raw data length if the file is compressed
if(hf->SectorOffsets != NULL)
{
@@ -227,6 +224,10 @@ static int CopyMpqFileSectors(
dwRawByteOffset = hf->SectorOffsets[dwSector];
}
// Last sector: If there is not enough bytes remaining in the file, cut the raw size
if(dwRawDataInSector > dwBytesToCopy)
dwRawDataInSector = dwBytesToCopy;
// Calculate the raw file offset of the file sector
CalculateRawSectorOffset(RawFilePos, hf, dwRawByteOffset);
@@ -263,7 +264,7 @@ static int CopyMpqFileSectors(
}
// Adjust byte counts
dwBytesToCopy -= hf->dwSectorSize;
dwBytesToCopy -= dwRawDataInSector;
dwCmpSize += dwRawDataInSector;
}
}
@@ -291,10 +292,39 @@ static int CopyMpqFileSectors(
}
// Size of the CRC block is also included in the compressed file size
dwBytesToCopy -= dwCrcLength;
dwCmpSize += dwCrcLength;
}
}
// There might be extra data beyond sector checksum table
// Sometimes, these data are even part of sector offset table
// Examples:
// 2012 - WoW\15354\locale-enGB.MPQ:DBFilesClient\SpellLevels.dbc
// 2012 - WoW\15354\locale-enGB.MPQ:Interface\AddOns\Blizzard_AuctionUI\Blizzard_AuctionUI.xml
if(nError == ERROR_SUCCESS && dwBytesToCopy != 0)
{
LPBYTE pbExtraData;
// Allocate space for the extra data
pbExtraData = STORM_ALLOC(BYTE, dwBytesToCopy);
if(pbExtraData != NULL)
{
if(!FileStream_Read(ha->pStream, NULL, pbExtraData, dwBytesToCopy))
nError = GetLastError();
if(!FileStream_Write(pNewStream, NULL, pbExtraData, dwBytesToCopy))
nError = GetLastError();
// Include these extra data in the compressed size
dwCmpSize += dwBytesToCopy;
dwBytesToCopy = 0;
STORM_FREE(pbExtraData);
}
else
nError = ERROR_NOT_ENOUGH_MEMORY;
}
// Write the MD5's of the raw file data, if needed
if(nError == ERROR_SUCCESS && ha->pHeader->dwRawChunkSize != 0)
{
@@ -469,13 +499,13 @@ bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bR
// Get the temporary file name and create it
if(nError == ERROR_SUCCESS)
{
_tcscpy(szTempFile, ha->pStream->szFileName);
_tcscpy(szTempFile, FileStream_GetFileName(ha->pStream));
if((szTemp = _tcsrchr(szTempFile, '.')) != NULL)
_tcscpy(szTemp + 1, _T("mp_"));
else
_tcscat(szTempFile, _T("_"));
pTempStream = FileStream_CreateFile(szTempFile);
pTempStream = FileStream_CreateFile(szTempFile, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
if(pTempStream == NULL)
nError = GetLastError();
}
@@ -531,7 +561,7 @@ bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bR
// If succeeded, switch the streams
if(nError == ERROR_SUCCESS)
{
if(FileStream_MoveFile(ha->pStream, pTempStream))
if(FileStream_Switch(ha->pStream, pTempStream))
pTempStream = NULL;
else
nError = ERROR_CAN_NOT_COMPLETE;
+45 -24
View File
@@ -67,18 +67,47 @@ static int WriteNakedMPQHeader(TMPQArchive * ha)
// Creates a new MPQ archive.
bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwFlags, DWORD dwMaxFileCount, HANDLE * phMpq)
{
SFILE_CREATE_MPQ CreateInfo;
// Fill the create structure
memset(&CreateInfo, 0, sizeof(SFILE_CREATE_MPQ));
CreateInfo.cbSize = sizeof(SFILE_CREATE_MPQ);
CreateInfo.dwMpqVersion = (dwFlags & MPQ_CREATE_ARCHIVE_VMASK) >> FLAGS_TO_FORMAT_SHIFT;
CreateInfo.dwStreamFlags = STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE;
CreateInfo.dwAttrFlags = (dwFlags & MPQ_CREATE_ATTRIBUTES) ? MPQ_ATTRIBUTE_ALL : 0;
CreateInfo.dwSectorSize = (CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_3) ? 0x4000 : 0x1000;
CreateInfo.dwRawChunkSize = (CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_4) ? 0x4000 : 0;
CreateInfo.dwMaxFileCount = dwMaxFileCount;
return SFileCreateArchive2(szMpqName, &CreateInfo, phMpq);
}
bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCreateInfo, HANDLE * phMpq)
{
TFileStream * pStream = NULL; // File stream
TMPQArchive * ha = NULL; // MPQ archive handle
ULONGLONG MpqPos = 0; // Position of MPQ header in the file
HANDLE hMpq = NULL;
USHORT wFormatVersion = MPQ_FORMAT_VERSION_1;
DWORD dwBlockTableSize = 0; // Initial block table size
DWORD dwHashTableSize = 0;
DWORD dwMaxFileCount;
int nError = ERROR_SUCCESS;
// Check the parameters, if they are valid
if(szMpqName == NULL || *szMpqName == 0 || phMpq == NULL)
if(szMpqName == NULL || *szMpqName == 0 || pCreateInfo == NULL || phMpq == NULL)
{
SetLastError(ERROR_INVALID_PARAMETER);
return false;
}
// Verify if all variables in SFILE_CREATE_MPQ are correct
if((pCreateInfo->cbSize == 0 || pCreateInfo->cbSize > sizeof(SFILE_CREATE_MPQ)) ||
(pCreateInfo->dwMpqVersion > MPQ_FORMAT_VERSION_4) ||
(pCreateInfo->pvUserData != NULL || pCreateInfo->cbUserData != 0) ||
(pCreateInfo->dwAttrFlags & ~MPQ_ATTRIBUTE_ALL) ||
(pCreateInfo->dwSectorSize & (pCreateInfo->dwSectorSize - 1)) ||
(pCreateInfo->dwRawChunkSize & (pCreateInfo->dwRawChunkSize - 1)) ||
(pCreateInfo->dwMaxFileCount < 4))
{
SetLastError(ERROR_INVALID_PARAMETER);
return false;
@@ -89,7 +118,7 @@ bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwFlags, DWORD dwM
// We verify if the file already exists and if it's a MPQ archive.
// If yes, we won't allow to overwrite it.
if(SFileOpenArchive(szMpqName, 0, dwFlags, &hMpq))
if(SFileOpenArchive(szMpqName, 0, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE | MPQ_OPEN_NO_ATTRIBUTES | MPQ_OPEN_NO_LISTFILE, &hMpq))
{
SFileCloseArchive(hMpq);
SetLastError(ERROR_ALREADY_EXISTS);
@@ -102,25 +131,18 @@ bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwFlags, DWORD dwM
// - If the file doesn't exist, create new empty file
//
pStream = FileStream_OpenFile(szMpqName, true);
pStream = FileStream_OpenFile(szMpqName, pCreateInfo->dwStreamFlags);
if(pStream == NULL)
{
pStream = FileStream_CreateFile(szMpqName);
pStream = FileStream_CreateFile(szMpqName, pCreateInfo->dwStreamFlags);
if(pStream == NULL)
return false;
}
// Decide what format to use
wFormatVersion = (USHORT)((dwFlags & MPQ_CREATE_ARCHIVE_VMASK) >> 16);
if(wFormatVersion > MPQ_FORMAT_VERSION_4)
{
SetLastError(ERROR_INVALID_PARAMETER);
return false;
}
// Increment the maximum amount of files to have space
// for listfile and attributes file
if(dwFlags & MPQ_CREATE_ATTRIBUTES)
dwMaxFileCount = pCreateInfo->dwMaxFileCount;
if(pCreateInfo->dwAttrFlags != 0)
dwMaxFileCount++;
dwMaxFileCount++;
@@ -150,19 +172,18 @@ bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwFlags, DWORD dwM
{
memset(ha, 0, sizeof(TMPQArchive));
ha->pStream = pStream;
ha->dwSectorSize = (wFormatVersion >= MPQ_FORMAT_VERSION_3) ? 0x4000 : 0x1000;
ha->dwSectorSize = pCreateInfo->dwSectorSize;
ha->UserDataPos = MpqPos;
ha->MpqPos = MpqPos;
ha->pHeader = (TMPQHeader *)ha->HeaderData;
ha->dwMaxFileCount = dwMaxFileCount;
ha->dwFileTableSize = 0;
ha->dwFileFlags1 = MPQ_FILE_ENCRYPTED | MPQ_FILE_COMPRESS | MPQ_FILE_REPLACEEXISTING;
ha->dwFileFlags2 = MPQ_FILE_ENCRYPTED | MPQ_FILE_COMPRESS | MPQ_FILE_REPLACEEXISTING;
ha->dwFileFlags1 = pCreateInfo->dwFileFlags1;
ha->dwFileFlags2 = pCreateInfo->dwFileFlags2;
ha->dwFlags = 0;
// Setup the attributes
if(dwFlags & MPQ_CREATE_ATTRIBUTES)
ha->dwAttrFlags = MPQ_ATTRIBUTE_CRC32 | MPQ_ATTRIBUTE_FILETIME | MPQ_ATTRIBUTE_MD5;
ha->dwAttrFlags = pCreateInfo->dwAttrFlags;
pStream = NULL;
}
@@ -174,9 +195,9 @@ bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwFlags, DWORD dwM
// Fill the MPQ header
memset(pHeader, 0, sizeof(ha->HeaderData));
pHeader->dwID = ID_MPQ;
pHeader->dwHeaderSize = MpqHeaderSizes[wFormatVersion];
pHeader->dwHeaderSize = MpqHeaderSizes[pCreateInfo->dwMpqVersion];
pHeader->dwArchiveSize = pHeader->dwHeaderSize + dwHashTableSize * sizeof(TMPQHash);
pHeader->wFormatVersion = wFormatVersion;
pHeader->wFormatVersion = (USHORT)pCreateInfo->dwMpqVersion;
pHeader->wSectorSize = GetSectorSizeShift(ha->dwSectorSize);
pHeader->dwHashTablePos = pHeader->dwHeaderSize;
pHeader->dwHashTableSize = dwHashTableSize;
@@ -185,8 +206,8 @@ bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwFlags, DWORD dwM
// For MPQs version 4 and higher, we set the size of raw data block
// for calculating MD5
if(wFormatVersion >= MPQ_FORMAT_VERSION_4)
pHeader->dwRawChunkSize = 0x4000;
if(pCreateInfo->dwMpqVersion >= MPQ_FORMAT_VERSION_4)
pHeader->dwRawChunkSize = pCreateInfo->dwRawChunkSize;
// Write the naked MPQ header
nError = WriteNakedMPQHeader(ha);
@@ -196,7 +217,7 @@ bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwFlags, DWORD dwM
}
// Create initial HET table, if the caller required an MPQ format 3.0 or newer
if(nError == ERROR_SUCCESS && wFormatVersion >= MPQ_FORMAT_VERSION_3)
if(nError == ERROR_SUCCESS && pCreateInfo->dwMpqVersion >= MPQ_FORMAT_VERSION_3)
{
ha->pHetTable = CreateHetTable(ha->dwMaxFileCount, 0x40, true);
if(ha->pHetTable == NULL)
+1 -1
View File
@@ -28,7 +28,7 @@ bool WINAPI SFileExtractFile(HANDLE hMpq, const char * szToExtract, const TCHAR
// Create the local file
if(nError == ERROR_SUCCESS)
{
pLocalFile = FileStream_CreateFile(szExtracted);
pLocalFile = FileStream_CreateFile(szExtracted, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
if(pLocalFile == NULL)
nError = GetLastError();
}
+4 -6
View File
@@ -31,7 +31,7 @@ struct TMPQSearch
DWORD dwSearchTableItems; // Number of items in the search table
DWORD dwNextIndex; // Next file index to be checked
DWORD dwFlagMask; // For checking flag mask
char * szSearchMask; // Search mask (variable length)
char szSearchMask[1]; // Search mask (variable length)
};
//-----------------------------------------------------------------------------
@@ -69,7 +69,7 @@ bool CheckWildCard(const char * szString, const char * szWildCard)
szString++;
}
// If there is '*', means zero or more chars. We have to
// If there is '*', means zero or more chars. We have to
// find the sequence after '*'
if(*szWildCard == '*')
{
@@ -337,8 +337,6 @@ static void FreeMPQSearch(TMPQSearch *& hs)
{
if(hs->pSearchTable != NULL)
STORM_FREE(hs->pSearchTable);
if(hs->szSearchMask != NULL)
free(hs->szSearchMask); // allocated with strdup
STORM_FREE(hs);
hs = NULL;
}
@@ -378,7 +376,7 @@ HANDLE WINAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_DA
if(nError == ERROR_SUCCESS)
{
memset(hs, 0, sizeof(TMPQSearch));
hs->szSearchMask = strdup(szMask);
strcpy(hs->szSearchMask, szMask);
hs->dwFlagMask = MPQ_FILE_EXISTS;
hs->ha = ha;
@@ -408,7 +406,7 @@ HANDLE WINAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_DA
FreeMPQSearch(hs);
SetLastError(nError);
}
// Return the result value
return (HANDLE)hs;
}
+5 -2
View File
@@ -343,13 +343,16 @@ int SListFileSaveToMpq(TMPQArchive * ha)
}
}
// Determine the flags for (listfile)
if(ha->dwFileFlags1 == 0)
ha->dwFileFlags1 = GetDefaultSpecialFileFlags(ha, dwFileSize);
// Create the listfile in the MPQ
assert(ha->dwFileFlags1 != 0);
nError = SFileAddFile_Init(ha, LISTFILE_NAME,
0,
dwFileSize,
LANG_NEUTRAL,
ha->dwFileFlags1,
ha->dwFileFlags1 | MPQ_FILE_REPLACEEXISTING,
&hf);
// Add all file names
if(nError == ERROR_SUCCESS)
+61 -30
View File
@@ -32,6 +32,34 @@ static bool IsAviFile(void * pvFileBegin)
return (DwordValue0 == 0x46464952 && DwordValue2 == 0x20495641 && DwordValue3 == 0x5453494C);
}
static TFileBitmap * CreateFileBitmap(TMPQArchive * ha, TMPQBitmap * pMpqBitmap, bool bFileIsComplete)
{
TFileBitmap * pBitmap;
size_t nLength;
// Calculate the length of the bitmap in blocks and in bytes
nLength = (size_t)(((ha->pHeader->ArchiveSize64 - 1) / pMpqBitmap->dwBlockSize) + 1);
nLength = (size_t)(((nLength - 1) / 8) + 1);
// Allocate the file bitmap
pBitmap = (TFileBitmap *)STORM_ALLOC(BYTE, sizeof(TFileBitmap) + nLength);
if(pBitmap != NULL)
{
// Fill the structure
pBitmap->StartOffset = ha->MpqPos;
pBitmap->EndOffset = ha->MpqPos + ha->pHeader->ArchiveSize64;
pBitmap->IsComplete = bFileIsComplete ? 1 : 0;
pBitmap->BitmapSize = (DWORD)nLength;
pBitmap->BlockSize = pMpqBitmap->dwBlockSize;
pBitmap->Reserved = 0;
// Copy the file bitmap
memcpy((pBitmap + 1), (pMpqBitmap + 1), nLength);
}
return pBitmap;
}
// This function gets the right positions of the hash table and the block table.
static int VerifyMpqTablePositions(TMPQArchive * ha, ULONGLONG FileSize)
{
@@ -91,19 +119,6 @@ static int VerifyMpqTablePositions(TMPQArchive * ha, ULONGLONG FileSize)
// SFileGetLocale and SFileSetLocale
// Set the locale for all newly opened files
DWORD WINAPI SFileGetGlobalFlags()
{
return dwGlobalFlags;
}
DWORD WINAPI SFileSetGlobalFlags(DWORD dwNewFlags)
{
DWORD dwOldFlags = dwGlobalFlags;
dwGlobalFlags = dwNewFlags;
return dwOldFlags;
}
LCID WINAPI SFileGetLocale()
{
return lcFileLocale;
@@ -145,18 +160,10 @@ bool WINAPI SFileOpenArchive(
// Open the MPQ archive file
if(nError == ERROR_SUCCESS)
{
if(!(dwFlags & MPQ_OPEN_ENCRYPTED))
{
pStream = FileStream_OpenFile(szMpqName, (dwFlags & MPQ_OPEN_READ_ONLY) ? false : true);
if(pStream == NULL)
nError = GetLastError();
}
else
{
pStream = FileStream_OpenEncrypted(szMpqName);
if(pStream == NULL)
nError = GetLastError();
}
// Initialize the stream
pStream = FileStream_OpenFile(szMpqName, (dwFlags & STREAM_OPTIONS_MASK));
if(pStream == NULL)
nError = GetLastError();
}
// Allocate the MPQhandle
@@ -175,7 +182,7 @@ bool WINAPI SFileOpenArchive(
pStream = NULL;
// Remember if the archive is open for write
if(ha->pStream->StreamFlags & (STREAM_FLAG_READ_ONLY | STREAM_FLAG_ENCRYPTED_FILE))
if(FileStream_IsReadOnly(ha->pStream))
ha->dwFlags |= MPQ_FLAG_READ_ONLY;
// Also remember if we shall check sector CRCs when reading file
@@ -276,10 +283,6 @@ bool WINAPI SFileOpenArchive(
if(dwFlags & (MPQ_OPEN_NO_LISTFILE | MPQ_OPEN_NO_ATTRIBUTES))
ha->dwFlags |= MPQ_FLAG_READ_ONLY;
// Set the default file flags for (listfile) and (attributes)
ha->dwFileFlags1 =
ha->dwFileFlags2 = MPQ_FILE_ENCRYPTED | MPQ_FILE_COMPRESS | MPQ_FILE_REPLACEEXISTING;
// Set the size of file sector
ha->dwSectorSize = (0x200 << ha->pHeader->wSectorSize);
@@ -287,6 +290,24 @@ bool WINAPI SFileOpenArchive(
nError = VerifyMpqTablePositions(ha, FileSize);
}
// Check if the MPQ has data bitmap. If yes, we can verify if the MPQ is complete
if(nError == ERROR_SUCCESS && ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_4)
{
TFileBitmap * pBitmap;
bool bFileIsComplete = true;
LoadMpqDataBitmap(ha, FileSize, &bFileIsComplete);
if(ha->pBitmap != NULL && bFileIsComplete == false)
{
// Convert the MPQ bitmap to the file bitmap
pBitmap = CreateFileBitmap(ha, ha->pBitmap, bFileIsComplete);
// Set the data bitmap into the file stream for additional checks
FileStream_SetBitmap(ha->pStream, pBitmap);
ha->dwFlags |= MPQ_FLAG_READ_ONLY;
}
}
// Read the hash table. Ignore the result, as hash table is no longer required
// Read HET table. Ignore the result, as HET table is no longer required
if(nError == ERROR_SUCCESS)
@@ -368,6 +389,16 @@ bool WINAPI SFileOpenArchive(
return (nError == ERROR_SUCCESS);
}
//-----------------------------------------------------------------------------
// SFileGetArchiveBitmap
bool WINAPI SFileGetArchiveBitmap(HANDLE hMpq, TFileBitmap * pBitmap, DWORD Length, LPDWORD LengthNeeded)
{
TMPQArchive * ha = (TMPQArchive *)hMpq;
return FileStream_GetBitmap(ha->pStream, pBitmap, Length, LengthNeeded);
}
//-----------------------------------------------------------------------------
// bool SFileFlushArchive(HANDLE hMpq)
//
+34 -17
View File
@@ -29,10 +29,10 @@ static bool OpenLocalFile(const char * szFileName, HANDLE * phFile)
for(i = 0; szFileName[i] != 0; i++)
szFileNameT[i] = szFileName[i];
szFileNameT[i] = 0;
pStream = FileStream_OpenFile(szFileNameT, false);
pStream = FileStream_OpenFile(szFileNameT, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
#else
pStream = FileStream_OpenFile(szFileName, false);
pStream = FileStream_OpenFile(szFileName, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
#endif
if(pStream != NULL)
@@ -73,7 +73,7 @@ bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, DWORD dwReserved, HAN
{
// Construct the name of the patch file
strcpy(szPatchFileName, ha->szPatchPrefix);
strcat(szPatchFileName, szFileName);
strcpy(&szPatchFileName[ha->cchPatchPrefix], szFileName);
if(SFileOpenFileEx((HANDLE)ha, szPatchFileName, SFILE_OPEN_FROM_MPQ, (HANDLE *)&hfBase))
{
// The file must be a base file, i.e. without MPQ_FILE_PATCH_FILE
@@ -102,7 +102,7 @@ bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, DWORD dwReserved, HAN
{
// Construct patch file name
strcpy(szPatchFileName, ha->szPatchPrefix);
strcat(szPatchFileName, szFileName);
strcpy(&szPatchFileName[ha->cchPatchPrefix], szFileName);
if(SFileOpenFileEx((HANDLE)ha, szPatchFileName, SFILE_OPEN_FROM_MPQ, &hPatchFile))
{
// Remember the new version
@@ -229,7 +229,11 @@ 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))
@@ -240,26 +244,39 @@ bool WINAPI SFileHasFile(HANDLE hMpq, const char * szFileName)
// Prepare the file opening
if(nError == ERROR_SUCCESS)
{
if(!IsPseudoFileName(szFileName, &dwFileIndex))
// Different processing for pseudo-names
bIsPseudoName = IsPseudoFileName(szFileName, &dwFileIndex);
// Walk through the MPQ and all patches
while(ha != NULL)
{
if(GetFileEntryLocale(ha, szFileName, lcFileLocale) == 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)
{
nError = ERROR_FILE_NOT_FOUND;
}
}
else
{
if(GetFileEntryByIndex(ha, dwFileIndex) == NULL)
{
nError = ERROR_FILE_NOT_FOUND;
strcpy(szPatchFileName, ha->szPatchPrefix);
strcat(szPatchFileName, szFileName);
szFileName = szPatchFileName;
}
}
// Not found, sorry
nError = ERROR_FILE_NOT_FOUND;
}
// Cleanup
if(nError != ERROR_SUCCESS)
SetLastError(nError);
return (nError == ERROR_SUCCESS);
SetLastError(nError);
return false;
}
+3 -3
View File
@@ -400,7 +400,7 @@ static int ApplyMpqPatch(
//-----------------------------------------------------------------------------
// Public functions (StormLib internals)
bool IsPatchData(const void * pvData, DWORD cbData, LPDWORD pdwPatchedFileSize)
bool IsIncrementalPatchFile(const void * pvData, DWORD cbData, LPDWORD pdwPatchedFileSize)
{
TPatchHeader * pPatchHeader = (TPatchHeader *)pvData;
BLIZZARD_BSDIFF40_FILE DiffFile;
@@ -520,7 +520,7 @@ bool WINAPI SFileOpenPatchArchive(
if(nError == ERROR_SUCCESS)
{
if((ha->pStream->StreamFlags & STREAM_FLAG_READ_ONLY) == 0)
if(!FileStream_IsReadOnly(ha->pStream))
nError = ERROR_ACCESS_DENIED;
}
@@ -539,7 +539,7 @@ bool WINAPI SFileOpenPatchArchive(
{
if(!SFileHasFile(hPatchMpq, PATCH_METADATA_NAME))
{
GetDefaultPatchPrefix(ha->pStream->szFileName, szPatchPrefixBuff);
GetDefaultPatchPrefix(FileStream_GetFileName(ha->pStream), szPatchPrefixBuff);
szPatchPathPrefix = szPatchPrefixBuff;
}
}
+53 -55
View File
@@ -75,13 +75,14 @@ static bool GetFilePatchChain(TMPQFile * hf, void * pvFileInfo, DWORD cbFileInfo
if(hf->pStream != NULL)
{
// Calculate the length needed
cchCharsNeeded += _tcslen(hf->pStream->szFileName) + 1;
szFileName = FileStream_GetFileName(hf->pStream);
cchCharsNeeded += _tcslen(szFileName) + 1;
cbLengthNeeded = (DWORD)(cchCharsNeeded * sizeof(TCHAR));
// If we have enough space, copy the file name
if(cbFileInfo >= cbLengthNeeded)
{
nLength = _tcslen(szFileName = hf->pStream->szFileName) + 1;
nLength = _tcslen(szFileName) + 1;
memcpy(szPatchChain, szFileName, nLength * sizeof(TCHAR));
szPatchChain += nLength;
@@ -93,7 +94,7 @@ static bool GetFilePatchChain(TMPQFile * hf, void * pvFileInfo, DWORD cbFileInfo
{
// Calculate number of characters needed
for(hfTemp = hf; hfTemp != NULL; hfTemp = hfTemp->hfPatchFile)
cchCharsNeeded += _tcslen(hfTemp->ha->pStream->szFileName) + 1;
cchCharsNeeded += _tcslen(FileStream_GetFileName(hfTemp->ha->pStream)) + 1;
cbLengthNeeded = (DWORD)(cchCharsNeeded * sizeof(TCHAR));
// If we have enough space, the copy the patch chain
@@ -101,7 +102,8 @@ static bool GetFilePatchChain(TMPQFile * hf, void * pvFileInfo, DWORD cbFileInfo
{
for(hfTemp = hf; hfTemp != NULL; hfTemp = hfTemp->hfPatchFile)
{
nLength = _tcslen(szFileName = hfTemp->ha->pStream->szFileName) + 1;
szFileName = FileStream_GetFileName(hfTemp->ha->pStream);
nLength = _tcslen(szFileName) + 1;
memcpy(szPatchChain, szFileName, nLength * sizeof(TCHAR));
szPatchChain += nLength;
}
@@ -265,15 +267,19 @@ static int ReadMpqSectors(TMPQFile * hf, LPBYTE pbBuffer, DWORD dwByteOffset, DW
int cbInSector = dwRawBytesInThisSector;
int nResult = 0;
// Is the file compressed by PKWARE Data Compression Library ?
if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE)
nResult = SCompExplode((char *)pbOutSector, &cbOutSector, (char *)pbInSector, cbInSector);
// Is the file compressed by Blizzard's multiple compression ?
if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS)
{
hf->PreviousCompression = pbInSector[0];
nResult = SCompDecompress((char *)pbOutSector, &cbOutSector, (char *)pbInSector, cbInSector);
if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2)
nResult = SCompDecompress2((char *)pbOutSector, &cbOutSector, (char *)pbInSector, cbInSector);
else
nResult = SCompDecompress((char *)pbOutSector, &cbOutSector, (char *)pbInSector, cbInSector);
}
// Is the file compressed by PKWARE Data Compression Library ?
else if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE)
{
nResult = SCompExplode((char *)pbOutSector, &cbOutSector, (char *)pbInSector, cbInSector);
}
// Did the decompression fail ?
@@ -314,7 +320,6 @@ static int ReadMpqFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos
TFileEntry * pFileEntry = hf->pFileEntry;
LPBYTE pbCompressed = NULL;
LPBYTE pbRawData = NULL;
bool bIsReallyCompressed = false;
int nError = ERROR_SUCCESS;
// If the file buffer is not allocated yet, do it.
@@ -334,13 +339,12 @@ static int ReadMpqFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos
if(hf->dwSectorOffs != 0)
{
// Is the file compressed?
if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS)
if(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED)
{
// Allocate space for compressed data
pbCompressed = STORM_ALLOC(BYTE, pFileEntry->dwCmpSize);
if(pbCompressed == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
bIsReallyCompressed = true;
pbRawData = pbCompressed;
}
@@ -359,47 +363,43 @@ static int ReadMpqFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos
BSWAP_ARRAY32_UNSIGNED(pbRawData, pFileEntry->dwCmpSize);
}
//
// In "wow-update-12694.MPQ" from Wow-Cataclysm BETA:
//
// File CmpSize FileSize Data
// -------------------------------------- ------- -------- ---------------
// esES\DBFilesClient\LightSkyBox.dbc 0xBE 0xBC Is compressed
// deDE\DBFilesClient\MountCapability.dbc 0x93 0x77 Is uncompressed
//
// Now tell me how to deal with this mess.
//
if(hf->pPatchInfo != NULL)
{
if(pbRawData[0] == 'P' && pbRawData[1] == 'T' && pbRawData[2] == 'C' && pbRawData[3] == 'H')
{
assert(pFileEntry->dwCmpSize >= hf->dwDataSize);
bIsReallyCompressed = false;
}
}
else
{
if(pFileEntry->dwCmpSize >= hf->dwDataSize)
bIsReallyCompressed = false;
}
// If the file is compressed, we have to decompress it now
if(bIsReallyCompressed)
if(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED)
{
int cbOutBuffer = (int)hf->dwDataSize;
int cbInBuffer = (int)pFileEntry->dwCmpSize;
int nResult = 0;
// Note: Single unit files compressed with IMPLODE are not supported by Blizzard
if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE)
{
if(!SCompExplode((char *)hf->pbFileSector, &cbOutBuffer, (char *)pbRawData, (int)pFileEntry->dwCmpSize))
nError = ERROR_FILE_CORRUPT;
}
//
// If the file is an incremental patch, the size of compressed data
// is determined as pFileEntry->dwCmpSize - sizeof(TPatchInfo)
//
// In "wow-update-12694.MPQ" from Wow-Cataclysm BETA:
//
// File CmprSize DcmpSize DataSize Compressed?
// -------------------------------------- ---------- -------- -------- ---------------
// esES\DBFilesClient\LightSkyBox.dbc 0xBE->0xA2 0xBC 0xBC Yes
// deDE\DBFilesClient\MountCapability.dbc 0x93->0x77 0x77 0x77 No
//
if(pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE)
cbInBuffer = cbInBuffer - sizeof(TPatchInfo);
// Is the file compressed by Blizzard's multiple compression ?
if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS)
{
if(!SCompDecompress((char *)hf->pbFileSector, &cbOutBuffer, (char *)pbRawData, (int)pFileEntry->dwCmpSize))
nError = ERROR_FILE_CORRUPT;
if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2)
nResult = SCompDecompress2((char *)hf->pbFileSector, &cbOutBuffer, (char *)pbRawData, cbInBuffer);
else
nResult = SCompDecompress((char *)hf->pbFileSector, &cbOutBuffer, (char *)pbRawData, cbInBuffer);
}
// Is the file compressed by PKWARE Data Compression Library ?
// Note: Single unit files compressed with IMPLODE are not supported by Blizzard
else if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE)
nResult = SCompExplode((char *)hf->pbFileSector, &cbOutBuffer, (char *)pbRawData, cbInBuffer);
nError = (nResult != 0) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT;
}
else
{
@@ -938,7 +938,7 @@ bool WINAPI SFileGetFileName(HANDLE hFile, char * szFileName)
if(pFileEntry != NULL && pFileEntry->szFileName != NULL)
strcpy(szFileName, pFileEntry->szFileName);
else if(hf->pStream != NULL)
CopyFileName(szFileName, hf->pStream->szFileName);
CopyFileName(szFileName, FileStream_GetFileName(hf->pStream));
}
return (nError == ERROR_SUCCESS);
}
@@ -988,8 +988,8 @@ bool WINAPI SFileGetFileInfo(
VERIFY_MPQ_HANDLE(ha);
// pvFileInfo receives the name of the archive, terminated by 0
cbLengthNeeded = (DWORD)(_tcslen(ha->pStream->szFileName) + 1) * sizeof(TCHAR);
pvSrcFileInfo = ha->pStream->szFileName;
pvSrcFileInfo = FileStream_GetFileName(ha->pStream);
cbLengthNeeded = (DWORD)(_tcslen((TCHAR *)pvSrcFileInfo) + 1) * sizeof(TCHAR);
break;
case SFILE_INFO_ARCHIVE_SIZE: // Size of the archive
@@ -1056,15 +1056,13 @@ bool WINAPI SFileGetFileInfo(
pvSrcFileInfo = &dwFileCount;
break;
case SFILE_INFO_STREAM_FLAGS: // Stream flags for the MPQ. See STREAM_FLAG_XXX
VERIFY_MPQ_HANDLE(ha);
cbLengthNeeded = sizeof(DWORD);
pvSrcFileInfo = &ha->pStream->StreamFlags;
case SFILE_INFO_STREAM_FLAGS: // Deprecated
nError = ERROR_INVALID_PARAMETER;
break;
case SFILE_INFO_IS_READ_ONLY:
VERIFY_MPQ_HANDLE(ha);
dwIsReadOnly = ((ha->pStream->StreamFlags & STREAM_FLAG_READ_ONLY) || (ha->dwFlags & MPQ_FLAG_READ_ONLY));
dwIsReadOnly = (FileStream_IsReadOnly(ha->pStream) || (ha->dwFlags & MPQ_FLAG_READ_ONLY));
cbLengthNeeded = sizeof(DWORD);
pvSrcFileInfo = &dwIsReadOnly;
break;
+38 -43
View File
@@ -169,9 +169,7 @@ static void CalculateArchiveRange(
TMPQArchive * ha,
PMPQ_SIGNATURE_INFO pSI)
{
TMPQHeader * pHeader = ha->pHeader;
ULONGLONG TempPos = 0;
ULONGLONG MaxPos;
char szMapHeader[0x200];
// Get the MPQ begin
@@ -188,31 +186,9 @@ static void CalculateArchiveRange(
}
}
// Get the MPQ data end. The end is calculated as the biggest
// value of (end of the last file), (end of block table),
// (end of ext block table), (end of hash table)
FindFreeMpqSpace(ha, &MaxPos);
// Check if hash table is beyond
TempPos = ha->MpqPos + MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos) + pHeader->HashTableSize64;
if(TempPos > MaxPos)
MaxPos = TempPos;
// Check if block table is beyond
TempPos = ha->MpqPos + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos) + pHeader->BlockTableSize64;
if(TempPos > MaxPos)
MaxPos = TempPos;
// Check if ext block table is beyond
if(pHeader->HiBlockTablePos64 != 0)
{
TempPos = ha->MpqPos + pHeader->HiBlockTablePos64 + pHeader->HiBlockTableSize64;
if(TempPos > MaxPos)
MaxPos = TempPos;
}
// Give the end
pSI->EndMpqData = MaxPos;
// Get the MPQ data end. This is stored in our MPQ header,
// and it's been already prepared by SFileOpenArchive,
pSI->EndMpqData = ha->MpqPos + ha->pHeader->ArchiveSize64;
// Get the size of the entire file
FileStream_GetSize(ha->pStream, pSI->EndOfFile);
@@ -421,7 +397,7 @@ static bool CalculateMpqHashSha1(
sha1_done(&sha1_state_temp, sha1_tail0);
memcpy(&sha1_state_temp, &sha1_state, sizeof(hash_state));
GetPlainAnsiFileName(ha->pStream->szFileName, szPlainName);
GetPlainAnsiFileName(FileStream_GetFileName(ha->pStream), szPlainName);
AddTailToSha1(&sha1_state_temp, szPlainName);
sha1_done(&sha1_state_temp, sha1_tail1);
@@ -449,11 +425,13 @@ static int VerifyRawMpqData(
DWORD dwMD5Size;
int nError = ERROR_SUCCESS;
// Don't verify zero-sized blocks
if(dwDataSize == 0)
return ERROR_SUCCESS;
// Get the number of data chunks to calculate MD5
assert(dwChunkSize != 0);
dwChunkCount = dwDataSize / dwChunkSize;
if(dwDataSize % dwChunkSize)
dwChunkCount++;
dwChunkCount = ((dwDataSize - 1) / dwChunkSize) + 1;
dwMD5Size = dwChunkCount * MD5_DIGEST_SIZE;
// Allocate space for data chunk and for the MD5 array
@@ -659,6 +637,35 @@ static DWORD VerifyFile(
if(SFileIsPatchedArchive(hMpq))
dwSearchScope = SFILE_OPEN_PATCHED_FILE;
// If we have to verify raw data MD5, do it before file open
if(dwFlags & SFILE_VERIFY_RAW_MD5)
{
TMPQArchive * ha = (TMPQArchive *)hMpq;
// Parse the base MPQ and all patches
while(ha != NULL)
{
// Does the archive have support for raw MD5?
if(ha->pHeader->dwRawChunkSize != 0)
{
// The file has raw MD5 if the archive supports it
dwVerifyResult |= VERIFY_FILE_HAS_RAW_MD5;
// Find file entry for the file
pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale);
if(pFileEntry != NULL)
{
// If the file's raw MD5 doesn't match, don't bother with more checks
if(VerifyRawMpqData(ha, pFileEntry->ByteOffset, pFileEntry->dwCmpSize) != ERROR_SUCCESS)
return dwVerifyResult | VERIFY_FILE_RAW_MD5_ERROR;
}
}
// Move to the next patch
ha = ha->haPatch;
}
}
// Attempt to open the file
if(SFileOpenFileEx(hMpq, szFileName, dwSearchScope, &hFile))
{
@@ -671,18 +678,6 @@ static DWORD VerifyFile(
md5_init(&md5_state);
dwCrc32 = crc32(0, Z_NULL, 0);
// If we have to verify raw data MD5, do it
if(dwFlags & SFILE_VERIFY_RAW_MD5)
{
if(hf->ha->pHeader->dwRawChunkSize != 0)
{
// Note: we have to open the file from the MPQ where it was open from
if(VerifyRawMpqData(hf->ha, pFileEntry->ByteOffset, pFileEntry->dwCmpSize) != ERROR_SUCCESS)
dwVerifyResult |= VERIFY_FILE_RAW_MD5_ERROR;
dwVerifyResult |= VERIFY_FILE_HAS_RAW_MD5;
}
}
// Also turn on sector checksum verification
if(dwFlags & SFILE_VERIFY_SECTOR_CRC)
hf->bCheckSectorCRCs = true;
+8 -4
View File
@@ -109,7 +109,6 @@ __inline void DebugFree(void * ptr)
//-----------------------------------------------------------------------------
// StormLib internal global variables
extern DWORD dwGlobalFlags; // Global StormLib flags
extern LCID lcFileLocale; // Preferred file locale
//-----------------------------------------------------------------------------
@@ -122,7 +121,7 @@ extern LCID lcFileLocale; // Preferred file locale
DWORD HashString(const char * szFileName, DWORD dwHashType);
void InitializeMpqCryptography();
void InitializeMpqCryptography();
DWORD GetHashTableSizeForFileCount(DWORD dwFileCount);
@@ -131,6 +130,8 @@ ULONGLONG HashStringJenkins(const char * szFileName);
int ConvertMpqHeaderToFormat4(TMPQArchive * ha, ULONGLONG FileSize, DWORD dwFlags);
DWORD GetDefaultSpecialFileFlags(TMPQArchive * ha, DWORD dwFileSize);
void EncryptMpqBlock(void * pvFileBlock, DWORD dwLength, DWORD dwKey);
void DecryptMpqBlock(void * pvFileBlock, DWORD dwLength, DWORD dwKey);
@@ -158,7 +159,10 @@ DWORD AllocateHetEntry(TMPQArchive * ha, TFileEntry * pFileEntry);
void FindFreeMpqSpace(TMPQArchive * ha, ULONGLONG * pFreeSpacePos);
// Functions that load the HET abd BET tables
// Functions that loads and verifies MPQ data bitmap
int LoadMpqDataBitmap(TMPQArchive * ha, ULONGLONG FileSize, bool * pbFileIsComplete);
// Functions that load the HET and BET tables
int CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize);
int LoadAnyHashTable(TMPQArchive * ha);
int BuildFileTable(TMPQArchive * ha, ULONGLONG FileSize);
@@ -206,7 +210,7 @@ int WriteMemDataMD5(TFileStream * pStream, ULONGLONG RawDataOffs, void * pvRawD
int WriteMpqDataMD5(TFileStream * pStream, ULONGLONG RawDataOffs, DWORD dwRawDataSize, DWORD dwChunkSize);
void FreeMPQFile(TMPQFile *& hf);
bool IsPatchData(const void * pvData, DWORD cbData, LPDWORD pdwPatchedFileSize);
bool IsIncrementalPatchFile(const void * pvData, DWORD cbData, LPDWORD pdwPatchedFileSize);
int PatchFileData(TMPQFile * hf);
void FreeMPQArchive(TMPQArchive *& ha);
+315 -306
View File
@@ -64,15 +64,16 @@
/* 20.09.10 8.00 Lad MPQs v 4, HET and BET tables */
/* 07.01.11 8.01 Lad Write support for MPQs v 3 and 4 */
/* 15.09.11 8.04 Lad Bug fixes, testing for Diablo III MPQs */
/* 26.04.12 8.10 Lad Support for data map, added SFileGetArchiveBitmap */
/*****************************************************************************/
#ifndef __STORMLIB_H__
#define __STORMLIB_H__
#ifdef _MSC_VER
#pragma warning(disable:4668) // 'XXX' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif'
#pragma warning(disable:4668) // 'XXX' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif'
#pragma warning(disable:4820) // 'XXX' : '2' bytes padding added after data member 'XXX::yyy'
#endif
#endif
#include "StormPort.h"
@@ -85,118 +86,111 @@ extern "C" {
//
// The library type is encoded in the library name as the following
// StormLibXYZ.lib
//
//
// X - D for Debug version, R for Release version
// Y - A for ANSI version, U for Unicode version
// Z - S for static-linked CRT library, D for multithreaded DLL CRT library
//
#if 0
#if defined(_MSC_VER) && !defined(__STORMLIB_SELF__)
#ifdef _DEBUG // DEBUG VERSIONS
#ifndef _UNICODE
#ifdef _DLL
#pragma comment(lib, "StormLibDAD.lib") // Debug Ansi CRT-DLL version
#else
#pragma comment(lib, "StormLibDAS.lib") // Debug Ansi CRT-LIB version
#if 0 && defined(_MSC_VER) && !defined(__STORMLIB_SELF__)
#ifdef _DEBUG // DEBUG VERSIONS
#ifndef _UNICODE
#ifdef _DLL
#pragma comment(lib, "StormLibDAD.lib") // Debug Ansi CRT-DLL version
#else
#pragma comment(lib, "StormLibDAS.lib") // Debug Ansi CRT-LIB version
#endif
#else
#ifdef _DLL
#pragma comment(lib, "StormLibDUD.lib") // Debug Unicode CRT-DLL version
#else
#pragma comment(lib, "StormLibDUS.lib") // Debug Unicode CRT-LIB version
#ifdef _DLL
#pragma comment(lib, "StormLibDUD.lib") // Debug Unicode CRT-DLL version
#else
#pragma comment(lib, "StormLibDUS.lib") // Debug Unicode CRT-LIB version
#endif
#endif
#else // RELEASE VERSIONS
#ifndef _UNICODE
#else // RELEASE VERSIONS
#ifndef _UNICODE
#ifdef _DLL
#pragma comment(lib, "StormLibRAD.lib") // Release Ansi CRT-DLL version
#else
#pragma comment(lib, "StormLibRAS.lib") // Release Ansi CRT-LIB version
#pragma comment(lib, "StormLibRAD.lib") // Release Ansi CRT-DLL version
#else
#pragma comment(lib, "StormLibRAS.lib") // Release Ansi CRT-LIB version
#endif
#else
#ifdef _DLL
#pragma comment(lib, "StormLibRUD.lib") // Release Unicode CRT-DLL version
#else
#pragma comment(lib, "StormLibRUS.lib") // Release Unicode CRT-LIB version
#pragma comment(lib, "StormLibRUD.lib") // Release Unicode CRT-DLL version
#else
#pragma comment(lib, "StormLibRUS.lib") // Release Unicode CRT-LIB version
#endif
#endif
#endif
#endif
#endif
//-----------------------------------------------------------------------------
// Defines
#define ID_MPQ 0x1A51504D // MPQ archive header ID ('MPQ\x1A')
#define ID_MPQ_USERDATA 0x1B51504D // MPQ userdata entry ('MPQ\x1B')
#define ID_MPQ 0x1A51504D // MPQ archive header ID ('MPQ\x1A')
#define ID_MPQ_USERDATA 0x1B51504D // MPQ userdata entry ('MPQ\x1B')
#define ERROR_AVI_FILE 10000 // No MPQ file, but AVI file.
#define ERROR_UNKNOWN_FILE_KEY 10001 // Returned by SFileReadFile when can't find file key
#define ERROR_CHECKSUM_ERROR 10002 // Returned by SFileReadFile when sector CRC doesn't match
#define ERROR_INTERNAL_FILE 10003 // The given operation is not allowed on internal file
#define ERROR_BASE_FILE_MISSING 10004 // The file is present as incremental patch file, but base file is missing
#define ERROR_MARKED_FOR_DELETE 10005 // The file was marked as "deleted" in the MPQ
#define ERROR_AVI_FILE 10000 // No MPQ file, but AVI file.
#define ERROR_UNKNOWN_FILE_KEY 10001 // Returned by SFileReadFile when can't find file key
#define ERROR_CHECKSUM_ERROR 10002 // Returned by SFileReadFile when sector CRC doesn't match
#define ERROR_INTERNAL_FILE 10003 // The given operation is not allowed on internal file
#define ERROR_BASE_FILE_MISSING 10004 // The file is present as incremental patch file, but base file is missing
#define ERROR_MARKED_FOR_DELETE 10005 // The file was marked as "deleted" in the MPQ
// Values for SFileCreateArchive
#define HASH_TABLE_SIZE_MIN 0x00000004 // Minimum acceptable hash table size
#define HASH_TABLE_SIZE_DEFAULT 0x00001000 // Default hash table size for empty MPQs
#define HASH_TABLE_SIZE_MAX 0x00080000 // Maximum acceptable hash table size
#define HASH_TABLE_SIZE_MIN 0x00000004 // Minimum acceptable hash table size
#define HASH_TABLE_SIZE_DEFAULT 0x00001000 // Default hash table size for empty MPQs
#define HASH_TABLE_SIZE_MAX 0x00080000 // Maximum acceptable hash table size
#define HASH_ENTRY_DELETED 0xFFFFFFFE // Block index for deleted entry in the hash table
#define HASH_ENTRY_FREE 0xFFFFFFFF // Block index for free entry in the hash table
#define HASH_ENTRY_DELETED 0xFFFFFFFE // Block index for deleted entry in the hash table
#define HASH_ENTRY_FREE 0xFFFFFFFF // Block index for free entry in the hash table
#define HET_ENTRY_DELETED 0x80 // HET hash value for a deleted entry
#define HET_ENTRY_FREE 0x00 // HET hash value for free entry
#define HET_ENTRY_DELETED 0x80 // HET hash value for a deleted entry
#define HET_ENTRY_FREE 0x00 // HET hash value for free entry
#define HASH_STATE_SIZE 0x60 // Size of LibTomCrypt's hash_state structure
#define HASH_STATE_SIZE 0x60 // Size of LibTomCrypt's hash_state structure
#define MPQ_PATCH_PREFIX_LEN 0x20 // Maximum length of the patch prefix
// Values for TFileStream::Flags
#define STREAM_FLAG_READ_ONLY 0x01 // The stream is read only
#define STREAM_FLAG_PART_FILE 0x02 // The stream is a PART file.
#define STREAM_FLAG_ENCRYPTED_FILE 0x04 // The stream is an encrypted MPQ (MPQE).
#define MPQ_PATCH_PREFIX_LEN 0x20 // Maximum length of the patch prefix
// Values for SFileOpenArchive
#define SFILE_OPEN_HARD_DISK_FILE 2 // Open the archive on HDD
#define SFILE_OPEN_CDROM_FILE 3 // Open the archive only if it is on CDROM
#define SFILE_OPEN_HARD_DISK_FILE 2 // Open the archive on HDD
#define SFILE_OPEN_CDROM_FILE 3 // Open the archive only if it is on CDROM
// Values for SFileOpenFile
#define SFILE_OPEN_FROM_MPQ 0x00000000 // Open the file from the MPQ archive
#define SFILE_OPEN_PATCHED_FILE 0x00000001 // Open the file from the MPQ archive
#define SFILE_OPEN_ANY_LOCALE 0xFFFFFFFE // Reserved for StormLib internal use
#define SFILE_OPEN_LOCAL_FILE 0xFFFFFFFF // Open a local file
#define SFILE_OPEN_FROM_MPQ 0x00000000 // Open the file from the MPQ archive
#define SFILE_OPEN_PATCHED_FILE 0x00000001 // Open the file from the MPQ archive
#define SFILE_OPEN_ANY_LOCALE 0xFFFFFFFE // Reserved for StormLib internal use
#define SFILE_OPEN_LOCAL_FILE 0xFFFFFFFF // Open a local file
// Flags for TMPQArchive::dwFlags
#define MPQ_FLAG_READ_ONLY 0x00000001 // If set, the MPQ has been open for read-only access
#define MPQ_FLAG_CHANGED 0x00000002 // If set, the MPQ tables have been changed
#define MPQ_FLAG_PROTECTED 0x00000004 // Set on protected MPQs (like W3M maps)
#define MPQ_FLAG_CHECK_SECTOR_CRC 0x0000008 // Checking sector CRC when reading files
#define MPQ_FLAG_NEED_FIX_SIZE 0x00000010 // Used during opening the archive
#define MPQ_FLAG_INV_LISTFILE 0x00000020 // If set, it means that the (listfile) has been invalidated
#define MPQ_FLAG_INV_ATTRIBUTES 0x00000040 // If set, it means that the (attributes) has been invalidated
#define MPQ_FLAG_READ_ONLY 0x00000001 // If set, the MPQ has been open for read-only access
#define MPQ_FLAG_CHANGED 0x00000002 // If set, the MPQ tables have been changed
#define MPQ_FLAG_PROTECTED 0x00000004 // Set on protected MPQs (like W3M maps)
#define MPQ_FLAG_CHECK_SECTOR_CRC 0x00000008 // Checking sector CRC when reading files
#define MPQ_FLAG_NEED_FIX_SIZE 0x00000010 // Used during opening the archive
#define MPQ_FLAG_INV_LISTFILE 0x00000020 // If set, it means that the (listfile) has been invalidated
#define MPQ_FLAG_INV_ATTRIBUTES 0x00000040 // If set, it means that the (attributes) has been invalidated
// Return value for SFilGetFileSize and SFileSetFilePointer
#define SFILE_INVALID_SIZE 0xFFFFFFFF
#define SFILE_INVALID_POS 0xFFFFFFFF
#define SFILE_INVALID_ATTRIBUTES 0xFFFFFFFF
// Return value for SFileGetFileSize and SFileSetFilePointer
#define SFILE_INVALID_SIZE 0xFFFFFFFF
#define SFILE_INVALID_POS 0xFFFFFFFF
#define SFILE_INVALID_ATTRIBUTES 0xFFFFFFFF
// Flags for SFileAddFile
#define MPQ_FILE_IMPLODE 0x00000100 // Implode method (By PKWARE Data Compression Library)
#define MPQ_FILE_COMPRESS 0x00000200 // Compress methods (By multiple methods)
#define MPQ_FILE_COMPRESSED 0x0000FF00 // File is compressed
#define MPQ_FILE_ENCRYPTED 0x00010000 // Indicates whether file is encrypted
#define MPQ_FILE_FIX_KEY 0x00020000 // File decryption key has to be fixed
#define MPQ_FILE_PATCH_FILE 0x00100000 // The file is a patch file. Raw file data begin with TPatchInfo structure
#define MPQ_FILE_SINGLE_UNIT 0x01000000 // File is stored as a single unit, rather than split into sectors (Thx, Quantam)
#define MPQ_FILE_DELETE_MARKER 0x02000000 // File is a deletion marker, indicating that the file no longer exists.
#define MPQ_FILE_SECTOR_CRC 0x04000000 // File has checksums for each sector.
// Ignored if file is not compressed or imploded.
#define MPQ_FILE_EXISTS 0x80000000 // Set if file exists, reset when the file was deleted
#define MPQ_FILE_REPLACEEXISTING 0x80000000 // Replace when the file exist (SFileAddFile)
#define MPQ_FILE_IMPLODE 0x00000100 // Implode method (By PKWARE Data Compression Library)
#define MPQ_FILE_COMPRESS 0x00000200 // Compress methods (By multiple methods)
#define MPQ_FILE_COMPRESSED 0x0000FF00 // File is compressed
#define MPQ_FILE_ENCRYPTED 0x00010000 // Indicates whether file is encrypted
#define MPQ_FILE_FIX_KEY 0x00020000 // File decryption key has to be fixed
#define MPQ_FILE_PATCH_FILE 0x00100000 // The file is a patch file. Raw file data begin with TPatchInfo structure
#define MPQ_FILE_SINGLE_UNIT 0x01000000 // File is stored as a single unit, rather than split into sectors (Thx, Quantam)
#define MPQ_FILE_DELETE_MARKER 0x02000000 // File is a deletion marker. Used in MPQ patches, indicating that the file no longer exists.
#define MPQ_FILE_SECTOR_CRC 0x04000000 // File has checksums for each sector.
// Ignored if file is not compressed or imploded.
#define MPQ_FILE_EXISTS 0x80000000 // Set if file exists, reset when the file was deleted
#define MPQ_FILE_REPLACEEXISTING 0x80000000 // Replace when the file exist (SFileAddFile)
#define MPQ_FILE_VALID_FLAGS (MPQ_FILE_IMPLODE | \
MPQ_FILE_COMPRESS | \
@@ -209,211 +203,180 @@ extern "C" {
MPQ_FILE_EXISTS)
// Compression types for multiple compressions
#define MPQ_COMPRESSION_HUFFMANN 0x01 // Huffmann compression (used on WAVE files only)
#define MPQ_COMPRESSION_ZLIB 0x02 // ZLIB compression
#define MPQ_COMPRESSION_PKWARE 0x08 // PKWARE DCL compression
#define MPQ_COMPRESSION_BZIP2 0x10 // BZIP2 compression (added in Warcraft III)
#define MPQ_COMPRESSION_SPARSE 0x20 // Sparse compression (added in Starcraft 2)
#define MPQ_COMPRESSION_ADPCM_MONO 0x40 // IMA ADPCM compression (mono)
#define MPQ_COMPRESSION_ADPCM_STEREO 0x80 // IMA ADPCM compression (stereo)
// For backward compatibility
#define MPQ_COMPRESSION_WAVE_MONO 0x40 // IMA ADPCM compression (mono)
#define MPQ_COMPRESSION_WAVE_STEREO 0x80 // IMA ADPCM compression (stereo)
// LZMA compression. Added in Starcraft 2. This value is NOT a combination of flags.
#define MPQ_COMPRESSION_LZMA 0x12
#define MPQ_COMPRESSION_HUFFMANN 0x01 // Huffmann compression (used on WAVE files only)
#define MPQ_COMPRESSION_ZLIB 0x02 // ZLIB compression
#define MPQ_COMPRESSION_PKWARE 0x08 // PKWARE DCL compression
#define MPQ_COMPRESSION_BZIP2 0x10 // BZIP2 compression (added in Warcraft III)
#define MPQ_COMPRESSION_SPARSE 0x20 // Sparse compression (added in Starcraft 2)
#define MPQ_COMPRESSION_ADPCM_MONO 0x40 // IMA ADPCM compression (mono)
#define MPQ_COMPRESSION_ADPCM_STEREO 0x80 // IMA ADPCM compression (stereo)
#define MPQ_COMPRESSION_LZMA 0x12 // LZMA compression. Added in Starcraft 2. This value is NOT a combination of flags.
#define MPQ_COMPRESSION_NEXT_SAME 0xFFFFFFFF // Same compression
// Constants for SFileAddWave
#define MPQ_WAVE_QUALITY_HIGH 0 // Best quality, the worst compression
#define MPQ_WAVE_QUALITY_MEDIUM 1 // Medium quality, medium compression
#define MPQ_WAVE_QUALITY_LOW 2 // Low quality, the best compression
#define MPQ_WAVE_QUALITY_HIGH 0 // Best quality, the worst compression
#define MPQ_WAVE_QUALITY_MEDIUM 1 // Medium quality, medium compression
#define MPQ_WAVE_QUALITY_LOW 2 // Low quality, the best compression
// Signatures for HET and BET table
#define HET_TABLE_SIGNATURE 0x1A544548 // 'HET\x1a'
#define BET_TABLE_SIGNATURE 0x1A544542 // 'BET\x1a'
#define HET_TABLE_SIGNATURE 0x1A544548 // 'HET\x1a'
#define BET_TABLE_SIGNATURE 0x1A544542 // 'BET\x1a'
// Decryption keys for MPQ tables
#define MPQ_KEY_HASH_TABLE 0xC3AF3770 // Obtained by HashString("(hash table)", MPQ_HASH_FILE_KEY)
#define MPQ_KEY_BLOCK_TABLE 0xEC83B3A3 // Obtained by HashString("(block table)", MPQ_HASH_FILE_KEY)
#define MPQ_KEY_HASH_TABLE 0xC3AF3770 // Obtained by HashString("(hash table)", MPQ_HASH_FILE_KEY)
#define MPQ_KEY_BLOCK_TABLE 0xEC83B3A3 // Obtained by HashString("(block table)", MPQ_HASH_FILE_KEY)
// Block map defines
#define MPQ_DATA_BITMAP_SIGNATURE 0x33767470 // Signature of the MPQ data bitmap ('ptv3')
// Constants for SFileGetFileInfo
#define SFILE_INFO_ARCHIVE_NAME 1 // MPQ size (value from header)
#define SFILE_INFO_ARCHIVE_SIZE 2 // MPQ size (value from header)
#define SFILE_INFO_MAX_FILE_COUNT 3 // Max number of files in the MPQ
#define SFILE_INFO_HASH_TABLE_SIZE 4 // Size of hash table, in entries
#define SFILE_INFO_BLOCK_TABLE_SIZE 5 // Number of entries in the block table
#define SFILE_INFO_SECTOR_SIZE 6 // Size of file sector (in bytes)
#define SFILE_INFO_HASH_TABLE 7 // Pointer to Hash table (TMPQHash *)
#define SFILE_INFO_BLOCK_TABLE 8 // Pointer to Block Table (TMPQBlock *)
#define SFILE_INFO_NUM_FILES 9 // Real number of files within archive
#define SFILE_INFO_STREAM_FLAGS 10 // Stream flags for the MPQ. See STREAM_FLAG_XXX
#define SFILE_INFO_IS_READ_ONLY 11 // TRUE of the MPQ was open as read only
//------
#define SFILE_INFO_HASH_INDEX 100 // Hash index of file in MPQ
#define SFILE_INFO_CODENAME1 101 // The first codename of the file
#define SFILE_INFO_CODENAME2 102 // The second codename of the file
#define SFILE_INFO_LOCALEID 103 // Locale ID of file in MPQ
#define SFILE_INFO_BLOCKINDEX 104 // Index to Block Table
#define SFILE_INFO_FILE_SIZE 105 // Original file size (from the block table)
#define SFILE_INFO_COMPRESSED_SIZE 106 // Compressed file size (from the block table)
#define SFILE_INFO_FLAGS 107 // File flags
#define SFILE_INFO_POSITION 108 // File position within archive
#define SFILE_INFO_KEY 109 // File decryption key
#define SFILE_INFO_KEY_UNFIXED 110 // Decryption key not fixed to file pos and size
#define SFILE_INFO_FILETIME 111 // TMPQFileTime
#define SFILE_INFO_PATCH_CHAIN 112 // Chain of patches
#define SFILE_INFO_ARCHIVE_NAME 1 // MPQ size (value from header)
#define SFILE_INFO_ARCHIVE_SIZE 2 // MPQ size (value from header)
#define SFILE_INFO_MAX_FILE_COUNT 3 // Max number of files in the MPQ
#define SFILE_INFO_HASH_TABLE_SIZE 4 // Size of hash table, in entries
#define SFILE_INFO_BLOCK_TABLE_SIZE 5 // Number of entries in the block table
#define SFILE_INFO_SECTOR_SIZE 6 // Size of file sector (in bytes)
#define SFILE_INFO_HASH_TABLE 7 // Pointer to Hash table (TMPQHash *)
#define SFILE_INFO_BLOCK_TABLE 8 // Pointer to Block Table (TMPQBlock *)
#define SFILE_INFO_NUM_FILES 9 // Real number of files within archive
#define SFILE_INFO_STREAM_FLAGS 10 // Stream flags for the MPQ. See STREAM_FLAG_XXX
#define SFILE_INFO_IS_READ_ONLY 11 // TRUE of the MPQ was open as read only
//------
#define SFILE_INFO_HASH_INDEX 100 // Hash index of file in MPQ
#define SFILE_INFO_CODENAME1 101 // The first codename of the file
#define SFILE_INFO_CODENAME2 102 // The second codename of the file
#define SFILE_INFO_LOCALEID 103 // Locale ID of file in MPQ
#define SFILE_INFO_BLOCKINDEX 104 // Index to Block Table
#define SFILE_INFO_FILE_SIZE 105 // Original file size (from the block table)
#define SFILE_INFO_COMPRESSED_SIZE 106 // Compressed file size (from the block table)
#define SFILE_INFO_FLAGS 107 // File flags
#define SFILE_INFO_POSITION 108 // File position within archive
#define SFILE_INFO_KEY 109 // File decryption key
#define SFILE_INFO_KEY_UNFIXED 110 // Decryption key not fixed to file pos and size
#define SFILE_INFO_FILETIME 111 // TMPQFileTime
#define SFILE_INFO_PATCH_CHAIN 112 // Chain of patches
#define LISTFILE_NAME "(listfile)" // Name of internal listfile
#define SIGNATURE_NAME "(signature)" // Name of internal signature
#define ATTRIBUTES_NAME "(attributes)" // Name of internal attributes file
#define LISTFILE_NAME "(listfile)" // Name of internal listfile
#define SIGNATURE_NAME "(signature)" // Name of internal signature
#define ATTRIBUTES_NAME "(attributes)" // Name of internal attributes file
#define PATCH_METADATA_NAME "(patch_metadata)"
#define STORMLIB_VERSION 0x0804 // Current version of StormLib (8.04)
#define STORMLIB_VERSION_STRING "8.04"
#define STORMLIB_VERSION 0x080A // Current version of StormLib (8.10)
#define STORMLIB_VERSION_STRING "8.10"
#define MPQ_FORMAT_VERSION_1 0 // Up to The Burning Crusade
#define MPQ_FORMAT_VERSION_2 1 // The Burning Crusade and newer
#define MPQ_FORMAT_VERSION_3 2 // WoW Cataclysm Beta
#define MPQ_FORMAT_VERSION_4 3 // WoW Cataclysm and newer
#define MPQ_FORMAT_VERSION_1 0 // Up to The Burning Crusade
#define MPQ_FORMAT_VERSION_2 1 // The Burning Crusade and newer
#define MPQ_FORMAT_VERSION_3 2 // WoW Cataclysm Beta
#define MPQ_FORMAT_VERSION_4 3 // WoW Cataclysm and newer
// Flags for MPQ attributes
#define MPQ_ATTRIBUTE_CRC32 0x00000001 // The "(attributes)" contains CRC32 for each file
#define MPQ_ATTRIBUTE_FILETIME 0x00000002 // The "(attributes)" contains file time for each file
#define MPQ_ATTRIBUTE_MD5 0x00000004 // The "(attributes)" contains MD5 for each file
#define MPQ_ATTRIBUTE_ALL 0x00000007 // Summary mask
#define MPQ_ATTRIBUTE_CRC32 0x00000001 // The "(attributes)" contains CRC32 for each file
#define MPQ_ATTRIBUTE_FILETIME 0x00000002 // The "(attributes)" contains file time for each file
#define MPQ_ATTRIBUTE_MD5 0x00000004 // The "(attributes)" contains MD5 for each file
#define MPQ_ATTRIBUTE_PATCH_BIT 0x00000008 // The "(attributes)" contains a patch bit for each file
#define MPQ_ATTRIBUTE_ALL 0x0000000F // Summary mask
#define MPQ_ATTRIBUTES_V1 100 // (attributes) format version 1.00
#define MPQ_ATTRIBUTES_V1 100 // (attributes) format version 1.00
// Flags for SFileOpenArchive
#define MPQ_OPEN_NO_LISTFILE 0x0010 // Don't load the internal listfile
#define MPQ_OPEN_NO_ATTRIBUTES 0x0020 // Don't open the attributes
#define MPQ_OPEN_FORCE_MPQ_V1 0x0040 // Always open the archive as MPQ v 1.00, ignore the "wFormatVersion" variable in the header
#define MPQ_OPEN_CHECK_SECTOR_CRC 0x0080 // On files with MPQ_FILE_SECTOR_CRC, the CRC will be checked when reading file
#define MPQ_OPEN_READ_ONLY 0x0100 // Open the archive for read-only access
#define MPQ_OPEN_ENCRYPTED 0x0200 // Opens an encrypted MPQ archive (Example: Starcraft II installation)
#define BASE_PROVIDER_FILE 0x00000000 // Base data source is a file
#define BASE_PROVIDER_MAP 0x00000001 // Base data source is memory-mapped file
#define BASE_PROVIDER_HTTP 0x00000002 // Base data source is a file on web server
#define BASE_PROVIDER_MASK 0x0000000F // Mask for base provider value
#define STREAM_PROVIDER_LINEAR 0x00000000 // Stream is linear with no offset mapping
#define STREAM_PROVIDER_PARTIAL 0x00000010 // Stream is partial file (.part)
#define STREAM_PROVIDER_ENCRYPTED 0x00000020 // Stream is an encrypted MPQ
#define STREAM_PROVIDER_MASK 0x000000F0 // Mask for stream provider value
#define STREAM_FLAG_READ_ONLY 0x00000100 // Stream is read only
#define STREAM_FLAG_WRITE_SHARE 0x00000200 // Allow write sharing when open for write
#define STREAM_FLAG_MASK 0x0000FF00 // Mask for stream flags
#define STREAM_OPTIONS_MASK 0x0000FFFF // Mask for all stream options
#define MPQ_OPEN_NO_LISTFILE 0x00010000 // Don't load the internal listfile
#define MPQ_OPEN_NO_ATTRIBUTES 0x00020000 // Don't open the attributes
#define MPQ_OPEN_FORCE_MPQ_V1 0x00040000 // Always open the archive as MPQ v 1.00, ignore the "wFormatVersion" variable in the header
#define MPQ_OPEN_CHECK_SECTOR_CRC 0x00080000 // On files with MPQ_FILE_SECTOR_CRC, the CRC will be checked when reading file
// Deprecated
#define MPQ_OPEN_READ_ONLY STREAM_FLAG_READ_ONLY
#define MPQ_OPEN_ENCRYPTED STREAM_PROVIDER_ENCRYPTED
// Flags for SFileCreateArchive
#define MPQ_CREATE_ATTRIBUTES 0x00000001 // Also add the (attributes) file
#define MPQ_CREATE_ARCHIVE_V1 0x00000000 // Creates archive of version 1 (size up to 4GB)
#define MPQ_CREATE_ARCHIVE_V2 0x00010000 // Creates archive of version 2 (larger than 4 GB)
#define MPQ_CREATE_ARCHIVE_V3 0x00020000 // Creates archive of version 3
#define MPQ_CREATE_ARCHIVE_V4 0x00030000 // Creates archive of version 4
#define MPQ_CREATE_ARCHIVE_VMASK 0x000F0000 // Mask for archive version
#define MPQ_CREATE_ATTRIBUTES 0x00100000 // Also add the (attributes) file
#define MPQ_CREATE_ARCHIVE_V1 0x00000000 // Creates archive of version 1 (size up to 4GB)
#define MPQ_CREATE_ARCHIVE_V2 0x01000000 // Creates archive of version 2 (larger than 4 GB)
#define MPQ_CREATE_ARCHIVE_V3 0x02000000 // Creates archive of version 3
#define MPQ_CREATE_ARCHIVE_V4 0x03000000 // Creates archive of version 4
#define MPQ_CREATE_ARCHIVE_VMASK 0x0F000000 // Mask for archive version
#define FLAGS_TO_FORMAT_SHIFT 24 // (MPQ_CREATE_ARCHIVE_V4 >> FLAGS_TO_FORMAT_SHIFT) => MPQ_FORMAT_VERSION_4
// Flags for SFileVerifyFile
#define SFILE_VERIFY_SECTOR_CRC 0x0001 // Verify sector checksum for the file, if available
#define SFILE_VERIFY_FILE_CRC 0x0002 // Verify file CRC, if available
#define SFILE_VERIFY_FILE_MD5 0x0004 // Verify file MD5, if available
#define SFILE_VERIFY_RAW_MD5 0x0008 // Verify raw file MD5, if available
#define SFILE_VERIFY_ALL 0x000F // Verify every checksum possible
#define SFILE_VERIFY_SECTOR_CRC 0x00000001 // Verify sector checksum for the file, if available
#define SFILE_VERIFY_FILE_CRC 0x00000002 // Verify file CRC, if available
#define SFILE_VERIFY_FILE_MD5 0x00000004 // Verify file MD5, if available
#define SFILE_VERIFY_RAW_MD5 0x00000008 // Verify raw file MD5, if available
#define SFILE_VERIFY_ALL 0x0000000F // Verify every checksum possible
// Return values for SFileVerifyFile
#define VERIFY_OPEN_ERROR 0x0001 // Failed to open the file
#define VERIFY_READ_ERROR 0x0002 // Failed to read all data from the file
#define VERIFY_FILE_HAS_SECTOR_CRC 0x0004 // File has sector CRC
#define VERIFY_FILE_SECTOR_CRC_ERROR 0x0008 // Sector CRC check failed
#define VERIFY_FILE_HAS_CHECKSUM 0x0010 // File has CRC32
#define VERIFY_FILE_CHECKSUM_ERROR 0x0020 // CRC32 check failed
#define VERIFY_FILE_HAS_MD5 0x0040 // File has data MD5
#define VERIFY_FILE_MD5_ERROR 0x0080 // MD5 check failed
#define VERIFY_FILE_HAS_RAW_MD5 0x0100 // File has raw data MD5
#define VERIFY_FILE_RAW_MD5_ERROR 0x0200 // Raw MD5 check failed
#define VERIFY_OPEN_ERROR 0x0001 // Failed to open the file
#define VERIFY_READ_ERROR 0x0002 // Failed to read all data from the file
#define VERIFY_FILE_HAS_SECTOR_CRC 0x0004 // File has sector CRC
#define VERIFY_FILE_SECTOR_CRC_ERROR 0x0008 // Sector CRC check failed
#define VERIFY_FILE_HAS_CHECKSUM 0x0010 // File has CRC32
#define VERIFY_FILE_CHECKSUM_ERROR 0x0020 // CRC32 check failed
#define VERIFY_FILE_HAS_MD5 0x0040 // File has data MD5
#define VERIFY_FILE_MD5_ERROR 0x0080 // MD5 check failed
#define VERIFY_FILE_HAS_RAW_MD5 0x0100 // File has raw data MD5
#define VERIFY_FILE_RAW_MD5_ERROR 0x0200 // Raw MD5 check failed
#define VERIFY_FILE_ERROR_MASK (VERIFY_OPEN_ERROR | VERIFY_READ_ERROR | VERIFY_FILE_SECTOR_CRC_ERROR | VERIFY_FILE_CHECKSUM_ERROR | VERIFY_FILE_MD5_ERROR | VERIFY_FILE_RAW_MD5_ERROR)
// Flags for SFileVerifyRawData (for MPQs version 4.0 or higher)
#define SFILE_VERIFY_MPQ_HEADER 0x0001 // Verify raw MPQ header
#define SFILE_VERIFY_HET_TABLE 0x0002 // Verify raw data of the HET table
#define SFILE_VERIFY_BET_TABLE 0x0003 // Verify raw data of the BET table
#define SFILE_VERIFY_HASH_TABLE 0x0004 // Verify raw data of the hash table
#define SFILE_VERIFY_BLOCK_TABLE 0x0005 // Verify raw data of the block table
#define SFILE_VERIFY_HIBLOCK_TABLE 0x0006 // Verify raw data of the hi-block table
#define SFILE_VERIFY_FILE 0x0007 // Verify raw data of a file
#define SFILE_VERIFY_MPQ_HEADER 0x0001 // Verify raw MPQ header
#define SFILE_VERIFY_HET_TABLE 0x0002 // Verify raw data of the HET table
#define SFILE_VERIFY_BET_TABLE 0x0003 // Verify raw data of the BET table
#define SFILE_VERIFY_HASH_TABLE 0x0004 // Verify raw data of the hash table
#define SFILE_VERIFY_BLOCK_TABLE 0x0005 // Verify raw data of the block table
#define SFILE_VERIFY_HIBLOCK_TABLE 0x0006 // Verify raw data of the hi-block table
#define SFILE_VERIFY_FILE 0x0007 // Verify raw data of a file
// Return values for SFileVerifyArchive
#define ERROR_NO_SIGNATURE 0 // There is no signature in the MPQ
#define ERROR_VERIFY_FAILED 1 // There was an error during verifying signature (like no memory)
#define ERROR_WEAK_SIGNATURE_OK 2 // There is a weak signature and sign check passed
#define ERROR_WEAK_SIGNATURE_ERROR 3 // There is a weak signature but sign check failed
#define ERROR_STRONG_SIGNATURE_OK 4 // There is a strong signature and sign check passed
#define ERROR_STRONG_SIGNATURE_ERROR 5 // There is a strong signature but sign check failed
#define ERROR_NO_SIGNATURE 0 // There is no signature in the MPQ
#define ERROR_VERIFY_FAILED 1 // There was an error during verifying signature (like no memory)
#define ERROR_WEAK_SIGNATURE_OK 2 // There is a weak signature and sign check passed
#define ERROR_WEAK_SIGNATURE_ERROR 3 // There is a weak signature but sign check failed
#define ERROR_STRONG_SIGNATURE_OK 4 // There is a strong signature and sign check passed
#define ERROR_STRONG_SIGNATURE_ERROR 5 // There is a strong signature but sign check failed
#ifndef MD5_DIGEST_SIZE
#define MD5_DIGEST_SIZE 0x10
#define MD5_DIGEST_SIZE 0x10
#endif
#ifndef SHA1_DIGEST_SIZE
#define SHA1_DIGEST_SIZE 0x14 // 160 bits
#define SHA1_DIGEST_SIZE 0x14 // 160 bits
#endif
#ifndef LANG_NEUTRAL
#define LANG_NEUTRAL 0x00 // Neutral locale
#define LANG_NEUTRAL 0x00 // Neutral locale
#endif
//-----------------------------------------------------------------------------
// Callback functions
// Values for compact callback
#define CCB_CHECKING_FILES 1 // Checking archive (dwParam1 = current, dwParam2 = total)
#define CCB_CHECKING_HASH_TABLE 2 // Checking hash table (dwParam1 = current, dwParam2 = total)
#define CCB_COPYING_NON_MPQ_DATA 3 // Copying non-MPQ data: No params used
#define CCB_COMPACTING_FILES 4 // Compacting archive (dwParam1 = current, dwParam2 = total)
#define CCB_CLOSING_ARCHIVE 5 // Closing archive: No params used
#define CCB_CHECKING_FILES 1 // Checking archive (dwParam1 = current, dwParam2 = total)
#define CCB_CHECKING_HASH_TABLE 2 // Checking hash table (dwParam1 = current, dwParam2 = total)
#define CCB_COPYING_NON_MPQ_DATA 3 // Copying non-MPQ data: No params used
#define CCB_COMPACTING_FILES 4 // Compacting archive (dwParam1 = current, dwParam2 = total)
#define CCB_CLOSING_ARCHIVE 5 // Closing archive: No params used
typedef void (WINAPI * SFILE_ADDFILE_CALLBACK)(void * pvUserData, DWORD dwBytesWritten, DWORD dwTotalBytes, bool bFinalCall);
typedef void (WINAPI * SFILE_COMPACT_CALLBACK)(void * pvUserData, DWORD dwWorkType, ULONGLONG BytesProcessed, ULONGLONG TotalBytes);
//-----------------------------------------------------------------------------
// Stream support - structures
struct TFileStream;
typedef bool (*STREAM_GETPOS)(
struct TFileStream * pStream, // Pointer to an open stream
ULONGLONG & ByteOffset // Pointer to store current file position
);
typedef bool (*STREAM_READ)(
struct TFileStream * pStream, // Pointer to an open stream
ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position
void * pvBuffer, // Pointer to data to be read
DWORD dwBytesToRead // Number of bytes to read from the file
);
typedef bool (*STREAM_WRITE)(
struct TFileStream * pStream, // Pointer to an open stream
ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it writes to the current position
const void * pvBuffer, // Pointer to data to be written
DWORD dwBytesToWrite // Number of bytes to read from the file
);
typedef bool (*STREAM_GETSIZE)(
struct TFileStream * pStream, // Pointer to an open stream
ULONGLONG & FileSize // Receives the file size, in bytes
);
typedef bool (*STREAM_SETSIZE)(
struct TFileStream * pStream, // Pointer to an open stream
ULONGLONG FileSize // New size for the file, in bytes
);
// Common stream structure. Can be variable length
struct TFileStream
{
ULONGLONG RawFilePos; // Current position in raw file
HANDLE hFile; // File handle. Do not use directly.
TCHAR szFileName[MAX_PATH];// Name of the file
BYTE StreamFlags; // See STREAM_FLAG_XXXX
STREAM_GETPOS StreamGetPos; // Pointer to function that returns current file position
STREAM_READ StreamRead; // Pointer to stream read function for this archive. Do not use directly.
STREAM_WRITE StreamWrite; // Pointer to stream write function for this archive. Do not use directly.
STREAM_GETSIZE StreamGetSize; // Pointer to function returning file size
STREAM_SETSIZE StreamSetSize; // Pointer to function changing file size
// Extra members may follow
};
//-----------------------------------------------------------------------------
// Structure for bit arrays used for HET and BET tables
@@ -422,8 +385,21 @@ struct TBitArray
void GetBits(unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, int nResultSize);
void SetBits(unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, int nResultSize);
DWORD NumberOfBits; // Total number of bits that are available
BYTE Elements[1]; // Array of elements (variable length)
DWORD NumberOfBits; // Total number of bits that are available
BYTE Elements[1]; // Array of elements (variable length)
};
// Structure for file bitmap. Used by SFileGetArchiveBitmap
struct TFileBitmap
{
ULONGLONG StartOffset; // Starting offset of the file, covered by bitmap
ULONGLONG EndOffset; // Ending offset of the file, covered by bitmap
DWORD IsComplete; // If nonzero, no blocks are missing
DWORD BitmapSize; // Size of the file bitmap (in bytes)
DWORD BlockSize; // Size of one block, in bytes
DWORD Reserved; // Alignment
// Followed by file bitmap (variable length), array of BYTEs)
};
//-----------------------------------------------------------------------------
@@ -465,10 +441,10 @@ struct TMPQUserData
struct TMPQHeader
{
// The ID_MPQ ('MPQ\x1A') signature
DWORD dwID;
DWORD dwID;
// Size of the archive header
DWORD dwHeaderSize;
DWORD dwHeaderSize;
// 32-bit size of MPQ archive
// This field is deprecated in the Burning Crusade MoPaQ format, and the size of the archive
@@ -488,14 +464,14 @@ struct TMPQHeader
// Offset to the beginning of the hash table, relative to the beginning of the archive.
DWORD dwHashTablePos;
// Offset to the beginning of the block table, relative to the beginning of the archive.
DWORD dwBlockTablePos;
// Number of entries in the hash table. Must be a power of two, and must be less than 2^16 for
// the original MoPaQ format, or less than 2^20 for the Burning Crusade format.
DWORD dwHashTableSize;
// Number of entries in the block table
DWORD dwBlockTableSize;
@@ -540,7 +516,7 @@ struct TMPQHeader
// Size of raw data chunk to calculate MD5.
// MD5 of each data chunk follows the raw file data.
DWORD dwRawChunkSize;
DWORD dwRawChunkSize;
// MD5 of MPQ tables
unsigned char MD5_BlockTable[MD5_DIGEST_SIZE]; // MD5 of the block table before decryption
@@ -558,7 +534,7 @@ struct TMPQHash
{
// The hash of the file path, using method A.
DWORD dwName1;
// The hash of the file path, using method B.
DWORD dwName2;
@@ -595,30 +571,30 @@ struct TMPQBlock
{
// Offset of the beginning of the file, relative to the beginning of the archive.
DWORD dwFilePos;
// Compressed file size
DWORD dwCSize;
// Only valid if the block is a file; otherwise meaningless, and should be 0.
// If the file is compressed, this is the size of the uncompressed file data.
DWORD dwFSize;
DWORD dwFSize;
// Flags for the file. See MPQ_FILE_XXXX constants
DWORD dwFlags;
DWORD dwFlags;
};
// Patch file information, preceding the sector offset table
struct TPatchInfo
{
DWORD dwLength; // Length of patch info header, in bytes
DWORD dwFlags; // Flags. 0x80000000 = MD5 (?)
DWORD dwDataSize; // Uncompressed size of the patch file
BYTE md5[0x10]; // MD5 of the entire patch file after decompression
DWORD dwLength; // Length of patch info header, in bytes
DWORD dwFlags; // Flags. 0x80000000 = MD5 (?)
DWORD dwDataSize; // Uncompressed size of the patch file
BYTE md5[0x10]; // MD5 of the entire patch file after decompression
// Followed by the sector table (variable length)
};
// Header for PTCH files
// Header for PTCH files
struct TPatchHeader
{
//-- PATCH header -----------------------------------
@@ -626,7 +602,7 @@ struct TPatchHeader
DWORD dwSizeOfPatchData; // Size of the entire patch (decompressed)
DWORD dwSizeBeforePatch; // Size of the file before patch
DWORD dwSizeAfterPatch; // Size of file after patch
//-- MD5 block --------------------------------------
DWORD dwMD5; // 'MD5_'
DWORD dwMd5BlockSize; // Size of the MD5 block, including the signature and size itself
@@ -666,54 +642,70 @@ struct TFileEntry
// Common header for HET and BET tables
struct TMPQExtTable
{
DWORD dwSignature; // 'HET\x1A' or 'BET\x1A'
DWORD dwVersion; // Version. Seems to be always 1
DWORD dwDataSize; // Size of the contained table
DWORD dwSignature; // 'HET\x1A' or 'BET\x1A'
DWORD dwVersion; // Version. Seems to be always 1
DWORD dwDataSize; // Size of the contained table
// Followed by the table header
// Followed by the table data
};
//
// MPQ data bitmap, can be found at (FileSize - sizeof(TMPQBlockMap))
//
// There is bit map of the entire MPQ before TMPQBitmap. Each 0x4000-byte
// block is represented by one bit (including the last, eventually incomplete block).
//
struct TMPQBitmap
{
DWORD dwSignature; // 'ptv3' (MPQ_BLOCK_MAP_SIGNATURE)
DWORD dwAlways3; // Unknown, seems to always have value of 3
DWORD dwBuildNumber; // Game build number for that MPQ
DWORD dwMapOffsetLo; // Low 32-bits of the offset of the bit map
DWORD dwMapOffsetHi; // High 32-bits of the offset of the bit map
DWORD dwBlockSize; // Size of one block (usually 0x4000 bytes)
};
// Structure for parsed HET table
struct TMPQHetTable
{
TBitArray * pBetIndexes; // Bit array of indexes to BET tables
LPBYTE pHetHashes; // Array of HET hashes. Each entry has size of 1 byte
ULONGLONG AndMask64; // AND mask used for calculating file name hash
ULONGLONG OrMask64; // OR mask used for setting the highest bit of the file name hash
TBitArray * pBetIndexes; // Bit array of indexes to BET tables
LPBYTE pHetHashes; // Array of HET hashes. Each entry has size of 1 byte
ULONGLONG AndMask64; // AND mask used for calculating file name hash
ULONGLONG OrMask64; // OR mask used for setting the highest bit of the file name hash
DWORD dwIndexSizeTotal; // Total size of one entry in pBetIndexes (in bits)
DWORD dwIndexSizeExtra; // Extra bits in the entry in pBetIndexes
DWORD dwIndexSize; // Effective size of one entry in pBetIndexes (in bits)
DWORD dwMaxFileCount; // Maximum number of files in the MPQ
DWORD dwHashTableSize; // Number of entries in pBetHashes
DWORD dwHashBitSize; // Effective number of bits in the hash
DWORD dwIndexSizeTotal; // Total size of one entry in pBetIndexes (in bits)
DWORD dwIndexSizeExtra; // Extra bits in the entry in pBetIndexes
DWORD dwIndexSize; // Effective size of one entry in pBetIndexes (in bits)
DWORD dwMaxFileCount; // Maximum number of files in the MPQ
DWORD dwHashTableSize; // Number of entries in pBetHashes
DWORD dwHashBitSize; // Effective number of bits in the hash
};
// Structure for parsed BET table
struct TMPQBetTable
{
TBitArray * pBetHashes; // Array of BET hashes
TBitArray * pFileTable; // Bit-based file table
LPDWORD pFileFlags; // Array of file flags
TBitArray * pBetHashes; // Array of BET hashes
TBitArray * pFileTable; // Bit-based file table
LPDWORD pFileFlags; // Array of file flags
DWORD dwTableEntrySize; // Size of one table entry, in bits
DWORD dwBitIndex_FilePos; // Bit index of the file position in the table entry
DWORD dwBitIndex_FileSize; // Bit index of the file size in the table entry
DWORD dwBitIndex_CmpSize; // Bit index of the compressed size in the table entry
DWORD dwBitIndex_FlagIndex; // Bit index of the flag index in the table entry
DWORD dwBitIndex_Unknown; // Bit index of ??? in the table entry
DWORD dwBitCount_FilePos; // Size of file offset (in bits) within table entry
DWORD dwBitCount_FileSize; // Size of file size (in bits) within table entry
DWORD dwBitCount_CmpSize; // Size of compressed file size (in bits) within table entry
DWORD dwBitCount_FlagIndex; // Size of flag index (in bits) within table entry
DWORD dwBitCount_Unknown; // Size of ??? (in bits) within table entry
DWORD dwBetHashSizeTotal; // Total size of bet hash
DWORD dwBetHashSizeExtra; // Extra bits in the bet hash
DWORD dwBetHashSize; // Effective size of the bet hash
DWORD dwFileCount; // Number of files (usually equal to maximum number of files)
DWORD dwFlagCount; // Number of entries in pFileFlags
DWORD dwTableEntrySize; // Size of one table entry, in bits
DWORD dwBitIndex_FilePos; // Bit index of the file position in the table entry
DWORD dwBitIndex_FileSize; // Bit index of the file size in the table entry
DWORD dwBitIndex_CmpSize; // Bit index of the compressed size in the table entry
DWORD dwBitIndex_FlagIndex; // Bit index of the flag index in the table entry
DWORD dwBitIndex_Unknown; // Bit index of ??? in the table entry
DWORD dwBitCount_FilePos; // Size of file offset (in bits) within table entry
DWORD dwBitCount_FileSize; // Size of file size (in bits) within table entry
DWORD dwBitCount_CmpSize; // Size of compressed file size (in bits) within table entry
DWORD dwBitCount_FlagIndex; // Size of flag index (in bits) within table entry
DWORD dwBitCount_Unknown; // Size of ??? (in bits) within table entry
DWORD dwBetHashSizeTotal; // Total size of bet hash
DWORD dwBetHashSizeExtra; // Extra bits in the bet hash
DWORD dwBetHashSize; // Effective size of the bet hash
DWORD dwFileCount; // Number of files (usually equal to maximum number of files)
DWORD dwFlagCount; // Number of entries in pFileFlags
};
// Archive handle structure
@@ -731,10 +723,11 @@ struct TMPQArchive
TMPQUserData * pUserData; // MPQ user data (NULL if not present in the file)
TMPQHeader * pHeader; // MPQ file header
TMPQBitmap * pBitmap; // MPQ bitmap
TMPQHash * pHashTable; // Hash table
TMPQHetTable * pHetTable; // Het table
TFileEntry * pFileTable; // File table
TMPQUserData UserData; // MPQ user data. Valid only when ID_MPQ_USERDATA has been found
BYTE HeaderData[MPQ_HEADER_SIZE_V4]; // Storage for MPQ header
@@ -747,7 +740,7 @@ struct TMPQArchive
DWORD dwFileFlags2; // Flags for (attributes)
DWORD dwAttrFlags; // Flags for the (attributes) file, see MPQ_ATTRIBUTE_XXX
DWORD dwFlags; // See MPQ_FLAG_XXXXX
};
};
// File handle structure
struct TMPQFile
@@ -767,7 +760,7 @@ struct TMPQFile
DWORD cbFileData; // Size of loaded patched data
TPatchInfo * pPatchInfo; // Patch info block, preceding the sector table
DWORD * SectorOffsets; // Position of each file sector, relative to the begin of the file. Only for compressed files.
DWORD * SectorOffsets; // Position of each file sector, relative to the begin of the file. Only for compressed files.
DWORD * SectorChksums; // Array of sector checksums (either ADLER32 or MD5) values for each file sector
DWORD dwSectorCount; // Number of sectors in the file
DWORD dwPatchedFileSize; // Size of patched file. Used when saving patch file to the MPQ
@@ -779,7 +772,6 @@ struct TMPQFile
unsigned char hctx[HASH_STATE_SIZE];// Hash state for MD5. Used when saving file to MPQ
DWORD dwCrc32; // CRC32 value, used when saving file to MPQ
BYTE PreviousCompression; // Compression of previous sector
bool bLoadedSectorCRCs; // If true, we already tried to load sector CRCs
bool bCheckSectorCRCs; // If true, then SFileReadFile will check sector CRCs when reading the file
@@ -803,19 +795,39 @@ typedef struct _SFILE_FIND_DATA
} SFILE_FIND_DATA, *PSFILE_FIND_DATA;
typedef struct _SFILE_CREATE_MPQ
{
DWORD cbSize; // Size of this structure, in bytes
DWORD dwMpqVersion; // Version of the MPQ to be created
void *pvUserData; // Reserved, must be NULL
DWORD cbUserData; // Reserved, must be 0
DWORD dwStreamFlags; // Stream flags for creating the MPQ
DWORD dwFileFlags1; // File flags for (listfile). 0 = default
DWORD dwFileFlags2; // File flags for (attributes). 0 = default
DWORD dwAttrFlags; // Flags for the (attributes) file. If 0, no attributes will be created
DWORD dwSectorSize; // Sector size for compressed files
DWORD dwRawChunkSize; // Size of raw data chunk
DWORD dwMaxFileCount; // File limit for the MPQ
} SFILE_CREATE_MPQ, *PSFILE_CREATE_MPQ;
//-----------------------------------------------------------------------------
// Stream support - functions
TFileStream * FileStream_CreateFile(const TCHAR * szFileName);
TFileStream * FileStream_OpenFile(const TCHAR * szFileName, bool bWriteAccess);
TFileStream * FileStream_OpenEncrypted(const TCHAR * szFileName);
bool FileStream_GetPos(TFileStream * pStream, ULONGLONG & ByteOffset);
TFileStream * FileStream_CreateFile(const TCHAR * szFileName, DWORD dwStreamFlags);
TFileStream * FileStream_OpenFile(const TCHAR * szFileName, DWORD dwStreamFlags);
TCHAR * FileStream_GetFileName(TFileStream * pStream);
bool FileStream_IsReadOnly(TFileStream * pStream);
bool FileStream_Read(TFileStream * pStream, ULONGLONG * pByteOffset, void * pvBuffer, DWORD dwBytesToRead);
bool FileStream_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const void * pvBuffer, DWORD dwBytesToWrite);
bool FileStream_GetLastWriteTime(TFileStream * pStream, ULONGLONG * pFT);
bool FileStream_GetPos(TFileStream * pStream, ULONGLONG & ByteOffset);
bool FileStream_SetPos(TFileStream * pStream, ULONGLONG ByteOffset);
bool FileStream_GetSize(TFileStream * pStream, ULONGLONG & FileSize);
bool FileStream_SetSize(TFileStream * pStream, ULONGLONG NewFileSize);
bool FileStream_MoveFile(TFileStream * pStream, TFileStream * pTempStream);
bool FileStream_GetTime(TFileStream * pStream, ULONGLONG * pFT);
bool FileStream_Switch(TFileStream * pStream, TFileStream * pTempStream);
bool FileStream_SetBitmap(TFileStream * pStream, TFileBitmap * pBitmap);
bool FileStream_GetBitmap(TFileStream * pStream, TFileBitmap * pBitmap, DWORD Length, LPDWORD LengthNeeded);
void FileStream_Close(TFileStream * pStream);
//-----------------------------------------------------------------------------
@@ -834,12 +846,6 @@ typedef bool (WINAPI * SFILEREADFILE)(HANDLE, void *, DWORD, LPDWORD, LPOVERLAP
//-----------------------------------------------------------------------------
// Functions for manipulation with StormLib global flags
#define SFILE_FLAG_ALLOW_WRITE_SHARE 0x00000001 // When a MPQ is open for write by StormLib,
// it is allowed to open it for write with another application.
DWORD WINAPI SFileGetGlobalFlags();
DWORD WINAPI SFileSetGlobalFlags(DWORD dwNewFlags);
LCID WINAPI SFileGetLocale();
LCID WINAPI SFileSetLocale(LCID lcNewLocale);
@@ -848,7 +854,9 @@ LCID WINAPI SFileSetLocale(LCID lcNewLocale);
bool WINAPI SFileOpenArchive(const TCHAR * szMpqName, DWORD dwPriority, DWORD dwFlags, HANDLE * phMpq);
bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwFlags, DWORD dwMaxFileCount, HANDLE * phMpq);
bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCreateInfo, HANDLE * phMpq);
bool WINAPI SFileGetArchiveBitmap(HANDLE hMpq, TFileBitmap * pBitmap, DWORD Length, LPDWORD LengthNeeded);
bool WINAPI SFileFlushArchive(HANDLE hMpq);
bool WINAPI SFileCloseArchive(HANDLE hMpq);
@@ -931,9 +939,9 @@ bool WINAPI SFileCreateFile(HANDLE hMpq, const char * szArchivedName, ULONGLON
bool WINAPI SFileWriteFile(HANDLE hFile, const void * pvData, DWORD dwSize, DWORD dwCompression);
bool WINAPI SFileFinishFile(HANDLE hFile);
bool WINAPI SFileAddFileEx(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwCompression, DWORD dwCompressionNext = 0xFFFFFFFF);
bool WINAPI SFileAddFile(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags);
bool WINAPI SFileAddWave(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwQuality);
bool WINAPI SFileAddFileEx(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwCompression, DWORD dwCompressionNext = MPQ_COMPRESSION_NEXT_SAME);
bool WINAPI SFileAddFile(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags);
bool WINAPI SFileAddWave(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwQuality);
bool WINAPI SFileRemoveFile(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope = SFILE_OPEN_FROM_MPQ);
bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szOldFileName, const char * szNewFileName);
bool WINAPI SFileSetFileLocale(HANDLE hFile, LCID lcNewLocale);
@@ -948,6 +956,7 @@ int WINAPI SCompImplode (char * pbOutBuffer, int * pcbOutBuffer, char * pb
int WINAPI SCompExplode (char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer);
int WINAPI SCompCompress (char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer, unsigned uCompressionMask, int nCmpType, int nCmpLevel);
int WINAPI SCompDecompress (char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer);
int WINAPI SCompDecompress2(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer);
//-----------------------------------------------------------------------------
// Non-Windows support for SetLastError/GetLastError
+24 -24
View File
@@ -46,6 +46,7 @@
#include <ctype.h>
#include <stdio.h>
#include <windows.h>
#include <wininet.h>
#define PLATFORM_LITTLE_ENDIAN
#ifdef WIN64
@@ -59,20 +60,26 @@
#endif
// Defines for Mac Carbon
#if !defined(PLATFORM_DEFINED) && defined(__APPLE__) // Mac Carbon API
// Defines for Mac
#if !defined(PLATFORM_DEFINED) && defined(__APPLE__) // Mac BSD API
// Macintosh using Carbon
#include <Carbon/Carbon.h> // Mac OS X
// Macintosh
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#define PKEXPORT
#define __SYS_ZLIB
#define __SYS_BZLIB
#ifndef __BIG_ENDIAN__
#define PLATFORM_LITTLE_ENDIAN // Apple is now making Macs with Intel CPUs
#define PLATFORM_LITTLE_ENDIAN
#endif
#define PLATFORM_MAC
#define PLATFORM_DEFINED // The platform is known now
@@ -83,6 +90,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
@@ -152,29 +160,21 @@
#define _stricmp strcasecmp
#define _strnicmp strncasecmp
#define _tcsnicmp strncasecmp
#endif // !WIN32
// Platform-specific error codes
#ifdef PLATFORM_MAC
#define ERROR_SUCCESS noErr
#define ERROR_FILE_NOT_FOUND fnfErr
#define ERROR_ACCESS_DENIED permErr
#define ERROR_INVALID_HANDLE rfNumErr
#define ERROR_NOT_ENOUGH_MEMORY mFulErr
#define ERROR_BAD_FORMAT 200 // Returned when the opened file is in format that is not recognized by StormLib
#define ERROR_NO_MORE_FILES errFSNoMoreItems
#define ERROR_HANDLE_EOF eofErr
#define ERROR_NOT_SUPPORTED 201
#define ERROR_INVALID_PARAMETER paramErr
#define ERROR_DISK_FULL dskFulErr
#define ERROR_ALREADY_EXISTS dupFNErr
#define ERROR_CAN_NOT_COMPLETE 202 // A generic error, when any operation fails from an unknown reason
#define ERROR_FILE_CORRUPT 203 // At any point when there is bad data format in the file
#define ERROR_INSUFFICIENT_BUFFER errFSBadBuffer
// 64-bit calls are supplied by "normal" calls on Mac
#if defined(PLATFORM_MAC)
#define stat64 stat
#define fstat64 fstat
#define lseek64 lseek
#define off64_t off_t
#define O_LARGEFILE 0
#endif
#ifdef PLATFORM_LINUX
// Platform-specific error codes for UNIX-based platforms
#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX)
#define ERROR_SUCCESS 0
#define ERROR_FILE_NOT_FOUND ENOENT
#define ERROR_ACCESS_DENIED EPERM
+16 -15
View File
@@ -17,17 +17,17 @@
//------------------------------------------------------------------------------
// Structures
union TByteAndWordPtr
typedef union _BYTE_AND_WORD_PTR
{
short * pw;
unsigned char * pb;
};
} BYTE_AND_WORD_PTR;
union TWordAndByteArray
typedef union _WORD_AND_BYTE_ARRAY
{
short w;
unsigned char b[2];
};
} WORD_AND_BYTE_ARRAY;
//-----------------------------------------------------------------------------
// Tables necessary dor decompression
@@ -63,8 +63,8 @@ static long step_table[] =
int CompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuffer, int dwInLength, int nChannels, int nCmpLevel)
// ECX EDX
{
TWordAndByteArray Wcmp;
TByteAndWordPtr out; // Pointer to the output buffer
WORD_AND_BYTE_ARRAY Wcmp;
BYTE_AND_WORD_PTR out; // Pointer to the output buffer
long SInt32Array1[2];
long SInt32Array2[2];
long SInt32Array3[2];
@@ -83,6 +83,7 @@ int CompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuff
int nLength;
int nIndex;
int nValue;
int i, chnl;
// If less than 2 bytes remain, don't decompress anything
// pbSaveOutBuffer = pbOutBuffer;
@@ -99,7 +100,7 @@ int CompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuff
SInt32Array1[0] = SInt32Array1[1] = 0x2C;
for(int i = 0; i < nChannels; i++)
for(i = 0; i < nChannels; i++)
{
nOneWord = BSWAP_INT16_SIGNED(*pwInBuffer++);
*out.pw++ = BSWAP_INT16_SIGNED((short)nOneWord);
@@ -119,7 +120,7 @@ int CompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuff
// ebx - nChannels
// ecx - pwOutPos
for(int chnl = nChannels; chnl < nWordsRemains; chnl++)
for(chnl = nChannels; chnl < nWordsRemains; chnl++)
{
// 1500F030
if((out.pb - pbOutBuffer + 2) > nBytesRemains)
@@ -223,14 +224,14 @@ int CompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuff
// 1500F230
int DecompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, unsigned char * pbInBuffer, int dwInLength, int nChannels)
{
TByteAndWordPtr out; // Output buffer
TByteAndWordPtr in;
BYTE_AND_WORD_PTR out; // Output buffer
BYTE_AND_WORD_PTR in;
unsigned char * pbInBufferEnd = (pbInBuffer + dwInLength);
long SInt32Array1[2];
long SInt32Array2[2];
long nOneWord;
int dwOutLengthCopy = dwOutLength;
int nIndex;
int i;
SInt32Array1[0] = SInt32Array1[1] = 0x2C;
out.pb = pbOutBuffer;
@@ -238,15 +239,15 @@ int DecompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, unsigned char
in.pw++;
// Fill the Uint32Array2 array by channel values.
for(int i = 0; i < nChannels; i++)
for(i = 0; i < nChannels; i++)
{
nOneWord = BSWAP_INT16_SIGNED(*in.pw++);
SInt32Array2[i] = nOneWord;
if(dwOutLengthCopy < 2)
if(dwOutLength < 2)
return (int)(out.pb - pbOutBuffer);
*out.pw++ = BSWAP_INT16_SIGNED((short)nOneWord);
dwOutLengthCopy -= sizeof(short);
dwOutLength -= sizeof(short);
}
// Get the initial index
@@ -270,7 +271,7 @@ int DecompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, unsigned char
if(SInt32Array1[nIndex] != 0)
SInt32Array1[nIndex]--;
if(dwOutLengthCopy < 2)
if(dwOutLength < 2)
return (int)(out.pb - pbOutBuffer);
*out.pw++ = BSWAP_INT16_SIGNED((unsigned short)SInt32Array2[nIndex]);
+14 -6
View File
@@ -263,10 +263,15 @@ static void InsertItem(THTreeItem ** itemPtr, THTreeItem * item, unsigned long n
if(PTR_INVALID(prev2))
{
prev2 = PTR_NOT(prev);
prev2->next = item;
item2->prev = item; // Next after last item
if(prev != NULL)
{
prev2 = PTR_NOT(prev);
if(prev2 != NULL)
{
prev2->next = item;
item2->prev = item; // Next after last item
}
}
return;
}
@@ -1009,8 +1014,8 @@ unsigned int THuffmannTree::DoDecompression(unsigned char * pbOutBuffer, unsigne
for(;;)
{
// Security check: If we are at the end of the input buffer,
// it means that the data are corrupt.
if(is->pbInBuffer > is->pbInBufferEnd)
// it means that the data is corrupt
if(is->BitCount == 0 && is->pbInBuffer >= is->pbInBufferEnd)
return 0;
// Get 7 bits from input stream
@@ -1046,6 +1051,9 @@ _1500E549:
do
{
if(pItem1 == NULL)
return 0;
pItem1 = pItem1->child; // Move down by one level
if(is->GetBit()) // If current bit is set, move to previous
pItem1 = pItem1->prev;
-2
View File
@@ -65,8 +65,6 @@ class TOutputStream
// Huffmann tree item (?)
struct THTreeItem
{
public:
THTreeItem * Call1501DB70(THTreeItem * pLast);
THTreeItem * GetPrevItem(LONG_PTR value);
void ClearItemLinks();
+1
View File
@@ -0,0 +1 @@
Taken from LZMA SDK v 9.11
+4
View File
@@ -255,6 +255,10 @@ int DecompressSparse(unsigned char * pbOutBuffer, int * pcbOutBuffer, unsigned c
OneByte = *pbInBuffer++;
cbOutBuffer |= (OneByte << 0x00);
// Verify the size of the stream against the output buffer size
if(cbOutBuffer > *pcbOutBuffer)
return 0;
// Put the output size to the buffer
*pcbOutBuffer = cbOutBuffer;
+1 -2
View File
@@ -8,13 +8,12 @@ LIBRARY StormLib.dll
EXPORTS
SFileGetGlobalFlags
SFileSetGlobalFlags
SFileSetLocale
SFileGetLocale
SFileOpenArchive
SFileCreateArchive
SFileGetArchiveBitmap
SFileFlushArchive
SFileCloseArchive
+1 -2
View File
@@ -4,13 +4,12 @@
# swilkins1337@gmail.com
#
_SFileGetGlobalFlags
_SFileSetGlobalFlags
_SFileSetLocale
_SFileGetLocale
_SFileOpenArchive
_SFileCreateArchive
_SFileGetArchiveBitmap
_SFileFlushArchive
_SFileCloseArchive
+263 -175
View File
@@ -36,7 +36,7 @@
#endif
#ifdef PLATFORM_MAC
#define WORK_PATH_ROOT "/Users/sam/Downloads/"
#define WORK_PATH_ROOT "/Users/sam/StormLib/test"
#endif
#ifndef LANG_CZECH
@@ -471,6 +471,29 @@ static int TestPartFileRead(const TCHAR * szFileName)
return nError;
}
//-----------------------------------------------------------------------------
// Compare PKLIB decompression
BYTE pbCompressed1[] = {0x00, 0x04, 0x00, 0x00, 0x04, 0xF0, 0x1F, 0x7B, 0x01, 0xFF};
BYTE pbCompressed2[] = {0x00, 0x04, 0x00, 0x00, 0x04, 0xF0, 0x1F, 0x00, 0x00, 0x04, 0xFC, 0x03};
static int ComparePklibCompressions()
{
char Decompressed[0x1000];
char Compressed[0x1000];
int cbDecompressed = 0x208;
int cbCompressed = sizeof(Compressed);
memset(Decompressed, 0, cbDecompressed);
SCompImplode(Compressed, &cbCompressed, Decompressed, cbDecompressed);
cbDecompressed = sizeof(Decompressed);
SCompExplode(Decompressed, &cbDecompressed, Compressed, cbCompressed);
return ERROR_SUCCESS;
}
//-----------------------------------------------------------------------------
// Compare LZMA decompression
@@ -713,7 +736,7 @@ __TryAgain:
static int TestArchiveOpenAndClose(const TCHAR * szMpqName)
{
const char * szFileName1 = "world\\maps\\AhnQiraj\\AhnQiraj_27_51_tex1.adt";
const char * szFileName1 = "DBFilesClient\\Item.dbc";
// const char * szFileName2 = "items\\map\\mapz_deleted.cel";
TMPQArchive * ha = NULL;
HANDLE hFile1 = NULL;
@@ -724,7 +747,7 @@ static int TestArchiveOpenAndClose(const TCHAR * szMpqName)
if(nError == ERROR_SUCCESS)
{
_tprintf(_T("Opening archive %s ...\n"), szMpqName);
if(!SFileOpenArchive(szMpqName, 0, 0, /* MPQ_OPEN_ENCRYPTED,*/ &hMpq))
if(!SFileOpenArchive(szMpqName, 0, STREAM_PROVIDER_ENCRYPTED | BASE_PROVIDER_FILE, &hMpq))
nError = GetLastError();
ha = (TMPQArchive *)hMpq;
}
@@ -866,7 +889,7 @@ static int TestMpqCompacting(const TCHAR * szMpqName)
if(nError == ERROR_SUCCESS)
{
char * szFileName = "Shaders\\Effects\\shadowmap.wfx";
const char * szFileName = "Shaders\\Effects\\shadowmap.wfx";
printf("Deleting file %s ...\r", szFileName);
if(!SFileRemoveFile(hMpq, szFileName))
@@ -904,7 +927,7 @@ static int TestCreateArchive(const TCHAR * szMpqName)
// Create the new file
_tprintf(_T("Creating %s ...\n"), szMpqName);
pStream = FileStream_CreateFile(szMpqName);
pStream = FileStream_CreateFile(szMpqName, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
if(pStream == NULL)
nError = GetLastError();
@@ -1305,7 +1328,7 @@ static int TestCreateArchiveCopy(const TCHAR * szMpqName, const TCHAR * szMpqCop
szListFile = NULL;
// Create the new file
pStream = FileStream_CreateFile(szMpqCopyName);
pStream = FileStream_CreateFile(szMpqCopyName, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
if(pStream == NULL)
nError = GetLastError();
@@ -1396,154 +1419,6 @@ static int TestCreateArchiveCopy(const TCHAR * szMpqName, const TCHAR * szMpqCop
return nError;
}
static int TestCompareTwoArchives(
const TCHAR * szMpqName1,
const TCHAR * szMpqName2,
const char * szListFile,
DWORD dwBlockSize)
{
TMPQArchive * ha1 = NULL;
TMPQArchive * ha2 = NULL;
LPBYTE pbBuffer1 = NULL;
LPBYTE pbBuffer2 = NULL;
HANDLE hMpq1 = NULL; // Handle of the first archive
HANDLE hMpq2 = NULL; // Handle of the second archive
HANDLE hFile1 = NULL;
HANDLE hFile2 = NULL;
int nError = ERROR_SUCCESS;
// If no listfile or an empty one, use NULL
if(szListFile == NULL || *szListFile == 0)
szListFile = NULL;
// Allocate both buffers
pbBuffer1 = new BYTE[dwBlockSize];
pbBuffer2 = new BYTE[dwBlockSize];
if(pbBuffer1 == NULL || pbBuffer2 == NULL)
nError = ERROR_NOT_ENOUGH_MEMORY;
_tprintf(_T("=============== Comparing MPQ archives ===============\n"));
// Open the first MPQ archive
if(nError == ERROR_SUCCESS && szMpqName1 != NULL)
{
_tprintf(_T("Opening %s ...\n"), szMpqName1);
if(!SFileOpenArchive(szMpqName1, 0, 0, &hMpq1))
nError = GetLastError();
ha1 = (TMPQArchive *)hMpq1;
}
// Open the second MPQ archive
if(nError == ERROR_SUCCESS && szMpqName2 != NULL)
{
_tprintf(_T("Opening %s ...\n"), szMpqName2);
if(!SFileOpenArchive(szMpqName2, 0, 0, &hMpq2))
nError = GetLastError();
ha2 = (TMPQArchive *)hMpq2;
}
// Compare the header
if(nError == ERROR_SUCCESS && (ha1 != NULL && ha2 != NULL))
{
if(ha1->pHeader->dwHeaderSize != ha2->pHeader->dwHeaderSize)
printf(" - Header size is different\n");
if(ha1->pHeader->wFormatVersion != ha2->pHeader->wFormatVersion)
printf(" - Format version is different\n");
if(ha1->pHeader->wSectorSize != ha2->pHeader->wSectorSize)
printf(" - Sector size is different\n");
if(ha1->pHeader->HetTableSize64 != ha2->pHeader->HetTableSize64)
printf(" - HET table size is different\n");
if(ha1->pHeader->BetTableSize64 != ha2->pHeader->BetTableSize64)
printf(" - BET table size is different\n");
if(ha1->pHeader->dwHashTableSize != ha2->pHeader->dwHashTableSize)
printf(" - Hash table size is different\n");
if(ha1->pHeader->dwBlockTableSize != ha2->pHeader->dwBlockTableSize)
printf(" - Block table size is different\n");
}
// Find all files in the first archive and compare them
if(nError == ERROR_SUCCESS)
{
SFILE_FIND_DATA sf;
HANDLE hFind = SFileFindFirstFile(hMpq1, "*", &sf, szListFile);
DWORD dwSearchScope1 = SFILE_OPEN_FROM_MPQ;
DWORD dwSearchScope2 = SFILE_OPEN_FROM_MPQ;
bool bResult = true;
if(hMpq1 == NULL)
dwSearchScope1 = SFILE_OPEN_LOCAL_FILE;
if(hMpq2 == NULL)
dwSearchScope2 = SFILE_OPEN_LOCAL_FILE;
while(hFind != NULL && bResult == true)
{
printf("%s\n", sf.cFileName);
SFileSetLocale(sf.lcLocale);
// Open the first file
if(!SFileOpenFileEx(hMpq1, sf.cFileName, dwSearchScope1, &hFile1))
{
printf("Failed to open the file %s in the first archive\n", sf.cFileName);
continue;
}
if(!SFileOpenFileEx(hMpq2, sf.cFileName, dwSearchScope2, &hFile2))
{
printf("Failed to open the file %s in the second archive\n", sf.cFileName);
continue;
}
if(dwSearchScope1 == SFILE_OPEN_FROM_MPQ && dwSearchScope2 == SFILE_OPEN_FROM_MPQ)
{
TMPQFile * hf1 = (TMPQFile *)hFile1;
TMPQFile * hf2 = (TMPQFile *)hFile2;
// Compare the file sizes
if(hf1->pFileEntry->dwFileSize != hf2->pFileEntry->dwFileSize)
printf(" - %s different size (%u x %u)\n", sf.cFileName, hf1->pFileEntry->dwFileSize, hf2->pFileEntry->dwFileSize);
if(hf1->pFileEntry->dwFlags != hf2->pFileEntry->dwFlags)
printf(" - %s different flags (%08X x %08X)\n", sf.cFileName, hf1->pFileEntry->dwFlags, hf2->pFileEntry->dwFlags);
}
if(!CompareArchivedFiles(sf.cFileName, hFile1, hFile2, 0x1001))
printf(" - %s different content\n", sf.cFileName);
if(!CompareArchivedFilesRR(sf.cFileName, hFile1, hFile2, 0x100000))
printf(" - %s different content\n", sf.cFileName);
// Close both files
SFileCloseFile(hFile2);
SFileCloseFile(hFile1);
hFile2 = hFile1 = NULL;
// Find the next file
bResult = SFileFindNextFile(hFind, &sf);
}
// Close all handles
if(hFile2 != NULL)
SFileCloseFile(hFile2);
if(hFile1 != NULL)
SFileCloseFile(hFile1);
if(hFind != NULL)
SFileFindClose(hFind);
}
// Close both archives
clreol();
printf("================ MPQ compare complete ================\n");
if(hMpq2 != NULL)
SFileCloseArchive(hMpq2);
if(hMpq1 != NULL)
SFileCloseArchive(hMpq1);
if(pbBuffer2 != NULL)
delete [] pbBuffer2;
if(pbBuffer1 != NULL)
delete [] pbBuffer1;
return nError;
}
static int TestOpenPatchedArchive(const TCHAR * szMpqName, ...)
{
TFileStream * pStream;
@@ -1648,7 +1523,7 @@ static int TestOpenPatchedArchive(const TCHAR * szMpqName, ...)
if(nError == ERROR_SUCCESS)
{
MergeLocalPath(szLocFileName, MAKE_PATH("Work//"), GetPlainFileNameA(szFileName));
pStream = FileStream_CreateFile(szLocFileName);
pStream = FileStream_CreateFile(szLocFileName, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
if(pStream != NULL)
{
FileStream_Write(pStream, NULL, pbFullFile, dwFileSize);
@@ -1669,6 +1544,222 @@ static int TestOpenPatchedArchive(const TCHAR * szMpqName, ...)
return nError;
}
static int TestCompareTwoArchives(
const TCHAR * szMpqName1,
const TCHAR * szMpqName2,
const char * szListFile,
DWORD dwBlockSize)
{
TMPQArchive * ha1 = NULL;
TMPQArchive * ha2 = NULL;
HANDLE hMpq1 = NULL; // Handle of the first archive
HANDLE hMpq2 = NULL; // Handle of the second archive
HANDLE hFile1 = NULL;
HANDLE hFile2 = NULL;
int nError = ERROR_SUCCESS;
// If no listfile or an empty one, use NULL
if(szListFile == NULL || *szListFile == 0)
szListFile = NULL;
_tprintf(_T("=============== Comparing MPQ archives ===============\n"));
// Open the first MPQ archive
if(nError == ERROR_SUCCESS && szMpqName1 != NULL)
{
_tprintf(_T("Opening %s ...\n"), szMpqName1);
if(!SFileOpenArchive(szMpqName1, 0, 0, &hMpq1))
nError = GetLastError();
ha1 = (TMPQArchive *)hMpq1;
}
// Open the second MPQ archive
if(nError == ERROR_SUCCESS && szMpqName2 != NULL)
{
_tprintf(_T("Opening %s ...\n"), szMpqName2);
if(!SFileOpenArchive(szMpqName2, 0, 0, &hMpq2))
nError = GetLastError();
ha2 = (TMPQArchive *)hMpq2;
}
// Compare the header
if(nError == ERROR_SUCCESS && (ha1 != NULL && ha2 != NULL))
{
if(ha1->pHeader->dwHeaderSize != ha2->pHeader->dwHeaderSize)
printf(" - Header size is different\n");
if(ha1->pHeader->wFormatVersion != ha2->pHeader->wFormatVersion)
printf(" - Format version is different\n");
if(ha1->pHeader->wSectorSize != ha2->pHeader->wSectorSize)
printf(" - Sector size is different\n");
if(ha1->pHeader->HetTableSize64 != ha2->pHeader->HetTableSize64)
printf(" - HET table size is different\n");
if(ha1->pHeader->BetTableSize64 != ha2->pHeader->BetTableSize64)
printf(" - BET table size is different\n");
if(ha1->pHeader->dwHashTableSize != ha2->pHeader->dwHashTableSize)
printf(" - Hash table size is different\n");
if(ha1->pHeader->dwBlockTableSize != ha2->pHeader->dwBlockTableSize)
printf(" - Block table size is different\n");
}
// Find all files in the first archive and compare them
if(nError == ERROR_SUCCESS)
{
SFILE_FIND_DATA sf;
TMPQFile * hf1;
TMPQFile * hf2;
HANDLE hFind = SFileFindFirstFile(hMpq1, "*", &sf, szListFile);
bool bResult = true;
while(hFind != NULL && bResult == true)
{
// printf("%s \r", sf.cFileName);
SFileSetLocale(sf.lcLocale);
// Open the first file
if(!SFileOpenFileEx(hMpq1, sf.cFileName, 0, &hFile1))
printf("Failed to open the file %s in the first archive\n", sf.cFileName);
if(!SFileOpenFileEx(hMpq2, sf.cFileName, 0, &hFile2))
printf("Failed to open the file %s in the second archive\n", sf.cFileName);
if(hFile1 != NULL && hFile2 != NULL)
{
// Get the TMPQFile pointers
hf1 = (TMPQFile *)hFile1;
hf2 = (TMPQFile *)hFile2;
// Compare the file sizes
if(hf1->pFileEntry->dwFileSize != hf2->pFileEntry->dwFileSize)
printf("Different file size: %s (%u x %u)\n", sf.cFileName, hf1->pFileEntry->dwFileSize, hf2->pFileEntry->dwFileSize);
if(hf1->pFileEntry->dwCmpSize != hf2->pFileEntry->dwCmpSize)
printf("Different cmpr size: %s (%u x %u)\n", sf.cFileName, hf1->pFileEntry->dwCmpSize, hf2->pFileEntry->dwCmpSize);
if(hf1->pFileEntry->dwFlags != hf2->pFileEntry->dwFlags)
printf("Different mpq flags: %s (%08X x %08X)\n", sf.cFileName, hf1->pFileEntry->dwFlags, hf2->pFileEntry->dwFlags);
if(!CompareArchivedFiles(sf.cFileName, hFile1, hFile2, dwBlockSize))
printf("Different file data: %s\n", sf.cFileName);
// if(!CompareArchivedFilesRR(sf.cFileName, hFile1, hFile2, dwBlockSize))
// printf("Different file data: %s\n", sf.cFileName);
}
// Close both files
if(hFile2 != NULL)
SFileCloseFile(hFile2);
if(hFile1 != NULL)
SFileCloseFile(hFile1);
hFile2 = hFile1 = NULL;
// Find the next file
bResult = SFileFindNextFile(hFind, &sf);
}
// Close the find handle
if(hFind != NULL)
SFileFindClose(hFind);
}
// Close both archives
clreol();
printf("================ MPQ compare complete ================\n");
if(hMpq2 != NULL)
SFileCloseArchive(hMpq2);
if(hMpq1 != NULL)
SFileCloseArchive(hMpq1);
return nError;
}
//-----------------------------------------------------------------------------
// Searching all known MPQs
#ifdef _WIN32
static int TestSearchOneArchive(const TCHAR * szMpqName)
{
SFILE_FIND_DATA sf;
HANDLE hFind;
HANDLE hMpq;
const TCHAR * szExtension;
bool bFound = true;
// Get the file extension
szExtension = _tcsrchr(szMpqName, _T('.'));
if(szExtension == NULL)
return ERROR_SUCCESS;
// Only search defined extensions
if(_tcsicmp(szExtension, _T(".mpq")) && _tcsnicmp(szExtension, _T(".SC2"), 4))
return ERROR_SUCCESS;
_tprintf(_T("Searching %s ...\n"), szMpqName);
// Try to open the MPQ
if(SFileOpenArchive(szMpqName, 0, MPQ_OPEN_READ_ONLY, &hMpq))
{
hFind = SFileFindFirstFile(hMpq, "*", &sf, NULL);
if(hFind != NULL)
{
while(bFound)
{
if(sf.dwFileFlags & MPQ_FILE_DELETE_MARKER)
_tprintf(_T("Delete marker: %s:%hs\n"), szMpqName, sf.cFileName);
bFound = SFileFindNextFile(hFind, &sf);
}
}
SFileCloseArchive(hMpq);
}
return ERROR_SUCCESS;
}
static int TestSearchAllArchives(const TCHAR * szSearchMask)
{
WIN32_FIND_DATA wf;
LPTSTR szFilePart;
HANDLE hFind;
TCHAR szFullPath[MAX_PATH];
BOOL bFound = TRUE;
// Initiate search
_tcscpy(szFullPath, szSearchMask);
szFilePart = _tcschr(szFullPath, _T('*'));
assert(szFilePart != NULL);
// Begin search
hFind = FindFirstFile(szSearchMask, &wf);
if(hFind != INVALID_HANDLE_VALUE)
{
while(bFound)
{
// Eliminate "." and ".."
if(wf.cFileName[0] != _T('.'))
{
// Construct the full path
_tcscpy(szFilePart, wf.cFileName);
// If it a directory?
if(wf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
_tcscat(szFullPath, _T("\\*"));
TestSearchAllArchives(szFullPath);
}
else
{
TestSearchOneArchive(szFullPath);
}
}
bFound = FindNextFile(hFind, &wf);
}
FindClose(hFind);
}
return ERROR_SUCCESS;
}
#endif
//-----------------------------------------------------------------------------
// Main
//
@@ -1697,6 +1788,9 @@ int main(void)
// if(nError == ERROR_SUCCESS)
// nError = TestPartFileRead(MAKE_PATH("2009 - PartialMPQs/patch.MPQ.part"));
// if(nError == ERROR_SUCCESS)
// nError = ComparePklibCompressions();
// Test LZMA compression method against the code ripped from Starcraft II
// if(nError == ERROR_SUCCESS)
// nError = CompareLzmaCompressions(MPQ_SECTOR_SIZE);
@@ -1706,8 +1800,8 @@ int main(void)
// nError = TestSectorCompress(MPQ_SECTOR_SIZE);
// Test the archive open and close
if(nError == ERROR_SUCCESS)
nError = TestArchiveOpenAndClose(MAKE_PATH("2011 - WoW/15050/world.MPQ"));
// if(nError == ERROR_SUCCESS)
// nError = TestArchiveOpenAndClose(_T("d:\\Install\\Blizzard\\Diablo III\\Diablo-III-8370-enGB-Installer\\Installer Tome 1.MPQE"));
// nError = TestArchiveOpenAndClose(MAKE_PATH("2011 - WoW BETA/wow-update-13202.MPQ"));
// nError = TestArchiveOpenAndClose(MAKE_PATH("2002 - Warcraft III/ProtectedMap_HashTable_FakeValid.w3x"));
// nError = TestArchiveOpenAndClose(MAKE_PATH("2010 - Starcraft II/Installer Tome 1 enGB.MPQE"));
@@ -1763,22 +1857,10 @@ int main(void)
// Create copy of the archive, appending some bytes before the MPQ header
// if(nError == ERROR_SUCCESS)
// nError = TestCreateArchiveCopy(MAKE_PATH("PartialMPQs/interface.MPQ.part"), MAKE_PATH("PartialMPQs/interface-copy.MPQ.part"), NULL);
/*
if(nError == ERROR_SUCCESS)
{
nError = TestCompareTwoArchives(MAKE_PATH("2011 - WoW-Cataclysm/wow-update-13189.MPQ"),
MAKE_PATH("wow-update-13189.MPQ"),
NULL,
0x1001);
}
*/
/*
if(nError == ERROR_SUCCESS)
{
nError = TestOpenPatchedArchive(MAKE_PATH("2004 - Wow 3.x/lichking.MPQ"),
MAKE_PATH("2011 - WoW 4.x/wow-update-13287.MPQ"),
NULL);
/*
nError = TestOpenPatchedArchive(MAKE_PATH("2011 - WoW 4.x/locale-enGB.MPQ"),
MAKE_PATH("2011 - WoW 4.x/wow-update-13164.MPQ"),
MAKE_PATH("2011 - WoW 4.x/wow-update-13205.MPQ"),
@@ -1791,14 +1873,20 @@ int main(void)
MAKE_PATH("2011 - WoW 4.x/wow-update-enGB-14333.MPQ"),
MAKE_PATH("2011 - WoW 4.x/wow-update-enGB-14480.MPQ"),
NULL);
}
*/
if(nError == ERROR_SUCCESS)
{
nError = TestCompareTwoArchives(MAKE_PATH("Sound-copy.mpq"),
MAKE_PATH("Sound.mpq"),
NULL,
0x1001);
}
// Remove the working directory
clreol();
if(nError != ERROR_SUCCESS)
printf("One or more errors occurred when testing StormLib\n");
printf("Work complete.\n");
// if(nError == ERROR_SUCCESS)
// nError = TestSearchAllArchives(MAKE_PATH("*"));
return nError;
}