summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLouis <35883445+louist103@users.noreply.github.com>2023-11-14 23:56:51 -0500
committerLouis <35883445+louist103@users.noreply.github.com>2023-11-15 18:48:39 -0500
commit4971e8ca91fe32a2f88e9d971685d95e2582e912 (patch)
tree8c9885bb468bc45d6015a52772ad2bf6751222d2
parent2de8af4d4ea9bfc4759f262f9d0d21bb09fe6911 (diff)
ZAPD builds an OTR
add 2s2h
-rw-r--r--mm/.gitignore2
-rw-r--r--mm/2s2h/BenPort.cpp1340
-rw-r--r--mm/2s2h/BenPort.h137
-rw-r--r--mm/2s2h/Extractor/EndianCvt.obin0 -> 1520 bytes
-rw-r--r--mm/2s2h/Extractor/Extract.cpp603
-rw-r--r--mm/2s2h/Extractor/Extract.h64
-rw-r--r--mm/2s2h/Extractor/FastCrc32C.c144
-rw-r--r--mm/2s2h/Extractor/portable-file-dialogs.h1887
-rw-r--r--mm/2s2h/SohGui.cpp210
-rw-r--r--mm/2s2h/SohGui.hpp42
-rw-r--r--mm/2s2h/SohMenuBar.cpp563
-rw-r--r--mm/2s2h/SohMenuBar.h16
-rw-r--r--mm/2s2h/UIWidgets.cpp716
-rw-r--r--mm/2s2h/UIWidgets.hpp101
-rw-r--r--mm/2s2h/gu_pc.c88
-rw-r--r--mm/2s2h/resource/importer/AnimationFactory.cpp108
-rw-r--r--mm/2s2h/resource/importer/AnimationFactory.h17
-rw-r--r--mm/2s2h/resource/importer/AudioSampleFactory.cpp141
-rw-r--r--mm/2s2h/resource/importer/AudioSampleFactory.h19
-rw-r--r--mm/2s2h/resource/importer/AudioSequenceFactory.cpp52
-rw-r--r--mm/2s2h/resource/importer/AudioSequenceFactory.h19
-rw-r--r--mm/2s2h/resource/importer/AudioSoundFontFactory.cpp190
-rw-r--r--mm/2s2h/resource/importer/AudioSoundFontFactory.h19
-rw-r--r--mm/2s2h/resource/importer/BackgroundFactory.cpp39
-rw-r--r--mm/2s2h/resource/importer/BackgroundFactory.h17
-rw-r--r--mm/2s2h/resource/importer/CollisionHeaderFactory.cpp142
-rw-r--r--mm/2s2h/resource/importer/CollisionHeaderFactory.h17
-rw-r--r--mm/2s2h/resource/importer/CutsceneFactory.cpp918
-rw-r--r--mm/2s2h/resource/importer/CutsceneFactory.h25
-rw-r--r--mm/2s2h/resource/importer/PathFactory.cpp113
-rw-r--r--mm/2s2h/resource/importer/PathFactory.h31
-rw-r--r--mm/2s2h/resource/importer/PlayerAnimationFactory.cpp41
-rw-r--r--mm/2s2h/resource/importer/PlayerAnimationFactory.h17
-rw-r--r--mm/2s2h/resource/importer/SceneFactory.cpp140
-rw-r--r--mm/2s2h/resource/importer/SceneFactory.h29
-rw-r--r--mm/2s2h/resource/importer/SkeletonFactory.cpp158
-rw-r--r--mm/2s2h/resource/importer/SkeletonFactory.h23
-rw-r--r--mm/2s2h/resource/importer/SkeletonLimbFactory.cpp271
-rw-r--r--mm/2s2h/resource/importer/SkeletonLimbFactory.h23
-rw-r--r--mm/2s2h/resource/importer/TextFactory.cpp93
-rw-r--r--mm/2s2h/resource/importer/TextFactory.h23
-rw-r--r--mm/2s2h/resource/importer/TextMMFactory.cpp100
-rw-r--r--mm/2s2h/resource/importer/TextMMFactory.h23
-rw-r--r--mm/2s2h/resource/importer/TextureAnimationFactory.cpp124
-rw-r--r--mm/2s2h/resource/importer/TextureAnimationFactory.h18
-rw-r--r--mm/2s2h/resource/importer/scenecommand/EndMarkerFactory.cpp38
-rw-r--r--mm/2s2h/resource/importer/scenecommand/EndMarkerFactory.h16
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SceneCommandFactory.cpp10
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SceneCommandFactory.h15
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetActorCutsceneListFactory.cpp55
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetActorCutsceneListFactory.h17
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetActorListFactory.cpp53
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetActorListFactory.h16
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetAlternateHeadersFactory.cpp49
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetAlternateHeadersFactory.h16
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetAnimatedMaterialListFactory.cpp41
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetAnimatedMaterialListFactory.h17
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetCameraSettingsFactory.cpp39
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetCameraSettingsFactory.h16
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetCollisionHeaderFactory.cpp39
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetCollisionHeaderFactory.h16
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetCsCameraFactory.cpp55
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetCsCameraFactory.h16
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetCutscenesFactory.cpp81
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetCutscenesFactory.h28
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetEchoSettingsFactory.cpp38
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetEchoSettingsFactory.h16
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetEntranceListFactory.cpp46
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetEntranceListFactory.h16
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetExitListFactory.cpp41
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetExitListFactory.h16
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetLightListFactory.cpp58
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetLightListFactory.h16
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetLightingSettingsFactory.cpp70
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetLightingSettingsFactory.h16
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetMeshFactory.cpp169
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetMeshFactory.h16
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetMinimapChestsFactory.cpp50
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetMinimapChestsFactory.h16
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetMinimapListFactory.cpp50
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetMinimapListFactory.h16
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetObjectListFactory.cpp42
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetObjectListFactory.h16
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetPathwaysFactory.cpp82
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetPathwaysFactory.h28
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetRoomBehaviorFactory.cpp38
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetRoomBehaviorFactory.h16
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetRoomListFactory.cpp50
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetRoomListFactory.h16
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetSkyboxModifierFactory.cpp38
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetSkyboxModifierFactory.h16
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetSkyboxSettingsFactory.cpp40
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetSkyboxSettingsFactory.h16
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetSoundSettingsFactory.cpp39
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetSoundSettingsFactory.h16
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetSpecialObjectsFactory.cpp38
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetSpecialObjectsFactory.h16
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetStartPositionListFactory.cpp55
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetStartPositionListFactory.h16
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetTimeSettingsFactory.cpp39
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetTimeSettingsFactory.h16
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetTransitionActorListFactory.cpp54
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetTransitionActorListFactory.h16
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetWindSettingsFactory.cpp40
-rw-r--r--mm/2s2h/resource/importer/scenecommand/SetWindSettingsFactory.h16
-rw-r--r--mm/2s2h/resource/type/Animation.cpp21
-rw-r--r--mm/2s2h/resource/type/Animation.h87
-rw-r--r--mm/2s2h/resource/type/AudioSample.cpp11
-rw-r--r--mm/2s2h/resource/type/AudioSample.h61
-rw-r--r--mm/2s2h/resource/type/AudioSequence.cpp12
-rw-r--r--mm/2s2h/resource/type/AudioSequence.h32
-rw-r--r--mm/2s2h/resource/type/AudioSoundFont.cpp11
-rw-r--r--mm/2s2h/resource/type/AudioSoundFont.h84
-rw-r--r--mm/2s2h/resource/type/Background.cpp11
-rw-r--r--mm/2s2h/resource/type/Background.h17
-rw-r--r--mm/2s2h/resource/type/CollisionHeader.cpp11
-rw-r--r--mm/2s2h/resource/type/CollisionHeader.h98
-rw-r--r--mm/2s2h/resource/type/Cutscene.cpp12
-rw-r--r--mm/2s2h/resource/type/Cutscene.h71
-rw-r--r--mm/2s2h/resource/type/Path.cpp19
-rw-r--r--mm/2s2h/resource/type/Path.h52
-rw-r--r--mm/2s2h/resource/type/PlayerAnimation.cpp12
-rw-r--r--mm/2s2h/resource/type/PlayerAnimation.h23
-rw-r--r--mm/2s2h/resource/type/Scene.cpp12
-rw-r--r--mm/2s2h/resource/type/Scene.h23
-rw-r--r--mm/2s2h/resource/type/Skeleton.cpp82
-rw-r--r--mm/2s2h/resource/type/Skeleton.h94
-rw-r--r--mm/2s2h/resource/type/SkeletonLimb.cpp24
-rw-r--r--mm/2s2h/resource/type/SkeletonLimb.h134
-rw-r--r--mm/2s2h/resource/type/Text.cpp11
-rw-r--r--mm/2s2h/resource/type/Text.h30
-rw-r--r--mm/2s2h/resource/type/TextMM.cpp11
-rw-r--r--mm/2s2h/resource/type/TextMM.h37
-rw-r--r--mm/2s2h/resource/type/TextureAnimation.cpp36
-rw-r--r--mm/2s2h/resource/type/TextureAnimation.h104
-rw-r--r--mm/2s2h/resource/type/scenecommand/EndMarker.cpp11
-rw-r--r--mm/2s2h/resource/type/scenecommand/EndMarker.h24
-rw-r--r--mm/2s2h/resource/type/scenecommand/RomFile.h11
-rw-r--r--mm/2s2h/resource/type/scenecommand/SceneCommand.h67
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetActorCutsceneList.cpp13
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetActorCutsceneList.h31
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetActorList.cpp11
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetActorList.h30
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetAlternateHeaders.cpp12
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetAlternateHeaders.h26
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetAnimatedMaterialList.cpp13
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetAnimatedMaterialList.h23
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetCameraSettings.cpp11
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetCameraSettings.h25
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetCollisionHeader.cpp17
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetCollisionHeader.h24
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetCsCamera.cpp18
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetCsCamera.h37
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetCutscenes.cpp35
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetCutscenes.h43
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetEchoSettings.cpp11
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetEchoSettings.h24
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetEntranceList.cpp11
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetEntranceList.h28
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetExitList.cpp11
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetExitList.h22
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetLightList.cpp11
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetLightList.h48
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetLightingSettings.cpp11
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetLightingSettings.h31
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetMesh.cpp11
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetMesh.h103
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetMinimapChests.cpp13
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetMinimapChests.h23
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetMinimapList.cpp13
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetMinimapList.h33
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetObjectList.cpp11
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetObjectList.h22
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetPathways.cpp18
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetPathways.h35
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetRoomBehavior.cpp11
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetRoomBehavior.h25
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetRoomList.cpp11
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetRoomList.h35
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetSkyboxModifier.cpp11
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetSkyboxModifier.h25
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetSkyboxSettings.cpp11
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetSkyboxSettings.h27
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetSoundSettings.cpp11
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetSoundSettings.h26
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetSpecialObjects.cpp11
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetSpecialObjects.h25
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetStartPositionList.cpp11
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetStartPositionList.h30
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetTimeSettings.cpp11
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetTimeSettings.h26
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetTransitionActorList.cpp11
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetTransitionActorList.h34
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetWindSettings.cpp11
-rw-r--r--mm/2s2h/resource/type/scenecommand/SetWindSettings.h27
-rw-r--r--mm/2s2h/z_message_OTR.cpp87
-rw-r--r--mm/2s2h/z_play_2SH.cpp80
-rw-r--r--mm/2s2h/z_scene_2SH.cpp444
-rw-r--r--mm/CMakeLists.txt822
-rw-r--r--mm/assets/xml/archives/icon_item_24_static.xml2
-rw-r--r--mm/assets/xml/archives/icon_item_static.xml2
-rw-r--r--mm/assets/xml/archives/item_name_static.xml2
-rw-r--r--mm/assets/xml/archives/map_grand_static.xml198
-rw-r--r--mm/assets/xml/archives/map_i_static.xml118
-rw-r--r--mm/assets/xml/archives/map_name_static.xml2
-rw-r--r--mm/assets/xml/archives/schedule_dma_static.xml2
206 files changed, 15105 insertions, 164 deletions
diff --git a/mm/.gitignore b/mm/.gitignore
index 8c5705f18..dc0447c84 100644
--- a/mm/.gitignore
+++ b/mm/.gitignore
@@ -7,7 +7,6 @@ __pycache__/
.vscode/
.vs/
.idea/
-CMakeLists.txt
cmake-build-debug
venv/
tags
@@ -55,3 +54,4 @@ docs/doxygen/
.python-version
.make_options
.*env
+*.otr \ No newline at end of file
diff --git a/mm/2s2h/BenPort.cpp b/mm/2s2h/BenPort.cpp
new file mode 100644
index 000000000..efc63589f
--- /dev/null
+++ b/mm/2s2h/BenPort.cpp
@@ -0,0 +1,1340 @@
+#include "BenPort.h"
+#include <iostream>
+#include <algorithm>
+#include <filesystem>
+#include <fstream>
+#include <chrono>
+
+#include <ResourceManager.h>
+#include <File.h>
+#include <DisplayList.h>
+#include <Window.h>
+#include <GameVersions.h>
+
+#include "z64animation.h"
+#include "z64bgcheck.h"
+#include <libultraship/libultra/gbi.h>
+#ifdef _WIN32
+#include <Windows.h>
+#else
+#include <time.h>
+#endif
+#include <Array.h>
+#include <stb/stb_image.h>
+#define DRMP3_IMPLEMENTATION
+#include <dr_libs/mp3.h>
+#define DRWAV_IMPLEMENTATION
+#include <dr_libs/wav.h>
+#include <AudioPlayer.h>
+#include "variables.h"
+#include "z64.h"
+#include "macros.h"
+#include <Utils/StringHelper.h>
+
+#include <Fast3D/gfx_pc.h>
+#include <Fast3D/gfx_rendering_api.h>
+
+#ifdef __APPLE__
+#include <SDL_scancode.h>
+#else
+#include <SDL2/SDL_scancode.h>
+#endif
+#include "Extractor/Extract.h"
+// OTRTODO
+//#include <functions.h>
+
+
+#ifdef ENABLE_CROWD_CONTROL
+#include "Enhancements/crowd-control/CrowdControl.h"
+CrowdControl* CrowdControl::Instance;
+#endif
+
+#include <libultraship/libultraship.h>
+#include <SohGui.hpp>
+
+// Resource Types/Factories
+#include "2s2h/resource/type/Animation.h"
+#include "2s2h/resource/type/AudioSample.h"
+#include "2s2h/resource/type/AudioSequence.h"
+#include "2s2h/resource/type/AudioSoundFont.h"
+#include "2s2h/resource/type/CollisionHeader.h"
+#include "2s2h/resource/type/Cutscene.h"
+#include "2s2h/resource/type/Path.h"
+#include "2s2h/resource/type/PlayerAnimation.h"
+#include "2s2h/resource/type/Scene.h"
+#include "2s2h/resource/type/Skeleton.h"
+#include "2s2h/resource/type/SkeletonLimb.h"
+#include "2s2h/resource/type/Text.h"
+#include "2s2h/resource/importer/AnimationFactory.h"
+#include "2s2h/resource/importer/AudioSampleFactory.h"
+#include "2s2h/resource/importer/AudioSequenceFactory.h"
+#include "2s2h/resource/importer/AudioSoundFontFactory.h"
+#include "2s2h/resource/importer/CollisionHeaderFactory.h"
+#include "2s2h/resource/importer/CutsceneFactory.h"
+#include "2s2h/resource/importer/PathFactory.h"
+#include "2s2h/resource/importer/PlayerAnimationFactory.h"
+#include "2s2h/resource/importer/SceneFactory.h"
+#include "2s2h/resource/importer/SkeletonFactory.h"
+#include "2s2h/resource/importer/SkeletonLimbFactory.h"
+#include "2s2h/resource/importer/TextFactory.h"
+#include "2s2h/resource/importer/TextMMFactory.h"
+#include "2s2h/resource/importer/BackgroundFactory.h"
+#include "2s2h/resource/importer/TextureAnimationFactory.h"
+
+OTRGlobals* OTRGlobals::Instance;
+
+extern "C" char** cameraStrings;
+std::vector<std::shared_ptr<std::string>> cameraStdStrings;
+
+Color_RGB8 kokiriColor = { 0x1E, 0x69, 0x1B };
+Color_RGB8 goronColor = { 0x64, 0x14, 0x00 };
+Color_RGB8 zoraColor = { 0x00, 0xEC, 0x64 };
+
+
+OTRGlobals::OTRGlobals() {
+ std::vector<std::string> OTRFiles;
+ //std::string mqPath = LUS::Context::LocateFileAcrossAppDirs("oot-mq.otr", appShortName);
+ //if (std::filesystem::exists(mqPath)) {
+ // OTRFiles.push_back(mqPath);
+ //}
+ std::string ootPath = LUS::Context::LocateFileAcrossAppDirs("mm.otr", appShortName);
+ if (std::filesystem::exists(ootPath)) {
+ OTRFiles.push_back(ootPath);
+ }
+ std::string sohOtrPath = LUS::Context::GetPathRelativeToAppBundle("soh.otr");
+ if (std::filesystem::exists(sohOtrPath)) {
+ OTRFiles.push_back(sohOtrPath);
+ }
+ std::string patchesPath = LUS::Context::LocateFileAcrossAppDirs("mods", appShortName);
+ if (patchesPath.length() > 0 && std::filesystem::exists(patchesPath)) {
+ if (std::filesystem::is_directory(patchesPath)) {
+ for (const auto& p : std::filesystem::recursive_directory_iterator(patchesPath)) {
+ if (StringHelper::IEquals(p.path().extension().string(), ".otr")) {
+ OTRFiles.push_back(p.path().generic_string());
+ }
+ }
+ }
+ }
+ std::unordered_set<uint32_t> ValidHashes = { OOT_PAL_MQ, OOT_NTSC_JP_MQ, OOT_NTSC_US_MQ, OOT_PAL_GC_MQ_DBG,
+ OOT_NTSC_US_10, OOT_NTSC_US_11, OOT_NTSC_US_12, OOT_PAL_10,
+ OOT_PAL_11, OOT_NTSC_JP_GC_CE, OOT_NTSC_JP_GC, OOT_NTSC_US_GC,
+ OOT_PAL_GC, OOT_PAL_GC_DBG1, OOT_PAL_GC_DBG2 };
+ // tell LUS to reserve 3 SoH specific threads (Game, Audio, Save)
+ context = LUS::Context::CreateInstance("2 Ship 2 Harkinian", appShortName, "shipofharkinian.json", OTRFiles, {}, 3);
+ context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(
+ LUS::ResourceType::SOH_Animation, "Animation", std::make_shared<LUS::AnimationFactory>());
+ context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(
+ LUS::ResourceType::SOH_PlayerAnimation, "PlayerAnimation", std::make_shared<LUS::PlayerAnimationFactory>());
+ context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(
+ LUS::ResourceType::SOH_Room, "Room", std::make_shared<LUS::SceneFactory>()); // Is room scene? maybe?
+ context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(
+ LUS::ResourceType::SOH_CollisionHeader, "CollisionHeader", std::make_shared<LUS::CollisionHeaderFactory>());
+ context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(
+ LUS::ResourceType::SOH_Skeleton, "Skeleton", std::make_shared<LUS::SkeletonFactory>());
+ context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(
+ LUS::ResourceType::SOH_SkeletonLimb, "SkeletonLimb", std::make_shared<LUS::SkeletonLimbFactory>());
+ // TODO should we use a custom command for this?
+ context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::SOH_Path, "Path",
+ std::make_shared<LUS::PathFactoryMM>());
+ context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(
+ LUS::ResourceType::SOH_Cutscene, "Cutscene", std::make_shared<LUS::CutsceneFactory>());
+ context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::SOH_Text, "Text",
+ std::make_shared<LUS::TextFactory>());
+ context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::SOH_TextMM, "TextMM",
+ std::make_shared<LUS::TextMMFactory>());
+ context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(
+ LUS::ResourceType::SOH_AudioSample, "AudioSample", std::make_shared<LUS::AudioSampleFactory>());
+ context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(
+ LUS::ResourceType::SOH_AudioSoundFont, "AudioSoundFont", std::make_shared<LUS::AudioSoundFontFactory>());
+ context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(
+ LUS::ResourceType::SOH_AudioSequence, "AudioSequence", std::make_shared<LUS::AudioSequenceFactory>());
+ context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(
+ LUS::ResourceType::SOH_Background, "Background", std::make_shared<LUS::BackgroundFactory>());
+ context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(
+ LUS::ResourceType::TSH_TexAnim, "TextureAnimation", std::make_shared<LUS::TextureAnimationFactory>());
+
+ //gSaveStateMgr = std::make_shared<SaveStateMgr>();
+ //gRandomizer = std::make_shared<Randomizer>();
+ hasMasterQuest = hasOriginal = false;
+
+ // Move the camera strings from read only memory onto the heap (writable memory)
+ // This is in OTRGlobals right now because this is a place that will only ever be run once at the beginning of
+ // startup. We should probably find some code in db_camera that does initialization and only run once, and then
+ // dealloc on deinitialization.
+ //cameraStrings = (char**)malloc(sizeof(constCameraStrings));
+ //for (int32_t i = 0; i < sizeof(constCameraStrings) / sizeof(char*); i++) {
+ // // OTRTODO: never deallocated...
+ // auto dup = strdup(constCameraStrings[i]);
+ // cameraStrings[i] = dup;
+ //}
+
+ auto versions = context->GetResourceManager()->GetArchive()->GetGameVersions();
+ #if 0
+ for (uint32_t version : versions) {
+ if (!ValidHashes.contains(version)) {
+#if defined(__SWITCH__)
+ SPDLOG_ERROR("Invalid OTR File!");
+#elif defined(__WIIU__)
+ LUS::WiiU::ThrowInvalidOTR();
+#else
+ SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Invalid OTR File",
+ "Attempted to load an invalid OTR file. Try regenerating.", nullptr);
+ SPDLOG_ERROR("Invalid OTR File!");
+#endif
+ exit(1);
+ }
+ switch (version) {
+ case OOT_PAL_MQ:
+ case OOT_NTSC_JP_MQ:
+ case OOT_NTSC_US_MQ:
+ case OOT_PAL_GC_MQ_DBG:
+ hasMasterQuest = true;
+ break;
+ case OOT_NTSC_US_10:
+ case OOT_NTSC_US_11:
+ case OOT_NTSC_US_12:
+ case OOT_PAL_10:
+ case OOT_PAL_11:
+ case OOT_NTSC_JP_GC_CE:
+ case OOT_NTSC_JP_GC:
+ case OOT_NTSC_US_GC:
+ case OOT_PAL_GC:
+ case OOT_PAL_GC_DBG1:
+ case OOT_PAL_GC_DBG2:
+ hasOriginal = true;
+ break;
+ default:
+ break;
+ }
+ }
+ #endif
+}
+
+OTRGlobals::~OTRGlobals() {
+}
+
+bool OTRGlobals::HasMasterQuest() {
+ return hasMasterQuest;
+}
+
+bool OTRGlobals::HasOriginal() {
+ return hasOriginal;
+}
+
+uint32_t OTRGlobals::GetInterpolationFPS() {
+ if (LUS::Context::GetInstance()->GetWindow()->GetWindowBackend() == LUS::WindowBackend::DX11) {
+ return CVarGetInteger("gInterpolationFPS", 20);
+ }
+
+ if (CVarGetInteger("gMatchRefreshRate", 0)) {
+ return LUS::Context::GetInstance()->GetWindow()->GetCurrentRefreshRate();
+ }
+
+ return std::min<uint32_t>(LUS::Context::GetInstance()->GetWindow()->GetCurrentRefreshRate(),
+ CVarGetInteger("gInterpolationFPS", 20));
+}
+
+struct ExtensionEntry {
+ std::string path;
+ std::string ext;
+};
+
+extern uintptr_t clearMtx;
+extern "C" Mtx gMtxClear;
+extern "C" MtxF gMtxFClear;
+extern "C" void OTRMessage_Init();
+extern "C" void AudioMgr_CreateNextAudioBuffer(s16* samples, u32 num_samples);
+extern "C" void AudioPlayer_Play(const uint8_t* buf, uint32_t len);
+extern "C" int AudioPlayer_Buffered(void);
+extern "C" int AudioPlayer_GetDesiredBuffered(void);
+extern "C" void ResourceMgr_LoadDirectory(const char* resName);
+std::unordered_map<std::string, ExtensionEntry> ExtensionCache;
+
+extern "C" void OTRExtScanner() {
+ auto lst = *LUS::Context::GetInstance()->GetResourceManager()->GetArchive()->ListFiles("*").get();
+
+ for (auto& rPath : lst) {
+ std::vector<std::string> raw = StringHelper::Split(rPath, ".");
+ std::string ext = raw[raw.size() - 1];
+ std::string nPath = rPath.substr(0, rPath.size() - (ext.size() + 1));
+ replace(nPath.begin(), nPath.end(), '\\', '/');
+
+ ExtensionCache[nPath] = { rPath, ext };
+ }
+}
+
+extern "C" void InitOTR() {
+#if not defined(__SWITCH__) && not defined(__WIIU__)
+ if (!std::filesystem::exists(LUS::Context::LocateFileAcrossAppDirs("mm.otr", appShortName))) {
+ std::string installPath = LUS::Context::GetAppBundlePath();
+ if (!std::filesystem::exists(installPath + "/assets/extractor")) {
+ Extractor::ShowErrorBox(
+ "Extractor assets not found",
+ "No OTR files found. Missing assets/extractor folder needed to generate OTR file. Exiting...");
+ exit(1);
+ }
+
+ bool generatedOtrIsMQ = false;
+ if (Extractor::ShowYesNoBox("No OTR Files", "No OTR files found. Generate one now?") == IDYES) {
+ Extractor extract;
+ if (!extract.Run()) {
+ Extractor::ShowErrorBox("Error", "An error occured, no OTR file was generated. Exiting...");
+ exit(1);
+ }
+ extract.CallZapd(installPath, LUS::Context::GetAppDirectoryPath(appShortName));
+ generatedOtrIsMQ = extract.IsMasterQuest();
+ } else {
+ exit(1);
+ }
+ }
+#endif
+
+#ifdef __SWITCH__
+ LUS::Switch::Init(LUS::PreInitPhase);
+#elif defined(__WIIU__)
+ LUS::WiiU::Init("soh");
+#endif
+
+ OTRGlobals::Instance = new OTRGlobals();
+ SohGui::SetupGuiElements();
+
+ clearMtx = (uintptr_t)&gMtxClear;
+ //OTRMessage_Init();
+ //OTRExtScanner();
+ time_t now = time(NULL);
+ tm* tm_now = localtime(&now);
+ if (tm_now->tm_mon == 11 && tm_now->tm_mday >= 24 && tm_now->tm_mday <= 25) {
+ CVarRegisterInteger("gLetItSnow", 1);
+ } else {
+ CVarClear("gLetItSnow");
+ }
+
+ srand(now);
+#ifdef ENABLE_CROWD_CONTROL
+ CrowdControl::Instance = new CrowdControl();
+ CrowdControl::Instance->Init();
+ if (CVarGetInteger("gCrowdControl", 0)) {
+ CrowdControl::Instance->Enable();
+ } else {
+ CrowdControl::Instance->Disable();
+ }
+#endif
+
+ std::shared_ptr<LUS::Config> conf = OTRGlobals::Instance->context->GetConfig();
+
+}
+
+extern "C" void SaveManager_ThreadPoolWait() {
+ //SaveManager::Instance->ThreadPoolWait();
+}
+
+extern "C" void DeinitOTR() {
+ SaveManager_ThreadPoolWait();
+#ifdef ENABLE_CROWD_CONTROL
+ CrowdControl::Instance->Disable();
+ CrowdControl::Instance->Shutdown();
+#endif
+
+ // Destroying gui here because we have shared ptrs to LUS objects which output to SPDLOG which is destroyed before
+ // these shared ptrs.
+ //SohGui::Destroy();
+
+ OTRGlobals::Instance->context = nullptr;
+}
+
+#ifdef _WIN32
+extern "C" uint64_t GetFrequency() {
+ LARGE_INTEGER nFreq;
+
+ QueryPerformanceFrequency(&nFreq);
+
+ return nFreq.QuadPart;
+}
+
+extern "C" uint64_t GetPerfCounter() {
+ LARGE_INTEGER ticks;
+ QueryPerformanceCounter(&ticks);
+
+ return ticks.QuadPart;
+}
+#else
+extern "C" uint64_t GetFrequency() {
+ return 1000; // sec -> ms
+}
+
+extern "C" uint64_t GetPerfCounter() {
+ struct timespec monotime;
+ clock_gettime(CLOCK_MONOTONIC, &monotime);
+
+ uint64_t remainingMs = (monotime.tv_nsec / 1000000);
+
+ // in milliseconds
+ return monotime.tv_sec * 1000 + remainingMs;
+}
+#endif
+
+extern "C" uint64_t GetUnixTimestamp() {
+ auto time = std::chrono::system_clock::now();
+ auto since_epoch = time.time_since_epoch();
+ auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(since_epoch);
+ long now = millis.count();
+ return now;
+}
+
+// C->C++ Bridge
+extern "C" void Graph_ProcessFrame(void (*run_one_game_iter)(void)) {
+ OTRGlobals::Instance->context->GetWindow()->MainLoop(run_one_game_iter);
+}
+
+extern bool ShouldClearTextureCacheAtEndOfFrame;
+
+extern "C" void Graph_StartFrame() {
+#ifndef __WIIU__
+ using LUS::KbScancode;
+ int32_t dwScancode = OTRGlobals::Instance->context->GetWindow()->GetLastScancode();
+ OTRGlobals::Instance->context->GetWindow()->SetLastScancode(-1);
+
+ switch (dwScancode) {
+ #if 0
+ case KbScancode::LUS_KB_F5: {
+ if (CVarGetInteger("gSaveStatesEnabled", 0) == 0) {
+ LUS::Context::GetInstance()->GetWindow()->GetGui()->GetGameOverlay()->TextDrawNotification(
+ 6.0f, true, "Save states not enabled. Check Cheats Menu.");
+ return;
+ }
+ const unsigned int slot = OTRGlobals::Instance->gSaveStateMgr->GetCurrentSlot();
+ const SaveStateReturn stateReturn =
+ OTRGlobals::Instance->gSaveStateMgr->AddRequest({ slot, RequestType::SAVE });
+
+ switch (stateReturn) {
+ case SaveStateReturn::SUCCESS:
+ SPDLOG_INFO("[SOH] Saved state to slot {}", slot);
+ break;
+ case SaveStateReturn::FAIL_WRONG_GAMESTATE:
+ SPDLOG_ERROR("[SOH] Can not save a state outside of \"GamePlay\"");
+ break;
+ [[unlikely]] default : break;
+ }
+ break;
+ }
+ case KbScancode::LUS_KB_F6: {
+ if (CVarGetInteger("gSaveStatesEnabled", 0) == 0) {
+ LUS::Context::GetInstance()->GetWindow()->GetGui()->GetGameOverlay()->TextDrawNotification(
+ 6.0f, true, "Save states not enabled. Check Cheats Menu.");
+ return;
+ }
+ unsigned int slot = OTRGlobals::Instance->gSaveStateMgr->GetCurrentSlot();
+ slot++;
+ if (slot > 5) {
+ slot = 0;
+ }
+ OTRGlobals::Instance->gSaveStateMgr->SetCurrentSlot(slot);
+ SPDLOG_INFO("Set SaveState slot to {}.", slot);
+ break;
+ }
+ case KbScancode::LUS_KB_F7: {
+ if (CVarGetInteger("gSaveStatesEnabled", 0) == 0) {
+ LUS::Context::GetInstance()->GetWindow()->GetGui()->GetGameOverlay()->TextDrawNotification(
+ 6.0f, true, "Save states not enabled. Check Cheats Menu.");
+ return;
+ }
+ const unsigned int slot = OTRGlobals::Instance->gSaveStateMgr->GetCurrentSlot();
+ const SaveStateReturn stateReturn =
+ OTRGlobals::Instance->gSaveStateMgr->AddRequest({ slot, RequestType::LOAD });
+
+ switch (stateReturn) {
+ case SaveStateReturn::SUCCESS:
+ SPDLOG_INFO("[SOH] Loaded state from slot {}", slot);
+ break;
+ case SaveStateReturn::FAIL_INVALID_SLOT:
+ SPDLOG_ERROR("[SOH] Invalid State Slot Number {}", slot);
+ break;
+ case SaveStateReturn::FAIL_STATE_EMPTY:
+ SPDLOG_ERROR("[SOH] State Slot {} is empty", slot);
+ break;
+ case SaveStateReturn::FAIL_WRONG_GAMESTATE:
+ SPDLOG_ERROR("[SOH] Can not load a state outside of \"GamePlay\"");
+ break;
+ [[unlikely]] default : break;
+ }
+
+ break;
+ }
+#endif
+#if defined(_WIN32) || defined(__APPLE__)
+ case KbScancode::LUS_KB_F9: {
+ // Toggle TTS
+ CVarSetInteger("gA11yTTS", !CVarGetInteger("gA11yTTS", 0));
+ break;
+ }
+#endif
+ case KbScancode::LUS_KB_TAB: {
+ // Toggle HD Assets
+ CVarSetInteger("gAltAssets", !CVarGetInteger("gAltAssets", 0));
+ //ShouldClearTextureCacheAtEndOfFrame = true;
+ break;
+ }
+ }
+#endif
+ OTRGlobals::Instance->context->GetWindow()->StartFrame();
+}
+
+void RunCommands(Gfx* Commands, const std::vector<std::unordered_map<Mtx*, MtxF>>& mtx_replacements) {
+ //for (const auto& m : mtx_replacements) {
+ gfx_run(Commands, {});
+ gfx_end_frame();
+ // }
+}
+
+// C->C++ Bridge
+extern "C" void Graph_ProcessGfxCommands(Gfx* commands) {
+ {
+ //std::unique_lock<std::mutex> Lock(audio.mutex);
+ //audio.processing = true;
+ }
+
+ //audio.cv_to_thread.notify_one();
+ std::vector<std::unordered_map<Mtx*, MtxF>> mtx_replacements;
+ int target_fps = 20;
+ //OTRGlobals::Instance->GetInterpolationFPS();
+ static int last_fps;
+ static int last_update_rate;
+ static int time;
+ int fps = target_fps;
+ int original_fps = 60 / R_UPDATE_RATE;
+
+ if (target_fps == 20 || original_fps > target_fps) {
+ fps = original_fps;
+ }
+
+ if (last_fps != fps || last_update_rate != R_UPDATE_RATE) {
+ time = 0;
+ }
+
+ // time_base = fps * original_fps (one second)
+ // int next_original_frame = fps;
+
+ //while (time + original_fps <= next_original_frame) {
+ // time += original_fps;
+ // if (time != next_original_frame) {
+ // mtx_replacements.push_back(FrameInterpolation_Interpolate((float)time / next_original_frame));
+ // } else {
+ // mtx_replacements.emplace_back();
+ // }
+ //}
+
+ //time -= fps;
+
+ OTRGlobals::Instance->context->GetWindow()->SetTargetFps(original_fps);
+ // OTRGlobals::Instance->context->GetWindow()->SetTargetFps(20);
+ //OTRGlobals::Instance->context->GetWindow()->SetTargetFps(60);
+
+ //int threshold = CVarGetInteger("gExtraLatencyThreshold", 80);
+ //OTRGlobals::Instance->context->GetWindow()->SetMaximumFrameLatency(threshold > 0 && target_fps >= threshold ? 2
+ //: 1);
+
+ RunCommands(commands, mtx_replacements);
+
+ //last_fps = fps;
+ //last_update_rate = R_UPDATE_RATE;
+
+ //{
+ // std::unique_lock<std::mutex> Lock(audio.mutex);
+ // while (audio.processing) {
+ // audio.cv_from_thread.wait(Lock);
+ // }
+ //}
+ //
+ //if (ShouldClearTextureCacheAtEndOfFrame) {
+ // gfx_texture_cache_clear();
+ // LUS::SkeletonPatcher::UpdateSkeletons();
+ // ShouldClearTextureCacheAtEndOfFrame = false;
+ //}
+
+ // OTRTODO: FIGURE OUT END FRAME POINT
+ /* if (OTRGlobals::Instance->context->lastScancode != -1)
+ OTRGlobals::Instance->context->lastScancode = -1;*/
+}
+
+float divisor_num = 0.0f;
+
+extern "C" void OTRGetPixelDepthPrepare(float x, float y) {
+ OTRGlobals::Instance->context->GetWindow()->GetPixelDepthPrepare(x, y);
+}
+
+extern "C" uint16_t OTRGetPixelDepth(float x, float y) {
+ return OTRGlobals::Instance->context->GetWindow()->GetPixelDepth(x, y);
+}
+
+extern "C" uint32_t ResourceMgr_GetNumGameVersions() {
+ return LUS::Context::GetInstance()->GetResourceManager()->GetArchive()->GetGameVersions().size();
+}
+
+extern "C" uint32_t ResourceMgr_GetGameVersion(int index) {
+ return LUS::Context::GetInstance()->GetResourceManager()->GetArchive()->GetGameVersions()[index];
+}
+
+extern "C" uint32_t ResourceMgr_GetGamePlatform(int index) {
+ uint32_t version = LUS::Context::GetInstance()->GetResourceManager()->GetArchive()->GetGameVersions()[index];
+
+ switch (version) {
+ case OOT_NTSC_US_10:
+ case OOT_NTSC_US_11:
+ case OOT_NTSC_US_12:
+ case OOT_PAL_10:
+ case OOT_PAL_11:
+ return GAME_PLATFORM_N64;
+ case OOT_NTSC_JP_GC:
+ case OOT_NTSC_US_GC:
+ case OOT_PAL_GC:
+ case OOT_NTSC_JP_MQ:
+ case OOT_NTSC_US_MQ:
+ case OOT_PAL_MQ:
+ case OOT_PAL_GC_DBG1:
+ case OOT_PAL_GC_DBG2:
+ case OOT_PAL_GC_MQ_DBG:
+ return GAME_PLATFORM_GC;
+ }
+}
+
+extern "C" uint32_t ResourceMgr_GetGameRegion(int index) {
+ uint32_t version = LUS::Context::GetInstance()->GetResourceManager()->GetArchive()->GetGameVersions()[index];
+
+ switch (version) {
+ case OOT_NTSC_US_10:
+ case OOT_NTSC_US_11:
+ case OOT_NTSC_US_12:
+ case OOT_NTSC_JP_GC:
+ case OOT_NTSC_US_GC:
+ case OOT_NTSC_JP_MQ:
+ case OOT_NTSC_US_MQ:
+ return GAME_REGION_NTSC;
+ case OOT_PAL_10:
+ case OOT_PAL_11:
+ case OOT_PAL_GC:
+ case OOT_PAL_MQ:
+ case OOT_PAL_GC_DBG1:
+ case OOT_PAL_GC_DBG2:
+ case OOT_PAL_GC_MQ_DBG:
+ return GAME_REGION_PAL;
+ }
+}
+
+uint32_t IsSceneMasterQuest(s16 sceneNum) {
+ return false;
+ uint32_t value = 0;
+ //uint8_t mqMode = CVarGetInteger("gBetterDebugWarpScreenMQMode", WARP_MODE_OVERRIDE_OFF);
+ //if (mqMode == WARP_MODE_OVERRIDE_MQ_AS_VANILLA) {
+ // return 1;
+ //} else if (mqMode == WARP_MODE_OVERRIDE_VANILLA_AS_MQ) {
+ // return 0;
+ //} else {
+ // if (OTRGlobals::Instance->HasMasterQuest()) {
+ // if (!OTRGlobals::Instance->HasOriginal()) {
+ // value = 1;
+ // } else if (IS_MASTER_QUEST) {
+ // value = 1;
+ // } else {
+ // value = 0;
+ // if (IS_RANDO && !OTRGlobals::Instance->gRandomizer->masterQuestDungeons.empty() &&
+ // OTRGlobals::Instance->gRandomizer->masterQuestDungeons.contains(sceneNum)) {
+ // value = 1;
+ // }
+ // }
+ // }
+ //}
+ return value;
+}
+
+uint32_t IsGameMasterQuest() {
+ return false;
+ //return gPlayState != NULL ? IsSceneMasterQuest(gPlayState->sceneNum) : 0;
+}
+
+extern "C" uint32_t ResourceMgr_GameHasMasterQuest() {
+ return OTRGlobals::Instance->HasMasterQuest();
+}
+
+extern "C" uint32_t ResourceMgr_GameHasOriginal() {
+ return OTRGlobals::Instance->HasOriginal();
+}
+
+extern "C" uint32_t ResourceMgr_IsSceneMasterQuest(s16 sceneNum) {
+ return IsSceneMasterQuest(sceneNum);
+}
+
+extern "C" uint32_t ResourceMgr_IsGameMasterQuest() {
+ return IsGameMasterQuest();
+}
+
+extern "C" void ResourceMgr_LoadDirectory(const char* resName) {
+ LUS::Context::GetInstance()->GetResourceManager()->LoadDirectory(resName);
+}
+extern "C" void ResourceMgr_DirtyDirectory(const char* resName) {
+ LUS::Context::GetInstance()->GetResourceManager()->DirtyDirectory(resName);
+}
+
+// OTRTODO: There is probably a more elegant way to go about this...
+// Kenix: This is definitely leaking memory when it's called.
+extern "C" char** ResourceMgr_ListFiles(const char* searchMask, int* resultSize) {
+ auto lst = LUS::Context::GetInstance()->GetResourceManager()->GetArchive()->ListFiles(searchMask);
+ char** result = (char**)malloc(lst->size() * sizeof(char*));
+
+ for (size_t i = 0; i < lst->size(); i++) {
+ char* str = (char*)malloc(lst.get()[0][i].size() + 1);
+ memcpy(str, lst.get()[0][i].data(), lst.get()[0][i].size());
+ str[lst.get()[0][i].size()] = '\0';
+ result[i] = str;
+ }
+ *resultSize = lst->size();
+
+ return result;
+}
+
+extern "C" uint8_t ResourceMgr_FileExists(const char* filePath) {
+ std::string path = filePath;
+ if (path.substr(0, 7) == "__OTR__") {
+ path = path.substr(7);
+ }
+
+ return ExtensionCache.contains(path);
+}
+
+extern "C" void ResourceMgr_LoadFile(const char* resName) {
+ LUS::Context::GetInstance()->GetResourceManager()->LoadResource(resName);
+}
+
+std::shared_ptr<LUS::IResource> GetResourceByNameHandlingMQ(const char* path) {
+ std::string Path = path;
+ if (ResourceMgr_IsGameMasterQuest()) {
+ size_t pos = 0;
+ if ((pos = Path.find("/nonmq/", 0)) != std::string::npos) {
+ Path.replace(pos, 7, "/mq/");
+ }
+ }
+ return LUS::Context::GetInstance()->GetResourceManager()->LoadResource(Path.c_str());
+}
+
+extern "C" char* GetResourceDataByNameHandlingMQ(const char* path) {
+ auto res = GetResourceByNameHandlingMQ(path);
+
+ if (res == nullptr) {
+ return nullptr;
+ }
+
+ return (char*)res->GetRawPointer();
+}
+
+extern "C" char* ResourceMgr_LoadFileFromDisk(const char* filePath) {
+ FILE* file = fopen(filePath, "r");
+ fseek(file, 0, SEEK_END);
+ int fSize = ftell(file);
+ fseek(file, 0, SEEK_SET);
+
+ char* data = (char*)malloc(fSize);
+ fread(data, 1, fSize, file);
+
+ fclose(file);
+
+ return data;
+}
+
+extern "C" uint8_t ResourceMgr_ResourceIsBackground(char* texPath) {
+ auto res = GetResourceByNameHandlingMQ(texPath);
+ return res->GetInitData()->Type == LUS::ResourceType::SOH_Background;
+}
+
+extern "C" char* ResourceMgr_LoadJPEG(char* data, size_t dataSize) {
+ static char* finalBuffer = 0;
+
+ if (finalBuffer == 0)
+ finalBuffer = (char*)malloc(dataSize);
+
+ int w;
+ int h;
+ int comp;
+
+ unsigned char* pixels =
+ stbi_load_from_memory((const unsigned char*)data, 320 * 240 * 2, &w, &h, &comp, STBI_rgb_alpha);
+ // unsigned char* pixels = stbi_load_from_memory((const unsigned char*)data, 480 * 240 * 2, &w, &h, &comp,
+ // STBI_rgb_alpha);
+ int idx = 0;
+
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ uint16_t* bufferTest = (uint16_t*)finalBuffer;
+ int pixelIdx = ((y * w) + x) * 4;
+
+ uint8_t r = pixels[pixelIdx + 0] / 8;
+ uint8_t g = pixels[pixelIdx + 1] / 8;
+ uint8_t b = pixels[pixelIdx + 2] / 8;
+
+ uint8_t alphaBit = pixels[pixelIdx + 3] != 0;
+
+ uint16_t data = (r << 11) + (g << 6) + (b << 1) + alphaBit;
+
+ finalBuffer[idx++] = (data & 0xFF00) >> 8;
+ finalBuffer[idx++] = (data & 0x00FF);
+ }
+ }
+
+ return (char*)finalBuffer;
+}
+
+extern "C" uint16_t ResourceMgr_LoadTexWidthByName(char* texPath);
+
+extern "C" uint16_t ResourceMgr_LoadTexHeightByName(char* texPath);
+
+extern "C" char* ResourceMgr_LoadTexOrDListByName(const char* filePath) {
+ auto res = GetResourceByNameHandlingMQ(filePath);
+
+ if (res->GetInitData()->Type == LUS::ResourceType::DisplayList)
+ return (char*)&((std::static_pointer_cast<LUS::DisplayList>(res))->Instructions[0]);
+ else if (res->GetInitData()->Type == LUS::ResourceType::Array)
+ return (char*)(std::static_pointer_cast<LUS::Array>(res))->Vertices.data();
+ else {
+ return (char*)GetResourceDataByNameHandlingMQ(filePath);
+ }
+}
+
+extern "C" char* ResourceMgr_LoadIfDListByName(const char* filePath) {
+ auto res = GetResourceByNameHandlingMQ(filePath);
+
+ if (res->GetInitData()->Type == LUS::ResourceType::DisplayList)
+ return (char*)&((std::static_pointer_cast<LUS::DisplayList>(res))->Instructions[0]);
+
+ return nullptr;
+}
+
+//extern "C" Sprite* GetSeedTexture(uint8_t index) {
+// return OTRGlobals::Instance->gRandomizer->GetSeedTexture(index);
+//}
+
+extern "C" char* ResourceMgr_LoadPlayerAnimByName(const char* animPath) {
+ auto anim = std::static_pointer_cast<LUS::PlayerAnimation>(GetResourceByNameHandlingMQ(animPath));
+
+ return (char*)&anim->limbRotData[0];
+}
+
+extern "C" void ResourceMgr_PushCurrentDirectory(char* path) {
+ gfx_push_current_dir(path);
+}
+
+extern "C" Gfx* ResourceMgr_LoadGfxByName(const char* path) {
+ auto res = std::static_pointer_cast<LUS::DisplayList>(GetResourceByNameHandlingMQ(path));
+ return (Gfx*)&res->Instructions[0];
+}
+
+typedef struct {
+ int index;
+ Gfx instruction;
+} GfxPatch;
+
+std::unordered_map<std::string, std::unordered_map<std::string, GfxPatch>> originalGfx;
+
+// Attention! This is primarily for cosmetics & bug fixes. For things like mods and model replacement you should be
+// using OTRs instead (When that is available). Index can be found using the commented out section below.
+extern "C" void ResourceMgr_PatchGfxByName(const char* path, const char* patchName, int index, Gfx instruction) {
+ auto res = std::static_pointer_cast<LUS::DisplayList>(
+ LUS::Context::GetInstance()->GetResourceManager()->LoadResource(path));
+
+ // Leaving this here for people attempting to find the correct Dlist index to patch
+ /*if (strcmp("__OTR__objects/object_gi_longsword/gGiBiggoronSwordDL", path) == 0) {
+ for (int i = 0; i < res->instructions.size(); i++) {
+ Gfx* gfx = (Gfx*)&res->instructions[i];
+ // Log all commands
+ // SPDLOG_INFO("index:{} command:{}", i, gfx->words.w0 >> 24);
+ // Log only SetPrimColors
+ if (gfx->words.w0 >> 24 == 250) {
+ SPDLOG_INFO("index:{} r:{} g:{} b:{} a:{}", i, _SHIFTR(gfx->words.w1, 24, 8), _SHIFTR(gfx->words.w1, 16,
+ 8), _SHIFTR(gfx->words.w1, 8, 8), _SHIFTR(gfx->words.w1, 0, 8));
+ }
+ }
+ }*/
+
+ // Index refers to individual gfx words, which are half the size on 32-bit
+ // if (sizeof(uintptr_t) < 8) {
+ // index /= 2;
+ // }
+
+ Gfx* gfx = (Gfx*)&res->Instructions[index];
+
+ if (!originalGfx.contains(path) || !originalGfx[path].contains(patchName)) {
+ originalGfx[path][patchName] = { index, *gfx };
+ }
+
+ *gfx = instruction;
+}
+
+extern "C" void ResourceMgr_PatchGfxCopyCommandByName(const char* path, const char* patchName, int destinationIndex,
+ int sourceIndex) {
+ auto res = std::static_pointer_cast<LUS::DisplayList>(
+ LUS::Context::GetInstance()->GetResourceManager()->LoadResource(path));
+
+ Gfx* destinationGfx = (Gfx*)&res->Instructions[destinationIndex];
+ Gfx sourceGfx = res->Instructions[sourceIndex];
+
+ if (!originalGfx.contains(path) || !originalGfx[path].contains(patchName)) {
+ originalGfx[path][patchName] = { destinationIndex, *destinationGfx };
+ }
+
+ *destinationGfx = sourceGfx;
+}
+
+extern "C" void ResourceMgr_UnpatchGfxByName(const char* path, const char* patchName) {
+ if (originalGfx.contains(path) && originalGfx[path].contains(patchName)) {
+ auto res = std::static_pointer_cast<LUS::DisplayList>(
+ LUS::Context::GetInstance()->GetResourceManager()->LoadResource(path));
+
+ Gfx* gfx = (Gfx*)&res->Instructions[originalGfx[path][patchName].index];
+ *gfx = originalGfx[path][patchName].instruction;
+
+ originalGfx[path].erase(patchName);
+ }
+}
+
+extern "C" char* ResourceMgr_LoadVtxArrayByName(const char* path) {
+ auto res = std::static_pointer_cast<LUS::Array>(GetResourceByNameHandlingMQ(path));
+
+ return (char*)res->Vertices.data();
+}
+
+extern "C" size_t ResourceMgr_GetVtxArraySizeByName(const char* path) {
+ auto res = std::static_pointer_cast<LUS::Array>(GetResourceByNameHandlingMQ(path));
+
+ return res->Vertices.size();
+ // }
+}
+
+extern "C" char* ResourceMgr_LoadArrayByName(const char* path) {
+ auto res = std::static_pointer_cast<LUS::Array>(GetResourceByNameHandlingMQ(path));
+
+ return (char*)res->Scalars.data();
+}
+
+extern "C" size_t ResourceMgr_GetArraySizeByName(const char* path) {
+ auto res = std::static_pointer_cast<LUS::Array>(GetResourceByNameHandlingMQ(path));
+
+ return res->Scalars.size();
+ // }
+}
+extern "C" char* ResourceMgr_LoadArrayByNameAsVec3s(const char* path) {
+ auto res = std::static_pointer_cast<LUS::Array>(GetResourceByNameHandlingMQ(path));
+
+ // if (res->CachedGameAsset != nullptr)
+ // return (char*)res->CachedGameAsset;
+ // else
+ // {
+ Vec3s* data = (Vec3s*)malloc(sizeof(Vec3s) * res->Scalars.size());
+
+ for (size_t i = 0; i < res->Scalars.size(); i += 3) {
+ data[(i / 3)].x = res->Scalars[i + 0].s16;
+ data[(i / 3)].y = res->Scalars[i + 1].s16;
+ data[(i / 3)].z = res->Scalars[i + 2].s16;
+ }
+
+ // res->CachedGameAsset = data;
+
+ return (char*)data;
+ // }
+}
+
+extern "C" AnimatedMaterial* ResourceMgr_LoadAnimatedMatByName(const char* path) {
+ return (AnimatedMaterial*)ResourceGetDataByName(path);
+}
+
+extern "C" CollisionHeader* ResourceMgr_LoadColByName(const char* path) {
+ return (CollisionHeader*)ResourceGetDataByName(path);
+}
+
+extern "C" Vtx* ResourceMgr_LoadVtxByName(char* path) {
+ return (Vtx*)ResourceGetDataByName(path);
+}
+
+//extern "C" SequenceData ResourceMgr_LoadSeqByName(const char* path) {
+// SequenceData* sequence = (SequenceData*)ResourceGetDataByName(path);
+// return *sequence;
+//}
+//
+//std::map<std::string, SoundFontSample*> cachedCustomSFs;
+#if 0
+extern "C" SoundFontSample* ReadCustomSample(const char* path) {
+ return nullptr;
+ /*
+ if (!ExtensionCache.contains(path))
+ return nullptr;
+
+ ExtensionEntry entry = ExtensionCache[path];
+
+ auto sampleRaw = LUS::Context::GetInstance()->GetResourceManager()->LoadFile(entry.path);
+ uint32_t* strem = (uint32_t*)sampleRaw->Buffer.get();
+ uint8_t* strem2 = (uint8_t*)strem;
+
+ SoundFontSample* sampleC = new SoundFontSample;
+
+ if (entry.ext == "wav") {
+ drwav_uint32 channels;
+ drwav_uint32 sampleRate;
+ drwav_uint64 totalPcm;
+ drmp3_int16* pcmData =
+ drwav_open_memory_and_read_pcm_frames_s16(strem2, sampleRaw->BufferSize, &channels, &sampleRate,
+ &totalPcm, NULL); sampleC->size = totalPcm; sampleC->sampleAddr = (uint8_t*)pcmData; sampleC->codec = CODEC_S16;
+
+ sampleC->loop = new AdpcmLoop;
+ sampleC->loop->start = 0;
+ sampleC->loop->end = sampleC->size - 1;
+ sampleC->loop->count = 0;
+ sampleC->sampleRateMagicValue = 'RIFF';
+ sampleC->sampleRate = sampleRate;
+
+ cachedCustomSFs[path] = sampleC;
+ return sampleC;
+ } else if (entry.ext == "mp3") {
+ drmp3_config mp3Info;
+ drmp3_uint64 totalPcm;
+ drmp3_int16* pcmData =
+ drmp3_open_memory_and_read_pcm_frames_s16(strem2, sampleRaw->BufferSize, &mp3Info, &totalPcm, NULL);
+
+ sampleC->size = totalPcm * mp3Info.channels * sizeof(short);
+ sampleC->sampleAddr = (uint8_t*)pcmData;
+ sampleC->codec = CODEC_S16;
+
+ sampleC->loop = new AdpcmLoop;
+ sampleC->loop->start = 0;
+ sampleC->loop->end = sampleC->size;
+ sampleC->loop->count = 0;
+ sampleC->sampleRateMagicValue = 'RIFF';
+ sampleC->sampleRate = mp3Info.sampleRate;
+
+ cachedCustomSFs[path] = sampleC;
+ return sampleC;
+ }
+
+ return nullptr;
+ */
+}
+
+extern "C" SoundFontSample* ResourceMgr_LoadAudioSample(const char* path) {
+ return (SoundFontSample*)ResourceGetDataByName(path);
+}
+
+extern "C" SoundFont* ResourceMgr_LoadAudioSoundFont(const char* path) {
+ return (SoundFont*)ResourceGetDataByName(path);
+}
+#endif
+extern "C" int ResourceMgr_OTRSigCheck(char* imgData) {
+ uintptr_t i = (uintptr_t)(imgData);
+
+ // if (i == 0xD9000000 || i == 0xE7000000 || (i & 1) == 1)
+ if ((i & 1) == 1)
+ return 0;
+
+ // if ((i & 0xFF000000) != 0xAB000000 && (i & 0xFF000000) != 0xCD000000 && i != 0) {
+ if (i != 0) {
+ if (imgData[0] == '_' && imgData[1] == '_' && imgData[2] == 'O' && imgData[3] == 'T' && imgData[4] == 'R' &&
+ imgData[5] == '_' && imgData[6] == '_')
+ return 1;
+ }
+
+ return 0;
+}
+
+extern "C" AnimationHeaderCommon* ResourceMgr_LoadAnimByName(const char* path) {
+ return (AnimationHeaderCommon*)ResourceGetDataByName(path);
+}
+
+extern "C" SkeletonHeader* ResourceMgr_LoadSkeletonByName(const char* path, SkelAnime* skelAnime) {
+ std::string pathStr = std::string(path);
+ static const std::string sOtr = "__OTR__";
+
+ if (pathStr.starts_with(sOtr)) {
+ pathStr = pathStr.substr(sOtr.length());
+ }
+
+ bool isAlt = CVarGetInteger("gAltAssets", 0);
+
+ if (isAlt) {
+ pathStr = LUS::IResource::gAltAssetPrefix + pathStr;
+ }
+
+ SkeletonHeader* skelHeader = (SkeletonHeader*)ResourceGetDataByName(pathStr.c_str());
+
+ // If there isn't an alternate model, load the regular one
+ if (isAlt && skelHeader == NULL) {
+ skelHeader = (SkeletonHeader*)ResourceGetDataByName(path);
+ }
+
+ // This function is only called when a skeleton is initialized.
+ // Therefore we can take this oppurtunity to take note of the Skeleton that is created...
+ if (skelAnime != nullptr) {
+ auto stringPath = std::string(path);
+ //LUS::SkeletonPatcher::RegisterSkeleton(stringPath, skelAnime);
+ }
+
+ return skelHeader;
+}
+
+extern "C" void ResourceMgr_UnregisterSkeleton(SkelAnime* skelAnime) {
+ if (skelAnime != nullptr)
+ LUS::SkeletonPatcher::UnregisterSkeleton(skelAnime);
+}
+
+extern "C" void ResourceMgr_ClearSkeletons(SkelAnime* skelAnime) {
+ if (skelAnime != nullptr)
+ LUS::SkeletonPatcher::ClearSkeletons();
+}
+
+extern "C" s32* ResourceMgr_LoadCSByName(const char* path) {
+ return (s32*)GetResourceDataByNameHandlingMQ(path);
+}
+
+std::filesystem::path GetSaveFile(std::shared_ptr<LUS::Config> Conf) {
+ const std::string fileName =
+ Conf->GetString("Game.SaveName", LUS::Context::GetPathRelativeToAppDirectory("oot_save.sav"));
+ std::filesystem::path saveFile = std::filesystem::absolute(fileName);
+
+ if (!exists(saveFile.parent_path())) {
+ create_directories(saveFile.parent_path());
+ }
+
+ return saveFile;
+}
+
+std::filesystem::path GetSaveFile() {
+ const std::shared_ptr<LUS::Config> pConf = OTRGlobals::Instance->context->GetConfig();
+
+ return GetSaveFile(pConf);
+}
+
+void OTRGlobals::CheckSaveFile(size_t sramSize) const {
+ const std::shared_ptr<LUS::Config> pConf = Instance->context->GetConfig();
+
+ std::filesystem::path savePath = GetSaveFile(pConf);
+ std::fstream saveFile(savePath, std::fstream::in | std::fstream::out | std::fstream::binary);
+ if (saveFile.fail()) {
+ saveFile.open(savePath, std::fstream::in | std::fstream::out | std::fstream::binary | std::fstream::app);
+ for (int i = 0; i < sramSize; ++i) {
+ saveFile.write("\0", 1);
+ }
+ }
+ saveFile.close();
+}
+
+//extern "C" void Ctx_ReadSaveFile(uintptr_t addr, void* dramAddr, size_t size) {
+// SaveManager::ReadSaveFile(GetSaveFile(), addr, dramAddr, size);
+//}
+
+//extern "C" void Ctx_WriteSaveFile(uintptr_t addr, void* dramAddr, size_t size) {
+// SaveManager::WriteSaveFile(GetSaveFile(), addr, dramAddr, size);
+//}
+
+std::wstring StringToU16(const std::string& s) {
+ std::vector<unsigned long> result;
+ size_t i = 0;
+ while (i < s.size()) {
+ unsigned long uni;
+ size_t nbytes;
+ bool error = false;
+ unsigned char c = s[i++];
+ if (c < 0x80) { // ascii
+ uni = c;
+ nbytes = 0;
+ } else if (c <= 0xBF) { // assuming kata/hiragana delimiter
+ nbytes = 0;
+ uni = '\1';
+ } else if (c <= 0xDF) {
+ uni = c & 0x1F;
+ nbytes = 1;
+ } else if (c <= 0xEF) {
+ uni = c & 0x0F;
+ nbytes = 2;
+ } else if (c <= 0xF7) {
+ uni = c & 0x07;
+ nbytes = 3;
+ }
+ for (size_t j = 0; j < nbytes; ++j) {
+ unsigned char c = s[i++];
+ uni <<= 6;
+ uni += c & 0x3F;
+ }
+ if (uni != '\1')
+ result.push_back(uni);
+ }
+ std::wstring utf16;
+ for (size_t i = 0; i < result.size(); ++i) {
+ unsigned long uni = result[i];
+ if (uni <= 0xFFFF) {
+ utf16 += (wchar_t)uni;
+ } else {
+ uni -= 0x10000;
+ utf16 += (wchar_t)((uni >> 10) + 0xD800);
+ utf16 += (wchar_t)((uni & 0x3FF) + 0xDC00);
+ }
+ }
+ return utf16;
+}
+
+int CopyStringToCharBuffer(const std::string& inputStr, char* buffer, const int maxBufferSize) {
+ if (!inputStr.empty()) {
+ // Prevent potential horrible overflow due to implicit conversion of maxBufferSize to an unsigned. Prevents
+ // negatives.
+ memset(buffer, 0, std::max<int>(0, maxBufferSize));
+ // Gaurentee that this value will be greater than 0, regardless of passed variables.
+ const int copiedCharLen = std::min<int>(std::max<int>(0, maxBufferSize - 1), inputStr.length());
+ memcpy(buffer, inputStr.c_str(), copiedCharLen);
+ return copiedCharLen;
+ }
+
+ return 0;
+}
+
+extern "C" void OTRGfxPrint(const char* str, void* printer, void (*printImpl)(void*, char)) {
+ const std::vector<uint32_t> hira1 = {
+ u'を', u'ぁ', u'ぃ', u'ぅ', u'ぇ', u'ぉ', u'ゃ', u'ゅ', u'ょ', u'っ', u'-', u'あ', u'い',
+ u'う', u'え', u'お', u'か', u'き', u'く', u'け', u'こ', u'さ', u'し', u'す', u'せ', u'そ',
+ };
+
+ const std::vector<uint32_t> hira2 = {
+ u'た', u'ち', u'つ', u'て', u'と', u'な', u'に', u'ぬ', u'ね', u'の', u'は', u'ひ', u'ふ', u'へ', u'ほ', u'ま',
+ u'み', u'む', u'め', u'も', u'や', u'ゆ', u'よ', u'ら', u'り', u'る', u'れ', u'ろ', u'わ', u'ん', u'゛', u'゜',
+ };
+
+ std::wstring wstr = StringToU16(str);
+
+ for (const auto& c : wstr) {
+ unsigned char convt = ' ';
+ if (c < 0x80) {
+ printImpl(printer, c);
+ } else if (c >= u'。' && c <= u'゚') { // katakana
+ printImpl(printer, c - 0xFEC0);
+ } else {
+ auto it = std::find(hira1.begin(), hira1.end(), c);
+ if (it != hira1.end()) { // hiragana block 1
+ printImpl(printer, 0x88 + std::distance(hira1.begin(), it));
+ }
+
+ auto it2 = std::find(hira2.begin(), hira2.end(), c);
+ if (it2 != hira2.end()) { // hiragana block 2
+ printImpl(printer, 0xe0 + std::distance(hira2.begin(), it2));
+ }
+ }
+ }
+}
+
+extern "C" uint32_t OTRGetCurrentWidth() {
+ return OTRGlobals::Instance->context->GetWindow()->GetWidth();
+}
+
+extern "C" uint32_t OTRGetCurrentHeight() {
+ return OTRGlobals::Instance->context->GetWindow()->GetHeight();
+}
+
+Color_RGB8 GetColorForControllerLED() {
+ #if 0
+ auto brightness = CVarGetFloat("gLedBrightness", 1.0f) / 1.0f;
+ Color_RGB8 color = { 0, 0, 0 };
+ if (brightness > 0.0f) {
+ LEDColorSource source =
+ static_cast<LEDColorSource>(CVarGetInteger("gLedColorSource", LED_SOURCE_TUNIC_ORIGINAL));
+ bool criticalOverride = CVarGetInteger("gLedCriticalOverride", 1);
+ if (gPlayState && (source == LED_SOURCE_TUNIC_ORIGINAL || source == LED_SOURCE_TUNIC_COSMETICS)) {
+ switch (CUR_EQUIP_VALUE(EQUIP_TUNIC) - 1) {
+ case PLAYER_TUNIC_KOKIRI:
+ color = source == LED_SOURCE_TUNIC_COSMETICS
+ ? CVarGetColor24("gCosmetics.Link_KokiriTunic.Value", kokiriColor)
+ : kokiriColor;
+ break;
+ case PLAYER_TUNIC_GORON:
+ color = source == LED_SOURCE_TUNIC_COSMETICS
+ ? CVarGetColor24("gCosmetics.Link_GoronTunic.Value", goronColor)
+ : goronColor;
+ break;
+ case PLAYER_TUNIC_ZORA:
+ color = source == LED_SOURCE_TUNIC_COSMETICS
+ ? CVarGetColor24("gCosmetics.Link_ZoraTunic.Value", zoraColor)
+ : zoraColor;
+ break;
+ }
+ }
+ if (source == LED_SOURCE_CUSTOM) {
+ color = CVarGetColor24("gLedPort1Color", { 255, 255, 255 });
+ }
+ if (criticalOverride || source == LED_SOURCE_HEALTH) {
+ if (HealthMeter_IsCritical()) {
+ color = { 0xFF, 0, 0 };
+ } else if (source == LED_SOURCE_HEALTH) {
+ if (gSaveContext.health / gSaveContext.healthCapacity <= 0.4f) {
+ color = { 0xFF, 0xFF, 0 };
+ } else {
+ color = { 0, 0xFF, 0 };
+ }
+ }
+ }
+ color.r = color.r * brightness;
+ color.g = color.g * brightness;
+ color.b = color.b * brightness;
+ }
+ #endif
+ return { 0, 0, 0 };
+}
+
+extern "C" void OTRControllerCallback(uint8_t rumble) {
+ auto physicalDevice = LUS::Context::GetInstance()->GetControlDeck()->GetDeviceFromPortIndex(0);
+
+ if (physicalDevice->CanSetLed()) {
+ // We call this every tick, SDL accounts for this use and prevents driver spam
+ // https://github.com/libsdl-org/SDL/blob/f17058b562c8a1090c0c996b42982721ace90903/src/joystick/SDL_joystick.c#L1114-L1144
+ physicalDevice->SetLedColor(0, GetColorForControllerLED());
+ }
+
+ physicalDevice->SetRumble(0, rumble);
+}
+
+extern "C" float OTRGetAspectRatio() {
+ return gfx_current_dimensions.aspect_ratio;
+}
+
+extern "C" float OTRGetDimensionFromLeftEdge(float v) {
+ return (SCREEN_WIDTH / 2 - SCREEN_HEIGHT / 2 * OTRGetAspectRatio() + (v));
+}
+
+extern "C" float OTRGetDimensionFromRightEdge(float v) {
+ return (SCREEN_WIDTH / 2 + SCREEN_HEIGHT / 2 * OTRGetAspectRatio() - (SCREEN_WIDTH - v));
+}
+
+f32 floorf(f32 x);
+f32 ceilf(f32 x);
+
+extern "C" int16_t OTRGetRectDimensionFromLeftEdge(float v) {
+ return ((int)floorf(OTRGetDimensionFromLeftEdge(v)));
+}
+
+extern "C" int16_t OTRGetRectDimensionFromRightEdge(float v) {
+ return ((int)ceilf(OTRGetDimensionFromRightEdge(v)));
+}
+
+extern "C" int AudioPlayer_Buffered(void) {
+ return AudioPlayerBuffered();
+}
+
+extern "C" int AudioPlayer_GetDesiredBuffered(void) {
+ return AudioPlayerGetDesiredBuffered();
+}
+
+extern "C" void AudioPlayer_Play(const uint8_t* buf, uint32_t len) {
+ AudioPlayerPlayFrame(buf, len);
+}
+
+extern "C" int Controller_ShouldRumble(size_t slot) {
+ auto controlDeck = LUS::Context::GetInstance()->GetControlDeck();
+
+ if (slot < controlDeck->GetNumConnectedPorts()) {
+ auto physicalDevice = controlDeck->GetDeviceFromPortIndex(slot);
+
+ if (physicalDevice->GetProfile(slot)->UseRumble && physicalDevice->CanRumble()) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
diff --git a/mm/2s2h/BenPort.h b/mm/2s2h/BenPort.h
new file mode 100644
index 000000000..fbdd93bcf
--- /dev/null
+++ b/mm/2s2h/BenPort.h
@@ -0,0 +1,137 @@
+#ifndef OTR_GLOBALS_H
+#define OTR_GLOBALS_H
+
+#pragma once
+
+
+#define GAME_REGION_NTSC 0
+#define GAME_REGION_PAL 1
+
+#define GAME_PLATFORM_N64 0
+#define GAME_PLATFORM_GC 1
+
+#ifdef __cplusplus
+#include <Context.h>
+
+#include <vector>
+
+const std::string customMessageTableID = "BaseGameOverrides";
+const std::string appShortName = "soh";
+
+class OTRGlobals {
+ public:
+ static OTRGlobals* Instance;
+
+ std::shared_ptr<LUS::Context> context;
+
+ OTRGlobals();
+ ~OTRGlobals();
+
+ bool HasMasterQuest();
+ bool HasOriginal();
+ uint32_t GetInterpolationFPS();
+ std::shared_ptr<std::vector<std::string>> ListFiles(std::string path);
+
+ private:
+ void CheckSaveFile(size_t sramSize) const;
+ bool hasMasterQuest;
+ bool hasOriginal;
+};
+
+uint32_t IsGameMasterQuest();
+#endif
+
+#ifndef __cplusplus
+void InitOTR(void);
+void DeinitOTR(void);
+void VanillaItemTable_Init();
+void OTRAudio_Init();
+void OTRMessage_Init();
+void InitAudio();
+void Graph_StartFrame();
+void Graph_ProcessGfxCommands(Gfx* commands);
+void Graph_ProcessFrame(void (*run_one_game_iter)(void));
+void OTRLogString(const char* src);
+void OTRGfxPrint(const char* str, void* printer, void (*printImpl)(void*, char));
+void OTRGetPixelDepthPrepare(float x, float y);
+uint16_t OTRGetPixelDepth(float x, float y);
+int32_t OTRGetLastScancode();
+uint32_t ResourceMgr_IsGameMasterQuest();
+uint32_t ResourceMgr_IsSceneMasterQuest(s16 sceneNum);
+uint32_t ResourceMgr_GameHasMasterQuest();
+uint32_t ResourceMgr_GameHasOriginal();
+uint32_t ResourceMgr_GetNumGameVersions();
+uint32_t ResourceMgr_GetGameVersion(int index);
+uint32_t ResourceMgr_GetGamePlatform(int index);
+uint32_t ResourceMgr_GetGameRegion(int index);
+void ResourceMgr_LoadDirectory(const char* resName);
+char** ResourceMgr_ListFiles(const char* searchMask, int* resultSize);
+uint8_t ResourceMgr_FileExists(const char* resName);
+char* GetResourceDataByNameHandlingMQ(const char* path);
+void ResourceMgr_LoadFile(const char* resName);
+char* ResourceMgr_LoadFileFromDisk(const char* filePath);
+uint8_t ResourceMgr_ResourceIsBackground(char* texPath);
+char* ResourceMgr_LoadJPEG(char* data, size_t dataSize);
+uint16_t ResourceMgr_LoadTexWidthByName(char* texPath);
+uint16_t ResourceMgr_LoadTexHeightByName(char* texPath);
+CollisionHeader* ResourceMgr_LoadColByName(const char* path);
+AnimatedMaterial* ResourceMgr_LoadAnimatedMatByName(const char* path);
+char* ResourceMgr_LoadTexOrDListByName(const char* filePath);
+char* ResourceMgr_LoadPlayerAnimByName(const char* animPath);
+AnimationHeaderCommon* ResourceMgr_LoadAnimByName(const char* path);
+char* ResourceMgr_GetNameByCRC(uint64_t crc, char* alloc);
+Gfx* ResourceMgr_LoadGfxByCRC(uint64_t crc);
+Gfx* ResourceMgr_LoadGfxByName(const char* path);
+void ResourceMgr_PatchGfxByName(const char* path, const char* patchName, int index, Gfx instruction);
+void ResourceMgr_UnpatchGfxByName(const char* path, const char* patchName);
+char* ResourceMgr_LoadArrayByNameAsVec3s(const char* path);
+char* ResourceMgr_LoadArrayByName(const char* path);
+size_t ResourceMgr_GetArraySizeByName(const char* path);
+Vtx* ResourceMgr_LoadVtxByCRC(uint64_t crc);
+char* ResourceMgr_LoadVtxArrayByName(const char* path);
+size_t ResourceMgr_GetVtxArraySizeByName(const char* path);
+Vtx* ResourceMgr_LoadVtxByName(char* path);
+
+void Ctx_ReadSaveFile(uintptr_t addr, void* dramAddr, size_t size);
+void Ctx_WriteSaveFile(uintptr_t addr, void* dramAddr, size_t size);
+
+uint64_t GetPerfCounter();
+struct SkeletonHeader* ResourceMgr_LoadSkeletonByName(const char* path, SkelAnime* skelAnime);
+void ResourceMgr_UnregisterSkeleton(SkelAnime* skelAnime);
+void ResourceMgr_ClearSkeletons();
+s32* ResourceMgr_LoadCSByName(const char* path);
+int ResourceMgr_OTRSigCheck(char* imgData);
+uint64_t osGetTime(void);
+uint32_t osGetCount(void);
+uint32_t OTRGetCurrentWidth(void);
+uint32_t OTRGetCurrentHeight(void);
+float OTRGetAspectRatio(void);
+float OTRGetDimensionFromLeftEdge(float v);
+float OTRGetDimensionFromRightEdge(float v);
+int16_t OTRGetRectDimensionFromLeftEdge(float v);
+int16_t OTRGetRectDimensionFromRightEdge(float v);
+int AudioPlayer_Buffered(void);
+int AudioPlayer_GetDesiredBuffered(void);
+void AudioPlayer_Play(const uint8_t* buf, uint32_t len);
+void AudioMgr_CreateNextAudioBuffer(s16* samples, u32 num_samples);
+int Controller_ShouldRumble(size_t slot);
+void Controller_BlockGameInput();
+void Controller_UnblockGameInput();
+void Overlay_DisplayText(float duration, const char* text);
+void Overlay_DisplayText_Seconds(int seconds, const char* text);
+
+void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* replacement);
+void CheckTracker_OnMessageClose();
+
+int32_t GetGIID(uint32_t itemID);
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+uint64_t GetUnixTimestamp();
+#ifdef __cplusplus
+};
+#endif
+
+#endif
diff --git a/mm/2s2h/Extractor/EndianCvt.o b/mm/2s2h/Extractor/EndianCvt.o
new file mode 100644
index 000000000..23c9daa88
--- /dev/null
+++ b/mm/2s2h/Extractor/EndianCvt.o
Binary files differ
diff --git a/mm/2s2h/Extractor/Extract.cpp b/mm/2s2h/Extractor/Extract.cpp
new file mode 100644
index 000000000..ae3e325fb
--- /dev/null
+++ b/mm/2s2h/Extractor/Extract.cpp
@@ -0,0 +1,603 @@
+#ifdef _WIN32
+#include <Windows.h>
+#include <winuser.h>
+#include <shlwapi.h>
+#pragma comment(lib, "Shlwapi.lib")
+#endif
+#include "Extract.h"
+#include "portable-file-dialogs.h"
+#include <Utils/BitConverter.h>
+
+#ifdef unix
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#endif
+
+#ifdef _MSC_VER
+#define BSWAP32 _byteswap_ulong
+#define BSWAP16 _byteswap_ushort
+#elif __has_include(<byteswap.h>)
+#include <byteswap.h>
+#define BSWAP32 bswap_32
+#define BSWAP16 bswap_16
+#else
+#define BSWAP16(value) ((((value)&0xff) << 8) | ((value) >> 8))
+
+#define BSWAP32(value) \
+ (((uint32_t)BSWAP16((uint16_t)((value)&0xffff)) << 16) | (uint32_t)BSWAP16((uint16_t)((value) >> 16)))
+#endif
+
+#if defined(_MSC_VER)
+#define UNREACHABLE __assume(0)
+#elif __llvm__
+#define UNREACHABLE __builtin_assume(0)
+#else
+#define UNREACHABLE __builtin_unreachable();
+#endif
+
+#include <stdlib.h>
+
+#include <SDL2/SDL_messagebox.h>
+
+#include <array>
+#include <fstream>
+#include <filesystem>
+#include <unordered_map>
+#include <random>
+#include <string>
+
+extern "C" uint32_t CRC32C(unsigned char* data, size_t dataSize);
+
+static constexpr uint32_t OOT_PAL_GC = 0x09465AC3;
+static constexpr uint32_t OOT_PAL_MQ = 0x1D4136F3;
+static constexpr uint32_t OOT_PAL_GC_DBG1 = 0x871E1C92; // 03-21-2002 build
+static constexpr uint32_t OOT_PAL_GC_DBG2 = 0x87121EFE; // 03-13-2002 build
+static constexpr uint32_t OOT_PAL_GC_MQ_DBG = 0x917D18F6;
+static constexpr uint32_t OOT_PAL_10 = 0xB044B569;
+static constexpr uint32_t OOT_PAL_11 = 0xB2055FBD;
+static constexpr uint32_t MM_US_10 = 0x5354631C;
+
+static const std::unordered_map<uint32_t, const char*> verMap = {
+ { MM_US_10, "U.S 1.0" },
+ //{ OOT_PAL_GC, "PAL Gamecube" },
+ //{ OOT_PAL_MQ, "PAL MQ" },
+ //{ OOT_PAL_GC_DBG1, "PAL Debug 1" },
+ //{ OOT_PAL_GC_DBG2, "PAL Debug 2" },
+ //{ OOT_PAL_GC_MQ_DBG, "PAL MQ Debug" },
+ //{ OOT_PAL_10, "PAL N64 1.0" },
+ //{ OOT_PAL_11, "PAL N64 1.1" },
+};
+
+// TODO only check the first 54MB of the rom.
+static constexpr std::array<const uint32_t, 10> goodCrcs = {
+
+ //0xfa8c0555, // MQ DBG 64MB (Original overdump)
+ //0x8652ac4c, // MQ DBG 64MB
+ //0x5B8A1EB7, // MQ DBG 64MB (Empty overdump)
+ //0x1f731ffe, // MQ DBG 54MB
+ //0x044b3982, // NMQ DBG 54MB
+ //0xEB15D7B9, // NMQ DBG 64MB
+ //0xDA8E61BF, // GC PAL
+ //0x7A2FAE68, // GC MQ PAL
+ //0xFD9913B1, // N64 PAL 1.0
+ //0xE033FBBA, // N64 PAL 1.1
+};
+
+enum class ButtonId : int {
+ YES,
+ NO,
+ FIND,
+};
+
+
+void Extractor::ShowErrorBox(const char* title, const char* text) {
+#ifdef _WIN32
+ MessageBoxA(nullptr, text, title, MB_OK | MB_ICONERROR);
+#else
+ SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title, text, nullptr);
+#endif
+}
+
+void Extractor::ShowSizeErrorBox() const {
+ std::unique_ptr<char[]> boxBuffer = std::make_unique<char[]>(mCurrentRomPath.size() + 100);
+ snprintf(boxBuffer.get(), mCurrentRomPath.size() + 100,
+ "The rom file %s was not a valid size. Was %zu MB, expecting 32, 54, or 64MB.", mCurrentRomPath.c_str(),
+ mCurRomSize / MB_BASE);
+ ShowErrorBox("Invalid Rom Size", boxBuffer.get());
+}
+
+void Extractor::ShowCrcErrorBox() const {
+ ShowErrorBox("Rom CRC invalid", "Rom CRC did not match the list of known good roms. Please find another.");
+}
+
+int Extractor::ShowRomPickBox(uint32_t verCrc) const {
+ std::unique_ptr<char[]> boxBuffer = std::make_unique<char[]>(mCurrentRomPath.size() + 100);
+ SDL_MessageBoxData boxData = { 0 };
+ SDL_MessageBoxButtonData buttons[3] = { { 0 } };
+ int ret;
+
+ buttons[0].buttonid = 0;
+ buttons[0].text = "Yes";
+ buttons[0].flags = SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
+ buttons[1].buttonid = 1;
+ buttons[1].text = "No";
+ buttons[1].flags = SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT;
+ buttons[2].buttonid = 2;
+ buttons[2].text = "Find ROM";
+ boxData.numbuttons = 3;
+ boxData.flags = SDL_MESSAGEBOX_INFORMATION;
+ boxData.message = boxBuffer.get();
+ boxData.title = "Rom Detected";
+ boxData.window = nullptr;
+
+ boxData.buttons = buttons;
+ snprintf(boxBuffer.get(), mCurrentRomPath.size() + 100,
+ "Rom detected: %s, Header CRC32: %8X. It appears to be: %s. Use this rom?", mCurrentRomPath.c_str(),
+ verCrc, verMap.at(verCrc));
+
+ SDL_ShowMessageBox(&boxData, &ret);
+ return ret;
+}
+
+int Extractor::ShowYesNoBox(const char* title, const char* box) {
+ int ret;
+#ifdef _WIN32
+ ret = MessageBoxA(nullptr, box, title, MB_YESNO | MB_ICONQUESTION);
+#else
+ SDL_MessageBoxData boxData = { 0 };
+ SDL_MessageBoxButtonData buttons[2] = { { 0 } };
+
+ buttons[0].buttonid = IDYES;
+ buttons[0].text = "Yes";
+ buttons[0].flags = SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
+ buttons[1].buttonid = IDNO;
+ buttons[1].text = "No";
+ buttons[1].flags = SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT;
+ boxData.numbuttons = 2;
+ boxData.flags = SDL_MESSAGEBOX_INFORMATION;
+ boxData.message = box;
+ boxData.title = title;
+ boxData.buttons = buttons;
+ SDL_ShowMessageBox(&boxData, &ret);
+#endif
+ return ret;
+}
+
+void Extractor::SetRomInfo(const std::string& path) {
+ mCurrentRomPath = path;
+ mCurRomSize = GetCurRomSize();
+}
+
+void Extractor::FilterRoms(std::vector<std::string>& roms, RomSearchMode searchMode) {
+ std::ifstream inFile;
+ std::vector<std::string>::iterator it = roms.begin();
+
+ while (it != roms.end()) {
+ std::string rom = *it;
+ SetRomInfo(rom);
+
+ // Skip. We will handle rom size errors later on after filtering
+ if (!ValidateRomSize()) {
+ it++;
+ continue;
+ }
+
+ inFile.open(rom, std::ios::in | std::ios::binary);
+ inFile.read((char*)mRomData.get(), mCurRomSize);
+ inFile.clear();
+ inFile.close();
+
+ BitConverter::RomToBigEndian(mRomData.get(), mCurRomSize);
+
+ // Rom doesn't claim to be valid
+ // Game type doesn't match search mode
+ if (!verMap.contains(GetRomVerCrc()) ||
+ (searchMode == RomSearchMode::Vanilla && IsMasterQuest()) ||
+ (searchMode == RomSearchMode::MQ && !IsMasterQuest())) {
+ it = roms.erase(it);
+ continue;
+ }
+
+ it++;
+ }
+}
+
+void Extractor::GetRoms(std::vector<std::string>& roms) {
+#ifdef _WIN32
+ WIN32_FIND_DATAA ffd;
+ HANDLE h = FindFirstFileA(".\\*", &ffd);
+
+ do {
+ if (!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
+ char* ext = PathFindExtensionA(ffd.cFileName);
+
+ // Check for any standard N64 rom file extensions.
+ if ((strcmp(ext, ".z64") == 0) || (strcmp(ext, ".n64") == 0) || (strcmp(ext, ".v64") == 0))
+ roms.push_back(ffd.cFileName);
+ }
+ } while (FindNextFileA(h, &ffd) != 0);
+ // if (h != nullptr) {
+ // CloseHandle(h);
+ //}
+#elif unix
+ // Open the directory of the app.
+ DIR* d = opendir(".");
+ struct dirent* dir;
+
+ if (d != NULL) {
+ // Go through each file in the directory
+ while ((dir = readdir(d)) != NULL) {
+ struct stat path;
+
+ // Check if current entry is not folder
+ stat(dir->d_name, &path);
+ if (S_ISREG(path.st_mode)) {
+
+ // Get the position of the extension character.
+ char* ext = strrchr(dir->d_name, '.');
+ if (ext != NULL && (strcmp(ext, ".z64") == 0 || strcmp(ext, ".n64") == 0 ||
+ strcmp(ext, ".v64") == 0)) {
+ roms.push_back(dir->d_name);
+ }
+ }
+ }
+ }
+ closedir(d);
+#else
+ for (const auto& file : std::filesystem::directory_iterator("./")) {
+ if (file.is_directory())
+ continue;
+ if ((file.path().extension() == ".n64") || (file.path().extension() == ".z64") ||
+ (file.path().extension() == ".v64")) {
+ roms.push_back((file.path()));
+ }
+ }
+#endif
+}
+
+bool Extractor::GetRomPathFromBox() {
+#ifdef _WIN32
+ OPENFILENAMEA box = { 0 };
+ char nameBuffer[512];
+ nameBuffer[0] = 0;
+
+ box.lStructSize = sizeof(box);
+ box.lpstrFile = nameBuffer;
+ box.nMaxFile = sizeof(nameBuffer) / sizeof(nameBuffer[0]);
+ box.lpstrTitle = "Open Rom";
+ box.Flags = OFN_NOCHANGEDIR | OFN_ENABLESIZING | OFN_FILEMUSTEXIST | OFN_LONGNAMES | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
+ box.lpstrFilter = "N64 Roms\0*.z64;*.v64;*.n64\0\0";
+ if (!GetOpenFileNameA(&box)) {
+ DWORD err = CommDlgExtendedError();
+ // GetOpenFileName will return 0 but no error is set if the user just closes the box.
+ if (err != 0) {
+ const char* errStr = nullptr;
+ switch (err) {
+ case FNERR_BUFFERTOOSMALL:
+ errStr = "Path buffer too small. Move file closer to root of your drive";
+ break;
+ case FNERR_INVALIDFILENAME:
+ errStr = "File name for rom provided is invalid.";
+ break;
+ case FNERR_SUBCLASSFAILURE:
+ errStr = "Failed to open a filebox because there is not enough RAM to do so.";
+ break;
+ }
+ MessageBoxA(nullptr, "Box Error", errStr, MB_OK | MB_ICONERROR);
+ return false;
+ }
+ }
+ // The box was closed without something being selected.
+ if (nameBuffer[0] == 0) {
+ return false;
+ }
+ mCurrentRomPath = nameBuffer;
+ #else
+ auto selection = pfd::open_file("Select a file", ".", { "N64 Roms", "*.z64 *.n64 *.v64" }).result();
+
+ if (selection.empty()) {
+ return false;
+ }
+
+ mCurrentRomPath = selection[0];
+ #endif
+ mCurRomSize = GetCurRomSize();
+ return true;
+}
+
+uint32_t Extractor::GetRomVerCrc() const {
+ return BSWAP32(((uint32_t*)mRomData.get())[4]);
+}
+
+size_t Extractor::GetCurRomSize() const {
+ return std::filesystem::file_size(mCurrentRomPath);
+}
+
+bool Extractor::ValidateAndFixRom() {
+ // The MQ debug rom sometimes has the header patched to look like a US rom. Change it back
+ if (GetRomVerCrc() == OOT_PAL_GC_MQ_DBG) {
+ mRomData[0x3E] = 'P';
+ }
+
+ const uint32_t actualCrc = CRC32C(mRomData.get(), mCurRomSize);
+
+ for (const uint32_t crc : goodCrcs) {
+ if (actualCrc == crc) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Extractor::ValidateRomSize() const {
+ if (mCurRomSize != MB32 && mCurRomSize != MB54 && mCurRomSize != MB64) {
+ return false;
+ }
+ return true;
+}
+
+bool Extractor::ValidateRom(bool skipCrcTextBox) {
+ if (!ValidateRomSize()) {
+ ShowSizeErrorBox();
+ return false;
+ }
+ if (!ValidateAndFixRom()) {
+ if (!skipCrcTextBox) {
+ ShowCrcErrorBox();
+ }
+ return false;
+ }
+ return true;
+}
+
+bool Extractor::ManuallySearchForRom() {
+ std::ifstream inFile;
+
+ if (!GetRomPathFromBox()) {
+ ShowErrorBox("No rom selected", "No Rom selected. Exiting");
+ return false;
+ }
+
+ inFile.open(mCurrentRomPath, std::ios::in | std::ios::binary);
+
+ if (!inFile.is_open()) {
+ return false; // TODO Handle error
+ }
+
+ inFile.read((char*)mRomData.get(), mCurRomSize);
+ inFile.close();
+ BitConverter::RomToBigEndian(mRomData.get(), mCurRomSize);
+
+ if (!ValidateRom()) {
+ return false;
+ }
+
+ return true;
+}
+
+bool Extractor::ManuallySearchForRomMatchingType(RomSearchMode searchMode) {
+ if (!ManuallySearchForRom()) {
+ return false;
+ }
+
+ char msgBuf[150];
+ snprintf(msgBuf, 150, "The selected rom does not match the expected game type\nExpected type: %s.\n\nDo you want to search again?",
+ searchMode == RomSearchMode::MQ ? "Master Quest" : "Vanilla");
+
+ while ((searchMode == RomSearchMode::Vanilla && IsMasterQuest()) ||
+ (searchMode == RomSearchMode::MQ && !IsMasterQuest())) {
+ int ret = ShowYesNoBox("Wrong Game Type", msgBuf);
+ switch (ret) {
+ case IDYES:
+ if (!ManuallySearchForRom()) {
+ return false;
+ }
+ continue;
+ case IDNO:
+ return false;
+ default:
+ UNREACHABLE;
+ break;
+ }
+ }
+
+ return true;
+}
+
+bool Extractor::Run(RomSearchMode searchMode) {
+ std::vector<std::string> roms;
+ std::ifstream inFile;
+
+ GetRoms(roms);
+ FilterRoms(roms, searchMode);
+
+ if (roms.empty()) {
+ int ret = ShowYesNoBox("No roms found", "No roms found. Look for one?");
+
+ switch (ret) {
+ case IDYES:
+ if (!ManuallySearchForRomMatchingType(searchMode)) {
+ return false;
+ }
+ break;
+ case IDNO:
+ ShowErrorBox("No rom selected", "No rom selected. Exiting");
+ return false;
+ default:
+ UNREACHABLE;
+ break;
+ }
+ }
+
+ for (const auto& rom : roms) {
+ SetRomInfo(rom);
+
+ if (!ValidateRomSize()) {
+ ShowSizeErrorBox();
+ continue;
+ }
+
+ inFile.open(rom, std::ios::in | std::ios::binary);
+ inFile.read((char*)mRomData.get(), mCurRomSize);
+ inFile.clear();
+ inFile.close();
+ BitConverter::RomToBigEndian(mRomData.get(), mCurRomSize);
+
+ int option = ShowRomPickBox(GetRomVerCrc());
+
+ if (option == (int)ButtonId::YES) {
+ if (!ValidateRom(true)) {
+ if (rom == roms.back()) {
+ ShowCrcErrorBox();
+ } else {
+ ShowErrorBox("Rom CRC invalid",
+ "Rom CRC did not match the list of known good roms. Trying the next one...");
+ }
+ continue;
+ }
+ break;
+ } else if (option == (int)ButtonId::FIND) {
+ if (!ManuallySearchForRomMatchingType(searchMode)) {
+ return false;
+ }
+ break;
+ } else if (option == (int)ButtonId::NO) {
+ if (rom == roms.back()) {
+ ShowErrorBox("No rom provided", "No rom provided. Exiting");
+ return false;
+ }
+ continue;
+ }
+ break;
+ }
+ return true;
+}
+
+bool Extractor::IsMasterQuest() const {
+ switch (GetRomVerCrc()) {
+ case OOT_PAL_MQ:
+ case OOT_PAL_GC_MQ_DBG:
+ return true;
+ case OOT_PAL_10:
+ case OOT_PAL_11:
+ case OOT_PAL_GC:
+ case OOT_PAL_GC_DBG1:
+ return false;
+ default:
+ UNREACHABLE;
+ }
+}
+
+const char* Extractor::GetZapdVerStr() const {
+ switch (GetRomVerCrc()) {
+ case OOT_PAL_GC:
+ return "GC_NMQ_PAL_F";
+ case OOT_PAL_MQ:
+ return "GC_MQ_PAL_F";
+ case OOT_PAL_GC_DBG1:
+ return "GC_NMQ_D";
+ case OOT_PAL_GC_MQ_DBG:
+ return "GC_MQ_D";
+ case OOT_PAL_10:
+ return "N64_PAL_10";
+ case OOT_PAL_11:
+ return "N64_PAL_11";
+ default:
+ // We should never be in a state where this path happens.
+ UNREACHABLE;
+ break;
+ }
+}
+
+std::string Extractor::Mkdtemp() {
+ std::string temp_dir = std::filesystem::temp_directory_path().string();
+
+ // create 6 random alphanumeric characters
+ static const char charset[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+ std::random_device rd;
+ std::mt19937 gen(rd());
+ std::uniform_int_distribution<> dist(0, sizeof(charset) - 1);
+
+ char randchr[7];
+ for (int i = 0; i < 6; i++) {
+ randchr[i] = charset[dist(gen)];
+ }
+ randchr[6] = '\0';
+
+ std::string tmppath = temp_dir + "/extractor-" + randchr;
+ std::filesystem::create_directory(tmppath);
+ return tmppath;
+}
+
+extern "C" int zapd_main(int argc, char** argv);
+
+bool Extractor::CallZapd(std::string installPath, std::string exportdir) {
+ constexpr int argc = 16;
+ char xmlPath[1024];
+ char confPath[1024];
+ std::array<const char*, argc> argv;
+ const char* version = GetZapdVerStr();
+ const char* otrFile = IsMasterQuest() ? "oot-mq.otr" : "oot.otr";
+
+ std::string romPath = std::filesystem::absolute(mCurrentRomPath).string();
+ installPath = std::filesystem::absolute(installPath).string();
+ exportdir = std::filesystem::absolute(exportdir).string();
+ // Work this out in the temporary folder
+ std::string tempdir = Mkdtemp();
+ std::string curdir = std::filesystem::current_path().string();
+#ifdef _WIN32
+ std::filesystem::copy(installPath + "/assets", tempdir + "/assets",
+ std::filesystem::copy_options::recursive | std::filesystem::copy_options::update_existing);
+#else
+ std::filesystem::create_symlink(installPath + "/assets", tempdir + "/assets");
+#endif
+
+ std::filesystem::current_path(tempdir);
+
+ snprintf(xmlPath, 1024, "assets/extractor/xmls/%s", version);
+ snprintf(confPath, 1024, "assets/extractor/Config_%s.xml", version);
+
+ argv[0] = "ZAPD";
+ argv[1] = "ed";
+ argv[2] = "-i";
+ argv[3] = xmlPath;
+ argv[4] = "-b";
+ argv[5] = romPath.c_str();
+ argv[6] = "-fl";
+ argv[7] = "assets/extractor/filelists";
+ argv[8] = "-gsf";
+ argv[9] = "1";
+ argv[10] = "-rconf";
+ argv[11] = confPath;
+ argv[12] = "-se";
+ argv[13] = "OTR";
+ argv[14] = "--otrfile";
+ argv[15] = otrFile;
+
+#ifdef _WIN32
+ // Grab a handle to the command window.
+ HWND cmdWindow = GetConsoleWindow();
+
+ // Normally the command window is hidden. We want the window to be shown here so the user can see the progess of the extraction.
+ ShowWindow(cmdWindow, SW_SHOW);
+ SetWindowPos(cmdWindow, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
+#endif
+
+ zapd_main(argc, (char**)argv.data());
+
+#ifdef _WIN32
+ // Hide the command window again.
+ ShowWindow(cmdWindow, SW_HIDE);
+#endif
+
+ std::filesystem::copy(otrFile, exportdir + "/" + otrFile, std::filesystem::copy_options::overwrite_existing);
+
+ // Go back to where this game was executed from
+ std::filesystem::current_path(curdir);
+ std::filesystem::remove_all(tempdir);
+
+ return 0;
+}
+
diff --git a/mm/2s2h/Extractor/Extract.h b/mm/2s2h/Extractor/Extract.h
new file mode 100644
index 000000000..6c9b4a078
--- /dev/null
+++ b/mm/2s2h/Extractor/Extract.h
@@ -0,0 +1,64 @@
+#ifndef EXTRACT_H
+#define EXTRACT_H
+
+#include <stdint.h>
+#include <string>
+#include <memory>
+#include <vector>
+
+// Values come from windows.h
+#ifndef IDYES
+#define IDYES 6
+#endif
+#ifndef IDNO
+#define IDNO 7
+#endif
+
+static constexpr size_t MB_BASE = 1024 * 1024;
+static constexpr size_t MB32 = 32 * MB_BASE;
+static constexpr size_t MB54 = 54 * MB_BASE;
+static constexpr size_t MB64 = 64 * MB_BASE;
+
+enum class RomSearchMode {
+ Both = 0,
+ Vanilla = 1,
+ MQ = 2,
+};
+
+class Extractor {
+ std::unique_ptr<unsigned char[]> mRomData = std::make_unique<unsigned char[]>(MB64);
+ std::string mCurrentRomPath;
+ size_t mCurRomSize = 0;
+
+ bool GetRomPathFromBox();
+
+ uint32_t GetRomVerCrc() const;
+ size_t GetCurRomSize() const;
+ bool ValidateAndFixRom();
+ bool ValidateRomSize() const;
+
+ bool ValidateRom(bool skipCrcBox = false);
+ const char* GetZapdVerStr() const;
+
+ void SetRomInfo(const std::string& path);
+
+ void FilterRoms(std::vector<std::string>& roms, RomSearchMode searchMode);
+ void GetRoms(std::vector<std::string>& roms);
+ void ShowSizeErrorBox() const;
+ void ShowCrcErrorBox() const;
+ int ShowRomPickBox(uint32_t verCrc) const;
+ bool ManuallySearchForRom();
+ bool ManuallySearchForRomMatchingType(RomSearchMode searchMode);
+
+ public:
+ //TODO create some kind of abstraction for message boxes.
+ static int ShowYesNoBox(const char* title, const char* text);
+ static void ShowErrorBox(const char* title, const char* text);
+ bool IsMasterQuest() const;
+
+ bool Run(RomSearchMode searchMode = RomSearchMode::Both);
+ bool CallZapd(std::string installPath, std::string exportdir);
+ const char* GetZapdStr();
+ std::string Mkdtemp();
+};
+#endif
diff --git a/mm/2s2h/Extractor/FastCrc32C.c b/mm/2s2h/Extractor/FastCrc32C.c
new file mode 100644
index 000000000..d88b04beb
--- /dev/null
+++ b/mm/2s2h/Extractor/FastCrc32C.c
@@ -0,0 +1,144 @@
+#include <stdint.h>
+#include <stddef.h>
+
+// Force the compiler to assume we have support for the CRC32 intrinsic. We will check for our selves later.
+// Clang will define both __llvm__ and __GNUC__ but GCC will only define __GNUC__. So we need to check for __llvm__ first.
+#if ((defined(__llvm__) && (defined(__x86_64__) || defined(__i386__))))
+#pragma clang attribute push(__attribute__((target("crc32"))), apply_to = function)
+#elif ((defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))))
+// GCC Only lets you enable all of sse4.2 so we will for just this file and reset it at the end.
+#pragma GCC push_options
+#pragma GCC target("sse4.2")
+#endif
+
+// Include headers for the CRC32 intrinsic and cpuid instruction on windows. No need to do any other checks because it assumes the target will support CRC32
+#ifdef _WIN32
+#include <immintrin.h>
+#include <intrin.h>
+// Same as above but these platforms use slightly different headers
+#elif ((defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))))
+#include <nmmintrin.h>
+#include <cpuid.h>
+#elif defined(__aarch64__) && defined(__ARM_FEATURE_CRC32)
+// Nothing cause its a compiler builtin
+#else
+#define NO_CRC_INTRIN
+#endif
+
+#if defined(__aarch64__) && defined(__ARM_FEATURE_CRC32)
+#define INTRIN_CRC32_64(crc, value) __asm__("crc32cx %w[c], %w[c], %x[v]" : [c] "+r"(crc) : [v] "r"(value))
+#define INTRIN_CRC32_32(crc, value) __asm__("crc32cw %w[c], %w[c], %w[v]" : [c] "+r"(crc) : [v] "r"(value))
+#define INTRIN_CRC32_16(crc, value) __asm__("crc32ch %w[c], %w[c], %w[v]" : [c] "+r"(crc) : [v] "r"(value))
+#define INTRIN_CRC32_8(crc, value) __asm__("crc32cb %w[c], %w[c], %w[v]" : [c] "+r"(crc) : [v] "r"(value))
+#elif defined(__GNUC__) || defined(_MSC_VER)
+#define INTRIN_CRC32_64(crc, data) crc = _mm_crc32_u64(crc, data)
+#define INTRIN_CRC32_32(crc, data) crc = _mm_crc32_u32(crc, data)
+#define INTRIN_CRC32_16(crc, data) crc = _mm_crc32_u16(crc, data)
+#define INTRIN_CRC32_8(crc, data) crc = _mm_crc32_u8(crc, data)
+#endif
+
+static const uint32_t crc32Table[256] = {
+ 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, 0x8AD958CFL,
+ 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, 0x105EC76FL, 0xE235446CL,
+ 0xF165B798L, 0x030E349BL, 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L,
+ 0x89D76C54L, 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL, 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL,
+ 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, 0x6DFE410EL,
+ 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L, 0xF779DEAEL, 0x05125DADL,
+ 0x1642AE59L, 0xE4292D5AL, 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L,
+ 0x6EF07595L, 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L,
+ 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L, 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L, 0x5125DAD3L,
+ 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, 0xDBFC821CL, 0x2997011FL,
+ 0x3AC7F2EBL, 0xC8AC71E8L, 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L, 0x61C69362L, 0x93AD1061L, 0x80FDE395L,
+ 0x72966096L, 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L,
+ 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L, 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L, 0xB602C312L,
+ 0x44694011L, 0x5739B3E5L, 0xA55230E6L, 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, 0x3CDB9BDDL, 0xCEB018DEL,
+ 0xDDE0EB2AL, 0x2F8B6829L, 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL, 0x456CAC67L, 0xB7072F64L, 0xA457DC90L,
+ 0x563C5F93L, 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L, 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL,
+ 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL, 0x1871A4D8L,
+ 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL, 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, 0xA24BB5A6L, 0x502036A5L,
+ 0x4370C551L, 0xB11B4652L, 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL,
+ 0x3BC21E9DL, 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L, 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
+ 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, 0xFF56BD19L,
+ 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL, 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L, 0x0417B1DBL, 0xF67C32D8L,
+ 0xE52CC12CL, 0x1747422FL, 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L,
+ 0x9D9E1AE0L, 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL, 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L,
+ 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, 0xE330A81AL,
+ 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L, 0x69E9F0D5L, 0x9B8273D6L,
+ 0x88D28022L, 0x7AB90321L, 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, 0xF36E6F75L, 0x0105EC76L, 0x12551F82L,
+ 0xE03E9C81L, 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL,
+ 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L
+};
+// On platforms that we know will never support a crc32 instruction (such as the WiiU) we will skip compiling this function in.
+#ifndef NO_CRC_INTRIN
+
+static uint32_t CRC32IntrinImpl(unsigned char* data, size_t dataSize) {
+ uint32_t ret = 0xFFFFFFFF;
+ int64_t sizeSigned = dataSize;
+// Only 64bit platforms support doing a CRC32 operation on a 64bit value
+#if defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__)
+ while ((sizeSigned -= sizeof(uint64_t)) >= 0) {
+ INTRIN_CRC32_64(ret, *(uint64_t*)data);
+ data += sizeof(uint64_t);
+ }
+
+ if (sizeSigned & sizeof(uint32_t)) {
+ INTRIN_CRC32_32(ret, *(uint32_t*)data);
+
+ data += sizeof(uint32_t);
+ }
+// On 32 bit we can only do 32bit operations
+#elif defined(_M_IX86) || defined(__i386__)
+ while ((sizeSigned -= sizeof(uint32_t)) >= 0) {
+ INTRIN_CRC32_32(ret, *(uint32_t*)data);
+ data += sizeof(uint32_t);
+ }
+#endif
+ if (sizeSigned & sizeof(uint16_t)) {
+ INTRIN_CRC32_16(ret, *(uint16_t*)data);
+ data += sizeof(uint16_t);
+ }
+
+ if (sizeSigned & sizeof(uint8_t)) {
+ INTRIN_CRC32_8(ret, *data);
+ }
+
+ return ~ret;
+}
+#endif
+
+static uint32_t CRC32TableImpl(unsigned char* data, size_t dataSize) {
+ const uint8_t* p = data;
+ uint32_t crc = 0xFFFFFFFF;
+
+ while (dataSize--)
+ crc = crc32Table[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+
+ return ~crc;
+}
+
+uint32_t CRC32C(unsigned char* data, size_t dataSize) {
+#ifndef NO_CRC_INTRIN
+ // Test to make sure the CPU supports the CRC32 intrinsic
+ unsigned int cpuidData[4];
+#ifdef _WIN32
+ __cpuid(cpuidData, 1);
+#elif __APPLE__ || (defined(__aarch64__) && defined(__ARM_FEATURE_CRC32))
+// Every Mac that supports SoH should support this instruction. Also check for ARM64 at the same time
+ return CRC32IntrinImpl(data, dataSize);
+#else
+ __get_cpuid(1, &cpuidData[0], &cpuidData[1], &cpuidData[2], &cpuidData[3]);
+#endif
+
+ if (cpuidData[2] & (1 << 20)) { // bit_SSE4_2
+ return CRC32IntrinImpl(data, dataSize);
+ }
+#endif // NO_CRC_INTRIN
+ return CRC32TableImpl(data, dataSize);
+}
+
+#if ((defined(__llvm__) && (defined(__x86_64__) || defined(__i386__))))
+#pragma clang attribute pop
+#elif ((defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))))
+#pragma GCC pop_options
+#else
+#endif
diff --git a/mm/2s2h/Extractor/portable-file-dialogs.h b/mm/2s2h/Extractor/portable-file-dialogs.h
new file mode 100644
index 000000000..1fc79a291
--- /dev/null
+++ b/mm/2s2h/Extractor/portable-file-dialogs.h
@@ -0,0 +1,1887 @@
+//
+// Portable File Dialogs
+//
+// Copyright © 2018–2022 Sam Hocevar <sam@hocevar.net>
+//
+// This library is free software. It comes without any warranty, to
+// the extent permitted by applicable law. You can redistribute it
+// and/or modify it under the terms of the Do What the Fuck You Want
+// to Public License, Version 2, as published by the WTFPL Task Force.
+// See http://www.wtfpl.net/ for more details.
+//
+
+#pragma once
+
+#if _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN 1
+#endif
+#include <windows.h>
+#include <commdlg.h>
+#include <shlobj.h>
+#include <shobjidl.h> // IFileDialog
+#include <shellapi.h>
+#include <strsafe.h>
+#include <future> // std::async
+#include <userenv.h> // GetUserProfileDirectory()
+
+#elif __EMSCRIPTEN__
+#include <emscripten.h>
+
+#else
+#ifndef _POSIX_C_SOURCE
+# define _POSIX_C_SOURCE 2 // for popen()
+#endif
+#ifdef __APPLE__
+# ifndef _DARWIN_C_SOURCE
+# define _DARWIN_C_SOURCE
+# endif
+#endif
+#include <cstdio> // popen()
+#include <cstdlib> // std::getenv()
+#include <fcntl.h> // fcntl()
+#include <unistd.h> // read(), pipe(), dup2(), getuid()
+#include <csignal> // ::kill, std::signal
+#include <sys/stat.h> // stat()
+#include <sys/wait.h> // waitpid()
+#include <pwd.h> // getpwnam()
+#endif
+
+#include <string> // std::string
+#include <memory> // std::shared_ptr
+#include <iostream> // std::ostream
+#include <map> // std::map
+#include <set> // std::set
+#include <regex> // std::regex
+#include <thread> // std::mutex, std::this_thread
+#include <chrono> // std::chrono
+
+// Versions of mingw64 g++ up to 9.3.0 do not have a complete IFileDialog
+#ifndef PFD_HAS_IFILEDIALOG
+# define PFD_HAS_IFILEDIALOG 1
+# if (defined __MINGW64__ || defined __MINGW32__) && defined __GXX_ABI_VERSION
+# if __GXX_ABI_VERSION <= 1013
+# undef PFD_HAS_IFILEDIALOG
+# define PFD_HAS_IFILEDIALOG 0
+# endif
+# endif
+#endif
+
+namespace pfd
+{
+
+enum class button
+{
+ cancel = -1,
+ ok,
+ yes,
+ no,
+ abort,
+ retry,
+ ignore,
+};
+
+enum class choice
+{
+ ok = 0,
+ ok_cancel,
+ yes_no,
+ yes_no_cancel,
+ retry_cancel,
+ abort_retry_ignore,
+};
+
+enum class icon
+{
+ info = 0,
+ warning,
+ error,
+ question,
+};
+
+// Additional option flags for various dialog constructors
+enum class opt : uint8_t
+{
+ none = 0,
+ // For file open, allow multiselect.
+ multiselect = 0x1,
+ // For file save, force overwrite and disable the confirmation dialog.
+ force_overwrite = 0x2,
+ // For folder select, force path to be the provided argument instead
+ // of the last opened directory, which is the Microsoft-recommended,
+ // user-friendly behaviour.
+ force_path = 0x4,
+};
+
+inline opt operator |(opt a, opt b) { return opt(uint8_t(a) | uint8_t(b)); }
+inline bool operator &(opt a, opt b) { return bool(uint8_t(a) & uint8_t(b)); }
+
+// The settings class, only exposing to the user a way to set verbose mode
+// and to force a rescan of installed desktop helpers (zenity, kdialog…).
+class settings
+{
+public:
+ static bool available();
+
+ static void verbose(bool value);
+ static void rescan();
+
+protected:
+ explicit settings(bool resync = false);
+
+ bool check_program(std::string const &program);
+
+ inline bool is_osascript() const;
+ inline bool is_zenity() const;
+ inline bool is_kdialog() const;
+
+ enum class flag
+ {
+ is_scanned = 0,
+ is_verbose,
+
+ has_zenity,
+ has_matedialog,
+ has_qarma,
+ has_kdialog,
+ is_vista,
+
+ max_flag,
+ };
+
+ // Static array of flags for internal state
+ bool const &flags(flag in_flag) const;
+
+ // Non-const getter for the static array of flags
+ bool &flags(flag in_flag);
+};
+
+// Internal classes, not to be used by client applications
+namespace internal
+{
+
+// Process wait timeout, in milliseconds
+static int const default_wait_timeout = 20;
+
+class executor
+{
+ friend class dialog;
+
+public:
+ // High level function to get the result of a command
+ std::string result(int *exit_code = nullptr);
+
+ // High level function to abort
+ bool kill();
+
+#if _WIN32
+ void start_func(std::function<std::string(int *)> const &fun);
+ static BOOL CALLBACK enum_windows_callback(HWND hwnd, LPARAM lParam);
+#elif __EMSCRIPTEN__
+ void start(int exit_code);
+#else
+ void start_process(std::vector<std::string> const &command);
+#endif
+
+ ~executor();
+
+protected:
+ bool ready(int timeout = default_wait_timeout);
+ void stop();
+
+private:
+ bool m_running = false;
+ std::string m_stdout;
+ int m_exit_code = -1;
+#if _WIN32
+ std::future<std::string> m_future;
+ std::set<HWND> m_windows;
+ std::condition_variable m_cond;
+ std::mutex m_mutex;
+ DWORD m_tid;
+#elif __EMSCRIPTEN__ || __NX__
+ // FIXME: do something
+#else
+ pid_t m_pid = 0;
+ int m_fd = -1;
+#endif
+};
+
+class platform
+{
+protected:
+#if _WIN32
+ // Helper class around LoadLibraryA() and GetProcAddress() with some safety
+ class dll
+ {
+ public:
+ dll(std::string const &name);
+ ~dll();
+
+ template<typename T> class proc
+ {
+ public:
+ proc(dll const &lib, std::string const &sym)
+ : m_proc(reinterpret_cast<T *>((void *)::GetProcAddress(lib.handle, sym.c_str())))
+ {}
+
+ operator bool() const { return m_proc != nullptr; }
+ operator T *() const { return m_proc; }
+
+ private:
+ T *m_proc;
+ };
+
+ private:
+ HMODULE handle;
+ };
+
+ // Helper class around CoInitialize() and CoUnInitialize()
+ class ole32_dll : public dll
+ {
+ public:
+ ole32_dll();
+ ~ole32_dll();
+ bool is_initialized();
+
+ private:
+ HRESULT m_state;
+ };
+
+ // Helper class around CreateActCtx() and ActivateActCtx()
+ class new_style_context
+ {
+ public:
+ new_style_context();
+ ~new_style_context();
+
+ private:
+ HANDLE create();
+ ULONG_PTR m_cookie = 0;
+ };
+#endif
+};
+
+class dialog : protected settings, protected platform
+{
+public:
+ bool ready(int timeout = default_wait_timeout) const;
+ bool kill() const;
+
+protected:
+ explicit dialog();
+
+ std::vector<std::string> desktop_helper() const;
+ static std::string buttons_to_name(choice _choice);
+ static std::string get_icon_name(icon _icon);
+
+ std::string powershell_quote(std::string const &str) const;
+ std::string osascript_quote(std::string const &str) const;
+ std::string shell_quote(std::string const &str) const;
+
+ // Keep handle to executing command
+ std::shared_ptr<executor> m_async;
+};
+
+class file_dialog : public dialog
+{
+protected:
+ enum type
+ {
+ open,
+ save,
+ folder,
+ };
+
+ file_dialog(type in_type,
+ std::string const &title,
+ std::string const &default_path = "",
+ std::vector<std::string> const &filters = {},
+ opt options = opt::none);
+
+protected:
+ std::string string_result();
+ std::vector<std::string> vector_result();
+
+#if _WIN32
+ static int CALLBACK bffcallback(HWND hwnd, UINT uMsg, LPARAM, LPARAM pData);
+#if PFD_HAS_IFILEDIALOG
+ std::string select_folder_vista(IFileDialog *ifd, bool force_path);
+#endif
+
+ std::wstring m_wtitle;
+ std::wstring m_wdefault_path;
+
+ std::vector<std::string> m_vector_result;
+#endif
+};
+
+} // namespace internal
+
+//
+// The path class provides some platform-specific path constants
+//
+
+class path : protected internal::platform
+{
+public:
+ static std::string home();
+ static std::string separator();
+};
+
+//
+// The notify widget
+//
+
+class notify : public internal::dialog
+{
+public:
+ notify(std::string const &title,
+ std::string const &message,
+ icon _icon = icon::info);
+};
+
+//
+// The message widget
+//
+
+class message : public internal::dialog
+{
+public:
+ message(std::string const &title,
+ std::string const &text,
+ choice _choice = choice::ok_cancel,
+ icon _icon = icon::info);
+
+ button result();
+
+private:
+ // Some extra logic to map the exit code to button number
+ std::map<int, button> m_mappings;
+};
+
+//
+// The open_file, save_file, and open_folder widgets
+//
+
+class open_file : public internal::file_dialog
+{
+public:
+ open_file(std::string const &title,
+ std::string const &default_path = "",
+ std::vector<std::string> const &filters = { "All Files", "*" },
+ opt options = opt::none);
+
+#if defined(__has_cpp_attribute)
+#if __has_cpp_attribute(deprecated)
+ // Backwards compatibility
+ [[deprecated("Use pfd::opt::multiselect instead of allow_multiselect")]]
+#endif
+#endif
+ open_file(std::string const &title,
+ std::string const &default_path,
+ std::vector<std::string> const &filters,
+ bool allow_multiselect);
+
+ std::vector<std::string> result();
+};
+
+class save_file : public internal::file_dialog
+{
+public:
+ save_file(std::string const &title,
+ std::string const &default_path = "",
+ std::vector<std::string> const &filters = { "All Files", "*" },
+ opt options = opt::none);
+
+#if defined(__has_cpp_attribute)
+#if __has_cpp_attribute(deprecated)
+ // Backwards compatibility
+ [[deprecated("Use pfd::opt::force_overwrite instead of confirm_overwrite")]]
+#endif
+#endif
+ save_file(std::string const &title,
+ std::string const &default_path,
+ std::vector<std::string> const &filters,
+ bool confirm_overwrite);
+
+ std::string result();
+};
+
+class select_folder : public internal::file_dialog
+{
+public:
+ select_folder(std::string const &title,
+ std::string const &default_path = "",
+ opt options = opt::none);
+
+ std::string result();
+};
+
+//
+// Below this are all the method implementations. You may choose to define the
+// macro PFD_SKIP_IMPLEMENTATION everywhere before including this header except
+// in one place. This may reduce compilation times.
+//
+
+#if !defined PFD_SKIP_IMPLEMENTATION
+
+// internal free functions implementations
+
+namespace internal
+{
+
+#if _WIN32
+static inline std::wstring str2wstr(std::string const &str)
+{
+ int len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.size(), nullptr, 0);
+ std::wstring ret(len, '\0');
+ MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.size(), (LPWSTR)ret.data(), (int)ret.size());
+ return ret;
+}
+
+static inline std::string wstr2str(std::wstring const &str)
+{
+ int len = WideCharToMultiByte(CP_UTF8, 0, str.c_str(), (int)str.size(), nullptr, 0, nullptr, nullptr);
+ std::string ret(len, '\0');
+ WideCharToMultiByte(CP_UTF8, 0, str.c_str(), (int)str.size(), (LPSTR)ret.data(), (int)ret.size(), nullptr, nullptr);
+ return ret;
+}
+
+static inline bool is_vista()
+{
+ OSVERSIONINFOEXW osvi;
+ memset(&osvi, 0, sizeof(osvi));
+ DWORDLONG const mask = VerSetConditionMask(
+ VerSetConditionMask(
+ VerSetConditionMask(
+ 0, VER_MAJORVERSION, VER_GREATER_EQUAL),
+ VER_MINORVERSION, VER_GREATER_EQUAL),
+ VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
+ osvi.dwOSVersionInfoSize = sizeof(osvi);
+ osvi.dwMajorVersion = HIBYTE(_WIN32_WINNT_VISTA);
+ osvi.dwMinorVersion = LOBYTE(_WIN32_WINNT_VISTA);
+ osvi.wServicePackMajor = 0;
+
+ return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, mask) != FALSE;
+}
+#endif
+
+// This is necessary until C++20 which will have std::string::ends_with() etc.
+
+static inline bool ends_with(std::string const &str, std::string const &suffix)
+{
+ return suffix.size() <= str.size() &&
+ str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
+}
+
+static inline bool starts_with(std::string const &str, std::string const &prefix)
+{
+ return prefix.size() <= str.size() &&
+ str.compare(0, prefix.size(), prefix) == 0;
+}
+
+// This is necessary until C++17 which will have std::filesystem::is_directory
+
+static inline bool is_directory(std::string const &path)
+{
+#if _WIN32
+ auto attr = GetFileAttributesA(path.c_str());
+ return attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY);
+#elif __EMSCRIPTEN__
+ // TODO
+ return false;
+#else
+ struct stat s;
+ return stat(path.c_str(), &s) == 0 && S_ISDIR(s.st_mode);
+#endif
+}
+
+// This is necessary because getenv is not thread-safe
+
+static inline std::string getenv(std::string const &str)
+{
+#if _MSC_VER
+ char *buf = nullptr;
+ size_t size = 0;
+ if (_dupenv_s(&buf, &size, str.c_str()) == 0 && buf)
+ {
+ std::string ret(buf);
+ free(buf);
+ return ret;
+ }
+ return "";
+#else
+ auto buf = std::getenv(str.c_str());
+ return buf ? buf : "";
+#endif
+}
+
+} // namespace internal
+
+// settings implementation
+
+inline settings::settings(bool resync)
+{
+ flags(flag::is_scanned) &= !resync;
+
+ if (flags(flag::is_scanned))
+ return;
+
+ auto pfd_verbose = internal::getenv("PFD_VERBOSE");
+ auto match_no = std::regex("(|0|no|false)", std::regex_constants::icase);
+ if (!std::regex_match(pfd_verbose, match_no))
+ flags(flag::is_verbose) = true;
+
+#if _WIN32
+ flags(flag::is_vista) = internal::is_vista();
+#elif !__APPLE__
+ flags(flag::has_zenity) = check_program("zenity");
+ flags(flag::has_matedialog) = check_program("matedialog");
+ flags(flag::has_qarma) = check_program("qarma");
+ flags(flag::has_kdialog) = check_program("kdialog");
+
+ // If multiple helpers are available, try to default to the best one
+ if (flags(flag::has_zenity) && flags(flag::has_kdialog))
+ {
+ auto desktop_name = internal::getenv("XDG_SESSION_DESKTOP");
+ if (desktop_name == std::string("gnome"))
+ flags(flag::has_kdialog) = false;
+ else if (desktop_name == std::string("KDE"))
+ flags(flag::has_zenity) = false;
+ }
+#endif
+
+ flags(flag::is_scanned) = true;
+}
+
+inline bool settings::available()
+{
+#if _WIN32
+ return true;
+#elif __APPLE__
+ return true;
+#elif __EMSCRIPTEN__
+ // FIXME: Return true after implementation is complete.
+ return false;
+#else
+ settings tmp;
+ return tmp.flags(flag::has_zenity) ||
+ tmp.flags(flag::has_matedialog) ||
+ tmp.flags(flag::has_qarma) ||
+ tmp.flags(flag::has_kdialog);
+#endif
+}
+
+inline void settings::verbose(bool value)
+{
+ settings().flags(flag::is_verbose) = value;
+}
+
+inline void settings::rescan()
+{
+ settings(/* resync = */ true);
+}
+
+// Check whether a program is present using “which”.
+inline bool settings::check_program(std::string const &program)
+{
+#if _WIN32
+ (void)program;
+ return false;
+#elif __EMSCRIPTEN__
+ (void)program;
+ return false;
+#else
+ int exit_code = -1;
+ internal::executor async;
+ async.start_process({"/bin/sh", "-c", "which " + program});
+ async.result(&exit_code);
+ return exit_code == 0;
+#endif
+}
+
+inline bool settings::is_osascript() const
+{
+#if __APPLE__
+ return true;
+#else
+ return false;
+#endif
+}
+
+inline bool settings::is_zenity() const
+{
+ return flags(flag::has_zenity) ||
+ flags(flag::has_matedialog) ||
+ flags(flag::has_qarma);
+}
+
+inline bool settings::is_kdialog() const
+{
+ return flags(flag::has_kdialog);
+}
+
+inline bool const &settings::flags(flag in_flag) const
+{
+ static bool flags[size_t(flag::max_flag)];
+ return flags[size_t(in_flag)];
+}
+
+inline bool &settings::flags(flag in_flag)
+{
+ return const_cast<bool &>(static_cast<settings const *>(this)->flags(in_flag));
+}
+
+// path implementation
+inline std::string path::home()
+{
+#if _WIN32
+ // First try the USERPROFILE environment variable
+ auto user_profile = internal::getenv("USERPROFILE");
+ if (user_profile.size() > 0)
+ return user_profile;
+ // Otherwise, try GetUserProfileDirectory()
+ HANDLE token = nullptr;
+ DWORD len = MAX_PATH;
+ char buf[MAX_PATH] = { '\0' };
+ if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
+ {
+ dll userenv("userenv.dll");
+ dll::proc<BOOL WINAPI (HANDLE, LPSTR, LPDWORD)> get_user_profile_directory(userenv, "GetUserProfileDirectoryA");
+ get_user_profile_directory(token, buf, &len);
+ CloseHandle(token);
+ if (*buf)
+ return buf;
+ }
+#elif __EMSCRIPTEN__
+ return "/";
+#else
+ // First try the HOME environment variable
+ auto home = internal::getenv("HOME");
+ if (home.size() > 0)
+ return home;
+ // Otherwise, try getpwuid_r()
+ size_t len = 4096;
+#if defined(_SC_GETPW_R_SIZE_MAX)
+ auto size_max = sysconf(_SC_GETPW_R_SIZE_MAX);
+ if (size_max != -1)
+ len = size_t(size_max);
+#endif
+ std::vector<char> buf(len);
+ struct passwd pwd, *result;
+ if (getpwuid_r(getuid(), &pwd, buf.data(), buf.size(), &result) == 0)
+ return result->pw_dir;
+#endif
+ return "/";
+}
+
+inline std::string path::separator()
+{
+#if _WIN32
+ return "\\";
+#else
+ return "/";
+#endif
+}
+
+// executor implementation
+
+inline std::string internal::executor::result(int *exit_code /* = nullptr */)
+{
+ stop();
+ if (exit_code)
+ *exit_code = m_exit_code;
+ return m_stdout;
+}
+
+inline bool internal::executor::kill()
+{
+#if _WIN32
+ if (m_future.valid())
+ {
+ // Close all windows that weren’t open when we started the future
+ auto previous_windows = m_windows;
+ EnumWindows(&enum_windows_callback, (LPARAM)this);
+ for (auto hwnd : m_windows)
+ if (previous_windows.find(hwnd) == previous_windows.end())
+ {
+ SendMessage(hwnd, WM_CLOSE, 0, 0);
+ // Also send IDNO in case of a Yes/No or Abort/Retry/Ignore messagebox
+ SendMessage(hwnd, WM_COMMAND, IDNO, 0);
+ }
+ }
+#elif __EMSCRIPTEN__ || __NX__
+ // FIXME: do something
+ return false; // cannot kill
+#else
+ ::kill(m_pid, SIGKILL);
+#endif
+ stop();
+ return true;
+}
+
+#if _WIN32
+inline BOOL CALLBACK internal::executor::enum_windows_callback(HWND hwnd, LPARAM lParam)
+{
+ auto that = (executor *)lParam;
+
+ DWORD pid;
+ auto tid = GetWindowThreadProcessId(hwnd, &pid);
+ if (tid == that->m_tid)
+ that->m_windows.insert(hwnd);
+ return TRUE;
+}
+#endif
+
+#if _WIN32
+inline void internal::executor::start_func(std::function<std::string(int *)> const &fun)
+{
+ stop();
+
+ auto trampoline = [fun, this]()
+ {
+ // Save our thread id so that the caller can cancel us
+ m_tid = GetCurrentThreadId();
+ EnumWindows(&enum_windows_callback, (LPARAM)this);
+ m_cond.notify_all();
+ return fun(&m_exit_code);
+ };
+
+ std::unique_lock<std::mutex> lock(m_mutex);
+ m_future = std::async(std::launch::async, trampoline);
+ m_cond.wait(lock);
+ m_running = true;
+}
+
+#elif __EMSCRIPTEN__
+inline void internal::executor::start(int exit_code)
+{
+ m_exit_code = exit_code;
+}
+
+#else
+inline void internal::executor::start_process(std::vector<std::string> const &command)
+{
+ stop();
+ m_stdout.clear();
+ m_exit_code = -1;
+
+ int in[2], out[2];
+ if (pipe(in) != 0 || pipe(out) != 0)
+ return;
+
+ m_pid = fork();
+ if (m_pid < 0)
+ return;
+
+ close(in[m_pid ? 0 : 1]);
+ close(out[m_pid ? 1 : 0]);
+
+ if (m_pid == 0)
+ {
+ dup2(in[0], STDIN_FILENO);
+ dup2(out[1], STDOUT_FILENO);
+
+ // Ignore stderr so that it doesn’t pollute the console (e.g. GTK+ errors from zenity)
+ int fd = open("/dev/null", O_WRONLY);
+ dup2(fd, STDERR_FILENO);
+ close(fd);
+
+ std::vector<char *> args;
+ std::transform(command.cbegin(), command.cend(), std::back_inserter(args),
+ [](std::string const &s) { return const_cast<char *>(s.c_str()); });
+ args.push_back(nullptr); // null-terminate argv[]
+
+ execvp(args[0], args.data());
+ exit(1);
+ }
+
+ close(in[1]);
+ m_fd = out[0];
+ auto flags = fcntl(m_fd, F_GETFL);
+ fcntl(m_fd, F_SETFL, flags | O_NONBLOCK);
+
+ m_running = true;
+}
+#endif
+
+inline internal::executor::~executor()
+{
+ stop();
+}
+
+inline bool internal::executor::ready(int timeout /* = default_wait_timeout */)
+{
+ if (!m_running)
+ return true;
+
+#if _WIN32
+ if (m_future.valid())
+ {
+ auto status = m_future.wait_for(std::chrono::milliseconds(timeout));
+ if (status != std::future_status::ready)
+ {
+ // On Windows, we need to run the message pump. If the async
+ // thread uses a Windows API dialog, it may be attached to the
+ // main thread and waiting for messages that only we can dispatch.
+ MSG msg;
+ while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ return false;
+ }
+
+ m_stdout = m_future.get();
+ }
+#elif __EMSCRIPTEN__ || __NX__
+ // FIXME: do something
+ (void)timeout;
+#else
+ char buf[BUFSIZ];
+ ssize_t received = read(m_fd, buf, BUFSIZ); // Flawfinder: ignore
+ if (received > 0)
+ {
+ m_stdout += std::string(buf, received);
+ return false;
+ }
+
+ // Reap child process if it is dead. It is possible that the system has already reaped it
+ // (this happens when the calling application handles or ignores SIG_CHLD) and results in
+ // waitpid() failing with ECHILD. Otherwise we assume the child is running and we sleep for
+ // a little while.
+ int status;
+ pid_t child = waitpid(m_pid, &status, WNOHANG);
+ if (child != m_pid && (child >= 0 || errno != ECHILD))
+ {
+ // FIXME: this happens almost always at first iteration
+ std::this_thread::sleep_for(std::chrono::milliseconds(timeout));
+ return false;
+ }
+
+ close(m_fd);
+ m_exit_code = WEXITSTATUS(status);
+#endif
+
+ m_running = false;
+ return true;
+}
+
+inline void internal::executor::stop()
+{
+ // Loop until the user closes the dialog
+ while (!ready())
+ ;
+}
+
+// dll implementation
+
+#if _WIN32
+inline internal::platform::dll::dll(std::string const &name)
+ : handle(::LoadLibraryA(name.c_str()))
+{}
+
+inline internal::platform::dll::~dll()
+{
+ if (handle)
+ ::FreeLibrary(handle);
+}
+#endif // _WIN32
+
+// ole32_dll implementation
+
+#if _WIN32
+inline internal::platform::ole32_dll::ole32_dll()
+ : dll("ole32.dll")
+{
+ // Use COINIT_MULTITHREADED because COINIT_APARTMENTTHREADED causes crashes.
+ // See https://github.com/samhocevar/portable-file-dialogs/issues/51
+ auto coinit = proc<HRESULT WINAPI (LPVOID, DWORD)>(*this, "CoInitializeEx");
+ m_state = coinit(nullptr, COINIT_MULTITHREADED);
+}
+
+inline internal::platform::ole32_dll::~ole32_dll()
+{
+ if (is_initialized())
+ proc<void WINAPI ()>(*this, "CoUninitialize")();
+}
+
+inline bool internal::platform::ole32_dll::is_initialized()
+{
+ return m_state == S_OK || m_state == S_FALSE;
+}
+#endif
+
+// new_style_context implementation
+
+#if _WIN32
+inline internal::platform::new_style_context::new_style_context()
+{
+ // Only create one activation context for the whole app lifetime.
+ static HANDLE hctx = create();
+
+ if (hctx != INVALID_HANDLE_VALUE)
+ ActivateActCtx(hctx, &m_cookie);
+}
+
+inline internal::platform::new_style_context::~new_style_context()
+{
+ DeactivateActCtx(0, m_cookie);
+}
+
+inline HANDLE internal::platform::new_style_context::create()
+{
+ // This “hack” seems to be necessary for this code to work on windows XP.
+ // Without it, dialogs do not show and close immediately. GetError()
+ // returns 0 so I don’t know what causes this. I was not able to reproduce
+ // this behavior on Windows 7 and 10 but just in case, let it be here for
+ // those versions too.
+ // This hack is not required if other dialogs are used (they load comdlg32
+ // automatically), only if message boxes are used.
+ dll comdlg32("comdlg32.dll");
+
+ // Using approach as shown here: https://stackoverflow.com/a/10444161
+ UINT len = ::GetSystemDirectoryA(nullptr, 0);
+ std::string sys_dir(len, '\0');
+ ::GetSystemDirectoryA(&sys_dir[0], len);
+
+ ACTCTXA act_ctx =
+ {
+ // Do not set flag ACTCTX_FLAG_SET_PROCESS_DEFAULT, since it causes a
+ // crash with error “default context is already set”.
+ sizeof(act_ctx),
+ ACTCTX_FLAG_RESOURCE_NAME_VALID | ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID,
+ "shell32.dll", 0, 0, sys_dir.c_str(), (LPCSTR)124, nullptr, 0,
+ };
+
+ return ::CreateActCtxA(&act_ctx);
+}
+#endif // _WIN32
+
+// dialog implementation
+
+inline bool internal::dialog::ready(int timeout /* = default_wait_timeout */) const
+{
+ return m_async->ready(timeout);
+}
+
+inline bool internal::dialog::kill() const
+{
+ return m_async->kill();
+}
+
+inline internal::dialog::dialog()
+ : m_async(std::make_shared<executor>())
+{
+}
+
+inline std::vector<std::string> internal::dialog::desktop_helper() const
+{
+#if __APPLE__
+ return { "osascript" };
+#else
+ return { flags(flag::has_zenity) ? "zenity"
+ : flags(flag::has_matedialog) ? "matedialog"
+ : flags(flag::has_qarma) ? "qarma"
+ : flags(flag::has_kdialog) ? "kdialog"
+ : "echo" };
+#endif
+}
+
+inline std::string internal::dialog::buttons_to_name(choice _choice)
+{
+ switch (_choice)
+ {
+ case choice::ok_cancel: return "okcancel";
+ case choice::yes_no: return "yesno";
+ case choice::yes_no_cancel: return "yesnocancel";
+ case choice::retry_cancel: return "retrycancel";
+ case choice::abort_retry_ignore: return "abortretryignore";
+ /* case choice::ok: */ default: return "ok";
+ }
+}
+
+inline std::string internal::dialog::get_icon_name(icon _icon)
+{
+ switch (_icon)
+ {
+ case icon::warning: return "warning";
+ case icon::error: return "error";
+ case icon::question: return "question";
+ // Zenity wants "information" but WinForms wants "info"
+ /* case icon::info: */ default:
+#if _WIN32
+ return "info";
+#else
+ return "information";
+#endif
+ }
+}
+
+// This is only used for debugging purposes
+inline std::ostream& operator <<(std::ostream &s, std::vector<std::string> const &v)
+{
+ int not_first = 0;
+ for (auto &e : v)
+ s << (not_first++ ? " " : "") << e;
+ return s;
+}
+
+// Properly quote a string for Powershell: replace ' or " with '' or ""
+// FIXME: we should probably get rid of newlines!
+// FIXME: the \" sequence seems unsafe, too!
+// XXX: this is no longer used but I would like to keep it around just in case
+inline std::string internal::dialog::powershell_quote(std::string const &str) const
+{
+ return "'" + std::regex_replace(str, std::regex("['\"]"), "$&$&") + "'";
+}
+
+// Properly quote a string for osascript: replace \ or " with \\ or \"
+// XXX: this also used to replace ' with \' when popen was used, but it would be
+// smarter to do shell_quote(osascript_quote(...)) if this is needed again.
+inline std::string internal::dialog::osascript_quote(std::string const &str) const
+{
+ return "\"" + std::regex_replace(str, std::regex("[\\\\\"]"), "\\$&") + "\"";
+}
+
+// Properly quote a string for the shell: just replace ' with '\''
+// XXX: this is no longer used but I would like to keep it around just in case
+inline std::string internal::dialog::shell_quote(std::string const &str) const
+{
+ return "'" + std::regex_replace(str, std::regex("'"), "'\\''") + "'";
+}
+
+// file_dialog implementation
+
+inline internal::file_dialog::file_dialog(type in_type,
+ std::string const &title,
+ std::string const &default_path /* = "" */,
+ std::vector<std::string> const &filters /* = {} */,
+ opt options /* = opt::none */)
+{
+#if _WIN32
+ std::string filter_list;
+ std::regex whitespace(" *");
+ for (size_t i = 0; i + 1 < filters.size(); i += 2)
+ {
+ filter_list += filters[i] + '\0';
+ filter_list += std::regex_replace(filters[i + 1], whitespace, ";") + '\0';
+ }
+ filter_list += '\0';
+
+ m_async->start_func([this, in_type, title, default_path, filter_list,
+ options](int *exit_code) -> std::string
+ {
+ (void)exit_code;
+ m_wtitle = internal::str2wstr(title);
+ m_wdefault_path = internal::str2wstr(default_path);
+ auto wfilter_list = internal::str2wstr(filter_list);
+
+ // Initialise COM. This is required for the new folder selection window,
+ // (see https://github.com/samhocevar/portable-file-dialogs/pull/21)
+ // and to avoid random crashes with GetOpenFileNameW() (see
+ // https://github.com/samhocevar/portable-file-dialogs/issues/51)
+ ole32_dll ole32;
+
+ // Folder selection uses a different method
+ if (in_type == type::folder)
+ {
+#if PFD_HAS_IFILEDIALOG
+ if (flags(flag::is_vista))
+ {
+ // On Vista and higher we should be able to use IFileDialog for folder selection
+ IFileDialog *ifd;
+ HRESULT hr = dll::proc<HRESULT WINAPI (REFCLSID, LPUNKNOWN, DWORD, REFIID, LPVOID *)>(ole32, "CoCreateInstance")
+ (CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&ifd));
+
+ // In case CoCreateInstance fails (which it should not), try legacy approach
+ if (SUCCEEDED(hr))
+ return select_folder_vista(ifd, options & opt::force_path);
+ }
+#endif
+
+ BROWSEINFOW bi;
+ memset(&bi, 0, sizeof(bi));
+
+ bi.lpfn = &bffcallback;
+ bi.lParam = (LPARAM)this;
+
+ if (flags(flag::is_vista))
+ {
+ if (ole32.is_initialized())
+ bi.ulFlags |= BIF_NEWDIALOGSTYLE;
+ bi.ulFlags |= BIF_EDITBOX;
+ bi.ulFlags |= BIF_STATUSTEXT;
+ }
+
+ auto *list = SHBrowseForFolderW(&bi);
+ std::string ret;
+ if (list)
+ {
+ auto buffer = new wchar_t[MAX_PATH];
+ SHGetPathFromIDListW(list, buffer);
+ dll::proc<void WINAPI (LPVOID)>(ole32, "CoTaskMemFree")(list);
+ ret = internal::wstr2str(buffer);
+ delete[] buffer;
+ }
+ return ret;
+ }
+
+ OPENFILENAMEW ofn;
+ memset(&ofn, 0, sizeof(ofn));
+ ofn.lStructSize = sizeof(OPENFILENAMEW);
+ ofn.hwndOwner = GetActiveWindow();
+
+ ofn.lpstrFilter = wfilter_list.c_str();
+
+ auto woutput = std::wstring(MAX_PATH * 256, L'\0');
+ ofn.lpstrFile = (LPWSTR)woutput.data();
+ ofn.nMaxFile = (DWORD)woutput.size();
+ if (!m_wdefault_path.empty())
+ {
+ // If a directory was provided, use it as the initial directory. If
+ // a valid path was provided, use it as the initial file. Otherwise,
+ // let the Windows API decide.
+ auto path_attr = GetFileAttributesW(m_wdefault_path.c_str());
+ if (path_attr != INVALID_FILE_ATTRIBUTES && (path_attr & FILE_ATTRIBUTE_DIRECTORY))
+ ofn.lpstrInitialDir = m_wdefault_path.c_str();
+ else if (m_wdefault_path.size() <= woutput.size())
+ //second argument is size of buffer, not length of string
+ StringCchCopyW(ofn.lpstrFile, MAX_PATH*256+1, m_wdefault_path.c_str());
+ else
+ {
+ ofn.lpstrFileTitle = (LPWSTR)m_wdefault_path.data();
+ ofn.nMaxFileTitle = (DWORD)m_wdefault_path.size();
+ }
+ }
+ ofn.lpstrTitle = m_wtitle.c_str();
+ ofn.Flags = OFN_NOCHANGEDIR | OFN_EXPLORER;
+
+ dll comdlg32("comdlg32.dll");
+
+ // Apply new visual style (required for windows XP)
+ new_style_context ctx;
+
+ if (in_type == type::save)
+ {
+ if (!(options & opt::force_overwrite))
+ ofn.Flags |= OFN_OVERWRITEPROMPT;
+
+ dll::proc<BOOL WINAPI (LPOPENFILENAMEW)> get_save_file_name(comdlg32, "GetSaveFileNameW");
+ if (get_save_file_name(&ofn) == 0)
+ return "";
+ return internal::wstr2str(woutput.c_str());
+ }
+ else
+ {
+ if (options & opt::multiselect)
+ ofn.Flags |= OFN_ALLOWMULTISELECT;
+ ofn.Flags |= OFN_PATHMUSTEXIST;
+
+ dll::proc<BOOL WINAPI (LPOPENFILENAMEW)> get_open_file_name(comdlg32, "GetOpenFileNameW");
+ if (get_open_file_name(&ofn) == 0)
+ return "";
+ }
+
+ std::string prefix;
+ for (wchar_t const *p = woutput.c_str(); *p; )
+ {
+ auto filename = internal::wstr2str(p);
+ p += wcslen(p);
+ // In multiselect mode, we advance p one wchar further and
+ // check for another filename. If there is one and the
+ // prefix is empty, it means we just read the prefix.
+ if ((options & opt::multiselect) && *++p && prefix.empty())
+ {
+ prefix = filename + "/";
+ continue;
+ }
+
+ m_vector_result.push_back(prefix + filename);
+ }
+
+ return "";
+ });
+#elif __EMSCRIPTEN__
+ // FIXME: do something
+ (void)in_type;
+ (void)title;
+ (void)default_path;
+ (void)filters;
+ (void)options;
+#else
+ auto command = desktop_helper();
+
+ if (is_osascript())
+ {
+ std::string script = "set ret to choose";
+ switch (in_type)
+ {
+ case type::save:
+ script += " file name";
+ break;
+ case type::open: default:
+ script += " file";
+ if (options & opt::multiselect)
+ script += " with multiple selections allowed";
+ break;
+ case type::folder:
+ script += " folder";
+ break;
+ }
+
+ if (default_path.size())
+ {
+ if (in_type == type::folder || is_directory(default_path))
+ script += " default location ";
+ else
+ script += " default name ";
+ script += osascript_quote(default_path);
+ }
+
+ script += " with prompt " + osascript_quote(title);
+
+ if (in_type == type::open)
+ {
+ // Concatenate all user-provided filter patterns
+ std::string patterns;
+ for (size_t i = 0; i < filters.size() / 2; ++i)
+ patterns += " " + filters[2 * i + 1];
+
+ // Split the pattern list to check whether "*" is in there; if it
+ // is, we have to disable filters because there is no mechanism in
+ // OS X for the user to override the filter.
+ std::regex sep("\\s+");
+ std::string filter_list;
+ bool has_filter = true;
+ std::sregex_token_iterator iter(patterns.begin(), patterns.end(), sep, -1);
+ std::sregex_token_iterator end;
+ for ( ; iter != end; ++iter)
+ {
+ auto pat = iter->str();
+ if (pat == "*" || pat == "*.*")
+ has_filter = false;
+ else if (internal::starts_with(pat, "*."))
+ filter_list += "," + osascript_quote(pat.substr(2, pat.size() - 2));
+ }
+
+ if (has_filter && filter_list.size() > 0)
+ {
+ // There is a weird AppleScript bug where file extensions of length != 3 are
+ // ignored, e.g. type{"txt"} works, but type{"json"} does not. Fortunately if
+ // the whole list starts with a 3-character extension, everything works again.
+ // We use "///" for such an extension because we are sure it cannot appear in
+ // an actual filename.
+ script += " of type {\"///\"" + filter_list + "}";
+ }
+ }
+
+ if (in_type == type::open && (options & opt::multiselect))
+ {
+ script += "\nset s to \"\"";
+ script += "\nrepeat with i in ret";
+ script += "\n set s to s & (POSIX path of i) & \"\\n\"";
+ script += "\nend repeat";
+ script += "\ncopy s to stdout";
+ }
+ else
+ {
+ script += "\nPOSIX path of ret";
+ }
+
+ command.push_back("-e");
+ command.push_back(script);
+ }
+ else if (is_zenity())
+ {
+ command.push_back("--file-selection");
+
+ // If the default path is a directory, make sure it ends with "/" otherwise zenity will
+ // open the file dialog in the parent directory.
+ auto filename_arg = "--filename=" + default_path;
+ if (in_type != type::folder && !ends_with(default_path, "/") && internal::is_directory(default_path))
+ filename_arg += "/";
+ command.push_back(filename_arg);
+
+ command.push_back("--title");
+ command.push_back(title);
+ command.push_back("--separator=\n");
+
+ for (size_t i = 0; i < filters.size() / 2; ++i)
+ {
+ command.push_back("--file-filter");
+ command.push_back(filters[2 * i] + "|" + filters[2 * i + 1]);
+ }
+
+ if (in_type == type::save)
+ command.push_back("--save");
+ if (in_type == type::folder)
+ command.push_back("--directory");
+ if (!(options & opt::force_overwrite))
+ command.push_back("--confirm-overwrite");
+ if (options & opt::multiselect)
+ command.push_back("--multiple");
+ }
+ else if (is_kdialog())
+ {
+ switch (in_type)
+ {
+ case type::save: command.push_back("--getsavefilename"); break;
+ case type::open: command.push_back("--getopenfilename"); break;
+ case type::folder: command.push_back("--getexistingdirectory"); break;
+ }
+ if (options & opt::multiselect)
+ {
+ command.push_back("--multiple");
+ command.push_back("--separate-output");
+ }
+
+ command.push_back(default_path);
+
+ std::string filter;
+ for (size_t i = 0; i < filters.size() / 2; ++i)
+ filter += (i == 0 ? "" : " | ") + filters[2 * i] + "(" + filters[2 * i + 1] + ")";
+ command.push_back(filter);
+
+ command.push_back("--title");
+ command.push_back(title);
+ }
+
+ if (flags(flag::is_verbose))
+ std::cerr << "pfd: " << command << std::endl;
+
+ m_async->start_process(command);
+#endif
+}
+
+inline std::string internal::file_dialog::string_result()
+{
+#if _WIN32
+ return m_async->result();
+#else
+ auto ret = m_async->result();
+ // Strip potential trailing newline (zenity). Also strip trailing slash
+ // added by osascript for consistency with other backends.
+ while (!ret.empty() && (ret.back() == '\n' || ret.back() == '/'))
+ ret.pop_back();
+ return ret;
+#endif
+}
+
+inline std::vector<std::string> internal::file_dialog::vector_result()
+{
+#if _WIN32
+ m_async->result();
+ return m_vector_result;
+#else
+ std::vector<std::string> ret;
+ auto result = m_async->result();
+ for (;;)
+ {
+ // Split result along newline characters
+ auto i = result.find('\n');
+ if (i == 0 || i == std::string::npos)
+ break;
+ ret.push_back(result.substr(0, i));
+ result = result.substr(i + 1, result.size());
+ }
+ return ret;
+#endif
+}
+
+#if _WIN32
+// Use a static function to pass as BFFCALLBACK for legacy folder select
+inline int CALLBACK internal::file_dialog::bffcallback(HWND hwnd, UINT uMsg,
+ LPARAM, LPARAM pData)
+{
+ auto inst = (file_dialog *)pData;
+ switch (uMsg)
+ {
+ case BFFM_INITIALIZED:
+ SendMessage(hwnd, BFFM_SETSELECTIONW, TRUE, (LPARAM)inst->m_wdefault_path.c_str());
+ break;
+ }
+ return 0;
+}
+
+#if PFD_HAS_IFILEDIALOG
+inline std::string internal::file_dialog::select_folder_vista(IFileDialog *ifd, bool force_path)
+{
+ std::string result;
+
+ IShellItem *folder;
+
+ // Load library at runtime so app doesn't link it at load time (which will fail on windows XP)
+ dll shell32("shell32.dll");
+ dll::proc<HRESULT WINAPI (PCWSTR, IBindCtx*, REFIID, void**)>
+ create_item(shell32, "SHCreateItemFromParsingName");
+
+ if (!create_item)
+ return "";
+
+ auto hr = create_item(m_wdefault_path.c_str(),
+ nullptr,
+ IID_PPV_ARGS(&folder));
+
+ // Set default folder if found. This only sets the default folder. If
+ // Windows has any info about the most recently selected folder, it
+ // will display it instead. Generally, calling SetFolder() to set the
+ // current directory “is not a good or expected user experience and
+ // should therefore be avoided”:
+ // https://docs.microsoft.com/windows/win32/api/shobjidl_core/nf-shobjidl_core-ifiledialog-setfolder
+ if (SUCCEEDED(hr))
+ {
+ if (force_path)
+ ifd->SetFolder(folder);
+ else
+ ifd->SetDefaultFolder(folder);
+ folder->Release();
+ }
+
+ // Set the dialog title and option to select folders
+ ifd->SetOptions(FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM);
+ ifd->SetTitle(m_wtitle.c_str());
+
+ hr = ifd->Show(GetActiveWindow());
+ if (SUCCEEDED(hr))
+ {
+ IShellItem* item;
+ hr = ifd->GetResult(&item);
+ if (SUCCEEDED(hr))
+ {
+ wchar_t* wname = nullptr;
+ // This is unlikely to fail because we use FOS_FORCEFILESYSTEM, but try
+ // to output a debug message just in case.
+ if (SUCCEEDED(item->GetDisplayName(SIGDN_FILESYSPATH, &wname)))
+ {
+ result = internal::wstr2str(std::wstring(wname));
+ dll::proc<void WINAPI (LPVOID)>(ole32_dll(), "CoTaskMemFree")(wname);
+ }
+ else
+ {
+ if (SUCCEEDED(item->GetDisplayName(SIGDN_NORMALDISPLAY, &wname)))
+ {
+ auto name = internal::wstr2str(std::wstring(wname));
+ dll::proc<void WINAPI (LPVOID)>(ole32_dll(), "CoTaskMemFree")(wname);
+ std::cerr << "pfd: failed to get path for " << name << std::endl;
+ }
+ else
+ std::cerr << "pfd: item of unknown type selected" << std::endl;
+ }
+
+ item->Release();
+ }
+ }
+
+ ifd->Release();
+
+ return result;
+}
+#endif
+#endif
+
+// notify implementation
+
+inline notify::notify(std::string const &title,
+ std::string const &message,
+ icon _icon /* = icon::info */)
+{
+ if (_icon == icon::question) // Not supported by notifications
+ _icon = icon::info;
+
+#if _WIN32
+ // Use a static shared pointer for notify_icon so that we can delete
+ // it whenever we need to display a new one, and we can also wait
+ // until the program has finished running.
+ struct notify_icon_data : public NOTIFYICONDATAW
+ {
+ ~notify_icon_data() { Shell_NotifyIconW(NIM_DELETE, this); }
+ };
+
+ static std::shared_ptr<notify_icon_data> nid;
+
+ // Release the previous notification icon, if any, and allocate a new
+ // one. Note that std::make_shared() does value initialization, so there
+ // is no need to memset the structure.
+ nid = nullptr;
+ nid = std::make_shared<notify_icon_data>();
+
+ // For XP support
+ nid->cbSize = NOTIFYICONDATAW_V2_SIZE;
+ nid->hWnd = nullptr;
+ nid->uID = 0;
+
+ // Flag Description:
+ // - NIF_ICON The hIcon member is valid.
+ // - NIF_MESSAGE The uCallbackMessage member is valid.
+ // - NIF_TIP The szTip member is valid.
+ // - NIF_STATE The dwState and dwStateMask members are valid.
+ // - NIF_INFO Use a balloon ToolTip instead of a standard ToolTip. The szInfo, uTimeout, szInfoTitle, and dwInfoFlags members are valid.
+ // - NIF_GUID Reserved.
+ nid->uFlags = NIF_MESSAGE | NIF_ICON | NIF_INFO;
+
+ // Flag Description
+ // - NIIF_ERROR An error icon.
+ // - NIIF_INFO An information icon.
+ // - NIIF_NONE No icon.
+ // - NIIF_WARNING A warning icon.
+ // - NIIF_ICON_MASK Version 6.0. Reserved.
+ // - NIIF_NOSOUND Version 6.0. Do not play the associated sound. Applies only to balloon ToolTips
+ switch (_icon)
+ {
+ case icon::warning: nid->dwInfoFlags = NIIF_WARNING; break;
+ case icon::error: nid->dwInfoFlags = NIIF_ERROR; break;
+ /* case icon::info: */ default: nid->dwInfoFlags = NIIF_INFO; break;
+ }
+
+ ENUMRESNAMEPROC icon_enum_callback = [](HMODULE, LPCTSTR, LPTSTR lpName, LONG_PTR lParam) -> BOOL
+ {
+ ((NOTIFYICONDATAW *)lParam)->hIcon = ::LoadIcon(GetModuleHandle(nullptr), lpName);
+ return false;
+ };
+
+ nid->hIcon = ::LoadIcon(nullptr, IDI_APPLICATION);
+ ::EnumResourceNames(nullptr, RT_GROUP_ICON, icon_enum_callback, (LONG_PTR)nid.get());
+
+ nid->uTimeout = 5000;
+
+ StringCchCopyW(nid->szInfoTitle, ARRAYSIZE(nid->szInfoTitle), internal::str2wstr(title).c_str());
+ StringCchCopyW(nid->szInfo, ARRAYSIZE(nid->szInfo), internal::str2wstr(message).c_str());
+
+ // Display the new icon
+ Shell_NotifyIconW(NIM_ADD, nid.get());
+#elif __EMSCRIPTEN__
+ // FIXME: do something
+ (void)title;
+ (void)message;
+#else
+ auto command = desktop_helper();
+
+ if (is_osascript())
+ {
+ command.push_back("-e");
+ command.push_back("display notification " + osascript_quote(message) +
+ " with title " + osascript_quote(title));
+ }
+ else if (is_zenity())
+ {
+ command.push_back("--notification");
+ command.push_back("--window-icon");
+ command.push_back(get_icon_name(_icon));
+ command.push_back("--text");
+ command.push_back(title + "\n" + message);
+ }
+ else if (is_kdialog())
+ {
+ command.push_back("--icon");
+ command.push_back(get_icon_name(_icon));
+ command.push_back("--title");
+ command.push_back(title);
+ command.push_back("--passivepopup");
+ command.push_back(message);
+ command.push_back("5");
+ }
+
+ if (flags(flag::is_verbose))
+ std::cerr << "pfd: " << command << std::endl;
+
+ m_async->start_process(command);
+#endif
+}
+
+// message implementation
+
+inline message::message(std::string const &title,
+ std::string const &text,
+ choice _choice /* = choice::ok_cancel */,
+ icon _icon /* = icon::info */)
+{
+#if _WIN32
+ // Use MB_SYSTEMMODAL rather than MB_TOPMOST to ensure the message window is brought
+ // to front. See https://github.com/samhocevar/portable-file-dialogs/issues/52
+ UINT style = MB_SYSTEMMODAL;
+ switch (_icon)
+ {
+ case icon::warning: style |= MB_ICONWARNING; break;
+ case icon::error: style |= MB_ICONERROR; break;
+ case icon::question: style |= MB_ICONQUESTION; break;
+ /* case icon::info: */ default: style |= MB_ICONINFORMATION; break;
+ }
+
+ switch (_choice)
+ {
+ case choice::ok_cancel: style |= MB_OKCANCEL; break;
+ case choice::yes_no: style |= MB_YESNO; break;
+ case choice::yes_no_cancel: style |= MB_YESNOCANCEL; break;
+ case choice::retry_cancel: style |= MB_RETRYCANCEL; break;
+ case choice::abort_retry_ignore: style |= MB_ABORTRETRYIGNORE; break;
+ /* case choice::ok: */ default: style |= MB_OK; break;
+ }
+
+ m_mappings[IDCANCEL] = button::cancel;
+ m_mappings[IDOK] = button::ok;
+ m_mappings[IDYES] = button::yes;
+ m_mappings[IDNO] = button::no;
+ m_mappings[IDABORT] = button::abort;
+ m_mappings[IDRETRY] = button::retry;
+ m_mappings[IDIGNORE] = button::ignore;
+
+ m_async->start_func([text, title, style](int* exit_code) -> std::string
+ {
+ auto wtext = internal::str2wstr(text);
+ auto wtitle = internal::str2wstr(title);
+ // Apply new visual style (required for all Windows versions)
+ new_style_context ctx;
+ *exit_code = MessageBoxW(GetActiveWindow(), wtext.c_str(), wtitle.c_str(), style);
+ return "";
+ });
+
+#elif __EMSCRIPTEN__
+ std::string full_message;
+ switch (_icon)
+ {
+ case icon::warning: full_message = "⚠️"; break;
+ case icon::error: full_message = "⛔"; break;
+ case icon::question: full_message = "❓"; break;
+ /* case icon::info: */ default: full_message = "ℹ"; break;
+ }
+
+ full_message += ' ' + title + "\n\n" + text;
+
+ // This does not really start an async task; it just passes the
+ // EM_ASM_INT return value to a fake start() function.
+ m_async->start(EM_ASM_INT(
+ {
+ if ($1)
+ return window.confirm(UTF8ToString($0)) ? 0 : -1;
+ alert(UTF8ToString($0));
+ return 0;
+ }, full_message.c_str(), _choice == choice::ok_cancel));
+#else
+ auto command = desktop_helper();
+
+ if (is_osascript())
+ {
+ std::string script = "display dialog " + osascript_quote(text) +
+ " with title " + osascript_quote(title);
+ auto if_cancel = button::cancel;
+ switch (_choice)
+ {
+ case choice::ok_cancel:
+ script += "buttons {\"OK\", \"Cancel\"}"
+ " default button \"OK\""
+ " cancel button \"Cancel\"";
+ break;
+ case choice::yes_no:
+ script += "buttons {\"Yes\", \"No\"}"
+ " default button \"Yes\""
+ " cancel button \"No\"";
+ if_cancel = button::no;
+ break;
+ case choice::yes_no_cancel:
+ script += "buttons {\"Yes\", \"No\", \"Cancel\"}"
+ " default button \"Yes\""
+ " cancel button \"Cancel\"";
+ break;
+ case choice::retry_cancel:
+ script += "buttons {\"Retry\", \"Cancel\"}"
+ " default button \"Retry\""
+ " cancel button \"Cancel\"";
+ break;
+ case choice::abort_retry_ignore:
+ script += "buttons {\"Abort\", \"Retry\", \"Ignore\"}"
+ " default button \"Abort\""
+ " cancel button \"Retry\"";
+ if_cancel = button::retry;
+ break;
+ case choice::ok: default:
+ script += "buttons {\"OK\"}"
+ " default button \"OK\""
+ " cancel button \"OK\"";
+ if_cancel = button::ok;
+ break;
+ }
+ m_mappings[1] = if_cancel;
+ m_mappings[256] = if_cancel; // XXX: I think this was never correct
+ script += " with icon ";
+ switch (_icon)
+ {
+ #define PFD_OSX_ICON(n) "alias ((path to library folder from system domain) as text " \
+ "& \"CoreServices:CoreTypes.bundle:Contents:Resources:" n ".icns\")"
+ case icon::info: default: script += PFD_OSX_ICON("ToolBarInfo"); break;
+ case icon::warning: script += "caution"; break;
+ case icon::error: script += "stop"; break;
+ case icon::question: script += PFD_OSX_ICON("GenericQuestionMarkIcon"); break;
+ #undef PFD_OSX_ICON
+ }
+
+ command.push_back("-e");
+ command.push_back(script);
+ }
+ else if (is_zenity())
+ {
+ switch (_choice)
+ {
+ case choice::ok_cancel:
+ command.insert(command.end(), { "--question", "--cancel-label=Cancel", "--ok-label=OK" }); break;
+ case choice::yes_no:
+ // Do not use standard --question because it causes “No” to return -1,
+ // which is inconsistent with the “Yes/No/Cancel” mode below.
+ command.insert(command.end(), { "--question", "--switch", "--extra-button=No", "--extra-button=Yes" }); break;
+ case choice::yes_no_cancel:
+ command.insert(command.end(), { "--question", "--switch", "--extra-button=Cancel", "--extra-button=No", "--extra-button=Yes" }); break;
+ case choice::retry_cancel:
+ command.insert(command.end(), { "--question", "--switch", "--extra-button=Cancel", "--extra-button=Retry" }); break;
+ case choice::abort_retry_ignore:
+ command.insert(command.end(), { "--question", "--switch", "--extra-button=Ignore", "--extra-button=Abort", "--extra-button=Retry" }); break;
+ case choice::ok:
+ default:
+ switch (_icon)
+ {
+ case icon::error: command.push_back("--error"); break;
+ case icon::warning: command.push_back("--warning"); break;
+ default: command.push_back("--info"); break;
+ }
+ }
+
+ command.insert(command.end(), { "--title", title,
+ "--width=300", "--height=0", // sensible defaults
+ "--no-markup", // do not interpret text as Pango markup
+ "--text", text,
+ "--icon-name=dialog-" + get_icon_name(_icon) });
+ }
+ else if (is_kdialog())
+ {
+ if (_choice == choice::ok)
+ {
+ switch (_icon)
+ {
+ case icon::error: command.push_back("--error"); break;
+ case icon::warning: command.push_back("--sorry"); break;
+ default: command.push_back("--msgbox"); break;
+ }
+ }
+ else
+ {
+ std::string flag = "--";
+ if (_icon == icon::warning || _icon == icon::error)
+ flag += "warning";
+ flag += "yesno";
+ if (_choice == choice::yes_no_cancel)
+ flag += "cancel";
+ command.push_back(flag);
+ if (_choice == choice::yes_no || _choice == choice::yes_no_cancel)
+ {
+ m_mappings[0] = button::yes;
+ m_mappings[256] = button::no;
+ }
+ }
+
+ command.push_back(text);
+ command.push_back("--title");
+ command.push_back(title);
+
+ // Must be after the above part
+ if (_choice == choice::ok_cancel)
+ command.insert(command.end(), { "--yes-label", "OK", "--no-label", "Cancel" });
+ }
+
+ if (flags(flag::is_verbose))
+ std::cerr << "pfd: " << command << std::endl;
+
+ m_async->start_process(command);
+#endif
+}
+
+inline button message::result()
+{
+ int exit_code;
+ auto ret = m_async->result(&exit_code);
+ // osascript will say "button returned:Cancel\n"
+ // and others will just say "Cancel\n"
+ if (internal::ends_with(ret, "Cancel\n"))
+ return button::cancel;
+ if (internal::ends_with(ret, "OK\n"))
+ return button::ok;
+ if (internal::ends_with(ret, "Yes\n"))
+ return button::yes;
+ if (internal::ends_with(ret, "No\n"))
+ return button::no;
+ if (internal::ends_with(ret, "Abort\n"))
+ return button::abort;
+ if (internal::ends_with(ret, "Retry\n"))
+ return button::retry;
+ if (internal::ends_with(ret, "Ignore\n"))
+ return button::ignore;
+ if (m_mappings.count(exit_code) != 0)
+ return m_mappings[exit_code];
+ return exit_code == 0 ? button::ok : button::cancel;
+}
+
+// open_file implementation
+
+inline open_file::open_file(std::string const &title,
+ std::string const &default_path /* = "" */,
+ std::vector<std::string> const &filters /* = { "All Files", "*" } */,
+ opt options /* = opt::none */)
+ : file_dialog(type::open, title, default_path, filters, options)
+{
+}
+
+inline open_file::open_file(std::string const &title,
+ std::string const &default_path,
+ std::vector<std::string> const &filters,
+ bool allow_multiselect)
+ : open_file(title, default_path, filters,
+ (allow_multiselect ? opt::multiselect : opt::none))
+{
+}
+
+inline std::vector<std::string> open_file::result()
+{
+ return vector_result();
+}
+
+// save_file implementation
+
+inline save_file::save_file(std::string const &title,
+ std::string const &default_path /* = "" */,
+ std::vector<std::string> const &filters /* = { "All Files", "*" } */,
+ opt options /* = opt::none */)
+ : file_dialog(type::save, title, default_path, filters, options)
+{
+}
+
+inline save_file::save_file(std::string const &title,
+ std::string const &default_path,
+ std::vector<std::string> const &filters,
+ bool confirm_overwrite)
+ : save_file(title, default_path, filters,
+ (confirm_overwrite ? opt::none : opt::force_overwrite))
+{
+}
+
+inline std::string save_file::result()
+{
+ return string_result();
+}
+
+// select_folder implementation
+
+inline select_folder::select_folder(std::string const &title,
+ std::string const &default_path /* = "" */,
+ opt options /* = opt::none */)
+ : file_dialog(type::folder, title, default_path, {}, options)
+{
+}
+
+inline std::string select_folder::result()
+{
+ return string_result();
+}
+
+#endif // PFD_SKIP_IMPLEMENTATION
+
+} // namespace pfd
diff --git a/mm/2s2h/SohGui.cpp b/mm/2s2h/SohGui.cpp
new file mode 100644
index 000000000..3f7b792f2
--- /dev/null
+++ b/mm/2s2h/SohGui.cpp
@@ -0,0 +1,210 @@
+//
+// SohGui.cpp
+// soh
+//
+// Created by David Chavez on 24.08.22.
+//
+
+#include "SohGui.hpp"
+
+#include <spdlog/spdlog.h>
+#include <ImGui/imgui.h>
+#define IMGUI_DEFINE_MATH_OPERATORS
+#include <ImGui/imgui_internal.h>
+#include <libultraship/libultraship.h>
+#include <Fast3D/gfx_pc.h>
+
+#ifdef __APPLE__
+#include "graphic/Fast3D/gfx_metal.h"
+#endif
+
+#ifdef __SWITCH__
+#include <port/switch/SwitchImpl.h>
+#endif
+
+//#include "UIWidgets.hpp"
+#include "include/global.h"
+#include "include/z64audio.h"
+//#include "soh/SaveManager.h"
+//#include "OTRGlobals.h"
+//#include "soh/Enhancements/presets.h"
+//#include "2s2h/resource/type/Skeleton.h"
+#include "libultraship/libultraship.h"
+
+//#ifdef ENABLE_CROWD_CONTROL
+//#include "Enhancements/crowd-control/CrowdControl.h"
+//#endif
+
+//#include "Enhancements/game-interactor/GameInteractor.h"
+//#include "Enhancements/cosmetics/authenticGfxPatches.h"
+
+bool ShouldClearTextureCacheAtEndOfFrame = false;
+bool isBetaQuestEnabled = false;
+
+extern "C" {
+ void enableBetaQuest() { isBetaQuestEnabled = true; }
+ void disableBetaQuest() { isBetaQuestEnabled = false; }
+}
+
+
+namespace SohGui {
+
+ // MARK: - Properties
+
+ static const char* chestSizeAndTextureMatchesContentsOptions[4] = { "Disabled", "Both", "Texture Only", "Size Only" };
+ static const char* bunnyHoodOptions[3] = { "Disabled", "Faster Run & Longer Jump", "Faster Run" };
+ static const char* allPowers[9] = {
+ "Vanilla (1x)",
+ "Double (2x)",
+ "Quadruple (4x)",
+ "Octuple (8x)",
+ "Foolish (16x)",
+ "Ridiculous (32x)",
+ "Merciless (64x)",
+ "Pure Torture (128x)",
+ "OHKO (256x)" };
+ static const char* subPowers[8] = { allPowers[0], allPowers[1], allPowers[2], allPowers[3], allPowers[4], allPowers[5], allPowers[6], allPowers[7] };
+ static const char* subSubPowers[7] = { allPowers[0], allPowers[1], allPowers[2], allPowers[3], allPowers[4], allPowers[5], allPowers[6] };
+ static const char* zFightingOptions[3] = { "Disabled", "Consistent Vanish", "No Vanish" };
+ static const char* autosaveLabels[6] = { "Off", "New Location + Major Item", "New Location + Any Item", "New Location", "Major Item", "Any Item" };
+ static const char* FastFileSelect[5] = { "File N.1", "File N.2", "File N.3", "Zelda Map Select (require OoT Debug Mode)", "File select" };
+ static const char* bonkDamageValues[8] = {
+ "No Damage",
+ "0.25 Heart",
+ "0.5 Heart",
+ "1 Heart",
+ "2 Hearts",
+ "4 Hearts",
+ "8 Hearts",
+ "OHKO"
+ };
+
+ static const inline std::vector<std::pair<const char*, const char*>> audioBackends = {
+#ifdef _WIN32
+ { "wasapi", "Windows Audio Session API" },
+#endif
+#if defined(__linux)
+ { "pulse", "PulseAudio" },
+#endif
+ { "sdl", "SDL Audio" }
+ };
+
+
+ // MARK: - Helpers
+
+ std::string GetWindowButtonText(const char* text, bool menuOpen) {
+ char buttonText[100] = "";
+ if (menuOpen) {
+ strcat(buttonText, ICON_FA_CHEVRON_RIGHT " ");
+ }
+ strcat(buttonText, text);
+ if (!menuOpen) { strcat(buttonText, " "); }
+ return buttonText;
+ }
+
+
+ // MARK: - Delegates
+
+ std::shared_ptr<SohMenuBar> mSohMenuBar;
+
+ std::shared_ptr<LUS::GuiWindow> mConsoleWindow;
+ std::shared_ptr<LUS::GuiWindow> mStatsWindow;
+ std::shared_ptr<LUS::GuiWindow> mInputEditorWindow;
+
+ //std::shared_ptr<AudioEditor> mAudioEditorWindow;
+ //std::shared_ptr<GameControlEditor::GameControlEditorWindow> mGameControlEditorWindow;
+ //std::shared_ptr<CosmeticsEditorWindow> mCosmeticsEditorWindow;
+ //std::shared_ptr<ActorViewerWindow> mActorViewerWindow;
+ //std::shared_ptr<ColViewerWindow> mColViewerWindow;
+ //std::shared_ptr<SaveEditorWindow> mSaveEditorWindow;
+ //std::shared_ptr<DLViewerWindow> mDLViewerWindow;
+ //std::shared_ptr<GameplayStatsWindow> mGameplayStatsWindow;
+ //std::shared_ptr<CheckTracker::CheckTrackerSettingsWindow> mCheckTrackerSettingsWindow;
+ //std::shared_ptr<CheckTracker::CheckTrackerWindow> mCheckTrackerWindow;
+ //std::shared_ptr<EntranceTrackerWindow> mEntranceTrackerWindow;
+ //std::shared_ptr<ItemTrackerSettingsWindow> mItemTrackerSettingsWindow;
+ //std::shared_ptr<ItemTrackerWindow> mItemTrackerWindow;
+ //std::shared_ptr<RandomizerSettingsWindow> mRandomizerSettingsWindow;
+
+ void SetupGuiElements() {
+ auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui();
+
+ mSohMenuBar = std::make_shared<SohMenuBar>("gOpenMenuBar", CVarGetInteger("gOpenMenuBar", 0));
+ gui->SetMenuBar(std::reinterpret_pointer_cast<LUS::GuiMenuBar>(mSohMenuBar));
+
+ if (gui->GetMenuBar() && !gui->GetMenuBar()->IsVisible()) {
+#if defined(__SWITCH__) || defined(__WIIU__)
+ gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Press - to access enhancements menu");
+#else
+ gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Press F1 to access enhancements menu");
+#endif
+ }
+
+ mStatsWindow = gui->GetGuiWindow("Stats");
+ if (mStatsWindow == nullptr) {
+ SPDLOG_ERROR("Could not find stats window");
+ }
+
+ mConsoleWindow = gui->GetGuiWindow("Console");
+ if (mConsoleWindow == nullptr) {
+ SPDLOG_ERROR("Could not find console window");
+ }
+
+ mInputEditorWindow = gui->GetGuiWindow("Input Editor");
+ if (mInputEditorWindow == nullptr) {
+ SPDLOG_ERROR("Could not find input editor window");
+ }
+
+ /*
+ mAudioEditorWindow = std::make_shared<AudioEditor>("gAudioEditor.WindowOpen", "Audio Editor");
+ gui->AddGuiWindow(mAudioEditorWindow);
+ mGameControlEditorWindow = std::make_shared<GameControlEditor::GameControlEditorWindow>("gGameControlEditorEnabled", "Game Control Editor");
+ gui->AddGuiWindow(mGameControlEditorWindow);
+ mCosmeticsEditorWindow = std::make_shared<CosmeticsEditorWindow>("gCosmeticsEditorEnabled", "Cosmetics Editor");
+ gui->AddGuiWindow(mCosmeticsEditorWindow);
+ mActorViewerWindow = std::make_shared<ActorViewerWindow>("gActorViewerEnabled", "Actor Viewer");
+ gui->AddGuiWindow(mActorViewerWindow);
+ mColViewerWindow = std::make_shared<ColViewerWindow>("gCollisionViewerEnabled", "Collision Viewer");
+ gui->AddGuiWindow(mColViewerWindow);
+ mSaveEditorWindow = std::make_shared<SaveEditorWindow>("gSaveEditorEnabled", "Save Editor");
+ gui->AddGuiWindow(mSaveEditorWindow);
+ mDLViewerWindow = std::make_shared<DLViewerWindow>("gDLViewerEnabled", "Display List Viewer");
+ gui->AddGuiWindow(mDLViewerWindow);
+ mGameplayStatsWindow = std::make_shared<GameplayStatsWindow>("gGameplayStatsEnabled", "Gameplay Stats");
+ gui->AddGuiWindow(mGameplayStatsWindow);
+ mCheckTrackerWindow = std::make_shared<CheckTracker::CheckTrackerWindow>("gCheckTrackerEnabled", "Check Tracker");
+ gui->AddGuiWindow(mCheckTrackerWindow);
+ mCheckTrackerSettingsWindow = std::make_shared<CheckTracker::CheckTrackerSettingsWindow>("gCheckTrackerSettingsEnabled", "Check Tracker Settings");
+ gui->AddGuiWindow(mCheckTrackerSettingsWindow);
+ mEntranceTrackerWindow = std::make_shared<EntranceTrackerWindow>("gEntranceTrackerEnabled","Entrance Tracker");
+ gui->AddGuiWindow(mEntranceTrackerWindow);
+ mItemTrackerWindow = std::make_shared<ItemTrackerWindow>("gItemTrackerEnabled", "Item Tracker");
+ gui->AddGuiWindow(mItemTrackerWindow);
+ mItemTrackerSettingsWindow = std::make_shared<ItemTrackerSettingsWindow>("gItemTrackerSettingsEnabled", "Item Tracker Settings");
+ gui->AddGuiWindow(mItemTrackerSettingsWindow);
+ mRandomizerSettingsWindow = std::make_shared<RandomizerSettingsWindow>("gRandomizerSettingsEnabled", "Randomizer Settings");
+ gui->AddGuiWindow(mRandomizerSettingsWindow);
+ */
+ }
+
+ void Destroy() {
+ //mRandomizerSettingsWindow = nullptr;
+ //mItemTrackerWindow = nullptr;
+ //mItemTrackerSettingsWindow = nullptr;
+ //mEntranceTrackerWindow = nullptr;
+ //mCheckTrackerWindow = nullptr;
+ //mCheckTrackerSettingsWindow = nullptr;
+ //mGameplayStatsWindow = nullptr;
+ //mDLViewerWindow = nullptr;
+ //mSaveEditorWindow = nullptr;
+ //mColViewerWindow = nullptr;
+ //mActorViewerWindow = nullptr;
+ //mCosmeticsEditorWindow = nullptr;
+ //mGameControlEditorWindow = nullptr;
+ //mAudioEditorWindow = nullptr;
+ //mInputEditorWindow = nullptr;
+ mStatsWindow = nullptr;
+ mConsoleWindow = nullptr;
+ mSohMenuBar = nullptr;
+ }
+}
diff --git a/mm/2s2h/SohGui.hpp b/mm/2s2h/SohGui.hpp
new file mode 100644
index 000000000..8e5fc9447
--- /dev/null
+++ b/mm/2s2h/SohGui.hpp
@@ -0,0 +1,42 @@
+//
+// SohGui.hpp
+// soh
+//
+// Created by David Chavez on 24.08.22.
+//
+
+#ifndef SohGui_hpp
+#define SohGui_hpp
+
+#include <stdio.h>
+#include "SohMenuBar.h"
+//#include "Enhancements/audio/AudioEditor.h"
+//#include "Enhancements/controls/GameControlEditor.h"
+//#include "Enhancements/cosmetics/CosmeticsEditor.h"
+//#include "Enhancements/debugger/actorViewer.h"
+//#include "Enhancements/debugger/colViewer.h"
+//#include "Enhancements/debugger/debugSaveEditor.h"
+//#include "Enhancements/debugger/dlViewer.h"
+//#include "Enhancements/gameplaystatswindow.h"
+//#include "Enhancements/randomizer/randomizer_check_tracker.h"
+//#include "Enhancements/randomizer/randomizer_entrance_tracker.h"
+//#include "Enhancements/randomizer/randomizer_item_tracker.h"
+//#include "Enhancements/randomizer/randomizer_settings_window.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void enableBetaQuest();
+ void disableBetaQuest();
+#ifdef __cplusplus
+}
+#endif
+
+namespace SohGui {
+ void SetupHooks();
+ void SetupGuiElements();
+ void Draw();
+ void Destroy();
+}
+
+#endif /* SohGui_hpp */
diff --git a/mm/2s2h/SohMenuBar.cpp b/mm/2s2h/SohMenuBar.cpp
new file mode 100644
index 000000000..1192cd2bf
--- /dev/null
+++ b/mm/2s2h/SohMenuBar.cpp
@@ -0,0 +1,563 @@
+#include "SohMenuBar.h"
+#include "ImGui/imgui.h"
+#include "public/bridge/consolevariablebridge.h"
+#include <libultraship/libultraship.h>
+#include "UIWidgets.hpp"
+//#include "include/z64audio.h"
+//#include "OTRGlobals.h"
+#include "z64.h"
+//#include "Enhancements/game-interactor/GameInteractor.h"
+//#include "soh/Enhancements/presets.h"
+//#include "soh/Enhancements/mods.h"
+//#include "Enhancements/cosmetics/authenticGfxPatches.h"
+#ifdef ENABLE_CROWD_CONTROL
+#include "Enhancements/crowd-control/CrowdControl.h"
+#endif
+
+
+//#include "Enhancements/audio/AudioEditor.h"
+//#include "Enhancements/controls/GameControlEditor.h"
+//#include "Enhancements/cosmetics/CosmeticsEditor.h"
+//#include "Enhancements/debugger/actorViewer.h"
+//#include "Enhancements/debugger/colViewer.h"
+//#include "Enhancements/debugger/debugSaveEditor.h"
+//#include "Enhancements/debugger/dlViewer.h"
+//#include "Enhancements/gameplaystatswindow.h"
+//#include "Enhancements/randomizer/randomizer_check_tracker.h"
+//#include "Enhancements/randomizer/randomizer_entrance_tracker.h"
+//#include "Enhancements/randomizer/randomizer_item_tracker.h"
+//#include "Enhancements/randomizer/randomizer_settings_window.h"
+
+extern bool ShouldClearTextureCacheAtEndOfFrame;
+extern bool isBetaQuestEnabled;
+
+extern "C" PlayState* gPlayState;
+
+enum SeqPlayers {
+ /* 0 */ SEQ_BGM_MAIN,
+ /* 1 */ SEQ_FANFARE,
+ /* 2 */ SEQ_SFX,
+ /* 3 */ SEQ_BGM_SUB,
+ /* 4 */ SEQ_MAX
+};
+
+std::string GetWindowButtonText(const char* text, bool menuOpen) {
+ char buttonText[100] = "";
+ if (menuOpen) {
+ strcat(buttonText, ICON_FA_CHEVRON_RIGHT " ");
+ }
+ strcat(buttonText, text);
+ if (!menuOpen) { strcat(buttonText, " "); }
+ return buttonText;
+}
+
+ static const char* filters[3] = {
+#ifdef __WIIU__
+ "",
+#else
+ "Three-Point",
+#endif
+ "Linear", "None"
+ };
+
+ static const char* chestStyleMatchesContentsOptions[4] = { "Disabled", "Both", "Texture Only", "Size Only" };
+ static const char* bunnyHoodOptions[3] = { "Disabled", "Faster Run & Longer Jump", "Faster Run" };
+ static const char* mirroredWorldModes[9] = {
+ "Disabled", "Always", "Random", "Random (Seeded)", "Dungeons",
+ "Dungeons (Vanilla)", "Dungeons (MQ)", "Dungeons Random", "Dungeons Random (Seeded)",
+ };
+ static const char* enemyRandomizerModes[3] = { "Disabled", "Random", "Random (Seeded)" };
+ static const char* allPowers[9] = {
+ "Vanilla (1x)",
+ "Double (2x)",
+ "Quadruple (4x)",
+ "Octuple (8x)",
+ "Foolish (16x)",
+ "Ridiculous (32x)",
+ "Merciless (64x)",
+ "Pure Torture (128x)",
+ "OHKO (256x)" };
+ static const char* subPowers[8] = { allPowers[0], allPowers[1], allPowers[2], allPowers[3], allPowers[4], allPowers[5], allPowers[6], allPowers[7] };
+ static const char* subSubPowers[7] = { allPowers[0], allPowers[1], allPowers[2], allPowers[3], allPowers[4], allPowers[5], allPowers[6] };
+ static const char* zFightingOptions[3] = { "Disabled", "Consistent Vanish", "No Vanish" };
+ static const char* autosaveLabels[6] = { "Off", "New Location + Major Item", "New Location + Any Item", "New Location", "Major Item", "Any Item" };
+ static const char* DebugSaveFileModes[3] = { "Off", "Vanilla", "Maxed" };
+ static const char* FastFileSelect[5] = { "File N.1", "File N.2", "File N.3", "Zelda Map Select (require OoT Debug Mode)", "File select" };
+ static const char* DekuStickCheat[3] = { "Normal", "Unbreakable", "Unbreakable + Always on Fire" };
+ static const char* bonkDamageValues[8] = {
+ "No Damage",
+ "0.25 Heart",
+ "0.5 Heart",
+ "1 Heart",
+ "2 Hearts",
+ "4 Hearts",
+ "8 Hearts",
+ "OHKO"
+ };
+ static const char* timeTravelOptions[3] = { "Disabled", "Ocarina of Time", "Any Ocarina" };
+
+extern "C" SaveContext gSaveContext;
+
+namespace SohGui {
+
+void DrawMenuBarIcon() {
+ static bool gameIconLoaded = false;
+ if (!gameIconLoaded) {
+ LUS::Context::GetInstance()->GetWindow()->GetGui()->LoadTexture("Game_Icon", "textures/icons/gIcon.png");
+ gameIconLoaded = true;
+ }
+
+ if (LUS::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("Game_Icon")) {
+#ifdef __SWITCH__
+ ImVec2 iconSize = ImVec2(20.0f, 20.0f);
+ float posScale = 1.0f;
+#elif defined(__WIIU__)
+ ImVec2 iconSize = ImVec2(16.0f * 2, 16.0f * 2);
+ float posScale = 2.0f;
+#else
+ ImVec2 iconSize = ImVec2(16.0f, 16.0f);
+ float posScale = 1.0f;
+#endif
+ ImGui::SetCursorPos(ImVec2(5, 2.5f) * posScale);
+ ImGui::Image(LUS::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("Game_Icon"), iconSize);
+ ImGui::SameLine();
+ ImGui::SetCursorPos(ImVec2(25, 0) * posScale);
+ }
+}
+
+void DrawShipMenu() {
+ if (ImGui::BeginMenu("Ship")) {
+ if (ImGui::MenuItem("Hide Menu Bar",
+#if !defined(__SWITCH__) && !defined(__WIIU__)
+ "F1"
+#else
+ "[-]"
+#endif
+ )) {
+ LUS::Context::GetInstance()->GetWindow()->GetGui()->GetMenuBar()->ToggleVisibility();
+ }
+ UIWidgets::Spacer(0);
+#if !defined(__SWITCH__) && !defined(__WIIU__)
+ if (ImGui::MenuItem("Toggle Fullscreen", "F11")) {
+ LUS::Context::GetInstance()->GetWindow()->ToggleFullscreen();
+ }
+ UIWidgets::Spacer(0);
+#endif
+ if (ImGui::MenuItem("Reset",
+#ifdef __APPLE__
+ "Command-R"
+#elif !defined(__SWITCH__) && !defined(__WIIU__)
+ "Ctrl+R"
+#else
+ ""
+#endif
+ )) {
+ std::reinterpret_pointer_cast<LUS::ConsoleWindow>(
+ LUS::Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Console"))
+ ->Dispatch("reset");
+ }
+#if !defined(__SWITCH__) && !defined(__WIIU__)
+ UIWidgets::Spacer(0);
+ if (ImGui::MenuItem("Open App Files Folder")) {
+ std::string filesPath = LUS::Context::GetInstance()->GetAppDirectoryPath();
+ SDL_OpenURL(std::string("file:///" + std::filesystem::absolute(filesPath).string()).c_str());
+ }
+ UIWidgets::Spacer(0);
+
+ if (ImGui::MenuItem("Quit")) {
+ LUS::Context::GetInstance()->GetWindow()->Close();
+ }
+#endif
+ ImGui::EndMenu();
+ }
+}
+
+extern std::shared_ptr<LUS::GuiWindow> mInputEditorWindow;
+// extern std::shared_ptr<GameControlEditor::GameControlEditorWindow> mGameControlEditorWindow;
+
+void DrawSettingsMenu() {
+ if (ImGui::BeginMenu("Settings")) {
+ if (ImGui::BeginMenu("Audio")) {
+ UIWidgets::PaddedEnhancementSliderFloat("Master Volume: %d %%", "##Master_Vol", "gGameMasterVolume", 0.0f,
+ 1.0f, "", 1.0f, true, true, false, true);
+ if (UIWidgets::PaddedEnhancementSliderFloat("Main Music Volume: %d %%", "##Main_Music_Vol",
+ "gMainMusicVolume", 0.0f, 1.0f, "", 1.0f, true, true, false,
+ true)) {
+ // Audio_SetGameVolume(SEQ_BGM_MAIN, CVarGetFloat("gMainMusicVolume", 1.0f));
+ }
+ if (UIWidgets::PaddedEnhancementSliderFloat("Sub Music Volume: %d %%", "##Sub_Music_Vol", "gSubMusicVolume",
+ 0.0f, 1.0f, "", 1.0f, true, true, false, true)) {
+ // Audio_SetGameVolume(SEQ_BGM_SUB, CVarGetFloat("gSubMusicVolume", 1.0f));
+ }
+ if (UIWidgets::PaddedEnhancementSliderFloat("Sound Effects Volume: %d %%", "##Sound_Effect_Vol",
+ "gSFXMusicVolume", 0.0f, 1.0f, "", 1.0f, true, true, false,
+ true)) {
+ // Audio_SetGameVolume(SEQ_SFX, CVarGetFloat("gSFXMusicVolume", 1.0f));
+ }
+ if (UIWidgets::PaddedEnhancementSliderFloat("Fanfare Volume: %d %%", "##Fanfare_Vol", "gFanfareVolume",
+ 0.0f, 1.0f, "", 1.0f, true, true, false, true)) {
+ // Audio_SetGameVolume(SEQ_FANFARE, CVarGetFloat("gFanfareVolume", 1.0f));
+ }
+
+ static std::unordered_map<LUS::AudioBackend, const char*> audioBackendNames = {
+ { LUS::AudioBackend::WASAPI, "Windows Audio Session API" },
+ { LUS::AudioBackend::PULSE, "PulseAudio" },
+ { LUS::AudioBackend::SDL, "SDL" },
+ };
+
+ ImGui::Text("Audio API (Needs reload)");
+ auto currentAudioBackend = LUS::Context::GetInstance()->GetAudio()->GetAudioBackend();
+
+ if (LUS::Context::GetInstance()->GetAudio()->GetAvailableAudioBackends()->size() <= 1) {
+ UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f);
+ }
+ if (ImGui::BeginCombo("##AApi", audioBackendNames[currentAudioBackend])) {
+ for (uint8_t i = 0; i < LUS::Context::GetInstance()->GetAudio()->GetAvailableAudioBackends()->size();
+ i++) {
+ auto backend = LUS::Context::GetInstance()->GetAudio()->GetAvailableAudioBackends()->data()[i];
+ if (ImGui::Selectable(audioBackendNames[backend], backend == currentAudioBackend)) {
+ LUS::Context::GetInstance()->GetAudio()->SetAudioBackend(backend);
+ }
+ }
+ ImGui::EndCombo();
+ }
+ if (LUS::Context::GetInstance()->GetAudio()->GetAvailableAudioBackends()->size() <= 1) {
+ UIWidgets::ReEnableComponent("");
+ }
+
+ ImGui::EndMenu();
+ }
+
+ UIWidgets::Spacer(0);
+
+ if (ImGui::BeginMenu("Controller")) {
+ //ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(12.0f, 6.0f));
+ //ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0f, 0.0f));
+ //ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
+ //ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.22f, 0.38f, 0.56f, 1.0f));
+ if (mInputEditorWindow) {
+ if (ImGui::Button(
+ GetWindowButtonText("Controller Mapping", CVarGetInteger("gControllerConfigurationEnabled", 0))
+ .c_str(),
+ ImVec2(-1.0f, 0.0f))) {
+ mInputEditorWindow->ToggleVisibility();
+ }
+ }
+ // if (mGameControlEditorWindow) {
+ // if (ImGui::Button(GetWindowButtonText("Additional Controller Options",
+ // CVarGetInteger("gGameControlEditorEnabled", 0)).c_str(), ImVec2(-1.0f, 0.0f))) {
+ // mGameControlEditorWindow->ToggleVisibility();
+ //}
+
+ ImGui::EndMenu();
+
+ }
+ UIWidgets::PaddedSeparator();
+ //ImGui::PopStyleColor(1);
+ //ImGui::PopStyleVar(3);
+#ifndef __SWITCH__
+ UIWidgets::EnhancementCheckbox("Menubar Controller Navigation", "gControlNav");
+ UIWidgets::Tooltip("Allows controller navigation of the SOH menu bar (Settings, Enhancements,...)\nCAUTION: "
+ "This will disable game inputs while the menubar is visible.\n\nD-pad to move between "
+ "items, A to select, and X to grab focus on the menu bar");
+#endif
+ UIWidgets::PaddedEnhancementCheckbox("Show Inputs", "gInputEnabled", true, false);
+ UIWidgets::Tooltip("Shows currently pressed inputs on the bottom right of the screen");
+ UIWidgets::PaddedEnhancementSliderFloat("Input Scale: %.1f", "##Input", "gInputScale", 1.0f, 3.0f, "", 1.0f,
+ false, true, true, false);
+ UIWidgets::Tooltip("Sets the on screen size of the displayed inputs from the Show Inputs setting");
+ UIWidgets::PaddedEnhancementSliderInt("Simulated Input Lag: %d frames", "##SimulatedInputLag",
+ "gSimulatedInputLag", 0, 6, "", 0, true, true, false);
+ UIWidgets::Tooltip("Buffers your inputs to be executed a specified amount of frames later");
+
+ ImGui::EndMenu();
+ }
+
+ UIWidgets::Spacer(0);
+
+ if (ImGui::BeginMenu("Graphics")) {
+#ifndef __APPLE__
+ if (UIWidgets::EnhancementSliderFloat("Internal Resolution: %d %%", "##IMul", "gInternalResolution", 0.5f, 2.0f,
+ "", 1.0f, true)) {
+ LUS::Context::GetInstance()->GetWindow()->SetResolutionMultiplier(CVarGetFloat("gInternalResolution", 1));
+ };
+ UIWidgets::Tooltip("Multiplies your output resolution by the value inputted, as a more intensive but effective "
+ "form of anti-aliasing");
+#endif
+#ifndef __WIIU__
+ if (UIWidgets::PaddedEnhancementSliderInt("MSAA: %d", "##IMSAA", "gMSAAValue", 1, 8, "", 1, true, true,
+ false)) {
+ LUS::Context::GetInstance()->GetWindow()->SetMsaaLevel(CVarGetInteger("gMSAAValue", 1));
+ };
+ UIWidgets::Tooltip("Activates multi-sample anti-aliasing when above 1x up to 8x for 8 samples for every pixel");
+#endif
+
+ { // FPS Slider
+ const int minFps = 20;
+ static int maxFps;
+ if (LUS::Context::GetInstance()->GetWindow()->GetWindowBackend() == LUS::WindowBackend::DX11) {
+ maxFps = 360;
+ } else {
+ maxFps = LUS::Context::GetInstance()->GetWindow()->GetCurrentRefreshRate();
+ }
+ // int currentFps = fmax(fmin(OTRGlobals::Instance->GetInterpolationFPS(), maxFps), minFps);
+ int currentFps = 20;
+ bool matchingRefreshRate =
+ CVarGetInteger("gMatchRefreshRate", 0) &&
+ LUS::Context::GetInstance()->GetWindow()->GetWindowBackend() != LUS::WindowBackend::DX11;
+ UIWidgets::PaddedEnhancementSliderInt((currentFps == 20) ? "FPS: Original (20)" : "FPS: %d",
+ "##FPSInterpolation", "gInterpolationFPS", minFps, maxFps, "", 20,
+ true, true, false, matchingRefreshRate);
+ if (LUS::Context::GetInstance()->GetWindow()->GetWindowBackend() == LUS::WindowBackend::DX11) {
+ UIWidgets::Tooltip(
+ "Uses Matrix Interpolation to create extra frames, resulting in smoother graphics. This is purely "
+ "visual and does not impact game logic, execution of glitches etc.\n\n"
+ "A higher target FPS than your monitor's refresh rate will waste resources, and might give a worse "
+ "result.");
+ } else {
+ UIWidgets::Tooltip(
+ "Uses Matrix Interpolation to create extra frames, resulting in smoother graphics. This is purely "
+ "visual and does not impact game logic, execution of glitches etc.");
+ }
+ } // END FPS Slider
+
+ if (LUS::Context::GetInstance()->GetWindow()->GetWindowBackend() == LUS::WindowBackend::DX11) {
+ UIWidgets::Spacer(0);
+ if (ImGui::Button("Match Refresh Rate")) {
+ int hz = LUS::Context::GetInstance()->GetWindow()->GetCurrentRefreshRate();
+ if (hz >= 20 && hz <= 360) {
+ CVarSetInteger("gInterpolationFPS", hz);
+ LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
+ }
+ }
+ } else {
+ UIWidgets::PaddedEnhancementCheckbox("Match Refresh Rate", "gMatchRefreshRate", true, false);
+ }
+ UIWidgets::Tooltip("Matches interpolation value to the current game's window refresh rate");
+
+ if (LUS::Context::GetInstance()->GetWindow()->GetWindowBackend() == LUS::WindowBackend::DX11) {
+ UIWidgets::PaddedEnhancementSliderInt(
+ CVarGetInteger("gExtraLatencyThreshold", 80) == 0 ? "Jitter fix: Off" : "Jitter fix: >= %d FPS",
+ "##ExtraLatencyThreshold", "gExtraLatencyThreshold", 0, 360, "", 80, true, true, false);
+ UIWidgets::Tooltip("When Interpolation FPS setting is at least this threshold, add one frame of input lag "
+ "(e.g. 16.6 ms for 60 FPS) in order to avoid jitter. This setting allows the CPU to "
+ "work on one frame while GPU works on the previous frame.\nThis setting should be used "
+ "when your computer is too slow to do CPU + GPU work in time.");
+ }
+
+ UIWidgets::PaddedSeparator(true, true, 3.0f, 3.0f);
+
+ static std::unordered_map<LUS::WindowBackend, const char*> windowBackendNames = {
+ { LUS::WindowBackend::DX11, "DirectX" },
+ { LUS::WindowBackend::SDL_OPENGL, "OpenGL" },
+ { LUS::WindowBackend::SDL_METAL, "Metal" },
+ { LUS::WindowBackend::GX2, "GX2" }
+ };
+
+ ImGui::Text("Renderer API (Needs reload)");
+ LUS::WindowBackend runningWindowBackend = LUS::Context::GetInstance()->GetWindow()->GetWindowBackend();
+ LUS::WindowBackend configWindowBackend;
+ int configWindowBackendId = LUS::Context::GetInstance()->GetConfig()->GetInt("Window.Backend.Id", -1);
+ if (configWindowBackendId != -1 &&
+ configWindowBackendId < static_cast<int>(LUS::WindowBackend::BACKEND_COUNT)) {
+ configWindowBackend = static_cast<LUS::WindowBackend>(configWindowBackendId);
+ } else {
+ configWindowBackend = runningWindowBackend;
+ }
+
+ if (LUS::Context::GetInstance()->GetWindow()->GetAvailableWindowBackends()->size() <= 1) {
+ UIWidgets::DisableComponent(ImGui::GetStyle().Alpha * 0.5f);
+ }
+ if (ImGui::BeginCombo("##RApi", windowBackendNames[configWindowBackend])) {
+ for (size_t i = 0; i < LUS::Context::GetInstance()->GetWindow()->GetAvailableWindowBackends()->size();
+ i++) {
+ auto backend = LUS::Context::GetInstance()->GetWindow()->GetAvailableWindowBackends()->data()[i];
+ if (ImGui::Selectable(windowBackendNames[backend], backend == configWindowBackend)) {
+ LUS::Context::GetInstance()->GetConfig()->SetInt("Window.Backend.Id", static_cast<int>(backend));
+ LUS::Context::GetInstance()->GetConfig()->SetString("Window.Backend.Name",
+ windowBackendNames[backend]);
+ LUS::Context::GetInstance()->GetConfig()->Save();
+ }
+ }
+ ImGui::EndCombo();
+ }
+ if (LUS::Context::GetInstance()->GetWindow()->GetAvailableWindowBackends()->size() <= 1) {
+ UIWidgets::ReEnableComponent("");
+ }
+
+ if (LUS::Context::GetInstance()->GetWindow()->CanDisableVerticalSync()) {
+ UIWidgets::PaddedEnhancementCheckbox("Enable Vsync", "gVsyncEnabled", true, false);
+ }
+
+ if (LUS::Context::GetInstance()->GetWindow()->SupportsWindowedFullscreen()) {
+ UIWidgets::PaddedEnhancementCheckbox("Windowed fullscreen", "gSdlWindowedFullscreen", true, false);
+ }
+
+ if (LUS::Context::GetInstance()->GetWindow()->GetGui()->SupportsViewports()) {
+ UIWidgets::PaddedEnhancementCheckbox("Allow multi-windows", "gEnableMultiViewports", true, false, false, "",
+ UIWidgets::CheckboxGraphics::Cross, true);
+ UIWidgets::Tooltip("Allows windows to be able to be dragged off of the main game window. Requires a reload "
+ "to take effect.");
+ }
+
+ // If more filters are added to LUS, make sure to add them to the filters list here
+ ImGui::Text("Texture Filter (Needs reload)");
+
+ UIWidgets::EnhancementCombobox("gTextureFilter", filters, FILTER_THREE_POINT);
+
+ UIWidgets::Spacer(0);
+
+ LUS::Context::GetInstance()->GetWindow()->GetGui()->GetGameOverlay()->DrawSettings();
+
+ ImGui::EndMenu();
+ }
+
+ UIWidgets::Spacer(0);
+
+ if (ImGui::BeginMenu("Languages")) {
+ UIWidgets::PaddedEnhancementCheckbox("Translate Title Screen", "gTitleScreenTranslation");
+ if (UIWidgets::EnhancementRadioButton("English", "gLanguages", LANGUAGE_ENG)) {
+ // GameInteractor::Instance->ExecuteHooks<GameInteractor::OnSetGameLanguage>();
+ }
+ if (UIWidgets::EnhancementRadioButton("German", "gLanguages", LANGUAGE_GER)) {
+ // GameInteractor::Instance->ExecuteHooks<GameInteractor::OnSetGameLanguage>();
+ }
+ // if (UIWidgets::EnhancementRadioButton("French", "gLanguages", LANGUAGE_FRA)) {
+ // GameInteractor::Instance->ExecuteHooks<GameInteractor::OnSetGameLanguage>();
+ //}
+ ImGui::EndMenu();
+ }
+
+ UIWidgets::Spacer(0);
+
+ if (ImGui::BeginMenu("Accessibility")) {
+#if defined(_WIN32) || defined(__APPLE__)
+ UIWidgets::PaddedEnhancementCheckbox("Text to Speech", "gA11yTTS");
+ UIWidgets::Tooltip("Enables text to speech for in game dialog");
+#endif
+ UIWidgets::PaddedEnhancementCheckbox("Disable Idle Camera Re-Centering", "gA11yDisableIdleCam");
+ UIWidgets::Tooltip("Disables the automatic re-centering of the camera when idle.");
+
+ ImGui::EndMenu();
+ }
+
+ //ImGui::EndMenu();
+}
+
+extern std::shared_ptr<LUS::GuiWindow> mStatsWindow;
+extern std::shared_ptr<LUS::GuiWindow> mConsoleWindow;
+// extern std::shared_ptr<SaveEditorWindow> mSaveEditorWindow;
+// extern std::shared_ptr<ColViewerWindow> mColViewerWindow;
+// extern std::shared_ptr<ActorViewerWindow> mActorViewerWindow;
+// extern std::shared_ptr<DLViewerWindow> mDLViewerWindow;
+
+void DrawDeveloperToolsMenu() {
+ if (ImGui::BeginMenu("Developer Tools")) {
+ UIWidgets::EnhancementCheckbox("OoT Debug Mode", "gDebugEnabled");
+ UIWidgets::Tooltip("Enables Debug Mode, allowing you to select maps with L + R + Z, noclip with L + D-pad "
+ "Right, and open the debug menu with L on the pause screen");
+ if (CVarGetInteger("gDebugEnabled", 0)) {
+ ImGui::Text("Debug Save File Mode:");
+ UIWidgets::EnhancementCombobox("gDebugSaveFileMode", DebugSaveFileModes, 1);
+ UIWidgets::Tooltip("Changes the behaviour of debug file select creation (creating a save file on slot 1 "
+ "with debug mode on)\n"
+ "- Off: The debug save file will be a normal savefile\n"
+ "- Vanilla: The debug save file will be the debug save file from the original game\n"
+ "- Maxed: The debug save file will be a save file with all of the items & upgrades");
+ }
+ UIWidgets::PaddedEnhancementCheckbox("OoT Skulltula Debug", "gSkulltulaDebugEnabled", true, false);
+ UIWidgets::Tooltip("Enables Skulltula Debug, when moving the cursor in the menu above various map icons (boss "
+ "key, compass, map screen locations, etc) will set the GS bits in that area.\nUSE WITH "
+ "CAUTION AS IT DOES NOT UPDATE THE GS COUNT.");
+ UIWidgets::PaddedEnhancementCheckbox("Fast File Select", "gSkipLogoTitle", true, false);
+ UIWidgets::Tooltip(
+ "Load the game to the selected menu or file\n\"Zelda Map Select\" require debug mode else you will "
+ "fallback to File choose menu\nUsing a file number that don't have save will create a save file only if "
+ "you toggle on \"Create a new save if none ?\" else it will bring you to the File choose menu");
+ if (CVarGetInteger("gSkipLogoTitle", 0)) {
+ ImGui::Text("Loading:");
+ UIWidgets::EnhancementCombobox("gSaveFileID", FastFileSelect, 0);
+ };
+ UIWidgets::PaddedEnhancementCheckbox("Better Debug Warp Screen", "gBetterDebugWarpScreen", true, false);
+ UIWidgets::Tooltip("Optimized debug warp screen, with the added ability to chose entrances and time of day");
+ UIWidgets::PaddedEnhancementCheckbox("Debug Warp Screen Translation", "gDebugWarpScreenTranslation", true,
+ false, false, "", UIWidgets::CheckboxGraphics::Cross, true);
+ UIWidgets::Tooltip("Translate the Debug Warp Screen based on the game language");
+ UIWidgets::PaddedSeparator();
+ ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(12.0f, 6.0f));
+ ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0, 0));
+ ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
+ ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.22f, 0.38f, 0.56f, 1.0f));
+ if (mStatsWindow) {
+ if (ImGui::Button(GetWindowButtonText("Stats", CVarGetInteger("gStatsEnabled", 0)).c_str(),
+ ImVec2(-1.0f, 0.0f))) {
+ mStatsWindow->ToggleVisibility();
+ }
+ UIWidgets::Tooltip("Shows the stats window, with your FPS and frametimes, and the OS you're playing on");
+ }
+ UIWidgets::Spacer(0);
+ if (mConsoleWindow) {
+ if (ImGui::Button(GetWindowButtonText("Console", CVarGetInteger("gConsoleEnabled", 0)).c_str(),
+ ImVec2(-1.0f, 0.0f))) {
+ mConsoleWindow->ToggleVisibility();
+ }
+ UIWidgets::Tooltip(
+ "Enables the console window, allowing you to input commands, type help for some examples");
+ }
+ UIWidgets::Spacer(0);
+ // if (mSaveEditorWindow) {
+ // if (ImGui::Button(GetWindowButtonText("Save Editor", CVarGetInteger("gSaveEditorEnabled", 0)).c_str(),
+ // ImVec2(-1.0f, 0.0f))) {
+ // //mSaveEditorWindow->ToggleVisibility();
+ // }
+ // }
+ // UIWidgets::Spacer(0);
+ // if (mColViewerWindow) {
+ // if (ImGui::Button(GetWindowButtonText("Collision Viewer", CVarGetInteger("gCollisionViewerEnabled",
+ // 0)).c_str(), ImVec2(-1.0f, 0.0f))) {
+ // //mColViewerWindow->ToggleVisibility();
+ // }
+ // }
+ // UIWidgets::Spacer(0);
+ // if (mActorViewerWindow) {
+ // if (ImGui::Button(GetWindowButtonText("Actor Viewer", CVarGetInteger("gActorViewerEnabled", 0)).c_str(),
+ // ImVec2(-1.0f, 0.0f))) {
+ // //mActorViewerWindow->ToggleVisibility();
+ // }
+ // }
+ // UIWidgets::Spacer(0);
+ // if (mDLViewerWindow) {
+ // if (ImGui::Button(GetWindowButtonText("Display List Viewer", CVarGetInteger("gDLViewerEnabled",
+ // 0)).c_str(), ImVec2(-1.0f, 0.0f))) {
+ // //mDLViewerWindow->ToggleVisibility();
+ // }
+ // }
+
+ ImGui::PopStyleVar(3);
+ ImGui::PopStyleColor(1);
+
+ ImGui::EndMenu();
+ }
+}
+
+void SohMenuBar::DrawElement() {
+ if (ImGui::BeginMenuBar()) {
+ static ImVec2 sWindowPadding(8.0f, 8.0f);
+
+ ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, sWindowPadding);
+
+ ImGui::SetCursorPosY(0.0f);
+ DrawSettingsMenu();
+
+ ImGui::SetCursorPosY(0.0f);
+
+ DrawDeveloperToolsMenu();
+
+ ImGui::SetCursorPosY(0.0f);
+
+ ImGui::PopStyleVar(1);
+ ImGui::EndMenuBar();
+ }
+}
+}
+ // namespace SohGui \ No newline at end of file
diff --git a/mm/2s2h/SohMenuBar.h b/mm/2s2h/SohMenuBar.h
new file mode 100644
index 000000000..ecfb2764e
--- /dev/null
+++ b/mm/2s2h/SohMenuBar.h
@@ -0,0 +1,16 @@
+#pragma once
+
+//#include <libultraship/libultraship.h>
+#include "window/gui/GuiMenuBar.h"
+#include "window/gui/GuiElement.h"
+
+namespace SohGui {
+class SohMenuBar : public LUS::GuiMenuBar {
+ public:
+ using LUS::GuiMenuBar::GuiMenuBar;
+ protected:
+ void DrawElement() override;
+ void InitElement() override {};
+ void UpdateElement() override {};
+};
+} // namespace SohGui \ No newline at end of file
diff --git a/mm/2s2h/UIWidgets.cpp b/mm/2s2h/UIWidgets.cpp
new file mode 100644
index 000000000..3086f32ad
--- /dev/null
+++ b/mm/2s2h/UIWidgets.cpp
@@ -0,0 +1,716 @@
+//
+// UIWidgets.cpp
+// soh
+//
+// Created by David Chavez on 25.08.22.
+//
+
+#include "UIWidgets.hpp"
+
+#define IMGUI_DEFINE_MATH_OPERATORS
+#include <ImGui/imgui_internal.h>
+#include <libultraship/libultraship.h>
+
+#include <libultraship/libultra/types.h>
+//#include "soh/Enhancements/cosmetics/CosmeticsEditor.h"
+
+namespace UIWidgets {
+
+ // MARK: - Layout Helper
+
+ // Automatically adds newlines to break up text longer than a specified number of characters
+ // Manually included newlines will still be respected and reset the line length
+ // If line is midword when it hits the limit, text should break at the last encountered space
+ char* WrappedText(const char* text, unsigned int charactersPerLine) {
+ std::string newText(text);
+ const size_t tipLength = newText.length();
+ int lastSpace = -1;
+ int currentLineLength = 0;
+ for (unsigned int currentCharacter = 0; currentCharacter < tipLength; currentCharacter++) {
+ if (newText[currentCharacter] == '\n') {
+ currentLineLength = 0;
+ lastSpace = -1;
+ continue;
+ } else if (newText[currentCharacter] == ' ') {
+ lastSpace = currentCharacter;
+ }
+
+ if ((currentLineLength >= charactersPerLine) && (lastSpace >= 0)) {
+ newText[lastSpace] = '\n';
+ currentLineLength = currentCharacter - lastSpace - 1;
+ lastSpace = -1;
+ }
+ currentLineLength++;
+ }
+
+ return strdup(newText.c_str());
+ }
+
+ char* WrappedText(const std::string& text, unsigned int charactersPerLine) {
+ return WrappedText(text.c_str(), charactersPerLine);
+ }
+
+ void SetLastItemHoverText(const std::string& text) {
+ if (ImGui::IsItemHovered()) {
+ ImGui::BeginTooltip();
+ ImGui::Text("%s", WrappedText(text, 60));
+ ImGui::EndTooltip();
+ }
+ }
+
+ void SetLastItemHoverText(const char* text) {
+ if (ImGui::IsItemHovered()) {
+ ImGui::BeginTooltip();
+ ImGui::Text("%s", WrappedText(text, 60));
+ ImGui::EndTooltip();
+ }
+ }
+
+ // Adds a "?" next to the previous ImGui item with a custom tooltip
+ void InsertHelpHoverText(const std::string& text) {
+ ImGui::SameLine();
+ ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "?");
+ if (ImGui::IsItemHovered()) {
+ ImGui::BeginTooltip();
+ ImGui::Text("%s", WrappedText(text, 60));
+ ImGui::EndTooltip();
+ }
+ }
+
+ void InsertHelpHoverText(const char* text) {
+ ImGui::SameLine();
+ ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "?");
+ if (ImGui::IsItemHovered()) {
+ ImGui::BeginTooltip();
+ ImGui::Text("%s", WrappedText(text, 60));
+ ImGui::EndTooltip();
+ }
+ }
+
+
+ // MARK: - UI Elements
+
+ void Tooltip(const char* text) {
+ if (ImGui::IsItemHovered()) {
+ ImGui::SetTooltip("%s", WrappedText(text));
+ }
+ }
+
+ void Spacer(float height) {
+ ImGui::Dummy(ImVec2(0.0f, height));
+ }
+
+ void PaddedSeparator(bool padTop, bool padBottom, float extraVerticalTopPadding, float extraVerticalBottomPadding) {
+ if (padTop) {
+ Spacer(extraVerticalTopPadding);
+ }
+ ImGui::Separator();
+ if (padBottom) {
+ Spacer(extraVerticalBottomPadding);
+ }
+ }
+
+ void RenderCross(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float sz) {
+ float thickness = ImMax(sz / 5.0f, 1.0f);
+ sz -= thickness * 0.5f;
+ pos += ImVec2(thickness * 0.25f, thickness * 0.25f);
+
+ draw_list->PathLineTo(ImVec2(pos.x, pos.y));
+ draw_list->PathLineTo(ImVec2(pos.x + sz, pos.y + sz));
+ draw_list->PathStroke(col, 0, thickness);
+
+ draw_list->PathLineTo(ImVec2(pos.x + sz, pos.y));
+ draw_list->PathLineTo(ImVec2(pos.x, pos.y + sz));
+ draw_list->PathStroke(col, 0, thickness);
+ }
+
+ bool CustomCheckbox(const char* label, bool* v, bool disabled, CheckboxGraphics disabledGraphic) {
+ ImGuiWindow* window = ImGui::GetCurrentWindow();
+ if (window->SkipItems) {
+ return false;
+ }
+
+ ImGuiContext& g = *GImGui;
+ const ImGuiStyle& style = g.Style;
+ const ImGuiID id = window->GetID(label);
+ const ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true);
+
+ const float square_sz = ImGui::GetFrameHeight();
+ const ImVec2 pos = window->DC.CursorPos;
+ const ImRect total_bb(pos, pos + ImVec2(square_sz + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f));
+ ImGui::ItemSize(total_bb, style.FramePadding.y);
+ if (!ImGui::ItemAdd(total_bb, id)) {
+ IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0));
+ return false;
+ }
+
+ bool hovered, held;
+ bool pressed = ImGui::ButtonBehavior(total_bb, id, &hovered, &held);
+ if (pressed) {
+ *v = !(*v);
+ ImGui::MarkItemEdited(id);
+ }
+
+ const ImRect check_bb(pos, pos + ImVec2(square_sz, square_sz));
+ ImGui::RenderNavHighlight(total_bb, id);
+ ImGui::RenderFrame(check_bb.Min, check_bb.Max, ImGui::GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding);
+ ImU32 check_col = ImGui::GetColorU32(ImGuiCol_CheckMark);
+ ImU32 cross_col = ImGui::GetColorU32(ImVec4(0.50f, 0.50f, 0.50f, 1.00f));
+ bool mixed_value = (g.LastItemData.InFlags & ImGuiItemFlags_MixedValue) != 0;
+ if (mixed_value) {
+ // Undocumented tristate/mixed/indeterminate checkbox (#2644)
+ // This may seem awkwardly designed because the aim is to make ImGuiItemFlags_MixedValue supported by all widgets (not just checkbox)
+ ImVec2 pad(ImMax(1.0f, IM_FLOOR(square_sz / 3.6f)), ImMax(1.0f, IM_FLOOR(square_sz / 3.6f)));
+ window->DrawList->AddRectFilled(check_bb.Min + pad, check_bb.Max - pad, check_col, style.FrameRounding);
+ } else if ((!disabled && *v) || (disabled && disabledGraphic == CheckboxGraphics::Checkmark)) {
+ const float pad = ImMax(1.0f, IM_FLOOR(square_sz / 6.0f));
+ ImGui::RenderCheckMark(window->DrawList, check_bb.Min + ImVec2(pad, pad), check_col, square_sz - pad * 2.0f);
+ } else if (disabled && disabledGraphic == CheckboxGraphics::Cross) {
+ const float pad = ImMax(1.0f, IM_FLOOR(square_sz / 6.0f));
+ RenderCross(window->DrawList, check_bb.Min + ImVec2(pad, pad), cross_col, square_sz - pad * 2.0f);
+ }
+
+ ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y);
+ if (g.LogEnabled) {
+ ImGui::LogRenderedText(&label_pos, mixed_value ? "[~]" : *v ? "[x]" : "[ ]");
+ }
+ if (label_size.x > 0.0f) {
+ ImGui::RenderText(label_pos, label);
+ }
+
+ IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0));
+ return pressed;
+ }
+
+ void ReEnableComponent(const char* disabledTooltipText) {
+ // End of disable region of previous component
+ ImGui::PopStyleVar(1);
+ if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && strcmp(disabledTooltipText, "") != 0) {
+ ImGui::SetTooltip("%s", disabledTooltipText);
+ }
+ ImGui::PopItemFlag();
+ }
+
+ void DisableComponent(const float alpha) {
+ ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
+ ImGui::PushStyleVar(ImGuiStyleVar_Alpha, alpha);
+ }
+
+ bool EnhancementCheckbox(const char* text, const char* cvarName, bool disabled, const char* disabledTooltipText, CheckboxGraphics disabledGraphic, bool defaultValue) {
+ bool changed = false;
+ if (disabled) {
+ DisableComponent(ImGui::GetStyle().Alpha * 0.5f);
+ }
+
+ bool val = (bool)CVarGetInteger(cvarName, defaultValue);
+ if (CustomCheckbox(text, &val, disabled, disabledGraphic)) {
+ CVarSetInteger(cvarName, val);
+ LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
+ changed = true;
+ }
+
+ if (disabled) {
+ ReEnableComponent(disabledTooltipText);
+ }
+ return changed;
+ }
+
+ bool PaddedEnhancementCheckbox(const char* text, const char* cvarName, bool padTop, bool padBottom, bool disabled, const char* disabledTooltipText, CheckboxGraphics disabledGraphic, bool defaultValue) {
+ ImGui::BeginGroup();
+ if (padTop) Spacer(0);
+
+ bool changed = EnhancementCheckbox(text, cvarName, disabled, disabledTooltipText, disabledGraphic, defaultValue);
+
+ if (padBottom) Spacer(0);
+ ImGui::EndGroup();
+ return changed;
+ }
+
+ bool EnhancementCombobox(const char* cvarName, std::span<const char*, std::dynamic_extent> comboArray, uint8_t defaultIndex, bool disabled, const char* disabledTooltipText, uint8_t disabledValue) {
+ bool changed = false;
+ if (defaultIndex <= 0) {
+ defaultIndex = 0;
+ }
+
+ if (disabled) {
+ DisableComponent(ImGui::GetStyle().Alpha * 0.5f);
+ }
+
+ uint8_t selected = CVarGetInteger(cvarName, defaultIndex);
+ std::string comboName = std::string("##") + std::string(cvarName);
+ if (ImGui::BeginCombo(comboName.c_str(), comboArray[selected])) {
+ for (uint8_t i = 0; i < comboArray.size(); i++) {
+ if (strlen(comboArray[i]) > 0) {
+ if (ImGui::Selectable(comboArray[i], i == selected)) {
+ CVarSetInteger(cvarName, i);
+ selected = i;
+ changed = true;
+ LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
+ }
+ }
+ }
+ ImGui::EndCombo();
+ }
+
+ if (disabled) {
+ ReEnableComponent(disabledTooltipText);
+
+ if (disabledValue >= 0 && selected != disabledValue) {
+ CVarSetInteger(cvarName, disabledValue);
+ changed = true;
+ LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
+ }
+ }
+
+ return changed;
+ }
+
+ bool LabeledRightAlignedEnhancementCombobox(const char* label, const char* cvarName, std::span<const char*, std::dynamic_extent> comboArray, uint8_t defaultIndex, bool disabled, const char* disabledTooltipText, uint8_t disabledValue) {
+ ImGui::Text("%s", label);
+ s32 currentValue = CVarGetInteger(cvarName, defaultIndex);
+
+#ifdef __WIIU__
+ ImGui::SameLine(ImGui::GetContentRegionAvail().x - (ImGui::CalcTextSize(comboArray[currentValue]).x + 40.0f));
+ ImGui::PushItemWidth(ImGui::CalcTextSize(comboArray[currentValue]).x + 60.0f);
+#else
+ ImGui::SameLine(ImGui::GetContentRegionAvail().x - (ImGui::CalcTextSize(comboArray[currentValue]).x + 20.0f));
+ ImGui::PushItemWidth(ImGui::CalcTextSize(comboArray[currentValue]).x + 30.0f);
+#endif
+
+ bool changed = EnhancementCombobox(cvarName, comboArray, defaultIndex, disabled, disabledTooltipText, disabledValue);
+
+ ImGui::PopItemWidth();
+ return changed;
+ }
+
+ void PaddedText(const char* text, bool padTop, bool padBottom) {
+ if (padTop) Spacer(0);
+
+ ImGui::Text("%s", text);
+
+ if (padBottom) Spacer(0);
+ }
+
+ bool EnhancementSliderInt(const char* text, const char* id, const char* cvarName, int min, int max, const char* format, int defaultValue, bool PlusMinusButton, bool disabled, const char* disabledTooltipText) {
+ bool changed = false;
+ int val = CVarGetInteger(cvarName, defaultValue);
+
+ if (disabled) {
+ DisableComponent(ImGui::GetStyle().Alpha * 0.5f);
+ }
+
+ ImGui::Text(text, val);
+ Spacer(0);
+
+ ImGui::BeginGroup();
+ if (PlusMinusButton) {
+ std::string MinusBTNName = " - ##" + std::string(cvarName);
+ if (ImGui::Button(MinusBTNName.c_str())) {
+ val--;
+ changed = true;
+ }
+ ImGui::SameLine();
+ ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f);
+ }
+
+ ImGui::PushItemWidth(std::min((ImGui::GetContentRegionAvail().x - (PlusMinusButton ? sliderButtonWidth : 0.0f)), maxSliderWidth));
+ if (ImGui::SliderInt(id, &val, min, max, format, ImGuiSliderFlags_AlwaysClamp))
+ {
+ changed = true;
+ }
+ ImGui::PopItemWidth();
+
+ if (PlusMinusButton) {
+ std::string PlusBTNName = " + ##" + std::string(cvarName);
+ ImGui::SameLine();
+ ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f);
+ if (ImGui::Button(PlusBTNName.c_str())) {
+ val++;
+ changed = true;
+ }
+ }
+ ImGui::EndGroup();
+
+ if (disabled) {
+ ReEnableComponent(disabledTooltipText);
+ }
+
+ if (val < min) {
+ val = min;
+ changed = true;
+ }
+
+ if (val > max) {
+ val = max;
+ changed = true;
+ }
+
+ if (changed) {
+ CVarSetInteger(cvarName, val);
+ LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
+ }
+
+ return changed;
+ }
+
+ bool EnhancementSliderFloat(const char* text, const char* id, const char* cvarName, float min, float max, const char* format, float defaultValue, bool isPercentage, bool PlusMinusButton, bool disabled, const char* disabledTooltipText) {
+ bool changed = false;
+ float val = CVarGetFloat(cvarName, defaultValue);
+
+ if (disabled) {
+ DisableComponent(ImGui::GetStyle().Alpha * 0.5f);
+ }
+
+ if (!isPercentage) {
+ ImGui::Text(text, val);
+ } else {
+ ImGui::Text(text, static_cast<int>(100 * val));
+ }
+ Spacer(0);
+
+ ImGui::BeginGroup();
+ if (PlusMinusButton) {
+ std::string MinusBTNName = " - ##" + std::string(cvarName);
+ if (ImGui::Button(MinusBTNName.c_str())) {
+ if (isPercentage) {
+ val -= 0.01f;
+ } else {
+ val -= 0.1f;
+ }
+ changed = true;
+ }
+ ImGui::SameLine();
+ ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f);
+ }
+
+ ImGui::PushItemWidth(std::min((ImGui::GetContentRegionAvail().x - (PlusMinusButton ? sliderButtonWidth : 0.0f)), maxSliderWidth));
+ if (ImGui::SliderFloat(id, &val, min, max, format, ImGuiSliderFlags_AlwaysClamp)) {
+ if (isPercentage) {
+ val = roundf(val * 100) / 100;
+ }
+ changed = true;
+ }
+ ImGui::PopItemWidth();
+
+ if (PlusMinusButton) {
+ std::string PlusBTNName = " + ##" + std::string(cvarName);
+ ImGui::SameLine();
+ ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 7.0f);
+ if (ImGui::Button(PlusBTNName.c_str())) {
+ if (isPercentage) {
+ val += 0.01f;
+ } else {
+ val += 0.1f;
+ }
+ changed = true;
+ }
+ }
+ ImGui::EndGroup();
+
+ if (disabled) {
+ ReEnableComponent(disabledTooltipText);
+ }
+
+ if (val < min) {
+ val = min;
+ changed = true;
+ }
+
+ if (val > max) {
+ val = max;
+ changed = true;
+ }
+
+ if (changed) {
+ CVarSetFloat(cvarName, val);
+ LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
+ }
+
+ return changed;
+ }
+
+ bool PaddedEnhancementSliderInt(const char* text, const char* id, const char* cvarName, int min, int max, const char* format, int defaultValue, bool PlusMinusButton, bool padTop, bool padBottom, bool disabled, const char* disabledTooltipText) {
+ bool changed = false;
+ ImGui::BeginGroup();
+ if (padTop) Spacer(0);
+
+ changed = EnhancementSliderInt(text, id, cvarName, min, max, format, defaultValue, PlusMinusButton, disabled, disabledTooltipText);
+
+ if (padBottom) Spacer(0);
+ ImGui::EndGroup();
+ return changed;
+ }
+
+ bool PaddedEnhancementSliderFloat(const char* text, const char* id, const char* cvarName, float min, float max, const char* format, float defaultValue, bool isPercentage, bool PlusMinusButton, bool padTop, bool padBottom, bool disabled, const char* disabledTooltipText) {
+ bool changed = false;
+ ImGui::BeginGroup();
+ if (padTop) Spacer(0);
+
+ changed = EnhancementSliderFloat(text, id, cvarName, min, max, format, defaultValue, isPercentage, PlusMinusButton, disabled, disabledTooltipText);
+
+ if (padBottom) Spacer(0);
+ ImGui::EndGroup();
+ return changed;
+ }
+
+ bool EnhancementRadioButton(const char* text, const char* cvarName, int id) {
+ /*Usage :
+ EnhancementRadioButton("My Visible Name","gMyCVarName", MyID);
+ First arg is the visible name of the Radio button
+ Second is the cvar name where MyID will be saved.
+ Note: the CVar name should be the same to each Buddies.
+ Example :
+ EnhancementRadioButton("English", "gLanguages", LANGUAGE_ENG);
+ EnhancementRadioButton("German", "gLanguages", LANGUAGE_GER);
+ EnhancementRadioButton("French", "gLanguages", LANGUAGE_FRA);
+ */
+ std::string make_invisible = "##" + std::string(text) + std::string(cvarName);
+
+ bool ret = false;
+ int val = CVarGetInteger(cvarName, 0);
+ if (ImGui::RadioButton(make_invisible.c_str(), id == val)) {
+ CVarSetInteger(cvarName, id);
+ LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
+ ret = true;
+ }
+ ImGui::SameLine();
+ ImGui::Text("%s", text);
+
+ return ret;
+ }
+
+ bool DrawResetColorButton(const char* cvarName, ImVec4* colors, ImVec4 defaultcolors, bool has_alpha) {
+ bool changed = false;
+ std::string Cvar_RBM = std::string(cvarName) + "RBM";
+ std::string MakeInvisible = "Reset##" + std::string(cvarName) + "Reset";
+ if (ImGui::Button(MakeInvisible.c_str())) {
+ colors->x = defaultcolors.x;
+ colors->y = defaultcolors.y;
+ colors->z = defaultcolors.z;
+ colors->w = has_alpha ? defaultcolors.w : 255.0f;
+
+ Color_RGBA8 colorsRGBA;
+ colorsRGBA.r = defaultcolors.x;
+ colorsRGBA.g = defaultcolors.y;
+ colorsRGBA.b = defaultcolors.z;
+ colorsRGBA.a = has_alpha ? defaultcolors.w : 255.0f;
+
+ CVarSetColor(cvarName, colorsRGBA);
+ CVarSetInteger(Cvar_RBM.c_str(), 0); //On click disable rainbow mode.
+ LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
+ changed = true;
+ }
+ Tooltip("Revert colors to the game's original colors (GameCube version)\nOverwrites previously chosen color");
+ return changed;
+ }
+
+ bool DrawRandomizeColorButton(const char* cvarName, ImVec4* colors) {
+ return false;
+ }
+
+ void DrawLockColorCheckbox(const char* cvarName) {
+ std::string Cvar_Lock = std::string(cvarName) + "Lock";
+ s32 lock = CVarGetInteger(Cvar_Lock.c_str(), 0);
+ std::string FullName = "Lock##" + Cvar_Lock;
+ EnhancementCheckbox(FullName.c_str(), Cvar_Lock.c_str());
+ Tooltip("Prevents this color from being changed upon selecting \"Randomize all\"");
+ }
+
+ void RainbowColor(const char* cvarName, ImVec4* colors) {
+ std::string Cvar_RBM = std::string(cvarName) + "RBM";
+ std::string MakeInvisible = "Rainbow##" + std::string(cvarName) + "Rainbow";
+
+ EnhancementCheckbox(MakeInvisible.c_str(), Cvar_RBM.c_str());
+ Tooltip("Cycles through colors on a timer\nOverwrites previously chosen color");
+ }
+
+ void LoadPickersColors(ImVec4& ColorArray, const char* cvarname, const ImVec4& default_colors, bool has_alpha) {
+ Color_RGBA8 defaultColors;
+ defaultColors.r = default_colors.x;
+ defaultColors.g = default_colors.y;
+ defaultColors.b = default_colors.z;
+ defaultColors.a = default_colors.w;
+
+ Color_RGBA8 cvarColor = CVarGetColor(cvarname, defaultColors);
+
+ ColorArray.x = cvarColor.r / 255.0;
+ ColorArray.y = cvarColor.g / 255.0;
+ ColorArray.z = cvarColor.b / 255.0;
+ ColorArray.w = cvarColor.a / 255.0;
+ }
+
+ bool EnhancementColor(const char* text, const char* cvarName, ImVec4 ColorRGBA, ImVec4 default_colors, bool allow_rainbow, bool has_alpha, bool TitleSameLine) {
+ bool changed = false;
+ LoadPickersColors(ColorRGBA, cvarName, default_colors, has_alpha);
+
+ ImGuiColorEditFlags flags = ImGuiColorEditFlags_None;
+
+ if (!TitleSameLine) {
+ ImGui::Text("%s", text);
+ flags = ImGuiColorEditFlags_NoLabel;
+ }
+
+ ImGui::PushID(cvarName);
+
+ if (!has_alpha) {
+ if (ImGui::ColorEdit3(text, (float*)&ColorRGBA, flags))
+ {
+ Color_RGBA8 colors;
+ colors.r = ColorRGBA.x * 255.0;
+ colors.g = ColorRGBA.y * 255.0;
+ colors.b = ColorRGBA.z * 255.0;
+ colors.a = 255.0;
+
+ CVarSetColor(cvarName, colors);
+ LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
+ changed = true;
+ }
+ }
+ else
+ {
+ flags |= ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_AlphaPreview;
+ if (ImGui::ColorEdit4(text, (float*)&ColorRGBA, flags))
+ {
+ Color_RGBA8 colors;
+ colors.r = ColorRGBA.x * 255.0;
+ colors.g = ColorRGBA.y * 255.0;
+ colors.b = ColorRGBA.z * 255.0;
+ colors.a = ColorRGBA.w * 255.0;
+
+ CVarSetColor(cvarName, colors);
+ LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
+ changed = true;
+ }
+ }
+
+ ImGui::PopID();
+
+ //ImGui::SameLine(); // Removing that one to gain some width spacing on the HUD editor
+ ImGui::PushItemWidth(-FLT_MIN);
+ if (DrawResetColorButton(cvarName, &ColorRGBA, default_colors, has_alpha)) {
+ changed = true;
+ }
+ ImGui::SameLine();
+ if (DrawRandomizeColorButton(cvarName, &ColorRGBA)) {
+ changed = true;
+ }
+ if (allow_rainbow) {
+ if (ImGui::GetContentRegionAvail().x > 185) {
+ ImGui::SameLine();
+ }
+ RainbowColor(cvarName, &ColorRGBA);
+ }
+ DrawLockColorCheckbox(cvarName);
+ ImGui::NewLine();
+ ImGui::PopItemWidth();
+
+ return changed;
+ }
+
+ void DrawFlagArray32(const std::string& name, uint32_t& flags) {
+ ImGui::PushID(name.c_str());
+ for (int32_t flagIndex = 0; flagIndex < 32; flagIndex++) {
+ if ((flagIndex % 8) != 0) {
+ ImGui::SameLine();
+ }
+ ImGui::PushID(flagIndex);
+ uint32_t bitMask = 1 << flagIndex;
+ bool flag = (flags & bitMask) != 0;
+ if (ImGui::Checkbox("##check", &flag)) {
+ if (flag) {
+ flags |= bitMask;
+ } else {
+ flags &= ~bitMask;
+ }
+ }
+ ImGui::PopID();
+ }
+ ImGui::PopID();
+ }
+
+ void DrawFlagArray16(const std::string& name, uint16_t& flags) {
+ ImGui::PushID(name.c_str());
+ for (int16_t flagIndex = 0; flagIndex < 16; flagIndex++) {
+ if ((flagIndex % 8) != 0) {
+ ImGui::SameLine();
+ }
+ ImGui::PushID(flagIndex);
+ uint16_t bitMask = 1 << flagIndex;
+ bool flag = (flags & bitMask) != 0;
+ if (ImGui::Checkbox("##check", &flag)) {
+ if (flag) {
+ flags |= bitMask;
+ } else {
+ flags &= ~bitMask;
+ }
+ }
+ ImGui::PopID();
+ }
+ ImGui::PopID();
+ }
+
+ void DrawFlagArray8(const std::string& name, uint8_t& flags) {
+ ImGui::PushID(name.c_str());
+ for (int8_t flagIndex = 0; flagIndex < 8; flagIndex++) {
+ if ((flagIndex % 8) != 0) {
+ ImGui::SameLine();
+ }
+ ImGui::PushID(flagIndex);
+ uint8_t bitMask = 1 << flagIndex;
+ bool flag = (flags & bitMask) != 0;
+ if (ImGui::Checkbox("##check", &flag)) {
+ if (flag) {
+ flags |= bitMask;
+ } else {
+ flags &= ~bitMask;
+ }
+ }
+ ImGui::PopID();
+ }
+ ImGui::PopID();
+ }
+
+ bool StateButtonEx(const char* str_id, const char* label, ImVec2 size, ImGuiButtonFlags flags) {
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = ImGui::GetCurrentWindow();
+ if (window->SkipItems)
+ return false;
+
+ const ImGuiStyle& style = g.Style;
+ const ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true);
+
+ const ImGuiID id = window->GetID(str_id);
+ const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
+ const float default_size = ImGui::GetFrameHeight();
+ ImGui::ItemSize(size, (size.y >= default_size) ? g.Style.FramePadding.y : -1.0f);
+ if (!ImGui::ItemAdd(bb, id))
+ return false;
+
+ if (g.LastItemData.InFlags & ImGuiItemFlags_ButtonRepeat)
+ flags |= ImGuiButtonFlags_Repeat;
+
+ bool hovered, held;
+ bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held, flags);
+
+ // Render
+ const ImU32 bg_col = ImGui::GetColorU32((held && hovered) ? ImGuiCol_ButtonActive
+ : hovered ? ImGuiCol_ButtonHovered
+ : ImGuiCol_Button);
+ //const ImU32 text_col = ImGui::GetColorU32(ImGuiCol_Text);
+ ImGui::RenderNavHighlight(bb, id);
+ ImGui::RenderFrame(bb.Min, bb.Max, bg_col, true, g.Style.FrameRounding);
+ ImGui::RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, {0.55f, 0.45f}, &bb);
+ /*ImGui::RenderArrow(window->DrawList,
+ bb.Min +
+ ImVec2(ImMax(0.0f, (size.x - g.FontSize) * 0.5f), ImMax(0.0f, (size.y - g.FontSize) * 0.5f)),
+ text_col, dir);*/
+
+ IMGUI_TEST_ENGINE_ITEM_INFO(id, str_id, g.LastItemData.StatusFlags);
+ return pressed;
+ }
+
+ bool StateButton(const char* str_id, const char* label) {
+ float sz = ImGui::GetFrameHeight();
+ return StateButtonEx(str_id, label, ImVec2(sz, sz), ImGuiButtonFlags_None);
+ }
+}
diff --git a/mm/2s2h/UIWidgets.hpp b/mm/2s2h/UIWidgets.hpp
new file mode 100644
index 000000000..b18487977
--- /dev/null
+++ b/mm/2s2h/UIWidgets.hpp
@@ -0,0 +1,101 @@
+//
+// UIWidgets.hpp
+// soh
+//
+// Created by David Chavez on 25.08.22.
+//
+
+#ifndef UIWidgets_hpp
+#define UIWidgets_hpp
+
+#include <string>
+#include <vector>
+#include <span>
+#include <stdint.h>
+#include <ImGui/imgui.h>
+
+namespace UIWidgets {
+
+ struct TextFilters {
+ static int FilterNumbers(ImGuiInputTextCallbackData* data) {
+ if (data->EventChar < 256 && strchr("1234567890", (char)data->EventChar)) {
+ return 0;
+ }
+ return 1;
+ }
+
+ static int FilterAlphaNum(ImGuiInputTextCallbackData* data) {
+ const char* alphanum = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWYZ0123456789";
+ if (data->EventChar < 256 && strchr(alphanum, (char)data->EventChar)) {
+ return 0;
+ }
+ return 1;
+ }
+
+ };
+
+ // MARK: - Enums
+
+ enum class CheckboxGraphics {
+ Cross,
+ Checkmark,
+ None
+ };
+ constexpr float maxSliderWidth = 260.0f;
+#ifdef __SWITCH__
+ constexpr float sliderButtonWidth = 42.0f;
+#elif defined(__WIIU__)
+ constexpr float sliderButtonWidth = 60.0f;
+#else
+ constexpr float sliderButtonWidth = 30.0f;
+#endif
+
+ char* WrappedText(const char* text, unsigned int charactersPerLine = 60);
+ char* WrappedText(const std::string& text, unsigned int charactersPerLine);
+
+ void SetLastItemHoverText(const std::string& text);
+ void SetLastItemHoverText(const char* text);
+
+ void InsertHelpHoverText(const std::string& text);
+ void InsertHelpHoverText(const char* text);
+
+ void Tooltip(const char* text);
+ void Spacer(float height);
+ void PaddedSeparator(bool padTop = true, bool padBottom = true, float extraVerticalTopPadding = 0.0f, float extraVerticalBottomPadding = 0.0f);
+
+ void RenderCross(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float sz);
+ bool CustomCheckbox(const char* label, bool* v, bool disabled, CheckboxGraphics disabledGraphic);
+
+ void ReEnableComponent(const char* disabledTooltipText);
+ void DisableComponent(const float alpha);
+
+ bool EnhancementCheckbox(const char* text, const char* cvarName, bool disabled = false, const char* disabledTooltipText = "", CheckboxGraphics disabledGraphic = CheckboxGraphics::Cross, bool defaultValue = false);
+ bool PaddedEnhancementCheckbox(const char* text, const char* cvarName, bool padTop = true, bool padBottom = true, bool disabled = false, const char* disabledTooltipText = "", CheckboxGraphics disabledGraphic = CheckboxGraphics::Cross, bool defaultValue = false);
+
+ bool EnhancementCombobox(const char* cvarName, std::span<const char*, std::dynamic_extent> comboArray, uint8_t defaultIndex, bool disabled = false, const char* disabledTooltipText = "", uint8_t disabledValue = -1);
+ bool LabeledRightAlignedEnhancementCombobox(const char* label, const char* cvarName, std::span<const char*, std::dynamic_extent> comboArray, uint8_t defaultIndex, bool disabled = false, const char* disabledTooltipText = "", uint8_t disabledValue = -1);
+
+ void PaddedText(const char* text, bool padTop = true, bool padBottom = true);
+
+ bool EnhancementSliderInt(const char* text, const char* id, const char* cvarName, int min, int max, const char* format, int defaultValue = 0, bool PlusMinusButton = true, bool disabled = false, const char* disabledTooltipText = "");
+ bool PaddedEnhancementSliderInt(const char* text, const char* id, const char* cvarName, int min, int max, const char* format, int defaultValue = 0, bool PlusMinusButton = true, bool padTop = true, bool padBottom = true, bool disabled = false, const char* disabledTooltipText = "");
+ bool EnhancementSliderFloat(const char* text, const char* id, const char* cvarName, float min, float max, const char* format, float defaultValue, bool isPercentage, bool PlusMinusButton = true, bool disabled = false, const char* disabledTooltipText = "");
+ bool PaddedEnhancementSliderFloat(const char* text, const char* id, const char* cvarName, float min, float max, const char* format, float defaultValue, bool isPercentage, bool PlusMinusButton = true, bool padTop = true, bool padBottom = true, bool disabled = false, const char* disabledTooltipText = "");
+
+ bool EnhancementRadioButton(const char* text, const char* cvarName, int id);
+
+ bool DrawResetColorButton(const char* cvarName, ImVec4* colors, ImVec4 defaultcolors, bool has_alpha);
+ bool DrawRandomizeColorButton(const char* cvarName, ImVec4* colors);
+ void DrawLockColorCheckbox(const char* cvarName);
+ void RainbowColor(const char* cvarName, ImVec4* colors);
+
+ void LoadPickersColors(ImVec4& ColorArray, const char* cvarname, const ImVec4& default_colors, bool has_alpha);
+ bool EnhancementColor(const char* text, const char* cvarName, ImVec4 ColorRGBA, ImVec4 default_colors, bool allow_rainbow = true, bool has_alpha = false, bool TitleSameLine = false);
+
+ void DrawFlagArray32(const std::string& name, uint32_t& flags);
+ void DrawFlagArray16(const std::string& name, uint16_t& flags);
+ void DrawFlagArray8(const std::string& name, uint8_t& flags);
+ bool StateButton(const char* str_id, const char* label);
+}
+
+#endif /* UIWidgets_hpp */
diff --git a/mm/2s2h/gu_pc.c b/mm/2s2h/gu_pc.c
new file mode 100644
index 000000000..be1954a5d
--- /dev/null
+++ b/mm/2s2h/gu_pc.c
@@ -0,0 +1,88 @@
+#include <math.h>
+#include "z64.h"
+
+void guMtxF2L(float mf[4][4], Mtx* m) {
+ unsigned int r, c;
+ s32 tmp1;
+ s32 tmp2;
+ s32* m1 = &m->m[0][0];
+ s32* m2 = &m->m[2][0];
+ for (r = 0; r < 4; r++) {
+ for (c = 0; c < 2; c++) {
+ tmp1 = mf[r][2 * c] * 65536.0f;
+ tmp2 = mf[r][2 * c + 1] * 65536.0f;
+ *m1++ = (tmp1 & 0xffff0000) | ((tmp2 >> 0x10) & 0xffff);
+ *m2++ = ((tmp1 << 0x10) & 0xffff0000) | (tmp2 & 0xffff);
+ }
+ }
+}
+
+void guMtxL2F(float mf[4][4], Mtx* m) {
+ unsigned int r, c;
+ u32 tmp1;
+ u32 tmp2;
+ u32* m1;
+ u32* m2;
+ s32 stmp1, stmp2;
+ m1 = (u32*)&m->m[0][0];
+ m2 = (u32*)&m->m[2][0];
+ for (r = 0; r < 4; r++) {
+ for (c = 0; c < 2; c++) {
+ tmp1 = (*m1 & 0xffff0000) | ((*m2 >> 0x10) & 0xffff);
+ tmp2 = ((*m1++ << 0x10) & 0xffff0000) | (*m2++ & 0xffff);
+ stmp1 = *(s32*)&tmp1;
+ stmp2 = *(s32*)&tmp2;
+ mf[r][c * 2 + 0] = stmp1 / 65536.0f;
+ mf[r][c * 2 + 1] = stmp2 / 65536.0f;
+ }
+ }
+}
+
+void guMtxIdentF(f32 mf[4][4]) {
+ unsigned int r, c;
+ for (r = 0; r < 4; r++) {
+ for (c = 0; c < 4; c++) {
+ if (r == c) {
+ mf[r][c] = 1.0f;
+ } else {
+ mf[r][c] = 0.0f;
+ }
+ }
+ }
+}
+
+void guMtxIdent(Mtx* m) {
+ guMtxIdentF(m->m);
+}
+
+void guTranslateF(float m[4][4], float x, float y, float z) {
+ guMtxIdentF(m);
+ m[3][0] = x;
+ m[3][1] = y;
+ m[3][2] = z;
+}
+void guTranslate(Mtx* m, float x, float y, float z) {
+ float mf[4][4];
+ guTranslateF(mf, x, y, z);
+ guMtxF2L(mf, m);
+}
+
+void guScaleF(float mf[4][4], float x, float y, float z) {
+ guMtxIdentF(mf);
+ mf[0][0] = x;
+ mf[1][1] = y;
+ mf[2][2] = z;
+ mf[3][3] = 1.0;
+}
+void guScale(Mtx* m, float x, float y, float z) {
+ float mf[4][4];
+ guScaleF(mf, x, y, z);
+ guMtxF2L(mf, m);
+}
+
+void guNormalize(f32* x, f32* y, f32* z) {
+ f32 tmp = 1.0f / sqrtf(*x * *x + *y * *y + *z * *z);
+ *x = *x * tmp;
+ *y = *y * tmp;
+ *z = *z * tmp;
+}
diff --git a/mm/2s2h/resource/importer/AnimationFactory.cpp b/mm/2s2h/resource/importer/AnimationFactory.cpp
new file mode 100644
index 000000000..c177c8ea9
--- /dev/null
+++ b/mm/2s2h/resource/importer/AnimationFactory.cpp
@@ -0,0 +1,108 @@
+#include "2s2h/resource/importer/AnimationFactory.h"
+#include "2s2h/resource/type/Animation.h"
+#include "2s2h/resource/importer/PlayerAnimationFactory.h"
+#include <libultraship/libultraship.h>
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource>
+AnimationFactory::ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<Animation>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<AnimationFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load Animation with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::AnimationFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) {
+ std::shared_ptr<Animation> animation = std::static_pointer_cast<Animation>(resource);
+
+ ResourceVersionFactory::ParseFileBinary(reader, animation);
+
+ AnimationType animType = (AnimationType)reader->ReadUInt32();
+ animation->type = animType;
+
+ if (animType == AnimationType::Normal) {
+ // Set frame count
+ animation->animationData.animationHeader.common.frameCount = reader->ReadInt16();
+
+ // Populate frame data
+ uint32_t rotValuesCnt = reader->ReadUInt32();
+ animation->rotationValues.reserve(rotValuesCnt);
+ for (uint32_t i = 0; i < rotValuesCnt; i++) {
+ animation->rotationValues.push_back(reader->ReadUInt16());
+ }
+ animation->animationData.animationHeader.frameData = (int16_t*)animation->rotationValues.data();
+
+ // Populate joint indices
+ uint32_t rotIndCnt = reader->ReadUInt32();
+ animation->rotationIndices.reserve(rotIndCnt);
+ for (size_t i = 0; i < rotIndCnt; i++) {
+ uint16_t x = reader->ReadUInt16();
+ uint16_t y = reader->ReadUInt16();
+ uint16_t z = reader->ReadUInt16();
+ animation->rotationIndices.push_back(RotationIndex(x, y, z));
+ }
+ animation->animationData.animationHeader.jointIndices = (JointIndex*)animation->rotationIndices.data();
+
+ // Set static index max
+ animation->animationData.animationHeader.staticIndexMax = reader->ReadInt16();
+ } else if (animType == AnimationType::Curve) {
+ // Read frame count (unused in this animation type)
+ reader->ReadInt16();
+
+ // Set refIndex
+ uint32_t refArrCnt = reader->ReadUInt32();
+ animation->refIndexArr.reserve(refArrCnt);
+ for (uint32_t i = 0; i < refArrCnt; i++) {
+ animation->refIndexArr.push_back(reader->ReadUByte());
+ }
+ animation->animationData.transformUpdateIndex.refIndex = animation->refIndexArr.data();
+
+ // Populate transform data
+ uint32_t transformDataCnt = reader->ReadUInt32();
+ animation->transformDataArr.reserve(transformDataCnt);
+ for (uint32_t i = 0; i < transformDataCnt; i++) {
+ TransformData data;
+ data.unk_00 = reader->ReadUInt16();
+ data.unk_02 = reader->ReadInt16();
+ data.unk_04 = reader->ReadInt16();
+ data.unk_06 = reader->ReadInt16();
+ data.unk_08 = reader->ReadFloat();
+
+ animation->transformDataArr.push_back(data);
+ }
+ animation->animationData.transformUpdateIndex.transformData = animation->transformDataArr.data();
+
+ // Populate copy values
+ uint32_t copyValuesCnt = reader->ReadUInt32();
+ animation->copyValuesArr.reserve(copyValuesCnt);
+ for (uint32_t i = 0; i < copyValuesCnt; i++) {
+ animation->copyValuesArr.push_back(reader->ReadInt16());
+ }
+ animation->animationData.transformUpdateIndex.copyValues = animation->copyValuesArr.data();
+ } else if (animType == AnimationType::Link) {
+ // Read the frame count
+ animation->animationData.linkAnimationHeader.common.frameCount = reader->ReadInt16();
+
+ // Read the segment pointer (always 32 bit, doesn't adjust for system pointer size)
+ std::string path = reader->ReadString();
+
+ animation->animationData.linkAnimationHeader.segment = ResourceGetDataByName(path.c_str());
+ } else if (animType == AnimationType::Legacy) {
+ SPDLOG_DEBUG("BEYTAH ANIMATION?!");
+ }
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/AnimationFactory.h b/mm/2s2h/resource/importer/AnimationFactory.h
new file mode 100644
index 000000000..dee2026f9
--- /dev/null
+++ b/mm/2s2h/resource/importer/AnimationFactory.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "Resource.h"
+#include "ResourceFactory.h"
+
+namespace LUS {
+class AnimationFactory : public ResourceFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class AnimationFactoryV0 : public ResourceVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/AudioSampleFactory.cpp b/mm/2s2h/resource/importer/AudioSampleFactory.cpp
new file mode 100644
index 000000000..73c5af484
--- /dev/null
+++ b/mm/2s2h/resource/importer/AudioSampleFactory.cpp
@@ -0,0 +1,141 @@
+#include "2s2h/resource/importer/AudioSampleFactory.h"
+#include "2s2h/resource/type/AudioSample.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource>
+AudioSampleFactory::ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<AudioSample>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 2:
+ factory = std::make_shared<AudioSampleFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load AudioSample with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::AudioSampleFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource)
+{
+ std::shared_ptr<AudioSample> audioSample = std::static_pointer_cast<AudioSample>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, audioSample);
+
+ audioSample->sample.codec = reader->ReadUByte();
+ audioSample->sample.medium = reader->ReadUByte();
+ audioSample->sample.unk_bit26 = reader->ReadUByte();
+ audioSample->sample.unk_bit25 = reader->ReadUByte();
+ audioSample->sample.size = reader->ReadUInt32();
+
+ audioSample->audioSampleData.reserve(audioSample->sample.size);
+ for (uint32_t i = 0; i < audioSample->sample.size; i++) {
+ audioSample->audioSampleData.push_back(reader->ReadUByte());
+ }
+ audioSample->sample.sampleAddr = audioSample->audioSampleData.data();
+
+ audioSample->loop.start = reader->ReadUInt32();
+ audioSample->loop.end = reader->ReadUInt32();
+ audioSample->loop.count = reader->ReadUInt32();
+
+ audioSample->loopStateCount = reader->ReadUInt32();
+ for (int i = 0; i < 16; i++) {
+ audioSample->loop.state[i] = 0;
+ }
+ for (uint32_t i = 0; i < audioSample->loopStateCount; i++) {
+ audioSample->loop.state[i] = reader->ReadInt16();
+ }
+ audioSample->sample.loop = &audioSample->loop;
+
+ audioSample->book.order = reader->ReadInt32();
+ audioSample->book.npredictors = reader->ReadInt32();
+ audioSample->bookDataCount = reader->ReadUInt32();
+
+ audioSample->bookData.reserve(audioSample->bookDataCount);
+ for (uint32_t i = 0; i < audioSample->bookDataCount; i++) {
+ audioSample->bookData.push_back(reader->ReadInt16());
+ }
+ audioSample->book.book = audioSample->bookData.data();
+ audioSample->sample.book = &audioSample->book;
+}
+} // namespace LUS
+
+
+/*
+in ResourceMgr_LoadAudioSample we used to have
+--------------
+ if (cachedCustomSFs.find(path) != cachedCustomSFs.end())
+ return cachedCustomSFs[path];
+
+ SoundFontSample* cSample = ReadCustomSample(path);
+
+ if (cSample != nullptr)
+ return cSample;
+--------------
+before the rest of the standard sample reading, this is the ReadCustomSample code we used to have
+
+extern "C" SoundFontSample* ReadCustomSample(const char* path) {
+
+ if (!ExtensionCache.contains(path))
+ return nullptr;
+
+ ExtensionEntry entry = ExtensionCache[path];
+
+ auto sampleRaw = LUS::Context::GetInstance()->GetResourceManager()->LoadFile(entry.path);
+ uint32_t* strem = (uint32_t*)sampleRaw->Buffer.get();
+ uint8_t* strem2 = (uint8_t*)strem;
+
+ SoundFontSample* sampleC = new SoundFontSample;
+
+ if (entry.ext == "wav") {
+ drwav_uint32 channels;
+ drwav_uint32 sampleRate;
+ drwav_uint64 totalPcm;
+ drmp3_int16* pcmData =
+ drwav_open_memory_and_read_pcm_frames_s16(strem2, sampleRaw->BufferSize, &channels, &sampleRate, &totalPcm, NULL);
+ sampleC->size = totalPcm;
+ sampleC->sampleAddr = (uint8_t*)pcmData;
+ sampleC->codec = CODEC_S16;
+
+ sampleC->loop = new AdpcmLoop;
+ sampleC->loop->start = 0;
+ sampleC->loop->end = sampleC->size - 1;
+ sampleC->loop->count = 0;
+ sampleC->sampleRateMagicValue = 'RIFF';
+ sampleC->sampleRate = sampleRate;
+
+ cachedCustomSFs[path] = sampleC;
+ return sampleC;
+ } else if (entry.ext == "mp3") {
+ drmp3_config mp3Info;
+ drmp3_uint64 totalPcm;
+ drmp3_int16* pcmData =
+ drmp3_open_memory_and_read_pcm_frames_s16(strem2, sampleRaw->BufferSize, &mp3Info, &totalPcm, NULL);
+
+ sampleC->size = totalPcm * mp3Info.channels * sizeof(short);
+ sampleC->sampleAddr = (uint8_t*)pcmData;
+ sampleC->codec = CODEC_S16;
+
+ sampleC->loop = new AdpcmLoop;
+ sampleC->loop->start = 0;
+ sampleC->loop->end = sampleC->size;
+ sampleC->loop->count = 0;
+ sampleC->sampleRateMagicValue = 'RIFF';
+ sampleC->sampleRate = mp3Info.sampleRate;
+
+ cachedCustomSFs[path] = sampleC;
+ return sampleC;
+ }
+
+ return nullptr;
+}
+
+*/
diff --git a/mm/2s2h/resource/importer/AudioSampleFactory.h b/mm/2s2h/resource/importer/AudioSampleFactory.h
new file mode 100644
index 000000000..6e9ddc17c
--- /dev/null
+++ b/mm/2s2h/resource/importer/AudioSampleFactory.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "Resource.h"
+#include "ResourceFactory.h"
+
+namespace LUS {
+class AudioSampleFactory : public ResourceFactory
+{
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class AudioSampleFactoryV0 : public ResourceVersionFactory
+{
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/AudioSequenceFactory.cpp b/mm/2s2h/resource/importer/AudioSequenceFactory.cpp
new file mode 100644
index 000000000..20c5df8a3
--- /dev/null
+++ b/mm/2s2h/resource/importer/AudioSequenceFactory.cpp
@@ -0,0 +1,52 @@
+#include "2s2h/resource/importer/AudioSequenceFactory.h"
+#include "2s2h/resource/type/AudioSequence.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource>
+AudioSequenceFactory::ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<AudioSequence>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 2:
+ factory = std::make_shared<AudioSequenceFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr)
+ {
+ SPDLOG_ERROR("Failed to load AudioSequence with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::AudioSequenceFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource) {
+ std::shared_ptr<AudioSequence> audioSequence = std::static_pointer_cast<AudioSequence>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, audioSequence);
+
+ audioSequence->sequence.seqDataSize = reader->ReadInt32();
+ audioSequence->sequenceData.reserve(audioSequence->sequence.seqDataSize);
+ for (uint32_t i = 0; i < audioSequence->sequence.seqDataSize; i++) {
+ audioSequence->sequenceData.push_back(reader->ReadChar());
+ }
+ audioSequence->sequence.seqData = audioSequence->sequenceData.data();
+
+ audioSequence->sequence.seqNumber = reader->ReadUByte();
+ audioSequence->sequence.medium = reader->ReadUByte();
+ audioSequence->sequence.cachePolicy = reader->ReadUByte();
+
+ audioSequence->sequence.numFonts = reader->ReadUInt32();
+ for (uint32_t i = 0; i < 16; i++) {
+ audioSequence->sequence.fonts[i] = 0;
+ }
+ for (uint32_t i = 0; i < audioSequence->sequence.numFonts; i++) {
+ audioSequence->sequence.fonts[i] = reader->ReadUByte();
+ }
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/AudioSequenceFactory.h b/mm/2s2h/resource/importer/AudioSequenceFactory.h
new file mode 100644
index 000000000..12b3809aa
--- /dev/null
+++ b/mm/2s2h/resource/importer/AudioSequenceFactory.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "Resource.h"
+#include "ResourceFactory.h"
+
+namespace LUS {
+class AudioSequenceFactory : public ResourceFactory
+{
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class AudioSequenceFactoryV0 : public ResourceVersionFactory
+{
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/AudioSoundFontFactory.cpp b/mm/2s2h/resource/importer/AudioSoundFontFactory.cpp
new file mode 100644
index 000000000..4ccb7edad
--- /dev/null
+++ b/mm/2s2h/resource/importer/AudioSoundFontFactory.cpp
@@ -0,0 +1,190 @@
+#include "2s2h/resource/importer/AudioSoundFontFactory.h"
+#include "2s2h/resource/type/AudioSoundFont.h"
+#include "spdlog/spdlog.h"
+#include "libultraship/libultraship.h"
+
+namespace LUS {
+std::shared_ptr<IResource>
+AudioSoundFontFactory::ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<AudioSoundFont>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 2:
+ factory = std::make_shared<AudioSoundFontFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr)
+ {
+ SPDLOG_ERROR("Failed to load AudioSoundFont with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::AudioSoundFontFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource) {
+ std::shared_ptr<AudioSoundFont> audioSoundFont = std::static_pointer_cast<AudioSoundFont>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, audioSoundFont);
+
+ audioSoundFont->soundFont.fntIndex = reader->ReadInt32();
+ audioSoundFont->medium = reader->ReadInt8();
+ audioSoundFont->cachePolicy = reader->ReadInt8();
+
+ audioSoundFont->data1 = reader->ReadUInt16();
+ audioSoundFont->soundFont.sampleBankId1 = audioSoundFont->data1 >> 8;
+ audioSoundFont->soundFont.sampleBankId2 = audioSoundFont->data1 & 0xFF;
+
+ audioSoundFont->data2 = reader->ReadUInt16();
+ audioSoundFont->data3 = reader->ReadUInt16();
+
+ uint32_t drumCount = reader->ReadUInt32();
+ audioSoundFont->soundFont.numDrums = drumCount;
+
+ uint32_t instrumentCount = reader->ReadUInt32();
+ audioSoundFont->soundFont.numInstruments = instrumentCount;
+
+ uint32_t soundEffectCount = reader->ReadUInt32();
+ audioSoundFont->soundFont.numSfx = soundEffectCount;
+
+ // 🥁 DRUMS 🥁
+ audioSoundFont->drums.reserve(audioSoundFont->soundFont.numDrums);
+ audioSoundFont->drumAddresses.reserve(audioSoundFont->soundFont.numDrums);
+ for (uint32_t i = 0; i < audioSoundFont->soundFont.numDrums; i++) {
+ Drum drum;
+ drum.releaseRate = reader->ReadUByte();
+ drum.pan = reader->ReadUByte();
+ drum.loaded = reader->ReadUByte();
+ drum.loaded = 0; // this was always getting set to zero in ResourceMgr_LoadAudioSoundFont
+
+ uint32_t envelopeCount = reader->ReadUInt32();
+ audioSoundFont->drumEnvelopeCounts.push_back(envelopeCount);
+ std::vector<AdsrEnvelope> drumEnvelopes;
+ drumEnvelopes.reserve(audioSoundFont->drumEnvelopeCounts[i]);
+ for (uint32_t j = 0; j < audioSoundFont->drumEnvelopeCounts.back(); j++) {
+ AdsrEnvelope env;
+
+ int16_t delay = reader->ReadInt16();
+ int16_t arg = reader->ReadInt16();
+
+ env.delay = BE16SWAP(delay);
+ env.arg = BE16SWAP(arg);
+
+ drumEnvelopes.push_back(env);
+ }
+ audioSoundFont->drumEnvelopeArrays.push_back(drumEnvelopes);
+ drum.envelope = audioSoundFont->drumEnvelopeArrays.back().data();
+
+ bool hasSample = reader->ReadInt8();
+ std::string sampleFileName = reader->ReadString();
+ drum.sound.tuning = reader->ReadFloat();
+
+ if (sampleFileName.empty()) {
+ drum.sound.sample = nullptr;
+ } else {
+ auto res = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(sampleFileName.c_str());
+ drum.sound.sample = static_cast<Sample*>(res ? res->GetRawPointer() : nullptr);
+ }
+
+ audioSoundFont->drums.push_back(drum);
+ audioSoundFont->drumAddresses.push_back(&audioSoundFont->drums.back());
+ }
+ audioSoundFont->soundFont.drums = audioSoundFont->drumAddresses.data();
+
+ // 🎺🎻🎷🎸🎹 INSTRUMENTS 🎹🎸🎷🎻🎺
+ audioSoundFont->instruments.reserve(audioSoundFont->soundFont.numInstruments);
+ for (uint32_t i = 0; i < audioSoundFont->soundFont.numInstruments; i++) {
+ Instrument instrument;
+
+ uint8_t isValidEntry = reader->ReadUByte();
+ instrument.loaded = reader->ReadUByte();
+ instrument.loaded = 0; // this was always getting set to zero in ResourceMgr_LoadAudioSoundFont
+
+ instrument.normalRangeLo = reader->ReadUByte();
+ instrument.normalRangeHi = reader->ReadUByte();
+ instrument.releaseRate = reader->ReadUByte();
+
+ uint32_t envelopeCount = reader->ReadInt32();
+ audioSoundFont->instrumentEnvelopeCounts.push_back(envelopeCount);
+ std::vector<AdsrEnvelope> instrumentEnvelopes;
+ for (uint32_t j = 0; j < audioSoundFont->instrumentEnvelopeCounts.back(); j++) {
+ AdsrEnvelope env;
+
+ int16_t delay = reader->ReadInt16();
+ int16_t arg = reader->ReadInt16();
+
+ env.delay = BE16SWAP(delay);
+ env.arg = BE16SWAP(arg);
+
+ instrumentEnvelopes.push_back(env);
+ }
+ audioSoundFont->instrumentEnvelopeArrays.push_back(instrumentEnvelopes);
+ instrument.envelope = audioSoundFont->instrumentEnvelopeArrays.back().data();
+
+ bool hasLowNoteSoundFontEntry = reader->ReadInt8();
+ if (hasLowNoteSoundFontEntry) {
+ bool hasSampleRef = reader->ReadInt8();
+ std::string sampleFileName = reader->ReadString();
+ instrument.lowNotesSound.tuning = reader->ReadFloat();
+ auto res = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(sampleFileName.c_str());
+ instrument.lowNotesSound.sample = static_cast<Sample*>(res ? res->GetRawPointer() : nullptr);
+ } else {
+ instrument.lowNotesSound.sample = nullptr;
+ instrument.lowNotesSound.tuning = 0;
+ }
+
+ bool hasNormalNoteSoundFontEntry = reader->ReadInt8();
+ if (hasNormalNoteSoundFontEntry) {
+ bool hasSampleRef = reader->ReadInt8();
+ std::string sampleFileName = reader->ReadString();
+ instrument.normalNotesSound.tuning = reader->ReadFloat();
+ auto res = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(sampleFileName.c_str());
+ instrument.normalNotesSound.sample = static_cast<Sample*>(res ? res->GetRawPointer() : nullptr);
+ } else {
+ instrument.normalNotesSound.sample = nullptr;
+ instrument.normalNotesSound.tuning = 0;
+ }
+
+ bool hasHighNoteSoundFontEntry = reader->ReadInt8();
+ if (hasHighNoteSoundFontEntry) {
+ bool hasSampleRef = reader->ReadInt8();
+ std::string sampleFileName = reader->ReadString();
+ instrument.highNotesSound.tuning = reader->ReadFloat();
+ auto res = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(sampleFileName.c_str());
+ instrument.highNotesSound.sample = static_cast<Sample*>(res ? res->GetRawPointer() : nullptr);
+ } else {
+ instrument.highNotesSound.sample = nullptr;
+ instrument.highNotesSound.tuning = 0;
+ }
+
+
+ audioSoundFont->instruments.push_back(instrument);
+ audioSoundFont->instrumentAddresses.push_back(isValidEntry ?
+ &audioSoundFont->instruments.back() :
+ nullptr);
+ }
+ audioSoundFont->soundFont.instruments = audioSoundFont->instrumentAddresses.data();
+
+ // 🔊 SOUND EFFECTS 🔊
+ audioSoundFont->soundEffects.reserve(audioSoundFont->soundFont.numSfx);
+ for (uint32_t i = 0; i < audioSoundFont->soundFont.numSfx; i++) {
+ SoundFontSound soundEffect;
+
+ bool hasSFEntry = reader->ReadInt8();
+ if (hasSFEntry) {
+ bool hasSampleRef = reader->ReadInt8();
+ std::string sampleFileName = reader->ReadString();
+ soundEffect.tuning = reader->ReadFloat();
+ auto res = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(sampleFileName.c_str());
+ soundEffect.sample = static_cast<Sample*>(res ? res->GetRawPointer() : nullptr);
+ }
+
+ audioSoundFont->soundEffects.push_back(soundEffect);
+ }
+ audioSoundFont->soundFont.soundEffects = audioSoundFont->soundEffects.data();
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/AudioSoundFontFactory.h b/mm/2s2h/resource/importer/AudioSoundFontFactory.h
new file mode 100644
index 000000000..7dd5bc0d5
--- /dev/null
+++ b/mm/2s2h/resource/importer/AudioSoundFontFactory.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "Resource.h"
+#include "ResourceFactory.h"
+
+namespace LUS {
+class AudioSoundFontFactory : public ResourceFactory
+{
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class AudioSoundFontFactoryV0 : public ResourceVersionFactory
+{
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/BackgroundFactory.cpp b/mm/2s2h/resource/importer/BackgroundFactory.cpp
new file mode 100644
index 000000000..6acc7737c
--- /dev/null
+++ b/mm/2s2h/resource/importer/BackgroundFactory.cpp
@@ -0,0 +1,39 @@
+#include "2s2h/resource/importer/BackgroundFactory.h"
+#include "2s2h/resource/type/Background.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource>
+BackgroundFactory::ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<Background>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<BackgroundFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load Background with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void BackgroundFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) {
+ std::shared_ptr<Background> background = std::static_pointer_cast<Background>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, background);
+
+ uint32_t dataSize = reader->ReadUInt32();
+
+ background->Data.reserve(dataSize);
+
+ for (uint32_t i = 0; i < dataSize; i++) {
+ background->Data.push_back(reader->ReadUByte());
+ }
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/BackgroundFactory.h b/mm/2s2h/resource/importer/BackgroundFactory.h
new file mode 100644
index 000000000..9767fdf8b
--- /dev/null
+++ b/mm/2s2h/resource/importer/BackgroundFactory.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "resource/Resource.h"
+#include "resource/ResourceFactory.h"
+
+namespace LUS {
+class BackgroundFactory : public ResourceFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class BackgroundFactoryV0 : public ResourceVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/CollisionHeaderFactory.cpp b/mm/2s2h/resource/importer/CollisionHeaderFactory.cpp
new file mode 100644
index 000000000..4b5b45f0d
--- /dev/null
+++ b/mm/2s2h/resource/importer/CollisionHeaderFactory.cpp
@@ -0,0 +1,142 @@
+#include "2s2h/resource/importer/CollisionHeaderFactory.h"
+#include "2s2h/resource/type/CollisionHeader.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource>
+CollisionHeaderFactory::ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<CollisionHeader>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<CollisionHeaderFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load Collision Header with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::CollisionHeaderFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource)
+{
+ std::shared_ptr<CollisionHeader> collisionHeader = std::static_pointer_cast<CollisionHeader>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, collisionHeader);
+
+ collisionHeader->collisionHeaderData.minBounds.x = reader->ReadInt16();
+ collisionHeader->collisionHeaderData.minBounds.y = reader->ReadInt16();
+ collisionHeader->collisionHeaderData.minBounds.z = reader->ReadInt16();
+
+ collisionHeader->collisionHeaderData.maxBounds.x = reader->ReadInt16();
+ collisionHeader->collisionHeaderData.maxBounds.y = reader->ReadInt16();
+ collisionHeader->collisionHeaderData.maxBounds.z = reader->ReadInt16();
+
+ collisionHeader->collisionHeaderData.numVertices = reader->ReadInt32();
+ collisionHeader->vertices.reserve(collisionHeader->collisionHeaderData.numVertices);
+ for (int32_t i = 0; i < collisionHeader->collisionHeaderData.numVertices; i++) {
+ Vec3s vtx;
+ vtx.x = reader->ReadInt16();
+ vtx.y = reader->ReadInt16();
+ vtx.z = reader->ReadInt16();
+ collisionHeader->vertices.push_back(vtx);
+ }
+ collisionHeader->collisionHeaderData.vtxList = collisionHeader->vertices.data();
+
+ collisionHeader->collisionHeaderData.numPolygons = reader->ReadUInt32();
+ collisionHeader->polygons.reserve(collisionHeader->collisionHeaderData.numPolygons);
+ for (uint32_t i = 0; i < collisionHeader->collisionHeaderData.numPolygons; i++) {
+ CollisionPoly polygon;
+
+ polygon.type = reader->ReadUInt16();
+
+ polygon.flags_vIA = reader->ReadUInt16();
+ polygon.flags_vIB = reader->ReadUInt16();
+ polygon.vIC = reader->ReadUInt16();
+
+ polygon.normal.x = reader->ReadUInt16();
+ polygon.normal.y = reader->ReadUInt16();
+ polygon.normal.z = reader->ReadUInt16();
+
+ polygon.dist = reader->ReadUInt16();
+
+ collisionHeader->polygons.push_back(polygon);
+
+ }
+ collisionHeader->collisionHeaderData.polyList = collisionHeader->polygons.data();
+
+ collisionHeader->surfaceTypesCount = reader->ReadUInt32();
+ collisionHeader->surfaceTypes.reserve(collisionHeader->surfaceTypesCount);
+ for (uint32_t i = 0; i < collisionHeader->surfaceTypesCount; i++) {
+ SurfaceType surfaceType;
+
+ surfaceType.data[1] = reader->ReadUInt32();
+ surfaceType.data[0] = reader->ReadUInt32();
+
+ collisionHeader->surfaceTypes.push_back(surfaceType);
+ }
+ collisionHeader->collisionHeaderData.surfaceTypeList = collisionHeader->surfaceTypes.data();
+
+ collisionHeader->camDataCount = reader->ReadUInt32();
+ collisionHeader->camData.reserve(collisionHeader->camDataCount);
+ collisionHeader->camPosDataIndices.reserve(collisionHeader->camDataCount);
+ for (uint32_t i = 0; i < collisionHeader->camDataCount; i++) {
+ CamData camDataEntry;
+ camDataEntry.cameraSType = reader->ReadUInt16();
+ camDataEntry.numCameras = reader->ReadInt16();
+ collisionHeader->camData.push_back(camDataEntry);
+
+ int32_t camPosDataIdx = reader->ReadInt32();
+ collisionHeader->camPosDataIndices.push_back(camPosDataIdx);
+ }
+
+ collisionHeader->camPosCount = reader->ReadInt32();
+ collisionHeader->camPosData.reserve(collisionHeader->camPosCount);
+ for (int32_t i = 0; i < collisionHeader->camPosCount; i++) {
+ Vec3s pos;
+ pos.x = reader->ReadInt16();
+ pos.y = reader->ReadInt16();
+ pos.z = reader->ReadInt16();
+ collisionHeader->camPosData.push_back(pos);
+ }
+
+ Vec3s zero;
+ zero.x = 0;
+ zero.y = 0;
+ zero.z = 0;
+ collisionHeader->camPosDataZero = zero;
+
+ for (size_t i = 0; i < collisionHeader->camDataCount; i++) {
+ int32_t idx = collisionHeader->camPosDataIndices[i];
+
+ if (collisionHeader->camPosCount > 0) {
+ collisionHeader->camData[i].camPosData = &collisionHeader->camPosData[idx];
+ } else {
+ collisionHeader->camData[i].camPosData = &collisionHeader->camPosDataZero;
+ }
+ }
+ collisionHeader->collisionHeaderData.cameraDataList = collisionHeader->camData.data();
+ collisionHeader->collisionHeaderData.cameraDataListLen = collisionHeader->camDataCount;
+
+ collisionHeader->collisionHeaderData.numWaterBoxes = reader->ReadInt32();
+ collisionHeader->waterBoxes.reserve(collisionHeader->collisionHeaderData.numWaterBoxes);
+ for (int32_t i = 0; i < collisionHeader->collisionHeaderData.numWaterBoxes; i++) {
+ WaterBox waterBox;
+ waterBox.xMin = reader->ReadInt16();
+ waterBox.ySurface = reader->ReadInt16();
+ waterBox.zMin = reader->ReadInt16();
+ waterBox.xLength = reader->ReadInt16();
+ waterBox.zLength = reader->ReadInt16();
+ waterBox.properties = reader->ReadInt32();
+
+ collisionHeader->waterBoxes.push_back(waterBox);
+ }
+ collisionHeader->collisionHeaderData.waterBoxes = collisionHeader->waterBoxes.data();
+}
+}
diff --git a/mm/2s2h/resource/importer/CollisionHeaderFactory.h b/mm/2s2h/resource/importer/CollisionHeaderFactory.h
new file mode 100644
index 000000000..09d6ba4c5
--- /dev/null
+++ b/mm/2s2h/resource/importer/CollisionHeaderFactory.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "Resource.h"
+#include "ResourceFactory.h"
+
+namespace LUS {
+class CollisionHeaderFactory : public ResourceFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class CollisionHeaderFactoryV0 : public ResourceVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/CutsceneFactory.cpp b/mm/2s2h/resource/importer/CutsceneFactory.cpp
new file mode 100644
index 000000000..34c12ccc0
--- /dev/null
+++ b/mm/2s2h/resource/importer/CutsceneFactory.cpp
@@ -0,0 +1,918 @@
+#include "2s2h/resource/importer/CutsceneFactory.h"
+#include "2s2h/resource/type/Cutscene.h"
+#include "spdlog/spdlog.h"
+// TODO headers
+//extern "C" {
+//#include "z64cutscene.h"
+//}
+typedef enum {
+ /* 0x00A */ CS_CMD_TEXT = 10,
+ /* 0x05A */ CS_CMD_CAMERA_SPLINE = 90,
+ /* 0x064 */ CS_CMD_ACTOR_CUE_100 = 100,
+ /* 0x065 */ CS_CMD_ACTOR_CUE_101,
+ /* 0x066 */ CS_CMD_ACTOR_CUE_102,
+ /* 0x067 */ CS_CMD_ACTOR_CUE_103,
+ /* 0x068 */ CS_CMD_ACTOR_CUE_104,
+ /* 0x069 */ CS_CMD_ACTOR_CUE_105,
+ /* 0x06A */ CS_CMD_ACTOR_CUE_106,
+ /* 0x06B */ CS_CMD_ACTOR_CUE_107,
+ /* 0x06C */ CS_CMD_ACTOR_CUE_108,
+ /* 0x06D */ CS_CMD_ACTOR_CUE_109,
+ /* 0x06E */ CS_CMD_ACTOR_CUE_110,
+ /* 0x06F */ CS_CMD_ACTOR_CUE_111,
+ /* 0x070 */ CS_CMD_ACTOR_CUE_112,
+ /* 0x071 */ CS_CMD_ACTOR_CUE_113,
+ /* 0x072 */ CS_CMD_ACTOR_CUE_114,
+ /* 0x073 */ CS_CMD_ACTOR_CUE_115,
+ /* 0x074 */ CS_CMD_ACTOR_CUE_116,
+ /* 0x075 */ CS_CMD_ACTOR_CUE_117,
+ /* 0x076 */ CS_CMD_ACTOR_CUE_118,
+ /* 0x077 */ CS_CMD_ACTOR_CUE_119,
+ /* 0x078 */ CS_CMD_ACTOR_CUE_120,
+ /* 0x079 */ CS_CMD_ACTOR_CUE_121,
+ /* 0x07A */ CS_CMD_ACTOR_CUE_122,
+ /* 0x07B */ CS_CMD_ACTOR_CUE_123,
+ /* 0x07C */ CS_CMD_ACTOR_CUE_124,
+ /* 0x07D */ CS_CMD_ACTOR_CUE_125,
+ /* 0x07E */ CS_CMD_ACTOR_CUE_126,
+ /* 0x07F */ CS_CMD_ACTOR_CUE_127,
+ /* 0x080 */ CS_CMD_ACTOR_CUE_128,
+ /* 0x081 */ CS_CMD_ACTOR_CUE_129,
+ /* 0x082 */ CS_CMD_ACTOR_CUE_130,
+ /* 0x083 */ CS_CMD_ACTOR_CUE_131,
+ /* 0x084 */ CS_CMD_ACTOR_CUE_132,
+ /* 0x085 */ CS_CMD_ACTOR_CUE_133,
+ /* 0x086 */ CS_CMD_ACTOR_CUE_134,
+ /* 0x087 */ CS_CMD_ACTOR_CUE_135,
+ /* 0x088 */ CS_CMD_ACTOR_CUE_136,
+ /* 0x089 */ CS_CMD_ACTOR_CUE_137,
+ /* 0x08A */ CS_CMD_ACTOR_CUE_138,
+ /* 0x08B */ CS_CMD_ACTOR_CUE_139,
+ /* 0x08C */ CS_CMD_ACTOR_CUE_140,
+ /* 0x08D */ CS_CMD_ACTOR_CUE_141,
+ /* 0x08E */ CS_CMD_ACTOR_CUE_142,
+ /* 0x08F */ CS_CMD_ACTOR_CUE_143,
+ /* 0x090 */ CS_CMD_ACTOR_CUE_144,
+ /* 0x091 */ CS_CMD_ACTOR_CUE_145,
+ /* 0x092 */ CS_CMD_ACTOR_CUE_146,
+ /* 0x093 */ CS_CMD_ACTOR_CUE_147,
+ /* 0x094 */ CS_CMD_ACTOR_CUE_148,
+ /* 0x095 */ CS_CMD_ACTOR_CUE_149,
+ /* 0x096 */ CS_CMD_MISC,
+ /* 0x097 */ CS_CMD_LIGHT_SETTING,
+ /* 0x098 */ CS_CMD_TRANSITION,
+ /* 0x099 */ CS_CMD_MOTION_BLUR,
+ /* 0x09A */ CS_CMD_GIVE_TATL,
+ /* 0x09B */ CS_CMD_TRANSITION_GENERAL,
+ /* 0x09C */ CS_CMD_FADE_OUT_SEQ,
+ /* 0x09D */ CS_CMD_TIME,
+ /* 0x0C8 */ CS_CMD_PLAYER_CUE = 200,
+ /* 0x0C9 */ CS_CMD_ACTOR_CUE_201,
+ /* 0x0FA */ CS_CMD_UNK_DATA_FA = 0xFA,
+ /* 0x0FE */ CS_CMD_UNK_DATA_FE = 0xFE,
+ /* 0x0FF */ CS_CMD_UNK_DATA_FF,
+ /* 0x100 */ CS_CMD_UNK_DATA_100,
+ /* 0x101 */ CS_CMD_UNK_DATA_101,
+ /* 0x102 */ CS_CMD_UNK_DATA_102,
+ /* 0x103 */ CS_CMD_UNK_DATA_103,
+ /* 0x104 */ CS_CMD_UNK_DATA_104,
+ /* 0x105 */ CS_CMD_UNK_DATA_105,
+ /* 0x108 */ CS_CMD_UNK_DATA_108 = 0x108,
+ /* 0x109 */ CS_CMD_UNK_DATA_109,
+ /* 0x12C */ CS_CMD_START_SEQ = 300,
+ /* 0x12D */ CS_CMD_STOP_SEQ,
+ /* 0x12E */ CS_CMD_START_AMBIENCE,
+ /* 0x12F */ CS_CMD_FADE_OUT_AMBIENCE,
+ /* 0x130 */ CS_CMD_SFX_REVERB_INDEX_2,
+ /* 0x131 */ CS_CMD_SFX_REVERB_INDEX_1,
+ /* 0x132 */ CS_CMD_MODIFY_SEQ,
+ /* 0x15E */ CS_CMD_DESTINATION = 350,
+ /* 0x15F */ CS_CMD_CHOOSE_CREDITS_SCENES,
+ /* 0x190 */ CS_CMD_RUMBLE = 400,
+ /* 0x1C2 */ CS_CMD_ACTOR_CUE_450 = 450,
+ /* 0x1C3 */ CS_CMD_ACTOR_CUE_451,
+ /* 0x1C4 */ CS_CMD_ACTOR_CUE_452,
+ /* 0x1C5 */ CS_CMD_ACTOR_CUE_453,
+ /* 0x1C6 */ CS_CMD_ACTOR_CUE_454,
+ /* 0x1C7 */ CS_CMD_ACTOR_CUE_455,
+ /* 0x1C8 */ CS_CMD_ACTOR_CUE_456,
+ /* 0x1C9 */ CS_CMD_ACTOR_CUE_457,
+ /* 0x1CA */ CS_CMD_ACTOR_CUE_458,
+ /* 0x1CB */ CS_CMD_ACTOR_CUE_459,
+ /* 0x1CC */ CS_CMD_ACTOR_CUE_460,
+ /* 0x1CD */ CS_CMD_ACTOR_CUE_461,
+ /* 0x1CE */ CS_CMD_ACTOR_CUE_462,
+ /* 0x1CF */ CS_CMD_ACTOR_CUE_463,
+ /* 0x1D0 */ CS_CMD_ACTOR_CUE_464,
+ /* 0x1D1 */ CS_CMD_ACTOR_CUE_465,
+ /* 0x1D2 */ CS_CMD_ACTOR_CUE_466,
+ /* 0x1D3 */ CS_CMD_ACTOR_CUE_467,
+ /* 0x1D4 */ CS_CMD_ACTOR_CUE_468,
+ /* 0x1D5 */ CS_CMD_ACTOR_CUE_469,
+ /* 0x1D6 */ CS_CMD_ACTOR_CUE_470,
+ /* 0x1D7 */ CS_CMD_ACTOR_CUE_471,
+ /* 0x1D8 */ CS_CMD_ACTOR_CUE_472,
+ /* 0x1D9 */ CS_CMD_ACTOR_CUE_473,
+ /* 0x1DA */ CS_CMD_ACTOR_CUE_474,
+ /* 0x1DB */ CS_CMD_ACTOR_CUE_475,
+ /* 0x1DC */ CS_CMD_ACTOR_CUE_476,
+ /* 0x1DD */ CS_CMD_ACTOR_CUE_477,
+ /* 0x1DE */ CS_CMD_ACTOR_CUE_478,
+ /* 0x1DF */ CS_CMD_ACTOR_CUE_479,
+ /* 0x1E0 */ CS_CMD_ACTOR_CUE_480,
+ /* 0x1E1 */ CS_CMD_ACTOR_CUE_481,
+ /* 0x1E2 */ CS_CMD_ACTOR_CUE_482,
+ /* 0x1E3 */ CS_CMD_ACTOR_CUE_483,
+ /* 0x1E4 */ CS_CMD_ACTOR_CUE_484,
+ /* 0x1E5 */ CS_CMD_ACTOR_CUE_485,
+ /* 0x1E6 */ CS_CMD_ACTOR_CUE_486,
+ /* 0x1E7 */ CS_CMD_ACTOR_CUE_487,
+ /* 0x1E8 */ CS_CMD_ACTOR_CUE_488,
+ /* 0x1E9 */ CS_CMD_ACTOR_CUE_489,
+ /* 0x1EA */ CS_CMD_ACTOR_CUE_490,
+ /* 0x1EB */ CS_CMD_ACTOR_CUE_491,
+ /* 0x1EC */ CS_CMD_ACTOR_CUE_492,
+ /* 0x1ED */ CS_CMD_ACTOR_CUE_493,
+ /* 0x1EE */ CS_CMD_ACTOR_CUE_494,
+ /* 0x1EF */ CS_CMD_ACTOR_CUE_495,
+ /* 0x1F0 */ CS_CMD_ACTOR_CUE_496,
+ /* 0x1F1 */ CS_CMD_ACTOR_CUE_497,
+ /* 0x1F2 */ CS_CMD_ACTOR_CUE_498,
+ /* 0x1F3 */ CS_CMD_ACTOR_CUE_499,
+ /* 0x1F4 */ CS_CMD_ACTOR_CUE_500,
+ /* 0x1F5 */ CS_CMD_ACTOR_CUE_501,
+ /* 0x1F6 */ CS_CMD_ACTOR_CUE_502,
+ /* 0x1F7 */ CS_CMD_ACTOR_CUE_503,
+ /* 0x1F8 */ CS_CMD_ACTOR_CUE_504,
+ /* 0x1F9 */ CS_CMD_ACTOR_CUE_SOTCS, // Song of Time Cutscenes (Double SoT, Three-Day Reset SoT)
+ /* 0x1FA */ CS_CMD_ACTOR_CUE_506,
+ /* 0x1FB */ CS_CMD_ACTOR_CUE_507,
+ /* 0x1FC */ CS_CMD_ACTOR_CUE_508,
+ /* 0x1FD */ CS_CMD_ACTOR_CUE_509,
+ /* 0x1FE */ CS_CMD_ACTOR_CUE_510,
+ /* 0x1FF */ CS_CMD_ACTOR_CUE_511,
+ /* 0x200 */ CS_CMD_ACTOR_CUE_512,
+ /* 0x201 */ CS_CMD_ACTOR_CUE_513,
+ /* 0x202 */ CS_CMD_ACTOR_CUE_514,
+ /* 0x203 */ CS_CMD_ACTOR_CUE_515,
+ /* 0x204 */ CS_CMD_ACTOR_CUE_516,
+ /* 0x205 */ CS_CMD_ACTOR_CUE_517,
+ /* 0x206 */ CS_CMD_ACTOR_CUE_518,
+ /* 0x207 */ CS_CMD_ACTOR_CUE_519,
+ /* 0x208 */ CS_CMD_ACTOR_CUE_520,
+ /* 0x209 */ CS_CMD_ACTOR_CUE_521,
+ /* 0x20A */ CS_CMD_ACTOR_CUE_522,
+ /* 0x20B */ CS_CMD_ACTOR_CUE_523,
+ /* 0x20C */ CS_CMD_ACTOR_CUE_524,
+ /* 0x20D */ CS_CMD_ACTOR_CUE_525,
+ /* 0x20E */ CS_CMD_ACTOR_CUE_526,
+ /* 0x20F */ CS_CMD_ACTOR_CUE_527,
+ /* 0x210 */ CS_CMD_ACTOR_CUE_528,
+ /* 0x211 */ CS_CMD_ACTOR_CUE_529,
+ /* 0x212 */ CS_CMD_ACTOR_CUE_530,
+ /* 0x213 */ CS_CMD_ACTOR_CUE_531,
+ /* 0x214 */ CS_CMD_ACTOR_CUE_532,
+ /* 0x215 */ CS_CMD_ACTOR_CUE_533,
+ /* 0x216 */ CS_CMD_ACTOR_CUE_534,
+ /* 0x217 */ CS_CMD_ACTOR_CUE_535,
+ /* 0x218 */ CS_CMD_ACTOR_CUE_536,
+ /* 0x219 */ CS_CMD_ACTOR_CUE_537,
+ /* 0x21A */ CS_CMD_ACTOR_CUE_538,
+ /* 0x21B */ CS_CMD_ACTOR_CUE_539,
+ /* 0x21C */ CS_CMD_ACTOR_CUE_540,
+ /* 0x21D */ CS_CMD_ACTOR_CUE_541,
+ /* 0x21E */ CS_CMD_ACTOR_CUE_542,
+ /* 0x21F */ CS_CMD_ACTOR_CUE_543,
+ /* 0x220 */ CS_CMD_ACTOR_CUE_544,
+ /* 0x221 */ CS_CMD_ACTOR_CUE_545,
+ /* 0x222 */ CS_CMD_ACTOR_CUE_546,
+ /* 0x223 */ CS_CMD_ACTOR_CUE_547,
+ /* 0x224 */ CS_CMD_ACTOR_CUE_548,
+ /* 0x225 */ CS_CMD_ACTOR_CUE_549,
+ /* 0x226 */ CS_CMD_ACTOR_CUE_550,
+ /* 0x227 */ CS_CMD_ACTOR_CUE_551,
+ /* 0x228 */ CS_CMD_ACTOR_CUE_552,
+ /* 0x229 */ CS_CMD_ACTOR_CUE_553,
+ /* 0x22A */ CS_CMD_ACTOR_CUE_554,
+ /* 0x22B */ CS_CMD_ACTOR_CUE_555,
+ /* 0x22C */ CS_CMD_ACTOR_CUE_556,
+ /* 0x22D */ CS_CMD_ACTOR_CUE_557,
+ /* 0x22E */ CS_CMD_ACTOR_CUE_558,
+ /* 0x22F */ CS_CMD_ACTOR_CUE_559,
+ /* 0x230 */ CS_CMD_ACTOR_CUE_560,
+ /* 0x231 */ CS_CMD_ACTOR_CUE_561,
+ /* 0x232 */ CS_CMD_ACTOR_CUE_562,
+ /* 0x233 */ CS_CMD_ACTOR_CUE_563,
+ /* 0x234 */ CS_CMD_ACTOR_CUE_564,
+ /* 0x235 */ CS_CMD_ACTOR_CUE_565,
+ /* 0x236 */ CS_CMD_ACTOR_CUE_566,
+ /* 0x237 */ CS_CMD_ACTOR_CUE_567,
+ /* 0x238 */ CS_CMD_ACTOR_CUE_568,
+ /* 0x239 */ CS_CMD_ACTOR_CUE_569,
+ /* 0x23A */ CS_CMD_ACTOR_CUE_570,
+ /* 0x23B */ CS_CMD_ACTOR_CUE_571,
+ /* 0x23C */ CS_CMD_ACTOR_CUE_572,
+ /* 0x23D */ CS_CMD_ACTOR_CUE_573,
+ /* 0x23E */ CS_CMD_ACTOR_CUE_574,
+ /* 0x23F */ CS_CMD_ACTOR_CUE_575,
+ /* 0x240 */ CS_CMD_ACTOR_CUE_576,
+ /* 0x241 */ CS_CMD_ACTOR_CUE_577,
+ /* 0x242 */ CS_CMD_ACTOR_CUE_578,
+ /* 0x243 */ CS_CMD_ACTOR_CUE_579,
+ /* 0x244 */ CS_CMD_ACTOR_CUE_580,
+ /* 0x245 */ CS_CMD_ACTOR_CUE_581,
+ /* 0x246 */ CS_CMD_ACTOR_CUE_582,
+ /* 0x247 */ CS_CMD_ACTOR_CUE_583,
+ /* 0x248 */ CS_CMD_ACTOR_CUE_584,
+ /* 0x249 */ CS_CMD_ACTOR_CUE_585,
+ /* 0x24A */ CS_CMD_ACTOR_CUE_586,
+ /* 0x24B */ CS_CMD_ACTOR_CUE_587,
+ /* 0x24C */ CS_CMD_ACTOR_CUE_588,
+ /* 0x24D */ CS_CMD_ACTOR_CUE_589,
+ /* 0x24E */ CS_CMD_ACTOR_CUE_590,
+ /* 0x24F */ CS_CMD_ACTOR_CUE_591,
+ /* 0x250 */ CS_CMD_ACTOR_CUE_592,
+ /* 0x251 */ CS_CMD_ACTOR_CUE_593,
+ /* 0x252 */ CS_CMD_ACTOR_CUE_594,
+ /* 0x253 */ CS_CMD_ACTOR_CUE_595,
+ /* 0x254 */ CS_CMD_ACTOR_CUE_596,
+ /* 0x255 */ CS_CMD_ACTOR_CUE_597,
+ /* 0x256 */ CS_CMD_ACTOR_CUE_598,
+ /* 0x257 */ CS_CMD_ACTOR_CUE_599,
+ /* -2 */ CS_CMD_ACTOR_CUE_POST_PROCESS = 0xFFFFFFFE,
+ /* -1 */ CS_CAM_STOP // OoT Remnant
+} CutsceneCmd;
+
+namespace LUS {
+std::shared_ptr<IResource>
+CutsceneFactory::ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<Cutscene>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<CutsceneFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load Cutscene with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+static inline uint32_t read_CMD_BBBB(std::shared_ptr<BinaryReader> reader) {
+ uint32_t v;
+ reader->Read((char*)&v, sizeof(uint32_t));
+
+ return v;
+}
+
+static inline uint32_t read_CMD_BBH(std::shared_ptr<BinaryReader> reader) {
+ uint32_t v;
+ reader->Read((char*)&v, sizeof(uint32_t));
+
+ // swap the half word to match endianness
+ if (reader->GetEndianness() != LUS::Endianness::Native) {
+ uint8_t* b = (uint8_t*)&v;
+ uint8_t tmp = b[2];
+ b[2] = b[3];
+ b[3] = tmp;
+ }
+
+ return v;
+}
+
+static inline uint32_t read_CMD_HBB(std::shared_ptr<BinaryReader> reader) {
+ uint32_t v;
+ reader->Read((char*)&v, sizeof(uint32_t));
+
+ // swap the half word to match endianness
+ if (reader->GetEndianness() != LUS::Endianness::Native) {
+ uint8_t* b = (uint8_t*)&v;
+ uint8_t tmp = b[0];
+ b[0] = b[1];
+ b[1] = tmp;
+ }
+
+ return v;
+}
+
+static inline uint32_t read_CMD_HH(std::shared_ptr<BinaryReader> reader) {
+ uint32_t v;
+ reader->Read((char*)&v, sizeof(uint32_t));
+
+ // swap the half words to match endianness
+ if (reader->GetEndianness() != LUS::Endianness::Native) {
+ uint8_t* b = (uint8_t*)&v;
+ uint8_t tmp = b[0];
+ b[0] = b[1];
+ b[1] = tmp;
+ tmp = b[2];
+ b[2] = b[3];
+ b[3] = tmp;
+ }
+
+ return v;
+}
+
+void LUS::CutsceneFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource)
+{
+ std::shared_ptr<Cutscene> cutscene = std::static_pointer_cast<Cutscene>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, cutscene);
+
+ uint32_t numEntries = reader->ReadUInt32();
+ cutscene->commands.reserve(numEntries);
+
+ cutscene->numCommands = reader->ReadUInt32();
+ cutscene->commands.push_back(cutscene->numCommands);
+
+ cutscene->endFrame = reader->ReadUInt32();
+ cutscene->commands.push_back(cutscene->endFrame);
+
+ // BENTODO detect the game
+ ParseFileBinaryMM(reader, cutscene);
+}
+
+void LUS::CutsceneFactoryV0::ParseFileBinaryOoT(std::shared_ptr<BinaryReader> reader, std::shared_ptr<Cutscene> cutscene) {
+ while (true) {
+ uint32_t commandId = reader->ReadUInt32();
+ cutscene->commands.push_back(commandId);
+
+ switch (commandId) {
+ case (uint32_t)CutsceneCommands::SetCameraPos: {
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+
+ while (true) {
+ uint32_t val = read_CMD_BBH(reader);
+ int8_t continueFlag = ((int8_t*)&val)[0];
+
+ cutscene->commands.push_back(val);
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+
+ if (continueFlag == -1) {
+ break;
+ }
+ }
+ } break;
+ case (uint32_t)CutsceneCommands::SetCameraFocus: {
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+
+ while (true) {
+ uint32_t val = read_CMD_BBH(reader);
+ int8_t continueFlag = ((int8_t*)&val)[0];
+
+ cutscene->commands.push_back(val);
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+
+ if (continueFlag == -1) {
+ break;
+ }
+ }
+ break;
+ }
+ case (uint32_t)CutsceneCommands::SpecialAction: {
+ uint32_t size = reader->ReadUInt32();
+ cutscene->commands.push_back(size);
+
+ for (uint32_t i = 0; i < size; i++) {
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ }
+ break;
+ }
+ case (uint32_t)CutsceneCommands::SetLighting: {
+ uint32_t size = reader->ReadUInt32();
+ cutscene->commands.push_back(size);
+
+ for (uint32_t i = 0; i < size; i++) {
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ }
+ break;
+ }
+ case (uint32_t)CutsceneCommands::SetCameraPosLink: {
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+
+ while (true) {
+ uint32_t val = read_CMD_BBH(reader);
+ int8_t continueFlag = ((int8_t*)&val)[0];
+
+ cutscene->commands.push_back(val);
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+
+ if (continueFlag == -1) {
+ break;
+ }
+ }
+ break;
+ }
+ case (uint32_t)CutsceneCommands::SetCameraFocusLink: {
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+
+ while (true) {
+ uint32_t val = read_CMD_BBH(reader);
+ int8_t continueFlag = ((int8_t*)&val)[0];
+
+ cutscene->commands.push_back(val);
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+
+ if (continueFlag == -1) {
+ break;
+ }
+ }
+ break;
+ }
+ case (uint32_t)CutsceneCommands::Cmd09: {
+ uint32_t size = reader->ReadUInt32();
+ cutscene->commands.push_back(size);
+
+ for (uint32_t i = 0; i < size; i++) {
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HBB(reader));
+ cutscene->commands.push_back(read_CMD_BBH(reader));
+ }
+ break;
+ }
+ case 0x15:
+ case (uint32_t)CutsceneCommands::Unknown: {
+ uint32_t size = reader->ReadUInt32();
+ cutscene->commands.push_back(size);
+
+ for (uint32_t i = 0; i < size; i++) {
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ }
+ } break;
+ case (uint32_t)CutsceneCommands::Textbox: {
+ uint32_t size = reader->ReadUInt32();
+ cutscene->commands.push_back(size);
+
+ for (uint32_t i = 0; i < size; i++) {
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ }
+ break;
+ }
+ case (uint32_t)CutsceneCommands::SetActorAction0:
+ case (uint32_t)CutsceneCommands::SetActorAction1:
+ case 17:
+ case 18:
+ case 23:
+ case 34:
+ case 39:
+ case 46:
+ case 76:
+ case 85:
+ case 93:
+ case 105:
+ case 107:
+ case 110:
+ case 119:
+ case 123:
+ case 138:
+ case 139:
+ case 144:
+ case (uint32_t)CutsceneCommands::SetActorAction2:
+ case 16:
+ case 24:
+ case 35:
+ case 40:
+ case 48:
+ case 64:
+ case 68:
+ case 70:
+ case 78:
+ case 80:
+ case 94:
+ case 116:
+ case 118:
+ case 120:
+ case 125:
+ case 131:
+ case 141:
+ case (uint32_t)CutsceneCommands::SetActorAction3:
+ case 36:
+ case 41:
+ case 50:
+ case 67:
+ case 69:
+ case 72:
+ case 74:
+ case 81:
+ case 106:
+ case 117:
+ case 121:
+ case 126:
+ case 132:
+ case (uint32_t)CutsceneCommands::SetActorAction4:
+ case 37:
+ case 42:
+ case 51:
+ case 53:
+ case 63:
+ case 65:
+ case 66:
+ case 75:
+ case 82:
+ case 108:
+ case 127:
+ case 133:
+ case (uint32_t)CutsceneCommands::SetActorAction5:
+ case 38:
+ case 43:
+ case 47:
+ case 54:
+ case 79:
+ case 83:
+ case 128:
+ case 135:
+ case (uint32_t)CutsceneCommands::SetActorAction6:
+ case 55:
+ case 77:
+ case 84:
+ case 90:
+ case 129:
+ case 136:
+ case (uint32_t)CutsceneCommands::SetActorAction7:
+ case 52:
+ case 57:
+ case 58:
+ case 88:
+ case 115:
+ case 130:
+ case 137:
+ case (uint32_t)CutsceneCommands::SetActorAction8:
+ case 60:
+ case 89:
+ case 111:
+ case 114:
+ case 134:
+ case 142:
+ case (uint32_t)CutsceneCommands::SetActorAction9:
+ case (uint32_t)CutsceneCommands::SetActorAction10: {
+ uint32_t size = reader->ReadUInt32();
+ cutscene->commands.push_back(size);
+
+ for (uint32_t i = 0; i < size; i++) {
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ }
+
+ break;
+ }
+ case (uint32_t)CutsceneCommands::SetSceneTransFX: {
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ break;
+ }
+ case (uint32_t)CutsceneCommands::PlayBGM: {
+ uint32_t size = reader->ReadUInt32();
+ cutscene->commands.push_back(size);
+
+ for (uint32_t i = 0; i < size; i++) {
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ }
+ break;
+ }
+ case (uint32_t)CutsceneCommands::StopBGM: {
+ uint32_t size = reader->ReadUInt32();
+ cutscene->commands.push_back(size);
+
+ for (uint32_t i = 0; i < size; i++) {
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ }
+ break;
+ }
+ case (uint32_t)CutsceneCommands::FadeBGM: {
+ uint32_t size = reader->ReadUInt32();
+ cutscene->commands.push_back(size);
+
+ for (uint32_t i = 0; i < size; i++) {
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ }
+ break;
+ }
+ case (uint32_t)CutsceneCommands::SetTime: {
+ uint32_t size = reader->ReadUInt32();
+ cutscene->commands.push_back(size);
+
+ for (uint32_t i = 0; i < size; i++) {
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HBB(reader));
+ cutscene->commands.push_back(reader->ReadUInt32());
+ }
+ break;
+ }
+ case (uint32_t)CutsceneCommands::Terminator: {
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ break;
+ }
+ case 0xFFFFFFFF: // CS_END
+ {
+ cutscene->commands.push_back(reader->ReadUInt32());
+ return;
+ }
+ default:
+ SPDLOG_TRACE("CutsceneV0: Unknown command {}\n", commandId);
+ // error?
+ break;
+ }
+ }
+}
+
+void LUS::CutsceneFactoryV0::ParseFileBinaryMM(std::shared_ptr<BinaryReader> reader, std::shared_ptr<Cutscene> cutscene) {
+ while (true) {
+ uint32_t command = reader->ReadUInt32();
+ cutscene->commands.push_back(command);
+
+ if (((command >= CS_CMD_ACTOR_CUE_100) &&
+ (command <= CS_CMD_ACTOR_CUE_149)) ||
+ (command == CS_CMD_ACTOR_CUE_201) ||
+ ((command >= CS_CMD_ACTOR_CUE_450) &&
+ (command <= CS_CMD_ACTOR_CUE_599))) {
+ goto actorCue;
+ }
+
+ switch (command) {
+ case CS_CMD_TEXT: {
+ uint32_t size = reader->ReadUInt32();
+ //uint8_t type = reader->ReadInt8();
+ cutscene->commands.push_back(size);
+ // BENTODO do we need to read the type?
+
+ for (uint32_t i = 0; i < size; i++) {
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ }
+ break;
+ }
+ case CS_CMD_CAMERA_SPLINE: {
+ uint32_t size = reader->ReadUInt32();
+ cutscene->commands.push_back(size);
+
+ for (uint32_t i = 0; i < (size / 4); i++) {
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ }
+ break;
+ }
+ case CS_CMD_MISC: {
+ uint32_t size = reader->ReadUInt32();
+ cutscene->commands.push_back(size);
+
+ for (uint32_t i = 0; i < size; i++) {
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ }
+ break;
+ }
+ case CS_CMD_LIGHT_SETTING: {
+ uint32_t size = reader->ReadUInt32();
+ cutscene->commands.push_back(size);
+
+ for (uint32_t i = 0; i < size; i++) {
+ cutscene->commands.push_back(read_CMD_BBH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ }
+ break;
+ }
+ case CS_CMD_TRANSITION: {
+ uint32_t size = reader->ReadUInt32();
+ cutscene->commands.push_back(size);
+
+ for (uint32_t i = 0; i < size; i++) {
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ }
+ break;
+ }
+ case CS_CMD_MOTION_BLUR: {
+ uint32_t size = reader->ReadUInt32();
+ cutscene->commands.push_back(size);
+
+ for (uint32_t i = 0; i < size; i++) {
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ }
+ break;
+ }
+ case CS_CMD_GIVE_TATL: {
+ uint32_t size = reader->ReadUInt32();
+ cutscene->commands.push_back(size);
+
+ for (uint32_t i = 0; i < size; i++) {
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ }
+ break;
+ }
+ case CS_CMD_START_SEQ:
+ case CS_CMD_STOP_SEQ: {
+ uint32_t size = reader->ReadUInt32();
+ cutscene->commands.push_back(size);
+
+ for (uint32_t i = 0; i < size; i++) {
+ cutscene->commands.push_back(read_CMD_BBH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ }
+ break;
+ }
+ case CS_CMD_SFX_REVERB_INDEX_2:
+ case CS_CMD_SFX_REVERB_INDEX_1:
+ case CS_CMD_MODIFY_SEQ:
+
+ case CS_CMD_START_AMBIENCE:
+ case CS_CMD_FADE_OUT_AMBIENCE:
+ case CS_CMD_DESTINATION:
+ case CS_CMD_CHOOSE_CREDITS_SCENES:
+
+ case CS_CMD_UNK_DATA_FA:
+ case CS_CMD_UNK_DATA_FE:
+ case CS_CMD_UNK_DATA_FF:
+ case CS_CMD_UNK_DATA_100:
+ case CS_CMD_UNK_DATA_101:
+ case CS_CMD_UNK_DATA_102:
+ case CS_CMD_UNK_DATA_103:
+ case CS_CMD_UNK_DATA_104:
+ case CS_CMD_UNK_DATA_105:
+ case CS_CMD_UNK_DATA_108:
+ case CS_CMD_UNK_DATA_109: {
+ uint32_t size = reader->ReadUInt32();
+ cutscene->commands.push_back(size);
+
+ for (uint32_t i = 0; i < size; i++) {
+ cutscene->commands.push_back(read_CMD_BBH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ }
+ break;
+ }
+ case CS_CMD_TRANSITION_GENERAL: {
+ uint32_t size = reader->ReadUInt32();
+ cutscene->commands.push_back(size);
+
+ for (uint32_t i = 0; i < size; i++) {
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HBB(reader));
+ cutscene->commands.push_back(read_CMD_BBBB(reader));
+ }
+ break;
+ }
+ case CS_CMD_FADE_OUT_SEQ: {
+ uint32_t size = reader->ReadUInt32();
+ cutscene->commands.push_back(size);
+
+ for (uint32_t i = 0; i < size; i++) {
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ }
+ break;
+ }
+ case CS_CMD_TIME: {
+ uint32_t size = reader->ReadUInt32();
+ cutscene->commands.push_back(size);
+
+ for (uint32_t i = 0; i < size; i++) {
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HBB(reader));
+ }
+ break;
+ }
+ case CS_CMD_PLAYER_CUE: {
+ actorCue:
+ uint32_t size = reader->ReadUInt32();
+ cutscene->commands.push_back(size);
+ for (uint32_t i = 0; i < size; i++) {
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ cutscene->commands.push_back(reader->ReadUInt32());
+ }
+ break;
+ }
+ case CS_CMD_RUMBLE: {
+ uint32_t size = reader->ReadUInt32();
+ cutscene->commands.push_back(size);
+
+ for (uint32_t i = 0; i < size; i++) {
+ cutscene->commands.push_back(read_CMD_HH(reader));
+ cutscene->commands.push_back(read_CMD_HBB(reader));
+ cutscene->commands.push_back(read_CMD_BBBB(reader));
+ }
+ break;
+ }
+ case 0xFFFFFFFF: {
+ cutscene->commands.push_back(reader->ReadUInt32());
+ return;
+ }
+ default:
+ SPDLOG_TRACE("CutsceneV0: Unknown command {}\n", command);
+ // error?
+ break;
+ }
+ }
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/CutsceneFactory.h b/mm/2s2h/resource/importer/CutsceneFactory.h
new file mode 100644
index 000000000..729041ce3
--- /dev/null
+++ b/mm/2s2h/resource/importer/CutsceneFactory.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "Resource.h"
+#include "ResourceFactory.h"
+
+
+namespace LUS {
+class Cutscene;
+class CutsceneFactory : public ResourceFactory
+{
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class CutsceneFactoryV0 : public ResourceVersionFactory
+{
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+
+ private:
+ void ParseFileBinaryOoT(std::shared_ptr<BinaryReader> reader,std::shared_ptr<Cutscene> cutscene);
+ void ParseFileBinaryMM(std::shared_ptr<BinaryReader> reader, std::shared_ptr<Cutscene> cutscene);
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/PathFactory.cpp b/mm/2s2h/resource/importer/PathFactory.cpp
new file mode 100644
index 000000000..07e5b6e7b
--- /dev/null
+++ b/mm/2s2h/resource/importer/PathFactory.cpp
@@ -0,0 +1,113 @@
+#include "2s2h/resource/importer/PathFactory.h"
+#include "2s2h/resource/type/Path.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource>
+PathFactory::ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<Path>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<PathFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load Path with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::PathFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource) {
+ std::shared_ptr<Path> path = std::static_pointer_cast<Path>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, path);
+
+ path->numPaths = reader->ReadUInt32();
+ path->paths.reserve(path->numPaths);
+ for (uint32_t k = 0; k < path->numPaths; k++) {
+ std::vector<Vec3s> points;
+ uint32_t pointCount = reader->ReadUInt32();
+ points.reserve(pointCount);
+ for (uint32_t i = 0; i < pointCount; i++) {
+ Vec3s point;
+ point.x = reader->ReadInt16();
+ point.y = reader->ReadInt16();
+ point.z = reader->ReadInt16();
+
+ points.push_back(point);
+ }
+
+ PathData pathDataEntry;
+ pathDataEntry.count = pointCount;
+
+ path->paths.push_back(points);
+ pathDataEntry.points = path->paths.back().data();
+
+ path->pathData.push_back(pathDataEntry);
+ }
+}
+
+std::shared_ptr<IResource> PathFactoryMM::ReadResource(std::shared_ptr<ResourceInitData> initData,
+ std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<Path>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<PathFactoryMMV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load Path with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::PathFactoryMMV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) {
+ std::shared_ptr<PathMM> path = std::static_pointer_cast<PathMM>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, path);
+
+ path->numPaths = reader->ReadUInt32();
+ path->paths.reserve(path->numPaths);
+ for (uint32_t k = 0; k < path->numPaths; k++) {
+ std::vector<Vec3s> points;
+ uint32_t pointCount = reader->ReadUInt32();
+
+ uint8_t additionalPathIndex = reader->ReadUByte();
+ int16_t customValue = reader->ReadInt16();
+
+ for (uint32_t i = 0; i < pointCount; i++) {
+ Vec3s point;
+ point.x = reader->ReadInt16();
+ point.y = reader->ReadInt16();
+ point.z = reader->ReadInt16();
+
+ points.push_back(point);
+ }
+
+ PathDataMM pathDataEntry;
+ pathDataEntry.count = pointCount;
+ pathDataEntry.additionalPathIndex = additionalPathIndex;
+ pathDataEntry.customValue = customValue;
+
+ path->paths.push_back(points);
+ pathDataEntry.points = path->paths.back().data();
+
+ path->pathData.push_back(pathDataEntry);
+ }
+}
+
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/PathFactory.h b/mm/2s2h/resource/importer/PathFactory.h
new file mode 100644
index 000000000..08f9e26cf
--- /dev/null
+++ b/mm/2s2h/resource/importer/PathFactory.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include "Resource.h"
+#include "ResourceFactory.h"
+
+namespace LUS {
+class PathFactory : public ResourceFactory
+{
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class PathFactoryV0 : public ResourceVersionFactory
+{
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+
+class PathFactoryMM : public ResourceFactory {
+ public:
+ std::shared_ptr<IResource> ReadResource(std::shared_ptr<ResourceInitData> initData,
+ std::shared_ptr<BinaryReader> reader) override;
+};
+
+class PathFactoryMMV0 : public ResourceVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/PlayerAnimationFactory.cpp b/mm/2s2h/resource/importer/PlayerAnimationFactory.cpp
new file mode 100644
index 000000000..7e6aba360
--- /dev/null
+++ b/mm/2s2h/resource/importer/PlayerAnimationFactory.cpp
@@ -0,0 +1,41 @@
+#include "2s2h/resource/importer/PlayerAnimationFactory.h"
+#include "2s2h/resource/type/PlayerAnimation.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource>
+PlayerAnimationFactory::ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<PlayerAnimation>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<PlayerAnimationFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr)
+ {
+ SPDLOG_ERROR("Failed to load PlayerAnimation with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::PlayerAnimationFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource)
+{
+ std::shared_ptr<PlayerAnimation> playerAnimation = std::static_pointer_cast<PlayerAnimation>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, playerAnimation);
+
+ uint32_t numEntries = reader->ReadUInt32();
+ playerAnimation->limbRotData.reserve(numEntries);
+
+ for (uint32_t i = 0; i < numEntries; i++) {
+ playerAnimation->limbRotData.push_back(reader->ReadInt16());
+ }
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/PlayerAnimationFactory.h b/mm/2s2h/resource/importer/PlayerAnimationFactory.h
new file mode 100644
index 000000000..b4981f7c6
--- /dev/null
+++ b/mm/2s2h/resource/importer/PlayerAnimationFactory.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "Resource.h"
+#include "ResourceFactory.h"
+
+namespace LUS {
+class PlayerAnimationFactory : public ResourceFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class PlayerAnimationFactoryV0 : public ResourceVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/SceneFactory.cpp b/mm/2s2h/resource/importer/SceneFactory.cpp
new file mode 100644
index 000000000..0e93da4f6
--- /dev/null
+++ b/mm/2s2h/resource/importer/SceneFactory.cpp
@@ -0,0 +1,140 @@
+#include "spdlog/spdlog.h"
+#include "2s2h/resource/importer/SceneFactory.h"
+#include "2s2h/resource/type/Scene.h"
+#include "2s2h/resource/type/scenecommand/SceneCommand.h"
+#include "2s2h/resource/importer/scenecommand/SetLightingSettingsFactory.h"
+#include "2s2h/resource/importer/scenecommand/SetWindSettingsFactory.h"
+#include "2s2h/resource/importer/scenecommand/SetExitListFactory.h"
+#include "2s2h/resource/importer/scenecommand/SetTimeSettingsFactory.h"
+#include "2s2h/resource/importer/scenecommand/SetSkyboxModifierFactory.h"
+#include "2s2h/resource/importer/scenecommand/SetEchoSettingsFactory.h"
+#include "2s2h/resource/importer/scenecommand/SetSoundSettingsFactory.h"
+#include "2s2h/resource/importer/scenecommand/SetSkyboxSettingsFactory.h"
+#include "2s2h/resource/importer/scenecommand/SetRoomBehaviorFactory.h"
+#include "2s2h/resource/importer/scenecommand/SetCsCameraFactory.h"
+#include "2s2h/resource/importer/scenecommand/SetCameraSettingsFactory.h"
+#include "2s2h/resource/importer/scenecommand/SetRoomListFactory.h"
+#include "2s2h/resource/importer/scenecommand/SetCollisionHeaderFactory.h"
+#include "2s2h/resource/importer/scenecommand/SetEntranceListFactory.h"
+#include "2s2h/resource/importer/scenecommand/SetSpecialObjectsFactory.h"
+#include "2s2h/resource/importer/scenecommand/SetObjectListFactory.h"
+#include "2s2h/resource/importer/scenecommand/SetStartPositionListFactory.h"
+#include "2s2h/resource/importer/scenecommand/SetActorListFactory.h"
+#include "2s2h/resource/importer/scenecommand/SetTransitionActorListFactory.h"
+#include "2s2h/resource/importer/scenecommand/EndMarkerFactory.h"
+#include "2s2h/resource/importer/scenecommand/SetAlternateHeadersFactory.h"
+#include "2s2h/resource/importer/scenecommand/SetPathwaysFactory.h"
+#include "2s2h/resource/importer/scenecommand/SetCutscenesFactory.h"
+#include "2s2h/resource/importer/scenecommand/SetLightListFactory.h"
+#include "2s2h/resource/importer/scenecommand/SetMeshFactory.h"
+#include "2s2h/resource/importer/scenecommand/SetAnimatedMaterialListFactory.h"
+#include "2s2h/resource/importer/scenecommand/SetMinimapListFactory.h"
+#include "2s2h/resource/importer/scenecommand/SetMinimapChestsFactory.h"
+#include "2s2h/resource/importer/scenecommand/SetActorCutsceneListFactory.h"
+
+namespace LUS {
+
+std::shared_ptr<IResource>
+SceneFactory::ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) {
+ if (SceneFactory::sceneCommandFactories.empty()) {
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::SetLightingSettings] = std::make_shared<SetLightingSettingsFactory>();
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::SetWind] = std::make_shared<SetWindSettingsFactory>();
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::SetExitList] = std::make_shared<SetExitListFactory>();
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::SetTimeSettings] = std::make_shared<SetTimeSettingsFactory>();
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::SetSkyboxModifier] = std::make_shared<SetSkyboxModifierFactory>();
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::SetEchoSettings] = std::make_shared<SetEchoSettingsFactory>();
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::SetSoundSettings] = std::make_shared<SetSoundSettingsFactory>();
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::SetSkyboxSettings] = std::make_shared<SetSkyboxSettingsFactory>();
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::SetRoomBehavior] = std::make_shared<SetRoomBehaviorFactory>();
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::SetCsCamera] = std::make_shared<SetCsCameraFactory>();
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::SetCameraSettings] = std::make_shared<SetCameraSettingsFactory>();
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::SetRoomList] = std::make_shared<SetRoomListFactory>();
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::SetCollisionHeader] = std::make_shared<SetCollisionHeaderFactory>();
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::SetEntranceList] = std::make_shared<SetEntranceListFactory>();
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::SetSpecialObjects] = std::make_shared<SetSpecialObjectsFactory>();
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::SetObjectList] = std::make_shared<SetObjectListFactory>();
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::SetStartPositionList] = std::make_shared<SetStartPositionListFactory>();
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::SetActorList] = std::make_shared<SetActorListFactory>();
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::SetTransitionActorList] = std::make_shared<SetTransitionActorListFactory>();
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::EndMarker] = std::make_shared<EndMarkerFactory>();
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::SetAlternateHeaders] = std::make_shared<SetAlternateHeadersFactory>();
+ // TODO should we use a different custom scene command like cutscenes?
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::SetPathways] = std::make_shared<SetPathwaysMMFactory>();
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::SetCutscenes] = std::make_shared<SetCutscenesFactory>();
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::SetLightList] = std::make_shared<SetLightListFactory>();
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::SetMesh] = std::make_shared<SetMeshFactory>();
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::SetCutscenesMM] = std::make_shared<SetCutsceneFactoryMM>();
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::SetAnimatedMaterialList] =
+ std::make_shared<SetAnimatedMaterialListFactory>();
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::SetMinimapList] =
+ std::make_shared<SetMinimapListFactory>();
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::SetMinimapChests] =
+ std::make_shared<SetMinimapChestsFactory>();
+ SceneFactory::sceneCommandFactories[LUS::SceneCommandID::SetActorCutsceneList] =
+ std::make_shared<SetActorCutsceneListFactory>();
+ }
+
+ auto resource = std::make_shared<Scene>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SceneFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load Scene with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void SceneFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource)
+{
+ std::shared_ptr<Scene> scene = std::static_pointer_cast<Scene>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, scene);
+
+ ParseSceneCommands(scene, reader);
+}
+
+void SceneFactoryV0::ParseSceneCommands(std::shared_ptr<Scene> scene, std::shared_ptr<BinaryReader> reader) {
+ uint32_t commandCount = reader->ReadUInt32();
+ scene->commands.reserve(commandCount);
+
+ for (uint32_t i = 0; i < commandCount; i++) {
+ scene->commands.push_back(ParseSceneCommand(scene, reader, i));
+ }
+}
+
+std::shared_ptr<ISceneCommand> SceneFactoryV0::ParseSceneCommand(std::shared_ptr<Scene> scene,
+ std::shared_ptr<BinaryReader> reader, uint32_t index) {
+ SceneCommandID cmdID = (SceneCommandID)reader->ReadInt32();
+
+ reader->Seek(-sizeof(int32_t), SeekOffsetType::Current);
+
+ std::shared_ptr<ISceneCommand> result = nullptr;
+ std::shared_ptr<SceneCommandFactory> commandFactory = SceneFactory::sceneCommandFactories[cmdID];
+
+ if (commandFactory != nullptr) {
+ auto initData = std::make_shared<ResourceInitData>();
+ initData->Id = scene->GetInitData()->Id;
+ initData->Type = ResourceType::SOH_SceneCommand;
+ initData->Path = scene->GetInitData()->Path + "/SceneCommand" + std::to_string(index);
+ initData->ResourceVersion = scene->GetInitData()->ResourceVersion;
+ result = std::static_pointer_cast<ISceneCommand>(commandFactory->ReadResource(initData, reader));
+ // Cache the resource?
+ }
+
+ if (result == nullptr) {
+ SPDLOG_ERROR("Failed to load scene command of type {} in scene {}", (uint32_t)cmdID, scene->GetInitData()->Path);
+ }
+
+ return result;
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/SceneFactory.h b/mm/2s2h/resource/importer/SceneFactory.h
new file mode 100644
index 000000000..40887983d
--- /dev/null
+++ b/mm/2s2h/resource/importer/SceneFactory.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "2s2h/resource/type/Scene.h"
+#include "2s2h/resource/type/scenecommand/SceneCommand.h"
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+#include "Resource.h"
+#include "ResourceFactory.h"
+
+namespace LUS {
+class SceneFactory : public ResourceFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+
+ // Doing something very similar to what we do on the ResourceLoader.
+ // Eventually, scene commands should be moved up to the ResourceLoader as well.
+ // They can not right now because the exporter does not give them a proper resource type enum value,
+ // and the exporter does not export the commands with a proper OTR header.
+ static inline std::unordered_map<SceneCommandID, std::shared_ptr<SceneCommandFactory>> sceneCommandFactories;
+};
+
+class SceneFactoryV0 : public ResourceVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+ void ParseSceneCommands(std::shared_ptr<Scene> scene, std::shared_ptr<BinaryReader> reader);
+protected:
+ std::shared_ptr<ISceneCommand> ParseSceneCommand(std::shared_ptr<Scene> scene, std::shared_ptr<BinaryReader> reader, uint32_t index);
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/SkeletonFactory.cpp b/mm/2s2h/resource/importer/SkeletonFactory.cpp
new file mode 100644
index 000000000..aa4c814bd
--- /dev/null
+++ b/mm/2s2h/resource/importer/SkeletonFactory.cpp
@@ -0,0 +1,158 @@
+#include "2s2h/resource/importer/SkeletonFactory.h"
+#include "2s2h/resource/type/Skeleton.h"
+#include <spdlog/spdlog.h>
+#include <libultraship/libultraship.h>
+
+namespace LUS {
+std::shared_ptr<IResource>
+SkeletonFactory::ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<Skeleton>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SkeletonFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load Skeleton with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+std::shared_ptr<IResource>
+SkeletonFactory::ReadResourceXML(std::shared_ptr<ResourceInitData> initData, tinyxml2::XMLElement *reader) {
+ auto resource = std::make_shared<Skeleton>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SkeletonFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load Skeleton with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileXML(reader, resource);
+
+ return resource;
+}
+
+void SkeletonFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource)
+{
+ std::shared_ptr<Skeleton> skeleton = std::static_pointer_cast<Skeleton>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, skeleton);
+
+ skeleton->type = (SkeletonType)reader->ReadInt8();
+ skeleton->limbType = (LimbType)reader->ReadInt8();
+ skeleton->limbCount = reader->ReadUInt32();
+ skeleton->dListCount = reader->ReadUInt32();
+ skeleton->limbTableType = (LimbType)reader->ReadInt8();
+ skeleton->limbTableCount = reader->ReadUInt32();
+
+ skeleton->limbTable.reserve(skeleton->limbTableCount);
+ for (uint32_t i = 0; i < skeleton->limbTableCount; i++) {
+ std::string limbPath = reader->ReadString();
+
+ skeleton->limbTable.push_back(limbPath);
+ }
+
+ if (skeleton->type == LUS::SkeletonType::Curve) {
+ skeleton->skeletonData.skelCurveLimbList.limbCount = skeleton->limbCount;
+ skeleton->curveLimbArray.reserve(skeleton->skeletonData.skelCurveLimbList.limbCount);
+ } else if (skeleton->type == LUS::SkeletonType::Flex) {
+ skeleton->skeletonData.flexSkeletonHeader.dListCount = skeleton->dListCount;
+ }
+
+ if (skeleton->type == LUS::SkeletonType::Normal) {
+ skeleton->skeletonData.skeletonHeader.limbCount = skeleton->limbCount;
+ skeleton->standardLimbArray.reserve(skeleton->skeletonData.skeletonHeader.limbCount);
+ } else if (skeleton->type == LUS::SkeletonType::Flex) {
+ skeleton->skeletonData.flexSkeletonHeader.sh.limbCount = skeleton->limbCount;
+ skeleton->standardLimbArray.reserve(skeleton->skeletonData.flexSkeletonHeader.sh.limbCount);
+ }
+
+ for (size_t i = 0; i < skeleton->limbTable.size(); i++) {
+ std::string limbStr = skeleton->limbTable[i];
+ auto limb = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(limbStr.c_str());
+ skeleton->skeletonHeaderSegments.push_back(limb ? limb->GetRawPointer() : nullptr);
+ }
+
+ if (skeleton->type == LUS::SkeletonType::Normal) {
+ skeleton->skeletonData.skeletonHeader.segment = (void**)skeleton->skeletonHeaderSegments.data();
+ } else if (skeleton->type == LUS::SkeletonType::Flex) {
+ skeleton->skeletonData.flexSkeletonHeader.sh.segment = (void**)skeleton->skeletonHeaderSegments.data();
+ } else if (skeleton->type == LUS::SkeletonType::Curve) {
+ skeleton->skeletonData.skelCurveLimbList.limbs = (SkelCurveLimb**)skeleton->skeletonHeaderSegments.data();
+ } else {
+ SPDLOG_ERROR("unknown skeleton type {}", (uint32_t)skeleton->type);
+ }
+
+ skeleton->skeletonData.skeletonHeader.skeletonType = (uint8_t)skeleton->type;
+}
+void SkeletonFactoryV0::ParseFileXML(tinyxml2::XMLElement* reader, std::shared_ptr<IResource> resource)
+{
+ std::shared_ptr<Skeleton> skel = std::static_pointer_cast<Skeleton>(resource);
+
+ std::string skeletonType = reader->Attribute("Type");
+ // std::string skeletonLimbType = reader->Attribute("LimbType");
+ int numLimbs = reader->IntAttribute("LimbCount");
+ int numDLs = reader->IntAttribute("DisplayListCount");
+
+ if (skeletonType == "Flex") {
+ skel->type = SkeletonType::Flex;
+ } else if (skeletonType == "Curve") {
+ skel->type = SkeletonType::Curve;
+ } else if (skeletonType == "Normal") {
+ skel->type = SkeletonType::Normal;
+ }
+
+ skel->type = SkeletonType::Flex;
+ skel->limbType = LimbType::LOD;
+
+ // if (skeletonLimbType == "Standard")
+ // skel->limbType = LimbType::Standard;
+ // else if (skeletonLimbType == "LOD")
+ // skel->limbType = LimbType::LOD;
+ // else if (skeletonLimbType == "Curve")
+ // skel->limbType = LimbType::Curve;
+ // else if (skeletonLimbType == "Skin")
+ // skel->limbType = LimbType::Skin;
+ // else if (skeletonLimbType == "Legacy")
+ // Sskel->limbType = LimbType::Legacy;
+
+ auto child = reader->FirstChildElement();
+
+ skel->limbCount = numLimbs;
+ skel->dListCount = numDLs;
+
+ while (child != nullptr) {
+ std::string childName = child->Name();
+
+ if (childName == "SkeletonLimb") {
+ std::string limbName = child->Attribute("Path");
+ skel->limbTable.push_back(limbName);
+
+ auto limb = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(limbName.c_str());
+ skel->skeletonHeaderSegments.push_back(limb ? limb->GetRawPointer() : nullptr);
+ }
+
+ child = child->NextSiblingElement();
+ }
+
+ skel->skeletonData.flexSkeletonHeader.sh.limbCount = skel->limbCount;
+ skel->skeletonData.flexSkeletonHeader.sh.segment = (void**)skel->skeletonHeaderSegments.data();
+ skel->skeletonData.flexSkeletonHeader.dListCount = skel->dListCount;
+ skel->skeletonData.skeletonHeader.skeletonType = (uint8_t)skel->type;
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/SkeletonFactory.h b/mm/2s2h/resource/importer/SkeletonFactory.h
new file mode 100644
index 000000000..d64449393
--- /dev/null
+++ b/mm/2s2h/resource/importer/SkeletonFactory.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "Resource.h"
+#include "ResourceFactory.h"
+
+namespace LUS {
+class SkeletonFactory : public ResourceFactory
+{
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+ std::shared_ptr<IResource>
+ ReadResourceXML(std::shared_ptr<ResourceInitData> initData, tinyxml2::XMLElement *reader) override;
+};
+
+class SkeletonFactoryV0 : public ResourceVersionFactory
+{
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+ void ParseFileXML(tinyxml2::XMLElement* reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
+
diff --git a/mm/2s2h/resource/importer/SkeletonLimbFactory.cpp b/mm/2s2h/resource/importer/SkeletonLimbFactory.cpp
new file mode 100644
index 000000000..ad2fec085
--- /dev/null
+++ b/mm/2s2h/resource/importer/SkeletonLimbFactory.cpp
@@ -0,0 +1,271 @@
+#include "2s2h/resource/importer/SkeletonLimbFactory.h"
+#include "2s2h/resource/type/SkeletonLimb.h"
+#include "spdlog/spdlog.h"
+#include "libultraship/libultraship.h"
+
+namespace LUS {
+std::shared_ptr<IResource>
+SkeletonLimbFactory::ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SkeletonLimb>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SkeletonLimbFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load Skeleton Limb with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+std::shared_ptr<IResource>
+SkeletonLimbFactory::ReadResourceXML(std::shared_ptr<ResourceInitData> initData, tinyxml2::XMLElement *reader) {
+ auto resource = std::make_shared<SkeletonLimb>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SkeletonLimbFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load Skeleton Limb with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileXML(reader, resource);
+
+ return resource;
+}
+
+void LUS::SkeletonLimbFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource)
+{
+ std::shared_ptr<SkeletonLimb> skeletonLimb = std::static_pointer_cast<SkeletonLimb>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, skeletonLimb);
+
+ skeletonLimb->limbType = (LimbType)reader->ReadInt8();
+ skeletonLimb->skinSegmentType = (ZLimbSkinType)reader->ReadInt8();
+ skeletonLimb->skinDList = reader->ReadString();
+
+ skeletonLimb->skinVtxCnt = reader->ReadUInt16();
+
+ skeletonLimb->skinLimbModifCount = reader->ReadUInt32();
+ skeletonLimb->skinLimbModifArray.reserve(skeletonLimb->skinLimbModifCount);
+ skeletonLimb->skinLimbModifVertexArrays.reserve(skeletonLimb->skinLimbModifCount);
+ skeletonLimb->skinLimbModifTransformationArrays.reserve(skeletonLimb->skinLimbModifCount);
+ for (size_t i = 0; i < skeletonLimb->skinLimbModifCount; i++) {
+ SkinLimbModif skinLimbModif;
+ skinLimbModif.unk_4 = reader->ReadUInt16();
+ skeletonLimb->skinLimbModifArray.push_back(skinLimbModif);
+
+ std::vector<SkinVertex> skinVertexArray;
+ int32_t skinVertexCount = reader->ReadInt32();
+ skinVertexArray.reserve(skinVertexCount);
+ for (int32_t k = 0; k < skinVertexCount; k++) {
+ SkinVertex skinVertex;
+
+ skinVertex.index = reader->ReadInt16();
+ skinVertex.s = reader->ReadInt16();
+ skinVertex.t = reader->ReadInt16();
+ skinVertex.normX = reader->ReadInt8();
+ skinVertex.normY = reader->ReadInt8();
+ skinVertex.normZ = reader->ReadInt8();
+ skinVertex.alpha = reader->ReadUByte();
+
+ skinVertexArray.push_back(skinVertex);
+ }
+ skeletonLimb->skinLimbModifVertexArrays.push_back(skinVertexArray);
+
+ std::vector<SkinTransformation> skinTransformationArray;
+ int32_t skinTransformationCount = reader->ReadInt32();
+ skinTransformationArray.reserve(skinTransformationCount);
+ for (int32_t k = 0; k < skinTransformationCount; k++) {
+ SkinTransformation skinTransformation;
+
+ skinTransformation.limbIndex = reader->ReadUByte();
+ skinTransformation.x = reader->ReadInt16();
+ skinTransformation.y = reader->ReadInt16();
+ skinTransformation.z = reader->ReadInt16();
+ skinTransformation.scale = reader->ReadUByte();
+
+ skinTransformationArray.push_back(skinTransformation);
+ }
+ skeletonLimb->skinLimbModifTransformationArrays.push_back(skinTransformationArray);
+ }
+
+ skeletonLimb->skinDList2 = reader->ReadString();
+
+ skeletonLimb->legTransX = reader->ReadFloat();
+ skeletonLimb->legTransY = reader->ReadFloat();
+ skeletonLimb->legTransZ = reader->ReadFloat();
+
+ skeletonLimb->rotX = reader->ReadUInt16();
+ skeletonLimb->rotY = reader->ReadUInt16();
+ skeletonLimb->rotZ = reader->ReadUInt16();
+
+ skeletonLimb->childPtr = reader->ReadString();
+ skeletonLimb->siblingPtr = reader->ReadString();
+ skeletonLimb->dListPtr = reader->ReadString();
+ skeletonLimb->dList2Ptr = reader->ReadString();
+
+ skeletonLimb->transX = reader->ReadInt16();
+ skeletonLimb->transY = reader->ReadInt16();
+ skeletonLimb->transZ = reader->ReadInt16();
+
+ skeletonLimb->childIndex = reader->ReadUByte();
+ skeletonLimb->siblingIndex = reader->ReadUByte();
+
+ if (skeletonLimb->limbType == LUS::LimbType::LOD) {
+ skeletonLimb->limbData.lodLimb.jointPos.x = skeletonLimb->transX;
+ skeletonLimb->limbData.lodLimb.jointPos.y = skeletonLimb->transY;
+ skeletonLimb->limbData.lodLimb.jointPos.z = skeletonLimb->transZ;
+ skeletonLimb->limbData.lodLimb.child = skeletonLimb->childIndex;
+ skeletonLimb->limbData.lodLimb.sibling = skeletonLimb->siblingIndex;
+
+ if (skeletonLimb->dListPtr != "") {
+ auto dList = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(skeletonLimb->dListPtr.c_str());
+ skeletonLimb->limbData.lodLimb.dLists[0] = (Gfx*)(dList ? dList->GetRawPointer() : nullptr);
+ } else {
+ skeletonLimb->limbData.lodLimb.dLists[0] = nullptr;
+ }
+
+ if (skeletonLimb->dList2Ptr != "") {
+ auto dList = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(skeletonLimb->dList2Ptr.c_str());
+ skeletonLimb->limbData.lodLimb.dLists[1] = (Gfx*)(dList ? dList->GetRawPointer() : nullptr);
+ } else {
+ skeletonLimb->limbData.lodLimb.dLists[1] = nullptr;
+ }
+ } else if (skeletonLimb->limbType == LUS::LimbType::Standard) {
+ skeletonLimb->limbData.standardLimb.jointPos.x = skeletonLimb->transX;
+ skeletonLimb->limbData.standardLimb.jointPos.y = skeletonLimb->transY;
+ skeletonLimb->limbData.standardLimb.jointPos.z = skeletonLimb->transZ;
+ skeletonLimb->limbData.standardLimb.child = skeletonLimb->childIndex;
+ skeletonLimb->limbData.standardLimb.sibling = skeletonLimb->siblingIndex;
+ skeletonLimb->limbData.standardLimb.dList = nullptr;
+
+ if (!skeletonLimb->dListPtr.empty()) {
+ const auto dList = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(skeletonLimb->dListPtr.c_str());
+ skeletonLimb->limbData.standardLimb.dList = (Gfx*)(dList ? dList->GetRawPointer() : nullptr);
+ }
+ } else if (skeletonLimb->limbType == LUS::LimbType::Curve) {
+ skeletonLimb->limbData.skelCurveLimb.firstChildIdx = skeletonLimb->childIndex;
+ skeletonLimb->limbData.skelCurveLimb.nextLimbIdx = skeletonLimb->siblingIndex;
+ skeletonLimb->limbData.skelCurveLimb.dList[0] = nullptr;
+ skeletonLimb->limbData.skelCurveLimb.dList[1] = nullptr;
+
+ if (!skeletonLimb->dListPtr.empty()) {
+ const auto dList = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(skeletonLimb->dListPtr.c_str());
+ skeletonLimb->limbData.skelCurveLimb.dList[0] = (Gfx*)(dList ? dList->GetRawPointer() : nullptr);
+ }
+
+ if (!skeletonLimb->dList2Ptr.empty()) {
+ const auto dList = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(skeletonLimb->dList2Ptr.c_str());
+ skeletonLimb->limbData.skelCurveLimb.dList[1] = (Gfx*)(dList ? dList->GetRawPointer() : nullptr);
+ }
+ } else if (skeletonLimb->limbType == LUS::LimbType::Skin) {
+ skeletonLimb->limbData.skinLimb.jointPos.x = skeletonLimb->transX;
+ skeletonLimb->limbData.skinLimb.jointPos.y = skeletonLimb->transY;
+ skeletonLimb->limbData.skinLimb.jointPos.z = skeletonLimb->transZ;
+ skeletonLimb->limbData.skinLimb.child = skeletonLimb->childIndex;
+ skeletonLimb->limbData.skinLimb.sibling = skeletonLimb->siblingIndex;
+
+ if (skeletonLimb->skinSegmentType == LUS::ZLimbSkinType::SkinType_DList) {
+ skeletonLimb->limbData.skinLimb.segmentType = static_cast<int32_t>(skeletonLimb->skinSegmentType);
+ } else if (skeletonLimb->skinSegmentType == LUS::ZLimbSkinType::SkinType_4) {
+ skeletonLimb->limbData.skinLimb.segmentType = 4;
+ } else if (skeletonLimb->skinSegmentType == LUS::ZLimbSkinType::SkinType_5) {
+ skeletonLimb->limbData.skinLimb.segmentType = 5;
+ } else {
+ skeletonLimb->limbData.skinLimb.segmentType = 0;
+ }
+
+ if (skeletonLimb->skinSegmentType == LUS::ZLimbSkinType::SkinType_DList) {
+ auto res = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(skeletonLimb->skinDList.c_str());
+ skeletonLimb->limbData.skinLimb.segment = res ? res->GetRawPointer() : nullptr;
+ } else if (skeletonLimb->skinSegmentType == LUS::ZLimbSkinType::SkinType_4) {
+ skeletonLimb->skinAnimLimbData.totalVtxCount = skeletonLimb->skinVtxCnt;
+ skeletonLimb->skinAnimLimbData.limbModifCount = skeletonLimb->skinLimbModifCount;
+ skeletonLimb->skinAnimLimbData.limbModifications = skeletonLimb->skinLimbModifArray.data();
+ auto res = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(skeletonLimb->skinDList2.c_str());
+ skeletonLimb->skinAnimLimbData.dlist = (Gfx*)(res ? res->GetRawPointer() : nullptr);
+
+ for (size_t i = 0; i < skeletonLimb->skinLimbModifArray.size(); i++) {
+ skeletonLimb->skinAnimLimbData.limbModifications[i].vtxCount = skeletonLimb->skinLimbModifVertexArrays[i].size();
+ skeletonLimb->skinAnimLimbData.limbModifications[i].skinVertices = skeletonLimb->skinLimbModifVertexArrays[i].data();
+
+ skeletonLimb->skinAnimLimbData.limbModifications[i].transformCount = skeletonLimb->skinLimbModifTransformationArrays[i].size();
+ skeletonLimb->skinAnimLimbData.limbModifications[i].limbTransformations = skeletonLimb->skinLimbModifTransformationArrays[i].data();
+
+ skeletonLimb->skinAnimLimbData.limbModifications[i].unk_4 = skeletonLimb->skinLimbModifArray[i].unk_4;
+ }
+
+ skeletonLimb->limbData.skinLimb.segment = &skeletonLimb->skinAnimLimbData;
+ }
+ }
+}
+void SkeletonLimbFactoryV0::ParseFileXML(tinyxml2::XMLElement* reader, std::shared_ptr<IResource> resource)
+{
+ std::shared_ptr<SkeletonLimb> skelLimb = std::static_pointer_cast<SkeletonLimb>(resource);
+
+ std::string limbType = reader->Attribute("Type");
+
+ // OTRTODO
+ skelLimb->limbType = LimbType::LOD;
+
+ // skelLimb->legTransX = reader->FloatAttribute("LegTransX");
+ // skelLimb->legTransY = reader->FloatAttribute("LegTransY");
+ // skelLimb->legTransZ = reader->FloatAttribute("LegTransZ");
+ skelLimb->rotX = reader->IntAttribute("RotX");
+ skelLimb->rotY = reader->IntAttribute("RotY");
+ skelLimb->rotZ = reader->IntAttribute("RotZ");
+
+ // skelLimb->transX = reader->IntAttribute("TransX");
+ // skelLimb->transY = reader->IntAttribute("TransY");
+ // skelLimb->transZ = reader->IntAttribute("TransZ");
+
+ skelLimb->transX = (int)reader->FloatAttribute("LegTransX");
+ skelLimb->transY = (int)reader->FloatAttribute("LegTransY");
+ skelLimb->transZ = (int)reader->FloatAttribute("LegTransZ");
+
+ skelLimb->childIndex = reader->IntAttribute("ChildIndex");
+ skelLimb->siblingIndex = reader->IntAttribute("SiblingIndex");
+
+ // skelLimb->childPtr = reader->Attribute("ChildLimb");
+ // skelLimb->siblingPtr = reader->Attribute("SiblingLimb");
+ skelLimb->dListPtr = reader->Attribute("DisplayList1");
+
+ if (std::string(reader->Attribute("DisplayList1")) == "gEmptyDL") {
+ skelLimb->dListPtr = "";
+ }
+
+ auto& limbData = skelLimb->limbData;
+
+ limbData.lodLimb.jointPos.x = skelLimb->transX;
+ limbData.lodLimb.jointPos.y = skelLimb->transY;
+ limbData.lodLimb.jointPos.z = skelLimb->transZ;
+
+ if (skelLimb->dListPtr != "") {
+ auto res = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess((const char*)skelLimb->dListPtr.c_str());
+ limbData.lodLimb.dLists[0] = (Gfx*)(res ? res->GetRawPointer() : nullptr);
+ } else {
+ limbData.lodLimb.dLists[0] = nullptr;
+ }
+
+ limbData.lodLimb.dLists[1] = nullptr;
+
+ limbData.lodLimb.child = skelLimb->childIndex;
+ limbData.lodLimb.sibling = skelLimb->siblingIndex;
+
+ // skelLimb->dList2Ptr = reader->Attribute("DisplayList2");
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/SkeletonLimbFactory.h b/mm/2s2h/resource/importer/SkeletonLimbFactory.h
new file mode 100644
index 000000000..8480a0126
--- /dev/null
+++ b/mm/2s2h/resource/importer/SkeletonLimbFactory.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "Resource.h"
+#include "ResourceFactory.h"
+
+namespace LUS {
+class SkeletonLimbFactory : public ResourceFactory
+{
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+ std::shared_ptr<IResource>
+ ReadResourceXML(std::shared_ptr<ResourceInitData> initData, tinyxml2::XMLElement *reader) override;
+};
+
+class SkeletonLimbFactoryV0 : public ResourceVersionFactory
+{
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+ void ParseFileXML(tinyxml2::XMLElement* reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
+
diff --git a/mm/2s2h/resource/importer/TextFactory.cpp b/mm/2s2h/resource/importer/TextFactory.cpp
new file mode 100644
index 000000000..7d7ffb4f3
--- /dev/null
+++ b/mm/2s2h/resource/importer/TextFactory.cpp
@@ -0,0 +1,93 @@
+#include "2s2h/resource/importer/TextFactory.h"
+#include "2s2h/resource/type/Text.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource>
+TextFactory::ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<Text>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<TextFactoryV0>();
+ break;
+ default:
+ // VERSION NOT SUPPORTED
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load Text with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+std::shared_ptr<IResource>
+TextFactory::ReadResourceXML(std::shared_ptr<ResourceInitData> initData, tinyxml2::XMLElement *reader) {
+ auto resource = std::make_shared<Text>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<TextFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load Text with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileXML(reader, resource);
+
+ return resource;
+}
+
+void LUS::TextFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource) {
+ std::shared_ptr<Text> text = std::static_pointer_cast<Text>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, text);
+
+ uint32_t msgCount = reader->ReadUInt32();
+ text->messages.reserve(msgCount);
+
+ for (uint32_t i = 0; i < msgCount; i++) {
+ MessageEntry entry;
+ entry.id = reader->ReadUInt16();
+ entry.textboxType = reader->ReadUByte();
+ entry.textboxYPos = reader->ReadUByte();
+ entry.msg = reader->ReadString();
+
+ text->messages.push_back(entry);
+ }
+}
+void TextFactoryV0::ParseFileXML(tinyxml2::XMLElement* reader, std::shared_ptr<IResource> resource) {
+ std::shared_ptr<Text> txt = std::static_pointer_cast<Text>(resource);
+
+ auto child = reader->FirstChildElement();
+
+ while (child != nullptr) {
+ std::string childName = child->Name();
+
+ if (childName == "TextEntry") {
+ MessageEntry entry;
+ entry.id = child->IntAttribute("ID");
+ entry.textboxType = child->IntAttribute("TextboxType");
+ entry.textboxYPos = child->IntAttribute("TextboxYPos");
+ entry.msg = child->Attribute("Message");
+ entry.msg += "\x2";
+
+ txt->messages.push_back(entry);
+ int bp = 0;
+ }
+
+ child = child->NextSiblingElement();
+ }
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/TextFactory.h b/mm/2s2h/resource/importer/TextFactory.h
new file mode 100644
index 000000000..7a6ae2841
--- /dev/null
+++ b/mm/2s2h/resource/importer/TextFactory.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "Resource.h"
+#include "ResourceFactory.h"
+
+namespace LUS {
+class TextFactory : public ResourceFactory
+{
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+ std::shared_ptr<IResource>
+ ReadResourceXML(std::shared_ptr<ResourceInitData> initData, tinyxml2::XMLElement *reader) override;
+};
+
+class TextFactoryV0 : public ResourceVersionFactory
+{
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+ void ParseFileXML(tinyxml2::XMLElement* reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
+
diff --git a/mm/2s2h/resource/importer/TextMMFactory.cpp b/mm/2s2h/resource/importer/TextMMFactory.cpp
new file mode 100644
index 000000000..3c2892001
--- /dev/null
+++ b/mm/2s2h/resource/importer/TextMMFactory.cpp
@@ -0,0 +1,100 @@
+#include "2s2h/resource/importer/TextMMFactory.h"
+#include "2s2h/resource/type/TextMM.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource>
+TextMMFactory::ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<TextMM>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<TextMMFactoryV0>();
+ break;
+ default:
+ // VERSION NOT SUPPORTED
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load Text with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+std::shared_ptr<IResource> TextMMFactory::ReadResourceXML(std::shared_ptr<ResourceInitData> initData,
+ tinyxml2::XMLElement* reader) {
+ auto resource = std::make_shared<TextMM>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<TextMMFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load Text with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileXML(reader, resource);
+
+ return resource;
+}
+
+void LUS::TextMMFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) {
+ std::shared_ptr<TextMM> text = std::static_pointer_cast<TextMM>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, text);
+
+ uint32_t msgCount = reader->ReadUInt32();
+ text->messages.reserve(msgCount);
+
+ for (uint32_t i = 0; i < msgCount; i++) {
+ MessageEntryMM entry;
+ entry.id = reader->ReadUInt16();
+ entry.textboxType = reader->ReadUByte();
+ entry.textboxYPos = reader->ReadUByte();
+ entry.icon = reader->ReadUByte();
+ entry.nextMessageID = reader->ReadUInt16();
+ entry.firstItemCost = reader->ReadUInt16();
+ entry.secondItemCost = reader->ReadUInt16();
+ entry.msg = reader->ReadString();
+
+ text->messages.push_back(entry);
+ }
+}
+void TextMMFactoryV0::ParseFileXML(tinyxml2::XMLElement* reader, std::shared_ptr<IResource> resource) {
+ std::shared_ptr<TextMM> txt = std::static_pointer_cast<TextMM>(resource);
+
+ auto child = reader->FirstChildElement();
+
+ while (child != nullptr) {
+ std::string childName = child->Name();
+
+ if (childName == "TextEntry") {
+ MessageEntryMM entry;
+ entry.id = child->IntAttribute("ID");
+ entry.textboxType = child->IntAttribute("TextboxType");
+ entry.textboxYPos = child->IntAttribute("TextboxYPos");
+
+ // BENTODO: MM Unique Fields
+
+
+ entry.msg = child->Attribute("Message");
+ entry.msg += "\x2";
+
+ txt->messages.push_back(entry);
+ int bp = 0;
+ }
+
+ child = child->NextSiblingElement();
+ }
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/TextMMFactory.h b/mm/2s2h/resource/importer/TextMMFactory.h
new file mode 100644
index 000000000..0322a3762
--- /dev/null
+++ b/mm/2s2h/resource/importer/TextMMFactory.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "Resource.h"
+#include "ResourceFactory.h"
+
+namespace LUS {
+class TextMMFactory : public ResourceFactory
+{
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+ std::shared_ptr<IResource>
+ ReadResourceXML(std::shared_ptr<ResourceInitData> initData, tinyxml2::XMLElement *reader) override;
+};
+
+class TextMMFactoryV0 : public ResourceVersionFactory
+{
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+ void ParseFileXML(tinyxml2::XMLElement* reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
+
diff --git a/mm/2s2h/resource/importer/TextureAnimationFactory.cpp b/mm/2s2h/resource/importer/TextureAnimationFactory.cpp
new file mode 100644
index 000000000..cc33beecf
--- /dev/null
+++ b/mm/2s2h/resource/importer/TextureAnimationFactory.cpp
@@ -0,0 +1,124 @@
+#include "2s2h/resource/importer/TextureAnimationFactory.h"
+#include "2s2h/resource/type/TextureAnimation.h"
+#include <libultraship/libultraship.h>
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+
+std::shared_ptr<IResource> TextureAnimationFactory::ReadResource(std::shared_ptr<ResourceInitData> initData,
+ std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<TextureAnimation>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<TextureAnimationFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load Texture Animation with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::TextureAnimationFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource) {
+ std::shared_ptr<TextureAnimation> tAnim = std::static_pointer_cast<TextureAnimation>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, tAnim);
+
+ size_t numEntries = reader->ReadUInt32();
+
+ for (size_t i = 0; i < numEntries; i++) {
+ AnimatedMaterial anim;
+ anim.segment = reader->ReadInt8();
+ anim.type = reader->ReadInt8();
+
+ switch ((TextureAnimationParamsType)anim.type) {
+ case TextureAnimationParamsType::SingleScroll: {
+ auto* e = new AnimatedMatTexScrollParams;
+ e->xStep = reader->ReadInt8();
+ e->yStep = reader->ReadInt8();
+ e->width = reader->ReadUByte();
+ e->height = reader->ReadUByte();
+ anim.params = e;
+ break;
+ }
+ case TextureAnimationParamsType::DualScroll: {
+ auto* e = new AnimatedMatTexScrollParams[2];
+ e[0].xStep = reader->ReadInt8();
+ e[0].yStep = reader->ReadInt8();
+ e[0].width = reader->ReadUByte();
+ e[0].height = reader->ReadUByte();
+ e[1].xStep = reader->ReadInt8();
+ e[1].yStep = reader->ReadInt8();
+ e[1].width = reader->ReadUByte();
+ e[1].height = reader->ReadUByte();
+ anim.params = e;
+ break;
+ }
+ case TextureAnimationParamsType::ColorChange:
+ case TextureAnimationParamsType::ColorChangeLERP:
+ case TextureAnimationParamsType::ColorChangeLagrange: {
+ auto* e = new AnimatedMatColorParams;
+ e->keyFrameLength = reader->ReadUInt16();
+ e->keyFrameCount = reader->ReadUInt16();
+
+ size_t frames = reader->ReadUInt32();
+
+ e->keyFrames = new uint16_t[frames];
+ for (size_t i = 0; i < frames; i++) {
+ e->keyFrames[i] = reader->ReadUInt16();
+ }
+ size_t primColorSize = reader->ReadUInt32();
+ e->primColors = new F3DPrimColor[primColorSize];
+
+ for (size_t i = 0; i < primColorSize; i++) {
+ e->primColors[i].r = reader->ReadUByte();
+ e->primColors[i].g = reader->ReadUByte();
+ e->primColors[i].b = reader->ReadUByte();
+ e->primColors[i].a = reader->ReadUByte();
+ e->primColors[i].lodFrac = reader->ReadUByte();
+ }
+
+ size_t envColorSize = reader->ReadUInt16();
+ e->envColors = new F3DEnvColor[envColorSize];
+ for (size_t i = 0; i < envColorSize; i++) {
+ e->envColors[i].r = reader->ReadUByte();
+ e->envColors[i].g = reader->ReadUByte();
+ e->envColors[i].b = reader->ReadUByte();
+ e->envColors[i].a = reader->ReadUByte();
+ }
+ anim.params = e;
+ break;
+ }
+ case TextureAnimationParamsType::TextureCycle: {
+ auto* e = new AnimatedMatTexCycleParams;
+
+ e->keyFrameLength = reader->ReadUInt16();
+ e->textureList = new void*[e->keyFrameLength];
+ e->textureIndexList = new uint8_t[e->keyFrameLength];
+
+ for (size_t i = 0; i < e->keyFrameLength; i++) {
+ e->textureList[i] = ResourceGetDataByName(reader->ReadString().c_str());
+ }
+ for (size_t i = 0; i < e->keyFrameLength; i++) {
+ e->textureIndexList[i] = reader->ReadUByte();
+ }
+ anim.params = e;
+ break;
+ }
+ case TextureAnimationParamsType::Empty: {
+ anim.params = nullptr;
+ break;
+ }
+ }
+ tAnim->anims.emplace_back(anim);
+ }
+}
+
+} // namespace LUS \ No newline at end of file
diff --git a/mm/2s2h/resource/importer/TextureAnimationFactory.h b/mm/2s2h/resource/importer/TextureAnimationFactory.h
new file mode 100644
index 000000000..1bc52e246
--- /dev/null
+++ b/mm/2s2h/resource/importer/TextureAnimationFactory.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "Resource.h"
+#include "ResourceFactory.h"
+
+namespace LUS {
+class TextureAnimationFactory : public ResourceFactory {
+ public:
+ std::shared_ptr<IResource> ReadResource(std::shared_ptr<ResourceInitData> initData,
+ std::shared_ptr<BinaryReader> reader) override;
+
+};
+
+class TextureAnimationFactoryV0 : public ResourceVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS \ No newline at end of file
diff --git a/mm/2s2h/resource/importer/scenecommand/EndMarkerFactory.cpp b/mm/2s2h/resource/importer/scenecommand/EndMarkerFactory.cpp
new file mode 100644
index 000000000..61dba4ca3
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/EndMarkerFactory.cpp
@@ -0,0 +1,38 @@
+#include "2s2h/resource/importer/scenecommand/EndMarkerFactory.h"
+#include "2s2h/resource/type/scenecommand/EndMarker.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource>
+EndMarkerFactory::ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<EndMarker>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<EndMarkerFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load EndMarker with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::EndMarkerFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource)
+{
+ std::shared_ptr<EndMarker> endMarker = std::static_pointer_cast<EndMarker>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, endMarker);
+
+ ReadCommandId(endMarker, reader);
+
+ // This has no data.
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/EndMarkerFactory.h b/mm/2s2h/resource/importer/scenecommand/EndMarkerFactory.h
new file mode 100644
index 000000000..a2580c077
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/EndMarkerFactory.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+
+namespace LUS {
+class EndMarkerFactory : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class EndMarkerFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SceneCommandFactory.cpp b/mm/2s2h/resource/importer/scenecommand/SceneCommandFactory.cpp
new file mode 100644
index 000000000..a2ed3c829
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SceneCommandFactory.cpp
@@ -0,0 +1,10 @@
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+#include "2s2h/resource/type/scenecommand/SceneCommand.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+void SceneCommandVersionFactory::ReadCommandId(std::shared_ptr<ISceneCommand> command, std::shared_ptr<BinaryReader> reader) {
+ command->cmdId = (SceneCommandID)reader->ReadInt32();
+}
+}
+ \ No newline at end of file
diff --git a/mm/2s2h/resource/importer/scenecommand/SceneCommandFactory.h b/mm/2s2h/resource/importer/scenecommand/SceneCommandFactory.h
new file mode 100644
index 000000000..b1449fad4
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SceneCommandFactory.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <memory>
+#include "Resource.h"
+#include "ResourceFactory.h"
+#include "2s2h/resource/type/scenecommand/SceneCommand.h"
+
+namespace LUS {
+class SceneCommandFactory : public ResourceFactory {};
+
+class SceneCommandVersionFactory : public ResourceVersionFactory {
+protected:
+ void ReadCommandId(std::shared_ptr<ISceneCommand> command, std::shared_ptr<BinaryReader> reader);
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetActorCutsceneListFactory.cpp b/mm/2s2h/resource/importer/scenecommand/SetActorCutsceneListFactory.cpp
new file mode 100644
index 000000000..4ad852fb1
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetActorCutsceneListFactory.cpp
@@ -0,0 +1,55 @@
+#include "2s2h/resource/importer/scenecommand/SetActorCutsceneListFactory.h"
+#include "2s2h/resource/type/scenecommand/SetActorCutsceneList.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource> SetActorCutsceneListFactory::ReadResource(std::shared_ptr<ResourceInitData> initData,
+ std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetActorCutsceneList>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SetActorCutsceneListFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load SetActorList with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+
+void SetActorCutsceneListFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource) {
+ std::shared_ptr<SetActorCutsceneList> setActorCsList = std::static_pointer_cast<SetActorCutsceneList>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, setActorCsList);
+
+ ReadCommandId(setActorCsList, reader);
+
+ setActorCsList->numEntries = reader->ReadUInt32();
+ setActorCsList->entries.reserve(setActorCsList->numEntries);
+
+ for (uint32_t i = 0; i < setActorCsList->numEntries; i++) {
+ CutsceneEntry e;
+ e.priority = reader->ReadInt16();
+ e.length = reader->ReadInt16();
+ e.csCamId = reader->ReadInt16();
+ e.scriptIndex = reader->ReadInt16();
+ e.additionalCsId = reader->ReadInt16();
+ e.endSfx = reader->ReadUByte();
+ e.customValue = reader->ReadUByte();
+ e.hudVisibility = reader->ReadInt16();
+ e.endCam = reader->ReadUByte();
+ e.letterboxSize = reader->ReadUByte();
+ setActorCsList->entries.emplace_back(e);
+ }
+
+}
+
+} // namespace LUS \ No newline at end of file
diff --git a/mm/2s2h/resource/importer/scenecommand/SetActorCutsceneListFactory.h b/mm/2s2h/resource/importer/scenecommand/SetActorCutsceneListFactory.h
new file mode 100644
index 000000000..23314c129
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetActorCutsceneListFactory.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+
+namespace LUS {
+class SetActorCutsceneListFactory : public SceneCommandFactory {
+ std::shared_ptr<IResource> ReadResource(std::shared_ptr<ResourceInitData> initData,
+ std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetActorCutsceneListFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+
+};
+
+}
diff --git a/mm/2s2h/resource/importer/scenecommand/SetActorListFactory.cpp b/mm/2s2h/resource/importer/scenecommand/SetActorListFactory.cpp
new file mode 100644
index 000000000..32aa3db5f
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetActorListFactory.cpp
@@ -0,0 +1,53 @@
+#include "2s2h/resource/importer/scenecommand/SetActorListFactory.h"
+#include "2s2h/resource/type/scenecommand/SetActorList.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource>
+SetActorListFactory::ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetActorList>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SetActorListFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr)
+ {
+ SPDLOG_ERROR("Failed to load SetActorList with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::SetActorListFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource) {
+ std::shared_ptr<SetActorList> setActorList = std::static_pointer_cast<SetActorList>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, setActorList);
+
+ ReadCommandId(setActorList, reader);
+
+ setActorList->numActors = reader->ReadUInt32();
+ setActorList->actorList.reserve(setActorList->numActors);
+ for (uint32_t i = 0; i < setActorList->numActors; i++) {
+ ActorEntry entry;
+
+ entry.id = reader->ReadUInt16();
+ entry.pos.x = reader->ReadInt16();
+ entry.pos.y = reader->ReadInt16();
+ entry.pos.z = reader->ReadInt16();
+ entry.rot.x = reader->ReadInt16();
+ entry.rot.y = reader->ReadInt16();
+ entry.rot.z = reader->ReadInt16();
+ entry.params = reader->ReadUInt16();
+
+ setActorList->actorList.push_back(entry);
+ }
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetActorListFactory.h b/mm/2s2h/resource/importer/scenecommand/SetActorListFactory.h
new file mode 100644
index 000000000..5958ac576
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetActorListFactory.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+
+namespace LUS {
+class SetActorListFactory : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetActorListFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetAlternateHeadersFactory.cpp b/mm/2s2h/resource/importer/scenecommand/SetAlternateHeadersFactory.cpp
new file mode 100644
index 000000000..d68d65c34
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetAlternateHeadersFactory.cpp
@@ -0,0 +1,49 @@
+#include "2s2h/resource/importer/scenecommand/SetAlternateHeadersFactory.h"
+#include "2s2h/resource/type/scenecommand/SetAlternateHeaders.h"
+#include "spdlog/spdlog.h"
+#include "libultraship/libultraship.h"
+
+namespace LUS {
+std::shared_ptr<IResource> SetAlternateHeadersFactory::ReadResource(std::shared_ptr<ResourceInitData> initData,
+ std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetAlternateHeaders>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SetAlternateHeadersFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr)
+ {
+ SPDLOG_ERROR("Failed to load SetAlternateHeaders with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::SetAlternateHeadersFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource)
+{
+ std::shared_ptr<SetAlternateHeaders> setAlternateHeaders = std::static_pointer_cast<SetAlternateHeaders>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, setAlternateHeaders);
+
+ ReadCommandId(setAlternateHeaders, reader);
+
+ setAlternateHeaders->numHeaders = reader->ReadUInt32();
+ setAlternateHeaders->headers.reserve(setAlternateHeaders->numHeaders);
+ for (uint32_t i = 0; i < setAlternateHeaders->numHeaders; i++) {
+ auto headerName = reader->ReadString();
+ if (!headerName.empty()) {
+ setAlternateHeaders->headers.push_back(std::static_pointer_cast<LUS::Scene>(LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(headerName.c_str())));
+ } else {
+ setAlternateHeaders->headers.push_back(nullptr);
+ }
+ }
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetAlternateHeadersFactory.h b/mm/2s2h/resource/importer/scenecommand/SetAlternateHeadersFactory.h
new file mode 100644
index 000000000..efa96f68f
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetAlternateHeadersFactory.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+
+namespace LUS {
+class SetAlternateHeadersFactory : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetAlternateHeadersFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetAnimatedMaterialListFactory.cpp b/mm/2s2h/resource/importer/scenecommand/SetAnimatedMaterialListFactory.cpp
new file mode 100644
index 000000000..e6e9865ec
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetAnimatedMaterialListFactory.cpp
@@ -0,0 +1,41 @@
+#include "2s2h/resource/importer/scenecommand/SetAnimatedMaterialListFactory.h"
+#include "2s2h/resource/type/scenecommand/SetAnimatedMaterialList.h"
+#include "2s2h/resource/type/TextureAnimation.h"
+#include "spdlog/spdlog.h"
+#include <libultraship/libultraship.h>
+
+namespace LUS {
+
+std::shared_ptr<IResource> LUS::SetAnimatedMaterialListFactory::ReadResource(std::shared_ptr<ResourceInitData> initData,
+ std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetAnimatedMaterialList>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SetAnimatedMaterialListFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load SetAnimatedMaterialList with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::SetAnimatedMaterialListFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource) {
+ std::shared_ptr<SetAnimatedMaterialList> setAnimatedMat =
+ std::static_pointer_cast<SetAnimatedMaterialList>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, setAnimatedMat);
+
+ ReadCommandId(setAnimatedMat, reader);
+ AnimatedMaterialData* res = (AnimatedMaterialData*)ResourceGetDataByName(reader->ReadString().c_str());
+ setAnimatedMat->mat = res;
+}
+
+} // namespace LUS \ No newline at end of file
diff --git a/mm/2s2h/resource/importer/scenecommand/SetAnimatedMaterialListFactory.h b/mm/2s2h/resource/importer/scenecommand/SetAnimatedMaterialListFactory.h
new file mode 100644
index 000000000..22e3fff69
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetAnimatedMaterialListFactory.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+
+namespace LUS {
+class SetAnimatedMaterialListFactory : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource> ReadResource(std::shared_ptr<ResourceInitData> initData,
+ std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetAnimatedMaterialListFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+
+}
diff --git a/mm/2s2h/resource/importer/scenecommand/SetCameraSettingsFactory.cpp b/mm/2s2h/resource/importer/scenecommand/SetCameraSettingsFactory.cpp
new file mode 100644
index 000000000..d36d412e9
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetCameraSettingsFactory.cpp
@@ -0,0 +1,39 @@
+#include "2s2h/resource/importer/scenecommand/SetCameraSettingsFactory.h"
+#include "2s2h/resource/type/scenecommand/SetCameraSettings.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource> SetCameraSettingsFactory::ReadResource(std::shared_ptr<ResourceInitData> initData,
+ std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetCameraSettings>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SetCameraSettingsFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load SetCameraSettings with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::SetCameraSettingsFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource)
+{
+ std::shared_ptr<SetCameraSettings> setCameraSettings = std::static_pointer_cast<SetCameraSettings>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, setCameraSettings);
+
+ ReadCommandId(setCameraSettings, reader);
+
+ setCameraSettings->settings.cameraMovement = reader->ReadInt8();
+ setCameraSettings->settings.worldMapArea = reader->ReadInt32();
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetCameraSettingsFactory.h b/mm/2s2h/resource/importer/scenecommand/SetCameraSettingsFactory.h
new file mode 100644
index 000000000..e17150807
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetCameraSettingsFactory.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+
+namespace LUS {
+class SetCameraSettingsFactory : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetCameraSettingsFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetCollisionHeaderFactory.cpp b/mm/2s2h/resource/importer/scenecommand/SetCollisionHeaderFactory.cpp
new file mode 100644
index 000000000..4badbb055
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetCollisionHeaderFactory.cpp
@@ -0,0 +1,39 @@
+#include "2s2h/resource/importer/scenecommand/SetCollisionHeaderFactory.h"
+#include "2s2h/resource/type/scenecommand/SetCollisionHeader.h"
+#include "libultraship/libultraship.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource> SetCollisionHeaderFactory::ReadResource(std::shared_ptr<ResourceInitData> initData,
+ std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetCollisionHeader>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SetCollisionHeaderFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load SetCollisionHeader with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::SetCollisionHeaderFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource) {
+ std::shared_ptr<SetCollisionHeader> setCollisionHeader = std::static_pointer_cast<SetCollisionHeader>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, setCollisionHeader);
+
+ ReadCommandId(setCollisionHeader, reader);
+
+ setCollisionHeader->fileName = reader->ReadString();
+ setCollisionHeader->collisionHeader = std::static_pointer_cast<CollisionHeader>(LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(setCollisionHeader->fileName.c_str()));
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetCollisionHeaderFactory.h b/mm/2s2h/resource/importer/scenecommand/SetCollisionHeaderFactory.h
new file mode 100644
index 000000000..76655015d
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetCollisionHeaderFactory.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+
+namespace LUS {
+class SetCollisionHeaderFactory : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetCollisionHeaderFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetCsCameraFactory.cpp b/mm/2s2h/resource/importer/scenecommand/SetCsCameraFactory.cpp
new file mode 100644
index 000000000..4e7c74067
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetCsCameraFactory.cpp
@@ -0,0 +1,55 @@
+#include "2s2h/resource/importer/scenecommand/SetCsCameraFactory.h"
+#include "2s2h/resource/type/scenecommand/SetCsCamera.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource>
+SetCsCameraFactory::ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetCsCamera>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SetCsCameraFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load SetCsCamera with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::SetCsCameraFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource) {
+ std::shared_ptr<SetCsCamera> setCsCamera = std::static_pointer_cast<SetCsCamera>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, setCsCamera);
+
+ ReadCommandId(setCsCamera, reader);
+
+ size_t camSize = reader->ReadUInt32();
+
+ for (size_t i = 0; i < camSize; i++) {
+ ActorCsCamInfoData data;
+ data.setting = reader->ReadUInt16();
+ data.count = reader->ReadUInt16();
+ if (data.count == 0) {
+ data.actorCsCamFuncData = nullptr;
+ continue;
+ }
+ data.actorCsCamFuncData = new z64Vec3s[data.count];
+ for (size_t j = 0; j < data.count; j++) {
+ data.actorCsCamFuncData->x = reader->ReadInt16();
+ data.actorCsCamFuncData->y = reader->ReadInt16();
+ data.actorCsCamFuncData->z = reader->ReadInt16();
+ }
+ setCsCamera->csCamera.emplace_back(data);
+ }
+
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetCsCameraFactory.h b/mm/2s2h/resource/importer/scenecommand/SetCsCameraFactory.h
new file mode 100644
index 000000000..4be361364
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetCsCameraFactory.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+
+namespace LUS {
+class SetCsCameraFactory : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetCsCameraFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetCutscenesFactory.cpp b/mm/2s2h/resource/importer/scenecommand/SetCutscenesFactory.cpp
new file mode 100644
index 000000000..ce429d8c1
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetCutscenesFactory.cpp
@@ -0,0 +1,81 @@
+#include "2s2h/resource/importer/scenecommand/SetCutscenesFactory.h"
+#include "2s2h/resource/type/scenecommand/SetCutscenes.h"
+#include <libultraship/libultraship.h>
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource>
+SetCutscenesFactory::ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetCutscenes>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SetCutscenesFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr)
+ {
+ SPDLOG_ERROR("Failed to load SetCutscenes with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::SetCutscenesFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource) {
+ std::shared_ptr<SetCutscenes> setCutscenes = std::static_pointer_cast<SetCutscenes>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, setCutscenes);
+
+ ReadCommandId(setCutscenes, reader);
+
+ setCutscenes->fileName = reader->ReadString();
+ setCutscenes->cutscene = std::static_pointer_cast<Cutscene>(LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(setCutscenes->fileName.c_str()));
+}
+
+std::shared_ptr<IResource> SetCutsceneFactoryMM::ReadResource(std::shared_ptr<ResourceInitData> initData,
+ std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetCutscenesMM>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SetCutscenesFactoryMMV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load SetCutscenes with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::SetCutscenesFactoryMMV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource) {
+ std::shared_ptr<SetCutscenesMM> setCutscenes = std::static_pointer_cast<SetCutscenesMM>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, setCutscenes);
+
+ ReadCommandId(setCutscenes, reader);
+
+ size_t numCs = reader->ReadUByte();
+
+ for (size_t i = 0; i < numCs; i++) {
+ CutsceneScriptEntry entry;
+ std::string path = reader->ReadString();
+ entry.exit = reader->ReadUInt16();
+ entry.entrance = reader->ReadUByte();
+ entry.flag = reader->ReadUByte();
+ entry.data = ResourceGetDataByName(path.c_str());
+ setCutscenes->entries.emplace_back(entry);
+ }
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetCutscenesFactory.h b/mm/2s2h/resource/importer/scenecommand/SetCutscenesFactory.h
new file mode 100644
index 000000000..905eab39a
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetCutscenesFactory.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+
+namespace LUS {
+class SetCutscenesFactory : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetCutscenesFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+
+class SetCutsceneFactoryMM : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource> ReadResource(std::shared_ptr<ResourceInitData> initData,
+ std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetCutscenesFactoryMMV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetEchoSettingsFactory.cpp b/mm/2s2h/resource/importer/scenecommand/SetEchoSettingsFactory.cpp
new file mode 100644
index 000000000..c9bdfef54
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetEchoSettingsFactory.cpp
@@ -0,0 +1,38 @@
+#include "2s2h/resource/importer/scenecommand/SetEchoSettingsFactory.h"
+#include "2s2h/resource/type/scenecommand/SetEchoSettings.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource>
+SetEchoSettingsFactory::ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetEchoSettings>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SetEchoSettingsFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load SetEchoSettings with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::SetEchoSettingsFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource)
+{
+ std::shared_ptr<SetEchoSettings> setEchoSettings = std::static_pointer_cast<SetEchoSettings>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, setEchoSettings);
+
+ ReadCommandId(setEchoSettings, reader);
+
+ setEchoSettings->settings.echo = reader->ReadInt8();
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetEchoSettingsFactory.h b/mm/2s2h/resource/importer/scenecommand/SetEchoSettingsFactory.h
new file mode 100644
index 000000000..20673aa57
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetEchoSettingsFactory.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+
+namespace LUS {
+class SetEchoSettingsFactory : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetEchoSettingsFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetEntranceListFactory.cpp b/mm/2s2h/resource/importer/scenecommand/SetEntranceListFactory.cpp
new file mode 100644
index 000000000..34a50248a
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetEntranceListFactory.cpp
@@ -0,0 +1,46 @@
+#include "2s2h/resource/importer/scenecommand/SetEntranceListFactory.h"
+#include "2s2h/resource/type/scenecommand/SetEntranceList.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource>
+SetEntranceListFactory::ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetEntranceList>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SetEntranceListFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load SetEntranceListList with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::SetEntranceListFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource) {
+ std::shared_ptr<SetEntranceList> setEntranceList = std::static_pointer_cast<SetEntranceList>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, setEntranceList);
+
+ ReadCommandId(setEntranceList, reader);
+
+ setEntranceList->numEntrances = reader->ReadUInt32();
+ setEntranceList->entrances.reserve(setEntranceList->numEntrances);
+ for (uint32_t i = 0; i < setEntranceList->numEntrances; i++) {
+ EntranceEntry entranceEntry;
+
+ entranceEntry.spawn = reader->ReadInt8();
+ entranceEntry.room = reader->ReadInt8();
+
+ setEntranceList->entrances.push_back(entranceEntry);
+ }
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetEntranceListFactory.h b/mm/2s2h/resource/importer/scenecommand/SetEntranceListFactory.h
new file mode 100644
index 000000000..2bb1186ba
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetEntranceListFactory.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+
+namespace LUS {
+class SetEntranceListFactory : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetEntranceListFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetExitListFactory.cpp b/mm/2s2h/resource/importer/scenecommand/SetExitListFactory.cpp
new file mode 100644
index 000000000..4c5ddcb88
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetExitListFactory.cpp
@@ -0,0 +1,41 @@
+#include "2s2h/resource/importer/scenecommand/SetExitListFactory.h"
+#include "2s2h/resource/type/scenecommand/SetExitList.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource>
+SetExitListFactory::ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetExitList>( initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SetExitListFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load SetExitList with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::SetExitListFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource) {
+ std::shared_ptr<SetExitList> setExitList = std::static_pointer_cast<SetExitList>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, setExitList);
+
+ ReadCommandId(setExitList, reader);
+
+ setExitList->numExits = reader->ReadUInt32();
+ setExitList->exits.reserve(setExitList->numExits);
+ for (uint32_t i = 0; i < setExitList->numExits; i++) {
+ setExitList->exits.push_back(reader->ReadUInt16());
+ }
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetExitListFactory.h b/mm/2s2h/resource/importer/scenecommand/SetExitListFactory.h
new file mode 100644
index 000000000..e04ab4ba0
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetExitListFactory.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+
+namespace LUS {
+class SetExitListFactory : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetExitListFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetLightListFactory.cpp b/mm/2s2h/resource/importer/scenecommand/SetLightListFactory.cpp
new file mode 100644
index 000000000..ab83635bf
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetLightListFactory.cpp
@@ -0,0 +1,58 @@
+#include "2s2h/resource/importer/scenecommand/SetLightListFactory.h"
+#include "2s2h/resource/type/scenecommand/SetLightList.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource>
+SetLightListFactory::ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetLightList>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SetLightListFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr)
+ {
+ SPDLOG_ERROR("Failed to load SetLightList with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::SetLightListFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource)
+{
+ std::shared_ptr<SetLightList> setLightList = std::static_pointer_cast<SetLightList>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, setLightList);
+
+ ReadCommandId(setLightList, reader);
+
+ setLightList->numLights = reader->ReadUInt32();
+ setLightList->lightList.reserve(setLightList->numLights);
+ for (uint32_t i = 0; i < setLightList->numLights; i++) {
+ LightInfo light;
+
+ light.type = reader->ReadUByte();
+
+ light.params.point.x = reader->ReadInt16();
+ light.params.point.y = reader->ReadInt16();
+ light.params.point.z = reader->ReadInt16();
+
+ light.params.point.color[0] = reader->ReadUByte(); // r
+ light.params.point.color[1] = reader->ReadUByte(); // g
+ light.params.point.color[2] = reader->ReadUByte(); // b
+
+ light.params.point.drawGlow = reader->ReadUByte();
+ light.params.point.radius = reader->ReadInt16();
+
+ setLightList->lightList.push_back(light);
+ }
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetLightListFactory.h b/mm/2s2h/resource/importer/scenecommand/SetLightListFactory.h
new file mode 100644
index 000000000..c099b82c5
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetLightListFactory.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+
+namespace LUS {
+class SetLightListFactory : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetLightListFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetLightingSettingsFactory.cpp b/mm/2s2h/resource/importer/scenecommand/SetLightingSettingsFactory.cpp
new file mode 100644
index 000000000..c13aeffec
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetLightingSettingsFactory.cpp
@@ -0,0 +1,70 @@
+#include "2s2h/resource/importer/scenecommand/SetLightingSettingsFactory.h"
+#include "2s2h/resource/type/scenecommand/SetLightingSettings.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource> SetLightingSettingsFactory::ReadResource(std::shared_ptr<ResourceInitData> initData,
+ std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetLightingSettings>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SetLightingSettingsFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load SetLightingSettings with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::SetLightingSettingsFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource)
+{
+ std::shared_ptr<SetLightingSettings> setLightingSettings = std::static_pointer_cast<SetLightingSettings>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, setLightingSettings);
+
+ ReadCommandId(setLightingSettings, reader);
+
+ uint32_t count = reader->ReadInt32();
+ setLightingSettings->settings.reserve(count);
+
+ for (uint32_t i = 0; i < count; i++) {
+ EnvLightSettings lightSettings;
+ lightSettings.ambientColor[0] = reader->ReadInt8();
+ lightSettings.ambientColor[1] = reader->ReadInt8();
+ lightSettings.ambientColor[2] = reader->ReadInt8();
+
+ lightSettings.light1Dir[0] = reader->ReadInt8();
+ lightSettings.light1Dir[1] = reader->ReadInt8();
+ lightSettings.light1Dir[2] = reader->ReadInt8();
+
+ lightSettings.light1Color[0] = reader->ReadInt8();
+ lightSettings.light1Color[1] = reader->ReadInt8();
+ lightSettings.light1Color[2] = reader->ReadInt8();
+
+ lightSettings.light2Dir[0] = reader->ReadInt8();
+ lightSettings.light2Dir[1] = reader->ReadInt8();
+ lightSettings.light2Dir[2] = reader->ReadInt8();
+
+ lightSettings.light2Color[0] = reader->ReadInt8();
+ lightSettings.light2Color[1] = reader->ReadInt8();
+ lightSettings.light2Color[2] = reader->ReadInt8();
+
+ lightSettings.fogColor[0] = reader->ReadInt8();
+ lightSettings.fogColor[1] = reader->ReadInt8();
+ lightSettings.fogColor[2] = reader->ReadInt8();
+
+ lightSettings.fogNear = reader->ReadInt16();
+ lightSettings.fogFar = reader->ReadUInt16();
+ setLightingSettings->settings.push_back(lightSettings);
+ }
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetLightingSettingsFactory.h b/mm/2s2h/resource/importer/scenecommand/SetLightingSettingsFactory.h
new file mode 100644
index 000000000..b25f85a97
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetLightingSettingsFactory.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+
+namespace LUS {
+class SetLightingSettingsFactory : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetLightingSettingsFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetMeshFactory.cpp b/mm/2s2h/resource/importer/scenecommand/SetMeshFactory.cpp
new file mode 100644
index 000000000..dcb3f4c10
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetMeshFactory.cpp
@@ -0,0 +1,169 @@
+#include "2s2h/resource/importer/scenecommand/SetMeshFactory.h"
+#include "2s2h/resource/type/scenecommand/SetMesh.h"
+#include "spdlog/spdlog.h"
+#include "libultraship/libultraship.h"
+
+namespace LUS {
+std::shared_ptr<IResource>
+SetMeshFactory::ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetMesh>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SetMeshFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr)
+ {
+ SPDLOG_ERROR("Failed to load SetMesh with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::SetMeshFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource)
+{
+ std::shared_ptr<SetMesh> setMesh = std::static_pointer_cast<SetMesh>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, setMesh);
+
+ ReadCommandId(setMesh, reader);
+
+ setMesh->data = reader->ReadInt8();
+
+ setMesh->meshHeader.base.type = reader->ReadInt8();
+ int32_t polyNum = 1;
+
+ if (setMesh->meshHeader.base.type != 1) {
+ polyNum = reader->ReadInt8();
+ if (setMesh->meshHeader.base.type == 0) {
+ setMesh->meshHeader.polygon0.num = polyNum;
+ } else if (setMesh->meshHeader.base.type == 2) {
+ setMesh->meshHeader.polygon2.num = polyNum;
+ } else {
+ SPDLOG_ERROR("Tried to load mesh in SetMesh scene header with type that doesn't exist: {}", setMesh->meshHeader.base.type);
+ }
+ }
+
+ if (setMesh->meshHeader.base.type == 2) {
+ setMesh->dlists2.reserve(polyNum);
+ } else {
+ setMesh->dlists.reserve(setMesh->meshHeader.polygon0.num);
+ }
+
+ for (int32_t i = 0; i < polyNum; i++) {
+ if (setMesh->meshHeader.base.type == 0) {
+ PolygonDlist dlist;
+
+ int32_t polyType = reader->ReadInt8(); // Unused
+ std::string meshOpa = reader->ReadString();
+ std::string meshXlu = reader->ReadString();
+
+ auto opaRes = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(meshOpa.c_str());
+ auto xluRes = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(meshXlu.c_str());
+ dlist.opa = meshOpa != "" ? (Gfx*)(opaRes ? opaRes->GetRawPointer() : nullptr) : 0;
+ dlist.xlu = meshXlu != "" ? (Gfx*)(xluRes ? xluRes->GetRawPointer() : nullptr) : 0;
+
+ setMesh->dlists.push_back(dlist);
+ } else if (setMesh->meshHeader.base.type == 1) {
+ PolygonDlist pType;
+
+ setMesh->meshHeader.polygon1.format = reader->ReadUByte();
+ std::string imgOpa = reader->ReadString();
+ std::string imgXlu = reader->ReadString();
+
+ auto opaRes = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(imgOpa.c_str());
+ auto xluRes = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(imgXlu.c_str());
+ pType.opa = imgOpa != "" ? (Gfx*)(opaRes ? opaRes->GetRawPointer() : nullptr) : 0;
+ pType.xlu = imgXlu != "" ? (Gfx*)(xluRes ? xluRes->GetRawPointer() : nullptr) : 0;
+
+ int32_t bgImageCount = reader->ReadUInt32();
+ setMesh->images.reserve(bgImageCount);
+
+ for (int32_t i = 0; i < bgImageCount; i++) {
+ BgImage image;
+ image.unk_00 = reader->ReadUInt16();
+ image.id = reader->ReadUByte();
+ std::string imagePath = "__OTR__" + reader->ReadString();
+ setMesh->imagePaths.push_back(imagePath);
+ image.source = (void*)setMesh->imagePaths.back().c_str();
+ image.unk_0C = reader->ReadUInt32();
+ image.tlut = reader->ReadUInt32();
+ image.width = reader->ReadUInt16();
+ image.height = reader->ReadUInt16();
+ image.fmt = reader->ReadUByte();
+ image.siz = reader->ReadUByte();
+ image.mode0 = reader->ReadUInt16();
+ image.tlutCount = reader->ReadUInt16();
+
+ if (setMesh->meshHeader.polygon1.format == 1) {
+ setMesh->meshHeader.polygon1.single.source = image.source;
+ setMesh->meshHeader.polygon1.single.unk_0C = image.unk_0C;
+ setMesh->meshHeader.polygon1.single.tlut = (void*)image.tlut; // OTRTODO: type of bgimage.tlut should be uintptr_t
+ setMesh->meshHeader.polygon1.single.width = image.width;
+ setMesh->meshHeader.polygon1.single.height = image.height;
+ setMesh->meshHeader.polygon1.single.fmt = image.fmt;
+ setMesh->meshHeader.polygon1.single.siz = image.siz;
+ setMesh->meshHeader.polygon1.single.mode0 = image.mode0;
+ setMesh->meshHeader.polygon1.single.tlutCount = image.tlutCount;
+ } else {
+ setMesh->images.push_back(image);
+ }
+ }
+
+ if (setMesh->meshHeader.polygon1.format != 1) {
+ setMesh->meshHeader.polygon1.multi.count = bgImageCount;
+ }
+
+ int32_t polyType = reader->ReadInt8(); // Unused??
+
+ std::string meshOpa = reader->ReadString();
+ std::string meshXlu = reader->ReadString();
+
+ opaRes = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(meshOpa.c_str());
+ xluRes = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(meshXlu.c_str());
+ pType.opa = meshOpa != "" ? (Gfx*)(opaRes ? opaRes->GetRawPointer() : nullptr) : 0;
+ pType.xlu = meshXlu != "" ? (Gfx*)(xluRes ? xluRes->GetRawPointer() : nullptr) : 0;
+
+ setMesh->dlists.push_back(pType);
+ } else if (setMesh->meshHeader.base.type == 2) {
+ PolygonDlist2 dlist;
+
+ int32_t polyType = reader->ReadInt8(); // Unused
+ dlist.pos.x = reader->ReadInt16();
+ dlist.pos.y = reader->ReadInt16();
+ dlist.pos.z = reader->ReadInt16();
+ dlist.unk_06 = reader->ReadInt16();
+
+ std::string meshOpa = reader->ReadString();
+ std::string meshXlu = reader->ReadString();
+
+ auto opaRes = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(meshOpa.c_str());
+ auto xluRes = LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(meshXlu.c_str());
+ dlist.opa = meshOpa != "" ? (Gfx*)(opaRes ? opaRes->GetRawPointer() : nullptr) : 0;
+ dlist.xlu = meshXlu != "" ? (Gfx*)(xluRes ? xluRes->GetRawPointer() : nullptr) : 0;
+
+ setMesh->dlists2.push_back(dlist);
+ } else {
+ SPDLOG_ERROR("Tried to load mesh in SetMesh scene header with type that doesn't exist: {}", setMesh->meshHeader.base.type);
+ }
+ }
+
+ if (setMesh->meshHeader.base.type == 2) {
+ setMesh->meshHeader.polygon2.start = setMesh->dlists2.data();
+ } else if (setMesh->meshHeader.base.type == 0) {
+ setMesh->meshHeader.polygon0.start = setMesh->dlists.data();
+ } else if (setMesh->meshHeader.base.type == 1) {
+ setMesh->meshHeader.polygon1.multi.list = setMesh->images.data();
+ setMesh->meshHeader.polygon1.dlist = (Gfx*)setMesh->dlists.data();
+ } else {
+ SPDLOG_ERROR("Tried to load mesh in SetMesh scene header with type that doesn't exist: {}", setMesh->meshHeader.base.type);
+ }
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetMeshFactory.h b/mm/2s2h/resource/importer/scenecommand/SetMeshFactory.h
new file mode 100644
index 000000000..3ae72fdbc
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetMeshFactory.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+
+namespace LUS {
+class SetMeshFactory : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetMeshFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetMinimapChestsFactory.cpp b/mm/2s2h/resource/importer/scenecommand/SetMinimapChestsFactory.cpp
new file mode 100644
index 000000000..dae215ab1
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetMinimapChestsFactory.cpp
@@ -0,0 +1,50 @@
+#include "2s2h/resource/importer/scenecommand/SetMinimapChestsFactory.h"
+#include "2s2h/resource/type/scenecommand/SetMinimapChests.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+
+std::shared_ptr<IResource> SetMinimapChestsFactory::ReadResource(std::shared_ptr<ResourceInitData> initData,
+ std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetMinimapChests>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SetMinimapChestsFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load SetMinimapChestsFactory with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void SetMinimapChestsFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource) {
+ std::shared_ptr<SetMinimapChests> chests = std::static_pointer_cast<SetMinimapChests>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, chests);
+
+ ReadCommandId(chests, reader);
+
+ size_t size = reader->ReadUInt32();
+
+ chests->chests.reserve(size);
+
+ for (size_t i = 0; i < size; i++) {
+ MinimapChestData d;
+ d.unk_0 = reader->ReadUInt16();
+ d.unk_2 = reader->ReadUInt16();
+ d.unk_4 = reader->ReadUInt16();
+ d.unk_6 = reader->ReadUInt16();
+ d.unk_8 = reader->ReadUInt16();
+ chests->chests.emplace_back(d);
+ }
+}
+
+} // namespace LUS \ No newline at end of file
diff --git a/mm/2s2h/resource/importer/scenecommand/SetMinimapChestsFactory.h b/mm/2s2h/resource/importer/scenecommand/SetMinimapChestsFactory.h
new file mode 100644
index 000000000..941405c40
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetMinimapChestsFactory.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+
+namespace LUS {
+class SetMinimapChestsFactory : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource> ReadResource(std::shared_ptr<ResourceInitData> initData,
+ std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetMinimapChestsFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+} \ No newline at end of file
diff --git a/mm/2s2h/resource/importer/scenecommand/SetMinimapListFactory.cpp b/mm/2s2h/resource/importer/scenecommand/SetMinimapListFactory.cpp
new file mode 100644
index 000000000..43eb4b496
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetMinimapListFactory.cpp
@@ -0,0 +1,50 @@
+#include "2s2h/resource/importer/scenecommand/SetMinimapListFactory.h"
+#include "2s2h/resource/type/scenecommand/SetMinimapList.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource> SetMinimapListFactory::ReadResource(std::shared_ptr<ResourceInitData> initData,
+ std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetMinimapList>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SetMinimapListFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load SetMinimapListFactory with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+void SetMinimapListFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource) {
+ std::shared_ptr<SetMinimapList> mapList = std::static_pointer_cast<SetMinimapList>(resource);
+
+ ResourceVersionFactory::ParseFileBinary(reader, mapList);
+
+ ReadCommandId(mapList, reader);
+
+ size_t size = reader->ReadUInt32();
+
+ mapList->list.scale = reader->ReadUInt16();
+ mapList->entries.reserve(size);
+
+ for (size_t i = 0; i < size; i++) {
+ MinimapEntryData data;
+ data.mapId = reader->ReadUInt16();
+ data.unk2 = reader->ReadUInt16();
+ data.unk4 = reader->ReadUInt16();
+ data.unk6 = reader->ReadUInt16();
+ data.unk8 = reader->ReadUInt16();
+ mapList->entries.emplace_back(data);
+ }
+ mapList->list.entry = mapList->entries.data();
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetMinimapListFactory.h b/mm/2s2h/resource/importer/scenecommand/SetMinimapListFactory.h
new file mode 100644
index 000000000..d8a5fd643
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetMinimapListFactory.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+
+namespace LUS {
+class SetMinimapListFactory : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource> ReadResource(std::shared_ptr<ResourceInitData> initData,
+ std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetMinimapListFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS \ No newline at end of file
diff --git a/mm/2s2h/resource/importer/scenecommand/SetObjectListFactory.cpp b/mm/2s2h/resource/importer/scenecommand/SetObjectListFactory.cpp
new file mode 100644
index 000000000..274407c00
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetObjectListFactory.cpp
@@ -0,0 +1,42 @@
+#include "2s2h/resource/importer/scenecommand/SetObjectListFactory.h"
+#include "2s2h/resource/type/scenecommand/SetObjectList.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource>
+SetObjectListFactory::ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetObjectList>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SetObjectListFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load SetObjectList with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::SetObjectListFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource)
+{
+ std::shared_ptr<SetObjectList> setObjectList = std::static_pointer_cast<SetObjectList>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, setObjectList);
+
+ ReadCommandId(setObjectList, reader);
+
+ setObjectList->numObjects = reader->ReadUInt32();
+ setObjectList->objects.reserve(setObjectList->numObjects);
+ for (uint32_t i = 0; i < setObjectList->numObjects; i++) {
+ setObjectList->objects.push_back(reader->ReadUInt16());
+ }
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetObjectListFactory.h b/mm/2s2h/resource/importer/scenecommand/SetObjectListFactory.h
new file mode 100644
index 000000000..b977576d3
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetObjectListFactory.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+
+namespace LUS {
+class SetObjectListFactory : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetObjectListFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetPathwaysFactory.cpp b/mm/2s2h/resource/importer/scenecommand/SetPathwaysFactory.cpp
new file mode 100644
index 000000000..a361a0915
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetPathwaysFactory.cpp
@@ -0,0 +1,82 @@
+#include "2s2h/resource/importer/scenecommand/SetPathwaysFactory.h"
+#include "2s2h/resource/type/scenecommand/SetPathways.h"
+#include "spdlog/spdlog.h"
+#include <libultraship/libultraship.h>
+
+namespace LUS {
+std::shared_ptr<IResource>
+SetPathwaysFactory::ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetPathways>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SetPathwaysFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load SetPathways with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::SetPathwaysFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource) {
+ std::shared_ptr<SetPathways> setPathways = std::static_pointer_cast<SetPathways>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, setPathways);
+
+ ReadCommandId(setPathways, reader);
+
+ setPathways->numPaths = reader->ReadUInt32();
+ setPathways->paths.reserve(setPathways->numPaths);
+ for (uint32_t i = 0; i < setPathways->numPaths; i++) {
+ std::string pathFileName = reader->ReadString();
+ auto path = std::static_pointer_cast<Path>(LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(pathFileName.c_str()));
+ setPathways->paths.push_back(path->GetPointer());
+ }
+}
+
+std::shared_ptr<IResource> SetPathwaysMMFactory::ReadResource(std::shared_ptr<ResourceInitData> initData,
+ std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetPathwaysMM>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SetPathwaysMMFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load SetPathwaysMM with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::SetPathwaysMMFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource) {
+ std::shared_ptr<SetPathwaysMM> setPathways = std::static_pointer_cast<SetPathwaysMM>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, setPathways);
+
+ ReadCommandId(setPathways, reader);
+
+ setPathways->numPaths = reader->ReadUInt32();
+ setPathways->paths.reserve(setPathways->numPaths);
+ for (uint32_t i = 0; i < setPathways->numPaths; i++) {
+ std::string pathFileName = reader->ReadString();
+ auto path = std::static_pointer_cast<PathMM>(
+ LUS::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(pathFileName.c_str()));
+ setPathways->paths.push_back(path->GetPointer());
+ }
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetPathwaysFactory.h b/mm/2s2h/resource/importer/scenecommand/SetPathwaysFactory.h
new file mode 100644
index 000000000..544ef02dc
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetPathwaysFactory.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+
+namespace LUS {
+class SetPathwaysFactory : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetPathwaysFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+
+class SetPathwaysMMFactory : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource> ReadResource(std::shared_ptr<ResourceInitData> initData,
+ std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetPathwaysMMFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetRoomBehaviorFactory.cpp b/mm/2s2h/resource/importer/scenecommand/SetRoomBehaviorFactory.cpp
new file mode 100644
index 000000000..6222eb3b6
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetRoomBehaviorFactory.cpp
@@ -0,0 +1,38 @@
+#include "2s2h/resource/importer/scenecommand/SetRoomBehaviorFactory.h"
+#include "2s2h/resource/type/scenecommand/SetRoomBehavior.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource>
+SetRoomBehaviorFactory::ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetRoomBehavior>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SetRoomBehaviorFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load SetRoomBehavior with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::SetRoomBehaviorFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource) {
+ std::shared_ptr<SetRoomBehavior> setRoomBehavior = std::static_pointer_cast<SetRoomBehavior>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, setRoomBehavior);
+
+ ReadCommandId(setRoomBehavior, reader);
+
+ setRoomBehavior->roomBehavior.gameplayFlags = reader->ReadInt8();
+ setRoomBehavior->roomBehavior.gameplayFlags2 = reader->ReadInt32();
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetRoomBehaviorFactory.h b/mm/2s2h/resource/importer/scenecommand/SetRoomBehaviorFactory.h
new file mode 100644
index 000000000..de812249d
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetRoomBehaviorFactory.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+
+namespace LUS {
+class SetRoomBehaviorFactory : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetRoomBehaviorFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetRoomListFactory.cpp b/mm/2s2h/resource/importer/scenecommand/SetRoomListFactory.cpp
new file mode 100644
index 000000000..ac4040afa
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetRoomListFactory.cpp
@@ -0,0 +1,50 @@
+#include "2s2h/resource/importer/scenecommand/SetRoomListFactory.h"
+#include "2s2h/resource/type/scenecommand/SetRoomList.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource>
+SetRoomListFactory::ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetRoomList>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SetRoomListFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr)
+ {
+ SPDLOG_ERROR("Failed to load SetRoomList with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::SetRoomListFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource) {
+ std::shared_ptr<SetRoomList> setRoomList = std::static_pointer_cast<SetRoomList>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, setRoomList);
+
+ ReadCommandId(setRoomList, reader);
+
+ setRoomList->numRooms = reader->ReadInt32();
+ setRoomList->rooms.reserve(setRoomList->numRooms);
+ for (uint32_t i = 0; i < setRoomList->numRooms; i++) {
+ RomFile room;
+
+ setRoomList->fileNames.push_back(reader->ReadString());
+
+ room.fileName = (char*)setRoomList->fileNames.back().c_str();
+ room.vromStart = reader->ReadInt32();
+ room.vromEnd = reader->ReadInt32();
+
+ setRoomList->rooms.push_back(room);
+ }
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetRoomListFactory.h b/mm/2s2h/resource/importer/scenecommand/SetRoomListFactory.h
new file mode 100644
index 000000000..eeb099aa8
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetRoomListFactory.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+
+namespace LUS {
+class SetRoomListFactory : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetRoomListFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetSkyboxModifierFactory.cpp b/mm/2s2h/resource/importer/scenecommand/SetSkyboxModifierFactory.cpp
new file mode 100644
index 000000000..c2670e0fd
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetSkyboxModifierFactory.cpp
@@ -0,0 +1,38 @@
+#include "2s2h/resource/importer/scenecommand/SetSkyboxModifierFactory.h"
+#include "2s2h/resource/type/scenecommand/SetSkyboxModifier.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource> SetSkyboxModifierFactory::ReadResource(std::shared_ptr<ResourceInitData> initData,
+ std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetSkyboxModifier>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SetSkyboxModifierFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load SetSkyboxModifier with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::SetSkyboxModifierFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource) {
+ std::shared_ptr<SetSkyboxModifier> setSkyboxModifier = std::static_pointer_cast<SetSkyboxModifier>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, setSkyboxModifier);
+
+ ReadCommandId(setSkyboxModifier, reader);
+
+ setSkyboxModifier->modifier.skyboxDisabled = reader->ReadInt8();
+ setSkyboxModifier->modifier.sunMoonDisabled = reader->ReadInt8();
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetSkyboxModifierFactory.h b/mm/2s2h/resource/importer/scenecommand/SetSkyboxModifierFactory.h
new file mode 100644
index 000000000..3880751fe
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetSkyboxModifierFactory.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+
+namespace LUS {
+class SetSkyboxModifierFactory : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetSkyboxModifierFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetSkyboxSettingsFactory.cpp b/mm/2s2h/resource/importer/scenecommand/SetSkyboxSettingsFactory.cpp
new file mode 100644
index 000000000..8ac3b91a4
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetSkyboxSettingsFactory.cpp
@@ -0,0 +1,40 @@
+#include "2s2h/resource/importer/scenecommand/SetSkyboxSettingsFactory.h"
+#include "2s2h/resource/type/scenecommand/SetSkyboxSettings.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource> SetSkyboxSettingsFactory::ReadResource(std::shared_ptr<ResourceInitData> initData,
+ std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetSkyboxSettings>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SetSkyboxSettingsFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load SetSkyboxSettings with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void SetSkyboxSettingsFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource) {
+ std::shared_ptr<SetSkyboxSettings> setSkyboxSettings = std::static_pointer_cast<SetSkyboxSettings>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, setSkyboxSettings);
+
+ ReadCommandId(setSkyboxSettings, reader);
+
+ setSkyboxSettings->settings.unk = reader->ReadInt8();
+ setSkyboxSettings->settings.skyboxId = reader->ReadInt8();
+ setSkyboxSettings->settings.weather = reader->ReadInt8();
+ setSkyboxSettings->settings.indoors = reader->ReadInt8();
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetSkyboxSettingsFactory.h b/mm/2s2h/resource/importer/scenecommand/SetSkyboxSettingsFactory.h
new file mode 100644
index 000000000..c75c86673
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetSkyboxSettingsFactory.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+
+namespace LUS {
+class SetSkyboxSettingsFactory : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetSkyboxSettingsFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetSoundSettingsFactory.cpp b/mm/2s2h/resource/importer/scenecommand/SetSoundSettingsFactory.cpp
new file mode 100644
index 000000000..98ee97f03
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetSoundSettingsFactory.cpp
@@ -0,0 +1,39 @@
+#include "2s2h/resource/importer/scenecommand/SetSoundSettingsFactory.h"
+#include "2s2h/resource/type/scenecommand/SetSoundSettings.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource> SetSoundSettingsFactory::ReadResource(std::shared_ptr<ResourceInitData> initData,
+ std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetSoundSettings>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SetSoundSettingsFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load SetSoundSettings with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::SetSoundSettingsFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource) {
+ std::shared_ptr<SetSoundSettings> setSoundSettings = std::static_pointer_cast<SetSoundSettings>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, setSoundSettings);
+
+ ReadCommandId(setSoundSettings, reader);
+
+ setSoundSettings->settings.reverb = reader->ReadInt8();
+ setSoundSettings->settings.natureAmbienceId = reader->ReadInt8();
+ setSoundSettings->settings.seqId = reader->ReadInt8();
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetSoundSettingsFactory.h b/mm/2s2h/resource/importer/scenecommand/SetSoundSettingsFactory.h
new file mode 100644
index 000000000..235c617a2
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetSoundSettingsFactory.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+
+namespace LUS {
+class SetSoundSettingsFactory : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetSoundSettingsFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetSpecialObjectsFactory.cpp b/mm/2s2h/resource/importer/scenecommand/SetSpecialObjectsFactory.cpp
new file mode 100644
index 000000000..35fc6614e
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetSpecialObjectsFactory.cpp
@@ -0,0 +1,38 @@
+#include "2s2h/resource/importer/scenecommand/SetSpecialObjectsFactory.h"
+#include "2s2h/resource/type/scenecommand/SetSpecialObjects.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource> SetSpecialObjectsFactory::ReadResource(std::shared_ptr<ResourceInitData> initData,
+ std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetSpecialObjects>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SetSpecialObjectsFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr){
+ SPDLOG_ERROR("Failed to load SetSpecialObjects with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::SetSpecialObjectsFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource) {
+ std::shared_ptr<SetSpecialObjects> setSpecialObjects = std::static_pointer_cast<SetSpecialObjects>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, setSpecialObjects);
+
+ ReadCommandId(setSpecialObjects, reader);
+
+ setSpecialObjects->specialObjects.elfMessage = reader->ReadInt8();
+ setSpecialObjects->specialObjects.globalObject = reader->ReadInt16();
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetSpecialObjectsFactory.h b/mm/2s2h/resource/importer/scenecommand/SetSpecialObjectsFactory.h
new file mode 100644
index 000000000..b62180a45
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetSpecialObjectsFactory.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+
+namespace LUS {
+class SetSpecialObjectsFactory : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetSpecialObjectsFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetStartPositionListFactory.cpp b/mm/2s2h/resource/importer/scenecommand/SetStartPositionListFactory.cpp
new file mode 100644
index 000000000..9d95a01ce
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetStartPositionListFactory.cpp
@@ -0,0 +1,55 @@
+#include "2s2h/resource/importer/scenecommand/SetStartPositionListFactory.h"
+#include "2s2h/resource/type/scenecommand/SetStartPositionList.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource> SetStartPositionListFactory::ReadResource(std::shared_ptr<ResourceInitData> initData,
+ std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetStartPositionList>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion)
+ {
+ case 0:
+ factory = std::make_shared<SetStartPositionListFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr)
+ {
+ SPDLOG_ERROR("Failed to load SetStartPositionList with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::SetStartPositionListFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource)
+{
+ std::shared_ptr<SetStartPositionList> setStartPositionList = std::static_pointer_cast<SetStartPositionList>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, setStartPositionList);
+
+ ReadCommandId(setStartPositionList, reader);
+
+ setStartPositionList->numStartPositions = reader->ReadUInt32();
+ setStartPositionList->startPositions.reserve(setStartPositionList->numStartPositions);
+ for (uint32_t i = 0; i < setStartPositionList->numStartPositions; i++) {
+ ActorEntry entry;
+
+ entry.id = reader->ReadUInt16();
+ entry.pos.x = reader->ReadInt16();
+ entry.pos.y = reader->ReadInt16();
+ entry.pos.z = reader->ReadInt16();
+ entry.rot.x = reader->ReadInt16();
+ entry.rot.y = reader->ReadInt16();
+ entry.rot.z = reader->ReadInt16();
+ entry.params = reader->ReadUInt16();
+
+ setStartPositionList->startPositions.push_back(entry);
+ }
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetStartPositionListFactory.h b/mm/2s2h/resource/importer/scenecommand/SetStartPositionListFactory.h
new file mode 100644
index 000000000..b029f3a1e
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetStartPositionListFactory.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+
+namespace LUS {
+class SetStartPositionListFactory : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetStartPositionListFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetTimeSettingsFactory.cpp b/mm/2s2h/resource/importer/scenecommand/SetTimeSettingsFactory.cpp
new file mode 100644
index 000000000..e3f2d77e5
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetTimeSettingsFactory.cpp
@@ -0,0 +1,39 @@
+#include "2s2h/resource/importer/scenecommand/SetTimeSettingsFactory.h"
+#include "2s2h/resource/type/scenecommand/SetTimeSettings.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource>
+SetTimeSettingsFactory::ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetTimeSettings>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SetTimeSettingsFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load SetTimeSettings with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::SetTimeSettingsFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource) {
+ std::shared_ptr<SetTimeSettings> setTimeSettings = std::static_pointer_cast<SetTimeSettings>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, setTimeSettings);
+
+ ReadCommandId(setTimeSettings, reader);
+
+ setTimeSettings->settings.hour = reader->ReadInt8();
+ setTimeSettings->settings.minute = reader->ReadInt8();
+ setTimeSettings->settings.timeIncrement = reader->ReadInt8();
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetTimeSettingsFactory.h b/mm/2s2h/resource/importer/scenecommand/SetTimeSettingsFactory.h
new file mode 100644
index 000000000..1482c2892
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetTimeSettingsFactory.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+
+namespace LUS {
+class SetTimeSettingsFactory : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetTimeSettingsFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetTransitionActorListFactory.cpp b/mm/2s2h/resource/importer/scenecommand/SetTransitionActorListFactory.cpp
new file mode 100644
index 000000000..611897fbe
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetTransitionActorListFactory.cpp
@@ -0,0 +1,54 @@
+#include "2s2h/resource/importer/scenecommand/SetTransitionActorListFactory.h"
+#include "2s2h/resource/type/scenecommand/SetTransitionActorList.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource> SetTransitionActorListFactory::ReadResource(std::shared_ptr<ResourceInitData> initData,
+ std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetTransitionActorList>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SetTransitionActorListFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load SetTransitionActorList with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::SetTransitionActorListFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource) {
+ std::shared_ptr<SetTransitionActorList> setTransitionActorList = std::static_pointer_cast<SetTransitionActorList>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, setTransitionActorList);
+
+ ReadCommandId(setTransitionActorList, reader);
+
+ setTransitionActorList->numTransitionActors = reader->ReadUInt32();
+ setTransitionActorList->transitionActorList.reserve(setTransitionActorList->numTransitionActors);
+ for (uint32_t i = 0; i < setTransitionActorList->numTransitionActors; i++) {
+ TransitionActorEntry entry;
+
+ entry.sides[0].room = reader->ReadUByte();
+ entry.sides[0].effects = reader->ReadUByte();
+ entry.sides[1].room = reader->ReadUByte();
+ entry.sides[1].effects = reader->ReadUByte();
+ entry.id = reader->ReadInt16();
+ entry.pos.x = reader->ReadInt16();
+ entry.pos.y = reader->ReadInt16();
+ entry.pos.z = reader->ReadInt16();
+ entry.rotY = reader->ReadInt16();
+ entry.params = reader->ReadUInt16();
+
+ setTransitionActorList->transitionActorList.push_back(entry);
+ }
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetTransitionActorListFactory.h b/mm/2s2h/resource/importer/scenecommand/SetTransitionActorListFactory.h
new file mode 100644
index 000000000..d300fdf3c
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetTransitionActorListFactory.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+
+namespace LUS {
+class SetTransitionActorListFactory : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetTransitionActorListFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetWindSettingsFactory.cpp b/mm/2s2h/resource/importer/scenecommand/SetWindSettingsFactory.cpp
new file mode 100644
index 000000000..74b3e78ab
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetWindSettingsFactory.cpp
@@ -0,0 +1,40 @@
+#include "2s2h/resource/importer/scenecommand/SetWindSettingsFactory.h"
+#include "2s2h/resource/type/scenecommand/SetWindSettings.h"
+#include "spdlog/spdlog.h"
+
+namespace LUS {
+std::shared_ptr<IResource>
+SetWindSettingsFactory::ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) {
+ auto resource = std::make_shared<SetWindSettings>(initData);
+ std::shared_ptr<ResourceVersionFactory> factory = nullptr;
+
+ switch (resource->GetInitData()->ResourceVersion) {
+ case 0:
+ factory = std::make_shared<SetWindSettingsFactoryV0>();
+ break;
+ }
+
+ if (factory == nullptr) {
+ SPDLOG_ERROR("Failed to load SetWindSettings with version {}", resource->GetInitData()->ResourceVersion);
+ return nullptr;
+ }
+
+ factory->ParseFileBinary(reader, resource);
+
+ return resource;
+}
+
+void LUS::SetWindSettingsFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReader> reader,
+ std::shared_ptr<IResource> resource) {
+ std::shared_ptr<SetWindSettings> setWind = std::static_pointer_cast<SetWindSettings>(resource);
+ ResourceVersionFactory::ParseFileBinary(reader, setWind);
+
+ ReadCommandId(setWind, reader);
+
+ setWind->settings.windWest = reader->ReadInt8();
+ setWind->settings.windVertical = reader->ReadInt8();
+ setWind->settings.windSouth = reader->ReadInt8();
+ setWind->settings.windSpeed = reader->ReadUByte();
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/importer/scenecommand/SetWindSettingsFactory.h b/mm/2s2h/resource/importer/scenecommand/SetWindSettingsFactory.h
new file mode 100644
index 000000000..b4fb4d615
--- /dev/null
+++ b/mm/2s2h/resource/importer/scenecommand/SetWindSettingsFactory.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "2s2h/resource/importer/scenecommand/SceneCommandFactory.h"
+
+namespace LUS {
+class SetWindSettingsFactory : public SceneCommandFactory {
+ public:
+ std::shared_ptr<IResource>
+ ReadResource(std::shared_ptr<ResourceInitData> initData, std::shared_ptr<BinaryReader> reader) override;
+};
+
+class SetWindSettingsFactoryV0 : public SceneCommandVersionFactory {
+ public:
+ void ParseFileBinary(std::shared_ptr<BinaryReader> reader, std::shared_ptr<IResource> resource) override;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/Animation.cpp b/mm/2s2h/resource/type/Animation.cpp
new file mode 100644
index 000000000..46327c732
--- /dev/null
+++ b/mm/2s2h/resource/type/Animation.cpp
@@ -0,0 +1,21 @@
+#include "Animation.h"
+
+namespace LUS {
+AnimationData* Animation::GetPointer() {
+ return &animationData;
+}
+
+size_t Animation::GetPointerSize() {
+ switch(type) {
+ case AnimationType::Normal:
+ return sizeof(animationData.animationHeader);
+ case AnimationType::Link:
+ return sizeof(animationData.linkAnimationHeader);
+ case AnimationType::Curve:
+ return sizeof(animationData.transformUpdateIndex);
+ case AnimationType::Legacy:
+ default:
+ return 0;
+ }
+}
+} // namespace LUS \ No newline at end of file
diff --git a/mm/2s2h/resource/type/Animation.h b/mm/2s2h/resource/type/Animation.h
new file mode 100644
index 000000000..3d6b810bf
--- /dev/null
+++ b/mm/2s2h/resource/type/Animation.h
@@ -0,0 +1,87 @@
+#pragma once
+
+#include "Resource.h"
+#include <libultraship/libultra/types.h>
+
+namespace LUS {
+ enum class AnimationType {
+ Normal = 0,
+ Link = 1,
+ Curve = 2,
+ Legacy = 3,
+ };
+
+ struct RotationIndex {
+ uint16_t x, y, z;
+
+ RotationIndex(uint16_t nX, uint16_t nY, uint16_t nZ) : x(nX), y(nY), z(nZ) {
+ }
+ };
+
+ typedef struct {
+ /* 0x0000 */ u16 unk_00; // appears to be flags
+ /* 0x0002 */ s16 unk_02;
+ /* 0x0004 */ s16 unk_04;
+ /* 0x0006 */ s16 unk_06;
+ /* 0x0008 */ f32 unk_08;
+ } TransformData; // size = 0xC
+
+ typedef struct {
+ /* 0x0000 */ u8* refIndex;
+ /* 0x0004 */ TransformData* transformData;
+ /* 0x0008 */ s16* copyValues;
+ /* 0x000C */ s16 unk_0C;
+ /* 0x000E */ s16 unk_0E;
+ } TransformUpdateIndex; // size = 0x10
+
+ typedef struct {
+ /* 0x00 */ s16 frameCount;
+ } AnimationHeaderCommon;
+
+ // Index into the frame data table.
+ typedef struct {
+ /* 0x00 */ u16 x;
+ /* 0x02 */ u16 y;
+ /* 0x04 */ u16 z;
+ } JointIndex; // size = 0x06
+
+ typedef struct {
+ /* 0x00 */ AnimationHeaderCommon common;
+ /* 0x04 */ s16* frameData; // "tbl"
+ /* 0x08 */ JointIndex* jointIndices; // "ref_tbl"
+ /* 0x0C */ u16 staticIndexMax;
+ } AnimationHeader; // size = 0x10
+
+ typedef struct {
+ /* 0x00 */ AnimationHeaderCommon common;
+ /* 0x04 */ void* segment;
+ } LinkAnimationHeader; // size = 0x8
+
+ union AnimationData {
+ AnimationHeader animationHeader;
+ LinkAnimationHeader linkAnimationHeader;
+ TransformUpdateIndex transformUpdateIndex;
+ };
+
+ class Animation : public Resource<AnimationData> {
+ public:
+ using Resource::Resource;
+
+ Animation() : Resource(std::shared_ptr<ResourceInitData>()) {}
+
+ AnimationData* GetPointer();
+ size_t GetPointerSize();
+
+ AnimationType type;
+ AnimationData animationData;
+
+ // NORMAL
+ std::vector<uint16_t> rotationValues;
+ std::vector<RotationIndex> rotationIndices;
+
+ // CURVE
+ std::vector<uint8_t> refIndexArr;
+ std::vector<TransformData> transformDataArr;
+ std::vector<int16_t> copyValuesArr;
+ };
+}; // namespace LUS \ No newline at end of file
diff --git a/mm/2s2h/resource/type/AudioSample.cpp b/mm/2s2h/resource/type/AudioSample.cpp
new file mode 100644
index 000000000..951aae6c1
--- /dev/null
+++ b/mm/2s2h/resource/type/AudioSample.cpp
@@ -0,0 +1,11 @@
+#include "AudioSample.h"
+
+namespace LUS {
+Sample* AudioSample::GetPointer() {
+ return &sample;
+}
+
+size_t AudioSample::GetPointerSize() {
+ return sizeof(Sample);
+}
+} // namespace LUS \ No newline at end of file
diff --git a/mm/2s2h/resource/type/AudioSample.h b/mm/2s2h/resource/type/AudioSample.h
new file mode 100644
index 000000000..eda8aa456
--- /dev/null
+++ b/mm/2s2h/resource/type/AudioSample.h
@@ -0,0 +1,61 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include "Resource.h"
+#include <libultraship/libultra/types.h>
+
+namespace LUS {
+ typedef struct {
+ /* 0x00 */ uintptr_t start;
+ /* 0x04 */ uintptr_t end;
+ /* 0x08 */ u32 count;
+ /* 0x0C */ char unk_0C[0x4];
+ /* 0x10 */ s16 state[16]; // only exists if count != 0. 8-byte aligned
+ } AdpcmLoop; // size = 0x30 (or 0x10)
+
+ typedef struct {
+ /* 0x00 */ s32 order;
+ /* 0x04 */ s32 npredictors;
+ /* 0x08 */ s16* book; // size 8 * order * npredictors. 8-byte aligned
+ } AdpcmBook; // s
+
+ typedef struct {
+ union {
+ struct {
+ /* 0x00 */ u32 codec : 4;
+ /* 0x00 */ u32 medium : 2;
+ /* 0x00 */ u32 unk_bit26 : 1;
+ /* 0x00 */ u32 unk_bit25 : 1; // this has been named isRelocated in zret
+ /* 0x01 */ u32 size : 24;
+ };
+ u32 asU32;
+ };
+
+ /* 0x04 */ u8* sampleAddr;
+ /* 0x08 */ AdpcmLoop* loop;
+ /* 0x0C */ AdpcmBook* book;
+ u32 sampleRateMagicValue; // For wav samples only...
+ s32 sampleRate; // For wav samples only...
+ } Sample; // size = 0x10
+
+ class AudioSample : public Resource<Sample> {
+ public:
+ using Resource::Resource;
+
+ AudioSample() : Resource(std::shared_ptr<ResourceInitData>()) {}
+
+ Sample* GetPointer();
+ size_t GetPointerSize();
+
+ Sample sample;
+ std::vector<uint8_t> audioSampleData;
+
+ AdpcmLoop loop;
+ uint32_t loopStateCount;
+
+ AdpcmBook book;
+ uint32_t bookDataCount;
+ std::vector<int16_t> bookData;
+ };
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/AudioSequence.cpp b/mm/2s2h/resource/type/AudioSequence.cpp
new file mode 100644
index 000000000..c09514a67
--- /dev/null
+++ b/mm/2s2h/resource/type/AudioSequence.cpp
@@ -0,0 +1,12 @@
+#include "AudioSequence.h"
+
+namespace LUS {
+
+Sequence* AudioSequence::GetPointer() {
+ return &sequence;
+}
+
+size_t AudioSequence::GetPointerSize() {
+ return sizeof(Sequence);
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/AudioSequence.h b/mm/2s2h/resource/type/AudioSequence.h
new file mode 100644
index 000000000..2b2bb8be9
--- /dev/null
+++ b/mm/2s2h/resource/type/AudioSequence.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include "Resource.h"
+#include <libultraship/libultra/types.h>
+
+namespace LUS {
+
+typedef struct {
+ char* seqData;
+ int32_t seqDataSize;
+ uint16_t seqNumber;
+ uint8_t medium;
+ uint8_t cachePolicy;
+ int32_t numFonts;
+ uint8_t fonts[16];
+} Sequence;
+
+class AudioSequence : public Resource<Sequence> {
+public:
+ using Resource::Resource;
+
+ AudioSequence() : Resource(std::shared_ptr<ResourceInitData>()) {}
+
+ Sequence* GetPointer();
+ size_t GetPointerSize();
+
+ Sequence sequence;
+ std::vector<char> sequenceData;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/AudioSoundFont.cpp b/mm/2s2h/resource/type/AudioSoundFont.cpp
new file mode 100644
index 000000000..43ac40abf
--- /dev/null
+++ b/mm/2s2h/resource/type/AudioSoundFont.cpp
@@ -0,0 +1,11 @@
+#include "AudioSoundFont.h"
+
+namespace LUS {
+SoundFont* AudioSoundFont::GetPointer() {
+ return &soundFont;
+}
+
+size_t AudioSoundFont::GetPointerSize() {
+ return sizeof(SoundFont);
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/AudioSoundFont.h b/mm/2s2h/resource/type/AudioSoundFont.h
new file mode 100644
index 000000000..99b18b401
--- /dev/null
+++ b/mm/2s2h/resource/type/AudioSoundFont.h
@@ -0,0 +1,84 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include "Resource.h"
+#include "2s2h/resource/type/AudioSample.h"
+#include <libultraship/libultra/types.h>
+
+namespace LUS {
+
+typedef struct {
+ /* 0x0 */ s16 delay;
+ /* 0x2 */ s16 arg;
+} AdsrEnvelope; // size = 0x4
+
+typedef struct {
+ /* 0x00 */ Sample* sample;
+ /* 0x04 */ union {
+ u32 tuningAsU32;
+ f32 tuning;// frequency scale factor
+ };
+} SoundFontSound; // size = 0x8
+
+typedef struct {
+ /* 0x00 */ u8 loaded;
+ /* 0x01 */ u8 normalRangeLo;
+ /* 0x02 */ u8 normalRangeHi;
+ /* 0x03 */ u8 releaseRate;
+ /* 0x04 */ AdsrEnvelope* envelope;
+ /* 0x08 */ SoundFontSound lowNotesSound;
+ /* 0x10 */ SoundFontSound normalNotesSound;
+ /* 0x18 */ SoundFontSound highNotesSound;
+} Instrument; // size = 0x20
+
+typedef struct {
+ /* 0x00 */ u8 releaseRate;
+ /* 0x01 */ u8 pan;
+ /* 0x02 */ u8 loaded;
+ /* 0x04 */ SoundFontSound sound;
+ /* 0x14 */ AdsrEnvelope* envelope;
+} Drum; // size = 0x14
+
+typedef struct {
+ /* 0x00 */ u8 numInstruments;
+ /* 0x01 */ u8 numDrums;
+ /* 0x02 */ u8 sampleBankId1;
+ /* 0x03 */ u8 sampleBankId2;
+ /* 0x04 */ u16 numSfx;
+ /* 0x08 */ Instrument** instruments;
+ /* 0x0C */ Drum** drums;
+ /* 0x10 */ SoundFontSound* soundEffects;
+ s32 fntIndex;
+} SoundFont; // size = 0x14
+
+class AudioSoundFont : public Resource<SoundFont> {
+public:
+ using Resource::Resource;
+
+ AudioSoundFont() : Resource(std::shared_ptr<ResourceInitData>()) {}
+
+ SoundFont* GetPointer();
+ size_t GetPointerSize();
+
+ int8_t medium;
+ int8_t cachePolicy;
+ uint16_t data1;
+ uint16_t data2;
+ uint16_t data3;
+
+ std::vector<Drum> drums;
+ std::vector<Drum*> drumAddresses;
+ std::vector<uint32_t> drumEnvelopeCounts;
+ std::vector<std::vector<AdsrEnvelope>> drumEnvelopeArrays;
+
+ std::vector<Instrument> instruments;
+ std::vector<Instrument*> instrumentAddresses;
+ std::vector<uint32_t> instrumentEnvelopeCounts;
+ std::vector<std::vector<AdsrEnvelope>> instrumentEnvelopeArrays;
+
+ std::vector<SoundFontSound> soundEffects;
+
+ SoundFont soundFont;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/Background.cpp b/mm/2s2h/resource/type/Background.cpp
new file mode 100644
index 000000000..bc1047b5c
--- /dev/null
+++ b/mm/2s2h/resource/type/Background.cpp
@@ -0,0 +1,11 @@
+#include "Background.h"
+
+namespace LUS {
+uint8_t* Background::GetPointer() {
+ return Data.data();
+}
+
+size_t Background::GetPointerSize() {
+ return Data.size() * sizeof(uint8_t);
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/Background.h b/mm/2s2h/resource/type/Background.h
new file mode 100644
index 000000000..7f22658e0
--- /dev/null
+++ b/mm/2s2h/resource/type/Background.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "resource/Resource.h"
+
+namespace LUS {
+class Background : public Resource<uint8_t> {
+ public:
+ using Resource::Resource;
+
+ Background() : Resource(std::shared_ptr<ResourceInitData>()) {}
+
+ uint8_t* GetPointer();
+ size_t GetPointerSize();
+
+ std::vector<uint8_t> Data;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/CollisionHeader.cpp b/mm/2s2h/resource/type/CollisionHeader.cpp
new file mode 100644
index 000000000..5625d2b59
--- /dev/null
+++ b/mm/2s2h/resource/type/CollisionHeader.cpp
@@ -0,0 +1,11 @@
+#include "CollisionHeader.h"
+
+namespace LUS {
+CollisionHeaderData* CollisionHeader::GetPointer() {
+ return &collisionHeaderData;
+}
+
+size_t CollisionHeader::GetPointerSize() {
+ return sizeof(collisionHeaderData);
+}
+} // namespace LUS \ No newline at end of file
diff --git a/mm/2s2h/resource/type/CollisionHeader.h b/mm/2s2h/resource/type/CollisionHeader.h
new file mode 100644
index 000000000..e401aa7f5
--- /dev/null
+++ b/mm/2s2h/resource/type/CollisionHeader.h
@@ -0,0 +1,98 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include "Resource.h"
+#include <libultraship/libultra.h>
+#include "z64math.h"
+
+namespace LUS {
+
+typedef struct {
+ /* 0x00 */ u16 type;
+ union {
+ u16 vtxData[3];
+ struct {
+ /* 0x02 */ u16 flags_vIA; // 0xE000 is poly exclusion flags (xpFlags), 0x1FFF is vtxId
+ /* 0x04 */ u16 flags_vIB; // 0xE000 is flags, 0x1FFF is vtxId
+ // 0x2000 = poly IsConveyor surface
+ /* 0x06 */ u16 vIC;
+ };
+ };
+ /* 0x08 */ Vec3s normal; // Unit normal vector
+ // Value ranges from -0x7FFF to 0x7FFF, representing -1.0 to 1.0; 0x8000 is invalid
+
+ /* 0x0E */ s16 dist; // Plane distance from origin along the normal
+} CollisionPoly; // size = 0x10
+
+typedef struct {
+ /* 0x00 */ s16 xMin;
+ /* 0x02 */ s16 ySurface;
+ /* 0x04 */ s16 zMin;
+ /* 0x06 */ s16 xLength;
+ /* 0x08 */ s16 zLength;
+ /* 0x0C */ u32 properties;
+
+ // 0x0008_0000 = ?
+ // 0x0007_E000 = Room Index, 0x3F = all rooms
+ // 0x0000_1F00 = Lighting Settings Index
+ // 0x0000_00FF = CamData index
+} WaterBox; // size = 0x10
+
+typedef struct {
+ /* 0x00 */ u16 cameraSType;
+ /* 0x02 */ s16 numCameras;
+ /* 0x04 */ Vec3s* camPosData;
+} CamData;
+
+typedef struct {
+ u32 data[2];
+
+ // Type 1
+ // 0x0800_0000 = wall damage
+} SurfaceType;
+
+
+typedef struct {
+ /* 0x00 */ Vec3s minBounds; // minimum coordinates of poly bounding box
+ /* 0x06 */ Vec3s maxBounds; // maximum coordinates of poly bounding box
+ /* 0x0C */ u16 numVertices;
+ /* 0x10 */ Vec3s* vtxList;
+ /* 0x14 */ u16 numPolygons;
+ /* 0x18 */ CollisionPoly* polyList;
+ /* 0x1C */ SurfaceType* surfaceTypeList;
+ /* 0x20 */ CamData* cameraDataList;
+ /* 0x24 */ u16 numWaterBoxes;
+ /* 0x28 */ WaterBox* waterBoxes;
+ size_t cameraDataListLen; // OTRTODO: Added to allow for bounds checking the cameraDataList.
+} CollisionHeaderData; // original name: BGDataInfo
+
+class CollisionHeader : public Resource<CollisionHeaderData> {
+public:
+ using Resource::Resource;
+
+ CollisionHeader() : Resource(std::shared_ptr<ResourceInitData>()) {}
+
+ CollisionHeaderData* GetPointer();
+ size_t GetPointerSize();
+
+ CollisionHeaderData collisionHeaderData;
+
+ std::vector<Vec3s> vertices;
+
+ std::vector<CollisionPoly> polygons;
+
+ uint32_t surfaceTypesCount;
+ std::vector<SurfaceType> surfaceTypes;
+
+ uint32_t camDataCount;
+ std::vector<CamData> camData;
+ std::vector<int32_t> camPosDataIndices;
+
+ int32_t camPosCount;
+ Vec3s camPosDataZero;
+ std::vector<Vec3s> camPosData;
+
+ std::vector<WaterBox> waterBoxes;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/Cutscene.cpp b/mm/2s2h/resource/type/Cutscene.cpp
new file mode 100644
index 000000000..c7e98062a
--- /dev/null
+++ b/mm/2s2h/resource/type/Cutscene.cpp
@@ -0,0 +1,12 @@
+#include "Cutscene.h"
+#include <libultraship/libultra/gbi.h>
+
+namespace LUS {
+uint32_t* Cutscene::GetPointer() {
+ return commands.data();
+}
+
+size_t Cutscene::GetPointerSize() {
+ return commands.size() * sizeof(uint32_t);
+}
+} // namespace LUS \ No newline at end of file
diff --git a/mm/2s2h/resource/type/Cutscene.h b/mm/2s2h/resource/type/Cutscene.h
new file mode 100644
index 000000000..5bd268e61
--- /dev/null
+++ b/mm/2s2h/resource/type/Cutscene.h
@@ -0,0 +1,71 @@
+#pragma once
+
+#include <vector>
+#include <string>
+#include "Resource.h"
+#include "Vec2f.h"
+#include "Vec3f.h"
+#include "Color3b.h"
+
+namespace LUS {
+
+enum class CutsceneCommands {
+ Cmd00 = 0x0000,
+ SetCameraPos = 0x0001,
+ SetCameraFocus = 0x0002,
+ SpecialAction = 0x0003,
+ SetLighting = 0x0004,
+ SetCameraPosLink = 0x0005,
+ SetCameraFocusLink = 0x0006,
+ Cmd07 = 0x0007,
+ Cmd08 = 0x0008,
+ Cmd09 = 0x0009,
+ Unknown = 0x001A,
+ Textbox = 0x0013,
+ SetActorAction0 = 0x000A,
+ SetActorAction1 = 0x000F,
+ SetActorAction2 = 0x000E,
+ SetActorAction3 = 0x0019,
+ SetActorAction4 = 0x001D,
+ SetActorAction5 = 0x001E,
+ SetActorAction6 = 0x002C,
+ SetActorAction7 = 0x001F,
+ SetActorAction8 = 0x0031,
+ SetActorAction9 = 0x003E,
+ SetActorAction10 = 0x008F,
+ SetSceneTransFX = 0x002D,
+ Nop = 0x000B,
+ PlayBGM = 0x0056,
+ StopBGM = 0x0057,
+ FadeBGM = 0x007C,
+ SetTime = 0x008C,
+ Terminator = 0x03E8,
+ End = 0xFFFF,
+ Error = 0xFEAF,
+};
+
+class Cutscene : public Resource<uint32_t> {
+ public:
+ using Resource::Resource;
+
+ Cutscene() : Resource(std::shared_ptr<ResourceInitData>()) {}
+
+ uint32_t* GetPointer();
+ size_t GetPointerSize();
+
+ uint32_t numCommands;
+ uint32_t endFrame;
+ std::vector<uint32_t> commands;
+};
+} // namespace LUS
+
+
+/////////////
+
+// class CutsceneCommand {
+// public:
+// uint32_t commandID;
+// uint32_t commandIndex;
+
+// CutsceneCommand(){};
+// };
diff --git a/mm/2s2h/resource/type/Path.cpp b/mm/2s2h/resource/type/Path.cpp
new file mode 100644
index 000000000..8ca2ac78d
--- /dev/null
+++ b/mm/2s2h/resource/type/Path.cpp
@@ -0,0 +1,19 @@
+#include "Path.h"
+
+namespace LUS {
+PathData* Path::GetPointer() {
+ return pathData.data();
+}
+
+size_t Path::GetPointerSize() {
+ return pathData.size() * sizeof(PathData);
+}
+
+PathDataMM* PathMM::GetPointer() {
+ return pathData.data();
+}
+size_t PathMM::GetPointerSize() {
+ return pathData.size() * sizeof(PathData);
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/Path.h b/mm/2s2h/resource/type/Path.h
new file mode 100644
index 000000000..045764cc8
--- /dev/null
+++ b/mm/2s2h/resource/type/Path.h
@@ -0,0 +1,52 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include "Resource.h"
+#include <libultraship/libultra/types.h>
+#include "z64math.h"
+
+namespace LUS {
+
+typedef struct {
+ /* 0x00 */ u8 count; // number of points in the path
+ /* 0x04 */ Vec3s* points; // Segment Address to the array of points
+} PathData; // size = 0x8
+
+typedef struct {
+ /* 0x0 */ u8 count; // Number of points in the path
+ /* 0x1 */ u8 additionalPathIndex;
+ /* 0x2 */ s16 customValue; // Path specific to help distinguish different paths
+ /* 0x4 */ Vec3s* points; // Segment Address to the array of points
+} PathDataMM; // size = 0x8
+
+class Path : public Resource<PathData> {
+public:
+ using Resource::Resource;
+
+ Path() : Resource(std::shared_ptr<ResourceInitData>()) {}
+
+ PathData* GetPointer();
+ size_t GetPointerSize();
+
+ uint32_t numPaths;
+ std::vector<PathData> pathData;
+ std::vector<std::vector<Vec3s>> paths;
+};
+
+class PathMM : public Resource<PathDataMM> {
+ public:
+ using Resource::Resource;
+
+ PathMM() : Resource(std::shared_ptr<ResourceInitData>()) {
+ }
+
+ PathDataMM* GetPointer();
+ size_t GetPointerSize();
+
+ uint32_t numPaths;
+ std::vector<PathDataMM> pathData;
+ std::vector<std::vector<Vec3s>> paths;
+};
+
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/PlayerAnimation.cpp b/mm/2s2h/resource/type/PlayerAnimation.cpp
new file mode 100644
index 000000000..55925f49c
--- /dev/null
+++ b/mm/2s2h/resource/type/PlayerAnimation.cpp
@@ -0,0 +1,12 @@
+#include "PlayerAnimation.h"
+#include <libultraship/libultra/gbi.h>
+
+namespace LUS {
+int16_t* PlayerAnimation::GetPointer() {
+ return limbRotData.data();
+}
+
+size_t PlayerAnimation::GetPointerSize() {
+ return limbRotData.size() * sizeof(int16_t);
+}
+} // namespace LUS \ No newline at end of file
diff --git a/mm/2s2h/resource/type/PlayerAnimation.h b/mm/2s2h/resource/type/PlayerAnimation.h
new file mode 100644
index 000000000..4eb51b139
--- /dev/null
+++ b/mm/2s2h/resource/type/PlayerAnimation.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <vector>
+#include <string>
+#include "Resource.h"
+#include "Vec2f.h"
+#include "Vec3f.h"
+#include "Color3b.h"
+
+namespace LUS {
+
+class PlayerAnimation : public Resource<int16_t> {
+ public:
+ using Resource::Resource;
+
+ PlayerAnimation() : Resource(std::shared_ptr<ResourceInitData>()) {}
+
+ int16_t* GetPointer();
+ size_t GetPointerSize();
+
+ std::vector<int16_t> limbRotData;
+};
+} // namespace LUS \ No newline at end of file
diff --git a/mm/2s2h/resource/type/Scene.cpp b/mm/2s2h/resource/type/Scene.cpp
new file mode 100644
index 000000000..bfe219e54
--- /dev/null
+++ b/mm/2s2h/resource/type/Scene.cpp
@@ -0,0 +1,12 @@
+#include "Scene.h"
+
+namespace LUS {
+void* Scene::GetPointer() {
+ // Scene is a special type that requries C++ processing. As such, we return nothing.
+ return nullptr;
+}
+
+size_t Scene::GetPointerSize() {
+ return 0;
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/Scene.h b/mm/2s2h/resource/type/Scene.h
new file mode 100644
index 000000000..f726da359
--- /dev/null
+++ b/mm/2s2h/resource/type/Scene.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <memory>
+#include "Resource.h"
+#include "scenecommand/SceneCommand.h"
+#include <libultraship/libultra/types.h>
+
+namespace LUS {
+
+class Scene : public Resource<void> {
+public:
+ using Resource::Resource;
+
+ Scene() : Resource(std::shared_ptr<ResourceInitData>()) {}
+
+ void* GetPointer();
+ size_t GetPointerSize();
+
+ std::vector<std::shared_ptr<ISceneCommand>> commands;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/Skeleton.cpp b/mm/2s2h/resource/type/Skeleton.cpp
new file mode 100644
index 000000000..7b9003bab
--- /dev/null
+++ b/mm/2s2h/resource/type/Skeleton.cpp
@@ -0,0 +1,82 @@
+#include "resource/ResourceManager.h"
+#include "Skeleton.h"
+#include "2s2h/BenPort.h"
+#include "libultraship/libultraship.h"
+
+namespace LUS {
+SkeletonData* Skeleton::GetPointer() {
+ return &skeletonData;
+}
+
+size_t Skeleton::GetPointerSize() {
+ switch(type) {
+ case SkeletonType::Normal:
+ return sizeof(skeletonData.skeletonHeader);
+ case SkeletonType::Flex:
+ return sizeof(skeletonData.flexSkeletonHeader);
+ case SkeletonType::Curve:
+ return sizeof(skeletonData.skelCurveLimbList);
+ default:
+ return 0;
+ }
+}
+
+std::vector<SkeletonPatchInfo> SkeletonPatcher::skeletons;
+
+
+void SkeletonPatcher::RegisterSkeleton(std::string& path, SkelAnime* skelAnime) {
+ SkeletonPatchInfo info;
+
+ info.skelAnime = skelAnime;
+
+ static const std::string sOtr = "__OTR__";
+
+ if (path.starts_with(sOtr)) {
+ path = path.substr(sOtr.length());
+ }
+
+ // Determine if we're using an alternate skeleton
+ if (path.starts_with(LUS::IResource::gAltAssetPrefix)) {
+ info.vanillaSkeletonPath = path.substr(LUS::IResource::gAltAssetPrefix.length(),
+ path.size() - LUS::IResource::gAltAssetPrefix.length());
+ } else {
+ info.vanillaSkeletonPath = path;
+ }
+
+ skeletons.push_back(info);
+}
+
+void SkeletonPatcher::UnregisterSkeleton(SkelAnime* skelAnime) {
+
+ // TODO: Should probably just use a dictionary here...
+ for (int i = 0; i < skeletons.size(); i++)
+ {
+ auto skel = skeletons[i];
+
+ if (skel.skelAnime == skelAnime) {
+ skeletons.erase(skeletons.begin() + i);
+ break;
+ }
+ }
+}
+void SkeletonPatcher::ClearSkeletons()
+{
+ skeletons.clear();
+}
+
+void SkeletonPatcher::UpdateSkeletons() {
+ bool isHD = CVarGetInteger("gAltAssets", 0);
+ for (auto skel : skeletons) {
+ Skeleton* newSkel =
+ (Skeleton*)LUS::Context::GetInstance()->GetResourceManager()
+ ->LoadResource((isHD ? LUS::IResource::gAltAssetPrefix : "") + skel.vanillaSkeletonPath, true)
+ .get();
+
+ if (newSkel != nullptr) {
+ skel.skelAnime->skeleton = newSkel->skeletonData.skeletonHeader.segment;
+ uintptr_t skelPtr = (uintptr_t)newSkel->GetPointer();
+ memcpy(&skel.skelAnime->skeleton, &skelPtr, sizeof(uintptr_t)); // Dumb thing that needs to be done because cast is not cooperating
+ }
+ }
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/Skeleton.h b/mm/2s2h/resource/type/Skeleton.h
new file mode 100644
index 000000000..490614a44
--- /dev/null
+++ b/mm/2s2h/resource/type/Skeleton.h
@@ -0,0 +1,94 @@
+#pragma once
+
+#include <cstdint>
+#include "Resource.h"
+#include "SkeletonLimb.h"
+#include <z64animation.h>
+
+namespace LUS {
+
+enum class SkeletonType {
+ Normal,
+ Flex,
+ Curve,
+};
+
+// typedef struct {
+// /* 0x00 */ Vec3s jointPos; // Root is position in model space, children are relative to parent
+// /* 0x06 */ u8 child;
+// /* 0x07 */ u8 sibling;
+// /* 0x08 */ Gfx* dList;
+// } StandardLimb; // size = 0xC
+
+// Model has limbs with only rigid meshes
+typedef struct {
+ /* 0x00 */ void** segment;
+ /* 0x04 */ uint8_t limbCount;
+ uint8_t skeletonType;
+} SkeletonHeader; // size = 0x8
+
+// Model has limbs with flexible meshes
+typedef struct {
+ /* 0x00 */ SkeletonHeader sh;
+ /* 0x08 */ uint8_t dListCount;
+} FlexSkeletonHeader; // size = 0xC
+
+// typedef struct {
+// /* 0x0000 */ u8 firstChildIdx;
+// /* 0x0001 */ u8 nextLimbIdx;
+// /* 0x0004 */ Gfx* dList[2];
+// } SkelCurveLimb; // size = 0xC
+
+typedef struct {
+ /* 0x0000 */ SkelCurveLimb** limbs;
+ /* 0x0004 */ uint8_t limbCount;
+} SkelCurveLimbList; // size = 0x8
+
+union SkeletonData {
+ SkeletonHeader skeletonHeader;
+ FlexSkeletonHeader flexSkeletonHeader;
+ SkelCurveLimbList skelCurveLimbList;
+};
+
+class Skeleton : public Resource<SkeletonData> {
+ public:
+ using Resource::Resource;
+
+ Skeleton() : Resource(std::shared_ptr<ResourceInitData>()) {}
+
+ SkeletonData* GetPointer();
+ size_t GetPointerSize();
+
+ SkeletonType type;
+ SkeletonData skeletonData;
+
+ LimbType limbType;
+ int limbCount;
+ int dListCount;
+ LimbType limbTableType;
+ int limbTableCount;
+ std::vector<StandardLimb> standardLimbArray;
+ std::vector<SkelCurveLimb> curveLimbArray;
+ std::vector<std::string> limbTable;
+ std::vector<void*> skeletonHeaderSegments;
+};
+
+// TODO: CLEAN THIS UP LATER
+struct SkeletonPatchInfo
+{
+ SkelAnime* skelAnime;
+ std::string vanillaSkeletonPath;
+};
+
+class SkeletonPatcher {
+ public:
+ static void RegisterSkeleton(std::string& path, SkelAnime* skelAnime);
+ static void UnregisterSkeleton(SkelAnime* skelAnime);
+ static void ClearSkeletons();
+ static void UpdateSkeletons();
+
+ static std::vector<SkeletonPatchInfo> skeletons;
+};
+
+
+} // namespace LUS \ No newline at end of file
diff --git a/mm/2s2h/resource/type/SkeletonLimb.cpp b/mm/2s2h/resource/type/SkeletonLimb.cpp
new file mode 100644
index 000000000..8e5a8ef6d
--- /dev/null
+++ b/mm/2s2h/resource/type/SkeletonLimb.cpp
@@ -0,0 +1,24 @@
+#include "SkeletonLimb.h"
+
+namespace LUS {
+SkeletonLimbData* SkeletonLimb::GetPointer() {
+ return &limbData;
+}
+
+size_t SkeletonLimb::GetPointerSize() {
+ switch(limbType) {
+ case LimbType::Standard:
+ return sizeof(limbData.standardLimb);
+ case LimbType::LOD:
+ return sizeof(limbData.lodLimb);
+ case LimbType::Skin:
+ return sizeof(limbData.skinLimb);
+ case LimbType::Curve:
+ return sizeof(limbData.skelCurveLimb);
+ case LimbType::Invalid:
+ case LimbType::Legacy:
+ default:
+ return 0;
+ }
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/SkeletonLimb.h b/mm/2s2h/resource/type/SkeletonLimb.h
new file mode 100644
index 000000000..2110b3484
--- /dev/null
+++ b/mm/2s2h/resource/type/SkeletonLimb.h
@@ -0,0 +1,134 @@
+#pragma once
+
+#include "Resource.h"
+#include "libultraship/libultra.h"
+#include "z64math.h"
+
+namespace LUS {
+enum class LimbType {
+ Invalid,
+ Standard,
+ LOD,
+ Skin,
+ Curve,
+ Legacy,
+};
+
+enum class ZLimbSkinType
+{
+ SkinType_0, // Segment = 0
+ SkinType_4 = 4, // Segment = segmented address // Struct_800A5E28
+ SkinType_5 = 5, // Segment = 0
+ SkinType_DList = 11, // Segment = DList address
+};
+
+/**
+ * Holds a compact version of a vertex used in the Skin system
+ * It is used to initialise the Vtx used by an animated limb
+ */
+typedef struct {
+ /* 0x00 */ u16 index;
+ /* 0x02 */ s16 s; // s and t are texture coordinates (also known as u and v)
+ /* 0x04 */ s16 t;
+ /* 0x06 */ s8 normX;
+ /* 0x07 */ s8 normY;
+ /* 0x08 */ s8 normZ;
+ /* 0x09 */ u8 alpha;
+} SkinVertex; // size = 0xA
+
+/**
+ * Describes a position displacement and a scale to be applied to a limb at index `limbIndex`
+ */
+typedef struct {
+ /* 0x00 */ u8 limbIndex;
+ /* 0x02 */ s16 x;
+ /* 0x04 */ s16 y;
+ /* 0x06 */ s16 z;
+ /* 0x08 */ u8 scale;
+} SkinTransformation; // size = 0xA
+
+typedef struct {
+ /* 0x00 */ u16 vtxCount; // number of vertices in this modif entry
+ /* 0x02 */ u16 transformCount;
+ /* 0x04 */ u16 unk_4; // index of limbTransformations?
+ /* 0x08 */ SkinVertex* skinVertices;
+ /* 0x0C */ SkinTransformation* limbTransformations;
+} SkinLimbModif; // size = 0x10
+
+typedef struct {
+ /* 0x00 */ Vec3s jointPos; // Root is position in model space, children are relative to parent
+ /* 0x06 */ u8 child;
+ /* 0x07 */ u8 sibling;
+ /* 0x08 */ Gfx* dLists[2]; // Near and far
+} LodLimb; // size = 0x10
+
+typedef struct {
+ /* 0x00 */ Vec3s jointPos; // Root is position in model space, children are relative to parent
+ /* 0x06 */ u8 child;
+ /* 0x07 */ u8 sibling;
+ /* 0x08 */ Gfx* dList;
+} StandardLimb; // size = 0xC
+
+typedef struct {
+ /* 0x0000 */ u8 firstChildIdx;
+ /* 0x0001 */ u8 nextLimbIdx;
+ /* 0x0004 */ Gfx* dList[2];
+} SkelCurveLimb; // size = 0xC
+
+typedef struct {
+ /* 0x00 */ Vec3s jointPos; // Root is position in model space, children are relative to parent
+ /* 0x06 */ u8 child;
+ /* 0x07 */ u8 sibling;
+ /* 0x08 */ s32 segmentType; // Type of data contained in segment
+ /* 0x0C */ void* segment; // Gfx* if segmentType is SKIN_LIMB_TYPE_NORMAL, SkinAnimatedLimbData* if segmentType is SKIN_LIMB_TYPE_ANIMATED, NULL otherwise
+} SkinLimb; // size = 0x10
+
+typedef struct {
+ /* 0x00 */ u16 totalVtxCount; // total vertex count for all modif entries
+ /* 0x02 */ u16 limbModifCount;
+ /* 0x04 */ SkinLimbModif* limbModifications;
+ /* 0x08 */ Gfx* dlist;
+} SkinAnimatedLimbData; // size = 0xC
+
+union SkeletonLimbData {
+ LodLimb lodLimb;
+ StandardLimb standardLimb;
+ SkelCurveLimb skelCurveLimb;
+ SkinLimb skinLimb;
+};
+
+class SkeletonLimb : public Resource<SkeletonLimbData> {
+public:
+ using Resource::Resource;
+
+ SkeletonLimb() : Resource(std::shared_ptr<ResourceInitData>()) {}
+
+ SkeletonLimbData* GetPointer();
+ size_t GetPointerSize();
+
+ LimbType limbType;
+ SkeletonLimbData limbData;
+
+ ZLimbSkinType skinSegmentType;
+ uint16_t skinVtxCnt;
+ SkinAnimatedLimbData skinAnimLimbData;
+
+ std::string skinDataDList;
+ std::string skinDList;
+ std::string skinDList2;
+
+ float legTransX, legTransY, legTransZ; // Vec3f
+ uint16_t rotX, rotY, rotZ; // Vec3s
+
+ std::string childPtr, siblingPtr, dListPtr, dList2Ptr;
+
+ int16_t transX, transY, transZ;
+ uint8_t childIndex, siblingIndex;
+
+ uint32_t skinLimbModifCount;
+ std::vector<SkinLimbModif> skinLimbModifArray;
+
+ std::vector<std::vector<SkinVertex>> skinLimbModifVertexArrays;
+ std::vector<std::vector<SkinTransformation>> skinLimbModifTransformationArrays;
+};
+} // namespace LUS \ No newline at end of file
diff --git a/mm/2s2h/resource/type/Text.cpp b/mm/2s2h/resource/type/Text.cpp
new file mode 100644
index 000000000..eee289f8a
--- /dev/null
+++ b/mm/2s2h/resource/type/Text.cpp
@@ -0,0 +1,11 @@
+#include "Text.h"
+
+namespace LUS {
+MessageEntry* Text::GetPointer() {
+ return messages.data();
+}
+
+size_t Text::GetPointerSize() {
+ return messages.size() * sizeof(MessageEntry);
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/Text.h b/mm/2s2h/resource/type/Text.h
new file mode 100644
index 000000000..db0cb36a0
--- /dev/null
+++ b/mm/2s2h/resource/type/Text.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include "Resource.h"
+#include <libultraship/libultra/types.h>
+
+namespace LUS {
+// TODO: we've moved away from using classes for this stuff
+class MessageEntry
+{
+public:
+ uint16_t id;
+ uint8_t textboxType;
+ uint8_t textboxYPos;
+ std::string msg;
+};
+
+class Text : public Resource<MessageEntry> {
+public:
+ using Resource::Resource;
+
+ Text() : Resource(std::shared_ptr<ResourceInitData>()) {}
+
+ MessageEntry* GetPointer();
+ size_t GetPointerSize();
+
+ std::vector<MessageEntry> messages;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/TextMM.cpp b/mm/2s2h/resource/type/TextMM.cpp
new file mode 100644
index 000000000..b4acc8d18
--- /dev/null
+++ b/mm/2s2h/resource/type/TextMM.cpp
@@ -0,0 +1,11 @@
+#include "TextMM.h"
+
+namespace LUS {
+MessageEntryMM* TextMM::GetPointer() {
+ return messages.data();
+}
+
+size_t TextMM::GetPointerSize() {
+ return messages.size() * sizeof(MessageEntryMM);
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/TextMM.h b/mm/2s2h/resource/type/TextMM.h
new file mode 100644
index 000000000..7a6e2d1ab
--- /dev/null
+++ b/mm/2s2h/resource/type/TextMM.h
@@ -0,0 +1,37 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include "Resource.h"
+#include <libultraship/libultra/types.h>
+
+namespace LUS {
+// TODO: we've moved away from using classes for this stuff
+
+class MessageEntryMM {
+ public:
+ uint16_t id;
+ uint8_t textboxType;
+ uint8_t textboxYPos;
+ uint8_t icon;
+ uint16_t nextMessageID;
+ uint16_t firstItemCost;
+ uint16_t secondItemCost;
+ uint32_t segmentId;
+ uint32_t msgOffset;
+ std::string msg;
+};
+
+class TextMM : public Resource<MessageEntryMM> {
+public:
+ using Resource::Resource;
+
+ TextMM() : Resource(std::shared_ptr<ResourceInitData>()) {
+ }
+
+ MessageEntryMM* GetPointer();
+ size_t GetPointerSize();
+
+ std::vector<MessageEntryMM> messages;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/TextureAnimation.cpp b/mm/2s2h/resource/type/TextureAnimation.cpp
new file mode 100644
index 000000000..0d49d0e5b
--- /dev/null
+++ b/mm/2s2h/resource/type/TextureAnimation.cpp
@@ -0,0 +1,36 @@
+#include "TextureAnimation.h"
+
+namespace LUS {
+TextureAnimation::~TextureAnimation() {
+ for (auto& a : anims) {
+ switch ((TextureAnimationParamsType)a.type) {
+ case TextureAnimationParamsType::SingleScroll:
+ delete a.params;
+ break;
+ case TextureAnimationParamsType::DualScroll:
+ delete[] a.params;
+ break;
+ case TextureAnimationParamsType::ColorChange:
+ case TextureAnimationParamsType::ColorChangeLERP:
+ case TextureAnimationParamsType::ColorChangeLagrange:
+ delete[] ((AnimatedMatColorParams*)a.params)->keyFrames;
+ delete[] ((AnimatedMatColorParams*)a.params)->primColors;
+ delete[] ((AnimatedMatColorParams*)a.params)->envColors;
+ delete a.params;
+ break;
+ case TextureAnimationParamsType::TextureCycle:
+ // BENTODO free the textures
+ delete[] ((AnimatedMatTexCycleParams*)a.params)->textureIndexList;
+ delete[] ((AnimatedMatTexCycleParams*)a.params)->textureList;
+ delete a.params;
+ break;
+ }
+ }
+}
+AnimatedMaterial* TextureAnimation::GetPointer() {
+ return anims.data();
+}
+size_t TextureAnimation::GetPointerSize() {
+ return anims.size() * sizeof(AnimatedMaterial);
+}
+} // namespace LUS \ No newline at end of file
diff --git a/mm/2s2h/resource/type/TextureAnimation.h b/mm/2s2h/resource/type/TextureAnimation.h
new file mode 100644
index 000000000..5538547c6
--- /dev/null
+++ b/mm/2s2h/resource/type/TextureAnimation.h
@@ -0,0 +1,104 @@
+#pragma once
+#include "Resource.h"
+
+namespace LUS {
+enum class TextureAnimationParamsType {
+ /* 0 */ SingleScroll,
+ /* 1 */ DualScroll,
+ /* 2 */ ColorChange,
+ /* 3 */ ColorChangeLERP,
+ /* 4 */ ColorChangeLagrange,
+ /* 5 */ TextureCycle,
+ /* 6 */ Empty // An empty TextureAnimation has the form 00 00 00 06 00000000
+};
+
+class ZTextureAnimationParams {
+ public:
+ TextureAnimationParamsType type;
+};
+
+struct TextureScrollingParamsEntry {
+ int8_t xStep;
+ int8_t yStep;
+ uint8_t width;
+ uint8_t height;
+};
+
+class TextureScrollingParams : public ZTextureAnimationParams {
+ public:
+ int count; // 1 for Single, 2 for Dual
+ TextureScrollingParamsEntry rows[2]; // Too small to make a vector worth it
+};
+
+struct F3DPrimColor {
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+ uint8_t a;
+ uint8_t lodFrac;
+};
+
+struct F3DEnvColor {
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+ uint8_t a;
+};
+
+class TextureColorChangingParams : public ZTextureAnimationParams {
+ public:
+ uint16_t animLength; // size of list for type 2
+ uint16_t colorListCount;
+ std::vector<F3DPrimColor> primColorList;
+ std::vector<F3DEnvColor> envColorList;
+ std::vector<uint16_t> frameDataList;
+};
+
+class TextureCyclingParams : public ZTextureAnimationParams {
+ public:
+ uint16_t cycleLength;
+ std::vector<void*> textureList;
+ std::vector<uint8_t> textureIndexList;
+};
+
+typedef struct {
+ /* 0x0 */ uint16_t keyFrameLength;
+ /* 0x2 */ uint16_t keyFrameCount;
+ /* 0x4 */ F3DPrimColor* primColors;
+ /* 0x8 */ F3DEnvColor* envColors;
+ /* 0xC */ uint16_t* keyFrames;
+} AnimatedMatColorParams; // size = 0x10
+
+typedef struct {
+ /* 0x0 */ int8_t xStep;
+ /* 0x1 */ int8_t yStep;
+ /* 0x2 */ uint8_t width;
+ /* 0x3 */ uint8_t height;
+} AnimatedMatTexScrollParams; // size = 0x4
+
+typedef struct {
+ /* 0x0 */ uint16_t keyFrameLength;
+ /* 0x4 */ void** textureList;
+ /* 0x8 */ uint8_t* textureIndexList;
+} AnimatedMatTexCycleParams; // size = 0xC
+
+typedef struct {
+ /* 0x0 */ int8_t segment;
+ /* 0x2 */ int16_t type;
+ /* 0x4 */ void* params;
+} AnimatedMaterial; // size = 0x8
+
+class TextureAnimation : public Resource<AnimatedMaterial> {
+ public:
+ using Resource::Resource;
+ ~TextureAnimation();
+
+ TextureAnimation() : Resource(std::shared_ptr<ResourceInitData>()) {
+ }
+
+ AnimatedMaterial* GetPointer();
+ size_t GetPointerSize();
+
+ std::vector<AnimatedMaterial> anims;
+};
+} // namespace LUS \ No newline at end of file
diff --git a/mm/2s2h/resource/type/scenecommand/EndMarker.cpp b/mm/2s2h/resource/type/scenecommand/EndMarker.cpp
new file mode 100644
index 000000000..9d12c1518
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/EndMarker.cpp
@@ -0,0 +1,11 @@
+#include "EndMarker.h"
+
+namespace LUS {
+Marker* EndMarker::GetPointer() {
+ return &endMarker;
+}
+
+size_t EndMarker::GetPointerSize() {
+ return sizeof(Marker);
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/EndMarker.h b/mm/2s2h/resource/type/scenecommand/EndMarker.h
new file mode 100644
index 000000000..9f981f1f9
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/EndMarker.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <memory>
+#include "Resource.h"
+#include "SceneCommand.h"
+#include <libultraship/libultra/types.h>
+
+namespace LUS {
+typedef struct {
+
+} Marker;
+
+class EndMarker : public SceneCommand<Marker> {
+ public:
+ using SceneCommand::SceneCommand;
+
+ Marker* GetPointer();
+ size_t GetPointerSize();
+
+ Marker endMarker;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/RomFile.h b/mm/2s2h/resource/type/scenecommand/RomFile.h
new file mode 100644
index 000000000..ebdc3dc3b
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/RomFile.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include <cstdint>
+
+namespace LUS {
+ typedef struct {
+ /* 0x00 */ uintptr_t vromStart;
+ /* 0x04 */ uintptr_t vromEnd;
+ char* fileName;
+ } RomFile; // size = 0x8
+} \ No newline at end of file
diff --git a/mm/2s2h/resource/type/scenecommand/SceneCommand.h b/mm/2s2h/resource/type/scenecommand/SceneCommand.h
new file mode 100644
index 000000000..ea99e066e
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SceneCommand.h
@@ -0,0 +1,67 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <memory>
+#include "Resource.h"
+#include <libultraship/libultra/types.h>
+
+namespace LUS {
+
+enum class SceneCommandID : uint8_t {
+ SetStartPositionList = 0x00,
+ SetActorList = 0x01,
+ SetCsCamera = 0x02,
+ SetCollisionHeader = 0x03,
+ SetRoomList = 0x04,
+ SetWind = 0x05,
+ SetEntranceList = 0x06,
+ SetSpecialObjects = 0x07,
+ SetRoomBehavior = 0x08,
+ Unused09 = 0x09,
+ SetMesh = 0x0A,
+ SetObjectList = 0x0B,
+ SetLightList = 0x0C,
+ SetPathways = 0x0D,
+ SetTransitionActorList = 0x0E,
+ SetLightingSettings = 0x0F,
+ SetTimeSettings = 0x10,
+ SetSkyboxSettings = 0x11,
+ SetSkyboxModifier = 0x12,
+ SetExitList = 0x13,
+ EndMarker = 0x14,
+ SetSoundSettings = 0x15,
+ SetEchoSettings = 0x16,
+ SetCutscenes = 0x17,
+ SetAlternateHeaders = 0x18,
+ SetCameraSettings = 0x19,
+
+ // MM Commands
+ SetWorldMapVisited = 0x19,
+ SetAnimatedMaterialList = 0x1A,
+ SetActorCutsceneList = 0x1B,
+ SetMinimapList = 0x1C,
+ Unused1D = 0x1D,
+ SetMinimapChests = 0x1E,
+ SetCutscenesMM = 0x1F, // This opcode is not in the original game. Its a special command for OTRs.
+
+ Error = 0xFF
+};
+
+class ISceneCommand : public IResource {
+public:
+ using IResource::IResource;
+ ISceneCommand() : IResource(std::shared_ptr<ResourceInitData>()) {}
+ SceneCommandID cmdId;
+};
+
+template <class T> class SceneCommand : public ISceneCommand {
+ public:
+ using ISceneCommand::ISceneCommand;
+ virtual T* GetPointer() = 0;
+ void* GetRawPointer() override {
+ return static_cast<void*>(GetPointer());
+ }
+};
+
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetActorCutsceneList.cpp b/mm/2s2h/resource/type/scenecommand/SetActorCutsceneList.cpp
new file mode 100644
index 000000000..79d5253ac
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetActorCutsceneList.cpp
@@ -0,0 +1,13 @@
+#include "SetActorCutsceneList.h"
+#include "2s2h/resource/type/scenecommand/SetActorCutsceneList.h"
+
+namespace LUS {
+CutsceneEntry* SetActorCutsceneList::GetPointer() {
+ return entries.data();
+}
+
+size_t SetActorCutsceneList::GetPointerSize() {
+ return entries.size() * sizeof(CutsceneEntry);
+}
+
+} // namespace LUS \ No newline at end of file
diff --git a/mm/2s2h/resource/type/scenecommand/SetActorCutsceneList.h b/mm/2s2h/resource/type/scenecommand/SetActorCutsceneList.h
new file mode 100644
index 000000000..5b93d1a5b
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetActorCutsceneList.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include "Resource.h"
+#include "SceneCommand.h"
+
+namespace LUS {
+typedef struct CutsceneEntry {
+ int16_t priority;
+ int16_t length;
+ int16_t csCamId;
+ int16_t scriptIndex;
+ int16_t additionalCsId;
+ uint8_t endSfx;
+ uint8_t customValue;
+ int16_t hudVisibility;
+ uint8_t endCam;
+ uint8_t letterboxSize;
+} CutsceneEntry;
+
+class SetActorCutsceneList : public SceneCommand<CutsceneEntry> {
+ public:
+ using SceneCommand::SceneCommand;
+
+ CutsceneEntry* GetPointer();
+ size_t GetPointerSize();
+
+ uint32_t numEntries;
+ std::vector<CutsceneEntry> entries;
+};
+
+} \ No newline at end of file
diff --git a/mm/2s2h/resource/type/scenecommand/SetActorList.cpp b/mm/2s2h/resource/type/scenecommand/SetActorList.cpp
new file mode 100644
index 000000000..d37ebb148
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetActorList.cpp
@@ -0,0 +1,11 @@
+#include "SetActorList.h"
+
+namespace LUS {
+ActorEntry* SetActorList::GetPointer() {
+ return actorList.data();
+}
+
+size_t SetActorList::GetPointerSize() {
+ return actorList.size() * sizeof(ActorEntry);
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetActorList.h b/mm/2s2h/resource/type/scenecommand/SetActorList.h
new file mode 100644
index 000000000..117175d7d
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetActorList.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <memory>
+#include <string>
+#include "Resource.h"
+#include "SceneCommand.h"
+// #include <libultraship/libultra/types.h>
+#include "z64math.h"
+
+namespace LUS {
+typedef struct {
+ /* 0x00 */ s16 id;
+ /* 0x02 */ Vec3s pos;
+ /* 0x08 */ Vec3s rot;
+ /* 0x0E */ s16 params;
+} ActorEntry; // size = 0x10
+
+class SetActorList : public SceneCommand<ActorEntry> {
+ public:
+ using SceneCommand::SceneCommand;
+
+ ActorEntry* GetPointer();
+ size_t GetPointerSize();
+
+ uint32_t numActors;
+ std::vector<ActorEntry> actorList;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetAlternateHeaders.cpp b/mm/2s2h/resource/type/scenecommand/SetAlternateHeaders.cpp
new file mode 100644
index 000000000..15b3c97d4
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetAlternateHeaders.cpp
@@ -0,0 +1,12 @@
+#include "SetAlternateHeaders.h"
+
+namespace LUS {
+void* SetAlternateHeaders::GetPointer() {
+ // Like Scene, SetAlternateHeader is a special type that is only acted upon in C++.
+ return nullptr;
+}
+
+size_t SetAlternateHeaders::GetPointerSize() {
+ return 0;
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetAlternateHeaders.h b/mm/2s2h/resource/type/scenecommand/SetAlternateHeaders.h
new file mode 100644
index 000000000..ebc571e3d
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetAlternateHeaders.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <memory>
+#include <string>
+#include "Resource.h"
+#include "SceneCommand.h"
+#include "2s2h/resource/type/Scene.h"
+#include "RomFile.h"
+#include <libultraship/libultra/types.h>
+
+
+namespace LUS {
+
+class SetAlternateHeaders : public SceneCommand<void> {
+ public:
+ using SceneCommand::SceneCommand;
+
+ void* GetPointer();
+ size_t GetPointerSize();
+
+ uint32_t numHeaders;
+ std::vector<std::shared_ptr<Scene>> headers;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetAnimatedMaterialList.cpp b/mm/2s2h/resource/type/scenecommand/SetAnimatedMaterialList.cpp
new file mode 100644
index 000000000..ef3adc543
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetAnimatedMaterialList.cpp
@@ -0,0 +1,13 @@
+#include "SetAnimatedMaterialList.h"
+
+namespace LUS {
+
+AnimatedMaterialData* LUS::SetAnimatedMaterialList::GetPointer() {
+ return mat;
+}
+
+size_t LUS::SetAnimatedMaterialList::GetPointerSize() {
+ return sizeof(AnimatedMaterialData);
+}
+
+} // namespace LUS \ No newline at end of file
diff --git a/mm/2s2h/resource/type/scenecommand/SetAnimatedMaterialList.h b/mm/2s2h/resource/type/scenecommand/SetAnimatedMaterialList.h
new file mode 100644
index 000000000..02a8c7c38
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetAnimatedMaterialList.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "Resource.h"
+#include "SceneCommand.h"
+#include <libultraship/libultra/types.h>
+
+namespace LUS {
+typedef struct {
+ /* 0x0 */ int8_t segment;
+ /* 0x2 */ int16_t type;
+ /* 0x4 */ void* params;
+} AnimatedMaterialData; // size = 0x8
+
+class SetAnimatedMaterialList : public SceneCommand<AnimatedMaterialData> {
+ public:
+ using SceneCommand::SceneCommand;
+
+ AnimatedMaterialData* GetPointer();
+ size_t GetPointerSize();
+
+ AnimatedMaterialData* mat;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetCameraSettings.cpp b/mm/2s2h/resource/type/scenecommand/SetCameraSettings.cpp
new file mode 100644
index 000000000..e1d2967e7
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetCameraSettings.cpp
@@ -0,0 +1,11 @@
+#include "SetCameraSettings.h"
+
+namespace LUS {
+CameraSettings* SetCameraSettings::GetPointer() {
+ return &settings;
+}
+
+size_t SetCameraSettings::GetPointerSize() {
+ return sizeof(CameraSettings);
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetCameraSettings.h b/mm/2s2h/resource/type/scenecommand/SetCameraSettings.h
new file mode 100644
index 000000000..adbde2e02
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetCameraSettings.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <memory>
+#include "Resource.h"
+#include "SceneCommand.h"
+#include <libultraship/libultra/types.h>
+
+namespace LUS {
+typedef struct {
+ int8_t cameraMovement;
+ int32_t worldMapArea;
+} CameraSettings;
+
+class SetCameraSettings : public SceneCommand<CameraSettings> {
+ public:
+ using SceneCommand::SceneCommand;
+
+ CameraSettings* GetPointer();
+ size_t GetPointerSize();
+
+ CameraSettings settings;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetCollisionHeader.cpp b/mm/2s2h/resource/type/scenecommand/SetCollisionHeader.cpp
new file mode 100644
index 000000000..db8cdcb32
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetCollisionHeader.cpp
@@ -0,0 +1,17 @@
+#include "SetCollisionHeader.h"
+
+namespace LUS {
+CollisionHeaderData* SetCollisionHeader::GetPointer() {
+ if (collisionHeader == nullptr) {
+ return nullptr;
+ }
+ return collisionHeader->GetPointer();
+}
+
+size_t SetCollisionHeader::GetPointerSize() {
+ if (collisionHeader == nullptr) {
+ return 0;
+ }
+ return collisionHeader->GetPointerSize();
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetCollisionHeader.h b/mm/2s2h/resource/type/scenecommand/SetCollisionHeader.h
new file mode 100644
index 000000000..c615eedad
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetCollisionHeader.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <memory>
+#include <string>
+#include "Resource.h"
+#include "2s2h/resource/type/scenecommand/SceneCommand.h"
+#include "2s2h/resource/type/CollisionHeader.h"
+// #include <libultraship/libultra/types.h>
+
+namespace LUS {
+class SetCollisionHeader : public SceneCommand<CollisionHeaderData> {
+ public:
+ using SceneCommand::SceneCommand;
+
+ CollisionHeaderData* GetPointer();
+ size_t GetPointerSize();
+
+ std::string fileName;
+
+ std::shared_ptr<CollisionHeader> collisionHeader;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetCsCamera.cpp b/mm/2s2h/resource/type/scenecommand/SetCsCamera.cpp
new file mode 100644
index 000000000..186ffcae6
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetCsCamera.cpp
@@ -0,0 +1,18 @@
+#include "SetCsCamera.h"
+
+namespace LUS {
+SetCsCamera::~SetCsCamera() {
+ for (auto c : csCamera) {
+ if (c.actorCsCamFuncData != nullptr) {
+ delete c.actorCsCamFuncData;
+ }
+ }
+}
+ActorCsCamInfoData* SetCsCamera::GetPointer() {
+ return csCamera.data();
+}
+
+size_t SetCsCamera::GetPointerSize() {
+ return sizeof(ActorCsCamInfoData);
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetCsCamera.h b/mm/2s2h/resource/type/scenecommand/SetCsCamera.h
new file mode 100644
index 000000000..fa5d9a500
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetCsCamera.h
@@ -0,0 +1,37 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <memory>
+#include "Resource.h"
+#include "SceneCommand.h"
+#include <libultraship/libultra/types.h>
+
+namespace LUS {
+typedef struct {
+
+} CsCamera;
+
+typedef struct {
+ /* 0x0 */ s16 x;
+ /* 0x2 */ s16 y;
+ /* 0x4 */ s16 z;
+} z64Vec3s; // size = 0x6
+
+typedef struct {
+ /* 0x0 */ s16 setting; // camera setting described by CameraSettingType enum
+ /* 0x2 */ s16 count;
+ /* 0x4 */ z64Vec3s* actorCsCamFuncData; // s16 data grouped in threes
+} ActorCsCamInfoData; // size = 0x8
+
+class SetCsCamera : public SceneCommand<ActorCsCamInfoData> {
+ public:
+ using SceneCommand::SceneCommand;
+ ~SetCsCamera();
+
+ ActorCsCamInfoData* GetPointer();
+ size_t GetPointerSize();
+
+ std::vector<ActorCsCamInfoData> csCamera;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetCutscenes.cpp b/mm/2s2h/resource/type/scenecommand/SetCutscenes.cpp
new file mode 100644
index 000000000..01c88751b
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetCutscenes.cpp
@@ -0,0 +1,35 @@
+#include "SetCutscenes.h"
+
+namespace LUS {
+uint32_t* SetCutscenes::GetPointer() {
+ if (cutscene == nullptr) {
+ return nullptr;
+ }
+ return cutscene->GetPointer();
+}
+
+size_t SetCutscenes::GetPointerSize() {
+ if (cutscene == nullptr) {
+ return 0;
+ }
+ return cutscene->GetPointerSize();
+}
+
+CutsceneScriptEntry* SetCutscenesMM::GetPointer() {
+ // return nullptr; // BENTODO
+ if (entries.size() == 0) {
+ return nullptr;
+ }
+ return entries.data();
+}
+
+size_t SetCutscenesMM::GetPointerSize() {
+ //return 0;
+ // BENTODO
+ if (entries.size() == 0) {
+ return 0;
+ }
+ return entries.size() * sizeof(CutsceneScriptEntry);
+}
+
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetCutscenes.h b/mm/2s2h/resource/type/scenecommand/SetCutscenes.h
new file mode 100644
index 000000000..900201522
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetCutscenes.h
@@ -0,0 +1,43 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <memory>
+#include <string>
+#include "Resource.h"
+#include "2s2h/resource/type/scenecommand/SceneCommand.h"
+#include "2s2h/resource/type/Cutscene.h"
+// #include <libultraship/libultra.h>
+
+namespace LUS {
+class SetCutscenes : public SceneCommand<uint32_t> {
+ public:
+ using SceneCommand::SceneCommand;
+
+ uint32_t* GetPointer();
+ size_t GetPointerSize();
+
+ std::string fileName;
+ std::shared_ptr<Cutscene> cutscene;
+};
+
+class CutsceneScriptEntry {
+ public:
+ void* data;
+ uint16_t exit;
+ uint8_t entrance;
+ uint8_t flag;
+};
+
+
+class SetCutscenesMM : public SceneCommand<CutsceneScriptEntry> {
+ public:
+ using SceneCommand::SceneCommand;
+
+ CutsceneScriptEntry* GetPointer();
+ size_t GetPointerSize();
+
+ std::vector<CutsceneScriptEntry> entries;
+};
+
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetEchoSettings.cpp b/mm/2s2h/resource/type/scenecommand/SetEchoSettings.cpp
new file mode 100644
index 000000000..47cfa85ad
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetEchoSettings.cpp
@@ -0,0 +1,11 @@
+#include "SetEchoSettings.h"
+
+namespace LUS {
+EchoSettings* SetEchoSettings::GetPointer() {
+ return &settings;
+}
+
+size_t SetEchoSettings::GetPointerSize() {
+ return sizeof(EchoSettings);
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetEchoSettings.h b/mm/2s2h/resource/type/scenecommand/SetEchoSettings.h
new file mode 100644
index 000000000..ea2f664d4
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetEchoSettings.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <memory>
+#include "Resource.h"
+#include "SceneCommand.h"
+#include <libultraship/libultra/types.h>
+
+namespace LUS {
+typedef struct {
+ int8_t echo;
+} EchoSettings;
+
+class SetEchoSettings : public SceneCommand<EchoSettings> {
+ public:
+ using SceneCommand::SceneCommand;
+
+ EchoSettings* GetPointer();
+ size_t GetPointerSize();
+
+ EchoSettings settings;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetEntranceList.cpp b/mm/2s2h/resource/type/scenecommand/SetEntranceList.cpp
new file mode 100644
index 000000000..d33ac189b
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetEntranceList.cpp
@@ -0,0 +1,11 @@
+#include "SetEntranceList.h"
+
+namespace LUS {
+EntranceEntry* SetEntranceList::GetPointer() {
+ return entrances.data();
+}
+
+size_t SetEntranceList::GetPointerSize() {
+ return entrances.size() * sizeof(EntranceEntry);
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetEntranceList.h b/mm/2s2h/resource/type/scenecommand/SetEntranceList.h
new file mode 100644
index 000000000..d31dda78c
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetEntranceList.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <memory>
+#include <string>
+#include "Resource.h"
+#include "SceneCommand.h"
+#include <libultraship/libultra/types.h>
+
+namespace LUS {
+typedef struct {
+ /* 0x00 */ u8 spawn;
+ /* 0x01 */ u8 room;
+} EntranceEntry;
+
+class SetEntranceList : public SceneCommand<EntranceEntry> {
+ public:
+ using SceneCommand::SceneCommand;
+
+ EntranceEntry* GetPointer();
+ size_t GetPointerSize();
+
+ uint32_t numEntrances;
+
+ std::vector<EntranceEntry> entrances;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetExitList.cpp b/mm/2s2h/resource/type/scenecommand/SetExitList.cpp
new file mode 100644
index 000000000..99966cdb4
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetExitList.cpp
@@ -0,0 +1,11 @@
+#include "SetExitList.h"
+
+namespace LUS {
+uint16_t* SetExitList::GetPointer() {
+ return exits.data();
+}
+
+size_t SetExitList::GetPointerSize() {
+ return exits.size() * sizeof(int16_t);
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetExitList.h b/mm/2s2h/resource/type/scenecommand/SetExitList.h
new file mode 100644
index 000000000..c6f0b3f42
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetExitList.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <memory>
+#include "Resource.h"
+#include "SceneCommand.h"
+#include <libultraship/libultra/types.h>
+
+namespace LUS {
+class SetExitList : public SceneCommand<uint16_t> {
+ public:
+ using SceneCommand::SceneCommand;
+
+ uint16_t* GetPointer();
+ size_t GetPointerSize();
+
+ uint32_t numExits;
+
+ std::vector<uint16_t> exits;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetLightList.cpp b/mm/2s2h/resource/type/scenecommand/SetLightList.cpp
new file mode 100644
index 000000000..c59f75c20
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetLightList.cpp
@@ -0,0 +1,11 @@
+#include "SetLightList.h"
+
+namespace LUS {
+LightInfo* SetLightList::GetPointer() {
+ return lightList.data();
+}
+
+size_t SetLightList::GetPointerSize() {
+ return lightList.size() * sizeof(LightInfo);
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetLightList.h b/mm/2s2h/resource/type/scenecommand/SetLightList.h
new file mode 100644
index 000000000..bbc5aad3d
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetLightList.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <memory>
+#include <string>
+#include "Resource.h"
+#include "SceneCommand.h"
+#include <libultraship/libultra/types.h>
+
+namespace LUS {
+typedef struct {
+ /* 0x0 */ s16 x;
+ /* 0x2 */ s16 y;
+ /* 0x4 */ s16 z;
+ /* 0x6 */ u8 color[3];
+ /* 0x9 */ u8 drawGlow;
+ /* 0xA */ s16 radius;
+} LightPoint; // size = 0xC
+
+typedef struct {
+ /* 0x0 */ s8 x;
+ /* 0x1 */ s8 y;
+ /* 0x2 */ s8 z;
+ /* 0x3 */ u8 color[3];
+} LightDirectional; // size = 0x6
+
+typedef union {
+ LightPoint point;
+ LightDirectional dir;
+} LightParams; // size = 0xC
+
+typedef struct {
+ /* 0x0 */ u8 type;
+ /* 0x2 */ LightParams params;
+} LightInfo; // size = 0xE
+
+class SetLightList : public SceneCommand<LightInfo> {
+ public:
+ using SceneCommand::SceneCommand;
+
+ LightInfo* GetPointer();
+ size_t GetPointerSize();
+
+ uint32_t numLights;
+ std::vector<LightInfo> lightList;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetLightingSettings.cpp b/mm/2s2h/resource/type/scenecommand/SetLightingSettings.cpp
new file mode 100644
index 000000000..69127f19c
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetLightingSettings.cpp
@@ -0,0 +1,11 @@
+#include "SetLightingSettings.h"
+
+namespace LUS {
+EnvLightSettings* SetLightingSettings::GetPointer() {
+ return settings.data();
+}
+
+size_t SetLightingSettings::GetPointerSize() {
+ return settings.size() * sizeof(EnvLightSettings);
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetLightingSettings.h b/mm/2s2h/resource/type/scenecommand/SetLightingSettings.h
new file mode 100644
index 000000000..ca894fec3
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetLightingSettings.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <memory>
+#include "Resource.h"
+#include "SceneCommand.h"
+#include <libultraship/libultra/types.h>
+
+namespace LUS {
+typedef struct {
+ /* 0x00 */ u8 ambientColor[3];
+ /* 0x03 */ s8 light1Dir[3];
+ /* 0x06 */ u8 light1Color[3];
+ /* 0x09 */ s8 light2Dir[3];
+ /* 0x0C */ u8 light2Color[3];
+ /* 0x0F */ u8 fogColor[3];
+ /* 0x12 */ s16 fogNear;
+ /* 0x14 */ s16 fogFar;
+} EnvLightSettings; // size = 0x16
+
+class SetLightingSettings : public SceneCommand<EnvLightSettings> {
+ public:
+ using SceneCommand::SceneCommand;
+
+ EnvLightSettings* GetPointer();
+ size_t GetPointerSize();
+
+ std::vector<EnvLightSettings> settings;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetMesh.cpp b/mm/2s2h/resource/type/scenecommand/SetMesh.cpp
new file mode 100644
index 000000000..5ecd2cbfa
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetMesh.cpp
@@ -0,0 +1,11 @@
+#include "SetMesh.h"
+
+namespace LUS {
+MeshHeader* SetMesh::GetPointer() {
+ return &meshHeader;
+}
+
+size_t SetMesh::GetPointerSize() {
+ return sizeof(MeshHeader);
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetMesh.h b/mm/2s2h/resource/type/scenecommand/SetMesh.h
new file mode 100644
index 000000000..a3a6c9731
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetMesh.h
@@ -0,0 +1,103 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <memory>
+#include "Resource.h"
+#include "SceneCommand.h"
+#include "libultraship/libultra.h"
+#include "z64math.h"
+
+namespace LUS {
+typedef struct {
+ /* 0x00 */ u8 type;
+} PolygonBase;
+
+typedef struct {
+ /* 0x00 */ PolygonBase base;
+ /* 0x01 */ u8 num; // number of dlist entries
+ /* 0x04 */ void* start;
+ /* 0x08 */ void* end;
+} PolygonType0; // size = 0xC
+
+typedef struct {
+ /* 0x00 */ u16 unk_00;
+ /* 0x02 */ u8 id;
+ /* 0x04 */ void* source;
+ /* 0x08 */ u32 unk_0C;
+ /* 0x0C */ u32 tlut;
+ /* 0x10 */ u16 width;
+ /* 0x12 */ u16 height;
+ /* 0x14 */ u8 fmt;
+ /* 0x15 */ u8 siz;
+ /* 0x16 */ u16 mode0;
+ /* 0x18 */ u16 tlutCount;
+} BgImage; // size = 0x1C
+
+typedef struct {
+ /* 0x00 */ PolygonBase base;
+ /* 0x01 */ u8 format; // 1 = single, 2 = multi
+ /* 0x04 */ Gfx* dlist;
+ union {
+ struct {
+ /* 0x08 */ void* source;
+ /* 0x0C */ u32 unk_0C;
+ /* 0x10 */ void* tlut;
+ /* 0x14 */ u16 width;
+ /* 0x16 */ u16 height;
+ /* 0x18 */ u8 fmt;
+ /* 0x19 */ u8 siz;
+ /* 0x1A */ u16 mode0;
+ /* 0x1C */ u16 tlutCount;
+ } single;
+ struct {
+ /* 0x08 */ u8 count;
+ /* 0x0C */ BgImage* list;
+ } multi;
+ };
+} PolygonType1;
+
+typedef struct {
+ /* 0x00 */ PolygonBase base;
+ /* 0x01 */ u8 num; // number of dlist entries
+ /* 0x04 */ void* start;
+ /* 0x08 */ void* end;
+} PolygonType2; // size = 0xC
+
+typedef union {
+ PolygonBase base;
+ PolygonType0 polygon0;
+ PolygonType1 polygon1;
+ PolygonType2 polygon2;
+} MeshHeader; // "Ground Shape"
+
+typedef struct {
+ /* 0x00 */ Vec3s pos;
+ /* 0x06 */ s16 unk_06;
+ /* 0x08 */ Gfx* opa;
+ /* 0x0C */ Gfx* xlu;
+} PolygonDlist2; // size = 0x8
+
+typedef struct {
+ /* 0x00 */ Gfx* opa;
+ /* 0x04 */ Gfx* xlu;
+} PolygonDlist; // size = 0x8
+
+class SetMesh : public SceneCommand<MeshHeader> {
+ public:
+ using SceneCommand::SceneCommand;
+
+ MeshHeader* GetPointer();
+ size_t GetPointerSize();
+
+ uint32_t numPoly;
+ uint8_t data;
+ uint8_t meshHeaderType;
+
+ std::vector<PolygonDlist> dlists;
+ std::vector<PolygonDlist2> dlists2;
+ std::vector<std::string> imagePaths;
+ std::vector<BgImage> images;
+ MeshHeader meshHeader;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetMinimapChests.cpp b/mm/2s2h/resource/type/scenecommand/SetMinimapChests.cpp
new file mode 100644
index 000000000..dde6da61d
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetMinimapChests.cpp
@@ -0,0 +1,13 @@
+#include "SetMinimapChests.h"
+
+namespace LUS {
+
+MinimapChestData* SetMinimapChests::GetPointer() {
+ return chests.data();
+}
+
+size_t SetMinimapChests::GetPointerSize() {
+ return sizeof(MinimapChestData);
+}
+
+} // namespace LUS \ No newline at end of file
diff --git a/mm/2s2h/resource/type/scenecommand/SetMinimapChests.h b/mm/2s2h/resource/type/scenecommand/SetMinimapChests.h
new file mode 100644
index 000000000..2877d4dc7
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetMinimapChests.h
@@ -0,0 +1,23 @@
+#pragma once
+#include "Resource.h"
+#include "SceneCommand.h"
+
+namespace LUS {
+typedef struct {
+ int16_t unk_0;
+ int16_t unk_2;
+ int16_t unk_4;
+ int16_t unk_6;
+ int16_t unk_8;
+} MinimapChestData;
+
+class SetMinimapChests : public SceneCommand<MinimapChestData> {
+ public:
+ using SceneCommand::SceneCommand;
+
+ MinimapChestData* GetPointer();
+ size_t GetPointerSize();
+ std::vector<MinimapChestData> chests;
+};
+
+}
diff --git a/mm/2s2h/resource/type/scenecommand/SetMinimapList.cpp b/mm/2s2h/resource/type/scenecommand/SetMinimapList.cpp
new file mode 100644
index 000000000..e97a6eee0
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetMinimapList.cpp
@@ -0,0 +1,13 @@
+#include "SetMinimapList.h"
+
+namespace LUS {
+
+MinimapListData* LUS::SetMinimapList::GetPointer() {
+ return &list;
+}
+
+size_t LUS::SetMinimapList::GetPointerSize() {
+ return sizeof(list);
+}
+
+} // namespace LUS \ No newline at end of file
diff --git a/mm/2s2h/resource/type/scenecommand/SetMinimapList.h b/mm/2s2h/resource/type/scenecommand/SetMinimapList.h
new file mode 100644
index 000000000..ecebbc323
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetMinimapList.h
@@ -0,0 +1,33 @@
+#pragma once
+
+#include "Resource.h"
+#include "SceneCommand.h"
+#include <libultraship/libultra/types.h>
+
+
+namespace LUS {
+typedef struct {
+ /* 0x00 */ u16 mapId;
+ /* 0x02 */ s16 unk2;
+ /* 0x04 */ s16 unk4;
+ /* 0x06 */ s16 unk6;
+ /* 0x08 */ u16 unk8; // flags; 1 = mirror x? 2 = mirror y?
+} MinimapEntryData; // size = 0xA
+
+typedef struct {
+ /* 0x00 */ MinimapEntryData* entry;
+ /* 0x04 */ s16 scale;
+} MinimapListData; // size = 0x8
+
+
+class SetMinimapList : public SceneCommand<MinimapListData> {
+ public:
+ using SceneCommand::SceneCommand;
+ MinimapListData* GetPointer();
+ size_t GetPointerSize();
+
+ MinimapListData list;
+ std::vector<MinimapEntryData> entries;
+};
+
+}
diff --git a/mm/2s2h/resource/type/scenecommand/SetObjectList.cpp b/mm/2s2h/resource/type/scenecommand/SetObjectList.cpp
new file mode 100644
index 000000000..921166675
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetObjectList.cpp
@@ -0,0 +1,11 @@
+#include "SetObjectList.h"
+
+namespace LUS {
+int16_t* SetObjectList::GetPointer() {
+ return objects.data();
+}
+
+size_t SetObjectList::GetPointerSize() {
+ return objects.size() * sizeof(int16_t);
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetObjectList.h b/mm/2s2h/resource/type/scenecommand/SetObjectList.h
new file mode 100644
index 000000000..c6f786636
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetObjectList.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <memory>
+#include <string>
+#include "Resource.h"
+#include "SceneCommand.h"
+#include <libultraship/libultra/types.h>
+
+namespace LUS {
+class SetObjectList : public SceneCommand<int16_t> {
+ public:
+ using SceneCommand::SceneCommand;
+
+ int16_t* GetPointer();
+ size_t GetPointerSize();
+
+ uint32_t numObjects;
+ std::vector<int16_t> objects;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetPathways.cpp b/mm/2s2h/resource/type/scenecommand/SetPathways.cpp
new file mode 100644
index 000000000..046543a5b
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetPathways.cpp
@@ -0,0 +1,18 @@
+#include "SetPathways.h"
+
+namespace LUS {
+PathData** SetPathways::GetPointer() {
+ return paths.data();
+}
+
+size_t SetPathways::GetPointerSize() {
+ return paths.size() * sizeof(PathData*);
+}
+
+PathDataMM** SetPathwaysMM::GetPointer() {
+ return paths.data();
+}
+size_t SetPathwaysMM::GetPointerSize() {
+ return paths.size() * sizeof(PathData*);
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetPathways.h b/mm/2s2h/resource/type/scenecommand/SetPathways.h
new file mode 100644
index 000000000..e72cdcb29
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetPathways.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <memory>
+#include "Resource.h"
+#include "SceneCommand.h"
+// #include <libultraship/libultra/types.h>
+#include "2s2h/resource/type/Path.h"
+
+namespace LUS {
+
+class SetPathways : public SceneCommand<PathData*> {
+ public:
+ using SceneCommand::SceneCommand;
+
+ PathData** GetPointer();
+ size_t GetPointerSize();
+
+ uint32_t numPaths;
+ std::vector<PathData*> paths;
+};
+
+class SetPathwaysMM : public SceneCommand<PathDataMM*> {
+ public:
+ using SceneCommand::SceneCommand;
+
+ PathDataMM** GetPointer();
+ size_t GetPointerSize();
+
+ uint32_t numPaths;
+ std::vector<PathDataMM*> paths;
+};
+
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetRoomBehavior.cpp b/mm/2s2h/resource/type/scenecommand/SetRoomBehavior.cpp
new file mode 100644
index 000000000..deababc87
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetRoomBehavior.cpp
@@ -0,0 +1,11 @@
+#include "SetRoomBehavior.h"
+
+namespace LUS {
+RoomBehavior* SetRoomBehavior::GetPointer() {
+ return &roomBehavior;
+}
+
+size_t SetRoomBehavior::GetPointerSize() {
+ return sizeof(RoomBehavior);
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetRoomBehavior.h b/mm/2s2h/resource/type/scenecommand/SetRoomBehavior.h
new file mode 100644
index 000000000..c2a28176e
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetRoomBehavior.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <memory>
+#include "Resource.h"
+#include "SceneCommand.h"
+#include <libultraship/libultra/types.h>
+
+namespace LUS {
+typedef struct {
+ int8_t gameplayFlags;
+ int32_t gameplayFlags2;
+} RoomBehavior;
+
+class SetRoomBehavior : public SceneCommand<RoomBehavior> {
+ public:
+ using SceneCommand::SceneCommand;
+
+ RoomBehavior* GetPointer();
+ size_t GetPointerSize();
+
+ RoomBehavior roomBehavior;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetRoomList.cpp b/mm/2s2h/resource/type/scenecommand/SetRoomList.cpp
new file mode 100644
index 000000000..cf8c1d61a
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetRoomList.cpp
@@ -0,0 +1,11 @@
+#include "SetRoomList.h"
+
+namespace LUS {
+RomFile* SetRoomList::GetPointer() {
+ return rooms.data();
+}
+
+size_t SetRoomList::GetPointerSize() {
+ return rooms.size() * sizeof(RomFile);
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetRoomList.h b/mm/2s2h/resource/type/scenecommand/SetRoomList.h
new file mode 100644
index 000000000..9bd0e07bc
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetRoomList.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <memory>
+#include <string>
+#include "Resource.h"
+#include "SceneCommand.h"
+#include "RomFile.h"
+#include <libultraship/libultra/types.h>
+#include <spdlog/spdlog.h>
+
+namespace LUS {
+// typedef struct {
+// /* 0x00 */ uintptr_t vromStart;
+// /* 0x04 */ uintptr_t vromEnd;
+// char* fileName;
+// } RomFile; // size = 0x8
+
+class SetRoomList : public SceneCommand<RomFile> {
+ public:
+ using SceneCommand::SceneCommand;
+
+ RomFile* GetPointer();
+ size_t GetPointerSize();
+
+ uint32_t numRooms;
+
+ std::vector<std::string> fileNames;
+ std::vector<RomFile> rooms;
+ ~SetRoomList() {
+ SPDLOG_TRACE("ROOM LIST DTOR");
+ }
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetSkyboxModifier.cpp b/mm/2s2h/resource/type/scenecommand/SetSkyboxModifier.cpp
new file mode 100644
index 000000000..9926ec01a
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetSkyboxModifier.cpp
@@ -0,0 +1,11 @@
+#include "SetSkyboxModifier.h"
+
+namespace LUS {
+SkyboxModifier* SetSkyboxModifier::GetPointer() {
+ return &modifier;
+}
+
+size_t SetSkyboxModifier::GetPointerSize() {
+ return sizeof(SkyboxModifier);
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetSkyboxModifier.h b/mm/2s2h/resource/type/scenecommand/SetSkyboxModifier.h
new file mode 100644
index 000000000..6a54879e6
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetSkyboxModifier.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <memory>
+#include "Resource.h"
+#include "SceneCommand.h"
+#include <libultraship/libultra/types.h>
+
+namespace LUS {
+typedef struct {
+ uint8_t skyboxDisabled;
+ uint8_t sunMoonDisabled;
+} SkyboxModifier;
+
+class SetSkyboxModifier : public SceneCommand<SkyboxModifier> {
+ public:
+ using SceneCommand::SceneCommand;
+
+ SkyboxModifier* GetPointer();
+ size_t GetPointerSize();
+
+ SkyboxModifier modifier;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetSkyboxSettings.cpp b/mm/2s2h/resource/type/scenecommand/SetSkyboxSettings.cpp
new file mode 100644
index 000000000..7f12d4fa3
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetSkyboxSettings.cpp
@@ -0,0 +1,11 @@
+#include "SetSkyboxSettings.h"
+
+namespace LUS {
+SkyboxSettings* SetSkyboxSettings::GetPointer() {
+ return &settings;
+}
+
+size_t SetSkyboxSettings::GetPointerSize() {
+ return sizeof(SetSkyboxSettings);
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetSkyboxSettings.h b/mm/2s2h/resource/type/scenecommand/SetSkyboxSettings.h
new file mode 100644
index 000000000..a2a9593b0
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetSkyboxSettings.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <memory>
+#include "Resource.h"
+#include "SceneCommand.h"
+#include <libultraship/libultra/types.h>
+
+namespace LUS {
+typedef struct {
+ uint8_t unk;
+ uint8_t skyboxId;
+ uint8_t weather;
+ uint8_t indoors;
+} SkyboxSettings;
+
+class SetSkyboxSettings : public SceneCommand<SkyboxSettings> {
+ public:
+ using SceneCommand::SceneCommand;
+
+ SkyboxSettings* GetPointer();
+ size_t GetPointerSize();
+
+ SkyboxSettings settings;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetSoundSettings.cpp b/mm/2s2h/resource/type/scenecommand/SetSoundSettings.cpp
new file mode 100644
index 000000000..f6f1941d1
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetSoundSettings.cpp
@@ -0,0 +1,11 @@
+#include "SetSoundSettings.h"
+
+namespace LUS {
+SoundSettings* SetSoundSettings::GetPointer() {
+ return &settings;
+}
+
+size_t SetSoundSettings::GetPointerSize() {
+ return sizeof(SoundSettings);
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetSoundSettings.h b/mm/2s2h/resource/type/scenecommand/SetSoundSettings.h
new file mode 100644
index 000000000..c4ee4668d
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetSoundSettings.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <memory>
+#include "Resource.h"
+#include "SceneCommand.h"
+#include <libultraship/libultra/types.h>
+
+namespace LUS {
+typedef struct {
+ uint8_t seqId;
+ uint8_t natureAmbienceId;
+ uint8_t reverb;
+} SoundSettings;
+
+class SetSoundSettings : public SceneCommand<SoundSettings> {
+ public:
+ using SceneCommand::SceneCommand;
+
+ SoundSettings* GetPointer();
+ size_t GetPointerSize();
+
+ SoundSettings settings;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetSpecialObjects.cpp b/mm/2s2h/resource/type/scenecommand/SetSpecialObjects.cpp
new file mode 100644
index 000000000..3887107da
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetSpecialObjects.cpp
@@ -0,0 +1,11 @@
+#include "SetSpecialObjects.h"
+
+namespace LUS {
+SpecialObjects* SetSpecialObjects::GetPointer() {
+ return &specialObjects;
+}
+
+size_t SetSpecialObjects::GetPointerSize() {
+ return sizeof(SpecialObjects);
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetSpecialObjects.h b/mm/2s2h/resource/type/scenecommand/SetSpecialObjects.h
new file mode 100644
index 000000000..78cfcd425
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetSpecialObjects.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <memory>
+#include "Resource.h"
+#include "SceneCommand.h"
+#include <libultraship/libultra/types.h>
+
+namespace LUS {
+typedef struct {
+ int8_t elfMessage;
+ int16_t globalObject;
+} SpecialObjects;
+
+class SetSpecialObjects : public SceneCommand<SpecialObjects> {
+ public:
+ using SceneCommand::SceneCommand;
+
+ SpecialObjects* GetPointer();
+ size_t GetPointerSize();
+
+ SpecialObjects specialObjects;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetStartPositionList.cpp b/mm/2s2h/resource/type/scenecommand/SetStartPositionList.cpp
new file mode 100644
index 000000000..89fb40049
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetStartPositionList.cpp
@@ -0,0 +1,11 @@
+#include "SetStartPositionList.h"
+
+namespace LUS {
+ActorEntry* SetStartPositionList::GetPointer() {
+ return startPositions.data();
+}
+
+size_t SetStartPositionList::GetPointerSize() {
+ return startPositions.size() * sizeof(ActorEntry);
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetStartPositionList.h b/mm/2s2h/resource/type/scenecommand/SetStartPositionList.h
new file mode 100644
index 000000000..cc939aca0
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetStartPositionList.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <memory>
+#include <string>
+#include "Resource.h"
+#include "SceneCommand.h"
+#include "2s2h/resource/type/scenecommand/SetActorList.h"
+// #include <libultraship/libultra/types.h>
+
+namespace LUS {
+// typedef struct {
+// /* 0x00 */ s16 id;
+// /* 0x02 */ Vec3s pos;
+// /* 0x08 */ Vec3s rot;
+// /* 0x0E */ s16 params;
+// } ActorEntry; // size = 0x10
+
+class SetStartPositionList : public SceneCommand<ActorEntry> {
+ public:
+ using SceneCommand::SceneCommand;
+
+ ActorEntry* GetPointer();
+ size_t GetPointerSize();
+
+ uint32_t numStartPositions;
+ std::vector<ActorEntry> startPositions;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetTimeSettings.cpp b/mm/2s2h/resource/type/scenecommand/SetTimeSettings.cpp
new file mode 100644
index 000000000..9a10fb022
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetTimeSettings.cpp
@@ -0,0 +1,11 @@
+#include "SetTimeSettings.h"
+
+namespace LUS {
+TimeSettings* SetTimeSettings::GetPointer() {
+ return &settings;
+}
+
+size_t SetTimeSettings::GetPointerSize() {
+ return sizeof(TimeSettings);
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetTimeSettings.h b/mm/2s2h/resource/type/scenecommand/SetTimeSettings.h
new file mode 100644
index 000000000..637398652
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetTimeSettings.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <memory>
+#include "Resource.h"
+#include "SceneCommand.h"
+#include <libultraship/libultra/types.h>
+
+namespace LUS {
+typedef struct {
+ uint8_t hour;
+ uint8_t minute;
+ uint8_t timeIncrement;
+} TimeSettings;
+
+class SetTimeSettings : public SceneCommand<TimeSettings> {
+ public:
+ using SceneCommand::SceneCommand;
+
+ TimeSettings* GetPointer();
+ size_t GetPointerSize();
+
+ TimeSettings settings;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetTransitionActorList.cpp b/mm/2s2h/resource/type/scenecommand/SetTransitionActorList.cpp
new file mode 100644
index 000000000..86387e682
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetTransitionActorList.cpp
@@ -0,0 +1,11 @@
+#include "SetTransitionActorList.h"
+
+namespace LUS {
+TransitionActorEntry* SetTransitionActorList::GetPointer() {
+ return transitionActorList.data();
+}
+
+size_t SetTransitionActorList::GetPointerSize() {
+ return transitionActorList.size() * sizeof(TransitionActorEntry);
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetTransitionActorList.h b/mm/2s2h/resource/type/scenecommand/SetTransitionActorList.h
new file mode 100644
index 000000000..bb6fc2ac6
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetTransitionActorList.h
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <memory>
+#include <string>
+#include "Resource.h"
+#include "SceneCommand.h"
+// #include <libultraship/libultra/types.h>
+#include "z64math.h"
+
+namespace LUS {
+typedef struct {
+ struct {
+ s8 room; // Room to switch to
+ s8 effects; // How the camera reacts during the transition
+ } /* 0x00 */ sides[2]; // 0 = front, 1 = back
+ /* 0x04 */ s16 id;
+ /* 0x06 */ Vec3s pos;
+ /* 0x0C */ s16 rotY;
+ /* 0x0E */ s16 params;
+} TransitionActorEntry; // size = 0x10
+
+class SetTransitionActorList : public SceneCommand<TransitionActorEntry> {
+ public:
+ using SceneCommand::SceneCommand;
+
+ TransitionActorEntry* GetPointer();
+ size_t GetPointerSize();
+
+ uint32_t numTransitionActors;
+ std::vector<TransitionActorEntry> transitionActorList;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetWindSettings.cpp b/mm/2s2h/resource/type/scenecommand/SetWindSettings.cpp
new file mode 100644
index 000000000..3aa9ce7a6
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetWindSettings.cpp
@@ -0,0 +1,11 @@
+#include "SetWindSettings.h"
+
+namespace LUS {
+WindSettings* SetWindSettings::GetPointer() {
+ return &settings;
+}
+
+size_t SetWindSettings::GetPointerSize() {
+ return sizeof(WindSettings);
+}
+} // namespace LUS
diff --git a/mm/2s2h/resource/type/scenecommand/SetWindSettings.h b/mm/2s2h/resource/type/scenecommand/SetWindSettings.h
new file mode 100644
index 000000000..e79b445ae
--- /dev/null
+++ b/mm/2s2h/resource/type/scenecommand/SetWindSettings.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+#include <memory>
+#include "Resource.h"
+#include "SceneCommand.h"
+#include <libultraship/libultra/types.h>
+
+namespace LUS {
+typedef struct {
+ int8_t windWest;
+ int8_t windVertical;
+ int8_t windSouth;
+ uint8_t windSpeed;
+} WindSettings;
+
+class SetWindSettings : public SceneCommand<WindSettings> {
+ public:
+ using SceneCommand::SceneCommand;
+
+ WindSettings* GetPointer();
+ size_t GetPointerSize();
+
+ WindSettings settings;
+};
+}; // namespace LUS
diff --git a/mm/2s2h/z_message_OTR.cpp b/mm/2s2h/z_message_OTR.cpp
new file mode 100644
index 000000000..808d9b794
--- /dev/null
+++ b/mm/2s2h/z_message_OTR.cpp
@@ -0,0 +1,87 @@
+#include "BenPort.h"
+#include <libultraship/libultraship.h>
+#include "2s2h/resource/type/Scene.h"
+#include <Utils/StringHelper.h>
+#include "global.h"
+#include "vt.h"
+#include "2s2h/resource/type/TextMM.h"
+#include <message_data_static.h>
+#include <resource/type/Text.h>
+
+// BENTODO: This needs to be cleaned up. It can probably be implemented better.
+struct MessageStruct
+{
+ uint8_t textboxType;
+ uint8_t textboxYPos;
+ uint8_t icon;
+ uint16_t nextMessageID;
+ uint16_t firstItemCost;
+ uint16_t secondItemCost;
+ uint16_t alwaysFFFF;
+};
+
+//extern "C" MessageTableEntry* sNesMessageEntryTablePtr;
+//extern "C" MessageTableEntry* sStaffMessageEntryTablePtr;
+
+MessageTableEntry* OTRMessage_LoadTable(const char* filePath, bool isNES) {
+ auto file = std::static_pointer_cast<LUS::TextMM>(LUS::Context::GetInstance()->GetResourceManager()->LoadResource(filePath));
+
+ if (file == nullptr)
+ return nullptr;
+
+ // Allocate room for an additional message
+ // OTRTODO: Should not be malloc'ing here. It's fine for now since we check elsewhere that the message table is
+ // already null.
+ MessageTableEntry* table = (MessageTableEntry*)malloc(sizeof(MessageTableEntry) * (file->messages.size() + 1));
+
+ for (size_t i = 0; i < file->messages.size(); i++) {
+ table[i].textId = file->messages[i].id;
+ table[i].typePos = (file->messages[i].textboxType << 4) | file->messages[i].textboxYPos;
+ table[i].segment = (const char*)malloc(file->messages[i].msg.size() + 11);
+
+ MessageStruct* msgStruct = (MessageStruct*)table[i].segment;
+ msgStruct->textboxType = file->messages[i].textboxType;
+ msgStruct->textboxYPos = file->messages[i].textboxYPos;
+ msgStruct->icon = file->messages[i].icon;
+ msgStruct->nextMessageID = file->messages[i].nextMessageID;
+ msgStruct->firstItemCost = file->messages[i].firstItemCost;
+ msgStruct->secondItemCost = file->messages[i].secondItemCost;
+ msgStruct->alwaysFFFF = 0xFFFF;
+
+ memcpy((void*)(&table[i].segment[11]), file->messages[i].msg.c_str(), file->messages[i].msg.size());
+
+ //table[i].segment = file->messages[i].msg.c_str();
+ table[i].msgSize = file->messages[i].msg.size() + 11;
+
+ //if (isNES && file->messages[i].id == 0xFFFC)
+ //_message_0xFFFC_nes = (char*)file->messages[i].msg.c_str();
+ }
+
+ return table;
+}
+
+extern "C" void OTRMessage_Init(PlayState* play) {
+ // OTRTODO: Added a lot of null checks here so that we don't malloc the table multiple times causing a memory leak.
+ // We really ought to fix the implementation such that we aren't malloc'ing new tables.
+ // Once we fix the implementation, remove these NULL checks.
+ //if (play->msgCtx.messageEntryTableNes == NULL) {
+ play->msgCtx.messageEntryTableNes = OTRMessage_LoadTable("text/message_data_static/message_data_static", true);
+ play->msgCtx.messageEntryTable = play->msgCtx.messageEntryTableNes;
+ //}
+
+ //if (play->msgCtx.messageTableStaff == NULL) {
+ auto file2 =
+ std::static_pointer_cast<LUS::TextMM>(LUS::Context::GetInstance()->GetResourceManager()->LoadResource(
+ "text/staff_message_data_static/staff_message_data_static"));
+ // OTRTODO: Should not be malloc'ing here. It's fine for now since we check that the message table is already null.
+ play->msgCtx.messageTableStaff = (MessageTableEntry*)malloc(sizeof(MessageTableEntry) * file2->messages.size());
+
+ for (size_t i = 0; i < file2->messages.size(); i++) {
+ play->msgCtx.messageTableStaff[i].textId = file2->messages[i].id;
+ play->msgCtx.messageTableStaff[i].typePos =
+ (file2->messages[i].textboxType << 4) | file2->messages[i].textboxYPos;
+ play->msgCtx.messageTableStaff[i].segment = file2->messages[i].msg.c_str();
+ play->msgCtx.messageTableStaff[i].msgSize = file2->messages[i].msg.size();
+ }
+ //}
+}
diff --git a/mm/2s2h/z_play_2SH.cpp b/mm/2s2h/z_play_2SH.cpp
new file mode 100644
index 000000000..2350c4dbf
--- /dev/null
+++ b/mm/2s2h/z_play_2SH.cpp
@@ -0,0 +1,80 @@
+#include "BenPort.h"
+#include <libultraship/libultraship.h>
+#include "2s2h/resource/type/Scene.h"
+#include <Utils/StringHelper.h>
+#include <Vertex.h>
+extern "C" {
+#include "global.h"
+#include "vt.h"
+#include <z64scene.h>
+}
+LUS::IResource* OTRPlay_LoadFile(PlayState* play, const char* fileName) {
+ auto res = LUS::Context::GetInstance()->GetResourceManager()->LoadResource(fileName);
+ return res.get();
+}
+
+s32 OTRScene_ExecuteCommands(PlayState* play, LUS::Scene* scene);
+
+extern "C" void OTRPlay_InitScene(PlayState* play, s32 spawn) {
+ play->curSpawn = spawn;
+ play->linkActorEntry = NULL;
+ play->actorCsCamList = NULL;
+ play->setupEntranceList = NULL;
+ play->setupExitList = NULL;
+ play->naviQuestHints = NULL;
+ play->setupPathList = NULL;
+ play->sceneMaterialAnims = NULL;
+ play->roomCtx.unk74 = NULL;
+ play->numSetupActors = 0;
+ Object_InitContext(&play->state, &play->objectCtx);
+ LightContext_Init(play, &play->lightCtx);
+ Scene_ResetTransiActorList(&play->state, &play->transitionActors);
+ Room_Init(play, &play->roomCtx);
+ gSaveContext.worldMapArea = 0;
+ OTRScene_ExecuteCommands(play, (LUS::Scene*)play->sceneSegment);
+ Play_InitEnvironment(play, play->skyboxId);
+}
+
+extern "C" void OTRPlay_SpawnScene(PlayState* play, s32 sceneId, s32 spawn) {
+ s32 pad;
+ SceneTableEntry* scene = &gSceneTable[sceneId];
+
+ scene->unk_D = 0;
+ play->loadedScene = scene;
+ play->sceneId = sceneId;
+ play->sceneConfig = scene->drawConfig;
+ std::string scenePath = StringHelper::Sprintf("scenes/nonmq/%s/%s", scene->segment.fileName, scene->segment.fileName);
+ play->sceneSegment = OTRPlay_LoadFile(play, scenePath.c_str());
+ scene->unk_D = 0;
+ gSegments[2] = (uintptr_t)VIRTUAL_TO_PHYSICAL(play->sceneSegment);
+ OTRPlay_InitScene(play, spawn);
+ Room_AllocateAndLoad(play, &play->roomCtx);
+}
+
+extern "C" s32 OTRfunc_800973FC(PlayState* play, RoomContext* roomCtx) {
+ if (roomCtx->status == 1) {
+ // if (!osRecvMesg(&roomCtx->loadQueue, NULL, OS_MESG_NOBLOCK)) {
+ if (1) {
+ roomCtx->status = 0;
+ roomCtx->curRoom.segment = roomCtx->activeRoomVram;
+ gSegments[3] = (uintptr_t)VIRTUAL_TO_PHYSICAL(roomCtx->activeRoomVram);
+
+ OTRScene_ExecuteCommands(play, (LUS::Scene*)roomCtx->curRoom.segment);
+ func_80123140(play, GET_PLAYER(play));
+ Actor_SpawnTransitionActors(play, &play->actorCtx);
+ if (((play->sceneId != SCENE_IKANA) || (roomCtx->curRoom.num != 1)) && (play->sceneId != SCENE_IKNINSIDE)) {
+ play->envCtx.lightSettingOverride = LIGHT_SETTING_OVERRIDE_NONE;
+ play->envCtx.lightBlendOverride = LIGHT_BLEND_OVERRIDE_NONE;
+ }
+ func_800FEAB0();
+ if (Environment_GetStormState(play) == STORM_STATE_OFF) {
+ Environment_StopStormNatureAmbience(play);
+ }
+ return 1;
+ }
+
+ return 0;
+ }
+
+ return 1;
+} \ No newline at end of file
diff --git a/mm/2s2h/z_scene_2SH.cpp b/mm/2s2h/z_scene_2SH.cpp
new file mode 100644
index 000000000..79945e9a8
--- /dev/null
+++ b/mm/2s2h/z_scene_2SH.cpp
@@ -0,0 +1,444 @@
+#include "BenPort.h"
+extern "C" {
+#include "z64.h"
+#include "vt.h"
+#include "global.h"
+}
+#include <libultraship/libultraship.h>
+#include <Blob.h>
+#include <memory>
+#include <cassert>
+#include <Utils/StringHelper.h>
+#include <DisplayList.h>
+#include "2s2h/resource/type/Scene.h"
+#include "2s2h/resource/type/CollisionHeader.h"
+#include "2s2h/resource/type/Cutscene.h"
+#include "2s2h/resource/type/Path.h"
+#include "2s2h/resource/type/Text.h"
+#include "2s2h/resource/type/scenecommand/SetCameraSettings.h"
+#include "2s2h/resource/type/scenecommand/SetCutscenes.h"
+#include "2s2h/resource/type/scenecommand/SetStartPositionList.h"
+#include "2s2h/resource/type/scenecommand/SetActorList.h"
+#include "2s2h/resource/type/scenecommand/SetCollisionHeader.h"
+#include "2s2h/resource/type/scenecommand/SetRoomList.h"
+#include "2s2h/resource/type/scenecommand/SetEntranceList.h"
+#include "2s2h/resource/type/scenecommand/SetSpecialObjects.h"
+#include "2s2h/resource/type/scenecommand/SetRoomBehavior.h"
+#include "2s2h/resource/type/scenecommand/SetMesh.h"
+#include "2s2h/resource/type/scenecommand/SetObjectList.h"
+#include "2s2h/resource/type/scenecommand/SetLightList.h"
+#include "2s2h/resource/type/scenecommand/SetPathways.h"
+#include "2s2h/resource/type/scenecommand/SetTransitionActorList.h"
+#include "2s2h/resource/type/scenecommand/SetSkyboxSettings.h"
+#include "2s2h/resource/type/scenecommand/SetSkyboxModifier.h"
+#include "2s2h/resource/type/scenecommand/SetTimeSettings.h"
+#include "2s2h/resource/type/scenecommand/SetWindSettings.h"
+#include "2s2h/resource/type/scenecommand/SetSoundSettings.h"
+#include "2s2h/resource/type/scenecommand/SetEchoSettings.h"
+#include "2s2h/resource/type/scenecommand/SetAlternateHeaders.h"
+#include "2s2h/resource/type/scenecommand/SetActorCutsceneList.h"
+#include "2s2h/resource/type/scenecommand/SetAnimatedMaterialList.h"
+#include "2s2h/resource/type/scenecommand/SetMinimapList.h"
+#include "2s2h/resource/type/scenecommand/SetMinimapChests.h"
+#include "2s2h/resource/type/scenecommand/SetCsCamera.h"
+
+s32 OTRScene_ExecuteCommands(PlayState* play, LUS::Scene* scene);
+
+
+void Scene_CommandSpawnList(PlayState* play, LUS::ISceneCommand* cmd) {
+ LUS::SetStartPositionList* list = (LUS::SetStartPositionList*)cmd;
+ ActorEntry* entries = (ActorEntry*)(list->GetRawPointer());
+ s32 loadedCount;
+ s16 playerObjectId;
+ void* objectPtr;
+
+ play->linkActorEntry = &entries[play->setupEntranceList[play->curSpawn].spawn];
+
+ if ((PLAYER_GET_INITMODE(play->linkActorEntry) == PLAYER_INITMODE_TELESCOPE) ||
+ ((gSaveContext.respawnFlag == 2) &&
+ (gSaveContext.respawn[RESPAWN_MODE_RETURN].playerParams == PLAYER_PARAMS(0xFF, PLAYER_INITMODE_TELESCOPE)))) {
+ // Skull Kid Object
+ Object_SpawnPersistent(&play->objectCtx, OBJECT_STK);
+ return;
+ }
+
+ loadedCount = Object_SpawnPersistent(&play->objectCtx, OBJECT_LINK_CHILD);
+ objectPtr = play->objectCtx.slots[play->objectCtx.numEntries].segment;
+ play->objectCtx.numEntries = loadedCount;
+ play->objectCtx.numPersistentEntries = loadedCount;
+ playerObjectId = gPlayerFormObjectIds[GET_PLAYER_FORM];
+ gActorOverlayTable[0].initInfo->objectId = playerObjectId;
+ Object_SpawnPersistent(&play->objectCtx, playerObjectId);
+
+ play->objectCtx.slots[play->objectCtx.numEntries].segment = objectPtr;
+}
+
+void Scene_CommandActorList(PlayState* play, LUS::ISceneCommand* cmd) {
+ LUS::SetActorList* list = (LUS::SetActorList*)cmd;
+
+ play->numSetupActors = list->numActors;
+ play->actorCtx.halfDaysBit = 0;
+ play->setupActorList = (ActorEntry*)list->GetRawPointer();
+}
+
+void Scene_CommandActorCutsceneCamList(PlayState* play, LUS::ISceneCommand* cmd) {
+ LUS::SetCsCamera* cams = (LUS::SetCsCamera*)cmd;
+
+ play->actorCsCamList = (ActorCsCamInfo*)cams->GetPointer();
+}
+
+void Scene_CommandCollisionHeader(PlayState* play, LUS::ISceneCommand* cmd) {
+ LUS::SetCollisionHeader* colHeader = (LUS::SetCollisionHeader*)cmd;
+ BgCheck_Allocate(&play->colCtx, play, (CollisionHeader*)colHeader->GetRawPointer());
+}
+
+void Scene_CommandRoomList(PlayState* play, LUS::ISceneCommand* cmd) {
+ LUS::SetRoomList* list = (LUS::SetRoomList*)cmd;
+ play->numRooms = list->numRooms;
+ play->roomList = (RomFile*)list->GetPointer();
+}
+
+void Scene_CommandWindSettings(PlayState* play, LUS::ISceneCommand* cmd) {
+ LUS::SetWindSettings* settings = (LUS::SetWindSettings*)cmd;
+
+ play->envCtx.windDirection.x = settings->settings.windWest;
+ play->envCtx.windDirection.y = settings->settings.windVertical;
+ play->envCtx.windDirection.z = settings->settings.windSouth;
+ play->envCtx.windSpeed = settings->settings.windSpeed;
+}
+
+void Scene_CommandEntranceList(PlayState* play, LUS::ISceneCommand* cmd) {
+ LUS::SetEntranceList* list = (LUS::SetEntranceList*)cmd;
+
+ play->setupEntranceList = (EntranceEntry*)list->GetRawPointer();
+}
+
+void Scene_CommandSpecialFiles(PlayState* play, LUS::ISceneCommand* cmd) {
+ printf("Un-implemented command %02X\n", cmd->cmdId);
+ // Unused according to z_scene.c
+}
+
+void Scene_CommandRoomBehavior(PlayState* play, LUS::ISceneCommand* cmd) {
+ LUS::SetRoomBehavior* behavior = (LUS::SetRoomBehavior*)cmd;
+
+ play->roomCtx.curRoom.behaviorType1 = behavior->roomBehavior.gameplayFlags;
+ play->roomCtx.curRoom.behaviorType2 = behavior->roomBehavior.gameplayFlags2 & 0xFF;
+ play->roomCtx.curRoom.lensMode = (behavior->roomBehavior.gameplayFlags2 >> 8) & 1;
+ play->msgCtx.unk12044 = (behavior->roomBehavior.gameplayFlags2 >> 0xA) & 1;
+ play->roomCtx.curRoom.enablePosLights = (behavior->roomBehavior.gameplayFlags2 >> 0xB) & 1;
+ play->envCtx.stormState = (behavior->roomBehavior.gameplayFlags2 >> 0xC) & 1;
+}
+
+void Scene_Command09(PlayState* play, LUS::ISceneCommand* cmd) {
+ // Empty in z_scene.c
+}
+
+void Scene_CommandMesh(PlayState* play, LUS::ISceneCommand* cmd) {
+ LUS::SetMesh* mesh = (LUS::SetMesh*)cmd;
+
+ play->roomCtx.curRoom.roomShape = (RoomShape*)mesh->GetRawPointer();
+}
+
+void Scene_CommandObjectList(PlayState* play, LUS::ISceneCommand* cmd) {
+ LUS::SetObjectList* objList = (LUS::SetObjectList*)cmd;
+
+ s16* entry = (s16*)objList->GetRawPointer();
+
+ for (unsigned int i = 0; i < objList->objects.size(); i++) {
+ bool alreadyIncluded = true;
+
+ for (unsigned int j = 0; j < play->objectCtx.numEntries; j++) {
+ if (play->objectCtx.slots[j].id == objList->objects[i]) {
+ alreadyIncluded = true;
+ break;
+ }
+ }
+
+ if (!alreadyIncluded) {
+ play->objectCtx.slots[play->objectCtx.numEntries++].id = objList->objects[i];
+ Actor_KillAllWithMissingObject(play, &play->actorCtx);
+ }
+ }
+}
+
+void Scene_CommandLightList(PlayState* play, LUS::ISceneCommand* cmd) {
+ LUS::SetLightList* lightList = (LUS::SetLightList*)cmd;
+
+ for (unsigned int i = 0; i < lightList->numLights; i++) {
+ LightContext_InsertLight(play, &play->lightCtx, (LightInfo*)&lightList->lightList[i]);
+ }
+}
+
+void Scene_CommandPathList(PlayState* play, LUS::ISceneCommand* cmd) {
+ LUS::SetPathwaysMM* paths = (LUS::SetPathwaysMM*)cmd;
+
+ play->setupPathList = (Path*)paths->GetPointer()[0];
+}
+
+void Scene_CommandTransiActorList(PlayState* play, LUS::ISceneCommand* cmd) {
+ LUS::SetTransitionActorList* list = (LUS::SetTransitionActorList*)cmd;
+
+ play->transitionActors.count = list->numTransitionActors;
+ play->transitionActors.list = (TransitionActorEntry*)list->GetRawPointer();
+}
+
+void Scene_CommandEnvLightSettings(PlayState* play, LUS::ISceneCommand* cmd) {
+ play->envCtx.lightSettingsList = (EnvLightSettings*)cmd->GetRawPointer();
+}
+
+void Scene_CommandTimeSettings(PlayState* play, LUS::ISceneCommand* cmd) {
+ LUS::SetTimeSettings* settings = (LUS::SetTimeSettings*)cmd;
+
+ if ((settings->settings.hour != 0xFF) && (settings->settings.minute != 0xFF)) {
+ gSaveContext.skyboxTime = gSaveContext.save.time = CLOCK_TIME_ALT2_F(settings->settings.hour, settings->settings.minute);
+ }
+
+ if (settings->settings.timeIncrement != 0xFF) {
+ play->envCtx.sceneTimeSpeed = settings->settings.timeIncrement;
+ } else {
+ play->envCtx.sceneTimeSpeed = 0;
+ }
+
+ // Increase time speed during first cycle
+ if ((gSaveContext.save.saveInfo.inventory.items[SLOT_OCARINA] == ITEM_NONE) && (play->envCtx.sceneTimeSpeed != 0)) {
+ play->envCtx.sceneTimeSpeed = 5;
+ }
+
+ if (gSaveContext.sunsSongState == SUNSSONG_INACTIVE) {
+ R_TIME_SPEED = play->envCtx.sceneTimeSpeed;
+ }
+
+ play->envCtx.sunPos.x = -(Math_SinS(((void)0, gSaveContext.save.time) - CLOCK_TIME(12, 0)) * 120.0f) * 25.0f;
+ play->envCtx.sunPos.y = (Math_CosS(((void)0, gSaveContext.save.time) - CLOCK_TIME(12, 0)) * 120.0f) * 25.0f;
+ play->envCtx.sunPos.z = (Math_CosS(((void)0, gSaveContext.save.time) - CLOCK_TIME(12, 0)) * 20.0f) * 25.0f;
+
+ if ((play->envCtx.sceneTimeSpeed == 0) && (gSaveContext.save.cutsceneIndex < 0xFFF0)) {
+ gSaveContext.skyboxTime = gSaveContext.save.time;
+
+ if ((gSaveContext.skyboxTime >= CLOCK_TIME(4, 0)) && (gSaveContext.skyboxTime < CLOCK_TIME(6, 30))) {
+ gSaveContext.skyboxTime = CLOCK_TIME(5, 0);
+ } else if ((gSaveContext.skyboxTime >= CLOCK_TIME(6, 30)) && (gSaveContext.skyboxTime < CLOCK_TIME(8, 0))) {
+ gSaveContext.skyboxTime = CLOCK_TIME(8, 0);
+ } else if ((gSaveContext.skyboxTime >= CLOCK_TIME(16, 0)) && (gSaveContext.skyboxTime < CLOCK_TIME(17, 0))) {
+ gSaveContext.skyboxTime = CLOCK_TIME(17, 0);
+ } else if ((gSaveContext.skyboxTime >= CLOCK_TIME(18, 0)) && (gSaveContext.skyboxTime < CLOCK_TIME(19, 0))) {
+ gSaveContext.skyboxTime = CLOCK_TIME(19, 0);
+ }
+ }
+}
+
+void Scene_CommandSkyboxSettings(PlayState* play, LUS::ISceneCommand* cmd) {
+ LUS::SetSkyboxSettings* settings = (LUS::SetSkyboxSettings*)cmd;
+
+ play->skyboxId = settings->settings.skyboxId & 3;
+ // BENTODO z_scene.c reads from skyboxSettings.skyboxConfig not weather
+ // Settings uses names from OOT
+ play->envCtx.skyboxConfig = play->envCtx.changeSkyboxNextConfig = settings->settings.weather;
+ play->envCtx.lightMode = settings->settings.indoors;
+ //Scene_LoadAreaTextures(play, settings->settings.)
+}
+
+void Scene_CommandSkyboxDisables(PlayState* play, LUS::ISceneCommand* cmd) {
+ LUS::SetSkyboxModifier* mod = (LUS::SetSkyboxModifier*)cmd;
+
+ play->envCtx.skyboxDisabled = mod->modifier.skyboxDisabled;
+ play->envCtx.sunDisabled = mod->modifier.sunMoonDisabled;
+}
+
+void Scene_CommandExitList(PlayState* play, LUS::ISceneCommand* cmd) {
+ play->setupExitList = (u16*)cmd->GetRawPointer();
+}
+
+void Scene_CommandSoundSettings(PlayState* play, LUS::ISceneCommand* cmd) {
+ LUS::SetSoundSettings* settings = (LUS::SetSoundSettings*)cmd;
+
+ play->sequenceCtx.seqId = settings->settings.seqId;
+ play->sequenceCtx.ambienceId = settings->settings.natureAmbienceId;
+
+ if(gSaveContext.seqId == (u8)NA_BGM_DISABLED || AudioSeq_GetActiveSeqId(SEQ_PLAYER_BGM_MAIN) == NA_BGM_FINAL_HOURS) {
+ Audio_SetSpec(settings->settings.reverb); // BENTODO Verify if this should be reverb
+ }
+ }
+
+void Scene_CommandEchoSetting(PlayState* play, LUS::ISceneCommand* cmd) {
+ LUS::SetEchoSettings* echo = (LUS::SetEchoSettings*)cmd;
+ play->roomCtx.curRoom.echo = echo->settings.echo;
+}
+
+void Scene_CommandCutsceneScriptList(PlayState* play, LUS::ISceneCommand* cmd) {
+ LUS::SetCutscenesMM* cs = (LUS::SetCutscenesMM*)cmd;
+ play->csCtx.scriptListCount = cs->entries.size();
+ // BENTODO do this the right way with get pointer
+ play->csCtx.scriptList = (CutsceneScriptEntry*)cs->entries.data();
+
+}
+
+void Scene_CommandAltHeaderList(PlayState* play, LUS::ISceneCommand* cmd) {
+ LUS::SetAlternateHeaders* headers = (LUS::SetAlternateHeaders*)cmd;
+
+ if (gSaveContext.sceneLayer != 0) {
+ LUS::Scene* desiredHeader =
+ std::static_pointer_cast<LUS::Scene>(headers->headers[gSaveContext.sceneLayer - 1]).get();
+
+ if (desiredHeader != nullptr) {
+ OTRScene_ExecuteCommands(play, desiredHeader);
+ // z_scene does (cmd + 1)->base.code = 0x14;
+ }
+ }
+}
+
+void Scene_CommandSetRegionVisitedFlag(PlayState* play, LUS::ISceneCommand* cmd) {
+ s16 j = 0;
+ s16 i = 0;
+
+ while (true) {
+ if (gSceneIdsPerRegion[i][j] == 0xFFFF) {
+ i++;
+ j = 0;
+
+ if (i == REGION_MAX) {
+ break;
+ }
+ }
+
+ if (play->sceneId == gSceneIdsPerRegion[i][j]) {
+ break;
+ }
+
+ j++;
+ }
+
+ if (i < REGION_MAX) {
+ gSaveContext.save.saveInfo.regionsVisited =
+ (gBitFlags[i] | gSaveContext.save.saveInfo.regionsVisited) | gSaveContext.save.saveInfo.regionsVisited;
+ }
+}
+
+void Scene_CommandAnimatedMaterials(PlayState* play, LUS::ISceneCommand* cmd) {
+ LUS::SetAnimatedMaterialList* list = (LUS::SetAnimatedMaterialList*)cmd;
+ play->sceneMaterialAnims = (AnimatedMaterial*)list->mat;
+}
+
+void Scene_CommandCutsceneList(PlayState* play, LUS::ISceneCommand* cmd) {
+ LUS::SetActorCutsceneList* list = (LUS::SetActorCutsceneList*)cmd;
+
+ CutsceneManager_Init(play, (ActorCutscene*)list->GetPointer(), list->numEntries);
+}
+
+void Scene_CommandMiniMap(PlayState* play, LUS::ISceneCommand* cmd) {
+ LUS::SetMinimapList* list = (LUS::SetMinimapList*)cmd;
+
+ MapDisp_Init(play);
+
+ func_8010549C(play, list->GetPointer());
+}
+
+void Scene_Command1D(PlayState* play, LUS::ISceneCommand* cmd) {
+
+}
+
+void Scene_CommandMiniMapCompassInfo(PlayState* play, LUS::ISceneCommand* cmd) {
+ LUS::SetMinimapChests* chests = (LUS::SetMinimapChests*)cmd;
+
+ func_8010565C(play, chests->chests.size(), chests->GetPointer());
+}
+
+void (*sSceneCmdHandlersOTR[SCENE_CMD_MAX])(PlayState*, LUS::ISceneCommand*) = {
+ Scene_CommandSpawnList, // SCENE_CMD_ID_SPAWN_LIST
+ Scene_CommandActorList, // SCENE_CMD_ID_ACTOR_LIST
+ Scene_CommandActorCutsceneCamList, // SCENE_CMD_ID_ACTOR_CUTSCENE_CAM_LIST
+ Scene_CommandCollisionHeader, // SCENE_CMD_ID_COL_HEADER
+ Scene_CommandRoomList, // SCENE_CMD_ID_ROOM_LIST
+ Scene_CommandWindSettings, // SCENE_CMD_ID_WIND_SETTINGS
+ Scene_CommandEntranceList, // SCENE_CMD_ID_ENTRANCE_LIST
+ Scene_CommandSpecialFiles, // SCENE_CMD_ID_SPECIAL_FILES
+ Scene_CommandRoomBehavior, // SCENE_CMD_ID_ROOM_BEHAVIOR
+ Scene_Command09, // SCENE_CMD_ID_UNK_09
+ Scene_CommandMesh, // SCENE_CMD_ID_ROOM_SHAPE
+ Scene_CommandObjectList, // SCENE_CMD_ID_OBJECT_LIST
+ Scene_CommandLightList, // SCENE_CMD_ID_LIGHT_LIST
+ Scene_CommandPathList, // SCENE_CMD_ID_PATH_LIST
+ Scene_CommandTransiActorList, // SCENE_CMD_ID_TRANSI_ACTOR_LIST
+ Scene_CommandEnvLightSettings, // SCENE_CMD_ID_ENV_LIGHT_SETTINGS
+ Scene_CommandTimeSettings, // SCENE_CMD_ID_TIME_SETTINGS
+ Scene_CommandSkyboxSettings, // SCENE_CMD_ID_SKYBOX_SETTINGS
+ Scene_CommandSkyboxDisables, // SCENE_CMD_ID_SKYBOX_DISABLES
+ Scene_CommandExitList, // SCENE_CMD_ID_EXIT_LIST
+ NULL, // SCENE_CMD_ID_END
+ Scene_CommandSoundSettings, // SCENE_CMD_ID_SOUND_SETTINGS
+ Scene_CommandEchoSetting, // SCENE_CMD_ID_ECHO_SETTINGS
+ Scene_CommandCutsceneScriptList, // SCENE_CMD_ID_CUTSCENE_SCRIPT_LIST
+ Scene_CommandAltHeaderList, // SCENE_CMD_ID_ALTERNATE_HEADER_LIST
+ Scene_CommandSetRegionVisitedFlag, // SCENE_CMD_ID_SET_REGION_VISITED
+ Scene_CommandAnimatedMaterials, // SCENE_CMD_ID_ANIMATED_MATERIAL_LIST
+ Scene_CommandCutsceneList, // SCENE_CMD_ID_ACTOR_CUTSCENE_LIST
+ Scene_CommandMiniMap, // SCENE_CMD_ID_MINIMAP_INFO
+ Scene_Command1D, // SCENE_CMD_ID_UNUSED_1D
+ Scene_CommandMiniMapCompassInfo, // SCENE_CMD_ID_MINIMAP_COMPASS_ICON_INFO
+};
+
+s32 OTRScene_ExecuteCommands(PlayState* play, LUS::Scene* scene) {
+ LUS::SceneCommandID cmdCode;
+
+ for (int i = 0; i < scene->commands.size(); i++) {
+ auto sceneCmd = scene->commands[i];
+
+ if (sceneCmd == nullptr) // UH OH
+ continue;
+
+ cmdCode = sceneCmd->cmdId;
+ // osSyncPrintf("*** Scene_Word = { code=%d, data1=%02x, data2=%04x } ***\n", cmdCode, sceneCmd->base.data1, sceneCmd->base.data2);
+ //SPDLOG_TRACE("CMD {:X}", cmdCode);
+ if ((int)cmdCode == SCENE_CMD_ID_END) {
+ break;
+ } else if (cmdCode == LUS::SceneCommandID::SetCutscenesMM)
+ cmdCode = LUS::SceneCommandID::SetCutscenes;
+
+ //if ((int)cmdCode <= 0x19)
+ sSceneCmdHandlersOTR[(int)cmdCode](play, sceneCmd.get());
+
+
+ // sceneCmd++;
+ }
+ return 0;
+}
+
+
+std::shared_ptr<LUS::IResource> GetResourceByNameHandlingMQ(const char* path);
+
+extern "C" s32 OTRfunc_8009728C(PlayState* play, RoomContext* roomCtx, s32 roomNum) {
+
+ u32 size;
+
+ if (roomCtx->status == 0) {
+ roomCtx->prevRoom = roomCtx->curRoom;
+ roomCtx->curRoom.num = roomNum;
+ roomCtx->curRoom.segment = NULL;
+ roomCtx->status = 1;
+
+ //assert(roomNum < play->numRooms);
+
+ if (roomNum >= play->numRooms)
+ return 0; // UH OH
+
+ size = play->roomList[roomNum].vromEnd - play->roomList[roomNum].vromStart;
+ //roomCtx->activeRoomVram =
+ // (void*)((uintptr_t)roomCtx->roomMemPages[roomCtx->activeMemPage] - ((size + 8) * roomCtx->activeMemPage + 7));
+
+ // DmaMgr_SendRequest2(&roomCtx->dmaRequest, roomCtx->unk_34, play->roomList[roomNum].vromStart, size, 0,
+ //&roomCtx->loadQueue, NULL, __FILE__, __LINE__);
+ printf("File Name %s\n", play->roomList[roomNum].fileName);
+ auto roomData =
+ std::static_pointer_cast<LUS::Scene>(GetResourceByNameHandlingMQ(play->roomList[roomNum].fileName));
+ roomCtx->status = 1;
+ roomCtx->activeRoomVram = roomData.get();
+
+ roomCtx->activeMemPage ^= 1;
+
+ //SPDLOG_INFO("Room Init - curRoom.num: {0:#x}", roomCtx->curRoom.num);
+
+ return 1;
+ }
+
+ return 0;
+} \ No newline at end of file
diff --git a/mm/CMakeLists.txt b/mm/CMakeLists.txt
new file mode 100644
index 000000000..0a2e1a0e0
--- /dev/null
+++ b/mm/CMakeLists.txt
@@ -0,0 +1,822 @@
+cmake_minimum_required(VERSION 3.16.0 FATAL_ERROR)
+
+set(CMAKE_SYSTEM_VERSION 10.0 CACHE STRING "" FORCE)
+
+project(mm LANGUAGES C CXX)
+set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use")
+
+if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
+ enable_language(OBJCXX)
+ set(CMAKE_OBJC_FLAGS "${CMAKE_OBJC_FLAGS} -fobjc-arc")
+ set(CMAKE_OBJCXX_FLAGS "${CMAKE_OBJCXX_FLAGS} -fobjc-arc")
+endif()
+
+set (BUILD_UTILS OFF CACHE STRING "no utilities")
+set (BUILD_SHARED_LIBS OFF CACHE STRING "install/link shared instead of static libs")
+
+################################################################################
+# Set target arch type if empty. Visual studio solution generator provides it.
+################################################################################
+if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
+ if(NOT CMAKE_VS_PLATFORM_NAME)
+ set(CMAKE_VS_PLATFORM_NAME "x64")
+ endif()
+ message("${CMAKE_VS_PLATFORM_NAME} architecture in use")
+
+ if(NOT ("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "x64"
+ OR "${CMAKE_VS_PLATFORM_NAME}" STREQUAL "Win32"))
+ message(FATAL_ERROR "${CMAKE_VS_PLATFORM_NAME} arch is not supported!")
+ endif()
+endif()
+
+################################################################################
+# Global configuration types
+################################################################################
+set(CMAKE_CONFIGURATION_TYPES
+ "Debug"
+ "Release"
+ CACHE STRING "" FORCE
+)
+
+################################################################################
+# Global compiler options
+################################################################################
+if(MSVC)
+ # remove default flags provided with CMake for MSVC
+ set(CMAKE_C_FLAGS "")
+ set(CMAKE_C_FLAGS_DEBUG "")
+ set(CMAKE_C_FLAGS_RELEASE "")
+ set(CMAKE_CXX_FLAGS "")
+ set(CMAKE_CXX_FLAGS_DEBUG "")
+ set(CMAKE_CXX_FLAGS_RELEASE "")
+endif()
+
+################################################################################
+# Global linker options
+################################################################################
+if(MSVC)
+ # remove default flags provided with CMake for MSVC
+ set(CMAKE_EXE_LINKER_FLAGS "")
+ set(CMAKE_MODULE_LINKER_FLAGS "")
+ set(CMAKE_SHARED_LINKER_FLAGS "")
+ set(CMAKE_STATIC_LINKER_FLAGS "")
+ set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS}")
+ set(CMAKE_MODULE_LINKER_FLAGS_DEBUG "${CMAKE_MODULE_LINKER_FLAGS}")
+ set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS}")
+ set(CMAKE_STATIC_LINKER_FLAGS_DEBUG "${CMAKE_STATIC_LINKER_FLAGS}")
+ set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS}")
+ set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS}")
+ set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS}")
+ set(CMAKE_STATIC_LINKER_FLAGS_RELEASE "${CMAKE_STATIC_LINKER_FLAGS}")
+endif()
+
+################################################################################
+# Common utils
+################################################################################
+include(CMake/Utils.cmake)
+
+################################################################################
+# Additional Global Settings(add specific info there)
+################################################################################
+include(CMake/GlobalSettingsInclude.cmake OPTIONAL)
+
+################################################################################
+# Use solution folders feature
+################################################################################
+set_property(GLOBAL PROPERTY USE_FOLDERS ON)
+
+################################################################################
+# Sub-projects
+################################################################################
+if (NOT TARGET libultraship)
+ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../libultraship ${CMAKE_BINARY_DIR}/libultraship)
+endif()
+
+if (NOT TARGET ZAPDUtils)
+ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../ZAPDTR/ZAPDUtils ${CMAKE_BINARY_DIR}/ZAPDUtils)
+endif()
+
+if (NOT TARGET ZAPDLib)
+ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../ZAPDTR/ZAPD ${CMAKE_BINARY_DIR}/ZAPD)
+endif()
+
+set(PROJECT_NAME mm)
+
+################################################################################
+# Sources
+################################################################################
+configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/src/boot/build.c.in ${CMAKE_BINARY_DIR}/build.c @ONLY)
+#configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/src/boot/properties.h.in ${CMAKE_CURRENT_SOURCE_DIR}/properties.h @ONLY)
+
+#set(Header_Files "resource.h")
+source_group("headers" FILES ${Header_Files})
+
+# include {{{
+file(GLOB Header_Files__include "include/*.h" "include/*.inc")
+list(APPEND Header_Files__include ${CMAKE_CURRENT_SOURCE_DIR}/include/libc/stdarg.h)
+list(REMOVE_ITEM Header_Files__include ${CMAKE_CURRENT_SOURCE_DIR}/include/bgm.h)
+list(REMOVE_ITEM Header_Files__include ${CMAKE_CURRENT_SOURCE_DIR}/include/math_n64.h)
+list(REMOVE_ITEM Header_Files__include ${CMAKE_CURRENT_SOURCE_DIR}/include/stdbool_n64.h)
+list(REMOVE_ITEM Header_Files__include ${CMAKE_CURRENT_SOURCE_DIR}/include/stddef_n64.h)
+list(REMOVE_ITEM Header_Files__include ${CMAKE_CURRENT_SOURCE_DIR}/include/stdlib_n64.h)
+list(REMOVE_ITEM Header_Files__include ${CMAKE_CURRENT_SOURCE_DIR}/include/ultra64.h)
+source_group("include" FILES ${Header_Files__include})
+# }}}
+
+# m (root)
+file(GLOB soh__ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "2s2h/*.c" "2s2h/*.cpp" "2s2h/*.h")
+source_group("2s2h" FILES ${soh__})
+
+file(GLOB_RECURSE libultra_headers RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ "include/PR/*.h"
+)
+
+if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
+ set_source_files_properties(2s2h/BenPort.cpp PROPERTIES COMPILE_FLAGS "/utf-8")
+endif()
+
+# soh/config {{{
+#file(GLOB_RECURSE soh__config RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+# "soh/config/*.h"
+# "soh/config/*.cpp"
+#)
+# }}}
+
+# soh/enhancements {{{
+#file(GLOB_RECURSE soh__Enhancements RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+# "soh/Enhancements/*.c"
+# "soh/Enhancements/*.cpp"
+# "soh/Enhancements/*.h"
+# "soh/Enhancements/*.hpp"
+# "soh/Enhancements/*_extern.inc"
+# "soh/Enhancements/*.mm"
+#)
+
+#list(REMOVE_ITEM soh__Enhancements "soh/Enhancements/gamecommand.h")
+#list(FILTER soh__Enhancements EXCLUDE REGEX "soh/Enhancements/gfx.*")
+
+# handle crowd control removals
+#list(REMOVE_ITEM soh__Enhancements "soh/Enhancements/crowd-control/soh.cs")
+#list(REMOVE_ITEM soh__Enhancements "soh/Enhancements/crowd-control/soh.ccpak")
+#if (!BUILD_CROWD_CONTROL)
+# list(FILTER soh__Enhancements EXCLUDE REGEX "soh/Enhancements/crowd-control/*")
+#endif()
+
+# handle speechsynthesizer removals
+#if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
+# list(FILTER soh__Enhancements EXCLUDE REGEX "soh/Enhancements/speechsynthesizer/Darwin*")
+#elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
+# list(FILTER soh__Enhancements EXCLUDE REGEX "soh/Enhancements/speechsynthesizer/SAPI*")
+#else()
+# list(FILTER soh__Enhancements EXCLUDE REGEX "soh/Enhancements/speechsynthesizer/(Darwin|SAPI).*")
+#endif()
+
+#source_group("soh\\Enhancements" REGULAR_EXPRESSION "soh/Enhancements/*")
+#source_group("soh\\Enhancements\\audio" REGULAR_EXPRESSION "soh/Enhancements/audio/*")
+#source_group("soh\\Enhancements\\controls" REGULAR_EXPRESSION "soh/Enhancements/controls/*")
+#source_group("soh\\Enhancements\\cosmetics" REGULAR_EXPRESSION "soh/Enhancements/cosmetics/*")
+#source_group("soh\\Enhancements\\crowd-control" REGULAR_EXPRESSION "soh/Enhancements/crowd-control/*")
+#source_group("soh\\Enhancements\\custom-message" REGULAR_EXPRESSION "soh/Enhancements/custom-message/*")
+#source_group("soh\\Enhancements\\debugger" REGULAR_EXPRESSION "soh/Enhancements/debugger/*")
+#source_group("soh\\Enhancements\\game-interactor" REGULAR_EXPRESSION "soh/Enhancements/game-interactor/*")
+#source_group("soh\\Enhancements\\item-tables" REGULAR_EXPRESSION "soh/Enhancements/item-tables/*")
+#source_group("soh\\Enhancements\\randomizer" REGULAR_EXPRESSION "soh/Enhancements/randomizer/*")
+#source_group("soh\\Enhancements\\randomizer\\3drando" REGULAR_EXPRESSION "soh/Enhancements/randomizer/3drando/*")
+#source_group("soh\\Enhancements\\randomizer\\3drando\\hint_list" REGULAR_EXPRESSION "soh/Enhancements/randomizer/3drando/hint_list/*")
+#source_group("soh\\Enhancements\\randomizer\\3drando\\location_access" REGULAR_EXPRESSION "soh/Enhancements/randomizer/3drando/location_access/*")
+#source_group("soh\\Enhancements\\speechsynthesizer" REGULAR_EXPRESSION "soh/Enhancements/speechsynthesizer/*")
+#source_group("soh\\Enhancements\\tts" REGULAR_EXPRESSION "soh/Enhancements/tts/*")
+
+#if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
+# set_source_files_properties(soh/Enhancements/tts/tts.cpp PROPERTIES COMPILE_FLAGS "/utf-8")
+#endif()
+# }}}
+
+if(NOT CMAKE_SYSTEM_NAME MATCHES "NintendoSwitch|CafeOS")
+ # 2s2h/Extractor {{{
+ file(GLOB_RECURSE soh__Extractor RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ "2s2h/Extractor/*.c"
+ "2s2h/Extractor/*.cpp"
+ "2s2h/Extractor/*.h"
+ "2s2h/Extractor/*.hpp"
+ )
+ # }}}
+else()
+ file(GLOB_RECURSE soh__Extractor RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ "2s2h/Extractor/*.h"
+ "2s2h/Extractor/*.hpp"
+ )
+# }}}
+endif()
+
+# 2s2h/resource {{{
+file(GLOB_RECURSE soh__Resource RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "2s2h/resource/*.cpp" "2s2h/resource/*.h")
+
+source_group("2s2h\\resource\\type" REGULAR_EXPRESSION "2s2h/resource/type/*")
+source_group("2s2h\\resource\\type\\scenecommand" REGULAR_EXPRESSION "2s2h/resource/type/scenecommand/*")
+source_group("2s2h\\resource\\importer" REGULAR_EXPRESSION "2s2h/resource/importer/*")
+source_group("2s2h\\resource\\importer\\scenecommand" REGULAR_EXPRESSION "2s2h/resource/importer/scenecommand/*")
+# }}}
+
+# src (decomp) {{{
+file(GLOB_RECURSE src__ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.c" "src/*.h")
+
+list(APPEND src__ ${CMAKE_BINARY_DIR}/build.c)
+#list(APPEND src__ ${CMAKE_CURRENT_SOURCE_DIR}/properties.h)
+#list(APPEND src__ ${CMAKE_CURRENT_SOURCE_DIR}/Resource.rc)
+list(FILTER src__ EXCLUDE REGEX "src/dmadata/*")
+list(FILTER src__ EXCLUDE REGEX "src/elf_message/*")
+list(FILTER src__ EXCLUDE REGEX "src/libultra/io/*")
+list(FILTER src__ EXCLUDE REGEX "src/libultra/libc/*")
+list(FILTER src__ EXCLUDE REGEX "src/libultra/os/*")
+list(FILTER src__ EXCLUDE REGEX "src/libultra/rmon/*")
+list(FILTER src__ EXCLUDE REGEX "src/libultra/*")
+#list(APPEND src__ "src/libultra/libc/sprintf.c")
+#list(REMOVE_ITEM src__ "src/libultra/gu/cosf.c")
+#list(REMOVE_ITEM src__ "src/libultra/gu/lookat.c")
+#list(REMOVE_ITEM src__ "src/libultra/gu/lookathil.c")
+#list(REMOVE_ITEM src__ "src/libultra/gu/perspective.c")
+#list(REMOVE_ITEM src__ "src/libultra/gu/position.c")
+#list(REMOVE_ITEM src__ "src/libultra/gu/sinf.c")
+#list(REMOVE_ITEM src__ "src/libultra/gu/sinf.c")
+#list(REMOVE_ITEM src__ "src/libultra/gu/sqrtf.c")
+#list(REMOVE_ITEM src__ "src/libultra/gu/us2dex.c")
+
+source_group("src" REGULAR_EXPRESSION "src/*")
+source_group("src\\build" FILES ${CMAKE_BINARY_DIR}/build.c)
+source_group("src\\boot" REGULAR_EXPRESSION "src/boot/*")
+source_group("src\\buffers" REGULAR_EXPRESSION "src/buffers/*")
+source_group("src\\code" REGULAR_EXPRESSION "src/code/*")
+#source_group("src\\libultra" REGULAR_EXPRESSION "src/libultra/*")
+source_group("src\\overlays\\actors" REGULAR_EXPRESSION "src/overlays/actors/*")
+source_group("src\\overlays\\effects" REGULAR_EXPRESSION "src/overlays/effects/*")
+source_group("src\\overlays\\fbdemos" REGULAR_EXPRESSION "src/overlays/fbdemos/*")
+source_group("src\\overlays\\gamestates" REGULAR_EXPRESSION "src/overlays/gamestates/*")
+source_group("src\\overlays\\misc" REGULAR_EXPRESSION "src/overlays/misc/*")
+# }}}
+
+set(ALL_FILES
+ ${Header_Files}
+ ${Header_Files__include}
+ ${soh__}
+ ${libultra_headers}
+# ${soh__config}
+# ${soh__Enhancements}
+ ${soh__Extractor}
+ ${soh__Resource}
+ ${src__}
+)
+
+################################################################################
+# Target
+################################################################################
+add_executable(${PROJECT_NAME} ${ALL_FILES})
+
+if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
+use_props(${PROJECT_NAME} "${CMAKE_CONFIGURATION_TYPES}" "${DEFAULT_CXX_PROPS}")
+endif()
+
+set(ROOT_NAMESPACE 2s2h)
+
+if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
+ set_target_properties(${PROJECT_NAME} PROPERTIES
+ VS_GLOBAL_KEYWORD "Win32Proj"
+ )
+ if("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "x64")
+ set_target_properties(${PROJECT_NAME} PROPERTIES
+ INTERPROCEDURAL_OPTIMIZATION_RELEASE "TRUE"
+ )
+ elseif("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "Win32")
+ set_target_properties(${PROJECT_NAME} PROPERTIES
+ INTERPROCEDURAL_OPTIMIZATION_RELEASE "TRUE"
+ )
+ endif()
+elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
+ set_target_properties(${PROJECT_NAME} PROPERTIES
+ XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES
+ OUTPUT_NAME "2s2h-macos"
+ )
+elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ set_target_properties(${PROJECT_NAME} PROPERTIES
+ OUTPUT_NAME "2s2h.elf"
+ )
+endif()
+################################################################################
+# MSVC runtime library
+################################################################################
+if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
+ get_property(MSVC_RUNTIME_LIBRARY_DEFAULT TARGET ${PROJECT_NAME} PROPERTY MSVC_RUNTIME_LIBRARY)
+ if("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "x64")
+ string(CONCAT "MSVC_RUNTIME_LIBRARY_STR"
+ $<$<CONFIG:Debug>:
+ MultiThreadedDebug
+ >
+ $<$<CONFIG:Release>:
+ MultiThreaded
+ >
+ $<$<NOT:$<OR:$<CONFIG:Debug>,$<CONFIG:Release>>>:${MSVC_RUNTIME_LIBRARY_DEFAULT}>
+ )
+ elseif("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "Win32")
+ string(CONCAT "MSVC_RUNTIME_LIBRARY_STR"
+ $<$<CONFIG:Debug>:
+ MultiThreadedDebug
+ >
+ $<$<CONFIG:Release>:
+ MultiThreaded
+ >
+ $<$<NOT:$<OR:$<CONFIG:Debug>,$<CONFIG:Release>>>:${MSVC_RUNTIME_LIBRARY_DEFAULT}>
+ )
+ endif()
+ set_target_properties(${PROJECT_NAME} PROPERTIES MSVC_RUNTIME_LIBRARY ${MSVC_RUNTIME_LIBRARY_STR})
+endif()
+################################################################################
+# Find/download Boost
+################################################################################
+include(FetchContent)
+FetchContent_Declare(
+ Boost
+ URL https://boostorg.jfrog.io/artifactory/main/release/1.81.0/source/boost_1_81_0.tar.gz
+ URL_HASH SHA256=205666dea9f6a7cfed87c7a6dfbeb52a2c1b9de55712c9c1a87735d7181452b6
+ SOURCE_SUBDIR "null" # Set to a nonexistent directory so boost is not built (we don't need to build it)
+ DOWNLOAD_EXTRACT_TIMESTAMP false # supress timestamp warning, not needed since the url wont change
+)
+
+set(Boost_NO_BOOST_CMAKE false)
+set(BOOST_INCLUDEDIR ${FETCHCONTENT_BASE_DIR}/boost-src) # Location where FetchContent stores the source
+message("Searching for Boost installation")
+find_package(Boost)
+
+if (NOT ${Boost_FOUND})
+ message("Boost not found. Downloading now...")
+ FetchContent_MakeAvailable(Boost)
+ message("Boost downloaded to " ${FETCHCONTENT_BASE_DIR}/boost-src)
+ set(BOOST-INCLUDE ${FETCHCONTENT_BASE_DIR}/boost-src)
+else()
+ message("Boost found in " ${Boost_INCLUDE_DIRS})
+ set(BOOST-INCLUDE ${Boost_INCLUDE_DIRS})
+endif()
+################################################################################
+# Compile definitions
+################################################################################
+find_package(SDL2)
+set(SDL2-INCLUDE ${SDL2_INCLUDE_DIRS})
+
+if (BUILD_CROWD_CONTROL)
+ find_package(SDL2_net)
+ set(SDL2-NET-INCLUDE ${SDL_NET_INCLUDE_DIRS})
+endif()
+
+target_include_directories(${PROJECT_NAME} PRIVATE assets
+ ${CMAKE_CURRENT_SOURCE_DIR}/include/
+ ${CMAKE_CURRENT_SOURCE_DIR}/include/PR
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/
+ ${CMAKE_CURRENT_SOURCE_DIR}/../libultraship
+ ${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/include
+ ${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/src
+ ${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/src/log
+ ${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/src/debug
+ ${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/src/menu
+ ${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/src/utils
+ ${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/src/utils/binarytools
+ ${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/src/config
+ ${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/src/resource
+ ${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/src/resource/type
+ ${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/src/resource/factory
+ ${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/src/audio
+ ${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/src/window
+ ${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/src/window/gui
+ ${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/src/config
+ ${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/src/public
+ ${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/src/public/libultra
+ ${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/src/public/bridge
+ ${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/extern
+ ${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/extern/tinyxml2
+ ${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/libultraship/Lib/
+ ${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/libultraship/Lib/libjpeg/include/
+ ${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/libultraship/Lib/spdlog/include/
+ ${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/src/graphic/Fast3D/U64/PR
+ ${CMAKE_CURRENT_SOURCE_DIR}/../libultraship/src/graphic
+ ${CMAKE_CURRENT_SOURCE_DIR}/../ZAPDTR/ZAPDUtils
+ ${CMAKE_CURRENT_SOURCE_DIR}/../ZAPDTR/ZAPD/resource/type
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/2s2h/
+ ${SDL2-INCLUDE}
+ ${SDL2-NET-INCLUDE}
+ ${BOOST-INCLUDE}
+ ${CMAKE_CURRENT_SOURCE_DIR}/assets/
+ .
+)
+
+if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
+ if("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "x64")
+ target_compile_definitions(${PROJECT_NAME} PRIVATE
+ "$<$<CONFIG:Debug>:"
+ "_DEBUG;"
+ "_CRT_SECURE_NO_WARNINGS;"
+ "ENABLE_DX11;"
+ ">"
+ "$<$<CONFIG:Release>:"
+ "NDEBUG"
+ ">"
+ "$<$<BOOL:${BUILD_CROWD_CONTROL}>:ENABLE_CROWD_CONTROL>"
+ "INCLUDE_GAME_PRINTF;"
+ "ENABLE_CROWD_CONTROL;"
+ "UNICODE;"
+ "_UNICODE"
+ STORMLIB_NO_AUTO_LINK
+ "_CRT_SECURE_NO_WARNINGS;"
+ )
+ elseif("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "Win32")
+ target_compile_definitions(${PROJECT_NAME} PRIVATE
+ "$<$<CONFIG:Debug>:"
+ "NOINCLUDE_GAME_PRINTF;"
+ "_DEBUG;"
+ "_CRT_SECURE_NO_WARNINGS;"
+ "ENABLE_OPENGL"
+ ">"
+ "$<$<CONFIG:Release>:"
+ "NDEBUG;"
+ ">"
+ "INCLUDE_GAME_PRINTF;"
+ "NON_EQUIVALENT;"
+ "NON_MATCHING;"
+ "WIN32;"
+ "UNICODE;"
+ "_UNICODE"
+ STORMLIB_NO_AUTO_LINK
+ )
+ endif()
+elseif (CMAKE_SYSTEM_NAME STREQUAL "CafeOS")
+ target_compile_definitions(${PROJECT_NAME} PRIVATE
+ "$<$<CONFIG:Debug>:"
+ "_DEBUG"
+ ">"
+ "$<$<CONFIG:Release>:"
+ "NDEBUG"
+ ">"
+ "SPDLOG_ACTIVE_LEVEL=3;"
+ "SPDLOG_NO_THREAD_ID;"
+ "SPDLOG_NO_TLS;"
+ "STBI_NO_THREAD_LOCALS;"
+ )
+elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU|Clang|AppleClang")
+ target_compile_definitions(${PROJECT_NAME} PRIVATE
+ "$<$<CONFIG:Debug>:"
+ "_DEBUG"
+ ">"
+ "$<$<CONFIG:Release>:"
+ "NDEBUG"
+ ">"
+ "$<$<BOOL:${BUILD_CROWD_CONTROL}>:ENABLE_CROWD_CONTROL>"
+ "SPDLOG_ACTIVE_LEVEL=0;"
+ "_CONSOLE;"
+ "_CRT_SECURE_NO_WARNINGS;"
+ "ENABLE_OPENGL;"
+ "UNICODE;"
+ "_UNICODE"
+ "NON_MATCHING;"
+ "NON_EQUIVALENT;"
+ )
+endif()
+################################################################################
+# Compile and link options
+################################################################################
+if(MSVC)
+ if("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "x64")
+ target_compile_options(${PROJECT_NAME} PRIVATE
+ $<$<CONFIG:Debug>:
+ /w;
+ /Od
+ >
+ $<$<CONFIG:Release>:
+ /Oi;
+ /Gy;
+ /W3
+ >
+ /sdl-;
+ /permissive-;
+ /MP;
+ ${DEFAULT_CXX_DEBUG_INFORMATION_FORMAT};
+ ${DEFAULT_CXX_EXCEPTION_HANDLING}
+ )
+ target_compile_options(${PROJECT_NAME} PRIVATE $<$<CONFIG:Debug>:/ZI;>)
+ elseif("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "Win32")
+ target_compile_options(${PROJECT_NAME} PRIVATE
+ $<$<CONFIG:Debug>:
+ /RTCs
+ >
+ $<$<CONFIG:Release>:
+ /O2;
+ /Oi;
+ /Gy
+ >
+ /permissive-;
+ /MP;
+ /sdl-;
+ /w;
+ ${DEFAULT_CXX_DEBUG_INFORMATION_FORMAT};
+ ${DEFAULT_CXX_EXCEPTION_HANDLING}
+ )
+ endif()
+ if("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "x64")
+ target_link_options(${PROJECT_NAME} PRIVATE
+ $<$<CONFIG:Debug>:
+ /INCREMENTAL
+ >
+ $<$<CONFIG:Release>:
+ /OPT:REF;
+ /OPT:ICF;
+ /INCREMENTAL:NO;
+ /FORCE:MULTIPLE
+ >
+ /MANIFEST:NO;
+ /DEBUG;
+ /SUBSYSTEM:WINDOWS
+ )
+ elseif("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "Win32")
+ target_link_options(${PROJECT_NAME} PRIVATE
+ $<$<CONFIG:Debug>:
+ /STACK:8777216
+ >
+ $<$<CONFIG:Release>:
+ /OPT:REF;
+ /OPT:ICF;
+ /INCREMENTAL:NO;
+ /FORCE:MULTIPLE
+ >
+ /MANIFEST:NO;
+ /DEBUG;
+ /SUBSYSTEM:WINDOWS
+ )
+ endif()
+endif()
+
+if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang")
+ if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
+ target_compile_options(${PROJECT_NAME} PRIVATE
+ -Wall -Wextra -Wno-error
+ -Wno-return-type
+ -Wno-unused-parameter
+ -Wno-unused-function
+ -Wno-unused-variable
+ -Wno-missing-field-initializers
+ -Wno-parentheses
+ -Wno-narrowing
+ -Wno-missing-braces
+ $<$<COMPILE_LANGUAGE:C>:
+ #-Werror-implicit-function-declaration
+ -Wno-incompatible-pointer-types
+ >
+ $<$<COMPILE_LANGUAGE:CXX>:-fpermissive>
+ $<$<COMPILE_LANGUAGE:CXX>:
+ -Wno-c++11-narrowing
+ -Wno-deprecated-enum-enum-conversion
+ >
+ -pthread
+ )
+
+ target_link_options(${PROJECT_NAME} PRIVATE
+ -pthread
+ )
+ elseif (CMAKE_SYSTEM_NAME STREQUAL "NintendoSwitch")
+ target_compile_options(${PROJECT_NAME} PRIVATE
+ -Wall -Wextra -Wno-error
+ -Wno-return-type
+ -Wno-unused-parameter
+ -Wno-unused-function
+ -Wno-unused-variable
+ -Wno-missing-field-initializers
+ -Wno-parentheses
+ -Wno-narrowing
+ -Wno-missing-braces
+ $<$<COMPILE_LANGUAGE:C>:
+ -Werror-implicit-function-declaration
+ -Wno-incompatible-pointer-types
+ >
+ $<$<COMPILE_LANGUAGE:CXX>:-fpermissive>
+ $<$<COMPILE_LANGUAGE:CXX>:
+ -Wno-c++11-narrowing
+ -Wno-deprecated-enum-enum-conversion
+ >
+ -pthread
+ )
+
+ target_link_options(${PROJECT_NAME} PRIVATE
+ -pthread
+ )
+ elseif (CMAKE_SYSTEM_NAME STREQUAL "CafeOS")
+ target_compile_options(${PROJECT_NAME} PRIVATE
+ -O2
+
+ # disable some warnings to not clutter output
+ -Wno-multichar
+ -Wno-return-type
+ -Wno-narrowing
+ -Wno-switch-outside-range
+ $<$<COMPILE_LANGUAGE:C>:
+ -Werror-implicit-function-declaration
+ -Wno-incompatible-pointer-types
+ -Wno-discarded-array-qualifiers
+ -Wno-discarded-qualifiers
+ -Wno-int-conversion
+ -Wno-builtin-declaration-mismatch
+ -Wno-switch-unreachable
+ -Wno-stringop-overflow
+ >
+ )
+ else()
+ if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
+ set(CPU_OPTION -msse2 -mfpmath=sse)
+ endif()
+
+ target_compile_options(${PROJECT_NAME} PRIVATE
+ -Wall -Wextra -Wno-error
+ -Wno-unused-parameter
+ -Wno-unused-function
+ -Wno-unused-variable
+ -Wno-missing-field-initializers
+ -Wno-parentheses
+ -Wno-narrowing
+ -Wno-missing-braces
+ $<$<COMPILE_LANGUAGE:C>:
+ #-Werror-implicit-function-declaration
+ -Wno-incompatible-pointer-types
+ >
+ $<$<COMPILE_LANGUAGE:CXX>:-fpermissive>
+ $<$<COMPILE_LANGUAGE:CXX>:-Wno-deprecated-enum-enum-conversion>
+ -pthread
+ ${CPU_OPTION}
+ )
+
+ target_link_options(${PROJECT_NAME} PRIVATE
+ -pthread
+ -Wl,-export-dynamic
+ )
+ endif()
+endif()
+################################################################################
+# Pre build events
+################################################################################
+if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
+ add_custom_command_if(
+ TARGET ${PROJECT_NAME}
+ PRE_BUILD
+ COMMANDS
+ COMMAND $<CONFIG:Debug> copy /b $<SHELL_PATH:${CMAKE_BINARY_DIR}/>build.c +,,
+ )
+endif()
+
+if(NOT CMAKE_SYSTEM_NAME MATCHES "NintendoSwitch|CafeOS")
+ add_custom_command(
+ TARGET ${PROJECT_NAME}
+ POST_BUILD
+ COMMENT "Copying asset xmls..."
+ #COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/mm/assets/extractor $<TARGET_FILE_DIR:mm>/assets/extractor
+ #COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/mm/assets/xml $<TARGET_FILE_DIR:mm>/assets/extractor/xmls
+ #COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/OTRExporter/CFG/filelists $<TARGET_FILE_DIR:mm>/assets/extractor/filelists
+ #COMMAND ${CMAKE_COMMAND} -E make_directory $<TARGET_FILE_DIR:mm>/assets/extractor/symbols
+ #COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/OTRExporter/CFG/ActorList_MM.txt $<TARGET_FILE_DIR:mm>/assets/extractor/symbols
+ #COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/OTRExporter/CFG/ObjectList_MM.txt $<TARGET_FILE_DIR:mm>/assets/extractor/symbols
+ #COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/OTRExporter/CFG/SymbolMap_MM.txt $<TARGET_FILE_DIR:mm>/assets/extractor/symbols
+ )
+endif()
+################################################################################
+# Dependencies
+################################################################################
+add_dependencies(${PROJECT_NAME}
+ ZAPDUtils
+ libultraship
+)
+if(NOT CMAKE_SYSTEM_NAME MATCHES "NintendoSwitch|CafeOS")
+add_dependencies(${PROJECT_NAME}
+ ZAPDLib
+)
+endif()
+
+if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
+ find_package(glfw3 REQUIRED)
+ if("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "x64")
+ set(ADDITIONAL_LIBRARY_DEPENDENCIES
+ "libultraship;"
+ "ZAPDUtils;"
+ "ZAPDLib;"
+ "glu32;"
+ "SDL2::SDL2;"
+ "SDL2::SDL2main;"
+ "$<$<BOOL:${BUILD_CROWD_CONTROL}>:SDL2_net::SDL2_net-static>"
+ "glfw;"
+ "winmm;"
+ "imm32;"
+ "version;"
+ "setupapi"
+ )
+ elseif("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "Win32")
+ set(ADDITIONAL_LIBRARY_DEPENDENCIES
+ "libultraship;"
+ "ZAPDUtils;"
+ "ZAPDLib;"
+ "glu32;"
+ "SDL2::SDL2;"
+ "SDL2::SDL2main;"
+ "glfw;"
+ "winmm;"
+ "imm32;"
+ "version;"
+ "setupapi"
+ )
+ endif()
+elseif(CMAKE_SYSTEM_NAME STREQUAL "NintendoSwitch")
+ find_package(SDL2)
+ set(THREADS_PREFER_PTHREAD_FLAG ON)
+ find_package(Threads REQUIRED)
+ set(ADDITIONAL_LIBRARY_DEPENDENCIES
+ "libultraship;"
+ "ZAPDUtils;"
+ SDL2::SDL2
+ -lglad
+ Threads::Threads
+ )
+elseif(CMAKE_SYSTEM_NAME STREQUAL "CafeOS")
+ find_package(SDL2 REQUIRED)
+ set(ADDITIONAL_LIBRARY_DEPENDENCIES
+ "libultraship;"
+ SDL2::SDL2-static
+
+ "$<$<CONFIG:Debug>:-Wl,--wrap=abort>"
+ )
+ target_include_directories(${PROJECT_NAME} PRIVATE
+ ${DEVKITPRO}/portlibs/wiiu/include/
+ )
+else()
+ find_package(SDL2)
+ set(THREADS_PREFER_PTHREAD_FLAG ON)
+ find_package(Threads REQUIRED)
+ set(ADDITIONAL_LIBRARY_DEPENDENCIES
+ "libultraship;"
+ "ZAPDUtils;"
+ "ZAPDLib;"
+ SDL2::SDL2
+ "$<$<BOOL:${BUILD_CROWD_CONTROL}>:SDL2_net::SDL2_net>"
+ ${CMAKE_DL_LIBS}
+ Threads::Threads
+ )
+endif()
+
+if(NOT CMAKE_SYSTEM_NAME MATCHES "NintendoSwitch|CafeOS")
+INSTALL(TARGETS mm DESTINATION . COMPONENT 2s2h)
+endif()
+
+if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
+INSTALL(FILES $<TARGET_PDB_FILE:mm> DESTINATION ./debug COMPONENT 2s2h)
+INSTALL(FILES ${CMAKE_BINARY_DIR}/mm/soh.otr DESTINATION . COMPONENT 2s2h)
+endif()
+
+find_program(CURL NAMES curl DOC "Path to the curl program. Used to download files.")
+execute_process(COMMAND ${CURL} -sSfL https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt -o ${CMAKE_BINARY_DIR}/gamecontrollerdb.txt OUTPUT_VARIABLE RESULT)
+
+if("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")
+configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/macosx/Info.plist.in ${CMAKE_BINARY_DIR}/macosx/Info.plist @ONLY)
+INSTALL(FILES ${CMAKE_BINARY_DIR}/gamecontrollerdb.txt DESTINATION ../MacOS COMPONENT 2s2h)
+INSTALL(FILES ${CMAKE_BINARY_DIR}/mm/soh.otr DESTINATION ../Resources COMPONENT 2s2h)
+elseif(NOT "${CMAKE_SYSTEM_NAME}" MATCHES "NintendoSwitch|CafeOS")
+INSTALL(FILES ${CMAKE_BINARY_DIR}/gamecontrollerdb.txt DESTINATION . COMPONENT 2s2h)
+endif()
+
+if(CMAKE_SYSTEM_NAME MATCHES "NintendoSwitch|CafeOS")
+ if (NOT TARGET pathconf)
+ add_library(pathconf OBJECT platform/pathconf.c)
+ endif()
+ target_link_libraries(${PROJECT_NAME} PRIVATE "${ADDITIONAL_LIBRARY_DEPENDENCIES}" $<TARGET_OBJECTS:pathconf> )
+else()
+ target_link_libraries(${PROJECT_NAME} PRIVATE "${ADDITIONAL_LIBRARY_DEPENDENCIES}")
+endif()
+
+if(CMAKE_SYSTEM_NAME MATCHES "NintendoSwitch")
+
+nx_generate_nacp(2s2h.nacp
+ NAME "2s2h of Harkinian"
+ AUTHOR "${PROJECT_TEAM}"
+ VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}"
+)
+
+nx_create_nro(2s2h
+ NACP 2s2h.nacp
+ ICON ${CMAKE_CURRENT_SOURCE_DIR}/icon.jpg
+)
+
+INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/2s2h.nro DESTINATION . COMPONENT 2s2h)
+
+elseif(CMAKE_SYSTEM_NAME MATCHES "CafeOS")
+
+wut_create_rpx(${PROJECT_NAME})
+
+wut_create_wuhb(${PROJECT_NAME}
+ NAME "Two Ship Two Harkinian"
+ SHORTNAME "2S2H"
+ AUTHOR "${PROJECT_TEAM}"
+ ICON ${CMAKE_CURRENT_SOURCE_DIR}/icon.jpg
+)
+
+INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/2s2h.rpx ${CMAKE_CURRENT_BINARY_DIR}/2s2h.wuhb DESTINATION . COMPONENT 2s2h)
+
+endif()
diff --git a/mm/assets/xml/archives/icon_item_24_static.xml b/mm/assets/xml/archives/icon_item_24_static.xml
index 2997b546b..6bf637534 100644
--- a/mm/assets/xml/archives/icon_item_24_static.xml
+++ b/mm/assets/xml/archives/icon_item_24_static.xml
@@ -1,5 +1,5 @@
<Root>
- <File Name="icon_item_24_static_yar.unarchive" Segment="9">
+ <File Name="icon_item_24_static_yar" Segment="9">
<Texture Name="gQuestIconGoldSkulltulaTex" OutName="gold_skulltula_icon" Format="rgba32" Width="24" Height="24" Offset="0x0" />
<Texture Name="gQuestIconHeartContainerTex" OutName="heart_container_icon" Format="rgba32" Width="24" Height="24" Offset="0x900" />
<Texture Name="gQuestIconPieceOfHeartTex" OutName="piece_of_heart_icon" Format="rgba32" Width="24" Height="24" Offset="0x1200" />
diff --git a/mm/assets/xml/archives/icon_item_static.xml b/mm/assets/xml/archives/icon_item_static.xml
index 3480d704a..52cf6a3a9 100644
--- a/mm/assets/xml/archives/icon_item_static.xml
+++ b/mm/assets/xml/archives/icon_item_static.xml
@@ -1,5 +1,5 @@
<Root>
- <File Name="icon_item_static_yar.unarchive" Segment="8">
+ <File Name="icon_item_static_yar" Segment="8">
<!-- Item icons -->
<Texture Name="gItemIconOcarinaOfTimeTex" OutName="ocarina_of_time_icon" Format="rgba32" Width="32" Height="32" Offset="0x0000"/>
<Texture Name="gItemIconBowTex" OutName="bow_icon" Format="rgba32" Width="32" Height="32" Offset="0x1000"/>
diff --git a/mm/assets/xml/archives/item_name_static.xml b/mm/assets/xml/archives/item_name_static.xml
index da0b903a0..b9ff2a5ee 100644
--- a/mm/assets/xml/archives/item_name_static.xml
+++ b/mm/assets/xml/archives/item_name_static.xml
@@ -1,6 +1,6 @@
<Root>
<!-- Item Names. Must be ia4 128x16 and ordered following `ItemId` -->
- <File Name="item_name_static.unarchive">
+ <File Name="item_name_static">
<!-- Main items -->
<Texture Name="gItemNameOcarinaOfTimeENGTex" OutName="item_name_ocarina_of_time_eng" Format="ia4" Width="128" Height="16" Offset="0x0" /> <!-- ITEM_OCARINA_OF_TIME -->
<Texture Name="gItemNameHerosBowENGTex" OutName="item_name_heros_bow_eng" Format="ia4" Width="128" Height="16" Offset="0x400" /> <!-- ITEM_BOW -->
diff --git a/mm/assets/xml/archives/map_grand_static.xml b/mm/assets/xml/archives/map_grand_static.xml
index 05e5893c1..94e51b350 100644
--- a/mm/assets/xml/archives/map_grand_static.xml
+++ b/mm/assets/xml/archives/map_grand_static.xml
@@ -1,102 +1,102 @@
<Root>
- <File Name="map_grand_static.unarchive">
- <Texture Name="gMapGrandStatic100Tex" OutName="map_100" Format="i4" Width="96" Height="85" Offset="0x0" />
- <Texture Name="gMapGrandStatic101Tex" OutName="map_101" Format="i4" Width="96" Height="85" Offset="0xFF0" />
- <Texture Name="gMapGrandStatic102Tex" OutName="map_102" Format="i4" Width="96" Height="85" Offset="0x1FE0" />
- <Texture Name="gMapGrandStatic103Tex" OutName="map_103" Format="i4" Width="96" Height="85" Offset="0x2FD0" />
- <Texture Name="gMapGrandStatic104Tex" OutName="map_104" Format="i4" Width="96" Height="85" Offset="0x3FC0" />
- <Texture Name="gMapGrandStatic105Tex" OutName="map_105" Format="i4" Width="80" Height="72" Offset="0x4FB0" />
- <Texture Name="gMapGrandStatic106Tex" OutName="map_106" Format="i4" Width="96" Height="85" Offset="0x5AF0" />
- <Texture Name="gMapGrandStatic107Tex" OutName="map_107" Format="i4" Width="96" Height="85" Offset="0x6AE0" />
- <Texture Name="gMapGrandStatic108Tex" OutName="map_108" Format="i4" Width="96" Height="85" Offset="0x7AD0" />
- <Texture Name="gMapGrandStatic109Tex" OutName="map_109" Format="i4" Width="96" Height="85" Offset="0x8AC0" />
- <Texture Name="gMapGrandStatic10ATex" OutName="map_10A" Format="i4" Width="48" Height="44" Offset="0x9AB0" />
- <Texture Name="gMapGrandStatic10BTex" OutName="map_10B" Format="i4" Width="96" Height="85" Offset="0x9ED0" />
- <Texture Name="gMapGrandStatic10CTex" OutName="map_10C" Format="i4" Width="48" Height="46" Offset="0xAEC0" />
- <Texture Name="gMapGrandStatic10DTex" OutName="map_10D" Format="i4" Width="64" Height="61" Offset="0xB310" />
- <Texture Name="gMapGrandStatic10ETex" OutName="map_10E" Format="i4" Width="96" Height="85" Offset="0xBAB0" />
- <Texture Name="gMapGrandStatic10FTex" OutName="map_10F" Format="i4" Width="96" Height="85" Offset="0xCAA0" />
- <Texture Name="gMapGrandStatic110Tex" OutName="map_110" Format="i4" Width="96" Height="85" Offset="0xDA90" />
- <Texture Name="gMapGrandStatic111Tex" OutName="map_111" Format="i4" Width="48" Height="80" Offset="0xEA80" />
- <Texture Name="gMapGrandStatic112Tex" OutName="map_112" Format="i4" Width="32" Height="128" Offset="0xF200" />
- <Texture Name="gMapGrandStatic113Tex" OutName="map_113" Format="i4" Width="80" Height="53" Offset="0xFA00" />
- <Texture Name="gMapGrandStatic114Tex" OutName="map_114" Format="i4" Width="32" Height="81" Offset="0x10250" />
- <Texture Name="gMapGrandStatic115Tex" OutName="map_115" Format="i4" Width="80" Height="45" Offset="0x10760" />
- <Texture Name="gMapGrandStatic116Tex" OutName="map_116" Format="i4" Width="64" Height="87" Offset="0x10E70" />
- <Texture Name="gMapGrandStatic117Tex" OutName="map_117" Format="i4" Width="64" Height="51" Offset="0x11950" />
- <Texture Name="gMapGrandStatic118Tex" OutName="map_118" Format="i4" Width="80" Height="79" Offset="0x11FB0" />
- <Texture Name="gMapGrandStatic119Tex" OutName="map_119" Format="i4" Width="96" Height="75" Offset="0x12C10" />
- <Texture Name="gMapGrandStatic11ATex" OutName="map_11A" Format="i4" Width="48" Height="41" Offset="0x13A20" />
- <Texture Name="gMapGrandStatic11BTex" OutName="map_11B" Format="i4" Width="64" Height="45" Offset="0x13E00" />
- <Texture Name="gMapGrandStatic11CTex" OutName="map_11C" Format="i4" Width="80" Height="52" Offset="0x143A0" />
- <Texture Name="gMapGrandStatic11DTex" OutName="map_11D" Format="i4" Width="48" Height="45" Offset="0x14BC0" />
- <Texture Name="gMapGrandStatic11ETex" OutName="map_11E" Format="i4" Width="48" Height="59" Offset="0x15000" />
- <Texture Name="gMapGrandStatic11FTex" OutName="map_11F" Format="i4" Width="48" Height="60" Offset="0x15590" />
- <Texture Name="gMapGrandStatic120Tex" OutName="map_120" Format="i4" Width="48" Height="81" Offset="0x15B30" />
- <Texture Name="gMapGrandStatic121Tex" OutName="map_121" Format="i4" Width="48" Height="81" Offset="0x162D0" />
- <Texture Name="gMapGrandStatic122Tex" OutName="map_122" Format="i4" Width="80" Height="89" Offset="0x16A70" />
- <Texture Name="gMapGrandStatic123Tex" OutName="map_123" Format="i4" Width="80" Height="53" Offset="0x17860" />
- <Texture Name="gMapGrandStatic124Tex" OutName="map_124" Format="i4" Width="64" Height="78" Offset="0x180B0" />
- <Texture Name="gMapGrandStatic125Tex" OutName="map_125" Format="i4" Width="64" Height="68" Offset="0x18A70" />
- <Texture Name="gMapGrandStatic126Tex" OutName="map_126" Format="i4" Width="48" Height="68" Offset="0x192F0" />
- <Texture Name="gMapGrandStatic127Tex" OutName="map_127" Format="i4" Width="48" Height="36" Offset="0x19950" />
- <Texture Name="gMapGrandStatic128Tex" OutName="map_128" Format="i4" Width="48" Height="25" Offset="0x19CB0" />
- <Texture Name="gMapGrandStatic129Tex" OutName="map_129" Format="i4" Width="64" Height="75" Offset="0x19F10" />
- <Texture Name="gMapGrandStatic12ATex" OutName="map_12A" Format="i4" Width="32" Height="53" Offset="0x1A870" />
- <Texture Name="gMapGrandStatic12BTex" OutName="map_12B" Format="i4" Width="80" Height="62" Offset="0x1ABC0" />
- <Texture Name="gMapGrandStatic12CTex" OutName="map_12C" Format="i4" Width="64" Height="76" Offset="0x1B570" />
- <Texture Name="gMapGrandStatic12DTex" OutName="map_12D" Format="i4" Width="96" Height="85" Offset="0x1BEF0" />
- <Texture Name="gMapGrandStatic12ETex" OutName="map_12E" Format="i4" Width="80" Height="71" Offset="0x1CEE0" />
- <Texture Name="gMapGrandStatic12FTex" OutName="map_12F" Format="i4" Width="80" Height="87" Offset="0x1DA00" />
- <Texture Name="gMapGrandStatic130Tex" OutName="map_130" Format="i4" Width="64" Height="65" Offset="0x1E7A0" />
- <Texture Name="gMapGrandStatic131Tex" OutName="map_131" Format="i4" Width="48" Height="41" Offset="0x1EFC0" />
- <Texture Name="gMapGrandStatic132Tex" OutName="map_132" Format="i4" Width="64" Height="76" Offset="0x1F3A0" />
- <Texture Name="gMapGrandStatic133Tex" OutName="map_133" Format="i4" Width="80" Height="60" Offset="0x1FD20" />
- <Texture Name="gMapGrandStatic134Tex" OutName="map_134" Format="i4" Width="80" Height="47" Offset="0x20680" />
- <Texture Name="gMapGrandStatic135Tex" OutName="map_135" Format="i4" Width="80" Height="60" Offset="0x20DE0" />
- <Texture Name="gMapGrandStatic136Tex" OutName="map_136" Format="i4" Width="48" Height="19" Offset="0x21740" />
- <Texture Name="gMapGrandStatic137Tex" OutName="map_137" Format="i4" Width="32" Height="99" Offset="0x21910" />
- <Texture Name="gMapGrandStatic138Tex" OutName="map_138" Format="i4" Width="48" Height="69" Offset="0x21F40" />
- <Texture Name="gMapGrandStatic139Tex" OutName="map_139" Format="i4" Width="48" Height="83" Offset="0x225C0" />
- <Texture Name="gMapGrandStatic13ATex" OutName="map_13A" Format="i4" Width="80" Height="54" Offset="0x22D90" />
- <Texture Name="gMapGrandStatic13BTex" OutName="map_13B" Format="i4" Width="80" Height="92" Offset="0x23600" />
- <Texture Name="gMapGrandStatic13CTex" OutName="map_13C" Format="i4" Width="48" Height="75" Offset="0x24460" />
- <Texture Name="gMapGrandStatic13DTex" OutName="map_13D" Format="i4" Width="64" Height="49" Offset="0x24B70" />
- <Texture Name="gMapGrandStatic13ETex" OutName="map_13E" Format="i4" Width="64" Height="49" Offset="0x25190" />
- <Texture Name="gMapGrandStatic13FTex" OutName="map_13F" Format="i4" Width="64" Height="52" Offset="0x257B0" />
- <Texture Name="gMapGrandStatic140Tex" OutName="map_140" Format="i4" Width="64" Height="49" Offset="0x25E30" />
- <Texture Name="gMapGrandStatic141Tex" OutName="map_141" Format="i4" Width="32" Height="33" Offset="0x26450" />
- <Texture Name="gMapGrandStatic142Tex" OutName="map_142" Format="i4" Width="48" Height="50" Offset="0x26660" />
- <Texture Name="gMapGrandStatic143Tex" OutName="map_143" Format="i4" Width="64" Height="52" Offset="0x26B10" />
- <Texture Name="gMapGrandStatic144Tex" OutName="map_144" Format="i4" Width="48" Height="35" Offset="0x27190" />
- <Texture Name="gMapGrandStatic145Tex" OutName="map_145" Format="i4" Width="64" Height="51" Offset="0x274E0" />
- <Texture Name="gMapGrandStatic146Tex" OutName="map_146" Format="i4" Width="48" Height="35" Offset="0x27B40" />
- <Texture Name="gMapGrandStatic147Tex" OutName="map_147" Format="i4" Width="48" Height="53" Offset="0x27E90" />
- <Texture Name="gMapGrandStatic148Tex" OutName="map_148" Format="i4" Width="64" Height="53" Offset="0x28390" />
- <Texture Name="gMapGrandStatic149Tex" OutName="map_149" Format="i4" Width="64" Height="47" Offset="0x28A30" />
- <Texture Name="gMapGrandStatic14ATex" OutName="map_14A" Format="i4" Width="64" Height="52" Offset="0x29010" />
- <Texture Name="gMapGrandStatic14BTex" OutName="map_14B" Format="i4" Width="48" Height="48" Offset="0x29690" />
- <Texture Name="gMapGrandStatic14CTex" OutName="map_14C" Format="i4" Width="64" Height="87" Offset="0x29B10" />
- <Texture Name="gMapGrandStatic14DTex" OutName="map_14D" Format="i4" Width="48" Height="30" Offset="0x2A5F0" />
- <Texture Name="gMapGrandStatic14ETex" OutName="map_14E" Format="i4" Width="80" Height="74" Offset="0x2A8C0" />
- <Texture Name="gMapGrandStatic14FTex" OutName="map_14F" Format="i4" Width="80" Height="87" Offset="0x2B450" />
- <Texture Name="gMapGrandStatic150Tex" OutName="map_150" Format="i4" Width="80" Height="56" Offset="0x2C1F0" />
- <Texture Name="gMapGrandStatic151Tex" OutName="map_151" Format="i4" Width="80" Height="65" Offset="0x2CAB0" />
- <Texture Name="gMapGrandStatic152Tex" OutName="map_152" Format="i4" Width="16" Height="38" Offset="0x2D4E0" />
- <Texture Name="gMapGrandStatic153Tex" OutName="map_153" Format="i4" Width="80" Height="78" Offset="0x2D610" />
- <Texture Name="gMapGrandStatic154Tex" OutName="map_154" Format="i4" Width="80" Height="55" Offset="0x2E240" />
- <Texture Name="gMapGrandStatic155Tex" OutName="map_155" Format="i4" Width="80" Height="98" Offset="0x2EAE0" />
- <Texture Name="gMapGrandStatic156Tex" OutName="map_156" Format="i4" Width="80" Height="62" Offset="0x2FA30" />
- <Texture Name="gMapGrandStatic157Tex" OutName="map_157" Format="i4" Width="32" Height="123" Offset="0x303E0" />
- <Texture Name="gMapGrandStatic158Tex" OutName="map_158" Format="i4" Width="80" Height="60" Offset="0x30B90" />
- <Texture Name="gMapGrandStatic159Tex" OutName="map_159" Format="i4" Width="48" Height="77" Offset="0x314F0" />
- <Texture Name="gMapGrandStatic15ATex" OutName="map_15A" Format="i4" Width="80" Height="64" Offset="0x31C30" />
- <Texture Name="gMapGrandStatic15BTex" OutName="map_15B" Format="i4" Width="64" Height="50" Offset="0x32630" />
- <Texture Name="gMapGrandStatic15CTex" OutName="map_15C" Format="i4" Width="80" Height="65" Offset="0x32C70" />
- <Texture Name="gMapGrandStatic15DTex" OutName="map_15D" Format="i4" Width="32" Height="61" Offset="0x336A0" />
- <Texture Name="gMapGrandStatic15ETex" OutName="map_15E" Format="i4" Width="80" Height="83" Offset="0x33A70" />
- <Texture Name="gMapGrandStatic15FTex" OutName="map_15F" Format="i4" Width="80" Height="27" Offset="0x34770" />
- <Texture Name="gMapGrandStatic160Tex" OutName="map_160" Format="i4" Width="80" Height="23" Offset="0x34BB0" />
- <Texture Name="gMapGrandStatic161Tex" OutName="map_161" Format="i4" Width="80" Height="60" Offset="0x34F50" />
+ <File Name="map_grand_static">
+ <Blob Name="map_grand_static_Blob_000000" Size="0x0FF0" Offset="0x0" />
+ <Blob Name="map_grand_static_Blob_000FF0" Size="0x0FF0" Offset="0xFF0" />
+ <Blob Name="map_grand_static_Blob_001FE0" Size="0x0FF0" Offset="0x1FE0" />
+ <Blob Name="map_grand_static_Blob_002FD0" Size="0x0FF0" Offset="0x2FD0" />
+ <Blob Name="map_grand_static_Blob_003FC0" Size="0x0FF0" Offset="0x3FC0" />
+ <Blob Name="map_grand_static_Blob_004FB0" Size="0x0B40" Offset="0x4FB0" />
+ <Blob Name="map_grand_static_Blob_005AF0" Size="0x0FF0" Offset="0x5AF0" />
+ <Blob Name="map_grand_static_Blob_006AE0" Size="0x0FF0" Offset="0x6AE0" />
+ <Blob Name="map_grand_static_Blob_007AD0" Size="0x0FF0" Offset="0x7AD0" />
+ <Blob Name="map_grand_static_Blob_008AC0" Size="0x0FF0" Offset="0x8AC0" />
+ <Blob Name="map_grand_static_Blob_009AB0" Size="0x0420" Offset="0x9AB0" />
+ <Blob Name="map_grand_static_Blob_009ED0" Size="0x0FF0" Offset="0x9ED0" />
+ <Blob Name="map_grand_static_Blob_00AEC0" Size="0x0450" Offset="0xAEC0" />
+ <Blob Name="map_grand_static_Blob_00B310" Size="0x07A0" Offset="0xB310" />
+ <Blob Name="map_grand_static_Blob_00BAB0" Size="0x0FF0" Offset="0xBAB0" />
+ <Blob Name="map_grand_static_Blob_00CAA0" Size="0x0FF0" Offset="0xCAA0" />
+ <Blob Name="map_grand_static_Blob_00DA90" Size="0x0FF0" Offset="0xDA90" />
+ <Blob Name="map_grand_static_Blob_00EA80" Size="0x0780" Offset="0xEA80" />
+ <Blob Name="map_grand_static_Blob_00F200" Size="0x0800" Offset="0xF200" />
+ <Blob Name="map_grand_static_Blob_00FA00" Size="0x0850" Offset="0xFA00" />
+ <Blob Name="map_grand_static_Blob_010250" Size="0x0510" Offset="0x10250" />
+ <Blob Name="map_grand_static_Blob_010760" Size="0x0710" Offset="0x10760" />
+ <Blob Name="map_grand_static_Blob_010E70" Size="0x0AE0" Offset="0x10E70" />
+ <Blob Name="map_grand_static_Blob_011950" Size="0x0660" Offset="0x11950" />
+ <Blob Name="map_grand_static_Blob_011FB0" Size="0x0C60" Offset="0x11FB0" />
+ <Blob Name="map_grand_static_Blob_012C10" Size="0x0E10" Offset="0x12C10" />
+ <Blob Name="map_grand_static_Blob_013A20" Size="0x03E0" Offset="0x13A20" />
+ <Blob Name="map_grand_static_Blob_013E00" Size="0x05A0" Offset="0x13E00" />
+ <Blob Name="map_grand_static_Blob_0143A0" Size="0x0820" Offset="0x143A0" />
+ <Blob Name="map_grand_static_Blob_014BC0" Size="0x0440" Offset="0x14BC0" />
+ <Blob Name="map_grand_static_Blob_015000" Size="0x0590" Offset="0x15000" />
+ <Blob Name="map_grand_static_Blob_015590" Size="0x05A0" Offset="0x15590" />
+ <Blob Name="map_grand_static_Blob_015B30" Size="0x07A0" Offset="0x15B30" />
+ <Blob Name="map_grand_static_Blob_0162D0" Size="0x07A0" Offset="0x162D0" />
+ <Blob Name="map_grand_static_Blob_016A70" Size="0x0DF0" Offset="0x16A70" />
+ <Blob Name="map_grand_static_Blob_017860" Size="0x0850" Offset="0x17860" />
+ <Blob Name="map_grand_static_Blob_0180B0" Size="0x09C0" Offset="0x180B0" />
+ <Blob Name="map_grand_static_Blob_018A70" Size="0x0880" Offset="0x18A70" />
+ <Blob Name="map_grand_static_Blob_0192F0" Size="0x0660" Offset="0x192F0" />
+ <Blob Name="map_grand_static_Blob_019950" Size="0x0360" Offset="0x19950" />
+ <Blob Name="map_grand_static_Blob_019CB0" Size="0x0260" Offset="0x19CB0" />
+ <Blob Name="map_grand_static_Blob_019F10" Size="0x0960" Offset="0x19F10" />
+ <Blob Name="map_grand_static_Blob_01A870" Size="0x0350" Offset="0x1A870" />
+ <Blob Name="map_grand_static_Blob_01ABC0" Size="0x09B0" Offset="0x1ABC0" />
+ <Blob Name="map_grand_static_Blob_01B570" Size="0x0980" Offset="0x1B570" />
+ <Blob Name="map_grand_static_Blob_01BEF0" Size="0x0FF0" Offset="0x1BEF0" />
+ <Blob Name="map_grand_static_Blob_01CEE0" Size="0x0B20" Offset="0x1CEE0" />
+ <Blob Name="map_grand_static_Blob_01DA00" Size="0x0DA0" Offset="0x1DA00" />
+ <Blob Name="map_grand_static_Blob_01E7A0" Size="0x0820" Offset="0x1E7A0" />
+ <Blob Name="map_grand_static_Blob_01EFC0" Size="0x03E0" Offset="0x1EFC0" />
+ <Blob Name="map_grand_static_Blob_01F3A0" Size="0x0980" Offset="0x1F3A0" />
+ <Blob Name="map_grand_static_Blob_01FD20" Size="0x0960" Offset="0x1FD20" />
+ <Blob Name="map_grand_static_Blob_020680" Size="0x0760" Offset="0x20680" />
+ <Blob Name="map_grand_static_Blob_020DE0" Size="0x0960" Offset="0x20DE0" />
+ <Blob Name="map_grand_static_Blob_021740" Size="0x01D0" Offset="0x21740" />
+ <Blob Name="map_grand_static_Blob_021910" Size="0x0630" Offset="0x21910" />
+ <Blob Name="map_grand_static_Blob_021F40" Size="0x0680" Offset="0x21F40" />
+ <Blob Name="map_grand_static_Blob_0225C0" Size="0x07D0" Offset="0x225C0" />
+ <Blob Name="map_grand_static_Blob_022D90" Size="0x0870" Offset="0x22D90" />
+ <Blob Name="map_grand_static_Blob_023600" Size="0x0E60" Offset="0x23600" />
+ <Blob Name="map_grand_static_Blob_024460" Size="0x0710" Offset="0x24460" />
+ <Blob Name="map_grand_static_Blob_024B70" Size="0x0620" Offset="0x24B70" />
+ <Blob Name="map_grand_static_Blob_025190" Size="0x0620" Offset="0x25190" />
+ <Blob Name="map_grand_static_Blob_0257B0" Size="0x0680" Offset="0x257B0" />
+ <Blob Name="map_grand_static_Blob_025E30" Size="0x0620" Offset="0x25E30" />
+ <Blob Name="map_grand_static_Blob_026450" Size="0x0210" Offset="0x26450" />
+ <Blob Name="map_grand_static_Blob_026660" Size="0x04B0" Offset="0x26660" />
+ <Blob Name="map_grand_static_Blob_026B10" Size="0x0680" Offset="0x26B10" />
+ <Blob Name="map_grand_static_Blob_027190" Size="0x0350" Offset="0x27190" />
+ <Blob Name="map_grand_static_Blob_0274E0" Size="0x0660" Offset="0x274E0" />
+ <Blob Name="map_grand_static_Blob_027B40" Size="0x0350" Offset="0x27B40" />
+ <Blob Name="map_grand_static_Blob_027E90" Size="0x0500" Offset="0x27E90" />
+ <Blob Name="map_grand_static_Blob_028390" Size="0x06A0" Offset="0x28390" />
+ <Blob Name="map_grand_static_Blob_028A30" Size="0x05E0" Offset="0x28A30" />
+ <Blob Name="map_grand_static_Blob_029010" Size="0x0680" Offset="0x29010" />
+ <Blob Name="map_grand_static_Blob_029690" Size="0x0480" Offset="0x29690" />
+ <Blob Name="map_grand_static_Blob_029B10" Size="0x0AE0" Offset="0x29B10" />
+ <Blob Name="map_grand_static_Blob_02A5F0" Size="0x02D0" Offset="0x2A5F0" />
+ <Blob Name="map_grand_static_Blob_02A8C0" Size="0x0B90" Offset="0x2A8C0" />
+ <Blob Name="map_grand_static_Blob_02B450" Size="0x0DA0" Offset="0x2B450" />
+ <Blob Name="map_grand_static_Blob_02C1F0" Size="0x08C0" Offset="0x2C1F0" />
+ <Blob Name="map_grand_static_Blob_02CAB0" Size="0x0A30" Offset="0x2CAB0" />
+ <Blob Name="map_grand_static_Blob_02D4E0" Size="0x0130" Offset="0x2D4E0" />
+ <Blob Name="map_grand_static_Blob_02D610" Size="0x0C30" Offset="0x2D610" />
+ <Blob Name="map_grand_static_Blob_02E240" Size="0x08A0" Offset="0x2E240" />
+ <Blob Name="map_grand_static_Blob_02EAE0" Size="0x0F50" Offset="0x2EAE0" />
+ <Blob Name="map_grand_static_Blob_02FA30" Size="0x09B0" Offset="0x2FA30" />
+ <Blob Name="map_grand_static_Blob_0303E0" Size="0x07B0" Offset="0x303E0" />
+ <Blob Name="map_grand_static_Blob_030B90" Size="0x0960" Offset="0x30B90" />
+ <Blob Name="map_grand_static_Blob_0314F0" Size="0x0740" Offset="0x314F0" />
+ <Blob Name="map_grand_static_Blob_031C30" Size="0x0A00" Offset="0x31C30" />
+ <Blob Name="map_grand_static_Blob_032630" Size="0x0640" Offset="0x32630" />
+ <Blob Name="map_grand_static_Blob_032C70" Size="0x0A30" Offset="0x32C70" />
+ <Blob Name="map_grand_static_Blob_0336A0" Size="0x03D0" Offset="0x336A0" />
+ <Blob Name="map_grand_static_Blob_033A70" Size="0x0D00" Offset="0x33A70" />
+ <Blob Name="map_grand_static_Blob_034770" Size="0x0440" Offset="0x34770" />
+ <Blob Name="map_grand_static_Blob_034BB0" Size="0x03A0" Offset="0x34BB0" />
+ <Blob Name="map_grand_static_Blob_034F50" Size="0x0960" Offset="0x34F50" />
</File>
</Root>
diff --git a/mm/assets/xml/archives/map_i_static.xml b/mm/assets/xml/archives/map_i_static.xml
index fce2cc437..15e1a4348 100644
--- a/mm/assets/xml/archives/map_i_static.xml
+++ b/mm/assets/xml/archives/map_i_static.xml
@@ -1,62 +1,62 @@
<Root>
- <File Name="map_i_static.unarchive">
- <Texture Name="gMapIStatic00Tex" OutName="map_00" Format="i4" Width="96" Height="85" Offset="0x0" />
- <Texture Name="gMapIStatic01Tex" OutName="map_01" Format="i4" Width="96" Height="85" Offset="0xFF0" />
- <Texture Name="gMapIStatic02Tex" OutName="map_02" Format="i4" Width="96" Height="85" Offset="0x1FE0" />
- <Texture Name="gMapIStatic03Tex" OutName="map_03" Format="i4" Width="96" Height="85" Offset="0x2FD0" />
- <Texture Name="gMapIStatic04Tex" OutName="map_04" Format="i4" Width="96" Height="85" Offset="0x3FC0" />
- <Texture Name="gMapIStatic05Tex" OutName="map_05" Format="i4" Width="32" Height="29" Offset="0x4FB0" />
- <Texture Name="gMapIStatic06Tex" OutName="map_06" Format="i4" Width="32" Height="27" Offset="0x5180" />
- <Texture Name="gMapIStatic07Tex" OutName="map_07" Format="i4" Width="32" Height="30" Offset="0x5330" />
- <Texture Name="gMapIStatic08Tex" OutName="map_08" Format="i4" Width="16" Height="32" Offset="0x5510" />
- <Texture Name="gMapIStatic09Tex" OutName="map_09" Format="i4" Width="16" Height="12" Offset="0x5610" />
- <Texture Name="gMapIStatic0ATex" OutName="map_0A" Format="i4" Width="32" Height="27" Offset="0x5670" />
- <Texture Name="gMapIStatic0BTex" OutName="map_0B" Format="i4" Width="16" Height="14" Offset="0x5820" />
- <Texture Name="gMapIStatic0CTex" OutName="map_0C" Format="i4" Width="32" Height="19" Offset="0x5890" />
- <Texture Name="gMapIStatic0DTex" OutName="map_0D" Format="i4" Width="32" Height="26" Offset="0x59C0" />
- <Texture Name="gMapIStatic0ETex" OutName="map_0E" Format="i4" Width="16" Height="32" Offset="0x5B60" />
- <Texture Name="gMapIStatic0FTex" OutName="map_0F" Format="i4" Width="32" Height="27" Offset="0x5C60" />
- <Texture Name="gMapIStatic10Tex" OutName="map_10" Format="i4" Width="16" Height="35" Offset="0x5E10" />
- <Texture Name="gMapIStatic11Tex" OutName="map_11" Format="i4" Width="32" Height="18" Offset="0x5F30" />
- <Texture Name="gMapIStatic12Tex" OutName="map_12" Format="i4" Width="32" Height="38" Offset="0x6050" />
- <Texture Name="gMapIStatic13Tex" OutName="map_13" Format="i4" Width="32" Height="21" Offset="0x62B0" />
- <Texture Name="gMapIStatic14Tex" OutName="map_14" Format="i4" Width="32" Height="34" Offset="0x6400" />
- <Texture Name="gMapIStatic15Tex" OutName="map_15" Format="i4" Width="48" Height="32" Offset="0x6620" />
- <Texture Name="gMapIStatic16Tex" OutName="map_16" Format="i4" Width="32" Height="17" Offset="0x6920" />
- <Texture Name="gMapIStatic17Tex" OutName="map_17" Format="i4" Width="32" Height="17" Offset="0x6A30" />
- <Texture Name="gMapIStatic18Tex" OutName="map_18" Format="i4" Width="32" Height="21" Offset="0x6B40" />
- <Texture Name="gMapIStatic19Tex" OutName="map_19" Format="i4" Width="32" Height="18" Offset="0x6C90" />
- <Texture Name="gMapIStatic1ATex" OutName="map_1A" Format="i4" Width="16" Height="24" Offset="0x6DB0" />
- <Texture Name="gMapIStatic1BTex" OutName="map_1B" Format="i4" Width="16" Height="25" Offset="0x6E70" />
- <Texture Name="gMapIStatic1CTex" OutName="map_1C" Format="i4" Width="32" Height="35" Offset="0x6F40" />
- <Texture Name="gMapIStatic1DTex" OutName="map_1D" Format="i4" Width="16" Height="19" Offset="0x7170" />
- <Texture Name="gMapIStatic1ETex" OutName="map_1E" Format="i4" Width="32" Height="28" Offset="0x7210" />
- <Texture Name="gMapIStatic1FTex" OutName="map_1F" Format="i4" Width="32" Height="16" Offset="0x73D0" />
- <Texture Name="gMapIStatic20Tex" OutName="map_20" Format="i4" Width="32" Height="24" Offset="0x74D0" />
- <Texture Name="gMapIStatic21Tex" OutName="map_21" Format="i4" Width="32" Height="21" Offset="0x7650" />
- <Texture Name="gMapIStatic22Tex" OutName="map_22" Format="i4" Width="16" Height="21" Offset="0x77A0" />
- <Texture Name="gMapIStatic23Tex" OutName="map_23" Format="i4" Width="16" Height="10" Offset="0x7850" />
- <Texture Name="gMapIStatic24Tex" OutName="map_24" Format="i4" Width="16" Height="7" Offset="0x78A0" />
- <Texture Name="gMapIStatic25Tex" OutName="map_25" Format="i4" Width="32" Height="23" Offset="0x78E0" />
- <Texture Name="gMapIStatic26Tex" OutName="map_26" Format="i4" Width="16" Height="16" Offset="0x7A50" />
- <Texture Name="gMapIStatic27Tex" OutName="map_27" Format="i4" Width="16" Height="17" Offset="0x7AD0" />
- <Texture Name="gMapIStatic28Tex" OutName="map_28" Format="i4" Width="16" Height="23" Offset="0x7B60" />
- <Texture Name="gMapIStatic29Tex" OutName="map_29" Format="i4" Width="16" Height="15" Offset="0x7C20" />
- <Texture Name="gMapIStatic2ATex" OutName="map_2A" Format="i4" Width="32" Height="15" Offset="0x7CA0" />
- <Texture Name="gMapIStatic2BTex" OutName="map_2B" Format="i4" Width="32" Height="17" Offset="0x7D90" />
- <Texture Name="gMapIStatic2CTex" OutName="map_2C" Format="i4" Width="32" Height="16" Offset="0x7EA0" />
- <Texture Name="gMapIStatic2DTex" OutName="map_2D" Format="i4" Width="16" Height="10" Offset="0x7FA0" />
- <Texture Name="gMapIStatic2ETex" OutName="map_2E" Format="i4" Width="16" Height="17" Offset="0x7FF0" />
- <Texture Name="gMapIStatic2FTex" OutName="map_2F" Format="i4" Width="32" Height="17" Offset="0x8080" />
- <Texture Name="gMapIStatic30Tex" OutName="map_30" Format="i4" Width="16" Height="11" Offset="0x8190" />
- <Texture Name="gMapIStatic31Tex" OutName="map_31" Format="i4" Width="32" Height="17" Offset="0x81F0" />
- <Texture Name="gMapIStatic32Tex" OutName="map_32" Format="i4" Width="16" Height="12" Offset="0x8300" />
- <Texture Name="gMapIStatic33Tex" OutName="map_33" Format="i4" Width="16" Height="18" Offset="0x8360" />
- <Texture Name="gMapIStatic34Tex" OutName="map_34" Format="i4" Width="32" Height="17" Offset="0x83F0" />
- <Texture Name="gMapIStatic35Tex" OutName="map_35" Format="i4" Width="32" Height="15" Offset="0x8500" />
- <Texture Name="gMapIStatic36Tex" OutName="map_36" Format="i4" Width="16" Height="18" Offset="0x85F0" />
- <Texture Name="gMapIStatic37Tex" OutName="map_37" Format="i4" Width="16" Height="16" Offset="0x8680" />
- <Texture Name="gMapIStatic38Tex" OutName="map_38" Format="i4" Width="16" Height="30" Offset="0x8700" />
- <Texture Name="gMapIStatic39Tex" OutName="map_39" Format="i4" Width="16" Height="9" Offset="0x87F0" />
+ <File Name="map_i_static">
+ <Blob Name="map_i_static_Blob_000000" Size="0x0FF0" Offset="0x0" />
+ <Blob Name="map_i_static_Blob_000FF0" Size="0x0FF0" Offset="0xFF0" />
+ <Blob Name="map_i_static_Blob_001FE0" Size="0x0FF0" Offset="0x1FE0" />
+ <Blob Name="map_i_static_Blob_002FD0" Size="0x0FF0" Offset="0x2FD0" />
+ <Blob Name="map_i_static_Blob_003FC0" Size="0x0FF0" Offset="0x3FC0" />
+ <Blob Name="map_i_static_Blob_004FB0" Size="0x01D0" Offset="0x4FB0" />
+ <Blob Name="map_i_static_Blob_005180" Size="0x01B0" Offset="0x5180" />
+ <Blob Name="map_i_static_Blob_005330" Size="0x01E0" Offset="0x5330" />
+ <Blob Name="map_i_static_Blob_005510" Size="0x0100" Offset="0x5510" />
+ <Blob Name="map_i_static_Blob_005610" Size="0x0060" Offset="0x5610" />
+ <Blob Name="map_i_static_Blob_005670" Size="0x01B0" Offset="0x5670" />
+ <Blob Name="map_i_static_Blob_005820" Size="0x0070" Offset="0x5820" />
+ <Blob Name="map_i_static_Blob_005890" Size="0x0130" Offset="0x5890" />
+ <Blob Name="map_i_static_Blob_0059C0" Size="0x01A0" Offset="0x59C0" />
+ <Blob Name="map_i_static_Blob_005B60" Size="0x0100" Offset="0x5B60" />
+ <Blob Name="map_i_static_Blob_005C60" Size="0x01B0" Offset="0x5C60" />
+ <Blob Name="map_i_static_Blob_005E10" Size="0x0120" Offset="0x5E10" />
+ <Blob Name="map_i_static_Blob_005F30" Size="0x0120" Offset="0x5F30" />
+ <Blob Name="map_i_static_Blob_006050" Size="0x0260" Offset="0x6050" />
+ <Blob Name="map_i_static_Blob_0062B0" Size="0x0150" Offset="0x62B0" />
+ <Blob Name="map_i_static_Blob_006400" Size="0x0220" Offset="0x6400" />
+ <Blob Name="map_i_static_Blob_006620" Size="0x0300" Offset="0x6620" />
+ <Blob Name="map_i_static_Blob_006920" Size="0x0110" Offset="0x6920" />
+ <Blob Name="map_i_static_Blob_006A30" Size="0x0110" Offset="0x6A30" />
+ <Blob Name="map_i_static_Blob_006B40" Size="0x0150" Offset="0x6B40" />
+ <Blob Name="map_i_static_Blob_006C90" Size="0x0120" Offset="0x6C90" />
+ <Blob Name="map_i_static_Blob_006DB0" Size="0x00C0" Offset="0x6DB0" />
+ <Blob Name="map_i_static_Blob_006E70" Size="0x00D0" Offset="0x6E70" />
+ <Blob Name="map_i_static_Blob_006F40" Size="0x0230" Offset="0x6F40" />
+ <Blob Name="map_i_static_Blob_007170" Size="0x00A0" Offset="0x7170" />
+ <Blob Name="map_i_static_Blob_007210" Size="0x01C0" Offset="0x7210" />
+ <Blob Name="map_i_static_Blob_0073D0" Size="0x0100" Offset="0x73D0" />
+ <Blob Name="map_i_static_Blob_0074D0" Size="0x0180" Offset="0x74D0" />
+ <Blob Name="map_i_static_Blob_007650" Size="0x0150" Offset="0x7650" />
+ <Blob Name="map_i_static_Blob_0077A0" Size="0x00B0" Offset="0x77A0" />
+ <Blob Name="map_i_static_Blob_007850" Size="0x0050" Offset="0x7850" />
+ <Blob Name="map_i_static_Blob_0078A0" Size="0x0040" Offset="0x78A0" />
+ <Blob Name="map_i_static_Blob_0078E0" Size="0x0170" Offset="0x78E0" />
+ <Blob Name="map_i_static_Blob_007A50" Size="0x0080" Offset="0x7A50" />
+ <Blob Name="map_i_static_Blob_007AD0" Size="0x0090" Offset="0x7AD0" />
+ <Blob Name="map_i_static_Blob_007B60" Size="0x00C0" Offset="0x7B60" />
+ <Blob Name="map_i_static_Blob_007C20" Size="0x0080" Offset="0x7C20" />
+ <Blob Name="map_i_static_Blob_007CA0" Size="0x00F0" Offset="0x7CA0" />
+ <Blob Name="map_i_static_Blob_007D90" Size="0x0110" Offset="0x7D90" />
+ <Blob Name="map_i_static_Blob_007EA0" Size="0x0100" Offset="0x7EA0" />
+ <Blob Name="map_i_static_Blob_007FA0" Size="0x0050" Offset="0x7FA0" />
+ <Blob Name="map_i_static_Blob_007FF0" Size="0x0090" Offset="0x7FF0" />
+ <Blob Name="map_i_static_Blob_008080" Size="0x0110" Offset="0x8080" />
+ <Blob Name="map_i_static_Blob_008190" Size="0x0060" Offset="0x8190" />
+ <Blob Name="map_i_static_Blob_0081F0" Size="0x0110" Offset="0x81F0" />
+ <Blob Name="map_i_static_Blob_008300" Size="0x0060" Offset="0x8300" />
+ <Blob Name="map_i_static_Blob_008360" Size="0x0090" Offset="0x8360" />
+ <Blob Name="map_i_static_Blob_0083F0" Size="0x0110" Offset="0x83F0" />
+ <Blob Name="map_i_static_Blob_008500" Size="0x00F0" Offset="0x8500" />
+ <Blob Name="map_i_static_Blob_0085F0" Size="0x0090" Offset="0x85F0" />
+ <Blob Name="map_i_static_Blob_008680" Size="0x0080" Offset="0x8680" />
+ <Blob Name="map_i_static_Blob_008700" Size="0x00F0" Offset="0x8700" />
+ <Blob Name="map_i_static_Blob_0087F0" Size="0x0050" Offset="0x87F0" />
</File>
</Root>
diff --git a/mm/assets/xml/archives/map_name_static.xml b/mm/assets/xml/archives/map_name_static.xml
index fe392eb03..d672cdc39 100644
--- a/mm/assets/xml/archives/map_name_static.xml
+++ b/mm/assets/xml/archives/map_name_static.xml
@@ -1,6 +1,6 @@
<Root>
<!-- Map Point Names. Must be ia4 128x16 and continguous with `ItemId` starting from `ITEM_MAP_POINT_GREAT_BAY` -->
- <File Name="map_name_static.unarchive">
+ <File Name="map_name_static">
<!-- First entries must be continguous with `RegionId` -->
<Texture Name="gMapPointGreatBayENGTex" OutName="map_point_great_bay_eng" Format="ia4" Width="128" Height="16" Offset="0x0" /> <!-- ITEM_MAP_POINT_GREAT_BAY -->
<Texture Name="gMapPointZoraHallENGTex" OutName="map_point_zora_hall_eng" Format="ia4" Width="128" Height="16" Offset="0x400" /> <!-- ITEM_MAP_POINT_ZORA_HALL -->
diff --git a/mm/assets/xml/archives/schedule_dma_static.xml b/mm/assets/xml/archives/schedule_dma_static.xml
index c71bdb413..42f75f6d9 100644
--- a/mm/assets/xml/archives/schedule_dma_static.xml
+++ b/mm/assets/xml/archives/schedule_dma_static.xml
@@ -1,5 +1,5 @@
<Root>
- <File Name="schedule_dma_static_yar.unarchive" Segment="7">
+ <File Name="schedule_dma_static_yar" Segment="7">
<Texture Name="gBombersNotebookPhotoAnjuTex" OutName="bombers_notebook_photo_anju" Format="rgba16" Width="32" Height="32" Offset="0x0" />
<Texture Name="gBombersNotebookPhotoKafeiTex" OutName="bombers_notebook_photo_kafei" Format="rgba16" Width="32" Height="32" Offset="0x800" />
<Texture Name="gBombersNotebookPhotoCuriosityShopManTex" OutName="bombers_notebook_photo_curiosity_shop_man" Format="rgba16" Width="32" Height="32" Offset="0x1000" />