mirror of
https://github.com/danbulant/Cosmos
synced 2026-05-27 14:02:19 +00:00
1679 lines
No EOL
72 KiB
C#
1679 lines
No EOL
72 KiB
C#
/*
|
|
* showverb - part of infodump
|
|
*
|
|
* Verb and grammar display routines.
|
|
*/
|
|
|
|
using zword_t = System.UInt16;
|
|
using zbyte_t = System.Byte;
|
|
|
|
using System;
|
|
|
|
enum gv2_tokentype { TT_ILLEGAL, TT_ELEMENTARY, TT_PREPOSITION, TT_NOUNR, TT_ATTRIBUTE, TT_SCOPER, TT_ROUTINE };
|
|
|
|
namespace ZTools
|
|
{
|
|
internal static class showverb
|
|
{
|
|
static int[] verb_sizes;
|
|
|
|
static showverb()
|
|
{
|
|
verb_sizes = new int[] { 2, 4, 7, 0 };
|
|
}
|
|
|
|
/*
|
|
* configure_parse_tables
|
|
*
|
|
* Determine the start of the parse table, the start of the parse data, the
|
|
* start of the action routine table, the start of the pre-action routine
|
|
* table, the start of the preposition list and other sundry things.
|
|
*
|
|
* Format of the verb parse tables:
|
|
*
|
|
* base:
|
|
* Table of pointers (2 bytes) to each verb entry. Each verb in the
|
|
* dictionary has an index (1 byte) into this table. The index in the
|
|
* dictionary is inverted so an index of 255 = table entry 0, etc.
|
|
* In GV2 the index is 2 bytes and it is not inverted -- except that
|
|
* Inform 6.10 uses the old method. Thus type GV2A is used internally
|
|
* to indicate the 2-byte form which will presumably be used in
|
|
* later Inform versions.
|
|
*
|
|
* Next comes the parse data for each verb pointed to by the table of
|
|
* pointers. The format of the parse data varies between games. Basically,
|
|
* each entry has a count (1 byte) of parse structures corresponding to
|
|
* different sentence structures. For example, the verb 'take' in the
|
|
* dictionary may have two forms; 'take object' or 'take object with object'.
|
|
* Each form has an index (1 byte) into the pre-action and action routine
|
|
* tables.
|
|
*
|
|
* Next come the action routine tables. There is an entry (2 bytes) for
|
|
* each verb form index. The entries are packed addresses of the Z-code
|
|
* routines that perform the main verb processing.
|
|
*
|
|
* Next come the pre-action routine tables. There is an entry (2 bytes) for
|
|
* each verb form index. The entries are packed addresses of the Z-code
|
|
* routines that are called before the main verb action routine.
|
|
*
|
|
* Finally, there is a list of prepositions that can be used by any verb
|
|
* in the verb parse table. This list has a count (2 bytes) followed by
|
|
* the address of the preposition in the dictionary and an index.
|
|
*
|
|
* Verb parse entry format:
|
|
*
|
|
* The format of the data varies between games. The information in the
|
|
* entry is the same though:
|
|
*
|
|
* An object count (0, 1 or 2)
|
|
* A preposition index for the verb
|
|
* Data for object(s) 1
|
|
* A preposition index for the objects
|
|
* Data for object(s) 2
|
|
* A pre-action/action routine table index
|
|
*
|
|
* This means a sentence can have the following form:
|
|
*
|
|
* verb [+ prep] 'look [at]'
|
|
* verb [+ prep] + object 'look [at] book'
|
|
* verb [+ prep] + object [+ prep] + object 'look [at] book [with] glasses'
|
|
*
|
|
* The verb and prepositions can only be a single word each. The object
|
|
* could be a multiple words 'green book' or a list depending on the object
|
|
* data.
|
|
*
|
|
* Notes:
|
|
*
|
|
* Story files produced by Graham Nelson's Inform compiler do not have a
|
|
* concept of pre-actions. Instead, the pre-actions table is filled with
|
|
* pointers to general parsing routines which are related to certain verb
|
|
* parse entries. The format of the parse entries has changed, too. Parse
|
|
* entries have now become a string of tokens. Objects have token values
|
|
* between 0x00 and 0xaf, prepositions correspond to token values from 0xb0
|
|
* to 0xff. Therefore more complicated verb structures with more than two
|
|
* prepositions are possible. See the "Inform Technical Manual" for further
|
|
* information.
|
|
*
|
|
* Inform 6 reduces the size of the pre-action table (which no longer holds
|
|
* any pre-action routines, see above) to the number of parsing routines.
|
|
*
|
|
* Inform 6.10 adds a new grammar form, called GV2, which does away with
|
|
* the pre-action table and preposition table entirely, and completely
|
|
* changes the parse entry format. Again, see the "Inform Technical Manual"
|
|
* for further information. Also note that Inform (in both GV1 and GV2)
|
|
* uses the flags bytes in the dictionary entry in a slightly different
|
|
* manner than Infocom games.
|
|
*
|
|
* Graphic (V6) Infocom games use a different grammar format. The basic
|
|
* elements are mostly still there, but in a different set of tables.
|
|
* The table of pointers to the verb entries is gone. Instead, the
|
|
* first word of 'extra' dictionary information contains a pointer to the
|
|
* verb entry. Pointers to the action and preaction tables are in
|
|
* the next-to-last and last global variables respectively, but in practice
|
|
* the tables are in their usual location right after the verb grammar table,
|
|
* which is in its usual location at the base of dynamic memory. The
|
|
* verb grammar table has the following format
|
|
*
|
|
* Bytes 0-1: Action/pre-action index of the 0-object entry. That is, action
|
|
* if this verb is used alone. $FFFF if this verb cannot be used
|
|
* as a sentence in itself
|
|
*
|
|
* Bytes 2-3: Doesn't seem to be used. Might be intended for actions with
|
|
* no objects but with a preposition.
|
|
*
|
|
* Bytes 4-5: Pointer to grammar entries for 1-object entries -- i.e. those
|
|
* of the form "verb [+ prep] + object"
|
|
*
|
|
* Bytes 6-7: Pointer to grammar entries for 2-object entries -- i.e. those
|
|
* of the form "verb [+ prep] + object [+ prep] + object"
|
|
*
|
|
* The grammar entries area is new. Each item contains data of the following
|
|
* format:
|
|
*
|
|
* Bytes 0-1: Number of entries in this item. Entries immediately follow
|
|
* For each entry in the item:
|
|
* Bytes 0-1: Action/pre-action index for this item.
|
|
* Bytes 2-3: Byte address of the dictionary word for the preposition
|
|
* $0000 if no preposition.
|
|
*
|
|
* Byte 4: Attribute # associated with this entry. This does not appear
|
|
* to be used by the parser itself, but instead by the helper
|
|
* which suggests possible commands, in order to suggest
|
|
* sensible ones. Actually verifying that the object has the
|
|
* attribute seems to be left to the action routine.
|
|
*
|
|
* Byte 5: I'm not sure about this one. $80 appears to mean anything can
|
|
* be used, particularly including quoted strings. $0F seems to
|
|
* mean an object in scope. $14 may mean an object being held. I
|
|
* suspect it's a flags byte.
|
|
*
|
|
* Bytes 6-7: (Two object entries only) Same as bytes 2-3, for second preposition
|
|
* Bytes 8-9: (Two object entries only) Same as bytes 4-5, for second object
|
|
*
|
|
*
|
|
* Note also that the dictionary flags have moved from the first to the last byte
|
|
* of each dictionary entry, and that while Zork Zero has only three bytes of
|
|
* extra dictionary data (as with V1-5), Shogun and Arthur have four.
|
|
* Also, I believe there is more grammar data than I've listed here, though how
|
|
* much is for the parser proper and how much for the helper I don't know -- MTR
|
|
*
|
|
* Journey has no grammar.
|
|
*
|
|
*/
|
|
|
|
internal static void configure_parse_tables(out uint verb_count,
|
|
out uint action_count,
|
|
out uint parse_count,
|
|
out uint parser_type,
|
|
out uint prep_type,
|
|
out ulong verb_table_base,
|
|
out ulong verb_data_base,
|
|
out ulong action_table_base,
|
|
out ulong preact_table_base,
|
|
out ulong prep_table_base,
|
|
out ulong prep_table_end)
|
|
{
|
|
ulong address, first_entry, second_entry, verb_entry;
|
|
uint entry_count, object_count, prep_count, action_index;
|
|
ulong parse_index;
|
|
uint val;
|
|
int i, j;
|
|
|
|
verb_table_base = 0;
|
|
verb_data_base = 0;
|
|
action_table_base = 0;
|
|
preact_table_base = 0;
|
|
prep_table_base = 0;
|
|
prep_table_end = 0;
|
|
verb_count = 0;
|
|
action_count = 0;
|
|
parser_type = 0;
|
|
prep_type = 0;
|
|
parse_count = 0;
|
|
|
|
var header = txio.header;
|
|
|
|
if (header.serial[0] >= '0' && header.serial[0] <= '9' &&
|
|
header.serial[1] >= '0' && header.serial[1] <= '9' &&
|
|
header.serial[2] >= '0' && header.serial[2] <= '1' &&
|
|
header.serial[3] >= '0' && header.serial[3] <= '9' &&
|
|
header.serial[4] >= '0' && header.serial[4] <= '3' &&
|
|
header.serial[5] >= '0' && header.serial[5] <= '9' &&
|
|
header.serial[0] != '8')
|
|
{
|
|
parser_type = (uint)tx_h.parser_types.inform5_grammar;
|
|
|
|
if (header.name[4] >= '6')
|
|
parser_type = (uint)tx_h.parser_types.inform_gv1;
|
|
}
|
|
|
|
if ((parser_type < (uint)tx_h.parser_types.inform5_grammar) && (uint)header.version == tx_h.V6)
|
|
{
|
|
// TODO This code does not work!
|
|
ulong word_address, first_word, last_word;
|
|
ushort word_size, word_count;
|
|
ulong vbase, vend;
|
|
ulong area2base, area2end;
|
|
ulong parse_address;
|
|
|
|
parser_type = (uint)tx_h.parser_types.infocom6_grammar;
|
|
address = (ulong)(header.objects - 4);
|
|
action_table_base = (ushort)txio.read_data_word(ref address);
|
|
preact_table_base = (ushort)txio.read_data_word(ref address);
|
|
|
|
/* Calculate dictionary bounds and entry size */
|
|
|
|
address = (ulong)header.dictionary;
|
|
ulong temp = (ulong)txio.read_data_byte(ref address);
|
|
address += temp;
|
|
word_size = txio.read_data_byte(ref address);
|
|
word_count = txio.read_data_word(ref address);
|
|
first_word = address;
|
|
last_word = (address + ((ulong)(word_count - 1) * word_size));
|
|
|
|
vbase = area2base = 0xFFFF;
|
|
vend = area2end = 0;
|
|
|
|
for (word_address = first_word; word_address <= last_word; word_address += word_size)
|
|
{
|
|
address = word_address + 6;
|
|
parse_index = (ushort)txio.read_data_word(ref address);
|
|
address = word_address + word_size - 1;
|
|
val = txio.read_data_byte(ref address); /* flags */
|
|
if ((val & 1) != 0 && (val & 0x80) == 0 && (parse_index != 0) && (parse_index < action_table_base))
|
|
{ /* dictionary verb */
|
|
if (vbase > parse_index)
|
|
vbase = parse_index;
|
|
if (vend <= parse_index)
|
|
vend = parse_index + 8;
|
|
address = parse_index + 4;
|
|
|
|
/* retrieve direct-object only parse entries */
|
|
parse_address = (ushort)txio.read_data_word(ref address);
|
|
if (parse_address > 0 && (area2base > parse_address))
|
|
area2base = parse_address;
|
|
|
|
if (parse_address > 0 && (area2end <= parse_address))
|
|
{
|
|
val = (ushort)txio.read_data_word(ref parse_address);
|
|
area2end = (parse_address + 6 * val);
|
|
}
|
|
|
|
/* retrieve indrect-object parse entries */
|
|
parse_address = (ushort)txio.read_data_word(ref address);
|
|
if (parse_address > 0 && (area2base > parse_address))
|
|
area2base = parse_address;
|
|
|
|
if (parse_address > 0 && (area2end <= parse_address))
|
|
{
|
|
val = (ushort)txio.read_data_word(ref parse_address);
|
|
area2end = (parse_address + 10 * val);
|
|
}
|
|
}
|
|
}
|
|
if (vend == 0) /* no verbs */
|
|
return;
|
|
verb_count = (uint)((vend - vbase) / 8);
|
|
verb_table_base = vbase;
|
|
verb_data_base = area2base;
|
|
/* there is no preposition table, but *prep_table_base bounds the verb data area */
|
|
prep_table_base = area2end;
|
|
prep_table_end = area2end;
|
|
action_count = (uint)(preact_table_base - action_table_base) / 2;
|
|
return;
|
|
}
|
|
|
|
/* Start of table comes from the header */
|
|
|
|
verb_table_base = (ulong)header.dynamic_size;
|
|
|
|
/*
|
|
* Calculate the number of verb entries in the table. This can be done
|
|
* because the verb entries immediately follow the verb table.
|
|
*/
|
|
|
|
address = verb_table_base;
|
|
first_entry = txio.read_data_word(ref address);
|
|
if (first_entry == 0) /* No verb entries at all */
|
|
return;
|
|
verb_count = (uint)((first_entry - verb_table_base) / sizeof(zword_t));
|
|
|
|
/*
|
|
* Calculate the form of the verb parse table entries. Basically,
|
|
* Infocom used two types of table. The first types have 8 bytes per
|
|
* entry, and the second type has a variable sized amount of data per
|
|
* entry. In addition, Inform uses two new types of table. We look at
|
|
* the serial number to distinguish Inform story files from Infocom
|
|
* games, and we look at the last header entry to identify Inform 6
|
|
* story files because Inform 6 writes its version number into the
|
|
* last four bytes of this entry.
|
|
*/
|
|
|
|
/*
|
|
* Inform 6.10 addes an additional table format, called GV2. GV1 is the
|
|
* Inform 6.0-6.05 format, and is essentially similar to the Inform 5
|
|
* format except that the parsing routine table is not padded out
|
|
* to the length of the action table.
|
|
* Infocom: parser_type = 0,1
|
|
* Inform 1?-5: parser_type = 2
|
|
* Inform 6 GV1: parser_type = 3
|
|
* Inform 6 GV2: parser_type = 4
|
|
*/
|
|
|
|
address = verb_table_base;
|
|
first_entry = txio.read_data_word(ref address);
|
|
second_entry = txio.read_data_word(ref address);
|
|
verb_data_base = first_entry;
|
|
entry_count = (uint)txio.read_data_byte(ref first_entry);
|
|
|
|
if (parser_type < (uint)tx_h.parser_types.inform5_grammar)
|
|
{
|
|
parser_type = (uint)tx_h.parser_types.infocom_fixed;
|
|
|
|
if (((second_entry - first_entry) / entry_count) <= 7)
|
|
parser_type = (uint)tx_h.parser_types.infocom_variable;
|
|
}
|
|
|
|
/* Distinguishing between GV1 and GV2 isn't trivial.
|
|
Here I check the length of the first entry. It will be 1 mod 3
|
|
for GV2 and 1 mod 8 for GV1. If it's 1 mod 24, first I check to see if
|
|
its length matches the GV1 length. Then I check for illegal GV1 values.
|
|
If they aren't found, I assume GV1. I believe it is actually possible for
|
|
a legal (if somewhat nonsensical) GV1 table to be the same as a legal GV2
|
|
table, but I haven't actually constructed such a weird table. In practice,
|
|
the ENDIT (15) byte of the GV2 table will probably cause an illegal token
|
|
if the table is interpreted as GV1 -- MTR.
|
|
*/
|
|
|
|
if (parser_type == (int)tx_h.parser_types.inform_gv1)
|
|
{
|
|
first_entry = verb_data_base;
|
|
if (((second_entry - first_entry) % 3) == 1)
|
|
{
|
|
entry_count = txio.read_data_byte(ref first_entry);
|
|
if ((entry_count * 8 + 1) == (second_entry - first_entry))
|
|
{
|
|
/* this is the most ambiguous case */
|
|
for (i = 0; i < entry_count && (parser_type == (int)tx_h.parser_types.inform_gv1); i++)
|
|
{
|
|
if (txio.read_data_byte(ref first_entry) > 6)
|
|
{
|
|
parser_type = (int)tx_h.parser_types.inform_gv2;
|
|
break;
|
|
}
|
|
for (j = 1; j < 7; j++)
|
|
{
|
|
val = txio.read_data_byte(ref first_entry);
|
|
if ((val >= 9) || (val <= 15) || (val >= 112) || (val <= 127))
|
|
{
|
|
parser_type = (int)tx_h.parser_types.inform_gv2;
|
|
break;
|
|
}
|
|
}
|
|
txio.read_data_byte(ref first_entry); /* action number. This can be anything */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
parser_type = (int)tx_h.parser_types.inform_gv2;
|
|
}
|
|
}
|
|
else if (((second_entry - first_entry) % 8) != 1)
|
|
{
|
|
Console.Error.WriteLine("Grammar table illegal size!");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Make a pass through the verb parse table looking at the pre-action and
|
|
* action routine indices. We need to know what the highest index is to
|
|
* find the size of the pre-action and action tables. Before Inform 6
|
|
* both tables had the same size. For Inform 6 story files we also need
|
|
* to know the number of parsing routines that occupy the pre-action
|
|
* table (instead of pre-actions).
|
|
*/
|
|
|
|
verb_entry = 0;
|
|
action_count = 0;
|
|
parse_count = 0;
|
|
address = verb_table_base;
|
|
for (i = 0; (uint)i < verb_count; i++)
|
|
{
|
|
verb_entry = (ulong)txio.read_data_word(ref address);
|
|
entry_count = (uint)txio.read_data_byte(ref verb_entry);
|
|
while (entry_count-- > 0)
|
|
{
|
|
if (parser_type == (int)tx_h.parser_types.infocom_fixed)
|
|
{
|
|
verb_entry += 7;
|
|
action_index = (uint)txio.read_data_byte(ref verb_entry);
|
|
}
|
|
else if (parser_type == (int)tx_h.parser_types.infocom_variable)
|
|
{
|
|
object_count = (uint)txio.read_data_byte(ref verb_entry);
|
|
action_index = (uint)txio.read_data_byte(ref verb_entry);
|
|
verb_entry += (ulong)(verb_sizes[(object_count >> 6) & 0x03] - 2);
|
|
}
|
|
else if ((parser_type == (int)tx_h.parser_types.inform_gv1) || (parser_type == (int)tx_h.parser_types.inform5_grammar))
|
|
{
|
|
/* GV1 */
|
|
object_count = (uint)txio.read_data_byte(ref verb_entry);
|
|
for (j = 0; j < 6; j++)
|
|
{
|
|
val = txio.read_data_byte(ref verb_entry);
|
|
if (val < 16 || val >= 112)
|
|
continue;
|
|
parse_index = (val - 16) % 32;
|
|
if (parse_index > parse_count)
|
|
parse_count = (uint)parse_index;
|
|
}
|
|
action_index = (uint)txio.read_data_byte(ref verb_entry);
|
|
}
|
|
else
|
|
{
|
|
/* GV2 */
|
|
action_index = (uint)(txio.read_data_word(ref verb_entry) & 0x3FF);
|
|
val = txio.read_data_byte(ref verb_entry);
|
|
while (val != 15)
|
|
{
|
|
txio.read_data_byte(ref verb_entry);
|
|
txio.read_data_byte(ref verb_entry);
|
|
val = txio.read_data_byte(ref verb_entry);
|
|
}
|
|
}
|
|
if (action_index > action_count)
|
|
action_count = action_index;
|
|
}
|
|
}
|
|
(action_count)++;
|
|
if ((parser_type == (int)tx_h.parser_types.inform_gv1) || (parser_type == (int)tx_h.parser_types.inform5_grammar))
|
|
(parse_count)++;
|
|
|
|
while ((uint)txio.read_data_byte(ref verb_entry) == 0) /* Skip padding, if any */
|
|
;
|
|
|
|
/*
|
|
* Set the start addresses of the pre-action and action routines tables
|
|
* and the preposition table.
|
|
*/
|
|
|
|
action_table_base = verb_entry - 1;
|
|
preact_table_base = action_table_base + (action_count * sizeof(zword_t));
|
|
|
|
if (parser_type >= (int)tx_h.parser_types.inform_gv2)
|
|
{
|
|
/* GV2 has neither preaction/parse table nor preposition table */
|
|
prep_table_base = preact_table_base;
|
|
prep_table_end = preact_table_base;
|
|
}
|
|
else
|
|
{
|
|
if (parser_type < (int)tx_h.parser_types.inform_gv1)
|
|
prep_table_base = preact_table_base + (action_count * sizeof(zword_t));
|
|
else
|
|
prep_table_base = preact_table_base + (parse_count * sizeof(zword_t));
|
|
|
|
/*
|
|
* Set the preposition table type by looking to see if the byte index
|
|
* is stored in a word (an hence the upper 8 bits are zero).
|
|
*/
|
|
|
|
address = prep_table_base;
|
|
prep_count = (uint)txio.read_data_word(ref address);
|
|
address += 2; /* Skip first address */
|
|
if ((uint)txio.read_data_byte(ref address) == 0)
|
|
{
|
|
prep_type = 0;
|
|
prep_table_end = prep_table_base + 2 + (4 * prep_count) - 1;
|
|
}
|
|
else
|
|
{
|
|
prep_type = 1;
|
|
prep_table_end = prep_table_base + 2 + (3 * prep_count) - 1;
|
|
}
|
|
}
|
|
|
|
}/* configure_parse_tables */
|
|
|
|
/*
|
|
* show_verbs
|
|
*
|
|
* Display the verb parse tables, sentence structure, action routines and
|
|
* prepositions.
|
|
*/
|
|
|
|
internal static void show_verbs(int symbolic)
|
|
{
|
|
ulong verb_table_base, verb_data_base;
|
|
ulong action_table_base, preact_table_base;
|
|
ulong prep_table_base, prep_table_end;
|
|
uint verb_count, action_count, parse_count, parser_type, prep_type;
|
|
|
|
int obj_count;
|
|
ulong obj_table_base, obj_table_end, obj_data_base, obj_data_end;
|
|
ushort inform_version;
|
|
ulong class_numbers_base, class_numbers_end;
|
|
ulong property_names_base, property_names_end;
|
|
ulong attr_names_base, attr_names_end;
|
|
ulong action_names_base;
|
|
|
|
attr_names_end = 0;
|
|
|
|
/* Get parse table configuration */
|
|
|
|
configure_parse_tables(out verb_count, out action_count, out parse_count, out parser_type, out prep_type,
|
|
out verb_table_base, out verb_data_base,
|
|
out action_table_base, out preact_table_base,
|
|
out prep_table_base, out prep_table_end);
|
|
|
|
/* I wonder weather you can guess which author required the following test? */
|
|
|
|
if (verb_count == 0)
|
|
{
|
|
txio.tx_printf("\n **** There are no parse tables ****\n\n");
|
|
txio.tx_printf(" Verb entries = 0\n\n");
|
|
|
|
return;
|
|
}
|
|
|
|
if (symbolic > 0)
|
|
{
|
|
showobj.configure_object_tables(out obj_count, out obj_table_base, out obj_table_end,
|
|
out obj_data_base, out obj_data_end);
|
|
infinfo.configure_inform_tables(obj_data_end, out inform_version, out class_numbers_base, out class_numbers_end,
|
|
out property_names_base, out property_names_end, out attr_names_base, out attr_names_end);
|
|
}
|
|
else
|
|
{
|
|
attr_names_base = property_names_base = class_numbers_base = 0;
|
|
}
|
|
|
|
action_names_base = attr_names_base > 0 ? attr_names_end + 1 : 0;
|
|
|
|
/* Display parse data */
|
|
|
|
show_verb_parse_table(verb_table_base, verb_count, parser_type,
|
|
prep_type, prep_table_base, attr_names_base);
|
|
|
|
/* Display action routines */
|
|
|
|
show_action_tables(verb_table_base,
|
|
verb_count, action_count, parse_count, parser_type, prep_type,
|
|
action_table_base, preact_table_base,
|
|
prep_table_base, attr_names_base, action_names_base);
|
|
|
|
/* Display prepositions */
|
|
if ((parser_type <= (uint)tx_h.parser_types.inform_gv2) && (parser_type != (int)tx_h.parser_types.infocom6_grammar)) /* no preposition table in GV2 */
|
|
show_preposition_table(prep_type, prep_table_base, parser_type);
|
|
|
|
}/* show_verbs */
|
|
|
|
/*
|
|
* show_verb_parse_table
|
|
*
|
|
* Display the parse information associated with each verb. The entry into the
|
|
* table is found from the dictionary. Each verb has a parse table entry index.
|
|
* These indices range from 255 to 0*. Each parse table entry can have one or
|
|
* more sentence formats associated with the verb. Once the verb and sentence
|
|
* structure match, an index taken from the parse data is used to index into the
|
|
* pre-action and action routine tables. This format allows multiple similar
|
|
* verb and sentence structures to parse to the same action routine.
|
|
* * 0 to 65535 in GV2
|
|
*
|
|
* Synonyms for each verb are also show. The first verb in the dictionary is
|
|
* used in the sentence structure text. This can lead to bizarre looking
|
|
* sentences, but they all work!
|
|
*
|
|
* The index used to find the action routine is the same number printed when
|
|
* debugging is turned on in games that support this. The number is printed as
|
|
* performing: nn
|
|
*/
|
|
|
|
private static void show_verb_parse_table(ulong verb_table_base,
|
|
uint verb_count,
|
|
uint parser_type,
|
|
uint prep_type,
|
|
ulong prep_table_base,
|
|
ulong attr_names_base)
|
|
{
|
|
ulong address;
|
|
uint entry_count, object_count;
|
|
uint parse_data = 0;
|
|
int i, j, verb_size;
|
|
|
|
ulong parse_entry = 0;
|
|
ulong verb_entry = 0;
|
|
|
|
txio.tx_printf("\n **** Parse tables ****\n\n");
|
|
txio.tx_printf(" Verb entries = {0}\n", (int)verb_count);
|
|
|
|
/* Go through each verb and print its parse information and grammar */
|
|
|
|
address = verb_table_base;
|
|
for (i = 0; (uint)i < verb_count; i++)
|
|
{
|
|
|
|
/* Get start of verb entry and number of entries */
|
|
|
|
if (parser_type == (uint)tx_h.parser_types.infocom6_grammar)
|
|
{
|
|
ulong do_address, doio_address;
|
|
uint verb_address;
|
|
|
|
verb_address = (uint)address; /* cast is guaranteed to work provided uint >= 16 bits */
|
|
txio.tx_printf("\n{0,3:d}. @ ${1:X4}, verb = ", i, address);
|
|
show_words((uint)address, 0L, tx_h.VERB_V6, parser_type);
|
|
txio.tx_printf("\n Main data");
|
|
txio.tx_printf("\n [");
|
|
parse_data = (uint)txio.read_data_word(ref address);
|
|
txio.tx_printf("{0:X4} ", (uint)parse_data);
|
|
txio.read_data_word(ref address); /* I don't know what this word does */
|
|
txio.tx_printf("{0:X4} ", (uint)parse_data);
|
|
do_address = (uint)txio.read_data_word(ref address);
|
|
txio.tx_printf("{0:X4} ", (uint)do_address);
|
|
doio_address = (uint)txio.read_data_word(ref address);
|
|
txio.tx_printf("{0:X4}", (uint)doio_address);
|
|
txio.tx_printf("] ");
|
|
if (verb_entry != 0xFFFF)
|
|
show_verb_grammar(parse_entry, verb_address, (int)parser_type, 0, 0, 0L, 0L);
|
|
|
|
if (do_address > 0)
|
|
{
|
|
txio.tx_printf("\n One object entries:\n");
|
|
verb_entry = do_address;
|
|
entry_count = (uint)txio.read_data_word(ref verb_entry);
|
|
verb_size = 3; /* words */
|
|
while (entry_count-- > 0)
|
|
{
|
|
parse_entry = verb_entry;
|
|
txio.tx_printf(" [");
|
|
for (j = 0; j < verb_size; j++)
|
|
{
|
|
parse_data = (uint)txio.read_data_word(ref verb_entry);
|
|
txio.tx_printf("{0:X4}", (uint)parse_data);
|
|
if (j < (verb_size - 1))
|
|
txio.tx_printf(" ");
|
|
}
|
|
txio.tx_printf("] ");
|
|
show_verb_grammar(parse_entry, verb_address, (int)parser_type, 1,
|
|
0, 0L, 0L);
|
|
txio.tx_printf("\n");
|
|
}
|
|
}
|
|
if (doio_address > 0)
|
|
{
|
|
txio.tx_printf("\n Two object entries:\n");
|
|
verb_entry = doio_address;
|
|
entry_count = (uint)txio.read_data_word(ref verb_entry);
|
|
verb_size = 5; /* words */
|
|
while (entry_count-- > 0)
|
|
{
|
|
parse_entry = verb_entry;
|
|
txio.tx_printf(" [");
|
|
for (j = 0; j < verb_size; j++)
|
|
{
|
|
parse_data = (uint)txio.read_data_word(ref verb_entry);
|
|
txio.tx_printf("{0:X4}", (uint)parse_data);
|
|
if (j < (verb_size - 1))
|
|
txio.tx_printf(" ");
|
|
}
|
|
txio.tx_printf("] ");
|
|
show_verb_grammar(parse_entry, verb_address, (int)parser_type, 2,
|
|
0, 0L, 0L);
|
|
txio.tx_printf("\n");
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{ /* everything but Zork Zero, Shogun, and Arthur */
|
|
verb_entry = (ulong)txio.read_data_word(ref address);
|
|
entry_count = (uint)txio.read_data_byte(ref verb_entry);
|
|
|
|
/* Show the verb index, entry count, verb and synonyms */
|
|
|
|
txio.tx_printf("\n{0:d3}. {1} entr{2}, verb = ", (int)tx_h.VERB_NUM(i, parser_type),
|
|
(int)entry_count, (entry_count == 1) ? "y" : "ies");
|
|
show_words(tx_h.VERB_NUM(i, parser_type), 0L, tx_h.VERB, parser_type);
|
|
txio.tx_printf("\n");
|
|
|
|
/* Show parse data and grammar for each verb entry */
|
|
|
|
while (entry_count-- > 0)
|
|
{
|
|
parse_entry = verb_entry;
|
|
|
|
/* Calculate the amount of verb data */
|
|
|
|
if (parser_type != (int)tx_h.parser_types.infocom_variable)
|
|
{
|
|
verb_size = 8;
|
|
}
|
|
else
|
|
{
|
|
object_count = (uint)txio.read_data_byte(ref parse_entry);
|
|
verb_size = verb_sizes[(object_count >> 6) & 0x03];
|
|
parse_entry = verb_entry;
|
|
}
|
|
|
|
/* Show parse data for each verb */
|
|
|
|
txio.tx_printf(" [");
|
|
|
|
if (parser_type < (int)tx_h.parser_types.inform_gv2)
|
|
{
|
|
for (j = 0; j < verb_size; j++)
|
|
{
|
|
parse_data = (uint)txio.read_data_byte(ref verb_entry);
|
|
txio.tx_printf("{0:X2}", (uint)parse_data);
|
|
if (j < (verb_size - 1))
|
|
txio.tx_printf(" ");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* GV2 variable entry format
|
|
<flags and action high> <action low> n*(<token type> <token data 1> <token data 2>) <ENDIT>*/
|
|
for (j = 0; (j == 0) || (j % 3 != 0) || (parse_data != tx_h.ENDIT); j++)
|
|
{
|
|
if (j != 0)
|
|
txio.tx_printf(" ");
|
|
parse_data = (uint)txio.read_data_byte(ref verb_entry);
|
|
txio.tx_printf("{0:X2}", (uint)parse_data);
|
|
}
|
|
verb_size = j;
|
|
}
|
|
txio.tx_printf("] ");
|
|
for (; j < 8; j++)
|
|
txio.tx_printf(" ");
|
|
|
|
/* Show the verb grammar for this entry */
|
|
|
|
show_verb_grammar(parse_entry, tx_h.VERB_NUM(i, parser_type), (int)parser_type, 0,
|
|
(int)prep_type, prep_table_base, attr_names_base);
|
|
txio.tx_printf("\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
}/* show_verb_parse_table */
|
|
|
|
/* show_syntax_of_action
|
|
*
|
|
* Display the syntax entries for a given action number. Used by
|
|
* txd as well as by show_action_tables. A pre-action number works as well
|
|
* (because they are the same as action numbers), but not a parsing routine
|
|
* number (see show_syntax_of_parsing_routine)
|
|
*
|
|
*/
|
|
|
|
internal static void show_syntax_of_action(uint action,
|
|
ulong verb_table_base,
|
|
uint verb_count,
|
|
uint parser_type,
|
|
uint prep_type,
|
|
ulong prep_table_base,
|
|
ulong attr_names_base)
|
|
{
|
|
ulong address;
|
|
ulong verb_entry, parse_entry;
|
|
uint entry_count, object_count, val, action_index;
|
|
int i;
|
|
bool matched = false;
|
|
|
|
address = verb_table_base;
|
|
for (i = 0; (uint)i < verb_count; i++)
|
|
{
|
|
|
|
if (parser_type == (uint)tx_h.parser_types.infocom6_grammar)
|
|
{
|
|
ulong do_address, doio_address;
|
|
uint verb_address;
|
|
|
|
verb_address = (uint)address;
|
|
parse_entry = address;
|
|
action_index = txio.read_data_word(ref address);
|
|
if (action_index == (uint)action)
|
|
{
|
|
show_verb_grammar(parse_entry, verb_address, (int)parser_type, 0,
|
|
(int)0, 0L, 0L);
|
|
txio.tx_printf("\n");
|
|
matched = true;
|
|
}
|
|
txio.read_data_word(ref address);
|
|
do_address = txio.read_data_word(ref address);
|
|
doio_address = txio.read_data_word(ref address);
|
|
|
|
if (do_address > 0)
|
|
{
|
|
verb_entry = do_address;
|
|
entry_count = (uint)txio.read_data_word(ref verb_entry);
|
|
while (entry_count-- > 0)
|
|
{
|
|
parse_entry = verb_entry;
|
|
action_index = txio.read_data_word(ref verb_entry);
|
|
if (action_index == (uint)action)
|
|
{
|
|
show_verb_grammar(parse_entry, verb_address, (int)parser_type, 1,
|
|
0, 0L, 0L);
|
|
txio.tx_printf("\n");
|
|
matched = true;
|
|
}
|
|
verb_entry += 4; /* skip preposition and object */
|
|
}
|
|
}
|
|
|
|
if (doio_address > 0)
|
|
{
|
|
verb_entry = doio_address;
|
|
entry_count = (uint)txio.read_data_word(ref verb_entry);
|
|
while (entry_count-- > 0)
|
|
{
|
|
parse_entry = verb_entry;
|
|
action_index = txio.read_data_word(ref verb_entry);
|
|
if (action_index == (uint)action)
|
|
{
|
|
show_verb_grammar(parse_entry, verb_address, (int)parser_type, 2,
|
|
0, 0L, 0L);
|
|
txio.tx_printf("\n");
|
|
matched = true;
|
|
}
|
|
verb_entry += 8; /* skip preposition and direct object and preposition and indirect object*/
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Get the parse data address for this entry */
|
|
|
|
verb_entry = (ulong)txio.read_data_word(ref address);
|
|
entry_count = (uint)txio.read_data_byte(ref verb_entry);
|
|
|
|
/* Look through the sentence structures looking for a match */
|
|
|
|
while (entry_count-- > 0)
|
|
{
|
|
parse_entry = verb_entry;
|
|
if (parser_type >= (uint)tx_h.parser_types.inform_gv2)
|
|
{ /* GV2, variable length with terminator */
|
|
action_index = (uint)txio.read_data_word(ref verb_entry) & 0x3FF;
|
|
val = txio.read_data_byte(ref verb_entry);
|
|
while (val != tx_h.ENDIT)
|
|
{
|
|
txio.read_data_word(ref verb_entry);
|
|
val = txio.read_data_byte(ref verb_entry);
|
|
}
|
|
}
|
|
else if (parser_type != (uint)tx_h.parser_types.infocom_variable)
|
|
{ /* Index is in last (8th) byte */
|
|
verb_entry += 7;
|
|
action_index = (uint)txio.read_data_byte(ref verb_entry);
|
|
}
|
|
else
|
|
{ /* Index is in second byte */
|
|
object_count = (uint)txio.read_data_byte(ref verb_entry);
|
|
action_index = (uint)txio.read_data_byte(ref verb_entry);
|
|
verb_entry += (ulong)(verb_sizes[(object_count >> 6) & 0x03] - 2);
|
|
}
|
|
|
|
/* Check if this verb/sentence structure uses the action routine */
|
|
|
|
if (action_index == (uint)action)
|
|
{
|
|
show_verb_grammar(parse_entry, tx_h.VERB_NUM(i, parser_type), (int)parser_type, 0,
|
|
(int)prep_type, prep_table_base, attr_names_base);
|
|
txio.tx_printf("\n");
|
|
matched = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!matched)
|
|
{
|
|
txio.tx_printf("\n");
|
|
}
|
|
}
|
|
|
|
internal static bool is_gv2_parsing_routine(ulong parsing_routine,
|
|
ulong verb_table_base,
|
|
uint verb_count)
|
|
{
|
|
ulong address;
|
|
ulong verb_entry;
|
|
ushort token_data;
|
|
uint entry_count, val;
|
|
int i;
|
|
ulong parsing_routine_packed = (parsing_routine - (ulong)txio.story_scaler * txio.header.routines_offset) / txio.code_scaler;
|
|
|
|
bool found = false;
|
|
|
|
address = verb_table_base;
|
|
found = false;
|
|
for (i = 0; !found && (uint)i < verb_count; i++)
|
|
{
|
|
|
|
/* Get the parse data address for this entry */
|
|
|
|
verb_entry = (ulong)txio.read_data_word(ref address);
|
|
entry_count = (uint)txio.read_data_byte(ref verb_entry);
|
|
while (!found && entry_count-- > 0)
|
|
{
|
|
txio.read_data_word(ref verb_entry); /* skip action # and flags */
|
|
val = (uint)txio.read_data_byte(ref verb_entry);
|
|
while (val != tx_h.ENDIT)
|
|
{
|
|
token_data = txio.read_data_word(ref verb_entry);
|
|
if (((val & 0xC0) == 0x80) && (token_data == parsing_routine_packed))
|
|
found = true;
|
|
val = (uint)txio.read_data_byte(ref verb_entry);
|
|
}
|
|
}
|
|
}
|
|
return found;
|
|
}
|
|
|
|
/* show_syntax_of_parsing_routine
|
|
*
|
|
* Display the syntax entries for a given parsing routine number or address. Used by
|
|
* txd as well as by show_action_tables. For Inform 5 and GV1, the input should be
|
|
* the parsing routine number. For GV2, it should be the parsing routine address.
|
|
*
|
|
*/
|
|
|
|
internal static void show_syntax_of_parsing_routine(ulong parsing_routine,
|
|
ulong verb_table_base,
|
|
uint verb_count,
|
|
uint parser_type,
|
|
uint prep_type,
|
|
ulong prep_table_base,
|
|
ulong attr_names_base)
|
|
{
|
|
ulong address;
|
|
ulong verb_entry, parse_entry;
|
|
ushort token_data;
|
|
uint entry_count, object_count, val;
|
|
ulong parsing_routine_packed = (parsing_routine - (ulong)txio.story_scaler * txio.header.routines_offset) / txio.code_scaler;
|
|
int i;
|
|
bool found = false;
|
|
|
|
address = verb_table_base;
|
|
for (i = 0; (uint)i < verb_count; i++)
|
|
{
|
|
|
|
/* Get the parse data address for this entry */
|
|
|
|
verb_entry = (ulong)txio.read_data_word(ref address);
|
|
entry_count = (uint)txio.read_data_byte(ref verb_entry);
|
|
while (entry_count-- > 0)
|
|
{
|
|
parse_entry = verb_entry;
|
|
found = false;
|
|
if (parser_type < (int)tx_h.parser_types.inform_gv2)
|
|
{
|
|
object_count = (uint)txio.read_data_byte(ref verb_entry);
|
|
while (object_count > 0)
|
|
{
|
|
val = (uint)txio.read_data_byte(ref verb_entry);
|
|
if (val < 0xb0)
|
|
{
|
|
object_count--;
|
|
if (val >= 0x10 && val < 0x70 && ((val - 0x10) & 0x1f) == (uint)parsing_routine)
|
|
found = true;
|
|
}
|
|
}
|
|
verb_entry = parse_entry + 8;
|
|
}
|
|
else
|
|
{
|
|
txio.read_data_word(ref verb_entry); /* skip action # and flags */
|
|
val = (uint)txio.read_data_byte(ref verb_entry);
|
|
while (val != tx_h.ENDIT)
|
|
{
|
|
token_data = txio.read_data_word(ref verb_entry);
|
|
if (((val & 0xC0) == 0x80) && (token_data == parsing_routine_packed)) /* V7/V6 issue here */
|
|
found = true;
|
|
val = (uint)txio.read_data_byte(ref verb_entry);
|
|
}
|
|
}
|
|
if (found)
|
|
{
|
|
show_verb_grammar(parse_entry, tx_h.VERB_NUM(i, parser_type), (int)parser_type, (int)prep_type, 0,
|
|
prep_table_base, attr_names_base);
|
|
txio.tx_printf("\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* show_action_tables
|
|
*
|
|
* Display the pre-action and action routine addresses. The list of
|
|
* verb/sentence structures is displayed with each routine. A list of
|
|
* verb/sentence structures against each routine indicate that the routine
|
|
* is called when any of the verb/sentence structures are typed. Inform
|
|
* written games, however, do not have a concept of pre-actions. Their
|
|
* pre-actions table is filled with so-called parsing routines which
|
|
* are linked to single objects within verb/sentence structures. Usually
|
|
* these routines decide if a specific object or text matches these
|
|
* sentence structures.
|
|
*/
|
|
|
|
static void show_action_tables(ulong verb_table_base,
|
|
uint verb_count,
|
|
uint action_count,
|
|
uint parse_count,
|
|
uint parser_type,
|
|
uint prep_type,
|
|
ulong action_table_base,
|
|
ulong preact_table_base,
|
|
ulong prep_table_base,
|
|
ulong attr_names_base,
|
|
ulong action_names_base)
|
|
{
|
|
|
|
ulong actions_address, preacts_address;
|
|
ulong routine_address;
|
|
int action;
|
|
|
|
var header = txio.header;
|
|
|
|
txio.tx_printf("\n **** Verb action routines ****\n\n");
|
|
txio.tx_printf(" Action table entries = {0:d}\n\n", (int)action_count);
|
|
txio.tx_printf("action# ");
|
|
if (parser_type <= (uint)tx_h.parser_types.infocom6_grammar)
|
|
txio.tx_printf("pre-action-routine ");
|
|
|
|
txio.tx_printf("action-routine \"verb...\"\n\n");
|
|
|
|
actions_address = action_table_base;
|
|
preacts_address = preact_table_base;
|
|
|
|
/* Iterate through all routine entries for pre-action and action routines */
|
|
|
|
for (action = 0; (uint)action < action_count; action++)
|
|
{
|
|
|
|
/* Display the routine index and addresses */
|
|
|
|
txio.tx_printf("{0:d3}. ", (int)action);
|
|
if (parser_type <= (uint)tx_h.parser_types.infocom6_grammar)
|
|
{
|
|
routine_address = (ulong)txio.read_data_word(ref preacts_address) * txio.code_scaler;
|
|
if (routine_address > 0)
|
|
routine_address += (ulong)txio.story_scaler * header.routines_offset;
|
|
txio.tx_printf("{0:X5} ", routine_address);
|
|
}
|
|
routine_address = (ulong)txio.read_data_word(ref actions_address) * txio.code_scaler;
|
|
if (routine_address > 0)
|
|
routine_address += (ulong)txio.story_scaler * header.routines_offset;
|
|
txio.tx_printf("{0:X5} ", routine_address);
|
|
txio.tx_printf(" ");
|
|
txio.tx_fix_margin(1);
|
|
if (action_names_base > 0)
|
|
{
|
|
txio.tx_printf("<");
|
|
infinfo.print_inform_action_name(action_names_base, action);
|
|
txio.tx_printf(">\n");
|
|
}
|
|
|
|
/*
|
|
* Now scan down the parse table looking for all verb/sentence formats
|
|
* that cause this action routine to be called.
|
|
*/
|
|
|
|
show_syntax_of_action((uint)action,
|
|
verb_table_base,
|
|
verb_count,
|
|
parser_type,
|
|
prep_type,
|
|
prep_table_base,
|
|
attr_names_base);
|
|
|
|
txio.tx_fix_margin(0);
|
|
}
|
|
|
|
if ((parser_type >= (uint)tx_h.parser_types.inform5_grammar) && (parser_type < (uint)tx_h.parser_types.inform_gv2))
|
|
{
|
|
|
|
/* Determine number of parsing routines (ie. the number of
|
|
non-zero entries in the former pre-actions table) */
|
|
|
|
txio.tx_printf("\n **** Parsing routines ****\n\n");
|
|
txio.tx_printf(" Number of parsing routines = %d\n\n", (int)parse_count);
|
|
txio.tx_printf("parse# parsing-routine \"verb...\"\n\n");
|
|
|
|
for (action = 0; (uint)action < parse_count; action++)
|
|
{
|
|
|
|
/* Display the routine index and addresses */
|
|
|
|
txio.tx_printf("{0:d3}. ", (int)action);
|
|
txio.tx_printf("{0:X5} ", (ulong)txio.read_data_word(ref preacts_address) * txio.code_scaler + (ulong)txio.story_scaler * header.routines_offset);
|
|
txio.tx_printf(" ");
|
|
txio.tx_fix_margin(1);
|
|
/*
|
|
* Now scan down the parse table looking for all verb/sentence formats
|
|
* that this parsing routine applies to.
|
|
*/
|
|
|
|
show_syntax_of_parsing_routine((ulong)action,
|
|
verb_table_base,
|
|
verb_count,
|
|
parser_type,
|
|
prep_type,
|
|
prep_table_base,
|
|
attr_names_base);
|
|
txio.tx_fix_margin(0);
|
|
}
|
|
}
|
|
|
|
}/* show_action_tables */
|
|
|
|
/*
|
|
* show_preposition table
|
|
*
|
|
* Displays all the prepositions and their synonyms. The preposition index can
|
|
* be found in the sentence structure data in the parse tables.
|
|
*/
|
|
|
|
internal static void show_preposition_table(uint prep_type,
|
|
ulong prep_table_base,
|
|
uint parser_type)
|
|
{
|
|
ulong address, prep_address;
|
|
uint count, prep_index;
|
|
int i;
|
|
|
|
/* Get the base address and count of prepositions */
|
|
|
|
address = prep_table_base;
|
|
count = (uint)txio.read_data_word(ref address);
|
|
|
|
txio.tx_printf("\n **** Prepositions ****\n\n");
|
|
txio.tx_printf(" Table entries = {0}\n\n", (int)count);
|
|
|
|
/* Iterate through all prepositions */
|
|
|
|
for (i = 0; (uint)i < count; i++)
|
|
{
|
|
|
|
/* Read the dictionary address of the text for this entry */
|
|
|
|
prep_address = txio.read_data_word(ref address);
|
|
|
|
/* Pick up the index */
|
|
|
|
if (prep_type == 0)
|
|
prep_index = (uint)txio.read_data_word(ref address);
|
|
else
|
|
prep_index = (uint)txio.read_data_byte(ref address);
|
|
|
|
/* Display index and word */
|
|
|
|
txio.tx_printf("{0:d3}. ", (int)prep_index);
|
|
show_words(prep_index, prep_address, tx_h.PREP, parser_type);
|
|
txio.tx_printf("\n");
|
|
}
|
|
|
|
}/* show_preposition_table */
|
|
|
|
/*
|
|
* show_words
|
|
*
|
|
* Display any verb/preposition and synonyms by index. Inform written games
|
|
* do not have synonyms for prepositions.
|
|
*/
|
|
|
|
private static void show_words(uint indx,
|
|
ulong prep_address,
|
|
uint type,
|
|
uint parser_type)
|
|
{
|
|
ulong address, word_address;
|
|
int flag = 0;
|
|
|
|
/* If this is a preposition then we have an address */
|
|
|
|
if (type == tx_h.PREP)
|
|
word_address = prep_address;
|
|
else
|
|
word_address = lookup_word(0L, indx, type, parser_type);
|
|
|
|
/* If the word address is NULL then there are no entries */
|
|
|
|
if (word_address == 0)
|
|
{
|
|
txio.tx_printf(" no-");
|
|
if ((type == tx_h.VERB) || (type == tx_h.VERB_V6))
|
|
txio.tx_printf("verb");
|
|
if (type == tx_h.PREP)
|
|
txio.tx_printf("preposition");
|
|
}
|
|
|
|
/* Display all synonyms for the verb or preposition */
|
|
|
|
for (flag = 0; word_address > 0; flag++)
|
|
{
|
|
if (flag > 0)
|
|
txio.tx_printf(", ");
|
|
if (flag == 1)
|
|
{
|
|
txio.tx_printf("synonyms = ");
|
|
txio.tx_fix_margin(1);
|
|
}
|
|
|
|
/* Display the current word */
|
|
|
|
address = word_address;
|
|
txio.tx_printf("\"");
|
|
txio.decode_text(ref address);
|
|
txio.tx_printf("\"");
|
|
|
|
/* Lookup the next synonym (but skip the word itself) */
|
|
|
|
if (type == tx_h.PREP && flag == 0)
|
|
word_address = 0;
|
|
if (type != tx_h.PREP || parser_type <= (int)tx_h.parser_types.infocom_variable)
|
|
{
|
|
word_address = lookup_word(word_address, indx, type, parser_type);
|
|
if (type == tx_h.PREP && word_address == prep_address)
|
|
word_address = lookup_word(word_address, indx, type, parser_type);
|
|
}
|
|
}
|
|
if (flag > 0)
|
|
txio.tx_fix_margin(0);
|
|
|
|
}/* show_words */
|
|
|
|
/*
|
|
* show_verb_grammar
|
|
*
|
|
* Display the sentence structure associated with a parse table entry.
|
|
*/
|
|
|
|
internal static void show_verb_grammar(ulong verb_entry,
|
|
uint verb_index,
|
|
int parser_type,
|
|
int v6_number_objects,
|
|
int prep_type,
|
|
ulong prep_table_base,
|
|
ulong attr_names_base)
|
|
{
|
|
|
|
ulong address, verb_address, prep_address;
|
|
uint parse_data, objs, val;
|
|
uint token_type, token_data, action;
|
|
int i;
|
|
uint[] preps = new uint[2];
|
|
|
|
string[] GV2_elementary = new String[] {"noun" ,"held", "multi", "multiheld",
|
|
"multiexcept", "multiinside", "creature",
|
|
"special", "number", "topic"};
|
|
address = verb_entry;
|
|
|
|
if (parser_type == (int)tx_h.parser_types.infocom6_grammar)
|
|
{
|
|
txio.tx_printf("\"");
|
|
verb_address = lookup_word(0L, verb_index, tx_h.VERB_V6, (uint)parser_type);
|
|
if (verb_address > 0)
|
|
txio.decode_text(ref verb_address);
|
|
else
|
|
txio.tx_printf("no-verb");
|
|
|
|
if (v6_number_objects > 0)
|
|
{
|
|
txio.tx_printf(" ");
|
|
|
|
action = txio.read_data_word(ref address);
|
|
while (v6_number_objects-- > 0)
|
|
{
|
|
token_data = txio.read_data_word(ref address);
|
|
token_type = txio.read_data_word(ref address);
|
|
if (token_data > 0)
|
|
{
|
|
prep_address = token_data;
|
|
txio.decode_text(ref prep_address);
|
|
txio.tx_printf(" ");
|
|
}
|
|
// txio.tx_printf("${0:X4}", token_type); /* turn this on if you want to see the attribute and flag? info for the object */
|
|
|
|
txio.tx_printf("OBJ");
|
|
|
|
if (v6_number_objects > 0)
|
|
txio.tx_printf(" ");
|
|
}
|
|
}
|
|
txio.tx_printf("\"");
|
|
}
|
|
else if (parser_type >= (int)tx_h.parser_types.inform_gv2)
|
|
{
|
|
/* Inform 6 GV2 verb entry */
|
|
|
|
txio.tx_printf("\"");
|
|
|
|
/* Print verb if one is present */
|
|
|
|
verb_address = lookup_word(0L, verb_index, tx_h.VERB, (uint)parser_type);
|
|
|
|
if (verb_address > 0)
|
|
txio.decode_text(ref verb_address);
|
|
else
|
|
txio.tx_printf("no-verb");
|
|
|
|
action = txio.read_data_word(ref address); /* Action # and flags*/
|
|
|
|
val = txio.read_data_byte(ref address);
|
|
while (val != tx_h.ENDIT)
|
|
{
|
|
if (((val & 0x30) == 0x10) || ((val & 0x30) == 0x30)) /* 2nd ... nth byte of alternative list */
|
|
txio.tx_printf(" /");
|
|
txio.tx_printf(" ");
|
|
token_type = val & 0xF;
|
|
token_data = txio.read_data_word(ref address);
|
|
switch ((gv2_tokentype)token_type)
|
|
{
|
|
case gv2_tokentype.TT_ELEMENTARY:
|
|
if (token_data < GV2_elementary.Length)
|
|
txio.tx_printf(GV2_elementary[token_data]);
|
|
else
|
|
txio.tx_printf("UNKNOWN_ELEMENTARY");
|
|
break;
|
|
case gv2_tokentype.TT_PREPOSITION:
|
|
prep_address = token_data;
|
|
txio.decode_text(ref prep_address);
|
|
break;
|
|
case gv2_tokentype.TT_NOUNR:
|
|
txio.tx_printf("noun = [parse ${0:X4}]", token_data);
|
|
break;
|
|
case gv2_tokentype.TT_ATTRIBUTE:
|
|
txio.tx_printf("ATTRIBUTE(");
|
|
if (symbols.print_attribute_name(attr_names_base, (int)token_data) == 0)
|
|
{
|
|
txio.tx_printf("{0}", token_data);
|
|
}
|
|
txio.tx_printf(")", token_data);
|
|
break;
|
|
case gv2_tokentype.TT_SCOPER:
|
|
txio.tx_printf("scope = [parse ${0:X4}]", token_data);
|
|
break;
|
|
case gv2_tokentype.TT_ROUTINE:
|
|
txio.tx_printf("[parse ${0:X4}]", token_data);
|
|
break;
|
|
default:
|
|
txio.tx_printf("UNKNOWN");
|
|
break;
|
|
}
|
|
val = txio.read_data_byte(ref address);
|
|
}
|
|
txio.tx_printf("\"");
|
|
if ((action & 0x0400) > 0)
|
|
txio.tx_printf(" REVERSE");
|
|
}
|
|
else if (parser_type >= (int)tx_h.parser_types.inform5_grammar)
|
|
{
|
|
/* Inform 5 and GV1 verb entries are just a series of tokens */
|
|
|
|
txio.tx_printf("\"");
|
|
|
|
/* Print verb if one is present */
|
|
|
|
verb_address = lookup_word(0L, verb_index, tx_h.VERB, (uint)parser_type);
|
|
|
|
if (verb_address > 0)
|
|
txio.decode_text(ref verb_address);
|
|
else
|
|
txio.tx_printf("no-verb");
|
|
|
|
objs = txio.read_data_byte(ref address);
|
|
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
val = txio.read_data_byte(ref address);
|
|
if (val < 0xb0)
|
|
{
|
|
if (val == 0 && objs == 0)
|
|
break;
|
|
txio.tx_printf(" ");
|
|
if (val == 0)
|
|
txio.tx_printf("NOUN");
|
|
else if (val == 1)
|
|
txio.tx_printf("HELD");
|
|
else if (val == 2)
|
|
txio.tx_printf("MULTI");
|
|
else if (val == 3)
|
|
txio.tx_printf("MULTIHELD");
|
|
else if (val == 4)
|
|
txio.tx_printf("MULTIEXCEPT");
|
|
else if (val == 5)
|
|
txio.tx_printf("MULTIINSIDE");
|
|
else if (val == 6)
|
|
txio.tx_printf("CREATURE");
|
|
else if (val == 7)
|
|
txio.tx_printf("SPECIAL");
|
|
else if (val == 8)
|
|
txio.tx_printf("NUMBER");
|
|
else if (val >= 16 && val < 48)
|
|
txio.tx_printf("NOUN [parse {0}]", val - 16);
|
|
else if (val >= 48 && val < 80)
|
|
txio.tx_printf("TEXT [parse {0}]", val - 48);
|
|
else if (val >= 80 && val < 112)
|
|
txio.tx_printf("SCOPE [parse {0}]", val - 80);
|
|
else if (val >= 128 && val < 176)
|
|
{
|
|
txio.tx_printf("ATTRIBUTE(");
|
|
if (symbols.print_attribute_name(attr_names_base, (int)(val - 128)) == 0)
|
|
{
|
|
txio.tx_printf("{0}", val - 128);
|
|
}
|
|
txio.tx_printf(")");
|
|
}
|
|
else
|
|
txio.tx_printf("UNKNOWN");
|
|
objs--;
|
|
}
|
|
else
|
|
{
|
|
txio.tx_printf(" ");
|
|
show_preposition(val, prep_type, prep_table_base);
|
|
}
|
|
}
|
|
|
|
txio.tx_printf("\"");
|
|
}
|
|
else
|
|
{
|
|
|
|
address = verb_entry;
|
|
preps[0] = preps[1] = 0;
|
|
|
|
/* Calculate noun count and prepositions */
|
|
|
|
if (parser_type == (int)tx_h.parser_types.infocom_fixed)
|
|
{
|
|
|
|
/* Fixed length parse table format */
|
|
|
|
/* Object count in 1st byte, preposition indices in next two bytes */
|
|
|
|
objs = (uint)txio.read_data_byte(ref address);
|
|
preps[0] = (uint)txio.read_data_byte(ref address);
|
|
preps[0] = (preps[0] >= 0x80) ? preps[0] : 0;
|
|
preps[1] = (uint)txio.read_data_byte(ref address);
|
|
preps[1] = (preps[1] >= 0x80) ? preps[1] : 0;
|
|
}
|
|
else
|
|
{
|
|
|
|
/* Variable length parse table format */
|
|
|
|
/* Object count in top two bits of first byte */
|
|
|
|
parse_data = (uint)txio.read_data_byte(ref address);
|
|
objs = (parse_data >> 6) & 0x03;
|
|
|
|
/* 1st preposition in bottom 6 bits of first byte. Fill in top two bits */
|
|
|
|
preps[0] = (parse_data & 0x3f) > 0 ? parse_data | 0xc0 : 0;
|
|
parse_data = (uint)txio.read_data_byte(ref address);
|
|
|
|
/* Check for more than one object */
|
|
|
|
if (objs > 0)
|
|
{
|
|
|
|
/* Skip object data */
|
|
|
|
parse_data = (uint)txio.read_data_byte(ref address);
|
|
parse_data = (uint)txio.read_data_byte(ref address);
|
|
|
|
/* Check for more than two objects */
|
|
|
|
if (objs > 1)
|
|
{
|
|
|
|
/* 2nd preposition in bottom 6 bits of byte. Fill in top two bits */
|
|
|
|
parse_data = (uint)txio.read_data_byte(ref address);
|
|
preps[1] = (parse_data & 0x3f) > 0 ? parse_data | 0xc0 : 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Check that there are 0 - 2 objects only */
|
|
|
|
if (objs > 2)
|
|
{
|
|
|
|
txio.tx_printf("Bad object count (%d)", (int)objs);
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
txio.tx_printf("\"");
|
|
|
|
/* Print verb if one is present */
|
|
|
|
verb_address = lookup_word(0L, verb_index, tx_h.VERB, (uint)parser_type);
|
|
|
|
if (verb_address > 0)
|
|
txio.decode_text(ref verb_address);
|
|
else
|
|
txio.tx_printf("no-verb");
|
|
|
|
/* Display any prepositions and objects if present */
|
|
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
if (preps[i] != 0)
|
|
{
|
|
txio.tx_printf(" ");
|
|
show_preposition(preps[i], prep_type, prep_table_base);
|
|
}
|
|
if (objs > (uint)i)
|
|
txio.tx_printf(" OBJ");
|
|
}
|
|
|
|
txio.tx_printf("\"");
|
|
|
|
}
|
|
}
|
|
|
|
}/* show_verb_grammar */
|
|
|
|
/*
|
|
* show_preposition
|
|
*
|
|
* Display a preposition by index.
|
|
*/
|
|
|
|
static void show_preposition(uint prep,
|
|
int prep_type,
|
|
ulong prep_table_base)
|
|
{
|
|
ulong address, text_address;
|
|
uint prep_count, prep_num;
|
|
int i;
|
|
|
|
address = prep_table_base;
|
|
prep_count = (uint)txio.read_data_word(ref address);
|
|
|
|
/* Iterate through the preposition table looking for a match */
|
|
|
|
for (i = 0; (uint)i < prep_count; i++)
|
|
{
|
|
text_address = txio.read_data_word(ref address);
|
|
if (prep_type == 0)
|
|
prep_num = (uint)txio.read_data_word(ref address);
|
|
else
|
|
prep_num = (uint)txio.read_data_byte(ref address);
|
|
|
|
/* If the indices match then print the preposition text */
|
|
|
|
if (prep == prep_num)
|
|
{
|
|
txio.decode_text(ref text_address);
|
|
return;
|
|
}
|
|
}
|
|
|
|
}/* show_preposition */
|
|
|
|
/*
|
|
* lookup_word
|
|
*
|
|
* Look up a word in the dictionary based on its type; verb, preposition, etc.
|
|
* The return entry is used to restart the search from the last word found.
|
|
*/
|
|
|
|
private static ulong lookup_word(ulong entry,
|
|
uint number,
|
|
uint mask,
|
|
uint parser_type)
|
|
{
|
|
ulong address, word_address, first_word, last_word;
|
|
uint word_count, word_size, flags, data;
|
|
|
|
/* Calculate dictionary bounds and entry size */
|
|
|
|
address = (ulong)txio.header.dictionary;
|
|
ulong temp = (ulong)txio.read_data_byte(ref address);
|
|
address += temp;
|
|
word_size = txio.read_data_byte(ref address);
|
|
word_count = txio.read_data_word(ref address);
|
|
first_word = address;
|
|
last_word = address + ((word_count - 1) * word_size);
|
|
|
|
/* If entry is 0 then set to first word, otherwise advance to next word */
|
|
|
|
if (entry == 0)
|
|
entry = first_word;
|
|
else
|
|
entry += word_size;
|
|
|
|
/* Correct Inform verb mask -- Inform sets both 0x40 and 0x01, but only 0x01 is documented */
|
|
if ((mask == tx_h.VERB) && (parser_type >= (int)tx_h.parser_types.inform5_grammar))
|
|
mask = (uint)tx_h.VERB_INFORM;
|
|
|
|
/* Scan down the dictionary from entry looking for a match */
|
|
|
|
for (word_address = entry; word_address <= last_word; word_address += word_size)
|
|
{
|
|
|
|
/* Skip to flags byte and read it */
|
|
|
|
if (parser_type != (int)tx_h.parser_types.infocom6_grammar)
|
|
{
|
|
address = word_address + (ulong)(((uint)txio.header.version < tx_h.V4) ? 4 : 6);
|
|
flags = txio.read_data_byte(ref address);
|
|
}
|
|
else
|
|
{
|
|
address = word_address + word_size - 1;
|
|
flags = txio.read_data_byte(ref address);
|
|
address = word_address + 6;
|
|
}
|
|
|
|
/* Check if this word is the type we are looking for */
|
|
|
|
if ((flags & mask) > 0)
|
|
{
|
|
|
|
if ((parser_type == (int)tx_h.parser_types.infocom6_grammar) || (parser_type >= (int)tx_h.parser_types.inform_gv2a))
|
|
{
|
|
data = (uint)txio.read_data_word(ref address);
|
|
}
|
|
else if (parser_type <= (int)tx_h.parser_types.inform_gv1)
|
|
{
|
|
/* Infocom, Inform 5, GV1. Verbs only for Inform */
|
|
/* Read the data for the word */
|
|
|
|
data = (uint)txio.read_data_byte(ref address);
|
|
|
|
/* Skip to next byte under some circumstances */
|
|
|
|
if (((mask == tx_h.VERB) && (flags & tx_h.DATA_FIRST) != tx_h.VERB_FIRST) ||
|
|
((mask == tx_h.DESC) && (flags & tx_h.DATA_FIRST) != tx_h.ADJ_FIRST))
|
|
data = (uint)txio.read_data_byte(ref address);
|
|
}
|
|
else
|
|
{
|
|
/* GV2, Inform 6.10 version */
|
|
data = (uint)txio.read_data_byte(ref address);
|
|
}
|
|
|
|
/* If this word matches the type and index then return its address */
|
|
|
|
if (data == number)
|
|
return (word_address);
|
|
}
|
|
}
|
|
|
|
/* Return 0 if no more words found */
|
|
|
|
return 0;
|
|
|
|
}/* lookup_word */
|
|
}
|
|
} |