nwn-lib 0.4.9 → 0.4.10
Sign up to get free protection for your applications and to get access to all the features.
- 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
|