// Near Infinity - An Infinity Engine Browser and Editor // Copyright (C) 2001 - 2005 Jon Olav Hauglid // See LICENSE.txt for license information package org.infinity.resource.graphics; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.zip.DataFormatException; import java.util.zip.Deflater; import java.util.zip.Inflater; import org.infinity.util.ArrayUtil; import org.infinity.util.DynamicArray; import org.infinity.util.io.StreamUtils; public final class Compressor { /** * Compresses the data of the specified ByteBuffer object and creates a simple header. * @param buffer Contains the data to compress. * @param signature Signature ID for the header. * @param version Version ID for the header. * @return The compressed data including header as {@link ByteBuffer}. */ public static ByteBuffer compress(ByteBuffer buffer, String signature, String version) { ByteBuffer retVal = null; if (buffer != null && buffer.remaining() > 0 && signature.length() == 4 && version.length() == 4) { buffer.position(0); byte[] data = new byte[buffer.remaining()]; buffer.put(data); data = compress(data, 0, data.length, false); if (data != null) { retVal = StreamUtils.getByteBuffer(12 + data.length); retVal.put(signature.getBytes()); retVal.put(version.getBytes()); retVal.putInt(buffer.limit()); retVal.put(data); retVal.position(0); } } return retVal; } /** * Compresses the specified data and creates a simple header. * @param data The data to compress * @param signature Signature ID for the header. * @param version Version ID for the header. * @return The compressed data including header. */ public static byte[] compress(byte data[], String signature, String version) { byte header[] = ArrayUtil.mergeArrays(signature.getBytes(), version.getBytes()); header = ArrayUtil.mergeArrays(header, DynamicArray.convertInt(data.length)); byte[] result = compress(data, 0, data.length, false); if (result != null) { byte[] output = new byte[header.length + result.length]; System.arraycopy(header, 0, output, 0, header.length); System.arraycopy(result, 0, output, header.length, result.length); return output; } return null; } /** * Compresses the specified data without creating a header. * @param data The data block to compress. * @param ofs Start offset of the data. * @param len Length of data to compress in bytes. * @param prependSize If {@code true} the uncompressed size will be written to the * output block right before the compressed data. * @return The compressed data as byte array. */ public static byte[] compress(byte[] data, int ofs, int len, boolean prependSize) { byte[] result = null; if (data != null && ofs >= 0 && ofs < data.length) { if (ofs + len > data.length) len = data.length - ofs; int dstOfs = 0; result = new byte[len*2]; if (prependSize) { dstOfs = 4; DynamicArray.putInt(result, 0, len); } Deflater deflater = new Deflater(); deflater.setInput(data, ofs, len); deflater.finish(); int clength = deflater.deflate(result, dstOfs, result.length - dstOfs); return Arrays.copyOfRange(result, 0, clength + dstOfs); } return result; } public static ByteBuffer decompress(ByteBuffer buffer) throws IOException { return decompress(buffer, 8); } public static ByteBuffer decompress(ByteBuffer buffer, int offset) throws IOException { ByteBuffer retVal = null; if (buffer != null && offset < buffer.limit()) { byte[] src = StreamUtils.toArray(buffer); byte[] dst = decompress(src, offset); retVal = StreamUtils.getByteBuffer(dst); } return retVal; } public static byte[] decompress(byte buffer[]) throws IOException { return decompress(buffer, 8); } public static byte[] decompress(byte buffer[], int ofs) throws IOException { Inflater inflater = new Inflater(); byte result[] = new byte[DynamicArray.getInt(buffer, ofs)]; ofs += 4; inflater.setInput(buffer, ofs, buffer.length - ofs); try { inflater.inflate(result); } catch (DataFormatException e) { throw new IOException(); } inflater.reset(); return result; } private Compressor(){} }