public void CopyTo(Stream dest) { if ((fileDes.Flags & FileCompressed) != 0) { var inf = new Inflater(true); var buffer = new byte[165535]; do { var bytesToExtract = cabFile.ReadUInt16(); RemainingArchiveStream -= 2u; RemainingFileStream -= 2u; inf.SetInput(GetBytes(bytesToExtract)); RemainingFileStream -= bytesToExtract; while (!inf.IsNeedingInput) { var inflated = inf.Inflate(buffer); dest.Write(buffer, 0, inflated); } inf.Reset(); } while (RemainingFileStream > 0); } else { do { RemainingFileStream -= RemainingArchiveStream; dest.Write(GetBytes(RemainingArchiveStream), 0, (int)RemainingArchiveStream); } while (RemainingFileStream > 0); } }
public void CopyTo(Stream output, Action<int> onProgress) { if (file.Flags.HasFlag(CABFlags.FileCompressed)) { var inf = new Inflater(true); var buffer = new byte[165535]; do { var bytesToExtract = currentVolume.ReadUInt16(); remainingInArchive -= 2; toExtract -= 2; inf.SetInput(GetBytes(bytesToExtract)); toExtract -= bytesToExtract; while (!inf.IsNeedingInput) { if (onProgress != null) onProgress((int)(100 * output.Position / file.ExpandedSize)); var inflated = inf.Inflate(buffer); output.Write(buffer, 0, inflated); } inf.Reset(); } while (toExtract > 0); } else { do { if (onProgress != null) onProgress((int)(100 * output.Position / file.ExpandedSize)); toExtract -= remainingInArchive; output.Write(GetBytes(remainingInArchive), 0, (int)remainingInArchive); } while (toExtract > 0); } }
/// <summary> /// Reads a MPK from a binary reader /// </summary> /// <param name="rdr">The binary reader pointing to the MPK</param> private void ReadArchive(BinaryReader rdr) { _files.Clear(); _crc.Value = 0; _sizeDir = 0; _sizeName = 0; _numFiles = 0; var buf = new byte[16]; rdr.Read(buf, 0, 16); for (byte i = 0; i < 16; ++i) { buf[i] ^= i; } _crc.Value = ((buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]); _sizeDir = ((buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]); _sizeName = ((buf[8] << 24) | (buf[9] << 16) | (buf[10] << 8) | buf[11]); _numFiles = ((buf[12] << 24) | (buf[13] << 16) | (buf[14] << 8) | buf[15]); buf = new byte[_sizeName]; rdr.Read(buf, 0, _sizeName); var inf = new Inflater(); inf.SetInput(buf); buf = new byte[1024]; inf.Inflate(buf); buf[inf.TotalOut] = 0; _name = Marshal.ConvertToString(buf); long totalin = 0; buf = ReadDirectory(rdr, ref totalin); using (var directory = new MemoryStream(buf)) { long pos = rdr.BaseStream.Position; long len = rdr.BaseStream.Seek(0, SeekOrigin.End); rdr.BaseStream.Position = pos; buf = new byte[len - pos]; rdr.Read(buf, 0, buf.Length); using (var files = new MemoryStream(buf)) { rdr.BaseStream.Position = pos - totalin; buf = new byte[totalin]; rdr.Read(buf, 0, buf.Length); var crc = new Crc32(); crc.Reset(); crc.Update(buf); if (crc.Value != _crc.Value) { throw new Exception("Invalid or corrupt MPK"); } while (directory.Position < directory.Length && files.Position < files.Length) { crc.Reset(); buf = new byte[MPKFileHeader.MaxSize]; directory.Read(buf, 0, MPKFileHeader.MaxSize); MPKFileHeader hdr; using (var hdrStream = new MemoryStream(buf)) { using (var hdrRdr = new BinaryReader(hdrStream, Encoding.UTF8)) { hdr = new MPKFileHeader(hdrRdr); } } var compbuf = new byte[hdr.CompressedSize]; files.Read(compbuf, 0, compbuf.Length); crc.Update(compbuf, 0, compbuf.Length); inf.Reset(); inf.SetInput(compbuf, 0, compbuf.Length); buf = new byte[hdr.UncompressedSize]; inf.Inflate(buf, 0, buf.Length); var file = new MPKFile(compbuf, buf, hdr); if (crc.Value != hdr.CRC.Value) { OnInvalidFile(file); continue; } _files.Add(hdr.Name.ToLower(), file); } } } }
/** * Release an inflater previously obtained from this cache. * * @param i * the inflater to return. May be null, in which case this method * does nothing. */ public void release(Inflater i) { if (i != null) { i.Reset(); releaseImpl(i); } }
// make string large to orginal size public string Inflate(string input) { byte[] inputData = System.Convert.FromBase64String(input); Inflater inflater = new Inflater(false); using (var inputStream = new MemoryStream(inputData)) using (var ms = new MemoryStream()) { var inputBuffer = new byte[4096]; var outputBuffer = new byte[4096]; while (inputStream.Position < inputData.Length) { var read = inputStream.Read(inputBuffer, 0, inputBuffer.Length); inflater.SetInput(inputBuffer, 0, read); while (inflater.IsNeedingInput == false) { var written = inflater.Inflate(outputBuffer, 0, outputBuffer.Length); if (written == 0) break; ms.Write(outputBuffer, 0, written); } if (inflater.IsFinished == true) break; } inflater.Reset(); return Convert.ToBase64String( ms.ToArray()); } }
/// <summary>Release an inflater previously obtained from this cache.</summary> /// <remarks>Release an inflater previously obtained from this cache.</remarks> /// <param name="i"> /// the inflater to return. May be null, in which case this method /// does nothing. /// </param> public static void Release(Inflater i) { if (i != null) { i.Reset(); if (ReleaseImpl(i)) { i.Finish(); } } }
//........................................................... /// <summary> /// Get an uncompressed blob from PAK_STREAM using the meta-data in ENTRY. /// </summary> /// <returns>A MemoryStream if possible, otherwise a FileStream to a auto-delete temp file.</returns> protected Stream Decompress(Stream PAK_STREAM, cmk.NMS.PAK.Item.Info ENTRY) { if (PAK_STREAM == null) { return(null); } Stream entry; if (ENTRY.Length < Int32.MaxValue) { entry = new MemoryStream((int)ENTRY.Length); } else { var temp = System.IO.Path.GetTempFileName(); entry = new FileStream(temp, FileMode.Open, FileAccess.ReadWrite, FileShare.None, m_block_size, FileOptions.DeleteOnClose ); } // m_block_size is the max decompressed size of a block. // each compressed block will be this size or smaller. var compressed = new byte [m_block_size]; var decompressed = new byte [m_block_size]; var inflater = new zlib.Inflater(); // using System.IO.Compression.DeflateStream.Read fails to parse the data, // even though it supposidly uses zlib behind the scenes. // todo: assume used it incorrectly, would prefer using built-in API instead of nuget zlib. var index = ENTRY.Index; // index of first block for ENTRY var offset = ENTRY.Offset; // where the first block ( m_blocks[index]) starts // e.g. ENTRY: Index = 7, Offset = 123456, Length = 123456: // - m_blocks[7] = 12345, m_blocks[8] = 6789 (example compressed sizes) // these would (likely) decompress to: 65536 and 57920 bytes = 123456 bytes Length. // - offset = 123456 is where m_blocks[7] 12345 bytes of compressed data starts, // m_blocks[8] 6789 bytes of compressed data will immediately follow // m_blocks[7] 12345 bytes, and so on. for ( ; entry.Length < ENTRY.Length; ++index) { PAK_STREAM.Position = offset; // current block is uncompressed full block, just copy to output if (m_blocks[index] == 0) { PAK_STREAM.Read(compressed, 0, m_block_size); entry.Write(compressed, 0, m_block_size); offset += m_block_size; continue; } PAK_STREAM.Read(compressed, 0, (int)m_blocks[index]); // read compressed data for current block offset += m_blocks[index]; // update offset for next block // if it's not an uncompressed full block we MUST assume it's a 'compressed' block (has compression header). // we have no way to differentiate a compression header from valid uncompressed data. // // original psarc.exe code assumes that if the compressed block does not // start with 0x78da then it's an uncompressed partial block - ugghhh. // // https://stackoverflow.com/questions/9050260/what-does-a-zlib-header-look-like // Level | ZLIB | GZIP // 1 | 78 01 | 1F 8B - No Compression/low // 2 | 78 5E | 1F 8B - Fastest Compression // 3 | 78 5E | 1F 8B // 4 | 78 5E | 1F 8B // 5 | 78 5E | 1F 8B // 6 | 78 9C | 1F 8B - Default Compression // 7 | 78 DA | 1F 8B - Best Compression (Slowest) // 8 | 78 DA | 1F 8B // 9 | 78 DA | 1F 8B // if (BitConverter.IsLittleEndian) { Array.Reverse(compressed, 0x00, 2); } var is_compressed = BitConverter.ToUInt16(compressed, 0); if (BitConverter.IsLittleEndian) { Array.Reverse(compressed, 0x00, 2); } if (is_compressed != 0x7801 && is_compressed != 0x785e && is_compressed != 0x789c && is_compressed != 0x78da ) { // MessageBox.Show("Surprise, uncompressed partial ... maybe"); // ... turns out these exist entry.Write(compressed, 0, (int)m_blocks[index]); continue; } // ??? wtf ??? // for blocks that inflate to a full block size (65536) we end up with 4 bytes // in RemainingInput after first Inflate call, the second call results in 0 bytes inflated. // the resulting data in uncompressed_file seems good though, no missing data. // if there was padding added by Deflate i'd expect it to be consumed by first Inflate. // maybe it's the 4 byte adler checksum ??? inflater.SetInput(compressed, 0, (int)m_blocks[index]); var length = inflater.Inflate(decompressed); entry.Write(decompressed, 0, length); inflater.Reset(); } entry.Position = 0; return(entry); }
public void ExtractFile(string filename, Stream output, Action<int> onProgress = null) { var file = files.FirstOrDefault(f => f.FileName == filename); if (file == null) throw new FileNotFoundException(filename); var folder = folders[file.FolderIndex]; stream.Seek(folder.BlockOffset, SeekOrigin.Begin); var inflater = new Inflater(true); var buffer = new byte[4096]; var decompressedBytes = 0; for (var i = 0; i < folder.BlockCount; i++) { if (onProgress != null) onProgress((int)(100 * output.Position / file.DecompressedLength)); // Ignore checksums stream.Position += 4; var blockLength = stream.ReadUInt16(); stream.Position += 4; using (var batch = new MemoryStream(stream.ReadBytes(blockLength - 2))) using (var inflaterStream = new InflaterInputStream(batch, inflater)) { int n; while ((n = inflaterStream.Read(buffer, 0, buffer.Length)) > 0) { var offset = Math.Max(0, file.DecompressedOffset - decompressedBytes); var count = Math.Min(n - offset, file.DecompressedLength - decompressedBytes); if (offset < n) output.Write(buffer, (int)offset, (int)count); decompressedBytes += n; } } inflater.Reset(); } }