Cosmos/Demos/zMachine/Frotz.Net/source/Desktop/FrotzBase/ZTools/showobj.cs
2016-06-09 10:34:36 -04:00

508 lines
No EOL
18 KiB
C#

/*
* showobj - part of infodump
*
* Object display routines.
*/
using System.Collections.Generic;
namespace ZTools
{
internal static class showobj
{
/*
* configure_object_tables
*
* Determine the object table start and end addresses, together with the
* property data start and end addresses, and the number of objects.
*
* Format:
*
* The object information consists of two parts. Firstly a fixed table of
* objects and secondly, an area of variable property data.
*
* The format of the object varies between game types. For pre-V4 games
* the format is:
*
* struct zobject {
* ushort attributes[2];
* unsigned char parent;
* unsigned char next;
* unsigned char child;
* ushort property_offset;
* }
*
* Post-V3 the format is:
*
* struct zobject {
* ushort attributes[3];
* ushort parent;
* ushort next;
* ushort child;
* ushort property_offset;
* }
*
* Attributes are an array of bits that can be tested, set and cleared. The
* parent, next and child fields are object numbers. These fields are used to
* construct an object tree that represents concepts such as object contains
* or room contains. The property offset is the address in the data file of the
* start of the property data for the object. Objects are numbered from 1.
* Object 0 is used as the NULL object to terminate object lists.
*
* Note: The start of the object table contains a list of default property
* values that are used when a property is not present for an object. The size
* of this table in words is the maximum number of properties minus 1.
*
* The format of the object properties is complex. It is:
*
* [Common prefix][property n]...[property n][property 0]
*
* Properties occur in descending order from highest property number to zero.
* Property zero always terminates the list, but is not referenced by the Z-code.
* Instead, property zero is used to terminate the scan down the property list, if
* a property is not defined. This behaviour is required when loading a default
* property list item, or to catch setting undefined property values.
*
* The size information is ignored for property 0, which is actually just specified
* as a byte containing 0x00.
*
* Key:
*
* (n) = size of block in bytes
* max = maximum number of recurring blocks
* min = minimum number of recurring blocks
*
* Common prefix:
*
* (1) (2) (2)
* +-------+ +------+ +------+
* | count | | text | ... | text | max=255, min=0
* +-------+ +------+ +------+
*
* count = number of following text blocks
* text = object description, encoded
*
* Property n (V3 format):
*
* (1) (1) (1)
* +--------+----+ +------+ +------+
* | size-1 | id | | data | ... | data | max=8, min=1
* +--------+----+ +------+ +------+
* 7 5 4 0
*
* size-1 = size of property - 1
* id = property identifier
* data = property data
*
* Maximum property number = 31
*
* Property n (V4 format):
*
* (1) Property header byte (1) Property size byte (1) (1)
* +-----------+-----------+----+ +-----------+---------+------+ +------+ +------+
* | size byte | word data | id | | size byte | ignored | size | | data | ... | data | max=63, min=0
* +-----------+-----------+----+ +-----------+---------+------+ +------+ +------+
* 7 6 5 0 7 6 5 0
*
* size byte = if set then next data block is a the property size byte
* if clear then the 'word data' flag is checked and the property has no size byte
* word data = if set then 2 data blocks follow
* if clear 1 data block follows
* ignored = this flag is not used by the property manipulation opcodes, it can be set to an arbitary value
* (note: this bit could be used to increase the property size from 63 to 127 bytes)
* id = property identifier
* size = size of property
* data = property data
*
* Maximum property number = 63
*/
internal static void configure_object_tables(out int obj_count,
out ulong obj_table_base,
out ulong obj_table_end,
out ulong obj_data_base,
out ulong obj_data_end)
{
ulong object_address, address;
uint data_count, data;
obj_table_base = 0;
obj_table_end = 0;
obj_data_base = 0;
obj_data_end = 0;
obj_count = 0;
/* The object table address comes from the header */
obj_table_base = (ulong)txio.header.objects;
/* Calculate the number of objects and property addresses range */
do
{
/* Count this object and get its address */
//(*obj_count)++;
//object_address = (ulong)get_object_address(*obj_count);
obj_count++;
object_address = (ulong)get_object_address(obj_count);
/* Check if we have got to the end of the object list */
if (obj_data_base == 0 || object_address < obj_data_base)
{
/* Calculate the range of property data */
if ((uint)txio.header.version < tx_h.V4)
object_address += tx_h.O3_PROPERTY_OFFSET;
else
object_address += tx_h.O4_PROPERTY_OFFSET;
address = txio.read_data_word(ref object_address);
if (obj_data_base == 0 || address < obj_data_base)
obj_data_base = address;
if (obj_data_end == 0 || address > obj_data_end)
obj_data_end = address;
}
} while (object_address < obj_data_base);
obj_table_end = object_address - 1;
/* Skip any description for the last property */
if (txio.read_data_byte(ref obj_data_end) > 0)
while (((uint)txio.read_data_word(ref obj_data_end) & 0x8000) == 0)
;
/* Skip any properties to calculate the end address of the last property */
while ((data = txio.read_data_byte(ref obj_data_end)) != 0)
{
if ((uint)txio.header.version < tx_h.V4)
data_count = (uint)(((data & txio.property_size_mask) >> 5) + 1);
else if ((data & 0x80) > 0)
data_count = (uint)(txio.read_data_byte(ref obj_data_end) & txio.property_size_mask);
else if ((data & 0x40) > 0)
data_count = 2;
else
data_count = 1;
obj_data_end += data_count;
}
obj_data_end--;
// (*obj_data_end)--; // TODO I'm not sure about this
}/* configure_object_tables */
/*
* show_objects
*
* List all objects and property data.
*/
internal static void show_objects(int symbolic)
{
ulong object_address, address;
ulong obj_table_base, obj_table_end, obj_data_base, obj_data_end;
uint data, pobj, nobj, cobj;
int i, j, k, list;
ushort inform_version;
ulong class_numbers_base, class_numbers_end;
ulong property_names_base, property_names_end;
ulong attr_names_base, attr_names_end;
int obj_count;
/* Get objects configuration */
configure_object_tables(out obj_count, out obj_table_base, out obj_table_end,
out obj_data_base, out obj_data_end);
if (symbolic != 0)
{
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;
}
txio.tx_printf("\n **** Objects ****\n\n");
txio.tx_printf(" Object count = {0}\n", obj_count);
/* Iterate through each object */
for (i = 1; (uint)i <= obj_count; i++)
{
txio.tx_printf("\n");
/* Get address of object */
object_address = (ulong)get_object_address(i);
/* Display attributes */
txio.tx_printf("{0:d3}. Attributes: ", (int)i);
list = 0;
for (j = 0; j < (((uint)txio.header.version < tx_h.V4) ? 4 : 6); j++)
{
data = (uint)txio.read_data_byte(ref object_address);
for (k = 7; k >= 0; k--)
{
if (((data >> k) & 1) > 0)
{
txio.tx_printf("{0}", (list++) > 0 ? ", " : "");
if (symbols.print_attribute_name(attr_names_base, (int)((j * 8) + (7 - k))) > 0)
txio.tx_printf("({0:d})", (int)((j * 8) + (7 - k)));
else
txio.tx_printf("{0:d}", (int)((j * 8) + (7 - k)));
}
}
}
if (list == 0)
txio.tx_printf("None");
txio.tx_printf("\n");
/* Get object linkage information */
if ((uint)txio.header.version < tx_h.V4)
{
pobj = (uint)txio.read_data_byte(ref object_address);
nobj = (uint)txio.read_data_byte(ref object_address);
cobj = (uint)txio.read_data_byte(ref object_address);
}
else
{
pobj = (uint)txio.read_data_word(ref object_address);
nobj = (uint)txio.read_data_word(ref object_address);
cobj = (uint)txio.read_data_word(ref object_address);
}
address = txio.read_data_word(ref object_address);
txio.tx_printf(" Parent object: {0:d3} ", (int)pobj);
txio.tx_printf("Sibling object: {0:d3} ", (int)nobj);
txio.tx_printf("Child object: {0:d3}\n", (int)cobj);
txio.tx_printf(" Property address: {0:X4}\n", (ulong)address);
txio.tx_printf(" Description: \"");
/* If object has a description then display it */
if ((uint)txio.read_data_byte(ref address) > 0)
txio.decode_text(ref address);
txio.tx_printf("\"\n");
/* Print property list */
txio.tx_printf(" Properties:\n");
print_property_list(ref address, property_names_base);
}
}/* show_objects */
/*
* get_object_address
*
* Given an object number calculate the data file address of the object data.
*/
private static int get_object_address(int obj)
{
int offset;
/* Address calculation is object table base + size of default properties area +
object number-1 * object size */
offset = txio.header.objects;
if ((uint)txio.header.version <= tx_h.V3)
offset += ((tx_h.P3_MAX_PROPERTIES - 1) * 2) + ((obj - 1) * tx_h.O3_SIZE);
else
offset += ((tx_h.P4_MAX_PROPERTIES - 1) * 2) + ((obj - 1) * tx_h.O4_SIZE);
return offset;
}/* get_object_address */
/*
* print_property_list
*
* Display the data associated with each object property.
*/
internal static void print_property_list(ref ulong address, ulong property_names_base)
{
int data, count;
/* Scan down the property address displaying each property */
for (data = txio.read_data_byte(ref address); data > 0; data = txio.read_data_byte(ref address))
{
txio.tx_printf(" ");
if (symbols.print_property_name(property_names_base, (int)(data & txio.property_mask)) > 0)
txio.tx_printf("\n ");
else
txio.tx_printf(" ");
txio.tx_printf("[%2d] ", (int)(data & txio.property_mask));
if ((uint)txio.header.version <= tx_h.V3)
count = ((data & txio.property_size_mask) >> 5) + 1;
else if ((data & 0x80) > 0)
count = (int)((uint)txio.read_data_byte(ref address) & txio.property_size_mask);
else if ((data & 0x40) > 0)
count = 2;
else
count = 1;
while (count-- > 0)
txio.tx_printf("{0:X2} ", (uint)txio.read_data_byte(ref address));
txio.tx_printf("\n");
}
}/* print_property_list */
/*
* show_tree
*
* Use the object linkage information to display a hierarchical list of
* objects.
*/
internal static void show_tree()
{
ulong object_address;
ulong obj_table_base, obj_table_end, obj_data_base, obj_data_end;
int i, parent;
int obj_count;
/* Get objects configuration */
configure_object_tables(out obj_count, out obj_table_base, out obj_table_end,
out obj_data_base, out obj_data_end);
txio.tx_printf("\n **** Object tree ****\n\n");
/* Iterate through each object */
for (i = 1; i <= obj_count; i++)
{
/* Get object address */
object_address = (ulong)get_object_address(i);
/* Get parent for this object */
if ((uint)txio.header.version <= tx_h.V3)
{
object_address += tx_h.O3_PARENT;
parent = txio.read_data_byte(ref object_address);
}
else
{
object_address += tx_h.O4_PARENT;
parent = txio.read_data_word(ref object_address);
}
/*
* If object has no parent then it is a root object so display the tree
* from the object.
*/
if (parent == 0)
print_object((int)i, 0);
}
}/* show_tree */
/*
* print_object
*
* Print an object description and its children for a point in the object tree.
*/
private static void print_object(int obj, int depth)
{
ulong object_address, address;
int child, i;
/* Continue until the next object number is NULL */
while (obj > 0)
{
/* Display object depth and description */
for (i = 0; i < depth; i++)
txio.tx_printf(" . ");
txio.tx_printf("[{0:d3}] ", (int)obj);
print_object_desc(obj);
txio.tx_printf("\n");
/* Get object address */
object_address = (ulong)get_object_address(obj);
/* Get any child object and the next object at this level */
if ((uint)txio.header.version <= tx_h.V3)
{
address = object_address + tx_h.O3_CHILD;
child = txio.read_data_byte(ref address);
address = object_address + tx_h.O3_NEXT;
obj = txio.read_data_byte(ref address);
}
else
{
address = object_address + tx_h.O4_CHILD;
child = txio.read_data_word(ref address);
address = object_address + tx_h.O4_NEXT;
obj = txio.read_data_word(ref address);
}
/* If this object has a child then print its tree */
if (child > 0)
print_object(child, depth + 1);
}
}/* print_object */
/*
* print_object_description
*
* Display the description of an object.
*/
internal static void print_object_desc(int obj)
{
ulong object_address, address;
txio.tx_printf("\"");
/* Check for a NULL object number */
if (obj > 0)
{
/* Get object address */
object_address = (ulong)get_object_address(obj);
if ((uint)txio.header.version <= tx_h.V3)
address = object_address + tx_h.O3_PROPERTY_OFFSET;
else
address = object_address + tx_h.O4_PROPERTY_OFFSET;
/* Get the property address */
address = txio.read_data_word(ref address);
/* Display the description if the object has one */
if ((uint)txio.read_data_byte(ref address) > 0)
txio.decode_text(ref address);
}
txio.tx_printf("\"");
}/* print_object_desc */
}
}