Exemplo n.º 1
0
        private byte[] LoadBlock(int blockIndex, int expectedLength)
        {
            uint offset;
            int  toread;
            uint encryptionseed;

            if (_entry.IsCompressed)
            {
                offset = _blockPositions[blockIndex];
                toread = (int)(_blockPositions[blockIndex + 1] - offset);
            }
            else
            {
                offset = (uint)(blockIndex * _blockSize);
                toread = expectedLength;
            }

            offset += _entry.FilePos;

            var data = new byte[toread];

            lock (_stream)
            {
                _stream.Seek(offset, SeekOrigin.Begin);
                var read = _stream.Read(data, 0, toread);
                if (read != toread)
                {
                    throw new MpqParserException("Insufficient data or invalid data length");
                }
            }

            if (_entry.IsEncrypted && _entry.FileSize > 3)
            {
                if (_entry.EncryptionSeed == 0)
                {
                    throw new MpqParserException("Unable to determine encryption key");
                }

                encryptionseed = (uint)(blockIndex + _entry.EncryptionSeed);
                StormBuffer.DecryptBlock(data, encryptionseed);
            }

            if (_entry.IsCompressed && (toread != expectedLength))
            {
                data = (_entry.Flags & MpqFileFlags.CompressedMulti) != 0
                    ? DecompressMulti(data, expectedLength)
                    : PKDecompress(new MemoryStream(data), expectedLength);
            }

            return(data);
        }
Exemplo n.º 2
0
 /// <summary>
 /// Decrypts the contents of the <see cref="MpqTable"/>.
 /// </summary>
 /// <param name="data">The encrypted entries in the table.</param>
 internal void Decrypt(byte[] data)
 {
     StormBuffer.DecryptBlock(data, StormBuffer.HashString(Key, 0x300));
 }
Exemplo n.º 3
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).");
                        }
                    }
                }