From 02e77eb610ac0e4c16991d4f96f15174e1a16003 Mon Sep 17 00:00:00 2001 From: cracyc Date: Tue, 13 Jun 2017 14:34:24 -0500 Subject: plugins/cheat: nes, snes, genesis game genie support [Carl] --- plugins/cheat/cheat_simple.lua | 188 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 169 insertions(+), 19 deletions(-) diff --git a/plugins/cheat/cheat_simple.lua b/plugins/cheat/cheat_simple.lua index 68c46598f75..71de504c594 100644 --- a/plugins/cheat/cheat_simple.lua +++ b/plugins/cheat/cheat_simple.lua @@ -1,3 +1,13 @@ +-- converter for simple cheats +-- simple cheats are single address every frame ram or gg,ar cheats in one file called cheat.simple +-- ram cheat format: ,,,,, +-- gg,ar cheat format: ,,, like "nes/smb,gg,SXIOPO,Infinite Lives" +-- gg for game genie -- nes, snes, megadriv +-- set name is / like "nes/smb" for softlist items +-- only program address space is supported, comments are prepended with ; +-- size is b - u8, w - u16, d - u32, q - u64 +-- Don't use commas in the description + local simple = {} simple.romset = "????" @@ -7,30 +17,170 @@ function simple.filename(name) return "cheat.simple" end --- converter for simple cheats --- simple cheats are single address every frame ram cheats in one file called cheat.simple --- format: ,,,,, --- only program address space is supported, comments are prepended with ; --- size is b - u8, w - u16, d - u32, q - u64 +local codefuncs = {} + +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 cheat = { desc = desc, region = { rom = ":nes_slot:cart:prg_rom" } } + 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 + cheat.script = { off = "if on then rom:write_u8(addr, save) end" } + 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) + cheat.script.on = string.format([[ + on = true + addr = %d + save = rom:read_u8(addr) + rom:write_u8(addr, %d)]], addr, newval) + 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 << 1) & 0x80) + -- assume 8K banks, 32K also common but is an easy multiple of 8K + cheat.script.on = string.format([[ + addr = %d + save = %d + for i = 0, rom.size, 8192 do + if rom:read_u8(i + addr) == save then + on = true + addr = i + addr + rom:write_u8(addr, %d) + break + end + end]], addr & 0x3fff, comp, newval) + else + error("error game genie cheat incorrect length " .. desc) + end + return cheat +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 + local cheat = { desc = desc, region = { rom = ":snsslot:cart:rom" } } + cheat.script = { off = "if on then rom:write_u8(addr, save) end" } + 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) + if (manager:machine().devices[":maincpu"].spaces["program"]:read_u8(0xffd5) & 1) == 1 then --hirom + local bank = addr >> 16 + local offset = addr & 0xffff + 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 game genie cheat not rom addr " .. desc) + end + else --lorom + local bank = addr >> 16 + local offset = addr & 0xffff + if (bank & 0x7f) <= 0x3f and offset >= 0x8000 then + addr = ((addr >> 1) & 0x78000) | (addr & 0x7fff) + elseif (bank & 0x7f) >= 0x40 and (bank & 0x7f) <= 0x6f then + addr = ((addr >> 1) & 0x78000) | (addr & 0x7fff) + elseif (bank & 0x7f) >= 0x70 and (bank & 0x7f) <= 0x7d and offset >= 0x8000 then + addr = ((addr >> 1) & 0x78000) | (addr & 0x7fff) + elseif bank >= 0xfe and offset >= 0x8000 then + addr = ((addr >> 1) & 0x78000) | (addr & 0x7fff) + else + error("error game genie cheat not rom addr " .. desc) + end + end + cheat.script.on = string.format([[ + addr = %d + save = rom:read_u8(addr) + on = true + rom:write_u8(addr, %d) + ]], addr, newval) + return cheat +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 + local cheat = { desc = desc, region = { rom = ":mdslot:cart:rom" } } + cheat.script = { off = "if on then rom:write_u16(addr, save) end" } + 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) + local newval = ((value >> 32) & 0xff) | ((value >> 3) & 0x1f00) | ((value << 5) & 0xe000) + local addr = (value & 0xff00ff) | ((value >> 16) & 0xff00) + cheat.script.on = string.format([[ + addr = %d + save = rom:read_u16(addr) + on = true + rom:write_u16(addr, %d) + ]], addr, newval) + return cheat +end + function simple.conv_cheat(data) local cheats = {} for line in data:gmatch('([^\n;]+)') do - local set, cputag, offset, size, val, name = line:match('([^,]+),([^,]+),([^,]+),([^,]+),([^,]+),(.+)') + local set, cputag, offset, size, val, desc = line:match('([^,]+),([^,]+),([^,]+),?([^,]*),?([^,]*),(.*)') if set == simple.romset then - local cheat = {} - cheat.desc = name - cheat.space = { cpup = { tag = cputag, type = "program" } } - if size == "w" then - size "u16" - elseif size == "d" then - size = "u32" - elseif size == "q" then - size = "u64" - else - size = "u8" + 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 + cheat = { desc = desc, space = { cpup = { tag = cputag, type = "program" } } } + if size == "w" then + size "u16" + elseif size == "d" then + size = "u32" + elseif size == "q" then + size = "u64" + else + size = "u8" + end + cheat.script = { run = "cpup:write_" .. size .. "(0x" .. offset .. ",0x" .. val .. ", true)" } + end + if cheat then + cheats[#cheats + 1] = cheat end - cheat.script = { run = "cpup:write_" .. size .. "(0x" .. offset .. ",0x" .. val .. ", true)" } - cheats[#cheats + 1] = cheat end end return cheats -- cgit v1.2.3