[4057] added: Line of sight (vmaps) [part 2] (last part)
[mangos-git.git] / contrib / vmap_extractor_v2 / stormlib / SFileCompactArchive.cpp
blob30540d1141640e6728453dbfc04409da838b098e
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__
13 #include "StormLib.h"
14 #include "SCommon.h"
16 /*****************************************************************************/
17 /* Local structures */
18 /*****************************************************************************/
20 /*****************************************************************************/
21 /* Local variables */
22 /*****************************************************************************/
24 static COMPACTCB CompactCB = NULL;
25 static void * lpUserData = NULL;
27 /*****************************************************************************/
28 /* Local functions */
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
46 TMPQHash * pHash;
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;
58 // Notify the user
59 if(CompactCB != NULL)
60 CompactCB(lpUserData, CCB_CHECKING_FILES, 0, ha->pHeader->dwBlockTableSize);
63 // Now check all the files from the filelist
64 if(nError == ERROR_SUCCESS)
66 SFILE_FIND_DATA wf;
67 HANDLE hFind = SFileFindFirstFile((HANDLE)ha, "*", &wf, szListFile);
68 BOOL bResult = TRUE;
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
77 if(pHash != NULL)
79 pHash = pHashTableCopy + (pHash - ha->pHashTable);
80 if(pHash->dwName1 != (DWORD)-1 && pHash->dwName2 != (DWORD)-1)
82 TMPQBlock * pBlock = ha->pBlockTable + pHash->dwBlockIndex;
83 DWORD dwSeed = 0;
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;
92 else
93 szFileName++;
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;
108 // Notify the user
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);
116 if(hFind != NULL)
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
124 dwFileCount = 0;
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)
134 HANDLE hFile = NULL;
135 DWORD dwFlags = 0;
136 DWORD dwSeed = 0;
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;
151 break;
154 // Remember the seed
155 pFileSeeds[pHash->dwBlockIndex] = dwSeed;
157 // Notify the user
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);
167 return nError;
170 // Copies all file blocks into another archive.
171 // TODO: Test for archives > 4GB
172 static int CopyMpqFileBlocks(
173 HANDLE hFile,
174 TMPQArchive * ha,
175 TMPQBlockEx * pBlockEx,
176 TMPQBlock * pBlock,
177 DWORD dwSeed)
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)
201 nBlocks++;
202 pbBlock = ALLOCMEM(BYTE, ha->dwBlockSize);
203 if(pbBlock == NULL)
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))
228 dwSeed1 = dwSeed;
229 if(pBlock->dwFlags & MPQ_FILE_FIXSEED)
230 dwSeed = (dwSeed1 ^ pBlock->dwFSize) - pBlock->dwFilePos;
232 dwSeed2 = dwSeed;
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)
241 // Allocate buffers
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);
276 else
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;
314 break;
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;
334 break;
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];
344 if(dwBytes != 0)
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;
354 else
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);
380 if(pbBlock != NULL)
381 FREEMEM(pbBlock);
382 return nError;
386 static int CopyNonMpqData(
387 HANDLE hSrcFile,
388 HANDLE hTrgFile,
389 LARGE_INTEGER & DataSizeToCopy)
391 LARGE_INTEGER DataSize = DataSizeToCopy;
392 DWORD dwTransferred;
393 DWORD dwToRead;
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;
409 break;
412 // Write to the target file
413 WriteFile(hTrgFile, DataBuffer, dwToRead, &dwTransferred, NULL);
414 if(dwTransferred != dwToRead)
416 nError = ERROR_DISK_FULL;
417 break;
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;
431 TMPQBlock * pBlock;
432 DWORD dwSeed1;
433 DWORD dwIndex;
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)
448 // DebugBreak();
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)
456 break;
460 // Cleanup and exit
461 return nError;
465 /*****************************************************************************/
466 /* Public functions */
467 /*****************************************************************************/
469 BOOL WINAPI SFileSetCompactCallback(HANDLE /* hMPQ */, COMPACTCB aCompactCB, void * lpData)
471 CompactCB = aCompactCB;
472 lpUserData = lpData;
473 return TRUE;
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;
487 DWORD dwTransferred;
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);
499 else
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_");
517 else
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);
531 else
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;
587 pBlockEx++;
588 pBlock++;
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;
647 ha->hFile = hFile;
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;
677 ha->dwBlockPos = 0;
678 ha->dwBuffPos = 0;
681 // Cleanup and return
682 if(hFile != INVALID_HANDLE_VALUE)
683 CloseHandle(hFile);
684 if(pFileSeeds != NULL)
685 FREEMEM(pFileSeeds);
686 if(nError != ERROR_SUCCESS)
687 SetLastError(nError);
688 DeleteFile(szTempFile);
689 CompactCB = NULL;
690 return (nError == ERROR_SUCCESS);