summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorYunataSavior <58997725+YunataSavior@users.noreply.github.com>2025-04-27 21:06:44 -0700
committerGitHub <noreply@github.com>2025-04-27 21:06:44 -0700
commit430dc32da995dba5ee33a88dc890eafb5259ff83 (patch)
tree21e753668a5ef4f7148a73baa268f583c5e32e70 /tools
parent7e06b18a2a37819555460508f950b09f5ca7ec03 (diff)
Utility script 'beautify_anm_data.py' to assist getting started with d_a_npc TUs (#2413)
Diffstat (limited to 'tools')
-rw-r--r--tools/utilities/beautify_anm_data.py238
1 files changed, 238 insertions, 0 deletions
diff --git a/tools/utilities/beautify_anm_data.py b/tools/utilities/beautify_anm_data.py
new file mode 100644
index 0000000000..fd36219128
--- /dev/null
+++ b/tools/utilities/beautify_anm_data.py
@@ -0,0 +1,238 @@
+#
+# Author: YunataSavior
+# Brief: Converts byte-array anm data into their proper forms.
+#
+# Used for decompiling d_a_npc_ETC TUs.
+#
+
+import os
+import re
+import sys
+
+
+FACE_MOTION_TYPE = "daNpcT_faceMotionAnmData_c l_faceMotionAnmData"
+FACE_MOTION_PATTERN = r'SECTION_DATA static u8 l_faceMotionAnmData\[\d+\] = {'
+
+MOTION_TYPE = "daNpcT_motionAnmData_c l_motionAnmData"
+MOTION_PATTERN = r'SECTION_DATA static u8 l_motionAnmData\[\d+\] = {'
+
+SEQ_FACE_MOTION_TYPE = "daNpcT_MotionSeqMngr_c::sequenceStepData_c l_faceMotionSequenceData"
+SEQ_FACE_MOTION_PATTERN = "SECTION_DATA static u8 l_faceMotionSequenceData\[\d+\] = {"
+
+SEQ_MOTION_TYPE = "daNpcT_MotionSeqMngr_c::sequenceStepData_c l_motionSequenceData"
+SEQ_MOTION_PATTERN = "SECTION_DATA static u8 l_motionSequenceData\[\d+\] = {"
+
+HEAP_SIZE_TYPE = "int const heapSize"
+HEAP_SIZE_PATTERN = "SECTION_RODATA static u8 const heapSize\[\d+\] = {"
+
+PARAM_TYPE = "::m"
+PARAM_PATTERN = r'SECTION_RODATA u8 const (\w+_Param_c)::m\[\d+\] = {'
+
+
+def twos_complement(hexstr, bits):
+ # https://stackoverflow.com/questions/6727875/hex-string-to-signed-int-in-python
+ value = int(hexstr, 16)
+ if value & (1 << (bits - 1)):
+ value -= 1 << bits
+
+ return value
+
+
+def build_anm_struct(byte_collection, anm_type, param_name):
+ my_len = len(byte_collection)
+ piece_size = 1
+ instr_arr = []
+ is_array = False
+ if anm_type is PARAM_TYPE:
+ # Special handling.
+ if my_len % 4 != 0:
+ print(f"Error: len() = '{my_len}' isn't divisble by 4")
+ sys.exit(1)
+
+ piece_size = 4
+ instr_arr = ["h4"]
+ else:
+ if anm_type is FACE_MOTION_TYPE:
+ piece_size = 28
+ instr_arr = ["s4", "s4", "s4", "s4", "s4", "s4", "s4"]
+ is_array = True
+ elif anm_type is MOTION_TYPE:
+ piece_size = 28
+ instr_arr = ["s4", "s4", "s4", "s4", "s4", "s4", "s2", "s2"]
+ is_array = True
+ elif anm_type is SEQ_FACE_MOTION_TYPE or anm_type is SEQ_MOTION_TYPE:
+ piece_size = 4
+ instr_arr = ["s2", "s1", "s1"]
+ is_array = True
+ elif anm_type is HEAP_SIZE_TYPE:
+ piece_size = 4
+ instr_arr = ["s4"]
+
+ if my_len % piece_size != 0:
+ print(f"Error: len() = '{my_len}' isn't divisble by '{piece_size}'")
+ sys.exit(1)
+
+ ptr = 0
+ cur_instr = 0
+ hexstr = ""
+ full_res_arr = []
+ pos_arr = []
+ while ptr < my_len:
+ curbyte = byte_collection[ptr]
+ ptr += 1
+ if curbyte[:2] != "0x" or len(curbyte) != 4:
+ print(f"Error: '{curbyte}' isn't formatted as a byte")
+ sys.exit(1)
+
+ hexstr += curbyte[-2:]
+ exp_bytes = int(instr_arr[cur_instr][1])
+ if len(hexstr) / 2 == exp_bytes:
+ my_type = instr_arr[cur_instr][0]
+ if my_type == 's':
+ val = twos_complement(hexstr, exp_bytes*8)
+ pos_arr.append(val)
+ elif my_type == 'h':
+ pos_arr.append("0x" + hexstr)
+ else:
+ print(f"Error: unknown type '{my_type}'")
+ sys.exit(1)
+
+ hexstr = ""
+ cur_instr += 1
+ if cur_instr == len(instr_arr):
+ cur_instr = 0
+ full_res_arr.append(pos_arr.copy())
+ pos_arr.clear()
+
+ res_str = ""
+ if anm_type is PARAM_TYPE:
+ res_str += "struct Data {\n"
+ idx = 0
+ while idx < my_len:
+ upper = f'{idx:02X}'
+ lower = f'{idx:02x}'
+ res_str += " /* 0x{} */ u32 field_0x{};\n".format(upper, lower)
+ idx += 4
+ res_str += "};\n"
+ res_str += "static const Data m;\n\n"
+ res_str += "{}::Data const {}::m".format(param_name, param_name)
+ else:
+ res_str += "static {}".format(anm_type)
+
+ res_len = my_len / piece_size
+ cutoff_num = 1
+ if anm_type is SEQ_FACE_MOTION_TYPE or anm_type is SEQ_MOTION_TYPE:
+ cutoff_num = 8
+ elif anm_type is HEAP_SIZE_TYPE:
+ cutoff_num = 12
+
+ if anm_type is not PARAM_TYPE:
+ res_str += "[{}]".format(int(res_len))
+
+ res_str += " = {\n"
+ cur_in_line = 0
+ for my_arr in full_res_arr:
+ if cur_in_line == 0:
+ res_str += " "
+
+ if is_array is True:
+ res_str += "{"
+
+ in_mid = False
+ for value in my_arr:
+ if in_mid is False:
+ in_mid = True
+ else:
+ res_str += ", "
+
+ if isinstance(value, int):
+ res_str += str(value)
+ elif isinstance(value, float):
+ res_str += str(value) + "f"
+ else:
+ res_str += value
+
+ if is_array is True:
+ res_str += "}"
+
+ res_str += ","
+ cur_in_line += 1
+ if cur_in_line == cutoff_num:
+ cur_in_line = 0
+ res_str += "\n"
+ else:
+ res_str += " "
+
+ if cur_in_line != 0:
+ res_str += "\n"
+
+ res_str += "};\n"
+ print(res_str)
+
+
+def run_beautify_anm_data(in_file):
+ fDesc = open(in_file, 'r')
+ fConts = fDesc.read()
+ lines = fConts.splitlines()
+
+ in_byte_array = False
+ byte_collection = []
+ anm_type = ""
+ param_name = ""
+
+ for line in lines:
+ words = line.split()
+ if len(words) == 0:
+ continue
+
+ if in_byte_array is False:
+ if re.search(FACE_MOTION_PATTERN, line):
+ in_byte_array = True
+ anm_type = FACE_MOTION_TYPE
+ elif re.search(MOTION_PATTERN, line):
+ in_byte_array = True
+ anm_type = MOTION_TYPE
+ elif re.search(SEQ_FACE_MOTION_PATTERN, line):
+ in_byte_array = True
+ anm_type = SEQ_FACE_MOTION_TYPE
+ elif re.search(SEQ_MOTION_PATTERN, line):
+ in_byte_array = True
+ anm_type = SEQ_MOTION_TYPE
+ elif re.search(HEAP_SIZE_PATTERN, line):
+ in_byte_array = True
+ anm_type = HEAP_SIZE_TYPE
+ else:
+ match = re.search(PARAM_PATTERN, line)
+ if match:
+ in_byte_array = True
+ anm_type = PARAM_TYPE
+ param_name = match.group(1)
+ else:
+ if words[0] == '};':
+ build_anm_struct(byte_collection, anm_type, param_name)
+ in_byte_array = False
+ byte_collection.clear()
+ else:
+ bytes = re.split(r'[,\s]+', line)
+ for byte in bytes:
+ if (len(byte) == 0):
+ continue
+ byte_collection.append(byte)
+
+ # End of for loop.
+ fDesc.close()
+
+
+if __name__ == "__main__":
+ if len(sys.argv) < 2:
+ print("Usage: python3 beautify_anm_data.py <filename>")
+ sys.exit(1)
+
+ in_file = sys.argv[1]
+
+ # Check if the file exists
+ if not os.path.isfile(in_file):
+ print(f"Error: File '{in_file}' not found.")
+ sys.exit(1)
+
+ run_beautify_anm_data(in_file)