mirror of
https://github.com/holub/mame
synced 2025-04-26 02:07:14 +03:00
plugins/cheatfind: make menu layout much more flexible (nw)
This commit is contained in:
parent
75b1f0ca79
commit
216869186c
@ -76,7 +76,18 @@ function cheatfind.startplugin()
|
||||
local ref = {} -- this is a helper for comparing two match lists
|
||||
local bitmask = nil
|
||||
|
||||
local function bne(a, b, val, addr)
|
||||
local cfoper = {
|
||||
lt = function(a, b, val) return (a < b and val == 0) or (val > 0 and (a + val) == b) end,
|
||||
gt = function(a, b, val) return (a > b and val == 0) or (val > 0 and (a - val) == b) end,
|
||||
eq = function(a, b, val) return a == b end,
|
||||
ne = function(a, b, val) return (a ~= b and val == 0) or
|
||||
(val > 0 and ((a - val) == b or (a + val) == b)) end,
|
||||
ltv = function(a, b, val) return a < val end,
|
||||
gtv = function(a, b, val) return a > val end,
|
||||
eqv = function(a, b, val) return a == val end,
|
||||
nev = function(a, b, val) return a ~= val end }
|
||||
|
||||
function cfoper.bne(a, b, val, addr)
|
||||
if type(val) ~= "table" then
|
||||
bitmask = a ~ b
|
||||
return bitmask ~= 0
|
||||
@ -88,7 +99,7 @@ function cheatfind.startplugin()
|
||||
end
|
||||
end
|
||||
|
||||
local function beq(a, b, val, addr)
|
||||
function cfoper.beq(a, b, val, addr)
|
||||
if type(val) ~= "table" then
|
||||
bitmask = ~a ~ b
|
||||
return bitmask ~= 0
|
||||
@ -100,17 +111,6 @@ function cheatfind.startplugin()
|
||||
end
|
||||
end
|
||||
|
||||
local cfoper = {
|
||||
lt = function(a, b, val) return (a < b and val == 0) or (val > 0 and (a + val) == b) end,
|
||||
gt = function(a, b, val) return (a > b and val == 0) or (val > 0 and (a - val) == b) end,
|
||||
eq = function(a, b, val) return a == b end,
|
||||
ne = function(a, b, val) return (a ~= b and val == 0) or
|
||||
(val > 0 and ((a - val) == b or (a + val) == b)) end,
|
||||
ltv = function(a, b, val) return a < val end,
|
||||
gtv = function(a, b, val) return a > val end,
|
||||
eqv = function(a, b, val) return a == val end,
|
||||
nev = function(a, b, val) return a ~= val end,
|
||||
bne = bne, beq = beq }
|
||||
|
||||
local function check_bcd(val)
|
||||
local a = val + 0x0666666666666666
|
||||
@ -232,9 +232,8 @@ function cheatfind.startplugin()
|
||||
local matches = {}
|
||||
local matchsel = 0
|
||||
local menu_blocks = {}
|
||||
local midx = { region = 1, init = 2, save = 4, comp = 5, lop = 6, op = 7, rop = 8, val = 9,
|
||||
width = 11, bcd = 12, undo = 13, match = 15, watch = 0 }
|
||||
local watches = {}
|
||||
local menu_func
|
||||
|
||||
local function start()
|
||||
devtable = {}
|
||||
@ -290,46 +289,208 @@ function cheatfind.startplugin()
|
||||
end
|
||||
end
|
||||
|
||||
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])
|
||||
local function incdec(event, val, min, max)
|
||||
local ret
|
||||
if event == "left" and val ~= min then
|
||||
val = val - 1
|
||||
ret = true
|
||||
elseif event == "right" and val ~= max then
|
||||
val = val + 1
|
||||
ret = true
|
||||
end
|
||||
return val, ret
|
||||
end
|
||||
|
||||
|
||||
menu[#menu + 1] = function()
|
||||
local m = { "CPU or RAM", devtable[devsel].tag, 0 }
|
||||
if #devtable == 1 then
|
||||
m[3] = 0
|
||||
else
|
||||
menu_lim(devsel, 1, #devtable, m)
|
||||
end
|
||||
local function f(event)
|
||||
if (event == "left" or event == "right") and #menu_blocks ~= 0 then
|
||||
manager:machine():popmessage("Changes to this only take effect when \"Start new search\" is selected")
|
||||
end
|
||||
devsel = incdec(event, devsel, 1, #devtable)
|
||||
return true
|
||||
end
|
||||
return m, f
|
||||
end
|
||||
|
||||
menu[#menu + 1] = function()
|
||||
local function f(event)
|
||||
local ret = false
|
||||
if event == "select" then
|
||||
menu_blocks = {}
|
||||
matches = {}
|
||||
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")
|
||||
watches = {}
|
||||
leftop = 2
|
||||
rightop = 1
|
||||
matchsel = 0
|
||||
return true
|
||||
end
|
||||
devcur = devsel
|
||||
end
|
||||
return { "Start new search", "", 0 }, f
|
||||
end
|
||||
menu[midx.init] = { "Start new search", "", 0 }
|
||||
if #menu_blocks ~= 0 then
|
||||
menu[midx.init + 1] = { "---", "", "off" }
|
||||
menu[midx.save] = { "Save current -- #" .. #menu_blocks[1] + 1, "", 0 }
|
||||
menu[midx.comp] = { "Compare", "", 0 }
|
||||
menu[midx.lop] = { "Left operand", leftop, "" }
|
||||
menu_lim(leftop, 1, #menu_blocks[1] + 1, menu[midx.lop])
|
||||
if leftop == #menu_blocks[1] + 1 then
|
||||
menu[midx.lop][2] = "Current"
|
||||
menu[#menu + 1] = function() return { "---", "", "off" }, nil end
|
||||
menu[#menu + 1] = function()
|
||||
local function f(event)
|
||||
if event == "select" then
|
||||
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")
|
||||
leftop = (leftop == #menu_blocks[1]) and #menu_blocks[1] + 1 or leftop
|
||||
rightop = (rightop == #menu_blocks[1] - 1) and #menu_blocks[1] or rightop
|
||||
return true
|
||||
end
|
||||
end
|
||||
return { "Save current -- #" .. #menu_blocks[1] + 1, "", 0 }, f
|
||||
end
|
||||
menu[midx.op] = { "Operator", optable[opsel], "" }
|
||||
menu_lim(opsel, 1, #optable, menu[midx.op])
|
||||
menu[midx.rop] = { "Right operand", rightop, "" }
|
||||
menu_lim(rightop, 1, #menu_blocks[1], menu[midx.rop])
|
||||
menu[midx.val] = { "Value", value, "" }
|
||||
menu_lim(value, 0, 100, menu[midx.val]) -- max value?
|
||||
if value == 0 and optable[opsel]:sub(3, 3) ~= "v" then
|
||||
menu[midx.val][2] = "Any"
|
||||
menu[#menu + 1] = function()
|
||||
function f(event)
|
||||
if event == "select" then
|
||||
local count = 0
|
||||
if #matches == 0 then
|
||||
matches[1] = {}
|
||||
for num = 1, #menu_blocks do
|
||||
if leftop == #menu_blocks[1] + 1 then
|
||||
matches[1][num] = cheat.compcur(menu_blocks[num][rightop], optable[opsel],
|
||||
formtable[width], value, bcd == 1)
|
||||
else
|
||||
matches[1][num] = cheat.comp(menu_blocks[num][leftop], menu_blocks[num][rightop],
|
||||
optable[opsel], formtable[width], value, bcd == 1)
|
||||
end
|
||||
count = count + #matches[1][num]
|
||||
end
|
||||
else
|
||||
lastmatch = matches[#matches]
|
||||
matches[#matches + 1] = {}
|
||||
for num = 1, #menu_blocks do
|
||||
if leftop == #menu_blocks[1] + 1 then
|
||||
matches[#matches][num] = cheat.compcurnext(menu_blocks[num][rightop], lastmatch[num],
|
||||
optable[opsel], formtable[width], value, bcd == 1)
|
||||
else
|
||||
matches[#matches][num] = cheat.compnext(menu_blocks[num][leftop], menu_blocks[num][rightop],
|
||||
lastmatch[num], optable[opsel], formtable[width], value, bcd == 1)
|
||||
end
|
||||
count = count + #matches[#matches][num]
|
||||
end
|
||||
end
|
||||
manager:machine():popmessage(count .. " total matches found")
|
||||
return true
|
||||
end
|
||||
end
|
||||
return { "Compare", "", 0 }, f
|
||||
end
|
||||
menu[midx.val + 1] = { "---", "", "off" }
|
||||
menu[midx.width] = { "Data Format", formname[width], 0 }
|
||||
menu_lim(width, 1, #formtable, menu[midx.width])
|
||||
menu[midx.bcd] = { "BCD", "Off", 0 }
|
||||
menu_lim(bcd, 0, 1, menu[midx.bcd])
|
||||
if bcd == 1 then
|
||||
menu[midx.bcd][2] = "On"
|
||||
menu[#menu + 1] = function()
|
||||
local m = { "Left operand", leftop, "" }
|
||||
menu_lim(leftop, 1, #menu_blocks[1] + 1, m)
|
||||
if leftop == #menu_blocks[1] + 1 then
|
||||
m[2] = "Current"
|
||||
end
|
||||
return m, function(event) local r leftop, r = incdec(event, leftop, 1, #menu_blocks[1] + 1) return r end
|
||||
end
|
||||
menu[#menu + 1] = function()
|
||||
local m = { "Operator", optable[opsel], "" }
|
||||
menu_lim(opsel, 1, #optable, m)
|
||||
function f(event)
|
||||
local r
|
||||
opsel, r = incdec(event, opsel, 1, #optable)
|
||||
if event == "left" or event == "right" or event == "comment" then
|
||||
if optable[opsel] == "lt" then
|
||||
manager:machine():popmessage("Left less than right, value is difference")
|
||||
elseif optable[opsel] == "gt" then
|
||||
manager:machine():popmessage("Left greater than right, value is difference")
|
||||
elseif optable[opsel] == "eq" then
|
||||
manager:machine():popmessage("Left equal to right")
|
||||
elseif optable[opsel] == "ne" then
|
||||
manager:machine():popmessage("Left not equal to right, value is difference")
|
||||
elseif optable[opsel] == "beq" then
|
||||
manager:machine():popmessage("Left equal to right with bitmask")
|
||||
elseif optable[opsel] == "bne" then
|
||||
manager:machine():popmessage("Left not equal to right with bitmask")
|
||||
elseif optable[opsel] == "ltv" then
|
||||
manager:machine():popmessage("Left less than value")
|
||||
elseif optable[opsel] == "gtv" then
|
||||
manager:machine():popmessage("Left greater than value")
|
||||
elseif optable[opsel] == "eqv" then
|
||||
manager:machine():popmessage("Left equal to value")
|
||||
elseif optable[opsel] == "nev" then
|
||||
manager:machine():popmessage("Left not equal to value")
|
||||
end
|
||||
end
|
||||
return r
|
||||
end
|
||||
return m, f
|
||||
end
|
||||
menu[#menu + 1] = function()
|
||||
if optable[opsel]:sub(3, 3) == "v" then
|
||||
return nil
|
||||
end
|
||||
local m = { "Right operand", rightop, "" }
|
||||
menu_lim(rightop, 1, #menu_blocks[1], m)
|
||||
return m, function(event) local r rightop, r = incdec(event, rightop, 1, #menu_blocks[1]) return r end
|
||||
end
|
||||
menu[#menu + 1] = function()
|
||||
if optable[opsel] == "bne" or optable[opsel] == "beq" or optable[opsel] == "eq" then
|
||||
return nil
|
||||
end
|
||||
local m = { "Value", value, "" }
|
||||
local max = 100 -- max value?
|
||||
menu_lim(value, 0, max, m)
|
||||
if value == 0 and optable[opsel]:sub(3, 3) ~= "v" then
|
||||
m[2] = "Any"
|
||||
end
|
||||
return m, function(event) local r value, r = incdec(event, value, 0, max) return r end
|
||||
end
|
||||
menu[#menu + 1] = function() return { "---", "", "off" }, nil end
|
||||
menu[#menu + 1] = function()
|
||||
local m = { "Data Format", formname[width], 0 }
|
||||
menu_lim(width, 1, #formtable, m)
|
||||
return m, function(event) local r width, r = incdec(event, width, 1, #formtable) return r end
|
||||
end
|
||||
menu[#menu + 1] = function()
|
||||
if optable[opsel] == "bne" or optable[opsel] == "beq" then
|
||||
return nil
|
||||
end
|
||||
local m = { "BCD", "Off", 0 }
|
||||
menu_lim(bcd, 0, 1, m)
|
||||
if bcd == 1 then
|
||||
m[2] = "On"
|
||||
end
|
||||
return m, function(event) local r bcd, r = incdec(bcd, 0, 1) return r end
|
||||
end
|
||||
menu[#menu + 1] = function()
|
||||
if #matches == 0 then
|
||||
return nil
|
||||
end
|
||||
function f(event)
|
||||
if event == "select" and #matches > 0 then
|
||||
matches[#matches] = nil
|
||||
return true
|
||||
end
|
||||
end
|
||||
return { "Undo last search -- #" .. #matches, "", 0 }, f
|
||||
end
|
||||
menu[midx.undo] = { "Undo last search -- #" .. #matches, "", 0 }
|
||||
if #matches ~= 0 then
|
||||
menu[midx.undo + 1] = { "---", "", "off" }
|
||||
menu[midx.match] = { "Match block", matchsel, "" }
|
||||
menu_lim(matchsel, 0, #matches[#matches], menu[midx.match])
|
||||
if matchsel == 0 then
|
||||
menu[midx.match][2] = "All"
|
||||
menu[#menu + 1] = function() return { "---", "", "off" }, nil end
|
||||
menu[#menu + 1] = function()
|
||||
local m = { "Match block", matchsel, "" }
|
||||
menu_lim(matchsel, 0, #matches[#matches], m)
|
||||
if matchsel == 0 then
|
||||
m[2] = "All"
|
||||
end
|
||||
return m, function(event) local r matchsel, r = incdec(matchsel, 0, #matches[#matches]) return r end
|
||||
end
|
||||
local function mpairs(sel, list)
|
||||
if #list == 0 then
|
||||
@ -357,250 +518,141 @@ function cheatfind.startplugin()
|
||||
end
|
||||
return mpairs_it, list, 0
|
||||
end
|
||||
local bitwidth = formtable[width]:sub(2, 2):lower()
|
||||
local mstart = #menu + 1
|
||||
if bitwidth == "h" then
|
||||
bitwidth = " %04x"
|
||||
elseif bitwidth == "l" then
|
||||
bitwidth = " %08x"
|
||||
elseif bitwidth == "j" then
|
||||
bitwidth = " %016x"
|
||||
else
|
||||
bitwidth = " %02x"
|
||||
end
|
||||
for num2, match in mpairs(matchsel, matches[#matches]) do
|
||||
if #menu > 100 then
|
||||
break
|
||||
end
|
||||
local numform = ""
|
||||
local bitwidth = formtable[width]:sub(2, 2):lower()
|
||||
if bitwidth == "h" then
|
||||
numform = " %04x"
|
||||
elseif bitwidth == "l" then
|
||||
numform = " %08x"
|
||||
elseif bitwidth == "j" then
|
||||
numform = " %016x"
|
||||
else
|
||||
numform = " %02x"
|
||||
end
|
||||
menu[#menu + 1] = { string.format("%08x" .. numform .. numform, match.addr, match.oldval,
|
||||
menu[#menu + 1] = function()
|
||||
local m = { string.format("%08x" .. bitwidth .. bitwidth, match.addr, match.oldval,
|
||||
match.newval), "", 0 }
|
||||
if not match.mode then
|
||||
match.mode = 1
|
||||
if not match.mode then
|
||||
match.mode = 1
|
||||
end
|
||||
if match.mode == 1 then
|
||||
m[2] = "Test"
|
||||
elseif match.mode == 2 then
|
||||
m[2] = "Write"
|
||||
else
|
||||
m[2] = "Watch"
|
||||
end
|
||||
menu_lim(match.mode, 1, 3, m)
|
||||
function f(event)
|
||||
local r
|
||||
match.mode, r = incdec(event, match.mode, 1, 3)
|
||||
if event == "select" then
|
||||
local match
|
||||
if matchsel == 0 then
|
||||
local sel = index - mstart
|
||||
for i = 1, #matches[#matches] do
|
||||
if sel <= #matches[#matches][i] then
|
||||
match = matches[#matches][i][sel]
|
||||
break
|
||||
else
|
||||
sel = sel - #matches[#matches][i]
|
||||
end
|
||||
end
|
||||
else
|
||||
match = matches[#matches][matchsel][index - mstart]
|
||||
end
|
||||
local dev = devtable[devcur]
|
||||
local cheat = { desc = string.format("Test cheat at addr %08X", match.addr), script = {} }
|
||||
local wid = formtable[width]:sub(2, 2):lower()
|
||||
local xmlcheat
|
||||
local form
|
||||
if wid == "h" then
|
||||
wid = "u16"
|
||||
form = "%08x %04x"
|
||||
xmlcheat = "pw"
|
||||
elseif wid == "l" then
|
||||
wid = "u32"
|
||||
form = "%08x %08x"
|
||||
xmlcheat = "pd"
|
||||
elseif wid == "j" then
|
||||
wid = "u64"
|
||||
form = "%08x %016x"
|
||||
xmlcheat = "pq"
|
||||
else
|
||||
wid = "u8"
|
||||
form = "%08x %02x"
|
||||
xmlcheat = "pb"
|
||||
end
|
||||
xmlcheat = string.format("<mamecheat version=1>\n<cheat desc=\"%s\">\n<script state=\"run\">\n<action>%s.%s@%X=%X</action>\n</script>\n</cheat>\n</mamecheat>", cheat.desc, dev.tag:sub(2), xmlcheat, match.addr, match.newval)
|
||||
|
||||
if dev.space.shortname then
|
||||
cheat.ram = { ram = dev.tag }
|
||||
cheat.script.on = "ram:write(" .. match.addr .. "," .. match.newval .. ")"
|
||||
else
|
||||
cheat.space = { cpu = { tag = dev.tag, type = "program" } }
|
||||
cheat.script.run = "cpu:write_" .. wid .. "(" .. match.addr .. "," .. match.newval .. ")"
|
||||
end
|
||||
if match.mode == 1 then
|
||||
if not _G.ce then
|
||||
manager:machine():popmessage("Cheat engine not available")
|
||||
else
|
||||
_G.ce.inject(cheat)
|
||||
end
|
||||
elseif match.mode == 2 then
|
||||
local filename = string.format("%s/%s_%08X_cheat", manager:machine():options().entries.cheatpath:value():match("([^;]+)"), emu.romname(), match.addr)
|
||||
local json = require("json")
|
||||
local file = io.open(filename .. ".json", "w")
|
||||
if file then
|
||||
file:write(json.stringify({[1] = cheat}, {indent = true}))
|
||||
file:close()
|
||||
file = io.open(filename .. ".xml", "w")
|
||||
file:write(xmlcheat)
|
||||
file:close()
|
||||
manager:machine():popmessage("Cheat written to " .. filename)
|
||||
else
|
||||
manager:machine():popmessage("Unable to write file\nCheck cheatpath dir exists")
|
||||
end
|
||||
else
|
||||
local func = "return space:read"
|
||||
local env = { space = devtable[devcur].space }
|
||||
if not dev.space.shortname then
|
||||
func = func .. "_" .. wid
|
||||
end
|
||||
func = func .. "(" .. match.addr .. ")"
|
||||
watches[#watches + 1] = { addr = match.addr, func = load(func, func, "t", env), format = form }
|
||||
r = true
|
||||
end
|
||||
end
|
||||
return r
|
||||
end
|
||||
return m, f
|
||||
end
|
||||
if match.mode == 1 then
|
||||
menu[#menu][2] = "Test"
|
||||
elseif match.mode == 2 then
|
||||
menu[#menu][2] = "Write"
|
||||
else
|
||||
menu[#menu][2] = "Watch"
|
||||
end
|
||||
menu_lim(match.mode, 1, 3, menu[#menu])
|
||||
end
|
||||
end
|
||||
if #watches ~= 0 then
|
||||
menu[#menu + 1] = { "Clear Watches", "", 0 }
|
||||
midx.watch = #menu
|
||||
menu[#menu + 1] = function()
|
||||
return { "Clear Watches", "", 0 }, function(event) if event == "select" then watches = {} return true end end
|
||||
end
|
||||
end
|
||||
end
|
||||
return menu
|
||||
local menu_list = {}
|
||||
menu_func = {}
|
||||
for num, func in ipairs(menu) do
|
||||
local item, f = func()
|
||||
if item then
|
||||
menu_list[#menu_list + 1] = item
|
||||
menu_func[#menu_list] = f
|
||||
end
|
||||
end
|
||||
return menu_list
|
||||
end
|
||||
|
||||
local function menu_callback(index, event)
|
||||
local ret = false
|
||||
|
||||
local function incdec(val, min, max)
|
||||
if event == "left" and val ~= min then
|
||||
val = val - 1
|
||||
ret = true
|
||||
elseif event == "right" and val ~= max then
|
||||
val = val + 1
|
||||
ret = true
|
||||
end
|
||||
return val
|
||||
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 \"Start new search\" is selected")
|
||||
end
|
||||
devsel = incdec(devsel, 1, #devtable)
|
||||
return true
|
||||
elseif index == midx.init then
|
||||
if event == "select" then
|
||||
menu_blocks = {}
|
||||
matches = {}
|
||||
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")
|
||||
watches = {}
|
||||
leftop = 2
|
||||
rightop = 1
|
||||
matchsel = 0
|
||||
ret = true
|
||||
end
|
||||
devcur = devsel
|
||||
elseif index == midx.undo then
|
||||
if event == "select" and #matches > 0 then
|
||||
matches[#matches] = nil
|
||||
end
|
||||
ret = true
|
||||
elseif index == midx.save then
|
||||
if event == "select" then
|
||||
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")
|
||||
leftop = (leftop == #menu_blocks[1]) and #menu_blocks[1] + 1 or leftop
|
||||
rightop = (rightop == #menu_blocks[1] - 1) and #menu_blocks[1] or rightop
|
||||
ret = true
|
||||
end
|
||||
elseif index == midx.op then
|
||||
opsel = incdec(opsel, 1, #optable)
|
||||
if event == "left" or event == "right" or event == "comment" then
|
||||
if optable[opsel] == "lt" then
|
||||
manager:machine():popmessage("Left less than right, value is difference")
|
||||
elseif optable[opsel] == "gt" then
|
||||
manager:machine():popmessage("Left greater than right, value is difference")
|
||||
elseif optable[opsel] == "eq" then
|
||||
manager:machine():popmessage("Left equal to right, value is ignored")
|
||||
elseif optable[opsel] == "ne" then
|
||||
manager:machine():popmessage("Left not equal to right, value is difference")
|
||||
elseif optable[opsel] == "beq" then
|
||||
manager:machine():popmessage("Left equal to right with bitmask, value is ignored")
|
||||
elseif optable[opsel] == "bne" then
|
||||
manager:machine():popmessage("Left not equal to right with bitmask, value is ignored")
|
||||
elseif optable[opsel] == "ltv" then
|
||||
manager:machine():popmessage("Left less than value, right is ignored")
|
||||
elseif optable[opsel] == "gtv" then
|
||||
manager:machine():popmessage("Left greater than value, right is ignored")
|
||||
elseif optable[opsel] == "eqv" then
|
||||
manager:machine():popmessage("Left equal to value, right is ignored")
|
||||
elseif optable[opsel] == "nev" then
|
||||
manager:machine():popmessage("Left not equal to value, right is ignored")
|
||||
end
|
||||
end
|
||||
ret = true
|
||||
elseif index == midx.val then
|
||||
value = incdec(value, 0, 100)
|
||||
elseif index == midx.lop then
|
||||
leftop = incdec(leftop, 1, #menu_blocks[1] + 1)
|
||||
elseif index == midx.rop then
|
||||
rightop = incdec(rightop, 1, #menu_blocks[1])
|
||||
elseif index == midx.width then
|
||||
width = incdec(width, 1, #formtable)
|
||||
elseif index == midx.bcd then
|
||||
bcd = incdec(bcd, 0, 1)
|
||||
elseif index == midx.comp then
|
||||
if event == "select" then
|
||||
local count = 0
|
||||
if #matches == 0 then
|
||||
matches[1] = {}
|
||||
for num = 1, #menu_blocks do
|
||||
if leftop == #menu_blocks[1] + 1 then
|
||||
matches[1][num] = cheat.compcur(menu_blocks[num][rightop], optable[opsel],
|
||||
formtable[width], value, bcd == 1)
|
||||
else
|
||||
matches[1][num] = cheat.comp(menu_blocks[num][leftop], menu_blocks[num][rightop],
|
||||
optable[opsel], formtable[width], value, bcd == 1)
|
||||
end
|
||||
count = count + #matches[1][num]
|
||||
end
|
||||
else
|
||||
lastmatch = matches[#matches]
|
||||
matches[#matches + 1] = {}
|
||||
for num = 1, #menu_blocks do
|
||||
if leftop == #menu_blocks[1] + 1 then
|
||||
matches[#matches][num] = cheat.compcurnext(menu_blocks[num][rightop], lastmatch[num],
|
||||
optable[opsel], formtable[width], value, bcd == 1)
|
||||
else
|
||||
matches[#matches][num] = cheat.compnext(menu_blocks[num][leftop], menu_blocks[num][rightop],
|
||||
lastmatch[num], optable[opsel], formtable[width], value, bcd == 1)
|
||||
end
|
||||
count = count + #matches[#matches][num]
|
||||
end
|
||||
end
|
||||
manager:machine():popmessage(count .. " total matches found")
|
||||
ret = true
|
||||
end
|
||||
elseif index == midx.match then
|
||||
matchsel = incdec(matchsel, 0, #matches[#matches])
|
||||
elseif index == midx.watch then
|
||||
watches = {}
|
||||
ret = true
|
||||
elseif index > midx.match then
|
||||
local match
|
||||
if matchsel == 0 then
|
||||
local sel = index - midx.match
|
||||
for i = 1, #matches[#matches] do
|
||||
if sel <= #matches[#matches][i] then
|
||||
match = matches[#matches][i][sel]
|
||||
break
|
||||
else
|
||||
sel = sel - #matches[#matches][i]
|
||||
end
|
||||
end
|
||||
else
|
||||
match = matches[#matches][matchsel][index - midx.match]
|
||||
end
|
||||
match.mode = incdec(match.mode, 1, 3)
|
||||
if event == "select" then
|
||||
local dev = devtable[devcur]
|
||||
local cheat = { desc = string.format("Test cheat at addr %08X", match.addr), script = {} }
|
||||
local wid = formtable[width]:sub(2, 2):lower()
|
||||
local xmlcheat
|
||||
local form
|
||||
if wid == "h" then
|
||||
wid = "u16"
|
||||
form = "%08x %04x"
|
||||
xmlcheat = "pw"
|
||||
elseif wid == "l" then
|
||||
wid = "u32"
|
||||
form = "%08x %08x"
|
||||
xmlcheat = "pd"
|
||||
elseif wid == "j" then
|
||||
wid = "u64"
|
||||
form = "%08x %016x"
|
||||
xmlcheat = "pq"
|
||||
else
|
||||
wid = "u8"
|
||||
form = "%08x %02x"
|
||||
xmlcheat = "pb"
|
||||
end
|
||||
xmlcheat = string.format("<mamecheat version=1>\n<cheat desc=\"%s\">\n<script state=\"run\">\n<action>%s.%s@%X=%X</action>\n</script>\n</cheat>\n</mamecheat>", cheat.desc, dev.tag:sub(2), xmlcheat, match.addr, match.newval)
|
||||
|
||||
if dev.space.shortname then
|
||||
cheat.ram = { ram = dev.tag }
|
||||
cheat.script.on = "ram:write(" .. match.addr .. "," .. match.newval .. ")"
|
||||
else
|
||||
cheat.space = { cpu = { tag = dev.tag, type = "program" } }
|
||||
cheat.script.run = "cpu:write_" .. wid .. "(" .. match.addr .. "," .. match.newval .. ")"
|
||||
end
|
||||
if match.mode == 1 then
|
||||
if not _G.ce then
|
||||
manager:machine():popmessage("Cheat engine not available")
|
||||
else
|
||||
_G.ce.inject(cheat)
|
||||
end
|
||||
elseif match.mode == 2 then
|
||||
local filename = string.format("%s/%s_%08X_cheat", manager:machine():options().entries.cheatpath:value():match("([^;]+)"), emu.romname(), match.addr)
|
||||
local json = require("json")
|
||||
local file = io.open(filename .. ".json", "w")
|
||||
if file then
|
||||
file:write(json.stringify({[1] = cheat}, {indent = true}))
|
||||
file:close()
|
||||
file = io.open(filename .. ".xml", "w")
|
||||
file:write(xmlcheat)
|
||||
file:close()
|
||||
manager:machine():popmessage("Cheat written to " .. filename)
|
||||
else
|
||||
manager:machine():popmessage("Unable to write file\nCheck cheatpath dir exists")
|
||||
end
|
||||
else
|
||||
local func = "return space:read"
|
||||
local env = { space = devtable[devcur].space }
|
||||
if not dev.space.shortname then
|
||||
func = func .. "_" .. wid
|
||||
end
|
||||
func = func .. "(" .. match.addr .. ")"
|
||||
watches[#watches + 1] = { addr = match.addr, func = load(func, func, "t", env), format = form }
|
||||
end
|
||||
end
|
||||
ret = true
|
||||
end
|
||||
devsel = devcur
|
||||
return ret
|
||||
return menu_func[index](event)
|
||||
end
|
||||
emu.register_menu(menu_callback, menu_populate, "Cheat Finder")
|
||||
emu.register_frame_done(function ()
|
||||
|
Loading…
Reference in New Issue
Block a user