mm/tools/overlayhelpers/damage_table.py

151 lines
4.4 KiB
Python
Executable File

#!/usr/bin/env python3
import os
import argparse
from actor_symbols import resolve_symbol
damage_types = [
'Deku Nut',
'Deku Stick',
'Horse trample',
'Explosives',
'Zora boomerang',
'Normal arrow',
'UNK_DMG_0x06',
'Hookshot',
'Goron punch',
'Sword',
'Goron pound',
'Fire arrow',
'Ice arrow',
'Light arrow',
'Goron spikes',
'Deku spin',
'Deku bubble',
'Deku launch',
'UNK_DMG_0x12',
'Zora barrier',
'Normal shield',
'Light ray',
'Thrown object',
'Zora punch',
'Spin attack',
'Sword beam',
'Normal Roll',
'UNK_DMG_0x1B',
'UNK_DMG_0x1C',
'Unblockable',
'UNK_DMG_0x1E',
'Powder Keg'
]
def convert_dmg_table_str(dmg_table_str):
split_data = dmg_table_str.replace(' ','').replace('\n','').replace('{',',').replace('}',',').split(',')
hex_data = [int(x, 16) for x in split_data if '0x' in x]
name = dmg_table_str.split('=')[0].split()[-1]
return make_table(name, hex_data)
def replace_damage_table_all(repo):
actors = 'src' + os.sep + 'overlays' + os.sep + 'actors'
for subdir, dirs, files in os.walk(repo + os.sep + actors):
for filename in files:
if(filename.endswith('.c')):
file = subdir + os.sep + filename
status = replace_damage_table(file)
if(status < 0):
print('Encountered error in', file)
elif(status > 0):
print('Updated', status, 'table' + ('s' if status > 1 else ''), file)
return 1
def replace_damage_table(src):
with open(src, 'r', encoding = 'utf-8') as srcfile:
srcdata = srcfile.readlines()
read_table = False
replacements = []
for i, line in enumerate(srcdata):
if("DamageTable" in line and "= {" in line):
if(read_table):
print('Bad formatting in', file, 'at line', i)
return -1
else:
read_table = True
start = i
if(read_table and "DMG_" in line):
read_table = False
if(read_table and ";" in line):
dmg_table_str = "".join(srcdata[start:i+1])
damage_table = convert_dmg_table_str(dmg_table_str)
replacements += [[dmg_table_str, damage_table]]
read_table = False
if len(replacements) == 0:
return 0
with open(src,'r', encoding = 'utf-8') as srcfile:
srcstr = srcfile.read()
for replace in replacements:
srcstr = srcstr.replace(replace[0], replace[1])
with open(src,'w', encoding = 'utf-8', newline = '\n') as outfile:
outfile.write(srcstr)
return len(replacements)
def make_table(name, data):
damage_table = "static DamageTable " + name + " = {\n"
linestart = " /* "
padlen = max([len(x) for x in damage_types])
for i, dbyte in enumerate(data):
typestr = damage_types[i]
typestr += ' ' * (padlen - len(typestr)) + ' */ '
damage_table += linestart + typestr + format_damage_byte(dbyte)
damage_table += '};\n'
return damage_table
def format_damage_byte(dbyte):
effect = (dbyte >> 4) & 0xF
damage = dbyte & 0xF
effstr = format(effect, 'X')
dstr = str(damage)
dtentry = 'DMG_ENTRY(' + dstr + ', 0x' + effstr + '),\n'
return dtentry
def get_damage_bytes(address):
file_name, file_offset = resolve_symbol(address)
print(hex(file_offset), "in", file_name.split(os.sep)[-1], "\n")
with open(file_name, "rb") as ovl_file:
ovl_data = bytearray(ovl_file.read())
damage_data = ovl_data[file_offset:file_offset+0x20]
damage_table = make_table('D_' + format(address, 'X'), damage_data)
return damage_table
def hex_parse(s):
return int(s, 16)
parser = argparse.ArgumentParser(description = "Converts raw bytes into a formatted damage table")
parser.add_argument('-a', metavar='address', dest='address', help="VRam or Rom address of the damage table", type=hex_parse)
parser.add_argument('-f', metavar='file', dest='file', help='C file with unformatted damage tables')
if __name__ == "__main__":
args = parser.parse_args()
script_dir = os.path.dirname(os.path.realpath(__file__))
repo = script_dir + os.sep + ".." + os.sep + ".."
if(args.address):
print(get_damage_bytes(args.address))
elif(args.file):
replace_damage_table(args.file)
else:
print('Error: address or file required. Use -h for more detail.')