mirror of
https://github.com/Mr-Wiseguy/Zelda64Recomp.git
synced 2024-12-26 12:51:49 +01:00
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:
parent
c7bfc15caa
commit
8e4e4b1cae
@ -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
|
||||
|
@ -133,7 +133,7 @@
|
||||
<div
|
||||
class="control-option"
|
||||
id="input_row"
|
||||
data-for="input_bindings, i : inputs"
|
||||
data-for="input_bindings, i : inputs.array"
|
||||
data-event-mouseover="set_input_row_focus(i)"
|
||||
>
|
||||
<label
|
||||
@ -169,7 +169,7 @@
|
||||
<h3>Current focused row: {{cur_input_row}}</h3>
|
||||
<div
|
||||
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)"
|
||||
>
|
||||
{{get_input_name(i)}}
|
||||
@ -180,14 +180,6 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="config__footer">
|
||||
<div>
|
||||
<button
|
||||
class="button button--secondary"
|
||||
id="save_button"
|
||||
>
|
||||
<div class="button__label">Save</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</panel>
|
||||
|
17
include/recomp_config.h
Normal file
17
include/recomp_config.h
Normal 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
|
@ -7,8 +7,50 @@
|
||||
#include <type_traits>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
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();
|
||||
|
@ -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();
|
||||
|
||||
|
59
src/game/config.cpp
Normal file
59
src/game/config.cpp
Normal 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() {
|
||||
|
||||
}
|
@ -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<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 controller_input_mappings{};
|
||||
|
||||
@ -73,57 +32,31 @@ static const std::vector<std::string> 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<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() {
|
||||
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<size_t>(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<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.
|
||||
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<size_t>(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<size_t>(input));
|
||||
|
||||
if (binding_index < cur_input_mapping.size()) {
|
||||
cur_input_mapping[binding_index] = value;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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<recomp::GameInput>(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<Rml::Input::KeyIdentifier>("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<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) {
|
||||
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",
|
||||
@ -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<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++) {
|
||||
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<recomp::GameInput>((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<InputContainer>(Rml::MakeUnique<InputContainerVariableDefinition>());
|
||||
@ -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<recomp::GameInput>(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<recomp::GameInput>(scanned_input_index));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -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
|
||||
|
@ -28,7 +28,6 @@ struct SwapBuffersAction {
|
||||
};
|
||||
|
||||
struct UpdateConfigAction {
|
||||
ultramodern::GraphicsConfig config;
|
||||
};
|
||||
|
||||
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) {
|
||||
events_context.action_queue.enqueue(UpdateConfigAction{ config });
|
||||
static std::atomic<ultramodern::GraphicsConfig> 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<UpdateConfigAction>(&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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user