mirror of
https://github.com/dmolony/DiskBrowser.git
synced 2024-09-30 13:58:30 +00:00
407 lines
14 KiB
Java
407 lines
14 KiB
Java
|
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.*;
|
||
|
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 boolean CHECK_SELF_POINTER = true;
|
||
|
|
||
|
private final DosVTOCSector dosVTOCSector;
|
||
|
private final Color green = new Color (0, 200, 0);
|
||
|
private final DefaultMutableTreeNode volumeNode;
|
||
|
|
||
|
private int freeSectors;
|
||
|
private int usedSectors;
|
||
|
|
||
|
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<AppleFileSource> ();
|
||
|
|
||
|
enum FileType
|
||
|
{
|
||
|
Text, ApplesoftBasic, IntegerBasic, Binary, Relocatable, SS, AA, BB
|
||
|
}
|
||
|
|
||
|
public DosDisk (Disk disk)
|
||
|
{
|
||
|
super (disk);
|
||
|
|
||
|
sectorTypesList.add (dosSector);
|
||
|
sectorTypesList.add (vtocSector);
|
||
|
sectorTypesList.add (catalogSector);
|
||
|
sectorTypesList.add (tsListSector);
|
||
|
sectorTypesList.add (dataSector);
|
||
|
|
||
|
byte[] sectorBuffer = disk.readSector (0, 0); // Boot sector
|
||
|
bootSector = new BootSector (disk, sectorBuffer, "DOS");
|
||
|
|
||
|
sectorBuffer = disk.readSector (CATALOG_TRACK, 0); // VTOC
|
||
|
dosVTOCSector = new DosVTOCSector (this, disk, sectorBuffer);
|
||
|
|
||
|
DiskAddress catalogStart = disk.getDiskAddress (sectorBuffer[1], sectorBuffer[2]);
|
||
|
|
||
|
if (dosVTOCSector.sectorSize != disk.getBlockSize ())
|
||
|
System.out.println ("Invalid sector size : " + dosVTOCSector.sectorSize);
|
||
|
if (dosVTOCSector.maxSectors != disk.getSectorsPerTrack ())
|
||
|
System.out.println ("Invalid sectors per track : " + dosVTOCSector.maxSectors);
|
||
|
|
||
|
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
|
||
|
DiskAddress da = disk.getDiskAddress (catalogStart.getBlock ());
|
||
|
do
|
||
|
{
|
||
|
if (!disk.isValidAddress (da))
|
||
|
break;
|
||
|
sectorBuffer = disk.readSector (da);
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
sectorTypes[da.getBlock ()] = catalogSector;
|
||
|
|
||
|
int track = sectorBuffer[1] & 0xFF;
|
||
|
int sector = sectorBuffer[2] & 0xFF;
|
||
|
if (!disk.isValidAddress (track, sector))
|
||
|
break;
|
||
|
|
||
|
// int thisBlock = da.getBlock ();
|
||
|
da = disk.getDiskAddress (track, sector);
|
||
|
|
||
|
// if (CHECK_SELF_POINTER && da.getBlock () == thisBlock)
|
||
|
// break;
|
||
|
|
||
|
} while (da.getBlock () != 0);
|
||
|
|
||
|
// same loop, but now all the catalog sectors are properly flagged
|
||
|
da = disk.getDiskAddress (catalogStart.getBlock ());
|
||
|
loop: do
|
||
|
{
|
||
|
if (!disk.isValidAddress (da))
|
||
|
break;
|
||
|
sectorBuffer = disk.readSector (da);
|
||
|
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
|
||
|
continue;
|
||
|
// break loop;
|
||
|
|
||
|
byte[] entry = new byte[ENTRY_SIZE];
|
||
|
System.arraycopy (sectorBuffer, ptr, entry, 0, ENTRY_SIZE);
|
||
|
|
||
|
if (entry[0] == (byte) 0xFF) // deleted file
|
||
|
{
|
||
|
DeletedCatalogEntry deletedCatalogEntry = new DeletedCatalogEntry (this, da, entry);
|
||
|
deletedFileEntries.add (deletedCatalogEntry);
|
||
|
DefaultMutableTreeNode node = new DefaultMutableTreeNode (deletedCatalogEntry);
|
||
|
node.setAllowsChildren (false);
|
||
|
deletedFilesNode.add (node);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CatalogEntry catalogEntry = new CatalogEntry (this, da, entry);
|
||
|
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 (!disk.isValidAddress (track, sector))
|
||
|
break;
|
||
|
|
||
|
// int thisBlock = da.getBlock ();
|
||
|
da = disk.getDiskAddress (sectorBuffer[1], sectorBuffer[2]);
|
||
|
|
||
|
// if (CHECK_SELF_POINTER && da.getBlock () == thisBlock)
|
||
|
// break;
|
||
|
|
||
|
} while (da.getBlock () != 0);
|
||
|
|
||
|
// 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)
|
||
|
{
|
||
|
int blockNo = da2.getBlock ();
|
||
|
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);
|
||
|
volumeNode.setUserObject (getCatalog ()); // this has already been set in the constructor
|
||
|
}
|
||
|
|
||
|
// 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)
|
||
|
{
|
||
|
disk.setInterleave (0);
|
||
|
int catalogBlocks = checkFormat (disk);
|
||
|
if (catalogBlocks > 3)
|
||
|
return true;
|
||
|
disk.setInterleave (1);
|
||
|
int cb2 = checkFormat (disk);
|
||
|
// if (cb2 > catalogBlocks)
|
||
|
if (cb2 > 3)
|
||
|
return true;
|
||
|
disk.setInterleave (2);
|
||
|
if (false)
|
||
|
{
|
||
|
int cb3 = checkFormat (disk);
|
||
|
if (cb3 > 3)
|
||
|
return true;
|
||
|
}
|
||
|
if (catalogBlocks > 0)
|
||
|
{
|
||
|
disk.setInterleave (0);
|
||
|
return true;
|
||
|
}
|
||
|
if (cb2 > 0)
|
||
|
return true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
private static int checkFormat (AppleDisk disk)
|
||
|
{
|
||
|
byte[] buffer = disk.readSector (0x11, 0x00);
|
||
|
|
||
|
// DISCCOMMANDER.DSK uses track 0x17 for the catalog
|
||
|
// if (buffer[1] != 0x11) // first catalog track
|
||
|
// return 0;
|
||
|
|
||
|
if (buffer[53] != 16 && buffer[53] != 13) // tracks per sector
|
||
|
{
|
||
|
// System.out.println ("VTOC tracks per sector : " + (buffer[53 & 0xFF]));
|
||
|
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];
|
||
|
if (version < -1 || version > 4)
|
||
|
{
|
||
|
System.out.println ("Bad version : " + buffer[3]);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return countCatalogBlocks (disk, buffer);
|
||
|
}
|
||
|
|
||
|
private static int countCatalogBlocks (AppleDisk disk, byte[] buffer)
|
||
|
{
|
||
|
DiskAddress catalogStart = disk.getDiskAddress (buffer[1], buffer[2]);
|
||
|
// int catalogBlocks = 0;
|
||
|
DiskAddress da = disk.getDiskAddress (catalogStart.getBlock ());
|
||
|
List<DiskAddress> catalogAddresses = new ArrayList<DiskAddress> ();
|
||
|
|
||
|
do
|
||
|
{
|
||
|
// System.out.printf ("%5d %s%n", catalogBlocks, da);
|
||
|
if (!disk.isValidAddress (da))
|
||
|
break;
|
||
|
|
||
|
if (catalogAddresses.contains (da))
|
||
|
{
|
||
|
System.out.println ("Catalog looping");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
buffer = disk.readSector (da);
|
||
|
if (!disk.isValidAddress (buffer[1], buffer[2]))
|
||
|
{
|
||
|
System.out.printf ("Invalid address : %02X / %02X%n", buffer[1], buffer[2]);
|
||
|
// System.out.println (HexFormatter.format (buffer));
|
||
|
// System.out.println (da);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
catalogAddresses.add (da);
|
||
|
// catalogBlocks++;
|
||
|
// if (catalogBlocks > 1000) // looping
|
||
|
// {
|
||
|
// System.out.println ("Disk appears to be looping in countCatalogBlocks()");
|
||
|
// return 0;
|
||
|
// }
|
||
|
|
||
|
// int thisBlock = da.getBlock ();
|
||
|
da = disk.getDiskAddress (buffer[1], buffer[2]);
|
||
|
|
||
|
// if (CHECK_SELF_POINTER && da.getBlock () == thisBlock)
|
||
|
// break;
|
||
|
|
||
|
} while (da.getBlock () != 0);
|
||
|
|
||
|
// if (catalogBlocks != catalogAddresses.size ())
|
||
|
// System.out.printf ("CB: %d, size: %d%n", catalogBlocks, catalogAddresses.size ());
|
||
|
return catalogAddresses.size ();
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public String toString ()
|
||
|
{
|
||
|
StringBuffer text = new StringBuffer (dosVTOCSector.toString ());
|
||
|
return text.toString ();
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public DataSource getFormattedSector (DiskAddress da)
|
||
|
{
|
||
|
SectorType type = sectorTypes[da.getBlock ()];
|
||
|
if (type == vtocSector)
|
||
|
return dosVTOCSector;
|
||
|
if (da.getBlock () == 0)
|
||
|
return bootSector;
|
||
|
|
||
|
byte[] buffer = disk.readSector (da);
|
||
|
String address = String.format ("%02X %02X", da.getTrack (), da.getSector ());
|
||
|
|
||
|
if (type == tsListSector)
|
||
|
return new DosTSListSector (getSectorFilename (da), disk, buffer);
|
||
|
if (type == catalogSector)
|
||
|
return new DosCatalogSector (disk, buffer);
|
||
|
if (type == dataSector)
|
||
|
return new DefaultSector ("Data Sector at " + address + " : " + getSectorFilename (da),
|
||
|
disk, buffer);
|
||
|
if (type == dosSector)
|
||
|
return new DefaultSector ("DOS sector at " + address, disk, buffer);
|
||
|
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 (fileEntries.size () > 0 && fileEntries.size () > fileNo)
|
||
|
return fileEntries.get (fileNo).getSectors ();
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public AppleFileSource getCatalog ()
|
||
|
{
|
||
|
String newLine = String.format ("%n");
|
||
|
String line =
|
||
|
"- --- --- ------------------------------ ----- -------------"
|
||
|
+ " -- ---- ----------------" + newLine;
|
||
|
StringBuilder text = new StringBuilder ();
|
||
|
text.append (String.format ("Disk : %s%n%n", getAbsolutePath ()));
|
||
|
text.append ("L Typ Len Name Addr"
|
||
|
+ " Length TS Data Comment" + newLine);
|
||
|
text.append (line);
|
||
|
|
||
|
for (AppleFileSource ce : fileEntries)
|
||
|
text.append (((CatalogEntry) ce).getDetails () + newLine);
|
||
|
|
||
|
text.append (line);
|
||
|
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)));
|
||
|
return new DefaultAppleFileSource ("Volume " + dosVTOCSector.volume, 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);
|
||
|
}
|
||
|
}
|