Dependencies: lib_2da, lib_anon, lib_array, lib_fn, lib_ini, lib_sfo, lib_sugar, lib_tools
Functions to interact with IE resources under the general 'ie-struct' paradigm. i.e. we read in the struct, edit it, and then write it back again.
struct_add(insert_point:i="-1", number:i=1, auto_open:b=1, auto_close:b=1, equip:b=1, replace:b, debug:b=1, struct:s, type:s, patch:f, match_parent:f, vertices:s, slots:s)=(struct:a) dimorphic
Adds 'number' new entries to the list of extended headers of type 'type', and then applies 'patch' to them. Entries are inserted after header 'insert_point', or after the last header if there is no such header (this is the default). 'struct' is the struct in which the data is stored and is passed back at the end of the function.
If 'type' is of form 'parent_child' (e.g. 'ab_fx'), instead add entries of type 'child' as children of type 'parent' in the same fashion, whenever the parent returns true to 'match_parent'.
You can use the anonymous function construct with 'patch', and 'match_parent'. By default (i.e. unless altered by 'auto_open' and 'auto_close'), the anonymous function starts by opening the header into the struct 's', and finishes by writing it back. Also by default, the parent (if any) is opened into the struct 'p'.
We can't enforce it, but to be meaningful the in and out 'struct' need to be the same (since we only write the changes).
If appropriate (e.g. for adding known spells to CRE files) the header array is sorted before reinsertion.
struct_alter(auto_open:b=1, auto_close:b=1, debug:b=1, equip:b, replace:b, struct:a, type:s, match:f, patch:f, match_parent:f, vertices:s, slots:s)=(struct:a) dimorphic
Applies a patch function 'patch' to every extended header of type 'type' that returns true (1) to function 'match', or to every such header if no function is specified. 'struct' is the struct in which the data is stored and is passed back at the end of the function.
If 'type' is of form 'parent_child' (e.g. 'ab_fx'), instead apply 'patch' to every child header of type 'child' whose parent is type 'parent', and where the child returns true to 'match' and the parent returns true to 'match_parent'.
You can use the anonymous function construct with 'patch', 'match', and 'match_parent'. By default (i.e. unless altered by 'auto_open' and 'auto_close'), the anonymous function starts by opening the header into the struct 's', and finishes by writing it back. Also by default, the parent (if any) is opened into the struct 'p'.
We can't enforce it, but to be meaningful the in and out 'struct' need to be the same (since we only write the changes).
struct_apply_regexp(auto_open:i, write:i=1, report_back:i=1, ext:s, regexp:s=".*", function:s, type:s, strtype:s)=(array:a) action
Quickly apply the function 'function' to every header of the specified type where the file matches 'regexp'. Return an array of all entries where the fn returns 'value' for at least one entry.
struct_clone(number:i=1, multi_match:i=9999, clone_above:i, auto_open:b=1, auto_close:b=1, open_on_match:b, open_parent:b, debug:b=1, struct:s, type:s, match:f, patch:f, match_parent:f, vertices:s)=(struct:a) dimorphic
Adds 'number' copies of any extended header of type 'type' which return true to the function 'match', and then applies 'patch' to them. 'struct' is the struct in which the data is stored and is passed back at the end of the function.
If 'type' is of form 'parent_child' (e.g. 'ab_fx'), instead clone entries of type 'child' as children of type 'parent' in the same fashion, under the additional requirement that the parent returns true to 'match_parent'.
You can use the anonymous function construct with 'patch', 'match', and 'match_parent'. By default (i.e. unless altered by 'auto_open' and 'auto_close'), the anonymous function starts by opening the header into the struct 's', and finishes by writing it back. Also by default, the parent (if any) is opened into the struct 'p'.
We can't enforce it, but to be meaningful the in and out 'struct' need to be the same (since we only write the changes).
struct_copy(allow_missing:i, tv:i, debug:i=1, file:s, ext:s, edits:s, path:s, location:s, locbase:s, source_path:s, source_location:s, source_locbase:s)=() action
'file' is a string of k=>v pairs (or string of single entries, treated as k=. Copy each k to v (or k.default_ext to v.default_ext if it is set). k and v are located at SFO-standard locations defined by, respectively, (source_path/source_location/source_locbase) and (path/location/locbase), with 'override' as the default in each case.
Apply 'edits' as an anonymous patch function in the process, opening k into struct m in the process and writing it at the end.)
struct_debug(function:s, strtype:s, struct:s)=() dimorphic
Checks an expression (assumed to be an anon function) for apparent references to nonexistent keys.
struct_delete(auto_open:b=1, debug:b=1, struct:s, type:s, match:f, match_parent:f)=(struct:a) dimorphic
Delete any extended headers of type 'type' which return true to the function 'match'. 'struct' is the struct in which the data is stored and is passed back at the end of the function.
If 'type' is of form 'parent_child' (e.g. 'ab_fx'), instead delete entries of type 'child' as children of type 'parent' in the same fashion, under the additional requirement that the parent returns true to 'match_parent'.
You can use the anonymous function construct with 'match' and 'match_parent'. By default (i.e. unless altered by 'auto_open' and 'auto_close'), the anonymous function starts by opening the header into the struct 's'. Also by default, the parent (if any) is opened into the struct 'p'.
We can't enforce it, but to be meaningful the in and out 'struct' need to be the same (since we only write the changes).
struct_display_lookups(struct:s)=() dimorphic
Display the lookup table for the extended-header types. (For debugging.)
struct_echo(struct:s, strtype:s)=() dimorphic
Display the contents of the 'header' part of an IE struct. (For debugging.)
struct_edit(allow_missing:b, tv:b, debug:b, edit_strrefs_in_place:b, open_extended:b="-1", file:s, ext:s, edits:s, path:s, location:s, locbase:s)=() action
'object' is a list of strings s, interpreted as files. Locate each file at path|location|locbase, defaulting to in-game if all are empty. COPY or COPY_EXISTING COPY_EXISTING each s (or s.default_ext if it is set) over itself. Apply 'edits' as an anonymous patch function in the process, opening s into struct m in the process and writing it at the end.
struct_expand_slots(slots:s)=(slots:s) patch
struct_extract(array:a, struct:a)=(array:a) dimorphic
Given an array of keys, for each key which also keys the struct, set the corresponding array value to the struct value.
struct_get(arguments:s)=(value:s) patch
Returns the contents of a field. (Use for quick lightweight edits where it's not worth reading in the struct.)
struct_get_default_version(ext:s)=(strtype:s, version:s) dimorphic
Returns the game-default strtype for a given extension.
struct_get_offset_array(type:s)=(array:a) patch
Returns the offset array for type type of the currently-being-patched strtype
struct_get_offset_array2(offset:i, type:s)=(array:a) patch
Returns the secondary offset array at offset 'offset' for parent-child type type of the currently-being-patched strtype
struct_initialize action_macro
This reads in all the defining data for the various game files that lib_struct can patch. It's a macro because there are lots of them and they're dispersed over lots of arrays; it's not convenient to bundle them up.
Here's what we do:
struct_inject(array_in:a, struct:s)=(struct:a) dimorphic
Put each k1...k_n=>v pair in array_in into the struct.
struct_iter(struct:s, strtype:s, type:s)=(blockcount:s, length:s, iter_array:a) patch
For given subtype 'type', return an array k=>v, where k is the index of a 'type' element and v is its lookup in the struct; also return 'length', the number of 'type' elements, and 'blockcount', then length of the data block in the struct for that element.
(The struct version of GET_OFFSET_ARRAY)
struct_make(debug:i=1, file:s, ext:s, edits:s, version:s, path:s, location:s, locbase:s)=() action
Make a new object of type ext. Open it into a struct m, apply the contents of 'edits' as an anonymous function, and then write m back in again. If 'version' is unset, make the current game version.
struct_new(strtype:s, ext:s)=(struct:a) dimorphic
struct_read(open_header:b=1, open_extended:b=1, strtype:s, file:s, path:s)=(struct:a) action
struct_read(open_header:b=1, open_extended:b=1, strtype:s)=(struct:a) patch
From 'path/file', or from existing game file 'file' if path is unspecified, or in patch context the currently-open file, read in its contents according to its structure definition, into the structure 'struct'.The file type is specified by 'strtype'; note that we might well be editing an extended header in an INNER_PATCH, not the main file.
If strtype isn't specified, we assume we're patching a base file and we try to infer it from the file itself.
If open_header=0, we don't break up the header, we just read it in as one big string. (It takes maybe 5-10ms to process, so this is unlikely to be necessary - if you're doing bulk patching, struct is too slow anyway.)
If open_extended=0, just read in the header, don't bother with the extended contents.
struct_read_vartypes(strtype:s, type:s, data:s)=(data:s) patch
struct_write(write_header:b=1, write_extended:b=1, telemetry:b, overwrite:b, edit_strrefs_in_place:b, strtype:s, struct:s, file:s, path:s)=() action
struct_write(write_header:b=1, write_extended:b=1, edit_strrefs_in_place:b, telemetry:b, overwrite:b, struct:s, strtype:s)=() patch
Given a structure 'struct', write it to 'path/file', or to existing game file 'file' if path is unspecified, or to the currently-open file in patch context. The file type is specified by 'strtype'; note that we might well be editing an extended header in an INNER_PATCH, not the main file. If strype isn't specified, we assume we're patching a base file and we read it in from the struct itself.
if write_header=0, leave the header alone; similarly for write_extended.
struct_write_all_vartypes()=() patch
These should not be called from outside the library itself.
struct_add_child(auto_open:b=1, auto_close:b=1, debug:b=1, insert_point:i="-1", number:i=1, struct:s, pc_id:s, match_parent:f, patch:f)=(struct:a) patch
struct_alter_child(auto_open:b=1, auto_close:b=1, debug:b=1, struct:s, pc_id:s, match:f, match_parent:f, patch:f)=(struct:a) patch
struct_apply_to_strrefs_helper(function:s, struct:s)=(struct:a) patch
struct_clone_child(clone_above:i, number:i=1, auto_open:b=1, open_on_match:b, open_parent:b, debug:b=1, multi_match:i=9999, struct:s, pc_id:s, match:f, match_parent:f, patch:f)=(struct:a) patch
struct_delete_child(auto_open:b=1, debug:b=1, struct:s, pc_id:s, match:f, match_parent:f)=(struct:a) patch
struct_delete_orphaned_items(struct:s)=(struct:a) patch
struct_get_strtype(strtype:s)=(strtype:s) patch
struct_identify_slots(resref:s)=(slot:s, twohanded:s) patch
Given a resref (assumed to be of an item), deduce which creature inventory slot(s) it ought to go into
struct_init(type:s="toplevel", strtype:s)=() patch
Used internally by struct_add to set some default values when adding structs. (Use only if you can't set the default values via the struct-defining 2das.)
struct_parent_child_definitions(file:s, path:s)=(parent_child_data:a, parent_list:a, child_list:a, parent_child_id:a) action
Read in one of the 2das that defines the parent/child relations in a file type. (This stores where the child offsets are in the parent, and also serves to tell us that the parent/child relation exists). The 2da isn't ordered. Return the 2da as a 2d array. Also return lists, in the form k=>_, of the parents, the children, and the relation IDs.
struct_process_vertices(vertices:s)=(vertex_data:a) patch
Take as input a string in form
(identifier (int,int) list ) list
Return an array of form
vertex_data(identifier x n)=nth x-coordinate under identifier n vertex_data(identifier y n)=nth y-coordinate under identifier n vertex_data(identifier count)= how many x,y pairs under identifier n vertex_data(identifier box [left|right|top|bottom])=BB data for identifier-n coordinates
identifiers are 'base', 'open', 'closed', 'impeded_open', 'impeded_closed'. 'base' can be omitted if it's the first entry.
struct_read_main_definitions(file:s, path:s)=(has_strrefs:s, has_resrefs:s, offsets:a, types:a, flags:a, asciis:a, ids:a, lookups:a, commalists:a, multiples:a, resrefs:a, defaults:a) action
Read in one of the files that defines a header and return its contents in a bunch of arrays (all with the entry label as key):
struct_set_bb()=() patch
Not-at-all-encapsulated helper function that just sets the bounding box of a region, door or container according to its vertices
struct_set_slot(item_number:i, actual, struct, number,, not, the, lookup, replace:i, force_inv:i, equip:i=1, struct:s, slots:s, item_data:s)=(displaced_item_number:s, slot_number_assigned:s, struct:a) patch
struct_srt_cre_ks(arguments:s)=(value:s) patch
struct_write_postprocess(strtype:s)=() patch
ARE files need a bit of postprocessing to update the offsets for headers only used in saved-game data. PRO files need to be deflated if they don't use the extended header
struct_write_strtype(strtype:s)=() patch
The ie struct is organized like this: