load "VP-map.lua" --[[ restrict = { [3610] = true, } dump_font = true ]]-- function startup(file_in) if loadmodule then loadmodule "lualibs" loadmodule "luahandle" loadmodule "luacd" loadmodule "luaslz" loadmodule "luahttp" loadmodule "luaxml" iso_in = cdabstract(file_in or "VP-SQUISH.bin") cdutil = cdutils(iso_in) else error("This can only be used with lua-interface now...") end end num_entries = 5120 fkey_size = 256 skey_size = 16 log_file = nil function log(str) print(str) log_file:write(str .. "\n") end function load_index() local vp_sec, cfat, csiz, fat, siz, fkey, skey, fkey_pos, skey_pos vp_sec = 150 cfat = cdutil:cdfile(vp_sec, num_entries * 4) csiz = cdutil:cdfile(vp_sec + num_entries * 4 / 2048, num_entries) fat = Buffer(true) siz = Buffer(true) fkey = Buffer(true) skey = Buffer(true) cfat:seek(num_entries * 4 - fkey_size) csiz:seek(num_entries - skey_size) fkey:copyfrom(cfat, fkey_size) skey:copyfrom(csiz, skey_size) cfat:seek(0) csiz:seek(0) fkey_pos = 0 skey_pos = 0 print "Decrypting index..." for i = 0, (num_entries * 4 - 1) do fat:writeU8(xorB(cfat:readU8(), fkey:readU8())) fkey_pos = fkey_pos + 1 if fkey_pos == fkey_size then fkey_pos = 0 fkey:seek(0) end end for i = 0, (num_entries - 1) do siz:writeU8(xorB(csiz:readU8(), skey:readU8())) skey_pos = skey_pos + 1 if skey_pos == skey_size then skey_pos = 0 skey:seek(0) end end cfat:destroy() csiz:destroy() fkey:destroy() skey:destroy() print "Done." local o, m, s, f, size_high, size for k, v in pairs(VP_maintypes) do mkdir("DUMP/" .. v) end fat:seek(4) siz:seek(1) index = {} for i = 1, (num_entries - 1) do m = fat:readU8() s = fat:readU8() f = fat:readU8() size_high = fat:readU8() sector = from_MSF(m, s, f) size = siz:readU8() + size_high * 256 if (sector ~= 4294967146) then log("Entry " .. i .. ", sector " .. sector .. ", nsectors = " .. size) index[i] = { sector = sector, size = size } end end fat:destroy() siz:destroy() return index end function dump_single_file(fname, h, size, ext, handler) local sig = h:readU32() h:seek(-4, SEEK_CUR) local is_slz = sig == 0x05a4c53 or sig == 0x15a4c53 or sig == 0x25a4c53 or sig == 0x35a4c53 local is_tim = sig == 0x00000010 local eof = false if is_slz then log("Dumping " .. fname .. "." .. ext .. ".slz") o = Output(fname .. "." .. ext .. ".slz") o:copyfrom(h, size) o:destroy() h:seek(-size, SEEK_CUR) local num, esiz, stype = 1 log("Extracting " .. fname .. ".slz") while not eof do local snum snum = string.format("-%02i", num) if num ~= 1 then log("... file " .. num .. " to " .. fname .. snum .. "." .. ext) end o = Buffer(true) local tell = h:tell() esiz, stype = slz_decomp(h, o) h:seek(tell + esiz) dump_single_file(fname .. snum, o, o:getsize(), ext, handler) o:destroy() eof = esiz == 0 num = num + 1 end else if is_tim and ext ~= "tim" and not handler then ext = ext .. ".magic.tim" end log("Dumping " .. fname .. "." .. ext) o = Output(fname .. "." .. ext) local tell = h:tell() o:copyfrom(h, size) h:seek(tell) o:destroy() if handler then handler(fname, h, size, ext) end end end function extract_arcgfx(fname, h, size, ext) log("Dumping " .. fname .. "." .. ext) o = Output(fname .. "." .. ext) o:copyfrom(h, size) o:destroy() h:seek(-size, SEEK_CUR) log("Extracting " .. fname .. " - format 'arcgfx'") mkdir(fname) local nfiles = h:readU32() local extra = h:readU32() o = Output(fname .. "/--extra.bin") o:writeU32(extra) o:destroy() o = Output(fname .. string.format("/0000-%08X", extra)) o:writeU32(extra) o:destroy() index = {} for i = 1, nfiles do index[i] = {} index[i].extra = h:readU32() index[i].size = h:readU32() end local tell for i = 1, nfiles do tell = h:tell() dump_single_file(fname .. string.format("/%04i-%08X", i, index[i].extra), h, index[i].size, ext) h:seek(tell + index[i].size) end end function resolve_font(font) local r = {} for k, v in pairs(font) do local g = glyphes[v] if not g then error("Glyphe " .. v .. " not found!") end r[k] = g.attr.Text end return r end function extract_font(fname, h) local n_glyphes = h:readU32() local height = h:readU32() local widths = {} for i = 1, n_glyphes do widths[i] = h:readU8() end local o local r = {} if dump_glyph then mkdir(fname) end for i = 1, n_glyphes do if dump_glyph then o = Output(fname .. string.format("/%03i.glyph", i)) o:copyfrom(h, height * 2) o:destroy() h:seek(-height * 2, SEEK_CUR) o = Output(fname .. string.format("/%03i.raw", i)) local bits for j = 1, height do bits = h:readU16() for k = 1, height do -- should be widths[i] bits = shl(bits, 1) local bit = andB(bits, 65536) ~= 0 o:writeU8(bit and 0xff or 0) end end o:destroy() h:seek(-height * 2, SEEK_CUR) end local b = Buffer(true) b:copyfrom(h, height * 2) r[i] = Base64Encode(b) b:destroy() end return r end function dump_special(out, script, code) if code == 0 then out:write("\n") elseif code == 1 then out:write("\n\n") elseif code == 3 then local speed = script:readU8() if speed == 255 then out:write('') else out:write('') end elseif code == 5 then out:write('') elseif code == 14 then out:write('') elseif code == 4 then out:write('') else local a1, a2 if code == 3 or code == 4 or code == 7 or code == 8 or code == 14 then a1 = script:readU8() out:write('') elseif code == 6 or code == 12 or code == 17 or code == 19 then a1 = script:readU8() a2 = script:readU8() out:write('') else out:write('') end end end function extract_char(out, script, lookup) local c = script:readU8() if c == 0 then return false end if c >= 0x80 then c = (c - 0x80) + (script:readU8() * 128) end if c >= 0x4000 then dump_special(out, script, c - 0x4000) else local l = lookup[c] if not l and not sloppy_extract then error("Lookup failed for character " .. c) end if not l then out:write('') else out:write(xml_escape(l)) end end return true end function extract_script(fname, script, font) log("Dumping script " .. fname) mkdir(fname) mkdir(fname .. "/EXTRA") local font = extract_font(fname .. "/FONT", font) local lookup = resolve_font(font) local size_1 = script:readU32() local u1 = script:readU32() local u2 = script:readU32() local n_ptrs = script:readU32() local u3 = script:readU32() local u4 = script:readU32() local u5 = script:readU32() local o o = Output(fname .. "/EXTRA/00-head1-" .. string.format("%08X-%08X", u1, u2) .. ".bin") o:writeU32(u1) o:writeU32(u2) 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:writeU32(u2) 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() local ptrs = {} for i = 1, n_ptrs do ptrs = script:readU16() end o = Output(fname .. "-script.xml") o:write('\n') o:destroy() end function extract_archive(fname, h, size, ext) log("Dumping " .. fname .. "." .. ext) local o = Output(fname .. "." .. ext) o:copyfrom(h, size) o:destroy() h:seek(-size, SEEK_CUR) log("Extracting " .. fname .. " - format 'archive'") mkdir(fname) local nfiles = h:readU32() local offset = h:readU32() if nfiles * 8 + 8 ~= offset then error("Bad archive format.") end index = {} for i = 1, nfiles do index[i] = {} index[i].ftype = h:readU32() index[i].size = h:readU32() end local tell for i = 1, nfiles do tell = h:tell() local handler = nil local script = nil local font = nil local counter = 1 if index[i].ftype == 4 then log("Preparing to dump script...") script = Buffer(true) font = Buffer(true) handler = function(fname, h, size, ext) if counter == 1 then log("Taking script...") script:copyfrom(h) elseif counter == 2 then log("Taking font...") font:copyfrom(h) else error("Too many files.") end counter = counter + 1 end end dump_single_file(fname .. string.format("/%04i-%08X", i, index[i].ftype), h, index[i].size, ext, handler) if handler then extract_script(fname .. string.format("/%04i", i), script, font) script:destroy() font:destroy() end h:seek(tell + index[i].size) end end function do_dump(i) if not restrict then return true end return restrict[i] end function dump_files(index, map) print("Dumping files...") local file_in, sig, is_slz, o, mode, dir, ftype for i = 1, (num_entries - 1) do if index[i] and do_dump(i) then if map[i] then mode = map[i].mode dir = map[i].dir ext = map[i].ext ftype = map[i].ftype else mode = MODE2_FORM1 dir = "UNKNOWN" ext = "out" ftype = nil end if not mode then mode = MODE2_FORM1 end if not ext then ext = "out" end if not dir then dir = "UNKNOWN" end file_in = cdutil:cdfile(index[i].sector, index[i].size * sec_sizes[mode]) mkdir("DUMP/" .. dir) if not ftype then dump_single_file("DUMP/" .. dir .. string.format("/%04i", i), file_in, file_in:getsize(), ext) elseif ftype == "arcgfx" then extract_arcgfx("DUMP/" .. dir .. string.format("/%04i", i), file_in, file_in:getsize(), ext) elseif ftype == "archive" then extract_archive("DUMP/" .. dir .. string.format("/%04i", i), file_in, file_in:getsize(), ext) else error("Unknow ftype: " .. ftype) end file_in:destroy() end end end function main(...) startup(...) local tglyphes = xml.LoadHandle(Input "VP-database.xml")[1] glyphes = {} for k, v in ipairs(tglyphes) do glyphes[v.attr.Data] = v end mkdir "DUMP" log_file = Output "DUMP/log.txt" local index = load_index() dump_files(index, VP_map) log_file:destroy() end