summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorinspectredc <inspectredc@gmail.com>2024-04-25 17:32:49 +0100
committerLywx <kiritodev01@gmail.com>2024-04-25 10:41:49 -0600
commitceca1c3e45995a0b43fac2a292634ec6063d7b83 (patch)
tree5f98d62e52a920292adcf909a7785008cc4cea47
parente8d296c3e197d4f3280ccec515513c5a373ee9fb (diff)
Behavior Script Factory
-rw-r--r--src/Companion.cpp2
-rw-r--r--src/factories/ResourceType.h23
-rw-r--r--src/factories/sm64/BehaviorScriptFactory.cpp489
-rw-r--r--src/factories/sm64/BehaviorScriptFactory.h51
-rw-r--r--src/factories/sm64/behavior/BehaviorCommand.h261
5 files changed, 815 insertions, 11 deletions
diff --git a/src/Companion.cpp b/src/Companion.cpp
index bc218b5..9d8f3b1 100644
--- a/src/Companion.cpp
+++ b/src/Companion.cpp
@@ -30,6 +30,7 @@
#include "factories/AssetArrayFactory.h"
#include "factories/sm64/AnimationFactory.h"
+#include "factories/sm64/BehaviorScriptFactory.h"
#include "factories/sm64/CollisionFactory.h"
#include "factories/sm64/DialogFactory.h"
#include "factories/sm64/DictionaryFactory.h"
@@ -97,6 +98,7 @@ void Companion::Init(const ExportType type) {
this->RegisterFactory("SM64:TEXT", std::make_shared<SM64::TextFactory>());
this->RegisterFactory("SM64:DICTIONARY", std::make_shared<SM64::DictionaryFactory>());
this->RegisterFactory("SM64:ANIM", std::make_shared<SM64::AnimationFactory>());
+ this->RegisterFactory("SM64:BEHAVIOR_SCRIPT", std::make_shared<SM64::TrajectoryFactory>());
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>());
diff --git a/src/factories/ResourceType.h b/src/factories/ResourceType.h
index 98c8cf5..6ba4b1d 100644
--- a/src/factories/ResourceType.h
+++ b/src/factories/ResourceType.h
@@ -25,17 +25,18 @@ enum class ResourceType {
AssetArray = 0x41415252, // AARR
// SM64
- Anim = 0x414E494D, // ANIM
- SDialog = 0x53444C47, // SDLG
- Dictionary = 0x44494354, // DICT
- GeoLayout = 0x47454F20, // GEO
- Collision = 0x434F4C20, // COL
- LevelScript = 0x4C564C53, // LVLS
- MacroObject = 0x4D41434F, // MACO
- Movtex = 0x4D4F5654, // MOVT
- MovtexQuad = 0x4D4F5651, // MOVQ
- PaintingData = 0x5041494E, // PAIN
- Trajectory = 0x5452414A, // TRAJ
+ Anim = 0x414E494D, // ANIM
+ BehaviorScript = 0x42485653, // BHVS
+ SDialog = 0x53444C47, // SDLG
+ Dictionary = 0x44494354, // DICT
+ GeoLayout = 0x47454F20, // GEO
+ Collision = 0x434F4C20, // COL
+ LevelScript = 0x4C564C53, // LVLS
+ MacroObject = 0x4D41434F, // MACO
+ Movtex = 0x4D4F5654, // MOVT
+ MovtexQuad = 0x4D4F5651, // MOVQ
+ PaintingData = 0x5041494E, // PAIN
+ Trajectory = 0x5452414A, // TRAJ
// MK64
CourseVertex = 0x43565458, // CVTX
diff --git a/src/factories/sm64/BehaviorScriptFactory.cpp b/src/factories/sm64/BehaviorScriptFactory.cpp
new file mode 100644
index 0000000..6cbcd3e
--- /dev/null
+++ b/src/factories/sm64/BehaviorScriptFactory.cpp
@@ -0,0 +1,489 @@
+#include "BehaviorScriptFactory.h"
+#include "spdlog/spdlog.h"
+
+#include "Companion.h"
+#include "utils/Decompressor.h"
+#include "utils/TorchUtils.h"
+#include <regex>
+
+ExportResult SM64::BehaviorScriptHeaderExporter::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 BehaviorScript " << symbol << "[];\n";
+ return std::nullopt;
+}
+
+ExportResult SM64::BehaviorScriptCodeExporter::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<BehaviorScriptData>(raw)->mCommands;
+ uint32_t indentCount = 1;
+
+ write << "static const BehaviorScript " << symbol << "[] = {\n";
+
+ for(auto& [opcode, arguments] : commands) {
+ bool commaFlag = false;
+
+ for (uint32_t i = 0; i < indentCount; ++i) {
+ write << fourSpaceTab;
+ }
+
+ if (opcode == BehaviorOpcode::BEGIN_LOOP || opcode == BehaviorOpcode::BEGIN_REPEAT) {
+ ++indentCount;
+ } else if (opcode == BehaviorOpcode::END_LOOP || opcode == BehaviorOpcode::END_REPEAT || opcode == BehaviorOpcode::END_REPEAT_CONTINUE) {
+ --indentCount;
+ }
+
+ write << opcode << "(";
+ for(auto& args : arguments) {
+ if (commaFlag) {
+ write << ", ";
+ } else {
+ commaFlag = true;
+ }
+
+ switch(static_cast<BehaviorArgumentType>(args.index())) {
+ case BehaviorArgumentType::U8: {
+ write << std::hex << "0x" << static_cast<uint32_t>(std::get<uint8_t>(args));
+ break;
+ }
+ case BehaviorArgumentType::S8: {
+ write << std::hex << "0x" << static_cast<uint32_t>(std::get<int8_t>(args));
+ break;
+ }
+ case BehaviorArgumentType::U16: {
+ write << std::hex << "0x" << std::get<uint16_t>(args);
+ break;
+ }
+ case BehaviorArgumentType::S16: {
+ write << std::dec << std::get<int16_t>(args);
+ break;
+ }
+ case BehaviorArgumentType::U32: {
+ write << std::hex << "0x" << std::get<uint32_t>(args);
+ break;
+ }
+ case BehaviorArgumentType::S32: {
+ write << std::dec << std::get<int32_t>(args);
+ break;
+ }
+ case BehaviorArgumentType::F32: {
+ write << std::dec << std::get<float>(args);
+ break;
+ }
+ case BehaviorArgumentType::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 Behavior Argument Type");
+ }
+ }
+ }
+ write << "),\n";
+ }
+
+ write << "\n};\n";
+
+ size_t size = 0;
+
+ return offset + size;
+}
+
+ExportResult SM64::BehaviorScriptBinaryExporter::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<BehaviorScriptData>(raw)->mCommands;
+
+ WriteHeader(writer, LUS::ResourceType::BehaviorScript, 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<BehaviorArgumentType>(args.index())) {
+ case BehaviorArgumentType::U8: {
+ writer.Write(std::get<uint8_t>(args));
+ break;
+ }
+ case BehaviorArgumentType::S8: {
+ writer.Write(std::get<int8_t>(args));
+ break;
+ }
+ case BehaviorArgumentType::U16: {
+ writer.Write(std::get<uint16_t>(args));
+ break;
+ }
+ case BehaviorArgumentType::S16: {
+ writer.Write(std::get<int16_t>(args));
+ break;
+ }
+ case BehaviorArgumentType::U32: {
+ writer.Write(std::get<uint32_t>(args));
+ break;
+ }
+ case BehaviorArgumentType::S32: {
+ writer.Write(std::get<int32_t>(args));
+ break;
+ }
+ case BehaviorArgumentType::F32: {
+ writer.Write(std::get<float>(args));
+ break;
+ }
+ case BehaviorArgumentType::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 Behavior Argument Type");
+ }
+ }
+ }
+ }
+
+ writer.Finish(write);
+ return std::nullopt;
+}
+
+std::optional<std::shared_ptr<IParsedData>> SM64::BehaviorScriptFactory::parse(std::vector<uint8_t>& buffer, YAML::Node& node) {
+ auto [_, segment] = Decompressor::AutoDecode(node, buffer);
+ auto cmd = segment.data;
+ bool processing = true;
+ std::vector<BehaviorCommand> commands;
+
+ while(processing) {
+ auto opcode = static_cast<BehaviorOpcode>(cmd[0x00]);
+
+ SPDLOG_INFO("Processing Command {}", opcode);
+
+ std::vector<BehaviorArgument> arguments;
+
+ switch (opcode) {
+ case BehaviorOpcode::BEGIN: {
+ auto objList = cur_behavior_cmd_u8(0x01);
+ arguments.emplace_back(objList);
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::DELAY: {
+ auto num = cur_behavior_cmd_u8(0x01);
+ arguments.emplace_back(num);
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::CALL: {
+ uint64_t addr = cur_behavior_cmd_u32(0x04);
+ arguments.emplace_back(addr);
+ cmd += 0x8 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::RETURN: {
+ processing = false;
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::GOTO: {
+ uint64_t addr = cur_behavior_cmd_u32(0x04);
+ arguments.emplace_back(addr);
+ cmd += 0x8 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::BEGIN_REPEAT: {
+ auto count = cur_behavior_cmd_s16(0x02);
+ arguments.emplace_back(count);
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::END_REPEAT:
+ case BehaviorOpcode::END_REPEAT_CONTINUE:
+ case BehaviorOpcode::BEGIN_LOOP: {
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::END_LOOP:
+ case BehaviorOpcode::BREAK:
+ case BehaviorOpcode::BREAK_UNUSED: {
+ processing = false;
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::CALL_NATIVE: {
+ auto func = cur_behavior_cmd_u32(0x04);
+ arguments.emplace_back(func);
+ cmd += 0x8 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::ADD_FLOAT:
+ case BehaviorOpcode::SET_FLOAT:
+ case BehaviorOpcode::ADD_INT:
+ case BehaviorOpcode::SET_INT:
+ case BehaviorOpcode::OR_INT:
+ case BehaviorOpcode::BIT_CLEAR: {
+ auto field = cur_behavior_cmd_u8(0x01);
+ auto value = cur_behavior_cmd_s16(0x02);
+ arguments.emplace_back(field);
+ arguments.emplace_back(value);
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::SET_INT_RAND_RSHIFT:
+ case BehaviorOpcode::SET_RANDOM_FLOAT:
+ case BehaviorOpcode::SET_RANDOM_INT:
+ case BehaviorOpcode::ADD_RANDOM_FLOAT:
+ case BehaviorOpcode::ADD_INT_RAND_RSHIFT: {
+ auto field = cur_behavior_cmd_u8(0x01);
+ auto min = cur_behavior_cmd_s16(0x02);
+ auto rangeRShift = cur_behavior_cmd_s16(0x04);
+ arguments.emplace_back(field);
+ arguments.emplace_back(min);
+ arguments.emplace_back(rangeRShift);
+ cmd += 0x8 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::CMD_NOP_1:
+ case BehaviorOpcode::CMD_NOP_2:
+ case BehaviorOpcode::CMD_NOP_3: {
+ auto field = cur_behavior_cmd_u8(0x01);
+ arguments.emplace_back(field);
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::SET_MODEL: {
+ auto model = cur_behavior_cmd_s16(0x02);
+ arguments.emplace_back(model);
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::SPAWN_CHILD: {
+ auto modelId = cur_behavior_cmd_s32(0x04);
+ uint64_t behavior = cur_behavior_cmd_u32(0x08);
+ arguments.emplace_back(modelId);
+ arguments.emplace_back(behavior);
+ cmd += 0xC << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::DEACTIVATE: {
+ processing = false;
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::DROP_TO_FLOOR: {
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::SUM_FLOAT:
+ case BehaviorOpcode::SUM_INT: {
+ auto fieldDst = cur_behavior_cmd_u8(0x01);
+ auto fieldSrc1 = cur_behavior_cmd_u8(0x02);
+ auto fieldSrc2 = cur_behavior_cmd_u8(0x03);
+ arguments.emplace_back(fieldDst);
+ arguments.emplace_back(fieldSrc1);
+ arguments.emplace_back(fieldSrc2);
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::BILLBOARD:
+ case BehaviorOpcode::HIDE: {
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::SET_HITBOX: {
+ auto radius = cur_behavior_cmd_s16(0x04);
+ auto height = cur_behavior_cmd_s16(0x06);
+ arguments.emplace_back(radius);
+ arguments.emplace_back(height);
+ cmd += 0x8 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::CMD_NOP_4: {
+ auto field = cur_behavior_cmd_u8(0x01);
+ auto value = cur_behavior_cmd_s16(0x02);
+ arguments.emplace_back(field);
+ arguments.emplace_back(value);
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::DELAY_VAR: {
+ auto field = cur_behavior_cmd_u8(0x01);
+ arguments.emplace_back(field);
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::BEGIN_REPEAT_UNUSED: {
+ auto count = cur_behavior_cmd_u8(0x01);
+ arguments.emplace_back(count);
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::LOAD_ANIMATIONS: {
+ auto field = cur_behavior_cmd_u8(0x01);
+ uint64_t anims = cur_behavior_cmd_u32(0x04);
+ arguments.emplace_back(field);
+ arguments.emplace_back(anims);
+ cmd += 0x8 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::ANIMATE: {
+ auto animIndex = cur_behavior_cmd_u8(0x01);
+ arguments.emplace_back(animIndex);
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::SPAWN_CHILD_WITH_PARAM: {
+ auto bhvParam = cur_behavior_cmd_u8(0x01);
+ auto modelId = cur_behavior_cmd_s32(0x04);
+ uint64_t behavior = cur_behavior_cmd_u32(0x08);
+ arguments.emplace_back(bhvParam);
+ arguments.emplace_back(modelId);
+ arguments.emplace_back(behavior);
+ cmd += 0xC << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::LOAD_COLLISION_DATA: {
+ uint64_t collisionData = cur_behavior_cmd_u32(0x04);
+ arguments.emplace_back(collisionData);
+ cmd += 0x8 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::SET_HITBOX_WITH_OFFSET: {
+ auto radius = cur_behavior_cmd_s16(0x04);
+ auto height = cur_behavior_cmd_s16(0x06);
+ auto downOffset = cur_behavior_cmd_s16(0x08);
+ arguments.emplace_back(radius);
+ arguments.emplace_back(height);
+ arguments.emplace_back(downOffset);
+ cmd += 0xC << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::SPAWN_OBJ: {
+ auto modelId = cur_behavior_cmd_s32(0x04);
+ uint64_t behavior = cur_behavior_cmd_u32(0x08);
+ arguments.emplace_back(modelId);
+ arguments.emplace_back(behavior);
+ cmd += 0xC << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::SET_HOME: {
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::SET_HURTBOX: {
+ auto radius = cur_behavior_cmd_s16(0x04);
+ auto height = cur_behavior_cmd_s16(0x06);
+ arguments.emplace_back(radius);
+ arguments.emplace_back(height);
+ cmd += 0x8 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::SET_INTERACT_TYPE: {
+ auto type = cur_behavior_cmd_s32(0x04);
+ arguments.emplace_back(type);
+ cmd += 0x8 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::SET_OBJ_PHYSICS: {
+ auto wallHitboxRadius = cur_behavior_cmd_s16(0x04);
+ auto gravity = cur_behavior_cmd_s16(0x06);
+ auto bounciness = cur_behavior_cmd_s16(0x08);
+ auto dragStrength = cur_behavior_cmd_s16(0x0A);
+ auto friction = cur_behavior_cmd_s16(0x0C);
+ auto buoyancy = cur_behavior_cmd_s16(0x0E);
+ auto unused1 = cur_behavior_cmd_s16(0x10);
+ auto unused2 = cur_behavior_cmd_s16(0x12);
+ arguments.emplace_back(wallHitboxRadius);
+ arguments.emplace_back(gravity);
+ arguments.emplace_back(bounciness);
+ arguments.emplace_back(dragStrength);
+ arguments.emplace_back(friction);
+ arguments.emplace_back(buoyancy);
+ arguments.emplace_back(unused1);
+ arguments.emplace_back(unused2);
+ cmd += 0x14 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::SET_INTERACT_SUBTYPE: {
+ auto subType = cur_behavior_cmd_s32(0x04);
+ arguments.emplace_back(subType);
+ cmd += 0x8 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::SCALE: {
+ auto unusedField = cur_behavior_cmd_u8(0x01);
+ auto percent = cur_behavior_cmd_s16(0x02);
+ arguments.emplace_back(unusedField);
+ arguments.emplace_back(percent);
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::PARENT_BIT_CLEAR: {
+ auto field = cur_behavior_cmd_u8(0x01);
+ auto flags = cur_behavior_cmd_s32(0x04);
+ arguments.emplace_back(field);
+ arguments.emplace_back(flags);
+ cmd += 0x8 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::ANIMATE_TEXTURE: {
+ auto field = cur_behavior_cmd_u8(0x01);
+ auto rate = cur_behavior_cmd_s16(0x02);
+ arguments.emplace_back(field);
+ arguments.emplace_back(rate);
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::DISABLE_RENDERING: {
+ cmd += 0x4 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::SET_INT_UNUSED: {
+ auto field = cur_behavior_cmd_u8(0x01);
+ auto value = cur_behavior_cmd_s16(0x06);
+ arguments.emplace_back(field);
+ arguments.emplace_back(value);
+ cmd += 0x8 << CMD_SIZE_SHIFT;
+ break;
+ }
+ case BehaviorOpcode::SPAWN_WATER_DROPLET: {
+ uint64_t dropletParams = cur_behavior_cmd_s32(0x04);
+ arguments.emplace_back(dropletParams);
+ cmd += 0x8 << CMD_SIZE_SHIFT;
+ break;
+ }
+ default:
+ throw std::runtime_error("Unknown Behavior Opcode");
+ }
+
+ commands.emplace_back(opcode, arguments);
+ }
+
+ return std::make_shared<SM64::BehaviorScriptData>(commands);
+}
diff --git a/src/factories/sm64/BehaviorScriptFactory.h b/src/factories/sm64/BehaviorScriptFactory.h
new file mode 100644
index 0000000..d1c451a
--- /dev/null
+++ b/src/factories/sm64/BehaviorScriptFactory.h
@@ -0,0 +1,51 @@
+#pragma once
+
+#include "factories/BaseFactory.h"
+#include "behavior/BehaviorCommand.h"
+
+typedef std::variant<uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, float, uint64_t> BehaviorArgument;
+
+enum class BehaviorArgumentType {
+ U8, S8, U16, S16, U32, S32, F32, PTR
+};
+
+namespace SM64 {
+
+struct BehaviorCommand {
+ BehaviorOpcode opcode;
+ std::vector<BehaviorArgument> arguments;
+
+ BehaviorCommand(BehaviorOpcode opcode, std::vector<BehaviorArgument> arguments) : opcode(opcode), arguments(arguments) {}
+};
+
+class BehaviorScriptData : public IParsedData {
+public:
+ std::vector<BehaviorCommand> mCommands;
+
+ BehaviorScriptData(std::vector<BehaviorCommand> commands) : mCommands(std::move(commands)) {}
+};
+
+class BehaviorScriptHeaderExporter : public BaseExporter {
+ ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override;
+};
+
+class BehaviorScriptBinaryExporter : public BaseExporter {
+ ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override;
+};
+
+class BehaviorScriptCodeExporter : public BaseExporter {
+ ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override;
+};
+
+class BehaviorScriptFactory : 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, BehaviorScriptCodeExporter)
+ REGISTER(Header, BehaviorScriptHeaderExporter)
+ REGISTER(Binary, BehaviorScriptBinaryExporter)
+ };
+ }
+};
+}
diff --git a/src/factories/sm64/behavior/BehaviorCommand.h b/src/factories/sm64/behavior/BehaviorCommand.h
new file mode 100644
index 0000000..6effad3
--- /dev/null
+++ b/src/factories/sm64/behavior/BehaviorCommand.h
@@ -0,0 +1,261 @@
+#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 BehaviorOpcode {
+ BEGIN,
+ DELAY,
+ CALL,
+ RETURN,
+ GOTO,
+ BEGIN_REPEAT,
+ END_REPEAT,
+ END_REPEAT_CONTINUE,
+ BEGIN_LOOP,
+ END_LOOP,
+ BREAK,
+ BREAK_UNUSED,
+ CALL_NATIVE,
+ ADD_FLOAT,
+ SET_FLOAT,
+ ADD_INT,
+ SET_INT,
+ OR_INT,
+ BIT_CLEAR,
+ SET_INT_RAND_RSHIFT,
+ SET_RANDOM_FLOAT,
+ SET_RANDOM_INT,
+ ADD_RANDOM_FLOAT,
+ ADD_INT_RAND_RSHIFT,
+ CMD_NOP_1,
+ CMD_NOP_2,
+ CMD_NOP_3,
+ SET_MODEL,
+ SPAWN_CHILD,
+ DEACTIVATE,
+ DROP_TO_FLOOR,
+ SUM_FLOAT,
+ SUM_INT,
+ BILLBOARD,
+ HIDE,
+ SET_HITBOX,
+ CMD_NOP_4,
+ DELAY_VAR,
+ BEGIN_REPEAT_UNUSED,
+ LOAD_ANIMATIONS,
+ ANIMATE,
+ SPAWN_CHILD_WITH_PARAM,
+ LOAD_COLLISION_DATA,
+ SET_HITBOX_WITH_OFFSET,
+ SPAWN_OBJ,
+ SET_HOME,
+ SET_HURTBOX,
+ SET_INTERACT_TYPE,
+ SET_OBJ_PHYSICS,
+ SET_INTERACT_SUBTYPE,
+ SCALE,
+ PARENT_BIT_CLEAR,
+ ANIMATE_TEXTURE,
+ DISABLE_RENDERING,
+ SET_INT_UNUSED,
+ SPAWN_WATER_DROPLET,
+};
+
+inline std::ostream& operator<<(std::ostream& out, const BehaviorOpcode& opcode) {
+ std::string output;
+ switch (opcode) {
+ case BehaviorOpcode::BEGIN:
+ output = "BEGIN";
+ break;
+ case BehaviorOpcode::DELAY:
+ output = "DELAY";
+ break;
+ case BehaviorOpcode::CALL:
+ output = "CALL";
+ break;
+ case BehaviorOpcode::RETURN:
+ output = "RETURN";
+ break;
+ case BehaviorOpcode::GOTO:
+ output = "GOTO";
+ break;
+ case BehaviorOpcode::BEGIN_REPEAT:
+ output = "BEGIN_REPEAT";
+ break;
+ case BehaviorOpcode::END_REPEAT:
+ output = "END_REPEAT";
+ break;
+ case BehaviorOpcode::END_REPEAT_CONTINUE:
+ output = "END_REPEAT_CONTINUE";
+ break;
+ case BehaviorOpcode::BEGIN_LOOP:
+ output = "BEGIN_LOOP";
+ break;
+ case BehaviorOpcode::END_LOOP:
+ output = "END_LOOP";
+ break;
+ case BehaviorOpcode::BREAK:
+ output = "BREAK";
+ break;
+ case BehaviorOpcode::BREAK_UNUSED:
+ output = "BREAK_UNUSED";
+ break;
+ case BehaviorOpcode::CALL_NATIVE:
+ output = "CALL_NATIVE";
+ break;
+ case BehaviorOpcode::ADD_FLOAT:
+ output = "ADD_FLOAT";
+ break;
+ case BehaviorOpcode::SET_FLOAT:
+ output = "SET_FLOAT";
+ break;
+ case BehaviorOpcode::ADD_INT:
+ output = "ADD_INT";
+ break;
+ case BehaviorOpcode::SET_INT:
+ output = "SET_INT";
+ break;
+ case BehaviorOpcode::OR_INT:
+ output = "OR_INT";
+ break;
+ case BehaviorOpcode::BIT_CLEAR:
+ output = "BIT_CLEAR";
+ break;
+ case BehaviorOpcode::SET_INT_RAND_RSHIFT:
+ output = "SET_INT_RAND_RSHIFT";
+ break;
+ case BehaviorOpcode::SET_RANDOM_FLOAT:
+ output = "SET_RANDOM_FLOAT";
+ break;
+ case BehaviorOpcode::SET_RANDOM_INT:
+ output = "SET_RANDOM_INT";
+ break;
+ case BehaviorOpcode::ADD_RANDOM_FLOAT:
+ output = "ADD_RANDOM_FLOAT";
+ break;
+ case BehaviorOpcode::ADD_INT_RAND_RSHIFT:
+ output = "ADD_INT_RAND_RSHIFT";
+ break;
+ case BehaviorOpcode::CMD_NOP_1:
+ output = "CMD_NOP_1";
+ break;
+ case BehaviorOpcode::CMD_NOP_2:
+ output = "CMD_NOP_2";
+ break;
+ case BehaviorOpcode::CMD_NOP_3:
+ output = "CMD_NOP_3";
+ break;
+ case BehaviorOpcode::SET_MODEL:
+ output = "SET_MODEL";
+ break;
+ case BehaviorOpcode::SPAWN_CHILD:
+ output = "SPAWN_CHILD";
+ break;
+ case BehaviorOpcode::DEACTIVATE:
+ output = "DEACTIVATE";
+ break;
+ case BehaviorOpcode::DROP_TO_FLOOR:
+ output = "DROP_TO_FLOOR";
+ break;
+ case BehaviorOpcode::SUM_FLOAT:
+ output = "SUM_FLOAT";
+ break;
+ case BehaviorOpcode::SUM_INT:
+ output = "SUM_INT";
+ break;
+ case BehaviorOpcode::BILLBOARD:
+ output = "BILLBOARD";
+ break;
+ case BehaviorOpcode::HIDE:
+ output = "HIDE";
+ break;
+ case BehaviorOpcode::SET_HITBOX:
+ output = "SET_HITBOX";
+ break;
+ case BehaviorOpcode::CMD_NOP_4:
+ output = "CMD_NOP_4";
+ break;
+ case BehaviorOpcode::DELAY_VAR:
+ output = "DELAY_VAR";
+ break;
+ case BehaviorOpcode::BEGIN_REPEAT_UNUSED:
+ output = "BEGIN_REPEAT_UNUSED";
+ break;
+ case BehaviorOpcode::LOAD_ANIMATIONS:
+ output = "LOAD_ANIMATIONS";
+ break;
+ case BehaviorOpcode::ANIMATE:
+ output = "ANIMATE";
+ break;
+ case BehaviorOpcode::SPAWN_CHILD_WITH_PARAM:
+ output = "SPAWN_CHILD_WITH_PARAM";
+ break;
+ case BehaviorOpcode::LOAD_COLLISION_DATA:
+ output = "LOAD_COLLISION_DATA";
+ break;
+ case BehaviorOpcode::SET_HITBOX_WITH_OFFSET:
+ output = "SET_HITBOX_WITH_OFFSET";
+ break;
+ case BehaviorOpcode::SPAWN_OBJ:
+ output = "SPAWN_OBJ";
+ break;
+ case BehaviorOpcode::SET_HOME:
+ output = "SET_HOME";
+ break;
+ case BehaviorOpcode::SET_HURTBOX:
+ output = "SET_HURTBOX";
+ break;
+ case BehaviorOpcode::SET_INTERACT_TYPE:
+ output = "SET_INTERACT_TYPE";
+ break;
+ case BehaviorOpcode::SET_OBJ_PHYSICS:
+ output = "SET_OBJ_PHYSICS";
+ break;
+ case BehaviorOpcode::SET_INTERACT_SUBTYPE:
+ output = "SET_INTERACT_SUBTYPE";
+ break;
+ case BehaviorOpcode::SCALE:
+ output = "SCALE";
+ break;
+ case BehaviorOpcode::PARENT_BIT_CLEAR:
+ output = "PARENT_BIT_CLEAR";
+ break;
+ case BehaviorOpcode::ANIMATE_TEXTURE:
+ output = "ANIMATE_TEXTURE";
+ break;
+ case BehaviorOpcode::DISABLE_RENDERING:
+ output = "DISABLE_RENDERING";
+ break;
+ case BehaviorOpcode::SET_INT_UNUSED:
+ output = "SET_INT_UNUSED";
+ break;
+ case BehaviorOpcode::SPAWN_WATER_DROPLET:
+ output = "SPAWN_WATER_DROPLET";
+ break;
+ default:
+ throw std::runtime_error("Unknown Behavior Opcode");
+ }
+
+ return out << output;
+}
+
+#define cur_behavior_cmd_u8(offset) \
+ (cmd[CMD_PROCESS_OFFSET(offset)])
+
+#define cur_behavior_cmd_s16(offset) \
+ (int16_t)BSWAP16((*(int16_t *) &cmd[CMD_PROCESS_OFFSET(offset)]))
+
+#define cur_behavior_cmd_s32(offset) \
+ (int32_t)BSWAP32((*(int32_t *) &cmd[CMD_PROCESS_OFFSET(offset)]))
+
+#define cur_behavior_cmd_u32(offset) \
+ (uint32_t) BSWAP32((*(uint32_t *) &cmd[CMD_PROCESS_OFFSET(offset)]))
+
+#define cur_behavior_cmd_f32(offset) \
+ (float) BSWAP32((*(float *) &cmd[CMD_PROCESS_OFFSET(offset)]))