matching many more data objects

This commit is contained in:
roeming
2025-07-25 15:57:43 -04:00
parent cf189b9913
commit 017eddf8ec
97 changed files with 9282 additions and 493 deletions
+340 -30
View File
@@ -1,3 +1,4 @@
import pathlib
from argparse import ArgumentParser
from gfxdis import convert_binary_to_gfx
from dataclasses import dataclass
@@ -7,6 +8,145 @@ ASSET_ALIGN = "ATTRIBUTE_ALIGN(32)"
NO_ALIGN = ""
def dict_enum_from_list(l: list): return {i: x for i, x, in enumerate(l)}
EFFECT_TYPES = dict_enum_from_list([
"eEC_EFFECT_SHOCK",
"eEC_EFFECT_DUST",
"eEC_EFFECT_MUKA",
"eEC_EFFECT_WARAU",
"eEC_EFFECT_HA",
"eEC_EFFECT_SIBUKI",
"eEC_EFFECT_GIMONHU",
"eEC_EFFECT_KANTANHU",
"eEC_EFFECT_ASE",
"eEC_EFFECT_ASE_CH",
"eEC_EFFECT_KANGAERU",
"eEC_EFFECT_OTIKOMI",
"eEC_EFFECT_BURUBURU",
"eEC_EFFECT_SIAWASE_HIKARI",
"eEC_EFFECT_SIAWASE_HANA",
"eEC_EFFECT_SIAWASE_HANA_CH",
"eEC_EFFECT_TAKURAMI",
"eEC_EFFECT_TAKURAMI_KIRA",
"eEC_EFFECT_KAZE",
"eEC_EFFECT_KAZE_HAPPA",
"eEC_EFFECT_LOVELOVE",
"eEC_EFFECT_LOVELOVE_HEART",
"eEC_EFFECT_PUN",
"eEC_EFFECT_PUN_YUGE",
"eEC_EFFECT_PUN_SEKIMEN",
"eEC_EFFECT_NAKU",
"eEC_EFFECT_NAMIDA",
"eEC_EFFECT_HIRAMEKI_DEN",
"eEC_EFFECT_HIRAMEKI_HIKARI",
"eEC_EFFECT_SITUREN",
"eEC_EFFECT_ASE2",
"eEC_EFFECT_DASH_ASIMOTO",
"eEC_EFFECT_KONPU",
"eEC_EFFECT_LOVELOVE2",
"eEC_EFFECT_KPUN",
"eEC_EFFECT_KISHA_KEMURI",
"eEC_EFFECT_NEBOKE",
"eEC_EFFECT_NEBOKE_AWA",
"eEC_EFFECT_HANABI_YANAGI",
"eEC_EFFECT_HANABI_BOTAN1",
"eEC_EFFECT_HANABI_BOTAN2",
"eEC_EFFECT_HANABI_HOSHI",
"eEC_EFFECT_HANABI_SET",
"eEC_EFFECT_HANABI_SWITCH",
"eEC_EFFECT_FOOTPRINT",
"eEC_EFFECT_TURN_FOOTPRINT",
"eEC_EFFECT_BUBU",
"eEC_EFFECT_TURN_ASIMOTO",
"eEC_EFFECT_STRING",
"eEC_EFFECT_YUKIHANE",
"eEC_EFFECT_ANAHIKARI",
"eEC_EFFECT_BUSH_HAPPA",
"eEC_EFFECT_BUSH_YUKI",
"eEC_EFFECT_TUMBLE",
"eEC_EFFECT_TUMBLE_BODYPRINT",
"eEC_EFFECT_SLIP",
"eEC_EFFECT_TUMBLE_DUST",
"eEC_EFFECT_SWING_NET",
"eEC_EFFECT_AMI_MIZU",
"eEC_EFFECT_MIZUTAMA",
"eEC_EFFECT_YUKIDAMA",
"eEC_EFFECT_KIKUZU",
"eEC_EFFECT_SWING_AXE",
"eEC_EFFECT_STEAM",
"eEC_EFFECT_NIGHT15_MOON",
"eEC_EFFECT_NIGHT13_MOON",
"eEC_EFFECT_NEBOKE_AKUBI",
"eEC_EFFECT_SLIP_FOOTPRINT",
"eEC_EFFECT_WALK_ASIMOTO",
"eEC_EFFECT_TURI_HAMON",
"eEC_EFFECT_TURI_MIZU",
"eEC_EFFECT_TURI_HANE0",
"eEC_EFFECT_TURI_HANE1",
"eEC_EFFECT_TURI_SUITEKI",
"eEC_EFFECT_ONGEN",
"eEC_EFFECT_IMPACT_STAR",
"eEC_EFFECT_SANDSPLASH",
"eEC_EFFECT_YOUNG_TREE",
"eEC_EFFECT_SWING_ROD",
"eEC_EFFECT_DOYON",
"eEC_EFFECT_KAGU_HAPPA",
"eEC_EFFECT_HANABI_DUMMY",
"eEC_EFFECT_DIG_HOLE",
"eEC_EFFECT_DIG_SCOOP",
"eEC_EFFECT_DIG_MUD",
"eEC_EFFECT_KIGAE",
"eEC_EFFECT_KIGAE_LIGHT",
"eEC_EFFECT_TAPE",
"eEC_EFFECT_CLACKER",
"eEC_EFFECT_KAMIFUBUKI",
"eEC_EFFECT_CAR_BLIGHT",
"eEC_EFFECT_TAMAIRE",
"eEC_EFFECT_CAR_LIGHT",
"eEC_EFFECT_RESET_HOLE",
"eEC_EFFECT_GOKI",
"eEC_EFFECT_HANABIRA",
"eEC_EFFECT_WAIT_ASIMOTO",
"eEC_EFFECT_OTOSIANA",
"eEC_EFFECT_IKIGIRE",
"eEC_EFFECT_YUKIDARUMA",
"eEC_EFFECT_KYOUSOU_ONPU",
"eEC_EFFECT_DOUZOU_LIGHT",
"eEC_EFFECT_HALLOWEEN_SMOKE",
"eEC_EFFECT_HALLOWEEN",
"eEC_EFFECT_MOTIYUGE",
"eEC_EFFECT_TABERU",
"eEC_EFFECT_KASAMIZU",
"eEC_EFFECT_KASAMIZUTAMA",
"eEC_EFFECT_HANATIRI",
"eEC_EFFECT_URANAI",
"eEC_EFFECT_SUISOU_AWA",
"eEC_EFFECT_BREAK_AXE",
"eEC_EFFECT_YAJIRUSHI",
"eEC_EFFECT_SOBA_YUGE",
"eEC_EFFECT_FURO_YUGE",
"eEC_EFFECT_SHOOTING",
"eEC_EFFECT_SHOOTING_SET",
"eEC_EFFECT_SHOOTING_KIRA",
"eEC_EFFECT_COIN",
"eEC_EFFECT_TENT_LAMP",
"eEC_EFFECT_FLASH",
"eEC_EFFECT_FLASHC",
"eEC_EFFECT_KILLER",
"eEC_EFFECT_MAKE_HEM",
"eEC_EFFECT_MAKE_HEM_KIRA",
"eEC_EFFECT_MAKE_HEM_LIGHT",
"eEC_EFFECT_NUM"
])
FRAMECONTROL_MODE = dict_enum_from_list([
"cKF_FRAMECONTROL_STOP",
"cKF_FRAMECONTROL_REPEAT",
])
@dataclass
class struct_ref:
symbol_name: str
@@ -30,11 +170,11 @@ def anim_type_conv(anim_type: int):
}.get(anim_type, str(anim_type))
def parse_bin_formatted(buff: bytes, in_format: list[tuple[str, str]], _symbols: dict[int, str], val_conv: callable = None, type_conv: callable = None):
def parse_bin_formatted(buff: bytes, in_format: list[tuple[str, str]], _symbols: dict[int, str], val_conv: callable = None, type_conv: callable = None, fmt_conv: callable = None):
struct_format = ">"
symbols_offsets = {}
symbol_order = []
reloc_symbols = set()
reloc_symbols = []
for format_pair in in_format:
if format_pair == None:
@@ -45,7 +185,7 @@ def parse_bin_formatted(buff: bytes, in_format: list[tuple[str, str]], _symbols:
struct_type, name = format_pair
if struct_type == "p":
reloc_symbols.add(name)
reloc_symbols.append(name)
struct_type = "xxxx"
struct_format += struct_type
@@ -90,6 +230,18 @@ def parse_bin_formatted(buff: bytes, in_format: list[tuple[str, str]], _symbols:
if new_type != None:
found_syms.append(struct_ref(value, new_type))
new_converted_vals = {}
if fmt_conv:
for name, val in collapsed_data.items():
new_val = fmt_conv(name, val, dict(collapsed_data))
if new_val != None:
new_converted_vals[name] = new_val
collapsed_data.update(new_converted_vals)
new_converted_vals = {}
vals = collapsed_data.values()
out_c_data.append(f"\t{{ {', '.join([str(x) for x in vals])} }}")
@@ -180,12 +332,61 @@ def parse_cKF_Animation_R_c(buff: bytes, symbols: list[str]):
"fixed_table": "s16",
}.get(name, None)
def vcf(name, value, symbols):
if name == "x":
return parse_bin_formatted(buff, this_format, symbols, type_conv=tcf)
def parse_aNPC_Animation_c(buff: bytes, symbols: list[str]):
this_format = [
("p", "flag_table"),
("p", "data_table"),
("p", "key_table"),
("p", "fixed_table"),
("h", "pad"),
("h", "frames"),
("f", "start_time"),
("f", "end_time"),
("l", "mode"),
("f", "morph_counter"),
("p", "eye_seq_p"),
("h", "eye_seq_type"),
("h", "eye_seq_stop_frame"),
("p", "mouth_seq_p"),
("h", "mouth_seq_type"),
("h", "mouth_seq_stop_frame"),
("h", "feel_effect_set_frame"),
("h", "feel_effect_type"),
("p", "feel_effect"),
("p", "se_data_table")]
def tcf(name, symbols):
return {
"flag_table": "u8",
"data_table": "s16",
"key_table": "s16",
"fixed_table": "s16",
"eye_seq_p": "u8",
"mouth_seq_p": "u8",
# "feel_effect": "aNPC_feel_effect_c",
# "se_data_table": "aNPC_se_data_table_c",
}.get(name, None)
def fcf(name, value, symbols):
if name == "flag_table":
return f"{{ {value}"
if name == "z":
if name == "frames":
return f"{value} }}"
return parse_bin_formatted(buff, this_format, symbols, vcf, tcf)
if name == "se_data_table":
return f"{value},"
if name in ["start_time", "end_time", "morph_counter"]:
return f"{value}f"
def vcf(name, value, symbols):
if name == "feel_effect_type":
return EFFECT_TYPES.get(value, str(value))
if name == "mode":
return FRAMECONTROL_MODE.get(value, str(value))
return parse_bin_formatted(buff, this_format, symbols, vcf, tcf, fcf)
def parse_cKF_Joint_R_c(buff: bytes, symbols: list[str]):
@@ -197,17 +398,19 @@ def parse_cKF_Joint_R_c(buff: bytes, symbols: list[str]):
"joint_table": "Gfx",
}.get(name, None)
def vcf(name, value, symbols):
def fcf(name, value, symbols):
if name == "x":
return f"{{ {value}"
if name == "z":
return f"{value} }}"
def vcf(name, value, symbols):
if name == "flags":
return {
0: "cKF_JOINT_FLAG_DISP_OPA",
1: "cKF_JOINT_FLAG_DISP_XLU"
}.get(value, None)
return parse_bin_formatted(buff, this_format, symbols, vcf, tcf)
return parse_bin_formatted(buff, this_format, symbols, vcf, tcf, fcf)
def parse_u16(buff: bytes, symbols: list[str]):
@@ -228,6 +431,30 @@ def parse_u8(buff: bytes, symbols: list[str]):
return struct_parse_result(out_str, [])
def lookup_rel_vtx_offset(lines: list[str], name: str):
symbol_name, offset_label = name.split(", ")
offset = 0
inside = False
for line in lines:
if line.startswith(f".obj {symbol_name}"):
inside = True
if not inside:
continue
if line.startswith(offset_label):
return offset
if ".4byte" in line:
offset += 4
elif ".2byte" in line:
offset += 2
elif ".byte" in line:
offset += line.count(",") + 1
else:
assert (not line.endswith(f".endobj {symbol_name}"))
assert (False)
def lookup_bins_and_symbols(lines: list[str], name: str):
out_bin = bytearray()
out_symbols = []
@@ -250,7 +477,10 @@ def lookup_bins_and_symbols(lines: list[str], name: str):
out_symbols.append(data)
elif ".rel" in line:
out_bin.extend(b'\0\0\0\0')
out_symbols.append(f"&{line.split(".rel ")[1].split(",")[0]}[0]")
rel_plus_offset = line.split(".rel ")[1]
vtx_offset = lookup_rel_vtx_offset(lines, rel_plus_offset)
out_symbols.append(
f"&{rel_plus_offset.split(", ")[0]}[{vtx_offset//0x10}]")
elif ".byte" in line:
data = line.split(".byte ")[1]
out_bin.extend(int(data, 16).to_bytes(1, 'big'))
@@ -258,14 +488,19 @@ def lookup_bins_and_symbols(lines: list[str], name: str):
return out_bin, out_symbols
def lookup_static(lines: list[str], name: str):
lookfor = f".obj {name}"
def is_static(lines: list[str], name: str):
lookfor = f".obj {name},"
for line in lines:
if line.startswith(lookfor):
if line.startswith(lookfor + ", local"):
return "static "
else:
return ""
if line.startswith(lookfor + " local"):
return True
break
return False
def lookup_static(lines: list[str], name: str):
if is_static(lines, name):
return "static "
return ""
@@ -314,12 +549,23 @@ def lookup_bins_and_symbols2(lines: list[str], name: str):
return out_bin, out_symbols
def lookup_address(lines: list[str], name: str):
for i, line in enumerate(lines):
if line.startswith(f".obj {name},"):
addr = lines[i-1].split(" | ")[1]
return addr
assert (False)
def convert_source_to_gfx_c_source(src_file, dest_path):
with open(src_file) as f:
lines = f.read().split("\n")
src_file_name = pathlib.Path(src_file).stem
config_change_path = str(
pathlib.PurePosixPath(dest_path).relative_to("src"))
includes = ["libforest/gbi_extensions.h",
"PR/gbi.h", "evw_anime.h", "c_keyframe.h"]
"PR/gbi.h", "evw_anime.h", "c_keyframe.h", "ac_npc.h"]
header = "\n".join([f'#include "{x}"' for x in includes]) + "\n"
@@ -345,6 +591,8 @@ def convert_source_to_gfx_c_source(src_file, dest_path):
found_types.append((this_obj, "TEX"))
elif this_obj.startswith("cKF_bs_r"):
found_types.append((this_obj, "cKF_Skeleton_R_c"))
elif this_obj.startswith("cKF_ba_r_npc"):
found_types.append((this_obj, "aNPC_Animation_c"))
elif this_obj.startswith("cKF_ba_r"):
found_types.append((this_obj, "cKF_Animation_R_c"))
@@ -369,25 +617,29 @@ def convert_source_to_gfx_c_source(src_file, dest_path):
"u8": parse_u8,
"u16": parse_u16,
"s16": parse_s16,
"aNPC_Animation_c": parse_aNPC_Animation_c,
# "aNPC_feel_effect_c": parse_aNPC_Animation_c,
# "aNPC_se_data_table_c": parse_aNPC_Animation_c,
}
# we now have a list of objects+type
while len(found_types) > 0:
obj_name, type = found_types.pop()
default_data = f'#include "assets/{obj_name}.inc"'
if is_static(lines, obj_name):
default_data = f'#include "assets/{src_file_name}/{obj_name}.inc"'
if obj_name in converted_types:
continue
if type == "Vtx":
data = f'#include "assets/{obj_name}.inc"'
converted_types[obj_name] = (type, data, NO_ALIGN)
converted_types[obj_name] = (type, default_data, NO_ALIGN)
elif type == "Gfx":
data = convert_binary_to_gfx(
*lookup_bins_and_symbols(lines, obj_name))
converted_types[obj_name] = (type, data + ",", NO_ALIGN)
elif type == "PAL":
data = f'#include "assets/{obj_name}.inc"'
converted_types[obj_name] = ("u16", data, ASSET_ALIGN)
converted_types[obj_name] = ("u16", default_data, ASSET_ALIGN)
elif type == "TEX":
data = f'#include "assets/{obj_name}.inc"'
converted_types[obj_name] = ("u8", data, ASSET_ALIGN)
converted_types[obj_name] = ("u8", default_data, ASSET_ALIGN)
elif type in lookup_table:
res: struct_parse_result = lookup_table[type](
@@ -398,27 +650,85 @@ def convert_source_to_gfx_c_source(src_file, dest_path):
found_types = [(ref.symbol_name, ref.symbol_type)
for ref in res.referenced_objects] + found_types
out = header + "\n\n"
out = header + "\n"
for obj in all_objs:
default_include = f'#include "assets/{obj}.inc"'
if is_static(lines, obj):
default_include = f'#include "assets/{src_file_name}/{obj}.inc"'
this_type, out_data, align = converted_types.get(
obj, ("u8", f'#include "assets/{obj}.inc"', NO_ALIGN))
obj, ("u8", default_include, NO_ALIGN))
static_typing = lookup_static(lines, obj)
out += f"{static_typing}{this_type} {obj}[] {align}= {{ \n{out_data}\n}};\n\n"
# print(out)
if this_type in ["aNPC_Animation_c", "cKF_Animation_R_c", "cKF_Skeleton_R_c"]:
out += f"{static_typing}{this_type} {obj} {align}= \n{out_data}\n;\n\n"
else:
out += f"{static_typing}{this_type} {obj}[] {align}= {{\n{out_data}\n}};\n\n"
with open(dest_path, "w") as f:
f.write(out)
# print(out)
config_py = "configure.py"
with open(config_py, "r") as f:
config_txt = f.read()
config_txt = config_txt.replace(f"Object(NonMatching, \"{config_change_path}\"),",
f"Object(Matching, \"{config_change_path}\"),")
with open(config_py, "w") as f:
f.write(config_txt)
config_yaml = "config/GAFE01_00/config.yml"
with open(config_yaml, "a") as f:
for i, line in enumerate(out.split("\n")):
if line.startswith("#include \"assets"):
prev_line = out.split("\n")[i-1]
parts = prev_line.split("=")[0].split(" ")
type = parts[0]
name = parts[1]
im_static = False
if type == "static":
im_static = True
type = parts[1]
name = parts[2]
name = name[:-2] # cut off the array []
if im_static:
out_config = f"""
- symbol: {name}!.data:{lookup_address(lines, name)}
binary: assets/{src_file_name}/{name}.bin
header: assets/{src_file_name}/{name}.inc
"""
else:
out_config = f"""
- symbol: {name}
binary: assets/{name}.bin
header: assets/{name}.inc
"""
if type == "u8":
out_config += " header_type: raw\n"
elif type == "Vtx":
out_config += " header_type: none\n custom_type: vtx\n"
elif type == "u16":
out_config += " header_type: none\n custom_type: pal16\n"
else:
assert (False)
f.write(out_config)
def main():
parser = ArgumentParser(
description="Converts a binary file into gfx calls"
)
parser.add_argument("src_path", type=str, help="Binary source file path")
parser.add_argument("dest_path", type=str,
help="Destination C include file path")
args = parser.parse_args()
convert_source_to_gfx_c_source(args.src_path, args.dest_path)
presented_path = args.src_path
src_path = str(pathlib.PurePosixPath(
"./build/GAFE01_00/foresta/asm/") / (presented_path[:-2] + ".s"))
dest_path = str(pathlib.PurePosixPath("src") / presented_path)
print(dest_path)
convert_source_to_gfx_c_source(src_path, dest_path)
if __name__ == "__main__":