wolftrans 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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/game_dat.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
|
-
require '
|
1
|
+
require 'stringio'
|
2
2
|
|
3
3
|
module WolfRpg
|
4
4
|
class GameDat
|
5
|
+
attr_reader :legacy
|
6
|
+
alias_method :legacy?, :legacy
|
7
|
+
|
5
8
|
attr_accessor :unknown1
|
9
|
+
attr_accessor :unknown4
|
6
10
|
attr_accessor :title
|
7
11
|
attr_accessor :unknown2
|
8
12
|
attr_accessor :font
|
@@ -12,65 +16,123 @@ module WolfRpg
|
|
12
16
|
attr_accessor :unknown3
|
13
17
|
|
14
18
|
def initialize(filename)
|
15
|
-
|
16
|
-
|
19
|
+
FileCoder.open(filename, :read) do |coder|
|
20
|
+
if (first_byte = coder.read_byte) == 0
|
21
|
+
@legacy = false
|
22
|
+
coder.verify(MAGIC_NUMBER)
|
23
|
+
else
|
24
|
+
@legacy = true
|
25
|
+
@seeds = Array.new(3)
|
26
|
+
@xseeds = Array.new(3)
|
27
|
+
@seeds[0] = first_byte
|
28
|
+
coder.skip(1) # garbage
|
29
|
+
@xseeds[0] = coder.read_byte
|
30
|
+
@xseeds[1] = coder.read_byte
|
31
|
+
@xseeds[2] = coder.read_byte
|
32
|
+
coder.skip(1) # garbage
|
33
|
+
@seeds[2] = coder.read_byte
|
34
|
+
coder.skip(1) # garbage
|
35
|
+
@seeds[1] = coder.read_byte
|
36
|
+
coder.skip(1) # garbage
|
37
|
+
coder = FileCoder.new(StringIO.new(crypt(coder.read)))
|
38
|
+
end
|
39
|
+
|
17
40
|
#TODO what is most of the junk in this file?
|
18
|
-
|
41
|
+
unknown1_size = coder.read_int
|
42
|
+
@unknown1 = coder.read(unknown1_size)
|
43
|
+
@unknown4 = coder.read_int
|
19
44
|
|
20
|
-
@title =
|
21
|
-
if (magic_string =
|
45
|
+
@title = coder.read_string
|
46
|
+
if (magic_string = coder.read_string) != MAGIC_STRING
|
22
47
|
raise "magic string invalid (got #{magic_string})"
|
23
48
|
end
|
24
49
|
|
25
|
-
unknown2_size =
|
26
|
-
@unknown2 =
|
50
|
+
unknown2_size = coder.read_int
|
51
|
+
@unknown2 = coder.read(unknown2_size)
|
27
52
|
|
28
|
-
|
29
|
-
#abort
|
30
|
-
@font = IO.read_string(file)
|
53
|
+
@font = coder.read_string
|
31
54
|
@subfonts = Array.new(3)
|
32
55
|
@subfonts.each_index do |i|
|
33
|
-
@subfonts[i] =
|
56
|
+
@subfonts[i] = coder.read_string
|
34
57
|
end
|
35
58
|
|
36
|
-
@default_pc_graphic =
|
37
|
-
@version =
|
59
|
+
@default_pc_graphic = coder.read_string
|
60
|
+
@version = coder.read_string
|
38
61
|
|
39
62
|
# This is the size of the file minus one.
|
40
63
|
# We don't need it, so discard it.
|
41
|
-
|
64
|
+
coder.skip(4)
|
42
65
|
|
43
66
|
# We don't care about the rest of this file for translation
|
44
67
|
# purposes.
|
45
68
|
# Someday we will know what the hell is stored in here... But not today.
|
46
|
-
@unknown3 =
|
69
|
+
@unknown3 = coder.read
|
47
70
|
end
|
48
71
|
end
|
49
72
|
|
50
73
|
def dump(filename)
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
74
|
+
begin
|
75
|
+
if @legacy
|
76
|
+
coder = FileCoder.new(StringIO.new('', 'wb'))
|
77
|
+
else
|
78
|
+
coder = FileCoder.open(filename, :write)
|
79
|
+
coder.write_byte(0)
|
80
|
+
coder.write(MAGIC_NUMBER)
|
81
|
+
end
|
82
|
+
|
83
|
+
coder.write_int(@unknown1.size)
|
84
|
+
coder.write(@unknown1)
|
85
|
+
coder.write_int(@unknown4)
|
86
|
+
coder.write_string(@title)
|
87
|
+
coder.write_string(MAGIC_STRING)
|
88
|
+
coder.write_int(@unknown2.bytesize)
|
89
|
+
coder.write(@unknown2)
|
90
|
+
coder.write_string(@font)
|
59
91
|
@subfonts.each do |subfont|
|
60
|
-
|
92
|
+
coder.write_string(subfont)
|
93
|
+
end
|
94
|
+
coder.write_string(@default_pc_graphic)
|
95
|
+
coder.write_string(@version)
|
96
|
+
coder.write_int(coder.tell + 4 + @unknown3.bytesize - 1 + (@legacy ? 10 : 0))
|
97
|
+
coder.write(@unknown3)
|
98
|
+
|
99
|
+
if @legacy
|
100
|
+
data = crypt(coder.io.string)
|
101
|
+
FileCoder.open(filename, :write) do |coder|
|
102
|
+
coder.write_byte(@seeds[0])
|
103
|
+
coder.write_byte(0) # garbage
|
104
|
+
coder.write_byte(@xseeds[0])
|
105
|
+
coder.write_byte(@xseeds[1])
|
106
|
+
coder.write_byte(@xseeds[2])
|
107
|
+
coder.write_byte(0) # garbage
|
108
|
+
coder.write_byte(@seeds[2])
|
109
|
+
coder.write_byte(0) # garbage
|
110
|
+
coder.write_byte(@seeds[1])
|
111
|
+
coder.write_byte(0) # garbage
|
112
|
+
coder.write(data)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
ensure
|
116
|
+
coder.close
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def crypt(data_str)
|
121
|
+
data = data_str.unpack('C*')
|
122
|
+
@seeds.each_with_index do |seed, s|
|
123
|
+
(0...data.size).step(DECRYPT_INTERVALS[s]) do |i|
|
124
|
+
seed = (seed * 0x343FD + 0x269EC3) & 0xFFFFFFFF
|
125
|
+
data[i] ^= (seed >> 28) & 7
|
61
126
|
end
|
62
|
-
IO.write_string(file, @default_pc_graphic)
|
63
|
-
IO.write_string(file, @version)
|
64
|
-
IO.write_int(file, file.tell + 4 + @unknown3.bytesize - 1)
|
65
|
-
IO.write(file, @unknown3)
|
66
127
|
end
|
128
|
+
return data.pack('C*')
|
67
129
|
end
|
68
130
|
|
69
131
|
private
|
70
132
|
MAGIC_NUMBER = [
|
71
|
-
|
72
|
-
0x15, 0x00, 0x00, 0x00, # likely an integer
|
133
|
+
0x57, 0x00, 0x00, 0x4f, 0x4c, 0x00, 0x46, 0x4d, 0x00
|
73
134
|
].pack('C*')
|
74
135
|
MAGIC_STRING = "0000-0000" # who knows what this is supposed to be
|
136
|
+
DECRYPT_INTERVALS = [1, 2, 5]
|
75
137
|
end
|
76
138
|
end
|
data/lib/wolfrpg/map.rb
CHANGED
@@ -1,7 +1,3 @@
|
|
1
|
-
require 'wolfrpg/io'
|
2
|
-
require 'wolfrpg/route'
|
3
|
-
require 'wolfrpg/command'
|
4
|
-
|
5
1
|
module WolfRpg
|
6
2
|
class Map
|
7
3
|
attr_reader :tileset_id
|
@@ -14,48 +10,48 @@ module WolfRpg
|
|
14
10
|
|
15
11
|
def initialize(filename)
|
16
12
|
@filename = File.basename(filename, '.*')
|
17
|
-
|
18
|
-
|
13
|
+
FileCoder.open(filename, :read) do |coder|
|
14
|
+
coder.verify(MAGIC_NUMBER)
|
19
15
|
|
20
|
-
@tileset_id =
|
16
|
+
@tileset_id = coder.read_int
|
21
17
|
|
22
18
|
# Read basic data
|
23
|
-
@width =
|
24
|
-
@height =
|
25
|
-
@events = Array.new(
|
19
|
+
@width = coder.read_int
|
20
|
+
@height = coder.read_int
|
21
|
+
@events = Array.new(coder.read_int)
|
26
22
|
|
27
23
|
# Read tiles
|
28
24
|
#TODO: interpret this data later
|
29
|
-
@tiles =
|
25
|
+
@tiles = coder.read(@width * @height * 3 * 4)
|
30
26
|
|
31
27
|
# Read events
|
32
|
-
while (indicator =
|
33
|
-
event = Event.new(
|
28
|
+
while (indicator = coder.read_byte) == 0x6F
|
29
|
+
event = Event.new(coder)
|
34
30
|
@events[event.id] = event
|
35
31
|
end
|
36
32
|
if indicator != 0x66
|
37
33
|
raise "unexpected event indicator: #{indicator.to_s(16)}"
|
38
34
|
end
|
39
|
-
unless
|
35
|
+
unless coder.eof?
|
40
36
|
raise "file not fully parsed"
|
41
37
|
end
|
42
38
|
end
|
43
39
|
end
|
44
40
|
|
45
41
|
def dump(filename)
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
42
|
+
FileCoder.open(filename, :write) do |coder|
|
43
|
+
coder.write(MAGIC_NUMBER)
|
44
|
+
coder.write_int(@tileset_id)
|
45
|
+
coder.write_int(@width)
|
46
|
+
coder.write_int(@height)
|
47
|
+
coder.write_int(@events.size)
|
48
|
+
coder.write(@tiles)
|
53
49
|
@events.each do |event|
|
54
50
|
next unless event
|
55
|
-
|
56
|
-
event.dump(
|
51
|
+
coder.write_byte(0x6F)
|
52
|
+
event.dump(coder)
|
57
53
|
end
|
58
|
-
|
54
|
+
coder.write_byte(0x66)
|
59
55
|
end
|
60
56
|
end
|
61
57
|
|
@@ -94,19 +90,19 @@ module WolfRpg
|
|
94
90
|
attr_accessor :y
|
95
91
|
attr_accessor :pages
|
96
92
|
|
97
|
-
def initialize(
|
98
|
-
|
99
|
-
@id =
|
100
|
-
@name =
|
101
|
-
@x =
|
102
|
-
@y =
|
103
|
-
@pages = Array.new(
|
104
|
-
|
93
|
+
def initialize(coder)
|
94
|
+
coder.verify(MAGIC_NUMBER1)
|
95
|
+
@id = coder.read_int
|
96
|
+
@name = coder.read_string
|
97
|
+
@x = coder.read_int
|
98
|
+
@y = coder.read_int
|
99
|
+
@pages = Array.new(coder.read_int)
|
100
|
+
coder.verify(MAGIC_NUMBER2)
|
105
101
|
|
106
102
|
# Read pages
|
107
103
|
page_id = 0
|
108
|
-
while (indicator =
|
109
|
-
page = Page.new(
|
104
|
+
while (indicator = coder.read_byte) == 0x79
|
105
|
+
page = Page.new(coder, page_id)
|
110
106
|
@pages[page_id] = page
|
111
107
|
page_id += 1
|
112
108
|
end
|
@@ -115,21 +111,21 @@ module WolfRpg
|
|
115
111
|
end
|
116
112
|
end
|
117
113
|
|
118
|
-
def dump(
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
114
|
+
def dump(coder)
|
115
|
+
coder.write(MAGIC_NUMBER1)
|
116
|
+
coder.write_int(@id)
|
117
|
+
coder.write_string(@name)
|
118
|
+
coder.write_int(@x)
|
119
|
+
coder.write_int(@y)
|
120
|
+
coder.write_int(@pages.size)
|
121
|
+
coder.write(MAGIC_NUMBER2)
|
126
122
|
|
127
123
|
# Write pages
|
128
124
|
@pages.each do |page|
|
129
|
-
|
130
|
-
page.dump(
|
125
|
+
coder.write_byte(0x79)
|
126
|
+
page.dump(coder)
|
131
127
|
end
|
132
|
-
|
128
|
+
coder.write_byte(0x70)
|
133
129
|
end
|
134
130
|
|
135
131
|
class Page
|
@@ -150,77 +146,77 @@ module WolfRpg
|
|
150
146
|
attr_accessor :collision_width
|
151
147
|
attr_accessor :collision_height
|
152
148
|
|
153
|
-
def initialize(
|
149
|
+
def initialize(coder, id)
|
154
150
|
@id = id
|
155
151
|
|
156
152
|
#TODO ???
|
157
|
-
@unknown1 =
|
153
|
+
@unknown1 = coder.read_int
|
158
154
|
|
159
155
|
#TODO further abstract graphics options
|
160
|
-
@graphic_name =
|
161
|
-
@graphic_direction =
|
162
|
-
@graphic_frame =
|
163
|
-
@graphic_opacity =
|
164
|
-
@graphic_render_mode =
|
156
|
+
@graphic_name = coder.read_string
|
157
|
+
@graphic_direction = coder.read_byte
|
158
|
+
@graphic_frame = coder.read_byte
|
159
|
+
@graphic_opacity = coder.read_byte
|
160
|
+
@graphic_render_mode = coder.read_byte
|
165
161
|
|
166
162
|
#TODO parse conditions later
|
167
|
-
@conditions =
|
163
|
+
@conditions = coder.read(1 + 4 + 4*4 + 4*4)
|
168
164
|
#TODO parse movement options later
|
169
|
-
@movement =
|
165
|
+
@movement = coder.read(4)
|
170
166
|
|
171
167
|
#TODO further abstract flags
|
172
|
-
@flags =
|
168
|
+
@flags = coder.read_byte
|
173
169
|
|
174
170
|
#TODO further abstract flags
|
175
|
-
@route_flags =
|
171
|
+
@route_flags = coder.read_byte
|
176
172
|
|
177
173
|
# Parse move route
|
178
|
-
@route = Array.new(
|
174
|
+
@route = Array.new(coder.read_int)
|
179
175
|
@route.each_index do |i|
|
180
|
-
@route[i] = RouteCommand.create(
|
176
|
+
@route[i] = RouteCommand.create(coder)
|
181
177
|
end
|
182
178
|
|
183
179
|
# Parse commands
|
184
|
-
@commands = Array.new(
|
180
|
+
@commands = Array.new(coder.read_int)
|
185
181
|
@commands.each_index do |i|
|
186
|
-
@commands[i] = Command.create(
|
182
|
+
@commands[i] = Command.create(coder)
|
187
183
|
end
|
188
|
-
|
184
|
+
coder.verify(COMMANDS_TERMINATOR)
|
189
185
|
|
190
186
|
#TODO abstract these options later
|
191
|
-
@shadow_graphic_num =
|
192
|
-
@collision_width =
|
193
|
-
@collision_height =
|
187
|
+
@shadow_graphic_num = coder.read_byte
|
188
|
+
@collision_width = coder.read_byte
|
189
|
+
@collision_height = coder.read_byte
|
194
190
|
|
195
|
-
if (terminator =
|
191
|
+
if (terminator = coder.read_byte) != 0x7A
|
196
192
|
raise "page terminator not 7A (found #{terminator.to_s(16)})"
|
197
193
|
end
|
198
194
|
end
|
199
195
|
|
200
|
-
def dump(
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
196
|
+
def dump(coder)
|
197
|
+
coder.write_int(@unknown1)
|
198
|
+
coder.write_string(@graphic_name)
|
199
|
+
coder.write_byte(@graphic_direction)
|
200
|
+
coder.write_byte(@graphic_frame)
|
201
|
+
coder.write_byte(@graphic_opacity)
|
202
|
+
coder.write_byte(@graphic_render_mode)
|
203
|
+
coder.write(@conditions)
|
204
|
+
coder.write(@movement)
|
205
|
+
coder.write_byte(@flags)
|
206
|
+
coder.write_byte(@route_flags)
|
207
|
+
coder.write_int(@route.size)
|
212
208
|
@route.each do |cmd|
|
213
|
-
cmd.dump(
|
209
|
+
cmd.dump(coder)
|
214
210
|
end
|
215
|
-
|
211
|
+
coder.write_int(@commands.size)
|
216
212
|
@commands.each do |cmd|
|
217
|
-
cmd.dump(
|
213
|
+
cmd.dump(coder)
|
218
214
|
end
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
215
|
+
coder.write(COMMANDS_TERMINATOR)
|
216
|
+
coder.write_byte(@shadow_graphic_num)
|
217
|
+
coder.write_byte(@collision_width)
|
218
|
+
coder.write_byte(@collision_height)
|
219
|
+
coder.write_byte(0x7A)
|
224
220
|
end
|
225
221
|
|
226
222
|
COMMANDS_TERMINATOR = [
|
data/lib/wolfrpg/route.rb
CHANGED
@@ -1,25 +1,25 @@
|
|
1
1
|
module WolfRpg
|
2
2
|
class RouteCommand
|
3
|
-
def self.create(
|
3
|
+
def self.create(coder)
|
4
4
|
# Read all data for this movement command from file
|
5
|
-
id =
|
6
|
-
args = Array.new(
|
5
|
+
id = coder.read_byte
|
6
|
+
args = Array.new(coder.read_byte)
|
7
7
|
args.each_index do |i|
|
8
|
-
args[i] =
|
8
|
+
args[i] = coder.read_int
|
9
9
|
end
|
10
|
-
|
10
|
+
coder.verify(TERMINATOR)
|
11
11
|
|
12
12
|
#TODO Create proper route command
|
13
13
|
return RouteCommand.new(id, args)
|
14
14
|
end
|
15
15
|
|
16
|
-
def dump(
|
17
|
-
|
18
|
-
|
16
|
+
def dump(coder)
|
17
|
+
coder.write_byte(@id)
|
18
|
+
coder.write_byte(@args.size)
|
19
19
|
@args.each do |arg|
|
20
|
-
|
20
|
+
coder.write_int(arg)
|
21
21
|
end
|
22
|
-
|
22
|
+
coder.write(TERMINATOR)
|
23
23
|
end
|
24
24
|
|
25
25
|
attr_accessor :id
|