diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9cd89d7..0a17f46 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -121,6 +121,7 @@ set (SOURCES
${CMAKE_SOURCE_DIR}/src/game/input.cpp
${CMAKE_SOURCE_DIR}/src/game/controls.cpp
+ ${CMAKE_SOURCE_DIR}/src/game/config.cpp
${CMAKE_SOURCE_DIR}/src/game/quicksaving.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_renderer.cpp
diff --git a/assets/config_menu.rml b/assets/config_menu.rml
index e5e60b7..a157178 100644
--- a/assets/config_menu.rml
+++ b/assets/config_menu.rml
@@ -133,7 +133,7 @@
diff --git a/include/recomp_config.h b/include/recomp_config.h
new file mode 100644
index 0000000..a3caaee
--- /dev/null
+++ b/include/recomp_config.h
@@ -0,0 +1,17 @@
+#ifndef __RECOMP_CONFIG_H__
+#define __RECOMP_CONFIG_H__
+
+#include
+#include "../ultramodern/config.hpp"
+
+namespace recomp {
+ constexpr std::u8string_view program_id = u8"ZeldaRecomp";
+
+ void load_config();
+ void save_config();
+
+ void reset_input_bindings();
+ void reset_graphics_options();
+};
+
+#endif
\ No newline at end of file
diff --git a/include/recomp_input.h b/include/recomp_input.h
index 0eb1488..e64cd05 100644
--- a/include/recomp_input.h
+++ b/include/recomp_input.h
@@ -7,8 +7,50 @@
#include
#include
#include
+#include
namespace recomp {
+ // x-macros to build input enums and arrays.
+ // First parameter is the enum name, second parameter is the bit field for the input (or 0 if there is no associated one), third is the readable name.
+ #define DEFINE_N64_BUTTON_INPUTS() \
+ DEFINE_INPUT(A, 0x8000, "[A Button]") \
+ DEFINE_INPUT(B, 0x4000, "[B Button]") \
+ DEFINE_INPUT(Z, 0x2000, "[Z Button]") \
+ DEFINE_INPUT(START, 0x1000, "[Start Button]") \
+ DEFINE_INPUT(DPAD_UP, 0x0800, "[Dpad Up]") \
+ DEFINE_INPUT(DPAD_DOWN, 0x0400, "[Dpad Down]") \
+ DEFINE_INPUT(DPAD_LEFT, 0x0200, "[Dpad Left]") \
+ DEFINE_INPUT(DPAD_RIGHT, 0x0100, "[Dpad Right]") \
+ DEFINE_INPUT(L, 0x0020, "[L Button]") \
+ DEFINE_INPUT(R, 0x0010, "[R Button]") \
+ DEFINE_INPUT(C_UP, 0x0008, "[C Up]") \
+ DEFINE_INPUT(C_DOWN, 0x0004, "[C Down]") \
+ DEFINE_INPUT(C_LEFT, 0x0002, "[C Left]") \
+ DEFINE_INPUT(C_RIGHT, 0x0001, "[C Right]")
+
+ #define DEFINE_N64_AXIS_INPUTS() \
+ DEFINE_INPUT(X_AXIS_NEG, 0, "[Analog Left]") \
+ DEFINE_INPUT(X_AXIS_POS, 0, "[Analog Right]") \
+ DEFINE_INPUT(Y_AXIS_NEG, 0, "[Analog Down]") \
+ DEFINE_INPUT(Y_AXIS_POS, 0, "[Analog Up]") \
+
+ #define DEFINE_ALL_INPUTS() \
+ DEFINE_N64_BUTTON_INPUTS() \
+ DEFINE_N64_AXIS_INPUTS()
+
+ // Enum containing every recomp input.
+ #define DEFINE_INPUT(name, value, readable) name,
+ enum class GameInput {
+ DEFINE_ALL_INPUTS()
+
+ COUNT,
+ N64_BUTTON_START = A,
+ N64_BUTTON_COUNT = C_RIGHT - N64_BUTTON_START + 1,
+ N64_AXIS_START = X_AXIS_NEG,
+ N64_AXIS_COUNT = Y_AXIS_POS - N64_AXIS_START + 1,
+ };
+ #undef DEFINE_INPUT
+
struct InputField {
uint32_t input_type;
int32_t input_id;
@@ -61,13 +103,12 @@ namespace recomp {
constexpr size_t bindings_per_input = 2;
- // Loads the user's saved controller mapping if one exists, loads the default mappings if no saved mapping exists.
- void init_control_mappings();
size_t get_num_inputs();
- const std::string& get_input_name(size_t input_index);
- const std::string& get_input_enum_name(size_t input_index);
- InputField& get_input_binding(size_t input_index, size_t binding_index, InputDevice device);
- void set_input_binding(size_t input_index, size_t binding_index, InputDevice device, InputField value);
+ const std::string& get_input_name(GameInput input);
+ const std::string& get_input_enum_name(GameInput input);
+ GameInput get_input_from_enum_name(const std::string_view name);
+ InputField& get_input_binding(GameInput input, size_t binding_index, InputDevice device);
+ void set_input_binding(GameInput input, size_t binding_index, InputDevice device, InputField value);
void get_n64_input(uint16_t* buttons_out, float* x_out, float* y_out);
void handle_events();
diff --git a/include/rt64_layer.h b/include/rt64_layer.h
index 3840c12..a27c094 100644
--- a/include/rt64_layer.h
+++ b/include/rt64_layer.h
@@ -18,6 +18,7 @@ void RT64SendDL(uint8_t* rdram, const OSTask* task);
void RT64UpdateScreen(uint32_t vi_origin);
void RT64ChangeWindow();
void RT64Shutdown();
+RT64::UserConfiguration::Antialiasing RT64MaxMSAA();
void set_rt64_hooks();
diff --git a/src/game/config.cpp b/src/game/config.cpp
new file mode 100644
index 0000000..739d051
--- /dev/null
+++ b/src/game/config.cpp
@@ -0,0 +1,59 @@
+#include "recomp_config.h"
+#include "recomp_input.h"
+#include "../../ultramodern/config.hpp"
+
+void recomp::reset_input_bindings() {
+ auto assign_mapping = [](recomp::InputDevice device, recomp::GameInput input, const std::vector& value) {
+ for (size_t binding_index = 0; binding_index < std::min(value.size(), recomp::bindings_per_input); binding_index++) {
+ recomp::set_input_binding(input, binding_index, device, value[binding_index]);
+ }
+ };
+
+ auto assign_all_mappings = [&](recomp::InputDevice device, const recomp::DefaultN64Mappings& values) {
+ assign_mapping(device, recomp::GameInput::A, values.a);
+ assign_mapping(device, recomp::GameInput::B, values.b);
+ assign_mapping(device, recomp::GameInput::Z, values.z);
+ assign_mapping(device, recomp::GameInput::START, values.start);
+ assign_mapping(device, recomp::GameInput::DPAD_UP, values.dpad_up);
+ assign_mapping(device, recomp::GameInput::DPAD_DOWN, values.dpad_down);
+ assign_mapping(device, recomp::GameInput::DPAD_LEFT, values.dpad_left);
+ assign_mapping(device, recomp::GameInput::DPAD_RIGHT, values.dpad_right);
+ assign_mapping(device, recomp::GameInput::L, values.l);
+ assign_mapping(device, recomp::GameInput::R, values.r);
+ assign_mapping(device, recomp::GameInput::C_UP, values.c_up);
+ assign_mapping(device, recomp::GameInput::C_DOWN, values.c_down);
+ assign_mapping(device, recomp::GameInput::C_LEFT, values.c_left);
+ assign_mapping(device, recomp::GameInput::C_RIGHT, values.c_right);
+
+ assign_mapping(device, recomp::GameInput::X_AXIS_NEG, values.analog_left);
+ assign_mapping(device, recomp::GameInput::X_AXIS_POS, values.analog_right);
+ assign_mapping(device, recomp::GameInput::Y_AXIS_NEG, values.analog_down);
+ assign_mapping(device, recomp::GameInput::Y_AXIS_POS, values.analog_up);
+ };
+
+ assign_all_mappings(recomp::InputDevice::Keyboard, recomp::default_n64_keyboard_mappings);
+ assign_all_mappings(recomp::InputDevice::Controller, recomp::default_n64_controller_mappings);
+}
+
+void recomp::reset_graphics_options() {
+ ultramodern::GraphicsConfig new_config{};
+ new_config.res_option = ultramodern::Resolution::Auto;
+ new_config.wm_option = ultramodern::WindowMode::Fullscreen;
+ new_config.ar_option = RT64::UserConfiguration::AspectRatio::Expand;
+ new_config.msaa_option = RT64::UserConfiguration::Antialiasing::MSAA4X;
+ new_config.rr_option = RT64::UserConfiguration::RefreshRate::Original;
+ new_config.rr_manual_value = 60;
+ ultramodern::set_graphics_config(new_config);
+}
+
+void recomp::load_config() {
+ // TODO load from a file if one exists.
+ recomp::reset_input_bindings();
+
+ // TODO load from a file if one exists.
+ recomp::reset_graphics_options();
+}
+
+void recomp::save_config() {
+
+}
diff --git a/src/game/controls.cpp b/src/game/controls.cpp
index d0e2ef6..265310a 100644
--- a/src/game/controls.cpp
+++ b/src/game/controls.cpp
@@ -5,50 +5,9 @@
#include "../ultramodern/ultramodern.hpp"
#include "../patches/input.h"
-// x-macros to build input enums and arrays.
-// First parameter is the enum name, second parameter is the bit field for the input (or 0 if there is no associated one), third is the readable name.
-#define DEFINE_N64_BUTTON_INPUTS() \
- DEFINE_INPUT(A, 0x8000, "[A Button]") \
- DEFINE_INPUT(B, 0x4000, "[B Button]") \
- DEFINE_INPUT(Z, 0x2000, "[Z Button]") \
- DEFINE_INPUT(START, 0x1000, "[Start Button]") \
- DEFINE_INPUT(DPAD_UP, 0x0800, "[Dpad Up]") \
- DEFINE_INPUT(DPAD_DOWN, 0x0400, "[Dpad Down]") \
- DEFINE_INPUT(DPAD_LEFT, 0x0200, "[Dpad Left]") \
- DEFINE_INPUT(DPAD_RIGHT, 0x0100, "[Dpad Right]") \
- DEFINE_INPUT(L, 0x0020, "[L Button]") \
- DEFINE_INPUT(R, 0x0010, "[R Button]") \
- DEFINE_INPUT(C_UP, 0x0008, "[C Up]") \
- DEFINE_INPUT(C_DOWN, 0x0004, "[C Down]") \
- DEFINE_INPUT(C_LEFT, 0x0002, "[C Left]") \
- DEFINE_INPUT(C_RIGHT, 0x0001, "[C Right]")
-
-#define DEFINE_N64_AXIS_INPUTS() \
- DEFINE_INPUT(X_AXIS_NEG, 0, "[Analog Left]") \
- DEFINE_INPUT(X_AXIS_POS, 0, "[Analog Right]") \
- DEFINE_INPUT(Y_AXIS_NEG, 0, "[Analog Down]") \
- DEFINE_INPUT(Y_AXIS_POS, 0, "[Analog Up]") \
-
-#define DEFINE_ALL_INPUTS() \
- DEFINE_N64_BUTTON_INPUTS() \
- DEFINE_N64_AXIS_INPUTS()
-
-// Make the input enum.
-#define DEFINE_INPUT(name, value, readable) name,
-enum class GameInput {
- DEFINE_ALL_INPUTS()
-
- COUNT,
- N64_BUTTON_START = A,
- N64_BUTTON_COUNT = C_RIGHT - N64_BUTTON_START + 1,
- N64_AXIS_START = X_AXIS_NEG,
- N64_AXIS_COUNT = Y_AXIS_POS - N64_AXIS_START + 1,
-};
-#undef DEFINE_INPUT
-
// Arrays that hold the mappings for every input for keyboard and controller respectively.
using input_mapping = std::array;
-using input_mapping_array = std::array;
+using input_mapping_array = std::array(recomp::GameInput::COUNT)>;
static input_mapping_array keyboard_input_mappings{};
static input_mapping_array controller_input_mappings{};
@@ -73,57 +32,31 @@ static const std::vector input_enum_names = {
};
#undef DEFINE_INPUT
-void recomp::init_control_mappings() {
- // TODO load from a file if one exists.
-
- auto assign_mapping = [](input_mapping_array& mapping, GameInput input, const std::vector& value) {
- input_mapping& cur_mapping = mapping.at((size_t)input);
- std::copy_n(value.begin(), std::min(value.size(), cur_mapping.size()), cur_mapping.begin());
- };
-
- auto assign_all_mappings = [&](input_mapping_array& mapping, const recomp::DefaultN64Mappings& values) {
- assign_mapping(mapping, GameInput::A, values.a);
- assign_mapping(mapping, GameInput::A, values.a);
- assign_mapping(mapping, GameInput::B, values.b);
- assign_mapping(mapping, GameInput::Z, values.z);
- assign_mapping(mapping, GameInput::START, values.start);
- assign_mapping(mapping, GameInput::DPAD_UP, values.dpad_up);
- assign_mapping(mapping, GameInput::DPAD_DOWN, values.dpad_down);
- assign_mapping(mapping, GameInput::DPAD_LEFT, values.dpad_left);
- assign_mapping(mapping, GameInput::DPAD_RIGHT, values.dpad_right);
- assign_mapping(mapping, GameInput::L, values.l);
- assign_mapping(mapping, GameInput::R, values.r);
- assign_mapping(mapping, GameInput::C_UP, values.c_up);
- assign_mapping(mapping, GameInput::C_DOWN, values.c_down);
- assign_mapping(mapping, GameInput::C_LEFT, values.c_left);
- assign_mapping(mapping, GameInput::C_RIGHT, values.c_right);
-
- assign_mapping(mapping, GameInput::X_AXIS_NEG, values.analog_left);
- assign_mapping(mapping, GameInput::X_AXIS_POS, values.analog_right);
- assign_mapping(mapping, GameInput::Y_AXIS_NEG, values.analog_down);
- assign_mapping(mapping, GameInput::Y_AXIS_POS, values.analog_up);
- };
-
- assign_all_mappings(keyboard_input_mappings, recomp::default_n64_keyboard_mappings);
- assign_all_mappings(controller_input_mappings, recomp::default_n64_controller_mappings);
-}
-
size_t recomp::get_num_inputs() {
return (size_t)GameInput::COUNT;
}
-const std::string& recomp::get_input_name(size_t input_index) {
- return input_names.at(input_index);
+const std::string& recomp::get_input_name(GameInput input) {
+ return input_names.at(static_cast(input));
}
-const std::string& recomp::get_input_enum_name(size_t input_index) {
- return input_enum_names.at(input_index);
+const std::string& recomp::get_input_enum_name(GameInput input) {
+ return input_enum_names.at(static_cast(input));
+}
+
+recomp::GameInput recomp::get_input_from_enum_name(const std::string_view enum_name) {
+ auto find_it = std::find(input_enum_names.begin(), input_enum_names.end(), enum_name);
+ if (find_it == input_enum_names.end()) {
+ return recomp::GameInput::COUNT;
+ }
+
+ return static_cast(find_it - input_enum_names.begin());
}
// Due to an RmlUi limitation this can't be const. Ideally it would return a const reference or even just a straight up copy.
-recomp::InputField& recomp::get_input_binding(size_t input_index, size_t binding_index, recomp::InputDevice device) {
+recomp::InputField& recomp::get_input_binding(GameInput input, size_t binding_index, recomp::InputDevice device) {
input_mapping_array& device_mappings = (device == recomp::InputDevice::Controller) ? controller_input_mappings : keyboard_input_mappings;
- input_mapping& cur_input_mapping = device_mappings.at(input_index);
+ input_mapping& cur_input_mapping = device_mappings.at(static_cast(input));
if (binding_index < cur_input_mapping.size()) {
return cur_input_mapping[binding_index];
@@ -134,9 +67,9 @@ recomp::InputField& recomp::get_input_binding(size_t input_index, size_t binding
}
}
-void recomp::set_input_binding(size_t input_index, size_t binding_index, recomp::InputDevice device, recomp::InputField value) {
+void recomp::set_input_binding(recomp::GameInput input, size_t binding_index, recomp::InputDevice device, recomp::InputField value) {
input_mapping_array& device_mappings = (device == recomp::InputDevice::Controller) ? controller_input_mappings : keyboard_input_mappings;
- input_mapping& cur_input_mapping = device_mappings.at(input_index);
+ input_mapping& cur_input_mapping = device_mappings.at(static_cast(input));
if (binding_index < cur_input_mapping.size()) {
cur_input_mapping[binding_index] = value;
diff --git a/src/main/main.cpp b/src/main/main.cpp
index daa88a0..1f25c59 100644
--- a/src/main/main.cpp
+++ b/src/main/main.cpp
@@ -19,6 +19,7 @@
#include "recomp_ui.h"
#include "recomp_input.h"
+#include "recomp_config.h"
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
@@ -234,7 +235,7 @@ int main(int argc, char** argv) {
reset_audio(48000);
init();
- recomp::init_control_mappings();
+ recomp::load_config();
ultramodern::gfx_callbacks_t gfx_callbacks{
.create_gfx = create_gfx,
diff --git a/src/recomp/rt64_layer.cpp b/src/recomp/rt64_layer.cpp
index 962af8f..da8eaa7 100644
--- a/src/recomp/rt64_layer.cpp
+++ b/src/recomp/rt64_layer.cpp
@@ -108,6 +108,8 @@ unsigned int VI_Y_SCALE_REG = 0;
unsigned int SP_STATUS_REG = 0;
unsigned int RDRAM_SIZE = 0x800000;
+static RT64::UserConfiguration::Antialiasing device_max_msaa = RT64::UserConfiguration::Antialiasing::None;
+
#define GET_FUNC(lib, name) \
name = (decltype(name))GetProcAddress(lib, #name)
@@ -115,6 +117,19 @@ void dummy_check_interrupts() {
}
+RT64::UserConfiguration::Antialiasing compute_max_supported_aa(RT64::RenderSampleCounts bits) {
+ if (bits & RT64::RenderSampleCount::Bits::COUNT_2) {
+ if (bits & RT64::RenderSampleCount::Bits::COUNT_4) {
+ if (bits & RT64::RenderSampleCount::Bits::COUNT_8) {
+ return RT64::UserConfiguration::Antialiasing::MSAA8X;
+ }
+ return RT64::UserConfiguration::Antialiasing::MSAA4X;
+ }
+ return RT64::UserConfiguration::Antialiasing::MSAA2X;
+ };
+ return RT64::UserConfiguration::Antialiasing::None;
+}
+
RT64::Application* RT64Init(uint8_t* rom, uint8_t* rdram, ultramodern::WindowHandle window_handle) {
set_rt64_hooks();
// Dynamic loading
@@ -170,14 +185,23 @@ RT64::Application* RT64Init(uint8_t* rom, uint8_t* rdram, ultramodern::WindowHan
gfx_info.RDRAM_SIZE = &RDRAM_SIZE;
#if defined(_WIN32)
- return InitiateGFXWindows(gfx_info, window_handle.window, window_handle.thread_id);
+ RT64::Application* ret = InitiateGFXWindows(gfx_info, window_handle.window, window_handle.thread_id);
#elif defined(__ANDROID__)
static_assert(false && "Unimplemented");
#elif defined(__linux__)
- return InitiateGFXLinux(gfx_info, window_handle.window, window_handle.display);
+ RT64::Application* ret = InitiateGFXLinux(gfx_info, window_handle.window, window_handle.display);
#else
static_assert(false && "Unimplemented");
#endif
+
+ // Before configuring multisampling, make sure the device actually supports it for the formats we'll use. If it doesn't, turn off antialiasing in the configuration.
+ RT64::RenderSampleCounts color_sample_counts = ret->device->getSampleCountsSupported(RT64::RenderFormat::R8G8B8A8_UNORM);
+ RT64::RenderSampleCounts depth_sample_counts = ret->device->getSampleCountsSupported(RT64::RenderFormat::D32_FLOAT);
+ RT64::RenderSampleCounts common_sample_counts = color_sample_counts & depth_sample_counts;
+
+ device_max_msaa = compute_max_supported_aa(common_sample_counts);
+
+ return ret;
}
void RT64SendDL(uint8_t* rdram, const OSTask* task) {
@@ -235,3 +259,7 @@ void RT64UpdateConfig(RT64::Application* application, const ultramodern::Graphic
application->updateMultisampling();
}
}
+
+RT64::UserConfiguration::Antialiasing RT64MaxMSAA() {
+ return device_max_msaa;
+}
diff --git a/src/ui/ui_config.cpp b/src/ui/ui_config.cpp
index 75b2768..892193f 100644
--- a/src/ui/ui_config.cpp
+++ b/src/ui/ui_config.cpp
@@ -1,10 +1,10 @@
#include "recomp_ui.h"
#include "recomp_input.h"
+#include "recomp_config.h"
#include "../../ultramodern/config.hpp"
#include "../../ultramodern/ultramodern.hpp"
#include "RmlUi/Core.h"
-ultramodern::GraphicsConfig cur_options;
ultramodern::GraphicsConfig new_options;
Rml::DataModelHandle graphics_model_handle;
Rml::DataModelHandle controls_model_handle;
@@ -61,7 +61,7 @@ static int focused_input_index = -1;
constexpr recomp::InputDevice cur_device = recomp::InputDevice::Controller;
void recomp::finish_scanning_input(recomp::InputField scanned_field) {
- recomp::set_input_binding(scanned_input_index, scanned_binding_index, cur_device, scanned_field);
+ recomp::set_input_binding(static_cast(scanned_input_index), scanned_binding_index, cur_device, scanned_field);
scanned_input_index = -1;
scanned_binding_index = -1;
controls_model_handle.DirtyVariable("inputs");
@@ -69,6 +69,25 @@ void recomp::finish_scanning_input(recomp::InputField scanned_field) {
controls_model_handle.DirtyVariable("active_binding_slot");
}
+// Counts down every frame while positive until it reaches 0, then saves the graphics config file.
+// This prevents a graphics config that would cause the game to crash from
+static std::atomic_int save_graphics_config_frame_timer;
+
+void queue_saving_graphics_config() {
+ save_graphics_config_frame_timer = 5;
+}
+
+void close_config_menu() {
+ recomp::save_config();
+
+ if (ultramodern::is_game_started()) {
+ recomp::set_current_menu(recomp::Menu::None);
+ }
+ else {
+ recomp::set_current_menu(recomp::Menu::Launcher);
+ }
+}
+
class ConfigMenu : public recomp::MenuController {
public:
ConfigMenu() {
@@ -83,20 +102,14 @@ public:
void register_events(recomp::UiEventListenerInstancer& listener) override {
recomp::register_event(listener, "apply_options",
[](const std::string& param, Rml::Event& event) {
- cur_options = new_options;
graphics_model_handle.DirtyVariable("options_changed");
- update_graphics_config(new_options);
+ ultramodern::set_graphics_config(new_options);
});
recomp::register_event(listener, "config_keydown",
[](const std::string& param, Rml::Event& event) {
if (event.GetId() == Rml::EventId::Keydown) {
if (event.GetParameter("key_identifier", Rml::Input::KeyIdentifier::KI_UNKNOWN) == Rml::Input::KeyIdentifier::KI_ESCAPE) {
- if (ultramodern::is_game_started()) {
- recomp::set_current_menu(recomp::Menu::None);
- }
- else {
- recomp::set_current_menu(recomp::Menu::Launcher);
- }
+ close_config_menu();
}
}
});
@@ -107,6 +120,12 @@ public:
throw std::runtime_error("Failed to make RmlUi data model for the graphics config menu");
}
+ {
+ using namespace std::chrono_literals;
+ std::this_thread::sleep_for(50ms);
+ }
+ new_options = ultramodern::get_graphics_config();
+
bind_option(constructor, "res_option", &new_options.res_option);
bind_option(constructor, "wm_option", &new_options.wm_option);
bind_option(constructor, "ar_option", &new_options.ar_option);
@@ -123,7 +142,7 @@ public:
constructor.BindFunc("options_changed",
[](Rml::Variant& out) {
- out = (cur_options != new_options);
+ out = (ultramodern::get_graphics_config() != new_options);
});
graphics_model_handle = constructor.GetModelHandle();
@@ -138,11 +157,11 @@ public:
constructor.BindFunc("input_count", [](Rml::Variant& out) { out = recomp::get_num_inputs(); } );
constructor.RegisterTransformFunc("get_input_name", [](const Rml::VariantList& inputs) {
- return Rml::Variant{recomp::get_input_name(inputs.at(0).Get())};
+ return Rml::Variant{recomp::get_input_name(static_cast(inputs.at(0).Get()))};
});
constructor.RegisterTransformFunc("get_input_enum_name", [](const Rml::VariantList& inputs) {
- return Rml::Variant{recomp::get_input_enum_name(inputs.at(0).Get())};
+ return Rml::Variant{recomp::get_input_enum_name(static_cast(inputs.at(0).Get()))};
});
constructor.BindEventCallback("set_input_binding",
@@ -156,9 +175,9 @@ public:
constructor.BindEventCallback("clear_input_bindings",
[](Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs) {
- size_t input_index = inputs.at(0).Get();
+ recomp::GameInput input = static_cast(inputs.at(0).Get());
for (size_t binding_index = 0; binding_index < recomp::bindings_per_input; binding_index++) {
- recomp::set_input_binding(input_index, binding_index, cur_device, recomp::InputField{});
+ recomp::set_input_binding(input, binding_index, cur_device, recomp::InputField{});
}
model_handle.DirtyVariable("inputs");
});
@@ -193,16 +212,16 @@ public:
virtual int Size(void* ptr) override { return recomp::bindings_per_input; }
virtual Rml::DataVariable Child(void* ptr, const Rml::DataAddressEntry& address) override {
- uintptr_t input_index = (uintptr_t)ptr;
- return Rml::DataVariable{&input_field_definition_instance, & recomp::get_input_binding(input_index, address.index, recomp::InputDevice::Controller)};
+ recomp::GameInput input = static_cast((uintptr_t)ptr);
+ return Rml::DataVariable{&input_field_definition_instance, &recomp::get_input_binding(input, address.index, recomp::InputDevice::Controller)};
}
};
- // Static instance of the InputField array variable definition to a fixed pointer to return to RmlUi.
+ // Static instance of the InputField array variable definition to have a fixed pointer to return to RmlUi.
static BindingContainerVariableDefinition binding_container_var_instance{};
// Rml variable definition for an array of an array of InputField values (e.g. all the bindings for all inputs).
- struct InputContainerVariableDefinition : public Rml::VariableDefinition {
- InputContainerVariableDefinition() : Rml::VariableDefinition(Rml::DataVariableType::Array) {}
+ struct BindingArrayContainerVariableDefinition : public Rml::VariableDefinition {
+ BindingArrayContainerVariableDefinition() : Rml::VariableDefinition(Rml::DataVariableType::Array) {}
virtual bool Get(void* ptr, Rml::Variant& variant) override { return false; }
virtual bool Set(void* ptr, const Rml::Variant& variant) override { return false; }
@@ -214,6 +233,30 @@ public:
}
};
+ // Static instance of the BindingArrayContainerVariableDefinition variable definition to have a fixed pointer to return to RmlUi.
+ static BindingArrayContainerVariableDefinition binding_array_var_instance{};
+
+ struct InputContainerVariableDefinition : public Rml::VariableDefinition {
+ InputContainerVariableDefinition() : Rml::VariableDefinition(Rml::DataVariableType::Struct) {}
+
+ virtual bool Get(void* ptr, Rml::Variant& variant) override { return true; }
+ virtual bool Set(void* ptr, const Rml::Variant& variant) override { return false; }
+
+ virtual int Size(void* ptr) override { return recomp::get_num_inputs(); }
+ virtual Rml::DataVariable Child(void* ptr, const Rml::DataAddressEntry& address) override {
+ if (address.name == "array") {
+ return Rml::DataVariable(&binding_array_var_instance, nullptr);
+ }
+ else {
+ recomp::GameInput input = recomp::get_input_from_enum_name(address.name);
+ if (input != recomp::GameInput::COUNT) {
+ return Rml::DataVariable(&binding_container_var_instance, (void*)(uintptr_t)input);
+ }
+ }
+ return Rml::DataVariable{};
+ }
+ };
+
// Dummy type to associate with the variable definition.
struct InputContainer {};
constructor.RegisterCustomDataVariableDefinition(Rml::MakeUnique());
@@ -227,7 +270,7 @@ public:
out = "NONE";
}
else {
- out = recomp::get_input_enum_name(focused_input_index);
+ out = recomp::get_input_enum_name(static_cast(focused_input_index));
}
});
@@ -236,7 +279,7 @@ public:
out = "NONE";
}
else {
- out = recomp::get_input_enum_name(scanned_input_index);
+ out = recomp::get_input_enum_name(static_cast(scanned_input_index));
}
});
diff --git a/ultramodern/config.hpp b/ultramodern/config.hpp
index 0f5694a..ba6d135 100644
--- a/ultramodern/config.hpp
+++ b/ultramodern/config.hpp
@@ -27,7 +27,8 @@ namespace ultramodern {
auto operator<=>(const GraphicsConfig& rhs) const = default;
};
- void update_graphics_config(const GraphicsConfig& config);
+ void set_graphics_config(const GraphicsConfig& config);
+ const GraphicsConfig& get_graphics_config();
};
#endif
diff --git a/ultramodern/events.cpp b/ultramodern/events.cpp
index a926534..03ea2ac 100644
--- a/ultramodern/events.cpp
+++ b/ultramodern/events.cpp
@@ -28,7 +28,6 @@ struct SwapBuffersAction {
};
struct UpdateConfigAction {
- ultramodern::GraphicsConfig config;
};
using Action = std::variant;
@@ -257,8 +256,15 @@ void task_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_rea
}
}
-void ultramodern::update_graphics_config(const ultramodern::GraphicsConfig& config) {
- events_context.action_queue.enqueue(UpdateConfigAction{ config });
+static std::atomic cur_config{};
+
+void ultramodern::set_graphics_config(const ultramodern::GraphicsConfig& config) {
+ cur_config = config;
+ events_context.action_queue.enqueue(UpdateConfigAction{});
+}
+
+const ultramodern::GraphicsConfig& ultramodern::get_graphics_config() {
+ return cur_config;
}
void gfx_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_ready, ultramodern::WindowHandle window_handle) {
@@ -267,7 +273,7 @@ void gfx_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_read
ultramodern::set_native_thread_name("Gfx Thread");
ultramodern::set_native_thread_priority(ultramodern::ThreadPriority::Normal);
- ultramodern::GraphicsConfig cur_config{};
+ ultramodern::GraphicsConfig old_config;
RT64::Application* application = RT64Init(rom, rdram, window_handle);
@@ -300,9 +306,10 @@ void gfx_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_read
RT64UpdateScreen(swap_action->origin);
}
else if (const auto* config_action = std::get_if(&action)) {
- if (cur_config != config_action->config) {
- RT64UpdateConfig(application, cur_config, config_action->config);
- cur_config = config_action->config;
+ ultramodern::GraphicsConfig new_config = cur_config;
+ if (old_config != new_config) {
+ RT64UpdateConfig(application, old_config, new_config);
+ old_config = new_config;
}
}
}