From 07ab192b5b28a7e48d871c76d5545684446b7a7d Mon Sep 17 00:00:00 2001 From: Dexrn ZacAttack Date: Tue, 17 Feb 2026 18:29:09 -0800 Subject: [PATCH 1/2] feat: refactor our tools --- .gitmodules | 2 +- data/data_symbols.csv | 1 + tools/common | 2 +- tools/config.toml | 1 + tools/ida/export_data.py | 57 ++++++++++++ tools/ida/export_data_as_csv.py | 43 --------- ...unctions_as_csv.py => export_functions.py} | 28 ++++-- tools/ida/rename_data.py | 93 ++++++++++--------- tools/ida/rename_functions.py | 13 ++- 9 files changed, 139 insertions(+), 101 deletions(-) create mode 100644 tools/ida/export_data.py delete mode 100644 tools/ida/export_data_as_csv.py rename tools/ida/{export_functions_as_csv.py => export_functions.py} (61%) diff --git a/.gitmodules b/.gitmodules index b4adee04..101aa1ab 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,7 +3,7 @@ url = https://github.com/open-ead/nnheaders.git [submodule "tools/common"] path = tools/common - url = https://github.com/open-ead/nx-decomp-tools + url = https://github.com/DexrnZacAttack/nx-decomp-tools [submodule "toolchain/musl"] path = toolchain/musl url = https://github.com/open-ead/botw-lib-musl diff --git a/data/data_symbols.csv b/data/data_symbols.csv index 0fe1d7e5..e9f0404c 100644 --- a/data/data_symbols.csv +++ b/data/data_symbols.csv @@ -1,3 +1,4 @@ +Address,Name 0x0000007101781BBC,AABB__sThreadStorageCount 0x0000007101781BC0,AABB__sDefaultThreadStorage 0x0000007101781BE0,_ZN6Blocks8RegistryE diff --git a/tools/common b/tools/common index ba1c7b02..cca779d7 160000 --- a/tools/common +++ b/tools/common @@ -1 +1 @@ -Subproject commit ba1c7b025d23c9292e87cf4848d74c59fe6dfb99 +Subproject commit cca779d704e1937f94b2173a7b5dfaa99e7134ca diff --git a/tools/config.toml b/tools/config.toml index 61a65fe8..7c64012c 100644 --- a/tools/config.toml +++ b/tools/config.toml @@ -1,4 +1,5 @@ functions_csv = "data/mcswitch_functions.csv" +data_csv = "data/data_symbols.csv" build_target = "mcswitch" [decomp_me] diff --git a/tools/ida/export_data.py b/tools/ida/export_data.py new file mode 100644 index 00000000..f6f5ec71 --- /dev/null +++ b/tools/ida/export_data.py @@ -0,0 +1,57 @@ +import csv +import ida_kernwin +import idc +import idautils +import os + +# TODO later I can probably write some compat classes so this script will run on Ghidra as well. -Dexrn + +common = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "common")) +sys.path.append(common) + +from util import config + +output = config.get_data_csv_path() +if not output: + output = ida_kernwin.ask_file(1, "*.csv", "Choose a path to write the CSV file to") + +print(f"[OUTPUT] {output}") + +f = ['Address', 'Name'] +existing_data = {} +if output and os.path.exists(output): + with open(output, 'r', newline='') as csv: + reader = csv.DictReader(csv) + + for row in reader: + if len(row) >= 2: + addr, name = row["Address"], row["Name"] + existing_data[addr] = name + +if output: + seg = idaapi.get_segm_by_name(".bss") + + start = seg.start_ea + end = seg.end_ea + + for ea, sym in idautils.Names(): + if start <= ea < end: + if not sym: + continue + + addr = f'0x{ea:016X}' + existing_data[addr] = sym + + with open(output, 'w', newline='') as csv: + writer = csv.DictWriter(csv, fieldnames=f) + writer.writeheader() + + for addr, name in sorted(existing_data.items(), key=lambda item: int(item[0], 16)): + writer.writerow({ + 'Address': addr, + 'Name': name + }) + + print("[EXPORT_DATA] Done!") +else: + print("[EXPORT_DATA] Cancelled.") diff --git a/tools/ida/export_data_as_csv.py b/tools/ida/export_data_as_csv.py deleted file mode 100644 index 1f8560dc..00000000 --- a/tools/ida/export_data_as_csv.py +++ /dev/null @@ -1,43 +0,0 @@ -import csv -import ida_kernwin -import idc -import idautils -import os - -output_file_path = ida_kernwin.ask_file(1, "*.csv", "Save CSV file") - -# Load the existing csv if it exists -existing_data = {} -if output_file_path and os.path.exists(output_file_path): - with open(output_file_path, 'r', newline='') as csvfile: - reader = csv.reader(csvfile) - for row in reader: - if len(row) >= 2: - address, name = row[0], row[1] - existing_data[address] = {"Address": address, "Name": name} - -if output_file_path: - seg = idaapi.get_segm_by_name(".bss") - - start = seg.start_ea - end = seg.end_ea - - for ea, symbol_name in idautils.Names(): - if start <= ea < end: - if not symbol_name: - continue - - address = f'0x{ea:016X}' - - existing_data[address] = {"Address": address, "Name": symbol_name} - - # Write merged results back - with open(output_file_path, 'w', newline='') as csvfile: - csvwriter = csv.writer(csvfile) - - for address, row in sorted(existing_data.items(), key=lambda item: int(item[0], 16)): - csvwriter.writerow([row["Address"], row["Name"]]) - - print(f"CSV file '{output_file_path}' has been generated.") -else: - print("Operation cancelled by the user.") diff --git a/tools/ida/export_functions_as_csv.py b/tools/ida/export_functions.py similarity index 61% rename from tools/ida/export_functions_as_csv.py rename to tools/ida/export_functions.py index 4e4d3d3a..7c4bf0b0 100644 --- a/tools/ida/export_functions_as_csv.py +++ b/tools/ida/export_functions.py @@ -3,23 +3,33 @@ import idc import os -output_file_path = ida_kernwin.ask_file(1, "*.csv", "Select the CSV file to update, checked functions will be preserved") +common = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "common")) +sys.path.append(common) + +from util import config + +output = config.get_data_csv_path() +if not output: + output = ida_kernwin.ask_file(1, "*.csv", "Choose a path to write the CSV file to") + +print(f"[OUTPUT] {output}") def is_valid_name(name: str) -> bool: return not name.startswith(("sub_", "nullsub_", "j_")) -# Load the existing csv if it exists + +fieldnames = ['Address', 'Quality', 'Size', 'Name'] existing_data = {} -if output_file_path and os.path.exists(output_file_path): - with open(output_file_path, 'r', newline='') as csvfile: +if output and os.path.exists(output): + with open(output, 'r', newline='') as csvfile: + reader = csv.DictReader(csvfile) for row in reader: address = row['Address'] existing_data[address] = row -if output_file_path: - with open(output_file_path, 'w', newline='') as csvfile: - fieldnames = ['Address', 'Quality', 'Size', 'Name'] +if output: + with open(output, 'w', newline='') as csvfile: csvwriter = csv.DictWriter(csvfile, fieldnames=fieldnames) csvwriter.writeheader() @@ -37,6 +47,6 @@ def is_valid_name(name: str) -> bool: 'Name': func_name if is_valid_name(func_name) else '' }) - print(f"CSV file '{output_file_path}' has been updated.") + print("[EXPORT_FUNCTIONS] Done!") else: - print("Operation cancelled by the user.") + print("[EXPORT_FUNCTIONS] Cancelled.") diff --git a/tools/ida/rename_data.py b/tools/ida/rename_data.py index 3a808da5..8a963757 100644 --- a/tools/ida/rename_data.py +++ b/tools/ida/rename_data.py @@ -1,49 +1,54 @@ import csv +import os +import sys + +# gotta do this otherwise it won't find the util folder +common = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "common")) +sys.path.append(common) + +from util import config + +input = config.get_data_csv_path() +print(f"[INPUT] {input}") + import ida_kernwin import idc import idautils -input_file_path = ida_kernwin.ask_file(0, "*.csv", "Select CSV file") - -if input_file_path: - with open(input_file_path, 'r', newline='') as csvfile: - csvreader = csv.reader(csvfile) - - print(csvfile) - - for row in csvreader: - if len(row) < 2: - continue - - address_str, name = row[0], row[1] - - if not address_str or not name: - continue - - try: - address = int(address_str, 16) - except ValueError: - print(f"Invalid address format: {address_str}", flush=True) - continue - - seg = idc.get_segm_name(address) - if seg != '.bss': - print(f"Address {address_str} not in .bss, skipping.", flush=True) - continue - - # Attempt to rename - current_name = idc.get_name(address) - if not current_name: - if idc.set_name(address, name, idc.SN_CHECK): - print(f"Added new name '{name}' at {address_str}", flush=True) - else: - print(f"Failed to add name '{name}' at {address_str}", flush=True) - elif current_name != name: - if idc.set_name(address, name, idc.SN_CHECK): - print(f"Renamed '{current_name}' to '{name}' at {address_str}", flush=True) - else: - print(f"Failed to rename '{current_name}' at {address_str} to '{name}'", flush=True) - - print("Renaming from CSV completed.", flush=True) -else: - print("Operation cancelled by the user.", flush=True) +with open(input, 'r', newline='') as f: + reader = csv.DictReader(f) + + for row in reader: + # print(row) + + if len(row) < 2: + continue + + addr, name = row["Address"], row["Name"] + + if not addr or not name: + continue + + try: + address = int(addr, 16) + except ValueError: + print(f"[ERR] Invalid address str: {addr}", flush=True) # in what world would this ever happen??????? + continue + + seg = idc.get_segm_name(address) + if seg != '.bss': + continue + + cur_name = idc.get_name(address) + if not cur_name: + if idc.set_name(address, name, idc.SN_CHECK): + print(f"[CREATE] -> {addr}: '{name}'", flush=True) + else: + print(f"[ERR] Failed to set name '{name}' at {addr}", flush=True) + elif cur_name != name: + if idc.set_name(address, name, idc.SN_CHECK): + print(f"[RENAME] -> {addr}: '{cur_name}' -> '{name}'", flush=True) + else: + print(f"[ERR] Failed to rename '{cur_name}' at {addr} to '{name}'", flush=True) + +print("[RENAME_DATA] Done!", flush=True) \ No newline at end of file diff --git a/tools/ida/rename_functions.py b/tools/ida/rename_functions.py index 51402f99..dca681f7 100644 --- a/tools/ida/rename_functions.py +++ b/tools/ida/rename_functions.py @@ -5,8 +5,10 @@ import idc import ida_funcs import ida_range +import ida_auto import idautils import idaapi +import ida_hexrays import os import sys @@ -59,24 +61,29 @@ def can_overwrite_name(addr: int, new_name: str): for fn in reader: addr = int(fn[0], 16) + progress = fn[1] size = int(fn[2]) name = fn[3] func = ida_funcs.get_func(addr) if func is None: - print(f"Creating function at {hex(addr)} with name {name}") + print(f"[CREATE] -> {hex(addr)}: {name}") ida_funcs.add_func(addr) ida_funcs.set_func_end(addr, addr + size) elif func.start_ea != addr: - print(f"Fixing function at {hex(addr)} with name {name}") + print(f"[FIX] -> {hex(addr)}: {name}") ida_funcs.set_func_end(prev_addr, prev_addr + prev_size) ida_funcs.add_func(addr) ida_funcs.set_func_end(addr, addr + size) if can_overwrite_name(addr, name): idc.set_name(addr, name, idc.SN_CHECK | idc.SN_NOWARN) + + if progress == "O": + idc.set_color(addr, idc.CIC_FUNC, 0x276e20) + prev_addr = addr prev_size = size -print("Renaming from functions completed.") +print("[RENAME_FUNCTIONS] Done!") From f83f4f8ec3275880d012ab13c7e9fdc5d50e14bf Mon Sep 17 00:00:00 2001 From: Dexrn ZacAttack Date: Tue, 24 Feb 2026 05:35:55 -0800 Subject: [PATCH 2/2] feat: commit sections --- data/sections.csv | 24 ++++++++++++++++++++++++ tools/common | 2 +- tools/config.toml | 1 + 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 data/sections.csv diff --git a/data/sections.csv b/data/sections.csv new file mode 100644 index 00000000..86ca4dae --- /dev/null +++ b/data/sections.csv @@ -0,0 +1,24 @@ +Name,Start Address,End Address,Real Address,O,R,W,X,V,A +.shstrtab,0x0000007100000000,0x0000007100000088,0x00000000000006a8,False,True,False,False,False,False +.init,0x0000007100000088,0x0000007100000160,0x0000000000000730,False,True,False,True,False,False +.fini,0x0000007100000160,0x0000007100000194,0x0000000000000808,False,True,False,True,False,False +.text,0x0000007100000194,0x00000071009c9a18,0x000000000000083c,False,True,False,True,False,False +.plt,0x00000071009c9a18,0x00000071009cbc28,0x00000000009ca0c0,False,True,False,True,False,False +.rodata,0x00000071009cc000,0x00000071009cc090,0x00000000009cc2d0,False,True,False,False,False,False +.rela.dyn,0x00000071009cc090,0x0000007100d5a950,0x00000000009cc360,False,True,False,False,False,False +.rela.plt,0x0000007100d5a950,0x0000007100d5dc38,0x0000000000d5ac20,False,True,False,False,False,False +.hash,0x0000007100d5dc38,0x0000007100d5eefc,0x0000000000d5df08,False,True,False,False,False,False +.rodata,0x0000007100d5eefc,0x0000007100d62e90,0x0000000000d5f1cc,False,True,False,False,False,False +.dynstr,0x0000007100d62e90,0x0000007100d695a4,0x0000000000d63160,False,True,False,False,False,False +.rodata,0x0000007100d695a4,0x0000007101016af4,0x0000000000d69874,False,True,False,False,False,False +.note,0x0000007101016af4,0x0000007101016b14,0x0000000001016dc4,False,True,False,False,False,False +.rodata,0x0000007101016b14,0x0000007101016cbf,0x0000000001016de4,False,True,False,False,False,False +.data,0x0000007101017000,0x0000007101769a40,0x0000000001016f8f,False,True,True,False,False,False +.got.plt,0x0000007101769a40,0x000000710176ab50,0x00000000017699cf,False,True,True,False,False,False +.got,0x000000710176ab50,0x0000007101781780,0x000000000176aadf,False,True,True,False,False,False +.data,0x0000007101781780,0x00000071017817b8,0x000000000178170f,False,True,True,False,False,False +.init_array,0x00000071017817b8,0x0000007101781b00,0x0000000001781747,False,True,True,False,False,False +.bss,0x0000007101781b00,0x00000071023fd000,,False,True,True,False,False,False +.prgend,0x00000071023fd000,0x00000071023fd001,,False,False,False,False,False,False +extern,0x00000071023fd008,0x00000071023fe4b8,,False,False,False,False,False,False +abs,0x00000071023fe4b8,0x00000071023fe4c0,,False,False,False,False,False,False diff --git a/tools/common b/tools/common index cca779d7..5dc63ddb 160000 --- a/tools/common +++ b/tools/common @@ -1 +1 @@ -Subproject commit cca779d704e1937f94b2173a7b5dfaa99e7134ca +Subproject commit 5dc63ddb613170eb2454c3f0ccc8925968a44a4a diff --git a/tools/config.toml b/tools/config.toml index 7c64012c..99a174a3 100644 --- a/tools/config.toml +++ b/tools/config.toml @@ -1,5 +1,6 @@ functions_csv = "data/mcswitch_functions.csv" data_csv = "data/data_symbols.csv" +sections_csv = "data/sections.csv" build_target = "mcswitch" [decomp_me]