public MemoryStream DecompressEntry(FileEntryStruct e) { //Debug.WriteLine("Decompressing " + e.FileName); MemoryStream result = new MemoryStream(); uint count = 0; byte[] inputBlock; long left = e.RealUncompressedSize; FileStream fs = new FileStream(FileName, FileMode.Open, FileAccess.Read); fs.Seek(e.BlockOffsets[0], SeekOrigin.Begin); byte[] buff; if (e.BlockSizeTableIndex == 0xFFFFFFFF) { buff = new byte[e.RealUncompressedSize]; fs.Read(buff, 0, buff.Length); result.Write(buff, 0, buff.Length); } else { while (left > 0) { uint compressedBlockSize = e.BlockSizes[count]; if (compressedBlockSize == 0) { compressedBlockSize = Header.MaxBlockSize; } if (compressedBlockSize == Header.MaxBlockSize || compressedBlockSize == left) { //uncompressed? buff = new byte[compressedBlockSize]; fs.Read(buff, 0, buff.Length); result.Write(buff, 0, buff.Length); left -= compressedBlockSize; } else { var uncompressedBlockSize = (uint)Math.Min(left, Header.MaxBlockSize); if (compressedBlockSize < 5) { throw new Exception("compressed block size smaller than 5"); } inputBlock = new byte[compressedBlockSize]; //Debug.WriteLine($"Decompressing at 0x{fs.Position:X8}"); fs.Read(inputBlock, 0, (int)compressedBlockSize); uint actualUncompressedBlockSize = uncompressedBlockSize; if (Header.CompressionScheme == "amzl" /* PC */ || Header.CompressionScheme == "lzma" /* PS3 (it doesn't appear to actually be LZMA!), WiiU */) { //if (Header.CompressionScheme == "lzma") //{ //PS3 - This doesn't work. I'm not sure what kind of LZMA this uses but it has seemingly no header //var attachedHeader = new byte[inputBlock.Length + 5]; //attachedHeader[0] = 0x5D; ////attachedHeader[1] = (byte) (Header.Version >> 24); ////attachedHeader[2] = (byte)(Header.Version >> 16); ////attachedHeader[3] = (byte)(Header.Version >> 8); ////attachedHeader[4] = (byte) Header.Version; //attachedHeader[1] = (byte)Header.Version; //attachedHeader[2] = (byte)(Header.Version >> 8); //attachedHeader[3] = (byte)(Header.Version >> 16); //attachedHeader[4] = (byte)(Header.Version >> 24); //Buffer.BlockCopy(inputBlock,0,attachedHeader,5, inputBlock.Length); //inputBlock = attachedHeader; //} var outputBlock = LZMA.Decompress(inputBlock, actualUncompressedBlockSize); if (outputBlock.Length != actualUncompressedBlockSize) { throw new Exception("SFAR LZMA Decompression Error"); } result.Write(outputBlock, 0, (int)actualUncompressedBlockSize); left -= uncompressedBlockSize; //continue; } if (Header.CompressionScheme == "lzx") //Xbox { var outputBlock = new byte[actualUncompressedBlockSize]; var decompResult = LZX.Decompress(inputBlock, (uint)inputBlock.Length, outputBlock); if (decompResult != 0) { throw new Exception("SFAR LZX Decompression Error"); } result.Write(outputBlock, 0, (int)actualUncompressedBlockSize); left -= uncompressedBlockSize; } } count++; } } fs.Close(); result.Position = 0; return(result); }
// #region Decompression // // REMOVE THIS METHOD AND USE THE STANDARDIZED ONE // /// <summary> // /// decompress an entire ME3, 2, or 1 package file. // /// </summary> // /// <param name="pccFileName">pcc file's name to open.</param> // /// <returns>a decompressed array of bytes.</returns> // //public static Stream Decompress(string pccFileName) // //{ // // using (FileStream input = File.OpenRead(pccFileName)) // // { // // EndianReader packageReader = EndianReader.SetupForPackageReading(input); // // packageReader.SkipInt32(); //skip package tag // // var versionLicenseePacked = packageReader.ReadUInt32(); // // var unrealVersion = (ushort)(versionLicenseePacked & 0xFFFF); // // var licenseeVersion = (ushort)(versionLicenseePacked >> 16); // // //ME3 // // if ((unrealVersion == MEPackage.ME3UnrealVersion || unrealVersion == MEPackage.ME3WiiUUnrealVersion) && licenseeVersion == MEPackage.ME3LicenseeVersion) // // { // // return DecompressME3(packageReader); // // } // // //Support other platforms // // //ME2 || ME1 // // else if (unrealVersion == 512 && licenseeVersion == 130 || unrealVersion == 491 && licenseeVersion == 1008) // // { // // return DecompressME1orME2(input); // // } // // else // // { // // throw new FormatException("Not an ME1, ME2, or ME3 package file."); // // } // // } // //} // // REMOVE THIS METHOD AND USE THE STANDARDIZED ONE // /// <summary> // /// decompress an entire ME1 or 2 pcc file. // /// </summary> // /// <param name="raw">pcc file passed in stream format</param> // /// <returns>a decompressed stream.</returns> // //public static MemoryStream DecompressME1orME2(Stream raw) // //{ // // raw.Seek(4, SeekOrigin.Begin); // // ushort versionLo = raw.ReadUInt16(); // // ushort versionHi = raw.ReadUInt16(); // // raw.Seek(12, SeekOrigin.Begin); // // int tempNameSize = raw.ReadInt32(); // // raw.Seek(64 + tempNameSize, SeekOrigin.Begin); // // int tempGenerations = raw.ReadInt32(); // // raw.Seek(32 + tempGenerations * 12, SeekOrigin.Current); // // //if ME1 // // if (versionLo == 491 && versionHi == 1008) // // { // // raw.Seek(4, SeekOrigin.Current); // // } // // UnrealPackageFile.CompressionType compressionType = (UnrealPackageFile.CompressionType)raw.ReadUInt32(); // // int pos = 4; // // int NumChunks = raw.ReadInt32(); // // var Chunks = new List<Chunk>(); // // //DebugOutput.PrintLn("Reading chunk headers..."); // // for (int i = 0; i < NumChunks; i++) // // { // // Chunk c = new Chunk // // { // // uncompressedOffset = raw.ReadInt32(), // // uncompressedSize = raw.ReadInt32(), // // compressedOffset = raw.ReadInt32(), // // compressedSize = raw.ReadInt32() // // }; // // c.Compressed = new byte[c.compressedSize]; // // c.Uncompressed = new byte[c.uncompressedSize]; // // //DebugOutput.PrintLn("Chunk " + i + ", compressed size = " + c.compressedSize + ", uncompressed size = " + c.uncompressedSize); // // //DebugOutput.PrintLn("Compressed offset = " + c.compressedOffset + ", uncompressed offset = " + c.uncompressedOffset); // // Chunks.Add(c); // // } // // //DebugOutput.PrintLn("\tRead Chunks..."); // // int count = 0; // // for (int i = 0; i < Chunks.Count; i++) // // { // // Chunk c = Chunks[i]; // // raw.Seek(c.compressedOffset, SeekOrigin.Begin); // // c.Compressed = raw.ReadToBuffer(c.compressedSize); // // ChunkHeader h = new ChunkHeader // // { // // magic = BitConverter.ToInt32(c.Compressed, 0), // // blocksize = BitConverter.ToInt32(c.Compressed, 4), // // compressedsize = BitConverter.ToInt32(c.Compressed, 8), // // uncompressedsize = BitConverter.ToInt32(c.Compressed, 12) // // }; // // if (h.magic != -1641380927) // // throw new FormatException("Chunk magic number incorrect"); // // //DebugOutput.PrintLn("Chunkheader read: Magic = " + h.magic + ", Blocksize = " + h.blocksize + ", Compressed Size = " + h.compressedsize + ", Uncompressed size = " + h.uncompressedsize); // // pos = 16; // // int blockCount = (h.uncompressedsize % h.blocksize == 0) // // ? // // h.uncompressedsize / h.blocksize // // : // // h.uncompressedsize / h.blocksize + 1; // // var BlockList = new List<Block>(); // // //DebugOutput.PrintLn("\t\t" + count + " Read Blockheaders..."); // // for (int j = 0; j < blockCount; j++) // // { // // Block b = new Block // // { // // compressedsize = BitConverter.ToInt32(c.Compressed, pos), // // uncompressedsize = BitConverter.ToInt32(c.Compressed, pos + 4) // // }; // // //DebugOutput.PrintLn("Block " + j + ", compressed size = " + b.compressedsize + ", uncompressed size = " + b.uncompressedsize); // // pos += 8; // // BlockList.Add(b); // // } // // int outpos = 0; // // //DebugOutput.PrintLn("\t\t" + count + " Read and decompress Blocks..."); // // foreach (Block b in BlockList) // // { // // var datain = new byte[b.compressedsize]; // // var dataout = new byte[b.uncompressedsize]; // // for (int j = 0; j < b.compressedsize; j++) // // datain[j] = c.Compressed[pos + j]; // // pos += b.compressedsize; // // switch (compressionType) // // { // // case UnrealPackageFile.CompressionType.LZO: // // { // // if ( // // LZO2.Decompress(datain, (uint)datain.Length, dataout) != b.uncompressedsize) // // throw new Exception("LZO decompression failed!"); // // break; // // } // // case UnrealPackageFile.CompressionType.Zlib: // // { // // if (ZlibHelper.Zlib.Decompress(datain, (uint)datain.Length, dataout) != b.uncompressedsize) // // throw new Exception("Zlib decompression failed!"); // // break; // // } // // default: // // throw new Exception("Unknown compression type for this package."); // // } // // for (int j = 0; j < b.uncompressedsize; j++) // // c.Uncompressed[outpos + j] = dataout[j]; // // outpos += b.uncompressedsize; // // } // // c.header = h; // // c.blocks = BlockList; // // count++; // // Chunks[i] = c; // // } // // MemoryStream result = new MemoryStream(); // // foreach (Chunk c in Chunks) // // { // // result.Seek(c.uncompressedOffset, SeekOrigin.Begin); // // result.WriteFromBuffer(c.Uncompressed); // // } // // return result; // //} // /// <summary> // /// Reads // /// </summary> // /// <param name="input"></param> // /// <param name="compressionType"></param> // /// <returns></returns> // public static byte[] DecompressChunks(EndianReader input, List<Chunk> chunkTable, UnrealPackageFile.CompressionType compressionType) // { // var fullUncompressedSize = chunkTable.Sum(x => x.uncompressedSize); // byte[] decompressedBuffer = new byte[fullUncompressedSize]; // foreach (var chunk in chunkTable) // { // //Header of individual chunk // input.Seek(chunk.compressedOffset, SeekOrigin.Begin); // var uncompressedOffset = input.ReadUInt32(); //where to write to into the decompressed buffer // var uncompressedSize = input.ReadUInt32(); //how much to write // var compressedOffset = input.ReadUInt32(); //where to read from // var compressedSize = input.ReadUInt32(); //how much to read // var firstBlockInfoOffset = (int)input.Position; // var buff = new byte[compressedSize]; // input.Seek(compressedOffset, SeekOrigin.Begin); // input.Read(buff, 0, buff.Length); // if (compressionType == UnrealPackageFile.CompressionType.Zlib) // { // } // else if (compressionType == UnrealPackageFile.CompressionType.LZMA) // { // } // //AmaroK86.MassEffect3.ZlibBlock.ZBlock.Decompress(buff, i); // //tasks[i] = AmaroK86.MassEffect3.ZlibBlock.ZBlock.Decompress(buff, i); // } // return decompressedBuffer; // } // #region Block decompression // public static readonly uint zlibmagic = 0x9E2A83C1; // public static readonly uint zlibmaxsegmentsize = 0x20000; // public static byte[] DecompressZLibBlock(byte[] buffer, int num = 0) // { // if (buffer == null) // throw new ArgumentNullException(); // using (MemoryStream buffStream = new MemoryStream(buffer)) // { // EndianReader reader = EndianReader.SetupForReading(buffStream, (int)zlibmagic, out int zlibBlockMagic); // if ((uint)zlibBlockMagic != zlibmagic) // { // throw new InvalidDataException("found an invalid zlib block, wrong magic"); // } // uint buffMaxSegmentSize = reader.ReadUInt32(); // if (buffMaxSegmentSize != zlibmaxsegmentsize) // { // throw new FormatException("Wrong segment size for ZLIB!"); // } // uint totComprSize = reader.ReadUInt32(); // uint totUncomprSize = reader.ReadUInt32(); // byte[] outputBuffer = new byte[totUncomprSize]; // int numOfSegm = (int)Math.Ceiling(totUncomprSize / (double)zlibmaxsegmentsize); // int headSegm = 16; // int dataSegm = headSegm + (numOfSegm * 8); // int buffOff = 0; // for (int i = 0; i < numOfSegm; i++) // { // reader.Seek(headSegm, SeekOrigin.Begin); // int comprSegm = reader.ReadInt32(); // int uncomprSegm = reader.ReadInt32(); // headSegm = (int)reader.Position; // reader.Seek(dataSegm, SeekOrigin.Begin); // //Console.WriteLine("compr size: {0}, uncompr size: {1}, data offset: 0x{2:X8}", comprSegm, uncomprSegm, dataSegm); // byte[] src = reader.ReadBytes(comprSegm); // byte[] dst = new byte[uncomprSegm]; // if (Zlib.Decompress(src, (uint)src.Length, dst) != uncomprSegm) // throw new Exception("Zlib decompression failed!"); // Buffer.BlockCopy(dst, 0, outputBuffer, buffOff, uncomprSegm); // buffOff += uncomprSegm; // dataSegm += comprSegm; // } // reader.Close(); // return outputBuffer; // } // } // #endregion // /// <summary> // /// decompress an entire ME3 pcc file into a new stream // /// </summary> // /// <param name="input">pcc file passed in stream format (wrapped in endianreader)</param> // /// <returns>a decompressed array of bytes</returns> // //public static MemoryStream DecompressME3(EndianReader input) // //{ // // input.Seek(0, SeekOrigin.Begin); // // var magic = input.ReadUInt32(); // // if (magic != 0x9E2A83C1) // // { // // throw new FormatException("not a pcc file"); // // } // // var versionLo = input.ReadUInt16(); // // var versionHi = input.ReadUInt16(); // // //if (versionLo != 684 && // // // versionHi != 194) // // //{ // // // throw new FormatException("unsupported pcc version"); // // //} // // long headerSize = 8; // // input.Seek(4, SeekOrigin.Current); // // headerSize += 4; // // var folderNameLength = input.ReadInt32(); // // headerSize += 4; // // var folderNameByteLength = // // folderNameLength >= 0 ? folderNameLength : (-folderNameLength * 2); // // input.Seek(folderNameByteLength, SeekOrigin.Current); // // headerSize += folderNameByteLength; // // var packageFlagsOffset = input.Position; // // var packageFlags = input.ReadUInt32(); // // headerSize += 4; // // if ((packageFlags & 0x02000000u) == 0) // // { // // throw new FormatException("pcc file is already decompressed"); // // } // // if ((packageFlags & 8) != 0) // // { // // input.Seek(4, SeekOrigin.Current); // // headerSize += 4; // // } // // uint nameCount = input.ReadUInt32(); // // uint nameOffset = input.ReadUInt32(); // // input.Seek(52, SeekOrigin.Current); // // headerSize += 60; // // var generationsCount = input.ReadUInt32(); // // input.Seek(generationsCount * 12, SeekOrigin.Current); // // headerSize += generationsCount * 12; // // input.Seek(20, SeekOrigin.Current); // // headerSize += 24; // // var blockCount = input.ReadUInt32(); // // int headBlockOff = (int)input.Position; // // var afterBlockTableOffset = headBlockOff + (blockCount * 16); // // var indataOffset = afterBlockTableOffset + 8; // // input.Seek(0, SeekOrigin.Begin); // // MemoryStream output = new MemoryStream(); // // output.Seek(0, SeekOrigin.Begin); // // output.WriteFromStream(input.BaseStream, headerSize); // // output.WriteUInt32(0);// block count // // input.Seek(afterBlockTableOffset, SeekOrigin.Begin); // // output.WriteFromStream(input.BaseStream, 8); // // //check if has extra name list (don't know it's usage...) // // if ((packageFlags & 0x10000000) != 0) // // { // // long curPos = output.Position; // // output.WriteFromStream(input.BaseStream, nameOffset - curPos); // // } // // //decompress blocks in parallel // // var tasks = new Task<byte[]>[blockCount]; // // var uncompressedOffsets = new uint[blockCount]; // // for (int i = 0; i < blockCount; i++) // // { // // input.Seek(headBlockOff, SeekOrigin.Begin); // // uncompressedOffsets[i] = input.ReadUInt32(); // // var uncompressedSize = input.ReadUInt32(); // // var compressedOffset = input.ReadUInt32(); // // var compressedSize = input.ReadUInt32(); // // headBlockOff = (int)input.Position; // // var buff = new byte[compressedSize]; // // input.Seek(compressedOffset, SeekOrigin.Begin); // // input.Read(buff, 0, buff.Length); // // AmaroK86.MassEffect3.ZlibBlock.ZBlock.Decompress(buff, i); // // //tasks[i] = AmaroK86.MassEffect3.ZlibBlock.ZBlock.Decompress(buff, i); // // } // // Task.WaitAll(tasks); // // for (int i = 0; i < blockCount; i++) // // { // // output.Seek(uncompressedOffsets[i], SeekOrigin.Begin); // // output.WriteFromBuffer(tasks[i].Result); // // } // // //Do not change the IsCompressed bit as it will not accurately reflect the state of the file on disk. // // //output.Seek(packageFlagsOffset, SeekOrigin.Begin); // // //output.WriteUInt32(packageFlags & ~0x02000000u, ); //Mark file as decompressed. // // return output; // //} /// <summary> /// Decompresses a fully compressed package file. These only occur on console platforms. /// </summary> /// <param name="rawInput">Input stream to read from</param> /// <param name="compressionType">Known compression type of package. If this is not known, it will attempt to be determined, and this variable will be updated.</param> /// <returns></returns> public static MemoryStream DecompressFullyCompressedPackage(EndianReader rawInput, ref UnrealPackageFile.CompressionType compressionType) { rawInput.Position = 0; var magic = rawInput.ReadInt32(); var blockSize = rawInput.ReadInt32(); var compressedSize = rawInput.ReadInt32(); var decompressedSize = rawInput.ReadInt32(); var blockCount = 0; if (decompressedSize < blockSize) { blockCount = 1; } else { // Calculate the number of blocks blockCount = decompressedSize / blockSize; if (decompressedSize % blockSize != 0) { blockCount++; //Add one to decompress the final data } } MemoryStream outStream = new MemoryStream(); List <(int blockCompressedSize, int blockDecompressedSize)> blockTable = new List <(int blockCompressedSize, int blockDecompressedSize)>(); // Read Block Table int i = 0; while (i < blockCount) { blockTable.Add((rawInput.ReadInt32(), rawInput.ReadInt32())); i++; } int index = 0; foreach (var btInfo in blockTable) { // Decompress //Debug.WriteLine($"Decompressing data at 0x{raw.Position:X8}"); var datain = rawInput.ReadToBuffer(btInfo.blockCompressedSize); if (compressionType == UnrealPackageFile.CompressionType.None) { // We have to determine if it's LZMA or LZX based on first few bytes if (datain[0] == 0x5D && BitConverter.ToInt32(datain, 1) == 0x10000) { // This is LZMA header Debug.WriteLine("Fully compressed package: Detected LZMA compression"); compressionType = UnrealPackageFile.CompressionType.LZMA; } else { Debug.WriteLine("Fully compressed package: Detected LZX compression"); compressionType = UnrealPackageFile.CompressionType.LZX; } } var dataout = new byte[btInfo.blockDecompressedSize]; if (dataout.Length == datain.Length) { // WiiU SFXGame has weird case where one single block has same sizes and does not have LZMA compression flag for some reason dataout = datain; } else { switch (compressionType) { case UnrealPackageFile.CompressionType.LZO: if (LZO2.Decompress(datain, (uint)datain.Length, dataout) != btInfo.blockDecompressedSize) { throw new Exception("LZO decompression failed!"); } break; case UnrealPackageFile.CompressionType.Zlib: if (Zlib.Decompress(datain, (uint)datain.Length, dataout) != btInfo.blockDecompressedSize) { throw new Exception("Zlib decompression failed!"); } break; case UnrealPackageFile.CompressionType.LZMA: dataout = LZMA.Decompress(datain, (uint)btInfo.blockDecompressedSize); if (dataout.Length != btInfo.blockDecompressedSize) { throw new Exception("LZMA decompression failed!"); } break; case UnrealPackageFile.CompressionType.LZX: if (LZX.Decompress(datain, (uint)datain.Length, dataout) != 0) { throw new Exception("LZX decompression failed!"); } break; default: throw new Exception("Unknown compression type for this package."); } } index++; outStream.WriteFromBuffer(dataout); } outStream.Position = 0; return(outStream); }
/// <summary> /// Decompresses a compressed package. Works with ME1/ME2/ME3/UDK /// </summary> /// <param name="raw"></param> /// <param name="compressionInfoOffset"></param> /// <param name="compressionType"></param> /// <param name="NumChunks"></param> /// <param name="game"></param> /// <param name="platform"></param> /// <returns></returns> public static MemoryStream DecompressPackage(EndianReader raw, long compressionInfoOffset, UnrealPackageFile.CompressionType compressionType = UnrealPackageFile.CompressionType.None, int NumChunks = 0, MEGame game = MEGame.Unknown, GamePlatform platform = GamePlatform.PC) { raw.BaseStream.JumpTo(compressionInfoOffset); if (compressionType == UnrealPackageFile.CompressionType.None) { compressionType = (UnrealPackageFile.CompressionType)raw.ReadUInt32(); } if (NumChunks == 0) { NumChunks = raw.ReadInt32(); } var Chunks = new List <Chunk>(); var chunkTableStart = raw.Position; //DebugOutput.PrintLn("Reading chunk headers..."); for (int i = 0; i < NumChunks; i++) { Chunk c = new Chunk { uncompressedOffset = raw.ReadInt32(), uncompressedSize = raw.ReadInt32(), compressedOffset = raw.ReadInt32(), compressedSize = raw.ReadInt32() }; c.Compressed = new byte[c.compressedSize]; c.Uncompressed = new byte[c.uncompressedSize]; Chunks.Add(c); } //DebugOutput.PrintLn("\tRead Chunks..."); int count = 0; for (int i = 0; i < Chunks.Count; i++) { Chunk c = Chunks[i]; //Debug.WriteLine($"Compressed offset at {c.compressedOffset:X8}"); raw.Seek(c.compressedOffset, SeekOrigin.Begin); raw.Read(c.Compressed, 0, c.compressedSize); ChunkHeader h = new ChunkHeader { magic = EndianReader.ToInt32(c.Compressed, 0, raw.Endian), // must force block size for ME1 xbox cause in place of block size it seems to list package tag again which breaks loads of things blocksize = (platform == GamePlatform.Xenon && game == MEGame.ME1) ? 0x20000 : EndianReader.ToInt32(c.Compressed, 4, raw.Endian), compressedsize = EndianReader.ToInt32(c.Compressed, 8, raw.Endian), uncompressedsize = EndianReader.ToInt32(c.Compressed, 12, raw.Endian) }; if (h.magic != -1641380927) { throw new FormatException("Chunk magic number incorrect"); } //DebugOutput.PrintLn("Chunkheader read: Magic = " + h.magic + ", Blocksize = " + h.blocksize + ", Compressed Size = " + h.compressedsize + ", Uncompressed size = " + h.uncompressedsize); int pos = 16; int blockCount = h.uncompressedsize / h.blocksize; if (h.uncompressedsize % h.blocksize != 0) { blockCount++; } var BlockList = new List <Block>(); //DebugOutput.PrintLn("\t\t" + count + " Read Blockheaders..."); for (int j = 0; j < blockCount; j++) { Block b = new Block { compressedsize = EndianReader.ToInt32(c.Compressed, pos, raw.Endian), uncompressedsize = EndianReader.ToInt32(c.Compressed, pos + 4, raw.Endian) }; //DebugOutput.PrintLn("Block " + j + ", compressed size = " + b.compressedsize + ", uncompressed size = " + b.uncompressedsize); pos += 8; BlockList.Add(b); } int outpos = 0; int blocknum = 0; //DebugOutput.PrintLn("\t\t" + count + " Read and decompress Blocks..."); foreach (Block b in BlockList) { //Debug.WriteLine("Decompressing block " + blocknum); var datain = new byte[b.compressedsize]; var dataout = new byte[b.uncompressedsize]; Buffer.BlockCopy(c.Compressed, pos, datain, 0, b.compressedsize); //for (int j = 0; j < b.compressedsize; j++) // datain[j] = c.Compressed[pos + j]; pos += b.compressedsize; switch (compressionType) { case UnrealPackageFile.CompressionType.LZO: { if (LZO2.Decompress(datain, (uint)datain.Length, dataout) != b.uncompressedsize) { throw new Exception("LZO decompression failed!"); } break; } case UnrealPackageFile.CompressionType.Zlib: { if (Zlib.Decompress(datain, (uint)datain.Length, dataout) != b.uncompressedsize) { throw new Exception("Zlib decompression failed!"); } break; } case UnrealPackageFile.CompressionType.LZMA: dataout = LZMA.Decompress(datain, (uint)b.uncompressedsize); if (dataout.Length != b.uncompressedsize) { throw new Exception("LZMA decompression failed!"); } break; case UnrealPackageFile.CompressionType.LZX: if (LZX.Decompress(datain, (uint)datain.Length, dataout) != 0) { throw new Exception("LZX decompression failed!"); } break; default: throw new Exception("Unknown compression type for this package."); } for (int j = 0; j < b.uncompressedsize; j++) { c.Uncompressed[outpos + j] = dataout[j]; } outpos += b.uncompressedsize; blocknum++; } c.header = h; c.blocks = BlockList; count++; Chunks[i] = c; } MemoryStream result = new MemoryStream(); foreach (Chunk c in Chunks) { result.Seek(c.uncompressedOffset, SeekOrigin.Begin); result.WriteFromBuffer(c.Uncompressed); } // Reattach the original header result.Position = 0; raw.Position = 0; raw.BaseStream.CopyToEx(result, Chunks.MinBy(x => x.uncompressedOffset).uncompressedOffset); // Copy the header in // Does header need adjusted here to be accurate? // Do we change it to show decompressed, as the actual state, or the state of what it was on disk? return(result); }
/// <summary> /// Extracts data from a Brutal Legend package. /// </summary> /// <param name="HeaderFile">The *.~h Header file path</param> /// <param name="DataFile">The ~.~p Data file path</param> /// <param name="OutFolder">The output folder</param> /// <returns>False if header file is invalid, true otherwise</returns> public static bool Extract(string HeaderFile, string DataFile, string OutFolder) { FileStream Header = new FileStream(HeaderFile, FileMode.Open); FileStream Data = new FileStream(DataFile, FileMode.Open); EndianBinaryReader Reader = new EndianBinaryReader(Header, Endian.Big); if (StringUtilities.ReadASCIIString(Header, 4) != "dfpf") { return(false); } Header.Seek(0x14, SeekOrigin.Begin); uint StringsTableOffset = Reader.ReadUInt32(); uint StringsTableFlags = Reader.ReadUInt32(); uint StringsTableLength = Reader.ReadUInt32(); uint FilesCount = Reader.ReadUInt32(); Header.Seek(0x18, SeekOrigin.Current); uint FilesTableOffset = Reader.ReadUInt32(); for (int Index = 0; Index < FilesCount; Index++) { Header.Seek(FilesTableOffset + Index * 0x10, SeekOrigin.Begin); //Lengths uint DecompressedLength = Reader.ReadUInt24(); uint LengthDifference = (uint)(Reader.ReadUInt16() << 1) | ((DecompressedLength & 1) << 17); //Probably for "obfuscation" uint CompressedLength = Reader.ReadUInt24(); LengthDifference |= CompressedLength >> 23; DecompressedLength = (DecompressedLength >> 1) + LengthDifference; CompressedLength = (CompressedLength & 0x7fffff) >> 1; //Offsets uint DataOffset = Reader.ReadUInt24() << 5; byte DataFormat = Reader.ReadByte(); DataOffset |= (uint)(DataFormat & 0xf8) >> 3; DataFormat &= 7; uint NameOffset = (Reader.ReadUInt24() >> 3) + StringsTableOffset; byte Flags = Reader.ReadByte(); Header.Seek(NameOffset, SeekOrigin.Begin); string FileName = StringUtilities.ReadASCIIString(Header); if (OnStatusReport != null) { BrutalStatusReport Report = new BrutalStatusReport(); Report.Status = "Extracting " + FileName + "..."; Report.ProcessedFiles = Index; Report.TotalFiles = (int)FilesCount; OnStatusReport(null, Report); } Data.Seek(DataOffset, SeekOrigin.Begin); byte[] Buffer = new byte[CompressedLength]; Data.Read(Buffer, 0, Buffer.Length); ICompression Decompressor; switch ((CompressionType)((Flags >> 1) & 3)) { case CompressionType.None: Decompressor = new NoCompression(); break; case CompressionType.ZLib: Decompressor = new ZLib(); break; case CompressionType.LZX: Decompressor = new LZX(); break; default: throw new Exception("Unknown compression!"); } Buffer = Decompressor.Decompress(Buffer, DecompressedLength); string FullName = Path.Combine(OutFolder, FileName); string DirName = Path.GetDirectoryName(FullName); if (!Directory.Exists(DirName)) { Directory.CreateDirectory(DirName); } File.WriteAllBytes(FullName + ".bin", Buffer); } Header.Close(); Data.Close(); return(true); }