mirror of
https://github.com/dmolony/DiskBrowser.git
synced 2024-06-14 12:29:34 +00:00
initial shk support
This commit is contained in:
parent
6aa882ecbd
commit
2cb40b84e4
|
@ -67,7 +67,7 @@ public interface ProdosConstants
|
||||||
"$48", "$49", "$4A", "$4B", "$4C", "$4D", "$4E", "$4F", //
|
"$48", "$49", "$4A", "$4B", "$4C", "$4D", "$4E", "$4F", //
|
||||||
"GWP", "GSS", "GDB", "DRW", "GDP", "HMD", "EDU", "STN", //
|
"GWP", "GSS", "GDB", "DRW", "GDP", "HMD", "EDU", "STN", //
|
||||||
"HLP", "COM", "CFG", "ANM", "MUM", "ENT", "DVU", "FIN", //
|
"HLP", "COM", "CFG", "ANM", "MUM", "ENT", "DVU", "FIN", //
|
||||||
"$60", "$61", "$62", "$63", "$64", "$65", "$66", "$67", //
|
"$60", "$61", "$62", "$63", "$64", "$65", "NCF", "$67", //
|
||||||
"$68", "$69", "$6A", "BIO", "$6C", "TDR", "PRE", "HDV", //
|
"$68", "$69", "$6A", "BIO", "$6C", "TDR", "PRE", "HDV", //
|
||||||
"$70", "$71", "$72", "$73", "$74", "$75", "$76", "$77", //
|
"$70", "$71", "$72", "$73", "$74", "$75", "$76", "$77", //
|
||||||
"$78", "$79", "$7A", "$7B", "$7C", "$7D", "$7E", "$7F", //
|
"$78", "$79", "$7A", "$7B", "$7C", "$7D", "$7E", "$7F", //
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.bytezone.diskbrowser.prodos.write;
|
||||||
|
|
||||||
|
public class DiskFullException extends Exception
|
||||||
|
{
|
||||||
|
public DiskFullException (String message)
|
||||||
|
{
|
||||||
|
super (message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -98,7 +98,7 @@ public class FileEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
void writeFile (byte[] dataBuffer)
|
void writeFile (byte[] dataBuffer) throws DiskFullException
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
{
|
{
|
||||||
this.eof = dataBuffer.length;
|
this.eof = dataBuffer.length;
|
||||||
|
@ -124,7 +124,7 @@ public class FileEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
void writeRecord (int recordNo, byte[] dataBuffer)
|
void writeRecord (int recordNo, byte[] dataBuffer) throws DiskFullException
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
{
|
{
|
||||||
assert auxType > 0; // record length
|
assert auxType > 0; // record length
|
||||||
|
@ -160,7 +160,7 @@ public class FileEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
int allocateNextBlock ()
|
int allocateNextBlock () throws DiskFullException
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
{
|
{
|
||||||
blocksUsed++;
|
blocksUsed++;
|
||||||
|
@ -168,7 +168,7 @@ public class FileEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
int getActualBlockNo (int logicalBlockNo)
|
int getActualBlockNo (int logicalBlockNo) throws DiskFullException
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
{
|
{
|
||||||
int actualBlockNo = 0;
|
int actualBlockNo = 0;
|
||||||
|
@ -211,7 +211,7 @@ public class FileEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
void map (int logicalBlockNo, int actualBlockNo)
|
void map (int logicalBlockNo, int actualBlockNo) throws DiskFullException
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
{
|
{
|
||||||
if (logicalBlockNo > 255) // potential TREE
|
if (logicalBlockNo > 255) // potential TREE
|
||||||
|
@ -280,7 +280,7 @@ public class FileEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
IndexBlock getIndexBlock (int position)
|
IndexBlock getIndexBlock (int position) throws DiskFullException
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
{
|
{
|
||||||
IndexBlock indexBlock = masterIndexBlock.get (position);
|
IndexBlock indexBlock = masterIndexBlock.get (position);
|
||||||
|
|
|
@ -33,7 +33,7 @@ public class ProdosDisk
|
||||||
private Map<Integer, SubdirectoryHeader> subdirectoryHeaders = new HashMap<> ();
|
private Map<Integer, SubdirectoryHeader> subdirectoryHeaders = new HashMap<> ();
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
public ProdosDisk (int blocks, String volumeName) throws IOException
|
public ProdosDisk (int blocks, String volumeName) throws IOException, DiskFullException
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
{
|
{
|
||||||
try (DataInputStream in = new DataInputStream (ProdosDisk.class.getClassLoader ()
|
try (DataInputStream in = new DataInputStream (ProdosDisk.class.getClassLoader ()
|
||||||
|
@ -63,7 +63,7 @@ public class ProdosDisk
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
void createCatalog (String volumeName)
|
void createCatalog (String volumeName) throws DiskFullException
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
{
|
{
|
||||||
// reserve two boot blocks
|
// reserve two boot blocks
|
||||||
|
@ -112,7 +112,8 @@ public class ProdosDisk
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
public FileEntry addFile (String path, int type, byte[] dataBuffer)
|
public FileEntry addFile (String path, byte type, int auxType, LocalDateTime created,
|
||||||
|
LocalDateTime modified, byte[] dataBuffer) throws DiskFullException
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
{
|
{
|
||||||
String[] subdirectories;
|
String[] subdirectories;
|
||||||
|
@ -158,7 +159,10 @@ public class ProdosDisk
|
||||||
fileEntry.version = 0x00;
|
fileEntry.version = 0x00;
|
||||||
fileEntry.minVersion = 0x00;
|
fileEntry.minVersion = 0x00;
|
||||||
fileEntry.headerPointer = catalogBlockNo;
|
fileEntry.headerPointer = catalogBlockNo;
|
||||||
fileEntry.fileType = 4; // text
|
fileEntry.fileType = type;
|
||||||
|
fileEntry.auxType = auxType;
|
||||||
|
fileEntry.creationDate = created;
|
||||||
|
fileEntry.modifiedDate = modified;
|
||||||
|
|
||||||
fileEntry.writeFile (dataBuffer);
|
fileEntry.writeFile (dataBuffer);
|
||||||
|
|
||||||
|
@ -221,7 +225,7 @@ public class ProdosDisk
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
private FileEntry createSubdirectory (int blockNo, String name)
|
private FileEntry createSubdirectory (int blockNo, String name) throws DiskFullException
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
{
|
{
|
||||||
FileEntry fileEntry = findFreeSlot (blockNo);
|
FileEntry fileEntry = findFreeSlot (blockNo);
|
||||||
|
@ -281,10 +285,15 @@ public class ProdosDisk
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
int allocateNextBlock ()
|
int allocateNextBlock () throws DiskFullException
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
{
|
{
|
||||||
int nextBlock = getFreeBlock ();
|
int nextBlock = getFreeBlock ();
|
||||||
|
if (nextBlock < 0)
|
||||||
|
{
|
||||||
|
throw new DiskFullException ("Full");
|
||||||
|
}
|
||||||
|
|
||||||
volumeBitMap.set (nextBlock, false); // mark as unavailable
|
volumeBitMap.set (nextBlock, false); // mark as unavailable
|
||||||
|
|
||||||
return nextBlock;
|
return nextBlock;
|
||||||
|
@ -298,7 +307,7 @@ public class ProdosDisk
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
private FileEntry findFreeSlot (int blockNo)
|
private FileEntry findFreeSlot (int blockNo) throws DiskFullException
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
{
|
{
|
||||||
SubdirectoryHeader subdirectoryHeader = subdirectoryHeaders.get (blockNo);
|
SubdirectoryHeader subdirectoryHeader = subdirectoryHeaders.get (blockNo);
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package com.bytezone.diskbrowser.utilities;
|
package com.bytezone.diskbrowser.utilities;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------//
|
// -----------------------------------------------------------------------------------//
|
||||||
class DateTime
|
class DateTime
|
||||||
// -----------------------------------------------------------------------------------//
|
// -----------------------------------------------------------------------------------//
|
||||||
|
@ -39,6 +41,14 @@ class DateTime
|
||||||
days[weekDay], day, months[month], year);
|
days[weekDay], day, months[month], year);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------//
|
||||||
|
public LocalDateTime getLocalDateTime ()
|
||||||
|
// ---------------------------------------------------------------------------------//
|
||||||
|
{
|
||||||
|
int adjustedYear = year + (year > 70 ? 1900 : 2000);
|
||||||
|
return LocalDateTime.of (adjustedYear, month + 1, day, hour, minute);
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
@Override
|
@Override
|
||||||
public String toString ()
|
public String toString ()
|
||||||
|
|
|
@ -4,9 +4,11 @@ import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.bytezone.diskbrowser.prodos.write.DiskFullException;
|
||||||
import com.bytezone.diskbrowser.prodos.write.ProdosDisk;
|
import com.bytezone.diskbrowser.prodos.write.ProdosDisk;
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------//
|
// -----------------------------------------------------------------------------------//
|
||||||
|
@ -55,11 +57,7 @@ public class NuFX
|
||||||
dataPtr += thread.getCompressedEOF ();
|
dataPtr += thread.getCompressedEOF ();
|
||||||
|
|
||||||
if (debug)
|
if (debug)
|
||||||
{
|
|
||||||
System.out.printf ("Thread: %d%n%n%s%n%n", i, thread);
|
System.out.printf ("Thread: %d%n%n%s%n%n", i, thread);
|
||||||
// if (rec == 122 && thread.hasFile ())
|
|
||||||
// System.out.println (HexFormatter.format (thread.getData ()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (record.hasFile ())
|
if (record.hasFile ())
|
||||||
|
@ -82,10 +80,9 @@ public class NuFX
|
||||||
}
|
}
|
||||||
else if (totalFiles > 0)
|
else if (totalFiles > 0)
|
||||||
{
|
{
|
||||||
// listFiles ();
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ProdosDisk disk = new ProdosDisk (1600, "DISKBROWSER");
|
ProdosDisk disk = new ProdosDisk (3200, "DISKBROWSER");
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (Record record : records)
|
for (Record record : records)
|
||||||
{
|
{
|
||||||
|
@ -93,15 +90,17 @@ public class NuFX
|
||||||
{
|
{
|
||||||
String fileName = record.getFileName ();
|
String fileName = record.getFileName ();
|
||||||
int fileSize = record.getFileSize ();
|
int fileSize = record.getFileSize ();
|
||||||
int fileType = record.getFileType ();
|
byte fileType = (byte) record.getFileType ();
|
||||||
int eof = record.getUncompressedSize ();
|
int eof = record.getUncompressedSize ();
|
||||||
|
int auxType = record.getAuxType ();
|
||||||
|
LocalDateTime created = record.getCreated ();
|
||||||
|
LocalDateTime modified = record.getModified ();
|
||||||
byte[] buffer = record.getData ();
|
byte[] buffer = record.getData ();
|
||||||
System.out.printf ("%3d %-35s %,7d %d %,7d %d%n", ++count, fileName,
|
|
||||||
fileSize, fileType, eof, buffer.length);
|
System.out.printf ("%3d %-35s %,7d %02X %,7d %,7d %,7d %s %s%n",
|
||||||
if (fileType == 4 && count > 10 && count < 16)
|
++count, fileName, fileSize, fileType, eof, auxType, buffer.length,
|
||||||
{
|
created, modified);
|
||||||
disk.addFile (fileName, fileType, buffer);
|
disk.addFile (fileName, fileType, auxType, created, modified, buffer);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,6 +112,10 @@ public class NuFX
|
||||||
{
|
{
|
||||||
e.printStackTrace ();
|
e.printStackTrace ();
|
||||||
}
|
}
|
||||||
|
catch (DiskFullException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace ();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -176,7 +179,7 @@ public class NuFX
|
||||||
public static void main (String[] args) throws FileFormatException, IOException
|
public static void main (String[] args) throws FileFormatException, IOException
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
{
|
{
|
||||||
File file = new File ("/Users/denismolony/Dropbox/Examples/SHK/Applenet20.shk");
|
File file = new File ("/Users/denismolony/Dropbox/Examples/SHK/DiversiCopy.3.1.shk");
|
||||||
|
|
||||||
NuFX nufx = new NuFX (file.toPath ());
|
NuFX nufx = new NuFX (file.toPath ());
|
||||||
System.out.println (nufx);
|
System.out.println (nufx);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package com.bytezone.diskbrowser.utilities;
|
package com.bytezone.diskbrowser.utilities;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -159,7 +160,12 @@ class Record
|
||||||
{
|
{
|
||||||
for (Thread thread : threads)
|
for (Thread thread : threads)
|
||||||
if (thread.hasFileName ())
|
if (thread.hasFileName ())
|
||||||
|
{
|
||||||
|
String fileName = thread.getFileName ();
|
||||||
|
if (separator != '/')
|
||||||
|
return fileName.replace (separator, '/');
|
||||||
return thread.getFileName ();
|
return thread.getFileName ();
|
||||||
|
}
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -171,6 +177,31 @@ class Record
|
||||||
return fileType;
|
return fileType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------//
|
||||||
|
int getAuxType ()
|
||||||
|
// ---------------------------------------------------------------------------------//
|
||||||
|
{
|
||||||
|
return auxType;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------//
|
||||||
|
LocalDateTime getCreated ()
|
||||||
|
// ---------------------------------------------------------------------------------//
|
||||||
|
{
|
||||||
|
if (created == null)
|
||||||
|
return null;
|
||||||
|
return created.getLocalDateTime ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------//
|
||||||
|
LocalDateTime getModified ()
|
||||||
|
// ---------------------------------------------------------------------------------//
|
||||||
|
{
|
||||||
|
if (modified == null)
|
||||||
|
return null;
|
||||||
|
return modified.getLocalDateTime ();
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
int getFileSize ()
|
int getFileSize ()
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
|
@ -214,7 +245,7 @@ class Record
|
||||||
bits = bits.substring (bits.length () - 8);
|
bits = bits.substring (bits.length () - 8);
|
||||||
String decode = Utility.matchFlags (access, accessChars);
|
String decode = Utility.matchFlags (access, accessChars);
|
||||||
|
|
||||||
text.append (String.format ("Header CRC ..... %,d (%04X)%n", crc, crc));
|
text.append (String.format ("Header CRC ..... %,d (%<04X)%n", crc));
|
||||||
text.append (String.format ("Attributes ..... %d%n", attributes));
|
text.append (String.format ("Attributes ..... %d%n", attributes));
|
||||||
text.append (String.format ("Version ........ %d%n", version));
|
text.append (String.format ("Version ........ %d%n", version));
|
||||||
text.append (String.format ("Threads ........ %d%n", totThreads));
|
text.append (String.format ("Threads ........ %d%n", totThreads));
|
||||||
|
|
|
@ -195,12 +195,33 @@ public class Utility
|
||||||
int minute = buffer[offset + 2] & 0x3F;
|
int minute = buffer[offset + 2] & 0x3F;
|
||||||
int hour = buffer[offset + 3] & 0x1F;
|
int hour = buffer[offset + 3] & 0x1F;
|
||||||
|
|
||||||
|
if (month < 1 || month > 12)
|
||||||
|
{
|
||||||
|
System.out.printf ("Invalid month: %d%n", month);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hour > 23)
|
||||||
|
{
|
||||||
|
System.out.printf ("Invalid hour: %d%n", hour);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (year < 70)
|
if (year < 70)
|
||||||
year += 2000;
|
year += 2000;
|
||||||
else
|
else
|
||||||
year += 1900;
|
year += 1900;
|
||||||
return LocalDateTime.of (year, month - 1, day, hour, minute);
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return LocalDateTime.of (year, month - 1, day, hour, minute);
|
||||||
|
}
|
||||||
|
catch (DateTimeException e)
|
||||||
|
{
|
||||||
|
System.out.println ("Bad date/time");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,14 +270,15 @@ public class Utility
|
||||||
public static int readShort (byte[] buffer, int ptr)
|
public static int readShort (byte[] buffer, int ptr)
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
{
|
{
|
||||||
return buffer[ptr] | buffer[ptr + 1] << 8;
|
return (buffer[ptr] & 0xFF) | (buffer[ptr + 1] & 0xFF) << 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
public static int readTriple (byte[] buffer, int ptr)
|
public static int readTriple (byte[] buffer, int ptr)
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
{
|
{
|
||||||
return buffer[ptr] | buffer[ptr + 1] << 8 | buffer[ptr + 2] << 16;
|
return (buffer[ptr] & 0xFF) | (buffer[ptr + 1] & 0xFF) << 8
|
||||||
|
| (buffer[ptr + 2] & 0xFF) << 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------//
|
// ---------------------------------------------------------------------------------//
|
||||||
|
|
Loading…
Reference in New Issue
Block a user