public byte[] CompressChunk(Chunk chunk) { int numBlocks = (chunk.Uncompressed.Length + maxBlockSize - 1) / maxBlockSize; if (numBlocks > 8) { throw new FormatException("Maximum block number exceeded"); } ChunkHeader head = new ChunkHeader(); head.magic = -1641380927; head.blocksize = maxBlockSize; head.uncompressedsize = chunk.Uncompressed.Length; int pos = 0; MemoryStream mem = new MemoryStream(); List <Block> blockList = new List <Block>(); int startData = 16 + 8 * numBlocks; mem.Seek(startData, SeekOrigin.Begin); for (int i = 0; i < numBlocks; i++) { Block block = new Block(); byte[] result, temp; if (i != numBlocks - 1) { block.uncompressedsize = maxBlockSize; temp = new byte[maxBlockSize]; } else { block.uncompressedsize = head.uncompressedsize - pos; temp = new byte[block.uncompressedsize]; } Buffer.BlockCopy(chunk.Uncompressed, pos, temp, 0, temp.Length); result = LZO2.Compress(temp); if (result.Length == 0) { throw new Exception("LZO compression error!"); } block.compressedsize = result.Length; mem.WriteBytes(result); blockList.Add(block); pos += maxBlockSize; } head.compressedsize = (int)mem.Length; mem.Seek(0, SeekOrigin.Begin); mem.WriteValueS32(head.magic); mem.WriteValueS32(head.blocksize); mem.WriteValueS32(head.compressedsize); mem.WriteValueS32(head.uncompressedsize); foreach (Block block in blockList) { mem.WriteValueS32(block.compressedsize); mem.WriteValueS32(block.uncompressedsize); } return(mem.ToArray()); }
/// <summary> /// Takes regular image data and returns it in a compressed form ready for archiving /// </summary> /// <param name="data"></param> /// <returns></returns> public byte[] CompressTex(byte[] data) { int chunkSize = 131072; //Set at this stage. Easy to change later int noChunks = (data.Length + chunkSize - 1) / chunkSize; CompressedChunkBlock[] chunks = new CompressedChunkBlock[noChunks]; int pos = 0; for (int i = 0; i < noChunks; i++) { if (data.Length - (pos + chunkSize) < 0) { chunks[i].uncSize = data.Length - pos; chunks[i].rawData = new byte[chunks[i].uncSize]; Buffer.BlockCopy(data, pos, chunks[i].rawData, 0, chunks[i].uncSize); pos += chunks[i].uncSize; } else { chunks[i].uncSize = chunkSize; chunks[i].rawData = new byte[chunkSize]; Buffer.BlockCopy(data, pos, chunks[i].rawData, 0, chunkSize); pos += chunks[i].uncSize; } } pos = 0; CompressedChunkBlock[] newChunks = new CompressedChunkBlock[noChunks]; for (int i = 0; i < noChunks; i++) { newChunks[i].rawData = LZO2.Compress(chunks[i].rawData); if (newChunks[i].rawData.Length == 0) { throw new Exception("LZO compression failed!"); } newChunks[i].cprSize = newChunks[i].rawData.Length; newChunks[i].uncSize = chunks[i].uncSize; pos += newChunks[i].cprSize; } byte[] result; using (MemoryStream stream = new MemoryStream()) { byte[] magic = { 0xC1, 0x83, 0x2A, 0x9E, 0x00, 0x00, 0x02, 0x00 }; pos = Gibbed.IO.NumberHelpers.LittleEndian(pos); BinaryWriter bin = new BinaryWriter(stream); bin.Write(magic); bin.Write(pos); pos = Gibbed.IO.NumberHelpers.LittleEndian(data.Length); //unc size bin.Write(pos); for (int i = 0; i < noChunks; i++) { int uncSize = newChunks[i].uncSize; int cprSize = newChunks[i].cprSize; uncSize = Gibbed.IO.NumberHelpers.LittleEndian(uncSize); cprSize = Gibbed.IO.NumberHelpers.LittleEndian(cprSize); bin.Write(cprSize); bin.Write(uncSize); } for (int i = 0; i < noChunks; i++) { bin.Write(newChunks[i].rawData); } result = stream.ToArray(); } return(result); }
public static byte[] CompressTexture(byte[] inputData, StorageTypes type) { using (MemoryStream ouputStream = new MemoryStream()) { uint compressedSize = 0; uint dataBlockLeft = (uint)inputData.Length; uint newNumBlocks = ((uint)inputData.Length + maxBlockSize - 1) / maxBlockSize; List <ChunkBlock> blocks = new List <ChunkBlock>(); using (MemoryStream inputStream = new MemoryStream(inputData)) { // skip blocks header and table - filled later ouputStream.Seek(SizeOfChunk + SizeOfChunkBlock * newNumBlocks, SeekOrigin.Begin); for (int b = 0; b < newNumBlocks; b++) { ChunkBlock block = new ChunkBlock(); block.uncomprSize = Math.Min(maxBlockSize, dataBlockLeft); dataBlockLeft -= block.uncomprSize; block.uncompressedBuffer = inputStream.ReadToBuffer(block.uncomprSize); blocks.Add(block); } } Parallel.For(0, blocks.Count, b => { ChunkBlock block = blocks[b]; if (type == StorageTypes.extLZO || type == StorageTypes.pccLZO) { block.compressedBuffer = LZO2.Compress(block.uncompressedBuffer); } else if (type == StorageTypes.extZlib || type == StorageTypes.pccZlib) { block.compressedBuffer = Zlib.Compress(block.uncompressedBuffer); } else { throw new Exception("Compression type not expected!"); } if (block.compressedBuffer.Length == 0) { throw new Exception("Compression failed!"); } block.comprSize = (uint)block.compressedBuffer.Length; blocks[b] = block; }); for (int b = 0; b < blocks.Count; b++) { ChunkBlock block = blocks[b]; ouputStream.Write(block.compressedBuffer, 0, (int)block.comprSize); compressedSize += block.comprSize; } ouputStream.SeekBegin(); ouputStream.WriteUInt32(textureTag); ouputStream.WriteUInt32(maxBlockSize); ouputStream.WriteUInt32(compressedSize); ouputStream.WriteInt32(inputData.Length); foreach (ChunkBlock block in blocks) { ouputStream.WriteUInt32(block.comprSize); ouputStream.WriteUInt32(block.uncomprSize); } return(ouputStream.ToArray()); } }