diff --git a/Lua/actor lister.lua b/Lua/actor lister.lua index 54b85d2..e45ebae 100755 --- a/Lua/actor lister.lua +++ b/Lua/actor lister.lua @@ -1,8 +1,8 @@ require = require "depend" require "boilerplate" require "addrs.init" -require "classes" require "messages" +require "classes" -- check for errors in the actor linked lists local validate = false @@ -37,13 +37,6 @@ local debug_watch = mm and { -- using a template to offset from will do for now. local actor_t = Actor(0) -local suffix = oot and " oot" or "" -local actor_names = require("data.actor names"..suffix) -local damage_names = require("data.damage names"..suffix) - --- hack to avoid N64 logo spitting errors -local stupid = addrs.actor_counts[0].addr - 0x8 - function sort_by_key(t) local sorted = {} local i = 1 @@ -223,191 +216,17 @@ function collect_actors() return any > 0, actors_by_type, new_counts end -ActorLister = Class() -function ActorLister:init() - self.before = 0 - self.wait = 0 - self.focus_at = 2 - self.focus_ai = 0 - self.seen_once = {} - self.seen_strs = {} - self.seen_strs_sorted = {} - self.input = InputHandler{ - enter = "P1 L", - up = "P1 DPad U", - down = "P1 DPad D", - left = "P1 DPad L", - right = "P1 DPad R", - } -end +local input_handler = InputHandler{ + enter = "P1 L", + up = "P1 DPad U", + down = "P1 DPad D", + left = "P1 DPad L", + right = "P1 DPad R", +} -function ActorLister:wipe() - if #self.seen_strs_sorted > 0 then - print() - print("# actors wiped #") - print() - end - self.seen_once = {} - self.seen_strs = {} - self.seen_strs_sorted = {} -end - -function ActorLister:run(now) - local game_counts = nil - local seen = {} - local cursor, target - - local ctrl, pressed = self.input:update() - - if pressed.left then self.focus_ai = self.focus_ai - 1 end - if pressed.right then self.focus_ai = self.focus_ai + 1 end - if pressed.down then - -- follow Link again - self.focus_at = 2 - self.focus_ai = 0 - end - - if R4(stupid) ~= 0 then - T_BR(0, 0, "red", "stupid") - return - end - - local any, actors_by_type, new_counts = collect_actors() - - if not any then - self:wipe() - else - while self.focus_ai < 0 do - self.focus_at = (self.focus_at - 1) % 12 - self.focus_ai = new_counts[self.focus_at] - 1 - end - while self.focus_ai >= new_counts[self.focus_at] do - self.focus_at = (self.focus_at + 1) % 12 - self.focus_ai = 0 - end - cursor = deref(addrs.z_cursor_actor()) - target = deref(addrs.z_target_actor()) - end - - local focus_link = self.focus_at == 2 and self.focus_ai == 0 - if debug_mode then focus_link = false end - local needs_update = false - - for at, actors in pairs(actors_by_type) do - for ai, addr in pairs(actors) do -- FIXME: sorry for this pseudo-indent - local var = R2(addr + actor_t.var.addr) - local hp = R1(addr + actor_t.hp.addr) - local num = R2(addr + actor_t.num.addr) - local name = actor_names[num] - local fa = addr + actor_t.flags.addr - local flags = R4(fa) - - local focus_this = at == self.focus_at and ai == self.focus_ai - - seen[num] = true - - if not name then - name = "NEW" - actor_names[num] = name - dprint(("\t[0x%03X]=\"NEW\","):format(num)) - end - - if not self.seen_once[num] then - self.seen_once[num] = now - needs_update = true - local str - if name:sub(1,1) == "?" then - str = ("%s (%03X)"):format(name, num) - else - str = ("%s"):format(name) - end - self.seen_strs[num] = str - dprint(str) - end - - local focal = false - if not debug_mode then - focal = focal or (focus_this and not focus_link) - focal = focal or (focus_link and addr == target) - else - if target then - focal = addr == target - else - focal = focus_this - end - end - if focal then - local actor = { - name = name, - addr = addr, - ai = ai, - type_count = new_counts[at], - - at = at, - var = var, - flags = flags, - hp = hp, - num = num, - } - focus(actor, pressed.up) - end - - if focus_this then - W1(addrs.camera_target.addr, 0x80) - W3(addrs.camera_target.addr + 1, addr) - end - - -- make all actors z-targetable - if not (focus_this and focus_link) then - flags = bit.bor(flags, 0x00000001) - W4(fa, flags) - end - end - end - - if needs_update then - self.seen_strs_sorted = sort_by_key(self.seen_strs) - end - - if focus_link and not target then - for i, t in ipairs(self.seen_strs_sorted) do - local color = 'white' - if self.seen_once[t.k] and now - 60 <= self.seen_once[t.k] then - color = 'lime' - end - if not seen[t.k] then - color = 'orange' - end - T_TL(0, i - 1, color, t.v) - end - end - - T_BR(0, 0, nil, "unique:%3i", #self.seen_strs_sorted) - - if any then - local z = target or cursor - if z then - local num = R2(z) - T_TR(0, 0, nil, self.seen_strs[num]) - end - end -end - --- TODO: abstract to wrapper class or something -function ActorLister:runwrap(now) - if now < self.before then self.wait = 2 end - self.before = now - if self.wait > 0 then - -- prevent script from lagging reversing - self.wait = self.wait - 1 - if self.wait == 0 then self:wipe() end - else - self:run(now) - end -end - -al = ActorLister() -event.onloadstate(function() al:wipe() end, 'actor wipe') +local al = ActorLister(input_handler, debug_mode) +event.onexit(function() al = nil end, 'actor cleanup') +event.onloadstate(function() if al then al:wipe() end end, 'actor wipe') while oot or mm do local now = emu.framecount() al:runwrap(now) diff --git a/Lua/boilerplate.lua b/Lua/boilerplate.lua index c0a4e71..87824e2 100755 --- a/Lua/boilerplate.lua +++ b/Lua/boilerplate.lua @@ -57,6 +57,11 @@ function A(addr, atype) end Class = function(inherit) + --[[ don't entirely like the idea but leaving it here + if type(inherit) == 'string' then + inherit = require("classes."..inherit) + end + --]] return setmetatable({}, { __call = function(self, ...) local obj = setmetatable({}, {__index = self}) diff --git a/Lua/classes.lua b/Lua/classes.lua index ca224b6..17d1337 100644 --- a/Lua/classes.lua +++ b/Lua/classes.lua @@ -1,72 +1,17 @@ -require "serialize" +-- for lazy people. +-- populate the global namespace with all available classes, +-- excluding menu/interface classes. -Monitor = Class() -function Monitor:init(name, a) - self.name = name - self.begin = a.addr - self.len = a.type - self.once = false - self.old_bytes = {} - self.modified = {} - self.dirty = false -end +require "boilerplate" -function Monitor:read() - -- bizhawk has an off-by-one bug where this returns length + 1 bytes - local raw = mainmemory.readbyterange(self.begin, self.len-1) - local bytes = {} - local begin = self.begin - for k, v in pairs(raw) do - bytes[k - begin] = v - end - return bytes -end +local classes = { + "Monitor", + "ByteMonitor", + "FlagMonitor", + "ActorLister", + "InputHandler", +} -function Monitor:diff() - local bytes = self:read() - local old_bytes = self.old_bytes - if self.once then - for i, v in pairs(bytes) do - local x = v - local x1 = old_bytes[i] - if x ~= x1 then - self:mark(i, x, x1) - end - end - end - self.old_bytes = bytes - self.once = true -end - -function Monitor:load(fn) - self.modified = deserialize(fn) or {} - self.dirty = false - self.fn = fn -end - -function Monitor:save(fn) - if self.dirty then - serialize(fn or self.fn, self.modified) - self.dirty = false - end -end - -InputHandler = Class() -function InputHandler:init(binds) - self.binds = binds - self.old_ctrl = {} -end - -function InputHandler:update() - local ctrl = {} - local pressed = {} - local j = joypad.getimmediate() - for k, v in pairs(self.binds) do - ctrl[k] = j[v] - end - for k, v in pairs(ctrl) do - pressed[k] = ctrl[k] and not self.old_ctrl[k] - end - self.old_ctrl = ctrl - return ctrl, pressed +for _, class in ipairs(classes) do + _G[class] = require("classes."..class) end diff --git a/Lua/classes/ActorLister.lua b/Lua/classes/ActorLister.lua new file mode 100644 index 0000000..c91cff8 --- /dev/null +++ b/Lua/classes/ActorLister.lua @@ -0,0 +1,192 @@ +local print = dprint or print + +-- hack to avoid N64 logo spitting errors +local stupid = addrs.actor_counts[0].addr - 0x8 + +-- creating an object every time is a bit slow, so +-- using a template to offset from will do for now. +local actor_t = Actor(0) + +local suffix = oot and " oot" or "" +local actor_names = require("data.actor names"..suffix) +local damage_names = require("data.damage names"..suffix) + +local ActorLister = Class() +function ActorLister:init(input_handler, debug_mode) + self.before = 0 + self.wait = 0 + self.focus_at = 2 + self.focus_ai = 0 + self.seen_once = {} + self.seen_strs = {} + self.seen_strs_sorted = {} + self.input = input_handler + self.debug_mode = debug_mode +end + +function ActorLister:wipe() + if #self.seen_strs_sorted > 0 then + print() + print("# actors wiped #") + print() + end + self.seen_once = {} + self.seen_strs = {} + self.seen_strs_sorted = {} +end + +function ActorLister:run(now) + local game_counts = nil + local seen = {} + local cursor, target + + local ctrl, pressed = self.input:update() + + if pressed.left then self.focus_ai = self.focus_ai - 1 end + if pressed.right then self.focus_ai = self.focus_ai + 1 end + if pressed.down then + -- follow Link again + self.focus_at = 2 + self.focus_ai = 0 + end + + if R4(stupid) ~= 0 then + T_BR(0, 0, "red", "stupid") + return + end + + local any, actors_by_type, new_counts = collect_actors() + + if not any then + self:wipe() + else + while self.focus_ai < 0 do + self.focus_at = (self.focus_at - 1) % 12 + self.focus_ai = new_counts[self.focus_at] - 1 + end + while self.focus_ai >= new_counts[self.focus_at] do + self.focus_at = (self.focus_at + 1) % 12 + self.focus_ai = 0 + end + cursor = deref(addrs.z_cursor_actor()) + target = deref(addrs.z_target_actor()) + end + + local focus_link = self.focus_at == 2 and self.focus_ai == 0 + if self.debug_mode then focus_link = false end + local needs_update = false + + for at, actors in pairs(actors_by_type) do + for ai, addr in pairs(actors) do -- FIXME: sorry for this pseudo-indent + local var = R2(addr + actor_t.var.addr) + local hp = R1(addr + actor_t.hp.addr) + local num = R2(addr + actor_t.num.addr) + local name = actor_names[num] + local fa = addr + actor_t.flags.addr + local flags = R4(fa) + + local focus_this = at == self.focus_at and ai == self.focus_ai + + seen[num] = true + + if not name then + name = "NEW" + actor_names[num] = name + print(("\t[0x%03X]=\"NEW\","):format(num)) + end + + if not self.seen_once[num] then + self.seen_once[num] = now + needs_update = true + local str + if name:sub(1,1) == "?" then + str = ("%s (%03X)"):format(name, num) + else + str = ("%s"):format(name) + end + self.seen_strs[num] = str + print(str) + end + + local focal = false + if not self.debug_mode then + focal = focal or (focus_this and not focus_link) + focal = focal or (focus_link and addr == target) + else + if target then + focal = addr == target + else + focal = focus_this + end + end + if focal then + local actor = { + name = name, + addr = addr, + ai = ai, + type_count = new_counts[at], + + at = at, + var = var, + flags = flags, + hp = hp, + num = num, + } + focus(actor, pressed.up) -- FIXME: global + end + + if focus_this then + W1(addrs.camera_target.addr, 0x80) + W3(addrs.camera_target.addr + 1, addr) + end + + -- make all actors z-targetable + if not (focus_this and focus_link) then + flags = bit.bor(flags, 0x00000001) + W4(fa, flags) + end + end + end + + if needs_update then + self.seen_strs_sorted = sort_by_key(self.seen_strs) + end + + if focus_link and not target then + for i, t in ipairs(self.seen_strs_sorted) do + local color = 'white' + if self.seen_once[t.k] and now - 60 <= self.seen_once[t.k] then + color = 'lime' + end + if not seen[t.k] then + color = 'orange' + end + T_TL(0, i - 1, color, t.v) + end + end + + T_BR(0, 0, nil, "unique:%3i", #self.seen_strs_sorted) + + if any then + local z = target or cursor + if z then + local num = R2(z) + T_TR(0, 0, nil, self.seen_strs[num]) + end + end +end + +-- TODO: abstract to wrapper class or something +function ActorLister:runwrap(now) + if now < self.before then self.wait = 2 end + self.before = now + if self.wait > 0 then + -- prevent script from lagging reversing + self.wait = self.wait - 1 + if self.wait == 0 then self:wipe() end + else + self:run(now) + end +end + +return ActorLister diff --git a/Lua/classes/ByteMonitor.lua b/Lua/classes/ByteMonitor.lua new file mode 100644 index 0000000..70dd4bc --- /dev/null +++ b/Lua/classes/ByteMonitor.lua @@ -0,0 +1,19 @@ +local Monitor = require "classes.Monitor" +local ByteMonitor = Class(Monitor) + +function ByteMonitor:mark(i, x, x1) + local now = emu.framecount() + local str = ('%02i=%02X (%s)'):format(i, x, self.name) + if not self.modified[i] then + self.modified[i] = {} + end + if not self.modified[i][x] then + self.modified[i][x] = true + self.dirty = true + str = str..' (NEW!)' + end + printf('%s @%i', str, now) + message(str, 180) +end + +return ByteMonitor diff --git a/Lua/classes/FlagMonitor.lua b/Lua/classes/FlagMonitor.lua new file mode 100644 index 0000000..87168a5 --- /dev/null +++ b/Lua/classes/FlagMonitor.lua @@ -0,0 +1,107 @@ +local printf = dprintf or printf + +local Monitor = require "classes.Monitor" +local FlagMonitor = Class(Monitor) + +function FlagMonitor:init(name, a, ignore) + Monitor.init(self, name, a) + self.ignore = ignore or {} +end + +function FlagMonitor:mark(i, x, x1) + local now = emu.framecount() + local diff = bit.bxor(x, x1) + for which = 0, 7 do + if bit.band(diff, 2^which) ~= 0 then + local state = bit.band(x, 2^which) ~= 0 and 1 or 0 + local str + if self.oot then + local row = math.floor(i/2) + local col = which + (1 - (i % 2))*8 + str = ('%02i,%X=%i (%s)'):format(row, col, state, self.name) + else + str = ('%02i,%i=%i (%s)'):format(i, which, state, self.name) + end + local ib = i*8 + which + local curious = self.modified[ib] == "curious" + if not self.modified[ib] or curious then + self.modified[ib] = true + self.dirty = true + if not curious then + str = str..' (NEW!)' + else + str = str..' (!!!)' + end + end + if not self.ignore[str] then + printf('%s @%i', str, now) + message(str, 180) + end + end + end +end + +function FlagMonitor:dump(current) + local t = current and self:read() or self.modified + + local size = self.oot and 16 or 8 + local rows = math.floor(self.len/size*8) + + local buff = self.name..'\n' + + buff = buff..' \t' + for col = size-1, 0, -1 do + buff = buff..('%X'):format(col) + if col % 4 == 0 then buff = buff..' ' end + end + + for row = 0, rows-1 do + s = ('%02i\t'):format(row) + for col = size-1, 0, -1 do + local B, b = row, col + if size == 16 then + B = row*2 + (col < 8 and 1 or 0) + b = col % 8 + end + local ib = B*8 + b + local v + if current then v = bit.band(t[B], 2^b) > 0 else v = t[ib] end + s = s..(v and '1' or '0') + if col % 4 == 0 then s = s..' ' end + end + buff = buff..'\n'..s + end + + return buff +end + +function FlagMonitor:wipe() + for i = self.begin, self.begin+self.len-1 do + W1(i, 0) + end +end + +function FlagMonitor:set_unknowns() + self.save = function() end -- no clutter + local mod = self.modified_backup + if not mod then + mod = {} + for i, v in pairs(self.modified) do + mod[i] = v + end + self.modified_backup = mod + end + for i = 0, self.len-1 do + local v = R1(self.begin + i) + for which = 0, 7 do + local ib = i*8 + which + if not mod[ib] and bit.band(v, 2^which) == 0 then + v = v + 2^which + end + end + --printf("%04X = %02X", self.begin + i, sum) + W1(self.begin + i, v) + end +end + +return FlagMonitor diff --git a/Lua/classes/InputHandler.lua b/Lua/classes/InputHandler.lua new file mode 100644 index 0000000..9ed49e5 --- /dev/null +++ b/Lua/classes/InputHandler.lua @@ -0,0 +1,21 @@ +local InputHandler = Class() +function InputHandler:init(binds) + self.binds = binds + self.old_ctrl = {} +end + +function InputHandler:update() + local ctrl = {} + local pressed = {} + local j = joypad.getimmediate() + for k, v in pairs(self.binds) do + ctrl[k] = j[v] + end + for k, v in pairs(ctrl) do + pressed[k] = ctrl[k] and not self.old_ctrl[k] + end + self.old_ctrl = ctrl + return ctrl, pressed +end + +return InputHandler diff --git a/Lua/classes/Monitor.lua b/Lua/classes/Monitor.lua new file mode 100644 index 0000000..52b2118 --- /dev/null +++ b/Lua/classes/Monitor.lua @@ -0,0 +1,54 @@ +require "serialize" + +local Monitor = Class() +function Monitor:init(name, a) + self.name = name + self.begin = a.addr + self.len = a.type + self.once = false + self.old_bytes = {} + self.modified = {} + self.dirty = false +end + +function Monitor:read() + -- bizhawk has an off-by-one bug where this returns length + 1 bytes + local raw = mainmemory.readbyterange(self.begin, self.len-1) + local bytes = {} + local begin = self.begin + for k, v in pairs(raw) do + bytes[k - begin] = v + end + return bytes +end + +function Monitor:diff() + local bytes = self:read() + local old_bytes = self.old_bytes + if self.once then + for i, v in pairs(bytes) do + local x = v + local x1 = old_bytes[i] + if x ~= x1 then + self:mark(i, x, x1) + end + end + end + self.old_bytes = bytes + self.once = true +end + +function Monitor:load(fn) + self.modified = deserialize(fn) or {} + self.dirty = false + self.fn = fn +end + +function Monitor:save(fn) + if self.dirty then + serialize(fn or self.fn, self.modified) + self.dirty = false + end +end + +return Monitor diff --git a/Lua/event flag monitor.lua b/Lua/event flag monitor.lua index 53df926..43bd31b 100644 --- a/Lua/event flag monitor.lua +++ b/Lua/event flag monitor.lua @@ -1,10 +1,11 @@ require = require "depend" require "boilerplate" require "addrs.init" -require "classes" require "messages" +require "classes" -local ignore = { +local mm_ignore = { + -- TODO: use list of bytes/bits rather than full strings -- every time a scene (un)loads ['92,7=0 (weg)'] = true, ['92,7=1 (weg)'] = true, @@ -24,107 +25,9 @@ local ignore = { ['28,2=1 (weg)'] = true, } -FlagMonitor = Class(Monitor) - -function FlagMonitor:mark(i, x, x1) - local now = emu.framecount() - local diff = bit.bxor(x, x1) - for which = 0, 7 do - if bit.band(diff, 2^which) ~= 0 then - local state = bit.band(x, 2^which) ~= 0 and 1 or 0 - local str - if self.oot then - local row = math.floor(i/2) - local col = which + (1 - (i % 2))*8 - str = ('%02i,%X=%i (%s)'):format(row, col, state, self.name) - else - str = ('%02i,%i=%i (%s)'):format(i, which, state, self.name) - end - local ib = i*8 + which - local curious = self.modified[ib] == "curious" - if not self.modified[ib] or curious then - self.modified[ib] = true - self.dirty = true - if not curious then - str = str..' (NEW!)' - else - str = str..' (!!!)' - end - end - if not ignore[str] then - dprintf('%s @%i', str, now) - message(str, 180) - end - end - end -end - -function FlagMonitor:dump(current) - local t = current and self:read() or self.modified - - local size = self.oot and 16 or 8 - local rows = math.floor(self.len/size*8) - - local buff = self.name..'\n' - - buff = buff..' \t' - for col = size-1, 0, -1 do - buff = buff..('%X'):format(col) - if col % 4 == 0 then buff = buff..' ' end - end - - for row = 0, rows-1 do - s = ('%02i\t'):format(row) - for col = size-1, 0, -1 do - local B, b = row, col - if size == 16 then - B = row*2 + (col < 8 and 1 or 0) - b = col % 8 - end - local ib = B*8 + b - local v - if current then v = bit.band(t[B], 2^b) > 0 else v = t[ib] end - s = s..(v and '1' or '0') - if col % 4 == 0 then s = s..' ' end - end - buff = buff..'\n'..s - end - - return buff -end - -function FlagMonitor:wipe() - for i = self.begin, self.begin+self.len-1 do - W1(i, 0) - end -end - -function FlagMonitor:set_unknowns() - self.save = function() end -- no clutter - local mod = self.modified_backup - if not mod then - mod = {} - for i, v in pairs(self.modified) do - mod[i] = v - end - self.modified_backup = mod - end - for i = 0, self.len-1 do - local v = R1(self.begin + i) - for which = 0, 7 do - local ib = i*8 + which - if not mod[ib] and bit.band(v, 2^which) == 0 then - v = v + 2^which - end - end - --printf("%04X = %02X", self.begin + i, sum) - W1(self.begin + i, v) - end -end - if mm then - weg = FlagMonitor('weg', addrs.week_event_reg) - inf = FlagMonitor('inf', addrs.event_inf) + weg = FlagMonitor('weg', addrs.week_event_reg, mm_ignore) + inf = FlagMonitor('inf', addrs.event_inf, mm_ignore) --mmb = FlagMonitor('mmb', addrs.mask_mask_bit) -- 100% known, no point weg:load('data/_weg.lua') inf:load('data/_inf.lua') diff --git a/Lua/misc monitor.lua b/Lua/misc monitor.lua index fabe852..da6a30c 100644 --- a/Lua/misc monitor.lua +++ b/Lua/misc monitor.lua @@ -3,23 +3,6 @@ require "addrs.init" require "classes" require "messages" -ByteMonitor = Class(Monitor) - -function ByteMonitor:mark(i, x, x1) - local now = emu.framecount() - local str = ('%02i=%02X (%s)'):format(i, x, self.name) - if not self.modified[i] then - self.modified[i] = {} - end - if not self.modified[i][x] then - self.modified[i][x] = true - self.dirty = true - str = str..' (NEW!)' - end - printf('%s @%i', str, now) - message(str, 180) -end - local unk = ByteMonitor('unk', AL(0xF6, 0x37A)) unk:load('data/_unk.lua') while mm do