nwn-lib 0.3.2 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,44 +1,77 @@
1
- === 0.3.2
2
-
3
- * Gff: fix reading some type fields raising an exception in rare conditions
4
- * Add NWN::Gff::Struct#set for easy label setting
5
-
6
- === 0.3.1
7
-
8
- * Bugfix release only
9
- * Gff: fix struct delegator bug not passing on blocks (yuk!)
10
-
11
- === 0.3.0
12
-
13
- * Binary: nwn-gff-import
14
- * Binary: nwn-gff-print supports -m, ruby marshalling
15
- * helper: NWN::Gff::Helpers.item_property
16
- * TwoDA: Cache, more compatibility to slightly broken 2da files
17
- * CExoLocString: now has its own proper class for a more convenient API
18
- * Gff::Struct: has changed its internal API (transparent to the outside)
19
-
20
- === 0.2.2
1
+ === 0.1
2
+ Bernhard Stoeckner <elven@swordcoast.net> (15):
3
+ Add proper struct support, fix some values
4
+ Suspected to be fully-working Gff::Reader
5
+ Add working rudimentary Gff::Writer
6
+ Add substruct support
7
+ Add untested support for complex datatypes
8
+ add gff_to_yaml.rb
9
+ Add COPYING, LICENCE, README, Rakefile, spec/
10
+ Update README
11
+ add nwn-gff-print.rb binary, update gff lib
12
+ rename nwn-gff-print.rb to nwn-gff-print
13
+ nwn-gff-print: allow proper printing of sub-structs/lists
14
+ Add proper get/set support for Gff, add Element validations
15
+ Rakefile/gem: add nwn-gff-print to binary list
16
+ Rename project to nwn-lib
17
+ Fix Writer method scope
21
18
 
22
- * Binary: nwn-gff-irb: interactive gff inspection/editing shell
23
- * Gff: Errors are now RuntimeErrors instead of Exceptions
24
- * Gff::Reader: properly read structures with no labels inside
25
- * Gff::Reader: void datatype support
19
+ === 0.2
20
+ Bernhard Stoeckner <elven@swordcoast.net> (6):
21
+ Fix stupid typo in Rakefile
22
+ Add a basic 2da parser
23
+ twoda: Read windows-linebreaks as well.
24
+ Twoda: add .to_2da
25
+ nwn-gff-print/Gff: kivinen_format is now a member of the Gff module and supports printing types
26
+ 0.2-rel, add CHANGELOG
26
27
 
27
28
  === 0.2.1
29
+ Bernhard Stoeckner <elven@swordcoast.net> (6):
30
+ TwoDA: Update API for new schema
31
+ Gff: minor documentation update
32
+ Gff/kivinen_print: be more compatible
33
+ Gff/kivinen_format: add filetype-override for custom headers, add documentation
34
+ Add cheatsheet
35
+ Version 0.2.1-rel
28
36
 
29
- * TwoDA allows creation of tables and proper writing, HAS NEW API. (sorry!)
30
- * NWN::Gff.kivinen_print takes more options, compatibility updates.
31
- * nwn-gff-print takes option --type: override file type, prints a custom header.
32
- * nwn-gff-print takes option -f: print full path. (formerly -k)
33
- * nwn-gff-print takes option --struct: override struct id
34
- * Added CHEATSHEET
37
+ === 0.3.0
38
+ Bernhard Stoeckner <elven@swordcoast.net> (16):
39
+ Gff::Reader: Fix reading structs with no subvalues
40
+ Gff::Reader: void datatype support
41
+ add nwn-gff-irb
42
+ Gff: raise RuntimeError instead of generic Exceptions
43
+ Update CHEATSHEET for gff-irb
44
+ 0.2.2-rel
45
+ TwoDA: Do not parse empty lines
46
+ Add global, optional, 2da cache, add NWN::Gff.item_property helper
47
+ item_property: resolve unique partial matches
48
+ CExoLocString: more comfortable class to manage (API change)
49
+ compatibility fixes: allow non-empty-line-2das, be slightly more verbose
50
+ Gff::Struct is a delegator to Hash to allow seamless YAML marshalling
51
+ nwn-gff-print: -m: add support for native ruby marshalling
52
+ Binary: add nwn-gff-import
53
+ TwoDA: compatibility fixes (just warn on double ID assignments)
54
+ 0.3.0-rel
35
55
 
36
- === 0.2
56
+ === 0.3.1
57
+ Bernhard Stoeckner <elven@swordcoast.net> (2):
58
+ Gff: fix struct delegator bug not passing on blocks (yuk!)
59
+ 0.3.1-rel
37
60
 
38
- * Add TwoDA reading/writing support.
39
- * nwn-gff-print supports kivinen-style ____type printing.
61
+ === 0.3.2
62
+ Bernhard Stoeckner <elven@swordcoast.net> (3):
63
+ Add NWN::Gff::Struct#set for easy label setting
64
+ Gff: fix reading some type fields raising an exception in rare conditions
65
+ 0.3.2-rel
40
66
 
41
- === 0.1
67
+ === 0.3.3
68
+ Bernhard Stoeckner <elven@swordcoast.net> (8):
69
+ Gff#get_or_set: fix addressing exolocstr languages (/Description/0)
70
+ Helpers.item_property: ? expands the full list
71
+ fix private method `select' called while accessing ExoLocStr/Language
72
+ add support for sorted and cleaned-up yaml output
73
+ writer: void written properly
74
+ nwn-gff-print: add support for multiple files
75
+ nwn-gff-import: add support for multiple files
76
+ 0.3.3-rel
42
77
 
43
- * Add basic functionality for reading and writing arbitary gff files.
44
- * Binaries: nwn-gff-print for simple display of gff files.
data/Rakefile CHANGED
@@ -9,7 +9,7 @@ include FileUtils
9
9
  # Configuration
10
10
  ##############################################################################
11
11
  NAME = "nwn-lib"
12
- VERS = "0.3.2"
12
+ VERS = "0.3.3"
13
13
  CLEAN.include ["**/.*.sw?", "pkg", ".config", "rdoc", "coverage"]
14
14
  RDOC_OPTS = ["--quiet", "--line-numbers", "--inline-source", '--title', \
15
15
  'nwn-lib: a ruby library for accessing NWN resource files', \
data/bin/nwn-gff-import CHANGED
@@ -6,49 +6,65 @@ require 'nwn/helpers'
6
6
  require 'yaml'
7
7
 
8
8
  format = nil
9
+ postfix = nil
10
+ outfile = nil
9
11
 
10
12
  OptionParser.new do |o|
11
- o.banner = "Usage: nwn-gff-import [options] file/- outfile/-"
13
+ o.banner = "Usage: nwn-gff-import [options] file/- [file, file, file]"
14
+
12
15
  o.on "-y", "--yaml", "Import as yaml" do
13
16
  format = :yaml
14
17
  end
15
18
  o.on "-m", "--marshal", "Import as native ruby marshal data" do
16
19
  format = :marshal
17
20
  end
21
+
22
+ o.on "-o", "--outfile F", "Write to outfile instead of stdout" do |out|
23
+ outfile = out
24
+ end
25
+
26
+ o.on "--postfix P", "Strip the given postfix from file and write to that instead of stdout (overrides -o)" do |p|
27
+ postfix = p
28
+ end
29
+
18
30
  end.parse!
19
31
 
20
- infile = ARGV.shift or begin
32
+ ARGV.size > 0 or begin
21
33
  $stderr.puts "Required argument: filename to read, or - for stdin (try -h)."
22
34
  exit 1
23
35
  end
24
- outfile = ARGV.shift or begin
25
- $stderr.puts "Required argument: filename to write, or - for stdout (try -h)."
26
- exit 1
27
- end
28
36
 
29
- if infile == "-"
30
- inbytes = $stdin.read
31
- else
37
+ ARGV.each do |infile|
38
+ data = nil
39
+
40
+ if !postfix && !outfile
41
+ outfile = $stdout
42
+ elsif postfix && outfile
43
+ outfile = outfile.gsub(/#{Regexp.escape(postfix)}$/, "")
44
+ outfile = File.new(outfile, "w")
45
+ elsif outfile && !postfix
46
+ outfile = File.new(outfile, "w")
47
+ end
48
+
49
+ infile = $stdin if infile == "-"
32
50
  inbytes = IO.read(infile)
33
- end
34
51
 
35
- data = nil
52
+ case format
53
+ when :yaml
54
+ data = YAML.load(inbytes)
55
+ when :marshal
56
+ data = Marshal.load(inbytes)
57
+ else
58
+ raise ArgumentError, "Unknown format; try -h"
59
+ end
60
+
61
+ raise ArgumentError, "Input stream is NOT a valid gff object" unless
62
+ data.is_a?(NWN::Gff::Gff)
63
+
64
+ outbytes = NWN::Gff::Writer.dump(data)
36
65
 
37
- case format
38
- when :yaml
39
- data = YAML.load(inbytes)
40
- when :marshal
41
- data = Marshal.load(inbytes)
42
- else
43
- raise ArgumentError, "Unknown format; try -h"
44
- end
45
66
 
46
- raise ArgumentError, "Input stream is NOT a valid gff object" unless
47
- data.is_a?(NWN::Gff::Gff)
67
+ outfile.write(outbytes)
48
68
 
49
- outbytes = NWN::Gff::Writer.dump(data)
50
- if outfile == "-"
51
- puts outbytes
52
- else
53
- File.open(outfile, "w") {|f| f.write(outbytes) }
69
+ outfile.close
54
70
  end
data/bin/nwn-gff-print CHANGED
@@ -10,25 +10,45 @@ types_too = false
10
10
  full = false
11
11
  file_type = nil
12
12
  struct_id = nil
13
+ path = nil
14
+ prefix = :none
15
+ postfix = nil
16
+ verbose = false
13
17
 
14
18
  OptionParser.new do |o|
15
- o.banner = "Usage: nwn-gff-print [options] file/- [path]"
19
+ o.banner = "Usage: nwn-gff-print [options] file/- [file, file, ..]"
20
+
16
21
  o.on "-y", "--yaml", "Dump as yaml" do
17
22
  format = :yaml
18
23
  end
24
+
19
25
  o.on "-k", "--kivinen", "Dump as kivinens dump format (like the perl tools)" do
20
26
  format = :kivinen
21
27
  end
22
- o.on "-f", "--kivinen-full-path", "Print the full path (implies -k)" do
28
+ o.on "--kivinen-full-path", "Print the full path (implies -k)" do
23
29
  format = :kivinen
24
30
  full = true
25
31
  end
32
+
33
+ o.on "-b", "--print-basename", "Prefix the file basename to the output" do |b|
34
+ prefix = :base
35
+ end
36
+ o.on "-f", "--print-filename", "Prefix the full filename to the output" do |b|
37
+ prefix = :full
38
+ end
39
+
40
+ o.on "-t", "--print-types", "Print types as well" do
41
+ types_too = true
42
+ end
43
+
26
44
  o.on "-m", "--marshal", "Native ruby marshalling. Warning: raw bytes" do
27
45
  format = :marshal
28
46
  end
29
- o.on "-t", "--types", "Dump types as well (only applies to -k, for now)" do
30
- types_too = true
47
+
48
+ o.on "--postfix P", "Write output to file postfixed with P instead of stdout" do |p|
49
+ postfix = p
31
50
  end
51
+
32
52
  o.on "--type filetype", "Override the file type (implies --struct 0xffffffff)" do |t|
33
53
  if t.size != 3
34
54
  $stderr.puts "Invalid type #{t} passwd."
@@ -37,44 +57,67 @@ OptionParser.new do |o|
37
57
  file_type = t.upcase + " "
38
58
  struct_id = 0xffffffff
39
59
  end
60
+
40
61
  o.on "--struct id", "Override struct id (as hex, please)" do |s|
41
62
  struct_id = s.hex
42
63
  end
64
+ o.on "-p=path", "--path path", "Only print the given path" do |p|
65
+ path = p
66
+ end
67
+
68
+ o.on "-v" "--verbose", "Be verbose" do
69
+ verbose = true
70
+ end
43
71
  end.parse!
44
72
 
45
- file = ARGV.shift or begin
46
- $stderr.puts "Required argument: filename to process, or - for stdin (try -h)."
73
+ ARGV.size > 0 or begin
74
+ $stderr.puts "Required argument: filename to read, or - for stdin (try -h)."
47
75
  exit 1
48
76
  end
49
77
 
50
- path = ARGV.shift
78
+ ARGV.each {|file|
51
79
 
52
- if file == "-"
53
- bytes = $stdin.read
54
- else
55
- bytes = IO.read(file)
56
- end
80
+ if file == "-"
81
+ bytes = $stdin.read
82
+ else
83
+ bytes = IO.read(file)
84
+ end
57
85
 
58
- g = NWN::Gff::Reader.read(bytes)
86
+ g = NWN::Gff::Reader.read(bytes)
59
87
 
60
- if path
61
- begin
62
- g = g[path]
63
- rescue Exception => e
64
- $stderr.puts "Error: " + e.to_s
65
- exit 1
88
+ if path
89
+ begin
90
+ g = g[path]
91
+ rescue Exception => e
92
+ $stderr.puts "Error: " + e.to_s
93
+ exit 1
94
+ end
66
95
  end
67
- end
68
96
 
69
- case format
70
- when :yaml
71
- y g
72
- when :kivinen
73
- NWN::Gff.kivinen_format g, "/", types_too, full, file_type, struct_id do |label, value|
74
- puts "%s:\t%s" % [label, value]
75
- end
76
- when :marshal
77
- puts Marshal.dump(g)
78
- else
79
- puts "Unknown format; try -h"
80
- end
97
+ my_prefix = case prefix
98
+ when :none
99
+ ""
100
+ when :base
101
+ File.basename(file) + ": "
102
+ when :full
103
+ File.expand_path(file) + ": "
104
+ end
105
+
106
+ write_to = postfix ? File.new(file + postfix, "w") : $stdout
107
+ $stderr.puts "Processing `#{file}`" if verbose
108
+ case format
109
+ when :yaml
110
+ write_to.puts g.to_yaml.split("\n").map {|ln| my_prefix + ln }.join("\n")
111
+ when :kivinen
112
+ NWN::Gff.kivinen_format g, my_prefix + "/", types_too, full, file_type, struct_id do |label, value|
113
+ write_to.puts "%s:\t%s" % [label, value]
114
+ end
115
+ when :marshal
116
+ write_to.print Marshal.dump(g)
117
+ else
118
+ $stderr.puts "Unknown format; try -h"
119
+ exit 1
120
+ end
121
+
122
+ write_to.close if postfix
123
+ }
data/lib/nwn/gff.rb CHANGED
@@ -87,6 +87,11 @@ module NWN
87
87
  s = NWN::Gff::Element.new(add_prefix ? "(unlabeled struct)" : "", :struct, s)
88
88
  end
89
89
 
90
+ if s.is_a?(String)
91
+ yield("(unlabeled string)" + prefix, s)
92
+ return
93
+ end
94
+
90
95
  case s.type
91
96
  when :struct
92
97
  yield(prefix + " ____struct_type", struct_id.nil? ? s.value.struct_id : struct_id) if types_too
@@ -101,7 +106,7 @@ module NWN
101
106
  s.value.each {|kk,vv|
102
107
  yield(prefix + s.label + "/" + kk.to_s, vv.gsub(/([\000-\037\177-\377%])/) {|v| "%" + v.unpack("H2")[0] })
103
108
  }
104
- yield(prefix + s.label + ". ____string_ref", s._str_ref)
109
+ yield(prefix + s.label + ". ____string_ref", s.str_ref)
105
110
 
106
111
  when :list
107
112
  s.value.each_with_index {|vv, idx|
@@ -138,6 +143,10 @@ class NWN::Gff::Gff
138
143
  attr_accessor :type
139
144
  attr_accessor :version
140
145
 
146
+ def to_yaml_properties
147
+ [ '@type', '@version', '@hash' ]
148
+ end
149
+
141
150
  # Create a new GFF object from the given +struct+.
142
151
  # This is normally not needed unless you are creating
143
152
  # GFF objects entirely from hand.
@@ -174,7 +183,7 @@ class NWN::Gff::Gff
174
183
  # overwrite an existing one with the same label.
175
184
  # gff['/PropertiesList[0]'] = 'Test'
176
185
  # This will raise an error (obviously)
177
- def get_or_set k, new_value = nil, new_type = nil, new_label = nil, new_str_ref = nil
186
+ def get_or_set k, new_value = nil, new_type = nil, new_label = nil, newstr_ref = nil
178
187
  h = self.root_struct
179
188
  path = []
180
189
  value_path = [h]
@@ -192,8 +201,7 @@ class NWN::Gff::Gff
192
201
  if h.is_a?(Gff::Element)
193
202
  case h.type
194
203
  when :cexolocstr
195
- current_value = h.value.select {|vx| vx.language.to_i == v.to_i}
196
- current_value = current_value[0] != nil ? current_value[0].text : ''
204
+ current_value = h.value.languages[v.to_i] || ''
197
205
 
198
206
  when :list
199
207
  raise GffPathInvalidError, "List-selector access not implemented yet."
@@ -259,10 +267,10 @@ class NWN::Gff::Gff
259
267
 
260
268
  end
261
269
 
262
- if !new_str_ref.nil?
270
+ if !newstr_ref.nil?
263
271
  # Set a new str_ref
264
272
  raise GffTypeError, "specified path is not a CExoStr" unless current_value.is_a?(Gff::CExoString)
265
- current_value._str_ref = new_str_ref.to_i
273
+ current_value.str_ref = new_str_ref.to_i
266
274
  end
267
275
 
268
276
  if !new_value.nil?
@@ -276,7 +284,7 @@ class NWN::Gff::Gff
276
284
 
277
285
  when String #means: cexolocstr assignment
278
286
  if value_path[-2].is_a?(Gff::Element) && value_path[-2].type == :cexolocstr
279
- value_path[-2].value.select{|xy| xy.language == path[-1].to_i }[0].text = new_value
287
+ value_path[-2].value[path[-1].to_i] = new_value
280
288
  else
281
289
  raise GffPathInvalidError, "Dont know how to set #{new_value.class} on #{path.inspect}."
282
290
  end
@@ -304,20 +312,36 @@ end
304
312
 
305
313
  # A Element wraps a GFF label->value pair,
306
314
  # provides a +.type+ and, optionally,
307
- # a +._str_ref+ for CExoLocStrings.
315
+ # a +.str_ref+ for CExoLocStrings.
308
316
  #
309
317
  # Fields:
310
318
  # [+label+] The label of this element, for reference.
311
319
  # [+type+] The type of this element. (See NWN::Gff)
312
320
  # [+value+] The value of this element.
313
321
  class NWN::Gff::Element
314
- attr_accessor :label, :type, :value
315
- attr_accessor :_str_ref
322
+ NonInline = [:struct, :list, :cexolocstr]
323
+
324
+ attr_accessor :label, :type, :value, :str_ref
325
+
326
+ def to_yaml_properties
327
+ [ '@label', '@type', '@value', '@str_ref' ]
328
+ end
316
329
 
317
330
  def initialize label = nil, type = nil, value = nil
318
331
  @label, @type, @value = label, type, value
319
332
  end
320
333
 
334
+ def to_yaml( opts = {} )
335
+ YAML::quick_emit( self, opts ) do |out|
336
+ out.map( taguri, to_yaml_style ) do |map|
337
+ map.style = :inline unless NonInline.index(self.type)
338
+ to_yaml_properties.sort.each do |m|
339
+ map.add( m[1..-1], instance_variable_get( m ) ) unless instance_variable_get( m ).nil?
340
+ end
341
+ end
342
+ end
343
+ end
344
+
321
345
  def validate path_prefix = "/"
322
346
  raise NWN::Gff::GffTypeError, "#{path_prefix}#{self.label}: New value #{self.value} is not compatible with the current type #{self.type}" unless
323
347
  self.class.valid_for?(self.value, self.type)
@@ -376,6 +400,10 @@ class NWN::Gff::Struct
376
400
  attr_accessor :struct_id
377
401
  attr_accessor :hash
378
402
 
403
+ def to_yaml_properties
404
+ [ '@struct_id', '@hash' ]
405
+ end
406
+
379
407
  def initialize
380
408
  @struct_id = 0
381
409
  @hash = {}
@@ -602,7 +630,7 @@ class NWN::Gff::Reader
602
630
  total_size, str_ref, str_count =
603
631
  @field_data[data_or_offset, 12].unpack("VVV")
604
632
  all = @field_data[data_or_offset + 12, total_size]
605
- field._str_ref = str_ref
633
+ field.str_ref = str_ref
606
634
 
607
635
  str_count.times {
608
636
  id, len = all.unpack("VV")
@@ -770,8 +798,6 @@ private
770
798
 
771
799
  # complex data types
772
800
  when :dword64, :int64, :double, :void
773
- $stderr.puts "Warning: complex datatypes dword64, int64, double and void are untested."
774
-
775
801
  fields_of_this_struct << add_data_field(v.type, k, @field_data.size)
776
802
  format = Formats[v.type]
777
803
  @field_data << case v.type
@@ -781,13 +807,11 @@ private
781
807
  v.value % (2**32)
782
808
  ].pack("II")
783
809
  when :void
784
- [ v.value.size, v.value ].pack("VH*")
810
+ [ v.value.size / 2, v.value ].pack("VH*")
785
811
  else
786
812
  [v.value].pack(format)
787
813
  end
788
814
 
789
- raise GffError, "unhandled complex datatype #{v.type}"
790
-
791
815
  when :struct
792
816
  raise GffError, "type = struct, but value not a hash" unless
793
817
  v.value.is_a?(Gff::Struct)
@@ -833,7 +857,7 @@ private
833
857
  }
834
858
  @field_data << [
835
859
  total_size,
836
- v._str_ref,
860
+ v.str_ref,
837
861
  v.value.size
838
862
  ].pack("VVV")
839
863
 
data/lib/nwn/helpers.rb CHANGED
@@ -68,6 +68,8 @@ module NWN
68
68
  def self.resolve_or_match_partial name_spec, list #:nodoc:
69
69
  name_spec = name_spec.downcase
70
70
 
71
+ raise ArgumentError, "?-expand: #{list.inspect}" if name_spec == '?'
72
+
71
73
  list.each {|l|
72
74
  return l if l.downcase == name_spec
73
75
  }
data/lib/nwn/yaml.rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'yaml'
2
+
3
+ class Hash
4
+ # Replacing the to_yaml function so it'll serialize hashes sorted (by their keys)
5
+ # Original function is in /usr/lib/ruby/1.8/yaml/rubytypes.rb
6
+ def to_yaml( opts = {} )
7
+ YAML::quick_emit( object_id, opts ) do |out|
8
+ out.map( taguri, to_yaml_style ) do |map|
9
+ sort.each do |k, v|
10
+ map.add( k, v )
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: nwn-lib
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.3.2
7
- date: 2008-07-10 00:00:00 +02:00
6
+ version: 0.3.3
7
+ date: 2008-08-07 00:00:00 +02:00
8
8
  summary: a ruby library for accessing Neverwinter Nights resource files
9
9
  require_paths:
10
10
  - lib
@@ -39,6 +39,7 @@ files:
39
39
  - spec/spec.opts
40
40
  - spec/rcov.opts
41
41
  - lib/nwn
42
+ - lib/nwn/yaml.rb
42
43
  - lib/nwn/twoda.rb
43
44
  - lib/nwn/gff.rb
44
45
  - lib/nwn/helpers.rb