diff options
| author | SuperDude88 <82904174+SuperDude88@users.noreply.github.com> | 2026-07-13 15:22:49 -0400 |
|---|---|---|
| committer | SuperDude88 <82904174+SuperDude88@users.noreply.github.com> | 2026-07-13 17:09:42 -0400 |
| commit | 99f93ebe98d144f48e4e494724a163b026fe9d91 (patch) | |
| tree | 03494240d0cc04dfafdec31fb8e57c1b062cd8c6 | |
| parent | 5319b0e006c365d16f509daa0e627baa8e5156c5 (diff) | |
Add Model Extraction Script
- Support extraction of Vec/cXy/GXColor data
| -rwxr-xr-x | configure.py | 20 | ||||
| -rw-r--r-- | tools/converters/extract_model_data.py | 145 |
2 files changed, 165 insertions, 0 deletions
diff --git a/configure.py b/configure.py index a6bcd75d..9e25d2ae 100755 --- a/configure.py +++ b/configure.py @@ -1838,6 +1838,11 @@ config.custom_build_rules = [ "command": "$python tools/converters/matDL_dis.py $in $out --symbol $symbol --scope $scope", "description": "CONVERT $symbol", }, + { + "name": "convert_embedded_model_data", + "command": "$python tools/converters/extract_model_data.py $in $out --type $type --symbol $symbol --scope $scope", + "description": "CONVERT $symbol", + }, ] config.custom_build_steps = {} @@ -1870,6 +1875,21 @@ def emit_build_rule(asset): } ) + case "Vec" | "cXy" | "GXColor": + steps.append( + { + "rule": "convert_embedded_model_data", + "inputs": out_dir / "bin" / asset["binary"], + "outputs": out_dir / "include" / asset["header"], + "variables": { + "type": asset.get("custom_type"), + "symbol": asset.get("rename") or asset["symbol"], + "scope": custom_data.get("scope", "local") + }, + "implicit": Path("tools/converters/extract_model_data.py"), + } + ) + case _: print("Unknown asset type: " + asset["custom_type"]) diff --git a/tools/converters/extract_model_data.py b/tools/converters/extract_model_data.py new file mode 100644 index 00000000..bd05e7a3 --- /dev/null +++ b/tools/converters/extract_model_data.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 + +from argparse import ArgumentParser +import struct +import os +from enum import Enum +from binary_funcs import read_f32, read_u8 + +def write_array_declaration(c_file, symbol_name: str, scope: str, type: str): + if scope == "local": + var_def_prefix = "static " + else: + var_def_prefix = "" + + c_file.write(f"{var_def_prefix}{type} {symbol_name}[] = {{\n") + +def format_float_array(elements: list[tuple[float, ...]]) -> str: + if len(elements) == 0: + return "" + + num_components: int = len(elements[0]) + + # Get the longest float for each component + longest_components: list[int] = [0] * num_components + for element in elements: + for i, component in enumerate(element): + length = len(f"{component:f}".rstrip('0')) + if f"{component:f}".rstrip('0').endswith('.'): + length += 1 + longest_components[i] = max(length, longest_components[i]) + + result: str = "" + for element in elements: + # Open each array element + result += " {" + for i, component in enumerate(element): + # Remove all the trailing 0s (unless the value would end with the decimal point) + value = f'{component:f}'.rstrip('0') + if value.endswith('.'): + value += '0' + + # Only add the comma + minimum 1 space if this isn't the last element + if i != len(element) - 1: + result += f"{f'{value}f, ':<{longest_components[i] + 3}}" + else: + result += f"{f'{value}f':<{longest_components[i] + 1}}" + + result += "},\n" + + return result + +def convert_binary_to_Vec_c_source(src_path: str, dest_path: str, symbol_name: str, scope: str): + # Load data + with open(src_path, "rb") as binary_file, open(dest_path, "w") as c_file: + + write_array_declaration(c_file, symbol_name, scope, "Vec") + + elements: list[tuple[float, ...]] = [] + while True: + try: + x, y, z = read_f32(binary_file), read_f32(binary_file), read_f32(binary_file) + except EOFError: + break + + elements.append((x, y, z)) + + lines = format_float_array(elements) + c_file.write(lines) + + c_file.write("};\n") + +def convert_binary_to_cXy_c_source(src_path: str, dest_path: str, symbol_name: str, scope: str): + # Load data + with open(src_path, "rb") as binary_file, open(dest_path, "w") as c_file: + + write_array_declaration(c_file, symbol_name, scope, "cXy") + + elements: list[tuple[float, ...]] = [] + while True: + try: + x, y = read_f32(binary_file), read_f32(binary_file) + except EOFError: + break + + elements.append((x, y)) + + lines = format_float_array(elements) + c_file.write(lines) + + c_file.write("};\n") + +def convert_binary_to_GXColor_c_source(src_path: str, dest_path: str, symbol_name: str, scope: str): + # Load data + with open(src_path, "rb") as binary_file, open(dest_path, "w") as c_file: + + write_array_declaration(c_file, symbol_name, scope, "GXColor") + + while True: + try: + r, g, b, a = read_u8(binary_file), read_u8(binary_file), read_u8(binary_file), read_u8(binary_file) + except EOFError: + break + + c_file.write(" " + f"{{0x{r:02X}, 0x{g:02X}, 0x{b:02X}, 0x{a:02X}}}" + ",\n") + + c_file.write("};\n") + + +def main(): + parser = ArgumentParser( + description="Converts a binary file containing vertex data to an array of the specified type" + ) + 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") + parser.add_argument( + "--type", + type=str, + help="Data type", + ) + parser.add_argument( + "--symbol", + type=str, + help="Symbol name", + ) + parser.add_argument( + "--scope", + choices=["global", "local"], + default="local", + help="The scope of the symbol", + ) + + args = parser.parse_args() + + if args.type == "Vec": + convert_binary_to_Vec_c_source(args.src_path, args.dest_path, args.symbol, args.scope) + elif args.type == "cXy": + convert_binary_to_cXy_c_source(args.src_path, args.dest_path, args.symbol, args.scope) + elif args.type == "GXColor": + convert_binary_to_GXColor_c_source(args.src_path, args.dest_path, args.symbol, args.scope) + else: + raise Exception(f"Unknown data type: {args.type}") + + +if __name__ == "__main__": + main() |
