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); }
/// <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)); }
/// <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)."); } } }