method header lines

This commit is contained in:
Denis Molony 2020-02-09 23:13:33 +10:00
parent 5b640cbb1c
commit 6b3c7011d3
17 changed files with 2209 additions and 2037 deletions

View File

@ -11,7 +11,9 @@ import com.bytezone.diskbrowser.disk.DiskAddress;
import com.bytezone.diskbrowser.disk.FormattedDisk; import com.bytezone.diskbrowser.disk.FormattedDisk;
import com.bytezone.diskbrowser.utilities.HexFormatter; import com.bytezone.diskbrowser.utilities.HexFormatter;
// -----------------------------------------------------------------------------------//
abstract class CatalogEntry implements AppleFileSource abstract class CatalogEntry implements AppleFileSource
// -----------------------------------------------------------------------------------//
{ {
protected AbstractFile file; protected AbstractFile file;
protected final PascalDisk parent; protected final PascalDisk parent;
@ -23,7 +25,9 @@ abstract class CatalogEntry implements AppleFileSource
protected int bytesUsedInLastBlock; protected int bytesUsedInLastBlock;
protected final List<DiskAddress> blocks = new ArrayList<> (); protected final List<DiskAddress> blocks = new ArrayList<> ();
public CatalogEntry (PascalDisk parent, byte[] buffer) // ---------------------------------------------------------------------------------//
CatalogEntry (PascalDisk parent, byte[] buffer)
// ---------------------------------------------------------------------------------//
{ {
this.parent = parent; this.parent = parent;
@ -39,8 +43,10 @@ abstract class CatalogEntry implements AppleFileSource
blocks.add (disk.getDiskAddress (i)); blocks.add (disk.getDiskAddress (i));
} }
// ---------------------------------------------------------------------------------//
@Override @Override
public boolean contains (DiskAddress da) public boolean contains (DiskAddress da)
// ---------------------------------------------------------------------------------//
{ {
for (DiskAddress sector : blocks) for (DiskAddress sector : blocks)
if (sector.matches (da)) if (sector.matches (da))
@ -48,27 +54,35 @@ abstract class CatalogEntry implements AppleFileSource
return false; return false;
} }
// ---------------------------------------------------------------------------------//
@Override @Override
public List<DiskAddress> getSectors () public List<DiskAddress> getSectors ()
// ---------------------------------------------------------------------------------//
{ {
List<DiskAddress> sectors = new ArrayList<> (blocks); List<DiskAddress> sectors = new ArrayList<> (blocks);
return sectors; return sectors;
} }
// ---------------------------------------------------------------------------------//
@Override @Override
public FormattedDisk getFormattedDisk () public FormattedDisk getFormattedDisk ()
// ---------------------------------------------------------------------------------//
{ {
return parent; return parent;
} }
// ---------------------------------------------------------------------------------//
@Override @Override
public String getUniqueName () public String getUniqueName ()
// ---------------------------------------------------------------------------------//
{ {
return name; return name;
} }
// ---------------------------------------------------------------------------------//
@Override @Override
public String toString () public String toString ()
// ---------------------------------------------------------------------------------//
{ {
int size = lastBlock - firstBlock; int size = lastBlock - firstBlock;
String fileTypeText = fileType < 0 || fileType >= parent.fileTypes.length ? "????" String fileTypeText = fileType < 0 || fileType >= parent.fileTypes.length ? "????"

View File

@ -2,15 +2,26 @@ package com.bytezone.diskbrowser.pascal;
import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultMutableTreeNode;
import com.bytezone.diskbrowser.applefile.*; import com.bytezone.diskbrowser.applefile.AbstractFile;
import com.bytezone.diskbrowser.applefile.AssemblerProgram;
import com.bytezone.diskbrowser.applefile.Charset;
import com.bytezone.diskbrowser.applefile.DefaultAppleFile;
import com.bytezone.diskbrowser.applefile.PascalCode;
import com.bytezone.diskbrowser.applefile.PascalInfo;
import com.bytezone.diskbrowser.applefile.PascalSegment;
import com.bytezone.diskbrowser.applefile.PascalText;
import com.bytezone.diskbrowser.utilities.FileFormatException; import com.bytezone.diskbrowser.utilities.FileFormatException;
import com.bytezone.diskbrowser.utilities.HexFormatter; import com.bytezone.diskbrowser.utilities.HexFormatter;
// -----------------------------------------------------------------------------------//
public class FileEntry extends CatalogEntry public class FileEntry extends CatalogEntry
// -----------------------------------------------------------------------------------//
{ {
private DefaultMutableTreeNode node; private DefaultMutableTreeNode node;
// ---------------------------------------------------------------------------------//
public FileEntry (PascalDisk parent, byte[] buffer) public FileEntry (PascalDisk parent, byte[] buffer)
// ---------------------------------------------------------------------------------//
{ {
super (parent, buffer); super (parent, buffer);
@ -30,18 +41,24 @@ public class FileEntry extends CatalogEntry
} }
} }
// ---------------------------------------------------------------------------------//
void setNode (DefaultMutableTreeNode node) void setNode (DefaultMutableTreeNode node)
// ---------------------------------------------------------------------------------//
{ {
this.node = node; this.node = node;
} }
// ---------------------------------------------------------------------------------//
public void setFile (AbstractFile file) public void setFile (AbstractFile file)
// ---------------------------------------------------------------------------------//
{ {
this.file = file; this.file = file;
} }
// ---------------------------------------------------------------------------------//
@Override @Override
public AbstractFile getDataSource () public AbstractFile getDataSource ()
// ---------------------------------------------------------------------------------//
{ {
if (file != null) if (file != null)
return file; return file;
@ -99,7 +116,9 @@ public class FileEntry extends CatalogEntry
return file; return file;
} }
// ---------------------------------------------------------------------------------//
private byte[] getExactBuffer () private byte[] getExactBuffer ()
// ---------------------------------------------------------------------------------//
{ {
byte[] buffer = parent.getDisk ().readSectors (blocks); byte[] buffer = parent.getDisk ().readSectors (blocks);
byte[] exactBuffer; byte[] exactBuffer;

View File

@ -9,19 +9,25 @@ import com.bytezone.diskbrowser.disk.Disk;
import com.bytezone.diskbrowser.disk.DiskAddress; import com.bytezone.diskbrowser.disk.DiskAddress;
import com.bytezone.diskbrowser.utilities.HexFormatter; import com.bytezone.diskbrowser.utilities.HexFormatter;
// -----------------------------------------------------------------------------------//
class PascalCatalogSector extends AbstractSector class PascalCatalogSector extends AbstractSector
// -----------------------------------------------------------------------------------//
{ {
private final DateFormat df = DateFormat.getDateInstance (DateFormat.SHORT); private final DateFormat df = DateFormat.getDateInstance (DateFormat.SHORT);
private static String[] fileTypes = private static String[] fileTypes =
{ "Volume", "Bad", "Code", "Text", "Info", "Data", "Graf", "Foto", "SecureDir" }; { "Volume", "Bad", "Code", "Text", "Info", "Data", "Graf", "Foto", "SecureDir" };
public PascalCatalogSector (Disk disk, byte[] buffer, List<DiskAddress> diskAddress) // ---------------------------------------------------------------------------------//
PascalCatalogSector (Disk disk, byte[] buffer, List<DiskAddress> diskAddress)
// ---------------------------------------------------------------------------------//
{ {
super (disk, buffer); super (disk, buffer);
} }
// ---------------------------------------------------------------------------------//
@Override @Override
public String createText () public String createText ()
// ---------------------------------------------------------------------------------//
{ {
StringBuilder text = getHeader ("Pascal Catalog Sectors"); StringBuilder text = getHeader ("Pascal Catalog Sectors");

View File

@ -11,13 +11,17 @@ import com.bytezone.diskbrowser.disk.DiskAddress;
import com.bytezone.diskbrowser.disk.FormattedDisk; import com.bytezone.diskbrowser.disk.FormattedDisk;
import com.bytezone.diskbrowser.gui.DataSource; import com.bytezone.diskbrowser.gui.DataSource;
// -----------------------------------------------------------------------------------//
class PascalCodeObject implements AppleFileSource class PascalCodeObject implements AppleFileSource
// -----------------------------------------------------------------------------------//
{ {
private final PascalDisk parent; private final PascalDisk parent;
private final AbstractFile segment; private final AbstractFile segment;
private final List<DiskAddress> blocks = new ArrayList<> (); private final List<DiskAddress> blocks = new ArrayList<> ();
public PascalCodeObject (PascalDisk parent, PascalSegment segment, int firstBlock) // ---------------------------------------------------------------------------------//
PascalCodeObject (PascalDisk parent, PascalSegment segment, int firstBlock)
// ---------------------------------------------------------------------------------//
{ {
this.parent = parent; this.parent = parent;
this.segment = segment; this.segment = segment;
@ -30,26 +34,34 @@ class PascalCodeObject implements AppleFileSource
blocks.add (disk.getDiskAddress (i)); blocks.add (disk.getDiskAddress (i));
} }
// ---------------------------------------------------------------------------------//
@Override @Override
public DataSource getDataSource () public DataSource getDataSource ()
// ---------------------------------------------------------------------------------//
{ {
return segment; return segment;
} }
// ---------------------------------------------------------------------------------//
@Override @Override
public FormattedDisk getFormattedDisk () public FormattedDisk getFormattedDisk ()
// ---------------------------------------------------------------------------------//
{ {
return parent; return parent;
} }
// ---------------------------------------------------------------------------------//
@Override @Override
public List<DiskAddress> getSectors () public List<DiskAddress> getSectors ()
// ---------------------------------------------------------------------------------//
{ {
return blocks; return blocks;
} }
// ---------------------------------------------------------------------------------//
@Override @Override
public boolean contains (DiskAddress da) public boolean contains (DiskAddress da)
// ---------------------------------------------------------------------------------//
{ {
for (DiskAddress sector : blocks) for (DiskAddress sector : blocks)
if (sector.matches (da)) if (sector.matches (da))
@ -57,14 +69,18 @@ class PascalCodeObject implements AppleFileSource
return false; return false;
} }
// ---------------------------------------------------------------------------------//
@Override @Override
public String getUniqueName () public String getUniqueName ()
// ---------------------------------------------------------------------------------//
{ {
return segment.getName (); // this should be fileName/segmentName return segment.getName (); // this should be fileName/segmentName
} }
// ---------------------------------------------------------------------------------//
@Override @Override
public String toString () public String toString ()
// ---------------------------------------------------------------------------------//
{ {
return segment.getName (); return segment.getName ();
} }

View File

@ -1,286 +1,310 @@
package com.bytezone.diskbrowser.pascal; package com.bytezone.diskbrowser.pascal;
import java.awt.Color; import java.awt.Color;
import java.text.DateFormat; import java.text.DateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.List; import java.util.List;
import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultMutableTreeNode;
import com.bytezone.diskbrowser.applefile.AppleFileSource; import com.bytezone.diskbrowser.applefile.AppleFileSource;
import com.bytezone.diskbrowser.applefile.BootSector; import com.bytezone.diskbrowser.applefile.BootSector;
import com.bytezone.diskbrowser.disk.*; import com.bytezone.diskbrowser.disk.AbstractFormattedDisk;
import com.bytezone.diskbrowser.gui.DataSource; import com.bytezone.diskbrowser.disk.AppleDisk;
import com.bytezone.diskbrowser.utilities.HexFormatter; import com.bytezone.diskbrowser.disk.DefaultAppleFileSource;
import com.bytezone.diskbrowser.wizardry.Relocator; import com.bytezone.diskbrowser.disk.DefaultSector;
import com.bytezone.diskbrowser.disk.Disk;
public class PascalDisk extends AbstractFormattedDisk import com.bytezone.diskbrowser.disk.DiskAddress;
{ import com.bytezone.diskbrowser.disk.SectorType;
static final int CATALOG_ENTRY_SIZE = 26; import com.bytezone.diskbrowser.gui.DataSource;
private final DateFormat df = DateFormat.getDateInstance (DateFormat.SHORT); import com.bytezone.diskbrowser.utilities.HexFormatter;
private final VolumeEntry volumeEntry; import com.bytezone.diskbrowser.wizardry.Relocator;
private final PascalCatalogSector diskCatalogSector;
// -----------------------------------------------------------------------------------//
protected Relocator relocator; public class PascalDisk extends AbstractFormattedDisk
// -----------------------------------------------------------------------------------//
final String[] fileTypes = {
{ "Volume", "Bad ", "Code", "Text", "Info", "Data", "Graf", "Foto", "SecureDir" }; static final int CATALOG_ENTRY_SIZE = 26;
private final DateFormat df = DateFormat.getDateInstance (DateFormat.SHORT);
SectorType diskBootSector = new SectorType ("Boot", Color.lightGray); private final VolumeEntry volumeEntry;
SectorType catalogSector = new SectorType ("Catalog", Color.magenta); private final PascalCatalogSector diskCatalogSector;
SectorType dataSector = new SectorType ("Data", new Color (0, 200, 0)); // green
SectorType codeSector = new SectorType ("Code", Color.red); protected Relocator relocator;
SectorType textSector = new SectorType ("Text", Color.blue);
SectorType infoSector = new SectorType ("Info", Color.orange); final String[] fileTypes =
SectorType grafSector = new SectorType ("Graf", Color.cyan); { "Volume", "Bad ", "Code", "Text", "Info", "Data", "Graf", "Foto", "SecureDir" };
SectorType fotoSector = new SectorType ("Foto", Color.gray);
SectorType badSector = new SectorType ("Bad", Color.darkGray); SectorType diskBootSector = new SectorType ("Boot", Color.lightGray);
SectorType catalogSector = new SectorType ("Catalog", Color.magenta);
SectorType[] sectors = { catalogSector, badSector, codeSector, textSector, infoSector, SectorType dataSector = new SectorType ("Data", new Color (0, 200, 0)); // green
dataSector, grafSector, fotoSector }; SectorType codeSector = new SectorType ("Code", Color.red);
SectorType textSector = new SectorType ("Text", Color.blue);
public PascalDisk (Disk disk) SectorType infoSector = new SectorType ("Info", Color.orange);
{ SectorType grafSector = new SectorType ("Graf", Color.cyan);
super (disk); SectorType fotoSector = new SectorType ("Foto", Color.gray);
SectorType badSector = new SectorType ("Bad", Color.darkGray);
sectorTypesList.add (diskBootSector);
sectorTypesList.add (catalogSector); SectorType[] sectors = { catalogSector, badSector, codeSector, textSector, infoSector,
sectorTypesList.add (dataSector); dataSector, grafSector, fotoSector };
sectorTypesList.add (codeSector);
sectorTypesList.add (textSector); // ---------------------------------------------------------------------------------//
sectorTypesList.add (infoSector); public PascalDisk (Disk disk)
sectorTypesList.add (grafSector); // ---------------------------------------------------------------------------------//
sectorTypesList.add (fotoSector); {
sectorTypesList.add (badSector); super (disk);
List<DiskAddress> blocks = disk.getDiskAddressList (0, 1); // B0, B1 sectorTypesList.add (diskBootSector);
this.bootSector = new BootSector (disk, disk.readSectors (blocks), "Pascal"); sectorTypesList.add (catalogSector);
sectorTypesList.add (dataSector);
for (int i = 0; i < 2; i++) sectorTypesList.add (codeSector);
if (!disk.isSectorEmpty (i)) sectorTypesList.add (textSector);
{ sectorTypesList.add (infoSector);
sectorTypes[i] = diskBootSector; sectorTypesList.add (grafSector);
freeBlocks.set (i, false); sectorTypesList.add (fotoSector);
} sectorTypesList.add (badSector);
for (int i = 2; i < disk.getTotalBlocks (); i++) List<DiskAddress> blocks = disk.getDiskAddressList (0, 1); // B0, B1
freeBlocks.set (i, true); this.bootSector = new BootSector (disk, disk.readSectors (blocks), "Pascal");
byte[] buffer = disk.readSector (2); for (int i = 0; i < 2; i++)
byte[] data = new byte[CATALOG_ENTRY_SIZE]; if (!disk.isSectorEmpty (i))
System.arraycopy (buffer, 0, data, 0, CATALOG_ENTRY_SIZE); {
volumeEntry = new VolumeEntry (this, data); sectorTypes[i] = diskBootSector;
freeBlocks.set (i, false);
DefaultMutableTreeNode root = getCatalogTreeRoot (); }
DefaultMutableTreeNode volumeNode = new DefaultMutableTreeNode (volumeEntry);
root.add (volumeNode); for (int i = 2; i < disk.getTotalBlocks (); i++)
freeBlocks.set (i, true);
List<DiskAddress> sectors = new ArrayList<> ();
int max = Math.min (volumeEntry.lastBlock, disk.getTotalBlocks ()); byte[] buffer = disk.readSector (2);
for (int i = 2; i < max; i++) byte[] data = new byte[CATALOG_ENTRY_SIZE];
{ System.arraycopy (buffer, 0, data, 0, CATALOG_ENTRY_SIZE);
DiskAddress da = disk.getDiskAddress (i); volumeEntry = new VolumeEntry (this, data);
if (!disk.isSectorEmpty (da))
sectorTypes[i] = catalogSector; DefaultMutableTreeNode root = getCatalogTreeRoot ();
sectors.add (da); DefaultMutableTreeNode volumeNode = new DefaultMutableTreeNode (volumeEntry);
freeBlocks.set (i, false); root.add (volumeNode);
}
List<DiskAddress> sectors = new ArrayList<> ();
diskCatalogSector = int max = Math.min (volumeEntry.lastBlock, disk.getTotalBlocks ());
new PascalCatalogSector (disk, disk.readSectors (sectors), sectors); for (int i = 2; i < max; i++)
{
// read the catalog DiskAddress da = disk.getDiskAddress (i);
List<DiskAddress> addresses = new ArrayList<> (); if (!disk.isSectorEmpty (da))
for (int i = 2; i < max; i++) sectorTypes[i] = catalogSector;
addresses.add (disk.getDiskAddress (i)); sectors.add (da);
buffer = disk.readSectors (addresses); freeBlocks.set (i, false);
}
// loop through each catalog entry (what if there are deleted files?)
for (int i = 1; i <= volumeEntry.totalFiles; i++) diskCatalogSector =
{ new PascalCatalogSector (disk, disk.readSectors (sectors), sectors);
int ptr = i * CATALOG_ENTRY_SIZE;
data = new byte[CATALOG_ENTRY_SIZE]; // read the catalog
System.arraycopy (buffer, ptr, data, 0, CATALOG_ENTRY_SIZE); List<DiskAddress> addresses = new ArrayList<> ();
FileEntry fileEntry = new FileEntry (this, data); for (int i = 2; i < max; i++)
addresses.add (disk.getDiskAddress (i));
fileEntries.add (fileEntry); buffer = disk.readSectors (addresses);
DefaultMutableTreeNode node = new DefaultMutableTreeNode (fileEntry);
fileEntry.setNode (node); // loop through each catalog entry (what if there are deleted files?)
for (int i = 1; i <= volumeEntry.totalFiles; i++)
if (fileEntry.fileType == 2) {
{ int ptr = i * CATALOG_ENTRY_SIZE;
node.setAllowsChildren (true); data = new byte[CATALOG_ENTRY_SIZE];
fileEntry.getDataSource (); // build segments System.arraycopy (buffer, ptr, data, 0, CATALOG_ENTRY_SIZE);
} FileEntry fileEntry = new FileEntry (this, data);
else
node.setAllowsChildren (false); fileEntries.add (fileEntry);
DefaultMutableTreeNode node = new DefaultMutableTreeNode (fileEntry);
volumeNode.add (node); fileEntry.setNode (node);
for (int j = fileEntry.firstBlock; j < fileEntry.lastBlock; j++)
freeBlocks.set (j, false); if (fileEntry.fileType == 2)
} {
node.setAllowsChildren (true);
volumeNode.setUserObject (getCatalog ()); fileEntry.getDataSource (); // build segments
makeNodeVisible (volumeNode.getFirstLeaf ()); }
} else
node.setAllowsChildren (false);
public static boolean isCorrectFormat (AppleDisk disk, boolean debug)
{ volumeNode.add (node);
disk.setInterleave (1); // should only ever be Prodos for (int j = fileEntry.firstBlock; j < fileEntry.lastBlock; j++)
if (checkFormat (disk, debug)) freeBlocks.set (j, false);
return true; }
disk.setInterleave (0); // see SANE Disk 2.po
if (checkFormat (disk, debug)) volumeNode.setUserObject (getCatalog ());
return true; makeNodeVisible (volumeNode.getFirstLeaf ());
return false; }
}
// ---------------------------------------------------------------------------------//
public static boolean checkFormat (AppleDisk disk, boolean debug) public static boolean isCorrectFormat (AppleDisk disk, boolean debug)
{ // ---------------------------------------------------------------------------------//
byte[] buffer = disk.readSector (2); {
int nameLength = buffer[6] & 0xFF; disk.setInterleave (1); // should only ever be Prodos
if (nameLength < 1 || nameLength > 7) if (checkFormat (disk, debug))
{ return true;
if (debug) disk.setInterleave (0); // see SANE Disk 2.po
System.out.println ("bad name length : " + nameLength); if (checkFormat (disk, debug))
return false; return true;
} return false;
}
if (debug)
{ // ---------------------------------------------------------------------------------//
String name = HexFormatter.getPascalString (buffer, 6); public static boolean checkFormat (AppleDisk disk, boolean debug)
System.out.println ("Name ok : " + name); // ---------------------------------------------------------------------------------//
} {
byte[] buffer = disk.readSector (2);
int from = HexFormatter.intValue (buffer[0], buffer[1]); int nameLength = buffer[6] & 0xFF;
int to = HexFormatter.intValue (buffer[2], buffer[3]); if (nameLength < 1 || nameLength > 7)
if (from != 0 || to != 6) {
{ if (debug)
if (debug) System.out.println ("bad name length : " + nameLength);
System.out.printf ("from: %d, to: %d%n", from, to); return false;
return false; // will only work for floppies! }
}
if (debug)
int blocks = HexFormatter.intValue (buffer[14], buffer[15]); {
if (blocks > 280) String name = HexFormatter.getPascalString (buffer, 6);
{ System.out.println ("Name ok : " + name);
if (debug) }
System.out.printf ("Blocks > 280: %d%n", blocks);
// return false; int from = HexFormatter.intValue (buffer[0], buffer[1]);
} int to = HexFormatter.intValue (buffer[2], buffer[3]);
if (from != 0 || to != 6)
List<DiskAddress> addresses = new ArrayList<> (); {
for (int i = 2; i < to; i++) if (debug)
addresses.add (disk.getDiskAddress (i)); System.out.printf ("from: %d, to: %d%n", from, to);
buffer = disk.readSectors (addresses); return false; // will only work for floppies!
}
int files = HexFormatter.intValue (buffer[16], buffer[17]);
if (files < 0 || files > 77) int blocks = HexFormatter.intValue (buffer[14], buffer[15]);
{ if (blocks > 280)
if (debug) {
System.out.printf ("Files: %d%n", files); if (debug)
return false; System.out.printf ("Blocks > 280: %d%n", blocks);
} // return false;
}
if (debug)
System.out.println ("Files found : " + files); List<DiskAddress> addresses = new ArrayList<> ();
for (int i = 2; i < to; i++)
for (int i = 1; i <= files; i++) addresses.add (disk.getDiskAddress (i));
{ buffer = disk.readSectors (addresses);
int ptr = i * 26;
int firstBlock = HexFormatter.intValue (buffer[ptr], buffer[ptr + 1]); int files = HexFormatter.intValue (buffer[16], buffer[17]);
int lastBlock = HexFormatter.intValue (buffer[ptr + 2], buffer[ptr + 3]); if (files < 0 || files > 77)
int kind = HexFormatter.intValue (buffer[ptr + 4], buffer[ptr + 5]); {
if (lastBlock < firstBlock) if (debug)
return false; System.out.printf ("Files: %d%n", files);
if (kind == 0) return false;
return false; }
nameLength = buffer[ptr + 6] & 0xFF;
if (nameLength < 1 || nameLength > 15) if (debug)
return false; System.out.println ("Files found : " + files);
int lastByte = HexFormatter.intValue (buffer[ptr + 22], buffer[ptr + 23]);
GregorianCalendar date = HexFormatter.getPascalDate (buffer, 24); for (int i = 1; i <= files; i++)
if (debug) {
System.out.printf ("%4d %4d %d %-15s %d %s%n", firstBlock, lastBlock, kind, int ptr = i * 26;
new String (buffer, ptr + 7, nameLength), lastByte, date); int firstBlock = HexFormatter.intValue (buffer[ptr], buffer[ptr + 1]);
} int lastBlock = HexFormatter.intValue (buffer[ptr + 2], buffer[ptr + 3]);
int kind = HexFormatter.intValue (buffer[ptr + 4], buffer[ptr + 5]);
return true; if (lastBlock < firstBlock)
} return false;
if (kind == 0)
@Override return false;
public DataSource getFormattedSector (DiskAddress da) nameLength = buffer[ptr + 6] & 0xFF;
{ if (nameLength < 1 || nameLength > 15)
SectorType st = sectorTypes[da.getBlock ()]; return false;
if (st == diskBootSector) int lastByte = HexFormatter.intValue (buffer[ptr + 22], buffer[ptr + 23]);
return bootSector; GregorianCalendar date = HexFormatter.getPascalDate (buffer, 24);
if (st == catalogSector) if (debug)
return diskCatalogSector; System.out.printf ("%4d %4d %d %-15s %d %s%n", firstBlock, lastBlock, kind,
String name = getSectorFilename (da); new String (buffer, ptr + 7, nameLength), lastByte, date);
if (name != null) }
return new DefaultSector (name, disk, disk.readSector (da), da);
return super.getFormattedSector (da); return true;
} }
@Override // ---------------------------------------------------------------------------------//
public String getSectorFilename (DiskAddress da) @Override
{ public DataSource getFormattedSector (DiskAddress da)
for (AppleFileSource ce : fileEntries) // ---------------------------------------------------------------------------------//
if (((CatalogEntry) ce).contains (da)) {
return ((CatalogEntry) ce).name; SectorType st = sectorTypes[da.getBlock ()];
return null; if (st == diskBootSector)
} return bootSector;
if (st == catalogSector)
@Override return diskCatalogSector;
public List<DiskAddress> getFileSectors (int fileNo) String name = getSectorFilename (da);
{ if (name != null)
if (fileNo < 0 || fileNo >= fileEntries.size ()) return new DefaultSector (name, disk, disk.readSector (da), da);
return null; return super.getFormattedSector (da);
return fileEntries.get (fileNo).getSectors (); }
}
// ---------------------------------------------------------------------------------//
public DataSource getFile (int fileNo) @Override
{ public String getSectorFilename (DiskAddress da)
if (fileNo < 0 || fileNo >= fileEntries.size ()) // ---------------------------------------------------------------------------------//
return null; {
return fileEntries.get (fileNo).getDataSource (); for (AppleFileSource ce : fileEntries)
} if (((CatalogEntry) ce).contains (da))
return ((CatalogEntry) ce).name;
@Override return null;
public AppleFileSource getCatalog () }
{
String newLine = String.format ("%n"); // ---------------------------------------------------------------------------------//
String newLine2 = newLine + newLine; @Override
String line = "---- --------------- ---- -------- ------- ---- ---- ----" public List<DiskAddress> getFileSectors (int fileNo)
+ newLine; // ---------------------------------------------------------------------------------//
String date = {
volumeEntry.date == null ? "--" : df.format (volumeEntry.date.getTime ()); if (fileNo < 0 || fileNo >= fileEntries.size ())
StringBuilder text = new StringBuilder (); return null;
text.append ("Disk : " + getDisplayPath () + newLine2); return fileEntries.get (fileNo).getSectors ();
text.append ("Volume : " + volumeEntry.name + newLine); }
text.append ("Date : " + date + newLine2);
text.append ( // ---------------------------------------------------------------------------------//
"Blks Name Type Date Length Frst Last Blks\n"); public DataSource getFile (int fileNo)
text.append (line); // ---------------------------------------------------------------------------------//
{
int usedBlocks = 6; if (fileNo < 0 || fileNo >= fileEntries.size ())
for (AppleFileSource fe : fileEntries) return null;
{ return fileEntries.get (fileNo).getDataSource ();
FileEntry ce = (FileEntry) fe; }
int size = ce.lastBlock - ce.firstBlock;
usedBlocks += size; // ---------------------------------------------------------------------------------//
date = ce.date == null ? "--" : df.format (ce.date.getTime ()); @Override
int bytes = (size - 1) * 512 + ce.bytesUsedInLastBlock; public AppleFileSource getCatalog ()
String fileType = ce.fileType < 0 || ce.fileType >= fileTypes.length ? "????" // ---------------------------------------------------------------------------------//
: fileTypes[ce.fileType]; {
text.append (String.format ("%4d %-15s %s %8s %,8d $%03X $%03X $%03X%n", String newLine = String.format ("%n");
size, ce.name, fileType, date, bytes, ce.firstBlock, ce.lastBlock, size)); String newLine2 = newLine + newLine;
} String line = "---- --------------- ---- -------- ------- ---- ---- ----"
text.append (line); + newLine;
text.append ( String date =
String.format ("Blocks free : %3d Blocks used : %3d Total blocks : %3d%n", volumeEntry.date == null ? "--" : df.format (volumeEntry.date.getTime ());
(volumeEntry.totalBlocks - usedBlocks), usedBlocks, volumeEntry.totalBlocks)); StringBuilder text = new StringBuilder ();
return new DefaultAppleFileSource (volumeEntry.name, text.toString (), this); text.append ("Disk : " + getDisplayPath () + newLine2);
} text.append ("Volume : " + volumeEntry.name + newLine);
text.append ("Date : " + date + newLine2);
text.append (
"Blks Name Type Date Length Frst Last Blks\n");
text.append (line);
int usedBlocks = 6;
for (AppleFileSource fe : fileEntries)
{
FileEntry ce = (FileEntry) fe;
int size = ce.lastBlock - ce.firstBlock;
usedBlocks += size;
date = ce.date == null ? "--" : df.format (ce.date.getTime ());
int bytes = (size - 1) * 512 + ce.bytesUsedInLastBlock;
String fileType = ce.fileType < 0 || ce.fileType >= fileTypes.length ? "????"
: fileTypes[ce.fileType];
text.append (String.format ("%4d %-15s %s %8s %,8d $%03X $%03X $%03X%n",
size, ce.name, fileType, date, bytes, ce.firstBlock, ce.lastBlock, size));
}
text.append (line);
text.append (
String.format ("Blocks free : %3d Blocks used : %3d Total blocks : %3d%n",
(volumeEntry.totalBlocks - usedBlocks), usedBlocks, volumeEntry.totalBlocks));
return new DefaultAppleFileSource (volumeEntry.name, text.toString (), this);
}
} }

View File

@ -4,12 +4,16 @@ import com.bytezone.diskbrowser.applefile.AbstractFile;
import com.bytezone.diskbrowser.applefile.DefaultAppleFile; import com.bytezone.diskbrowser.applefile.DefaultAppleFile;
import com.bytezone.diskbrowser.utilities.HexFormatter; import com.bytezone.diskbrowser.utilities.HexFormatter;
// -----------------------------------------------------------------------------------//
class VolumeEntry extends CatalogEntry class VolumeEntry extends CatalogEntry
// -----------------------------------------------------------------------------------//
{ {
final int totalFiles; final int totalFiles;
final int totalBlocks; final int totalBlocks;
public VolumeEntry (PascalDisk parent, byte[] buffer) // ---------------------------------------------------------------------------------//
VolumeEntry (PascalDisk parent, byte[] buffer)
// ---------------------------------------------------------------------------------//
{ {
super (parent, buffer); super (parent, buffer);
@ -18,8 +22,10 @@ class VolumeEntry extends CatalogEntry
date = HexFormatter.getPascalDate (buffer, 20); // 2 bytes date = HexFormatter.getPascalDate (buffer, 20); // 2 bytes
} }
// ---------------------------------------------------------------------------------//
@Override @Override
public AbstractFile getDataSource () public AbstractFile getDataSource ()
// ---------------------------------------------------------------------------------//
{ {
if (file != null) if (file != null)
return file; return file;

View File

@ -1,74 +1,86 @@
package com.bytezone.diskbrowser.prodos; package com.bytezone.diskbrowser.prodos;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.List; import java.util.List;
import com.bytezone.diskbrowser.applefile.AppleFileSource; import com.bytezone.diskbrowser.applefile.AppleFileSource;
import com.bytezone.diskbrowser.disk.Disk; import com.bytezone.diskbrowser.disk.Disk;
import com.bytezone.diskbrowser.disk.DiskAddress; import com.bytezone.diskbrowser.disk.DiskAddress;
import com.bytezone.diskbrowser.disk.FormattedDisk; import com.bytezone.diskbrowser.disk.FormattedDisk;
import com.bytezone.diskbrowser.utilities.HexFormatter; import com.bytezone.diskbrowser.utilities.HexFormatter;
abstract class CatalogEntry implements AppleFileSource // -----------------------------------------------------------------------------------//
{ abstract class CatalogEntry implements AppleFileSource
ProdosDisk parentDisk; // -----------------------------------------------------------------------------------//
DirectoryHeader parentDirectory; {
String name; ProdosDisk parentDisk;
int storageType; DirectoryHeader parentDirectory;
GregorianCalendar created; String name;
int version; int storageType;
int minVersion; GregorianCalendar created;
int access; int version;
List<DiskAddress> dataBlocks = new ArrayList<> (); int minVersion;
Disk disk; int access;
List<DiskAddress> dataBlocks = new ArrayList<> ();
public CatalogEntry (ProdosDisk parentDisk, byte[] entryBuffer) Disk disk;
{
this.parentDisk = parentDisk; // ---------------------------------------------------------------------------------//
this.disk = parentDisk.getDisk (); CatalogEntry (ProdosDisk parentDisk, byte[] entryBuffer)
name = HexFormatter.getString (entryBuffer, 1, entryBuffer[0] & 0x0F); // ---------------------------------------------------------------------------------//
storageType = (entryBuffer[0] & 0xF0) >> 4; {
created = HexFormatter.getAppleDate (entryBuffer, 24); this.parentDisk = parentDisk;
version = entryBuffer[28] & 0xFF; this.disk = parentDisk.getDisk ();
minVersion = entryBuffer[29] & 0xFF; name = HexFormatter.getString (entryBuffer, 1, entryBuffer[0] & 0x0F);
access = entryBuffer[30] & 0xFF; storageType = (entryBuffer[0] & 0xF0) >> 4;
} created = HexFormatter.getAppleDate (entryBuffer, 24);
version = entryBuffer[28] & 0xFF;
@Override minVersion = entryBuffer[29] & 0xFF;
public String getUniqueName () access = entryBuffer[30] & 0xFF;
{ }
if (parentDirectory == null)
return name; // ---------------------------------------------------------------------------------//
return parentDirectory.getUniqueName () + "/" + name; @Override
} public String getUniqueName ()
// ---------------------------------------------------------------------------------//
@Override {
public FormattedDisk getFormattedDisk () if (parentDirectory == null)
{ return name;
return parentDisk; return parentDirectory.getUniqueName () + "/" + name;
} }
@Override // ---------------------------------------------------------------------------------//
public boolean contains (DiskAddress da) @Override
{ public FormattedDisk getFormattedDisk ()
for (DiskAddress sector : dataBlocks) // ---------------------------------------------------------------------------------//
if (sector.matches (da)) {
return true; return parentDisk;
return false; }
}
// ---------------------------------------------------------------------------------//
@Override @Override
public String toString () public boolean contains (DiskAddress da)
{ // ---------------------------------------------------------------------------------//
StringBuilder text = new StringBuilder (); {
for (DiskAddress sector : dataBlocks)
text.append (String.format ("Name .......... %s%n", name)); if (sector.matches (da))
text.append (String.format ("Storage type... %02X%n", storageType)); return true;
text.append (String.format ("Created ....... %s%n", return false;
created == null ? "" : parentDisk.df.format (created.getTime ()))); }
text.append (String.format ("Version ....... %d%n", version));
// ---------------------------------------------------------------------------------//
return text.toString (); @Override
} public String toString ()
// ---------------------------------------------------------------------------------//
{
StringBuilder text = new StringBuilder ();
text.append (String.format ("Name .......... %s%n", name));
text.append (String.format ("Storage type... %02X%n", storageType));
text.append (String.format ("Created ....... %s%n",
created == null ? "" : parentDisk.df.format (created.getTime ())));
text.append (String.format ("Version ....... %d%n", version));
return text.toString ();
}
} }

View File

@ -1,19 +1,23 @@
package com.bytezone.diskbrowser.prodos; package com.bytezone.diskbrowser.prodos;
import com.bytezone.diskbrowser.utilities.HexFormatter; import com.bytezone.diskbrowser.utilities.HexFormatter;
abstract class DirectoryHeader extends CatalogEntry // -----------------------------------------------------------------------------------//
{ abstract class DirectoryHeader extends CatalogEntry
final int entryLength; // -----------------------------------------------------------------------------------//
final int entriesPerBlock; {
final int fileCount; final int entryLength;
final int entriesPerBlock;
public DirectoryHeader (ProdosDisk parentDisk, byte[] entryBuffer) final int fileCount;
{
super (parentDisk, entryBuffer); // ---------------------------------------------------------------------------------//
DirectoryHeader (ProdosDisk parentDisk, byte[] entryBuffer)
entryLength = entryBuffer[31] & 0xFF; // ---------------------------------------------------------------------------------//
entriesPerBlock = entryBuffer[32] & 0xFF; {
fileCount = HexFormatter.intValue (entryBuffer[33], entryBuffer[34]); super (parentDisk, entryBuffer);
}
entryLength = entryBuffer[31] & 0xFF;
entriesPerBlock = entryBuffer[32] & 0xFF;
fileCount = HexFormatter.intValue (entryBuffer[33], entryBuffer[34]);
}
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,69 +1,75 @@
package com.bytezone.diskbrowser.prodos; package com.bytezone.diskbrowser.prodos;
import java.awt.Dimension; import java.awt.Dimension;
import com.bytezone.diskbrowser.disk.AbstractSector; import com.bytezone.diskbrowser.disk.AbstractSector;
import com.bytezone.diskbrowser.disk.Disk; import com.bytezone.diskbrowser.disk.Disk;
import com.bytezone.diskbrowser.disk.DiskAddress; import com.bytezone.diskbrowser.disk.DiskAddress;
class ProdosBitMapSector extends AbstractSector // -----------------------------------------------------------------------------------//
{ class ProdosBitMapSector extends AbstractSector
private final ProdosDisk parent; // -----------------------------------------------------------------------------------//
{
ProdosBitMapSector (ProdosDisk parent, Disk disk, byte[] buffer, DiskAddress da) private final ProdosDisk parent;
{
super (disk, buffer, da); // ---------------------------------------------------------------------------------//
this.parent = parent; ProdosBitMapSector (ProdosDisk parent, Disk disk, byte[] buffer, DiskAddress da)
} // ---------------------------------------------------------------------------------//
{
@Override super (disk, buffer, da);
public String createText () this.parent = parent;
{ }
Dimension grid = parent.getGridLayout ();
// ---------------------------------------------------------------------------------//
// check range of bits for current block - so far I don't have a disk that needs @Override
// more than a single block public String createText ()
int relativeBlock = // ---------------------------------------------------------------------------------//
diskAddress.getBlock () - parent.getVolumeDirectoryHeader ().bitMapBlock; {
int startBit = relativeBlock * 4096; Dimension grid = parent.getGridLayout ();
// int endBit = startBit + 4096;
if (startBit >= grid.width * grid.height) // check range of bits for current block - so far I don't have a disk that needs
return "This sector is not used - the physical file size makes it unnecessary"; // more than a single block
int relativeBlock =
int width = (grid.width - 1) / 8 + 1; // must be 1-4 diskAddress.getBlock () - parent.getVolumeDirectoryHeader ().bitMapBlock;
int startBit = relativeBlock * 4096;
StringBuilder text = getHeader ("Volume Bit Map Block"); // int endBit = startBit + 4096;
if (startBit >= grid.width * grid.height)
if (false) return "This sector is not used - the physical file size makes it unnecessary";
{
int block = 0; int width = (grid.width - 1) / 8 + 1; // must be 1-4
for (int row = 0; row < grid.height; row++)
{ StringBuilder text = getHeader ("Volume Bit Map Block");
int offset = block / 8;
StringBuilder details = new StringBuilder (); if (false)
for (int col = 0; col < grid.width; col++) {
details.append (parent.isSectorFree (block++) ? ". " : "X "); int block = 0;
addText (text, buffer, offset, width, details.toString ()); for (int row = 0; row < grid.height; row++)
} {
} int offset = block / 8;
else StringBuilder details = new StringBuilder ();
{ for (int col = 0; col < grid.width; col++)
int startRow = relativeBlock * 512 / width; details.append (parent.isSectorFree (block++) ? ". " : "X ");
int endRow = startRow + (512 / width); addText (text, buffer, offset, width, details.toString ());
int block = startBit; }
int byteNo = 0; }
// System.out.printf ("Start %d, end %d%n", startRow, endRow); else
for (int row = startRow; row < endRow; row++) {
{ int startRow = relativeBlock * 512 / width;
StringBuilder details = new StringBuilder (); int endRow = startRow + (512 / width);
for (int col = 0; col < grid.width; col++) int block = startBit;
details.append (parent.isSectorFree (block++) ? ". " : "X "); int byteNo = 0;
addText (text, buffer, byteNo, width, details.toString ()); // System.out.printf ("Start %d, end %d%n", startRow, endRow);
byteNo += width; for (int row = startRow; row < endRow; row++)
} {
} StringBuilder details = new StringBuilder ();
for (int col = 0; col < grid.width; col++)
text.deleteCharAt (text.length () - 1); details.append (parent.isSectorFree (block++) ? ". " : "X ");
return text.toString (); addText (text, buffer, byteNo, width, details.toString ());
} byteNo += width;
}
}
text.deleteCharAt (text.length () - 1);
return text.toString ();
}
} }

View File

@ -1,203 +1,232 @@
package com.bytezone.diskbrowser.prodos; package com.bytezone.diskbrowser.prodos;
import static com.bytezone.diskbrowser.prodos.ProdosConstants.*; import static com.bytezone.diskbrowser.prodos.ProdosConstants.ENTRY_SIZE;
import static com.bytezone.diskbrowser.prodos.ProdosConstants.FREE;
import java.util.GregorianCalendar; import static com.bytezone.diskbrowser.prodos.ProdosConstants.GSOS_EXTENDED_FILE;
import static com.bytezone.diskbrowser.prodos.ProdosConstants.PASCAL_ON_PROFILE;
import com.bytezone.diskbrowser.disk.AbstractSector; import static com.bytezone.diskbrowser.prodos.ProdosConstants.SAPLING;
import com.bytezone.diskbrowser.disk.Disk; import static com.bytezone.diskbrowser.prodos.ProdosConstants.SEEDLING;
import com.bytezone.diskbrowser.disk.DiskAddress; import static com.bytezone.diskbrowser.prodos.ProdosConstants.SUBDIRECTORY;
import com.bytezone.diskbrowser.utilities.HexFormatter; import static com.bytezone.diskbrowser.prodos.ProdosConstants.SUBDIRECTORY_HEADER;
import static com.bytezone.diskbrowser.prodos.ProdosConstants.TREE;
class ProdosCatalogSector extends AbstractSector import static com.bytezone.diskbrowser.prodos.ProdosConstants.VOLUME_HEADER;
{
private final ProdosDisk parent; import java.util.GregorianCalendar;
ProdosCatalogSector (ProdosDisk parent, Disk disk, byte[] buffer, import com.bytezone.diskbrowser.disk.AbstractSector;
DiskAddress diskAddress) import com.bytezone.diskbrowser.disk.Disk;
{ import com.bytezone.diskbrowser.disk.DiskAddress;
super (disk, buffer, diskAddress); import com.bytezone.diskbrowser.utilities.HexFormatter;
this.parent = parent;
} // -----------------------------------------------------------------------------------//
class ProdosCatalogSector extends AbstractSector
@Override // -----------------------------------------------------------------------------------//
public String createText () {
{ private final ProdosDisk parent;
StringBuilder text = getHeader ("Volume Directory Block");
// ---------------------------------------------------------------------------------//
addTextAndDecimal (text, buffer, 0, 2, "Previous block"); ProdosCatalogSector (ProdosDisk parent, Disk disk, byte[] buffer,
addTextAndDecimal (text, buffer, 2, 2, "Next block"); DiskAddress diskAddress)
// ---------------------------------------------------------------------------------//
for (int i = 4, max = buffer.length - ENTRY_SIZE; i <= max; i += ENTRY_SIZE) {
{ super (disk, buffer, diskAddress);
if (buffer[i] == 0 && buffer[i + 1] == 0) this.parent = parent;
break; }
text.append ("\n");
// ---------------------------------------------------------------------------------//
// first byte contains the file type (left nybble) and name length (right nybble) @Override
int fileType = (buffer[i] & 0xF0) >> 4; public String createText ()
int nameLength = buffer[i] & 0x0F; // ---------------------------------------------------------------------------------//
String hex1 = String.format ("%02X", buffer[i] & 0xF0); {
String hex2 = String.format ("%02X", nameLength); StringBuilder text = getHeader ("Volume Directory Block");
// deleted files set file type and name length to zero, but the file addTextAndDecimal (text, buffer, 0, 2, "Previous block");
// name is still valid addTextAndDecimal (text, buffer, 2, 2, "Next block");
String typeText = hex1 + " = " + getType (buffer[i]);
if (fileType == 0) for (int i = 4, max = buffer.length - ENTRY_SIZE; i <= max; i += ENTRY_SIZE)
addText (text, buffer, i, 1, typeText + " : " + getDeletedName (i + 1)); {
else if (buffer[i] == 0 && buffer[i + 1] == 0)
addText (text, buffer, i, 1, typeText + ", " + hex2 + " = Name length"); break;
text.append ("\n");
addText (text, buffer, i + 1, 4,
HexFormatter.getString (buffer, i + 1, nameLength)); // first byte contains the file type (left nybble) and name length (right nybble)
int fileType = (buffer[i] & 0xF0) >> 4;
switch (fileType) int nameLength = buffer[i] & 0x0F;
{ String hex1 = String.format ("%02X", buffer[i] & 0xF0);
case FREE: String hex2 = String.format ("%02X", nameLength);
case SEEDLING:
case SAPLING: // deleted files set file type and name length to zero, but the file
case TREE: // name is still valid
case PASCAL_ON_PROFILE: String typeText = hex1 + " = " + getType (buffer[i]);
case GSOS_EXTENDED_FILE: if (fileType == 0)
case SUBDIRECTORY: addText (text, buffer, i, 1, typeText + " : " + getDeletedName (i + 1));
text.append (doFileDescription (i)); else
break; addText (text, buffer, i, 1, typeText + ", " + hex2 + " = Name length");
case SUBDIRECTORY_HEADER:
text.append (doSubdirectoryHeader (i)); addText (text, buffer, i + 1, 4,
break; HexFormatter.getString (buffer, i + 1, nameLength));
case VOLUME_HEADER:
text.append (doVolumeDirectoryHeader (i)); switch (fileType)
break; {
default: case FREE:
text.append ("Unknown\n"); case SEEDLING:
} case SAPLING:
} case TREE:
if (text.length () > 0) case PASCAL_ON_PROFILE:
text.deleteCharAt (text.length () - 1); case GSOS_EXTENDED_FILE:
return text.toString (); case SUBDIRECTORY:
} text.append (doFileDescription (i));
break;
private String doFileDescription (int offset) case SUBDIRECTORY_HEADER:
{ text.append (doSubdirectoryHeader (i));
StringBuilder text = new StringBuilder (); break;
int fileType = buffer[offset + 16] & 0xFF; case VOLUME_HEADER:
int auxType = HexFormatter.unsignedShort (buffer, offset + 31); text.append (doVolumeDirectoryHeader (i));
addText (text, buffer, offset + 16, 1, break;
"File type (" + ProdosConstants.fileTypes[fileType] + ")"); default:
addTextAndDecimal (text, buffer, offset + 17, 2, "Key pointer"); text.append ("Unknown\n");
addTextAndDecimal (text, buffer, offset + 19, 2, "Blocks used"); }
addTextAndDecimal (text, buffer, offset + 21, 3, "EOF"); }
GregorianCalendar created = HexFormatter.getAppleDate (buffer, offset + 24); if (text.length () > 0)
String dateC = created == null ? "" : parent.df.format (created.getTime ()); text.deleteCharAt (text.length () - 1);
addText (text, buffer, offset + 24, 4, "Creation date : " + dateC); return text.toString ();
addTextAndDecimal (text, buffer, offset + 28, 1, "Version"); }
addText (text, buffer, offset + 29, 1, "Minimum version");
addText (text, buffer, offset + 30, 1, "Access"); // ---------------------------------------------------------------------------------//
addTextAndDecimal (text, buffer, offset + 31, 2, private String doFileDescription (int offset)
"Auxilliary type - " + getAuxilliaryText (fileType, auxType)); // ---------------------------------------------------------------------------------//
GregorianCalendar modified = HexFormatter.getAppleDate (buffer, offset + 33); {
String dateM = modified == null ? "" : parent.df.format (modified.getTime ()); StringBuilder text = new StringBuilder ();
addText (text, buffer, offset + 33, 4, "Modification date : " + dateM); int fileType = buffer[offset + 16] & 0xFF;
addTextAndDecimal (text, buffer, offset + 37, 2, "Header pointer"); int auxType = HexFormatter.unsignedShort (buffer, offset + 31);
return text.toString (); addText (text, buffer, offset + 16, 1,
} "File type (" + ProdosConstants.fileTypes[fileType] + ")");
addTextAndDecimal (text, buffer, offset + 17, 2, "Key pointer");
private String doVolumeDirectoryHeader (int offset) addTextAndDecimal (text, buffer, offset + 19, 2, "Blocks used");
{ addTextAndDecimal (text, buffer, offset + 21, 3, "EOF");
StringBuilder text = new StringBuilder (); GregorianCalendar created = HexFormatter.getAppleDate (buffer, offset + 24);
addText (text, buffer, offset + 16, 4, "Not used"); String dateC = created == null ? "" : parent.df.format (created.getTime ());
text.append (getCommonHeader (offset)); addText (text, buffer, offset + 24, 4, "Creation date : " + dateC);
addTextAndDecimal (text, buffer, offset + 35, 2, "Bit map pointer"); addTextAndDecimal (text, buffer, offset + 28, 1, "Version");
addTextAndDecimal (text, buffer, offset + 37, 2, "Total blocks"); addText (text, buffer, offset + 29, 1, "Minimum version");
return text.toString (); addText (text, buffer, offset + 30, 1, "Access");
} addTextAndDecimal (text, buffer, offset + 31, 2,
"Auxilliary type - " + getAuxilliaryText (fileType, auxType));
private String doSubdirectoryHeader (int offset) GregorianCalendar modified = HexFormatter.getAppleDate (buffer, offset + 33);
{ String dateM = modified == null ? "" : parent.df.format (modified.getTime ());
StringBuilder text = new StringBuilder (); addText (text, buffer, offset + 33, 4, "Modification date : " + dateM);
addText (text, buffer, offset + 16, 1, "Hex $75"); addTextAndDecimal (text, buffer, offset + 37, 2, "Header pointer");
addText (text, buffer, offset + 17, 3, "Not used"); return text.toString ();
text.append (getCommonHeader (offset)); }
addTextAndDecimal (text, buffer, offset + 35, 2, "Parent block");
addTextAndDecimal (text, buffer, offset + 37, 1, "Parent entry number"); // ---------------------------------------------------------------------------------//
addTextAndDecimal (text, buffer, offset + 38, 1, "Parent entry length"); private String doVolumeDirectoryHeader (int offset)
return text.toString (); // ---------------------------------------------------------------------------------//
} {
StringBuilder text = new StringBuilder ();
private String getCommonHeader (int offset) addText (text, buffer, offset + 16, 4, "Not used");
{ text.append (getCommonHeader (offset));
StringBuilder text = new StringBuilder (); addTextAndDecimal (text, buffer, offset + 35, 2, "Bit map pointer");
addText (text, buffer, offset + 20, 4, "Not used"); addTextAndDecimal (text, buffer, offset + 37, 2, "Total blocks");
GregorianCalendar created = HexFormatter.getAppleDate (buffer, offset + 24); return text.toString ();
String dateC = created == null ? "" : parent.df.format (created.getTime ()); }
addText (text, buffer, offset + 24, 4, "Creation date : " + dateC);
addText (text, buffer, offset + 28, 1, "Prodos version"); // ---------------------------------------------------------------------------------//
addText (text, buffer, offset + 29, 1, "Minimum version"); private String doSubdirectoryHeader (int offset)
addText (text, buffer, offset + 30, 1, "Access"); // ---------------------------------------------------------------------------------//
addTextAndDecimal (text, buffer, offset + 31, 1, "Entry length"); {
addTextAndDecimal (text, buffer, offset + 32, 1, "Entries per block"); StringBuilder text = new StringBuilder ();
addTextAndDecimal (text, buffer, offset + 33, 2, "File count"); addText (text, buffer, offset + 16, 1, "Hex $75");
addText (text, buffer, offset + 17, 3, "Not used");
return text.toString (); text.append (getCommonHeader (offset));
} addTextAndDecimal (text, buffer, offset + 35, 2, "Parent block");
addTextAndDecimal (text, buffer, offset + 37, 1, "Parent entry number");
private String getAuxilliaryText (int fileType, int auxType) addTextAndDecimal (text, buffer, offset + 38, 1, "Parent entry length");
{ return text.toString ();
switch (fileType) }
{
case 0x04: // ---------------------------------------------------------------------------------//
return "record length"; private String getCommonHeader (int offset)
case 0xFD: // ---------------------------------------------------------------------------------//
return "address of stored variables"; {
case 0x06: StringBuilder text = new StringBuilder ();
case 0xFC: addText (text, buffer, offset + 20, 4, "Not used");
case 0xFF: GregorianCalendar created = HexFormatter.getAppleDate (buffer, offset + 24);
return "load address"; String dateC = created == null ? "" : parent.df.format (created.getTime ());
case 0xB3: addText (text, buffer, offset + 24, 4, "Creation date : " + dateC);
return "forked"; addText (text, buffer, offset + 28, 1, "Prodos version");
case 0xC1: addText (text, buffer, offset + 29, 1, "Minimum version");
return "PIC"; addText (text, buffer, offset + 30, 1, "Access");
case 0xC8: addTextAndDecimal (text, buffer, offset + 31, 1, "Entry length");
return auxType == 0 ? "QuickDraw bitmap font" addTextAndDecimal (text, buffer, offset + 32, 1, "Entries per block");
: auxType == 1 ? "Pointless truetype font" : "??"; addTextAndDecimal (text, buffer, offset + 33, 2, "File count");
default:
return "???"; return text.toString ();
} }
}
// ---------------------------------------------------------------------------------//
private String getType (byte flag) private String getAuxilliaryText (int fileType, int auxType)
{ // ---------------------------------------------------------------------------------//
switch ((flag & 0xF0) >> 4) {
{ switch (fileType)
case FREE: {
return "Deleted"; case 0x04:
case SEEDLING: return "record length";
return "Seedling"; case 0xFD:
case SAPLING: return "address of stored variables";
return "Sapling"; case 0x06:
case TREE: case 0xFC:
return "Tree"; case 0xFF:
case PASCAL_ON_PROFILE: return "load address";
return "Pascal area on a Profile HD"; case 0xB3:
case GSOS_EXTENDED_FILE: return "forked";
return "GS/OS extended file"; case 0xC1:
case SUBDIRECTORY: return "PIC";
return "Subdirectory"; case 0xC8:
case SUBDIRECTORY_HEADER: return auxType == 0 ? "QuickDraw bitmap font"
return "Subdirectory Header"; : auxType == 1 ? "Pointless truetype font" : "??";
case VOLUME_HEADER: default:
return "Volume Directory Header"; return "???";
default: }
return "???"; }
}
} // ---------------------------------------------------------------------------------//
private String getType (byte flag)
// Deleted files leave the name intact, but set the name length to zero // ---------------------------------------------------------------------------------//
private String getDeletedName (int offset) {
{ switch ((flag & 0xF0) >> 4)
StringBuilder text = new StringBuilder (); {
for (int i = offset, max = offset + 15; i < max && buffer[i] != 0; i++) case FREE:
text.append ((char) (buffer[i] & 0xFF)); return "Deleted";
return text.toString (); case SEEDLING:
} return "Seedling";
case SAPLING:
return "Sapling";
case TREE:
return "Tree";
case PASCAL_ON_PROFILE:
return "Pascal area on a Profile HD";
case GSOS_EXTENDED_FILE:
return "GS/OS extended file";
case SUBDIRECTORY:
return "Subdirectory";
case SUBDIRECTORY_HEADER:
return "Subdirectory Header";
case VOLUME_HEADER:
return "Volume Directory Header";
default:
return "???";
}
}
// Deleted files leave the name intact, but set the name length to zero
// ---------------------------------------------------------------------------------//
private String getDeletedName (int offset)
// ---------------------------------------------------------------------------------//
{
StringBuilder text = new StringBuilder ();
for (int i = offset, max = offset + 15; i < max && buffer[i] != 0; i++)
text.append ((char) (buffer[i] & 0xFF));
return text.toString ();
}
} }

View File

@ -1,253 +1,255 @@
package com.bytezone.diskbrowser.prodos; package com.bytezone.diskbrowser.prodos;
public interface ProdosConstants // -----------------------------------------------------------------------------------//
{ public interface ProdosConstants
static int FILE_TYPE_NON = 0x00; // -----------------------------------------------------------------------------------//
static int FILE_TYPE_PCD = 0x02; {
static int FILE_TYPE_TEXT = 0x04; static int FILE_TYPE_NON = 0x00;
static int FILE_TYPE_PDA = 0x05; static int FILE_TYPE_PCD = 0x02;
static int FILE_TYPE_BINARY = 0x06; static int FILE_TYPE_TEXT = 0x04;
static int FILE_TYPE_FNT = 0x07; static int FILE_TYPE_PDA = 0x05;
static int FILE_TYPE_FOT = 0x08; // was Apple /// FotoFile static int FILE_TYPE_BINARY = 0x06;
static int FILE_TYPE_DIRECTORY = 0x0F; static int FILE_TYPE_FNT = 0x07;
static int FILE_TYPE_ADB = 0x19; static int FILE_TYPE_FOT = 0x08; // was Apple /// FotoFile
static int FILE_TYPE_AWP = 0x1A; static int FILE_TYPE_DIRECTORY = 0x0F;
static int FILE_TYPE_ASP = 0x1B; static int FILE_TYPE_ADB = 0x19;
static int FILE_TYPE_DESCRIPTOR_TABLE = 0x42; static int FILE_TYPE_AWP = 0x1A;
static int FILE_TYPE_GWP = 0x50; static int FILE_TYPE_ASP = 0x1B;
static int FILE_TYPE_GEO = 0x82; static int FILE_TYPE_DESCRIPTOR_TABLE = 0x42;
static int FILE_TYPE_IIGS_SOURCE = 0xB0; static int FILE_TYPE_GWP = 0x50;
static int FILE_TYPE_IIGS_OBJECT = 0xB1; static int FILE_TYPE_GEO = 0x82;
static int FILE_TYPE_IIGS_APPLICATION = 0xB3; static int FILE_TYPE_IIGS_SOURCE = 0xB0;
static int FILE_TYPE_IIGS_DEVICE_DRIVER = 0xBB; static int FILE_TYPE_IIGS_OBJECT = 0xB1;
static int FILE_TYPE_LDF = 0xBC; static int FILE_TYPE_IIGS_APPLICATION = 0xB3;
static int FILE_TYPE_GS_BASIC = 0xAB; static int FILE_TYPE_IIGS_DEVICE_DRIVER = 0xBB;
static int FILE_TYPE_GSOS_FILE_SYSTEM_TRANSLATOR = 0xBD; static int FILE_TYPE_LDF = 0xBC;
static int FILE_TYPE_PNT = 0xC0; static int FILE_TYPE_GS_BASIC = 0xAB;
static int FILE_TYPE_PIC = 0xC1; static int FILE_TYPE_GSOS_FILE_SYSTEM_TRANSLATOR = 0xBD;
static int FILE_TYPE_ANI = 0xC2; static int FILE_TYPE_PNT = 0xC0;
static int FILE_TYPE_PAL = 0xC3; static int FILE_TYPE_PIC = 0xC1;
static int FILE_TYPE_FONT = 0xC8; static int FILE_TYPE_ANI = 0xC2;
static int FILE_TYPE_FINDER = 0xC9; static int FILE_TYPE_PAL = 0xC3;
static int FILE_TYPE_ICN = 0xCA; static int FILE_TYPE_FONT = 0xC8;
static int FILE_TYPE_APPLETALK = 0xE2; static int FILE_TYPE_FINDER = 0xC9;
static int FILE_TYPE_PASCAL_VOLUME = 0xEF; static int FILE_TYPE_ICN = 0xCA;
static int FILE_TYPE_USER_DEFINED_1 = 0xF1; // OVL static int FILE_TYPE_APPLETALK = 0xE2;
static int FILE_TYPE_BAT = 0xF5; static int FILE_TYPE_PASCAL_VOLUME = 0xEF;
static int FILE_TYPE_INTEGER_BASIC = 0xFA; static int FILE_TYPE_USER_DEFINED_1 = 0xF1; // OVL
static int FILE_TYPE_INTEGER_BASIC_VARS = 0xFB; static int FILE_TYPE_BAT = 0xF5;
static int FILE_TYPE_APPLESOFT_BASIC = 0xFC; static int FILE_TYPE_INTEGER_BASIC = 0xFA;
static int FILE_TYPE_APPLESOFT_BASIC_VARS = 0xFD; static int FILE_TYPE_INTEGER_BASIC_VARS = 0xFB;
static int FILE_TYPE_RELOCATABLE = 0xFE; static int FILE_TYPE_APPLESOFT_BASIC = 0xFC;
static int FILE_TYPE_SYS = 0xFF; static int FILE_TYPE_APPLESOFT_BASIC_VARS = 0xFD;
static int FILE_TYPE_RELOCATABLE = 0xFE;
static int VOLUME_HEADER = 15; static int FILE_TYPE_SYS = 0xFF;
static int SUBDIRECTORY_HEADER = 14;
static int SUBDIRECTORY = 13; static int VOLUME_HEADER = 15;
static int GSOS_EXTENDED_FILE = 5; // tech note #25 static int SUBDIRECTORY_HEADER = 14;
static int PASCAL_ON_PROFILE = 4; // tech note #25 static int SUBDIRECTORY = 13;
static int TREE = 3; static int GSOS_EXTENDED_FILE = 5; // tech note #25
static int SAPLING = 2; static int PASCAL_ON_PROFILE = 4; // tech note #25
static int SEEDLING = 1; static int TREE = 3;
static int FREE = 0; static int SAPLING = 2;
static int SEEDLING = 1;
static String[] fileTypes = { // static int FREE = 0;
"NON", "BAD", "PCD", "PTX", "TXT", "PDA", "BIN", "FNT", //
"FOT", "BA3", "DA3", "WPF", "SOS", "$0D", "$0E", "DIR", // static String[] fileTypes = { //
"RPD", "RPI", "AFD", "AFM", "AFR", "SCL", "PFS", "$17", // "NON", "BAD", "PCD", "PTX", "TXT", "PDA", "BIN", "FNT", //
"$18", "ADB", "AWP", "ASP", "$1C", "$1D", "$1E", "$1F", // "FOT", "BA3", "DA3", "WPF", "SOS", "$0D", "$0E", "DIR", //
"TDM", "$21", "$22", "$23", "$24", "$25", "$26", "$27", // "RPD", "RPI", "AFD", "AFM", "AFR", "SCL", "PFS", "$17", //
"$28", "$29", "8SC", "8OB", "8IC", "8LD", "P8C", "$2F", // "$18", "ADB", "AWP", "ASP", "$1C", "$1D", "$1E", "$1F", //
"$30", "$31", "$32", "$33", "$34", "$35", "$36", "$37", // "TDM", "$21", "$22", "$23", "$24", "$25", "$26", "$27", //
"$38", "$39", "$3A", "$3B", "$3C", "$3D", "$3E", "$3F", // "$28", "$29", "8SC", "8OB", "8IC", "8LD", "P8C", "$2F", //
"DIC", "OCR", "FTD", "$43", "$44", "$45", "$46", "$47", // "$30", "$31", "$32", "$33", "$34", "$35", "$36", "$37", //
"$48", "$49", "$4A", "$4B", "$4C", "$4D", "$4E", "$4F", // "$38", "$39", "$3A", "$3B", "$3C", "$3D", "$3E", "$3F", //
"GWP", "GSS", "GDB", "DRW", "GDP", "HMD", "EDU", "STN", // "DIC", "OCR", "FTD", "$43", "$44", "$45", "$46", "$47", //
"HLP", "COM", "CFG", "ANM", "MUM", "ENT", "DVU", "FIN", // "$48", "$49", "$4A", "$4B", "$4C", "$4D", "$4E", "$4F", //
"$60", "$61", "$62", "$63", "$64", "$65", "$66", "$67", // "GWP", "GSS", "GDB", "DRW", "GDP", "HMD", "EDU", "STN", //
"$68", "$69", "$6A", "BIO", "$6C", "TDR", "PRE", "HDV", // "HLP", "COM", "CFG", "ANM", "MUM", "ENT", "DVU", "FIN", //
"$70", "$71", "$72", "$73", "$74", "$75", "$76", "$77", // "$60", "$61", "$62", "$63", "$64", "$65", "$66", "$67", //
"$78", "$79", "$7A", "$7B", "$7C", "$7D", "$7E", "$7F", // "$68", "$69", "$6A", "BIO", "$6C", "TDR", "PRE", "HDV", //
"GES", "GEA", "GEO", "GED", "GEF", "GEP", "GEI", "GEX", // "$70", "$71", "$72", "$73", "$74", "$75", "$76", "$77", //
"$88", "GEV", "$8A", "GEC", "GEK", "GEW", "$8E", "$8F", // "$78", "$79", "$7A", "$7B", "$7C", "$7D", "$7E", "$7F", //
"$90", "$91", "$92", "$93", "$94", "$95", "$96", "$97", // "GES", "GEA", "GEO", "GED", "GEF", "GEP", "GEI", "GEX", //
"$98", "$99", "$9A", "$9B", "$9C", "$9D", "$9E", "$9F", // "$88", "GEV", "$8A", "GEC", "GEK", "GEW", "$8E", "$8F", //
"WP ", "$A1", "$A2", "$A3", "$A4", "$A5", "$A6", "$A7", // "$90", "$91", "$92", "$93", "$94", "$95", "$96", "$97", //
"$A8", "$A9", "$AA", "GSB", "TDF", "BDF", "$AE", "$AF", // "$98", "$99", "$9A", "$9B", "$9C", "$9D", "$9E", "$9F", //
"SRC", "OBJ", "LIB", "S16", "RTL", "EXE", "PIF", "TIF", // "WP ", "$A1", "$A2", "$A3", "$A4", "$A5", "$A6", "$A7", //
"NDA", "CDA", "TOL", "DVR", "LDF", "FST", "$BE", "DOC", // "$A8", "$A9", "$AA", "GSB", "TDF", "BDF", "$AE", "$AF", //
"PNT", "PIC", "ANI", "PAL", "$C4", "OOG", "SCR", "CDV", // "SRC", "OBJ", "LIB", "S16", "RTL", "EXE", "PIF", "TIF", //
"FON", "FND", "ICN", "$CB", "$CC", "$CD", "$CE", "$CF", // "NDA", "CDA", "TOL", "DVR", "LDF", "FST", "$BE", "DOC", //
"$D0", "$D1", "$D2", "$D3", "$D4", "MUS", "INS", "MDI", // "PNT", "PIC", "ANI", "PAL", "$C4", "OOG", "SCR", "CDV", //
"SND", "$D9", "$DA", "DBM", "$DC", "DDD", "$DE", "$DF", // "FON", "FND", "ICN", "$CB", "$CC", "$CD", "$CE", "$CF", //
"LBR", "$E1", "ATK", "$E3", "$E4", "$E5", "$E6", "$E7", // "$D0", "$D1", "$D2", "$D3", "$D4", "MUS", "INS", "MDI", //
"$E8", "$E9", "$EA", "$EB", "$EC", "$ED", "R16", "PAR", // "SND", "$D9", "$DA", "DBM", "$DC", "DDD", "$DE", "$DF", //
"CMD", "OVL", "UD2", "UD3", "UD4", "BAT", "UD6", "UD7", // "LBR", "$E1", "ATK", "$E3", "$E4", "$E5", "$E6", "$E7", //
"PRG", "P16", "INT", "IVR", "BAS", "VAR", "REL", "SYS" }; "$E8", "$E9", "$EA", "$EB", "$EC", "$ED", "R16", "PAR", //
"CMD", "OVL", "UD2", "UD3", "UD4", "BAT", "UD6", "UD7", //
static int ENTRY_SIZE = 39; "PRG", "P16", "INT", "IVR", "BAS", "VAR", "REL", "SYS" };
static int ENTRIES_PER_BLOCK = 13;
static int BLOCK_ENTRY_SIZE = ENTRY_SIZE * ENTRIES_PER_BLOCK; static int ENTRY_SIZE = 39;
} static int ENTRIES_PER_BLOCK = 13;
static int BLOCK_ENTRY_SIZE = ENTRY_SIZE * ENTRIES_PER_BLOCK;
/* http://www.kreativekorp.com/miscpages/a2info/filetypes.shtml }
*
* $00 UNK Unknown /* http://www.kreativekorp.com/miscpages/a2info/filetypes.shtml
* $01 BAD Bad Block File *
* $02 PCD Pascal Code * $00 UNK Unknown
* $03 PTX Pascal Text * $01 BAD Bad Block File
* $04 TXT ASCII Text * $02 PCD Pascal Code
* $05 PDA Pascal Data * $03 PTX Pascal Text
* $06 BIN Binary File * $04 TXT ASCII Text
* $07 FNT Apple III Font * $05 PDA Pascal Data
* $08 FOT HiRes/Double HiRes File * $06 BIN Binary File
* $09 BA3 Apple III BASIC Program * $07 FNT Apple III Font
* $0A DA3 Apple III BASIC Data * $08 FOT HiRes/Double HiRes File
* $0B WPF Generic Word Processor File * $09 BA3 Apple III BASIC Program
* $0C SOS SOS System File * $0A DA3 Apple III BASIC Data
* $0F DIR ProDOS Directory * $0B WPF Generic Word Processor File
* $10 RPD RPS Data * $0C SOS SOS System File
* $11 RPI RPS Index * $0F DIR ProDOS Directory
* $12 AFD AppleFile Discard * $10 RPD RPS Data
* $13 AFM AppleFile Model * $11 RPI RPS Index
* $14 AFR AppleFile Report * $12 AFD AppleFile Discard
* $15 SCL Screen Library * $13 AFM AppleFile Model
* $16 PFS PFS Document * $14 AFR AppleFile Report
* $19 ADB AppleWorks Database * $15 SCL Screen Library
* $1A AWP AppleWorks Word Processor * $16 PFS PFS Document
* $1B ASP AppleWorks Spreadsheet * $19 ADB AppleWorks Database
* $20 TDM Desktop Manager File * $1A AWP AppleWorks Word Processor
* $21 IPS Instant Pascal Source * $1B ASP AppleWorks Spreadsheet
* $22 UPV UCSD Pascal Volume * $20 TDM Desktop Manager File
* $29 3SD SOS Directory * $21 IPS Instant Pascal Source
* $2A 8SC Source Code * $22 UPV UCSD Pascal Volume
* $2B 8OB Object Code * $29 3SD SOS Directory
* $2C 8IC Interpretted Code * $2A 8SC Source Code
* $2D 8LD Language Data * $2B 8OB Object Code
* $2E P8C ProDOS 8 Code Module * $2C 8IC Interpretted Code
* $41 OCR Optical Character Recognition File * $2D 8LD Language Data
* $50 GWP Apple IIgs Word Processor File * $2E P8C ProDOS 8 Code Module
* $51 GSS Apple IIgs Spreadsheet File * $41 OCR Optical Character Recognition File
* $52 GDB Apple IIgs Database File * $50 GWP Apple IIgs Word Processor File
* $53 DRW Object Oriented Graphics File * $51 GSS Apple IIgs Spreadsheet File
* $54 GDP Apple IIgs Desktop Publishing File * $52 GDB Apple IIgs Database File
* $55 HMD HyperMedia * $53 DRW Object Oriented Graphics File
* $56 EDU Educational Program Data * $54 GDP Apple IIgs Desktop Publishing File
* $57 STN Stationery * $55 HMD HyperMedia
* $58 HLP Help File * $56 EDU Educational Program Data
* $59 COM Communications File * $57 STN Stationery
* $5A CFG Configuration File * $58 HLP Help File
* $5B ANM Animation File * $59 COM Communications File
* $5C MUM Multimedia File * $5A CFG Configuration File
* $5D ENT Entertainment Program File * $5B ANM Animation File
* $5E DVU Development Utility File * $5C MUM Multimedia File
* $60 PRE PC Pre-Boot * $5D ENT Entertainment Program File
* $6B BIO PC BIOS * $5E DVU Development Utility File
* $6D DVR PC Driver * $60 PRE PC Pre-Boot
* $6E PRE PC Pre-Boot * $6B BIO PC BIOS
* $6F HDV PC Hard Disk Image * $6D DVR PC Driver
* $77 KES KES Software * $6E PRE PC Pre-Boot
* $7B TLB KES Software * $6F HDV PC Hard Disk Image
* $7F JCP KES Software * $77 KES KES Software
* $80 GeOS System File * $7B TLB KES Software
* $81 GeOS Desk Accessory * $7F JCP KES Software
* $82 GeOS Application * $80 GeOS System File
* $83 GeOS Document * $81 GeOS Desk Accessory
* $84 GeOS Font * $82 GeOS Application
* $85 GeOS Printer Driver * $83 GeOS Document
* $86 GeOS Input Driver * $84 GeOS Font
* $87 GeOS Auxilary Driver * $85 GeOS Printer Driver
* $8B GeOS Clock Driver * $86 GeOS Input Driver
* $8C GeOS Interface Card Driver * $87 GeOS Auxilary Driver
* $8D GeOS Formatting Data * $8B GeOS Clock Driver
* $A0 WP WordPerfect File * $8C GeOS Interface Card Driver
* $A6 * $8D GeOS Formatting Data
* $AB GSB Apple IIgs BASIC Program * $A0 WP WordPerfect File
* $AC TDF Apple IIgs BASIC TDF * $A6
* $AD BDF Apple IIgs BASIC Data * $AB GSB Apple IIgs BASIC Program
* $B0 SRC Apple IIgs Source Code * $AC TDF Apple IIgs BASIC TDF
* $B1 OBJ Apple IIgs Object Code * $AD BDF Apple IIgs BASIC Data
* $B2 LIB Apple IIgs Library * $B0 SRC Apple IIgs Source Code
* $B3 S16 Apple IIgs Application Program * $B1 OBJ Apple IIgs Object Code
* $B4 RTL Apple IIgs Runtime Library * $B2 LIB Apple IIgs Library
* $B5 EXE Apple IIgs Shell * $B3 S16 Apple IIgs Application Program
* $B6 PIF Apple IIgs Permanent INIT * $B4 RTL Apple IIgs Runtime Library
* $B7 TIF Apple IIgs Temporary INIT * $B5 EXE Apple IIgs Shell
* $B8 NDA Apple IIgs New Desk Accessory * $B6 PIF Apple IIgs Permanent INIT
* $B9 CDA Apple IIgs Classic Desk Accessory * $B7 TIF Apple IIgs Temporary INIT
* $BA TOL Apple IIgs Tool * $B8 NDA Apple IIgs New Desk Accessory
* $BB DRV Apple IIgs Device Driver * $B9 CDA Apple IIgs Classic Desk Accessory
* $BC LDF Apple IIgs Generic Load File * $BA TOL Apple IIgs Tool
* $BD FST Apple IIgs File System Translator * $BB DRV Apple IIgs Device Driver
* $BF DOC Apple IIgs Document * $BC LDF Apple IIgs Generic Load File
* $C0 PNT Apple IIgs Packed Super HiRes * $BD FST Apple IIgs File System Translator
* $C1 PIC Apple IIgs Super HiRes * $BF DOC Apple IIgs Document
* $C2 ANI PaintWorks Animation * $C0 PNT Apple IIgs Packed Super HiRes
* $C3 PAL PaintWorks Palette * $C1 PIC Apple IIgs Super HiRes
* $C5 OOG Object-Oriented Graphics * $C2 ANI PaintWorks Animation
* $C6 SCR Script * $C3 PAL PaintWorks Palette
* $C7 CDV Apple IIgs Control Panel * $C5 OOG Object-Oriented Graphics
* $C8 FON Apple IIgs Font * $C6 SCR Script
* $C9 FND Apple IIgs Finder Data * $C7 CDV Apple IIgs Control Panel
* $CA ICN Apple IIgs Icon File * $C8 FON Apple IIgs Font
* $D5 MUS Music File * $C9 FND Apple IIgs Finder Data
* $D6 INS Instrument File * $CA ICN Apple IIgs Icon File
* $D7 MDI MIDI File * $D5 MUS Music File
* $D8 SND Apple IIgs Sound File * $D6 INS Instrument File
* $DB DBM DB Master Document * $D7 MDI MIDI File
* $E0 LBR Archive File * $D8 SND Apple IIgs Sound File
* $E2 ATK AppleTalk Data * $DB DBM DB Master Document
* $EE R16 EDASM 816 Relocatable Code * $E0 LBR Archive File
* $EF PAR Pascal Area * $E2 ATK AppleTalk Data
* $F0 CMD ProDOS Command File * $EE R16 EDASM 816 Relocatable Code
* $F1 OVL User Defined 1 * $EF PAR Pascal Area
* $F2 User Defined 2 * $F0 CMD ProDOS Command File
* $F3 User Defined 3 * $F1 OVL User Defined 1
* $F4 User Defined 4 * $F2 User Defined 2
* $F5 BAT User Defined 5 * $F3 User Defined 3
* $F6 User Defined 6 * $F4 User Defined 4
* $F7 User Defined 7 * $F5 BAT User Defined 5
* $F8 PRG User Defined 8 * $F6 User Defined 6
* $F9 P16 Apple IIgs System File * $F7 User Defined 7
* $FA INT Integer BASIC Program * $F8 PRG User Defined 8
* $FB IVR Integer BASIC Variables * $F9 P16 Apple IIgs System File
* $FC BAS Applesoft BASIC Program * $FA INT Integer BASIC Program
* $FD VAR Applesoft BASIC Variables * $FB IVR Integer BASIC Variables
* $FE REL EDASM Relocatable Code * $FC BAS Applesoft BASIC Program
* $FF SYS ProDOS System File * $FD VAR Applesoft BASIC Variables
*/ * $FE REL EDASM Relocatable Code
* $FF SYS ProDOS System File
// See also http://www.kreativekorp.com/miscpages/a2info/filetypes.shtml */
/* // See also http://www.kreativekorp.com/miscpages/a2info/filetypes.shtml
* https://groups.google.com/forum/#!topic/comp.sys.apple2/waoYCIbkJKs
* /*
* There are a number of disk utilities available that store images of disks that * https://groups.google.com/forum/#!topic/comp.sys.apple2/waoYCIbkJKs
* utilize file systems that are not ProDOS, at the end of a ProDOS volume. *
* There's DOS Master, by Glen Bredon, that stores images of DOS 3.3 disks at the * There are a number of disk utilities available that store images of disks that
* end of a ProDOS volume. Similarly, Pro/Part, by Steven Hirsch, stores images * utilize file systems that are not ProDOS, at the end of a ProDOS volume.
* of CP/M volumes. Also, there's Pascal Partition Manager (PPM) that stores * There's DOS Master, by Glen Bredon, that stores images of DOS 3.3 disks at the
* images of UCSD Pascal volumes. I've decided to refer to the area used to store * end of a ProDOS volume. Similarly, Pro/Part, by Steven Hirsch, stores images
* volume images, by all three of these systems, as a Foreign Volume Area or FVA. * of CP/M volumes. Also, there's Pascal Partition Manager (PPM) that stores
* All three of these systems modify the Block Allocation Map of a ProDOS volume * images of UCSD Pascal volumes. I've decided to refer to the area used to store
* to keep ProDOS from assigning blocks used by FVAs for use by files being * volume images, by all three of these systems, as a Foreign Volume Area or FVA.
* written by ProDOS. Pascal Partition Manager is different from the other two * All three of these systems modify the Block Allocation Map of a ProDOS volume
* in that it has a file type ($EF) and file kind (4) assigned to it by Apple. * to keep ProDOS from assigning blocks used by FVAs for use by files being
* A directory listing of a ProODS volume containing an FVA managed by PPM will * written by ProDOS. Pascal Partition Manager is different from the other two
* show a file name of "PASCAL.AREA". A directory listing of a ProDOS volume * in that it has a file type ($EF) and file kind (4) assigned to it by Apple.
* containing an FVA managed by DOS Master or Pro/Part will show absolutely nothing. * A directory listing of a ProODS volume containing an FVA managed by PPM will
* Running a popular utility named "MR.FIXIT", also by Glen Bredon, against a * show a file name of "PASCAL.AREA". A directory listing of a ProDOS volume
* ProDOS volume containing an FVA will report an error. Specifically, "MR.FIXIT" * containing an FVA managed by DOS Master or Pro/Part will show absolutely nothing.
* will complain that all the blocks used by an FVA as allocated but not in use. * Running a popular utility named "MR.FIXIT", also by Glen Bredon, against a
* To solve this problem for Pro/Part I wrote a Foreign Volume Area utility * ProDOS volume containing an FVA will report an error. Specifically, "MR.FIXIT"
* program that generates a directory entry for the Pro/Part area. That entry has * will complain that all the blocks used by an FVA as allocated but not in use.
* file kind 4, file type $EF, file name "PROPART.AREA" and an auxiliary file * To solve this problem for Pro/Part I wrote a Foreign Volume Area utility
* type $4853 (Steven Hirsch's initials). Today I realized that it's likely that * program that generates a directory entry for the Pro/Part area. That entry has
* the same thing could be done for DOS Master. Study of the source code for * file kind 4, file type $EF, file name "PROPART.AREA" and an auxiliary file
* DOS Master will reveal it that's true. If it is, I propose that "DOS33.AREA" * type $4853 (Steven Hirsch's initials). Today I realized that it's likely that
* be used as the file name and $4247 as the auxiliary type (Glen Bredon's initials). * the same thing could be done for DOS Master. Study of the source code for
* As I compose the text of this message I realize that another solution is to * DOS Master will reveal it that's true. If it is, I propose that "DOS33.AREA"
* modify "MR.FIXIT" to be aware of FVAs. But doing that would not allow someone * be used as the file name and $4247 as the auxiliary type (Glen Bredon's initials).
* doing a directory listing of a ProDOS volume containing an FVA to be aware * As I compose the text of this message I realize that another solution is to
* that the FVA exists. * modify "MR.FIXIT" to be aware of FVAs. But doing that would not allow someone
* doing a directory listing of a ProDOS volume containing an FVA to be aware
* that the FVA exists.
*/ */

View File

@ -1,145 +1,145 @@
package com.bytezone.diskbrowser.prodos; package com.bytezone.diskbrowser.prodos;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import com.bytezone.diskbrowser.applefile.AbstractFile; import com.bytezone.diskbrowser.applefile.AbstractFile;
import com.bytezone.diskbrowser.disk.FormattedDisk; import com.bytezone.diskbrowser.disk.FormattedDisk;
import com.bytezone.diskbrowser.utilities.HexFormatter; import com.bytezone.diskbrowser.utilities.HexFormatter;
// -----------------------------------------------------------------------------------// // -----------------------------------------------------------------------------------//
class ProdosDirectory extends AbstractFile implements ProdosConstants class ProdosDirectory extends AbstractFile implements ProdosConstants
// -----------------------------------------------------------------------------------// // -----------------------------------------------------------------------------------//
{ {
private static final String NO_DATE = "<NO DATE>"; private static final String NO_DATE = "<NO DATE>";
private static final String newLine = String.format ("%n"); private static final String newLine = String.format ("%n");
private static final String newLine2 = newLine + newLine; private static final String newLine2 = newLine + newLine;
private static final SimpleDateFormat sdf = new SimpleDateFormat ("d-MMM-yy"); private static final SimpleDateFormat sdf = new SimpleDateFormat ("d-MMM-yy");
private static final SimpleDateFormat stf = new SimpleDateFormat ("H:mm"); private static final SimpleDateFormat stf = new SimpleDateFormat ("H:mm");
private final ProdosDisk parentFD; private final ProdosDisk parentFD;
private final int totalBlocks; private final int totalBlocks;
private final int freeBlocks; private final int freeBlocks;
private final int usedBlocks; private final int usedBlocks;
// ---------------------------------------------------------------------------------// // ---------------------------------------------------------------------------------//
public ProdosDirectory (FormattedDisk parent, String name, byte[] buffer, ProdosDirectory (FormattedDisk parent, String name, byte[] buffer, int totalBlocks,
int totalBlocks, int freeBlocks, int usedBlocks) int freeBlocks, int usedBlocks)
// ---------------------------------------------------------------------------------// // ---------------------------------------------------------------------------------//
{ {
super (name, buffer); super (name, buffer);
this.parentFD = (ProdosDisk) parent; this.parentFD = (ProdosDisk) parent;
this.totalBlocks = totalBlocks; this.totalBlocks = totalBlocks;
this.freeBlocks = freeBlocks; this.freeBlocks = freeBlocks;
this.usedBlocks = usedBlocks; this.usedBlocks = usedBlocks;
} }
// ---------------------------------------------------------------------------------// // ---------------------------------------------------------------------------------//
@Override @Override
public String getText () public String getText ()
// ---------------------------------------------------------------------------------// // ---------------------------------------------------------------------------------//
{ {
StringBuffer text = new StringBuffer (); StringBuffer text = new StringBuffer ();
text.append ("Disk : " + parentFD.getDisplayPath () + newLine2); text.append ("Disk : " + parentFD.getDisplayPath () + newLine2);
for (int i = 0; i < buffer.length; i += 39) for (int i = 0; i < buffer.length; i += 39)
{ {
int storageType = (buffer[i] & 0xF0) >> 4; int storageType = (buffer[i] & 0xF0) >> 4;
if (storageType == 0) if (storageType == 0)
continue; // break?? continue; // break??
int nameLength = buffer[i] & 0x0F; int nameLength = buffer[i] & 0x0F;
String filename = HexFormatter.getString (buffer, i + 1, nameLength); String filename = HexFormatter.getString (buffer, i + 1, nameLength);
String subType = ""; String subType = "";
String locked; String locked;
switch (storageType) switch (storageType)
{ {
case ProdosConstants.VOLUME_HEADER: case ProdosConstants.VOLUME_HEADER:
case ProdosConstants.SUBDIRECTORY_HEADER: case ProdosConstants.SUBDIRECTORY_HEADER:
text.append ("/" + filename + newLine2); text.append ("/" + filename + newLine2);
text.append (" NAME TYPE BLOCKS " text.append (" NAME TYPE BLOCKS "
+ "MODIFIED CREATED ENDFILE SUBTYPE" + newLine2); + "MODIFIED CREATED ENDFILE SUBTYPE" + newLine2);
break; break;
case ProdosConstants.FREE: case ProdosConstants.FREE:
case ProdosConstants.SEEDLING: case ProdosConstants.SEEDLING:
case ProdosConstants.SAPLING: case ProdosConstants.SAPLING:
case ProdosConstants.TREE: case ProdosConstants.TREE:
case ProdosConstants.PASCAL_ON_PROFILE: case ProdosConstants.PASCAL_ON_PROFILE:
case ProdosConstants.GSOS_EXTENDED_FILE: case ProdosConstants.GSOS_EXTENDED_FILE:
case ProdosConstants.SUBDIRECTORY: case ProdosConstants.SUBDIRECTORY:
int type = buffer[i + 16] & 0xFF; int type = buffer[i + 16] & 0xFF;
int blocks = HexFormatter.intValue (buffer[i + 19], buffer[i + 20]); int blocks = HexFormatter.intValue (buffer[i + 19], buffer[i + 20]);
GregorianCalendar created = HexFormatter.getAppleDate (buffer, i + 24); GregorianCalendar created = HexFormatter.getAppleDate (buffer, i + 24);
String dateC = created == null ? NO_DATE String dateC = created == null ? NO_DATE
: sdf.format (created.getTime ()).toUpperCase ().replace (".", ""); : sdf.format (created.getTime ()).toUpperCase ().replace (".", "");
String timeC = created == null ? "" : stf.format (created.getTime ()); String timeC = created == null ? "" : stf.format (created.getTime ());
GregorianCalendar modified = HexFormatter.getAppleDate (buffer, i + 33); GregorianCalendar modified = HexFormatter.getAppleDate (buffer, i + 33);
String dateM = modified == null ? NO_DATE String dateM = modified == null ? NO_DATE
: sdf.format (modified.getTime ()).toUpperCase ().replace (".", ""); : sdf.format (modified.getTime ()).toUpperCase ().replace (".", "");
String timeM = modified == null ? "" : stf.format (modified.getTime ()); String timeM = modified == null ? "" : stf.format (modified.getTime ());
int eof = int eof =
HexFormatter.intValue (buffer[i + 21], buffer[i + 22], buffer[i + 23]); HexFormatter.intValue (buffer[i + 21], buffer[i + 22], buffer[i + 23]);
int fileType = buffer[i + 16] & 0xFF; int fileType = buffer[i + 16] & 0xFF;
locked = (buffer[i + 30] & 0xE0) == 0xE0 ? " " : "*"; locked = (buffer[i + 30] & 0xE0) == 0xE0 ? " " : "*";
switch (fileType) switch (fileType)
{ {
case FILE_TYPE_TEXT: case FILE_TYPE_TEXT:
int aux = HexFormatter.intValue (buffer[i + 31], buffer[i + 32]); int aux = HexFormatter.intValue (buffer[i + 31], buffer[i + 32]);
subType = String.format ("R=%5d", aux); subType = String.format ("R=%5d", aux);
break; break;
case FILE_TYPE_BINARY: case FILE_TYPE_BINARY:
case FILE_TYPE_PNT: case FILE_TYPE_PNT:
case FILE_TYPE_PIC: case FILE_TYPE_PIC:
case FILE_TYPE_FOT: case FILE_TYPE_FOT:
aux = HexFormatter.intValue (buffer[i + 31], buffer[i + 32]); aux = HexFormatter.intValue (buffer[i + 31], buffer[i + 32]);
subType = String.format ("A=$%4X", aux); subType = String.format ("A=$%4X", aux);
break; break;
case FILE_TYPE_AWP: case FILE_TYPE_AWP:
aux = HexFormatter.intValue (buffer[i + 32], buffer[i + 31]); // backwards! aux = HexFormatter.intValue (buffer[i + 32], buffer[i + 31]); // backwards!
if (aux != 0) if (aux != 0)
filename = convert (filename, aux); filename = convert (filename, aux);
break; break;
default: default:
subType = ""; subType = "";
} }
text.append (String.format ("%s%-15s %3s %5d %9s %5s %9s %5s %8d %7s%n", text.append (String.format ("%s%-15s %3s %5d %9s %5s %9s %5s %8d %7s%n",
locked, filename, ProdosConstants.fileTypes[type], blocks, dateM, timeM, locked, filename, ProdosConstants.fileTypes[type], blocks, dateM, timeM,
dateC, timeC, eof, subType)); dateC, timeC, eof, subType));
break; break;
default: default:
text.append (" <Unknown strage type : " + storageType + newLine); text.append (" <Unknown strage type : " + storageType + newLine);
} }
} }
text.append ( text.append (
String.format ("%nBLOCKS FREE:%5d BLOCKS USED:%5d TOTAL BLOCKS:%5d%n", String.format ("%nBLOCKS FREE:%5d BLOCKS USED:%5d TOTAL BLOCKS:%5d%n",
freeBlocks, usedBlocks, totalBlocks)); freeBlocks, usedBlocks, totalBlocks));
return text.toString (); return text.toString ();
} }
// ---------------------------------------------------------------------------------// // ---------------------------------------------------------------------------------//
private String convert (String name, int flags) private String convert (String name, int flags)
// ---------------------------------------------------------------------------------// // ---------------------------------------------------------------------------------//
{ {
char[] newName = name.toCharArray (); char[] newName = name.toCharArray ();
for (int i = 0, weight = 0x8000; i < newName.length; i++, weight >>>= 1) for (int i = 0, weight = 0x8000; i < newName.length; i++, weight >>>= 1)
{ {
if ((flags & weight) != 0) if ((flags & weight) != 0)
{ {
if (newName[i] == '.') if (newName[i] == '.')
newName[i] = ' '; newName[i] = ' ';
else if (newName[i] >= 'A' && newName[i] <= 'Z') else if (newName[i] >= 'A' && newName[i] <= 'Z')
newName[i] += 32; newName[i] += 32;
} }
} }
return new String (newName); return new String (newName);
} }
} }

View File

@ -1,59 +1,67 @@
package com.bytezone.diskbrowser.prodos; package com.bytezone.diskbrowser.prodos;
import com.bytezone.diskbrowser.disk.AbstractSector; import com.bytezone.diskbrowser.disk.AbstractSector;
import com.bytezone.diskbrowser.disk.Disk; import com.bytezone.diskbrowser.disk.Disk;
import com.bytezone.diskbrowser.disk.DiskAddress; import com.bytezone.diskbrowser.disk.DiskAddress;
// see Prodos 8 Tech note #25 // see Prodos 8 Tech note #25
class ProdosExtendedKeySector extends AbstractSector // -----------------------------------------------------------------------------------//
{ class ProdosExtendedKeySector extends AbstractSector
public ProdosExtendedKeySector (Disk disk, byte[] buffer, DiskAddress diskAddress) // -----------------------------------------------------------------------------------//
{ {
super (disk, buffer, diskAddress); // ---------------------------------------------------------------------------------//
} ProdosExtendedKeySector (Disk disk, byte[] buffer, DiskAddress diskAddress)
// ---------------------------------------------------------------------------------//
@Override {
public String createText () super (disk, buffer, diskAddress);
{ }
StringBuilder text = getHeader ("Prodos Extended Key Block");
// ---------------------------------------------------------------------------------//
for (int i = 0; i < 512; i += 256) @Override
{ public String createText ()
String type = i == 0 ? "Data" : "Resource"; // ---------------------------------------------------------------------------------//
addText (text, buffer, i, 1, {
type + " fork storage type (" + getType (buffer[i]) + ")"); StringBuilder text = getHeader ("Prodos Extended Key Block");
addTextAndDecimal (text, buffer, i + 1, 2, "Key block");
addTextAndDecimal (text, buffer, i + 3, 2, "Blocks used"); for (int i = 0; i < 512; i += 256)
addTextAndDecimal (text, buffer, i + 5, 3, "EOF"); {
text.append ("\n"); String type = i == 0 ? "Data" : "Resource";
addText (text, buffer, i, 1,
// check for Finder Info records type + " fork storage type (" + getType (buffer[i]) + ")");
if (i == 0 && buffer[8] != 0) addTextAndDecimal (text, buffer, i + 1, 2, "Key block");
{ addTextAndDecimal (text, buffer, i + 3, 2, "Blocks used");
for (int j = 0; j <= 18; j += 18) addTextAndDecimal (text, buffer, i + 5, 3, "EOF");
{ text.append ("\n");
addTextAndDecimal (text, buffer, j + 8, 1, "Size");
addTextAndDecimal (text, buffer, j + 9, 1, "Type"); // check for Finder Info records
addTextAndDecimal (text, buffer, j + 10, 16, "Finder info"); if (i == 0 && buffer[8] != 0)
} {
} for (int j = 0; j <= 18; j += 18)
} {
addTextAndDecimal (text, buffer, j + 8, 1, "Size");
return text.toString (); addTextAndDecimal (text, buffer, j + 9, 1, "Type");
} addTextAndDecimal (text, buffer, j + 10, 16, "Finder info");
}
private String getType (byte flag) }
{ }
switch ((flag & 0x0F))
{ return text.toString ();
case ProdosConstants.SEEDLING: }
return "Seedling";
case ProdosConstants.SAPLING: // ---------------------------------------------------------------------------------//
return "Sapling"; private String getType (byte flag)
case ProdosConstants.TREE: // ---------------------------------------------------------------------------------//
return "Tree"; {
default: switch ((flag & 0x0F))
return "???"; {
} case ProdosConstants.SEEDLING:
} return "Seedling";
case ProdosConstants.SAPLING:
return "Sapling";
case ProdosConstants.TREE:
return "Tree";
default:
return "???";
}
}
} }

View File

@ -1,42 +1,48 @@
package com.bytezone.diskbrowser.prodos; package com.bytezone.diskbrowser.prodos;
import com.bytezone.diskbrowser.disk.AbstractSector; import com.bytezone.diskbrowser.disk.AbstractSector;
import com.bytezone.diskbrowser.disk.Disk; import com.bytezone.diskbrowser.disk.Disk;
import com.bytezone.diskbrowser.disk.DiskAddress; import com.bytezone.diskbrowser.disk.DiskAddress;
import com.bytezone.diskbrowser.utilities.HexFormatter; import com.bytezone.diskbrowser.utilities.HexFormatter;
class ProdosIndexSector extends AbstractSector // -----------------------------------------------------------------------------------//
{ class ProdosIndexSector extends AbstractSector
private final String name; // -----------------------------------------------------------------------------------//
{
ProdosIndexSector (String name, Disk disk, byte[] buffer, DiskAddress diskAddress) private final String name;
{
super (disk, buffer, diskAddress); // ---------------------------------------------------------------------------------//
this.name = name; ProdosIndexSector (String name, Disk disk, byte[] buffer, DiskAddress diskAddress)
} // ---------------------------------------------------------------------------------//
{
@Override super (disk, buffer, diskAddress);
public String createText () this.name = name;
{ }
StringBuilder text = getHeader ("Prodos Index Block : " + name);
// ---------------------------------------------------------------------------------//
for (int i = 0; i < 256; i++) @Override
{ public String createText ()
text.append ( // ---------------------------------------------------------------------------------//
String.format ("%02X %02X %02X", i, buffer[i], buffer[i + 256])); {
if (buffer[i] != 0 || buffer[i + 256] != 0) StringBuilder text = getHeader ("Prodos Index Block : " + name);
{
int blockNo = HexFormatter.intValue (buffer[i], buffer[i + 256]); for (int i = 0; i < 256; i++)
String valid = disk.isValidAddress (blockNo) ? "" : " *** invalid ***"; {
text.append (String.format (" %s%s%n", "block " + blockNo, valid)); text.append (
} String.format ("%02X %02X %02X", i, buffer[i], buffer[i + 256]));
else if (buffer[i] != 0 || buffer[i + 256] != 0)
text.append ("\n"); {
} int blockNo = HexFormatter.intValue (buffer[i], buffer[i + 256]);
String valid = disk.isValidAddress (blockNo) ? "" : " *** invalid ***";
if (text.length () > 0) text.append (String.format (" %s%s%n", "block " + blockNo, valid));
text.deleteCharAt (text.length () - 1); }
else
return text.toString (); text.append ("\n");
} }
if (text.length () > 0)
text.deleteCharAt (text.length () - 1);
return text.toString ();
}
} }

View File

@ -1,48 +1,58 @@
package com.bytezone.diskbrowser.prodos; package com.bytezone.diskbrowser.prodos;
import java.util.List; import java.util.List;
import com.bytezone.diskbrowser.disk.DiskAddress; import com.bytezone.diskbrowser.disk.DiskAddress;
import com.bytezone.diskbrowser.gui.DataSource; import com.bytezone.diskbrowser.gui.DataSource;
import com.bytezone.diskbrowser.utilities.HexFormatter; import com.bytezone.diskbrowser.utilities.HexFormatter;
class SubDirectoryHeader extends DirectoryHeader // -----------------------------------------------------------------------------------//
{ class SubDirectoryHeader extends DirectoryHeader
private final int parentPointer; // -----------------------------------------------------------------------------------//
private final int parentSequence; {
private final int parentSize; private final int parentPointer;
private final int parentSequence;
public SubDirectoryHeader (ProdosDisk parentDisk, byte[] entryBuffer, FileEntry parent) private final int parentSize;
{
super (parentDisk, entryBuffer); // ---------------------------------------------------------------------------------//
this.parentDirectory = parent.parentDirectory; SubDirectoryHeader (ProdosDisk parentDisk, byte[] entryBuffer, FileEntry parent)
// ---------------------------------------------------------------------------------//
parentPointer = HexFormatter.intValue (entryBuffer[35], entryBuffer[36]); {
parentSequence = entryBuffer[37] & 0xFF; super (parentDisk, entryBuffer);
parentSize = entryBuffer[38] & 0xFF; this.parentDirectory = parent.parentDirectory;
if (false) parentPointer = HexFormatter.intValue (entryBuffer[35], entryBuffer[36]);
System.out.printf ("", parentPointer, parentSequence, parentSize); parentSequence = entryBuffer[37] & 0xFF;
} parentSize = entryBuffer[38] & 0xFF;
@Override if (false)
public DataSource getDataSource () System.out.printf ("", parentPointer, parentSequence, parentSize);
{ }
// should this return a directory listing?
return null; // ---------------------------------------------------------------------------------//
} @Override
public DataSource getDataSource ()
@Override // ---------------------------------------------------------------------------------//
public List<DiskAddress> getSectors () {
{ // should this return a directory listing?
return null; return null;
} }
@Override // ---------------------------------------------------------------------------------//
public String toString () @Override
{ public List<DiskAddress> getSectors ()
String locked = (access == 0x01) ? "*" : " "; // ---------------------------------------------------------------------------------//
return String.format (" %s%-40s %15s", locked, "/" + name, {
parentDisk.df.format (created.getTime ())); return null;
} }
// ---------------------------------------------------------------------------------//
@Override
public String toString ()
// ---------------------------------------------------------------------------------//
{
String locked = (access == 0x01) ? "*" : " ";
return String.format (" %s%-40s %15s", locked, "/" + name,
parentDisk.df.format (created.getTime ()));
}
} }

View File

@ -1,133 +1,143 @@
package com.bytezone.diskbrowser.prodos; package com.bytezone.diskbrowser.prodos;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.bytezone.diskbrowser.disk.DiskAddress; import com.bytezone.diskbrowser.disk.DiskAddress;
import com.bytezone.diskbrowser.gui.DataSource; import com.bytezone.diskbrowser.gui.DataSource;
import com.bytezone.diskbrowser.utilities.HexFormatter; import com.bytezone.diskbrowser.utilities.HexFormatter;
/* /*
* There is only one of these - it is always the first entry in the first block. * There is only one of these - it is always the first entry in the first block.
* Every other entry will be either a SubDirectoryHeader or a FileEntry. * Every other entry will be either a SubDirectoryHeader or a FileEntry.
*/ */
class VolumeDirectoryHeader extends DirectoryHeader // -----------------------------------------------------------------------------------//
{ class VolumeDirectoryHeader extends DirectoryHeader
protected final int bitMapBlock; // -----------------------------------------------------------------------------------//
protected int totalBlocks; {
protected int freeBlocks; protected final int bitMapBlock;
protected int usedBlocks; protected int totalBlocks;
protected int totalBitMapBlocks; protected int freeBlocks;
protected int usedBlocks;
public VolumeDirectoryHeader (ProdosDisk parentDisk, byte[] entryBuffer) protected int totalBitMapBlocks;
{
super (parentDisk, entryBuffer); // ---------------------------------------------------------------------------------//
VolumeDirectoryHeader (ProdosDisk parentDisk, byte[] entryBuffer)
bitMapBlock = HexFormatter.unsignedShort (entryBuffer, 35); // ---------------------------------------------------------------------------------//
totalBlocks = HexFormatter.unsignedShort (entryBuffer, 37); {
super (parentDisk, entryBuffer);
// if (totalBlocks == 0xFFFF || totalBlocks == 0x7FFF)
// totalBlocks = (int) disk.getFile ().length () / 4096 * 8;// ignore extra bytes bitMapBlock = HexFormatter.unsignedShort (entryBuffer, 35);
totalBlocks = HexFormatter.unsignedShort (entryBuffer, 37);
totalBitMapBlocks = (totalBlocks - 1) / 512 + 1;
// if (totalBlocks == 0xFFFF || totalBlocks == 0x7FFF)
int block = 2; // totalBlocks = (int) disk.getFile ().length () / 4096 * 8;// ignore extra bytes
do
{ totalBitMapBlocks = (totalBlocks - 1) / 512 + 1;
dataBlocks.add (disk.getDiskAddress (block));
byte[] buffer = disk.readSector (block); int block = 2;
block = HexFormatter.unsignedShort (buffer, 2); do
} while (block > 0); {
dataBlocks.add (disk.getDiskAddress (block));
// convert the Free Sector Table byte[] buffer = disk.readSector (block);
// int bitMapBytes = totalBlocks / 8; // one bit per block block = HexFormatter.unsignedShort (buffer, 2);
int bitMapBytes = (totalBlocks - 1) / 8 + 1; // one bit per block } while (block > 0);
byte[] buffer = new byte[bitMapBytes];
int bitMapBlocks = (bitMapBytes - 1) / disk.getSectorsPerTrack () + 1; // convert the Free Sector Table
int lastBitMapBlock = bitMapBlock + bitMapBlocks - 1; // int bitMapBytes = totalBlocks / 8; // one bit per block
int ptr = 0; int bitMapBytes = (totalBlocks - 1) / 8 + 1; // one bit per block
byte[] buffer = new byte[bitMapBytes];
for (block = bitMapBlock; block <= lastBitMapBlock; block++) int bitMapBlocks = (bitMapBytes - 1) / disk.getSectorsPerTrack () + 1;
{ int lastBitMapBlock = bitMapBlock + bitMapBlocks - 1;
byte[] temp = disk.readSector (block); int ptr = 0;
int bytesToCopy = buffer.length - ptr;
if (bytesToCopy > temp.length) for (block = bitMapBlock; block <= lastBitMapBlock; block++)
bytesToCopy = temp.length; {
System.arraycopy (temp, 0, buffer, ptr, bytesToCopy); byte[] temp = disk.readSector (block);
ptr += bytesToCopy; int bytesToCopy = buffer.length - ptr;
} if (bytesToCopy > temp.length)
bytesToCopy = temp.length;
block = 0; System.arraycopy (temp, 0, buffer, ptr, bytesToCopy);
ptr += bytesToCopy;
// nb1 dual-dos disk needs to use totalBlocks obtained from disk }
// int max1 = (totalBlocks - 1) / 8 + 1; // bytes required for sector map
// nb2 hard disk may be truncated, so use actual number of blocks block = 0;
// int max2 = (disk.getTotalBlocks () - 1) / 8 + 1; // bytes required for sector map
// nb1 dual-dos disk needs to use totalBlocks obtained from disk
int max = (Math.min (totalBlocks, disk.getTotalBlocks ()) - 1) / 8 + 1; // int max1 = (totalBlocks - 1) / 8 + 1; // bytes required for sector map
// System.out.printf ("total blocks %,d%n", totalBlocks); // nb2 hard disk may be truncated, so use actual number of blocks
// System.out.printf ("disk blocks %,d%n", disk.getTotalBlocks ()); // int max2 = (disk.getTotalBlocks () - 1) / 8 + 1; // bytes required for sector map
for (int i = 0; i < max; i++) int max = (Math.min (totalBlocks, disk.getTotalBlocks ()) - 1) / 8 + 1;
{ // System.out.printf ("total blocks %,d%n", totalBlocks);
byte b = buffer[i]; // System.out.printf ("disk blocks %,d%n", disk.getTotalBlocks ());
for (int j = 0; j < 8; j++)
{ for (int i = 0; i < max; i++)
if ((b & 0x80) == 0x80) {
{ byte b = buffer[i];
freeBlocks++; for (int j = 0; j < 8; j++)
parentDisk.setSectorFree (block++, true); {
} if ((b & 0x80) == 0x80)
else {
{ freeBlocks++;
usedBlocks++; parentDisk.setSectorFree (block++, true);
parentDisk.setSectorFree (block++, false); }
} else
b <<= 1; {
} usedBlocks++;
} parentDisk.setSectorFree (block++, false);
} }
b <<= 1;
@Override }
public DataSource getDataSource () }
{ }
List<byte[]> blockList = new ArrayList<> ();
int block = 2; // ---------------------------------------------------------------------------------//
do @Override
{ public DataSource getDataSource ()
byte[] buf = disk.readSector (block); // ---------------------------------------------------------------------------------//
blockList.add (buf); {
block = HexFormatter.intValue (buf[2], buf[3]); // next block List<byte[]> blockList = new ArrayList<> ();
} while (block > 0); int block = 2;
do
byte[] fullBuffer = new byte[blockList.size () * 507]; {
int offset = 0; byte[] buf = disk.readSector (block);
for (byte[] bfr : blockList) blockList.add (buf);
{ block = HexFormatter.intValue (buf[2], buf[3]); // next block
System.arraycopy (bfr, 4, fullBuffer, offset, 507); } while (block > 0);
offset += 507;
} byte[] fullBuffer = new byte[blockList.size () * 507];
return new ProdosDirectory (parentDisk, name, fullBuffer, totalBlocks, freeBlocks, int offset = 0;
usedBlocks); for (byte[] bfr : blockList)
} {
System.arraycopy (bfr, 4, fullBuffer, offset, 507);
@Override offset += 507;
public List<DiskAddress> getSectors () }
{ return new ProdosDirectory (parentDisk, name, fullBuffer, totalBlocks, freeBlocks,
List<DiskAddress> sectors = new ArrayList<> (); usedBlocks);
sectors.addAll (dataBlocks); }
return sectors;
} // ---------------------------------------------------------------------------------//
@Override
@Override public List<DiskAddress> getSectors ()
public String toString () // ---------------------------------------------------------------------------------//
{ {
if (false) List<DiskAddress> sectors = new ArrayList<> ();
{ sectors.addAll (dataBlocks);
String locked = (access == 0x01) ? "*" : " "; return sectors;
String timeC = created == null ? "" : parentDisk.df.format (created.getTime ()); }
return String.format (" %s%-42s %15s", locked, "/" + name, timeC);
} // ---------------------------------------------------------------------------------//
return name; @Override
} public String toString ()
// ---------------------------------------------------------------------------------//
{
if (false)
{
String locked = (access == 0x01) ? "*" : " ";
String timeC = created == null ? "" : parentDisk.df.format (created.getTime ());
return String.format (" %s%-42s %15s", locked, "/" + name, timeC);
}
return name;
}
} }