mirror of
https://github.com/AppleCommander/AppleCommander.git
synced 2026-04-27 02:17:54 +00:00
Merge pull request #149 from AppleCommander/feature/woz-image
Adding WOZ image 5.25" support; adding 13-sector 5&3 decoding (WOZ/Nibble images)
This commit is contained in:
14 files changed
+962
-311
No files matched your search
@@ -19,36 +19,25 @@
|
||||
*/
|
||||
package com.webcodepro.applecommander.storage;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import com.webcodepro.applecommander.storage.os.cpm.CpmFileEntry;
|
||||
import com.webcodepro.applecommander.storage.os.cpm.CpmFormatDisk;
|
||||
import com.webcodepro.applecommander.storage.os.dos33.DosFormatDisk;
|
||||
import com.webcodepro.applecommander.storage.os.dos33.OzDosFormatDisk;
|
||||
import com.webcodepro.applecommander.storage.os.dos33.UniDosFormatDisk;
|
||||
import com.webcodepro.applecommander.storage.os.nakedos.NakedosFormatDisk;
|
||||
import com.webcodepro.applecommander.storage.os.gutenberg.GutenbergFormatDisk;
|
||||
import com.webcodepro.applecommander.storage.os.nakedos.NakedosFormatDisk;
|
||||
import com.webcodepro.applecommander.storage.os.pascal.PascalFormatDisk;
|
||||
import com.webcodepro.applecommander.storage.os.prodos.ProdosFormatDisk;
|
||||
import com.webcodepro.applecommander.storage.os.rdos.RdosFormatDisk;
|
||||
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;
|
||||
import com.webcodepro.applecommander.storage.physical.UniversalDiskImageLayout;
|
||||
import com.webcodepro.applecommander.storage.physical.*;
|
||||
import com.webcodepro.applecommander.util.AppleUtil;
|
||||
import com.webcodepro.applecommander.util.StreamUtil;
|
||||
import com.webcodepro.applecommander.util.TextBundle;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
/**
|
||||
* Abstract representation of an Apple2 disk (floppy, 800k, hard disk).
|
||||
* <p>
|
||||
@@ -130,7 +119,7 @@ public class Disk {
|
||||
private Disk() {
|
||||
filenameFilters = new FilenameFilter[] {
|
||||
new FilenameFilter(textBundle.get("Disk.AllImages"), //$NON-NLS-1$
|
||||
"*.do", "*.dsk", "*.po", "*.nib", "*.2mg", "*.2img", "*.hdv", "*.do.gz", "*.dsk.gz", "*.po.gz", "*.nib.gz", "*.2mg.gz", "*.2img.gz"), //$NON-NLS-1$
|
||||
"*.do", "*.dsk", "*.po", "*.nib", "*.2mg", "*.2img", "*.hdv", "*.do.gz", "*.dsk.gz", "*.po.gz", "*.nib.gz", "*.2mg.gz", "*.2img.gz", "*.woz"), //$NON-NLS-1$
|
||||
new FilenameFilter(textBundle.get("Disk.140kDosImages"), //$NON-NLS-1$
|
||||
"*.do", "*.dsk", "*.do.gz", "*.dsk.gz"), //$NON-NLS-1$
|
||||
new FilenameFilter(textBundle.get("Disk.140kNibbleImages"), //$NON-NLS-1$
|
||||
@@ -143,6 +132,8 @@ public class Disk {
|
||||
"*.hdv"), //$NON-NLS-1$
|
||||
new FilenameFilter(textBundle.get("Disk.CompressedImages"), //$NON-NLS-1$
|
||||
"*.sdk", "*.shk", "*.do.gz", "*.dsk.gz", "*.po.gz", "*.2mg.gz", "*.2img.gz"), //$NON-NLS-1$
|
||||
new FilenameFilter(textBundle.get("Disk.WozImages"),
|
||||
"*.woz"),
|
||||
new FilenameFilter(textBundle.get("Disk.AllFiles"), //$NON-NLS-1$
|
||||
"*.*") //$NON-NLS-1$
|
||||
};
|
||||
@@ -229,10 +220,16 @@ public class Disk {
|
||||
diskImage = diskImageByteArray.toByteArray();
|
||||
}
|
||||
boolean is2img = false;
|
||||
boolean isWoz = false;
|
||||
/* Does it have the 2IMG header? */
|
||||
if ((diskImage[0] == 0x32) && (diskImage[1] == 0x49) && (diskImage[2] == 0x4D) && (diskImage[3]) == 0x47) {
|
||||
is2img = true;
|
||||
}
|
||||
/* Does it have the WOZ1 or WOZ2 header? */
|
||||
else if ((diskImage[0] == 0x57) && (diskImage[1] == 0x4f) && (diskImage[2] == 0x5a)
|
||||
&& ((diskImage[3] == 0x31) || (diskImage[3] == 0x32))) {
|
||||
isWoz = true;
|
||||
}
|
||||
/* Does it have the DiskCopy 4.2 header? */
|
||||
else if (Disk.isDC42(diskImage)) {
|
||||
isDC42 = true;
|
||||
@@ -265,6 +262,8 @@ public class Disk {
|
||||
|
||||
if (isSDK()) {
|
||||
imageOrder = proDosOrder; // SDKs are always in ProDOS order
|
||||
} else if (isWoz) {
|
||||
imageOrder = new WozOrder(diskImageManager);
|
||||
} else {
|
||||
/*
|
||||
* First step: test physical disk orders for viable file systems.
|
||||
@@ -519,6 +518,10 @@ public class Disk {
|
||||
* Identify the size of this disk.
|
||||
*/
|
||||
public int getPhysicalSize() {
|
||||
if (getImageOrder() instanceof WozOrder) {
|
||||
// Total hack since WOZ is currently a special case.
|
||||
return getImageOrder().getPhysicalSize();
|
||||
}
|
||||
if (getDiskImageManager() != null) {
|
||||
return getDiskImageManager().getPhysicalSize();
|
||||
}
|
||||
@@ -605,7 +608,7 @@ public class Disk {
|
||||
// can vary && vtoc[0x02] == 15 // expect catalog to start on sector 15 (140KB disk only!)
|
||||
&& vtoc[0x27] == 122 // expect 122 track/sector pairs per sector
|
||||
&& (vtoc[0x34] == 35 || vtoc[0x34] == 40) // expect 35 or 40 tracks per disk (140KB disk only!)
|
||||
&& vtoc[0x35] == 16 // expect 16 sectors per disk (140KB disk only!)
|
||||
&& (vtoc[0x35] == 16 || vtoc[0x35] == 13) // expect 13 or 16 sectors per disk (140KB disk only!)
|
||||
;
|
||||
if (good) {
|
||||
int catTrack = vtoc[0x01]; // Pull out the first catalog track/sector
|
||||
@@ -789,6 +792,7 @@ public class Disk {
|
||||
*/
|
||||
public boolean isRdosFormat() {
|
||||
if (!is140KbDisk()) return false;
|
||||
if (getImageOrder().getSectorsPerTrack() != 16) return false;
|
||||
byte[] block = readSector(0, 0x0d);
|
||||
String id = AppleUtil.getString(block, 0xe0, 4);
|
||||
return "RDOS".equals(id) || isRdos33Format(); //$NON-NLS-1$
|
||||
|
||||
+4
-2
@@ -130,11 +130,13 @@ public class DosFormatDisk extends FormattedDisk {
|
||||
}
|
||||
|
||||
/**
|
||||
* Identify the operating system format of this disk as DOS 3.3.
|
||||
* Identify the operating system format of this disk as DOS 3.x.
|
||||
* @see com.webcodepro.applecommander.storage.FormattedDisk#getFormat()
|
||||
*/
|
||||
public String getFormat() {
|
||||
return textBundle.get("Dos33"); //$NON-NLS-1$
|
||||
byte[] vtoc = readVtoc();
|
||||
int version = Byte.toUnsignedInt(vtoc[3]);
|
||||
return String.format("DOS 3.%d", version);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+40
-36
@@ -3,29 +3,30 @@
|
||||
* Copyright (C) 2003-2022 by Robert Greene
|
||||
* 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
|
||||
* 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
|
||||
* 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.,
|
||||
* 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 com.webcodepro.applecommander.storage.physical;
|
||||
|
||||
import com.webcodepro.applecommander.storage.Disk;
|
||||
import com.webcodepro.applecommander.storage.StorageBundle;
|
||||
import com.webcodepro.applecommander.storage.os.dos33.DosSectorAddress;
|
||||
import com.webcodepro.applecommander.util.TextBundle;
|
||||
|
||||
/**
|
||||
* Supports disk images stored in DOS physical order.
|
||||
* <p>
|
||||
* <p>
|
||||
* @author Rob Greene (RobGreene@users.sourceforge.net)
|
||||
*/
|
||||
public class DosOrder extends ImageOrder {
|
||||
@@ -43,7 +44,7 @@ public class DosOrder extends ImageOrder {
|
||||
public boolean isBlockDevice() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Indicates that this device is track and sector ordered.
|
||||
*/
|
||||
@@ -57,11 +58,11 @@ public class DosOrder extends ImageOrder {
|
||||
public byte[] readSector(int track, int sector) throws IllegalArgumentException {
|
||||
return readBytes(getOffset(track, sector), Disk.SECTOR_SIZE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Write the specified sector.
|
||||
*/
|
||||
public void writeSector(int track, int sector, byte[] bytes)
|
||||
public void writeSector(int track, int sector, byte[] bytes)
|
||||
throws IllegalArgumentException {
|
||||
writeBytes(getOffset(track, sector), bytes);
|
||||
}
|
||||
@@ -72,18 +73,18 @@ public class DosOrder extends ImageOrder {
|
||||
* with.
|
||||
*/
|
||||
protected int getOffset(int track, int sector) throws IllegalArgumentException {
|
||||
if (!isSizeApprox(Disk.APPLE_140KB_DISK)
|
||||
&& !isSizeApprox(Disk.APPLE_800KB_DISK)
|
||||
&& !isSizeApprox(Disk.APPLE_800KB_2IMG_DISK)
|
||||
&& track != 0 && sector != 0) { // HACK: Allows boot sector writing
|
||||
if (!isSizeApprox(Disk.APPLE_140KB_DISK)
|
||||
&& !isSizeApprox(Disk.APPLE_800KB_DISK)
|
||||
&& !isSizeApprox(Disk.APPLE_800KB_2IMG_DISK)
|
||||
&& track != 0 && sector != 0) { // HACK: Allows boot sector writing
|
||||
throw new IllegalArgumentException(
|
||||
textBundle.get("DosOrder.UnrecognizedFormatError")); //$NON-NLS-1$
|
||||
}
|
||||
int offset = (track * getSectorsPerTrack() + sector) * Disk.SECTOR_SIZE;
|
||||
if (offset > getPhysicalSize()) {
|
||||
throw new IllegalArgumentException(
|
||||
textBundle.format("DosOrder.InvalidSizeError", //$NON-NLS-1$
|
||||
track, sector));
|
||||
textBundle.format("DosOrder.InvalidSizeError", //$NON-NLS-1$
|
||||
track, sector));
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
@@ -93,40 +94,43 @@ public class DosOrder extends ImageOrder {
|
||||
* Note: Defined in terms of reading sectors.
|
||||
*/
|
||||
public byte[] readBlock(int block) {
|
||||
int track = block / 8;
|
||||
int sectorIndex = block % 8;
|
||||
int[] sectorMapping1 = { 0, 13, 11, 9, 7, 5, 3, 1 };
|
||||
int[] sectorMapping2 = { 14, 12, 10, 8, 6, 4, 2, 15 };
|
||||
int sector1 = sectorMapping1[sectorIndex];
|
||||
int sector2 = sectorMapping2[sectorIndex];
|
||||
DosSectorAddress[] sectors = blockToSectors525(block);
|
||||
byte[] blockData = new byte[Disk.BLOCK_SIZE];
|
||||
System.arraycopy(readSector(track, sector1), 0, blockData, 0, Disk.SECTOR_SIZE);
|
||||
System.arraycopy(readSector(track, sector2), 0, blockData, Disk.SECTOR_SIZE, Disk.SECTOR_SIZE);
|
||||
System.arraycopy(readSector(sectors[0].track, sectors[0].sector), 0, blockData, 0, Disk.SECTOR_SIZE);
|
||||
System.arraycopy(readSector(sectors[1].track, sectors[1].sector), 0, blockData, Disk.SECTOR_SIZE, Disk.SECTOR_SIZE);
|
||||
return blockData;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Write the block to the disk image.
|
||||
* Note: Defined in terms of reading sectors.
|
||||
*/
|
||||
public void writeBlock(int block, byte[] data) {
|
||||
int track = block / 8;
|
||||
int sectorIndex = block % 8;
|
||||
int[] sectorMapping1 = { 0, 13, 11, 9, 7, 5, 3, 1 };
|
||||
int[] sectorMapping2 = { 14, 12, 10, 8, 6, 4, 2, 15 };
|
||||
int sector1 = sectorMapping1[sectorIndex];
|
||||
int sector2 = sectorMapping2[sectorIndex];
|
||||
DosSectorAddress[] sectors = blockToSectors525(block);
|
||||
byte[] sectorData = new byte[Disk.SECTOR_SIZE];
|
||||
System.arraycopy(data, 0, sectorData, 0, Disk.SECTOR_SIZE);
|
||||
writeSector(track, sector1, sectorData);
|
||||
writeSector(sectors[0].track, sectors[0].sector, sectorData);
|
||||
System.arraycopy(data, Disk.SECTOR_SIZE, sectorData, 0, Disk.SECTOR_SIZE);
|
||||
writeSector(track, sector2, sectorData);
|
||||
writeSector(sectors[1].track, sectors[1].sector, sectorData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of this image order.
|
||||
*/
|
||||
public String getName() {
|
||||
return textBundle.get("DosOrder.OrderName"); //$NON-NLS-1$
|
||||
return textBundle.get("DosOrder.OrderName"); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
public static DosSectorAddress[] blockToSectors525(int block) {
|
||||
int track = block / 8;
|
||||
int sectorIndex = block % 8;
|
||||
int[] sectorMapping1 = { 0, 13, 11, 9, 7, 5, 3, 1 };
|
||||
int[] sectorMapping2 = { 14, 12, 10, 8, 6, 4, 2, 15 };
|
||||
int sector1 = sectorMapping1[sectorIndex];
|
||||
int sector2 = sectorMapping2[sectorIndex];
|
||||
return new DosSectorAddress[] {
|
||||
new DosSectorAddress(track, sector1),
|
||||
new DosSectorAddress(track, sector2)
|
||||
};
|
||||
}
|
||||
}
|
||||
+345
@@ -0,0 +1,345 @@
|
||||
package com.webcodepro.applecommander.storage.physical;
|
||||
|
||||
import com.webcodepro.applecommander.storage.StorageBundle;
|
||||
import com.webcodepro.applecommander.util.AppleUtil;
|
||||
import com.webcodepro.applecommander.util.TextBundle;
|
||||
|
||||
public class NibbleCodec {
|
||||
private static final TextBundle textBundle = StorageBundle.getInstance();
|
||||
|
||||
/**
|
||||
* This is the 6 and 3 write translate table, as given in Beneath
|
||||
* Apple DOS and Beneath Apple ProDOS 2020, pg 25.
|
||||
*/
|
||||
private static final int[] writeTranslateTable53 = {
|
||||
//$0 $1 $2 $3 $4 $5 $6 $7
|
||||
0xab, 0xad, 0xae, 0xaf, 0xb5, 0xb6, 0xb7, 0xba, // +$00
|
||||
0xbb, 0xbd, 0xbe, 0xbf, 0xd6, 0xd7, 0xda, 0xdb, // +$08
|
||||
0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xed, 0xee, 0xef, // +$10
|
||||
0xf5, 0xf6, 0xf7, 0xfa, 0xfb, 0xfd, 0xfe, 0xff // +$18
|
||||
};
|
||||
/**
|
||||
* The 5&3 read translation table. Constructed from the 5&3
|
||||
* write translate table. Used to decode a disk byte into a
|
||||
* value from 0x00 to 0x1f which is further decoded...
|
||||
*/
|
||||
private static final int[] readTranslateTable53;
|
||||
static {
|
||||
// Construct the read translation table:
|
||||
readTranslateTable53 = new int[256];
|
||||
for (int i = 0; i< writeTranslateTable53.length; i++) {
|
||||
readTranslateTable53[writeTranslateTable53[i]] = i;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the 6 and 2 write translate table, as given in Beneath
|
||||
* Apple DOS, pg 3-21.
|
||||
*/
|
||||
private static final int[] writeTranslateTable62 = {
|
||||
//$0 $1 $2 $3 $4 $5 $6 $7
|
||||
0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6, // +$00
|
||||
0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3, // +$08
|
||||
0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc, // +$10
|
||||
0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3, // +$18
|
||||
0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, // +$20
|
||||
0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec, // +$28
|
||||
0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, // +$30
|
||||
0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff // +$38
|
||||
};
|
||||
/**
|
||||
* The 6&2 read translation table. Constructed from the 6&2
|
||||
* write translate table. Used to decode a disk byte into a
|
||||
* value from 0x00 to 0x3f which is further decoded...
|
||||
*/
|
||||
private static final int[] readTranslateTable62;
|
||||
static {
|
||||
// Construct the read translation table:
|
||||
readTranslateTable62 = new int[256];
|
||||
for (int i = 0; i< writeTranslateTable62.length; i++) {
|
||||
readTranslateTable62[writeTranslateTable62[i]] = i;
|
||||
}
|
||||
}
|
||||
|
||||
public static final int[] DOS32_ADDRESS_PROLOGUE = { 0xd5, 0xaa, 0xb5 };
|
||||
public static final int[] DOS32_DATA_PROLOGUE = { 0xd5, 0xaa, 0xad };
|
||||
public static final int[] DOS33_ADDRESS_PROLOGUE = { 0xd5, 0xaa, 0x96 };
|
||||
public static final int[] DOS33_DATA_PROLOGUE = { 0xd5, 0xaa, 0xad };
|
||||
|
||||
/**
|
||||
* Decode odd-even bytes as stored on disk. The format will be
|
||||
* in two bytes. They are stored as such:<pre>
|
||||
* XX = 1d1d1d1d (odd data bits)
|
||||
* YY = 1d1d1d1d (even data bits)
|
||||
* </pre>
|
||||
* XX is then shifted by a bit and ANDed with YY to get the data byte.
|
||||
* See page 3-12 in Beneath Apple DOS for more information.
|
||||
*/
|
||||
public static int decodeOddEven(byte[] buffer, int offset) {
|
||||
int b1 = AppleUtil.getUnsignedByte(buffer[offset]);
|
||||
int b2 = AppleUtil.getUnsignedByte(buffer[offset+1]);
|
||||
return (b1 << 1 | 0x01) & b2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode odd-even bytes to be stored on disk. See decodeOddEven
|
||||
* for the format.
|
||||
* @see #decodeOddEven
|
||||
*/
|
||||
public static void encodeOddEven(byte[] buffer, int offset, int value) {
|
||||
buffer[offset] = (byte) ((value >> 1) | 0xaa);
|
||||
buffer[offset+1] = (byte) (value | 0xaa);
|
||||
}
|
||||
|
||||
/**
|
||||
* Identify if this is a 13-sector or 16-sector disk. Note that it is "guaranteed"
|
||||
* to return 13, 16, or an exception.
|
||||
*/
|
||||
public static int identifySectorsPerTrack(byte[] trackData) {
|
||||
int sectorsPerTrack = 0;
|
||||
try {
|
||||
byte[] data = readSectorFromTrack62(trackData, 0, 0, 16);
|
||||
if (data != null) sectorsPerTrack = 16;
|
||||
} catch (IllegalArgumentException ex1) {
|
||||
// ignored
|
||||
}
|
||||
if (sectorsPerTrack == 0) {
|
||||
try {
|
||||
byte[] data = readSectorFromTrack53(trackData, 0, 0, 13);
|
||||
if (data != null) sectorsPerTrack = 13;
|
||||
} catch (IllegalArgumentException ex2) {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
if (sectorsPerTrack == 0) {
|
||||
throw new RuntimeException("unable to locate T0,S0 on disk");
|
||||
}
|
||||
return sectorsPerTrack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate physical sector in the given track data. Decodes 6+2 encoding.
|
||||
*/
|
||||
public static byte[] readSectorFromTrack62(byte[] trackData, int track, int physicalSector, int sectorsPerTrack) {
|
||||
// 2. locate address field for this track and sector
|
||||
int offset = 0;
|
||||
byte[] addressField = new byte[14];
|
||||
boolean found = false;
|
||||
int attempts = sectorsPerTrack;
|
||||
while (!found && attempts >= 0) {
|
||||
int nextOffset = locateField(DOS33_ADDRESS_PROLOGUE, trackData, addressField, offset);
|
||||
attempts--;
|
||||
offset = nextOffset;
|
||||
int t = decodeOddEven(addressField, 5);
|
||||
int s = decodeOddEven(addressField, 7);
|
||||
found = (t == track && s == physicalSector);
|
||||
}
|
||||
if (!found) {
|
||||
throw new IllegalArgumentException(textBundle
|
||||
.format("NibbleOrder.InvalidPhysicalSectorError", physicalSector, track, 1)); //$NON-NLS-1$
|
||||
}
|
||||
// 3. read data field that immediately follows the address field
|
||||
byte[] dataField = new byte[349];
|
||||
locateField(DOS33_DATA_PROLOGUE, trackData, dataField, offset);
|
||||
// 4. translate data field
|
||||
byte[] buffer = new byte[342];
|
||||
int checksum = 0;
|
||||
for (int i=0; i<buffer.length; i++) {
|
||||
int b = AppleUtil.getUnsignedByte(dataField[i+3]);
|
||||
checksum ^= readTranslateTable62[b]; // XOR
|
||||
if (i < 86) {
|
||||
buffer[buffer.length - i - 1] = (byte) checksum;
|
||||
} else {
|
||||
buffer[i - 86] = (byte) checksum;
|
||||
}
|
||||
}
|
||||
checksum ^= readTranslateTable62[AppleUtil.getUnsignedByte(dataField[345])];
|
||||
if (checksum != 0) return null; // BAD DATA
|
||||
// 5. decode data field
|
||||
byte[] sectorData = new byte[256];
|
||||
for (int i=0; i<sectorData.length; i++) {
|
||||
int b1 = AppleUtil.getUnsignedByte(buffer[i]);
|
||||
int lowerBits = buffer.length - (i % 86) - 1;
|
||||
int b2 = AppleUtil.getUnsignedByte(buffer[lowerBits]);
|
||||
int shiftPairs = (i / 86) * 2;
|
||||
// shift b1 up by 2 bytes (contains bits 7-2)
|
||||
// align 2 bits in b2 appropriately, mask off anything but
|
||||
// bits 0 and 1 and then REVERSE THEM...
|
||||
int[] reverseValues = { 0x0, 0x2, 0x1, 0x3 };
|
||||
int b = (b1 << 2) | reverseValues[(b2 >> shiftPairs) & 0x03];
|
||||
sectorData[i] = (byte) b;
|
||||
}
|
||||
return sectorData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate physical sector in the given track data. Decodes 5+3 encoding.
|
||||
*/
|
||||
public static byte[] readSectorFromTrack53(byte[] trackData, int track, int physicalSector, int sectorsPerTrack) {
|
||||
// 2. locate address field for this track and sector
|
||||
int offset = 0;
|
||||
byte[] addressField = new byte[14];
|
||||
boolean found = false;
|
||||
int attempts = sectorsPerTrack;
|
||||
while (!found && attempts >= 0) {
|
||||
int nextOffset = locateField(DOS32_ADDRESS_PROLOGUE, trackData, addressField, offset);
|
||||
attempts--;
|
||||
offset = nextOffset;
|
||||
int t = decodeOddEven(addressField, 5);
|
||||
int s = decodeOddEven(addressField, 7);
|
||||
found = (t == track && s == physicalSector);
|
||||
}
|
||||
if (!found) {
|
||||
throw new IllegalArgumentException(textBundle
|
||||
.format("NibbleOrder.InvalidPhysicalSectorError", physicalSector, track, 1)); //$NON-NLS-1$
|
||||
}
|
||||
// 3. read data field that immediately follows the address field (D5 AA AD ...410 data bytes... CKSUM DE AA EB
|
||||
byte[] dataField = new byte[417];
|
||||
locateField(DOS33_DATA_PROLOGUE, trackData, dataField, offset);
|
||||
// 4. translate data field
|
||||
byte[] buffer = new byte[410];
|
||||
int checksum = 0;
|
||||
for (int i=0; i<buffer.length; i++) {
|
||||
int b = AppleUtil.getUnsignedByte(dataField[i+3]);
|
||||
checksum ^= readTranslateTable53[b]; // XOR
|
||||
if (i < 154) {
|
||||
buffer[buffer.length - i - 1] = (byte) checksum;
|
||||
} else {
|
||||
buffer[i - 154] = (byte) checksum;
|
||||
}
|
||||
}
|
||||
checksum ^= readTranslateTable53[AppleUtil.getUnsignedByte(dataField[413])];
|
||||
if (checksum != 0) return null; // BAD DATA
|
||||
// 5. decode data field
|
||||
// Note: This reads from these ranges and leaves two bytes that we use at end:
|
||||
// 0x00-0x33, 0x33-0x65, 0x66-0x98, 0x99-0xcb, 0xcc-0xfe, 0xff - unused
|
||||
// 0x100-0x132, 0x133-0x165, 0x166-0x198, 0x199 - unused
|
||||
byte[] sectorData = new byte[256];
|
||||
offset = 0;
|
||||
for (int i=0x33-1; i>= 0; i--) {
|
||||
int a76543 = Byte.toUnsignedInt(buffer[i]);
|
||||
int b76543 = Byte.toUnsignedInt(buffer[0x33 + i]);
|
||||
int c76543 = Byte.toUnsignedInt(buffer[0x66 + i]);
|
||||
int d76543 = Byte.toUnsignedInt(buffer[0x99 + i]);
|
||||
int e76543 = Byte.toUnsignedInt(buffer[0xcc + i]);
|
||||
int a210d2e2 = Byte.toUnsignedInt(buffer[0x100 + i]);
|
||||
int b210d1e1 = Byte.toUnsignedInt(buffer[0x133 + i]);
|
||||
int c210d0e0 = Byte.toUnsignedInt(buffer[0x166 + i]);
|
||||
|
||||
sectorData[offset++] = (byte) (a76543 << 3 | (a210d2e2 >> 2) & 0x7);
|
||||
sectorData[offset++] = (byte) (b76543 << 3 | (b210d1e1 >> 2) & 0x7);
|
||||
sectorData[offset++] = (byte) (c76543 << 3 | (c210d0e0 >> 2) & 0x7);
|
||||
sectorData[offset++] = (byte) (d76543 << 3 | (a210d2e2 << 1) & 0x4 | b210d1e1 & 0x2 | (c210d0e0 >> 1) & 0x1);
|
||||
sectorData[offset++] = (byte) (e76543 << 3 | (a210d2e2 << 2) & 0x4 | (b210d1e1 << 1) & 0x2 | c210d0e0 & 0x1);
|
||||
}
|
||||
sectorData[255] = (byte)(buffer[0xff] << 3 | buffer[0x199] & 0x07);
|
||||
return sectorData;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Locate a field on the track. These are identified by a 3 byte unique
|
||||
* signature. Because of the way in which disk bytes are captured, we need
|
||||
* to wrap around the track to ensure all sequences of bytes are accounted for.
|
||||
* <p>
|
||||
* This method fills fieldData as well as returning the last position referenced
|
||||
* in the track buffer.
|
||||
*/
|
||||
public static int locateField(int[] prologue, byte[] trackData, byte[] fieldData, int startingOffset) {
|
||||
int i = startingOffset; // logical position in track buffer (can wrap)
|
||||
int position = 0; // physical position in field buffer
|
||||
while (i < trackData.length + fieldData.length) {
|
||||
int offset = i % trackData.length; // physical position in track buffer
|
||||
int b = AppleUtil.getUnsignedByte(trackData[offset]);
|
||||
if (position == 0 && b == prologue[0]) {
|
||||
fieldData[position++] = (byte) b;
|
||||
} else if (position == 1 && b == prologue[1]) {
|
||||
fieldData[position++] = (byte) b;
|
||||
} else if (position == 2 && b == prologue[2]) {
|
||||
fieldData[position++] = (byte) b;
|
||||
} else if (position >= 3 && position <= fieldData.length) {
|
||||
if (position < fieldData.length) fieldData[position++] = (byte) b;
|
||||
if (position == fieldData.length) break; // done!
|
||||
} else {
|
||||
position = 0;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return i % trackData.length;
|
||||
}
|
||||
|
||||
public static void writeSectorToTrack(byte[] trackData, byte[] sectorData, int track, int physicalSector, int sectorsPerTrack) {
|
||||
// 2. locate address field for this track and sector
|
||||
int offset = 0;
|
||||
byte[] addressField = new byte[14];
|
||||
boolean found = false;
|
||||
while (!found && offset < trackData.length) {
|
||||
int nextOffset = locateField(DOS33_ADDRESS_PROLOGUE, trackData, addressField, offset);
|
||||
if (nextOffset < offset) { // we wrapped!
|
||||
throw new IllegalArgumentException(textBundle
|
||||
.format("NibbleOrder.InvalidPhysicalSectorError", physicalSector, track, 2)); //$NON-NLS-1$
|
||||
}
|
||||
offset = nextOffset;
|
||||
int t = decodeOddEven(addressField, 5);
|
||||
int s = decodeOddEven(addressField, 7);
|
||||
found = (t == track && s == physicalSector);
|
||||
}
|
||||
if (!found) {
|
||||
throw new IllegalArgumentException(textBundle
|
||||
.format("NibbleOrder.InvalidPhysicalSectorError", physicalSector, track, 2)); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
// 3. PRENIBBLE: This is Java translated from assembly @ $B800
|
||||
// The Java routine was not working... :o(
|
||||
int[] bb00 = new int[0x100];
|
||||
int[] bc00 = new int[0x56];
|
||||
int x = 0;
|
||||
int y = 2;
|
||||
while (true) {
|
||||
y--;
|
||||
if (y < 0) {
|
||||
y+= 256;
|
||||
}
|
||||
int a = AppleUtil.getUnsignedByte(sectorData[y]);
|
||||
bc00[x]<<= 1;
|
||||
bc00[x]|= a & 1;
|
||||
a>>= 1;
|
||||
bc00[x]<<= 1;
|
||||
bc00[x]|= a & 1;
|
||||
a>>= 1;
|
||||
bb00[y] = a;
|
||||
x++;
|
||||
if (x >= 0x56) {
|
||||
x = 0;
|
||||
if (y == 0) break; // done
|
||||
}
|
||||
}
|
||||
for (x=0; x<0x56; x++) {
|
||||
bc00[x]&= 0x3f;
|
||||
}
|
||||
|
||||
// 4. Translated from portions of WRITE at $B82A:
|
||||
byte[] diskData = new byte[343];
|
||||
int pos = 0;
|
||||
for (y=0x56; y>0; y--) {
|
||||
if (y == 0x56) {
|
||||
diskData[pos++] = (byte) writeTranslateTable62[bc00[y-1]];
|
||||
} else {
|
||||
diskData[pos++] = (byte) writeTranslateTable62[bc00[y] ^ bc00[y-1]];
|
||||
}
|
||||
}
|
||||
diskData[pos++] = (byte) writeTranslateTable62[bc00[0] ^ bb00[y]];
|
||||
for (y=1; y<256; y++) {
|
||||
diskData[pos++] = (byte) writeTranslateTable62[bb00[y] ^ bb00[y-1]];
|
||||
}
|
||||
diskData[pos++] = (byte) writeTranslateTable62[bb00[255]];
|
||||
|
||||
// 5. write to disk (data may wrap - hence the manual copy)
|
||||
byte[] dataFieldPrologue = new byte[3];
|
||||
offset= locateField(DOS33_DATA_PROLOGUE, trackData, dataFieldPrologue, offset);
|
||||
for (int i=0; i<diskData.length; i++) {
|
||||
pos = (offset + i) % trackData.length;
|
||||
trackData[pos] = diskData[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
-232
@@ -3,27 +3,25 @@
|
||||
* Copyright (C) 2003-2022 by Robert Greene
|
||||
* 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
|
||||
* 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
|
||||
* 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.,
|
||||
* 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 com.webcodepro.applecommander.storage.physical;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.webcodepro.applecommander.storage.StorageBundle;
|
||||
import com.webcodepro.applecommander.util.AppleUtil;
|
||||
import com.webcodepro.applecommander.util.TextBundle;
|
||||
import static com.webcodepro.applecommander.storage.physical.NibbleCodec.*;
|
||||
|
||||
/**
|
||||
* Supports disk images stored in nibbilized DOS physical order.
|
||||
@@ -31,51 +29,28 @@ import com.webcodepro.applecommander.util.TextBundle;
|
||||
* @author Rob Greene (RobGreene@users.sourceforge.net)
|
||||
*/
|
||||
public class NibbleOrder extends DosOrder {
|
||||
private TextBundle textBundle = StorageBundle.getInstance();
|
||||
/**
|
||||
* This is the 6 and 2 write translate table, as given in Beneath
|
||||
* Apple DOS, pg 3-21.
|
||||
*/
|
||||
private static int[] writeTranslateTable = {
|
||||
//$0 $1 $2 $3 $4 $5 $6 $7
|
||||
0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6, // +$00
|
||||
0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3, // +$08
|
||||
0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc, // +$10
|
||||
0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3, // +$18
|
||||
0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, // +$20
|
||||
0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec, // +$28
|
||||
0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, // +$30
|
||||
0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff // +$38
|
||||
};
|
||||
/**
|
||||
* This maps a DOS 3.3 sector to a physical sector.
|
||||
* (readSector and writeSector work off of the DOS 3.3
|
||||
* sector numbering.)
|
||||
*/
|
||||
private static int[] sectorInterleave = {
|
||||
0x0, 0xd, 0xb, 0x9, 0x7, 0x5, 0x3, 0x1,
|
||||
0xe, 0xc, 0xa, 0x8, 0x6, 0x4, 0x2, 0xf
|
||||
};
|
||||
|
||||
/**
|
||||
* The read translation table. Constructed from the write
|
||||
* translate table. Used to decode a disk byte into a value
|
||||
* from 0x00 to 0x3f which is further decoded...
|
||||
*/
|
||||
public int[] readTranslateTable;
|
||||
|
||||
public static final int[] DOS_SECTOR_SKEW = {
|
||||
0x0, 0xd, 0xb, 0x9, 0x7, 0x5, 0x3, 0x1,
|
||||
0xe, 0xc, 0xa, 0x8, 0x6, 0x4, 0x2, 0xf
|
||||
};
|
||||
|
||||
private int sectorsPerTrack = 16;
|
||||
|
||||
/**
|
||||
* Construct a NibbleOrder.
|
||||
*/
|
||||
public NibbleOrder(ByteArrayImageLayout diskImageManager) {
|
||||
super(diskImageManager);
|
||||
// Construct the read translation table:
|
||||
readTranslateTable = new int[256];
|
||||
for (int i=0; i<writeTranslateTable.length; i++) {
|
||||
readTranslateTable[writeTranslateTable[i]] = i;
|
||||
}
|
||||
// Identify 13-sector vs 16-sector
|
||||
byte[] trackData = readTrackData(0);
|
||||
sectorsPerTrack = identifySectorsPerTrack(trackData);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read nibbilized track data.
|
||||
*/
|
||||
@@ -83,7 +58,7 @@ public class NibbleOrder extends DosOrder {
|
||||
int trackSize = getPhysicalSize() / getTracksPerDisk();
|
||||
return readBytes(track * trackSize, trackSize);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Write nibbilized track data.
|
||||
*/
|
||||
@@ -97,210 +72,41 @@ public class NibbleOrder extends DosOrder {
|
||||
* for this process is directly from Beneath Apple DOS, chapter 3.
|
||||
*/
|
||||
public byte[] readSector(int track, int dosSector) throws IllegalArgumentException {
|
||||
int sector = sectorInterleave[dosSector];
|
||||
// 1. read track
|
||||
byte[] trackData = readTrackData(track);
|
||||
// 2. locate address field for this track and sector
|
||||
int offset = 0;
|
||||
byte[] addressField = new byte[14];
|
||||
boolean found = false;
|
||||
int attempts = getSectorsPerTrack();
|
||||
while (!found && attempts >= 0) {
|
||||
int nextOffset = locateField(0xd5, 0xaa, 0x96, trackData, addressField, offset);
|
||||
attempts--;
|
||||
offset = nextOffset;
|
||||
int t = decodeOddEven(addressField, 5);
|
||||
int s = decodeOddEven(addressField, 7);
|
||||
found = (t == track && s == sector);
|
||||
if (sectorsPerTrack == 16) {
|
||||
int sector = DOS_SECTOR_SKEW[dosSector];
|
||||
byte[] trackData = readTrackData(track);
|
||||
return readSectorFromTrack62(trackData, track, sector, getSectorsPerTrack());
|
||||
} else {
|
||||
byte[] trackData = readTrackData(track);
|
||||
return readSectorFromTrack53(trackData, track, dosSector, getSectorsPerTrack());
|
||||
}
|
||||
if (!found) {
|
||||
throw new IllegalArgumentException(textBundle
|
||||
.format("NibbleOrder.InvalidPhysicalSectorError", sector, track, 1)); //$NON-NLS-1$
|
||||
}
|
||||
// 3. read data field that immediately follows the address field
|
||||
byte[] dataField = new byte[349];
|
||||
locateField(0xd5, 0xaa, 0xad, trackData, dataField, offset);
|
||||
// 4. translate data field
|
||||
byte[] buffer = new byte[342];
|
||||
int checksum = 0;
|
||||
for (int i=0; i<buffer.length; i++) {
|
||||
int b = AppleUtil.getUnsignedByte(dataField[i+3]);
|
||||
checksum ^= readTranslateTable[b]; // XOR
|
||||
if (i < 86) {
|
||||
buffer[buffer.length - i - 1] = (byte) checksum;
|
||||
} else {
|
||||
buffer[i - 86] = (byte) checksum;
|
||||
}
|
||||
}
|
||||
checksum ^= readTranslateTable[AppleUtil.getUnsignedByte(dataField[345])];
|
||||
if (checksum != 0) return null; // BAD DATA
|
||||
// 5. decode data field
|
||||
byte[] sectorData = new byte[256];
|
||||
for (int i=0; i<sectorData.length; i++) {
|
||||
int b1 = AppleUtil.getUnsignedByte(buffer[i]);
|
||||
int lowerBits = buffer.length - (i % 86) - 1;
|
||||
int b2 = AppleUtil.getUnsignedByte(buffer[lowerBits]);
|
||||
int shiftPairs = (i / 86) * 2;
|
||||
// shift b1 up by 2 bytes (contains bits 7-2)
|
||||
// align 2 bits in b2 appropriately, mask off anything but
|
||||
// bits 0 and 1 and then REVERSE THEM...
|
||||
int[] reverseValues = { 0x0, 0x2, 0x1, 0x3 };
|
||||
int b = (b1 << 2) | reverseValues[(b2 >> shiftPairs) & 0x03];
|
||||
sectorData[i] = (byte) b;
|
||||
}
|
||||
return sectorData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate a field on the track. These are identified by a 3 byte unique
|
||||
* signature. Because of the way in which disk bytes are captured, we need
|
||||
* to wrap around the track to ensure all sequences of bytes are accounted for.
|
||||
* <p>
|
||||
* This method fills fieldData as well as returning the last position referenced
|
||||
* in the track buffer.
|
||||
*/
|
||||
protected int locateField(int byte1, int byte2, int byte3, byte[] trackData, byte[] fieldData, int startingOffset) {
|
||||
int i = startingOffset; // logical position in track buffer (can wrap)
|
||||
int position = 0; // physical position in field buffer
|
||||
while (i < trackData.length + fieldData.length) {
|
||||
int offset = i % trackData.length; // physical position in track buffer
|
||||
int b = AppleUtil.getUnsignedByte(trackData[offset]);
|
||||
if (position == 0 && b == byte1) {
|
||||
fieldData[position++] = (byte) b;
|
||||
} else if (position == 1 && b == byte2) {
|
||||
fieldData[position++] = (byte) b;
|
||||
} else if (position == 2 && b == byte3) {
|
||||
fieldData[position++] = (byte) b;
|
||||
} else if (position >= 3 && position <= fieldData.length) {
|
||||
if (position < fieldData.length) fieldData[position++] = (byte) b;
|
||||
if (position == fieldData.length) break; // done!
|
||||
} else {
|
||||
position = 0;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return i % trackData.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode odd-even bytes as stored on disk. The format will be
|
||||
* in two bytes. They are stored as such:<pre>
|
||||
* XX = 1d1d1d1d (odd data bits)
|
||||
* YY = 1d1d1d1d (even data bits)
|
||||
* </pre>
|
||||
* XX is then shifted by a bit and ANDed with YY to get the data byte.
|
||||
* See page 3-12 in Beneath Apple DOS for more information.
|
||||
*/
|
||||
protected int decodeOddEven(byte[] buffer, int offset) {
|
||||
int b1 = AppleUtil.getUnsignedByte(buffer[offset]);
|
||||
int b2 = AppleUtil.getUnsignedByte(buffer[offset+1]);
|
||||
return (b1 << 1 | 0x01) & b2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode odd-even bytes to be stored on disk. See decodeOddEven
|
||||
* for the format.
|
||||
* @see #decodeOddEven
|
||||
*/
|
||||
protected void encodeOddEven(byte[] buffer, int offset, int value) {
|
||||
buffer[offset] = (byte) ((value >> 1) | 0xaa);
|
||||
buffer[offset+1] = (byte) (value | 0xaa);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Write the specified sector.
|
||||
*/
|
||||
public void writeSector(int track, int dosSector, byte[] sectorData) throws IllegalArgumentException {
|
||||
int sector = sectorInterleave[dosSector];
|
||||
// 1. read track
|
||||
if (sectorsPerTrack == 13) {
|
||||
throw new RuntimeException("writing to 13-sector disks not supported");
|
||||
}
|
||||
int sector = DOS_SECTOR_SKEW[dosSector];
|
||||
byte[] trackData = readTrackData(track);
|
||||
// 2. locate address field for this track and sector
|
||||
int offset = 0;
|
||||
byte[] addressField = new byte[14];
|
||||
boolean found = false;
|
||||
while (!found && offset < trackData.length) {
|
||||
int nextOffset = locateField(0xd5, 0xaa, 0x96, trackData, addressField, offset);
|
||||
if (nextOffset < offset) { // we wrapped!
|
||||
throw new IllegalArgumentException(textBundle
|
||||
.format("NibbleOrder.InvalidPhysicalSectorError", sector, track, 2)); //$NON-NLS-1$
|
||||
}
|
||||
offset = nextOffset;
|
||||
int t = decodeOddEven(addressField, 5);
|
||||
int s = decodeOddEven(addressField, 7);
|
||||
found = (t == track && s == sector);
|
||||
}
|
||||
if (!found) {
|
||||
throw new IllegalArgumentException(textBundle
|
||||
.format("NibbleOrder.InvalidPhysicalSectorError", sector, track, 2)); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
// 3. PRENIBBLE: This is Java translated from assembly @ $B800
|
||||
// The Java routine was not working... :o(
|
||||
int[] bb00 = new int[0x100];
|
||||
int[] bc00 = new int[0x56];
|
||||
int x = 0;
|
||||
int y = 2;
|
||||
while (true) {
|
||||
y--;
|
||||
if (y < 0) {
|
||||
y+= 256;
|
||||
}
|
||||
int a = AppleUtil.getUnsignedByte(sectorData[y]);
|
||||
bc00[x]<<= 1;
|
||||
bc00[x]|= a & 1;
|
||||
a>>= 1;
|
||||
bc00[x]<<= 1;
|
||||
bc00[x]|= a & 1;
|
||||
a>>= 1;
|
||||
bb00[y] = a;
|
||||
x++;
|
||||
if (x >= 0x56) {
|
||||
x = 0;
|
||||
if (y == 0) break; // done
|
||||
}
|
||||
}
|
||||
for (x=0; x<0x56; x++) {
|
||||
bc00[x]&= 0x3f;
|
||||
}
|
||||
|
||||
// 4. Translated from portions of WRITE at $B82A:
|
||||
byte[] diskData = new byte[343];
|
||||
int pos = 0;
|
||||
for (y=0x56; y>0; y--) {
|
||||
if (y == 0x56) {
|
||||
diskData[pos++] = (byte) writeTranslateTable[bc00[y-1]];
|
||||
} else {
|
||||
diskData[pos++] = (byte) writeTranslateTable[bc00[y] ^ bc00[y-1]];
|
||||
}
|
||||
}
|
||||
diskData[pos++] = (byte) writeTranslateTable[bc00[0] ^ bb00[y]];
|
||||
for (y=1; y<256; y++) {
|
||||
diskData[pos++] = (byte) writeTranslateTable[bb00[y] ^ bb00[y-1]];
|
||||
}
|
||||
diskData[pos++] = (byte) writeTranslateTable[bb00[255]];
|
||||
|
||||
// 5. write to disk (data may wrap - hence the manual copy)
|
||||
byte[] dataFieldPrologue = new byte[3];
|
||||
offset= locateField(0xd5, 0xaa, 0xad, trackData, dataFieldPrologue, offset);
|
||||
for (int i=0; i<diskData.length; i++) {
|
||||
pos = (offset + i) % trackData.length;
|
||||
trackData[pos] = diskData[i];
|
||||
}
|
||||
writeSectorToTrack(trackData, sectorData, track, sector, getSectorsPerTrack());
|
||||
writeTrackData(track, trackData);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Answer with the number of tracks on this device.
|
||||
*/
|
||||
public int getTracksPerDisk() {
|
||||
return 35;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Answer with the number of sectors per track on this device.
|
||||
*/
|
||||
public int getSectorsPerTrack() {
|
||||
return 16;
|
||||
return sectorsPerTrack;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -309,7 +115,7 @@ public class NibbleOrder extends DosOrder {
|
||||
* bytes (6+2 encoded) instead of a full 8-bit byte.
|
||||
*/
|
||||
public int getBlocksOnDevice() {
|
||||
return 280;
|
||||
return 280; // Note: Only relevant to DOS 3.3 disks; irrelevant for DOS 3.2. (Right?)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -320,6 +126,9 @@ public class NibbleOrder extends DosOrder {
|
||||
* sector markers.
|
||||
*/
|
||||
public void format() {
|
||||
if (sectorsPerTrack == 13) {
|
||||
throw new RuntimeException("formatting 13-sector disks not supported");
|
||||
}
|
||||
// pre-fill entire disk with 0xff
|
||||
byte[] diskImage = new byte[232960]; // 6656 bytes per track
|
||||
Arrays.fill(diskImage, (byte)0xff);
|
||||
|
||||
+2
-2
@@ -33,14 +33,14 @@ public class ProdosOrder extends ImageOrder {
|
||||
/**
|
||||
* This table contains the block offset for a particular DOS sector.
|
||||
*/
|
||||
private static final int[] blockInterleave = {
|
||||
public static final int[] blockInterleave = {
|
||||
0, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 7
|
||||
};
|
||||
/**
|
||||
* Defines the location within a block in which the DOS sector resides.
|
||||
* (0 = 0-255 and 1 = 256-511.)
|
||||
*/
|
||||
private static final int[] blockOffsets = {
|
||||
public static final int[] blockOffsets = {
|
||||
0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1
|
||||
};
|
||||
|
||||
|
||||
+453
@@ -0,0 +1,453 @@
|
||||
package com.webcodepro.applecommander.storage.physical;
|
||||
|
||||
import com.webcodepro.applecommander.storage.Disk;
|
||||
import com.webcodepro.applecommander.storage.os.dos33.DosSectorAddress;
|
||||
import com.webcodepro.applecommander.util.AppleUtil;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static com.webcodepro.applecommander.storage.physical.NibbleCodec.*;
|
||||
import static com.webcodepro.applecommander.storage.physical.NibbleOrder.DOS_SECTOR_SKEW;
|
||||
import static com.webcodepro.applecommander.storage.physical.ProdosOrder.blockInterleave;
|
||||
import static com.webcodepro.applecommander.storage.physical.ProdosOrder.blockOffsets;
|
||||
|
||||
public class WozOrder extends ImageOrder {
|
||||
// The magic bytes are read in little-endian order, so they do appear reversed here.
|
||||
public static final int WOZ1_MAGIC = 0x315a4f57;
|
||||
public static final int WOZ2_MAGIC = 0x325a4f57;
|
||||
public static final int INFO_CHUNK_ID = 0x4f464e49;
|
||||
public static final int TMAP_CHUNK_ID = 0x50414d54;
|
||||
public static final int TRKS_CHUNK_ID = 0x534b5254;
|
||||
public static final int FLUX_CHUNK_ID = 0x54495257;
|
||||
public static final int META_CHUNK_ID = 0x4154454d;
|
||||
|
||||
// WOZ image representation
|
||||
private InfoChunk info;
|
||||
private Map<String,String> meta = new HashMap<>();
|
||||
private List<TmapChunk> tmap = new ArrayList<>();
|
||||
private List<TrkInfo> trks = new ArrayList<>();
|
||||
|
||||
// Internal configuration settings
|
||||
private boolean blockDevice;
|
||||
private boolean trackAndSectorDevice;
|
||||
private int blocksOnDevice;
|
||||
private int sectorsPerTrack;
|
||||
private Function<Integer,byte[]> trackReader = null;
|
||||
private Function<Integer,byte[]> blockReader = this::readBlock525;
|
||||
private BiFunction<Integer,Integer,byte[]> sectorReader = this::readSector525;
|
||||
|
||||
public WozOrder(ByteArrayImageLayout layout) {
|
||||
super(layout);
|
||||
|
||||
ByteBuffer bb = ByteBuffer.wrap(layout.getDiskImage());
|
||||
bb.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
int sig = bb.getInt();
|
||||
int test = bb.getInt();
|
||||
final int testExpected = 0xa0d0aff;
|
||||
if (sig == WOZ1_MAGIC && test == testExpected) {
|
||||
this.trackReader = this::readTrackDataWOZ1;
|
||||
}
|
||||
else if (sig == WOZ2_MAGIC && test == testExpected) {
|
||||
this.trackReader = this::readTrackDataWOZ2;
|
||||
}
|
||||
else {
|
||||
throw new RuntimeException("Not a WOZ1 or WOZ2 format file.");
|
||||
}
|
||||
bb.getInt(); // ignoring CRC
|
||||
|
||||
Consumer<byte[]> tmapReader = this::readTmapChunk525;
|
||||
while (bb.hasRemaining()) {
|
||||
int chunkId = bb.getInt();
|
||||
int chunkSize = bb.getInt();
|
||||
byte[] data = new byte[chunkSize];
|
||||
bb.get(data);
|
||||
switch (chunkId) {
|
||||
case INFO_CHUNK_ID:
|
||||
this.info = new InfoChunk(data);
|
||||
if (this.info.getDiskType() == 1) { // DISK II - 5.25"
|
||||
this.blockDevice = false;
|
||||
this.trackAndSectorDevice = true;
|
||||
this.blocksOnDevice = 280;
|
||||
tmapReader = this::readTmapChunk525;
|
||||
this.blockReader = this::readBlock525;
|
||||
this.sectorReader = this::readSector525;
|
||||
}
|
||||
else { // 400K or 800K 3.5" disk
|
||||
this.blockDevice = true;
|
||||
this.trackAndSectorDevice = false;
|
||||
this.blocksOnDevice = this.info.getDiskSides() * 800;
|
||||
tmapReader = this::readTmapChunk35;
|
||||
//this.blockReader = this::readBlock35;
|
||||
this.sectorReader = this::readSector35;
|
||||
}
|
||||
break;
|
||||
case TMAP_CHUNK_ID:
|
||||
tmapReader.accept(data);
|
||||
break;
|
||||
case TRKS_CHUNK_ID:
|
||||
readTrksChunk(data);
|
||||
case META_CHUNK_ID:
|
||||
readMetaChunk(data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Identify 13-sector vs 16-sector
|
||||
byte[] trackData = readTrackData(0);
|
||||
sectorsPerTrack = identifySectorsPerTrack(trackData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "WOZ Disk Image";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPhysicalSize() {
|
||||
// We fake it. "Size" is sometimes used to determine OS type, so we give the decoded disk size.
|
||||
return this.blocksOnDevice * 512;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSectorsPerTrack() {
|
||||
return sectorsPerTrack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] readBlock(int block) {
|
||||
return this.blockReader.apply(block);
|
||||
}
|
||||
|
||||
public byte[] readBlock525(int block) {
|
||||
if (sectorsPerTrack == 13) {
|
||||
// 13 sector disks don't map to blocks, but the interrogation routines don't know this; faking it.
|
||||
return new byte[512];
|
||||
}
|
||||
byte[] sector1;
|
||||
byte[] sector2;
|
||||
DosSectorAddress[] sectors;
|
||||
sectors = DosOrder.blockToSectors525(block);
|
||||
sector1 = readSector(sectors[0].track, sectors[0].sector);
|
||||
sector2 = readSector(sectors[1].track, sectors[1].sector);
|
||||
byte[] blockData = new byte[Disk.BLOCK_SIZE];
|
||||
System.arraycopy(sector1, 0, blockData, 0, Disk.SECTOR_SIZE);
|
||||
System.arraycopy(sector2, 0, blockData, Disk.SECTOR_SIZE, Disk.SECTOR_SIZE);
|
||||
return blockData;
|
||||
}
|
||||
|
||||
// public byte[] readBlock35(int block) {
|
||||
// DosSectorAddress addr = blockToSector35(block);
|
||||
// byte[] trackData = readTrackData(addr.track);
|
||||
// return readSectorFromTrack35(trackData, addr.track, addr.sector, getSectorsPerTrack());
|
||||
// }
|
||||
|
||||
public DosSectorAddress blockToSector35(int block) {
|
||||
// 12x8, 11x8, 10x8, 9x8, 8x8 = 96+88+80+72+64 = 400 sectors per side
|
||||
int sector = block;
|
||||
int maxSectorsOnTrack = 0;
|
||||
int track = 0;
|
||||
locateBlockLoop:
|
||||
for (int i=0; i<info.getDiskSides(); i++) {
|
||||
for (int s=12; s>=8; s--) {
|
||||
maxSectorsOnTrack = s;
|
||||
for (int c=0; c<8; c++) {
|
||||
if (sector < maxSectorsOnTrack) {
|
||||
break locateBlockLoop;
|
||||
}
|
||||
sector -= s;
|
||||
track++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new DosSectorAddress(track, sector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBlock(int block, byte[] data) {
|
||||
throw new RuntimeException("WOZ Disk Image does not support writing at this time");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBlockDevice() {
|
||||
return blockDevice;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTrackAndSectorDevice() {
|
||||
return trackAndSectorDevice;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlocksOnDevice() {
|
||||
return blocksOnDevice;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] readSector(int track, int sector) throws IllegalArgumentException {
|
||||
return this.sectorReader.apply(track, sector);
|
||||
}
|
||||
|
||||
public byte[] readSector525(int track, int sector) throws IllegalArgumentException {
|
||||
if (sectorsPerTrack == 16) {
|
||||
sector = DOS_SECTOR_SKEW[sector];
|
||||
byte[] trackData = readTrackData(track);
|
||||
return readSectorFromTrack62(trackData, track, sector, getSectorsPerTrack());
|
||||
} else {
|
||||
byte[] trackData = readTrackData(track);
|
||||
return readSectorFromTrack53(trackData, track, sector, getSectorsPerTrack());
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] readSector35(int track, int sector) throws IllegalArgumentException {
|
||||
int block = track * 8 + blockInterleave[sector];
|
||||
byte[] blockData = readBlock(block);
|
||||
int offset = blockOffsets[sector];
|
||||
byte[] sectorData = new byte[Disk.SECTOR_SIZE];
|
||||
System.arraycopy(blockData, offset * Disk.SECTOR_SIZE,
|
||||
sectorData, 0, Disk.SECTOR_SIZE);
|
||||
return sectorData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSector(int track, int sector, byte[] bytes) throws IllegalArgumentException {
|
||||
throw new RuntimeException("WOZ Disk Image does not support writing at this time");
|
||||
}
|
||||
|
||||
private void readMetaChunk(byte[] data) {
|
||||
if (data.length > 0) {
|
||||
String meta = new String(data);
|
||||
for (String line : meta.split("\n")) {
|
||||
String[] parts = line.split("\t");
|
||||
if (parts.length == 2) {
|
||||
this.meta.put(parts[0], parts[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void readTmapChunk525(byte[] data) {
|
||||
ByteBuffer bb = ByteBuffer.wrap(data);
|
||||
while (bb.hasRemaining()) {
|
||||
byte[] chunk = new byte[4];
|
||||
bb.get(chunk);
|
||||
tmap.add(new TmapChunk(chunk));
|
||||
}
|
||||
}
|
||||
|
||||
private void readTmapChunk35(byte[] data) {
|
||||
ByteBuffer bb = ByteBuffer.wrap(data);
|
||||
while (bb.hasRemaining()) {
|
||||
byte chunk = bb.get();
|
||||
tmap.add(new TmapChunk(chunk));
|
||||
}
|
||||
}
|
||||
|
||||
private void readTrksChunk(byte[] data) {
|
||||
// Note: WOZ2 only
|
||||
ByteBuffer bb = ByteBuffer.wrap(data);
|
||||
bb.order(ByteOrder.LITTLE_ENDIAN);
|
||||
for (int i=0; i<160; i++) {
|
||||
trks.add(new TrkInfo(bb));
|
||||
}
|
||||
}
|
||||
|
||||
protected byte[] readTrackData(int track) {
|
||||
return this.trackReader.apply(track);
|
||||
}
|
||||
|
||||
protected byte[] readTrackDataWOZ2(int track) {
|
||||
TmapChunk map = tmap.get(track);
|
||||
int trkInfo = map.getOffset00();
|
||||
if (trkInfo == 255) trkInfo = map.getOffset25();
|
||||
if (trkInfo == 255) trkInfo = map.getOffset50();
|
||||
if (trkInfo == 255) trkInfo = map.getOffset75();
|
||||
TrkInfo trk = trks.get(trkInfo);
|
||||
byte[] rawData = readBytes(trk.getStartingBlock()*512, trk.getBlockCount()*512);
|
||||
return transformBitstream(rawData, trk.getBitCount());
|
||||
}
|
||||
|
||||
protected byte[] readTrackDataWOZ1(int track) {
|
||||
final int trackLength = 6656;
|
||||
int start = 256 + (track * trackLength);
|
||||
byte[] details = readBytes(start + 6646, 10);
|
||||
int bytesUsed = AppleUtil.getWordValue(details, 0);
|
||||
int bitCount = AppleUtil.getWordValue(details, 2);
|
||||
int end = start + bytesUsed;
|
||||
byte[] rawData = readBytes(start, end);
|
||||
return transformBitstream(rawData, bitCount);
|
||||
}
|
||||
|
||||
protected byte[] transformBitstream(byte[] rawData, int bitCount) {
|
||||
// NOTE: Uncertain if we need to track 0's (only 2 allowed by hardware) or not.
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
int bitNo = 7;
|
||||
int byteNo = 0;
|
||||
int byteVal = 0;
|
||||
while (bitCount > 0) {
|
||||
bitCount--;
|
||||
byteVal <<= 1;
|
||||
if (AppleUtil.isBitSet(rawData[byteNo], bitNo)) {
|
||||
byteVal |= 1;
|
||||
}
|
||||
bitNo--;
|
||||
if (bitNo < 0) {
|
||||
bitNo = 7;
|
||||
byteNo++;
|
||||
}
|
||||
if (byteVal >= 128) {
|
||||
baos.write(byteVal);
|
||||
byteVal = 0;
|
||||
}
|
||||
}
|
||||
return baos.toByteArray();
|
||||
|
||||
}
|
||||
|
||||
public static class InfoChunk {
|
||||
private int version;
|
||||
private int diskType;
|
||||
private int writeProtected;
|
||||
private int synchrinized;
|
||||
private int cleaned;
|
||||
private String creator;
|
||||
private int diskSides;
|
||||
private int bootSectorFormat;
|
||||
private int optimalBitTiming;
|
||||
private int compatibleHardware;
|
||||
private int requiredRAM;
|
||||
private int largestTrack;
|
||||
private int fluxBlock;
|
||||
private int largestFluxTrack;
|
||||
|
||||
public InfoChunk(byte[] data) {
|
||||
ByteBuffer bb = ByteBuffer.wrap(data);
|
||||
bb.order(ByteOrder.LITTLE_ENDIAN);
|
||||
this.version = bb.get();
|
||||
this.diskType = bb.get();
|
||||
this.writeProtected = bb.get();
|
||||
this.synchrinized = bb.get();
|
||||
this.cleaned = bb.get();
|
||||
byte[] creator = new byte[32];
|
||||
bb.get(creator);
|
||||
this.creator = new String(creator);
|
||||
if (this.version == 2) {
|
||||
this.diskSides = bb.get();
|
||||
this.bootSectorFormat = bb.get();
|
||||
this.optimalBitTiming = bb.get();
|
||||
this.compatibleHardware = bb.getShort();
|
||||
this.requiredRAM = bb.getShort();
|
||||
this.largestTrack = bb.getShort();
|
||||
}
|
||||
if (this.version == 3) {
|
||||
this.fluxBlock = bb.getShort();
|
||||
this.largestFluxTrack = bb.getShort();
|
||||
}
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
public int getDiskType() {
|
||||
return diskType;
|
||||
}
|
||||
public int getWriteProtected() {
|
||||
return writeProtected;
|
||||
}
|
||||
public int getSynchrinized() {
|
||||
return synchrinized;
|
||||
}
|
||||
public int getCleaned() {
|
||||
return cleaned;
|
||||
}
|
||||
public String getCreator() {
|
||||
return creator;
|
||||
}
|
||||
public int getDiskSides() {
|
||||
return diskSides;
|
||||
}
|
||||
public int getBootSectorFormat() {
|
||||
return bootSectorFormat;
|
||||
}
|
||||
public int getOptimalBitTiming() {
|
||||
return optimalBitTiming;
|
||||
}
|
||||
public int getCompatibleHardware() {
|
||||
return compatibleHardware;
|
||||
}
|
||||
public int getRequiredRAM() {
|
||||
return requiredRAM;
|
||||
}
|
||||
public int getLargestTrack() {
|
||||
return largestTrack;
|
||||
}
|
||||
public int getFluxBlock() {
|
||||
return fluxBlock;
|
||||
}
|
||||
public int getLargestFluxTrack() {
|
||||
return largestFluxTrack;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TmapChunk {
|
||||
private byte[] tmap;
|
||||
|
||||
/** Create a 5.25" TMAP entry; one for every quarter track. */
|
||||
public TmapChunk(byte[] data) {
|
||||
if (data.length != 4) {
|
||||
throw new RuntimeException("Unexpected TMAP chunk size of " + data.length);
|
||||
}
|
||||
tmap = data;
|
||||
}
|
||||
/** Create a 3.5" TMAP entry; just one for the track. */
|
||||
public TmapChunk(byte data) {
|
||||
tmap = new byte[4];
|
||||
tmap[0] = data;
|
||||
tmap[1] = (byte)255;
|
||||
tmap[2] = (byte)255;
|
||||
tmap[3] = (byte)255;
|
||||
}
|
||||
|
||||
public int getOffset00() {
|
||||
return Byte.toUnsignedInt(tmap[0]);
|
||||
}
|
||||
public int getOffset25() {
|
||||
return Byte.toUnsignedInt(tmap[1]);
|
||||
}
|
||||
public int getOffset50() {
|
||||
return Byte.toUnsignedInt(tmap[2]);
|
||||
}
|
||||
public int getOffset75() {
|
||||
return Byte.toUnsignedInt(tmap[3]);
|
||||
}
|
||||
}
|
||||
|
||||
public static class TrkInfo {
|
||||
private int startingBlock;
|
||||
private int blockCount;
|
||||
private int bitCount;
|
||||
|
||||
public TrkInfo(ByteBuffer bb) {
|
||||
this.startingBlock = bb.getShort();
|
||||
this.blockCount = bb.getShort();
|
||||
this.bitCount = bb.getInt();
|
||||
}
|
||||
public int getStartingBlock() {
|
||||
return startingBlock;
|
||||
}
|
||||
public int getBlockCount() {
|
||||
return blockCount;
|
||||
}
|
||||
public int getBitCount() {
|
||||
return bitCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -21,7 +21,6 @@ Filetype=Filetype
|
||||
Modified=Modified
|
||||
SizeInBytes=Size (bytes)
|
||||
DiskNameN={0} (Disk {1})
|
||||
Dos33=DOS 3.3
|
||||
NakedOS=NakedOS
|
||||
LockedQ=Locked?
|
||||
DirectoryCreationNotSupported=Unable to create directories.
|
||||
@@ -36,6 +35,7 @@ Disk.140kDosImages=140K DOS Ordered Images (*.do, *.dsk)
|
||||
Disk.140kNibbleImages=140K Nibbilized Images (*.nib)
|
||||
Disk.140kProdosImages=140K ProDOS Ordered Images (*.po)
|
||||
Disk.800kProdosImages=800K ProDOS Ordered Images (*.2mg, *.2img)
|
||||
Disk.WozImages=WOZ 2.x Images (*.woz)
|
||||
Disk.ApplePcImages=ApplePC Hard Disk Images (*.hdv)
|
||||
Disk.CompressedImages=All Compressed Images
|
||||
Disk.AllFiles=All Files
|
||||
|
||||
+49
-13
@@ -19,22 +19,15 @@
|
||||
*/
|
||||
package com.webcodepro.applecommander.storage;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import com.webcodepro.applecommander.storage.FormattedDisk.DiskUsage;
|
||||
import com.webcodepro.applecommander.storage.filters.*;
|
||||
import com.webcodepro.applecommander.testconfig.TestConfig;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.webcodepro.applecommander.storage.FormattedDisk.DiskUsage;
|
||||
import com.webcodepro.applecommander.storage.filters.ApplesoftFileFilter;
|
||||
import com.webcodepro.applecommander.storage.filters.BinaryFileFilter;
|
||||
import com.webcodepro.applecommander.storage.filters.DisassemblyFileFilter;
|
||||
import com.webcodepro.applecommander.storage.filters.GraphicsFileFilter;
|
||||
import com.webcodepro.applecommander.storage.filters.IntegerBasicFileFilter;
|
||||
import com.webcodepro.applecommander.storage.filters.TextFileFilter;
|
||||
import com.webcodepro.applecommander.testconfig.TestConfig;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Test Disk and FormattedDisk for read.
|
||||
@@ -43,6 +36,9 @@ import com.webcodepro.applecommander.testconfig.TestConfig;
|
||||
* @author Rob Greene
|
||||
*/
|
||||
public class DiskHelperTest {
|
||||
private static final String DOS33_FORMAT = "DOS 3.3";
|
||||
private static final String DOS32_FORMAT = "DOS 3.2";
|
||||
|
||||
private TestConfig config = TestConfig.getInstance();
|
||||
|
||||
@Test
|
||||
@@ -53,6 +49,7 @@ public class DiskHelperTest {
|
||||
assertIntegerFile(disks[0], "ANIMALS"); //$NON-NLS-1$
|
||||
assertTextFile(disks[0], "APPLE PROMS"); //$NON-NLS-1$
|
||||
assertBinaryFile(disks[0], "BOOT13"); //$NON-NLS-1$
|
||||
assertEquals(DOS33_FORMAT, disks[0].getFormat());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -147,7 +144,46 @@ public class DiskHelperTest {
|
||||
showDirectory(config.getDiskDir()
|
||||
+ "/D3151.dsk"); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testLoadDos33SystemMasterWoz1() throws IOException, DiskException {
|
||||
FormattedDisk[] disks = showDirectory(config.getDiskDir() +
|
||||
"/DOS 3.3 System Master.woz1");
|
||||
assertApplesoftFile(disks[0], "HELLO"); //$NON-NLS-1$
|
||||
assertIntegerFile(disks[0], "COPY"); //$NON-NLS-1$
|
||||
assertBinaryFile(disks[0], "BOOT13"); //$NON-NLS-1$
|
||||
assertEquals(DOS33_FORMAT, disks[0].getFormat());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadDos33SystemMasterWoz2() throws IOException, DiskException {
|
||||
FormattedDisk[] disks = showDirectory(config.getDiskDir() +
|
||||
"/DOS 3.3 System Master.woz2");
|
||||
assertApplesoftFile(disks[0], "HELLO"); //$NON-NLS-1$
|
||||
assertIntegerFile(disks[0], "COPY"); //$NON-NLS-1$
|
||||
assertBinaryFile(disks[0], "BOOT13"); //$NON-NLS-1$
|
||||
assertEquals(DOS33_FORMAT, disks[0].getFormat());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadDos32SystemMasterWoz() throws IOException, DiskException {
|
||||
FormattedDisk[] disks = showDirectory(config.getDiskDir() +
|
||||
"/DOS 3.2 System Master.woz");
|
||||
assertIntegerFile(disks[0], "HELLO"); //$NON-NLS-1$
|
||||
assertBinaryFile(disks[0], "UPDATE 3.2"); //$NON-NLS-1$
|
||||
assertEquals(DOS32_FORMAT, disks[0].getFormat());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadDos32SystemMasterNib() throws IOException, DiskException {
|
||||
FormattedDisk[] disks = showDirectory(config.getDiskDir() +
|
||||
"/original321sysmaspls.nib");
|
||||
assertApplesoftFile(disks[0], "HELLO");
|
||||
assertBinaryFile(disks[0], "UPDATE 3.2.1");
|
||||
assertTextFile(disks[0], "APPLE PROMS");
|
||||
assertEquals(DOS32_FORMAT, disks[0].getFormat());
|
||||
}
|
||||
|
||||
protected FormattedDisk[] showDirectory(String imageName) throws IOException, DiskException {
|
||||
Disk disk = new Disk(imageName);
|
||||
FormattedDisk[] formattedDisks = disk.getFormattedDisks();
|
||||
|
||||
@@ -19,12 +19,6 @@
|
||||
*/
|
||||
package com.webcodepro.applecommander.util;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.webcodepro.applecommander.storage.Disk;
|
||||
import com.webcodepro.applecommander.storage.DiskFullException;
|
||||
import com.webcodepro.applecommander.storage.FileEntry;
|
||||
@@ -36,6 +30,9 @@ import com.webcodepro.applecommander.storage.physical.ByteArrayImageLayout;
|
||||
import com.webcodepro.applecommander.storage.physical.DosOrder;
|
||||
import com.webcodepro.applecommander.storage.physical.NibbleOrder;
|
||||
import com.webcodepro.applecommander.storage.physical.ProdosOrder;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Test AppleUtil.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
@@ -0,0 +1 @@
|
||||
������������������������������������������������摘��������������摘�狠踔换��祷蹼惦��莼踔���蛀葺趸莴�诀诸��觖揸趸莴�恢蹼��臌��蝶�猾腚��趸值祷齄第肢铑邴�髦莼�蹼踔�踔�趸氲肢��蓦蒗�换莴浑贿��之���碉��哧��巨蓦揠拗鲚貂貊醌�祸伙唼��坏只腙陬���膪�罪咧鲚嵇�竞脒唠镲��鲲邛碇警�鬓谵夯���篆范宏鬓邡谆���篆粉钻鬓邹��哧鬓钻��蹶鬓�氙愤猾��犄�疥�������������������鲻�������������������������睫����������������������������摘�������蕺�������摘����莼郛觖懋铛痪���岛喵痪究胼椒�最�决吨划寒淡�葵浸警锘�湖捉葵懋筋�趾旷缔�诤醵�黼到谯缔�恩龆�份击眭�埤鳆汾巨锦��亏蘅谯�钪鲭磔胼��董�����鬓但�靠淼��觑埘铽�旷锏晋霏��犄铽蹶�鳙镛�龇泛�划�矾觑��虢葺麝凯�宏积逢贳�嵇钲�禁凤祷�肢�埝氆铽詈劢�汉�觋谨�铽作铽鬃��诏唔档谨铽铸氟��贽�刀铽呓�吊贩�貂鳟瞢蒋鲲汁鼢�忽�符椒镏�铽菌�贩恩志霁觐��蹲圯腚锆呓郢镗醌�甑镞�锦��昃������������������������������摘�������������摘��麟�葳走�作胼麝畀��蹙殿揪睇��碹钓眵�疃莸膂眄���葳志魂��醴薜觜挚醵��谵揄�悍巾助钻哆圯��腚牖葺谮窘俄薜磉喏��撖伙众捱�揩��葑戤褐��瞢�揶蕲�瞰����蕻���韩����胡鄂恨��鬃�庄谳���陉挚扣��骄奉糊警贻捣�烤囤�淀钰麟�铍�铥����陔�君�只��揄踝哧俄揄氕��隹腩桔奉咧鞫咿��咦��陔�递����贻蒗鬓�递腼�磙锦��斤谮牒揠�弹�锆氙町�降壕���净揎局�蹀但��睇帝��诮眦蹀詈葑黠锥疆谳��恨蕺����������������������������摘������蕺�������摘�捱咻诙侩氆腙咿�画��颠咻经卷额哧钶葺惦�啐刖醉精讽�坏汾�淀懋趸蝴葺揪鼋��汾�箕环服叻纷��醐递�敕诛�呖经哌捱赀�胼葺叨���精��蝴�鬏蘧�铪��锟陔貅函眍�锝��镲觜�邡�符��眄谮谥诛�口钸井符��趸宗�郗秭蒋��寒魁呖�慨�第�揪揶瞰�饿�觜羁�昊��骱�钭�碲谮砗哼���鲒谮�������钸��局沮�唢钭��谮鲚氕稻咀���貊诋悼孔助蒋�貂���搛诋悼哭函锥口�骱����株磉泛慨������觖莴�降悼�������������������������������摘��������������摘�弹�纷揞龊蓓�邝帝���埚眄邝�蓣精虢汁谆��鲫蹙�黝葺诰但���斗葑帝睫呲诜捉诘攵字�唤��晋尔胱款晋�薅斛酆霎牾址�竭薤����杲樊�貂����鲎符镲�憝��瞢�胶�靠���韩�函邹钝贳莺晋钲�侄��埘�缀鄱���氆涵�织但���帆��葺��炕弘氆���懂诳动��觋�慝涵�阶东��追�矾犏锆鳋骄莓摞磔��诋��碉镗鳙掭��涵��翻��泛黝牖淀葑锘甓铪�蓥捱�郛邴吆忽霁莸瞰警郢�唤���汉豢劬黪劭�戬�昕霁�髦碜呔鼢��蕺����������������������������摘��������������摘���钝贻��岛碉蹲����蛔���鲱睑�鹤��谵���但揄瑚黑攉���蛔�薜猾�铤�莸肢�撅樊�囤烬�����鳋但霁�脎腩�矾��攵蒇郢�弹龇�����氆�种种肢������������������������������������������������������������������������������������������������������������������������������������������������������������摘�������������摘�蒉�莪�唠蕉�筋樊锖��鬃�撅糊蓐�蔺���������蛀逗贽�醉睚醴咻�町划�岛�攉�鲚莼趸酆���捱憾�碟���鄯�莪�递恨�诰陧化化画�拷聚但�����������分�慝������������������������������������������������翻桩锂���������������������符�分�������������������������分�铽���������������蕺����������������������������摘�������������摘�揞���黼腩�莸肿圳到猾�洱东�篆�刀�����踅攴颠����喵哳藓�肢档胶肢觉惠犏�谥��鲻萁将殿�鳟黪铛虢蹩弋眄瞽旰���鲰���主扶胼��莘谨�锃莼���撅�陬�莴哼�铑�积�鞫攉铽臌�郗秕埙锟����鲚氟拗葳憾�枯做服镪呔逗��莴蓐蓊膈搋具醯糠��趾�诰纵��貂�挚鞯揄垌�侩戬黜�淼犄铒呖禁碹肪鄯����镞埤韩函但�牿铸扣追�镜�铛纷鞯镏�摭�纸贩靠莼�宽�镱朕碉君醉薜揩�悍貂��缔哆帆刖秭邺��胶痕�铒��忿����������������������������摘�������������摘���龊觏�锦睇环孵州猴愤�坏份攉氘甑畀���烤��碟�淡掭��胼�董贻髭坊�郫��肢但究��谳�铽�黼���贽��降�刀淡�据圩揎�镙机鲭蹩�呲贻蓊��慝净镲铥�郛亏牒�谥沮档�瞰慝篆觚警�窥扣到碉唼搡鲫画蛾欢掇莴慝�祷乎镗诤�旰莜�悍节氆疃翻亢扶�鬃�砭����仅��蹼蕉碇隹岛��董��忒叠蔸盹藓镯弋埙葸淀�黼��瞢龆���胱慝貊洱痕�椒抖将方圹局伙�疃亏髯份��邴�抵葺膈州柞��蹼抵诘�但�捣镲悼诜髹决�����铸�奉蕺����������������������������摘������蕺�������摘���烬���昊蹴斫具骺嵇旰醌町胼��髭�嵇脎�鲒鳊侄州��攉�莸蒿井铐�缔铽�惦�斫�悍机眍帆锆腚主�篆亢���亢��额�菥醍劬��换��鲭铛醌蘅岛�撖炕戤斫碉莪�搋揶�讷�乎诜膻昕����搡况�氕��膂蕲肢�揶��瑚�镱���镛�葵葑醴�值主掰�麝���谮邡���邗钷�究瞩蛀蒗圳�铑�虢�貊碜��蒉�蝴纵氆��醐郢�邗��刖�纷侩讷�浸诜鲱�圯诨藁亢痉吨鲛���鼾����鳟净�蓰踮捋���株埏觖���鲚�锞戢���种��蕺����������������������������摘�������������摘���精�詈�戢昕�罘哆锆疆��乎瞰镯觏砘凯镱腩�助�铟搋攵霏鳙���撰瞢节���莘�份����降之�遁蹼町�攮淀锆咻泛�鳅��坏脍膑�作涵��服鼢��啕�铒呓龊羁邝�邗锞氆鲛动�街峻霁诘�董���钲匡谶肿�氆莴�貊竭粉旷��攵�揩慷贩房�����划��攵睫��莪拗��蹲莜�莘����呲谵纷��镞瞢额�Line truncated
|
||||
Reference in new issue
Block a user