Exemple #1
0
        /// <summary>
        /// Initializes a new instance of the <see cref="Warcraft.MPQ.MPQ"/> class.
        /// </summary>
        /// <param name="mpqStream">An open stream to data containing an MPQ archive.</param>
        public MPQ([NotNull] Stream mpqStream)
        {
            _archiveReader = new BinaryReader(mpqStream);

            Header = new MPQHeader(_archiveReader.ReadBytes((int)PeekHeaderSize()));

            // Seek to the hash table and load it
            _archiveReader.BaseStream.Position = (long)Header.GetHashTableOffset();

            var encryptedHashTable = _archiveReader.ReadBytes((int)Header.GetHashTableSize());
            var hashTableData      = MPQCrypt.DecryptData(encryptedHashTable, HashTable.TableKey);

            ArchiveHashTable = new HashTable(hashTableData);

            // Seek to the block table and load it
            _archiveReader.BaseStream.Position = (long)Header.GetBlockTableOffset();

            var encryptedBlockTable = _archiveReader.ReadBytes((int)Header.GetBlockTableSize());
            var blockTableData      = MPQCrypt.DecryptData(encryptedBlockTable, BlockTable.TableKey);

            ArchiveBlockTable = new BlockTable(blockTableData);

            if (Header.GetFormat() != MPQFormat.ExtendedV1)
            {
                return;
            }

            // Seek to the extended block table and load it, if necessary
            if (Header.GetExtendedBlockTableOffset() <= 0)
            {
                return;
            }

            _archiveReader.BaseStream.Position = (long)Header.GetExtendedBlockTableOffset();
            var extendedTable = new List <ushort>();

            for (var i = 0; i < Header.GetBlockTableEntryCount(); ++i)
            {
                extendedTable.Add(_archiveReader.ReadUInt16());
            }

            ExtendedBlockTable = extendedTable;
        }
Exemple #2
0
        /// <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));
        }
Exemple #3
0
        /// <summary>
        /// Initializes a new instance of the <see cref="Warcraft.MPQ.MPQ"/> class.
        /// </summary>
        /// <param name="mpqStream">An open stream to data containing an MPQ archive.</param>
        public MPQ(Stream mpqStream)
        {
            ArchiveReader = new BinaryReader(mpqStream);

            Header = new MPQHeader(ArchiveReader.ReadBytes((int)PeekHeaderSize()));

            // Seek to the hash table and load it
            ArchiveReader.BaseStream.Position = (long)Header.GetHashTableOffset();

            byte[] hashTableData;
            if (Header.IsHashTableCompressed())
            {
                byte[] encryptedData = ArchiveReader.ReadBytes((int)Header.GetCompressedHashTableSize());
                byte[] decryptedData = MPQCrypt.DecryptData(encryptedData, HashTable.TableKey);

                BlockFlags tableFlags = BlockFlags.IsCompressedMultiple;
                hashTableData = Compression.DecompressSector(decryptedData, tableFlags);
            }
            else
            {
                byte[] encryptedData = ArchiveReader.ReadBytes((int)Header.GetHashTableSize());
                hashTableData = MPQCrypt.DecryptData(encryptedData, HashTable.TableKey);
            }

            ArchiveHashTable = new HashTable(hashTableData);

            // Seek to the block table and load it
            ArchiveReader.BaseStream.Position = (long)Header.GetBlockTableOffset();

            byte[] blockTableData;
            if (Header.IsBlockTableCompressed())
            {
                byte[] encryptedData = ArchiveReader.ReadBytes((int)Header.GetCompressedBlockTableSize());
                byte[] decryptedData = MPQCrypt.DecryptData(encryptedData, BlockTable.TableKey);

                BlockFlags tableFlags = BlockFlags.IsCompressedMultiple;
                blockTableData = Compression.DecompressSector(decryptedData, tableFlags);
            }
            else
            {
                byte[] encryptedData = ArchiveReader.ReadBytes((int)Header.GetBlockTableSize());
                blockTableData = MPQCrypt.DecryptData(encryptedData, BlockTable.TableKey);
            }

            ArchiveBlockTable = new BlockTable(blockTableData);

            // TODO: Seek to the extended hash table and load it
            // TODO: Seek to the extended block table and load it

            if (Header.GetFormat() >= MPQFormat.ExtendedV1)
            {
                // Seek to the extended block table and load it, if neccesary
                if (Header.GetExtendedBlockTableOffset() <= 0)
                {
                    return;
                }

                ArchiveReader.BaseStream.Position = (long)Header.GetExtendedBlockTableOffset();
                for (int i = 0; i < Header.GetBlockTableEntryCount(); ++i)
                {
                    ExtendedBlockTable.Add(ArchiveReader.ReadUInt16());
                }
            }
        }