mirror of
https://github.com/Abdess/retrobios.git
synced 2026-06-27 05:02:48 +00:00
expand bios collection, retrobat at 93% coverage
This commit is contained in:
parent
851a14e49a
commit
e6ea0484a8
3946 changed files with 8119839 additions and 2930936 deletions
12
bios/Arcade/MAME/plugins/cheat/cheat_json.lua
Normal file
12
bios/Arcade/MAME/plugins/cheat/cheat_json.lua
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
local jsoncheat = {}
|
||||
|
||||
function jsoncheat.filename(name)
|
||||
return name .. ".json"
|
||||
end
|
||||
|
||||
function jsoncheat.conv_cheat(data)
|
||||
local json = require("json")
|
||||
return json.parse(data)
|
||||
end
|
||||
|
||||
return jsoncheat
|
||||
328
bios/Arcade/MAME/plugins/cheat/cheat_simple.lua
Normal file
328
bios/Arcade/MAME/plugins/cheat/cheat_simple.lua
Normal file
|
|
@ -0,0 +1,328 @@
|
|||
-- converter for simple cheats
|
||||
-- simple cheats are single/linked address every frame ram, rom or gg,ar cheats in one file called cheat.simple
|
||||
--
|
||||
-- ram/rom cheat format: <set name>,<cputag|regiontag>,<hex offset>,<b|w|d|q - size>,<hex value>,<desc>
|
||||
-- only program address space is supported, comments are prepended with ;
|
||||
-- size is b - u8, w - u16, d - u32, q - u64
|
||||
--
|
||||
-- gg,ar cheat format: <set name>,<gg|ar - type>,<code>,<desc> like "nes/smb,gg,SXIOPO,Infinite Lives"
|
||||
-- gg for game genie -- nes, snes, megadriv, gamegear, gameboy
|
||||
-- ar for action replay -- nes, snes, megadriv, gamegear, sms
|
||||
--
|
||||
-- use "^" as description to link to previous cheat
|
||||
-- set name is <softlist>/<entry> like "nes/smb" for softlist items
|
||||
-- Don't use commas in the description
|
||||
|
||||
local simple = {}
|
||||
|
||||
simple.romset = "????"
|
||||
|
||||
function simple.filename(name)
|
||||
simple.romset = name
|
||||
return "cheat.simple"
|
||||
end
|
||||
|
||||
local codefuncs = {}
|
||||
local currcheat
|
||||
|
||||
local function prepare_rom_cheat(desc, region, addr, val, size, banksize, comp)
|
||||
local cheat
|
||||
if desc:sub(1,1) ~= "^" then
|
||||
currcheat = { desc = desc, region = { rom = region } }
|
||||
currcheat.script = { off = string.format([[
|
||||
if on then
|
||||
for k, v in pairs(addrs) do
|
||||
rom:write_u%d(v.addr, v.save)
|
||||
end
|
||||
end]], size),
|
||||
on = string.format([[
|
||||
addrs = {
|
||||
--flag
|
||||
}
|
||||
on = true
|
||||
for k, v in pairs(addrs) do
|
||||
v.save = rom:read_u%d(v.addr)
|
||||
rom:write_u%d(v.addr, v.val)
|
||||
end]], size, size) }
|
||||
cheat = currcheat
|
||||
|
||||
end
|
||||
if banksize and comp then
|
||||
local rom = manager.machine.memory.regions[region]
|
||||
local bankaddr = addr & (banksize - 1)
|
||||
addr = nil
|
||||
if not rom then
|
||||
error("rom cheat invalid region " .. desc)
|
||||
end
|
||||
for i = 0, rom.size, banksize do
|
||||
if rom:read_u8(i + bankaddr) == comp then
|
||||
addr = i + bankaddr
|
||||
break
|
||||
end
|
||||
end
|
||||
if not addr then
|
||||
error("rom cheat compare value not found " .. desc)
|
||||
end
|
||||
end
|
||||
currcheat.script.on = currcheat.script.on:gsub("%-%-flag", string.format("{addr = %d, val = %d},\n--flag", addr, val), 1)
|
||||
return cheat
|
||||
end
|
||||
|
||||
local function prepare_ram_cheat(desc, tag, addr, val, size)
|
||||
local cheat
|
||||
if desc:sub(1,1) ~= "^" then
|
||||
currcheat = { desc = desc, space = { cpup = { tag = tag, type = "program" } }, script = { run = "" } }
|
||||
cheat = currcheat
|
||||
end
|
||||
currcheat.script.run = currcheat.script.run .. " cpup:write_u" .. size .. "(" .. addr .. "," .. val .. ", true)"
|
||||
return cheat
|
||||
end
|
||||
|
||||
function codefuncs.nes_gg(desc, code)
|
||||
local xlate = { A = 0, P = 1, Z = 2, L = 3, G = 4, I = 5, T = 6, Y = 7, E = 8,
|
||||
O = 9, X = 10, U = 11, K = 12, S = 13, V = 14, N = 15 }
|
||||
local value = 0
|
||||
code:upper():gsub("(.)", function(s)
|
||||
if not xlate[s] then
|
||||
error("error parsing game genie cheat " .. desc)
|
||||
end
|
||||
value = (value << 4) | xlate[s]
|
||||
end)
|
||||
local addr, newval, comp
|
||||
if #code == 6 then
|
||||
addr = ((value >> 4) & 7) | ((value >> 8) & 0x78) | ((value >> 12) & 0x80) | ((value << 8) & 0x700) | ((value << 4) & 0x7800)
|
||||
newval = ((value >> 20) & 7) | (value & 8) | ((value >> 12) & 0x70) | ((value >> 16) & 0x80)
|
||||
if manager.machine.memory.regions[":nes_slot:cart:prg_rom"].size > 32768 then
|
||||
emu.print_verbose("warning: gamegenie 6 char code with banked rom " .. desc)
|
||||
end
|
||||
return prepare_rom_cheat(desc, ":nes_slot:cart:prg_rom", addr, newval, 8)
|
||||
elseif #code == 8 then
|
||||
addr = ((value >> 12) & 7) | ((value >> 16) & 0x78) | ((value >> 20) & 0x80) | (value & 0x700) | ((value >> 4) & 0x7800)
|
||||
newval = ((value >> 28) & 7) | (value & 8) | ((value >> 20) & 0x70) | ((value >> 24) & 0x80)
|
||||
comp = ((value >> 4) & 7) | ((value >> 8) & 8) | ((value << 4) & 0x70) | (value & 0x80)
|
||||
-- try 32K banks then 8K
|
||||
local status, cheat = pcall(prepare_rom_cheat, desc, ":nes_slot:cart:prg_rom", addr, newval, 8, 32768, comp)
|
||||
if not status then
|
||||
cheat = prepare_rom_cheat(desc, ":nes_slot:cart:prg_rom", addr, newval, 8, 8192, comp)
|
||||
end
|
||||
return cheat
|
||||
else
|
||||
error("error game genie cheat incorrect length " .. desc)
|
||||
end
|
||||
end
|
||||
|
||||
function codefuncs.nes_ar(desc, code)
|
||||
code = code:gsub("[: %-]", "")
|
||||
if #code ~= 8 then
|
||||
error("error action replay cheat incorrect length " .. desc)
|
||||
end
|
||||
local newval = tonumber(code:sub(7, 8), 16)
|
||||
local addr = tonumber(code:sub(3, 6), 16)
|
||||
if not newval or not addr then
|
||||
error("error parsing action replay cheat " .. desc)
|
||||
end
|
||||
return prepare_ram_cheat(desc, ":maincpu", addr, newval, 8)
|
||||
end
|
||||
|
||||
local function snes_prepare_cheat(desc, addr, val)
|
||||
local bank = addr >> 16
|
||||
local offset = addr & 0xffff
|
||||
if ((bank <= 0x3f) and (offset < 0x2000)) or ((bank & 0xfe) == 0x7e) then
|
||||
return prepare_ram_cheat(desc, ":maincpu", addr, val, 8)
|
||||
end
|
||||
if (manager.machine.devices[":maincpu"].spaces["program"]:read_u8(0xffd5) & 1) == 1 then --hirom
|
||||
if (bank & 0x7f) <= 0x3f and offset >= 0x8000 then
|
||||
-- direct map
|
||||
elseif (bank & 0x7f) >= 0x40 and (bank & 0x7f) <= 0x7d then
|
||||
addr = addr & 0x3fffff
|
||||
elseif bank >= 0xfe then
|
||||
addr = addr & 0x3fffff
|
||||
else
|
||||
error("error cheat not rom or ram addr " .. desc)
|
||||
end
|
||||
else --lorom
|
||||
if (bank & 0x7f) <= 0x3f and offset >= 0x8000 then
|
||||
addr = ((addr >> 1) & 0x3f8000) | (addr & 0x7fff)
|
||||
elseif (bank & 0x7f) >= 0x40 and (bank & 0x7f) <= 0x6f then
|
||||
addr = ((addr >> 1) & 0x3f8000) | (addr & 0x7fff)
|
||||
elseif (bank & 0x7f) >= 0x70 and (bank & 0x7f) <= 0x7d and offset >= 0x8000 then
|
||||
addr = ((addr >> 1) & 0x3f8000) | (addr & 0x7fff)
|
||||
elseif bank >= 0xfe and offset >= 0x8000 then
|
||||
addr = ((addr >> 1) & 0x3f8000) | (addr & 0x7fff)
|
||||
else
|
||||
error("error cheat not rom or ram addr " .. desc)
|
||||
end
|
||||
end
|
||||
return prepare_rom_cheat(desc, ":snsslot:cart:rom", addr, val, 8)
|
||||
end
|
||||
|
||||
function codefuncs.snes_gg(desc, code)
|
||||
local xlate = { D = 0, F = 1, ["4"] = 2, ["7"] = 3, ["0"] = 4, ["9"] = 5, ["1"] = 6, ["5"] = 7,
|
||||
["6"] = 8, B = 9, C = 10, ["8"] = 11, A = 12, ["2"] = 13, ["3"] = 14, E = 15 }
|
||||
local value = 0
|
||||
local count = 0
|
||||
code:upper():gsub("(.)", function(s)
|
||||
if s == "-" then
|
||||
return
|
||||
elseif not xlate[s] then
|
||||
error("error parsing game genie cheat " .. desc)
|
||||
end
|
||||
count = count + 1
|
||||
value = (value << 4) | xlate[s]
|
||||
end)
|
||||
if count ~= 8 then
|
||||
error("error game genie cheat incorrect length " .. desc)
|
||||
end
|
||||
local newval = (value >> 24) & 0xff
|
||||
local addr = ((value >> 6) & 0xf) | ((value >> 12) & 0xf0) | ((value >> 6) & 0x300) | ((value << 10) & 0xc00) |
|
||||
((value >> 8) & 0xf000) | ((value << 14) & 0xf0000) | ((value << 10) & 0xf00000)
|
||||
return snes_prepare_cheat(desc, addr, newval)
|
||||
end
|
||||
|
||||
function codefuncs.snes_ar(desc, code)
|
||||
code = code:gsub("[: %-]", "")
|
||||
if #code ~= 8 then
|
||||
error("error action replay cheat incorrect length " .. desc)
|
||||
end
|
||||
local addr = tonumber(code:sub(1, 6), 16)
|
||||
local val = tonumber(code:sub(7, 8), 16)
|
||||
if not addr or not val then
|
||||
error("error parsing action replay cheat " .. desc)
|
||||
end
|
||||
return snes_prepare_cheat(desc, addr, val)
|
||||
end
|
||||
|
||||
function codefuncs.megadriv_gg(desc, code)
|
||||
local xlate = { A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6, H = 7, J = 8, K = 9, L = 10, M = 11, N = 12,
|
||||
P = 13, R = 14, S = 15, T = 16, V = 17, W = 18, X = 19, Y = 20, Z = 21, ["0"] = 22, ["1"] = 23,
|
||||
["2"] = 24, ["3"] = 25, ["4"] = 26, ["5"] = 27, ["6"] = 28, ["7"] = 29, ["8"] = 30, ["9"] = 31 }
|
||||
local value = 0
|
||||
local count = 0
|
||||
code:upper():gsub("(.)", function(s)
|
||||
if s == "-" then
|
||||
return
|
||||
elseif not xlate[s] then
|
||||
error("error parsing game genie cheat " .. desc)
|
||||
end
|
||||
count = count + 1
|
||||
value = (value << 5) | xlate[s]
|
||||
end)
|
||||
if count ~= 8 then
|
||||
error("error game genie cheat incorrect length " .. desc)
|
||||
end
|
||||
local newval = ((value >> 32) & 0xff) | ((value >> 3) & 0x1f00) | ((value << 5) & 0xe000)
|
||||
local addr = (value & 0xff00ff) | ((value >> 16) & 0xff00)
|
||||
return prepare_rom_cheat(desc, ":mdslot:cart:rom", addr, newval, 16)
|
||||
end
|
||||
|
||||
function codefuncs.megadriv_ar(desc, code)
|
||||
code = code:gsub("[: %-]", "")
|
||||
if #code ~= 10 then
|
||||
error("error action replay cheat incorrect length " .. desc)
|
||||
end
|
||||
local addr = tonumber(code:sub(1, 6), 16)
|
||||
local val = tonumber(code:sub(7, 10), 16)
|
||||
if addr < 0xff0000 then
|
||||
error("error action replay cheat not ram addr " .. desc)
|
||||
end
|
||||
return prepare_ram_cheat(desc, ":maincpu", addr, val, 16)
|
||||
end
|
||||
|
||||
local function gbgg_ggcodes(desc, code, region)
|
||||
code = code:gsub("%-", "")
|
||||
local comp
|
||||
if #code == 6 then
|
||||
comp = -1
|
||||
elseif #code == 9 then
|
||||
comp = ~tonumber(code:sub(7, 7) .. code:sub(9, 9), 16) & 0xff
|
||||
comp = ((comp >> 2) | ((comp << 6) & 0xc0)) ~ 0x45
|
||||
else
|
||||
error("error game genie cheat incorrect length " .. desc)
|
||||
end
|
||||
local newval = tonumber(code:sub(1, 2), 16)
|
||||
local addr = tonumber(code:sub(6, 6) .. code:sub(3, 5), 16)
|
||||
if not newval or not addr or not comp then
|
||||
error("error parsing game genie cheat " .. desc)
|
||||
end
|
||||
addr = (~addr & 0xf000) | (addr & 0xfff)
|
||||
if addr > 0x7fff then
|
||||
error("error game genie cheat bad addr " .. desc)
|
||||
end
|
||||
if comp == -1 then
|
||||
return prepare_rom_cheat(desc, region, addr, newval, 8)
|
||||
else
|
||||
-- assume 8K banks
|
||||
return prepare_rom_cheat(desc, region, addr, newval, 8, 8192, comp)
|
||||
end
|
||||
return cheat
|
||||
end
|
||||
|
||||
function codefuncs.gameboy_gg(desc, code)
|
||||
return gbgg_ggcodes(desc, code, ":gbslot:cart:rom")
|
||||
end
|
||||
|
||||
function codefuncs.gamegear_gg(desc, code)
|
||||
return gbgg_ggcodes(desc, code, ":slot:cart:rom")
|
||||
end
|
||||
|
||||
function codefuncs.gamegear_ar(desc, code)
|
||||
code = code:gsub("[: %-]", "")
|
||||
if #code ~= 8 then
|
||||
error("error action replay cheat incorrect length " .. desc)
|
||||
end
|
||||
local addr = tonumber(code:sub(1, 6), 16)
|
||||
local val = tonumber(code:sub(7, 8), 16)
|
||||
if addr < 0xc000 or addr >= 0xe000 then
|
||||
error("error action replay cheat not ram addr " .. desc)
|
||||
end
|
||||
return prepare_ram_cheat(desc, ":maincpu", addr, val, 8)
|
||||
end
|
||||
|
||||
codefuncs.sms_ar = codefuncs.gamegear_ar
|
||||
|
||||
function simple.conv_cheat(data)
|
||||
local cheats = {}
|
||||
for line in data:gmatch('([^\n;]+)') do
|
||||
local set, cputag, offset, size, val, desc = line:match('([^,]+),([^,]+),([^,]+),?([^,]*),?([^,]*),(.*)')
|
||||
if set == simple.romset then
|
||||
local cheat
|
||||
if cputag:sub(1,1) ~= ":" then
|
||||
local list, name = set:match('([^/]+)/(.+)')
|
||||
local func = list .. "_" .. cputag
|
||||
if list and desc and codefuncs[func] then
|
||||
local status
|
||||
status, cheat = pcall(codefuncs[func], desc, offset)
|
||||
if not status then
|
||||
emu.print_error(cheat)
|
||||
cheat = nil
|
||||
end
|
||||
end
|
||||
elseif size and val then
|
||||
if size == "w" then
|
||||
size = 16
|
||||
elseif size == "d" then
|
||||
size = 32
|
||||
elseif size == "q" then
|
||||
size = 64
|
||||
else
|
||||
size = 8
|
||||
end
|
||||
offset = tonumber(offset, 16)
|
||||
val = tonumber(val, 16)
|
||||
if manager.machine.devices[cputag] then
|
||||
cheat = prepare_ram_cheat(desc, cputag, offset, val, size)
|
||||
else
|
||||
cheat = prepare_rom_cheat(desc, cputag, offset, val, size)
|
||||
end
|
||||
end
|
||||
if cheat then
|
||||
cheats[#cheats + 1] = cheat
|
||||
end
|
||||
end
|
||||
end
|
||||
currcheat = nil
|
||||
return cheats
|
||||
end
|
||||
|
||||
return simple
|
||||
|
||||
285
bios/Arcade/MAME/plugins/cheat/cheat_xml.lua
Normal file
285
bios/Arcade/MAME/plugins/cheat/cheat_xml.lua
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
local xml = {}
|
||||
|
||||
function xml.filename(name)
|
||||
return name .. ".xml"
|
||||
end
|
||||
|
||||
-- basic xml parser for mamecheat only
|
||||
local function xml_parse(data)
|
||||
local function fix_gt(str)
|
||||
str = str:gsub(">=", " ge ")
|
||||
str = str:gsub(">", " gt ")
|
||||
return str
|
||||
end
|
||||
data = data:gsub("(condition=%b\"\")", fix_gt)
|
||||
local cheat_str = data:match("<mamecheat.->(.*)</ *mamecheat>")
|
||||
|
||||
local function get_tags(str)
|
||||
local arr = {}
|
||||
while str ~= "" do
|
||||
local tag, attr, stop
|
||||
tag, attr, stop, str = str:match("<([%w!%-]+) ?(.-)(/?)[ %-]->(.*)")
|
||||
|
||||
if not tag then
|
||||
return arr
|
||||
end
|
||||
if tag:sub(0, 3) ~= "!--" then
|
||||
local block = {}
|
||||
if stop ~= "/" then
|
||||
local nest
|
||||
nest, str = str:match("(.-)</ *" .. tag .. " *>(.*)")
|
||||
local children = get_tags(nest)
|
||||
if not next(children) then
|
||||
nest = nest:gsub("<!--.-%-%->", "")
|
||||
nest = nest:gsub("^%s*(.-)%s*$", "%1")
|
||||
block["text"] = nest
|
||||
else
|
||||
block = children
|
||||
end
|
||||
end
|
||||
if attr then
|
||||
for name, value in attr:gmatch("(%w-)=\"(.-)\"") do
|
||||
block[name] = value:gsub("^%s*(.-)%s*$", "%1")
|
||||
end
|
||||
end
|
||||
if not arr[tag] then
|
||||
arr[tag] = {}
|
||||
end
|
||||
arr[tag][#arr[tag] + 1] = block
|
||||
end
|
||||
end
|
||||
return arr
|
||||
end
|
||||
local xml_table = get_tags(cheat_str)
|
||||
return xml_table
|
||||
end
|
||||
|
||||
function xml.conv_cheat(data)
|
||||
local spaces, regions, output
|
||||
data = xml_parse(data)
|
||||
local cpu_spaces = {}
|
||||
|
||||
for tag, device in pairs(manager.machine.devices) do
|
||||
local sp
|
||||
for name, space in pairs(device.spaces) do
|
||||
if not sp then
|
||||
sp = {}
|
||||
cpu_spaces[tag] = sp
|
||||
end
|
||||
sp[space.index] = space.name
|
||||
end
|
||||
end
|
||||
|
||||
local function convert_expr(data)
|
||||
local write = false
|
||||
|
||||
local function convert_memref(cpu, phys, space, width, addr, rw)
|
||||
-- debug expressions address spaces by index not by name
|
||||
local function get_space_name(index)
|
||||
local prefix = cpu:sub(1, 1)
|
||||
if prefix == ":" then
|
||||
return cpu_spaces[cpu][index]
|
||||
else
|
||||
return cpu_spaces[":" .. cpu][index]
|
||||
end
|
||||
end
|
||||
|
||||
local mod = ""
|
||||
if space == "p" then
|
||||
fullspace = get_space_name(0)
|
||||
elseif space == "d" then
|
||||
fullspace = get_space_name(1)
|
||||
elseif space == "i" then
|
||||
fullspace = get_space_name(2)
|
||||
elseif space == "r" then
|
||||
fullspace = get_space_name(0)
|
||||
mod = "_direct"
|
||||
space = "p"
|
||||
elseif space == "o" then
|
||||
fullspace = get_space_name(3)
|
||||
mod = "_direct"
|
||||
space = "o"
|
||||
end
|
||||
if width == "b" then
|
||||
width = "u8"
|
||||
elseif width == "w" then
|
||||
width = "u16"
|
||||
elseif width == "d" then
|
||||
width = "u32"
|
||||
elseif width == "q" then
|
||||
width = "u64"
|
||||
end
|
||||
|
||||
local prefix = cpu:sub(1,1)
|
||||
if prefix == ":" then
|
||||
cpu = cpu:sub(2,cpu:len())
|
||||
end
|
||||
|
||||
local cpuname = cpu:gsub(":", "_")
|
||||
if space == "m" then
|
||||
regions[cpuname .. space] = ":" .. cpu
|
||||
else
|
||||
spaces[cpuname .. space] = { tag = ":" .. cpu, type = fullspace }
|
||||
if phys ~= "p" and mod == "" then
|
||||
mod = "v"
|
||||
end
|
||||
end
|
||||
local ret
|
||||
if rw == "=" then
|
||||
write = true
|
||||
ret = string.format("%s%s:write%s_%s(%s,", cpuname, space, mod, width, addr)
|
||||
else
|
||||
ret = string.format("%s%s:read%s_%s(%s)", cpuname, space, mod, width, addr)
|
||||
end
|
||||
if rw == "==" then
|
||||
ret = ret .. "=="
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
local function frame()
|
||||
output = true
|
||||
return "screen:frame_number()"
|
||||
end
|
||||
|
||||
data = data:lower()
|
||||
data = data:gsub("^[(](.-)[)]$", "%1")
|
||||
data = data:gsub("%f[%w]lt%f[%W]", "<")
|
||||
data = data:gsub("%f[%w]ge%f[%W]", ">=")
|
||||
data = data:gsub("%f[%w]gt%f[%W]", ">")
|
||||
data = data:gsub("%f[%w]le%f[%W]", "<=")
|
||||
data = data:gsub("%f[%w]eq%f[%W]", "==")
|
||||
data = data:gsub("%f[%w]ne%f[%W]", "~=")
|
||||
data = data:gsub("!=", "~=")
|
||||
data = data:gsub("||", " or ")
|
||||
data = data:gsub("%f[%w]frame%f[%W]", frame)
|
||||
data = data:gsub("%f[%w]band%f[%W]", "&")
|
||||
data = data:gsub("%f[%w]bor%f[%W]", "|")
|
||||
data = data:gsub("%f[%w]rshift%f[%W]", ">>")
|
||||
data = data:gsub("%f[%w]lshift%f[%W]", "<<")
|
||||
data = data:gsub("(%w-)%+%+", "%1 = %1 + 1")
|
||||
data = data:gsub("%f[%w](%x+)%f[%W]", "0x%1")
|
||||
-- 0?x? avoids an issue where db (data region byte) is interepeted as a hex number
|
||||
data = data:gsub("([%w_:]-)%.(p?)0?x?([pmrodi3])([bwdq])@(%w+) *(=*)", convert_memref)
|
||||
local count
|
||||
repeat
|
||||
data, count = data:gsub("([%w_:]-)%.(p?)0?x?([pmrodi3])([bwdq])@(%b()) *(=*)", convert_memref)
|
||||
until count == 0
|
||||
if write then
|
||||
data = data .. ")"
|
||||
end
|
||||
return data
|
||||
end
|
||||
|
||||
local function convert_output(data)
|
||||
local str = "draw_text(ui,"
|
||||
if data["align"] then
|
||||
str = str .. data["align"]
|
||||
else
|
||||
str = str .. "\"left\""
|
||||
end
|
||||
if data["line"] then
|
||||
str = str .. ",\"" .. data["line"] .. "\""
|
||||
else
|
||||
str = str .. ", \"auto\""
|
||||
end
|
||||
str = str .. ", nil,\"" .. data["format"] .. "\""
|
||||
if data["argument"] then
|
||||
for count, block in pairs(data["argument"]) do
|
||||
local expr = convert_expr(block["text"])
|
||||
if block["count"] then
|
||||
for i = 0, block["count"] - 1 do
|
||||
str = str .. "," .. expr:gsub("argindex", i)
|
||||
end
|
||||
else
|
||||
str = str .. "," .. expr
|
||||
end
|
||||
end
|
||||
end
|
||||
return str .. ")"
|
||||
end
|
||||
|
||||
local function convert_script(data)
|
||||
local str = ""
|
||||
local state = "run"
|
||||
for tag, block in pairs(data) do
|
||||
if tag == "state" then
|
||||
state = block
|
||||
elseif tag == "action" then
|
||||
for count, action in pairs(block) do
|
||||
if action["condition"] then
|
||||
str = str .. " if (" .. convert_expr(action["condition"]) .. ") then "
|
||||
for expr in action["text"]:gmatch("([^,]+)") do
|
||||
str = str .. convert_expr(expr) .. " "
|
||||
end
|
||||
str = str .. "end"
|
||||
else
|
||||
for expr in action["text"]:gmatch("([^,]+)") do
|
||||
str = str .. " " .. convert_expr(expr) .. " "
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif tag == "output" then
|
||||
output = true
|
||||
for count, output in pairs(block) do
|
||||
if output["condition"] then
|
||||
str = str .. " if " .. convert_expr(output["condition"]) .. " then "
|
||||
str = str .. convert_output(output) .. " end "
|
||||
else
|
||||
str = str .. " " .. convert_output(output) .. " "
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return state, str
|
||||
end
|
||||
|
||||
for count, cheat in pairs(data["cheat"]) do
|
||||
spaces = {}
|
||||
regions = {}
|
||||
output = false
|
||||
for tag, block in pairs(cheat) do
|
||||
if tag == "comment" then
|
||||
data["cheat"][count]["comment"] = block[1]["text"]
|
||||
elseif tag == "script" then
|
||||
local scripts = {}
|
||||
for count2, script in pairs(block) do
|
||||
local state, str = convert_script(script)
|
||||
scripts[state] = str
|
||||
end
|
||||
data["cheat"][count]["script"] = scripts
|
||||
elseif tag == "parameter" then
|
||||
if block[1]["min"] then
|
||||
block[1]["min"] = block[1]["min"]:gsub("%$","0x")
|
||||
end
|
||||
if block[1]["max"] then
|
||||
block[1]["max"] = block[1]["max"]:gsub("%$","0x")
|
||||
end
|
||||
if block[1]["step"] then
|
||||
block[1]["step"] = block[1]["step"]:gsub("%$","0x")
|
||||
end
|
||||
data["cheat"][count]["parameter"] = block[1]
|
||||
end
|
||||
end
|
||||
if next(spaces) then
|
||||
data["cheat"][count]["space"] = {}
|
||||
for name, space in pairs(spaces) do
|
||||
data["cheat"][count]["space"][name] = { type = space["type"], tag = space["tag"] }
|
||||
end
|
||||
end
|
||||
if next(regions) then
|
||||
data["cheat"][count]["region"] = {}
|
||||
for name, region in pairs(regions) do
|
||||
data["cheat"][count]["region"][name] = region
|
||||
end
|
||||
end
|
||||
if output then
|
||||
data["cheat"][count]["screen"] = {}
|
||||
data["cheat"][count]["screen"]["screen"] = ":screen"
|
||||
data["cheat"][count]["screen"]["ui"] = "ui"
|
||||
end
|
||||
end
|
||||
return data["cheat"]
|
||||
end
|
||||
|
||||
return xml
|
||||
985
bios/Arcade/MAME/plugins/cheat/init.lua
Normal file
985
bios/Arcade/MAME/plugins/cheat/init.lua
Normal file
|
|
@ -0,0 +1,985 @@
|
|||
-- license:BSD-3-Clause
|
||||
-- copyright-holders:Carl
|
||||
--
|
||||
-- json cheat file format
|
||||
-- [{
|
||||
-- "desc": "text",
|
||||
-- "parameter": {
|
||||
-- "min": "minval(0)",
|
||||
-- "max": "maxval(numitems)",
|
||||
-- "step": "stepval(1)",
|
||||
-- "item" [{
|
||||
-- "value": "itemval(index*stepval+minval)",
|
||||
-- "text": "text"
|
||||
-- },
|
||||
-- ... ]
|
||||
-- },
|
||||
-- "cpu": {
|
||||
-- "varname": "tag"
|
||||
-- ...
|
||||
-- }
|
||||
-- "space": {
|
||||
-- "varname": {
|
||||
-- "tag": "tag",
|
||||
-- "type": "program|data|io"
|
||||
-- },
|
||||
-- ...
|
||||
-- },
|
||||
-- "screen": {
|
||||
-- "varname": "tag",
|
||||
-- ...
|
||||
-- },
|
||||
-- "region": {
|
||||
-- "varname": "tag",
|
||||
-- ...
|
||||
-- },
|
||||
-- "ram": {
|
||||
-- "varname": "tag",
|
||||
-- ...
|
||||
-- },
|
||||
-- "share": {
|
||||
-- "varname": "tag",
|
||||
-- ...
|
||||
-- },
|
||||
-- "script": {
|
||||
-- "on|off|run|change": "script",
|
||||
-- ...
|
||||
-- },
|
||||
-- "comment": "text"
|
||||
-- },
|
||||
-- ... ]
|
||||
--
|
||||
-- Scripts are lua scripts with a limited api. Most library functions are unavailable.
|
||||
-- Like the XML cheats, param is the current parameter value and variables are shared between scripts within a cheat
|
||||
-- Differences from XML cheats:
|
||||
-- - actions are only one line which include the entire script
|
||||
-- - "condexpr" is replaced with lua control statements (if-then-else-end)
|
||||
-- - variables are only limited by the limits of the lua interperter, you can have strings and tables
|
||||
-- - the address spaces in the "space" blocks are accessible to the script if included,
|
||||
-- same with regions (the "m" space in debug expr)
|
||||
-- - frame is replaced by screen:frame_number() so if you use frame a screen needs to be in the device section
|
||||
-- - output is a function and argindex isn't supported, output args need to be explicit and a screen device
|
||||
-- must be provided
|
||||
-- - cpu is only used for break and watch points, if it is defined and the debugger is not enabled (-debugger none is enough)
|
||||
-- it will disable the cheat only if a point is set, check var for nil first
|
||||
-- - watch points require the address space that you want to set the watch on, wptype is "r"-read, "w"-write or "rw"-both
|
||||
|
||||
local exports = {}
|
||||
exports.name = "cheat"
|
||||
exports.version = "0.0.1"
|
||||
exports.description = "Cheat plugin"
|
||||
exports.license = "BSD-3-Clause"
|
||||
exports.author = { name = "Carl" }
|
||||
|
||||
local cheat = exports
|
||||
|
||||
local reset_subscription, stop_subscription, frame_subscription
|
||||
|
||||
function cheat.set_folder(path)
|
||||
cheat.path = path
|
||||
end
|
||||
|
||||
function cheat.startplugin()
|
||||
local cheats = {}
|
||||
local output = {}
|
||||
local line = 0
|
||||
local start_time = 0
|
||||
local stop = true
|
||||
local cheatname = ""
|
||||
local consolelog = nil
|
||||
local consolelast = 0
|
||||
local perodicset = false
|
||||
local watches = {}
|
||||
local breaks = {}
|
||||
local inputs = {}
|
||||
|
||||
local function load_cheats()
|
||||
local filename = emu.romname()
|
||||
local newcheats = {}
|
||||
local file = emu.file(manager.machine.options.entries.cheatpath:value():gsub("([^;]+)", "%1;%1/cheat") , 1)
|
||||
|
||||
for name, image in pairs(manager.machine.images) do
|
||||
if image.exists and image.software_list_name ~= "" then
|
||||
filename = image.software_list_name .. "/" .. image.filename
|
||||
end
|
||||
end
|
||||
|
||||
cheatname = filename
|
||||
local function add(addcheats)
|
||||
if not next(newcheats) then
|
||||
newcheats = addcheats
|
||||
else
|
||||
for num, cheat in pairs(addcheats) do
|
||||
newcheats[#newcheats + 1] = cheat
|
||||
end
|
||||
end
|
||||
end
|
||||
for scrfile in lfs.dir(cheat.path) do
|
||||
local name = string.match(scrfile, "^(cheat_.*).lua$")
|
||||
if name then
|
||||
local conv = require("cheat/" .. name)
|
||||
if conv then
|
||||
local ret = file:open(conv.filename(filename))
|
||||
while not ret do
|
||||
add(conv.conv_cheat(file:read(file:size())))
|
||||
ret = file:open_next()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return newcheats
|
||||
end
|
||||
|
||||
local function load_hotkeys()
|
||||
local json = require("json")
|
||||
local file = io.open(manager.machine.options.entries.cheatpath:value():match("([^;]+)") .. "/" .. cheatname .. "_hotkeys.json", "r")
|
||||
if not file then
|
||||
return
|
||||
end
|
||||
local hotkeys = json.parse(file:read("a"))
|
||||
for num, val in ipairs(hotkeys) do
|
||||
for num, cheat in pairs(cheats) do
|
||||
if val.desc == cheat.desc then
|
||||
cheat.hotkeys = {pressed = false, keys = manager.machine.input:seq_from_tokens(val.keys)}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function save_hotkeys()
|
||||
local hotkeys = {}
|
||||
for num, cheat in ipairs(cheats) do
|
||||
if cheat.hotkeys then
|
||||
local hotkey = {desc = cheat.desc, keys = manager.machine.input:seq_to_tokens(cheat.hotkeys.keys)}
|
||||
if hotkey.keys ~= "" then
|
||||
hotkeys[#hotkeys + 1] = hotkey
|
||||
end
|
||||
end
|
||||
end
|
||||
local path = manager.machine.options.entries.cheatpath:value():match("([^;]+)")
|
||||
local filepath = path .. "/" .. cheatname .. "_hotkeys.json"
|
||||
if #hotkeys > 0 then
|
||||
local json = require("json")
|
||||
local attr = lfs.attributes(path)
|
||||
if not attr then
|
||||
lfs.mkdir(path)
|
||||
elseif attr.mode ~= "directory" then -- uhhh?
|
||||
return
|
||||
end
|
||||
if cheatname:find("/", 1, true) then
|
||||
local softpath = path .. "/" .. cheatname:match("([^/]+)")
|
||||
attr = lfs.attributes(softpath)
|
||||
if not attr then
|
||||
lfs.mkdir(softpath)
|
||||
elseif attr.mode ~= "directory" then -- uhhh?
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local file = io.open(filepath, "w+")
|
||||
if file then
|
||||
file:write(json.stringify(hotkeys, {indent = true}))
|
||||
file:close()
|
||||
end
|
||||
else
|
||||
local attr = lfs.attributes(filepath)
|
||||
if attr and (attr.mode == "file") then
|
||||
local json = require("json")
|
||||
local file = io.open(filepath, "w+")
|
||||
if file then
|
||||
file:write(json.stringify(hotkeys, {indent = true}))
|
||||
file:close()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function cheat_error(cheat, msg)
|
||||
emu.print_error("error cheat script error: \"" .. cheat.desc .. "\" " .. msg)
|
||||
cheat.desc = cheat.desc .. " error"
|
||||
cheat.script = nil
|
||||
cheat.enabled = nil
|
||||
return
|
||||
end
|
||||
|
||||
local function run_if(cheat, func)
|
||||
if func then
|
||||
local stat, err = pcall(func)
|
||||
if not stat then
|
||||
cheat_error(cheat, err)
|
||||
end
|
||||
return func
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function draw_text(screen, x, y, color, form, ...)
|
||||
local str = form:format(...)
|
||||
if y == "auto" then
|
||||
y = line
|
||||
line = line + 1
|
||||
end
|
||||
if not screen then
|
||||
emu.print_verbose("draw_text: invalid screen")
|
||||
return
|
||||
end
|
||||
if type(x) == "string" then
|
||||
y = y * mame_manager.ui.line_height
|
||||
end
|
||||
output[#output + 1] = { type = "text", scr = screen, x = x, y = y, str = str, color = color }
|
||||
end
|
||||
|
||||
local function draw_line(screen, x1, y1, x2, y2, color)
|
||||
if not screen then
|
||||
emu.print_verbose("draw_line: invalid screen")
|
||||
return
|
||||
end
|
||||
output[#output + 1] = { type = "line", scr = screen, x1 = x1, x2 = x2, y1 = y1, y2 = y2, color = color }
|
||||
end
|
||||
|
||||
local function draw_box(screen, x1, y1, x2, y2, bgcolor, linecolor)
|
||||
if not screen then
|
||||
emu.print_verbose("draw_box: invalid screen")
|
||||
return
|
||||
end
|
||||
output[#output + 1] = { type = "box", scr = screen, x1 = x1, x2 = x2, y1 = y1, y2 = y2, bgcolor = bgcolor, linecolor = linecolor }
|
||||
end
|
||||
|
||||
local function tobcd(val)
|
||||
local result = 0
|
||||
local shift = 0
|
||||
while val ~= 0 do
|
||||
result = result + ((val % 10) << shift)
|
||||
val = val / 10
|
||||
shift = shift + 4
|
||||
end
|
||||
return result
|
||||
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
|
||||
|
||||
local function time()
|
||||
return emu.time() - start_time
|
||||
end
|
||||
|
||||
local function periodiccb()
|
||||
local last = consolelast
|
||||
local msg = consolelog[#consolelog]
|
||||
consolelast = #consolelog
|
||||
if #consolelog > last and msg:find("Stopped at", 1, true) then
|
||||
local point = tonumber(msg:match("Stopped at breakpoint ([0-9]+)"))
|
||||
if not point then
|
||||
point = tonumber(msg:match("Stopped at watchpoint ([0-9]+"))
|
||||
if not point then
|
||||
return -- ??
|
||||
end
|
||||
local wp = watches[point]
|
||||
if wp then
|
||||
run_if(wp.cheat, wp.func)
|
||||
-- go in case a debugger other than "none" is enabled
|
||||
-- don't use an b/wpset action because that will supress the b/wp index
|
||||
manager.machine.debugger.execution_state = "run"
|
||||
end
|
||||
else
|
||||
local bp = breaks[point]
|
||||
if bp then
|
||||
run_if(bp.cheat, bp.func)
|
||||
manager.machine.debugger.execution_state = "run"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function bpset(cheat, dev, addr, func)
|
||||
if cheat.is_oneshot then
|
||||
error("bpset not permitted in oneshot cheat")
|
||||
return
|
||||
end
|
||||
local idx = dev.debug:bpset(addr)
|
||||
breaks[idx] = {cheat = cheat, func = func, dev = dev}
|
||||
end
|
||||
|
||||
local function wpset(cheat, dev, space, wptype, addr, len, func)
|
||||
if cheat.is_oneshot then
|
||||
error("wpset not permitted in oneshot cheat")
|
||||
return
|
||||
end
|
||||
if not space.name then
|
||||
error("bad space in wpset")
|
||||
return
|
||||
end
|
||||
local idx = dev.debug:wpset(space, wptype, addr, len)
|
||||
watches[idx] = {cheat = cheat, func = func, dev = dev}
|
||||
end
|
||||
|
||||
local function bwpclr(cheat)
|
||||
if not manager.machine.debugger then
|
||||
return
|
||||
end
|
||||
for num, bp in pairs(breaks) do
|
||||
if cheat == bp.cheat then
|
||||
bp.dev.debug:bpclr(num)
|
||||
end
|
||||
end
|
||||
for num, wp in pairs(watches) do
|
||||
if cheat == wp.cheat then
|
||||
wp.dev.debug:wpclr(num)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function input_trans(list)
|
||||
local xlate = { start = {}, stop = {}, last = 0 }
|
||||
local function errout(port, field)
|
||||
cheat:set_enabled(false)
|
||||
error(port .. field .. " not found")
|
||||
return
|
||||
end
|
||||
|
||||
for num, entry in ipairs(list) do
|
||||
if entry.port:sub(1, 1) ~= ":" then
|
||||
entry.port = ":" .. entry.port
|
||||
end
|
||||
local port = manager.machine.ioport.ports[entry.port]
|
||||
if not port then
|
||||
errout(entry.port, entry.field)
|
||||
end
|
||||
local field = port.fields[entry.field]
|
||||
if not field then
|
||||
errout(entry.port, entry.field)
|
||||
end
|
||||
if not xlate.start[entry.start] then
|
||||
xlate.start[entry.start] = {}
|
||||
end
|
||||
if not xlate.stop[entry.stop] then
|
||||
xlate.stop[entry.stop] = {}
|
||||
end
|
||||
local start = xlate.start[entry.start]
|
||||
local stop = xlate.stop[entry.stop]
|
||||
local ent = { port = port, field = field }
|
||||
stop[#stop + 1] = ent
|
||||
start[#start + 1] = ent
|
||||
if entry.stop > xlate.last then
|
||||
xlate.last = entry.stop
|
||||
end
|
||||
end
|
||||
return xlate
|
||||
end
|
||||
|
||||
local function input_run(cheat, list)
|
||||
if not cheat.is_oneshot then
|
||||
cheat.enabled = false
|
||||
error("input_run only allowed in one shot cheats")
|
||||
return
|
||||
end
|
||||
local _, screen = next(manager.machine.screens)
|
||||
list.begin = screen:frame_number()
|
||||
inputs[#inputs + 1] = list
|
||||
end
|
||||
|
||||
local function param_calc(param)
|
||||
if param.item then
|
||||
if not param.item[param.index] then -- uh oh
|
||||
param.index = 1
|
||||
end
|
||||
param.value = param.item[param.index].value
|
||||
return
|
||||
end
|
||||
param.value = param.min + (param.step * (param.index - 1))
|
||||
if param.value > param.max then
|
||||
param.value = param.max
|
||||
end
|
||||
end
|
||||
|
||||
-- return is current state, ui change
|
||||
local function set_enabled(cheat, state)
|
||||
if cheat.is_oneshot then
|
||||
if state then
|
||||
if cheat.parameter and cheat.script.change and cheat.parameter.index ~= 0 then
|
||||
param_calc(cheat.parameter)
|
||||
cheat.cheat_env.param = cheat.parameter.value
|
||||
cheat.script.change()
|
||||
elseif not cheat.parameter and cheat.script.on then
|
||||
cheat.script.on()
|
||||
end
|
||||
end
|
||||
return false, false
|
||||
end
|
||||
if cheat.enabled == state then
|
||||
return state, false
|
||||
end
|
||||
if not state then
|
||||
cheat.enabled = false
|
||||
run_if(cheat, cheat.script.off)
|
||||
bwpclr(cheat)
|
||||
else
|
||||
cheat.enabled = true
|
||||
run_if(cheat, cheat.script.on)
|
||||
end
|
||||
return state, true
|
||||
end
|
||||
|
||||
-- return is current index, ui change
|
||||
local function set_index(cheat, index)
|
||||
local param = cheat.parameter
|
||||
local oldindex = param.index
|
||||
if (index < 0) or (index > param.last) or (param.index == index) then
|
||||
return param.index, false
|
||||
end
|
||||
param.index = index
|
||||
if index == 0 then
|
||||
cheat.cheat_env.param = param.min
|
||||
cheat:set_enabled(false)
|
||||
else
|
||||
if oldindex == 0 then
|
||||
cheat:set_enabled(true)
|
||||
end
|
||||
param_calc(param)
|
||||
cheat.cheat_env.param = param.value
|
||||
if not cheat.is_oneshot then
|
||||
run_if(cheat, cheat.script.change)
|
||||
end
|
||||
end
|
||||
return index, true
|
||||
end
|
||||
|
||||
local function parse_cheat(cheat)
|
||||
cheat.cheat_env = {
|
||||
draw_text = draw_text,
|
||||
draw_line = draw_line,
|
||||
draw_box = draw_box,
|
||||
tobcd = tobcd,
|
||||
frombcd = frombcd,
|
||||
pairs = pairs,
|
||||
ipairs = ipairs,
|
||||
outputs = manager.machine.output,
|
||||
time = time,
|
||||
input_trans = input_trans,
|
||||
input_run = function(list) input_run(cheat, list) end,
|
||||
os = { time = os.time, date = os.date, difftime = os.difftime },
|
||||
table = { insert = table.insert, remove = table.remove },
|
||||
string = { format = string.format, char = string.char }
|
||||
}
|
||||
cheat.enabled = false
|
||||
cheat.set_enabled = set_enabled;
|
||||
cheat.get_enabled = function(cheat) return cheat.enabled end
|
||||
cheat.is_oneshot = cheat.script and not cheat.script.run and not cheat.script.off
|
||||
|
||||
-- verify scripts are valid first
|
||||
if not cheat.script then
|
||||
return
|
||||
end
|
||||
for name, script in pairs(cheat.script) do
|
||||
script, err = load(script, cheat.desc .. name, "t", cheat.cheat_env)
|
||||
if not script then
|
||||
cheat_error(cheat, err)
|
||||
return
|
||||
end
|
||||
cheat.script[name] = script
|
||||
end
|
||||
-- initialize temp[0-9] for backward compatbility reasons
|
||||
for i = 0, 9 do
|
||||
cheat.cheat_env["temp" .. i] = 0
|
||||
end
|
||||
if cheat.cpu then
|
||||
cheat.cpudev = {}
|
||||
for name, tag in pairs(cheat.cpu) do
|
||||
if manager.machine.debugger then
|
||||
local dev = manager.machine.devices[tag]
|
||||
if not dev or not dev.debug then
|
||||
cheat_error(cheat, "missing or invalid device " .. tag)
|
||||
return
|
||||
end
|
||||
cheat.cheat_env[name] = {
|
||||
bpset = function(addr, func) bpset(cheat, dev, addr, func) end,
|
||||
wpset = function(space, wptype, addr, len, func) wpset(cheat, dev, space, wptype, addr, len, func) end,
|
||||
regs = dev.state }
|
||||
cheat.bp = {}
|
||||
cheat.wp = {}
|
||||
if not periodicset then
|
||||
emu.register_periodic(periodic_cb)
|
||||
periodicset = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if cheat.space then
|
||||
for name, space in pairs(cheat.space) do
|
||||
local cpu, mem
|
||||
cpu = manager.machine.devices[space.tag]
|
||||
if not cpu then
|
||||
cheat_error(cheat, "missing device " .. space.tag)
|
||||
return
|
||||
end
|
||||
if space.type then
|
||||
mem = cpu.spaces[space.type]
|
||||
else
|
||||
space.type = "program"
|
||||
mem = cpu.spaces["program"]
|
||||
end
|
||||
if not mem then
|
||||
cheat_error(cheat, "missing space " .. space.type)
|
||||
return
|
||||
end
|
||||
cheat.cheat_env[name] = mem
|
||||
end
|
||||
end
|
||||
if cheat.screen then
|
||||
for name, screen in pairs(cheat.screen) do
|
||||
local scr = manager.machine.screens[screen]
|
||||
if screen == "ui" then
|
||||
scr = manager.machine.render.ui_container
|
||||
elseif not scr then
|
||||
local tag
|
||||
local nxt, coll = manager.machine.screens:pairs()
|
||||
tag, scr = nxt(coll) -- get any screen
|
||||
end
|
||||
cheat.cheat_env[name] = scr
|
||||
end
|
||||
end
|
||||
if cheat.region then
|
||||
for name, region in pairs(cheat.region) do
|
||||
local mem = manager.machine.memory.regions[region]
|
||||
if not mem then
|
||||
cheat_error(cheat, "missing region " .. region)
|
||||
return
|
||||
end
|
||||
cheat.cheat_env[name] = mem
|
||||
end
|
||||
end
|
||||
if cheat.ram then
|
||||
for name, tag in pairs(cheat.ram) do
|
||||
local ram = manager.machine.devices[tag]
|
||||
if not ram then
|
||||
cheat_error(cheat, "missing ram device " .. tag)
|
||||
return
|
||||
end
|
||||
cheat.cheat_env[name] = emu.item(ram.items["0/m_pointer"])
|
||||
end
|
||||
end
|
||||
if cheat.share then
|
||||
for name, tag in pairs(cheat.share) do
|
||||
local share = manager.machine.memory.shares[tag]
|
||||
if not share then
|
||||
cheat_error(cheat, "missing share " .. share)
|
||||
return
|
||||
end
|
||||
cheat.cheat_env[name] = share
|
||||
end
|
||||
end
|
||||
local param = cheat.parameter
|
||||
if not param then
|
||||
return
|
||||
end
|
||||
cheat.set_index = set_index;
|
||||
cheat.set_value = function(cheat, value)
|
||||
local idx = ((value - cheat.parameter.min) / cheat.parameter.step) + 1
|
||||
local chg = false
|
||||
if math.integer(idx) == idx then
|
||||
idx, chg = cheat:set_index(idx)
|
||||
end
|
||||
return cheat.parameter.value, chg
|
||||
end
|
||||
cheat.get_index = function(cheat) return cheat.parameter.index end
|
||||
cheat.get_value = function(cheat) return cheat.parameter.value end
|
||||
param.min = tonumber(param.min) or 0
|
||||
param.max = tonumber(param.max) or #param.item
|
||||
param.step = tonumber(param.step) or 1
|
||||
if param.item then
|
||||
for count, item in pairs(param.item) do
|
||||
if not item.value then
|
||||
item.value = (count * param.step) + param.min
|
||||
else
|
||||
item.value = tonumber(item.value)
|
||||
end
|
||||
end
|
||||
param.last = #param.item
|
||||
else
|
||||
param.last = ((param.max - param.min) / param.step) + 1
|
||||
end
|
||||
param.index = 0
|
||||
param.value = param.min
|
||||
cheat.cheat_env.param = param.min
|
||||
end
|
||||
|
||||
local hotkeymenu = false
|
||||
local hotkeylist = {}
|
||||
local commonui
|
||||
local poller
|
||||
|
||||
local function menu_populate()
|
||||
local menu = {}
|
||||
if hotkeymenu then
|
||||
local ioport = manager.machine.ioport
|
||||
local input = manager.machine.input
|
||||
|
||||
menu[1] = {_("Select cheat to set hotkey"), "", "off"}
|
||||
menu[2] = {string.format(_("Press %s to clear hotkey"), manager.ui:get_general_input_setting(ioport:token_to_input_type("UI_CLEAR"))), "", "off"}
|
||||
menu[3] = {"---", "", "off"}
|
||||
hotkeylist = {}
|
||||
|
||||
local function hkcbfunc(cheat, event)
|
||||
if poller then
|
||||
if poller:poll() then
|
||||
if poller.sequence then
|
||||
cheat.hotkeys = { pressed = false, keys = poller.sequence }
|
||||
end
|
||||
poller = nil
|
||||
return true
|
||||
end
|
||||
elseif event == "clear" then
|
||||
cheat.hotkeys = nil
|
||||
return true
|
||||
elseif event == "select" then
|
||||
if not commonui then
|
||||
commonui = require('commonui')
|
||||
end
|
||||
poller = commonui.switch_polling_helper()
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
for num, cheat in ipairs(cheats) do
|
||||
if cheat.script then
|
||||
local setting = cheat.hotkeys and input:seq_name(cheat.hotkeys.keys) or _("None")
|
||||
menu[#menu + 1] = {cheat.desc, setting, ""}
|
||||
hotkeylist[#hotkeylist + 1] = function(event) return hkcbfunc(cheat, event) end
|
||||
end
|
||||
end
|
||||
menu[#menu + 1] = {"---", "", ""}
|
||||
menu[#menu + 1] = {_("Done"), "", ""}
|
||||
if poller then
|
||||
return poller:overlay(menu)
|
||||
else
|
||||
return menu
|
||||
end
|
||||
end
|
||||
for num, cheat in ipairs(cheats) do
|
||||
menu[num] = {}
|
||||
menu[num][1] = cheat.desc
|
||||
if not cheat.parameter then
|
||||
if not cheat.script then
|
||||
if cheat.desc == "" then
|
||||
menu[num][1] = "---"
|
||||
end
|
||||
menu[num][2] = ""
|
||||
menu[num][3] = "off"
|
||||
elseif cheat.is_oneshot then
|
||||
menu[num][2] = _("Set")
|
||||
menu[num][3] = ""
|
||||
else
|
||||
if cheat.enabled then
|
||||
menu[num][2] = _("On")
|
||||
menu[num][3] = "l"
|
||||
else
|
||||
menu[num][2] = _("Off")
|
||||
menu[num][3] = "r"
|
||||
end
|
||||
end
|
||||
else
|
||||
if cheat.parameter.index == 0 then
|
||||
if cheat.is_oneshot then
|
||||
menu[num][2] = _("Set")
|
||||
else
|
||||
menu[num][2] = _("Off")
|
||||
end
|
||||
menu[num][3] = "r"
|
||||
else
|
||||
if cheat.parameter.item then
|
||||
menu[num][2] = cheat.parameter.item[cheat.parameter.index].text
|
||||
else
|
||||
menu[num][2] = cheat.parameter.value
|
||||
end
|
||||
menu[num][3] = "l"
|
||||
if cheat.parameter.index < cheat.parameter.last then
|
||||
menu[num][3] = "lr"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
menu[#menu + 1] = {"---", "", ""}
|
||||
menu[#menu + 1] = {_("Set hotkeys"), "", ""}
|
||||
menu[#menu + 1] = {_("Reset All"), "", ""}
|
||||
menu[#menu + 1] = {_("Reload All"), "", ""}
|
||||
return menu
|
||||
end
|
||||
|
||||
local function menu_callback(index, event)
|
||||
manager.machine:popmessage()
|
||||
if hotkeymenu then
|
||||
if event == "back" then
|
||||
hotkeymenu = false
|
||||
return true
|
||||
else
|
||||
index = index - 3
|
||||
if index >= 1 and index <= #hotkeylist then
|
||||
hotkeylist[index](event)
|
||||
return true
|
||||
elseif index == #hotkeylist + 2 and event == "select" then
|
||||
hotkeymenu = false
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
if index > #cheats and event == "select" then
|
||||
index = index - #cheats
|
||||
if index == 2 then
|
||||
hotkeymenu = true
|
||||
elseif index == 3 then
|
||||
for num, cheat in pairs(cheats) do
|
||||
cheat:set_enabled(false)
|
||||
if cheat.parameter then
|
||||
cheat:set_index(0)
|
||||
end
|
||||
end
|
||||
elseif index == 4 then
|
||||
for num, cheat in pairs(cheats) do
|
||||
cheat:set_enabled(false)
|
||||
end
|
||||
cheats = load_cheats()
|
||||
for num, cheat in pairs(cheats) do
|
||||
parse_cheat(cheat)
|
||||
end
|
||||
load_hotkeys()
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local cheat = cheats[index]
|
||||
if not cheat then
|
||||
return false
|
||||
end
|
||||
if event == "up" or event == "down" or event == "comment" then
|
||||
if cheat.comment then
|
||||
manager.machine:popmessage(string.format(_("Cheat Comment:\n%s"), cheat.comment))
|
||||
end
|
||||
elseif event == "left" then
|
||||
if cheat.parameter then
|
||||
local idx, chg = cheat:set_index(cheat:get_index() - 1)
|
||||
return chg
|
||||
else
|
||||
if not cheat.is_oneshot then
|
||||
local state, chg = cheat:set_enabled(false)
|
||||
return chg
|
||||
end
|
||||
return false
|
||||
end
|
||||
elseif event == "right" then
|
||||
if cheat.parameter then
|
||||
local idx, chg = cheat:set_index(cheat:get_index() + 1)
|
||||
return chg
|
||||
else
|
||||
if not cheat.is_oneshot then
|
||||
local state, chg = cheat:set_enabled(true)
|
||||
return chg
|
||||
end
|
||||
return false
|
||||
end
|
||||
elseif event == "select" then
|
||||
if cheat.is_oneshot then
|
||||
cheat:set_enabled(true)
|
||||
if cheat.parameter and cheat.script.change and cheat:get_index() ~= 0 then
|
||||
local itemtext
|
||||
if cheat.parameter.item then
|
||||
itemtext = cheat.parameter.item[cheat.parameter.index].text
|
||||
else
|
||||
itemtext = cheat.parameter.value
|
||||
end
|
||||
manager.machine:popmessage(string.format(_("Activated: %s = %s"), cheat.desc, itemtext))
|
||||
elseif not cheat.parameter and cheat.script.on then
|
||||
manager.machine:popmessage(string.format(_("Activated: %s"), cheat.desc))
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
emu.register_menu(function(index, event)
|
||||
return menu_callback(index, event)
|
||||
end,
|
||||
function()
|
||||
return menu_populate()
|
||||
end, _("Cheat"))
|
||||
|
||||
reset_subscription = emu.add_machine_reset_notifier(function ()
|
||||
if not stop then
|
||||
return
|
||||
end
|
||||
stop = false
|
||||
start_time = emu.time()
|
||||
cheats = load_cheats()
|
||||
local json = require("json")
|
||||
local file = io.open(manager.machine.options.entries.cheatpath:value():match("([^;]+)") .. "/output.json", "w")
|
||||
if file then
|
||||
file:write(json.stringify(cheats, {indent = true}))
|
||||
file:close()
|
||||
end
|
||||
for num, cheat in pairs(cheats) do
|
||||
parse_cheat(cheat)
|
||||
end
|
||||
load_hotkeys()
|
||||
if manager.machine.debugger then
|
||||
consolelog = manager.machine.debugger.consolelog
|
||||
consolelast = 0
|
||||
end
|
||||
end)
|
||||
|
||||
stop_subscription = emu.add_machine_stop_notifier(function ()
|
||||
stop = true
|
||||
consolelog = nil
|
||||
save_hotkeys()
|
||||
end)
|
||||
|
||||
frame_subscription = emu.add_machine_frame_notifier(function ()
|
||||
if stop then
|
||||
return
|
||||
end
|
||||
for num, cheat in pairs(cheats) do
|
||||
if cheat.enabled then
|
||||
run_if(cheat, cheat.script.run)
|
||||
end
|
||||
if cheat.hotkeys and cheat.hotkeys.keys then
|
||||
if manager.machine.input:seq_pressed(cheat.hotkeys.keys) then
|
||||
if not cheat.hotkeys.pressed then
|
||||
if cheat.is_oneshot then
|
||||
if not run_if(cheat, cheat.script.change) then
|
||||
run_if(cheat, cheat.script.on)
|
||||
end
|
||||
manager.machine:popmessage(string.format(_("Activated: %s"), cheat.desc))
|
||||
elseif not cheat.enabled then
|
||||
cheat.enabled = true
|
||||
run_if(cheat, cheat.script.on)
|
||||
manager.machine:popmessage(string.format(_("Enabled: %s"), cheat.desc))
|
||||
else
|
||||
cheat.enabled = false
|
||||
run_if(cheat, cheat.script.off)
|
||||
bwpclr(cheat)
|
||||
manager.machine:popmessage(string.format(_("Disabled: %s"), cheat.desc))
|
||||
end
|
||||
end
|
||||
cheat.hotkeys.pressed = true
|
||||
else
|
||||
cheat.hotkeys.pressed = false
|
||||
end
|
||||
end
|
||||
end
|
||||
for num, input in pairs(inputs) do
|
||||
local _, screen = next(manager.machine.screens)
|
||||
local framenum = screen:frame_number() - input.begin
|
||||
local enttab = input.start[framenum]
|
||||
if enttab then
|
||||
for num, entry in pairs(enttab) do
|
||||
entry.field:set_value(1)
|
||||
end
|
||||
end
|
||||
enttab = input.stop[framenum]
|
||||
if enttab then
|
||||
for num, entry in pairs(enttab) do
|
||||
entry.field:set_value(0)
|
||||
|
||||
end
|
||||
end
|
||||
if framenum >= input.last then
|
||||
table.remove(inputs, num)
|
||||
end
|
||||
end
|
||||
|
||||
end)
|
||||
|
||||
emu.register_frame_done(function()
|
||||
if stop then
|
||||
return
|
||||
end
|
||||
line = 0
|
||||
for num, draw in pairs(output) do
|
||||
if draw.type == "text" then
|
||||
if not draw.color then
|
||||
draw.scr:draw_text(draw.x, draw.y, draw.str)
|
||||
else
|
||||
draw.scr:draw_text(draw.x, draw.y, draw.str, draw.color)
|
||||
end
|
||||
elseif draw.type == "line" then
|
||||
draw.scr:draw_line(draw.x1, draw.y1, draw.x2, draw.y2, draw.color)
|
||||
elseif draw.type == "box" then
|
||||
draw.scr:draw_box(draw.x1, draw.y1, draw.x2, draw.y2, draw.linecolor, draw.bgcolor)
|
||||
end
|
||||
end
|
||||
output = {}
|
||||
end)
|
||||
|
||||
local ce = {}
|
||||
|
||||
-- interface to script cheat engine
|
||||
function ce.inject(newcheat)
|
||||
cheats[#cheats + 1] = newcheat
|
||||
parse_cheat(newcheat)
|
||||
manager.machine:popmessage(string.format(_("%s added"), newcheat.desc))
|
||||
end
|
||||
|
||||
function ce.get(index)
|
||||
local cheat = cheats[index]
|
||||
if not cheat then
|
||||
return nil
|
||||
end
|
||||
local intf = {
|
||||
get_enabled = function() return cheat:get_enabled() end,
|
||||
set_enabled = function(status) return cheat:set_enabled(status) end,
|
||||
desc = cheat.desc,
|
||||
is_oneshot = cheat.is_oneshot,
|
||||
comment = cheat.comment,
|
||||
get_hotkeys = function() if cheat.hotkeys then return cheat.hotkeys.keys end return nil end,
|
||||
set_hotkeys = function(seq) cheat.hotkeys = { pressed = false, keys = manager.machine.input:seq_clean(seq) } end
|
||||
}
|
||||
if cheat.script then
|
||||
intf.script = {}
|
||||
if cheat.script.on then intf.script.on = true end
|
||||
if cheat.script.off then intf.script.off = true end
|
||||
if cheat.script.run then intf.script.run = true end
|
||||
if cheat.script.change then intf.script.change = true end
|
||||
end
|
||||
|
||||
if cheat.parameter then
|
||||
intf.parameter = {}
|
||||
intf.get_value = function() return cheat:get_value() end
|
||||
intf.set_value = function(value) return cheat:set_value(value) end
|
||||
intf.get_index = function() return cheat:get_index() end
|
||||
intf.set_index = function(index) return cheat:set_index(index) end
|
||||
intf.parameter.min = cheat.parameter.min
|
||||
intf.parameter.max = cheat.parameter.max
|
||||
intf.parameter.step = cheat.parameter.step
|
||||
if cheat.parameter.item then
|
||||
intf.parameter.item = {}
|
||||
for idx, item in pairs(cheat.parameter.item) do
|
||||
intf.parameter.item[idx] = {}
|
||||
intf.parameter.item[idx].text = cheat.parameter.item[idx].text
|
||||
intf.parameter.item[idx].value = cheat.parameter.item[idx].value
|
||||
end
|
||||
end
|
||||
end
|
||||
return intf
|
||||
end
|
||||
|
||||
function ce.list()
|
||||
local list = {}
|
||||
for num, cheat in pairs(cheats) do
|
||||
list[num] = cheat.desc
|
||||
end
|
||||
return list
|
||||
end
|
||||
|
||||
_G.emu.plugin.cheat = ce
|
||||
|
||||
end
|
||||
|
||||
return exports
|
||||
10
bios/Arcade/MAME/plugins/cheat/plugin.json
Normal file
10
bios/Arcade/MAME/plugins/cheat/plugin.json
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"plugin": {
|
||||
"name": "cheat",
|
||||
"description": "Cheat plugin",
|
||||
"version": "0.0.1",
|
||||
"author": "Carl",
|
||||
"type": "plugin",
|
||||
"start": "false"
|
||||
}
|
||||
}
|
||||
11
bios/Arcade/MAME/plugins/cheat/xml_to_json.lua
Normal file
11
bios/Arcade/MAME/plugins/cheat/xml_to_json.lua
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
xml = require("cheat_xml")
|
||||
json = dofile("../json/init.lua")
|
||||
|
||||
function readAll(file)
|
||||
local f = io.open(file, "rb")
|
||||
local content = f:read("*all")
|
||||
f:close()
|
||||
return content
|
||||
end
|
||||
|
||||
print(json.stringify(xml.conv_cheat(readAll(arg[1])), {indent = true}))
|
||||
Loading…
Add table
Add a link
Reference in a new issue