wolftrans 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,8 +1,12 @@
1
- require 'wolfrpg/io'
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
- File.open(filename, 'rb') do |file|
16
- IO.verify(file, MAGIC_NUMBER)
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
- @unknown1 = IO.read(file, 25)
41
+ unknown1_size = coder.read_int
42
+ @unknown1 = coder.read(unknown1_size)
43
+ @unknown4 = coder.read_int
19
44
 
20
- @title = IO.read_string(file)
21
- if (magic_string = IO.read_string(file)) != 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 = IO.read_int(file)
26
- @unknown2 = IO.read(file, unknown2_size)
50
+ unknown2_size = coder.read_int
51
+ @unknown2 = coder.read(unknown2_size)
27
52
 
28
- #IO.dump(file, 64)
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] = IO.read_string(file)
56
+ @subfonts[i] = coder.read_string
34
57
  end
35
58
 
36
- @default_pc_graphic = IO.read_string(file)
37
- @version = IO.read_string(file)
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
- file.seek(4, :CUR)
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 = file.read
69
+ @unknown3 = coder.read
47
70
  end
48
71
  end
49
72
 
50
73
  def dump(filename)
51
- File.open(filename, 'wb') do |file|
52
- IO.write(file, MAGIC_NUMBER)
53
- IO.write(file, @unknown1)
54
- IO.write_string(file, @title)
55
- IO.write_string(file, MAGIC_STRING)
56
- IO.write_int(file, @unknown2.bytesize)
57
- IO.write(file, @unknown2)
58
- IO.write_string(file, @font)
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
- IO.write_string(file, subfont)
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
- 0x00, 0x57, 0x00, 0x00, 0x4f, 0x4c, 0x00, 0x46, 0x4d, 0x00,
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
@@ -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
- File.open(filename, 'rb') do |file|
18
- IO.verify(file, MAGIC_NUMBER)
13
+ FileCoder.open(filename, :read) do |coder|
14
+ coder.verify(MAGIC_NUMBER)
19
15
 
20
- @tileset_id = IO.read_int(file)
16
+ @tileset_id = coder.read_int
21
17
 
22
18
  # Read basic data
23
- @width = IO.read_int(file)
24
- @height = IO.read_int(file)
25
- @events = Array.new(IO.read_int(file))
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 = IO.read(file, @width * @height * 3 * 4)
25
+ @tiles = coder.read(@width * @height * 3 * 4)
30
26
 
31
27
  # Read events
32
- while (indicator = IO.read_byte(file)) == 0x6F
33
- event = Event.new(file)
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 file.eof?
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
- File.open(filename, 'wb') do |file|
47
- IO.write(file, MAGIC_NUMBER)
48
- IO.write_int(file, @tileset_id)
49
- IO.write_int(file, @width)
50
- IO.write_int(file, @height)
51
- IO.write_int(file, @events.size)
52
- IO.write(file, @tiles)
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
- IO.write_byte(file, 0x6F)
56
- event.dump(file)
51
+ coder.write_byte(0x6F)
52
+ event.dump(coder)
57
53
  end
58
- IO.write_byte(file, 0x66)
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(file)
98
- IO.verify(file, MAGIC_NUMBER1)
99
- @id = IO.read_int(file)
100
- @name = IO.read_string(file)
101
- @x = IO.read_int(file)
102
- @y = IO.read_int(file)
103
- @pages = Array.new(IO.read_int(file))
104
- IO.verify(file, MAGIC_NUMBER2)
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 = IO.read_byte(file)) == 0x79
109
- page = Page.new(file, page_id)
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(file)
119
- IO.write(file, MAGIC_NUMBER1)
120
- IO.write_int(file, @id)
121
- IO.write_string(file, @name)
122
- IO.write_int(file, @x)
123
- IO.write_int(file, @y)
124
- IO.write_int(file, @pages.size)
125
- IO.write(file, MAGIC_NUMBER2)
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
- IO.write_byte(file, 0x79)
130
- page.dump(file)
125
+ coder.write_byte(0x79)
126
+ page.dump(coder)
131
127
  end
132
- IO.write_byte(file, 0x70)
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(file, id)
149
+ def initialize(coder, id)
154
150
  @id = id
155
151
 
156
152
  #TODO ???
157
- @unknown1 = IO.read_int(file)
153
+ @unknown1 = coder.read_int
158
154
 
159
155
  #TODO further abstract graphics options
160
- @graphic_name = IO.read_string(file)
161
- @graphic_direction = IO.read_byte(file)
162
- @graphic_frame = IO.read_byte(file)
163
- @graphic_opacity = IO.read_byte(file)
164
- @graphic_render_mode = IO.read_byte(file)
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 = IO.read(file, 1 + 4 + 4*4 + 4*4)
163
+ @conditions = coder.read(1 + 4 + 4*4 + 4*4)
168
164
  #TODO parse movement options later
169
- @movement = IO.read(file, 4)
165
+ @movement = coder.read(4)
170
166
 
171
167
  #TODO further abstract flags
172
- @flags = IO.read_byte(file)
168
+ @flags = coder.read_byte
173
169
 
174
170
  #TODO further abstract flags
175
- @route_flags = IO.read_byte(file)
171
+ @route_flags = coder.read_byte
176
172
 
177
173
  # Parse move route
178
- @route = Array.new(IO.read_int(file))
174
+ @route = Array.new(coder.read_int)
179
175
  @route.each_index do |i|
180
- @route[i] = RouteCommand.create(file)
176
+ @route[i] = RouteCommand.create(coder)
181
177
  end
182
178
 
183
179
  # Parse commands
184
- @commands = Array.new(IO.read_int(file))
180
+ @commands = Array.new(coder.read_int)
185
181
  @commands.each_index do |i|
186
- @commands[i] = Command.create(file)
182
+ @commands[i] = Command.create(coder)
187
183
  end
188
- IO.verify(file, COMMANDS_TERMINATOR)
184
+ coder.verify(COMMANDS_TERMINATOR)
189
185
 
190
186
  #TODO abstract these options later
191
- @shadow_graphic_num = IO.read_byte(file)
192
- @collision_width = IO.read_byte(file)
193
- @collision_height = IO.read_byte(file)
187
+ @shadow_graphic_num = coder.read_byte
188
+ @collision_width = coder.read_byte
189
+ @collision_height = coder.read_byte
194
190
 
195
- if (terminator = IO.read_byte(file)) != 0x7A
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(file)
201
- IO.write_int(file, @unknown1)
202
- IO.write_string(file, @graphic_name)
203
- IO.write_byte(file, @graphic_direction)
204
- IO.write_byte(file, @graphic_frame)
205
- IO.write_byte(file, @graphic_opacity)
206
- IO.write_byte(file, @graphic_render_mode)
207
- IO.write(file, @conditions)
208
- IO.write(file, @movement)
209
- IO.write_byte(file, @flags)
210
- IO.write_byte(file, @route_flags)
211
- IO.write_int(file, @route.size)
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(file)
209
+ cmd.dump(coder)
214
210
  end
215
- IO.write_int(file, @commands.size)
211
+ coder.write_int(@commands.size)
216
212
  @commands.each do |cmd|
217
- cmd.dump(file)
213
+ cmd.dump(coder)
218
214
  end
219
- IO.write(file, COMMANDS_TERMINATOR)
220
- IO.write_byte(file, @shadow_graphic_num)
221
- IO.write_byte(file, @collision_width)
222
- IO.write_byte(file, @collision_height)
223
- IO.write_byte(file, 0x7A)
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 = [
@@ -1,25 +1,25 @@
1
1
  module WolfRpg
2
2
  class RouteCommand
3
- def self.create(file)
3
+ def self.create(coder)
4
4
  # Read all data for this movement command from file
5
- id = IO.read_byte(file)
6
- args = Array.new(IO.read_byte(file))
5
+ id = coder.read_byte
6
+ args = Array.new(coder.read_byte)
7
7
  args.each_index do |i|
8
- args[i] = IO.read_int(file)
8
+ args[i] = coder.read_int
9
9
  end
10
- IO.verify(file, TERMINATOR)
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(file)
17
- IO.write_byte(file, @id)
18
- IO.write_byte(file, @args.size)
16
+ def dump(coder)
17
+ coder.write_byte(@id)
18
+ coder.write_byte(@args.size)
19
19
  @args.each do |arg|
20
- IO.write_int(file, arg)
20
+ coder.write_int(arg)
21
21
  end
22
- IO.write(file, TERMINATOR)
22
+ coder.write(TERMINATOR)
23
23
  end
24
24
 
25
25
  attr_accessor :id