summaryrefslogtreecommitdiff
path: root/VP-roomwork.lua
blob: 4349ca437e4cbeecb04461380a842b955553490b (plain)
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