SMS-GCI-Loader/gecko/bin2gct.py
sup39 23e5c658ad Add script to build gecko code of GCI Loader
- GCI Loader: gecko-gosub + 06 bin + 04 bl to onReadOptionBlock
- place GCI Loader code at 817FE800 since [817FEEA0, 81800000) is used
  by system
- place buffer at 817F1800 (instead of 817F1000+4) since there seems to
  be some alignment issue
- Assume file size == 0x2000*6 since fileInfo->length seems to be 0
2023-02-11 19:36:44 +09:00

101 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)