pmd-sky/tools/sync_pmdsky_debug/sync_from_pmdsky_debug.py
UsernameFodder 3d87a95850 Support syncing BSS symbols to pmdsky-debug
These data symbols can be added to ram.yml. Since information is lost,
it's not possible to go in the other direction, so RAM syncing can only
be done TO pmdsky-debug.
2023-12-28 23:50:54 -06:00

131 lines
6.6 KiB
Python

import os
import re
from typing import List
from pmdsky_debug_reader import read_pmdsky_debug_symbols
from symbol_details import MIXED_CASE_SYMBOLS_ARM7, MIXED_CASE_SYMBOLS_ARM9, WRAM_OFFSET
from xmap_reader import HEADER_FOLDER, read_xmap_symbols
# Syncs symbols from pmdsky-debug (https://github.com/UsernameFodder/pmdsky-debug) to the decomp.
# To use this script, you will need:
# - A file named pmdsky_debug_location.txt with the file path to your local clone of pmdsky-debug.
# - Python dependencies in requirements.txt.
# Make sure there are no uncommitted changes when running this, in case you need to revert.
pmdsky_debug_symbols = read_pmdsky_debug_symbols()
xmap_symbols = read_xmap_symbols()
asm_files = []
"""
Searches for all files within a directory that have certain extensions.
"""
def add_files_with_extensions(folder: str, extensions: List[str]) -> List[str]:
found_files = []
for root, _, files in os.walk(folder):
for file in files:
for extension in extensions:
if file.endswith(extension):
found_files.append(os.path.join(root, file))
break
return found_files
asm_files = add_files_with_extensions('asm', ['.s', '.inc'])
asm_arm7_files = add_files_with_extensions(os.path.join('sub', 'asm'), ['.s', '.inc'])
asm_arm7_files.extend(add_files_with_extensions(os.path.join('sub', 'lib'), ['.s', '.inc']))
src_files = add_files_with_extensions(HEADER_FOLDER, ['.h'])
src_files.extend(add_files_with_extensions('src', ['.c']))
replaced_symbols = set()
for language, pmdsky_debug_language_symbols in pmdsky_debug_symbols.items():
xmap_language_symbols = xmap_symbols[language]
for section_name, pmdsky_debug_section in pmdsky_debug_language_symbols.items():
if section_name == 'ram':
# We can't distinguish between different types of RAM symbols.
# E.g., they could be in the BSS, heap, etc.
continue
if section_name in xmap_language_symbols:
xmap_section = xmap_language_symbols[section_name]
else:
xmap_section = {}
for address, symbol in pmdsky_debug_section.items():
if section_name == 'arm7' and address < 0x27E0000:
# Shift ARM 7 WRAM to its RAM location.
address += WRAM_OFFSET
if address in xmap_section and xmap_section[address].name != symbol.name and xmap_section[address].name not in MIXED_CASE_SYMBOLS_ARM9 and xmap_section[address].name not in MIXED_CASE_SYMBOLS_ARM7 and xmap_section[address].name not in replaced_symbols:
old_symbol = xmap_section[address]
if '__' in old_symbol.name:
# When a symbol is duplicated in multiple places and has the address appended to the symbol name,
# don't flag the EU name/address combination as a different symbol.
base_symbol = old_symbol.name.split('__')[0]
if symbol.name.startswith(f'{base_symbol}__'):
continue
print(f'Replacing {old_symbol.name} with {symbol.name}')
replaced_symbols.add(old_symbol.name)
# Replace symbol occurrences in ASM files.
if symbol.is_data:
asm_search_string_bases = [
f'\n{old_symbol.name}:\n',
f'.word {old_symbol.name}\n',
f'; ={old_symbol.name}\n',
f'.global {old_symbol.name}\n',
f'.public {old_symbol.name}\n',
]
else:
asm_search_string_bases = [
f'arm_func_start {old_symbol.name}\n',
f'arm_func_end {old_symbol.name}\n',
f'\n{old_symbol.name}: ',
f'thumb_func_start {old_symbol.name}\n',
f'thumb_func_end {old_symbol.name}\n',
f'.word {old_symbol.name}\n',
f'b {old_symbol.name} ; case',
f'bl {old_symbol.name}\n',
f'blx {old_symbol.name}\n',
f'beq {old_symbol.name}\n',
f'bne {old_symbol.name}\n',
f'; ={old_symbol.name}\n',
f'.public {old_symbol.name}\n',
]
asm_search_strings = [(base, base.replace(old_symbol.name, symbol.name)) for base in asm_search_string_bases]
candidate_asm_files = asm_files
if section_name == 'arm7':
candidate_asm_files = asm_arm7_files
for file_path in candidate_asm_files:
with open(file_path, 'r') as asm_file:
asm_contents = asm_file.read()
for search_string in asm_search_strings:
asm_contents = asm_contents.replace(search_string[0], search_string[1])
if file_path.endswith('.inc') and 'macros' not in file_path:
asm_contents_split = asm_contents.split('\n')
sorted_imports = sorted(asm_contents_split[1:], key=str.casefold)
if len(sorted_imports) and sorted_imports[0] == '':
sorted_imports = sorted_imports[1:]
asm_contents = '#pragma once\n' + '\n'.join(sorted_imports) + '\n'
with open(file_path, 'w') as asm_file:
asm_file.write(asm_contents)
# Replace symbol occurrences in C files.
src_search_string_data_regex = re.compile(fr'([ &*(]){old_symbol.name}([,); [])')
src_search_string_data_regex_replace = fr'\1{symbol.name}\2'
src_search_string_bases = [
f' {old_symbol.name}(',
f'({old_symbol.name}(',
]
src_search_function_strings = [(base, base.replace(old_symbol.name, symbol.name)) for base in src_search_string_bases]
for file_path in src_files:
with open(file_path, 'r') as src_file:
src_contents = src_file.read()
if symbol.is_data:
src_contents = src_search_string_data_regex.sub(src_search_string_data_regex_replace, src_contents)
else:
for search_string in src_search_function_strings:
src_contents = src_contents.replace(search_string[0], search_string[1])
with open(file_path, 'w') as src_file:
src_file.write(src_contents)