mirror of
https://github.com/danbulant/Cosmos
synced 2026-05-27 14:02:19 +00:00
508 lines
No EOL
18 KiB
C#
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 */
|
|
}
|
|
} |