mirror of
https://github.com/dmolony/DiskBrowser.git
synced 2024-11-29 11:49:29 +00:00
added support for .woz files
This commit is contained in:
parent
9d57fcf366
commit
7f728e8e53
@ -226,6 +226,21 @@ public class AppleDisk implements Disk
|
||||
diskBuffer = disk.buffer;
|
||||
}
|
||||
|
||||
public AppleDisk (WozDisk disk) // not used yet
|
||||
{
|
||||
tracks = 35;
|
||||
trackSize = 4096;
|
||||
file = disk.file;
|
||||
diskBuffer = disk.diskBuffer;
|
||||
|
||||
sectorSize = 256;
|
||||
sectors = 16;
|
||||
blocks = tracks * sectors;
|
||||
hasData = new boolean[blocks];
|
||||
|
||||
checkSectorsForData ();
|
||||
}
|
||||
|
||||
private byte[] getPrefix (File path)
|
||||
{
|
||||
byte[] buffer = new byte[64];
|
||||
|
@ -247,6 +247,16 @@ public class DiskFactory
|
||||
return null;
|
||||
}
|
||||
|
||||
if (suffix.equals ("woz"))
|
||||
{
|
||||
if (debug)
|
||||
System.out.println (" ** woz **");
|
||||
WozDisk wozDisk = new WozDisk (file);
|
||||
AppleDisk appleDisk16 = new AppleDisk (wozDisk);
|
||||
disk = checkDos (appleDisk16);
|
||||
return disk;
|
||||
}
|
||||
|
||||
long length = file.length ();
|
||||
|
||||
if (length == 116480) // 13 sector disk
|
||||
|
@ -18,7 +18,7 @@ public class NibDisk
|
||||
// .nib files are 232,960 bytes
|
||||
// 6,656 bytes x 35 tracks (0x1A00)
|
||||
|
||||
// add 'nib' to TreeBuilder to allow nib files to be selected
|
||||
// add 'nib' to Utility.suffixes to allow nib files to be selected
|
||||
|
||||
public NibDisk (File file)
|
||||
{
|
||||
|
@ -93,7 +93,7 @@ public class Nibblizer
|
||||
{
|
||||
for (int i = 0; i < writeTranslateTable.length; i++)
|
||||
{
|
||||
int j = (writeTranslateTable[i] & 0xFF) - 150; // skip first 150 blanks
|
||||
int j = (writeTranslateTable[i] & 0xFF) - 0x96; // skip first 150 blanks
|
||||
readTranslateTable[j] = (byte) (i + 1); // offset by 1 to avoid zero
|
||||
}
|
||||
}
|
||||
@ -125,39 +125,72 @@ public class Nibblizer
|
||||
public boolean processTrack (int trackNo, byte[] buffer, byte[] diskBuffer)
|
||||
{
|
||||
int ptr = 0;
|
||||
|
||||
while (buffer[ptr] == (byte) 0xEB)
|
||||
{
|
||||
System.out.printf ("%s overrun 0xEB offset %d in track %02X%n", file.getName (),
|
||||
ptr, trackNo);
|
||||
++ptr;
|
||||
}
|
||||
|
||||
ptr += skipBytes (buffer, ptr, (byte) 0xFF); // gap1
|
||||
int totalSectors = 0;
|
||||
boolean[] sectorsFound = new boolean[16];
|
||||
|
||||
while (ptr < buffer.length)
|
||||
{
|
||||
ptr = findBytes (buffer, ptr, addressPrologue);
|
||||
if (ptr < 0)
|
||||
{
|
||||
System.out.printf ("Track: %02X - Address prologue not found%n", trackNo);
|
||||
// System.out.println (HexFormatter.format (buffer));
|
||||
return false;
|
||||
}
|
||||
AddressField addressField = getAddressField (buffer, ptr);
|
||||
if (!addressField.isValid ())
|
||||
{
|
||||
System.out.printf ("Track: %02X - Invalid address field%n", trackNo);
|
||||
return false;
|
||||
}
|
||||
if (addressField.track != trackNo)
|
||||
{
|
||||
System.out.printf ("Track: %02X - Wrong track found (%02X)%n", trackNo,
|
||||
addressField.track);
|
||||
return false;
|
||||
}
|
||||
if (sectorsFound[addressField.sector])
|
||||
{
|
||||
System.out.printf ("Track: %02X - Sector already processes (%02X)%n", trackNo,
|
||||
addressField.sector);
|
||||
return false;
|
||||
}
|
||||
sectorsFound[addressField.sector] = true;
|
||||
|
||||
assert addressField.track == trackNo;
|
||||
|
||||
ptr += addressField.size ();
|
||||
ptr += skipBytes (buffer, ptr, (byte) 0xFF); // gap2
|
||||
ptr = findBytes (buffer, ptr, dataPrologue);
|
||||
if (ptr < 0)
|
||||
{
|
||||
System.out.printf ("Track: %02X - Data prologue not found%n", trackNo);
|
||||
return false;
|
||||
}
|
||||
|
||||
DataField dataField = getDataField (buffer, ptr);
|
||||
if (!dataField.isValid ())
|
||||
{
|
||||
System.out.printf ("Track: %02X - Invalid data field%n", trackNo);
|
||||
return false;
|
||||
}
|
||||
|
||||
int offset = addressField.track * TRACK_SIZE
|
||||
+ interleave[DOS][addressField.sector] * BLOCK_SIZE;
|
||||
|
||||
System.arraycopy (dataField.dataBuffer, 0, diskBuffer, offset, BLOCK_SIZE);
|
||||
|
||||
if (++totalSectors == 16)
|
||||
break;
|
||||
|
||||
ptr += dataField.size ();
|
||||
ptr += skipBytes (buffer, ptr, (byte) 0xFF); // gap3
|
||||
}
|
||||
if (totalSectors != 16)
|
||||
{
|
||||
System.out.printf ("Track: %02X - Sectors found: %02X%n", trackNo, totalSectors);
|
||||
return false;
|
||||
}
|
||||
|
||||
// System.out.printf ("Track: %02X - OK%n", trackNo);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -260,13 +293,13 @@ public class Nibblizer
|
||||
return bits == 1 ? 2 : bits == 2 ? 1 : bits;
|
||||
}
|
||||
|
||||
private int skipBytes (byte[] buffer, int offset, byte skipValue)
|
||||
{
|
||||
int count = 0;
|
||||
while (offset < buffer.length && buffer[offset++] == skipValue)
|
||||
++count;
|
||||
return count;
|
||||
}
|
||||
// private int skipBytes (byte[] buffer, int offset, byte skipValue)
|
||||
// {
|
||||
// int count = 0;
|
||||
// while (offset < buffer.length && buffer[offset++] == skipValue)
|
||||
// ++count;
|
||||
// return count;
|
||||
// }
|
||||
|
||||
private String listBytes (byte[] buffer, int offset, int length)
|
||||
{
|
||||
@ -327,6 +360,12 @@ public class Nibblizer
|
||||
assert length > 0;
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString ()
|
||||
{
|
||||
return String.format ("[Offset: %04X, Length: %04X]", offset, length);
|
||||
}
|
||||
}
|
||||
|
||||
class AddressField extends Field
|
||||
@ -337,8 +376,8 @@ public class Nibblizer
|
||||
{
|
||||
super (buffer, offset);
|
||||
|
||||
if (matchBytes (buffer, offset, addressPrologue)
|
||||
&& matchBytes (buffer, offset + 11, epilogue))
|
||||
if (matchBytes (buffer, offset, addressPrologue))
|
||||
// && matchBytes (buffer, offset + 11, epilogue))
|
||||
{
|
||||
volume = decode4and4 (buffer, offset + 3);
|
||||
track = decode4and4 (buffer, offset + 5);
|
||||
@ -351,6 +390,13 @@ public class Nibblizer
|
||||
|
||||
length = 14;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString ()
|
||||
{
|
||||
return String.format ("[volume: %02X, track: %02X, sector: %02X, checksum: %02X]",
|
||||
volume, track, sector, checksum);
|
||||
}
|
||||
}
|
||||
|
||||
class DataField extends Field
|
||||
@ -365,12 +411,12 @@ public class Nibblizer
|
||||
{
|
||||
valid = true;
|
||||
dataBuffer = decode6and2 (buffer, offset + 3);
|
||||
if (!matchBytes (buffer, offset + 3 + BUFFER_WITH_CHECKSUM_SIZE, epilogue))
|
||||
{
|
||||
System.out.print (" bad data epilogue: ");
|
||||
System.out
|
||||
.println (listBytes (buffer, offset + 3 + BUFFER_WITH_CHECKSUM_SIZE, 3));
|
||||
}
|
||||
// if (!matchBytes (buffer, offset + 3 + BUFFER_WITH_CHECKSUM_SIZE, epilogue))
|
||||
// {
|
||||
// System.out.print (" bad data epilogue: ");
|
||||
// System.out
|
||||
// .println (listBytes (buffer, offset + 3 + BUFFER_WITH_CHECKSUM_SIZE, 3));
|
||||
// }
|
||||
}
|
||||
else
|
||||
{
|
||||
|
192
src/com/bytezone/diskbrowser/disk/WozDisk.java
Normal file
192
src/com/bytezone/diskbrowser/disk/WozDisk.java
Normal file
@ -0,0 +1,192 @@
|
||||
package com.bytezone.diskbrowser.disk;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class WozDisk
|
||||
{
|
||||
private static final int TRK_SIZE = 0x1A00;
|
||||
private static final int DATA_SIZE = TRK_SIZE - 10;
|
||||
private static byte[] header =
|
||||
{ 0x57, 0x4F, 0x5A, 0x31, (byte) 0xFF, 0x0a, 0x0D, 0x0A };
|
||||
private static final String SPACES = " ";
|
||||
private final boolean debug = false;
|
||||
|
||||
final File file;
|
||||
private final Nibblizer nibbler;
|
||||
final byte[] diskBuffer = new byte[4096 * 35];
|
||||
|
||||
public WozDisk (File f)
|
||||
{
|
||||
this.file = f;
|
||||
nibbler = new Nibblizer (f);
|
||||
|
||||
byte[] id = new byte[8];
|
||||
byte[] checksum = new byte[4];
|
||||
byte[] trackBuffer = new byte[TRK_SIZE];
|
||||
byte[] infoBuffer = new byte[60];
|
||||
byte[] tmapBuffer = new byte[160];
|
||||
|
||||
try
|
||||
{
|
||||
BufferedInputStream in = new BufferedInputStream (new FileInputStream (file));
|
||||
in.read (id);
|
||||
assert matches (id, header);
|
||||
in.read (checksum);
|
||||
|
||||
read: while (in.available () > 8)
|
||||
{
|
||||
String chunkId = readString (in, 4);
|
||||
int chunkSize = readInt (in, 4);
|
||||
|
||||
if ("INFO".equals (chunkId))
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
in.read (infoBuffer);
|
||||
System.out.printf ("Version ........... %02X%n", infoBuffer[0]);
|
||||
System.out.printf ("Disk type ......... %02X%n", infoBuffer[1]);
|
||||
System.out.printf ("Write protected ... %02X%n", infoBuffer[2]);
|
||||
System.out.printf ("Synchronised ...... %02X%n", infoBuffer[3]);
|
||||
System.out.printf ("Cleaned ........... %02X%n", infoBuffer[4]);
|
||||
System.out.printf ("Creator ........... %s%n%n",
|
||||
new String (infoBuffer, 5, 32));
|
||||
}
|
||||
else
|
||||
in.skip (infoBuffer.length);
|
||||
}
|
||||
else if ("TMAP".equals (chunkId))
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
in.read (tmapBuffer);
|
||||
int ptr = 0;
|
||||
for (int track = 0; track < 40; track++)
|
||||
{
|
||||
for (int qtr = 0; qtr < 4; qtr++)
|
||||
System.out.printf ("%02X ", tmapBuffer[ptr++]);
|
||||
System.out.println ();
|
||||
}
|
||||
System.out.println ();
|
||||
}
|
||||
else
|
||||
in.skip (tmapBuffer.length);
|
||||
}
|
||||
else if ("TRKS".equals (chunkId))
|
||||
{
|
||||
int tracks = chunkSize / TRK_SIZE;
|
||||
for (int track = 0; track < tracks; track++)
|
||||
{
|
||||
int bytesRead = in.read (trackBuffer);
|
||||
assert bytesRead == TRK_SIZE;
|
||||
int bytesUsed = readInt (trackBuffer, DATA_SIZE, 2);
|
||||
int bitCount = readInt (trackBuffer, DATA_SIZE + 2, 2);
|
||||
|
||||
byte[] trackData = new byte[bytesUsed];
|
||||
readTrack (trackBuffer, trackData, bytesUsed);
|
||||
if (!nibbler.processTrack (track, trackData, diskBuffer))
|
||||
{
|
||||
System.out.println ("Nibblizer failure");
|
||||
// System.out.println (HexFormatter.format (trackBuffer, 0, trackBuffer.length,
|
||||
// TRK_SIZE * track + 256));
|
||||
// System.out.println ();
|
||||
break read;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ("META".equals (chunkId))
|
||||
{
|
||||
System.out.printf ("[%s] %08X%n", chunkId, chunkSize);
|
||||
skip (in, chunkSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
System.out.printf ("Unknown %08X%n", chunkSize);
|
||||
skip (in, chunkSize);
|
||||
}
|
||||
}
|
||||
in.close ();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace ();
|
||||
System.exit (1);
|
||||
}
|
||||
}
|
||||
|
||||
private void skip (BufferedInputStream file, int size) throws IOException
|
||||
{
|
||||
while ((size -= file.skip (size)) > 0)
|
||||
;
|
||||
}
|
||||
|
||||
private String readString (BufferedInputStream file, int size) throws IOException
|
||||
{
|
||||
byte[] bytes = new byte[size];
|
||||
file.read (bytes);
|
||||
// for (byte b : bytes)
|
||||
// System.out.printf ("%02X ", b);
|
||||
// System.out.println ();
|
||||
return new String (bytes);
|
||||
}
|
||||
|
||||
private int readInt (byte[] buffer, int offset, int length)
|
||||
{
|
||||
int shift = 0;
|
||||
int value = 0;
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
value |= (buffer[offset + i] & 0xFF) << shift;
|
||||
shift += 8;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private int readInt (BufferedInputStream file, int size) throws IOException
|
||||
{
|
||||
byte[] buffer = new byte[size];
|
||||
file.read (buffer);
|
||||
return readInt (buffer, 0, size);
|
||||
}
|
||||
|
||||
private boolean matches (byte[] b1, byte[] b2)
|
||||
{
|
||||
for (int i = 0; i < b1.length; i++)
|
||||
if (b1[i] != b2[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void readTrack (byte[] buffer, byte[] trackData, int bytesUsed)
|
||||
{
|
||||
// int consecutiveZeros = 0;
|
||||
int value = 0;
|
||||
int ptr = 0;
|
||||
|
||||
for (int i = 0; i < bytesUsed; i++)
|
||||
{
|
||||
int b = buffer[i] & 0xFF;
|
||||
for (int mask = 0x80; mask > 0; mask >>>= 1)
|
||||
{
|
||||
int bit = (b & mask) == 0 ? 0 : 1;
|
||||
// if (bit == 1)
|
||||
// consecutiveZeros = 0;
|
||||
// else if (++consecutiveZeros > 3)
|
||||
// bit = (random.nextInt () & 0x01);
|
||||
|
||||
value <<= 1;
|
||||
value |= bit;
|
||||
|
||||
if ((value & 0x80) != 0) // is hi-bit set?
|
||||
{
|
||||
trackData[ptr++] = (byte) (value & 0xFF);
|
||||
value = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert value == 0;
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ import java.util.List;
|
||||
public class Utility
|
||||
{
|
||||
public static final List<String> suffixes =
|
||||
Arrays.asList ("po", "dsk", "do", "hdv", "2mg", "v2d", "d13", "sdk");
|
||||
Arrays.asList ("po", "dsk", "do", "hdv", "2mg", "v2d", "d13", "sdk", "woz");
|
||||
|
||||
// not used - it doesn't work with Oracle's JDK
|
||||
// private static boolean hasRetinaDisplay ()
|
||||
|
Loading…
Reference in New Issue
Block a user