mirror of
https://github.com/holub/mame
synced 2025-04-16 21:44:32 +03:00

* Change makefile rules to treat mame.pot as a target so rules can depend on it * Put mame.pot inside the build directory so it will get cleaned * Couldn't get xgettext to scrape lua and C++ in the same command and still remove stale strings * Use larger strings and format specifiers to fix some localisation issues - Issue with "None" lacking context in Russian and Turkish translations - Issue with "Not implemented" changing depending on the noun in Serbian - Issues with lua plugins not allowing for languages with different grammar/punctuation Strings that need to be translated after this change - most of these are existing text that's been made into larger chunks or reworded slightly: "Mechanical Machine\tYes\n" "Mechanical Machine\tNo\n" "Requires Artwork\tYes\n" "Requires Artwork\tNo\n" "Requires Clickable Artwork\tYes\n" "Requires Clickable Artwork\tNo\n" "Support Cocktail\tYes\n" "Support Cocktail\tNo\n" "Driver is BIOS\tYes\n" "Driver is BIOS\tNo\n" "Support Save\tYes\n" "Support Save\tNo\n" "Screen Orientation\tVertical\n" "Screen Orientation\tHorizontal\n" "Requires CHD\tYes\n" "Requires CHD\tNo\n" "ROM Audit Result\tOK\n" "ROM Audit Result\tBAD\n" "Samples Audit Result\tNone Needed\n" "Samples Audit Result\tOK\n" "Samples Audit Result\tBAD\n" "ROM Audit Disabled\t\n" "Samples Audit Disabled\t\n" "Activated: %s = %s" "Activated: %s" "Enabled: %s" "Disabled: %s" "%s added" "Default name is %s" "Cheat written to %s and added to cheat.simple" "Unable to write file\n" "Ensure that cheatpath folder exists"
869 lines
22 KiB
Lua
869 lines
22 KiB
Lua
-- 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",
|
|
-- ...
|
|
-- },
|
|
-- "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 = "The BSD 3-Clause License"
|
|
exports.author = { name = "Carl" }
|
|
|
|
local cheat = exports
|
|
|
|
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)
|
|
if emu.softname() ~= "" then
|
|
for name, image in pairs(manager:machine().images) do
|
|
if image:exists() and image:software_list_name() ~= "" then
|
|
filename = image:software_list_name() .. "/" .. emu.softname()
|
|
end
|
|
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(lfs.env_replace(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
|
|
if #hotkeys > 0 then
|
|
local json = require("json")
|
|
local path = lfs.env_replace(manager:machine():options().entries.cheatpath:value():match("([^;]+)"))
|
|
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("([^/]+)")
|
|
local attr = lfs.attributes(softpath)
|
|
if not attr then
|
|
lfs.mkdir(softpath)
|
|
elseif attr.mode ~= "directory" then -- uhhh?
|
|
return
|
|
end
|
|
end
|
|
|
|
local file = io.open(path .. "/" .. cheatname .. "_hotkeys.json", "w+")
|
|
if file then
|
|
file:write(json.stringify(hotkeys, {indent = true}))
|
|
file:close()
|
|
end
|
|
end
|
|
end
|
|
|
|
local function cheat_error(cheat, msg)
|
|
emu.print_verbose("error cheat script error: \"" .. cheat.desc .. "\" " .. msg)
|
|
cheat.desc = cheat.desc .. " error"
|
|
cheat.script = nil
|
|
cheat.enabled = nil
|
|
return
|
|
end
|
|
|
|
local function is_oneshot(cheat) return cheat.script and not cheat.script.run and not cheat.script.off 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():get_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 is_oneshot(cheat) 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 is_oneshot(cheat) 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.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 is_oneshot(cheat) 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 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():outputs(),
|
|
time = time,
|
|
input_trans = input_trans,
|
|
input_run = function(list) input_run(cheat, list) end,
|
|
table =
|
|
{ insert = table.insert,
|
|
remove = table.remove } }
|
|
cheat.enabled = false
|
|
|
|
-- 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 not scr then
|
|
local tag
|
|
tag, scr = next(manager:machine().screens) -- 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
|
|
local param = cheat.parameter
|
|
if not param then
|
|
return
|
|
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 function menu_populate()
|
|
local menu = {}
|
|
if hotkeymenu then
|
|
menu[1] = {_("Select cheat to set hotkey"), "", "off"}
|
|
menu[2] = {"---", "", "off"}
|
|
hotkeylist = {}
|
|
|
|
local function hkcbfunc(cheat)
|
|
local input = manager:machine():input()
|
|
manager:machine():popmessage(_("Press button for hotkey or wait to clear"))
|
|
manager:machine():video():frame_update(true)
|
|
input:seq_poll_start("switch")
|
|
local time = os.clock()
|
|
while (not input:seq_poll()) and (os.clock() < time + 1) do end
|
|
cheat.hotkeys = {pressed = false, keys = input:seq_poll_final()}
|
|
manager:machine():popmessage()
|
|
manager:machine():video():frame_update(true)
|
|
end
|
|
|
|
for num, cheat in ipairs(cheats) do
|
|
if cheat.script then
|
|
menu[#menu + 1] = {cheat.desc, cheat.hotkeys and manager:machine():input():seq_name(cheat.hotkeys.keys) or _("None"), ""}
|
|
hotkeylist[#hotkeylist + 1] = function() return hkcbfunc(cheat) end
|
|
end
|
|
end
|
|
menu[#menu + 1] = {"---", "", ""}
|
|
menu[#menu + 1] = {_("Done"), "", ""}
|
|
return menu
|
|
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 is_oneshot(cheat) then
|
|
menu[num][2] = _("Set")
|
|
menu[num][3] = 0
|
|
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 is_oneshot(cheat) 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] = {"---", "", 0}
|
|
menu[#menu + 1] = {_("Set hotkeys"), "", 0}
|
|
menu[#menu + 1] = {_("Reset All"), "", 0}
|
|
menu[#menu + 1] = {_("Reload All"), "", 0}
|
|
return menu
|
|
end
|
|
|
|
local function menu_callback(index, event)
|
|
manager:machine():popmessage()
|
|
if hotkeymenu then
|
|
if event == "select" then
|
|
index = index - 2
|
|
if index >= 1 and index <= #hotkeylist then
|
|
hotkeylist[index]()
|
|
return true
|
|
elseif index == #hotkeylist + 2 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
|
|
if cheat.enabled then
|
|
run_if(cheat, cheat.script.off)
|
|
bwpclr(cheat)
|
|
end
|
|
cheat.enabled = false
|
|
if cheat.parameter then
|
|
cheat.parameter.value = cheat.parameter.min
|
|
cheat.parameter.index = 0
|
|
end
|
|
end
|
|
elseif index == 4 then
|
|
for num, cheat in pairs(cheats) do
|
|
if cheat.enabled then
|
|
run_if(cheat, cheat.script.off)
|
|
bwpclr(cheat)
|
|
end
|
|
end
|
|
cheats = load_cheats()
|
|
for num, cheat in pairs(cheats) do
|
|
parse_cheat(cheat)
|
|
end
|
|
load_hotkeys()
|
|
end
|
|
return true
|
|
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
|
|
|
|
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 param = cheat.parameter
|
|
if param.index == 1 then
|
|
param.index = 0
|
|
cheat.enabled = false
|
|
cheat.cheat_env.param = param.min
|
|
run_if(cheat, cheat.script.off)
|
|
bwpclr(cheat)
|
|
return true
|
|
elseif param.index == 0 then
|
|
return false
|
|
end
|
|
param.index = param.index - 1
|
|
param_calc(param)
|
|
cheat.cheat_env.param = param.value
|
|
if not is_oneshot(cheat) then
|
|
run_if(cheat, cheat.script.change)
|
|
end
|
|
return true
|
|
else
|
|
if cheat.enabled and not is_oneshot(cheat) then
|
|
cheat.enabled = false
|
|
run_if(cheat, cheat.script.off)
|
|
bwpclr(cheat)
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
elseif event == "right" then
|
|
if cheat.parameter then
|
|
local param = cheat.parameter
|
|
if param.index == 0 then
|
|
cheat.enabled = true
|
|
run_if(cheat, cheat.script.on)
|
|
elseif param.index == param.last then
|
|
return false
|
|
end
|
|
param.index = param.index + 1
|
|
param_calc(param)
|
|
cheat.cheat_env.param = param.value
|
|
if not is_oneshot(cheat) then
|
|
run_if(cheat, cheat.script.change)
|
|
end
|
|
return true
|
|
else
|
|
if not cheat.enabled and not is_oneshot(cheat) then
|
|
cheat.enabled = true
|
|
run_if(cheat, cheat.script.on)
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
elseif event == "select" then
|
|
if is_oneshot(cheat) 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()
|
|
local subtext
|
|
if cheat.parameter.item then
|
|
subtext = cheat.parameter.item[cheat.parameter.index]
|
|
else
|
|
subtext = cheat.parameter.value
|
|
end
|
|
manager:machine():popmessage(string.format(_("Activated: %s = %s"), cheat.desc, subtext))
|
|
elseif not cheat.parameter and cheat.script.on then
|
|
cheat.script.on()
|
|
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"))
|
|
|
|
emu.register_start(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)
|
|
|
|
emu.register_stop(function()
|
|
stop = true
|
|
consolelog = nil
|
|
save_hotkeys()
|
|
end)
|
|
|
|
emu.register_frame(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 is_oneshot(cheat) 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.bgcolor, draw.linecolor)
|
|
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)
|
|
return cheats[index]
|
|
end
|
|
|
|
function ce.list()
|
|
local list = {}
|
|
for num, cheat in pairs(cheats) do
|
|
list[num] = cheat.desc
|
|
end
|
|
return list
|
|
end
|
|
|
|
_G.ce = ce
|
|
|
|
end
|
|
|
|
return exports
|