// 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)); }
/// <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> /// <exception cref="FileDeletedException">Thrown if the file is deleted in the archive.</exception> public byte[] ExtractFile(string filePath) { ThrowIfDisposed(); // Reset all positions to be safe ArchiveReader.BaseStream.Position = 0; HashTableEntry fileHashEntry; try { fileHashEntry = ArchiveHashTable.FindEntry(filePath); } catch (FileNotFoundException fex) { throw new FileNotFoundException("No file found at the given path.", filePath, fex); } BlockTableEntry fileBlockEntry = ArchiveBlockTable.GetEntry((int)fileHashEntry.GetBlockEntryIndex()); // Drop out if the file has been deleted if (fileBlockEntry.IsDeleted()) { throw new FileDeletedException("The given file is deleted.", filePath); } // Seek to the beginning of the file's sectors long adjustedBlockOffset; if (Header.GetFormat() >= MPQFormat.ExtendedV1 && RequiresExtendedFormat()) { ushort upperOffsetBits = ExtendedBlockTable[(int)fileHashEntry.GetBlockEntryIndex()]; adjustedBlockOffset = (long)fileBlockEntry.GetExtendedBlockOffset(upperOffsetBits); } else { adjustedBlockOffset = fileBlockEntry.GetBlockOffset(); } 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)); }