mirror of
https://github.com/AppleCommander/AppleCommander.git
synced 2024-12-22 08:30:35 +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],
|
||||
(args.length > 4 ? args[4] : "0x2000"));
|
||||
} 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$
|
||||
deleteFile(args[1], args[2]);
|
||||
} 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
|
||||
* 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());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
* Note: only volume level supported; input size unlimited.
|
||||
|
@ -17,12 +17,19 @@ public class TranslatorStream extends InputStream {
|
||||
|
||||
@Override
|
||||
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) {
|
||||
return value | 0x80;
|
||||
}
|
||||
private int clearHighBit(int value) {
|
||||
return value & 0x7f;
|
||||
}
|
||||
private int lfToCr(int value) {
|
||||
if (value == '\r') {
|
||||
try {
|
||||
@ -58,6 +65,9 @@ public class TranslatorStream extends InputStream {
|
||||
public Builder setHighBit() {
|
||||
return fn(stream::setHighBit);
|
||||
}
|
||||
public Builder clearHighBit() {
|
||||
return fn(stream::clearHighBit);
|
||||
}
|
||||
public Builder lfToCr() {
|
||||
return fn(stream::lfToCr);
|
||||
}
|
||||
|
@ -103,29 +103,31 @@ CommandLineErrorMessage = Error: {0}
|
||||
CommandLineNoMatchMessage = {0}: No match.
|
||||
CommandLineStatus = {0} format; {1} bytes free; {2} bytes used.
|
||||
CommandLineHelp = \
|
||||
CommandLineHelp = AppleCommander command line options [{0}]:\n\
|
||||
-i <imagename> [<imagename>] display information about image(s).\n\
|
||||
-ls <imagename> [<imagename>] list brief directory of image(s).\n\
|
||||
-l <imagename> [<imagename>] list 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\
|
||||
-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\
|
||||
-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\
|
||||
-d <imagename> <filename> delete file from image.\n\
|
||||
-k <imagename> <filename> lock file on image.\n\
|
||||
-u <imagename> <filename> unlock file on image.\n\
|
||||
-n <imagename> <volname> change volume name (ProDOS or Pascal).\n\
|
||||
-dos <imagename> <filename> <type> put stdin with DOS header\n in filename on image, using file type and address from header.\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\
|
||||
-geos <imagename> interpret stdin as a GEOS conversion file and\n place it on image (ProDOS only).\n\
|
||||
-dos140 <imagename> create a 140K DOS 3.3 image.\n-pro140 <imagename> <volname> create a 140K ProDOS 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\
|
||||
AppleCommander command line options [{0}]:\n\
|
||||
-i <imagename> [<imagename>] display information about image(s).\n\
|
||||
-ls <imagename> [<imagename>] list brief directory of image(s).\n\
|
||||
-l <imagename> [<imagename>] list 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\
|
||||
-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\
|
||||
-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\
|
||||
-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\
|
||||
-d <imagename> <filename> delete file from image.\n\
|
||||
-k <imagename> <filename> lock file on image.\n\
|
||||
-u <imagename> <filename> unlock file on image.\n\
|
||||
-n <imagename> <volname> change volume name (ProDOS or Pascal).\n\
|
||||
-dos <imagename> <filename> <type> put stdin with DOS header\n in filename on image, using file type and address from header.\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\
|
||||
-geos <imagename> interpret stdin as a GEOS conversion file and\n place it on image (ProDOS only).\n\
|
||||
-dos140 <imagename> create a 140K DOS 3.3 image.\n\
|
||||
-pro140 <imagename> <volname>\n create a 140K ProDOS 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\
|
||||
-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.
|
||||
CommandLineDC42Bad = Unable to interpret this DiskCopy 42 image.
|
||||
|
||||
|
@ -4,35 +4,37 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.junit.Assert;
|
||||
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 {
|
||||
@Test
|
||||
public void testUnixLineEndings() throws IOException {
|
||||
byte[] source = "Hello\nWorld!\n".getBytes();
|
||||
testAgainstExpected(source);
|
||||
@Parameters(name = "{index}: {0}")
|
||||
public static Collection<TestData> data() {
|
||||
return Arrays.asList(
|
||||
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()
|
||||
);
|
||||
}
|
||||
|
||||
@Parameter
|
||||
public TestData testData;
|
||||
|
||||
@Test
|
||||
public void testDosLineEndings() throws IOException {
|
||||
byte[] source = "Hello\r\nWorld!\r\n".getBytes();
|
||||
testAgainstExpected(source);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAppleLineEndings() throws IOException {
|
||||
byte[] source = "Hello\rWorld!\r".getBytes();
|
||||
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();
|
||||
public void test() throws IOException {
|
||||
InputStream is = testData.getSourceStream();
|
||||
|
||||
byte[] actual = null;
|
||||
try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
|
||||
@ -40,6 +42,93 @@ public class TranslatorStreamTest {
|
||||
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