Allowing nibble track dumps even if we don't recognize the disk.

This commit is contained in:
Rob Greene
2025-09-05 11:47:19 -05:00
parent 02dd465630
commit e2b409534a
3 changed files with 125 additions and 27 deletions
@@ -0,0 +1,48 @@
/*
* AppleCommander - An Apple ][ image utility.
* Copyright (C) 2025 by Robert Greene and others
* robgreene at users.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package io.github.applecommander.acx.base;
import com.webcodepro.applecommander.storage.DiskFactory;
import com.webcodepro.applecommander.storage.FormattedDisk;
import io.github.applecommander.acx.converter.DiskFactoryContextConverter;
import picocli.CommandLine.Option;
import java.util.List;
public abstract class ReadOnlyDiskContextCommandOptions extends ReusableCommandOptions {
@Option(names = { "-d", "--disk" }, description = "Image to process [$ACX_DISK_NAME].", required = true,
converter = DiskFactoryContextConverter.class, defaultValue = "${ACX_DISK_NAME}")
private DiskFactory.Context ctx;
@Option(names = { "-k", "--number" }, description = "Select disk number to access [$ACX_DISK_NUMBER].",
defaultValue = "${ACX_DISK_NUMBER}")
private Integer diskNumber;
protected List<FormattedDisk> selectedDisks() {
if (diskNumber != null) {
return List.of(ctx.disks.get(diskNumber));
}
return ctx.disks;
}
protected DiskFactory.Context context() {
return ctx;
}
}
@@ -31,7 +31,7 @@ import com.webcodepro.applecommander.storage.TrackSectorDeviceAdapter;
import com.webcodepro.applecommander.util.AppleUtil;
import com.webcodepro.applecommander.util.Range;
import io.github.applecommander.acx.base.ReadOnlyDiskImageCommandOptions;
import io.github.applecommander.acx.base.ReadOnlyDiskContextCommandOptions;
import io.github.applecommander.acx.converter.IntegerTypeConverter;
import io.github.applecommander.acx.converter.RangeTypeConverter;
import io.github.applecommander.disassembler.api.Disassembler;
@@ -49,7 +49,7 @@ import picocli.CommandLine.Mixin;
import picocli.CommandLine.Option;
@Command(name = "dump", description = "Dump a block or sector.", sortOptions = false)
public class DumpCommand extends ReadOnlyDiskImageCommandOptions {
public class DumpCommand extends ReadOnlyDiskContextCommandOptions {
@ArgGroup(heading = "%nOutput Selection:%n")
private OutputSelection output = new OutputSelection();
@@ -58,34 +58,37 @@ public class DumpCommand extends ReadOnlyDiskImageCommandOptions {
@Override
public int handleCommand() throws Exception {
FormattedDisk disk = selectedDisks().getFirst();
if (options.coordinate.blockRangeSelection != null) {
BlockDevice device = BlockDeviceAdapter.from(disk);
options.coordinate.blockRangeSelection.blocks.stream().forEach(block -> {
validateBlockNum(device, block);
options.includesBootSector = block == 0;
byte[] data = device.readBlock(block).asBytes();
System.out.printf("Block #%d:\n", block);
System.out.println(output.format(options, data));
});
return 0;
}
else if (options.coordinate.trackSectorRangeSelection != null) {
options.coordinate.trackSectorRangeSelection.tracks.stream().forEach(track -> {
TrackSectorDevice device = TrackSectorDeviceAdapter.from(disk);
options.coordinate.trackSectorRangeSelection.sectors.stream().forEach(sector -> {
validateTrackAndSector(device, track, sector);
options.includesBootSector = track == 0 && sector == 0;
byte[] data = device.readSector(track, sector).asBytes();
System.out.printf("Track %02d, Sector %02d:\n", track, sector);
if (!selectedDisks().isEmpty()) {
FormattedDisk disk = selectedDisks().getFirst();
if (options.coordinate.blockRangeSelection != null) {
BlockDevice device = BlockDeviceAdapter.from(disk);
options.coordinate.blockRangeSelection.blocks.stream().forEach(block -> {
validateBlockNum(device, block);
options.includesBootSector = block == 0;
byte[] data = device.readBlock(block).asBytes();
System.out.printf("Block #%d:\n", block);
System.out.println(output.format(options, data));
});
});
return 0;
return 0;
} else if (options.coordinate.trackSectorRangeSelection != null) {
options.coordinate.trackSectorRangeSelection.tracks.stream().forEach(track -> {
TrackSectorDevice device = TrackSectorDeviceAdapter.from(disk);
options.coordinate.trackSectorRangeSelection.sectors.stream().forEach(sector -> {
validateTrackAndSector(device, track, sector);
options.includesBootSector = track == 0 && sector == 0;
byte[] data = device.readSector(track, sector).asBytes();
System.out.printf("Track %02d, Sector %02d:\n", track, sector);
System.out.println(output.format(options, data));
});
});
return 0;
}
}
else if (options.coordinate.nibbleTrackRangeSelection != null) {
NibbleTrackReaderWriter trackReaderWriter = disk.get(NibbleTrackReaderWriter.class)
.orElseThrow(() -> new RuntimeException("This is not a nibble device."));
if (context().nibbleTrackReaderWriter == null) {
throw new RuntimeException("This is not a nibble device.");
}
NibbleTrackReaderWriter trackReaderWriter = context().nibbleTrackReaderWriter;
options.coordinate.nibbleTrackRangeSelection.tracks.stream().forEach(track -> {
final int tracksPerDisk = trackReaderWriter.getTracksOnDevice();
if (track < 0 || track >= tracksPerDisk) {
@@ -98,7 +101,13 @@ public class DumpCommand extends ReadOnlyDiskImageCommandOptions {
});
return 0;
}
System.out.println("Please choose block(s) or track(s) and sector(s).");
// detect errors since we might get here with an unrecognized disk
if (options.coordinate.blockRangeSelection != null || options.coordinate.trackSectorRangeSelection != null) {
System.out.println("Disk was not recognized.");
}
else {
System.out.println("Please choose block(s) or track(s) and sector(s).");
}
return 1;
}
@@ -0,0 +1,41 @@
/*
* AppleCommander - An Apple ][ image utility.
* Copyright (C) 2025 by Robert Greene and others
* robgreene at users.sourceforge.net
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package io.github.applecommander.acx.converter;
import com.webcodepro.applecommander.storage.DiskFactory;
import com.webcodepro.applecommander.storage.Disks;
import org.applecommander.source.Source;
import org.applecommander.source.Sources;
import picocli.CommandLine.ITypeConverter;
import picocli.CommandLine.TypeConversionException;
import java.nio.file.Files;
import java.nio.file.Path;
public class DiskFactoryContextConverter implements ITypeConverter<DiskFactory.Context> {
@Override
public DiskFactory.Context convert(String filename) throws Exception {
if (Files.exists(Path.of(filename))) {
Source source = Sources.create(filename).orElseThrow();
return Disks.inspect(source);
}
throw new TypeConversionException(String.format("Disk '%s' not found", filename));
}
}