25
25
typedef struct _HET_TABLE_HEADER
26
26
{
27
27
DWORD dwTableSize; // Size of the entire HET table, including HET_TABLE_HEADER (in bytes)
28
- DWORD dwMaxFileCount ; // Maximum number of files in the MPQ
28
+ DWORD dwFileCount ; // Number of occupied entries in the hash table
29
29
DWORD dwHashTableSize; // Size of the hash table (in bytes)
30
30
DWORD dwHashEntrySize; // Effective size of the hash entry (in bits)
31
31
DWORD dwIndexSizeTotal; // Total size of file index (in bits)
@@ -722,10 +722,10 @@ static void CreateHetHeader(
722
722
PHET_TABLE_HEADER pHetHeader)
723
723
{
724
724
// Fill the BET header
725
- pHetHeader->dwMaxFileCount = pHetTable->dwMaxFileCount ;
725
+ pHetHeader->dwFileCount = pHetTable->dwFileCount ;
726
726
pHetHeader->dwHashTableSize = pHetTable->dwHashTableSize ;
727
727
pHetHeader->dwHashEntrySize = pHetTable->dwHashBitSize ;
728
- pHetHeader->dwIndexSizeTotal = GetNecessaryBitCount (pHetTable->dwMaxFileCount );
728
+ pHetHeader->dwIndexSizeTotal = GetNecessaryBitCount (pHetTable->dwHashTableSize );
729
729
pHetHeader->dwIndexSizeExtra = 0 ;
730
730
pHetHeader->dwIndexSize = pHetHeader->dwIndexSizeTotal ;
731
731
pHetHeader->dwIndexTableSize = ((pHetHeader->dwIndexSizeTotal * pHetTable->dwHashTableSize ) + 7 ) / 8 ;
@@ -736,7 +736,7 @@ static void CreateHetHeader(
736
736
pHetHeader->dwIndexTableSize ;
737
737
}
738
738
739
- TMPQHetTable * CreateHetTable (DWORD dwMaxFileCount , DWORD dwHashBitSize, bool bCreateEmpty)
739
+ TMPQHetTable * CreateHetTable (DWORD dwHashTableSize, DWORD dwFileCount , DWORD dwHashBitSize, bool bCreateEmpty)
740
740
{
741
741
TMPQHetTable * pHetTable;
742
742
@@ -746,12 +746,22 @@ TMPQHetTable * CreateHetTable(DWORD dwMaxFileCount, DWORD dwHashBitSize, bool bC
746
746
pHetTable->dwIndexSizeTotal = 0 ;
747
747
pHetTable->dwIndexSizeExtra = 0 ;
748
748
pHetTable->dwIndexSize = pHetTable->dwIndexSizeTotal ;
749
- pHetTable->dwMaxFileCount = dwMaxFileCount;
750
- pHetTable->dwHashTableSize = (dwMaxFileCount * 4 / 3 );
751
749
pHetTable->dwHashBitSize = dwHashBitSize;
752
750
753
- // Size of one index is calculated from max file count
754
- pHetTable->dwIndexSizeTotal = GetNecessaryBitCount (dwMaxFileCount);
751
+ // If the hash table size is not entered, calculate an optimal
752
+ // hash table size as 4/3 of the current file count.
753
+ if (dwHashTableSize == 0 )
754
+ {
755
+ dwHashTableSize = (dwFileCount * 4 / 3 );
756
+ assert (dwFileCount != 0 );
757
+ }
758
+
759
+ // Store the hash table size and file count
760
+ pHetTable->dwHashTableSize = dwHashTableSize;
761
+ pHetTable->dwFileCount = dwFileCount;
762
+
763
+ // Size of one index is calculated from hash table size
764
+ pHetTable->dwIndexSizeTotal = GetNecessaryBitCount (dwHashTableSize);
755
765
pHetTable->dwIndexSizeExtra = 0 ;
756
766
pHetTable->dwIndexSize = pHetTable->dwIndexSizeTotal ;
757
767
@@ -805,8 +815,12 @@ static TMPQHetTable * TranslateHetTable(TMPQExtTable * pExtTable)
805
815
// Verify the size of the table in the header
806
816
if (HetHeader.dwTableSize == pExtTable->dwDataSize )
807
817
{
818
+ // The size of index table (in entries) is expected
819
+ // to be the same like the hash table size (in bytes)
820
+ assert (((HetHeader.dwIndexTableSize * 8 ) / HetHeader.dwIndexSize ) == HetHeader.dwHashTableSize );
821
+
808
822
// Create translated table
809
- pHetTable = CreateHetTable (HetHeader.dwMaxFileCount , HetHeader.dwHashEntrySize , false );
823
+ pHetTable = CreateHetTable (HetHeader.dwHashTableSize , HetHeader. dwFileCount , HetHeader.dwHashEntrySize , false );
810
824
if (pHetTable != NULL )
811
825
{
812
826
// Copy the hash table size, index size and extra bits from the HET header
@@ -909,7 +923,7 @@ DWORD GetFileIndex_Het(TMPQArchive * ha, const char * szFileName)
909
923
// Go through HET table until we find a terminator
910
924
while (pHetTable->pHetHashes [Index] != HET_ENTRY_FREE)
911
925
{
912
- // Did we find match ?
926
+ // Did we find a match ?
913
927
if (pHetTable->pHetHashes [Index] == HetHash)
914
928
{
915
929
DWORD dwFileIndex = 0 ;
@@ -919,7 +933,6 @@ DWORD GetFileIndex_Het(TMPQArchive * ha, const char * szFileName)
919
933
pHetTable->dwIndexSize ,
920
934
&dwFileIndex,
921
935
4 );
922
-
923
936
//
924
937
// TODO: This condition only happens when we are opening a MPQ
925
938
// where some files were deleted by StormLib. Perhaps
@@ -952,6 +965,7 @@ DWORD AllocateHetEntry(
952
965
ULONGLONG AndMask64;
953
966
ULONGLONG OrMask64;
954
967
ULONGLONG BetHash;
968
+ DWORD FileCountIncrement = 0 ;
955
969
DWORD FreeHetIndex = HASH_ENTRY_FREE;
956
970
DWORD dwFileIndex;
957
971
DWORD StartIndex;
@@ -978,29 +992,35 @@ DWORD AllocateHetEntry(
978
992
// Go through HET table until we find a terminator
979
993
for (;;)
980
994
{
981
- // Check for entries that might have been deleted
982
- if (pHetTable->pHetHashes [Index] == HET_ENTRY_DELETED )
995
+ // Did we find a match ?
996
+ if (pHetTable->pHetHashes [Index] == HetHash )
983
997
{
984
- DWORD dwInvalidBetIndex = (1 << pHetTable->dwIndexSizeTotal ) - 1 ;
985
- DWORD dwBetIndex = 0 ;
998
+ DWORD dwFileIndex = 0 ;
986
999
987
- // Verify the BET index. If it's really free, we can use it
988
- dwFileIndex = (DWORD)(pFileEntry - ha->pFileTable );
1000
+ // Get the index of the BetHash
989
1001
GetBits (pHetTable->pBetIndexes , pHetTable->dwIndexSizeTotal * Index,
990
1002
pHetTable->dwIndexSize ,
991
- &dwBetIndex ,
1003
+ &dwFileIndex ,
992
1004
4 );
993
-
994
- if (dwBetIndex == dwInvalidBetIndex)
1005
+ //
1006
+ // TODO: This condition only happens when we are opening a MPQ
1007
+ // where some files were deleted by StormLib. Perhaps
1008
+ // we should not allow shrinking of the file table in MPQs v 4.0?
1009
+ // assert(dwFileIndex <= ha->dwFileTableSize);
1010
+ //
1011
+
1012
+ // Verify the BetHash against the entry in the table of BET hashes
1013
+ if (dwFileIndex <= ha->dwFileTableSize && ha->pFileTable [dwFileIndex].BetHash == BetHash)
995
1014
{
996
1015
FreeHetIndex = Index;
997
1016
break ;
998
1017
}
999
1018
}
1000
1019
1001
- // Is that entry free ?
1002
- if (pHetTable->pHetHashes [Index] == HET_ENTRY_FREE)
1020
+ // Check for entries that might have been deleted
1021
+ if (pHetTable->pHetHashes [Index] == HET_ENTRY_DELETED || pHetTable-> pHetHashes [Index] == HET_ENTRY_FREE)
1003
1022
{
1023
+ FileCountIncrement++;
1004
1024
FreeHetIndex = Index;
1005
1025
break ;
1006
1026
}
@@ -1015,10 +1035,12 @@ DWORD AllocateHetEntry(
1015
1035
// Fill the HET table entry
1016
1036
dwFileIndex = (DWORD)(pFileEntry - ha->pFileTable );
1017
1037
pHetTable->pHetHashes [FreeHetIndex] = HetHash;
1038
+ pHetTable->dwFileCount += FileCountIncrement;
1018
1039
SetBits (pHetTable->pBetIndexes , pHetTable->dwIndexSizeTotal * FreeHetIndex,
1019
1040
pHetTable->dwIndexSize ,
1020
1041
&dwFileIndex,
1021
1042
4 );
1043
+
1022
1044
// Fill the file entry
1023
1045
pFileEntry->BetHash = BetHash;
1024
1046
pFileEntry->dwHetIndex = FreeHetIndex;
@@ -1792,22 +1814,28 @@ void InvalidateInternalFiles(TMPQArchive * ha)
1792
1814
{
1793
1815
TFileEntry * pFileEntry;
1794
1816
1817
+ //
1818
+ // Note: We set the size of both (listfile) and (attributes) to zero.
1819
+ // This causes allocating space for newly added files straight over
1820
+ // (listfile)/(attributes), if these were the last ones in the MPQ
1821
+ //
1822
+
1795
1823
// Invalidate the (listfile), if not done yet
1796
- if (!(ha->dwFlags & MPQ_FLAG_INV_LISTFILE ))
1824
+ if (!(ha->dwFlags & MPQ_FLAG_LISTFILE_INVALID ))
1797
1825
{
1798
1826
pFileEntry = GetFileEntryExact (ha, LISTFILE_NAME, LANG_NEUTRAL);
1799
1827
if (pFileEntry != NULL )
1800
- FreeFileEntry (ha, pFileEntry) ;
1801
- ha->dwFlags |= MPQ_FLAG_INV_LISTFILE ;
1828
+ pFileEntry-> dwFileSize = pFileEntry-> dwCmpSize = 0 ;
1829
+ ha->dwFlags |= MPQ_FLAG_LISTFILE_INVALID ;
1802
1830
}
1803
1831
1804
1832
// Invalidate the (attributes), if not done yet
1805
- if (!(ha->dwFlags & MPQ_FLAG_INV_ATTRIBUTES ))
1833
+ if (!(ha->dwFlags & MPQ_FLAG_ATTRIBUTES_INVALID ))
1806
1834
{
1807
1835
pFileEntry = GetFileEntryExact (ha, ATTRIBUTES_NAME, LANG_NEUTRAL);
1808
1836
if (pFileEntry != NULL )
1809
- FreeFileEntry (ha, pFileEntry) ;
1810
- ha->dwFlags |= MPQ_FLAG_INV_ATTRIBUTES ;
1837
+ pFileEntry-> dwFileSize = pFileEntry-> dwCmpSize = 0 ;
1838
+ ha->dwFlags |= MPQ_FLAG_ATTRIBUTES_INVALID ;
1811
1839
}
1812
1840
1813
1841
// Remember that the MPQ has been changed and it will be necessary
@@ -2085,11 +2113,11 @@ TMPQBlock * LoadBlockTable(TMPQArchive * ha, ULONGLONG FileSize)
2085
2113
return pBlockTable;
2086
2114
}
2087
2115
2088
- int LoadHetTable (TMPQArchive * ha)
2116
+ TMPQHetTable * LoadHetTable (TMPQArchive * ha)
2089
2117
{
2118
+ TMPQHetTable * pHetTable = NULL ;
2090
2119
TMPQExtTable * pExtTable;
2091
2120
TMPQHeader * pHeader = ha->pHeader ;
2092
- int nError = ERROR_SUCCESS;
2093
2121
2094
2122
// If the HET table position is not NULL, we expect
2095
2123
// both HET and BET tables to be present.
@@ -2099,22 +2127,13 @@ int LoadHetTable(TMPQArchive * ha)
2099
2127
pExtTable = LoadExtTable (ha, pHeader->HetTablePos64 , (size_t )pHeader->HetTableSize64 , HET_TABLE_SIGNATURE, MPQ_KEY_HASH_TABLE);
2100
2128
if (pExtTable != NULL )
2101
2129
{
2102
- // If succeeded, we have to limit the maximum file count
2103
- // to the values saved in the HET table
2104
2130
// If loading HET table fails, we ignore the result.
2105
- ha->pHetTable = TranslateHetTable (pExtTable);
2106
- if (ha->pHetTable != NULL )
2107
- ha->dwMaxFileCount = ha->pHetTable ->dwMaxFileCount ;
2108
-
2131
+ pHetTable = TranslateHetTable (pExtTable);
2109
2132
STORM_FREE (pExtTable);
2110
2133
}
2111
-
2112
- // If the HET hable failed to load, it's corrupt.
2113
- if (ha->pHetTable == NULL )
2114
- nError = ERROR_FILE_CORRUPT;
2115
2134
}
2116
2135
2117
- return nError ;
2136
+ return pHetTable ;
2118
2137
}
2119
2138
2120
2139
TMPQBetTable * LoadBetTable (TMPQArchive * ha)
@@ -2149,21 +2168,18 @@ int LoadAnyHashTable(TMPQArchive * ha)
2149
2168
if (pHeader->dwHashTableSize == 0 && pHeader->HetTableSize64 == 0 )
2150
2169
return CreateHashTable (ha, HASH_TABLE_SIZE_DEFAULT);
2151
2170
2152
- // Try to load HET and/or classic hash table
2153
- LoadHetTable (ha);
2154
-
2155
- // Load the HASH table
2171
+ // Try to load HET table (the new hash table) and the classic HASH table
2172
+ ha->pHetTable = LoadHetTable (ha);
2156
2173
ha->pHashTable = LoadHashTable (ha);
2157
-
2158
- // Set the maximum file count to the size of the hash table
2159
- // In case there is HET table, we have to keep the file limit
2160
- if (ha->pHetTable == NULL )
2161
- ha->dwMaxFileCount = pHeader->dwHashTableSize ;
2162
-
2163
- // Did at least one succeed?
2164
2174
if (ha->pHetTable == NULL && ha->pHashTable == NULL )
2165
2175
return ERROR_FILE_CORRUPT;
2166
2176
2177
+ // Set the maximum file count
2178
+ if (ha->pHetTable != NULL && ha->pHashTable != NULL )
2179
+ ha->dwMaxFileCount = STORMLIB_MIN (ha->pHetTable ->dwHashTableSize , pHeader->dwHashTableSize );
2180
+ else
2181
+ ha->dwMaxFileCount = (ha->pHetTable != NULL ) ? ha->pHetTable ->dwHashTableSize : pHeader->dwHashTableSize ;
2182
+
2167
2183
// In theory, a MPQ could have bigger block table than hash table
2168
2184
if (ha->pHeader ->dwBlockTableSize > ha->dwMaxFileCount )
2169
2185
{
@@ -2445,7 +2461,7 @@ int SaveMPQTables(TMPQArchive * ha)
2445
2461
assert (ha->dwFlags & MPQ_FLAG_CHANGED);
2446
2462
2447
2463
// Find the space where the MPQ tables will be saved
2448
- FindFreeMpqSpace (ha, &TablePos );
2464
+ TablePos = FindFreeMpqSpace (ha);
2449
2465
2450
2466
// If the MPQ has HET table, we prepare a ready-to-save version
2451
2467
if (nError == ERROR_SUCCESS && ha->pHetTable != NULL )
0 commit comments