Defragment the file table so it does not contain any gaps Note: As long as all values of all TMPQHash::dwBlockIndex are not HASH_ENTRY_FREE, the startup search index does not matter. Hash table is circular, so as long as there is no terminator, all entries will be found.
| 680 | // Hash table is circular, so as long as there is no terminator, |
| 681 | // all entries will be found. |
| 682 | static TMPQHash * DefragmentHashTable( |
| 683 | TMPQArchive * ha, |
| 684 | TMPQHash * pHashTable, |
| 685 | TMPQBlock * pBlockTable) |
| 686 | { |
| 687 | TMPQHeader * pHeader = ha->pHeader; |
| 688 | TMPQHash * pHashTableEnd = pHashTable + pHeader->dwHashTableSize; |
| 689 | TMPQHash * pSource = pHashTable; |
| 690 | TMPQHash * pTarget = pHashTable; |
| 691 | DWORD dwFirstFreeEntry; |
| 692 | DWORD dwNewTableSize; |
| 693 | |
| 694 | // Sanity checks |
| 695 | assert(pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1); |
| 696 | assert(pHeader->HiBlockTablePos64 == 0); |
| 697 | |
| 698 | // Parse the hash table and move the entries to the begin of it |
| 699 | for(pSource = pHashTable; pSource < pHashTableEnd; pSource++) |
| 700 | { |
| 701 | // Check whether this is a valid hash table entry |
| 702 | if(IsValidHashEntry1(ha, pSource, pBlockTable)) |
| 703 | { |
| 704 | // Copy the hash table entry back |
| 705 | if(pSource > pTarget) |
| 706 | pTarget[0] = pSource[0]; |
| 707 | |
| 708 | // Move the target |
| 709 | pTarget++; |
| 710 | } |
| 711 | } |
| 712 | |
| 713 | // Calculate how many entries in the hash table we really need |
| 714 | dwFirstFreeEntry = (DWORD)(pTarget - pHashTable); |
| 715 | dwNewTableSize = GetNearestPowerOfTwo(dwFirstFreeEntry); |
| 716 | |
| 717 | // Fill the rest with entries that look like deleted |
| 718 | pHashTableEnd = pHashTable + dwNewTableSize; |
| 719 | pSource = pHashTable + dwFirstFreeEntry; |
| 720 | memset(pSource, 0xFF, (dwNewTableSize - dwFirstFreeEntry) * sizeof(TMPQHash)); |
| 721 | |
| 722 | // Mark the block indexes as deleted |
| 723 | for(; pSource < pHashTableEnd; pSource++) |
| 724 | pSource->dwBlockIndex = HASH_ENTRY_DELETED; |
| 725 | |
| 726 | // Free some of the space occupied by the hash table |
| 727 | if(dwNewTableSize < pHeader->dwHashTableSize) |
| 728 | { |
| 729 | pHashTable = STORM_REALLOC(TMPQHash, pHashTable, dwNewTableSize); |
| 730 | ha->pHeader->BlockTableSize64 = dwNewTableSize * sizeof(TMPQHash); |
| 731 | ha->pHeader->dwHashTableSize = dwNewTableSize; |
| 732 | } |
| 733 | |
| 734 | return pHashTable; |
| 735 | } |
| 736 | |
| 737 | static int BuildFileTableFromBlockTable( |
| 738 | TMPQArchive * ha, |
no test coverage detected