2015-06-01 09:35:51 +00:00
|
|
|
package com.bytezone.diskbrowser.infocom;
|
|
|
|
|
|
|
|
import java.awt.Color;
|
|
|
|
import java.io.File;
|
|
|
|
import java.io.FileOutputStream;
|
|
|
|
import java.io.IOException;
|
2016-07-19 07:44:42 +00:00
|
|
|
import java.util.ArrayList;
|
2015-06-01 09:35:51 +00:00
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
import javax.swing.JOptionPane;
|
|
|
|
import javax.swing.tree.DefaultMutableTreeNode;
|
|
|
|
|
|
|
|
import com.bytezone.diskbrowser.applefile.AppleFileSource;
|
2016-02-24 12:39:09 +00:00
|
|
|
import com.bytezone.diskbrowser.disk.*;
|
2015-06-01 09:35:51 +00:00
|
|
|
import com.bytezone.diskbrowser.gui.DataSource;
|
2016-02-24 21:11:14 +00:00
|
|
|
import com.bytezone.diskbrowser.utilities.HexFormatter;
|
2015-06-01 09:35:51 +00:00
|
|
|
|
2018-10-01 09:34:38 +00:00
|
|
|
// https://mud.co.uk/richard/htflpism.htm
|
|
|
|
// https://inform-fiction.org/zmachine/standards/
|
2019-04-19 21:15:12 +00:00
|
|
|
// https://github.com/historicalsource?tab=repositories
|
2018-10-01 09:34:38 +00:00
|
|
|
|
2015-06-01 09:35:51 +00:00
|
|
|
public class InfocomDisk extends AbstractFormattedDisk
|
|
|
|
{
|
2016-02-25 07:45:24 +00:00
|
|
|
private static final int BLOCK_SIZE = 256;
|
|
|
|
private static final boolean TYPE_NODE = true;
|
|
|
|
private static final boolean TYPE_LEAF = false;
|
|
|
|
private byte[] data;
|
2016-07-19 01:24:36 +00:00
|
|
|
// private int version;
|
2016-02-29 01:54:44 +00:00
|
|
|
private final Header header;
|
2016-02-25 07:45:24 +00:00
|
|
|
|
|
|
|
Color green = new Color (0, 200, 0);
|
|
|
|
|
|
|
|
SectorType bootSector = new SectorType ("Boot code", Color.lightGray);
|
|
|
|
SectorType stringsSector = new SectorType ("Strings", Color.magenta);
|
|
|
|
SectorType objectsSector = new SectorType ("Objects", green);
|
|
|
|
SectorType dictionarySector = new SectorType ("Dictionary", Color.blue);
|
|
|
|
SectorType abbreviationsSector = new SectorType ("Abbreviations", Color.red);
|
|
|
|
SectorType codeSector = new SectorType ("Code", Color.orange);
|
|
|
|
SectorType headerSector = new SectorType ("Header", Color.cyan);
|
|
|
|
SectorType globalsSector = new SectorType ("Globals", Color.darkGray);
|
|
|
|
SectorType grammarSector = new SectorType ("Grammar", Color.gray);
|
|
|
|
|
|
|
|
public InfocomDisk (Disk disk)
|
|
|
|
{
|
|
|
|
super (disk);
|
|
|
|
|
2016-02-29 01:54:44 +00:00
|
|
|
setInfocomSectorTypes ();
|
2016-02-25 07:45:24 +00:00
|
|
|
|
2016-03-01 00:16:31 +00:00
|
|
|
data = disk.readSector (3, 0); // read first sector to get file size
|
|
|
|
data = getBuffer (getWord (26) * 2); // read entire file into data buffer
|
2016-02-25 07:45:24 +00:00
|
|
|
|
|
|
|
if (false)
|
|
|
|
createStoryFile ("Zork1.sf");
|
|
|
|
|
|
|
|
DefaultMutableTreeNode root = getCatalogTreeRoot ();
|
2016-07-19 07:44:42 +00:00
|
|
|
DefaultMutableTreeNode headerNode = null;
|
|
|
|
DefaultMutableTreeNode abbreviationsNode = null;
|
2016-02-25 07:45:24 +00:00
|
|
|
DefaultMutableTreeNode codeNode = null;
|
|
|
|
DefaultMutableTreeNode objectNode = null;
|
2016-07-19 07:44:42 +00:00
|
|
|
DefaultMutableTreeNode globalsNode = null;
|
|
|
|
DefaultMutableTreeNode grammarNode = null;
|
|
|
|
DefaultMutableTreeNode dictionaryNode = null;
|
|
|
|
DefaultMutableTreeNode stringsNode = null;
|
2016-02-25 07:45:24 +00:00
|
|
|
|
2016-07-20 05:22:33 +00:00
|
|
|
header = new Header ("Header", data, disk);
|
2016-02-25 07:45:24 +00:00
|
|
|
|
2016-07-19 07:44:42 +00:00
|
|
|
headerNode = addToTree (root, "Header", header, TYPE_LEAF);
|
|
|
|
DefaultAppleFileSource dafs = (DefaultAppleFileSource) headerNode.getUserObject ();
|
|
|
|
List<DiskAddress> blocks = new ArrayList<DiskAddress> ();
|
|
|
|
blocks.add (disk.getDiskAddress (3, 0));
|
|
|
|
dafs.setSectors (blocks);
|
|
|
|
|
|
|
|
abbreviationsNode =
|
|
|
|
addToTree (root, "Abbreviations", header.abbreviations, TYPE_LEAF);
|
2016-02-25 07:45:24 +00:00
|
|
|
|
|
|
|
objectNode = addToTree (root, "Objects", header.objectManager, TYPE_NODE);
|
|
|
|
header.objectManager.addNodes (objectNode, this);
|
2016-07-19 07:44:42 +00:00
|
|
|
|
|
|
|
globalsNode = addToTree (root, "Globals", header.globals, TYPE_LEAF);
|
|
|
|
grammarNode = addToTree (root, "Grammar", header.grammar, TYPE_LEAF);
|
|
|
|
dictionaryNode = addToTree (root, "Dictionary", header.dictionary, TYPE_LEAF);
|
|
|
|
|
2016-02-25 07:45:24 +00:00
|
|
|
codeNode = addToTree (root, "Code", header.codeManager, TYPE_NODE);
|
|
|
|
header.codeManager.addNodes (codeNode, this);
|
|
|
|
|
2016-07-19 07:44:42 +00:00
|
|
|
stringsNode = addToTree (root, "Strings", header.stringManager, TYPE_LEAF);
|
2016-02-25 07:45:24 +00:00
|
|
|
|
|
|
|
PropertyManager pm = new PropertyManager ("Properties", data, header);
|
|
|
|
pm.addNodes (addToTree (objectNode, "Properties", pm, TYPE_NODE), this);
|
|
|
|
|
|
|
|
AttributeManager am = new AttributeManager ("Attributes", data, header);
|
|
|
|
am.addNodes (addToTree (objectNode, "Attributes", am, TYPE_NODE), this);
|
|
|
|
|
|
|
|
sectorTypes[48] = headerSector;
|
|
|
|
|
2016-07-19 07:44:42 +00:00
|
|
|
setSectorTypes (header.abbreviationsTable, header.objectTable, abbreviationsSector,
|
2016-11-28 00:45:17 +00:00
|
|
|
abbreviationsNode);
|
2016-07-19 07:44:42 +00:00
|
|
|
setSectorTypes (header.objectTable, header.globalsOffset, objectsSector, objectNode);
|
|
|
|
setSectorTypes (header.globalsOffset, header.staticMemory, globalsSector,
|
2016-11-28 00:45:17 +00:00
|
|
|
globalsNode);
|
2016-07-19 07:44:42 +00:00
|
|
|
setSectorTypes (header.staticMemory, header.dictionaryOffset, grammarSector,
|
2016-11-28 00:45:17 +00:00
|
|
|
grammarNode);
|
2016-07-19 07:44:42 +00:00
|
|
|
setSectorTypes (header.dictionaryOffset, header.highMemory, dictionarySector,
|
2016-11-28 00:45:17 +00:00
|
|
|
dictionaryNode);
|
2016-07-19 07:44:42 +00:00
|
|
|
setSectorTypes (header.highMemory, header.stringPointer, codeSector, codeNode);
|
|
|
|
setSectorTypes (header.stringPointer, header.fileLength, stringsSector, stringsNode);
|
2016-02-25 07:45:24 +00:00
|
|
|
}
|
|
|
|
|
2016-02-29 01:54:44 +00:00
|
|
|
protected void setInfocomSectorTypes ()
|
2016-02-25 07:45:24 +00:00
|
|
|
{
|
|
|
|
sectorTypesList.add (bootSector);
|
|
|
|
sectorTypesList.add (headerSector);
|
|
|
|
sectorTypesList.add (abbreviationsSector);
|
|
|
|
sectorTypesList.add (objectsSector);
|
|
|
|
sectorTypesList.add (globalsSector);
|
|
|
|
sectorTypesList.add (grammarSector);
|
|
|
|
sectorTypesList.add (dictionarySector);
|
|
|
|
sectorTypesList.add (codeSector);
|
|
|
|
sectorTypesList.add (stringsSector);
|
|
|
|
|
|
|
|
for (int track = 0; track < 3; track++)
|
|
|
|
for (int sector = 0; sector < 16; sector++)
|
|
|
|
if (!disk.isSectorEmpty (track, sector))
|
|
|
|
sectorTypes[track * 16 + sector] = bootSector;
|
|
|
|
}
|
|
|
|
|
2016-07-19 07:44:42 +00:00
|
|
|
private void setSectorTypes (int sectorFrom, int sectorTo, SectorType type,
|
|
|
|
DefaultMutableTreeNode node)
|
2016-02-25 07:45:24 +00:00
|
|
|
{
|
2016-07-19 07:44:42 +00:00
|
|
|
DefaultAppleFileSource dafs = (DefaultAppleFileSource) node.getUserObject ();
|
|
|
|
List<DiskAddress> blocks = new ArrayList<DiskAddress> ();
|
|
|
|
|
2016-02-25 07:45:24 +00:00
|
|
|
int blockNo = sectorFrom / disk.getBlockSize () + 48;
|
|
|
|
int blockTo = sectorTo / disk.getBlockSize () + 48;
|
|
|
|
while (blockNo <= blockTo)
|
|
|
|
{
|
2016-07-19 07:44:42 +00:00
|
|
|
blocks.add (disk.getDiskAddress (blockNo));
|
2016-02-25 07:45:24 +00:00
|
|
|
if (!disk.isSectorEmpty (blockNo))
|
|
|
|
sectorTypes[blockNo] = type;
|
|
|
|
blockNo++;
|
|
|
|
}
|
2016-07-19 07:44:42 +00:00
|
|
|
dafs.setSectors (blocks);
|
2016-02-25 07:45:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private int getFileSize ()
|
|
|
|
{
|
|
|
|
byte[] buffer = null;
|
|
|
|
int startBlock = getWord (4) / 256 + 48;
|
|
|
|
int fileSize = 0;
|
|
|
|
for (DiskAddress da : disk)
|
|
|
|
{
|
|
|
|
if (da.getBlock () > startBlock && disk.isSectorEmpty (da))
|
|
|
|
{
|
2016-02-29 01:54:44 +00:00
|
|
|
System.out.println ("Empty : " + da);
|
2016-02-25 07:45:24 +00:00
|
|
|
buffer = disk.readSector (da.getBlock () - 1);
|
|
|
|
fileSize = (da.getBlock () - 48) * disk.getBlockSize ();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-03-23 23:37:59 +00:00
|
|
|
|
|
|
|
if (buffer != null)
|
|
|
|
{
|
|
|
|
int ptr = 255;
|
|
|
|
while (buffer[ptr--] == 0)
|
|
|
|
fileSize--;
|
|
|
|
}
|
2016-02-25 07:45:24 +00:00
|
|
|
return fileSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
private byte[] getBuffer (int fileSize)
|
|
|
|
{
|
|
|
|
if (fileSize == 0)
|
|
|
|
fileSize = getFileSize ();
|
|
|
|
data = new byte[fileSize];
|
|
|
|
|
|
|
|
for (int track = 3, ptr = 0; track < 35; track++)
|
|
|
|
for (int sector = 0; sector < 16; sector++, ptr += BLOCK_SIZE)
|
|
|
|
{
|
|
|
|
byte[] temp = disk.readSector (track, sector);
|
|
|
|
int spaceLeft = fileSize - ptr;
|
|
|
|
if (spaceLeft <= BLOCK_SIZE)
|
|
|
|
{
|
|
|
|
System.arraycopy (temp, 0, data, ptr, spaceLeft);
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
System.arraycopy (temp, 0, data, ptr, BLOCK_SIZE);
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
private DefaultMutableTreeNode addToTree (DefaultMutableTreeNode root, String title,
|
|
|
|
DataSource af, boolean allowsChildren)
|
|
|
|
{
|
2016-07-19 07:44:42 +00:00
|
|
|
DefaultAppleFileSource dafs = new DefaultAppleFileSource (title, af, this);
|
|
|
|
|
|
|
|
// dafs.setSectors (blocks);
|
|
|
|
DefaultMutableTreeNode node = new DefaultMutableTreeNode (dafs);
|
2016-02-25 07:45:24 +00:00
|
|
|
node.setAllowsChildren (allowsChildren);
|
|
|
|
root.add (node);
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public List<DiskAddress> getFileSectors (int fileNo)
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public AppleFileSource getCatalog ()
|
|
|
|
{
|
|
|
|
return new DefaultAppleFileSource (header.getText (), this);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static boolean isCorrectFormat (AppleDisk disk)
|
|
|
|
{
|
|
|
|
disk.setInterleave (2);
|
|
|
|
return checkFormat (disk);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static boolean checkFormat (AppleDisk disk)
|
|
|
|
{
|
|
|
|
byte[] buffer = disk.readSector (3, 0);
|
|
|
|
|
|
|
|
int version = buffer[0] & 0xFF;
|
|
|
|
int highMemory = HexFormatter.intValue (buffer[5], buffer[4]);
|
|
|
|
int programCounter = HexFormatter.intValue (buffer[7], buffer[6]);
|
|
|
|
int dictionary = HexFormatter.intValue (buffer[9], buffer[8]);
|
|
|
|
int objectTable = HexFormatter.intValue (buffer[11], buffer[10]);
|
|
|
|
int globals = HexFormatter.intValue (buffer[13], buffer[12]);
|
|
|
|
int staticMemory = HexFormatter.intValue (buffer[15], buffer[14]);
|
|
|
|
int abbreviationsTable = HexFormatter.intValue (buffer[25], buffer[24]);
|
|
|
|
int fileLength = HexFormatter.intValue (buffer[27], buffer[26]);
|
|
|
|
|
|
|
|
if (false)
|
|
|
|
{
|
|
|
|
System.out.printf ("Version %,6d%n", version);
|
|
|
|
System.out.printf ("Abbreviations %,6d%n", abbreviationsTable);
|
|
|
|
System.out.printf ("Objects %,6d%n", objectTable);
|
|
|
|
System.out.printf ("Globals %,6d%n", globals);
|
|
|
|
System.out.printf ("Static memory %,6d%n", staticMemory);
|
|
|
|
System.out.printf ("Dictionary %,6d%n", dictionary);
|
|
|
|
System.out.printf ("High memory %,6d%n", highMemory);
|
|
|
|
System.out.printf ("Program counter %,6d%n", programCounter);
|
|
|
|
System.out.printf ("File length %,6d%n", fileLength);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (abbreviationsTable >= objectTable)
|
|
|
|
return false;
|
|
|
|
if (objectTable >= globals)
|
|
|
|
return false;
|
|
|
|
if (globals >= staticMemory)
|
|
|
|
return false;
|
|
|
|
if (staticMemory >= dictionary)
|
|
|
|
return false;
|
|
|
|
if (dictionary >= highMemory)
|
|
|
|
return false;
|
|
|
|
// if (highMemory > programCounter)
|
|
|
|
// return false;
|
|
|
|
|
|
|
|
if (version < 2 || version > 3)
|
|
|
|
{
|
|
|
|
System.out.println ("Incorrect format : " + version);
|
2016-11-28 00:45:17 +00:00
|
|
|
JOptionPane.showMessageDialog (null,
|
|
|
|
"This appears to be an Infocom disk," + " but version " + version
|
|
|
|
+ " is not supported",
|
|
|
|
"Unknown disk format", JOptionPane.INFORMATION_MESSAGE);
|
2016-02-25 07:45:24 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private int getWord (int offset)
|
|
|
|
{
|
|
|
|
return (((data[offset] << 8) & 0xFF00) | ((data[offset + 1]) & 0xFF));
|
|
|
|
}
|
|
|
|
|
|
|
|
private void createStoryFile (String fileName)
|
|
|
|
{
|
|
|
|
File f = new File (fileName);
|
|
|
|
try
|
|
|
|
{
|
|
|
|
FileOutputStream fos = new FileOutputStream (f);
|
|
|
|
fos.write (data);
|
|
|
|
fos.close ();
|
|
|
|
}
|
|
|
|
catch (IOException e)
|
|
|
|
{
|
|
|
|
e.printStackTrace ();
|
|
|
|
}
|
|
|
|
}
|
2015-06-01 09:35:51 +00:00
|
|
|
}
|