From 3484dbbcea094ac64ccaf5e20418dfab24ba9a02 Mon Sep 17 00:00:00 2001 From: Mauro Junior <45118493+jetrotal@users.noreply.github.com> Date: Fri, 3 May 2024 01:15:02 -0300 Subject: [PATCH] New Command 2055 - Process JSON Code --- CMakeLists.txt | 2 + src/game_interpreter.cpp | 42 +++++++++++ src/game_interpreter.h | 1 + src/json_helper.cpp | 159 +++++++++++++++++++++++++++++++++++++++ src/json_helper.h | 11 +++ 5 files changed, 215 insertions(+) create mode 100644 src/json_helper.cpp create mode 100644 src/json_helper.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 40535dc7ca9..e1d3e9c4282 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -233,6 +233,8 @@ add_library(${PROJECT_NAME} OBJECT src/input_source.h src/instrumentation.cpp src/instrumentation.h + src/json_helper.cpp + src/json_helper.h src/keys.h src/main_data.cpp src/main_data.h diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 6ba30865184..c3187c4bc16 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -44,6 +44,7 @@ #include "game_screen.h" #include "game_interpreter_control_variables.h" #include "game_windows.h" +#include "json_helper.h" #include "maniac_patch.h" #include "spriteset_map.h" #include "sprite_character.h" @@ -826,6 +827,8 @@ bool Game_Interpreter::ExecuteCommand(lcf::rpg::EventCommand const& com) { return CommandManiacControlStrings(com); case Cmd::Maniac_CallCommand: return CommandManiacCallCommand(com); + case static_cast(2055): //EasyRPG_ProcessJson + return CommandProcessJson(com); default: return true; } @@ -5108,6 +5111,45 @@ bool Game_Interpreter::CommandManiacCallCommand(lcf::rpg::EventCommand const& co return true; } +bool Game_Interpreter::CommandProcessJson(lcf::rpg::EventCommand const& com) { + if (!Player::IsPatchManiac()) { + return true; + } + + int operation = ValueOrVariable(com.parameters[0], com.parameters[1]); + int sourceVarId = ValueOrVariable(com.parameters[2], com.parameters[3]); + int targetVarId = ValueOrVariable(com.parameters[4], com.parameters[5]); + + std::string jsonPath = ToString(CommandStringOrVariable(com, 6, 7)); + std::string jsonData = ToString(Main_Data::game_strings->Get(sourceVarId)); + + int assignVarId = 0; + std::string result; + std::string newValue; + + switch (operation) { + case 0: // Get operation: Extract a value from JSON data + result = Json_Helper::GetValue(jsonData, jsonPath); + assignVarId = targetVarId; + break; + + case 1: // Set operation: Update JSON data with a new value + newValue = ToString(Main_Data::game_strings->Get(targetVarId)); + result = Json_Helper::SetValue(jsonData, jsonPath, newValue); + assignVarId = sourceVarId; + break; + + default: + Output::Warning("Process JSON - Invalid Operation: {}", operation); + result = "<>"; + break; + } + + if (result != "<>") Main_Data::game_strings->Asg({ assignVarId }, result); + + return true; +} + Game_Interpreter& Game_Interpreter::GetForegroundInterpreter() { return Game_Battle::IsBattleRunning() ? Game_Battle::GetInterpreter() diff --git a/src/game_interpreter.h b/src/game_interpreter.h index f349b391885..9b02c204ba1 100644 --- a/src/game_interpreter.h +++ b/src/game_interpreter.h @@ -289,6 +289,7 @@ class Game_Interpreter bool CommandManiacSetGameOption(lcf::rpg::EventCommand const& com); bool CommandManiacControlStrings(lcf::rpg::EventCommand const& com); bool CommandManiacCallCommand(lcf::rpg::EventCommand const& com); + bool CommandProcessJson(lcf::rpg::EventCommand const& com); int DecodeInt(lcf::DBArray::const_iterator& it); const std::string DecodeString(lcf::DBArray::const_iterator& it); diff --git a/src/json_helper.cpp b/src/json_helper.cpp new file mode 100644 index 00000000000..83d139f4a7c --- /dev/null +++ b/src/json_helper.cpp @@ -0,0 +1,159 @@ +#include "json_helper.h" +#include "external/picojson.h" +#include "output.h" +#include + +namespace Json_Helper { + const std::string kInvalidOutput = "<>"; + + std::vector SplitPath(const std::string& jsonPath) { + std::istringstream iss(jsonPath); + std::vector pathParts; + std::string part; + while (std::getline(iss, part, '.')) { + pathParts.push_back(part); + } + return pathParts; + } + + picojson::value* GetValuePointer(picojson::value& jsonValue, const std::vector& pathParts, bool allowCreation) { + picojson::value* currentValue = &jsonValue; + + for (const auto& part : pathParts) { + std::string arrayName; + int arrayIndex = -1; + bool inArray = false; + + for (char c : part) { + if (!inArray) { + if (c == '[') { + inArray = true; + arrayIndex = 0; + } + else { + arrayName += c; + } + } + else { + if (c == ']') { + break; + } + else { + arrayIndex = arrayIndex * 10 + (c - '0'); + } + } + } + + if (inArray) { + if (!currentValue->is()) { + if (allowCreation) { + *currentValue = picojson::value(picojson::object()); + } + else { + Output::Warning("JSON_ERROR - Invalid JSON type for array: {}", arrayName); + return nullptr; + } + } + + auto& obj = currentValue->get(); + auto arrayIt = obj.find(arrayName); + + if (arrayIt == obj.end()) { + if (allowCreation) { + obj.emplace(arrayName, picojson::value(picojson::array())); + arrayIt = obj.find(arrayName); + } + else { + Output::Warning("JSON_ERROR - Array not found: {}", arrayName); + return nullptr; + } + } + + auto& arr = arrayIt->second.get(); + + if (arrayIndex >= static_cast(arr.size())) { + if (allowCreation) { + arr.resize(arrayIndex + 1); + } + else { + Output::Warning("JSON_ERROR - Array index out of bounds: {}", part); + return nullptr; + } + } + + currentValue = &arr[arrayIndex]; + } + else { + if (!currentValue->is()) { + if (allowCreation) { + *currentValue = picojson::value(picojson::object()); + } + else { + Output::Warning("JSON_ERROR - Invalid JSON type for path: {}", part); + return nullptr; + } + } + + auto& obj = currentValue->get(); + auto objIt = obj.find(arrayName); + + if (objIt == obj.end()) { + if (allowCreation) { + obj.emplace(arrayName, picojson::value(picojson::object())); + objIt = obj.find(arrayName); + } + else { + Output::Warning("JSON_ERROR - Object key not found: {}", part); + return nullptr; + } + } + + currentValue = &objIt->second; + } + } + + return currentValue; + } + + std::string GetValue(const std::string& jsonData, const std::string& jsonPath) { + picojson::value jsonValue; + std::string err = picojson::parse(jsonValue, jsonData); + + if (!err.empty()) { + Output::Warning("JSON_ERROR - JSON parsing error: {}", err); + return kInvalidOutput; + } + + auto pathParts = SplitPath(jsonPath); + auto valuePtr = GetValuePointer(jsonValue, pathParts, false); + + if (valuePtr == nullptr || !valuePtr->is()) { + Output::Warning("JSON_ERROR - Value not found or not a string"); + return kInvalidOutput; + } + + return valuePtr->get(); + } + + std::string SetValue(const std::string& jsonData, const std::string& jsonPath, const std::string& value) { + picojson::value jsonValue; + std::string err = picojson::parse(jsonValue, jsonData); + + if (!err.empty()) { + Output::Warning("JSON_ERROR - JSON parsing error: {}", err); + return kInvalidOutput; + } + + auto pathParts = SplitPath(jsonPath); + auto valuePtr = GetValuePointer(jsonValue, pathParts, true); + + if (valuePtr == nullptr) { + Output::Warning("JSON_ERROR - Unable to create value"); + return kInvalidOutput; + } + + *valuePtr = picojson::value(value); + + return picojson::value(jsonValue).serialize(); + } +} diff --git a/src/json_helper.h b/src/json_helper.h new file mode 100644 index 00000000000..1e846a94c07 --- /dev/null +++ b/src/json_helper.h @@ -0,0 +1,11 @@ +#ifndef JSON_HELPER_H +#define JSON_HELPER_H + +#include + +namespace Json_Helper { + std::string GetValue(const std::string& jsonData, const std::string& jsonPath); + std::string SetValue(const std::string& jsonData, const std::string& jsonPath, const std::string& value); +} + +#endif // JSON_HELPER_H