102 lines
3.2 KiB
Python
102 lines
3.2 KiB
Python
|
import sys
|
||
|
import os
|
||
|
from GeckoFactory import int2bytes, make06, makeC0
|
||
|
|
||
|
validArguments = '''
|
||
|
C0:{input.bin}
|
||
|
hook:{input.map}:{input.bin}:{symbol=fromAddr}...
|
||
|
'''[1:-1]
|
||
|
|
||
|
def exitInvalidSyntax(arg):
|
||
|
print('Invalid argument `%s`\nValid arguments:\n%s'%(arg, validArguments))
|
||
|
sys.exit(1)
|
||
|
def exitMessage(msg):
|
||
|
print(msg)
|
||
|
sys.exit(1)
|
||
|
|
||
|
def printCode(raw, file=sys.stdout):
|
||
|
assert len(raw)%8 == 0, \
|
||
|
f'Expect code with length of multiple of 8, got {len(raw)}'
|
||
|
for i in range(0, len(raw), 8):
|
||
|
print(raw[i:i+4].hex().upper(), raw[i+4:i+8].hex().upper(), file=file)
|
||
|
|
||
|
def loadHookDB(fn):
|
||
|
import csv
|
||
|
with open(fn) as f:
|
||
|
reader = csv.DictReader(f)
|
||
|
return {
|
||
|
row['symbol']: row
|
||
|
for row in reader
|
||
|
}
|
||
|
|
||
|
def parseMap(fn):
|
||
|
with open(fn) as f:
|
||
|
sections = {}
|
||
|
symbols = {}
|
||
|
for line in f:
|
||
|
args = line.split()
|
||
|
try:
|
||
|
if line.startswith('.') and len(args) == 3:
|
||
|
# .text 0x00000000817fe800 0x1dc
|
||
|
sections[args[0]] = int(args[1], 16)
|
||
|
elif len(args) == 2:
|
||
|
# 0x00000000817fe800 loadCard
|
||
|
symbols[args[1]] = int(args[0], 16)
|
||
|
except: pass
|
||
|
return sections, symbols
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
if len(sys.argv) < 2:
|
||
|
print('Usage: %s {ARG}...\nARG:\n%s'%(sys.argv[0], validArguments))
|
||
|
sys.exit(1)
|
||
|
|
||
|
__dirname = os.path.dirname(__file__)
|
||
|
hookDB = loadHookDB(os.path.join(__dirname, './hook.csv'))
|
||
|
|
||
|
code = b''
|
||
|
for arg0 in sys.argv[1:]:
|
||
|
toks = arg0.split(':', 1)
|
||
|
if len(toks) != 2: exitInvalidSyntax(arg)
|
||
|
ct, arg = toks
|
||
|
if ct == 'C0':
|
||
|
code += makeC0(arg)
|
||
|
elif ct == 'hook':
|
||
|
args = arg.split(':')
|
||
|
if len(args) < 3: exitInvalidSyntax(arg)
|
||
|
fnMap, fnBin, *entries = args
|
||
|
## parse map file
|
||
|
sections, symbols = parseMap(fnMap)
|
||
|
assert '.text' in sections, f'.text section does not exists in file {fnMap}'
|
||
|
dst = sections['.text']
|
||
|
## 06
|
||
|
code += make06(dst, fnBin)
|
||
|
## 04 entries
|
||
|
for entry in entries:
|
||
|
toks = entry.split('=')
|
||
|
name = toks[0]
|
||
|
if len(toks) == 1:
|
||
|
version = os.environ.get('GAME_VERSION')
|
||
|
if not version: exitMessage('Environment variable `GAME_VERSION` is not set. Please set the GAME_VERSION environment variable or specify the address manually')
|
||
|
if name not in hookDB: exitMessage('The address for symbol `%s` is unknown. Please specify it manually\nSyntax: {name}={addr}'%name)
|
||
|
hook = hookDB[name]
|
||
|
if version not in hook: exitMessage(f'Invalid GAME_VERSION: {version}')
|
||
|
addr0 = int(hook[version], 16)
|
||
|
elif len(toks) == 2:
|
||
|
try:
|
||
|
addr0 = int(toks[1], 16)
|
||
|
except ValueError:
|
||
|
exitInvalidSyntax(arg)
|
||
|
else:
|
||
|
exitInvalidSyntax(arg)
|
||
|
assert name in symbols, f'symbol `{name}` does not exists in file {fnMap}'
|
||
|
addr1 = symbols[name]
|
||
|
dis = addr1 - addr0
|
||
|
assert -0x200_0000 <= dis < 0x200_0000, 'too far to branch from %s(%08X) to %08X'%(name, addr0, addr1)
|
||
|
## make 04
|
||
|
code += int2bytes(0x0400_0000 | addr0 & 0x1ff_ffff)
|
||
|
code += int2bytes(0x4800_0001 | dis & 0x3ff_ffff)
|
||
|
else:
|
||
|
exitInvalidSyntax(arg)
|
||
|
|
||
|
printCode(code)
|