mirror of
https://github.com/AppleCommander/AppleCommander.git
synced 2024-12-22 08:30:35 +00:00
Get started on SHK unpacking
ShrinkIt fixes - needed new ways to seek the header magic signatures
This commit is contained in:
parent
a79df8f3bc
commit
37e7476bfb
@ -138,7 +138,7 @@ public class Disk {
|
||||
new FilenameFilter(textBundle.get("Disk.ApplePcImages"), //$NON-NLS-1$
|
||||
"*.hdv"), //$NON-NLS-1$
|
||||
new FilenameFilter(textBundle.get("Disk.CompressedImages"), //$NON-NLS-1$
|
||||
".sdk; *.do.gz; *.dsk.gz; *.po.gz; *.2mg.gz; *.2img.gz"), //$NON-NLS-1$
|
||||
"*.sdk; *.shk; *.do.gz; *.dsk.gz; *.po.gz; *.2mg.gz; *.2img.gz"), //$NON-NLS-1$
|
||||
new FilenameFilter(textBundle.get("Disk.AllFiles"), //$NON-NLS-1$
|
||||
"*.*") //$NON-NLS-1$
|
||||
};
|
||||
@ -148,6 +148,7 @@ public class Disk {
|
||||
".po", //$NON-NLS-1$
|
||||
".nib", //$NON-NLS-1$
|
||||
".sdk", //$NON-NLS-1$
|
||||
".shk", //$NON-NLS-1$
|
||||
".2mg", //$NON-NLS-1$
|
||||
".2img", //$NON-NLS-1$
|
||||
".hdv", //$NON-NLS-1$
|
||||
@ -182,6 +183,11 @@ public class Disk {
|
||||
// If we have an SDK, unpack it and send along the byte array
|
||||
diskImage = com.webcodepro.shrinkit.Utilities.unpackSDKFile(filename);
|
||||
diskSize = diskImage.length;
|
||||
} else if (isSHK()) {
|
||||
// If we have an SHK, unpack it and send along the byte array
|
||||
diskImage = com.webcodepro.shrinkit.Utilities.unpackSHKFile(filename);
|
||||
throw new IOException("SHK unpacking is not implemented yet."); // TODO - remove me
|
||||
//TODO - diskSize = diskImage.length;
|
||||
} else {
|
||||
File file = new File(filename);
|
||||
diskSize = (int) file.length();
|
||||
@ -388,6 +394,14 @@ public class Disk {
|
||||
return filename.toLowerCase().endsWith(".sdk"); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate if this disk is a ShrinkIt-compressed package.
|
||||
*/
|
||||
public boolean isSHK()
|
||||
{
|
||||
return filename.toLowerCase().endsWith(".shk"); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate if this disk is ProDOS ordered (beginning with block 0).
|
||||
*/
|
||||
|
@ -151,7 +151,7 @@ public class ac {
|
||||
File file = new File(fileName);
|
||||
if (!file.canRead())
|
||||
{
|
||||
throw new IOException("Unable to read input file named "+fileName+".");
|
||||
throw new IOException("Unable to read input file named "+fileName+"."); // FIXME - NLS
|
||||
}
|
||||
ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
||||
byte[] inb = new byte[1024];
|
||||
@ -204,7 +204,7 @@ public class ac {
|
||||
}
|
||||
}
|
||||
else
|
||||
throw new IOException(textBundle.get("CommandLineSDKReadOnly"));
|
||||
throw new IOException(textBundle.get("CommandLineSDKReadOnly")); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -44,7 +44,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();
|
||||
|
@ -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,25 +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;
|
||||
nuFileId = bs.readBytes(6);
|
||||
int fileType = 0, headerOffset = 0;
|
||||
fileType = bs.seekFileType();
|
||||
|
||||
if (checkId(nuFileId,BXY_ID)) {
|
||||
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
|
||||
@ -105,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 };
|
||||
|
||||
}
|
||||
|
@ -15,7 +15,14 @@ import com.webcodepro.shrinkit.io.LittleEndianByteInputStream;
|
||||
public class NuFileArchive {
|
||||
private MasterHeaderBlock master;
|
||||
private List<HeaderBlock> headers;
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
@ -66,12 +66,57 @@ public class Utilities
|
||||
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();
|
||||
if (null != dataThread) {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpret a SHK 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[] unpackSHKFile(String fileName) throws IOException {
|
||||
TextBundle textBundle = StorageBundle.getInstance();
|
||||
byte buffer[] = null;
|
||||
ThreadRecord dataThread = null;
|
||||
File file = new File(fileName);
|
||||
if (file.isDirectory() || !file.canRead()) {
|
||||
throw new IllegalArgumentException(textBundle.format("NotAFile", fileName, 1)); //$NON-NLS-1$
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
if (null != dataThread) {
|
||||
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;
|
||||
}
|
||||
|
@ -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. */
|
||||
|
@ -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.
|
||||
@ -69,18 +70,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<data.length;i++) {
|
||||
data[i] = 0;
|
||||
}
|
||||
for (i = 0; i < max; i++) {
|
||||
data[i] = (byte)readByte();
|
||||
}
|
||||
while (pos < data.length-max) {
|
||||
if (max == 6) {
|
||||
System.arraycopy(data, pos, testNUFILE, 0, NUFILE_ID.length);
|
||||
if (Arrays.equals(testNUFILE,NUFILE_ID)) {
|
||||
type = NuFileArchive.NUFILE_ARCHIVE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
System.arraycopy(data, pos, testNUFX, 0, NUFX_ID.length);
|
||||
System.arraycopy(data, pos, testBXY, 0, BXY_ID.length);
|
||||
if (Arrays.equals(testNUFX, NUFX_ID)) {
|
||||
type = NuFileArchive.NUFX_ARCHIVE;
|
||||
break;
|
||||
} else if (Arrays.equals(testBXY,BXY_ID)) {
|
||||
type = NuFileArchive.BXY_ARCHIVE;
|
||||
break;
|
||||
}
|
||||
data[pos+max] = (byte)readByte();
|
||||
pos++;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
/**
|
||||
* Read the two bytes in as a "Word" which needs to be stored as a Java int.
|
||||
|
Loading…
Reference in New Issue
Block a user