package org.jnode.fs.hfsplus.compression; import java.io.IOException; import java.nio.ByteBuffer; import java.util.zip.DataFormatException; import java.util.zip.Inflater; import org.jnode.fs.hfsplus.HfsPlusFile; import org.jnode.fs.hfsplus.HfsPlusFileSystem; import org.jnode.fs.hfsplus.attributes.AttributeData; /** * Attribute stored ZLIB compressed data. * * @author Luke Quinane */ public class AttributeZlibCompression implements HfsPlusCompression { /** * The attribute data to read in. */ private final AttributeData attributeData; /** * The decompression header. */ private final DecmpfsDiskHeader decmpfsDiskHeader; /** * The uncompressed copy of the data. */ private ByteBuffer uncompressed; /** * Creates a new instance. * * @param attributeData the attribute data to read from. * @param decmpfsDiskHeader the decompression header. */ public AttributeZlibCompression(AttributeData attributeData, DecmpfsDiskHeader decmpfsDiskHeader) { this.attributeData = attributeData; this.decmpfsDiskHeader = decmpfsDiskHeader; } @Override public void read(HfsPlusFileSystem fs, long fileOffset, ByteBuffer dest) throws IOException { if (uncompressed == null) { ByteBuffer compressed = ByteBuffer.allocate((int) attributeData.getSize() - DecmpfsDiskHeader.LENGTH); attributeData.read(fs, DecmpfsDiskHeader.LENGTH, compressed); uncompressed = ByteBuffer.allocate((int) decmpfsDiskHeader.getUncompressedSize()); if (compressed.array()[0] == (byte) 0xff) { // 0xff seems to be a marker for uncompressed data. Skip this byte any just copy the data out. // I have no idea why they didn't use 'CMP_Type1' which is meant for uncompressed data according to the // headers compressed.position(1); uncompressed.put(compressed); } else { Inflater inflater = new Inflater(); inflater.setInput(compressed.array()); try { inflater.inflate(uncompressed.array()); } catch (DataFormatException e) { throw new IllegalStateException("Error uncompressing data", e); } } } uncompressed.position((int) fileOffset); uncompressed.limit(Math.min(uncompressed.capacity(), uncompressed.position() + dest.remaining())); dest.put(uncompressed); } /** * The factory for this compression type. */ public static class Factory implements HfsPlusCompressionFactory { @Override public HfsPlusCompression createDecompressor(HfsPlusFile file, AttributeData attributeData, DecmpfsDiskHeader decmpfsDiskHeader) { return new AttributeZlibCompression(attributeData, decmpfsDiskHeader); } } }