infocom changes

This commit is contained in:
Denis Molony 2019-04-22 14:35:50 +10:00
parent 8764dd0db8
commit 76125f9a4e
16 changed files with 1051 additions and 870 deletions

View File

@ -20,7 +20,7 @@ class Abbreviations extends InfocomAbstractFile
dataPtr = header.getWord (header.abbreviationsTable) * 2; dataPtr = header.getWord (header.abbreviationsTable) * 2;
dataSize = header.abbreviationsTable - dataPtr; dataSize = header.abbreviationsTable - dataPtr;
tablePtr = header.abbreviationsTable; tablePtr = header.abbreviationsTable;
tableSize = header.objectTable - header.abbreviationsTable; tableSize = header.objectTableOffset - header.abbreviationsTable;
// prepare hex dump // prepare hex dump
hexBlocks.add (new HexBlock (dataPtr, dataSize, "Abbreviations data:")); hexBlocks.add (new HexBlock (dataPtr, dataSize, "Abbreviations data:"));
@ -29,21 +29,17 @@ class Abbreviations extends InfocomAbstractFile
private void populate () private void populate ()
{ {
System.out.println ("populating abbreviations");
list = new ArrayList<ZString> (); list = new ArrayList<ZString> ();
for (int i = header.abbreviationsTable; i < header.objectTable; i += 2) for (int i = header.abbreviationsTable; i < header.objectTableOffset; i += 2)
{ list.add (new ZString (header, header.getWord (i) * 2));
int j = header.getWord (i) * 2;
ZString zs = new ZString (buffer, j, header);
list.add (zs);
}
} }
public String getAbbreviation (int abbreviationNumber) public String getAbbreviation (int abbreviationNumber)
{ {
if (list == null) if (list == null)
populate (); populate ();
return list.get (abbreviationNumber).value; return list.get (abbreviationNumber).value;
} }

View File

@ -69,7 +69,7 @@ class AttributeManager extends AbstractFile
new StringBuilder ("Objects with attribute " + id + " set:\n\n"); new StringBuilder ("Objects with attribute " + id + " set:\n\n");
for (ZObject o : list) for (ZObject o : list)
{ {
text.append (String.format ("%3d %-28s%n", o.id, o.getName ())); text.append (String.format ("%3d %-28s%n", o.getId (), o.getName ()));
} }
if (text.length () > 0) if (text.length () > 0)
text.deleteCharAt (text.length () - 1); text.deleteCharAt (text.length () - 1);

View File

@ -36,7 +36,7 @@ class CodeManager extends AbstractFile
for (Routine routine : routines.values ()) for (Routine routine : routines.values ())
{ {
String name = String.format ("%3d %s (%04X)", ++count, routine.getName (), String name = String.format ("%3d %s (%04X)", ++count, routine.getName (),
routine.startPtr / 2); routine.startPtr / 2);
DefaultAppleFileSource dafs = new DefaultAppleFileSource (name, routine, disk); DefaultAppleFileSource dafs = new DefaultAppleFileSource (name, routine, disk);
dafs.setSectors (getSectors (routine, disk.getDisk ())); dafs.setSectors (getSectors (routine, disk.getDisk ()));
@ -114,10 +114,10 @@ class CodeManager extends AbstractFile
for (Routine r : routines.values ()) for (Routine r : routines.values ())
{ {
int gap = r.startPtr - nextAddress; int gap = r.startPtr - nextAddress;
text.append (String text.append (String.format (
.format ("%3d %05X %5d %3d %2d %3d %3d %4d %04X%n", "%3d %05X %5d %3d %2d %3d %3d %4d %04X%n", ++count,
++count, r.startPtr, r.length, r.instructions.size (), r.strings, r.startPtr, r.length, r.instructions.size (), r.strings, r.calledBy.size (),
r.calledBy.size (), r.calls.size (), gap, r.startPtr / 2)); r.calls.size (), gap, r.startPtr / 2));
nextAddress = r.startPtr + r.length; nextAddress = r.startPtr + r.length;
} }
@ -148,7 +148,7 @@ class CodeManager extends AbstractFile
Routine addRoutine (int address, int caller) Routine addRoutine (int address, int caller)
{ {
if (address == 0) // stack-based call if (address == 0) // stack-based call
return null; return null;
if (address > header.fileLength) if (address > header.fileLength)
return null; return null;
@ -163,7 +163,7 @@ class CodeManager extends AbstractFile
// try to create a new Routine // try to create a new Routine
Routine r = new Routine (address, header, caller); Routine r = new Routine (address, header, caller);
if (r.length == 0) // invalid routine if (r.length == 0) // invalid routine
return null; return null;
// recursively add all routines called by this one // recursively add all routines called by this one

View File

@ -15,15 +15,12 @@ class Dictionary extends AbstractFile
private final int totalSeparators; private final int totalSeparators;
private final int dictionaryPtr, dictionarySize; private final int dictionaryPtr, dictionarySize;
private final int entryLength; private final int entryLength;
// private final Header header;
// this could be a Google Multimap
Map<Integer, List<WordEntry>> synonymList = new TreeMap<Integer, List<WordEntry>> (); Map<Integer, List<WordEntry>> synonymList = new TreeMap<Integer, List<WordEntry>> ();
public Dictionary (Header header) public Dictionary (Header header)
{ {
super ("Dictionary", header.buffer); super ("Dictionary", header.buffer);
// this.header = header;
dictionaryPtr = header.dictionaryOffset; dictionaryPtr = header.dictionaryOffset;
dictionary = new TreeMap<Integer, ZString> (); dictionary = new TreeMap<Integer, ZString> ();
@ -38,7 +35,7 @@ class Dictionary extends AbstractFile
int count = 0; int count = 0;
for (int i = 0; i < totalEntries; i++) for (int i = 0; i < totalEntries; i++)
{ {
ZString string = new ZString (buffer, ptr, header); ZString string = new ZString (header, ptr);
dictionary.put (ptr, string); dictionary.put (ptr, string);
WordEntry wordEntry = new WordEntry (string, count++); WordEntry wordEntry = new WordEntry (string, count++);
@ -56,8 +53,8 @@ class Dictionary extends AbstractFile
{ {
int b1 = buffer[ptr + 5] & 0xFF; int b1 = buffer[ptr + 5] & 0xFF;
int property = (b1 >= 1 && b1 <= 31) ? b1 : buffer[ptr + 6] & 0xFF; int property = (b1 >= 1 && b1 <= 31) ? b1 : buffer[ptr + 6] & 0xFF;
if (header.propertyNames[property] == null if (header.getPropertyName (property) == null
|| header.propertyNames[property].length () > string.value.length ()) || header.getPropertyName (property).length () > string.value.length ())
header.propertyNames[property] = string.value; header.propertyNames[property] = string.value;
} }
ptr += entryLength; ptr += entryLength;
@ -68,6 +65,48 @@ class Dictionary extends AbstractFile
for (int i = 1; i < header.propertyNames.length; i++) for (int i = 1; i < header.propertyNames.length; i++)
if (header.propertyNames[i] == null) if (header.propertyNames[i] == null)
header.propertyNames[i] = i + ""; header.propertyNames[i] = i + "";
// testing (only works in Zork 1)
if (false)
{
if (header.propertyNames[4].equals ("4"))
header.propertyNames[4] = "PSEUDO";
if (header.propertyNames[5].equals ("5"))
header.propertyNames[5] = "GLOBAL";
if (header.propertyNames[6].equals ("6"))
header.propertyNames[6] = "VTYPE";
if (header.propertyNames[7].equals ("7"))
header.propertyNames[7] = "STRENGTH";
if (header.propertyNames[10].equals ("10"))
header.propertyNames[10] = "CAPACITY";
if (header.propertyNames[12].equals ("12"))
header.propertyNames[12] = "TVALU";
if (header.propertyNames[13].equals ("13"))
header.propertyNames[13] = "VALUE";
if (header.propertyNames[15].equals ("15"))
header.propertyNames[15] = "SIZE";
if (header.propertyNames[16].equals ("16"))
header.propertyNames[16] = "ADJ";
}
// 4 = PSEUDO (property 4)
// 5 = GLOBAL (property 5)
// 6 = VTYPE (property 6)
// 7 = STRENGTH (property 7)
// STR3 = TEXT (property 8)
// CODE2 = DESCFCN (property 9)
// 10 = CAPACITY (property 10)
// STR1 = LDESC (property 11)
// 12 = TVALUE (property 12) value in trophy case
// 13 = VALUE (property 13)
// STR2 = FDESC (property 14)
// 15 = SIZE (property 15)
// 16 = ADJ (property 16)
// CODE1 = ACTION (property 17)
// 18 = DICT (property 18)
// 19 = LAND (property 19)
// 20 = OUT (property 20)
// 21 = IN (property 21)
} }
public boolean containsWordAt (int address) public boolean containsWordAt (int address)
@ -168,7 +207,7 @@ class Dictionary extends AbstractFile
text.append ("\n"); text.append ("\n");
} }
if (wordEntry.value == 0x80) // nouns are all in one entry if (wordEntry.value == 0x80) // nouns are all in one entry
{ {
for (WordEntry we : list) for (WordEntry we : list)
text.append (we + "\n"); text.append (we + "\n");

View File

@ -1,13 +1,19 @@
package com.bytezone.diskbrowser.infocom; package com.bytezone.diskbrowser.infocom;
import java.util.ArrayList;
import java.util.List;
import com.bytezone.diskbrowser.infocom.Instruction.Operand;
class Globals extends InfocomAbstractFile class Globals extends InfocomAbstractFile
{ {
static final int TOTAL_GLOBALS = 240; private static final int TOTAL_GLOBALS = 240;
Header header; private final Header header;
int globalsPtr, globalsSize; private final int globalsPtr, globalsSize;
int arrayPtr, arraySize; private final int arrayPtr, arraySize;
private final List<List<Routine>> globalRoutines;
public Globals (Header header) Globals (Header header)
{ {
super ("Globals", header.buffer); super ("Globals", header.buffer);
this.header = header; this.header = header;
@ -20,21 +26,39 @@ class Globals extends InfocomAbstractFile
// add entries for AbstractFile.getHexDump () // add entries for AbstractFile.getHexDump ()
hexBlocks.add (new HexBlock (globalsPtr, globalsSize, "Globals:")); hexBlocks.add (new HexBlock (globalsPtr, globalsSize, "Globals:"));
hexBlocks.add (new HexBlock (arrayPtr, arraySize, "Arrays:")); hexBlocks.add (new HexBlock (arrayPtr, arraySize, "Arrays:"));
globalRoutines = new ArrayList<> (250);
for (int i = 0; i < 250; i++)
globalRoutines.add (new ArrayList<> ());
}
void addRoutine (Routine routine, Operand operand)
{
int global = operand.value - 15;
List<Routine> list = globalRoutines.get (global);
if (!list.contains (routine))
list.add (routine);
} }
@Override @Override
public String getText () public String getText ()
{ {
StringBuilder text = new StringBuilder (); StringBuilder text = new StringBuilder ("GLB Value Routines\n");
for (int i = 1; i <= TOTAL_GLOBALS; i++) for (int i = 1; i <= TOTAL_GLOBALS; i++)
{ {
int value = header.getWord (globalsPtr + i * 2); int value = header.getWord (globalsPtr + i * 2);
text.append (String.format ("G%03d %04X ", i, value)); text.append (String.format ("G%03d %04X %02d ", i, value,
globalRoutines.get (i).size ()));
int address = value * 2; int address = value * 2;
if (address >= header.stringPointer && address < header.fileLength) if (address >= header.stringPointer && address < header.fileLength)
text.append (header.stringManager.stringAt (address) + "\n"); text.append (header.stringManager.stringAt (address) + "\n");
else else
text.append (String.format ("%,6d%n", value)); {
for (Routine routine : globalRoutines.get (i))
text.append (String.format ("%05X ", routine.startPtr));
text.append ("\n");
}
// text.append (String.format ("%,6d%n", value));
} }
text.deleteCharAt (text.length () - 1); text.deleteCharAt (text.length () - 1);
return text.toString (); return text.toString ();

View File

@ -7,23 +7,24 @@ import com.bytezone.diskbrowser.utilities.HexFormatter;
class Grammar extends InfocomAbstractFile class Grammar extends InfocomAbstractFile
{ {
private static final int SENTENCE_LENGTH = 8; private static final int SENTENCE_LENGTH = 8;
Header header; private final Header header;
int indexPtr, indexSize; private final int indexPtr, indexSize;
int tablePtr, tableSize; private final int tablePtr, tableSize;
int actionPtr, actionSize; private final int actionPtr, actionSize;
int preActionPtr, preActionSize; private final int preActionPtr, preActionSize;
int prepositionPtr, prepositionSize; private final int prepositionPtr, prepositionSize;
int indexEntries; private final int indexEntries;
int totalPrepositions; private final int totalPrepositions;
int padding; private final int padding;
List<SentenceGroup> sentenceGroups = new ArrayList<SentenceGroup> (); private final List<SentenceGroup> sentenceGroups = new ArrayList<SentenceGroup> ();
Map<Integer, List<Sentence>> actionList = new TreeMap<Integer, List<Sentence>> (); private final Map<Integer, List<Sentence>> actionList =
new TreeMap<Integer, List<Sentence>> ();
List<Integer> actionRoutines = new ArrayList<Integer> (); private final List<Integer> actionRoutines = new ArrayList<Integer> ();
List<Integer> preActionRoutines = new ArrayList<Integer> (); private final List<Integer> preActionRoutines = new ArrayList<Integer> ();
public Grammar (String name, byte[] buffer, Header header) Grammar (String name, byte[] buffer, Header header)
{ {
super (name, buffer); super (name, buffer);
this.header = header; this.header = header;
@ -173,8 +174,8 @@ class Grammar extends InfocomAbstractFile
text.append (line); text.append (line);
} }
text.append ("\n" + actionRoutines.size () text.append (
+ " Action routines\n===================\n\n"); "\n" + actionRoutines.size () + " Action routines\n===================\n\n");
// add sentences in action routine sequence // add sentences in action routine sequence
for (Integer routine : actionRoutines) for (Integer routine : actionRoutines)

View File

@ -9,12 +9,11 @@ class Header extends InfocomAbstractFile
final String[] propertyNames = new String[32]; final String[] propertyNames = new String[32];
private final File file; private final File file;
// private final Disk disk;
int version; int version;
int highMemory; int highMemory;
int programCounter; int programCounter;
int dictionaryOffset; int dictionaryOffset;
int objectTable; int objectTableOffset;
int globalsOffset; int globalsOffset;
int staticMemory; int staticMemory;
int abbreviationsTable; int abbreviationsTable;
@ -23,29 +22,37 @@ class Header extends InfocomAbstractFile
int stringPointer; int stringPointer;
final Abbreviations abbreviations; final Abbreviations abbreviations;
final Dictionary dictionary;
final ObjectManager objectManager; final ObjectManager objectManager;
final StringManager stringManager;
final CodeManager codeManager;
final Globals globals; final Globals globals;
final Grammar grammar; final Grammar grammar;
final Dictionary dictionary;
final CodeManager codeManager;
final StringManager stringManager;
public Header (String name, byte[] buffer, Disk disk) public Header (String name, byte[] buffer, Disk disk)
{ {
super (name, buffer); super (name, buffer);
// this.disk = disk;
this.file = disk.getFile (); this.file = disk.getFile ();
version = getByte (0); version = getByte (00);
highMemory = getWord (4); highMemory = getWord (0x04);
programCounter = getWord (6); programCounter = getWord (0x06);
dictionaryOffset = getWord (8);
objectTable = getWord (10); dictionaryOffset = getWord (0x08);
globalsOffset = getWord (12); objectTableOffset = getWord (0x0A);
staticMemory = getWord (14); globalsOffset = getWord (0x0C);
abbreviationsTable = getWord (24); staticMemory = getWord (0x0E);
checksum = getWord (28); abbreviationsTable = getWord (0x18);
fileLength = getWord (26) * 2;
fileLength = getWord (0x1A) * 2; // 2 for versions 1-3
checksum = getWord (0x1C);
int interpreterNumber = getByte (0x1E);
int interpreterVersion = getByte (0x1F);
int revision = getWord (0x30);
System.out.printf ("Version : %d%n", version);
System.out.printf ("Interpreter: %d.%d%n", interpreterNumber, interpreterVersion);
System.out.printf ("Revision : %d%n", revision);
if (fileLength == 0) if (fileLength == 0)
fileLength = buffer.length; fileLength = buffer.length;
@ -75,6 +82,11 @@ class Header extends InfocomAbstractFile
hexBlocks.add (new HexBlock (0, 64, "Header data:")); hexBlocks.add (new HexBlock (0, 64, "Header data:"));
} }
String getPropertyName (int id)
{
return propertyNames[id];
}
public String getAbbreviation (int index) public String getAbbreviation (int index)
{ {
return abbreviations.getAbbreviation (index); return abbreviations.getAbbreviation (index);
@ -100,8 +112,8 @@ class Header extends InfocomAbstractFile
text.append ("\nDynamic memory:\n"); text.append ("\nDynamic memory:\n");
text.append (String.format (" Abbreviation table %04X %,6d%n", text.append (String.format (" Abbreviation table %04X %,6d%n",
abbreviationsTable, abbreviationsTable)); abbreviationsTable, abbreviationsTable));
text.append (String.format (" Objects table %04X %,6d%n", objectTable, text.append (String.format (" Objects table %04X %,6d%n",
objectTable)); objectTableOffset, objectTableOffset));
text.append (String.format (" Global variables %04X %,6d%n", globalsOffset, text.append (String.format (" Global variables %04X %,6d%n", globalsOffset,
globalsOffset)); globalsOffset));
@ -125,11 +137,16 @@ class Header extends InfocomAbstractFile
text.append (String.format ("Total strings %d%n", text.append (String.format ("Total strings %d%n",
stringManager.strings.size ())); stringManager.strings.size ()));
text.append (String.format ("Total objects %d%n", text.append (String.format ("Total objects %d%n",
objectManager.list.size ())); objectManager.getObjects ().size ()));
return text.toString (); return text.toString ();
} }
ZObject getObject (int index)
{
return objectManager.getObject (index);
}
int getByte (int offset) int getByte (int offset)
{ {
return buffer[offset] & 0xFF; return buffer[offset] & 0xFF;

View File

@ -86,16 +86,16 @@ public class InfocomDisk extends AbstractFormattedDisk
stringsNode = addToTree (root, "Strings", header.stringManager, TYPE_LEAF); stringsNode = addToTree (root, "Strings", header.stringManager, TYPE_LEAF);
PropertyManager pm = new PropertyManager ("Properties", data, header); PropertyManager pm = new PropertyManager ("Properties", data, header);
pm.addNodes (addToTree (objectNode, "Properties", pm, TYPE_NODE), this); pm.addNodes (addToTree (root, "Properties", pm, TYPE_NODE), this);
AttributeManager am = new AttributeManager ("Attributes", data, header); AttributeManager am = new AttributeManager ("Attributes", data, header);
am.addNodes (addToTree (objectNode, "Attributes", am, TYPE_NODE), this); am.addNodes (addToTree (root, "Attributes", am, TYPE_NODE), this);
sectorTypes[48] = headerSector; sectorTypes[48] = headerSector;
setSectorTypes (header.abbreviationsTable, header.objectTable, abbreviationsSector, setSectorTypes (header.abbreviationsTable, header.objectTableOffset, abbreviationsSector,
abbreviationsNode); abbreviationsNode);
setSectorTypes (header.objectTable, header.globalsOffset, objectsSector, objectNode); setSectorTypes (header.objectTableOffset, header.globalsOffset, objectsSector, objectNode);
setSectorTypes (header.globalsOffset, header.staticMemory, globalsSector, setSectorTypes (header.globalsOffset, header.staticMemory, globalsSector,
globalsNode); globalsNode);
setSectorTypes (header.staticMemory, header.dictionaryOffset, grammarSector, setSectorTypes (header.staticMemory, header.dictionaryOffset, grammarSector,

View File

@ -7,487 +7,508 @@ import com.bytezone.diskbrowser.utilities.HexFormatter;
class Instruction class Instruction
{ {
Opcode opcode; Opcode opcode;
int startPtr; int startPtr;
byte[] buffer; byte[] buffer;
// List<ZString> abbreviations; // List<ZString> abbreviations;
Header header; Header header;
static final String[] name2OP = enum OperandType
{ "*bad*", "je", "jl", "jg", "dec_chk", "inc_chk", "jin", "test", "or", "and", "test_attr", {
"set_attr", "clear_attr", "store", "insert_obj", "loadw", "loadb", "get_prop", VAR_STACK, VAR_LOCAL, VAR_GLOBAL, BYTE, WORD, ARG_BRANCH, ARG_STRING
"get_prop_addr", "get_next_prop", "add", "sub", "mul", "div", "mod", "call_2s", }
"call_2n", "set_colour", "throw", "*bad*", "*bad*", "*bad*" };
static final String[] name1OP =
{ "jz", "get_sibling", "get_child", "get_parent", "get_prop_len", "inc", "dec",
"print_addr", "call_ls", "remove_obj", "print_obj", "ret", "jump", "print_paddr", "load",
"not" };
static final String[] name0OP =
{ "rtrue", "rfalse", "print", "print_ret", "nop", "save", "restore", "restart",
"ret_popped", "pop", "quit", "new_line", "show_status", "verify", "", "piracy" };
static final String[] nameVAR =
{ "call", "storew", "storeb", "put_prop", "sread", "print_char", "print_num", "random",
"push", "pull", "split_window", "set_window", "call_vs2", "erase_window", "erase_line",
"set_cursor", "get_cursor", "set_text_style", "buffer_mode", "output_stream",
"input_stream", "sound_effect", "read_char", "scan_table", "not", "call_vn", "call_vn2",
"tokenise", "encode_text", "copy_table", "print_table", "check_arg" };
public Instruction (byte[] buffer, int ptr, Header header) static final String[] name2OP =
{ { "*bad*", "je", "jl", "jg", "dec_chk", "inc_chk", "jin", "test", "or", "and",
this.buffer = buffer; "test_attr", "set_attr", "clear_attr", "store", "insert_obj", "loadw", "loadb",
this.startPtr = ptr; "get_prop", "get_prop_addr", "get_next_prop", "add", "sub", "mul", "div", "mod",
this.header = header; "call_2s", "call_2n", "set_colour", "throw", "*bad*", "*bad*", "*bad*" };
byte b1 = buffer[ptr]; static final String[] name1OP =
{ "jz", "get_sibling", "get_child", "get_parent", "get_prop_len", "inc", "dec",
"print_addr", "call_ls", "remove_obj", "print_obj", "ret", "jump", "print_paddr",
"load", "not" };
static final String[] name0OP =
{ "rtrue", "rfalse", "print", "print_ret", "nop", "save", "restore", "restart",
"ret_popped", "pop", "quit", "new_line", "show_status", "verify", "", "piracy" };
static final String[] nameVAR =
{ "call", "storew", "storeb", "put_prop", "sread", "print_char", "print_num",
"random", "push", "pull", "split_window", "set_window", "call_vs2",
"erase_window", "erase_line", "set_cursor", "get_cursor", "set_text_style",
"buffer_mode", "output_stream", "input_stream", "sound_effect", "read_char",
"scan_table", "not", "call_vn", "call_vn2", "tokenise", "encode_text",
"copy_table", "print_table", "check_arg" };
// long public Instruction (byte[] buffer, int ptr, Header header)
if ((b1 & 0x80) == 0) {
opcode = new Opcode2OPLong (buffer, ptr); this.buffer = buffer;
// short this.startPtr = ptr;
else if ((b1 & 0x40) == 0) this.header = header;
{ byte b1 = buffer[ptr];
if ((b1 & 0x30) == 0x30)
opcode = new Opcode0OP (buffer, ptr);
else
opcode = new Opcode1OP (buffer, ptr);
}
// variable
else
{
if ((b1 & 0x20) == 0)
opcode = new Opcode2OPVar (buffer, ptr);
else
opcode = new OpcodeVar (buffer, ptr);
}
}
public int length () // long
{ if ((b1 & 0x80) == 0)
return opcode.length (); opcode = new Opcode2OPLong (buffer, ptr);
} // short
else if ((b1 & 0x40) == 0)
{
if ((b1 & 0x30) == 0x30)
opcode = new Opcode0OP (buffer, ptr);
else
opcode = new Opcode1OP (buffer, ptr);
}
// variable
else
{
if ((b1 & 0x20) == 0)
opcode = new Opcode2OPVar (buffer, ptr);
else
opcode = new OpcodeVar (buffer, ptr);
}
}
public boolean isReturn () public int length ()
{ {
return opcode.isReturn; return opcode.length ();
} }
public boolean isPrint () public boolean isReturn ()
{ {
return opcode.string != null; return opcode.isReturn;
} }
public boolean isCall () public boolean isPrint ()
{ {
return opcode.isCall; return opcode.string != null;
} }
public boolean isJump () public boolean isCall ()
{ {
// could use jumpTarget != 0 return opcode.isCall;
return (opcode instanceof Opcode1OP && opcode.opcodeNumber == 12); }
}
public boolean isBranch () public boolean isJump ()
{ {
return opcode.branch != null; // could use jumpTarget != 0
} return (opcode instanceof Opcode1OP && opcode.opcodeNumber == 12);
}
public boolean isStore () public boolean isBranch ()
{ {
return opcode.store != null; return opcode.branch != null;
} }
public int target () public boolean isStore ()
{ {
return isBranch () ? opcode.branch.target : 0; return opcode.store != null;
} }
@Override public int target ()
public String toString () {
{ return isBranch () ? opcode.branch.target : 0;
int max = opcode.length (); }
String extra = "";
if (max > 9)
{
max = 9;
extra = "..";
}
String hex = HexFormatter.getHexString (buffer, startPtr, max);
return String.format ("%-26s%2s %s", hex, extra, opcode.toString ());
}
abstract class Opcode @Override
{ public String toString ()
int opcodeNumber; {
int opcodeLength; int max = opcode.length ();
List<Operand> operands; String extra = "";
int totalOperandLength; if (max > 9)
ArgumentBranch branch; {
ArgumentString string; max = 9;
OperandVariable store; extra = "..";
boolean isReturn, isCall, isExit; }
int jumpTarget; String hex = HexFormatter.getHexString (buffer, startPtr, max);
int callTarget; return String.format ("%-26s%2s %s", hex, extra, opcode.toString ());
}
public Opcode () abstract class Opcode
{ {
operands = new ArrayList<Operand> (); int opcodeNumber;
} int opcodeLength;
List<Operand> operands;
int totalOperandLength;
ArgumentBranch branch;
ArgumentString string;
OperandVariable store;
boolean isReturn, isCall, isExit;
int jumpTarget;
int callTarget;
@Override public Opcode ()
public String toString () {
{ operands = new ArrayList<Operand> ();
StringBuilder text = new StringBuilder (); }
if (false)
text.append (HexFormatter.formatNoHeader (buffer, startPtr, length ()) + "\n");
text.append (String.format ("%05X : %-12s", startPtr, opcodeName ()));
if (jumpTarget != 0)
text.append (String.format (" L:%05X", jumpTarget));
else if (isCall)
{
text.append (String.format (" R:%05X (", callTarget));
int count = 0;
for (Operand op : operands)
if (count++ > 0)
text.append (op + ", ");
if (operands.size () > 1)
text.delete (text.length () - 2, text.length ());
text.append (") --> " + store);
}
else
{
for (Operand op : operands)
text.append (" " + op);
if (branch != null)
text.append (branch);
if (store != null)
text.append (" --> " + store);
if (string != null)
text.append (" \"" + string + "\"");
}
return text.toString ();
}
public int length () @Override
{ public String toString ()
int length = totalOperandLength + opcodeLength; {
if (branch != null) StringBuilder text = new StringBuilder ();
length += branch.length; if (false)
if (store != null) text.append (HexFormatter.formatNoHeader (buffer, startPtr, length ()) + "\n");
length += store.length; text.append (String.format ("%05X : %-12s", startPtr, opcodeName ()));
if (string != null) if (jumpTarget != 0)
length += string.length; text.append (String.format (" L:%05X", jumpTarget));
return length; else if (isCall)
} {
text.append (String.format (" R:%05X (", callTarget));
int count = 0;
for (Operand op : operands)
if (count++ > 0)
text.append (op + ", ");
if (operands.size () > 1)
text.delete (text.length () - 2, text.length ());
text.append (") --> " + store);
}
else
{
for (Operand op : operands)
text.append (" " + op);
if (branch != null)
text.append (branch);
if (store != null)
text.append (" --> " + store);
if (string != null)
text.append (" \"" + string + "\"");
}
return text.toString ();
}
public abstract String opcodeName (); public int length ()
{
int length = totalOperandLength + opcodeLength;
if (branch != null)
length += branch.length;
if (store != null)
length += store.length;
if (string != null)
length += string.length;
return length;
}
private void addOperand (Operand operand) public abstract String opcodeName ();
{
operands.add (operand);
totalOperandLength += operand.length;
}
protected void addOperand (byte[] buffer, int ptr, boolean bit1, boolean bit2) private void addOperand (Operand operand)
{ {
int offset = ptr + totalOperandLength; operands.add (operand);
if (bit1) totalOperandLength += operand.length;
{ }
if (!bit2)
addOperand (new OperandVariable (buffer[offset])); // %10
}
else if (bit2)
addOperand (new OperandByte (buffer[offset])); // %01
else
addOperand (new OperandWord (header.getWord (offset))); // %00
}
protected void addOperand (byte[] buffer, int ptr, boolean bit) protected void addOperand (byte[] buffer, int ptr, boolean bit1, boolean bit2)
{ {
int address = ptr + totalOperandLength; int offset = ptr + totalOperandLength;
if (address >= buffer.length) if (bit1)
{ {
System.out.println ("Illegal byte address : " + address); if (!bit2)
return; addOperand (new OperandVariable (buffer[offset])); // %10
} }
if (bit) else if (bit2)
addOperand (new OperandVariable (buffer[address])); addOperand (new OperandByte (buffer[offset])); // %01
else else
addOperand (new OperandByte (buffer[address])); addOperand (new OperandWord (header.getWord (offset))); // %00
} }
protected void setVariableOperands (int ptr) protected void addOperand (byte[] buffer, int ptr, boolean bit)
{ {
int value = buffer[ptr + 1] & 0xFF; int address = ptr + totalOperandLength;
for (int i = 0; i < 4; i++) if (address >= buffer.length)
{ {
boolean bit1 = ((value & 0x80) == 0x80); System.out.println ("Illegal byte address : " + address);
boolean bit2 = ((value & 0x40) == 0x40); return;
if (bit1 && bit2) }
break; if (bit)
addOperand (buffer, ptr + 2, bit1, bit2); addOperand (new OperandVariable (buffer[address]));
value <<= 2; else
} addOperand (new OperandByte (buffer[address]));
} }
protected void setStore (byte[] buffer) protected void setVariableOperands (int ptr)
{ {
store = new OperandVariable (buffer[startPtr + totalOperandLength + opcodeLength]); int value = buffer[ptr + 1] & 0xFF;
} for (int i = 0; i < 4; i++)
{
boolean bit1 = ((value & 0x80) == 0x80);
boolean bit2 = ((value & 0x40) == 0x40);
if (bit1 && bit2)
break;
addOperand (buffer, ptr + 2, bit1, bit2);
value <<= 2;
}
}
protected void setBranch (byte[] buffer) protected void setStore (byte[] buffer)
{ {
int offset = startPtr + totalOperandLength + (store == null ? 0 : 1) + opcodeLength; store = new OperandVariable (buffer[startPtr + totalOperandLength + opcodeLength]);
if ((buffer[offset] & 0x40) == 0x40) }
branch = new ArgumentBranch (buffer[offset], offset);
else
branch = new ArgumentBranch (header.getWord (offset), offset);
}
protected void setZString (byte[] buffer) protected void setBranch (byte[] buffer)
{ {
int offset = startPtr + totalOperandLength + opcodeLength; int offset = startPtr + totalOperandLength + (store == null ? 0 : 1) + opcodeLength;
string = new ArgumentString (buffer, offset); if ((buffer[offset] & 0x40) == 0x40)
} branch = new ArgumentBranch (buffer[offset], offset);
} else
branch = new ArgumentBranch (header.getWord (offset), offset);
}
class Opcode0OP extends Opcode protected void setZString (byte[] buffer)
{ {
public Opcode0OP (byte[] buffer, int ptr) int offset = startPtr + totalOperandLength + opcodeLength;
{ string = new ArgumentString (buffer, offset);
opcodeNumber = buffer[ptr] & 0x0F; }
opcodeLength = 1; }
if (opcodeNumber == 5 || opcodeNumber == 6 || opcodeNumber == 13) class Opcode0OP extends Opcode
setBranch (buffer); {
public Opcode0OP (byte[] buffer, int ptr)
{
opcodeNumber = buffer[ptr] & 0x0F;
opcodeLength = 1;
if (opcodeNumber == 0 || opcodeNumber == 1 || opcodeNumber == 3 || opcodeNumber == 8) if (opcodeNumber == 5 || opcodeNumber == 6 || opcodeNumber == 13)
isReturn = true; setBranch (buffer);
if (opcodeNumber == 2 || opcodeNumber == 3) if (opcodeNumber == 0 || opcodeNumber == 1 || opcodeNumber == 3
setZString (buffer); || opcodeNumber == 8)
isReturn = true;
if (opcodeNumber == 7 || opcodeNumber == 10) if (opcodeNumber == 2 || opcodeNumber == 3)
isExit = true; setZString (buffer);
}
@Override if (opcodeNumber == 7 || opcodeNumber == 10)
public String opcodeName () isExit = true;
{ }
return name0OP[opcodeNumber];
}
}
class Opcode1OP extends Opcode @Override
{ public String opcodeName ()
public Opcode1OP (byte[] buffer, int ptr) {
{ return name0OP[opcodeNumber];
opcodeNumber = buffer[ptr] & 0x0F; }
opcodeLength = 1; }
boolean bit1 = ((buffer[ptr] & 0x20) == 0x20); class Opcode1OP extends Opcode
boolean bit2 = ((buffer[ptr] & 0x10) == 0x10); {
addOperand (buffer, ptr + 1, bit1, bit2); public Opcode1OP (byte[] buffer, int ptr)
{
opcodeNumber = buffer[ptr] & 0x0F;
opcodeLength = 1;
if ((opcodeNumber >= 1 && opcodeNumber <= 4) || opcodeNumber == 8 || opcodeNumber == 14 boolean bit1 = ((buffer[ptr] & 0x20) == 0x20);
|| opcodeNumber == 15) boolean bit2 = ((buffer[ptr] & 0x10) == 0x10);
setStore (buffer); addOperand (buffer, ptr + 1, bit1, bit2);
if (opcodeNumber <= 2)
setBranch (buffer);
if (opcodeNumber == 12)
jumpTarget = (short) operands.get (0).value + startPtr - 2 + length ();
if (opcodeNumber == 11)
isReturn = true;
}
@Override if ((opcodeNumber >= 1 && opcodeNumber <= 4) || opcodeNumber == 8
public String opcodeName () || opcodeNumber == 14 || opcodeNumber == 15)
{ setStore (buffer);
return name1OP[opcodeNumber]; if (opcodeNumber <= 2)
} setBranch (buffer);
} if (opcodeNumber == 12)
jumpTarget = (short) operands.get (0).value + startPtr - 2 + length ();
if (opcodeNumber == 11)
isReturn = true;
}
abstract class Opcode2OP extends Opcode @Override
{ public String opcodeName ()
public Opcode2OP () {
{ return name1OP[opcodeNumber];
opcodeLength = 1; }
} }
public void setArguments (byte[] buffer) abstract class Opcode2OP extends Opcode
{ {
if ((opcodeNumber >= 1 && opcodeNumber <= 7) || opcodeNumber == 10) public Opcode2OP ()
setBranch (buffer); {
else if ((opcodeNumber >= 15 && opcodeNumber <= 25) || opcodeNumber == 8 || opcodeNumber == 9) opcodeLength = 1;
setStore (buffer); }
}
@Override public void setArguments (byte[] buffer)
public String opcodeName () {
{ if ((opcodeNumber >= 1 && opcodeNumber <= 7) || opcodeNumber == 10)
return name2OP[opcodeNumber]; setBranch (buffer);
} else if ((opcodeNumber >= 15 && opcodeNumber <= 25) || opcodeNumber == 8
} || opcodeNumber == 9)
setStore (buffer);
}
class Opcode2OPLong extends Opcode2OP @Override
{ public String opcodeName ()
public Opcode2OPLong (byte[] buffer, int ptr) {
{ return name2OP[opcodeNumber];
opcodeNumber = buffer[ptr] & 0x1F; }
boolean bit1 = ((buffer[ptr] & 0x40) == 0x40); }
boolean bit2 = ((buffer[ptr] & 0x20) == 0x20);
addOperand (buffer, ptr + 1, bit1);
addOperand (buffer, ptr + 1, bit2);
setArguments (buffer); class Opcode2OPLong extends Opcode2OP
} {
} public Opcode2OPLong (byte[] buffer, int ptr)
{
opcodeNumber = buffer[ptr] & 0x1F;
boolean bit1 = ((buffer[ptr] & 0x40) == 0x40);
boolean bit2 = ((buffer[ptr] & 0x20) == 0x20);
addOperand (buffer, ptr + 1, bit1);
addOperand (buffer, ptr + 1, bit2);
class Opcode2OPVar extends Opcode2OP setArguments (buffer);
{ }
public Opcode2OPVar (byte[] buffer, int ptr) }
{
opcodeNumber = buffer[ptr] & 0x1F;
opcodeLength = 2;
setVariableOperands (ptr);
setArguments (buffer);
}
}
class OpcodeVar extends Opcode class Opcode2OPVar extends Opcode2OP
{ {
public OpcodeVar (byte[] buffer, int ptr) public Opcode2OPVar (byte[] buffer, int ptr)
{ {
opcodeNumber = buffer[ptr] & 0x1F; opcodeNumber = buffer[ptr] & 0x1F;
opcodeLength = 2; opcodeLength = 2;
setVariableOperands (ptr); setVariableOperands (ptr);
setArguments (buffer);
}
}
if (opcodeNumber == 0 || opcodeNumber == 7) class OpcodeVar extends Opcode
setStore (buffer); {
if (opcodeNumber == 0) public OpcodeVar (byte[] buffer, int ptr)
{ {
isCall = true; opcodeNumber = buffer[ptr] & 0x1F;
callTarget = operands.get (0).value * 2; opcodeLength = 2;
} setVariableOperands (ptr);
}
@Override if (opcodeNumber == 0 || opcodeNumber == 7)
public String opcodeName () setStore (buffer);
{ if (opcodeNumber == 0)
return nameVAR[opcodeNumber]; {
} isCall = true;
} callTarget = operands.get (0).value * 2;
}
}
abstract class Operand @Override
{ public String opcodeName ()
int length; {
int value; return nameVAR[opcodeNumber];
} }
}
class OperandWord extends Operand abstract class Operand
{ {
public OperandWord (int value) int length;
{ int value;
this.value = value; OperandType operandType;
length = 2; }
}
@Override class OperandWord extends Operand
public String toString () {
{ public OperandWord (int value)
return String.format ("#%05d", value); {
} this.value = value;
} length = 2;
operandType = OperandType.WORD;
}
class OperandByte extends Operand @Override
{ public String toString ()
public OperandByte (byte value) {
{ return String.format ("#%05d", value);
this.value = value & 0xFF; }
length = 1; }
}
@Override class OperandByte extends Operand
public String toString () {
{ public OperandByte (byte value)
return String.format ("#%03d", value); {
} this.value = value & 0xFF;
} length = 1;
operandType = OperandType.BYTE;
}
class OperandVariable extends Operand @Override
{ public String toString ()
public OperandVariable (byte value) {
{ return String.format ("#%03d", value);
this.value = value & 0xFF; }
length = 1; }
}
@Override class OperandVariable extends Operand
public String toString () {
{ public OperandVariable (byte value)
if (value == 0) {
return ("ToS"); this.value = value & 0xFF;
if (value <= 15) length = 1;
return (String.format ("L%02d", value));
return String.format ("G%03d", (value - 15));
}
}
class ArgumentBranch extends Operand if (value == 0)
{ operandType = OperandType.VAR_STACK;
int target; else if (value <= 15)
boolean branchOnTrue; operandType = OperandType.VAR_LOCAL;
else
operandType = OperandType.VAR_GLOBAL;
}
public ArgumentBranch (byte value, int offset) @Override
{ public String toString ()
branchOnTrue = (value & 0x80) == 0x80; {
int val = value & 0x3F; // unsigned if (operandType == OperandType.VAR_STACK)
if (val <= 1) return ("ToS");
target = val; if (operandType == OperandType.VAR_LOCAL)
else return (String.format ("L%02d", value));
target = val + offset - 1; return String.format ("G%03d", (value - 15));
length = 1; }
} }
public ArgumentBranch (int value, int offset) class ArgumentBranch extends Operand
{ {
branchOnTrue = (value & 0x8000) == 0x8000; int target;
int val = value & 0x3FFF; // signed boolean branchOnTrue;
if (val > 8191)
val -= 16384;
target = val + offset;
length = 2;
}
@Override public ArgumentBranch (byte value, int offset)
public String toString () {
{ branchOnTrue = (value & 0x80) == 0x80;
StringBuilder text = new StringBuilder (); int val = value & 0x3F; // unsigned
text.append (" [" + (branchOnTrue ? "true" : "false") + "] "); if (val <= 1)
if (target == 0 || target == 1) target = val;
text.append (target == 0 ? "RFALSE" : "RTRUE"); else
else target = val + offset - 1;
text.append (String.format ("%05X", target)); length = 1;
return text.toString (); operandType = OperandType.ARG_BRANCH;
} }
}
class ArgumentString extends Operand public ArgumentBranch (int value, int offset)
{ {
ZString text; branchOnTrue = (value & 0x8000) == 0x8000;
int startPtr; int val = value & 0x3FFF; // signed
byte[] buffer; if (val > 8191)
val -= 16384;
target = val + offset;
length = 2;
operandType = OperandType.ARG_BRANCH;
}
public ArgumentString (byte[] buffer, int offset) @Override
{ public String toString ()
this.buffer = buffer; {
text = new ZString (buffer, offset, header); StringBuilder text = new StringBuilder ();
length = text.length; text.append (" [" + (branchOnTrue ? "true" : "false") + "] ");
} if (target == 0 || target == 1)
text.append (target == 0 ? "RFALSE" : "RTRUE");
else
text.append (String.format ("%05X", target));
return text.toString ();
}
}
@Override class ArgumentString extends Operand
public String toString () {
{ ZString text;
return text.value; int startPtr;
} byte[] buffer;
}
public ArgumentString (byte[] buffer, int offset)
{
this.buffer = buffer;
text = new ZString (header, offset);
length = text.length;
operandType = OperandType.ARG_STRING;
}
@Override
public String toString ()
{
return text.value;
}
}
} }

View File

@ -8,210 +8,212 @@ import com.bytezone.diskbrowser.infocom.ZObject.Property;
class ObjectAnalyser class ObjectAnalyser
{ {
Header header; Header header;
ObjectManager parent; ObjectManager parent;
List<Statistics> list = new ArrayList<Statistics> (); List<Statistics> list = new ArrayList<Statistics> ();
List<Integer> routines = new ArrayList<Integer> (); List<Integer> routines = new ArrayList<Integer> ();
public ObjectAnalyser (Header header, ObjectManager parent) public ObjectAnalyser (Header header, ObjectManager parent)
{ {
this.header = header; this.header = header;
this.parent = parent; this.parent = parent;
// assign the DICT property for each object // assign the DICT property for each object
setDictionary (); setDictionary ();
// find the point where code ends and strings begin // find the point where code ends and strings begin
setStringPointer (); setStringPointer ();
// add routines called from object properties (requires stringPointer) // add routines called from object properties (requires stringPointer)
createPropertyLinks (); createPropertyLinks ();
// assumes that all properties with exactly three bytes are routine addresses // assumes that all properties with exactly three bytes are routine addresses
// checkThreeByteProperties (); // checkThreeByteProperties ();
} }
public void setStringPointer () public void setStringPointer ()
{ {
PropertyTester pt = new PropertyTester (parent.list); PropertyTester pt = new PropertyTester (parent.getObjects ());
pt.addTest (new LengthTwoCondition ()); pt.addTest (new LengthTwoCondition ());
HighMemoryCondition hmc = new HighMemoryCondition (); HighMemoryCondition hmc = new HighMemoryCondition ();
pt.addTest (hmc); pt.addTest (hmc);
pt.doTests (); pt.doTests ();
System.out.println ("\nSetting the string pointer\n"); // System.out.println ("\nSetting the string pointer\n");
for (Integer propertyNo : pt) for (Integer propertyNo : pt)
// list of all properties that passed all tests // list of all properties that passed all tests
list.add (hmc.statistics[propertyNo]); list.add (hmc.statistics[propertyNo]);
Collections.sort (list); Collections.sort (list);
// Calculate lowest string pointer // Calculate lowest string pointer
int lo = list.get (0).lo; int lo = list.get (0).lo;
for (Statistics s : list) for (Statistics s : list)
{ {
System.out.println (s); System.out.println (s);
if (s.hi > lo && s.lo < lo) if (s.hi > lo && s.lo < lo)
lo = s.lo; lo = s.lo;
if (s.hi < lo) if (s.hi < lo)
{ {
header.stringPointer = lo; header.stringPointer = lo;
break; break;
} }
} }
} }
public void createPropertyLinks () public void createPropertyLinks ()
{ {
int sCount = 0; int sCount = 0;
int rCount = 0; int rCount = 0;
int totStrings = 0; int totStrings = 0;
int totRoutines = 0; int totRoutines = 0;
for (Statistics s : list) for (Statistics s : list)
{ {
if (header.propertyNames[s.propertyNumber].charAt (0) >= 'a') if (header.getPropertyName (s.propertyNumber).charAt (0) >= 'a')
continue; continue;
if (s.lo >= header.stringPointer) if (s.lo >= header.stringPointer)
{ {
header.propertyNames[s.propertyNumber] = "STR" + ++sCount; header.propertyNames[s.propertyNumber] = "STR" + ++sCount;
totStrings += s.offsets.size (); totStrings += s.offsets.size ();
} }
else else
{ {
header.propertyNames[s.propertyNumber] = "CODE" + ++rCount; header.propertyNames[s.propertyNumber] = "CODE" + ++rCount;
routines.addAll (s.offsets); routines.addAll (s.offsets);
totRoutines += s.offsets.size (); totRoutines += s.offsets.size ();
} }
} }
System.out.println ("Strings found : " + totStrings); System.out.println ("Strings found : " + totStrings);
System.out.println ("Routines found : " + totRoutines); System.out.println ("Routines found : " + totRoutines);
} }
public void checkThreeByteProperties () public void checkThreeByteProperties ()
{ {
for (ZObject object : parent.list) for (ZObject object : parent.getObjects ())
{ {
for (Property property : object.properties) for (Property property : object.properties)
{ {
if (header.propertyNames[property.propertyNumber].charAt (0) < 'a' && property.length == 3) if (header.getPropertyName (property.propertyNumber).charAt (0) < 'a'
{ && property.length == 3)
int address = header.getWord (property.ptr + 1) * 2; {
System.out.println ("checking " + address); int address = header.getWord (property.ptr + 1) * 2;
header.codeManager.addRoutine (address, 0); System.out.println ("checking " + address);
} header.codeManager.addRoutine (address, 0);
} }
} }
} }
}
// find the property with only dictionary entries // find the property with only dictionary entries
public void setDictionary () public void setDictionary ()
{ {
PropertyTester pt = new PropertyTester (parent.list); PropertyTester pt = new PropertyTester (parent.getObjects ());
pt.addTest (new LengthEvenCondition ()); pt.addTest (new LengthEvenCondition ());
pt.addTest (new ValidDictionaryCondition ()); pt.addTest (new ValidDictionaryCondition ());
pt.doTests (); pt.doTests ();
for (Integer i : pt) for (Integer i : pt)
// should only be one // should only be one
header.propertyNames[i] = "DICT"; header.propertyNames[i] = "DICT"; // SYNONYM
} }
class Statistics implements Comparable<Statistics> class Statistics implements Comparable<Statistics>
{ {
int propertyNumber; int propertyNumber;
int lo; int lo;
int hi; int hi;
List<Integer> offsets = new ArrayList<Integer> (); List<Integer> offsets = new ArrayList<Integer> ();
public Statistics (int propertyNumber) public Statistics (int propertyNumber)
{ {
this.propertyNumber = propertyNumber; this.propertyNumber = propertyNumber;
} }
public void increment (Property property) public void increment (Property property)
{ {
offsets.add (property.offset); offsets.add (property.offset);
if (property.offset > hi) if (property.offset > hi)
hi = property.offset; hi = property.offset;
if (property.offset < lo || lo == 0) if (property.offset < lo || lo == 0)
lo = property.offset; lo = property.offset;
} }
@Override @Override
public String toString () public String toString ()
{ {
return String.format ("%2d %3d %,7d %,7d", propertyNumber, offsets.size (), lo, hi); return String.format ("%2d %3d %,7d %,7d", propertyNumber, offsets.size (),
} lo, hi);
}
@Override @Override
public int compareTo (Statistics o) public int compareTo (Statistics o)
{ {
return o.hi - hi; return o.hi - hi;
} }
} }
class LengthTwoCondition extends Condition class LengthTwoCondition extends Condition
{ {
@Override @Override
boolean test (Property property) boolean test (Property property)
{ {
return property.length == 2; return property.length == 2;
} }
} }
class LengthThreeCondition extends Condition class LengthThreeCondition extends Condition
{ {
@Override @Override
boolean test (Property property) boolean test (Property property)
{ {
return property.length == 3; return property.length == 3;
} }
} }
class LengthEvenCondition extends Condition class LengthEvenCondition extends Condition
{ {
@Override @Override
boolean test (Property property) boolean test (Property property)
{ {
return (property.length % 2) == 0; return (property.length % 2) == 0;
} }
} }
class HighMemoryCondition extends Condition class HighMemoryCondition extends Condition
{ {
int lo, hi; int lo, hi;
Statistics[] statistics = new Statistics[32]; // note there is no property #0 Statistics[] statistics = new Statistics[32]; // note there is no property #0
public HighMemoryCondition () public HighMemoryCondition ()
{ {
lo = header.highMemory; lo = header.highMemory;
hi = header.fileLength; hi = header.fileLength;
for (int i = 1; i < statistics.length; i++) for (int i = 1; i < statistics.length; i++)
statistics[i] = new Statistics (i); statistics[i] = new Statistics (i);
} }
@Override @Override
boolean test (Property property) boolean test (Property property)
{ {
statistics[property.propertyNumber].increment (property); statistics[property.propertyNumber].increment (property);
int address = header.getWord (property.ptr + 1) * 2; int address = header.getWord (property.ptr + 1) * 2;
return (address >= lo && address < hi) || address == 0; return (address >= lo && address < hi) || address == 0;
} }
} }
class ValidDictionaryCondition extends Condition class ValidDictionaryCondition extends Condition
{ {
@Override @Override
boolean test (Property property) boolean test (Property property)
{ {
for (int i = 1; i <= property.length; i += 2) for (int i = 1; i <= property.length; i += 2)
{ {
int address = header.getWord (property.ptr + i); int address = header.getWord (property.ptr + i);
if (!header.containsWordAt (address)) if (!header.containsWordAt (address))
return false; return false;
} }
return true; return true;
} }
} }
} }

View File

@ -1,6 +1,7 @@
package com.bytezone.diskbrowser.infocom; package com.bytezone.diskbrowser.infocom;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -11,21 +12,22 @@ import com.bytezone.diskbrowser.disk.FormattedDisk;
class ObjectManager extends InfocomAbstractFile implements Iterable<ZObject> class ObjectManager extends InfocomAbstractFile implements Iterable<ZObject>
{ {
Header header; private final Header header;
List<ZObject> list; private final List<ZObject> list;
int defaultsPtr, defaultsSize; private List<ZObject> sortedList;
int tablePtr, tableSize; private final int defaultsPtr, defaultsSize;
int propertyPtr, propertySize; private final int tablePtr, tableSize;
ObjectAnalyser analyser; private final int propertyPtr, propertySize;
private final ObjectAnalyser analyser;
public ObjectManager (Header header) public ObjectManager (Header header)
{ {
super ("Objects", header.buffer); super ("Objects", header.buffer);
this.header = header; this.header = header;
defaultsPtr = header.objectTable; defaultsPtr = header.objectTableOffset;
defaultsSize = 62; defaultsSize = 62;
tablePtr = header.objectTable + 62; tablePtr = header.objectTableOffset + 62;
propertyPtr = header.getWord (tablePtr + 7); propertyPtr = header.getWord (tablePtr + 7);
propertySize = header.globalsOffset - propertyPtr; propertySize = header.globalsOffset - propertyPtr;
tableSize = (propertyPtr - tablePtr); tableSize = (propertyPtr - tablePtr);
@ -45,6 +47,16 @@ class ObjectManager extends InfocomAbstractFile implements Iterable<ZObject>
hexBlocks.add (new HexBlock (propertyPtr, propertySize, "Properties:")); hexBlocks.add (new HexBlock (propertyPtr, propertySize, "Properties:"));
} }
List<ZObject> getObjects ()
{
return list;
}
ZObject getObject (int index)
{
return list.get (index);
}
public void addNodes (DefaultMutableTreeNode root, FormattedDisk disk) public void addNodes (DefaultMutableTreeNode root, FormattedDisk disk)
{ {
root.setAllowsChildren (true); root.setAllowsChildren (true);
@ -61,10 +73,9 @@ class ObjectManager extends InfocomAbstractFile implements Iterable<ZObject>
new DefaultAppleFileSource (object.getName (), object, disk)); new DefaultAppleFileSource (object.getName (), object, disk));
parentNode.add (child); parentNode.add (child);
if (object.sibling > 0) if (object.sibling > 0)
buildObjectTree (header.objectManager.list.get (object.sibling - 1), parentNode, buildObjectTree (list.get (object.sibling - 1), parentNode, disk);
disk);
if (object.child > 0) if (object.child > 0)
buildObjectTree (header.objectManager.list.get (object.child - 1), child, disk); buildObjectTree (list.get (object.child - 1), child, disk);
else else
child.setAllowsChildren (false); child.setAllowsChildren (false);
} }
@ -77,8 +88,8 @@ class ObjectManager extends InfocomAbstractFile implements Iterable<ZObject>
@Override @Override
public String getText () public String getText ()
{ {
String header1 = "ID Attributes Pr Sb Ch Prop Title\n-- -----------" // String header1 = "ID Attributes Pr Sb Ch Prop Title\n-- -----------"
+ " -- -- -- ----- -----------------------------\n"; // + " -- -- -- ----- -----------------------------\n";
String underline = " ----------------------------------------"; String underline = " ----------------------------------------";
String titles[] = String titles[] =
{ "ID ", "Title ", { "ID ", "Title ",
@ -89,13 +100,21 @@ class ObjectManager extends InfocomAbstractFile implements Iterable<ZObject>
+ "-- " + underline + underline + underline + underline + " ----------- -----\n"; + "-- " + underline + underline + underline + underline + " ----------- -----\n";
StringBuilder text = new StringBuilder (header2); StringBuilder text = new StringBuilder (header2);
int objectNumber = 0; if (sortedList == null)
sortedList = new ArrayList<> (list);
Collections.sort (sortedList);
// int objectNumber = 0;
for (ZObject zo : list) for (ZObject zo : list)
if (false) // if (false)
text.append (String.format ("%02X %s%n", ++objectNumber, zo)); // text.append (String.format ("%02X %s%n", ++objectNumber, zo));
else // else
text.append ( text.append (String.format ("%02X %s%n", zo.getId (), zo.getDescription (list)));
String.format ("%02X %s%n", ++objectNumber, zo.getDescription (list)));
text.append ("\n\n");
text.append (header2);
for (ZObject zo : sortedList)
text.append (String.format ("%02X %s%n", zo.getId (), zo.getDescription (list)));
text.deleteCharAt (text.length () - 1); text.deleteCharAt (text.length () - 1);
return text.toString (); return text.toString ();

View File

@ -33,7 +33,7 @@ class PropertyManager extends AbstractFile
for (Statistic stat : list) for (Statistic stat : list)
if (stat.list.size () > 0) if (stat.list.size () > 0)
{ {
String title = "Property " + header.propertyNames[stat.id].trim (); String title = "Property " + header.getPropertyName (stat.id).trim ();
DefaultMutableTreeNode child = new DefaultMutableTreeNode ( DefaultMutableTreeNode child = new DefaultMutableTreeNode (
new DefaultAppleFileSource (title, stat.getText (), disk)); new DefaultAppleFileSource (title, stat.getText (), disk));
node.add (child); node.add (child);
@ -72,13 +72,13 @@ class PropertyManager extends AbstractFile
String getText () String getText ()
{ {
StringBuilder text = StringBuilder text = new StringBuilder (String
new StringBuilder ("Objects with property " + id + " set:\n\n"); .format ("Objects with property %d %s set:%n%n", id, header.propertyNames[id]));
for (ZObject o : list) for (ZObject o : list)
{ {
ZObject.Property p = o.getProperty (id); ZObject.Property p = o.getProperty (id);
text.append (String.format ("%02X %-29s%s%n", o.id, o.getName (), text.append (String.format ("%02X %-29s%s%n", o.getId (), o.getName (),
p.toString ().substring (7))); p.toString ().substring (11)));
} }
if (text.length () > 0) if (text.length () > 0)
text.deleteCharAt (text.length () - 1); text.deleteCharAt (text.length () - 1);
@ -88,7 +88,7 @@ class PropertyManager extends AbstractFile
@Override @Override
public String toString () public String toString ()
{ {
return String.format (" %2d %-6s %3d", id, header.propertyNames[id], return String.format (" %2d %-6s %3d", id, header.getPropertyName (id),
list.size ()); list.size ());
} }
} }

View File

@ -4,13 +4,15 @@ import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import com.bytezone.diskbrowser.infocom.Instruction.Operand;
import com.bytezone.diskbrowser.infocom.Instruction.OperandType;
class Routine extends InfocomAbstractFile class Routine extends InfocomAbstractFile
implements Iterable<Instruction>, Comparable<Routine> implements Iterable<Instruction>, Comparable<Routine>
{ {
private static final String padding = " "; private static final String padding = " ";
int startPtr, length, strings, locals; int startPtr, length, strings, locals;
// private final Header header;
List<Parameter> parameters = new ArrayList<Parameter> (); List<Parameter> parameters = new ArrayList<Parameter> ();
List<Instruction> instructions = new ArrayList<Instruction> (); List<Instruction> instructions = new ArrayList<Instruction> ();
@ -21,11 +23,13 @@ class Routine extends InfocomAbstractFile
public Routine (int ptr, Header header, int caller) public Routine (int ptr, Header header, int caller)
{ {
super (String.format ("Routine %05X", ptr), header.buffer); super (String.format ("Routine %05X", ptr), header.buffer);
// this.header = header;
locals = buffer[ptr] & 0xFF; locals = buffer[ptr] & 0xFF;
if (locals > 15) if (locals > 15)
{
System.out.println ("Too many locals: " + locals);
return; return;
}
startPtr = ptr++; // also used to flag a valid routine startPtr = ptr++; // also used to flag a valid routine
calledBy.add (caller); calledBy.add (caller);
@ -53,6 +57,10 @@ class Routine extends InfocomAbstractFile
if (instruction.isPrint ()) if (instruction.isPrint ())
strings++; strings++;
for (Operand operand : instruction.opcode.operands)
if (operand.operandType == OperandType.VAR_GLOBAL)
header.globals.addRoutine (this, operand);
ptr += instruction.length (); ptr += instruction.length ();
// is it a backwards jump? // is it a backwards jump?

View File

@ -20,9 +20,9 @@ class StringManager extends AbstractFile
int max = header.fileLength; int max = header.fileLength;
while (ptr < max) while (ptr < max)
{ {
ZString zs = new ZString (buffer, ptr, header); ZString zs = new ZString (header, ptr);
if (zs.value == null) if (zs.value == null)
break; // used when eof not known or correct - fix!! break; // used when eof not known or correct - fix!!
strings.put (ptr, zs); strings.put (ptr, zs);
ptr += zs.length; ptr += zs.length;
} }
@ -47,7 +47,7 @@ class StringManager extends AbstractFile
int count = 0; int count = 0;
text.append (" # Start String\n"); text.append (" # Start String\n");
text.append ("--- ----- --------------------------------------------------------" text.append ("--- ----- --------------------------------------------------------"
+ "-------------------\n"); + "-------------------\n");
for (ZString s : strings.values ()) for (ZString s : strings.values ())
{ {

View File

@ -7,26 +7,28 @@ import java.util.List;
import com.bytezone.diskbrowser.applefile.AbstractFile; import com.bytezone.diskbrowser.applefile.AbstractFile;
import com.bytezone.diskbrowser.utilities.HexFormatter; import com.bytezone.diskbrowser.utilities.HexFormatter;
class ZObject extends AbstractFile class ZObject extends AbstractFile implements Comparable<ZObject>
{ {
static final int HEADER_SIZE = 9; static final int HEADER_SIZE = 9;
int id; private final Header header;
int startPtr; private final int id;
int propertyTablePtr; private final int startPtr;
int propertyTableLength;
int parent, sibling, child;
List<Property> properties = new ArrayList<Property> ();
Header header;
BitSet attributes = new BitSet (32);
public ZObject (String name, byte[] buffer, int offset, int seq, Header header) private final int propertyTablePtr;
private final int propertyTableLength;
final int parent, sibling, child;
final List<Property> properties = new ArrayList<Property> ();
final BitSet attributes = new BitSet (32);
public ZObject (String name, byte[] buffer, int offset, int id, Header header)
{ {
super (name, buffer); super (name, buffer);
this.header = header; this.header = header;
this.startPtr = offset; this.startPtr = offset;
id = seq; this.id = id;
// attributes // attributes
int bitIndex = 0; int bitIndex = 0;
@ -51,19 +53,24 @@ class ZObject extends AbstractFile
propertyTablePtr = header.getWord (offset + 7); propertyTablePtr = header.getWord (offset + 7);
int ptr = propertyTablePtr; int ptr = propertyTablePtr;
int nameLength = header.getByte (ptr) * 2; int nameLength = header.getByte (ptr) * 2;
this.name = nameLength == 0 ? "<<none>>" : new ZString (buffer, ++ptr, header).value; this.name = nameLength == 0 ? "<<" + id + ">>" : new ZString (header, ++ptr).value;
ptr += nameLength; ptr += nameLength;
// read each property // read each property
while (buffer[ptr] != 0) while (buffer[ptr] != 0)
{ {
Property p = new Property (buffer, ptr); Property p = new Property (ptr);
properties.add (p); properties.add (p);
ptr += p.length + 1; ptr += p.length + 1;
} }
propertyTableLength = ptr - propertyTablePtr; propertyTableLength = ptr - propertyTablePtr;
} }
int getId ()
{
return id;
}
@Override @Override
public String getText () public String getText ()
{ {
@ -71,9 +78,9 @@ class ZObject extends AbstractFile
text.append (String.format ("ID : %02X (%<3d) %s%n%n", id, name)); text.append (String.format ("ID : %02X (%<3d) %s%n%n", id, name));
String obj1 = parent == 0 ? "" : header.objectManager.list.get (parent - 1).name; String obj1 = parent == 0 ? "" : header.getObject (parent - 1).name;
String obj2 = sibling == 0 ? "" : header.objectManager.list.get (sibling - 1).name; String obj2 = sibling == 0 ? "" : header.getObject (sibling - 1).name;
String obj3 = child == 0 ? "" : header.objectManager.list.get (child - 1).name; String obj3 = child == 0 ? "" : header.getObject (child - 1).name;
text.append (String.format ("Parent : %02X (%<3d) %s%n", parent, obj1)); text.append (String.format ("Parent : %02X (%<3d) %s%n", parent, obj1));
text.append (String.format ("Sibling : %02X (%<3d) %s%n", sibling, obj2)); text.append (String.format ("Sibling : %02X (%<3d) %s%n", sibling, obj2));
@ -140,7 +147,7 @@ class ZObject extends AbstractFile
int length; int length;
int offset; // only used if length == 2 int offset; // only used if length == 2
public Property (byte[] buffer, int ptr) public Property (int ptr)
{ {
this.ptr = ptr; this.ptr = ptr;
length = header.getByte (ptr) / 32 + 1; length = header.getByte (ptr) / 32 + 1;
@ -152,41 +159,53 @@ class ZObject extends AbstractFile
private ZObject getObject () private ZObject getObject ()
{ {
return header.objectManager.list.get ((buffer[ptr + 1] & 0xFF) - 1); return header.getObject ((buffer[ptr + 1] & 0xFF) - 1);
}
private ZObject getObject (int id)
{
return header.getObject (id - 1);
} }
@Override @Override
public String toString () public String toString ()
{ {
StringBuilder text = new StringBuilder ( StringBuilder text = new StringBuilder (
String.format ("%5s : ", header.propertyNames[propertyNumber])); String.format ("%8s : ", header.getPropertyName (propertyNumber)));
String propertyType = header.propertyNames[propertyNumber]; String propertyType = header.getPropertyName (propertyNumber);
if (!propertyType.equals ("DICT") && !propertyType.contains ("STR")) if (!(propertyType.equals ("DICT") || propertyType.startsWith ("STR")))
text.append ( text.append (
String.format ("%-20s", HexFormatter.getHexString (buffer, ptr + 1, length))); String.format ("%-20s", HexFormatter.getHexString (buffer, ptr + 1, length)));
if (propertyType.charAt (0) >= 'a') // directions are in lowercase if (propertyNumber >= 19) // directions
{ {
switch (length) switch (length)
{ {
case 1: case 1: // UEXIT - unconditional exit
text.append (getObject ().name); text.append (getObject ().name);
break; break;
case 2: case 2:
text.append ("\"" + header.stringManager.stringAt (offset) + "\""); text.append ("\"" + header.stringManager.stringAt (offset) + "\"");
break; break;
case 3: // executable routine case 3: // FEXIT - function exit
int address = header.getWord (ptr + 1) * 2; int address = header.getWord (ptr + 1) * 2;
text.append (String.format ("R:%05X", address)); text.append (String.format ("R:%05X", address));
appendRoutine (text, address); appendRoutine (text, address);
break; break;
case 4: case 4:
// text.append (getObject ().name + " : ");
text.append (String.format ("%s : IF G%02X ELSE ", getObject ().name,
header.getByte (ptr + 2)));
address = header.getWord (ptr + 3) * 2; address = header.getWord (ptr + 3) * 2;
if (address > 0) if (address > 0)
text.append ("\"" + header.stringManager.stringAt (address) + "\""); text.append ("\"" + header.stringManager.stringAt (address) + "\"");
break; break;
case 5:
text.append (String.format ("%s : IF G%02X ", getObject ().name,
header.getByte (ptr + 2)));
break;
default: default:
break; break;
} }
@ -196,7 +215,7 @@ class ZObject extends AbstractFile
for (int i = 1; i <= length; i += 2) for (int i = 1; i <= length; i += 2)
{ {
int address = header.getWord (ptr + i); int address = header.getWord (ptr + i);
text.append (header.wordAt (address) + ", "); text.append (String.format ("%02X: %s, ", address, header.wordAt (address)));
} }
text.deleteCharAt (text.length () - 1); text.deleteCharAt (text.length () - 1);
text.deleteCharAt (text.length () - 1); text.deleteCharAt (text.length () - 1);
@ -211,6 +230,33 @@ class ZObject extends AbstractFile
text.append (String.format ("(%4X) \"%s\"", offset, text.append (String.format ("(%4X) \"%s\"", offset,
header.stringManager.stringAt (offset))); header.stringManager.stringAt (offset)));
} }
else if (propertyType.equals ("ADJ"))
{
}
else if (propertyType.equals ("SIZE"))
{
}
else if (propertyType.equals ("VALUE"))
{
}
else if (propertyType.equals ("TVALU"))
{
}
else if (propertyType.equals ("GLBL"))
{
for (int i = 0; i < length; i++)
{
int objectId = header.getByte (ptr + i + 1);
text.append (
String.format ("%s%s", (i == 0 ? "" : ", "), getObject (objectId).name));
}
}
// else
// text.append ("Unknown property type: " + propertyType);
return text.toString (); return text.toString ();
} }
@ -224,4 +270,10 @@ class ZObject extends AbstractFile
text.append ("\n\n****** null routine\n"); text.append ("\n\n****** null routine\n");
} }
} }
@Override
public int compareTo (ZObject o)
{
return this.name.compareTo (o.name);
}
} }

View File

@ -2,155 +2,157 @@ package com.bytezone.diskbrowser.infocom;
class ZString class ZString
{ {
private static String[] letters = private static String[] letters =
{ " abcdefghijklmnopqrstuvwxyz", " ABCDEFGHIJKLMNOPQRSTUVWXYZ", { " abcdefghijklmnopqrstuvwxyz", " ABCDEFGHIJKLMNOPQRSTUVWXYZ",
" 0123456789.,!?_#\'\"/\\-:()" }; " 0123456789.,!?_#\'\"/\\-:()" };
String value; String value;
Header header; Header header;
int startPtr; int startPtr;
int length; int length;
public ZString (byte[] buffer, int offset, Header header) public ZString (Header header, int offset)
{ {
ZStringBuilder text = new ZStringBuilder (); ZStringBuilder text = new ZStringBuilder ();
this.header = header; this.header = header;
this.startPtr = offset; this.startPtr = offset;
while (true) while (true)
{ {
if (offset >= header.buffer.length - 1) if (offset >= header.buffer.length - 1)
{ {
System.out.println ("********" + text.toString ()); System.out.println ("********" + text.toString ());
break; break;
} }
// get the next two bytes // get the next two bytes
int val = header.getWord (offset); int val = header.getWord (offset);
// process each zChar as a 5-bit value // process each zChar as a 5-bit value
text.processZChar ((byte) ((val >> 10) & 0x1F)); text.processZChar ((byte) ((val >>> 10) & 0x1F));
text.processZChar ((byte) ((val >> 5) & 0x1F)); text.processZChar ((byte) ((val >>> 5) & 0x1F));
text.processZChar ((byte) (val & 0x1F)); text.processZChar ((byte) (val & 0x1F));
if ((val & 0x8000) > 0) // bit 15 = finished flag if ((val & 0x8000) != 0) // bit 15 = finished flag
{ {
length = offset - startPtr + 2; length = offset - startPtr + 2;
value = text.toString (); value = text.toString ();
break; break;
} }
offset += 2; offset += 2;
} }
} }
@Override @Override
public String toString () public String toString ()
{ {
return value; return value;
} }
private class ZStringBuilder private class ZStringBuilder
{ {
int alphabet; int alphabet;
boolean shift; boolean shift;
int shiftAlphabet; int shiftAlphabet;
int synonym; int synonym;
int buildingLevel; int buildingLevel;
int builtLetter; int builtLetter;
StringBuilder text = new StringBuilder (); StringBuilder text = new StringBuilder ();
private void processZChar (byte zchar) private void processZChar (byte zchar)
{ {
// A flag to indicate that we are building a character not in the alphabet. The // A flag to indicate that we are building a character not in the alphabet. The
// value indicates which half of the character the current zchar goes into. Once // value indicates which half of the character the current zchar goes into. Once
// both halves are full, we use the ascii value in builtLetter. // both halves are full, we use the ascii value in builtLetter.
if (buildingLevel > 0) if (buildingLevel > 0)
{ {
builtLetter = (short) ((builtLetter << 5) | zchar); builtLetter = (short) ((builtLetter << 5) | zchar);
if (++buildingLevel == 3) if (++buildingLevel == 3)
{ {
text.append ((char) builtLetter); text.append ((char) builtLetter);
buildingLevel = 0; buildingLevel = 0;
} }
return; return;
} }
// A flag to indicate that we need to insert an abbreviation. The synonym value // A flag to indicate that we need to insert an abbreviation. The synonym value
// (1-3) indicates which abbreviation block to use, and the current zchar is the offset // (1-3) indicates which abbreviation block to use, and the current zchar is the
// within that block. // offset within that block.
if (synonym > 0) if (synonym > 0)
{ {
text.append (header.getAbbreviation ((synonym - 1) * 32 + zchar)); text.append (header.getAbbreviation ((synonym - 1) * 32 + zchar));
synonym = 0; synonym = 0;
return; return;
} }
// this should be in the switch if ((shift && shiftAlphabet == 2) || (!shift && alphabet == 2))
if ((shift && shiftAlphabet == 2) || (!shift && alphabet == 2)) {
{ if (zchar == 6)
if (zchar == 6) {
{ buildingLevel = 1;
buildingLevel = 1; builtLetter = 0;
builtLetter = 0; shift = false;
shift = false; return;
return; }
} if (zchar == 7)
if (zchar == 7) {
{ text.append ("\n");
text.append ("\n"); shift = false;
shift = false; return;
return; }
} }
}
// zChar values 0-5 have special meanings, and 6-7 are special only in alphabet #2. // zChar values 0-5 have special meanings, and 6-7 are special only in alphabet #2.
// Otherwise it's just a straight lookup into the current alphabet. // Otherwise it's just a straight lookup into the current alphabet.
switch (zchar) switch (zchar)
{ {
case 0: case 0:
text.append (" "); text.append (" ");
shift = false; shift = false;
return; return;
case 1:
synonym = zchar;
return;
case 2:
case 3:
if (header.version >= 3)
{
synonym = zchar;
return;
}
// version 1 or 2
shiftAlphabet = (alphabet + zchar - 1) % 3;
shift = true;
return;
case 4:
case 5:
if (header.version >= 3) // shift key
{
shiftAlphabet = zchar - 3;
shift = true;
}
else
// shift lock key
alphabet = (alphabet + zchar - 3) % 3;
return;
default:
if (shift)
{
text.append (letters[shiftAlphabet].charAt (zchar));
shift = false;
}
else
text.append (letters[alphabet].charAt (zchar));
return;
}
}
@Override case 1:
public String toString () synonym = zchar;
{ return;
return text.toString ();
} case 2:
} case 3:
if (header.version >= 3)
{
synonym = zchar;
return;
}
// version 1 or 2
shiftAlphabet = (alphabet + zchar - 1) % 3;
shift = true;
return;
case 4:
case 5:
if (header.version >= 3) // shift key
{
shiftAlphabet = zchar - 3;
shift = true;
}
else // shift lock key
alphabet = (alphabet + zchar - 3) % 3;
return;
default:
if (shift)
{
text.append (letters[shiftAlphabet].charAt (zchar));
shift = false;
}
else
text.append (letters[alphabet].charAt (zchar));
return;
}
}
@Override
public String toString ()
{
return text.toString ();
}
}
} }