public HashTable([JetBrains.Annotations.NotNull] byte[] data) { using var ms = new MemoryStream(data); using var br = new BinaryReader(ms); for (long i = 0; i < data.Length; i += HashTableEntry.GetSize()) { var entryBytes = br.ReadBytes((int)HashTableEntry.GetSize()); var newEntry = new HashTableEntry(entryBytes); _entries.Add(newEntry); } }
public bool TryFindEntry([NotNull] string fileName, out HashTableEntry tableEntry) { var entryHomeIndex = MPQCrypt.Hash(fileName, HashType.FileHashTableOffset) & (uint)_entries.Count - 1; var hashA = MPQCrypt.Hash(fileName, HashType.FilePathA); var hashB = MPQCrypt.Hash(fileName, HashType.FilePathB); if (!TryFindEntry(hashA, hashB, entryHomeIndex, out tableEntry)) { return(false); } return(true); }
/// <summary> /// Initializes a new instance of the <see cref="HashTable"/> class from /// a block of data containing hash table entries. /// </summary> /// <param name="data">Data.</param> public HashTable(byte[] data) { using (MemoryStream ms = new MemoryStream(data)) { using (BinaryReader br = new BinaryReader(ms)) { for (long i = 0; i < data.Length; i += HashTableEntry.GetSize()) { byte[] entryBytes = br.ReadBytes((int)HashTableEntry.GetSize()); HashTableEntry newEntry = new HashTableEntry(entryBytes); this.Entries.Add(newEntry); } } } }
public ulong GetSize() { return((ulong)(_entries.Count * HashTableEntry.GetSize())); }
public bool TryFindEntry ( uint hashA, uint hashB, uint entryHomeIndex, out HashTableEntry tableEntry ) { tableEntry = null; // First, see if the file has ever existed. If it has and matches, return it. if (_entries[(int)entryHomeIndex].HasFileEverExisted()) { if (_entries[(int)entryHomeIndex].GetPrimaryHash() == hashA && _entries[(int)entryHomeIndex].GetSecondaryHash() == hashB) { tableEntry = _entries[(int)entryHomeIndex]; return(true); } } else { return(false); } // If that file doesn't match (but has existed, or is occupied, let's keep looking down the table. HashTableEntry currentEntry; HashTableEntry deletionEntry = null; for (var i = (int)entryHomeIndex + 1; i < _entries.Count - 1; ++i) { currentEntry = _entries[i]; if (!currentEntry.HasFileEverExisted()) { continue; } if (currentEntry.GetPrimaryHash() != hashA || currentEntry.GetSecondaryHash() != hashB) { continue; } if (currentEntry.DoesFileExist()) { // Found it! tableEntry = currentEntry; return(true); } // The file might have been deleted. Store it as a possible return candidate, but keep looking. deletionEntry = currentEntry; } // Still nothing? Loop around and scan the start of the table as well for (var i = 0; i < entryHomeIndex; ++i) { currentEntry = _entries[i]; if (!currentEntry.HasFileEverExisted()) { continue; } if (currentEntry.GetPrimaryHash() != hashA || currentEntry.GetSecondaryHash() != hashB) { continue; } if (currentEntry.DoesFileExist()) { // Found it! tableEntry = currentEntry; return(true); } // The file might have been deleted. Store it as a possible return candidate, but keep looking. deletionEntry = currentEntry; } if (deletionEntry is null) { return(false); } // We found the file, but it's been deleted. tableEntry = deletionEntry; return(true); }
/// <summary> /// Finds a valid entry for a given hash pair, starting at the specified offset. /// </summary> /// <returns>The entry.</returns> /// <param name="hashA">A hash of the filename (Algorithm A).</param> /// <param name="hashB">A hash of the filename (Algorithm B)</param> /// <param name="entryHomeIndex">The home index for the file we're searching for. Reduces lookup times.</param> public HashTableEntry FindEntry(uint hashA, uint hashB, uint entryHomeIndex) { // First, see if the file has ever existed. If it has and matches, return it. if (this.Entries[(int)entryHomeIndex].HasFileEverExisted()) { if (this.Entries[(int)entryHomeIndex].GetPrimaryHash() == hashA && this.Entries[(int)entryHomeIndex].GetSecondaryHash() == hashB) { return(this.Entries[(int)entryHomeIndex]); } } else { return(null); } // If that file doesn't match (but has existed, or is occupied, let's keep looking down the table. HashTableEntry currentEntry = null; HashTableEntry deletionEntry = null; for (int i = (int)entryHomeIndex + 1; i < this.Entries.Count - 1; ++i) { currentEntry = this.Entries[i]; if (currentEntry.HasFileEverExisted()) { if (currentEntry.GetPrimaryHash() == hashA && currentEntry.GetSecondaryHash() == hashB) { if (currentEntry.DoesFileExist()) { // Found it! return(currentEntry); } else { // The file might have been deleted. Store it as a possible return candidate, but keep looking. deletionEntry = currentEntry; } } } } // Still nothing? Loop around and scan the start of the table as well for (int i = 0; i < entryHomeIndex; ++i) { currentEntry = this.Entries[i]; if (currentEntry.HasFileEverExisted()) { if (currentEntry.GetPrimaryHash() == hashA && currentEntry.GetSecondaryHash() == hashB) { if (currentEntry.DoesFileExist()) { // Found it! return(currentEntry); } else { // The file might have been deleted. Store it as a possible return candidate, but keep looking. deletionEntry = currentEntry; } } } } // We found the file, but it's been deleted. return(deletionEntry); }
/// <summary> /// Initializes a new instance of the <see cref="Warcraft.MPQ.FileInfo.MPQFileInfo"/> class. /// </summary> /// <param name="InPath">In path.</param> /// <param name="InHashEntry">In hash entry.</param> /// <param name="InBlockEntry">In block entry.</param> public MPQFileInfo(string InPath, HashTableEntry InHashEntry, BlockTableEntry InBlockEntry) { this.Path = InPath; this.HashEntry = InHashEntry; this.BlockEntry = InBlockEntry; }
/// <summary> /// Initializes a new instance of the <see cref="Warcraft.MPQ.FileInfo.MPQFileInfo"/> class. /// </summary> /// <param name="InPath">In path.</param> /// <param name="InHashEntry">In hash entry.</param> /// <param name="InBlockEntry">In block entry.</param> /// <param name="InAttributes">In attributes.</param> public MPQFileInfo(string InPath, HashTableEntry InHashEntry, BlockTableEntry InBlockEntry, FileAttributes InAttributes) : this(InPath, InHashEntry, InBlockEntry) { this.Attributes = InAttributes; }