From fd066eea6d6abd9cf38c636c65e1187fcd29f416 Mon Sep 17 00:00:00 2001 From: Rob Greene Date: Sat, 26 Mar 2022 22:41:13 -0500 Subject: [PATCH] Adding read/write commands. --- .../io/github/applecommander/acx/Main.java | 6 +- .../acx/arggroup/CoordinateSelection.java | 77 ++++++++++++++++ .../acx/command/DumpCommand.java | 38 +------- .../acx/command/ReadCommand.java | 91 +++++++++++++++++++ .../acx/command/WriteCommand.java | 82 +++++++++++++++++ 5 files changed, 256 insertions(+), 38 deletions(-) create mode 100644 app/cli-acx/src/main/java/io/github/applecommander/acx/arggroup/CoordinateSelection.java create mode 100644 app/cli-acx/src/main/java/io/github/applecommander/acx/command/ReadCommand.java create mode 100644 app/cli-acx/src/main/java/io/github/applecommander/acx/command/WriteCommand.java diff --git a/app/cli-acx/src/main/java/io/github/applecommander/acx/Main.java b/app/cli-acx/src/main/java/io/github/applecommander/acx/Main.java index 4d1e7c6..73d12f1 100644 --- a/app/cli-acx/src/main/java/io/github/applecommander/acx/Main.java +++ b/app/cli-acx/src/main/java/io/github/applecommander/acx/Main.java @@ -37,10 +37,12 @@ import io.github.applecommander.acx.command.InfoCommand; import io.github.applecommander.acx.command.ListCommand; import io.github.applecommander.acx.command.LockCommand; import io.github.applecommander.acx.command.MkdirCommand; +import io.github.applecommander.acx.command.ReadCommand; import io.github.applecommander.acx.command.RenameDiskCommand; import io.github.applecommander.acx.command.RenameFileCommand; import io.github.applecommander.acx.command.RmdirCommand; import io.github.applecommander.acx.command.UnlockCommand; +import io.github.applecommander.acx.command.WriteCommand; import picocli.CommandLine; import picocli.CommandLine.Command; import picocli.CommandLine.HelpCommand; @@ -69,10 +71,12 @@ import picocli.CommandLine.Option; ListCommand.class, LockCommand.class, MkdirCommand.class, + ReadCommand.class, RenameFileCommand.class, RenameDiskCommand.class, RmdirCommand.class, - UnlockCommand.class + UnlockCommand.class, + WriteCommand.class }) public class Main { private static Logger LOG = Logger.getLogger(Main.class.getName()); diff --git a/app/cli-acx/src/main/java/io/github/applecommander/acx/arggroup/CoordinateSelection.java b/app/cli-acx/src/main/java/io/github/applecommander/acx/arggroup/CoordinateSelection.java new file mode 100644 index 0000000..09b6082 --- /dev/null +++ b/app/cli-acx/src/main/java/io/github/applecommander/acx/arggroup/CoordinateSelection.java @@ -0,0 +1,77 @@ +/* + * AppleCommander - An Apple ][ image utility. + * Copyright (C) 2019-2022 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.arggroup; + +import com.webcodepro.applecommander.storage.Disk; + +import picocli.CommandLine.ArgGroup; +import picocli.CommandLine.Option; + +public class CoordinateSelection { + @ArgGroup(exclusive = false) + private CoordinateSelection.SectorCoordinateSelection sectorCoordinate; + @ArgGroup(exclusive = false) + private CoordinateSelection.BlockCoordinateSelection blockCoordinate; + + public byte[] read(Disk disk) { + if (sectorCoordinate != null) { + return sectorCoordinate.read(disk); + } + else if (blockCoordinate != null) { + return blockCoordinate.read(disk); + } + return disk.readSector(0, 0); + } + + public void write(Disk disk, byte[] data) { + if (sectorCoordinate != null) { + sectorCoordinate.write(disk, data); + } + else if (blockCoordinate != null) { + blockCoordinate.write(disk, data); + } + disk.writeSector(0, 0, data); + } + + public static class SectorCoordinateSelection { + @Option(names = { "-t", "--track" }, required = true, description = "Track number.") + private Integer track; + @Option(names = { "-s", "--sector" }, required = true, description = "Sector number.") + private Integer sector; + + public byte[] read(Disk disk) { + return disk.readSector(track, sector); + } + public void write(Disk disk, byte[] data) { + disk.writeSector(track, sector, data); + } + } + public static class BlockCoordinateSelection { + @Option(names = { "-b", "--block" }, description = "Block number.") + private Integer block; + + public byte[] read(Disk disk) { + return disk.readBlock(block); + } + public void write(Disk disk, byte[] data) { + disk.writeBlock(block, data); + } + } +} \ No newline at end of file diff --git a/app/cli-acx/src/main/java/io/github/applecommander/acx/command/DumpCommand.java b/app/cli-acx/src/main/java/io/github/applecommander/acx/command/DumpCommand.java index 2e8f316..c341761 100644 --- a/app/cli-acx/src/main/java/io/github/applecommander/acx/command/DumpCommand.java +++ b/app/cli-acx/src/main/java/io/github/applecommander/acx/command/DumpCommand.java @@ -24,9 +24,9 @@ import java.io.StringWriter; import java.util.function.BiFunction; import java.util.stream.Collectors; -import com.webcodepro.applecommander.storage.Disk; import com.webcodepro.applecommander.util.AppleUtil; +import io.github.applecommander.acx.arggroup.CoordinateSelection; import io.github.applecommander.acx.base.ReadOnlyDiskImageCommandOptions; import io.github.applecommander.disassembler.api.Disassembler; import io.github.applecommander.disassembler.api.Instruction; @@ -53,42 +53,6 @@ public class DumpCommand extends ReadOnlyDiskImageCommandOptions { return 0; } - public static class CoordinateSelection { - @ArgGroup(exclusive = false) - private SectorCoordinateSelection sectorCoordinate; - @ArgGroup(exclusive = false) - private BlockCoordinateSelection blockCoordinate; - - public byte[] read(Disk disk) { - if (sectorCoordinate != null) { - return sectorCoordinate.read(disk); - } - else if (blockCoordinate != null) { - return blockCoordinate.read(disk); - } - return disk.readSector(0, 0); - } - - public static class SectorCoordinateSelection { - @Option(names = { "-t", "--track" }, required = true, description = "Track number.") - private Integer track; - @Option(names = { "-s", "--sector" }, required = true, description = "Sector number.") - private Integer sector; - - public byte[] read(Disk disk) { - return disk.readSector(track, sector); - } - } - public static class BlockCoordinateSelection { - @Option(names = { "-b", "--block" }, description = "Block number.") - private Integer block; - - public byte[] read(Disk disk) { - return disk.readBlock(block); - } - } - } - public static class OutputSelection { private BiFunction fn = this::formatHexDump; public String format(int address, byte[] data) { diff --git a/app/cli-acx/src/main/java/io/github/applecommander/acx/command/ReadCommand.java b/app/cli-acx/src/main/java/io/github/applecommander/acx/command/ReadCommand.java new file mode 100644 index 0000000..517e9f4 --- /dev/null +++ b/app/cli-acx/src/main/java/io/github/applecommander/acx/command/ReadCommand.java @@ -0,0 +1,91 @@ +/* + * AppleCommander - An Apple ][ image utility. + * Copyright (C) 2019-2022 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.command; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.function.Consumer; + +import io.github.applecommander.acx.arggroup.CoordinateSelection; +import io.github.applecommander.acx.base.ReadOnlyDiskImageCommandOptions; +import picocli.CommandLine.ArgGroup; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; + +@Command(name = "read", description = "Read a block or sector.") +public class ReadCommand extends ReadOnlyDiskImageCommandOptions { + @ArgGroup(multiplicity = "1", heading = "%nCoordinate Selection:%n") + private CoordinateSelection coordinate = new CoordinateSelection(); + + @ArgGroup(heading = "%nOutput Selection:%n") + private OutputSelection output = new OutputSelection(); + + @Option(names = { "-f", "--force" }, description = "Overwrite existing file (combine with '-o').") + private void selectForceFile(boolean flag) { + openOptions = new OpenOption[] { StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, + StandardOpenOption.WRITE }; + } + private static OpenOption[] openOptions = { StandardOpenOption.CREATE_NEW }; + + @Override + public int handleCommand() throws Exception { + byte[] data = coordinate.read(disk); + output.write(data); + return 0; + } + + public static class OutputSelection { + private Consumer sink = this::writeToStdout; + private String filename; + + public void write(byte[] data) { + sink.accept(data); + } + + @Option(names = "--stdout", description = "Write raw data to stdout. (default)") + public void selectStdout(boolean flag) { + sink = this::writeToStdout; + } + @Option(names = { "-o", "--output" }, description = "Write raw data to file.") + public void selectFile(String filename) { + this.filename = filename; + this.sink = this::writeToFile; + } + + public void writeToStdout(byte[] data) { + try { + System.out.write(data); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + public void writeToFile(byte[] data) { + try { + Files.write(Path.of(filename), data, openOptions); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + } +} diff --git a/app/cli-acx/src/main/java/io/github/applecommander/acx/command/WriteCommand.java b/app/cli-acx/src/main/java/io/github/applecommander/acx/command/WriteCommand.java new file mode 100644 index 0000000..5670164 --- /dev/null +++ b/app/cli-acx/src/main/java/io/github/applecommander/acx/command/WriteCommand.java @@ -0,0 +1,82 @@ +/* + * AppleCommander - An Apple ][ image utility. + * Copyright (C) 2019-2022 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.command; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.function.Supplier; + +import io.github.applecommander.acx.arggroup.CoordinateSelection; +import io.github.applecommander.acx.base.ReadOnlyDiskImageCommandOptions; +import picocli.CommandLine.ArgGroup; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; + +@Command(name = "write", description = "Write a block or sector.") +public class WriteCommand extends ReadOnlyDiskImageCommandOptions { + @ArgGroup(multiplicity = "1", heading = "%nCoordinate Selection:%n") + private CoordinateSelection coordinate = new CoordinateSelection(); + + @ArgGroup(heading = "%nInput Selection:%n") + private InputSelection input = new InputSelection(); + + @Override + public int handleCommand() throws Exception { + byte[] data = input.read(); + coordinate.write(disk, data); + return 0; + } + + public static class InputSelection { + private Supplier source = this::readFromStdin; + private String filename; + + public byte[] read() { + return source.get(); + } + + @Option(names = "--stdin", description = "Read raw data from stdin. (default)") + public void selectStdout(boolean flag) { + source = this::readFromStdin; + } + @Option(names = { "-f", "--input" }, description = "Read raw data from file.") + public void selectFile(String filename) { + this.filename = filename; + this.source = this::readFromFile; + } + + public byte[] readFromStdin() { + try { + return System.in.readAllBytes(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + public byte[] readFromFile() { + try { + return Files.readAllBytes(Path.of(filename)); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + } +}