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:
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/nwn/gff/struct.rb', line 109 def method_missing meth, *av, &block # :nodoc: if meth.to_s =~ /^add_(.+)$/ if NWN::Gff::Types.key($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.
68 69 70 71 72 73 74 75 |
# File 'lib/nwn/gff/struct.rb', line 68 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.
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
# File 'lib/nwn/gff/struct.rb', line 228 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
.
222 223 224 |
# File 'lib/nwn/gff/struct.rb', line 222 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
96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/nwn/gff/struct.rb', line 96 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.
250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
# File 'lib/nwn/gff/struct.rb', line 250 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.
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 218 219 |
# File 'lib/nwn/gff/struct.rb', line 173 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 47 |
# 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 && defined?(@element) && !@element.nil? @data_type = k end |
#each_by_flat_path(prefix = "/", &block) ⇒ Object
Example: “/AddCost” => ..
137 138 139 140 141 142 143 |
# File 'lib/nwn/gff/struct.rb', line 137 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 defined?(@element) && !@element.nil? @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
58 59 60 |
# File 'lib/nwn/gff/struct.rb', line 58 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
127 128 129 |
# File 'lib/nwn/gff/struct.rb', line 127 def to_s "<NWN::Gff::Struct #{self.data_type}/#{self.data_version}, #{self.keys.size} fields>" end |
#to_yaml(opts = {}) ⇒ Object
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/nwn/yaml_support.rb', line 55 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 |