Beispiel #1
0
        public void RemoveFile(MpqArchive mpqArchive, MpqEntry mpqEntry)
        {
            var blockIndex = mpqArchive.BlockTable._entries.IndexOf(mpqEntry);

            if (blockIndex == -1)
            {
                throw new ArgumentException("The given mpq entry could not be found in the archive.", nameof(mpqEntry));
            }

            RemoveFile(mpqArchive, blockIndex);
        }
Beispiel #2
0
        private uint _hashIndex; // position in hashtable

        /// <summary>
        /// Initializes a new instance of the <see cref="MpqFile"/> class.
        /// </summary>
        /// <param name="sourceStream"></param>
        /// <param name="fileName"></param>
        /// <param name="flags"></param>
        /// <param name="blockSize"></param>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="sourceStream"/> argument is null.</exception>
        public MpqFile(Stream sourceStream, string fileName, MpqFileFlags flags, ushort blockSize)
        {
            _baseStream = sourceStream ?? throw new ArgumentNullException(nameof(sourceStream));
            _fileName   = fileName;

            _blockSize = 0x200 << blockSize;

            var fileSize       = (uint)_baseStream.Length;
            var compressedSize = ((flags & MpqFileFlags.Compressed) != 0) ? Compress() : fileSize;

            _entry = new MpqEntry(fileName, compressedSize, fileSize, flags);
        }
Beispiel #3
0
        /// <summary>
        /// Initializes a new instance of the <see cref="MpqStream"/> class.
        /// </summary>
        /// <param name="entry"></param>
        /// <param name="baseStream"></param>
        /// <param name="blockSize"></param>
        internal MpqStream(MpqEntry entry, Stream baseStream, int blockSize)
        {
            _entry = entry;

            _stream    = baseStream;
            _blockSize = blockSize;

            if (_entry.IsCompressed && !_entry.IsSingleUnit)
            {
                LoadBlockPositions();
            }
        }
Beispiel #4
0
        /// <summary>
        /// Initializes a new instance of the <see cref="MpqStream"/> class.
        /// </summary>
        /// <param name="archive"></param>
        /// <param name="entry"></param>
        internal MpqStream(MpqArchive archive, MpqEntry entry)
        {
            _entry = entry;

            _stream    = archive.BaseStream;
            _blockSize = archive.BlockSize;

            if (_entry.IsCompressed && !_entry.IsSingleUnit)
            {
                LoadBlockPositions();
            }
        }
Beispiel #5
0
 protected override void GetTableEntries(MpqArchive mpqArchive, uint index, uint relativeFileOffset, uint compressedSize, uint fileSize, out MpqEntry mpqEntry, out MpqHash mpqHash)
 {
     throw new NotSupportedException();
 }
Beispiel #6
0
        /// <summary>
        /// Initializes a new instance of the <see cref="MpqStream"/> class.
        /// </summary>
        /// <param name="entry">The file's entry in the <see cref="BlockTable"/>.</param>
        /// <param name="baseStream">The <see cref="MpqArchive"/>'s stream.</param>
        /// <param name="blockSize">The <see cref="MpqArchive.BlockSize"/>.</param>
        internal MpqStream(MpqEntry entry, Stream baseStream, int blockSize)
        {
            _mode          = MpqStreamMode.Read;
            _isStreamOwner = false;

            _filePosition   = entry.FilePosition;
            _fileSize       = entry.FileSize;
            _compressedSize = entry.CompressedSize;
            _flags          = entry.Flags;
            _isCompressed   = (_flags & MpqFileFlags.Compressed) != 0;
            _isEncrypted    = _flags.HasFlag(MpqFileFlags.Encrypted);
            _isSingleUnit   = _flags.HasFlag(MpqFileFlags.SingleUnit);

            _encryptionSeed     = entry.EncryptionSeed;
            _baseEncryptionSeed = entry.BaseEncryptionSeed;

            _stream    = baseStream;
            _blockSize = blockSize;

            if (_isSingleUnit)
            {
                // Read the entire file into memory
                var filedata = new byte[_compressedSize];
                lock (_stream)
                {
                    _stream.Seek(_filePosition, SeekOrigin.Begin);
                    var read = _stream.Read(filedata, 0, filedata.Length);
                    if (read != filedata.Length)
                    {
                        throw new MpqParserException("Insufficient data or invalid data length");
                    }
                }

                if (_isEncrypted && _fileSize > 3)
                {
                    if (_encryptionSeed == 0)
                    {
                        throw new MpqParserException("Unable to determine encryption key");
                    }

                    StormBuffer.DecryptBlock(filedata, _encryptionSeed);
                }

                _currentData = _flags.HasFlag(MpqFileFlags.CompressedMulti) && _compressedSize > 0
                    ? DecompressMulti(filedata, _fileSize)
                    : filedata;
            }
            else
            {
                _currentBlockIndex = -1;

                // Compressed files start with an array of offsets to make seeking possible
                if (_isCompressed)
                {
                    var blockposcount = (int)((_fileSize + _blockSize - 1) / _blockSize) + 1;

                    // Files with metadata have an extra block containing block checksums
                    if ((_flags & MpqFileFlags.FileHasMetadata) != 0)
                    {
                        blockposcount++;
                    }

                    _blockPositions = new uint[blockposcount];

                    lock (_stream)
                    {
                        _stream.Seek(_filePosition, SeekOrigin.Begin);
                        using (var br = new BinaryReader(_stream, new UTF8Encoding(), true))
                        {
                            for (var i = 0; i < blockposcount; i++)
                            {
                                _blockPositions[i] = br.ReadUInt32();
                            }
                        }
                    }

                    var blockpossize = (uint)blockposcount * 4;

                    /*
                     * if (_blockPositions[0] != blockpossize)
                     * {
                     *  // _entry.Flags |= MpqFileFlags.Encrypted;
                     *  throw new MpqParserException();
                     * }
                     */

                    if (_isEncrypted && blockposcount > 1)
                    {
                        var maxOffset1 = (uint)_blockSize + blockpossize;
                        if (_encryptionSeed == 0)
                        {
                            // This should only happen when the file name is not known.
                            if (!entry.TryUpdateEncryptionSeed(_blockPositions[0], _blockPositions[1], blockpossize, maxOffset1))
                            {
                                throw new MpqParserException("Unable to determine encyption seed");
                            }
                        }

                        _encryptionSeed     = entry.EncryptionSeed;
                        _baseEncryptionSeed = entry.BaseEncryptionSeed;
                        StormBuffer.DecryptBlock(_blockPositions, _encryptionSeed - 1);

                        if (_blockPositions[0] != blockpossize)
                        {
                            throw new MpqParserException($"Decryption failed{(string.IsNullOrEmpty(entry.FileName) ? string.Empty : $" for '{entry.FileName}'")} (block position 0).");
                        }

                        if (_blockPositions[1] > maxOffset1)
                        {
                            throw new MpqParserException($"Decryption failed{(string.IsNullOrEmpty(entry.FileName) ? string.Empty : $" for '{entry.FileName}'")} (block position 1).");
                        }
                    }
                }
Beispiel #7
0
 /// <summary>
 /// Initializes a new instance of the <see cref="MpqStream"/> class.
 /// </summary>
 /// <param name="archive">The archive from which to load a file.</param>
 /// <param name="entry">The file's entry in the <see cref="BlockTable"/>.</param>
 internal MpqStream(MpqArchive archive, MpqEntry entry)
     : this(entry, archive.BaseStream, archive.BlockSize)
 {
 }
Beispiel #8
0
 protected override void GetTableEntries(MpqArchive mpqArchive, uint index, uint relativeFileOffset, uint compressedSize, uint fileSize, out MpqEntry mpqEntry, out MpqHash mpqHash)
 {
     mpqEntry = new MpqEntry(null, mpqArchive.HeaderOffset, relativeFileOffset, compressedSize, fileSize, TargetFlags);
     mpqHash  = new MpqHash(Name, Locale, index, Mask);
 }
Beispiel #9
0
        internal Stream Transform(MpqFileFlags targetFlags, MpqCompressionType compressionType, uint targetFilePosition, int targetBlockSize)
        {
            using var memoryStream = new MemoryStream();
            CopyTo(memoryStream);
            memoryStream.Position = 0;
            var fileSize = memoryStream.Length;

            using var compressedStream = GetCompressedStream(memoryStream, targetFlags, compressionType, targetBlockSize);
            var compressedSize = (uint)compressedStream.Length;

            var resultStream = new MemoryStream();

            var blockPosCount = (uint)(((int)fileSize + targetBlockSize - 1) / targetBlockSize) + 1;

            if (targetFlags.HasFlag(MpqFileFlags.Encrypted) && blockPosCount > 1)
            {
                var blockPositions = new int[blockPosCount];
                var singleUnit     = targetFlags.HasFlag(MpqFileFlags.SingleUnit);

                var hasBlockPositions = !singleUnit && ((targetFlags & MpqFileFlags.Compressed) != 0);
                if (hasBlockPositions)
                {
                    for (var blockIndex = 0; blockIndex < blockPosCount; blockIndex++)
                    {
                        using (var br = new BinaryReader(compressedStream, new UTF8Encoding(), true))
                        {
                            for (var i = 0; i < blockPosCount; i++)
                            {
                                blockPositions[i] = (int)br.ReadUInt32();
                            }
                        }

                        compressedStream.Seek(0, SeekOrigin.Begin);
                    }
                }
                else
                {
                    if (singleUnit)
                    {
                        blockPosCount = 2;
                    }

                    blockPositions[0] = 0;
                    for (var blockIndex = 2; blockIndex < blockPosCount; blockIndex++)
                    {
                        blockPositions[blockIndex - 1] = targetBlockSize * (blockIndex - 1);
                    }

                    blockPositions[blockPosCount - 1] = (int)compressedSize;
                }

                var encryptionSeed = _baseEncryptionSeed;
                if (targetFlags.HasFlag(MpqFileFlags.BlockOffsetAdjustedKey))
                {
                    encryptionSeed = MpqEntry.AdjustEncryptionSeed(encryptionSeed, targetFilePosition, (uint)fileSize);
                }

                var currentOffset = 0;
                using (var writer = new BinaryWriter(resultStream, new UTF8Encoding(false, true), true))
                {
                    for (var blockIndex = hasBlockPositions ? 0 : 1; blockIndex < blockPosCount; blockIndex++)
                    {
                        var toWrite = blockPositions[blockIndex] - currentOffset;

                        var data = StormBuffer.EncryptStream(compressedStream, (uint)(encryptionSeed + blockIndex - 1), currentOffset, toWrite);
                        writer.Write(data);

                        currentOffset += toWrite;
                    }
                }
            }
            else
            {
                compressedStream.CopyTo(resultStream);
            }

            resultStream.Position = 0;
            return(resultStream);
        }