mirror of
https://github.com/mangosfour/server.git
synced 2026-01-02 01:37:05 +00:00
Some missing from merge.
Signed-off-by: Salja <salja2012@hotmail.de>
This commit is contained in:
parent
ec939a5bce
commit
f4be15a7af
1895 changed files with 160408 additions and 53601 deletions
|
|
@ -17,55 +17,47 @@
|
|||
#include "StormLib.h"
|
||||
#include "StormCommon.h"
|
||||
|
||||
#define HEADER_SEARCH_BUFFER_SIZE 0x1000
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Local functions */
|
||||
/*****************************************************************************/
|
||||
|
||||
static bool IsAviFile(DWORD * HeaderData)
|
||||
static bool IsAviFile(void * pvFileBegin)
|
||||
{
|
||||
DWORD DwordValue0 = BSWAP_INT32_UNSIGNED(HeaderData[0]);
|
||||
DWORD DwordValue2 = BSWAP_INT32_UNSIGNED(HeaderData[2]);
|
||||
DWORD DwordValue3 = BSWAP_INT32_UNSIGNED(HeaderData[3]);
|
||||
LPDWORD AviHeader = (DWORD *)pvFileBegin;
|
||||
DWORD DwordValue0 = BSWAP_INT32_UNSIGNED(AviHeader[0]);
|
||||
DWORD DwordValue2 = BSWAP_INT32_UNSIGNED(AviHeader[2]);
|
||||
DWORD DwordValue3 = BSWAP_INT32_UNSIGNED(AviHeader[3]);
|
||||
|
||||
// Test for 'RIFF', 'AVI ' or 'LIST'
|
||||
return (DwordValue0 == 0x46464952 && DwordValue2 == 0x20495641 && DwordValue3 == 0x5453494C);
|
||||
}
|
||||
|
||||
static bool IsWarcraft3Map(DWORD * HeaderData)
|
||||
static TFileBitmap * CreateFileBitmap(TMPQArchive * ha, TMPQBitmap * pMpqBitmap, bool bFileIsComplete)
|
||||
{
|
||||
DWORD DwordValue0 = BSWAP_INT32_UNSIGNED(HeaderData[0]);
|
||||
DWORD DwordValue1 = BSWAP_INT32_UNSIGNED(HeaderData[1]);
|
||||
TFileBitmap * pBitmap;
|
||||
size_t nLength;
|
||||
|
||||
return (DwordValue0 == 0x57334D48 && DwordValue1 == 0x00000000);
|
||||
}
|
||||
// Calculate the length of the bitmap in blocks and in bytes
|
||||
nLength = (size_t)(((ha->pHeader->ArchiveSize64 - 1) / pMpqBitmap->dwBlockSize) + 1);
|
||||
nLength = (size_t)(((nLength - 1) / 8) + 1);
|
||||
|
||||
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)
|
||||
// Allocate the file bitmap
|
||||
pBitmap = (TFileBitmap *)STORM_ALLOC(BYTE, sizeof(TFileBitmap) + nLength);
|
||||
if(pBitmap != NULL)
|
||||
{
|
||||
// Move to the position given by the userdata
|
||||
ByteOffset += pUserData->dwHeaderOffs;
|
||||
// Fill the structure
|
||||
pBitmap->StartOffset = ha->MpqPos;
|
||||
pBitmap->EndOffset = ha->MpqPos + ha->pHeader->ArchiveSize64;
|
||||
pBitmap->IsComplete = bFileIsComplete ? 1 : 0;
|
||||
pBitmap->BitmapSize = (DWORD)nLength;
|
||||
pBitmap->BlockSize = pMpqBitmap->dwBlockSize;
|
||||
pBitmap->Reserved = 0;
|
||||
|
||||
// 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;
|
||||
}
|
||||
// Copy the file bitmap
|
||||
memcpy((pBitmap + 1), (pMpqBitmap + 1), nLength);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return pBitmap;
|
||||
}
|
||||
|
||||
// This function gets the right positions of the hash table and the block table.
|
||||
|
|
@ -93,7 +85,7 @@ static int VerifyMpqTablePositions(TMPQArchive * ha, ULONGLONG FileSize)
|
|||
// Check the begin of hash table
|
||||
if(pHeader->wHashTablePosHi || pHeader->dwHashTablePos)
|
||||
{
|
||||
ByteOffset = FileOffsetFromMpqOffset(ha, MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos));
|
||||
ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos);
|
||||
if(ByteOffset > FileSize)
|
||||
return ERROR_BAD_FORMAT;
|
||||
}
|
||||
|
|
@ -101,7 +93,7 @@ static int VerifyMpqTablePositions(TMPQArchive * ha, ULONGLONG FileSize)
|
|||
// Check the begin of block table
|
||||
if(pHeader->wBlockTablePosHi || pHeader->dwBlockTablePos)
|
||||
{
|
||||
ByteOffset = FileOffsetFromMpqOffset(ha, MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos));
|
||||
ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos);
|
||||
if(ByteOffset > FileSize)
|
||||
return ERROR_BAD_FORMAT;
|
||||
}
|
||||
|
|
@ -118,6 +110,7 @@ static int VerifyMpqTablePositions(TMPQArchive * ha, ULONGLONG FileSize)
|
|||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Public functions */
|
||||
/*****************************************************************************/
|
||||
|
|
@ -146,213 +139,137 @@ LCID WINAPI SFileSetLocale(LCID lcNewLocale)
|
|||
// phMpq - Pointer to store open archive handle
|
||||
|
||||
bool WINAPI SFileOpenArchive(
|
||||
const TCHAR * szMpqName,
|
||||
DWORD dwPriority,
|
||||
DWORD dwFlags,
|
||||
HANDLE * phMpq)
|
||||
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;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Verify the parameters
|
||||
if(szMpqName == NULL || *szMpqName == 0 || phMpq == NULL)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
}
|
||||
nError = ERROR_INVALID_PARAMETER;
|
||||
|
||||
// One time initialization of MPQ cryptography
|
||||
InitializeMpqCryptography();
|
||||
|
||||
// If not forcing MPQ v 1.0, also use file bitmap
|
||||
dwStreamFlags |= (dwFlags & MPQ_OPEN_FORCE_MPQ_V1) ? 0 : STREAM_FLAG_USE_BITMAP;
|
||||
dwPriority = dwPriority;
|
||||
|
||||
// Open the MPQ archive file
|
||||
pStream = FileStream_OpenFile(szMpqName, 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;
|
||||
// Initialize the stream
|
||||
pStream = FileStream_OpenFile(szMpqName, (dwFlags & STREAM_OPTIONS_MASK));
|
||||
if(pStream == NULL)
|
||||
nError = GetLastError();
|
||||
}
|
||||
|
||||
// Allocate the MPQhandle
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
FileStream_GetSize(pStream, &FileSize);
|
||||
if((ha = STORM_ALLOC(TMPQArchive, 1)) == NULL)
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
// Allocate buffer for searching MPQ header
|
||||
// Initialize handle structure and allocate structure for 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;
|
||||
// Remember if the archive is open for write
|
||||
if(FileStream_IsReadOnly(ha->pStream))
|
||||
ha->dwFlags |= MPQ_FLAG_READ_ONLY;
|
||||
|
||||
// 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;
|
||||
if(dwFlags & MPQ_OPEN_CHECK_SECTOR_CRC)
|
||||
ha->dwFlags |= MPQ_FLAG_CHECK_SECTOR_CRC;
|
||||
}
|
||||
|
||||
// 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
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
ULONGLONG SearchPos = 0;
|
||||
DWORD dwHeaderID;
|
||||
|
||||
// Find the offset of MPQ header within the file
|
||||
while(bSearchComplete == false && SearchOffset < EndOfSearch)
|
||||
while(SearchPos < FileSize)
|
||||
{
|
||||
// Always read at least 0x1000 bytes for performance.
|
||||
// This is what Storm.dll (2002) does.
|
||||
DWORD dwBytesAvailable = HEADER_SEARCH_BUFFER_SIZE;
|
||||
DWORD dwInBufferOffset = 0;
|
||||
DWORD dwBytesAvailable = MPQ_HEADER_SIZE_V4;
|
||||
|
||||
// Cut the bytes available, if needed
|
||||
if((FileSize - SearchOffset) < HEADER_SEARCH_BUFFER_SIZE)
|
||||
dwBytesAvailable = (DWORD)(FileSize - SearchOffset);
|
||||
if((FileSize - SearchPos) < MPQ_HEADER_SIZE_V4)
|
||||
dwBytesAvailable = (DWORD)(FileSize - SearchPos);
|
||||
|
||||
// Read the eventual MPQ header
|
||||
if(!FileStream_Read(ha->pStream, &SearchOffset, pbHeaderBuffer, dwBytesAvailable))
|
||||
if(!FileStream_Read(ha->pStream, &SearchPos, ha->HeaderData, dwBytesAvailable))
|
||||
{
|
||||
nError = GetLastError();
|
||||
break;
|
||||
}
|
||||
|
||||
// There are AVI files from Warcraft III with 'MPQ' extension.
|
||||
if(SearchOffset == 0)
|
||||
if(SearchPos == 0 && IsAviFile(ha->HeaderData))
|
||||
{
|
||||
if(IsAviFile((DWORD *)pbHeaderBuffer))
|
||||
{
|
||||
nError = ERROR_AVI_FILE;
|
||||
break;
|
||||
}
|
||||
|
||||
bIsWarcraft3Map = IsWarcraft3Map((DWORD *)pbHeaderBuffer);
|
||||
nError = ERROR_AVI_FILE;
|
||||
break;
|
||||
}
|
||||
|
||||
// Search the header buffer
|
||||
while(dwInBufferOffset < dwBytesAvailable)
|
||||
// If there is the MPQ user data signature, process it
|
||||
dwHeaderID = BSWAP_INT32_UNSIGNED(*(LPDWORD)ha->HeaderData);
|
||||
if(dwHeaderID == ID_MPQ_USERDATA && ha->pUserData == NULL)
|
||||
{
|
||||
// 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)
|
||||
// Ignore the MPQ user data completely if the caller wants to open the MPQ as V1.0
|
||||
if((dwFlags & MPQ_OPEN_FORCE_MPQ_V1) == 0)
|
||||
{
|
||||
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));
|
||||
// Fill the user data header
|
||||
ha->pUserData = &ha->UserData;
|
||||
memcpy(ha->pUserData, ha->HeaderData, sizeof(TMPQUserData));
|
||||
BSWAP_TMPQUSERDATA(ha->pUserData);
|
||||
|
||||
// Continue searching from that position
|
||||
SearchOffset += ha->pUserData->dwHeaderOffs;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Remember the position of the user data and continue search
|
||||
ha->UserDataPos = SearchPos;
|
||||
SearchPos += ha->pUserData->dwHeaderOffs;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// There must be MPQ header signature
|
||||
if(dwHeaderID == ID_MPQ)
|
||||
{
|
||||
// Save the position where the MPQ header has been found
|
||||
if(ha->pUserData == NULL)
|
||||
ha->UserDataPos = SearchPos;
|
||||
ha->pHeader = (TMPQHeader *)ha->HeaderData;
|
||||
ha->MpqPos = SearchPos;
|
||||
|
||||
// Now convert the header to version 4
|
||||
BSWAP_TMPQHEADER(ha->pHeader);
|
||||
nError = ConvertMpqHeaderToFormat4(ha, FileSize, dwFlags);
|
||||
break;
|
||||
}
|
||||
|
||||
// Move to the next possible offset
|
||||
SearchPos += 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;
|
||||
}
|
||||
// If we haven't found MPQ header in the file, it's an error
|
||||
if(ha->pHeader == NULL)
|
||||
nError = ERROR_BAD_FORMAT;
|
||||
}
|
||||
|
||||
// Fix table positions according to format
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Dump the header
|
||||
// DumpMpqHeader(ha->pHeader);
|
||||
// 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
|
||||
// and probably 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)
|
||||
|
|
@ -363,25 +280,10 @@ bool WINAPI SFileOpenArchive(
|
|||
ha->pUserData = NULL;
|
||||
}
|
||||
|
||||
// Anti-overflow. If the hash table size in the header is
|
||||
// higher than 0x10000000, it would overflow in 32-bit version
|
||||
// Observed in the malformed Warcraft III maps
|
||||
// Example map: MPQ_2016_v1_ProtectedMap_TableSizeOverflow.w3x
|
||||
ha->pHeader->dwBlockTableSize = (ha->pHeader->dwBlockTableSize & BLOCK_INDEX_MASK);
|
||||
ha->pHeader->dwHashTableSize = (ha->pHeader->dwHashTableSize & BLOCK_INDEX_MASK);
|
||||
|
||||
// Both MPQ_OPEN_NO_LISTFILE or MPQ_OPEN_NO_ATTRIBUTES trigger read only mode
|
||||
if(dwFlags & (MPQ_OPEN_NO_LISTFILE | MPQ_OPEN_NO_ATTRIBUTES))
|
||||
ha->dwFlags |= MPQ_FLAG_READ_ONLY;
|
||||
|
||||
// Check if the caller wants to force adding listfile
|
||||
if(dwFlags & MPQ_OPEN_FORCE_LISTFILE)
|
||||
ha->dwFlags |= MPQ_FLAG_LISTFILE_FORCE;
|
||||
|
||||
// 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);
|
||||
|
||||
|
|
@ -389,6 +291,24 @@ bool WINAPI SFileOpenArchive(
|
|||
nError = VerifyMpqTablePositions(ha, FileSize);
|
||||
}
|
||||
|
||||
// Check if the MPQ has data bitmap. If yes, we can verify if the MPQ is complete
|
||||
if(nError == ERROR_SUCCESS && ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_4)
|
||||
{
|
||||
TFileBitmap * pBitmap;
|
||||
bool bFileIsComplete = true;
|
||||
|
||||
LoadMpqDataBitmap(ha, FileSize, &bFileIsComplete);
|
||||
if(ha->pBitmap != NULL && bFileIsComplete == false)
|
||||
{
|
||||
// Convert the MPQ bitmap to the file bitmap
|
||||
pBitmap = CreateFileBitmap(ha, ha->pBitmap, bFileIsComplete);
|
||||
|
||||
// Set the data bitmap into the file stream for additional checks
|
||||
FileStream_SetBitmap(ha->pStream, pBitmap);
|
||||
ha->dwFlags |= MPQ_FLAG_READ_ONLY;
|
||||
}
|
||||
}
|
||||
|
||||
// Read the hash table. Ignore the result, as hash table is no longer required
|
||||
// Read HET table. Ignore the result, as HET table is no longer required
|
||||
if(nError == ERROR_SUCCESS)
|
||||
|
|
@ -400,86 +320,93 @@ bool WINAPI SFileOpenArchive(
|
|||
// the block table, BET table, hi-block table, (attributes) and (listfile).
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
nError = BuildFileTable(ha);
|
||||
nError = BuildFileTable(ha, FileSize);
|
||||
}
|
||||
|
||||
// Verify the file table, if no kind of protection was detected
|
||||
if(nError == ERROR_SUCCESS && (ha->dwFlags & MPQ_FLAG_PROTECTED) == 0)
|
||||
{
|
||||
TFileEntry * pFileTableEnd = ha->pFileTable + ha->pHeader->dwBlockTableSize;
|
||||
// ULONGLONG ArchiveSize = 0;
|
||||
ULONGLONG RawFilePos;
|
||||
|
||||
// Parse all file entries
|
||||
for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
|
||||
{
|
||||
// If that file entry is valid, check the file position
|
||||
if(pFileEntry->dwFlags & MPQ_FILE_EXISTS)
|
||||
{
|
||||
// Get the 64-bit file position,
|
||||
// relative to the begin of the file
|
||||
RawFilePos = ha->MpqPos + pFileEntry->ByteOffset;
|
||||
|
||||
// Begin of the file must be within range
|
||||
if(RawFilePos > FileSize)
|
||||
{
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
break;
|
||||
}
|
||||
|
||||
// End of the file must be within range
|
||||
RawFilePos += pFileEntry->dwCmpSize;
|
||||
if(RawFilePos > FileSize)
|
||||
{
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
break;
|
||||
}
|
||||
|
||||
// Also, we remember end of the file
|
||||
// if(RawFilePos > ArchiveSize)
|
||||
// ArchiveSize = RawFilePos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
// Save the flags 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;
|
||||
}
|
||||
|
||||
// Ignore result of the operation. (listfile) is optional.
|
||||
SFileAddListFile((HANDLE)ha, NULL);
|
||||
}
|
||||
|
||||
// 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)
|
||||
if(nError == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_ATTRIBUTES) == 0)
|
||||
{
|
||||
// Quick check for (attributes)
|
||||
// Save the flags for (attributes)
|
||||
pFileEntry = GetFileEntryLocale(ha, ATTRIBUTES_NAME, LANG_NEUTRAL);
|
||||
if(pFileEntry != NULL)
|
||||
{
|
||||
// Ignore result of the operation. (attributes) is optional.
|
||||
SAttrLoadAttributes(ha);
|
||||
ha->dwFileFlags2 = pFileEntry->dwFlags;
|
||||
}
|
||||
}
|
||||
|
||||
// Remember whether the archive has weak signature. Only for MPQs format 1.0.
|
||||
if(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;
|
||||
// Ignore result of the operation. (attributes) is optional.
|
||||
SAttrLoadAttributes(ha);
|
||||
}
|
||||
|
||||
// Cleanup and exit
|
||||
if(nError != ERROR_SUCCESS)
|
||||
{
|
||||
FileStream_Close(pStream);
|
||||
FreeArchiveHandle(ha);
|
||||
FreeMPQArchive(ha);
|
||||
SetLastError(nError);
|
||||
ha = NULL;
|
||||
}
|
||||
|
||||
// Free the header buffer
|
||||
if(pbHeaderBuffer != NULL)
|
||||
STORM_FREE(pbHeaderBuffer);
|
||||
if(phMpq != NULL)
|
||||
*phMpq = ha;
|
||||
*phMpq = ha;
|
||||
return (nError == ERROR_SUCCESS);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// bool WINAPI SFileSetDownloadCallback(HANDLE, SFILE_DOWNLOAD_CALLBACK, void *);
|
||||
//
|
||||
// Sets a callback that is called when content is downloaded from the master MPQ
|
||||
//
|
||||
// SFileGetArchiveBitmap
|
||||
|
||||
bool WINAPI SFileSetDownloadCallback(HANDLE hMpq, SFILE_DOWNLOAD_CALLBACK DownloadCB, void * pvUserData)
|
||||
bool WINAPI SFileGetArchiveBitmap(HANDLE hMpq, TFileBitmap * pBitmap, DWORD Length, LPDWORD LengthNeeded)
|
||||
{
|
||||
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);
|
||||
return FileStream_GetBitmap(ha->pStream, pBitmap, Length, LengthNeeded);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
@ -493,75 +420,39 @@ bool WINAPI SFileSetDownloadCallback(HANDLE hMpq, SFILE_DOWNLOAD_CALLBACK Downlo
|
|||
|
||||
bool WINAPI SFileFlushArchive(HANDLE hMpq)
|
||||
{
|
||||
TMPQArchive * ha;
|
||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
int nResultError = ERROR_SUCCESS;
|
||||
int nError;
|
||||
|
||||
// Do nothing if 'hMpq' is bad parameter
|
||||
if((ha = IsValidMpqHandle(hMpq)) == NULL)
|
||||
if(!IsValidMpqHandle(ha))
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only if the MPQ was changed
|
||||
// If the (listfile) has been invalidated, save it
|
||||
if(ha->dwFlags & MPQ_FLAG_INV_LISTFILE)
|
||||
{
|
||||
nError = SListFileSaveToMpq(ha);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
nResultError = nError;
|
||||
}
|
||||
|
||||
// If the (attributes) has been invalidated, save it
|
||||
if(ha->dwFlags & MPQ_FLAG_INV_ATTRIBUTES)
|
||||
{
|
||||
nError = SAttrFileSaveToMpq(ha);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
nResultError = nError;
|
||||
}
|
||||
|
||||
// Save HET table, BET table, hash table, block table, hi-block table
|
||||
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)
|
||||
{
|
||||
nError = SSignFileCreate(ha);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
nResultError = nError;
|
||||
}
|
||||
|
||||
if(ha->dwFlags & (MPQ_FLAG_LISTFILE_NEW | MPQ_FLAG_LISTFILE_FORCE))
|
||||
{
|
||||
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)
|
||||
{
|
||||
nError = SSignFileFinish(ha);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
nResultError = nError;
|
||||
}
|
||||
}
|
||||
|
||||
// We are no longer saving internal MPQ structures
|
||||
ha->dwFlags &= ~MPQ_FLAG_SAVING_TABLES;
|
||||
nError = SaveMPQTables(ha);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
nResultError = nError;
|
||||
}
|
||||
|
||||
// Return the error
|
||||
|
|
@ -576,25 +467,14 @@ bool WINAPI SFileFlushArchive(HANDLE hMpq)
|
|||
|
||||
bool WINAPI SFileCloseArchive(HANDLE hMpq)
|
||||
{
|
||||
TMPQArchive * ha = IsValidMpqHandle(hMpq);
|
||||
bool bResult = false;
|
||||
|
||||
// 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;
|
||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
bool bResult;
|
||||
|
||||
// Flush all unsaved data to the storage
|
||||
bResult = SFileFlushArchive(hMpq);
|
||||
|
||||
// Free all memory used by MPQ archive
|
||||
FreeArchiveHandle(ha);
|
||||
FreeMPQArchive(ha);
|
||||
return bResult;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue