2016-02-24 09:48:09 +00:00
|
|
|
package com.bytezone.diskbrowser.cpm;
|
|
|
|
|
2016-02-24 21:27:03 +00:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.List;
|
|
|
|
|
2016-02-25 01:27:22 +00:00
|
|
|
import com.bytezone.diskbrowser.applefile.AppleFileSource;
|
2021-07-25 08:30:22 +00:00
|
|
|
import com.bytezone.diskbrowser.applefile.CPMBasicFile;
|
2016-02-26 20:39:06 +00:00
|
|
|
import com.bytezone.diskbrowser.applefile.CPMTextFile;
|
2016-02-25 07:45:24 +00:00
|
|
|
import com.bytezone.diskbrowser.applefile.DefaultAppleFile;
|
2016-02-25 02:49:12 +00:00
|
|
|
import com.bytezone.diskbrowser.disk.AppleDiskAddress;
|
|
|
|
import com.bytezone.diskbrowser.disk.Disk;
|
2016-02-25 01:27:22 +00:00
|
|
|
import com.bytezone.diskbrowser.disk.DiskAddress;
|
|
|
|
import com.bytezone.diskbrowser.disk.FormattedDisk;
|
|
|
|
import com.bytezone.diskbrowser.gui.DataSource;
|
2016-02-24 21:11:14 +00:00
|
|
|
import com.bytezone.diskbrowser.utilities.HexFormatter;
|
2016-02-24 09:48:09 +00:00
|
|
|
|
2017-05-12 10:42:20 +00:00
|
|
|
// File Control Block (FCB)
|
2020-02-08 08:13:28 +00:00
|
|
|
// -----------------------------------------------------------------------------------//
|
|
|
|
class DirectoryEntry implements AppleFileSource
|
|
|
|
// -----------------------------------------------------------------------------------//
|
2016-02-24 09:48:09 +00:00
|
|
|
{
|
2016-02-25 07:45:24 +00:00
|
|
|
private final Disk disk;
|
2016-02-25 01:55:14 +00:00
|
|
|
private final CPMDisk parent;
|
2017-05-12 10:42:20 +00:00
|
|
|
private DataSource appleFile;
|
|
|
|
|
2016-02-24 09:48:09 +00:00
|
|
|
private final int userNumber;
|
|
|
|
private final String name;
|
|
|
|
private final String type;
|
2017-05-12 10:42:20 +00:00
|
|
|
private final int extent;
|
|
|
|
private final int s1; // reserved
|
|
|
|
private final int s2; // reserved
|
|
|
|
private final int recordsUsed; // records used in this extent
|
|
|
|
private final byte[] blockList = new byte[16]; // allocation blocks used
|
|
|
|
|
2020-02-02 10:17:49 +00:00
|
|
|
private final List<DirectoryEntry> entries = new ArrayList<> ();
|
|
|
|
private final List<DiskAddress> blocks = new ArrayList<> ();
|
2016-02-26 23:10:00 +00:00
|
|
|
private final boolean readOnly;
|
|
|
|
private final boolean systemFile;
|
2016-02-24 09:48:09 +00:00
|
|
|
|
2020-02-08 08:13:28 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
DirectoryEntry (CPMDisk parent, byte[] buffer, int offset)
|
|
|
|
// ---------------------------------------------------------------------------------//
|
2016-02-24 09:48:09 +00:00
|
|
|
{
|
2016-02-25 01:55:14 +00:00
|
|
|
this.parent = parent;
|
2016-02-25 07:45:24 +00:00
|
|
|
disk = parent.getDisk ();
|
|
|
|
|
2017-05-12 10:42:20 +00:00
|
|
|
// hi-bits of type are used for flags
|
2016-02-26 23:10:00 +00:00
|
|
|
readOnly = (buffer[offset + 9] & 0x80) != 0;
|
|
|
|
systemFile = (buffer[offset + 10] & 0x80) != 0;
|
|
|
|
|
2017-05-12 10:42:20 +00:00
|
|
|
byte[] typeBuffer = new byte[3];
|
|
|
|
typeBuffer[0] = (byte) (buffer[offset + 9] & 0x7F);
|
|
|
|
typeBuffer[1] = (byte) (buffer[offset + 10] & 0x7F);
|
|
|
|
typeBuffer[2] = (byte) (buffer[offset + 11] & 0x7F);
|
|
|
|
type = new String (typeBuffer).trim ();
|
2016-02-26 23:10:00 +00:00
|
|
|
|
2016-02-24 09:48:09 +00:00
|
|
|
userNumber = buffer[offset] & 0xFF;
|
2021-06-18 00:48:38 +00:00
|
|
|
if (userNumber == 0xE5 && buffer[offset + 1] == 0)
|
|
|
|
name = "";
|
|
|
|
else
|
|
|
|
name = new String (buffer, offset + 1, 8).trim ();
|
2021-07-25 08:30:22 +00:00
|
|
|
|
2017-05-12 10:42:20 +00:00
|
|
|
extent = buffer[offset + 12] & 0xFF;
|
2016-02-24 09:48:09 +00:00
|
|
|
s2 = buffer[offset + 13] & 0xFF;
|
|
|
|
s1 = buffer[offset + 14] & 0xFF;
|
2017-05-12 10:42:20 +00:00
|
|
|
recordsUsed = buffer[offset + 15] & 0xFF;
|
2016-02-24 09:48:09 +00:00
|
|
|
System.arraycopy (buffer, offset + 16, blockList, 0, 16);
|
2016-02-25 02:49:12 +00:00
|
|
|
|
|
|
|
Disk disk = parent.getDisk ();
|
|
|
|
for (byte b : blockList)
|
|
|
|
{
|
|
|
|
if (b == 0)
|
|
|
|
break;
|
|
|
|
|
2016-02-27 23:17:34 +00:00
|
|
|
int blockNumber;
|
|
|
|
|
|
|
|
if ((b & 0x80) == 0)
|
|
|
|
blockNumber = (b * 4 + 48);
|
|
|
|
else
|
|
|
|
blockNumber = (b & 0x7F) * 4;
|
|
|
|
|
2016-02-25 02:49:12 +00:00
|
|
|
for (int i = 0; i < 4; i++)
|
2016-07-17 22:35:18 +00:00
|
|
|
blocks.add (new AppleDiskAddress (disk, blockNumber + i));
|
2016-02-25 02:49:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-08 08:13:28 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
String getType ()
|
|
|
|
// ---------------------------------------------------------------------------------//
|
2016-02-25 02:49:12 +00:00
|
|
|
{
|
|
|
|
return type;
|
2016-02-24 09:48:09 +00:00
|
|
|
}
|
|
|
|
|
2020-02-08 08:13:28 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
boolean matches (DirectoryEntry directoryEntry)
|
|
|
|
// ---------------------------------------------------------------------------------//
|
2016-02-24 21:11:14 +00:00
|
|
|
{
|
|
|
|
return userNumber == directoryEntry.userNumber && name.equals (directoryEntry.name)
|
|
|
|
&& type.equals (directoryEntry.type);
|
|
|
|
}
|
|
|
|
|
2020-02-08 08:13:28 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
void add (DirectoryEntry entry)
|
|
|
|
// ---------------------------------------------------------------------------------//
|
2016-02-24 21:27:03 +00:00
|
|
|
{
|
|
|
|
entries.add (entry);
|
2016-02-25 02:49:12 +00:00
|
|
|
|
|
|
|
Disk disk = parent.getDisk ();
|
|
|
|
for (byte b : entry.blockList)
|
|
|
|
{
|
|
|
|
if (b == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
int blockNumber = b * 4 + 48;
|
|
|
|
for (int i = 0; i < 4; i++)
|
2016-07-17 22:35:18 +00:00
|
|
|
blocks.add (new AppleDiskAddress (disk, blockNumber + i));
|
2016-02-25 02:49:12 +00:00
|
|
|
}
|
2016-02-24 21:27:03 +00:00
|
|
|
}
|
|
|
|
|
2020-02-08 08:13:28 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2016-02-25 21:49:22 +00:00
|
|
|
@Override
|
|
|
|
public boolean contains (DiskAddress da)
|
2020-02-08 08:13:28 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2016-02-25 21:49:22 +00:00
|
|
|
{
|
|
|
|
for (DiskAddress sector : blocks)
|
2016-07-19 01:24:36 +00:00
|
|
|
if (sector.matches (da))
|
2016-02-25 21:49:22 +00:00
|
|
|
return true;
|
2021-06-18 00:48:38 +00:00
|
|
|
|
2016-02-25 21:49:22 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-02-08 08:13:28 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
String line ()
|
|
|
|
// ---------------------------------------------------------------------------------//
|
2016-02-25 01:27:22 +00:00
|
|
|
{
|
2016-02-25 02:49:12 +00:00
|
|
|
String bytes = HexFormatter.getHexString (blockList, 0, 16);
|
|
|
|
bytes = bytes.replaceAll ("00", " ");
|
2016-02-26 03:51:15 +00:00
|
|
|
|
2016-02-26 23:10:00 +00:00
|
|
|
char ro = readOnly ? '*' : ' ';
|
|
|
|
char sf = systemFile ? '*' : ' ';
|
|
|
|
|
|
|
|
String text =
|
|
|
|
String.format ("%3d %-8s %-3s %s %s %02X %02X %02X %02X %s",
|
2017-05-12 10:42:20 +00:00
|
|
|
userNumber, name, type, ro, sf, extent, s2, s1, recordsUsed, bytes);
|
2016-02-25 02:49:12 +00:00
|
|
|
for (DirectoryEntry entry : entries)
|
2016-02-26 03:09:14 +00:00
|
|
|
text = text + "\n" + entry.line ();
|
|
|
|
|
2017-05-12 10:42:20 +00:00
|
|
|
if (extent != 0)
|
2016-02-26 03:51:15 +00:00
|
|
|
text = " " + text.substring (20);
|
|
|
|
|
2016-02-25 02:49:12 +00:00
|
|
|
return text;
|
2016-02-25 01:27:22 +00:00
|
|
|
}
|
|
|
|
|
2020-02-08 08:13:28 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
String toDetailedString ()
|
|
|
|
// ---------------------------------------------------------------------------------//
|
2016-02-24 09:48:09 +00:00
|
|
|
{
|
|
|
|
StringBuilder text = new StringBuilder ();
|
|
|
|
|
|
|
|
text.append (String.format ("User number .... %d%n", userNumber));
|
|
|
|
text.append (String.format ("File name ...... %s%n", name + "." + type));
|
2017-05-12 10:42:20 +00:00
|
|
|
text.append (String.format ("Extents lo ..... %d%n", extent));
|
2016-02-24 09:48:09 +00:00
|
|
|
text.append (String.format ("Extents hi ..... %d%n", s2));
|
|
|
|
text.append (String.format ("Reserved ....... %d%n", s1));
|
|
|
|
|
2017-05-12 10:42:20 +00:00
|
|
|
int blocks = ((recordsUsed & 0xF0) >> 3) + (((recordsUsed & 0x0F) + 7) / 8);
|
|
|
|
text.append (String.format ("Records ........ %02X (%d)%n", recordsUsed, blocks));
|
2016-02-24 12:32:36 +00:00
|
|
|
|
|
|
|
String bytes = HexFormatter.getHexString (blockList, 0, 16);
|
|
|
|
text.append (String.format ("Allocation ..... %s%n", bytes));
|
2016-02-24 09:48:09 +00:00
|
|
|
|
2016-02-24 21:27:03 +00:00
|
|
|
for (DirectoryEntry entry : entries)
|
|
|
|
{
|
|
|
|
bytes = HexFormatter.getHexString (entry.blockList, 0, 16);
|
|
|
|
text.append (String.format (" %s%n", bytes));
|
|
|
|
}
|
|
|
|
|
2016-02-24 09:48:09 +00:00
|
|
|
return text.toString ();
|
|
|
|
}
|
2016-02-25 01:27:22 +00:00
|
|
|
|
2020-02-08 08:13:28 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2016-02-25 01:27:22 +00:00
|
|
|
@Override
|
|
|
|
public String getUniqueName ()
|
2020-02-08 08:13:28 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2016-02-25 01:27:22 +00:00
|
|
|
{
|
|
|
|
return name + "." + type;
|
|
|
|
}
|
|
|
|
|
2020-02-08 08:13:28 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2016-02-25 01:27:22 +00:00
|
|
|
@Override
|
|
|
|
public DataSource getDataSource ()
|
2020-02-08 08:13:28 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2016-02-25 01:27:22 +00:00
|
|
|
{
|
2016-02-25 07:45:24 +00:00
|
|
|
if (appleFile != null)
|
|
|
|
return appleFile;
|
|
|
|
|
2020-04-10 23:47:52 +00:00
|
|
|
byte[] buffer = disk.readBlocks (blocks);
|
2016-02-26 11:03:29 +00:00
|
|
|
|
2016-02-25 07:45:24 +00:00
|
|
|
if (buffer.length == 0)
|
|
|
|
{
|
|
|
|
appleFile = new DefaultAppleFile (name, buffer);
|
|
|
|
return appleFile;
|
|
|
|
}
|
2016-02-26 11:03:29 +00:00
|
|
|
|
2017-05-12 10:42:20 +00:00
|
|
|
DirectoryEntry entry = recordsUsed == 0x80 ? entries.get (entries.size () - 1) : this;
|
|
|
|
int len = (entry.extent * 128 + entry.recordsUsed) * 128;
|
2019-03-05 07:08:34 +00:00
|
|
|
if (len > buffer.length)
|
|
|
|
{
|
|
|
|
System.out.println ("too big"); // see tdbt12d1.sdk
|
|
|
|
len = buffer.length;
|
|
|
|
}
|
2016-02-26 11:03:29 +00:00
|
|
|
|
|
|
|
byte[] exactBuffer = new byte[len];
|
|
|
|
System.arraycopy (buffer, 0, exactBuffer, 0, len);
|
|
|
|
|
2016-02-26 21:23:05 +00:00
|
|
|
int max = Math.min (256, exactBuffer.length);
|
|
|
|
int count = 0;
|
|
|
|
for (int i = 1; i < max; i++)
|
|
|
|
{
|
|
|
|
if (exactBuffer[i - 1] == 0x0D && exactBuffer[i] == 0x0A)
|
|
|
|
++count;
|
|
|
|
}
|
|
|
|
|
2016-02-26 23:10:00 +00:00
|
|
|
if ("COM".equals (type))
|
|
|
|
appleFile = new DefaultAppleFile (name, exactBuffer, "COM File");
|
|
|
|
else if ("DVR".equals (type))
|
|
|
|
appleFile = new DefaultAppleFile (name, exactBuffer, "DVR File");
|
|
|
|
else if ("ASM".equals (type) || "DOC".equals (type) || "TXT".equals (type)
|
|
|
|
|| count > 2)
|
2016-02-26 20:39:06 +00:00
|
|
|
appleFile = new CPMTextFile (name, exactBuffer);
|
2021-07-25 08:30:22 +00:00
|
|
|
else if ("BAS".equals (type))
|
|
|
|
appleFile = new CPMBasicFile (name, exactBuffer);
|
2016-02-26 11:03:29 +00:00
|
|
|
else
|
|
|
|
appleFile = new DefaultAppleFile (name, exactBuffer, "CPM File : " + type);
|
|
|
|
|
2016-02-25 07:45:24 +00:00
|
|
|
return appleFile;
|
2016-02-25 01:27:22 +00:00
|
|
|
}
|
|
|
|
|
2020-02-08 08:13:28 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2016-02-25 01:27:22 +00:00
|
|
|
@Override
|
|
|
|
public List<DiskAddress> getSectors ()
|
2020-02-08 08:13:28 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2016-02-25 01:27:22 +00:00
|
|
|
{
|
2016-02-25 02:49:12 +00:00
|
|
|
return blocks;
|
2016-02-25 01:27:22 +00:00
|
|
|
}
|
|
|
|
|
2020-02-08 08:13:28 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2016-02-25 01:27:22 +00:00
|
|
|
@Override
|
|
|
|
public FormattedDisk getFormattedDisk ()
|
2020-02-08 08:13:28 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2016-02-25 01:27:22 +00:00
|
|
|
{
|
2016-02-25 01:55:14 +00:00
|
|
|
return parent;
|
|
|
|
}
|
|
|
|
|
2020-02-08 08:13:28 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2016-02-25 01:55:14 +00:00
|
|
|
@Override
|
|
|
|
public String toString ()
|
2020-02-08 08:13:28 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
2016-02-25 01:55:14 +00:00
|
|
|
{
|
|
|
|
return name + "." + type;
|
2016-02-25 01:27:22 +00:00
|
|
|
}
|
2016-02-24 09:48:09 +00:00
|
|
|
}
|