summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorinspectredc <inspectredc@gmail.com>2024-04-25 15:52:32 +0100
committerLywx <kiritodev01@gmail.com>2024-04-25 09:40:57 -0600
commit09e8620f7584c1ba7c7ed688fbeecf10dace94fe (patch)
tree29e4cfe75fd007871850fc7711d0f0d9f4cdfe0c /src
parentdf54367b676f44d2b925e681ae05eef665f7c45f (diff)
LevelScript Factory
Diffstat (limited to 'src')
-rw-r--r--src/Companion.cpp2
-rw-r--r--src/factories/ResourceType.h1
-rw-r--r--src/factories/sm64/LevelScriptFactory.cpp654
-rw-r--r--src/factories/sm64/LevelScriptFactory.h51
-rw-r--r--src/factories/sm64/level/LevelCommand.h281
5 files changed, 989 insertions, 0 deletions
diff --git a/src/Companion.cpp b/src/Companion.cpp
index 317fe64..e413ff7 100644
--- a/src/Companion.cpp
+++ b/src/Companion.cpp
@@ -34,6 +34,7 @@
#include "factories/sm64/DictionaryFactory.h"
#include "factories/sm64/TextFactory.h"
#include "factories/sm64/GeoLayoutFactory.h"
+#include "factories/sm64/LevelScriptFactory.h"
#include "factories/sm64/MacroFactory.h"
#include "factories/sm64/MovtexFactory.h"
#include "factories/sm64/MovtexQuadFactory.h"
@@ -96,6 +97,7 @@ void Companion::Init(const ExportType type) {
this->RegisterFactory("SM64:ANIM", std::make_shared<SM64::AnimationFactory>());
this->RegisterFactory("SM64:COLLISION", std::make_shared<SM64::CollisionFactory>());
this->RegisterFactory("SM64:GEO_LAYOUT", std::make_shared<SM64::GeoLayoutFactory>());
+ this->RegisterFactory("SM64:LEVEL_SCRIPT", std::make_shared<SM64::LevelScriptFactory>());
this->RegisterFactory("SM64:MACRO", std::make_shared<SM64::MacroFactory>());
this->RegisterFactory("SM64:MOVTEX_QUAD", std::make_shared<SM64::MovtexQuadFactory>());
this->RegisterFactory("SM64:MOVTEX", std::make_shared<SM64::MovtexFactory>());
diff --git a/src/factories/ResourceType.h b/src/factories/ResourceType.h
index 0076b7b..218bd92 100644
--- a/src/factories/ResourceType.h
+++ b/src/factories/ResourceType.h
@@ -29,6 +29,7 @@ enum class ResourceType {
Dictionary = 0x44494354, // DICT
GeoLayout = 0x47454F20, // GEO
Collision = 0x434F4C20, // COL
+ LevelScript = 0x4C564C53, // LVLS
MacroObject = 0x4D41434F, // MACO
Movtex = 0x4D4F5654, // MOVT
MovtexQuad = 0x4D4F5651, // MOVQ
diff --git a/src/factories/sm64/LevelScriptFactory.cpp b/src/factories/sm64/LevelScriptFactory.cpp
new file mode 100644
index 0000000..013feb6
--- /dev/null
+++ b/src/factories/sm64/LevelScriptFactory.cpp
@@ -0,0 +1,654 @@
+#include "LevelScriptFactory.h"
+#include "spdlog/spdlog.h"
+
+#include "Companion.h"
+#include "utils/Decompressor.h"
+#include "utils/TorchUtils.h"
+#include <regex>
+
+uint64_t RegisterPtr(uint32_t ptr, std::string type) {
+
+ // Disable autogen for now since most assets will be from external files
+
+ // if (ptr != 0) {
+ // YAML::Node node;
+ // node["type"] = type;
+ // node["offset"] = ptr;
+ // Companion::Instance->AddAsset(node);
+ // }
+
+ return ptr;
+}
+
+ExportResult SM64::LevelScriptHeaderExporter::Export(std::ostream &write, std::shared_ptr<IParsedData> raw, std::string& entryName, YAML::Node &node, std::string* replacement) {
+ const auto symbol = GetSafeNode(node, "symbol", entryName);
+
+ if(Companion::Instance->IsOTRMode()){
+ write << "static const char " << symbol << "[] = \"__OTR__" << (*replacement) << "\";\n\n";
+ return std::nullopt;
+ }
+
+ write << "extern LevelScript " << symbol << "[];\n";
+ return std::nullopt;
+}
+
+ExportResult SM64::LevelScriptCodeExporter::Export(std::ostream &write, std::shared_ptr<IParsedData> raw, std::string& entryName, YAML::Node &node, std::string* replacement ) {
+ const auto symbol = GetSafeNode(node, "symbol", entryName);
+ const auto offset = GetSafeNode<uint32_t>(node, "offset");
+ const auto commands = std::static_pointer_cast<LevelScriptData>(raw)->mCommands;
+ uint32_t indentCount = 1;
+
+ write << "static const LevelScript " << symbol << "[] = {\n";
+
+ for(auto& [opcode, arguments] : commands) {
+ bool commaFlag = false;
+
+ for (uint32_t i = 0; i < indentCount; ++i) {
+ write << fourSpaceTab;
+ }
+
+ if (opcode == LevelOpcode::AREA) {
+ ++indentCount;
+ } else if (opcode == LevelOpcode::END_AREA) {
+ --indentCount;
+ }
+
+ write << opcode << "(";
+ for(auto& args : arguments) {
+ if (commaFlag) {
+ write << ", ";
+ } else {
+ commaFlag = true;
+ }
+
+ switch(static_cast<LevelArgumentType>(args.index())) {
+ case LevelArgumentType::U8: {
+ write << std::hex << "0x" << static_cast<uint32_t>(std::get<uint8_t>(args));
+ break;
+ }
+ case LevelArgumentType::S8: {
+ write << std::hex << "0x" << static_cast<uint32_t>(std::get<int8_t>(args));
+ break;
+ }
+ case LevelArgumentType::U16: {
+ write << std::hex << "0x" << std::get<uint16_t>(args);
+ break;
+ }
+ case LevelArgumentType::S16: {
+ write << std::dec << std::get<int16_t>(args);
+ break;
+ }
+ case LevelArgumentType::U32: {
+ write << std::hex << "0x" << std::get<uint32_t>(args);
+ break;
+ }
+ case LevelArgumentType::S32: {
+ write << std::dec << std::get<int32_t>(args);
+ break;
+ }
+ case LevelArgumentType::F32: {
+ write << std::dec << std::get<float>(args);
+ break;
+ }
+ case LevelArgumentType::PTR: {
+ auto ptr = std::get<uint64_t>(args);
+ auto dec = Companion::Instance->GetNodeByAddr(ptr);
+ std::string symbol = "NULL";
+
+ if (ptr == 0) {
+ write << symbol;
+ } else if (dec.has_value()) {
+ auto node = std::get<1>(dec.value());
+ symbol = GetSafeNode<std::string>(node, "symbol");
+ write << symbol;
+ } else {
+ SPDLOG_WARN("Cannot find node for ptr 0x{:X}", ptr);
+ write << std::hex << "0x" << ptr;
+ }
+ break;
+ }
+ default: {
+ throw std::runtime_error("Unknown Level Argument Type");
+ }
+ }
+ }
+ write << "),\n";
+ }
+
+ write << "\n};\n";
+
+ size_t size = 0;
+
+ return offset + size;
+}
+
+ExportResult SM64::LevelScriptBinaryExporter::Export(std::ostream &write, std::shared_ptr<IParsedData> raw, std::string& entryName, YAML::Node &node, std::string* replacement ) {
+ auto writer = LUS::BinaryWriter();
+ const auto commands = std::static_pointer_cast<LevelScriptData>(raw)->mCommands;
+
+ WriteHeader(writer, LUS::ResourceType::LevelScript, 0);
+
+ writer.Write((uint32_t)commands.size());
+
+ for(auto& [opcode, arguments] : commands) {
+ writer.Write(static_cast<uint8_t>(opcode));
+
+ for(auto& args : arguments) {
+ switch(static_cast<LevelArgumentType>(args.index())) {
+ case LevelArgumentType::U8: {
+ writer.Write(std::get<uint8_t>(args));
+ break;
+ }
+ case LevelArgumentType::S8: {
+ writer.Write(std::get<int8_t>(args));
+ break;
+ }
+ case LevelArgumentType::U16: {
+ writer.Write(std::get<uint16_t>(args));
+ break;
+ }
+ case LevelArgumentType::S16: {
+ writer.Write(std::get<int16_t>(args));
+ break;
+ }
+ case LevelArgumentType::U32: {
+ writer.Write(std::get<uint32_t>(args));
+ break;
+ }
+ case LevelArgumentType::S32: {
+ writer.Write(std::get<int32_t>(args));
+ break;
+ }
+ case LevelArgumentType::F32: {
+ writer.Write(std::get<float>(args));
+ break;
+ }
+ case LevelArgumentType::PTR: {
+ auto ptr = static_cast<uint32_t>(std::get<uint64_t>(args));
+ auto dec = Companion::Instance->GetNodeByAddr(ptr);
+ if (ptr == 0) {
+ writer.Write(ptr);
+ } else if (dec.has_value()) {
+ uint64_t hash = CRC64(std::get<0>(dec.value()).c_str());
+ SPDLOG_INFO("Found Asset: 0x{:X} Hash: 0x{:X} Path: {}", ptr, hash, std::get<0>(dec.value()));
+ writer.Write(hash);
+ } else {
+ SPDLOG_WARN("Could not find Asset at 0x{:X}", ptr);
+ }
+ break;
+ }
+ default: {
+ throw std::runtime_error("Unknown Level Argument Type");
+ }
+ }
+ }
+ }
+
+ writer.Finish(write);
+ return std::nullopt;
+}
+
+std::optional<std::shared_ptr<IParsedData>> SM64::LevelScriptFactory::parse(std::vector<uint8_t>& buffer, YAML::Node& node) {
+ auto [_, segment] = Decompressor::AutoDecode(node, buffer);
+ auto cmd = segment.data;
+ bool processing = true;
+ std::vector<LevelCommand> commands;
+ size_t count;
+
+ if (node["count"]) {
+ count = GetSafeNode<size_t>(node, "count");
+ } else {
+ count = 0;
+ }
+
+ while(processing) {
+ auto opcode = static_cast<LevelOpcode>(cmd[0x00]);
+
+ SPDLOG_INFO("Processing Command {}", opcode);
+
+ std::vector<LevelArgument> arguments;
+
+ switch (opcode) {
+ case LevelOpcode::EXECUTE: {
+ auto seg = cur_level_cmd_s16(0x02);
+ auto scriptStart = cur_level_cmd_u32(0x04);
+ auto scriptEnd = cur_level_cmd_u32(0x08);
+ auto ptr = cur_level_cmd_u32(0x0C);
+ arguments.emplace_back(seg);
+ arguments.emplace_back(scriptStart);
+ arguments.emplace_back(scriptEnd);
+ arguments.emplace_back(RegisterPtr(ptr, "SM64:LEVEL_SCRIPT"));
+
+ cmd += 0x10 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::EXIT_AND_EXECUTE: {
+ auto seg = cur_level_cmd_s16(0x02);
+ auto scriptStart = cur_level_cmd_u32(0x04);
+ auto scriptEnd = cur_level_cmd_u32(0x08);
+ auto ptr = cur_level_cmd_u32(0x0C);
+ arguments.emplace_back(seg);
+ arguments.emplace_back(scriptStart);
+ arguments.emplace_back(scriptEnd);
+ arguments.emplace_back(RegisterPtr(ptr, "SM64:LEVEL_SCRIPT"));
+ processing = false;
+ cmd += 0x10 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::EXIT: {
+ processing = false;
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::SLEEP:
+ case LevelOpcode::SLEEP_BEFORE_EXIT: {
+ auto frames = cur_level_cmd_s16(0x02);
+ arguments.emplace_back(frames);
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::JUMP: {
+ auto targetPtr = cur_level_cmd_u32(0x04);
+ arguments.emplace_back(RegisterPtr(targetPtr, "SM64:LEVEL_SCRIPT"));
+ processing = false;
+ cmd += 0x8 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::JUMP_LINK: {
+ // unused, not sure if processing = false
+ auto targetPtr = cur_level_cmd_u32(0x04);
+ arguments.emplace_back(RegisterPtr(targetPtr, "SM64:LEVEL_SCRIPT"));
+ cmd += 0x8 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::RETURN: {
+ processing = false;
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::JUMP_LINK_PUSH_ARG: {
+ // unused, not sure if processing = false
+ auto arg = cur_level_cmd_s16(0x02);
+ arguments.emplace_back(arg);
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::JUMP_N_TIMES: {
+ // unused
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::LOOP_BEGIN: {
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::LOOP_UNTIL: {
+ auto op = cur_level_cmd_u8(0x02);
+ auto arg = cur_level_cmd_s32(0x04);
+ arguments.emplace_back(op);
+ arguments.emplace_back(arg);
+ cmd += 0x8 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::JUMP_IF:
+ case LevelOpcode::JUMP_LINK_IF: {
+ auto op = cur_level_cmd_u8(0x02);
+ auto arg = cur_level_cmd_s32(0x04);
+ auto targetPtr = cur_level_cmd_u32(0x08);
+ arguments.emplace_back(op);
+ arguments.emplace_back(arg);
+ arguments.emplace_back(RegisterPtr(targetPtr, "SM64:LEVEL_SCRIPT"));
+ cmd += 0xC << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::SKIP_IF: {
+ auto op = cur_level_cmd_u8(0x02);
+ auto arg = cur_level_cmd_s32(0x04);
+ arguments.emplace_back(op);
+ arguments.emplace_back(arg);
+ cmd += 0x8 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::SKIP:
+ case LevelOpcode::SKIP_NOP: {
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::CALL:
+ case LevelOpcode::CALL_LOOP: {
+ auto arg = cur_level_cmd_s16(0x02);
+ auto func = cur_level_cmd_u32(0x04);
+ arguments.emplace_back(arg);
+ arguments.emplace_back(func);
+ cmd += 0x8 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::SET_REG: {
+ auto value = cur_level_cmd_s16(0x02);
+ arguments.emplace_back(value);
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::PUSH_POOL:
+ case LevelOpcode::POP_POOL: {
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::FIXED_LOAD: {
+ auto loadAddr = cur_level_cmd_u32(0x04);
+ auto romStart = cur_level_cmd_u32(0x08);
+ auto romEnd = cur_level_cmd_u32(0x0C);
+ arguments.emplace_back(loadAddr);
+ arguments.emplace_back(romStart);
+ arguments.emplace_back(romEnd);
+ cmd += 0x10 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::LOAD_RAW:
+ case LevelOpcode::LOAD_MIO0: {
+ auto seg = cur_level_cmd_s16(0x02);
+ auto romStart = cur_level_cmd_u32(0x04);
+ auto romEnd = cur_level_cmd_u32(0x08);
+ arguments.emplace_back(seg);
+ arguments.emplace_back(romStart);
+ arguments.emplace_back(romEnd);
+ cmd += 0xC << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::LOAD_MARIO_HEAD: {
+ auto setHead = cur_level_cmd_s16(0x02);
+ arguments.emplace_back(setHead);
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::LOAD_MIO0_TEXTURE: {
+ auto seg = cur_level_cmd_s16(0x02);
+ auto romStart = cur_level_cmd_u32(0x04);
+ auto romEnd = cur_level_cmd_u32(0x08);
+ arguments.emplace_back(seg);
+ arguments.emplace_back(romStart);
+ arguments.emplace_back(romEnd);
+ cmd += 0xC << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::INIT_LEVEL:
+ case LevelOpcode::CLEAR_LEVEL:
+ case LevelOpcode::ALLOC_LEVEL_POOL:
+ case LevelOpcode::FREE_LEVEL_POOL: {
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::AREA: {
+ auto index = cur_level_cmd_u8(0x02);
+ auto geo = cur_level_cmd_u32(0x04);
+ arguments.emplace_back(index);
+ arguments.emplace_back(RegisterPtr(geo, "SM64:GEO_LAYOUT"));
+ cmd += 0x8 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::END_AREA: {
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::LOAD_MODEL_FROM_DL: {
+ auto modelLayer = cur_level_cmd_s16(0x02);
+ int16_t layer = (modelLayer >> 12);
+ int16_t model = modelLayer - (layer << 12);
+ auto dl = cur_level_cmd_u32(0x04);
+
+ arguments.emplace_back(model);
+ arguments.emplace_back(RegisterPtr(dl, "GFX"));
+ arguments.emplace_back(layer);
+ cmd += 0x8 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::LOAD_MODEL_FROM_GEO: {
+ auto model = cur_level_cmd_s16(0x02);
+ auto geo = cur_level_cmd_u32(0x04);
+ arguments.emplace_back(model);
+ arguments.emplace_back(RegisterPtr(geo, "SM64:GEO_LAYOUT"));
+ cmd += 0x8 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::CMD23: {
+ // unused
+ auto model = cur_level_cmd_s16(0x02);
+ auto unk4 = cur_level_cmd_u32(0x04);
+ auto unk8 = cur_level_cmd_f32(0x08);
+ arguments.emplace_back(model);
+ arguments.emplace_back(unk4);
+ arguments.emplace_back(unk8);
+ cmd += 0xC << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::OBJECT_WITH_ACTS: {
+ auto acts = cur_level_cmd_u8(0x02);
+ auto model = cur_level_cmd_u8(0x03);
+ auto posX = cur_level_cmd_s16(0x04);
+ auto posY = cur_level_cmd_s16(0x06);
+ auto posZ = cur_level_cmd_s16(0x08);
+ auto angleX = cur_level_cmd_s16(0x0A);
+ auto angleY = cur_level_cmd_s16(0x0C);
+ auto angleZ = cur_level_cmd_s16(0x0E);
+ auto bhvParam = cur_level_cmd_s32(0x10);
+ auto bhv = cur_level_cmd_u32(0x14);
+ arguments.emplace_back(model);
+ arguments.emplace_back(posX);
+ arguments.emplace_back(posY);
+ arguments.emplace_back(posZ);
+ arguments.emplace_back(angleX);
+ arguments.emplace_back(angleY);
+ arguments.emplace_back(angleZ);
+ arguments.emplace_back(bhvParam);
+ arguments.emplace_back(RegisterPtr(bhv, "SM64:BEHAVIOR_SCRIPT"));
+ arguments.emplace_back(acts);
+ cmd += 0x18 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::MARIO: {
+ auto model = cur_level_cmd_u8(0x03);
+ auto bhvArg = cur_level_cmd_s32(0x04);
+ auto bhv = cur_level_cmd_u32(0x08);
+ arguments.emplace_back(model);
+ arguments.emplace_back(bhvArg);
+ arguments.emplace_back(RegisterPtr(bhv, "SM64:BEHAVIOR_SCRIPT"));
+ cmd += 0xC << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::WARP_NODE:
+ case LevelOpcode::PAINTING_WARP_NODE: {
+ auto id = cur_level_cmd_u8(0x01);
+ auto destLevel = cur_level_cmd_u8(0x02);
+ auto destArea = cur_level_cmd_u8(0x03);
+ auto destNode = cur_level_cmd_u8(0x04);
+ auto flags = cur_level_cmd_u8(0x05);
+ arguments.emplace_back(id);
+ arguments.emplace_back(destLevel);
+ arguments.emplace_back(destArea);
+ arguments.emplace_back(destNode);
+ arguments.emplace_back(flags);
+ cmd += 0x8 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::INSTANT_WARP: {
+ auto id = cur_level_cmd_u8(0x02);
+ auto destArea = cur_level_cmd_u8(0x03);
+ auto displayX = cur_level_cmd_s16(0x04);
+ auto displayY = cur_level_cmd_s16(0x06);
+ auto displayZ = cur_level_cmd_s16(0x08);
+ arguments.emplace_back(id);
+ arguments.emplace_back(destArea);
+ arguments.emplace_back(displayX);
+ arguments.emplace_back(displayY);
+ arguments.emplace_back(displayZ);
+ cmd += 0xC << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::LOAD_AREA: {
+ auto area = cur_level_cmd_u8(0x02);
+ arguments.emplace_back(area);
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::CMD2A: {
+ auto unk2 = cur_level_cmd_u8(0x02);
+ arguments.emplace_back(unk2);
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::MARIO_POS: {
+ auto area = cur_level_cmd_u8(0x02);
+ auto yaw = cur_level_cmd_s16(0x04);
+ auto posX = cur_level_cmd_s16(0x06);
+ auto posY = cur_level_cmd_s16(0x08);
+ auto posZ = cur_level_cmd_s16(0x0A);
+ arguments.emplace_back(area);
+ arguments.emplace_back(yaw);
+ arguments.emplace_back(posX);
+ arguments.emplace_back(posY);
+ arguments.emplace_back(posZ);
+ cmd += 0xC << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::CMD2C:
+ case LevelOpcode::CMD2D: {
+ // unused
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::TERRAIN: {
+ auto terrainPtr = cur_level_cmd_u32(0x04);
+ arguments.emplace_back(RegisterPtr(terrainPtr, "SM64:COLLISION"));
+ cmd += 0x8 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::ROOMS: {
+ auto roomPtr = cur_level_cmd_u32(0x04);
+ arguments.emplace_back(RegisterPtr(roomPtr, "BLOB"));
+ cmd += 0x8 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::SHOW_DIALOG: {
+ auto index = cur_level_cmd_u8(0x02);
+ auto dialogId = cur_level_cmd_u8(0x03);
+ arguments.emplace_back(index);
+ arguments.emplace_back(dialogId);
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::TERRAIN_TYPE: {
+ auto terrainType = cur_level_cmd_s16(0x02);
+ arguments.emplace_back(terrainType);
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::NOP: {
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::TRANSITION: {
+ auto transType = cur_level_cmd_u8(0x02);
+ auto time = cur_level_cmd_u8(0x03);
+ auto colorR = cur_level_cmd_u8(0x04);
+ auto colorG = cur_level_cmd_u8(0x05);
+ auto colorB = cur_level_cmd_u8(0x06);
+ arguments.emplace_back(transType);
+ arguments.emplace_back(time);
+ arguments.emplace_back(colorR);
+ arguments.emplace_back(colorG);
+ arguments.emplace_back(colorB);
+ cmd += 0x8 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::BLACKOUT: {
+ auto active = cur_level_cmd_u8(0x02);
+ arguments.emplace_back(active);
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::GAMMA: {
+ auto enabled = cur_level_cmd_u8(0x02);
+ arguments.emplace_back(enabled);
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::SET_BACKGROUND_MUSIC: {
+ auto settingsPreset = cur_level_cmd_s16(0x02);
+ auto seq = cur_level_cmd_s16(0x04);
+ arguments.emplace_back(settingsPreset);
+ arguments.emplace_back(seq);
+ cmd += 0x8 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::SET_MENU_MUSIC: {
+ auto seq = cur_level_cmd_s16(0x02);
+ arguments.emplace_back(seq);
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::STOP_MUSIC: {
+ auto fadeOutTime = cur_level_cmd_s16(0x02);
+ arguments.emplace_back(fadeOutTime);
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::MACRO_OBJECTS: {
+ auto objPtr = cur_level_cmd_u32(0x04);
+ arguments.emplace_back(RegisterPtr(objPtr, "SM64:MACRO"));
+ cmd += 0x8 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::CMD3A: {
+ // unused
+ auto unk2 = cur_level_cmd_s16(0x02);
+ auto unk4 = cur_level_cmd_s16(0x04);
+ auto unk6 = cur_level_cmd_s16(0x06);
+ auto unk8 = cur_level_cmd_s16(0x08);
+ auto unkA = cur_level_cmd_s16(0x0A);
+ arguments.emplace_back(unk2);
+ arguments.emplace_back(unk4);
+ arguments.emplace_back(unk6);
+ arguments.emplace_back(unk8);
+ arguments.emplace_back(unkA);
+ cmd += 0xC << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::WHIRLPOOL: {
+ auto index = cur_level_cmd_u8(0x2);
+ auto condition = cur_level_cmd_u8(0x3);
+ auto posX = cur_level_cmd_s16(0x4);
+ auto posY = cur_level_cmd_s16(0x6);
+ auto posZ = cur_level_cmd_s16(0x8);
+ auto strength = cur_level_cmd_s16(0xA);
+ arguments.emplace_back(index);
+ arguments.emplace_back(condition);
+ arguments.emplace_back(posX);
+ arguments.emplace_back(posY);
+ arguments.emplace_back(posZ);
+ arguments.emplace_back(strength);
+ cmd += 0xC << CMD_SIZE_SHIFT;
+ break;
+ }
+ case LevelOpcode::GET_OR_SET: {
+ auto op = cur_level_cmd_u8(0x2);
+ auto var = cur_level_cmd_u8(0x3);
+ arguments.emplace_back(op);
+ arguments.emplace_back(var);
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ default:
+ throw std::runtime_error("Unknown Level Opcode");
+ }
+
+ commands.emplace_back(opcode, arguments);
+
+ if (commands.size() == count) {
+ // This condition is needed for malformed commands which have hardcoded jumps
+ // By default, commands sie will always be greater than count
+ break;
+ }
+ }
+
+ return std::make_shared<SM64::LevelScriptData>(commands);
+}
diff --git a/src/factories/sm64/LevelScriptFactory.h b/src/factories/sm64/LevelScriptFactory.h
new file mode 100644
index 0000000..460105f
--- /dev/null
+++ b/src/factories/sm64/LevelScriptFactory.h
@@ -0,0 +1,51 @@
+#pragma once
+
+#include "factories/BaseFactory.h"
+#include "level/LevelCommand.h"
+
+typedef std::variant<uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, float, uint64_t> LevelArgument;
+
+enum class LevelArgumentType {
+ U8, S8, U16, S16, U32, S32, F32, PTR
+};
+
+namespace SM64 {
+
+struct LevelCommand {
+ LevelOpcode opcode;
+ std::vector<LevelArgument> arguments;
+
+ LevelCommand(LevelOpcode opcode, std::vector<LevelArgument> arguments) : opcode(opcode), arguments(arguments) {}
+};
+
+class LevelScriptData : public IParsedData {
+public:
+ std::vector<LevelCommand> mCommands;
+
+ LevelScriptData(std::vector<LevelCommand> commands) : mCommands(std::move(commands)) {}
+};
+
+class LevelScriptHeaderExporter : public BaseExporter {
+ ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override;
+};
+
+class LevelScriptBinaryExporter : public BaseExporter {
+ ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override;
+};
+
+class LevelScriptCodeExporter : public BaseExporter {
+ ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override;
+};
+
+class LevelScriptFactory : public BaseFactory {
+public:
+ std::optional<std::shared_ptr<IParsedData>> parse(std::vector<uint8_t>& buffer, YAML::Node& data) override;
+ inline std::unordered_map<ExportType, std::shared_ptr<BaseExporter>> GetExporters() override {
+ return {
+ REGISTER(Code, LevelScriptCodeExporter)
+ REGISTER(Header, LevelScriptHeaderExporter)
+ REGISTER(Binary, LevelScriptBinaryExporter)
+ };
+ }
+};
+}
diff --git a/src/factories/sm64/level/LevelCommand.h b/src/factories/sm64/level/LevelCommand.h
new file mode 100644
index 0000000..8c19609
--- /dev/null
+++ b/src/factories/sm64/level/LevelCommand.h
@@ -0,0 +1,281 @@
+#pragma once
+
+#include "endianness.h"
+#include "n64/CommandMacros.h"
+#include <string>
+#include <spdlog/spdlog.h>
+#include <spdlog/fmt/ostr.h>
+#include <binarytools/BinaryWriter.h>
+#include <types/Vec3D.h>
+
+enum class LevelOpcode {
+ EXECUTE,
+ EXIT_AND_EXECUTE,
+ EXIT,
+ SLEEP,
+ SLEEP_BEFORE_EXIT,
+ JUMP,
+ JUMP_LINK,
+ RETURN,
+ JUMP_LINK_PUSH_ARG,
+ JUMP_N_TIMES,
+ LOOP_BEGIN,
+ LOOP_UNTIL,
+ JUMP_IF,
+ JUMP_LINK_IF,
+ SKIP_IF,
+ SKIP,
+ SKIP_NOP,
+ CALL,
+ CALL_LOOP,
+ SET_REG,
+ PUSH_POOL,
+ POP_POOL,
+ FIXED_LOAD,
+ LOAD_RAW,
+ LOAD_MIO0,
+ LOAD_MARIO_HEAD,
+ LOAD_MIO0_TEXTURE,
+ INIT_LEVEL,
+ CLEAR_LEVEL,
+ ALLOC_LEVEL_POOL,
+ FREE_LEVEL_POOL,
+ AREA,
+ END_AREA,
+ LOAD_MODEL_FROM_DL,
+ LOAD_MODEL_FROM_GEO,
+ CMD23,
+ OBJECT_WITH_ACTS,
+ MARIO,
+ WARP_NODE,
+ PAINTING_WARP_NODE,
+ INSTANT_WARP,
+ LOAD_AREA,
+ CMD2A,
+ MARIO_POS,
+ CMD2C,
+ CMD2D,
+ TERRAIN,
+ ROOMS,
+ SHOW_DIALOG,
+ TERRAIN_TYPE,
+ NOP,
+ TRANSITION,
+ BLACKOUT,
+ GAMMA,
+ SET_BACKGROUND_MUSIC,
+ SET_MENU_MUSIC,
+ STOP_MUSIC,
+ MACRO_OBJECTS,
+ CMD3A,
+ WHIRLPOOL,
+ GET_OR_SET,
+};
+
+inline std::ostream& operator<<(std::ostream& out, const LevelOpcode& opcode) {
+ std::string output;
+ switch (opcode) {
+ case LevelOpcode::EXECUTE:
+ output = "EXECUTE";
+ break;
+ case LevelOpcode::EXIT_AND_EXECUTE:
+ output = "EXIT_AND_EXECUTE";
+ break;
+ case LevelOpcode::EXIT:
+ output = "EXIT";
+ break;
+ case LevelOpcode::SLEEP:
+ output = "SLEEP";
+ break;
+ case LevelOpcode::SLEEP_BEFORE_EXIT:
+ output = "SLEEP_BEFORE_EXIT";
+ break;
+ case LevelOpcode::JUMP:
+ output = "JUMP";
+ break;
+ case LevelOpcode::JUMP_LINK:
+ output = "JUMP_LINK";
+ break;
+ case LevelOpcode::RETURN:
+ output = "RETURN";
+ break;
+ case LevelOpcode::JUMP_LINK_PUSH_ARG:
+ output = "JUMP_LINK_PUSH_ARG";
+ break;
+ case LevelOpcode::JUMP_N_TIMES:
+ output = "JUMP_N_TIMES";
+ break;
+ case LevelOpcode::LOOP_BEGIN:
+ output = "LOOP_BEGIN";
+ break;
+ case LevelOpcode::LOOP_UNTIL:
+ output = "LOOP_UNTIL";
+ break;
+ case LevelOpcode::JUMP_IF:
+ output = "JUMP_IF";
+ break;
+ case LevelOpcode::JUMP_LINK_IF:
+ output = "JUMP_LINK_IF";
+ break;
+ case LevelOpcode::SKIP_IF:
+ output = "SKIP_IF";
+ break;
+ case LevelOpcode::SKIP:
+ output = "SKIP";
+ break;
+ case LevelOpcode::SKIP_NOP:
+ output = "SKIP_NOP";
+ break;
+ case LevelOpcode::CALL:
+ output = "CALL";
+ break;
+ case LevelOpcode::CALL_LOOP:
+ output = "CALL_LOOP";
+ break;
+ case LevelOpcode::SET_REG:
+ output = "SET_REG";
+ break;
+ case LevelOpcode::PUSH_POOL:
+ output = "PUSH_POOL";
+ break;
+ case LevelOpcode::POP_POOL:
+ output = "POP_POOL";
+ break;
+ case LevelOpcode::FIXED_LOAD:
+ output = "FIXED_LOAD";
+ break;
+ case LevelOpcode::LOAD_RAW:
+ output = "LOAD_RAW";
+ break;
+ case LevelOpcode::LOAD_MIO0:
+ output = "LOAD_MIO0";
+ break;
+ case LevelOpcode::LOAD_MARIO_HEAD:
+ output = "LOAD_MARIO_HEAD";
+ break;
+ case LevelOpcode::LOAD_MIO0_TEXTURE:
+ output = "LOAD_MIO0_TEXTURE";
+ break;
+ case LevelOpcode::INIT_LEVEL:
+ output = "INIT_LEVEL";
+ break;
+ case LevelOpcode::CLEAR_LEVEL:
+ output = "CLEAR_LEVEL";
+ break;
+ case LevelOpcode::ALLOC_LEVEL_POOL:
+ output = "ALLOC_LEVEL_POOL";
+ break;
+ case LevelOpcode::FREE_LEVEL_POOL:
+ output = "FREE_LEVEL_POOL";
+ break;
+ case LevelOpcode::AREA:
+ output = "AREA";
+ break;
+ case LevelOpcode::END_AREA:
+ output = "END_AREA";
+ break;
+ case LevelOpcode::LOAD_MODEL_FROM_DL:
+ output = "LOAD_MODEL_FROM_DL";
+ break;
+ case LevelOpcode::LOAD_MODEL_FROM_GEO:
+ output = "LOAD_MODEL_FROM_GEO";
+ break;
+ case LevelOpcode::CMD23:
+ output = "CMD23";
+ break;
+ case LevelOpcode::OBJECT_WITH_ACTS:
+ output = "OBJECT_WITH_ACTS";
+ break;
+ case LevelOpcode::MARIO:
+ output = "MARIO";
+ break;
+ case LevelOpcode::WARP_NODE:
+ output = "WARP_NODE";
+ break;
+ case LevelOpcode::PAINTING_WARP_NODE:
+ output = "PAINTING_WARP_NODE";
+ break;
+ case LevelOpcode::INSTANT_WARP:
+ output = "INSTANT_WARP";
+ break;
+ case LevelOpcode::LOAD_AREA:
+ output = "LOAD_AREA";
+ break;
+ case LevelOpcode::CMD2A:
+ output = "CMD2A";
+ break;
+ case LevelOpcode::MARIO_POS:
+ output = "MARIO_POS";
+ break;
+ case LevelOpcode::CMD2C:
+ output = "CMD2C";
+ break;
+ case LevelOpcode::CMD2D:
+ output = "CMD2D";
+ break;
+ case LevelOpcode::TERRAIN:
+ output = "TERRAIN";
+ break;
+ case LevelOpcode::ROOMS:
+ output = "ROOMS";
+ break;
+ case LevelOpcode::SHOW_DIALOG:
+ output = "SHOW_DIALOG";
+ break;
+ case LevelOpcode::TERRAIN_TYPE:
+ output = "TERRAIN_TYPE";
+ break;
+ case LevelOpcode::NOP:
+ output = "NOP";
+ break;
+ case LevelOpcode::TRANSITION:
+ output = "TRANSITION";
+ break;
+ case LevelOpcode::BLACKOUT:
+ output = "BLACKOUT";
+ break;
+ case LevelOpcode::GAMMA:
+ output = "GAMMA";
+ break;
+ case LevelOpcode::SET_BACKGROUND_MUSIC:
+ output = "SET_BACKGROUND_MUSIC";
+ break;
+ case LevelOpcode::SET_MENU_MUSIC:
+ output = "SET_MENU_MUSIC";
+ break;
+ case LevelOpcode::STOP_MUSIC:
+ output = "STOP_MUSIC";
+ break;
+ case LevelOpcode::MACRO_OBJECTS:
+ output = "MACRO_OBJECTS";
+ break;
+ case LevelOpcode::CMD3A:
+ output = "CMD3A";
+ break;
+ case LevelOpcode::WHIRLPOOL:
+ output = "WHIRLPOOL";
+ break;
+ case LevelOpcode::GET_OR_SET:
+ output = "GET_OR_SET";
+ break;
+ default:
+ throw std::runtime_error("Unknown Level Opcode");
+ }
+
+ return out << output;
+}
+
+#define cur_level_cmd_u8(offset) \
+ (cmd[CMD_PROCESS_OFFSET(offset)])
+
+#define cur_level_cmd_s16(offset) \
+ (int16_t)BSWAP16((*(int16_t *) &cmd[CMD_PROCESS_OFFSET(offset)]))
+
+#define cur_level_cmd_s32(offset) \
+ (int32_t)BSWAP32((*(int32_t *) &cmd[CMD_PROCESS_OFFSET(offset)]))
+
+#define cur_level_cmd_u32(offset) \
+ (uint32_t) BSWAP32((*(uint32_t *) &cmd[CMD_PROCESS_OFFSET(offset)]))
+
+#define cur_level_cmd_f32(offset) \
+ (float) BSWAP32((*(float *) &cmd[CMD_PROCESS_OFFSET(offset)]))