diff options
| author | inspectredc <inspectredc@gmail.com> | 2024-04-25 15:52:32 +0100 |
|---|---|---|
| committer | Lywx <kiritodev01@gmail.com> | 2024-04-25 09:40:57 -0600 |
| commit | 09e8620f7584c1ba7c7ed688fbeecf10dace94fe (patch) | |
| tree | 29e4cfe75fd007871850fc7711d0f0d9f4cdfe0c /src | |
| parent | df54367b676f44d2b925e681ae05eef665f7c45f (diff) | |
LevelScript Factory
Diffstat (limited to 'src')
| -rw-r--r-- | src/Companion.cpp | 2 | ||||
| -rw-r--r-- | src/factories/ResourceType.h | 1 | ||||
| -rw-r--r-- | src/factories/sm64/LevelScriptFactory.cpp | 654 | ||||
| -rw-r--r-- | src/factories/sm64/LevelScriptFactory.h | 51 | ||||
| -rw-r--r-- | src/factories/sm64/level/LevelCommand.h | 281 |
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)])) |
