Changing how the TrackSectorNibbleDevice is created and identified (two new methods). This hopefully simplifies it's usage.

This commit is contained in:
Rob Greene
2025-09-04 21:25:19 -05:00
parent 7f81e147f4
commit 9c223a97b1
8 changed files with 100 additions and 98 deletions
@@ -33,9 +33,7 @@ import io.github.applecommander.acx.OrderType;
import io.github.applecommander.acx.SystemType;
import io.github.applecommander.acx.base.ReusableCommandOptions;
import io.github.applecommander.acx.converter.DataSizeConverter;
import org.applecommander.device.nibble.Nibble62Disk525Codec;
import org.applecommander.device.*;
import org.applecommander.device.nibble.DiskMarker;
import org.applecommander.hint.Hint;
import org.applecommander.image.NibbleImage;
import org.applecommander.os.dos.OzdosAdapterStrategy;
@@ -93,8 +91,7 @@ public class CreateDiskCommand extends ReusableCommandOptions {
case DOS -> {
TrackSectorDevice sectorDevice = switch (actualOrderType) {
case DOS -> new DosOrderedTrackSectorDevice(source, Hint.DOS_SECTOR_ORDER);
case NIBBLE -> new TrackSectorNibbleDevice(new NibbleImage(source), DiskMarker.disk525sector16(),
new Nibble62Disk525Codec(), 16);
case NIBBLE -> TrackSectorNibbleDevice.create(new NibbleImage(source), 16);
case PRODOS -> new BlockToTrackSectorAdapter(blockDevice, new ProdosBlockToTrackSectorAdapterStrategy());
};
yield DosFormatDisk.create(imageName, sectorDevice);
@@ -19,19 +19,16 @@
*/
package com.webcodepro.applecommander.storage;
import org.applecommander.device.nibble.Nibble53Disk525Codec;
import org.applecommander.device.nibble.Nibble62Disk525Codec;
import org.applecommander.device.*;
import org.applecommander.device.nibble.DiskMarker;
import org.applecommander.device.nibble.NibbleTrackReaderWriter;
import org.applecommander.hint.Hint;
import org.applecommander.image.NibbleImage;
import org.applecommander.image.WozImage;
import org.applecommander.source.Source;
import org.applecommander.util.DataBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* The DiskFactory inspects a given Source inspect it to see if it matches filesystem structure(s).
@@ -82,45 +79,6 @@ public interface DiskFactory {
}
}
/**
* Brute force attempt to identify 13 or 16 sector tracks. Note we only test track 0.
* Also note, we can do much better -- but all the nibble stuff will need to be reconfigured
* to allow different prologs/epilogs per track*. This likely can enable reading early software
* protection schemes that just fiddled with those bytes. DOS likely got moved around, so that
* would be coupled with more flexibility in DOS. See the "experimenting/identifying-nibble-prolog-bytes"
* for some experimental work.
* <p/>
* Note: the variance in prolog/epilog can be super detailed, but it is unlikely a DOS clone
* has different prolog/epilog bytes per sector. Per track may be a bit over-the-top. Except, that
* it appears Ultima I may have used it. :-)
*/
private TrackSectorDevice identifySectorsPerTrack(NibbleTrackReaderWriter trackReaderWriter) {
try {
// Try 16-sector disks first:
TrackSectorDevice device = new TrackSectorNibbleDevice(trackReaderWriter,
DiskMarker.disk525sector16(), new Nibble62Disk525Codec(), 16);
DataBuffer sectorData = device.readSector(0, 0);
if (sectorData.limit() == TrackSectorDevice.SECTOR_SIZE) {
return device;
}
} catch (Throwable t) {
// ignored
}
try {
// Next try 13-sector disks:
TrackSectorDevice device = new TrackSectorNibbleDevice(trackReaderWriter,
DiskMarker.disk525sector13(), new Nibble53Disk525Codec(), 13);
DataBuffer sectorData = device.readSector(0, 0);
if (sectorData.limit() == TrackSectorDevice.SECTOR_SIZE) {
return device;
}
} catch (Throwable t) {
// ignored
}
// Failure
return null;
}
public BlockDeviceBuilder blockDevice() {
return new BlockDeviceBuilder(this);
}
@@ -132,12 +90,12 @@ public interface DiskFactory {
}
public BlockDeviceBuilder include16Sector(Hint hint) {
if (ctx.nibbleTrackReaderWriter != null) {
TrackSectorDevice nibble = ctx.identifySectorsPerTrack(ctx.nibbleTrackReaderWriter);
if (nibble != null) {
Optional<TrackSectorDevice> nibble = TrackSectorNibbleDevice.identify(ctx.nibbleTrackReaderWriter);
if (nibble.isPresent()) {
TrackSectorDevice converted = switch (hint) {
case DOS_SECTOR_ORDER -> SkewedTrackSectorDevice.physicalToDosSkew(nibble);
case PRODOS_BLOCK_ORDER -> SkewedTrackSectorDevice.physicalToPascalSkew(nibble);
case NIBBLE_SECTOR_ORDER -> nibble;
case DOS_SECTOR_ORDER -> SkewedTrackSectorDevice.physicalToDosSkew(nibble.get());
case PRODOS_BLOCK_ORDER -> SkewedTrackSectorDevice.physicalToPascalSkew(nibble.get());
case NIBBLE_SECTOR_ORDER -> nibble.get();
default -> throw new RuntimeException("wrong hint type: " + hint);
};
devices.add(new TrackSectorToBlockAdapter(converted, TrackSectorToBlockAdapter.BlockStyle.PRODOS));
@@ -189,10 +147,12 @@ public interface DiskFactory {
}
public TrackSectorDeviceBuilder include13Sector() {
if (ctx.nibbleTrackReaderWriter != null) {
TrackSectorDevice nibble = ctx.identifySectorsPerTrack(ctx.nibbleTrackReaderWriter);
if (nibble != null && nibble.getGeometry().sectorsPerTrack() == 13) {
devices.add(nibble);
}
Optional<TrackSectorDevice> nibble = TrackSectorNibbleDevice.identify(ctx.nibbleTrackReaderWriter);
nibble.ifPresent(device -> {
if (device.getGeometry().sectorsPerTrack() == 13) {
devices.add(device);
}
});
}
else if (ctx.source.isApproxEQ(DiskConstants.APPLE_13SECTOR_DISK)) {
devices.add(new DosOrderedTrackSectorDevice(ctx.source));
@@ -201,16 +161,18 @@ public interface DiskFactory {
}
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);
}
Optional<TrackSectorDevice> nibble = TrackSectorNibbleDevice.identify(ctx.nibbleTrackReaderWriter);
nibble.ifPresent(device -> {
if (device.getGeometry().sectorsPerTrack() == 16) {
TrackSectorDevice converted = switch (hint) {
case DOS_SECTOR_ORDER -> SkewedTrackSectorDevice.physicalToDosSkew(nibble.get());
case PRODOS_BLOCK_ORDER -> SkewedTrackSectorDevice.physicalToPascalSkew(nibble.get());
case NIBBLE_SECTOR_ORDER -> nibble.get();
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;
@@ -20,9 +20,7 @@
package org.applecommander.device;
import org.applecommander.capability.Capability;
import org.applecommander.device.nibble.NibbleDiskCodec;
import org.applecommander.device.nibble.DiskMarker;
import org.applecommander.device.nibble.NibbleTrackReaderWriter;
import org.applecommander.device.nibble.*;
import org.applecommander.hint.Hint;
import org.applecommander.util.Container;
import org.applecommander.util.DataBuffer;
@@ -30,6 +28,69 @@ import org.applecommander.util.DataBuffer;
import java.util.Optional;
public class TrackSectorNibbleDevice implements TrackSectorDevice {
/**
* Create a TrackSectorNibbleDevice. Device is not formatted but does not need to
* go through an identification routine (which would fail with a blank image).
*/
public static TrackSectorDevice create(NibbleTrackReaderWriter trackReaderWriter, int sectorsPerTrack) {
assert trackReaderWriter != null;
assert sectorsPerTrack == 13 || sectorsPerTrack == 16;
return switch (sectorsPerTrack) {
case 13 -> new TrackSectorNibbleDevice(trackReaderWriter,
DiskMarker.disk525sector13(), new Nibble53Disk525Codec(), sectorsPerTrack);
case 16 -> new TrackSectorNibbleDevice(trackReaderWriter,
DiskMarker.disk525sector16(), new Nibble62Disk525Codec(), sectorsPerTrack);
default -> throw new RuntimeException("unexpected sectors per track: " + sectorsPerTrack);
};
}
/**
* Brute force attempt to identify 13 or 16 sector tracks. Note we only test track 0.
* Also note, we can do much better -- but all the nibble stuff will need to be reconfigured
* to allow different prologs/epilogs per track*. This likely can enable reading early software
* protection schemes that just fiddled with those bytes. DOS likely got moved around, so that
* would be coupled with more flexibility in DOS. See the "experimenting/identifying-nibble-prolog-bytes"
* for some experimental work.
* <p/>
* Note: the variance in prolog/epilog can be super detailed, but it is unlikely a DOS clone
* has different prolog/epilog bytes per sector. Per track may be a bit over-the-top. Except, that
* it appears Ultima I may have used it. :-)
*/
public static Optional<TrackSectorDevice> identify(NibbleTrackReaderWriter trackReaderWriter) {
try {
// Try 16-sector disks first:
final int sectorsPerTrack = 16;
TrackSectorDevice device = new TrackSectorNibbleDevice(trackReaderWriter,
DiskMarker.disk525sector16(), new Nibble62Disk525Codec(), sectorsPerTrack);
for (int sector = 0; sector < sectorsPerTrack; sector++) {
DataBuffer sectorData = device.readSector(0, sector);
if (sectorData.limit() != TrackSectorDevice.SECTOR_SIZE) {
return Optional.empty();
}
}
return Optional.of(device);
} catch (Throwable t) {
// ignored
}
try {
// Next try 13-sector disks:
final int sectorsPerTrack = 13;
TrackSectorDevice device = new TrackSectorNibbleDevice(trackReaderWriter,
DiskMarker.disk525sector13(), new Nibble53Disk525Codec(), sectorsPerTrack);
for (int sector = 0; sector < sectorsPerTrack; sector++) {
DataBuffer sectorData = device.readSector(0, sector);
if (sectorData.limit() != TrackSectorDevice.SECTOR_SIZE) {
return Optional.empty();
}
}
return Optional.of(device);
} catch (Throwable t) {
// ignored
}
// Failure
return Optional.empty();
}
/**
* This is the "data" component of the address field: 2 x (Volume, Track, Sector, Checksum).
*/
@@ -39,7 +100,7 @@ public class TrackSectorNibbleDevice implements TrackSectorDevice {
private final NibbleDiskCodec dataCodec;
private final Geometry geometry;
public TrackSectorNibbleDevice(NibbleTrackReaderWriter trackReaderWriter, DiskMarker diskMarker,
private TrackSectorNibbleDevice(NibbleTrackReaderWriter trackReaderWriter, DiskMarker diskMarker,
NibbleDiskCodec dataCodec, int sectorsPerTrack) {
this.trackReaderWriter = trackReaderWriter;
this.diskMarker = diskMarker;
@@ -22,9 +22,7 @@ package com.webcodepro.applecommander.storage;
import java.io.IOException;
import java.util.List;
import org.applecommander.device.nibble.Nibble62Disk525Codec;
import org.applecommander.device.*;
import org.applecommander.device.nibble.DiskMarker;
import org.applecommander.hint.Hint;
import org.applecommander.image.NibbleImage;
import org.applecommander.os.dos.OzdosAdapterStrategy;
@@ -72,8 +70,7 @@ public class DiskWriterTest {
@Test
public void testWriteToDos33Nibble() throws IOException, DiskException {
Source source = DataBufferSource.create(DiskConstants.APPLE_140KB_NIBBLE_DISK, "new-disk").get();
TrackSectorDevice sectorDevice = new TrackSectorNibbleDevice(new NibbleImage(source), DiskMarker.disk525sector16(),
new Nibble62Disk525Codec(), 16);
TrackSectorDevice sectorDevice = TrackSectorNibbleDevice.create(new NibbleImage(source), 16);
FormattedDisk[] disks = DosFormatDisk.create("write-test-dos33.nib", sectorDevice);
writeFiles(disks, "B", "T", false);
saveDisks(disks);
@@ -26,9 +26,7 @@ import com.webcodepro.applecommander.storage.compare.ComparisonResult;
import com.webcodepro.applecommander.storage.compare.DiskDiff;
import com.webcodepro.applecommander.storage.os.dos33.DosFormatDisk;
import com.webcodepro.applecommander.storage.os.prodos.ProdosFormatDisk;
import org.applecommander.device.nibble.Nibble62Disk525Codec;
import org.applecommander.device.*;
import org.applecommander.device.nibble.DiskMarker;
import org.applecommander.hint.Hint;
import org.applecommander.image.NibbleImage;
import org.applecommander.source.DataBufferSource;
@@ -91,8 +89,7 @@ public class AppleUtilTest {
fileEntry.setFileData("This is a test file.".getBytes()); //$NON-NLS-1$
// A duplicate - then we change it to a NIB disk image...
Source source2 = DataBufferSource.create(DiskConstants.APPLE_140KB_NIBBLE_DISK, "new-disk").get();
TrackSectorDevice device2 = new TrackSectorNibbleDevice(new NibbleImage(source2), DiskMarker.disk525sector16(),
new Nibble62Disk525Codec(), 16);
TrackSectorDevice device2 = TrackSectorNibbleDevice.create(new NibbleImage(source2), 16);
DosFormatDisk dosDiskNibbleOrder = DosFormatDisk.create("dostemp2.nib", device2)[0];
AppleUtil.changeOrderBySector(device1, device2);
// Confirm that these disks are identical:
@@ -114,8 +111,7 @@ public class AppleUtilTest {
fileEntry.setFileData("This is a test file.".getBytes()); //$NON-NLS-1$
// A duplicate - then we change it to a NIB disk image...
Source source2 = DataBufferSource.create(DiskConstants.APPLE_140KB_NIBBLE_DISK, "new-disk").get();
TrackSectorDevice nibbleDevice = new TrackSectorNibbleDevice(new NibbleImage(source2),
DiskMarker.disk525sector16(), new Nibble62Disk525Codec(), 16);
TrackSectorDevice nibbleDevice = TrackSectorNibbleDevice.create(new NibbleImage(source2), 16);
TrackSectorDevice skewedDevice = SkewedTrackSectorDevice.physicalToPascalSkew(nibbleDevice);
ProdosFormatDisk prodosDiskNibbleOrder = ProdosFormatDisk.create("prodostemp2.nib", //$NON-NLS-1$
"prodostemp2", //$NON-NLS-1$
@@ -20,10 +20,7 @@
package org.applecommander;
import com.webcodepro.applecommander.testconfig.TestConfig;
import org.applecommander.device.nibble.Nibble53Disk525Codec;
import org.applecommander.device.nibble.Nibble62Disk525Codec;
import org.applecommander.device.*;
import org.applecommander.device.nibble.DiskMarker;
import org.applecommander.device.nibble.NibbleTrackReaderWriter;
import org.applecommander.hint.Hint;
import org.applecommander.image.DiskCopyImage;
@@ -47,7 +44,7 @@ public class NewDeviceTest {
final String filename = "original321sysmaspls.nib";
Source source = sourceDisk(filename);
NibbleTrackReaderWriter trackReaderWriter = new NibbleImage(source);
TrackSectorDevice tsDevice = new TrackSectorNibbleDevice(trackReaderWriter, DiskMarker.disk525sector13(), new Nibble53Disk525Codec(), 13);
TrackSectorDevice tsDevice = TrackSectorNibbleDevice.identify(trackReaderWriter).orElseThrow();
DataBuffer sectorData = tsDevice.readSector(17,12);
dumpAsHex(sectorData, filename);
}
@@ -57,7 +54,7 @@ public class NewDeviceTest {
final String filename = "DOS 3.2 System Master.woz";
Source source = sourceDisk(filename);
NibbleTrackReaderWriter trackReaderWriter = new WozImage(source);
TrackSectorDevice tsDevice = new TrackSectorNibbleDevice(trackReaderWriter, DiskMarker.disk525sector13(), new Nibble53Disk525Codec(), 13);
TrackSectorDevice tsDevice = TrackSectorNibbleDevice.identify(trackReaderWriter).orElseThrow();
DataBuffer sectorData = tsDevice.readSector(17, 12);
dumpAsHex(sectorData, filename);
}
@@ -67,7 +64,7 @@ public class NewDeviceTest {
final String filename = "DOS 3.3 System Master.woz1";
Source source = sourceDisk(filename);
NibbleTrackReaderWriter trackReaderWriter = new WozImage(source);
TrackSectorDevice tsDevice = new TrackSectorNibbleDevice(trackReaderWriter, DiskMarker.disk525sector16(), new Nibble62Disk525Codec(), 16);
TrackSectorDevice tsDevice = TrackSectorNibbleDevice.identify(trackReaderWriter).orElseThrow();
DataBuffer sectorData = tsDevice.readSector(17, 15);
dumpAsHex(sectorData, filename);
}
@@ -77,7 +74,7 @@ public class NewDeviceTest {
final String filename = "DOS 3.3 System Master.woz2";
Source source = sourceDisk(filename);
NibbleTrackReaderWriter trackReaderWriter = new WozImage(source);
TrackSectorDevice tsDevice = new TrackSectorNibbleDevice(trackReaderWriter, DiskMarker.disk525sector16(), new Nibble62Disk525Codec(), 16);
TrackSectorDevice tsDevice = TrackSectorNibbleDevice.identify(trackReaderWriter).orElseThrow();
DataBuffer sectorData = tsDevice.readSector(17, 15);
dumpAsHex(sectorData, filename);
}
@@ -133,7 +130,7 @@ public class NewDeviceTest {
else if (info.isNibbleOrder()) {
// this is just guessing, and likely never occurs from what I've found, but...
NibbleTrackReaderWriter trackReaderWriter = new NibbleImage(image);
device = new TrackSectorNibbleDevice(trackReaderWriter, DiskMarker.disk525sector16(), new Nibble62Disk525Codec(), 16);
device = TrackSectorNibbleDevice.identify(trackReaderWriter).orElseThrow();
}
assert(device != null);
// Report out... making grand assumption that TS=DOS and block=ProDOS
@@ -44,9 +44,7 @@ import com.webcodepro.applecommander.util.Host;
import com.webcodepro.applecommander.util.StreamUtil;
import com.webcodepro.applecommander.util.TextBundle;
import io.github.applecommander.applesingle.AppleSingle;
import org.applecommander.device.nibble.Nibble62Disk525Codec;
import org.applecommander.device.*;
import org.applecommander.device.nibble.DiskMarker;
import org.applecommander.hint.Hint;
import org.applecommander.image.NibbleImage;
import org.applecommander.source.DataBufferSource;
@@ -1888,8 +1886,7 @@ public class DiskExplorerTab {
Device device = DeviceAdapter.from(getDisk(0));
if (!device.is(Hint.NIBBLE_SECTOR_ORDER)) {
Source nibbleSource = DataBufferSource.create(DiskConstants.APPLE_140KB_NIBBLE_DISK, "new-image.nib").get();
TrackSectorDevice nibbleDevice = new TrackSectorNibbleDevice(new NibbleImage(nibbleSource),
DiskMarker.disk525sector16(), new Nibble62Disk525Codec(), 16);
TrackSectorDevice nibbleDevice = TrackSectorNibbleDevice.create(new NibbleImage(nibbleSource), 16);
nibbleDevice.format();
changeImageOrder("nib",
SkewedTrackSectorDevice.physicalToDosSkew(nibbleDevice));
@@ -20,10 +20,7 @@
package com.webcodepro.applecommander.ui.swt.wizard.diskimage;
import com.webcodepro.applecommander.ui.swt.util.SwtUtil;
import org.applecommander.device.nibble.Nibble62Disk525Codec;
import org.applecommander.device.nibble.NibbleDiskCodec;
import org.applecommander.device.*;
import org.applecommander.device.nibble.DiskMarker;
import org.applecommander.device.nibble.NibbleTrackReaderWriter;
import org.applecommander.hint.Hint;
import org.applecommander.image.NibbleImage;
@@ -108,9 +105,7 @@ public class DiskImageWizard extends Wizard {
break;
case ORDER_NIBBLE:
NibbleTrackReaderWriter readerWriter = new NibbleImage(source);
DiskMarker diskMarker = DiskMarker.disk525sector16();
NibbleDiskCodec nibbleCodec = new Nibble62Disk525Codec();
TrackSectorDevice physicalDevice = new TrackSectorNibbleDevice(readerWriter, diskMarker, nibbleCodec, 16);
TrackSectorDevice physicalDevice = TrackSectorNibbleDevice.create(readerWriter, 16);
sectorDevice = SkewedTrackSectorDevice.physicalToDosSkew(physicalDevice);
blockDevice = new TrackSectorToBlockAdapter(SkewedTrackSectorDevice.physicalToPascalSkew(physicalDevice),
TrackSectorToBlockAdapter.BlockStyle.PRODOS);