private static MemoryStream Decompress(BinaryReader reader, PackedFileHeader header) { var blockCount = header.DataBlocksCount; var decompressedSize = header.DecompressedSize; var isReforged = header.IsReforgedReplay; var decompressedData = new MemoryStream(); for (var chunk = 0; chunk < blockCount; chunk++) { var compressedChunkSize = reader.ReadUInt16(); if (isReforged) { reader.BaseStream.Seek(2, SeekOrigin.Current); } var decompressedChunkSize = reader.ReadUInt16(); var checksum = reader.ReadUInt32(); if (isReforged) { reader.BaseStream.Seek(2, SeekOrigin.Current); } using var compressedDataStream = new MemoryStream(); reader.BaseStream.CopyTo(compressedDataStream, compressedChunkSize, StreamExtensions.DefaultBufferSize); compressedDataStream.Position = 0; var decompressedDataBytes = ZLibCompression.Decompress(compressedDataStream, decompressedChunkSize); var decompressedDataLength = (chunk + 1 == blockCount) ? (int)(decompressedSize - decompressedData.Length) : decompressedChunkSize; decompressedData.Write(decompressedDataBytes, 0, decompressedDataLength); } decompressedData.Position = 0; return(decompressedData); }
public static PackedFile Parse(Stream stream, bool leaveOpen = false) { var file = new PackedFile(); using (var reader = new BinaryReader(stream, new UTF8Encoding(false, true), leaveOpen)) { file._header = PackedFileHeader.FromReader(reader); file._decompressedData = Decompress(reader, file._header); } return(file); }
public static PackedFileHeader FromReader(BinaryReader reader) { if (new string(reader.ReadChars(HeaderString.Length)) != HeaderString) { throw new InvalidDataException("Invalid file header."); } using var crcStream = new MemoryStream(); using var crcWriter = new BinaryWriter(crcStream); var header = new PackedFileHeader(); header._dataOffset = reader.ReadUInt32(); header._compressedSize = reader.ReadUInt32(); header._headerVersion = reader.ReadUInt32(); header._decompressedSize = reader.ReadUInt32(); header._dataBlocksCount = reader.ReadUInt32(); crcWriter.WriteString(HeaderString); crcWriter.Write(header._dataOffset); crcWriter.Write(header._compressedSize); crcWriter.Write(header._headerVersion); crcWriter.Write(header._decompressedSize); crcWriter.Write(header._dataBlocksCount); if (header._headerVersion == 0x00) { if (header._dataOffset != 0x40) { throw new InvalidDataException("Invalid header size."); } if (reader.ReadUInt16() != 0) { throw new InvalidDataException(); } header._gameVersion = reader.ReadUInt16(); crcWriter.Write((ushort)0); crcWriter.Write((ushort)header._gameVersion); } else if (header._headerVersion == 0x01) { if (header._dataOffset != 0x44) { throw new InvalidDataException("Invalid header size."); } header._gameIdentifier = reader.ReadUInt32(); header._gameVersion = reader.ReadUInt32(); crcWriter.Write(header._gameIdentifier); crcWriter.Write(header._gameVersion); } else { throw new NotSupportedException("Only header version 1 is supported."); } header._build = reader.ReadUInt16(); header._flags = reader.ReadUInt16(); header._gameLength = reader.ReadUInt32(); header._checksum = reader.ReadUInt32(); crcWriter.Write(header._build); crcWriter.Write(header._flags); crcWriter.Write(header._gameLength); crcWriter.Write(0U); crcStream.Position = 0; var crc = new Ionic.Crc.CRC32().GetCrc32(crcStream); if (crc != header._checksum) { throw new InvalidDataException("CRC failed"); } return(header); }