Skip to content

Commit 2d3b3e1

Browse files
author
Ladislav Zezula
committed
+ Improved patching process so that it works for Starcraft II patches
+ Removed memory leaks in file search
1 parent 1aa906c commit 2d3b3e1

12 files changed

+536
-264
lines changed

doc/Sector Offset MD5.txt

-25
This file was deleted.

src/SBaseCommon.cpp

+9-8
Original file line numberDiff line numberDiff line change
@@ -1410,10 +1410,10 @@ void FreeFileHandle(TMPQFile *& hf)
14101410
FreeFileHandle(hf->hfPatch);
14111411

14121412
// Then free all buffers allocated in the file structure
1413-
if(hf->pPatchHeader != NULL)
1414-
STORM_FREE(hf->pPatchHeader);
14151413
if(hf->pbFileData != NULL)
14161414
STORM_FREE(hf->pbFileData);
1415+
if(hf->pPatchHeader != NULL)
1416+
STORM_FREE(hf->pPatchHeader);
14171417
if(hf->pPatchInfo != NULL)
14181418
STORM_FREE(hf->pPatchInfo);
14191419
if(hf->SectorOffsets != NULL)
@@ -1438,6 +1438,10 @@ void FreeArchiveHandle(TMPQArchive *& ha)
14381438
if(ha->haPatch != NULL)
14391439
FreeArchiveHandle(ha->haPatch);
14401440

1441+
// Free the patch prefix, if any
1442+
if(ha->pPatchPrefix != NULL)
1443+
STORM_FREE(ha->pPatchPrefix);
1444+
14411445
// Close the file stream
14421446
FileStream_Close(ha->pStream);
14431447
ha->pStream = NULL;
@@ -1517,12 +1521,9 @@ bool IsPseudoFileName(const char * szFileName, DWORD * pdwFileIndex)
15171521

15181522
bool IsValidMD5(LPBYTE pbMd5)
15191523
{
1520-
BYTE BitSummary = 0;
1521-
1522-
// The MD5 is considered invalid of it is zeroed
1523-
BitSummary |= pbMd5[0x00] | pbMd5[0x01] | pbMd5[0x02] | pbMd5[0x03] | pbMd5[0x04] | pbMd5[0x05] | pbMd5[0x06] | pbMd5[0x07];
1524-
BitSummary |= pbMd5[0x08] | pbMd5[0x09] | pbMd5[0x0A] | pbMd5[0x0B] | pbMd5[0x0C] | pbMd5[0x0D] | pbMd5[0x0E] | pbMd5[0x0F];
1525-
return (BitSummary != 0);
1524+
LPDWORD Md5 = (LPDWORD)pbMd5;
1525+
1526+
return (Md5[0] | Md5[1] | Md5[2] | Md5[3]) ? true : false;
15261527
}
15271528

15281529
bool VerifyDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE expected_md5)

src/SFileAttributes.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ typedef struct _MPQ_ATTRIBUTES_HEADER
2424
// Followed by an array of file times
2525
// Followed by an array of MD5
2626
// Followed by an array of patch bits
27+
28+
// Note: The MD5 in (attributes), if present, is a hash of the entire file.
29+
// In case the file is an incremental patch, it contains MD5 of the file
30+
// after being patched.
31+
2732
} MPQ_ATTRIBUTES_HEADER, *PMPQ_ATTRIBUTES_HEADER;
2833

2934
//-----------------------------------------------------------------------------

src/SFileFindFile.cpp

+67-47
Original file line numberDiff line numberDiff line change
@@ -167,14 +167,14 @@ static bool FileWasFoundBefore(
167167
{
168168
// If we are in patch MPQ, we check if patch prefix matches
169169
// and then trim the patch prefix
170-
if(ha->cchPatchPrefix != 0)
170+
if(ha->pPatchPrefix != NULL)
171171
{
172172
// If the patch prefix doesn't fit, we pretend that the file
173173
// was there before and it will be skipped
174-
if(_strnicmp(szRealFileName, ha->szPatchPrefix, ha->cchPatchPrefix))
174+
if(_strnicmp(szRealFileName, ha->pPatchPrefix->szPatchPrefix, ha->pPatchPrefix->nLength))
175175
return true;
176176

177-
szRealFileName += ha->cchPatchPrefix;
177+
szRealFileName += ha->pPatchPrefix->nLength;
178178
}
179179

180180
// Calculate the hash to the table
@@ -213,6 +213,14 @@ static bool FileWasFoundBefore(
213213
return false;
214214
}
215215

216+
static inline bool FileEntryIsInvalid(
217+
TMPQArchive * ha,
218+
TFileEntry * pFileEntry)
219+
{
220+
// Spazzler3 protector: Some files are clearly wrong
221+
return ((ha->dwFlags & MPQ_FLAG_MALFORMED) && (pFileEntry->dwCmpSize & 0xFFFF0000) >= 0x7FFF0000);
222+
}
223+
216224
static TFileEntry * FindPatchEntry(TMPQArchive * ha, TFileEntry * pFileEntry)
217225
{
218226
TFileEntry * pPatchEntry = NULL;
@@ -225,9 +233,11 @@ static TFileEntry * FindPatchEntry(TMPQArchive * ha, TFileEntry * pFileEntry)
225233
{
226234
// Move to the patch archive
227235
ha = ha->haPatch;
236+
szFileName[0] = 0;
228237

229238
// Prepare the prefix for the file name
230-
strcpy(szFileName, ha->szPatchPrefix);
239+
if(ha->pPatchPrefix != NULL)
240+
strcpy(szFileName, ha->pPatchPrefix->szPatchPrefix);
231241
strcat(szFileName, pFileEntry->szFileName);
232242

233243
// Try to find the file there
@@ -261,64 +271,68 @@ static int DoMPQSearch(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData)
261271
pFileEntry = ha->pFileTable + hs->dwNextIndex;
262272

263273
// Get the length of the patch prefix (0 if none)
264-
nPrefixLength = strlen(ha->szPatchPrefix);
274+
nPrefixLength = (ha->pPatchPrefix != NULL) ? ha->pPatchPrefix->nLength : 0;
265275

266276
// Parse the file table
267277
while(pFileEntry < pFileTableEnd)
268278
{
269279
// Increment the next index for subsequent search
270280
hs->dwNextIndex++;
271281

272-
// Is it a file and not a patch file?
282+
// Is it a file but not a patch file?
273283
if((pFileEntry->dwFlags & hs->dwFlagMask) == MPQ_FILE_EXISTS)
274284
{
275-
// Now we have to check if this file was not enumerated before
276-
if(!FileWasFoundBefore(ha, hs, pFileEntry))
277-
{
278-
// Find a patch to this file
279-
pPatchEntry = FindPatchEntry(ha, pFileEntry);
280-
if(pPatchEntry == NULL)
281-
pPatchEntry = pFileEntry;
285+
// Spazzler3 protector: Some files are clearly wrong
286+
if(!FileEntryIsInvalid(ha, pFileEntry))
287+
{
288+
// Now we have to check if this file was not enumerated before
289+
if(!FileWasFoundBefore(ha, hs, pFileEntry))
290+
{
291+
// Find a patch to this file
292+
pPatchEntry = FindPatchEntry(ha, pFileEntry);
293+
if(pPatchEntry == NULL)
294+
pPatchEntry = pFileEntry;
282295

283-
// Prepare the block index
284-
dwBlockIndex = (DWORD)(pFileEntry - ha->pFileTable);
296+
// Prepare the block index
297+
dwBlockIndex = (DWORD)(pFileEntry - ha->pFileTable);
285298

286-
// Get the file name. If it's not known, we will create pseudo-name
287-
szFileName = pFileEntry->szFileName;
288-
if(szFileName == NULL)
289-
{
290-
// Open the file by its pseudo-name.
291-
// This also generates the file name with a proper extension
292-
sprintf(szPseudoName, "File%08u.xxx", (unsigned int)dwBlockIndex);
293-
if(SFileOpenFileEx((HANDLE)hs->ha, szPseudoName, SFILE_OPEN_BASE_FILE, &hFile))
299+
// Get the file name. If it's not known, we will create pseudo-name
300+
szFileName = pFileEntry->szFileName;
301+
if(szFileName == NULL)
294302
{
295-
szFileName = (pFileEntry->szFileName != NULL) ? pFileEntry->szFileName : szPseudoName;
296-
SFileCloseFile(hFile);
303+
// Open the file by its pseudo-name.
304+
// This also generates the file name with a proper extension
305+
sprintf(szPseudoName, "File%08u.xxx", (unsigned int)dwBlockIndex);
306+
if(SFileOpenFileEx((HANDLE)hs->ha, szPseudoName, SFILE_OPEN_BASE_FILE, &hFile))
307+
{
308+
szFileName = (pFileEntry->szFileName != NULL) ? pFileEntry->szFileName : szPseudoName;
309+
SFileCloseFile(hFile);
310+
}
297311
}
298-
}
299312

300-
// If the file name is still NULL, we cannot include the file to the search
301-
if(szFileName != NULL)
302-
{
303-
// Check the file name against the wildcard
304-
if(CheckWildCard(szFileName + nPrefixLength, hs->szSearchMask))
313+
// If the file name is still NULL, we cannot include the file to the search
314+
if(szFileName != NULL)
305315
{
306-
// Fill the found entry
307-
lpFindFileData->dwHashIndex = pPatchEntry->dwHashIndex;
308-
lpFindFileData->dwBlockIndex = dwBlockIndex;
309-
lpFindFileData->dwFileSize = pPatchEntry->dwFileSize;
310-
lpFindFileData->dwFileFlags = pPatchEntry->dwFlags;
311-
lpFindFileData->dwCompSize = pPatchEntry->dwCmpSize;
312-
lpFindFileData->lcLocale = pPatchEntry->lcLocale;
313-
314-
// Fill the filetime
315-
lpFindFileData->dwFileTimeHi = (DWORD)(pPatchEntry->FileTime >> 32);
316-
lpFindFileData->dwFileTimeLo = (DWORD)(pPatchEntry->FileTime);
317-
318-
// Fill the file name and plain file name
319-
strcpy(lpFindFileData->cFileName, szFileName + nPrefixLength);
320-
lpFindFileData->szPlainName = (char *)GetPlainFileName(lpFindFileData->cFileName);
321-
return ERROR_SUCCESS;
316+
// Check the file name against the wildcard
317+
if(CheckWildCard(szFileName + nPrefixLength, hs->szSearchMask))
318+
{
319+
// Fill the found entry
320+
lpFindFileData->dwHashIndex = pPatchEntry->dwHashIndex;
321+
lpFindFileData->dwBlockIndex = dwBlockIndex;
322+
lpFindFileData->dwFileSize = pPatchEntry->dwFileSize;
323+
lpFindFileData->dwFileFlags = pPatchEntry->dwFlags;
324+
lpFindFileData->dwCompSize = pPatchEntry->dwCmpSize;
325+
lpFindFileData->lcLocale = pPatchEntry->lcLocale;
326+
327+
// Fill the filetime
328+
lpFindFileData->dwFileTimeHi = (DWORD)(pPatchEntry->FileTime >> 32);
329+
lpFindFileData->dwFileTimeLo = (DWORD)(pPatchEntry->FileTime);
330+
331+
// Fill the file name and plain file name
332+
strcpy(lpFindFileData->cFileName, szFileName + nPrefixLength);
333+
lpFindFileData->szPlainName = (char *)GetPlainFileName(lpFindFileData->cFileName);
334+
return ERROR_SUCCESS;
335+
}
322336
}
323337
}
324338
}
@@ -327,6 +341,12 @@ static int DoMPQSearch(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData)
327341
pFileEntry++;
328342
}
329343

344+
// If there is no more patches in the chain, stop it.
345+
// This also keeps hs->ha non-NULL, which is required
346+
// for freeing the handle later
347+
if(ha->haPatch == NULL)
348+
break;
349+
330350
// Move to the next patch in the patch chain
331351
hs->ha = ha = ha->haPatch;
332352
hs->dwNextIndex = 0;

src/SFileListFile.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,10 @@ static size_t ReadListFileLine(TListFileCache * pCache, char * szLine, int nMaxC
189189
if(*pCache->pPos == '~')
190190
szExtraString = szLine;
191191

192+
// Remember that last occurence of a slash or backslash
193+
// if(*pCache->pPos == '\\' || *pCache->pPos == '/')
194+
// szPlainName = szLine + 1;
195+
192196
// Copy the character
193197
*szLine++ = *pCache->pPos++;
194198
}

src/SFileOpenArchive.cpp

+5-3
Original file line numberDiff line numberDiff line change
@@ -211,9 +211,11 @@ bool WINAPI SFileOpenArchive(
211211
ha->dwFlags |= (dwStreamFlags & STREAM_FLAG_READ_ONLY) ? MPQ_FLAG_READ_ONLY : 0;
212212

213213
// Also remember if we shall check sector CRCs when reading file
214-
if(dwFlags & MPQ_OPEN_CHECK_SECTOR_CRC)
215-
ha->dwFlags |= MPQ_FLAG_CHECK_SECTOR_CRC;
216-
214+
ha->dwFlags |= (dwFlags & MPQ_OPEN_CHECK_SECTOR_CRC) ? MPQ_FLAG_CHECK_SECTOR_CRC : 0;
215+
216+
// Also remember if this MPQ is a patch
217+
ha->dwFlags |= (dwFlags & MPQ_OPEN_PATCH) ? MPQ_FLAG_PATCH : 0;
218+
217219
// Limit the header searching to about 130 MB of data
218220
if(EndOfSearch > 0x08000000)
219221
EndOfSearch = 0x08000000;

src/SFileOpenFileEx.cpp

+19-17
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,23 @@
1818

1919
static const char * GetPatchFileName(TMPQArchive * ha, const char * szFileName, char * szBuffer)
2020
{
21-
if(ha->cchPatchPrefix != 0)
21+
TMPQNamePrefix * pPrefix;
22+
23+
// Are there patches in the current MPQ?
24+
if(ha->dwFlags & MPQ_FLAG_PATCH)
2225
{
23-
// Copy the patch prefix
24-
memcpy(szBuffer, ha->szPatchPrefix, ha->cchPatchPrefix);
25-
26+
// The patch prefix must be already known here
27+
assert(ha->pPatchPrefix != NULL);
28+
pPrefix = ha->pPatchPrefix;
29+
2630
// The patch name for "OldWorld\\XXX\\YYY" is "Base\\XXX\YYY"
27-
// We need to remove the "Oldworld\\" prefix
31+
// We need to remove the "OldWorld\\" prefix
2832
if(!_strnicmp(szFileName, "OldWorld\\", 9))
2933
szFileName += 9;
3034

31-
// Copy the rest of the name
32-
strcpy(szBuffer + ha->cchPatchPrefix, szFileName);
35+
// Create the file name from the known patch entry
36+
memcpy(szBuffer, pPrefix->szPatchPrefix, pPrefix->nLength);
37+
strcpy(szBuffer + pPrefix->nLength, szFileName);
3338
szFileName = szBuffer;
3439
}
3540

@@ -67,7 +72,7 @@ static bool OpenLocalFile(const char * szFileName, HANDLE * phFile)
6772
return false;
6873
}
6974

70-
bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, DWORD dwReserved, HANDLE * phFile)
75+
bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, HANDLE * phFile)
7176
{
7277
TMPQArchive * haBase = NULL;
7378
TMPQArchive * ha = (TMPQArchive *)hMpq;
@@ -76,17 +81,14 @@ bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, DWORD dwReserved, HAN
7681
TMPQFile * hfBase = NULL; // Pointer to base open file
7782
TMPQFile * hf = NULL;
7883
HANDLE hPatchFile;
79-
char szPrefixBuffer[MAX_PATH];
80-
81-
// Keep this flag here for future updates
82-
dwReserved = dwReserved;
84+
char szNameBuffer[MAX_PATH];
8385

8486
// First of all, find the latest archive where the file is in base version
8587
// (i.e. where the original, unpatched version of the file exists)
8688
while(ha != NULL)
8789
{
8890
// If the file is there, then we remember the archive
89-
pFileEntry = GetFileEntryExact(ha, GetPatchFileName(ha, szFileName, szPrefixBuffer), 0);
91+
pFileEntry = GetFileEntryExact(ha, GetPatchFileName(ha, szFileName, szNameBuffer), 0);
9092
if(pFileEntry != NULL && (pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0)
9193
haBase = ha;
9294

@@ -102,7 +104,7 @@ bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, DWORD dwReserved, HAN
102104
}
103105

104106
// Now open the base file
105-
if(SFileOpenFileEx((HANDLE)ha, GetPatchFileName(ha, szFileName, szPrefixBuffer), SFILE_OPEN_BASE_FILE, (HANDLE *)&hfBase))
107+
if(SFileOpenFileEx((HANDLE)ha, GetPatchFileName(ha, szFileName, szNameBuffer), SFILE_OPEN_BASE_FILE, (HANDLE *)&hfBase))
106108
{
107109
// The file must be a base file, i.e. without MPQ_FILE_PATCH_FILE
108110
assert((hfBase->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0);
@@ -112,7 +114,7 @@ bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, DWORD dwReserved, HAN
112114
for(ha = ha->haPatch; ha != NULL; ha = ha->haPatch)
113115
{
114116
// Prepare the file name with a correct prefix
115-
if(SFileOpenFileEx((HANDLE)ha, GetPatchFileName(ha, szFileName, szPrefixBuffer), SFILE_OPEN_BASE_FILE, &hPatchFile))
117+
if(SFileOpenFileEx((HANDLE)ha, GetPatchFileName(ha, szFileName, szNameBuffer), SFILE_OPEN_BASE_FILE, &hPatchFile))
116118
{
117119
// Remember the new version
118120
hfPatch = (TMPQFile *)hPatchFile;
@@ -130,7 +132,7 @@ bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, DWORD dwReserved, HAN
130132
// Give the updated base MPQ
131133
if(phFile != NULL)
132134
*phFile = (HANDLE)hfBase;
133-
return true;
135+
return (hfBase != NULL);
134136
}
135137

136138
/*****************************************************************************/
@@ -328,7 +330,7 @@ bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearch
328330
}
329331
else
330332
{
331-
return OpenPatchedFile(hMpq, szFileName, 0, phFile);
333+
return OpenPatchedFile(hMpq, szFileName, phFile);
332334
}
333335
}
334336
break;

0 commit comments

Comments
 (0)