2018-08-17 01:20:00 +00:00
|
|
|
package com.bytezone.diskbrowser.nib;
|
2018-06-08 12:14:19 +00:00
|
|
|
|
|
|
|
import java.io.BufferedInputStream;
|
|
|
|
import java.io.File;
|
|
|
|
import java.io.FileInputStream;
|
|
|
|
import java.io.IOException;
|
2018-08-18 03:24:22 +00:00
|
|
|
import java.util.ArrayList;
|
2018-08-13 09:17:05 +00:00
|
|
|
import java.util.List;
|
2018-06-08 12:14:19 +00:00
|
|
|
|
2019-03-05 01:50:44 +00:00
|
|
|
import com.bytezone.diskbrowser.utilities.HexFormatter;
|
2018-06-10 03:12:32 +00:00
|
|
|
import com.bytezone.diskbrowser.utilities.Utility;
|
|
|
|
|
2018-08-17 01:20:00 +00:00
|
|
|
public class WozFile
|
2018-06-08 12:14:19 +00:00
|
|
|
{
|
2019-01-26 20:06:13 +00:00
|
|
|
private static final byte[] WOZ1_FILE_HEADER =
|
|
|
|
{ 0x57, 0x4F, 0x5A, 0x31, (byte) 0xFF, 0x0A, 0x0D, 0x0A };
|
|
|
|
private static final byte[] WOZ2_FILE_HEADER =
|
|
|
|
{ 0x57, 0x4F, 0x5A, 0x32, (byte) 0xFF, 0x0A, 0x0D, 0x0A };
|
|
|
|
|
2018-06-08 12:14:19 +00:00
|
|
|
private static final int TRK_SIZE = 0x1A00;
|
2018-06-09 01:11:02 +00:00
|
|
|
private static final int INFO_SIZE = 0x3C;
|
|
|
|
private static final int TMAP_SIZE = 0xA0;
|
2018-06-08 12:14:19 +00:00
|
|
|
private static final int DATA_SIZE = TRK_SIZE - 10;
|
2018-08-11 00:07:50 +00:00
|
|
|
|
2018-06-08 12:14:19 +00:00
|
|
|
private final boolean debug = false;
|
2018-08-07 08:40:06 +00:00
|
|
|
private int diskType; // 5.25 or 3.5
|
2019-03-21 23:01:08 +00:00
|
|
|
private int wozVersion;
|
|
|
|
private int bootSectorFormat;
|
2018-06-08 12:14:19 +00:00
|
|
|
|
2018-08-17 01:20:00 +00:00
|
|
|
public final File file;
|
2018-08-11 00:07:50 +00:00
|
|
|
byte[] diskBuffer;
|
|
|
|
|
|
|
|
private final MC3470 mc3470 = new MC3470 ();
|
2018-08-18 03:24:22 +00:00
|
|
|
private final List<NibbleTrack> nibbleTracks = new ArrayList<> (40);
|
2018-06-08 12:14:19 +00:00
|
|
|
|
2018-06-09 01:11:02 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
// constructor
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
2018-08-18 03:24:22 +00:00
|
|
|
public WozFile (File file) throws DiskNibbleException
|
2018-06-08 12:14:19 +00:00
|
|
|
{
|
2018-06-10 20:45:11 +00:00
|
|
|
this.file = file;
|
|
|
|
byte[] buffer = readFile ();
|
2018-08-18 03:24:22 +00:00
|
|
|
boolean valid = false;
|
2018-06-08 12:14:19 +00:00
|
|
|
|
2019-03-21 23:01:08 +00:00
|
|
|
if (matches (WOZ1_FILE_HEADER, buffer))
|
|
|
|
wozVersion = 1;
|
|
|
|
else if (matches (WOZ2_FILE_HEADER, buffer))
|
|
|
|
wozVersion = 2;
|
|
|
|
else
|
2019-03-05 01:50:44 +00:00
|
|
|
{
|
|
|
|
System.out.println (HexFormatter.format (buffer, 0, 20));
|
2018-08-18 03:24:22 +00:00
|
|
|
throw new DiskNibbleException ("Header error");
|
2019-03-05 01:50:44 +00:00
|
|
|
}
|
2018-06-10 03:12:32 +00:00
|
|
|
|
2018-08-13 09:17:05 +00:00
|
|
|
int checksum1 = readInt (buffer, 8, 4);
|
|
|
|
int checksum2 = Utility.crc32 (buffer, 12, buffer.length - 12);
|
|
|
|
if (checksum1 != checksum2)
|
2018-06-10 03:12:32 +00:00
|
|
|
{
|
2018-08-13 09:17:05 +00:00
|
|
|
System.out.printf ("Stored checksum : %08X%n", checksum1);
|
|
|
|
System.out.printf ("Calculated checksum : %08X%n", checksum2);
|
2018-08-18 03:24:22 +00:00
|
|
|
throw new DiskNibbleException ("Checksum error");
|
2018-06-10 03:12:32 +00:00
|
|
|
}
|
2018-06-08 12:14:19 +00:00
|
|
|
|
2018-06-10 03:12:32 +00:00
|
|
|
int ptr = 12;
|
|
|
|
read: while (ptr < buffer.length)
|
|
|
|
{
|
2018-06-10 08:41:39 +00:00
|
|
|
String chunkId = new String (buffer, ptr, 4);
|
2018-06-10 03:12:32 +00:00
|
|
|
ptr += 4;
|
|
|
|
int chunkSize = readInt (buffer, ptr, 4);
|
|
|
|
ptr += 4;
|
|
|
|
|
2019-03-21 23:01:08 +00:00
|
|
|
if (debug)
|
|
|
|
{
|
|
|
|
System.out.printf ("Offset : %06X%n", ptr - 8);
|
|
|
|
System.out.printf ("Chunk ID : %s%n", chunkId);
|
|
|
|
System.out.printf ("Chunk size: %,d%n", chunkSize);
|
|
|
|
}
|
|
|
|
|
2018-06-10 03:12:32 +00:00
|
|
|
if ("INFO".equals (chunkId))
|
|
|
|
{
|
|
|
|
if (debug)
|
2018-06-08 12:14:19 +00:00
|
|
|
{
|
2019-03-21 23:01:08 +00:00
|
|
|
System.out.println ();
|
2018-06-10 03:12:32 +00:00
|
|
|
System.out.printf ("Version ........... %02X%n", buffer[ptr]);
|
|
|
|
System.out.printf ("Disk type ......... %02X%n", buffer[ptr + 1]);
|
|
|
|
System.out.printf ("Write protected ... %02X%n", buffer[ptr + 2]);
|
|
|
|
System.out.printf ("Synchronised ...... %02X%n", buffer[ptr + 3]);
|
|
|
|
System.out.printf ("Cleaned ........... %02X%n", buffer[ptr + 4]);
|
2019-03-21 23:01:08 +00:00
|
|
|
System.out.printf ("Creator ........... %s%n",
|
|
|
|
new String (buffer, ptr + 5, 32).trim ());
|
|
|
|
|
|
|
|
if (wozVersion > 1)
|
|
|
|
{
|
|
|
|
System.out.printf ("Disk sides ........ %02X%n", buffer[ptr + 37]);
|
|
|
|
System.out.printf ("Boot format ....... %02X%n", buffer[ptr + 38]);
|
|
|
|
System.out.printf ("Optimal timing .... %02X%n", buffer[ptr + 39]);
|
|
|
|
System.out.printf ("Compatible flags .. %04X%n",
|
|
|
|
readInt (buffer, ptr + 40, 2));
|
|
|
|
System.out.printf ("Minimum RAM ....... %04X%n",
|
|
|
|
readInt (buffer, ptr + 42, 2));
|
|
|
|
System.out.printf ("Largest track ..... %04X%n",
|
|
|
|
readInt (buffer, ptr + 44, 2));
|
|
|
|
}
|
|
|
|
System.out.println ();
|
2018-06-08 12:14:19 +00:00
|
|
|
}
|
2019-03-21 23:01:08 +00:00
|
|
|
|
2018-08-07 08:40:06 +00:00
|
|
|
diskType = buffer[ptr + 1] & 0xFF;
|
2019-03-21 23:01:08 +00:00
|
|
|
if (wozVersion > 1)
|
|
|
|
bootSectorFormat = buffer[ptr + 38] & 0xFF;
|
|
|
|
|
2018-06-10 03:12:32 +00:00
|
|
|
ptr += INFO_SIZE;
|
|
|
|
}
|
|
|
|
else if ("TMAP".equals (chunkId))
|
|
|
|
{
|
|
|
|
if (debug)
|
2018-06-08 12:14:19 +00:00
|
|
|
{
|
2018-06-10 03:12:32 +00:00
|
|
|
for (int track = 0; track < 40; track++)
|
2018-06-08 12:14:19 +00:00
|
|
|
{
|
2018-06-10 03:12:32 +00:00
|
|
|
for (int qtr = 0; qtr < 4; qtr++)
|
|
|
|
System.out.printf ("%02X ", buffer[ptr++]);
|
2018-06-08 12:14:19 +00:00
|
|
|
System.out.println ();
|
|
|
|
}
|
2018-06-10 03:12:32 +00:00
|
|
|
System.out.println ();
|
2018-06-08 12:14:19 +00:00
|
|
|
}
|
2018-06-10 03:12:32 +00:00
|
|
|
else
|
|
|
|
ptr += TMAP_SIZE;
|
|
|
|
}
|
|
|
|
else if ("TRKS".equals (chunkId))
|
|
|
|
{
|
2018-08-07 08:40:06 +00:00
|
|
|
if (debug)
|
2018-08-12 07:16:03 +00:00
|
|
|
{
|
2019-03-21 23:01:08 +00:00
|
|
|
System.out.println ("***********************************************");
|
2018-08-12 07:16:03 +00:00
|
|
|
System.out.printf ("* Disk ......... %s%n", file.getName ());
|
2019-03-21 23:01:08 +00:00
|
|
|
System.out.println ("***********************************************");
|
2018-08-12 07:16:03 +00:00
|
|
|
}
|
2018-08-11 00:07:50 +00:00
|
|
|
|
2019-03-21 23:01:08 +00:00
|
|
|
if (wozVersion == 1)
|
2018-06-08 12:14:19 +00:00
|
|
|
{
|
2019-03-21 23:01:08 +00:00
|
|
|
int tracks = chunkSize / TRK_SIZE;
|
2018-06-10 03:12:32 +00:00
|
|
|
|
2019-03-21 23:01:08 +00:00
|
|
|
for (int trackNo = 0; trackNo < tracks; trackNo++)
|
2018-08-07 08:40:06 +00:00
|
|
|
{
|
2019-03-21 23:01:08 +00:00
|
|
|
int bytesUsed = readInt (buffer, ptr + DATA_SIZE, 2);
|
|
|
|
int bitCount = readInt (buffer, ptr + DATA_SIZE + 2, 2);
|
2018-08-07 08:40:06 +00:00
|
|
|
|
2019-03-21 23:01:08 +00:00
|
|
|
if (debug)
|
|
|
|
{
|
|
|
|
System.out.println ("***************************************");
|
|
|
|
System.out.printf ("* Track ......... %,6d of %,6d *%n", trackNo,
|
|
|
|
tracks);
|
|
|
|
System.out.printf ("* Bytes used .... %,6d *%n", bytesUsed);
|
|
|
|
System.out.printf ("* Bit count .... %,6d *%n", bitCount);
|
|
|
|
System.out.println ("***************************************");
|
|
|
|
}
|
2018-08-11 00:07:50 +00:00
|
|
|
|
2019-03-21 23:01:08 +00:00
|
|
|
try
|
2018-08-11 00:07:50 +00:00
|
|
|
{
|
2019-03-21 23:01:08 +00:00
|
|
|
// nibbleTracks.add (mc3470.getNibbleTrack (buffer, ptr, bytesUsed, bitCount));
|
|
|
|
List<RawDiskSector> diskSectors =
|
|
|
|
mc3470.readTrack (buffer, ptr, bytesUsed, bitCount);
|
|
|
|
|
|
|
|
if (trackNo == 0) // create disk buffer
|
2018-08-11 00:07:50 +00:00
|
|
|
{
|
2019-03-21 23:01:08 +00:00
|
|
|
if (mc3470.is13Sector ())
|
|
|
|
diskBuffer = new byte[35 * 13 * 256];
|
|
|
|
else if (mc3470.is16Sector ())
|
|
|
|
diskBuffer = new byte[35 * 16 * 256];
|
|
|
|
else
|
|
|
|
{
|
|
|
|
System.out.println ("unknown disk format");
|
|
|
|
break read;
|
|
|
|
}
|
2018-08-11 00:07:50 +00:00
|
|
|
}
|
2019-03-21 23:01:08 +00:00
|
|
|
|
|
|
|
mc3470.storeSectors (diskSectors, diskBuffer);
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
System.out.println (e);
|
|
|
|
break read;
|
2018-08-11 00:07:50 +00:00
|
|
|
}
|
|
|
|
|
2019-03-21 23:01:08 +00:00
|
|
|
ptr += TRK_SIZE;
|
2018-06-08 12:14:19 +00:00
|
|
|
}
|
2019-03-21 23:01:08 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
diskBuffer = new byte[(bootSectorFormat == 2 ? 13 : 16) * 35 * 256];
|
|
|
|
|
|
|
|
for (int trackNo = 0; trackNo < 160; trackNo++)
|
2018-08-11 00:07:50 +00:00
|
|
|
{
|
2019-03-21 23:01:08 +00:00
|
|
|
int p = 256 + trackNo * 8;
|
|
|
|
int startingBlock = readInt (buffer, p, 2);
|
|
|
|
int blockCount = readInt (buffer, p + 2, 2);
|
|
|
|
int bitCount = readInt (buffer, p + 4, 4);
|
2018-08-11 00:07:50 +00:00
|
|
|
|
2019-03-22 00:59:25 +00:00
|
|
|
if (debug)
|
|
|
|
{
|
|
|
|
System.out.println ("******************************");
|
|
|
|
System.out.printf ("* Track ......... %,6d *%n", trackNo);
|
|
|
|
System.out.printf ("* Block count ... %,6d *%n", blockCount);
|
|
|
|
System.out.printf ("* Bit count .... %,6d *%n", bitCount);
|
|
|
|
System.out.println ("******************************");
|
|
|
|
}
|
|
|
|
|
2019-03-21 23:01:08 +00:00
|
|
|
if (startingBlock == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
// nibbleTracks.add (mc3470.getNibbleTrack (buffer, ptr, bytesUsed, bitCount));
|
|
|
|
List<RawDiskSector> diskSectors = mc3470.readTrack (buffer,
|
|
|
|
startingBlock * 512, blockCount * 512, bitCount);
|
|
|
|
|
|
|
|
mc3470.storeSectors (diskSectors, diskBuffer);
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
System.out.println (e);
|
|
|
|
break read;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ptr += chunkSize;
|
2018-06-08 12:14:19 +00:00
|
|
|
}
|
|
|
|
}
|
2018-06-10 03:12:32 +00:00
|
|
|
else if ("META".equals (chunkId))
|
|
|
|
{
|
2018-08-12 07:16:03 +00:00
|
|
|
// System.out.printf ("[%s] %08X%n", chunkId, chunkSize);
|
|
|
|
// System.out.println (HexFormatter.format (buffer, ptr, chunkSize));
|
2018-06-10 03:12:32 +00:00
|
|
|
ptr += chunkSize;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
System.out.printf ("Unknown %08X%n", chunkSize);
|
|
|
|
ptr += chunkSize;
|
|
|
|
}
|
2018-06-08 12:14:19 +00:00
|
|
|
}
|
2018-08-18 03:24:22 +00:00
|
|
|
|
2019-03-21 23:01:08 +00:00
|
|
|
// if (!valid)
|
|
|
|
// readNibbleTracks (buffer);
|
2018-08-18 03:24:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
// readNibbleTracks
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
|
|
|
private void readNibbleTracks (byte[] buffer)
|
|
|
|
{
|
|
|
|
for (int track = 0; track < 35; track++)
|
|
|
|
{
|
|
|
|
int ptr = track * 6656 + 256;
|
|
|
|
|
|
|
|
int bytesUsed = readInt (buffer, ptr + DATA_SIZE, 2);
|
|
|
|
int bitCount = readInt (buffer, ptr + DATA_SIZE + 2, 2);
|
|
|
|
|
|
|
|
NibbleTrack nibbleTrack = mc3470.getNibbleTrack (buffer, ptr, bytesUsed, bitCount);
|
|
|
|
nibbleTracks.add (nibbleTrack);
|
|
|
|
}
|
2018-06-08 12:14:19 +00:00
|
|
|
}
|
|
|
|
|
2018-08-11 00:07:50 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
// getSectorsPerTrack
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
2018-08-17 01:20:00 +00:00
|
|
|
public int getSectorsPerTrack ()
|
2018-08-11 00:07:50 +00:00
|
|
|
{
|
2018-08-11 04:12:21 +00:00
|
|
|
return mc3470.is13Sector () ? 13 : mc3470.is16Sector () ? 16 : 0;
|
2018-08-11 00:07:50 +00:00
|
|
|
}
|
|
|
|
|
2018-06-09 01:11:02 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
// readInt
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
2018-06-08 12:14:19 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-08-17 01:20:00 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
// getDiskBuffer
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
|
|
|
public byte[] getDiskBuffer ()
|
|
|
|
{
|
|
|
|
return diskBuffer;
|
|
|
|
}
|
|
|
|
|
2018-06-10 20:45:11 +00:00
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
// readFile
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
|
|
|
private byte[] readFile ()
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
BufferedInputStream in = new BufferedInputStream (new FileInputStream (file));
|
|
|
|
byte[] buffer = in.readAllBytes ();
|
|
|
|
in.close ();
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
catch (IOException e)
|
|
|
|
{
|
|
|
|
e.printStackTrace ();
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
// matches
|
|
|
|
// ---------------------------------------------------------------------------------//
|
|
|
|
|
|
|
|
private boolean matches (byte[] b1, byte[] b2)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < b1.length; i++)
|
|
|
|
if (b1[i] != b2[i])
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
2018-06-08 12:14:19 +00:00
|
|
|
}
|