From 7f728e8e53ea76244ee51f9c6acd64d8f28174d0 Mon Sep 17 00:00:00 2001 From: Denis Molony Date: Fri, 8 Jun 2018 22:14:19 +1000 Subject: [PATCH] added support for .woz files --- .../bytezone/diskbrowser/disk/AppleDisk.java | 15 ++ .../diskbrowser/disk/DiskFactory.java | 10 + .../bytezone/diskbrowser/disk/NibDisk.java | 2 +- .../bytezone/diskbrowser/disk/Nibblizer.java | 100 ++++++--- .../bytezone/diskbrowser/disk/WozDisk.java | 192 ++++++++++++++++++ .../diskbrowser/utilities/Utility.java | 2 +- 6 files changed, 292 insertions(+), 29 deletions(-) create mode 100644 src/com/bytezone/diskbrowser/disk/WozDisk.java diff --git a/src/com/bytezone/diskbrowser/disk/AppleDisk.java b/src/com/bytezone/diskbrowser/disk/AppleDisk.java index 065388b..ebb9f4d 100755 --- a/src/com/bytezone/diskbrowser/disk/AppleDisk.java +++ b/src/com/bytezone/diskbrowser/disk/AppleDisk.java @@ -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]; diff --git a/src/com/bytezone/diskbrowser/disk/DiskFactory.java b/src/com/bytezone/diskbrowser/disk/DiskFactory.java index 6b08c3a..706878b 100755 --- a/src/com/bytezone/diskbrowser/disk/DiskFactory.java +++ b/src/com/bytezone/diskbrowser/disk/DiskFactory.java @@ -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 diff --git a/src/com/bytezone/diskbrowser/disk/NibDisk.java b/src/com/bytezone/diskbrowser/disk/NibDisk.java index 4219b02..683e47b 100644 --- a/src/com/bytezone/diskbrowser/disk/NibDisk.java +++ b/src/com/bytezone/diskbrowser/disk/NibDisk.java @@ -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) { diff --git a/src/com/bytezone/diskbrowser/disk/Nibblizer.java b/src/com/bytezone/diskbrowser/disk/Nibblizer.java index 98a074b..407b891 100644 --- a/src/com/bytezone/diskbrowser/disk/Nibblizer.java +++ b/src/com/bytezone/diskbrowser/disk/Nibblizer.java @@ -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 { diff --git a/src/com/bytezone/diskbrowser/disk/WozDisk.java b/src/com/bytezone/diskbrowser/disk/WozDisk.java new file mode 100644 index 0000000..8996f87 --- /dev/null +++ b/src/com/bytezone/diskbrowser/disk/WozDisk.java @@ -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; + } +} diff --git a/src/com/bytezone/diskbrowser/utilities/Utility.java b/src/com/bytezone/diskbrowser/utilities/Utility.java index 68a49af..6cc0efb 100644 --- a/src/com/bytezone/diskbrowser/utilities/Utility.java +++ b/src/com/bytezone/diskbrowser/utilities/Utility.java @@ -8,7 +8,7 @@ import java.util.List; public class Utility { public static final List 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 ()