/// <summary> /// Inflates selected entry. /// </summary> /// <param name="entry">Entry to unpack.</param> /// <param name = "destfilepath">Destination file used instead of the temp file.</param> public void InflateEntry(Entry entry, string destfilepath = "") { if (entry.Length == 0) { return; //skip empty files } // Decompress Entry const int zHeader = 0x78DA; uint zChunkID = entry.zIndexBegin; int blockSize = (int)_header.BlockSizeAlloc; //bool isZlib = _header.CompressionMethod == 2053925218; if (destfilepath.Length > 0) { entry.Data = new FileStream(destfilepath, FileMode.Create, FileAccess.Write, FileShare.Read); } else { if (UseMemory) { entry.Data = new MemoryStreamExtension(); } else { entry.Data = new TempFileStream(); } } _reader.BaseStream.Position = (long)entry.Offset; do { // check for corrupt CDLC content and catch exceptions try { if (_zBlocksSizeList[zChunkID] == 0U) // raw. full cluster used. { entry.Data.Write(_reader.ReadBytes(blockSize), 0, blockSize); } else { var num = _reader.ReadUInt16(); _reader.BaseStream.Position -= 2L; var array = _reader.ReadBytes((int)_zBlocksSizeList[zChunkID]); if (num == zHeader) { // compressed try { RijndaelEncryptor.Unzip(array, entry.Data, false); } catch (Exception ex) //IOException { // corrupt CDLC zlib.net exception ... try to unpack if (String.IsNullOrEmpty(entry.Name)) { Console.WriteLine(String.Format(@"CDLC contains a zlib exception.{1}Warning: {0}", ex.Message, Environment.NewLine)); } else { Console.WriteLine(String.Format(@"CDLC contains a broken datachunk in file '{0}'.{2}Warning Type 1: {1}", entry.Name.Split('/').Last(), ex.Message, Environment.NewLine)); } } } else // raw. used only for data(chunks) smaller than 64 kb { entry.Data.Write(array, 0, array.Length); } } zChunkID += 1; } catch (Exception ex) // index is outside the bounds of the array { // corrupt CDLC data length ... try to unpack Console.WriteLine(String.Format(@"CDLC contains a broken datachunk in file '{0}'.{2}Warning Type 2: {1}", entry.Name.Split('/').Last(), ex.Message, Environment.NewLine)); break; } } while (entry.Data.Length < (long)entry.Length); entry.Data.Seek(0, SeekOrigin.Begin); entry.Data.Flush(); }
public void Read(Stream psarc, bool lazy = false) { _toc.Clear(); _reader = new BigEndianBinaryReader(psarc); _header.MagicNumber = _reader.ReadUInt32(); if (_header.MagicNumber == 1347633490U)//PSAR (BE) { //Parse Header _header.VersionNumber = _reader.ReadUInt32(); _header.CompressionMethod = _reader.ReadUInt32(); _header.TotalTOCSize = _reader.ReadUInt32(); _header.TOCEntrySize = _reader.ReadUInt32(); _header.NumFiles = _reader.ReadUInt32(); _header.BlockSizeAlloc = _reader.ReadUInt32(); _header.ArchiveFlags = _reader.ReadUInt32(); //Read TOC int tocSize = (int)(_header.TotalTOCSize - 32U); if (_header.ArchiveFlags == 4)//TOC_ENCRYPTED { // Decrypt TOC var tocStream = new MemoryStream(); using (var decStream = new MemoryStream()) { RijndaelEncryptor.DecryptPSARC(psarc, decStream, _header.TotalTOCSize); int bytesRead; int decSize = 0; var buffer = new byte[_header.BlockSizeAlloc]; while ((bytesRead = decStream.Read(buffer, 0, buffer.Length)) > 0) { decSize += bytesRead; if (decSize > tocSize) { bytesRead = tocSize - (decSize - bytesRead); } tocStream.Write(buffer, 0, bytesRead); } } tocStream.Position = 0; _reader = new BigEndianBinaryReader(tocStream); } ParseTOC(); //Parse zBlocksSizeList int tocChunkSize = (int)(_header.NumFiles * _header.TOCEntrySize);//(int)_reader.BaseStream.Position //don't alter this with. causes issues int zNum = (tocSize - tocChunkSize) / bNum; var zLengths = new uint[zNum]; for (int i = 0; i < zNum; i++) { switch (bNum) { case 2: //64KB zLengths[i] = _reader.ReadUInt16(); break; case 3: //16MB zLengths[i] = _reader.ReadUInt24(); break; case 4: //4GB zLengths[i] = _reader.ReadUInt32(); break; } } _zBlocksSizeList = zLengths; //TODO: validate _reader.BaseStream.Flush(); //Free tocStream resources _reader = new BigEndianBinaryReader(psarc); // Validate psarc size // if (psarc.Length < RequiredPsarcSize()) // throw new InvalidDataException("Truncated psarc."); // try to unpack corrupt CDLC for now switch (_header.CompressionMethod) { case 2053925218: //zlib (BE) ReadManifest(); psarc.Seek(_header.TotalTOCSize, SeekOrigin.Begin); if (!lazy) { // Decompress Data InflateEntries(); } break; case 1819962721: //lzma (BE) throw new NotImplementedException("LZMA compression not supported."); default: throw new InvalidDataException("Unknown compression."); } } psarc.Flush(); }