Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- license:BSD-3-Clause
- -- copyright-holders:Carl
- -- This includes a library of functions to be used at the Lua console as cf.getspaces() etc...
- local exports = {}
- exports.name = "cheatfind"
- exports.version = "0.0.1"
- exports.description = "Cheat finder helper library"
- exports.license = "The BSD 3-Clause License"
- exports.author = { name = "Carl" }
- local cheatfind = exports
- function cheatfind.startplugin()
- local cheat = {}
- -- return table of devices and spaces
- function cheat.getspaces()
- local spaces = {}
- for tag, device in pairs(manager:machine().devices) do
- if device.spaces then
- spaces[tag] = {}
- for name, space in pairs(device.spaces) do
- spaces[tag][name] = space
- end
- end
- end
- return spaces
- end
- -- return table of ram devices
- function cheat.getram()
- local ram = {}
- for tag, device in pairs(manager:machine().devices) do
- if device:shortname() == "ram" then
- ram[tag] = {}
- ram[tag].dev = device
- ram[tag].size = emu.item(device.items["0/m_size"]):read(0)
- end
- end
- return ram
- end
- -- return table of share regions
- function cheat.getshares()
- local shares = {}
- for tag, share in pairs(manager:machine():memory().shares) do
- shares[tag] = share
- end
- return shares
- end
- -- save data block
- function cheat.save(space, start, size)
- local data = { block = "", start = start, size = size, space = space, shift = 0 }
- if getmetatable(space).__name:match("addr_space") then
- data.shift = space.shift
- end
- if getmetatable(space).__name:match("device_t") then
- if space:shortname() == "ram" then
- data.block = emu.item(space.items["0/m_pointer"]):read_block(start, size)
- if not data.block then
- return nil
- end
- end
- else
- local block = ""
- local temp = {}
- local j = 1
- if data.shift >= 0 then -- region or byte wide space
- for i = start, start + (size << data.shift), 1 << data.shift do
- if j < 65536 then
- temp[j] = string.pack("B", space:read_u8(i))
- j = j + 1
- else
- block = block .. table.concat(temp) .. string.pack("B", space:read_u8(i))
- temp = {}
- j = 1
- end
- end
- elseif data.shift < 0 then
- local s = -data.shift
- local read = (s == 1) and space.read_u16 or (s == 2) and space.read_u32 or (s == 3) and space.read_u64 or space.read_u8
- local pack = (s == 1) and "<I2" or (s == 2) and "<I4" or (s == 3) and "<I8" or "B"
- for i = start, start + (size >> s) do
- if j < 65536 then
- temp[j] = string.pack(pack, read(space, i))
- j = j + 1
- else
- block = block .. table.concat(temp) .. string.pack(pack, read(space, i))
- temp = {}
- j = 1
- end
- end
- end
- block = block .. table.concat(temp)
- data.block = block
- end
- return data
- end
- -- compare two data blocks, format is as lua string.unpack, bne and beq val is table of masks, step is address increment value
- function cheat.comp(newdata, olddata, oper, format, val, bcd, step)
- local ret = {}
- local ref = {} -- this is a helper for comparing two match lists
- local bitmask = nil
- if not step or step <= 0 then
- step = 1
- end
- if (olddata.shift < 0) and (step < (1 << -olddata.shift)) then
- step = 1 << -olddata.shift;
- 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 }
- function cfoper.bne(a, b, val, addr)
- if type(val) ~= "table" then
- bitmask = a ~ b
- return bitmask ~= 0
- elseif not val[addr] then
- return false
- else
- bitmask = (a ~ b) & val[addr]
- return bitmask ~= 0
- end
- end
- function cfoper.beq(a, b, val, addr)
- if type(val) ~= "table" then
- bitmask = ~a ~ b
- return bitmask ~= 0
- elseif not val[addr] then
- return false
- else
- bitmask = (~a ~ b) & val[addr]
- return bitmask ~= 0
- end
- end
- local function check_bcd(val)
- local a = val + 0x0666666666666666
- a = a ~ val
- return (a & 0x1111111111111110) == 0
- end
- local function frombcd(val)
- local result = 0
- local mul = 1
- while val ~= 0 do
- result = result + ((val % 16) * mul)
- val = val >> 4
- mul = mul * 10
- end
- return result
- end
- if not newdata and oper:sub(3, 3) == "v" then
- newdata = olddata
- end
- if olddata.start ~= newdata.start or olddata.size ~= newdata.size or not cfoper[oper] then
- return {}
- end
- if not val then
- val = 0
- end
- for i = 1, olddata.size, step do
- local oldstat, old = pcall(string.unpack, format, olddata.block, i)
- local newstat, new = pcall(string.unpack, format, newdata.block, i)
- if oldstat and newstat then
- local oldc, newc = old, new
- local comp = false
- local addr = i - 1
- if olddata.shift ~= 0 then
- local s = olddata.shift
- addr = (s < 0) and addr >> -s or (s > 0) and addr << s
- end
- addr = addr + olddata.start
- if not bcd or (check_bcd(old) and check_bcd(new)) then
- if bcd then
- oldc = frombcd(old)
- newc = frombcd(new)
- end
- if cfoper[oper](newc, oldc, val, addr) then
- ret[#ret + 1] = { addr = addr,
- oldval = old,
- newval = new,
- bitmask = bitmask }
- ref[addr] = #ret
- end
- end
- end
- end
- return ret, ref
- end
- local function check_val(oper, val, matches)
- if oper ~= "beq" and oper ~= "bne" then
- return val
- elseif not matches or not matches[1].bitmask then
- return nil
- end
- local masks = {}
- for num, match in pairs(matches) do
- masks[match.addr] = match.bitmask
- end
- return masks
- end
- -- compare two blocks and filter by table of previous matches
- function cheat.compnext(newdata, olddata, oldmatch, oper, format, val, bcd, step)
- local matches, refs = cheat.comp(newdata, olddata, oper, format, check_val(oper, val, oldmatch), bcd, step)
- local nonmatch = {}
- local oldrefs = {}
- for num, match in pairs(oldmatch) do
- oldrefs[match.addr] = num
- end
- for addr, ref in pairs(refs) do
- if not oldrefs[addr] then
- nonmatch[ref] = true
- refs[addr] = nil
- else
- matches[ref].oldval = oldmatch[oldrefs[addr]].oldval
- 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, format, val, bcd, step)
- local newdata = cheat.save(olddata.space, olddata.start, olddata.size, olddata.space)
- return cheat.comp(newdata, olddata, oper, format, val, bcd, step)
- end
- -- compare a data block to the current state and filter
- function cheat.compcurnext(olddata, oldmatch, oper, format, val, bcd, step)
- local newdata = cheat.save(olddata.space, olddata.start, olddata.size, olddata.space)
- return cheat.compnext(newdata, olddata, oldmatch, oper, format, val, bcd, step)
- end
- _G.cf = cheat
- local devtable = {}
- local devsel = 1
- local devcur = 1
- --local formtable = { " I1", " i1", "<I2", ">I2", "<i2", ">i2", "<I4", ">I4", "<i4", ">i4", "<I8", ">I8", "<i8", ">i8", }-- " <f", " >f", " <d", " >d" }
- --local formname = { "u8", "s8", "little u16", "big u16", "little s16", "big s16",
- -- "little u32", "big u32", "little s32", "big s32", "little u64", "big u64", "little s64", "big s64", }
- -- -- "little float", "big float", "little double", "big double" }
- -- Reordered into likelyhood of use order: unsigned byte by big endian unsigned by little endian unsigned then unsigned in same order
- local formtable = { " I1", ">I2", ">I4", ">I8", "<I2", "<I4", "<I8", " i1", ">i2", ">i4", ">i8", "<i2", "<i4", "<i8", }-- " <f", " >f", " <d", " >d" }
- local formname = { "u8", "big u16", "big u32", "big u64", "little u16", "little u32",
- "little u64", "s8", "big s16", "big s32", "big s64", "little s16", "little s32", "little s64", }
- local width = 1
- local bcd = 0
- local align = 0
- local optable = { "lt", "gt", "eq", "ne", "beq", "bne", "ltv", "gtv", "eqv", "nev" }
- local opsel = 1
- local value = 0
- local leftop = 1
- local rightop = 1
- local leftop_text = "Slot 1"
- local rightop_text = "Slot 1"
- local value_text = ""
- local expression_text = "Slot 1 < Slot 1"
- local pausetable = { "Automatic", "Manual" }
- local pausesel = 1
- local pokevaltable = { "Slot 1 Value", "Last Slot Value", "0x00", "0x01", "0x02", "0x03", "0x04", "0x05", "0x06", "0x07", "0x08", "0x09", "0x63 (Decimal 99)", "0x99 (BCD 99)",
- "0xFF (Decimal 255)" , "0x3E7 (Decimal 999)", "0x999 (BCD 999)", "0x270F (Decimal 9999)", "0x9999 (BCD 9999)", "0xFFFF (Decimal 65535)" }
- local pokevalsel = 1
- local matches = {}
- local matchsel = 0
- local matchpg = 0
- local menu_blocks = {}
- local watches = {}
- local menu_func
- local cheat_save
- local name = 1
- local name_player = 1
- local name_type = 1
- local function start()
- devtable = {}
- devsel = 1
- devcur = 1
- width = 1
- bcd = 0
- opsel = 1
- value = 0
- leftop = 1
- rightop = 1
- matches = {}
- matchsel = 0
- matchpg = 0
- menu_blocks = {}
- watches = {}
- local space_table = cheat.getspaces()
- for tag, list in pairs(space_table) do
- for name, space in pairs(list) do
- local ram = {}
- for num, entry in pairs(space.map) do
- if entry.writetype == "ram" then
- ram[#ram + 1] = { offset = entry.offset, size = entry.endoff - entry.offset }
- if space.shift > 0 then
- ram[#ram].size = ram[#ram].size >> space.shift
- elseif space.shift < 0 then
- ram[#ram].size = ram[#ram].size << -space.shift
- end
- end
- end
- if next(ram) then
- if tag == ":maincpu" and name == "program" then
- table.insert(devtable, 1, { name = tag .. ", " .. name, tag = tag, sname = name, space = space, ram = ram })
- else
- devtable[#devtable + 1] = { name = tag .. ", " .. name, tag = tag, sname = name, space = space, ram = ram }
- end
- end
- end
- end
- space_table = cheat.getram()
- for tag, ram in pairs(space_table) do
- devtable[#devtable + 1] = { tag = tag, name = "ram", space = ram.dev, ram = {{ offset = 0, size = ram.size }} }
- end
- space_table = cheat.getshares()
- for tag, share in pairs(space_table) do
- devtable[#devtable + 1] = { tag = tag, name = tag, space = share, ram = {{ offset = 0, size = share.size }} }
- end
- end
- emu.register_start(start)
- local menu_is_showing = false
- local tabbed_out = false
- local function menu_populate()
- if pausesel == 1 then
- emu.pause()
- menu_is_showing = true
- end
- local menu = {}
- local function menu_prepare()
- 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_lim(val, min, max, menuitem)
- if min == max then
- menuitem[3] = 0
- elseif val == min then
- menuitem[3] = "r"
- elseif val == max then
- menuitem[3] = "l"
- else
- menuitem[3] = "lr"
- end
- end
- 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
- if cheat_save then
- local cplayer = { "All", "P1", "P2", "P3", "P4" }
- local ctype = { "Infinite Credits", "Infinite Time", "Infinite Lives", "Infinite Energy", "Infinite Ammo", "Infinite Bombs", "Invincibility" }
- menu[#menu + 1] = function() return { _("Save Cheat"), "", "off" }, nil end
- menu[#menu + 1] = function() return { "---", "", "off" }, nil end
- menu[#menu + 1] = function()
- local c = { _("Default"), _("Custom") }
- local m = { _("Cheat Name"), c[name], 0 }
- menu_lim(name, 1, #c, m)
- local function f(event)
- local r
- name, r = incdec(event, name, 1, #c)
- if (event == "select" or event == "comment") and name == 1 then
- manager:machine():popmessage(string.format(_("Default name is %s"), cheat_save.name))
- end
- return r
- end
- return m, f
- end
- if name == 2 then
- menu[#menu + 1] = function()
- local m = { _("Player"), cplayer[name_player], 0 }
- menu_lim(name_player, 1, #cplayer, m)
- return m, function(event) local r name_player, r = incdec(event, name_player, 1, #cplayer) return r end
- end
- menu[#menu + 1] = function()
- local m = { _("Type"), ctype[name_type], 0 }
- menu_lim(name_type, 1, #ctype, m)
- return m, function(event) local r name_type, r = incdec(event, name_type, 1, #ctype) return r end
- end
- end
- menu[#menu + 1] = function()
- local m = { _("Save"), "", 0 }
- local function f(event)
- if event == "select" then
- local desc
- local written = false
- if name == 2 then
- if cplayer[name_player] == "All" then
- desc = ctype[name_type]
- else
- desc = cplayer[name_player] .. " " .. ctype[name_type]
- end
- else
- desc = cheat_save.name
- end
- local filename = cheat_save.filename .. "_" .. desc
- local file = io.open(filename .. ".json", "w")
- if file then
- file:write(string.format(cheat_save.json, desc))
- file:close()
- -- xml or simple are program space only
- if not getmetatable(devtable[devcur].space).__name:match("device_t") and devtable[devcur].sname == "program" then
- file = io.open(filename .. ".xml", "w")
- file:write(string.format(cheat_save.xml, desc))
- file:close()
- file = io.open(cheat_save.path .. "/cheat.simple", "a")
- file:write(string.format(cheat_save.simple, desc))
- -- old cheat .dat format, write support only (for cheat forum posting of new cheats if posted in simple format)
- file:write(string.format(cheat_save.dat, desc))
- file:close()
- manager:machine():popmessage(string.format(_("Cheat written to %s and added to cheat.simple"), filename))
- end
- written = true
- elseif not getmetatable(devtable[devcur].space).__name:match("device_t") and devtable[devcur].sname == "program" then
- file = io.open(cheat_save.path .. "/cheat.simple", "a")
- if file then
- file:write(string.format(cheat_save.simple, desc))
- -- old cheat .dat format, write support only (for cheat forum posting of new cheats if posted in simple format)
- file:write(string.format(cheat_save.dat, desc))
- file:close()
- manager:machine():popmessage(_("Cheat added to cheat.simple"))
- written = true
- end
- end
- if not written then
- manager:machine():popmessage(_("Unable to write file\nEnsure that cheatpath folder exists"))
- end
- cheat_save = nil
- return true
- end
- return false
- end
- return m, f
- end
- menu[#menu + 1] = function() return { _("Cancel"), "", 0 }, function(event) if event == "select" then cheat_save = nil return true end end end
- return menu_prepare()
- end
- menu[#menu + 1] = function()
- local m = { _("CPU or RAM"), devtable[devsel].name, 0 }
- menu_lim(devsel, 1, #devtable, m)
- 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 m = { _("Pause Mode"), pausetable[pausesel], 0 }
- menu_lim(pausesel, 1, pausetable, m)
- local function f(event)
- if (event == "left" or event == "right") then
- if pausesel == 1 then
- pausesel = 2
- menu_is_showing = false
- manager:machine():popmessage(_("Manually pause & unpause the game when needed with the pause hotkey"))
- else
- pausesel = 1
- emu.pause()
- end
- end
- 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 = {}
- devcur = devsel
- 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(_("All slots cleared and current state saved to Slot 1"))
- watches = {}
- opsel = 1
- value = 0
- leftop = 1
- rightop = 1
- leftop_text = "Slot 1"
- rightop_text = "Slot 1"
- value_text = ""
- expression_text = "Slot 1 < Slot 1"
- matchsel = 0
- return true
- end
- end
- local opsel = 1
- return { _("Start new search"), "", 0 }, f
- end
- if #menu_blocks ~= 0 then
- 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(_("Memory State saved to Slot ").. #menu_blocks[1])
- if (leftop == #menu_blocks[1] - 1 and rightop == #menu_blocks[1] - 2 ) then
- leftop = #menu_blocks[1]
- rightop = #menu_blocks[1]-1
- elseif (leftop == #menu_blocks[1] - 2 and rightop == #menu_blocks[1] - 1 ) then
- leftop = #menu_blocks[1]-1
- rightop = #menu_blocks[1]
- elseif (leftop == #menu_blocks[1] - 1 ) then
- leftop = #menu_blocks[1]
- elseif (rightop == #menu_blocks[1] - 1) then
- rightop = #menu_blocks[1]
- end
- leftop_text = "Slot " .. leftop
- rightop_text = "Slot " .. rightop
- devsel = devcur
- return true
- end
- end
- return { _("Save Current Memory State to Slot ") .. #menu_blocks[1] + 1, "", 0 }, f
- end
- menu[#menu + 1] = function() return { "---", "", "off" }, nil end
- menu[#menu + 1] = function()
- local function f(event)
- if event == "select" then
- local count = 0
- local step = align == 1 and formtable[width]:sub(3, 3) or "1"
- if step == "f" then
- step = 4
- elseif step == "d" then
- step = 8
- else
- step = tonumber(step)
- end
- if #matches == 0 then
- matches[1] = {}
- for num = 1, #menu_blocks do
- matches[1][num] = cheat.comp(menu_blocks[num][leftop], menu_blocks[num][rightop],
- optable[opsel], formtable[width], value, bcd == 1, step)
- count = count + #matches[1][num]
- end
- else
- lastmatch = matches[#matches]
- matches[#matches + 1] = {}
- for num = 1, #menu_blocks do
- matches[#matches][num] = cheat.compnext(menu_blocks[num][leftop], menu_blocks[num][rightop],
- lastmatch[num], optable[opsel], formtable[width], value, bcd == 1, step)
- count = count + #matches[#matches][num]
- end
- end
- manager:machine():popmessage(string.format(_("%d total matches found"), count))
- matches[#matches].count = count
- matchpg = 0
- devsel = devcur
- return true
- end
- end
- if optable[opsel] == "lt" then
- if (value == 0 ) then
- expression_text = leftop_text .. " < " .. rightop_text
- else
- expression_text = leftop_text .. " == " .. rightop_text .. " - " .. value
- end
- elseif optable[opsel] == "gt" then
- if (value == 0 ) then
- expression_text = leftop_text .. " > " .. rightop_text
- else
- expression_text = leftop_text .. " == " .. rightop_text .. " + " .. value
- end
- elseif optable[opsel] == "eq" then
- expression_text = leftop_text .. " == " .. rightop_text
- elseif optable[opsel] == "ne" then
- if (value == 0 ) then
- expression_text = leftop_text .. " != " .. rightop_text
- else
- expression_text = leftop_text .. " == " .. rightop_text .. " +/- " .. value
- end
- elseif optable[opsel] == "beq" then
- expression_text = leftop_text .. " BITWISE== " .. rightop_text
- elseif optable[opsel] == "bne" then
- expression_text = leftop_text .. " BITWISE!= " .. rightop_text
- elseif optable[opsel] == "ltv" then
- expression_text = leftop_text .. " < " .. value
- elseif optable[opsel] == "gtv" then
- expression_text = leftop_text .. " > " .. value
- elseif optable[opsel] == "eqv" then
- expression_text = leftop_text .. " == " .. value
- elseif optable[opsel] == "nev" then
- expression_text = leftop_text .. " != " .. value
- end
- return { _("Perform Compare : ") .. expression_text, "", 0 }, f
- end
- menu[#menu + 1] = function() return { "---", "", "off" }, nil end
- menu[#menu + 1] = function()
- local m = { _(leftop), "", 0 }
- menu_lim(leftop, 1, #menu_blocks[1], m)
- m[1] = "Slot " .. leftop
- return m, function(event) local r leftop, r = incdec(event, leftop, 1, #menu_blocks[1]) leftop_text = "Slot " .. leftop return r end
- end
- menu[#menu + 1] = function()
- local m = { _(optable[opsel]), "", 0 }
- menu_lim(opsel, 1, #optable, m)
- local 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"))
- elseif optable[opsel] == "gt" then
- manager:machine():popmessage(_("Left greater than right"))
- 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"))
- 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 = { _(rightop), "", 0 }
- menu_lim(rightop, 1, #menu_blocks[1], m)
- m[1] = "Slot " .. rightop
- return m, function(event) local r rightop, r = incdec(event, rightop, 1, #menu_blocks[1]) rightop_text = "Slot " .. rightop 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
- if optable[opsel] == "ltv" or optable[opsel] == "gtv" or optable[opsel] == "eqv" or optable[opsel] == "nev" then
- m = { _("Value"), value, "" }
- else
- m = { _("Difference"), value, "" }
- end
- 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()
- local m = { _("Test/Write Poke Value"), pokevaltable[pokevalsel], 0 }
- menu_lim(pokevalsel, 1, #pokevaltable, m)
- local function f(event)
- local r
- pokevalsel, r = incdec(event, pokevalsel, 1, #pokevaltable)
- if event == "left" or event == "right" or event == "comment" then
- if pokevalsel == 1 then
- manager:machine():popmessage(_("Use this if you want to poke the Slot 1 value (eg. You started with something but lost it)"))
- elseif pokevalsel == 2 then
- manager:machine():popmessage(_("Use this if you want to poke the Last Slot value (eg. You started without an item but finally got it)"))
- elseif pokevalsel == 3 then
- manager:machine():popmessage(_("Use this if you want to poke 0x00"))
- elseif pokevalsel == 4 then
- manager:machine():popmessage(_("Use this if you want to poke 0x01"))
- elseif pokevalsel == 5 then
- manager:machine():popmessage(_("Use this if you want to poke 0x02"))
- elseif pokevalsel == 6 then
- manager:machine():popmessage(_("Use this if you want to poke 0x03"))
- elseif pokevalsel == 7 then
- manager:machine():popmessage(_("Use this if you want to poke 0x04"))
- elseif pokevalsel == 8 then
- manager:machine():popmessage(_("Use this if you want to poke 0x05"))
- elseif pokevalsel == 9 then
- manager:machine():popmessage(_("Use this if you want to poke 0x06"))
- elseif pokevalsel == 10 then
- manager:machine():popmessage(_("Use this if you want to poke 0x07"))
- elseif pokevalsel == 11 then
- manager:machine():popmessage(_("Use this if you want to poke 0x08"))
- elseif pokevalsel == 12 then
- manager:machine():popmessage(_("Use this if you want to poke 0x09"))
- elseif pokevalsel == 13 then
- manager:machine():popmessage(_("Use this if you want to poke 0x63 (Decimal 99)"))
- elseif pokevalsel == 14 then
- manager:machine():popmessage(_("Use this if you want to poke 0x99 (BCD 99)"))
- elseif pokevalsel == 15 then
- manager:machine():popmessage(_("Use this if you want to poke 0xFF (Decimal 255)"))
- elseif pokevalsel == 16 then
- manager:machine():popmessage(_("Use this if you want to poke 0x3E7 (Decimal 999)"))
- elseif pokevalsel == 17 then
- manager:machine():popmessage(_("Use this if you want to poke 0x999 (BCD 999)"))
- elseif pokevalsel == 18 then
- manager:machine():popmessage(_("Use this if you want to poke 0x270F (Decimal 9999)"))
- elseif pokevalsel == 19 then
- manager:machine():popmessage(_("Use this if you want to poke 0x9999 (BCD 9999)"))
- elseif pokevalsel == 20 then
- manager:machine():popmessage(_("Use this if you want to poke 0xFFFF (Decimal 65535)"))
- end
- end
- return r
- end
- return m, f
- 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(event, bcd, 0, 1) return r end
- end
- menu[#menu + 1] = function()
- if formtable[width]:sub(3, 3) == "1" then
- return nil
- end
- local m = { "Aligned only", _("Off"), 0 }
- menu_lim(align, 0, 1, m)
- if align == 1 then
- m[2] = _("On")
- end
- return m, function(event) local r align, r = incdec(event, align, 0, 1) return r end
- end
- if #matches ~= 0 then
- menu[#menu + 1] = function()
- local function f(event)
- if event == "select" then
- matches[#matches] = nil
- matchpg = 0
- return true
- end
- end
- return { _("Undo last search -- #") .. #matches, "", 0 }, f
- end
- 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
- local function f(event)
- local r
- matchsel, r = incdec(event, matchsel, 0, #matches[#matches])
- if r then
- matchpg = 0
- end
- return r
- end
- return m, f
- end
- local function mpairs(sel, list, start)
- if #list == 0 then
- return function() end, nil, nil
- end
- if sel ~= 0 then
- list = {list[sel]}
- end
- local function mpairs_it(list, i)
- local match
- i = i + 1
- local sel = i + start
- for j = 1, #list do
- if sel <= #list[j] then
- match = list[j][sel]
- break
- else
- sel = sel - #list[j]
- end
- end
- if not match then
- return
- end
- return i, match
- end
- return mpairs_it, list, 0
- end
- local bitwidth = formtable[width]:sub(3, 3)
- if bitwidth == "2" then
- bitwidth = " %04x"
- elseif bitwidth == "4" then
- bitwidth = " %08x"
- elseif bitwidth == "8" then
- bitwidth = " %016x"
- elseif bitwidth == "f" or bitwidth == "d" then
- bitwidth = " %010f"
- else
- bitwidth = " %02x"
- end
- local function match_exec(match)
- local dev = devtable[devcur]
- local wid = formtable[width]:sub(3, 3)
- local widchar
- local pokevalue
- local form
- if pokevalsel == 1 then
- pokevalue = match.oldval
- elseif pokevalsel == 2 then
- pokevalue = match.newval
- elseif pokevalsel == 3 then
- pokevalue = 0
- elseif pokevalsel == 4 then
- pokevalue = 1
- elseif pokevalsel == 5 then
- pokevalue = 2
- elseif pokevalsel == 6 then
- pokevalue = 3
- elseif pokevalsel == 7 then
- pokevalue = 4
- elseif pokevalsel == 8 then
- pokevalue = 5
- elseif pokevalsel == 9 then
- pokevalue = 6
- elseif pokevalsel == 10 then
- pokevalue = 7
- elseif pokevalsel == 11 then
- pokevalue = 8
- elseif pokevalsel == 12 then
- pokevalue = 9
- elseif pokevalsel == 13 then
- pokevalue = 99
- elseif pokevalsel == 14 then
- pokevalue = 153
- elseif pokevalsel == 15 then
- pokevalue = 255
- elseif pokevalsel == 16 and wid == "1" then
- pokevalue = 99
- elseif pokevalsel == 17 and wid == "1" then
- pokevalue = 153
- elseif pokevalsel == 18 and wid == "1" then
- pokevalue = 99
- elseif pokevalsel == 19 and wid == "1" then
- pokevalue = 153
- elseif pokevalsel == 20 and wid == "1" then
- pokevalue = 255
- elseif pokevalsel == 16 then
- pokevalue = 999
- elseif pokevalsel == 17 then
- pokevalue = 2457
- elseif pokevalsel == 18 then
- pokevalue = 9999
- elseif pokevalsel == 19 then
- pokevalue = 39321
- elseif pokevalsel == 20 then
- pokevalue = 65535
- end
- local cheat = { desc = string.format(_("Test Cheat %08X:%02X"), match.addr, pokevalue), script = {} }
- if wid == "2" then
- wid = "u16"
- form = "%08x %04x"
- widchar = "w"
- elseif wid == "4" then
- wid = "u32"
- form = "%08x %08x"
- widchar = "d"
- elseif wid == "8" then
- wid = "u64"
- form = "%08x %016x"
- widchar = "q"
- elseif wid == "f" then
- wid = "u32"
- form = "%08x %f"
- widchar = "d"
- elseif wid == "d" then
- wid = "u64"
- form = "%08x %f"
- widchar = "q"
- else
- wid = "u8"
- form = "%08x %02x"
- widchar = "b"
- end
- if getmetatable(dev.space).__name:match("device_t") then
- cheat.ram = { ram = dev.tag }
- cheat.script.run = "ram:write(" .. match.addr .. "," .. pokevalue .. ")"
- elseif getmetatable(dev.space).__name:match("memory_share") then
- cheat.share = { share = dev.tag }
- cheat.script.run = "share:write_" .. wid .. "(" .. match.addr .. "," .. pokevalue .. ")"
- else
- cheat.space = { cpu = { tag = dev.tag, type = dev.sname } }
- cheat.script.run = "cpu:write_" .. wid .. "(" .. match.addr .. "," .. pokevalue .. ")"
- 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
- cheat_save = {}
- menu = 1
- menu_player = 1
- menu_type = 1
- local setname = emu.romname()
- if emu.softname() ~= "" then
- if emu.softname():find(":") then
- filename = emu.softname():gsub(":", "/")
- else
- for name, image in pairs(manager:machine().images) do
- if image:exists() and image:software_list_name() ~= "" then
- setname = image:software_list_name() .. "/" .. emu.softname()
- end
- end
- end
- end
- -- lfs.env_replace is defined in boot.lua
- cheat_save.path = lfs.env_replace(manager:machine():options().entries.cheatpath:value()):match("([^;]+)")
- cheat_save.filename = string.format("%s/%s", cheat_save.path, setname)
- cheat_save.name = cheat.desc
- local json = require("json")
- cheat.desc = "%s"
- cheat_save.json = json.stringify({[1] = cheat}, {indent = true})
- cheat_save.xml = string.format("<mamecheat version=\"1\">\n <cheat desc=\"%%s\">\n <script state=\"run\">\n <action>%s.pp%s@%X=%X</action>\n </script>\n </cheat>\n</mamecheat>", dev.tag:sub(2), widchar, match.addr, match.newval)
- cheat_save.simple = string.format("%s,%s,%X,%s,%X,%%s\n", setname, dev.tag, match.addr, widchar, pokevalue)
- cheat_save.dat = string.format(":%s:40000000:%X:%08X:FFFFFFFF:%%s\n", setname, match.addr, pokevalue)
- manager:machine():popmessage(string.format(_("Default name is %s"), cheat_save.name))
- return true
- else
- local func = "return space:read"
- local env = { space = devtable[devcur].space }
- if not getmetatable(dev.space).__name:match("device_t") then
- func = func .. "_" .. wid
- end
- func = func .. "(" .. match.addr .. ")"
- watches[#watches + 1] = { addr = match.addr, func = load(func, func, "t", env), format = form }
- return true
- end
- return false
- end
- for num2, match in mpairs(matchsel, matches[#matches], matchpg * 100) do
- if num2 > 100 then
- break
- end
- menu[#menu + 1] = function()
- if not match.mode then
- match.mode = 1
- end
- local modes = { _("Test"), _("Write"), _("Watch") }
- local m = { string.format("%08x" .. bitwidth .. bitwidth, match.addr, match.oldval,
- match.newval), modes[match.mode], 0 }
- menu_lim(match.mode, 1, #modes, m)
- local function f(event)
- local r
- match.mode, r = incdec(event, match.mode, 1, 3)
- if event == "select" then
- r = match_exec(match)
- end
- return r
- end
- return m, f
- end
- end
- if matches[#matches].count > 100 then
- menu[#menu + 1] = function()
- local m = { _("Page"), matchpg, 0 }
- local max
- if matchsel == 0 then
- max = math.ceil(matches[#matches].count / 100) - 1
- else
- max = #matches[#matches][matchsel]
- end
- menu_lim(matchpg, 0, max, m)
- local function f(event)
- matchpg, r = incdec(event, matchpg, 0, max)
- return r
- end
- return m, f
- end
- end
- end
- if #watches ~= 0 then
- menu[#menu + 1] = function()
- return { _("Clear Watches"), "", 0 }, function(event) if event == "select" then watches = {} return true end end
- end
- end
- end
- return menu_prepare()
- end
- local function menu_callback(index, event)
- if event == "cancel" and pausesel == 1 then
- emu.unpause()
- menu_is_showing = false
- return {0,0,0}
- end
- return menu_func[index](event)
- end
- emu.register_menu(menu_callback, menu_populate, _("Cheat Finder"))
- emu.register_frame_done(function ()
- local tag, screen = next(manager:machine().screens)
- local height = mame_manager:ui():get_line_height()
- for num, watch in ipairs(watches) do
- screen:draw_text("left", num * height, string.format(watch.format, watch.addr, watch.func()))
- end
- if tabbed_out and manager:ui():is_menu_active() then
- emu.pause()
- menu_is_showing = true
- tabbed_out = false
- end
- end)
- emu.register_periodic(function ()
- if menu_is_showing and not manager:ui():is_menu_active() then
- emu.unpause()
- menu_is_showing = false
- tabbed_out = true
- end
- end)
- end
- return exports
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement