Wiz 4 maps

This commit is contained in:
Denis Molony 2016-08-14 18:41:19 +10:00
parent 2036ca4b96
commit c0adaef3e2
12 changed files with 620 additions and 645 deletions

View File

@ -3,7 +3,6 @@ package com.bytezone.diskbrowser.applefile;
import java.util.ArrayList;
import java.util.List;
import com.bytezone.diskbrowser.applefile.Relocator.MultiDiskAddress;
import com.bytezone.diskbrowser.utilities.FileFormatException;
import com.bytezone.diskbrowser.utilities.HexFormatter;
@ -12,8 +11,9 @@ public class PascalSegment extends AbstractFile implements PascalConstants
private final static int BLOCK_SIZE = 512;
final int segmentNoHeader;
private int segmentNoBody;
private final int blockOffset;
// private final int blockOffset;
// private final Relocator relocator;
boolean debug = false;
public int blockNo;
// public int newBlockNo;
@ -28,14 +28,14 @@ public class PascalSegment extends AbstractFile implements PascalConstants
private final int slot;
private int totalProcedures;
private List<PascalProcedure> procedures;
private List<MultiDiskAddress> addresses;
// private List<MultiDiskAddress> addresses;
public PascalSegment (String name, byte[] fullBuffer, int seq, int blockOffset)
{
super (name, fullBuffer); // sets this.buffer to the full buffer temporarily
this.slot = seq;
this.blockOffset = blockOffset;
// this.blockOffset = blockOffset;
// this.relocator = relocator;
this.blockNo = HexFormatter.intValue (fullBuffer[seq * 4], fullBuffer[seq * 4 + 1]);
@ -89,18 +89,19 @@ public class PascalSegment extends AbstractFile implements PascalConstants
{
buffer = new byte[0];
}
else if (offset < fullBuffer.length)
else if ((offset + size) < fullBuffer.length)
{
buffer = new byte[size]; // replaces this.buffer with the segment buffer only
System.arraycopy (fullBuffer, offset, buffer, 0, size);
totalProcedures = buffer[size - 1] & 0xFF;
segmentNoBody = buffer[size - 2] & 0xFF;
if (segmentNoHeader == 0)
System.out.printf ("Zero segment header in %s seq %d%n", name, seq);
else if (segmentNoBody != segmentNoHeader)
System.out.println (
"Segment number mismatch : " + segmentNoBody + " / " + segmentNoHeader);
if (debug)
if (segmentNoHeader == 0)
System.out.printf ("Zero segment header in %s seq %d%n", name, seq);
else if (segmentNoBody != segmentNoHeader)
System.out.println (
"Segment number mismatch : " + segmentNoBody + " / " + segmentNoHeader);
}
else
{
@ -108,10 +109,10 @@ public class PascalSegment extends AbstractFile implements PascalConstants
}
}
void setMultiDiskAddresses (List<MultiDiskAddress> addresses)
{
this.addresses = addresses;
}
// void setMultiDiskAddresses (List<MultiDiskAddress> addresses)
// {
// this.addresses = addresses;
// }
private void buildProcedureList ()
{

View File

@ -1,364 +0,0 @@
package com.bytezone.diskbrowser.applefile;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.bytezone.diskbrowser.disk.AppleDisk;
import com.bytezone.diskbrowser.disk.Disk;
import com.bytezone.diskbrowser.disk.DiskAddress;
import com.bytezone.diskbrowser.utilities.HexFormatter;
public class Relocator extends AbstractFile
{
private final int checkByte;
private final List<DiskRecord> diskRecords = new ArrayList<DiskRecord> ();
private final List<MultiDiskAddress> addresses = new ArrayList<MultiDiskAddress> ();
private final List<MultiDiskAddress> newAddresses = new ArrayList<MultiDiskAddress> ();
private final List<MultiDiskAddress> oldAddresses = new ArrayList<MultiDiskAddress> ();
private final List<MultiDiskAddress> logicalAddresses =
new ArrayList<MultiDiskAddress> ();
private final byte[] diskBlocks = new byte[0x800];
private final int[] diskOffsets = new int[0x800];
private final int[] diskOffsets2 = new int[0x800];
private final Disk[] dataDisks = new Disk[5];
public Relocator (String name, byte[] buffer)
{
super (name, buffer);
checkByte = HexFormatter.intValue (buffer[0], buffer[1]);
int ptr = 2; // skip checkByte
while (buffer[ptr] != 0)
{
DiskRecord diskRecord = new DiskRecord (buffer, ptr);
diskRecords.add (diskRecord);
ptr += diskRecord.size ();
}
logicalAddresses.add (new MultiDiskAddress (0, 0, 0, 0x800));
for (DiskRecord diskRecord : diskRecords)
for (DiskSegment diskSegment : diskRecord.diskSegments)
{
addresses
.add (new MultiDiskAddress (diskRecord.diskNumber, diskSegment.logicalBlock,
diskSegment.physicalBlock, diskSegment.segmentLength));
addLogicalBlock ((byte) diskRecord.diskNumber, diskSegment);
}
getMultiDiskAddress ("BOOT", 0, 2);
getMultiDiskAddress ("CATALOG", 2, 4);
}
public void list ()
{
for (MultiDiskAddress multiDiskAddress : addresses)
System.out.printf ("%d %03X %03X %03X %s%n", multiDiskAddress.diskNumber,
multiDiskAddress.logicalBlockNumber, multiDiskAddress.physicalBlockNumber,
multiDiskAddress.totalBlocks, multiDiskAddress.name);
}
private void addLogicalBlock (byte disk, DiskSegment diskSegment)
{
int lo = diskSegment.logicalBlock;
int hi = diskSegment.logicalBlock + diskSegment.segmentLength;
int count = 0;
for (int i = lo; i < hi; i++)
if (diskBlocks[i] == 0)
{
diskBlocks[i] = disk;
diskOffsets[i] = diskSegment.physicalBlock;
diskOffsets2[i] = diskSegment.physicalBlock + count++;
}
}
public byte[] getLogicalBuffer (DiskAddress da)
{
int block = da.getBlock ();
System.out.println (diskBlocks[block]);
Disk disk = dataDisks[diskBlocks[block] - 1];
System.out.println (diskOffsets2[block]);
System.out.println (disk);
return disk.readSector (diskOffsets2[block]);
}
public List<MultiDiskAddress> getMultiDiskAddress (String name, int blockNumber,
int length)
{
List<MultiDiskAddress> foundAddresses = new ArrayList<MultiDiskAddress> ();
newAddresses.clear ();
oldAddresses.clear ();
// System.out.printf ("%04X %04X %s%n", blockNumber, length, name);
for (MultiDiskAddress multiDiskAddress : addresses)
{
if (multiDiskAddress.logicalBlockNumber == blockNumber)
{
if (multiDiskAddress.totalBlocks == length)
{
foundAddresses.add (multiDiskAddress);
if (multiDiskAddress.name.isEmpty ())
multiDiskAddress.name = name;
}
else if (multiDiskAddress.totalBlocks > length)
{
MultiDiskAddress newAddress1 = new MultiDiskAddress (
multiDiskAddress.diskNumber, multiDiskAddress.logicalBlockNumber,
multiDiskAddress.physicalBlockNumber, length, name);
MultiDiskAddress newAddress2 = new MultiDiskAddress (
multiDiskAddress.diskNumber, multiDiskAddress.logicalBlockNumber + length,
multiDiskAddress.physicalBlockNumber + length,
multiDiskAddress.totalBlocks - length);
oldAddresses.add (multiDiskAddress);
newAddresses.add (newAddress1);
newAddresses.add (newAddress2);
foundAddresses.add (newAddress1);
}
}
}
if (newAddresses.size () > 0)
{
addresses.addAll (newAddresses);
addresses.removeAll (oldAddresses);
Collections.sort (addresses);
}
return foundAddresses;
}
public void setDisks (Disk[] disks)
{
for (Disk disk : disks)
{
byte[] buffer = disk.readSector (1);
int diskNo = buffer[510] & 0xFF;
if (diskNo > 0 && diskNo <= 5)
dataDisks[diskNo - 1] = disk;
}
}
public boolean hasData ()
{
for (Disk disk : dataDisks)
if (disk == null)
return false;
return true;
}
public void createNewBuffer (Disk[] dataDisks)
{
AppleDisk master = (AppleDisk) dataDisks[0];
for (int logicalBlock = 0; logicalBlock < diskBlocks.length; logicalBlock++)
{
int physicalBlock = diskOffsets2[logicalBlock];
int diskNo = diskBlocks[logicalBlock];
if (diskNo > 0)
{
Disk disk = dataDisks[diskNo];
byte[] temp = disk.readSector (physicalBlock);
DiskAddress da = master.getDiskAddress (logicalBlock);
master.writeSector (da, temp);
}
}
}
@Override
public String getText ()
{
StringBuilder text = new StringBuilder ();
text.append ("Pascal Relocator\n\n");
text.append (String.format ("Check byte..... %04X%n%n", checkByte));
for (DiskRecord diskRecord : diskRecords)
{
text.append (diskRecord);
text.append ("\n");
}
if (false)
{
int previousDiskNumber = 0;
for (MultiDiskAddress multiDiskAddress : addresses)
{
if (multiDiskAddress.diskNumber != previousDiskNumber)
{
previousDiskNumber = multiDiskAddress.diskNumber;
text.append ("\n");
text.append ("Disk Logical Physical Size Name\n");
text.append ("---- ------- -------- ---- -------------\n");
}
text.append (String.format (" %d %03X %03X %03X %s%n",
multiDiskAddress.diskNumber, multiDiskAddress.logicalBlockNumber,
multiDiskAddress.physicalBlockNumber, multiDiskAddress.totalBlocks,
multiDiskAddress.name));
}
}
text.append ("\n Logical Size Disk Physical");
text.append ("\n--------- ---- ---- ---------\n");
int first = 0;
int lastDisk = diskBlocks[0];
int lastOffset = diskOffsets[0];
for (int i = 0; i < diskBlocks.length; i++)
{
if (diskBlocks[i] != lastDisk || diskOffsets[i] != lastOffset)
{
int size = i - first;
if (lastDisk > 0)
text.append (String.format ("%03X - %03X %03X %d %03X - %03X%n", first,
i - 1, size, lastDisk, lastOffset, lastOffset + size - 1));
else
text.append (String.format ("%03X - %03X %03X%n", first, i - 1, size));
first = i;
lastDisk = diskBlocks[i];
lastOffset = diskOffsets[i];
}
}
if (lastDisk > 0)
{
int max = diskBlocks.length;
int size = max - first;
text.append (String.format ("%03X - %03X %03X %d %03X - %03X%n", first,
max - 1, size, lastDisk, lastOffset, lastOffset + size - 1));
}
return text.toString ();
}
private class DiskRecord
{
int diskNumber;
int totDiskSegments;
List<DiskSegment> diskSegments = new ArrayList<DiskSegment> ();
public DiskRecord (byte[] buffer, int ptr)
{
diskNumber = HexFormatter.intValue (buffer[ptr], buffer[ptr + 1]);
totDiskSegments = HexFormatter.intValue (buffer[ptr + 2], buffer[ptr + 4]);
ptr += 4;
for (int i = 0; i < totDiskSegments; i++)
{
diskSegments.add (new DiskSegment (buffer, ptr));
ptr += 6;
}
}
int size ()
{
return 4 + diskSegments.size () * 6;
}
@Override
public String toString ()
{
StringBuilder text = new StringBuilder ();
text.append (String.format ("Disk number.... %04X%n", diskNumber));
text.append (String.format ("Segments....... %04X%n%n", totDiskSegments));
text.append (String.format (" Seg Skip Size Logical Physical%n"));
text.append (String.format (" --- ---- ---- ----------- -----------%n"));
int count = 1;
int last = 0;
int size = 0;
for (DiskSegment segment : diskSegments)
{
if (segment.logicalBlock > last)
{
int end = segment.logicalBlock - 1;
size = end - last + 1;
}
last = segment.logicalBlock + segment.segmentLength;
text.append (
String.format (" %02X %04X %s %n", count++, size, segment.toString ()));
}
return text.toString ();
}
}
private class DiskSegment
{
int logicalBlock;
int physicalBlock;
int segmentLength;
public DiskSegment (byte[] buffer, int ptr)
{
logicalBlock = HexFormatter.intValue (buffer[ptr], buffer[ptr + 1]);
physicalBlock = HexFormatter.intValue (buffer[ptr + 2], buffer[ptr + 3]);
segmentLength = HexFormatter.intValue (buffer[ptr + 4], buffer[ptr + 5]);
}
@Override
public String toString ()
{
return String.format (" %04X %04X - %04X %04X - %04X", segmentLength,
logicalBlock, (logicalBlock + segmentLength - 1), physicalBlock,
(physicalBlock + segmentLength - 1));
}
// public String toString (int offset)
// {
// int logical = logicalBlock - offset;
// int physical = physicalBlock - offset;
// if (physical >= 0)
// return String.format (" %04X %04X %04X %04X %04X",
// logicalBlock, physicalBlock, segmentLength, logical, physical);
// return String.format (" %04X %04X %04X", logicalBlock, physicalBlock,
// segmentLength);
// }
}
class MultiDiskAddress implements Comparable<MultiDiskAddress>
{
int diskNumber;
int logicalBlockNumber;
int physicalBlockNumber;
int totalBlocks;
String name = "";
public MultiDiskAddress (int diskNumber, int logicalBlockNumber,
int physicalBlockNumber, int totalBlocks)
{
this.diskNumber = diskNumber;
this.logicalBlockNumber = logicalBlockNumber;
this.physicalBlockNumber = physicalBlockNumber;
this.totalBlocks = totalBlocks;
}
public MultiDiskAddress (int diskNumber, int logicalBlockNumber,
int physicalBlockNumber, int totalBlocks, String name)
{
this (diskNumber, logicalBlockNumber, physicalBlockNumber, totalBlocks);
this.name = name;
}
@Override
public String toString ()
{
return String.format ("%d:%03X", diskNumber, physicalBlockNumber);
}
@Override
public int compareTo (MultiDiskAddress o)
{
if (this.diskNumber == o.diskNumber)
return this.logicalBlockNumber - o.logicalBlockNumber;
return this.diskNumber - o.diskNumber;
}
}
}

View File

@ -388,34 +388,35 @@ public class DiskFactory
if (debug)
System.out.println ("Not a Wizardry 1-3 disk");
if (Wizardry4BootDisk.isWizardryIV (disk, debug))
// check for compressed disk
if (file.getName ().endsWith (".tmp"))
return new PascalDisk (disk); // complicated joining up compressed disks
if (Wizardry4BootDisk.isWizardryIVorV (disk, debug))
{
// collect 4 extra data disks
AppleDisk[] disks = new AppleDisk[6];
String fileName = file.getAbsolutePath ().toLowerCase ();
int pos = file.getAbsolutePath ().indexOf ('.');
char c = fileName.charAt (pos - 1);
String suffix = fileName.substring (pos + 1);
int requiredDisks = c == '1' ? 6 : c == 'a' ? 10 : 0;
disks[0] = new AppleDisk (file, 256, 8); // will become a PascalDisk
disks[1] = new AppleDisk (file, 256, 8); // will remain a DataDisk
disks[0].setInterleave (1);
disks[1].setInterleave (1);
for (int i = 2; i < disks.length; i++)
if (requiredDisks > 0)
{
String filename = file.getAbsolutePath ().replace ("1.dsk", i + ".dsk");
File f = new File (filename);
if (f.exists () && f.isFile ())
// collect extra data disks
AppleDisk[] disks = new AppleDisk[requiredDisks];
disks[0] = new AppleDisk (file, 256, 8); // will become a PascalDisk
disks[0].setInterleave (1);
disks[1] = new AppleDisk (file, 256, 8); // will remain a DataDisk
disks[1].setInterleave (1);
if (pos > 0 && requiredDisks > 0)
{
AppleDisk dataDisk = new AppleDisk (f, 35, 8);
dataDisk.setInterleave (1);
disks[i] = dataDisk;
}
else
{
PascalDisk pascalDisk = new PascalDisk (disk);
return pascalDisk;
if (collectDataDisks (file.getAbsolutePath (), pos, disks))
return new Wizardry4BootDisk (disks);
}
}
Wizardry4BootDisk wiz4 = new Wizardry4BootDisk (disks);
return wiz4;
}
if (debug)
System.out.println ("Not a Wizardry IV disk");
@ -424,6 +425,29 @@ public class DiskFactory
return pascalDisk;
}
private static boolean collectDataDisks (String fileName, int dotPos, AppleDisk[] disks)
{
char c = fileName.charAt (dotPos - 1);
String suffix = fileName.substring (dotPos + 1);
for (int i = 2; i < disks.length; i++)
{
String old = new String (c + "." + suffix);
String rep = new String ((char) (c + i - 1) + "." + suffix);
// System.out.printf ("[%s] [%s]%n", old, rep);
File f = new File (fileName.replace (old, rep));
// System.out.println (f);
if (!f.exists () || !f.isFile ())
return false;
AppleDisk dataDisk = new AppleDisk (f, 35, 8);
dataDisk.setInterleave (1);
disks[i] = dataDisk;
}
return true;
}
private static InfocomDisk checkInfocomDisk (File file)
{
if (debug)

View File

@ -13,15 +13,15 @@ import com.bytezone.diskbrowser.utilities.HexFormatter;
abstract class CatalogEntry implements AppleFileSource
{
protected AbstractFile file;
protected final PascalDisk parent;
protected final String name;
int firstBlock;
int lastBlock; // block AFTER last used block
int fileType;
GregorianCalendar date;
int bytesUsedInLastBlock;
List<DiskAddress> blocks = new ArrayList<DiskAddress> ();
AbstractFile file;
protected String name;
protected int firstBlock;
protected int lastBlock; // block AFTER last used block
protected int fileType;
protected GregorianCalendar date;
protected int bytesUsedInLastBlock;
protected final List<DiskAddress> blocks = new ArrayList<DiskAddress> ();
public CatalogEntry (PascalDisk parent, byte[] buffer)
{
@ -34,7 +34,8 @@ abstract class CatalogEntry implements AppleFileSource
bytesUsedInLastBlock = HexFormatter.intValue (buffer[16], buffer[17]);
Disk disk = parent.getDisk ();
for (int i = firstBlock; i < lastBlock; i++)
int max = Math.min (lastBlock, disk.getTotalBlocks ());
for (int i = firstBlock; i < max; i++)
blocks.add (disk.getDiskAddress (i));
}
@ -47,13 +48,6 @@ abstract class CatalogEntry implements AppleFileSource
return false;
}
@Override
public String toString ()
{
int size = lastBlock - firstBlock;
return String.format ("%03d %s %-15s", size, parent.fileTypes[fileType], name);
}
@Override
public List<DiskAddress> getSectors ()
{
@ -72,4 +66,11 @@ abstract class CatalogEntry implements AppleFileSource
{
return name;
}
@Override
public String toString ()
{
int size = lastBlock - firstBlock;
return String.format ("%03d %s %-15s", size, parent.fileTypes[fileType], name);
}
}

View File

@ -8,9 +8,7 @@ import com.bytezone.diskbrowser.utilities.HexFormatter;
public class FileEntry extends CatalogEntry
{
int bytesUsedInLastBlock;
private DefaultMutableTreeNode node;
AbstractFile file;
public FileEntry (PascalDisk parent, byte[] buffer)
{
@ -19,7 +17,8 @@ public class FileEntry extends CatalogEntry
bytesUsedInLastBlock = HexFormatter.intValue (buffer[22], buffer[23]);
date = HexFormatter.getPascalDate (buffer, 24);
for (int i = firstBlock; i < lastBlock; i++)
int max = Math.min (lastBlock, parent.getDisk ().getTotalBlocks ());
for (int i = firstBlock; i < max; i++)
{
switch (fileType)
{

View File

@ -10,10 +10,10 @@ import javax.swing.tree.DefaultMutableTreeNode;
import com.bytezone.diskbrowser.applefile.AppleFileSource;
import com.bytezone.diskbrowser.applefile.BootSector;
import com.bytezone.diskbrowser.applefile.Relocator;
import com.bytezone.diskbrowser.disk.*;
import com.bytezone.diskbrowser.gui.DataSource;
import com.bytezone.diskbrowser.utilities.HexFormatter;
import com.bytezone.diskbrowser.wizardry.Relocator;
public class PascalDisk extends AbstractFormattedDisk
{
@ -52,12 +52,6 @@ public class PascalDisk extends AbstractFormattedDisk
List<DiskAddress> blocks = disk.getDiskAddressList (0, 1); // B0, B1
this.bootSector = new BootSector (disk, disk.readSectors (blocks), "Pascal");
byte[] buffer = disk.readSector (2);
byte[] data = new byte[CATALOG_ENTRY_SIZE];
System.arraycopy (buffer, 0, data, 0, CATALOG_ENTRY_SIZE);
volumeEntry = new VolumeEntry (this, data);
for (int i = 0; i < 2; i++)
if (!disk.isSectorEmpty (i))
{
@ -68,8 +62,18 @@ public class PascalDisk extends AbstractFormattedDisk
for (int i = 2; i < disk.getTotalBlocks (); i++)
freeBlocks.set (i, true);
byte[] buffer = disk.readSector (2);
byte[] data = new byte[CATALOG_ENTRY_SIZE];
System.arraycopy (buffer, 0, data, 0, CATALOG_ENTRY_SIZE);
volumeEntry = new VolumeEntry (this, data);
DefaultMutableTreeNode root = getCatalogTreeRoot ();
DefaultMutableTreeNode volumeNode = new DefaultMutableTreeNode (volumeEntry);
root.add (volumeNode);
List<DiskAddress> sectors = new ArrayList<DiskAddress> ();
for (int i = 2; i < volumeEntry.lastBlock; i++)
int max = Math.min (volumeEntry.lastBlock, disk.getTotalBlocks ());
for (int i = 2; i < max; i++)
{
DiskAddress da = disk.getDiskAddress (i);
if (!disk.isSectorEmpty (da))
@ -81,13 +85,9 @@ public class PascalDisk extends AbstractFormattedDisk
diskCatalogSector =
new PascalCatalogSector (disk, disk.readSectors (sectors), sectors);
DefaultMutableTreeNode root = getCatalogTreeRoot ();
DefaultMutableTreeNode volumeNode = new DefaultMutableTreeNode (volumeEntry);
root.add (volumeNode);
// read the catalog
List<DiskAddress> addresses = new ArrayList<DiskAddress> ();
for (int i = 2; i < volumeEntry.lastBlock; i++)
for (int i = 2; i < max; i++)
addresses.add (disk.getDiskAddress (i));
buffer = disk.readSectors (addresses);
@ -96,9 +96,9 @@ public class PascalDisk extends AbstractFormattedDisk
{
int ptr = i * CATALOG_ENTRY_SIZE;
data = new byte[CATALOG_ENTRY_SIZE];
System.arraycopy (buffer, ptr, data, 0, CATALOG_ENTRY_SIZE);
FileEntry fileEntry = new FileEntry (this, data);
fileEntries.add (fileEntry);
DefaultMutableTreeNode node = new DefaultMutableTreeNode (fileEntry);
fileEntry.setNode (node);
@ -106,7 +106,7 @@ public class PascalDisk extends AbstractFormattedDisk
if (fileEntry.fileType == 2)
{
node.setAllowsChildren (true);
fileEntry.getDataSource ();
fileEntry.getDataSource (); // build segments
}
else
node.setAllowsChildren (false);

View File

@ -8,37 +8,14 @@ class VolumeEntry extends CatalogEntry
{
final int totalFiles;
final int totalBlocks;
final int block1; // first block on the disk (usually 0)
final int lastDirectoryBlock; // (plus 1) (usually 6)
final int recordType; // 0 = directory
final int nameLength;
final String name;
public VolumeEntry (PascalDisk parent, byte[] buffer)
{
super (parent, buffer);
block1 = HexFormatter.intValue (buffer[0], buffer[1]); // 0
lastDirectoryBlock = HexFormatter.intValue (buffer[2], buffer[3]); // 6
recordType = HexFormatter.intValue (buffer[4], buffer[5]); // 0
nameLength = buffer[6] & 0xFF;
name = HexFormatter.getPascalString (buffer, 6); // 06-0D
totalBlocks = HexFormatter.intValue (buffer[14], buffer[15]); // 280
totalFiles = HexFormatter.intValue (buffer[16], buffer[17]);
firstBlock = HexFormatter.intValue (buffer[18], buffer[19]); // 0
date = HexFormatter.getPascalDate (buffer, 20); // 2 bytes
// bytes 0x16 - 0x19 are unused
if (false)
{
System.out.printf ("Total files ..... %d%n", totalFiles);
System.out.printf ("Total blocks .... %d%n", totalBlocks);
System.out.printf ("Block1 .......... %d%n", block1);
System.out.printf ("Last block ...... %d%n", lastDirectoryBlock);
System.out.printf ("Record type ..... %d%n", recordType);
System.out.printf ("Name length ..... %d%n", nameLength);
System.out.printf ("Name ............ %s%n", name);
}
}
@Override

View File

@ -11,206 +11,212 @@ import com.bytezone.diskbrowser.utilities.HexFormatter;
class MazeLevel extends AbstractFile
{
public int level;
private List<Message> messages;
private List<Monster> monsters;
private List<Item> items;
public final int level;
private List<Message> messages;
private List<Monster> monsters;
private List<Item> items;
public MazeLevel (byte[] buffer, int level)
{
super ("Level " + level, buffer);
this.level = level;
}
public MazeLevel (byte[] buffer, int level)
{
super ("Level " + level, buffer);
this.level = level;
}
@Override
public BufferedImage getImage ()
{
Dimension cellSize = new Dimension (22, 22);
image =
new BufferedImage (20 * cellSize.width + 1, 20 * cellSize.height + 1,
BufferedImage.TYPE_USHORT_555_RGB);
Graphics2D g = image.createGraphics ();
g.setRenderingHint (RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
@Override
public BufferedImage getImage ()
{
Dimension cellSize = new Dimension (22, 22);
image = new BufferedImage (20 * cellSize.width + 1, 20 * cellSize.height + 1,
BufferedImage.TYPE_USHORT_555_RGB);
Graphics2D g = image.createGraphics ();
g.setRenderingHint (RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (int row = 0; row < 20; row++)
for (int column = 0; column < 20; column++)
{
MazeCell cell = getLocation ((row) % 20, (column) % 20);
int x = column * cellSize.width;
int y = image.getHeight () - (row + 1) * cellSize.height - 1;
cell.draw (g, x, y);
}
return image;
}
for (int row = 0; row < 20; row++)
for (int column = 0; column < 20; column++)
{
MazeCell cell = getLocation (row, column);
int x = column * cellSize.width;
int y = image.getHeight () - (row + 1) * cellSize.height - 1;
cell.draw (g, x, y);
}
return image;
}
public void setMessages (List<Message> messages)
{
this.messages = messages;
}
public void setMessages (List<Message> messages)
{
this.messages = messages;
}
public void setMonsters (List<Monster> monsters)
{
this.monsters = monsters;
}
public void setMonsters (List<Monster> monsters)
{
this.monsters = monsters;
}
public void setItems (List<Item> items)
{
this.items = items;
}
public void setItems (List<Item> items)
{
this.items = items;
}
public MazeCell getLocation (int row, int column)
{
MazeAddress address = new MazeAddress (level, row, column);
MazeCell cell = new MazeCell (address);
public MazeCell getLocation (int row, int column)
{
MazeAddress address = new MazeAddress (level, row, column);
MazeCell cell = new MazeCell (address);
// doors and walls
// doors and walls
int offset = column * 6 + row / 4; // 6 bytes/column
int offset = column * 6 + row / 4; // 6 bytes/column
int value = HexFormatter.intValue (buffer[offset]);
value >>>= (row % 4) * 2;
cell.westWall = ((value & 1) == 1);
value >>>= 1;
cell.westDoor = ((value & 1) == 1);
int value = HexFormatter.intValue (buffer[offset]);
value >>>= (row % 4) * 2;
cell.westWall = ((value & 1) == 1);
value >>>= 1;
cell.westDoor = ((value & 1) == 1);
value = HexFormatter.intValue (buffer[offset + 120]);
value >>>= (row % 4) * 2;
cell.southWall = ((value & 1) == 1);
value >>>= 1;
cell.southDoor = ((value & 1) == 1);
value = HexFormatter.intValue (buffer[offset + 120]);
value >>>= (row % 4) * 2;
cell.southWall = ((value & 1) == 1);
value >>>= 1;
cell.southDoor = ((value & 1) == 1);
value = HexFormatter.intValue (buffer[offset + 240]);
value >>>= (row % 4) * 2;
cell.eastWall = ((value & 1) == 1);
value >>>= 1;
cell.eastDoor = ((value & 1) == 1);
value = HexFormatter.intValue (buffer[offset + 240]);
value >>>= (row % 4) * 2;
cell.eastWall = ((value & 1) == 1);
value >>>= 1;
cell.eastDoor = ((value & 1) == 1);
value = HexFormatter.intValue (buffer[offset + 360]);
value >>>= (row % 4) * 2;
cell.northWall = ((value & 1) == 1);
value >>>= 1;
cell.northDoor = ((value & 1) == 1);
value = HexFormatter.intValue (buffer[offset + 360]);
value >>>= (row % 4) * 2;
cell.northWall = ((value & 1) == 1);
value >>>= 1;
cell.northDoor = ((value & 1) == 1);
// monster table
// monster table
offset = column * 4 + row / 8; // 4 bytes/column, 1 bit/row
value = HexFormatter.intValue (buffer[offset + 480]);
value >>>= row % 8;
cell.monsterLair = ((value & 1) == 1);
offset = column * 4 + row / 8; // 4 bytes/column, 1 bit/row
value = HexFormatter.intValue (buffer[offset + 480]);
value >>>= row % 8;
cell.monsterLair = ((value & 1) == 1);
// stairs, pits, darkness etc.
// stairs, pits, darkness etc.
offset = column * 10 + row / 2; // 10 bytes/column, 4 bits/row
value = HexFormatter.intValue (buffer[offset + 560]);
int b = (row % 2 == 0) ? value % 16 : value / 16;
int c = HexFormatter.intValue (buffer[760 + b / 2]);
int d = (b % 2 == 0) ? c % 16 : c / 16;
offset = column * 10 + row / 2; // 10 bytes/column, 4 bits/row
value = HexFormatter.intValue (buffer[offset + 560]);
int b = (row % 2 == 0) ? value % 16 : value / 16;
int c = HexFormatter.intValue (buffer[760 + b / 2]);
int d = (b % 2 == 0) ? c % 16 : c / 16;
switch (d)
{
case 1:
cell.stairs = true;
cell.addressTo = getAddress (b);
break;
case 2:
cell.pit = true;
break;
case 3:
cell.chute = true;
cell.addressTo = getAddress (b);
break;
case 4:
cell.spinner = true;
break;
case 5:
cell.darkness = true;
break;
case 6:
cell.teleport = true;
cell.addressTo = getAddress (b);
break;
case 8:
cell.elevator = true;
cell.elevatorTo = HexFormatter.intValue (buffer[800 + b * 2], buffer[801 + b * 2]);
cell.elevatorFrom = HexFormatter.intValue (buffer[832 + b * 2], buffer[833 + b * 2]);
break;
case 9:
cell.rock = true;
break;
case 10:
cell.spellsBlocked = true;
break;
case 11:
int messageNum = HexFormatter.intValue (buffer[800 + b * 2], buffer[801 + b * 2]);
for (Message m : messages)
if (m.match (messageNum))
{
cell.message = m;
break;
}
if (cell.message == null)
System.out.println ("message not found : " + messageNum);
cell.messageType = HexFormatter.intValue (buffer[832 + b * 2], buffer[833 + b * 2]);
switch (d)
{
case 1:
cell.stairs = true;
cell.addressTo = getAddress (b);
break;
case 2:
cell.pit = true;
break;
case 3:
cell.chute = true;
cell.addressTo = getAddress (b);
break;
case 4:
cell.spinner = true;
break;
case 5:
cell.darkness = true;
break;
case 6:
cell.teleport = true;
cell.addressTo = getAddress (b);
break;
case 8:
cell.elevator = true;
cell.elevatorTo =
HexFormatter.intValue (buffer[800 + b * 2], buffer[801 + b * 2]);
cell.elevatorFrom =
HexFormatter.intValue (buffer[832 + b * 2], buffer[833 + b * 2]);
break;
case 9:
cell.rock = true;
break;
case 10:
cell.spellsBlocked = true;
break;
case 11:
int messageNum = HexFormatter.intValue (buffer[800 + b * 2], buffer[801 + b * 2]);
if (messages != null)
for (Message m : messages)
if (m.match (messageNum))
{
cell.message = m;
break;
}
if (cell.message == null)
System.out.println ("message not found : " + messageNum);
cell.messageType =
HexFormatter.intValue (buffer[832 + b * 2], buffer[833 + b * 2]);
int itemID = -1;
int itemID = -1;
if (cell.messageType == 2) // obtain Item
{
itemID = HexFormatter.intValue (buffer[768 + b * 2], buffer[769 + b * 2]);
cell.itemObtained = items.get (itemID);
}
if (cell.messageType == 5) // requires Item
{
itemID = HexFormatter.intValue (buffer[768 + b * 2], buffer[769 + b * 2]);
cell.itemRequired = items.get (itemID);
}
if (cell.messageType == 4)
{
value = HexFormatter.intValue (buffer[768 + b * 2], buffer[769 + b * 2]);
if (value <= 100)
{
cell.monsterID = value;
cell.monsters = monsters;
}
else
{
int val = (value - 64536) * -1;
System.out.println ("Value : " + val);
// this gives Index error: 20410, Size 104 in Wizardry_III/legacy2.dsk
if (val < items.size ())
cell.itemObtained = items.get (val); // check this
if (cell.itemObtained == null)
System.out.printf ("Item %d not found%n", val);
}
}
break;
case 12:
cell.monsterID = HexFormatter.intValue (buffer[832 + b * 2], buffer[833 + b * 2]);
cell.monsters = monsters;
break;
default:
cell.unknown = d;
break;
}
if (cell.messageType == 2 && items != null) // obtain Item
{
itemID = HexFormatter.intValue (buffer[768 + b * 2], buffer[769 + b * 2]);
cell.itemObtained = items.get (itemID);
}
return cell;
}
if (cell.messageType == 5 && items != null) // requires Item
{
itemID = HexFormatter.intValue (buffer[768 + b * 2], buffer[769 + b * 2]);
cell.itemRequired = items.get (itemID);
}
private MazeAddress getAddress (int a)
{
int b = a * 2;
return new MazeAddress (HexFormatter.intValue (buffer[768 + b], buffer[769 + b]), HexFormatter
.intValue (buffer[800 + b], buffer[801 + b]), HexFormatter.intValue (buffer[832 + b],
buffer[833 + b]));
}
if (cell.messageType == 4)
{
value = HexFormatter.intValue (buffer[768 + b * 2], buffer[769 + b * 2]);
if (value <= 100)
{
cell.monsterID = value;
cell.monsters = monsters;
}
else
{
int val = (value - 64536) * -1;
System.out.println ("Value : " + val);
// this gives Index error: 20410, Size 104 in Wizardry_III/legacy2.dsk
if (items != null && val < items.size ())
cell.itemObtained = items.get (val); // check this
if (cell.itemObtained == null)
System.out.printf ("Item %d not found%n", val);
}
}
break;
case 12:
cell.monsterID = HexFormatter.intValue (buffer[832 + b * 2], buffer[833 + b * 2]);
cell.monsters = monsters;
break;
default:
cell.unknown = d;
break;
}
public int getRows ()
{
return 20;
}
return cell;
}
public int getColumns ()
{
return 20;
}
private MazeAddress getAddress (int a)
{
int b = a * 2;
return new MazeAddress (HexFormatter.intValue (buffer[768 + b], buffer[769 + b]),
HexFormatter.intValue (buffer[800 + b], buffer[801 + b]),
HexFormatter.intValue (buffer[832 + b], buffer[833 + b]));
}
public int getRows ()
{
return 20;
}
public int getColumns ()
{
return 20;
}
}

View File

@ -0,0 +1,215 @@
package com.bytezone.diskbrowser.wizardry;
import java.util.ArrayList;
import java.util.List;
import com.bytezone.diskbrowser.applefile.AbstractFile;
import com.bytezone.diskbrowser.disk.AppleDisk;
import com.bytezone.diskbrowser.disk.Disk;
import com.bytezone.diskbrowser.disk.DiskAddress;
import com.bytezone.diskbrowser.utilities.HexFormatter;
public class Relocator extends AbstractFile
{
private final int checkByte;
private final List<DiskRecord> diskRecords = new ArrayList<DiskRecord> ();
private final int[] diskBlocks = new int[0x800];
private final int[] diskOffsets = new int[0x800];
public Relocator (String name, byte[] buffer)
{
super (name, buffer);
checkByte = HexFormatter.intValue (buffer[0], buffer[1]);
int ptr = 2; // skip checkByte
while (buffer[ptr] != 0)
{
DiskRecord diskRecord = new DiskRecord (buffer, ptr);
diskRecords.add (diskRecord);
ptr += diskRecord.size ();
}
for (DiskRecord diskRecord : diskRecords)
for (DiskSegment diskSegment : diskRecord.diskSegments)
addLogicalBlock ((byte) diskRecord.diskNumber, diskSegment);
}
private void addLogicalBlock (byte disk, DiskSegment diskSegment)
{
int lo = diskSegment.logicalBlock;
int hi = diskSegment.logicalBlock + diskSegment.segmentLength;
for (int i = lo, count = 0; i < hi; i++, count++)
// if (diskBlocks[i] == 0) // doesn't matter either way
{
diskBlocks[i] = disk;
diskOffsets[i] = diskSegment.physicalBlock + count;
}
}
public void createNewBuffer (Disk[] dataDisks)
{
AppleDisk master = (AppleDisk) dataDisks[0];
for (int logicalBlock = 0; logicalBlock < diskBlocks.length; logicalBlock++)
{
int diskNo = diskBlocks[logicalBlock];
if (diskNo > 0)
{
Disk disk = dataDisks[diskNo];
byte[] temp = disk.readSector (diskOffsets[logicalBlock]);
DiskAddress da = master.getDiskAddress (logicalBlock);
master.writeSector (da, temp);
}
}
}
@Override
public String getText ()
{
StringBuilder text = new StringBuilder ();
text.append ("Pascal Relocator\n\n");
text.append (String.format ("Check byte..... %04X%n%n", checkByte));
for (DiskRecord diskRecord : diskRecords)
{
text.append (diskRecord);
text.append ("\n");
}
List<String> lines = new ArrayList<String> ();
String heading = " Logical Size Disk Physical";
String underline = "--------- ---- ---- ---------";
int first = 0;
int lastDisk = diskBlocks[0];
int lastOffset = diskOffsets[0];
for (int i = 0; i < diskBlocks.length; i++)
{
if (diskBlocks[i] != lastDisk || diskOffsets[i] != lastOffset + i - first)
{
int size = i - first;
if (lastDisk > 0)
lines.add (String.format ("%03X - %03X %03X %d %03X - %03X", first,
i - 1, size, lastDisk, lastOffset, lastOffset + size - 1));
else
lines.add (String.format ("%03X - %03X %03X", first, i - 1, size));
first = i;
lastDisk = diskBlocks[i];
lastOffset = diskOffsets[i];
}
}
if (lastDisk > 0)
{
int max = diskBlocks.length;
int size = max - first;
lines.add (String.format ("%03X - %03X %03X %d %03X - %03X", first, max - 1,
size, lastDisk, lastOffset, lastOffset + size - 1));
}
for (int i = lines.size () - 1; i >= 0; i--)
{
String line = lines.get (i);
if (line.length () > 20)
break;
lines.remove (i);
}
text.append (String.format (" %s %s%n %s %s%n", heading, heading,
underline, underline));
int offset = (lines.size () + 1) / 2;
// boolean oddLines = lines.size () % 2 == 1;
int pairs = lines.size () / 2;
for (int i = 0; i < pairs; i++)
{
text.append (
String.format (" %-35s %s%n", lines.get (i), lines.get (i + offset)));
}
if (offset != pairs)
text.append (String.format (" %s%n", lines.get (pairs)));
return text.toString ();
}
private class DiskRecord
{
int diskNumber;
int totDiskSegments;
List<DiskSegment> diskSegments = new ArrayList<DiskSegment> ();
public DiskRecord (byte[] buffer, int ptr)
{
diskNumber = HexFormatter.intValue (buffer[ptr], buffer[ptr + 1]);
totDiskSegments = HexFormatter.intValue (buffer[ptr + 2], buffer[ptr + 4]);
ptr += 4;
for (int i = 0; i < totDiskSegments; i++)
{
diskSegments.add (new DiskSegment (buffer, ptr));
ptr += 6;
}
}
int size ()
{
return 4 + diskSegments.size () * 6;
}
@Override
public String toString ()
{
StringBuilder text = new StringBuilder ();
text.append (String.format ("Disk number.... %04X%n", diskNumber));
text.append (String.format ("Segments....... %04X%n%n", totDiskSegments));
text.append (String.format (" Seg Skip Size Logical Physical%n"));
text.append (String.format (" --- ---- ---- ----------- -----------%n"));
int count = 1;
int last = 0;
int skip = 0;
for (DiskSegment segment : diskSegments)
{
if (segment.logicalBlock > last)
{
int end = segment.logicalBlock - 1;
skip = end - last + 1;
}
last = segment.logicalBlock + segment.segmentLength;
text.append (String.format (" %02X %04X %s %n", count++, skip, segment));
}
return text.toString ();
}
}
private class DiskSegment
{
int logicalBlock;
int physicalBlock;
int segmentLength;
public DiskSegment (byte[] buffer, int ptr)
{
logicalBlock = HexFormatter.intValue (buffer[ptr], buffer[ptr + 1]);
physicalBlock = HexFormatter.intValue (buffer[ptr + 2], buffer[ptr + 3]);
segmentLength = HexFormatter.intValue (buffer[ptr + 4], buffer[ptr + 5]);
}
@Override
public String toString ()
{
return String.format (" %04X %04X - %04X %04X - %04X", segmentLength,
logicalBlock, (logicalBlock + segmentLength - 1), physicalBlock,
(physicalBlock + segmentLength - 1));
}
}
}

View File

@ -0,0 +1,46 @@
package com.bytezone.diskbrowser.wizardry;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
public class Wiz4Image extends AbstractImage
{
public Wiz4Image (String name, byte[] buffer)
{
super (name, buffer);
image = new BufferedImage (42, 40, BufferedImage.TYPE_BYTE_GRAY); // width/height
DataBuffer db = image.getRaster ().getDataBuffer ();
int element = 0;
// System.out.println (HexFormatter.format (buffer));
for (int row = 0; row < 5; row++)
{
for (int line = 0; line < 8; line++)
{
for (int col = 0; col < 6; col++)
{
int ptr = row * 48 + col * 8 + line;
{
byte b = buffer[ptr];
for (int bit = 0; bit < 7; bit++)
{
if ((b & 0x01) == 0x01)
{
db.setElem (element, 255);
// System.out.print ("X");
}
// else
// System.out.print (".");
b >>>= 1;
element++;
}
}
}
// System.out.println ();
}
}
}
}

View File

@ -6,27 +6,33 @@ import java.util.List;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import com.bytezone.diskbrowser.applefile.Relocator;
import com.bytezone.diskbrowser.applefile.AbstractFile;
import com.bytezone.diskbrowser.disk.AppleDisk;
import com.bytezone.diskbrowser.disk.DefaultAppleFileSource;
import com.bytezone.diskbrowser.disk.Disk;
import com.bytezone.diskbrowser.disk.DiskAddress;
import com.bytezone.diskbrowser.disk.SectorType;
import com.bytezone.diskbrowser.pascal.FileEntry;
import com.bytezone.diskbrowser.pascal.PascalDisk;
import com.bytezone.diskbrowser.utilities.HexFormatter;
import com.bytezone.diskbrowser.utilities.Utility;
public class Wizardry4BootDisk extends PascalDisk
{
List<AppleDisk> disks = new ArrayList<AppleDisk> ();
protected Disk[] dataDisks;
// protected Disk[] dataDisks;
private Relocator relocator;
public Wizardry4BootDisk (AppleDisk[] dataDisks)
{
super (dataDisks[0]);
this.dataDisks = dataDisks;
// this.dataDisks = dataDisks;
DefaultTreeModel model = (DefaultTreeModel) catalogTree.getModel ();
DefaultMutableTreeNode currentRoot = (DefaultMutableTreeNode) model.getRoot ();
// get the relocation table
DefaultMutableTreeNode relocNode = findNode (currentRoot, "SYSTEM.RELOC");
FileEntry fileEntry = (FileEntry) relocNode.getUserObject ();
@ -34,27 +40,90 @@ public class Wizardry4BootDisk extends PascalDisk
{
relocator =
new Relocator (fileEntry.getUniqueName (), fileEntry.getDataSource ().buffer);
relocator.createNewBuffer (dataDisks);
relocator.createNewBuffer (dataDisks); // create new data buffer
fileEntry.setFile (relocator);
}
// reset the code segment so that it rebuilds itself from the new data
DefaultMutableTreeNode pascalNode = findNode (currentRoot, "SYSTEM.PASCAL");
fileEntry = (FileEntry) pascalNode.getUserObject ();
fileEntry.setFile (null);
fileEntry.getDataSource ();
if (fileEntry != null)
{
// fileEntry.setFile (null);
// fileEntry.getDataSource ();
}
DefaultMutableTreeNode scenarioNode = findNode (currentRoot, "SCENARIO.DATA");
fileEntry = (FileEntry) scenarioNode.getUserObject ();
if (fileEntry != null)
{
fileEntry.setFile (null);
scenarioNode.setAllowsChildren (true);
fileEntry.setFile (null);
byte[] buffer = fileEntry.getDataSource ().buffer;
for (int i = 0; i < 11; i++)
{
byte[] level = new byte[896];
System.out.println (HexFormatter.format (buffer, 0, 512));
System.arraycopy (buffer, 0xC600 + i * 1024, level, 0, level.length);
MazeLevel maze = new MazeLevel (level, i);
List<DiskAddress> blocks = new ArrayList<DiskAddress> ();
addToNode (maze, scenarioNode, blocks, null);
}
}
DefaultMutableTreeNode monstersNode = findNode (currentRoot, "200.MONSTERS");
fileEntry = (FileEntry) monstersNode.getUserObject ();
if (fileEntry != null)
{
monstersNode.setAllowsChildren (true);
byte[] pictureBuffer = fileEntry.getDataSource ().buffer;
List<DiskAddress> pictureBlocks = fileEntry.getSectors ();
int count = 0;
loop: for (int block = 0; block < 24; block++)
{
int ptr = block * 512;
for (int pic = 0; pic < 2; pic++)
{
byte[] buffer = new byte[240];
System.arraycopy (pictureBuffer, ptr + pic * 256, buffer, 0, 240);
Wiz4Image image = new Wiz4Image ("Image " + count++, buffer);
List<DiskAddress> blocks = new ArrayList<DiskAddress> ();
blocks.add (pictureBlocks.get (block));
addToNode (image, monstersNode, blocks, null);
}
}
}
}
public static boolean isWizardryIV (Disk disk, boolean debug)
private void addToNode (AbstractFile af, DefaultMutableTreeNode node,
List<DiskAddress> blocks, SectorType type)
{
DefaultAppleFileSource dafs =
new DefaultAppleFileSource (af.getName (), af, this, blocks);
DefaultMutableTreeNode childNode = new DefaultMutableTreeNode (dafs);
node.add (childNode);
childNode.setAllowsChildren (false);
}
public static boolean isWizardryIVorV (Disk disk, boolean debug)
{
// Wizardry IV or V boot code
byte[] header = { 0x00, (byte) 0xEA, (byte) 0xA9, 0x60, (byte) 0x8D, 0x01, 0x08 };
byte[] buffer = disk.readSector (0);
if (!Utility.matches (buffer, 0, header))
return false;
buffer = disk.readSector (1);
if (buffer[510] != 1)
buffer = disk.readSector (1);
if (buffer[510] != 1 || buffer[511] != 0) // disk #1
return false;
return true;

View File

@ -485,7 +485,8 @@ public class WizardryScenarioDisk extends PascalDisk
nodeSectors.addAll (blocks);
byte[] buffer = disk.readSectors (blocks);
byte[] data2 = new byte[896];
System.arraycopy (buffer, 0, data2, 0, 896);
System.arraycopy (buffer, 0, data2, 0, data2.length);
// System.out.println (HexFormatter.format (data2));
MazeLevel model = new MazeLevel (data2, i + 1);
model.setMessages (messages);