dmolony-DiskBrowser/src/com/bytezone/diskbrowser/NuFX.java

268 lines
8.4 KiB
Java

package com.bytezone.diskbrowser;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import com.bytezone.common.Utility;
public class NuFX
{
private static String[] fileSystems = {//
"", "ProDOS/SOS", "DOS 3.3", "DOS 3.2", "Apple II Pascal", "Macintosh HFS",
"Macintosh MFS", "Lisa File System", "Apple CP/M", "", "MS-DOS", "High Sierra",
"ISO 9660", "AppleShare" };
private final Header header;
private final byte[] buffer;
private final boolean debug = false;
private final List<Record> records = new ArrayList<Record> ();
private final List<Thread> threads = new ArrayList<Thread> ();
public NuFX (Path path) throws FileFormatException, IOException
{
buffer = Files.readAllBytes (path);
header = new Header (buffer);
int dataPtr = 48;
if (header.bin2)
dataPtr += 128;
if (debug)
System.out.printf ("%s%n%n", header);
for (int rec = 0; rec < header.totalRecords; rec++)
{
Record record = new Record (dataPtr);
records.add (record);
if (debug)
System.out.printf ("Record: %d%n%n%s%n%n", rec, record);
dataPtr += record.attributes + record.fileNameLength;
int threadsPtr = dataPtr;
dataPtr += record.totThreads * 16;
for (int i = 0; i < record.totThreads; i++)
{
Thread thread = new Thread (buffer, threadsPtr + i * 16, dataPtr);
threads.add (thread);
dataPtr += thread.getCompressedEOF ();
if (debug)
System.out.printf ("Thread: %d%n%n%s%n%n", i, thread);
}
}
}
public byte[] getBuffer ()
{
for (Thread thread : threads)
if (thread.hasDisk ())
return thread.getData ();
return null;
}
@Override
public String toString ()
{
for (Thread thread : threads)
if (thread.hasDisk ())
return thread.toString ();
return "no disk";
}
protected static int getCRC (final byte[] buffer, int base)
{
int crc = base;
for (int j = 0; j < buffer.length; j++)
{
crc = ((crc >>> 8) | (crc << 8)) & 0xFFFF;
crc ^= (buffer[j] & 0xFF);
crc ^= ((crc & 0xFF) >> 4);
crc ^= (crc << 12) & 0xFFFF;
crc ^= ((crc & 0xFF) << 5) & 0xFFFF;
}
crc &= 0xFFFF;
return crc;
}
class Header
{
private final int totalRecords;
private final int version;
private final int eof;
private final int crc;
private final DateTime created;
private final DateTime modified;
boolean bin2;
public Header (byte[] buffer) throws FileFormatException
{
int ptr = 0;
while (true)
{
if (isNuFile (buffer, ptr))
break;
if (isBin2 (buffer, ptr))
{
ptr += 128;
bin2 = true;
continue;
}
throw new FileFormatException ("NuFile not found");
}
crc = Utility.getWord (buffer, ptr + 6);
totalRecords = Utility.getLong (buffer, ptr + 8);
created = new DateTime (buffer, ptr + 12);
modified = new DateTime (buffer, ptr + 20);
version = Utility.getWord (buffer, ptr + 28);
eof = Utility.getLong (buffer, ptr + 38);
byte[] crcBuffer = new byte[40];
System.arraycopy (buffer, ptr + 8, crcBuffer, 0, crcBuffer.length);
if (crc != getCRC (crcBuffer, 0))
{
System.out.println ("***** Master CRC mismatch *****");
throw new FileFormatException ("Master CRC failed");
}
}
private boolean isNuFile (byte[] buffer, int ptr)
{
if (buffer[ptr] == 0x4E && buffer[ptr + 1] == (byte) 0xF5 && buffer[ptr + 2] == 0x46
&& buffer[ptr + 3] == (byte) 0xE9 && buffer[ptr + 4] == 0x6C
&& buffer[ptr + 5] == (byte) 0xE5)
return true;
return false;
}
private boolean isBin2 (byte[] buffer, int ptr)
{
if (buffer[ptr] == 0x0A && buffer[ptr + 1] == 0x47 && buffer[ptr + 2] == 0x4C
&& buffer[ptr + 18] == (byte) 0x02)
return true;
return false;
}
@Override
public String toString ()
{
StringBuilder text = new StringBuilder ();
text.append (String.format ("Master CRC ..... %,d (%04X)%n", crc, crc));
text.append (String.format ("Records ........ %,d%n", totalRecords));
text.append (String.format ("Created ........ %s%n", created.format ()));
text.append (String.format ("Modified ....... %s%n", modified.format ()));
text.append (String.format ("Version ........ %,d%n", version));
text.append (String.format ("Master EOF ..... %,d", eof));
return text.toString ();
}
}
class Record
{
private final int totThreads;
private final int crc;
private final char separator;
private final int fileSystemID;
private final int attributes;
private final int version;
private final int access;
private final int fileType;
private final int auxType;
private final int storType;
private final DateTime created;
private final DateTime modified;
private final DateTime archived;
private final int optionSize;
private final int fileNameLength;
private final String fileName;
public Record (int dataPtr) throws FileFormatException
{
// check for NuFX
if (!isNuFX (buffer, dataPtr))
throw new FileFormatException ("NuFX not found");
crc = Utility.getWord (buffer, dataPtr + 4);
attributes = Utility.getWord (buffer, dataPtr + 6);
version = Utility.getWord (buffer, dataPtr + 8);
totThreads = Utility.getLong (buffer, dataPtr + 10);
fileSystemID = Utility.getWord (buffer, dataPtr + 14);
separator = (char) (buffer[dataPtr + 16] & 0x00FF);
access = Utility.getLong (buffer, dataPtr + 18);
fileType = Utility.getLong (buffer, dataPtr + 22);
auxType = Utility.getLong (buffer, dataPtr + 26);
storType = Utility.getWord (buffer, dataPtr + 30);
created = new DateTime (buffer, dataPtr + 32);
modified = new DateTime (buffer, dataPtr + 40);
archived = new DateTime (buffer, dataPtr + 48);
optionSize = Utility.getWord (buffer, dataPtr + 56);
fileNameLength = Utility.getWord (buffer, dataPtr + attributes - 2);
int len = attributes + fileNameLength - 6;
byte[] crcBuffer = new byte[len + totThreads * 16];
System.arraycopy (buffer, dataPtr + 6, crcBuffer, 0, crcBuffer.length);
if (crc != getCRC (crcBuffer, 0))
{
System.out.println ("***** Header CRC mismatch *****");
throw new FileFormatException ("Header CRC failed");
}
if (fileNameLength > 0)
{
int start = dataPtr + attributes;
int end = start + fileNameLength;
for (int i = start; i < end; i++)
buffer[i] &= 0x7F;
fileName = new String (buffer, start, fileNameLength);
}
else
fileName = "";
}
private boolean isNuFX (byte[] buffer, int ptr)
{
if (buffer[ptr] == 0x4E && buffer[ptr + 1] == (byte) 0xF5 && buffer[ptr + 2] == 0x46
&& buffer[ptr + 3] == (byte) 0xD8)
return true;
return false;
}
@Override
public String toString ()
{
StringBuilder text = new StringBuilder ();
text.append (String.format ("Header CRC ..... %,d (%04X)%n", crc, crc));
text.append (String.format ("Attributes ..... %d%n", attributes));
text.append (String.format ("Version ........ %d%n", version));
text.append (String.format ("Threads ........ %d%n", totThreads));
text.append (String.format ("File sys id .... %d (%s)%n", fileSystemID,
fileSystems[fileSystemID]));
text.append (String.format ("Separator ...... %s%n", separator));
text.append (String.format ("Access ......... %,d%n", access));
text.append (String.format ("File type ...... %,d%n", fileType));
text.append (String.format ("Aux type ....... %,d%n", auxType));
text.append (String.format ("Stor type ...... %,d%n", storType));
text.append (String.format ("Created ........ %s%n", created.format ()));
text.append (String.format ("Modified ....... %s%n", modified.format ()));
text.append (String.format ("Archived ....... %s%n", archived.format ()));
text.append (String.format ("Option size .... %,d%n", optionSize));
text.append (String.format ("Filename len ... %,d%n", fileNameLength));
text.append (String.format ("Filename ....... %s", fileName));
return text.toString ();
}
}
}