1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
|
function process_current_room_script()
local script, font, scriptfile, fontfile, nptrs
room_idx = current_file - 3610
if not rooms_lookup[room_idx] then return nil, nil end
script = Buffer(true)
scriptfile = Buffer(true)
fontfile = Buffer(true)
font = { idx = 1, ilookup = {}, clookup = {} }
for k, v in ipairs(script_types.room.translated) do
add_glyph(font, v)
end
for i = 1, script_types.room.size do
script:writeU8(script_types.room.header[i])
end
scriptfile:writeU16(0)
scriptfile:writeU16(3)
nptrs = 0
for pidx, pval in ipairs(rooms_lookup[room_idx]) do
nptrs = nptrs + 1
local ptr = rooms_txts[pval]
scriptfile:writeU16(script:getsize())
for k, v in ipairs(ptr) do
if type(v) == "string" then
local str = v
local char
local gidx
while str:len() ~= 0 do
char, str = get_next_utf8(str)
gidx = font.clookup[char] or add_glyph(font, char)
if gidx < 0x80 then
script:writeU8(gidx)
else
local high = shl(gidx, 7)
local low = andB(gidx, 127) + 0x80
script:writeU8(low)
script:writeU8(high)
end
end
elseif type(v) == "table" then
local code = v[1]
local high = 0x80
local low = 0x80 + code
script:writeU8(low)
script:writeU8(high)
if v[2] then script:writeU8(v[2]) end
if v[3] then script:writeU8(v[3]) end
if v[4] then error "Wrong script..." end
else
error("Wrong script...")
end
end
script:writeU8(0)
end
scriptfile:copyfrom(script)
script:destroy()
local npadding = alignment(#font.ilookup, 4)
fontfile:writeU32(#font.ilookup + npadding)
fontfile:writeU32(12)
for _, glyph in ipairs(font.ilookup) do
if not glyphes[glyph] then error("No glyph in database for " .. glyph) end
fontfile:writeU8(glyphes[glyph].attr.Width)
end
for i = 1, npadding do
fontfile:writeU8(0)
end
for _, glyph in ipairs(font.ilookup) do
Base64DecodeBin(glyphes[glyph].attr.Data, fontfile)
end
for i = 1, npadding do
for j = 1, 6 do fontfile:writeU32(0) end
end
return scriptfile, fontfile, nptrs + 2
end
function insert_script(old, new, nptrs)
local size_1 = old:readU32()
local u1 = old:readU32()
local u2 = old:readU32()
local n_ptrs = old:readU32()
local u3 = old:readU32()
local u4 = old:readU32()
local u5 = old:readU32()
local ret = Buffer(true)
ret:writeU32(size_1)
ret:writeU32(u1)
ret:writeU32(u2)
if nptrs ~= n_ptrs then error("Script inconsistancy! Got " .. nptrs .. " instead of " .. n_ptrs) end
ret:writeU32(nptrs)
ret:writeU32(u3)
ret:writeU32(u4)
ret:writeU32(u5)
if got_us and current_file == (3610 + 125) and activate_debug_room then
ret:copyfrom(old, 0x1158 * 4)
ret:writeU32(0x9e000000)
old:readU32()
ret:copyfrom(old, size_1 - 0x1159 * 4)
else
ret:copyfrom(old, size_1)
end
ret:copyfrom(new)
return ret
end
function process_arcroom(fname, h, size, ext)
log("Processing " .. fname .. "." .. ext)
local o
if dump_mode then
o = Output(fname .. "." .. ext)
o:copyfrom(h, size)
o:destroy()
h:seek(-size, SEEK_CUR)
end
log("Processing " .. fname .. " - format 'arcroom' - " .. ext)
dump_mkdir(fname)
local nfiles = h:readU32()
local offset = h:readU32()
if nfiles * 8 + 8 ~= offset then
error("Bad archive format.")
end
local ret
if not dump_mode then
ret = Buffer(true)
ret:writeU32(nfiles)
ret:writeU32(offset)
end
index = {}
for i = 1, nfiles do
index[i] = {}
index[i].ftype = h:readU32()
index[i].size = h:readU32()
end
local tell
local outfile = {}
local script = nil
local font = nil
local nptrs
if not dump_mode then
script, font, nptrs = process_current_room_script()
end
for i = 1, nfiles do
tell = h:tell()
local handler = nil
local counter = 1
if dump_mode and ext == "room" and index[i].ftype == 4 then
log("Preparing to dump script...")
handler = function(fname, h, size, ext)
if counter == 1 then
log("Taking script...")
script = Buffer(true)
script:copyfrom(h)
elseif counter == 2 then
log("Taking font...")
font = Buffer(true)
font:copyfrom(h)
else
error("Too many files.")
end
counter = counter + 1
end
elseif not dump_mode and ext == "room" and index[i].ftype == 4 and script and font then
old_script = Buffer(true)
handler = function(fname, h, size, ext)
local ret
if counter == 1 then
log("Putting script...")
ret = insert_script(h, script, nptrs)
elseif counter == 2 then
log("Putting font...")
ret = Buffer(true)
ret:copyfrom(font)
else
error("Too many files.")
end
counter = counter + 1
return ret
end
elseif dump_mode and ext == "sarc" and index[i].ftype == 4 then
if script then error "Can't have two scripts in these..." end
handler = function(fname, h, size, ext)
if counter == 1 then
log("Taking text...")
script = Buffer(true)
script:copyfrom(h)
else
error("Too many files")
end
counter = counter + 1
end
elseif dump_mode and ext == "sarc" and index[i].ftype == 7 then
if font then error "Can't have two fonts in these..." end
handler = function(fname, h, size, ext)
if counter == 1 then
log("Taking font...")
font = Buffer(true)
font:copyfrom(h)
else
error("Too many files")
end
counter = counter + 1
end
elseif not dump_mode and ext == "sarc" and index[i].ftype == 4 then
error "Not written yet"
elseif not dump_mode and ext == "sarc" and index[i].ftype == 7 then
error "Not written yet"
end
outfile[i] = process_single_file(fname .. string.format("/%04i-%08X", i, index[i].ftype), h, index[i].size, "room", handler)
if handler and script and font then
if dump_mode and counter == 3 then
extract_room_script(fname .. string.format("/%04i", i), script, font)
else
extract_simple_script(fname .. string.format("/%04i", i), script, font)
end
script:destroy()
font:destroy()
script = nil
font = nil
end
h:seek(tell + index[i].size)
end
if not dump_mode then
for i = 1, nfiles do
local alignment_bytes = alignment(outfile[i]:getsize(), 4)
for j = 1, alignment_bytes do
outfile[i]:writeU8(0)
end
ret:writeU32(index[i].ftype)
ret:writeU32(outfile[i]:getsize())
end
for i = 1, nfiles do
ret:copyfrom(outfile[i])
outfile[i]:destroy()
end
end
return ret
end
room_logic_check = {
[0x0090] = { 0x18000000, 0x00000008 }, [0x009B] = { 0x98100000, 0x17000000 },
[0x033C] = { 0x18000000, 0x0000002C }, [0x03BC] = { 0x92100000, 0x150003E1 }, [0x03C7] = { 0x94100000, 0x150003E1 }, [0x03D3] = { 0x93100000, 0x150003E1 }, [0x03DF] = { 0x92100000, 0x150003E1 },
[0x03E2] = { 0x18000000, 0x00000018 }, [0x03F0] = { 0x1400033C, 0x7E000000 },
[0x03F5] = { 0x18000000, 0x00000018 }, [0x0403] = { 0x1400033C, 0x7E000000 },
[0x0408] = { 0x18000000, 0x00000018 }, [0x0416] = { 0x1400033C, 0x7E000000 },
-- [0x041B] = { 0x18000000, 0x00000020 }, [0x042B] = { 0x1400033C, 0x7E000000 },
-- [0x0432] = { 0x18000000, 0x00000020 }, [0x0442] = { 0x1400033C, 0x7E000000 },
-- [0x0449] = { 0x18000000, 0x00000020 }, [0x0459] = { 0x1400033C, 0x7E000000 },
}
function check_opcode(script, PC, supposed)
script:seek(PC * 4)
local r = script:readU32()
if r ~= supposed then error(string.format("Error for script: opcode %04X is %08X but was supposed to be %08X", PC, r, supposed)) end
end
function check_room_logic(script)
for offset, v in pairs(room_logic_check) do
local PC = offset - 1
for suboffset, supposed in ipairs(v) do
check_opcode(script, PC + suboffset, supposed)
end
end
end
function extract_room_script(fname, script, font)
log("Processing script " .. fname)
dump_mkdir(fname)
dump_mkdir(fname .. "/EXTRA")
local font = extract_font(fname .. "/FONT", font)
local lookup = resolve_font(font)
local size_1 = script:readU32()
local logic_offset = script:readU32()
local u1 = script:readU32()
local n_ptrs = script:readU32()
local u3 = script:readU32()
local u4 = script:readU32()
local u5 = script:readU32()
if logic_offset ~= 0 then error("Unhandled logic offset " .. logic_offset) end
local o
o = Output(fname .. "/EXTRA/00-head1-" .. string.format("%08X", u1) .. ".bin")
o:writeU32(u1)
o:destroy()
o = Output(fname .. "/EXTRA/00-head2-" .. string.format("%08X-%08X-%08X", u3, u4, u5) .. ".bin")
o:writeU32(u3)
o:writeU32(u4)
o:writeU32(u5)
o:destroy()
o = Output(fname .. "/EXTRA/01-head1.bin")
o:writeU32(u1)
o:destroy()
o = Output(fname .. "/EXTRA/01-head2.bin")
o:writeU32(u3)
o:writeU32(u4)
o:writeU32(u5)
o:destroy()
o = Output(fname .. "/EXTRA/02-part1.bin")
o:copyfrom(script, size_1)
o:destroy()
script:seek(-size_1, SEEK_CUR)
local logic = Buffer(true)
logic:copyfrom(script, size_1)
local ptrs = {}
local ptr_start = 1
for i = 1, n_ptrs do
ptrs[i] = script:readU16()
end
local ptrs_contents, ptrs_raws = {}, {}
local tell = script:tell()
ptrs_contents[1] = ""
ptrs_contents[2] = ""
ptrs_raws[1] = ""
ptrs_raws[2] = ""
ptr_start = 3
local header = script_types.room.header
for i = 1, script_types.room.size do
if script:readU8() ~= header[i] then error("Script header file of type 'room' not according to its header") end
end
for i = ptr_start, n_ptrs do
ptrs_contents[i] = ""
ptrs_raws[i] = ""
if tell + ptrs[i] ~= script:tell() then error("Script consistancy failure for pointer " .. i .. " - we're at " .. script:tell() .. " and " .. (tell + ptrs[i]) .. " was expected.") end
while true do
local r, rr = extract_char(script, lookup)
if not r then break end
ptrs_contents[i] = ptrs_contents[i] .. r
ptrs_raws[i] = ptrs_raws[i] .. rr
end
end
o = Output(fname .. "/EXTRA/logic.txt")
check_room_logic(logic)
local textboxes = { }
local rd_opcode = function(PC)
logic:seek(PC * 4)
return logic:readU32()
end
local add_textbox = function(txtptr, txtbox)
if txtptr < 0 then return end
o:write("\n")
if ptrs_raws[txtptr + ptr_start] then
o:write(ptrs_raws[txtptr + ptr_start])
if textboxes[txtptr + ptr_start] then
o:write("\n****SUSPECT****PTR ALREADY RECORDED****: " .. (txtptr + ptr_start))
end
textboxes[txtptr + ptr_start] = txtbox
else
error("****SUSPECT****PTR TOO LARGE****: " .. (txtptr + ptr_start))
end
end
local txt_process = function(PC)
local opcode = rd_opcode(PC)
local txtptr
local x, y, width, height
if andB(opcode, 0x00100000) ~= 0 then
local psh1 = rd_opcode(PC - 1)
local psh2 = rd_opcode(PC - 2)
local psh3 = rd_opcode(PC - 3)
local psh4 = rd_opcode(PC - 4)
local psh5 = rd_opcode(PC - 5)
local psh6 = rd_opcode(PC - 6)
if andB(psh1, 0xff000000) ~= 0x12000000 then return end
if andB(psh2, 0xff000000) ~= 0x12000000 then return end
if andB(psh3, 0xff000000) ~= 0x12000000 then return end
if andB(psh4, 0xff000000) ~= 0x12000000 then return end
if andB(psh5, 0xff000000) ~= 0x12000000 then return end
if andB(psh6, 0xff000000) ~= 0x12000000 then return end
local arg1 = andB(psh1, 0x7fffff)
local arg2 = andB(psh2, 0x7fffff)
local arg3 = andB(psh3, 0x7fffff)
local arg4 = andB(psh4, 0x7fffff)
local arg5 = andB(psh5, 0x7fffff)
local arg6 = andB(psh6, 0x7fffff)
txtptr = arg6 + 1 - ptr_start
x = arg5
y = arg4
width = arg3
height = arg2
else
txtptr = andB(opcode, 0xff) + 1 - ptr_start
local arg1 = rd_opcode(PC + 1)
local arg2 = rd_opcode(PC + 2)
x = andB(arg1, 0xffff)
y = shr(arg1, 16)
width = andB(arg2, 0xffff)
height = shr(arg2, 16)
end
add_textbox(txtptr, {
x = x,
y = y,
width = width,
height = height,
})
end
local process_subtxt = function(PC)
local psh1 = rd_opcode(PC - 1)
local psh2 = rd_opcode(PC - 2)
local psh3 = rd_opcode(PC - 3)
local psh4 = rd_opcode(PC - 4)
local psh5 = rd_opcode(PC - 5)
local psh6 = rd_opcode(PC - 6)
if andB(psh1, 0xff000000) ~= 0x12000000 and andB(psh1, 0xff000000) ~= 0x0B000000 then return end
if andB(psh2, 0xff000000) ~= 0x12000000 then return end
if andB(psh3, 0xff000000) ~= 0x12000000 and andB(psh3, 0xff000000) ~= 0x0B000000 then return end
if andB(psh4, 0xff000000) ~= 0x12000000 and andB(psh4, 0xff000000) ~= 0x0B000000 then return end
if andB(psh5, 0xff000000) ~= 0x12000000 and andB(psh5, 0xff000000) ~= 0x0B000000 then return end
if andB(psh6, 0xff000000) ~= 0x12000000 and andB(psh6, 0xff000000) ~= 0x0B000000 then return end
local arg1 = andB(psh1, 0x7fffff)
local arg2 = andB(psh2, 0x7fffff)
local arg3 = andB(psh3, 0x7fffff)
local arg4 = andB(psh4, 0x7fffff)
local arg5 = andB(psh5, 0x7fffff)
local arg6 = andB(psh6, 0x7fffff)
if andB(psh1, 0xff000000) == 0x0B000000 then arg1 = nil end
if andB(psh3, 0xff000000) == 0x0B000000 then arg3 = nil end
if andB(psh4, 0xff000000) == 0x0B000000 then arg4 = nil end
if andB(psh5, 0xff000000) == 0x0B000000 then arg5 = nil end
if andB(psh6, 0xff000000) == 0x0B000000 then arg6 = nil end
local txtptr = arg2 + 1 - ptr_start
add_textbox(txtptr, {
x = arg6 or "var",
y = arg5 or "var",
width = arg4 or "var",
height = arg3 or "var",
})
end
local process_subtxt2 = function(PC)
local psh1 = rd_opcode(PC - 1)
local psh2 = rd_opcode(PC - 2)
local psh3 = rd_opcode(PC - 3)
local psh4 = rd_opcode(PC - 4)
local psh5 = rd_opcode(PC - 5)
if andB(psh1, 0xff000000) ~= 0x12000000 then return end
if andB(psh2, 0xff000000) ~= 0x0B000000 then return end
if andB(psh3, 0xff000000) ~= 0x22000000 then return end
if andB(psh4, 0xff000000) ~= 0x0B000000 then return end
if andB(psh5, 0xff000000) ~= 0xCB000000 then return end
local txtptr = andB(psh5, 0xff) + 1 - ptr_start
add_textbox(txtptr, {
typ = "fixed",
})
end
local call_process = function(PC)
local opcode = rd_opcode(PC)
if opcode == 0x1400033C or opcode == 0x140003E2 or opcode == 0x140003F5 or opcode == 0x14000408 then process_subtxt(PC) end
if opcode == 0x14000090 then process_subtxt2(PC) end
end
logic_disasm(logic, o, {
-- ret
[0x17] = function(PC) o:write("\n-----------------------------------------------------\n") end,
-- txtbox1
[0x92] = txt_process,
-- txtbox2
[0x93] = txt_process,
-- txtbox3
[0x94] = txt_process,
-- txtbox4
[0x98] = txt_process,
-- *sigh*
[0x14] = call_process,
} )
o:destroy()
logic:destroy()
for i = ptr_start, n_ptrs do
if textboxes[i] then
local tb = textboxes[i]
if tb.typ == "fixed" then
ptrs_contents[i] = '<window type="fixed"/>\n' .. ptrs_contents[i]
elseif tb.typ == "auto" then
ptrs_contents[i] = '<window type="auto"/>\n' .. ptrs_contents[i]
else
ptrs_contents[i] = '<window x="' .. tb.x .. '" y="' .. tb.y .. '" width="' .. tb.width .. '" height="' .. tb.height .. '"/>\n' .. ptrs_contents[i]
end
else
ptrs_contents[i] = '<nowindowdetected/>\n' .. ptrs_contents[i]
end
end
-- WARNING -- only for rooms
local ptrs_idx = process_ptrs(ptrs_contents, ptr_start, n_ptrs)
local o = Output(fname .. "-script.txt")
o:write("template:room\n")
for i = ptr_start, n_ptrs do
o:write(ptrs_idx[i] .. "\n")
end
lookup_rooms:write(" [" .. current_file - 3610 .. "] = { ")
for i = ptr_start, n_ptrs do
lookup_rooms:write(ptrs_idx[i] .. ", ")
end
lookup_rooms:write "},\n"
o:destroy()
end
function extract_simple_script(fname, script, font)
log("Processing script " .. fname)
dump_mkdir(fname)
local font = extract_font(fname .. "/FONT", font)
local lookup = resolve_font(font)
local n_ptrs = 1
local script_begin = script:readU32()
local ptrs = {}
local unks = {}
local ptr_start = 1
local tell = script:tell()
while tell < script_begin do
unks[n_ptrs] = script:readU32()
ptrs[n_ptrs] = script:readU32()
tell = script:tell()
n_ptrs = n_ptrs + 1
end
ptrs[n_ptrs] = script:getsize() - script_begin
n_ptrs = n_ptrs - 1
local ptrs_contents, ptrs_raws = {}, {}
ptr_start = 1
for i = ptr_start, n_ptrs do
ptrs_contents[i] = ""
ptrs_raws[i] = ""
-- if tell + ptrs[i] ~= script:tell() then print("Script consistancy failure for pointer " .. i .. " - we're at " .. script:tell() .. " and " .. (tell + ptrs[i]) .. " was expected.") end
script:seek(script_begin + ptrs[i])
while script:tell() ~= (script_begin + ptrs[i + 1]) do
local r, rr = extract_char(script, lookup)
if r then
ptrs_contents[i] = ptrs_contents[i] .. r
ptrs_raws[i] = ptrs_raws[i] .. rr
elseif script:tell() ~= (script_begin + ptrs[i + 1]) then
ptrs_contents[i] = ptrs_contents[i] .. "<ZERO>"
end
end
end
local o = Output(fname .. "-script.txt")
for i = ptr_start, n_ptrs do
o:write("<ptr " .. i .. " - " .. unks[i] .. ">\n")
o:write(ptrs_contents[i])
o:write("\n")
end
o:destroy()
end
|