diff --git a/CMakeLists.txt b/CMakeLists.txt
index d7883392b80..127159a25ad 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -216,6 +216,8 @@ add_library(${PROJECT_NAME} STATIC
src/logo.h
src/main_data.cpp
src/main_data.h
+ src/maniac_patch.cpp
+ src/maniac_patch.h
src/map_data.h
src/memory_management.h
src/message_overlay.cpp
diff --git a/Makefile.am b/Makefile.am
index b71c4191fd4..57144b3a7aa 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -202,6 +202,8 @@ libeasyrpg_player_a_SOURCES = \
src/logo.h \
src/main_data.cpp \
src/main_data.h \
+ src/maniac_patch.cpp \
+ src/maniac_patch.h \
src/map_data.h \
src/memory_management.h \
src/message_overlay.cpp \
diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp
index f842603ebf3..f9e99be61e6 100644
--- a/src/game_interpreter.cpp
+++ b/src/game_interpreter.cpp
@@ -40,6 +40,7 @@
#include "game_message.h"
#include "game_pictures.h"
#include "game_screen.h"
+#include "maniac_patch.h"
#include "spriteset_map.h"
#include "sprite_character.h"
#include "scene_gameover.h"
@@ -1499,8 +1500,8 @@ bool Game_Interpreter::CommandControlVariables(lcf::rpg::EventCommand const& com
}
case 21:
// Expression (Maniac)
- Output::Warning("ControlVariables: Maniac Patch expressions not supported");
- return true;
+ value = ManiacPatch::ParseExpression(MakeSpan(com.parameters).subspan(6, com.parameters[5]));
+ break;
default:
Output::Warning("ControlVariables: Unsupported operand {}", operand);
return true;
@@ -1529,8 +1530,9 @@ bool Game_Interpreter::CommandControlVariables(lcf::rpg::EventCommand const& com
end = Main_Data::game_variables->Get(com.parameters[2]);
} else if (target == 4 && Player::IsPatchManiac()) {
// Expression (Maniac)
- Output::Warning("ControlVariables: Maniac Patch expressions not supported");
- return true;
+ int idx = com.parameters[1];
+ start = ManiacPatch::ParseExpression(MakeSpan(com.parameters).subspan(idx + 1, com.parameters[idx]));
+ end = start;
} else {
return true;
}
diff --git a/src/maniac_patch.cpp b/src/maniac_patch.cpp
new file mode 100644
index 00000000000..59ab7fece57
--- /dev/null
+++ b/src/maniac_patch.cpp
@@ -0,0 +1,235 @@
+/*
+ * This file is part of EasyRPG Player.
+ *
+ * EasyRPG Player is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * EasyRPG Player is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with EasyRPG Player. If not, see .
+ */
+
+#include "maniac_patch.h"
+#include "main_data.h"
+#include "game_switches.h"
+#include "game_variables.h"
+#include "output.h"
+
+#include
+
+namespace {
+ enum class Op {
+ Null = 0,
+ U8,
+ U16,
+ S32,
+ UX8,
+ UX16,
+ SX32,
+ Var = 8,
+ Switch,
+ VarIndirect = 13,
+ SwitchIndirect,
+ Array = 19,
+ Negate = 24,
+ Not,
+ Flip,
+ AssignInplace = 34,
+ AddInplace,
+ SubInplace,
+ MulInplace,
+ DivInplace,
+ ModInplace,
+ BitOrInplace,
+ BitAndInplace,
+ BitXorInplace,
+ BitShiftLeftInplace,
+ BitShiftRightInplace,
+ Add = 48,
+ Sub,
+ Mul,
+ Div,
+ Mod,
+ BitOr,
+ BitAnd,
+ BitXor,
+ BitShiftLeft,
+ BitShiftRight,
+ Equal,
+ GreaterEqual,
+ LessEqual,
+ Greater,
+ Less,
+ NotEqual,
+ Or,
+ And,
+ Range,
+ Subscript,
+ Ternary = 72,
+ Function = 78
+ };
+
+ using Result = std::tuple::iterator, int>;
+}
+
+Result process(std::vector::iterator it, std::vector::iterator end) {
+ int value = 0;
+ int imm = 0;
+ int imm2 = 0;
+ int imm3 = 0;
+
+ auto op = static_cast(*it);
+ ++it;
+
+ // When entering the switch it is on the first argument
+ switch (op) {
+ case Op::Null:
+ return {++it, 0};
+ case Op::U8:
+ case Op::UX8:
+ value = *it++;
+ return {it, static_cast(value)};
+ case Op::U16:
+ case Op::UX16:
+ imm = *it++;
+ imm2 = *it++;
+ value = (imm2 << 8) + imm;
+ return {it, static_cast(value)};
+ case Op::S32:
+ case Op::SX32:
+ imm = *it++;
+ imm2 = *it++;
+ imm3 = *it++;
+ value = *it++;
+ value = (value << 24) + (imm3 << 16) + (imm2 << 8) + imm;
+ return {it, value};
+ case Op::Var:
+ std::tie(it, imm) = process(it, end);
+ return {it, Main_Data::game_variables->Get(imm)};
+ case Op::Switch:
+ std::tie(it, imm) = process(it, end);
+ return {it, Main_Data::game_switches->GetInt(imm)};
+ case Op::VarIndirect:
+ std::tie(it, imm) = process(it, end);
+ return {it, Main_Data::game_variables->GetIndirect(imm)};
+ case Op::SwitchIndirect:
+ std::tie(it, imm) = process(it, end);
+ return {it, Main_Data::game_switches->GetInt(Main_Data::game_variables->Get(imm))};
+ case Op::Negate:
+ std::tie(it, imm) = process(it, end);
+ return {it, -imm};
+ case Op::Not:
+ std::tie(it, imm) = process(it, end);
+ return {it, !imm ? 0 : 1};
+ case Op::Flip:
+ std::tie(it, imm) = process(it, end);
+ return {it, ~imm};
+ case Op::Add:
+ std::tie(it, imm) = process(it, end);
+ std::tie(it, imm2) = process(it, end);
+ return {it, imm + imm2};
+ case Op::Sub:
+ std::tie(it, imm) = process(it, end);
+ std::tie(it, imm2) = process(it, end);
+ return {it, imm - imm2};
+ case Op::Mul:
+ std::tie(it, imm) = process(it, end);
+ std::tie(it, imm2) = process(it, end);
+ return {it, imm * imm2};
+ case Op::Div:
+ std::tie(it, imm) = process(it, end);
+ std::tie(it, imm2) = process(it, end);
+ if (imm2 == 0) {
+ return {it, 0};
+ }
+ return {it, imm / imm2};
+ case Op::Mod:
+ std::tie(it, imm) = process(it, end);
+ std::tie(it, imm2) = process(it, end);
+ if (imm2 == 0) {
+ return {it, 0};
+ }
+ return {it, imm % imm2};
+ case Op::BitOr:
+ std::tie(it, imm) = process(it, end);
+ std::tie(it, imm2) = process(it, end);
+ return {it, imm | imm2};
+ case Op::BitAnd:
+ std::tie(it, imm) = process(it, end);
+ std::tie(it, imm2) = process(it, end);
+ return {it, imm & imm2};
+ case Op::BitXor:
+ std::tie(it, imm) = process(it, end);
+ std::tie(it, imm2) = process(it, end);
+ return {it, imm ^ imm2};
+ case Op::BitShiftLeft:
+ std::tie(it, imm) = process(it, end);
+ std::tie(it, imm2) = process(it, end);
+ return {it, imm << imm2};
+ case Op::BitShiftRight:
+ std::tie(it, imm) = process(it, end);
+ std::tie(it, imm2) = process(it, end);
+ return {it, imm >> imm2};
+ case Op::Equal:
+ std::tie(it, imm) = process(it, end);
+ std::tie(it, imm2) = process(it, end);
+ return {it, imm == imm2 ? 1 : 0};
+ case Op::GreaterEqual:
+ std::tie(it, imm) = process(it, end);
+ std::tie(it, imm2) = process(it, end);
+ return {it, imm >= imm2 ? 1 : 0};
+ case Op::LessEqual:
+ std::tie(it, imm) = process(it, end);
+ std::tie(it, imm2) = process(it, end);
+ return {it, imm <= imm2 ? 1 : 0};
+ case Op::Greater:
+ std::tie(it, imm) = process(it, end);
+ std::tie(it, imm2) = process(it, end);
+ return {it, imm > imm2 ? 1 : 0};
+ case Op::Less:
+ std::tie(it, imm) = process(it, end);
+ std::tie(it, imm2) = process(it, end);
+ return {it, imm < imm2 ? 1 : 0};
+ case Op::NotEqual:
+ std::tie(it, imm) = process(it, end);
+ std::tie(it, imm2) = process(it, end);
+ return {it, imm != imm2 ? 1 : 0};
+ case Op::Or:
+ std::tie(it, imm) = process(it, end);
+ std::tie(it, imm2) = process(it, end);
+ return {it, !!imm || !!imm2 ? 1 : 0};
+ case Op::And:
+ std::tie(it, imm) = process(it, end);
+ std::tie(it, imm2) = process(it, end);
+ return {it, !!imm && !!imm2 ? 1 : 0};
+ case Op::Subscript:
+ std::tie(it, imm) = process(it, end);
+ std::tie(it, imm2) = process(it, end);
+ return {it, 0};
+ case Op::Ternary:
+ std::tie(it, imm) = process(it, end);
+ std::tie(it, imm2) = process(it, end);
+ std::tie(it, imm3) = process(it, end);
+ return {it, imm != 0 ? imm2 : imm3};
+ default:
+ Output::Warning("Maniac: Expression contains unsupported operation {}", static_cast(op));
+ return {end, 0};
+ }
+}
+
+int32_t ManiacPatch::ParseExpression(Span op_codes) {
+ std::vector ops;
+ for (auto &o: op_codes) {
+ ops.push_back((o & 0x000000FF));
+ ops.push_back((o & 0x0000FF00) >> 8);
+ ops.push_back((o & 0x00FF0000) >> 16);
+ ops.push_back((o & 0xFF000000) >> 24);
+ }
+ return std::get(process(ops.begin(), ops.end()));
+}
\ No newline at end of file
diff --git a/src/maniac_patch.h b/src/maniac_patch.h
new file mode 100644
index 00000000000..6e7a5a8c009
--- /dev/null
+++ b/src/maniac_patch.h
@@ -0,0 +1,28 @@
+/*
+ * This file is part of EasyRPG Player.
+ *
+ * EasyRPG Player is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * EasyRPG Player is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with EasyRPG Player. If not, see .
+ */
+
+
+#ifndef EP_MANIAC_PATCH
+#define EP_MANIAC_PATCH
+
+#include "span.h"
+
+namespace ManiacPatch {
+ int32_t ParseExpression(Span op_codes);
+}
+
+#endif
\ No newline at end of file