Merge branch 'merge_acx'

This commit is contained in:
Rob Greene 2022-01-08 21:30:48 -06:00
commit 680c7abac9
11 changed files with 317 additions and 60 deletions

16
.gitignore vendored
View File

@ -10,15 +10,13 @@ build/
AppleCommander.preferences
*.bas
# Ignoring all disk images
*.dsk
*.po
*.hdv
*.do
*.2mg
*.2img
# But allowing disk images from the unit tests
!lib/ac-api/src/test/resources/disks/*
# Ignoring all disk images at root of project
/*.dsk
/*.po
/*.hdv
/*.do
/*.2mg
/*.2img
# Eclipse
.classpath

View File

@ -4,20 +4,45 @@ AppleCommander has switched to using [Gradle](https://gradle.org/) for build and
There is still a bunch of ANT related build information around. They no longer apply and "should" get cleaned up over time.
## Structure
The project is structured as a Gradle multi-project. Independent components have been broken out and each of the SWT targets are independent.
| Path | Note |
| ---- | ---- |
| `lib/ac-api` | The AppleCommander APIs. These are released via Maven and resused in several projects. |
| `lib/ac-swt-common` | The SWT GUI application. Since SWT targets specific environments with native libraries, the actual applications are in the `app` directories. |
| `app/cli-ac` | The `ac` CLI utility. |
| `app/cli-acx` | The `acx` CLI utility. |
| `app/gui-swt-<os>-<arch>` | The indepent SWT GUI applications; one project per combination. |
## Requirements
With the introduction of the Apple Silicon, AppleCommander switched over to the (relatively new) SWT libraries. With that switch, the SWT libraries now require Java 11.
## Tests
```
$ ./gradlew test
./gradlew test
BUILD SUCCESSFUL in 554ms
19 actionable tasks: 19 up-to-date
```
## Building
```
$ ./gradlew clean assemble
./gradlew clean build
BUILD SUCCESSFUL in 2s
6 actionable tasks: 6 executed
BUILD SUCCESSFUL in 7s
104 actionable tasks: 104 executed
```
## Older notes..
Just keeping these for the short-term...
```
$ tools/retrolambda.sh build/libs/AppleCommander-ac-<version>.jar
Converting...
Retrolambda 2.5.6

View File

@ -88,6 +88,7 @@ public class Main {
public static void main(String[] args) {
CommandLine cmd = new CommandLine(new Main());
cmd.setExecutionExceptionHandler(new PrintExceptionMessageHandler());
cmd.setCaseInsensitiveEnumValuesAllowed(true);
if (args.length == 0) {
cmd.usage(System.out);
System.exit(1);

View File

@ -0,0 +1,83 @@
package io.github.applecommander.acx;
import java.util.function.Function;
import java.util.logging.Logger;
import com.webcodepro.applecommander.storage.Disk;
import com.webcodepro.applecommander.storage.physical.ByteArrayImageLayout;
import com.webcodepro.applecommander.storage.physical.DosOrder;
import com.webcodepro.applecommander.storage.physical.ImageOrder;
import com.webcodepro.applecommander.storage.physical.NibbleOrder;
import com.webcodepro.applecommander.storage.physical.ProdosOrder;
public enum OrderType {
DOS(OrderType::createDosImageOrder),
NIBBLE(OrderType::create140kNibbleImageOrder),
PRODOS(OrderType::createProdosImageOrder);
private static Logger LOG = Logger.getLogger(OrderType.class.getName());
private Function<Integer,ImageOrder> createImageOrderFn;
private OrderType(Function<Integer,ImageOrder> createImageOrderFn) {
this.createImageOrderFn = createImageOrderFn;
}
public ImageOrder createImageOrder(int size) {
return createImageOrderFn.apply(size);
}
/**
* At this time, the various DOS disks only support 140K or 800K disks and
* we have to rely on the SystemType to actually evaluate the correct size.
*/
static ImageOrder createDosImageOrder(int size) {
if (size < Disk.APPLE_140KB_DISK) {
LOG.warning("Setting image size to 140KB.");
size = Disk.APPLE_140KB_DISK;
}
else if (size == Disk.APPLE_140KB_DISK) {
// Size is valid; don't warn and don't bump to 800K.
}
else if (size != Disk.APPLE_800KB_DISK) {
LOG.warning("Setting image size to 800KB.");
size = Disk.APPLE_800KB_DISK;
}
ByteArrayImageLayout layout = new ByteArrayImageLayout(new byte[size]);
return new DosOrder(layout);
}
/**
* Nibblized disks are always 140K disks (or ~230K on disk).
*/
static ImageOrder create140kNibbleImageOrder(int size) {
if (size != Disk.APPLE_140KB_NIBBLE_DISK && size != Disk.APPLE_140KB_DISK) {
LOG.warning("Setting image size to 140KB");
}
ByteArrayImageLayout layout = new ByteArrayImageLayout(new byte[Disk.APPLE_140KB_NIBBLE_DISK]);
return new NibbleOrder(layout);
}
/**
* Lock ProDOS into 140K, 800K, or anything between 800K and 32M.
* This means you _could_ setup a 807KB disk if you wanted.
*/
static ImageOrder createProdosImageOrder(int size) {
if (size < Disk.APPLE_140KB_DISK) {
LOG.warning("Setting image size to 140KB.");
size = Disk.APPLE_140KB_DISK;
}
else if (size == Disk.APPLE_140KB_DISK) {
// Size is valid; don't warn and don't bump to 800K.
}
else if (size < Disk.APPLE_800KB_DISK) {
LOG.warning("Setting image size to 800KB.");
size = Disk.APPLE_800KB_DISK;
}
else if (size > Disk.APPLE_32MB_HARDDISK) {
LOG.warning("Setting image size to 32MB.");
size = Disk.APPLE_32MB_HARDDISK;
}
ByteArrayImageLayout layout = new ByteArrayImageLayout(size);
return new ProdosOrder(layout);
}
}

View File

@ -14,8 +14,8 @@ public class PrintExceptionMessageHandler implements IExecutionExceptionHandler
ex.printStackTrace(System.err);
}
else {
// bold red error message
cmd.getErr().println(cmd.getColorScheme().errorText(ex.getMessage()));
// bold red error message (+ "" is to handle null messages)
cmd.getErr().println(cmd.getColorScheme().errorText(ex.getMessage() + ""));
}
return cmd.getExitCodeExceptionMapper() != null

View File

@ -5,60 +5,79 @@ import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.logging.Logger;
import com.webcodepro.applecommander.storage.Disk;
import com.webcodepro.applecommander.storage.DiskException;
import com.webcodepro.applecommander.storage.FileEntry;
import com.webcodepro.applecommander.storage.FormattedDisk;
import com.webcodepro.applecommander.storage.os.dos33.DosFormatDisk;
import com.webcodepro.applecommander.storage.physical.ByteArrayImageLayout;
import com.webcodepro.applecommander.storage.physical.DosOrder;
import com.webcodepro.applecommander.storage.physical.ImageOrder;
import com.webcodepro.applecommander.storage.physical.ProdosOrder;
import io.github.applecommander.acx.converter.DataSizeConverter;
import io.github.applecommander.acx.fileutil.FileUtils;
public enum SystemType {
DOS(SystemType::createDosImageOrder, SystemType::copyDosSystemTracks),
OZDOS(SystemType::create800kDosImageOrder, SystemType::copyDosSystemTracks),
UNIDOS(SystemType::create800kDosImageOrder, SystemType::copyDosSystemTracks),
PRODOS(SystemType::createProdosImageOrder, SystemType::copyProdosSystemFiles),
PASCAL(SystemType::createProdosImageOrder, SystemType::copyPascalSystemFiles);
DOS(OrderType.DOS, SystemType::enforce140KbDisk,
SystemType::copyDosSystemTracks),
// OzdosFormatDisk is structured on top of ProDOS blocks in the implementation.
OZDOS(OrderType.PRODOS, SystemType::enforce800KbDisk,
SystemType::copyDosSystemTracks),
// UnidosFormatDisk is structured on top of DOS track/sectors in the implementation.
UNIDOS(OrderType.DOS, SystemType::enforce800KbDisk,
SystemType::copyDosSystemTracks),
PRODOS(OrderType.PRODOS, SystemType::enforce140KbOr800KbUpTo32MbDisk,
SystemType::copyProdosSystemFiles),
PASCAL(OrderType.PRODOS, SystemType::enforce140KbDisk,
SystemType::copyPascalSystemFiles);
private static Logger LOG = Logger.getLogger(SystemType.class.getName());
static Logger LOG = Logger.getLogger(SystemType.class.getName());
private Function<Integer,ImageOrder> createImageOrderFn;
private OrderType defaultOrderType;
private Function<Integer,Integer> enforceDiskSizeFn;
private BiConsumer<FormattedDisk,FormattedDisk> copySystemFn;
private SystemType(Function<Integer,ImageOrder> createImageOrderFn,
BiConsumer<FormattedDisk,FormattedDisk> copySystemFn) {
this.createImageOrderFn = createImageOrderFn;
private SystemType(OrderType defaultOrderType,
Function<Integer,Integer> enforceDiskSizeFn,
BiConsumer<FormattedDisk,FormattedDisk> copySystemFn) {
this.defaultOrderType = defaultOrderType;
this.enforceDiskSizeFn = enforceDiskSizeFn;
this.copySystemFn = copySystemFn;
}
public ImageOrder createImageOrder(int size) {
return createImageOrderFn.apply(size);
public OrderType defaultOrderType() {
return defaultOrderType;
}
public int validateSize(int size) {
return enforceDiskSizeFn.apply(size);
}
public void copySystem(FormattedDisk target, FormattedDisk source) {
copySystemFn.accept(target, source);
}
private static ImageOrder createDosImageOrder(int size) {
ByteArrayImageLayout layout = new ByteArrayImageLayout(new byte[size]);
return new DosOrder(layout);
}
private static ImageOrder create800kDosImageOrder(int size) {
if (size != 800 * DataSizeConverter.KB) {
LOG.warning("Setting image size to 800KB.");
}
ByteArrayImageLayout layout = new ByteArrayImageLayout(new byte[800 * DataSizeConverter.KB]);
return new DosOrder(layout);
}
private static ImageOrder createProdosImageOrder(int size) {
ByteArrayImageLayout layout = new ByteArrayImageLayout(size);
return new ProdosOrder(layout);
}
private static void copyDosSystemTracks(FormattedDisk targetDisk, FormattedDisk source) {
static int enforce140KbDisk(int size) {
if (size != Disk.APPLE_140KB_DISK) {
LOG.warning("Setting image size to 140KB");
}
return Disk.APPLE_140KB_DISK;
}
static int enforce800KbDisk(int size) {
if (size != Disk.APPLE_800KB_DISK) {
LOG.warning("Setting image size to 800KB.");
}
return Disk.APPLE_800KB_DISK;
}
static int enforce140KbOr800KbUpTo32MbDisk(int size) {
if (size <= Disk.APPLE_140KB_DISK) {
return enforce140KbDisk(size);
}
if (size <= Disk.APPLE_800KB_DISK) {
return enforce800KbDisk(size);
}
if (size > Disk.APPLE_32MB_HARDDISK) {
LOG.warning("Setting image size to 32MB.");
return Disk.APPLE_32MB_HARDDISK;
}
return size;
}
static void copyDosSystemTracks(FormattedDisk targetDisk, FormattedDisk source) {
DosFormatDisk target = (DosFormatDisk)targetDisk;
// FIXME messing with the VTOC should be handled elsewhere
byte[] vtoc = source.readSector(DosFormatDisk.CATALOG_TRACK, DosFormatDisk.VTOC_SECTOR);
@ -71,7 +90,7 @@ public enum SystemType {
}
}
}
private static void copyProdosSystemFiles(FormattedDisk target, FormattedDisk source) {
static void copyProdosSystemFiles(FormattedDisk target, FormattedDisk source) {
// We need to explicitly fix the boot block
target.writeBlock(0, source.readBlock(0));
target.writeBlock(1, source.readBlock(1));
@ -86,7 +105,7 @@ public enum SystemType {
throw new RuntimeException(e);
}
}
private static void copyPascalSystemFiles(FormattedDisk target, FormattedDisk source) {
static void copyPascalSystemFiles(FormattedDisk target, FormattedDisk source) {
// We need to explicitly fix the boot block
target.writeBlock(0, source.readBlock(0));
target.writeBlock(1, source.readBlock(1));

View File

@ -1,5 +1,6 @@
package io.github.applecommander.acx.command;
import java.util.Optional;
import java.util.logging.Logger;
import com.webcodepro.applecommander.storage.Disk;
@ -11,10 +12,11 @@ import com.webcodepro.applecommander.storage.os.pascal.PascalFormatDisk;
import com.webcodepro.applecommander.storage.os.prodos.ProdosFormatDisk;
import com.webcodepro.applecommander.storage.physical.ImageOrder;
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 io.github.applecommander.acx.converter.SystemTypeConverter;
import picocli.CommandLine.ArgGroup;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
@ -27,12 +29,14 @@ public class CreateDiskCommand extends ReusableCommandOptions {
defaultValue = "${ACX_DISK_NAME}")
private String imageName;
@Option(names = { "-t", "--type" }, required = true, converter = SystemTypeConverter.class,
description = "Select system type (DOS, ProDOS, Pascal.")
private SystemType type;
@ArgGroup(multiplicity = "1", heading = "%nOperating System Selection:%n")
private SystemSelection systemSelection;
@ArgGroup(heading = "%nDisk Sector Ordering Selection:%n")
private OrderSelection orderSelection = new OrderSelection();
@Option(names = { "-s", "--size" }, defaultValue = "140kb", converter = DataSizeConverter.class,
description = "Select disk size (140K, 800K, 10M).")
description = "Select disk size (examples: 140K, 800K, 10M).")
private int size;
@Option(names = { "-f", "--format" },
@ -45,11 +49,20 @@ public class CreateDiskCommand extends ReusableCommandOptions {
@Override
public int handleCommand() throws Exception {
LOG.info(() -> String.format("Creating %s image of type %s.", DataSizeConverter.format(size), type));
SystemType systemType = systemSelection.get();
ImageOrder order = type.createImageOrder(size);
// This allows a defaulted OrderType to be adjusted based on SystemType.
OrderType actualOrderType = orderSelection.get().orElse(systemType.defaultOrderType());
// Size is constrained in DOS and Pascal
int correctedSize = systemType.validateSize(size);
LOG.info(() -> String.format("Creating %s image of type %s (%s).",
DataSizeConverter.format(correctedSize), systemType, actualOrderType));
ImageOrder order = actualOrderType.createImageOrder(correctedSize);
FormattedDisk[] disks = null;
switch (type) {
switch (systemType) {
case DOS:
disks = DosFormatDisk.create(imageName, order);
break;
@ -69,11 +82,61 @@ public class CreateDiskCommand extends ReusableCommandOptions {
if (formatSource != null) {
Disk systemSource = new Disk(formatSource);
type.copySystem(disks[0], systemSource.getFormattedDisks()[0]);
systemType.copySystem(disks[0], systemSource.getFormattedDisks()[0]);
}
saveDisk(disks[0]);
return 0;
}
private static class SystemSelection {
private SystemType systemType;
public SystemType get() {
return systemType;
}
@Option(names = "--dos", description = "DOS formatted disk.")
public void selectDos(boolean flag) {
systemType = SystemType.DOS;
}
@Option(names = "--ozdos", description = "OzDOS 800K formatted disk.")
public void selectOzdos(boolean flag) {
systemType = SystemType.OZDOS;
}
@Option(names = "--unidos", description = "UniDOS 800K formatted disk.")
public void selectUnidos(boolean flag) {
systemType = SystemType.UNIDOS;
}
@Option(names = "--pascal", description = "Pascal formatted disk.")
public void selectPascal(boolean flag) {
systemType = SystemType.PASCAL;
}
@Option(names = "--prodos", description = "ProDOS formatted disk.")
public void selectProdos(boolean flag) {
systemType = SystemType.PRODOS;
}
}
private static class OrderSelection {
private Optional<OrderType> orderType = Optional.empty();
public Optional<OrderType> get() {
return orderType;
}
@Option(names = { "--dos-order" }, description = "DOS ordered sectors.")
public void selectDosOrder(boolean flag) {
orderType = Optional.of(OrderType.DOS);
}
@Option(names = { "--nibble-order" }, description = "DOS ordered, nibble encoded sectors.")
public void selectNibbleOrder(boolean flag) {
orderType = Optional.of(OrderType.NIBBLE);
}
@Option(names = { "--prodos-order" }, description = "ProDOS ordered sectors/blocks.")
public void selectProdosOrder(boolean flag) {
orderType = Optional.of(OrderType.PRODOS);
}
}
}

View File

@ -0,0 +1,35 @@
package io.github.applecommander.acx;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import com.webcodepro.applecommander.storage.Disk;
public class OrderTypeTest {
@Test
public void testCreateDosImageOrder() {
assertEquals(Disk.APPLE_140KB_DISK, OrderType.createDosImageOrder(0).getPhysicalSize());
assertEquals(Disk.APPLE_140KB_DISK, OrderType.createDosImageOrder(Disk.APPLE_140KB_DISK).getPhysicalSize());
assertEquals(Disk.APPLE_800KB_DISK, OrderType.createDosImageOrder(Disk.APPLE_140KB_DISK+1).getPhysicalSize());
assertEquals(Disk.APPLE_800KB_DISK, OrderType.createDosImageOrder(Disk.APPLE_800KB_DISK).getPhysicalSize());
assertEquals(Disk.APPLE_800KB_DISK, OrderType.createDosImageOrder(Disk.APPLE_800KB_DISK+1).getPhysicalSize());
}
@Test
public void testCreate140kNibbleImageOrder() {
assertEquals(Disk.APPLE_140KB_NIBBLE_DISK, OrderType.create140kNibbleImageOrder(0).getPhysicalSize());
assertEquals(Disk.APPLE_140KB_NIBBLE_DISK, OrderType.create140kNibbleImageOrder(Disk.APPLE_140KB_DISK).getPhysicalSize());
assertEquals(Disk.APPLE_140KB_NIBBLE_DISK, OrderType.create140kNibbleImageOrder(Disk.APPLE_140KB_DISK+1).getPhysicalSize());
}
@Test
public void testCreateProdosImageOrder() {
assertEquals(Disk.APPLE_140KB_DISK, OrderType.createProdosImageOrder(0).getPhysicalSize());
assertEquals(Disk.APPLE_140KB_DISK, OrderType.createProdosImageOrder(Disk.APPLE_140KB_DISK).getPhysicalSize());
assertEquals(Disk.APPLE_800KB_DISK, OrderType.createProdosImageOrder(Disk.APPLE_140KB_DISK+1).getPhysicalSize());
assertEquals(Disk.APPLE_800KB_DISK, OrderType.createProdosImageOrder(Disk.APPLE_800KB_DISK).getPhysicalSize());
assertEquals(Disk.APPLE_800KB_DISK+1, OrderType.createProdosImageOrder(Disk.APPLE_800KB_DISK+1).getPhysicalSize());
assertEquals(Disk.APPLE_10MB_HARDDISK, OrderType.createProdosImageOrder(Disk.APPLE_10MB_HARDDISK).getPhysicalSize());
}
}

View File

@ -0,0 +1,33 @@
package io.github.applecommander.acx;
import org.junit.Test;
import static org.junit.Assert.*;
import com.webcodepro.applecommander.storage.Disk;
public class SystemTypeTest {
@Test
public void testEnforce140KbDisk() {
assertEquals(Disk.APPLE_140KB_DISK, SystemType.enforce140KbDisk(0));
assertEquals(Disk.APPLE_140KB_DISK, SystemType.enforce140KbDisk(Disk.APPLE_140KB_DISK));
assertEquals(Disk.APPLE_140KB_DISK, SystemType.enforce140KbDisk(Disk.APPLE_800KB_DISK));
}
@Test
public void testEnforce800KbDisk() {
assertEquals(Disk.APPLE_800KB_DISK, SystemType.enforce800KbDisk(0));
assertEquals(Disk.APPLE_800KB_DISK, SystemType.enforce800KbDisk(Disk.APPLE_800KB_DISK));
assertEquals(Disk.APPLE_800KB_DISK, SystemType.enforce800KbDisk(Disk.APPLE_32MB_HARDDISK));
}
@Test
public void testEnforce140KbOr800KbUpTo32MbDisk() {
assertEquals(Disk.APPLE_140KB_DISK, SystemType.enforce140KbOr800KbUpTo32MbDisk(0));
assertEquals(Disk.APPLE_140KB_DISK, SystemType.enforce140KbOr800KbUpTo32MbDisk(Disk.APPLE_140KB_DISK));
assertEquals(Disk.APPLE_800KB_DISK, SystemType.enforce140KbOr800KbUpTo32MbDisk(Disk.APPLE_140KB_DISK+1));
assertEquals(Disk.APPLE_800KB_DISK, SystemType.enforce140KbOr800KbUpTo32MbDisk(Disk.APPLE_800KB_DISK));
assertEquals(Disk.APPLE_800KB_DISK+1, SystemType.enforce140KbOr800KbUpTo32MbDisk(Disk.APPLE_800KB_DISK+1));
assertEquals(Disk.APPLE_32MB_HARDDISK, SystemType.enforce140KbOr800KbUpTo32MbDisk(Disk.APPLE_32MB_HARDDISK));
assertEquals(Disk.APPLE_32MB_HARDDISK, SystemType.enforce140KbOr800KbUpTo32MbDisk(Integer.MAX_VALUE));
}
}

Binary file not shown.

Binary file not shown.