2020-02-08 08:28:22 +00:00
|
|
|
|
package com.bytezone.diskbrowser.dos;
|
|
|
|
|
|
|
|
|
|
import java.awt.Color;
|
|
|
|
|
import java.nio.file.Path;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------------//
|
|
|
|
|
public class DosDisk extends AbstractFormattedDisk
|
|
|
|
|
// -----------------------------------------------------------------------------------//
|
|
|
|
|
{
|
|
|
|
|
private static final int ENTRY_SIZE = 35;
|
|
|
|
|
private static final int CATALOG_TRACK = 17;
|
|
|
|
|
private static final int VTOC_SECTOR = 0;
|
|
|
|
|
|
|
|
|
|
final DosVTOCSector dosVTOCSector;
|
|
|
|
|
private final Color green = new Color (0, 200, 0);
|
|
|
|
|
private final DefaultMutableTreeNode volumeNode;
|
|
|
|
|
|
|
|
|
|
private int freeSectors;
|
|
|
|
|
private int usedSectors;
|
|
|
|
|
private final int volumeNo; // for multi-volume disks
|
|
|
|
|
|
|
|
|
|
public final SectorType vtocSector = new SectorType ("VTOC", Color.magenta);
|
|
|
|
|
public final SectorType catalogSector = new SectorType ("Catalog", green);
|
|
|
|
|
public final SectorType tsListSector = new SectorType ("TSList", Color.blue);
|
|
|
|
|
public final SectorType dataSector = new SectorType ("Data", Color.red);
|
|
|
|
|
public final SectorType dosSector = new SectorType ("DOS", Color.lightGray);
|
|
|
|
|
|
|
|
|
|
protected List<AppleFileSource> deletedFileEntries = new ArrayList<> ();
|
|
|
|
|
|
2020-04-12 12:09:14 +00:00
|
|
|
|
private static boolean debug = false;
|
2020-04-09 23:57:47 +00:00
|
|
|
|
|
2020-02-08 08:28:22 +00:00
|
|
|
|
enum FileType
|
|
|
|
|
{
|
2021-03-28 06:44:36 +00:00
|
|
|
|
Text, ApplesoftBasic, IntegerBasic, Binary, SS, Relocatable, AA, BB
|
2020-02-08 08:28:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
public DosDisk (Disk disk)
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
{
|
|
|
|
|
this (disk, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
public DosDisk (Disk disk, int volumeNo)
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
{
|
|
|
|
|
super (disk);
|
|
|
|
|
|
|
|
|
|
this.volumeNo = volumeNo;
|
|
|
|
|
|
|
|
|
|
sectorTypesList.add (dosSector);
|
|
|
|
|
sectorTypesList.add (vtocSector);
|
|
|
|
|
sectorTypesList.add (catalogSector);
|
|
|
|
|
sectorTypesList.add (tsListSector);
|
|
|
|
|
sectorTypesList.add (dataSector);
|
|
|
|
|
|
|
|
|
|
DiskAddress da = disk.getDiskAddress (0, 0);
|
2020-04-10 23:47:52 +00:00
|
|
|
|
byte[] sectorBuffer = disk.readBlock (da); // Boot sector
|
2020-02-08 08:28:22 +00:00
|
|
|
|
bootSector = new BootSector (disk, sectorBuffer, "DOS", da);
|
|
|
|
|
|
|
|
|
|
da = disk.getDiskAddress (CATALOG_TRACK, VTOC_SECTOR);
|
2020-04-10 23:47:52 +00:00
|
|
|
|
sectorBuffer = disk.readBlock (da); // VTOC
|
2020-12-12 09:53:45 +00:00
|
|
|
|
((AppleDisk) disk).setDosVersion (sectorBuffer[3] & 0xFF);
|
2020-02-08 08:28:22 +00:00
|
|
|
|
dosVTOCSector = new DosVTOCSector (this, disk, sectorBuffer, da);
|
2020-04-10 23:47:52 +00:00
|
|
|
|
sectorTypes[da.getBlockNo ()] = vtocSector;
|
2020-02-08 08:28:22 +00:00
|
|
|
|
|
|
|
|
|
DiskAddress catalogStart = disk.getDiskAddress (sectorBuffer[1], sectorBuffer[2]);
|
|
|
|
|
|
|
|
|
|
if (dosVTOCSector.sectorSize != disk.getBlockSize ())
|
2020-04-10 10:10:53 +00:00
|
|
|
|
System.out.printf ("%s - invalid sector size : %d%n", disk.getFile ().getName (),
|
|
|
|
|
dosVTOCSector.sectorSize);
|
2020-04-10 23:47:52 +00:00
|
|
|
|
if (dosVTOCSector.maxSectors != disk.getBlocksPerTrack ())
|
2020-04-10 10:10:53 +00:00
|
|
|
|
System.out.printf ("%s - invalid sectors per track : %d%n",
|
|
|
|
|
disk.getFile ().getName (), dosVTOCSector.maxSectors);
|
2020-02-08 08:28:22 +00:00
|
|
|
|
|
|
|
|
|
// sectorTypes[CATALOG_TRACK * dosVTOCSector.maxSectors] = vtocSector;
|
|
|
|
|
|
|
|
|
|
// assert (maxTracks == disk.getTotalTracks ());
|
|
|
|
|
// assert (dosVTOCSector.maxSectors == disk.getSectorsPerTrack ());
|
|
|
|
|
// assert (sectorSize == disk.getBlockSize ()); HFSAssembler.dsk fails this
|
|
|
|
|
// assert (catalogStart.getTrack () == CATALOG_TRACK);
|
|
|
|
|
|
|
|
|
|
// arcboot.dsk starts the catalog at 17/13
|
|
|
|
|
// assert (catalogStart.getSector () == 15);
|
|
|
|
|
|
|
|
|
|
// build list of CatalogEntry objects
|
|
|
|
|
DefaultMutableTreeNode rootNode = getCatalogTreeRoot ();
|
|
|
|
|
volumeNode = new DefaultMutableTreeNode ();
|
|
|
|
|
DefaultMutableTreeNode deletedFilesNode = new DefaultMutableTreeNode ();
|
|
|
|
|
rootNode.add (volumeNode);
|
|
|
|
|
|
|
|
|
|
// flag the catalog sectors before any file mistakenly grabs them
|
2020-04-10 23:47:52 +00:00
|
|
|
|
da = disk.getDiskAddress (catalogStart.getBlockNo ());
|
2020-02-08 08:28:22 +00:00
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
if (!disk.isValidAddress (da))
|
|
|
|
|
break;
|
2020-04-10 23:47:52 +00:00
|
|
|
|
sectorBuffer = disk.readBlock (da);
|
2020-02-08 08:28:22 +00:00
|
|
|
|
if (!disk.isValidAddress (sectorBuffer[1], sectorBuffer[2]))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// The first byte is officially unused, but it always seems to contain 0x00 or 0xFF
|
|
|
|
|
// See beautifulboot.dsk.
|
|
|
|
|
// if (sectorBuffer[0] != 0 && (sectorBuffer[0] & 0xFF) != 0xFF && false)
|
|
|
|
|
// {
|
|
|
|
|
// System.out
|
|
|
|
|
// .println ("Dos catalog sector buffer byte #0 invalid : " + sectorBuffer[0]);
|
|
|
|
|
// break;
|
|
|
|
|
// }
|
|
|
|
|
|
2020-04-10 23:47:52 +00:00
|
|
|
|
sectorTypes[da.getBlockNo ()] = catalogSector;
|
2020-02-08 08:28:22 +00:00
|
|
|
|
|
|
|
|
|
int track = sectorBuffer[1] & 0xFF;
|
|
|
|
|
int sector = sectorBuffer[2] & 0xFF;
|
|
|
|
|
if (!disk.isValidAddress (track, sector))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
da = disk.getDiskAddress (track, sector);
|
|
|
|
|
|
2020-12-12 09:53:45 +00:00
|
|
|
|
} while (!da.isZero () && da.isValidAddress ());
|
2020-02-08 08:28:22 +00:00
|
|
|
|
|
|
|
|
|
// same loop, but now all the catalog sectors are properly flagged
|
2020-04-10 23:47:52 +00:00
|
|
|
|
da = disk.getDiskAddress (catalogStart.getBlockNo ());
|
2020-07-15 12:09:25 +00:00
|
|
|
|
loop: do
|
2020-02-08 08:28:22 +00:00
|
|
|
|
{
|
|
|
|
|
if (!disk.isValidAddress (da))
|
|
|
|
|
break;
|
2020-04-10 23:47:52 +00:00
|
|
|
|
sectorBuffer = disk.readBlock (da);
|
2020-02-08 08:28:22 +00:00
|
|
|
|
if (!disk.isValidAddress (sectorBuffer[1], sectorBuffer[2]))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
for (int ptr = 11; ptr < 256; ptr += ENTRY_SIZE)
|
|
|
|
|
{
|
|
|
|
|
if (sectorBuffer[ptr] == 0) // empty slot, no more catalog entries
|
2020-07-15 12:09:25 +00:00
|
|
|
|
break loop;
|
2020-02-08 08:28:22 +00:00
|
|
|
|
|
2020-05-10 11:28:01 +00:00
|
|
|
|
byte[] entryBuffer = new byte[ENTRY_SIZE];
|
|
|
|
|
System.arraycopy (sectorBuffer, ptr, entryBuffer, 0, ENTRY_SIZE);
|
|
|
|
|
int track = entryBuffer[0] & 0xFF;
|
|
|
|
|
boolean deletedFlag = (entryBuffer[0] & 0x80) != 0;
|
2020-02-08 08:28:22 +00:00
|
|
|
|
|
|
|
|
|
if (deletedFlag) // deleted file
|
|
|
|
|
{
|
|
|
|
|
DeletedCatalogEntry deletedCatalogEntry =
|
2020-05-10 11:28:01 +00:00
|
|
|
|
new DeletedCatalogEntry (this, da, entryBuffer, dosVTOCSector.dosVersion);
|
2020-02-08 08:28:22 +00:00
|
|
|
|
deletedFileEntries.add (deletedCatalogEntry);
|
|
|
|
|
DefaultMutableTreeNode node = new DefaultMutableTreeNode (deletedCatalogEntry);
|
|
|
|
|
node.setAllowsChildren (false);
|
|
|
|
|
deletedFilesNode.add (node);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-05-10 11:28:01 +00:00
|
|
|
|
CatalogEntry catalogEntry = new CatalogEntry (this, da, entryBuffer);
|
2020-02-08 08:28:22 +00:00
|
|
|
|
fileEntries.add (catalogEntry);
|
|
|
|
|
DefaultMutableTreeNode node = new DefaultMutableTreeNode (catalogEntry);
|
|
|
|
|
node.setAllowsChildren (false);
|
|
|
|
|
volumeNode.add (node);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int track = sectorBuffer[1] & 0xFF;
|
|
|
|
|
int sector = sectorBuffer[2] & 0xFF;
|
|
|
|
|
if (dosVTOCSector.dosVersion >= 0x41)
|
|
|
|
|
{
|
|
|
|
|
track = track & 0x3F;
|
|
|
|
|
sector = sector & 0x1F;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!disk.isValidAddress (track, sector))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
da = disk.getDiskAddress (sectorBuffer[1], sectorBuffer[2]);
|
|
|
|
|
|
2020-05-10 11:28:01 +00:00
|
|
|
|
} while (!da.isZero ());
|
2020-02-08 08:28:22 +00:00
|
|
|
|
|
|
|
|
|
// link double hi-res files
|
|
|
|
|
for (AppleFileSource fe : fileEntries)
|
|
|
|
|
{
|
|
|
|
|
String name = fe.getUniqueName ();
|
|
|
|
|
if (name.endsWith (".AUX"))
|
|
|
|
|
{
|
|
|
|
|
String partner1 = name.substring (0, name.length () - 4);
|
|
|
|
|
String partner2 = partner1 + ".BIN";
|
|
|
|
|
for (AppleFileSource fe2 : fileEntries)
|
|
|
|
|
if (fe2.getUniqueName ().equals (partner1)
|
|
|
|
|
|| fe2.getUniqueName ().equals (partner2))
|
|
|
|
|
{
|
|
|
|
|
((CatalogEntry) fe2).link ((CatalogEntry) fe);
|
|
|
|
|
((CatalogEntry) fe).link ((CatalogEntry) fe2);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// add up all the free and used sectors, and label DOS sectors while we're here
|
|
|
|
|
int lastDosSector = dosVTOCSector.maxSectors * 3; // first three tracks
|
|
|
|
|
for (DiskAddress da2 : disk)
|
|
|
|
|
{
|
2020-04-10 23:47:52 +00:00
|
|
|
|
int blockNo = da2.getBlockNo ();
|
2020-02-08 08:28:22 +00:00
|
|
|
|
if (blockNo < lastDosSector) // in the DOS region
|
|
|
|
|
{
|
|
|
|
|
if (freeBlocks.get (blockNo)) // according to the VTOC
|
|
|
|
|
++freeSectors;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
++usedSectors;
|
|
|
|
|
if (sectorTypes[blockNo] == usedSector)
|
|
|
|
|
sectorTypes[blockNo] = dosSector;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (stillAvailable (da2)) // free or used, ie not specifically labelled
|
|
|
|
|
++freeSectors;
|
|
|
|
|
else
|
|
|
|
|
++usedSectors;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (freeBlocks.get (blockNo) && !stillAvailable (da2))
|
|
|
|
|
falsePositives++;
|
|
|
|
|
if (!freeBlocks.get (blockNo) && stillAvailable (da2))
|
|
|
|
|
falseNegatives++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (deletedFilesNode.getDepth () > 0)
|
|
|
|
|
{
|
|
|
|
|
rootNode.add (deletedFilesNode);
|
|
|
|
|
deletedFilesNode.setUserObject (getDeletedList ());
|
|
|
|
|
makeNodeVisible (deletedFilesNode.getFirstLeaf ());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
volumeNode.setUserObject (getCatalog ());
|
|
|
|
|
makeNodeVisible (volumeNode.getFirstLeaf ());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
@Override
|
|
|
|
|
public void setOriginalPath (Path path)
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
{
|
|
|
|
|
super.setOriginalPath (path);
|
|
|
|
|
|
|
|
|
|
// this has already been set in the constructor
|
|
|
|
|
volumeNode.setUserObject (getCatalog ());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Beagle Bros FRAMEUP disk only has one catalog block
|
|
|
|
|
// ARCBOOT.DSK has a catalog which starts at sector 0C
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
public static boolean isCorrectFormat (AppleDisk disk)
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
{
|
2020-04-10 10:10:53 +00:00
|
|
|
|
if (false) // testing
|
2020-04-10 00:47:42 +00:00
|
|
|
|
{
|
2020-04-10 10:10:53 +00:00
|
|
|
|
disk.setInterleave (1);
|
2020-04-09 23:57:47 +00:00
|
|
|
|
return true;
|
2020-04-10 00:47:42 +00:00
|
|
|
|
}
|
2020-04-09 23:57:47 +00:00
|
|
|
|
|
2020-04-20 07:46:56 +00:00
|
|
|
|
int blocksPerTrack = disk.getBlocksPerTrack ();
|
|
|
|
|
if (blocksPerTrack > 16 && blocksPerTrack != 32) // 32 = unidos
|
|
|
|
|
{
|
|
|
|
|
if (debug)
|
|
|
|
|
System.out.printf ("Blocks per track: %d", blocksPerTrack);
|
2020-04-09 23:57:47 +00:00
|
|
|
|
return false;
|
2020-04-20 07:46:56 +00:00
|
|
|
|
}
|
2020-04-09 23:57:47 +00:00
|
|
|
|
|
2020-04-10 00:47:42 +00:00
|
|
|
|
int[] cb = new int[3];
|
2020-04-10 10:10:53 +00:00
|
|
|
|
int best = 0;
|
|
|
|
|
int il = -1;
|
2020-04-20 07:46:56 +00:00
|
|
|
|
int max = disk.getBlocksPerTrack () == 16 ? 3 : 1; // no interleave for 13 sector?
|
2020-04-10 10:10:53 +00:00
|
|
|
|
|
2020-04-20 07:46:56 +00:00
|
|
|
|
for (int interleave = 0; interleave < max; interleave++)
|
2020-02-08 08:28:22 +00:00
|
|
|
|
{
|
2020-04-10 00:47:42 +00:00
|
|
|
|
if (debug)
|
|
|
|
|
System.out.printf ("Checking interleave %d%n", interleave);
|
2020-04-10 10:10:53 +00:00
|
|
|
|
|
2020-04-10 00:47:42 +00:00
|
|
|
|
disk.setInterleave (interleave);
|
|
|
|
|
cb[interleave] = checkFormat (disk);
|
2020-04-10 10:10:53 +00:00
|
|
|
|
if (cb[interleave] >= 15)
|
2020-04-10 00:47:42 +00:00
|
|
|
|
return true;
|
2020-02-08 08:28:22 +00:00
|
|
|
|
|
2020-04-10 10:10:53 +00:00
|
|
|
|
if (cb[interleave] > best)
|
2020-04-10 00:47:42 +00:00
|
|
|
|
{
|
2020-04-10 10:10:53 +00:00
|
|
|
|
best = cb[interleave];
|
|
|
|
|
il = interleave;
|
2020-04-10 00:47:42 +00:00
|
|
|
|
}
|
2020-04-10 10:10:53 +00:00
|
|
|
|
}
|
2020-02-08 08:28:22 +00:00
|
|
|
|
|
2020-05-15 01:14:01 +00:00
|
|
|
|
if (best <= 1)
|
2020-04-10 10:10:53 +00:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
disk.setInterleave (il);
|
|
|
|
|
return true;
|
2020-02-08 08:28:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
public String getVersionText ()
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
{
|
|
|
|
|
switch (getVersion ())
|
|
|
|
|
{
|
|
|
|
|
case 0x01:
|
|
|
|
|
return "3.1";
|
|
|
|
|
case 0x02:
|
|
|
|
|
return "3.2";
|
|
|
|
|
case 0x03:
|
|
|
|
|
return "3.3";
|
|
|
|
|
case 0x41:
|
|
|
|
|
return "4.1";
|
|
|
|
|
case 0x42:
|
|
|
|
|
return "4.2";
|
|
|
|
|
case 0x43:
|
|
|
|
|
return "4.3";
|
2024-05-06 22:28:33 +00:00
|
|
|
|
case 0x45:
|
|
|
|
|
return "4.5";
|
2020-02-08 08:28:22 +00:00
|
|
|
|
default:
|
|
|
|
|
return "??";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
public int getVersion ()
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
{
|
|
|
|
|
return dosVTOCSector.dosVersion;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
private static int checkFormat (AppleDisk disk)
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
{
|
2020-04-10 23:47:52 +00:00
|
|
|
|
byte[] buffer = disk.readBlock (0x11, 0x00);
|
2020-02-08 08:28:22 +00:00
|
|
|
|
|
|
|
|
|
// DISCCOMMANDER.DSK uses track 0x17 for the catalog
|
|
|
|
|
// if (buffer[1] != 0x11) // first catalog track
|
|
|
|
|
// return 0;
|
|
|
|
|
|
2020-05-15 01:14:01 +00:00
|
|
|
|
// Apple Assembly Language.dsk claims 0x2A tracks per disk
|
|
|
|
|
// if (buffer[52] != 35 && buffer[52] != 50)
|
|
|
|
|
// {
|
|
|
|
|
// if (debug)
|
|
|
|
|
// System.out.printf ("Bad tracks per disk : %02X%n", buffer[52]);
|
|
|
|
|
// return 0;
|
|
|
|
|
// }
|
|
|
|
|
|
2020-04-09 23:57:47 +00:00
|
|
|
|
if (debug)
|
|
|
|
|
System.out.printf ("Sectors per track: %02X%n", buffer[53]);
|
|
|
|
|
|
2020-02-08 08:28:22 +00:00
|
|
|
|
if (buffer[53] != 16 && buffer[53] != 13 && buffer[53] != 32) // sectors per track
|
|
|
|
|
{
|
2020-04-10 10:10:53 +00:00
|
|
|
|
if (debug)
|
|
|
|
|
System.out.printf ("Bad sectors per track : %02X%n", buffer[53]);
|
2020-02-08 08:28:22 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if (buffer[49] < -1 || buffer[49] > 1) // direction of next file save
|
|
|
|
|
// {
|
|
|
|
|
// System.out.println ("Bad direction : " + buffer[49]);
|
|
|
|
|
// // Visicalc data disk had 0xF8
|
|
|
|
|
// // return 0;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
int version = buffer[3] & 0xFF;
|
2020-04-09 23:57:47 +00:00
|
|
|
|
if (debug)
|
|
|
|
|
System.out.printf ("Version: %02X%n", buffer[3]);
|
2024-05-06 22:28:33 +00:00
|
|
|
|
if (version == 0 || (version > 0x45 && version != 0xFF))
|
2020-02-08 08:28:22 +00:00
|
|
|
|
{
|
2020-05-15 01:14:01 +00:00
|
|
|
|
if (debug)
|
|
|
|
|
System.out.printf ("Bad version : %02X%n", version);
|
2020-02-08 08:28:22 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-09 23:57:47 +00:00
|
|
|
|
int catalogBlocks = countCatalogBlocks (disk, buffer);
|
|
|
|
|
if (debug)
|
|
|
|
|
System.out.printf ("Catalog blocks: %s%n", catalogBlocks);
|
|
|
|
|
|
2020-12-18 09:10:18 +00:00
|
|
|
|
if (catalogBlocks > 0)
|
|
|
|
|
disk.setDosVersion (version);
|
2020-04-09 23:57:47 +00:00
|
|
|
|
return catalogBlocks;
|
2020-02-08 08:28:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
private static int countCatalogBlocks (AppleDisk disk, byte[] buffer)
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
{
|
2020-12-18 09:10:18 +00:00
|
|
|
|
if (!disk.isValidAddress (buffer[1], buffer[2]))
|
|
|
|
|
{
|
|
|
|
|
if (debug)
|
|
|
|
|
System.out.printf ("Invalid address1: %02X %02X%n", buffer[1], buffer[2]);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-08 08:28:22 +00:00
|
|
|
|
DiskAddress catalogStart = disk.getDiskAddress (buffer[1], buffer[2]);
|
2020-04-10 23:47:52 +00:00
|
|
|
|
DiskAddress da = disk.getDiskAddress (catalogStart.getBlockNo ());
|
2020-02-08 08:28:22 +00:00
|
|
|
|
List<DiskAddress> catalogAddresses = new ArrayList<> ();
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
2020-04-09 23:57:47 +00:00
|
|
|
|
if (debug)
|
|
|
|
|
System.out.printf ("Checking: %s%n", da);
|
|
|
|
|
|
2020-02-08 08:28:22 +00:00
|
|
|
|
if (!disk.isValidAddress (da))
|
2020-04-09 23:57:47 +00:00
|
|
|
|
{
|
|
|
|
|
if (debug)
|
|
|
|
|
System.out.printf ("Invalid address: %s%n", da);
|
2020-02-08 08:28:22 +00:00
|
|
|
|
return 0;
|
2020-04-09 23:57:47 +00:00
|
|
|
|
}
|
2020-02-08 08:28:22 +00:00
|
|
|
|
|
2020-04-10 10:10:53 +00:00
|
|
|
|
if (isDuplicate (catalogAddresses, da))
|
2020-02-08 08:28:22 +00:00
|
|
|
|
{
|
2020-04-10 10:10:53 +00:00
|
|
|
|
if (debug)
|
|
|
|
|
System.out.println ("Catalog looping");
|
2020-02-08 08:28:22 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-10 23:47:52 +00:00
|
|
|
|
buffer = disk.readBlock (da);
|
2020-02-08 08:28:22 +00:00
|
|
|
|
if (!disk.isValidAddress (buffer[1], buffer[2]))
|
2020-04-09 23:57:47 +00:00
|
|
|
|
{
|
|
|
|
|
if (debug)
|
2020-12-18 09:10:18 +00:00
|
|
|
|
System.out.printf ("Invalid address2: %02X %02X%n", buffer[1], buffer[2]);
|
2020-04-09 23:57:47 +00:00
|
|
|
|
return catalogAddresses.size ();
|
|
|
|
|
}
|
2020-02-08 08:28:22 +00:00
|
|
|
|
|
|
|
|
|
catalogAddresses.add (da);
|
|
|
|
|
|
|
|
|
|
da = disk.getDiskAddress (buffer[1], buffer[2]);
|
2020-05-10 11:28:01 +00:00
|
|
|
|
} while (!da.isZero ());
|
2020-02-08 08:28:22 +00:00
|
|
|
|
|
2020-04-09 23:57:47 +00:00
|
|
|
|
if (debug)
|
|
|
|
|
System.out.printf ("Catalog blocks: %d%n", catalogAddresses.size ());
|
2020-02-08 08:28:22 +00:00
|
|
|
|
return catalogAddresses.size ();
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-10 10:10:53 +00:00
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
private static boolean isDuplicate (List<DiskAddress> catalogAddresses, DiskAddress da)
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
{
|
|
|
|
|
for (DiskAddress diskAddress : catalogAddresses)
|
2020-04-10 23:47:52 +00:00
|
|
|
|
if (diskAddress.getBlockNo () == da.getBlockNo ())
|
2020-04-10 10:10:53 +00:00
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-08 08:28:22 +00:00
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
@Override
|
|
|
|
|
public String toString ()
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
{
|
2020-04-20 07:46:56 +00:00
|
|
|
|
StringBuilder text = new StringBuilder ();
|
2020-02-08 08:28:22 +00:00
|
|
|
|
|
2020-04-20 07:46:56 +00:00
|
|
|
|
text.append (String.format ("Disk name ............. %s%n", getDisplayPath ()));
|
2020-02-08 08:28:22 +00:00
|
|
|
|
text.append (
|
|
|
|
|
String.format ("DOS version ........... %s%n", dosVTOCSector.dosVersion));
|
|
|
|
|
text.append (
|
|
|
|
|
String.format ("Sectors per track ..... %d%n", dosVTOCSector.maxSectors));
|
2020-04-20 07:46:56 +00:00
|
|
|
|
text.append (String.format ("Volume no ............. %d%n", volumeNo));
|
|
|
|
|
text.append (String.format ("Interleave ............ %d", disk.getInterleave ()));
|
2020-02-08 08:28:22 +00:00
|
|
|
|
|
|
|
|
|
return text.toString ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
@Override
|
|
|
|
|
public DataSource getFormattedSector (DiskAddress da)
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
{
|
2020-04-10 23:47:52 +00:00
|
|
|
|
SectorType type = sectorTypes[da.getBlockNo ()];
|
2020-02-08 08:28:22 +00:00
|
|
|
|
if (type == vtocSector)
|
|
|
|
|
return dosVTOCSector;
|
2020-05-10 11:28:01 +00:00
|
|
|
|
if (da.isZero ())
|
2020-02-08 08:28:22 +00:00
|
|
|
|
return bootSector;
|
|
|
|
|
|
2020-04-10 23:47:52 +00:00
|
|
|
|
byte[] buffer = disk.readBlock (da);
|
|
|
|
|
String address = String.format ("%02X %02X", da.getTrackNo (), da.getSectorNo ());
|
2020-02-08 08:28:22 +00:00
|
|
|
|
|
|
|
|
|
if (type == tsListSector)
|
|
|
|
|
return new DosTSListSector (getSectorFilename (da), disk, buffer, da);
|
|
|
|
|
if (type == catalogSector)
|
|
|
|
|
return new DosCatalogSector (this, disk, buffer, da);
|
|
|
|
|
if (type == dataSector)
|
|
|
|
|
return new DefaultSector (
|
|
|
|
|
"Data Sector at " + address + " : " + getSectorFilename (da), disk, buffer, da);
|
|
|
|
|
if (type == dosSector)
|
|
|
|
|
return new DefaultSector ("DOS sector at " + address, disk, buffer, da);
|
|
|
|
|
return super.getFormattedSector (da);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
@Override
|
|
|
|
|
public List<DiskAddress> getFileSectors (int fileNo)
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
{
|
|
|
|
|
if (fileEntries.size () > 0 && fileEntries.size () > fileNo)
|
|
|
|
|
return fileEntries.get (fileNo).getSectors ();
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-20 21:50:51 +00:00
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
private int countEntries (AppleFileSource catalogEntry)
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
{
|
|
|
|
|
int count = 0;
|
|
|
|
|
for (AppleFileSource ce : fileEntries)
|
|
|
|
|
{
|
|
|
|
|
if (ce.getUniqueName ().equals (catalogEntry.getUniqueName ()))
|
|
|
|
|
count++;
|
|
|
|
|
}
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-08 08:28:22 +00:00
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
@Override
|
|
|
|
|
public AppleFileSource getCatalog ()
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
{
|
|
|
|
|
String newLine = String.format ("%n");
|
2020-12-20 21:50:51 +00:00
|
|
|
|
String underline = "- --- --- ------------------------------ ----- -------------"
|
2020-02-08 08:28:22 +00:00
|
|
|
|
+ " -- ---- -------------------" + newLine;
|
2020-05-22 06:13:45 +00:00
|
|
|
|
|
2020-02-08 08:28:22 +00:00
|
|
|
|
StringBuilder text = new StringBuilder ();
|
2020-05-22 06:13:45 +00:00
|
|
|
|
|
2021-04-18 23:53:37 +00:00
|
|
|
|
text.append (String.format ("File : %s%n%n", getDisplayPath ()));
|
2020-02-08 08:28:22 +00:00
|
|
|
|
text.append ("L Typ Len Name Addr"
|
|
|
|
|
+ " Length TS Data Comment" + newLine);
|
2020-12-20 21:50:51 +00:00
|
|
|
|
text.append (underline);
|
2020-02-08 08:28:22 +00:00
|
|
|
|
|
|
|
|
|
for (AppleFileSource fileEntry : fileEntries)
|
2020-12-20 21:50:51 +00:00
|
|
|
|
{
|
|
|
|
|
String entry = ((CatalogEntry) fileEntry).getDetails ();
|
|
|
|
|
if (countEntries (fileEntry) > 1)
|
|
|
|
|
entry += "** duplicate **";
|
|
|
|
|
text.append (entry + newLine);
|
|
|
|
|
}
|
2020-02-08 08:28:22 +00:00
|
|
|
|
|
2020-12-20 21:50:51 +00:00
|
|
|
|
text.append (underline);
|
2020-02-08 08:28:22 +00:00
|
|
|
|
text.append (String.format (
|
|
|
|
|
" Free sectors: %3d " + "Used sectors: %3d Total sectors: %3d",
|
|
|
|
|
dosVTOCSector.freeSectors, dosVTOCSector.usedSectors,
|
|
|
|
|
(dosVTOCSector.freeSectors + dosVTOCSector.usedSectors)));
|
|
|
|
|
if (dosVTOCSector.freeSectors != freeSectors)
|
|
|
|
|
text.append (String.format (
|
|
|
|
|
"%nActual: Free sectors: %3d "
|
|
|
|
|
+ "Used sectors: %3d Total sectors: %3d",
|
|
|
|
|
freeSectors, usedSectors, (usedSectors + freeSectors)));
|
|
|
|
|
|
|
|
|
|
String volumeText = volumeNo == 0 ? "" : "Side " + volumeNo + " ";
|
2020-05-22 06:13:45 +00:00
|
|
|
|
|
|
|
|
|
return new DefaultAppleFileSource (volumeText + "DOS Volume " + dosVTOCSector.volume,
|
2020-02-08 08:28:22 +00:00
|
|
|
|
text.toString (), this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
private AppleFileSource getDeletedList ()
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
{
|
|
|
|
|
StringBuilder text =
|
|
|
|
|
new StringBuilder ("List of files that were deleted from this disk\n");
|
|
|
|
|
|
|
|
|
|
for (AppleFileSource afs : deletedFileEntries)
|
|
|
|
|
text.append (((DeletedCatalogEntry) afs).getDetails () + "\n");
|
|
|
|
|
|
|
|
|
|
return new DefaultAppleFileSource ("Deleted files", text.toString (), this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* From http://apple2history.org/history/ah15/
|
|
|
|
|
*
|
|
|
|
|
There were actually three versions of DOS 3.3 that Apple released without
|
|
|
|
|
bumping the version number:
|
|
|
|
|
|
|
|
|
|
The first version that was released had FPBASIC and INTBASIC files that were 50
|
|
|
|
|
sectors in size.
|
|
|
|
|
|
|
|
|
|
The second version of DOS 3.3, often referred to as “DOS 3.3e”, appeared at the
|
|
|
|
|
time the Apple IIe was released. In this version, the FPBASIC and INTBASIC files
|
|
|
|
|
were 42 sectors in size. The changes introduced at that time included code to turn
|
|
|
|
|
off the IIe 80-column card at boot time, and an attempt to fix a bug in the APPEND
|
|
|
|
|
command. This fix reportedly introduced an even worse bug, but as the command was
|
|
|
|
|
not heavily used it did not make much of an impact on most programmers. The APPEND
|
|
|
|
|
fix was applied by utilizing some formerly unused space in the DOS 3.3 code.
|
|
|
|
|
|
|
|
|
|
The third version of DOS 3.3 appeared just before the first release of ProDOS.
|
|
|
|
|
The only mention of this in the press was in the DOSTalk column of Softalk magazine.
|
|
|
|
|
This final version of DOS 3.3 included a different fix for the APPEND bug, using
|
|
|
|
|
another bit of unused space in DOS 3.3.
|
|
|
|
|
|
|
|
|
|
With regard to the FPBASIC and INTBASIC files: There were three differences between
|
|
|
|
|
the 50 sector and the 42 sector versions of the INTBASIC file. Firstly, the
|
|
|
|
|
$F800-$FFFF section was removed. This area was the code for the Monitor, and with
|
|
|
|
|
the changes introduced in the Apple IIe, it could cause some things to “break” if
|
|
|
|
|
the older Monitor code was executed. Secondly, a FOR/NEXT bug in Integer BASIC was
|
|
|
|
|
fixed. Finally, there was a three-byte bug in the Programmer’s Aid ROM #1 chip.
|
|
|
|
|
The code for this chip was included in the INTBASIC file, and could therefore be
|
|
|
|
|
patched.
|
|
|
|
|
*/
|
2015-06-01 09:35:51 +00:00
|
|
|
|
}
|