mirror of
https://github.com/AppleCommander/bastools.git
synced 2025-02-25 11:29:00 +00:00
Adding generate subcommand. #16.
This commit is contained in:
parent
590314877b
commit
707b567b96
api/src
main/java/io/github/applecommander/bastools/api/shapes
test/java/io/github/applecommander/bastools/api/shapes
tools/st/src/main/java/io/github/applecommander/bastools/tools/st
@ -12,6 +12,7 @@ import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.github.applecommander.bastools.api.utils.Streams;
|
||||
|
||||
@ -59,16 +60,32 @@ public class ShapeTable {
|
||||
|
||||
public void write(OutputStream outputStream) throws IOException {
|
||||
Objects.requireNonNull(outputStream);
|
||||
// TODO
|
||||
// Header
|
||||
outputStream.write(shapes.size());
|
||||
outputStream.write(0);
|
||||
// Collect each shape
|
||||
List<byte[]> data = this.shapes.stream()
|
||||
.map(Shape::toVector)
|
||||
.map(VectorShape::toBytes)
|
||||
.collect(Collectors.toList());
|
||||
// Build offset table
|
||||
int offset = 2 + 2*data.size();
|
||||
for (byte[] d : data) {
|
||||
outputStream.write(offset & 0xff);
|
||||
outputStream.write(offset >> 8);
|
||||
offset += d.length;
|
||||
}
|
||||
// Write shape data
|
||||
for (byte[] d : data) {
|
||||
outputStream.write(d);
|
||||
}
|
||||
}
|
||||
|
||||
public void write(File file) throws IOException {
|
||||
Objects.requireNonNull(file);
|
||||
try (OutputStream outputStream = new FileOutputStream(file)) {
|
||||
write(file);
|
||||
}
|
||||
}
|
||||
|
||||
public void write(Path path) throws IOException {
|
||||
Objects.requireNonNull(path);
|
||||
write(path.toFile());
|
||||
|
@ -1,11 +1,13 @@
|
||||
package io.github.applecommander.bastools.api.shapes;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Queue;
|
||||
|
||||
public class VectorShape implements Shape {
|
||||
@ -97,6 +99,45 @@ public class VectorShape implements Shape {
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] toBytes() {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
LinkedList<VectorCommand> work = new LinkedList<>(vectors);
|
||||
while (!work.isEmpty()) {
|
||||
int section1 = work.remove().ordinal();
|
||||
VectorCommand vector2 = work.poll();
|
||||
int section2 = Optional.ofNullable(vector2).map(VectorCommand::ordinal).orElse(0);
|
||||
VectorCommand vector3 = work.poll();
|
||||
if (vector3 != null && vector3.plot) {
|
||||
work.add(0, vector3);
|
||||
vector3 = null;
|
||||
}
|
||||
int section3 = Optional.ofNullable(vector3).map(VectorCommand::ordinal).orElse(0);
|
||||
if ((section1 + section2 + section3) == 0) {
|
||||
// Cannot write a 0 byte except at end
|
||||
if (vector2 == null) {
|
||||
section2 = VectorCommand.MOVE_LEFT.ordinal();
|
||||
} else if (vector3 == null) {
|
||||
section3 = VectorCommand.MOVE_LEFT.ordinal();
|
||||
} else {
|
||||
section3 = VectorCommand.MOVE_LEFT.ordinal();
|
||||
if (!work.isEmpty()) {
|
||||
work.add(0, VectorCommand.MOVE_RIGHT);
|
||||
}
|
||||
}
|
||||
} else if (vector3 == VectorCommand.MOVE_UP) {
|
||||
// section 3 cannot be 0
|
||||
work.add(0, vector3);
|
||||
if (vector2 == VectorCommand.MOVE_UP) {
|
||||
// section 2 and 3 cannot be 0
|
||||
work.add(0, vector2);
|
||||
}
|
||||
}
|
||||
outputStream.write(section3 << 6 | section2 << 3 | section1);
|
||||
}
|
||||
outputStream.write(0);
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return vectors.isEmpty();
|
||||
|
@ -52,7 +52,7 @@ public class TextShapeExporter implements ShapeExporter {
|
||||
int width = blist.stream().mapToInt(BitmapShape::getWidth).max().getAsInt();
|
||||
int height = blist.stream().mapToInt(BitmapShape::getHeight).max().getAsInt();
|
||||
|
||||
int columns = Math.max(1, this.maxWidth / width);
|
||||
int columns = Math.min(Math.max(1, this.maxWidth / width), blist.size());
|
||||
|
||||
PrintWriter pw = new PrintWriter(outputStream);
|
||||
drawTopLine(pw, columns, width);
|
||||
|
@ -1,6 +1,7 @@
|
||||
package io.github.applecommander.bastools.api.shapes;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@ -10,17 +11,42 @@ import org.junit.Test;
|
||||
|
||||
public class ShapesTest {
|
||||
/**
|
||||
* This shape is taken from the Applesoft BASIC Programmer's Reference Manual (1987), p146.
|
||||
* This shape data is taken from the Applesoft BASIC Programmer's Reference Manual (1987), p146.
|
||||
*/
|
||||
public static final byte[] BOX_SAMPLE = { 0x01, 0x00, 0x04, 0x00, 0x12, 0x3F, 0x20, 0x64, 0x2d, 0x15, 0x36, 0x1e, 0x07, 0x00 };
|
||||
/**
|
||||
* These shape vectors are taken from the Applesoft BASIC Programmer's Reference Manual (1987), p146.
|
||||
*/
|
||||
public static VectorShape drawStandardBoxShape() {
|
||||
return new VectorShape()
|
||||
.moveDown().moveDown()
|
||||
.plotLeft().plotLeft()
|
||||
.moveUp().plotUp().plotUp().plotUp()
|
||||
.moveRight().plotRight().plotRight().plotRight()
|
||||
.moveDown().plotDown().plotDown().plotDown()
|
||||
.moveLeft().plotLeft();
|
||||
|
||||
}
|
||||
|
||||
public ShapeTable readStandardShapeTable() {
|
||||
final byte[] sample = { 0x01, 0x00, 0x04, 0x00, 0x12, 0x3F, 0x20, 0x64, 0x2d, 0x15, 0x36, 0x1e, 0x07, 0x00 };
|
||||
ShapeTable st = ShapeTable.read(sample);
|
||||
ShapeTable st = ShapeTable.read(BOX_SAMPLE);
|
||||
assertNotNull(st);
|
||||
assertNotNull(st.shapes);
|
||||
assertEquals(1, st.shapes.size());
|
||||
return st;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteStandardShapeTable() throws IOException {
|
||||
ShapeTable st = new ShapeTable();
|
||||
st.shapes.add(drawStandardBoxShape());
|
||||
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
st.write(outputStream);
|
||||
|
||||
assertArrayEquals(BOX_SAMPLE, outputStream.toByteArray());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardShapeTableVectors() {
|
||||
ShapeTable st = readStandardShapeTable();
|
||||
|
62
tools/st/src/main/java/io/github/applecommander/bastools/tools/st/GenerateCommand.java
Normal file
62
tools/st/src/main/java/io/github/applecommander/bastools/tools/st/GenerateCommand.java
Normal file
@ -0,0 +1,62 @@
|
||||
package io.github.applecommander.bastools.tools.st;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import io.github.applecommander.bastools.api.shapes.ShapeGenerator;
|
||||
import io.github.applecommander.bastools.api.shapes.ShapeTable;
|
||||
import picocli.CommandLine.Command;
|
||||
import picocli.CommandLine.Option;
|
||||
import picocli.CommandLine.Parameters;
|
||||
|
||||
@Command(name = "generate", description = { "Generate a shape table from source code" },
|
||||
parameterListHeading = "%nParameters:%n",
|
||||
descriptionHeading = "%n",
|
||||
optionListHeading = "%nOptions:%n")
|
||||
public class GenerateCommand implements Callable<Void> {
|
||||
@Option(names = { "-h", "--help" }, description = "Show help for subcommand", usageHelp = true)
|
||||
private boolean helpFlag;
|
||||
|
||||
@Option(names = "--stdin", description = "Read from stdin")
|
||||
private boolean stdinFlag;
|
||||
|
||||
@Option(names = "--stdout", description = "Write to stdout")
|
||||
private boolean stdoutFlag;
|
||||
|
||||
@Option(names = { "-o", "--output" }, description = "Write output to file")
|
||||
private Path outputFile;
|
||||
|
||||
@Parameters(arity = "0..1", description = "File to process")
|
||||
private Path inputFile;
|
||||
|
||||
@Override
|
||||
public Void call() throws IOException {
|
||||
validateArguments();
|
||||
|
||||
ShapeTable st = stdinFlag ? ShapeGenerator.generate(System.in) : ShapeGenerator.generate(inputFile);
|
||||
|
||||
if (stdoutFlag) {
|
||||
st.write(System.out);
|
||||
} else {
|
||||
st.write(outputFile);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void validateArguments() throws IOException {
|
||||
if (stdoutFlag && outputFile != null) {
|
||||
throw new IOException("Please choose one of stdout or output file");
|
||||
}
|
||||
if ((stdinFlag && inputFile != null) || (!stdinFlag && inputFile == null)) {
|
||||
throw new IOException("Please select ONE of stdin or file");
|
||||
}
|
||||
|
||||
// Assign defaults
|
||||
if (!stdoutFlag && outputFile == null) {
|
||||
outputFile = Paths.get("shape.out");
|
||||
}
|
||||
}
|
||||
}
|
@ -17,12 +17,13 @@ import picocli.CommandLine.Option;
|
||||
description = "Shape Tools utility",
|
||||
subcommands = {
|
||||
ExtractCommand.class,
|
||||
GenerateCommand.class,
|
||||
HelpCommand.class,
|
||||
})
|
||||
public class Main implements Runnable {
|
||||
@Option(names = "--debug", description = "Dump full stack trackes if an error occurs")
|
||||
@Option(names = "--debug", description = "Dump full stack traces if an error occurs")
|
||||
private static boolean debugFlag;
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
CommandLine.run(new Main(), args);
|
||||
|
Loading…
x
Reference in New Issue
Block a user