diff --git a/samples/Joystick.SHK b/samples/Joystick.SHK deleted file mode 100644 index 56d4748..0000000 Binary files a/samples/Joystick.SHK and /dev/null differ diff --git a/samples/Scc.shk b/samples/Scc.shk deleted file mode 100644 index 4947c3f..0000000 Binary files a/samples/Scc.shk and /dev/null differ diff --git a/src/com/webcodepro/shrinkit/HeaderBlock.java b/src/com/webcodepro/shrinkit/HeaderBlock.java index 26ca778..419b727 100644 --- a/src/com/webcodepro/shrinkit/HeaderBlock.java +++ b/src/com/webcodepro/shrinkit/HeaderBlock.java @@ -36,7 +36,8 @@ public class HeaderBlock { private byte[] optionListBytes; private byte[] attribBytes; private String filename; - private List threads; + private String rawFilename; + private List threads = new ArrayList(); /** * Create the Header Block. This is done dynamically since @@ -76,7 +77,7 @@ public class HeaderBlock { // Read the (defunct) filename int length = bs.readWord(); if (length > 0) { - filename = new String(bs.readBytes(length)); + rawFilename = new String(bs.readBytes(length)); } } /** @@ -84,11 +85,51 @@ public class HeaderBlock { * each thread's data is read (per NuFX spec). */ public void readThreads(LittleEndianByteInputStream bs) throws IOException { - threads = new ArrayList(); for (long l=0; l getThreadRecords() { return threads; } diff --git a/src/com/webcodepro/shrinkit/ThreadRecord.java b/src/com/webcodepro/shrinkit/ThreadRecord.java index 8eb43e7..3d39f43 100644 --- a/src/com/webcodepro/shrinkit/ThreadRecord.java +++ b/src/com/webcodepro/shrinkit/ThreadRecord.java @@ -5,6 +5,8 @@ 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. @@ -70,6 +72,21 @@ public class ThreadRecord { 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 diff --git a/test_src/com/webcodepro/shrinkit/HeaderBlockTest.java b/test_src/com/webcodepro/shrinkit/HeaderBlockTest.java index ac5d0ed..195668e 100644 --- a/test_src/com/webcodepro/shrinkit/HeaderBlockTest.java +++ b/test_src/com/webcodepro/shrinkit/HeaderBlockTest.java @@ -141,6 +141,6 @@ public class HeaderBlockTest extends TestCase { assertEquals(new LittleEndianByteInputStream(new byte[] {0x00,0x11,0x11,0x5e,0x13,0x01,0x00,0x01}).readDate(), b.getModWhen()); assertEquals(new LittleEndianByteInputStream(new byte[] {0x38,0x0c,0x14,0x5f,0x08,0x07,0x00,0x04}).readDate(), b.getArchiveWhen()); assertEquals(0x0000, b.getOptionSize()); - assertNull(b.getFilename()); + assertNull(b.getRawFilename()); } } diff --git a/test_src/com/webcodepro/shrinkit/NuFileArchiveTest.java b/test_src/com/webcodepro/shrinkit/NuFileArchiveTest.java deleted file mode 100644 index f24b5a3..0000000 --- a/test_src/com/webcodepro/shrinkit/NuFileArchiveTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.webcodepro.shrinkit; - -import java.io.IOException; -import java.io.InputStream; - -import junit.framework.TestCase; - -/** - * Not really a JUnit test, but this does verify that NuFX archives can be read. - * We read a couple of samples and dump out the details. - *

- * Note that to successfully run this, the classpath must have the samples folder - * added to it. - * - * @author robgreene@users.sourceforge.net - */ -public class NuFileArchiveTest extends TestCase { - public void testReadJoystickShk() throws IOException { - display("/Joystick.SHK"); - } - public void testReadSccShk() throws IOException { - display("/Scc.shk"); - } - - private void display(String archiveName) throws IOException { - System.out.printf("Details for %s\n\n", archiveName); - InputStream is = getClass().getResourceAsStream(archiveName); - if (is == null) { - System.out.printf("*** ERROR: Unable to locate '%s'", archiveName); - fail("Unable to locate archive file"); - } - NuFileArchive a = new NuFileArchive(is); - MasterHeaderBlock m = a.getMasterHeaderBlock(); - System.out.printf("Master Header Block\n==================\n" - + "master_crc=$%x\ntotal_records=%d\narchive_create_when=%tc\narchive_mod_when=%tc\n" - + "master_version=%d\nmaster_eof=$%x\nisValidCrc = %b\n\n", - m.getMasterCrc(), m.getTotalRecords(), m.getArchiveCreateWhen(), m.getArchiveModWhen(), - m.getMasterVersion(), m.getMasterEof(), m.isValidCrc()); - for (HeaderBlock b : a.getHeaderBlocks()) { - System.out.printf("\tHeader Block\n\t============\n"); - System.out.printf("\theader_crc=$%x\n\tattrib_count=%d\n\tversion_number=%d\n\ttotal_threads=%d\n\t" - + "file_sys_id=$%x\n\tfile_sys_info=$%x\n\taccess=$%x\n\tfile_type=$%x\n\textra_type=$%x\n\t" - + "storage_type=$%x\n\tcreate_when=%tc\n\tmod_when=%tc\n\tarchive_when=%tc\n\toption_size=%d\n\t" - + "filename=%s\n\n", - b.getHeaderCrc(), b.getAttribCount(), b.getVersionNumber(), b.getTotalThreads(), b.getFileSysId(), - b.getFileSysInfo(), b.getAccess(), b.getFileType(), b.getExtraType(), b.getStorageType(), - b.getCreateWhen(), b.getModWhen(), b.getArchiveWhen(), b.getOptionSize(), b.getFilename()); - System.out.printf("\t\tThreads\n\t\t=======\n"); - for (ThreadRecord r : b.getThreadRecords()) { - System.out.printf("\t\tthread_class=%s\n\t\tthread_format=%s\n\t\tthread_kind=%s\n\t\t" - + "thread_crc=$%x\n\t\tthread_eof=$%x\n\t\tcompThreadEof=$%x\n", - r.getThreadClass(), r.getThreadFormat(), r.getThreadKind(), r.getThreadCrc(), - r.getThreadEof(), r.getCompThreadEof()); - if (r.getThreadKind() == ThreadKind.FILENAME) { - System.out.printf("\t\tFILENAME=%s\n", r.getText()); - } - System.out.printf("\n"); - } - } - } -} diff --git a/test_src/com/webcodepro/shrinkit/io/NufxLzw1Test.java b/test_src/com/webcodepro/shrinkit/io/NufxLzw1Test.java index bcd698a..d9a3eec 100644 --- a/test_src/com/webcodepro/shrinkit/io/NufxLzw1Test.java +++ b/test_src/com/webcodepro/shrinkit/io/NufxLzw1Test.java @@ -1,12 +1,6 @@ package com.webcodepro.shrinkit.io; import java.io.IOException; -import java.util.List; - -import com.webcodepro.shrinkit.HeaderBlock; -import com.webcodepro.shrinkit.NuFileArchive; -import com.webcodepro.shrinkit.ThreadKind; -import com.webcodepro.shrinkit.ThreadRecord; /** * Test some LZW/1 format streams. @@ -22,26 +16,6 @@ public class NufxLzw1Test extends TestCaseHelper { assertEquals(expected, actual); assertTrue(is.isCrcValid()); } - - public void testAppleIIShk() throws IOException { - NuFileArchive archive = new NuFileArchive(getClass().getResourceAsStream("APPLE.II-LZW1.SHK")); - List blocks = archive.getHeaderBlocks(); - HeaderBlock block = blocks.get(0); // only one file - if (block.getFilename() != null) System.out.printf("\n\n%s\n\n", block.getFilename()); - List records = block.getThreadRecords(); - for (ThreadRecord record : records) { - if (record.getThreadKind() == ThreadKind.FILENAME) { - System.out.printf("\n\n%s\n\n", record.getText()); - } - long bytes = record.getThreadEof(); - if (record.getThreadKind() == ThreadKind.DATA_FORK) { - NufxLzw1InputStream is = new NufxLzw1InputStream(new LittleEndianByteInputStream(record.getRawInputStream())); - while ( bytes-- > 0 ) { - System.out.print((char)is.read()); - } - } - } - } private byte[] getTextFileLzw1StreamData() { diff --git a/test_src/com/webcodepro/shrinkit/io/NufxLzw2Test.java b/test_src/com/webcodepro/shrinkit/io/NufxLzw2Test.java deleted file mode 100644 index e7dfb56..0000000 --- a/test_src/com/webcodepro/shrinkit/io/NufxLzw2Test.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.webcodepro.shrinkit.io; - -import java.io.IOException; -import java.util.List; - -import junit.framework.TestCase; - -import com.webcodepro.shrinkit.HeaderBlock; -import com.webcodepro.shrinkit.NuFileArchive; -import com.webcodepro.shrinkit.ThreadKind; -import com.webcodepro.shrinkit.ThreadRecord; - -public class NufxLzw2Test extends TestCase { - public void testPascalFile() throws IOException { - NuFileArchive archive = new NuFileArchive(getClass().getResourceAsStream("APPLE.II-LZW2.SHK")); - List blocks = archive.getHeaderBlocks(); - HeaderBlock block = blocks.get(0); - if (block.getFilename() != null) System.out.printf("\n\n%s\n\n", block.getFilename()); - List records = block.getThreadRecords(); - for (ThreadRecord record : records) { - if (record.getThreadKind() == ThreadKind.FILENAME) { - System.out.printf("\n\n%s\n\n", record.getText()); - } - long bytes = record.getThreadEof(); - if (record.getThreadKind() == ThreadKind.DATA_FORK) { - NufxLzw2InputStream is = new NufxLzw2InputStream(new LittleEndianByteInputStream(record.getRawInputStream())); - while ( bytes-- > 0 ) { - System.out.print((char)is.read()); - } - } - } - } -} diff --git a/test_src/com/webcodepro/shrinkit/io/NufxLzwTest.java b/test_src/com/webcodepro/shrinkit/io/NufxLzwTest.java new file mode 100644 index 0000000..e0c7e04 --- /dev/null +++ b/test_src/com/webcodepro/shrinkit/io/NufxLzwTest.java @@ -0,0 +1,71 @@ +package com.webcodepro.shrinkit.io; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +import junit.framework.TestCase; + +import com.webcodepro.shrinkit.HeaderBlock; +import com.webcodepro.shrinkit.NuFileArchive; +import com.webcodepro.shrinkit.ThreadRecord; + +/** + * Compare actual decompressed content against the expected content. + * This is a very broad test of the NuFX archive format. + * + * @author robgreene@users.sourceforge.net + */ +public class NufxLzwTest extends TestCase { + public void testNufxLzw1() throws IOException { + check("APPLE.II-LZW1.SHK", "APPLE.II", "APPLE.II.txt"); + } + public void testNufxLzw2() throws IOException { + check("APPLE.II-LZW2.SHK", "APPLE.II", "APPLE.II.txt"); + } + + public void testLargerFilesNufxLzw1() throws IOException { + check("PRODOS.MSTR-LZW1.SHK", "SYSUTIL.SYSTEM", "SYSUTIL.SYSTEM.bin"); + check("PRODOS.MSTR-LZW1.SHK", "UTIL.0", "UTIL.0.bin"); + check("PRODOS.MSTR-LZW1.SHK", "UTIL.1", "UTIL.1.bin"); + check("PRODOS.MSTR-LZW1.SHK", "UTIL.2", "UTIL.2.bin"); + } + public void testLargerFilesNufxLzw2() throws IOException { + check("PRODOS.MSTR-LZW2.SHK", "SYSUTIL.SYSTEM", "SYSUTIL.SYSTEM.bin"); + check("PRODOS.MSTR-LZW2.SHK", "UTIL.0", "UTIL.0.bin"); + check("PRODOS.MSTR-LZW2.SHK", "UTIL.1", "UTIL.1.bin"); + check("PRODOS.MSTR-LZW2.SHK", "UTIL.2", "UTIL.2.bin"); + } + + /** + * Given details about an archive file and it's expected contents, locate that + * file and then check it against the contents. + */ + protected void check(String archiveName, String archiveFile, String expectedContentFile) throws IOException { + NuFileArchive archive = new NuFileArchive(getClass().getResourceAsStream(archiveName)); + List headers = archive.getHeaderBlocks(); + String actual = null; + for (HeaderBlock header : headers) { + if (archiveFile.equals(header.getFilename())) { + ThreadRecord r = header.getDataForkInputStream(); + long bytes = r.getThreadEof(); + StringBuffer buf = new StringBuffer(); + InputStream is = r.getInputStream(); + while ( bytes-- > 0 ) { + buf.append((char)is.read()); + } + actual = buf.toString(); + is.close(); + break; + } + } + InputStream is = getClass().getResourceAsStream(expectedContentFile); + StringBuffer buf = new StringBuffer(); + int ch; + while ( (ch = is.read()) != -1 ) { + buf.append((char)ch); + } + String expected = buf.toString(); + assertEquals(expected, actual); + } +} diff --git a/test_src/com/webcodepro/shrinkit/io/PRODOS.MSTR-LZW1.SHK b/test_src/com/webcodepro/shrinkit/io/PRODOS.MSTR-LZW1.SHK new file mode 100644 index 0000000..53701eb Binary files /dev/null and b/test_src/com/webcodepro/shrinkit/io/PRODOS.MSTR-LZW1.SHK differ diff --git a/test_src/com/webcodepro/shrinkit/io/PRODOS.MSTR-LZW2.SHK b/test_src/com/webcodepro/shrinkit/io/PRODOS.MSTR-LZW2.SHK new file mode 100644 index 0000000..62e7a92 Binary files /dev/null and b/test_src/com/webcodepro/shrinkit/io/PRODOS.MSTR-LZW2.SHK differ diff --git a/test_src/com/webcodepro/shrinkit/io/SYSUTIL.SYSTEM.bin b/test_src/com/webcodepro/shrinkit/io/SYSUTIL.SYSTEM.bin new file mode 100644 index 0000000..bd1685f Binary files /dev/null and b/test_src/com/webcodepro/shrinkit/io/SYSUTIL.SYSTEM.bin differ diff --git a/test_src/com/webcodepro/shrinkit/io/UTIL.0.bin b/test_src/com/webcodepro/shrinkit/io/UTIL.0.bin new file mode 100644 index 0000000..ff7d869 Binary files /dev/null and b/test_src/com/webcodepro/shrinkit/io/UTIL.0.bin differ diff --git a/test_src/com/webcodepro/shrinkit/io/UTIL.1.bin b/test_src/com/webcodepro/shrinkit/io/UTIL.1.bin new file mode 100644 index 0000000..d36863a Binary files /dev/null and b/test_src/com/webcodepro/shrinkit/io/UTIL.1.bin differ diff --git a/test_src/com/webcodepro/shrinkit/io/UTIL.2.bin b/test_src/com/webcodepro/shrinkit/io/UTIL.2.bin new file mode 100644 index 0000000..5302555 Binary files /dev/null and b/test_src/com/webcodepro/shrinkit/io/UTIL.2.bin differ