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();
}