A Gff::Struct is a hash of label->Element pairs with some meta-information in local variables.
Methods
public class
public instance
Constants
| DEFAULT_DATA_VERSION | = | "V3.2" |
Attributes
| data_version | [RW] | The file version. Usually “V3.2“. If not given in a source format, DEFAULT_DATA_VERSION is inferred and set for all structs. |
| element | [R] | The field this struct is value of. It is most likely a Field of :list, or :nil if it is the root struct. Setting this to a value detaches this struct from the old parent (though the old parent Field may still point to this object). |
| struct_id | [RW] | GFF struct type. The default is 0xffffffff. |
Public class methods
Create a new struct. Usually, you can leave out data_type and data_version for non-root structs, because that will be guess-inherited based on the existing associations.
You can pass a block to this method, which will receive the newly-created Struct as the only argument.
# File lib/nwn/gff/struct.rb, line 66 66: def self.new struct_id = 0xffffffff, data_type = nil, data_version = DEFAULT_DATA_VERSION 67: s = {}.extend(self) 68: s.struct_id = struct_id 69: s.data_type = data_type 70: s.data_version = data_version 71: yield(s) if block_given? 72: s 73: end
Deep-unboxes a Hash, e.g. iterating down, converting it to the native charset.
# File lib/nwn/gff/struct.rb, line 226 226: def self.unbox! o, parent = nil 227: o.extend(NWN::Gff::Struct) 228: o.element = parent if parent 229: o.struct_id = o.delete('__struct_id') 230: o.data_type = o.delete('__data_type') 231: o.data_version = o.delete('__data_version') 232: o.data_version ||= NWN::Gff::Struct::DEFAULT_DATA_VERSION 233: 234: NWN.log_debug("Unboxed without a root data type") if 235: !parent && !o.data_type 236: NWN.log_debug("Unboxed with explicit data type #{o.data_type.inspect}") if 237: parent && o.data_type 238: 239: o.each {|label,element| 240: o[label] = NWN::Gff::Field.unbox!(element, label, o) 241: } 242: 243: o 244: end
Public instance methods
An alias for by_path.
# File lib/nwn/gff/struct.rb, line 220 220: def / path 221: by_path(path) 222: end
Create a new field. Alternatively, you can use the shorthand methods:
add_#{type} - add_int, add_byte, ..
For example:
some_struct.add_field 'ID', :byte, 5
is equivalent to:
some_struct.add_byte 'ID', 5
You can pass a block to this method, which will receive the newly-created Field as an argument.
This allows for code like this:
Gff::Struct.new(0) do |s|
s.add_byte "Byte", 5
s.add_list "Some_List", [] do |l|
l.v << Gff::Struct.new ...
..
end
end
# File lib/nwn/gff/struct.rb, line 94 94: def add_field label, type, value = nil, &block 95: value ||= NWN::Gff::Field::DEFAULT_VALUES[type] || raise(ArgumentError, 96: "type #{type.inspect} requires explicit value") 97: self[label] = NWN::Gff::Field.new(label, type, value) 98: self[label].parent = self 99: yield(self[label]) if block_given? 100: if self[label].field_value.is_a?(NWN::Gff::Struct) 101: self[label].field_value.element = self[label] 102: end 103: self[label] 104: end
Returns a hash of this Struct without the API calls mixed in, converting it from the native charset.
# File lib/nwn/gff/struct.rb, line 248 248: def box 249: t = Hash[self] 250: t.merge!({ 251: '__struct_id' => self.struct_id 252: }) 253: t.merge!({ 254: '__data_version' => self.data_version, 255: }) if self.data_version && self.data_version != 256: NWN::Gff::Struct::DEFAULT_DATA_VERSION 257: t.merge!({ 258: '__data_type' => self.data_type 259: }) if @data_type 260: t 261: end
Retrieve an object from within the given tree. Path is a slash-separated destination, given as a string
Prefixed/postfixed slashes are optional.
You can retrieve CExoLocString values by giving the language ID as the last label:
/FirstName/0
You can retrieve list values by specifying the index in square brackets:
/SkillList[0]
/SkillList[0]/Rank => {"Rank"=>{"label"=>"Rank", "value"=>0, "type"=>:byte}}
You can directly retrieve field values and types instead of the field itself:
/SkillList[0]/Rank$ => 0 /SkillList[0]/Rank? => :byte
This will raise an error for non-field paths, naturally:
SkillList[0]$ => undefined method `field_value' for {"Rank"=>{"label"=>"Rank", "value"=>0, "type"=>:byte}}:Hash
SkillList[0]? => undefined method `field_type' for {"Rank"=>{"label"=>"Rank", "value"=>0, "type"=>:byte}}:Hash
For CExoLocStrings, you can retrieve the str_ref:
FirstName% => 4294967295
This will return DEFAULT_STR_REF (0xffffffff) if the given path does not have a str_ref.
# File lib/nwn/gff/struct.rb, line 171 171: def by_path path 172: struct = self 173: current_path = "" 174: path = path.split('/').map {|v| v.strip }.reject {|v| v.empty?}.join('/') 175: 176: path, mod = $1, $2 if path =~ /^(.+?)([\$\?%])?$/ 177: 178: path.split('/').each_with_index {|v, path_index| 179: if struct.is_a?(NWN::Gff::Field) && struct.field_type == :cexolocstr && 180: v =~ /^\d+$/ && path_index == path.split('/').size - 1 181: struct = struct.field_value[v.to_i] 182: break 183: end 184: 185: v, index = $1, $2 if v =~ /^(.+?)\[(\d+)\]$/ 186: 187: struct = struct.v if struct.is_a?(NWN::Gff::Field) && 188: struct.field_type == :struct 189: 190: struct = struct[v] 191: if index 192: struct.field_type == :list or raise NWN::Gff::GffPathInvalidError, 193: "Specified a list offset for a non-list item: #{v}[#{index}]." 194: 195: struct = struct.field_value[index.to_i] 196: end 197: 198: 199: raise NWN::Gff::GffPathInvalidError, 200: "Cannot find a path to /#{path} (at: #{current_path})." unless struct 201: 202: current_path += "/" + v 203: current_path += "[#{index}]" if index 204: } 205: 206: case mod 207: when "$" 208: struct.field_value 209: when "?" 210: struct.field_type 211: when "%" 212: struct.has_str_ref? ? struct.str_ref : 213: NWN::Gff::Cexolocstr::DEFAULT_STR_REF 214: else 215: struct 216: end 217: end
Each Gff::Struct has a data_type, which describes the type of data the struct contains. For top-level structs, this equals the data type written to the GFF file (“UTI”, for example); for sub structures, this is usually nil, but can be overriden by users explicitly to carry some meta-data (e.g. explicitly set UTC/ItemList[0] to UTI). This is not done automatically, however.
Scripts could use this, for example, to reliably re-attach a Item within /ItemList/ somewhere else, or export it as .uti.
# File lib/nwn/gff/struct.rb, line 37 37: def data_type 38: @data_type 39: end
Overrides the data type (used by the built-in file format readers).
# File lib/nwn/gff/struct.rb, line 42 42: def data_type= k 43: k = nil if k == "" 44: NWN.log_debug("Setting explicit data_type for parented element") if k && @element 45: @data_type = k 46: end
Example: “/AddCost” => {“type”=>:dword, ..}
# File lib/nwn/gff/struct.rb, line 135 135: def each_by_flat_path prefix = "/", &block 136: sort.each {|label, field| 137: field.each_by_flat_path do |ll, lv| 138: yield(prefix + label + ll, lv) 139: end 140: } 141: end
# File lib/nwn/gff/struct.rb, line 21 21: def path 22: if @element 23: @element.path 24: else 25: "/" 26: end 27: end
Dump this struct as GFF binary data.
Optionally specify data_type and data_version
# File lib/nwn/gff/struct.rb, line 56 56: def to_gff data_type = nil 57: NWN::Gff::Writer.dump(self, data_type) 58: end
# File lib/nwn/json_support.rb, line 4 4: def to_json(*a) 5: box.to_json(*a) 6: end
# File lib/nwn/gff/struct.rb, line 125 125: def to_s 126: "<NWN::Gff::Struct #{self.data_type}/#{self.data_version}, #{self.keys.size} fields>" 127: end