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; import org.jnode.fs.util.FSUtils; /** * ZLIB compressed data stored off in the file's resource fork. * * @author Luke Quinane */ public class ZlibForkCompression implements HfsPlusCompression { /** * The zlib fork compression chunk size. */ private static final int ZLIB_FORK_CHUNK_SIZE = 0x10000; /** * The HFS+ file. */ private final HfsPlusFile file; /** * The detail of the fork compression if it is being used. */ private ZlibForkCompressionDetails zlibForkCompressionDetails; /** * Creates a new decompressor. * * @param file the file to read from. */ public ZlibForkCompression(HfsPlusFile file) { this.file = file; } @Override public void read(HfsPlusFileSystem fs, long fileOffset, ByteBuffer dest) throws IOException { if (zlibForkCompressionDetails == null) { zlibForkCompressionDetails = new ZlibForkCompressionDetails(fs, file.getCatalogFile().getResources()); } while (dest.remaining() > 0) { int chunk = FSUtils.checkedCast(fileOffset / ZLIB_FORK_CHUNK_SIZE); int chunkLength = zlibForkCompressionDetails.getChunkLength(chunk); long chunkOffset = zlibForkCompressionDetails.getChunkOffset(chunk); ByteBuffer compressed = ByteBuffer.allocate(chunkLength); file.getCatalogFile().getResources().read(fs, chunkOffset, compressed); ByteBuffer uncompressed = ByteBuffer.allocate(ZLIB_FORK_CHUNK_SIZE); if (compressed.array()[0] == (byte) 0xff) { // 0xff seems to be a marker for uncompressed data. Skip this byte any just copy the data out. 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); } } int copySize = Math.min(dest.remaining(), uncompressed.remaining()); uncompressed.position((int) fileOffset % ZLIB_FORK_CHUNK_SIZE); uncompressed.limit(Math.min(uncompressed.capacity(), uncompressed.position() + dest.remaining())); dest.put(uncompressed); fileOffset += copySize; } } /** * The factory for this compression type. */ public static class Factory implements HfsPlusCompressionFactory { @Override public HfsPlusCompression createDecompressor(HfsPlusFile file, AttributeData attributeData, DecmpfsDiskHeader decmpfsDiskHeader) { return new ZlibForkCompression(file); } } }