dmolony-DiskBrowser/src/com/bytezone/diskbrowser/nufx/Record.java

395 lines
14 KiB
Java
Raw Normal View History

2021-05-21 02:57:03 +00:00
package com.bytezone.diskbrowser.nufx;
2021-03-29 01:04:01 +00:00
2021-05-02 04:45:26 +00:00
import static com.bytezone.diskbrowser.prodos.ProdosConstants.fileTypes;
2021-04-17 02:14:06 +00:00
import java.time.LocalDateTime;
2021-04-15 07:27:20 +00:00
import java.util.ArrayList;
import java.util.List;
2021-05-21 02:57:03 +00:00
import com.bytezone.diskbrowser.utilities.DateTime;
import com.bytezone.diskbrowser.utilities.FileFormatException;
import com.bytezone.diskbrowser.utilities.Utility;
2021-03-29 01:04:01 +00:00
// -----------------------------------------------------------------------------------//
class Record
// -----------------------------------------------------------------------------------//
{
2021-04-15 07:27:20 +00:00
private static final byte[] NuFX = { 0x4E, (byte) 0xF5, 0x46, (byte) 0xD8 };
2023-03-15 23:00:31 +00:00
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" };
2021-03-29 01:04:01 +00:00
2021-04-15 07:27:20 +00:00
private static String[] storage = { "", "Seedling", "Sapling", "Tree", "", "Extended",
2023-03-15 23:00:31 +00:00
"", "", "", "", "", "", "", "Subdirectory" };
2021-04-15 07:27:20 +00:00
private static String[] accessChars = { "D", "R", "B", "", "", "I", "W", "R" };
2021-05-02 04:45:26 +00:00
private static String threadFormats[] = { "unc", "sq ", "lz1", "lz2", "", "" };
2021-04-15 07:27:20 +00:00
2021-03-29 01:04:01 +00:00
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;
2021-04-15 07:27:20 +00:00
final List<Thread> threads = new ArrayList<> ();
2021-03-29 01:04:01 +00:00
// ---------------------------------------------------------------------------------//
public Record (byte[] buffer, int dataPtr) throws FileFormatException
// ---------------------------------------------------------------------------------//
{
// check for NuFX
2021-04-15 07:27:20 +00:00
if (!Utility.isMagic (buffer, dataPtr, NuFX))
2021-03-29 01:04:01 +00:00
throw new FileFormatException ("NuFX not found");
crc = Utility.getShort (buffer, dataPtr + 4);
attributes = Utility.getShort (buffer, dataPtr + 6);
version = Utility.getShort (buffer, dataPtr + 8);
2021-03-29 01:04:01 +00:00
totThreads = Utility.getLong (buffer, dataPtr + 10);
fileSystemID = Utility.getShort (buffer, dataPtr + 14);
2021-03-29 01:04:01 +00:00
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.getShort (buffer, dataPtr + 30);
2021-03-29 01:04:01 +00:00
created = new DateTime (buffer, dataPtr + 32);
modified = new DateTime (buffer, dataPtr + 40);
archived = new DateTime (buffer, dataPtr + 48);
optionSize = Utility.getShort (buffer, dataPtr + 56);
fileNameLength = Utility.getShort (buffer, dataPtr + attributes - 2);
2021-03-29 01:04:01 +00:00
int len = attributes + fileNameLength - 6;
byte[] crcBuffer = new byte[len + totThreads * 16];
System.arraycopy (buffer, dataPtr + 6, crcBuffer, 0, crcBuffer.length);
2021-04-17 04:41:36 +00:00
if (crc != Utility.getCRC (crcBuffer, crcBuffer.length, 0))
2021-03-29 01:04:01 +00:00
{
2021-04-18 10:11:26 +00:00
System.out.println ("***** Record CRC mismatch *****");
throw new FileFormatException ("Record CRC failed");
2021-03-29 01:04:01 +00:00
}
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 = "";
}
2021-04-27 11:26:09 +00:00
// ---------------------------------------------------------------------------------//
boolean isValidFileSystem ()
// ---------------------------------------------------------------------------------//
{
return fileSystemID <= 4 || fileSystemID == 8;
}
2021-03-29 01:04:01 +00:00
// ---------------------------------------------------------------------------------//
int getAttributes ()
// ---------------------------------------------------------------------------------//
{
return attributes;
}
// ---------------------------------------------------------------------------------//
int getFileNameLength ()
// ---------------------------------------------------------------------------------//
{
return fileNameLength;
}
// ---------------------------------------------------------------------------------//
int getTotalThreads ()
// ---------------------------------------------------------------------------------//
{
return totThreads;
}
2021-04-15 07:27:20 +00:00
// ---------------------------------------------------------------------------------//
boolean hasDisk ()
// ---------------------------------------------------------------------------------//
{
for (Thread thread : threads)
if (thread.hasDisk ())
return true;
return false;
}
// ---------------------------------------------------------------------------------//
boolean hasFile ()
// ---------------------------------------------------------------------------------//
{
for (Thread thread : threads)
if (thread.hasFile ())
return true;
return false;
}
// ---------------------------------------------------------------------------------//
boolean hasFile (String fileName)
// ---------------------------------------------------------------------------------//
{
for (Thread thread : threads)
if (thread.hasFile (fileName))
return true;
return false;
}
2021-05-03 20:12:44 +00:00
// ---------------------------------------------------------------------------------//
boolean hasResource ()
// ---------------------------------------------------------------------------------//
{
for (Thread thread : threads)
if (thread.hasResource ())
return true;
return false;
}
2021-04-15 07:27:20 +00:00
// ---------------------------------------------------------------------------------//
String getFileName ()
// ---------------------------------------------------------------------------------//
{
2021-04-26 00:03:43 +00:00
if (fileNameLength > 0) // probably version 0
return fileName;
2021-04-15 07:27:20 +00:00
for (Thread thread : threads)
if (thread.hasFileName ())
2021-04-17 02:14:06 +00:00
{
String fileName = thread.getFileName ();
if (separator != '/')
return fileName.replace (separator, '/');
2021-04-15 07:27:20 +00:00
return thread.getFileName ();
2021-04-17 02:14:06 +00:00
}
2021-04-15 07:27:20 +00:00
return "";
}
// ---------------------------------------------------------------------------------//
int getFileType ()
// ---------------------------------------------------------------------------------//
{
return fileType;
}
2021-04-17 02:14:06 +00:00
// ---------------------------------------------------------------------------------//
int getAuxType ()
// ---------------------------------------------------------------------------------//
{
return auxType;
}
// ---------------------------------------------------------------------------------//
LocalDateTime getCreated ()
// ---------------------------------------------------------------------------------//
{
2021-05-12 07:08:24 +00:00
return created == null ? null : created.getLocalDateTime ();
2021-04-17 02:14:06 +00:00
}
// ---------------------------------------------------------------------------------//
LocalDateTime getModified ()
// ---------------------------------------------------------------------------------//
{
2021-05-12 07:08:24 +00:00
return modified == null ? null : modified.getLocalDateTime ();
2021-04-17 02:14:06 +00:00
}
2021-05-03 20:12:44 +00:00
// ---------------------------------------------------------------------------------//
LocalDateTime getArchived ()
// ---------------------------------------------------------------------------------//
{
2021-05-12 07:08:24 +00:00
return archived == null ? null : archived.getLocalDateTime ();
2021-05-03 20:12:44 +00:00
}
2021-04-17 21:32:03 +00:00
// ---------------------------------------------------------------------------------//
int getFileSystemID ()
// ---------------------------------------------------------------------------------//
{
return fileSystemID;
}
2021-04-27 11:26:09 +00:00
// ---------------------------------------------------------------------------------//
String getFileSystemName ()
// ---------------------------------------------------------------------------------//
{
return fileSystems[fileSystemID];
}
2021-05-08 09:56:57 +00:00
// Called by NuFX.listFiles()
2021-04-15 07:27:20 +00:00
// ---------------------------------------------------------------------------------//
int getFileSize ()
// ---------------------------------------------------------------------------------//
{
for (Thread thread : threads)
if (thread.hasFile ())
return thread.getFileSize ();
return 0;
}
2021-05-02 04:45:26 +00:00
// ---------------------------------------------------------------------------------//
int getThreadFormat ()
// ---------------------------------------------------------------------------------//
{
for (Thread thread : threads)
2021-05-08 09:56:57 +00:00
if (thread.hasFile () || thread.hasDisk ())
2021-05-02 04:45:26 +00:00
return thread.threadFormat;
return 0;
}
2023-03-15 23:00:31 +00:00
// ---------------------------------------------------------------------------------//
String getThreadFormatText ()
// ---------------------------------------------------------------------------------//
{
return threadFormats[getThreadFormat ()];
}
2021-04-15 07:27:20 +00:00
// ---------------------------------------------------------------------------------//
int getUncompressedSize ()
// ---------------------------------------------------------------------------------//
{
2021-05-08 09:56:57 +00:00
if (hasDisk ())
return auxType * storType;
2021-05-03 20:12:44 +00:00
int size = 0;
2021-04-15 07:27:20 +00:00
for (Thread thread : threads)
2021-05-08 09:56:57 +00:00
if (thread.hasFile () || thread.hasResource () || thread.hasDisk ())
2021-05-03 20:12:44 +00:00
size += thread.getUncompressedEOF ();
2021-04-15 07:27:20 +00:00
2021-05-03 20:12:44 +00:00
return size;
2021-04-15 07:27:20 +00:00
}
2021-05-02 04:45:26 +00:00
// ---------------------------------------------------------------------------------//
int getCompressedSize ()
// ---------------------------------------------------------------------------------//
{
2021-05-03 20:12:44 +00:00
int size = 0;
2021-05-02 04:45:26 +00:00
for (Thread thread : threads)
2021-05-08 09:56:57 +00:00
if (thread.hasFile () || thread.hasResource () || thread.hasDisk ())
2021-05-03 20:12:44 +00:00
size += thread.getCompressedEOF ();
2021-05-02 04:45:26 +00:00
2021-05-03 20:12:44 +00:00
return size;
2021-05-02 04:45:26 +00:00
}
2023-03-15 23:00:31 +00:00
// ---------------------------------------------------------------------------------//
public float getCompressedPct ()
// ---------------------------------------------------------------------------------//
{
float pct = 100;
if (getUncompressedSize () > 0)
pct = getCompressedSize () * 100 / getUncompressedSize ();
return pct;
}
2021-03-29 01:04:01 +00:00
// ---------------------------------------------------------------------------------//
2021-04-16 11:08:59 +00:00
byte[] getData ()
// ---------------------------------------------------------------------------------//
{
for (Thread thread : threads)
if (thread.hasFile ())
return thread.getData ();
2021-05-03 20:12:44 +00:00
return null;
}
// ---------------------------------------------------------------------------------//
byte[] getResourceData ()
// ---------------------------------------------------------------------------------//
{
for (Thread thread : threads)
if (thread.hasResource ())
return thread.getData ();
2021-04-16 11:08:59 +00:00
return null;
}
2021-05-02 04:45:26 +00:00
// ---------------------------------------------------------------------------------//
String getLine ()
// ---------------------------------------------------------------------------------//
{
2021-05-08 09:56:57 +00:00
String name = getFileName ();
if (name.length () > 27)
name = ".." + name.substring (name.length () - 25);
2023-03-15 23:00:31 +00:00
// float pct = 100;
// if (getUncompressedSize () > 0)
// pct = getCompressedSize () * 100 / getUncompressedSize ();
2021-05-08 09:56:57 +00:00
2021-05-03 20:12:44 +00:00
String lockedFlag = (access | 0xC3) == 1 ? "+" : " ";
String forkedFlag = hasResource () ? "+" : " ";
2021-05-08 09:56:57 +00:00
if (hasDisk ())
return String.format ("%s%-27.27s %-4s %-6s %-15s %s %3.0f%% %7d", lockedFlag,
name, "Disk", (getUncompressedSize () / 1024) + "k", archived.format2 (),
2023-03-15 23:00:31 +00:00
getThreadFormatText (), getCompressedPct (), getUncompressedSize ());
2021-05-03 20:12:44 +00:00
return String.format ("%s%-27.27s %s%s $%04X %-15s %s %3.0f%% %7d", lockedFlag,
name, fileTypes[fileType], forkedFlag, auxType, archived.format2 (),
2023-03-15 23:00:31 +00:00
getThreadFormatText (), getCompressedPct (), getUncompressedSize ());
2021-05-02 04:45:26 +00:00
}
2021-04-16 11:08:59 +00:00
// ---------------------------------------------------------------------------------//
2021-03-29 01:04:01 +00:00
@Override
public String toString ()
// ---------------------------------------------------------------------------------//
{
StringBuilder text = new StringBuilder ();
2021-04-15 07:27:20 +00:00
String bits = "00000000" + Integer.toBinaryString (access & 0xFF);
bits = bits.substring (bits.length () - 8);
String decode = Utility.matchFlags (access, accessChars);
2021-04-17 02:14:06 +00:00
text.append (String.format ("Header CRC ..... %,d (%<04X)%n", crc));
2021-03-29 01:04:01 +00:00
text.append (String.format ("Attributes ..... %d%n", attributes));
text.append (String.format ("Version ........ %d%n", version));
text.append (String.format ("Threads ........ %d%n", totThreads));
2021-05-12 07:08:24 +00:00
text.append (
String.format ("File sys id .... %d (%s)%n", fileSystemID, getFileSystemName ()));
2021-03-29 01:04:01 +00:00
text.append (String.format ("Separator ...... %s%n", separator));
2021-04-15 07:27:20 +00:00
text.append (String.format ("Access ......... %s %s%n", bits, decode));
2021-04-18 10:11:26 +00:00
2021-04-15 07:27:20 +00:00
if (storType < 16)
{
2021-04-17 04:41:36 +00:00
text.append (String.format ("File type ...... %02X %s%n", fileType,
2021-05-02 04:45:26 +00:00
fileTypes[fileType]));
2021-04-17 04:41:36 +00:00
text.append (String.format ("Aux type ....... %,d $%<04X%n", auxType));
2021-04-15 07:27:20 +00:00
text.append (
String.format ("Stor type ...... %,d %s%n", storType, storage[storType]));
}
else
{
text.append (String.format ("Zero ........... %,d%n", fileType));
text.append (String.format ("Total blocks ... %,d%n", auxType));
text.append (String.format ("Block size ..... %,d%n", storType));
}
2021-04-18 10:11:26 +00:00
2021-03-29 01:04:01 +00:00
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 ();
}
}