nwn-lib 0.4.9 → 0.4.10
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/CHANGELOG +30 -0
- data/HOWTO +40 -0
- data/README +9 -2
- data/Rakefile +3 -7
- data/SETTINGS +9 -1
- data/bin/nwn-erf +18 -3
- data/bin/nwn-gff +14 -5
- data/lib/nwn/all.rb +5 -3
- data/lib/nwn/erf.rb +1 -1
- data/lib/nwn/gff/field.rb +50 -1
- data/lib/nwn/gff/reader.rb +2 -4
- data/lib/nwn/gff/struct.rb +40 -1
- data/lib/nwn/gff.rb +37 -36
- data/lib/nwn/json_support.rb +37 -0
- data/lib/nwn/key.rb +144 -0
- data/lib/nwn/kivinen_support.rb +67 -0
- data/lib/nwn/res.rb +16 -12
- data/lib/nwn/settings.rb +9 -1
- data/lib/nwn/{yaml.rb → yaml_support.rb} +20 -6
- data/spec/bin_gff_spec.rb +2 -2
- data/spec/erf_spec.rb +7 -0
- data/spec/key_spec.rb +38 -0
- data/spec/spec_helper.rb +31 -1
- metadata +40 -30
- data/lib/nwn/kivinen.rb +0 -55
- data/spec/rcov.opts +0 -0
- data/spec/spec.opts +0 -0
data/CHANGELOG
CHANGED
@@ -269,3 +269,33 @@ Bernhard Stoeckner <elven@swordcoast.net> (10):
|
|
269
269
|
Some medium rare bugfixes, and a overloaded operator for GFF structs,
|
270
270
|
which can be used to comfortable walk on trees.
|
271
271
|
|
272
|
+
=== 0.4.10
|
273
|
+
Bernhard Stoeckner <elven@swordcoast.net> (23):
|
274
|
+
bin/nwn-erf: exit(1) on errors
|
275
|
+
bin/nwn-erf: support extracting parts of archive with -x
|
276
|
+
Erf::Erf: set correct internal year on initialize
|
277
|
+
bin/nwn-gff: fix reading gff data from stdin
|
278
|
+
Gff::Field: transform type to symbols on extend
|
279
|
+
Gff::Struct#path: return full data type by walking the tree on the fly
|
280
|
+
Gff: add :pretty format that prettyprints
|
281
|
+
Gff::Struct, Gff::Field: add generic box/unbox methods
|
282
|
+
Gff: JSON support
|
283
|
+
new ENV NWN_LIB_IN_ENCODING specifies nwn-gff encoding
|
284
|
+
:pretty-print boxes before printing
|
285
|
+
Gff: JSON support adheres NWN_LIB_PRETTY_JSON env var
|
286
|
+
Kivinen-format: refactor into Marshal load/dump interface
|
287
|
+
Res/ContentObject: minor internal cleanups
|
288
|
+
Gff::Reader: do not compact exolocstrs on read-in
|
289
|
+
YAML/JSON support: fix spec, add dep, fix files to not clash with gem names
|
290
|
+
Rakefile: spec support for >= 1.1.9
|
291
|
+
NWN::Key: .key, .bif reading support
|
292
|
+
Resources::DirectoryContainer: do not fail on invalid files in directory
|
293
|
+
documentation updates
|
294
|
+
bin/nwn-erf: do not set description str_ref to 0x0
|
295
|
+
NWN::Gff: refactor API to properly partition different file format handlers
|
296
|
+
|
297
|
+
A swath of bugfixes, JSON read/write, and finally .key and .bif reading support.
|
298
|
+
Also, Resources::Manager got some documentation and a few touches here and there,
|
299
|
+
as did bin/nwn-erf, which supports partial extraction now.
|
300
|
+
For our non-ASCII users, there is a ENV variable that can be used to specify the
|
301
|
+
encoding in which local GFF data is supposed to be in.
|
data/HOWTO
CHANGED
@@ -168,3 +168,43 @@ Now read-only access with a TlkSet:
|
|
168
168
|
|
169
169
|
You cannot use TlkSet to write out .tlk files or modify
|
170
170
|
existing entries - it is merely a wrapper.
|
171
|
+
|
172
|
+
=== Accessing .key index files
|
173
|
+
|
174
|
+
A key file is an index into all shipped game resources.
|
175
|
+
|
176
|
+
key = Key::Key.new(File.new("/path/to/chitin.key", "r"), "/path/to/")
|
177
|
+
|
178
|
+
This will lookup all indexed bif files and the resources
|
179
|
+
contained within.
|
180
|
+
|
181
|
+
=== Resource Manager
|
182
|
+
|
183
|
+
The resource manager can be used to simulate a NWN-style
|
184
|
+
resource manager to read files from .key/bifs, override,
|
185
|
+
haks, and similar sources. Files are located in the reverse
|
186
|
+
order that their containers are added to the Manager.
|
187
|
+
|
188
|
+
Example detailing the usual NWN lookup procedure:
|
189
|
+
|
190
|
+
nwn_path = "/path/to/nwn/"
|
191
|
+
mgr = Resources::Manager.new
|
192
|
+
|
193
|
+
# First, all the base data files.
|
194
|
+
for key in %w{chitin.key xp1.key xp1patch.key xp2.key xp2patch.key xp3.key}
|
195
|
+
mgr.add_container(Key::Key.new(File.new(nwn_path + key, "r"), nwn_path))
|
196
|
+
end
|
197
|
+
|
198
|
+
# Override
|
199
|
+
mgr.add_container(Resources::DirectoryContainer.new(nwn_path + "override"))
|
200
|
+
|
201
|
+
# All custom haks
|
202
|
+
for hak in %w{a.hak b.hak c.hak}
|
203
|
+
mgr.add_container(Erf::Erf.new(File.new(nwn_path + "hak/" + hak, "r")))
|
204
|
+
end
|
205
|
+
|
206
|
+
# Now you can retrieve any indexed file:
|
207
|
+
puts mgr.get("actions.2da")
|
208
|
+
|
209
|
+
Note that initialising the whole Resource Manager this way takes
|
210
|
+
a few seconds depending on IO and CPU speed.
|
data/README
CHANGED
@@ -11,15 +11,22 @@ They should work with NWN2 just as well, since the file format specifications di
|
|
11
11
|
|
12
12
|
* GFF 3.2 (are, git, gic, dlg, itp, ifo, jrl, fac, ssf, ut*, among others)
|
13
13
|
* ERF (mod, hak, erf, among others)
|
14
|
+
* KEY, BIF (key, data/*.bif)
|
14
15
|
* 2DA V2.0
|
15
16
|
* TLK
|
16
17
|
|
17
|
-
==== nwn-lib can
|
18
|
+
==== nwn-lib can handle the following representations of GFF data:
|
18
19
|
|
19
|
-
|
20
|
+
===== read & write:
|
21
|
+
* native gff
|
20
22
|
* yaml presentation of data
|
23
|
+
* json presentation of data (with proper UTF-8 conversion)
|
21
24
|
* ruby-native marshalling of gff data
|
22
25
|
|
26
|
+
===== just write, for now:
|
27
|
+
* kivinen-style ("gffprint.pl") presentation of data
|
28
|
+
* prettyprint (ruby-specific)
|
29
|
+
|
23
30
|
==== Also in the box:
|
24
31
|
|
25
32
|
* shell scripts and tools to simplify your life (see BINARIES)
|
data/Rakefile
CHANGED
@@ -9,7 +9,7 @@ include FileUtils
|
|
9
9
|
# Configuration
|
10
10
|
##############################################################################
|
11
11
|
NAME = "nwn-lib"
|
12
|
-
VERS = "0.4.
|
12
|
+
VERS = "0.4.10"
|
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', \
|
@@ -44,6 +44,7 @@ spec = Gem::Specification.new do |s|
|
|
44
44
|
s.files = %w(COPYING CHANGELOG README Rakefile) + Dir.glob("{bin,doc,spec,lib,tools,scripts,data}/**/*")
|
45
45
|
s.require_path = "lib"
|
46
46
|
s.bindir = "bin"
|
47
|
+
s.add_dependency('json', '>= 1.1.9')
|
47
48
|
end
|
48
49
|
|
49
50
|
Rake::GemPackageTask.new(spec) do |p|
|
@@ -70,7 +71,7 @@ end
|
|
70
71
|
|
71
72
|
desc "Upload nwn-lib gem to rubyforge"
|
72
73
|
task :release => [:package] do
|
73
|
-
sh %{rubyforge login}
|
74
|
+
#sh %{rubyforge login}
|
74
75
|
sh %{rubyforge add_release nwn-lib #{NAME} #{VERS} pkg/#{NAME}-#{VERS}.tgz}
|
75
76
|
sh %{rubyforge add_file nwn-lib #{NAME} #{VERS} pkg/#{NAME}-#{VERS}.gem}
|
76
77
|
end
|
@@ -80,8 +81,6 @@ require "spec/rake/spectask"
|
|
80
81
|
desc "Run specs with coverage"
|
81
82
|
Spec::Rake::SpecTask.new("spec") do |t|
|
82
83
|
t.spec_files = FileList["spec/*_spec.rb"]
|
83
|
-
t.spec_opts = File.read("spec/spec.opts").split("\n")
|
84
|
-
t.rcov_opts = File.read("spec/rcov.opts").split("\n")
|
85
84
|
t.rcov = true
|
86
85
|
end
|
87
86
|
|
@@ -89,13 +88,10 @@ desc "Run specs without coverage"
|
|
89
88
|
task :default => [:spec_no_cov]
|
90
89
|
Spec::Rake::SpecTask.new("spec_no_cov") do |t|
|
91
90
|
t.spec_files = FileList["spec/*_spec.rb"]
|
92
|
-
t.spec_opts = File.read("spec/spec.opts").split("\n")
|
93
91
|
end
|
94
92
|
|
95
93
|
desc "Run rcov only"
|
96
94
|
Spec::Rake::SpecTask.new("rcov") do |t|
|
97
|
-
t.rcov_opts = File.read("spec/rcov.opts").split("\n")
|
98
|
-
t.spec_opts = File.read("spec/spec.opts").split("\n")
|
99
95
|
t.spec_files = FileList["spec/*_spec.rb"]
|
100
96
|
t.rcov = true
|
101
97
|
end
|
data/SETTINGS
CHANGED
@@ -6,6 +6,14 @@ Under linux, just add them to your shell environment (usually .bashrc), like so:
|
|
6
6
|
|
7
7
|
export NWN_LIB_DEBUG=0
|
8
8
|
|
9
|
+
== NWN_LIB_IN_ENCODING
|
10
|
+
|
11
|
+
The character encoding your local data files are in. Defaults to ISO-8859-1.
|
12
|
+
|
13
|
+
== NWN_LIB_PRETTY_JSON
|
14
|
+
|
15
|
+
Set to non-nil to make :json print pretty output (with whitespace) instead of a spaghetti
|
16
|
+
bowl. nwn-lib returns compact json output, by default. Does not affect reading json.
|
9
17
|
|
10
18
|
== NWN_LIB_DEBUG
|
11
19
|
|
@@ -24,7 +32,7 @@ time tracking things down.
|
|
24
32
|
|
25
33
|
Note that nwn-lib takes a parameter that can force a specific NWN version.
|
26
34
|
|
27
|
-
|
35
|
+
== NWN_LIB_RESREF32
|
28
36
|
|
29
37
|
Set this to "1" if you are working with NWN2 files and do not want to see the warnings
|
30
38
|
generated by too long resref values.
|
data/bin/nwn-erf
CHANGED
@@ -9,6 +9,8 @@ $type = 'ERF'
|
|
9
9
|
$allow_duplicates = false
|
10
10
|
$descriptions = {}
|
11
11
|
$version = "V1.0"
|
12
|
+
$file = nil
|
13
|
+
$error = 0
|
12
14
|
|
13
15
|
# tar-compat mode: first argument is options if no dash is specified.
|
14
16
|
ARGV[0] = "-" + ARGV[0] if ARGV.size > 0 && ARGV[0][0] != ?-
|
@@ -28,7 +30,7 @@ begin OptionParser.new do |o|
|
|
28
30
|
o.on "-c", "--create", "Create a new archive with the given files as contents." do
|
29
31
|
$action = :c
|
30
32
|
end
|
31
|
-
o.on "-x", "--extract", "Extract all
|
33
|
+
o.on "-x", "--extract", "Extract FILEs (or all) to current directory." do
|
32
34
|
$action = :x
|
33
35
|
end
|
34
36
|
o.on "-a", "--add", "Add files to the given archive.",
|
@@ -131,7 +133,17 @@ case $action
|
|
131
133
|
when :x
|
132
134
|
input {|f|
|
133
135
|
erf = NWN::Erf::Erf.new(f)
|
136
|
+
ARGV.each {|x|
|
137
|
+
wot = erf.content.select {|cc| cc.filename == x }
|
138
|
+
if wot.size == 0
|
139
|
+
$stderr.puts "nwn-erf: #{x}: not found in erf"
|
140
|
+
$error = 1
|
141
|
+
end
|
142
|
+
}
|
143
|
+
what = ARGV.map {|x| x.downcase }
|
134
144
|
erf.content.each {|c|
|
145
|
+
next if what.size > 0 && !what.index(c.filename.downcase)
|
146
|
+
|
135
147
|
puts "%s" % [c.filename] if $verbose
|
136
148
|
output(c.filename) {|ff|
|
137
149
|
ff.write(c.get)
|
@@ -146,7 +158,6 @@ case $action
|
|
146
158
|
|
147
159
|
if $descriptions
|
148
160
|
erf.localized_strings.merge! $descriptions
|
149
|
-
erf.description_str_ref = 0
|
150
161
|
end
|
151
162
|
|
152
163
|
ARGV.each {|a|
|
@@ -163,7 +174,6 @@ case $action
|
|
163
174
|
|
164
175
|
if $descriptions
|
165
176
|
erf.localized_strings.merge! $descriptions
|
166
|
-
erf.description_str_ref = 0
|
167
177
|
end
|
168
178
|
|
169
179
|
ARGV.each {|arg|
|
@@ -192,3 +202,8 @@ case $action
|
|
192
202
|
else
|
193
203
|
raise ArgumentError, "You need to specify a mode of operation (try -h)."
|
194
204
|
end
|
205
|
+
|
206
|
+
if $error == 1
|
207
|
+
$stderr.puts "nwn-erf: Exiting with failure status due to previous errors"
|
208
|
+
exit(1)
|
209
|
+
end
|
data/bin/nwn-gff
CHANGED
@@ -7,6 +7,7 @@ Thread.abort_on_exception = true
|
|
7
7
|
|
8
8
|
$options = {
|
9
9
|
:backup => nil,
|
10
|
+
:encoding => nil,
|
10
11
|
:force => false,
|
11
12
|
:infile => '-',
|
12
13
|
:outfile => '-',
|
@@ -25,8 +26,8 @@ begin OptionParser.new do |o|
|
|
25
26
|
o.on "-i", "--infile FILE", "Input file (default: stdin)" do |f|
|
26
27
|
$options[:infile] = f
|
27
28
|
end
|
28
|
-
o.on "-l, ""--infile-format FORMAT", [:auto] + NWN::Gff::
|
29
|
-
"Input format (#{([:auto] + NWN::Gff::
|
29
|
+
o.on "-l, ""--infile-format FORMAT", [:auto] + NWN::Gff::InputFormats.keys,
|
30
|
+
"Input format (#{([:auto] + NWN::Gff::InputFormats.keys).join(', ')})",
|
30
31
|
"(default: auto - try to guess based on extension)" do |f|
|
31
32
|
$options[:informat] = f
|
32
33
|
end
|
@@ -34,8 +35,8 @@ begin OptionParser.new do |o|
|
|
34
35
|
o.on "-o", "--outfile FILE", "Output file (default: stdout)" do |f|
|
35
36
|
$options[:outfile] = f
|
36
37
|
end
|
37
|
-
o.on "-k", "--outfile-format FORMAT", [:in, :none] + NWN::Gff::
|
38
|
-
"Output format (#{([:none, :in] + NWN::Gff::
|
38
|
+
o.on "-k", "--outfile-format FORMAT", [:in, :none] + NWN::Gff::OutputFormats.keys,
|
39
|
+
"Output format (#{([:none, :in] + NWN::Gff::OutputFormats.keys).join(', ')})",
|
39
40
|
"(default: in when stdout, try to guess based on extension otherwise)" do |f|
|
40
41
|
$options[:outformat] = f
|
41
42
|
end
|
@@ -48,6 +49,10 @@ begin OptionParser.new do |o|
|
|
48
49
|
"(see `man cp' for a description)" do |b|
|
49
50
|
$options[:backup] = b.nil? ? true : b
|
50
51
|
end
|
52
|
+
o.on "--encoding ENCODING", "sets the used input encoding in which your NWN",
|
53
|
+
"files are encoded in" do |e|
|
54
|
+
$options[:encoding] = e
|
55
|
+
end
|
51
56
|
|
52
57
|
o.on "-1", "--nwn1", "Allow 16 byte resrefs." do
|
53
58
|
ENV['NWN_LIB_RESREF32'] = nil
|
@@ -110,6 +115,10 @@ end
|
|
110
115
|
$options[:informat] or fail "No input format specified."
|
111
116
|
$options[:outformat] or fail "No output format specified."
|
112
117
|
|
118
|
+
if $options[:encoding]
|
119
|
+
NWN.setting(:in_encoding, $options[:encoding])
|
120
|
+
end
|
121
|
+
|
113
122
|
if :auto == $options[:informat]
|
114
123
|
$options[:informat] = NWN::Gff.guess_file_format($options[:infile].downcase)
|
115
124
|
fail "Cannot guess infile format from filename, specify with -l." unless
|
@@ -125,7 +134,7 @@ elsif :in == $options[:outformat]
|
|
125
134
|
end
|
126
135
|
|
127
136
|
vputs "Reading: #{$options[:infile]}"
|
128
|
-
data_in = $options[:infile] == '-' ? $stdin : File.open($options[:infile], "rb")
|
137
|
+
data_in = $options[:infile] == '-' ? StringIO.new($stdin.read) : File.open($options[:infile], "rb")
|
129
138
|
data_in = NWN::Gff.read(data_in, $options[:informat])
|
130
139
|
|
131
140
|
# verify that we read a GFF struct
|
data/lib/nwn/all.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
require 'stringio'
|
2
2
|
require 'nwn/io'
|
3
3
|
require 'nwn/twoda'
|
4
|
+
require 'nwn/settings'
|
4
5
|
require 'nwn/res'
|
5
6
|
require 'nwn/gff'
|
6
7
|
require 'nwn/tlk'
|
8
|
+
require 'nwn/key'
|
7
9
|
require 'nwn/erf'
|
8
|
-
require 'nwn/
|
9
|
-
require 'nwn/
|
10
|
+
require 'nwn/yaml_support'
|
11
|
+
require 'nwn/json_support'
|
12
|
+
require 'nwn/kivinen_support'
|
10
13
|
require 'nwn/scripting'
|
11
|
-
require 'nwn/settings'
|
data/lib/nwn/erf.rb
CHANGED
data/lib/nwn/gff/field.rb
CHANGED
@@ -16,10 +16,15 @@ module NWN::Gff::Field
|
|
16
16
|
# This is set internally by Gff::Reader on load.
|
17
17
|
attr_accessor :parent
|
18
18
|
|
19
|
+
# Transform type to symbol on extend.
|
20
|
+
def self.extended p1 #:nodoc:
|
21
|
+
p1['type'] = p1['type'].to_sym if p1['type']
|
22
|
+
end
|
23
|
+
|
19
24
|
# Create a new NWN::Gff::Field
|
20
25
|
def self.new label, type, value
|
21
26
|
s = {}.extend(self)
|
22
|
-
s['label'], s['type'], s['value'] = label, type, value
|
27
|
+
s['label'], s['type'], s['value'] = label, type.to_sym, value
|
23
28
|
s.extend_meta_classes
|
24
29
|
s.validate
|
25
30
|
s
|
@@ -190,4 +195,48 @@ module NWN::Gff::Field
|
|
190
195
|
end
|
191
196
|
#:startdoc:
|
192
197
|
|
198
|
+
# Deep-unboxes a Hash, e.g. iterating down, converting all strings
|
199
|
+
# from the native charset.
|
200
|
+
def self.unbox! element, parent_label, parent = nil
|
201
|
+
element.extend(NWN::Gff::Field)
|
202
|
+
element.field_label = parent_label
|
203
|
+
element.parent = parent
|
204
|
+
element.str_ref ||= NWN::Gff::Field::DEFAULT_STR_REF if element.respond_to?('str_ref=')
|
205
|
+
|
206
|
+
element.extend_meta_classes
|
207
|
+
case element.field_type
|
208
|
+
when :cexolocstr
|
209
|
+
element.field_value.each {|x,y|
|
210
|
+
element.field_value[x.to_i] = NWN::IconvNativeToGff.call.iconv(element.field_value.delete(x))
|
211
|
+
}
|
212
|
+
when :cexostr
|
213
|
+
element.field_value = NWN::IconvNativeToGff.call.iconv(element.field_value)
|
214
|
+
|
215
|
+
when :list
|
216
|
+
element.field_value.each_with_index {|x,idx|
|
217
|
+
element.field_value[idx] = NWN::Gff::Struct.unbox!(x, element)
|
218
|
+
}
|
219
|
+
when :struct
|
220
|
+
element.field_value = NWN::Gff::Struct.unbox!(element.field_value, element)
|
221
|
+
end
|
222
|
+
element.validate
|
223
|
+
element
|
224
|
+
end
|
225
|
+
|
226
|
+
# Returns a hash of this Field without the API calls mixed in,
|
227
|
+
# all language-strings transformed by str_handler.
|
228
|
+
def box
|
229
|
+
t = Hash[self]
|
230
|
+
t.delete('label')
|
231
|
+
case field_type
|
232
|
+
when :cexolocstr
|
233
|
+
t['value'].each {|x,y|
|
234
|
+
t['value'][x] = NWN::IconvGffToNative.call.iconv(y)
|
235
|
+
}
|
236
|
+
when :cexostr
|
237
|
+
t['value'] = NWN::IconvGffToNative.call.iconv(t['value'])
|
238
|
+
end
|
239
|
+
t
|
240
|
+
end
|
241
|
+
|
193
242
|
end
|
data/lib/nwn/gff/reader.rb
CHANGED
@@ -180,8 +180,6 @@ class NWN::Gff::Reader
|
|
180
180
|
exostr[id] = str
|
181
181
|
}
|
182
182
|
len = total_size + 4
|
183
|
-
# Filter out empty strings.
|
184
|
-
exostr.reject! {|k,v| v.nil? || v.empty?}
|
185
183
|
exostr
|
186
184
|
|
187
185
|
when :void
|
@@ -189,7 +187,7 @@ class NWN::Gff::Reader
|
|
189
187
|
@field_data[data_or_offset + 4, len].unpack("H*")[0]
|
190
188
|
|
191
189
|
when :struct
|
192
|
-
read_struct data_or_offset,
|
190
|
+
read_struct data_or_offset, nil, field.parent.data_version
|
193
191
|
|
194
192
|
when :list
|
195
193
|
list = []
|
@@ -210,7 +208,7 @@ class NWN::Gff::Reader
|
|
210
208
|
data_or_offset += 1
|
211
209
|
|
212
210
|
for i in data_or_offset...(data_or_offset + count)
|
213
|
-
list << read_struct(@list_indices[i],
|
211
|
+
list << read_struct(@list_indices[i], nil, field.parent.data_version)
|
214
212
|
end
|
215
213
|
|
216
214
|
list
|
data/lib/nwn/gff/struct.rb
CHANGED
@@ -30,7 +30,11 @@ module NWN::Gff::Struct
|
|
30
30
|
|
31
31
|
# Returns the path to this struct (which is usually __data_type)
|
32
32
|
def path
|
33
|
-
@
|
33
|
+
if @element
|
34
|
+
@element.path
|
35
|
+
else
|
36
|
+
@data_type.to_s
|
37
|
+
end
|
34
38
|
end
|
35
39
|
|
36
40
|
def element= e #:nodoc:
|
@@ -200,4 +204,39 @@ module NWN::Gff::Struct
|
|
200
204
|
by_path(path)
|
201
205
|
end
|
202
206
|
|
207
|
+
# Deep-unboxes a Hash, e.g. iterating down, converting it to
|
208
|
+
# the native charset.
|
209
|
+
def self.unbox! o, parent = nil
|
210
|
+
o.extend(NWN::Gff::Struct)
|
211
|
+
o.struct_id = o.delete('__struct_id')
|
212
|
+
o.data_type = if o['__data_type']
|
213
|
+
o.delete('__data_type')
|
214
|
+
else
|
215
|
+
o.path
|
216
|
+
end
|
217
|
+
o.data_version = o.delete('__data_version')
|
218
|
+
|
219
|
+
o.element = parent if parent
|
220
|
+
|
221
|
+
o.each {|label,element|
|
222
|
+
o[label] = NWN::Gff::Field.unbox!(element, label, o)
|
223
|
+
}
|
224
|
+
|
225
|
+
o
|
226
|
+
end
|
227
|
+
|
228
|
+
# Returns a hash of this Struct without the API calls mixed in,
|
229
|
+
# converting it from the native charset.
|
230
|
+
def box
|
231
|
+
t = Hash[self]
|
232
|
+
t.merge!({
|
233
|
+
'__struct_id' => self.struct_id,
|
234
|
+
'__data_version' => self.data_version,
|
235
|
+
})
|
236
|
+
t.merge!({
|
237
|
+
'__data_type' => self.data_type
|
238
|
+
}) if self.element == nil
|
239
|
+
t
|
240
|
+
end
|
241
|
+
|
203
242
|
end
|
data/lib/nwn/gff.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'pp'
|
2
|
+
|
1
3
|
module NWN
|
2
4
|
module Gff
|
3
5
|
# This error gets thrown if reading or writing fails.
|
@@ -56,20 +58,35 @@ module NWN
|
|
56
58
|
:double => 'd',
|
57
59
|
}.freeze
|
58
60
|
|
59
|
-
#
|
60
|
-
|
61
|
+
# Registers a new format handler that can deal with file formats for nwn-lib gff handling.
|
62
|
+
def self.register_format_handler name, fileFormatRegexp, klass, reads = true, writes = true
|
63
|
+
InputFormats[name.to_sym] = klass if reads
|
64
|
+
OutputFormats[name.to_sym] = klass if writes
|
65
|
+
FileFormatGuesses[fileFormatRegexp] = name.to_sym
|
66
|
+
end
|
67
|
+
|
68
|
+
class Handler
|
69
|
+
def self.load io
|
70
|
+
NWN::Gff::Reader.read(io)
|
71
|
+
end
|
72
|
+
def self.dump data, io
|
73
|
+
NWN::Gff::Writer.dump(data, io)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class Pretty
|
78
|
+
def self.dump data, io
|
79
|
+
old = $> ; $> = io ; pp data.box ; $> = old
|
80
|
+
end
|
81
|
+
end
|
61
82
|
|
62
|
-
|
83
|
+
InputFormats = {}
|
84
|
+
OutputFormats = {}
|
85
|
+
FileFormatGuesses = {}
|
63
86
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
/^(mod|ifo|fac|ssf|dlg|itp)$/ => :gff,
|
68
|
-
/^(bic)$/ => :gff,
|
69
|
-
/^ya?ml$/ => :yaml,
|
70
|
-
/^marshal$/ => :marshal,
|
71
|
-
/^k(ivinen)?$/ => :kivinen,
|
72
|
-
}
|
87
|
+
register_format_handler :gff, /^(ut[cdeimpstw]|git|are|gic|mod|ifo|fac|ssf|dlg|itp|bic)$/, NWN::Gff::Handler
|
88
|
+
register_format_handler :marshal, /^marshal$/, Marshal
|
89
|
+
register_format_handler :pretty, /^$/, Pretty, false, true
|
73
90
|
|
74
91
|
def self.guess_file_format(filename)
|
75
92
|
extension = File.extname(filename.downcase)[1..-1]
|
@@ -77,34 +94,18 @@ module NWN
|
|
77
94
|
end
|
78
95
|
|
79
96
|
def self.read(io, format)
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
YAML.load(io)
|
85
|
-
when :marshal
|
86
|
-
Marshal.load(io)
|
87
|
-
when :kivinen
|
88
|
-
raise NotImplementedError, "Reading kivinen-style data is not supported."
|
89
|
-
else
|
90
|
-
raise NotImplementedError, "Don't know how to read #{format}."
|
97
|
+
if InputFormats[format]
|
98
|
+
InputFormats[format].load(io)
|
99
|
+
else
|
100
|
+
raise NotImplementedError, "Don't know how to read #{format}."
|
91
101
|
end
|
92
102
|
end
|
93
103
|
|
94
104
|
def self.write(io, format, data)
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
io.puts data.to_yaml
|
100
|
-
when :marshal
|
101
|
-
io.print Marshal.dump(data)
|
102
|
-
when :kivinen
|
103
|
-
data.kivinen_format $options[:types], nil, nil do |l,v|
|
104
|
-
io.puts "%s:\t%s" % [l, v]
|
105
|
-
end
|
106
|
-
else
|
107
|
-
raise NotImplementedError, "Don't know how to write data-format #{format.inspect}"
|
105
|
+
if OutputFormats[format]
|
106
|
+
OutputFormats[format].dump(data, io)
|
107
|
+
else
|
108
|
+
raise NotImplementedError, "Don't know how to write #{format}."
|
108
109
|
end
|
109
110
|
end
|
110
111
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module NWN::Gff::Struct
|
4
|
+
def to_json(*a)
|
5
|
+
box.to_json(*a)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module NWN::Gff::Field
|
10
|
+
def to_json(*a)
|
11
|
+
box.to_json(*a)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module NWN::Gff::JSON
|
16
|
+
def self.load io
|
17
|
+
json = if io.respond_to?(:to_str)
|
18
|
+
io.to_str
|
19
|
+
elsif io.respond_to?(:to_io)
|
20
|
+
io.to_io.read
|
21
|
+
else
|
22
|
+
io.read
|
23
|
+
end
|
24
|
+
|
25
|
+
NWN::Gff::Struct.unbox!(JSON.parse(json), nil)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.dump struct, io
|
29
|
+
if NWN.setting(:pretty_json)
|
30
|
+
io.puts JSON.pretty_generate(struct)
|
31
|
+
else
|
32
|
+
io.print JSON.generate(struct)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
NWN::Gff.register_format_handler :json, /^json$/, NWN::Gff::JSON
|
data/lib/nwn/key.rb
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
module NWN
|
2
|
+
module Key
|
3
|
+
|
4
|
+
# A Bif object encapsulates an open file handle pointing
|
5
|
+
# to a .bif file. It's contents are indexed on first access,
|
6
|
+
# not on creation by NWN::Key::Key (to speed up things).
|
7
|
+
class Bif
|
8
|
+
|
9
|
+
# The Key object this Bif belongs to.
|
10
|
+
attr_reader :key
|
11
|
+
|
12
|
+
# The IO object pointing to the .bif file.
|
13
|
+
attr_reader :io
|
14
|
+
|
15
|
+
# A hash containing the resources contained. Usually not needed,
|
16
|
+
# accessed by the encapsulating Key object.
|
17
|
+
attr_reader :contained
|
18
|
+
|
19
|
+
def initialize key, io
|
20
|
+
@key = key
|
21
|
+
@io = io
|
22
|
+
|
23
|
+
@contained = {}
|
24
|
+
|
25
|
+
@file_type, @file_version,
|
26
|
+
@var_res_count, @fix_res_count,
|
27
|
+
@var_table_offset =
|
28
|
+
io.e_read(4 + 4 + 3 * 4, "header").unpack("a4 a4 V V V")
|
29
|
+
|
30
|
+
@io.seek(@var_table_offset)
|
31
|
+
data = @io.e_read(@var_res_count * 16, "var res table")
|
32
|
+
i = 0
|
33
|
+
while (x = data[i, 16]) && x.size == 16
|
34
|
+
i += 16
|
35
|
+
id, offset, size, restype = x.unpack("V V V V")
|
36
|
+
id &= 0xfffff
|
37
|
+
@contained[id] = [offset, size, restype]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def has_res_id? id
|
42
|
+
@contained[id] != nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def get_res_id id
|
46
|
+
offset, size, restype = @contained[id]
|
47
|
+
@io.seek(offset)
|
48
|
+
@io.e_read(size, "resource #{id} of type #{restype}")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class Key < NWN::Resources::Container
|
53
|
+
|
54
|
+
# An array of Bif objects contained in this key index.
|
55
|
+
# Not needed to access individual files, use Container#content instead.
|
56
|
+
attr_reader :bif
|
57
|
+
|
58
|
+
attr_reader :file_type
|
59
|
+
attr_reader :file_version
|
60
|
+
attr_reader :day_of_year
|
61
|
+
attr_reader :year
|
62
|
+
|
63
|
+
# Creates a new Key wrapper. The parameters exepected are an
|
64
|
+
# IO object pointing to the .key-file, and the base path in
|
65
|
+
# which your data/.bif files can be found. (This is usually your
|
66
|
+
# NWN directory, NOT the data/ directory).
|
67
|
+
def initialize io, data_path
|
68
|
+
super()
|
69
|
+
|
70
|
+
@root = data_path
|
71
|
+
@bif = []
|
72
|
+
|
73
|
+
@file_type, @file_version,
|
74
|
+
bif_count, key_count,
|
75
|
+
offset_to_file_table, offset_to_key_table,
|
76
|
+
@year, @day_of_year, reserved =
|
77
|
+
io.e_read(8 + (4 * 6) + 32, "header").unpack("A4 A4 VVVVVV a32")
|
78
|
+
|
79
|
+
io.seek(offset_to_file_table)
|
80
|
+
data = io.e_read(12 * bif_count, "bif data")
|
81
|
+
|
82
|
+
# Contains all bifs linked in this key
|
83
|
+
i = 0
|
84
|
+
@file_table = []
|
85
|
+
while (x = data[i, 12]) && x.size == 12
|
86
|
+
i += 12
|
87
|
+
size, name_offset, name_size, drives = x.unpack("VVvv")
|
88
|
+
io.seek(name_offset)
|
89
|
+
name = io.e_read(name_size, "name table").unpack("A*")[0]
|
90
|
+
name.gsub!("\\", "/")
|
91
|
+
name = File.expand_path(@root + "/" + name)
|
92
|
+
|
93
|
+
_io = File.new(name, "r")
|
94
|
+
@bif << Bif.new(self, _io)
|
95
|
+
|
96
|
+
@file_table << [size, name, drives]
|
97
|
+
end
|
98
|
+
|
99
|
+
@key_table = {}
|
100
|
+
io.seek(offset_to_key_table)
|
101
|
+
data = io.e_read(22 * key_count, "key table")
|
102
|
+
i = 0
|
103
|
+
while (x = data[i, 22]) && x.size == 22
|
104
|
+
i += 22
|
105
|
+
resref, res_type, res_id = x.unpack("A16 v V")
|
106
|
+
@key_table[res_id] = [resref, res_type]
|
107
|
+
end
|
108
|
+
|
109
|
+
@fn_to_co = {}
|
110
|
+
@key_table.each {|res_id, (resref, res_type)|
|
111
|
+
bif_index = res_id >> 20
|
112
|
+
bif = @bif[bif_index]
|
113
|
+
id = res_id & 0xfffff
|
114
|
+
bif.contained[id] or fail "#{bif} does not have #{id}"
|
115
|
+
ofs, sz, _rt = bif.contained[id]
|
116
|
+
o = NWN::Resources::ContentObject.new(resref, res_type, bif.io, ofs, sz)
|
117
|
+
if @fn_to_co[o.filename] && @fn_to_co[o.filename][2] < bif_index
|
118
|
+
oo, biff = @fn_to_co[o.filename]
|
119
|
+
NWN.log_debug "#{o.filename} in #{biff.io.inspect} shadowed by file of same name in #{bif.io.inspect}"
|
120
|
+
@content.delete(oo)
|
121
|
+
end
|
122
|
+
@fn_to_co[o.filename] = [o, bif, bif_index]
|
123
|
+
@content << o
|
124
|
+
}
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Get the ContentObject pointing to the given filename.
|
129
|
+
# Raises ENOENT if not mapped.
|
130
|
+
def get_content_object filename
|
131
|
+
filename = filename.downcase
|
132
|
+
ret, bif = @fn_to_co[filename]
|
133
|
+
raise Errno::ENOENT,
|
134
|
+
"No ContentObject with the given filename #{filename.inspect} found." unless
|
135
|
+
ret
|
136
|
+
ret
|
137
|
+
end
|
138
|
+
|
139
|
+
def filenames
|
140
|
+
@fn_to_co.indices
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module NWN::Gff::Kivinen
|
2
|
+
def self.load io
|
3
|
+
raise NotImplementedError, "Reading kivinen not supported"
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.dump struct, io
|
7
|
+
ret = ""
|
8
|
+
kivinen_format struct, $options[:types], nil, nil do |l,v|
|
9
|
+
ret += "%s:\t%s\n" % [l, v]
|
10
|
+
end
|
11
|
+
io.puts ret
|
12
|
+
end
|
13
|
+
|
14
|
+
# Parses +s+ as an arbitary GFF object and yields for each field found,
|
15
|
+
# with the proper prefix.
|
16
|
+
#
|
17
|
+
# [+struct+] The root-struct to dump
|
18
|
+
# [+prefix+] Supply a prefix to add to the output.
|
19
|
+
# [+types_too+] Yield type definitions as well (gffprint.pl -t).
|
20
|
+
# [+add_prefix+] Add a prefix <tt>(unknown type)</tt> of no type information can be derived from the input.
|
21
|
+
# [+file_type+] File type override. If non-null, add a global struct header with the given file type (useful for passing to gffencode.pl)
|
22
|
+
# [+struct_id+] Provide a struct_id override (if printing a struct).
|
23
|
+
def self.kivinen_format struct, types_too = false, add_prefix = true, file_type = nil, struct_id = nil, &block
|
24
|
+
|
25
|
+
if types_too
|
26
|
+
yield("/", "")
|
27
|
+
|
28
|
+
ftype = file_type ? file_type : struct.data_type
|
29
|
+
yield("/ ____file_type", ftype) if ftype
|
30
|
+
yield("/ ____file_version", struct.data_version) if struct.data_version
|
31
|
+
|
32
|
+
yield("/ ____struct_type", struct.struct_id)
|
33
|
+
end
|
34
|
+
|
35
|
+
struct.each_by_flat_path {|path, field|
|
36
|
+
case field
|
37
|
+
when String
|
38
|
+
yield(path, field)
|
39
|
+
|
40
|
+
when NWN::Gff::Struct
|
41
|
+
yield(path + "/", path)
|
42
|
+
yield(path + "/ ____struct_type", field.struct_id)
|
43
|
+
|
44
|
+
when NWN::Gff::Field
|
45
|
+
|
46
|
+
case field.field_type
|
47
|
+
when :list
|
48
|
+
when :struct
|
49
|
+
yield(path + "/", path)
|
50
|
+
yield(path + "/ ____struct_type", field.field_value.struct_id)
|
51
|
+
when :cexolocstr
|
52
|
+
else
|
53
|
+
yield(path, field.field_value)
|
54
|
+
end
|
55
|
+
|
56
|
+
yield(path + ". ____string_ref",field.str_ref) if
|
57
|
+
field.has_str_ref? || field.field_type == :cexolocstr
|
58
|
+
|
59
|
+
yield(path + ". ____type", NWN::Gff::Types.index(field.field_type)) if
|
60
|
+
types_too
|
61
|
+
|
62
|
+
end
|
63
|
+
}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
NWN::Gff.register_format_handler :kivinen, /^k(ivinen)?$/, NWN::Gff::Kivinen, false, true
|
data/lib/nwn/res.rb
CHANGED
@@ -61,18 +61,18 @@ module NWN
|
|
61
61
|
|
62
62
|
# Wraps n ContentObjects; a baseclass for erf/key encapsulation.
|
63
63
|
class Container
|
64
|
+
|
65
|
+
# An array of all ContentObjects indexed by this Container.
|
64
66
|
attr_reader :content
|
65
67
|
|
66
68
|
def initialize
|
67
69
|
@content = []
|
68
70
|
end
|
69
71
|
|
72
|
+
# Returns true if the given filename is contained herein.
|
73
|
+
# Case-insensitive.
|
70
74
|
def has?(filename)
|
71
|
-
|
72
|
-
@content.each {|f|
|
73
|
-
return true if f.filename.downcase == base.downcase
|
74
|
-
}
|
75
|
-
return false
|
75
|
+
filenames.index(filename.downcase) != nil
|
76
76
|
end
|
77
77
|
|
78
78
|
# Add a content object giving a +filename+ and a optional
|
@@ -86,17 +86,18 @@ module NWN
|
|
86
86
|
@content << o
|
87
87
|
end
|
88
88
|
|
89
|
-
# Returns a list of filenames
|
89
|
+
# Returns a list of filenames, all lowercase.
|
90
90
|
def filenames
|
91
|
-
@content.map {|x| x.filename }
|
91
|
+
@content.map {|x| x.filename.downcase }
|
92
92
|
end
|
93
93
|
|
94
94
|
# Get the ContentObject pointing to the given filename.
|
95
95
|
# Raises ENOENT if not mapped.
|
96
96
|
def get_content_object filename
|
97
|
-
|
97
|
+
filename = filename.downcase
|
98
|
+
ret = @content.select {|x| filename == x.filename }
|
98
99
|
raise Errno::ENOENT,
|
99
|
-
"No ContentObject with the given filename found." if
|
100
|
+
"No ContentObject with the given filename #{filename.inspect} found." if
|
100
101
|
ret.size == 0
|
101
102
|
ret[0]
|
102
103
|
end
|
@@ -104,7 +105,7 @@ module NWN
|
|
104
105
|
# Get the contents of the given filename.
|
105
106
|
# Raises ENOENT if not mapped.
|
106
107
|
def get filename
|
107
|
-
get_content_object(filename
|
108
|
+
get_content_object(filename).get
|
108
109
|
end
|
109
110
|
end
|
110
111
|
|
@@ -115,7 +116,10 @@ module NWN
|
|
115
116
|
super()
|
116
117
|
@path = path
|
117
118
|
Dir[path + "/*.*"].each {|x|
|
118
|
-
add_file x
|
119
|
+
begin add_file x
|
120
|
+
rescue ArgumentError => e
|
121
|
+
NWN.log_debug e.to_s
|
122
|
+
end
|
119
123
|
}
|
120
124
|
end
|
121
125
|
end
|
@@ -138,7 +142,7 @@ module NWN
|
|
138
142
|
con.has?(filename) or next
|
139
143
|
return con.get_content_object(filename)
|
140
144
|
}
|
141
|
-
raise Errno::ENOENT, "No ContentObject with the given filename found."
|
145
|
+
raise Errno::ENOENT, "No ContentObject with the given filename #{filename.inspect} found."
|
142
146
|
end
|
143
147
|
|
144
148
|
# Get the contents of the given filename.
|
data/lib/nwn/settings.rb
CHANGED
@@ -1,4 +1,9 @@
|
|
1
|
+
require 'iconv'
|
2
|
+
|
1
3
|
module NWN
|
4
|
+
SETTING_DEFAULT_VALUES = {
|
5
|
+
'NWN_LIB_IN_ENCODING' => 'ISO-8859-1'
|
6
|
+
}
|
2
7
|
|
3
8
|
# This writes a internal warnings and debug messages to stderr.
|
4
9
|
#
|
@@ -28,9 +33,12 @@ module NWN
|
|
28
33
|
ENV[name] = value.to_s if value != :_invalid_
|
29
34
|
ret
|
30
35
|
else
|
31
|
-
ENV[name] == "0" ? false : ENV[name]
|
36
|
+
ENV[name] == "0" ? false : (ENV[name] || SETTING_DEFAULT_VALUES[name])
|
32
37
|
end
|
33
38
|
end
|
39
|
+
|
40
|
+
IconvGffToNative = proc { Iconv.new('utf-8', NWN.setting(:in_encoding)) }
|
41
|
+
IconvNativeToGff = proc { Iconv.new(NWN.setting(:in_encoding), 'utf-8') }
|
34
42
|
end
|
35
43
|
|
36
44
|
NWN::TwoDA::Cache.setup NWN.setting("2da_location") if
|
@@ -1,8 +1,20 @@
|
|
1
1
|
# This file contains all YAML-specific loading and dumping code.
|
2
2
|
require 'yaml'
|
3
3
|
|
4
|
-
|
5
|
-
|
4
|
+
class NWN::Gff::YAML
|
5
|
+
# These field types can never be inlined in YAML.
|
6
|
+
NonInlineableFields = [:struct, :list, :cexolocstr]
|
7
|
+
|
8
|
+
# See http://www.taguri.org/ for the exact meaning of this.
|
9
|
+
Domain = "nwn-lib.elv.es,2008-12"
|
10
|
+
|
11
|
+
def self.load io
|
12
|
+
YAML.load(io)
|
13
|
+
end
|
14
|
+
def self.dump data, io
|
15
|
+
io.puts data.to_yaml
|
16
|
+
end
|
17
|
+
end
|
6
18
|
|
7
19
|
#:stopdoc:
|
8
20
|
class Array
|
@@ -34,7 +46,7 @@ end
|
|
34
46
|
|
35
47
|
module NWN::Gff::Struct
|
36
48
|
def to_yaml_type
|
37
|
-
"!#{NWN::
|
49
|
+
"!#{NWN::Gff::YAML::Domain}/struct"
|
38
50
|
end
|
39
51
|
|
40
52
|
def to_yaml(opts = {})
|
@@ -43,7 +55,7 @@ module NWN::Gff::Struct
|
|
43
55
|
# Inline certain structs that are small enough.
|
44
56
|
map.style = :inline if self.size <= 1 &&
|
45
57
|
self.values.select {|x|
|
46
|
-
NWN::Gff::
|
58
|
+
NWN::Gff::YAML::NonInlineableFields.index(x['type'])
|
47
59
|
}.size == 0
|
48
60
|
|
49
61
|
map.add('__' + 'data_type', @data_type) if @data_type
|
@@ -63,7 +75,7 @@ module NWN::Gff::Field
|
|
63
75
|
def to_yaml(opts = {})
|
64
76
|
YAML::quick_emit(nil, opts) do |out|
|
65
77
|
out.map(taguri, to_yaml_style) do |map|
|
66
|
-
map.style = :inline unless NWN::Gff::
|
78
|
+
map.style = :inline unless NWN::Gff::YAML::NonInlineableFields.index(self['type'])
|
67
79
|
map.add('type', self['type'])
|
68
80
|
map.add('str_ref', self['str_ref']) if has_str_ref?
|
69
81
|
map.add('value', self['value'])
|
@@ -73,7 +85,7 @@ module NWN::Gff::Field
|
|
73
85
|
end
|
74
86
|
|
75
87
|
# This parses the struct and extends all fields with their proper type.
|
76
|
-
YAML.add_domain_type(NWN::
|
88
|
+
YAML.add_domain_type(NWN::Gff::YAML::Domain,'struct') {|t,hash|
|
77
89
|
struct = {}
|
78
90
|
struct.extend(NWN::Gff::Struct)
|
79
91
|
|
@@ -101,3 +113,5 @@ YAML.add_domain_type(NWN::YAML_DOMAIN,'struct') {|t,hash|
|
|
101
113
|
|
102
114
|
struct
|
103
115
|
}
|
116
|
+
|
117
|
+
NWN::Gff.register_format_handler :yaml, /^(y|yml|yaml)$/, NWN::Gff::YAML
|
data/spec/bin_gff_spec.rb
CHANGED
@@ -3,10 +3,10 @@ require File.join(File.dirname(__FILE__), 'spec_helper')
|
|
3
3
|
describe "nwn-gff" do
|
4
4
|
it_should_behave_like "bin helper"
|
5
5
|
|
6
|
-
NWN::Gff::
|
6
|
+
NWN::Gff::InputFormats.each do |in_format, handler|
|
7
7
|
inf = in_format.to_s
|
8
8
|
|
9
|
-
NWN::Gff::
|
9
|
+
NWN::Gff::OutputFormats.each do |out_format, handler|
|
10
10
|
otf = out_format.to_s
|
11
11
|
|
12
12
|
it "converts #{inf} to #{otf}" do
|
data/spec/erf_spec.rb
CHANGED
@@ -30,6 +30,13 @@ describe "Erf::Erf", :shared => true do
|
|
30
30
|
wellformed_verify @erf
|
31
31
|
end
|
32
32
|
|
33
|
+
it "sets the correct default parameters" do
|
34
|
+
t = Erf::Erf.new
|
35
|
+
t.year.should == Time.now.year - 1900
|
36
|
+
t.day_of_year.should == Time.now.yday
|
37
|
+
t.content.size.should == 0
|
38
|
+
end
|
39
|
+
|
33
40
|
it "reproduces correct ERF binary data" do
|
34
41
|
t = Erf::Erf.new(StringIO.new @erf)
|
35
42
|
io = StringIO.new
|
data/spec/key_spec.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe "Key::Key", :shared => true do
|
4
|
+
def wellformed_verify binary
|
5
|
+
t = Key::Key.new(StringIO.new(binary), Dir.tmpdir)
|
6
|
+
|
7
|
+
t.file_type.should == "KEY"
|
8
|
+
t.file_version.should == "V1"
|
9
|
+
t.content.size.should == 2
|
10
|
+
t.content[0].resref.should == "abcdef"
|
11
|
+
t.content[1].resref.should == "123456"
|
12
|
+
t.content[0].get.should == "abcdefghij"
|
13
|
+
t.content[1].get.should == "0123456789"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should work" do
|
17
|
+
wellformed_verify @key
|
18
|
+
end
|
19
|
+
|
20
|
+
before do
|
21
|
+
@bif0 = File.join(Dir.tmpdir, "bif0.bif")
|
22
|
+
File.open(@bif0, "w") do |f|
|
23
|
+
f.write(WELLFORMED_BIF_0)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
after do
|
28
|
+
File.unlink(@bif0)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "Key V1.0" do
|
33
|
+
before do
|
34
|
+
@key = WELLFORMED_KEY.dup
|
35
|
+
end
|
36
|
+
|
37
|
+
it_should_behave_like "Key::Key"
|
38
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -92,6 +92,36 @@ WELLFORMED_TLK = ([
|
|
92
92
|
"1", "22", "333", "4444"
|
93
93
|
].join("")).freeze
|
94
94
|
|
95
|
+
WELLFORMED_BIF_0 = ([
|
96
|
+
"BIFF", "V1",
|
97
|
+
var_res_count = 2,
|
98
|
+
fix_res_count = 0,
|
99
|
+
var_table_offset = 20
|
100
|
+
].pack("a4 a4 V V V") + [
|
101
|
+
id0 = 124, var_table_offset + (16 * var_res_count), size0 = 10, type0 = 0,
|
102
|
+
id1 = 125, var_table_offset + (16 * var_res_count) + size0, size1 = 10, type1 = 0,
|
103
|
+
].pack("VVVV VVVV") + [
|
104
|
+
"abcdefghij", "0123456789"
|
105
|
+
].pack("a* a*")
|
106
|
+
).freeze
|
107
|
+
|
108
|
+
WELLFORMED_KEY = ([
|
109
|
+
"KEY", "V1",
|
110
|
+
bif_count = 1, key_count = 2,
|
111
|
+
offset_to_file_table = 8 + (4 * 6) + 32,
|
112
|
+
offset_to_key_table = offset_to_file_table + bif_count * 12 + 8,
|
113
|
+
100, 126, ""
|
114
|
+
].pack("a4 a4 VVVVVV a32") + [ # file table containing bifs
|
115
|
+
bifsize = WELLFORMED_BIF_0.size, bifname0offset = offset_to_file_table + 12, fnsize = 8, drives = 0,
|
116
|
+
].pack("VVvv") + [ #filename table
|
117
|
+
"bif0.bif"
|
118
|
+
].pack("a*") + [ # key table
|
119
|
+
"abcdef", 0, 124,
|
120
|
+
"123456", 0, 125
|
121
|
+
].pack("a16 v V a16 v V")
|
122
|
+
).freeze
|
123
|
+
|
124
|
+
|
95
125
|
TWODA_WELLFORMED = <<-EOT
|
96
126
|
2DA V2.0
|
97
127
|
|
@@ -172,7 +202,7 @@ describe "bin helper", :shared => true do
|
|
172
202
|
begin
|
173
203
|
Dir.chdir(@tmp)
|
174
204
|
Open3.popen3(
|
175
|
-
"ruby", "-I#{incl}",
|
205
|
+
"ruby", "-rubygems", "-I#{incl}",
|
176
206
|
binary,
|
177
207
|
*va
|
178
208
|
) do |i,o,e|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nwn-lib
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bernhard Stoeckner
|
@@ -9,10 +9,19 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-11-15 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
|
-
dependencies:
|
15
|
-
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: json
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.1.9
|
24
|
+
version:
|
16
25
|
description: a ruby library for accessing Neverwinter Nights resource files
|
17
26
|
email: elven@swordcoast.net
|
18
27
|
executables:
|
@@ -36,49 +45,50 @@ files:
|
|
36
45
|
- CHANGELOG
|
37
46
|
- README
|
38
47
|
- Rakefile
|
39
|
-
- bin/nwn-dsl
|
40
48
|
- bin/nwn-gff
|
41
|
-
- bin/nwn-
|
49
|
+
- bin/nwn-dsl
|
42
50
|
- bin/nwn-erf
|
43
|
-
-
|
51
|
+
- bin/nwn-irb
|
52
|
+
- spec/bin_dsl_spec.rb
|
44
53
|
- spec/gff_spec.rb
|
45
|
-
- spec/
|
46
|
-
- spec/
|
47
|
-
- spec/
|
54
|
+
- spec/key_spec.rb
|
55
|
+
- spec/bin_gff_spec.rb
|
56
|
+
- spec/bin_erf_spec.rb
|
48
57
|
- spec/field_spec.rb
|
49
58
|
- spec/cexolocstr_spec.rb
|
59
|
+
- spec/erf_spec.rb
|
50
60
|
- spec/struct_spec.rb
|
51
|
-
- spec/
|
61
|
+
- spec/res_spec.rb
|
52
62
|
- spec/tlk_spec.rb
|
53
|
-
- spec/bin_dsl_spec.rb
|
54
|
-
- spec/bin_gff_spec.rb
|
55
|
-
- spec/rcov.opts
|
56
|
-
- spec/bin_erf_spec.rb
|
57
63
|
- spec/twoda_spec.rb
|
58
|
-
-
|
59
|
-
-
|
60
|
-
- lib/nwn/twoda.rb
|
64
|
+
- spec/spec_helper.rb
|
65
|
+
- spec/wellformed_gff.bic
|
61
66
|
- lib/nwn/erf.rb
|
62
|
-
- lib/nwn/
|
63
|
-
- lib/nwn/gff.rb
|
64
|
-
- lib/nwn/tlk.rb
|
67
|
+
- lib/nwn/json_support.rb
|
65
68
|
- lib/nwn/io.rb
|
66
|
-
- lib/nwn/
|
67
|
-
- lib/nwn/
|
69
|
+
- lib/nwn/settings.rb
|
70
|
+
- lib/nwn/key.rb
|
68
71
|
- lib/nwn/all.rb
|
69
|
-
- lib/nwn/
|
70
|
-
- lib/nwn/
|
71
|
-
- lib/nwn/
|
72
|
+
- lib/nwn/res.rb
|
73
|
+
- lib/nwn/twoda.rb
|
74
|
+
- lib/nwn/tlk.rb
|
75
|
+
- lib/nwn/gff.rb
|
76
|
+
- lib/nwn/yaml_support.rb
|
77
|
+
- lib/nwn/scripting.rb
|
78
|
+
- lib/nwn/kivinen_support.rb
|
79
|
+
- lib/nwn/gff/writer.rb
|
72
80
|
- lib/nwn/gff/struct.rb
|
73
81
|
- lib/nwn/gff/list.rb
|
74
|
-
- lib/nwn/gff/
|
82
|
+
- lib/nwn/gff/reader.rb
|
83
|
+
- lib/nwn/gff/cexolocstr.rb
|
84
|
+
- lib/nwn/gff/field.rb
|
75
85
|
- tools/verify.sh
|
76
86
|
- tools/migrate_03x_to_04x.sh
|
77
87
|
- scripts/truncate_floats.rb
|
78
|
-
- scripts/reformat_2da
|
79
88
|
- scripts/clean_locstrs.rb
|
80
|
-
- scripts/extract_all_items.rb
|
81
89
|
- scripts/debug_check_objid.rb
|
90
|
+
- scripts/extract_all_items.rb
|
91
|
+
- scripts/reformat_2da
|
82
92
|
- BINARIES
|
83
93
|
- HOWTO
|
84
94
|
- SCRIPTING
|
@@ -116,7 +126,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
116
126
|
requirements: []
|
117
127
|
|
118
128
|
rubyforge_project: nwn-lib
|
119
|
-
rubygems_version: 1.3.
|
129
|
+
rubygems_version: 1.3.5
|
120
130
|
signing_key:
|
121
131
|
specification_version: 3
|
122
132
|
summary: a ruby library for accessing Neverwinter Nights resource files
|
data/lib/nwn/kivinen.rb
DELETED
@@ -1,55 +0,0 @@
|
|
1
|
-
module NWN::Gff::Struct
|
2
|
-
|
3
|
-
# yield (key, value) for each element, recursing into substructs.
|
4
|
-
|
5
|
-
# Parses +s+ as an arbitary GFF object and yields for each field found,
|
6
|
-
# with the proper prefix.
|
7
|
-
#
|
8
|
-
# [+prefix+] Supply a prefix to add to the output.
|
9
|
-
# [+types_too+] Yield type definitions as well (gffprint.pl -t).
|
10
|
-
# [+add_prefix+] Add a prefix <tt>(unknown type)</tt> of no type information can be derived from the input.
|
11
|
-
# [+file_type+] File type override. If non-null, add a global struct header with the given file type (useful for passing to gffencode.pl)
|
12
|
-
# [+struct_id+] Provide a struct_id override (if printing a struct).
|
13
|
-
def kivinen_format types_too = false, add_prefix = true, file_type = nil, struct_id = nil, &block
|
14
|
-
|
15
|
-
if types_too
|
16
|
-
yield("/", "")
|
17
|
-
|
18
|
-
ftype = file_type ? file_type : self.data_type
|
19
|
-
yield("/ ____file_type", ftype) if ftype
|
20
|
-
yield("/ ____file_version", self.data_version) if self.data_version
|
21
|
-
|
22
|
-
yield("/ ____struct_type", self.struct_id)
|
23
|
-
end
|
24
|
-
|
25
|
-
self.each_by_flat_path {|path, field|
|
26
|
-
case field
|
27
|
-
when String
|
28
|
-
yield(path, field)
|
29
|
-
|
30
|
-
when NWN::Gff::Struct
|
31
|
-
yield(path + "/", path)
|
32
|
-
yield(path + "/ ____struct_type", field.struct_id)
|
33
|
-
|
34
|
-
when NWN::Gff::Field
|
35
|
-
|
36
|
-
case field.field_type
|
37
|
-
when :list
|
38
|
-
when :struct
|
39
|
-
yield(path + "/", path)
|
40
|
-
yield(path + "/ ____struct_type", field.field_value.struct_id)
|
41
|
-
when :cexolocstr
|
42
|
-
else
|
43
|
-
yield(path, field.field_value)
|
44
|
-
end
|
45
|
-
|
46
|
-
yield(path + ". ____string_ref",field.str_ref) if
|
47
|
-
field.has_str_ref? || field.field_type == :cexolocstr
|
48
|
-
|
49
|
-
yield(path + ". ____type", NWN::Gff::Types.index(field.field_type)) if
|
50
|
-
types_too
|
51
|
-
|
52
|
-
end
|
53
|
-
}
|
54
|
-
end
|
55
|
-
end
|
data/spec/rcov.opts
DELETED
File without changes
|
data/spec/spec.opts
DELETED
File without changes
|