Module: NWN::Gff::Struct
- Defined in:
- lib/nwn/gff/struct.rb,
lib/nwn/json_support.rb,
lib/nwn/yaml_support.rb
Overview
A Gff::Struct is a hash of label->Element pairs with some meta-information in local variables.
Constant Summary collapse
- DEFAULT_DATA_VERSION =
"V3.2"
Instance Attribute Summary collapse
-
#data_version ⇒ Object
The file version.
-
#element ⇒ Object
The field this struct is value of.
-
#struct_id ⇒ Object
GFF struct type.
Class Method Summary collapse
-
.new(struct_id = 0xffffffff, data_type = nil, data_version = DEFAULT_DATA_VERSION) {|s| ... } ⇒ Object
Create a new struct.
-
.unbox!(o, parent = nil) ⇒ Object
Deep-unboxes a Hash, e.g.
Instance Method Summary collapse
-
#/(path) ⇒ Object
An alias for
by_path
. -
#add_field(label, type, value = nil) {|| ... } ⇒ Object
Create a new field.
-
#box ⇒ Object
Returns a hash of this Struct without the API calls mixed in, converting it from the native charset.
-
#by_path(path) ⇒ Object
Retrieve an object from within the given tree.
-
#data_type ⇒ Object
Each Gff::Struct has a data_type, which describes the type of data the struct contains.
-
#data_type=(k) ⇒ Object
Overrides the data type (used by the built-in file format readers).
-
#each_by_flat_path(prefix = "/", &block) ⇒ Object
Example: “/AddCost” => ...
-
#method_missing(meth, *av, &block) ⇒ Object
:nodoc:.
- #path ⇒ Object
-
#to_gff(data_type = nil) ⇒ Object
Dump this struct as GFF binary data.
- #to_json(*a) ⇒ Object
- #to_s ⇒ Object
- #to_yaml(opts = {}) ⇒ Object
- #to_yaml_type ⇒ Object
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(meth, *av, &block) ⇒ Object
:nodoc:
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/nwn/gff/struct.rb', line 107 def method_missing meth, *av, &block # :nodoc: if meth.to_s =~ /^add_(.+)$/ if NWN::Gff::Types.index($1.to_sym) av.size >= 1 || av.size <= 2 or raise(NoMethodError, "undefined method #{meth} (requires two arguments to infer add_any)") t = $1.to_sym b = av[1] || NWN::Gff::Field::DEFAULT_VALUES[t] || raise(NoMethodError, "undefined method #{meth} (type #{t.inspect} requires explicit value)") f = add_field(av[0], t, b, &block) return f else super end end super end |
Instance Attribute Details
#data_version ⇒ Object
The file version. Usually “V3.2”. If not given in a source format, DEFAULT_DATA_VERSION is inferred and set for all structs.
8 9 10 |
# File 'lib/nwn/gff/struct.rb', line 8 def data_version @data_version end |
#element ⇒ Object
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).
19 20 21 |
# File 'lib/nwn/gff/struct.rb', line 19 def element @element end |
#struct_id ⇒ Object
GFF struct type. The default is 0xffffffff.
11 12 13 |
# File 'lib/nwn/gff/struct.rb', line 11 def struct_id @struct_id end |
Class Method Details
.new(struct_id = 0xffffffff, data_type = nil, data_version = DEFAULT_DATA_VERSION) {|s| ... } ⇒ Object
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.
66 67 68 69 70 71 72 73 |
# File 'lib/nwn/gff/struct.rb', line 66 def self.new struct_id = 0xffffffff, data_type = nil, data_version = DEFAULT_DATA_VERSION s = {}.extend(self) s.struct_id = struct_id s.data_type = data_type s.data_version = data_version yield(s) if block_given? s end |
.unbox!(o, parent = nil) ⇒ Object
Deep-unboxes a Hash, e.g. iterating down, converting it to the native charset.
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
# File 'lib/nwn/gff/struct.rb', line 226 def self.unbox! o, parent = nil o.extend(NWN::Gff::Struct) o.element = parent if parent o.struct_id = o.delete('__struct_id') o.data_type = o.delete('__data_type') o.data_version = o.delete('__data_version') o.data_version ||= NWN::Gff::Struct::DEFAULT_DATA_VERSION NWN.log_debug("Unboxed without a root data type") if !parent && !o.data_type NWN.log_debug("Unboxed with explicit data type #{o.data_type.inspect}") if parent && o.data_type o.each {|label,element| o[label] = NWN::Gff::Field.unbox!(element, label, o) } o end |
Instance Method Details
#/(path) ⇒ Object
An alias for by_path
.
220 221 222 |
# File 'lib/nwn/gff/struct.rb', line 220 def / path by_path(path) end |
#add_field(label, type, value = nil) {|| ... } ⇒ Object
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
94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/nwn/gff/struct.rb', line 94 def add_field label, type, value = nil, &block value ||= NWN::Gff::Field::DEFAULT_VALUES[type] || raise(ArgumentError, "type #{type.inspect} requires explicit value") self[label] = NWN::Gff::Field.new(label, type, value) self[label].parent = self yield(self[label]) if block_given? if self[label].field_value.is_a?(NWN::Gff::Struct) self[label].field_value.element = self[label] end self[label] end |
#box ⇒ Object
Returns a hash of this Struct without the API calls mixed in, converting it from the native charset.
248 249 250 251 252 253 254 255 256 257 258 259 260 261 |
# File 'lib/nwn/gff/struct.rb', line 248 def box t = Hash[self] t.merge!({ '__struct_id' => self.struct_id }) t.merge!({ '__data_version' => self.data_version, }) if self.data_version && self.data_version != NWN::Gff::Struct::DEFAULT_DATA_VERSION t.merge!({ '__data_type' => self.data_type }) if @data_type t end |
#by_path(path) ⇒ Object
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.
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
# File 'lib/nwn/gff/struct.rb', line 171 def by_path path struct = self current_path = "" path = path.split('/').map {|v| v.strip }.reject {|v| v.empty?}.join('/') path, mod = $1, $2 if path =~ /^(.+?)([\$\?%])?$/ path.split('/').each_with_index {|v, path_index| if struct.is_a?(NWN::Gff::Field) && struct.field_type == :cexolocstr && v =~ /^\d+$/ && path_index == path.split('/').size - 1 struct = struct.field_value[v.to_i] break end v, index = $1, $2 if v =~ /^(.+?)\[(\d+)\]$/ struct = struct.v if struct.is_a?(NWN::Gff::Field) && struct.field_type == :struct struct = struct[v] if index struct.field_type == :list or raise NWN::Gff::GffPathInvalidError, "Specified a list offset for a non-list item: #{v}[#{index}]." struct = struct.field_value[index.to_i] end raise NWN::Gff::GffPathInvalidError, "Cannot find a path to /#{path} (at: #{current_path})." unless struct current_path += "/" + v current_path += "[#{index}]" if index } case mod when "$" struct.field_value when "?" struct.field_type when "%" struct.has_str_ref? ? struct.str_ref : NWN::Gff::Cexolocstr::DEFAULT_STR_REF else struct end end |
#data_type ⇒ Object
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 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.
37 38 39 |
# File 'lib/nwn/gff/struct.rb', line 37 def data_type @data_type end |
#data_type=(k) ⇒ Object
Overrides the data type (used by the built-in file format readers).
42 43 44 45 46 |
# File 'lib/nwn/gff/struct.rb', line 42 def data_type= k k = nil if k == "" NWN.log_debug("Setting explicit data_type for parented element") if k && @element @data_type = k end |
#each_by_flat_path(prefix = "/", &block) ⇒ Object
Example: “/AddCost” => ..
135 136 137 138 139 140 141 |
# File 'lib/nwn/gff/struct.rb', line 135 def each_by_flat_path prefix = "/", &block sort.each {|label, field| field.each_by_flat_path do |ll, lv| yield(prefix + label + ll, lv) end } end |
#path ⇒ Object
21 22 23 24 25 26 27 |
# File 'lib/nwn/gff/struct.rb', line 21 def path if @element @element.path else "/" end end |
#to_gff(data_type = nil) ⇒ Object
Dump this struct as GFF binary data.
Optionally specify data_type and data_version
56 57 58 |
# File 'lib/nwn/gff/struct.rb', line 56 def to_gff data_type = nil NWN::Gff::Writer.dump(self, data_type) end |
#to_json(*a) ⇒ Object
4 5 6 |
# File 'lib/nwn/json_support.rb', line 4 def to_json(*a) box.to_json(*a) end |
#to_s ⇒ Object
125 126 127 |
# File 'lib/nwn/gff/struct.rb', line 125 def to_s "<NWN::Gff::Struct #{self.data_type}/#{self.data_version}, #{self.keys.size} fields>" end |
#to_yaml(opts = {}) ⇒ Object
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/nwn/yaml_support.rb', line 54 def to_yaml(opts = {}) YAML::quick_emit(nil, opts) do |out| out.map(taguri, to_yaml_style) do |map| # Inline certain structs that are small enough. map.style = :inline if self.size <= 1 && self.values.select {|x| NWN::Gff::Handler::YAML::NonInlineableFields.index(x['type']) }.size == 0 map.add('__' + 'data_type', @data_type) if @data_type map.add('__' + 'data_version', @data_version) if @data_version && @data_version != DEFAULT_DATA_VERSION map.add('__' + 'struct_id', @struct_id) if @struct_id sort.each {|k,v| map.add(k,v) } end end end |