From 1bd7b5f431ea4ab5fc94cf28769d70f6b22bb258 Mon Sep 17 00:00:00 2001 From: Rob Greene Date: Tue, 6 Mar 2018 21:34:58 -0600 Subject: [PATCH] Merging changes that existed in AppleCommander's clone. Closes #2. --- .gitignore | 6 + .../com/webcodepro/shrinkit/HeaderBlock.java | 27 ++- .../shrinkit/MasterHeaderBlock.java | 37 +---- .../webcodepro/shrinkit/NuFileArchive.java | 21 ++- .../com/webcodepro/shrinkit/Utilities.java | 75 --------- .../webcodepro/shrinkit/io/ByteConstants.java | 2 + .../io/LittleEndianByteInputStream.java | 51 +++++- .../io/LittleEndianByteInputStreamTest.java | 154 ++++++++++-------- .../com/webcodepro/shrinkit/io/LzwTest.java | 2 +- 9 files changed, 186 insertions(+), 189 deletions(-) delete mode 100644 src/main/java/com/webcodepro/shrinkit/Utilities.java diff --git a/.gitignore b/.gitignore index 67bcc2f..ddd9ae2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,8 @@ +# Gradle .gradle/ build/ + +# Eclipse +.settings/ +.classpath +.project diff --git a/src/main/java/com/webcodepro/shrinkit/HeaderBlock.java b/src/main/java/com/webcodepro/shrinkit/HeaderBlock.java index eb71ba4..a76106a 100644 --- a/src/main/java/com/webcodepro/shrinkit/HeaderBlock.java +++ b/src/main/java/com/webcodepro/shrinkit/HeaderBlock.java @@ -37,6 +37,7 @@ public class HeaderBlock { private byte[] attribBytes; private String filename; private String rawFilename; + private long headerSize = 0; private List threads = new ArrayList(); /** @@ -44,7 +45,9 @@ public class HeaderBlock { * the Header Block size varies significantly. */ public HeaderBlock(LittleEndianByteInputStream bs) throws IOException { - bs.checkNuFxId(); + 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(); @@ -86,7 +89,10 @@ public class HeaderBlock { */ public void readThreads(LittleEndianByteInputStream bs) throws IOException { for (long l=0; l 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 index 70cb9ac..efe05db 100644 --- a/src/main/java/com/webcodepro/shrinkit/MasterHeaderBlock.java +++ b/src/main/java/com/webcodepro/shrinkit/MasterHeaderBlock.java @@ -3,6 +3,7 @@ package com.webcodepro.shrinkit; import java.io.IOException; import java.util.Date; +import com.webcodepro.shrinkit.io.ByteConstants; import com.webcodepro.shrinkit.io.LittleEndianByteInputStream; /** @@ -25,26 +26,24 @@ public class MasterHeaderBlock { private Date archiveModWhen; private int masterVersion; private long masterEof; - private byte[] nuFileId = {0,0,0,0,0,0}; /** * Create the Master Header Block, based on the LittleEndianByteInputStream. */ public MasterHeaderBlock(LittleEndianByteInputStream bs) throws IOException { - int headerOffset = 0; // Binary II wrappers will need to bump out the normal header size - nuFileId = bs.readBytes(6); + int fileType = 0, headerOffset = 0; + fileType = bs.seekFileType(); - if (checkId(nuFileId,BXY_ID)) { - // Binary II wrapper present... - bs.readBytes(127 - NUFILE_ID.length); + 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."); - nuFileId = bs.readBytes(6); + throw new IOException("This is actually a Binary II archive with multiple files in it."); // FIXME - NLS + fileType = bs.seekFileType(); } - if (!checkId(nuFileId,NUFILE_ID)) { - throw new IOException("Unable to decode this archive."); + 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 @@ -106,22 +105,4 @@ public class MasterHeaderBlock { public boolean isValidCrc() { return validCrc; } - /** - * Test that the requested constant is present. - */ - private boolean checkId(byte[] data, byte[] constant) { - for (int i = 0; i < constant.length; i++){ - if (data[i] != constant[i]) - return false; - } - return true; - } - - /** 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 }; - /** Binay II identifier "magic" bytes. */ - public static final byte[] BXY_ID = { 0x0a, 0x47, 0x4c }; - } diff --git a/src/main/java/com/webcodepro/shrinkit/NuFileArchive.java b/src/main/java/com/webcodepro/shrinkit/NuFileArchive.java index 21ed43e..fb1f7d0 100644 --- a/src/main/java/com/webcodepro/shrinkit/NuFileArchive.java +++ b/src/main/java/com/webcodepro/shrinkit/NuFileArchive.java @@ -15,7 +15,15 @@ import com.webcodepro.shrinkit.io.LittleEndianByteInputStream; 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. */ @@ -27,13 +35,20 @@ public class NuFileArchive { HeaderBlock header = new HeaderBlock(bs); header.readThreads(bs); headers.add(header); + totalSize += header.getHeaderSize(); } } + /** + * @return long size in bytes of the archive + */ + public long getArchiveSize() { + return totalSize; + } + public MasterHeaderBlock getMasterHeaderBlock() { return master; } public List getHeaderBlocks() { return headers; - } -} + }} diff --git a/src/main/java/com/webcodepro/shrinkit/Utilities.java b/src/main/java/com/webcodepro/shrinkit/Utilities.java deleted file mode 100644 index 1cd3d0e..0000000 --- a/src/main/java/com/webcodepro/shrinkit/Utilities.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.webcodepro.shrinkit; - -/* - * Copyright (C) 2012 by David Schmidt - * david__schmidt at users.sourceforge.net - * - * 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, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; - -import com.webcodepro.shrinkit.io.LittleEndianByteInputStream; - -/** - * Some higher-level utilities for dealing with a NuFX archive. - * - * @author david__schmidt@users.sourceforge.net - */ -public class Utilities -{ - /** - * Interpret a SDK NuFile/NuFX/Shrinkit archive as a full disk image. - * - * @return byte[] buffer containing full disk of data; null if unable to read - * @throws IllegalArgumentException if the filename is not able to be read - * @throws IOException the file has some malformed-ness about it - */ - public static byte[] unpackSDKFile(String fileName) throws IOException { - byte buffer[] = null; - ThreadRecord dataThread = null; - File file = new File(fileName); - if (file.isDirectory()) { - throw new IllegalArgumentException("'" + fileName + "' is not a file."); - } - InputStream is = new FileInputStream(file); - NuFileArchive a = new NuFileArchive(is); - for (HeaderBlock b : a.getHeaderBlocks()) { - for (ThreadRecord r : b.getThreadRecords()) { - try - { - if (r.getThreadKind() == ThreadKind.DISK_IMAGE) - { - dataThread = r; - } - } - catch (Exception ex) - { - System.out.println(ex); - } - } - dataThread.readThreadData(new LittleEndianByteInputStream(dataThread.getRawInputStream())); - InputStream fis = dataThread.getInputStream(); - int dmgLen = (int)(dataThread.getThreadEof()); - buffer = new byte[dmgLen]; - fis.read(buffer,0,dmgLen); - fis.close(); - } - return buffer; - } -} diff --git a/src/main/java/com/webcodepro/shrinkit/io/ByteConstants.java b/src/main/java/com/webcodepro/shrinkit/io/ByteConstants.java index d1a96d6..e1c7e1a 100644 --- a/src/main/java/com/webcodepro/shrinkit/io/ByteConstants.java +++ b/src/main/java/com/webcodepro/shrinkit/io/ByteConstants.java @@ -13,6 +13,8 @@ public interface ByteConstants { 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. */ diff --git a/src/main/java/com/webcodepro/shrinkit/io/LittleEndianByteInputStream.java b/src/main/java/com/webcodepro/shrinkit/io/LittleEndianByteInputStream.java index 5280c0d..bf5c145 100644 --- a/src/main/java/com/webcodepro/shrinkit/io/LittleEndianByteInputStream.java +++ b/src/main/java/com/webcodepro/shrinkit/io/LittleEndianByteInputStream.java @@ -8,6 +8,7 @@ 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. @@ -61,6 +62,9 @@ public class LittleEndianByteInputStream extends InputStream implements ByteCons 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"); } @@ -69,18 +73,49 @@ public class LittleEndianByteInputStream extends InputStream implements ByteCons } /** - * Test that the NuFile id is embedded in the LittleEndianByteInputStream. + * Test the beginning of the data stream for a magic signature, for up to a total + * of 2k bytes of leading garbage */ - public boolean checkNuFileId() throws IOException { - byte[] data = readBytes(6); - return Arrays.equals(data, NUFILE_ID); + public int seekFileType() throws IOException { + return seekFileType(6); } /** - * Test that the NuFx id is embedded in the LittleEndianByteInputStream. + * Test the beginning of the data stream for a magic signature, specifying the + * maximum size of a signature to test for */ - public boolean checkNuFxId() throws IOException { - byte[] data = readBytes(4); - return Arrays.equals(data, NUFX_ID); + 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