From 960a83f98b89d19404cf50805c426b927f35ed95 Mon Sep 17 00:00:00 2001 From: Yanis42 <35189056+Yanis42@users.noreply.github.com> Date: Tue, 16 Jan 2024 17:44:47 +0100 Subject: [PATCH 1/3] cs import additions/fixes (single import, quick import) --- fast64_internal/oot/cutscene/classes.py | 7 +- .../oot/cutscene/importer/classes.py | 21 ++++-- .../oot/cutscene/importer/functions.py | 5 +- fast64_internal/oot/cutscene/operators.py | 11 +++- fast64_internal/oot/cutscene/panels.py | 5 +- fast64_internal/oot/tools/quick_import.py | 64 +++++++++++-------- 6 files changed, 76 insertions(+), 37 deletions(-) diff --git a/fast64_internal/oot/cutscene/classes.py b/fast64_internal/oot/cutscene/classes.py index bdfc5ebb3..115e0016a 100644 --- a/fast64_internal/oot/cutscene/classes.py +++ b/fast64_internal/oot/cutscene/classes.py @@ -55,7 +55,7 @@ def __post_init__(self): class CutsceneCmdActorCue(CutsceneCmdBase): """This class contains a single Actor Cue command data""" - actionID: Optional[int] = None + actionID: Optional[int | str] = None rot: list[str] = field(default_factory=list) startPos: list[int] = field(default_factory=list) endPos: list[int] = field(default_factory=list) @@ -65,7 +65,10 @@ def __post_init__(self): if self.params is not None: self.startFrame = getInteger(self.params[1]) self.endFrame = getInteger(self.params[2]) - self.actionID = getInteger(self.params[0]) + try: + self.actionID = getInteger(self.params[0]) + except ValueError: + self.actionID = self.params[0] self.rot = [getRotation(self.params[3]), getRotation(self.params[4]), getRotation(self.params[5])] self.startPos = [getInteger(self.params[6]), getInteger(self.params[7]), getInteger(self.params[8])] self.endPos = [getInteger(self.params[9]), getInteger(self.params[10]), getInteger(self.params[11])] diff --git a/fast64_internal/oot/cutscene/importer/classes.py b/fast64_internal/oot/cutscene/importer/classes.py index 066494895..79270b77d 100644 --- a/fast64_internal/oot/cutscene/importer/classes.py +++ b/fast64_internal/oot/cutscene/importer/classes.py @@ -1,7 +1,7 @@ import bpy from dataclasses import dataclass -from typing import TYPE_CHECKING +from typing import Optional, TYPE_CHECKING from bpy.types import Object, Armature from ....utility import PluginError from ..motion.utility import setupCutscene, getBlenderPosition, getInteger @@ -46,8 +46,9 @@ class PropertyData: class CutsceneImport(CutsceneObjectFactory): """This class contains functions to create the new cutscene Blender data""" - filePath: str # used when importing from the panel - fileData: str # used when importing the cutscenes when importing a scene + filePath: Optional[str] # used when importing from the panel + fileData: Optional[str] # used when importing the cutscenes when importing a scene + csName: Optional[str] # used when import a specific cutscene def getCmdParams(self, data: str, cmdName: str, paramNumber: int): """Returns the list of every parameter of the given command""" @@ -86,6 +87,13 @@ def getParsedCutscenes(self): for oldName in oldNames: fileData = fileData.replace(f"{oldName}(", f"{ootCSLegacyToNewCmdNames[oldName]}(") + # make a list of existing cutscene names, to skip importing them if found + existingCutsceneNames = [ + csObj.name.removeprefix("Cutscene.") + for csObj in bpy.data.objects + if csObj.type == "EMPTY" and csObj.ootEmptyType == "Cutscene" + ] + # parse cutscenes fileLines = fileData.split("\n") csData = [] @@ -94,6 +102,10 @@ def getParsedCutscenes(self): for line in fileLines: if not line.startswith("//") and not line.startswith("/*"): if "CutsceneData " in line: + # split with "[" just in case the array has a set size + csName = line.split(" ")[1].split("[")[0] + if csName in existingCutsceneNames: + continue foundCutscene = True if foundCutscene: @@ -102,7 +114,8 @@ def getParsedCutscenes(self): line += fileLines[fileLines.index(line) + 1].strip() if len(csData) == 0 or "CS_" in line: - csData.append(line) + if self.csName is None or self.csName == csName: + csData.append(line) if "};" in line: foundCutscene = False diff --git a/fast64_internal/oot/cutscene/importer/functions.py b/fast64_internal/oot/cutscene/importer/functions.py index 402176e5d..20e562f76 100644 --- a/fast64_internal/oot/cutscene/importer/functions.py +++ b/fast64_internal/oot/cutscene/importer/functions.py @@ -1,10 +1,11 @@ import bpy +from typing import Optional from .classes import CutsceneImport -def importCutsceneData(filePath: str, sceneData: str): +def importCutsceneData(filePath: Optional[str], sceneData: Optional[str], csName: Optional[str] = None): """Initialises and imports the cutscene data from either a file or the scene data""" # NOTE: ``sceneData`` is the data read when importing a scene - csMotionImport = CutsceneImport(filePath, sceneData) + csMotionImport = CutsceneImport(filePath, sceneData, csName) return csMotionImport.setCutsceneData(bpy.context.scene.ootCSNumber) diff --git a/fast64_internal/oot/cutscene/operators.py b/fast64_internal/oot/cutscene/operators.py index bf02ff845..44e16494a 100644 --- a/fast64_internal/oot/cutscene/operators.py +++ b/fast64_internal/oot/cutscene/operators.py @@ -141,8 +141,8 @@ def execute(self, context): class OOT_ImportCutscene(Operator): bl_idname = "object.oot_import_cutscenes" - bl_label = "Import All Cutscenes" - bl_options = {"REGISTER", "UNDO", "PRESET"} + bl_label = "Import Cutscenes" + bl_options = {"REGISTER", "UNDO"} def execute(self, context): try: @@ -150,7 +150,8 @@ def execute(self, context): object.mode_set(mode="OBJECT") path = abspath(context.scene.ootCutsceneImportPath) - context.scene.ootCSNumber = importCutsceneData(path, None) + csName = context.scene.ootCSImportName if len(context.scene.ootCSImportName) > 0 else None + context.scene.ootCSNumber = importCutsceneData(path, None, csName) self.report({"INFO"}, "Successfully imported cutscenes") return {"FINISHED"} @@ -295,12 +296,16 @@ def cutscene_ops_register(): Scene.ootCutsceneExportPath = StringProperty(name="File", subtype="FILE_PATH") Scene.ootCutsceneImportPath = StringProperty(name="File", subtype="FILE_PATH") Scene.ootCSNumber = IntProperty(default=1, min=0) + Scene.ootCSImportName = StringProperty( + name="CS Name", description="Used to import a single cutscene, can be ``None``" + ) def cutscene_ops_unregister(): for cls in reversed(oot_cutscene_classes): unregister_class(cls) + del Scene.ootCSImportName del Scene.ootCSNumber del Scene.ootCutsceneImportPath del Scene.ootCutsceneExportPath diff --git a/fast64_internal/oot/cutscene/panels.py b/fast64_internal/oot/cutscene/panels.py index 76685144a..5ae1c0ac4 100644 --- a/fast64_internal/oot/cutscene/panels.py +++ b/fast64_internal/oot/cutscene/panels.py @@ -53,9 +53,12 @@ def draw(self, context): importBox = layout.box() importBox.label(text="Cutscene Importer") - prop_split(importBox, context.scene, "ootCutsceneImportPath", "Import From") + prop_split(importBox, context.scene, "ootCSImportName", "Import") + prop_split(importBox, context.scene, "ootCutsceneImportPath", "From") col = importBox.column() + if len(context.scene.ootCSImportName) == 0: + col.label(text="All Cutscenes will be imported.") col.operator(OOT_ImportCutscene.bl_idname) diff --git a/fast64_internal/oot/tools/quick_import.py b/fast64_internal/oot/tools/quick_import.py index 4dcb772d0..533db24c4 100644 --- a/fast64_internal/oot/tools/quick_import.py +++ b/fast64_internal/oot/tools/quick_import.py @@ -7,6 +7,7 @@ from ..f3d.properties import OOTDLImportSettings from ..skeleton.properties import OOTSkeletonImportSettings from ..animation.properties import OOTAnimImportSettingsProperty +from ..cutscene.importer import importCutsceneData class QuickImportAborted(Exception): @@ -15,6 +16,27 @@ def __init__(self, message): self.message = message +def get_found_defs(path: Path, sym_name: str, sym_def_pattern: re.Pattern[str]): + all_found_defs: dict[Path, list[tuple[str, str]]] = dict() + + for dirpath, _, filenames in os.walk(path): + 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 + + return all_found_defs + + def quick_import_exec(context: bpy.types.Context, sym_name: str): sym_name = sym_name.strip() if sym_name == "": @@ -37,23 +59,14 @@ def quick_import_exec(context: bpy.types.Context, sym_name: str): base_dir_p = Path(context.scene.ootDecompPath) assets_objects_dir_p = base_dir_p / "assets" / "objects" + assets_scenes_dir_p = base_dir_p / "assets" / "scenes" + is_sym_object = True + all_found_defs = get_found_defs(assets_objects_dir_p, sym_name, sym_def_pattern) + if len(all_found_defs) == 0: + is_sym_object = False + all_found_defs = get_found_defs(assets_scenes_dir_p, sym_name, sym_def_pattern) - 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 + found_dir_p = assets_objects_dir_p if is_sym_object else assets_scenes_dir_p # Ideally if for example sym_name was gLinkAdultHookshotTipDL, # all_found_defs now contains: @@ -66,24 +79,22 @@ def quick_import_exec(context: bpy.types.Context, sym_name: str): 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()) + + ", ".join(str(p.relative_to(found_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)}" - ) + raise QuickImportAborted(f"Found several definitions of {sym_name} in {sym_file_p.relative_to(found_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] + folder_name = sym_file_p.relative_to(found_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.folder = folder_name settings.actorOverlayName = "" settings.isCustom = False bpy.ops.object.oot_import_dl() @@ -97,7 +108,7 @@ def quick_import_exec(context: bpy.types.Context, sym_name: str): else: settings.mode = "Generic" settings.name = sym_name - settings.folder = object_name + settings.folder = folder_name settings.actorOverlayName = "" bpy.ops.object.oot_import_skeleton() elif sym_def_type == "AnimationHeader" and not is_array: @@ -105,11 +116,14 @@ def quick_import_exec(context: bpy.types.Context, sym_name: str): settings.isCustom = False settings.isLink = False settings.animName = sym_name - settings.folderName = object_name + settings.folderName = folder_name bpy.ops.object.oot_import_anim() + elif sym_def_type == "CutsceneData" and is_array: + path = assets_scenes_dir_p / folder_name / sym_file_p + bpy.context.scene.ootCSNumber = importCutsceneData(f"{path}", None, sym_name) else: raise QuickImportAborted( f"Don't know how to import {sym_def_type}" + ("[]" if is_array else "") - + f" (symbol found in {object_name})" + + f" (symbol found in {folder_name})" ) From cf7d7d96f7b14de550ea37f972b50d6fd074a617 Mon Sep 17 00:00:00 2001 From: Dragorn421 Date: Thu, 8 Aug 2024 16:27:39 +0200 Subject: [PATCH 2/3] fixup no longer only quickimporting from objects --- fast64_internal/oot/tools/quick_import.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/fast64_internal/oot/tools/quick_import.py b/fast64_internal/oot/tools/quick_import.py index 533db24c4..42bd47a82 100644 --- a/fast64_internal/oot/tools/quick_import.py +++ b/fast64_internal/oot/tools/quick_import.py @@ -91,7 +91,14 @@ def quick_import_exec(context: bpy.types.Context, sym_name: str): is_array = sym_def_array_decl != "" folder_name = sym_file_p.relative_to(found_dir_p).parts[0] + def raise_only_from_object(type: str): + if not is_sym_object: + raise QuickImportAborted( + f"Can only import {type} from an object ({sym_name} found in {sym_file_p.relative_to(base_dir_p)})" + ) + if sym_def_type == "Gfx" and is_array: + raise_only_from_object("Gfx[]") settings: OOTDLImportSettings = context.scene.fast64.oot.DLImportSettings settings.name = sym_name settings.folder = folder_name @@ -99,6 +106,7 @@ def quick_import_exec(context: bpy.types.Context, sym_name: str): settings.isCustom = False bpy.ops.object.oot_import_dl() elif sym_def_type in {"SkeletonHeader", "FlexSkeletonHeader"} and not is_array: + raise_only_from_object(sym_def_type) settings: OOTSkeletonImportSettings = context.scene.fast64.oot.skeletonImportSettings settings.isCustom = False if sym_name == "gLinkAdultSkel": @@ -112,6 +120,7 @@ def quick_import_exec(context: bpy.types.Context, sym_name: str): settings.actorOverlayName = "" bpy.ops.object.oot_import_skeleton() elif sym_def_type == "AnimationHeader" and not is_array: + raise_only_from_object(sym_def_type) settings: OOTAnimImportSettingsProperty = context.scene.fast64.oot.animImportSettings settings.isCustom = False settings.isLink = False @@ -119,11 +128,10 @@ def quick_import_exec(context: bpy.types.Context, sym_name: str): settings.folderName = folder_name bpy.ops.object.oot_import_anim() elif sym_def_type == "CutsceneData" and is_array: - path = assets_scenes_dir_p / folder_name / sym_file_p - bpy.context.scene.ootCSNumber = importCutsceneData(f"{path}", None, sym_name) + bpy.context.scene.ootCSNumber = importCutsceneData(f"{sym_file_p}", None, sym_name) else: raise QuickImportAborted( f"Don't know how to import {sym_def_type}" + ("[]" if is_array else "") - + f" (symbol found in {folder_name})" + + f" (symbol found in {sym_file_p.relative_to(base_dir_p)})" ) From 1b558caf0148e77506ca1cf8d988a521a263ba15 Mon Sep 17 00:00:00 2001 From: Yanis42 <35189056+Yanis42@users.noreply.github.com> Date: Tue, 13 Aug 2024 15:22:59 +0200 Subject: [PATCH 3/3] fix IndexError issue --- fast64_internal/oot/cutscene/importer/classes.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fast64_internal/oot/cutscene/importer/classes.py b/fast64_internal/oot/cutscene/importer/classes.py index 1ce416f25..ce76b694f 100644 --- a/fast64_internal/oot/cutscene/importer/classes.py +++ b/fast64_internal/oot/cutscene/importer/classes.py @@ -120,7 +120,8 @@ def getParsedCutscenes(self): sLine = line.strip() csCmd = sLine.split("(")[0] if "CutsceneData " not in line and "};" not in line and csCmd not in ootCutsceneCommandsC: - csData[-1] += line + if len(csData) > 0: + csData[-1] += line if len(csData) == 0 or sLine.startswith("CS_") and not sLine.startswith("CS_FLOAT"): if self.csName is None or self.csName == csName: