2016-02-24 21:11:14 +00:00
|
|
|
package com.bytezone.diskbrowser.utilities;
|
2015-06-01 09:35:51 +00:00
|
|
|
|
2021-04-15 07:27:20 +00:00
|
|
|
import java.io.File;
|
2015-06-01 09:35:51 +00:00
|
|
|
import java.io.IOException;
|
|
|
|
import java.nio.file.Files;
|
|
|
|
import java.nio.file.Path;
|
2021-04-17 02:14:06 +00:00
|
|
|
import java.time.LocalDateTime;
|
2015-06-01 09:35:51 +00:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.List;
|
|
|
|
|
2021-04-17 02:14:06 +00:00
|
|
|
import com.bytezone.diskbrowser.prodos.write.DiskFullException;
|
2021-04-16 11:08:59 +00:00
|
|
|
import com.bytezone.diskbrowser.prodos.write.ProdosDisk;
|
|
|
|
|
2020-02-07 23:26:38 +00:00
|
|
|
// -----------------------------------------------------------------------------------//
|
2015-06-01 09:35:51 +00:00
|
|
|
public class NuFX
|
2020-02-07 23:26:38 +00:00
|
|
|
// -----------------------------------------------------------------------------------//
|
2015-06-01 09:35:51 +00:00
|
|
|
{
|
2021-04-15 07:27:20 +00:00
|
|
|
private MasterHeader masterHeader;
|
2015-06-01 09:35:51 +00:00
|
|
|
private final byte[] buffer;
|
|
|
|
private final boolean debug = false;
|
|
|
|
|
2020-02-02 10:17:49 +00:00
|
|
|
private final List<Record> records = new ArrayList<> ();
|
2021-04-15 07:27:20 +00:00
|
|
|
private int totalFiles;
|
|
|
|
private int totalDisks;
|
2021-04-17 21:32:03 +00:00
|
|
|
private int totalBlocks;
|
2015-06-01 09:35:51 +00:00
|
|
|
|
2021-04-17 22:54:21 +00:00
|
|
|
private List<String> paths = new ArrayList<> ();
|
|
|
|
|
2020-02-07 23:26:38 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2015-06-01 09:35:51 +00:00
|
|
|
public NuFX (Path path) throws FileFormatException, IOException
|
2020-02-07 23:26:38 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2015-06-01 09:35:51 +00:00
|
|
|
{
|
|
|
|
buffer = Files.readAllBytes (path);
|
2016-12-15 00:01:42 +00:00
|
|
|
|
2021-04-15 07:27:20 +00:00
|
|
|
masterHeader = new MasterHeader (buffer);
|
2015-06-01 09:35:51 +00:00
|
|
|
|
|
|
|
int dataPtr = 48;
|
2021-04-15 07:27:20 +00:00
|
|
|
if (masterHeader.bin2)
|
2015-06-01 09:35:51 +00:00
|
|
|
dataPtr += 128;
|
|
|
|
|
|
|
|
if (debug)
|
2021-04-15 07:27:20 +00:00
|
|
|
System.out.printf ("%s%n%n", masterHeader);
|
2015-06-01 09:35:51 +00:00
|
|
|
|
2021-04-15 07:27:20 +00:00
|
|
|
for (int rec = 0; rec < masterHeader.getTotalRecords (); rec++)
|
2015-06-01 09:35:51 +00:00
|
|
|
{
|
2021-03-29 01:04:01 +00:00
|
|
|
Record record = new Record (buffer, dataPtr);
|
2021-04-17 21:32:03 +00:00
|
|
|
if (record.getFileSystemID () != 1)
|
|
|
|
{
|
|
|
|
System.out.println ("Not Prodos");
|
|
|
|
break;
|
|
|
|
}
|
2015-06-01 09:35:51 +00:00
|
|
|
records.add (record);
|
|
|
|
|
|
|
|
if (debug)
|
|
|
|
System.out.printf ("Record: %d%n%n%s%n%n", rec, record);
|
|
|
|
|
2021-03-29 01:04:01 +00:00
|
|
|
dataPtr += record.getAttributes () + record.getFileNameLength ();
|
2015-06-01 09:35:51 +00:00
|
|
|
int threadsPtr = dataPtr;
|
2021-03-29 01:04:01 +00:00
|
|
|
dataPtr += record.getTotalThreads () * 16;
|
2015-06-01 09:35:51 +00:00
|
|
|
|
2021-03-29 01:04:01 +00:00
|
|
|
for (int i = 0; i < record.getTotalThreads (); i++)
|
2015-06-01 09:35:51 +00:00
|
|
|
{
|
|
|
|
Thread thread = new Thread (buffer, threadsPtr + i * 16, dataPtr);
|
2021-04-15 07:27:20 +00:00
|
|
|
record.threads.add (thread);
|
2015-06-01 09:35:51 +00:00
|
|
|
dataPtr += thread.getCompressedEOF ();
|
|
|
|
|
|
|
|
if (debug)
|
|
|
|
System.out.printf ("Thread: %d%n%n%s%n%n", i, thread);
|
|
|
|
}
|
2021-04-15 07:27:20 +00:00
|
|
|
|
|
|
|
if (record.hasFile ())
|
2021-04-17 21:32:03 +00:00
|
|
|
{
|
2021-04-15 07:27:20 +00:00
|
|
|
++totalFiles;
|
2021-04-17 21:32:03 +00:00
|
|
|
int blocks = (record.getFileSize () - 1) / 512 + 1;
|
|
|
|
if (blocks == 1) // seedling
|
|
|
|
totalBlocks += blocks;
|
|
|
|
else if (blocks <= 256) // sapling
|
|
|
|
totalBlocks += blocks + 1;
|
|
|
|
else // tree
|
2021-04-17 22:54:21 +00:00
|
|
|
totalBlocks += blocks + (blocks / 256) + 2;
|
|
|
|
storePath (record.getFileName ());
|
2021-04-17 21:32:03 +00:00
|
|
|
}
|
|
|
|
|
2021-04-15 07:27:20 +00:00
|
|
|
if (record.hasDisk ())
|
|
|
|
++totalDisks;
|
2015-06-01 09:35:51 +00:00
|
|
|
}
|
2021-04-17 22:54:21 +00:00
|
|
|
|
|
|
|
if (false)
|
|
|
|
{
|
|
|
|
System.out.println ("Unique paths:");
|
|
|
|
if (paths.size () == 0)
|
|
|
|
System.out.println ("<none>");
|
|
|
|
for (String pathName : paths)
|
|
|
|
System.out.println (pathName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
private void storePath (String fileName)
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
int pos = fileName.lastIndexOf ('/');
|
|
|
|
if (pos > 0)
|
|
|
|
{
|
|
|
|
String path = fileName.substring (0, pos);
|
|
|
|
for (int i = 0; i < paths.size (); i++)
|
|
|
|
{
|
|
|
|
String cmp = paths.get (i);
|
|
|
|
if (cmp.startsWith (path)) // longer path already there
|
|
|
|
return;
|
|
|
|
if (path.startsWith (cmp))
|
|
|
|
{
|
|
|
|
paths.set (i, path); // replace shorter path with longer path
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
paths.add (path);
|
|
|
|
}
|
2015-06-01 09:35:51 +00:00
|
|
|
}
|
|
|
|
|
2020-02-07 23:26:38 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2021-04-15 07:27:20 +00:00
|
|
|
public byte[] getDiskBuffer ()
|
2020-02-07 23:26:38 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2015-06-01 09:35:51 +00:00
|
|
|
{
|
2021-04-15 07:27:20 +00:00
|
|
|
if (totalDisks > 0)
|
|
|
|
{
|
|
|
|
for (Record record : records)
|
|
|
|
for (Thread thread : record.threads)
|
|
|
|
if (thread.hasDisk ())
|
|
|
|
return thread.getData ();
|
|
|
|
}
|
|
|
|
else if (totalFiles > 0)
|
|
|
|
{
|
2021-04-17 21:32:03 +00:00
|
|
|
int[] diskSizes = { 280, 800, 1600, 3200, 6400, 65536 };
|
|
|
|
// System.out.printf ("Files require: %d blocks%n", totalBlocks);
|
|
|
|
|
2021-04-17 22:54:21 +00:00
|
|
|
// choose Volume Name
|
|
|
|
String volumeName = "Disk.Browser";
|
|
|
|
int nameOffset = 0;
|
|
|
|
if (paths.size () == 1)
|
|
|
|
{
|
|
|
|
String onlyPath = paths.get (0);
|
|
|
|
int pos = onlyPath.indexOf ('/');
|
|
|
|
if (pos == -1) // no separators
|
|
|
|
volumeName = onlyPath;
|
|
|
|
else // use first component
|
|
|
|
volumeName = onlyPath.substring (0, pos);
|
|
|
|
nameOffset = volumeName.length () + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int diskSize : diskSizes) // in case we choose a size that is too small
|
2021-04-16 11:08:59 +00:00
|
|
|
{
|
2021-04-17 21:32:03 +00:00
|
|
|
// System.out.printf ("Checking %d %d%n", diskSize, totalBlocks);
|
|
|
|
if (diskSize < (totalBlocks + 10))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
try
|
2021-04-16 11:08:59 +00:00
|
|
|
{
|
2021-04-17 22:54:21 +00:00
|
|
|
ProdosDisk disk = new ProdosDisk (diskSize, volumeName);
|
2021-04-17 21:32:03 +00:00
|
|
|
int count = 0;
|
|
|
|
for (Record record : records)
|
2021-04-16 11:08:59 +00:00
|
|
|
{
|
2021-04-17 21:32:03 +00:00
|
|
|
if (record.hasFile ())
|
|
|
|
{
|
|
|
|
String fileName = record.getFileName ();
|
|
|
|
// int fileSize = record.getFileSize ();
|
|
|
|
byte fileType = (byte) record.getFileType ();
|
|
|
|
int eof = record.getUncompressedSize ();
|
|
|
|
int auxType = record.getAuxType ();
|
|
|
|
LocalDateTime created = record.getCreated ();
|
|
|
|
LocalDateTime modified = record.getModified ();
|
|
|
|
byte[] buffer = record.getData ();
|
|
|
|
|
2021-04-17 22:54:21 +00:00
|
|
|
if (nameOffset > 0) // remove volume name from path
|
|
|
|
fileName = fileName.substring (nameOffset);
|
|
|
|
|
|
|
|
if (true)
|
2021-04-17 21:32:03 +00:00
|
|
|
System.out.printf ("%3d %-35s %02X %,7d %,7d %,7d %s %s%n", ++count,
|
|
|
|
fileName, fileType, auxType, eof, buffer.length, created, modified);
|
|
|
|
|
|
|
|
disk.addFile (fileName, fileType, auxType, created, modified, buffer);
|
|
|
|
}
|
2021-04-16 11:08:59 +00:00
|
|
|
}
|
|
|
|
|
2021-04-17 21:32:03 +00:00
|
|
|
disk.close ();
|
2021-04-16 11:08:59 +00:00
|
|
|
|
2021-04-17 21:32:03 +00:00
|
|
|
return disk.getBuffer ();
|
|
|
|
}
|
|
|
|
catch (IOException e)
|
|
|
|
{
|
|
|
|
e.printStackTrace ();
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
catch (DiskFullException e)
|
|
|
|
{
|
|
|
|
System.out.println ("disk full: " + diskSize); // go round again
|
|
|
|
}
|
2021-04-17 02:14:06 +00:00
|
|
|
}
|
2021-04-15 07:27:20 +00:00
|
|
|
}
|
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2021-04-15 07:27:20 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
public byte[] getFileBuffer (String fileName)
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
for (Record record : records)
|
|
|
|
if (record.hasFile (fileName))
|
|
|
|
for (Thread thread : record.threads)
|
|
|
|
if (thread.hasFile ())
|
|
|
|
return thread.getData ();
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
public int getTotalFiles ()
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
return totalFiles;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
public int getTotalDisks ()
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
return totalDisks;
|
|
|
|
}
|
|
|
|
|
2021-04-17 21:32:03 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
public int getTotalBlocks ()
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
return totalBlocks;
|
|
|
|
}
|
|
|
|
|
2021-04-15 07:27:20 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
private void listFiles ()
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
int count = 0;
|
|
|
|
for (Record record : records)
|
|
|
|
{
|
|
|
|
if (record.hasFile ())
|
|
|
|
System.out.printf ("%3d %-35s %,7d %d %,7d%n", count, record.getFileName (),
|
|
|
|
record.getFileSize (), record.getFileType (), record.getUncompressedSize ());
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-07 23:26:38 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2015-06-01 09:35:51 +00:00
|
|
|
@Override
|
|
|
|
public String toString ()
|
2020-02-07 23:26:38 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2015-06-01 09:35:51 +00:00
|
|
|
{
|
2021-04-15 07:27:20 +00:00
|
|
|
for (Record record : records)
|
|
|
|
for (Thread thread : record.threads)
|
|
|
|
if (thread.hasDisk ())
|
|
|
|
return thread.toString ();
|
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
return "no disk";
|
|
|
|
}
|
2021-04-15 07:27:20 +00:00
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
public static void main (String[] args) throws FileFormatException, IOException
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
2021-04-17 21:32:03 +00:00
|
|
|
File file = new File (
|
|
|
|
"/Users/denismolony/Dropbox/Examples/SHK/Disk Disintegrator Deluxe 5.0_D1.SHK");
|
2021-04-15 07:27:20 +00:00
|
|
|
|
|
|
|
NuFX nufx = new NuFX (file.toPath ());
|
|
|
|
System.out.println (nufx);
|
|
|
|
}
|
2015-06-01 09:35:51 +00:00
|
|
|
}
|