Browse Source

Add StormLib

pull/4/head
nomdenom 8 years ago
parent
commit
ede09dfa6e
  1. 21
      3rdParty/StormLib/LICENSE
  2. 11
      3rdParty/StormLib/README
  3. 2830
      3rdParty/StormLib/src/FileStream.cpp
  4. 217
      3rdParty/StormLib/src/FileStream.h
  5. 1856
      3rdParty/StormLib/src/SBaseCommon.cpp
  6. 2962
      3rdParty/StormLib/src/SBaseFileTable.cpp
  7. 618
      3rdParty/StormLib/src/SBaseSubTypes.cpp
  8. 1166
      3rdParty/StormLib/src/SCompression.cpp
  9. 64
      3rdParty/StormLib/src/SFileExtractFile.cpp
  10. 481
      3rdParty/StormLib/src/SFileFindFile.cpp
  11. 1010
      3rdParty/StormLib/src/SFileGetFileInfo.cpp
  12. 618
      3rdParty/StormLib/src/SFileOpenArchive.cpp
  13. 396
      3rdParty/StormLib/src/SFileOpenFileEx.cpp
  14. 907
      3rdParty/StormLib/src/SFileReadFile.cpp
  15. 387
      3rdParty/StormLib/src/StormCommon.h
  16. 1134
      3rdParty/StormLib/src/StormLib.h
  17. 309
      3rdParty/StormLib/src/StormPort.h

21
3rdParty/StormLib/LICENSE vendored

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 1999-2013 Ladislav Zezula
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

11
3rdParty/StormLib/README vendored

@ -0,0 +1,11 @@
This is a slightly modified version of ladislav-zezula/StormLib@4ad0bff21da0163917e1552960e6a43679586b89
Cosmetic changes:
* Remove all unused files.
* Comment out unnecessary bits with `#ifndef FULL`.
Implementation changes:
* Redefine `bool` to `BOOL`.
* Use `stdcall` calling convention.
* Use external Storm error handling (`SErrGetLastError()` etc.)
* Convert pathnames to Unix-style slashes.

2830
3rdParty/StormLib/src/FileStream.cpp vendored

File diff suppressed because it is too large Load Diff

217
3rdParty/StormLib/src/FileStream.h vendored

@ -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__

1856
3rdParty/StormLib/src/SBaseCommon.cpp vendored

File diff suppressed because it is too large Load Diff

2962
3rdParty/StormLib/src/SBaseFileTable.cpp vendored

File diff suppressed because it is too large Load Diff

618
3rdParty/StormLib/src/SBaseSubTypes.cpp vendored

@ -0,0 +1,618 @@
/*****************************************************************************/
/* SBaseSubTypes.cpp Copyright (c) Ladislav Zezula 2013 */
/*---------------------------------------------------------------------------*/
/* Conversion routines for archive formats that are similar to MPQ format */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 02.11.11 1.00 Lad The first version of SBaseSubTypes.cpp */
/*****************************************************************************/
#define __STORMLIB_SELF__
#include "StormLib.h"
#include "StormCommon.h"
/*****************************************************************************/
/* */
/* Support for SQP file format (War of the Immortals) */
/* */
/*****************************************************************************/
typedef struct _TSQPHeader
{
// The ID_MPQ ('MPQ\x1A') signature
DWORD dwID;
// Size of the archive header
DWORD dwHeaderSize;
// 32-bit size of MPQ archive
DWORD dwArchiveSize;
// Offset to the beginning of the hash table, relative to the beginning of the archive.
DWORD dwHashTablePos;
// Offset to the beginning of the block table, relative to the beginning of the archive.
DWORD dwBlockTablePos;
// Number of entries in the hash table. Must be a power of two, and must be less than 2^16 for
// the original MoPaQ format, or less than 2^20 for the Burning Crusade format.
DWORD dwHashTableSize;
// Number of entries in the block table
DWORD dwBlockTableSize;
// Must be zero for SQP files
USHORT wFormatVersion;
// Power of two exponent specifying the number of 512-byte disk sectors in each file sector
// in the archive. The size of each file sector in the archive is 512 * 2 ^ wSectorSize.
USHORT wSectorSize;
} TSQPHeader;
typedef struct _TSQPHash
{
// Most likely the lcLocale+wPlatform.
DWORD dwAlwaysZero;
// If the hash table entry is valid, this is the index into the block table of the file.
// Otherwise, one of the following two values:
// - FFFFFFFFh: Hash table entry is empty, and has always been empty.
// Terminates searches for a given file.
// - FFFFFFFEh: Hash table entry is empty, but was valid at some point (a deleted file).
// Does not terminate searches for a given file.
DWORD dwBlockIndex;
// The hash of the file path, using method A.
DWORD dwName1;
// The hash of the file path, using method B.
DWORD dwName2;
} TSQPHash;
typedef struct _TSQPBlock
{
// Offset of the beginning of the file, relative to the beginning of the archive.
DWORD dwFilePos;
// Flags for the file. See MPQ_FILE_XXXX constants
DWORD dwFlags;
// Compressed file size
DWORD dwCSize;
// Uncompressed file size
DWORD dwFSize;
} TSQPBlock;
//-----------------------------------------------------------------------------
// Functions - SQP file format
// This function converts SQP file header into MPQ file header
int ConvertSqpHeaderToFormat4(
TMPQArchive * ha,
ULONGLONG FileSize,
DWORD dwFlags)
{
TSQPHeader * pSqpHeader = (TSQPHeader *)ha->HeaderData;
TMPQHeader Header;
// SQP files from War of the Immortal use MPQ file format with slightly
// modified structure. These fields have different position:
//
// Offset TMPQHeader TSQPHeader
// ------ ---------- -----------
// 000C wFormatVersion dwHashTablePos (lo)
// 000E wSectorSize dwHashTablePos (hi)
// 001C dwBlockTableSize (lo) wBlockSize
// 001E dwHashTableSize (hi) wFormatVersion
// Can't open the archive with certain flags
if(dwFlags & MPQ_OPEN_FORCE_MPQ_V1)
return ERROR_FILE_CORRUPT;
// The file must not be greater than 4 GB
if((FileSize >> 0x20) != 0)
return ERROR_FILE_CORRUPT;
// Translate the SQP header into a MPQ header
memset(&Header, 0, sizeof(TMPQHeader));
Header.dwID = BSWAP_INT32_UNSIGNED(pSqpHeader->dwID);
Header.dwHeaderSize = BSWAP_INT32_UNSIGNED(pSqpHeader->dwHeaderSize);
Header.dwArchiveSize = BSWAP_INT32_UNSIGNED(pSqpHeader->dwArchiveSize);
Header.dwHashTablePos = BSWAP_INT32_UNSIGNED(pSqpHeader->dwHashTablePos);
Header.dwBlockTablePos = BSWAP_INT32_UNSIGNED(pSqpHeader->dwBlockTablePos);
Header.dwHashTableSize = BSWAP_INT32_UNSIGNED(pSqpHeader->dwHashTableSize);
Header.dwBlockTableSize = BSWAP_INT32_UNSIGNED(pSqpHeader->dwBlockTableSize);
Header.wFormatVersion = BSWAP_INT16_UNSIGNED(pSqpHeader->wFormatVersion);
Header.wSectorSize = BSWAP_INT16_UNSIGNED(pSqpHeader->wSectorSize);
// Verify the SQP header
if(Header.dwID == ID_MPQ && Header.dwHeaderSize == sizeof(TSQPHeader) && Header.dwArchiveSize == FileSize)
{
// Check for fixed values of version and sector size
if(Header.wFormatVersion == MPQ_FORMAT_VERSION_1 && Header.wSectorSize == 3)
{
// Initialize the fields of 3.0 header
Header.ArchiveSize64 = Header.dwArchiveSize;
Header.HashTableSize64 = Header.dwHashTableSize * sizeof(TMPQHash);
Header.BlockTableSize64 = Header.dwBlockTableSize * sizeof(TMPQBlock);
// Copy the converted MPQ header back
memcpy(ha->HeaderData, &Header, sizeof(TMPQHeader));
// Mark this file as SQP file
ha->pfnHashString = HashStringSlash;
ha->dwFlags |= MPQ_FLAG_READ_ONLY;
ha->dwSubType = MPQ_SUBTYPE_SQP;
return ERROR_SUCCESS;
}
}
return ERROR_FILE_CORRUPT;
}
void * LoadSqpTable(TMPQArchive * ha, DWORD dwByteOffset, DWORD cbTableSize, DWORD dwKey)
{
ULONGLONG ByteOffset;
LPBYTE pbSqpTable;
// Allocate buffer for the table
pbSqpTable = STORM_ALLOC(BYTE, cbTableSize);
if(pbSqpTable != NULL)
{
// Load the table
ByteOffset = ha->MpqPos + dwByteOffset;
if(FileStream_Read(ha->pStream, &ByteOffset, pbSqpTable, cbTableSize))
{
// Decrypt the SQP table
DecryptMpqBlock(pbSqpTable, cbTableSize, dwKey);
return pbSqpTable;
}
// Free the table
STORM_FREE(pbSqpTable);
}
return NULL;
}
TMPQHash * LoadSqpHashTable(TMPQArchive * ha)
{
TMPQHeader * pHeader = ha->pHeader;
TSQPHash * pSqpHashTable;
TSQPHash * pSqpHashEnd;
TSQPHash * pSqpHash;
TMPQHash * pMpqHash;
int nError = ERROR_SUCCESS;
// Load the hash table
pSqpHashTable = (TSQPHash *)LoadSqpTable(ha, pHeader->dwHashTablePos, pHeader->dwHashTableSize * sizeof(TSQPHash), MPQ_KEY_HASH_TABLE);
if(pSqpHashTable != NULL)
{
// Parse the entire hash table and convert it to MPQ hash table
pSqpHashEnd = pSqpHashTable + pHeader->dwHashTableSize;
pMpqHash = (TMPQHash *)pSqpHashTable;
for(pSqpHash = pSqpHashTable; pSqpHash < pSqpHashEnd; pSqpHash++, pMpqHash++)
{
// Ignore free entries
if(pSqpHash->dwBlockIndex != HASH_ENTRY_FREE)
{
// Check block index against the size of the block table
if(pHeader->dwBlockTableSize <= MPQ_BLOCK_INDEX(pSqpHash) && pSqpHash->dwBlockIndex < HASH_ENTRY_DELETED)
nError = ERROR_FILE_CORRUPT;
// We do not support nonzero locale and platform ID
if(pSqpHash->dwAlwaysZero != 0 && pSqpHash->dwAlwaysZero != HASH_ENTRY_FREE)
nError = ERROR_FILE_CORRUPT;
// Store the file name hash
pMpqHash->dwName1 = pSqpHash->dwName1;
pMpqHash->dwName2 = pSqpHash->dwName2;
// Store the rest. Note that this must be done last,
// because block index corresponds to pMpqHash->dwName2
pMpqHash->dwBlockIndex = MPQ_BLOCK_INDEX(pSqpHash);
pMpqHash->Platform = 0;
pMpqHash->lcLocale = 0;
}
}
// If an error occured, we need to free the hash table
if(nError != ERROR_SUCCESS)
{
STORM_FREE(pSqpHashTable);
pSqpHashTable = NULL;
}
}
// Return the converted hash table (or NULL on failure)
return (TMPQHash *)pSqpHashTable;
}
// Loads the SQP Block table and converts it to a MPQ block table
TMPQBlock * LoadSqpBlockTable(TMPQArchive * ha)
{
TMPQHeader * pHeader = ha->pHeader;
TSQPBlock * pSqpBlockTable;
TSQPBlock * pSqpBlockEnd;
TSQPBlock * pSqpBlock;
TMPQBlock * pMpqBlock;
DWORD dwFlags;
int nError = ERROR_SUCCESS;
// Load the hash table
pSqpBlockTable = (TSQPBlock *)LoadSqpTable(ha, pHeader->dwBlockTablePos, pHeader->dwBlockTableSize * sizeof(TSQPBlock), MPQ_KEY_BLOCK_TABLE);
if(pSqpBlockTable != NULL)
{
// Parse the entire hash table and convert it to MPQ hash table
pSqpBlockEnd = pSqpBlockTable + pHeader->dwBlockTableSize;
pMpqBlock = (TMPQBlock *)pSqpBlockTable;
for(pSqpBlock = pSqpBlockTable; pSqpBlock < pSqpBlockEnd; pSqpBlock++, pMpqBlock++)
{
// Check for valid flags
if(pSqpBlock->dwFlags & ~MPQ_FILE_VALID_FLAGS)
nError = ERROR_FILE_CORRUPT;
// Convert SQP block table entry to MPQ block table entry
dwFlags = pSqpBlock->dwFlags;
pMpqBlock->dwCSize = pSqpBlock->dwCSize;
pMpqBlock->dwFSize = pSqpBlock->dwFSize;
pMpqBlock->dwFlags = dwFlags;
}
// If an error occured, we need to free the hash table
if(nError != ERROR_SUCCESS)
{
STORM_FREE(pSqpBlockTable);
pSqpBlockTable = NULL;
}
}
// Return the converted hash table (or NULL on failure)
return (TMPQBlock *)pSqpBlockTable;
}
/*****************************************************************************/
/* */
/* Support for MPK file format (Longwu Online) */
/* */
/*****************************************************************************/
#define MPK_FILE_UNKNOWN_0001 0x00000001 // Seems to be always present
#define MPK_FILE_UNKNOWN_0010 0x00000010 // Seems to be always present
#define MPK_FILE_COMPRESSED 0x00000100 // Indicates a compressed file
#define MPK_FILE_UNKNOWN_2000 0x00002000 // Seems to be always present
#define MPK_FILE_EXISTS 0x01000000 // Seems to be always present
typedef struct _TMPKHeader
{
// The ID_MPK ('MPK\x1A') signature
DWORD dwID;
// Contains '2000'
DWORD dwVersion;
// 32-bit size of the archive
DWORD dwArchiveSize;
// Size of the archive header
DWORD dwHeaderSize;
DWORD dwHashTablePos;
DWORD dwHashTableSize;
DWORD dwBlockTablePos;
DWORD dwBlockTableSize;
DWORD dwUnknownPos;
DWORD dwUnknownSize;
} TMPKHeader;
typedef struct _TMPKHash
{
// The hash of the file path, using method A.
DWORD dwName1;
// The hash of the file path, using method B.
DWORD dwName2;
// The hash of the file path, using method C.
DWORD dwName3;
// If the hash table entry is valid, this is the index into the block table of the file.
// Otherwise, one of the following two values:
// - FFFFFFFFh: Hash table entry is empty, and has always been empty.
// Terminates searches for a given file.
// - FFFFFFFEh: Hash table entry is empty, but was valid at some point (a deleted file).
// Does not terminate searches for a given file.
DWORD dwBlockIndex;
} TMPKHash;
typedef struct _TMPKBlock
{
DWORD dwFlags; // 0x1121 - Compressed , 0x1120 - Not compressed
DWORD dwFilePos; // Offset of the beginning of the file, relative to the beginning of the archive.
DWORD dwFSize; // Uncompressed file size
DWORD dwCSize; // Compressed file size
DWORD dwUnknown; // 0x86364E6D
} TMPKBlock;
//-----------------------------------------------------------------------------
// Local variables - MPK file format
static const unsigned char MpkDecryptionKey[512] =
{
0x60, 0x20, 0x29, 0xE1, 0x01, 0xCE, 0xAA, 0xFE, 0xA3, 0xAB, 0x8E, 0x30, 0xAF, 0x02, 0xD1, 0x7D,
0x41, 0x24, 0x06, 0xBD, 0xAE, 0xBE, 0x43, 0xC3, 0xBA, 0xB7, 0x08, 0x13, 0x51, 0xCF, 0xF8, 0xF7,
0x25, 0x42, 0xA5, 0x4A, 0xDA, 0x0F, 0x52, 0x1C, 0x90, 0x3B, 0x63, 0x49, 0x36, 0xF6, 0xDD, 0x1B,
0xEA, 0x58, 0xD4, 0x40, 0x70, 0x61, 0x55, 0x09, 0xCD, 0x0B, 0xA2, 0x4B, 0x68, 0x2C, 0x8A, 0xF1,
0x3C, 0x3A, 0x65, 0xBB, 0xA1, 0xA8, 0x23, 0x97, 0xFD, 0x15, 0x00, 0x94, 0x88, 0x33, 0x59, 0xE9,
0xFB, 0x69, 0x21, 0xEF, 0x85, 0x5B, 0x57, 0x6C, 0xFA, 0xB5, 0xEE, 0xB8, 0x71, 0xDC, 0xB1, 0x38,
0x0C, 0x0A, 0x5C, 0x56, 0xC9, 0xB4, 0x84, 0x17, 0x1E, 0xE5, 0xD3, 0x5A, 0xCC, 0xFC, 0x11, 0x86,
0x7F, 0x45, 0x4F, 0x54, 0xC8, 0x8D, 0x73, 0x89, 0x79, 0x5D, 0xB3, 0xBF, 0xB9, 0xE3, 0x93, 0xE4,
0x6F, 0x35, 0x2D, 0x46, 0xF2, 0x76, 0xC5, 0x7E, 0xE2, 0xA4, 0xE6, 0xD9, 0x6E, 0x48, 0x34, 0x2B,
0xC6, 0x5F, 0xBC, 0xA0, 0x6D, 0x0D, 0x47, 0x6B, 0x95, 0x96, 0x92, 0x91, 0xB2, 0x27, 0xEB, 0x9E,
0xEC, 0x8F, 0xDF, 0x9C, 0x74, 0x99, 0x64, 0xF5, 0xFF, 0x28, 0xB6, 0x37, 0xF3, 0x7C, 0x81, 0x03,
0x44, 0x62, 0x1F, 0xDB, 0x04, 0x7B, 0xB0, 0x9B, 0x31, 0xA7, 0xDE, 0x78, 0x9F, 0xAD, 0x0E, 0x3F,
0x3E, 0x4D, 0xC7, 0xD7, 0x39, 0x19, 0x5E, 0xC2, 0xD0, 0xAC, 0xE8, 0x1A, 0x87, 0x8B, 0x07, 0x05,
0x22, 0xED, 0x72, 0x2E, 0x1D, 0xC1, 0xA9, 0xD6, 0xE0, 0x83, 0xD5, 0xD8, 0xCB, 0x80, 0xF0, 0x66,
0x7A, 0x9D, 0x50, 0xF9, 0x10, 0x4E, 0x16, 0x14, 0x77, 0x75, 0x6A, 0x67, 0xD2, 0xC0, 0xA6, 0xC4,
0x53, 0x8C, 0x32, 0xCA, 0x82, 0x2A, 0x18, 0x9A, 0xF4, 0x4C, 0x3D, 0x26, 0x12, 0xE7, 0x98, 0x2F,
0x4A, 0x04, 0x0D, 0xAF, 0xB4, 0xCF, 0x12, 0xCE, 0x1A, 0x37, 0x61, 0x39, 0x60, 0x95, 0xBE, 0x25,
0xE4, 0x6E, 0xFC, 0x1B, 0xE7, 0x49, 0xE6, 0x67, 0xF6, 0xC5, 0xCB, 0x2F, 0x27, 0xD4, 0x68, 0xB2,
0x01, 0x52, 0xD0, 0x46, 0x11, 0x20, 0xFB, 0x9D, 0xA9, 0x02, 0xF5, 0x8F, 0x3D, 0x82, 0xD3, 0xFF,
0x0B, 0xB8, 0xF2, 0x4D, 0x8E, 0x81, 0x2C, 0xAB, 0x5F, 0xC4, 0x41, 0x29, 0x40, 0xFA, 0xC0, 0xBF,
0x33, 0x10, 0x21, 0x16, 0xB0, 0x71, 0x83, 0x96, 0x8D, 0x2B, 0x23, 0x3B, 0xF9, 0xC1, 0xE5, 0x72,
0xE2, 0x1C, 0x26, 0xF0, 0x73, 0x36, 0x63, 0x56, 0x31, 0x4E, 0x6B, 0x55, 0x62, 0x79, 0xC6, 0x91,
0x00, 0x35, 0xB1, 0x2A, 0xA6, 0x42, 0xDF, 0xEB, 0x3C, 0x51, 0xEA, 0x97, 0x57, 0x94, 0x8C, 0x80,
0x34, 0x5C, 0xD2, 0x76, 0xA4, 0xE9, 0x85, 0xE8, 0xBB, 0x78, 0xE0, 0xB5, 0xAD, 0x0F, 0x87, 0x70,
0xDD, 0xAE, 0xF4, 0xD9, 0x66, 0x54, 0x6F, 0xCC, 0x4C, 0x77, 0x3E, 0xCD, 0xF1, 0x75, 0x0A, 0xA1,
0x28, 0x9B, 0x9A, 0x7E, 0x4B, 0x98, 0x99, 0x47, 0xFE, 0xA5, 0xF7, 0xB7, 0xA3, 0xE1, 0x9F, 0xBC,
0x93, 0x44, 0x3A, 0x08, 0x89, 0x22, 0xEE, 0xB9, 0x45, 0xD6, 0x06, 0x09, 0xC9, 0xBD, 0x14, 0x0C,
0xB6, 0x5E, 0x9C, 0x7A, 0x65, 0x59, 0xAA, 0x19, 0x5B, 0x7C, 0x18, 0x43, 0x92, 0x13, 0x15, 0x7B,
0xED, 0xD5, 0xC7, 0x17, 0xEF, 0x86, 0x90, 0xC2, 0x74, 0x64, 0xF3, 0xDC, 0x6C, 0x38, 0x05, 0x1D,
0xC8, 0x0E, 0xEC, 0x6A, 0x32, 0xDA, 0xD7, 0xC3, 0xDB, 0x8B, 0x24, 0xB3, 0x5D, 0x2E, 0xBA, 0xA2,
0xD8, 0x03, 0x88, 0x7D, 0x7F, 0x69, 0x8A, 0xFD, 0xCA, 0x4F, 0x30, 0x9E, 0xA0, 0xD1, 0x5A, 0x53,
0xDE, 0x3F, 0x84, 0xAC, 0xF8, 0xA7, 0x2D, 0x1F, 0x1E, 0xE3, 0x58, 0x50, 0x6D, 0x48, 0x07, 0xA8
};
//-----------------------------------------------------------------------------
// Functions - MPK file format
// This function converts MPK file header into MPQ file header
int ConvertMpkHeaderToFormat4(
TMPQArchive * ha,
ULONGLONG FileSize,
DWORD dwFlags)
{
TMPKHeader * pMpkHeader = (TMPKHeader *)ha->HeaderData;
TMPQHeader Header;
// Can't open the archive with certain flags
if(dwFlags & MPQ_OPEN_FORCE_MPQ_V1)
return ERROR_FILE_CORRUPT;
// Translate the MPK header into a MPQ header
// Note: Hash table size and block table size are in bytes, not in entries
memset(&Header, 0, sizeof(TMPQHeader));
Header.dwID = BSWAP_INT32_UNSIGNED(pMpkHeader->dwID);
Header.dwArchiveSize = BSWAP_INT32_UNSIGNED(pMpkHeader->dwArchiveSize);
Header.dwHeaderSize = BSWAP_INT32_UNSIGNED(pMpkHeader->dwHeaderSize);
Header.dwHashTablePos = BSWAP_INT32_UNSIGNED(pMpkHeader->dwHashTablePos);
Header.dwHashTableSize = BSWAP_INT32_UNSIGNED(pMpkHeader->dwHashTableSize) / sizeof(TMPKHash);
Header.dwBlockTablePos = BSWAP_INT32_UNSIGNED(pMpkHeader->dwBlockTablePos);
Header.dwBlockTableSize = BSWAP_INT32_UNSIGNED(pMpkHeader->dwBlockTableSize) / sizeof(TMPKBlock);
// Header.dwUnknownPos = BSWAP_INT32_UNSIGNED(pMpkHeader->dwUnknownPos);
// Header.dwUnknownSize = BSWAP_INT32_UNSIGNED(pMpkHeader->dwUnknownSize);
assert(Header.dwHeaderSize == sizeof(TMPKHeader));
// Verify the MPK header
if(Header.dwID == ID_MPK && Header.dwHeaderSize == sizeof(TMPKHeader) && Header.dwArchiveSize == (DWORD)FileSize)
{
// The header ID must be ID_MPQ
Header.dwID = ID_MPQ;
Header.wFormatVersion = MPQ_FORMAT_VERSION_1;
Header.wSectorSize = 3;
// Initialize the fields of 3.0 header
Header.ArchiveSize64 = Header.dwArchiveSize;
Header.HashTableSize64 = Header.dwHashTableSize * sizeof(TMPQHash);
Header.BlockTableSize64 = Header.dwBlockTableSize * sizeof(TMPQBlock);
// Copy the converted MPQ header back
memcpy(ha->HeaderData, &Header, sizeof(TMPQHeader));
// Mark this file as MPK file
ha->pfnHashString = HashStringLower;
ha->dwFlags |= MPQ_FLAG_READ_ONLY;
ha->dwSubType = MPQ_SUBTYPE_MPK;
return ERROR_SUCCESS;
}
return ERROR_FILE_CORRUPT;
}
// Attempts to search a free hash entry in the hash table being converted.
// The created hash table must always be of nonzero size,
// should have no duplicated items and no deleted entries
TMPQHash * FindFreeHashEntry(TMPQHash * pHashTable, DWORD dwHashTableSize, DWORD dwStartIndex)
{
TMPQHash * pHash;
DWORD dwIndex;
// Set the initial index
dwStartIndex = dwIndex = (dwStartIndex & (dwHashTableSize - 1));
assert(dwHashTableSize != 0);
// Search the hash table and return the found entries in the following priority:
for(;;)
{
// We are not expecting to find matching entry in the hash table being built
// We are not expecting to find deleted entry either
pHash = pHashTable + dwIndex;
// If we found a free entry, we need to stop searching
if(pHash->dwBlockIndex == HASH_ENTRY_FREE)
return pHash;
// Move to the next hash entry.
// If we reached the starting entry, it's failure.
dwIndex = (dwIndex + 1) & (dwHashTableSize - 1);
if(dwIndex == dwStartIndex)
break;
}
// We haven't found anything
assert(false);
return NULL;
}
void DecryptMpkTable(void * pvMpkTable, size_t cbSize)
{
LPBYTE pbMpkTable = (LPBYTE)pvMpkTable;
for(size_t i = 0; i < cbSize; i++)
pbMpkTable[i] = MpkDecryptionKey[pbMpkTable[i]];
}
void * LoadMpkTable(TMPQArchive * ha, DWORD dwByteOffset, DWORD cbTableSize)
{
ULONGLONG ByteOffset;
LPBYTE pbMpkTable = NULL;
// Allocate space for the table
pbMpkTable = STORM_ALLOC(BYTE, cbTableSize);
if(pbMpkTable != NULL)
{
// Load and the MPK hash table
ByteOffset = ha->MpqPos + dwByteOffset;
if(FileStream_Read(ha->pStream, &ByteOffset, pbMpkTable, cbTableSize))
{
// Decrypt the table
DecryptMpkTable(pbMpkTable, cbTableSize);
return pbMpkTable;
}
// Free the MPK table
STORM_FREE(pbMpkTable);
pbMpkTable = NULL;
}
// Return the table
return pbMpkTable;
}
TMPQHash * LoadMpkHashTable(TMPQArchive * ha)
{
TMPQHeader * pHeader = ha->pHeader;
TMPQHash * pHashTable = NULL;
TMPKHash * pMpkHash;
TMPQHash * pHash = NULL;
DWORD dwHashTableSize = pHeader->dwHashTableSize;
// MPKs use different hash table searching.
// Instead of using MPQ_HASH_TABLE_INDEX hash as index,
// they store the value directly in the hash table.
// Also for faster searching, the hash table is sorted ascending by the value
// Load and decrypt the MPK hash table.
pMpkHash = (TMPKHash *)LoadMpkTable(ha, pHeader->dwHashTablePos, pHeader->dwHashTableSize * sizeof(TMPKHash));
if(pMpkHash != NULL)
{
// Calculate the hash table size as if it was real MPQ hash table
pHeader->dwHashTableSize = GetNearestPowerOfTwo(pHeader->dwHashTableSize);
pHeader->HashTableSize64 = pHeader->dwHashTableSize * sizeof(TMPQHash);
// Now allocate table that will serve like a true MPQ hash table,
// so we translate the MPK hash table to MPQ hash table
pHashTable = STORM_ALLOC(TMPQHash, pHeader->dwHashTableSize);
if(pHashTable != NULL)
{
// Set the entire hash table to free
memset(pHashTable, 0xFF, (size_t)pHeader->HashTableSize64);
// Copy the MPK hash table into MPQ hash table
for(DWORD i = 0; i < dwHashTableSize; i++)
{
// Finds the free hash entry in the hash table
// We don't expect any errors here, because we are putting files to empty hash table
pHash = FindFreeHashEntry(pHashTable, pHeader->dwHashTableSize, pMpkHash[i].dwName1);
assert(pHash->dwBlockIndex == HASH_ENTRY_FREE);
// Copy the MPK hash entry to the hash table
pHash->dwBlockIndex = pMpkHash[i].dwBlockIndex;
pHash->Platform = 0;
pHash->lcLocale = 0;
pHash->dwName1 = pMpkHash[i].dwName2;
pHash->dwName2 = pMpkHash[i].dwName3;
}
}
// Free the temporary hash table
STORM_FREE(pMpkHash);
}
return pHashTable;
}
static DWORD ConvertMpkFlagsToMpqFlags(DWORD dwMpkFlags)
{
DWORD dwMpqFlags = MPQ_FILE_EXISTS;
// Check for flags that are always present
assert((dwMpkFlags & MPK_FILE_UNKNOWN_0001) != 0);
assert((dwMpkFlags & MPK_FILE_UNKNOWN_0010) != 0);
assert((dwMpkFlags & MPK_FILE_UNKNOWN_2000) != 0);
assert((dwMpkFlags & MPK_FILE_EXISTS) != 0);
// Append the compressed flag
dwMpqFlags |= (dwMpkFlags & MPK_FILE_COMPRESSED) ? MPQ_FILE_COMPRESS : 0;
// All files in the MPQ seem to be single unit files
dwMpqFlags |= MPQ_FILE_ENCRYPTED | MPQ_FILE_SINGLE_UNIT;
return dwMpqFlags;
}
TMPQBlock * LoadMpkBlockTable(TMPQArchive * ha)
{
TMPQHeader * pHeader = ha->pHeader;
TMPKBlock * pMpkBlockTable;
TMPKBlock * pMpkBlockEnd;
TMPQBlock * pBlockTable = NULL;
TMPKBlock * pMpkBlock;
TMPQBlock * pMpqBlock;
// Load and decrypt the MPK block table
pMpkBlockTable = pMpkBlock = (TMPKBlock *)LoadMpkTable(ha, pHeader->dwBlockTablePos, pHeader->dwBlockTableSize * sizeof(TMPKBlock));
if(pMpkBlockTable != NULL)
{
// Allocate buffer for MPQ-like block table
pBlockTable = pMpqBlock = STORM_ALLOC(TMPQBlock, pHeader->dwBlockTableSize);
if(pBlockTable != NULL)
{
// Convert the MPK block table to MPQ block table
pMpkBlockEnd = pMpkBlockTable + pHeader->dwBlockTableSize;
while(pMpkBlock < pMpkBlockEnd)
{
// Translate the MPK block table entry to MPQ block table entry
pMpqBlock->dwFilePos = pMpkBlock->dwFilePos;
pMpqBlock->dwCSize = pMpkBlock->dwCSize;
pMpqBlock->dwFSize = pMpkBlock->dwFSize;
pMpqBlock->dwFlags = ConvertMpkFlagsToMpqFlags(pMpkBlock->dwFlags);
// Move both
pMpkBlock++;
pMpqBlock++;
}
}
// Free the MPK block table
STORM_FREE(pMpkBlockTable);
}
return pBlockTable;
}

1166
3rdParty/StormLib/src/SCompression.cpp vendored

File diff suppressed because it is too large Load Diff

64
3rdParty/StormLib/src/SFileExtractFile.cpp vendored

@ -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;
int nError = ERROR_SUCCESS;
// Open the MPQ file
if(nError == ERROR_SUCCESS)
{
if(!SFileOpenFileEx(hMpq, szToExtract, dwSearchScope, &hMpqFile))
nError = GetLastError();
}
// Create the local file
if(nError == ERROR_SUCCESS)
{
pLocalFile = FileStream_CreateFile(szExtracted, 0);
if(pLocalFile == NULL)
nError = GetLastError();
}
// Copy the file's content
while(nError == ERROR_SUCCESS)
{
char szBuffer[0x1000];
DWORD dwTransferred = 0;
// dwTransferred is only set to nonzero if something has been read.
// nError can be ERROR_SUCCESS or ERROR_HANDLE_EOF
if(!SFileReadFile(hMpqFile, szBuffer, sizeof(szBuffer), &dwTransferred, NULL))
nError = GetLastError();
if(nError == ERROR_HANDLE_EOF)
nError = ERROR_SUCCESS;
if(dwTransferred == 0)
break;
// If something has been actually read, write it
if(!FileStream_Write(pLocalFile, NULL, szBuffer, dwTransferred))
nError = GetLastError();
}
// Close the files
if(hMpqFile != NULL)
SFileCloseFile(hMpqFile);
if(pLocalFile != NULL)
FileStream_Close(pLocalFile);
if(nError != ERROR_SUCCESS)
SetLastError(nError);
return (nError == ERROR_SUCCESS);
}

481
3rdParty/StormLib/src/SFileFindFile.cpp vendored

@ -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 CheckWildCard(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] == '*')
{
szWildCardPtr++;
if(szWildCardPtr[0] == '*')
continue;
if(szWildCardPtr[0] == 0)
return true;
if(AsciiToUpperTable[szWildCardPtr[0]] == AsciiToUpperTable[szString[0]])
{
if(CheckWildCard(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)
{
// 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.
sprintf(szNameBuff, "File%08u.xxx", (unsigned int)dwBlockIndex);
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(CheckWildCard(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->lcLocale;
// 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 = pHashEntry->lcLocale;
}
// 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 int 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 int DoMPQSearch_FileTable(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData, TMPQArchive * ha)
{
TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
TFileEntry * pFileEntry;
// Parse the file table
for(pFileEntry = ha->pFileTable + hs->dwNextIndex; pFileEntry < pFileTableEnd; pFileEntry++)
{
// Increment the next index for subsequent search
hs->dwNextIndex++;
// Check if this file entry should be included in the search result
if(DoMPQSearch_FileEntry(hs, lpFindFileData, ha, NULL, pFileEntry))
return ERROR_SUCCESS;
}
// No more files
return ERROR_NO_MORE_FILES;
}
// Performs one MPQ search
static int DoMPQSearch(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData)
{
TMPQArchive * ha = hs->ha;
int nError;
// 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
nError = (ha->pHashTable != NULL) ? DoMPQSearch_HashTable(hs, lpFindFileData, ha)
: DoMPQSearch_FileTable(hs, lpFindFileData, ha);
if(nError == ERROR_SUCCESS)
return nError;
// 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;
int nError = ERROR_SUCCESS;
// Check for the valid parameters
if(!IsValidMpqHandle(hMpq))
nError = ERROR_INVALID_HANDLE;
if(szMask == NULL || lpFindFileData == NULL)
nError = ERROR_INVALID_PARAMETER;
#ifdef FULL
// Include the listfile into the MPQ's internal listfile
// Note that if the listfile name is NULL, do nothing because the
// internal listfile is always included.
if(nError == ERROR_SUCCESS && szListFile != NULL && *szListFile != 0)
nError = SFileAddListFile((HANDLE)ha, szListFile);
#endif
// Allocate the structure for MPQ search
if(nError == ERROR_SUCCESS)
{
nSize = sizeof(TMPQSearch) + strlen(szMask) + 1;
if((hs = (TMPQSearch *)STORM_ALLOC(char, nSize)) == NULL)
nError = ERROR_NOT_ENOUGH_MEMORY;
}
// Perform the first search
if(nError == 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
nError = ERROR_NOT_ENOUGH_MEMORY;
}
}
// Perform first item searching
if(nError == ERROR_SUCCESS)
{
nError = DoMPQSearch(hs, lpFindFileData);
}
// Cleanup
if(nError != ERROR_SUCCESS)
{
FreeMPQSearch(hs);
SetLastError(nError);
}
// Return the result value
return (HANDLE)hs;
}
bool WINAPI SFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData)
{
TMPQSearch * hs = IsValidSearchHandle(hFind);
int nError = ERROR_SUCCESS;
// Check the parameters
if(hs == NULL)
nError = ERROR_INVALID_HANDLE;
if(lpFindFileData == NULL)
nError = ERROR_INVALID_PARAMETER;
if(nError == ERROR_SUCCESS)
nError = DoMPQSearch(hs, lpFindFileData);
if(nError != ERROR_SUCCESS)
SetLastError(nError);
return (nError == 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;
}

1010
3rdParty/StormLib/src/SFileGetFileInfo.cpp vendored

File diff suppressed because it is too large Load Diff

618
3rdParty/StormLib/src/SFileOpenArchive.cpp vendored

@ -0,0 +1,618 @@
/*****************************************************************************/
/* SFileOpenArchive.cpp Copyright Ladislav Zezula 1999 */
/* */
/* Author : Ladislav Zezula */
/* E-mail : ladik@zezula.net */
/* WWW : www.zezula.net */
/*---------------------------------------------------------------------------*/
/* Archive functions of Storm.dll */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* xx.xx.xx 1.00 Lad The first version of SFileOpenArchive.cpp */
/* 19.11.03 1.01 Dan Big endian handling */
/*****************************************************************************/
#define __STORMLIB_SELF__
#include "StormLib.h"
#include "StormCommon.h"
#define HEADER_SEARCH_BUFFER_SIZE 0x1000
/*****************************************************************************/
/* Local functions */
/*****************************************************************************/
static bool IsAviFile(DWORD * HeaderData)
{
DWORD DwordValue0 = BSWAP_INT32_UNSIGNED(HeaderData[0]);
DWORD DwordValue2 = BSWAP_INT32_UNSIGNED(HeaderData[2]);
DWORD DwordValue3 = BSWAP_INT32_UNSIGNED(HeaderData[3]);
// Test for 'RIFF', 'AVI ' or 'LIST'
return (DwordValue0 == 0x46464952 && DwordValue2 == 0x20495641 && DwordValue3 == 0x5453494C);
}
static bool IsWarcraft3Map(DWORD * HeaderData)
{
DWORD DwordValue0 = BSWAP_INT32_UNSIGNED(HeaderData[0]);
DWORD DwordValue1 = BSWAP_INT32_UNSIGNED(HeaderData[1]);
return (DwordValue0 == 0x57334D48 && DwordValue1 == 0x00000000);
}
static TMPQUserData * IsValidMpqUserData(ULONGLONG ByteOffset, ULONGLONG FileSize, void * pvUserData)
{
TMPQUserData * pUserData;
// BSWAP the source data and copy them to our buffer
BSWAP_ARRAY32_UNSIGNED(&pvUserData, sizeof(TMPQUserData));
pUserData = (TMPQUserData *)pvUserData;
// Check the sizes
if(pUserData->cbUserDataHeader <= pUserData->cbUserDataSize && pUserData->cbUserDataSize <= pUserData->dwHeaderOffs)
{
// Move to the position given by the userdata
ByteOffset += pUserData->dwHeaderOffs;
// The MPQ header should be within range of the file size
if((ByteOffset + MPQ_HEADER_SIZE_V1) < FileSize)
{
// Note: We should verify if there is the MPQ header.
// However, the header could be at any position below that
// that is multiplier of 0x200
return (TMPQUserData *)pvUserData;
}
}
return NULL;
}
// This function gets the right positions of the hash table and the block table.
static int VerifyMpqTablePositions(TMPQArchive * ha, ULONGLONG FileSize)
{
TMPQHeader * pHeader = ha->pHeader;
ULONGLONG ByteOffset;
// Check the begin of HET table
if(pHeader->HetTablePos64)
{
ByteOffset = ha->MpqPos + pHeader->HetTablePos64;
if(ByteOffset > FileSize)
return ERROR_BAD_FORMAT;
}
// Check the begin of BET table
if(pHeader->BetTablePos64)
{
ByteOffset = ha->MpqPos + pHeader->BetTablePos64;
if(ByteOffset > FileSize)
return ERROR_BAD_FORMAT;
}
// Check the begin of hash table
if(pHeader->wHashTablePosHi || pHeader->dwHashTablePos)
{
ByteOffset = FileOffsetFromMpqOffset(ha, MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos));
if(ByteOffset > FileSize)
return ERROR_BAD_FORMAT;
}
// Check the begin of block table
if(pHeader->wBlockTablePosHi || pHeader->dwBlockTablePos)
{
ByteOffset = FileOffsetFromMpqOffset(ha, MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos));
if(ByteOffset > FileSize)
return ERROR_BAD_FORMAT;
}
// Check the begin of hi-block table
if(pHeader->HiBlockTablePos64 != 0)
{
ByteOffset = ha->MpqPos + pHeader->HiBlockTablePos64;
if(ByteOffset > FileSize)
return ERROR_BAD_FORMAT;
}
// All OK.
return ERROR_SUCCESS;
}
/*****************************************************************************/
/* Public functions */
/*****************************************************************************/
//-----------------------------------------------------------------------------
// SFileGetLocale and SFileSetLocale
// Set the locale for all newly opened files
LCID WINAPI SFileGetLocale()
{
return lcFileLocale;
}
LCID WINAPI SFileSetLocale(LCID lcNewLocale)
{
lcFileLocale = lcNewLocale;
return lcFileLocale;
}
//-----------------------------------------------------------------------------
// SFileOpenArchive
//
// szFileName - MPQ archive file name to open
// dwPriority - When SFileOpenFileEx called, this contains the search priority for searched archives
// dwFlags - See MPQ_OPEN_XXX in StormLib.h
// phMpq - Pointer to store open archive handle
bool WINAPI SFileOpenArchive(
const TCHAR * szMpqName,
DWORD dwPriority,
DWORD dwFlags,
HANDLE * phMpq)
{
TMPQUserData * pUserData;
TFileStream * pStream = NULL; // Open file stream
TMPQArchive * ha = NULL; // Archive handle
TFileEntry * pFileEntry;
ULONGLONG FileSize = 0; // Size of the file
LPBYTE pbHeaderBuffer = NULL; // Buffer for searching MPQ header
DWORD dwStreamFlags = (dwFlags & STREAM_FLAGS_MASK);
bool bIsWarcraft3Map = false;
int nError = ERROR_SUCCESS;
// Verify the parameters
if(szMpqName == NULL || *szMpqName == 0 || phMpq == NULL)
{
SetLastError(ERROR_INVALID_PARAMETER);
return false;
}
// One time initialization of MPQ cryptography
InitializeMpqCryptography();
dwPriority = dwPriority;
// If not forcing MPQ v 1.0, also use file bitmap
dwStreamFlags |= (dwFlags & MPQ_OPEN_FORCE_MPQ_V1) ? 0 : STREAM_FLAG_USE_BITMAP;
#ifndef FULL
char translatedName[260];
TranslateFileName(translatedName, sizeof(translatedName), szMpqName);
#endif
// Open the MPQ archive file
pStream = FileStream_OpenFile(translatedName, dwStreamFlags);
if(pStream == NULL)
return false;
// Check the file size. There must be at least 0x20 bytes
if(nError == ERROR_SUCCESS)
{
FileStream_GetSize(pStream, &FileSize);
if(FileSize < MPQ_HEADER_SIZE_V1)
nError = ERROR_BAD_FORMAT;
}
// Allocate the MPQhandle
if(nError == ERROR_SUCCESS)
{
if((ha = STORM_ALLOC(TMPQArchive, 1)) == NULL)
nError = ERROR_NOT_ENOUGH_MEMORY;
}
// Allocate buffer for searching MPQ header
if(nError == ERROR_SUCCESS)
{
pbHeaderBuffer = STORM_ALLOC(BYTE, HEADER_SEARCH_BUFFER_SIZE);
if(pbHeaderBuffer == NULL)
nError = ERROR_NOT_ENOUGH_MEMORY;
}
// Find the position of MPQ header
if(nError == ERROR_SUCCESS)
{
ULONGLONG SearchOffset = 0;
ULONGLONG EndOfSearch = FileSize;
DWORD dwStrmFlags = 0;
DWORD dwHeaderSize;
DWORD dwHeaderID;
bool bSearchComplete = false;
memset(ha, 0, sizeof(TMPQArchive));
ha->pfnHashString = HashStringSlash;
ha->pStream = pStream;
pStream = NULL;
// Set the archive read only if the stream is read-only
FileStream_GetFlags(ha->pStream, &dwStrmFlags);
ha->dwFlags |= (dwStrmFlags & STREAM_FLAG_READ_ONLY) ? MPQ_FLAG_READ_ONLY : 0;
// Also remember if we shall check sector CRCs when reading file
ha->dwFlags |= (dwFlags & MPQ_OPEN_CHECK_SECTOR_CRC) ? MPQ_FLAG_CHECK_SECTOR_CRC : 0;
// Also remember if this MPQ is a patch
ha->dwFlags |= (dwFlags & MPQ_OPEN_PATCH) ? MPQ_FLAG_PATCH : 0;
// Limit the header searching to about 130 MB of data
if(EndOfSearch > 0x08000000)
EndOfSearch = 0x08000000;
// Find the offset of MPQ header within the file
while(bSearchComplete == false && SearchOffset < EndOfSearch)
{
// Always read at least 0x1000 bytes for performance.
// This is what Storm.dll (2002) does.
DWORD dwBytesAvailable = HEADER_SEARCH_BUFFER_SIZE;
DWORD dwInBufferOffset = 0;
// Cut the bytes available, if needed
if((FileSize - SearchOffset) < HEADER_SEARCH_BUFFER_SIZE)
dwBytesAvailable = (DWORD)(FileSize - SearchOffset);
// Read the eventual MPQ header
if(!FileStream_Read(ha->pStream, &SearchOffset, pbHeaderBuffer, dwBytesAvailable))
{
nError = GetLastError();
break;
}
// There are AVI files from Warcraft III with 'MPQ' extension.
if(SearchOffset == 0)
{
if(IsAviFile((DWORD *)pbHeaderBuffer))
{
nError = ERROR_AVI_FILE;
break;
}
bIsWarcraft3Map = IsWarcraft3Map((DWORD *)pbHeaderBuffer);
}
// Search the header buffer
while(dwInBufferOffset < dwBytesAvailable)
{
// Copy the data from the potential header buffer to the MPQ header
memcpy(ha->HeaderData, pbHeaderBuffer + dwInBufferOffset, sizeof(ha->HeaderData));
// If there is the MPQ user data, process it
// Note that Warcraft III does not check for user data, which is abused by many map protectors
dwHeaderID = BSWAP_INT32_UNSIGNED(ha->HeaderData[0]);
if(bIsWarcraft3Map == false && (dwFlags & MPQ_OPEN_FORCE_MPQ_V1) == 0)
{
if(ha->pUserData == NULL && dwHeaderID == ID_MPQ_USERDATA)
{
// Verify if this looks like a valid user data
pUserData = IsValidMpqUserData(SearchOffset, FileSize, ha->HeaderData);
if(pUserData != NULL)
{
// Fill the user data header
ha->UserDataPos = SearchOffset;
ha->pUserData = &ha->UserData;
memcpy(ha->pUserData, pUserData, sizeof(TMPQUserData));
// Continue searching from that position
SearchOffset += ha->pUserData->dwHeaderOffs;
break;
}
}
}
// There must be MPQ header signature. Note that STORM.dll from Warcraft III actually
// tests the MPQ header size. It must be at least 0x20 bytes in order to load it
// Abused by Spazzler Map protector. Note that the size check is not present
// in Storm.dll v 1.00, so Diablo I code would load the MPQ anyway.
dwHeaderSize = BSWAP_INT32_UNSIGNED(ha->HeaderData[1]);
if(dwHeaderID == ID_MPQ && dwHeaderSize >= MPQ_HEADER_SIZE_V1)
{
// Now convert the header to version 4
nError = ConvertMpqHeaderToFormat4(ha, SearchOffset, FileSize, dwFlags, bIsWarcraft3Map);
bSearchComplete = true;
break;
}
// Check for MPK archives (Longwu Online - MPQ fork)
if(bIsWarcraft3Map == false && dwHeaderID == ID_MPK)
{
// Now convert the MPK header to MPQ Header version 4
nError = ConvertMpkHeaderToFormat4(ha, FileSize, dwFlags);
bSearchComplete = true;
break;
}
// If searching for the MPQ header is disabled, return an error
if(dwFlags & MPQ_OPEN_NO_HEADER_SEARCH)
{
nError = ERROR_NOT_SUPPORTED;
bSearchComplete = true;
break;
}
// Move the pointers
SearchOffset += 0x200;
dwInBufferOffset += 0x200;
}
}
// Did we identify one of the supported headers?
if(nError == ERROR_SUCCESS)
{
// Set the user data position to the MPQ header, if none
if(ha->pUserData == NULL)
ha->UserDataPos = SearchOffset;
// Set the position of the MPQ header
ha->pHeader = (TMPQHeader *)ha->HeaderData;
ha->MpqPos = SearchOffset;
ha->FileSize = FileSize;
// Sector size must be nonzero.
if(SearchOffset >= FileSize || ha->pHeader->wSectorSize == 0)
nError = ERROR_BAD_FORMAT;
}
}
// Fix table positions according to format
if(nError == ERROR_SUCCESS)
{
// Dump the header
// DumpMpqHeader(ha->pHeader);
// W3x Map Protectors use the fact that War3's Storm.dll ignores the MPQ user data,
// and ignores the MPQ format version as well. The trick is to
// fake MPQ format 2, with an improper hi-word position of hash table and block table
// We can overcome such protectors by forcing opening the archive as MPQ v 1.0
if(dwFlags & MPQ_OPEN_FORCE_MPQ_V1)
{
ha->pHeader->wFormatVersion = MPQ_FORMAT_VERSION_1;
ha->pHeader->dwHeaderSize = MPQ_HEADER_SIZE_V1;
ha->dwFlags |= MPQ_FLAG_READ_ONLY;
ha->pUserData = NULL;
}
// Anti-overflow. If the hash table size in the header is
// higher than 0x10000000, it would overflow in 32-bit version
// Observed in the malformed Warcraft III maps
// Example map: MPQ_2016_v1_ProtectedMap_TableSizeOverflow.w3x
ha->pHeader->dwBlockTableSize = (ha->pHeader->dwBlockTableSize & BLOCK_INDEX_MASK);
ha->pHeader->dwHashTableSize = (ha->pHeader->dwHashTableSize & BLOCK_INDEX_MASK);
// Both MPQ_OPEN_NO_LISTFILE or MPQ_OPEN_NO_ATTRIBUTES trigger read only mode
if(dwFlags & (MPQ_OPEN_NO_LISTFILE | MPQ_OPEN_NO_ATTRIBUTES))
ha->dwFlags |= MPQ_FLAG_READ_ONLY;
// Remember whether whis is a map for Warcraft III
if(bIsWarcraft3Map)
ha->dwFlags |= MPQ_FLAG_WAR3_MAP;
// Set the size of file sector
ha->dwSectorSize = (0x200 << ha->pHeader->wSectorSize);
// Verify if any of the tables doesn't start beyond the end of the file
nError = VerifyMpqTablePositions(ha, FileSize);
}
// Read the hash table. Ignore the result, as hash table is no longer required
// Read HET table. Ignore the result, as HET table is no longer required
if(nError == ERROR_SUCCESS)
{
nError = LoadAnyHashTable(ha);
}
// Now, build the file table. It will be built by combining
// the block table, BET table, hi-block table, (attributes) and (listfile).
if(nError == ERROR_SUCCESS)
{
nError = BuildFileTable(ha);
}
#ifdef FULL
// Load the internal listfile and include it to the file table
if(nError == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_LISTFILE) == 0)
{
// Quick check for (listfile)
pFileEntry = GetFileEntryLocale(ha, LISTFILE_NAME, LANG_NEUTRAL);
if(pFileEntry != NULL)
{
// Ignore result of the operation. (listfile) is optional.
SFileAddListFile((HANDLE)ha, NULL);
ha->dwFileFlags1 = pFileEntry->dwFlags;
}
}
// Load the "(attributes)" file and merge it to the file table
if(nError == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_ATTRIBUTES) == 0 && (ha->dwFlags & MPQ_FLAG_BLOCK_TABLE_CUT) == 0)
{
// Quick check for (attributes)
pFileEntry = GetFileEntryLocale(ha, ATTRIBUTES_NAME, LANG_NEUTRAL);
if(pFileEntry != NULL)
{
// Ignore result of the operation. (attributes) is optional.
SAttrLoadAttributes(ha);
ha->dwFileFlags2 = pFileEntry->dwFlags;
}
}
#endif
// Remember whether the archive has weak signature. Only for MPQs format 1.0.
if(nError == ERROR_SUCCESS)
{
// Quick check for (signature)
pFileEntry = GetFileEntryLocale(ha, SIGNATURE_NAME, LANG_NEUTRAL);
if(pFileEntry != NULL)
{
// Just remember that the archive is weak-signed
assert((pFileEntry->dwFlags & MPQ_FILE_EXISTS) != 0);
ha->dwFileFlags3 = pFileEntry->dwFlags;
}
// Finally, set the MPQ_FLAG_READ_ONLY if the MPQ was found malformed
ha->dwFlags |= (ha->dwFlags & MPQ_FLAG_MALFORMED) ? MPQ_FLAG_READ_ONLY : 0;
}
// Cleanup and exit
if(nError != ERROR_SUCCESS)
{
FileStream_Close(pStream);
FreeArchiveHandle(ha);
SetLastError(nError);
ha = NULL;
}
// Free the header buffer
if(pbHeaderBuffer != NULL)
STORM_FREE(pbHeaderBuffer);
if(phMpq != NULL)
*phMpq = ha;
return (nError == ERROR_SUCCESS);
}
#ifdef FULL
//-----------------------------------------------------------------------------
// bool WINAPI SFileSetDownloadCallback(HANDLE, SFILE_DOWNLOAD_CALLBACK, void *);
//
// Sets a callback that is called when content is downloaded from the master MPQ
//
bool WINAPI SFileSetDownloadCallback(HANDLE hMpq, SFILE_DOWNLOAD_CALLBACK DownloadCB, void * pvUserData)
{
TMPQArchive * ha = (TMPQArchive *)hMpq;
// Do nothing if 'hMpq' is bad parameter
if(!IsValidMpqHandle(hMpq))
{
SetLastError(ERROR_INVALID_HANDLE);
return false;
}
return FileStream_SetCallback(ha->pStream, DownloadCB, pvUserData);
}
//-----------------------------------------------------------------------------
// bool SFileFlushArchive(HANDLE hMpq)
//
// Saves all dirty data into MPQ archive.
// Has similar effect like SFileCloseArchive, but the archive is not closed.
// Use on clients who keep MPQ archive open even for write operations,
// and terminating without calling SFileCloseArchive might corrupt the archive.
//
bool WINAPI SFileFlushArchive(HANDLE hMpq)
{
TMPQArchive * ha;
int nResultError = ERROR_SUCCESS;
int nError;
// Do nothing if 'hMpq' is bad parameter
if((ha = IsValidMpqHandle(hMpq)) == NULL)
{
SetLastError(ERROR_INVALID_HANDLE);
return false;
}
// Only if the MPQ was changed
if(ha->dwFlags & MPQ_FLAG_CHANGED)
{
// Indicate that we are saving MPQ internal structures
ha->dwFlags |= MPQ_FLAG_SAVING_TABLES;
// Defragment the file table. This will allow us to put the internal files to the end
DefragmentFileTable(ha);
//
// Create each internal file
// Note that the (signature) file is usually before (listfile) in the file table
//
if(ha->dwFlags & MPQ_FLAG_SIGNATURE_NEW)
{
#ifdef FULL
nError = SSignFileCreate(ha);
if(nError != ERROR_SUCCESS)
nResultError = nError;
#else
assert(0);
#endif
}
if(ha->dwFlags & MPQ_FLAG_LISTFILE_NEW)
{
nError = SListFileSaveToMpq(ha);
if(nError != ERROR_SUCCESS)
nResultError = nError;
}
if(ha->dwFlags & MPQ_FLAG_ATTRIBUTES_NEW)
{
nError = SAttrFileSaveToMpq(ha);
if(nError != ERROR_SUCCESS)
nResultError = nError;
}
// Save HET table, BET table, hash table, block table, hi-block table
if(ha->dwFlags & MPQ_FLAG_CHANGED)
{
// Rebuild the HET table
if(ha->pHetTable != NULL)
RebuildHetTable(ha);
// Save all MPQ tables first
nError = SaveMPQTables(ha);
if(nError != ERROR_SUCCESS)
nResultError = nError;
// If the archive has weak signature, we need to finish it
if(ha->dwFileFlags3 != 0)
{
#ifdef FULL
nError = SSignFileFinish(ha);
if(nError != ERROR_SUCCESS)
nResultError = nError;
#else
assert(0);
#endif
}
}
// We are no longer saving internal MPQ structures
ha->dwFlags &= ~MPQ_FLAG_SAVING_TABLES;
}
// Return the error
if(nResultError != ERROR_SUCCESS)
SetLastError(nResultError);
return (nResultError == ERROR_SUCCESS);
}
#endif
//-----------------------------------------------------------------------------
// bool SFileCloseArchive(HANDLE hMpq);
//
bool WINAPI SFileCloseArchive(HANDLE hMpq)
{
TMPQArchive * ha = IsValidMpqHandle(hMpq);
bool bResult = true;
// Only if the handle is valid
if(ha == NULL)
{
SetLastError(ERROR_INVALID_HANDLE);
return false;
}
// Invalidate the add file callback so it won't be called
// when saving (listfile) and (attributes)
ha->pfnAddFileCB = NULL;
ha->pvAddFileUserData = NULL;
#ifdef FULL
// Flush all unsaved data to the storage
bResult = SFileFlushArchive(hMpq);
#endif
// Free all memory used by MPQ archive
FreeArchiveHandle(ha);
return bResult;
}

396
3rdParty/StormLib/src/SFileOpenFileEx.cpp vendored

@ -0,0 +1,396 @@
/*****************************************************************************/
/* 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)
{
SetLastError(ERROR_FILE_NOT_FOUND);
return false;
}
// 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;
}
}
}
// 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 plcLocales. There must be enough entries to copy the localed,
// otherwise the function returns ERROR_INSUFFICIENT_BUFFER.
int WINAPI SFileEnumLocales(
HANDLE hMpq,
const char * szFileName,
LCID * PtrLocales,
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(PtrLocales != NULL && dwLocales < dwMaxLocales)
*PtrLocales++ = pHash->lcLocale;
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;
bool bOpenByIndex = false;
int nError = ERROR_SUCCESS;
// Don't accept NULL pointer to file handle
if(szFileName == NULL || *szFileName == 0)
nError = ERROR_INVALID_PARAMETER;
// When opening a file from MPQ, the handle must be valid
if(dwSearchScope != SFILE_OPEN_LOCAL_FILE && ha == NULL)
nError = ERROR_INVALID_HANDLE;
// When not checking for existence, the pointer to file handle must be valid
if(dwSearchScope != SFILE_OPEN_CHECK_EXISTS && PtrFile == NULL)
nError = ERROR_INVALID_PARAMETER;
// Prepare the file opening
if(nError == 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 = GetFileEntryLocale2(ha, szFileName, 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 = GetFileEntryLocale2(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
nError = ERROR_INVALID_PARAMETER;
break;
}
}
// Check whether the file really exists in the MPQ
if(nError == ERROR_SUCCESS)
{
if(pFileEntry == NULL || (pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0)
{
// Check the pseudo-file name
if((bOpenByIndex = IsPseudoFileName(szFileName, &dwFileIndex)) == true)
{
// Get the file entry for the file
if(dwFileIndex < ha->dwFileTableSize)
{
pFileEntry = ha->pFileTable + dwFileIndex;
}
}
nError = ERROR_FILE_NOT_FOUND;
}
// Ignore unknown loading flags (example: MPQ_2016_v1_WME4_4.w3x)
// if(pFileEntry != NULL && pFileEntry->dwFlags & ~MPQ_FILE_VALID_FLAGS)
// nError = ERROR_NOT_SUPPORTED;
}
// Did the caller just wanted to know if the file exists?
if(nError == 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
{
nError = ERROR_NOT_ENOUGH_MEMORY;
}
}
// Give the file entry
if(PtrFile != NULL)
PtrFile[0] = hf;
// Return error code
if(nError != ERROR_SUCCESS)
SetLastError(nError);
return (nError == 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;
}

907
3rdParty/StormLib/src/SFileReadFile.cpp vendored

@ -0,0 +1,907 @@
/*****************************************************************************/
/* SFileReadFile.cpp Copyright (c) Ladislav Zezula 2003 */
/*---------------------------------------------------------------------------*/
/* Description : */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* xx.xx.99 1.00 Lad The first version of SFileReadFile.cpp */
/* 24.03.99 1.00 Lad Added the SFileGetFileInfo function */
/*****************************************************************************/
#define __STORMLIB_SELF__
#include "StormLib.h"
#include "StormCommon.h"
//-----------------------------------------------------------------------------
// Local functions
// hf - MPQ File handle.
// pbBuffer - Pointer to target buffer to store sectors.
// dwByteOffset - Position of sector in the file (relative to file begin)
// dwBytesToRead - Number of bytes to read. Must be multiplier of sector size.
// pdwBytesRead - Stored number of bytes loaded
static int ReadMpqSectors(TMPQFile * hf, LPBYTE pbBuffer, DWORD dwByteOffset, DWORD dwBytesToRead, LPDWORD pdwBytesRead)
{
ULONGLONG RawFilePos;
TMPQArchive * ha = hf->ha;
TFileEntry * pFileEntry = hf->pFileEntry;
LPBYTE pbRawSector = NULL;
LPBYTE pbOutSector = pbBuffer;
LPBYTE pbInSector = pbBuffer;
DWORD dwRawBytesToRead;
DWORD dwRawSectorOffset = dwByteOffset;
DWORD dwSectorsToRead = dwBytesToRead / ha->dwSectorSize;
DWORD dwSectorIndex = dwByteOffset / ha->dwSectorSize;
DWORD dwSectorsDone = 0;
DWORD dwBytesRead = 0;
int nError = ERROR_SUCCESS;
// Note that dwByteOffset must be aligned to size of one sector
// Note that dwBytesToRead must be a multiplier of one sector size
// This is local function, so we won't check if that's true.
// Note that files stored in single units are processed by a separate function
// If there is not enough bytes remaining, cut dwBytesToRead
if((dwByteOffset + dwBytesToRead) > hf->dwDataSize)
dwBytesToRead = hf->dwDataSize - dwByteOffset;
dwRawBytesToRead = dwBytesToRead;
// Perform all necessary work to do with compressed files
if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK)
{
// If the sector positions are not loaded yet, do it
if(hf->SectorOffsets == NULL)
{
nError = AllocateSectorOffsets(hf, true);
if(nError != ERROR_SUCCESS || hf->SectorOffsets == NULL)
return nError;
}
// If the sector checksums are not loaded yet, load them now.
if(hf->SectorChksums == NULL && (pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC) && hf->bLoadedSectorCRCs == false)
{
//
// Sector CRCs is plain crap feature. It is almost never present,
// often it's empty, or the end offset of sector CRCs is zero.
// We only try to load sector CRCs once, and regardless if it fails
// or not, we won't try that again for the given file.
//
AllocateSectorChecksums(hf, true);
hf->bLoadedSectorCRCs = true;
}
// TODO: If the raw data MD5s are not loaded yet, load them now
// Only do it if the MPQ is of format 4.0
// if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_4 && ha->pHeader->dwRawChunkSize != 0)
// {
// nError = AllocateRawMD5s(hf, true);
// if(nError != ERROR_SUCCESS)
// return nError;
// }
// Assign the temporary buffer as target for read operation
dwRawSectorOffset = hf->SectorOffsets[dwSectorIndex];
dwRawBytesToRead = hf->SectorOffsets[dwSectorIndex + dwSectorsToRead] - dwRawSectorOffset;
// If the file is compressed, also allocate secondary buffer
pbInSector = pbRawSector = STORM_ALLOC(BYTE, dwRawBytesToRead);
if(pbRawSector == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
}
// Calculate raw file offset where the sector(s) are stored.
RawFilePos = CalculateRawSectorOffset(hf, dwRawSectorOffset);
// Set file pointer and read all required sectors
if(FileStream_Read(ha->pStream, &RawFilePos, pbInSector, dwRawBytesToRead))
{
// Now we have to decrypt and decompress all file sectors that have been loaded
for(DWORD i = 0; i < dwSectorsToRead; i++)
{
DWORD dwRawBytesInThisSector = ha->dwSectorSize;
DWORD dwBytesInThisSector = ha->dwSectorSize;
DWORD dwIndex = dwSectorIndex + i;
// If there is not enough bytes in the last sector,
// cut the number of bytes in this sector
if(dwRawBytesInThisSector > dwBytesToRead)
dwRawBytesInThisSector = dwBytesToRead;
if(dwBytesInThisSector > dwBytesToRead)
dwBytesInThisSector = dwBytesToRead;
// If the file is compressed, we have to adjust the raw sector size
if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK)
dwRawBytesInThisSector = hf->SectorOffsets[dwIndex + 1] - hf->SectorOffsets[dwIndex];
// If the file is encrypted, we have to decrypt the sector
if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)
{
BSWAP_ARRAY32_UNSIGNED(pbInSector, dwRawBytesInThisSector);
// If we don't know the key, try to detect it by file content
if(hf->dwFileKey == 0)
{
hf->dwFileKey = DetectFileKeyByContent(pbInSector, dwBytesInThisSector, hf->dwDataSize);
if(hf->dwFileKey == 0)
{
nError = ERROR_UNKNOWN_FILE_KEY;
break;
}
}
DecryptMpqBlock(pbInSector, dwRawBytesInThisSector, hf->dwFileKey + dwIndex);
BSWAP_ARRAY32_UNSIGNED(pbInSector, dwRawBytesInThisSector);
}
#ifdef FULL
// If the file has sector CRC check turned on, perform it
if(hf->bCheckSectorCRCs && hf->SectorChksums != NULL)
{
DWORD dwAdlerExpected = hf->SectorChksums[dwIndex];
DWORD dwAdlerValue = 0;
// We can only check sector CRC when it's not zero
// Neither can we check it if it's 0xFFFFFFFF.
if(dwAdlerExpected != 0 && dwAdlerExpected != 0xFFFFFFFF)
{
dwAdlerValue = adler32(0, pbInSector, dwRawBytesInThisSector);
if(dwAdlerValue != dwAdlerExpected)
{
nError = ERROR_CHECKSUM_ERROR;
break;
}
}
}
#endif
// If the sector is really compressed, decompress it.
// WARNING : Some sectors may not be compressed, it can be determined only
// by comparing uncompressed and compressed size !!!
if(dwRawBytesInThisSector < dwBytesInThisSector)
{
int cbOutSector = dwBytesInThisSector;
int cbInSector = dwRawBytesInThisSector;
int nResult = 0;
// Is the file compressed by Blizzard's multiple compression ?
if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS)
{
// Remember the last used compression
hf->dwCompression0 = pbInSector[0];
// Decompress the data
if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2)
nResult = SCompDecompress2(pbOutSector, &cbOutSector, pbInSector, cbInSector);
else
nResult = SCompDecompress(pbOutSector, &cbOutSector, pbInSector, cbInSector);
}
// Is the file compressed by PKWARE Data Compression Library ?
else if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE)
{
nResult = SCompExplode(pbOutSector, &cbOutSector, pbInSector, cbInSector);
}
// Did the decompression fail ?
if(nResult == 0)
{
nError = ERROR_FILE_CORRUPT;
break;
}
}
else
{
if(pbOutSector != pbInSector)
memcpy(pbOutSector, pbInSector, dwBytesInThisSector);
}
// Move pointers
dwBytesToRead -= dwBytesInThisSector;
dwByteOffset += dwBytesInThisSector;
dwBytesRead += dwBytesInThisSector;
pbOutSector += dwBytesInThisSector;
pbInSector += dwRawBytesInThisSector;
dwSectorsDone++;
}
}
else
{
nError = GetLastError();
}
// Free all used buffers
if(pbRawSector != NULL)
STORM_FREE(pbRawSector);
// Give the caller thenumber of bytes read
*pdwBytesRead = dwBytesRead;
return nError;
}
static int ReadMpqFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead)
{
ULONGLONG RawFilePos = hf->RawFilePos;
TMPQArchive * ha = hf->ha;
TFileEntry * pFileEntry = hf->pFileEntry;
LPBYTE pbCompressed = NULL;
LPBYTE pbRawData;
int nError = ERROR_SUCCESS;
// If the file buffer is not allocated yet, do it.
if(hf->pbFileSector == NULL)
{
nError = AllocateSectorBuffer(hf);
if(nError != ERROR_SUCCESS || hf->pbFileSector == NULL)
return nError;
}
// If the file is a patch file, adjust raw data offset
if(hf->pPatchInfo != NULL)
RawFilePos += hf->pPatchInfo->dwLength;
pbRawData = hf->pbFileSector;
// If the file sector is not loaded yet, do it
if(hf->dwSectorOffs != 0)
{
// Is the file compressed?
if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK)
{
// Allocate space for compressed data
pbCompressed = STORM_ALLOC(BYTE, pFileEntry->dwCmpSize);
if(pbCompressed == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
pbRawData = pbCompressed;
}
// Load the raw (compressed, encrypted) data
if(!FileStream_Read(ha->pStream, &RawFilePos, pbRawData, pFileEntry->dwCmpSize))
{
STORM_FREE(pbCompressed);
return GetLastError();
}
// If the file is encrypted, we have to decrypt the data first
if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)
{
BSWAP_ARRAY32_UNSIGNED(pbRawData, pFileEntry->dwCmpSize);
DecryptMpqBlock(pbRawData, pFileEntry->dwCmpSize, hf->dwFileKey);
BSWAP_ARRAY32_UNSIGNED(pbRawData, pFileEntry->dwCmpSize);
}
// If the file is compressed, we have to decompress it now
if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK)
{
int cbOutBuffer = (int)hf->dwDataSize;
int cbInBuffer = (int)pFileEntry->dwCmpSize;
int nResult = 0;
//
// 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)
{
// Remember the last used compression
hf->dwCompression0 = pbRawData[0];
// Decompress the file
if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2)
nResult = SCompDecompress2(hf->pbFileSector, &cbOutBuffer, pbRawData, cbInBuffer);
else
nResult = SCompDecompress(hf->pbFileSector, &cbOutBuffer, pbRawData, cbInBuffer);
}
// Is the file compressed by PKWARE Data Compression Library ?
// Note: Single unit files compressed with IMPLODE are not supported by Blizzard
else if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE)
nResult = SCompExplode(hf->pbFileSector, &cbOutBuffer, pbRawData, cbInBuffer);
nError = (nResult != 0) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT;
}
else
{
if(hf->pbFileSector != NULL && pbRawData != hf->pbFileSector)
memcpy(hf->pbFileSector, pbRawData, hf->dwDataSize);
}
// Free the decompression buffer.
if(pbCompressed != NULL)
STORM_FREE(pbCompressed);
// The file sector is now properly loaded
hf->dwSectorOffs = 0;
}
// At this moment, we have the file loaded into the file buffer.
// Copy as much as the caller wants
if(nError == ERROR_SUCCESS && hf->dwSectorOffs == 0)
{
// File position is greater or equal to file size ?
if(dwFilePos >= hf->dwDataSize)
{
*pdwBytesRead = 0;
return ERROR_SUCCESS;
}
// If not enough bytes remaining in the file, cut them
if((hf->dwDataSize - dwFilePos) < dwToRead)
dwToRead = (hf->dwDataSize - dwFilePos);
// Copy the bytes
memcpy(pvBuffer, hf->pbFileSector + dwFilePos, dwToRead);
// Give the number of bytes read
*pdwBytesRead = dwToRead;
return ERROR_SUCCESS;
}
// An error, sorry
return ERROR_CAN_NOT_COMPLETE;
}
static int ReadMpkFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead)
{
ULONGLONG RawFilePos = hf->RawFilePos + 0x0C; // For some reason, MPK files start at position (hf->RawFilePos + 0x0C)
TMPQArchive * ha = hf->ha;
TFileEntry * pFileEntry = hf->pFileEntry;
LPBYTE pbCompressed = NULL;
LPBYTE pbRawData = hf->pbFileSector;
int nError = ERROR_SUCCESS;
// We do not support patch files in MPK archives
assert(hf->pPatchInfo == NULL);
// If the file buffer is not allocated yet, do it.
if(hf->pbFileSector == NULL)
{
nError = AllocateSectorBuffer(hf);
if(nError != ERROR_SUCCESS || hf->pbFileSector == NULL)
return nError;
pbRawData = hf->pbFileSector;
}
// If the file sector is not loaded yet, do it
if(hf->dwSectorOffs != 0)
{
// Is the file compressed?
if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK)
{
// Allocate space for compressed data
pbCompressed = STORM_ALLOC(BYTE, pFileEntry->dwCmpSize);
if(pbCompressed == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
pbRawData = pbCompressed;
}
// Load the raw (compressed, encrypted) data
if(!FileStream_Read(ha->pStream, &RawFilePos, pbRawData, pFileEntry->dwCmpSize))
{
STORM_FREE(pbCompressed);
return GetLastError();
}
// If the file is encrypted, we have to decrypt the data first
if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)
{
DecryptMpkTable(pbRawData, pFileEntry->dwCmpSize);
}
// If the file is compressed, we have to decompress it now
if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK)
{
#ifdef FULL
int cbOutBuffer = (int)hf->dwDataSize;
hf->dwCompression0 = pbRawData[0];
if(!SCompDecompressMpk(hf->pbFileSector, &cbOutBuffer, pbRawData, (int)pFileEntry->dwCmpSize))
nError = ERROR_FILE_CORRUPT;
#else
assert(0);
#endif
}
else
{
if(pbRawData != hf->pbFileSector)
memcpy(hf->pbFileSector, pbRawData, hf->dwDataSize);
}
// Free the decompression buffer.
if(pbCompressed != NULL)
STORM_FREE(pbCompressed);
// The file sector is now properly loaded
hf->dwSectorOffs = 0;
}
// At this moment, we have the file loaded into the file buffer.
// Copy as much as the caller wants
if(nError == ERROR_SUCCESS && hf->dwSectorOffs == 0)
{
// File position is greater or equal to file size ?
if(dwFilePos >= hf->dwDataSize)
{
*pdwBytesRead = 0;
return ERROR_SUCCESS;
}
// If not enough bytes remaining in the file, cut them
if((hf->dwDataSize - dwFilePos) < dwToRead)
dwToRead = (hf->dwDataSize - dwFilePos);
// Copy the bytes
memcpy(pvBuffer, hf->pbFileSector + dwFilePos, dwToRead);
// Give the number of bytes read
*pdwBytesRead = dwToRead;
return ERROR_SUCCESS;
}
// An error, sorry
return ERROR_CAN_NOT_COMPLETE;
}
static int ReadMpqFileSectorFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwBytesToRead, LPDWORD pdwBytesRead)
{
TMPQArchive * ha = hf->ha;
LPBYTE pbBuffer = (BYTE *)pvBuffer;
DWORD dwTotalBytesRead = 0; // Total bytes read in all three parts
DWORD dwSectorSizeMask = ha->dwSectorSize - 1; // Mask for block size, usually 0x0FFF
DWORD dwFileSectorPos; // File offset of the loaded sector
DWORD dwBytesRead; // Number of bytes read (temporary variable)
int nError;
// If the file position is at or beyond end of file, do nothing
if(dwFilePos >= hf->dwDataSize)
{
*pdwBytesRead = 0;
return ERROR_SUCCESS;
}
// If not enough bytes in the file remaining, cut them
if(dwBytesToRead > (hf->dwDataSize - dwFilePos))
dwBytesToRead = (hf->dwDataSize - dwFilePos);
// Compute sector position in the file
dwFileSectorPos = dwFilePos & ~dwSectorSizeMask; // Position in the block
// If the file sector buffer is not allocated yet, do it now
if(hf->pbFileSector == NULL)
{
nError = AllocateSectorBuffer(hf);
if(nError != ERROR_SUCCESS || hf->pbFileSector == NULL)
return nError;
}
// Load the first (incomplete) file sector
if(dwFilePos & dwSectorSizeMask)
{
DWORD dwBytesInSector = ha->dwSectorSize;
DWORD dwBufferOffs = dwFilePos & dwSectorSizeMask;
DWORD dwToCopy;
// Is the file sector already loaded ?
if(hf->dwSectorOffs != dwFileSectorPos)
{
// Load one MPQ sector into archive buffer
nError = ReadMpqSectors(hf, hf->pbFileSector, dwFileSectorPos, ha->dwSectorSize, &dwBytesInSector);
if(nError != ERROR_SUCCESS)
return nError;
// Remember that the data loaded to the sector have new file offset
hf->dwSectorOffs = dwFileSectorPos;
}
else
{
if((dwFileSectorPos + dwBytesInSector) > hf->dwDataSize)
dwBytesInSector = hf->dwDataSize - dwFileSectorPos;
}
// Copy the data from the offset in the loaded sector to the end of the sector
dwToCopy = dwBytesInSector - dwBufferOffs;
if(dwToCopy > dwBytesToRead)
dwToCopy = dwBytesToRead;
// Copy data from sector buffer into target buffer
memcpy(pbBuffer, hf->pbFileSector + dwBufferOffs, dwToCopy);
// Update pointers and byte counts
dwTotalBytesRead += dwToCopy;
dwFileSectorPos += dwBytesInSector;
pbBuffer += dwToCopy;
dwBytesToRead -= dwToCopy;
}
// Load the whole ("middle") sectors only if there is at least one full sector to be read
if(dwBytesToRead >= ha->dwSectorSize)
{
DWORD dwBlockBytes = dwBytesToRead & ~dwSectorSizeMask;
// Load all sectors to the output buffer
nError = ReadMpqSectors(hf, pbBuffer, dwFileSectorPos, dwBlockBytes, &dwBytesRead);
if(nError != ERROR_SUCCESS)
return nError;
// Update pointers
dwTotalBytesRead += dwBytesRead;
dwFileSectorPos += dwBytesRead;
pbBuffer += dwBytesRead;
dwBytesToRead -= dwBytesRead;
}
// Read the terminating sector
if(dwBytesToRead > 0)
{
DWORD dwToCopy = ha->dwSectorSize;
// Is the file sector already loaded ?
if(hf->dwSectorOffs != dwFileSectorPos)
{
// Load one MPQ sector into archive buffer
nError = ReadMpqSectors(hf, hf->pbFileSector, dwFileSectorPos, ha->dwSectorSize, &dwBytesRead);
if(nError != ERROR_SUCCESS)
return nError;
// Remember that the data loaded to the sector have new file offset
hf->dwSectorOffs = dwFileSectorPos;
}
// Check number of bytes read
if(dwToCopy > dwBytesToRead)
dwToCopy = dwBytesToRead;
// Copy the data from the cached last sector to the caller's buffer
memcpy(pbBuffer, hf->pbFileSector, dwToCopy);
// Update pointers
dwTotalBytesRead += dwToCopy;
}
// Store total number of bytes read to the caller
*pdwBytesRead = dwTotalBytesRead;
return ERROR_SUCCESS;
}
#ifdef FULL
static int ReadMpqFilePatchFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead)
{
TMPQPatcher Patcher;
DWORD dwBytesToRead = dwToRead;
DWORD dwBytesRead = 0;
int nError = ERROR_SUCCESS;
// Make sure that the patch file is loaded completely
if(nError == ERROR_SUCCESS && hf->pbFileData == NULL)
{
// Initialize patching process and allocate data
nError = Patch_InitPatcher(&Patcher, hf);
if(nError != ERROR_SUCCESS)
return nError;
// Set the current data size
Patcher.cbFileData = hf->pFileEntry->dwFileSize;
// Initialize the patcher object with initial file data
if(hf->pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT)
nError = ReadMpqFileSingleUnit(hf, Patcher.pbFileData1, 0, Patcher.cbFileData, &dwBytesRead);
else
nError = ReadMpqFileSectorFile(hf, Patcher.pbFileData1, 0, Patcher.cbFileData, &dwBytesRead);
// Perform the patching process
if(nError == ERROR_SUCCESS)
nError = Patch_Process(&Patcher, hf);
// Finalize the patcher structure
Patch_Finalize(&Patcher);
dwBytesRead = 0;
}
// If there is something to read, do it
if(nError == ERROR_SUCCESS)
{
if(dwFilePos < hf->cbFileData)
{
// Make sure we don't copy more than file size
if((dwFilePos + dwToRead) > hf->cbFileData)
dwToRead = hf->cbFileData - dwFilePos;
// Copy the appropriate amount of the file data to the caller's buffer
memcpy(pvBuffer, hf->pbFileData + dwFilePos, dwToRead);
dwBytesRead = dwToRead;
}
// Set the proper error code
nError = (dwBytesRead == dwBytesToRead) ? ERROR_SUCCESS : ERROR_HANDLE_EOF;
}
// Give the result to the caller
if(pdwBytesRead != NULL)
*pdwBytesRead = dwBytesRead;
return nError;
}
#endif
static int ReadMpqFileLocalFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead)
{
ULONGLONG FilePosition1 = dwFilePos;
ULONGLONG FilePosition2;
DWORD dwBytesRead = 0;
int nError = ERROR_SUCCESS;
assert(hf->pStream != NULL);
// Because stream I/O functions are designed to read
// "all or nothing", we compare file position before and after,
// and if they differ, we assume that number of bytes read
// is the difference between them
if(!FileStream_Read(hf->pStream, &FilePosition1, pvBuffer, dwToRead))
{
// If not all bytes have been read, then return the number of bytes read
if((nError = GetLastError()) == ERROR_HANDLE_EOF)
{
FileStream_GetPos(hf->pStream, &FilePosition2);
dwBytesRead = (DWORD)(FilePosition2 - FilePosition1);
}
}
else
{
dwBytesRead = dwToRead;
}
*pdwBytesRead = dwBytesRead;
return nError;
}
//-----------------------------------------------------------------------------
// SFileReadFile
bool WINAPI SFileReadFile(HANDLE hFile, void * pvBuffer, DWORD dwToRead, LPDWORD pdwRead, LPOVERLAPPED lpOverlapped)
{
TMPQFile * hf = (TMPQFile *)hFile;
DWORD dwBytesRead = 0; // Number of bytes read
int nError = ERROR_SUCCESS;
// Always zero the result
if(pdwRead != NULL)
*pdwRead = 0;
lpOverlapped = lpOverlapped;
// Check valid parameters
if(!IsValidFileHandle(hFile))
{
SetLastError(ERROR_INVALID_HANDLE);
return false;
}
if(pvBuffer == NULL)
{
SetLastError(ERROR_INVALID_PARAMETER);
return false;
}
// If we didn't load the patch info yet, do it now
if(hf->pFileEntry != NULL && (hf->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) && hf->pPatchInfo == NULL)
{
nError = AllocatePatchInfo(hf, true);
if(nError != ERROR_SUCCESS || hf->pPatchInfo == NULL)
{
SetLastError(nError);
return false;
}
}
// Clear the last used compression
hf->dwCompression0 = 0;
// If the file is local file, read the data directly from the stream
if(hf->pStream != NULL)
{
nError = ReadMpqFileLocalFile(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead);
}
#ifdef FULL
// If the file is a patch file, we have to read it special way
else if(hf->hfPatch != NULL && (hf->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0)
{
nError = ReadMpqFilePatchFile(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead);
}
#endif
// If the archive is a MPK archive, we need special way to read the file
else if(hf->ha->dwSubType == MPQ_SUBTYPE_MPK)
{
nError = ReadMpkFileSingleUnit(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead);
}
// If the file is single unit file, redirect it to read file
else if(hf->pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT)
{
nError = ReadMpqFileSingleUnit(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead);
}
// Otherwise read it as sector based MPQ file
else
{
nError = ReadMpqFileSectorFile(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead);
}
// Increment the file position
hf->dwFilePos += dwBytesRead;
// Give the caller the number of bytes read
if(pdwRead != NULL)
*pdwRead = dwBytesRead;
// If the read operation succeeded, but not full number of bytes was read,
// set the last error to ERROR_HANDLE_EOF
if(nError == ERROR_SUCCESS && (dwBytesRead < dwToRead))
nError = ERROR_HANDLE_EOF;
// If something failed, set the last error value
if(nError != ERROR_SUCCESS)
SetLastError(nError);
return (nError == ERROR_SUCCESS);
}
//-----------------------------------------------------------------------------
// SFileGetFileSize
DWORD WINAPI SFileGetFileSize(HANDLE hFile, LPDWORD pdwFileSizeHigh)
{
ULONGLONG FileSize;
TMPQFile * hf = (TMPQFile *)hFile;
// Validate the file handle before we go on
if(IsValidFileHandle(hFile))
{
// Make sure that the variable is initialized
FileSize = 0;
// If the file is patched file, we have to get the size of the last version
if(hf->hfPatch != NULL)
{
// Walk through the entire patch chain, take the last version
while(hf != NULL)
{
// Get the size of the currently pointed version
FileSize = hf->pFileEntry->dwFileSize;
// Move to the next patch file in the hierarchy
hf = hf->hfPatch;
}
}
else
{
// Is it a local file ?
if(hf->pStream != NULL)
{
FileStream_GetSize(hf->pStream, &FileSize);
}
else
{
FileSize = hf->dwDataSize;
}
}
// If opened from archive, return file size
if(pdwFileSizeHigh != NULL)
*pdwFileSizeHigh = (DWORD)(FileSize >> 32);
return (DWORD)FileSize;
}
SetLastError(ERROR_INVALID_HANDLE);
return SFILE_INVALID_SIZE;
}
DWORD WINAPI SFileSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHigh, DWORD dwMoveMethod)
{
TMPQFile * hf = (TMPQFile *)hFile;
ULONGLONG OldPosition;
ULONGLONG NewPosition;
ULONGLONG FileSize;
ULONGLONG DeltaPos;
// If the hFile is not a valid file handle, return an error.
if(!IsValidFileHandle(hFile))
{
SetLastError(ERROR_INVALID_HANDLE);
return SFILE_INVALID_POS;
}
// Retrieve the file size for handling the limits
if(hf->pStream != NULL)
{
FileStream_GetSize(hf->pStream, &FileSize);
}
else
{
FileSize = SFileGetFileSize(hFile, NULL);
}
// Handle the NULL and non-NULL values of plFilePosHigh
// Non-NULL: The DeltaPos is combined from lFilePos and *lpFilePosHigh
// NULL: The DeltaPos is sign-extended value of lFilePos
DeltaPos = (plFilePosHigh != NULL) ? MAKE_OFFSET64(plFilePosHigh[0], lFilePos) : (ULONGLONG)(LONGLONG)lFilePos;
// Get the relative point where to move from
switch(dwMoveMethod)
{
case FILE_BEGIN:
// Move relative to the file begin.
OldPosition = 0;
break;
case FILE_CURRENT:
// Retrieve the current file position
if(hf->pStream != NULL)
{
FileStream_GetPos(hf->pStream, &OldPosition);
}
else
{
OldPosition = hf->dwFilePos;
}
break;
case FILE_END:
// Move relative to the end of the file
OldPosition = FileSize;
break;
default:
SetLastError(ERROR_INVALID_PARAMETER);
return SFILE_INVALID_POS;
}
// Calculate the new position
NewPosition = OldPosition + DeltaPos;
// If moving backward, don't allow the new position go negative
if((LONGLONG)DeltaPos < 0)
{
if(NewPosition > FileSize) // Position is negative
{
SetLastError(ERROR_NEGATIVE_SEEK);
return SFILE_INVALID_POS;
}
}
// If moving forward, don't allow the new position go past the end of the file
else
{
if(NewPosition > FileSize)
NewPosition = FileSize;
}
// Now apply the file pointer to the file
if(hf->pStream != NULL)
{
if(!FileStream_Read(hf->pStream, &NewPosition, NULL, 0))
return SFILE_INVALID_POS;
}
else
{
hf->dwFilePos = (DWORD)NewPosition;
}
// Return the new file position
if(plFilePosHigh != NULL)
*plFilePosHigh = (LONG)(NewPosition >> 32);
return (DWORD)NewPosition;
}

387
3rdParty/StormLib/src/StormCommon.h vendored

@ -0,0 +1,387 @@
/*****************************************************************************/
/* SCommon.h Copyright (c) Ladislav Zezula 2003 */
/*---------------------------------------------------------------------------*/
/* Common functions for encryption/decryption from Storm.dll. Included by */
/* SFile*** functions, do not include and do not use this file directly */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 24.03.03 1.00 Lad The first version of SFileCommon.h */
/* 12.06.04 1.00 Lad Renamed to SCommon.h */
/* 06.09.10 1.00 Lad Renamed to StormCommon.h */
/*****************************************************************************/
#ifndef __STORMCOMMON_H__
#define __STORMCOMMON_H__
//-----------------------------------------------------------------------------
// Compression support
// Include functions from Pkware Data Compression Library
#include "3rdParty/PKWare/pkware.h"
#ifdef FULL
// Include functions from Huffmann compression
#include "huffman/huff.h"
// Include functions from IMA ADPCM compression
#include "adpcm/adpcm.h"
// Include functions from SPARSE compression
#include "sparse/sparse.h"
// Include functions from LZMA compression
#include "lzma/C/LzmaEnc.h"
#include "lzma/C/LzmaDec.h"
// Include functions from zlib
#ifndef __SYS_ZLIB
#include "zlib/zlib.h"
#else
#include <zlib.h>
#endif
// Include functions from bzlib
#ifndef __SYS_BZLIB
#include "bzip2/bzlib.h"
#else
#include <bzlib.h>
#endif
//-----------------------------------------------------------------------------
// Cryptography support
// Headers from LibTomCrypt
#include "libtomcrypt/src/headers/tomcrypt.h"
// For HashStringJenkins
#include "jenkins/lookup.h"
#endif
//-----------------------------------------------------------------------------
// StormLib private defines
#define ID_MPQ_FILE 0x46494c45 // Used internally for checking TMPQFile ('FILE')
// Prevent problems with CRT "min" and "max" functions,
// as they are not defined on all platforms
#define STORMLIB_MIN(a, b) ((a < b) ? a : b)
#define STORMLIB_MAX(a, b) ((a > b) ? a : b)
#define STORMLIB_UNUSED(p) ((void)(p))
// Macro for building 64-bit file offset from two 32-bit
#define MAKE_OFFSET64(hi, lo) (((ULONGLONG)hi << 32) | (ULONGLONG)lo)
//-----------------------------------------------------------------------------
// MPQ signature information
// Size of each signature type
#define MPQ_WEAK_SIGNATURE_SIZE 64
#define MPQ_STRONG_SIGNATURE_SIZE 256
#define MPQ_STRONG_SIGNATURE_ID 0x5349474E // ID of the strong signature ("NGIS")
#define MPQ_SIGNATURE_FILE_SIZE (MPQ_WEAK_SIGNATURE_SIZE + 8)
// MPQ signature info
typedef struct _MPQ_SIGNATURE_INFO
{
ULONGLONG BeginMpqData; // File offset where the hashing starts
ULONGLONG BeginExclude; // Begin of the excluded area (used for (signature) file)
ULONGLONG EndExclude; // End of the excluded area (used for (signature) file)
ULONGLONG EndMpqData; // File offset where the hashing ends
ULONGLONG EndOfFile; // Size of the entire file
BYTE Signature[MPQ_STRONG_SIGNATURE_SIZE + 0x10];
DWORD cbSignatureSize; // Length of the signature
DWORD SignatureTypes; // See SIGNATURE_TYPE_XXX
} MPQ_SIGNATURE_INFO, *PMPQ_SIGNATURE_INFO;
//-----------------------------------------------------------------------------
// Memory management
//
// We use our own macros for allocating/freeing memory. If you want
// to redefine them, please keep the following rules:
//
// - The memory allocation must return NULL if not enough memory
// (i.e not to throw exception)
// - The allocating function does not need to fill the allocated buffer with zeros
// - Memory freeing function doesn't have to test the pointer to NULL
//
//#if defined(_MSC_VER) && defined(_DEBUG)
//
//#define STORM_ALLOC(type, nitems) (type *)HeapAlloc(GetProcessHeap(), 0, ((nitems) * sizeof(type)))
//#define STORM_REALLOC(type, ptr, nitems) (type *)HeapReAlloc(GetProcessHeap(), 0, ptr, ((nitems) * sizeof(type)))
//#define STORM_FREE(ptr) HeapFree(GetProcessHeap(), 0, ptr)
//
//#else
#define STORM_ALLOC(type, nitems) (type *)malloc((nitems) * sizeof(type))
#define STORM_REALLOC(type, ptr, nitems) (type *)realloc(ptr, ((nitems) * sizeof(type)))
#define STORM_FREE(ptr) free(ptr)
//#endif
//-----------------------------------------------------------------------------
// StormLib internal global variables
extern LCID lcFileLocale; // Preferred file locale
//-----------------------------------------------------------------------------
// Conversion to uppercase/lowercase (and "/" to "\")
extern unsigned char AsciiToLowerTable[256];
extern unsigned char AsciiToUpperTable[256];
//-----------------------------------------------------------------------------
// Safe string functions
void StringCopy(char * szTarget, size_t cchTarget, const char * szSource);
void StringCat(char * szTarget, size_t cchTargetMax, const char * szSource);
#ifdef _UNICODE
void StringCopy(TCHAR * szTarget, size_t cchTarget, const char * szSource);
void StringCopy(char * szTarget, size_t cchTarget, const TCHAR * szSource);
void StringCopy(TCHAR * szTarget, size_t cchTarget, const TCHAR * szSource);
void StringCat(TCHAR * szTarget, size_t cchTargetMax, const TCHAR * szSource);
#endif
//-----------------------------------------------------------------------------
// Encryption and decryption functions
#define MPQ_HASH_TABLE_INDEX 0x000
#define MPQ_HASH_NAME_A 0x100
#define MPQ_HASH_NAME_B 0x200
#define MPQ_HASH_FILE_KEY 0x300
#define MPQ_HASH_KEY2_MIX 0x400
DWORD HashString(const char * szFileName, DWORD dwHashType);
DWORD HashStringSlash(const char * szFileName, DWORD dwHashType);
DWORD HashStringLower(const char * szFileName, DWORD dwHashType);
void InitializeMpqCryptography();
DWORD GetNearestPowerOfTwo(DWORD dwFileCount);
bool IsPseudoFileName(const char * szFileName, LPDWORD pdwFileIndex);
ULONGLONG HashStringJenkins(const char * szFileName);
DWORD GetDefaultSpecialFileFlags(DWORD dwFileSize, USHORT wFormatVersion);
void EncryptMpqBlock(void * pvDataBlock, DWORD dwLength, DWORD dwKey);
void DecryptMpqBlock(void * pvDataBlock, DWORD dwLength, DWORD dwKey);
DWORD DetectFileKeyBySectorSize(LPDWORD EncryptedData, DWORD dwSectorSize, DWORD dwSectorOffsLen);
DWORD DetectFileKeyByContent(void * pvEncryptedData, DWORD dwSectorSize, DWORD dwFileSize);
DWORD DecryptFileKey(const char * szFileName, ULONGLONG MpqPos, DWORD dwFileSize, DWORD dwFlags);
bool IsValidMD5(LPBYTE pbMd5);
bool IsValidSignature(LPBYTE pbSignature);
bool VerifyDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE expected_md5);
void CalculateDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE md5_hash);
//-----------------------------------------------------------------------------
// Handle validation functions
TMPQArchive * IsValidMpqHandle(HANDLE hMpq);
TMPQFile * IsValidFileHandle(HANDLE hFile);
//-----------------------------------------------------------------------------
// Support for MPQ file tables
ULONGLONG FileOffsetFromMpqOffset(TMPQArchive * ha, ULONGLONG MpqOffset);
ULONGLONG CalculateRawSectorOffset(TMPQFile * hf, DWORD dwSectorOffset);
int ConvertMpqHeaderToFormat4(TMPQArchive * ha, ULONGLONG MpqOffset, ULONGLONG FileSize, DWORD dwFlags, bool bIsWarcraft3Map);
bool IsValidHashEntry(TMPQArchive * ha, TMPQHash * pHash);
TMPQHash * FindFreeHashEntry(TMPQArchive * ha, DWORD dwStartIndex, DWORD dwName1, DWORD dwName2, LCID lcLocale);
TMPQHash * GetFirstHashEntry(TMPQArchive * ha, const char * szFileName);
TMPQHash * GetNextHashEntry(TMPQArchive * ha, TMPQHash * pFirstHash, TMPQHash * pPrevHash);
TMPQHash * AllocateHashEntry(TMPQArchive * ha, TFileEntry * pFileEntry, LCID lcLocale);
TMPQExtHeader * LoadExtTable(TMPQArchive * ha, ULONGLONG ByteOffset, size_t Size, DWORD dwSignature, DWORD dwKey);
TMPQHetTable * LoadHetTable(TMPQArchive * ha);
TMPQBetTable * LoadBetTable(TMPQArchive * ha);
TMPQBlock * LoadBlockTable(TMPQArchive * ha, bool bDontFixEntries = false);
TMPQBlock * TranslateBlockTable(TMPQArchive * ha, ULONGLONG * pcbTableSize, bool * pbNeedHiBlockTable);
ULONGLONG FindFreeMpqSpace(TMPQArchive * ha);
// Functions that load the HET and BET tables
int CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize);
int LoadAnyHashTable(TMPQArchive * ha);
int BuildFileTable(TMPQArchive * ha);
int DefragmentFileTable(TMPQArchive * ha);
int CreateFileTable(TMPQArchive * ha, DWORD dwFileTableSize);
int RebuildHetTable(TMPQArchive * ha);
int RebuildFileTable(TMPQArchive * ha, DWORD dwNewHashTableSize);
int SaveMPQTables(TMPQArchive * ha);
TMPQHetTable * CreateHetTable(DWORD dwEntryCount, DWORD dwTotalCount, DWORD dwHashBitSize, LPBYTE pbSrcData);
void FreeHetTable(TMPQHetTable * pHetTable);
TMPQBetTable * CreateBetTable(DWORD dwMaxFileCount);
void FreeBetTable(TMPQBetTable * pBetTable);
// Functions for finding files in the file table
TFileEntry * GetFileEntryLocale2(TMPQArchive * ha, const char * szFileName, LCID lcLocale, LPDWORD PtrHashIndex);
TFileEntry * GetFileEntryLocale(TMPQArchive * ha, const char * szFileName, LCID lcLocale);
TFileEntry * GetFileEntryExact(TMPQArchive * ha, const char * szFileName, LCID lcLocale, LPDWORD PtrHashIndex);
// Allocates file name in the file entry
void AllocateFileName(TMPQArchive * ha, TFileEntry * pFileEntry, const char * szFileName);
// Allocates new file entry in the MPQ tables. Reuses existing, if possible
TFileEntry * AllocateFileEntry(TMPQArchive * ha, const char * szFileName, LCID lcLocale, LPDWORD PtrHashIndex);
int RenameFileEntry(TMPQArchive * ha, TMPQFile * hf, const char * szNewFileName);
int DeleteFileEntry(TMPQArchive * ha, TMPQFile * hf);
// Invalidates entries for (listfile) and (attributes)
void InvalidateInternalFiles(TMPQArchive * ha);
// Retrieves information about the strong signature
bool QueryMpqSignatureInfo(TMPQArchive * ha, PMPQ_SIGNATURE_INFO pSignatureInfo);
//-----------------------------------------------------------------------------
// Support for alternate file formats (SBaseSubTypes.cpp)
int ConvertSqpHeaderToFormat4(TMPQArchive * ha, ULONGLONG FileSize, DWORD dwFlags);
TMPQHash * LoadSqpHashTable(TMPQArchive * ha);
TMPQBlock * LoadSqpBlockTable(TMPQArchive * ha);
int ConvertMpkHeaderToFormat4(TMPQArchive * ha, ULONGLONG FileSize, DWORD dwFlags);
void DecryptMpkTable(void * pvMpkTable, size_t cbSize);
TMPQHash * LoadMpkHashTable(TMPQArchive * ha);
TMPQBlock * LoadMpkBlockTable(TMPQArchive * ha);
int SCompDecompressMpk(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer);
//-----------------------------------------------------------------------------
// Common functions - MPQ File
TMPQFile * CreateFileHandle(TMPQArchive * ha, TFileEntry * pFileEntry);
TMPQFile * CreateWritableHandle(TMPQArchive * ha, DWORD dwFileSize);
void * LoadMpqTable(TMPQArchive * ha, ULONGLONG ByteOffset, DWORD dwCompressedSize, DWORD dwRealSize, DWORD dwKey, bool * pbTableIsCut);
int AllocateSectorBuffer(TMPQFile * hf);
int AllocatePatchInfo(TMPQFile * hf, bool bLoadFromFile);
int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile);
int AllocateSectorChecksums(TMPQFile * hf, bool bLoadFromFile);
int WritePatchInfo(TMPQFile * hf);
int WriteSectorOffsets(TMPQFile * hf);
int WriteSectorChecksums(TMPQFile * hf);
int WriteMemDataMD5(TFileStream * pStream, ULONGLONG RawDataOffs, void * pvRawData, DWORD dwRawDataSize, DWORD dwChunkSize, LPDWORD pcbTotalSize);
int WriteMpqDataMD5(TFileStream * pStream, ULONGLONG RawDataOffs, DWORD dwRawDataSize, DWORD dwChunkSize);
void FreeFileHandle(TMPQFile *& hf);
void FreeArchiveHandle(TMPQArchive *& ha);
//-----------------------------------------------------------------------------
// Patch functions
// Structure used for the patching process
typedef struct _TMPQPatcher
{
BYTE this_md5[MD5_DIGEST_SIZE]; // MD5 of the current file state
LPBYTE pbFileData1; // Primary working buffer
LPBYTE pbFileData2; // Secondary working buffer
DWORD cbMaxFileData; // Maximum allowed size of the patch data
DWORD cbFileData; // Current size of the result data
DWORD nCounter; // Counter of the patch process
} TMPQPatcher;
bool IsIncrementalPatchFile(const void * pvData, DWORD cbData, LPDWORD pdwPatchedFileSize);
int Patch_InitPatcher(TMPQPatcher * pPatcher, TMPQFile * hf);
int Patch_Process(TMPQPatcher * pPatcher, TMPQFile * hf);
void Patch_Finalize(TMPQPatcher * pPatcher);
//-----------------------------------------------------------------------------
// Utility functions
bool CheckWildCard(const char * szString, const char * szWildCard);
bool IsInternalMpqFileName(const char * szFileName);
template <typename XCHAR>
const XCHAR * GetPlainFileName(const XCHAR * szFileName)
{
const XCHAR * szPlainName = szFileName;
while(*szFileName != 0)
{
if(*szFileName == '\\' || *szFileName == '/')
szPlainName = szFileName + 1;
szFileName++;
}
return szPlainName;
}
//-----------------------------------------------------------------------------
// Internal support for MPQ modifications
int SFileAddFile_Init(
TMPQArchive * ha,
const char * szArchivedName,
ULONGLONG ft,
DWORD dwFileSize,
LCID lcLocale,
DWORD dwFlags,
TMPQFile ** phf
);
int SFileAddFile_Init(
TMPQArchive * ha,
TMPQFile * hfSrc,
TMPQFile ** phf
);
int SFileAddFile_Write(
TMPQFile * hf,
const void * pvData,
DWORD dwSize,
DWORD dwCompression
);
int SFileAddFile_Finish(
TMPQFile * hf
);
//-----------------------------------------------------------------------------
// Attributes support
int SAttrLoadAttributes(TMPQArchive * ha);
int SAttrFileSaveToMpq(TMPQArchive * ha);
//-----------------------------------------------------------------------------
// Listfile functions
int SListFileSaveToMpq(TMPQArchive * ha);
//-----------------------------------------------------------------------------
// Weak signature support
int SSignFileCreate(TMPQArchive * ha);
int SSignFileFinish(TMPQArchive * ha);
//-----------------------------------------------------------------------------
// Dump data support
#ifdef __STORMLIB_DUMP_DATA__
void DumpMpqHeader(TMPQHeader * pHeader);
void DumpHashTable(TMPQHash * pHashTable, DWORD dwHashTableSize);
void DumpHetAndBetTable(TMPQHetTable * pHetTable, TMPQBetTable * pBetTable);
void DumpFileTable(TFileEntry * pFileTable, DWORD dwFileTableSize);
#else
#define DumpMpqHeader(h) /* */
#define DumpHashTable(t, s) /* */
#define DumpHetAndBetTable(t, s) /* */
#define DumpFileTable(t, s) /* */
#endif
#endif // __STORMCOMMON_H__

1134
3rdParty/StormLib/src/StormLib.h vendored

File diff suppressed because it is too large Load Diff

309
3rdParty/StormLib/src/StormPort.h vendored

@ -0,0 +1,309 @@
/*****************************************************************************/
/* StormPort.h Copyright (c) Marko Friedemann 2001 */
/*---------------------------------------------------------------------------*/
/* Portability module for the StormLib library. Contains a wrapper symbols */
/* to make the compilation under Linux work */
/* */
/* Author: Marko Friedemann <marko.friedemann@bmx-chemnitz.de> */
/* Created at: Mon Jan 29 18:26:01 CEST 2001 */
/* Computer: whiplash.flachland-chemnitz.de */
/* System: Linux 2.4.0 on i686 */
/* */
/* Author: Sam Wilkins <swilkins1337@gmail.com> */
/* System: Mac OS X and port to big endian processor */
/* */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 29.01.01 1.00 Mar Created */
/* 24.03.03 1.01 Lad Some cosmetic changes */
/* 12.11.03 1.02 Dan Macintosh compatibility */
/* 24.07.04 1.03 Sam Mac OS X compatibility */
/* 22.11.06 1.04 Sam Mac OS X compatibility (for StormLib 6.0) */
/* 31.12.06 1.05 XPinguin Full GNU/Linux compatibility */
/* 17.10.12 1.05 Lad Moved error codes so they don't overlap with errno.h */
/*****************************************************************************/
#ifndef __STORMPORT_H__
#define __STORMPORT_H__
#ifndef __cplusplus
#define bool char
#define true 1
#define false 0
#endif
//-----------------------------------------------------------------------------
// Defines for Windows
#if !defined(PLATFORM_DEFINED) && defined(_WIN32)
// In MSVC 8.0, there are some functions declared as deprecated.
#if _MSC_VER >= 1400
#define _CRT_SECURE_NO_DEPRECATE
#define _CRT_NON_CONFORMING_SWPRINTFS
#endif
#include <tchar.h>
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <windows.h>
#include <wininet.h>
#define PLATFORM_LITTLE_ENDIAN
#ifdef _WIN64
#define PLATFORM_64BIT
#else
#define PLATFORM_32BIT
#endif
#define PLATFORM_WINDOWS
#define PLATFORM_DEFINED // The platform is known now
#endif
//-----------------------------------------------------------------------------
// Defines for Mac
#if !defined(PLATFORM_DEFINED) && defined(__APPLE__) // Mac BSD API
// Macintosh
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
// Support for PowerPC on Max OS X
#if (__ppc__ == 1) || (__POWERPC__ == 1) || (_ARCH_PPC == 1)
#include <stdint.h>
#include <CoreFoundation/CFByteOrder.h>
#endif
#define PKEXPORT
#define __SYS_ZLIB
#define __SYS_BZLIB
#ifndef __BIG_ENDIAN__
#define PLATFORM_LITTLE_ENDIAN
#endif
#define PLATFORM_MAC
#define PLATFORM_DEFINED // The platform is known now
#endif
//-----------------------------------------------------------------------------
// Assumption: we are not on Windows nor Macintosh, so this must be linux *grin*
#if !defined(PLATFORM_DEFINED)
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <errno.h>
#define PLATFORM_LITTLE_ENDIAN
#define PLATFORM_LINUX
#define PLATFORM_DEFINED
#endif
//-----------------------------------------------------------------------------
// Definition of Windows-specific types for non-Windows platforms
#ifndef PLATFORM_WINDOWS
#if __LP64__
#define PLATFORM_64BIT
#else
#define PLATFORM_32BIT
#endif
// Typedefs for ANSI C
typedef unsigned char BYTE;
typedef unsigned short USHORT;
typedef int LONG;
typedef unsigned int DWORD;
typedef unsigned long DWORD_PTR;
typedef long LONG_PTR;
typedef long INT_PTR;
typedef long long LONGLONG;
typedef unsigned long long ULONGLONG;
typedef void * HANDLE;
typedef void * LPOVERLAPPED; // Unsupported on Linux and Mac
typedef char TCHAR;
typedef unsigned int LCID;
typedef LONG * PLONG;
typedef DWORD * LPDWORD;
typedef BYTE * LPBYTE;
typedef const char * LPCTSTR;
typedef const char * LPCSTR;
typedef char * LPTSTR;
typedef char * LPSTR;
#ifdef PLATFORM_32BIT
#define _LZMA_UINT32_IS_ULONG
#endif
// Some Windows-specific defines
#ifndef MAX_PATH
#define MAX_PATH 1024
#endif
#ifndef _countof
#define _countof(x) (sizeof(x) / sizeof(x[0]))
#endif
// MINIWIN change
#define WINAPI __attribute__((stdcall))
#define FILE_BEGIN SEEK_SET
#define FILE_CURRENT SEEK_CUR
#define FILE_END SEEK_END
#define _T(x) x
#define _tcslen strlen
#define _tcscpy strcpy
#define _tcscat strcat
#define _tcschr strchr
#define _tcsrchr strrchr
#define _tcsstr strstr
#define _tcsnicmp strncasecmp
#define _tprintf printf
#define _stprintf sprintf
#define _tremove remove
#define _tmain main
#define _stricmp strcasecmp
#define _strnicmp strncasecmp
#define _tcsicmp strcasecmp
#define _tcsnicmp strncasecmp
#endif // !PLATFORM_WINDOWS
// 64-bit calls are supplied by "normal" calls on Mac
#if defined(PLATFORM_MAC)
#define stat64 stat
#define fstat64 fstat
#define lseek64 lseek
#define ftruncate64 ftruncate
#define off64_t off_t
#define O_LARGEFILE 0
#endif
// Platform-specific error codes for UNIX-based platforms
#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX)
#define ERROR_SUCCESS 0
#define ERROR_FILE_NOT_FOUND ENOENT
#define ERROR_ACCESS_DENIED EPERM
#define ERROR_INVALID_HANDLE EBADF
#define ERROR_NOT_ENOUGH_MEMORY ENOMEM
#define ERROR_NOT_SUPPORTED ENOTSUP
#define ERROR_INVALID_PARAMETER EINVAL
#define ERROR_NEGATIVE_SEEK EINVAL
#define ERROR_DISK_FULL ENOSPC
#define ERROR_ALREADY_EXISTS EEXIST
#define ERROR_INSUFFICIENT_BUFFER ENOBUFS
#define ERROR_BAD_FORMAT 1000 // No such error code under Linux
#define ERROR_NO_MORE_FILES 1001 // No such error code under Linux
#define ERROR_HANDLE_EOF 1002 // No such error code under Linux
#define ERROR_CAN_NOT_COMPLETE 1003 // No such error code under Linux
#define ERROR_FILE_CORRUPT 1004 // No such error code under Linux
#endif
//-----------------------------------------------------------------------------
// Swapping functions
#ifdef PLATFORM_LITTLE_ENDIAN
#define BSWAP_INT16_UNSIGNED(a) (a)
#define BSWAP_INT16_SIGNED(a) (a)
#define BSWAP_INT32_UNSIGNED(a) (a)
#define BSWAP_INT32_SIGNED(a) (a)
#define BSWAP_INT64_SIGNED(a) (a)
#define BSWAP_INT64_UNSIGNED(a) (a)
#define BSWAP_ARRAY16_UNSIGNED(a,b) {}
#define BSWAP_ARRAY32_UNSIGNED(a,b) {}
#define BSWAP_ARRAY64_UNSIGNED(a,b) {}
#define BSWAP_PART_HEADER(a) {}
#define BSWAP_TMPQHEADER(a,b) {}
#define BSWAP_TMPKHEADER(a) {}
#else
#ifdef __cplusplus
extern "C" {
#endif
int16_t SwapInt16(uint16_t);
uint16_t SwapUInt16(uint16_t);
int32_t SwapInt32(uint32_t);
uint32_t SwapUInt32(uint32_t);
int64_t SwapInt64(uint64_t);
uint64_t SwapUInt64(uint64_t);
void ConvertUInt16Buffer(void * ptr, size_t length);
void ConvertUInt32Buffer(void * ptr, size_t length);
void ConvertUInt64Buffer(void * ptr, size_t length);
void ConvertTMPQUserData(void *userData);
void ConvertTMPQHeader(void *header, uint16_t wPart);
void ConvertTMPKHeader(void *header);
#ifdef __cplusplus
}
#endif
#define BSWAP_INT16_SIGNED(a) SwapInt16((a))
#define BSWAP_INT16_UNSIGNED(a) SwapUInt16((a))
#define BSWAP_INT32_SIGNED(a) SwapInt32((a))
#define BSWAP_INT32_UNSIGNED(a) SwapUInt32((a))
#define BSWAP_INT64_SIGNED(a) SwapInt64((a))
#define BSWAP_INT64_UNSIGNED(a) SwapUInt64((a))
#define BSWAP_ARRAY16_UNSIGNED(a,b) ConvertUInt16Buffer((a),(b))
#define BSWAP_ARRAY32_UNSIGNED(a,b) ConvertUInt32Buffer((a),(b))
#define BSWAP_ARRAY64_UNSIGNED(a,b) ConvertUInt64Buffer((a),(b))
#define BSWAP_TMPQHEADER(a,b) ConvertTMPQHeader((a),(b))
#define BSWAP_TMPKHEADER(a) ConvertTMPKHeader((a))
#endif
//-----------------------------------------------------------------------------
// Macro for deprecated symbols
/*
#ifdef _MSC_VER
#if _MSC_FULL_VER >= 140050320
#define STORMLIB_DEPRECATED(_Text) __declspec(deprecated(_Text))
#else
#define STORMLIB_DEPRECATED(_Text) __declspec(deprecated)
#endif
#else
#ifdef __GNUC__
#define STORMLIB_DEPRECATED(_Text) __attribute__((deprecated))
#else
#define STORMLIB_DEPRECATED(_Text) __attribute__((deprecated(_Text)))
#endif
#endif
// When a flag is deprecated, use this macro
#ifndef _STORMLIB_NO_DEPRECATE
#define STORMLIB_DEPRECATED_FLAG(type, oldflag, newflag) \
const STORMLIB_DEPRECATED(#oldflag " is deprecated. Use " #newflag ". To supress this warning, define _STORMLIB_NO_DEPRECATE") static type oldflag = (type)newflag;
#else
#define STORMLIB_DEPRECATED_FLAG(type, oldflag, newflag) static type oldflag = (type)newflag;
#endif
*/
//
// MINIWIN changes
//
#define bool int
void TranslateFileName(char* dst, int dstLen, const char* src);
#endif // __STORMPORT_H__
Loading…
Cancel
Save