dmolony-DiskBrowser/src/com/bytezone/diskbrowser/cpm/CPMDisk.java

368 lines
12 KiB
Java
Raw Normal View History

2015-06-01 09:35:51 +00:00
package com.bytezone.diskbrowser.cpm;
import java.awt.Color;
2021-07-24 10:59:00 +00:00
import java.util.ArrayList;
2015-06-01 09:35:51 +00:00
import java.util.List;
2016-02-25 01:55:14 +00:00
import javax.swing.tree.DefaultMutableTreeNode;
2016-02-25 01:27:22 +00:00
import com.bytezone.diskbrowser.applefile.AppleFileSource;
2020-02-08 08:13:28 +00:00
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;
2016-02-26 03:09:14 +00:00
import com.bytezone.diskbrowser.gui.DataSource;
import com.bytezone.diskbrowser.utilities.HexFormatter;
2015-06-01 09:35:51 +00:00
2023-01-16 03:08:46 +00:00
// https://www.retrotechnology.com/dri/howto_cpm.html
2020-02-08 08:13:28 +00:00
// -----------------------------------------------------------------------------------//
2015-06-01 09:35:51 +00:00
public class CPMDisk extends AbstractFormattedDisk
2020-02-08 08:13:28 +00:00
// -----------------------------------------------------------------------------------//
2015-06-01 09:35:51 +00:00
{
private static final int EMPTY_BYTE_VALUE = 0xE5;
2015-06-01 09:35:51 +00:00
private final Color green = new Color (0, 200, 0);
2016-02-25 07:45:24 +00:00
2015-06-01 09:35:51 +00:00
public final SectorType catalogSector = new SectorType ("Catalog", green);
2016-02-25 07:45:24 +00:00
public final SectorType prnSector = new SectorType ("PRN", Color.lightGray);
2016-02-25 02:49:12 +00:00
public final SectorType comSector = new SectorType ("COM", Color.red);
2016-02-28 05:41:10 +00:00
public final SectorType otherSector = new SectorType ("Other", Color.blue);
2016-02-25 02:49:12 +00:00
public final SectorType docSector = new SectorType ("DOC", Color.cyan);
2016-02-25 07:45:24 +00:00
public final SectorType basSector = new SectorType ("BAS", Color.gray);
2016-02-26 11:03:29 +00:00
public final SectorType asmSector = new SectorType ("ASM", Color.orange);
2016-02-27 23:44:28 +00:00
public final SectorType ovrSector = new SectorType ("OVR", Color.magenta);
2016-03-01 00:16:31 +00:00
public final SectorType macSector = new SectorType ("MAC", Color.green);
2015-06-01 09:35:51 +00:00
2016-02-24 12:32:36 +00:00
private int version; // http://www.seasip.info/Cpm/format22.html
2018-08-11 00:07:50 +00:00
// // http://www.seasip.info/Cpm/format31.html
2018-12-23 01:15:51 +00:00
private final DefaultMutableTreeNode volumeNode;
2016-02-24 12:32:36 +00:00
2020-02-08 08:13:28 +00:00
// ---------------------------------------------------------------------------------//
2015-06-01 09:35:51 +00:00
public CPMDisk (Disk disk)
2020-02-08 08:13:28 +00:00
// ---------------------------------------------------------------------------------//
2015-06-01 09:35:51 +00:00
{
super (disk);
2016-02-25 02:49:12 +00:00
2015-06-01 09:35:51 +00:00
sectorTypesList.add (catalogSector);
2016-02-25 07:45:24 +00:00
sectorTypesList.add (prnSector);
2016-02-25 02:49:12 +00:00
sectorTypesList.add (comSector);
2016-02-25 07:45:24 +00:00
sectorTypesList.add (basSector);
2016-02-25 02:49:12 +00:00
sectorTypesList.add (docSector);
2016-02-26 11:03:29 +00:00
sectorTypesList.add (asmSector);
2016-02-27 23:44:28 +00:00
sectorTypesList.add (ovrSector);
2016-02-28 05:41:10 +00:00
sectorTypesList.add (macSector);
sectorTypesList.add (otherSector);
2015-06-01 09:35:51 +00:00
setEmptyByte ((byte) EMPTY_BYTE_VALUE);
2016-02-25 07:45:24 +00:00
2017-04-05 08:47:28 +00:00
// search for the version string
2016-12-16 01:19:59 +00:00
for (int i = 8; i >= 4; i -= 2)
{
2020-04-10 23:47:52 +00:00
byte[] buffer = disk.readBlock (0, i);
2016-12-16 01:19:59 +00:00
String text = new String (buffer, 16, 24);
if ("DIR ERA TYPESAVEREN USER".equals (text))
{
version = buffer[41] & 0xFF;
break;
}
}
2016-02-24 12:32:36 +00:00
2018-12-23 01:15:51 +00:00
DefaultMutableTreeNode rootNode = getCatalogTreeRoot ();
volumeNode = new DefaultMutableTreeNode ();
rootNode.add (volumeNode);
2016-02-25 01:55:14 +00:00
2015-06-01 09:35:51 +00:00
for (int sector = 0; sector < 8; sector++)
{
DiskAddress da = disk.getDiskAddress (3, sector);
2016-02-24 21:27:03 +00:00
2020-04-10 23:47:52 +00:00
sectorTypes[da.getBlockNo ()] = catalogSector;
byte[] buffer = disk.readBlock (da);
2017-04-11 21:48:08 +00:00
int b1 = buffer[0] & 0xFF;
int b2 = buffer[1] & 0xFF;
2021-06-18 00:48:38 +00:00
if (b1 == EMPTY_BYTE_VALUE && (b2 == EMPTY_BYTE_VALUE || b2 == 0))
2017-04-11 21:48:08 +00:00
continue;
if (b1 > 31 && b1 != EMPTY_BYTE_VALUE)
2017-04-11 21:48:08 +00:00
break;
if (b2 <= 32 || (b2 > 126 && b2 != EMPTY_BYTE_VALUE))
2017-04-11 21:48:08 +00:00
break;
2016-02-25 23:43:44 +00:00
2016-02-24 21:27:03 +00:00
for (int i = 0; i < buffer.length; i += 32)
{
2017-04-11 21:48:08 +00:00
b1 = buffer[i] & 0xFF;
b2 = buffer[i + 1] & 0xFF;
if (b1 == EMPTY_BYTE_VALUE) // deleted file??
continue;
if (b2 <= 32 || (b2 > 126 && b2 != EMPTY_BYTE_VALUE))
2016-02-24 21:27:03 +00:00
break;
2016-02-27 23:17:34 +00:00
DirectoryEntry entry = new DirectoryEntry (this, buffer, i);
SectorType sectorType = getSectorType (entry.getType ());
for (DiskAddress block : entry.getSectors ())
2020-04-10 23:47:52 +00:00
if (!disk.isBlockEmpty (block))
sectorTypes[block.getBlockNo ()] = sectorType;
2016-02-27 23:17:34 +00:00
DirectoryEntry parent = findParent (entry);
if (parent == null)
2016-02-24 21:27:03 +00:00
{
2016-02-27 23:17:34 +00:00
fileEntries.add (entry);
DefaultMutableTreeNode node = new DefaultMutableTreeNode (entry);
2018-12-23 01:15:51 +00:00
volumeNode.add (node);
2016-02-27 23:17:34 +00:00
node.setAllowsChildren (false);
2016-02-24 21:27:03 +00:00
}
2016-02-27 23:17:34 +00:00
else
parent.add (entry);
2016-02-24 21:27:03 +00:00
}
2015-06-01 09:35:51 +00:00
}
2016-02-25 01:55:14 +00:00
2018-12-23 01:15:51 +00:00
volumeNode.setUserObject (getCatalog ());
makeNodeVisible (volumeNode.getFirstLeaf ());
2015-06-01 09:35:51 +00:00
}
2020-02-08 08:13:28 +00:00
// ---------------------------------------------------------------------------------//
2016-02-25 02:49:12 +00:00
private SectorType getSectorType (String type)
2020-02-08 08:13:28 +00:00
// ---------------------------------------------------------------------------------//
2016-02-25 02:49:12 +00:00
{
if ("COM".equals (type))
return comSector;
if ("DOC".equals (type))
return docSector;
2016-02-25 07:45:24 +00:00
if ("BAS".equals (type))
return basSector;
if ("PRN".equals (type))
return prnSector;
2016-02-26 11:03:29 +00:00
if ("ASM".equals (type))
return asmSector;
2016-02-27 23:44:28 +00:00
if ("OVR".equals (type))
return ovrSector;
2016-02-28 05:41:10 +00:00
if ("MAC".equals (type))
return macSector;
2016-02-25 02:49:12 +00:00
2016-02-28 05:41:10 +00:00
return otherSector;
2016-02-25 02:49:12 +00:00
}
2020-02-08 08:13:28 +00:00
// ---------------------------------------------------------------------------------//
2015-06-01 09:35:51 +00:00
@Override
public List<DiskAddress> getFileSectors (int fileNo)
2020-02-08 08:13:28 +00:00
// ---------------------------------------------------------------------------------//
2015-06-01 09:35:51 +00:00
{
2016-02-25 07:45:24 +00:00
if (fileEntries.size () > 0 && fileEntries.size () > fileNo)
return fileEntries.get (fileNo).getSectors ();
2015-06-01 09:35:51 +00:00
return null;
}
2020-02-08 08:13:28 +00:00
// ---------------------------------------------------------------------------------//
2016-02-24 21:27:03 +00:00
private DirectoryEntry findParent (DirectoryEntry child)
2020-02-08 08:13:28 +00:00
// ---------------------------------------------------------------------------------//
2016-02-24 21:27:03 +00:00
{
2016-02-25 01:55:14 +00:00
for (AppleFileSource entry : fileEntries)
if (((DirectoryEntry) entry).matches (child))
return (DirectoryEntry) entry;
2016-02-24 21:27:03 +00:00
return null;
}
2020-02-08 08:13:28 +00:00
// ---------------------------------------------------------------------------------//
2016-02-26 03:09:14 +00:00
@Override
public DataSource getFormattedSector (DiskAddress da)
2020-02-08 08:13:28 +00:00
// ---------------------------------------------------------------------------------//
2016-02-26 03:09:14 +00:00
{
2020-04-10 23:47:52 +00:00
SectorType type = sectorTypes[da.getBlockNo ()];
byte[] buffer = disk.readBlock (da);
2016-02-26 03:09:14 +00:00
if (type == catalogSector)
return new CPMCatalogSector (disk, buffer, da);
2016-02-26 03:09:14 +00:00
return super.getFormattedSector (da);
}
2020-02-08 08:13:28 +00:00
// ---------------------------------------------------------------------------------//
2016-02-25 01:27:22 +00:00
@Override
public AppleFileSource getCatalog ()
2020-02-08 08:13:28 +00:00
// ---------------------------------------------------------------------------------//
2016-02-25 01:27:22 +00:00
{
String line = "---- --------- --- - - -- -- -- -- ----------------------------"
+ "-------------------\n";
2016-02-25 01:27:22 +00:00
StringBuilder text = new StringBuilder ();
2021-04-18 23:53:37 +00:00
text.append (String.format ("File : %s%n%n", getDisplayPath ()));
2021-07-25 08:30:22 +00:00
text.append ("User Name Typ R S Ex S2 S1 RC Blocks\n");
2016-02-25 01:27:22 +00:00
text.append (line);
2016-02-25 01:55:14 +00:00
for (AppleFileSource entry : fileEntries)
2016-02-25 01:27:22 +00:00
{
2016-02-25 01:55:14 +00:00
text.append (((DirectoryEntry) entry).line ());
2021-07-25 08:30:22 +00:00
text.append ("\n");
2016-02-25 01:27:22 +00:00
}
2016-02-25 02:49:12 +00:00
text.append (line);
2016-12-16 20:45:08 +00:00
if (version != 0)
text.append ("Version: " + version);
2016-02-25 01:27:22 +00:00
return new DefaultAppleFileSource ("CPM Disk ", text.toString (), this);
}
2020-02-08 08:13:28 +00:00
// ---------------------------------------------------------------------------------//
2015-06-01 09:35:51 +00:00
public static boolean isCorrectFormat (AppleDisk disk)
2020-02-08 08:13:28 +00:00
// ---------------------------------------------------------------------------------//
2021-07-24 10:59:00 +00:00
{
boolean debug = false;
disk.setInterleave (3);
// collect catalog sectors
List<DiskAddress> catalog = new ArrayList<> ();
for (int i = 0; i < 8; i++)
catalog.add (disk.getDiskAddress (3, i));
byte[] buffer = disk.readBlocks (catalog);
if (debug)
System.out.println (HexFormatter.format (buffer));
for (int i = 0; i < 2; i++)
{
int start = i * 1024;
int end = start + 1024;
for (int ptr = start; ptr < end; ptr += 32)
{
if (buffer[ptr] == (byte) EMPTY_BYTE_VALUE)
{
2021-07-25 08:30:22 +00:00
if (buffer[ptr + 1] == (byte) EMPTY_BYTE_VALUE //
|| buffer[ptr + 1] == 0) // finished this block
2021-07-24 10:59:00 +00:00
break;
continue; // deleted file?
}
int userNo = buffer[ptr] & 0xFF;
if (userNo > 31)
return false;
for (int j = 1; j < 12; j++)
{
2021-07-25 08:30:22 +00:00
int ch = buffer[ptr + j] & 0x7F; // remove flag
2021-07-24 10:59:00 +00:00
if (ch < 32 || ch > 126) // invalid ascii
return false;
}
if (debug)
{
String fileName = new String (buffer, ptr + 1, 8);
String fileType = new String (buffer, ptr + 9, 3);
System.out.printf ("%2d %s %s%n", userNo, fileName, fileType);
}
}
}
2021-07-25 08:30:22 +00:00
if (debug)
System.out.println ("CP/M disk");
2021-07-24 10:59:00 +00:00
return true;
}
// ---------------------------------------------------------------------------------//
private static boolean isCorrectFormat2 (AppleDisk disk)
// ---------------------------------------------------------------------------------//
2015-06-01 09:35:51 +00:00
{
disk.setInterleave (3);
2016-12-15 00:01:42 +00:00
for (int i = 8; i >= 4; i -= 2)
{
2020-04-10 23:47:52 +00:00
byte[] buffer = disk.readBlock (0, i);
2016-12-15 00:01:42 +00:00
String text = new String (buffer, 16, 24);
if ("DIR ERA TYPESAVEREN USER".equals (text))
2016-12-16 01:19:59 +00:00
{
int version = buffer[41] & 0xFF;
System.out.printf ("CPM version %d%n", version);
2016-12-15 00:01:42 +00:00
return true;
2016-12-16 01:19:59 +00:00
}
2016-12-15 00:01:42 +00:00
}
2016-02-24 12:32:36 +00:00
2015-06-01 09:35:51 +00:00
for (int sector = 0; sector < 8; sector++)
{
2020-04-10 23:47:52 +00:00
byte[] buffer = disk.readBlock (3, sector);
System.out.println (HexFormatter.format (buffer));
2016-12-15 11:21:50 +00:00
// check if entire sector is empty (everything == 0xE5)
if (bufferContainsAll (buffer, (byte) EMPTY_BYTE_VALUE))
2016-12-15 11:21:50 +00:00
break;
2021-06-18 00:48:38 +00:00
int b1 = buffer[0] & 0xFF;
int b2 = buffer[1] & 0xFF;
if (b1 == EMPTY_BYTE_VALUE && (b2 == EMPTY_BYTE_VALUE || b2 == 0))
continue;
if (b1 > 31 && b1 != EMPTY_BYTE_VALUE)
break;
if (b2 < 32 || (b2 > 126 && b2 != EMPTY_BYTE_VALUE))
break;
2015-06-01 09:35:51 +00:00
for (int i = 0; i < buffer.length; i += 32)
{
2021-06-18 00:48:38 +00:00
b1 = buffer[i] & 0xFF;
b2 = buffer[i + 1] & 0xFF;
if (b1 == EMPTY_BYTE_VALUE) // deleted file??
continue;
2016-12-16 01:19:59 +00:00
2021-06-18 00:48:38 +00:00
if (b2 < 32 || (b2 > 126 && b2 != EMPTY_BYTE_VALUE))
2015-06-01 09:35:51 +00:00
return false;
2016-12-15 11:21:50 +00:00
2021-06-18 00:48:38 +00:00
// int val = buffer[i] & 0xFF;
// if (val == EMPTY_BYTE_VALUE)
// {
// if (debug)
// System.out.println ("empty value found - deleted file?");
// break;
// }
// if (val > 31)
// {
// if (debug)
// System.out.println ("val > 31");
// return false;
// }
// for (int j = 1; j <= 8; j++)
// {
// val = buffer[i + j] & 0xFF;
// if (val < 32 || (val > 126 && val != EMPTY_BYTE_VALUE))
// {
// if (debug)
// System.out.println ("val < 32 || val > 126");
// return false;
// }
// }
2015-06-01 09:35:51 +00:00
}
}
return true;
}
2016-12-15 11:21:50 +00:00
2020-02-08 08:13:28 +00:00
// ---------------------------------------------------------------------------------//
2016-12-15 11:21:50 +00:00
private static boolean bufferContainsAll (byte[] buffer, byte value)
2020-02-08 08:13:28 +00:00
// ---------------------------------------------------------------------------------//
2016-12-15 11:21:50 +00:00
{
for (byte b : buffer)
if (b != value)
return false;
return true;
}
2018-12-23 01:15:51 +00:00
2020-02-08 08:13:28 +00:00
// ---------------------------------------------------------------------------------//
2020-05-10 11:28:01 +00:00
// @Override
// public String toString ()
// // ---------------------------------------------------------------------------------//
// {
// StringBuffer text = new StringBuffer ("CPM disk");
// return text.toString ();
// }
2015-06-01 09:35:51 +00:00
}