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$
|
new FilenameFilter(textBundle.get("Disk.ApplePcImages"), //$NON-NLS-1$
|
||||||
"*.hdv"), //$NON-NLS-1$
|
"*.hdv"), //$NON-NLS-1$
|
||||||
new FilenameFilter(textBundle.get("Disk.CompressedImages"), //$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$
|
new FilenameFilter(textBundle.get("Disk.AllFiles"), //$NON-NLS-1$
|
||||||
"*.*") //$NON-NLS-1$
|
"*.*") //$NON-NLS-1$
|
||||||
};
|
};
|
||||||
@ -148,6 +148,7 @@ public class Disk {
|
|||||||
".po", //$NON-NLS-1$
|
".po", //$NON-NLS-1$
|
||||||
".nib", //$NON-NLS-1$
|
".nib", //$NON-NLS-1$
|
||||||
".sdk", //$NON-NLS-1$
|
".sdk", //$NON-NLS-1$
|
||||||
|
".shk", //$NON-NLS-1$
|
||||||
".2mg", //$NON-NLS-1$
|
".2mg", //$NON-NLS-1$
|
||||||
".2img", //$NON-NLS-1$
|
".2img", //$NON-NLS-1$
|
||||||
".hdv", //$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
|
// If we have an SDK, unpack it and send along the byte array
|
||||||
diskImage = com.webcodepro.shrinkit.Utilities.unpackSDKFile(filename);
|
diskImage = com.webcodepro.shrinkit.Utilities.unpackSDKFile(filename);
|
||||||
diskSize = diskImage.length;
|
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 {
|
} else {
|
||||||
File file = new File(filename);
|
File file = new File(filename);
|
||||||
diskSize = (int) file.length();
|
diskSize = (int) file.length();
|
||||||
@ -388,6 +394,14 @@ public class Disk {
|
|||||||
return filename.toLowerCase().endsWith(".sdk"); //$NON-NLS-1$
|
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).
|
* Indicate if this disk is ProDOS ordered (beginning with block 0).
|
||||||
*/
|
*/
|
||||||
|
@ -151,7 +151,7 @@ public class ac {
|
|||||||
File file = new File(fileName);
|
File file = new File(fileName);
|
||||||
if (!file.canRead())
|
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();
|
ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
||||||
byte[] inb = new byte[1024];
|
byte[] inb = new byte[1024];
|
||||||
@ -204,7 +204,7 @@ public class ac {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
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.
|
* the Header Block size varies significantly.
|
||||||
*/
|
*/
|
||||||
public HeaderBlock(LittleEndianByteInputStream bs) throws IOException {
|
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();
|
headerCrc = bs.readWord();
|
||||||
attribCount = bs.readWord();
|
attribCount = bs.readWord();
|
||||||
versionNumber = bs.readWord();
|
versionNumber = bs.readWord();
|
||||||
|
@ -3,6 +3,7 @@ package com.webcodepro.shrinkit;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
|
import com.webcodepro.shrinkit.io.ByteConstants;
|
||||||
import com.webcodepro.shrinkit.io.LittleEndianByteInputStream;
|
import com.webcodepro.shrinkit.io.LittleEndianByteInputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,25 +26,24 @@ public class MasterHeaderBlock {
|
|||||||
private Date archiveModWhen;
|
private Date archiveModWhen;
|
||||||
private int masterVersion;
|
private int masterVersion;
|
||||||
private long masterEof;
|
private long masterEof;
|
||||||
private byte[] nuFileId = {0,0,0,0,0,0};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the Master Header Block, based on the LittleEndianByteInputStream.
|
* Create the Master Header Block, based on the LittleEndianByteInputStream.
|
||||||
*/
|
*/
|
||||||
public MasterHeaderBlock(LittleEndianByteInputStream bs) throws IOException {
|
public MasterHeaderBlock(LittleEndianByteInputStream bs) throws IOException {
|
||||||
int headerOffset = 0;
|
int fileType = 0, headerOffset = 0;
|
||||||
nuFileId = bs.readBytes(6);
|
fileType = bs.seekFileType();
|
||||||
|
|
||||||
if (checkId(nuFileId,BXY_ID)) {
|
if (fileType == NuFileArchive.BXY_ARCHIVE) {
|
||||||
bs.readBytes(127 - NUFILE_ID.length);
|
bs.readBytes(127 - ByteConstants.NUFILE_ID.length);
|
||||||
headerOffset = 128;
|
headerOffset = 128;
|
||||||
int count = bs.read();
|
int count = bs.read();
|
||||||
if (count != 0)
|
if (count != 0)
|
||||||
throw new IOException("This is actually a Binary II archive with multiple files in it.");
|
throw new IOException("This is actually a Binary II archive with multiple files in it."); // FIXME - NLS
|
||||||
nuFileId = bs.readBytes(6);
|
fileType = bs.seekFileType();
|
||||||
}
|
}
|
||||||
if (!checkId(nuFileId,NUFILE_ID)) {
|
if (!(fileType == NuFileArchive.NUFILE_ARCHIVE)) {
|
||||||
throw new IOException("Unable to decode this archive.");
|
throw new IOException("Unable to decode this archive."); // FIXME - NLS
|
||||||
}
|
}
|
||||||
masterCrc = bs.readWord();
|
masterCrc = bs.readWord();
|
||||||
bs.resetCrc(); // CRC is computed from this point to the end of the header
|
bs.resetCrc(); // CRC is computed from this point to the end of the header
|
||||||
@ -105,22 +105,4 @@ public class MasterHeaderBlock {
|
|||||||
public boolean isValidCrc() {
|
public boolean isValidCrc() {
|
||||||
return validCrc;
|
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 {
|
public class NuFileArchive {
|
||||||
private MasterHeaderBlock master;
|
private MasterHeaderBlock master;
|
||||||
private List<HeaderBlock> headers;
|
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.
|
* Read in the NuFile/NuFX/Shrinkit archive.
|
||||||
*/
|
*/
|
||||||
|
@ -66,12 +66,57 @@ public class Utilities
|
|||||||
System.out.println(ex);
|
System.out.println(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dataThread.readThreadData(new LittleEndianByteInputStream(dataThread.getRawInputStream()));
|
if (null != dataThread) {
|
||||||
InputStream fis = dataThread.getInputStream();
|
dataThread.readThreadData(new LittleEndianByteInputStream(dataThread.getRawInputStream()));
|
||||||
int dmgLen = (int)(dataThread.getThreadEof());
|
InputStream fis = dataThread.getInputStream();
|
||||||
buffer = new byte[dmgLen];
|
int dmgLen = (int)(dataThread.getThreadEof());
|
||||||
fis.read(buffer,0,dmgLen);
|
buffer = new byte[dmgLen];
|
||||||
fis.close();
|
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;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,8 @@ public interface ByteConstants {
|
|||||||
public static final byte[] NUFILE_ID = { 0x4e, (byte)0xf5, 0x46, (byte)0xe9, 0x6c, (byte)0xe5 };
|
public static final byte[] NUFILE_ID = { 0x4e, (byte)0xf5, 0x46, (byte)0xe9, 0x6c, (byte)0xe5 };
|
||||||
/** Header Block identifier "magic" bytes. */
|
/** Header Block identifier "magic" bytes. */
|
||||||
public static final byte[] NUFX_ID = { 0x4e, (byte)0xf5, 0x46, (byte)0xd8 };
|
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. */
|
/** Apple IIgs Toolbox TimeRec seconds byte position. */
|
||||||
public static final int TIMEREC_SECOND = 0;
|
public static final int TIMEREC_SECOND = 0;
|
||||||
/** Apple IIgs Toolbox TimeRec seconds byte position. */
|
/** Apple IIgs Toolbox TimeRec seconds byte position. */
|
||||||
|
@ -8,6 +8,7 @@ import java.util.Date;
|
|||||||
import java.util.GregorianCalendar;
|
import java.util.GregorianCalendar;
|
||||||
|
|
||||||
import com.webcodepro.shrinkit.CRC16;
|
import com.webcodepro.shrinkit.CRC16;
|
||||||
|
import com.webcodepro.shrinkit.NuFileArchive;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple class to hide the source of byte data.
|
* 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 {
|
public int seekFileType() throws IOException {
|
||||||
byte[] data = readBytes(6);
|
return seekFileType(6);
|
||||||
return Arrays.equals(data, NUFILE_ID);
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 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 {
|
public int seekFileType(int max) throws IOException {
|
||||||
byte[] data = readBytes(4);
|
byte[] data = new byte[2048];
|
||||||
return Arrays.equals(data, NUFX_ID);
|
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.
|
* 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