2020-02-09 13:13:33 +00:00
|
|
|
package com.bytezone.diskbrowser.pascal;
|
|
|
|
|
|
|
|
import java.awt.Color;
|
|
|
|
import java.text.DateFormat;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.GregorianCalendar;
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
import javax.swing.tree.DefaultMutableTreeNode;
|
|
|
|
|
|
|
|
import com.bytezone.diskbrowser.applefile.AppleFileSource;
|
|
|
|
import com.bytezone.diskbrowser.applefile.BootSector;
|
|
|
|
import com.bytezone.diskbrowser.disk.AbstractFormattedDisk;
|
|
|
|
import com.bytezone.diskbrowser.disk.AppleDisk;
|
|
|
|
import com.bytezone.diskbrowser.disk.DefaultAppleFileSource;
|
|
|
|
import com.bytezone.diskbrowser.disk.DefaultSector;
|
|
|
|
import com.bytezone.diskbrowser.disk.Disk;
|
|
|
|
import com.bytezone.diskbrowser.disk.DiskAddress;
|
|
|
|
import com.bytezone.diskbrowser.disk.SectorType;
|
|
|
|
import com.bytezone.diskbrowser.gui.DataSource;
|
|
|
|
import com.bytezone.diskbrowser.utilities.HexFormatter;
|
2020-06-26 03:29:46 +00:00
|
|
|
import com.bytezone.diskbrowser.utilities.Utility;
|
2020-02-09 13:13:33 +00:00
|
|
|
import com.bytezone.diskbrowser.wizardry.Relocator;
|
|
|
|
|
2020-10-28 08:26:29 +00:00
|
|
|
// useful : http://pascal.hansotten.com/ucsd-p-system/apple-pascal/apple-ii/
|
2020-02-09 13:13:33 +00:00
|
|
|
// -----------------------------------------------------------------------------------//
|
|
|
|
public class PascalDisk extends AbstractFormattedDisk
|
|
|
|
// -----------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
static final int CATALOG_ENTRY_SIZE = 26;
|
|
|
|
private final DateFormat df = DateFormat.getDateInstance (DateFormat.SHORT);
|
|
|
|
private final VolumeEntry volumeEntry;
|
|
|
|
private final PascalCatalogSector diskCatalogSector;
|
|
|
|
|
|
|
|
protected Relocator relocator;
|
|
|
|
|
|
|
|
final String[] fileTypes =
|
|
|
|
{ "Volume", "Bad ", "Code", "Text", "Info", "Data", "Graf", "Foto", "SecureDir" };
|
|
|
|
|
|
|
|
SectorType diskBootSector = new SectorType ("Boot", Color.lightGray);
|
|
|
|
SectorType catalogSector = new SectorType ("Catalog", Color.magenta);
|
|
|
|
SectorType dataSector = new SectorType ("Data", new Color (0, 200, 0)); // green
|
|
|
|
SectorType codeSector = new SectorType ("Code", Color.red);
|
|
|
|
SectorType textSector = new SectorType ("Text", Color.blue);
|
|
|
|
SectorType infoSector = new SectorType ("Info", Color.orange);
|
|
|
|
SectorType grafSector = new SectorType ("Graf", Color.cyan);
|
|
|
|
SectorType fotoSector = new SectorType ("Foto", Color.gray);
|
|
|
|
SectorType badSector = new SectorType ("Bad", Color.darkGray);
|
|
|
|
|
|
|
|
SectorType[] sectors = { catalogSector, badSector, codeSector, textSector, infoSector,
|
2021-07-28 20:32:22 +00:00
|
|
|
dataSector, grafSector, fotoSector };
|
2020-02-09 13:13:33 +00:00
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
public PascalDisk (Disk disk)
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
super (disk);
|
|
|
|
|
|
|
|
sectorTypesList.add (diskBootSector);
|
|
|
|
sectorTypesList.add (catalogSector);
|
|
|
|
sectorTypesList.add (dataSector);
|
|
|
|
sectorTypesList.add (codeSector);
|
|
|
|
sectorTypesList.add (textSector);
|
|
|
|
sectorTypesList.add (infoSector);
|
|
|
|
sectorTypesList.add (grafSector);
|
|
|
|
sectorTypesList.add (fotoSector);
|
|
|
|
sectorTypesList.add (badSector);
|
|
|
|
|
|
|
|
List<DiskAddress> blocks = disk.getDiskAddressList (0, 1); // B0, B1
|
2020-04-10 23:47:52 +00:00
|
|
|
this.bootSector = new BootSector (disk, disk.readBlocks (blocks), "Pascal");
|
2020-02-09 13:13:33 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < 2; i++)
|
2020-04-10 23:47:52 +00:00
|
|
|
if (!disk.isBlockEmpty (i))
|
2020-02-09 13:13:33 +00:00
|
|
|
{
|
|
|
|
sectorTypes[i] = diskBootSector;
|
|
|
|
freeBlocks.set (i, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 2; i < disk.getTotalBlocks (); i++)
|
|
|
|
freeBlocks.set (i, true);
|
|
|
|
|
2020-04-10 23:47:52 +00:00
|
|
|
byte[] buffer = disk.readBlock (2);
|
2020-02-09 13:13:33 +00:00
|
|
|
byte[] data = new byte[CATALOG_ENTRY_SIZE];
|
|
|
|
System.arraycopy (buffer, 0, data, 0, CATALOG_ENTRY_SIZE);
|
|
|
|
volumeEntry = new VolumeEntry (this, data);
|
|
|
|
|
|
|
|
DefaultMutableTreeNode root = getCatalogTreeRoot ();
|
|
|
|
DefaultMutableTreeNode volumeNode = new DefaultMutableTreeNode (volumeEntry);
|
|
|
|
root.add (volumeNode);
|
|
|
|
|
|
|
|
List<DiskAddress> sectors = new ArrayList<> ();
|
|
|
|
int max = Math.min (volumeEntry.lastBlock, disk.getTotalBlocks ());
|
|
|
|
for (int i = 2; i < max; i++)
|
|
|
|
{
|
|
|
|
DiskAddress da = disk.getDiskAddress (i);
|
2020-04-10 23:47:52 +00:00
|
|
|
if (!disk.isBlockEmpty (da))
|
2020-02-09 13:13:33 +00:00
|
|
|
sectorTypes[i] = catalogSector;
|
|
|
|
sectors.add (da);
|
|
|
|
freeBlocks.set (i, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
diskCatalogSector =
|
2020-04-10 23:47:52 +00:00
|
|
|
new PascalCatalogSector (disk, disk.readBlocks (sectors), sectors);
|
2020-02-09 13:13:33 +00:00
|
|
|
|
|
|
|
// read the catalog
|
|
|
|
List<DiskAddress> addresses = new ArrayList<> ();
|
|
|
|
for (int i = 2; i < max; i++)
|
|
|
|
addresses.add (disk.getDiskAddress (i));
|
2020-04-10 23:47:52 +00:00
|
|
|
buffer = disk.readBlocks (addresses);
|
2020-02-09 13:13:33 +00:00
|
|
|
|
|
|
|
for (int i = 1; i <= volumeEntry.totalFiles; i++)
|
|
|
|
{
|
|
|
|
int ptr = i * CATALOG_ENTRY_SIZE;
|
|
|
|
data = new byte[CATALOG_ENTRY_SIZE];
|
|
|
|
System.arraycopy (buffer, ptr, data, 0, CATALOG_ENTRY_SIZE);
|
|
|
|
FileEntry fileEntry = new FileEntry (this, data);
|
|
|
|
|
|
|
|
fileEntries.add (fileEntry);
|
|
|
|
DefaultMutableTreeNode node = new DefaultMutableTreeNode (fileEntry);
|
|
|
|
fileEntry.setNode (node);
|
|
|
|
|
|
|
|
if (fileEntry.fileType == 2)
|
|
|
|
{
|
|
|
|
node.setAllowsChildren (true);
|
|
|
|
fileEntry.getDataSource (); // build segments
|
|
|
|
}
|
|
|
|
else
|
|
|
|
node.setAllowsChildren (false);
|
|
|
|
|
|
|
|
volumeNode.add (node);
|
|
|
|
for (int j = fileEntry.firstBlock; j < fileEntry.lastBlock; j++)
|
|
|
|
freeBlocks.set (j, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
volumeNode.setUserObject (getCatalog ());
|
|
|
|
makeNodeVisible (volumeNode.getFirstLeaf ());
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
public static boolean isCorrectFormat (AppleDisk disk, boolean debug)
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
disk.setInterleave (1); // should only ever be Prodos
|
|
|
|
if (checkFormat (disk, debug))
|
|
|
|
return true;
|
|
|
|
disk.setInterleave (0); // see SANE Disk 2.po
|
|
|
|
if (checkFormat (disk, debug))
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
public static boolean checkFormat (AppleDisk disk, boolean debug)
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
2020-04-10 23:47:52 +00:00
|
|
|
byte[] buffer = disk.readBlock (2);
|
2020-02-09 13:13:33 +00:00
|
|
|
int nameLength = buffer[6] & 0xFF;
|
|
|
|
if (nameLength < 1 || nameLength > 7)
|
|
|
|
{
|
|
|
|
if (debug)
|
|
|
|
System.out.println ("bad name length : " + nameLength);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (debug)
|
|
|
|
{
|
|
|
|
String name = HexFormatter.getPascalString (buffer, 6);
|
|
|
|
System.out.println ("Name ok : " + name);
|
|
|
|
}
|
|
|
|
|
2021-05-19 08:13:17 +00:00
|
|
|
int from = Utility.getShort (buffer, 0);
|
|
|
|
int to = Utility.getShort (buffer, 2);
|
2020-02-09 13:13:33 +00:00
|
|
|
if (from != 0 || to != 6)
|
|
|
|
{
|
|
|
|
if (debug)
|
|
|
|
System.out.printf ("from: %d, to: %d%n", from, to);
|
|
|
|
return false; // will only work for floppies!
|
|
|
|
}
|
|
|
|
|
2021-05-19 08:13:17 +00:00
|
|
|
int blocks = Utility.getShort (buffer, 14);
|
2020-12-18 09:10:18 +00:00
|
|
|
if (blocks != 280 && blocks != 1600)
|
2020-02-09 13:13:33 +00:00
|
|
|
{
|
|
|
|
if (debug)
|
|
|
|
System.out.printf ("Blocks > 280: %d%n", blocks);
|
2020-12-18 09:10:18 +00:00
|
|
|
return false;
|
2020-02-09 13:13:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
List<DiskAddress> addresses = new ArrayList<> ();
|
|
|
|
for (int i = 2; i < to; i++)
|
|
|
|
addresses.add (disk.getDiskAddress (i));
|
2020-04-10 23:47:52 +00:00
|
|
|
buffer = disk.readBlocks (addresses);
|
2020-02-09 13:13:33 +00:00
|
|
|
|
2021-05-19 08:13:17 +00:00
|
|
|
int files = Utility.getShort (buffer, 16);
|
2020-02-09 13:13:33 +00:00
|
|
|
if (files < 0 || files > 77)
|
|
|
|
{
|
|
|
|
if (debug)
|
|
|
|
System.out.printf ("Files: %d%n", files);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (debug)
|
|
|
|
System.out.println ("Files found : " + files);
|
|
|
|
|
|
|
|
for (int i = 1; i <= files; i++)
|
|
|
|
{
|
|
|
|
int ptr = i * 26;
|
2021-05-19 08:13:17 +00:00
|
|
|
int firstBlock = Utility.getShort (buffer, ptr);
|
|
|
|
int lastBlock = Utility.getShort (buffer, ptr + 2);
|
|
|
|
int kind = Utility.getShort (buffer, ptr + 4);
|
2020-02-09 13:13:33 +00:00
|
|
|
if (lastBlock < firstBlock)
|
|
|
|
return false;
|
|
|
|
if (kind == 0)
|
|
|
|
return false;
|
|
|
|
nameLength = buffer[ptr + 6] & 0xFF;
|
|
|
|
if (nameLength < 1 || nameLength > 15)
|
|
|
|
return false;
|
2021-05-19 08:13:17 +00:00
|
|
|
int lastByte = Utility.getShort (buffer, ptr + 22);
|
2021-07-28 20:32:22 +00:00
|
|
|
GregorianCalendar date = Utility.getPascalDate (buffer, 24);
|
2020-02-09 13:13:33 +00:00
|
|
|
if (debug)
|
|
|
|
System.out.printf ("%4d %4d %d %-15s %d %s%n", firstBlock, lastBlock, kind,
|
|
|
|
new String (buffer, ptr + 7, nameLength), lastByte, date);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
@Override
|
|
|
|
public DataSource getFormattedSector (DiskAddress da)
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
2020-04-10 23:47:52 +00:00
|
|
|
SectorType st = sectorTypes[da.getBlockNo ()];
|
2020-02-09 13:13:33 +00:00
|
|
|
if (st == diskBootSector)
|
|
|
|
return bootSector;
|
|
|
|
if (st == catalogSector)
|
|
|
|
return diskCatalogSector;
|
|
|
|
String name = getSectorFilename (da);
|
|
|
|
if (name != null)
|
2020-04-10 23:47:52 +00:00
|
|
|
return new DefaultSector (name, disk, disk.readBlock (da), da);
|
2020-02-09 13:13:33 +00:00
|
|
|
return super.getFormattedSector (da);
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
@Override
|
|
|
|
public String getSectorFilename (DiskAddress da)
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
for (AppleFileSource ce : fileEntries)
|
|
|
|
if (((CatalogEntry) ce).contains (da))
|
|
|
|
return ((CatalogEntry) ce).name;
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
@Override
|
|
|
|
public List<DiskAddress> getFileSectors (int fileNo)
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
if (fileNo < 0 || fileNo >= fileEntries.size ())
|
|
|
|
return null;
|
|
|
|
return fileEntries.get (fileNo).getSectors ();
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
public DataSource getFile (int fileNo)
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
if (fileNo < 0 || fileNo >= fileEntries.size ())
|
|
|
|
return null;
|
|
|
|
return fileEntries.get (fileNo).getDataSource ();
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
@Override
|
|
|
|
public AppleFileSource getCatalog ()
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
{
|
|
|
|
String newLine = String.format ("%n");
|
|
|
|
String newLine2 = newLine + newLine;
|
|
|
|
String line = "---- --------------- ---- -------- ------- ---- ---- ----"
|
|
|
|
+ newLine;
|
|
|
|
String date =
|
|
|
|
volumeEntry.date == null ? "--" : df.format (volumeEntry.date.getTime ());
|
|
|
|
StringBuilder text = new StringBuilder ();
|
2021-04-18 23:53:37 +00:00
|
|
|
text.append ("File : " + getDisplayPath () + newLine2);
|
2020-02-09 13:13:33 +00:00
|
|
|
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);
|
|
|
|
}
|
2015-06-01 09:35:51 +00:00
|
|
|
}
|