nwn-lib 0.4.5 → 0.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/BINARIES +1 -1
 - data/CHANGELOG +39 -1
 - data/CHEATSHEET +20 -6
 - data/README +29 -12
 - data/Rakefile +3 -3
 - data/SETTINGS +3 -69
 - data/bin/nwn-dsl +2 -7
 - data/lib/nwn/all.rb +1 -0
 - data/lib/nwn/erf.rb +19 -192
 - data/lib/nwn/gff.rb +0 -1
 - data/lib/nwn/gff/field.rb +53 -14
 - data/lib/nwn/gff/reader.rb +2 -2
 - data/lib/nwn/gff/struct.rb +27 -9
 - data/lib/nwn/res.rb +275 -0
 - data/lib/nwn/settings.rb +0 -4
 - data/lib/nwn/tlk.rb +1 -1
 - data/lib/nwn/twoda.rb +36 -17
 - data/lib/nwn/yaml.rb +13 -198
 - data/spec/cexolocstr_spec.rb +15 -0
 - data/spec/erf_spec.rb +87 -0
 - data/spec/field_spec.rb +21 -0
 - data/spec/gff_spec.rb +22 -0
 - data/spec/res_spec.rb +59 -0
 - data/spec/spec_helper.rb +24 -0
 - data/spec/struct_spec.rb +61 -0
 - data/spec/tlk_spec.rb +47 -0
 - data/spec/twoda_spec.rb +146 -0
 - data/spec/wellformed_gff.binary +0 -0
 - metadata +13 -8
 - data/DATA_STRUCTURES +0 -50
 - data/TYPE_VALUE_INFERRING +0 -93
 - data/data/gff-common-nwn1.yaml +0 -982
 - data/lib/nwn/infer.rb +0 -125
 
    
        data/lib/nwn/gff.rb
    CHANGED
    
    
    
        data/lib/nwn/gff/field.rb
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # A Field wraps a GFF label->value pair, providing:
         
     | 
| 
       2 
2 
     | 
    
         
             
            # * +.field_type+ describing the field type (e.g. :int)
         
     | 
| 
       3 
3 
     | 
    
         
             
            # * +.field_value+ holding the value of this Field
         
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
      
 4 
     | 
    
         
            +
            #
         
     | 
| 
       5 
5 
     | 
    
         
             
            # and, if loaded by Gff::Reader or through YAML:
         
     | 
| 
       6 
6 
     | 
    
         
             
            # * +.field_label+ holding the label
         
     | 
| 
       7 
7 
     | 
    
         
             
            # * +.parent+ holding the struct this Field is child of.
         
     | 
| 
         @@ -21,6 +21,7 @@ module NWN::Gff::Field 
     | 
|
| 
       21 
21 
     | 
    
         
             
                s = {}.extend(self)
         
     | 
| 
       22 
22 
     | 
    
         
             
                s['label'], s['type'], s['value'] = label, type, value
         
     | 
| 
       23 
23 
     | 
    
         
             
                s.extend_meta_classes
         
     | 
| 
      
 24 
     | 
    
         
            +
                s.validate
         
     | 
| 
       24 
25 
     | 
    
         
             
                s
         
     | 
| 
       25 
26 
     | 
    
         
             
              end
         
     | 
| 
       26 
27 
     | 
    
         | 
| 
         @@ -62,7 +63,12 @@ module NWN::Gff::Field 
     | 
|
| 
       62 
63 
     | 
    
         
             
              def path
         
     | 
| 
       63 
64 
     | 
    
         
             
                raise NWN::Gff::GffError, "field not bound to a parent" unless @parent
         
     | 
| 
       64 
65 
     | 
    
         
             
                parent_path = @parent.path
         
     | 
| 
       65 
     | 
    
         
            -
                 
     | 
| 
      
 66 
     | 
    
         
            +
                if @parent.element && @parent.element.field_type == :list
         
     | 
| 
      
 67 
     | 
    
         
            +
                  idx = @parent.element.field_value.index(@parent)
         
     | 
| 
      
 68 
     | 
    
         
            +
                  parent_path + "[#{idx}]/" + field_label
         
     | 
| 
      
 69 
     | 
    
         
            +
                else
         
     | 
| 
      
 70 
     | 
    
         
            +
                  parent_path + "/" + field_label
         
     | 
| 
      
 71 
     | 
    
         
            +
                end
         
     | 
| 
       66 
72 
     | 
    
         
             
              end
         
     | 
| 
       67 
73 
     | 
    
         | 
| 
       68 
74 
     | 
    
         
             
              # This extends this field object and its' value with the
         
     | 
| 
         @@ -83,29 +89,62 @@ module NWN::Gff::Field 
     | 
|
| 
       83 
89 
     | 
    
         
             
                  field_value.is_a?(field_value_klass)
         
     | 
| 
       84 
90 
     | 
    
         
             
              end
         
     | 
| 
       85 
91 
     | 
    
         | 
| 
      
 92 
     | 
    
         
            +
              # Validate if this field value is within the bounds of the set type.
         
     | 
| 
      
 93 
     | 
    
         
            +
              def valid?
         
     | 
| 
      
 94 
     | 
    
         
            +
                NWN::Gff::Field.valid_for? self.v, self.t
         
     | 
| 
      
 95 
     | 
    
         
            +
              end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
              # Validate this field, and raise an Excpetion if not valid.
         
     | 
| 
      
 98 
     | 
    
         
            +
              def validate
         
     | 
| 
      
 99 
     | 
    
         
            +
                valid? or raise NWN::Gff::GffError,
         
     | 
| 
      
 100 
     | 
    
         
            +
                  "#{self.path rescue $!.to_s + '/' + self.l}: " +
         
     | 
| 
      
 101 
     | 
    
         
            +
                    "value '#{self.v.inspect}' not valid for type '#{self.t.inspect}'"
         
     | 
| 
      
 102 
     | 
    
         
            +
              end
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
       86 
104 
     | 
    
         
             
              # Validate if +value+ is within bounds of +type+.
         
     | 
| 
       87 
105 
     | 
    
         
             
              def self.valid_for? value, type
         
     | 
| 
       88 
106 
     | 
    
         
             
                case type
         
     | 
| 
       89 
     | 
    
         
            -
                  when : 
     | 
| 
       90 
     | 
    
         
            -
                    value.is_a?( 
     | 
| 
       91 
     | 
    
         
            -
             
     | 
| 
       92 
     | 
    
         
            -
             
     | 
| 
       93 
     | 
    
         
            -
             
     | 
| 
       94 
     | 
    
         
            -
             
     | 
| 
       95 
     | 
    
         
            -
             
     | 
| 
       96 
     | 
    
         
            -
             
     | 
| 
      
 107 
     | 
    
         
            +
                  when :byte, :char
         
     | 
| 
      
 108 
     | 
    
         
            +
                    value.is_a?(Integer) && value >= 0 && value <= 255
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                  when :short
         
     | 
| 
      
 111 
     | 
    
         
            +
                    value.is_a?(Integer) && value >= -0x8000 && value <= 0x7fff
         
     | 
| 
      
 112 
     | 
    
         
            +
                  when :word
         
     | 
| 
      
 113 
     | 
    
         
            +
                    value.is_a?(Integer) && value >= 0 && value <= 0xffff
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                  when :int
         
     | 
| 
      
 116 
     | 
    
         
            +
                    value.is_a?(Integer) && value >= -0x80000000 && value <= 0x7fffffff
         
     | 
| 
      
 117 
     | 
    
         
            +
                  when :dword
         
     | 
| 
      
 118 
     | 
    
         
            +
                    value.is_a?(Integer) && value >= 0 && value <= 0xffffffff
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                  when :int64
         
     | 
| 
      
 121 
     | 
    
         
            +
                    value.is_a?(Integer) && value >= -0x800000000000 && value <= 0x7fffffffffff
         
     | 
| 
      
 122 
     | 
    
         
            +
                  when :dword64
         
     | 
| 
      
 123 
     | 
    
         
            +
                    value.is_a?(Integer) && value >= 0 && value <= 0xffffffffffff
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
       97 
125 
     | 
    
         
             
                  when :float, :double
         
     | 
| 
       98 
126 
     | 
    
         
             
                    value.is_a?(Float)
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
       99 
128 
     | 
    
         
             
                  when :resref
         
     | 
| 
       100 
     | 
    
         
            -
                    value.is_a?(String) && ( 
     | 
| 
      
 129 
     | 
    
         
            +
                    value.is_a?(String) && (0..16).member?(value.size)
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
       101 
131 
     | 
    
         
             
                  when :cexostr
         
     | 
| 
       102 
132 
     | 
    
         
             
                    value.is_a?(String)
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
       103 
134 
     | 
    
         
             
                  when :cexolocstr
         
     | 
| 
      
 135 
     | 
    
         
            +
                    value.is_a?(Hash) &&
         
     | 
| 
      
 136 
     | 
    
         
            +
                      value.keys.reject {|x| x.is_a?(Fixnum) && x >= 0 }.size == 0 &&
         
     | 
| 
      
 137 
     | 
    
         
            +
                      value.values.reject {|x| x.is_a?(String) }.size == 0
         
     | 
| 
      
 138 
     | 
    
         
            +
             
     | 
| 
      
 139 
     | 
    
         
            +
                  when :struct
         
     | 
| 
      
 140 
     | 
    
         
            +
                    value.is_a?(Hash)
         
     | 
| 
      
 141 
     | 
    
         
            +
             
     | 
| 
      
 142 
     | 
    
         
            +
                  when :list
         
     | 
| 
       104 
143 
     | 
    
         
             
                    value.is_a?(Array)
         
     | 
| 
       105 
     | 
    
         
            -
             
     | 
| 
       106 
     | 
    
         
            -
                    value.is_a?(Array)
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
       107 
145 
     | 
    
         
             
                  when :void
         
     | 
| 
       108 
     | 
    
         
            -
                     
     | 
| 
      
 146 
     | 
    
         
            +
                    value.is_a?(String)
         
     | 
| 
      
 147 
     | 
    
         
            +
             
     | 
| 
       109 
148 
     | 
    
         
             
                  else
         
     | 
| 
       110 
149 
     | 
    
         
             
                    false
         
     | 
| 
       111 
150 
     | 
    
         
             
                end
         
     | 
    
        data/lib/nwn/gff/reader.rb
    CHANGED
    
    | 
         @@ -166,8 +166,7 @@ class NWN::Gff::Reader 
     | 
|
| 
       166 
166 
     | 
    
         
             
                    }
         
     | 
| 
       167 
167 
     | 
    
         
             
                    len = total_size + 4
         
     | 
| 
       168 
168 
     | 
    
         
             
                    # Filter out empty strings.
         
     | 
| 
       169 
     | 
    
         
            -
                    exostr.reject! {|k,v| v.nil? || v.empty?} 
     | 
| 
       170 
     | 
    
         
            -
                      ENV['NWN_LIB_FILTER_EMPTY_EXOLOCSTR']
         
     | 
| 
      
 169 
     | 
    
         
            +
                    exostr.reject! {|k,v| v.nil? || v.empty?}
         
     | 
| 
       171 
170 
     | 
    
         
             
                    exostr.taint
         
     | 
| 
       172 
171 
     | 
    
         | 
| 
       173 
172 
     | 
    
         
             
                  when :void
         
     | 
| 
         @@ -214,6 +213,7 @@ class NWN::Gff::Reader 
     | 
|
| 
       214 
213 
     | 
    
         | 
| 
       215 
214 
     | 
    
         
             
                # We extend all fields and field_values with matching classes.
         
     | 
| 
       216 
215 
     | 
    
         
             
                field.extend_meta_classes
         
     | 
| 
      
 216 
     | 
    
         
            +
                field.validate
         
     | 
| 
       217 
217 
     | 
    
         | 
| 
       218 
218 
     | 
    
         
             
                [label, field]
         
     | 
| 
       219 
219 
     | 
    
         
             
              end
         
     | 
    
        data/lib/nwn/gff/struct.rb
    CHANGED
    
    | 
         @@ -3,16 +3,21 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
            module NWN::Gff::Struct
         
     | 
| 
       4 
4 
     | 
    
         
             
              DEFAULT_DATA_VERSION = "V3.2"
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
       6 
     | 
    
         
            -
              #  
     | 
| 
       7 
     | 
    
         
            -
              #  
     | 
| 
       8 
     | 
    
         
            -
              #  
     | 
| 
      
 6 
     | 
    
         
            +
              # Each Gff::Struct has a data_type, which describes the type of data the struct contains.
         
     | 
| 
      
 7 
     | 
    
         
            +
              # For top-level structs, this equals the data type written to the GFF file ("UTI",
         
     | 
| 
      
 8 
     | 
    
         
            +
              # for example); for sub structures, this is usually the top-level data type + the
         
     | 
| 
      
 9 
     | 
    
         
            +
              # field label ("UTI/PropertiesList", for example).
         
     | 
| 
      
 10 
     | 
    
         
            +
              #
         
     | 
| 
      
 11 
     | 
    
         
            +
              # This is set for completeness' sake, but is not required to save the struct.
         
     | 
| 
      
 12 
     | 
    
         
            +
              # Scripts could use this, for example, to reliably re-attach a Item within
         
     | 
| 
      
 13 
     | 
    
         
            +
              # /ItemList/ somewhere else, or export it as .uti.
         
     | 
| 
       9 
14 
     | 
    
         
             
              attr_accessor :data_type
         
     | 
| 
       10 
15 
     | 
    
         | 
| 
       11 
16 
     | 
    
         
             
              # The file version. Usually "V3.2" for root structs,
         
     | 
| 
       12 
17 
     | 
    
         
             
              # and nil for sub-structs.
         
     | 
| 
       13 
18 
     | 
    
         
             
              attr_accessor :data_version
         
     | 
| 
       14 
19 
     | 
    
         | 
| 
       15 
     | 
    
         
            -
              # GFF struct type
         
     | 
| 
      
 20 
     | 
    
         
            +
              # GFF struct type. The default is 0xffffffff.
         
     | 
| 
       16 
21 
     | 
    
         
             
              attr_accessor :struct_id
         
     | 
| 
       17 
22 
     | 
    
         | 
| 
       18 
23 
     | 
    
         
             
              # The field this struct is value of.
         
     | 
| 
         @@ -43,11 +48,15 @@ module NWN::Gff::Struct 
     | 
|
| 
       43 
48 
     | 
    
         
             
              # Create a new struct.
         
     | 
| 
       44 
49 
     | 
    
         
             
              # Usually, you can leave out data_type and data_version for non-root structs,
         
     | 
| 
       45 
50 
     | 
    
         
             
              # because that will be guess-inherited based on the existing associations.
         
     | 
| 
      
 51 
     | 
    
         
            +
              #
         
     | 
| 
      
 52 
     | 
    
         
            +
              # You can pass a block to this method, which will receive the newly-created
         
     | 
| 
      
 53 
     | 
    
         
            +
              # Struct as the only argument.
         
     | 
| 
       46 
54 
     | 
    
         
             
              def self.new struct_id = 0xffffffff, data_type = nil, data_version = nil
         
     | 
| 
       47 
55 
     | 
    
         
             
                s = {}.extend(self)
         
     | 
| 
       48 
56 
     | 
    
         
             
                s.struct_id = struct_id
         
     | 
| 
       49 
57 
     | 
    
         
             
                s.data_type = data_type
         
     | 
| 
       50 
58 
     | 
    
         
             
                s.data_version = data_version
         
     | 
| 
      
 59 
     | 
    
         
            +
                yield(s) if block_given?
         
     | 
| 
       51 
60 
     | 
    
         
             
                s
         
     | 
| 
       52 
61 
     | 
    
         
             
              end
         
     | 
| 
       53 
62 
     | 
    
         | 
| 
         @@ -58,17 +67,26 @@ module NWN::Gff::Struct 
     | 
|
| 
       58 
67 
     | 
    
         
             
              #  some_struct.add_field 'ID', :byte, 5
         
     | 
| 
       59 
68 
     | 
    
         
             
              # is equivalent to:
         
     | 
| 
       60 
69 
     | 
    
         
             
              #  some_struct.add_byte 'ID', 5
         
     | 
| 
      
 70 
     | 
    
         
            +
              #
         
     | 
| 
      
 71 
     | 
    
         
            +
              # You can pass a block to this method, which will receive the newly-created
         
     | 
| 
      
 72 
     | 
    
         
            +
              # Field as an argument.
         
     | 
| 
      
 73 
     | 
    
         
            +
              #
         
     | 
| 
      
 74 
     | 
    
         
            +
              # This allows for code like this:
         
     | 
| 
      
 75 
     | 
    
         
            +
              #  Gff::Struct.new(0) do |s|
         
     | 
| 
      
 76 
     | 
    
         
            +
              #    s.add_byte "Byte", 5
         
     | 
| 
      
 77 
     | 
    
         
            +
              #    s.add_list "Some_List", [] do |l|
         
     | 
| 
      
 78 
     | 
    
         
            +
              #      l.v << Gff::Struct.new ...
         
     | 
| 
      
 79 
     | 
    
         
            +
              #      ..
         
     | 
| 
      
 80 
     | 
    
         
            +
              #    end
         
     | 
| 
      
 81 
     | 
    
         
            +
              #  end
         
     | 
| 
       61 
82 
     | 
    
         
             
              def add_field label, type, value, &block
         
     | 
| 
       62 
83 
     | 
    
         
             
                self[label] = NWN::Gff::Field.new(label, type, value)
         
     | 
| 
       63 
84 
     | 
    
         
             
                self[label].parent = self
         
     | 
| 
       64 
     | 
    
         
            -
                if block_given?
         
     | 
| 
       65 
     | 
    
         
            -
                  yield(self[label])
         
     | 
| 
       66 
     | 
    
         
            -
                end
         
     | 
| 
      
 85 
     | 
    
         
            +
                yield(self[label]) if block_given?
         
     | 
| 
       67 
86 
     | 
    
         
             
                self[label]
         
     | 
| 
       68 
87 
     | 
    
         
             
              end
         
     | 
| 
       69 
88 
     | 
    
         | 
| 
       70 
     | 
    
         
            -
               
     | 
| 
       71 
     | 
    
         
            -
              def method_missing meth, *av, &block
         
     | 
| 
      
 89 
     | 
    
         
            +
              def method_missing meth, *av, &block # :nodoc:
         
     | 
| 
       72 
90 
     | 
    
         
             
                if meth.to_s =~ /^add_(.+)$/
         
     | 
| 
       73 
91 
     | 
    
         
             
                  if NWN::Gff::Types.index($1.to_sym)
         
     | 
| 
       74 
92 
     | 
    
         
             
                    av.size == 2 or super
         
     | 
    
        data/lib/nwn/res.rb
    ADDED
    
    | 
         @@ -0,0 +1,275 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module NWN
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Resources
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
                # This is a generic index to a resource.
         
     | 
| 
      
 5 
     | 
    
         
            +
                class ContentObject
         
     | 
| 
      
 6 
     | 
    
         
            +
                  attr_accessor :resref
         
     | 
| 
      
 7 
     | 
    
         
            +
                  attr_accessor :res_type
         
     | 
| 
      
 8 
     | 
    
         
            +
                  attr_accessor :io
         
     | 
| 
      
 9 
     | 
    
         
            +
                  attr_accessor :offset
         
     | 
| 
      
 10 
     | 
    
         
            +
                  attr_accessor :size_override
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  # Create a new index to +filename+, optionally specifying +io+.
         
     | 
| 
      
 13 
     | 
    
         
            +
                  def self.new_from filename, io = nil
         
     | 
| 
      
 14 
     | 
    
         
            +
                    filename = File.expand_path(filename)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    base = File.basename(filename).split(".")[0..-2].join(".").downcase
         
     | 
| 
      
 16 
     | 
    
         
            +
                    ext = File.extname(filename)[1..-1].downcase
         
     | 
| 
      
 17 
     | 
    
         
            +
                    res_type = NWN::Resources::Extensions[ext] or raise ArgumentError,
         
     | 
| 
      
 18 
     | 
    
         
            +
                      "Not a valid extension: #{ext.inspect} (while packing #{filename})"
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                    ContentObject.new(base, res_type, io || filename, 0, io.size || File.stat(filename).size)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  def initialize resref, res_type, io = nil, offset = nil, size = nil
         
     | 
| 
      
 24 
     | 
    
         
            +
                    @resref, @res_type = resref.downcase, res_type
         
     | 
| 
      
 25 
     | 
    
         
            +
                    @io, @offset = io, offset
         
     | 
| 
      
 26 
     | 
    
         
            +
                    @size_override = size
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                    raise ArgumentError, "Invalid object passed: responds_to :read, want @offset, but does not respond_to :seek" if
         
     | 
| 
      
 29 
     | 
    
         
            +
                      @io.respond_to?(:read) && @offset && @offset != 0 && !@io.respond_to?(:seek)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  # Get the size in bytes of this object.
         
     | 
| 
      
 33 
     | 
    
         
            +
                  def size
         
     | 
| 
      
 34 
     | 
    
         
            +
                    @size_override || (@io.is_a?(IO) ? @io.stat.size : File.stat(@io).size)
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                  # Get the contents of this object. This is a costly operation, loading
         
     | 
| 
      
 38 
     | 
    
         
            +
                  # the whole buffer. If you want fine-grained access, use ContentObject#io
         
     | 
| 
      
 39 
     | 
    
         
            +
                  # and do it yourself, observing ContentObject#offset and ContentObject#size.
         
     | 
| 
      
 40 
     | 
    
         
            +
                  def get
         
     | 
| 
      
 41 
     | 
    
         
            +
                    if @io.respond_to?(:read)
         
     | 
| 
      
 42 
     | 
    
         
            +
                      @io.seek(@offset) if @offset
         
     | 
| 
      
 43 
     | 
    
         
            +
                      d = @io.read(self.size)
         
     | 
| 
      
 44 
     | 
    
         
            +
                      raise IOError,
         
     | 
| 
      
 45 
     | 
    
         
            +
                        "not enough data available while reading #{self.filename}" if
         
     | 
| 
      
 46 
     | 
    
         
            +
                          d.size != self.size
         
     | 
| 
      
 47 
     | 
    
         
            +
                      d
         
     | 
| 
      
 48 
     | 
    
         
            +
                    else
         
     | 
| 
      
 49 
     | 
    
         
            +
                      IO.read(@io)
         
     | 
| 
      
 50 
     | 
    
         
            +
                    end
         
     | 
| 
      
 51 
     | 
    
         
            +
                  end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                  # Get the canonical filename of this object.
         
     | 
| 
      
 54 
     | 
    
         
            +
                  def filename
         
     | 
| 
      
 55 
     | 
    
         
            +
                    @resref + "." + self.extension
         
     | 
| 
      
 56 
     | 
    
         
            +
                  end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                  # Get the extension of this object.
         
     | 
| 
      
 59 
     | 
    
         
            +
                  def extension
         
     | 
| 
      
 60 
     | 
    
         
            +
                    NWN::Resources::Extensions.index(@res_type)
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
                end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                # Wraps n ContentObjects; a baseclass for erf/key encapsulation.
         
     | 
| 
      
 65 
     | 
    
         
            +
                class Container
         
     | 
| 
      
 66 
     | 
    
         
            +
                  attr_reader :content
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                  def initialize
         
     | 
| 
      
 69 
     | 
    
         
            +
                    @content = []
         
     | 
| 
      
 70 
     | 
    
         
            +
                  end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                  def has?(filename)
         
     | 
| 
      
 73 
     | 
    
         
            +
                    base = File.basename(filename)
         
     | 
| 
      
 74 
     | 
    
         
            +
                    @content.each {|f|
         
     | 
| 
      
 75 
     | 
    
         
            +
                      return true if f.filename.downcase == base.downcase
         
     | 
| 
      
 76 
     | 
    
         
            +
                    }
         
     | 
| 
      
 77 
     | 
    
         
            +
                    return false
         
     | 
| 
      
 78 
     | 
    
         
            +
                  end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                  # Add a content object giving a +filename+ and a optional
         
     | 
| 
      
 81 
     | 
    
         
            +
                  # +io+.
         
     | 
| 
      
 82 
     | 
    
         
            +
                  def add_file filename, io = nil
         
     | 
| 
      
 83 
     | 
    
         
            +
                    @content << ContentObject.new_from(filename, io)
         
     | 
| 
      
 84 
     | 
    
         
            +
                  end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                  # Add a content object giving the ContentObject
         
     | 
| 
      
 87 
     | 
    
         
            +
                  def add o
         
     | 
| 
      
 88 
     | 
    
         
            +
                    @content << o
         
     | 
| 
      
 89 
     | 
    
         
            +
                  end
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                  # Returns a list of filenames
         
     | 
| 
      
 92 
     | 
    
         
            +
                  def filenames
         
     | 
| 
      
 93 
     | 
    
         
            +
                    @content.map {|x| x.filename }
         
     | 
| 
      
 94 
     | 
    
         
            +
                  end
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                  # Get the ContentObject pointing to the given filename.
         
     | 
| 
      
 97 
     | 
    
         
            +
                  # Raises ENOENT if not mapped.
         
     | 
| 
      
 98 
     | 
    
         
            +
                  def get_content_object filename
         
     | 
| 
      
 99 
     | 
    
         
            +
                    ret = @content.select {|x| filename.downcase == x.filename }
         
     | 
| 
      
 100 
     | 
    
         
            +
                    raise Errno::ENOENT,
         
     | 
| 
      
 101 
     | 
    
         
            +
                      "No ContentObject with the given filename found." if
         
     | 
| 
      
 102 
     | 
    
         
            +
                        ret.size == 0
         
     | 
| 
      
 103 
     | 
    
         
            +
                    ret[0]
         
     | 
| 
      
 104 
     | 
    
         
            +
                  end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                  # Get the contents of the given filename.
         
     | 
| 
      
 107 
     | 
    
         
            +
                  # Raises ENOENT if not mapped.
         
     | 
| 
      
 108 
     | 
    
         
            +
                  def get filename
         
     | 
| 
      
 109 
     | 
    
         
            +
                    get_content_object(filename.downcase).get
         
     | 
| 
      
 110 
     | 
    
         
            +
                  end
         
     | 
| 
      
 111 
     | 
    
         
            +
                end
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                # A Container that directly wraps a directory (e.g. override/).
         
     | 
| 
      
 114 
     | 
    
         
            +
                # Does not update on changes - caches the directory entries on initialize.
         
     | 
| 
      
 115 
     | 
    
         
            +
                class DirectoryContainer < Container
         
     | 
| 
      
 116 
     | 
    
         
            +
                  def initialize path
         
     | 
| 
      
 117 
     | 
    
         
            +
                    super()
         
     | 
| 
      
 118 
     | 
    
         
            +
                    @path = path
         
     | 
| 
      
 119 
     | 
    
         
            +
                    Dir[path + "/*.*"].each {|x|
         
     | 
| 
      
 120 
     | 
    
         
            +
                      add_file x
         
     | 
| 
      
 121 
     | 
    
         
            +
                    }
         
     | 
| 
      
 122 
     | 
    
         
            +
                  end
         
     | 
| 
      
 123 
     | 
    
         
            +
                end
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                # The resource manager, providing ordered access to Container objects.
         
     | 
| 
      
 126 
     | 
    
         
            +
                class Manager
         
     | 
| 
      
 127 
     | 
    
         
            +
                  def initialize
         
     | 
| 
      
 128 
     | 
    
         
            +
                    @path = []
         
     | 
| 
      
 129 
     | 
    
         
            +
                    @_content_cache = nil
         
     | 
| 
      
 130 
     | 
    
         
            +
                  end
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
                  def add_container c
         
     | 
| 
      
 133 
     | 
    
         
            +
                    @path << c
         
     | 
| 
      
 134 
     | 
    
         
            +
                  end
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
                  # Get the ContentObject pointing to the given filename.
         
     | 
| 
      
 137 
     | 
    
         
            +
                  # Raises ENOENT if not mapped.
         
     | 
| 
      
 138 
     | 
    
         
            +
                  def get_content_object filename
         
     | 
| 
      
 139 
     | 
    
         
            +
                    @path.reverse.each {|con|
         
     | 
| 
      
 140 
     | 
    
         
            +
                      con.has?(filename) or next
         
     | 
| 
      
 141 
     | 
    
         
            +
                      return con.get_content_object(filename)
         
     | 
| 
      
 142 
     | 
    
         
            +
                    }
         
     | 
| 
      
 143 
     | 
    
         
            +
                    raise Errno::ENOENT, "No ContentObject with the given filename found."
         
     | 
| 
      
 144 
     | 
    
         
            +
                  end
         
     | 
| 
      
 145 
     | 
    
         
            +
             
     | 
| 
      
 146 
     | 
    
         
            +
                  # Get the contents of the given filename.
         
     | 
| 
      
 147 
     | 
    
         
            +
                  # Raises ENOENT if not mapped.
         
     | 
| 
      
 148 
     | 
    
         
            +
                  def get filename
         
     | 
| 
      
 149 
     | 
    
         
            +
                    get_content_object(filename).get
         
     | 
| 
      
 150 
     | 
    
         
            +
                  end
         
     | 
| 
      
 151 
     | 
    
         
            +
             
     | 
| 
      
 152 
     | 
    
         
            +
                  # Get a list of filenames contained inside.
         
     | 
| 
      
 153 
     | 
    
         
            +
                  def content
         
     | 
| 
      
 154 
     | 
    
         
            +
                    @_content_cache ||= @path.inject([]) {|a, x|
         
     | 
| 
      
 155 
     | 
    
         
            +
                      a |= x.filenames
         
     | 
| 
      
 156 
     | 
    
         
            +
                    }
         
     | 
| 
      
 157 
     | 
    
         
            +
                  end
         
     | 
| 
      
 158 
     | 
    
         
            +
                end
         
     | 
| 
      
 159 
     | 
    
         
            +
             
     | 
| 
      
 160 
     | 
    
         
            +
                Extensions = {
         
     | 
| 
      
 161 
     | 
    
         
            +
                  'res' => 0,
         
     | 
| 
      
 162 
     | 
    
         
            +
                  'bmp' => 1,
         
     | 
| 
      
 163 
     | 
    
         
            +
                  'mve' => 2,
         
     | 
| 
      
 164 
     | 
    
         
            +
                  'tga' => 3,
         
     | 
| 
      
 165 
     | 
    
         
            +
                  'wav' => 4,
         
     | 
| 
      
 166 
     | 
    
         
            +
                  'wfx' => 5,
         
     | 
| 
      
 167 
     | 
    
         
            +
                  'plt' => 6,
         
     | 
| 
      
 168 
     | 
    
         
            +
                  'ini' => 7,
         
     | 
| 
      
 169 
     | 
    
         
            +
                  'mp3' => 8,
         
     | 
| 
      
 170 
     | 
    
         
            +
                  'mpg' => 9,
         
     | 
| 
      
 171 
     | 
    
         
            +
                  'txt' => 10,
         
     | 
| 
      
 172 
     | 
    
         
            +
                  'plh' => 2000,
         
     | 
| 
      
 173 
     | 
    
         
            +
                  'tex' => 2001,
         
     | 
| 
      
 174 
     | 
    
         
            +
                  'mdl' => 2002,
         
     | 
| 
      
 175 
     | 
    
         
            +
                  'thg' => 2003,
         
     | 
| 
      
 176 
     | 
    
         
            +
                  'fnt' => 2005,
         
     | 
| 
      
 177 
     | 
    
         
            +
                  'lua' => 2007,
         
     | 
| 
      
 178 
     | 
    
         
            +
                  'slt' => 2008,
         
     | 
| 
      
 179 
     | 
    
         
            +
                  'nss' => 2009,
         
     | 
| 
      
 180 
     | 
    
         
            +
                  'ncs' => 2010,
         
     | 
| 
      
 181 
     | 
    
         
            +
                  'mod' => 2011,
         
     | 
| 
      
 182 
     | 
    
         
            +
                  'are' => 2012,
         
     | 
| 
      
 183 
     | 
    
         
            +
                  'set' => 2013,
         
     | 
| 
      
 184 
     | 
    
         
            +
                  'ifo' => 2014,
         
     | 
| 
      
 185 
     | 
    
         
            +
                  'bic' => 2015,
         
     | 
| 
      
 186 
     | 
    
         
            +
                  'wok' => 2016,
         
     | 
| 
      
 187 
     | 
    
         
            +
                  '2da' => 2017,
         
     | 
| 
      
 188 
     | 
    
         
            +
                  'tlk' => 2018,
         
     | 
| 
      
 189 
     | 
    
         
            +
                  'txi' => 2022,
         
     | 
| 
      
 190 
     | 
    
         
            +
                  'git' => 2023,
         
     | 
| 
      
 191 
     | 
    
         
            +
                  'bti' => 2024,
         
     | 
| 
      
 192 
     | 
    
         
            +
                  'uti' => 2025,
         
     | 
| 
      
 193 
     | 
    
         
            +
                  'btc' => 2026,
         
     | 
| 
      
 194 
     | 
    
         
            +
                  'utc' => 2027,
         
     | 
| 
      
 195 
     | 
    
         
            +
                  'dlg' => 2029,
         
     | 
| 
      
 196 
     | 
    
         
            +
                  'itp' => 2030,
         
     | 
| 
      
 197 
     | 
    
         
            +
                  'btt' => 2031,
         
     | 
| 
      
 198 
     | 
    
         
            +
                  'utt' => 2032,
         
     | 
| 
      
 199 
     | 
    
         
            +
                  'dds' => 2033,
         
     | 
| 
      
 200 
     | 
    
         
            +
                  'bts' => 2034,
         
     | 
| 
      
 201 
     | 
    
         
            +
                  'uts' => 2035,
         
     | 
| 
      
 202 
     | 
    
         
            +
                  'ltr' => 2036,
         
     | 
| 
      
 203 
     | 
    
         
            +
                  'gff' => 2037,
         
     | 
| 
      
 204 
     | 
    
         
            +
                  'fac' => 2038,
         
     | 
| 
      
 205 
     | 
    
         
            +
                  'bte' => 2039,
         
     | 
| 
      
 206 
     | 
    
         
            +
                  'ute' => 2040,
         
     | 
| 
      
 207 
     | 
    
         
            +
                  'btd' => 2041,
         
     | 
| 
      
 208 
     | 
    
         
            +
                  'utd' => 2042,
         
     | 
| 
      
 209 
     | 
    
         
            +
                  'btp' => 2043,
         
     | 
| 
      
 210 
     | 
    
         
            +
                  'utp' => 2044,
         
     | 
| 
      
 211 
     | 
    
         
            +
                  'dft' => 2045,
         
     | 
| 
      
 212 
     | 
    
         
            +
                  'gic' => 2046,
         
     | 
| 
      
 213 
     | 
    
         
            +
                  'gui' => 2047,
         
     | 
| 
      
 214 
     | 
    
         
            +
                  'css' => 2048,
         
     | 
| 
      
 215 
     | 
    
         
            +
                  'ccs' => 2049,
         
     | 
| 
      
 216 
     | 
    
         
            +
                  'btm' => 2050,
         
     | 
| 
      
 217 
     | 
    
         
            +
                  'utm' => 2051,
         
     | 
| 
      
 218 
     | 
    
         
            +
                  'dwk' => 2052,
         
     | 
| 
      
 219 
     | 
    
         
            +
                  'pwk' => 2053,
         
     | 
| 
      
 220 
     | 
    
         
            +
                  'btg' => 2054,
         
     | 
| 
      
 221 
     | 
    
         
            +
                  'utg' => 2055,
         
     | 
| 
      
 222 
     | 
    
         
            +
                  'jrl' => 2056,
         
     | 
| 
      
 223 
     | 
    
         
            +
                  'sav' => 2057,
         
     | 
| 
      
 224 
     | 
    
         
            +
                  'utw' => 2058,
         
     | 
| 
      
 225 
     | 
    
         
            +
                  '4pc' => 2059,
         
     | 
| 
      
 226 
     | 
    
         
            +
                  'ssf' => 2060,
         
     | 
| 
      
 227 
     | 
    
         
            +
                  'hak' => 2061,
         
     | 
| 
      
 228 
     | 
    
         
            +
                  'nwm' => 2062,
         
     | 
| 
      
 229 
     | 
    
         
            +
                  'bik' => 2063,
         
     | 
| 
      
 230 
     | 
    
         
            +
                  'ndb' => 2064,
         
     | 
| 
      
 231 
     | 
    
         
            +
                  'ptm' => 2065,
         
     | 
| 
      
 232 
     | 
    
         
            +
                  'ptt' => 2066,
         
     | 
| 
      
 233 
     | 
    
         
            +
                  'bak' => 2067,
         
     | 
| 
      
 234 
     | 
    
         
            +
                  'osc' => 3000,
         
     | 
| 
      
 235 
     | 
    
         
            +
                  'usc' => 3001,
         
     | 
| 
      
 236 
     | 
    
         
            +
                  'trn' => 3002,
         
     | 
| 
      
 237 
     | 
    
         
            +
                  'utr' => 3003,
         
     | 
| 
      
 238 
     | 
    
         
            +
                  'uen' => 3004,
         
     | 
| 
      
 239 
     | 
    
         
            +
                  'ult' => 3005,
         
     | 
| 
      
 240 
     | 
    
         
            +
                  'sef' => 3006,
         
     | 
| 
      
 241 
     | 
    
         
            +
                  'pfx' => 3007,
         
     | 
| 
      
 242 
     | 
    
         
            +
                  'cam' => 3008,
         
     | 
| 
      
 243 
     | 
    
         
            +
                  'lfx' => 3009,
         
     | 
| 
      
 244 
     | 
    
         
            +
                  'bfx' => 3010,
         
     | 
| 
      
 245 
     | 
    
         
            +
                  'upe' => 3011,
         
     | 
| 
      
 246 
     | 
    
         
            +
                  'ros' => 3012,
         
     | 
| 
      
 247 
     | 
    
         
            +
                  'rst' => 3013,
         
     | 
| 
      
 248 
     | 
    
         
            +
                  'ifx' => 3014,
         
     | 
| 
      
 249 
     | 
    
         
            +
                  'pfb' => 3015,
         
     | 
| 
      
 250 
     | 
    
         
            +
                  'zip' => 3016,
         
     | 
| 
      
 251 
     | 
    
         
            +
                  'wmp' => 3017,
         
     | 
| 
      
 252 
     | 
    
         
            +
                  'bbx' => 3018,
         
     | 
| 
      
 253 
     | 
    
         
            +
                  'tfx' => 3019,
         
     | 
| 
      
 254 
     | 
    
         
            +
                  'wlk' => 3020,
         
     | 
| 
      
 255 
     | 
    
         
            +
                  'xml' => 3021,
         
     | 
| 
      
 256 
     | 
    
         
            +
                  'scc' => 3022,
         
     | 
| 
      
 257 
     | 
    
         
            +
                  'ptx' => 3033,
         
     | 
| 
      
 258 
     | 
    
         
            +
                  'ltx' => 3034,
         
     | 
| 
      
 259 
     | 
    
         
            +
                  'trx' => 3035,
         
     | 
| 
      
 260 
     | 
    
         
            +
                  'mdb' => 4000,
         
     | 
| 
      
 261 
     | 
    
         
            +
                  'mda' => 4001,
         
     | 
| 
      
 262 
     | 
    
         
            +
                  'spt' => 4002,
         
     | 
| 
      
 263 
     | 
    
         
            +
                  'gr2' => 4003,
         
     | 
| 
      
 264 
     | 
    
         
            +
                  'fxa' => 4004,
         
     | 
| 
      
 265 
     | 
    
         
            +
                  'fxe' => 4005,
         
     | 
| 
      
 266 
     | 
    
         
            +
                  'jpg' => 4007,
         
     | 
| 
      
 267 
     | 
    
         
            +
                  'pwc' => 4008,
         
     | 
| 
      
 268 
     | 
    
         
            +
                  'ids' => 9996,
         
     | 
| 
      
 269 
     | 
    
         
            +
                  'erf' => 9997,
         
     | 
| 
      
 270 
     | 
    
         
            +
                  'bif' => 9998,
         
     | 
| 
      
 271 
     | 
    
         
            +
                  'key' => 9999,
         
     | 
| 
      
 272 
     | 
    
         
            +
                }.freeze
         
     | 
| 
      
 273 
     | 
    
         
            +
             
     | 
| 
      
 274 
     | 
    
         
            +
              end
         
     | 
| 
      
 275 
     | 
    
         
            +
            end
         
     |