diff --git a/plugins/cheat/init.lua b/plugins/cheat/init.lua index 63bd76271f2..0bd9427f8c0 100644 --- a/plugins/cheat/init.lua +++ b/plugins/cheat/init.lua @@ -23,12 +23,16 @@ -- }, -- "screen": { -- "varname": "tag" --- }, +-- }, -- ... -- "region": { -- "varname": "tag", -- ... -- }, +-- "ram": { +-- "varname": "tag", +-- ... +-- }, -- "script": { -- "on|off|run|change": "script", -- ... @@ -159,7 +163,7 @@ function cheat.startplugin() script = load(script, cheat.desc .. name, "t", cheat.cheat_env) if not script then print("error loading cheat script: " .. cheat.desc) - cheat = { desc = cheat.desc .. "error" } + cheat = { desc = cheat.desc .. " error" } return end cheat.script[name] = script @@ -174,7 +178,7 @@ function cheat.startplugin() cpu = manager:machine().devices[space.tag] if not cpu then print("error loading cheat script: " .. cheat.desc) - cheat = { desc = cheat.desc .. "error" } + cheat = { desc = cheat.desc .. " error" } return end if space.type then @@ -184,7 +188,7 @@ function cheat.startplugin() end if not mem then print("error loading cheat script: " .. cheat.desc) - cheat = { desc = cheat.desc .. "error" } + cheat = { desc = cheat.desc .. " error" } return end cheat.cheat_env[name] = mem @@ -207,12 +211,24 @@ function cheat.startplugin() mem = manager:machine():memory().regions[region] if not mem then print("error loading cheat script: " .. cheat.desc) - cheat = nil + cheat = { desc = cheat.desc .. " error" } return end cheat.cheat_env[name] = mem end end + if cheat.ram then + for name, ram in pairs(cheat.ram) do + local ram + ram = manager:machine().devices[ram] + if not ram then + print("error loading cheat script: " .. cheat.desc) + cheat = { desc = cheat.desc .. " error" } + return + end + cheat.cheat_env[name] = emu.item(ram.items["0/pointer"]) + end + end local param = cheat.parameter if not param then return diff --git a/plugins/cheatfind/init.lua b/plugins/cheatfind/init.lua index d2c29c7ab53..a777a908e36 100644 --- a/plugins/cheatfind/init.lua +++ b/plugins/cheatfind/init.lua @@ -63,6 +63,7 @@ function cheatfind.startplugin() -- compare two data blocks function cheat.comp(olddata, newdata, oper, val, signed, width, endian) local ret = {} + local ref = {} -- this is a helper for comparing two match lists local format = "" local size = olddata.size @@ -73,27 +74,27 @@ function cheatfind.startplugin() end if width == 16 then size = size & 0xfffe - if signed == "signed" then + if signed == 1 then format = format .. "h" else format = format .. "H" end elseif width == 32 then size = size & 0xfffc - if signed == "signed" then + if signed == 1 then format = format .. "l" else format = format .. "L" end elseif width == 64 then size = size & 0xfff8 - if signed == "signed" then + if signed == 1 then format = format .. "j" else format = format .. "J" end else - if signed == "signed" then + if signed == 1 then format = format .. "b" else format = format .. "B" @@ -115,6 +116,7 @@ function cheatfind.startplugin() ret[#ret + 1] = { addr = olddata.start + i - 1, oldval = old, newval = new} + ref[ret[#ret].addr] = #ret end end end @@ -127,6 +129,7 @@ function cheatfind.startplugin() ret[#ret + 1] = { addr = olddata.start + i - 1, oldval = old, newval = new} + ref[ret[#ret].addr] = #ret end end end @@ -138,12 +141,66 @@ function cheatfind.startplugin() ret[#ret + 1] = { addr = olddata.start + i - 1, oldval = old, newval = new} + ref[ret[#ret].addr] = #ret + end + end + elseif oper == "!" or oper == "ne" then + for i = 1, size do + local old = string.unpack(format, olddata.block, i) + local new = string.unpack(format, newdata.block, i) + if old ~= new then + ret[#ret + 1] = { addr = olddata.start + i - 1, + oldval = old, + newval = new} + ref[ret[#ret].addr] = #ret + end + end + elseif oper == "^" or oper == "xor" then + if val >= width then + return {} + end + for i = 1, size do + local old = string.unpack(format, olddata.block, i) + local new = string.unpack(format, newdata.block, i) + if ((old ~ new) & (1 << val)) ~= 0 then + ret[#ret + 1] = { addr = olddata.start + i - 1, + oldval = old, + newval = new} + ref[ret[#ret].addr] = #ret end end end - return ret + return ret, ref end + -- compare successive pairs of blocks from a table returning only addresses that match every comparison + function cheat.compall(dattable, oper, val, signed, width, endian) + if #dattable < 2 then + return {} + end + local matches, refs = cheat.comp(dattable[1], dattable[2], oper, val, signed, width, endian) + local nonmatch = {} + for i = 2, #dattable - 1 do + local matchnext, refsnext = cheat.comp(dattable[i], dattable[i+1], oper, val, signed, width, endian) + for addr, ref in pairs(refs) do + if not refsnext[addr] then + nonmatch[ref] = true + refs[addr] = nil + else + matches[ref].newval = matchnext[refsnext[addr]].newval + end + end + end + local resort = {} + for num, match in pairs(matches) do + if not nonmatch[num] then + resort[#resort + 1] = match + end + end + return resort + end + + -- compare a data block to the current state function cheat.compcur(olddata, oper, val, signed, width, endian) local newdata = cheat.save(olddata.space, olddata.start, olddata.size, olddata.space) @@ -154,11 +211,13 @@ function cheatfind.startplugin() local devtable = {} local devsel = 1 + local devcur = 1 local bitwidth = 3 local signed = 0 local endian = 0 - local optable = { "<", ">", "=" } + local optable = { "<", ">", "=", "!", "^" } local opsel = 1 + local bit = 0 local value = 0 local leftop = 1 local rightop = 0 @@ -166,19 +225,20 @@ function cheatfind.startplugin() local matchsel = 1 local menu_blocks = {} local midx = { region = 1, init = 2, save = 3, width = 5, signed = 6, endian = 7, op = 8, val = 9, - lop = 10, rop = 11, comp = 12, match = 14 } + lop = 10, rop = 11, comp = 12, match = 14 } local function start() devtable = {} devsel = 1 - devsel = 1 + devcur = 1 bitwidth = 3 signed = 0 endian = 0 opsel = 1 + bit = 0 value = 0 leftop = 1 - rightop = 0 + rightop = 1 matches = {} matchsel = 1 menu_blocks = {} @@ -218,14 +278,14 @@ function cheatfind.startplugin() end end - menu[midx.region] = { "CPU or RAM", devtable[devsel].tag, "" } + menu[midx.region] = { "CPU or RAM", devtable[devsel].tag, 0 } if #devtable == 1 then menu[midx.region][3] = 0 else menu_lim(devsel, 1, #devtable, menu[midx.region]) end menu[midx.init] = { "Init", "", 0 } - if next(menu_blocks) then + if #menu_blocks ~= 0 then menu[midx.save] = { "Save current", "", 0 } menu[midx.save + 1] = { "---", "", "off" } menu[midx.width] = { "Bit Width", 1 << bitwidth , 0 } @@ -242,24 +302,28 @@ function cheatfind.startplugin() end menu[midx.op] = { "Operator", optable[opsel], "" } menu_lim(opsel, 1, #optable, menu[midx.op]) - menu[midx.val] = { "Value", value, "" } - menu_lim(value, 0, 100, menu[midx.val]) -- max value? - if value == 0 then - menu[midx.val][2] = "Any" + if optable[opsel] == "^" then + menu[midx.val] = { "Bit", bit, "" } + menu_lim(bit, 0, (1 << bitwidth), menu[midx.val]) + else + menu[midx.val] = { "Value", value, "" } + menu_lim(value, 0, 100, menu[midx.val]) -- max value? + if value == 0 then + menu[midx.val][2] = "Any" + end end menu[midx.lop] = { "Left operand", leftop, "" } - if #menu_blocks == 1 then - menu[midx.lop][3] = 0 - else - menu_lim(leftop, 1, #menu_blocks, menu[midx.lop]) + menu_lim(leftop, 1, #menu_blocks[1] + 1, menu[midx.lop]) + if leftop == #menu_blocks[1] + 1 then + menu[midx.lop][2] = "All" end menu[midx.rop] = { "Right operand", rightop, "" } - menu_lim(rightop, 0, #menu_blocks, menu[midx.rop]) - if rightop == 0 then + menu_lim(rightop, 1, #menu_blocks[1] + 1, menu[midx.rop]) + if rightop == #menu_blocks[1] + 1 then menu[midx.rop][2] = "Current" end menu[midx.comp] = { "Compare", "", 0 } - if next(matches) then + if #matches ~= 0 then menu[midx.comp + 1] = { "---", "", "off" } menu[midx.match] = { "Match block", matchsel, "" } if #matches == 1 then @@ -304,70 +368,87 @@ function cheatfind.startplugin() end if index == midx.region then + if (event == "left" or event == "right") and #menu_blocks ~= 0 then + manager:machine():popmessage("Changes to this only take effect when Init is selected") + end devsel = incdec(devsel, 1, #devtable) + return true elseif index == midx.init then if event == "select" then - menu_blocks = {{}} + menu_blocks = {} matches = {} - for num, region in ipairs(devtable[devsel].ram) do - menu_blocks[1][num] = cheat.save(devtable[devsel].space, region.offset, region.size) - end - manager:machine():popmessage("Data cleared and current state saved") - ret = true - end - elseif index == midx.init then - if event == "select" then - menu_blocks = {{}} - matches = {} - for num, region in ipairs(devtable[devsel].ram) do - menu_blocks[1][num] = cheat.save(devtable[devsel].space, region.offset, region.size) + for num, region in ipairs(devtable[devcur].ram) do + menu_blocks[num] = {} + menu_blocks[num][1] = cheat.save(devtable[devcur].space, region.offset, region.size) end manager:machine():popmessage("Data cleared and current state saved") ret = true end + devcur = devsel elseif index == midx.save then if event == "select" then - menu_blocks[#menu_blocks + 1] = {} - for num, region in ipairs(devtable[devsel].ram) do - menu_blocks[#menu_blocks][num] = cheat.save(devtable[devsel].space, region.offset, region.size) + for num, region in ipairs(devtable[devcur].ram) do + menu_blocks[num][#menu_blocks[num] + 1] = cheat.save(devtable[devcur].space, region.offset, region.size) end manager:machine():popmessage("Current state saved") ret = true end elseif index == midx.width then bitwidth = incdec(bitwidth, 3, 6) + if event == "left" or event == "right" then + bit = 0 + end elseif index == midx.signed then signed = incdec(signed, 0, 1) elseif index == midx.endian then endian = incdec(endian, 0, 1) elseif index == midx.op then - if event == "up" or event == "down" or event == "comment" then + if event == "left" or event == "right" or event == "comment" then if optable[opsel] == "<" then manager:machine():popmessage("Left less than right, value is difference") elseif optable[opsel] == ">" then manager:machine():popmessage("Left greater than right, value is difference") elseif optable[opsel] == "=" then manager:machine():popmessage("Left equal to right, value is ignored") + elseif optable[opsel] == "!" then + manager:machine():popmessage("Left not equal to right, value is ignored") + elseif optable[opsel] == "^" then + manager:machine():popmessage("Left bit different than right") end - else - opsel = incdec(opsel, 1, #optable) end + opsel = incdec(opsel, 1, #optable) elseif index == midx.val then - value = incdec(value, 0, 100) + if optable[opsel] == "^" then + bit = incdec(bit, 0, (1 << bitwidth)) + else + value = incdec(value, 0, 100) + end elseif index == midx.lop then - leftop = incdec(leftop, 1, #menu_blocks) + leftop = incdec(leftop, 1, #menu_blocks[1] + 1) + if (event == "left" or event == "right" or event == "comment") and leftop == #menu_blocks[1] + 1 then + manager:machine():popmessage("Any compares every saved data pair and returns only matches that are true for all. Right operand is ignored.") + end elseif index == midx.rop then - rightop = incdec(rightop, 0, #menu_blocks) + rightop = incdec(rightop, 1, #menu_blocks[1] + 1) elseif index == midx.comp then if event == "select" then matches = {} - for num = 1, #menu_blocks[1] do - if rightop == 0 then - matches[#matches + 1] = cheat.compcur(menu_blocks[leftop][num], optable[opsel], value, - signed, 1 << bitwidth, endian) + local val + if optable[opsel] == "^" then + val = bit + else + val = value + end + for num = 1, #menu_blocks do + if leftop == #menu_blocks[1] + 1 then + matches[#matches + 1] = cheat.compall(menu_blocks[num], optable[opsel], val, signed, + 1 << bitwidth, endian) + elseif rightop == #menu_blocks[1] + 1 then + matches[#matches + 1] = cheat.compcur(menu_blocks[num][leftop], optable[opsel], val, + signed, 1 << bitwidth, endian) else - matches[#matches + 1] = cheat.comp(menu_blocks[leftop][num], menu_blocks[rightop][num], - optable[opsel], value, signed, 1 << bitwidth, endian) + matches[#matches + 1] = cheat.comp(menu_blocks[num][leftop], menu_blocks[num][rightop], + optable[opsel], val, signed, 1 << bitwidth, endian) end end ret = true @@ -379,6 +460,7 @@ function cheatfind.startplugin() -- write out a script? end end + devsel = devcur return ret end emu.register_menu(menu_callback, menu_populate, "Cheat Finder")