Update StormLib
This commit is contained in:
1 parent
6fe1657fe9
commit
459257b056
44 files changed
+3539
-2276
No files matched your search
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 <====
|
||||
+1504
-1071
File diff suppressed because it is too large.
Load diff
@@ -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__
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
//
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -65,8 +65,6 @@ class TOutputStream
|
||||
// Huffmann tree item (?)
|
||||
struct THTreeItem
|
||||
{
|
||||
public:
|
||||
|
||||
THTreeItem * Call1501DB70(THTreeItem * pLast);
|
||||
THTreeItem * GetPrevItem(LONG_PTR value);
|
||||
void ClearItemLinks();
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Taken from LZMA SDK v 9.11
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -8,13 +8,12 @@ LIBRARY StormLib.dll
|
||||
|
||||
EXPORTS
|
||||
|
||||
SFileGetGlobalFlags
|
||||
SFileSetGlobalFlags
|
||||
SFileSetLocale
|
||||
SFileGetLocale
|
||||
|
||||
SFileOpenArchive
|
||||
SFileCreateArchive
|
||||
SFileGetArchiveBitmap
|
||||
SFileFlushArchive
|
||||
SFileCloseArchive
|
||||
|
||||
|
||||
@@ -4,13 +4,12 @@
|
||||
# swilkins1337@gmail.com
|
||||
#
|
||||
|
||||
_SFileGetGlobalFlags
|
||||
_SFileSetGlobalFlags
|
||||
_SFileSetLocale
|
||||
_SFileGetLocale
|
||||
|
||||
_SFileOpenArchive
|
||||
_SFileCreateArchive
|
||||
_SFileGetArchiveBitmap
|
||||
_SFileFlushArchive
|
||||
_SFileCloseArchive
|
||||
|
||||
|
||||
+263
-175
@@ -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;
|
||||
}
|
||||
Reference in new issue
Block a user