Revamping how TrackSectorDevices and BlockDevices are discovered to consolidate some of the logic. Before this, every DiskFactory had some form of logic to suss out the details.

This commit is contained in:
Rob Greene
2025-09-04 16:19:45 -05:00
parent e3601c46c9
commit 49173b327f
8 changed files with 266 additions and 300 deletions

View File

@@ -33,6 +33,33 @@ import org.applecommander.util.DataBuffer;
import java.util.ArrayList;
import java.util.List;
/**
* The DiskFactory inspects a given Source inspect it to see if it matches filesystem structure(s).
* Invoke via {@link Disks#inspect(Source)} which will return a Context. The Context _can be empty_.
* If this is the case, devices can be created via the {@link Context#blockDevice()} and
* {@link Context#trackSectorDevice()} builders.
* <br/>
* The builders follow the observation about what each filesystem currently uses:
* <pre>
* OS 13-sector 16-sector 800K HDV
* ========= ========= ========== ====== ========
* CP/M - CP/M - -
* DOS Physical DOS Pascal -
* Gutenberg - DOS - -
* NakeDOS - Physical - -
* Pascal - Pascal Pascal -
* ProDOS - Pascal Pascal Pascal
* RDOS Physical DOS - -
* </pre>
* <ul>
* <li>13-sector disks are only track/sector devices. (And RDOS has a unique mapping to 256 byte blocks).</li>
* <li>16-sector disks have a variety of sector mappings. It can also double as a "standard" 512 byte block,
* as well as a unique CP/M 1024 block device.</li>
* <li>800K disks are only standard 512 byte block (Prodos or Pascal). OzDOS and UniDOS do have unique
* mappings to track and sector, but logically, they are block devices.</li>
* <li>Anything else (larger than 800K) are "hard-disk" images and only blocks.</li>
* </ul>
*/
public interface DiskFactory {
void inspect(Context ctx);
@@ -40,9 +67,6 @@ public interface DiskFactory {
public final Source source;
public final NibbleTrackReaderWriter nibbleTrackReaderWriter;
public final List<FormattedDisk> disks = new ArrayList<>();
// Note: These are only set if we *KNOW* what they are. Except DSK images, where both will be set.
public final BlockDevice blockDevice;
public final TrackSectorDevice sectorDevice;
public Context(Source source) {
this.source = source;
@@ -50,26 +74,11 @@ public interface DiskFactory {
/* Does it have the WOZ1 or WOZ2 header? */
int signature = source.readBytes(0, 4).readInt();
if (WozImage.WOZ1_MAGIC == signature || WozImage.WOZ2_MAGIC == signature) {
blockDevice = null;
nibbleTrackReaderWriter = new WozImage(source);
sectorDevice = identifySectorsPerTrack(nibbleTrackReaderWriter);
} else if (source.is(Hint.NIBBLE_SECTOR_ORDER) || source.isApproxEQ(DiskConstants.APPLE_140KB_NIBBLE_DISK)) {
blockDevice = null;
nibbleTrackReaderWriter = new NibbleImage(source);
sectorDevice = identifySectorsPerTrack(nibbleTrackReaderWriter);
} else if (source.is(Hint.PRODOS_BLOCK_ORDER) || source.getSize() > DiskConstants.APPLE_400KB_DISK || source.extensionLike("po")) {
nibbleTrackReaderWriter = null;
blockDevice = new ProdosOrderedBlockDevice(source, BlockDevice.STANDARD_BLOCK_SIZE);
sectorDevice = null;
} else if (source.is(Hint.DOS_SECTOR_ORDER) || source.extensionLike("do")) {
nibbleTrackReaderWriter = null;
blockDevice = null;
sectorDevice = new DosOrderedTrackSectorDevice(source, Hint.DOS_SECTOR_ORDER);
} else {
nibbleTrackReaderWriter = null;
// Could be either - most likely the nebulous "dsk" extension
blockDevice = new ProdosOrderedBlockDevice(source, BlockDevice.STANDARD_BLOCK_SIZE);
sectorDevice = new DosOrderedTrackSectorDevice(source);
}
}
@@ -111,5 +120,141 @@ public interface DiskFactory {
// Failure
return null;
}
public BlockDeviceBuilder blockDevice() {
return new BlockDeviceBuilder(this);
}
public static class BlockDeviceBuilder {
private Context ctx;
private List<BlockDevice> devices = new ArrayList<>();
private BlockDeviceBuilder(Context ctx) {
this.ctx = ctx;
}
public BlockDeviceBuilder include16Sector(Hint hint) {
if (ctx.nibbleTrackReaderWriter != null) {
TrackSectorDevice nibble = ctx.identifySectorsPerTrack(ctx.nibbleTrackReaderWriter);
if (nibble != null) {
TrackSectorDevice converted = switch (hint) {
case DOS_SECTOR_ORDER -> SkewedTrackSectorDevice.physicalToDosSkew(nibble);
case PRODOS_BLOCK_ORDER -> SkewedTrackSectorDevice.physicalToPascalSkew(nibble);
case NIBBLE_SECTOR_ORDER -> nibble;
default -> throw new RuntimeException("wrong hint type: " + hint);
};
devices.add(new TrackSectorToBlockAdapter(converted, TrackSectorToBlockAdapter.BlockStyle.PRODOS));
}
}
else if (ctx.source.isApproxEQ(DiskConstants.APPLE_140KB_DISK)) {
if (ctx.source.is(Hint.DOS_SECTOR_ORDER) || ctx.source.extensionLike("do")) {
TrackSectorDevice doDevice = new DosOrderedTrackSectorDevice(ctx.source, Hint.DOS_SECTOR_ORDER);
TrackSectorDevice poDevice = SkewedTrackSectorDevice.dosToPascalSkew(doDevice);
devices.add(new TrackSectorToBlockAdapter(poDevice, TrackSectorToBlockAdapter.BlockStyle.PRODOS));
}
else if (ctx.source.is(Hint.PRODOS_BLOCK_ORDER) || ctx.source.extensionLike("po")) {
devices.add(new ProdosOrderedBlockDevice(ctx.source, BlockDevice.STANDARD_BLOCK_SIZE));
}
else {
devices.add(new ProdosOrderedBlockDevice(ctx.source, BlockDevice.STANDARD_BLOCK_SIZE));
TrackSectorDevice doDevice = new DosOrderedTrackSectorDevice(ctx.source, Hint.DOS_SECTOR_ORDER);
TrackSectorDevice poDevice = SkewedTrackSectorDevice.dosToPascalSkew(doDevice);
devices.add(new TrackSectorToBlockAdapter(poDevice, TrackSectorToBlockAdapter.BlockStyle.PRODOS));
}
}
return this;
}
public BlockDeviceBuilder include800K() {
if (ctx.source.isApproxEQ(DiskConstants.APPLE_800KB_DISK)) {
devices.add(new ProdosOrderedBlockDevice(ctx.source, BlockDevice.STANDARD_BLOCK_SIZE));
}
return this;
}
public BlockDeviceBuilder includeHDV() {
if (ctx.source.getSize() > DiskConstants.APPLE_800KB_DISK) {
devices.add(new ProdosOrderedBlockDevice(ctx.source, BlockDevice.STANDARD_BLOCK_SIZE));
}
return this;
}
public List<BlockDevice> get() {
return devices;
}
}
public TrackSectorDeviceBuilder trackSectorDevice() {
return new TrackSectorDeviceBuilder(this);
}
public static class TrackSectorDeviceBuilder {
private Context ctx;
private List<TrackSectorDevice> devices = new ArrayList<>();
private TrackSectorDeviceBuilder(Context ctx) {
this.ctx = ctx;
}
public TrackSectorDeviceBuilder include13Sector() {
if (ctx.nibbleTrackReaderWriter != null) {
TrackSectorDevice nibble = ctx.identifySectorsPerTrack(ctx.nibbleTrackReaderWriter);
if (nibble != null && nibble.getGeometry().sectorsPerTrack() == 13) {
devices.add(nibble);
}
}
return this;
}
public TrackSectorDeviceBuilder include16Sector(Hint hint) {
if (ctx.nibbleTrackReaderWriter != null) {
TrackSectorDevice nibble = ctx.identifySectorsPerTrack(ctx.nibbleTrackReaderWriter);
if (nibble != null && nibble.getGeometry().sectorsPerTrack() == 16) {
TrackSectorDevice converted = switch (hint) {
case DOS_SECTOR_ORDER -> SkewedTrackSectorDevice.physicalToDosSkew(nibble);
case PRODOS_BLOCK_ORDER -> SkewedTrackSectorDevice.physicalToPascalSkew(nibble);
case NIBBLE_SECTOR_ORDER -> nibble;
default -> throw new RuntimeException("wrong hint type: " + hint);
};
devices.add(converted);
}
} else if (ctx.source.isApproxEQ(DiskConstants.APPLE_140KB_DISK)) {
TrackSectorDevice doDevice = null;
TrackSectorDevice poDevice = null;
if (ctx.source.is(Hint.DOS_SECTOR_ORDER) || ctx.source.extensionLike("do")) {
doDevice = new DosOrderedTrackSectorDevice(ctx.source, Hint.DOS_SECTOR_ORDER);
}
else if (ctx.source.is(Hint.PRODOS_BLOCK_ORDER) || ctx.source.extensionLike("po")) {
poDevice = new DosOrderedTrackSectorDevice(ctx.source, Hint.PRODOS_BLOCK_ORDER);
}
else {
doDevice = new DosOrderedTrackSectorDevice(ctx.source, Hint.DOS_SECTOR_ORDER);
poDevice = new DosOrderedTrackSectorDevice(ctx.source, Hint.PRODOS_BLOCK_ORDER);
}
switch (hint) {
case DOS_SECTOR_ORDER -> {
if (doDevice != null) {
devices.add(doDevice);
}
if (poDevice != null) {
TrackSectorDevice tmp = SkewedTrackSectorDevice.pascalToPhysicalSkew(poDevice);
devices.add(SkewedTrackSectorDevice.physicalToDosSkew(tmp));
}
}
case PRODOS_BLOCK_ORDER -> {
if (doDevice != null) {
TrackSectorDevice tmp = SkewedTrackSectorDevice.dosToPhysicalSkew(doDevice);
devices.add(SkewedTrackSectorDevice.physicalToPascalSkew(tmp));
}
if (poDevice != null) {
devices.add(poDevice);
}
}
case NIBBLE_SECTOR_ORDER -> {
if (doDevice != null) {
devices.add(SkewedTrackSectorDevice.dosToPhysicalSkew(doDevice));
}
if (poDevice != null) {
devices.add(SkewedTrackSectorDevice.pascalToPhysicalSkew(poDevice));
}
}
}
}
return this;
}
public List<TrackSectorDevice> get() {
return devices;
}
}
}
}

View File

@@ -19,15 +19,11 @@
*/
package com.webcodepro.applecommander.storage.os.cpm;
import com.webcodepro.applecommander.storage.DiskConstants;
import com.webcodepro.applecommander.storage.DiskFactory;
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>
@@ -36,42 +32,18 @@ import java.util.List;
public class CpmDiskFactory implements DiskFactory {
@Override
public void inspect(Context ctx) {
List<TrackSectorDevice> devices = new ArrayList<>();
if (ctx.sectorDevice != null) {
if (ctx.sectorDevice.is(Hint.NIBBLE_SECTOR_ORDER)) {
// cheating so I don't need to figure out physical to CP/M skew!
TrackSectorDevice dosSkew = SkewedTrackSectorDevice.physicalToDosSkew(ctx.sectorDevice);
devices.add(SkewedTrackSectorDevice.dosToCpmSkew(dosSkew));
}
else if (ctx.sectorDevice.is(Hint.DOS_SECTOR_ORDER)) {
devices.add(SkewedTrackSectorDevice.dosToCpmSkew(ctx.sectorDevice));
}
else if (ctx.sectorDevice.is(Hint.PRODOS_BLOCK_ORDER)) {
devices.add(SkewedTrackSectorDevice.pascalToCpmSkew(ctx.sectorDevice));
}
else {
// Presumably a DSK image, so DO and PO are possibilities
devices.add(SkewedTrackSectorDevice.dosToCpmSkew(ctx.sectorDevice));
devices.add(SkewedTrackSectorDevice.pascalToCpmSkew(ctx.sectorDevice));
}
}
else if (ctx.blockDevice != null) {
if (ctx.blockDevice.getGeometry().blocksOnDevice() == 280) {
TrackSectorDevice device = new BlockToTrackSectorAdapter(ctx.blockDevice,
new ProdosBlockToTrackSectorAdapterStrategy());
devices.add(SkewedTrackSectorDevice.pascalToCpmSkew(device));
}
}
// Any devices in the list are expected to be in CP/M block order
devices.forEach(device -> {
if (device.getGeometry().sectorsPerDisk() == 560) {
BlockDevice blockDevice = new TrackSectorToBlockAdapter(device, TrackSectorToBlockAdapter.BlockStyle.CPM);
CpmFormatDisk disk = new CpmFormatDisk(ctx.source.getName(), blockDevice);
if (check(disk)) {
ctx.disks.add(disk);
}
}
});
ctx.trackSectorDevice()
.include16Sector(Hint.DOS_SECTOR_ORDER)
.get()
.forEach(device -> {
BlockDevice blockDevice = new TrackSectorToBlockAdapter(
SkewedTrackSectorDevice.dosToCpmSkew(device),
TrackSectorToBlockAdapter.BlockStyle.CPM);
CpmFormatDisk disk = new CpmFormatDisk(ctx.source.getName(), blockDevice);
if (check(disk)) {
ctx.disks.add(disk);
}
});
}
public boolean check(CpmFormatDisk disk) {

View File

@@ -21,14 +21,11 @@ package com.webcodepro.applecommander.storage.os.dos33;
import com.webcodepro.applecommander.storage.DiskFactory;
import org.applecommander.device.BlockToTrackSectorAdapter;
import org.applecommander.device.ProdosBlockToTrackSectorAdapterStrategy;
import org.applecommander.device.SkewedTrackSectorDevice;
import org.applecommander.device.TrackSectorDevice;
import org.applecommander.hint.Hint;
import org.applecommander.os.dos.OzdosAdapterStrategy;
import org.applecommander.os.dos.UnidosAdapterStrategy;
import org.applecommander.util.DataBuffer;
import static com.webcodepro.applecommander.storage.DiskConstants.*;
import static com.webcodepro.applecommander.storage.os.dos33.DosFormatDisk.*;
import java.util.*;
@@ -37,53 +34,45 @@ public class DosDiskFactory implements DiskFactory {
@Override
public void inspect(Context ctx) {
// It seems easiest to gather all possibilities first...
List<TrackSectorDevice> devices = new ArrayList<>();
// We need DOS ordered...
if (ctx.sectorDevice != null) {
if (ctx.sectorDevice.is(Hint.NIBBLE_SECTOR_ORDER)) {
if (ctx.sectorDevice.getGeometry().sectorsPerTrack() == 13) {
// 13-sector = DOS 3.2 = physical sector order
devices.add(ctx.sectorDevice);
}
else {
// 16-sector = DOS 3.3 = DOS sector order
devices.add(SkewedTrackSectorDevice.physicalToDosSkew(ctx.sectorDevice));
}
}
else if (ctx.sectorDevice.is(Hint.DOS_SECTOR_ORDER)) {
devices.add(ctx.sectorDevice);
}
else if (ctx.sectorDevice.is(Hint.PRODOS_BLOCK_ORDER)) {
// Cheating a bit...
TrackSectorDevice tmp = SkewedTrackSectorDevice.pascalToPhysicalSkew(ctx.sectorDevice);
devices.add(SkewedTrackSectorDevice.physicalToDosSkew(tmp));
}
else {
// Likely a DSK image, need to pick between DO and PO...
TrackSectorDevice device1 = ctx.sectorDevice;
// Cheating a bit...
TrackSectorDevice tmp = SkewedTrackSectorDevice.pascalToPhysicalSkew(ctx.sectorDevice);
TrackSectorDevice device2 = SkewedTrackSectorDevice.physicalToDosSkew(tmp);
final List<TrackSectorDevice> devices = new ArrayList<>();
int count1 = count(device1);
int count2 = count(device2);
// Note this assumes DO was the first device in the list to give it an edge
if (count1 >= count2) devices.add(device1);
else devices.add(device2);
}
}
else if (ctx.blockDevice != null) {
if (ctx.blockDevice.getGeometry().blocksOnDevice() == PRODOS_BLOCKS_ON_140KB_DISK) {
devices.add(new BlockToTrackSectorAdapter(ctx.blockDevice, new ProdosBlockToTrackSectorAdapterStrategy()));
}
else if (ctx.blockDevice.getGeometry().blocksOnDevice() == PRODOS_BLOCKS_ON_800KB_DISK) {
devices.add(new BlockToTrackSectorAdapter(ctx.blockDevice, UnidosAdapterStrategy.UNIDOS_DISK_1));
devices.add(new BlockToTrackSectorAdapter(ctx.blockDevice, UnidosAdapterStrategy.UNIDOS_DISK_2));
devices.add(new BlockToTrackSectorAdapter(ctx.blockDevice, OzdosAdapterStrategy.OZDOS_DISK_1));
devices.add(new BlockToTrackSectorAdapter(ctx.blockDevice, OzdosAdapterStrategy.OZDOS_DISK_2));
// Look for DOS on 13-sector and 16-sector devices:
devices.addAll(ctx.trackSectorDevice()
.include13Sector()
.include16Sector(Hint.DOS_SECTOR_ORDER)
.get());
if (devices.size() == 2) {
// Likely a DSK image, need to pick between DO and PO...
TrackSectorDevice doDevice = null;
TrackSectorDevice poDevice = null;
for (TrackSectorDevice device : devices) {
if (device.is(Hint.DOS_SECTOR_ORDER) && doDevice == null) {
doDevice = device;
}
else if (device.is(Hint.PRODOS_BLOCK_ORDER) && poDevice == null) {
poDevice = device;
}
}
if (doDevice == null || poDevice == null) {
throw new RuntimeException("unexpected situation: device is neither PO or DO -or- we have more than 1");
}
int doCount = count(doDevice);
int poCount = count(poDevice);
// Keep the one with the most catalog sectors (slight edge to DO)
devices.remove(doCount >= poCount ? poDevice : doDevice);
}
// Look for 800K block devices to find UniDOS or OzDOS:
ctx.blockDevice()
.include800K()
.get()
.forEach(blockDevice -> {
devices.add(new BlockToTrackSectorAdapter(blockDevice, UnidosAdapterStrategy.UNIDOS_DISK_1));
devices.add(new BlockToTrackSectorAdapter(blockDevice, UnidosAdapterStrategy.UNIDOS_DISK_2));
devices.add(new BlockToTrackSectorAdapter(blockDevice, OzdosAdapterStrategy.OZDOS_DISK_1));
devices.add(new BlockToTrackSectorAdapter(blockDevice, OzdosAdapterStrategy.OZDOS_DISK_2));
});
// ... and then test for DOS VTOC etc. Passing track number along to hopefully handle it later!
for (TrackSectorDevice device : devices) {
if (check(device)) {

View File

@@ -21,53 +21,23 @@ package com.webcodepro.applecommander.storage.os.gutenberg;
import com.webcodepro.applecommander.storage.DiskConstants;
import com.webcodepro.applecommander.storage.DiskFactory;
import org.applecommander.device.BlockToTrackSectorAdapter;
import org.applecommander.device.ProdosBlockToTrackSectorAdapterStrategy;
import org.applecommander.device.SkewedTrackSectorDevice;
import org.applecommander.device.TrackSectorDevice;
import org.applecommander.hint.Hint;
import org.applecommander.util.DataBuffer;
import java.util.ArrayList;
import java.util.List;
import static com.webcodepro.applecommander.storage.os.gutenberg.GutenbergFormatDisk.*;
public class GutenbergDiskFactory implements DiskFactory {
@Override
public void inspect(Context ctx) {
List<TrackSectorDevice> devices = new ArrayList<>();
// We need DOS ordered...
if (ctx.sectorDevice != null) {
if (ctx.sectorDevice.is(Hint.NIBBLE_SECTOR_ORDER)) {
devices.add(SkewedTrackSectorDevice.physicalToDosSkew(ctx.sectorDevice));
}
else if (ctx.sectorDevice.is(Hint.DOS_SECTOR_ORDER)) {
devices.add(ctx.sectorDevice);
}
else if (ctx.sectorDevice.is(Hint.PRODOS_BLOCK_ORDER)) {
// Cheating a bit...
TrackSectorDevice tmp = SkewedTrackSectorDevice.pascalToPhysicalSkew(ctx.sectorDevice);
devices.add(SkewedTrackSectorDevice.physicalToDosSkew(tmp));
}
else {
// Likely a DSK image
devices.add(ctx.sectorDevice);
// Cheating a bit...
TrackSectorDevice tmp = SkewedTrackSectorDevice.pascalToPhysicalSkew(ctx.sectorDevice);
devices.add(SkewedTrackSectorDevice.physicalToDosSkew(tmp));
}
}
else if (ctx.blockDevice != null) {
TrackSectorDevice po = new BlockToTrackSectorAdapter(ctx.blockDevice, new ProdosBlockToTrackSectorAdapterStrategy());
TrackSectorDevice tmp = SkewedTrackSectorDevice.pascalToPhysicalSkew(po);
devices.add(SkewedTrackSectorDevice.physicalToDosSkew(tmp));
}
devices.forEach(device -> {
if (check(device)) {
ctx.disks.add(new GutenbergFormatDisk(ctx.source.getName(), device));
}
});
ctx.trackSectorDevice()
.include16Sector(Hint.DOS_SECTOR_ORDER)
.get()
.forEach(device -> {
if (check(device)) {
ctx.disks.add(new GutenbergFormatDisk(ctx.source.getName(), device));
}
});
}
public boolean check(TrackSectorDevice order) {

View File

@@ -20,46 +20,21 @@
package com.webcodepro.applecommander.storage.os.nakedos;
import com.webcodepro.applecommander.storage.DiskFactory;
import org.applecommander.device.BlockToTrackSectorAdapter;
import org.applecommander.device.ProdosBlockToTrackSectorAdapterStrategy;
import org.applecommander.device.SkewedTrackSectorDevice;
import org.applecommander.device.TrackSectorDevice;
import org.applecommander.hint.Hint;
import org.applecommander.util.DataBuffer;
import java.util.ArrayList;
import java.util.List;
public class NakedosDiskFactory implements DiskFactory {
@Override
public void inspect(Context ctx) {
List<TrackSectorDevice> devices = new ArrayList<>();
// NakeDOS expects "physical" sector ordering
if (ctx.sectorDevice != null) {
if (ctx.sectorDevice.is(Hint.NIBBLE_SECTOR_ORDER)) {
devices.add(ctx.sectorDevice);
}
else if (ctx.sectorDevice.is(Hint.DOS_SECTOR_ORDER)) {
devices.add(SkewedTrackSectorDevice.dosToPhysicalSkew(ctx.sectorDevice));
}
else if (ctx.sectorDevice.is(Hint.PRODOS_BLOCK_ORDER)) {
devices.add(SkewedTrackSectorDevice.pascalToPhysicalSkew(ctx.sectorDevice));
}
else {
devices.add(SkewedTrackSectorDevice.dosToPhysicalSkew(ctx.sectorDevice));
devices.add(SkewedTrackSectorDevice.pascalToPhysicalSkew(ctx.sectorDevice));
}
}
else if (ctx.blockDevice != null) {
devices.add(SkewedTrackSectorDevice.pascalToPhysicalSkew(new BlockToTrackSectorAdapter(
ctx.blockDevice, new ProdosBlockToTrackSectorAdapterStrategy())));
}
devices.forEach(device -> {
if (check(device)) {
ctx.disks.add(new NakedosFormatDisk(ctx.source.getName(), device));
}
});
ctx.trackSectorDevice()
.include16Sector(Hint.NIBBLE_SECTOR_ORDER)
.get()
.forEach(device -> {
if (check(device)) {
ctx.disks.add(new NakedosFormatDisk(ctx.source.getName(), device));
}
});
}
public boolean check(TrackSectorDevice device) {

View File

@@ -22,50 +22,24 @@ package com.webcodepro.applecommander.storage.os.pascal;
import com.webcodepro.applecommander.storage.DiskConstants;
import com.webcodepro.applecommander.storage.DiskFactory;
import org.applecommander.device.BlockDevice;
import org.applecommander.device.SkewedTrackSectorDevice;
import org.applecommander.device.TrackSectorDevice;
import org.applecommander.device.TrackSectorToBlockAdapter;
import org.applecommander.hint.Hint;
import org.applecommander.util.DataBuffer;
import java.util.ArrayList;
import java.util.List;
/**
* Automatic discovery of Pascal volumes.
*/
public class PascalDiskFactory implements DiskFactory {
@Override
public void inspect(Context ctx) {
List<BlockDevice> devices = new ArrayList<>();
if (ctx.blockDevice != null) {
devices.add(ctx.blockDevice);
}
if (ctx.sectorDevice != null && ctx.sectorDevice.getGeometry().sectorsPerDisk() <= 1600) {
if (ctx.sectorDevice.is(Hint.NIBBLE_SECTOR_ORDER)) {
TrackSectorDevice skewed = SkewedTrackSectorDevice.physicalToPascalSkew(ctx.sectorDevice);
devices.add(new TrackSectorToBlockAdapter(skewed, TrackSectorToBlockAdapter.BlockStyle.PASCAL));
}
else if (ctx.sectorDevice.is(Hint.DOS_SECTOR_ORDER)) {
TrackSectorDevice skewed = SkewedTrackSectorDevice.dosToPascalSkew(ctx.sectorDevice);
devices.add(new TrackSectorToBlockAdapter(skewed, TrackSectorToBlockAdapter.BlockStyle.PASCAL));
}
else {
// Likely a DSK image, need to pick between DO and PO...
// Try DO
TrackSectorDevice device1 = SkewedTrackSectorDevice.dosToPascalSkew(ctx.sectorDevice);
devices.add(new TrackSectorToBlockAdapter(device1, TrackSectorToBlockAdapter.BlockStyle.PRODOS));
// Try PO
TrackSectorDevice device2 = ctx.sectorDevice;
devices.add(new TrackSectorToBlockAdapter(device2, TrackSectorToBlockAdapter.BlockStyle.PRODOS));
}
}
devices.forEach(device -> {
if (check(device)) {
ctx.disks.add(new PascalFormatDisk(ctx.source.getName(), device));
}
});
ctx.blockDevice()
.include16Sector(Hint.PRODOS_BLOCK_ORDER)
.include800K()
.get()
.forEach(device -> {
if (check(device)) {
ctx.disks.add(new PascalFormatDisk(ctx.source.getName(), device));
}
});
}
/** Check for a likely directory structure. Note that we scan all sizes, even though that is overkill. */

View File

@@ -21,59 +21,28 @@ package com.webcodepro.applecommander.storage.os.prodos;
import com.webcodepro.applecommander.storage.DiskFactory;
import org.applecommander.device.BlockDevice;
import org.applecommander.device.SkewedTrackSectorDevice;
import org.applecommander.device.TrackSectorDevice;
import org.applecommander.device.TrackSectorToBlockAdapter;
import org.applecommander.hint.Hint;
import org.applecommander.util.DataBuffer;
import static com.webcodepro.applecommander.storage.DiskConstants.*;
import java.util.ArrayList;
import java.util.List;
public class ProdosDiskFactory implements DiskFactory {
@Override
public void inspect(Context ctx) {
// It seems easiest to gather all possibilities first...
List<ProdosFormatDisk> tests = new ArrayList<>();
if (ctx.blockDevice != null) {
tests.add(new ProdosFormatDisk(ctx.source.getName(), ctx.blockDevice));
}
if (ctx.sectorDevice != null && ctx.sectorDevice.getGeometry().sectorsPerDisk() <= 1600) {
TrackSectorDevice skewed = null;
if (ctx.sectorDevice.is(Hint.NIBBLE_SECTOR_ORDER)) {
skewed = SkewedTrackSectorDevice.physicalToPascalSkew(ctx.sectorDevice);
BlockDevice device = new TrackSectorToBlockAdapter(skewed, TrackSectorToBlockAdapter.BlockStyle.PRODOS);
tests.add(new ProdosFormatDisk(ctx.source.getName(), device));
}
else if (ctx.sectorDevice.is(Hint.DOS_SECTOR_ORDER)) {
skewed = SkewedTrackSectorDevice.dosToPascalSkew(ctx.sectorDevice);
BlockDevice device = new TrackSectorToBlockAdapter(skewed, TrackSectorToBlockAdapter.BlockStyle.PRODOS);
tests.add(new ProdosFormatDisk(ctx.source.getName(), device));
}
else {
// Likely a DSK image, need to pick between DO and PO...
// Try DO
TrackSectorDevice device1 = SkewedTrackSectorDevice.dosToPascalSkew(ctx.sectorDevice);
tests.add(new ProdosFormatDisk(ctx.source.getName(), new TrackSectorToBlockAdapter(device1,
TrackSectorToBlockAdapter.BlockStyle.PRODOS)));
// Try PO
TrackSectorDevice device2 = ctx.sectorDevice;
tests.add(new ProdosFormatDisk(ctx.source.getName(), new TrackSectorToBlockAdapter(device2,
TrackSectorToBlockAdapter.BlockStyle.PRODOS)));
}
}
// ... and then test for ProDOS details:
for (ProdosFormatDisk fdisk : tests) {
if (check(fdisk)) {
ctx.disks.add(fdisk);
}
}
ctx.blockDevice()
.include16Sector(Hint.PRODOS_BLOCK_ORDER)
.include800K()
.includeHDV()
.get()
.forEach(device -> {
if (check(device)) {
ctx.disks.add(new ProdosFormatDisk(ctx.source.getName(), device));
}
});
}
public boolean check(ProdosFormatDisk fdisk) {
public boolean check(BlockDevice device) {
int nextBlock = 2;
DataBuffer volumeDirectory = DataBuffer.wrap(fdisk.readBlock(nextBlock));
DataBuffer volumeDirectory = device.readBlock(nextBlock);
int priorBlock = volumeDirectory.getUnsignedShort(0x00);
int storageType = volumeDirectory.getUnsignedByte(0x04) >> 4;
int entryLength = volumeDirectory.getUnsignedByte(0x23);
@@ -109,7 +78,7 @@ public class ProdosDiskFactory implements DiskFactory {
nextBlock = volumeDirectory.getUnsignedShort(0x02);
if (nextBlock == 0) break;
if (nextBlock >= totalBlocks) return false;
volumeDirectory = DataBuffer.wrap(fdisk.readBlock(nextBlock));
volumeDirectory = device.readBlock(nextBlock);
}
return good;
}

View File

@@ -29,8 +29,6 @@ import org.applecommander.util.DataBuffer;
import static com.webcodepro.applecommander.storage.DiskConstants.DOS32_SECTORS_ON_115KB_DISK;
import static com.webcodepro.applecommander.storage.os.rdos.RdosFormatDisk.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class RdosDiskFactory implements DiskFactory {
@@ -38,50 +36,24 @@ public class RdosDiskFactory implements DiskFactory {
@Override
public void inspect(Context ctx) {
List<TrackSectorDevice> devices = new ArrayList<>();
if (ctx.sectorDevice != null) {
if (ctx.sectorDevice.is(Hint.NIBBLE_SECTOR_ORDER)) {
if (ctx.sectorDevice.getGeometry().sectorsPerTrack() == 13) {
devices.add(ctx.sectorDevice);
}
else {
devices.add(SkewedTrackSectorDevice.physicalToDosSkew(ctx.sectorDevice));
}
}
else if (ctx.sectorDevice.is(Hint.DOS_SECTOR_ORDER)) {
devices.add(ctx.sectorDevice);
}
else if (ctx.sectorDevice.is(Hint.PRODOS_BLOCK_ORDER)) {
// cheating a bit here
TrackSectorDevice physicalSkew = SkewedTrackSectorDevice.pascalToPhysicalSkew(ctx.sectorDevice);
devices.add(SkewedTrackSectorDevice.physicalToDosSkew(physicalSkew));
}
else {
// DSK image. Could be DO or PO.
devices.add(ctx.sectorDevice);
// cheating a bit here for PO
TrackSectorDevice physicalSkew = SkewedTrackSectorDevice.pascalToPhysicalSkew(ctx.sectorDevice);
devices.add(SkewedTrackSectorDevice.physicalToDosSkew(physicalSkew));
}
}
if (ctx.blockDevice != null) {
devices.add(new BlockToTrackSectorAdapter(ctx.blockDevice, new ProdosBlockToTrackSectorAdapterStrategy()));
}
devices.forEach(device -> {
int sectorsPerTrack = check(device);
if (sectorsPerTrack > 0) {
// Detect if we're a 16 sector disk but RDOS expects only 13 sectors:
if (sectorsPerTrack != device.getGeometry().sectorsPerTrack()) {
// 13-sector disks are in physical order, so fix it:
device = SkewedTrackSectorDevice.dosToPhysicalSkew(device);
// And make the 16-sector disk a fake 13-sector disk:
device = SkewedTrackSectorDevice.truncate16sectorTo13(device);
}
BlockDevice blockDevice = new TrackSectorToBlockAdapter(device, TrackSectorToBlockAdapter.BlockStyle.RDOS);
ctx.disks.add(new RdosFormatDisk(ctx.source.getName(), blockDevice));
}
});
ctx.trackSectorDevice()
.include13Sector()
.include16Sector(Hint.DOS_SECTOR_ORDER)
.get()
.forEach(device -> {
int sectorsPerTrack = check(device);
if (sectorsPerTrack > 0) {
// Detect if we're a 16 sector disk but RDOS expects only 13 sectors:
if (sectorsPerTrack != device.getGeometry().sectorsPerTrack()) {
// 13-sector disks are in physical order, so fix it:
device = SkewedTrackSectorDevice.dosToPhysicalSkew(device);
// And make the 16-sector disk a fake 13-sector disk:
device = SkewedTrackSectorDevice.truncate16sectorTo13(device);
}
BlockDevice blockDevice = new TrackSectorToBlockAdapter(device, TrackSectorToBlockAdapter.BlockStyle.RDOS);
ctx.disks.add(new RdosFormatDisk(ctx.source.getName(), blockDevice));
}
});
}
/**