internal MpqFile(ulong?hashedName, MpqStream mpqStream, MpqFileFlags flags, MpqLocale locale, bool leaveOpen) { _name = hashedName; _mpqStream = mpqStream ?? throw new ArgumentNullException(nameof(mpqStream)); _isStreamOwner = !leaveOpen; _flags = flags; _locale = locale; _compressionType = MpqCompressionType.ZLib; }
private Stream GetCompressedStream(Stream baseStream, MpqFileFlags targetFlags, MpqCompressionType compressionType, int targetBlockSize) { var resultStream = new MemoryStream(); var singleUnit = targetFlags.HasFlag(MpqFileFlags.SingleUnit); void TryCompress(uint bytes) { var offset = baseStream.Position; var compressedStream = compressionType switch { MpqCompressionType.ZLib => ZLibCompression.Compress(baseStream, (int)bytes, true), _ => throw new NotSupportedException(), }; // Add one because CompressionType byte not written yet. var length = compressedStream.Length + 1; if (!singleUnit && length >= bytes) { baseStream.CopyTo(resultStream, offset, (int)bytes, StreamExtensions.DefaultBufferSize); } else { resultStream.WriteByte((byte)compressionType); compressedStream.Position = 0; compressedStream.CopyTo(resultStream); } compressedStream.Dispose(); if (singleUnit) { baseStream.Dispose(); } } var length = (uint)baseStream.Length; if ((targetFlags & MpqFileFlags.Compressed) == 0) { baseStream.CopyTo(resultStream); } else if (singleUnit) { TryCompress(length); } else { var blockCount = (uint)((length + targetBlockSize - 1) / targetBlockSize) + 1; var blockOffsets = new uint[blockCount]; blockOffsets[0] = 4 * blockCount; resultStream.Position = blockOffsets[0]; for (var blockIndex = 1; blockIndex < blockCount; blockIndex++) { var bytesToCompress = blockIndex + 1 == blockCount ? (uint)(baseStream.Length - baseStream.Position) : (uint)targetBlockSize; TryCompress(bytesToCompress); blockOffsets[blockIndex] = (uint)resultStream.Position; } resultStream.Position = 0; using (var writer = new BinaryWriter(resultStream, new System.Text.UTF8Encoding(false, true), true)) { for (var blockIndex = 0; blockIndex < blockCount; blockIndex++) { writer.Write(blockOffsets[blockIndex]); } } } resultStream.Position = 0; return(resultStream); }
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); }