private static void HandleDataBlock(byte[] chunkBuffer, int index, BLTEChunkInfo chunk, MemoryStream chunkResult) { using (var chunkreader = new BinaryReader(new MemoryStream(chunkBuffer))) { var mode = chunkreader.ReadChar(); switch (mode) { case 'N': // none chunkResult.Write(chunkreader.ReadBytes(chunk.actualSize), 0, chunk.actualSize); //read actual size because we already read the N from chunkreader break; case 'Z': // zlib using (var stream = new MemoryStream(chunkreader.ReadBytes(chunk.inFileSize - 1), 2, chunk.inFileSize - 3)) using (var ds = new DeflateStream(stream, CompressionMode.Decompress)) { ds.CopyTo(chunkResult); } break; case 'E': // encrypted byte[] decrypted = new byte[chunkBuffer.Length - 15]; decrypted[0] = Convert.ToByte('N'); try { decrypted = Decrypt(chunkBuffer, index); } catch (KeyNotFoundException e) { Console.WriteLine(e.Message); chunkResult.Write(new byte[chunk.actualSize], 0, chunk.actualSize); break; } // Override inFileSize with decrypted length because it now differs from original encrypted chunk.inFileSize which breaks decompression chunk.inFileSize = decrypted.Length; //Console.WriteLine("Decrypted chunk size is " + chunk.inFileSize); HandleDataBlock(decrypted, index, chunk, chunkResult); break; case 'F': // frame default: throw new Exception("Unsupported mode " + mode + "!"); } } }
private static void HandleDataBlock(byte[] data, int index, BLTEChunkInfo chunk, MemoryStream result) { switch (data[0]) { case 0x4E: // N (no compression) result.Write(data, 1, data.Length - 1); break; case 0x5A: // Z (zlib, compressed) using (var stream = new MemoryStream(data, 3, chunk.compSize - 3)) using (var ds = new DeflateStream(stream, CompressionMode.Decompress)) { ds.CopyTo(result); } break; case 0x45: // E (encrypted) byte[] decrypted = new byte[data.Length - 15]; decrypted[0] = 0x4E; // N try { decrypted = Decrypt(data, index); } catch (KeyNotFoundException e) { Console.WriteLine(e.Message); result.Write(new byte[chunk.decompSize], 0, chunk.decompSize); break; } // Override inFileSize with decrypted length because it now differs from original encrypted chunk.compSize which breaks decompression chunk.compSize = decrypted.Length; HandleDataBlock(decrypted, index, chunk, result); break; case 0x46: // F (frame) default: throw new Exception("Unsupported mode " + data[0].ToString("X") + "!"); } }
public static byte[] Parse(byte[] content, bool verify = true) { using (var result = new MemoryStream()) using (var bin = new BinaryReader(new MemoryStream(content))) { if (bin.ReadUInt32() != 0x45544c42) { throw new Exception("Not a BLTE file"); } var blteSize = bin.ReadUInt32(true); BLTEChunkInfo[] chunkInfos; if (blteSize == 0) { chunkInfos = new BLTEChunkInfo[1]; chunkInfos[0].isFullChunk = false; chunkInfos[0].inFileSize = Convert.ToInt32(bin.BaseStream.Length - bin.BaseStream.Position); chunkInfos[0].actualSize = Convert.ToInt32(bin.BaseStream.Length - bin.BaseStream.Position); chunkInfos[0].checkSum = new byte[16];; } else { var bytes = bin.ReadBytes(4); var chunkCount = bytes[1] << 16 | bytes[2] << 8 | bytes[3] << 0; var supposedHeaderSize = 24 * chunkCount + 12; if (supposedHeaderSize != blteSize) { throw new Exception("Invalid header size!"); } if (supposedHeaderSize > bin.BaseStream.Length) { throw new Exception("Not enough data"); } chunkInfos = new BLTEChunkInfo[chunkCount]; for (int i = 0; i < chunkCount; i++) { chunkInfos[i].isFullChunk = true; chunkInfos[i].inFileSize = bin.ReadInt32(true); chunkInfos[i].actualSize = bin.ReadInt32(true); chunkInfos[i].checkSum = new byte[16]; chunkInfos[i].checkSum = bin.ReadBytes(16); } } for (var index = 0; index < chunkInfos.Count(); index++) { var chunk = chunkInfos[index]; if (chunk.inFileSize > bin.BaseStream.Length) { throw new Exception("Trying to read more than is available!"); } var chunkBuffer = bin.ReadBytes(chunk.inFileSize); if (verify) { var hasher = MD5.Create(); var md5sum = hasher.ComputeHash(chunkBuffer); if (chunk.isFullChunk && BitConverter.ToString(md5sum) != BitConverter.ToString(chunk.checkSum)) { throw new Exception("MD5 checksum mismatch on BLTE chunk! Sum is " + BitConverter.ToString(md5sum).Replace("-", "") + " but is supposed to be " + BitConverter.ToString(chunk.checkSum).Replace("-", "")); } } using (var chunkResult = new MemoryStream()) { HandleDataBlock(chunkBuffer, index, chunk, chunkResult); var chunkres = chunkResult.ToArray(); if (chunk.isFullChunk && chunkres.Length != chunk.actualSize) { throw new Exception("Decoded result is wrong size!"); } result.Write(chunkres, 0, chunkres.Length); } } foreach (var chunk in chunkInfos) { if (chunk.inFileSize > bin.BaseStream.Length) { throw new Exception("Trying to read more than is available!"); } else { bin.BaseStream.Position += chunk.inFileSize; } } return(result.ToArray()); } }
public static byte[] Parse(byte[] content) { using (var result = new MemoryStream()) using (var bin = new BinaryReader(new MemoryStream(content))) { if (bin.ReadUInt32() != 0x45544c42) { throw new Exception("Not a BLTE file"); } var blteSize = bin.ReadUInt32(true); BLTEChunkInfo[] chunkInfos; if (blteSize == 0) { // These are always uncompressed chunkInfos = new BLTEChunkInfo[1]; chunkInfos[0].isFullChunk = false; chunkInfos[0].compSize = Convert.ToInt32(bin.BaseStream.Length - 8); chunkInfos[0].decompSize = Convert.ToInt32(bin.BaseStream.Length - 8 - 1); chunkInfos[0].checkSum = new byte[16]; } else { var bytes = bin.ReadBytes(4); var chunkCount = bytes[1] << 16 | bytes[2] << 8 | bytes[3] << 0; var supposedHeaderSize = 24 * chunkCount + 12; if (supposedHeaderSize != blteSize) { throw new Exception("Invalid header size!"); } if (supposedHeaderSize > bin.BaseStream.Length) { throw new Exception("Not enough data"); } chunkInfos = new BLTEChunkInfo[chunkCount]; for (int i = 0; i < chunkCount; i++) { chunkInfos[i].isFullChunk = true; chunkInfos[i].compSize = bin.ReadInt32(true); chunkInfos[i].decompSize = bin.ReadInt32(true); chunkInfos[i].checkSum = new byte[16]; chunkInfos[i].checkSum = bin.ReadBytes(16); } } for (var index = 0; index < chunkInfos.Count(); index++) { var chunk = chunkInfos[index]; if (chunk.compSize > (bin.BaseStream.Length - bin.BaseStream.Position)) { throw new Exception("Trying to read more than is available!"); } HandleDataBlock(bin.ReadBytes(chunk.compSize), index, chunk, result); } return(result.ToArray()); } }