Split recomp config handling into separate file, changed "inputs" RmlUi variable to be a struct with an "array" member and named fields for each input

This commit is contained in:
Mr-Wiseguy 2024-01-21 14:04:56 -05:00
parent c7bfc15caa
commit 8e4e4b1cae
12 changed files with 258 additions and 134 deletions

View File

@ -121,6 +121,7 @@ set (SOURCES
${CMAKE_SOURCE_DIR}/src/game/input.cpp ${CMAKE_SOURCE_DIR}/src/game/input.cpp
${CMAKE_SOURCE_DIR}/src/game/controls.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/game/quicksaving.cpp
${CMAKE_SOURCE_DIR}/src/ui/ui_renderer.cpp ${CMAKE_SOURCE_DIR}/src/ui/ui_renderer.cpp

View File

@ -133,7 +133,7 @@
<div <div
class="control-option" class="control-option"
id="input_row" id="input_row"
data-for="input_bindings, i : inputs" data-for="input_bindings, i : inputs.array"
data-event-mouseover="set_input_row_focus(i)" data-event-mouseover="set_input_row_focus(i)"
> >
<label <label
@ -169,7 +169,7 @@
<h3>Current focused row: {{cur_input_row}}</h3> <h3>Current focused row: {{cur_input_row}}</h3>
<div <div
class="input-config__visual-input" class="input-config__visual-input"
data-for="input_bindings, i : inputs" data-for="input_bindings, i : inputs.array"
data-attr-visual-input="get_input_enum_name(i)" data-attr-visual-input="get_input_enum_name(i)"
> >
{{get_input_name(i)}} {{get_input_name(i)}}
@ -180,14 +180,6 @@
</div> </div>
</div> </div>
<div class="config__footer"> <div class="config__footer">
<div>
<button
class="button button--secondary"
id="save_button"
>
<div class="button__label">Save</div>
</button>
</div>
</div> </div>
</form> </form>
</panel> </panel>

17
include/recomp_config.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef __RECOMP_CONFIG_H__
#define __RECOMP_CONFIG_H__
#include <string_view>
#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

View File

@ -7,8 +7,50 @@
#include <type_traits> #include <type_traits>
#include <span> #include <span>
#include <string> #include <string>
#include <string_view>
namespace recomp { 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 { struct InputField {
uint32_t input_type; uint32_t input_type;
int32_t input_id; int32_t input_id;
@ -61,13 +103,12 @@ namespace recomp {
constexpr size_t bindings_per_input = 2; 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(); size_t get_num_inputs();
const std::string& get_input_name(size_t input_index); const std::string& get_input_name(GameInput input);
const std::string& get_input_enum_name(size_t input_index); const std::string& get_input_enum_name(GameInput input);
InputField& get_input_binding(size_t input_index, size_t binding_index, InputDevice device); GameInput get_input_from_enum_name(const std::string_view name);
void set_input_binding(size_t input_index, size_t binding_index, InputDevice device, InputField value); 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 get_n64_input(uint16_t* buttons_out, float* x_out, float* y_out);
void handle_events(); void handle_events();

View File

@ -18,6 +18,7 @@ void RT64SendDL(uint8_t* rdram, const OSTask* task);
void RT64UpdateScreen(uint32_t vi_origin); void RT64UpdateScreen(uint32_t vi_origin);
void RT64ChangeWindow(); void RT64ChangeWindow();
void RT64Shutdown(); void RT64Shutdown();
RT64::UserConfiguration::Antialiasing RT64MaxMSAA();
void set_rt64_hooks(); void set_rt64_hooks();

59
src/game/config.cpp Normal file
View File

@ -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<recomp::InputField>& 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() {
}

View File

@ -5,50 +5,9 @@
#include "../ultramodern/ultramodern.hpp" #include "../ultramodern/ultramodern.hpp"
#include "../patches/input.h" #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. // Arrays that hold the mappings for every input for keyboard and controller respectively.
using input_mapping = std::array<recomp::InputField, recomp::bindings_per_input>; using input_mapping = std::array<recomp::InputField, recomp::bindings_per_input>;
using input_mapping_array = std::array<input_mapping, (size_t)GameInput::COUNT>; using input_mapping_array = std::array<input_mapping, static_cast<size_t>(recomp::GameInput::COUNT)>;
static input_mapping_array keyboard_input_mappings{}; static input_mapping_array keyboard_input_mappings{};
static input_mapping_array controller_input_mappings{}; static input_mapping_array controller_input_mappings{};
@ -73,57 +32,31 @@ static const std::vector<std::string> input_enum_names = {
}; };
#undef DEFINE_INPUT #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<recomp::InputField>& 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() { size_t recomp::get_num_inputs() {
return (size_t)GameInput::COUNT; return (size_t)GameInput::COUNT;
} }
const std::string& recomp::get_input_name(size_t input_index) { const std::string& recomp::get_input_name(GameInput input) {
return input_names.at(input_index); return input_names.at(static_cast<size_t>(input));
} }
const std::string& recomp::get_input_enum_name(size_t input_index) { const std::string& recomp::get_input_enum_name(GameInput input) {
return input_enum_names.at(input_index); return input_enum_names.at(static_cast<size_t>(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<recomp::GameInput>(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. // 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_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<size_t>(input));
if (binding_index < cur_input_mapping.size()) { if (binding_index < cur_input_mapping.size()) {
return cur_input_mapping[binding_index]; 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_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<size_t>(input));
if (binding_index < cur_input_mapping.size()) { if (binding_index < cur_input_mapping.size()) {
cur_input_mapping[binding_index] = value; cur_input_mapping[binding_index] = value;

View File

@ -19,6 +19,7 @@
#include "recomp_ui.h" #include "recomp_ui.h"
#include "recomp_input.h" #include "recomp_input.h"
#include "recomp_config.h"
#ifdef _WIN32 #ifdef _WIN32
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
@ -234,7 +235,7 @@ int main(int argc, char** argv) {
reset_audio(48000); reset_audio(48000);
init(); init();
recomp::init_control_mappings(); recomp::load_config();
ultramodern::gfx_callbacks_t gfx_callbacks{ ultramodern::gfx_callbacks_t gfx_callbacks{
.create_gfx = create_gfx, .create_gfx = create_gfx,

View File

@ -108,6 +108,8 @@ unsigned int VI_Y_SCALE_REG = 0;
unsigned int SP_STATUS_REG = 0; unsigned int SP_STATUS_REG = 0;
unsigned int RDRAM_SIZE = 0x800000; unsigned int RDRAM_SIZE = 0x800000;
static RT64::UserConfiguration::Antialiasing device_max_msaa = RT64::UserConfiguration::Antialiasing::None;
#define GET_FUNC(lib, name) \ #define GET_FUNC(lib, name) \
name = (decltype(name))GetProcAddress(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) { RT64::Application* RT64Init(uint8_t* rom, uint8_t* rdram, ultramodern::WindowHandle window_handle) {
set_rt64_hooks(); set_rt64_hooks();
// Dynamic loading // Dynamic loading
@ -170,14 +185,23 @@ RT64::Application* RT64Init(uint8_t* rom, uint8_t* rdram, ultramodern::WindowHan
gfx_info.RDRAM_SIZE = &RDRAM_SIZE; gfx_info.RDRAM_SIZE = &RDRAM_SIZE;
#if defined(_WIN32) #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__) #elif defined(__ANDROID__)
static_assert(false && "Unimplemented"); static_assert(false && "Unimplemented");
#elif defined(__linux__) #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 #else
static_assert(false && "Unimplemented"); static_assert(false && "Unimplemented");
#endif #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) { void RT64SendDL(uint8_t* rdram, const OSTask* task) {
@ -235,3 +259,7 @@ void RT64UpdateConfig(RT64::Application* application, const ultramodern::Graphic
application->updateMultisampling(); application->updateMultisampling();
} }
} }
RT64::UserConfiguration::Antialiasing RT64MaxMSAA() {
return device_max_msaa;
}

View File

@ -1,10 +1,10 @@
#include "recomp_ui.h" #include "recomp_ui.h"
#include "recomp_input.h" #include "recomp_input.h"
#include "recomp_config.h"
#include "../../ultramodern/config.hpp" #include "../../ultramodern/config.hpp"
#include "../../ultramodern/ultramodern.hpp" #include "../../ultramodern/ultramodern.hpp"
#include "RmlUi/Core.h" #include "RmlUi/Core.h"
ultramodern::GraphicsConfig cur_options;
ultramodern::GraphicsConfig new_options; ultramodern::GraphicsConfig new_options;
Rml::DataModelHandle graphics_model_handle; Rml::DataModelHandle graphics_model_handle;
Rml::DataModelHandle controls_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; constexpr recomp::InputDevice cur_device = recomp::InputDevice::Controller;
void recomp::finish_scanning_input(recomp::InputField scanned_field) { 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<recomp::GameInput>(scanned_input_index), scanned_binding_index, cur_device, scanned_field);
scanned_input_index = -1; scanned_input_index = -1;
scanned_binding_index = -1; scanned_binding_index = -1;
controls_model_handle.DirtyVariable("inputs"); 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"); 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 { class ConfigMenu : public recomp::MenuController {
public: public:
ConfigMenu() { ConfigMenu() {
@ -83,20 +102,14 @@ public:
void register_events(recomp::UiEventListenerInstancer& listener) override { void register_events(recomp::UiEventListenerInstancer& listener) override {
recomp::register_event(listener, "apply_options", recomp::register_event(listener, "apply_options",
[](const std::string& param, Rml::Event& event) { [](const std::string& param, Rml::Event& event) {
cur_options = new_options;
graphics_model_handle.DirtyVariable("options_changed"); graphics_model_handle.DirtyVariable("options_changed");
update_graphics_config(new_options); ultramodern::set_graphics_config(new_options);
}); });
recomp::register_event(listener, "config_keydown", recomp::register_event(listener, "config_keydown",
[](const std::string& param, Rml::Event& event) { [](const std::string& param, Rml::Event& event) {
if (event.GetId() == Rml::EventId::Keydown) { if (event.GetId() == Rml::EventId::Keydown) {
if (event.GetParameter<Rml::Input::KeyIdentifier>("key_identifier", Rml::Input::KeyIdentifier::KI_UNKNOWN) == Rml::Input::KeyIdentifier::KI_ESCAPE) { if (event.GetParameter<Rml::Input::KeyIdentifier>("key_identifier", Rml::Input::KeyIdentifier::KI_UNKNOWN) == Rml::Input::KeyIdentifier::KI_ESCAPE) {
if (ultramodern::is_game_started()) { close_config_menu();
recomp::set_current_menu(recomp::Menu::None);
}
else {
recomp::set_current_menu(recomp::Menu::Launcher);
}
} }
} }
}); });
@ -107,6 +120,12 @@ public:
throw std::runtime_error("Failed to make RmlUi data model for the graphics config menu"); 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, "res_option", &new_options.res_option);
bind_option(constructor, "wm_option", &new_options.wm_option); bind_option(constructor, "wm_option", &new_options.wm_option);
bind_option(constructor, "ar_option", &new_options.ar_option); bind_option(constructor, "ar_option", &new_options.ar_option);
@ -123,7 +142,7 @@ public:
constructor.BindFunc("options_changed", constructor.BindFunc("options_changed",
[](Rml::Variant& out) { [](Rml::Variant& out) {
out = (cur_options != new_options); out = (ultramodern::get_graphics_config() != new_options);
}); });
graphics_model_handle = constructor.GetModelHandle(); graphics_model_handle = constructor.GetModelHandle();
@ -138,11 +157,11 @@ public:
constructor.BindFunc("input_count", [](Rml::Variant& out) { out = recomp::get_num_inputs(); } ); constructor.BindFunc("input_count", [](Rml::Variant& out) { out = recomp::get_num_inputs(); } );
constructor.RegisterTransformFunc("get_input_name", [](const Rml::VariantList& inputs) { constructor.RegisterTransformFunc("get_input_name", [](const Rml::VariantList& inputs) {
return Rml::Variant{recomp::get_input_name(inputs.at(0).Get<size_t>())}; return Rml::Variant{recomp::get_input_name(static_cast<recomp::GameInput>(inputs.at(0).Get<size_t>()))};
}); });
constructor.RegisterTransformFunc("get_input_enum_name", [](const Rml::VariantList& inputs) { constructor.RegisterTransformFunc("get_input_enum_name", [](const Rml::VariantList& inputs) {
return Rml::Variant{recomp::get_input_enum_name(inputs.at(0).Get<size_t>())}; return Rml::Variant{recomp::get_input_enum_name(static_cast<recomp::GameInput>(inputs.at(0).Get<size_t>()))};
}); });
constructor.BindEventCallback("set_input_binding", constructor.BindEventCallback("set_input_binding",
@ -156,9 +175,9 @@ public:
constructor.BindEventCallback("clear_input_bindings", constructor.BindEventCallback("clear_input_bindings",
[](Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs) { [](Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs) {
size_t input_index = inputs.at(0).Get<size_t>(); recomp::GameInput input = static_cast<recomp::GameInput>(inputs.at(0).Get<size_t>());
for (size_t binding_index = 0; binding_index < recomp::bindings_per_input; binding_index++) { 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"); model_handle.DirtyVariable("inputs");
}); });
@ -193,16 +212,16 @@ public:
virtual int Size(void* ptr) override { return recomp::bindings_per_input; } virtual int Size(void* ptr) override { return recomp::bindings_per_input; }
virtual Rml::DataVariable Child(void* ptr, const Rml::DataAddressEntry& address) override { virtual Rml::DataVariable Child(void* ptr, const Rml::DataAddressEntry& address) override {
uintptr_t input_index = (uintptr_t)ptr; recomp::GameInput input = static_cast<recomp::GameInput>((uintptr_t)ptr);
return Rml::DataVariable{&input_field_definition_instance, & recomp::get_input_binding(input_index, address.index, recomp::InputDevice::Controller)}; 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{}; 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). // 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 { struct BindingArrayContainerVariableDefinition : public Rml::VariableDefinition {
InputContainerVariableDefinition() : Rml::VariableDefinition(Rml::DataVariableType::Array) {} BindingArrayContainerVariableDefinition() : Rml::VariableDefinition(Rml::DataVariableType::Array) {}
virtual bool Get(void* ptr, Rml::Variant& variant) override { return false; } virtual bool Get(void* ptr, Rml::Variant& variant) override { return false; }
virtual bool Set(void* ptr, const 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. // Dummy type to associate with the variable definition.
struct InputContainer {}; struct InputContainer {};
constructor.RegisterCustomDataVariableDefinition<InputContainer>(Rml::MakeUnique<InputContainerVariableDefinition>()); constructor.RegisterCustomDataVariableDefinition<InputContainer>(Rml::MakeUnique<InputContainerVariableDefinition>());
@ -227,7 +270,7 @@ public:
out = "NONE"; out = "NONE";
} }
else { else {
out = recomp::get_input_enum_name(focused_input_index); out = recomp::get_input_enum_name(static_cast<recomp::GameInput>(focused_input_index));
} }
}); });
@ -236,7 +279,7 @@ public:
out = "NONE"; out = "NONE";
} }
else { else {
out = recomp::get_input_enum_name(scanned_input_index); out = recomp::get_input_enum_name(static_cast<recomp::GameInput>(scanned_input_index));
} }
}); });

View File

@ -27,7 +27,8 @@ namespace ultramodern {
auto operator<=>(const GraphicsConfig& rhs) const = default; 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 #endif

View File

@ -28,7 +28,6 @@ struct SwapBuffersAction {
}; };
struct UpdateConfigAction { struct UpdateConfigAction {
ultramodern::GraphicsConfig config;
}; };
using Action = std::variant<SpTaskAction, SwapBuffersAction, UpdateConfigAction>; using Action = std::variant<SpTaskAction, SwapBuffersAction, UpdateConfigAction>;
@ -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) { static std::atomic<ultramodern::GraphicsConfig> cur_config{};
events_context.action_queue.enqueue(UpdateConfigAction{ 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) { 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_name("Gfx Thread");
ultramodern::set_native_thread_priority(ultramodern::ThreadPriority::Normal); ultramodern::set_native_thread_priority(ultramodern::ThreadPriority::Normal);
ultramodern::GraphicsConfig cur_config{}; ultramodern::GraphicsConfig old_config;
RT64::Application* application = RT64Init(rom, rdram, window_handle); 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); RT64UpdateScreen(swap_action->origin);
} }
else if (const auto* config_action = std::get_if<UpdateConfigAction>(&action)) { else if (const auto* config_action = std::get_if<UpdateConfigAction>(&action)) {
if (cur_config != config_action->config) { ultramodern::GraphicsConfig new_config = cur_config;
RT64UpdateConfig(application, cur_config, config_action->config); if (old_config != new_config) {
cur_config = config_action->config; RT64UpdateConfig(application, old_config, new_config);
old_config = new_config;
} }
} }
} }