diff --git a/build.gradle b/build.gradle index 6c2e70e..ff87c5b 100644 --- a/build.gradle +++ b/build.gradle @@ -9,16 +9,6 @@ repositories { apply plugin: 'java' apply plugin: 'application' -ext { - sourceCompatibility = '1.8' - targetCompatibility = '1.8' - - swtVersion = "4.6.1" - junitVersion = "4.12" - antVersion = "1.8.2" - commonsLang3Version = "3.7" -} - mainClassName = 'com.webcodepro.applecommander.ui.AppleCommander' version "${version}" @@ -61,14 +51,18 @@ tasks.withType(Jar) { 'Implementation-Version': version } from('LICENSE', 'CONTRIB', 'TODO', 'VERSIONS') + doFirst { + // Pick and include ShrinkIt contents + from { configurations.runtime.collect { it.name.startsWith('ShrinkItArchive') ? zipTree(it) : 'fake' } } + } } dependencies { + compile "net.sf.applecommander:ShrinkItArchive:$shkVersion" compileOnly "org.apache.ant:ant:$antVersion" testCompile "junit:junit:$junitVersion" testCompile "org.apache.commons:commons-lang3:$commonsLang3Version" - } task acJar(type: Jar) { diff --git a/gradle.properties b/gradle.properties index 716c03b..af8fd29 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,3 +2,9 @@ # - Naming JAR file. # - The build will insert this into a file that is read at run time as well. version=1.4.0-BETA + +shkVersion=1.1.0 +swtVersion=4.6.1 +junitVersion=4.12 +antVersion=1.8.2 +commonsLang3Version=3.7 diff --git a/src/main/java/com/webcodepro/applecommander/storage/Disk.java b/src/main/java/com/webcodepro/applecommander/storage/Disk.java index fb01ffc..9d9883d 100644 --- a/src/main/java/com/webcodepro/applecommander/storage/Disk.java +++ b/src/main/java/com/webcodepro/applecommander/storage/Disk.java @@ -212,7 +212,7 @@ public class Disk { if (isSDK() || isSHK() || isBXY()) { // If we have an SDK, unpack it and send along the byte array // If we have a SHK, build a new disk and unpack the contents on to it - diskImage = com.webcodepro.shrinkit.Utilities.unpackSHKFile(filename, startBlocks); + diskImage = com.webcodepro.applecommander.util.ShrinkItUtilities.unpackSHKFile(filename, startBlocks); diskSize = diskImage.length; // Since we don't want to overwrite their shrinkit with a raw ProDOS image, // add a .po extension to it diff --git a/src/main/java/com/webcodepro/shrinkit/Utilities.java b/src/main/java/com/webcodepro/applecommander/util/ShrinkItUtilities.java similarity index 96% rename from src/main/java/com/webcodepro/shrinkit/Utilities.java rename to src/main/java/com/webcodepro/applecommander/util/ShrinkItUtilities.java index 3229c87..6fc04dc 100644 --- a/src/main/java/com/webcodepro/shrinkit/Utilities.java +++ b/src/main/java/com/webcodepro/applecommander/util/ShrinkItUtilities.java @@ -1,4 +1,4 @@ -package com.webcodepro.shrinkit; +package com.webcodepro.applecommander.util; /* * Copyright (C) 2012 by David Schmidt @@ -34,7 +34,9 @@ import com.webcodepro.applecommander.storage.physical.ByteArrayImageLayout; import com.webcodepro.applecommander.storage.physical.ImageOrder; import com.webcodepro.applecommander.storage.physical.ProdosOrder; import com.webcodepro.applecommander.ui.ac.Name; -import com.webcodepro.applecommander.util.TextBundle; +import com.webcodepro.shrinkit.HeaderBlock; +import com.webcodepro.shrinkit.NuFileArchive; +import com.webcodepro.shrinkit.ThreadRecord; import com.webcodepro.shrinkit.io.LittleEndianByteInputStream; /** @@ -42,7 +44,7 @@ import com.webcodepro.shrinkit.io.LittleEndianByteInputStream; * * @author david__schmidt at users.sourceforge.net */ -public class Utilities +public class ShrinkItUtilities { /** * Interpret a NuFile/NuFX/Shrinkit archive as a full disk image. diff --git a/src/main/java/com/webcodepro/shrinkit/CRC16.java b/src/main/java/com/webcodepro/shrinkit/CRC16.java deleted file mode 100644 index 7ca20d6..0000000 --- a/src/main/java/com/webcodepro/shrinkit/CRC16.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.webcodepro.shrinkit; - -import java.util.zip.Checksum; - -/** - * Crc16: Calculate 16-bit Cyclic Redundancy Check. - * License: GPL, incorporated by reference. - * - * @author John B. Matthews - */ -public class CRC16 implements Checksum { - - /** CCITT polynomial: x^16 + x^12 + x^5 + 1 -> 0x1021 (1000000100001) */ - private static final int poly = 0x1021; - private static final int[] table = new int[256]; - private int value = 0; - - static { // initialize static lookup table - for (int i = 0; i < 256; i++) { - int crc = i << 8; - for (int j = 0; j < 8; j++) { - if ((crc & 0x8000) == 0x8000) { - crc = (crc << 1) ^ poly; - } else { - crc = (crc << 1); - } - } - table[i] = crc & 0xffff; - } - } - - /** - * Update 16-bit CRC. - * - * @param crc starting CRC value - * @param bytes input byte array - * @param off start offset to data - * @param len number of bytes to process - * @return 16-bit unsigned CRC - */ - private int update(int crc, byte[] bytes, int off, int len) { - for (int i = off; i < (off + len); i++) { - int b = (bytes[i] & 0xff); - crc = (table[((crc >> 8) & 0xff) ^ b] ^ (crc << 8)) & 0xffff; - } - return crc; - } - - public static int[] getTable() { - return table; - } - - public long getValue() { - return value; - } - - public void reset() { - value = 0; - } - - /** - * Update 16-bit CRC. - * - * @param b input byte - */ - public void update(int b) { - byte[] ba = { (byte) (b & 0xff) }; - value = update(value, ba, 0, 1); - } - - /** - * Update 16-bit CRC. - * - * @param b input byte array - */ - public void update(byte[] b) { - value = update(value, b, 0, b.length); - } - - /** - * Update 16-bit CRC. - * - * @param b input byte array - * @param off starting offset to data - * @param len number of bytes to process - */ - public void update(byte[] b, int off, int len) { - value = update(value, b, off, len); - } - -} \ No newline at end of file diff --git a/src/main/java/com/webcodepro/shrinkit/HeaderBlock.java b/src/main/java/com/webcodepro/shrinkit/HeaderBlock.java deleted file mode 100644 index a76106a..0000000 --- a/src/main/java/com/webcodepro/shrinkit/HeaderBlock.java +++ /dev/null @@ -1,266 +0,0 @@ -package com.webcodepro.shrinkit; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import com.webcodepro.shrinkit.io.LittleEndianByteInputStream; - -/** - * The Header Block contains information and content - * about a single entry (be it a file or disk image). - *

- * Note that we need to support multiple versions of the NuFX - * archive format. Some details may be invalid, depending on - * version, and those are documented in the getter methods. - * - * @author robgreene@users.sourceforge.net - * @see http://www.nulib.com/library/FTN.e08002.htm - */ -public class HeaderBlock { - private int headerCrc; - private int attribCount; - private int versionNumber; - private long totalThreads; - private int fileSysId; - private int fileSysInfo; - private long access; - private long fileType; - private long extraType; - private int storageType; - private Date createWhen; - private Date modWhen; - private Date archiveWhen; - private int optionSize; - private byte[] optionListBytes; - private byte[] attribBytes; - private String filename; - private String rawFilename; - private long headerSize = 0; - private List threads = new ArrayList(); - - /** - * Create the Header Block. This is done dynamically since - * the Header Block size varies significantly. - */ - public HeaderBlock(LittleEndianByteInputStream bs) throws IOException { - int type = bs.seekFileType(4); - if (type == 0) - throw new IOException("Unable to decode this archive."); // FIXME - NLS - headerCrc = bs.readWord(); - attribCount = bs.readWord(); - versionNumber = bs.readWord(); - totalThreads = bs.readLong(); - fileSysId = bs.readWord(); - fileSysInfo = bs.readWord(); - access = bs.readLong(); - fileType = bs.readLong(); - extraType = bs.readLong(); - storageType = bs.readWord(); - createWhen = bs.readDate(); - modWhen = bs.readDate(); - archiveWhen = bs.readDate(); - // Read the mysterious option_list - if (versionNumber >= 1) { - optionSize = bs.readWord(); - if (optionSize > 0) { - optionListBytes = bs.readBytes(optionSize-2); - } - } - // Compute attribute bytes that exist and read (if needed) - int sizeofAttrib = attribCount - 58; - if (versionNumber >= 1) { - if (optionSize == 0) sizeofAttrib -= 2; - else sizeofAttrib -= optionSize; - } - if (sizeofAttrib > 0) { - attribBytes = bs.readBytes(sizeofAttrib); - } - // Read the (defunct) filename - int length = bs.readWord(); - if (length > 0) { - rawFilename = new String(bs.readBytes(length)); - } - } - /** - * Read in all data threads. All ThreadRecords are read and then - * each thread's data is read (per NuFX spec). - */ - public void readThreads(LittleEndianByteInputStream bs) throws IOException { - for (long l=0; l getThreadRecords() { - return threads; - } - public void setThreadRecords(List threads) { - this.threads = threads; - } - public long getHeaderSize() { - return headerSize; - } -} diff --git a/src/main/java/com/webcodepro/shrinkit/MasterHeaderBlock.java b/src/main/java/com/webcodepro/shrinkit/MasterHeaderBlock.java deleted file mode 100644 index efe05db..0000000 --- a/src/main/java/com/webcodepro/shrinkit/MasterHeaderBlock.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.webcodepro.shrinkit; - -import java.io.IOException; -import java.util.Date; - -import com.webcodepro.shrinkit.io.ByteConstants; -import com.webcodepro.shrinkit.io.LittleEndianByteInputStream; - -/** - * The Master Header Block contains information about the entire - * ShrinkIt archive. - *

- * Note that we need to support multiple versions of the NuFX - * archive format. Some details may be invalid, depending on - * version, and those are documented in the getter methods. - * - * @author robgreene@users.sourceforge.net - * @see http://www.nulib.com/library/FTN.e08002.htm - */ -public class MasterHeaderBlock { - private static final int MASTER_HEADER_LENGTH = 48; - private int masterCrc; - private boolean validCrc; - private long totalRecords; - private Date archiveCreateWhen; - private Date archiveModWhen; - private int masterVersion; - private long masterEof; - - /** - * Create the Master Header Block, based on the LittleEndianByteInputStream. - */ - public MasterHeaderBlock(LittleEndianByteInputStream bs) throws IOException { - int fileType = 0, headerOffset = 0; - fileType = bs.seekFileType(); - - if (fileType == NuFileArchive.BXY_ARCHIVE) { - bs.readBytes(127 - ByteConstants.NUFILE_ID.length); - headerOffset = 128; - int count = bs.read(); - if (count != 0) - throw new IOException("This is actually a Binary II archive with multiple files in it."); // FIXME - NLS - fileType = bs.seekFileType(); - } - if (!(fileType == NuFileArchive.NUFILE_ARCHIVE)) { - throw new IOException("Unable to decode this archive."); // FIXME - NLS - } - masterCrc = bs.readWord(); - bs.resetCrc(); // CRC is computed from this point to the end of the header - totalRecords = bs.readLong(); - archiveCreateWhen = bs.readDate(); - archiveModWhen = bs.readDate(); - masterVersion = bs.readWord(); - if (masterVersion > 0) { - bs.readBytes(8); // documented to be null, but we don't care - masterEof = bs.readLong(); - } else { - masterEof = -1; - } - // Read whatever remains of the fixed size header - while (bs.getTotalBytesRead() < MASTER_HEADER_LENGTH + headerOffset) { - bs.readByte(); - } - validCrc = (masterCrc == bs.getCrcValue()); - } - - // GENERATED CODE - - public int getMasterCrc() { - return masterCrc; - } - public void setMasterCrc(int masterCrc) { - this.masterCrc = masterCrc; - } - public long getTotalRecords() { - return totalRecords; - } - public void setTotalRecords(long totalRecords) { - this.totalRecords = totalRecords; - } - public Date getArchiveCreateWhen() { - return archiveCreateWhen; - } - public void setArchiveCreateWhen(Date archiveCreateWhen) { - this.archiveCreateWhen = archiveCreateWhen; - } - public Date getArchiveModWhen() { - return archiveModWhen; - } - public void setArchiveModWhen(Date archiveModWhen) { - this.archiveModWhen = archiveModWhen; - } - public int getMasterVersion() { - return masterVersion; - } - public void setMasterVersion(int masterVersion) { - this.masterVersion = masterVersion; - } - public long getMasterEof() { - return masterEof; - } - public void setMasterEof(long masterEof) { - this.masterEof = masterEof; - } - public boolean isValidCrc() { - return validCrc; - } -} diff --git a/src/main/java/com/webcodepro/shrinkit/NuFileArchive.java b/src/main/java/com/webcodepro/shrinkit/NuFileArchive.java deleted file mode 100644 index fb1f7d0..0000000 --- a/src/main/java/com/webcodepro/shrinkit/NuFileArchive.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.webcodepro.shrinkit; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; - -import com.webcodepro.shrinkit.io.LittleEndianByteInputStream; - -/** - * Basic reading of a NuFX archive. - * - * @author robgreene@users.sourceforge.net - */ -public class NuFileArchive { - private MasterHeaderBlock master; - private List headers; - private long totalSize = 0; - - /** - * Need to enumerate some basic sub-types of archives. - */ - public static final int NUFILE_ARCHIVE = 1; - public static final int NUFX_ARCHIVE = 2; - public static final int BXY_ARCHIVE = 3; - - /** - * Read in the NuFile/NuFX/Shrinkit archive. - */ - public NuFileArchive(InputStream inputStream) throws IOException { - LittleEndianByteInputStream bs = new LittleEndianByteInputStream(inputStream); - master = new MasterHeaderBlock(bs); - headers = new ArrayList(); - for (int i=0; i getHeaderBlocks() { - return headers; - }} diff --git a/src/main/java/com/webcodepro/shrinkit/ThreadClass.java b/src/main/java/com/webcodepro/shrinkit/ThreadClass.java deleted file mode 100644 index ba8da6f..0000000 --- a/src/main/java/com/webcodepro/shrinkit/ThreadClass.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.webcodepro.shrinkit; - -/** - * Define and decode the thread_class field. - * @author robgreene@users.sourceforge.net - */ -public enum ThreadClass { - MESSAGE, CONTROL, DATA, FILENAME; - - /** - * Find the given ThreadClass. - * @throws IllegalArgumentException if the thread_class is unknown - */ - public static ThreadClass find(int threadClass) { - switch (threadClass) { - case 0x0000: return MESSAGE; - case 0x0001: return CONTROL; - case 0x0002: return DATA; - case 0x0003: return FILENAME; - default: - throw new IllegalArgumentException("Unknown thread_class of " + threadClass); - } - } -} diff --git a/src/main/java/com/webcodepro/shrinkit/ThreadFormat.java b/src/main/java/com/webcodepro/shrinkit/ThreadFormat.java deleted file mode 100644 index 97f9f82..0000000 --- a/src/main/java/com/webcodepro/shrinkit/ThreadFormat.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.webcodepro.shrinkit; - -/** - * Define and decode the thread_format field. - * @author robgreene@users.sourceforge.net - */ -public enum ThreadFormat { - UNCOMPRESSED(0x0000), HUFFMAN_SQUEEZE(0x0001), DYNAMIC_LZW1(0x0002), DYNAMIC_LZW2(0x0003), - UNIX_12BIT_COMPRESS(0x0004), UNIX_16BIT_COMPRESS(0x0005); - - /** Associate the hex codes with the enum */ - private int threadFormat; - - private ThreadFormat(int threadFormat) { - this.threadFormat = threadFormat; - } - - public int getThreadFormat() { - return threadFormat; - } - - /** - * Find the ThreadFormat. - * @throws IllegalArgumentException if the thread_format is unknown - */ - public static ThreadFormat find(int threadFormat) { - for (ThreadFormat f : values()) { - if (threadFormat == f.getThreadFormat()) return f; - } - throw new IllegalArgumentException("Unknown thread_format of " + threadFormat); - } -} diff --git a/src/main/java/com/webcodepro/shrinkit/ThreadKind.java b/src/main/java/com/webcodepro/shrinkit/ThreadKind.java deleted file mode 100644 index 2418b60..0000000 --- a/src/main/java/com/webcodepro/shrinkit/ThreadKind.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.webcodepro.shrinkit; - -/** - * Define and decode the thread_kind field. - * @author robgreene@users.sourceforge.net - */ -public enum ThreadKind { - ASCII_TEXT, ALLOCATED_SPACE, APPLE_IIGS_ICON, CREATE_DIRECTORY, DATA_FORK, DISK_IMAGE, RESOURCE_FORK, - FILENAME; - - /** - * Find the specific ThreadKind. - * @throws IllegalArgumentException when the thread_kind cannot be determined - */ - public static ThreadKind find(int threadKind, ThreadClass threadClass) { - switch (threadClass) { - case MESSAGE: - switch (threadKind) { - case 0x0000: return ASCII_TEXT; - case 0x0001: return ALLOCATED_SPACE; - case 0x0002: return APPLE_IIGS_ICON; - } - throw new IllegalArgumentException("Unknown thread_kind "+threadKind+" for message thread_class of " + threadClass); - case CONTROL: - if (threadKind == 0x0000) return CREATE_DIRECTORY; - throw new IllegalArgumentException("Unknown thread_kind "+threadKind+" for control thread_class of " + threadClass); - case DATA: - switch (threadKind) { - case 0x0000: return DATA_FORK; - case 0x0001: return DISK_IMAGE; - case 0x0002: return RESOURCE_FORK; - } - throw new IllegalArgumentException("Unknown thread_kind "+threadKind+" for data thread_class of " + threadClass); - case FILENAME: - if (threadKind == 0x0000) return FILENAME; - throw new IllegalArgumentException("Unknown thread_kind "+threadKind+" for filename thread_class of " + threadClass); - default: - throw new IllegalArgumentException("Unknown thread_class of " + threadClass); - } - } -} diff --git a/src/main/java/com/webcodepro/shrinkit/ThreadRecord.java b/src/main/java/com/webcodepro/shrinkit/ThreadRecord.java deleted file mode 100644 index a32003c..0000000 --- a/src/main/java/com/webcodepro/shrinkit/ThreadRecord.java +++ /dev/null @@ -1,163 +0,0 @@ -package com.webcodepro.shrinkit; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; - -import com.webcodepro.shrinkit.io.LittleEndianByteInputStream; -import com.webcodepro.shrinkit.io.NufxLzw1InputStream; -import com.webcodepro.shrinkit.io.NufxLzw2InputStream; - -/** - * This represents a single thread from the Shrinkit archive. - * As it is constructed, the thread "header" is read. Once all - * threads have been constructed, use readThreadData - * to load up the data. - *

- * Depending on the type of thread, the data may be text. If so, - * isText will return true and getText - * will return the string. Otherwise the data should be read through - * one of the InputStream options. - * - * @author robgreene@users.sourceforge.net - */ -public class ThreadRecord { - private ThreadClass threadClass; - private ThreadFormat threadFormat; - private ThreadKind threadKind; - private int threadCrc; - private long threadEof; - private long compThreadEof; - private byte[] threadData; - - /** - * Construct the ThreadRecord and read the header details with no hints - * from the Header Block. - */ - public ThreadRecord(LittleEndianByteInputStream bs) throws IOException { - this(null, bs); - } - - /** - * Construct the ThreadRecord and read the header details. - */ - public ThreadRecord(HeaderBlock hb, LittleEndianByteInputStream bs) throws IOException { - threadClass = ThreadClass.find(bs.readWord()); - threadFormat = ThreadFormat.find(bs.readWord()); - threadKind = ThreadKind.find(bs.readWord(), threadClass); - threadCrc = bs.readWord(); - threadEof = bs.readLong(); - compThreadEof = bs.readLong(); - if ((threadKind == ThreadKind.DISK_IMAGE) && (hb != null)) { - /* If we have hints from the header block, repair some disk image related bugs. */ - if (hb.getStorageType() <= 13 ) { - /* supposed to be block size, but SHK v3.0.1 stored it wrong */ - threadEof = hb.getExtraType() * 512; - // System.out.println("Found erroneous storage type... fixing."); - } else if (hb.getStorageType() == 256 && - hb.getExtraType() == 280 && - hb.getFileSysId() == 2 ) { // FileSysDOS33 - /* - * Fix for less-common ShrinkIt problem: looks like an old - * version of GS/ShrinkIt used 256 as the block size when - * compressing DOS 3.3 images from 5.25" disks. If that - * appears to be the case here, crank up the block size. - */ - threadEof = hb.getExtraType() * 512; - } else { - threadEof = hb.getExtraType() * hb.getStorageType(); - } - } - } - - /** - * Read the raw thread data. This must be called. - */ - public void readThreadData(LittleEndianByteInputStream bs) throws IOException { - threadData = bs.readBytes((int)compThreadEof); - } - /** - * Determine if this is a text-type field. - */ - public boolean isText() { - return threadKind == ThreadKind.ASCII_TEXT || threadKind == ThreadKind.FILENAME; - } - /** - * Return the text data. - */ - public String getText() { - return isText() ? new String(threadData, 0, (int)threadEof) : null; - } - /** - * Get raw data bytes (compressed). - */ - public byte[] getBytes() { - return threadData; - } - /** - * Get the raw data input stream. - */ - public InputStream getRawInputStream() { - return new ByteArrayInputStream(threadData); - } - /** - * Get the appropriate input data stream for this thread to decompress the contents. - */ - public InputStream getInputStream() throws IOException { - switch (threadFormat) { - case UNCOMPRESSED: - return getRawInputStream(); - case DYNAMIC_LZW1: - return new NufxLzw1InputStream(new LittleEndianByteInputStream(getRawInputStream())); - case DYNAMIC_LZW2: - return new NufxLzw2InputStream(new LittleEndianByteInputStream(getRawInputStream())); - default: - throw new IOException("The thread format " + threadFormat + " does not have an InputStream associated with it!"); - } - } - - // GENERATED CODE - - public ThreadClass getThreadClass() { - return threadClass; - } - public void setThreadClass(ThreadClass threadClass) { - this.threadClass = threadClass; - } - public ThreadFormat getThreadFormat() { - return threadFormat; - } - public void setThreadFormat(ThreadFormat threadFormat) { - this.threadFormat = threadFormat; - } - public ThreadKind getThreadKind() { - return threadKind; - } - public void setThreadKind(ThreadKind threadKind) { - this.threadKind = threadKind; - } - public int getThreadCrc() { - return threadCrc; - } - public void setThreadCrc(int threadCrc) { - this.threadCrc = threadCrc; - } - public long getThreadEof() { - return threadEof; - } - public void setThreadEof(long threadEof) { - this.threadEof = threadEof; - } - public long getCompThreadEof() { - return compThreadEof; - } - public void setCompThreadEof(long compThreadEof) { - this.compThreadEof = compThreadEof; - } - public byte[] getThreadData() { - return threadData; - } - public void setThreadData(byte[] threadData) { - this.threadData = threadData; - } -} diff --git a/src/main/java/com/webcodepro/shrinkit/TimeRec.java b/src/main/java/com/webcodepro/shrinkit/TimeRec.java deleted file mode 100644 index 7ae035e..0000000 --- a/src/main/java/com/webcodepro/shrinkit/TimeRec.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.webcodepro.shrinkit; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; - -/** - * Apple IIgs Toolbox TimeRec object. - * - * @author robgreene@users.sourceforge.net - */ -public class TimeRec { - private static final int SECOND = 0; - private static final int MINUTE = 1; - private static final int HOUR = 2; - private static final int YEAR = 3; - private static final int DAY = 4; - private static final int MONTH = 5; - private static final int WEEKDAY = 7; - private static final int LENGTH = 8; - private byte[] data = null; - - /** - * Construct a TimeRec with the current date. - */ - public TimeRec() { - this(new Date()); - } - /** - * Construct a TimeRec with the specified date. You may pass in a null for a null date (all 0x00's). - */ - public TimeRec(Date date) { - setDate(date); - } - /** - * Construct a TimeRec from the given LENGTH byte array. - */ - public TimeRec(byte[] bytes, int offset) { - if (bytes == null || bytes.length - offset < LENGTH) { - throw new IllegalArgumentException("TimeRec requires a " + LENGTH + " byte array."); - } - //data = Arrays.copyOfRange(bytes, offset, LENGTH); - data = new byte[LENGTH]; - System.arraycopy(bytes, offset, data, 0, LENGTH); - } - /** - * Construct a TimeRec from the InputStream. - */ - public TimeRec(InputStream inputStream) throws IOException { - data = new byte[LENGTH]; - for (int i=0; i - * Warning: The read(byte[]) and read(byte[], int, int) - * methods of InputStream will not work appropriately with any - * bit size > 8 bits. - * - * @author robgreene@users.sourceforge.net - */ -public class BitInputStream extends InputStream implements BitConstants { - /** Our source of data. */ - private InputStream is; - /** The number of bits to read for a request. This can be adjusted dynamically. */ - private int requestedNumberOfBits; - /** The current bit mask to use when returning a read() request. */ - private int bitMask; - /** The buffer containing our bits. An int allows 32 bits which should cover up to a 24 bit read if my math is correct. :-) */ - private int data = 0; - /** Number of bits remaining in our buffer */ - private int bitsOfData = 0; - - /** - * Create a BitInputStream wrapping the given InputStream - * and reading the number of bits specified. - */ - public BitInputStream(InputStream is, int startingNumberOfBits) { - this.is = is; - setRequestedNumberOfBits(startingNumberOfBits); - } - - /** - * Set the number of bits to be read with each call to read(). - */ - public void setRequestedNumberOfBits(int numberOfBits) { - this.requestedNumberOfBits = numberOfBits; - this.bitMask = BIT_MASKS[numberOfBits]; - } - - /** - * Increase the requested number of bits by one. - * This is the general usage and prevents client from needing to track - * the requested number of bits or from making various method calls. - */ - public void increaseRequestedNumberOfBits() { - setRequestedNumberOfBits(requestedNumberOfBits + 1); - } - - /** - * Answer with the current bit mask for the current bit size. - */ - public int getBitMask() { - return bitMask; - } - - /** - * Read a number of bits off of the wrapped InputStream. - */ - public int read() throws IOException { - while (bitsOfData < requestedNumberOfBits) { - int b = is.read(); - if (b == -1) return b; - if (bitsOfData > 0) { - b <<= bitsOfData; // We're placing b on the high-bit side - } - data|= b; - bitsOfData+= 8; - } - int b = data & bitMask; - data >>= requestedNumberOfBits; - bitsOfData-= requestedNumberOfBits; - return b; - } - - /** - * When shifting from buffer to buffer, the input stream also should be reset. - * This allows the "left over" bits to be cleared. - */ - public void clearRemainingBitsOfData() { - this.bitsOfData = 0; - this.data = 0; - } -} - diff --git a/src/main/java/com/webcodepro/shrinkit/io/BitOutputStream.java b/src/main/java/com/webcodepro/shrinkit/io/BitOutputStream.java deleted file mode 100644 index ca81b7b..0000000 --- a/src/main/java/com/webcodepro/shrinkit/io/BitOutputStream.java +++ /dev/null @@ -1,98 +0,0 @@ -package com.webcodepro.shrinkit.io; - -import java.io.IOException; -import java.io.OutputStream; - -/** - * The BitOutputStream allows varying bit sizes to be written to the wrapped - * OutputStream. This is useful for LZW type compression algorithms - * where 9-12 bit codes are used instead of the 8-bit byte. - *

- * Warning: The write(byte[]) and write(byte[], int, int) - * methods of OutputStream will not work appropriately with any - * bit size > 8 bits. - * - * @author robgreene@users.sourceforge.net - */ -public class BitOutputStream extends OutputStream implements BitConstants { - /** Our data target. */ - private OutputStream os; - /** The number of bits to write for a request. This can be adjusted dynamically. */ - private int requestedNumberOfBits; - /** The current bit mask to use for a write(int) request. */ - private int bitMask; - /** The buffer containing our bits. */ - private int data = 0; - /** Number of bits remaining in our buffer */ - private int bitsOfData = 0; - - /** - * Create a BitOutpuStream wrapping the given OutputStream - * and writing the number of bits specified. - */ - public BitOutputStream(OutputStream os, int startingNumberOfBits) { - this.os = os; - setRequestedNumberOfBits(startingNumberOfBits); - } - - /** - * Set the number of bits to be write with each call to write(int). - */ - public void setRequestedNumberOfBits(int numberOfBits) { - this.requestedNumberOfBits = numberOfBits; - this.bitMask = BIT_MASKS[numberOfBits]; - } - - /** - * Increase the requested number of bits by one. - * This is the general usage and prevents client from needing to track - * the requested number of bits or from making various method calls. - */ - public void increaseRequestedNumberOfBits() { - setRequestedNumberOfBits(requestedNumberOfBits + 1); - } - - /** - * Answer with the current bit mask for the current bit size. - */ - public int getBitMask() { - return bitMask; - } - - /** - * Write the number of bits to the wrapped OutputStream. - */ - public void write(int b) throws IOException { - b &= bitMask; // Ensure we don't have extra baggage - b <<= bitsOfData; // Move beyond existing bits of data - data|= b; // Add in the additional data - bitsOfData+= requestedNumberOfBits; - while (bitsOfData >= 8) { - os.write(data & 0xff); - data >>= 8; - bitsOfData-= 8; - } - } - - /** - * When shifting from buffer to buffer, this OutputStream also should be reset. - * This allows the "left over" bits to be cleared. - */ - public void clearRemainingBitsOfData() { - this.bitsOfData = 0; - this.data = 0; - } - - /** - * Close the output stream and write any remaining byte to the output. - * Note that we may very well end up with extra bits if there are < 8 - * bits remaining. - */ - public void close() throws IOException { - if (bitsOfData > 0) { - write(0x00); // forces a flush of the remaining bits in the proper order - } - } - -} - diff --git a/src/main/java/com/webcodepro/shrinkit/io/ByteConstants.java b/src/main/java/com/webcodepro/shrinkit/io/ByteConstants.java deleted file mode 100644 index e1c7e1a..0000000 --- a/src/main/java/com/webcodepro/shrinkit/io/ByteConstants.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.webcodepro.shrinkit.io; - - -/** - * Provides constants for the LittleEndianByteInputStream and ByteTarget classes. - * - * @author robgreene@users.sourceforge.net - * @see LittleEndianByteInputStream - * @see ByteTarget - */ -public interface ByteConstants { - /** Master Header Block identifier "magic" bytes. */ - public static final byte[] NUFILE_ID = { 0x4e, (byte)0xf5, 0x46, (byte)0xe9, 0x6c, (byte)0xe5 }; - /** Header Block identifier "magic" bytes. */ - public static final byte[] NUFX_ID = { 0x4e, (byte)0xf5, 0x46, (byte)0xd8 }; - /** Binary II identifier "magic" bytes. */ - public static final byte[] BXY_ID = { 0x0a, 0x47, 0x4c }; - /** Apple IIgs Toolbox TimeRec seconds byte position. */ - public static final int TIMEREC_SECOND = 0; - /** Apple IIgs Toolbox TimeRec seconds byte position. */ - public static final int TIMEREC_MINUTE = 1; - /** Apple IIgs Toolbox TimeRec minutes byte position. */ - public static final int TIMEREC_HOUR = 2; - /** Apple IIgs Toolbox TimeRec hours byte position. */ - public static final int TIMEREC_YEAR = 3; - /** Apple IIgs Toolbox TimeRec year byte position. */ - public static final int TIMEREC_DAY = 4; - /** Apple IIgs Toolbox TimeRec day byte position. */ - public static final int TIMEREC_MONTH = 5; - /** Apple IIgs Toolbox TimeRec weekday (Mon, Tue, etc) byte position. */ - public static final int TIMEREC_WEEKDAY = 7; - /** Apple IIgs Toolbox TimeRec length. */ - public static final int TIMEREC_LENGTH = 8; - /** A null TimeRec */ - public static final byte[] TIMEREC_NULL = new byte[TIMEREC_LENGTH]; -} diff --git a/src/main/java/com/webcodepro/shrinkit/io/LittleEndianByteInputStream.java b/src/main/java/com/webcodepro/shrinkit/io/LittleEndianByteInputStream.java deleted file mode 100644 index bf5c145..0000000 --- a/src/main/java/com/webcodepro/shrinkit/io/LittleEndianByteInputStream.java +++ /dev/null @@ -1,171 +0,0 @@ -package com.webcodepro.shrinkit.io; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Arrays; -import java.util.Date; -import java.util.GregorianCalendar; - -import com.webcodepro.shrinkit.CRC16; -import com.webcodepro.shrinkit.NuFileArchive; - -/** - * A simple class to hide the source of byte data. - * @author robgreene@users.sourceforge.net - */ -public class LittleEndianByteInputStream extends InputStream implements ByteConstants { - private InputStream inputStream; - private long bytesRead = 0; - private CRC16 crc = new CRC16(); - - /** - * Construct a LittleEndianByteInputStream from an InputStream. - */ - public LittleEndianByteInputStream(InputStream inputStream) { - this.inputStream = inputStream; - } - /** - * Construct a LittleEndianByteInputStream from a byte array. - */ - public LittleEndianByteInputStream(byte[] data) { - this.inputStream = new ByteArrayInputStream(data); - } - - /** - * Get the next byte. - * Returns -1 if at end of input. - * Note that an unsigned byte needs to be returned in a larger container (ie, a short or int or long). - */ - public int read() throws IOException { - int b = inputStream.read(); - if (b != -1) { - crc.update(b); - bytesRead++; - } - return b; - } - /** - * Get the next byte and fail if we are at EOF. - * Note that an unsigned byte needs to be returned in a larger container (ie, a short or int or long). - */ - public int readByte() throws IOException { - int i = read(); - if (i == -1) throw new IOException("Expecting a byte but at EOF"); - return i; - } - /** - * Get the next set of bytes as an array. - * If EOF encountered, an IOException is thrown. - */ - public byte[] readBytes(int bytes) throws IOException { - byte[] data = new byte[bytes]; - int read = inputStream.read(data); - bytesRead+= read; - // In the case where we have a zero-byte file, 'read' stays at -1, which is not correct. Fix it. - if ((bytes == 0) && (read == -1)) - read = 0; - if (read < bytes) { - throw new IOException("Requested " + bytes + " bytes, but " + read + " read"); - } - crc.update(data); - return data; - } - - /** - * Test the beginning of the data stream for a magic signature, for up to a total - * of 2k bytes of leading garbage - */ - public int seekFileType() throws IOException { - return seekFileType(6); - } - /** - * Test the beginning of the data stream for a magic signature, specifying the - * maximum size of a signature to test for - */ - public int seekFileType(int max) throws IOException { - byte[] data = new byte[2048]; - byte[] testNUFILE = new byte[6]; - byte[] testNUFX = new byte[4]; - byte[] testBXY = new byte[3]; - int type = 0, i, pos = 0; - for (i = 0;i> 8); - } - /** - * Write a "Long". - */ - public void writeLong(long l) throws IOException { - write((int)(l & 0xff)); - write((int)((l >> 8) & 0xff)); - write((int)((l >> 16) & 0xff)); - write((int)((l >> 24) & 0xff)); - } - /** - * Write the Java Date object as a TimeRec. - * Note that years 2000-2039 are assumed to be 00-39 per the NuFX addendum - * at http://www.nulib.com/library/nufx-addendum.htm. - * @see http://www.nulib.com/library/nufx-addendum.htm - */ - public void writeDate(Date date) throws IOException { - byte[] data = null; - if (date == null) { - data = TIMEREC_NULL; - } else { - data = new byte[TIMEREC_LENGTH]; - GregorianCalendar gc = new GregorianCalendar(); - gc.setTime(date); - int year = gc.get(Calendar.YEAR); - year -= (year < 2000) ? 1900 : 2000; - data[TIMEREC_YEAR] = (byte)(year & 0xff); - data[TIMEREC_MONTH] = (byte)(gc.get(Calendar.MONTH) + 1); - data[TIMEREC_DAY] = (byte)gc.get(Calendar.DAY_OF_MONTH); - data[TIMEREC_HOUR] = (byte)gc.get(Calendar.HOUR_OF_DAY); - data[TIMEREC_MINUTE] = (byte)gc.get(Calendar.MINUTE); - data[TIMEREC_SECOND] = (byte)gc.get(Calendar.SECOND); - data[TIMEREC_WEEKDAY] = (byte)gc.get(Calendar.DAY_OF_WEEK); - } - write(data); - } - - /** - * Reset the CRC-16 to $0000. - */ - public void resetCrc() { - crc.reset(); - } - /** - * Get the current CRC-16 value. - */ - public long getCrcValue() { - return crc.getValue(); - } - - /** - * Answer with the total number of bytes written. - */ - public long getTotalBytesWritten() { - return bytesWritten; - } - - /** - * Pass the flush request to the wrapped stream. - */ - public void flush() throws IOException { - outputStream.flush(); - } - /** - * Pass the close request to the wrapped stream. - */ - public void close() throws IOException { - outputStream.close(); - } -} diff --git a/src/main/java/com/webcodepro/shrinkit/io/LzwInputStream.java b/src/main/java/com/webcodepro/shrinkit/io/LzwInputStream.java deleted file mode 100644 index 3204544..0000000 --- a/src/main/java/com/webcodepro/shrinkit/io/LzwInputStream.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.webcodepro.shrinkit.io; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; - -/** - * This is the generic Shrinkit LZW decompression algorithm. - * It does not deal with the vagaries of the LZW/1 and LZW/2 data streams. - * It does, however, deal with dictionary clears (0x100) and the - * BitInputStream bit sizes. - * - * @author robgreene@users.sourceforge.net - */ -public class LzwInputStream extends InputStream { - private BitInputStream is; - private List dictionary; - private Queue outputBuffer = new ConcurrentLinkedQueue(); - private boolean newBuffer = true; - // See Wikipedia entry on LZW for variable naming - private int k; - private int[] w; - private int[] entry; - - /** - * Create the LzwInputStream based on the given - * BitInputStream. - * @see BitInputStream - */ - public LzwInputStream(BitInputStream is) { - this.is = is; - } - - /** - * Answer with the next byte from the (now) decompressed input stream. - */ - public int read() throws IOException { - if (outputBuffer.isEmpty()) { - fillBuffer(); - } - return outputBuffer.remove(); - } - - /** - * Fill the buffer up with some decompressed data. - * This may range from one byte to many bytes, depending on what is in the - * dictionary. - * @see http://en.wikipedia.org/wiki/Lzw for the general algorithm - */ - public void fillBuffer() throws IOException { - if (dictionary == null) { - is.setRequestedNumberOfBits(9); - // Setup default dictionary for all bytes - dictionary = new ArrayList(); - for (short i=0; i<256; i++) dictionary.add(new int[] { i }); - dictionary.add(new int[] { 0x100 }); // 0x100 not used by NuFX - } - if (newBuffer) { - // Setup for decompression; - k = is.read(); - outputBuffer.add(k); - if (k == -1) return; - w = new int[] { k }; - newBuffer = false; - } - // LZW decompression - k = is.read(); - if (k == -1) { - outputBuffer.add(k); - return; - } - if (k == 0x100) { - dictionary = null; - is.setRequestedNumberOfBits(9); - k = 0; - w = null; - entry = null; - newBuffer = true; - fillBuffer(); // Warning: recursive call - return; - } - if (k < dictionary.size()) { - entry = dictionary.get(k); - } else if (k == dictionary.size()) { - //entry = Arrays.copyOf(w, w.length+1); - entry = new int[w.length+1]; - System.arraycopy(w, 0, entry, 0, w.length); - entry[w.length] = w[0]; - } else { - throw new IOException("Invalid code of <" + k + "> encountered"); - } - for (int i : entry) outputBuffer.add(i); - //int[] newEntry = Arrays.copyOf(w, w.length+1); - int[] newEntry = new int[w.length+1]; - System.arraycopy(w, 0, newEntry, 0, w.length); - newEntry[w.length] = entry[0]; - dictionary.add(newEntry); - w = entry; - // Exclusive-OR the current bitmask against the new dictionary size -- if all bits are - // on, we'll get 0. (That is, all 9 bits on is 0x01ff exclusive or bit mask of 0x01ff - // yields 0x0000.) This tells us we need to increase the number of bits we're pulling - // from the bit stream. - if ((dictionary.size() ^ is.getBitMask()) == 0) { - is.increaseRequestedNumberOfBits(); - } - } - - /** - * Clear out the dictionary. It will be rebuilt on the next call to - * fillBuffer. - */ - public void clearDictionary() { - dictionary = null; - is.setRequestedNumberOfBits(9); - is.clearRemainingBitsOfData(); - outputBuffer.clear(); - k = 0; - w = null; - entry = null; - newBuffer = true; - } - - /** - * Provide necessary housekeeping to reset LZW stream between NuFX buffer changes. - * The dictionary is the only item that is not cleared -- that needs to be done - * explicitly since behavior between LZW/1 and LZW/2 differ. - */ - public void clearData() { - is.clearRemainingBitsOfData(); - outputBuffer.clear(); - } -} diff --git a/src/main/java/com/webcodepro/shrinkit/io/LzwOutputStream.java b/src/main/java/com/webcodepro/shrinkit/io/LzwOutputStream.java deleted file mode 100644 index 0389903..0000000 --- a/src/main/java/com/webcodepro/shrinkit/io/LzwOutputStream.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.webcodepro.shrinkit.io; - -import java.io.IOException; -import java.io.OutputStream; -import java.util.HashMap; -import java.util.Map; - -import com.webcodepro.shrinkit.CRC16; - -/** - * This is the generic Shrinkit LZW compression algorithm. - * It does not deal with the vagaries of the LZW/1 and LZW/2 data streams. - * - * @author robgreene@users.sourceforge.net - */ -public class LzwOutputStream extends OutputStream { - private BitOutputStream os; - private Map dictionary = new HashMap(); - private int[] w = new int[0]; - private int nextCode = 0x101; - - /** - * This simple class can be used as a key into a Map. - * - * @author robgreene@users.sourceforge.net - */ - private class ByteArray { - /** Data being managed. */ - private int[] data; - /** The computed hash code -- CRC-16 for lack of imagination. */ - private int hashCode; - - public ByteArray(int d) { - this(new int[] { d }); - } - public ByteArray(int[] data) { - this.data = data; - CRC16 crc = new CRC16(); - for (int b : data) crc.update(b); - hashCode = (int)crc.getValue(); - } - public boolean equals(Object obj) { - ByteArray ba = (ByteArray)obj; - if (data.length != ba.data.length) return false; - for (int i=0; i 0) System.arraycopy(w, 0, wc, 0, w.length); - wc[wc.length-1]= c; - if (dictionary.containsKey(new ByteArray(wc))) { - w = wc; - } else { - dictionary.put(new ByteArray(wc), nextCode++); - os.write(dictionary.get(new ByteArray(w))); - w = new int[] { c }; - } - // Exclusive-OR the current bitmask against the new dictionary size -- if all bits are - // on, we'll get 0. (That is, all 9 bits on is 0x01ff exclusive or bit mask of 0x01ff - // yields 0x0000.) This tells us we need to increase the number of bits we're writing - // to the bit stream. - if ((dictionary.size() ^ os.getBitMask()) == 0) { - os.increaseRequestedNumberOfBits(); - } - } - - @Override - public void flush() throws IOException { - os.write(dictionary.get(new ByteArray(w))); - } - - @Override - public void close() throws IOException { - flush(); - os.flush(); - os.close(); - } -} diff --git a/src/main/java/com/webcodepro/shrinkit/io/NufxLzw1InputStream.java b/src/main/java/com/webcodepro/shrinkit/io/NufxLzw1InputStream.java deleted file mode 100644 index 520c6da..0000000 --- a/src/main/java/com/webcodepro/shrinkit/io/NufxLzw1InputStream.java +++ /dev/null @@ -1,144 +0,0 @@ -package com.webcodepro.shrinkit.io; - -import java.io.IOException; -import java.io.InputStream; - -import com.webcodepro.shrinkit.CRC16; - -/** - * The NufxLzw1InputStream reads a data fork or - * resource fork written in the NuFX LZW/1 format. - *

- * The layout of the LZW/1 data is as follows: - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
"Fork" Header
+0WordCRC-16 of the uncompressed data within the thread
+2ByteLow-level volume number use to format 5.25" disks
+3ByteRLE character used to decode this thread
Each subsequent 4K chunk of data
+0WordLength after RLE compression (if RLE is not used, length - * will be 4096
+2ByteA $01 indicates LZW applied to this chunk; $00 that LZW - * was not applied to this chunk
- *

- * Note that the LZW string table is cleared after - * every chunk. - * - * @author robgreene@users.sourceforge.net - */ -public class NufxLzw1InputStream extends InputStream { - /** This is the raw data stream with all markers and compressed data. */ - private LittleEndianByteInputStream dataStream; - /** Used for an LZW-only InputStream. */ - private LzwInputStream lzwStream; - /** Used for an RLE-only InputStream. */ - private RleInputStream rleStream; - /** Used for an LZW+RLE InputStream. */ - private InputStream lzwRleStream; - /** This is the generic decompression stream from which we read. */ - private InputStream decompressionStream; - /** Counts the number of bytes in the 4096 byte chunk. */ - private int bytesLeftInChunk; - /** This is the CRC-16 for the uncompressed fork. */ - private int givenCrc = -1; - /** This is the volume number for 5.25" disks. */ - private int volumeNumber; - /** This is the RLE character to use. */ - private int rleCharacter; - /** Used to track the CRC of data we've extracted */ - private CRC16 dataCrc = new CRC16(); - - /** - * Create the LZW/1 input stream. - */ - public NufxLzw1InputStream(LittleEndianByteInputStream dataStream) { - this.dataStream = dataStream; - } - - /** - * Read the next byte in the decompressed data stream. - */ - public int read() throws IOException { - if (givenCrc == -1) { // read the data or resource fork header - givenCrc = dataStream.readWord(); - volumeNumber = dataStream.readByte(); - rleCharacter = dataStream.readByte(); - lzwStream = new LzwInputStream(new BitInputStream(dataStream, 9)); - rleStream = new RleInputStream(dataStream, rleCharacter); - lzwRleStream = new RleInputStream(lzwStream); - } - if (bytesLeftInChunk == 0) { // read the chunk header - bytesLeftInChunk = 4096; // NuFX always reads 4096 bytes - lzwStream.clearDictionary(); // Always clear dictionary - int length = dataStream.readWord(); - int lzwFlag = dataStream.readByte(); - int flag = lzwFlag + (length == 4096 ? 0 : 2); - switch (flag) { - case 0: decompressionStream = dataStream; - break; - case 1: decompressionStream = lzwStream; - break; - case 2: decompressionStream = rleStream; - break; - case 3: decompressionStream = lzwRleStream; - break; - default: throw new IOException("Unknown type of decompression, flag = " + flag); - } - } - // Now we can read a data byte - int b = decompressionStream.read(); - bytesLeftInChunk--; - dataCrc.update(b); - return b; - } - - /** - * Indicates if the computed CRC matches the CRC given in the data stream. - */ - public boolean isCrcValid() { - return givenCrc == dataCrc.getValue(); - } - - // GENERATED CODE - - public int getGivenCrc() { - return givenCrc; - } - public void setGivenCrc(int givenCrc) { - this.givenCrc = givenCrc; - } - public int getVolumeNumber() { - return volumeNumber; - } - public void setVolumeNumber(int volumeNumber) { - this.volumeNumber = volumeNumber; - } - public int getRleCharacter() { - return rleCharacter; - } - public void setRleCharacter(int rleCharacter) { - this.rleCharacter = rleCharacter; - } - public long getDataCrc() { - return dataCrc.getValue(); - } -} diff --git a/src/main/java/com/webcodepro/shrinkit/io/NufxLzw2InputStream.java b/src/main/java/com/webcodepro/shrinkit/io/NufxLzw2InputStream.java deleted file mode 100644 index d38af67..0000000 --- a/src/main/java/com/webcodepro/shrinkit/io/NufxLzw2InputStream.java +++ /dev/null @@ -1,131 +0,0 @@ -package com.webcodepro.shrinkit.io; - -import java.io.IOException; -import java.io.InputStream; - -import com.webcodepro.shrinkit.CRC16; - -/** - * The NufxLzw2InputStream reads a data fork or - * resource fork written in the NuFX LZW/2 format. - *

- * The layout of the LZW/2 data is as follows: - *

- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
"Fork" Header
+0ByteLow-level volume number used to format 5.25" disks
+1ByteRLE character used to decode this thread
Each subsequent 4K chunk of data
+0WordBits 0-12: Length after RLE compression
- * Bit 15: LZW flag (set to 1 if LZW used)
+2WordIf LZW flag = 1, total bytes in chunk
- * Else (flag = 0) start of data
- *

- * The LZW/2 dictionary is only cleared when the table becomes full and is indicated - * in the input stream by 0x100. It is also cleared whenever a chunk that is not - * LZW encoded is encountered. - * - * @author robgreene@users.sourceforge.net - */ -public class NufxLzw2InputStream extends InputStream { - /** This is the raw data stream with all markers and compressed data. */ - private LittleEndianByteInputStream dataStream; - /** Used for an LZW-only InputStream. */ - private LzwInputStream lzwStream; - /** Used for an RLE-only InputStream. */ - private RleInputStream rleStream; - /** Used for an LZW+RLE InputStream. */ - private InputStream lzwRleStream; - /** This is the generic decompression stream from which we read. */ - private InputStream decompressionStream; - /** Counts the number of bytes in the 4096 byte chunk. */ - private int bytesLeftInChunk; - /** This is the volume number for 5.25" disks. */ - private int volumeNumber = -1; - /** This is the RLE character to use. */ - private int rleCharacter; - /** Used to track the CRC of data we've extracted */ - private CRC16 dataCrc = new CRC16(); - - /** - * Create the LZW/2 input stream. - */ - public NufxLzw2InputStream(LittleEndianByteInputStream dataStream) { - this.dataStream = dataStream; - } - - /** - * Read the next byte in the decompressed data stream. - */ - public int read() throws IOException { - if (volumeNumber == -1) { // read the data or resource fork header - volumeNumber = dataStream.readByte(); - rleCharacter = dataStream.readByte(); - lzwStream = new LzwInputStream(new BitInputStream(dataStream, 9)); - rleStream = new RleInputStream(dataStream, rleCharacter); - lzwRleStream = new RleInputStream(lzwStream); - } - if (bytesLeftInChunk == 0) { // read the chunk header - bytesLeftInChunk = 4096; // NuFX always reads 4096 bytes - lzwStream.clearData(); // Allow the LZW stream to do a little housekeeping - int word = dataStream.readWord(); - int length = word & 0x7fff; - int lzwFlag = word & 0x8000; - if (lzwFlag == 0) { // We clear dictionary whenever a non-LZW chunk is encountered - lzwStream.clearDictionary(); - } else { - dataStream.readWord(); // At this time, I just throw away the total bytes in this chunk... - } - int flag = (lzwFlag == 0 ? 0 : 1) + (length == 4096 ? 0 : 2); - switch (flag) { - case 0: decompressionStream = dataStream; - break; - case 1: decompressionStream = lzwStream; - break; - case 2: decompressionStream = rleStream; - break; - case 3: decompressionStream = lzwRleStream; - break; - default: throw new IOException("Unknown type of decompression, flag = " + flag); - } - } - // Now we can read a data byte - int b = decompressionStream.read(); - bytesLeftInChunk--; - dataCrc.update(b); - return b; - } - - // GENERATED CODE - - public int getVolumeNumber() { - return volumeNumber; - } - public void setVolumeNumber(int volumeNumber) { - this.volumeNumber = volumeNumber; - } - public int getRleCharacter() { - return rleCharacter; - } - public void setRleCharacter(int rleCharacter) { - this.rleCharacter = rleCharacter; - } - public long getDataCrc() { - return dataCrc.getValue(); - } -} diff --git a/src/main/java/com/webcodepro/shrinkit/io/RleInputStream.java b/src/main/java/com/webcodepro/shrinkit/io/RleInputStream.java deleted file mode 100644 index 9e2d42d..0000000 --- a/src/main/java/com/webcodepro/shrinkit/io/RleInputStream.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.webcodepro.shrinkit.io; - -import java.io.IOException; -import java.io.InputStream; - - -/** - * The RleInputStream handles the NuFX RLE data stream. - * This data stream is byte oriented. If a repeat occurs, - * the data stream will contain the marker byte, byte to - * repeat, and the number of repeats (zero based; ie, $00=1, - * $01=2, ... $ff=256). The default marker is $DB. - * - * @author robgreene@users.sourceforge.net - */ -public class RleInputStream extends InputStream { - private InputStream bs; - private int escapeChar; - private int repeatedByte; - private int numBytes = -1; - - /** - * Create an RLE input stream with the default marker byte. - */ - public RleInputStream(InputStream bs) { - this(bs, 0xdb); - } - /** - * Create an RLE input stream with the specified marker byte. - */ - public RleInputStream(InputStream bs, int escapeChar) { - this.bs = bs; - this.escapeChar = escapeChar; - } - - /** - * Read the next byte from the input stream. - */ - public int read() throws IOException { - if (numBytes == -1) { - int b = bs.read(); - if (b == escapeChar) { - repeatedByte = bs.read(); - numBytes = bs.read(); - } else { - return b; - } - } - numBytes--; - return repeatedByte; - } - -} diff --git a/src/main/java/com/webcodepro/shrinkit/io/RleOutputStream.java b/src/main/java/com/webcodepro/shrinkit/io/RleOutputStream.java deleted file mode 100644 index b365245..0000000 --- a/src/main/java/com/webcodepro/shrinkit/io/RleOutputStream.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.webcodepro.shrinkit.io; - -import java.io.IOException; -import java.io.OutputStream; - -/** - * The RleOutputStream handles the NuFX RLE data stream. - * This data stream is byte oriented. If a repeat occurs, - * the data stream will contain the marker byte, byte to - * repeat, and the number of repeats (zero based; ie, $00=1, - * $01=2, ... $ff=256). The default marker is $DB. - * - * @author robgreene@users.sourceforge.net - */ -public class RleOutputStream extends OutputStream { - private OutputStream os; - private int escapeChar; - private int repeatedByte; - private int numBytes = -1; - - /** - * Create an RLE output stream with the default marker byte. - */ - public RleOutputStream(OutputStream bs) { - this(bs, 0xdb); - } - /** - * Create an RLE output stream with the specified marker byte. - */ - public RleOutputStream(OutputStream os, int escapeChar) { - this.os = os; - this.escapeChar = escapeChar; - } - - /** - * Write the next byte to the output stream. - */ - public void write(int b) throws IOException { - if (numBytes == -1) { - repeatedByte = b; - numBytes++; - } else if (repeatedByte == b) { - numBytes++; - if (numBytes > 255) { - flush(); - } - } else { - flush(); - repeatedByte = b; - numBytes++; - } - } - - /** - * Flush out any remaining data. - * If we only have 1 byte and it is not the repeated - * byte, we can just dump that byte. Otherwise, we need to - * write out the escape character, the repeated byte, and - * the number of bytes. - */ - public void flush() throws IOException { - if (numBytes != -1) { - if (numBytes == 0 && escapeChar != repeatedByte) { - os.write(repeatedByte); - } else { - os.write(escapeChar); - os.write(repeatedByte); - os.write(numBytes); - } - numBytes = -1; - } - } - - /** - * Close out the data stream. Makes sure the repeate buffer - * is flushed. - */ - public void close() throws IOException { - flush(); - os.close(); - } -}