From 20b250182388ab7eb9f665d4305e5dd2c8cac748 Mon Sep 17 00:00:00 2001 From: Dragorn421 Date: Tue, 26 Dec 2023 18:38:49 +0100 Subject: [PATCH] Quick import --- fast64_internal/oot/tools/operators.py | 38 +++++++ fast64_internal/oot/tools/panel.py | 3 + fast64_internal/oot/tools/quick_import.py | 115 ++++++++++++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 fast64_internal/oot/tools/quick_import.py diff --git a/fast64_internal/oot/tools/operators.py b/fast64_internal/oot/tools/operators.py index 022a08b..5b40e36 100644 --- a/fast64_internal/oot/tools/operators.py +++ b/fast64_internal/oot/tools/operators.py @@ -8,6 +8,7 @@ from ...operators import AddWaterBox, addMaterialByName from ...utility import parentObject, setOrigin from ..cutscene.motion.utility import setupCutscene, createNewCameraShot from ..oot_utility import getNewPath +from .quick_import import QuickImportAborted, quick_import_exec class OOT_AddWaterBox(AddWaterBox): @@ -255,3 +256,40 @@ class OOTClearTransformAndLock(Operator): return {"FINISHED"} except: return {"CANCELLED"} + + +class OOTQuickImport(Operator): + bl_idname = "object.oot_quick_import" + bl_label = "Quick Import" + bl_options = {"REGISTER", "UNDO"} + bl_description = ( + "Import (almost) anything by inputting a symbol name from an object." + " This operator automatically finds the file to import from (within objects)" + ) + + sym_name: StringProperty( + name="Symbol name", + description=( + "Which symbol to import." + " This may be a display list (e.g. gBoomerangDL), " + "a skeleton (e.g. object_daiku_Skel_007958), " + "an animation (with the appropriate skeleton selected, e.g. object_daiku_Anim_008164)" + ), + ) + + def invoke(self, context, event): + return context.window_manager.invoke_props_dialog(self) + + def draw(self, context): + self.layout.prop(self, "sym_name", text="Symbol") + + def execute(self, context: Context): + try: + quick_import_exec( + context, + self.sym_name, + ) + except QuickImportAborted as e: + self.report({"ERROR"}, e.message) + return {"CANCELLED"} + return {"FINISHED"} diff --git a/fast64_internal/oot/tools/panel.py b/fast64_internal/oot/tools/panel.py index 759e109..e72ac48 100644 --- a/fast64_internal/oot/tools/panel.py +++ b/fast64_internal/oot/tools/panel.py @@ -8,6 +8,7 @@ from .operators import ( OOT_AddCutscene, OOT_AddPath, OOTClearTransformAndLock, + OOTQuickImport, ) @@ -24,6 +25,7 @@ class OoT_ToolsPanel(OOT_Panel): col.operator(OOT_AddCutscene.bl_idname) col.operator(OOT_AddPath.bl_idname) col.operator(OOTClearTransformAndLock.bl_idname) + col.operator(OOTQuickImport.bl_idname) oot_operator_panel_classes = [ @@ -38,6 +40,7 @@ toolOpsToRegister = [ OOT_AddCutscene, OOT_AddPath, OOTClearTransformAndLock, + OOTQuickImport, ] diff --git a/fast64_internal/oot/tools/quick_import.py b/fast64_internal/oot/tools/quick_import.py new file mode 100644 index 0000000..4dcb772 --- /dev/null +++ b/fast64_internal/oot/tools/quick_import.py @@ -0,0 +1,115 @@ +from pathlib import Path +import os +import re + +import bpy + +from ..f3d.properties import OOTDLImportSettings +from ..skeleton.properties import OOTSkeletonImportSettings +from ..animation.properties import OOTAnimImportSettingsProperty + + +class QuickImportAborted(Exception): + def __init__(self, message): + super().__init__(message) + self.message = message + + +def quick_import_exec(context: bpy.types.Context, sym_name: str): + sym_name = sym_name.strip() + if sym_name == "": + raise QuickImportAborted("No symbol name given") + if not all( + ( + "a" <= c <= "z" + or "A" <= c <= "Z" + or "0" <= c <= "9" + or c + in { + "_", + } + ) + for c in sym_name + ): + raise QuickImportAborted("Symbol names only have characters a-zA-Z0-9_") + + sym_def_pattern = re.compile(rf"([^\s]+)\s+{sym_name}\s*(\[[^\]]*\])?\s*=") + + base_dir_p = Path(context.scene.ootDecompPath) + assets_objects_dir_p = base_dir_p / "assets" / "objects" + + all_found_defs: dict[Path, list[tuple[str, str]]] = dict() + + for dirpath, dirnames, filenames in os.walk(assets_objects_dir_p): + dirpath_p = Path(dirpath) + for filename in filenames: + file_p = dirpath_p / filename + # Only look into C files + if file_p.suffix != ".c": + continue + source = file_p.read_text() + # Simple check to see if we should look into this file any further + if sym_name not in source: + continue + found_defs = sym_def_pattern.findall(source) + print(file_p, f"{found_defs=}") + all_found_defs[file_p] = found_defs + + # Ideally if for example sym_name was gLinkAdultHookshotTipDL, + # all_found_defs now contains: + # {Path('.../assets/objects/object_link_boy/object_link_boy.c'): [('Gfx', '[]')]} + # or with gButterflySkel: + # {Path('.../assets/objects/gameplay_field_keep/gameplay_field_keep.c'): [('SkeletonHeader', '')]} + + if len(all_found_defs) == 0: + raise QuickImportAborted(f"Couldn't find a definition of {sym_name}") + if len(all_found_defs) > 1: + raise QuickImportAborted( + f"Found definitions of {sym_name} in several files: " + + ", ".join(str(p.relative_to(assets_objects_dir_p)) for p in all_found_defs.keys()) + ) + assert len(all_found_defs) == 1 + sym_file_p, sym_defs = list(all_found_defs.items())[0] + if len(sym_defs) > 1: + raise QuickImportAborted( + f"Found several definitions of {sym_name} in {sym_file_p.relative_to(assets_objects_dir_p)}" + ) + + # We found a single definition of the symbol + sym_def_type, sym_def_array_decl = sym_defs[0] + is_array = sym_def_array_decl != "" + object_name = sym_file_p.relative_to(assets_objects_dir_p).parts[0] + + if sym_def_type == "Gfx" and is_array: + settings: OOTDLImportSettings = context.scene.fast64.oot.DLImportSettings + settings.name = sym_name + settings.folder = object_name + settings.actorOverlayName = "" + settings.isCustom = False + bpy.ops.object.oot_import_dl() + elif sym_def_type in {"SkeletonHeader", "FlexSkeletonHeader"} and not is_array: + settings: OOTSkeletonImportSettings = context.scene.fast64.oot.skeletonImportSettings + settings.isCustom = False + if sym_name == "gLinkAdultSkel": + settings.mode = "Adult Link" + elif sym_name == "gLinkChildSkel": + settings.mode = "Child Link" + else: + settings.mode = "Generic" + settings.name = sym_name + settings.folder = object_name + settings.actorOverlayName = "" + bpy.ops.object.oot_import_skeleton() + elif sym_def_type == "AnimationHeader" and not is_array: + settings: OOTAnimImportSettingsProperty = context.scene.fast64.oot.animImportSettings + settings.isCustom = False + settings.isLink = False + settings.animName = sym_name + settings.folderName = object_name + bpy.ops.object.oot_import_anim() + else: + raise QuickImportAborted( + f"Don't know how to import {sym_def_type}" + + ("[]" if is_array else "") + + f" (symbol found in {object_name})" + )