mirror of
https://github.com/sonicdcer/sf64.git
synced 2024-11-23 04:50:05 +00:00
Fixed and imported sym_info and name_fixer tools (#73)
This commit is contained in:
parent
3036b48338
commit
40af0ffc3d
29
sym_info.py
Normal file
29
sym_info.py
Normal file
@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
import mapfile_parser
|
||||
except ImportError:
|
||||
print("Missing dependency mapfile_parser, install it with `python3 -m pip install 'mapfile-parser>=1.2.1,<2.0.0'`")
|
||||
exit(1)
|
||||
|
||||
|
||||
def symInfoMain():
|
||||
parser = argparse.ArgumentParser(description="Display various information about a symbol or address.")
|
||||
parser.add_argument("symname", help="symbol name or VROM/VRAM address to lookup")
|
||||
parser.add_argument("-e", "--expected", dest="use_expected", action="store_true", help="use the map file in expected/build/ instead of build/")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
BUILTMAP = Path(f"build") / f"starfox64.us.map"
|
||||
|
||||
mapPath = BUILTMAP
|
||||
if args.use_expected:
|
||||
mapPath = "expected" / BUILTMAP
|
||||
|
||||
mapfile_parser.frontends.sym_info.doSymInfo(mapPath, args.symname)
|
||||
|
||||
if __name__ == "__main__":
|
||||
symInfoMain()
|
147
tools/name_fixer.py
Normal file
147
tools/name_fixer.py
Normal file
@ -0,0 +1,147 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import argparse
|
||||
|
||||
# all occurrences of keys will be replaced by associated value
|
||||
simpleReplace = {}
|
||||
|
||||
# all occurrences of keys will be replaced by associated value,
|
||||
# if the occurence is the whole word
|
||||
# for example, if there is a space before and an open parenthesis after,
|
||||
# like for a function call: ` func_8002E4B4(`
|
||||
#
|
||||
# Custom behaviour can be enabled by using a tuple as the value (see
|
||||
# explanation in replace_single below)
|
||||
wordReplace = {
|
||||
"func_80086B30": "new_func_naame",
|
||||
"var": "new_var_name",
|
||||
}
|
||||
|
||||
# [a-zA-Z0-9_]
|
||||
def is_word_char(c):
|
||||
return (c >= 'a' and c <= 'z') or (c >= 'A' and c <= 'Z') or (c >= '0' and c <= '9') or c == '_'
|
||||
|
||||
def replace_single(file):
|
||||
with open(file, 'r', encoding = 'utf-8') as infile:
|
||||
srcdata = infile.read()
|
||||
|
||||
changesCount = 0
|
||||
|
||||
for old, new in simpleReplace.items():
|
||||
# replace `old` with `new`
|
||||
if old in srcdata:
|
||||
changesCount += 1
|
||||
print(old, "->", new)
|
||||
srcdata = srcdata.replace(old, new)
|
||||
|
||||
for old, new in wordReplace.items():
|
||||
# `new` can be a tuple where the first element is what to replace `old` with,
|
||||
# and the second element is a dict containing "custom behavior" properties.
|
||||
if isinstance(new, tuple):
|
||||
custom_behavior = True
|
||||
new, custom_behavior_data = new
|
||||
# The "ignore" data is a tuple where the first element is an offset relative to
|
||||
# where `old` was found, and the string from that index must differ from the
|
||||
# tuple's second element for the replacement to be done.
|
||||
custom_behavior_ignore_data = custom_behavior_data.get("ignore")
|
||||
custom_behavior_ignore = custom_behavior_ignore_data is not None
|
||||
if custom_behavior_ignore:
|
||||
custom_behavior_ignore_offset, custom_behavior_ignore_match = custom_behavior_ignore_data
|
||||
else:
|
||||
custom_behavior = False
|
||||
# replace `old` with `new` if the occurence of `old` is the whole word
|
||||
oldStartIdx = srcdata.find(old)
|
||||
if oldStartIdx >= 0:
|
||||
old_start_as_word = is_word_char(old[0])
|
||||
old_end_as_word = is_word_char(old[-1])
|
||||
replaceCount = 0
|
||||
while oldStartIdx >= 0:
|
||||
replace = True
|
||||
if old_start_as_word:
|
||||
if oldStartIdx == 0:
|
||||
pass
|
||||
elif is_word_char(srcdata[oldStartIdx-1]):
|
||||
replace = False
|
||||
if old_end_as_word:
|
||||
oldEndIdx = oldStartIdx + len(old)
|
||||
if oldEndIdx >= len(srcdata):
|
||||
pass
|
||||
elif is_word_char(srcdata[oldEndIdx]):
|
||||
replace = False
|
||||
if replace and custom_behavior and custom_behavior_ignore:
|
||||
if srcdata[oldStartIdx + custom_behavior_ignore_offset:].startswith(custom_behavior_ignore_match):
|
||||
replace = False
|
||||
if replace:
|
||||
srcdata = srcdata[:oldStartIdx] + new + srcdata[oldEndIdx:]
|
||||
replaceCount += 1
|
||||
oldStartIdx = srcdata.find(old, oldStartIdx + len(new))
|
||||
if replaceCount > 0:
|
||||
changesCount += 1
|
||||
print(old, "->", new)
|
||||
|
||||
if changesCount > 0:
|
||||
print('Changed', changesCount, 'entry' if changesCount == 1 else 'entries', 'in', file)
|
||||
with open(file, 'w', encoding = 'utf-8', newline = '\n') as outfile:
|
||||
outfile.write(srcdata)
|
||||
|
||||
def replace_all(repo):
|
||||
for subdir, dirs, files in os.walk(os.path.join(repo,'src')):
|
||||
for filename in files:
|
||||
if filename.endswith('.c') or filename.endswith('.h'):
|
||||
file = os.path.join(subdir,filename)
|
||||
replace_single(file)
|
||||
|
||||
for subdir, dirs, files in os.walk(os.path.join(repo,'asm')):
|
||||
for filename in files:
|
||||
if filename.endswith('.s'):
|
||||
file = os.path.join(subdir,filename)
|
||||
replace_single(file)
|
||||
|
||||
for subdir, dirs, files in os.walk(os.path.join(repo,'data')):
|
||||
for filename in files:
|
||||
if filename.endswith('.s'):
|
||||
file = os.path.join(subdir,filename)
|
||||
replace_single(file)
|
||||
|
||||
for subdir, dirs, files in os.walk(os.path.join(repo,'docs')):
|
||||
for filename in files:
|
||||
if filename.endswith('.md'):
|
||||
file = os.path.join(subdir,filename)
|
||||
replace_single(file)
|
||||
|
||||
|
||||
def dictSanityCheck():
|
||||
keys = wordReplace.keys()
|
||||
values = wordReplace.values()
|
||||
for k in keys:
|
||||
if k in values:
|
||||
print(f"Key '{k}' found in values")
|
||||
print(f"This would produce unintended renames")
|
||||
print(f"Fix this by removing said key from the dictionary")
|
||||
exit(-1)
|
||||
|
||||
keys = simpleReplace.keys()
|
||||
values = {*wordReplace.values(), *simpleReplace.values()}
|
||||
for k in keys:
|
||||
for value in values:
|
||||
if k in value:
|
||||
print(f"Key '{k}' found in values")
|
||||
print(f"This would produce unintended renames")
|
||||
print(f"Fix this by removing said key from the dictionary")
|
||||
exit(-1)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Apply function renames to a file')
|
||||
parser.add_argument('file', help="source file to be processed. use . to process the whole repo")
|
||||
args = parser.parse_args()
|
||||
|
||||
dictSanityCheck()
|
||||
|
||||
if args.file == '.':
|
||||
replace_all(os.curdir)
|
||||
else:
|
||||
replace_single(args.file)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Reference in New Issue
Block a user