mirror of
https://github.com/AppleCommander/ShrinkItArchive.git
synced 2026-10-22 00:02:30 +00:00
Updating to Java 25-isms. Also accepting multiple IDE refactorings.
This commit is contained in:
1 parent
a7f72b2278
commit
88c47e63fc
29 files changed
+248
-304
No files matched your search
@@ -65,7 +65,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
# See: https://docs.github.com/en/actions/concepts/runners/about-github-hosted-runners#overview-of-github-hosted-runners
|
||||
# ... and follow the links to the related Github repositories
|
||||
# ... and follow the links to the related GitHub repositories
|
||||
include:
|
||||
- label: ubuntu-latest
|
||||
os: linux
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# ShrinkIt Archive Library for Java
|
||||
|
||||
This project is a direct off-shoot of the AppleCommander project and implements a pure Java API to manipulate ShrinkIt (NuFX)
|
||||
archives ([Apple File Type Note $E0/8002](http://www.apple2online.com/web_documents/ft_e0.8002_shrinkit.pdf)).
|
||||
archives ([Apple File Type Note $E0/8002](https://web.archive.org/web/20200218012014/https://apple2online.com/web_documents/ft_e0.8002_shrinkit.pdf)).
|
||||
|
||||
*Developers*
|
||||
|
||||
Being new to Gradle, all of the Maven release components are in the `build.gradle` file. More a warning than anything, just in case it causes issues. Please make more portable and submit a PR.
|
||||
Being new to Gradle, all the Maven release components are in the `build.gradle` file. More a warning than anything, just in case it causes issues. Please make more portable and submit a PR.
|
||||
+1
-1
@@ -5,5 +5,5 @@ platform=$(uname | tr '[:upper:]' '[:lower:]' | sed 's/darwin/macosx/')
|
||||
machine=$(uname -m | tr '[:upper:]' '[:lower:]' | sed 's/arm64/aarch64/')
|
||||
|
||||
# These are useful aliases while developing
|
||||
alias nufxscan="java -jar ${PWD}/build/libs/ShrinkItArchive-*.jar"
|
||||
alias nufxscan="java -jar ${PWD}/$(find build/libs/ -iname '*.jar' -not -iname '*sources*' -not -iname '*javadoc*')"
|
||||
alias nufxscann="${PWD}/build/native/nativeCompile/NufxScan-*"
|
||||
@@ -56,7 +56,7 @@ public class HeaderBlock {
|
||||
private String filename;
|
||||
private String rawFilename;
|
||||
private long headerSize = 0;
|
||||
private List<ThreadRecord> threads = new ArrayList<ThreadRecord>();
|
||||
private List<ThreadRecord> threads = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Create the Header Block. This is done dynamically since
|
||||
@@ -65,7 +65,7 @@ public class HeaderBlock {
|
||||
public HeaderBlock(LittleEndianByteInputStream bs) throws IOException {
|
||||
int type = bs.seekFileType(4);
|
||||
if (type == 0) {
|
||||
throw new IOException("Unable to decode this archive."); // FIXME - NLS
|
||||
throw new IOException("Unable to decode this archive.");
|
||||
}
|
||||
headerCrc = bs.readWord();
|
||||
attribCount = bs.readWord();
|
||||
@@ -170,7 +170,7 @@ public class HeaderBlock {
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate a ThreadRecord by it's ThreadKind.
|
||||
* Locate a ThreadRecord by its ThreadKind.
|
||||
*/
|
||||
protected ThreadRecord findThreadRecord(ThreadKind tk) {
|
||||
for (ThreadRecord r : threads) {
|
||||
@@ -186,20 +186,13 @@ public class HeaderBlock {
|
||||
* Due to some oddities, breaking apart by byte value...
|
||||
*/
|
||||
public String getFileSystemSeparator() {
|
||||
switch (getFileSysInfo() & 0xff) {
|
||||
case 0xaf:
|
||||
case 0x2f:
|
||||
return "/";
|
||||
case 0x3a:
|
||||
case 0xba:
|
||||
case 0x3f: // Note that $3F is per the documentation(!)
|
||||
return ":";
|
||||
case 0x5c:
|
||||
case 0xdc:
|
||||
return "\\";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
return switch (getFileSysInfo() & 0xff) {
|
||||
case 0xaf, 0x2f -> "/";
|
||||
// Note that $3F is per the documentation(!)
|
||||
case 0x3a, 0xba, 0x3f -> ":";
|
||||
case 0x5c, 0xdc -> "\\";
|
||||
default -> "";
|
||||
};
|
||||
}
|
||||
|
||||
public long getUncompressedSize() {
|
||||
@@ -221,6 +214,24 @@ public class HeaderBlock {
|
||||
return size;
|
||||
}
|
||||
|
||||
public String getFileSysIdText() {
|
||||
return switch (getFileSysInfo()) {
|
||||
case 1 -> "ProDOS/SOS";
|
||||
case 2 -> "DOS 3.3";
|
||||
case 3 -> "DOS 3.2";
|
||||
case 4 -> "Apple II Pascal";
|
||||
case 5 -> "Macintosh HFS";
|
||||
case 6 -> "Macintosh MFS";
|
||||
case 7 -> "Lisa File System";
|
||||
case 8 -> "Apple CP/M";
|
||||
case 10 -> "MS-DOS";
|
||||
case 11 -> "High Sierra";
|
||||
case 12 -> "ISO 9660";
|
||||
case 13 -> "AppleShare";
|
||||
default -> "Reserved";
|
||||
};
|
||||
}
|
||||
|
||||
// GENERATED CODE
|
||||
|
||||
public int getHeaderCrc() {
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* ShrinkItArchive
|
||||
* Copyright (C) 2026 Rob Greene
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see
|
||||
* <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.applecommander.shrinkit;
|
||||
|
||||
/**
|
||||
* Handy reference for constant values based on header blocks.
|
||||
* These are likely user interface oriented.
|
||||
*
|
||||
* @author rob
|
||||
*/
|
||||
public interface HeaderConstants {
|
||||
public static final String[] FILE_SYS = {
|
||||
"Reserved", "ProDOS/SOS", "DOS 3.3", "DOS 3.2", "Apple II Pascal", "Macintosh HFS", "Macintosh MFS",
|
||||
"Lisa File System", "Apple CP/M", "Reserved", "MS-DOS", "High Sierra", "ISO 9660", "AppleShare"
|
||||
};
|
||||
}
|
||||
@@ -38,7 +38,7 @@ import org.applecommander.shrinkit.io.LittleEndianByteInputStream;
|
||||
public class MasterHeaderBlock {
|
||||
private static final int MASTER_HEADER_LENGTH = 48;
|
||||
private int masterCrc;
|
||||
private boolean validCrc;
|
||||
private final boolean validCrc;
|
||||
private long totalRecords;
|
||||
private Date archiveCreateWhen;
|
||||
private Date archiveModWhen;
|
||||
@@ -57,12 +57,12 @@ public class MasterHeaderBlock {
|
||||
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
|
||||
throw new IOException("This is actually a Binary II archive with multiple files in it.");
|
||||
}
|
||||
fileType = bs.seekFileType();
|
||||
}
|
||||
if (!(fileType == NuFileArchive.NUFILE_ARCHIVE)) {
|
||||
throw new IOException("Unable to decode this archive."); // FIXME - NLS
|
||||
throw new IOException("Unable to decode this archive.");
|
||||
}
|
||||
masterCrc = bs.readWord();
|
||||
bs.resetCrc(); // CRC is computed from this point to the end of the header
|
||||
|
||||
@@ -37,12 +37,12 @@ public class NuFileArchive {
|
||||
VERSION = NuFileArchive.class.getPackage().getImplementationVersion();
|
||||
}
|
||||
|
||||
private MasterHeaderBlock master;
|
||||
private List<HeaderBlock> headers;
|
||||
private final MasterHeaderBlock master;
|
||||
private final List<HeaderBlock> headers;
|
||||
private long totalSize = 0;
|
||||
|
||||
/**
|
||||
* Need to enumerate some basic sub-types of archives.
|
||||
* Need to enumerate some basic subtypes of archives.
|
||||
*/
|
||||
public static final int NUFILE_ARCHIVE = 1;
|
||||
public static final int NUFX_ARCHIVE = 2;
|
||||
@@ -54,7 +54,7 @@ public class NuFileArchive {
|
||||
public NuFileArchive(InputStream inputStream) throws IOException {
|
||||
LittleEndianByteInputStream bs = new LittleEndianByteInputStream(inputStream);
|
||||
master = new MasterHeaderBlock(bs);
|
||||
headers = new ArrayList<HeaderBlock>();
|
||||
headers = new ArrayList<>();
|
||||
for (int i=0; i<master.getTotalRecords(); i++) {
|
||||
HeaderBlock header = new HeaderBlock(bs);
|
||||
header.readThreads(bs);
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
package org.applecommander.shrinkit;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -40,16 +39,18 @@ public class NufxScan {
|
||||
|
||||
private static boolean verify = false;
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
static void main(String[] args) throws IOException {
|
||||
if (args.length == 0) {
|
||||
System.out.println("Usage: nufxscan [ -v | --verify ] <path> ...");
|
||||
System.out.println();
|
||||
System.out.println("Scan NuFX/Shrinkit archives. Please include at least one path name.");
|
||||
System.out.println();
|
||||
System.out.println("Options:");
|
||||
System.out.println(" -v Show version and exit.");
|
||||
System.out.println(" --verify Enable verify flag. Scan becomes quiet and only reports files that");
|
||||
System.out.println(" the library cannot process. Helpful in identifying bugs.");
|
||||
System.out.println("""
|
||||
Usage: nufxscan [ -v | --verify ] <path> ...
|
||||
|
||||
Scan NuFX/Shrinkit archives. Please include at least one path name.
|
||||
|
||||
Options:
|
||||
-v Show version and exit.
|
||||
--verify Enable verify flag. Scan becomes quiet and only reports files that
|
||||
the library cannot process. Helpful in identifying bugs.
|
||||
""");
|
||||
} else if (args.length == 1 && "-v".equals(args[0])) {
|
||||
System.out.printf("ShrinkIt Library version %s\n", NuFileArchive.VERSION);
|
||||
} else {
|
||||
@@ -77,25 +78,27 @@ public class NufxScan {
|
||||
|
||||
private static void scanDirectory(File directory) throws IOException {
|
||||
if (!directory.isDirectory()) {
|
||||
throw new IllegalArgumentException("'" + directory.toString() + "' is not a directory");
|
||||
throw new IllegalArgumentException("'" + directory + "' is not a directory");
|
||||
}
|
||||
File[] files = directory.listFiles(new FileFilter() {
|
||||
public boolean accept(File file) {
|
||||
boolean isSHK = file.getName().toLowerCase().endsWith(".shk");
|
||||
boolean isSDK = file.getName().toLowerCase().endsWith(".sdk");
|
||||
boolean isDirectory = file.isDirectory();
|
||||
return isSHK || isSDK || isDirectory;
|
||||
}
|
||||
});
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
scanDirectory(file);
|
||||
} else {
|
||||
if (verify) {
|
||||
verifyArchive(file);
|
||||
}
|
||||
else {
|
||||
displayArchive(file);
|
||||
File[] files = directory.listFiles(file -> {
|
||||
boolean isSHK = file.getName().toLowerCase().endsWith(".shk");
|
||||
boolean isSDK = file.getName().toLowerCase().endsWith(".sdk");
|
||||
boolean isDirectory = file.isDirectory();
|
||||
return isSHK || isSDK || isDirectory;
|
||||
});
|
||||
if (files == null) {
|
||||
System.out.println("No ShrinkIt archives found.");
|
||||
}
|
||||
else {
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
scanDirectory(file);
|
||||
} else {
|
||||
if (verify) {
|
||||
verifyArchive(file);
|
||||
} else {
|
||||
displayArchive(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,7 +107,7 @@ public class NufxScan {
|
||||
private static void displayArchive(File archive) throws IOException {
|
||||
System.out.printf("Details for %s\n\n", archive.getAbsoluteFile());
|
||||
try (InputStream is = new FileInputStream(archive)) {
|
||||
NuFileArchive a = null;
|
||||
NuFileArchive a;
|
||||
try {
|
||||
a = new NuFileArchive(is);
|
||||
} catch (Throwable t) {
|
||||
@@ -139,7 +142,7 @@ public class NufxScan {
|
||||
threadsPrinted++;
|
||||
}
|
||||
System.out.printf("%08x %08x ", origSize, compSize);
|
||||
if (filename == null || filename.length() == 0) {
|
||||
if (filename == null || filename.isEmpty()) {
|
||||
filename = "<Unknown>";
|
||||
}
|
||||
System.out.println(filename);
|
||||
@@ -155,7 +158,7 @@ public class NufxScan {
|
||||
|
||||
private static void verifyArchive(File archive) throws IOException {
|
||||
try (InputStream is = new FileInputStream(archive)) {
|
||||
NuFileArchive a = null;
|
||||
NuFileArchive a;
|
||||
try {
|
||||
a = new NuFileArchive(is);
|
||||
} catch (Throwable t) {
|
||||
|
||||
@@ -30,13 +30,12 @@ public enum 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);
|
||||
}
|
||||
return switch (threadClass) {
|
||||
case 0x0000 -> MESSAGE;
|
||||
case 0x0001 -> CONTROL;
|
||||
case 0x0002 -> DATA;
|
||||
case 0x0003 -> FILENAME;
|
||||
default -> throw new IllegalArgumentException("Unknown thread_class of " + threadClass);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ public enum ThreadFormat {
|
||||
private final int threadFormat;
|
||||
private final String name;
|
||||
|
||||
private ThreadFormat(int threadFormat, String name) {
|
||||
ThreadFormat(int threadFormat, String name) {
|
||||
this.threadFormat = threadFormat;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@@ -31,29 +31,31 @@ public enum 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);
|
||||
}
|
||||
return switch (threadClass) {
|
||||
case MESSAGE -> switch (threadKind) {
|
||||
case 0x0000 -> ASCII_TEXT;
|
||||
case 0x0001 -> ALLOCATED_SPACE;
|
||||
case 0x0002 -> APPLE_IIGS_ICON;
|
||||
default -> throw new IllegalArgumentException("Unknown thread_kind " + threadKind
|
||||
+ " for message thread_class of " + threadClass);
|
||||
};
|
||||
case CONTROL -> {
|
||||
if (threadKind == 0x0000) yield CREATE_DIRECTORY;
|
||||
throw new IllegalArgumentException("Unknown thread_kind " + threadKind
|
||||
+ " for control thread_class of " + threadClass);
|
||||
}
|
||||
case DATA -> switch (threadKind) {
|
||||
case 0x0000 -> DATA_FORK;
|
||||
case 0x0001 -> DISK_IMAGE;
|
||||
case 0x0002 -> RESOURCE_FORK;
|
||||
default -> throw new IllegalArgumentException("Unknown thread_kind " + threadKind
|
||||
+ " for data thread_class of " + threadClass);
|
||||
};
|
||||
case FILENAME -> {
|
||||
if (threadKind == 0x0000) yield FILENAME;
|
||||
throw new IllegalArgumentException("Unknown thread_kind " + threadKind
|
||||
+ " for filename thread_class of " + threadClass);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ import org.applecommander.shrinkit.io.NufxLzw2InputStream;
|
||||
* <p>
|
||||
* Depending on the type of thread, the data may be text. If so,
|
||||
* <code>isText</code> will return true and <code>getText</code>
|
||||
* will return the string. Otherwise the data should be read through
|
||||
* will return the string. Otherwise, the data should be read through
|
||||
* one of the <code>InputStream</code> options.
|
||||
*
|
||||
* @author robgreene@users.sourceforge.net
|
||||
@@ -72,8 +72,7 @@ public class ThreadRecord {
|
||||
/* 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 &&
|
||||
} else if (hb.getStorageType() == 256 && hb.getExtraType() == 280 &&
|
||||
hb.getFileSysId() == 2 ) { // FileSysDOS33
|
||||
/*
|
||||
* Fix for less-common ShrinkIt problem: looks like an old
|
||||
@@ -122,16 +121,13 @@ public class ThreadRecord {
|
||||
* 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!");
|
||||
}
|
||||
return switch (threadFormat) {
|
||||
case UNCOMPRESSED -> getRawInputStream();
|
||||
case DYNAMIC_LZW1 -> new NufxLzw1InputStream(new LittleEndianByteInputStream(getRawInputStream()));
|
||||
case DYNAMIC_LZW2 -> new NufxLzw2InputStream(new LittleEndianByteInputStream(getRawInputStream()));
|
||||
default -> throw new IOException("The thread format " + threadFormat
|
||||
+ " does not have an InputStream associated with it!");
|
||||
};
|
||||
}
|
||||
|
||||
// GENERATED CODE
|
||||
|
||||
@@ -28,7 +28,7 @@ public interface BitConstants {
|
||||
/**
|
||||
* The low-tech way to compute a bit mask. Allowing up to 16 bits at this time.
|
||||
*/
|
||||
public static final int[] BIT_MASKS = new int[] {
|
||||
int[] BIT_MASKS = new int[] {
|
||||
0x0000, 0x0001, 0x0003, 0x0007, 0x000f,
|
||||
0x001f, 0x003f, 0x007f, 0x00ff, 0x01ff,
|
||||
0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff,
|
||||
|
||||
@@ -21,20 +21,18 @@ package org.applecommander.shrinkit.io;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* The BitInputStream allows varying bit sizes to be pulled out of the
|
||||
* wrapped InputStream. This is useful for LZW type compression algorithms
|
||||
* where 9-12 bit codes are used instead of the 8-bit byte.
|
||||
* <p>
|
||||
* Warning: The <code>read(byte[])</code> and <code>read(byte[], int, int)</code>
|
||||
* methods of <code>InputStream</code> will not work appropriately with any
|
||||
* bit size > 8 bits.
|
||||
*
|
||||
* @author robgreene@users.sourceforge.net
|
||||
*/
|
||||
/// The BitInputStream allows varying bit sizes to be pulled out of the
|
||||
/// wrapped InputStream. This is useful for LZW type compression algorithms
|
||||
/// where 9-12 bit codes are used instead of the 8-bit byte.
|
||||
///
|
||||
/// 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;
|
||||
private final 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 <code>read()</code> request. */
|
||||
|
||||
@@ -21,20 +21,18 @@ package org.applecommander.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.
|
||||
* <p>
|
||||
* Warning: The <code>write(byte[])</code> and <code>write(byte[], int, int)</code>
|
||||
* methods of <code>OutputStream</code> will not work appropriately with any
|
||||
* bit size > 8 bits.
|
||||
*
|
||||
* @author robgreene@users.sourceforge.net
|
||||
*/
|
||||
/// 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;
|
||||
private final 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 <code>write(int)</code> request. */
|
||||
@@ -45,7 +43,7 @@ public class BitOutputStream extends OutputStream implements BitConstants {
|
||||
private int bitsOfData = 0;
|
||||
|
||||
/**
|
||||
* Create a BitOutpuStream wrapping the given <code>OutputStream</code>
|
||||
* Create a BitOutputStream wrapping the given <code>OutputStream</code>
|
||||
* and writing the number of bits specified.
|
||||
*/
|
||||
public BitOutputStream(OutputStream os, int startingNumberOfBits) {
|
||||
@@ -54,7 +52,7 @@ public class BitOutputStream extends OutputStream implements BitConstants {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of bits to be write with each call to <code>write(int)</code>.
|
||||
* Set the number of bits to be written with each call to <code>write(int)</code>.
|
||||
*/
|
||||
public void setRequestedNumberOfBits(int numberOfBits) {
|
||||
this.requestedNumberOfBits = numberOfBits;
|
||||
|
||||
@@ -28,27 +28,27 @@ package org.applecommander.shrinkit.io;
|
||||
*/
|
||||
public interface ByteConstants {
|
||||
/** Master Header Block identifier "magic" bytes. */
|
||||
public static final byte[] NUFILE_ID = { 0x4e, (byte)0xf5, 0x46, (byte)0xe9, 0x6c, (byte)0xe5 };
|
||||
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 };
|
||||
byte[] NUFX_ID = { 0x4e, (byte)0xf5, 0x46, (byte)0xd8 };
|
||||
/** Binary II identifier "magic" bytes. */
|
||||
public static final byte[] BXY_ID = { 0x0a, 0x47, 0x4c };
|
||||
byte[] BXY_ID = { 0x0a, 0x47, 0x4c };
|
||||
/** Apple IIgs Toolbox TimeRec seconds byte position. */
|
||||
public static final int TIMEREC_SECOND = 0;
|
||||
int TIMEREC_SECOND = 0;
|
||||
/** Apple IIgs Toolbox TimeRec seconds byte position. */
|
||||
public static final int TIMEREC_MINUTE = 1;
|
||||
int TIMEREC_MINUTE = 1;
|
||||
/** Apple IIgs Toolbox TimeRec minutes byte position. */
|
||||
public static final int TIMEREC_HOUR = 2;
|
||||
int TIMEREC_HOUR = 2;
|
||||
/** Apple IIgs Toolbox TimeRec hours byte position. */
|
||||
public static final int TIMEREC_YEAR = 3;
|
||||
int TIMEREC_YEAR = 3;
|
||||
/** Apple IIgs Toolbox TimeRec year byte position. */
|
||||
public static final int TIMEREC_DAY = 4;
|
||||
int TIMEREC_DAY = 4;
|
||||
/** Apple IIgs Toolbox TimeRec day byte position. */
|
||||
public static final int TIMEREC_MONTH = 5;
|
||||
int TIMEREC_MONTH = 5;
|
||||
/** Apple IIgs Toolbox TimeRec weekday (Mon, Tue, etc) byte position. */
|
||||
public static final int TIMEREC_WEEKDAY = 7;
|
||||
int TIMEREC_WEEKDAY = 7;
|
||||
/** Apple IIgs Toolbox TimeRec length. */
|
||||
public static final int TIMEREC_LENGTH = 8;
|
||||
int TIMEREC_LENGTH = 8;
|
||||
/** A null TimeRec */
|
||||
public static final byte[] TIMEREC_NULL = new byte[TIMEREC_LENGTH];
|
||||
byte[] TIMEREC_NULL = new byte[TIMEREC_LENGTH];
|
||||
}
|
||||
@@ -33,9 +33,9 @@ import org.applecommander.shrinkit.NuFileArchive;
|
||||
* @author robgreene@users.sourceforge.net
|
||||
*/
|
||||
public class LittleEndianByteInputStream extends InputStream implements ByteConstants {
|
||||
private InputStream inputStream;
|
||||
private final InputStream inputStream;
|
||||
private long bytesRead = 0;
|
||||
private CRC16 crc = new CRC16();
|
||||
private final CRC16 crc = new CRC16();
|
||||
|
||||
/**
|
||||
* Construct a LittleEndianByteInputStream from an InputStream.
|
||||
@@ -150,7 +150,7 @@ public class LittleEndianByteInputStream extends InputStream implements ByteCons
|
||||
long b = readByte();
|
||||
long c = readByte();
|
||||
long d = readByte();
|
||||
return (long)(a | b<<8 | c<<16 | d<<24);
|
||||
return a | b<<8 | c<<16 | d<<24;
|
||||
}
|
||||
/**
|
||||
* Read the TimeRec into a Java Date object.
|
||||
|
||||
@@ -33,9 +33,9 @@ import org.applecommander.shrinkit.CRC16;
|
||||
* @author robgreene@users.sourceforge.net
|
||||
*/
|
||||
public class LittleEndianByteOutputStream extends OutputStream implements ByteConstants {
|
||||
private OutputStream outputStream;
|
||||
private final OutputStream outputStream;
|
||||
private long bytesWritten = 0;
|
||||
private CRC16 crc = new CRC16();
|
||||
private final CRC16 crc = new CRC16();
|
||||
|
||||
/**
|
||||
* Construct a LittleEndianByteOutputStream from an OutputStream.
|
||||
@@ -50,6 +50,7 @@ public class LittleEndianByteOutputStream extends OutputStream implements ByteCo
|
||||
public void write(int b) throws IOException {
|
||||
outputStream.write(b);
|
||||
crc.update(b);
|
||||
bytesWritten++;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,7 +87,7 @@ public class LittleEndianByteOutputStream extends OutputStream implements ByteCo
|
||||
* @see <a href="http://www.nulib.com/library/nufx-addendum.htm">NuFX addendum</a>
|
||||
*/
|
||||
public void writeDate(Date date) throws IOException {
|
||||
byte[] data = null;
|
||||
byte[] data;
|
||||
if (date == null) {
|
||||
data = TIMEREC_NULL;
|
||||
} else {
|
||||
|
||||
@@ -25,29 +25,24 @@ 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
|
||||
* <code>BitInputStream</code> bit sizes.
|
||||
*
|
||||
* @author robgreene@users.sourceforge.net
|
||||
*/
|
||||
/// 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 final BitInputStream is;
|
||||
private List<int[]> dictionary;
|
||||
private Queue<Integer> outputBuffer = new ConcurrentLinkedQueue<Integer>();
|
||||
private final Queue<Integer> 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 <code>LzwInputStream</code> based on the given
|
||||
* <code>BitInputStream</code>.
|
||||
* @see BitInputStream
|
||||
*/
|
||||
/// Create the `LzwInputStream` based on the given `BitInputStream`.
|
||||
/// @see BitInputStream
|
||||
public LzwInputStream(BitInputStream is) {
|
||||
this.is = is;
|
||||
}
|
||||
@@ -72,7 +67,7 @@ public class LzwInputStream extends InputStream {
|
||||
if (dictionary == null) {
|
||||
is.setRequestedNumberOfBits(9);
|
||||
// Setup default dictionary for all bytes
|
||||
dictionary = new ArrayList<int[]>();
|
||||
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
|
||||
}
|
||||
@@ -111,7 +106,6 @@ public class LzwInputStream extends InputStream {
|
||||
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];
|
||||
|
||||
@@ -32,8 +32,8 @@ import org.applecommander.shrinkit.CRC16;
|
||||
* @author robgreene@users.sourceforge.net
|
||||
*/
|
||||
public class LzwOutputStream extends OutputStream {
|
||||
private BitOutputStream os;
|
||||
private Map<ByteArray,Integer> dictionary = new HashMap<ByteArray,Integer>();
|
||||
private final BitOutputStream os;
|
||||
private final Map<ByteArray,Integer> dictionary = new HashMap<>();
|
||||
private int[] w = new int[0];
|
||||
private int nextCode = 0x101;
|
||||
|
||||
@@ -42,11 +42,11 @@ public class LzwOutputStream extends OutputStream {
|
||||
*
|
||||
* @author robgreene@users.sourceforge.net
|
||||
*/
|
||||
private class ByteArray {
|
||||
private static class ByteArray {
|
||||
/** Data being managed. */
|
||||
private int[] data;
|
||||
private final int[] data;
|
||||
/** The computed hash code -- CRC-16 for lack of imagination. */
|
||||
private int hashCode;
|
||||
private final int hashCode;
|
||||
|
||||
public ByteArray(int d) {
|
||||
this(new int[] { d });
|
||||
@@ -58,8 +58,10 @@ public class LzwOutputStream extends OutputStream {
|
||||
hashCode = (int)crc.getValue();
|
||||
}
|
||||
public boolean equals(Object obj) {
|
||||
ByteArray ba = (ByteArray)obj;
|
||||
if (data.length != ba.data.length) return false;
|
||||
if (!(obj instanceof ByteArray ba)) {
|
||||
return false;
|
||||
}
|
||||
if (data.length != ba.data.length) return false;
|
||||
for (int i=0; i<data.length; i++) {
|
||||
if (data[i] != ba.data[i]) return false;
|
||||
}
|
||||
|
||||
@@ -23,49 +23,33 @@ import java.io.InputStream;
|
||||
|
||||
import org.applecommander.shrinkit.CRC16;
|
||||
|
||||
/**
|
||||
* The <code>NufxLzw1InputStream</code> reads a data fork or
|
||||
* resource fork written in the NuFX LZW/1 format.
|
||||
* <p>
|
||||
* The layout of the LZW/1 data is as follows:
|
||||
* <table border="0">
|
||||
* <tr>
|
||||
* <th colspan="3">"Fork" Header</th>
|
||||
* </tr><tr>
|
||||
* <td>+0</td>
|
||||
* <td>Word</td>
|
||||
* <td>CRC-16 of the uncompressed data within the thread</td>
|
||||
* </tr><tr>
|
||||
* <td>+2</td>
|
||||
* <td>Byte</td>
|
||||
* <td>Low-level volume number use to format 5.25" disks</td>
|
||||
* </tr><tr>
|
||||
* <td>+3</td>
|
||||
* <td>Byte</td>
|
||||
* <td>RLE character used to decode this thread</td>
|
||||
* </tr><tr>
|
||||
* <th colspan="3">Each subsequent 4K chunk of data</th>
|
||||
* </tr><tr>
|
||||
* <td>+0</td>
|
||||
* <td>Word</td>
|
||||
* <td>Length after RLE compression (if RLE is not used, length
|
||||
* will be 4096</td>
|
||||
* </tr><tr>
|
||||
* <td>+2</td>
|
||||
* <td>Byte</td>
|
||||
* <td>A $01 indicates LZW applied to this chunk; $00 that LZW
|
||||
* <b>was not</b> applied to this chunk</td>
|
||||
* </tr>
|
||||
* <table>
|
||||
* <p>
|
||||
* Note that the LZW string table is <em>cleared</em> after
|
||||
* every chunk.
|
||||
*
|
||||
* @author robgreene@users.sourceforge.net
|
||||
*/
|
||||
/// 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
|
||||
///
|
||||
/// | Offset | Size | Description |
|
||||
/// | :--- | :--- | :--- |
|
||||
/// | +0 | Word | CRC-16 of the uncompressed data within the thread. |
|
||||
/// | +2 | Byte | Low-level volume number use to format 5.25" disks. |
|
||||
/// | +3 | Byte | RLE character used to decode this thread. |
|
||||
///
|
||||
/// * Each subsequent 4K chunk of data:
|
||||
///
|
||||
/// | Offset | Size | Description |
|
||||
/// | :--- | :--- | :--- |
|
||||
/// | +0 | Word | Length after RLE compression (if RLE is not used, length will be 4096). |
|
||||
/// | +2 | Byte | A `$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;
|
||||
private final LittleEndianByteInputStream dataStream;
|
||||
/** Used for an LZW-only <code>InputStream</code>. */
|
||||
private LzwInputStream lzwStream;
|
||||
/** Used for an RLE-only <code>InputStream</code>. */
|
||||
@@ -83,7 +67,7 @@ public class NufxLzw1InputStream extends InputStream {
|
||||
/** 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();
|
||||
private final CRC16 dataCrc = new CRC16();
|
||||
|
||||
/**
|
||||
* Create the LZW/1 input stream.
|
||||
|
||||
@@ -23,46 +23,33 @@ import java.io.InputStream;
|
||||
|
||||
import org.applecommander.shrinkit.CRC16;
|
||||
|
||||
/**
|
||||
* The <code>NufxLzw2InputStream</code> reads a data fork or
|
||||
* resource fork written in the NuFX LZW/2 format.
|
||||
* <p>
|
||||
* The layout of the LZW/2 data is as follows:
|
||||
* <table border="0">
|
||||
* <tr>
|
||||
* <th colspan="3">"Fork" Header</th>
|
||||
* </tr><tr>
|
||||
* <td>+0</td>
|
||||
* <td>Byte</td>
|
||||
* <td>Low-level volume number used to format 5.25" disks</td>
|
||||
* </tr><tr>
|
||||
* <td>+1</td>
|
||||
* <td>Byte</td>
|
||||
* <td>RLE character used to decode this thread</td>
|
||||
* </tr><tr>
|
||||
* <th colspan="3">Each subsequent 4K chunk of data</th>
|
||||
* </tr><tr>
|
||||
* <td>+0</td>
|
||||
* <td>Word</td>
|
||||
* <td>Bits 0-12: Length after RLE compression<br/>
|
||||
* Bit 15: LZW flag (set to 1 if LZW used)</td>
|
||||
* </tr><tr>
|
||||
* <td>+2</td>
|
||||
* <td>Word</td>
|
||||
* <td>If LZW flag = 1, total bytes in chunk<br/>
|
||||
* Else (flag = 0) start of data</td>
|
||||
* </tr>
|
||||
* <table>
|
||||
* <p>
|
||||
* 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
|
||||
*/
|
||||
/// 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:
|
||||
///
|
||||
/// | Offset | Size | Description |
|
||||
/// | :--- | :--- | :--- |
|
||||
/// | +0 | Byte | Low-level volume number used to format 5.25" disks. |
|
||||
/// | +1 | Byte | RLE character used to decode this thread. |
|
||||
///
|
||||
/// * Each subsequent 4K chunk of data:
|
||||
///
|
||||
/// | Offset | Size | Description |
|
||||
/// | :--- | :--- | :--- |
|
||||
/// | +0 | Word | Bits 0-12: Length after RLE compression.<br/>Bit 15: LZW flag (set to 1 if LZW used). |
|
||||
/// | +2 | Word | If LZW flag = 1, total bytes in chunk.<br/>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;
|
||||
private final LittleEndianByteInputStream dataStream;
|
||||
/** Used for an LZW-only <code>InputStream</code>. */
|
||||
private LzwInputStream lzwStream;
|
||||
/** Used for an RLE-only <code>InputStream</code>. */
|
||||
@@ -78,7 +65,7 @@ public class NufxLzw2InputStream extends InputStream {
|
||||
/** 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();
|
||||
private final CRC16 dataCrc = new CRC16();
|
||||
|
||||
/**
|
||||
* Create the LZW/2 input stream.
|
||||
|
||||
@@ -31,8 +31,8 @@ import java.io.InputStream;
|
||||
* @author robgreene@users.sourceforge.net
|
||||
*/
|
||||
public class RleInputStream extends InputStream {
|
||||
private InputStream bs;
|
||||
private int escapeChar;
|
||||
private final InputStream bs;
|
||||
private final int escapeChar;
|
||||
private int repeatedByte;
|
||||
private int numBytes = -1;
|
||||
|
||||
@@ -66,5 +66,4 @@ public class RleInputStream extends InputStream {
|
||||
numBytes--;
|
||||
return repeatedByte;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -31,8 +31,8 @@ import java.io.OutputStream;
|
||||
* @author robgreene@users.sourceforge.net
|
||||
*/
|
||||
public class RleOutputStream extends OutputStream {
|
||||
private OutputStream os;
|
||||
private int escapeChar;
|
||||
private final OutputStream os;
|
||||
private final int escapeChar;
|
||||
private int repeatedByte;
|
||||
private int numBytes = -1;
|
||||
|
||||
@@ -71,7 +71,7 @@ public class RleOutputStream extends OutputStream {
|
||||
|
||||
/**
|
||||
* Flush out any remaining data.
|
||||
* If we only have 1 byte and it is <em>not</em> the repeated
|
||||
* If we only have 1 byte, and it is <em>not</em> 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.
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
package org.applecommander.shrinkit;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
@@ -39,14 +40,14 @@ public class CRC16Test {
|
||||
@Test
|
||||
public void testUpdate() throws UnsupportedEncodingException {
|
||||
CRC16 crc16 = new CRC16();
|
||||
crc16.update("123456789".getBytes("UTF-8"));
|
||||
crc16.update("123456789".getBytes(StandardCharsets.UTF_8));
|
||||
Assert.assertEquals(0x31c3, crc16.getValue());
|
||||
crc16.update("ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes("UTF-8"));
|
||||
crc16.update("ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes(StandardCharsets.UTF_8));
|
||||
Assert.assertEquals(0x92cc, crc16.getValue());
|
||||
crc16.update("abcdefghijklmnopqrstuvwxyz".getBytes("UTF-8"));
|
||||
crc16.update("abcdefghijklmnopqrstuvwxyz".getBytes(StandardCharsets.UTF_8));
|
||||
Assert.assertEquals(0xfc85, crc16.getValue());
|
||||
crc16.reset();
|
||||
crc16.update("xxx123456789xxx".getBytes("UTF-8"), 3, 9);
|
||||
crc16.update("xxx123456789xxx".getBytes(StandardCharsets.UTF_8), 3, 9);
|
||||
Assert.assertEquals(0x31c3, crc16.getValue());
|
||||
}
|
||||
|
||||
|
||||
@@ -29,8 +29,7 @@ public class TestHelper {
|
||||
private TestHelper() {
|
||||
// Prevent construction
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static void checkDate(byte[] streamData, Date actual) throws IOException {
|
||||
try (LittleEndianByteInputStream is = new LittleEndianByteInputStream(streamData)) {
|
||||
Assert.assertEquals(is.readDate(), actual);
|
||||
|
||||
@@ -106,9 +106,9 @@ public class LittleEndianByteOutputStreamTest extends TestBase {
|
||||
bs.close();
|
||||
byte[] expected = new byte[] {
|
||||
// From NuFX documentation, final revision 3
|
||||
0x00, 0x0a, 0x01, 0x58, 0x16, 0x0a, 0x00, 0x07, // 01:10:00am 10/22/1988 saturday
|
||||
0x00, 0x10, 0x0b, 0x58, 0x11, 0x0b, 0x00, 0x05, // 11:16:00am 11/17/1988 thursday
|
||||
0x00, 0x0c, 0x0d, 0x58, 0x16, 0x0a, 0x00, 0x07, // 01:12:00pm 10/22/1988 saturday
|
||||
0x00, 0x0a, 0x01, 0x58, 0x16, 0x0a, 0x00, 0x07, // 01:10:00am 10/22/1988 Saturday
|
||||
0x00, 0x10, 0x0b, 0x58, 0x11, 0x0b, 0x00, 0x05, // 11:16:00am 11/17/1988 Thursday
|
||||
0x00, 0x0c, 0x0d, 0x58, 0x16, 0x0a, 0x00, 0x07, // 01:12:00pm 10/22/1988 Saturday
|
||||
};
|
||||
assertEquals(expected, os.toByteArray());
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ public class NufxLzw1Test extends TestBase {
|
||||
try (NufxLzw1InputStream is = new NufxLzw1InputStream(new LittleEndianByteInputStream(getTextFileLzw1StreamData()))) {
|
||||
byte[] expected = getTextFileData();
|
||||
byte[] actual = new byte[expected.length];
|
||||
is.read(actual);
|
||||
int _ = is.read(actual);
|
||||
assertEquals(expected, actual);
|
||||
assertTrue(is.isCrcValid());
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ public class NufxLzwTest extends TestBase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Given details about an archive file and it's expected contents, locate that
|
||||
* Given details about an archive file, and its expected contents, locate that
|
||||
* file and then check it against the contents.
|
||||
*/
|
||||
protected void check(String archiveName, String archiveFile, String expectedContentFile) throws IOException {
|
||||
@@ -91,12 +91,14 @@ public class NufxLzwTest extends TestBase {
|
||||
}
|
||||
}
|
||||
InputStream is = getClass().getResourceAsStream(expectedContentFile);
|
||||
assert is != null;
|
||||
ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
||||
int b;
|
||||
while ( (b = is.read()) != -1 ) {
|
||||
buf.write(b);
|
||||
}
|
||||
byte[] expected = buf.toByteArray();
|
||||
assert actual != null;
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user