mirror of
https://github.com/dmolony/DiskBrowser.git
synced 2025-02-20 04:29:02 +00:00
method header lines
This commit is contained in:
parent
ed872ce87e
commit
5b640cbb1c
@ -1,68 +1,78 @@
|
||||
package com.bytezone.diskbrowser.infocom;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class Abbreviations extends InfocomAbstractFile
|
||||
{
|
||||
List<ZString> 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<ZString> 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 ();
|
||||
}
|
||||
}
|
@ -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<Statistic> 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<ZObject> list = new ArrayList<> ();
|
||||
|
@ -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<Integer, Routine> 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<DiskAddress> 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<Integer> 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);
|
||||
|
@ -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<Integer, ZString> dictionary;
|
||||
private final int totalEntries;
|
||||
private final int totalSeparators;
|
||||
private final int dictionaryPtr, dictionarySize;
|
||||
private final int entryLength;
|
||||
private final String separators;
|
||||
|
||||
Map<Integer, List<WordEntry>> 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<WordEntry> 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<String> getVerbs (int value)
|
||||
{
|
||||
List<String> 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<String> getPrepositions (int value)
|
||||
{
|
||||
List<String> 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<WordEntry> 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<WordEntry> 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<WordEntry>
|
||||
{
|
||||
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<Integer, ZString> dictionary;
|
||||
private final int totalEntries;
|
||||
private final int totalSeparators;
|
||||
private final int dictionaryPtr, dictionarySize;
|
||||
private final int entryLength;
|
||||
private final String separators;
|
||||
|
||||
Map<Integer, List<WordEntry>> 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<WordEntry> 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<String> getVerbs (int value)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
List<String> 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<String> getPrepositions (int value)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
List<String> 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<WordEntry> 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<WordEntry> 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<WordEntry>
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
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 ();
|
||||
}
|
||||
}
|
||||
}
|
@ -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<List<Routine>> 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<Routine> 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++)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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<HexBlock> hexBlocks = new ArrayList<> ();
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public InfocomAbstractFile (String name, byte[] buffer)
|
||||
InfocomAbstractFile (String name, byte[] buffer)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
super (name, buffer);
|
||||
|
@ -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<DiskAddress> 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<DiskAddress> 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<DiskAddress> getFileSectors (int fileNo)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppleFileSource getCatalog ()
|
||||
{
|
||||
return new DefaultAppleFileSource (header.getText (), this);
|
||||
}
|
||||
|
||||
public static boolean isCorrectFormat (AppleDisk disk)
|
||||
{
|
||||
disk.setInterleave (2);
|
||||
return checkFormat (disk);
|
||||
}
|
||||
|
||||
public static boolean checkFormat (AppleDisk disk)
|
||||
{
|
||||
byte[] buffer = disk.readSector (3, 0);
|
||||
|
||||
int version = buffer[0] & 0xFF;
|
||||
int highMemory = HexFormatter.intValue (buffer[5], buffer[4]);
|
||||
int programCounter = HexFormatter.intValue (buffer[7], buffer[6]);
|
||||
int dictionary = HexFormatter.intValue (buffer[9], buffer[8]);
|
||||
int objectTable = HexFormatter.intValue (buffer[11], buffer[10]);
|
||||
int globals = HexFormatter.intValue (buffer[13], buffer[12]);
|
||||
int staticMemory = HexFormatter.intValue (buffer[15], buffer[14]);
|
||||
int abbreviationsTable = HexFormatter.intValue (buffer[25], buffer[24]);
|
||||
int fileLength = HexFormatter.intValue (buffer[27], buffer[26]);
|
||||
|
||||
if (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<DiskAddress> 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<DiskAddress> 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<DiskAddress> getFileSectors (int fileNo)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public AppleFileSource getCatalog ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return new DefaultAppleFileSource (header.getText (), this);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public static boolean isCorrectFormat (AppleDisk disk)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
disk.setInterleave (2);
|
||||
return checkFormat (disk);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public static boolean checkFormat (AppleDisk disk)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
byte[] buffer = disk.readSector (3, 0);
|
||||
|
||||
int version = buffer[0] & 0xFF;
|
||||
int highMemory = HexFormatter.intValue (buffer[5], buffer[4]);
|
||||
int programCounter = HexFormatter.intValue (buffer[7], buffer[6]);
|
||||
int dictionary = HexFormatter.intValue (buffer[9], buffer[8]);
|
||||
int objectTable = HexFormatter.intValue (buffer[11], buffer[10]);
|
||||
int globals = HexFormatter.intValue (buffer[13], buffer[12]);
|
||||
int staticMemory = HexFormatter.intValue (buffer[15], buffer[14]);
|
||||
int abbreviationsTable = HexFormatter.intValue (buffer[25], buffer[24]);
|
||||
int fileLength = HexFormatter.intValue (buffer[27], buffer[26]);
|
||||
|
||||
if (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 ();
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -6,14 +6,18 @@ import java.util.List;
|
||||
|
||||
import com.bytezone.diskbrowser.infocom.ZObject.Property;
|
||||
|
||||
// -----------------------------------------------------------------------------------//
|
||||
class ObjectAnalyser
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
Header header;
|
||||
ObjectManager parent;
|
||||
List<Statistics> list = new ArrayList<Statistics> ();
|
||||
List<Integer> routines = new ArrayList<Integer> ();
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
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<Statistics>
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
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)
|
||||
|
@ -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<ZObject>
|
||||
{
|
||||
// private final Header header;
|
||||
private final List<ZObject> list;
|
||||
private List<ZObject> 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<ZObject> 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<Integer> 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<ZObject> 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<ZObject>
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
private final List<ZObject> list;
|
||||
private List<ZObject> 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<ZObject> 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<Integer> 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<ZObject> iterator ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return list.iterator ();
|
||||
}
|
||||
}
|
@ -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<Statistic> 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<ZObject> list = new ArrayList<> ();
|
||||
|
@ -6,23 +6,31 @@ import java.util.List;
|
||||
|
||||
import com.bytezone.diskbrowser.infocom.ZObject.Property;
|
||||
|
||||
// -----------------------------------------------------------------------------------//
|
||||
class PropertyTester implements Iterable<Integer>
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
List<ZObject> objects;
|
||||
List<Condition> conditions = new ArrayList<> ();
|
||||
List<Integer> matchedProperties;
|
||||
|
||||
public PropertyTester (List<ZObject> objects)
|
||||
// ---------------------------------------------------------------------------------//
|
||||
PropertyTester (List<ZObject> 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<Integer>
|
||||
matchedProperties.add (i);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
@Override
|
||||
public Iterator<Integer> iterator ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return matchedProperties.iterator ();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
public int totalSuccessfulProperties ()
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
return matchedProperties.size ();
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------//
|
||||
abstract class Condition
|
||||
// ---------------------------------------------------------------------------------//
|
||||
{
|
||||
abstract boolean test (Property property);
|
||||
}
|
@ -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<Instruction>, Comparable<Routine>
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
int startPtr, length, strings, locals;
|
||||
|
||||
@ -20,7 +22,9 @@ class Routine extends InfocomAbstractFile
|
||||
List<Integer> actions = new ArrayList<> (); // not used yet
|
||||
List<Integer> 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;
|
||||
|
@ -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<Integer, ZString> strings = new TreeMap<Integer, ZString> ();
|
||||
|
||||
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);
|
||||
|
@ -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<ZObject>
|
||||
{
|
||||
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<Property> 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<ZObject> 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<ZObject>
|
||||
// -----------------------------------------------------------------------------------//
|
||||
{
|
||||
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<Property> 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<ZObject> 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);
|
||||
}
|
||||
}
|
@ -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 ();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user