Files
gen1recomp/tests/save_compat/_gen1_reference.lua
T
2026-10-01 17:30:52 -04:00

148 lines
4.8 KiB
Lua

local R = {}
local function u8(s, o) return s:byte(o + 1) or 0 end
local function be(s, o, n)
local v = 0
for i = 0, n - 1 do v = v * 256 + u8(s, o + i) end
return v
end
local function hex(s, o, n)
local t = {}
for i = 0, n - 1 do t[#t + 1] = ("%02X"):format(u8(s, o + i)) end
return table.concat(t)
end
local function name(s, o)
local t = {}
for i = 0, 10 do
local b = u8(s, o + i)
if b == 0x50 then break end
t[#t + 1] = ("%02X"):format(b)
end
return table.concat(t)
end
local function bcd(s, o, n)
local v = 0
for i = 0, n - 1 do
local b = u8(s, o + i)
v = v * 100 + math.floor(b / 16) * 10 + b % 16
end
return v
end
local function mon(s, o, party)
local m = {
species = u8(s, o), hp = be(s, o + 1, 2), boxLevel = u8(s, o + 3), status = u8(s, o + 4),
types = hex(s, o + 5, 2), catchRate = u8(s, o + 7), moves = hex(s, o + 8, 4),
otId = be(s, o + 12, 2), exp = be(s, o + 14, 3), statExp = hex(s, o + 17, 10),
dvs = hex(s, o + 27, 2), pp = hex(s, o + 29, 4),
}
if party then
m.level = u8(s, o + 33)
m.stats = hex(s, o + 34, 10)
end
return m
end
local function list(s, base, cap, size, party)
local out = { count = u8(s, base) }
if out.count > cap then out.invalid = true; return out end
local monsAt = base + 2 + cap
local otAt = monsAt + cap * size
local nickAt = otAt + cap * 11
for i = 0, out.count - 1 do
local m = mon(s, monsAt + i * size, party)
m.listSpecies = u8(s, base + 1 + i)
m.ot = name(s, otAt + i * 11)
m.nick = name(s, nickAt + i * 11)
out[#out + 1] = m
end
return out
end
local function rows(s, at, cap)
local out = {}
for i = 0, cap - 1 do
local id = u8(s, at + i * 2)
if id == 0xFF then break end
out[#out + 1] = ("%02X:%02X"):format(id, u8(s, at + i * 2 + 1))
end
return table.concat(out, ",")
end
function R.decode(s)
local d = {
playerName = name(s, 0x2598), rivalName = name(s, 0x25F6), playerId = be(s, 0x2605, 2),
money = bcd(s, 0x25F3, 3), coins = bcd(s, 0x2850, 2), badges = u8(s, 0x2602), options = u8(s, 0x2601),
dexOwned = hex(s, 0x25A3, 19), dexSeen = hex(s, 0x25B6, 19),
bag = rows(s, 0x25CA, 20), bagCount = u8(s, 0x25C9), pc = rows(s, 0x27E7, 50), pcCount = u8(s, 0x27E6),
curMap = u8(s, 0x260A), y = u8(s, 0x260D), x = u8(s, 0x260E), lastMap = u8(s, 0x2611),
boxByte = u8(s, 0x284C), numHoF = u8(s, 0x284E), toggles = hex(s, 0x2852, 32),
hiddenItems = hex(s, 0x299C, 14), walk = u8(s, 0x29AC), townVisited = be(s, 0x29B7, 2),
rivalStarter = u8(s, 0x29C1), playerStarter = u8(s, 0x29C3), blackout = u8(s, 0x29C5),
statusFlags = hex(s, 0x29D4, 13), events = hex(s, 0x29F3, 320),
playTime = hex(s, 0x2CED, 5),
tradeFlags = hex(s, 0x29E3, 2), hiddenCoins = hex(s, 0x29AA, 2), safariSteps = be(s, 0x29B9, 2),
safariBalls = u8(s, 0x2CF3), safariGate = u8(s, 0x28CB), fossil = hex(s, 0x29BB, 2),
trash = hex(s, 0x29EF, 2), palOffset = u8(s, 0x2609), surfHi = hex(s, 0x2741, 2),
yellowPikachu = hex(s, 0x271C, 2), emotion = u8(s, 0x2748),
dayCareInUse = u8(s, 0x2CF4), dayCareName = name(s, 0x2CF5), dayCareOt = name(s, 0x2D00),
dayCareMon = mon(s, 0x2D0B, false),
party = list(s, 0x2F2C, 6, 44, true),
boxes = {},
hof = {},
}
local cur = d.boxByte % 128
local init = d.boxByte >= 128
for b = 0, 11 do
local base = b < 6 and (0x4000 + b * 0x462) or (0x6000 + (b - 6) * 0x462)
if b == cur then
d.boxes[b + 1] = list(s, 0x30C0, 20, 33, false)
elseif init then
d.boxes[b + 1] = list(s, base, 20, 33, false)
else
d.boxes[b + 1] = { count = 0 }
end
end
for t = 0, math.min(d.numHoF, 50) - 1 do
local team = {}
for m = 0, 5 do
local at = 0x0598 + t * 96 + m * 16
if u8(s, at) == 0xFF then break end
team[#team + 1] = ("%02X/%d/%s"):format(u8(s, at), u8(s, at + 1), name(s, at + 2))
end
d.hof[#d.hof + 1] = table.concat(team, ",")
end
return d
end
local function walk(a, b, path, out)
if type(a) ~= "table" or type(b) ~= "table" then
if a ~= b then out[#out + 1] = ("%s: %s -> %s"):format(path, tostring(a), tostring(b)) end
return
end
local keys = {}
for k in pairs(a) do keys[k] = true end
for k in pairs(b) do keys[k] = true end
local sorted = {}
for k in pairs(keys) do sorted[#sorted + 1] = k end
table.sort(sorted, function(x, y) return tostring(x) < tostring(y) end)
for _, k in ipairs(sorted) do walk(a[k], b[k], path .. "." .. tostring(k), out) end
end
function R.compare(a, b, skip)
local out = {}
walk(a, b, "", out)
if skip then
local kept = {}
for _, line in ipairs(out) do
local drop = false
for _, p in ipairs(skip) do if line:match(p) then drop = true end end
if not drop then kept[#kept + 1] = line end
end
out = kept
end
return out
end
return R