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.
- checksums.yaml +4 -4
- data/Rakefile +22 -2
- data/bin/wolftrans +2 -1
- data/lib/wolfrpg.rb +6 -60
- data/lib/wolfrpg/command.rb +30 -30
- data/lib/wolfrpg/common_events.rb +69 -71
- data/lib/wolfrpg/database.rb +89 -91
- data/lib/wolfrpg/debug.rb +60 -0
- data/lib/wolfrpg/filecoder.rb +114 -0
- data/lib/wolfrpg/game_dat.rb +93 -31
- data/lib/wolfrpg/map.rb +81 -85
- data/lib/wolfrpg/route.rb +10 -10
- data/lib/wolftrans.rb +4 -64
- data/lib/wolftrans/context.rb +3 -3
- data/lib/wolftrans/debug.rb +35 -0
- data/lib/wolftrans/patch_data.rb +10 -8
- data/lib/wolftrans/patch_text.rb +2 -2
- data/lib/wolftrans/util.rb +29 -0
- data/lib/wolftrans/version.rb +3 -0
- data/test/test_wolftrans.rb +2 -1
- data/wolftrans.gemspec +6 -4
- metadata +8 -4
- data/lib/wolfrpg/io.rb +0 -63
data/lib/wolfrpg/database.rb
CHANGED
@@ -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
|
-
|
8
|
-
|
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(
|
9
|
+
@types[i] = Type.new(coder)
|
12
10
|
end
|
13
11
|
end
|
14
|
-
|
15
|
-
|
16
|
-
num_types =
|
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(
|
19
|
+
type.read_dat(coder)
|
22
20
|
end
|
23
|
-
if
|
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
|
-
|
30
|
-
|
27
|
+
FileCoder.open(project_filename, :write) do |coder|
|
28
|
+
coder.write_int(@types.size)
|
31
29
|
@types.each do |type|
|
32
|
-
type.dump_project(
|
30
|
+
type.dump_project(coder)
|
33
31
|
end
|
34
32
|
end
|
35
|
-
|
36
|
-
|
37
|
-
|
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(
|
37
|
+
type.dump_dat(coder)
|
40
38
|
end
|
41
|
-
|
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
|
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(
|
67
|
-
@name =
|
68
|
-
@fields = Array.new(
|
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(
|
68
|
+
@fields[i] = Field.new(coder)
|
71
69
|
end
|
72
|
-
@data = Array.new(
|
70
|
+
@data = Array.new(coder.read_int)
|
73
71
|
@data.each_index do |i|
|
74
|
-
@data[i] = Data.new(
|
72
|
+
@data[i] = Data.new(coder)
|
75
73
|
end
|
76
|
-
@description =
|
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 =
|
79
|
+
@field_type_list_size = coder.read_int
|
82
80
|
index = 0
|
83
81
|
while index < @fields.size
|
84
|
-
@fields[index].type =
|
82
|
+
@fields[index].type = coder.read_byte
|
85
83
|
index += 1
|
86
84
|
end
|
87
|
-
|
85
|
+
coder.skip(@field_type_list_size - index)
|
88
86
|
|
89
|
-
|
90
|
-
@fields[i].unknown1 =
|
87
|
+
coder.read_int.times do |i|
|
88
|
+
@fields[i].unknown1 = coder.read_string
|
91
89
|
end
|
92
|
-
|
93
|
-
@fields[i].string_args = Array.new(
|
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] =
|
93
|
+
@fields[i].string_args[j] = coder.read_string
|
96
94
|
end
|
97
95
|
end
|
98
|
-
|
99
|
-
@fields[i].args = Array.new(
|
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] =
|
99
|
+
@fields[i].args[j] = coder.read_int
|
102
100
|
end
|
103
101
|
end
|
104
|
-
|
105
|
-
@fields[i].default_value =
|
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(
|
110
|
-
|
111
|
-
|
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(
|
111
|
+
field.dump_project(coder)
|
114
112
|
end
|
115
|
-
|
113
|
+
coder.write_int(@data.size)
|
116
114
|
@data.each do |datum|
|
117
|
-
datum.dump_project(
|
115
|
+
datum.dump_project(coder)
|
118
116
|
end
|
119
|
-
|
117
|
+
coder.write_string(@description)
|
120
118
|
|
121
119
|
# Dump misc field data
|
122
|
-
|
120
|
+
coder.write_int(@field_type_list_size)
|
123
121
|
index = 0
|
124
122
|
while index < @fields.size
|
125
|
-
|
123
|
+
coder.write_byte(@fields[index].type)
|
126
124
|
index += 1
|
127
125
|
end
|
128
126
|
while index < @field_type_list_size
|
129
|
-
|
127
|
+
coder.write_byte(0)
|
130
128
|
index += 1
|
131
129
|
end
|
132
|
-
|
130
|
+
coder.write_int(@fields.size)
|
133
131
|
@fields.each do |field|
|
134
|
-
|
132
|
+
coder.write_string(field.unknown1)
|
135
133
|
end
|
136
|
-
|
134
|
+
coder.write_int(@fields.size)
|
137
135
|
@fields.each do |field|
|
138
|
-
|
136
|
+
coder.write_int(field.string_args.size)
|
139
137
|
field.string_args.each do |arg|
|
140
|
-
|
138
|
+
coder.write_string(arg)
|
141
139
|
end
|
142
140
|
end
|
143
|
-
|
141
|
+
coder.write_int(@fields.size)
|
144
142
|
@fields.each do |field|
|
145
|
-
|
143
|
+
coder.write_int(field.args.size)
|
146
144
|
field.args.each do |arg|
|
147
|
-
|
145
|
+
coder.write_int(arg)
|
148
146
|
end
|
149
147
|
end
|
150
|
-
|
148
|
+
coder.write_int(@fields.size)
|
151
149
|
@fields.each do |field|
|
152
|
-
|
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(
|
158
|
-
|
159
|
-
@unknown1 =
|
160
|
-
fields_size =
|
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(
|
161
|
+
field.read_dat(coder)
|
164
162
|
end
|
165
|
-
data_size =
|
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(
|
166
|
+
datum.read_dat(coder, @fields)
|
169
167
|
end
|
170
168
|
end
|
171
169
|
|
172
|
-
def dump_dat(
|
173
|
-
|
174
|
-
|
175
|
-
|
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(
|
175
|
+
field.dump_dat(coder)
|
178
176
|
end
|
179
|
-
|
177
|
+
coder.write_int(@data.size)
|
180
178
|
@data.each do |datum|
|
181
|
-
datum.dump_dat(
|
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(
|
197
|
-
@name =
|
194
|
+
def initialize(coder)
|
195
|
+
@name = coder.read_string
|
198
196
|
end
|
199
197
|
|
200
|
-
def dump_project(
|
201
|
-
|
198
|
+
def dump_project(coder)
|
199
|
+
coder.write_string(@name)
|
202
200
|
end
|
203
201
|
|
204
|
-
def read_dat(
|
205
|
-
@indexinfo =
|
202
|
+
def read_dat(coder)
|
203
|
+
@indexinfo = coder.read_int
|
206
204
|
end
|
207
205
|
|
208
|
-
def dump_dat(
|
209
|
-
|
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(
|
238
|
-
@name =
|
235
|
+
def initialize(coder)
|
236
|
+
@name = coder.read_string
|
239
237
|
end
|
240
238
|
|
241
|
-
def dump_project(
|
242
|
-
|
239
|
+
def dump_project(coder)
|
240
|
+
coder.write_string(@name)
|
243
241
|
end
|
244
242
|
|
245
|
-
def read_dat(
|
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] =
|
249
|
+
@int_values[i] = coder.read_int
|
252
250
|
end
|
253
251
|
@string_values.each_index do |i|
|
254
|
-
@string_values[i] =
|
252
|
+
@string_values[i] = coder.read_string
|
255
253
|
end
|
256
254
|
end
|
257
255
|
|
258
|
-
def dump_dat(
|
256
|
+
def dump_dat(coder)
|
259
257
|
@int_values.each do |i|
|
260
|
-
|
258
|
+
coder.write_int(i)
|
261
259
|
end
|
262
260
|
@string_values.each do |i|
|
263
|
-
|
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
|