wolftrans 0.0.2 → 0.1.0

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.
@@ -1,44 +1,42 @@
1
1
  module WolfRpg
2
2
  class Database
3
- attr_accessor :name #DEBUG
4
3
  attr_accessor :types
5
4
 
6
5
  def initialize(project_filename, dat_filename)
7
- @name = File.basename(project_filename, '.*')
8
- File.open(project_filename, 'rb') do |file|
9
- @types = Array.new(IO.read_int(file))
6
+ FileCoder.open(project_filename, :read) do |coder|
7
+ @types = Array.new(coder.read_int)
10
8
  @types.each_index do |i|
11
- @types[i] = Type.new(file)
9
+ @types[i] = Type.new(coder)
12
10
  end
13
11
  end
14
- File.open(dat_filename, 'rb') do |file|
15
- IO.verify(file, DAT_MAGIC_NUMBER)
16
- num_types = IO.read_int(file)
12
+ FileCoder.open(dat_filename, :read) do |coder|
13
+ coder.verify(DAT_MAGIC_NUMBER)
14
+ num_types = coder.read_int
17
15
  unless num_types == @types.size
18
16
  raise "database project and dat Type count mismatch (#{@types.size} vs. #{num_types})"
19
17
  end
20
18
  @types.each do |type|
21
- type.read_dat(file)
19
+ type.read_dat(coder)
22
20
  end
23
- if IO.read_byte(file) != 0xC1
21
+ if coder.read_byte != 0xC1
24
22
  STDERR.puts "warning: no C1 terminator at the end of '#{dat_filename}'"
25
23
  end
26
24
  end
27
25
 
28
26
  def dump(project_filename, dat_filename)
29
- File.open(project_filename, 'wb') do |file|
30
- IO.write_int(file, @types.size)
27
+ FileCoder.open(project_filename, :write) do |coder|
28
+ coder.write_int(@types.size)
31
29
  @types.each do |type|
32
- type.dump_project(file)
30
+ type.dump_project(coder)
33
31
  end
34
32
  end
35
- File.open(dat_filename, 'wb') do |file|
36
- IO.write(file, DAT_MAGIC_NUMBER)
37
- IO.write_int(file, @types.size)
33
+ FileCoder.open(dat_filename, :write) do |coder|
34
+ coder.write(DAT_MAGIC_NUMBER)
35
+ coder.write_int(@types.size)
38
36
  @types.each do |type|
39
- type.dump_dat(file)
37
+ type.dump_dat(coder)
40
38
  end
41
- IO.write_byte(file, 0xC1)
39
+ coder.write_byte(0xC1)
42
40
  end
43
41
  end
44
42
 
@@ -47,7 +45,7 @@ module WolfRpg
47
45
  type.data.each_with_index do |datum, datum_index|
48
46
  datum.each_translatable do |value, field|
49
47
  next unless value =~ needle
50
- puts "DB:#{@name}/[#{type_index}]#{type.name}/[#{datum_index}]#{datum.name}/[#{field.index}]#{field.name}"
48
+ puts "DB:[#{type_index}]#{type.name}/[#{datum_index}]#{datum.name}/[#{field.index}]#{field.name}"
51
49
  puts "\t" + value
52
50
  end
53
51
  end
@@ -63,122 +61,122 @@ module WolfRpg
63
61
  attr_accessor :unknown1
64
62
 
65
63
  # Initialize from project file IO
66
- def initialize(file)
67
- @name = IO.read_string(file)
68
- @fields = Array.new(IO.read_int(file))
64
+ def initialize(coder)
65
+ @name = coder.read_string
66
+ @fields = Array.new(coder.read_int)
69
67
  @fields.each_index do |i|
70
- @fields[i] = Field.new(file)
68
+ @fields[i] = Field.new(coder)
71
69
  end
72
- @data = Array.new(IO.read_int(file))
70
+ @data = Array.new(coder.read_int)
73
71
  @data.each_index do |i|
74
- @data[i] = Data.new(file)
72
+ @data[i] = Data.new(coder)
75
73
  end
76
- @description = IO.read_string(file)
74
+ @description = coder.read_string
77
75
 
78
76
  # Add misc data to fields. It's separated for some reason.
79
77
 
80
78
  # This appears to always be 0x64, but save it anyway
81
- @field_type_list_size = IO.read_int(file)
79
+ @field_type_list_size = coder.read_int
82
80
  index = 0
83
81
  while index < @fields.size
84
- @fields[index].type = IO.read_byte(file)
82
+ @fields[index].type = coder.read_byte
85
83
  index += 1
86
84
  end
87
- file.seek(@field_type_list_size - index, :CUR)
85
+ coder.skip(@field_type_list_size - index)
88
86
 
89
- IO.read_int(file).times do |i|
90
- @fields[i].unknown1 = IO.read_string(file)
87
+ coder.read_int.times do |i|
88
+ @fields[i].unknown1 = coder.read_string
91
89
  end
92
- IO.read_int(file).times do |i|
93
- @fields[i].string_args = Array.new(IO.read_int(file))
90
+ coder.read_int.times do |i|
91
+ @fields[i].string_args = Array.new(coder.read_int)
94
92
  @fields[i].string_args.each_index do |j|
95
- @fields[i].string_args[j] = IO.read_string(file)
93
+ @fields[i].string_args[j] = coder.read_string
96
94
  end
97
95
  end
98
- IO.read_int(file).times do |i|
99
- @fields[i].args = Array.new(IO.read_int(file))
96
+ coder.read_int.times do |i|
97
+ @fields[i].args = Array.new(coder.read_int)
100
98
  @fields[i].args.each_index do |j|
101
- @fields[i].args[j] = IO.read_int(file)
99
+ @fields[i].args[j] = coder.read_int
102
100
  end
103
101
  end
104
- IO.read_int(file).times do |i|
105
- @fields[i].default_value = IO.read_int(file)
102
+ coder.read_int.times do |i|
103
+ @fields[i].default_value = coder.read_int
106
104
  end
107
105
  end
108
106
 
109
- def dump_project(file)
110
- IO.write_string(file, @name)
111
- IO.write_int(file, @fields.size)
107
+ def dump_project(coder)
108
+ coder.write_string(@name)
109
+ coder.write_int(@fields.size)
112
110
  @fields.each do |field|
113
- field.dump_project(file)
111
+ field.dump_project(coder)
114
112
  end
115
- IO.write_int(file, @data.size)
113
+ coder.write_int(@data.size)
116
114
  @data.each do |datum|
117
- datum.dump_project(file)
115
+ datum.dump_project(coder)
118
116
  end
119
- IO.write_string(file, @description)
117
+ coder.write_string(@description)
120
118
 
121
119
  # Dump misc field data
122
- IO.write_int(file, @field_type_list_size)
120
+ coder.write_int(@field_type_list_size)
123
121
  index = 0
124
122
  while index < @fields.size
125
- IO.write_byte(file, @fields[index].type)
123
+ coder.write_byte(@fields[index].type)
126
124
  index += 1
127
125
  end
128
126
  while index < @field_type_list_size
129
- IO.write_byte(file, 0)
127
+ coder.write_byte(0)
130
128
  index += 1
131
129
  end
132
- IO.write_int(file, @fields.size)
130
+ coder.write_int(@fields.size)
133
131
  @fields.each do |field|
134
- IO.write_string(file, field.unknown1)
132
+ coder.write_string(field.unknown1)
135
133
  end
136
- IO.write_int(file, @fields.size)
134
+ coder.write_int(@fields.size)
137
135
  @fields.each do |field|
138
- IO.write_int(file, field.string_args.size)
136
+ coder.write_int(field.string_args.size)
139
137
  field.string_args.each do |arg|
140
- IO.write_string(file, arg)
138
+ coder.write_string(arg)
141
139
  end
142
140
  end
143
- IO.write_int(file, @fields.size)
141
+ coder.write_int(@fields.size)
144
142
  @fields.each do |field|
145
- IO.write_int(file, field.args.size)
143
+ coder.write_int(field.args.size)
146
144
  field.args.each do |arg|
147
- IO.write_int(file, arg)
145
+ coder.write_int(arg)
148
146
  end
149
147
  end
150
- IO.write_int(file, @fields.size)
148
+ coder.write_int(@fields.size)
151
149
  @fields.each do |field|
152
- IO.write_int(file, field.default_value)
150
+ coder.write_int(field.default_value)
153
151
  end
154
152
  end
155
153
 
156
154
  # Read the rest of the data from the dat file
157
- def read_dat(file)
158
- IO.verify(file, DAT_TYPE_SEPARATOR)
159
- @unknown1 = IO.read_int(file)
160
- fields_size = IO.read_int(file)
155
+ def read_dat(coder)
156
+ coder.verify(DAT_TYPE_SEPARATOR)
157
+ @unknown1 = coder.read_int
158
+ fields_size = coder.read_int
161
159
  @fields = @fields[0, fields_size] if fields_size != @fields.size
162
160
  @fields.each do |field|
163
- field.read_dat(file)
161
+ field.read_dat(coder)
164
162
  end
165
- data_size = IO.read_int(file)
163
+ data_size = coder.read_int
166
164
  @data = @data[0, data_size] if data_size != @data.size
167
165
  @data.each do |datum|
168
- datum.read_dat(file, @fields)
166
+ datum.read_dat(coder, @fields)
169
167
  end
170
168
  end
171
169
 
172
- def dump_dat(file)
173
- IO.write(file, DAT_TYPE_SEPARATOR)
174
- IO.write_int(file, @unknown1)
175
- IO.write_int(file, @fields.size)
170
+ def dump_dat(coder)
171
+ coder.write(DAT_TYPE_SEPARATOR)
172
+ coder.write_int(@unknown1)
173
+ coder.write_int(@fields.size)
176
174
  @fields.each do |field|
177
- field.dump_dat(file)
175
+ field.dump_dat(coder)
178
176
  end
179
- IO.write_int(file, @data.size)
177
+ coder.write_int(@data.size)
180
178
  @data.each do |datum|
181
- datum.dump_dat(file)
179
+ datum.dump_dat(coder)
182
180
  end
183
181
  end
184
182
  end
@@ -193,20 +191,20 @@ module WolfRpg
193
191
  attr_accessor :default_value
194
192
  attr_accessor :indexinfo
195
193
 
196
- def initialize(file)
197
- @name = IO.read_string(file)
194
+ def initialize(coder)
195
+ @name = coder.read_string
198
196
  end
199
197
 
200
- def dump_project(file)
201
- IO.write_string(file, @name)
198
+ def dump_project(coder)
199
+ coder.write_string(@name)
202
200
  end
203
201
 
204
- def read_dat(file)
205
- @indexinfo = IO.read_int(file)
202
+ def read_dat(coder)
203
+ @indexinfo = coder.read_int
206
204
  end
207
205
 
208
- def dump_dat(file)
209
- IO.write_int(file, @indexinfo)
206
+ def dump_dat(coder)
207
+ coder.write_int(@indexinfo)
210
208
  end
211
209
 
212
210
  def string?
@@ -234,33 +232,33 @@ module WolfRpg
234
232
  attr_accessor :int_values
235
233
  attr_accessor :string_values
236
234
 
237
- def initialize(file)
238
- @name = IO.read_string(file)
235
+ def initialize(coder)
236
+ @name = coder.read_string
239
237
  end
240
238
 
241
- def dump_project(file)
242
- IO.write_string(file, @name)
239
+ def dump_project(coder)
240
+ coder.write_string(@name)
243
241
  end
244
242
 
245
- def read_dat(file, fields)
243
+ def read_dat(coder, fields)
246
244
  @fields = fields
247
245
  @int_values = Array.new(fields.select(&:int?).size)
248
246
  @string_values = Array.new(fields.select(&:string?).size)
249
247
 
250
248
  @int_values.each_index do |i|
251
- @int_values[i] = IO.read_int(file)
249
+ @int_values[i] = coder.read_int
252
250
  end
253
251
  @string_values.each_index do |i|
254
- @string_values[i] = IO.read_string(file)
252
+ @string_values[i] = coder.read_string
255
253
  end
256
254
  end
257
255
 
258
- def dump_dat(file)
256
+ def dump_dat(coder)
259
257
  @int_values.each do |i|
260
- IO.write_int(file, i)
258
+ coder.write_int(i)
261
259
  end
262
260
  @string_values.each do |i|
263
- IO.write_string(file, i)
261
+ coder.write_string(i)
264
262
  end
265
263
  end
266
264
 
@@ -0,0 +1,60 @@
1
+ module WolfRpg
2
+ # Find strings in binary string and return them inline in an array
3
+ def self.parse_strings(data)
4
+ result = []
5
+
6
+ # Scan for strings
7
+ str_len = 0
8
+ can_seek_multibyte = false
9
+ data.each_byte.with_index do |c, i|
10
+ result << c
11
+
12
+ if can_seek_multibyte
13
+ if (c >= 0x40 && c <= 0x9E && c != 0x7F) ||
14
+ (c >= 0xA0 && c <= 0xFC)
15
+ str_len += 1
16
+ next
17
+ end
18
+ end
19
+ if (c >= 0x81 && c <= 0x84) || (c >= 0x87 && c <= 0x9F) ||
20
+ (c >= 0xE0 && c <= 0xEA) || (c >= 0xED && c <= 0xEE) ||
21
+ (c >= 0xFA && c <= 0xFC)
22
+ # head of multibyte character
23
+ str_len += 1
24
+ can_seek_multibyte = true
25
+ next
26
+ end
27
+
28
+ can_seek_multibyte = false
29
+ if c == 0x0A || c == 0x0D || c == 0x09 || # newline, CR, tab
30
+ (c >= 0x20 && c <= 0x7E) || # printable ascii
31
+ (c >= 0xA1 && c <= 0xDF) # half-width katakana
32
+ str_len += 1
33
+ else
34
+ str = ''
35
+ if c == 0 && str_len > 0
36
+ # End of the string. Make sure it's valid by checking for
37
+ # a length prefix.
38
+ str_len_check = data[i - str_len - 4,4].unpack('V').first
39
+ if str_len_check == str_len + 1
40
+ begin
41
+ str = data[i - str_len,str_len].encode(Encoding::UTF_8, Encoding::WINDOWS_31J)
42
+ rescue
43
+ #do nothing
44
+ end
45
+ end
46
+ end
47
+
48
+ # Either append the string or hex bytes
49
+ unless str.empty?
50
+ result.slice!(-(4 + str_len + 1)..-1)
51
+ result << str
52
+ end
53
+
54
+ # Reset str length
55
+ str_len = 0
56
+ end
57
+ end
58
+ return result
59
+ end
60
+ end
@@ -0,0 +1,114 @@
1
+ module WolfRpg
2
+ class FileCoder
3
+ attr_reader :io
4
+
5
+ def initialize(io)
6
+ @io = io
7
+ end
8
+
9
+ def self.open(filename, mode)
10
+ case mode
11
+ when :read
12
+ file = File.open(filename, 'rb')
13
+ when :write
14
+ file = File.open(filename, 'wb')
15
+ end
16
+ coder = FileCoder.new(file)
17
+ if block_given?
18
+ begin
19
+ yield coder
20
+ ensure
21
+ coder.close
22
+ end
23
+ end
24
+ return coder
25
+ end
26
+
27
+ ########
28
+ # Read #
29
+ def read(size = nil)
30
+ if size
31
+ @io.readpartial(size)
32
+ else
33
+ @io.read
34
+ end
35
+ end
36
+
37
+ def read_byte
38
+ @io.readpartial(1).ord
39
+ end
40
+
41
+ def read_int
42
+ @io.readpartial(4).unpack('l<').first
43
+ end
44
+
45
+ def read_string
46
+ size = read_int
47
+ raise "got a string of size <= 0" unless size > 0
48
+ str = read(size - 1).encode(Encoding::UTF_8, Encoding::WINDOWS_31J)
49
+ raise "string not null-terminated" unless read_byte == 0
50
+ return str
51
+ end
52
+
53
+ def verify(data)
54
+ got = read(data.length)
55
+ if got != data
56
+ raise "could not verify magic data (expecting #{data.unpack('C*')}, got #{got.unpack('C*')})"
57
+ end
58
+ end
59
+
60
+ def skip(size)
61
+ @io.seek(size, IO::SEEK_CUR)
62
+ end
63
+
64
+ def dump(size)
65
+ size.times do |i|
66
+ print " %02x" % read_byte
67
+ end
68
+ print "\n"
69
+ end
70
+
71
+ def dump_until(pattern)
72
+ str = ''.force_encoding('BINARY')
73
+ until str.end_with? pattern
74
+ str << @io.readpartial(1)
75
+ end
76
+ str[0...-pattern.bytesize].each_byte do |byte|
77
+ print " %02x" % byte
78
+ end
79
+ print "\n"
80
+ end
81
+
82
+ #########
83
+ # Write #
84
+ def write(data)
85
+ @io.write(data)
86
+ end
87
+ def write_byte(data)
88
+ @io.write(data.chr)
89
+ end
90
+ def write_int(data)
91
+ @io.write([data].pack('l<'))
92
+ end
93
+ def write_string(str)
94
+ str = str.encode(Encoding::WINDOWS_31J, Encoding::UTF_8)
95
+ write_int(str.bytesize + 1)
96
+ write(str)
97
+ write_byte(0)
98
+ end
99
+
100
+ #########
101
+ # Other #
102
+ def close
103
+ @io.close
104
+ end
105
+
106
+ def eof?
107
+ @io.eof?
108
+ end
109
+
110
+ def tell
111
+ @io.tell
112
+ end
113
+ end
114
+ end