113 lines
2.9 KiB
Python
Executable File
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()
|