From 04557bab39aa5c7bdb5a32705a32aeef63b26198 Mon Sep 17 00:00:00 2001 From: Denis Molony Date: Sat, 8 Feb 2020 18:13:28 +1000 Subject: [PATCH] method header lines --- .../diskbrowser/cpm/CPMCatalogSector.java | 12 +- src/com/bytezone/diskbrowser/cpm/CPMDisk.java | 27 +- .../diskbrowser/cpm/DirectoryEntry.java | 40 +- .../disk/AbstractFormattedDisk.java | 1019 ++++++----- .../diskbrowser/disk/AbstractSector.java | 279 +-- .../bytezone/diskbrowser/disk/AppleDisk.java | 1532 ++++++++-------- .../diskbrowser/disk/AppleDiskAddress.java | 194 +- .../bytezone/diskbrowser/disk/DataDisk.java | 114 +- .../disk/DefaultAppleFileSource.java | 224 +-- .../diskbrowser/disk/DefaultDataSource.java | 115 +- .../diskbrowser/disk/DefaultSector.java | 60 +- src/com/bytezone/diskbrowser/disk/Disk.java | 124 +- .../diskbrowser/disk/DiskAddress.java | 28 +- .../diskbrowser/disk/DiskFactory.java | 1585 +++++++++-------- .../diskbrowser/disk/FormattedDisk.java | 170 +- .../bytezone/diskbrowser/disk/SectorList.java | 108 +- .../diskbrowser/disk/SectorListConverter.java | 8 + .../bytezone/diskbrowser/disk/SectorType.java | 46 +- .../diskbrowser/disk/UnknownDisk.java | 6 + 19 files changed, 3041 insertions(+), 2650 deletions(-) diff --git a/src/com/bytezone/diskbrowser/cpm/CPMCatalogSector.java b/src/com/bytezone/diskbrowser/cpm/CPMCatalogSector.java index 20a8170..add3f1f 100644 --- a/src/com/bytezone/diskbrowser/cpm/CPMCatalogSector.java +++ b/src/com/bytezone/diskbrowser/cpm/CPMCatalogSector.java @@ -4,17 +4,23 @@ import com.bytezone.diskbrowser.disk.AbstractSector; import com.bytezone.diskbrowser.disk.Disk; import com.bytezone.diskbrowser.disk.DiskAddress; -public class CPMCatalogSector extends AbstractSector +// -----------------------------------------------------------------------------------// +class CPMCatalogSector extends AbstractSector +// -----------------------------------------------------------------------------------// { private static int CATALOG_ENTRY_SIZE = 32; - public CPMCatalogSector (Disk disk, byte[] buffer, DiskAddress diskAddress) + // ---------------------------------------------------------------------------------// + CPMCatalogSector (Disk disk, byte[] buffer, DiskAddress diskAddress) + // ---------------------------------------------------------------------------------// { super (disk, buffer, diskAddress); } + // ---------------------------------------------------------------------------------// @Override public String createText () + // ---------------------------------------------------------------------------------// { StringBuilder text = getHeader ("Catalog Sector"); @@ -36,7 +42,7 @@ public class CPMCatalogSector extends AbstractSector typeBuffer[2] = buffer[i + 11]; type = new String (typeBuffer).trim (); extra = String.format (" (%s%s)", readOnly ? "read only" : "", - systemFile ? "system file" : ""); + systemFile ? "system file" : ""); } else { diff --git a/src/com/bytezone/diskbrowser/cpm/CPMDisk.java b/src/com/bytezone/diskbrowser/cpm/CPMDisk.java index b3a3b2a..05b21c1 100644 --- a/src/com/bytezone/diskbrowser/cpm/CPMDisk.java +++ b/src/com/bytezone/diskbrowser/cpm/CPMDisk.java @@ -6,10 +6,17 @@ import java.util.List; import javax.swing.tree.DefaultMutableTreeNode; import com.bytezone.diskbrowser.applefile.AppleFileSource; -import com.bytezone.diskbrowser.disk.*; +import com.bytezone.diskbrowser.disk.AbstractFormattedDisk; +import com.bytezone.diskbrowser.disk.AppleDisk; +import com.bytezone.diskbrowser.disk.DefaultAppleFileSource; +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 CPMDisk extends AbstractFormattedDisk +// -----------------------------------------------------------------------------------// { private final Color green = new Color (0, 200, 0); @@ -27,7 +34,9 @@ public class CPMDisk extends AbstractFormattedDisk // // http://www.seasip.info/Cpm/format31.html private final DefaultMutableTreeNode volumeNode; + // ---------------------------------------------------------------------------------// public CPMDisk (Disk disk) + // ---------------------------------------------------------------------------------// { super (disk); @@ -109,7 +118,9 @@ public class CPMDisk extends AbstractFormattedDisk makeNodeVisible (volumeNode.getFirstLeaf ()); } + // ---------------------------------------------------------------------------------// private SectorType getSectorType (String type) + // ---------------------------------------------------------------------------------// { if ("COM".equals (type)) return comSector; @@ -129,15 +140,19 @@ public class CPMDisk extends AbstractFormattedDisk return otherSector; } + // ---------------------------------------------------------------------------------// @Override public List getFileSectors (int fileNo) + // ---------------------------------------------------------------------------------// { if (fileEntries.size () > 0 && fileEntries.size () > fileNo) return fileEntries.get (fileNo).getSectors (); return null; } + // ---------------------------------------------------------------------------------// private DirectoryEntry findParent (DirectoryEntry child) + // ---------------------------------------------------------------------------------// { for (AppleFileSource entry : fileEntries) if (((DirectoryEntry) entry).matches (child)) @@ -146,8 +161,10 @@ public class CPMDisk extends AbstractFormattedDisk return null; } + // ---------------------------------------------------------------------------------// @Override public DataSource getFormattedSector (DiskAddress da) + // ---------------------------------------------------------------------------------// { SectorType type = sectorTypes[da.getBlock ()]; byte[] buffer = disk.readSector (da); @@ -158,8 +175,10 @@ public class CPMDisk extends AbstractFormattedDisk return super.getFormattedSector (da); } + // ---------------------------------------------------------------------------------// @Override public AppleFileSource getCatalog () + // ---------------------------------------------------------------------------------// { String newLine = String.format ("%n"); String line = @@ -182,7 +201,9 @@ public class CPMDisk extends AbstractFormattedDisk return new DefaultAppleFileSource ("CPM Disk ", text.toString (), this); } + // ---------------------------------------------------------------------------------// public static boolean isCorrectFormat (AppleDisk disk) + // ---------------------------------------------------------------------------------// { disk.setInterleave (3); @@ -227,7 +248,9 @@ public class CPMDisk extends AbstractFormattedDisk return true; } + // ---------------------------------------------------------------------------------// private static boolean bufferContainsAll (byte[] buffer, byte value) + // ---------------------------------------------------------------------------------// { for (byte b : buffer) if (b != value) @@ -235,8 +258,10 @@ public class CPMDisk extends AbstractFormattedDisk return true; } + // ---------------------------------------------------------------------------------// @Override public String toString () + // ---------------------------------------------------------------------------------// { StringBuffer text = new StringBuffer ("CPM disk"); return text.toString (); diff --git a/src/com/bytezone/diskbrowser/cpm/DirectoryEntry.java b/src/com/bytezone/diskbrowser/cpm/DirectoryEntry.java index ba422a4..68a56e2 100644 --- a/src/com/bytezone/diskbrowser/cpm/DirectoryEntry.java +++ b/src/com/bytezone/diskbrowser/cpm/DirectoryEntry.java @@ -14,7 +14,9 @@ import com.bytezone.diskbrowser.gui.DataSource; import com.bytezone.diskbrowser.utilities.HexFormatter; // File Control Block (FCB) -public class DirectoryEntry implements AppleFileSource +// -----------------------------------------------------------------------------------// +class DirectoryEntry implements AppleFileSource +// -----------------------------------------------------------------------------------// { private final Disk disk; private final CPMDisk parent; @@ -34,7 +36,9 @@ public class DirectoryEntry implements AppleFileSource private final boolean readOnly; private final boolean systemFile; - public DirectoryEntry (CPMDisk parent, byte[] buffer, int offset) + // ---------------------------------------------------------------------------------// + DirectoryEntry (CPMDisk parent, byte[] buffer, int offset) + // ---------------------------------------------------------------------------------// { this.parent = parent; disk = parent.getDisk (); @@ -75,18 +79,24 @@ public class DirectoryEntry implements AppleFileSource } } - public String getType () + // ---------------------------------------------------------------------------------// + String getType () + // ---------------------------------------------------------------------------------// { return type; } - public boolean matches (DirectoryEntry directoryEntry) + // ---------------------------------------------------------------------------------// + boolean matches (DirectoryEntry directoryEntry) + // ---------------------------------------------------------------------------------// { return userNumber == directoryEntry.userNumber && name.equals (directoryEntry.name) && type.equals (directoryEntry.type); } - public void add (DirectoryEntry entry) + // ---------------------------------------------------------------------------------// + void add (DirectoryEntry entry) + // ---------------------------------------------------------------------------------// { entries.add (entry); @@ -102,8 +112,10 @@ public class DirectoryEntry implements AppleFileSource } } + // ---------------------------------------------------------------------------------// @Override public boolean contains (DiskAddress da) + // ---------------------------------------------------------------------------------// { for (DiskAddress sector : blocks) if (sector.matches (da)) @@ -111,7 +123,9 @@ public class DirectoryEntry implements AppleFileSource return false; } - public String line () + // ---------------------------------------------------------------------------------// + String line () + // ---------------------------------------------------------------------------------// { String bytes = HexFormatter.getHexString (blockList, 0, 16); bytes = bytes.replaceAll ("00", " "); @@ -131,7 +145,9 @@ public class DirectoryEntry implements AppleFileSource return text; } - public String toDetailedString () + // ---------------------------------------------------------------------------------// + String toDetailedString () + // ---------------------------------------------------------------------------------// { StringBuilder text = new StringBuilder (); @@ -156,14 +172,18 @@ public class DirectoryEntry implements AppleFileSource return text.toString (); } + // ---------------------------------------------------------------------------------// @Override public String getUniqueName () + // ---------------------------------------------------------------------------------// { return name + "." + type; } + // ---------------------------------------------------------------------------------// @Override public DataSource getDataSource () + // ---------------------------------------------------------------------------------// { if (appleFile != null) return appleFile; @@ -208,20 +228,26 @@ public class DirectoryEntry implements AppleFileSource return appleFile; } + // ---------------------------------------------------------------------------------// @Override public List getSectors () + // ---------------------------------------------------------------------------------// { return blocks; } + // ---------------------------------------------------------------------------------// @Override public FormattedDisk getFormattedDisk () + // ---------------------------------------------------------------------------------// { return parent; } + // ---------------------------------------------------------------------------------// @Override public String toString () + // ---------------------------------------------------------------------------------// { return name + "." + type; } diff --git a/src/com/bytezone/diskbrowser/disk/AbstractFormattedDisk.java b/src/com/bytezone/diskbrowser/disk/AbstractFormattedDisk.java index 61e7ecf..1dbb850 100755 --- a/src/com/bytezone/diskbrowser/disk/AbstractFormattedDisk.java +++ b/src/com/bytezone/diskbrowser/disk/AbstractFormattedDisk.java @@ -1,468 +1,553 @@ -package com.bytezone.diskbrowser.disk; - -import java.awt.AWTEventMulticaster; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.BitSet; -import java.util.Enumeration; -import java.util.List; - -import javax.swing.JTree; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.DefaultTreeModel; -import javax.swing.tree.TreeNode; -import javax.swing.tree.TreePath; - -import com.bytezone.diskbrowser.applefile.AbstractFile; -import com.bytezone.diskbrowser.applefile.AppleFileSource; -import com.bytezone.diskbrowser.applefile.BootSector; -import com.bytezone.diskbrowser.gui.DataSource; - -public abstract class AbstractFormattedDisk implements FormattedDisk -{ - protected Disk disk; - protected FormattedDisk parent; // used by Dual-dos disks - - protected ActionListener actionListenerList; - protected JTree catalogTree; - protected Path originalPath; - - protected List sectorTypesList = new ArrayList<> (); - protected List fileEntries = new ArrayList<> (); - - public SectorType[] sectorTypes; - - protected BootSector bootSector; - - public final SectorType emptySector = new SectorType ("Unused (empty)", Color.white); - public final SectorType usedSector = new SectorType ("Unused (data)", Color.yellow); - - protected int falsePositives; - protected int falseNegatives; - - protected Dimension gridLayout; - - protected BitSet freeBlocks; - protected BitSet usedBlocks; // still to be populated - currently using stillAvailable () - - public AbstractFormattedDisk (Disk disk) - { - this.disk = disk; - freeBlocks = new BitSet (disk.getTotalBlocks ()); - usedBlocks = new BitSet (disk.getTotalBlocks ()); - /* - * All formatted disks will have empty and/or used sectors, so set them - * here, and let the actual subclass add its sector types later. This list - * is used to hold one of each sector type so that the DiskLayoutPanel can - * draw its grid and key correctly. Every additional type that the instance - * creates should be added here too. - */ - sectorTypesList.add (emptySector); - sectorTypesList.add (usedSector); - /* - * Hopefully every used sector will be changed by the subclass to something - * sensible, but deleted files will always leave the sector as used/unknown - * as it contains data. - */ - setSectorTypes (); - setGridLayout (); - /* - * Create the disk name as the root for the catalog tree. Subclasses will - * have to append their catalog entries to this node. - */ - DefaultAppleFileSource afs = - new DefaultAppleFileSource (getName (), disk.toString (), this); - DefaultMutableTreeNode root = new DefaultMutableTreeNode (afs); - DefaultTreeModel treeModel = new DefaultTreeModel (root); - catalogTree = new JTree (treeModel); - treeModel.setAsksAllowsChildren (true); // allows empty nodes to appear as folders - /* - * Add an ActionListener to the disk in case the interleave or blocksize - * changes - */ - disk.addActionListener (new ActionListener () - { - @Override - public void actionPerformed (ActionEvent e) - { - setSectorTypes (); - } - }); - } - - protected void setEmptyByte (byte value) - { - getDisk ().setEmptyByte (value); - setSectorTypes (); - } - - private void setSectorTypes () - { - sectorTypes = new SectorType[disk.getTotalBlocks ()]; - - for (DiskAddress da : disk) - sectorTypes[da.getBlock ()] = disk.isSectorEmpty (da) ? emptySector : usedSector; - - setGridLayout (); - } - - private void setGridLayout () - { - int totalBlocks = disk.getTotalBlocks (); - - switch (totalBlocks) - { - case 280: - gridLayout = new Dimension (8, 35); - break; - - case 455: - gridLayout = new Dimension (13, 35); - break; - - case 560: - gridLayout = new Dimension (16, 35); - break; - - case 768: - gridLayout = new Dimension (16, 48); - break; - - case 1600: - if (disk.getSectorsPerTrack () == 32) - gridLayout = new Dimension (disk.getSectorsPerTrack (), disk.getTotalTracks ()); - else - gridLayout = new Dimension (16, 100); - break; - - case 2048: - gridLayout = new Dimension (8, 256); - break; - - default: - int[] sizes = { 32, 20, 16, 8 }; - for (int size : sizes) - if ((totalBlocks % size) == 0) - { - gridLayout = new Dimension (size, totalBlocks / size); - break; - } - if (gridLayout == null) - System.out.println ("Unusable total blocks : " + totalBlocks); - } - } - - @Override - public Dimension getGridLayout () - { - return gridLayout; - } - - @Override - public Disk getDisk () - { - return disk; - } - - @Override - public FormattedDisk getParent () - { - return parent; - } - - @Override - public void setParent (FormattedDisk disk) - { - parent = disk; - } - - @Override - public void setOriginalPath (Path path) - { - this.originalPath = path; - - DefaultMutableTreeNode root = getCatalogTreeRoot (); - if (root.getUserObject () == null) - root.setUserObject ( - new DefaultAppleFileSource (getName (), disk.toString (), this)); - } - - @Override - public Path getOriginalPath () - { - return originalPath; - } - - @Override - public String getAbsolutePath () - { - if (originalPath != null) - return originalPath.toString (); - - return disk.getFile ().getAbsolutePath (); - } - - @Override - public String getDisplayPath () - { - if (originalPath != null) - return originalPath.toString (); - - String home = System.getProperty ("user.home"); - String path = disk.getFile ().getAbsolutePath (); - if (path.startsWith (home)) - return "~" + path.substring (home.length ()); - - return disk.getFile ().getAbsolutePath (); - } - - @Override - public boolean isTempDisk () - { - return originalPath != null; - } - - @Override - public String getName () - { - if (originalPath != null) - { - Path path = originalPath.getFileName (); - if (path != null) - return path.toString (); - } - return disk.getFile ().getName (); - } - - @Override - public void writeFile (AbstractFile file) - { - System.out.println ("not implemented yet"); - } - - @Override - public List getCatalogList () - { - return fileEntries; - } - - @Override - public AppleFileSource getFile (String uniqueName) - { - for (AppleFileSource afs : fileEntries) - if (afs.getUniqueName ().equals (uniqueName)) - return afs; - return null; - } - - @Override - public JTree getCatalogTree () - { - return catalogTree; - } - - public DefaultMutableTreeNode getCatalogTreeRoot () - { - return (DefaultMutableTreeNode) catalogTree.getModel ().getRoot (); - } - - public void makeNodeVisible (DefaultMutableTreeNode node) - { - catalogTree.makeVisible ( - new TreePath (((DefaultTreeModel) catalogTree.getModel ()).getPathToRoot (node))); - } - - protected DefaultMutableTreeNode findNode (DefaultMutableTreeNode node, String name) - { - Enumeration children = node.breadthFirstEnumeration (); - if (children != null) - { - while (children.hasMoreElements ()) - { - DefaultMutableTreeNode childNode = - (DefaultMutableTreeNode) children.nextElement (); - if (childNode.getUserObject ().toString ().indexOf (name) > 0) - return childNode; - } - } - return null; - } - - /* - * These routines just hand back the information that was created above, and - * added to by the subclass. - */ - @Override - public SectorType getSectorType (int block) - { - return getSectorType (disk.getDiskAddress (block)); - } - - @Override - public SectorType getSectorType (int track, int sector) - { - return getSectorType (disk.getDiskAddress (track, sector)); - } - - @Override - public SectorType getSectorType (DiskAddress da) - { - return sectorTypes[da.getBlock ()]; - } - - @Override - public List getSectorTypeList () - { - return sectorTypesList; - } - - @Override - public void setSectorType (int block, SectorType type) - { - if (block < sectorTypes.length) - sectorTypes[block] = type; - else - System.out.println ("setSectorType: Invalid block number: " + block); - } - - // Override this so that the correct sector type can be displayed - @Override - public DataSource getFormattedSector (DiskAddress da) - { - if (da.getBlock () == 0 && bootSector != null) - return bootSector; - - SectorType sectorType = sectorTypes[da.getBlock ()]; - byte[] buffer = disk.readSector (da); - String address = String.format ("%02X %02X", da.getTrack (), da.getSector ()); - - if (sectorType == emptySector) - return new DefaultSector ("Empty sector at " + address, disk, buffer, da); - if (sectorType == usedSector) - return new DefaultSector ("Orphan sector at " + address, disk, buffer, da); - - String name = getSectorFilename (da); - if (!name.isEmpty ()) - name = " : " + name; - return new DefaultSector ("Data sector at " + address + name, disk, buffer, da); - } - - /* - * Override this with something useful - */ - @Override - public AppleFileSource getCatalog () - { - return new DefaultAppleFileSource (disk.toString (), this); - } - - @Override - public String getSectorFilename (DiskAddress da) - { - for (AppleFileSource entry : fileEntries) - if (entry.contains (da)) - return (entry).getUniqueName (); - - return ""; - } - - @Override - public int clearOrphans () - { - System.out.println ("Not implemented yet"); - return 0; - } - - @Override - public boolean isSectorFree (DiskAddress da) - { - return freeBlocks.get (da.getBlock ()); - } - - @Override - public boolean isSectorFree (int blockNo) - { - return freeBlocks.get (blockNo); - } - - // representation of the Free Sector Table - @Override - public void setSectorFree (int block, boolean free) - { - if (block < 0 || block >= freeBlocks.size ()) - { - System.out.printf ("Block %d not in range : 0-%d%n", block, freeBlocks.size () - 1); - return; - } - //assert block < freeBlocks.size () : String.format ("Set free block # %6d, size %6d", - // block, freeBlocks.size ()); - freeBlocks.set (block, free); - } - - // Check that the sector hasn't already been flagged as part of the disk structure - @Override - public boolean stillAvailable (DiskAddress da) - { - return stillAvailable (da.getBlock ()); - } - - @Override - public boolean stillAvailable (int blockNo) - { - return sectorTypes[blockNo] == usedSector || sectorTypes[blockNo] == emptySector; - } - - @Override - public void verify () - { - System.out.println ("Sectors to clean :"); - for (int i = 0, max = disk.getTotalBlocks (); i < max; i++) - { - if (freeBlocks.get (i)) - { - if (sectorTypes[i] == usedSector) - System.out.printf ("%04X clean%n", i); - } - else - { - if (sectorTypes[i] == usedSector) - System.out.printf ("%04X *** error ***%n", i); - } - } - } - - // VTOC flags sector as free, but it is in use by a file - @Override - public int falsePositiveBlocks () - { - return falsePositives; - } - - // VTOC flags sector as in use, but no file is using it (and not in the DOS tracks) - @Override - public int falseNegativeBlocks () - { - return falseNegatives; - } - - public void addActionListener (ActionListener actionListener) - { - actionListenerList = AWTEventMulticaster.add (actionListenerList, actionListener); - } - - public void removeActionListener (ActionListener actionListener) - { - actionListenerList = AWTEventMulticaster.remove (actionListenerList, actionListener); - } - - public void notifyListeners (String text) - { - if (actionListenerList != null) - actionListenerList - .actionPerformed (new ActionEvent (this, ActionEvent.ACTION_PERFORMED, text)); - } +package com.bytezone.diskbrowser.disk; + +import java.awt.AWTEventMulticaster; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.Enumeration; +import java.util.List; + +import javax.swing.JTree; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; + +import com.bytezone.diskbrowser.applefile.AbstractFile; +import com.bytezone.diskbrowser.applefile.AppleFileSource; +import com.bytezone.diskbrowser.applefile.BootSector; +import com.bytezone.diskbrowser.gui.DataSource; + +// -----------------------------------------------------------------------------------// +public abstract class AbstractFormattedDisk implements FormattedDisk +// -----------------------------------------------------------------------------------// +{ + protected Disk disk; + protected FormattedDisk parent; // used by Dual-dos disks + + protected ActionListener actionListenerList; + protected JTree catalogTree; + protected Path originalPath; + + protected List sectorTypesList = new ArrayList<> (); + protected List fileEntries = new ArrayList<> (); + + public SectorType[] sectorTypes; + + protected BootSector bootSector; + + public final SectorType emptySector = new SectorType ("Unused (empty)", Color.white); + public final SectorType usedSector = new SectorType ("Unused (data)", Color.yellow); + + protected int falsePositives; + protected int falseNegatives; + + protected Dimension gridLayout; + + protected BitSet freeBlocks; + protected BitSet usedBlocks; // still to be populated - currently using stillAvailable () + + // ---------------------------------------------------------------------------------// + public AbstractFormattedDisk (Disk disk) + // ---------------------------------------------------------------------------------// + { + this.disk = disk; + freeBlocks = new BitSet (disk.getTotalBlocks ()); + usedBlocks = new BitSet (disk.getTotalBlocks ()); + /* + * All formatted disks will have empty and/or used sectors, so set them + * here, and let the actual subclass add its sector types later. This list + * is used to hold one of each sector type so that the DiskLayoutPanel can + * draw its grid and key correctly. Every additional type that the instance + * creates should be added here too. + */ + sectorTypesList.add (emptySector); + sectorTypesList.add (usedSector); + /* + * Hopefully every used sector will be changed by the subclass to something + * sensible, but deleted files will always leave the sector as used/unknown + * as it contains data. + */ + setSectorTypes (); + setGridLayout (); + /* + * Create the disk name as the root for the catalog tree. Subclasses will + * have to append their catalog entries to this node. + */ + DefaultAppleFileSource afs = + new DefaultAppleFileSource (getName (), disk.toString (), this); + DefaultMutableTreeNode root = new DefaultMutableTreeNode (afs); + DefaultTreeModel treeModel = new DefaultTreeModel (root); + catalogTree = new JTree (treeModel); + treeModel.setAsksAllowsChildren (true); // allows empty nodes to appear as folders + /* + * Add an ActionListener to the disk in case the interleave or blocksize + * changes + */ + disk.addActionListener (new ActionListener () + { + @Override + public void actionPerformed (ActionEvent e) + { + setSectorTypes (); + } + }); + } + + // ---------------------------------------------------------------------------------// + protected void setEmptyByte (byte value) + // ---------------------------------------------------------------------------------// + { + getDisk ().setEmptyByte (value); + setSectorTypes (); + } + + // ---------------------------------------------------------------------------------// + private void setSectorTypes () + // ---------------------------------------------------------------------------------// + { + sectorTypes = new SectorType[disk.getTotalBlocks ()]; + + for (DiskAddress da : disk) + sectorTypes[da.getBlock ()] = disk.isSectorEmpty (da) ? emptySector : usedSector; + + setGridLayout (); + } + + // ---------------------------------------------------------------------------------// + private void setGridLayout () + // ---------------------------------------------------------------------------------// + { + int totalBlocks = disk.getTotalBlocks (); + + switch (totalBlocks) + { + case 280: + gridLayout = new Dimension (8, 35); + break; + + case 455: + gridLayout = new Dimension (13, 35); + break; + + case 560: + gridLayout = new Dimension (16, 35); + break; + + case 768: + gridLayout = new Dimension (16, 48); + break; + + case 1600: + if (disk.getSectorsPerTrack () == 32) + gridLayout = new Dimension (disk.getSectorsPerTrack (), disk.getTotalTracks ()); + else + gridLayout = new Dimension (16, 100); + break; + + case 2048: + gridLayout = new Dimension (8, 256); + break; + + default: + int[] sizes = { 32, 20, 16, 8 }; + for (int size : sizes) + if ((totalBlocks % size) == 0) + { + gridLayout = new Dimension (size, totalBlocks / size); + break; + } + if (gridLayout == null) + System.out.println ("Unusable total blocks : " + totalBlocks); + } + } + + // ---------------------------------------------------------------------------------// + @Override + public Dimension getGridLayout () + // ---------------------------------------------------------------------------------// + { + return gridLayout; + } + + // ---------------------------------------------------------------------------------// + @Override + public Disk getDisk () + // ---------------------------------------------------------------------------------// + { + return disk; + } + + // ---------------------------------------------------------------------------------// + @Override + public FormattedDisk getParent () + // ---------------------------------------------------------------------------------// + { + return parent; + } + + // ---------------------------------------------------------------------------------// + @Override + public void setParent (FormattedDisk disk) + // ---------------------------------------------------------------------------------// + { + parent = disk; + } + + // ---------------------------------------------------------------------------------// + @Override + public void setOriginalPath (Path path) + // ---------------------------------------------------------------------------------// + { + this.originalPath = path; + + DefaultMutableTreeNode root = getCatalogTreeRoot (); + if (root.getUserObject () == null) + root.setUserObject ( + new DefaultAppleFileSource (getName (), disk.toString (), this)); + } + + // ---------------------------------------------------------------------------------// + @Override + public Path getOriginalPath () + // ---------------------------------------------------------------------------------// + { + return originalPath; + } + + // ---------------------------------------------------------------------------------// + @Override + public String getAbsolutePath () + // ---------------------------------------------------------------------------------// + { + if (originalPath != null) + return originalPath.toString (); + + return disk.getFile ().getAbsolutePath (); + } + + // ---------------------------------------------------------------------------------// + @Override + public String getDisplayPath () + // ---------------------------------------------------------------------------------// + { + if (originalPath != null) + return originalPath.toString (); + + String home = System.getProperty ("user.home"); + String path = disk.getFile ().getAbsolutePath (); + if (path.startsWith (home)) + return "~" + path.substring (home.length ()); + + return disk.getFile ().getAbsolutePath (); + } + + // ---------------------------------------------------------------------------------// + @Override + public boolean isTempDisk () + // ---------------------------------------------------------------------------------// + { + return originalPath != null; + } + + // ---------------------------------------------------------------------------------// + @Override + public String getName () + // ---------------------------------------------------------------------------------// + { + if (originalPath != null) + { + Path path = originalPath.getFileName (); + if (path != null) + return path.toString (); + } + return disk.getFile ().getName (); + } + + // ---------------------------------------------------------------------------------// + @Override + public void writeFile (AbstractFile file) + // ---------------------------------------------------------------------------------// + { + System.out.println ("not implemented yet"); + } + + // ---------------------------------------------------------------------------------// + @Override + public List getCatalogList () + // ---------------------------------------------------------------------------------// + { + return fileEntries; + } + + // ---------------------------------------------------------------------------------// + @Override + public AppleFileSource getFile (String uniqueName) + // ---------------------------------------------------------------------------------// + { + for (AppleFileSource afs : fileEntries) + if (afs.getUniqueName ().equals (uniqueName)) + return afs; + return null; + } + + // ---------------------------------------------------------------------------------// + @Override + public JTree getCatalogTree () + // ---------------------------------------------------------------------------------// + { + return catalogTree; + } + + // ---------------------------------------------------------------------------------// + public DefaultMutableTreeNode getCatalogTreeRoot () + // ---------------------------------------------------------------------------------// + { + return (DefaultMutableTreeNode) catalogTree.getModel ().getRoot (); + } + + // ---------------------------------------------------------------------------------// + public void makeNodeVisible (DefaultMutableTreeNode node) + // ---------------------------------------------------------------------------------// + { + catalogTree.makeVisible ( + new TreePath (((DefaultTreeModel) catalogTree.getModel ()).getPathToRoot (node))); + } + + // ---------------------------------------------------------------------------------// + protected DefaultMutableTreeNode findNode (DefaultMutableTreeNode node, String name) + // ---------------------------------------------------------------------------------// + { + Enumeration children = node.breadthFirstEnumeration (); + if (children != null) + { + while (children.hasMoreElements ()) + { + DefaultMutableTreeNode childNode = + (DefaultMutableTreeNode) children.nextElement (); + if (childNode.getUserObject ().toString ().indexOf (name) > 0) + return childNode; + } + } + return null; + } + + // ---------------------------------------------------------------------------------// + + /* + * These routines just hand back the information that was created above, and + * added to by the subclass. + */ + @Override + public SectorType getSectorType (int block) + // ---------------------------------------------------------------------------------// + { + return getSectorType (disk.getDiskAddress (block)); + } + + // ---------------------------------------------------------------------------------// + @Override + public SectorType getSectorType (int track, int sector) + // ---------------------------------------------------------------------------------// + { + return getSectorType (disk.getDiskAddress (track, sector)); + } + + // ---------------------------------------------------------------------------------// + @Override + public SectorType getSectorType (DiskAddress da) + // ---------------------------------------------------------------------------------// + { + return sectorTypes[da.getBlock ()]; + } + + // ---------------------------------------------------------------------------------// + @Override + public List getSectorTypeList () + // ---------------------------------------------------------------------------------// + { + return sectorTypesList; + } + + // ---------------------------------------------------------------------------------// + @Override + public void setSectorType (int block, SectorType type) + // ---------------------------------------------------------------------------------// + { + if (block < sectorTypes.length) + sectorTypes[block] = type; + else + System.out.println ("setSectorType: Invalid block number: " + block); + } + + // Override this so that the correct sector type can be displayed + // ---------------------------------------------------------------------------------// + @Override + public DataSource getFormattedSector (DiskAddress da) + // ---------------------------------------------------------------------------------// + { + if (da.getBlock () == 0 && bootSector != null) + return bootSector; + + SectorType sectorType = sectorTypes[da.getBlock ()]; + byte[] buffer = disk.readSector (da); + String address = String.format ("%02X %02X", da.getTrack (), da.getSector ()); + + if (sectorType == emptySector) + return new DefaultSector ("Empty sector at " + address, disk, buffer, da); + if (sectorType == usedSector) + return new DefaultSector ("Orphan sector at " + address, disk, buffer, da); + + String name = getSectorFilename (da); + if (!name.isEmpty ()) + name = " : " + name; + return new DefaultSector ("Data sector at " + address + name, disk, buffer, da); + } + + /* + * Override this with something useful + */ + // ---------------------------------------------------------------------------------// + @Override + public AppleFileSource getCatalog () + // ---------------------------------------------------------------------------------// + { + return new DefaultAppleFileSource (disk.toString (), this); + } + + // ---------------------------------------------------------------------------------// + @Override + public String getSectorFilename (DiskAddress da) + // ---------------------------------------------------------------------------------// + { + for (AppleFileSource entry : fileEntries) + if (entry.contains (da)) + return (entry).getUniqueName (); + + return ""; + } + + // ---------------------------------------------------------------------------------// + @Override + public int clearOrphans () + // ---------------------------------------------------------------------------------// + { + System.out.println ("Not implemented yet"); + return 0; + } + + // ---------------------------------------------------------------------------------// + @Override + public boolean isSectorFree (DiskAddress da) + // ---------------------------------------------------------------------------------// + { + return freeBlocks.get (da.getBlock ()); + } + + // ---------------------------------------------------------------------------------// + @Override + public boolean isSectorFree (int blockNo) + // ---------------------------------------------------------------------------------// + { + return freeBlocks.get (blockNo); + } + + // representation of the Free Sector Table + // ---------------------------------------------------------------------------------// + @Override + public void setSectorFree (int block, boolean free) + // ---------------------------------------------------------------------------------// + { + if (block < 0 || block >= freeBlocks.size ()) + { + System.out.printf ("Block %d not in range : 0-%d%n", block, freeBlocks.size () - 1); + return; + } + //assert block < freeBlocks.size () : String.format ("Set free block # %6d, size %6d", + // block, freeBlocks.size ()); + freeBlocks.set (block, free); + } + + // Check that the sector hasn't already been flagged as part of the disk structure + // ---------------------------------------------------------------------------------// + @Override + public boolean stillAvailable (DiskAddress da) + // ---------------------------------------------------------------------------------// + { + return stillAvailable (da.getBlock ()); + } + + // ---------------------------------------------------------------------------------// + @Override + public boolean stillAvailable (int blockNo) + // ---------------------------------------------------------------------------------// + { + return sectorTypes[blockNo] == usedSector || sectorTypes[blockNo] == emptySector; + } + + // ---------------------------------------------------------------------------------// + @Override + public void verify () + // ---------------------------------------------------------------------------------// + { + System.out.println ("Sectors to clean :"); + for (int i = 0, max = disk.getTotalBlocks (); i < max; i++) + { + if (freeBlocks.get (i)) + { + if (sectorTypes[i] == usedSector) + System.out.printf ("%04X clean%n", i); + } + else + { + if (sectorTypes[i] == usedSector) + System.out.printf ("%04X *** error ***%n", i); + } + } + } + + // VTOC flags sector as free, but it is in use by a file + // ---------------------------------------------------------------------------------// + @Override + public int falsePositiveBlocks () + // ---------------------------------------------------------------------------------// + { + return falsePositives; + } + + // VTOC flags sector as in use, but no file is using it (and not in the DOS tracks) + // ---------------------------------------------------------------------------------// + @Override + public int falseNegativeBlocks () + // ---------------------------------------------------------------------------------// + { + return falseNegatives; + } + + // ---------------------------------------------------------------------------------// + public void addActionListener (ActionListener actionListener) + // ---------------------------------------------------------------------------------// + { + actionListenerList = AWTEventMulticaster.add (actionListenerList, actionListener); + } + + // ---------------------------------------------------------------------------------// + public void removeActionListener (ActionListener actionListener) + // ---------------------------------------------------------------------------------// + { + actionListenerList = AWTEventMulticaster.remove (actionListenerList, actionListener); + } + + // ---------------------------------------------------------------------------------// + public void notifyListeners (String text) + // ---------------------------------------------------------------------------------// + { + if (actionListenerList != null) + actionListenerList + .actionPerformed (new ActionEvent (this, ActionEvent.ACTION_PERFORMED, text)); + } } \ No newline at end of file diff --git a/src/com/bytezone/diskbrowser/disk/AbstractSector.java b/src/com/bytezone/diskbrowser/disk/AbstractSector.java index 8fae4dc..90d6bdd 100755 --- a/src/com/bytezone/diskbrowser/disk/AbstractSector.java +++ b/src/com/bytezone/diskbrowser/disk/AbstractSector.java @@ -1,129 +1,152 @@ -package com.bytezone.diskbrowser.disk; - -import java.awt.image.BufferedImage; - -import javax.swing.JComponent; -import javax.swing.JPanel; - -import com.bytezone.diskbrowser.applefile.AssemblerProgram; -import com.bytezone.diskbrowser.gui.DataSource; -import com.bytezone.diskbrowser.utilities.HexFormatter; - -public abstract class AbstractSector implements DataSource -{ - private static String newLine = String.format ("%n"); - private static String newLine2 = newLine + newLine; - - final public byte[] buffer; - protected Disk disk; - protected DiskAddress diskAddress; - // private List diskAddressList; - AssemblerProgram assembler; - String description; - - public AbstractSector (Disk disk, byte[] buffer, DiskAddress diskAddress) - { - this.buffer = buffer; - this.disk = disk; - this.diskAddress = diskAddress; - } - - public AbstractSector (Disk disk, byte[] buffer)//, List diskAddressList) - { - this.buffer = buffer; - this.disk = disk; - // this.diskAddressList = diskAddressList; - } - - @Override - public String getAssembler () - { - if (assembler == null) - assembler = new AssemblerProgram ("noname", buffer, 0); - return assembler.getText (); - } - - @Override - public String getHexDump () - { - return HexFormatter.format (buffer, 0, buffer.length); - } - - @Override - public BufferedImage getImage () - { - return null; - } - - @Override - public String getText () - { - if (description == null) - description = createText (); - return description; - } - - public abstract String createText (); - - protected StringBuilder getHeader (String title) - { - StringBuilder text = new StringBuilder (); - - text.append (title + newLine2); - text.append ("Offset Value Description" + newLine); - text.append ("======= =========== " - + "===============================================================" + newLine); - return text; - } - - @Override - public JComponent getComponent () - { - JPanel panel = new JPanel (); - return panel; - } - - protected void addText (StringBuilder text, byte[] buffer, int offset, int size, - String desc) - { - if ((offset + size - 1) > buffer.length) - return; - - switch (size) - { - case 1: - text.append (String.format ("%03X %02X %s%n", offset, - buffer[offset], desc)); - break; - case 2: - text.append (String.format ("%03X-%03X %02X %02X %s%n", offset, - offset + 1, buffer[offset], buffer[offset + 1], desc)); - break; - case 3: - text.append (String.format ("%03X-%03X %02X %02X %02X %s%n", offset, - offset + 2, buffer[offset], buffer[offset + 1], buffer[offset + 2], desc)); - break; - case 4: - text.append (String.format ("%03X-%03X %02X %02X %02X %02X %s%n", offset, - offset + 3, buffer[offset], buffer[offset + 1], buffer[offset + 2], - buffer[offset + 3], desc)); - break; - default: - System.out.println ("Invalid length : " + size); - } - } - - protected void addTextAndDecimal (StringBuilder text, byte[] b, int offset, int size, - String desc) - { - if (size == 1) - desc += " (" + (b[offset] & 0xFF) + ")"; - else if (size == 2) - desc += - String.format (" (%,d)", ((b[offset + 1] & 0xFF) * 256 + (b[offset] & 0xFF))); - else if (size == 3) - desc += String.format (" (%,d)", ((b[offset + 2] & 0xFF) * 65536) - + ((b[offset + 1] & 0xFF) * 256) + (b[offset] & 0xFF)); - addText (text, b, offset, size, desc); - } +package com.bytezone.diskbrowser.disk; + +import java.awt.image.BufferedImage; + +import javax.swing.JComponent; +import javax.swing.JPanel; + +import com.bytezone.diskbrowser.applefile.AssemblerProgram; +import com.bytezone.diskbrowser.gui.DataSource; +import com.bytezone.diskbrowser.utilities.HexFormatter; + +// -----------------------------------------------------------------------------------// +public abstract class AbstractSector implements DataSource +// -----------------------------------------------------------------------------------// +{ + private static String newLine = String.format ("%n"); + private static String newLine2 = newLine + newLine; + + final public byte[] buffer; + protected Disk disk; + protected DiskAddress diskAddress; + AssemblerProgram assembler; + String description; + + // ---------------------------------------------------------------------------------// + public AbstractSector (Disk disk, byte[] buffer, DiskAddress diskAddress) + // ---------------------------------------------------------------------------------// + { + this.buffer = buffer; + this.disk = disk; + this.diskAddress = diskAddress; + } + + // ---------------------------------------------------------------------------------// + public AbstractSector (Disk disk, byte[] buffer) + // ---------------------------------------------------------------------------------// + { + this.buffer = buffer; + this.disk = disk; + } + + // ---------------------------------------------------------------------------------// + @Override + public String getAssembler () + // ---------------------------------------------------------------------------------// + { + if (assembler == null) + assembler = new AssemblerProgram ("noname", buffer, 0); + return assembler.getText (); + } + + // ---------------------------------------------------------------------------------// + @Override + public String getHexDump () + // ---------------------------------------------------------------------------------// + { + return HexFormatter.format (buffer, 0, buffer.length); + } + + // ---------------------------------------------------------------------------------// + @Override + public BufferedImage getImage () + // ---------------------------------------------------------------------------------// + { + return null; + } + + // ---------------------------------------------------------------------------------// + @Override + public String getText () + // ---------------------------------------------------------------------------------// + { + if (description == null) + description = createText (); + return description; + } + + // ---------------------------------------------------------------------------------// + public abstract String createText (); + // ---------------------------------------------------------------------------------// + + // ---------------------------------------------------------------------------------// + protected StringBuilder getHeader (String title) + // ---------------------------------------------------------------------------------// + { + StringBuilder text = new StringBuilder (); + + text.append (title + newLine2); + text.append ("Offset Value Description" + newLine); + text.append ("======= =========== " + + "===============================================================" + newLine); + return text; + } + + // ---------------------------------------------------------------------------------// + @Override + public JComponent getComponent () + // ---------------------------------------------------------------------------------// + { + JPanel panel = new JPanel (); + return panel; + } + + // ---------------------------------------------------------------------------------// + protected void addText (StringBuilder text, byte[] buffer, int offset, int size, + String desc) + // ---------------------------------------------------------------------------------// + { + if ((offset + size - 1) > buffer.length) + return; + + switch (size) + { + case 1: + text.append (String.format ("%03X %02X %s%n", offset, + buffer[offset], desc)); + break; + case 2: + text.append (String.format ("%03X-%03X %02X %02X %s%n", offset, + offset + 1, buffer[offset], buffer[offset + 1], desc)); + break; + case 3: + text.append (String.format ("%03X-%03X %02X %02X %02X %s%n", offset, + offset + 2, buffer[offset], buffer[offset + 1], buffer[offset + 2], desc)); + break; + case 4: + text.append (String.format ("%03X-%03X %02X %02X %02X %02X %s%n", offset, + offset + 3, buffer[offset], buffer[offset + 1], buffer[offset + 2], + buffer[offset + 3], desc)); + break; + default: + System.out.println ("Invalid length : " + size); + } + } + + // ---------------------------------------------------------------------------------// + protected void addTextAndDecimal (StringBuilder text, byte[] b, int offset, int size, + String desc) + // ---------------------------------------------------------------------------------// + { + if (size == 1) + desc += " (" + (b[offset] & 0xFF) + ")"; + else if (size == 2) + desc += + String.format (" (%,d)", ((b[offset + 1] & 0xFF) * 256 + (b[offset] & 0xFF))); + else if (size == 3) + desc += String.format (" (%,d)", ((b[offset + 2] & 0xFF) * 65536) + + ((b[offset + 1] & 0xFF) * 256) + (b[offset] & 0xFF)); + + addText (text, b, offset, size, desc); + } } \ No newline at end of file diff --git a/src/com/bytezone/diskbrowser/disk/AppleDisk.java b/src/com/bytezone/diskbrowser/disk/AppleDisk.java index b135abb..917269d 100755 --- a/src/com/bytezone/diskbrowser/disk/AppleDisk.java +++ b/src/com/bytezone/diskbrowser/disk/AppleDisk.java @@ -1,722 +1,812 @@ -package com.bytezone.diskbrowser.disk; - -import java.awt.AWTEventMulticaster; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.zip.CRC32; -import java.util.zip.Checksum; - -import com.bytezone.common.Utility; -import com.bytezone.diskbrowser.applefile.AppleFileSource; -import com.bytezone.diskbrowser.nib.NibFile; -import com.bytezone.diskbrowser.nib.V2dFile; -import com.bytezone.diskbrowser.nib.WozFile; -import com.bytezone.diskbrowser.utilities.FileFormatException; -import com.bytezone.diskbrowser.utilities.HexFormatter; - -public class AppleDisk implements Disk -{ - private static final int MAX_INTERLEAVE = 3; - private static final int SECTOR_SIZE = 256; - private static final int BLOCK_SIZE = 512; - - public final File file; - private final byte[] diskBuffer; // contains the disk contents in memory - - private final int tracks; // usually 35 for floppy disks - private int sectors; // 8 or 16 (or 32 for unidos) - private int blocks; // 280 or 560 for floppy disks, higher for HD - - private final int trackSize; // 4096 - public int sectorSize; // 256 or 512 - - private int interleave = 0; - private static int[][] interleaveSector = // - { { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, - 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 }, // None - { 0, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 15 }, // Prodos/Pascal - { 0, 13, 11, 9, 7, 5, 3, 1, 14, 12, 10, 8, 6, 4, 2, 15 }, // Infocom - { 0, 6, 12, 3, 9, 15, 14, 5, 11, 2, 8, 7, 13, 4, 10, 1 } }; // CPM - - // Physical disk interleave: - // Info from http://www.applelogic.org/TheAppleIIEGettingStarted.html - // Block: 0 1 2 3 4 5 6 7 8 9 A B C D E F - // Position: 0 8 1 9 2 A 3 B 4 C 5 D 6 E 7 F - Prodos (.PO disks) - // Position: 0 7 E 6 D 5 C 4 B 3 A 2 9 1 8 F - Dos (.DO disks) - - // https://github.com/AppleWin/AppleWin/blob/master/source/DiskImageHelper.cpp - // DO logical order 0 1 2 3 4 5 6 7 8 9 A B C D E F */ - // physical order 0 D B 9 7 5 3 1 E C A 8 6 4 2 F */ - - // PO logical order 0 E D C B A 9 8 7 6 5 4 3 2 1 F */ - // physical order 0 2 4 6 8 A C E 1 3 5 7 9 B D F */ - - //BYTE CImageBase::ms_SectorNumber[NUM_SECTOR_ORDERS][0x10] = - //{ - // {0x00,0x08,0x01,0x09,0x02,0x0A,0x03,0x0B, 0x04,0x0C,0x05,0x0D,0x06,0x0E,0x07,0x0F}, - // {0x00,0x07,0x0E,0x06,0x0D,0x05,0x0C,0x04, 0x0B,0x03,0x0A,0x02,0x09,0x01,0x08,0x0F}, - // {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} - //}; - - // * TABLE OF PHYSICAL BSECTR NUMBERS - // * WHICH CORRESPOND TO THE LOGICAL - // * BSECTRS 0-F ON TRACK ZERO... - // BHERE2 EQU >* - // TABLE EQU $800+BHERE2 - // DFB $00,13,11 ;00->00,01->13,02->11 - // DFB 09,07,05 ;03->09,04->07;05->05 - // DFB 03,01,14 ;06->03,07->01,08->14 - // DFB 12,10,08 ;09->12,10->10,11->08 - // DFB 06,04,02,15 ;12->06,13->04,14->02,15->15 - - private boolean[] hasData; - private byte emptyByte = 0; - - private ActionListener actionListenerList; - private List blockList; - - private WozFile wozFile; - - private final boolean debug = false; - - public AppleDisk (File file, int tracks, int sectors) throws FileFormatException - { - this (file, tracks, sectors, 0); - } - - public AppleDisk (File file, int tracks, int sectors, int skip) - throws FileFormatException - { - assert (file.exists ()) : "No such path :" + file.getAbsolutePath (); - assert (!file.isDirectory ()) : "File is directory :" + file.getAbsolutePath (); - assert (file.length () <= Integer.MAX_VALUE) : "File too large"; - assert (file.length () != 0) : "File empty"; - - String name = file.getName (); - int pos = name.lastIndexOf ('.'); - - String suffix = pos > 0 ? name.substring (pos + 1) : ""; - - byte[] buffer = getPrefix (file); // HDV could be a 2mg - String prefix = new String (buffer, 0, 4); - - if (suffix.equalsIgnoreCase ("2mg") || "2IMG".equals (prefix)) - { - if (debug) - System.out.println (Utility.toHex (buffer)); - - // http://apple2.org.za/gswv/a2zine/Docs/DiskImage_2MG_Info.txt - if ("2IMG".equals (prefix)) - { - if (debug) - { - String creator = new String (buffer, 4, 4); - System.out.printf ("Prefix : %s%n", prefix); - System.out.printf ("Creator : %s%n", creator); - int headerSize = Utility.getWord (buffer, 8); - System.out.printf ("Header : %d%n", headerSize); - int version = Utility.getWord (buffer, 10); - System.out.printf ("Version : %d%n", version); - System.out.printf ("Format : %02X%n", buffer[12]); - } - - int diskData = Utility.getLong (buffer, 28); - blocks = HexFormatter.intValue (buffer[20], buffer[21]); // 1600 - - if (debug) - { - System.out.printf ("Data size : %08X (%,d)%n", diskData, diskData); - System.out.printf ("Blocks : %,d%n", blocks); - } - - if (diskData > 0) - this.blocks = diskData / 4096 * 8; // reduce blocks to a multiple of 8 - - // see /Asimov disks/images/gs/os/prodos16/ProDOS 16v1_3.2mg - - if (debug) - System.out.printf ("Blocks : %,d%n", blocks); - - this.sectorSize = 512; - this.trackSize = 8 * sectorSize; - skip = Utility.getWord (buffer, 8); - - tracks = blocks / 8; // change parameter! - sectors = 8; // change parameter! - } - else - { - System.out.println ("Not a 2mg file"); - this.blocks = (int) file.length () / 4096 * 8; // reduce blocks to a multiple of 8 - tracks = blocks / 8; // change parameter! - sectors = 8; // change parameter! - this.sectorSize = 512; - this.trackSize = sectors * sectorSize; - } - } - else if (suffix.equalsIgnoreCase ("HDV") - || (suffix.equalsIgnoreCase ("po") && tracks > 50)) // ULTIMATE APPLE1 CFFA 3.5.po - { - //this.blocks = (int) file.length () / 4096 * 8; // reduce blocks to a multiple of 8 - this.blocks = tracks * sectors; - this.sectorSize = 512; - this.trackSize = sectors * sectorSize; - } - else - { - if (file.length () == 143360 && tracks == 256 && sectors == 8) // wiz4 - { - this.blocks = tracks * sectors; - this.sectorSize = 512; - this.trackSize = sectors * sectorSize; - } - else if (file.length () == 819200 && tracks == 50 && sectors == 32) // unidisk - { - this.blocks = tracks * sectors; - this.sectorSize = 256; - this.trackSize = sectors * sectorSize; - } - else - { - this.blocks = tracks * sectors; - this.sectorSize = (int) file.length () / blocks; - this.trackSize = sectors * sectorSize; - } - } - - if (sectorSize != 256 && sectorSize != 512) - throw new FileFormatException ("Invalid sector size : " + sectorSize); - - this.file = file; - this.tracks = tracks; - this.sectors = sectors; - - if (debug) - { - System.out.printf ("Track size : %d%n", trackSize); - System.out.printf ("Sector size : %d%n", sectorSize); - System.out.printf ("Tracks : %d%n", tracks); - System.out.printf ("Sectors : %d%n", sectors); - System.out.printf ("Blocks : %d%n", blocks); - } - - diskBuffer = new byte[blocks * sectorSize]; - hasData = new boolean[blocks]; - - if (debug) - { - System.out.printf ("DiskBuffer size : %,d%n", diskBuffer.length); - System.out.printf ("Skip size : %,d%n", skip); - } - - try - { - BufferedInputStream in = new BufferedInputStream (new FileInputStream (file)); - if (skip > 0) - in.skip (skip); - in.read (diskBuffer); - in.close (); - } - catch (IOException e) - { - e.printStackTrace (); - System.exit (1); - } - - checkSectorsForData (); - } - - public AppleDisk (V2dFile disk, int tracks, int sectors) - { - this.tracks = tracks; - this.sectors = sectors; - file = disk.file; - diskBuffer = disk.getDiskBuffer (); - - trackSize = 4096; - sectorSize = trackSize / sectors; - blocks = tracks * sectors; - hasData = new boolean[blocks]; - - checkSectorsForData (); - } - - public AppleDisk (NibFile disk) // not used yet - { - tracks = 35; - trackSize = 4096; - file = disk.file; - diskBuffer = disk.getDiskBuffer (); - } - - public AppleDisk (WozFile wozFile, int tracks, int sectors) - { - this.wozFile = wozFile; - this.tracks = tracks; - this.sectors = sectors; - file = wozFile.file; - diskBuffer = wozFile.getDiskBuffer (); - - if (sectors == 13) - { - trackSize = 0xD00; - sectorSize = 256; - } - else - { - trackSize = 0x1000; - sectorSize = trackSize / sectors; - } - - blocks = tracks * sectors; - hasData = new boolean[blocks]; - - checkSectorsForData (); - } - - private byte[] getPrefix (File path) - { - byte[] buffer = new byte[64]; - try - { - BufferedInputStream file = new BufferedInputStream (new FileInputStream (path)); - file.read (buffer); - file.close (); - } - catch (IOException e) - { - e.printStackTrace (); - System.exit (1); - } - - return buffer; - } - - private void checkSectorsForData () - { - if (true) - { - checkSectorsFaster (); - return; - } - // force blockList to be rebuilt with the correct number/size of blocks - blockList = null; - - for (DiskAddress da : this) // uses blockList.iterator - { - byte[] buffer = readSector (da); - hasData[da.getBlock ()] = false; - for (int i = 0; i < sectorSize; i++) - if (buffer[i] != emptyByte) - { - hasData[da.getBlock ()] = true; - break; - } - } - } - - private void checkSectorsFaster () - { - // force blockList to be rebuilt with the correct number/size of blocks - blockList = null; - - for (DiskAddress da : this) // uses blockList.iterator - { - if (sectorSize == SECTOR_SIZE) // 256 byte sectors - { - int diskOffset = getBufferOffset (da); - hasData[da.getBlock ()] = check (diskOffset); - } - else // 512 byte blocks - { - int diskOffset1 = getBufferOffset (da, 0); - int diskOffset2 = getBufferOffset (da, 1); - hasData[da.getBlock ()] = check (diskOffset1) || check (diskOffset2); - } - } - } - - private boolean check (int diskOffset) - { - for (int i = diskOffset, max = diskOffset + SECTOR_SIZE; i < max; i++) - if (diskBuffer[i] != emptyByte) - return true; - return false; - } - - /* - * Routines that implement the Disk interface - */ - - @Override - public int getSectorsPerTrack () - { - return trackSize / sectorSize; - } - - @Override - public int getTrackSize () - { - return trackSize; - } - - @Override - public int getBlockSize () - { - return sectorSize; - } - - @Override - public int getTotalBlocks () - { - return blocks; - } - - @Override - public int getTotalTracks () - { - return tracks; - } - - @Override - public boolean isSectorEmpty (DiskAddress da) - { - return !hasData[da.getBlock ()]; - } - - @Override - public boolean isSectorEmpty (int block) - { - return !hasData[block]; - } - - @Override - public boolean isSectorEmpty (int track, int sector) - { - return !hasData[getDiskAddress (track, sector).getBlock ()]; - } - - // @Override - // public boolean isSectorMissing (DiskAddress da) - // { - // return isMissing[da.getBlock ()]; - // } - // - // @Override - // public boolean isSectorMissing (int block) - // { - // return isMissing[block]; - // } - // - // @Override - // public boolean isSectorMissing (int track, int sector) - // { - // return isMissing[getDiskAddress (track, sector).getBlock ()]; - // } - - @Override - public File getFile () - { - return file; - } - - @Override - public byte[] readSector (DiskAddress da) - { - byte[] buffer = new byte[sectorSize]; - if (da == null) - System.out.println ("Disk address is null"); - else - readBuffer (da, buffer, 0); - return buffer; - } - - @Override - public byte[] readSectors (List daList) - { - byte[] buffer = new byte[daList.size () * sectorSize]; - int ptr = 0; - for (DiskAddress da : daList) - { - // sparse text/PNT/PIC files may have gaps - if (da != null && (da.getBlock () > 0 || ((AppleDiskAddress) da).zeroFlag ())) - readBuffer (da, buffer, ptr); - ptr += sectorSize; - } - return buffer; - } - - @Override - public byte[] readSector (int track, int sector) - { - return readSector (getDiskAddress (track, sector)); - } - - @Override - public byte[] readSector (int block) - { - return readSector (getDiskAddress (block)); - } - - @Override - public void writeSector (DiskAddress da, byte[] buffer) - { - writeBuffer (da, buffer); - } - - @Override - public void setInterleave (int interleave) - { - assert (interleave >= 0 && interleave <= MAX_INTERLEAVE) : "Invalid interleave"; - this.interleave = interleave; - checkSectorsForData (); - if (actionListenerList != null) - notifyListeners ("Interleave changed"); - } - - @Override - public int getInterleave () - { - return interleave; - } - - @Override - public void setBlockSize (int size) - { - assert (size == SECTOR_SIZE || size == BLOCK_SIZE) : "Invalid sector size : " + size; - if (sectorSize == size) - return; - - sectorSize = size; - sectors = trackSize / sectorSize; - blocks = tracks * sectors; - - hasData = new boolean[blocks]; - checkSectorsForData (); - - if (actionListenerList != null) - notifyListeners ("Sector size changed"); - } - - @Override - public DiskAddress getDiskAddress (int track, int sector) - { - if (!isValidAddress (track, sector)) - { - System.out.println ("Invalid block : " + track + "/" + sector); - return null; - // return new AppleDiskAddress (this, 0); this was looping 26/07/2016 - } - return new AppleDiskAddress (this, track, sector); - } - - @Override - public DiskAddress getDiskAddress (int block) - { - if (!isValidAddress (block)) - { - System.out.printf ("getDiskAddress: Invalid block : %d of %d%n", block, - this.blocks); - return null; - // return new AppleDiskAddress (this, 0); // this was looping 26/07/2016 - } - return new AppleDiskAddress (this, block); - } - - @Override - public List getDiskAddressList (int... blocks) - { - List addressList = new ArrayList<> (); - - for (int block : blocks) - { - assert (isValidAddress (block)) : "Invalid block : " + block; - addressList.add (new AppleDiskAddress (this, block)); - } - return addressList; - } - - @Override - public boolean isValidAddress (int block) - { - return block >= 0 && block < this.blocks; - } - - @Override - public boolean isValidAddress (int track, int sector) - { - track &= 0x3F; - sector &= 0x1F; - - if (track < 0 || track >= this.tracks) - return false; - if (sector < 0 || sector >= this.sectors) - return false; - - return true; - } - - @Override - public boolean isValidAddress (DiskAddress da) - { - return da != null && isValidAddress (da.getTrack (), da.getSector ()); - } - - /* - * This is the only method that transfers data from the disk buffer to an output buffer. - * It handles sectors of 256 or 512 bytes, and both linear and interleaved sectors. - */ - private void readBuffer (DiskAddress da, byte[] buffer, int bufferOffset) - { - assert da.getDisk () == this : "Disk address not applicable to this disk"; - assert sectorSize == SECTOR_SIZE - || sectorSize == BLOCK_SIZE : "Invalid sector size : " + sectorSize; - assert interleave >= 0 && interleave <= MAX_INTERLEAVE : "Invalid interleave : " - + interleave; - - if (sectorSize == SECTOR_SIZE) - { - int diskOffset = getBufferOffset (da); - System.arraycopy (diskBuffer, diskOffset, buffer, bufferOffset, SECTOR_SIZE); - } - else - { - int diskOffset = getBufferOffset (da, 0); - System.arraycopy (diskBuffer, diskOffset, buffer, bufferOffset, SECTOR_SIZE); - - diskOffset = getBufferOffset (da, 1); - System.arraycopy (diskBuffer, diskOffset, buffer, bufferOffset + SECTOR_SIZE, - SECTOR_SIZE); - } - } - - private void writeBuffer (DiskAddress da, byte[] buffer) - { - assert da.getDisk () == this : "Disk address not applicable to this disk"; - assert sectorSize == SECTOR_SIZE - || sectorSize == BLOCK_SIZE : "Invalid sector size : " + sectorSize; - assert interleave >= 0 && interleave <= MAX_INTERLEAVE : "Invalid interleave : " - + interleave; - - if (sectorSize == SECTOR_SIZE) - { - int diskOffset = getBufferOffset (da); - System.arraycopy (buffer, 0, diskBuffer, diskOffset, SECTOR_SIZE); - } - else - { - int diskOffset = getBufferOffset (da, 0); - System.arraycopy (buffer, 0, diskBuffer, diskOffset, SECTOR_SIZE); - - diskOffset = getBufferOffset (da, 1); - System.arraycopy (buffer, SECTOR_SIZE, diskBuffer, diskOffset, SECTOR_SIZE); - } - } - - private int getBufferOffset (DiskAddress da) - { - assert sectorSize == SECTOR_SIZE; - - return da.getTrack () * trackSize - + interleaveSector[interleave][da.getSector ()] * SECTOR_SIZE; - } - - private int getBufferOffset (DiskAddress da, int seq) - { - assert sectorSize == BLOCK_SIZE; - - assert seq == 0 || seq == 1; - - return da.getTrack () * trackSize - + interleaveSector[interleave][da.getSector () * 2 + seq] * SECTOR_SIZE; - } - - @Override - public void addActionListener (ActionListener actionListener) - { - actionListenerList = AWTEventMulticaster.add (actionListenerList, actionListener); - } - - @Override - public void removeActionListener (ActionListener actionListener) - { - actionListenerList = AWTEventMulticaster.remove (actionListenerList, actionListener); - } - - public void notifyListeners (String text) - { - if (actionListenerList != null) - actionListenerList - .actionPerformed (new ActionEvent (this, ActionEvent.ACTION_PERFORMED, text)); - } - - public AppleFileSource getDetails () - { - return new DefaultAppleFileSource (toString (), file.getName (), null); - } - - @Override - public String toString () - { - StringBuilder text = new StringBuilder (); - - String path = file.getAbsolutePath (); - String home = System.getProperty ("user.home"); - if (path.startsWith (home)) - path = "~" + path.substring (home.length ()); - - text.append (String.format ("Path................. %s%n", path)); - text.append (String.format ("File name............ %s%n", file.getName ())); - text.append (String.format ("File size............ %,d%n", file.length ())); - text.append (String.format ("Tracks............... %d%n", tracks)); - text.append (String.format ("Sectors.............. %d%n", sectors)); - text.append (String.format ("Blocks............... %,d%n", blocks)); - text.append (String.format ("Track size........... %,d%n", trackSize)); - text.append (String.format ("Sector size.......... %d%n", sectorSize)); - text.append (String.format ("Interleave........... %d", interleave)); - - if (wozFile != null) - { - text.append ("\n\n"); - text.append (wozFile); - } - - return text.toString (); - } - - @Override - public Iterator iterator () - { - if (blockList == null) - { - blockList = new ArrayList<> (blocks); - for (int block = 0; block < blocks; block++) - blockList.add (new AppleDiskAddress (this, block)); - } - - return blockList.iterator (); - } - - @Override - public long getBootChecksum () - { - byte[] buffer = readSector (0, 0); - Checksum checksum = new CRC32 (); - checksum.update (buffer, 0, buffer.length); - return checksum.getValue (); - } - - @Override - public void setEmptyByte (byte value) - { - emptyByte = value; - checkSectorsForData (); - } +package com.bytezone.diskbrowser.disk; + +import java.awt.AWTEventMulticaster; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.zip.CRC32; +import java.util.zip.Checksum; + +import com.bytezone.common.Utility; +import com.bytezone.diskbrowser.applefile.AppleFileSource; +import com.bytezone.diskbrowser.nib.NibFile; +import com.bytezone.diskbrowser.nib.V2dFile; +import com.bytezone.diskbrowser.nib.WozFile; +import com.bytezone.diskbrowser.utilities.FileFormatException; +import com.bytezone.diskbrowser.utilities.HexFormatter; + +// -----------------------------------------------------------------------------------// +public class AppleDisk implements Disk +// -----------------------------------------------------------------------------------// +{ + private static final int MAX_INTERLEAVE = 3; + private static final int SECTOR_SIZE = 256; + private static final int BLOCK_SIZE = 512; + + public final File file; + private final byte[] diskBuffer; // contains the disk contents in memory + + private final int tracks; // usually 35 for floppy disks + private int sectors; // 8 or 16 (or 32 for unidos) + private int blocks; // 280 or 560 for floppy disks, higher for HD + + private final int trackSize; // 4096 + public int sectorSize; // 256 or 512 + + private int interleave = 0; + private static int[][] interleaveSector = // + { { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 }, // None + { 0, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 15 }, // Prodos/Pascal + { 0, 13, 11, 9, 7, 5, 3, 1, 14, 12, 10, 8, 6, 4, 2, 15 }, // Infocom + { 0, 6, 12, 3, 9, 15, 14, 5, 11, 2, 8, 7, 13, 4, 10, 1 } }; // CPM + + // Physical disk interleave: + // Info from http://www.applelogic.org/TheAppleIIEGettingStarted.html + // Block: 0 1 2 3 4 5 6 7 8 9 A B C D E F + // Position: 0 8 1 9 2 A 3 B 4 C 5 D 6 E 7 F - Prodos (.PO disks) + // Position: 0 7 E 6 D 5 C 4 B 3 A 2 9 1 8 F - Dos (.DO disks) + + // https://github.com/AppleWin/AppleWin/blob/master/source/DiskImageHelper.cpp + // DO logical order 0 1 2 3 4 5 6 7 8 9 A B C D E F */ + // physical order 0 D B 9 7 5 3 1 E C A 8 6 4 2 F */ + + // PO logical order 0 E D C B A 9 8 7 6 5 4 3 2 1 F */ + // physical order 0 2 4 6 8 A C E 1 3 5 7 9 B D F */ + + //BYTE CImageBase::ms_SectorNumber[NUM_SECTOR_ORDERS][0x10] = + //{ + // {0x00,0x08,0x01,0x09,0x02,0x0A,0x03,0x0B, 0x04,0x0C,0x05,0x0D,0x06,0x0E,0x07,0x0F}, + // {0x00,0x07,0x0E,0x06,0x0D,0x05,0x0C,0x04, 0x0B,0x03,0x0A,0x02,0x09,0x01,0x08,0x0F}, + // {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} + //}; + + // * TABLE OF PHYSICAL BSECTR NUMBERS + // * WHICH CORRESPOND TO THE LOGICAL + // * BSECTRS 0-F ON TRACK ZERO... + // BHERE2 EQU >* + // TABLE EQU $800+BHERE2 + // DFB $00,13,11 ;00->00,01->13,02->11 + // DFB 09,07,05 ;03->09,04->07;05->05 + // DFB 03,01,14 ;06->03,07->01,08->14 + // DFB 12,10,08 ;09->12,10->10,11->08 + // DFB 06,04,02,15 ;12->06,13->04,14->02,15->15 + + private boolean[] hasData; + private byte emptyByte = 0; + + private ActionListener actionListenerList; + private List blockList; + + private WozFile wozFile; + + private final boolean debug = false; + + // ---------------------------------------------------------------------------------// + public AppleDisk (File file, int tracks, int sectors) throws FileFormatException + // ---------------------------------------------------------------------------------// + { + this (file, tracks, sectors, 0); + } + + // ---------------------------------------------------------------------------------// + public AppleDisk (File file, int tracks, int sectors, int skip) + throws FileFormatException + // ---------------------------------------------------------------------------------// + { + assert (file.exists ()) : "No such path :" + file.getAbsolutePath (); + assert (!file.isDirectory ()) : "File is directory :" + file.getAbsolutePath (); + assert (file.length () <= Integer.MAX_VALUE) : "File too large"; + assert (file.length () != 0) : "File empty"; + + String name = file.getName (); + int pos = name.lastIndexOf ('.'); + + String suffix = pos > 0 ? name.substring (pos + 1) : ""; + + byte[] buffer = getPrefix (file); // HDV could be a 2mg + String prefix = new String (buffer, 0, 4); + + if (suffix.equalsIgnoreCase ("2mg") || "2IMG".equals (prefix)) + { + if (debug) + System.out.println (Utility.toHex (buffer)); + + // http://apple2.org.za/gswv/a2zine/Docs/DiskImage_2MG_Info.txt + if ("2IMG".equals (prefix)) + { + if (debug) + { + String creator = new String (buffer, 4, 4); + System.out.printf ("Prefix : %s%n", prefix); + System.out.printf ("Creator : %s%n", creator); + int headerSize = Utility.getWord (buffer, 8); + System.out.printf ("Header : %d%n", headerSize); + int version = Utility.getWord (buffer, 10); + System.out.printf ("Version : %d%n", version); + System.out.printf ("Format : %02X%n", buffer[12]); + } + + int diskData = Utility.getLong (buffer, 28); + blocks = HexFormatter.intValue (buffer[20], buffer[21]); // 1600 + + if (debug) + { + System.out.printf ("Data size : %08X (%,d)%n", diskData, diskData); + System.out.printf ("Blocks : %,d%n", blocks); + } + + if (diskData > 0) + this.blocks = diskData / 4096 * 8; // reduce blocks to a multiple of 8 + + // see /Asimov disks/images/gs/os/prodos16/ProDOS 16v1_3.2mg + + if (debug) + System.out.printf ("Blocks : %,d%n", blocks); + + this.sectorSize = 512; + this.trackSize = 8 * sectorSize; + skip = Utility.getWord (buffer, 8); + + tracks = blocks / 8; // change parameter! + sectors = 8; // change parameter! + } + else + { + System.out.println ("Not a 2mg file"); + this.blocks = (int) file.length () / 4096 * 8; // reduce blocks to a multiple of 8 + tracks = blocks / 8; // change parameter! + sectors = 8; // change parameter! + this.sectorSize = 512; + this.trackSize = sectors * sectorSize; + } + } + else if (suffix.equalsIgnoreCase ("HDV") + || (suffix.equalsIgnoreCase ("po") && tracks > 50)) // ULTIMATE APPLE1 CFFA 3.5.po + { + //this.blocks = (int) file.length () / 4096 * 8; // reduce blocks to a multiple of 8 + this.blocks = tracks * sectors; + this.sectorSize = 512; + this.trackSize = sectors * sectorSize; + } + else + { + if (file.length () == 143360 && tracks == 256 && sectors == 8) // wiz4 + { + this.blocks = tracks * sectors; + this.sectorSize = 512; + this.trackSize = sectors * sectorSize; + } + else if (file.length () == 819200 && tracks == 50 && sectors == 32) // unidisk + { + this.blocks = tracks * sectors; + this.sectorSize = 256; + this.trackSize = sectors * sectorSize; + } + else + { + this.blocks = tracks * sectors; + this.sectorSize = (int) file.length () / blocks; + this.trackSize = sectors * sectorSize; + } + } + + if (sectorSize != 256 && sectorSize != 512) + throw new FileFormatException ("Invalid sector size : " + sectorSize); + + this.file = file; + this.tracks = tracks; + this.sectors = sectors; + + if (debug) + { + System.out.printf ("Track size : %d%n", trackSize); + System.out.printf ("Sector size : %d%n", sectorSize); + System.out.printf ("Tracks : %d%n", tracks); + System.out.printf ("Sectors : %d%n", sectors); + System.out.printf ("Blocks : %d%n", blocks); + } + + diskBuffer = new byte[blocks * sectorSize]; + hasData = new boolean[blocks]; + + if (debug) + { + System.out.printf ("DiskBuffer size : %,d%n", diskBuffer.length); + System.out.printf ("Skip size : %,d%n", skip); + } + + try + { + BufferedInputStream in = new BufferedInputStream (new FileInputStream (file)); + if (skip > 0) + in.skip (skip); + in.read (diskBuffer); + in.close (); + } + catch (IOException e) + { + e.printStackTrace (); + System.exit (1); + } + + checkSectorsForData (); + } + + // ---------------------------------------------------------------------------------// + public AppleDisk (V2dFile disk, int tracks, int sectors) + // ---------------------------------------------------------------------------------// + { + this.tracks = tracks; + this.sectors = sectors; + file = disk.file; + diskBuffer = disk.getDiskBuffer (); + + trackSize = 4096; + sectorSize = trackSize / sectors; + blocks = tracks * sectors; + hasData = new boolean[blocks]; + + checkSectorsForData (); + } + + // ---------------------------------------------------------------------------------// + public AppleDisk (NibFile disk) // not used yet + // ---------------------------------------------------------------------------------// + { + tracks = 35; + trackSize = 4096; + file = disk.file; + diskBuffer = disk.getDiskBuffer (); + } + + // ---------------------------------------------------------------------------------// + public AppleDisk (WozFile wozFile, int tracks, int sectors) + // ---------------------------------------------------------------------------------// + { + this.wozFile = wozFile; + this.tracks = tracks; + this.sectors = sectors; + file = wozFile.file; + diskBuffer = wozFile.getDiskBuffer (); + + if (sectors == 13) + { + trackSize = 0xD00; + sectorSize = 256; + } + else + { + trackSize = 0x1000; + sectorSize = trackSize / sectors; + } + + blocks = tracks * sectors; + hasData = new boolean[blocks]; + + checkSectorsForData (); + } + + // ---------------------------------------------------------------------------------// + private byte[] getPrefix (File path) + // ---------------------------------------------------------------------------------// + { + byte[] buffer = new byte[64]; + try + { + BufferedInputStream file = new BufferedInputStream (new FileInputStream (path)); + file.read (buffer); + file.close (); + } + catch (IOException e) + { + e.printStackTrace (); + System.exit (1); + } + + return buffer; + } + + // ---------------------------------------------------------------------------------// + private void checkSectorsForData () + // ---------------------------------------------------------------------------------// + { + if (true) + { + checkSectorsFaster (); + return; + } + // force blockList to be rebuilt with the correct number/size of blocks + blockList = null; + + for (DiskAddress da : this) // uses blockList.iterator + { + byte[] buffer = readSector (da); + hasData[da.getBlock ()] = false; + for (int i = 0; i < sectorSize; i++) + if (buffer[i] != emptyByte) + { + hasData[da.getBlock ()] = true; + break; + } + } + } + + // ---------------------------------------------------------------------------------// + private void checkSectorsFaster () + // ---------------------------------------------------------------------------------// + { + // force blockList to be rebuilt with the correct number/size of blocks + blockList = null; + + for (DiskAddress da : this) // uses blockList.iterator + { + if (sectorSize == SECTOR_SIZE) // 256 byte sectors + { + int diskOffset = getBufferOffset (da); + hasData[da.getBlock ()] = check (diskOffset); + } + else // 512 byte blocks + { + int diskOffset1 = getBufferOffset (da, 0); + int diskOffset2 = getBufferOffset (da, 1); + hasData[da.getBlock ()] = check (diskOffset1) || check (diskOffset2); + } + } + } + + // ---------------------------------------------------------------------------------// + private boolean check (int diskOffset) + // ---------------------------------------------------------------------------------// + { + for (int i = diskOffset, max = diskOffset + SECTOR_SIZE; i < max; i++) + if (diskBuffer[i] != emptyByte) + return true; + return false; + } + + /* + * Routines that implement the Disk interface + */ + + // ---------------------------------------------------------------------------------// + @Override + public int getSectorsPerTrack () + // ---------------------------------------------------------------------------------// + { + return trackSize / sectorSize; + } + + // ---------------------------------------------------------------------------------// + @Override + public int getTrackSize () + // ---------------------------------------------------------------------------------// + { + return trackSize; + } + + // ---------------------------------------------------------------------------------// + @Override + public int getBlockSize () + // ---------------------------------------------------------------------------------// + { + return sectorSize; + } + + // ---------------------------------------------------------------------------------// + @Override + public int getTotalBlocks () + // ---------------------------------------------------------------------------------// + { + return blocks; + } + + // ---------------------------------------------------------------------------------// + @Override + public int getTotalTracks () + // ---------------------------------------------------------------------------------// + { + return tracks; + } + + // ---------------------------------------------------------------------------------// + @Override + public boolean isSectorEmpty (DiskAddress da) + // ---------------------------------------------------------------------------------// + { + return !hasData[da.getBlock ()]; + } + + // ---------------------------------------------------------------------------------// + @Override + public boolean isSectorEmpty (int block) + // ---------------------------------------------------------------------------------// + { + return !hasData[block]; + } + + // ---------------------------------------------------------------------------------// + @Override + public boolean isSectorEmpty (int track, int sector) + // ---------------------------------------------------------------------------------// + { + return !hasData[getDiskAddress (track, sector).getBlock ()]; + } + + // @Override + // public boolean isSectorMissing (DiskAddress da) + // { + // return isMissing[da.getBlock ()]; + // } + // + // @Override + // public boolean isSectorMissing (int block) + // { + // return isMissing[block]; + // } + // + // @Override + // public boolean isSectorMissing (int track, int sector) + // { + // return isMissing[getDiskAddress (track, sector).getBlock ()]; + // } + + // ---------------------------------------------------------------------------------// + @Override + public File getFile () + // ---------------------------------------------------------------------------------// + { + return file; + } + + // ---------------------------------------------------------------------------------// + @Override + public byte[] readSector (DiskAddress da) + // ---------------------------------------------------------------------------------// + { + byte[] buffer = new byte[sectorSize]; + if (da == null) + System.out.println ("Disk address is null"); + else + readBuffer (da, buffer, 0); + return buffer; + } + + // ---------------------------------------------------------------------------------// + @Override + public byte[] readSectors (List daList) + // ---------------------------------------------------------------------------------// + { + byte[] buffer = new byte[daList.size () * sectorSize]; + int ptr = 0; + for (DiskAddress da : daList) + { + // sparse text/PNT/PIC files may have gaps + if (da != null && (da.getBlock () > 0 || ((AppleDiskAddress) da).zeroFlag ())) + readBuffer (da, buffer, ptr); + ptr += sectorSize; + } + return buffer; + } + + // ---------------------------------------------------------------------------------// + @Override + public byte[] readSector (int track, int sector) + // ---------------------------------------------------------------------------------// + { + return readSector (getDiskAddress (track, sector)); + } + + // ---------------------------------------------------------------------------------// + @Override + public byte[] readSector (int block) + // ---------------------------------------------------------------------------------// + { + return readSector (getDiskAddress (block)); + } + + // ---------------------------------------------------------------------------------// + @Override + public void writeSector (DiskAddress da, byte[] buffer) + // ---------------------------------------------------------------------------------// + { + writeBuffer (da, buffer); + } + + // ---------------------------------------------------------------------------------// + @Override + public void setInterleave (int interleave) + // ---------------------------------------------------------------------------------// + { + assert (interleave >= 0 && interleave <= MAX_INTERLEAVE) : "Invalid interleave"; + this.interleave = interleave; + checkSectorsForData (); + if (actionListenerList != null) + notifyListeners ("Interleave changed"); + } + + // ---------------------------------------------------------------------------------// + @Override + public int getInterleave () + // ---------------------------------------------------------------------------------// + { + return interleave; + } + + // ---------------------------------------------------------------------------------// + @Override + public void setBlockSize (int size) + // ---------------------------------------------------------------------------------// + { + assert (size == SECTOR_SIZE || size == BLOCK_SIZE) : "Invalid sector size : " + size; + if (sectorSize == size) + return; + + sectorSize = size; + sectors = trackSize / sectorSize; + blocks = tracks * sectors; + + hasData = new boolean[blocks]; + checkSectorsForData (); + + if (actionListenerList != null) + notifyListeners ("Sector size changed"); + } + + // ---------------------------------------------------------------------------------// + @Override + public DiskAddress getDiskAddress (int track, int sector) + // ---------------------------------------------------------------------------------// + { + if (!isValidAddress (track, sector)) + { + System.out.println ("Invalid block : " + track + "/" + sector); + return null; + // return new AppleDiskAddress (this, 0); this was looping 26/07/2016 + } + return new AppleDiskAddress (this, track, sector); + } + + // ---------------------------------------------------------------------------------// + @Override + public DiskAddress getDiskAddress (int block) + // ---------------------------------------------------------------------------------// + { + if (!isValidAddress (block)) + { + System.out.printf ("getDiskAddress: Invalid block : %d of %d%n", block, + this.blocks); + return null; + // return new AppleDiskAddress (this, 0); // this was looping 26/07/2016 + } + return new AppleDiskAddress (this, block); + } + + // ---------------------------------------------------------------------------------// + @Override + public List getDiskAddressList (int... blocks) + // ---------------------------------------------------------------------------------// + { + List addressList = new ArrayList<> (); + + for (int block : blocks) + { + assert (isValidAddress (block)) : "Invalid block : " + block; + addressList.add (new AppleDiskAddress (this, block)); + } + return addressList; + } + + // ---------------------------------------------------------------------------------// + @Override + public boolean isValidAddress (int block) + // ---------------------------------------------------------------------------------// + { + return block >= 0 && block < this.blocks; + } + + // ---------------------------------------------------------------------------------// + @Override + public boolean isValidAddress (int track, int sector) + // ---------------------------------------------------------------------------------// + { + track &= 0x3F; + sector &= 0x1F; + + if (track < 0 || track >= this.tracks) + return false; + if (sector < 0 || sector >= this.sectors) + return false; + + return true; + } + + // ---------------------------------------------------------------------------------// + @Override + public boolean isValidAddress (DiskAddress da) + // ---------------------------------------------------------------------------------// + { + return da != null && isValidAddress (da.getTrack (), da.getSector ()); + } + + /* + * This is the only method that transfers data from the disk buffer to an output buffer. + * It handles sectors of 256 or 512 bytes, and both linear and interleaved sectors. + */ + // ---------------------------------------------------------------------------------// + private void readBuffer (DiskAddress da, byte[] buffer, int bufferOffset) + // ---------------------------------------------------------------------------------// + { + assert da.getDisk () == this : "Disk address not applicable to this disk"; + assert sectorSize == SECTOR_SIZE + || sectorSize == BLOCK_SIZE : "Invalid sector size : " + sectorSize; + assert interleave >= 0 && interleave <= MAX_INTERLEAVE : "Invalid interleave : " + + interleave; + + if (sectorSize == SECTOR_SIZE) + { + int diskOffset = getBufferOffset (da); + System.arraycopy (diskBuffer, diskOffset, buffer, bufferOffset, SECTOR_SIZE); + } + else + { + int diskOffset = getBufferOffset (da, 0); + System.arraycopy (diskBuffer, diskOffset, buffer, bufferOffset, SECTOR_SIZE); + + diskOffset = getBufferOffset (da, 1); + System.arraycopy (diskBuffer, diskOffset, buffer, bufferOffset + SECTOR_SIZE, + SECTOR_SIZE); + } + } + + // ---------------------------------------------------------------------------------// + private void writeBuffer (DiskAddress da, byte[] buffer) + // ---------------------------------------------------------------------------------// + { + assert da.getDisk () == this : "Disk address not applicable to this disk"; + assert sectorSize == SECTOR_SIZE + || sectorSize == BLOCK_SIZE : "Invalid sector size : " + sectorSize; + assert interleave >= 0 && interleave <= MAX_INTERLEAVE : "Invalid interleave : " + + interleave; + + if (sectorSize == SECTOR_SIZE) + { + int diskOffset = getBufferOffset (da); + System.arraycopy (buffer, 0, diskBuffer, diskOffset, SECTOR_SIZE); + } + else + { + int diskOffset = getBufferOffset (da, 0); + System.arraycopy (buffer, 0, diskBuffer, diskOffset, SECTOR_SIZE); + + diskOffset = getBufferOffset (da, 1); + System.arraycopy (buffer, SECTOR_SIZE, diskBuffer, diskOffset, SECTOR_SIZE); + } + } + + // ---------------------------------------------------------------------------------// + private int getBufferOffset (DiskAddress da) + // ---------------------------------------------------------------------------------// + { + assert sectorSize == SECTOR_SIZE; + + return da.getTrack () * trackSize + + interleaveSector[interleave][da.getSector ()] * SECTOR_SIZE; + } + + // ---------------------------------------------------------------------------------// + private int getBufferOffset (DiskAddress da, int seq) + // ---------------------------------------------------------------------------------// + { + assert sectorSize == BLOCK_SIZE; + + assert seq == 0 || seq == 1; + + return da.getTrack () * trackSize + + interleaveSector[interleave][da.getSector () * 2 + seq] * SECTOR_SIZE; + } + + // ---------------------------------------------------------------------------------// + @Override + public void addActionListener (ActionListener actionListener) + // ---------------------------------------------------------------------------------// + { + actionListenerList = AWTEventMulticaster.add (actionListenerList, actionListener); + } + + // ---------------------------------------------------------------------------------// + @Override + public void removeActionListener (ActionListener actionListener) + // ---------------------------------------------------------------------------------// + { + actionListenerList = AWTEventMulticaster.remove (actionListenerList, actionListener); + } + + // ---------------------------------------------------------------------------------// + public void notifyListeners (String text) + // ---------------------------------------------------------------------------------// + { + if (actionListenerList != null) + actionListenerList + .actionPerformed (new ActionEvent (this, ActionEvent.ACTION_PERFORMED, text)); + } + + // ---------------------------------------------------------------------------------// + public AppleFileSource getDetails () + // ---------------------------------------------------------------------------------// + { + return new DefaultAppleFileSource (toString (), file.getName (), null); + } + + // ---------------------------------------------------------------------------------// + @Override + public String toString () + // ---------------------------------------------------------------------------------// + { + StringBuilder text = new StringBuilder (); + + String path = file.getAbsolutePath (); + String home = System.getProperty ("user.home"); + if (path.startsWith (home)) + path = "~" + path.substring (home.length ()); + + text.append (String.format ("Path................. %s%n", path)); + text.append (String.format ("File name............ %s%n", file.getName ())); + text.append (String.format ("File size............ %,d%n", file.length ())); + text.append (String.format ("Tracks............... %d%n", tracks)); + text.append (String.format ("Sectors.............. %d%n", sectors)); + text.append (String.format ("Blocks............... %,d%n", blocks)); + text.append (String.format ("Track size........... %,d%n", trackSize)); + text.append (String.format ("Sector size.......... %d%n", sectorSize)); + text.append (String.format ("Interleave........... %d", interleave)); + + if (wozFile != null) + { + text.append ("\n\n"); + text.append (wozFile); + } + + return text.toString (); + } + + // ---------------------------------------------------------------------------------// + @Override + public Iterator iterator () + // ---------------------------------------------------------------------------------// + { + if (blockList == null) + { + blockList = new ArrayList<> (blocks); + for (int block = 0; block < blocks; block++) + blockList.add (new AppleDiskAddress (this, block)); + } + + return blockList.iterator (); + } + + // ---------------------------------------------------------------------------------// + @Override + public long getBootChecksum () + // ---------------------------------------------------------------------------------// + { + byte[] buffer = readSector (0, 0); + Checksum checksum = new CRC32 (); + checksum.update (buffer, 0, buffer.length); + return checksum.getValue (); + } + + // ---------------------------------------------------------------------------------// + @Override + public void setEmptyByte (byte value) + // ---------------------------------------------------------------------------------// + { + emptyByte = value; + checkSectorsForData (); + } } \ No newline at end of file diff --git a/src/com/bytezone/diskbrowser/disk/AppleDiskAddress.java b/src/com/bytezone/diskbrowser/disk/AppleDiskAddress.java index c19da14..21796f3 100755 --- a/src/com/bytezone/diskbrowser/disk/AppleDiskAddress.java +++ b/src/com/bytezone/diskbrowser/disk/AppleDiskAddress.java @@ -1,87 +1,109 @@ -package com.bytezone.diskbrowser.disk; - -public class AppleDiskAddress implements DiskAddress -{ - private final int block; - private final int track; - private final int sector; - public final Disk owner; - - private boolean zeroFlag; - - public AppleDiskAddress (Disk owner, int block) - { - this.owner = owner; - this.block = block; - int sectorsPerTrack = owner.getSectorsPerTrack (); - if (sectorsPerTrack == 0) - { - track = 0; - sector = 0; - } - else - { - track = block / sectorsPerTrack; - sector = block % sectorsPerTrack; - } - } - - public AppleDiskAddress (Disk owner, int track, int sector) - { - this.owner = owner; - zeroFlag = (track & 0x40) != 0; - this.track = track & 0x3F; - this.sector = sector & 0x1F; - this.block = this.track * owner.getSectorsPerTrack () + this.sector; - } - - public boolean zeroFlag () - { - return zeroFlag; - } - - @Override - public int compareTo (DiskAddress that) - { - return this.block - that.getBlock (); - } - - @Override - public boolean matches (DiskAddress that) - { - if (that == null) - return false; - return this.block == that.getBlock (); - } - - @Override - public int getBlock () - { - return block; - } - - @Override - public int getSector () - { - return sector; - } - - @Override - public int getTrack () - { - return track; - } - - @Override - public Disk getDisk () - { - return owner; - } - - @Override - public String toString () - { - return String.format ("[Block=%3d, Track=%2d, Sector=%2d, Zero=%s]", block, track, - sector, zeroFlag); - } +package com.bytezone.diskbrowser.disk; + +// -----------------------------------------------------------------------------------// +public class AppleDiskAddress implements DiskAddress +// -----------------------------------------------------------------------------------// +{ + private final int block; + private final int track; + private final int sector; + public final Disk owner; + + private boolean zeroFlag; + + // ---------------------------------------------------------------------------------// + public AppleDiskAddress (Disk owner, int block) + // ---------------------------------------------------------------------------------// + { + this.owner = owner; + this.block = block; + int sectorsPerTrack = owner.getSectorsPerTrack (); + if (sectorsPerTrack == 0) + { + track = 0; + sector = 0; + } + else + { + track = block / sectorsPerTrack; + sector = block % sectorsPerTrack; + } + } + + // ---------------------------------------------------------------------------------// + public AppleDiskAddress (Disk owner, int track, int sector) + // ---------------------------------------------------------------------------------// + { + this.owner = owner; + zeroFlag = (track & 0x40) != 0; + this.track = track & 0x3F; + this.sector = sector & 0x1F; + this.block = this.track * owner.getSectorsPerTrack () + this.sector; + } + + // ---------------------------------------------------------------------------------// + public boolean zeroFlag () + // ---------------------------------------------------------------------------------// + { + return zeroFlag; + } + + // ---------------------------------------------------------------------------------// + @Override + public int compareTo (DiskAddress that) + // ---------------------------------------------------------------------------------// + { + return this.block - that.getBlock (); + } + + // ---------------------------------------------------------------------------------// + @Override + public boolean matches (DiskAddress that) + // ---------------------------------------------------------------------------------// + { + if (that == null) + return false; + return this.block == that.getBlock (); + } + + // ---------------------------------------------------------------------------------// + @Override + public int getBlock () + // ---------------------------------------------------------------------------------// + { + return block; + } + + // ---------------------------------------------------------------------------------// + @Override + public int getSector () + // ---------------------------------------------------------------------------------// + { + return sector; + } + + // ---------------------------------------------------------------------------------// + @Override + public int getTrack () + // ---------------------------------------------------------------------------------// + { + return track; + } + + // ---------------------------------------------------------------------------------// + @Override + public Disk getDisk () + // ---------------------------------------------------------------------------------// + { + return owner; + } + + // ---------------------------------------------------------------------------------// + @Override + public String toString () + // ---------------------------------------------------------------------------------// + { + return String.format ("[Block=%3d, Track=%2d, Sector=%2d, Zero=%s]", block, track, + sector, zeroFlag); + } } \ No newline at end of file diff --git a/src/com/bytezone/diskbrowser/disk/DataDisk.java b/src/com/bytezone/diskbrowser/disk/DataDisk.java index 6394195..d58eea3 100755 --- a/src/com/bytezone/diskbrowser/disk/DataDisk.java +++ b/src/com/bytezone/diskbrowser/disk/DataDisk.java @@ -1,53 +1,63 @@ -package com.bytezone.diskbrowser.disk; - -import java.util.ArrayList; -import java.util.List; - -import com.bytezone.diskbrowser.gui.DataSource; - -public class DataDisk extends AbstractFormattedDisk -{ - // static final byte[] dos = { 0x01, (byte) 0xA5, 0x27, (byte) 0xC9, 0x09 }; - - // this should somehow tie in with the checksum from DiskFactory to determine - // whether it has a bootloader - - public DataDisk (AppleDisk disk) - { - super (disk); - - // byte[] buffer = disk.readSector (0, 0); // Boot sector - // boolean ok = true; - // for (int i = 0; i < dos.length; i++) - // if (buffer[i] != dos[i]) - // { - // ok = false; - // break; - // } - // if (buffer[0] == 0x01) - // { - // bootSector = new BootSector (disk, buffer, "DOS"); - // sectorTypesList.add (dosSector); - // sectorTypes[0] = dosSector; - // } - } - - // no files on data disks - @Override - public List getFileSectors (int fileNo) - { - return new ArrayList<> (); - } - - // no files on data disks - public DataSource getFile (int fileNo) - { - return null; - } - - @Override - public String toString () - { - return disk.toString (); - } +package com.bytezone.diskbrowser.disk; + +import java.util.ArrayList; +import java.util.List; + +import com.bytezone.diskbrowser.gui.DataSource; + +// -----------------------------------------------------------------------------------// +public class DataDisk extends AbstractFormattedDisk +// -----------------------------------------------------------------------------------// +{ + // static final byte[] dos = { 0x01, (byte) 0xA5, 0x27, (byte) 0xC9, 0x09 }; + + // this should somehow tie in with the checksum from DiskFactory to determine + // whether it has a bootloader + + // ---------------------------------------------------------------------------------// + public DataDisk (AppleDisk disk) + // ---------------------------------------------------------------------------------// + { + super (disk); + + // byte[] buffer = disk.readSector (0, 0); // Boot sector + // boolean ok = true; + // for (int i = 0; i < dos.length; i++) + // if (buffer[i] != dos[i]) + // { + // ok = false; + // break; + // } + // if (buffer[0] == 0x01) + // { + // bootSector = new BootSector (disk, buffer, "DOS"); + // sectorTypesList.add (dosSector); + // sectorTypes[0] = dosSector; + // } + } + + // no files on data disks + // ---------------------------------------------------------------------------------// + @Override + public List getFileSectors (int fileNo) + // ---------------------------------------------------------------------------------// + { + return new ArrayList<> (); + } + + // no files on data disks + // ---------------------------------------------------------------------------------// + public DataSource getFile (int fileNo) + // ---------------------------------------------------------------------------------// + { + return null; + } + + // ---------------------------------------------------------------------------------// + @Override + public String toString () + // ---------------------------------------------------------------------------------// + { + return disk.toString (); + } } \ No newline at end of file diff --git a/src/com/bytezone/diskbrowser/disk/DefaultAppleFileSource.java b/src/com/bytezone/diskbrowser/disk/DefaultAppleFileSource.java index def7d2f..7c9d0d4 100755 --- a/src/com/bytezone/diskbrowser/disk/DefaultAppleFileSource.java +++ b/src/com/bytezone/diskbrowser/disk/DefaultAppleFileSource.java @@ -1,101 +1,125 @@ -package com.bytezone.diskbrowser.disk; - -import java.util.List; - -import com.bytezone.diskbrowser.applefile.AppleFileSource; -import com.bytezone.diskbrowser.gui.DataSource; - -/* - * Most AppleFileSource objects are CatalogEntry types. In order to allow Disk - * and Volume nodes in the tree to show some text in the centre panel, use a - * DefaultAppleFileSource which returns a DefaultDataSource (just some text). - */ -public class DefaultAppleFileSource implements AppleFileSource -{ - final String title; - final DataSource file; - final FormattedDisk owner; - List blocks; - - public DefaultAppleFileSource (String text, FormattedDisk owner) - { - this ("", text, owner); - } - - public DefaultAppleFileSource (String title, String text, FormattedDisk owner) - { - this (title, new DefaultDataSource (text), owner); - } - - public DefaultAppleFileSource (String title, DataSource file, FormattedDisk owner) - { - this.title = title; - this.file = file; - this.owner = owner; - } - - public DefaultAppleFileSource (String title, DataSource file, FormattedDisk owner, - List blocks) - { - this (title, file, owner); - this.blocks = blocks; - if (file instanceof DefaultDataSource) - ((DefaultDataSource) file).buffer = owner.getDisk ().readSectors (blocks); - } - - public void setSectors (List blocks) - { - this.blocks = blocks; - if (file instanceof DefaultDataSource) - ((DefaultDataSource) file).buffer = owner.getDisk ().readSectors (blocks); - } - - @Override - public DataSource getDataSource () - { - return file; - } - - @Override - public FormattedDisk getFormattedDisk () - { - return owner; - } - - @Override - public List getSectors () - { - return blocks; - } - - /* - * See similar routine in CatalogPanel.DiskNode - */ - @Override - public String toString () - { - final int MAX_NAME_LENGTH = 40; - final int SUFFIX_LENGTH = 12; - final int PREFIX_LENGTH = MAX_NAME_LENGTH - SUFFIX_LENGTH - 3; - - if (title.length () > MAX_NAME_LENGTH) - return title.substring (0, PREFIX_LENGTH) + "..." - + title.substring (title.length () - SUFFIX_LENGTH); - return title; - } - - @Override - public String getUniqueName () - { - return title; - } - - @Override - public boolean contains (DiskAddress diskAddress) - { - for (DiskAddress da : blocks) - if (da.matches (diskAddress)) - return true; - return false; - } +package com.bytezone.diskbrowser.disk; + +import java.util.List; + +import com.bytezone.diskbrowser.applefile.AppleFileSource; +import com.bytezone.diskbrowser.gui.DataSource; + +/* + * Most AppleFileSource objects are CatalogEntry types. In order to allow Disk + * and Volume nodes in the tree to show some text in the centre panel, use a + * DefaultAppleFileSource which returns a DefaultDataSource (just some text). + */ +// -----------------------------------------------------------------------------------// +public class DefaultAppleFileSource implements AppleFileSource +// -----------------------------------------------------------------------------------// +{ + final String title; + final DataSource file; + final FormattedDisk owner; + List blocks; + + // ---------------------------------------------------------------------------------// + public DefaultAppleFileSource (String text, FormattedDisk owner) + // ---------------------------------------------------------------------------------// + { + this ("", text, owner); + } + + // ---------------------------------------------------------------------------------// + public DefaultAppleFileSource (String title, String text, FormattedDisk owner) + // ---------------------------------------------------------------------------------// + { + this (title, new DefaultDataSource (text), owner); + } + + // ---------------------------------------------------------------------------------// + public DefaultAppleFileSource (String title, DataSource file, FormattedDisk owner) + // ---------------------------------------------------------------------------------// + { + this.title = title; + this.file = file; + this.owner = owner; + } + + // ---------------------------------------------------------------------------------// + public DefaultAppleFileSource (String title, DataSource file, FormattedDisk owner, + List blocks) + // ---------------------------------------------------------------------------------// + { + this (title, file, owner); + this.blocks = blocks; + if (file instanceof DefaultDataSource) + ((DefaultDataSource) file).buffer = owner.getDisk ().readSectors (blocks); + } + + // ---------------------------------------------------------------------------------// + public void setSectors (List blocks) + // ---------------------------------------------------------------------------------// + { + this.blocks = blocks; + if (file instanceof DefaultDataSource) + ((DefaultDataSource) file).buffer = owner.getDisk ().readSectors (blocks); + } + + // ---------------------------------------------------------------------------------// + @Override + public DataSource getDataSource () + // ---------------------------------------------------------------------------------// + { + return file; + } + + // ---------------------------------------------------------------------------------// + @Override + public FormattedDisk getFormattedDisk () + // ---------------------------------------------------------------------------------// + { + return owner; + } + + // ---------------------------------------------------------------------------------// + @Override + public List getSectors () + // ---------------------------------------------------------------------------------// + { + return blocks; + } + + /* + * See similar routine in CatalogPanel.DiskNode + */ + // ---------------------------------------------------------------------------------// + @Override + public String toString () + // ---------------------------------------------------------------------------------// + { + final int MAX_NAME_LENGTH = 40; + final int SUFFIX_LENGTH = 12; + final int PREFIX_LENGTH = MAX_NAME_LENGTH - SUFFIX_LENGTH - 3; + + if (title.length () > MAX_NAME_LENGTH) + return title.substring (0, PREFIX_LENGTH) + "..." + + title.substring (title.length () - SUFFIX_LENGTH); + return title; + } + + // ---------------------------------------------------------------------------------// + @Override + public String getUniqueName () + // ---------------------------------------------------------------------------------// + { + return title; + } + + // ---------------------------------------------------------------------------------// + @Override + public boolean contains (DiskAddress diskAddress) + // ---------------------------------------------------------------------------------// + { + for (DiskAddress da : blocks) + if (da.matches (diskAddress)) + return true; + return false; + } } \ No newline at end of file diff --git a/src/com/bytezone/diskbrowser/disk/DefaultDataSource.java b/src/com/bytezone/diskbrowser/disk/DefaultDataSource.java index 1bfafcf..07e1002 100755 --- a/src/com/bytezone/diskbrowser/disk/DefaultDataSource.java +++ b/src/com/bytezone/diskbrowser/disk/DefaultDataSource.java @@ -1,49 +1,68 @@ -package com.bytezone.diskbrowser.disk; - -import java.awt.image.BufferedImage; - -import javax.swing.JComponent; -import javax.swing.JPanel; - -import com.bytezone.diskbrowser.gui.DataSource; -import com.bytezone.diskbrowser.utilities.HexFormatter; - -public class DefaultDataSource implements DataSource -{ - public String text; - byte[] buffer; - - public DefaultDataSource (String text) - { - this.text = text; - } - - public String getAssembler () - { - return null; - } - - public String getHexDump () - { - if (buffer != null) - return HexFormatter.format (buffer, 0, buffer.length); - return null; - } - - public BufferedImage getImage () - { - return null; - } - - public String getText () - { - return text; - } - - public JComponent getComponent () - { - System.out.println ("In DefaultDataSource.getComponent()"); - JPanel panel = new JPanel (); - return panel; - } +package com.bytezone.diskbrowser.disk; + +import java.awt.image.BufferedImage; + +import javax.swing.JComponent; +import javax.swing.JPanel; + +import com.bytezone.diskbrowser.gui.DataSource; +import com.bytezone.diskbrowser.utilities.HexFormatter; + +// -----------------------------------------------------------------------------------// +public class DefaultDataSource implements DataSource +// -----------------------------------------------------------------------------------// +{ + public String text; + byte[] buffer; + + // ---------------------------------------------------------------------------------// + public DefaultDataSource (String text) + // ---------------------------------------------------------------------------------// + { + this.text = text; + } + + // ---------------------------------------------------------------------------------// + @Override + public String getAssembler () + // ---------------------------------------------------------------------------------// + { + return null; + } + + // ---------------------------------------------------------------------------------// + @Override + public String getHexDump () + // ---------------------------------------------------------------------------------// + { + if (buffer != null) + return HexFormatter.format (buffer, 0, buffer.length); + return null; + } + + // ---------------------------------------------------------------------------------// + @Override + public BufferedImage getImage () + // ---------------------------------------------------------------------------------// + { + return null; + } + + // ---------------------------------------------------------------------------------// + @Override + public String getText () + // ---------------------------------------------------------------------------------// + { + return text; + } + + // ---------------------------------------------------------------------------------// + @Override + public JComponent getComponent () + // ---------------------------------------------------------------------------------// + { + System.out.println ("In DefaultDataSource.getComponent()"); + JPanel panel = new JPanel (); + return panel; + } } \ No newline at end of file diff --git a/src/com/bytezone/diskbrowser/disk/DefaultSector.java b/src/com/bytezone/diskbrowser/disk/DefaultSector.java index 3ba9c8e..57b26ba 100755 --- a/src/com/bytezone/diskbrowser/disk/DefaultSector.java +++ b/src/com/bytezone/diskbrowser/disk/DefaultSector.java @@ -1,26 +1,34 @@ -package com.bytezone.diskbrowser.disk; - -import com.bytezone.diskbrowser.utilities.HexFormatter; - -public class DefaultSector extends AbstractSector -{ - String name; - - public DefaultSector (String name, Disk disk, byte[] buffer, DiskAddress diskAddress) - { - super (disk, buffer, diskAddress); - this.name = name; - } - - @Override - public String createText () - { - return name + "\n\n" + HexFormatter.format (buffer, 0, buffer.length); - } - - @Override - public String toString () - { - return name; - } -} \ No newline at end of file +package com.bytezone.diskbrowser.disk; + +import com.bytezone.diskbrowser.utilities.HexFormatter; + +// -----------------------------------------------------------------------------------// +public class DefaultSector extends AbstractSector +// -----------------------------------------------------------------------------------// +{ + String name; + + // ---------------------------------------------------------------------------------// + public DefaultSector (String name, Disk disk, byte[] buffer, DiskAddress diskAddress) + // ---------------------------------------------------------------------------------// + { + super (disk, buffer, diskAddress); + this.name = name; + } + + // ---------------------------------------------------------------------------------// + @Override + public String createText () + // ---------------------------------------------------------------------------------// + { + return name + "\n\n" + HexFormatter.format (buffer, 0, buffer.length); + } + + // ---------------------------------------------------------------------------------// + @Override + public String toString () + // ---------------------------------------------------------------------------------// + { + return name; + } +} diff --git a/src/com/bytezone/diskbrowser/disk/Disk.java b/src/com/bytezone/diskbrowser/disk/Disk.java index e67516d..d66406b 100755 --- a/src/com/bytezone/diskbrowser/disk/Disk.java +++ b/src/com/bytezone/diskbrowser/disk/Disk.java @@ -1,62 +1,64 @@ -package com.bytezone.diskbrowser.disk; - -import java.awt.event.ActionListener; -import java.io.File; -import java.util.List; - -public interface Disk extends Iterable -{ - public long getBootChecksum (); - - public void setEmptyByte (byte value); - - public int getTotalBlocks (); // blocks per disk - usually 560 or 280 - - public int getTotalTracks (); // usually 35 - - public int getBlockSize (); // bytes per block - 256 or 512 - - public void setBlockSize (int blockSize); - - public int getTrackSize (); // bytes per track - 4096 - - public int getSectorsPerTrack (); // 8 or 16 - - public void setInterleave (int interleave); - - public int getInterleave (); - - public DiskAddress getDiskAddress (int block); - - public List getDiskAddressList (int... blocks); - - public DiskAddress getDiskAddress (int track, int sector); - - public byte[] readSector (int block); - - public byte[] readSector (int track, int sector); - - public byte[] readSector (DiskAddress da); - - public byte[] readSectors (List daList); - - public void writeSector (DiskAddress da, byte[] buffer); - - public boolean isSectorEmpty (int block); - - public boolean isSectorEmpty (int track, int sector); - - public boolean isSectorEmpty (DiskAddress da); - - public boolean isValidAddress (int block); - - public boolean isValidAddress (int track, int sector); - - public boolean isValidAddress (DiskAddress da); - - public File getFile (); - - public void addActionListener (ActionListener listener); - - public void removeActionListener (ActionListener listener); +package com.bytezone.diskbrowser.disk; + +import java.awt.event.ActionListener; +import java.io.File; +import java.util.List; + +// -----------------------------------------------------------------------------------// +public interface Disk extends Iterable +// -----------------------------------------------------------------------------------// +{ + public long getBootChecksum (); + + public void setEmptyByte (byte value); + + public int getTotalBlocks (); // blocks per disk - usually 560 or 280 + + public int getTotalTracks (); // usually 35 + + public int getBlockSize (); // bytes per block - 256 or 512 + + public void setBlockSize (int blockSize); + + public int getTrackSize (); // bytes per track - 4096 + + public int getSectorsPerTrack (); // 8 or 16 + + public void setInterleave (int interleave); + + public int getInterleave (); + + public DiskAddress getDiskAddress (int block); + + public List getDiskAddressList (int... blocks); + + public DiskAddress getDiskAddress (int track, int sector); + + public byte[] readSector (int block); + + public byte[] readSector (int track, int sector); + + public byte[] readSector (DiskAddress da); + + public byte[] readSectors (List daList); + + public void writeSector (DiskAddress da, byte[] buffer); + + public boolean isSectorEmpty (int block); + + public boolean isSectorEmpty (int track, int sector); + + public boolean isSectorEmpty (DiskAddress da); + + public boolean isValidAddress (int block); + + public boolean isValidAddress (int track, int sector); + + public boolean isValidAddress (DiskAddress da); + + public File getFile (); + + public void addActionListener (ActionListener listener); + + public void removeActionListener (ActionListener listener); } \ No newline at end of file diff --git a/src/com/bytezone/diskbrowser/disk/DiskAddress.java b/src/com/bytezone/diskbrowser/disk/DiskAddress.java index 0b91398..b6438f1 100755 --- a/src/com/bytezone/diskbrowser/disk/DiskAddress.java +++ b/src/com/bytezone/diskbrowser/disk/DiskAddress.java @@ -1,14 +1,16 @@ -package com.bytezone.diskbrowser.disk; - -public interface DiskAddress extends Comparable -{ - public int getBlock (); - - public int getTrack (); - - public int getSector (); - - public Disk getDisk (); - - public boolean matches (DiskAddress other); +package com.bytezone.diskbrowser.disk; + +// -----------------------------------------------------------------------------------// +public interface DiskAddress extends Comparable +// -----------------------------------------------------------------------------------// +{ + public int getBlock (); + + public int getTrack (); + + public int getSector (); + + public Disk getDisk (); + + public boolean matches (DiskAddress other); } \ No newline at end of file diff --git a/src/com/bytezone/diskbrowser/disk/DiskFactory.java b/src/com/bytezone/diskbrowser/disk/DiskFactory.java index 255f867..e2458c1 100755 --- a/src/com/bytezone/diskbrowser/disk/DiskFactory.java +++ b/src/com/bytezone/diskbrowser/disk/DiskFactory.java @@ -1,783 +1,804 @@ -package com.bytezone.diskbrowser.disk; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Enumeration; -import java.util.zip.GZIPInputStream; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -import com.bytezone.diskbrowser.cpm.CPMDisk; -import com.bytezone.diskbrowser.dos.DosDisk; -import com.bytezone.diskbrowser.infocom.InfocomDisk; -import com.bytezone.diskbrowser.nib.NibFile; -import com.bytezone.diskbrowser.nib.V2dFile; -import com.bytezone.diskbrowser.nib.WozFile; -import com.bytezone.diskbrowser.pascal.PascalDisk; -import com.bytezone.diskbrowser.prodos.ProdosDisk; -import com.bytezone.diskbrowser.utilities.FileFormatException; -import com.bytezone.diskbrowser.utilities.NuFX; -import com.bytezone.diskbrowser.utilities.Utility; -import com.bytezone.diskbrowser.wizardry.Wizardry4BootDisk; -import com.bytezone.diskbrowser.wizardry.WizardryScenarioDisk; - -public class DiskFactory -{ - private static boolean debug = false; - - private DiskFactory () - { - } - - public static FormattedDisk createDisk (File file) - { - return createDisk (file.getAbsolutePath ()); - } - - public static FormattedDisk createDisk (String path) - { - if (debug) - System.out.println ("\nFactory : " + path); - - File file = new File (path); - if (!file.exists ()) - return null; - - String suffix = path.substring (path.lastIndexOf (".") + 1).toLowerCase (); - Boolean compressed = false; - Path originalPath = Paths.get (path); - - if ("gz".equals (suffix)) - { - if (debug) - System.out.println (" ** gzip **"); - try - { - InputStream in = new GZIPInputStream (new FileInputStream (path)); - File tmp = File.createTempFile ("gzip", null); - FileOutputStream fos = new FileOutputStream (tmp); - - int bytesRead; - byte[] buffer = new byte[1024]; - while ((bytesRead = in.read (buffer)) > 0) - fos.write (buffer, 0, bytesRead); - - fos.close (); - in.close (); - tmp.deleteOnExit (); - - suffix = Utility.getSuffix (file.getName ()); // ignores the .gz and .zip - file = tmp; - compressed = true; - } - catch (IOException e) // can get EOFException: Unexpected end of ZLIB input stream - { - e.printStackTrace (); - return null; - } - } - else if ("zip".equals (suffix)) - { - if (debug) - System.out.println (" ** zip **"); - try - { - ZipFile zipFile = new ZipFile (path); - Enumeration entries = zipFile.entries (); - - while (entries.hasMoreElements ()) // loop until first valid name - { - ZipEntry entry = entries.nextElement (); - if (Utility.validFileType (entry.getName ())) - { - InputStream stream = zipFile.getInputStream (entry); - File tmp = File.createTempFile ("zip", null); - FileOutputStream fos = new FileOutputStream (tmp); - - int bytesRead; - byte[] buffer = new byte[1024]; - while ((bytesRead = stream.read (buffer)) > 0) - fos.write (buffer, 0, bytesRead); - - stream.close (); - fos.close (); - tmp.deleteOnExit (); - - suffix = Utility.getSuffix (file.getName ()); // ignores the .gz and .zip - file = tmp; - compressed = true; - - break; - } - } - - zipFile.close (); - } - catch (IOException e) - { - e.printStackTrace (); - return null; - } - } - - if (suffix.equals ("sdk")) - { - if (debug) - System.out.println (" ** sdk **"); - try - { - NuFX nuFX = new NuFX (file); - File tmp = File.createTempFile ("sdk", null); - FileOutputStream fos = new FileOutputStream (tmp); - fos.write (nuFX.getBuffer ()); - fos.close (); - tmp.deleteOnExit (); - file = tmp; - suffix = "dsk"; - compressed = true; - } - catch (IOException e) - { - e.printStackTrace (); - return null; - } - catch (FileFormatException e) - { - return null; - } - } - - FormattedDisk disk = null; - FormattedDisk disk2 = null; - - if (suffix.equals ("hdv")) - { - if (debug) - System.out.println (" ** hdv **"); - FormattedDisk prodosDisk = checkHardDisk (file); - if (prodosDisk != null) - return prodosDisk; - - disk2 = check2mgDisk (file); - if (disk2 != null) - { - if (compressed) - disk2.setOriginalPath (originalPath); - return disk2; - } - - AppleDisk appleDisk = new AppleDisk (file, (int) file.length () / 4096, 8); - return new DataDisk (appleDisk); - } - - if (suffix.equals ("2mg")) - { - if (debug) - System.out.println (" ** 2mg **"); - disk2 = check2mgDisk (file); - if (disk2 != null) - { - if (compressed) - disk2.setOriginalPath (originalPath); - return disk2; - } - - AppleDisk appleDisk = new AppleDisk (file, (int) file.length () / 4096, 8); - return new DataDisk (appleDisk); - } - - if (((suffix.equals ("po") || suffix.equals ("dsk")) && file.length () > 143360)) - { - if (file.length () < 143500) // slightly bigger than a floppy - { - System.out.println ("File length is wrong: " + file.length ()); - disk = checkDos (new AppleDisk (file, 35, 16)); - if (disk != null) - return disk; - } - - if (debug) - System.out.printf (" Checking po or dsk hard drive: %,d%n", file.length ()); - - disk = checkHardDisk (file); - if (disk != null) - { - if (compressed) - disk.setOriginalPath (originalPath); - return disk; - } - - if (file.length () == 819200) // 800K 3.5" - { - if (debug) - System.out.println ("UniDos ?"); - // 2 x 400k disk images - AppleDisk appleDisk1 = new AppleDisk (file, 50, 32); - AppleDisk appleDisk2 = new AppleDisk (file, 50, 32, (int) (file.length () / 2)); - disk = checkUnidos (appleDisk1, 1); - disk2 = checkUnidos (appleDisk2, 2); - if (disk != null && disk2 != null) - return new DualDosDisk (disk, disk2); - } - - if (debug) - System.out.println (" Creating a data disk from bad length"); - - try - { - AppleDisk appleDisk = new AppleDisk (file, (int) file.length () / 4096, 8); - if (debug) - System.out.println (" created data disk"); - return new DataDisk (appleDisk); - } - catch (FileFormatException e) - { - if (debug) - System.out.println (" Creating AppleDisk failed"); - return null; - } - } - - if (suffix.equals ("woz")) - { - if (debug) - System.out.println ("Checking woz"); - try - { - WozFile wozFile = new WozFile (file); - - if (wozFile.getSectorsPerTrack () == 13) - { - AppleDisk appleDisk = new AppleDisk (wozFile, 35, 13); - disk = checkDos (appleDisk); - return disk == null ? new DataDisk (appleDisk) : disk; - } - - if (wozFile.getSectorsPerTrack () == 16) - { - if (wozFile.getDiskType () == 2) - { - if (debug) - System.out.println ("Checking woz 3.5"); - AppleDisk disk800 = new AppleDisk (wozFile, 100 * wozFile.getSides (), 8); - if (ProdosDisk.isCorrectFormat (disk800)) - { - if (debug) - System.out.println (" --> PRODOS hard disk"); - return new ProdosDisk (disk800); - } - disk = new DataDisk (disk800); - } - else - { - AppleDisk appleDisk256 = new AppleDisk (wozFile, wozFile.getTracks (), 16); - disk = checkDos (appleDisk256); - if (disk == null) - disk = checkProdos (new AppleDisk (wozFile, 35, 8)); - if (disk == null) - disk = new DataDisk (appleDisk256); - } - } - - return disk; - } - catch (Exception e) - { - System.out.println (e); - return null; - } - } - - if (suffix.equals ("v2d")) - { - V2dFile v2dDisk = new V2dFile (file); - AppleDisk appleDisk256 = new AppleDisk (v2dDisk, 35, 16); - disk = checkDos (appleDisk256); - if (disk == null) - disk = checkProdos (new AppleDisk (v2dDisk, 35, 8)); - if (disk == null) - disk = new DataDisk (appleDisk256); - return disk; - } - - if (suffix.equals ("nib")) // not implemented yet - { - if (debug) - System.out.println (" ** nib **"); - - NibFile nibDisk = new NibFile (file); - AppleDisk appleDisk16 = new AppleDisk (nibDisk); - disk = checkDos (appleDisk16); - return null; - } - - long length = file.length (); - - if (length == 116480) // 13 sector disk - { - if (debug) - System.out.println (" ** 13 sector **"); - if (!suffix.equals ("d13")) - System.out.printf ("%s should have a d13 suffix%n", file.getName ()); - - AppleDisk appleDisk = new AppleDisk (file, 35, 13); - disk = checkDos (appleDisk); - return disk == null ? new DataDisk (appleDisk) : disk; - } - - if (length != 143360) - { - System.out.printf ("%s: invalid file length : %,d%n", file.getName (), - file.length ()); - return null; - } - - AppleDisk appleDisk256 = new AppleDisk (file, 35, 16); - AppleDisk appleDisk512 = new AppleDisk (file, 35, 8); - - if (true) - { - long checksum = appleDisk256.getBootChecksum (); - - if (checksum == 227968344L) // empty boot sector - { - // could be wizardry data, visialc data ... - if (debug) - System.out.println (" empty sector checksum : " + checksum); - } - else if (checksum == 3176296590L // - || checksum == 108825457L // - || checksum == 1439356606L // - || checksum == 1550012074L // - || checksum == 1614602459L // - || checksum == 940889336L // - || checksum == 2936955085L // - || checksum == 1348415927L // - || checksum == 3340889101L // - || checksum == 18315788L // - || checksum == 993895235L // - || checksum == 2378342794L) // LazerPascal1.dsk - { - if (debug) - System.out.println (" known DOS checksum : " + checksum); - disk = checkDos (appleDisk256); - disk2 = checkProdos (appleDisk512); // no need for this - if (disk2 != null && disk != null) // should be impossible - { - if (debug) - System.out.println (" --> Dual dos/prodos 1"); - System.out.println ("** impossible **"); - disk = new DualDosDisk (disk, disk2); - } - } - else if (checksum == 1737448647L // - || checksum == 170399908L // - || checksum == 990032697) // Apple Assembly Line - { - if (debug) - System.out.println (" known PRODOS checksum : " + checksum); - disk = checkProdos (appleDisk512); - disk2 = checkDos (appleDisk256); - if (disk2 != null && disk != null) - { - if (debug) - System.out.println (" --> Dual prodos/dos 2"); - disk = new DualDosDisk (disk, disk2); - } - } - else if (checksum == 2803644711L // Apple Pascal disk 0 - || checksum == 3317783349L // - || checksum == 1728863694L // Wizardry_I_boot.dsk - || checksum == 198094178L) // - { - if (debug) - System.out.println (" known PASCAL checksum : " + checksum); - disk = checkPascalDisk (appleDisk512); - disk2 = checkDos (appleDisk256); - if (disk2 != null) - disk = new DualDosDisk (disk, disk2); - } - else if (checksum == 3028642627L // - || checksum == 2070151659L) // Enchanter - { - if (debug) - System.out.println (" known INFOCOM checksum : " + checksum); - disk = checkInfocomDisk (appleDisk256); - } - else if (debug) - System.out.println (" unknown checksum : " + checksum); - - // else if (checksum == 1212926910L || checksum == 1365043894L - // || checksum == 2128073918L) - // disk = checkCPMDisk (file); - - // System.out.println (checksum); - - if (disk != null) - { - if (compressed) - disk.setOriginalPath (originalPath); - return disk; - } - - // empty boot sector - if (checksum != 227968344L && false) - System.out.println ("Unknown checksum : " + checksum + " : " + path); - } - - if (debug) - System.out.println (" checksum no help"); - if (debug) - System.out.println (" Suffix : " + suffix); - - if (suffix.equals ("dsk") || suffix.equals ("do")) - { - disk = checkDos (appleDisk256); - if (disk == null) - disk = checkProdos (appleDisk512); - else - { - if (debug) - System.out.println ("Checking DualDos disk"); - - disk2 = checkProdos (appleDisk512); - if (disk2 != null) - disk = new DualDosDisk (disk, disk2); - - AppleDisk appleDisk = new AppleDisk (file, 35, 16); - disk2 = checkCPMDisk (appleDisk); - if (disk2 != null) - disk = new DualDosDisk (disk, disk2); - } - } - else if (suffix.equals ("po")) - { - disk = checkProdos (appleDisk512); - if (disk == null) - disk = checkDos (appleDisk256); - } - - if (disk == null) - disk = checkPascalDisk (appleDisk512); - - if (disk == null) - disk = checkCPMDisk (appleDisk256); - - if (disk == null) - { - disk2 = checkInfocomDisk (appleDisk256); - if (disk2 != null) - disk = disk2; - } - - if (disk == null) - disk = new DataDisk (appleDisk256); - - if (debug) - System.out.println ( - "Factory creating disk : " + disk.getDisk ().getFile ().getAbsolutePath ()); - - if (disk != null && compressed) - disk.setOriginalPath (originalPath); - - return disk; - } - - private static DosDisk checkDos (AppleDisk disk) - { - if (debug) - System.out.println ("Checking DOS disk"); - - try - { - if (DosDisk.isCorrectFormat (disk)) - { - if (debug) - System.out.println (" --> DOS"); - return new DosDisk (disk); - } - } - catch (Exception e) - { - e.printStackTrace (); - } - if (debug) - System.out.println (" not a DOS disk"); - return null; - } - - private static ProdosDisk checkProdos (AppleDisk disk) - { - if (debug) - System.out.println ("Checking Prodos disk"); - - try - { - if (ProdosDisk.isCorrectFormat (disk)) - { - if (debug) - System.out.println (" --> PRODOS"); - return new ProdosDisk (disk); - } - } - catch (Exception e) - { - } - if (debug) - System.out.println (" not a Prodos disk"); - return null; - } - - private static DosDisk checkUnidos (AppleDisk disk, int side) - { - if (debug) - System.out.println ("Checking UniDOS disk"); - - try - { - if (DosDisk.isCorrectFormat (disk)) - { - if (debug) - System.out.println (" --> UniDOS"); - return new DosDisk (disk, side); - } - } - catch (Exception e) - { - e.printStackTrace (); - } - if (debug) - System.out.println (" not a UniDOS disk"); - return null; - } - - private static FormattedDisk checkHardDisk (File file) - { - if (debug) - { - System.out.println ("\nChecking Prodos hard disk"); - System.out.printf ("Total blocks : %f%n", (float) file.length () / 512); - System.out.printf ("Total tracks : %f%n", (float) file.length () / 4096); - System.out.printf ("File length : %d%n", file.length ()); - System.out.println (); - } - - // assumes a sector is 512 bytes - if ((file.length () % 512) != 0) - { - if (debug) - System.out.printf ("file length not divisible by 512 : %,d%n%n", file.length ()); - return null; - } - - try - { - // extend the file if necessary - int tracks = (int) (file.length () - 1) / 4096 + 1; - if (tracks * 4096 != file.length ()) - { - System.out.println ("*** extended ***"); // System Addons.hdv - // System.out.println (tracks); - } - AppleDisk disk = new AppleDisk (file, tracks, 8); - if (ProdosDisk.isCorrectFormat (disk)) - { - if (debug) - System.out.println (" --> PRODOS hard disk"); - return new ProdosDisk (disk); - } - if (PascalDisk.isCorrectFormat (disk, debug)) - { - if (debug) - System.out.println (" --> Pascal hard disk"); - return new PascalDisk (disk); - } - } - catch (Exception e) - { - System.out.println (e); - System.out.println ("Prodos hard disk had error"); - } - - if (debug) - System.out.println (" not a Prodos hard disk\n"); - - return null; - } - - /* - offset | size | description - ------ | ---- | ----------- - +$000 | Long | The integer constant '2IMG'. This integer should be little-endian, - so on the Apple IIgs, this is equivalent to the four characters - 'GMI2'; in ORCA/C 2.1, you can use the integer constant '2IMG'. - +$004 | Long | A four-character tag identifying the application that created the - file. - +$008 | Word | The length of this header, in bytes. Should be 52. - +$00A | Word | The version number of the image file format. Should be 1. - +$00C | Long | The image format. See table below. - +$010 | Long | Flags. See table below. - +$014 | Long | The number of 512-byte blocks in the disk image. This value should - be zero unless the image format is 1 (ProDOS order). - +$018 | Long | Offset to the first byte of the first block of the disk in the image - file, from the beginning of the file. The disk data must come before - the comment and creator-specific chunks. - +$01C | Long | Length of the disk data in bytes. This should be the number of - blocks * 512. - +$020 | Long | Offset to the first byte of the image comment. Can be zero if - there's no comment. The comment must come after the data chunk, but - before the creator-specific chunk. The comment, if it exists, should - be raw text; no length byte or C-style null terminator byte is - required (that's what the next field is for). - +$024 | Long | Length of the comment chunk. Zero if there's no comment. - +$028 | Long | Offset to the first byte of the creator-specific data chunk, or zero - if there is none. - +$02C | Long | Length of the creator-specific chunk; zero if there is no - creator-specific data. - +$030 | 16 bytes | Reserved space; this pads the header to 64 bytes. These values - must all be zero. - */ - - private static FormattedDisk check2mgDisk (File file) - { - if (debug) - System.out.println ("Checking 2mg disk"); - - try - { - AppleDisk disk = new AppleDisk (file, 0, 0); - if (disk.getTotalBlocks () > 0 && ProdosDisk.isCorrectFormat (disk)) - return new ProdosDisk (disk); - // should check for DOS, but AppleDisk assumes 2mg has 512 byte blocks - } - catch (Exception e) - { - e.printStackTrace (); - // System.out.println (e); - } - if (debug) - System.out.println ("Not a Prodos 2mg disk"); - - return null; - } - - private static FormattedDisk checkPascalDisk (AppleDisk disk) - { - if (debug) - System.out.println ("Checking Pascal disk"); - - File file = disk.getFile (); - - if (!PascalDisk.isCorrectFormat (disk, debug)) - return null; - - if (debug) - System.out.println ("Pascal disk OK - Checking Wizardry disk"); - - if (WizardryScenarioDisk.isWizardryFormat (disk, debug)) - return new WizardryScenarioDisk (disk); - - if (debug) - System.out.println ("Not a Wizardry 1-3 disk"); - - // check for compressed disk - if (file.getName ().endsWith (".tmp")) - return new PascalDisk (disk); // complicated joining up compressed disks - - if (Wizardry4BootDisk.isWizardryIVorV (disk, debug)) - { - String fileName = file.getAbsolutePath ().toLowerCase (); - int pos = file.getAbsolutePath ().indexOf ('.'); - char c = fileName.charAt (pos - 1); - // String suffix = fileName.substring (pos + 1); - int requiredDisks = c == '1' ? 6 : c == 'a' ? 10 : 0; - - if (requiredDisks > 0) - { - // collect extra data disks - AppleDisk[] disks = new AppleDisk[requiredDisks]; - - disks[0] = new AppleDisk (file, 256, 8); // will become a PascalDisk - disks[0].setInterleave (1); - - disks[1] = new AppleDisk (file, 256, 8); // will remain a DataDisk - disks[1].setInterleave (1); - - if (pos > 0 && requiredDisks > 0) - { - if (collectDataDisks (file.getAbsolutePath (), pos, disks)) - return new Wizardry4BootDisk (disks); - } - } - } - if (debug) - System.out.println ("Not a Wizardry IV disk"); - - PascalDisk pascalDisk = new PascalDisk (disk); - return pascalDisk; - } - - private static boolean collectDataDisks (String fileName, int dotPos, AppleDisk[] disks) - { - char c = fileName.charAt (dotPos - 1); - String suffix = fileName.substring (dotPos + 1); - - for (int i = 2; i < disks.length; i++) - { - String old = new String (c + "." + suffix); - String rep = new String ((char) (c + i - 1) + "." + suffix); - - File f = new File (fileName.replace (old, rep)); - if (!f.exists () || !f.isFile ()) - return false; - - AppleDisk dataDisk = new AppleDisk (f, 35, 8); - dataDisk.setInterleave (1); - disks[i] = dataDisk; - } - - return true; - } - - private static InfocomDisk checkInfocomDisk (AppleDisk disk) - { - if (debug) - System.out.println ("Checking Infocom disk"); - - if (InfocomDisk.isCorrectFormat (disk)) - { - if (debug) - System.out.println (" --> INFOCOM"); - return new InfocomDisk (disk); - } - - if (debug) - System.out.println ("Not an InfocomDisk disk"); - - return null; - } - - private static CPMDisk checkCPMDisk (AppleDisk disk) - { - if (debug) - System.out.println ("Checking CPM disk"); - - if (CPMDisk.isCorrectFormat (disk)) - return new CPMDisk (disk); - - if (debug) - System.out.println ("Not a CPM disk"); - - return null; - } - - private static void checkMissingSectors (AppleDisk disk, WozFile wozFile) - { - - } +package com.bytezone.diskbrowser.disk; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Enumeration; +import java.util.zip.GZIPInputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import com.bytezone.diskbrowser.cpm.CPMDisk; +import com.bytezone.diskbrowser.dos.DosDisk; +import com.bytezone.diskbrowser.infocom.InfocomDisk; +import com.bytezone.diskbrowser.nib.NibFile; +import com.bytezone.diskbrowser.nib.V2dFile; +import com.bytezone.diskbrowser.nib.WozFile; +import com.bytezone.diskbrowser.pascal.PascalDisk; +import com.bytezone.diskbrowser.prodos.ProdosDisk; +import com.bytezone.diskbrowser.utilities.FileFormatException; +import com.bytezone.diskbrowser.utilities.NuFX; +import com.bytezone.diskbrowser.utilities.Utility; +import com.bytezone.diskbrowser.wizardry.Wizardry4BootDisk; +import com.bytezone.diskbrowser.wizardry.WizardryScenarioDisk; + +// -----------------------------------------------------------------------------------// +public class DiskFactory +// -----------------------------------------------------------------------------------// +{ + private static boolean debug = false; + + // ---------------------------------------------------------------------------------// + private DiskFactory () + // ---------------------------------------------------------------------------------// + { + } + + // ---------------------------------------------------------------------------------// + public static FormattedDisk createDisk (File file) + // ---------------------------------------------------------------------------------// + { + return createDisk (file.getAbsolutePath ()); + } + + // ---------------------------------------------------------------------------------// + public static FormattedDisk createDisk (String path) + // ---------------------------------------------------------------------------------// + { + if (debug) + System.out.println ("\nFactory : " + path); + + File file = new File (path); + if (!file.exists ()) + return null; + + String suffix = path.substring (path.lastIndexOf (".") + 1).toLowerCase (); + Boolean compressed = false; + Path originalPath = Paths.get (path); + + if ("gz".equals (suffix)) + { + if (debug) + System.out.println (" ** gzip **"); + try + { + InputStream in = new GZIPInputStream (new FileInputStream (path)); + File tmp = File.createTempFile ("gzip", null); + FileOutputStream fos = new FileOutputStream (tmp); + + int bytesRead; + byte[] buffer = new byte[1024]; + while ((bytesRead = in.read (buffer)) > 0) + fos.write (buffer, 0, bytesRead); + + fos.close (); + in.close (); + tmp.deleteOnExit (); + + suffix = Utility.getSuffix (file.getName ()); // ignores the .gz and .zip + file = tmp; + compressed = true; + } + catch (IOException e) // can get EOFException: Unexpected end of ZLIB input stream + { + e.printStackTrace (); + return null; + } + } + else if ("zip".equals (suffix)) + { + if (debug) + System.out.println (" ** zip **"); + try + { + ZipFile zipFile = new ZipFile (path); + Enumeration entries = zipFile.entries (); + + while (entries.hasMoreElements ()) // loop until first valid name + { + ZipEntry entry = entries.nextElement (); + if (Utility.validFileType (entry.getName ())) + { + InputStream stream = zipFile.getInputStream (entry); + File tmp = File.createTempFile ("zip", null); + FileOutputStream fos = new FileOutputStream (tmp); + + int bytesRead; + byte[] buffer = new byte[1024]; + while ((bytesRead = stream.read (buffer)) > 0) + fos.write (buffer, 0, bytesRead); + + stream.close (); + fos.close (); + tmp.deleteOnExit (); + + suffix = Utility.getSuffix (file.getName ()); // ignores the .gz and .zip + file = tmp; + compressed = true; + + break; + } + } + + zipFile.close (); + } + catch (IOException e) + { + e.printStackTrace (); + return null; + } + } + + if (suffix.equals ("sdk")) + { + if (debug) + System.out.println (" ** sdk **"); + try + { + NuFX nuFX = new NuFX (file); + File tmp = File.createTempFile ("sdk", null); + FileOutputStream fos = new FileOutputStream (tmp); + fos.write (nuFX.getBuffer ()); + fos.close (); + tmp.deleteOnExit (); + file = tmp; + suffix = "dsk"; + compressed = true; + } + catch (IOException e) + { + e.printStackTrace (); + return null; + } + catch (FileFormatException e) + { + return null; + } + } + + FormattedDisk disk = null; + FormattedDisk disk2 = null; + + if (suffix.equals ("hdv")) + { + if (debug) + System.out.println (" ** hdv **"); + FormattedDisk prodosDisk = checkHardDisk (file); + if (prodosDisk != null) + return prodosDisk; + + disk2 = check2mgDisk (file); + if (disk2 != null) + { + if (compressed) + disk2.setOriginalPath (originalPath); + return disk2; + } + + AppleDisk appleDisk = new AppleDisk (file, (int) file.length () / 4096, 8); + return new DataDisk (appleDisk); + } + + if (suffix.equals ("2mg")) + { + if (debug) + System.out.println (" ** 2mg **"); + disk2 = check2mgDisk (file); + if (disk2 != null) + { + if (compressed) + disk2.setOriginalPath (originalPath); + return disk2; + } + + AppleDisk appleDisk = new AppleDisk (file, (int) file.length () / 4096, 8); + return new DataDisk (appleDisk); + } + + if (((suffix.equals ("po") || suffix.equals ("dsk")) && file.length () > 143360)) + { + if (file.length () < 143500) // slightly bigger than a floppy + { + System.out.println ("File length is wrong: " + file.length ()); + disk = checkDos (new AppleDisk (file, 35, 16)); + if (disk != null) + return disk; + } + + if (debug) + System.out.printf (" Checking po or dsk hard drive: %,d%n", file.length ()); + + disk = checkHardDisk (file); + if (disk != null) + { + if (compressed) + disk.setOriginalPath (originalPath); + return disk; + } + + if (file.length () == 819200) // 800K 3.5" + { + if (debug) + System.out.println ("UniDos ?"); + // 2 x 400k disk images + AppleDisk appleDisk1 = new AppleDisk (file, 50, 32); + AppleDisk appleDisk2 = new AppleDisk (file, 50, 32, (int) (file.length () / 2)); + disk = checkUnidos (appleDisk1, 1); + disk2 = checkUnidos (appleDisk2, 2); + if (disk != null && disk2 != null) + return new DualDosDisk (disk, disk2); + } + + if (debug) + System.out.println (" Creating a data disk from bad length"); + + try + { + AppleDisk appleDisk = new AppleDisk (file, (int) file.length () / 4096, 8); + if (debug) + System.out.println (" created data disk"); + return new DataDisk (appleDisk); + } + catch (FileFormatException e) + { + if (debug) + System.out.println (" Creating AppleDisk failed"); + return null; + } + } + + if (suffix.equals ("woz")) + { + if (debug) + System.out.println ("Checking woz"); + try + { + WozFile wozFile = new WozFile (file); + + if (wozFile.getSectorsPerTrack () == 13) + { + AppleDisk appleDisk = new AppleDisk (wozFile, 35, 13); + disk = checkDos (appleDisk); + return disk == null ? new DataDisk (appleDisk) : disk; + } + + if (wozFile.getSectorsPerTrack () == 16) + { + if (wozFile.getDiskType () == 2) + { + if (debug) + System.out.println ("Checking woz 3.5"); + AppleDisk disk800 = new AppleDisk (wozFile, 100 * wozFile.getSides (), 8); + if (ProdosDisk.isCorrectFormat (disk800)) + { + if (debug) + System.out.println (" --> PRODOS hard disk"); + return new ProdosDisk (disk800); + } + disk = new DataDisk (disk800); + } + else + { + AppleDisk appleDisk256 = new AppleDisk (wozFile, wozFile.getTracks (), 16); + disk = checkDos (appleDisk256); + if (disk == null) + disk = checkProdos (new AppleDisk (wozFile, 35, 8)); + if (disk == null) + disk = new DataDisk (appleDisk256); + } + } + + return disk; + } + catch (Exception e) + { + System.out.println (e); + return null; + } + } + + if (suffix.equals ("v2d")) + { + V2dFile v2dDisk = new V2dFile (file); + AppleDisk appleDisk256 = new AppleDisk (v2dDisk, 35, 16); + disk = checkDos (appleDisk256); + if (disk == null) + disk = checkProdos (new AppleDisk (v2dDisk, 35, 8)); + if (disk == null) + disk = new DataDisk (appleDisk256); + return disk; + } + + if (suffix.equals ("nib")) // not implemented yet + { + if (debug) + System.out.println (" ** nib **"); + + NibFile nibDisk = new NibFile (file); + AppleDisk appleDisk16 = new AppleDisk (nibDisk); + disk = checkDos (appleDisk16); + return null; + } + + long length = file.length (); + + if (length == 116480) // 13 sector disk + { + if (debug) + System.out.println (" ** 13 sector **"); + if (!suffix.equals ("d13")) + System.out.printf ("%s should have a d13 suffix%n", file.getName ()); + + AppleDisk appleDisk = new AppleDisk (file, 35, 13); + disk = checkDos (appleDisk); + return disk == null ? new DataDisk (appleDisk) : disk; + } + + if (length != 143360) + { + System.out.printf ("%s: invalid file length : %,d%n", file.getName (), + file.length ()); + return null; + } + + AppleDisk appleDisk256 = new AppleDisk (file, 35, 16); + AppleDisk appleDisk512 = new AppleDisk (file, 35, 8); + + if (true) + { + long checksum = appleDisk256.getBootChecksum (); + + if (checksum == 227968344L) // empty boot sector + { + // could be wizardry data, visialc data ... + if (debug) + System.out.println (" empty sector checksum : " + checksum); + } + else if (checksum == 3176296590L // + || checksum == 108825457L // + || checksum == 1439356606L // + || checksum == 1550012074L // + || checksum == 1614602459L // + || checksum == 940889336L // + || checksum == 2936955085L // + || checksum == 1348415927L // + || checksum == 3340889101L // + || checksum == 18315788L // + || checksum == 993895235L // + || checksum == 2378342794L) // LazerPascal1.dsk + { + if (debug) + System.out.println (" known DOS checksum : " + checksum); + disk = checkDos (appleDisk256); + disk2 = checkProdos (appleDisk512); // no need for this + if (disk2 != null && disk != null) // should be impossible + { + if (debug) + System.out.println (" --> Dual dos/prodos 1"); + System.out.println ("** impossible **"); + disk = new DualDosDisk (disk, disk2); + } + } + else if (checksum == 1737448647L // + || checksum == 170399908L // + || checksum == 990032697) // Apple Assembly Line + { + if (debug) + System.out.println (" known PRODOS checksum : " + checksum); + disk = checkProdos (appleDisk512); + disk2 = checkDos (appleDisk256); + if (disk2 != null && disk != null) + { + if (debug) + System.out.println (" --> Dual prodos/dos 2"); + disk = new DualDosDisk (disk, disk2); + } + } + else if (checksum == 2803644711L // Apple Pascal disk 0 + || checksum == 3317783349L // + || checksum == 1728863694L // Wizardry_I_boot.dsk + || checksum == 198094178L) // + { + if (debug) + System.out.println (" known PASCAL checksum : " + checksum); + disk = checkPascalDisk (appleDisk512); + disk2 = checkDos (appleDisk256); + if (disk2 != null) + disk = new DualDosDisk (disk, disk2); + } + else if (checksum == 3028642627L // + || checksum == 2070151659L) // Enchanter + { + if (debug) + System.out.println (" known INFOCOM checksum : " + checksum); + disk = checkInfocomDisk (appleDisk256); + } + else if (debug) + System.out.println (" unknown checksum : " + checksum); + + // else if (checksum == 1212926910L || checksum == 1365043894L + // || checksum == 2128073918L) + // disk = checkCPMDisk (file); + + // System.out.println (checksum); + + if (disk != null) + { + if (compressed) + disk.setOriginalPath (originalPath); + return disk; + } + + // empty boot sector + if (checksum != 227968344L && false) + System.out.println ("Unknown checksum : " + checksum + " : " + path); + } + + if (debug) + System.out.println (" checksum no help"); + if (debug) + System.out.println (" Suffix : " + suffix); + + if (suffix.equals ("dsk") || suffix.equals ("do")) + { + disk = checkDos (appleDisk256); + if (disk == null) + disk = checkProdos (appleDisk512); + else + { + if (debug) + System.out.println ("Checking DualDos disk"); + + disk2 = checkProdos (appleDisk512); + if (disk2 != null) + disk = new DualDosDisk (disk, disk2); + + AppleDisk appleDisk = new AppleDisk (file, 35, 16); + disk2 = checkCPMDisk (appleDisk); + if (disk2 != null) + disk = new DualDosDisk (disk, disk2); + } + } + else if (suffix.equals ("po")) + { + disk = checkProdos (appleDisk512); + if (disk == null) + disk = checkDos (appleDisk256); + } + + if (disk == null) + disk = checkPascalDisk (appleDisk512); + + if (disk == null) + disk = checkCPMDisk (appleDisk256); + + if (disk == null) + { + disk2 = checkInfocomDisk (appleDisk256); + if (disk2 != null) + disk = disk2; + } + + if (disk == null) + disk = new DataDisk (appleDisk256); + + if (debug) + System.out.println ( + "Factory creating disk : " + disk.getDisk ().getFile ().getAbsolutePath ()); + + if (disk != null && compressed) + disk.setOriginalPath (originalPath); + + return disk; + } + + // ---------------------------------------------------------------------------------// + private static DosDisk checkDos (AppleDisk disk) + // ---------------------------------------------------------------------------------// + { + if (debug) + System.out.println ("Checking DOS disk"); + + try + { + if (DosDisk.isCorrectFormat (disk)) + { + if (debug) + System.out.println (" --> DOS"); + return new DosDisk (disk); + } + } + catch (Exception e) + { + e.printStackTrace (); + } + if (debug) + System.out.println (" not a DOS disk"); + return null; + } + + // ---------------------------------------------------------------------------------// + private static ProdosDisk checkProdos (AppleDisk disk) + // ---------------------------------------------------------------------------------// + { + if (debug) + System.out.println ("Checking Prodos disk"); + + try + { + if (ProdosDisk.isCorrectFormat (disk)) + { + if (debug) + System.out.println (" --> PRODOS"); + return new ProdosDisk (disk); + } + } + catch (Exception e) + { + } + if (debug) + System.out.println (" not a Prodos disk"); + return null; + } + + // ---------------------------------------------------------------------------------// + private static DosDisk checkUnidos (AppleDisk disk, int side) + // ---------------------------------------------------------------------------------// + { + if (debug) + System.out.println ("Checking UniDOS disk"); + + try + { + if (DosDisk.isCorrectFormat (disk)) + { + if (debug) + System.out.println (" --> UniDOS"); + return new DosDisk (disk, side); + } + } + catch (Exception e) + { + e.printStackTrace (); + } + if (debug) + System.out.println (" not a UniDOS disk"); + return null; + } + + // ---------------------------------------------------------------------------------// + private static FormattedDisk checkHardDisk (File file) + // ---------------------------------------------------------------------------------// + { + if (debug) + { + System.out.println ("\nChecking Prodos hard disk"); + System.out.printf ("Total blocks : %f%n", (float) file.length () / 512); + System.out.printf ("Total tracks : %f%n", (float) file.length () / 4096); + System.out.printf ("File length : %d%n", file.length ()); + System.out.println (); + } + + // assumes a sector is 512 bytes + if ((file.length () % 512) != 0) + { + if (debug) + System.out.printf ("file length not divisible by 512 : %,d%n%n", file.length ()); + return null; + } + + try + { + // extend the file if necessary + int tracks = (int) (file.length () - 1) / 4096 + 1; + if (tracks * 4096 != file.length ()) + { + System.out.println ("*** extended ***"); // System Addons.hdv + // System.out.println (tracks); + } + AppleDisk disk = new AppleDisk (file, tracks, 8); + if (ProdosDisk.isCorrectFormat (disk)) + { + if (debug) + System.out.println (" --> PRODOS hard disk"); + return new ProdosDisk (disk); + } + if (PascalDisk.isCorrectFormat (disk, debug)) + { + if (debug) + System.out.println (" --> Pascal hard disk"); + return new PascalDisk (disk); + } + } + catch (Exception e) + { + System.out.println (e); + System.out.println ("Prodos hard disk had error"); + } + + if (debug) + System.out.println (" not a Prodos hard disk\n"); + + return null; + } + + /* + offset | size | description + ------ | ---- | ----------- + +$000 | Long | The integer constant '2IMG'. This integer should be little-endian, + so on the Apple IIgs, this is equivalent to the four characters + 'GMI2'; in ORCA/C 2.1, you can use the integer constant '2IMG'. + +$004 | Long | A four-character tag identifying the application that created the + file. + +$008 | Word | The length of this header, in bytes. Should be 52. + +$00A | Word | The version number of the image file format. Should be 1. + +$00C | Long | The image format. See table below. + +$010 | Long | Flags. See table below. + +$014 | Long | The number of 512-byte blocks in the disk image. This value should + be zero unless the image format is 1 (ProDOS order). + +$018 | Long | Offset to the first byte of the first block of the disk in the image + file, from the beginning of the file. The disk data must come before + the comment and creator-specific chunks. + +$01C | Long | Length of the disk data in bytes. This should be the number of + blocks * 512. + +$020 | Long | Offset to the first byte of the image comment. Can be zero if + there's no comment. The comment must come after the data chunk, but + before the creator-specific chunk. The comment, if it exists, should + be raw text; no length byte or C-style null terminator byte is + required (that's what the next field is for). + +$024 | Long | Length of the comment chunk. Zero if there's no comment. + +$028 | Long | Offset to the first byte of the creator-specific data chunk, or zero + if there is none. + +$02C | Long | Length of the creator-specific chunk; zero if there is no + creator-specific data. + +$030 | 16 bytes | Reserved space; this pads the header to 64 bytes. These values + must all be zero. + */ + + // ---------------------------------------------------------------------------------// + private static FormattedDisk check2mgDisk (File file) + // ---------------------------------------------------------------------------------// + { + if (debug) + System.out.println ("Checking 2mg disk"); + + try + { + AppleDisk disk = new AppleDisk (file, 0, 0); + if (disk.getTotalBlocks () > 0 && ProdosDisk.isCorrectFormat (disk)) + return new ProdosDisk (disk); + // should check for DOS, but AppleDisk assumes 2mg has 512 byte blocks + } + catch (Exception e) + { + e.printStackTrace (); + // System.out.println (e); + } + if (debug) + System.out.println ("Not a Prodos 2mg disk"); + + return null; + } + + // ---------------------------------------------------------------------------------// + private static FormattedDisk checkPascalDisk (AppleDisk disk) + // ---------------------------------------------------------------------------------// + { + if (debug) + System.out.println ("Checking Pascal disk"); + + File file = disk.getFile (); + + if (!PascalDisk.isCorrectFormat (disk, debug)) + return null; + + if (debug) + System.out.println ("Pascal disk OK - Checking Wizardry disk"); + + if (WizardryScenarioDisk.isWizardryFormat (disk, debug)) + return new WizardryScenarioDisk (disk); + + if (debug) + System.out.println ("Not a Wizardry 1-3 disk"); + + // check for compressed disk + if (file.getName ().endsWith (".tmp")) + return new PascalDisk (disk); // complicated joining up compressed disks + + if (Wizardry4BootDisk.isWizardryIVorV (disk, debug)) + { + String fileName = file.getAbsolutePath ().toLowerCase (); + int pos = file.getAbsolutePath ().indexOf ('.'); + char c = fileName.charAt (pos - 1); + // String suffix = fileName.substring (pos + 1); + int requiredDisks = c == '1' ? 6 : c == 'a' ? 10 : 0; + + if (requiredDisks > 0) + { + // collect extra data disks + AppleDisk[] disks = new AppleDisk[requiredDisks]; + + disks[0] = new AppleDisk (file, 256, 8); // will become a PascalDisk + disks[0].setInterleave (1); + + disks[1] = new AppleDisk (file, 256, 8); // will remain a DataDisk + disks[1].setInterleave (1); + + if (pos > 0 && requiredDisks > 0) + { + if (collectDataDisks (file.getAbsolutePath (), pos, disks)) + return new Wizardry4BootDisk (disks); + } + } + } + if (debug) + System.out.println ("Not a Wizardry IV disk"); + + PascalDisk pascalDisk = new PascalDisk (disk); + return pascalDisk; + } + + // ---------------------------------------------------------------------------------// + private static boolean collectDataDisks (String fileName, int dotPos, AppleDisk[] disks) + // ---------------------------------------------------------------------------------// + { + char c = fileName.charAt (dotPos - 1); + String suffix = fileName.substring (dotPos + 1); + + for (int i = 2; i < disks.length; i++) + { + String old = new String (c + "." + suffix); + String rep = new String ((char) (c + i - 1) + "." + suffix); + + File f = new File (fileName.replace (old, rep)); + if (!f.exists () || !f.isFile ()) + return false; + + AppleDisk dataDisk = new AppleDisk (f, 35, 8); + dataDisk.setInterleave (1); + disks[i] = dataDisk; + } + + return true; + } + + // ---------------------------------------------------------------------------------// + private static InfocomDisk checkInfocomDisk (AppleDisk disk) + // ---------------------------------------------------------------------------------// + { + if (debug) + System.out.println ("Checking Infocom disk"); + + if (InfocomDisk.isCorrectFormat (disk)) + { + if (debug) + System.out.println (" --> INFOCOM"); + return new InfocomDisk (disk); + } + + if (debug) + System.out.println ("Not an InfocomDisk disk"); + + return null; + } + + // ---------------------------------------------------------------------------------// + private static CPMDisk checkCPMDisk (AppleDisk disk) + // ---------------------------------------------------------------------------------// + { + if (debug) + System.out.println ("Checking CPM disk"); + + if (CPMDisk.isCorrectFormat (disk)) + return new CPMDisk (disk); + + if (debug) + System.out.println ("Not a CPM disk"); + + return null; + } } \ No newline at end of file diff --git a/src/com/bytezone/diskbrowser/disk/FormattedDisk.java b/src/com/bytezone/diskbrowser/disk/FormattedDisk.java index 77f1b28..404b74c 100755 --- a/src/com/bytezone/diskbrowser/disk/FormattedDisk.java +++ b/src/com/bytezone/diskbrowser/disk/FormattedDisk.java @@ -1,85 +1,87 @@ -package com.bytezone.diskbrowser.disk; - -import java.awt.Dimension; -import java.nio.file.Path; -import java.util.List; - -import javax.swing.JTree; - -import com.bytezone.diskbrowser.applefile.AbstractFile; -import com.bytezone.diskbrowser.applefile.AppleFileSource; -import com.bytezone.diskbrowser.gui.DataSource; - -public interface FormattedDisk -{ - // Methods to be implemented by the implementer - public DataSource getFormattedSector (DiskAddress da); - - public List getFileSectors (int fileNo); - - // Methods implemented by AbstractFormattedDisk - public JTree getCatalogTree (); // each node is an AppleFileSource - - public List getCatalogList (); - - public void writeFile (AbstractFile file); - - public SectorType getSectorType (int track, int sector); - - public SectorType getSectorType (int block); - - public SectorType getSectorType (DiskAddress da); - - public void setSectorType (int block, SectorType type); - - public String getSectorFilename (DiskAddress da); - - public List getSectorTypeList (); - - public Disk getDisk (); - - public FormattedDisk getParent (); - - public void setParent (FormattedDisk disk); - - public AppleFileSource getCatalog (); - - public AppleFileSource getFile (String uniqueName); - - public int clearOrphans (); - - public void setSectorFree (int block, boolean free); - - public boolean isSectorFree (DiskAddress da); - - public boolean isSectorFree (int block); - - public void verify (); - - public boolean stillAvailable (DiskAddress da); - - public boolean stillAvailable (int block); - - public Dimension getGridLayout (); - - public String getAbsolutePath (); - - public String getDisplayPath (); - - public void setOriginalPath (Path path); - - public Path getOriginalPath (); - - public boolean isTempDisk (); - - // VTOC flags sector as free, but it is in use by a file - public int falsePositiveBlocks (); - - // VTOC flags sector as in use, but no file is using it - public int falseNegativeBlocks (); - - public String getName (); -} - -// getFileTypeList () +package com.bytezone.diskbrowser.disk; + +import java.awt.Dimension; +import java.nio.file.Path; +import java.util.List; + +import javax.swing.JTree; + +import com.bytezone.diskbrowser.applefile.AbstractFile; +import com.bytezone.diskbrowser.applefile.AppleFileSource; +import com.bytezone.diskbrowser.gui.DataSource; + +// -----------------------------------------------------------------------------------// +public interface FormattedDisk +// -----------------------------------------------------------------------------------// +{ + // Methods to be implemented by the implementer + public DataSource getFormattedSector (DiskAddress da); + + public List getFileSectors (int fileNo); + + // Methods implemented by AbstractFormattedDisk + public JTree getCatalogTree (); // each node is an AppleFileSource + + public List getCatalogList (); + + public void writeFile (AbstractFile file); + + public SectorType getSectorType (int track, int sector); + + public SectorType getSectorType (int block); + + public SectorType getSectorType (DiskAddress da); + + public void setSectorType (int block, SectorType type); + + public String getSectorFilename (DiskAddress da); + + public List getSectorTypeList (); + + public Disk getDisk (); + + public FormattedDisk getParent (); + + public void setParent (FormattedDisk disk); + + public AppleFileSource getCatalog (); + + public AppleFileSource getFile (String uniqueName); + + public int clearOrphans (); + + public void setSectorFree (int block, boolean free); + + public boolean isSectorFree (DiskAddress da); + + public boolean isSectorFree (int block); + + public void verify (); + + public boolean stillAvailable (DiskAddress da); + + public boolean stillAvailable (int block); + + public Dimension getGridLayout (); + + public String getAbsolutePath (); + + public String getDisplayPath (); + + public void setOriginalPath (Path path); + + public Path getOriginalPath (); + + public boolean isTempDisk (); + + // VTOC flags sector as free, but it is in use by a file + public int falsePositiveBlocks (); + + // VTOC flags sector as in use, but no file is using it + public int falseNegativeBlocks (); + + public String getName (); +} + +// getFileTypeList () // getFiles (FileType type) \ No newline at end of file diff --git a/src/com/bytezone/diskbrowser/disk/SectorList.java b/src/com/bytezone/diskbrowser/disk/SectorList.java index 7b104f5..dcae6fd 100755 --- a/src/com/bytezone/diskbrowser/disk/SectorList.java +++ b/src/com/bytezone/diskbrowser/disk/SectorList.java @@ -1,52 +1,58 @@ -package com.bytezone.diskbrowser.disk; - -import java.util.List; - -import com.bytezone.diskbrowser.applefile.AbstractFile; - -public class SectorList extends AbstractFile -{ - List sectors; - FormattedDisk formattedDisk; - - public SectorList (FormattedDisk formattedDisk, List sectors) - { - super ("noname", null); - - this.sectors = sectors; - this.formattedDisk = formattedDisk; - - Disk disk = formattedDisk.getDisk (); - int ptr = 0; - buffer = new byte[sectors.size () * disk.getBlockSize ()]; - - for (DiskAddress da : sectors) - { - if (!disk.isValidAddress (da)) - break; - byte[] tempBuffer = disk.readSector (da); - System.arraycopy (tempBuffer, 0, buffer, ptr, disk.getBlockSize ()); - ptr += disk.getBlockSize (); - } - } - - @Override - public String getText () - { - StringBuilder text = new StringBuilder ("Block Sector Type Owner\n"); - text.append ( - "----- ------------------ ---------------------------------------------\n"); - - for (DiskAddress da : sectors) - { - SectorType sectorType = formattedDisk.getSectorType (da); - String owner = formattedDisk.getSectorFilename (da); - if (owner == null) - owner = ""; - text.append ( - String.format (" %04X %-18s %s%n", da.getBlock (), sectorType.name, owner)); - } - - return text.toString (); - } +package com.bytezone.diskbrowser.disk; + +import java.util.List; + +import com.bytezone.diskbrowser.applefile.AbstractFile; + +// -----------------------------------------------------------------------------------// +public class SectorList extends AbstractFile +// -----------------------------------------------------------------------------------// +{ + List sectors; + FormattedDisk formattedDisk; + + // ---------------------------------------------------------------------------------// + public SectorList (FormattedDisk formattedDisk, List sectors) + // ---------------------------------------------------------------------------------// + { + super ("noname", null); + + this.sectors = sectors; + this.formattedDisk = formattedDisk; + + Disk disk = formattedDisk.getDisk (); + int ptr = 0; + buffer = new byte[sectors.size () * disk.getBlockSize ()]; + + for (DiskAddress da : sectors) + { + if (!disk.isValidAddress (da)) + break; + byte[] tempBuffer = disk.readSector (da); + System.arraycopy (tempBuffer, 0, buffer, ptr, disk.getBlockSize ()); + ptr += disk.getBlockSize (); + } + } + + // ---------------------------------------------------------------------------------// + @Override + public String getText () + // ---------------------------------------------------------------------------------// + { + StringBuilder text = new StringBuilder ("Block Sector Type Owner\n"); + text.append ( + "----- ------------------ ---------------------------------------------\n"); + + for (DiskAddress da : sectors) + { + SectorType sectorType = formattedDisk.getSectorType (da); + String owner = formattedDisk.getSectorFilename (da); + if (owner == null) + owner = ""; + text.append ( + String.format (" %04X %-18s %s%n", da.getBlock (), sectorType.name, owner)); + } + + return text.toString (); + } } \ No newline at end of file diff --git a/src/com/bytezone/diskbrowser/disk/SectorListConverter.java b/src/com/bytezone/diskbrowser/disk/SectorListConverter.java index 1ebd51a..13b7772 100644 --- a/src/com/bytezone/diskbrowser/disk/SectorListConverter.java +++ b/src/com/bytezone/diskbrowser/disk/SectorListConverter.java @@ -3,12 +3,16 @@ package com.bytezone.diskbrowser.disk; import java.util.ArrayList; import java.util.List; +// -----------------------------------------------------------------------------------// public class SectorListConverter +// -----------------------------------------------------------------------------------// { public final List sectors; public final String sectorText; + // ---------------------------------------------------------------------------------// public SectorListConverter (String text, Disk disk) + // ---------------------------------------------------------------------------------// { sectors = new ArrayList<> (); sectorText = text; @@ -29,7 +33,9 @@ public class SectorListConverter } } + // ---------------------------------------------------------------------------------// public SectorListConverter (List sectors) + // ---------------------------------------------------------------------------------// { this.sectors = sectors; StringBuilder text = new StringBuilder (); @@ -55,7 +61,9 @@ public class SectorListConverter sectorText = text.deleteCharAt (text.length () - 1).toString (); } + // ---------------------------------------------------------------------------------// private void addToText (StringBuilder text, int firstBlock, int runLength) + // ---------------------------------------------------------------------------------// { if (runLength == 0) text.append (firstBlock + ";"); diff --git a/src/com/bytezone/diskbrowser/disk/SectorType.java b/src/com/bytezone/diskbrowser/disk/SectorType.java index f8d56ee..dd31f03 100755 --- a/src/com/bytezone/diskbrowser/disk/SectorType.java +++ b/src/com/bytezone/diskbrowser/disk/SectorType.java @@ -1,21 +1,27 @@ -package com.bytezone.diskbrowser.disk; - -import java.awt.Color; - -public class SectorType -{ - public final String name; - public final Color colour; - - public SectorType (String name, Color colour) - { - this.name = name; - this.colour = colour; - } - - @Override - public String toString () - { - return String.format ("[SectorType : %s, %s]", name, colour); - } +package com.bytezone.diskbrowser.disk; + +import java.awt.Color; + +// -----------------------------------------------------------------------------------// +public class SectorType +// -----------------------------------------------------------------------------------// +{ + public final String name; + public final Color colour; + + // ---------------------------------------------------------------------------------// + public SectorType (String name, Color colour) + // ---------------------------------------------------------------------------------// + { + this.name = name; + this.colour = colour; + } + + // ---------------------------------------------------------------------------------// + @Override + public String toString () + // ---------------------------------------------------------------------------------// + { + return String.format ("[SectorType : %s, %s]", name, colour); + } } \ No newline at end of file diff --git a/src/com/bytezone/diskbrowser/disk/UnknownDisk.java b/src/com/bytezone/diskbrowser/disk/UnknownDisk.java index 25d12bd..3c8c8cf 100644 --- a/src/com/bytezone/diskbrowser/disk/UnknownDisk.java +++ b/src/com/bytezone/diskbrowser/disk/UnknownDisk.java @@ -3,17 +3,23 @@ package com.bytezone.diskbrowser.disk; import java.util.ArrayList; import java.util.List; +// -----------------------------------------------------------------------------------// public class UnknownDisk extends AbstractFormattedDisk +// -----------------------------------------------------------------------------------// { // could arrange for the blocks to appear as a question mark + // ---------------------------------------------------------------------------------// public UnknownDisk (AppleDisk disk) + // ---------------------------------------------------------------------------------// { super (disk); } + // ---------------------------------------------------------------------------------// @Override public List getFileSectors (int fileNo) + // ---------------------------------------------------------------------------------// { return new ArrayList<> (); }