misc/tg_menuhelpers.py

113 lines
2.9 KiB
Python
Executable File

import gdb
import struct
# address for all platforms (us only atm)
TG_MENU_ID_ADDR = {
'us': 0x0036b1e8
}
tg_lang = 'us'
# TODO: maybe symbolize common states
STATE_NAME_TABLE = {
0: 'Invalid',
1: 'Pause (Underground)',
2: 'Pause (Arcade)',
3: 'Pause (Unknown?)', # dunno bout these ones
4: 'Pause (Unknown 2?)',
5: 'Invalid',
6: 'Rerun Ended',
0x7: 'Confirm Quit',
0x8: 'Confirm Restart', # dups?
0x9: 'Confirm Restart',
0xa: 'Save Game',
0xb: 'Overwrite Game',
0xc: 'Debug Menu',
0xd: 'Other Debug Settings',
0xe: 'Camera Debug',
0xf: 'Invalid', # maybe another debug options if I had to guess
0x10: 'Rendering Options',
0x11: 'Controller Configuration',
0x12: 'Arcade High Scores',
0x14: 'Audio Settings',
0x15: 'Preferences',
0x18: 'Debug Shell',
0x19: 'Shell',
0x1a: 'Stats Debug',
0x1b: 'Check Poly Totals',
0x1c: 'Memory/Polygon Budget',
0x1d: 'Permanent Allocation',
0x1e: 'Shell Allocation',
0x1f: 'Game Allocation',
0x20: 'Win',
0x21: 'Failed',
0x22: 'Survived',
0x23: 'Dead',
0x24: 'Won',
0x25: 'Lost',
0x26: 'Won dupe',
0x27: 'Lost dupe',
0x28: 'Rerun Finished',
0x29: 'Time Up',
0x30: 'Arcade'
}
def read_4b_value(address):
inferior = gdb.selected_inferior()
data = inferior.read_memory(address, 4)
return struct.unpack('<I', data)[0]
# or probably a class which wraps over gdb inferior to do stuff with it
# a bit easier. idk.
def write_4b_value(address, value):
inferior = gdb.selected_inferior()
packed_byte = struct.pack('<I', value)
inferior.write_memory(address, packed_byte)
class tgGetMenuState(gdb.Command):
def __init__(self):
super(tgGetMenuState, self).__init__("toxic-get-menu", gdb.COMMAND_USER)
def invoke(self, arg, tty):
state = read_4b_value(TG_MENU_ID_ADDR[tg_lang])
if state in STATE_NAME_TABLE:
print(f'Menu state is currently: {STATE_NAME_TABLE[state]} (0x{state:08x})')
else:
print(f'Menu state is currently: 0x{state:08x}')
class tgSetMenuState(gdb.Command):
def __init__(self):
super(tgSetMenuState, self).__init__("toxic-set-menu", gdb.COMMAND_USER)
def invoke(self, arg, tty):
args = gdb.string_to_argv(arg)
if len(args) == 1:
val = 0
try:
val = int(args[0])
except:
try:
val = int(args[0], base=16)
except:
raise ValueError('argument is not an number')
pass
write_4b_value(TG_MENU_ID_ADDR[tg_lang], val)
print(f'set Menu state 0x{val:08x}')
gdb.execute("cont", True) # continue immediately, a quick nicity
else:
raise ValueError('i need one argument please')
tgGetMenuState()
tgSetMenuState()