Module: NWN::Gff::Field

Defined in:
lib/nwn/gff/field.rb,
lib/nwn/json_support.rb,
lib/nwn/yaml_support.rb,
lib/nwn/gff/cexolocstr.rb

Overview

A Field wraps a GFF label->value pair, providing:

  • .field_type describing the field type (e.g. :int)

  • .field_value holding the value of this Field

and, if loaded by Gff::Reader or through YAML:

  • .field_label holding the label

  • .parent holding the struct this Field is child of.

Note that it is ADVISED to use the provided accessors, since they do some structure-keeping in the background. If you do NOT want it to do that, use hash-notation for access:

field['value'], field['type'], field['str_ref'], field['label']

Constant Summary collapse

DEFAULT_VALUES =

The default values of fields.

{
  :byte => 0,
  :char => 0,
  :word => 0,
  :short => 0,
  :dword => 0,
  :int => 0,
  :dword64 => 0,
  :int64 => 0,
  :float => 0.0,
  :double => 0.0,
  :cexostr => '',
  :resref => '',
  :cexolocstr => {}.extend(NWN::Gff::CexolocstrValue),
  :void => '',
  :struct => NWN::Gff::Struct.new,
  :list => [],
}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#parentObject

The parent struct. This is set internally by Gff::Reader on load.



38
39
40
# File 'lib/nwn/gff/field.rb', line 38

def parent
  @parent
end

Class Method Details

.extended(p1) ⇒ Object

Transform type to symbol on extend.



41
42
43
# File 'lib/nwn/gff/field.rb', line 41

def self.extended p1 #:nodoc:
  p1['type'] = p1['type'].to_sym if p1['type']
end

.new(label, type, value) ⇒ Object

Create a new NWN::Gff::Field



46
47
48
49
50
51
52
# File 'lib/nwn/gff/field.rb', line 46

def self.new label, type, value
  s = {}.extend(self)
  s['label'], s['type'], s['value'] = label, type.to_sym, value
  s.extend_meta_classes
  s.validate
  s
end

.unbox!(element, parent_label, parent) ⇒ Object

Deep-unboxes a Hash, e.g. iterating down, converting all strings from the native charset.



222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/nwn/gff/field.rb', line 222

def self.unbox! element, parent_label, parent
  element.extend(NWN::Gff::Field)
  element.field_label = parent_label
  element.parent = parent
  element.str_ref ||= NWN::Gff::Field::DEFAULT_STR_REF if element.respond_to?('str_ref=')

  element.extend_meta_classes
  case element.field_type
    when :cexolocstr
      mod = {}
      element.field_value.each {|x,y|
        mod[x] = NWN.iconv_native_to_gff(y)
      }
      mod.each {|x,y|
        element.field_value.delete(x)
        element.field_value[x.to_i] = y
      }
    when :cexostr
      element.field_value = NWN.iconv_native_to_gff(element.field_value)

    when :list
      mod = {}
      element.field_value.each_with_index {|x,idx|
        mod[idx] = NWN::Gff::Struct.unbox!(x, element)
      }
      mod.each {|x,y|
        element.field_value[x] = y
      }
    when :struct
      element.field_value = NWN::Gff::Struct.unbox!(element.field_value, element)
  end
  element.validate
  element
end

.valid_for?(value, type) ⇒ Boolean

Validate if value is within bounds of type.

Returns:

  • (Boolean)


132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/nwn/gff/field.rb', line 132

def self.valid_for? value, type
  case type
    when :byte, :char
      value.is_a?(Integer) && value >= 0 && value <= 255

    when :short
      value.is_a?(Integer) && value >= -0x8000 && value <= 0x7fff
    when :word
      value.is_a?(Integer) && value >= 0 && value <= 0xffff

    when :int
      value.is_a?(Integer) && value >= -0x80000000 && value <= 0x7fffffff
    when :dword
      value.is_a?(Integer) && value >= 0 && value <= 0xffffffff

    when :int64
      value.is_a?(Integer) && value >= -0x8000000000000000 && value <= 0x7fffffffffffffff
    when :dword64
      value.is_a?(Integer) && value >= 0 && value <= 0xffffffffffffffff

    when :float, :double
      value.is_a?(Float)

    when :resref
      if !NWN.setting(:resref32) && value.is_a?(String) && value.size > 16
        NWN.log_debug("Warning: :resref too long for NWN1, set env NWN_LIB_RESREF32=1 to turn off warning for NWN2.")
        NWN.log_debug("  Value found: #{value.inspect}")
      end

      if NWN.setting(:resref16)
        value.is_a?(String) && (0..16).member?(value.size)
      else
        value.is_a?(String) && (0..32).member?(value.size)
      end

    when :cexostr
      value.is_a?(String)

    when :cexolocstr
      value.is_a?(Hash) &&
        value.keys.reject {|x| x.is_a?(Fixnum) && x >= 0 }.size == 0 &&
        value.values.reject {|x| x.is_a?(String) }.size == 0

    when :struct
      value.is_a?(Hash)

    when :list
      value.is_a?(Array)

    when :void
      value.is_a?(String)

    else
      false
  end
end

Instance Method Details

#boxObject

Returns a hash of this Field without the API calls mixed in, all language-strings transformed by str_handler.



259
260
261
262
263
264
265
266
267
268
269
270
271
# File 'lib/nwn/gff/field.rb', line 259

def box
  t = Hash[self]
  t.delete('label')
  case field_type
    when :cexolocstr
      t['value'].each {|x,y|
        t['value'][x] = NWN.iconv_gff_to_native(y)
      }
    when :cexostr
      t['value'] = NWN.iconv_gff_to_native(t['value'])
  end
  t
end

#each_by_flat_path(&block) ⇒ Object

Used by NWN::Gff::Struct#by_flat_path



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/field.rb', line 191

def each_by_flat_path &block
  case field_type
    when :cexolocstr
      yield("", self)
      field_value.sort.each {|lid, str|
        yield("/" + lid.to_s, str)
      }

    when :struct
      yield("", self)
      field_value.each_by_flat_path {|v, x|
        yield(v, x)
      }

    when :list
      yield("", self)
      field_value.each_with_index {|item, index|
        yield("[" + index.to_s + "]", item)
        item.each_by_flat_path("/") {|v, x|
          yield("[" + index.to_s + "]" + v, x)
        }
      }

    else
      yield("", self)
  end
end

#extend_meta_classesObject

This extends this field object and its’ value with the appropriate meta classes, depending on field_type.



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/nwn/gff/field.rb', line 102

def extend_meta_classes
  return if field_type == :struct

  field_klass_name = field_type.to_s.capitalize

  field_klass = NWN::Gff.const_defined?(field_klass_name, false) ?
    NWN::Gff.const_get(field_klass_name, false) : nil
  field_value_klass = NWN::Gff.const_defined?(field_klass_name + 'Value', false) ?
    NWN::Gff.const_get(field_klass_name + 'Value', false) : nil

  self.extend(field_klass) unless field_klass.nil? ||
    self.is_a?(field_klass)

  field_value.extend(field_value_klass) unless field_value_klass.nil? ||
    field_value.is_a?(field_value_klass)
end

#field_labelObject Also known as: l



77
78
79
# File 'lib/nwn/gff/field.rb', line 77

def field_label
  self['label']
end

#field_label=(l) ⇒ Object Also known as: l=



82
83
84
# File 'lib/nwn/gff/field.rb', line 82

def field_label= l
  self['label']= l
end

#field_typeObject Also known as: t



54
55
56
# File 'lib/nwn/gff/field.rb', line 54

def field_type
  self['type']
end

#field_type=(t) ⇒ Object Also known as: t=



59
60
61
# File 'lib/nwn/gff/field.rb', line 59

def field_type= t
  self['type'] = t
end

#field_valueObject Also known as: v



64
65
66
# File 'lib/nwn/gff/field.rb', line 64

def field_value
  self['value']
end

#field_value=(v) ⇒ Object Also known as: v=



69
70
71
72
73
74
# File 'lib/nwn/gff/field.rb', line 69

def field_value= v
  NWN::Gff::Field.valid_for?(v, field_type) or raise ArgumentError,
    "Given field_value is not valid for type #{field_type.inspect}."

  self['value'] = v
end

#has_str_ref?Boolean

Returns:

  • (Boolean)


2
3
4
# File 'lib/nwn/gff/cexolocstr.rb', line 2

def has_str_ref?
  false
end

#pathObject

Returns the path to this field, including all parents structs. For example: UTI/PropertiesList/CostTable

Raises:



89
90
91
92
93
94
95
96
97
98
# File 'lib/nwn/gff/field.rb', line 89

def path
  raise NWN::Gff::GffError, "field not bound to a parent" unless @parent
  parent_path = @parent.path
  if @parent.element && @parent.element.field_type == :list
    idx = @parent.element.field_value.index(@parent)
    parent_path + "[#{idx}]/" + field_label
  else
    parent_path + "/" + field_label
  end.gsub(%r{/+}, "/")
end

#to_json(*a) ⇒ Object



10
11
12
# File 'lib/nwn/json_support.rb', line 10

def to_json(*a)
  box.to_json(*a)
end

#to_yaml(opts = {}) ⇒ Object



78
79
80
81
82
83
84
85
86
87
# File 'lib/nwn/yaml_support.rb', line 78

def to_yaml(opts = {})
  YAML::quick_emit(nil, opts) do |out|
    out.map(taguri, to_yaml_style) do |map|
      map.style = :inline unless NWN::Gff::Handler::YAML::NonInlineableFields.index(self['type'])
      map.add('type', self['type'])
      map.add('str_ref', self['str_ref']) if has_str_ref?
      map.add('value', self['value'])
    end
  end
end

#valid?Boolean

Validate if this field value is within the bounds of the set type.

Returns:

  • (Boolean)


120
121
122
# File 'lib/nwn/gff/field.rb', line 120

def valid?
  NWN::Gff::Field.valid_for? self.v, self.t
end

#validateObject

Validate this field, and raise an Excpetion if not valid.



125
126
127
128
129
# File 'lib/nwn/gff/field.rb', line 125

def validate
  valid? or raise NWN::Gff::GffError,
    "#{self.path rescue $!.to_s + '/' + self.l}: " +
      "value '#{self.v.inspect}' not valid for type '#{self.t.inspect}'"
end