summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorAetias <aetias@outlook.com>2024-02-26 21:13:23 +0100
committerAetias <aetias@outlook.com>2024-02-26 21:13:23 +0100
commit09fc5602927f67948a5f91ef4c10eacc308ea2cb (patch)
treee34b6995ee99dc17bc1059ff4b489a5d3f85f0f7 /tools
parentb736e9e539a91f62afa1a391f85a115a44024d15 (diff)
Add `progress.py`
Diffstat (limited to 'tools')
-rw-r--r--tools/progress.py128
1 files changed, 128 insertions, 0 deletions
diff --git a/tools/progress.py b/tools/progress.py
new file mode 100644
index 00000000..70bfcf4d
--- /dev/null
+++ b/tools/progress.py
@@ -0,0 +1,128 @@
+import sys
+import os
+import re
+from pathlib import Path
+
+
+def eprint(*args, **kwargs):
+ print(*args, file=sys.stderr, **kwargs)
+
+
+def remove_whitespace(s: str):
+ return ''.join(s.split())
+
+
+def get_nonmatching_funcs(source: str):
+ # Example:
+ # ARM bool NONMATCH(ItemManager::func_ov00_020ad790)(unk32 param1)
+ # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ funcs = re.findall(r'NONMATCH\W*\(\W*([^\)]+)', source)
+ return [remove_whitespace(func) for func in funcs]
+
+
+def get_all_nonmatching_funcs():
+ funcs = []
+ for root, dirs, files in os.walk('src'):
+ for file in files:
+ name, ext = file.rsplit('.', 1)
+ if ext not in ['c', 'cpp']: continue
+ with open(os.path.join(root, file), 'r') as f:
+ source = f.read()
+ funcs.extend(get_nonmatching_funcs(source))
+ return set(funcs)
+
+
+class Section:
+ def __init__(self, name: str) -> None:
+ self.name = name
+ # Number of bytes from assembly, C/C++ and nonmatching code
+ # In total, they amount to the entire code
+ self.bytes_asm = 0
+ self.bytes_c_cpp = 0
+ self.bytes_nonmatch = 0
+
+ def total(self):
+ return self.bytes_asm + self.bytes_c_cpp + self.bytes_nonmatch
+
+
+nonmatching_funcs = get_all_nonmatching_funcs()
+sections = ['.text', '.rodata', '.data', '.bss', '.init']
+sections = {name: Section(name) for name in sections}
+
+map_path = None
+regions = ['eur', 'usa']
+for region in regions:
+ map_path = Path(f'build/{region}/main.bin.xMAP')
+ if map_path.is_file(): break
+if map_path is None:
+ eprint('You must build the ROM first!')
+ exit(1)
+
+with open(f'build/{region}/main.bin.xMAP', 'r') as f:
+ line = '\n'
+ while len(line) > 0:
+ # 020AD090 00000028 .text ItemManager::Create() (ItemManager.cpp.o)
+ line = f.readline()
+ words = line.split()
+ if len(words) < 5: continue
+
+ # 020AD090
+ addr = words[0]
+ try: addr = int(addr, base=16)
+ except: continue
+
+ # 00000028
+ size = words[1]
+ try: size = int(size, base=16)
+ except: continue
+
+ # .text
+ section = words[2]
+ # Much of .sbss hasn't been defined yet, so group it together with .bss
+ if section == '.sbss': section = '.bss'
+ # .ctor and .init are coupled together
+ if section == '.ctor': section = '.init'
+ section = sections[section]
+
+ # ItemManager::Create()
+ func = words[3]
+ if func == '.text': continue
+ # ItemManager::Create
+ func = func.split('(', 1)[0]
+ is_nonmatch = func in nonmatching_funcs
+ if is_nonmatch: nonmatching_funcs.remove(func)
+
+ # (ItemManager.cpp.o)
+ file = words[-1]
+ if not file.startswith('('): continue
+ if not file.endswith(')'): continue
+ # ItemManager.cpp.o
+ file = file[1:-1]
+
+ is_asm = file.endswith('.s.o')
+ is_c_cpp = file.endswith('.c.o') or file.endswith('.cpp.o')
+ if not is_asm and not is_c_cpp:
+ eprint(f"Unknown file extension for '{file}'")
+ continue
+
+ if is_asm: section.bytes_asm += size
+ if is_c_cpp:
+ if is_nonmatch: section.bytes_nonmatch += size
+ else: section.bytes_c_cpp += size
+
+if len(nonmatching_funcs) > 0:
+ eprint('Some nonmatching functions were not found in the map file:')
+ eprint('\n'.join(list(nonmatching_funcs)))
+ eprint()
+
+for name, section in sections.items():
+ total = section.total()
+ percent_asm = 100 * (section.bytes_asm / total)
+ percent_c_cpp = 100 * (section.bytes_c_cpp / total)
+ percent_nonmatch = 100 * (section.bytes_nonmatch / total)
+
+ print(f'{name}:')
+ print(f' asm = {section.bytes_asm:7} bytes ({percent_asm:7.3f}%)')
+ print(f' C/C++ = {section.bytes_c_cpp:7} bytes ({percent_c_cpp:7.3f}%)')
+ print(f' nonmatch = {section.bytes_nonmatch:7} bytes ({percent_nonmatch:7.3f}%)')
+ print()