private HashTableEntry BinarySearch(int key) { int start = 0; int end = _count - 1; HashTableEntry currentEntry = null; while (start != end) { int middle = (start + end) / 2; currentEntry = _entries[middle]; var testKey = currentEntry.key; if (testKey == key) { return(currentEntry); } if (testKey > key) { // search in interval [start;middle-1] end = middle - 1; } else { // search in interval [middle+1,_count-1) start = middle + 1; } } currentEntry = _entries[start]; return(currentEntry.key == key ? currentEntry : null); }
/// <summary> /// Gets the file info of the provided path. /// </summary> /// <returns>The file info, or null if the file doesn't exist in the archive.</returns> /// <param name="filePath">File path.</param> public MPQFileInfo GetFileInfo(string filePath) { if (this.IsDisposed) { throw new ObjectDisposedException(ToString(), "Cannot use a disposed archive."); } if (ContainsFile(filePath)) { HashTableEntry hashEntry = this.ArchiveHashTable.FindEntry(filePath); BlockTableEntry blockEntry = this.ArchiveBlockTable.GetEntry((int)hashEntry.GetBlockEntryIndex()); if (HasFileAttributes()) { return(new MPQFileInfo(filePath, hashEntry, blockEntry)); } else { return(new MPQFileInfo(filePath, hashEntry, blockEntry, this.FileAttributes.FileAttributes[(int)hashEntry.GetBlockEntryIndex()])); } } else { return(null); } }
/// <summary> /// Adds value for the provided key; if key already exists, updates value. /// </summary> /// <param name="key"></param> /// <param name="value"></param> public void Add(K key, T value) { var index = GetIndex(key); var entryList = table[index]; var entry = new HashTableEntry <K, T> { Key = key, Value = value }; if (entryList == null || entryList.IsEmpty) { var list = new LinkedList <HashTableEntry <K, T> >(); list.PushFront(entry); table[index] = list; } else { var list = table[index]; for (int i = 0; i < list.Length; i++) { var element = list.ElementAt(i); if (element.Key.Equals(key)) { list.Set(i, entry); return; } } list.PushFront(entry); } }
/// <summary> /// Expands the capacity of the keys and values arrays. /// </summary> private void _expandCapacity(int minCapacity) { if (_hashTableStore.Length < minCapacity) { int newCapacity = (_hashTableStore.Length == 0 ? _defaultCapacity : _getExpandPrime(_hashTableStore.Length * 2)); if (newCapacity >= MAX_PRIME_ARRAY_LENGTH) { newCapacity = MAX_PRIME_ARRAY_LENGTH; } // Try to expand the size try { HashTableEntry <TKey, TValue>[] newKeysMap = new HashTableEntry <TKey, TValue> [newCapacity]; if (_size > 0) { // REHASH } _hashTableStore = newKeysMap; } catch (OutOfMemoryException) { throw; } } }
public void RemoveHandler(int key, Delegate handler) { HashTableEntry e = null; e = BinarySearch(key); if (e != null) { e.handler = Delegate.Remove(e.handler, handler); } }
public MPQFileInfo ( [NotNull] string inPath, [NotNull] HashTableEntry inHashEntry, [NotNull] BlockTableEntry inBlockEntry ) { Path = inPath; HashEntry = inHashEntry; BlockEntry = inBlockEntry; }
public MPQFileInfo ( [NotNull] string inPath, [NotNull] HashTableEntry inHashEntry, [NotNull] BlockTableEntry inBlockEntry, [NotNull] FileAttributes inAttributes ) : this(inPath, inHashEntry, inBlockEntry) { Attributes = inAttributes; }
/// <summary> /// Checks if the specified file path exists in the archive. /// </summary> /// <returns><c>true</c>, if the file exists, <c>false</c> otherwise.</returns> /// <param name="filePath">File path.</param> public bool ContainsFile(string filePath) { if (this.IsDisposed) { throw new ObjectDisposedException(ToString(), "Cannot use a disposed archive."); } HashTableEntry fileHashEntry = this.ArchiveHashTable.FindEntry(filePath.ToUpperInvariant()); return(fileHashEntry != null); }
private void InsertDelegate(int key, int afterPosition, Delegate handler) { if ((_count + 1) == _entries.Length) { Array.Resize(ref _entries, _entries.Length * 2); } for (int i = _count - 1; i > afterPosition; i--) { _entries[i + 1] = _entries[i]; } _entries[afterPosition + 1] = new HashTableEntry(key, handler); }
/// <summary> /// Fast initialize /// </summary> /// <param name="keys"></param> /// <param name="handlers"></param> internal void Initialize(int[] keys, Delegate[] handlers) { HashTableEntry[] table = new HashTableEntry[keys.Length]; for (int i = 0; i < keys.Length; i++) { table[i] = new HashTableEntry(keys[i], handlers[i]); } Array.Sort(table, (x, y) => x.key.CompareTo(y.key)); _entries = table; _count = _entries.Length; }
/// <summary> /// Removes the given key from the hash table. /// </summary> /// <param name="key"></param> public void Remove(K key) { var index = GetIndex(key); var list = table[index]; var entry = new HashTableEntry <K, T> { Key = key }; if (list == null || list.IsEmpty) { return; } list.Remove(entry); }
public void AddHandler(int key, Delegate handler) { HashTableEntry e = null; int nearest; e = BinarySearch(key, out nearest); if (e != null) { e.handler = Delegate.Combine(e.handler, handler); } else { InsertDelegate(key, nearest, handler); } }
public void Add(object key, object value) { HashTableEntry hashTableEntry = new HashTableEntry(); int index = Index(key); if (Contains(key)) { throw new Exception("Key already exists in the hash table"); } hashTableEntry.Key = key; hashTableEntry.Value = value; _tableEntries[index] = hashTableEntry; _size++; }
/// <inheritdoc /> /// <exception cref="ObjectDisposedException">Thrown if the archive has been disposed.</exception> /// <exception cref="FileNotFoundException">Thrown if the archive does not contain a file at the given path.</exception> public MPQFileInfo GetFileInfo(string filePath) { ThrowIfDisposed(); if (!ContainsFile(filePath)) { throw new FileNotFoundException("The given file was not present in the archive.", filePath); } HashTableEntry hashEntry = ArchiveHashTable.FindEntry(filePath); BlockTableEntry blockEntry = ArchiveBlockTable.GetEntry((int)hashEntry.GetBlockEntryIndex()); if (HasFileAttributes()) { return(new MPQFileInfo(filePath, hashEntry, blockEntry)); } return(new MPQFileInfo(filePath, hashEntry, blockEntry, FileAttributes.FileAttributes[(int)hashEntry.GetBlockEntryIndex()])); }
/// <summary> /// Add an item into the hash table /// </summary> /// <param name="key"></param> /// <param name="value"></param> public void Add(K key, V value) { if (key == null) { throw new ArgumentNullException("Null key"); } //Next pointer is null by default. No need to set var newEntry = new HashTableEntry(key, value); var hash = key.GetHashCode(); //Resize making the max this current hash if (hash >= currentBucketSize) { currentBucketSize = hash + 1; Array.Resize <HashTableEntry>(ref bucketsArray, currentBucketSize); } var firstItem = bucketsArray[hash]; //First check if this bucket is empty if (firstItem == null) { //Make it the first item in the chain bucketsArray[hash] = newEntry; } //There are other items in the chain. Add it to the end else { var curr = firstItem; //Keep going until we hit the end of the list while (curr.nextEntry != null) { curr = curr.nextEntry; } //Make the current guy point to the new entry we are adding curr.nextEntry = newEntry; } }
/// <summary> /// Contracts the capacity of the keys and values arrays. /// </summary> private void _contractCapacity() { // Only contract the array if the number of elements is less than 1/3 of the total array size. int oneThird = (_hashTableStore.Length / 3); if (_size <= oneThird) { int newCapacity = (_hashTableStore.Length == 0 ? _defaultCapacity : _getContractPrime(_hashTableStore.Length)); // Try to expand the size HashTableEntry <TKey, TValue>[] newKeysMap = new HashTableEntry <TKey, TValue> [newCapacity]; if (_size > 0) { // REHASH } _hashTableStore = newKeysMap; } }
private void Resize(int newSize, bool ForceNewHashCodes) { if (newSize < this.entries.Length) { throw new ArgumentOutOfRangeException(); } int[] newBuckets = new int[newSize]; for (int i = 0; i < newBuckets.Length; i++) { newBuckets[i] = -1; } HashTableEntry[] newEntries = new HashTableEntry[newSize]; Array.Copy(this.entries, 0, newEntries, 0, this.count); if (ForceNewHashCodes) { for (int i = 0; i < this.count; i++) { if (newEntries[i].HashCode != -1) { newEntries[i].HashCode = ReHash(newEntries[i].Key.GetHashCode()) & 0x7FFFFFFF; } } } for (int i = 0; i < this.count; i++) { if (newEntries[i].HashCode >= 0) { int bucket = newEntries[i].HashCode % newSize; newEntries[i].Next = newBuckets[bucket]; newBuckets[bucket] = i; } } this.buckets = newBuckets; this.entries = newEntries; }
public Delegate this[int key] { get { HashTableEntry e = null; e = BinarySearch(key); return(e != null ? e.handler : null); } set { HashTableEntry e = null; int nearest; e = BinarySearch(key, out nearest); if (e != null) { e.handler = value; } else { InsertDelegate(key, nearest, value); } } }
/// <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; }
/// <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; }
/// <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; }
private void InsertDelegate( int key, int afterPosition, Delegate handler ) { if ( ( _count + 1 ) == _entries.Length ) { Array.Resize( ref _entries, _entries.Length*2 ); } for ( int i = _count - 1; i > afterPosition; i-- ) { _entries[ i + 1 ] = _entries[ i ]; } _entries[afterPosition + 1] = new HashTableEntry(key, handler); }
/// <summary> /// Fast initialize /// </summary> /// <param name="keys"></param> /// <param name="handlers"></param> internal void Initialize( int[] keys, Delegate[] handlers ) { HashTableEntry[] table = new HashTableEntry[keys.Length]; for ( int i = 0; i < keys.Length; i++ ) { table[ i ] = new HashTableEntry( keys[ i ], handlers[ i ] ); } Array.Sort( table, ( x, y ) => x.key.CompareTo( y.key ) ); _entries = table; _count = _entries.Length; }
/// <summary> /// Gets the size of the full hash table. /// </summary> /// <returns>The hash table size.</returns> public ulong GetHashTableSize() { return((ulong)(HashTableEntryCount * HashTableEntry.GetSize())); }
// TODO: Filter files based on language and platform /// <summary> /// Extract the file at <paramref name="filePath"/> from the archive. /// </summary> /// <returns>The file as a byte array, or null if the file could not be found.</returns> /// <param name="filePath">Path to the file in the archive.</param> public byte[] ExtractFile(string filePath) { if (this.IsDisposed) { throw new ObjectDisposedException(ToString(), "Cannot use a disposed archive."); } // Reset all positions to be safe this.ArchiveReader.BaseStream.Position = 0; HashTableEntry fileHashEntry = this.ArchiveHashTable.FindEntry(filePath); if (fileHashEntry == null) { return(null); } BlockTableEntry fileBlockEntry = this.ArchiveBlockTable.GetEntry((int)fileHashEntry.GetBlockEntryIndex()); // Drop out if the file is not actually a file if (!fileBlockEntry.HasData()) { return(null); } // Seek to the beginning of the file's sectors long adjustedBlockOffset; if (this.Header.GetFormat() >= MPQFormat.ExtendedV1 && RequiresExtendedFormat()) { ushort upperOffsetBits = this.ExtendedBlockTable[(int)fileHashEntry.GetBlockEntryIndex()]; adjustedBlockOffset = (long)fileBlockEntry.GetExtendedBlockOffset(upperOffsetBits); } else { adjustedBlockOffset = fileBlockEntry.GetBlockOffset(); } this.ArchiveReader.BaseStream.Position = adjustedBlockOffset; // Calculate the decryption key if neccesary uint fileKey = MPQCrypt.CreateFileEncryptionKey ( filePath, fileBlockEntry.ShouldEncryptionKeyBeAdjusted(), adjustedBlockOffset, fileBlockEntry.GetFileSize() ); // Examine the file storage types and extract as neccesary if (fileBlockEntry.IsSingleUnit()) { return(ExtractSingleUnitFile(fileBlockEntry, fileKey)); } if (fileBlockEntry.IsCompressed()) { return(ExtractCompressedSectoredFile(fileBlockEntry, fileKey, adjustedBlockOffset)); } return(ExtractUncompressedSectoredFile(fileBlockEntry, fileKey)); }
/// <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) { Path = inPath; HashEntry = inHashEntry; BlockEntry = inBlockEntry; }