mirror of
https://github.com/AppleCommander/AppleCommander.git
synced 2024-12-22 23:29:34 +00:00
Adding a -ptx flag to load a text file with high bit cleared. #33
This commit is contained in:
parent
aa3e8ae22f
commit
c26a8b0844
@ -136,7 +136,9 @@ public class ac {
|
|||||||
putFile(args[1], new Name(args[2]), args[3],
|
putFile(args[1], new Name(args[2]), args[3],
|
||||||
(args.length > 4 ? args[4] : "0x2000"));
|
(args.length > 4 ? args[4] : "0x2000"));
|
||||||
} else if ("-pt".equalsIgnoreCase(args[0])) {
|
} else if ("-pt".equalsIgnoreCase(args[0])) {
|
||||||
putTxtFile(args[1], new Name(args[2]));
|
putTxtFileSetHighBit(args[1], new Name(args[2]));
|
||||||
|
} else if ("-ptx".equalsIgnoreCase(args[0])) {
|
||||||
|
putTxtFileClearHighBit(args[1], new Name(args[2]));
|
||||||
} else if ("-d".equalsIgnoreCase(args[0])) { //$NON-NLS-1$
|
} else if ("-d".equalsIgnoreCase(args[0])) { //$NON-NLS-1$
|
||||||
deleteFile(args[1], args[2]);
|
deleteFile(args[1], args[2]);
|
||||||
} else if ("-k".equalsIgnoreCase(args[0])) { //$NON-NLS-1$
|
} else if ("-k".equalsIgnoreCase(args[0])) { //$NON-NLS-1$
|
||||||
@ -270,10 +272,20 @@ public class ac {
|
|||||||
* Put <stdin>. as an Apple text file into the file named
|
* Put <stdin>. as an Apple text file into the file named
|
||||||
* fileName on the disk named imageName.
|
* fileName on the disk named imageName.
|
||||||
*/
|
*/
|
||||||
static void putTxtFile(String imageName, Name name) throws IOException, DiskException {
|
static void putTxtFileSetHighBit(String imageName, Name name) throws IOException, DiskException {
|
||||||
|
// Order on the stream is important to ensure the translated newlines have the high bit done appropriately
|
||||||
putFile(imageName, name, "TXT", "0", TranslatorStream.builder(System.in).lfToCr().setHighBit().get());
|
putFile(imageName, name, "TXT", "0", TranslatorStream.builder(System.in).lfToCr().setHighBit().get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Put <stdin>. as an Apple text file into the file named
|
||||||
|
* fileName on the disk named imageName.
|
||||||
|
*/
|
||||||
|
static void putTxtFileClearHighBit(String imageName, Name name) throws IOException, DiskException {
|
||||||
|
// Order on the stream is important to ensure the translated newlines have the high bit done appropriately
|
||||||
|
putFile(imageName, name, "TXT", "0", TranslatorStream.builder(System.in).lfToCr().clearHighBit().get());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Put InputStream into the file named fileName on the disk named imageName;
|
* Put InputStream into the file named fileName on the disk named imageName;
|
||||||
* Note: only volume level supported; input size unlimited.
|
* Note: only volume level supported; input size unlimited.
|
||||||
|
@ -17,12 +17,19 @@ public class TranslatorStream extends InputStream {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read() throws IOException {
|
public int read() throws IOException {
|
||||||
return fn.apply(sourceStream.read());
|
int data = sourceStream.read();
|
||||||
|
if (data == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return fn.apply(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int setHighBit(int value) {
|
private int setHighBit(int value) {
|
||||||
return value | 0x80;
|
return value | 0x80;
|
||||||
}
|
}
|
||||||
|
private int clearHighBit(int value) {
|
||||||
|
return value & 0x7f;
|
||||||
|
}
|
||||||
private int lfToCr(int value) {
|
private int lfToCr(int value) {
|
||||||
if (value == '\r') {
|
if (value == '\r') {
|
||||||
try {
|
try {
|
||||||
@ -58,6 +65,9 @@ public class TranslatorStream extends InputStream {
|
|||||||
public Builder setHighBit() {
|
public Builder setHighBit() {
|
||||||
return fn(stream::setHighBit);
|
return fn(stream::setHighBit);
|
||||||
}
|
}
|
||||||
|
public Builder clearHighBit() {
|
||||||
|
return fn(stream::clearHighBit);
|
||||||
|
}
|
||||||
public Builder lfToCr() {
|
public Builder lfToCr() {
|
||||||
return fn(stream::lfToCr);
|
return fn(stream::lfToCr);
|
||||||
}
|
}
|
||||||
|
@ -103,29 +103,31 @@ CommandLineErrorMessage = Error: {0}
|
|||||||
CommandLineNoMatchMessage = {0}: No match.
|
CommandLineNoMatchMessage = {0}: No match.
|
||||||
CommandLineStatus = {0} format; {1} bytes free; {2} bytes used.
|
CommandLineStatus = {0} format; {1} bytes free; {2} bytes used.
|
||||||
CommandLineHelp = \
|
CommandLineHelp = \
|
||||||
CommandLineHelp = AppleCommander command line options [{0}]:\n\
|
AppleCommander command line options [{0}]:\n\
|
||||||
-i <imagename> [<imagename>] display information about image(s).\n\
|
-i <imagename> [<imagename>] display information about image(s).\n\
|
||||||
-ls <imagename> [<imagename>] list brief directory of image(s).\n\
|
-ls <imagename> [<imagename>] list brief directory of image(s).\n\
|
||||||
-l <imagename> [<imagename>] list directory of image(s).\n\
|
-l <imagename> [<imagename>] list directory of image(s).\n\
|
||||||
-ll <imagename> [<imagename>] list detailed directory of image(s).\n\
|
-ll <imagename> [<imagename>] list detailed directory of image(s).\n\
|
||||||
-e <imagename> <filename> [<output>] export file from image to stdout\n or to an output file.\n\
|
-e <imagename> <filename> [<output>] export file from image to stdout\n or to an output file.\n\
|
||||||
-x <imagename> [<directory>] extract all files from image to directory.\n\
|
-x <imagename> [<directory>] extract all files from image to directory.\n\
|
||||||
-g <imagename> <filename> [<output>] get raw file from image to stdout\n or to an output file.\n\
|
-g <imagename> <filename> [<output>] get raw file from image to stdout\n or to an output file.\n\
|
||||||
-p <imagename> <filename> <type> [[$|0x]<addr>] put stdin\n in filename on image, using file type and address [0x2000].\n\
|
-p <imagename> <filename> <type> [[$|0x]<addr>] put stdin\n in filename on image, using file type and address [0x2000].\n\
|
||||||
-pt <imagename> <filename> put stdin in filename on image\n defaulting to TXT file type, setting high bit on and replacing\n newline characters with $8D.\n\
|
-pt <imagename> <filename> put stdin in filename on image\n defaulting to TXT file type, setting high bit on and replacing\n newline characters with $8D.\n\
|
||||||
-d <imagename> <filename> delete file from image.\n\
|
-ptx <imagename> <filename> put stdin in filename on image\n defaulting to TXT file type, clearing high bit and replacing\n newline characters with $0D.\n\
|
||||||
-k <imagename> <filename> lock file on image.\n\
|
-d <imagename> <filename> delete file from image.\n\
|
||||||
-u <imagename> <filename> unlock file on image.\n\
|
-k <imagename> <filename> lock file on image.\n\
|
||||||
-n <imagename> <volname> change volume name (ProDOS or Pascal).\n\
|
-u <imagename> <filename> unlock file on image.\n\
|
||||||
-dos <imagename> <filename> <type> put stdin with DOS header\n in filename on image, using file type and address from header.\n\
|
-n <imagename> <volname> change volume name (ProDOS or Pascal).\n\
|
||||||
-as <imagename> [<filename>] put stdin with AppleSingle format\n in filename on image, using file type, address, and (optionally) name\n from the AppleSingle file.\n\
|
-dos <imagename> <filename> <type> put stdin with DOS header\n in filename on image, using file type and address from header.\n\
|
||||||
-geos <imagename> interpret stdin as a GEOS conversion file and\n place it on image (ProDOS only).\n\
|
-as <imagename> [<filename>] put stdin with AppleSingle format\n in filename on image, using file type, address, and (optionally) name\n from the AppleSingle file.\n\
|
||||||
-dos140 <imagename> create a 140K DOS 3.3 image.\n-pro140 <imagename> <volname> create a 140K ProDOS image.\n\
|
-geos <imagename> interpret stdin as a GEOS conversion file and\n place it on image (ProDOS only).\n\
|
||||||
-pro800 <imagename> <volname> create an 800K ProDOS image.\n\
|
-dos140 <imagename> create a 140K DOS 3.3 image.\n\
|
||||||
-pas140 <imagename> <volname> create a 140K Pascal image.\n\
|
-pro140 <imagename> <volname>\n create a 140K ProDOS image.\n\
|
||||||
-pas800 <imagename> <volname> create an 800K Pascal image.\n\
|
-pro800 <imagename> <volname> create an 800K ProDOS image.\n\
|
||||||
|
-pas140 <imagename> <volname> create a 140K Pascal image.\n\
|
||||||
|
-pas800 <imagename> <volname> create an 800K Pascal image.\n\
|
||||||
-convert <filename> <imagename> [<sizeblocks>] uncompress a ShrinkIt or Binary\n II file; or convert a DiskCopy 4.2 image into a ProDOS disk image.\n\
|
-convert <filename> <imagename> [<sizeblocks>] uncompress a ShrinkIt or Binary\n II file; or convert a DiskCopy 4.2 image into a ProDOS disk image.\n\
|
||||||
-bas <imagename> <filename> import an AppleSoft basic file from text\n back to it's tokenized format.
|
-bas <imagename> <filename> import an AppleSoft basic file from text\n back to it's tokenized format.
|
||||||
CommandLineSDKReadOnly = SDK, SHK, and DC42 files are read-only. Use the convert option on them first.
|
CommandLineSDKReadOnly = SDK, SHK, and DC42 files are read-only. Use the convert option on them first.
|
||||||
CommandLineDC42Bad = Unable to interpret this DiskCopy 42 image.
|
CommandLineDC42Bad = Unable to interpret this DiskCopy 42 image.
|
||||||
|
|
||||||
|
@ -4,35 +4,37 @@ import java.io.ByteArrayInputStream;
|
|||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.Parameterized;
|
||||||
|
import org.junit.runners.Parameterized.Parameter;
|
||||||
|
import org.junit.runners.Parameterized.Parameters;
|
||||||
|
|
||||||
|
@RunWith(Parameterized.class)
|
||||||
public class TranslatorStreamTest {
|
public class TranslatorStreamTest {
|
||||||
@Test
|
@Parameters(name = "{index}: {0}")
|
||||||
public void testUnixLineEndings() throws IOException {
|
public static Collection<TestData> data() {
|
||||||
byte[] source = "Hello\nWorld!\n".getBytes();
|
return Arrays.asList(
|
||||||
testAgainstExpected(source);
|
TestData.builder().unixLineEndings().highBitSet().build(),
|
||||||
|
TestData.builder().dosLineEndings().highBitSet().build(),
|
||||||
|
TestData.builder().appleLineEndings().highBitSet().build(),
|
||||||
|
TestData.builder().unixLineEndings().highBitClear().build(),
|
||||||
|
TestData.builder().dosLineEndings().highBitClear().build(),
|
||||||
|
TestData.builder().appleLineEndings().highBitClear().build()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Parameter
|
||||||
public void testDosLineEndings() throws IOException {
|
public TestData testData;
|
||||||
byte[] source = "Hello\r\nWorld!\r\n".getBytes();
|
|
||||||
testAgainstExpected(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAppleLineEndings() throws IOException {
|
public void test() throws IOException {
|
||||||
byte[] source = "Hello\rWorld!\r".getBytes();
|
InputStream is = testData.getSourceStream();
|
||||||
testAgainstExpected(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void testAgainstExpected(final byte[] source) throws IOException {
|
|
||||||
final byte[] expected = { (byte)0xc8, (byte)0xe5, (byte)0xec, (byte)0xec,
|
|
||||||
(byte)0xef, (byte)0x8d, (byte)0xd7, (byte)0xef, (byte)0xf2,
|
|
||||||
(byte)0xec, (byte)0xe4, (byte)0xa1, (byte)0x8d };
|
|
||||||
|
|
||||||
InputStream is = TranslatorStream.builder(new ByteArrayInputStream(source)).lfToCr().setHighBit().get();
|
|
||||||
|
|
||||||
byte[] actual = null;
|
byte[] actual = null;
|
||||||
try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
|
try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
|
||||||
@ -40,6 +42,93 @@ public class TranslatorStreamTest {
|
|||||||
actual = os.toByteArray();
|
actual = os.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.assertArrayEquals(expected, actual);
|
Assert.assertArrayEquals(testData.getExpected(), actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TestData {
|
||||||
|
private String name;
|
||||||
|
private byte[] expected;
|
||||||
|
private InputStream sourceStream;
|
||||||
|
|
||||||
|
public TestData(String name, byte[] expected, InputStream sourceStream) {
|
||||||
|
this.name = name;
|
||||||
|
this.expected = expected;
|
||||||
|
this.sourceStream = sourceStream;
|
||||||
|
}
|
||||||
|
public byte[] getExpected() {
|
||||||
|
return expected;
|
||||||
|
}
|
||||||
|
public InputStream getSourceStream() {
|
||||||
|
return sourceStream;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Builder builder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
private String expectedDescription;
|
||||||
|
private byte[] expected;
|
||||||
|
private String sourceDescription;
|
||||||
|
private byte[] source;
|
||||||
|
private Supplier<TranslatorStream> configurer;
|
||||||
|
|
||||||
|
public Builder unixLineEndings() {
|
||||||
|
this.sourceDescription = "unix line endings";
|
||||||
|
this.source = "Hello\nWorld!\n".getBytes();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public Builder dosLineEndings() {
|
||||||
|
this.sourceDescription = "dos line endings";
|
||||||
|
this.source = "Hello\r\nWorld!\r\n".getBytes();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public Builder appleLineEndings() {
|
||||||
|
this.sourceDescription = "apple line endings";
|
||||||
|
this.source = "Hello\rWorld!\r".getBytes();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/** "Hello^MWorld!^M" with high bit set. */
|
||||||
|
public Builder highBitSet() {
|
||||||
|
this.expectedDescription = "high bit set";
|
||||||
|
this.expected = new byte[] {
|
||||||
|
(byte)0xc8, (byte)0xe5, (byte)0xec, (byte)0xec,
|
||||||
|
(byte)0xef, (byte)0x8d, (byte)0xd7, (byte)0xef, (byte)0xf2,
|
||||||
|
(byte)0xec, (byte)0xe4, (byte)0xa1, (byte)0x8d
|
||||||
|
};
|
||||||
|
this.configurer = this::highBitSetConfigurer;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
private TranslatorStream highBitSetConfigurer() {
|
||||||
|
return TranslatorStream.builder(new ByteArrayInputStream(source)).lfToCr().setHighBit().get();
|
||||||
|
}
|
||||||
|
/** "Hello^MWorld!^M" with high bit clear. */
|
||||||
|
public Builder highBitClear() {
|
||||||
|
this.expectedDescription = "high bit clear";
|
||||||
|
this.expected = new byte[] {
|
||||||
|
0x48, 0x65, 0x6c, 0x6c,
|
||||||
|
0x6f, 0x0d, 0x57, 0x6f, 0x72,
|
||||||
|
0x6c, 0x64, 0x21, 0x0d
|
||||||
|
};
|
||||||
|
this.configurer = this::highBitClearConfigurer;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
private TranslatorStream highBitClearConfigurer() {
|
||||||
|
return TranslatorStream.builder(new ByteArrayInputStream(source)).lfToCr().clearHighBit().get();
|
||||||
|
}
|
||||||
|
public TestData build() {
|
||||||
|
if (this.expected == null || this.expectedDescription == null
|
||||||
|
|| this.source == null || this.sourceDescription == null
|
||||||
|
|| this.configurer == null) {
|
||||||
|
throw new RuntimeException("Not all variables are set!");
|
||||||
|
}
|
||||||
|
String name = String.format("%s with %s", this.sourceDescription, this.expectedDescription);
|
||||||
|
return new TestData(name, this.expected, this.configurer.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user