diff options
author | Ladislav Zezula <zezula@volny.cz> | 2023-12-30 09:02:20 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-30 09:02:20 +0100 |
commit | 8978bdc8214f4ec3543ac11962fc094c3b0803b2 (patch) | |
tree | 0b819048623265303dc22374831cc1f84be6f328 | |
parent | 61b6665433e8de0e4be68e69ddfb0128421d4057 (diff) | |
parent | 909ff807e78b0b92afffaf0d5ed7149982d8ac33 (diff) |
Merge pull request #316 from ladislav-zezula/LZ_HashEntryIndex_Fix
Multiple changes
-rw-r--r-- | make-msvc.bat | 6 | ||||
-rw-r--r-- | src/SBaseCommon.cpp | 2 | ||||
-rw-r--r-- | src/SBaseFileTable.cpp | 43 | ||||
-rw-r--r-- | src/SFileCreateArchive.cpp | 3 | ||||
-rw-r--r-- | src/SFileOpenArchive.cpp | 5 | ||||
-rw-r--r-- | src/SFileOpenFileEx.cpp | 18 | ||||
-rw-r--r-- | src/StormCommon.h | 1 | ||||
-rw-r--r-- | src/StormLib.h | 1 | ||||
-rwxr-xr-x | test/StormTest.cpp | 50 |
9 files changed, 93 insertions, 36 deletions
diff --git a/make-msvc.bat b/make-msvc.bat index 7d1052a..de634fa 100644 --- a/make-msvc.bat +++ b/make-msvc.bat @@ -13,8 +13,8 @@ if exist "%ProgramW6432%" set PROGRAM_FILES_X64=%ProgramW6432% if exist "%ProgramFiles%" set PROGRAM_FILES_DIR=%ProgramFiles% if exist "%ProgramFiles(x86)%" set PROGRAM_FILES_DIR=%ProgramFiles(x86)% -:: Determine the installed version of Visual Studio (Prioritize Enterprise over Professional) -if exist "%PROGRAM_FILES_DIR%\Microsoft Visual Studio 9.0\VC\vcvarsall.bat" set VCVARS_2008=%PROGRAM_FILES_DIR%\Microsoft Visual Studio 9.0\VC\vcvarsall.bat +:: Determine the installed version of Visual Studio (Enterprise > Professional > Community) +::if exist "%PROGRAM_FILES_DIR%\Microsoft Visual Studio 9.0\VC\vcvarsall.bat" set VCVARS_2008=%PROGRAM_FILES_DIR%\Microsoft Visual Studio 9.0\VC\vcvarsall.bat if exist "%PROGRAM_FILES_DIR%\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" set VCVARS_20xx=%PROGRAM_FILES_DIR%\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat if exist "%PROGRAM_FILES_DIR%\Microsoft Visual Studio\2017\Professional\VC\Auxiliary\Build\vcvarsall.bat" set VCVARS_20xx=%PROGRAM_FILES_DIR%\Microsoft Visual Studio\2017\Professional\VC\Auxiliary\Build\vcvarsall.bat if exist "%PROGRAM_FILES_DIR%\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" set VCVARS_20xx=%PROGRAM_FILES_DIR%\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat @@ -25,7 +25,7 @@ if exist "%PROGRAM_FILES_X64%\Microsoft Visual Studio\2022\Enterprise\VC\Auxilia if exist "%PROGRAM_FILES_X64%\Microsoft Visual Studio\2022\Professional\VC\Auxiliary\Build\vcvarsall.bat" set VCVARS_20xx=%PROGRAM_FILES_X64%\Microsoft Visual Studio\2022\Professional\VC\Auxiliary\Build\vcvarsall.bat if exist "%PROGRAM_FILES_X64%\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" set VCVARS_20xx=%PROGRAM_FILES_X64%\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat -:: Build all libraries using Visual Studio 2008 and 2019 +:: Build all libraries using Visual Studio 2008 and 202x if not "x%VCVARS_2008%" == "x" call :BuildLibs "%VCVARS_2008%" x86 %LIB_NAME%_vs08.sln \vs2008 if not "x%VCVARS_2008%" == "x" call :BuildLibs "%VCVARS_2008%" x64 %LIB_NAME%_vs08.sln \vs2008 if not "x%VCVARS_20xx%" == "x" call :BuildLibs "%VCVARS_20xx%" x86 %LIB_NAME%.sln diff --git a/src/SBaseCommon.cpp b/src/SBaseCommon.cpp index 4fc473a..7415583 100644 --- a/src/SBaseCommon.cpp +++ b/src/SBaseCommon.cpp @@ -15,7 +15,7 @@ #include "StormLib.h"
#include "StormCommon.h"
-char StormLibCopyright[] = "StormLib v " STORMLIB_VERSION_STRING " Copyright Ladislav Zezula 1998-2014";
+char StormLibCopyright[] = "StormLib v " STORMLIB_VERSION_STRING " Copyright Ladislav Zezula 1998-2023";
//-----------------------------------------------------------------------------
// Local variables
diff --git a/src/SBaseFileTable.cpp b/src/SBaseFileTable.cpp index a53d6a6..ca14693 100644 --- a/src/SBaseFileTable.cpp +++ b/src/SBaseFileTable.cpp @@ -385,20 +385,39 @@ static ULONGLONG DetermineArchiveSize_V4( return ArchiveSize;
}
-ULONGLONG FileOffsetFromMpqOffset(TMPQArchive * ha, ULONGLONG MpqOffset)
+ULONGLONG GetFileOffsetMask(TMPQArchive * ha)
{
+ ULONGLONG FileOffsetMask = (ULONGLONG)(-1);
+
+ // Sanity checks
+ assert(ha != NULL);
+ assert(ha->pHeader != NULL);
+
+ // MPQs of format 1 are 32-bit only
if(ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1)
- {
- // For MPQ archive v1, any file offset is only 32-bit
- return (ULONGLONG)((DWORD)ha->MpqPos + (DWORD)MpqOffset);
- }
- else
- {
- // For MPQ archive v2+, file offsets are full 64-bit
- return ha->MpqPos + MpqOffset;
- }
+ FileOffsetMask = (ULONGLONG)(DWORD)(-1);
+ return FileOffsetMask;
}
+ULONGLONG FileOffsetFromMpqOffset(TMPQArchive * ha, ULONGLONG MpqOffset)
+{
+ return (ha->MpqPos + MpqOffset) & ha->FileOffsetMask;
+}
+
+//ULONGLONG FileOffsetFromMpqOffset(TMPQArchive * ha, ULONGLONG MpqOffset)
+//{
+// if(ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1)
+// {
+// // For MPQ archive v1, any file offset is only 32-bit
+// return (ULONGLONG)((DWORD)ha->MpqPos + (DWORD)MpqOffset);
+// }
+// else
+// {
+// // For MPQ archive v2+, file offsets are full 64-bit
+// return ha->MpqPos + MpqOffset;
+// }
+//}
+
ULONGLONG CalculateRawSectorOffset(
TMPQFile * hf,
DWORD dwSectorOffset)
@@ -418,9 +437,7 @@ ULONGLONG CalculateRawSectorOffset( // For MPQs version 1.0, the offset is purely 32-bit
//
- RawFilePos = hf->RawFilePos + dwSectorOffset;
- if(hf->ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1)
- RawFilePos = (DWORD)hf->ha->MpqPos + (DWORD)hf->pFileEntry->ByteOffset + dwSectorOffset;
+ RawFilePos = (hf->RawFilePos + dwSectorOffset) & hf->ha->FileOffsetMask;
// We also have to add patch header size, if patch header is present
if(hf->pPatchInfo != NULL)
diff --git a/src/SFileCreateArchive.cpp b/src/SFileCreateArchive.cpp index c0ea367..bae3dcc 100644 --- a/src/SFileCreateArchive.cpp +++ b/src/SFileCreateArchive.cpp @@ -238,6 +238,9 @@ bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCrea pHeader->dwBlockTablePos = pHeader->dwHashTablePos + dwHashTableSize * sizeof(TMPQHash); pHeader->dwBlockTableSize = dwBlockTableSize; + // Set the mask for MPQ byte offset + ha->FileOffsetMask = GetFileOffsetMask(ha); + // For MPQs version 4 and higher, we set the size of raw data block // for calculating MD5 if(pCreateInfo->dwMpqVersion >= MPQ_FORMAT_VERSION_4) diff --git a/src/SFileOpenArchive.cpp b/src/SFileOpenArchive.cpp index baad848..4b2d3b4 100644 --- a/src/SFileOpenArchive.cpp +++ b/src/SFileOpenArchive.cpp @@ -463,6 +463,11 @@ bool WINAPI SFileOpenArchive( if(IsStarcraftBetaArchive(ha->pHeader)) ha->dwFlags |= MPQ_FLAG_STARCRAFT_BETA; + // Set the mask for the file offset. In MPQs version 1, + // all offsets are 32-bit and overflow is allowed. + // For MPQs v2+, file offset if 64-bit. + ha->FileOffsetMask = GetFileOffsetMask(ha); + // Maps from StarCraft and Warcraft III need special treatment switch(MapType) { diff --git a/src/SFileOpenFileEx.cpp b/src/SFileOpenFileEx.cpp index d26e01e..157ad15 100644 --- a/src/SFileOpenFileEx.cpp +++ b/src/SFileOpenFileEx.cpp @@ -16,10 +16,13 @@ /* Local functions */ /*****************************************************************************/ +// Finds hash index of the entry that was open by pseudo-name static DWORD FindHashIndex(TMPQArchive * ha, DWORD dwFileIndex) { TMPQHash * pHashTableEnd; TMPQHash * pHash; + DWORD dwHashIndex = HASH_ENTRY_FREE; + DWORD dwCount = 0; // Should only be called if the archive has hash table assert(ha->pHashTable != NULL); @@ -31,15 +34,18 @@ static DWORD FindHashIndex(TMPQArchive * ha, DWORD dwFileIndex) { if(MPQ_BLOCK_INDEX(pHash) == dwFileIndex) { - // Find the first hash entry that points to it. - // If there are multiple hash entries that point - // to the same file, only the first one is returned. - return (DWORD)(pHash - ha->pHashTable); + // Example: MPQ_2023_v1_Lusin2Rpg1.28.w3x, file index 24483 + // ReplaceableTextures\CommandButtons\BTNHaboss79.blp + // Hash Table Index #1 = 18 + // Hash Table Index #2 = 8446 + if(dwCount++ > 0) + return HASH_ENTRY_FREE; + dwHashIndex = (DWORD)(pHash - ha->pHashTable); } } - // No item was found - return HASH_ENTRY_FREE; + // Return the found hash index, if there are no duplicities + return dwHashIndex; } static const char * GetPatchFileName(TMPQArchive * ha, const char * szFileName, char * szBuffer) diff --git a/src/StormCommon.h b/src/StormCommon.h index 34077fd..c73543d 100644 --- a/src/StormCommon.h +++ b/src/StormCommon.h @@ -251,6 +251,7 @@ TMPQFile * IsValidFileHandle(HANDLE hFile); //-----------------------------------------------------------------------------
// Support for MPQ file tables
+ULONGLONG GetFileOffsetMask(TMPQArchive * ha);
ULONGLONG FileOffsetFromMpqOffset(TMPQArchive * ha, ULONGLONG MpqOffset);
ULONGLONG CalculateRawSectorOffset(TMPQFile * hf, DWORD dwSectorOffset);
diff --git a/src/StormLib.h b/src/StormLib.h index b78f7ef..e211cd8 100644 --- a/src/StormLib.h +++ b/src/StormLib.h @@ -830,6 +830,7 @@ typedef struct _TMPQArchive ULONGLONG UserDataPos; // Position of user data (relative to the begin of the file) ULONGLONG MpqPos; // MPQ header offset (relative to the begin of the file) ULONGLONG FileSize; // Size of the file at the moment of file open + ULONGLONG FileOffsetMask; // 0xFFFFFFFF for MPQ v 1, otherwise 0xFFFFFFFFFFFFFFFFull struct _TMPQArchive * haPatch; // Pointer to patch archive, if any struct _TMPQArchive * haBase; // Pointer to base ("previous version") archive, if any diff --git a/test/StormTest.cpp b/test/StormTest.cpp index 186f33d..21f395f 100755 --- a/test/StormTest.cpp +++ b/test/StormTest.cpp @@ -88,12 +88,17 @@ typedef struct _TEST_EXTRA_PATCHES DWORD dwPatchCount; // Number of patches
} TEST_EXTRA_PATCHES, *PTEST_EXTRA_PATCHES;
-typedef struct _TEST_EXTRA_HASHVALS
+typedef struct _TEST_EXTRA_HASHVAL
{
- EXTRA_TYPE Type; // Must be PatchList
- LPCSTR szFileName; // File name
DWORD dwHash1; // Hash A of the file name
DWORD dwHash2; // Hash B of the file name
+ LPCSTR szFileName; // File name
+} TEST_EXTRA_HASHVAL, *PTEST_EXTRA_HASHVAL;
+
+typedef struct _TEST_EXTRA_HASHVALS
+{
+ EXTRA_TYPE Type; // Must be PatchList
+ TEST_EXTRA_HASHVAL Items[2];
} TEST_EXTRA_HASHVALS, *PTEST_EXTRA_HASHVALS;
typedef struct _TEST_INFO
@@ -2439,17 +2444,29 @@ static DWORD TestOpenArchive_Extra_HashValues(TLogHelper & Logger, HANDLE hMpq, DWORD dwHash2 = 0;
DWORD cbHash = 0;
- if(SFileOpenFileEx(hMpq, pExtra->szFileName, 0, &hFile))
+ for(size_t i = 0; i < _countof(pExtra->Items); i++)
{
- SFileGetFileInfo(hFile, SFileInfoNameHash1, &dwHash1, sizeof(dwHash1), &cbHash);
- assert(cbHash == sizeof(DWORD));
+ PTEST_EXTRA_HASHVAL pItem = &pExtra->Items[i];
- SFileGetFileInfo(hFile, SFileInfoNameHash2, &dwHash2, sizeof(dwHash2), &cbHash);
- assert(cbHash == sizeof(DWORD));
+ if(SFileOpenFileEx(hMpq, pItem->szFileName, 0, &hFile))
+ {
+ if(SFileGetFileInfo(hFile, SFileInfoNameHash1, &dwHash1, sizeof(dwHash1), &cbHash))
+ {
+ assert(cbHash == sizeof(DWORD));
+ }
- if(dwHash1 != pExtra->dwHash1 || dwHash2 != pExtra->dwHash2)
- dwErrCode = Logger.PrintError("Name hash values mismatch on %s", pExtra->szFileName);
- SFileCloseFile(hFile);
+ if(SFileGetFileInfo(hFile, SFileInfoNameHash2, &dwHash2, sizeof(dwHash2), &cbHash))
+ {
+ assert(cbHash == sizeof(DWORD));
+ }
+
+ if(dwHash1 != pItem->dwHash1 || dwHash2 != pItem->dwHash2)
+ {
+ dwErrCode = Logger.PrintError("Name hash values mismatch on %s", pItem->szFileName);
+ }
+
+ SFileCloseFile(hFile);
+ }
}
return dwErrCode;
}
@@ -3877,7 +3894,14 @@ static const TEST_EXTRA_TWOFILES TwoFilesD2 = {TwoFiles, "waitingroombkgd.dc6"}; static const TEST_EXTRA_TWOFILES TwoFilesW3M = {TwoFiles, "file00000002.blp"};
static const TEST_EXTRA_TWOFILES TwoFilesW3X = {TwoFiles, "BlueCrystal.mdx"};
-static const TEST_EXTRA_HASHVALS HashVals = {HashValues, "File00002875.blp", 0xb93b0e63, 0xe50dfdd8};
+static const TEST_EXTRA_HASHVALS HashVals =
+{
+ HashValues,
+ {
+ {0x00000000, 0x00000000, "File00024483.blp"},
+ {0x8bd6929a, 0xfd55129b, "ReplaceableTextures\\CommandButtons\\BTNHaboss79.blp"}
+ }
+};
static const TEST_EXTRA_PATCHES PatchSC1 =
{
@@ -4193,7 +4217,7 @@ static const TEST_INFO Test_ReplaceFile[] = //-----------------------------------------------------------------------------
// Main
-#define TEST_COMMAND_LINE
+//#define TEST_COMMAND_LINE
#define TEST_LOCAL_LISTFILE
#define TEST_STREAM_OPERATIONS
#define TEST_MASTER_MIRROR
|