diff --git a/librecomp/CMakeLists.txt b/librecomp/CMakeLists.txt index 61db595..9c4bf53 100644 --- a/librecomp/CMakeLists.txt +++ b/librecomp/CMakeLists.txt @@ -20,6 +20,7 @@ add_library(librecomp STATIC "${CMAKE_CURRENT_SOURCE_DIR}/src/mod_events.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/mod_hooks.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/mod_manifest.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/mod_config_api.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/overlays.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/pak.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/pi.cpp" diff --git a/librecomp/include/librecomp/helpers.hpp b/librecomp/include/librecomp/helpers.hpp index 08d4544..d8f5afd 100644 --- a/librecomp/include/librecomp/helpers.hpp +++ b/librecomp/include/librecomp/helpers.hpp @@ -1,6 +1,8 @@ #ifndef __RECOMP_HELPERS__ #define __RECOMP_HELPERS__ +#include + #include "recomp.h" #include @@ -51,6 +53,26 @@ inline float _arg_float_f14(uint8_t* rdram, recomp_context* ctx) { return ctx->f14.fl; } +template +std::string _arg_string(uint8_t* rdram, recomp_context* ctx) { + PTR(char) str = _arg(rdram, ctx); + + // Get the length of the byteswapped string. + size_t len = 0; + while (MEM_B(str, len) != 0x00) { + len++; + } + + std::string ret{}; + ret.reserve(len + 1); + + for (size_t i = 0; i < len; i++) { + ret += (char)MEM_B(str, i); + } + + return ret; +} + template void _return(recomp_context* ctx, T val) { static_assert(sizeof(T) <= 4 && "Only 32-bit value returns supported currently"); diff --git a/librecomp/include/librecomp/mods.hpp b/librecomp/include/librecomp/mods.hpp index 824a3d2..20b7f80 100644 --- a/librecomp/include/librecomp/mods.hpp +++ b/librecomp/include/librecomp/mods.hpp @@ -335,7 +335,9 @@ namespace recomp { void set_mod_index(const std::string &mod_game_id, const std::string &mod_id, size_t index); const ConfigSchema &get_mod_config_schema(const std::string &mod_id) const; const std::vector &get_mod_thumbnail(const std::string &mod_id) const; + void set_mod_config_value(size_t mod_index, const std::string &option_id, const ConfigValueVariant &value); void set_mod_config_value(const std::string &mod_id, const std::string &option_id, const ConfigValueVariant &value); + ConfigValueVariant get_mod_config_value(size_t mod_index, const std::string &option_id); ConfigValueVariant get_mod_config_value(const std::string &mod_id, const std::string &option_id); void set_mods_config_path(const std::filesystem::path &path); void set_mod_config_directory(const std::filesystem::path &path); @@ -561,10 +563,15 @@ namespace recomp { bool is_mod_auto_enabled(const std::string& mod_id); const ConfigSchema &get_mod_config_schema(const std::string &mod_id); const std::vector &get_mod_thumbnail(const std::string &mod_id); + void set_mod_config_value(size_t mod_index, const std::string &option_id, const ConfigValueVariant &value); void set_mod_config_value(const std::string &mod_id, const std::string &option_id, const ConfigValueVariant &value); + ConfigValueVariant get_mod_config_value(size_t mod_index, const std::string &option_id); ConfigValueVariant get_mod_config_value(const std::string &mod_id, const std::string &option_id); ModContentTypeId register_mod_content_type(const ModContentType& type); bool register_mod_container_type(const std::string& extension, const std::vector& content_types, bool requires_manifest); + + + void register_config_exports(); } }; diff --git a/librecomp/src/mod_config_api.cpp b/librecomp/src/mod_config_api.cpp new file mode 100644 index 0000000..f619b2f --- /dev/null +++ b/librecomp/src/mod_config_api.cpp @@ -0,0 +1,60 @@ +#include "librecomp/mods.hpp" +#include "librecomp/helpers.hpp" +#include "librecomp/addresses.hpp" + +void recomp_get_config_u32(uint8_t* rdram, recomp_context* ctx, size_t mod_index) { + recomp::mods::ConfigValueVariant val = recomp::mods::get_mod_config_value(mod_index, _arg_string<0>(rdram, ctx)); + if (uint32_t* as_u32 = std::get_if(&val)) { + _return(ctx, *as_u32); + } + else { + _return(ctx, uint32_t{0}); + } +} + +void recomp_get_config_double(uint8_t* rdram, recomp_context* ctx, size_t mod_index) { + recomp::mods::ConfigValueVariant val = recomp::mods::get_mod_config_value(mod_index, _arg_string<0>(rdram, ctx)); + if (double* as_double = std::get_if(&val)) { + ctx->f0.d = *as_double; + } + else { + ctx->f0.d = 0.0; + } +} + +void recomp_get_config_string(uint8_t* rdram, recomp_context* ctx, size_t mod_index) { + recomp::mods::ConfigValueVariant val = recomp::mods::get_mod_config_value(mod_index, _arg_string<0>(rdram, ctx)); + if (std::string* as_string = std::get_if(&val)) { + const std::string& str = *as_string; + // Allocate space in the recomp heap to hold the string, including the null terminator. + size_t alloc_size = (str.size() + 1 + 15) & ~15; + gpr offset = reinterpret_cast(recomp::alloc(rdram, alloc_size)) - rdram; + gpr addr = offset + 0xFFFFFFFF80000000ULL; + + // Copy the string's data into the allocated memory and null terminate it. + for (size_t i = 0; i < str.size(); i++) { + MEM_B(i, addr) = str[i]; + } + MEM_B(str.size(), addr) = 0; + + // Return the allocated memory. + ctx->r2 = addr; + } + else { + _return(ctx, NULLPTR); + } +} + +void recomp_free_config_string(uint8_t* rdram, recomp_context* ctx) { + gpr str_rdram = (gpr)_arg<0, PTR(char)>(rdram, ctx); + gpr offset = str_rdram - 0xFFFFFFFF80000000ULL; + + recomp::free(rdram, rdram + offset); +} + +void recomp::mods::register_config_exports() { + recomp::overlays::register_ext_base_export("recomp_get_config_u32", recomp_get_config_u32); + recomp::overlays::register_ext_base_export("recomp_get_config_double", recomp_get_config_double); + recomp::overlays::register_ext_base_export("recomp_get_config_string", recomp_get_config_string); + recomp::overlays::register_base_export("recomp_free_config_string", recomp_free_config_string); +} diff --git a/librecomp/src/recomp.cpp b/librecomp/src/recomp.cpp index e0f24b4..f697f0e 100644 --- a/librecomp/src/recomp.cpp +++ b/librecomp/src/recomp.cpp @@ -530,11 +530,21 @@ const std::vector &recomp::mods::get_mod_thumbnail(const std::string &mod_ return mod_context->get_mod_thumbnail(mod_id); } +void recomp::mods::set_mod_config_value(size_t mod_index, const std::string &option_id, const ConfigValueVariant &value) { + std::lock_guard lock{ mod_context_mutex }; + return mod_context->set_mod_config_value(mod_index, option_id, value); +} + void recomp::mods::set_mod_config_value(const std::string &mod_id, const std::string &option_id, const ConfigValueVariant &value) { std::lock_guard lock{ mod_context_mutex }; return mod_context->set_mod_config_value(mod_id, option_id, value); } +recomp::mods::ConfigValueVariant recomp::mods::get_mod_config_value(size_t mod_index, const std::string &option_id) { + std::lock_guard lock{ mod_context_mutex }; + return mod_context->get_mod_config_value(mod_index, option_id); +} + recomp::mods::ConfigValueVariant recomp::mods::get_mod_config_value(const std::string &mod_id, const std::string &option_id) { std::lock_guard lock{ mod_context_mutex }; return mod_context->get_mod_config_value(mod_id, option_id); @@ -711,6 +721,7 @@ void recomp::start( } recomp::register_heap_exports(); + recomp::mods::register_config_exports(); std::thread game_thread{[](ultramodern::renderer::WindowHandle window_handle, uint8_t* rdram) { debug_printf("[Recomp] Starting\n");