Added StormLib, nlohmann, BinaryTools and CLI11

This commit is contained in:
KiritoDv
2023-08-17 00:35:58 -06:00
commit 6fd8e5ee43
385 changed files with 134476 additions and 0 deletions
+24
View File
@@ -0,0 +1,24 @@
/*****************************************************************************/
/* DllMain.c Copyright (c) Ladislav Zezula 2006 */
/*---------------------------------------------------------------------------*/
/* Description: DllMain for the StormLib.dll library */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 23.11.06 1.00 Lad The first version of DllMain.c */
/*****************************************************************************/
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
//-----------------------------------------------------------------------------
// DllMain
BOOL WINAPI DllMain(HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved)
{
UNREFERENCED_PARAMETER(hInst);
UNREFERENCED_PARAMETER(dwReason);
UNREFERENCED_PARAMETER(lpReserved);
return TRUE;
}
+79
View File
@@ -0,0 +1,79 @@
;
; Export file for Windows
; Copyright (c) 2007-2010 Ladislav Zezula
; ladik@zezula.net
;
LIBRARY StormLib.dll
EXPORTS
SFileSetLocale
SFileGetLocale
SFileOpenArchive
SFileCreateArchive
SFileCreateArchive2
SFileFlushArchive
SFileCloseArchive
SFileAddListFile
SFileSetCompactCallback
SFileCompactArchive
SFileGetMaxFileCount
SFileSetMaxFileCount
SFileGetAttributes
SFileSetAttributes
SFileUpdateFileAttributes
SFileOpenPatchArchive
SFileIsPatchedArchive
SFileOpenFileEx
SFileGetFileSize
SFileSetFilePointer
SFileReadFile
SFileCloseFile
SFileHasFile
SFileGetFileName
SFileGetFileInfo
SFileExtractFile
SFileVerifyFile
SFileVerifyRawData
SFileVerifyArchive
SFileFindFirstFile
SFileFindNextFile
SFileFindClose
SListFileFindFirstFile
SListFileFindNextFile
SListFileFindClose
SFileEnumLocales
SFileCreateFile
SFileWriteFile
SFileFinishFile
SFileAddFileEx
SFileAddFile
SFileAddWave
SFileRemoveFile
SFileRenameFile
SFileSetFileLocale
SFileSetDataCompression
SFileSetAddFileCallback
SCompImplode
SCompExplode
SCompCompress
SCompDecompress
GetLastError=Kernel32.GetLastError
SetLastError=Kernel32.SetLastError
+114
View File
@@ -0,0 +1,114 @@
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// Neutral resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU)
#ifdef _WIN32
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
#pragma code_page(1250)
#endif //_WIN32
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 9,22,0,3
PRODUCTVERSION 9,22,0,3
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x0L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040504b0"
BEGIN
VALUE "Comments", "http://www.zezula.net/mpq.html"
VALUE "FileDescription", "StormLib library for reading Blizzard MPQ archives"
VALUE "FileVersion", "9, 22, 0, 3\0"
VALUE "InternalName", "StormLib"
VALUE "LegalCopyright", "Copyright (c) 2014 - 2020 Ladislav Zezula"
VALUE "OriginalFilename", "StormLib.dll"
VALUE "ProductName", "StormLib"
VALUE "ProductVersion", "9, 22, 0, 3\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x405, 1200
END
END
#endif // Neutral resources
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// Czech resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CSY)
#ifdef _WIN32
LANGUAGE LANG_CZECH, SUBLANG_DEFAULT
#pragma code_page(1250)
#endif //_WIN32
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
2 TEXTINCLUDE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
#endif // APSTUDIO_INVOKED
#endif // Czech resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
File diff suppressed because it is too large Load Diff
+217
View File
@@ -0,0 +1,217 @@
/*****************************************************************************/
/* 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 void (*STREAM_INIT)(
struct TFileStream * pStream // Pointer to an unopened stream
);
typedef bool (*STREAM_CREATE)(
struct TFileStream * pStream // Pointer to an unopened stream
);
typedef bool (*STREAM_OPEN)(
struct TFileStream * pStream, // Pointer to an unopened stream
const TCHAR * szFileName, // Pointer to file name to be open
DWORD dwStreamFlags // Stream flags
);
typedef bool (*STREAM_READ)(
struct TFileStream * pStream, // Pointer to an open stream
ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position
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_RESIZE)(
struct TFileStream * pStream, // Pointer to an open stream
ULONGLONG FileSize // New size for the file, in bytes
);
typedef bool (*STREAM_GETSIZE)(
struct TFileStream * pStream, // Pointer to an open stream
ULONGLONG * pFileSize // Receives the file size, in bytes
);
typedef bool (*STREAM_GETPOS)(
struct TFileStream * pStream, // Pointer to an open stream
ULONGLONG * pByteOffset // Pointer to store current file position
);
typedef void (*STREAM_CLOSE)(
struct TFileStream * pStream // Pointer to an open stream
);
typedef bool (*BLOCK_READ)(
struct TFileStream * pStream, // Pointer to a block-oriented stream
ULONGLONG StartOffset, // Byte offset of start of the block array
ULONGLONG EndOffset, // End offset (either end of the block or end of the file)
LPBYTE BlockBuffer, // Pointer to block-aligned buffer
DWORD BytesNeeded, // Number of bytes that are really needed
bool bAvailable // true if the block is available
);
typedef bool (*BLOCK_CHECK)(
struct TFileStream * pStream, // Pointer to a block-oriented stream
ULONGLONG BlockOffset // Offset of the file to check
);
typedef void (*BLOCK_SAVEMAP)(
struct TFileStream * pStream // Pointer to a block-oriented stream
);
//-----------------------------------------------------------------------------
// Local structures - partial file structure and bitmap footer
#define ID_FILE_BITMAP_FOOTER 0x33767470 // Signature of the file bitmap footer ('ptv3')
#define DEFAULT_BLOCK_SIZE 0x00004000 // Default size of the stream block
#define DEFAULT_BUILD_NUMBER 10958 // Build number for newly created partial MPQs
typedef struct _PART_FILE_HEADER
{
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;
typedef struct _FILE_BITMAP_FOOTER
{
DWORD Signature; // 'ptv3' (ID_FILE_BITMAP_FOOTER)
DWORD Version; // Unknown, seems to always have value of 3 (version?)
DWORD BuildNumber; // Game build number for that MPQ
DWORD MapOffsetLo; // Low 32-bits of the offset of the bit map
DWORD MapOffsetHi; // High 32-bits of the offset of the bit map
DWORD BlockSize; // Size of one block (usually 0x4000 bytes)
} FILE_BITMAP_FOOTER, *PFILE_BITMAP_FOOTER;
//-----------------------------------------------------------------------------
// Structure for file stream
union TBaseProviderData
{
struct
{
ULONGLONG FileSize; // Size of the file
ULONGLONG FilePos; // Current file position
ULONGLONG FileTime; // Last write time
HANDLE hFile; // File handle
} File;
struct
{
ULONGLONG FileSize; // Size of the file
ULONGLONG FilePos; // Current file position
ULONGLONG FileTime; // Last write time
LPBYTE pbFile; // Pointer to mapped view
} Map;
struct
{
ULONGLONG FileSize; // Size of the file
ULONGLONG FilePos; // Current file position
ULONGLONG FileTime; // Last write time
HANDLE hInternet; // Internet handle
HANDLE hConnect; // Connection to the internet server
} Http;
};
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_RESIZE StreamResize; // Pointer to function changing file size
STREAM_GETSIZE StreamGetSize; // Pointer to function returning file size
STREAM_GETPOS StreamGetPos; // Pointer to function that returns current file position
STREAM_CLOSE StreamClose; // Pointer to function closing the stream
// Block-oriented functions
BLOCK_READ BlockRead; // Pointer to function reading one or more blocks
BLOCK_CHECK BlockCheck; // Pointer to function checking whether the block is present
// Base provider functions
STREAM_CREATE BaseCreate; // Pointer to base create function
STREAM_OPEN BaseOpen; // Pointer to base open function
STREAM_READ BaseRead; // Read from the stream
STREAM_WRITE BaseWrite; // Write to the stream
STREAM_RESIZE BaseResize; // Pointer to function changing file size
STREAM_GETSIZE BaseGetSize; // Pointer to function returning file size
STREAM_GETPOS BaseGetPos; // Pointer to function that returns current file position
STREAM_CLOSE BaseClose; // Pointer to function closing the stream
// Base provider data (file size, file position)
TBaseProviderData Base;
// Stream provider data
TFileStream * pMaster; // Master stream (e.g. MPQ on a web server)
TCHAR * szFileName; // File name (self-relative pointer)
ULONGLONG StreamSize; // Stream size (can be less than file size)
ULONGLONG StreamPos; // Stream position
DWORD BuildNumber; // Game build number
DWORD dwFlags; // Stream flags
// Followed by stream provider data, with variable length
};
//-----------------------------------------------------------------------------
// Structures for block-oriented stream
struct TBlockStream : public TFileStream
{
SFILE_DOWNLOAD_CALLBACK pfnCallback; // Callback for downloading
void * FileBitmap; // Array of bits for file blocks
void * UserData; // User data to be passed to the download callback
DWORD BitmapSize; // Size of the file bitmap (in bytes)
DWORD BlockSize; // Size of one block, in bytes
DWORD BlockCount; // Number of data blocks in the file
DWORD IsComplete; // If nonzero, no blocks are missing
DWORD IsModified; // nonzero if the bitmap has been modified
};
//-----------------------------------------------------------------------------
// Structure for encrypted stream
#define MPQE_CHUNK_SIZE 0x40 // Size of one chunk to be decrypted
struct TEncryptedStream : public TBlockStream
{
BYTE Key[MPQE_CHUNK_SIZE]; // File key
};
#endif // __FILESTREAM_H__
File diff suppressed because it is too large Load Diff
+183
View File
@@ -0,0 +1,183 @@
/*****************************************************************************/
/* SBaseDumpData.cpp Copyright (c) Ladislav Zezula 2011 */
/*---------------------------------------------------------------------------*/
/* Description : */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 26.01.11 1.00 Lad The first version of SBaseDumpData.cpp */
/*****************************************************************************/
#define __STORMLIB_SELF__
#include "StormLib.h"
#include "StormCommon.h"
#ifdef __STORMLIB_DUMP_DATA__
void DumpMpqHeader(TMPQHeader * pHeader)
{
printf("== MPQ Header =================================\n");
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 = %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);
printf("ULONGLONG HashTableSize64 = %016llX\n", pHeader->HashTableSize64);
printf("ULONGLONG BlockTableSize64 = %016llX\n", pHeader->BlockTableSize64);
printf("ULONGLONG HiBlockTableSize64 = %016llX\n", pHeader->HiBlockTableSize64);
printf("ULONGLONG HetTableSize64 = %016llX\n", pHeader->HetTableSize64);
printf("ULONGLONG BetTableSize64 = %016llX\n", pHeader->BetTableSize64);
printf("DWORD dwRawChunkSize = %08X\n", pHeader->dwRawChunkSize);
printf("-----------------------------------------------\n\n");
}
void DumpHashTable(TMPQHash * pHashTable, DWORD dwHashTableSize)
{
DWORD i;
if(pHashTable == NULL || dwHashTableSize == 0)
return;
printf("== Hash Table =================================\n");
for(i = 0; i < dwHashTableSize; i++)
{
printf("[%08x] %08X %08X %04X %02X %08X\n", i,
pHashTable[i].dwName1,
pHashTable[i].dwName2,
pHashTable[i].Locale,
pHashTable[i].Platform,
pHashTable[i].dwBlockIndex);
}
printf("-----------------------------------------------\n\n");
}
void DumpHetAndBetTable(TMPQHetTable * pHetTable, TMPQBetTable * pBetTable)
{
DWORD i;
if(pHetTable == NULL || pBetTable == NULL)
return;
printf("== HET Header =================================\n");
printf("ULONGLONG AndMask64 = %016llX\n", pHetTable->AndMask64);
printf("ULONGLONG OrMask64 = %016llX\n", pHetTable->OrMask64);
printf("DWORD dwEntryCount = %08X\n", pHetTable->dwEntryCount);
printf("DWORD dwTotalCount = %08X\n", pHetTable->dwTotalCount);
printf("DWORD dwNameHashBitSize = %08X\n", pHetTable->dwNameHashBitSize);
printf("DWORD dwIndexSizeTotal = %08X\n", pHetTable->dwIndexSizeTotal);
printf("DWORD dwIndexSizeExtra = %08X\n", pHetTable->dwIndexSizeExtra);
printf("DWORD dwIndexSize = %08X\n", pHetTable->dwIndexSize);
printf("-----------------------------------------------\n\n");
printf("== BET Header =================================\n");
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 dwBitTotal_NameHash2 = %08X\n", pBetTable->dwBitTotal_NameHash2);
printf("DWORD dwBitExtra_NameHash2 = %08X\n", pBetTable->dwBitExtra_NameHash2);
printf("DWORD dwBitCount_NameHash2 = %08X\n", pBetTable->dwBitCount_NameHash2);
printf("DWORD dwEntryCount = %08X\n", pBetTable->dwEntryCount);
printf("DWORD dwFlagCount = %08X\n", pBetTable->dwFlagCount);
printf("-----------------------------------------------\n\n");
printf("== HET & Bet Table ======================================================================\n\n");
printf("HetIdx HetHash BetIdx BetHash ByteOffset FileSize CmpSize FlgIdx Flags \n");
printf("------ ------- ------ ---------------- ---------------- -------- -------- ------ --------\n");
for(i = 0; i < pHetTable->dwTotalCount; i++)
{
ULONGLONG ByteOffset = 0;
ULONGLONG BetHash = 0;
DWORD dwFileSize = 0;
DWORD dwCmpSize = 0;
DWORD dwFlagIndex = 0;
DWORD dwFlags = 0;
DWORD dwBetIndex = 0;
GetMPQBits(pHetTable->pBetIndexes, i * pHetTable->dwIndexSizeTotal,
pHetTable->dwIndexSize,
&dwBetIndex, 4);
if(dwBetIndex < pHetTable->dwTotalCount)
{
DWORD dwEntryIndex = pBetTable->dwTableEntrySize * dwBetIndex;
GetMPQBits(pBetTable->pNameHashes, dwBetIndex * pBetTable->dwBitTotal_NameHash2,
pBetTable->dwBitCount_NameHash2,
&BetHash, 8);
GetMPQBits(pBetTable->pFileTable, dwEntryIndex + pBetTable->dwBitIndex_FilePos,
pBetTable->dwBitCount_FilePos,
&ByteOffset, 8);
GetMPQBits(pBetTable->pFileTable, dwEntryIndex + pBetTable->dwBitIndex_FileSize,
pBetTable->dwBitCount_FileSize,
&dwFileSize, 4);
GetMPQBits(pBetTable->pFileTable, dwEntryIndex + pBetTable->dwBitIndex_CmpSize,
pBetTable->dwBitCount_CmpSize,
&dwCmpSize, 4);
GetMPQBits(pBetTable->pFileTable, dwEntryIndex + pBetTable->dwBitIndex_FlagIndex,
pBetTable->dwBitCount_FlagIndex,
&dwFlagIndex, 4);
dwFlags = pBetTable->pFileFlags[dwFlagIndex];
}
printf(" %04X %02lX %04X %016llX %016llX %08X %08X %04X %08X\n", i,
pHetTable->pNameHashes[i],
dwBetIndex,
BetHash,
ByteOffset,
dwFileSize,
dwCmpSize,
dwFlagIndex,
dwFlags);
}
printf("-----------------------------------------------------------------------------------------\n");
}
void DumpFileTable(TFileEntry * pFileTable, DWORD dwFileTableSize)
{
DWORD i;
if(pFileTable == NULL || dwFileTableSize == 0)
return;
printf("== File Table =================================\n");
for(i = 0; i < dwFileTableSize; i++, pFileTable++)
{
printf("[%04u] %08X-%08X %08X-%08X %08X-%08X 0x%08X 0x%08X 0x%08X %s\n", i,
(DWORD)(pFileTable->FileNameHash >> 0x20),
(DWORD)(pFileTable->FileNameHash & 0xFFFFFFFF),
(DWORD)(pFileTable->ByteOffset >> 0x20),
(DWORD)(pFileTable->ByteOffset & 0xFFFFFFFF),
(DWORD)(pFileTable->FileTime >> 0x20),
(DWORD)(pFileTable->FileTime & 0xFFFFFFFF),
pFileTable->dwFileSize,
pFileTable->dwCmpSize,
pFileTable->dwFlags,
pFileTable->szFileName != NULL ? pFileTable->szFileName : "");
}
printf("-----------------------------------------------\n\n");
}
#endif // __STORMLIB_DUMP_DATA__
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+277
View File
@@ -0,0 +1,277 @@
/*****************************************************************************/
/* SFileCreateArchive.cpp Copyright (c) Ladislav Zezula 2003 */
/*---------------------------------------------------------------------------*/
/* MPQ Editing functions */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 24.03.03 1.00 Lad Splitted from SFileOpenArchive.cpp */
/* 08.06.10 1.00 Lad Renamed to SFileCreateArchive.cpp */
/*****************************************************************************/
#define __STORMLIB_SELF__
#include "StormLib.h"
#include "StormCommon.h"
//-----------------------------------------------------------------------------
// Local variables
static const DWORD MpqHeaderSizes[] =
{
MPQ_HEADER_SIZE_V1,
MPQ_HEADER_SIZE_V2,
MPQ_HEADER_SIZE_V3,
MPQ_HEADER_SIZE_V4
};
//-----------------------------------------------------------------------------
// Local functions
static USHORT GetSectorSizeShift(DWORD dwSectorSize)
{
USHORT wSectorSizeShift = 0;
while(dwSectorSize > 0x200)
{
dwSectorSize >>= 1;
wSectorSizeShift++;
}
return wSectorSizeShift;
}
static DWORD WriteNakedMPQHeader(TMPQArchive * ha)
{
TMPQHeader * pHeader = ha->pHeader;
TMPQHeader Header;
DWORD dwBytesToWrite = pHeader->dwHeaderSize;
DWORD dwErrCode = ERROR_SUCCESS;
// Prepare the naked MPQ header
memset(&Header, 0, sizeof(TMPQHeader));
Header.dwID = pHeader->dwID;
Header.dwHeaderSize = pHeader->dwHeaderSize;
Header.dwArchiveSize = pHeader->dwHeaderSize;
Header.wFormatVersion = pHeader->wFormatVersion;
Header.wSectorSize = pHeader->wSectorSize;
// Write it to the file
BSWAP_TMPQHEADER(&Header, MPQ_FORMAT_VERSION_1);
BSWAP_TMPQHEADER(&Header, MPQ_FORMAT_VERSION_2);
BSWAP_TMPQHEADER(&Header, MPQ_FORMAT_VERSION_3);
BSWAP_TMPQHEADER(&Header, MPQ_FORMAT_VERSION_4);
if(!FileStream_Write(ha->pStream, &ha->MpqPos, &Header, dwBytesToWrite))
dwErrCode = GetLastError();
return dwErrCode;
}
//-----------------------------------------------------------------------------
// Creates a new MPQ archive.
bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwCreateFlags, DWORD dwMaxFileCount, HANDLE * phMpq)
{
SFILE_CREATE_MPQ CreateInfo;
// Fill the create structure
memset(&CreateInfo, 0, sizeof(SFILE_CREATE_MPQ));
CreateInfo.cbSize = sizeof(SFILE_CREATE_MPQ);
CreateInfo.dwMpqVersion = (dwCreateFlags & MPQ_CREATE_ARCHIVE_VMASK) >> FLAGS_TO_FORMAT_SHIFT;
CreateInfo.dwStreamFlags = STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE;
CreateInfo.dwFileFlags1 = (dwCreateFlags & MPQ_CREATE_LISTFILE) ? MPQ_FILE_DEFAULT_INTERNAL : 0;
CreateInfo.dwFileFlags2 = (dwCreateFlags & MPQ_CREATE_ATTRIBUTES) ? MPQ_FILE_DEFAULT_INTERNAL : 0;
CreateInfo.dwFileFlags3 = (dwCreateFlags & MPQ_CREATE_SIGNATURE) ? MPQ_FILE_DEFAULT_INTERNAL : 0;
CreateInfo.dwAttrFlags = (dwCreateFlags & MPQ_CREATE_ATTRIBUTES) ? (MPQ_ATTRIBUTE_CRC32 | MPQ_ATTRIBUTE_FILETIME | MPQ_ATTRIBUTE_MD5) : 0;
CreateInfo.dwSectorSize = (CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_3) ? 0x4000 : 0x1000;
CreateInfo.dwRawChunkSize = (CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_4) ? 0x4000 : 0;
CreateInfo.dwMaxFileCount = dwMaxFileCount;
// Set the proper attribute parts
if((CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_3) && (dwCreateFlags & MPQ_CREATE_ATTRIBUTES))
CreateInfo.dwAttrFlags |= MPQ_ATTRIBUTE_PATCH_BIT;
// Backward compatibility: SFileCreateArchive always used to add (listfile)
// We would break loads of applications if we change that
CreateInfo.dwFileFlags1 = MPQ_FILE_DEFAULT_INTERNAL;
// Let the main function create the archive
return SFileCreateArchive2(szMpqName, &CreateInfo, phMpq);
}
bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCreateInfo, HANDLE * phMpq)
{
TFileStream * pStream = NULL; // File stream
TMPQArchive * ha = NULL; // MPQ archive handle
TMPQHeader * pHeader;
ULONGLONG MpqPos = 0; // Position of MPQ header in the file
HANDLE hMpq = NULL;
DWORD dwBlockTableSize = 0; // Initial block table size
DWORD dwHashTableSize = 0;
DWORD dwReservedFiles = 0; // Number of reserved file entries
DWORD dwMpqFlags = 0;
DWORD dwErrCode = ERROR_SUCCESS;
// Check the parameters, if they are valid
if(szMpqName == NULL || *szMpqName == 0 || pCreateInfo == NULL || phMpq == NULL)
{
SetLastError(ERROR_INVALID_PARAMETER);
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)))
{
SetLastError(ERROR_INVALID_PARAMETER);
return false;
}
// One time initialization of MPQ cryptography
InitializeMpqCryptography();
// We verify if the file already exists and if it's a MPQ archive.
// If yes, we won't allow to overwrite it.
if(SFileOpenArchive(szMpqName, 0, STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE | MPQ_OPEN_NO_ATTRIBUTES | MPQ_OPEN_NO_LISTFILE, &hMpq))
{
SFileCloseArchive(hMpq);
SetLastError(ERROR_ALREADY_EXISTS);
return false;
}
//
// At this point, we have to create the archive.
// - If the file exists, convert it to MPQ archive.
// - If the file doesn't exist, create new empty file
//
pStream = FileStream_OpenFile(szMpqName, pCreateInfo->dwStreamFlags);
if(pStream == NULL)
{
pStream = FileStream_CreateFile(szMpqName, pCreateInfo->dwStreamFlags);
if(pStream == NULL)
return false;
}
// Increment the maximum amount of files to have space for (listfile)
if(pCreateInfo->dwMaxFileCount && pCreateInfo->dwFileFlags1)
{
dwMpqFlags |= MPQ_FLAG_LISTFILE_NEW;
dwReservedFiles++;
}
// Increment the maximum amount of files to have space for (attributes)
if(pCreateInfo->dwMaxFileCount && pCreateInfo->dwFileFlags2 && pCreateInfo->dwAttrFlags)
{
dwMpqFlags |= MPQ_FLAG_ATTRIBUTES_NEW;
dwReservedFiles++;
}
// Increment the maximum amount of files to have space for (signature)
if(pCreateInfo->dwMaxFileCount && pCreateInfo->dwFileFlags3)
{
dwMpqFlags |= MPQ_FLAG_SIGNATURE_NEW;
dwReservedFiles++;
}
// If file count is not zero, initialize the hash table size
dwHashTableSize = GetNearestPowerOfTwo(pCreateInfo->dwMaxFileCount + dwReservedFiles);
// Retrieve the file size and round it up to 0x200 bytes
FileStream_GetSize(pStream, &MpqPos);
MpqPos = (MpqPos + 0x1FF) & (ULONGLONG)0xFFFFFFFFFFFFFE00ULL;
if(!FileStream_SetSize(pStream, MpqPos))
dwErrCode = GetLastError();
#ifdef _DEBUG
// Debug code, used for testing StormLib
// dwBlockTableSize = dwHashTableSize * 2;
#endif
// Create the archive handle
if(dwErrCode == ERROR_SUCCESS)
{
if((ha = STORM_ALLOC(TMPQArchive, 1)) == NULL)
dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
}
// Fill the MPQ archive handle structure
if(dwErrCode == ERROR_SUCCESS)
{
memset(ha, 0, sizeof(TMPQArchive));
ha->pfnHashString = HashStringSlash;
ha->pStream = pStream;
ha->dwSectorSize = pCreateInfo->dwSectorSize;
ha->UserDataPos = MpqPos;
ha->MpqPos = MpqPos;
ha->pHeader = pHeader = (TMPQHeader *)ha->HeaderData;
ha->dwMaxFileCount = dwHashTableSize;
ha->dwFileTableSize = 0;
ha->dwReservedFiles = dwReservedFiles;
ha->dwValidFileFlags = (pCreateInfo->dwMpqVersion > 1) ? MPQ_FILE_VALID_FLAGS : MPQ_FILE_VALID_FLAGS_W3X;
ha->dwFileFlags1 = pCreateInfo->dwFileFlags1;
ha->dwFileFlags2 = pCreateInfo->dwFileFlags2;
ha->dwFileFlags3 = pCreateInfo->dwFileFlags3 ? MPQ_FILE_EXISTS : 0;
ha->dwAttrFlags = pCreateInfo->dwAttrFlags;
ha->dwFlags = dwMpqFlags | MPQ_FLAG_CHANGED;
ha->useFreeSpaceOptimization = true;
ha->lastFreeSpaceEntry = nullptr;
pStream = NULL;
// Fill the MPQ header
memset(pHeader, 0, sizeof(ha->HeaderData));
pHeader->dwID = g_dwMpqSignature;
pHeader->dwHeaderSize = MpqHeaderSizes[pCreateInfo->dwMpqVersion];
pHeader->dwArchiveSize = pHeader->dwHeaderSize + dwHashTableSize * sizeof(TMPQHash);
pHeader->wFormatVersion = (USHORT)pCreateInfo->dwMpqVersion;
pHeader->wSectorSize = GetSectorSizeShift(ha->dwSectorSize);
pHeader->dwHashTablePos = pHeader->dwHeaderSize;
pHeader->dwHashTableSize = dwHashTableSize;
pHeader->dwBlockTablePos = pHeader->dwHashTablePos + dwHashTableSize * sizeof(TMPQHash);
pHeader->dwBlockTableSize = dwBlockTableSize;
// For MPQs version 4 and higher, we set the size of raw data block
// for calculating MD5
if(pCreateInfo->dwMpqVersion >= MPQ_FORMAT_VERSION_4)
pHeader->dwRawChunkSize = pCreateInfo->dwRawChunkSize;
// Write the naked MPQ header
dwErrCode = WriteNakedMPQHeader(ha);
}
// Create initial HET table, if the caller required an MPQ format 3.0 or newer
if(dwErrCode == ERROR_SUCCESS && pCreateInfo->dwMpqVersion >= MPQ_FORMAT_VERSION_3 && pCreateInfo->dwMaxFileCount != 0)
{
ha->pHetTable = CreateHetTable(ha->dwFileTableSize, 0, 0x40, NULL);
if(ha->pHetTable == NULL)
dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
}
// Create initial hash table
if(dwErrCode == ERROR_SUCCESS && dwHashTableSize != 0)
{
dwErrCode = CreateHashTable(ha, dwHashTableSize);
}
// Create initial file table
if(dwErrCode == ERROR_SUCCESS && ha->dwMaxFileCount != 0)
{
dwErrCode = CreateFileTable(ha, ha->dwMaxFileCount);
}
// Cleanup : If an error, delete all buffers and return
if(dwErrCode != ERROR_SUCCESS)
{
FileStream_Close(pStream);
FreeArchiveHandle(ha);
SetLastError(dwErrCode);
ha = NULL;
}
// Return the values
*phMpq = (HANDLE)ha;
return (dwErrCode == ERROR_SUCCESS);
}
+64
View File
@@ -0,0 +1,64 @@
/*****************************************************************************/
/* SFileExtractFile.cpp Copyright (c) Ladislav Zezula 2003 */
/*---------------------------------------------------------------------------*/
/* Simple extracting utility */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 20.06.03 1.00 Lad The first version of SFileExtractFile.cpp */
/*****************************************************************************/
#define __STORMLIB_SELF__
#include "StormLib.h"
#include "StormCommon.h"
bool WINAPI SFileExtractFile(HANDLE hMpq, const char * szToExtract, const TCHAR * szExtracted, DWORD dwSearchScope)
{
TFileStream * pLocalFile = NULL;
HANDLE hMpqFile = NULL;
DWORD dwErrCode = ERROR_SUCCESS;
// Open the MPQ file
if(dwErrCode == ERROR_SUCCESS)
{
if(!SFileOpenFileEx(hMpq, szToExtract, dwSearchScope, &hMpqFile))
dwErrCode = GetLastError();
}
// Create the local file
if(dwErrCode == ERROR_SUCCESS)
{
pLocalFile = FileStream_CreateFile(szExtracted, 0);
if(pLocalFile == NULL)
dwErrCode = GetLastError();
}
// Copy the file's content
while(dwErrCode == ERROR_SUCCESS)
{
char szBuffer[0x1000];
DWORD dwTransferred = 0;
// dwTransferred is only set to nonzero if something has been read.
// dwErrCode can be ERROR_SUCCESS or ERROR_HANDLE_EOF
if(!SFileReadFile(hMpqFile, szBuffer, sizeof(szBuffer), &dwTransferred, NULL))
dwErrCode = GetLastError();
if(dwErrCode == ERROR_HANDLE_EOF)
dwErrCode = ERROR_SUCCESS;
if(dwTransferred == 0)
break;
// If something has been actually read, write it
if(!FileStream_Write(pLocalFile, NULL, szBuffer, dwTransferred))
dwErrCode = GetLastError();
}
// Close the files
if(hMpqFile != NULL)
SFileCloseFile(hMpqFile);
if(pLocalFile != NULL)
FileStream_Close(pLocalFile);
if(dwErrCode != ERROR_SUCCESS)
SetLastError(dwErrCode);
return (dwErrCode == ERROR_SUCCESS);
}
+481
View File
@@ -0,0 +1,481 @@
/*****************************************************************************/
/* SFileFindFile.cpp Copyright (c) Ladislav Zezula 2003 */
/*---------------------------------------------------------------------------*/
/* A module for file searching within MPQs */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 25.03.03 1.00 Lad The first version of SFileFindFile.cpp */
/*****************************************************************************/
#define __STORMLIB_SELF__
#include "StormLib.h"
#include "StormCommon.h"
//-----------------------------------------------------------------------------
// Private structure used for file search (search handle)
// Used by searching in MPQ archives
struct TMPQSearch
{
TMPQArchive * ha; // Handle to MPQ, where the search runs
TFileEntry ** pSearchTable; // Table for files that have been already found
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[1]; // Search mask (variable length)
};
//-----------------------------------------------------------------------------
// Local functions
static TMPQSearch * IsValidSearchHandle(HANDLE hFind)
{
TMPQSearch * hs = (TMPQSearch *)hFind;
if(hs != NULL && IsValidMpqHandle(hs->ha))
return hs;
return NULL;
}
bool SFileCheckWildCard(const char * szString, const char * szWildCard)
{
const char * szWildCardPtr;
for(;;)
{
// If there is '?' in the wildcard, we skip one char
while(szWildCard[0] == '?')
{
if(szString[0] == 0)
return false;
szWildCard++;
szString++;
}
// Handle '*'
szWildCardPtr = szWildCard;
if(szWildCardPtr[0] != 0)
{
if(szWildCardPtr[0] == '*')
{
while(szWildCardPtr[0] == '*')
szWildCardPtr++;
if(szWildCardPtr[0] == 0)
return true;
if(AsciiToUpperTable[szWildCardPtr[0]] == AsciiToUpperTable[szString[0]])
{
if(SFileCheckWildCard(szString, szWildCardPtr))
return true;
}
}
else
{
if(AsciiToUpperTable[szWildCardPtr[0]] != AsciiToUpperTable[szString[0]])
return false;
szWildCard = szWildCardPtr + 1;
}
if(szString[0] == 0)
return false;
szString++;
}
else
{
return (szString[0] == 0) ? true : false;
}
}
}
static DWORD GetSearchTableItems(TMPQArchive * ha)
{
DWORD dwMergeItems = 0;
// Loop over all patches
while(ha != NULL)
{
// Append the number of files
dwMergeItems += (ha->pHetTable != NULL) ? ha->pHetTable->dwEntryCount
: ha->pHeader->dwBlockTableSize;
// Move to the patched archive
ha = ha->haPatch;
}
// Return the double size of number of items
return (dwMergeItems | 1);
}
static bool FileWasFoundBefore(
TMPQArchive * ha,
TMPQSearch * hs,
TFileEntry * pFileEntry)
{
TFileEntry * pEntry;
char * szRealFileName = pFileEntry->szFileName;
DWORD dwStartIndex;
DWORD dwNameHash;
DWORD dwIndex;
if(hs->pSearchTable != NULL && szRealFileName != NULL)
{
// If we are in patch MPQ, we check if patch prefix matches
// and then trim the patch prefix
if(ha->pPatchPrefix != NULL)
{
// If the patch prefix doesn't fit, we pretend that the file
// was there before and it will be skipped
if(_strnicmp(szRealFileName, ha->pPatchPrefix->szPatchPrefix, ha->pPatchPrefix->nLength))
return true;
szRealFileName += ha->pPatchPrefix->nLength;
}
// Calculate the hash to the table
dwNameHash = ha->pfnHashString(szRealFileName, MPQ_HASH_NAME_A);
dwStartIndex = dwIndex = (dwNameHash % hs->dwSearchTableItems);
// The file might have been found before
// only if this is not the first MPQ being searched
if(ha->haBase != NULL)
{
// Enumerate all entries in the search table
for(;;)
{
// Get the file entry at that position
pEntry = hs->pSearchTable[dwIndex];
if(pEntry == NULL)
break;
if(pEntry->szFileName != NULL)
{
// Does the name match?
if(!_stricmp(pEntry->szFileName, szRealFileName))
return true;
}
// Move to the next entry
dwIndex = (dwIndex + 1) % hs->dwSearchTableItems;
if(dwIndex == dwStartIndex)
break;
}
}
// Put the entry to the table for later use
hs->pSearchTable[dwIndex] = pFileEntry;
}
return false;
}
static TFileEntry * FindPatchEntry(TMPQArchive * ha, TFileEntry * pFileEntry)
{
TFileEntry * pPatchEntry = pFileEntry;
TFileEntry * pTempEntry;
char szFileName[MAX_PATH+1];
// Can't find patch entry for a file that doesn't have name
if(pFileEntry->szFileName != NULL && pFileEntry->szFileName[0] != 0)
{
// Go while there are patches
while(ha->haPatch != NULL)
{
// Move to the patch archive
ha = ha->haPatch;
szFileName[0] = 0;
// Prepare the prefix for the file name
if(ha->pPatchPrefix && ha->pPatchPrefix->nLength)
StringCopy(szFileName, _countof(szFileName), ha->pPatchPrefix->szPatchPrefix);
StringCat(szFileName, _countof(szFileName), pFileEntry->szFileName);
// Try to find the file there
pTempEntry = GetFileEntryExact(ha, szFileName, 0, NULL);
if(pTempEntry != NULL)
pPatchEntry = pTempEntry;
}
}
// Return the found patch entry
return pPatchEntry;
}
static bool DoMPQSearch_FileEntry(
TMPQSearch * hs,
SFILE_FIND_DATA * lpFindFileData,
TMPQArchive * ha,
TMPQHash * pHashEntry,
TFileEntry * pFileEntry)
{
TFileEntry * pPatchEntry;
HANDLE hFile = NULL;
const char * szFileName;
size_t nPrefixLength = (ha->pPatchPrefix != NULL) ? ha->pPatchPrefix->nLength : 0;
DWORD dwBlockIndex;
char szNameBuff[MAX_PATH];
// Is it a file but not a patch file?
if((pFileEntry->dwFlags & hs->dwFlagMask) == MPQ_FILE_EXISTS)
{
// Ignore fake files which are not compressed but have size higher than the archive
if((pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) == 0 && (pFileEntry->dwFileSize > ha->FileSize))
return false;
// Now we have to check if this file was not enumerated before
if(!FileWasFoundBefore(ha, hs, pFileEntry))
{
// if(pFileEntry != NULL && !_stricmp(pFileEntry->szFileName, "TriggerLibs\\NativeLib.galaxy"))
// DebugBreak();
// Find a patch to this file
// Note: This either succeeds or returns pFileEntry
pPatchEntry = FindPatchEntry(ha, pFileEntry);
// Prepare the block index
dwBlockIndex = (DWORD)(pFileEntry - ha->pFileTable);
// Get the file name. If it's not known, we will create pseudo-name
szFileName = pFileEntry->szFileName;
if(szFileName == NULL)
{
// Open the file by its pseudo-name.
StringCreatePseudoFileName(szNameBuff, _countof(szNameBuff), dwBlockIndex, "xxx");
if(SFileOpenFileEx((HANDLE)hs->ha, szNameBuff, SFILE_OPEN_BASE_FILE, &hFile))
{
SFileGetFileName(hFile, szNameBuff);
szFileName = szNameBuff;
SFileCloseFile(hFile);
}
}
// If the file name is still NULL, we cannot include the file to search results
if(szFileName != NULL)
{
// Check the file name against the wildcard
if(SFileCheckWildCard(szFileName + nPrefixLength, hs->szSearchMask))
{
// Fill the found entry. hash entry and block index are taken from the base MPQ
lpFindFileData->dwHashIndex = HASH_ENTRY_FREE;
lpFindFileData->dwBlockIndex = dwBlockIndex;
lpFindFileData->dwFileSize = pPatchEntry->dwFileSize;
lpFindFileData->dwFileFlags = pPatchEntry->dwFlags;
lpFindFileData->dwCompSize = pPatchEntry->dwCmpSize;
lpFindFileData->lcLocale = 0; // pPatchEntry->lcFileLocale;
// Fill the filetime
lpFindFileData->dwFileTimeHi = (DWORD)(pPatchEntry->FileTime >> 32);
lpFindFileData->dwFileTimeLo = (DWORD)(pPatchEntry->FileTime);
// Fill-in the entries from hash table entry, if given
if(pHashEntry != NULL)
{
lpFindFileData->dwHashIndex = (DWORD)(pHashEntry - ha->pHashTable);
lpFindFileData->lcLocale = SFILE_MAKE_LCID(pHashEntry->Locale, pHashEntry->Platform);
}
// Fill the file name and plain file name
StringCopy(lpFindFileData->cFileName, _countof(lpFindFileData->cFileName), szFileName + nPrefixLength);
lpFindFileData->szPlainName = (char *)GetPlainFileName(lpFindFileData->cFileName);
return true;
}
}
}
}
// Either not a valid item or was found before
return false;
}
static DWORD DoMPQSearch_HashTable(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData, TMPQArchive * ha)
{
TMPQHash * pHashTableEnd = ha->pHashTable + ha->pHeader->dwHashTableSize;
TMPQHash * pHash;
// Parse the file table
for(pHash = ha->pHashTable + hs->dwNextIndex; pHash < pHashTableEnd; pHash++)
{
// Increment the next index for subsequent search
hs->dwNextIndex++;
// Does this hash table entry point to a proper block table entry?
if(IsValidHashEntry(ha, pHash))
{
// Check if this file entry should be included in the search result
if(DoMPQSearch_FileEntry(hs, lpFindFileData, ha, pHash, ha->pFileTable + MPQ_BLOCK_INDEX(pHash)))
return ERROR_SUCCESS;
}
}
// No more files
return ERROR_NO_MORE_FILES;
}
static DWORD DoMPQSearch_FileTable(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData, TMPQArchive * ha)
{
TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
TFileEntry * pFileEntry;
// Parse the file table
for(pFileEntry = ha->pFileTable + hs->dwNextIndex; pFileEntry < pFileTableEnd; pFileEntry++)
{
// Increment the next index for subsequent search
hs->dwNextIndex++;
// Check if this file entry should be included in the search result
if(DoMPQSearch_FileEntry(hs, lpFindFileData, ha, NULL, pFileEntry))
return ERROR_SUCCESS;
}
// No more files
return ERROR_NO_MORE_FILES;
}
// Performs one MPQ search
static DWORD DoMPQSearch(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData)
{
TMPQArchive * ha = hs->ha;
DWORD dwErrCode;
// Start searching with base MPQ
while(ha != NULL)
{
// If the archive has hash table, we need to use hash table
// in order to catch hash table index and file locale.
// Note: If multiple hash table entries, point to the same block entry,
// we need, to report them all
dwErrCode = (ha->pHashTable != NULL) ? DoMPQSearch_HashTable(hs, lpFindFileData, ha)
: DoMPQSearch_FileTable(hs, lpFindFileData, ha);
if(dwErrCode == ERROR_SUCCESS)
return dwErrCode;
// If there is no more patches in the chain, stop it.
// This also keeps hs->ha non-NULL, which is required
// for freeing the handle later
if(ha->haPatch == NULL)
break;
// Move to the next patch in the patch chain
hs->ha = ha = ha->haPatch;
hs->dwNextIndex = 0;
}
// No more files found, return error
return ERROR_NO_MORE_FILES;
}
static void FreeMPQSearch(TMPQSearch *& hs)
{
if(hs != NULL)
{
if(hs->pSearchTable != NULL)
STORM_FREE(hs->pSearchTable);
STORM_FREE(hs);
hs = NULL;
}
}
//-----------------------------------------------------------------------------
// Public functions
HANDLE WINAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_DATA * lpFindFileData, const TCHAR * szListFile)
{
TMPQArchive * ha = (TMPQArchive *)hMpq;
TMPQSearch * hs = NULL;
size_t nSize = 0;
DWORD dwErrCode = ERROR_SUCCESS;
// Check for the valid parameters
if(!IsValidMpqHandle(hMpq))
dwErrCode = ERROR_INVALID_HANDLE;
if(szMask == NULL || lpFindFileData == NULL)
dwErrCode = ERROR_INVALID_PARAMETER;
// Include the listfile into the MPQ's internal listfile
// Note that if the listfile name is NULL, do nothing because the
// internal listfile is always included.
if(dwErrCode == ERROR_SUCCESS && szListFile != NULL && *szListFile != 0)
dwErrCode = SFileAddListFile((HANDLE)ha, szListFile);
// Allocate the structure for MPQ search
if(dwErrCode == ERROR_SUCCESS)
{
nSize = sizeof(TMPQSearch) + strlen(szMask) + 1;
if((hs = (TMPQSearch *)STORM_ALLOC(char, nSize)) == NULL)
dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
}
// Perform the first search
if(dwErrCode == ERROR_SUCCESS)
{
memset(hs, 0, sizeof(TMPQSearch));
strcpy(hs->szSearchMask, szMask);
hs->dwFlagMask = MPQ_FILE_EXISTS;
hs->ha = ha;
// If the archive is patched archive, we have to create a merge table
// to prevent files being repeated
if(ha->haPatch != NULL)
{
hs->dwSearchTableItems = GetSearchTableItems(ha);
hs->pSearchTable = STORM_ALLOC(TFileEntry *, hs->dwSearchTableItems);
hs->dwFlagMask = MPQ_FILE_EXISTS | MPQ_FILE_PATCH_FILE;
if(hs->pSearchTable != NULL)
memset(hs->pSearchTable, 0, hs->dwSearchTableItems * sizeof(TFileEntry *));
else
dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
}
}
// Perform first item searching
if(dwErrCode == ERROR_SUCCESS)
{
dwErrCode = DoMPQSearch(hs, lpFindFileData);
}
// Cleanup
if(dwErrCode != ERROR_SUCCESS)
{
FreeMPQSearch(hs);
SetLastError(dwErrCode);
}
// Return the result value
return (HANDLE)hs;
}
bool WINAPI SFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData)
{
TMPQSearch * hs = IsValidSearchHandle(hFind);
DWORD dwErrCode = ERROR_SUCCESS;
// Check the parameters
if(hs == NULL)
dwErrCode = ERROR_INVALID_HANDLE;
if(lpFindFileData == NULL)
dwErrCode = ERROR_INVALID_PARAMETER;
if(dwErrCode == ERROR_SUCCESS)
dwErrCode = DoMPQSearch(hs, lpFindFileData);
if(dwErrCode != ERROR_SUCCESS)
SetLastError(dwErrCode);
return (dwErrCode == ERROR_SUCCESS);
}
bool WINAPI SFileFindClose(HANDLE hFind)
{
TMPQSearch * hs = IsValidSearchHandle(hFind);
// Check the parameters
if(hs == NULL)
{
SetLastError(ERROR_INVALID_HANDLE);
return false;
}
FreeMPQSearch(hs);
return true;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+418
View File
@@ -0,0 +1,418 @@
/*****************************************************************************/
/* SFileOpenFileEx.cpp Copyright (c) Ladislav Zezula 2003 */
/*---------------------------------------------------------------------------*/
/* Description : */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* xx.xx.99 1.00 Lad The first version of SFileOpenFileEx.cpp */
/*****************************************************************************/
#define __STORMLIB_SELF__
#include "StormLib.h"
#include "StormCommon.h"
/*****************************************************************************/
/* Local functions */
/*****************************************************************************/
static DWORD FindHashIndex(TMPQArchive * ha, DWORD dwFileIndex)
{
TMPQHash * pHashTableEnd;
TMPQHash * pHash;
DWORD dwFirstIndex = HASH_ENTRY_FREE;
// Should only be called if the archive has hash table
assert(ha->pHashTable != NULL);
// Multiple hash table entries can point to the file table entry.
// We need to search all of them
pHashTableEnd = ha->pHashTable + ha->pHeader->dwHashTableSize;
for(pHash = ha->pHashTable; pHash < pHashTableEnd; pHash++)
{
if(MPQ_BLOCK_INDEX(pHash) == dwFileIndex)
{
// Duplicate hash entry found
if(dwFirstIndex != HASH_ENTRY_FREE)
return HASH_ENTRY_FREE;
dwFirstIndex = (DWORD)(pHash - ha->pHashTable);
}
}
// Return the hash table entry index
return dwFirstIndex;
}
static const char * GetPatchFileName(TMPQArchive * ha, const char * szFileName, char * szBuffer)
{
TMPQNamePrefix * pPrefix;
// Are there patches in the current MPQ?
if(ha->dwFlags & MPQ_FLAG_PATCH)
{
// The patch prefix must be already known here
assert(ha->pPatchPrefix != NULL);
pPrefix = ha->pPatchPrefix;
// The patch name for "OldWorld\\XXX\\YYY" is "Base\\XXX\YYY"
// We need to remove the "OldWorld\\" prefix
if(!_strnicmp(szFileName, "OldWorld\\", 9))
szFileName += 9;
// Create the file name from the known patch entry
memcpy(szBuffer, pPrefix->szPatchPrefix, pPrefix->nLength);
strcpy(szBuffer + pPrefix->nLength, szFileName);
szFileName = szBuffer;
}
return szFileName;
}
static bool OpenLocalFile(const char * szFileName, HANDLE * PtrFile)
{
TFileStream * pStream;
TMPQFile * hf = NULL;
TCHAR szFileNameT[MAX_PATH];
// Convert the file name to UNICODE (if needed)
StringCopy(szFileNameT, _countof(szFileNameT), szFileName);
// Open the file and create the TMPQFile structure
pStream = FileStream_OpenFile(szFileNameT, STREAM_FLAG_READ_ONLY);
if(pStream != NULL)
{
// Allocate and initialize file handle
hf = CreateFileHandle(NULL, NULL);
if(hf != NULL)
{
hf->pStream = pStream;
*PtrFile = hf;
return true;
}
else
{
FileStream_Close(pStream);
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
}
}
*PtrFile = NULL;
return false;
}
bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, HANDLE * PtrFile)
{
TMPQArchive * haBase = NULL;
TMPQArchive * ha = (TMPQArchive *)hMpq;
TFileEntry * pFileEntry;
TMPQFile * hfPatch; // Pointer to patch file
TMPQFile * hfBase = NULL; // Pointer to base open file
TMPQFile * hf = NULL;
HANDLE hPatchFile;
char szNameBuffer[MAX_PATH];
// First of all, find the latest archive where the file is in base version
// (i.e. where the original, unpatched version of the file exists)
while(ha != NULL)
{
// If the file is there, then we remember the archive
pFileEntry = GetFileEntryExact(ha, GetPatchFileName(ha, szFileName, szNameBuffer), 0, NULL);
if(pFileEntry != NULL && (pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0)
haBase = ha;
// Move to the patch archive
ha = ha->haPatch;
}
// If we couldn't find the base file in any of the patches, it doesn't exist
if((ha = haBase) != NULL)
{
// Now open the base file
if(SFileOpenFileEx((HANDLE)ha, GetPatchFileName(ha, szFileName, szNameBuffer), SFILE_OPEN_BASE_FILE, (HANDLE *)&hfBase))
{
// The file must be a base file, i.e. without MPQ_FILE_PATCH_FILE
assert((hfBase->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0);
hf = hfBase;
// Now open all patches and attach them on top of the base file
for(ha = ha->haPatch; ha != NULL; ha = ha->haPatch)
{
// Prepare the file name with a correct prefix
if(SFileOpenFileEx((HANDLE)ha, GetPatchFileName(ha, szFileName, szNameBuffer), SFILE_OPEN_BASE_FILE, &hPatchFile))
{
// Remember the new version
hfPatch = (TMPQFile *)hPatchFile;
// We should not find patch file
assert((hfPatch->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) != 0);
// Attach the patch to the base file
hf->hfPatch = hfPatch;
hf = hfPatch;
}
}
}
}
else
{
SetLastError(ERROR_FILE_NOT_FOUND);
}
// Give the updated base MPQ
if(PtrFile != NULL)
*PtrFile = (HANDLE)hfBase;
return (hfBase != NULL);
}
/*****************************************************************************/
/* Public functions */
/*****************************************************************************/
//-----------------------------------------------------------------------------
// SFileEnumLocales enums all locale versions within MPQ.
// Functions fills all available language identifiers on a file into the buffer
// pointed by PtrFileLocales. There must be enough entries to copy the localed,
// otherwise the function returns ERROR_INSUFFICIENT_BUFFER.
DWORD WINAPI SFileEnumLocales(
HANDLE hMpq,
const char * szFileName,
LCID * PtrFileLocales,
LPDWORD PtrMaxLocales,
DWORD dwSearchScope)
{
TMPQArchive * ha = (TMPQArchive *)hMpq;
TMPQHash * pFirstHash;
TMPQHash * pHash;
DWORD dwFileIndex = 0;
DWORD dwMaxLocales;
DWORD dwLocales = 0;
// Test the parameters
if(!IsValidMpqHandle(hMpq))
return ERROR_INVALID_HANDLE;
if(szFileName == NULL || *szFileName == 0)
return ERROR_INVALID_PARAMETER;
if(ha->pHashTable == NULL)
return ERROR_NOT_SUPPORTED;
if(PtrMaxLocales == NULL)
return ERROR_INVALID_PARAMETER;
if(IsPseudoFileName(szFileName, &dwFileIndex))
return ERROR_INVALID_PARAMETER;
// Keep compiler happy
dwMaxLocales = PtrMaxLocales[0];
dwSearchScope = dwSearchScope;
// Parse all files with that name
pFirstHash = pHash = GetFirstHashEntry(ha, szFileName);
while(pHash != NULL)
{
// Put the locales to the buffer
if(PtrFileLocales != NULL && dwLocales < dwMaxLocales)
*PtrFileLocales++ = SFILE_MAKE_LCID(pHash->Locale, pHash->Platform);
dwLocales++;
// Get the next locale
pHash = GetNextHashEntry(ha, pFirstHash, pHash);
}
// Give the caller the number of locales and return
PtrMaxLocales[0] = dwLocales;
return (dwLocales <= dwMaxLocales) ? ERROR_SUCCESS : ERROR_INSUFFICIENT_BUFFER;
}
//-----------------------------------------------------------------------------
// SFileOpenFileEx
//
// hMpq - Handle of opened MPQ archive
// szFileName - Name of file to open
// dwSearchScope - Where to search
// PtrFile - Pointer to store opened file handle
bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope, HANDLE * PtrFile)
{
TMPQArchive * ha = IsValidMpqHandle(hMpq);
TFileEntry * pFileEntry = NULL;
TMPQFile * hf = NULL;
DWORD dwHashIndex = HASH_ENTRY_FREE;
DWORD dwFileIndex = 0;
DWORD dwErrCode = ERROR_SUCCESS;
bool bOpenByIndex = false;
// Don't accept NULL pointer to file handle
if(szFileName == NULL || *szFileName == 0)
dwErrCode = ERROR_INVALID_PARAMETER;
// When opening a file from MPQ, the handle must be valid
if(dwSearchScope != SFILE_OPEN_LOCAL_FILE && ha == NULL)
dwErrCode = ERROR_INVALID_HANDLE;
// When not checking for existence, the pointer to file handle must be valid
if(dwSearchScope != SFILE_OPEN_CHECK_EXISTS && PtrFile == NULL)
dwErrCode = ERROR_INVALID_PARAMETER;
// Prepare the file opening
if(dwErrCode == ERROR_SUCCESS)
{
switch(dwSearchScope)
{
case SFILE_OPEN_FROM_MPQ:
case SFILE_OPEN_BASE_FILE:
case SFILE_OPEN_CHECK_EXISTS:
// If this MPQ has no patches, open the file from this MPQ directly
if(ha->haPatch == NULL || dwSearchScope == SFILE_OPEN_BASE_FILE)
{
pFileEntry = GetFileEntryLocale(ha, szFileName, g_lcFileLocale, &dwHashIndex);
}
// If this MPQ is a patched archive, open the file as patched
else
{
return OpenPatchedFile(hMpq, szFileName, PtrFile);
}
break;
case SFILE_OPEN_ANY_LOCALE:
// This open option is reserved for opening MPQ internal listfile.
// No argument validation. Tries to open file with neutral locale first,
// then any other available.
pFileEntry = GetFileEntryLocale(ha, szFileName, 0, &dwHashIndex);
break;
case SFILE_OPEN_LOCAL_FILE:
// Open a local file
return OpenLocalFile(szFileName, PtrFile);
default:
// Don't accept any other value
dwErrCode = ERROR_INVALID_PARAMETER;
break;
}
}
// Check whether the file really exists in the MPQ
if(dwErrCode == ERROR_SUCCESS)
{
// If we didn't find the file, try to open it using pseudo file name ("File
if (pFileEntry == NULL || (pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0)
{
// Check the pseudo-file name ("File00000001.ext")
if ((bOpenByIndex = IsPseudoFileName(szFileName, &dwFileIndex)) == true)
{
// Get the file entry for the file
if (dwFileIndex < ha->dwFileTableSize)
{
pFileEntry = ha->pFileTable + dwFileIndex;
}
}
// Still not found?
if (pFileEntry == NULL)
{
dwErrCode = ERROR_FILE_NOT_FOUND;
}
}
// Perform some checks of invalid files
if (pFileEntry != NULL)
{
// MPQ protectors use insanely amount of fake files, often with very high size.
// We won't open any files whose compressed size is bigger than archive size
// If the file is not compressed, its size cannot be bigger than archive size
if ((pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) == 0 && (pFileEntry->dwFileSize > ha->FileSize))
{
dwErrCode = ERROR_FILE_CORRUPT;
pFileEntry = NULL;
}
// Ignore unknown loading flags (example: MPQ_2016_v1_WME4_4.w3x)
// if(pFileEntry->dwFlags & ~MPQ_FILE_VALID_FLAGS)
// {
// dwErrCode = ERROR_NOT_SUPPORTED;
// pFileEntry = NULL;
// }
}
}
// Did the caller just wanted to know if the file exists?
if(dwErrCode == ERROR_SUCCESS && dwSearchScope != SFILE_OPEN_CHECK_EXISTS)
{
// Allocate file handle
hf = CreateFileHandle(ha, pFileEntry);
if(hf != NULL)
{
// Get the hash index for the file
if(ha->pHashTable != NULL && dwHashIndex == HASH_ENTRY_FREE)
dwHashIndex = FindHashIndex(ha, dwFileIndex);
if(dwHashIndex != HASH_ENTRY_FREE)
hf->pHashEntry = ha->pHashTable + dwHashIndex;
hf->dwHashIndex = dwHashIndex;
// If the MPQ has sector CRC enabled, enable if for the file
if(ha->dwFlags & MPQ_FLAG_CHECK_SECTOR_CRC)
hf->bCheckSectorCRCs = true;
// If we know the real file name, copy it to the file entry
if(bOpenByIndex == false)
{
// If there is no file name yet, allocate it
AllocateFileName(ha, pFileEntry, szFileName);
// If the file is encrypted, we should detect the file key
if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)
{
hf->dwFileKey = DecryptFileKey(szFileName,
pFileEntry->ByteOffset,
pFileEntry->dwFileSize,
pFileEntry->dwFlags);
}
}
}
else
{
dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
}
}
// Give the file entry
if(PtrFile != NULL)
PtrFile[0] = hf;
// Return error code
if(dwErrCode != ERROR_SUCCESS)
SetLastError(dwErrCode);
return (dwErrCode == ERROR_SUCCESS);
}
//-----------------------------------------------------------------------------
// SFileHasFile
//
// hMpq - Handle of opened MPQ archive
// szFileName - Name of file to look for
bool WINAPI SFileHasFile(HANDLE hMpq, const char * szFileName)
{
return SFileOpenFileEx(hMpq, szFileName, SFILE_OPEN_CHECK_EXISTS, NULL);
}
//-----------------------------------------------------------------------------
// bool WINAPI SFileCloseFile(HANDLE hFile);
bool WINAPI SFileCloseFile(HANDLE hFile)
{
TMPQFile * hf = (TMPQFile *)hFile;
if(!IsValidFileHandle(hFile))
{
SetLastError(ERROR_INVALID_HANDLE);
return false;
}
// Free the structure
FreeFileHandle(hf);
return true;
}

Some files were not shown because too many files have changed in this diff Show More