1 /*****************************************************************************/
2 /* SFileCompactArchive.cpp Copyright (c) Ladislav Zezula 2003 */
3 /*---------------------------------------------------------------------------*/
4 /* Archive compacting function */
5 /*---------------------------------------------------------------------------*/
6 /* Date Ver Who Comment */
7 /* -------- ---- --- ------- */
8 /* 14.04.03 1.00 Lad Splitted from SFileCreateArchiveEx.cpp */
9 /* 19.11.03 1.01 Dan Big endian handling */
10 /*****************************************************************************/
12 #define __STORMLIB_SELF__
16 /*****************************************************************************/
17 /* Local structures */
18 /*****************************************************************************/
20 /*****************************************************************************/
22 /*****************************************************************************/
24 static COMPACTCB CompactCB
= NULL
;
25 static void * lpUserData
= NULL
;
27 /*****************************************************************************/
29 /*****************************************************************************/
31 // Creates a copy of hash table
32 static TMPQHash
* CopyHashTable(TMPQArchive
* ha
)
34 TMPQHash
* pHashTableCopy
= ALLOCMEM(TMPQHash
, ha
->pHeader
->dwHashTableSize
);
36 if(pHashTableCopy
!= NULL
)
37 memcpy(pHashTableCopy
, ha
->pHashTable
, sizeof(TMPQHash
) * ha
->pHeader
->dwHashTableSize
);
39 return pHashTableCopy
;
42 // TODO: Test for archives > 4GB
43 static int CheckIfAllFilesKnown(TMPQArchive
* ha
, const char * szListFile
, DWORD
* pFileSeeds
)
45 TMPQHash
* pHashTableCopy
= NULL
; // Copy of the hash table
47 TMPQHash
* pHashEnd
= NULL
; // End of the hash table
48 DWORD dwFileCount
= 0;
49 int nError
= ERROR_SUCCESS
;
51 // First of all, create a copy of hash table
52 if(nError
== ERROR_SUCCESS
)
54 if((pHashTableCopy
= CopyHashTable(ha
)) == NULL
)
55 nError
= ERROR_NOT_ENOUGH_MEMORY
;
56 pHashEnd
= pHashTableCopy
+ ha
->pHeader
->dwHashTableSize
;
60 CompactCB(lpUserData
, CCB_CHECKING_FILES
, 0, ha
->pHeader
->dwBlockTableSize
);
63 // Now check all the files from the filelist
64 if(nError
== ERROR_SUCCESS
)
67 HANDLE hFind
= SFileFindFirstFile((HANDLE
)ha
, "*", &wf
, szListFile
);
70 // Do while some files have been found
71 while(hFind
!= NULL
&& bResult
)
73 TMPQHash
* pHash
= GetHashEntry(ha
, wf
.cFileName
);
75 // If the hash table entry has been found, find it's position
76 // in the hash table copy
79 pHash
= pHashTableCopy
+ (pHash
- ha
->pHashTable
);
80 if(pHash
->dwName1
!= (DWORD
)-1 && pHash
->dwName2
!= (DWORD
)-1)
82 TMPQBlock
* pBlock
= ha
->pBlockTable
+ pHash
->dwBlockIndex
;
85 // Resolve the file seed. Use plain file name for it
86 if(pBlock
->dwFlags
& MPQ_FILE_ENCRYPTED
)
88 char * szFileName
= strrchr(wf
.cFileName
, '\\');
90 if(szFileName
== NULL
)
91 szFileName
= wf
.cFileName
;
95 dwSeed
= DecryptFileSeed(szFileName
);
96 if(pBlock
->dwFlags
& MPQ_FILE_FIXSEED
)
97 dwSeed
= (dwSeed
+ pBlock
->dwFilePos
) ^ pBlock
->dwFSize
;
99 pFileSeeds
[pHash
->dwBlockIndex
] = dwSeed
;
101 pHash
->dwName1
= 0xFFFFFFFF;
102 pHash
->dwName2
= 0xFFFFFFFF;
103 pHash
->lcLocale
= 0xFFFF;
104 pHash
->wPlatform
= 0xFFFF;
105 pHash
->dwBlockIndex
= 0xFFFFFFFF;
109 if(CompactCB
!= NULL
)
110 CompactCB(lpUserData
, CCB_CHECKING_FILES
, ++dwFileCount
, ha
->pHeader
->dwBlockTableSize
);
112 // Find the next file in the archive
113 bResult
= SFileFindNextFile(hFind
, &wf
);
117 SFileFindClose(hFind
);
120 // When the filelist checking is complete, parse the hash table copy and find the
121 if(nError
== ERROR_SUCCESS
)
123 // Notify the user about checking hash table
125 if(CompactCB
!= NULL
)
126 CompactCB(lpUserData
, CCB_CHECKING_HASH_TABLE
, dwFileCount
, ha
->pHeader
->dwBlockTableSize
);
128 for(pHash
= pHashTableCopy
; pHash
< pHashEnd
; pHash
++)
130 // If there is an unresolved entry, try to detect its seed. If it fails,
131 // we cannot complete the work
132 if(pHash
->dwName1
!= (DWORD
)-1 && pHash
->dwName2
!= (DWORD
)-1)
138 if(SFileOpenFileEx((HANDLE
)ha
, (char *)(DWORD_PTR
)pHash
->dwBlockIndex
, 0, &hFile
))
140 TMPQFile
* hf
= (TMPQFile
*)hFile
;
141 dwFlags
= hf
->pBlock
->dwFlags
;
142 dwSeed
= hf
->dwSeed1
;
143 SFileCloseFile(hFile
);
146 // If the file is encrypted, we have to check
147 // If we can apply the file decryption seed
148 if(dwFlags
& MPQ_FILE_ENCRYPTED
&& dwSeed
== 0)
150 nError
= ERROR_CAN_NOT_COMPLETE
;
155 pFileSeeds
[pHash
->dwBlockIndex
] = dwSeed
;
158 if(CompactCB
!= NULL
)
159 CompactCB(lpUserData
, CCB_CHECKING_HASH_TABLE
, ++dwFileCount
, ha
->pHeader
->dwBlockTableSize
);
164 // Delete the copy of hash table
165 if(pHashTableCopy
!= NULL
)
166 FREEMEM(pHashTableCopy
);
170 // Copies all file blocks into another archive.
171 // TODO: Test for archives > 4GB
172 static int CopyMpqFileBlocks(
175 TMPQBlockEx
* pBlockEx
,
179 LARGE_INTEGER FilePos
= {0};
180 DWORD
* pdwBlockPos2
= NULL
; // File block positions to be written to target file
181 DWORD
* pdwBlockPos
= NULL
; // File block positions (unencrypted)
182 BYTE
* pbBlock
= NULL
; // Buffer for the file block
183 DWORD dwTransferred
; // Number of bytes transferred
184 DWORD dwCSize
= 0; // Compressed file size
185 DWORD dwBytes
= 0; // Number of bytes
186 DWORD dwSeed1
= 0; // File seed used for decryption
187 DWORD dwSeed2
= 0; // File seed used for encryption
188 DWORD nBlocks
= 0; // Number of file blocks
189 DWORD nBlock
= 0; // Currently processed file block
190 int nError
= ERROR_SUCCESS
;
192 // When file length is zero, do nothing
193 if(pBlock
->dwFSize
== 0)
194 return ERROR_SUCCESS
;
196 // Calculate number of blocks in the file
197 if(nError
== ERROR_SUCCESS
)
199 nBlocks
= pBlock
->dwFSize
/ ha
->dwBlockSize
;
200 if(pBlock
->dwFSize
% ha
->dwBlockSize
)
202 pbBlock
= ALLOCMEM(BYTE
, ha
->dwBlockSize
);
204 nError
= ERROR_NOT_ENOUGH_MEMORY
;
207 // Set the position to the begin of the file within archive
208 if(nError
== ERROR_SUCCESS
)
210 FilePos
.HighPart
= pBlockEx
->wFilePosHigh
;
211 FilePos
.LowPart
= pBlock
->dwFilePos
;
212 FilePos
.QuadPart
+= ha
->MpqPos
.QuadPart
;
213 if(SetFilePointer(ha
->hFile
, FilePos
.LowPart
, &FilePos
.HighPart
, FILE_BEGIN
) != FilePos
.LowPart
)
214 nError
= GetLastError();
217 // Remember the position in the destination file
218 if(nError
== ERROR_SUCCESS
)
220 FilePos
.HighPart
= 0;
221 FilePos
.LowPart
= SetFilePointer(hFile
, 0, &FilePos
.HighPart
, FILE_CURRENT
);
224 // Resolve decryption seeds. The 'dwSeed' parameter is the decryption
225 // seed for the file.
226 if(nError
== ERROR_SUCCESS
&& (pBlock
->dwFlags
& MPQ_FILE_ENCRYPTED
))
229 if(pBlock
->dwFlags
& MPQ_FILE_FIXSEED
)
230 dwSeed
= (dwSeed1
^ pBlock
->dwFSize
) - pBlock
->dwFilePos
;
233 if(pBlock
->dwFlags
& MPQ_FILE_FIXSEED
)
234 dwSeed2
= (dwSeed
+ (DWORD
)(FilePos
.QuadPart
- ha
->MpqPos
.QuadPart
)) ^ pBlock
->dwFSize
;
237 // Load the file positions from the archive and save it to the target file
238 // (only if the file is compressed)
239 if(pBlock
->dwFlags
& MPQ_FILE_COMPRESSED
)
242 if(nError
== ERROR_SUCCESS
)
244 pdwBlockPos
= ALLOCMEM(DWORD
, nBlocks
+ 2);
245 pdwBlockPos2
= ALLOCMEM(DWORD
, nBlocks
+ 2);
247 if(pdwBlockPos
== NULL
|| pdwBlockPos2
== NULL
)
248 nError
= ERROR_NOT_ENOUGH_MEMORY
;
251 // Load the block positions
252 if(nError
== ERROR_SUCCESS
)
254 dwBytes
= (nBlocks
+ 1) * sizeof(DWORD
);
255 if(pBlock
->dwFlags
& MPQ_FILE_HAS_EXTRA
)
256 dwBytes
+= sizeof(DWORD
);
258 ReadFile(ha
->hFile
, pdwBlockPos
, dwBytes
, &dwTransferred
, NULL
);
259 if(dwTransferred
!= dwBytes
)
260 nError
= ERROR_FILE_CORRUPT
;
263 // Re-encrypt the block table positions
264 if(nError
== ERROR_SUCCESS
)
266 BSWAP_ARRAY32_UNSIGNED(pdwBlockPos
, dwBytes
/ sizeof(DWORD
));
267 if(pBlock
->dwFlags
& MPQ_FILE_ENCRYPTED
)
269 DecryptMPQBlock(pdwBlockPos
, dwBytes
, dwSeed1
- 1);
270 if(pdwBlockPos
[0] != dwBytes
)
271 nError
= ERROR_FILE_CORRUPT
;
273 memcpy(pdwBlockPos2
, pdwBlockPos
, dwBytes
);
274 EncryptMPQBlock(pdwBlockPos2
, dwBytes
, dwSeed2
- 1);
278 memcpy(pdwBlockPos2
, pdwBlockPos
, dwBytes
);
280 BSWAP_ARRAY32_UNSIGNED(pdwBlockPos2
, dwBytes
/ sizeof(DWORD
));
283 // Write to the target file
284 if(nError
== ERROR_SUCCESS
)
286 WriteFile(hFile
, pdwBlockPos2
, dwBytes
, &dwTransferred
, NULL
);
287 dwCSize
+= dwTransferred
;
288 if(dwTransferred
!= dwBytes
)
289 nError
= ERROR_DISK_FULL
;
293 // Now we have to copy all file block. We will do it without
294 // recompression, because re-compression is not necessary in this case
295 if(nError
== ERROR_SUCCESS
)
297 for(nBlock
= 0; nBlock
< nBlocks
; nBlock
++)
299 // Fix: The last block must not be exactly the size of one block.
300 dwBytes
= ha
->dwBlockSize
;
301 if(nBlock
== nBlocks
- 1)
303 dwBytes
= pBlock
->dwFSize
- (ha
->dwBlockSize
* (nBlocks
- 1));
306 if(pBlock
->dwFlags
& MPQ_FILE_COMPRESSED
)
307 dwBytes
= pdwBlockPos
[nBlock
+1] - pdwBlockPos
[nBlock
];
309 // Read the file block
310 ReadFile(ha
->hFile
, pbBlock
, dwBytes
, &dwTransferred
, NULL
);
311 if(dwTransferred
!= dwBytes
)
313 nError
= ERROR_FILE_CORRUPT
;
317 // If necessary, re-encrypt the block
318 // Note: Recompression is not necessary here. Unlike encryption,
319 // the compression does not depend on the position of the file in MPQ.
320 if((pBlock
->dwFlags
& MPQ_FILE_ENCRYPTED
) && dwSeed1
!= dwSeed2
)
322 BSWAP_ARRAY32_UNSIGNED((DWORD
*)pbBlock
, dwBytes
/sizeof(DWORD
));
323 DecryptMPQBlock((DWORD
*)pbBlock
, dwBytes
, dwSeed1
+ nBlock
);
324 EncryptMPQBlock((DWORD
*)pbBlock
, dwBytes
, dwSeed2
+ nBlock
);
325 BSWAP_ARRAY32_UNSIGNED((DWORD
*)pbBlock
, dwBytes
/sizeof(DWORD
));
328 // Now write the block back to the file
329 WriteFile(hFile
, pbBlock
, dwBytes
, &dwTransferred
, NULL
);
330 dwCSize
+= dwTransferred
;
331 if(dwTransferred
!= dwBytes
)
333 nError
= ERROR_DISK_FULL
;
339 // Copy the file extras, if any
340 // These extras does not seem to be encrypted, and their purpose is unknown
341 if(nError
== ERROR_SUCCESS
&& (pBlock
->dwFlags
& MPQ_FILE_HAS_EXTRA
))
343 dwBytes
= pdwBlockPos
[nBlocks
+ 1] - pdwBlockPos
[nBlocks
];
346 ReadFile(ha
->hFile
, pbBlock
, dwBytes
, &dwTransferred
, NULL
);
347 if(dwTransferred
== dwBytes
)
349 WriteFile(hFile
, pbBlock
, dwBytes
, &dwTransferred
, NULL
);
350 dwCSize
+= dwTransferred
;
351 if(dwTransferred
!= dwBytes
)
352 nError
= ERROR_DISK_FULL
;
356 nError
= ERROR_FILE_CORRUPT
;
361 // Update file position in the block table
362 if(nError
== ERROR_SUCCESS
)
364 // At this point, number of bytes written should be exactly
365 // the same like the compressed file size. If it isn't, there's something wrong
366 // (maybe new archive version ?)
367 assert(dwCSize
== pBlock
->dwCSize
);
369 // Update file pos in the block table
370 FilePos
.QuadPart
-= ha
->MpqPos
.QuadPart
;
371 pBlockEx
->wFilePosHigh
= (USHORT
)FilePos
.HighPart
;
372 pBlock
->dwFilePos
= FilePos
.LowPart
;
375 // Cleanup and return
376 if(pdwBlockPos2
!= NULL
)
377 FREEMEM(pdwBlockPos2
);
378 if(pdwBlockPos
!= NULL
)
379 FREEMEM(pdwBlockPos
);
386 static int CopyNonMpqData(
389 LARGE_INTEGER
& DataSizeToCopy
)
391 LARGE_INTEGER DataSize
= DataSizeToCopy
;
394 char DataBuffer
[0x1000];
395 int nError
= ERROR_SUCCESS
;
397 while(DataSize
.QuadPart
> 0)
399 // Get the proper size of data
400 dwToRead
= sizeof(DataBuffer
);
401 if(DataSize
.HighPart
== 0 && DataSize
.LowPart
< dwToRead
)
402 dwToRead
= DataSize
.LowPart
;
404 // Read the source file
405 ReadFile(hSrcFile
, DataBuffer
, dwToRead
, &dwTransferred
, NULL
);
406 if(dwTransferred
!= dwToRead
)
408 nError
= ERROR_CAN_NOT_COMPLETE
;
412 // Write to the target file
413 WriteFile(hTrgFile
, DataBuffer
, dwToRead
, &dwTransferred
, NULL
);
414 if(dwTransferred
!= dwToRead
)
416 nError
= ERROR_DISK_FULL
;
420 // Decrement the number of data to be copied
421 DataSize
.QuadPart
-= dwTransferred
;
424 return ERROR_SUCCESS
;
427 // TODO: Test for archives > 4GB
428 static int CopyMpqFiles(HANDLE hFile
, TMPQArchive
* ha
, DWORD
* pFileSeeds
)
430 TMPQBlockEx
* pBlockEx
;
434 int nError
= ERROR_SUCCESS
;
436 // Walk through all files and write them to the destination MPQ archive
437 for(dwIndex
= 0; dwIndex
< ha
->pHeader
->dwBlockTableSize
; dwIndex
++)
439 pBlockEx
= ha
->pExtBlockTable
+ dwIndex
;
440 pBlock
= ha
->pBlockTable
+ dwIndex
;
441 dwSeed1
= pFileSeeds
[dwIndex
];
443 // Notify the caller about work
444 if(CompactCB
!= NULL
)
445 CompactCB(lpUserData
, CCB_COMPACTING_FILES
, dwIndex
, ha
->pHeader
->dwBlockTableSize
);
447 // if(dwIndex == 0x1B9)
450 // Copy all the file blocks
451 // Debug: Break at (dwIndex == 5973)
452 if(pBlock
->dwFlags
& MPQ_FILE_EXISTS
)
454 nError
= CopyMpqFileBlocks(hFile
, ha
, pBlockEx
, pBlock
, dwSeed1
);
455 if(nError
!= ERROR_SUCCESS
)
465 /*****************************************************************************/
466 /* Public functions */
467 /*****************************************************************************/
469 BOOL WINAPI
SFileSetCompactCallback(HANDLE
/* hMPQ */, COMPACTCB aCompactCB
, void * lpData
)
471 CompactCB
= aCompactCB
;
476 //-----------------------------------------------------------------------------
477 // Archive compacting (incomplete)
479 // TODO: Test for archives > 4GB
480 BOOL WINAPI
SFileCompactArchive(HANDLE hMPQ
, const char * szListFile
, BOOL
/* bReserved */)
482 TMPQArchive
* ha
= (TMPQArchive
*)hMPQ
;
483 HANDLE hFile
= INVALID_HANDLE_VALUE
;
484 DWORD
* pFileSeeds
= NULL
;
485 char szTempFile
[MAX_PATH
] = "";
486 char * szTemp
= NULL
;
488 int nError
= ERROR_SUCCESS
;
490 // Test the valid parameters
491 if(!IsValidMpqHandle(ha
))
492 nError
= ERROR_INVALID_PARAMETER
;
494 // Create the table with file seeds
495 if(nError
== ERROR_SUCCESS
)
497 if((pFileSeeds
= ALLOCMEM(DWORD
, ha
->pHeader
->dwBlockTableSize
)) != NULL
)
498 memset(pFileSeeds
, 0, sizeof(DWORD
) * ha
->pHeader
->dwBlockTableSize
);
500 nError
= ERROR_NOT_ENOUGH_MEMORY
;
503 // First of all, we have to check of we are able to decrypt all files.
504 // If not, sorry, but the archive cannot be compacted.
505 if(nError
== ERROR_SUCCESS
)
506 nError
= CheckIfAllFilesKnown(ha
, szListFile
, pFileSeeds
);
508 // Get the temporary file name and create it
509 if(nError
== ERROR_SUCCESS
)
511 if(CompactCB
!= NULL
)
512 CompactCB(lpUserData
, CCB_COPYING_NON_MPQ_DATA
, 0, 0);
514 strcpy(szTempFile
, ha
->szFileName
);
515 if((szTemp
= strrchr(szTempFile
, '.')) != NULL
)
516 strcpy(szTemp
+ 1, "mp_");
518 strcat(szTempFile
, "_");
520 hFile
= CreateFile(szTempFile
, GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
, NULL
, CREATE_ALWAYS
, 0, NULL
);
521 if(hFile
== INVALID_HANDLE_VALUE
)
522 nError
= GetLastError();
525 // Write the data before MPQ header (if any)
526 if(nError
== ERROR_SUCCESS
&& ha
->MpqPos
.QuadPart
> 0)
528 SetFilePointer(ha
->hFile
, 0, NULL
, FILE_BEGIN
);
529 if(ha
->pShunt
!= NULL
)
530 nError
= CopyNonMpqData(ha
->hFile
, hFile
, ha
->ShuntPos
);
532 nError
= CopyNonMpqData(ha
->hFile
, hFile
, ha
->MpqPos
);
535 // Write the MPQ shunt (if any)
536 if(nError
== ERROR_SUCCESS
&& ha
->pShunt
!= NULL
)
538 BSWAP_TMPQSHUNT(ha
->pShunt
);
539 WriteFile(hFile
, ha
->pShunt
, sizeof(TMPQShunt
), &dwTransferred
, NULL
);
540 BSWAP_TMPQSHUNT(ha
->pShunt
);
542 if(dwTransferred
!= sizeof(TMPQShunt
))
543 nError
= ERROR_DISK_FULL
;
546 // Write the data between MPQ shunt and the MPQ header (if any)
547 if(nError
== ERROR_SUCCESS
&& ha
->pShunt
!= NULL
)
549 LARGE_INTEGER BytesToCopy
;
551 BytesToCopy
.QuadPart
= ha
->MpqPos
.QuadPart
- (ha
->ShuntPos
.QuadPart
+ sizeof(TMPQShunt
));
552 nError
= CopyNonMpqData(ha
->hFile
, hFile
, BytesToCopy
);
555 // Write the MPQ header
556 if(nError
== ERROR_SUCCESS
)
558 BSWAP_TMPQHEADER(ha
->pHeader
);
559 WriteFile(hFile
, ha
->pHeader
, ha
->pHeader
->dwHeaderSize
, &dwTransferred
, NULL
);
560 BSWAP_TMPQHEADER(ha
->pHeader
);
561 if(dwTransferred
!= ha
->pHeader
->dwHeaderSize
)
562 nError
= ERROR_DISK_FULL
;
565 // Write the data between the header and between the first file
566 // For this, we have to determine where the first file begins
567 if(nError
== ERROR_SUCCESS
)
569 LARGE_INTEGER FirstFilePos
;
570 LARGE_INTEGER TempPos
;
571 TMPQBlockEx
* pBlockEx
= ha
->pExtBlockTable
;
572 TMPQBlock
* pBlockEnd
= ha
->pBlockTable
+ ha
->pHeader
->dwBlockTableSize
;
573 TMPQBlock
* pBlock
= ha
->pBlockTable
;
575 // Maximum file position
576 FirstFilePos
.HighPart
= 0x7FFFFFFF;
577 FirstFilePos
.LowPart
= 0xFFFFFFFF;
579 // Find the block with the least position in the MPQ
580 while(pBlock
< pBlockEnd
)
582 TempPos
.HighPart
= pBlockEx
->wFilePosHigh
;
583 TempPos
.LowPart
= pBlock
->dwFilePos
;
584 if(TempPos
.QuadPart
< FirstFilePos
.QuadPart
)
585 FirstFilePos
= TempPos
;
591 // Set the position in the source file right after the file header
592 TempPos
.QuadPart
= ha
->MpqPos
.QuadPart
+ ha
->pHeader
->dwHeaderSize
;
593 SetFilePointer(ha
->hFile
, TempPos
.LowPart
, &TempPos
.HighPart
, FILE_BEGIN
);
595 // Get the number of bytes to copy
596 FirstFilePos
.QuadPart
-= ha
->pHeader
->dwHeaderSize
;
597 nError
= CopyNonMpqData(ha
->hFile
, hFile
, FirstFilePos
);
600 // Now write all file blocks.
601 if(nError
== ERROR_SUCCESS
)
602 nError
= CopyMpqFiles(hFile
, ha
, pFileSeeds
);
604 // Now we need to update the tables positions
605 // (but only if the tables are at the end of the file)
606 if(nError
== ERROR_SUCCESS
)
608 LARGE_INTEGER RelativePos
;
609 LARGE_INTEGER FilePos
= {0};
611 // Set the hash table position
612 FilePos
.LowPart
= SetFilePointer(hFile
, 0, &FilePos
.HighPart
, FILE_CURRENT
);
613 RelativePos
.QuadPart
= FilePos
.QuadPart
- ha
->MpqPos
.QuadPart
;
614 ha
->pHeader
->wHashTablePosHigh
= (USHORT
)RelativePos
.HighPart
;
615 ha
->pHeader
->dwHashTablePos
= RelativePos
.LowPart
;
616 ha
->HashTablePos
= FilePos
;
618 // Set the block table position
619 RelativePos
.QuadPart
+= ha
->pHeader
->dwHashTableSize
* sizeof(TMPQHash
);
620 FilePos
.QuadPart
+= ha
->pHeader
->dwHashTableSize
* sizeof(TMPQHash
);
621 ha
->pHeader
->wBlockTablePosHigh
= (USHORT
)RelativePos
.HighPart
;
622 ha
->pHeader
->dwBlockTablePos
= RelativePos
.LowPart
;
623 ha
->BlockTablePos
= FilePos
;
625 // Set the extended block table position
626 RelativePos
.QuadPart
+= ha
->pHeader
->dwBlockTableSize
* sizeof(TMPQBlock
);
627 FilePos
.QuadPart
+= ha
->pHeader
->dwBlockTableSize
* sizeof(TMPQBlock
);
628 if(ha
->ExtBlockTablePos
.QuadPart
!= 0)
630 ha
->pHeader
->ExtBlockTablePos
= RelativePos
;
631 ha
->ExtBlockTablePos
= FilePos
;
633 RelativePos
.QuadPart
+= ha
->pHeader
->dwBlockTableSize
* sizeof(TMPQBlockEx
);
634 FilePos
.QuadPart
+= ha
->pHeader
->dwBlockTableSize
* sizeof(TMPQBlockEx
);
637 // Set the archive size
638 ha
->pHeader
->dwArchiveSize
= RelativePos
.LowPart
;
639 ha
->MpqSize
= RelativePos
;
642 // If succeeded, update the tables in the file
643 if(nError
== ERROR_SUCCESS
)
645 CloseHandle(ha
->hFile
);
646 ha
->FilePointer
.QuadPart
= 0;
648 hFile
= INVALID_HANDLE_VALUE
;
649 nError
= SaveMPQTables(ha
);
652 // If all succeeded, switch the archives
653 if(nError
== ERROR_SUCCESS
)
655 if(CompactCB
!= NULL
)
656 CompactCB(lpUserData
, CCB_CLOSING_ARCHIVE
, 0, 0);
658 if(!DeleteFile(ha
->szFileName
) || // Delete the old archive
659 !CloseHandle(ha
->hFile
) || // Close the new archive
660 !MoveFile(szTempFile
, ha
->szFileName
)) // Rename the temporary archive
661 nError
= GetLastError();
664 // Now open the freshly renamed archive file
665 if(nError
== ERROR_SUCCESS
)
667 ha
->hFile
= CreateFile(ha
->szFileName
, GENERIC_READ
| GENERIC_WRITE
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
668 if(ha
->hFile
== INVALID_HANDLE_VALUE
)
669 nError
= GetLastError();
672 // Invalidate the positions of the archive
673 if(nError
== ERROR_SUCCESS
)
675 ha
->FilePointer
.QuadPart
= 0;
676 ha
->pLastFile
= NULL
;
681 // Cleanup and return
682 if(hFile
!= INVALID_HANDLE_VALUE
)
684 if(pFileSeeds
!= NULL
)
686 if(nError
!= ERROR_SUCCESS
)
687 SetLastError(nError
);
688 DeleteFile(szTempFile
);
690 return (nError
== ERROR_SUCCESS
);