Revamping CPM detection a bit based on scan results.

This commit is contained in:
Rob Greene
2025-10-09 10:36:37 -05:00
parent ec2ff6fbed
commit aaaae89ccb

View File

@@ -24,6 +24,9 @@ import org.applecommander.device.*;
import org.applecommander.hint.Hint;
import org.applecommander.util.DataBuffer;
import java.util.ArrayList;
import java.util.List;
/**
* Test this disk for a likely CP/M filesystem.
* @see <a href="https://www.seasip.info/Cpm/format22.html">CP/M 2.2</a>
@@ -32,6 +35,7 @@ import org.applecommander.util.DataBuffer;
public class CpmDiskFactory implements DiskFactory {
@Override
public void inspect(Context ctx) {
List<CpmFormatDisk> candidates = new ArrayList<>();
ctx.trackSectorDevice()
.include16Sector(Hint.DOS_SECTOR_ORDER)
.get()
@@ -40,41 +44,57 @@ public class CpmDiskFactory implements DiskFactory {
SkewedTrackSectorDevice.dosToCpmSkew(device),
TrackSectorToBlockAdapter.BlockStyle.CPM);
CpmFormatDisk disk = new CpmFormatDisk(ctx.source.getName(), blockDevice);
if (check(disk)) {
ctx.disks.add(disk);
}
candidates.add(disk);
});
// Look for the one with the largest directory entry
int maxFileCount = -1;
CpmFormatDisk selectedDisk = null;
for (CpmFormatDisk candidate : candidates) {
int count = check(candidate);
if (count != -1 && count > maxFileCount) {
selectedDisk = candidate;
maxFileCount = count;
}
}
if (selectedDisk != null) {
ctx.disks.add(selectedDisk);
}
}
public boolean check(CpmFormatDisk disk) {
public int check(CpmFormatDisk disk) {
DataBuffer entries = DataBuffer.wrap(disk.readCpmFileEntries());
int offset = 0;
int count = 0;
while (offset < entries.limit()) {
// Check if this is an empty directory entry (and ignore it)
int e5count = 0;
for (int i=0; i<CpmFileEntry.ENTRY_LENGTH; i++) {
e5count+= entries.getUnsignedByte(offset+i) == 0xe5 ? 1 : 0;
}
if (e5count != CpmFileEntry.ENTRY_LENGTH) { // Not all bytes were 0xE5
// Check user number. Should be 0-15 or 0xE5
int userNumber = entries.getUnsignedByte(offset);
if (userNumber > 0x1f && userNumber != 0xe5) return false;
// Validate filename has highbit off and is a character
for (int i=0; i<8; i++) {
int ch = entries.getUnsignedByte(offset+1+i);
if (ch < 0x20 || ch > 127) return false;
}
// Extent should be 0-31 (low = 0-31 and high = 0)
int exLow = entries.getUnsignedByte(offset+0xc);
int exHighS2 = entries.getUnsignedByte(offset+0xe);
if (exLow > 31 || exHighS2 > 0) return false;
// Number of used records cannot exceed 0x80
int numberOfRecords = entries.getUnsignedByte(offset+0xf);
if (numberOfRecords > 0x80) return false;
if (e5count == CpmFileEntry.ENTRY_LENGTH) {
// If we find a fully blank entry, assume we are done. Should assist with sector order determination.
return count;
}
// Not all bytes were 0xE5
// Check user number. Should be 0-15 or 0xE5
int userNumber = entries.getUnsignedByte(offset);
if (userNumber > 0x1f && userNumber != 0xe5) return -1;
// Validate filename has highbit off and is a character
for (int i=0; i<8; i++) {
int ch = entries.getUnsignedByte(offset+1+i);
if (ch < 0x20 || ch > 127) return -1;
}
// Extent should be 0-31 (low = 0-31 and high = 0)
int exLow = entries.getUnsignedByte(offset+0xc);
int exHighS2 = entries.getUnsignedByte(offset+0xe);
if (exLow > 31 || exHighS2 > 0) return -1;
// Number of used records cannot exceed 0x80
int numberOfRecords = entries.getUnsignedByte(offset+0xf);
if (numberOfRecords > 0x80) return -1;
count++;
// Next entry
offset+= CpmFileEntry.ENTRY_LENGTH;
}
return true;
return count;
}
}