diff --git a/fast64_internal/oot/__init__.py b/fast64_internal/oot/__init__.py index 5c4bd90a8..663838d68 100644 --- a/fast64_internal/oot/__init__.py +++ b/fast64_internal/oot/__init__.py @@ -60,14 +60,19 @@ oot_versions_items = [ ("Custom", "Custom", "Custom"), + ("ntsc-1.0", "ntsc-1.0", "ntsc-1.0"), + ("ntsc-1.1", "ntsc-1.1", "ntsc-1.1"), + ("pal-1.0", "pal-1.0", "pal-1.0"), + ("ntsc-1.2", "ntsc-1.2", "ntsc-1.2"), + ("pal-1.1", "pal-1.1", "pal-1.1"), ("gc-jp", "gc-jp", "gc-jp"), ("gc-jp-mq", "gc-jp-mq", "gc-jp-mq"), - ("gc-jp-ce", "gc-jp-ce", "gc-jp-ce"), ("gc-us", "gc-us", "gc-us"), ("gc-us-mq", "gc-us-mq", "gc-us-mq"), + ("gc-eu-mq-dbg", "gc-eu-mq-dbg", "gc-eu-mq-dbg"), ("gc-eu", "gc-eu", "gc-eu"), ("gc-eu-mq", "gc-eu-mq", "gc-eu-mq"), - ("gc-eu-mq-dbg", "gc-eu-mq-dbg", "gc-eu-mq-dbg"), + ("gc-jp-ce", "gc-jp-ce", "gc-jp-ce"), ("hackeroot-mq", "HackerOoT", "hackeroot-mq"), # TODO: force this value if HackerOoT features are enabled? ("legacy", "Legacy", "Older Decomp Version"), ] diff --git a/fast64_internal/oot/cutscene/importer/classes.py b/fast64_internal/oot/cutscene/importer/classes.py index ce76b694f..f906e3c61 100644 --- a/fast64_internal/oot/cutscene/importer/classes.py +++ b/fast64_internal/oot/cutscene/importer/classes.py @@ -115,6 +115,7 @@ def getParsedCutscenes(self): if csName in existingCutsceneNames: continue foundCutscene = True + print(f"INFO: Found cutscene '{csName}' in the file data.") if foundCutscene: sLine = line.strip() @@ -129,7 +130,8 @@ def getParsedCutscenes(self): if "};" in line: foundCutscene = False - cutsceneList.append(csData) + if len(csData) > 0: + cutsceneList.append(csData) csData = [] if len(cutsceneList) == 0: @@ -187,7 +189,11 @@ def getParsedCutscenes(self): elif not "CutsceneData" in curCmd and not "};" in curCmd: print(f"WARNING: Unknown command found: ``{curCmd}``") cmdListFound = False - parsedCutscenes.append(ParsedCutscene(csName, parsedCS)) + + if csName is not None and len(parsedCS) > 0: + parsedCutscenes.append(ParsedCutscene(csName, parsedCS)) + else: + raise PluginError("ERROR: Something wrong happened during the parsing of the cutscene.") return parsedCutscenes @@ -200,6 +206,9 @@ def getCutsceneList(self): # if it's none then there's no cutscene in the file return None + if len(parsedCutscenes) == 0: + raise PluginError("ERROR: No cutscene was found.") + cutsceneList: list[Cutscene] = [] # for each cutscene from the list returned by getParsedCutscenes(), @@ -486,6 +495,9 @@ def setCutsceneData(self, csNumber): # if it's none then there's no cutscene in the file return csNumber + if len(cutsceneList) == 0: + raise PluginError("ERROR: No cutscene was found.") + for i, cutscene in enumerate(cutsceneList, csNumber): print(f'Found Cutscene "{cutscene.name}"! Importing...') self.validateCameraData(cutscene) diff --git a/fast64_internal/oot/cutscene/preview.py b/fast64_internal/oot/cutscene/preview.py index 9d3a28830..b3a4f8c40 100644 --- a/fast64_internal/oot/cutscene/preview.py +++ b/fast64_internal/oot/cutscene/preview.py @@ -1,11 +1,15 @@ import bpy from math import isclose +from typing import TYPE_CHECKING from bpy.types import Scene, Object, Node from bpy.app.handlers import persistent from ...utility import gammaInverse, hexOrDecInt from .motion.utility import getCutsceneCamera +if TYPE_CHECKING: + from .properties import OOTCutscenePreviewSettingsProperty, OOTCutscenePreviewProperty + def getLerp(max: float, min: float, val: float): # from ``Environment_LerpWeight()`` in decomp @@ -112,13 +116,14 @@ def initFirstFrame(csObj: Object, useNodeFeatures: bool, defaultCam: Object): def processCurrentFrame(csObj: Object, curFrame: float, useNodeFeatures: bool, cameraObjects: Object): """Execute the actions of each command to create the preview for the current frame""" # this function was partially adapted from ``z_demo.c`` + previewProp: "OOTCutscenePreviewProperty" = csObj.ootCutsceneProperty.preview + preview_settings: "OOTCutscenePreviewSettingsProperty" = bpy.context.scene.ootPreviewSettingsProperty if curFrame == 0: initFirstFrame(csObj, useNodeFeatures, cameraObjects[1]) if useNodeFeatures: - previewProp = csObj.ootCutsceneProperty.preview - for transitionCmd in csObj.ootCutsceneProperty.preview.transitionList: + for transitionCmd in previewProp.transitionList: startFrame = transitionCmd.startFrame endFrame = transitionCmd.endFrame frameCur = curFrame @@ -168,7 +173,7 @@ def processCurrentFrame(csObj: Object, curFrame: float, useNodeFeatures: bool, c color[3] = alpha bpy.context.scene.node_tree.nodes["CSTrans_RGB"].outputs[0].default_value = color - for miscCmd in csObj.ootCutsceneProperty.preview.miscList: + for miscCmd in previewProp.miscList: startFrame = miscCmd.startFrame endFrame = miscCmd.endFrame @@ -177,10 +182,9 @@ def processCurrentFrame(csObj: Object, curFrame: float, useNodeFeatures: bool, c if curFrame == startFrame: if miscCmd.type == "set_locked_viewpoint" and not None in cameraObjects: - bpy.context.scene.camera = cameraObjects[int(csObj.ootCutsceneProperty.preview.isFixedCamSet)] - csObj.ootCutsceneProperty.preview.isFixedCamSet ^= True - - elif miscCmd.type == "stop_cutscene": + bpy.context.scene.camera = cameraObjects[int(previewProp.isFixedCamSet)] + previewProp.isFixedCamSet ^= True + elif not preview_settings.ignore_cs_misc_stop and miscCmd.type == "stop_cutscene": # stop the playback and set the frame to 0 bpy.ops.screen.animation_cancel() bpy.context.scene.frame_set(bpy.context.scene.frame_start) @@ -219,7 +223,7 @@ def processCurrentFrame(csObj: Object, curFrame: float, useNodeFeatures: bool, c @persistent def cutscenePreviewFrameHandler(scene: Scene): """Preview frame handler, executes each frame when the cutscene is played""" - previewSettings = scene.ootPreviewSettingsProperty + previewSettings: "OOTCutscenePreviewSettingsProperty" = scene.ootPreviewSettingsProperty csObj: Object = previewSettings.ootCSPreviewCSObj if csObj is None or not csObj.type == "EMPTY" and not csObj.ootEmptyType == "Cutscene": diff --git a/fast64_internal/oot/cutscene/properties.py b/fast64_internal/oot/cutscene/properties.py index f2f6c3c58..b09e5aecc 100644 --- a/fast64_internal/oot/cutscene/properties.py +++ b/fast64_internal/oot/cutscene/properties.py @@ -322,6 +322,8 @@ class OOTCutscenePreviewSettingsProperty(PropertyGroup): default="link_adult", ) + ignore_cs_misc_stop: BoolProperty(name="Ignore 'Stop Cutscene' Command", default=False) + # internal only ootCSPreviewNodesReady: BoolProperty(default=False) ootCSPreviewCSObj: PointerProperty(type=Object) @@ -350,6 +352,7 @@ def draw_props(self, layout: UILayout): prop_split(previewBox, self, "previewPlayerAge", "Player Age for Preview") previewBox.prop(self, "useWidescreen") previewBox.prop(self, "useOpaqueCamBg") + previewBox.prop(self, "ignore_cs_misc_stop") class OOTCutsceneProperty(PropertyGroup): diff --git a/fast64_internal/oot/tools/quick_import.py b/fast64_internal/oot/tools/quick_import.py index 0a51344ee..807d69bce 100644 --- a/fast64_internal/oot/tools/quick_import.py +++ b/fast64_internal/oot/tools/quick_import.py @@ -1,9 +1,9 @@ -from pathlib import Path import os import re - import bpy +from pathlib import Path +from typing import Optional from ..f3d.properties import OOTDLImportSettings from ..skeleton.properties import OOTSkeletonImportSettings from ..animation.properties import OOTAnimImportSettingsProperty @@ -21,18 +21,25 @@ def get_found_defs(path: Path, sym_name: str, sym_def_pattern: re.Pattern[str]): 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 + + if len(found_defs) > 0: + print(file_p, f"{found_defs=}") + all_found_defs[file_p] = found_defs return all_found_defs @@ -57,33 +64,30 @@ def quick_import_exec(context: bpy.types.Context, sym_name: str): sym_def_pattern = re.compile(rf"([^\s]+)\s+{sym_name}\s*(\[[^\]]*\])?\s*=") - base_dir_p = Path(context.scene.ootDecompPath) / context.scene.fast64.oot.get_extracted_path() - 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) - - found_dir_p = assets_objects_dir_p if is_sym_object else assets_scenes_dir_p all_found_defs: dict[Path, list[tuple[str, str]]] = dict() + found_dir_p: Optional[Path] = None + base_dir_p = Path(context.scene.ootDecompPath) - for dirpath, dirnames, filenames in os.walk(found_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=}") - if found_defs: - all_found_defs[file_p] = found_defs + # this str cast completely useless, it's there to force linting to recognize a Path element + extracted_dir_p = base_dir_p / str(context.scene.fast64.oot.get_extracted_path()) + + assets_paths: list[Path] = [ + # objects + extracted_dir_p / "assets" / "objects", + # scenes + extracted_dir_p / "assets" / "scenes", + # other assets embedded in actors (cutscenes for instance) + base_dir_p / "src" / "overlays" / "actors", + ] + + for path in assets_paths: + all_found_defs = get_found_defs(path, sym_name, sym_def_pattern) + + if len(all_found_defs) > 0: + found_dir_p = path + break + + assert found_dir_p is not None # Ideally if for example sym_name was gLinkAdultHookshotTipDL, # all_found_defs now contains: @@ -93,13 +97,15 @@ def quick_import_exec(context: bpy.types.Context, sym_name: str): if len(all_found_defs) == 0: raise QuickImportAborted(f"Couldn't find a definition of {sym_name}, is the OoT Version correct?") + if len(all_found_defs) > 1: raise QuickImportAborted( f"Found definitions of {sym_name} in several files: " + ", ".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(found_dir_p)}") @@ -109,7 +115,7 @@ def quick_import_exec(context: bpy.types.Context, sym_name: str): folder_name = sym_file_p.relative_to(found_dir_p).parts[0] def raise_only_from_object(type: str): - if not is_sym_object: + if "objects" not in str(found_dir_p): raise QuickImportAborted( f"Can only import {type} from an object ({sym_name} found in {sym_file_p.relative_to(base_dir_p)})" )