diff --git a/src/com/bytezone/diskbrowser/infocom/Abbreviations.java b/src/com/bytezone/diskbrowser/infocom/Abbreviations.java index 3d0957d..5975ba9 100755 --- a/src/com/bytezone/diskbrowser/infocom/Abbreviations.java +++ b/src/com/bytezone/diskbrowser/infocom/Abbreviations.java @@ -1,68 +1,78 @@ -package com.bytezone.diskbrowser.infocom; - -import java.util.ArrayList; -import java.util.List; - -class Abbreviations extends InfocomAbstractFile -{ - List list; - Header header; - int dataPtr; - int dataSize; - int tablePtr; - int tableSize; - - public Abbreviations (Header header) - { - super ("Abbreviations", header.buffer); - this.header = header; - - dataPtr = header.getWord (header.abbreviationsTable) * 2; - dataSize = header.abbreviationsTable - dataPtr; - tablePtr = header.abbreviationsTable; - tableSize = header.objectTableOffset - header.abbreviationsTable; - - // prepare hex dump - hexBlocks.add (new HexBlock (dataPtr, dataSize, "Abbreviations data:")); - hexBlocks.add (new HexBlock (tablePtr, tableSize, "Abbreviations table:")); - } - - private void populate () - { - list = new ArrayList<> (); - - for (int i = header.abbreviationsTable; i < header.objectTableOffset; i += 2) - list.add (new ZString (header, header.getWord (i) * 2)); - } - - public String getAbbreviation (int abbreviationNumber) - { - if (list == null) - populate (); - - return list.get (abbreviationNumber).value; - } - - @Override - public String getText () - { - if (list == null) - populate (); - - StringBuilder text = new StringBuilder (); - - // text.append (String.format ("Data address....%04X %d%n", dataPtr, dataPtr)); - // text.append (String.format ("Data size.......%04X %d%n", dataSize, dataSize)); - // text.append (String.format ("Table address...%04X %d%n", tablePtr, tablePtr)); - // text.append (String.format ("Table size......%04X %d (%d words)%n%n", tableSize, tableSize, - // (tableSize / 2))); - - int count = 0; - for (ZString word : list) - text.append (String.format ("%3d %s%n", count++, word.value)); - if (list.size () > 0) - text.deleteCharAt (text.length () - 1); - - return text.toString (); - } +package com.bytezone.diskbrowser.infocom; + +import java.util.ArrayList; +import java.util.List; + +// -----------------------------------------------------------------------------------// +class Abbreviations extends InfocomAbstractFile +// -----------------------------------------------------------------------------------// +{ + List list; + Header header; + int dataPtr; + int dataSize; + int tablePtr; + int tableSize; + + // ---------------------------------------------------------------------------------// + Abbreviations (Header header) + // ---------------------------------------------------------------------------------// + { + super ("Abbreviations", header.buffer); + this.header = header; + + dataPtr = header.getWord (header.abbreviationsTable) * 2; + dataSize = header.abbreviationsTable - dataPtr; + tablePtr = header.abbreviationsTable; + tableSize = header.objectTableOffset - header.abbreviationsTable; + + // prepare hex dump + hexBlocks.add (new HexBlock (dataPtr, dataSize, "Abbreviations data:")); + hexBlocks.add (new HexBlock (tablePtr, tableSize, "Abbreviations table:")); + } + + // ---------------------------------------------------------------------------------// + private void populate () + // ---------------------------------------------------------------------------------// + { + list = new ArrayList<> (); + + for (int i = header.abbreviationsTable; i < header.objectTableOffset; i += 2) + list.add (new ZString (header, header.getWord (i) * 2)); + } + + // ---------------------------------------------------------------------------------// + String getAbbreviation (int abbreviationNumber) + // ---------------------------------------------------------------------------------// + { + if (list == null) + populate (); + + return list.get (abbreviationNumber).value; + } + + // ---------------------------------------------------------------------------------// + @Override + public String getText () + // ---------------------------------------------------------------------------------// + { + if (list == null) + populate (); + + StringBuilder text = new StringBuilder (); + + // text.append (String.format ("Data address....%04X %d%n", dataPtr, dataPtr)); + // text.append (String.format ("Data size.......%04X %d%n", dataSize, dataSize)); + // text.append (String.format ("Table address...%04X %d%n", tablePtr, tablePtr)); + // text.append (String.format ("Table size......%04X %d (%d words)%n%n", + // tableSize, tableSize, (tableSize / 2))); + + int count = 0; + for (ZString word : list) + text.append (String.format ("%3d %s%n", count++, word.value)); + if (list.size () > 0) + text.deleteCharAt (text.length () - 1); + + return text.toString (); + } } \ No newline at end of file diff --git a/src/com/bytezone/diskbrowser/infocom/AttributeManager.java b/src/com/bytezone/diskbrowser/infocom/AttributeManager.java index ffe3954..7dccf53 100644 --- a/src/com/bytezone/diskbrowser/infocom/AttributeManager.java +++ b/src/com/bytezone/diskbrowser/infocom/AttributeManager.java @@ -9,12 +9,16 @@ import com.bytezone.diskbrowser.applefile.AbstractFile; import com.bytezone.diskbrowser.disk.DefaultAppleFileSource; import com.bytezone.diskbrowser.disk.FormattedDisk; +// -----------------------------------------------------------------------------------// class AttributeManager extends AbstractFile +// -----------------------------------------------------------------------------------// { List list = new ArrayList<> (); Header header; + // ---------------------------------------------------------------------------------// public AttributeManager (String name, byte[] buffer, Header header) + // ---------------------------------------------------------------------------------// { super (name, buffer); this.header = header; @@ -23,7 +27,9 @@ class AttributeManager extends AbstractFile list.add (new Statistic (attrNo)); } + // ---------------------------------------------------------------------------------// public void addNodes (DefaultMutableTreeNode node, FormattedDisk disk) + // ---------------------------------------------------------------------------------// { node.setAllowsChildren (true); @@ -37,8 +43,10 @@ class AttributeManager extends AbstractFile } } + // ---------------------------------------------------------------------------------// @Override public String getText () + // ---------------------------------------------------------------------------------// { StringBuilder text = new StringBuilder ("Attribute Frequency\n"); text.append ("--------- ---------\n"); @@ -50,7 +58,9 @@ class AttributeManager extends AbstractFile return text.toString (); } + // ---------------------------------------------------------------------------------// private class Statistic + // ---------------------------------------------------------------------------------// { int id; List list = new ArrayList<> (); diff --git a/src/com/bytezone/diskbrowser/infocom/CodeManager.java b/src/com/bytezone/diskbrowser/infocom/CodeManager.java index d8a8320..16e7279 100644 --- a/src/com/bytezone/diskbrowser/infocom/CodeManager.java +++ b/src/com/bytezone/diskbrowser/infocom/CodeManager.java @@ -15,19 +15,25 @@ import com.bytezone.diskbrowser.infocom.Grammar.Sentence; import com.bytezone.diskbrowser.infocom.Grammar.SentenceGroup; import com.bytezone.diskbrowser.utilities.HexFormatter; +// -----------------------------------------------------------------------------------// class CodeManager extends AbstractFile +// -----------------------------------------------------------------------------------// { private final Header header; private int codeSize; private final Map routines = new TreeMap<> (); - public CodeManager (Header header) + // ---------------------------------------------------------------------------------// + CodeManager (Header header) + // ---------------------------------------------------------------------------------// { super ("Code", header.buffer); this.header = header; } + // ---------------------------------------------------------------------------------// void addNodes (DefaultMutableTreeNode root, InfocomDisk disk) + // ---------------------------------------------------------------------------------// { root.setAllowsChildren (true); @@ -48,7 +54,9 @@ class CodeManager extends AbstractFile } } + // ---------------------------------------------------------------------------------// private List getSectors (Routine routine, Disk disk) + // ---------------------------------------------------------------------------------// { int blockNo = routine.startPtr / 256 + 48; int size = routine.length; @@ -62,7 +70,9 @@ class CodeManager extends AbstractFile return blocks; } + // ---------------------------------------------------------------------------------// void addRoutines (int programCounter) + // ---------------------------------------------------------------------------------// { addRoutine (programCounter - 1, -1); addActionRoutines (); // obtained from Grammar @@ -111,19 +121,25 @@ class CodeManager extends AbstractFile } } + // ---------------------------------------------------------------------------------// private int checkAlignment (int ptr) + // ---------------------------------------------------------------------------------// { if (ptr % 2 == 1) // routine must start on a word boundary ++ptr; return ptr; } + // ---------------------------------------------------------------------------------// private void addGlobalRoutines () + // ---------------------------------------------------------------------------------// { } + // ---------------------------------------------------------------------------------// private void addMissingRoutines () + // ---------------------------------------------------------------------------------// { System.out.printf ("%nWalking the code block%n%n"); int total = routines.size (); @@ -158,7 +174,9 @@ class CodeManager extends AbstractFile routines.size () - total); } + // ---------------------------------------------------------------------------------// private int findNextRoutine (int address) + // ---------------------------------------------------------------------------------// { for (Routine routine : routines.values ()) if (routine.startPtr > address) @@ -166,8 +184,10 @@ class CodeManager extends AbstractFile return 0; } + // ---------------------------------------------------------------------------------// @Override public String getText () + // ---------------------------------------------------------------------------------// { StringBuilder text = new StringBuilder (); int count = 0; @@ -188,7 +208,9 @@ class CodeManager extends AbstractFile return text.toString (); } + // ---------------------------------------------------------------------------------// private void addCodeRoutines () + // ---------------------------------------------------------------------------------// { List routines = header.objectManager.getCodeRoutines (); System.out.println ("Adding " + routines.size () + " code routines"); @@ -196,7 +218,9 @@ class CodeManager extends AbstractFile addRoutine (address, 0); } + // ---------------------------------------------------------------------------------// private void addActionRoutines () + // ---------------------------------------------------------------------------------// { int total = routines.size (); @@ -211,7 +235,9 @@ class CodeManager extends AbstractFile System.out.printf ("Added %d action routines%n", routines.size () - total); } + // ---------------------------------------------------------------------------------// Routine addRoutine (int address, int caller) + // ---------------------------------------------------------------------------------// { if (address == 0) // stack-based call return null; @@ -240,13 +266,17 @@ class CodeManager extends AbstractFile return routine; } + // ---------------------------------------------------------------------------------// Routine getRoutine (int address) + // ---------------------------------------------------------------------------------// { return routines.get (address); } + // ---------------------------------------------------------------------------------// @Override public String getHexDump () + // ---------------------------------------------------------------------------------// { // this depends on codeSize being set after the strings have been processed return HexFormatter.format (buffer, header.highMemory, codeSize); diff --git a/src/com/bytezone/diskbrowser/infocom/Dictionary.java b/src/com/bytezone/diskbrowser/infocom/Dictionary.java index 05cc1a9..6c6398a 100755 --- a/src/com/bytezone/diskbrowser/infocom/Dictionary.java +++ b/src/com/bytezone/diskbrowser/infocom/Dictionary.java @@ -1,344 +1,362 @@ -package com.bytezone.diskbrowser.infocom; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - -import com.bytezone.diskbrowser.applefile.AbstractFile; -import com.bytezone.diskbrowser.utilities.HexFormatter; - -class Dictionary extends AbstractFile -{ - private final Map dictionary; - private final int totalEntries; - private final int totalSeparators; - private final int dictionaryPtr, dictionarySize; - private final int entryLength; - private final String separators; - - Map> synonymList = new TreeMap<> (); - - public Dictionary (Header header) - { - super ("Dictionary", header.buffer); - - dictionaryPtr = header.dictionaryOffset; - dictionary = new TreeMap<> (); - - totalSeparators = buffer[dictionaryPtr] & 0xFF; - - StringBuilder sep = new StringBuilder (); - for (int i = 0; i < totalSeparators; i++) - sep.append ((char) (buffer[dictionaryPtr + i + 1] & 0xFF)); - separators = sep.toString (); - - int ptr = dictionaryPtr + totalSeparators + 1; - entryLength = buffer[ptr++] & 0xFF; - - totalEntries = header.getWord (ptr); - - ptr += 2; - int count = 0; - for (int i = 0; i < totalEntries; i++) - { - ZString string = new ZString (header, ptr); - dictionary.put (ptr, string); - WordEntry wordEntry = new WordEntry (string, count++); - - // add the WordEntry to the appropriate list - List wordEntryList = synonymList.get (wordEntry.key); - if (wordEntryList == null) - { - wordEntryList = new ArrayList<> (); - synonymList.put (wordEntry.key, wordEntryList); - } - wordEntryList.add (wordEntry); - - // check for words with the direction flag - if ((buffer[ptr + 4] & 0x10) != 0) - { - int b1 = buffer[ptr + 5] & 0xFF; - int b2 = buffer[ptr + 6] & 0xFF; - int property = b2 == 0 ? b1 : b2; - String propertyName = header.getPropertyName (property); - System.out.printf ("%02X %s%n", property, string.value); - if (propertyName == null || propertyName.length () > string.value.length ()) - header.propertyNames[property] = string.value; - } - ptr += entryLength; - } - - dictionarySize = totalSeparators + 3 + entryLength * totalEntries; - - for (int i = 1; i < header.propertyNames.length; i++) - if (header.propertyNames[i] == null) - header.propertyNames[i] = i + ""; - - // testing (only works in Zork 1) - if (true) - { - 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"; - } - - // 04 = PSEUDO (property 4) - // 05 = GLOBAL (property 5) - // 06 = VTYPE (property 6) - // 07 = STRENGTH (property 7) - // STR3 = TEXT (property 8) - // CODE2 = DESCFCN (property 9) - // 0A = CAPACITY (property 10) - // STR1 = LDESC (property 11) - // 0C = TVALUE (property 12) value in trophy case - // 0D = VALUE (property 13) - // STR2 = FDESC (property 14) - // 0F = SIZE (property 15) - // 10 = ADJ (property 16) - // CODE1 = ACTION (property 17) - // 12 = DICT (property 18) - - // 13 land - // 14 out - // 15 in, inside, into - // 16 d, down - // 17 u, up - // 18 sw, southw - // 19 se, southe - // 1A nw, northw - // 1B ne, northe - // 1C s, south - // 1D w, west - // 1E e, east - // 1F n, north - } - - public boolean containsWordAt (int address) - { - return dictionary.containsKey (address); - } - - public String wordAt (int address) - { - if (dictionary.containsKey (address)) - return dictionary.get (address).value; - return "dictionary can't find word @ " + address; - } - - public List getVerbs (int value) - { - List words = new ArrayList<> (); - int ptr = dictionaryPtr + totalSeparators + 4; - - for (ZString word : dictionary.values ()) - { - int b1 = buffer[ptr + 4] & 0xFF; - int b2 = buffer[ptr + 5] & 0xFF; - int b3 = buffer[ptr + 6] & 0xFF; - // mask seems to be 0x40 - if ((b1 == 0x41 && b2 == value) - || ((b1 == 0x62 || b1 == 0xC0 || b1 == 0x44) && b3 == value)) - words.add (word.value); - ptr += entryLength; - } - return words; - } - - public List getPrepositions (int value) - { - List words = new ArrayList<> (); - int ptr = dictionaryPtr + totalSeparators + 4; - - for (ZString word : dictionary.values ()) - { - int b1 = buffer[ptr + 4] & 0xFF; - int b2 = buffer[ptr + 5] & 0xFF; - int b3 = buffer[ptr + 6] & 0xFF; - // mask seems to be 0x08 - if (((b1 == 0x08 || b1 == 0x18 || b1 == 0x48) && b2 == value) - || ((b1 == 0x1B || b1 == 0x0C || b1 == 0x2A) && b3 == value)) - words.add (word.value); - ptr += entryLength; - } - return words; - } - - @Override - public String getHexDump () - { - StringBuilder text = new StringBuilder (); - text.append (HexFormatter.format (buffer, dictionaryPtr, dictionarySize)); - return text.toString (); - } - - @Override - public String getText () - { - StringBuilder text = new StringBuilder (); - - text.append (String.format ("Entries : %,6d%n", totalEntries)); - text.append (String.format ("Separators : %s%n", separators)); - text.append ( - String.format ("Offset : %,6d %04X%n%n", dictionaryPtr, dictionaryPtr)); - - int count = 0; - int ptr = dictionaryPtr + totalSeparators + 4; - - for (ZString word : dictionary.values ()) - { - String bits = Integer.toBinaryString (buffer[ptr + 4] & 0xFF); - if (bits.length () < 8) - bits = "00000000".substring (bits.length ()) + bits; - - text.append (String.format ("%04X %3d %-6s %s %s", ptr, count++, word.value, - bits, HexFormatter.getHexString (buffer, ptr + 4, entryLength - 4))); - int b1 = buffer[ptr + 4] & 0xFF; - int b2 = buffer[ptr + 5] & 0xFF; - int b3 = buffer[ptr + 6] & 0xFF; - if (b1 == 65) - text.append (String.format (" %3d%n", b2)); - else if (b1 == 98 || b1 == 0xC0 || b1 == 0x44) - text.append (String.format (" %3d%n", b3)); - else - text.append ("\n"); - ptr += entryLength; - } - - if (false) - { - int lastValue = 0; - for (List list : synonymList.values ()) - { - WordEntry wordEntry = list.get (0); - if (wordEntry.flags != lastValue) - { - lastValue = wordEntry.flags; - text.append ("\n"); - } - - if (wordEntry.flags == 0x80) // nouns are all in one entry - { - for (WordEntry we : list) - text.append (we + "\n"); - text.deleteCharAt (text.length () - 1); - } - else - text.append (wordEntry); - - if ((buffer[wordEntry.word.startPtr + 4] & 0x10) != 0) - text.append (" direction"); - - text.append ("\n"); - } - } - - if (true) - { - text.append ("\n"); - int lastValue = 0; - - for (int bit = 1; bit < 256; bit *= 2) - { - String bits = Integer.toBinaryString (bit & 0xFF); - if (bits.length () < 8) - bits = "00000000".substring (bits.length ()) + bits; - - text.append (String.format ("Bits: %s%n", bits)); - for (List list : synonymList.values ()) - { - WordEntry wordEntry = list.get (0); - if ((wordEntry.flags & bit) != 0) - { - if (wordEntry.flags != lastValue) - { - lastValue = wordEntry.flags; - text.append ("\n"); - } - if (wordEntry.flags == 0x80) // nouns are all in one entry - { - for (WordEntry we : list) - text.append (we + "\n"); - } - else - text.append (wordEntry + "\n"); - } - } - text.append ("\n"); - } - } - - text.deleteCharAt (text.length () - 1); - return text.toString (); - } - - private class WordEntry implements Comparable - { - ZString word; - int seq; - int flags; - int key; - String bits; - - public WordEntry (ZString word, int seq) - { - this.word = word; - this.seq = seq; - - // build key from 3 bytes following the word characters - int b1 = buffer[word.startPtr + 4] & 0xFF; - int b2 = buffer[word.startPtr + 5] & 0xFF; - int b3 = buffer[word.startPtr + 6] & 0xFF; - - this.key = (b1 << 16) | (b2 << 8) | b3; - this.flags = b1; - this.bits = Integer.toBinaryString (b1); - if (bits.length () < 8) - bits = "00000000".substring (bits.length ()) + bits; - } - - @Override - public int compareTo (WordEntry o) - { - return this.flags - o.flags; - } - - @Override - public String toString () - { - StringBuilder list = new StringBuilder ("["); - if ((key & 0x0800000) == 0) - { - for (WordEntry we : synonymList.get (key)) - list.append (we.word.value + ", "); - list.deleteCharAt (list.length () - 1); - list.deleteCharAt (list.length () - 1); - } - else - list.append (word.value); - list.append ("]"); - - StringBuilder text = new StringBuilder (); - text.append (String.format ("%04X %3d %-6s %s %s %s", word.startPtr, seq, - word.value, bits, - HexFormatter.getHexString (buffer, word.startPtr + 4, entryLength - 4), - list.toString ())); - return text.toString (); - } - } +package com.bytezone.diskbrowser.infocom; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import com.bytezone.diskbrowser.applefile.AbstractFile; +import com.bytezone.diskbrowser.utilities.HexFormatter; + +// -----------------------------------------------------------------------------------// +class Dictionary extends AbstractFile +// -----------------------------------------------------------------------------------// +{ + private final Map dictionary; + private final int totalEntries; + private final int totalSeparators; + private final int dictionaryPtr, dictionarySize; + private final int entryLength; + private final String separators; + + Map> synonymList = new TreeMap<> (); + + // ---------------------------------------------------------------------------------// + Dictionary (Header header) + // ---------------------------------------------------------------------------------// + { + super ("Dictionary", header.buffer); + + dictionaryPtr = header.dictionaryOffset; + dictionary = new TreeMap<> (); + + totalSeparators = buffer[dictionaryPtr] & 0xFF; + + StringBuilder sep = new StringBuilder (); + for (int i = 0; i < totalSeparators; i++) + sep.append ((char) (buffer[dictionaryPtr + i + 1] & 0xFF)); + separators = sep.toString (); + + int ptr = dictionaryPtr + totalSeparators + 1; + entryLength = buffer[ptr++] & 0xFF; + + totalEntries = header.getWord (ptr); + + ptr += 2; + int count = 0; + for (int i = 0; i < totalEntries; i++) + { + ZString string = new ZString (header, ptr); + dictionary.put (ptr, string); + WordEntry wordEntry = new WordEntry (string, count++); + + // add the WordEntry to the appropriate list + List wordEntryList = synonymList.get (wordEntry.key); + if (wordEntryList == null) + { + wordEntryList = new ArrayList<> (); + synonymList.put (wordEntry.key, wordEntryList); + } + wordEntryList.add (wordEntry); + + // check for words with the direction flag + if ((buffer[ptr + 4] & 0x10) != 0) + { + int b1 = buffer[ptr + 5] & 0xFF; + int b2 = buffer[ptr + 6] & 0xFF; + int property = b2 == 0 ? b1 : b2; + String propertyName = header.getPropertyName (property); + System.out.printf ("%02X %s%n", property, string.value); + if (propertyName == null || propertyName.length () > string.value.length ()) + header.propertyNames[property] = string.value; + } + ptr += entryLength; + } + + dictionarySize = totalSeparators + 3 + entryLength * totalEntries; + + for (int i = 1; i < header.propertyNames.length; i++) + if (header.propertyNames[i] == null) + header.propertyNames[i] = i + ""; + + // testing (only works in Zork 1) + if (true) + { + 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"; + } + + // 04 = PSEUDO (property 4) + // 05 = GLOBAL (property 5) + // 06 = VTYPE (property 6) + // 07 = STRENGTH (property 7) + // STR3 = TEXT (property 8) + // CODE2 = DESCFCN (property 9) + // 0A = CAPACITY (property 10) + // STR1 = LDESC (property 11) + // 0C = TVALUE (property 12) value in trophy case + // 0D = VALUE (property 13) + // STR2 = FDESC (property 14) + // 0F = SIZE (property 15) + // 10 = ADJ (property 16) + // CODE1 = ACTION (property 17) + // 12 = DICT (property 18) + + // 13 land + // 14 out + // 15 in, inside, into + // 16 d, down + // 17 u, up + // 18 sw, southw + // 19 se, southe + // 1A nw, northw + // 1B ne, northe + // 1C s, south + // 1D w, west + // 1E e, east + // 1F n, north + } + + // ---------------------------------------------------------------------------------// + public boolean containsWordAt (int address) + // ---------------------------------------------------------------------------------// + { + return dictionary.containsKey (address); + } + + // ---------------------------------------------------------------------------------// + public String wordAt (int address) + // ---------------------------------------------------------------------------------// + { + if (dictionary.containsKey (address)) + return dictionary.get (address).value; + return "dictionary can't find word @ " + address; + } + + // ---------------------------------------------------------------------------------// + public List getVerbs (int value) + // ---------------------------------------------------------------------------------// + { + List words = new ArrayList<> (); + int ptr = dictionaryPtr + totalSeparators + 4; + + for (ZString word : dictionary.values ()) + { + int b1 = buffer[ptr + 4] & 0xFF; + int b2 = buffer[ptr + 5] & 0xFF; + int b3 = buffer[ptr + 6] & 0xFF; + // mask seems to be 0x40 + if ((b1 == 0x41 && b2 == value) + || ((b1 == 0x62 || b1 == 0xC0 || b1 == 0x44) && b3 == value)) + words.add (word.value); + ptr += entryLength; + } + return words; + } + + // ---------------------------------------------------------------------------------// + public List getPrepositions (int value) + // ---------------------------------------------------------------------------------// + { + List words = new ArrayList<> (); + int ptr = dictionaryPtr + totalSeparators + 4; + + for (ZString word : dictionary.values ()) + { + int b1 = buffer[ptr + 4] & 0xFF; + int b2 = buffer[ptr + 5] & 0xFF; + int b3 = buffer[ptr + 6] & 0xFF; + // mask seems to be 0x08 + if (((b1 == 0x08 || b1 == 0x18 || b1 == 0x48) && b2 == value) + || ((b1 == 0x1B || b1 == 0x0C || b1 == 0x2A) && b3 == value)) + words.add (word.value); + ptr += entryLength; + } + return words; + } + + // ---------------------------------------------------------------------------------// + @Override + public String getHexDump () + // ---------------------------------------------------------------------------------// + { + StringBuilder text = new StringBuilder (); + text.append (HexFormatter.format (buffer, dictionaryPtr, dictionarySize)); + return text.toString (); + } + + // ---------------------------------------------------------------------------------// + @Override + public String getText () + // ---------------------------------------------------------------------------------// + { + StringBuilder text = new StringBuilder (); + + text.append (String.format ("Entries : %,6d%n", totalEntries)); + text.append (String.format ("Separators : %s%n", separators)); + text.append ( + String.format ("Offset : %,6d %04X%n%n", dictionaryPtr, dictionaryPtr)); + + int count = 0; + int ptr = dictionaryPtr + totalSeparators + 4; + + for (ZString word : dictionary.values ()) + { + String bits = Integer.toBinaryString (buffer[ptr + 4] & 0xFF); + if (bits.length () < 8) + bits = "00000000".substring (bits.length ()) + bits; + + text.append (String.format ("%04X %3d %-6s %s %s", ptr, count++, word.value, + bits, HexFormatter.getHexString (buffer, ptr + 4, entryLength - 4))); + int b1 = buffer[ptr + 4] & 0xFF; + int b2 = buffer[ptr + 5] & 0xFF; + int b3 = buffer[ptr + 6] & 0xFF; + if (b1 == 65) + text.append (String.format (" %3d%n", b2)); + else if (b1 == 98 || b1 == 0xC0 || b1 == 0x44) + text.append (String.format (" %3d%n", b3)); + else + text.append ("\n"); + ptr += entryLength; + } + + if (false) + { + int lastValue = 0; + for (List list : synonymList.values ()) + { + WordEntry wordEntry = list.get (0); + if (wordEntry.flags != lastValue) + { + lastValue = wordEntry.flags; + text.append ("\n"); + } + + if (wordEntry.flags == 0x80) // nouns are all in one entry + { + for (WordEntry we : list) + text.append (we + "\n"); + text.deleteCharAt (text.length () - 1); + } + else + text.append (wordEntry); + + if ((buffer[wordEntry.word.startPtr + 4] & 0x10) != 0) + text.append (" direction"); + + text.append ("\n"); + } + } + + if (true) + { + text.append ("\n"); + int lastValue = 0; + + for (int bit = 1; bit < 256; bit *= 2) + { + String bits = Integer.toBinaryString (bit & 0xFF); + if (bits.length () < 8) + bits = "00000000".substring (bits.length ()) + bits; + + text.append (String.format ("Bits: %s%n", bits)); + for (List list : synonymList.values ()) + { + WordEntry wordEntry = list.get (0); + if ((wordEntry.flags & bit) != 0) + { + if (wordEntry.flags != lastValue) + { + lastValue = wordEntry.flags; + text.append ("\n"); + } + if (wordEntry.flags == 0x80) // nouns are all in one entry + { + for (WordEntry we : list) + text.append (we + "\n"); + } + else + text.append (wordEntry + "\n"); + } + } + text.append ("\n"); + } + } + + text.deleteCharAt (text.length () - 1); + return text.toString (); + } + + // ---------------------------------------------------------------------------------// + private class WordEntry implements Comparable + // ---------------------------------------------------------------------------------// + { + ZString word; + int seq; + int flags; + int key; + String bits; + + public WordEntry (ZString word, int seq) + { + this.word = word; + this.seq = seq; + + // build key from 3 bytes following the word characters + int b1 = buffer[word.startPtr + 4] & 0xFF; + int b2 = buffer[word.startPtr + 5] & 0xFF; + int b3 = buffer[word.startPtr + 6] & 0xFF; + + this.key = (b1 << 16) | (b2 << 8) | b3; + this.flags = b1; + this.bits = Integer.toBinaryString (b1); + if (bits.length () < 8) + bits = "00000000".substring (bits.length ()) + bits; + } + + @Override + public int compareTo (WordEntry o) + { + return this.flags - o.flags; + } + + @Override + public String toString () + { + StringBuilder list = new StringBuilder ("["); + if ((key & 0x0800000) == 0) + { + for (WordEntry we : synonymList.get (key)) + list.append (we.word.value + ", "); + list.deleteCharAt (list.length () - 1); + list.deleteCharAt (list.length () - 1); + } + else + list.append (word.value); + list.append ("]"); + + StringBuilder text = new StringBuilder (); + text.append (String.format ("%04X %3d %-6s %s %s %s", word.startPtr, seq, + word.value, bits, + HexFormatter.getHexString (buffer, word.startPtr + 4, entryLength - 4), + list.toString ())); + return text.toString (); + } + } } \ No newline at end of file diff --git a/src/com/bytezone/diskbrowser/infocom/Globals.java b/src/com/bytezone/diskbrowser/infocom/Globals.java index 9d5a55f..05aa7ac 100644 --- a/src/com/bytezone/diskbrowser/infocom/Globals.java +++ b/src/com/bytezone/diskbrowser/infocom/Globals.java @@ -5,7 +5,9 @@ import java.util.List; import com.bytezone.diskbrowser.infocom.Instruction.Operand; +// -----------------------------------------------------------------------------------// class Globals extends InfocomAbstractFile +// -----------------------------------------------------------------------------------// { private static final int TOTAL_GLOBALS = 240; private final Header header; @@ -13,7 +15,9 @@ class Globals extends InfocomAbstractFile private final int arrayPtr, arraySize; private final List> globalRoutines; + // ---------------------------------------------------------------------------------// Globals (Header header) + // ---------------------------------------------------------------------------------// { super ("Globals", header.buffer); this.header = header; @@ -32,15 +36,19 @@ class Globals extends InfocomAbstractFile globalRoutines.add (new ArrayList<> ()); } + // ---------------------------------------------------------------------------------// void addRoutine (Routine routine, Operand operand) + // ---------------------------------------------------------------------------------// { List list = globalRoutines.get (operand.value - 16); if (!list.contains (routine)) list.add (routine); } + // ---------------------------------------------------------------------------------// @Override public String getText () + // ---------------------------------------------------------------------------------// { StringBuilder text = new StringBuilder ("GLB Value Routines\n"); for (int i = 0; i < TOTAL_GLOBALS; i++) diff --git a/src/com/bytezone/diskbrowser/infocom/Header.java b/src/com/bytezone/diskbrowser/infocom/Header.java index 90b6703..4bd524d 100755 --- a/src/com/bytezone/diskbrowser/infocom/Header.java +++ b/src/com/bytezone/diskbrowser/infocom/Header.java @@ -1,215 +1,215 @@ -package com.bytezone.diskbrowser.infocom; - -import java.io.File; - -import com.bytezone.diskbrowser.disk.Disk; - -// -----------------------------------------------------------------------------------// -class Header extends InfocomAbstractFile -// -----------------------------------------------------------------------------------// -{ - final String[] propertyNames = new String[32]; - - private final File file; - int version; - int highMemory; - int programCounter; - int dictionaryOffset; - int objectTableOffset; - int globalsOffset; - int staticMemory; - int abbreviationsTable; - int fileLength; - int checksum; - int stringPointer; - - final Abbreviations abbreviations; - final ObjectManager objectManager; - final Globals globals; - final Grammar grammar; - final Dictionary dictionary; - final CodeManager codeManager; - final StringManager stringManager; - - // ---------------------------------------------------------------------------------// - public Header (String name, byte[] buffer, Disk disk) - // ---------------------------------------------------------------------------------// - { - super (name, buffer); - this.file = disk.getFile (); - - version = getByte (00); - highMemory = getWord (0x04); - programCounter = getWord (0x06); - - dictionaryOffset = getWord (0x08); - objectTableOffset = getWord (0x0A); - globalsOffset = getWord (0x0C); - staticMemory = getWord (0x0E); - abbreviationsTable = getWord (0x18); - - 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) - fileLength = buffer.length; - - // do the basic managers - abbreviations = new Abbreviations (this); - dictionary = new Dictionary (this); - globals = new Globals (this); // may display ZStrings - - // set up an empty object to store Routines in - codeManager = new CodeManager (this); - - grammar = new Grammar ("Grammar", buffer, this); - - // add all the ZObjects, and analyse them to find stringPtr, DICT etc. - objectManager = new ObjectManager (this); - - // add all the ZStrings - stringManager = new StringManager ("Strings", buffer, this); - - codeManager.addRoutines (programCounter); - - // add entries for AbstractFile.getHexDump () - hexBlocks.add (new HexBlock (0, 64, "Header data:")); - } - - // ---------------------------------------------------------------------------------// - String getPropertyName (int id) - // ---------------------------------------------------------------------------------// - { - return propertyNames[id]; - } - - // ---------------------------------------------------------------------------------// - public String getAbbreviation (int index) - // ---------------------------------------------------------------------------------// - { - return abbreviations.getAbbreviation (index); - } - - // ---------------------------------------------------------------------------------// - public boolean containsWordAt (int address) - // ---------------------------------------------------------------------------------// - { - return dictionary.containsWordAt (address); - } - - // ---------------------------------------------------------------------------------// - public String wordAt (int address) - // ---------------------------------------------------------------------------------// - { - return dictionary.wordAt (address); - } - - // ---------------------------------------------------------------------------------// - @Override - public String getText () - // ---------------------------------------------------------------------------------// - { - StringBuilder text = new StringBuilder (); - - text.append (String.format ("Disk name %s%n", file.getName ())); - text.append (String.format ("Version %d%n", version)); - text.append ("\nDynamic memory:\n"); - text.append (String.format (" Abbreviation table %04X %,6d%n", - abbreviationsTable, abbreviationsTable)); - text.append (String.format (" Objects table %04X %,6d%n", - objectTableOffset, objectTableOffset)); - text.append (String.format (" Global variables %04X %,6d%n", globalsOffset, - globalsOffset)); - - text.append ("\nStatic memory:\n"); - text.append (String.format (" Grammar table etc %04X %,6d%n", staticMemory, - staticMemory)); - text.append (String.format (" Dictionary %04X %,6d%n", dictionaryOffset, - dictionaryOffset)); - text.append ("\nHigh memory:\n"); - text.append ( - String.format (" ZCode %04X %,6d%n", highMemory, highMemory)); - text.append (String.format (" Program counter %04X %,6d%n", programCounter, - programCounter)); - text.append ( - String.format ("\nFile length %05X %,7d%n", fileLength, fileLength)); - text.append ( - String.format ("Checksum %04X %,6d%n", checksum, checksum)); - text.append (String.format ("%nZString offset %05X %,7d%n", stringPointer, - stringPointer)); - - text.append (String.format ("Total strings %d%n", - stringManager.strings.size ())); - text.append (String.format ("Total objects %d%n", - objectManager.getObjects ().size ())); - - text.append (getAlternate ()); - - return text.toString (); - } - - // ---------------------------------------------------------------------------------// - private String getAlternate () - // ---------------------------------------------------------------------------------// - { - StringBuilder text = new StringBuilder ("\n\n"); - - text.append (getLine (0, 1, "version")); - text.append (getLine (1, 3, "flags 1")); - text.append (getLine (4, 2, "high memory")); - text.append (getLine (6, 2, "program counter")); - text.append (getLine (8, 2, "dictionary")); - text.append (getLine (10, 2, "object table")); - text.append (getLine (12, 2, "global variables")); - text.append (getLine (14, 2, "static memory")); - text.append (getLine (16, 2, "flags 2")); - text.append (getLine (24, 2, "abbreviations table")); - text.append (getLine (26, 2, "length of file (x2 = " + fileLength + ")")); - text.append (getLine (28, 2, "checksum")); - text.append (getLine (50, 1, "revision number")); - return text.toString (); - } - - // ---------------------------------------------------------------------------------// - private String getLine (int offset, int size, String description) - // ---------------------------------------------------------------------------------// - { - StringBuilder text = new StringBuilder (); - text.append (String.format ("%04X - %04X ", offset, offset + size - 1)); - for (int i = 0; i < size; i++) - text.append (String.format ("%02X ", buffer[offset + i])); - while (text.length () < 24) - text.append (" "); - text.append (description); - text.append ("\n"); - return text.toString (); - } - - // ---------------------------------------------------------------------------------// - ZObject getObject (int index) - // ---------------------------------------------------------------------------------// - { - return objectManager.getObject (index); - } - - // ---------------------------------------------------------------------------------// - int getByte (int offset) - // ---------------------------------------------------------------------------------// - { - return buffer[offset] & 0xFF; - } - - // ---------------------------------------------------------------------------------// - int getWord (int offset) - // ---------------------------------------------------------------------------------// - { - return ((buffer[offset] << 8) & 0xFF00) | ((buffer[offset + 1]) & 0xFF); - } +package com.bytezone.diskbrowser.infocom; + +import java.io.File; + +import com.bytezone.diskbrowser.disk.Disk; + +// -----------------------------------------------------------------------------------// +class Header extends InfocomAbstractFile +// -----------------------------------------------------------------------------------// +{ + final String[] propertyNames = new String[32]; + + private final File file; + int version; + int highMemory; + int programCounter; + int dictionaryOffset; + int objectTableOffset; + int globalsOffset; + int staticMemory; + int abbreviationsTable; + int fileLength; + int checksum; + int stringPointer; + + final Abbreviations abbreviations; + final ObjectManager objectManager; + final Globals globals; + final Grammar grammar; + final Dictionary dictionary; + final CodeManager codeManager; + final StringManager stringManager; + + // ---------------------------------------------------------------------------------// + Header (String name, byte[] buffer, Disk disk) + // ---------------------------------------------------------------------------------// + { + super (name, buffer); + this.file = disk.getFile (); + + version = getByte (00); + highMemory = getWord (0x04); + programCounter = getWord (0x06); + + dictionaryOffset = getWord (0x08); + objectTableOffset = getWord (0x0A); + globalsOffset = getWord (0x0C); + staticMemory = getWord (0x0E); + abbreviationsTable = getWord (0x18); + + 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) + fileLength = buffer.length; + + // do the basic managers + abbreviations = new Abbreviations (this); + dictionary = new Dictionary (this); + globals = new Globals (this); // may display ZStrings + + // set up an empty object to store Routines in + codeManager = new CodeManager (this); + + grammar = new Grammar ("Grammar", buffer, this); + + // add all the ZObjects, and analyse them to find stringPtr, DICT etc. + objectManager = new ObjectManager (this); + + // add all the ZStrings + stringManager = new StringManager ("Strings", buffer, this); + + codeManager.addRoutines (programCounter); + + // add entries for AbstractFile.getHexDump () + hexBlocks.add (new HexBlock (0, 64, "Header data:")); + } + + // ---------------------------------------------------------------------------------// + String getPropertyName (int id) + // ---------------------------------------------------------------------------------// + { + return propertyNames[id]; + } + + // ---------------------------------------------------------------------------------// + public String getAbbreviation (int index) + // ---------------------------------------------------------------------------------// + { + return abbreviations.getAbbreviation (index); + } + + // ---------------------------------------------------------------------------------// + public boolean containsWordAt (int address) + // ---------------------------------------------------------------------------------// + { + return dictionary.containsWordAt (address); + } + + // ---------------------------------------------------------------------------------// + public String wordAt (int address) + // ---------------------------------------------------------------------------------// + { + return dictionary.wordAt (address); + } + + // ---------------------------------------------------------------------------------// + @Override + public String getText () + // ---------------------------------------------------------------------------------// + { + StringBuilder text = new StringBuilder (); + + text.append (String.format ("Disk name %s%n", file.getName ())); + text.append (String.format ("Version %d%n", version)); + text.append ("\nDynamic memory:\n"); + text.append (String.format (" Abbreviation table %04X %,6d%n", + abbreviationsTable, abbreviationsTable)); + text.append (String.format (" Objects table %04X %,6d%n", + objectTableOffset, objectTableOffset)); + text.append (String.format (" Global variables %04X %,6d%n", globalsOffset, + globalsOffset)); + + text.append ("\nStatic memory:\n"); + text.append (String.format (" Grammar table etc %04X %,6d%n", staticMemory, + staticMemory)); + text.append (String.format (" Dictionary %04X %,6d%n", dictionaryOffset, + dictionaryOffset)); + text.append ("\nHigh memory:\n"); + text.append ( + String.format (" ZCode %04X %,6d%n", highMemory, highMemory)); + text.append (String.format (" Program counter %04X %,6d%n", programCounter, + programCounter)); + text.append ( + String.format ("\nFile length %05X %,7d%n", fileLength, fileLength)); + text.append ( + String.format ("Checksum %04X %,6d%n", checksum, checksum)); + text.append (String.format ("%nZString offset %05X %,7d%n", stringPointer, + stringPointer)); + + text.append (String.format ("Total strings %d%n", + stringManager.strings.size ())); + text.append (String.format ("Total objects %d%n", + objectManager.getObjects ().size ())); + + text.append (getAlternate ()); + + return text.toString (); + } + + // ---------------------------------------------------------------------------------// + private String getAlternate () + // ---------------------------------------------------------------------------------// + { + StringBuilder text = new StringBuilder ("\n\n"); + + text.append (getLine (0, 1, "version")); + text.append (getLine (1, 3, "flags 1")); + text.append (getLine (4, 2, "high memory")); + text.append (getLine (6, 2, "program counter")); + text.append (getLine (8, 2, "dictionary")); + text.append (getLine (10, 2, "object table")); + text.append (getLine (12, 2, "global variables")); + text.append (getLine (14, 2, "static memory")); + text.append (getLine (16, 2, "flags 2")); + text.append (getLine (24, 2, "abbreviations table")); + text.append (getLine (26, 2, "length of file (x2 = " + fileLength + ")")); + text.append (getLine (28, 2, "checksum")); + text.append (getLine (50, 1, "revision number")); + return text.toString (); + } + + // ---------------------------------------------------------------------------------// + private String getLine (int offset, int size, String description) + // ---------------------------------------------------------------------------------// + { + StringBuilder text = new StringBuilder (); + text.append (String.format ("%04X - %04X ", offset, offset + size - 1)); + for (int i = 0; i < size; i++) + text.append (String.format ("%02X ", buffer[offset + i])); + while (text.length () < 24) + text.append (" "); + text.append (description); + text.append ("\n"); + return text.toString (); + } + + // ---------------------------------------------------------------------------------// + ZObject getObject (int index) + // ---------------------------------------------------------------------------------// + { + return objectManager.getObject (index); + } + + // ---------------------------------------------------------------------------------// + int getByte (int offset) + // ---------------------------------------------------------------------------------// + { + return buffer[offset] & 0xFF; + } + + // ---------------------------------------------------------------------------------// + int getWord (int offset) + // ---------------------------------------------------------------------------------// + { + return ((buffer[offset] << 8) & 0xFF00) | ((buffer[offset + 1]) & 0xFF); + } } \ No newline at end of file diff --git a/src/com/bytezone/diskbrowser/infocom/InfocomAbstractFile.java b/src/com/bytezone/diskbrowser/infocom/InfocomAbstractFile.java index b3c087e..a468012 100644 --- a/src/com/bytezone/diskbrowser/infocom/InfocomAbstractFile.java +++ b/src/com/bytezone/diskbrowser/infocom/InfocomAbstractFile.java @@ -7,13 +7,13 @@ import com.bytezone.diskbrowser.applefile.AbstractFile; import com.bytezone.diskbrowser.utilities.HexFormatter; // -----------------------------------------------------------------------------------// -public class InfocomAbstractFile extends AbstractFile +class InfocomAbstractFile extends AbstractFile // -----------------------------------------------------------------------------------// { protected List hexBlocks = new ArrayList<> (); // ---------------------------------------------------------------------------------// - public InfocomAbstractFile (String name, byte[] buffer) + InfocomAbstractFile (String name, byte[] buffer) // ---------------------------------------------------------------------------------// { super (name, buffer); diff --git a/src/com/bytezone/diskbrowser/infocom/InfocomDisk.java b/src/com/bytezone/diskbrowser/infocom/InfocomDisk.java index fe91390..0ab6f91 100755 --- a/src/com/bytezone/diskbrowser/infocom/InfocomDisk.java +++ b/src/com/bytezone/diskbrowser/infocom/InfocomDisk.java @@ -1,298 +1,324 @@ -package com.bytezone.diskbrowser.infocom; - -import java.awt.Color; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import javax.swing.JOptionPane; -import javax.swing.tree.DefaultMutableTreeNode; - -import com.bytezone.diskbrowser.applefile.AppleFileSource; -import com.bytezone.diskbrowser.disk.AbstractFormattedDisk; -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.gui.DataSource; -import com.bytezone.diskbrowser.utilities.HexFormatter; - -// https://mud.co.uk/richard/htflpism.htm -// https://inform-fiction.org/zmachine/standards/ -// https://github.com/historicalsource?tab=repositories - -public class InfocomDisk extends AbstractFormattedDisk -{ - private static final int BLOCK_SIZE = 256; - private static final boolean TYPE_NODE = true; - private static final boolean TYPE_LEAF = false; - private byte[] data; - private final Header header; - - Color green = new Color (0, 200, 0); - - SectorType bootSector = new SectorType ("ZIP 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); - - setInfocomSectorTypes (); - - data = disk.readSector (3, 0); // read first sector to get file size - data = getBuffer (getWord (26) * 2); // read entire file into data buffer - - if (false) - createStoryFile ("Zork1.sf"); - - DefaultMutableTreeNode root = getCatalogTreeRoot (); - DefaultMutableTreeNode headerNode = null; - DefaultMutableTreeNode abbreviationsNode = null; - DefaultMutableTreeNode codeNode = null; - DefaultMutableTreeNode objectNode = null; - DefaultMutableTreeNode globalsNode = null; - DefaultMutableTreeNode grammarNode = null; - DefaultMutableTreeNode dictionaryNode = null; - DefaultMutableTreeNode stringsNode = null; - - header = new Header ("Header", data, disk); - - headerNode = addToTree (root, "Header", header, TYPE_LEAF); - DefaultAppleFileSource dafs = (DefaultAppleFileSource) headerNode.getUserObject (); - List blocks = new ArrayList<> (); - blocks.add (disk.getDiskAddress (3, 0)); - dafs.setSectors (blocks); - - abbreviationsNode = - addToTree (root, "Abbreviations", header.abbreviations, TYPE_LEAF); - - objectNode = addToTree (root, "Objects", header.objectManager, TYPE_NODE); - header.objectManager.addNodes (objectNode, this); - - globalsNode = addToTree (root, "Globals", header.globals, TYPE_LEAF); - grammarNode = addToTree (root, "Grammar", header.grammar, TYPE_LEAF); - dictionaryNode = addToTree (root, "Dictionary", header.dictionary, TYPE_LEAF); - - codeNode = addToTree (root, "Code", header.codeManager, TYPE_NODE); - header.codeManager.addNodes (codeNode, this); - - stringsNode = addToTree (root, "Strings", header.stringManager, TYPE_LEAF); - - PropertyManager pm = new PropertyManager ("Properties", data, header); - pm.addNodes (addToTree (root, "Properties", pm, TYPE_NODE), this); - - AttributeManager am = new AttributeManager ("Attributes", data, header); - am.addNodes (addToTree (root, "Attributes", am, TYPE_NODE), this); - - sectorTypes[48] = headerSector; - - setSectorTypes (header.abbreviationsTable, header.objectTableOffset, - abbreviationsSector, abbreviationsNode); - setSectorTypes (header.objectTableOffset, header.globalsOffset, objectsSector, - objectNode); - setSectorTypes (header.globalsOffset, header.staticMemory, globalsSector, - globalsNode); - setSectorTypes (header.staticMemory, header.dictionaryOffset, grammarSector, - grammarNode); - setSectorTypes (header.dictionaryOffset, header.highMemory, dictionarySector, - dictionaryNode); - setSectorTypes (header.highMemory, header.stringPointer, codeSector, codeNode); - setSectorTypes (header.stringPointer, header.fileLength, stringsSector, stringsNode); - } - - protected void setInfocomSectorTypes () - { - 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; - } - - private void setSectorTypes (int sectorFrom, int sectorTo, SectorType type, - DefaultMutableTreeNode node) - { - DefaultAppleFileSource dafs = (DefaultAppleFileSource) node.getUserObject (); - List blocks = new ArrayList<> (); - - int blockNo = sectorFrom / disk.getBlockSize () + 48; - int blockTo = sectorTo / disk.getBlockSize () + 48; - while (blockNo <= blockTo) - { - blocks.add (disk.getDiskAddress (blockNo)); - if (!disk.isSectorEmpty (blockNo)) - sectorTypes[blockNo] = type; - blockNo++; - } - dafs.setSectors (blocks); - } - - 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)) - { - System.out.println ("Empty : " + da); - buffer = disk.readSector (da.getBlock () - 1); - fileSize = (da.getBlock () - 48) * disk.getBlockSize (); - break; - } - } - - if (buffer != null) - { - int ptr = 255; - while (buffer[ptr--] == 0) - fileSize--; - } - 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) - { - DefaultAppleFileSource dafs = new DefaultAppleFileSource (title, af, this); - - // dafs.setSectors (blocks); - DefaultMutableTreeNode node = new DefaultMutableTreeNode (dafs); - node.setAllowsChildren (allowsChildren); - root.add (node); - return node; - } - - @Override - public List 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 (true) - { - 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); - JOptionPane.showMessageDialog (null, - "This appears to be an Infocom disk," + " but version " + version - + " is not supported", - "Unknown disk format", JOptionPane.INFORMATION_MESSAGE); - 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 (); - } - } +package com.bytezone.diskbrowser.infocom; + +import java.awt.Color; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JOptionPane; +import javax.swing.tree.DefaultMutableTreeNode; + +import com.bytezone.diskbrowser.applefile.AppleFileSource; +import com.bytezone.diskbrowser.disk.AbstractFormattedDisk; +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.gui.DataSource; +import com.bytezone.diskbrowser.utilities.HexFormatter; + +// https://mud.co.uk/richard/htflpism.htm +// https://inform-fiction.org/zmachine/standards/ +// https://github.com/historicalsource?tab=repositories + +// -----------------------------------------------------------------------------------// +public class InfocomDisk extends AbstractFormattedDisk +// -----------------------------------------------------------------------------------// +{ + private static final int BLOCK_SIZE = 256; + private static final boolean TYPE_NODE = true; + private static final boolean TYPE_LEAF = false; + private byte[] data; + private final Header header; + + Color green = new Color (0, 200, 0); + + SectorType bootSector = new SectorType ("ZIP 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); + + setInfocomSectorTypes (); + + data = disk.readSector (3, 0); // read first sector to get file size + data = getBuffer (getWord (26) * 2); // read entire file into data buffer + + if (false) + createStoryFile ("Zork1.sf"); + + DefaultMutableTreeNode root = getCatalogTreeRoot (); + DefaultMutableTreeNode headerNode = null; + DefaultMutableTreeNode abbreviationsNode = null; + DefaultMutableTreeNode codeNode = null; + DefaultMutableTreeNode objectNode = null; + DefaultMutableTreeNode globalsNode = null; + DefaultMutableTreeNode grammarNode = null; + DefaultMutableTreeNode dictionaryNode = null; + DefaultMutableTreeNode stringsNode = null; + + header = new Header ("Header", data, disk); + + headerNode = addToTree (root, "Header", header, TYPE_LEAF); + DefaultAppleFileSource dafs = (DefaultAppleFileSource) headerNode.getUserObject (); + List blocks = new ArrayList<> (); + blocks.add (disk.getDiskAddress (3, 0)); + dafs.setSectors (blocks); + + abbreviationsNode = + addToTree (root, "Abbreviations", header.abbreviations, TYPE_LEAF); + + objectNode = addToTree (root, "Objects", header.objectManager, TYPE_NODE); + header.objectManager.addNodes (objectNode, this); + + globalsNode = addToTree (root, "Globals", header.globals, TYPE_LEAF); + grammarNode = addToTree (root, "Grammar", header.grammar, TYPE_LEAF); + dictionaryNode = addToTree (root, "Dictionary", header.dictionary, TYPE_LEAF); + + codeNode = addToTree (root, "Code", header.codeManager, TYPE_NODE); + header.codeManager.addNodes (codeNode, this); + + stringsNode = addToTree (root, "Strings", header.stringManager, TYPE_LEAF); + + PropertyManager pm = new PropertyManager ("Properties", data, header); + pm.addNodes (addToTree (root, "Properties", pm, TYPE_NODE), this); + + AttributeManager am = new AttributeManager ("Attributes", data, header); + am.addNodes (addToTree (root, "Attributes", am, TYPE_NODE), this); + + sectorTypes[48] = headerSector; + + setSectorTypes (header.abbreviationsTable, header.objectTableOffset, + abbreviationsSector, abbreviationsNode); + setSectorTypes (header.objectTableOffset, header.globalsOffset, objectsSector, + objectNode); + setSectorTypes (header.globalsOffset, header.staticMemory, globalsSector, + globalsNode); + setSectorTypes (header.staticMemory, header.dictionaryOffset, grammarSector, + grammarNode); + setSectorTypes (header.dictionaryOffset, header.highMemory, dictionarySector, + dictionaryNode); + setSectorTypes (header.highMemory, header.stringPointer, codeSector, codeNode); + setSectorTypes (header.stringPointer, header.fileLength, stringsSector, stringsNode); + } + + // ---------------------------------------------------------------------------------// + protected void setInfocomSectorTypes () + // ---------------------------------------------------------------------------------// + { + 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; + } + + // ---------------------------------------------------------------------------------// + private void setSectorTypes (int sectorFrom, int sectorTo, SectorType type, + DefaultMutableTreeNode node) + // ---------------------------------------------------------------------------------// + { + DefaultAppleFileSource dafs = (DefaultAppleFileSource) node.getUserObject (); + List blocks = new ArrayList<> (); + + int blockNo = sectorFrom / disk.getBlockSize () + 48; + int blockTo = sectorTo / disk.getBlockSize () + 48; + while (blockNo <= blockTo) + { + blocks.add (disk.getDiskAddress (blockNo)); + if (!disk.isSectorEmpty (blockNo)) + sectorTypes[blockNo] = type; + blockNo++; + } + dafs.setSectors (blocks); + } + + // ---------------------------------------------------------------------------------// + 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)) + { + System.out.println ("Empty : " + da); + buffer = disk.readSector (da.getBlock () - 1); + fileSize = (da.getBlock () - 48) * disk.getBlockSize (); + break; + } + } + + if (buffer != null) + { + int ptr = 255; + while (buffer[ptr--] == 0) + fileSize--; + } + 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) + // ---------------------------------------------------------------------------------// + { + DefaultAppleFileSource dafs = new DefaultAppleFileSource (title, af, this); + + // dafs.setSectors (blocks); + DefaultMutableTreeNode node = new DefaultMutableTreeNode (dafs); + node.setAllowsChildren (allowsChildren); + root.add (node); + return node; + } + + // ---------------------------------------------------------------------------------// + @Override + public List 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 (true) + { + 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); + JOptionPane.showMessageDialog (null, + "This appears to be an Infocom disk," + " but version " + version + + " is not supported", + "Unknown disk format", JOptionPane.INFORMATION_MESSAGE); + 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 (); + } + } } \ No newline at end of file diff --git a/src/com/bytezone/diskbrowser/infocom/Instruction.java b/src/com/bytezone/diskbrowser/infocom/Instruction.java index efb7144..71ce94b 100755 --- a/src/com/bytezone/diskbrowser/infocom/Instruction.java +++ b/src/com/bytezone/diskbrowser/infocom/Instruction.java @@ -1,547 +1,599 @@ -package com.bytezone.diskbrowser.infocom; - -import java.util.ArrayList; -import java.util.List; - -import com.bytezone.diskbrowser.utilities.HexFormatter; - -class Instruction -{ - static int version = 3; - - final Opcode opcode; - final int startPtr; - private byte[] buffer; - // List abbreviations; - private Header header; - - enum OperandType - { - VAR_SP, VAR_LOCAL, VAR_GLOBAL, BYTE, WORD, ARG_BRANCH, ARG_STRING, OBJECT - } - - static final String[] name2OP = - { "*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", "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" }; - - Instruction (byte[] buffer, int ptr, Header header) - { - this.buffer = buffer; - this.startPtr = ptr; - this.header = header; - byte b1 = buffer[ptr]; - - int type = (b1 & 0xC0) >>> 6; - switch (type) - { - case 0x03: // 11 - variable - if ((b1 & 0x20) == 0x20) - opcode = new OpcodeVar (buffer, ptr); - else - opcode = new Opcode2OPVar (buffer, ptr); - break; - - case 0x02: // 10 - extended or short - if (b1 == 0xBE && version >= 5) - opcode = null; - else if ((b1 & 0x30) == 0x30) - opcode = new Opcode0OP (buffer, ptr); - else - opcode = new Opcode1OP (buffer, ptr); - break; - - default: // 00, 01 - long - opcode = new Opcode2OPLong (buffer, ptr); - } - } - - int length () - { - return opcode.length (); - } - - boolean isReturn () - { - return opcode.isReturn; - } - - boolean isPrint () - { - return opcode.string != null; - } - - boolean isCall () - { - return opcode.isCall; - } - - boolean isJump () - { - // could use jumpTarget != 0 - return (opcode instanceof Opcode1OP && opcode.opcodeNumber == 12); - } - - boolean isBranch () - { - return opcode.branch != null; - } - - boolean isStore () - { - return opcode.store != null; - } - - int target () - { - return isBranch () ? opcode.branch.target : isJump () ? opcode.jumpTarget : 0; - } - - String dump () - { - return String.format ("%05X : %s", startPtr, - HexFormatter.getHexString (buffer, startPtr, opcode.length ())); - } - - String getHex () - { - int max = opcode.length (); - String extra = ""; - if (max > 9) - { - max = 9; - extra = ".."; - } - - String hex = HexFormatter.getHexString (buffer, startPtr, max); - return String.format ("%05X : %-26s%2s", startPtr, hex, extra); - } - - @Override - public String toString () - { - return opcode.toString (); - } - - abstract class Opcode - { - int opcodeNumber; - int opcodeLength; - List operands = new ArrayList<> (); - int totalOperandLength; - ArgumentBranch branch; - ArgumentString string; - OperandVariable store; - boolean isReturn, isCall, isExit; - int jumpTarget; - int callTarget; - - @Override - public String toString () - { - StringBuilder text = new StringBuilder (); - - text.append (String.format ("%-12s", 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 (); - } - - 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; - } - - abstract String opcodeName (); - - private void addOperand (Operand operand) - { - operands.add (operand); - totalOperandLength += operand.length; - } - - void addOperand (byte[] buffer, int ptr, boolean bit1, boolean bit2) - { - int offset = ptr + totalOperandLength; - if (bit1) - { - 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 - } - - void addOperand (byte[] buffer, int ptr, boolean bit) - { - int address = ptr + totalOperandLength; - if (address >= buffer.length) - { - System.out.println ("Illegal byte address : " + address); - return; - } - if (bit) - addOperand (new OperandVariable (buffer[address])); - else - addOperand (new OperandByte (buffer[address])); - } - - void setVariableOperands (int ptr) - { - 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; - } - } - - void setStore (byte[] buffer) - { - store = new OperandVariable (buffer[startPtr + totalOperandLength + opcodeLength]); - } - - void setBranch (byte[] buffer) - { - int offset = startPtr + totalOperandLength + (store == null ? 0 : 1) + opcodeLength; - if ((buffer[offset] & 0x40) != 0) - branch = new ArgumentBranch (buffer[offset], offset); - else - branch = new ArgumentBranch (header.getWord (offset), offset); - } - - void setZString (byte[] buffer) - { - int offset = startPtr + totalOperandLength + opcodeLength; - string = new ArgumentString (buffer, offset); - } - } - - class Opcode0OP extends Opcode - { - Opcode0OP (byte[] buffer, int ptr) - { - opcodeNumber = buffer[ptr] & 0x0F; - opcodeLength = 1; - - if (opcodeNumber == 5 || opcodeNumber == 6 || opcodeNumber == 13) - setBranch (buffer); - - if (opcodeNumber == 0 || opcodeNumber == 1 || opcodeNumber == 3 - || opcodeNumber == 8) - isReturn = true; - - if (opcodeNumber == 2 || opcodeNumber == 3) - setZString (buffer); - - if (opcodeNumber == 7 || opcodeNumber == 10) - isExit = true; - } - - @Override - public String opcodeName () - { - return name0OP[opcodeNumber]; - } - } - - class Opcode1OP extends Opcode - { - Opcode1OP (byte[] buffer, int ptr) - { - opcodeNumber = buffer[ptr] & 0x0F; - opcodeLength = 1; - - boolean bit1 = ((buffer[ptr] & 0x20) == 0x20); - boolean bit2 = ((buffer[ptr] & 0x10) == 0x10); - addOperand (buffer, ptr + 1, bit1, bit2); - - if ((opcodeNumber >= 1 && opcodeNumber <= 4) || opcodeNumber == 8 - || opcodeNumber == 14 || opcodeNumber == 15) - setStore (buffer); - if (opcodeNumber <= 2) - setBranch (buffer); - if (opcodeNumber == 12) - jumpTarget = (short) operands.get (0).value + startPtr - 2 + length (); - if (opcodeNumber == 11) - isReturn = true; - } - - @Override - public String opcodeName () - { - return name1OP[opcodeNumber]; - } - } - - abstract class Opcode2OP extends Opcode - { - Opcode2OP () - { - opcodeLength = 1; - } - - void setArguments (byte[] buffer) - { - if ((opcodeNumber >= 1 && opcodeNumber <= 7) || opcodeNumber == 10) - setBranch (buffer); - else if ((opcodeNumber >= 15 && opcodeNumber <= 25) || opcodeNumber == 8 - || opcodeNumber == 9) - setStore (buffer); - } - - @Override - public String opcodeName () - { - return name2OP[opcodeNumber]; - } - } - - class Opcode2OPLong extends Opcode2OP - { - Opcode2OPLong (byte[] buffer, int ptr) - { - opcodeNumber = buffer[ptr] & 0x1F; - boolean bit1 = ((buffer[ptr] & 0x40) == 0x40); - boolean bit2 = ((buffer[ptr] & 0x20) == 0x20); - - if (opcodeNumber == 0x0D) // store (variable) value - { - addOperand (buffer, ptr + 1, true); // always a variable - addOperand (buffer, ptr + 1, bit2); - } - else - { - addOperand (buffer, ptr + 1, bit1); - addOperand (buffer, ptr + 1, bit2); - } - - setArguments (buffer); - } - } - - class Opcode2OPVar extends Opcode2OP - { - Opcode2OPVar (byte[] buffer, int ptr) - { - opcodeNumber = buffer[ptr] & 0x1F; - opcodeLength = 2; - setVariableOperands (ptr); - setArguments (buffer); - } - } - - class OpcodeVar extends Opcode - { - OpcodeVar (byte[] buffer, int ptr) - { - opcodeNumber = buffer[ptr] & 0x1F; - opcodeLength = 2; - setVariableOperands (ptr); - - if (opcodeNumber == 0 || opcodeNumber == 7) - setStore (buffer); - - if (opcodeNumber == 0) // call routine - { - isCall = true; - callTarget = operands.get (0).value * 2; - } - - if (opcodeNumber == 3) // put prop object propertyValue - { - // first parameter is the object id - operands.get (0).operandType = OperandType.OBJECT; - } - } - - @Override - public String opcodeName () - { - return nameVAR[opcodeNumber]; - } - } - - abstract class Operand - { - int length; - int value; - OperandType operandType; - - @Override - public String toString () - { - switch (operandType) - { - case VAR_SP: - return ("(SP)"); - - case VAR_LOCAL: - return (String.format ("L%02X", value - 1)); - - case VAR_GLOBAL: - return String.format ("G%02X", (value - 16)); - - case BYTE: - return String.format ("#%02X", value); - - case WORD: - return String.format ("#%04X", value); - - case OBJECT: - return "\"" + header.objectManager.getObject (value - 1).getName () + "\""; - - default: - return "*** Illegal ***"; - } - } - } - - class OperandWord extends Operand - { - OperandWord (int value) - { - this.value = value; - length = 2; - operandType = OperandType.WORD; - } - } - - class OperandByte extends Operand - { - OperandByte (byte value) - { - this.value = value & 0xFF; - length = 1; - operandType = OperandType.BYTE; - } - } - - class OperandVariable extends Operand - { - OperandVariable (byte value) - { - this.value = value & 0xFF; - length = 1; - - if (this.value == 0) - operandType = OperandType.VAR_SP; - else if (this.value <= 15) - operandType = OperandType.VAR_LOCAL; - else - operandType = OperandType.VAR_GLOBAL; - } - } - - class ArgumentBranch extends Operand - { - private int target; - private boolean branchOnTrue; - - ArgumentBranch (byte value, int offset) - { - branchOnTrue = (value & 0x80) != 0; - int val = value & 0x3F; // 0 - 63 - if (val <= 1) - target = val; - else - target = val + offset - 1; - length = 1; - operandType = OperandType.ARG_BRANCH; - } - - ArgumentBranch (int value, int offset) - { - branchOnTrue = (value & 0x8000) != 0; - int val = ((value & 0x3FFF) << 18) >> 18; // signed 14-bit number - - target = val + offset; - length = 2; - operandType = OperandType.ARG_BRANCH; - } - - @Override - public String toString () - { - StringBuilder text = new StringBuilder (); - 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 (); - } - } - - class ArgumentString extends Operand - { - private ZString text; - private int startPtr; - private byte[] buffer; - - 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; - } - } +package com.bytezone.diskbrowser.infocom; + +import java.util.ArrayList; +import java.util.List; + +import com.bytezone.diskbrowser.utilities.HexFormatter; + +// -----------------------------------------------------------------------------------// +class Instruction +// -----------------------------------------------------------------------------------// +{ + static int version = 3; + + final Opcode opcode; + final int startPtr; + private byte[] buffer; + // List abbreviations; + private Header header; + + enum OperandType + { + VAR_SP, VAR_LOCAL, VAR_GLOBAL, BYTE, WORD, ARG_BRANCH, ARG_STRING, OBJECT + } + + static final String[] name2OP = + { "*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", "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" }; + + // ---------------------------------------------------------------------------------// + Instruction (byte[] buffer, int ptr, Header header) + // ---------------------------------------------------------------------------------// + { + this.buffer = buffer; + this.startPtr = ptr; + this.header = header; + byte b1 = buffer[ptr]; + + int type = (b1 & 0xC0) >>> 6; + switch (type) + { + case 0x03: // 11 - variable + if ((b1 & 0x20) == 0x20) + opcode = new OpcodeVar (buffer, ptr); + else + opcode = new Opcode2OPVar (buffer, ptr); + break; + + case 0x02: // 10 - extended or short + if (b1 == 0xBE && version >= 5) + opcode = null; + else if ((b1 & 0x30) == 0x30) + opcode = new Opcode0OP (buffer, ptr); + else + opcode = new Opcode1OP (buffer, ptr); + break; + + default: // 00, 01 - long + opcode = new Opcode2OPLong (buffer, ptr); + } + } + + // ---------------------------------------------------------------------------------// + int length () + // ---------------------------------------------------------------------------------// + { + return opcode.length (); + } + + // ---------------------------------------------------------------------------------// + boolean isReturn () + // ---------------------------------------------------------------------------------// + { + return opcode.isReturn; + } + + // ---------------------------------------------------------------------------------// + boolean isPrint () + // ---------------------------------------------------------------------------------// + { + return opcode.string != null; + } + + // ---------------------------------------------------------------------------------// + boolean isCall () + // ---------------------------------------------------------------------------------// + { + return opcode.isCall; + } + + // ---------------------------------------------------------------------------------// + boolean isJump () + // ---------------------------------------------------------------------------------// + { + // could use jumpTarget != 0 + return (opcode instanceof Opcode1OP && opcode.opcodeNumber == 12); + } + + // ---------------------------------------------------------------------------------// + boolean isBranch () + // ---------------------------------------------------------------------------------// + { + return opcode.branch != null; + } + + // ---------------------------------------------------------------------------------// + boolean isStore () + // ---------------------------------------------------------------------------------// + { + return opcode.store != null; + } + + // ---------------------------------------------------------------------------------// + int target () + // ---------------------------------------------------------------------------------// + { + return isBranch () ? opcode.branch.target : isJump () ? opcode.jumpTarget : 0; + } + + // ---------------------------------------------------------------------------------// + String dump () + // ---------------------------------------------------------------------------------// + { + return String.format ("%05X : %s", startPtr, + HexFormatter.getHexString (buffer, startPtr, opcode.length ())); + } + + // ---------------------------------------------------------------------------------// + String getHex () + // ---------------------------------------------------------------------------------// + { + int max = opcode.length (); + String extra = ""; + if (max > 9) + { + max = 9; + extra = ".."; + } + + String hex = HexFormatter.getHexString (buffer, startPtr, max); + return String.format ("%05X : %-26s%2s", startPtr, hex, extra); + } + + // ---------------------------------------------------------------------------------// + @Override + public String toString () + // ---------------------------------------------------------------------------------// + { + return opcode.toString (); + } + + // ---------------------------------------------------------------------------------// + abstract class Opcode + // ---------------------------------------------------------------------------------// + { + int opcodeNumber; + int opcodeLength; + List operands = new ArrayList<> (); + int totalOperandLength; + ArgumentBranch branch; + ArgumentString string; + OperandVariable store; + boolean isReturn, isCall, isExit; + int jumpTarget; + int callTarget; + + @Override + public String toString () + { + StringBuilder text = new StringBuilder (); + + text.append (String.format ("%-12s", 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 (); + } + + 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; + } + + abstract String opcodeName (); + + private void addOperand (Operand operand) + { + operands.add (operand); + totalOperandLength += operand.length; + } + + void addOperand (byte[] buffer, int ptr, boolean bit1, boolean bit2) + { + int offset = ptr + totalOperandLength; + if (bit1) + { + 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 + } + + void addOperand (byte[] buffer, int ptr, boolean bit) + { + int address = ptr + totalOperandLength; + if (address >= buffer.length) + { + System.out.println ("Illegal byte address : " + address); + return; + } + if (bit) + addOperand (new OperandVariable (buffer[address])); + else + addOperand (new OperandByte (buffer[address])); + } + + void setVariableOperands (int ptr) + { + 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; + } + } + + void setStore (byte[] buffer) + { + store = new OperandVariable (buffer[startPtr + totalOperandLength + opcodeLength]); + } + + void setBranch (byte[] buffer) + { + int offset = startPtr + totalOperandLength + (store == null ? 0 : 1) + opcodeLength; + if ((buffer[offset] & 0x40) != 0) + branch = new ArgumentBranch (buffer[offset], offset); + else + branch = new ArgumentBranch (header.getWord (offset), offset); + } + + void setZString (byte[] buffer) + { + int offset = startPtr + totalOperandLength + opcodeLength; + string = new ArgumentString (buffer, offset); + } + } + + // ---------------------------------------------------------------------------------// + class Opcode0OP extends Opcode + // ---------------------------------------------------------------------------------// + { + Opcode0OP (byte[] buffer, int ptr) + { + opcodeNumber = buffer[ptr] & 0x0F; + opcodeLength = 1; + + if (opcodeNumber == 5 || opcodeNumber == 6 || opcodeNumber == 13) + setBranch (buffer); + + if (opcodeNumber == 0 || opcodeNumber == 1 || opcodeNumber == 3 + || opcodeNumber == 8) + isReturn = true; + + if (opcodeNumber == 2 || opcodeNumber == 3) + setZString (buffer); + + if (opcodeNumber == 7 || opcodeNumber == 10) + isExit = true; + } + + @Override + public String opcodeName () + { + return name0OP[opcodeNumber]; + } + } + + // ---------------------------------------------------------------------------------// + class Opcode1OP extends Opcode + // ---------------------------------------------------------------------------------// + { + Opcode1OP (byte[] buffer, int ptr) + { + opcodeNumber = buffer[ptr] & 0x0F; + opcodeLength = 1; + + boolean bit1 = ((buffer[ptr] & 0x20) == 0x20); + boolean bit2 = ((buffer[ptr] & 0x10) == 0x10); + addOperand (buffer, ptr + 1, bit1, bit2); + + if ((opcodeNumber >= 1 && opcodeNumber <= 4) || opcodeNumber == 8 + || opcodeNumber == 14 || opcodeNumber == 15) + setStore (buffer); + if (opcodeNumber <= 2) + setBranch (buffer); + if (opcodeNumber == 12) + jumpTarget = (short) operands.get (0).value + startPtr - 2 + length (); + if (opcodeNumber == 11) + isReturn = true; + } + + @Override + public String opcodeName () + { + return name1OP[opcodeNumber]; + } + } + + // ---------------------------------------------------------------------------------// + abstract class Opcode2OP extends Opcode + // ---------------------------------------------------------------------------------// + { + Opcode2OP () + { + opcodeLength = 1; + } + + void setArguments (byte[] buffer) + { + if ((opcodeNumber >= 1 && opcodeNumber <= 7) || opcodeNumber == 10) + setBranch (buffer); + else if ((opcodeNumber >= 15 && opcodeNumber <= 25) || opcodeNumber == 8 + || opcodeNumber == 9) + setStore (buffer); + } + + @Override + public String opcodeName () + { + return name2OP[opcodeNumber]; + } + } + + // ---------------------------------------------------------------------------------// + class Opcode2OPLong extends Opcode2OP + // ---------------------------------------------------------------------------------// + { + Opcode2OPLong (byte[] buffer, int ptr) + { + opcodeNumber = buffer[ptr] & 0x1F; + boolean bit1 = ((buffer[ptr] & 0x40) == 0x40); + boolean bit2 = ((buffer[ptr] & 0x20) == 0x20); + + if (opcodeNumber == 0x0D) // store (variable) value + { + addOperand (buffer, ptr + 1, true); // always a variable + addOperand (buffer, ptr + 1, bit2); + } + else + { + addOperand (buffer, ptr + 1, bit1); + addOperand (buffer, ptr + 1, bit2); + } + + setArguments (buffer); + } + } + + // ---------------------------------------------------------------------------------// + class Opcode2OPVar extends Opcode2OP + // ---------------------------------------------------------------------------------// + { + Opcode2OPVar (byte[] buffer, int ptr) + { + opcodeNumber = buffer[ptr] & 0x1F; + opcodeLength = 2; + setVariableOperands (ptr); + setArguments (buffer); + } + } + + // ---------------------------------------------------------------------------------// + class OpcodeVar extends Opcode + // ---------------------------------------------------------------------------------// + { + OpcodeVar (byte[] buffer, int ptr) + { + opcodeNumber = buffer[ptr] & 0x1F; + opcodeLength = 2; + setVariableOperands (ptr); + + if (opcodeNumber == 0 || opcodeNumber == 7) + setStore (buffer); + + if (opcodeNumber == 0) // call routine + { + isCall = true; + callTarget = operands.get (0).value * 2; + } + + if (opcodeNumber == 3) // put prop object propertyValue + { + // first parameter is the object id + operands.get (0).operandType = OperandType.OBJECT; + } + } + + @Override + public String opcodeName () + { + return nameVAR[opcodeNumber]; + } + } + + // ---------------------------------------------------------------------------------// + abstract class Operand + // ---------------------------------------------------------------------------------// + { + int length; + int value; + OperandType operandType; + + @Override + public String toString () + { + switch (operandType) + { + case VAR_SP: + return ("(SP)"); + + case VAR_LOCAL: + return (String.format ("L%02X", value - 1)); + + case VAR_GLOBAL: + return String.format ("G%02X", (value - 16)); + + case BYTE: + return String.format ("#%02X", value); + + case WORD: + return String.format ("#%04X", value); + + case OBJECT: + return "\"" + header.objectManager.getObject (value - 1).getName () + "\""; + + default: + return "*** Illegal ***"; + } + } + } + + // ---------------------------------------------------------------------------------// + class OperandWord extends Operand + // ---------------------------------------------------------------------------------// + { + OperandWord (int value) + { + this.value = value; + length = 2; + operandType = OperandType.WORD; + } + } + + // ---------------------------------------------------------------------------------// + class OperandByte extends Operand + // ---------------------------------------------------------------------------------// + { + OperandByte (byte value) + { + this.value = value & 0xFF; + length = 1; + operandType = OperandType.BYTE; + } + } + + // ---------------------------------------------------------------------------------// + class OperandVariable extends Operand + // ---------------------------------------------------------------------------------// + { + OperandVariable (byte value) + { + this.value = value & 0xFF; + length = 1; + + if (this.value == 0) + operandType = OperandType.VAR_SP; + else if (this.value <= 15) + operandType = OperandType.VAR_LOCAL; + else + operandType = OperandType.VAR_GLOBAL; + } + } + + // ---------------------------------------------------------------------------------// + class ArgumentBranch extends Operand + // ---------------------------------------------------------------------------------// + { + private int target; + private boolean branchOnTrue; + + ArgumentBranch (byte value, int offset) + { + branchOnTrue = (value & 0x80) != 0; + int val = value & 0x3F; // 0 - 63 + if (val <= 1) + target = val; + else + target = val + offset - 1; + length = 1; + operandType = OperandType.ARG_BRANCH; + } + + ArgumentBranch (int value, int offset) + { + branchOnTrue = (value & 0x8000) != 0; + int val = ((value & 0x3FFF) << 18) >> 18; // signed 14-bit number + + target = val + offset; + length = 2; + operandType = OperandType.ARG_BRANCH; + } + + @Override + public String toString () + { + StringBuilder text = new StringBuilder (); + 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 (); + } + } + + // ---------------------------------------------------------------------------------// + class ArgumentString extends Operand + // ---------------------------------------------------------------------------------// + { + private ZString text; + private int startPtr; + private byte[] buffer; + + 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; + } + } } \ No newline at end of file diff --git a/src/com/bytezone/diskbrowser/infocom/ObjectAnalyser.java b/src/com/bytezone/diskbrowser/infocom/ObjectAnalyser.java index b3110e0..6674b5b 100644 --- a/src/com/bytezone/diskbrowser/infocom/ObjectAnalyser.java +++ b/src/com/bytezone/diskbrowser/infocom/ObjectAnalyser.java @@ -6,14 +6,18 @@ import java.util.List; import com.bytezone.diskbrowser.infocom.ZObject.Property; +// -----------------------------------------------------------------------------------// class ObjectAnalyser +// -----------------------------------------------------------------------------------// { Header header; ObjectManager parent; List list = new ArrayList (); List routines = new ArrayList (); + // ---------------------------------------------------------------------------------// public ObjectAnalyser (Header header, ObjectManager parent) + // ---------------------------------------------------------------------------------// { this.header = header; this.parent = parent; @@ -31,7 +35,9 @@ class ObjectAnalyser checkThreeByteProperties (); } + // ---------------------------------------------------------------------------------// public void setStringPointer () + // ---------------------------------------------------------------------------------// { PropertyTester pt = new PropertyTester (parent.getObjects ()); pt.addTest (new LengthTwoCondition ()); @@ -58,7 +64,9 @@ class ObjectAnalyser } } + // ---------------------------------------------------------------------------------// public void createPropertyLinks () + // ---------------------------------------------------------------------------------// { int sCount = 0; int rCount = 0; @@ -85,7 +93,9 @@ class ObjectAnalyser System.out.println ("Routines found : " + totRoutines); } + // ---------------------------------------------------------------------------------// private void checkThreeByteProperties () + // ---------------------------------------------------------------------------------// { System.out.printf ("Checking %d objects%n", parent.getObjects ().size ()); for (ZObject object : parent.getObjects ()) @@ -106,7 +116,9 @@ class ObjectAnalyser } // find the property with only dictionary entries + // ---------------------------------------------------------------------------------// public void setDictionary () + // ---------------------------------------------------------------------------------// { PropertyTester pt = new PropertyTester (parent.getObjects ()); pt.addTest (new LengthEvenCondition ()); @@ -118,7 +130,9 @@ class ObjectAnalyser header.propertyNames[i] = "DICT"; // SYNONYM } + // ---------------------------------------------------------------------------------// class Statistics implements Comparable + // ---------------------------------------------------------------------------------// { int propertyNumber; int lo; @@ -153,7 +167,9 @@ class ObjectAnalyser } } + // ---------------------------------------------------------------------------------// class LengthTwoCondition extends Condition + // ---------------------------------------------------------------------------------// { @Override boolean test (Property property) @@ -162,7 +178,9 @@ class ObjectAnalyser } } + // ---------------------------------------------------------------------------------// class LengthThreeCondition extends Condition + // ---------------------------------------------------------------------------------// { @Override boolean test (Property property) @@ -171,7 +189,9 @@ class ObjectAnalyser } } + // ---------------------------------------------------------------------------------// class LengthEvenCondition extends Condition + // ---------------------------------------------------------------------------------// { @Override boolean test (Property property) @@ -180,7 +200,9 @@ class ObjectAnalyser } } + // ---------------------------------------------------------------------------------// class HighMemoryCondition extends Condition + // ---------------------------------------------------------------------------------// { int lo, hi; Statistics[] statistics = new Statistics[32]; // note there is no property #0 @@ -202,7 +224,9 @@ class ObjectAnalyser } } + // ---------------------------------------------------------------------------------// class ValidDictionaryCondition extends Condition + // ---------------------------------------------------------------------------------// { @Override boolean test (Property property) diff --git a/src/com/bytezone/diskbrowser/infocom/ObjectManager.java b/src/com/bytezone/diskbrowser/infocom/ObjectManager.java index 57b2b41..0e07c00 100755 --- a/src/com/bytezone/diskbrowser/infocom/ObjectManager.java +++ b/src/com/bytezone/diskbrowser/infocom/ObjectManager.java @@ -1,133 +1,143 @@ -package com.bytezone.diskbrowser.infocom; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -import javax.swing.tree.DefaultMutableTreeNode; - -import com.bytezone.diskbrowser.disk.DefaultAppleFileSource; -import com.bytezone.diskbrowser.disk.FormattedDisk; - -class ObjectManager extends InfocomAbstractFile implements Iterable -{ - // private final Header header; - private final List list; - private List sortedList; - private final int defaultsPtr, defaultsSize; - private final int tablePtr, tableSize; - private final int propertyPtr, propertySize; - private final ObjectAnalyser analyser; - - public ObjectManager (Header header) - { - super ("Objects", header.buffer); - // this.header = header; - - defaultsPtr = header.objectTableOffset; - defaultsSize = 62; // 31 words - tablePtr = header.objectTableOffset + 62; - propertyPtr = header.getWord (tablePtr + 7); - propertySize = header.globalsOffset - propertyPtr; - tableSize = (propertyPtr - tablePtr); - int totalObjects = tableSize / ZObject.HEADER_SIZE; - list = new ArrayList<> (tableSize); - - for (int objectNo = 0; objectNo < totalObjects; objectNo++) - list.add (new ZObject (null, buffer, tablePtr + objectNo * ZObject.HEADER_SIZE, - objectNo + 1, header)); - - // analyse objects - set stringPtr etc. - analyser = new ObjectAnalyser (header, this); - - // add entries for AbstractFile.getHexDump () - hexBlocks.add (new HexBlock (defaultsPtr, defaultsSize, "Property defaults:")); - hexBlocks.add (new HexBlock (tablePtr, tableSize, "Objects table:")); - hexBlocks.add (new HexBlock (propertyPtr, propertySize, "Properties:")); - } - - List getObjects () - { - return list; - } - - ZObject getObject (int index) - { - if (index < 0 || index >= list.size ()) - { - System.out.printf ("Invalid index: %d / %d%n", index, list.size ()); - return null; - } - return list.get (index); - } - - public void addNodes (DefaultMutableTreeNode root, FormattedDisk disk) - { - root.setAllowsChildren (true); - - for (ZObject zo : list) - if (zo.parent == 0) - buildObjectTree (zo, root, disk); - } - - private void buildObjectTree (ZObject object, DefaultMutableTreeNode parentNode, - FormattedDisk disk) - { - DefaultMutableTreeNode child = new DefaultMutableTreeNode ( - new DefaultAppleFileSource (object.getName (), object, disk)); - parentNode.add (child); - if (object.sibling > 0) - buildObjectTree (list.get (object.sibling - 1), parentNode, disk); - if (object.child > 0) - buildObjectTree (list.get (object.child - 1), child, disk); - else - child.setAllowsChildren (false); - } - - public List getCodeRoutines () - { - return analyser.routines; - } - - @Override - public String getText () - { - // String header1 = "ID Attributes Pr Sb Ch Prop Title\n-- -----------" - // + " -- -- -- ----- -----------------------------\n"; - String underline = " ----------------------------------------"; - String titles[] = - { "ID ", "Title ", - "Parent ", - "Sibling ", - "Child ", "Attributes Prop\n" }; - String header2 = titles[0] + titles[1] + titles[2] + titles[3] + titles[4] + titles[5] - + "-- " + underline + underline + underline + underline + " ----------- -----\n"; - StringBuilder text = new StringBuilder (header2); - - if (sortedList == null) - sortedList = new ArrayList<> (list); - Collections.sort (sortedList); - - // int objectNumber = 0; - for (ZObject zo : list) - // if (false) - // text.append (String.format ("%02X %s%n", ++objectNumber, zo)); - // else - text.append (String.format ("%02X %s%n", zo.getId (), 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); - return text.toString (); - } - - @Override - public Iterator iterator () - { - return list.iterator (); - } +package com.bytezone.diskbrowser.infocom; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import javax.swing.tree.DefaultMutableTreeNode; + +import com.bytezone.diskbrowser.disk.DefaultAppleFileSource; +import com.bytezone.diskbrowser.disk.FormattedDisk; + +// -----------------------------------------------------------------------------------// +class ObjectManager extends InfocomAbstractFile implements Iterable +// -----------------------------------------------------------------------------------// +{ + private final List list; + private List sortedList; + private final int defaultsPtr, defaultsSize; + private final int tablePtr, tableSize; + private final int propertyPtr, propertySize; + private final ObjectAnalyser analyser; + + // ---------------------------------------------------------------------------------// + ObjectManager (Header header) + // ---------------------------------------------------------------------------------// + { + super ("Objects", header.buffer); + + defaultsPtr = header.objectTableOffset; + defaultsSize = 62; // 31 words + tablePtr = header.objectTableOffset + 62; + propertyPtr = header.getWord (tablePtr + 7); + propertySize = header.globalsOffset - propertyPtr; + tableSize = (propertyPtr - tablePtr); + int totalObjects = tableSize / ZObject.HEADER_SIZE; + list = new ArrayList<> (tableSize); + + for (int objectNo = 0; objectNo < totalObjects; objectNo++) + list.add (new ZObject (null, buffer, tablePtr + objectNo * ZObject.HEADER_SIZE, + objectNo + 1, header)); + + // analyse objects - set stringPtr etc. + analyser = new ObjectAnalyser (header, this); + + // add entries for AbstractFile.getHexDump () + hexBlocks.add (new HexBlock (defaultsPtr, defaultsSize, "Property defaults:")); + hexBlocks.add (new HexBlock (tablePtr, tableSize, "Objects table:")); + hexBlocks.add (new HexBlock (propertyPtr, propertySize, "Properties:")); + } + + // ---------------------------------------------------------------------------------// + List getObjects () + // ---------------------------------------------------------------------------------// + { + return list; + } + + // ---------------------------------------------------------------------------------// + ZObject getObject (int index) + // ---------------------------------------------------------------------------------// + { + if (index < 0 || index >= list.size ()) + { + System.out.printf ("Invalid index: %d / %d%n", index, list.size ()); + return null; + } + return list.get (index); + } + + // ---------------------------------------------------------------------------------// + public void addNodes (DefaultMutableTreeNode root, FormattedDisk disk) + // ---------------------------------------------------------------------------------// + { + root.setAllowsChildren (true); + + for (ZObject zo : list) + if (zo.parent == 0) + buildObjectTree (zo, root, disk); + } + + // ---------------------------------------------------------------------------------// + private void buildObjectTree (ZObject object, DefaultMutableTreeNode parentNode, + FormattedDisk disk) + // ---------------------------------------------------------------------------------// + { + DefaultMutableTreeNode child = new DefaultMutableTreeNode ( + new DefaultAppleFileSource (object.getName (), object, disk)); + parentNode.add (child); + if (object.sibling > 0) + buildObjectTree (list.get (object.sibling - 1), parentNode, disk); + if (object.child > 0) + buildObjectTree (list.get (object.child - 1), child, disk); + else + child.setAllowsChildren (false); + } + + // ---------------------------------------------------------------------------------// + public List getCodeRoutines () + // ---------------------------------------------------------------------------------// + { + return analyser.routines; + } + + // ---------------------------------------------------------------------------------// + @Override + public String getText () + // ---------------------------------------------------------------------------------// + { + String underline = " ----------------------------------------"; + String titles[] = + { "ID ", "Title ", + "Parent ", + "Sibling ", + "Child ", "Attributes Prop\n" }; + String header2 = titles[0] + titles[1] + titles[2] + titles[3] + titles[4] + titles[5] + + "-- " + underline + underline + underline + underline + " ----------- -----\n"; + StringBuilder text = new StringBuilder (header2); + + if (sortedList == null) + sortedList = new ArrayList<> (list); + Collections.sort (sortedList); + + for (ZObject zo : list) + text.append (String.format ("%02X %s%n", zo.getId (), 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); + return text.toString (); + } + + // ---------------------------------------------------------------------------------// + @Override + public Iterator iterator () + // ---------------------------------------------------------------------------------// + { + return list.iterator (); + } } \ No newline at end of file diff --git a/src/com/bytezone/diskbrowser/infocom/PropertyManager.java b/src/com/bytezone/diskbrowser/infocom/PropertyManager.java index 2e5d0fa..14016c6 100644 --- a/src/com/bytezone/diskbrowser/infocom/PropertyManager.java +++ b/src/com/bytezone/diskbrowser/infocom/PropertyManager.java @@ -9,12 +9,16 @@ import com.bytezone.diskbrowser.applefile.AbstractFile; import com.bytezone.diskbrowser.disk.DefaultAppleFileSource; import com.bytezone.diskbrowser.disk.FormattedDisk; +// -----------------------------------------------------------------------------------// class PropertyManager extends AbstractFile +// -----------------------------------------------------------------------------------// { List list = new ArrayList<> (); Header header; - public PropertyManager (String name, byte[] buffer, Header header) + // ---------------------------------------------------------------------------------// + PropertyManager (String name, byte[] buffer, Header header) + // ---------------------------------------------------------------------------------// { super (name, buffer); this.header = header; @@ -26,7 +30,9 @@ class PropertyManager extends AbstractFile } } + // ---------------------------------------------------------------------------------// public void addNodes (DefaultMutableTreeNode node, FormattedDisk disk) + // ---------------------------------------------------------------------------------// { node.setAllowsChildren (true); @@ -41,8 +47,10 @@ class PropertyManager extends AbstractFile } } + // ---------------------------------------------------------------------------------// @Override public String getText () + // ---------------------------------------------------------------------------------// { StringBuilder text = new StringBuilder ("Property Type Frequency\n"); text.append ("-------- ----- ---------\n"); @@ -54,7 +62,9 @@ class PropertyManager extends AbstractFile return text.toString (); } + // ---------------------------------------------------------------------------------// private class Statistic + // ---------------------------------------------------------------------------------// { int id; List list = new ArrayList<> (); diff --git a/src/com/bytezone/diskbrowser/infocom/PropertyTester.java b/src/com/bytezone/diskbrowser/infocom/PropertyTester.java index 38a1710..b319d32 100644 --- a/src/com/bytezone/diskbrowser/infocom/PropertyTester.java +++ b/src/com/bytezone/diskbrowser/infocom/PropertyTester.java @@ -6,23 +6,31 @@ import java.util.List; import com.bytezone.diskbrowser.infocom.ZObject.Property; +// -----------------------------------------------------------------------------------// class PropertyTester implements Iterable +// -----------------------------------------------------------------------------------// { List objects; List conditions = new ArrayList<> (); List matchedProperties; - public PropertyTester (List objects) + // ---------------------------------------------------------------------------------// + PropertyTester (List objects) + // ---------------------------------------------------------------------------------// { this.objects = objects; } + // ---------------------------------------------------------------------------------// public void addTest (Condition test) + // ---------------------------------------------------------------------------------// { conditions.add (test); } + // ---------------------------------------------------------------------------------// public void doTests () + // ---------------------------------------------------------------------------------// { boolean[] propFail = new boolean[32]; int[] propTestCount = new int[32]; @@ -47,19 +55,25 @@ class PropertyTester implements Iterable matchedProperties.add (i); } + // ---------------------------------------------------------------------------------// @Override public Iterator iterator () + // ---------------------------------------------------------------------------------// { return matchedProperties.iterator (); } + // ---------------------------------------------------------------------------------// public int totalSuccessfulProperties () + // ---------------------------------------------------------------------------------// { return matchedProperties.size (); } } +// ---------------------------------------------------------------------------------// abstract class Condition +// ---------------------------------------------------------------------------------// { abstract boolean test (Property property); } \ No newline at end of file diff --git a/src/com/bytezone/diskbrowser/infocom/Routine.java b/src/com/bytezone/diskbrowser/infocom/Routine.java index e144707..3cefc15 100644 --- a/src/com/bytezone/diskbrowser/infocom/Routine.java +++ b/src/com/bytezone/diskbrowser/infocom/Routine.java @@ -8,8 +8,10 @@ import com.bytezone.diskbrowser.infocom.Instruction.Operand; import com.bytezone.diskbrowser.infocom.Instruction.OperandType; import com.bytezone.diskbrowser.utilities.HexFormatter; +// -----------------------------------------------------------------------------------// class Routine extends InfocomAbstractFile implements Iterable, Comparable +// -----------------------------------------------------------------------------------// { int startPtr, length, strings, locals; @@ -20,7 +22,9 @@ class Routine extends InfocomAbstractFile List actions = new ArrayList<> (); // not used yet List targets = new ArrayList<> (); - public Routine (int ptr, Header header, int caller) + // ---------------------------------------------------------------------------------// + Routine (int ptr, Header header, int caller) + // ---------------------------------------------------------------------------------// { super (String.format ("Routine %05X", ptr), header.buffer); @@ -104,7 +108,9 @@ class Routine extends InfocomAbstractFile } } + // ---------------------------------------------------------------------------------// String dump () + // ---------------------------------------------------------------------------------// { StringBuilder text = new StringBuilder (); text.append (String.format ("%05X : %s", startPtr, @@ -118,13 +124,17 @@ class Routine extends InfocomAbstractFile return text.toString (); } + // ---------------------------------------------------------------------------------// boolean isValid () + // ---------------------------------------------------------------------------------// { return startPtr > 0; } // test whether the routine contains any instructions pointing to this address + // ---------------------------------------------------------------------------------// private boolean isTarget (int ptr) + // ---------------------------------------------------------------------------------// { for (Instruction ins : instructions) { @@ -137,13 +147,17 @@ class Routine extends InfocomAbstractFile return false; } + // ---------------------------------------------------------------------------------// public void addCaller (int caller) + // ---------------------------------------------------------------------------------// { calledBy.add (caller); } + // ---------------------------------------------------------------------------------// @Override public String getText () + // ---------------------------------------------------------------------------------// { StringBuilder text = new StringBuilder (); @@ -193,14 +207,18 @@ class Routine extends InfocomAbstractFile return text.toString (); } + // ---------------------------------------------------------------------------------// @Override public String toString () + // ---------------------------------------------------------------------------------// { return String.format ("[Start: %05X, Len: %4d, Strings: %2d, Locals: %2d]", startPtr, length, strings, locals); } + // ---------------------------------------------------------------------------------// class Parameter + // ---------------------------------------------------------------------------------// { int value; int sequence; diff --git a/src/com/bytezone/diskbrowser/infocom/StringManager.java b/src/com/bytezone/diskbrowser/infocom/StringManager.java index 0ab2878..baa633b 100644 --- a/src/com/bytezone/diskbrowser/infocom/StringManager.java +++ b/src/com/bytezone/diskbrowser/infocom/StringManager.java @@ -6,12 +6,16 @@ import java.util.TreeMap; import com.bytezone.diskbrowser.applefile.AbstractFile; import com.bytezone.diskbrowser.utilities.HexFormatter; +// -----------------------------------------------------------------------------------// class StringManager extends AbstractFile +// -----------------------------------------------------------------------------------// { Header header; Map strings = new TreeMap (); - public StringManager (String name, byte[] buffer, Header header) + // ---------------------------------------------------------------------------------// + StringManager (String name, byte[] buffer, Header header) + // ---------------------------------------------------------------------------------// { super (name, buffer); this.header = header; @@ -28,20 +32,26 @@ class StringManager extends AbstractFile } } + // ---------------------------------------------------------------------------------// public boolean containsStringAt (int address) + // ---------------------------------------------------------------------------------// { return strings.containsKey (address); } + // ---------------------------------------------------------------------------------// public String stringAt (int address) + // ---------------------------------------------------------------------------------// { if (strings.containsKey (address)) return strings.get (address).value; return "String not found at : " + address; } + // ---------------------------------------------------------------------------------// @Override public String getText () + // ---------------------------------------------------------------------------------// { StringBuilder text = new StringBuilder (); int count = 0; @@ -61,8 +71,10 @@ class StringManager extends AbstractFile return text.toString (); } + // ---------------------------------------------------------------------------------// @Override public String getHexDump () + // ---------------------------------------------------------------------------------// { int size = header.fileLength - header.stringPointer; return HexFormatter.format (buffer, header.stringPointer, size); diff --git a/src/com/bytezone/diskbrowser/infocom/ZObject.java b/src/com/bytezone/diskbrowser/infocom/ZObject.java index c0bc73b..840871a 100755 --- a/src/com/bytezone/diskbrowser/infocom/ZObject.java +++ b/src/com/bytezone/diskbrowser/infocom/ZObject.java @@ -1,278 +1,298 @@ -package com.bytezone.diskbrowser.infocom; - -import java.util.ArrayList; -import java.util.BitSet; -import java.util.List; - -import com.bytezone.diskbrowser.applefile.AbstractFile; -import com.bytezone.diskbrowser.utilities.HexFormatter; - -class ZObject extends AbstractFile implements Comparable -{ - static final int HEADER_SIZE = 9; - - private final Header header; - private final int id; - private final int startPtr; - - private final int propertyTablePtr; - private final int propertyTableLength; - - final int parent, sibling, child; - final List properties = new ArrayList<> (); - final BitSet attributes = new BitSet (32); - - public ZObject (String name, byte[] buffer, int offset, int id, Header header) - { - super (name, buffer); - - this.header = header; - this.startPtr = offset; - this.id = id; - - // 32 attributes - int bitIndex = 0; - for (int i = 0; i < 4; i++) - { - byte b = buffer[offset + i]; - for (int j = 0; j < 8; j++) - { - if ((b & 0x80) != 0) - attributes.set (bitIndex); - b <<= 1; - ++bitIndex; - } - } - - // object's relatives - parent = header.getByte (offset + 4); - sibling = header.getByte (offset + 5); - child = header.getByte (offset + 6); - - // the property header contains the object's short name - propertyTablePtr = header.getWord (offset + 7); - int ptr = propertyTablePtr; - int nameLength = header.getByte (ptr) * 2; - this.name = nameLength == 0 ? "<<" + id + ">>" : new ZString (header, ++ptr).value; - ptr += nameLength; - - // read each property - while (buffer[ptr] != 0) - { - Property p = new Property (ptr); - properties.add (p); - ptr += p.length + 1; - } - propertyTableLength = ptr - propertyTablePtr; - } - - int getId () - { - return id; - } - - @Override - public String getText () - { - StringBuilder text = new StringBuilder (); - - text.append (String.format ("ID : %02X (%<3d) %s%n%n", id, name)); - - String obj1 = parent == 0 ? "" : header.getObject (parent - 1).name; - String obj2 = sibling == 0 ? "" : header.getObject (sibling - 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 ("Sibling : %02X (%<3d) %s%n", sibling, obj2)); - text.append (String.format ("Child : %02X (%<3d) %s%n%n", child, obj3)); - - text.append ("Attributes : "); - text.append (HexFormatter.getHexString (buffer, startPtr, 4)); - text.append (" " + attributes.toString () + "\n\n"); - - for (Property prop : properties) - text.append (prop + "\n"); - - return text.toString (); - } - - @Override - public String getHexDump () - { - StringBuilder text = new StringBuilder ("Header :\n\n"); - text.append (HexFormatter.formatNoHeader (buffer, startPtr, HEADER_SIZE)); - text.append ("\n\nProperty table:\n\n"); - text.append ( - HexFormatter.formatNoHeader (buffer, propertyTablePtr, propertyTableLength)); - return text.toString (); - } - - Property getProperty (int id) - { - for (Property p : properties) - if (p.propertyNumber == id) - return p; - return null; - } - - @Override - public String toString () - { - return HexFormatter.getHexString (buffer, startPtr, HEADER_SIZE) + " " + name; - } - - public String getDescription (List list) - { - StringBuilder text = new StringBuilder (String.format (" %-40s", getName ())); - - for (int i = 4; i < 7; i++) - { - int index = buffer[startPtr + i] & 0xFF; - String name = index > 0 ? list.get (index - 1).getName () : ""; - text.append (String.format (" %-40s", name)); - } - - text.append (" "); - text.append (HexFormatter.getHexString (buffer, startPtr, 4)); - text.append (" "); - text.append (HexFormatter.getHexString (buffer, startPtr + 7, 2)); - - return text.toString (); - } - - class Property - { - int propertyNumber; - int ptr; - int length; - int offset; // only used if length == 2 - - public Property (int ptr) - { - this.ptr = ptr; - length = header.getByte (ptr) / 32 + 1; - propertyNumber = header.getByte (ptr) % 32; - - if (length == 2) - offset = header.getWord (ptr + 1) * 2; - } - - private ZObject getObject () - { - return header.getObject ((buffer[ptr + 1] & 0xFF) - 1); - } - - private ZObject getObject (int id) - { - return header.getObject (id - 1); - } - - @Override - public String toString () - { - StringBuilder text = new StringBuilder ( - String.format ("%8s : ", header.getPropertyName (propertyNumber))); - - String propertyType = header.getPropertyName (propertyNumber); - - if (!(propertyType.equals ("DICT") || propertyType.startsWith ("STR"))) - text.append ( - String.format ("%-20s", HexFormatter.getHexString (buffer, ptr + 1, length))); - - if (propertyNumber >= 19) // directions - { - switch (length) - { - case 1: // UEXIT - unconditional exit - text.append (getObject ().name); - break; - case 2: - text.append ("\"" + header.stringManager.stringAt (offset) + "\""); - break; - case 3: // FEXIT - function exit - int address = header.getWord (ptr + 1) * 2; - text.append (String.format ("R:%05X", address)); - appendRoutine (text, address); - break; - case 4: - text.append (String.format ("%s : IF G%02X ELSE ", getObject ().name, - header.getByte (ptr + 2))); - address = header.getWord (ptr + 3) * 2; - if (address > 0) - text.append ("\"" + header.stringManager.stringAt (address) + "\""); - break; - case 5: - text.append (String.format ("%s : IF G%02X ", getObject ().name, - header.getByte (ptr + 2))); - break; - default: - break; - } - } - else if (propertyType.equals ("DICT")) - { - for (int i = 1; i <= length; i += 2) - { - int address = header.getWord (ptr + i); - text.append (String.format ("%02X: %s, ", address, header.wordAt (address))); - } - text.deleteCharAt (text.length () - 1); - text.deleteCharAt (text.length () - 1); - } - else if (propertyType.startsWith ("CODE")) - { - if (offset > 0) // cretin contains 00 00 - appendRoutine (text, offset); - } - else if (propertyType.startsWith ("STR")) - { - text.append (String.format ("(%4X) \"%s\"", 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 (); - } - - private void appendRoutine (StringBuilder text, int offset) - { - Routine r = header.codeManager.getRoutine (offset); - if (r != null) - text.append ("\n\n" + r.getText ()); - else // this can happen if the property is mislabelled as code - text.append ("\n\n****** null routine\n"); - } - } - - @Override - public int compareTo (ZObject o) - { - return this.name.compareTo (o.name); - } +package com.bytezone.diskbrowser.infocom; + +import java.util.ArrayList; +import java.util.BitSet; +import java.util.List; + +import com.bytezone.diskbrowser.applefile.AbstractFile; +import com.bytezone.diskbrowser.utilities.HexFormatter; + +// -----------------------------------------------------------------------------------// +class ZObject extends AbstractFile implements Comparable +// -----------------------------------------------------------------------------------// +{ + static final int HEADER_SIZE = 9; + + private final Header header; + private final int id; + private final int startPtr; + + private final int propertyTablePtr; + private final int propertyTableLength; + + final int parent, sibling, child; + final List properties = new ArrayList<> (); + final BitSet attributes = new BitSet (32); + + // ---------------------------------------------------------------------------------// + ZObject (String name, byte[] buffer, int offset, int id, Header header) + // ---------------------------------------------------------------------------------// + { + super (name, buffer); + + this.header = header; + this.startPtr = offset; + this.id = id; + + // 32 attributes + int bitIndex = 0; + for (int i = 0; i < 4; i++) + { + byte b = buffer[offset + i]; + for (int j = 0; j < 8; j++) + { + if ((b & 0x80) != 0) + attributes.set (bitIndex); + b <<= 1; + ++bitIndex; + } + } + + // object's relatives + parent = header.getByte (offset + 4); + sibling = header.getByte (offset + 5); + child = header.getByte (offset + 6); + + // the property header contains the object's short name + propertyTablePtr = header.getWord (offset + 7); + int ptr = propertyTablePtr; + int nameLength = header.getByte (ptr) * 2; + this.name = nameLength == 0 ? "<<" + id + ">>" : new ZString (header, ++ptr).value; + ptr += nameLength; + + // read each property + while (buffer[ptr] != 0) + { + Property p = new Property (ptr); + properties.add (p); + ptr += p.length + 1; + } + propertyTableLength = ptr - propertyTablePtr; + } + + // ---------------------------------------------------------------------------------// + int getId () + // ---------------------------------------------------------------------------------// + { + return id; + } + + // ---------------------------------------------------------------------------------// + @Override + public String getText () + // ---------------------------------------------------------------------------------// + { + StringBuilder text = new StringBuilder (); + + text.append (String.format ("ID : %02X (%<3d) %s%n%n", id, name)); + + String obj1 = parent == 0 ? "" : header.getObject (parent - 1).name; + String obj2 = sibling == 0 ? "" : header.getObject (sibling - 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 ("Sibling : %02X (%<3d) %s%n", sibling, obj2)); + text.append (String.format ("Child : %02X (%<3d) %s%n%n", child, obj3)); + + text.append ("Attributes : "); + text.append (HexFormatter.getHexString (buffer, startPtr, 4)); + text.append (" " + attributes.toString () + "\n\n"); + + for (Property prop : properties) + text.append (prop + "\n"); + + return text.toString (); + } + + // ---------------------------------------------------------------------------------// + @Override + public String getHexDump () + // ---------------------------------------------------------------------------------// + { + StringBuilder text = new StringBuilder ("Header :\n\n"); + text.append (HexFormatter.formatNoHeader (buffer, startPtr, HEADER_SIZE)); + text.append ("\n\nProperty table:\n\n"); + text.append ( + HexFormatter.formatNoHeader (buffer, propertyTablePtr, propertyTableLength)); + return text.toString (); + } + + // ---------------------------------------------------------------------------------// + Property getProperty (int id) + // ---------------------------------------------------------------------------------// + { + for (Property p : properties) + if (p.propertyNumber == id) + return p; + return null; + } + + // ---------------------------------------------------------------------------------// + @Override + public String toString () + // ---------------------------------------------------------------------------------// + { + return HexFormatter.getHexString (buffer, startPtr, HEADER_SIZE) + " " + name; + } + + // ---------------------------------------------------------------------------------// + public String getDescription (List list) + // ---------------------------------------------------------------------------------// + { + StringBuilder text = new StringBuilder (String.format (" %-40s", getName ())); + + for (int i = 4; i < 7; i++) + { + int index = buffer[startPtr + i] & 0xFF; + String name = index > 0 ? list.get (index - 1).getName () : ""; + text.append (String.format (" %-40s", name)); + } + + text.append (" "); + text.append (HexFormatter.getHexString (buffer, startPtr, 4)); + text.append (" "); + text.append (HexFormatter.getHexString (buffer, startPtr + 7, 2)); + + return text.toString (); + } + + // ---------------------------------------------------------------------------------// + class Property + // ---------------------------------------------------------------------------------// + { + int propertyNumber; + int ptr; + int length; + int offset; // only used if length == 2 + + public Property (int ptr) + { + this.ptr = ptr; + length = header.getByte (ptr) / 32 + 1; + propertyNumber = header.getByte (ptr) % 32; + + if (length == 2) + offset = header.getWord (ptr + 1) * 2; + } + + private ZObject getObject () + { + return header.getObject ((buffer[ptr + 1] & 0xFF) - 1); + } + + private ZObject getObject (int id) + { + return header.getObject (id - 1); + } + + @Override + public String toString () + { + StringBuilder text = new StringBuilder ( + String.format ("%8s : ", header.getPropertyName (propertyNumber))); + + String propertyType = header.getPropertyName (propertyNumber); + + if (!(propertyType.equals ("DICT") || propertyType.startsWith ("STR"))) + text.append ( + String.format ("%-20s", HexFormatter.getHexString (buffer, ptr + 1, length))); + + if (propertyNumber >= 19) // directions + { + switch (length) + { + case 1: // UEXIT - unconditional exit + text.append (getObject ().name); + break; + case 2: + text.append ("\"" + header.stringManager.stringAt (offset) + "\""); + break; + case 3: // FEXIT - function exit + int address = header.getWord (ptr + 1) * 2; + text.append (String.format ("R:%05X", address)); + appendRoutine (text, address); + break; + case 4: + text.append (String.format ("%s : IF G%02X ELSE ", getObject ().name, + header.getByte (ptr + 2))); + address = header.getWord (ptr + 3) * 2; + if (address > 0) + text.append ("\"" + header.stringManager.stringAt (address) + "\""); + break; + case 5: + text.append (String.format ("%s : IF G%02X ", getObject ().name, + header.getByte (ptr + 2))); + break; + default: + break; + } + } + else if (propertyType.equals ("DICT")) + { + for (int i = 1; i <= length; i += 2) + { + int address = header.getWord (ptr + i); + text.append (String.format ("%02X: %s, ", address, header.wordAt (address))); + } + text.deleteCharAt (text.length () - 1); + text.deleteCharAt (text.length () - 1); + } + else if (propertyType.startsWith ("CODE")) + { + if (offset > 0) // cretin contains 00 00 + appendRoutine (text, offset); + } + else if (propertyType.startsWith ("STR")) + { + text.append (String.format ("(%4X) \"%s\"", 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 (); + } + + private void appendRoutine (StringBuilder text, int offset) + { + Routine r = header.codeManager.getRoutine (offset); + if (r != null) + text.append ("\n\n" + r.getText ()); + else // this can happen if the property is mislabelled as code + text.append ("\n\n****** null routine\n"); + } + } + + // ---------------------------------------------------------------------------------// + @Override + public int compareTo (ZObject o) + // ---------------------------------------------------------------------------------// + { + return this.name.compareTo (o.name); + } } \ No newline at end of file diff --git a/src/com/bytezone/diskbrowser/infocom/ZString.java b/src/com/bytezone/diskbrowser/infocom/ZString.java index 25924cf..ec6d91d 100755 --- a/src/com/bytezone/diskbrowser/infocom/ZString.java +++ b/src/com/bytezone/diskbrowser/infocom/ZString.java @@ -1,158 +1,166 @@ -package com.bytezone.diskbrowser.infocom; - -class ZString -{ - private static String[] letters = - { " abcdefghijklmnopqrstuvwxyz", " ABCDEFGHIJKLMNOPQRSTUVWXYZ", - " 0123456789.,!?_#\'\"/\\-:()" }; - String value; - Header header; - int startPtr; - int length; - - public ZString (Header header, int offset) - { - ZStringBuilder text = new ZStringBuilder (); - this.header = header; - this.startPtr = offset; - - while (true) - { - if (offset >= header.buffer.length - 1) - { - System.out.println ("********" + text.toString ()); - break; - } - - // get the next two bytes - int val = header.getWord (offset); - - // process each zChar as a 5-bit value - text.processZChar ((byte) ((val >>> 10) & 0x1F)); - text.processZChar ((byte) ((val >>> 5) & 0x1F)); - text.processZChar ((byte) (val & 0x1F)); - - if ((val & 0x8000) != 0) // bit 15 = finished flag - { - length = offset - startPtr + 2; - value = text.toString (); - break; - } - offset += 2; - } - } - - @Override - public String toString () - { - return value; - } - - private class ZStringBuilder - { - int alphabet; - boolean shift; - int shiftAlphabet; - int synonym; - int buildingLevel; - int builtLetter; - StringBuilder text = new StringBuilder (); - - private void processZChar (byte zchar) - { - // 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 - // both halves are full, we use the ascii value in builtLetter. - if (buildingLevel > 0) - { - builtLetter = (short) ((builtLetter << 5) | zchar); - if (++buildingLevel == 3) - { - text.append ((char) builtLetter); - buildingLevel = 0; - } - return; - } - - // 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 within that block. - if (synonym > 0) - { - text.append (header.getAbbreviation ((synonym - 1) * 32 + zchar)); - synonym = 0; - return; - } - - if ((shift && shiftAlphabet == 2) || (!shift && alphabet == 2)) - { - if (zchar == 6) - { - buildingLevel = 1; - builtLetter = 0; - shift = false; - return; - } - if (zchar == 7) - { - text.append ("\n"); - shift = false; - return; - } - } - - // 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. - switch (zchar) - { - case 0: - text.append (" "); - shift = false; - 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 - public String toString () - { - return text.toString (); - } - } +package com.bytezone.diskbrowser.infocom; + +// -----------------------------------------------------------------------------------// +class ZString +// -----------------------------------------------------------------------------------// +{ + private static String[] letters = + { " abcdefghijklmnopqrstuvwxyz", " ABCDEFGHIJKLMNOPQRSTUVWXYZ", + " 0123456789.,!?_#\'\"/\\-:()" }; + String value; + Header header; + int startPtr; + int length; + + // ---------------------------------------------------------------------------------// + ZString (Header header, int offset) + // ---------------------------------------------------------------------------------// + { + ZStringBuilder text = new ZStringBuilder (); + this.header = header; + this.startPtr = offset; + + while (true) + { + if (offset >= header.buffer.length - 1) + { + System.out.println ("********" + text.toString ()); + break; + } + + // get the next two bytes + int val = header.getWord (offset); + + // process each zChar as a 5-bit value + text.processZChar ((byte) ((val >>> 10) & 0x1F)); + text.processZChar ((byte) ((val >>> 5) & 0x1F)); + text.processZChar ((byte) (val & 0x1F)); + + if ((val & 0x8000) != 0) // bit 15 = finished flag + { + length = offset - startPtr + 2; + value = text.toString (); + break; + } + offset += 2; + } + } + + // ---------------------------------------------------------------------------------// + @Override + public String toString () + // ---------------------------------------------------------------------------------// + { + return value; + } + + // ---------------------------------------------------------------------------------// + private class ZStringBuilder + // ---------------------------------------------------------------------------------// + { + int alphabet; + boolean shift; + int shiftAlphabet; + int synonym; + int buildingLevel; + int builtLetter; + StringBuilder text = new StringBuilder (); + + private void processZChar (byte zchar) + { + // 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 + // both halves are full, we use the ascii value in builtLetter. + if (buildingLevel > 0) + { + builtLetter = (short) ((builtLetter << 5) | zchar); + if (++buildingLevel == 3) + { + text.append ((char) builtLetter); + buildingLevel = 0; + } + return; + } + + // 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 within that block. + if (synonym > 0) + { + text.append (header.getAbbreviation ((synonym - 1) * 32 + zchar)); + synonym = 0; + return; + } + + if ((shift && shiftAlphabet == 2) || (!shift && alphabet == 2)) + { + if (zchar == 6) + { + buildingLevel = 1; + builtLetter = 0; + shift = false; + return; + } + if (zchar == 7) + { + text.append ("\n"); + shift = false; + return; + } + } + + // 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. + switch (zchar) + { + case 0: + text.append (" "); + shift = false; + 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 + public String toString () + { + return text.toString (); + } + } } \ No newline at end of file