UI: Massive revamp, new features and improvements

This commit is contained in:
Connor McLaughlin
2022-07-11 23:03:29 +10:00
parent 3fb61865e5
commit b42b5501f6
425 changed files with 39701 additions and 29487 deletions
+8 -15
View File
@@ -1,4 +1,5 @@
add_library(core
achievements.h
analog_controller.cpp
analog_controller.h
analog_joystick.cpp
@@ -28,6 +29,8 @@ add_library(core
digital_controller.h
dma.cpp
dma.h
game_database.cpp
game_database.h
gdb_protocol.cpp
gdb_protocol.h
gpu.cpp
@@ -48,21 +51,18 @@ add_library(core
gpu_sw_backend.cpp
gpu_sw_backend.h
gpu_types.h
guncon.cpp
guncon.h
gte.cpp
gte.h
gte_types.h
host.cpp
host.h
host_display.cpp
host_display.h
host_interface.cpp
host_interface.h
host_interface_progress_callback.cpp
host_interface_progress_callback.h
host_settings.h
imgui_styles.cpp
imgui_styles.h
imgui_fullscreen.cpp
imgui_fullscreen.h
interrupt_controller.cpp
interrupt_controller.h
libcrypt_game_codes.cpp
@@ -75,8 +75,6 @@ add_library(core
memory_card_image.h
multitap.cpp
multitap.h
namco_guncon.cpp
namco_guncon.h
negcon.cpp
negcon.h
pad.cpp
@@ -123,7 +121,7 @@ set(RECOMPILER_SRCS
target_include_directories(core PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/..")
target_include_directories(core PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..")
target_link_libraries(core PUBLIC Threads::Threads common util zlib)
target_link_libraries(core PRIVATE glad stb xxhash imgui)
target_link_libraries(core PRIVATE glad stb xxhash imgui rapidjson tinyxml2)
if(WIN32)
target_sources(core PRIVATE
@@ -161,10 +159,5 @@ else()
endif()
if(ENABLE_CHEEVOS)
target_sources(core PRIVATE
cheevos.cpp
cheevos.h
)
target_compile_definitions(core PUBLIC -DWITH_CHEEVOS=1)
target_link_libraries(core PRIVATE rcheevos rapidjson)
target_compile_definitions(core PRIVATE -DWITH_CHEEVOS=1)
endif()
+55
View File
@@ -0,0 +1,55 @@
#pragma once
#include "common/types.h"
class StateWrapper;
class CDImage;
namespace Achievements {
#ifdef WITH_CHEEVOS
// Implemented in Host.
extern bool Reset();
extern bool DoState(StateWrapper& sw);
extern void GameChanged(const std::string& path, CDImage* image);
/// Re-enables hardcode mode if it is enabled in the settings.
extern void ResetChallengeMode();
/// Forces hardcore mode off until next reset.
extern void DisableChallengeMode();
/// Prompts the user to disable hardcore mode, if they agree, returns true.
extern bool ConfirmChallengeModeDisable(const char* trigger);
/// Returns true if features such as save states should be disabled.
extern bool ChallengeModeActive();
#else
// Make noops when compiling without cheevos.
static inline bool Reset()
{
return true;
}
static inline bool DoState(StateWrapper& sw)
{
return true;
}
static constexpr inline bool ChallengeModeActive()
{
return false;
}
static inline void ResetChallengeMode() {}
static inline void DisableChallengeMode() {}
static inline bool ConfirmChallengeModeDisable(const char* trigger)
{
return true;
}
#endif
} // namespace Achievements
+194 -211
View File
@@ -1,14 +1,14 @@
#include "analog_controller.h"
#include "common/log.h"
#include "common/string_util.h"
#include "host_interface.h"
#include "host.h"
#include "settings.h"
#include "system.h"
#include "util/state_wrapper.h"
#include <cmath>
Log_SetChannel(AnalogController);
AnalogController::AnalogController(u32 index) : m_index(index)
AnalogController::AnalogController(u32 index) : Controller(index)
{
m_axis_state.fill(0x80);
Reset();
@@ -29,7 +29,12 @@ void AnalogController::Reset()
m_tx_buffer.fill(0x00);
m_analog_mode = false;
m_configuration_mode = false;
m_motor_state.fill(0);
for (u32 i = 0; i < NUM_MOTORS; i++)
{
if (m_motor_state[i] != 0)
SetMotorState(i, 0);
}
m_dualshock_enabled = false;
ResetRumbleConfig();
@@ -40,8 +45,8 @@ void AnalogController::Reset()
{
if (g_settings.controller_disable_analog_mode_forcing)
{
g_host_interface->AddOSDMessage(
g_host_interface->TranslateStdString(
Host::AddOSDMessage(
Host::TranslateStdString(
"OSDMessage", "Analog mode forcing is disabled by game settings. Controller will start in digital mode."),
10.0f);
}
@@ -88,72 +93,42 @@ bool AnalogController::DoState(StateWrapper& sw, bool apply_input_state)
if (old_analog_mode != m_analog_mode)
{
g_host_interface->AddFormattedOSDMessage(
Host::AddFormattedOSDMessage(
5.0f,
m_analog_mode ?
g_host_interface->TranslateString("AnalogController", "Controller %u switched to analog mode.") :
g_host_interface->TranslateString("AnalogController", "Controller %u switched to digital mode."),
m_analog_mode ? Host::TranslateString("AnalogController", "Controller %u switched to analog mode.") :
Host::TranslateString("AnalogController", "Controller %u switched to digital mode."),
m_index + 1u);
}
}
return true;
}
std::optional<s32> AnalogController::GetAxisCodeByName(std::string_view axis_name) const
float AnalogController::GetBindState(u32 index) const
{
return StaticGetAxisCodeByName(axis_name);
}
if (index >= static_cast<u32>(Button::Count))
{
const u32 sub_index = index - static_cast<u32>(Button::Count);
if (sub_index >= static_cast<u32>(m_half_axis_state.size()))
return 0.0f;
std::optional<s32> AnalogController::GetButtonCodeByName(std::string_view button_name) const
{
return StaticGetButtonCodeByName(button_name);
}
float AnalogController::GetAxisState(s32 axis_code) const
{
if (axis_code < 0 || axis_code >= static_cast<s32>(Axis::Count))
return static_cast<float>(m_half_axis_state[sub_index]) * (1.0f / 255.0f);
}
else if (index < static_cast<u32>(Button::Analog))
{
return static_cast<float>(((m_button_state >> index) & 1u) ^ 1u);
}
else
{
return 0.0f;
// 0..255 -> -1..1
const float value = (((static_cast<float>(m_axis_state[static_cast<s32>(axis_code)]) / 255.0f) * 2.0f) - 1.0f);
return std::clamp(value / m_axis_scale, -1.0f, 1.0f);
}
}
void AnalogController::SetAxisState(s32 axis_code, float value)
void AnalogController::SetBindState(u32 index, float value)
{
if (axis_code < 0 || axis_code >= static_cast<s32>(Axis::Count))
return;
// -1..1 -> 0..255
const float scaled_value = std::clamp(value * m_axis_scale, -1.0f, 1.0f);
const u8 u8_value = static_cast<u8>(std::clamp(std::round(((scaled_value + 1.0f) / 2.0f) * 255.0f), 0.0f, 255.0f));
SetAxisState(static_cast<Axis>(axis_code), u8_value);
}
void AnalogController::SetAxisState(Axis axis, u8 value)
{
if (value != m_axis_state[static_cast<u8>(axis)])
System::SetRunaheadReplayFlag();
m_axis_state[static_cast<u8>(axis)] = value;
}
bool AnalogController::GetButtonState(s32 button_code) const
{
if (button_code < 0 || button_code >= static_cast<s32>(Button::Analog))
return false;
const u16 bit = u16(1) << static_cast<u8>(button_code);
return ((m_button_state & bit) == 0);
}
void AnalogController::SetButtonState(Button button, bool pressed)
{
if (button == Button::Analog)
if (index == static_cast<s32>(Button::Analog))
{
// analog toggle
if (pressed)
if (value >= 0.5f)
{
if (m_command == Command::Idle)
ProcessAnalogModeToggle();
@@ -163,10 +138,58 @@ void AnalogController::SetButtonState(Button button, bool pressed)
return;
}
else if (index >= static_cast<u32>(Button::Count))
{
const u32 sub_index = index - static_cast<u32>(Button::Count);
if (sub_index >= static_cast<u32>(m_half_axis_state.size()))
return;
const u16 bit = u16(1) << static_cast<u8>(button);
value = ApplyAnalogDeadzoneSensitivity(m_analog_deadzone, m_analog_sensitivity, value);
const u8 u8_value = static_cast<u8>(std::clamp(value * 255.0f, 0.0f, 255.0f));
if (u8_value == m_half_axis_state[sub_index])
return;
if (pressed)
m_half_axis_state[sub_index] = u8_value;
System::SetRunaheadReplayFlag();
#define MERGE(pos, neg) \
((m_half_axis_state[static_cast<u32>(pos)] != 0) ? (127u + ((m_half_axis_state[static_cast<u32>(pos)] + 1u) / 2u)) : \
(127u - (m_half_axis_state[static_cast<u32>(neg)] / 2u)))
switch (static_cast<HalfAxis>(sub_index))
{
case HalfAxis::LLeft:
case HalfAxis::LRight:
m_axis_state[static_cast<u8>(Axis::LeftX)] = MERGE(HalfAxis::LRight, HalfAxis::LLeft);
break;
case HalfAxis::LDown:
case HalfAxis::LUp:
m_axis_state[static_cast<u8>(Axis::LeftY)] = MERGE(HalfAxis::LDown, HalfAxis::LUp);
break;
case HalfAxis::RLeft:
case HalfAxis::RRight:
m_axis_state[static_cast<u8>(Axis::RightX)] = MERGE(HalfAxis::RRight, HalfAxis::RLeft);
break;
case HalfAxis::RDown:
case HalfAxis::RUp:
m_axis_state[static_cast<u8>(Axis::RightY)] = MERGE(HalfAxis::RDown, HalfAxis::RUp);
break;
default:
break;
}
#undef MERGE
return;
}
const u16 bit = u16(1) << static_cast<u8>(index);
if (value >= 0.5f)
{
if (m_button_state & bit)
System::SetRunaheadReplayFlag();
@@ -182,14 +205,6 @@ void AnalogController::SetButtonState(Button button, bool pressed)
}
}
void AnalogController::SetButtonState(s32 button_code, bool pressed)
{
if (button_code < 0 || button_code >= static_cast<s32>(Button::Count))
return;
SetButtonState(static_cast<Button>(button_code), pressed);
}
u32 AnalogController::GetButtonStateBits() const
{
// flip bits, native data is active low
@@ -202,26 +217,6 @@ std::optional<u32> AnalogController::GetAnalogInputBytes() const
m_axis_state[static_cast<size_t>(Axis::RightY)] << 8 | m_axis_state[static_cast<size_t>(Axis::RightX)];
}
u32 AnalogController::GetVibrationMotorCount() const
{
return NUM_MOTORS;
}
float AnalogController::GetVibrationMotorStrength(u32 motor)
{
DebugAssert(motor < NUM_MOTORS);
if (m_motor_state[motor] == 0)
return 0.0f;
// Curve from https://github.com/KrossX/Pokopom/blob/master/Pokopom/Input_XInput.cpp#L210
const double x =
static_cast<double>(std::min<u32>(static_cast<u32>(m_motor_state[motor]) + static_cast<u32>(m_rumble_bias), 255));
const double strength = 0.006474549734772402 * std::pow(x, 3.0) - 1.258165252213538 * std::pow(x, 2.0) +
156.82454281087692 * x + 3.637978807091713e-11;
return static_cast<float>(strength / 65535.0);
}
void AnalogController::ResetTransferState()
{
if (m_analog_toggle_queued)
@@ -240,11 +235,11 @@ void AnalogController::SetAnalogMode(bool enabled)
return;
Log_InfoPrintf("Controller %u switched to %s mode.", m_index + 1u, enabled ? "analog" : "digital");
g_host_interface->AddFormattedOSDMessage(
5.0f,
enabled ? g_host_interface->TranslateString("AnalogController", "Controller %u switched to analog mode.") :
g_host_interface->TranslateString("AnalogController", "Controller %u switched to digital mode."),
m_index + 1u);
Host::AddFormattedOSDMessage(5.0f,
enabled ?
Host::TranslateString("AnalogController", "Controller %u switched to analog mode.") :
Host::TranslateString("AnalogController", "Controller %u switched to digital mode."),
m_index + 1u);
m_analog_mode = enabled;
}
@@ -252,11 +247,10 @@ void AnalogController::ProcessAnalogModeToggle()
{
if (m_analog_locked)
{
g_host_interface->AddFormattedOSDMessage(
Host::AddFormattedOSDMessage(
5.0f,
m_analog_mode ?
g_host_interface->TranslateString("AnalogController", "Controller %u is locked to analog mode by the game.") :
g_host_interface->TranslateString("AnalogController", "Controller %u is locked to digital mode by the game."),
m_analog_mode ? Host::TranslateString("AnalogController", "Controller %u is locked to analog mode by the game.") :
Host::TranslateString("AnalogController", "Controller %u is locked to digital mode by the game."),
m_index + 1u);
}
else
@@ -269,10 +263,32 @@ void AnalogController::ProcessAnalogModeToggle()
}
}
void AnalogController::SetMotorState(u8 motor, u8 value)
void AnalogController::SetMotorState(u32 motor, u8 value)
{
DebugAssert(motor < NUM_MOTORS);
m_motor_state[motor] = value;
if (m_motor_state[motor] != value)
{
m_motor_state[motor] = value;
UpdateHostVibration();
}
}
void AnalogController::UpdateHostVibration()
{
std::array<float, NUM_MOTORS> hvalues;
for (u32 motor = 0; motor < NUM_MOTORS; motor++)
{
// Curve from https://github.com/KrossX/Pokopom/blob/master/Pokopom/Input_XInput.cpp#L210
const u8 state = m_motor_state[motor];
const double x =
static_cast<double>(std::min<u32>(state + static_cast<u32>(m_rumble_bias), 255));
const double strength = 0.006474549734772402 * std::pow(x, 3.0) - 1.258165252213538 * std::pow(x, 2.0) +
156.82454281087692 * x + 3.637978807091713e-11;
hvalues[motor] = (state != 0) ? static_cast<float>(strength / 65535.0) : 0.0f;
}
Host::SetPadVibrationIntensity(m_index, hvalues[0], hvalues[1]);
}
u8 AnalogController::GetExtraButtonMaskLSB() const
@@ -350,7 +366,7 @@ bool AnalogController::Transfer(const u8 data_in, u8* data_out)
if (data_in == 0x01)
{
Log_DevPrintf("ACK controller access");
Log_DebugPrintf("ACK controller access");
m_command = Command::Ready;
return true;
}
@@ -689,10 +705,10 @@ bool AnalogController::Transfer(const u8 data_in, u8* data_out)
{
m_command = Command::Idle;
Log_DevPrintf("Rx: %02x %02x %02x %02x %02x %02x %02x %02x", m_rx_buffer[0], m_rx_buffer[1], m_rx_buffer[2],
m_rx_buffer[3], m_rx_buffer[4], m_rx_buffer[5], m_rx_buffer[6], m_rx_buffer[7]);
Log_DevPrintf("Tx: %02x %02x %02x %02x %02x %02x %02x %02x", m_tx_buffer[0], m_tx_buffer[1], m_tx_buffer[2],
m_tx_buffer[3], m_tx_buffer[4], m_tx_buffer[5], m_tx_buffer[6], m_tx_buffer[7]);
Log_DebugPrintf("Rx: %02x %02x %02x %02x %02x %02x %02x %02x", m_rx_buffer[0], m_rx_buffer[1], m_rx_buffer[2],
m_rx_buffer[3], m_rx_buffer[4], m_rx_buffer[5], m_rx_buffer[6], m_rx_buffer[7]);
Log_DebugPrintf("Tx: %02x %02x %02x %02x %02x %02x %02x %02x", m_tx_buffer[0], m_tx_buffer[1], m_tx_buffer[2],
m_tx_buffer[3], m_tx_buffer[4], m_tx_buffer[5], m_tx_buffer[6], m_tx_buffer[7]);
m_rx_buffer.fill(0x00);
m_tx_buffer.fill(0x00);
@@ -706,122 +722,89 @@ std::unique_ptr<AnalogController> AnalogController::Create(u32 index)
return std::make_unique<AnalogController>(index);
}
std::optional<s32> AnalogController::StaticGetAxisCodeByName(std::string_view axis_name)
{
#define AXIS(name) \
if (axis_name == #name) \
static const Controller::ControllerBindingInfo s_binding_info[] = {
#define BUTTON(name, display_name, button, genb) \
{ \
return static_cast<s32>(ZeroExtend32(static_cast<u8>(Axis::name))); \
name, display_name, static_cast<u32>(button), Controller::ControllerBindingType::Button, genb \
}
#define AXIS(name, display_name, halfaxis, genb) \
{ \
name, display_name, static_cast<u32>(AnalogController::Button::Count) + static_cast<u32>(halfaxis), \
Controller::ControllerBindingType::HalfAxis, genb \
}
AXIS(LeftX);
AXIS(LeftY);
AXIS(RightX);
AXIS(RightY);
BUTTON("Up", "D-Pad Up", AnalogController::Button::Up, GenericInputBinding::DPadUp),
BUTTON("Right", "D-Pad Right", AnalogController::Button::Right, GenericInputBinding::DPadRight),
BUTTON("Down", "D-Pad Down", AnalogController::Button::Down, GenericInputBinding::DPadDown),
BUTTON("Left", "D-Pad Left", AnalogController::Button::Left, GenericInputBinding::DPadLeft),
BUTTON("Triangle", "Triangle", AnalogController::Button::Triangle, GenericInputBinding::Triangle),
BUTTON("Circle", "Circle", AnalogController::Button::Circle, GenericInputBinding::Circle),
BUTTON("Cross", "Cross", AnalogController::Button::Cross, GenericInputBinding::Cross),
BUTTON("Square", "Square", AnalogController::Button::Square, GenericInputBinding::Square),
BUTTON("Select", "Select", AnalogController::Button::Select, GenericInputBinding::Select),
BUTTON("Start", "Start", AnalogController::Button::Start, GenericInputBinding::Start),
BUTTON("Analog", "Analog Toggle", AnalogController::Button::Analog, GenericInputBinding::System),
BUTTON("L1", "L1", AnalogController::Button::L1, GenericInputBinding::L1),
BUTTON("R1", "R1", AnalogController::Button::R1, GenericInputBinding::R1),
BUTTON("L2", "L2", AnalogController::Button::L2, GenericInputBinding::L2),
BUTTON("R2", "R2", AnalogController::Button::R2, GenericInputBinding::R2),
BUTTON("L3", "L3", AnalogController::Button::L3, GenericInputBinding::L3),
BUTTON("R3", "R3", AnalogController::Button::R3, GenericInputBinding::R3),
return std::nullopt;
AXIS("LLeft", "Left Stick Left", AnalogController::HalfAxis::LLeft, GenericInputBinding::LeftStickLeft),
AXIS("LRight", "Left Stick Right", AnalogController::HalfAxis::LRight, GenericInputBinding::LeftStickRight),
AXIS("LDown", "Left Stick Down", AnalogController::HalfAxis::LDown, GenericInputBinding::LeftStickDown),
AXIS("LUp", "Left Stick Up", AnalogController::HalfAxis::LUp, GenericInputBinding::LeftStickUp),
AXIS("RLeft", "Right Stick Left", AnalogController::HalfAxis::RLeft, GenericInputBinding::RightStickLeft),
AXIS("RRight", "Right Stick Right", AnalogController::HalfAxis::RRight, GenericInputBinding::RightStickRight),
AXIS("RDown", "Right Stick Down", AnalogController::HalfAxis::RDown, GenericInputBinding::RightStickDown),
AXIS("RUp", "Right Stick Up", AnalogController::HalfAxis::RUp, GenericInputBinding::RightStickUp),
#undef AXIS
}
std::optional<s32> AnalogController::StaticGetButtonCodeByName(std::string_view button_name)
{
#define BUTTON(name) \
if (button_name == #name) \
{ \
return static_cast<s32>(ZeroExtend32(static_cast<u8>(Button::name))); \
}
BUTTON(Select);
BUTTON(L3);
BUTTON(R3);
BUTTON(Start);
BUTTON(Up);
BUTTON(Right);
BUTTON(Down);
BUTTON(Left);
BUTTON(L2);
BUTTON(R2);
BUTTON(L1);
BUTTON(R1);
BUTTON(Triangle);
BUTTON(Circle);
BUTTON(Cross);
BUTTON(Square);
BUTTON(Analog);
return std::nullopt;
#undef BUTTON
}
};
Controller::AxisList AnalogController::StaticGetAxisNames()
static const SettingInfo s_settings[] = {
{SettingInfo::Type::Boolean, "ForceAnalogOnReset", TRANSLATABLE("AnalogController", "Force Analog Mode on Reset"),
TRANSLATABLE("AnalogController", "Forces the controller to analog mode when the console is reset/powered on. May "
"cause issues with games, so it is recommended to leave this option off."),
"true"},
{SettingInfo::Type::Boolean, "AnalogDPadInDigitalMode",
TRANSLATABLE("AnalogController", "Use Analog Sticks for D-Pad in Digital Mode"),
TRANSLATABLE("AnalogController",
"Allows you to use the analog sticks to control the d-pad in digital mode, as well as the buttons."),
"true"},
{SettingInfo::Type::Float, "AnalogDeadzone", TRANSLATABLE("AnalogController", "Analog Deadzone"),
TRANSLATABLE("AnalogController",
"Sets the analog stick deadzone, i.e. the fraction of the stick movement which will be ignored."),
"0.00f", "0.00f", "1.00f", "0.01f"},
{SettingInfo::Type::Float, "AnalogSensitivity", TRANSLATABLE("AnalogController", "Analog Sensitivity"),
TRANSLATABLE(
"AnalogController",
"Sets the analog stick axis scaling factor. A value between 1.30 and 1.40 is recommended when using recent "
"controllers, e.g. DualShock 4, Xbox One Controller."),
"1.33f", "0.01f", "2.00f", "0.01f"},
{SettingInfo::Type::Integer, "VibrationBias", TRANSLATABLE("AnalogController", "Vibration Bias"),
TRANSLATABLE("AnalogController", "Sets the rumble bias value. If rumble in some games is too weak or not "
"functioning, try increasing this value."),
"8", "0", "255", "1"}};
const Controller::ControllerInfo AnalogController::INFO = {ControllerType::AnalogController,
"AnalogController",
TRANSLATABLE("ControllerType", "Analog Controller"),
s_binding_info,
countof(s_binding_info),
s_settings,
countof(s_settings),
Controller::VibrationCapabilities::LargeSmallMotors};
void AnalogController::LoadSettings(SettingsInterface& si, const char* section)
{
return {{TRANSLATABLE("AnalogController", "LeftX"), static_cast<s32>(Axis::LeftX), AxisType::Full},
{TRANSLATABLE("AnalogController", "LeftY"), static_cast<s32>(Axis::LeftY), AxisType::Full},
{TRANSLATABLE("AnalogController", "RightX"), static_cast<s32>(Axis::RightX), AxisType::Full},
{TRANSLATABLE("AnalogController", "RightY"), static_cast<s32>(Axis::RightY), AxisType::Full}};
}
Controller::ButtonList AnalogController::StaticGetButtonNames()
{
return {{TRANSLATABLE("AnalogController", "Up"), static_cast<s32>(Button::Up)},
{TRANSLATABLE("AnalogController", "Down"), static_cast<s32>(Button::Down)},
{TRANSLATABLE("AnalogController", "Left"), static_cast<s32>(Button::Left)},
{TRANSLATABLE("AnalogController", "Right"), static_cast<s32>(Button::Right)},
{TRANSLATABLE("AnalogController", "Select"), static_cast<s32>(Button::Select)},
{TRANSLATABLE("AnalogController", "Start"), static_cast<s32>(Button::Start)},
{TRANSLATABLE("AnalogController", "Triangle"), static_cast<s32>(Button::Triangle)},
{TRANSLATABLE("AnalogController", "Cross"), static_cast<s32>(Button::Cross)},
{TRANSLATABLE("AnalogController", "Circle"), static_cast<s32>(Button::Circle)},
{TRANSLATABLE("AnalogController", "Square"), static_cast<s32>(Button::Square)},
{TRANSLATABLE("AnalogController", "L1"), static_cast<s32>(Button::L1)},
{TRANSLATABLE("AnalogController", "L2"), static_cast<s32>(Button::L2)},
{TRANSLATABLE("AnalogController", "R1"), static_cast<s32>(Button::R1)},
{TRANSLATABLE("AnalogController", "R2"), static_cast<s32>(Button::R2)},
{TRANSLATABLE("AnalogController", "L3"), static_cast<s32>(Button::L3)},
{TRANSLATABLE("AnalogController", "R3"), static_cast<s32>(Button::R3)},
{TRANSLATABLE("AnalogController", "Analog"), static_cast<s32>(Button::Analog)}};
}
u32 AnalogController::StaticGetVibrationMotorCount()
{
return NUM_MOTORS;
}
Controller::SettingList AnalogController::StaticGetSettings()
{
static constexpr std::array<SettingInfo, 4> settings = {
{{SettingInfo::Type::Boolean, "ForceAnalogOnReset", TRANSLATABLE("AnalogController", "Force Analog Mode on Reset"),
TRANSLATABLE("AnalogController", "Forces the controller to analog mode when the console is reset/powered on. May "
"cause issues with games, so it is recommended to leave this option off."),
"false"},
{SettingInfo::Type::Boolean, "AnalogDPadInDigitalMode",
TRANSLATABLE("AnalogController", "Use Analog Sticks for D-Pad in Digital Mode"),
TRANSLATABLE("AnalogController",
"Allows you to use the analog sticks to control the d-pad in digital mode, as well as the buttons."),
"false"},
{SettingInfo::Type::Float, "AxisScale", TRANSLATABLE("AnalogController", "Analog Axis Scale"),
TRANSLATABLE(
"AnalogController",
"Sets the analog stick axis scaling factor. A value between 1.30 and 1.40 is recommended when using recent "
"controllers, e.g. DualShock 4, Xbox One Controller."),
"1.00f", "0.01f", "1.50f", "0.01f"},
{SettingInfo::Type::Integer, "VibrationBias", TRANSLATABLE("AnalogController", "Vibration Bias"),
TRANSLATABLE("AnalogController", "Sets the rumble bias value. If rumble in some games is too weak or not "
"functioning, try increasing this value."),
"8", "0", "255", "1"}}};
return SettingList(settings.begin(), settings.end());
}
void AnalogController::LoadSettings(const char* section)
{
Controller::LoadSettings(section);
m_force_analog_on_reset = g_host_interface->GetBoolSettingValue(section, "ForceAnalogOnReset", false);
m_analog_dpad_in_digital_mode = g_host_interface->GetBoolSettingValue(section, "AnalogDPadInDigitalMode", false);
m_axis_scale =
std::clamp(std::abs(g_host_interface->GetFloatSettingValue(section, "AxisScale", 1.00f)), 0.01f, 1.50f);
m_rumble_bias =
static_cast<u8>(std::min<u32>(g_host_interface->GetIntSettingValue(section, "VibrationBias", 8), 255));
Controller::LoadSettings(si, section);
m_force_analog_on_reset = si.GetBoolValue(section, "ForceAnalogOnReset", true);
m_analog_dpad_in_digital_mode = si.GetBoolValue(section, "AnalogDPadInDigitalMode", true);
m_analog_deadzone = std::clamp(si.GetFloatValue(section, "AnalogDeadzone", DEFAULT_STICK_DEADZONE), 0.0f, 1.0f);
m_analog_sensitivity =
std::clamp(si.GetFloatValue(section, "AnalogSensitivity", DEFAULT_STICK_SENSITIVITY), 0.01f, 3.0f);
m_rumble_bias = static_cast<u8>(std::min<u32>(si.GetIntValue(section, "VibrationBias", 8), 255));
}
+25 -23
View File
@@ -39,43 +39,42 @@ public:
Count
};
enum class HalfAxis : u8
{
LLeft,
LRight,
LDown,
LUp,
RLeft,
RRight,
RDown,
RUp,
Count
};
static constexpr u8 NUM_MOTORS = 2;
static const Controller::ControllerInfo INFO;
AnalogController(u32 index);
~AnalogController() override;
static std::unique_ptr<AnalogController> Create(u32 index);
static std::optional<s32> StaticGetAxisCodeByName(std::string_view axis_name);
static std::optional<s32> StaticGetButtonCodeByName(std::string_view button_name);
static AxisList StaticGetAxisNames();
static ButtonList StaticGetButtonNames();
static u32 StaticGetVibrationMotorCount();
static SettingList StaticGetSettings();
ControllerType GetType() const override;
std::optional<s32> GetAxisCodeByName(std::string_view axis_name) const override;
std::optional<s32> GetButtonCodeByName(std::string_view button_name) const override;
void Reset() override;
bool DoState(StateWrapper& sw, bool ignore_input_state) override;
float GetAxisState(s32 axis_code) const override;
void SetAxisState(s32 axis_code, float value) override;
bool GetButtonState(s32 button_code) const override;
void SetButtonState(s32 button_code, bool pressed) override;
float GetBindState(u32 index) const override;
void SetBindState(u32 index, float value) override;
u32 GetButtonStateBits() const override;
std::optional<u32> GetAnalogInputBytes() const override;
void ResetTransferState() override;
bool Transfer(const u8 data_in, u8* data_out) override;
void SetAxisState(Axis axis, u8 value);
void SetButtonState(Button button, bool pressed);
u32 GetVibrationMotorCount() const override;
float GetVibrationMotorStrength(u32 motor) override;
void LoadSettings(const char* section) override;
void LoadSettings(SettingsInterface& si, const char* section) override;
private:
using MotorState = std::array<u8, NUM_MOTORS>;
@@ -111,16 +110,16 @@ private:
void SetAnalogMode(bool enabled);
void ProcessAnalogModeToggle();
void SetMotorState(u8 motor, u8 value);
void SetMotorState(u32 motor, u8 value);
void UpdateHostVibration();
u8 GetExtraButtonMaskLSB() const;
void ResetRumbleConfig();
void SetMotorStateForConfigIndex(int index, u8 value);
u32 m_index;
bool m_force_analog_on_reset = false;
bool m_analog_dpad_in_digital_mode = false;
float m_axis_scale = 1.00f;
float m_analog_deadzone = 0.0f;
float m_analog_sensitivity = 1.33f;
u8 m_rumble_bias = 8;
bool m_analog_mode = false;
@@ -151,6 +150,9 @@ private:
MotorState m_motor_state{};
// both directions of axis state, merged to m_axis_state
std::array<u8, static_cast<u32>(HalfAxis::Count)> m_half_axis_state{};
// Member variables that are no longer used, but kept and serialized for compatibility with older save states
u8 m_command_param = 0;
bool m_legacy_rumble_unlocked = false;
+146 -167
View File
@@ -1,15 +1,14 @@
#include "analog_joystick.h"
#include "common/log.h"
#include "common/string_util.h"
#include "host_interface.h"
#include "host.h"
#include "system.h"
#include "util/state_wrapper.h"
#include <cmath>
Log_SetChannel(AnalogJoystick);
AnalogJoystick::AnalogJoystick(u32 index)
AnalogJoystick::AnalogJoystick(u32 index) : Controller(index)
{
m_index = index;
m_axis_state.fill(0x80);
Reset();
}
@@ -50,82 +49,101 @@ bool AnalogJoystick::DoState(StateWrapper& sw, bool apply_input_state)
if (sw.IsReading() && (old_analog_mode != m_analog_mode))
{
g_host_interface->AddFormattedOSDMessage(
5.0f,
m_analog_mode ? g_host_interface->TranslateString("AnalogJoystick", "Controller %u switched to analog mode.") :
g_host_interface->TranslateString("AnalogJoystick", "Controller %u switched to digital mode."),
m_index + 1u);
Host::AddFormattedOSDMessage(5.0f,
m_analog_mode ?
Host::TranslateString("AnalogJoystick", "Controller %u switched to analog mode.") :
Host::TranslateString("AnalogJoystick", "Controller %u switched to digital mode."),
m_index + 1u);
}
return true;
}
std::optional<s32> AnalogJoystick::GetAxisCodeByName(std::string_view axis_name) const
float AnalogJoystick::GetBindState(u32 index) const
{
return StaticGetAxisCodeByName(axis_name);
}
std::optional<s32> AnalogJoystick::GetButtonCodeByName(std::string_view button_name) const
{
return StaticGetButtonCodeByName(button_name);
}
float AnalogJoystick::GetAxisState(s32 axis_code) const
{
if (axis_code < 0 || axis_code >= static_cast<s32>(Axis::Count))
return 0.0f;
// 0..255 -> -1..1
const float value = (((static_cast<float>(m_axis_state[static_cast<s32>(axis_code)]) / 255.0f) * 2.0f) - 1.0f);
return std::clamp(value / m_axis_scale, -1.0f, 1.0f);
}
void AnalogJoystick::SetAxisState(s32 axis_code, float value)
{
if (axis_code < 0 || axis_code >= static_cast<s32>(Axis::Count))
return;
// -1..1 -> 0..255
const float scaled_value = std::clamp(value * m_axis_scale, -1.0f, 1.0f);
const u8 u8_value = static_cast<u8>(std::clamp(std::round(((scaled_value + 1.0f) / 2.0f) * 255.0f), 0.0f, 255.0f));
SetAxisState(static_cast<Axis>(axis_code), u8_value);
}
void AnalogJoystick::SetAxisState(Axis axis, u8 value)
{
if (m_axis_state[static_cast<u8>(axis)] != value)
System::SetRunaheadReplayFlag();
m_axis_state[static_cast<u8>(axis)] = value;
}
bool AnalogJoystick::GetButtonState(s32 button_code) const
{
if (button_code < 0 || button_code >= static_cast<s32>(Button::Count))
return false;
const u16 bit = u16(1) << static_cast<u8>(button_code);
return ((m_button_state & bit) == 0);
}
void AnalogJoystick::SetButtonState(Button button, bool pressed)
{
if (button == Button::Mode)
if (index >= static_cast<u32>(Button::Count))
{
if (pressed)
const u32 sub_index = index - static_cast<u32>(Button::Count);
if (sub_index >= static_cast<u32>(m_half_axis_state.size()))
return 0.0f;
return static_cast<float>(m_half_axis_state[sub_index]) * (1.0f / 255.0f);
}
else if (index < static_cast<u32>(Button::Mode))
{
return static_cast<float>(((m_button_state >> index) & 1u) ^ 1u);
}
else
{
return 0.0f;
}
}
void AnalogJoystick::SetBindState(u32 index, float value)
{
if (index == static_cast<s32>(Button::Mode))
{
// analog toggle
if (value >= 0.5f)
ToggleAnalogMode();
return;
}
else if (index >= static_cast<u32>(Button::Count))
{
const u32 sub_index = index - static_cast<u32>(Button::Count);
if (sub_index >= static_cast<u32>(m_half_axis_state.size()))
return;
const u16 bit = u16(1) << static_cast<u8>(button);
value = ApplyAnalogDeadzoneSensitivity(m_analog_deadzone, m_analog_sensitivity, value);
const u8 u8_value = static_cast<u8>(std::clamp(value * 255.0f, 0.0f, 255.0f));
if (u8_value != m_half_axis_state[sub_index])
System::SetRunaheadReplayFlag();
if (pressed)
m_half_axis_state[sub_index] = u8_value;
#define MERGE(pos, neg) \
((m_half_axis_state[static_cast<u32>(pos)] != 0) ? (127u + ((m_half_axis_state[static_cast<u32>(pos)] + 1u) / 2u)) : \
(127u - (m_half_axis_state[static_cast<u32>(neg)] / 2u)))
switch (static_cast<HalfAxis>(sub_index))
{
case HalfAxis::LLeft:
case HalfAxis::LRight:
m_axis_state[static_cast<u8>(Axis::LeftX)] = MERGE(HalfAxis::LRight, HalfAxis::LLeft);
break;
case HalfAxis::LDown:
case HalfAxis::LUp:
m_axis_state[static_cast<u8>(Axis::LeftY)] = MERGE(HalfAxis::LDown, HalfAxis::LUp);
break;
case HalfAxis::RLeft:
case HalfAxis::RRight:
m_axis_state[static_cast<u8>(Axis::RightX)] = MERGE(HalfAxis::RRight, HalfAxis::RLeft);
break;
case HalfAxis::RDown:
case HalfAxis::RUp:
m_axis_state[static_cast<u8>(Axis::RightY)] = MERGE(HalfAxis::RDown, HalfAxis::RUp);
break;
default:
break;
}
#undef MERGE
return;
}
const u16 bit = u16(1) << static_cast<u8>(index);
if (value >= 0.5f)
{
if (m_button_state & bit)
System::SetRunaheadReplayFlag();
m_button_state &= ~bit;
m_button_state &= ~(bit);
}
else
{
@@ -136,14 +154,6 @@ void AnalogJoystick::SetButtonState(Button button, bool pressed)
}
}
void AnalogJoystick::SetButtonState(s32 button_code, bool pressed)
{
if (button_code < 0 || button_code >= static_cast<s32>(Button::Count))
return;
SetButtonState(static_cast<Button>(button_code), pressed);
}
u32 AnalogJoystick::GetButtonStateBits() const
{
return m_button_state ^ 0xFFFF;
@@ -173,11 +183,11 @@ void AnalogJoystick::ToggleAnalogMode()
m_analog_mode = !m_analog_mode;
Log_InfoPrintf("Joystick %u switched to %s mode.", m_index + 1u, m_analog_mode ? "analog" : "digital");
g_host_interface->AddFormattedOSDMessage(
5.0f,
m_analog_mode ? g_host_interface->TranslateString("AnalogJoystick", "Controller %u switched to analog mode.") :
g_host_interface->TranslateString("AnalogJoystick", "Controller %u switched to digital mode."),
m_index + 1u);
Host::AddFormattedOSDMessage(5.0f,
m_analog_mode ?
Host::TranslateString("AnalogJoystick", "Controller %u switched to analog mode.") :
Host::TranslateString("AnalogJoystick", "Controller %u switched to digital mode."),
m_index + 1u);
}
bool AnalogJoystick::Transfer(const u8 data_in, u8* data_out)
@@ -272,104 +282,73 @@ std::unique_ptr<AnalogJoystick> AnalogJoystick::Create(u32 index)
return std::make_unique<AnalogJoystick>(index);
}
std::optional<s32> AnalogJoystick::StaticGetAxisCodeByName(std::string_view axis_name)
{
#define AXIS(name) \
if (axis_name == #name) \
static const Controller::ControllerBindingInfo s_binding_info[] = {
#define BUTTON(name, display_name, button, genb) \
{ \
return static_cast<s32>(ZeroExtend32(static_cast<u8>(Axis::name))); \
name, display_name, static_cast<u32>(button), Controller::ControllerBindingType::Button, genb \
}
#define AXIS(name, display_name, halfaxis, genb) \
{ \
name, display_name, static_cast<u32>(AnalogJoystick::Button::Count) + static_cast<u32>(halfaxis), \
Controller::ControllerBindingType::HalfAxis, genb \
}
AXIS(LeftX);
AXIS(LeftY);
AXIS(RightX);
AXIS(RightY);
BUTTON("Up", "D-Pad Up", AnalogJoystick::Button::Up, GenericInputBinding::DPadUp),
BUTTON("Right", "D-Pad Right", AnalogJoystick::Button::Right, GenericInputBinding::DPadRight),
BUTTON("Down", "D-Pad Down", AnalogJoystick::Button::Down, GenericInputBinding::DPadDown),
BUTTON("Left", "D-Pad Left", AnalogJoystick::Button::Left, GenericInputBinding::DPadLeft),
BUTTON("Triangle", "Triangle", AnalogJoystick::Button::Triangle, GenericInputBinding::Triangle),
BUTTON("Circle", "Circle", AnalogJoystick::Button::Circle, GenericInputBinding::Circle),
BUTTON("Cross", "Cross", AnalogJoystick::Button::Cross, GenericInputBinding::Cross),
BUTTON("Square", "Square", AnalogJoystick::Button::Square, GenericInputBinding::Square),
BUTTON("Select", "Select", AnalogJoystick::Button::Select, GenericInputBinding::Select),
BUTTON("Start", "Start", AnalogJoystick::Button::Start, GenericInputBinding::Start),
BUTTON("Mode", "Mode Toggle", AnalogJoystick::Button::Mode, GenericInputBinding::System),
BUTTON("L1", "L1", AnalogJoystick::Button::L1, GenericInputBinding::L1),
BUTTON("R1", "R1", AnalogJoystick::Button::R1, GenericInputBinding::R1),
BUTTON("L2", "L2", AnalogJoystick::Button::L2, GenericInputBinding::L2),
BUTTON("R2", "R2", AnalogJoystick::Button::R2, GenericInputBinding::R2),
BUTTON("L3", "L3", AnalogJoystick::Button::L3, GenericInputBinding::L3),
BUTTON("R3", "R3", AnalogJoystick::Button::R3, GenericInputBinding::R3),
return std::nullopt;
AXIS("LLeft", "Left Stick Left", AnalogJoystick::HalfAxis::LLeft, GenericInputBinding::LeftStickLeft),
AXIS("LRight", "Left Stick Right", AnalogJoystick::HalfAxis::LRight, GenericInputBinding::LeftStickRight),
AXIS("LDown", "Left Stick Down", AnalogJoystick::HalfAxis::LDown, GenericInputBinding::LeftStickDown),
AXIS("LUp", "Left Stick Up", AnalogJoystick::HalfAxis::LUp, GenericInputBinding::LeftStickUp),
AXIS("RLeft", "Right Stick Left", AnalogJoystick::HalfAxis::RLeft, GenericInputBinding::RightStickLeft),
AXIS("RRight", "Right Stick Right", AnalogJoystick::HalfAxis::RRight, GenericInputBinding::RightStickRight),
AXIS("RDown", "Right Stick Down", AnalogJoystick::HalfAxis::RDown, GenericInputBinding::RightStickDown),
AXIS("RUp", "Right Stick Up", AnalogJoystick::HalfAxis::RUp, GenericInputBinding::RightStickUp),
#undef AXIS
}
std::optional<s32> AnalogJoystick::StaticGetButtonCodeByName(std::string_view button_name)
{
#define BUTTON(name) \
if (button_name == #name) \
{ \
return static_cast<s32>(ZeroExtend32(static_cast<u8>(Button::name))); \
}
BUTTON(Select);
BUTTON(L3);
BUTTON(R3);
BUTTON(Start);
BUTTON(Up);
BUTTON(Right);
BUTTON(Down);
BUTTON(Left);
BUTTON(L2);
BUTTON(R2);
BUTTON(L1);
BUTTON(R1);
BUTTON(Triangle);
BUTTON(Circle);
BUTTON(Cross);
BUTTON(Square);
BUTTON(Mode);
return std::nullopt;
#undef BUTTON
}
};
Controller::AxisList AnalogJoystick::StaticGetAxisNames()
static const SettingInfo s_settings[] = {
{SettingInfo::Type::Float, "AnalogDeadzone", TRANSLATABLE("AnalogController", "Analog Deadzone"),
TRANSLATABLE("AnalogController",
"Sets the analog stick deadzone, i.e. the fraction of the stick movement which will be ignored.s"),
"1.00f", "0.00f", "1.00f", "0.01f"},
{SettingInfo::Type::Float, "AnalogSensitivity", TRANSLATABLE("AnalogController", "Analog Sensitivity"),
TRANSLATABLE(
"AnalogController",
"Sets the analog stick axis scaling factor. A value between 1.30 and 1.40 is recommended when using recent "
"controllers, e.g. DualShock 4, Xbox One Controller."),
"1.33f", "0.01f", "2.00f", "0.01f"}};
const Controller::ControllerInfo AnalogJoystick::INFO = {ControllerType::AnalogJoystick,
"AnalogJoystick",
TRANSLATABLE("ControllerType", "Analog Joystick"),
s_binding_info,
countof(s_binding_info),
s_settings,
countof(s_settings),
Controller::VibrationCapabilities::NoVibration};
void AnalogJoystick::LoadSettings(SettingsInterface& si, const char* section)
{
return {{TRANSLATABLE("AnalogJoystick", "LeftX"), static_cast<s32>(Axis::LeftX), AxisType::Full},
{TRANSLATABLE("AnalogJoystick", "LeftY"), static_cast<s32>(Axis::LeftY), AxisType::Full},
{TRANSLATABLE("AnalogJoystick", "RightX"), static_cast<s32>(Axis::RightX), AxisType::Full},
{TRANSLATABLE("AnalogJoystick", "RightY"), static_cast<s32>(Axis::RightY), AxisType::Full}};
}
Controller::ButtonList AnalogJoystick::StaticGetButtonNames()
{
return {{TRANSLATABLE("AnalogJoystick", "Up"), static_cast<s32>(Button::Up)},
{TRANSLATABLE("AnalogJoystick", "Down"), static_cast<s32>(Button::Down)},
{TRANSLATABLE("AnalogJoystick", "Left"), static_cast<s32>(Button::Left)},
{TRANSLATABLE("AnalogJoystick", "Right"), static_cast<s32>(Button::Right)},
{TRANSLATABLE("AnalogJoystick", "Select"), static_cast<s32>(Button::Select)},
{TRANSLATABLE("AnalogJoystick", "Start"), static_cast<s32>(Button::Start)},
{TRANSLATABLE("AnalogJoystick", "Triangle"), static_cast<s32>(Button::Triangle)},
{TRANSLATABLE("AnalogJoystick", "Cross"), static_cast<s32>(Button::Cross)},
{TRANSLATABLE("AnalogJoystick", "Circle"), static_cast<s32>(Button::Circle)},
{TRANSLATABLE("AnalogJoystick", "Square"), static_cast<s32>(Button::Square)},
{TRANSLATABLE("AnalogJoystick", "L1"), static_cast<s32>(Button::L1)},
{TRANSLATABLE("AnalogJoystick", "L2"), static_cast<s32>(Button::L2)},
{TRANSLATABLE("AnalogJoystick", "R1"), static_cast<s32>(Button::R1)},
{TRANSLATABLE("AnalogJoystick", "R2"), static_cast<s32>(Button::R2)},
{TRANSLATABLE("AnalogJoystick", "L3"), static_cast<s32>(Button::L3)},
{TRANSLATABLE("AnalogJoystick", "R3"), static_cast<s32>(Button::R3)},
{TRANSLATABLE("AnalogJoystick", "Analog"), static_cast<s32>(Button::Mode)}};
}
u32 AnalogJoystick::StaticGetVibrationMotorCount()
{
return 0;
}
Controller::SettingList AnalogJoystick::StaticGetSettings()
{
static constexpr std::array<SettingInfo, 1> settings = {
{{SettingInfo::Type::Float, "AxisScale", TRANSLATABLE("AnalogJoystick", "Analog Axis Scale"),
TRANSLATABLE(
"AnalogJoystick",
"Sets the analog stick axis scaling factor. A value between 1.30 and 1.40 is recommended when using recent "
"controllers, e.g. DualShock 4, Xbox One Controller."),
"1.00f", "0.01f", "1.50f", "0.01f"}}};
return SettingList(settings.begin(), settings.end());
}
void AnalogJoystick::LoadSettings(const char* section)
{
Controller::LoadSettings(section);
m_axis_scale = std::clamp(g_host_interface->GetFloatSettingValue(section, "AxisScale", 1.00f), 0.01f, 1.50f);
Controller::LoadSettings(si, section);
m_analog_deadzone = std::clamp(si.GetFloatValue(section, "AnalogDeadzone", DEFAULT_STICK_DEADZONE), 0.0f, 1.0f);
m_analog_sensitivity =
std::clamp(si.GetFloatValue(section, "AnalogSensitivity", DEFAULT_STICK_SENSITIVITY), 0.01f, 3.0f);
}
+23 -19
View File
@@ -39,38 +39,40 @@ public:
Count
};
enum class HalfAxis : u8
{
LLeft,
LRight,
LDown,
LUp,
RLeft,
RRight,
RDown,
RUp,
Count
};
static const Controller::ControllerInfo INFO;
AnalogJoystick(u32 index);
~AnalogJoystick() override;
static std::unique_ptr<AnalogJoystick> Create(u32 index);
static std::optional<s32> StaticGetAxisCodeByName(std::string_view axis_name);
static std::optional<s32> StaticGetButtonCodeByName(std::string_view button_name);
static AxisList StaticGetAxisNames();
static ButtonList StaticGetButtonNames();
static u32 StaticGetVibrationMotorCount();
static SettingList StaticGetSettings();
ControllerType GetType() const override;
std::optional<s32> GetAxisCodeByName(std::string_view axis_name) const override;
std::optional<s32> GetButtonCodeByName(std::string_view button_name) const override;
void Reset() override;
bool DoState(StateWrapper& sw, bool apply_input_state) override;
float GetAxisState(s32 axis_code) const override;
void SetAxisState(s32 axis_code, float value) override;
bool GetButtonState(s32 button_code) const override;
void SetButtonState(s32 button_code, bool pressed) override;
float GetBindState(u32 index) const override;
void SetBindState(u32 index, float value) override;
u32 GetButtonStateBits() const override;
std::optional<u32> GetAnalogInputBytes() const override;
void ResetTransferState() override;
bool Transfer(const u8 data_in, u8* data_out) override;
void LoadSettings(const char* section) override;
void SetAxisState(Axis axis, u8 value);
void SetButtonState(Button button, bool pressed);
void LoadSettings(SettingsInterface& si, const char* section) override;
private:
enum class TransferState : u8
@@ -89,9 +91,8 @@ private:
u16 GetID() const;
void ToggleAnalogMode();
u32 m_index;
float m_axis_scale = 1.00f;
float m_analog_deadzone = 0.0f;
float m_analog_sensitivity = 1.33f;
// On original hardware, the mode toggle is a switch rather than a button, so we'll enable Analog Mode by default
bool m_analog_mode = true;
@@ -101,5 +102,8 @@ private:
std::array<u8, static_cast<u8>(Axis::Count)> m_axis_state{};
// both directions of axis state, merged to m_axis_state
std::array<u8, static_cast<u32>(HalfAxis::Count)> m_half_axis_state{};
TransferState m_transfer_state = TransferState::Idle;
};
+144
View File
@@ -3,7 +3,11 @@
#include "common/file_system.h"
#include "common/log.h"
#include "common/md5_digest.h"
#include "common/path.h"
#include "cpu_disasm.h"
#include "host.h"
#include "host_settings.h"
#include "settings.h"
#include <array>
#include <cerrno>
Log_SetChannel(BIOS);
@@ -272,3 +276,143 @@ DiscRegion GetPSExeDiscRegion(const PSEXEHeader& header)
}
} // namespace BIOS
std::optional<std::vector<u8>> BIOS::GetBIOSImage(ConsoleRegion region)
{
std::string bios_name;
switch (region)
{
case ConsoleRegion::NTSC_J:
bios_name = Host::GetStringSettingValue("BIOS", "PathNTSCJ", "");
break;
case ConsoleRegion::PAL:
bios_name = Host::GetStringSettingValue("BIOS", "PathPAL", "");
break;
case ConsoleRegion::NTSC_U:
default:
bios_name = Host::GetStringSettingValue("BIOS", "PathNTSCU", "");
break;
}
if (bios_name.empty())
{
// auto-detect
return FindBIOSImageInDirectory(region, EmuFolders::Bios.c_str());
}
// try the configured path
std::optional<Image> image = LoadImageFromFile(Path::Combine(EmuFolders::Bios, bios_name).c_str());
if (!image.has_value())
{
Host::ReportFormattedErrorAsync(
"Error", Host::TranslateString("HostInterface", "Failed to load configured BIOS file '%s'"), bios_name.c_str());
return std::nullopt;
}
Hash found_hash = GetHash(*image);
Log_DevPrintf("Hash for BIOS '%s': %s", bios_name.c_str(), found_hash.ToString().c_str());
if (!IsValidHashForRegion(region, found_hash))
Log_WarningPrintf("Hash for BIOS '%s' does not match region. This may cause issues.", bios_name.c_str());
return image;
}
std::optional<std::vector<u8>> BIOS::FindBIOSImageInDirectory(ConsoleRegion region, const char* directory)
{
Log_InfoPrintf("Searching for a %s BIOS in '%s'...", Settings::GetConsoleRegionDisplayName(region), directory);
FileSystem::FindResultsArray results;
FileSystem::FindFiles(
directory, "*", FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES | FILESYSTEM_FIND_RELATIVE_PATHS, &results);
std::string fallback_path;
std::optional<Image> fallback_image;
const ImageInfo* fallback_info = nullptr;
for (const FILESYSTEM_FIND_DATA& fd : results)
{
if (fd.Size != BIOS_SIZE && fd.Size != BIOS_SIZE_PS2 && fd.Size != BIOS_SIZE_PS3)
{
Log_WarningPrintf("Skipping '%s': incorrect size", fd.FileName.c_str());
continue;
}
std::string full_path(Path::Combine(directory, fd.FileName));
std::optional<Image> found_image = LoadImageFromFile(full_path.c_str());
if (!found_image)
continue;
Hash found_hash = GetHash(*found_image);
Log_DevPrintf("Hash for BIOS '%s': %s", fd.FileName.c_str(), found_hash.ToString().c_str());
const ImageInfo* ii = GetImageInfoForHash(found_hash);
if (IsValidHashForRegion(region, found_hash))
{
Log_InfoPrintf("Using BIOS '%s': %s", fd.FileName.c_str(), ii ? ii->description : "");
return found_image;
}
// don't let an unknown bios take precedence over a known one
if (!fallback_path.empty() && (fallback_info || !ii))
continue;
fallback_path = std::move(full_path);
fallback_image = std::move(found_image);
fallback_info = ii;
}
if (!fallback_image.has_value())
{
Host::ReportFormattedErrorAsync("Error",
Host::TranslateString("HostInterface", "No BIOS image found for %s region"),
Settings::GetConsoleRegionDisplayName(region));
return std::nullopt;
}
if (!fallback_info)
{
Log_WarningPrintf("Using unknown BIOS '%s'. This may crash.", fallback_path.c_str());
}
else
{
Log_WarningPrintf("Falling back to possibly-incompatible image '%s': %s", fallback_path.c_str(),
fallback_info->description);
}
return fallback_image;
}
std::vector<std::pair<std::string, const BIOS::ImageInfo*>> BIOS::FindBIOSImagesInDirectory(const char* directory)
{
std::vector<std::pair<std::string, const ImageInfo*>> results;
FileSystem::FindResultsArray files;
FileSystem::FindFiles(directory, "*",
FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES | FILESYSTEM_FIND_RELATIVE_PATHS, &files);
for (FILESYSTEM_FIND_DATA& fd : files)
{
if (fd.Size != BIOS_SIZE && fd.Size != BIOS_SIZE_PS2 && fd.Size != BIOS_SIZE_PS3)
continue;
std::string full_path(Path::Combine(directory, fd.FileName));
std::optional<Image> found_image = LoadImageFromFile(full_path.c_str());
if (!found_image)
continue;
Hash found_hash = GetHash(*found_image);
const ImageInfo* ii = GetImageInfoForHash(found_hash);
results.emplace_back(std::move(fd.FileName), ii);
}
return results;
}
bool BIOS::HasAnyBIOSImages()
{
return FindBIOSImageInDirectory(ConsoleRegion::Auto, EmuFolders::Bios.c_str()).has_value();
}
+13
View File
@@ -70,4 +70,17 @@ bool PatchBIOSForEXE(u8* image, u32 image_size, u32 r_pc, u32 r_gp, u32 r_sp, u3
bool IsValidPSExeHeader(const PSEXEHeader& header, u32 file_size);
DiscRegion GetPSExeDiscRegion(const PSEXEHeader& header);
/// Loads the BIOS image for the specified region.
std::optional<std::vector<u8>> GetBIOSImage(ConsoleRegion region);
/// Searches for a BIOS image for the specified region in the specified directory. If no match is found, the first
/// BIOS image within 512KB and 4MB will be used.
std::optional<std::vector<u8>> FindBIOSImageInDirectory(ConsoleRegion region, const char* directory);
/// Returns a list of filenames and descriptions for BIOS images in a directory.
std::vector<std::pair<std::string, const BIOS::ImageInfo*>> FindBIOSImagesInDirectory(const char* directory);
/// Returns true if any BIOS images are found in the configured BIOS directory.
bool HasAnyBIOSImages();
} // namespace BIOS
+2 -2
View File
@@ -10,7 +10,7 @@
#include "cpu_disasm.h"
#include "dma.h"
#include "gpu.h"
#include "host_interface.h"
#include "host.h"
#include "interrupt_controller.h"
#include "mdec.h"
#include "pad.h"
@@ -130,7 +130,7 @@ bool Initialize()
{
if (!AllocateMemory(g_settings.enable_8mb_ram))
{
g_host_interface->ReportError("Failed to allocate memory");
Host::ReportErrorAsync("Error", "Failed to allocate memory");
return false;
}
+2 -1
View File
@@ -3,6 +3,7 @@
#include "common/log.h"
#include "common/platform.h"
#include "dma.h"
#include "host.h"
#include "imgui.h"
#include "interrupt_controller.h"
#include "settings.h"
@@ -2722,7 +2723,7 @@ void CDROM::DrawDebugWindow()
{
static const ImVec4 active_color{1.0f, 1.0f, 1.0f, 1.0f};
static const ImVec4 inactive_color{0.4f, 0.4f, 0.4f, 1.0f};
const float framebuffer_scale = ImGui::GetIO().DisplayFramebufferScale.x;
const float framebuffer_scale = Host::GetOSDScale();
ImGui::SetNextWindowSize(ImVec2(800.0f * framebuffer_scale, 550.0f * framebuffer_scale), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("CDROM State", nullptr))
+1
View File
@@ -29,6 +29,7 @@ public:
bool HasMedia() const { return m_reader.HasMedia(); }
const std::string& GetMediaFileName() const { return m_reader.GetMediaFileName(); }
const CDImage* GetMedia() const { return m_reader.GetMedia(); }
DiscRegion GetDiscRegion() const { return m_disc_region; }
bool IsMediaPS1Disc() const;
bool DoesMediaRegionMatchConsole() const;
+4 -10
View File
@@ -9,7 +9,7 @@
#include "controller.h"
#include "cpu_code_cache.h"
#include "cpu_core.h"
#include "host_interface.h"
#include "host.h"
#include "system.h"
#include <cctype>
#include <iomanip>
@@ -686,17 +686,11 @@ bool CheatList::SaveToPCSXRFile(const char* filename)
bool CheatList::LoadFromPackage(const std::string& game_code)
{
std::unique_ptr<ByteStream> stream =
g_host_interface->OpenPackageFile("database/chtdb.txt", BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED);
if (!stream)
const std::optional<std::string> db_string(Host::ReadResourceFileToString("chtdb.txt"));
if (!db_string.has_value())
return false;
std::string db_string = ByteStream::ReadStreamToString(stream.get());
stream.reset();
if (db_string.empty())
return false;
std::istringstream iss(db_string);
std::istringstream iss(db_string.value());
std::string line;
while (std::getline(iss, line))
{
-1802
View File
File diff suppressed because it is too large Load Diff
-158
View File
@@ -1,158 +0,0 @@
#pragma once
#include "common/string.h"
#include "core/types.h"
#include <functional>
#include <optional>
#include <string>
#include <utility>
#include <vector>
class CDImage;
class StateWrapper;
namespace Cheevos {
enum class AchievementCategory : u32
{
Local = 0,
Core = 3,
Unofficial = 5
};
struct Achievement
{
u32 id;
std::string title;
std::string description;
std::string memaddr;
std::string locked_badge_path;
std::string unlocked_badge_path;
u32 points;
AchievementCategory category;
bool locked;
bool active;
};
struct Leaderboard
{
u32 id;
std::string title;
std::string description;
int format;
};
struct LeaderboardEntry
{
std::string user;
std::string formatted_score;
u32 rank;
bool is_self;
};
extern bool g_active;
extern bool g_challenge_mode;
extern u32 g_game_id;
// RAIntegration only exists for Windows, so no point checking it on other platforms.
#ifdef WITH_RAINTEGRATION
extern bool g_using_raintegration;
static ALWAYS_INLINE bool IsUsingRAIntegration()
{
return g_using_raintegration;
}
#else
static ALWAYS_INLINE bool IsUsingRAIntegration()
{
return false;
}
#endif
ALWAYS_INLINE bool IsActive()
{
return g_active;
}
ALWAYS_INLINE bool IsChallengeModeEnabled()
{
return g_challenge_mode;
}
ALWAYS_INLINE bool IsChallengeModeActive()
{
return g_active && g_challenge_mode;
}
ALWAYS_INLINE bool HasActiveGame()
{
return g_game_id != 0;
}
ALWAYS_INLINE u32 GetGameID()
{
return g_game_id;
}
bool Initialize(bool test_mode, bool use_first_disc_from_playlist, bool enable_rich_presence, bool challenge_mode,
bool include_unofficial);
void Reset();
void Shutdown();
void Update();
bool DoState(StateWrapper& sw);
bool IsLoggedIn();
bool IsTestModeActive();
bool IsUnofficialTestModeActive();
bool IsUsingFirstDiscFromPlaylist();
bool IsRichPresenceEnabled();
const std::string& GetUsername();
const std::string& GetRichPresenceString();
bool LoginAsync(const char* username, const char* password);
bool Login(const char* username, const char* password);
void Logout();
bool HasActiveGame();
void GameChanged(const std::string& path, CDImage* image);
const std::string& GetGameTitle();
const std::string& GetGameDeveloper();
const std::string& GetGamePublisher();
const std::string& GetGameReleaseDate();
const std::string& GetGameIcon();
bool EnumerateAchievements(std::function<bool(const Achievement&)> callback);
u32 GetUnlockedAchiementCount();
u32 GetAchievementCount();
u32 GetMaximumPointsForGame();
u32 GetCurrentPointsForGame();
bool EnumerateLeaderboards(std::function<bool(const Leaderboard&)> callback);
std::optional<bool> TryEnumerateLeaderboardEntries(u32 id, std::function<bool(const LeaderboardEntry&)> callback);
const Leaderboard* GetLeaderboardByID(u32 id);
u32 GetLeaderboardCount();
bool IsLeaderboardTimeType(const Leaderboard& leaderboard);
std::pair<u32, u32> GetAchievementProgress(const Achievement& achievement);
TinyString GetAchievementProgressText(const Achievement& achievement);
void UnlockAchievement(u32 achievement_id, bool add_notification = true);
void SubmitLeaderboard(u32 leaderboard_id, int value);
#ifdef WITH_RAINTEGRATION
void SwitchToRAIntegration();
namespace RAIntegration {
void MainWindowChanged(void* new_handle);
void GameChanged();
std::vector<std::pair<int, const char*>> GetMenuItems();
void ActivateMenuItem(int item);
} // namespace RAIntegration
#endif
} // namespace Cheevos
+135 -182
View File
@@ -2,12 +2,27 @@
#include "analog_controller.h"
#include "analog_joystick.h"
#include "digital_controller.h"
#include "namco_guncon.h"
#include "fmt/format.h"
#include "guncon.h"
#include "negcon.h"
#include "playstation_mouse.h"
#include "util/state_wrapper.h"
Controller::Controller() = default;
static const Controller::ControllerInfo s_none_info = {ControllerType::None,
"None",
"Not Connected",
nullptr,
0,
nullptr,
0,
Controller::VibrationCapabilities::NoVibration};
static const Controller::ControllerInfo* s_controller_info[] = {
&s_none_info, &DigitalController::INFO, &AnalogController::INFO, &AnalogJoystick::INFO, &NeGcon::INFO,
&GunCon::INFO, &PlayStationMouse::INFO,
};
Controller::Controller(u32 index) : m_index(index) {}
Controller::~Controller() = default;
@@ -26,19 +41,12 @@ bool Controller::Transfer(const u8 data_in, u8* data_out)
return false;
}
float Controller::GetAxisState(s32 axis_code) const
float Controller::GetBindState(u32 index) const
{
return 0.0f;
}
void Controller::SetAxisState(s32 axis_code, float value) {}
bool Controller::GetButtonState(s32 button_code) const
{
return false;
}
void Controller::SetButtonState(s32 button_code, bool pressed) {}
void Controller::SetBindState(u32 index, float value) {}
u32 Controller::GetButtonStateBits() const
{
@@ -50,17 +58,7 @@ std::optional<u32> Controller::GetAnalogInputBytes() const
return std::nullopt;
}
u32 Controller::GetVibrationMotorCount() const
{
return 0;
}
float Controller::GetVibrationMotorStrength(u32 motor)
{
return 0.0f;
}
void Controller::LoadSettings(const char* section) {}
void Controller::LoadSettings(SettingsInterface& si, const char* section) {}
bool Controller::GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode)
{
@@ -72,7 +70,7 @@ std::unique_ptr<Controller> Controller::Create(ControllerType type, u32 index)
switch (type)
{
case ControllerType::DigitalController:
return DigitalController::Create();
return DigitalController::Create(index);
case ControllerType::AnalogController:
return AnalogController::Create(index);
@@ -80,14 +78,14 @@ std::unique_ptr<Controller> Controller::Create(ControllerType type, u32 index)
case ControllerType::AnalogJoystick:
return AnalogJoystick::Create(index);
case ControllerType::NamcoGunCon:
return NamcoGunCon::Create();
case ControllerType::GunCon:
return GunCon::Create(index);
case ControllerType::PlayStationMouse:
return PlayStationMouse::Create();
return PlayStationMouse::Create(index);
case ControllerType::NeGcon:
return NeGcon::Create();
return NeGcon::Create(index);
case ControllerType::None:
default:
@@ -95,179 +93,134 @@ std::unique_ptr<Controller> Controller::Create(ControllerType type, u32 index)
}
}
std::optional<s32> Controller::GetAxisCodeByName(std::string_view button_name) const
const char* Controller::GetDefaultPadType(u32 pad)
{
return (pad == 0) ? "DigitalController" : "None";
}
const Controller::ControllerInfo* Controller::GetControllerInfo(ControllerType type)
{
for (const ControllerInfo* info : s_controller_info)
{
if (type == info->type)
return info;
}
return nullptr;
}
const Controller::ControllerInfo* Controller::GetControllerInfo(const std::string_view& name)
{
for (const ControllerInfo* info : s_controller_info)
{
if (name == info->name)
return info;
}
return nullptr;
}
std::vector<std::pair<std::string, std::string>> Controller::GetControllerTypeNames()
{
std::vector<std::pair<std::string, std::string>> ret;
for (const ControllerInfo* info : s_controller_info)
ret.emplace_back(info->name, info->display_name);
return ret;
}
std::vector<std::string> Controller::GetControllerBinds(const std::string_view& type)
{
std::vector<std::string> ret;
const ControllerInfo* info = GetControllerInfo(type);
if (info)
{
for (u32 i = 0; i < info->num_bindings; i++)
{
const ControllerBindingInfo& bi = info->bindings[i];
if (bi.type == ControllerBindingType::Unknown || bi.type == ControllerBindingType::Motor)
continue;
ret.emplace_back(info->bindings[i].name);
}
}
return ret;
}
std::vector<std::string> Controller::GetControllerBinds(ControllerType type)
{
std::vector<std::string> ret;
const ControllerInfo* info = GetControllerInfo(type);
if (info)
{
for (u32 i = 0; i < info->num_bindings; i++)
{
const ControllerBindingInfo& bi = info->bindings[i];
if (bi.type == ControllerBindingType::Unknown || bi.type == ControllerBindingType::Motor)
continue;
ret.emplace_back(info->bindings[i].name);
}
}
return ret;
}
std::optional<u32> Controller::GetBindIndex(ControllerType type, const std::string_view& bind_name)
{
const ControllerInfo* info = GetControllerInfo(type);
if (!info)
return std::nullopt;
for (u32 i = 0; i < info->num_bindings; i++)
{
if (bind_name == info->bindings[i].name)
return i;
}
return std::nullopt;
}
std::optional<s32> Controller::GetButtonCodeByName(std::string_view button_name) const
Controller::VibrationCapabilities Controller::GetControllerVibrationCapabilities(const std::string_view& type)
{
return std::nullopt;
const ControllerInfo* info = GetControllerInfo(type);
return info ? info->vibration_caps : VibrationCapabilities::NoVibration;
}
Controller::AxisList Controller::GetAxisNames(ControllerType type)
std::tuple<u32, u32> Controller::ConvertPadToPortAndSlot(u32 index)
{
switch (type)
{
case ControllerType::DigitalController:
return DigitalController::StaticGetAxisNames();
case ControllerType::AnalogController:
return AnalogController::StaticGetAxisNames();
case ControllerType::AnalogJoystick:
return AnalogJoystick::StaticGetAxisNames();
case ControllerType::NamcoGunCon:
return NamcoGunCon::StaticGetAxisNames();
case ControllerType::PlayStationMouse:
return PlayStationMouse::StaticGetAxisNames();
case ControllerType::NeGcon:
return NeGcon::StaticGetAxisNames();
case ControllerType::None:
default:
return {};
}
if (index > 4) // [5,6,7]
return std::make_tuple(1, index - 4); // 2B,2C,2D
else if (index > 1) // [2,3,4]
return std::make_tuple(0, index - 1); // 1B,1C,1D
else // [0,1]
return std::make_tuple(index, 0); // 1A,2A
}
Controller::ButtonList Controller::GetButtonNames(ControllerType type)
u32 Controller::ConvertPortAndSlotToPad(u32 port, u32 slot)
{
switch (type)
{
case ControllerType::DigitalController:
return DigitalController::StaticGetButtonNames();
case ControllerType::AnalogController:
return AnalogController::StaticGetButtonNames();
case ControllerType::AnalogJoystick:
return AnalogJoystick::StaticGetButtonNames();
case ControllerType::NamcoGunCon:
return NamcoGunCon::StaticGetButtonNames();
case ControllerType::PlayStationMouse:
return PlayStationMouse::StaticGetButtonNames();
case ControllerType::NeGcon:
return NeGcon::StaticGetButtonNames();
case ControllerType::None:
default:
return {};
}
if (slot == 0)
return port;
else if (port == 0) // slot=[0,1]
return slot + 1; // 2,3,4
else
return slot + 4; // 5,6,7
}
u32 Controller::GetVibrationMotorCount(ControllerType type)
bool Controller::PadIsMultitapSlot(u32 index)
{
switch (type)
{
case ControllerType::DigitalController:
return DigitalController::StaticGetVibrationMotorCount();
case ControllerType::AnalogController:
return AnalogController::StaticGetVibrationMotorCount();
case ControllerType::AnalogJoystick:
return AnalogJoystick::StaticGetVibrationMotorCount();
case ControllerType::NamcoGunCon:
return NamcoGunCon::StaticGetVibrationMotorCount();
case ControllerType::PlayStationMouse:
return PlayStationMouse::StaticGetVibrationMotorCount();
case ControllerType::NeGcon:
return NeGcon::StaticGetVibrationMotorCount();
case ControllerType::None:
default:
return 0;
}
return (index >= 2);
}
std::optional<s32> Controller::GetAxisCodeByName(ControllerType type, std::string_view axis_name)
bool Controller::PortAndSlotIsMultitap(u32 port, u32 slot)
{
switch (type)
{
case ControllerType::DigitalController:
return DigitalController::StaticGetAxisCodeByName(axis_name);
case ControllerType::AnalogController:
return AnalogController::StaticGetAxisCodeByName(axis_name);
case ControllerType::AnalogJoystick:
return AnalogJoystick::StaticGetAxisCodeByName(axis_name);
case ControllerType::NamcoGunCon:
return NamcoGunCon::StaticGetAxisCodeByName(axis_name);
case ControllerType::PlayStationMouse:
return PlayStationMouse::StaticGetAxisCodeByName(axis_name);
case ControllerType::NeGcon:
return NeGcon::StaticGetAxisCodeByName(axis_name);
case ControllerType::None:
default:
return std::nullopt;
}
return (slot != 0);
}
std::optional<s32> Controller::GetButtonCodeByName(ControllerType type, std::string_view button_name)
std::string Controller::GetSettingsSection(u32 pad)
{
switch (type)
{
case ControllerType::DigitalController:
return DigitalController::StaticGetButtonCodeByName(button_name);
case ControllerType::AnalogController:
return AnalogController::StaticGetButtonCodeByName(button_name);
case ControllerType::AnalogJoystick:
return AnalogJoystick::StaticGetButtonCodeByName(button_name);
case ControllerType::NamcoGunCon:
return NamcoGunCon::StaticGetButtonCodeByName(button_name);
case ControllerType::PlayStationMouse:
return PlayStationMouse::StaticGetButtonCodeByName(button_name);
case ControllerType::NeGcon:
return NeGcon::StaticGetButtonCodeByName(button_name);
case ControllerType::None:
default:
return std::nullopt;
}
}
Controller::SettingList Controller::GetSettings(ControllerType type)
{
switch (type)
{
case ControllerType::DigitalController:
return DigitalController::StaticGetSettings();
case ControllerType::AnalogController:
return AnalogController::StaticGetSettings();
case ControllerType::AnalogJoystick:
return AnalogJoystick::StaticGetSettings();
case ControllerType::NamcoGunCon:
return NamcoGunCon::StaticGetSettings();
case ControllerType::NeGcon:
return NeGcon::StaticGetSettings();
case ControllerType::PlayStationMouse:
return PlayStationMouse::StaticGetSettings();
default:
return {};
}
return fmt::format("Pad{}", pad + 1u);
}
+83 -40
View File
@@ -6,36 +6,67 @@
#include <optional>
#include <string>
#include <string_view>
#include <tuple>
#include <vector>
class SettingsInterface;
class StateWrapper;
class HostInterface;
enum class GenericInputBinding : u8;
class Controller
{
public:
enum class AxisType : u8
enum class ControllerBindingType : u8
{
Full,
Half
Unknown,
Button,
Axis,
HalfAxis,
Motor,
Macro
};
using ButtonList = std::vector<std::pair<std::string, s32>>;
using AxisList = std::vector<std::tuple<std::string, s32, AxisType>>;
using SettingList = std::vector<SettingInfo>;
enum class VibrationCapabilities : u8
{
NoVibration,
LargeSmallMotors,
SingleMotor,
Count
};
Controller();
struct ControllerBindingInfo
{
const char* name;
const char* display_name;
u32 bind_index;
ControllerBindingType type;
GenericInputBinding generic_mapping;
};
struct ControllerInfo
{
ControllerType type;
const char* name;
const char* display_name;
const ControllerBindingInfo* bindings;
u32 num_bindings;
const SettingInfo* settings;
u32 num_settings;
VibrationCapabilities vibration_caps;
};
/// Default stick deadzone/sensitivity.
static constexpr float DEFAULT_STICK_DEADZONE = 0.0f;
static constexpr float DEFAULT_STICK_SENSITIVITY = 1.33f;
Controller(u32 index);
virtual ~Controller();
/// Returns the type of controller.
virtual ControllerType GetType() const = 0;
/// Gets the integer code for an axis in the specified controller type.
virtual std::optional<s32> GetAxisCodeByName(std::string_view axis_name) const;
/// Gets the integer code for a button in the specified controller type.
virtual std::optional<s32> GetButtonCodeByName(std::string_view button_name) const;
virtual void Reset();
virtual bool DoState(StateWrapper& sw, bool apply_input_state);
@@ -46,16 +77,10 @@ public:
virtual bool Transfer(const u8 data_in, u8* data_out);
/// Changes the specified axis state. Values are normalized from -1..1.
virtual float GetAxisState(s32 axis_code) const;
virtual float GetBindState(u32 index) const;
/// Changes the specified axis state. Values are normalized from -1..1.
virtual void SetAxisState(s32 axis_code, float value);
/// Returns the specified button state.
virtual bool GetButtonState(s32 button_code) const;
/// Changes the specified button state.
virtual void SetButtonState(s32 button_code, bool pressed);
/// Changes the specified bind state. Values are normalized from -1..1.
virtual void SetBindState(u32 index, float value);
/// Returns a bitmask of the current button states, 1 = on.
virtual u32 GetButtonStateBits() const;
@@ -63,14 +88,8 @@ public:
/// Returns analog input bytes packed as a u32. Values are specific to controller type.
virtual std::optional<u32> GetAnalogInputBytes() const;
/// Returns the number of vibration motors.
virtual u32 GetVibrationMotorCount() const;
/// Queries the state of the specified vibration motor. Values are normalized from 0..1.
virtual float GetVibrationMotorStrength(u32 motor);
/// Loads/refreshes any per-controller settings.
virtual void LoadSettings(const char* section);
virtual void LoadSettings(SettingsInterface& si, const char* section);
/// Returns the software cursor to use for this controller, if any.
virtual bool GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode);
@@ -78,21 +97,45 @@ public:
/// Creates a new controller of the specified type.
static std::unique_ptr<Controller> Create(ControllerType type, u32 index);
/// Returns the default type for the specified port.
static const char* GetDefaultPadType(u32 pad);
/// Returns a list of controller type names. Pair of [name, display name].
static std::vector<std::pair<std::string, std::string>> GetControllerTypeNames();
/// Returns the list of binds for the specified controller type.
static std::vector<std::string> GetControllerBinds(const std::string_view& type);
static std::vector<std::string> GetControllerBinds(ControllerType type);
/// Gets the integer code for an axis in the specified controller type.
static std::optional<s32> GetAxisCodeByName(ControllerType type, std::string_view axis_name);
static std::optional<u32> GetBindIndex(ControllerType type, const std::string_view& bind_name);
/// Gets the integer code for a button in the specified controller type.
static std::optional<s32> GetButtonCodeByName(ControllerType type, std::string_view button_name);
/// Returns the vibration configuration for the specified controller type.
static VibrationCapabilities GetControllerVibrationCapabilities(const std::string_view& type);
/// Returns a list of axises for the specified controller type.
static AxisList GetAxisNames(ControllerType type);
/// Returns general information for the specified controller type.
static const ControllerInfo* GetControllerInfo(ControllerType type);
static const ControllerInfo* GetControllerInfo(const std::string_view& name);
/// Returns a list of buttons for the specified controller type.
static ButtonList GetButtonNames(ControllerType type);
/// Converts a global pad index to a multitap port and slot.
static std::tuple<u32, u32> ConvertPadToPortAndSlot(u32 index);
/// Returns the number of vibration motors.
static u32 GetVibrationMotorCount(ControllerType type);
/// Converts a multitap port and slot to a global pad index.
static u32 ConvertPortAndSlotToPad(u32 port, u32 slot);
/// Returns settings for the controller.
static SettingList GetSettings(ControllerType type);
/// Returns true if the given pad index is a multitap slot.
static bool PadIsMultitapSlot(u32 index);
static bool PortAndSlotIsMultitap(u32 port, u32 slot);
/// Returns the configuration section for the specified gamepad.
static std::string GetSettingsSection(u32 pad);
/// Applies an analog deadzone/sensitivity.
static float ApplyAnalogDeadzoneSensitivity(float deadzone, float sensitivity, float value)
{
return (value < deadzone) ? 0.0f : ((value - deadzone) / (1.0f - deadzone) * sensitivity);
}
protected:
u32 m_index;
};
+2 -2
View File
@@ -9,7 +9,7 @@
<PreprocessorDefinitions Condition="('$(Platform)'=='x64' Or '$(Platform)'=='ARM' Or '$(Platform)'=='ARM64')">WITH_RECOMPILER=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="('$(Platform)'=='x64' Or '$(Platform)'=='ARM64') And ('$(BuildingForUWP)'!='true')">WITH_MMAP_FASTMEM=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\rcheevos\include;$(SolutionDir)dep\rapidjson\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\rcheevos\include;$(SolutionDir)dep\rapidjson\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="('$(BuildingForUWP)'!='true' And '$(Platform)'!='ARM64')">$(SolutionDir)dep\rainterface;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories Condition="'$(Platform)'=='x64'">$(SolutionDir)dep\xbyak\xbyak;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
@@ -19,7 +19,7 @@
<ItemDefinitionGroup>
<Lib>
<AdditionalDependencies>$(RootBuildDir)rcheevos\rcheevos.lib;$(RootBuildDir)imgui\imgui.lib;$(RootBuildDir)stb\stb.lib;$(RootBuildDir)xxhash\xxhash.lib;$(RootBuildDir)zlib\zlib.lib;$(RootBuildDir)util\util.lib;$(RootBuildDir)common\common.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>$(RootBuildDir)tinyxml2\tinyxml2.lib;$(RootBuildDir)rcheevos\rcheevos.lib;$(RootBuildDir)imgui\imgui.lib;$(RootBuildDir)stb\stb.lib;$(RootBuildDir)xxhash\xxhash.lib;$(RootBuildDir)zlib\zlib.lib;$(RootBuildDir)util\util.lib;$(RootBuildDir)common\common.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies Condition="('$(BuildingForUWP)'!='true' And '$(Platform)'!='ARM64')">$(RootBuildDir)rainterface\rainterface.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies Condition="'$(Platform)'=='ARM64'">$(RootBuildDir)vixl\vixl.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Lib>
+6 -10
View File
@@ -9,7 +9,6 @@
<ClCompile Include="cdrom.cpp" />
<ClCompile Include="cdrom_async_reader.cpp" />
<ClCompile Include="cheats.cpp" />
<ClCompile Include="cheevos.cpp" />
<ClCompile Include="cpu_core.cpp" />
<ClCompile Include="cpu_disasm.cpp" />
<ClCompile Include="cpu_code_cache.cpp" />
@@ -33,6 +32,7 @@
</ClCompile>
<ClCompile Include="cpu_types.cpp" />
<ClCompile Include="digital_controller.cpp" />
<ClCompile Include="game_database.cpp" />
<ClCompile Include="gpu_backend.cpp" />
<ClCompile Include="gpu_commands.cpp" />
<ClCompile Include="gpu_hw_d3d11.cpp" />
@@ -47,18 +47,16 @@
<ClCompile Include="gpu.cpp" />
<ClCompile Include="gpu_hw.cpp" />
<ClCompile Include="gpu_hw_opengl.cpp" />
<ClCompile Include="host.cpp" />
<ClCompile Include="host_display.cpp" />
<ClCompile Include="host_interface.cpp" />
<ClCompile Include="host_interface_progress_callback.cpp" />
<ClCompile Include="imgui_fullscreen.cpp" />
<ClCompile Include="imgui_styles.cpp" />
<ClCompile Include="interrupt_controller.cpp" />
<ClCompile Include="libcrypt_game_codes.cpp" />
<ClCompile Include="mdec.cpp" />
<ClCompile Include="memory_card.cpp" />
<ClCompile Include="memory_card_image.cpp" />
<ClCompile Include="multitap.cpp" />
<ClCompile Include="namco_guncon.cpp" />
<ClCompile Include="guncon.cpp" />
<ClCompile Include="negcon.cpp" />
<ClCompile Include="pad.cpp" />
<ClCompile Include="controller.cpp" />
@@ -83,7 +81,7 @@
<ClInclude Include="cdrom.h" />
<ClInclude Include="cdrom_async_reader.h" />
<ClInclude Include="cheats.h" />
<ClInclude Include="cheevos.h" />
<ClInclude Include="achievements.h" />
<ClInclude Include="cpu_core.h" />
<ClInclude Include="cpu_core_private.h" />
<ClInclude Include="cpu_disasm.h" />
@@ -101,6 +99,7 @@
<ExcludedFromBuild Condition="'$(Platform)'=='Win32'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="digital_controller.h" />
<ClInclude Include="game_database.h" />
<ClInclude Include="gpu_backend.h" />
<ClInclude Include="gpu_hw_d3d11.h" />
<ClInclude Include="gpu_hw_d3d12.h" />
@@ -119,18 +118,15 @@
<ClInclude Include="gte_types.h" />
<ClInclude Include="host.h" />
<ClInclude Include="host_display.h" />
<ClInclude Include="host_interface.h" />
<ClInclude Include="host_interface_progress_callback.h" />
<ClInclude Include="host_settings.h" />
<ClInclude Include="imgui_fullscreen.h" />
<ClInclude Include="imgui_styles.h" />
<ClInclude Include="interrupt_controller.h" />
<ClInclude Include="libcrypt_game_codes.h" />
<ClInclude Include="mdec.h" />
<ClInclude Include="memory_card.h" />
<ClInclude Include="memory_card_image.h" />
<ClInclude Include="multitap.h" />
<ClInclude Include="namco_guncon.h" />
<ClInclude Include="guncon.h" />
<ClInclude Include="negcon.h" />
<ClInclude Include="pad.h" />
<ClInclude Include="controller.h" />
+6 -10
View File
@@ -10,7 +10,6 @@
<ClCompile Include="gpu.cpp" />
<ClCompile Include="gpu_hw_opengl.cpp" />
<ClCompile Include="gpu_hw.cpp" />
<ClCompile Include="host_interface.cpp" />
<ClCompile Include="interrupt_controller.cpp" />
<ClCompile Include="cdrom.cpp" />
<ClCompile Include="gte.cpp" />
@@ -40,7 +39,7 @@
<ClCompile Include="timing_event.cpp" />
<ClCompile Include="cdrom_async_reader.cpp" />
<ClCompile Include="psf_loader.cpp" />
<ClCompile Include="namco_guncon.cpp" />
<ClCompile Include="guncon.cpp" />
<ClCompile Include="playstation_mouse.cpp" />
<ClCompile Include="negcon.cpp" />
<ClCompile Include="gpu_hw_vulkan.cpp" />
@@ -58,9 +57,8 @@
<ClCompile Include="texture_replacements.cpp" />
<ClCompile Include="multitap.cpp" />
<ClCompile Include="gpu_hw_d3d12.cpp" />
<ClCompile Include="imgui_fullscreen.cpp" />
<ClCompile Include="imgui_styles.cpp" />
<ClCompile Include="cheevos.cpp" />
<ClCompile Include="host.cpp" />
<ClCompile Include="game_database.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="types.h" />
@@ -74,7 +72,6 @@
<ClInclude Include="gpu.h" />
<ClInclude Include="gpu_hw_opengl.h" />
<ClInclude Include="gpu_hw.h" />
<ClInclude Include="host_interface.h" />
<ClInclude Include="interrupt_controller.h" />
<ClInclude Include="cdrom.h" />
<ClInclude Include="gte.h" />
@@ -101,7 +98,7 @@
<ClInclude Include="timing_event.h" />
<ClInclude Include="cdrom_async_reader.h" />
<ClInclude Include="psf_loader.h" />
<ClInclude Include="namco_guncon.h" />
<ClInclude Include="guncon.h" />
<ClInclude Include="playstation_mouse.h" />
<ClInclude Include="negcon.h" />
<ClInclude Include="gpu_hw_vulkan.h" />
@@ -123,10 +120,9 @@
<ClInclude Include="multitap.h" />
<ClInclude Include="gpu_hw_d3d12.h" />
<ClInclude Include="gdb_protocol.h" />
<ClInclude Include="imgui_fullscreen.h" />
<ClInclude Include="imgui_styles.h" />
<ClInclude Include="cheevos.h" />
<ClInclude Include="host.h" />
<ClInclude Include="host_settings.h" />
<ClInclude Include="achievements.h" />
<ClInclude Include="game_database.h" />
</ItemGroup>
</Project>
+19 -23
View File
@@ -7,7 +7,7 @@
#include "cpu_disasm.h"
#include "cpu_recompiler_thunks.h"
#include "gte.h"
#include "host_interface.h"
#include "host.h"
#include "pgxp.h"
#include "settings.h"
#include "system.h"
@@ -1691,8 +1691,8 @@ bool AddBreakpoint(VirtualMemoryAddress address, bool auto_clear, bool enabled)
if (!auto_clear)
{
g_host_interface->ReportFormattedDebuggerMessage(
g_host_interface->TranslateString("DebuggerMessage", "Added breakpoint at 0x%08X."), address);
Host::ReportFormattedDebuggerMessage(Host::TranslateString("DebuggerMessage", "Added breakpoint at 0x%08X."),
address);
}
return true;
@@ -1718,8 +1718,8 @@ bool RemoveBreakpoint(VirtualMemoryAddress address)
if (it == s_breakpoints.end())
return false;
g_host_interface->ReportFormattedDebuggerMessage(
g_host_interface->TranslateString("DebuggerMessage", "Removed breakpoint at 0x%08X."), address);
Host::ReportFormattedDebuggerMessage(Host::TranslateString("DebuggerMessage", "Removed breakpoint at 0x%08X."),
address);
s_breakpoints.erase(it);
UpdateDebugDispatcherFlag();
@@ -1750,8 +1750,8 @@ bool AddStepOverBreakpoint()
if (!IsCallInstruction(inst))
{
g_host_interface->ReportFormattedDebuggerMessage(
g_host_interface->TranslateString("DebuggerMessage", "0x%08X is not a call instruction."), g_state.regs.pc);
Host::ReportFormattedDebuggerMessage(Host::TranslateString("DebuggerMessage", "0x%08X is not a call instruction."),
g_state.regs.pc);
return false;
}
@@ -1760,16 +1760,15 @@ bool AddStepOverBreakpoint()
if (IsBranchInstruction(inst))
{
g_host_interface->ReportFormattedDebuggerMessage(
g_host_interface->TranslateString("DebuggerMessage", "Can't step over double branch at 0x%08X"), g_state.regs.pc);
Host::ReportFormattedDebuggerMessage(
Host::TranslateString("DebuggerMessage", "Can't step over double branch at 0x%08X"), g_state.regs.pc);
return false;
}
// skip the delay slot
bp_pc += sizeof(Instruction);
g_host_interface->ReportFormattedDebuggerMessage(
g_host_interface->TranslateString("DebuggerMessage", "Stepping over to 0x%08X."), bp_pc);
Host::ReportFormattedDebuggerMessage(Host::TranslateString("DebuggerMessage", "Stepping over to 0x%08X."), bp_pc);
return AddBreakpoint(bp_pc, true);
}
@@ -1785,25 +1784,22 @@ bool AddStepOutBreakpoint(u32 max_instructions_to_search)
Instruction inst;
if (!SafeReadInstruction(ret_pc, &inst.bits))
{
g_host_interface->ReportFormattedDebuggerMessage(
g_host_interface->TranslateString("DebuggerMessage",
"Instruction read failed at %08X while searching for function end."),
Host::ReportFormattedDebuggerMessage(
Host::TranslateString("DebuggerMessage", "Instruction read failed at %08X while searching for function end."),
ret_pc);
return false;
}
if (IsReturnInstruction(inst))
{
g_host_interface->ReportFormattedDebuggerMessage(
g_host_interface->TranslateString("DebuggerMessage", "Stepping out to 0x%08X."), ret_pc);
Host::ReportFormattedDebuggerMessage(Host::TranslateString("DebuggerMessage", "Stepping out to 0x%08X."), ret_pc);
return AddBreakpoint(ret_pc, true);
}
}
g_host_interface->ReportFormattedDebuggerMessage(
g_host_interface->TranslateString("DebuggerMessage",
"No return instruction found after %u instructions for step-out at %08X."),
Host::ReportFormattedDebuggerMessage(
Host::TranslateString("DebuggerMessage", "No return instruction found after %u instructions for step-out at %08X."),
max_instructions_to_search, g_state.regs.pc);
return false;
@@ -1857,18 +1853,18 @@ ALWAYS_INLINE_RELEASE static bool BreakpointCheck()
}
else
{
g_host_interface->PauseSystem(true);
System::PauseSystem(true);
if (bp.auto_clear)
{
g_host_interface->ReportFormattedDebuggerMessage("Stopped execution at 0x%08X.", pc);
Host::ReportFormattedDebuggerMessage("Stopped execution at 0x%08X.", pc);
s_breakpoints.erase(s_breakpoints.begin() + i);
count--;
UpdateDebugDispatcherFlag();
}
else
{
g_host_interface->ReportFormattedDebuggerMessage("Hit breakpoint %u at 0x%08X.", bp.number, pc);
Host::ReportFormattedDebuggerMessage("Hit breakpoint %u at 0x%08X.", bp.number, pc);
i++;
}
}
@@ -1979,7 +1975,7 @@ void SingleStep()
{
s_single_step = true;
ExecuteDebug();
g_host_interface->ReportFormattedDebuggerMessage("Stepped to 0x%08X.", g_state.regs.pc);
Host::ReportFormattedDebuggerMessage("Stepped to 0x%08X.", g_state.regs.pc);
}
namespace CodeCache {
+52 -97
View File
@@ -1,10 +1,10 @@
#include "digital_controller.h"
#include "common/assert.h"
#include "host_interface.h"
#include "host.h"
#include "system.h"
#include "util/state_wrapper.h"
DigitalController::DigitalController() = default;
DigitalController::DigitalController(u32 index) : Controller(index) {}
DigitalController::~DigitalController() = default;
@@ -13,16 +13,6 @@ ControllerType DigitalController::GetType() const
return ControllerType::DigitalController;
}
std::optional<s32> DigitalController::GetAxisCodeByName(std::string_view axis_name) const
{
return StaticGetAxisCodeByName(axis_name);
}
std::optional<s32> DigitalController::GetButtonCodeByName(std::string_view button_name) const
{
return StaticGetButtonCodeByName(button_name);
}
void DigitalController::Reset()
{
m_transfer_state = TransferState::Idle;
@@ -42,18 +32,25 @@ bool DigitalController::DoState(StateWrapper& sw, bool apply_input_state)
return true;
}
bool DigitalController::GetButtonState(s32 button_code) const
float DigitalController::GetBindState(u32 index) const
{
if (button_code < 0 || button_code >= static_cast<s32>(Button::Count))
return false;
const u16 bit = u16(1) << static_cast<u8>(button_code);
return ((m_button_state & bit) == 0);
if (index < static_cast<u32>(Button::Count))
{
return static_cast<float>(((m_button_state >> index) & 1u) ^ 1u);
}
else
{
return 0.0f;
}
}
void DigitalController::SetButtonState(Button button, bool pressed)
void DigitalController::SetBindState(u32 index, float value)
{
const u16 bit = u16(1) << static_cast<u8>(button);
if (index >= static_cast<u32>(Button::Count))
return;
const bool pressed = (value >= 0.5f);
const u16 bit = u16(1) << static_cast<u8>(index);
if (pressed)
{
if (m_button_state & bit)
@@ -70,14 +67,6 @@ void DigitalController::SetButtonState(Button button, bool pressed)
}
}
void DigitalController::SetButtonState(s32 button_code, bool pressed)
{
if (button_code < 0 || button_code >= static_cast<s32>(Button::Count))
return;
SetButtonState(static_cast<Button>(button_code), pressed);
}
u32 DigitalController::GetButtonStateBits() const
{
return m_button_state ^ 0xFFFF;
@@ -146,87 +135,53 @@ bool DigitalController::Transfer(const u8 data_in, u8* data_out)
}
}
std::unique_ptr<DigitalController> DigitalController::Create()
std::unique_ptr<DigitalController> DigitalController::Create(u32 index)
{
return std::make_unique<DigitalController>();
return std::make_unique<DigitalController>(index);
}
std::optional<s32> DigitalController::StaticGetAxisCodeByName(std::string_view button_name)
{
return std::nullopt;
}
std::optional<s32> DigitalController::StaticGetButtonCodeByName(std::string_view button_name)
{
#define BUTTON(name) \
if (button_name == #name) \
static const Controller::ControllerBindingInfo s_binding_info[] = {
#define BUTTON(name, display_name, button, genb) \
{ \
return static_cast<s32>(ZeroExtend32(static_cast<u8>(Button::name))); \
name, display_name, static_cast<u32>(button), Controller::ControllerBindingType::Button, genb \
}
BUTTON(Select);
BUTTON(L3);
BUTTON(R3);
BUTTON(Start);
BUTTON(Up);
BUTTON(Right);
BUTTON(Down);
BUTTON(Left);
BUTTON(L2);
BUTTON(R2);
BUTTON(L1);
BUTTON(R1);
BUTTON(Triangle);
BUTTON(Circle);
BUTTON(Cross);
BUTTON(Square);
return std::nullopt;
BUTTON("Up", "D-Pad Up", DigitalController::Button::Up, GenericInputBinding::DPadUp),
BUTTON("Right", "D-Pad Right", DigitalController::Button::Right, GenericInputBinding::DPadRight),
BUTTON("Down", "D-Pad Down", DigitalController::Button::Down, GenericInputBinding::DPadDown),
BUTTON("Left", "D-Pad Left", DigitalController::Button::Left, GenericInputBinding::DPadLeft),
BUTTON("Triangle", "Triangle", DigitalController::Button::Triangle, GenericInputBinding::Triangle),
BUTTON("Circle", "Circle", DigitalController::Button::Circle, GenericInputBinding::Circle),
BUTTON("Cross", "Cross", DigitalController::Button::Cross, GenericInputBinding::Cross),
BUTTON("Square", "Square", DigitalController::Button::Square, GenericInputBinding::Square),
BUTTON("Select", "Select", DigitalController::Button::Select, GenericInputBinding::Select),
BUTTON("Start", "Start", DigitalController::Button::Start, GenericInputBinding::Start),
BUTTON("L1", "L1", DigitalController::Button::L1, GenericInputBinding::L1),
BUTTON("R1", "R1", DigitalController::Button::R1, GenericInputBinding::R1),
BUTTON("L2", "L2", DigitalController::Button::L2, GenericInputBinding::L2),
BUTTON("R2", "R2", DigitalController::Button::R2, GenericInputBinding::R2),
#undef BUTTON
}
};
Controller::AxisList DigitalController::StaticGetAxisNames()
{
return {};
}
static const SettingInfo s_settings[] = {
{SettingInfo::Type::Boolean, "ForcePopnControllerMode",
TRANSLATABLE("DigitalController", "Force Pop'n Controller Mode"),
TRANSLATABLE("DigitalController", "Forces the Digital Controller to act as a Pop'n Controller."), "false"}};
Controller::ButtonList DigitalController::StaticGetButtonNames()
{
return {{TRANSLATABLE("DigitalController", "Up"), static_cast<s32>(Button::Up)},
{TRANSLATABLE("DigitalController", "Down"), static_cast<s32>(Button::Down)},
{TRANSLATABLE("DigitalController", "Left"), static_cast<s32>(Button::Left)},
{TRANSLATABLE("DigitalController", "Right"), static_cast<s32>(Button::Right)},
{TRANSLATABLE("DigitalController", "Select"), static_cast<s32>(Button::Select)},
{TRANSLATABLE("DigitalController", "Start"), static_cast<s32>(Button::Start)},
{TRANSLATABLE("DigitalController", "Triangle"), static_cast<s32>(Button::Triangle)},
{TRANSLATABLE("DigitalController", "Cross"), static_cast<s32>(Button::Cross)},
{TRANSLATABLE("DigitalController", "Circle"), static_cast<s32>(Button::Circle)},
{TRANSLATABLE("DigitalController", "Square"), static_cast<s32>(Button::Square)},
{TRANSLATABLE("DigitalController", "L1"), static_cast<s32>(Button::L1)},
{TRANSLATABLE("DigitalController", "L2"), static_cast<s32>(Button::L2)},
{TRANSLATABLE("DigitalController", "R1"), static_cast<s32>(Button::R1)},
{TRANSLATABLE("DigitalController", "R2"), static_cast<s32>(Button::R2)}};
}
const Controller::ControllerInfo DigitalController::INFO = {ControllerType::DigitalController,
"DigitalController",
TRANSLATABLE("ControllerType", "Digital Controller"),
s_binding_info,
countof(s_binding_info),
s_settings,
countof(s_settings),
Controller::VibrationCapabilities::NoVibration};
u32 DigitalController::StaticGetVibrationMotorCount()
void DigitalController::LoadSettings(SettingsInterface& si, const char* section)
{
return 0;
}
Controller::SettingList DigitalController::StaticGetSettings()
{
static constexpr std::array<SettingInfo, 1> settings = {
{{SettingInfo::Type::Boolean, "ForcePopnControllerMode",
TRANSLATABLE("DigitalController", "Force Pop'n Controller Mode"),
TRANSLATABLE("DigitalController", "Forces the Digital Controller to act as a Pop'n Controller."), "false"}}};
return SettingList(settings.begin(), settings.end());
}
void DigitalController::LoadSettings(const char* section)
{
Controller::LoadSettings(section);
m_popn_controller_mode = g_host_interface->GetBoolSettingValue(section, "ForcePopnControllerMode", false);
Controller::LoadSettings(si, section);
m_popn_controller_mode = si.GetBoolValue(section, "ForcePopnControllerMode", false);
}
u8 DigitalController::GetButtonsLSBMask() const
+7 -15
View File
@@ -28,34 +28,26 @@ public:
Count
};
DigitalController();
static const Controller::ControllerInfo INFO;
DigitalController(u32 index);
~DigitalController() override;
static std::unique_ptr<DigitalController> Create();
static std::optional<s32> StaticGetAxisCodeByName(std::string_view button_name);
static std::optional<s32> StaticGetButtonCodeByName(std::string_view button_name);
static AxisList StaticGetAxisNames();
static ButtonList StaticGetButtonNames();
static u32 StaticGetVibrationMotorCount();
static SettingList StaticGetSettings();
static std::unique_ptr<DigitalController> Create(u32 index);
ControllerType GetType() const override;
std::optional<s32> GetAxisCodeByName(std::string_view axis_name) const override;
std::optional<s32> GetButtonCodeByName(std::string_view button_name) const override;
void Reset() override;
bool DoState(StateWrapper& sw, bool apply_input_state) override;
bool GetButtonState(s32 button_code) const override;
void SetButtonState(s32 button_code, bool pressed) override;
float GetBindState(u32 index) const override;
void SetBindState(u32 index, float value) override;
u32 GetButtonStateBits() const override;
void ResetTransferState() override;
bool Transfer(const u8 data_in, u8* data_out) override;
void SetButtonState(Button button, bool pressed);
void LoadSettings(const char* section) override;
void LoadSettings(SettingsInterface& si, const char* section) override;
private:
enum class TransferState : u8
+2 -1
View File
@@ -6,6 +6,7 @@
#include "cpu_code_cache.h"
#include "cpu_core.h"
#include "gpu.h"
#include "host.h"
#include "imgui.h"
#include "interrupt_controller.h"
#include "mdec.h"
@@ -626,7 +627,7 @@ void DMA::DrawDebugStateWindow()
{"MDECin", "MDECout", "GPU", "CDROM", "SPU", "PIO", "OTC"}};
static constexpr std::array<const char*, 4> sync_mode_names = {{"Manual", "Request", "LinkedList", "Reserved"}};
const float framebuffer_scale = ImGui::GetIO().DisplayFramebufferScale.x;
const float framebuffer_scale = Host::GetOSDScale();
ImGui::SetNextWindowSize(ImVec2(850.0f * framebuffer_scale, 250.0f * framebuffer_scale), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("DMA State", nullptr))
File diff suppressed because it is too large Load Diff
+122
View File
@@ -0,0 +1,122 @@
#pragma once
#include "core/types.h"
#include "util/cd_image_hasher.h"
#include <bitset>
#include <map>
#include <string>
#include <string_view>
#include <vector>
class CDImage;
struct Settings;
namespace GameDatabase {
enum class CompatibilityRating : u32
{
Unknown = 0,
DoesntBoot = 1,
CrashesInIntro = 2,
CrashesInGame = 3,
GraphicalAudioIssues = 4,
NoIssues = 5,
Count,
};
enum class Trait : u32
{
ForceInterpreter,
ForceSoftwareRenderer,
ForceSoftwareRendererForReadbacks,
ForceInterlacing,
DisableTrueColor,
DisableUpscaling,
DisableScaledDithering,
DisableForceNTSCTimings,
DisableWidescreen,
DisablePGXP,
DisablePGXPCulling,
DisablePGXPTextureCorrection,
DisablePGXPDepthBuffer,
ForcePGXPVertexCache,
ForcePGXPCPUMode,
ForceRecompilerMemoryExceptions,
ForceRecompilerICache,
ForceRecompilerLUTFastmem,
Count
};
struct Entry
{
// TODO: Make string_view.
std::string serial;
std::string title;
std::string genre;
std::string developer;
std::string publisher;
u64 release_date;
u8 min_players;
u8 max_players;
u8 min_blocks;
u8 max_blocks;
u32 supported_controllers;
CompatibilityRating compatibility;
std::bitset<static_cast<int>(Trait::Count)> traits{};
std::optional<s16> display_active_start_offset;
std::optional<s16> display_active_end_offset;
std::optional<s8> display_line_start_offset;
std::optional<s8> display_line_end_offset;
std::optional<u32> dma_max_slice_ticks;
std::optional<u32> dma_halt_ticks;
std::optional<u32> gpu_fifo_size;
std::optional<u32> gpu_max_run_ahead;
std::optional<float> gpu_pgxp_tolerance;
std::optional<float> gpu_pgxp_depth_threshold;
ALWAYS_INLINE bool HasTrait(Trait trait) const { return traits[static_cast<int>(trait)]; }
void ApplySettings(Settings& settings, bool display_osd_messages) const;
};
void EnsureLoaded();
void Unload();
const Entry* GetEntryForDisc(CDImage* image);
const Entry* GetEntryForSerial(const std::string_view& serial);
const Entry* GetEntryForCode(const std::string_view& code);
std::string GetSerialForDisc(CDImage* image);
std::string GetSerialForPath(const char* path);
const char* GetTraitName(Trait trait);
const char* GetTraitDisplayName(Trait trait);
const char* GetCompatibilityRatingName(CompatibilityRating rating);
const char* GetCompatibilityRatingDisplayName(CompatibilityRating rating);
/// Map of track hashes for image verification
struct TrackData
{
TrackData(std::vector<std::string> codes, std::string revisionString, uint32_t revision)
: codes(std::move(codes)), revisionString(revisionString), revision(revision)
{
}
friend bool operator==(const TrackData& left, const TrackData& right)
{
// 'revisionString' is deliberately ignored in comparisons as it's redundant with comparing 'revision'! Do not
// change!
return left.codes == right.codes && left.revision == right.revision;
}
std::vector<std::string> codes;
std::string revisionString;
uint32_t revision;
};
using TrackHashesMap = std::multimap<CDImageHasher::Hash, TrackData>;
const TrackHashesMap& GetTrackHashesMap();
void EnsureTrackHashesMapLoaded();
} // namespace GameDatabase
+1 -1
View File
@@ -4,7 +4,7 @@
#include "common/log.h"
#include "common/string_util.h"
#include "cpu_core.h"
#include "frontend-common/common_host_interface.h"
#include "frontend-common/common_host.h"
#include "system.h"
#include <functional>
#include <iomanip>
+20 -7
View File
@@ -4,8 +4,8 @@
#include "common/log.h"
#include "common/string_util.h"
#include "dma.h"
#include "host.h"
#include "host_display.h"
#include "host_interface.h"
#include "imgui.h"
#include "interrupt_controller.h"
#include "settings.h"
@@ -24,9 +24,8 @@ GPU::GPU() = default;
GPU::~GPU() = default;
bool GPU::Initialize(HostDisplay* host_display)
bool GPU::Initialize()
{
m_host_display = host_display;
m_force_progressive_scan = g_settings.gpu_disable_interlacing;
m_force_ntsc_timings = g_settings.gpu_force_ntsc_timings;
m_crtc_tick_event = TimingEvents::CreateTimingEvent(
@@ -41,6 +40,16 @@ bool GPU::Initialize(HostDisplay* host_display)
m_max_run_ahead = g_settings.gpu_max_run_ahead;
m_console_is_pal = System::IsPALRegion();
UpdateCRTCConfig();
g_host_display->SetDisplayLinearFiltering(g_settings.display_linear_filtering);
g_host_display->SetDisplayIntegerScaling(g_settings.display_integer_scaling);
g_host_display->SetDisplayStretch(g_settings.display_stretch);
if (g_settings.display_post_processing &&
!g_host_display->SetPostProcessingChain(g_settings.display_post_process_chain))
{
Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "Failed to load post processing shader chain."), 20.0f);
}
return true;
}
@@ -59,6 +68,10 @@ void GPU::UpdateSettings()
// Crop mode calls this, so recalculate the display area
UpdateCRTCDisplayParameters();
g_host_display->SetDisplayLinearFiltering(g_settings.display_linear_filtering);
g_host_display->SetDisplayIntegerScaling(g_settings.display_integer_scaling);
g_host_display->SetDisplayStretch(g_settings.display_stretch);
}
bool GPU::IsHardwareRenderer()
@@ -962,9 +975,9 @@ void GPU::UpdateCommandTickEvent()
bool GPU::ConvertScreenCoordinatesToBeamTicksAndLines(s32 window_x, s32 window_y, float x_scale, u32* out_tick,
u32* out_line) const
{
auto [display_x, display_y] = m_host_display->ConvertWindowCoordinatesToDisplayCoordinates(
window_x, window_y, m_host_display->GetWindowWidth(), m_host_display->GetWindowHeight(),
m_host_display->GetDisplayTopMargin());
auto [display_x, display_y] = g_host_display->ConvertWindowCoordinatesToDisplayCoordinates(
window_x, window_y, g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight(),
g_host_display->GetDisplayTopMargin());
if (x_scale != 1.0f)
{
@@ -1551,7 +1564,7 @@ bool GPU::DumpVRAMToFile(const char* filename, u32 width, u32 height, u32 stride
void GPU::DrawDebugStateWindow()
{
const float framebuffer_scale = ImGui::GetIO().DisplayFramebufferScale.x;
const float framebuffer_scale = Host::GetOSDScale();
ImGui::SetNextWindowSize(ImVec2(450.0f * framebuffer_scale, 550.0f * framebuffer_scale), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("GPU", nullptr))
+1 -3
View File
@@ -75,7 +75,7 @@ public:
virtual GPURenderer GetRendererType() const = 0;
virtual bool Initialize(HostDisplay* host_display);
virtual bool Initialize();
virtual void Reset(bool clear_vram);
virtual bool DoState(StateWrapper& sw, HostDisplayTexture** save_to_texture, bool update_display);
@@ -319,8 +319,6 @@ protected:
AddCommandTicks(std::max(width, height));
}
HostDisplay* m_host_display = nullptr;
std::unique_ptr<TimingEvent> m_crtc_tick_event;
std::unique_ptr<TimingEvent> m_command_tick_event;
+2 -2
View File
@@ -173,7 +173,7 @@ void GPUBackend::StartGPUThread()
{
m_gpu_loop_done.store(false);
m_use_gpu_thread = true;
m_gpu_thread = std::thread(&GPUBackend::RunGPULoop, this);
m_gpu_thread.Start([this]() { RunGPULoop(); });
Log_InfoPrint("GPU thread started.");
}
@@ -184,7 +184,7 @@ void GPUBackend::StopGPUThread()
m_gpu_loop_done.store(true);
WakeGPUThread();
m_gpu_thread.join();
m_gpu_thread.Join();
m_use_gpu_thread = false;
Log_InfoPrint("GPU thread stopped.");
}
+3 -1
View File
@@ -1,6 +1,7 @@
#pragma once
#include "common/event.h"
#include "common/heap_array.h"
#include "common/threading.h"
#include "gpu_types.h"
#include <atomic>
#include <condition_variable>
@@ -20,6 +21,7 @@ public:
virtual ~GPUBackend();
ALWAYS_INLINE u16* GetVRAM() const { return m_vram_ptr; }
ALWAYS_INLINE const Threading::Thread* GetThread() const { return m_use_gpu_thread ? &m_gpu_thread : nullptr; }
virtual bool Initialize(bool force_thread);
virtual void UpdateSettings();
@@ -67,7 +69,7 @@ protected:
Common::Event m_sync_event;
std::atomic_bool m_gpu_thread_sleeping{false};
std::atomic_bool m_gpu_loop_done{false};
std::thread m_gpu_thread;
Threading::Thread m_gpu_thread;
bool m_use_gpu_thread = false;
std::mutex m_sync_mutex;
+27 -31
View File
@@ -4,6 +4,7 @@
#include "common/log.h"
#include "cpu_core.h"
#include "gpu_sw_backend.h"
#include "host.h"
#include "imgui.h"
#include "pgxp.h"
#include "settings.h"
@@ -43,14 +44,14 @@ GPU_HW::~GPU_HW()
}
}
bool GPU_HW::Initialize(HostDisplay* host_display)
bool GPU_HW::Initialize()
{
if (!GPU::Initialize(host_display))
if (!GPU::Initialize())
return false;
m_resolution_scale = CalculateResolutionScale();
m_multisamples = std::min(g_settings.gpu_multisamples, m_max_multisamples);
m_render_api = host_display->GetRenderAPI();
m_render_api = g_host_display->GetRenderAPI();
m_per_sample_shading = g_settings.gpu_per_sample_shading && m_supports_per_sample_shading;
m_true_color = g_settings.gpu_true_color;
m_scaled_dithering = g_settings.gpu_scaled_dithering;
@@ -61,29 +62,26 @@ bool GPU_HW::Initialize(HostDisplay* host_display)
if (m_multisamples != g_settings.gpu_multisamples)
{
g_host_interface->AddFormattedOSDMessage(
20.0f, g_host_interface->TranslateString("OSDMessage", "%ux MSAA is not supported, using %ux instead."),
g_settings.gpu_multisamples, m_multisamples);
Host::AddFormattedOSDMessage(20.0f,
Host::TranslateString("OSDMessage", "%ux MSAA is not supported, using %ux instead."),
g_settings.gpu_multisamples, m_multisamples);
}
if (!m_per_sample_shading && g_settings.gpu_per_sample_shading)
{
g_host_interface->AddOSDMessage(
g_host_interface->TranslateStdString("OSDMessage", "SSAA is not supported, using MSAA instead."), 20.0f);
Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "SSAA is not supported, using MSAA instead."), 20.0f);
}
if (!m_supports_dual_source_blend && TextureFilterRequiresDualSourceBlend(m_texture_filtering))
{
g_host_interface->AddFormattedOSDMessage(
20.0f,
g_host_interface->TranslateString("OSDMessage",
"Texture filter '%s' is not supported with the current renderer."),
Host::AddFormattedOSDMessage(
20.0f, Host::TranslateString("OSDMessage", "Texture filter '%s' is not supported with the current renderer."),
Settings::GetTextureFilterDisplayName(m_texture_filtering));
m_texture_filtering = GPUTextureFilter::Nearest;
}
if (!m_supports_adaptive_downsampling && g_settings.gpu_resolution_scale > 1 &&
g_settings.gpu_downsample_mode == GPUDownsampleMode::Adaptive)
{
g_host_interface->AddOSDMessage(
g_host_interface->TranslateStdString(
Host::AddOSDMessage(
Host::TranslateStdString(
"OSDMessage", "Adaptive downsampling is not supported with the current renderer, using box filter instead."),
20.0f);
}
@@ -149,27 +147,26 @@ void GPU_HW::UpdateHWSettings(bool* framebuffer_changed, bool* shaders_changed)
if (m_resolution_scale != resolution_scale)
{
g_host_interface->AddKeyedFormattedOSDMessage(
Host::AddKeyedFormattedOSDMessage(
"ResolutionScale", 10.0f,
g_host_interface->TranslateString("OSDMessage", "Resolution scale set to %ux (display %ux%u, VRAM %ux%u)"),
resolution_scale, m_crtc_state.display_vram_width * resolution_scale,
resolution_scale * m_crtc_state.display_vram_height, VRAM_WIDTH * resolution_scale,
VRAM_HEIGHT * resolution_scale);
Host::TranslateString("OSDMessage", "Resolution scale set to %ux (display %ux%u, VRAM %ux%u)"), resolution_scale,
m_crtc_state.display_vram_width * resolution_scale, resolution_scale * m_crtc_state.display_vram_height,
VRAM_WIDTH * resolution_scale, VRAM_HEIGHT * resolution_scale);
}
if (m_multisamples != multisamples || m_per_sample_shading != per_sample_shading)
{
if (per_sample_shading)
{
g_host_interface->AddKeyedFormattedOSDMessage(
"Multisampling", 10.0f,
g_host_interface->TranslateString("OSDMessage", "Multisample anti-aliasing set to %ux (SSAA)."), multisamples);
Host::AddKeyedFormattedOSDMessage(
"Multisampling", 10.0f, Host::TranslateString("OSDMessage", "Multisample anti-aliasing set to %ux (SSAA)."),
multisamples);
}
else
{
g_host_interface->AddKeyedFormattedOSDMessage(
"Multisampling", 10.0f,
g_host_interface->TranslateString("OSDMessage", "Multisample anti-aliasing set to %ux."), multisamples);
Host::AddKeyedFormattedOSDMessage("Multisampling", 10.0f,
Host::TranslateString("OSDMessage", "Multisample anti-aliasing set to %ux."),
multisamples);
}
}
@@ -215,7 +212,7 @@ u32 GPU_HW::CalculateResolutionScale() const
(m_console_is_pal ? (PAL_VERTICAL_ACTIVE_END - PAL_VERTICAL_ACTIVE_START) :
(NTSC_VERTICAL_ACTIVE_END - NTSC_VERTICAL_ACTIVE_START));
const s32 preferred_scale =
static_cast<s32>(std::ceil(static_cast<float>(m_host_display->GetWindowHeight()) / height));
static_cast<s32>(std::ceil(static_cast<float>(g_host_display->GetWindowHeight()) / height));
Log_InfoPrintf("Height = %d, preferred scale = %d", height, preferred_scale);
scale = static_cast<u32>(std::clamp<s32>(preferred_scale, 1, m_max_resolution_scale));
@@ -229,10 +226,9 @@ u32 GPU_HW::CalculateResolutionScale() const
if (g_settings.gpu_resolution_scale != 0)
{
g_host_interface->AddFormattedOSDMessage(
Host::AddFormattedOSDMessage(
10.0f,
g_host_interface->TranslateString("OSDMessage",
"Resolution scale %ux not supported for adaptive smoothing, using %ux."),
Host::TranslateString("OSDMessage", "Resolution scale %ux not supported for adaptive smoothing, using %ux."),
scale, new_scale);
}
@@ -1410,7 +1406,7 @@ void GPU_HW::DrawRendererStats(bool is_idle_frame)
const auto& stats = m_last_renderer_stats;
ImGui::Columns(2);
ImGui::SetColumnWidth(0, 200.0f * ImGui::GetIO().DisplayFramebufferScale.x);
ImGui::SetColumnWidth(0, 200.0f * Host::GetOSDScale());
ImGui::TextUnformatted("Resolution Scale:");
ImGui::NextColumn();
@@ -1487,7 +1483,7 @@ void GPU_HW::ShaderCompileProgressTracker::Increment()
const u64 tv = Common::Timer::GetCurrentValue();
if ((tv - m_start_time) >= m_min_time && (tv - m_last_update_time) >= m_update_interval)
{
g_host_interface->DisplayLoadingScreen(m_title.c_str(), 0, static_cast<int>(m_total), static_cast<int>(m_progress));
Host::DisplayLoadingScreen(m_title.c_str(), 0, static_cast<int>(m_total), static_cast<int>(m_progress));
m_last_update_time = tv;
}
}
+1 -1
View File
@@ -33,7 +33,7 @@ public:
GPU_HW();
virtual ~GPU_HW();
virtual bool Initialize(HostDisplay* host_display) override;
virtual bool Initialize() override;
virtual void Reset(bool clear_vram) override;
virtual bool DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display) override;
+24 -22
View File
@@ -6,7 +6,6 @@
#include "gpu_hw_shadergen.h"
#include "gpu_sw_backend.h"
#include "host_display.h"
#include "host_interface.h"
#include "shader_cache_version.h"
#include "system.h"
#include "util/state_wrapper.h"
@@ -16,8 +15,11 @@ GPU_HW_D3D11::GPU_HW_D3D11() = default;
GPU_HW_D3D11::~GPU_HW_D3D11()
{
if (m_host_display)
m_host_display->ClearDisplayTexture();
if (g_host_display)
{
g_host_display->ClearDisplayTexture();
ResetGraphicsAPIState();
}
if (m_context)
m_context->ClearState();
@@ -31,22 +33,22 @@ GPURenderer GPU_HW_D3D11::GetRendererType() const
return GPURenderer::HardwareD3D11;
}
bool GPU_HW_D3D11::Initialize(HostDisplay* host_display)
bool GPU_HW_D3D11::Initialize()
{
if (host_display->GetRenderAPI() != HostDisplay::RenderAPI::D3D11)
if (!Host::AcquireHostDisplay(HostDisplay::RenderAPI::D3D11))
{
Log_ErrorPrintf("Host render API is incompatible");
return false;
}
m_device = static_cast<ID3D11Device*>(host_display->GetRenderDevice());
m_context = static_cast<ID3D11DeviceContext*>(host_display->GetRenderContext());
m_device = static_cast<ID3D11Device*>(g_host_display->GetRenderDevice());
m_context = static_cast<ID3D11DeviceContext*>(g_host_display->GetRenderContext());
if (!m_device || !m_context)
return false;
SetCapabilities();
if (!GPU_HW::Initialize(host_display))
if (!GPU_HW::Initialize())
return false;
if (!CreateFramebuffer())
@@ -122,7 +124,7 @@ bool GPU_HW_D3D11::DoState(StateWrapper& sw, HostDisplayTexture** host_texture,
{
delete tex;
tex = m_host_display
tex = g_host_display
->CreateTexture(m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), 1, 1,
m_vram_texture.GetSamples(), HostDisplayPixelFormat::RGBA8, nullptr, 0, false)
.release();
@@ -178,7 +180,7 @@ void GPU_HW_D3D11::UpdateSettings()
RestoreGraphicsAPIState();
ReadVRAM(0, 0, VRAM_WIDTH, VRAM_HEIGHT);
ResetGraphicsAPIState();
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
CreateFramebuffer();
}
@@ -501,10 +503,10 @@ void GPU_HW_D3D11::DestroyStateObjects()
bool GPU_HW_D3D11::CompileShaders()
{
D3D11::ShaderCache shader_cache;
shader_cache.Open(g_host_interface->GetShaderCacheBasePath(), m_device->GetFeatureLevel(), SHADER_CACHE_VERSION,
shader_cache.Open(EmuFolders::Cache, m_device->GetFeatureLevel(), SHADER_CACHE_VERSION,
g_settings.gpu_use_debug_device);
GPU_HW_ShaderGen shadergen(m_host_display->GetRenderAPI(), m_resolution_scale, m_multisamples, m_per_sample_shading,
GPU_HW_ShaderGen shadergen(g_host_display->GetRenderAPI(), m_resolution_scale, m_multisamples, m_per_sample_shading,
m_true_color, m_scaled_dithering, m_texture_filtering, m_using_uv_limits,
m_pgxp_depth_buffer, m_supports_dual_source_blend);
@@ -826,7 +828,7 @@ void GPU_HW_D3D11::ClearDisplay()
{
GPU_HW::ClearDisplay();
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
static constexpr std::array<float, 4> clear_color = {0.0f, 0.0f, 0.0f, 1.0f};
m_context->ClearRenderTargetView(m_display_texture.GetD3DRTV(), clear_color.data());
@@ -841,23 +843,23 @@ void GPU_HW_D3D11::UpdateDisplay()
if (IsUsingMultisampling())
{
UpdateVRAMReadTexture();
m_host_display->SetDisplayTexture(m_vram_read_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
g_host_display->SetDisplayTexture(m_vram_read_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
m_vram_read_texture.GetWidth(), m_vram_read_texture.GetHeight(), 0, 0,
m_vram_read_texture.GetWidth(), m_vram_read_texture.GetHeight());
}
else
{
m_host_display->SetDisplayTexture(m_vram_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
g_host_display->SetDisplayTexture(m_vram_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), 0, 0,
m_vram_texture.GetWidth(), m_vram_texture.GetHeight());
}
m_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
g_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
static_cast<float>(VRAM_WIDTH) / static_cast<float>(VRAM_HEIGHT));
}
else
{
m_host_display->SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height,
g_host_display->SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height,
m_crtc_state.display_origin_left, m_crtc_state.display_origin_top,
m_crtc_state.display_vram_width, m_crtc_state.display_vram_height,
GetDisplayAspectRatio());
@@ -875,7 +877,7 @@ void GPU_HW_D3D11::UpdateDisplay()
if (IsDisplayDisabled())
{
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
}
else if (!m_GPUSTAT.display_area_color_depth_24 && interlaced == InterlacedRenderMode::None &&
!IsUsingMultisampling() && (scaled_vram_offset_x + scaled_display_width) <= m_vram_texture.GetWidth() &&
@@ -889,7 +891,7 @@ void GPU_HW_D3D11::UpdateDisplay()
}
else
{
m_host_display->SetDisplayTexture(m_vram_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
g_host_display->SetDisplayTexture(m_vram_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), scaled_vram_offset_x,
scaled_vram_offset_y, scaled_display_width, scaled_display_height);
}
@@ -921,7 +923,7 @@ void GPU_HW_D3D11::UpdateDisplay()
}
else
{
m_host_display->SetDisplayTexture(m_display_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
g_host_display->SetDisplayTexture(m_display_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
m_display_texture.GetWidth(), m_display_texture.GetHeight(), 0, 0,
scaled_display_width, scaled_display_height);
}
@@ -1202,7 +1204,7 @@ void GPU_HW_D3D11::DownsampleFramebufferAdaptive(D3D11::Texture& source, u32 lef
RestoreGraphicsAPIState();
m_host_display->SetDisplayTexture(m_display_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
g_host_display->SetDisplayTexture(m_display_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
m_display_texture.GetWidth(), m_display_texture.GetHeight(), left, top, width,
height);
}
@@ -1227,7 +1229,7 @@ void GPU_HW_D3D11::DownsampleFramebufferBoxFilter(D3D11::Texture& source, u32 le
RestoreGraphicsAPIState();
m_host_display->SetDisplayTexture(m_downsample_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
g_host_display->SetDisplayTexture(m_downsample_texture.GetD3DSRV(), HostDisplayPixelFormat::RGBA8,
m_downsample_texture.GetWidth(), m_downsample_texture.GetHeight(), ds_left, ds_top,
ds_width, ds_height);
}
+1 -1
View File
@@ -22,7 +22,7 @@ public:
GPURenderer GetRendererType() const override;
bool Initialize(HostDisplay* host_display) override;
bool Initialize() override;
void Reset(bool clear_vram) override;
bool DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display) override;
+16 -18
View File
@@ -11,7 +11,6 @@
#include "common/timer.h"
#include "gpu_hw_shadergen.h"
#include "host_display.h"
#include "host_interface.h"
#include "system.h"
Log_SetChannel(GPU_HW_D3D12);
@@ -19,9 +18,9 @@ GPU_HW_D3D12::GPU_HW_D3D12() = default;
GPU_HW_D3D12::~GPU_HW_D3D12()
{
if (m_host_display)
if (g_host_display)
{
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
ResetGraphicsAPIState();
}
@@ -33,9 +32,9 @@ GPURenderer GPU_HW_D3D12::GetRendererType() const
return GPURenderer::HardwareD3D12;
}
bool GPU_HW_D3D12::Initialize(HostDisplay* host_display)
bool GPU_HW_D3D12::Initialize()
{
if (host_display->GetRenderAPI() != HostDisplay::RenderAPI::D3D12)
if (!Host::AcquireHostDisplay(HostDisplay::RenderAPI::D3D12))
{
Log_ErrorPrintf("Host render API is incompatible");
return false;
@@ -43,7 +42,7 @@ bool GPU_HW_D3D12::Initialize(HostDisplay* host_display)
SetCapabilities();
if (!GPU_HW::Initialize(host_display))
if (!GPU_HW::Initialize())
return false;
if (!CreateRootSignatures())
@@ -144,7 +143,7 @@ void GPU_HW_D3D12::UpdateSettings()
}
// Everything should be finished executing before recreating resources.
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
g_d3d12_context->ExecuteCommandList(true);
if (framebuffer_changed)
@@ -413,10 +412,9 @@ bool GPU_HW_D3D12::CreateTextureBuffer()
bool GPU_HW_D3D12::CompilePipelines()
{
D3D12::ShaderCache shader_cache;
shader_cache.Open(g_host_interface->GetShaderCacheBasePath(), g_d3d12_context->GetFeatureLevel(),
g_settings.gpu_use_debug_device);
shader_cache.Open(EmuFolders::Cache, g_d3d12_context->GetFeatureLevel(), g_settings.gpu_use_debug_device);
GPU_HW_ShaderGen shadergen(m_host_display->GetRenderAPI(), m_resolution_scale, m_multisamples, m_per_sample_shading,
GPU_HW_ShaderGen shadergen(g_host_display->GetRenderAPI(), m_resolution_scale, m_multisamples, m_per_sample_shading,
m_true_color, m_scaled_dithering, m_texture_filtering, m_using_uv_limits,
m_pgxp_depth_buffer, m_supports_dual_source_blend);
@@ -852,7 +850,7 @@ void GPU_HW_D3D12::ClearDisplay()
{
GPU_HW::ClearDisplay();
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
static constexpr float clear_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
m_display_texture.TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET);
@@ -869,23 +867,23 @@ void GPU_HW_D3D12::UpdateDisplay()
if (IsUsingMultisampling())
{
UpdateVRAMReadTexture();
m_host_display->SetDisplayTexture(&m_vram_read_texture, HostDisplayPixelFormat::RGBA8,
g_host_display->SetDisplayTexture(&m_vram_read_texture, HostDisplayPixelFormat::RGBA8,
m_vram_read_texture.GetWidth(), m_vram_read_texture.GetHeight(), 0, 0,
m_vram_read_texture.GetWidth(), m_vram_read_texture.GetHeight());
}
else
{
m_vram_texture.TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
m_host_display->SetDisplayTexture(&m_vram_texture, HostDisplayPixelFormat::RGBA8, m_vram_texture.GetWidth(),
g_host_display->SetDisplayTexture(&m_vram_texture, HostDisplayPixelFormat::RGBA8, m_vram_texture.GetWidth(),
m_vram_texture.GetHeight(), 0, 0, m_vram_texture.GetWidth(),
m_vram_texture.GetHeight());
}
m_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
g_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
static_cast<float>(VRAM_WIDTH) / static_cast<float>(VRAM_HEIGHT));
}
else
{
m_host_display->SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height,
g_host_display->SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height,
m_crtc_state.display_origin_left, m_crtc_state.display_origin_top,
m_crtc_state.display_vram_width, m_crtc_state.display_vram_height,
GetDisplayAspectRatio());
@@ -903,14 +901,14 @@ void GPU_HW_D3D12::UpdateDisplay()
if (IsDisplayDisabled())
{
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
}
else if (!m_GPUSTAT.display_area_color_depth_24 && interlaced == InterlacedRenderMode::None &&
!IsUsingMultisampling() && (scaled_vram_offset_x + scaled_display_width) <= m_vram_texture.GetWidth() &&
(scaled_vram_offset_y + scaled_display_height) <= m_vram_texture.GetHeight())
{
m_vram_texture.TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
m_host_display->SetDisplayTexture(&m_vram_texture, HostDisplayPixelFormat::RGBA8, m_vram_texture.GetWidth(),
g_host_display->SetDisplayTexture(&m_vram_texture, HostDisplayPixelFormat::RGBA8, m_vram_texture.GetWidth(),
m_vram_texture.GetHeight(), scaled_vram_offset_x, scaled_vram_offset_y,
scaled_display_width, scaled_display_height);
}
@@ -938,7 +936,7 @@ void GPU_HW_D3D12::UpdateDisplay()
m_display_texture.TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
m_vram_texture.TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET);
m_host_display->SetDisplayTexture(&m_display_texture, HostDisplayPixelFormat::RGBA8, m_display_texture.GetWidth(),
g_host_display->SetDisplayTexture(&m_display_texture, HostDisplayPixelFormat::RGBA8, m_display_texture.GetWidth(),
m_display_texture.GetHeight(), 0, 0, scaled_display_width,
scaled_display_height);
+1 -1
View File
@@ -20,7 +20,7 @@ public:
GPURenderer GetRendererType() const override;
bool Initialize(HostDisplay* host_display) override;
bool Initialize() override;
void Reset(bool clear_vram) override;
void ResetGraphicsAPIState() override;
+27 -27
View File
@@ -3,6 +3,7 @@
#include "common/log.h"
#include "common/timer.h"
#include "gpu_hw_shadergen.h"
#include "host.h"
#include "host_display.h"
#include "shader_cache_version.h"
#include "system.h"
@@ -24,9 +25,9 @@ GPU_HW_OpenGL::~GPU_HW_OpenGL()
if (m_texture_buffer_r16ui_texture != 0)
glDeleteTextures(1, &m_texture_buffer_r16ui_texture);
if (m_host_display)
if (g_host_display)
{
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
ResetGraphicsAPIState();
}
@@ -40,31 +41,30 @@ GPURenderer GPU_HW_OpenGL::GetRendererType() const
return GPURenderer::HardwareOpenGL;
}
bool GPU_HW_OpenGL::Initialize(HostDisplay* host_display)
bool GPU_HW_OpenGL::Initialize()
{
if (host_display->GetRenderAPI() != HostDisplay::RenderAPI::OpenGL &&
host_display->GetRenderAPI() != HostDisplay::RenderAPI::OpenGLES)
if (!Host::AcquireHostDisplay(HostDisplay::RenderAPI::OpenGL))
{
Log_ErrorPrintf("Host render API type is incompatible");
return false;
}
const bool opengl_is_available =
((host_display->GetRenderAPI() == HostDisplay::RenderAPI::OpenGL &&
((g_host_display->GetRenderAPI() == HostDisplay::RenderAPI::OpenGL &&
(GLAD_GL_VERSION_3_0 || GLAD_GL_ARB_uniform_buffer_object)) ||
(host_display->GetRenderAPI() == HostDisplay::RenderAPI::OpenGLES && GLAD_GL_ES_VERSION_3_0));
(g_host_display->GetRenderAPI() == HostDisplay::RenderAPI::OpenGLES && GLAD_GL_ES_VERSION_3_0));
if (!opengl_is_available)
{
g_host_interface->AddOSDMessage(
g_host_interface->TranslateStdString("OSDMessage", "OpenGL renderer unavailable, your driver or hardware is not "
"recent enough. OpenGL 3.1 or OpenGL ES 3.0 is required."),
20.0f);
Host::AddOSDMessage(Host::TranslateStdString("OSDMessage",
"OpenGL renderer unavailable, your driver or hardware is not "
"recent enough. OpenGL 3.1 or OpenGL ES 3.0 is required."),
20.0f);
return false;
}
SetCapabilities(host_display);
SetCapabilities();
if (!GPU_HW::Initialize(host_display))
if (!GPU_HW::Initialize())
return false;
if (!CreateFramebuffer())
@@ -133,7 +133,7 @@ bool GPU_HW_OpenGL::DoState(StateWrapper& sw, HostDisplayTexture** host_texture,
{
delete tex;
tex = m_host_display
tex = g_host_display
->CreateTexture(m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), 1, 1,
m_vram_texture.GetSamples(), HostDisplayPixelFormat::RGBA8, nullptr, 0, false)
.release();
@@ -252,7 +252,7 @@ void GPU_HW_OpenGL::UpdateSettings()
RestoreGraphicsAPIState();
ReadVRAM(0, 0, VRAM_WIDTH, VRAM_HEIGHT);
ResetGraphicsAPIState();
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
CreateFramebuffer();
}
if (shaders_changed)
@@ -297,7 +297,7 @@ std::tuple<s32, s32> GPU_HW_OpenGL::ConvertToFramebufferCoordinates(s32 x, s32 y
return std::make_tuple(x, static_cast<s32>(static_cast<s32>(VRAM_HEIGHT) - y));
}
void GPU_HW_OpenGL::SetCapabilities(HostDisplay* host_display)
void GPU_HW_OpenGL::SetCapabilities()
{
GLint max_texture_size = VRAM_WIDTH;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
@@ -510,10 +510,10 @@ bool GPU_HW_OpenGL::CreateTextureBuffer()
bool GPU_HW_OpenGL::CompilePrograms()
{
GL::ShaderCache shader_cache;
shader_cache.Open(IsGLES(), g_host_interface->GetShaderCacheBasePath(), SHADER_CACHE_VERSION);
shader_cache.Open(IsGLES(), EmuFolders::Cache, SHADER_CACHE_VERSION);
const bool use_binding_layout = GPU_HW_ShaderGen::UseGLSLBindingLayout();
GPU_HW_ShaderGen shadergen(m_host_display->GetRenderAPI(), m_resolution_scale, m_multisamples, m_per_sample_shading,
GPU_HW_ShaderGen shadergen(g_host_display->GetRenderAPI(), m_resolution_scale, m_multisamples, m_per_sample_shading,
m_true_color, m_scaled_dithering, m_texture_filtering, m_using_uv_limits,
m_pgxp_depth_buffer, m_supports_dual_source_blend);
@@ -844,7 +844,7 @@ void GPU_HW_OpenGL::ClearDisplay()
{
GPU_HW::ClearDisplay();
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
m_display_texture.BindFramebuffer(GL_DRAW_FRAMEBUFFER);
glDisable(GL_SCISSOR_TEST);
@@ -864,7 +864,7 @@ void GPU_HW_OpenGL::UpdateDisplay()
{
UpdateVRAMReadTexture();
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_read_texture.GetGLId())),
g_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_read_texture.GetGLId())),
HostDisplayPixelFormat::RGBA8, m_vram_read_texture.GetWidth(),
static_cast<s32>(m_vram_read_texture.GetHeight()), 0,
m_vram_read_texture.GetHeight(), m_vram_read_texture.GetWidth(),
@@ -872,17 +872,17 @@ void GPU_HW_OpenGL::UpdateDisplay()
}
else
{
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture.GetGLId())),
g_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture.GetGLId())),
HostDisplayPixelFormat::RGBA8, m_vram_texture.GetWidth(),
static_cast<s32>(m_vram_texture.GetHeight()), 0, m_vram_texture.GetHeight(),
m_vram_texture.GetWidth(), -static_cast<s32>(m_vram_texture.GetHeight()));
}
m_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
g_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
static_cast<float>(VRAM_WIDTH) / static_cast<float>(VRAM_HEIGHT));
}
else
{
m_host_display->SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height,
g_host_display->SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height,
m_crtc_state.display_origin_left, m_crtc_state.display_origin_top,
m_crtc_state.display_vram_width, m_crtc_state.display_vram_height,
GetDisplayAspectRatio());
@@ -900,7 +900,7 @@ void GPU_HW_OpenGL::UpdateDisplay()
if (IsDisplayDisabled())
{
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
}
else if (!m_GPUSTAT.display_area_color_depth_24 && interlaced == GPU_HW::InterlacedRenderMode::None &&
!IsUsingMultisampling() && (scaled_vram_offset_x + scaled_display_width) <= m_vram_texture.GetWidth() &&
@@ -913,7 +913,7 @@ void GPU_HW_OpenGL::UpdateDisplay()
}
else
{
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture.GetGLId())),
g_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_vram_texture.GetGLId())),
HostDisplayPixelFormat::RGBA8, m_vram_texture.GetWidth(),
m_vram_texture.GetHeight(), scaled_vram_offset_x,
m_vram_texture.GetHeight() - scaled_vram_offset_y, scaled_display_width,
@@ -960,7 +960,7 @@ void GPU_HW_OpenGL::UpdateDisplay()
}
else
{
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_display_texture.GetGLId())),
g_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_display_texture.GetGLId())),
HostDisplayPixelFormat::RGBA8, m_display_texture.GetWidth(),
m_display_texture.GetHeight(), 0, scaled_display_height, scaled_display_width,
-static_cast<s32>(scaled_display_height));
@@ -1353,7 +1353,7 @@ void GPU_HW_OpenGL::DownsampleFramebufferBoxFilter(GL::Texture& source, u32 left
RestoreGraphicsAPIState();
m_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_downsample_texture.GetGLId())),
g_host_display->SetDisplayTexture(reinterpret_cast<void*>(static_cast<uintptr_t>(m_downsample_texture.GetGLId())),
HostDisplayPixelFormat::RGBA8, m_downsample_texture.GetWidth(),
m_downsample_texture.GetHeight(), ds_left,
m_downsample_texture.GetHeight() - ds_top, ds_width, -static_cast<s32>(ds_height));
+2 -2
View File
@@ -18,7 +18,7 @@ public:
GPURenderer GetRendererType() const override;
bool Initialize(HostDisplay* host_display) override;
bool Initialize() override;
void Reset(bool clear_vram) override;
bool DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display) override;
@@ -57,7 +57,7 @@ private:
std::tuple<s32, s32> ConvertToFramebufferCoordinates(s32 x, s32 y);
void SetCapabilities(HostDisplay* host_display);
void SetCapabilities();
bool CreateFramebuffer();
void ClearFramebuffer();
void CopyFramebufferForState(GLenum target, GLuint src_texture, u32 src_fbo, u32 src_x, u32 src_y, GLuint dst_texture,
+19 -20
View File
@@ -9,7 +9,6 @@
#include "common/vulkan/util.h"
#include "gpu_hw_shadergen.h"
#include "host_display.h"
#include "host_interface.h"
#include "system.h"
#include "util/state_wrapper.h"
Log_SetChannel(GPU_HW_Vulkan);
@@ -18,9 +17,9 @@ GPU_HW_Vulkan::GPU_HW_Vulkan() = default;
GPU_HW_Vulkan::~GPU_HW_Vulkan()
{
if (m_host_display)
if (g_host_display)
{
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
ResetGraphicsAPIState();
}
@@ -32,9 +31,9 @@ GPURenderer GPU_HW_Vulkan::GetRendererType() const
return GPURenderer::HardwareVulkan;
}
bool GPU_HW_Vulkan::Initialize(HostDisplay* host_display)
bool GPU_HW_Vulkan::Initialize()
{
if (host_display->GetRenderAPI() != HostDisplay::RenderAPI::Vulkan)
if (!Host::AcquireHostDisplay(HostDisplay::RenderAPI::Vulkan))
{
Log_ErrorPrintf("Host render API is incompatible");
return false;
@@ -43,7 +42,7 @@ bool GPU_HW_Vulkan::Initialize(HostDisplay* host_display)
Assert(g_vulkan_shader_cache);
SetCapabilities();
if (!GPU_HW::Initialize(host_display))
if (!GPU_HW::Initialize())
return false;
if (!CreatePipelineLayouts())
@@ -144,7 +143,7 @@ bool GPU_HW_Vulkan::DoState(StateWrapper& sw, HostDisplayTexture** host_texture,
{
delete htex;
htex = m_host_display
htex = g_host_display
->CreateTexture(m_vram_texture.GetWidth(), m_vram_texture.GetHeight(), 1, 1,
m_vram_texture.GetSamples(), HostDisplayPixelFormat::RGBA8, nullptr, 0, false)
.release();
@@ -179,7 +178,7 @@ void GPU_HW_Vulkan::ResetGraphicsAPIState()
EndRenderPass();
if (m_host_display->GetDisplayTextureHandle() == &m_vram_texture)
if (g_host_display->GetDisplayTextureHandle() == &m_vram_texture)
{
m_vram_texture.TransitionToLayout(g_vulkan_context->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
@@ -218,7 +217,7 @@ void GPU_HW_Vulkan::UpdateSettings()
}
// Everything should be finished executing before recreating resources.
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
g_vulkan_context->ExecuteCommandBuffer(true);
if (framebuffer_changed)
@@ -923,7 +922,7 @@ bool GPU_HW_Vulkan::CompilePipelines()
VkDevice device = g_vulkan_context->GetDevice();
VkPipelineCache pipeline_cache = g_vulkan_shader_cache->GetPipelineCache();
GPU_HW_ShaderGen shadergen(m_host_display->GetRenderAPI(), m_resolution_scale, m_multisamples, m_per_sample_shading,
GPU_HW_ShaderGen shadergen(g_host_display->GetRenderAPI(), m_resolution_scale, m_multisamples, m_per_sample_shading,
m_true_color, m_scaled_dithering, m_texture_filtering, m_using_uv_limits,
m_pgxp_depth_buffer, m_supports_dual_source_blend);
@@ -1405,7 +1404,7 @@ void GPU_HW_Vulkan::ClearDisplay()
GPU_HW::ClearDisplay();
EndRenderPass();
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
VkCommandBuffer cmdbuf = g_vulkan_context->GetCurrentCommandBuffer();
const Vulkan::Util::DebugScope debugScope(cmdbuf, "GPU_HW_Vulkan::ClearDisplay");
@@ -1435,22 +1434,22 @@ void GPU_HW_Vulkan::UpdateDisplay()
UpdateVRAMReadTexture();
}
m_host_display->SetDisplayTexture(&m_vram_read_texture, HostDisplayPixelFormat::RGBA8,
g_host_display->SetDisplayTexture(&m_vram_read_texture, HostDisplayPixelFormat::RGBA8,
m_vram_read_texture.GetWidth(), m_vram_read_texture.GetHeight(), 0, 0,
m_vram_read_texture.GetWidth(), m_vram_read_texture.GetHeight());
}
else
{
m_host_display->SetDisplayTexture(&m_vram_texture, HostDisplayPixelFormat::RGBA8, m_vram_texture.GetWidth(),
g_host_display->SetDisplayTexture(&m_vram_texture, HostDisplayPixelFormat::RGBA8, m_vram_texture.GetWidth(),
m_vram_texture.GetHeight(), 0, 0, m_vram_texture.GetWidth(),
m_vram_texture.GetHeight());
}
m_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
g_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
static_cast<float>(VRAM_WIDTH) / static_cast<float>(VRAM_HEIGHT));
}
else
{
m_host_display->SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height,
g_host_display->SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height,
m_crtc_state.display_origin_left, m_crtc_state.display_origin_top,
m_crtc_state.display_vram_width, m_crtc_state.display_vram_height,
GetDisplayAspectRatio());
@@ -1468,7 +1467,7 @@ void GPU_HW_Vulkan::UpdateDisplay()
if (IsDisplayDisabled())
{
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
}
else if (!m_GPUSTAT.display_area_color_depth_24 && interlaced == InterlacedRenderMode::None &&
!IsUsingMultisampling() && (scaled_vram_offset_x + scaled_display_width) <= m_vram_texture.GetWidth() &&
@@ -1481,7 +1480,7 @@ void GPU_HW_Vulkan::UpdateDisplay()
}
else
{
m_host_display->SetDisplayTexture(&m_vram_texture, HostDisplayPixelFormat::RGBA8, m_vram_texture.GetWidth(),
g_host_display->SetDisplayTexture(&m_vram_texture, HostDisplayPixelFormat::RGBA8, m_vram_texture.GetWidth(),
m_vram_texture.GetHeight(), scaled_vram_offset_x, scaled_vram_offset_y,
scaled_display_width, scaled_display_height);
}
@@ -1528,7 +1527,7 @@ void GPU_HW_Vulkan::UpdateDisplay()
}
else
{
m_host_display->SetDisplayTexture(&m_display_texture, HostDisplayPixelFormat::RGBA8,
g_host_display->SetDisplayTexture(&m_display_texture, HostDisplayPixelFormat::RGBA8,
m_display_texture.GetWidth(), m_display_texture.GetHeight(), 0, 0,
scaled_display_width, scaled_display_height);
RestoreGraphicsAPIState();
@@ -1938,7 +1937,7 @@ void GPU_HW_Vulkan::DownsampleFramebufferBoxFilter(Vulkan::Texture& source, u32
RestoreGraphicsAPIState();
m_host_display->SetDisplayTexture(&m_downsample_texture, HostDisplayPixelFormat::RGBA8,
g_host_display->SetDisplayTexture(&m_downsample_texture, HostDisplayPixelFormat::RGBA8,
m_downsample_texture.GetWidth(), m_downsample_texture.GetHeight(), ds_left, ds_top,
ds_width, ds_height);
}
@@ -2037,7 +2036,7 @@ void GPU_HW_Vulkan::DownsampleFramebufferAdaptive(Vulkan::Texture& source, u32 l
}
RestoreGraphicsAPIState();
m_host_display->SetDisplayTexture(&m_display_texture, HostDisplayPixelFormat::RGBA8, m_display_texture.GetWidth(),
g_host_display->SetDisplayTexture(&m_display_texture, HostDisplayPixelFormat::RGBA8, m_display_texture.GetWidth(),
m_display_texture.GetHeight(), left, top, width, height);
}
+1 -1
View File
@@ -17,7 +17,7 @@ public:
GPURenderer GetRendererType() const override;
bool Initialize(HostDisplay* host_display) override;
bool Initialize() override;
void Reset(bool clear_vram) override;
bool DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display) override;
+19 -15
View File
@@ -36,8 +36,8 @@ GPU_SW::GPU_SW()
GPU_SW::~GPU_SW()
{
m_backend.Shutdown();
if (m_host_display)
m_host_display->ClearDisplayTexture();
if (g_host_display)
g_host_display->ClearDisplayTexture();
}
GPURenderer GPU_SW::GetRendererType() const
@@ -45,9 +45,13 @@ GPURenderer GPU_SW::GetRendererType() const
return GPURenderer::Software;
}
bool GPU_SW::Initialize(HostDisplay* host_display)
bool GPU_SW::Initialize()
{
if (!GPU::Initialize(host_display) || !m_backend.Initialize(false))
// we need something to draw in.. but keep the current api if we have one
if (!g_host_display && !Host::AcquireHostDisplay(HostDisplay::GetPreferredAPI()))
return false;
if (!GPU::Initialize() || !m_backend.Initialize(false))
return false;
static constexpr auto formats_for_16bit = make_array(HostDisplayPixelFormat::RGB565, HostDisplayPixelFormat::RGBA5551,
@@ -57,7 +61,7 @@ bool GPU_SW::Initialize(HostDisplay* host_display)
HostDisplayPixelFormat::RGBA5551);
for (const HostDisplayPixelFormat format : formats_for_16bit)
{
if (m_host_display->SupportsDisplayPixelFormat(format))
if (g_host_display->SupportsDisplayPixelFormat(format))
{
m_16bit_display_format = format;
break;
@@ -65,7 +69,7 @@ bool GPU_SW::Initialize(HostDisplay* host_display)
}
for (const HostDisplayPixelFormat format : formats_for_24bit)
{
if (m_host_display->SupportsDisplayPixelFormat(format))
if (g_host_display->SupportsDisplayPixelFormat(format))
{
m_24bit_display_format = format;
break;
@@ -233,7 +237,7 @@ void GPU_SW::CopyOut15Bit(u32 src_x, u32 src_y, u32 width, u32 height, u32 field
if (!interlaced)
{
if (!m_host_display->BeginSetDisplayPixels(display_format, width, height, reinterpret_cast<void**>(&dst_ptr),
if (!g_host_display->BeginSetDisplayPixels(display_format, width, height, reinterpret_cast<void**>(&dst_ptr),
&dst_stride))
{
return;
@@ -285,11 +289,11 @@ void GPU_SW::CopyOut15Bit(u32 src_x, u32 src_y, u32 width, u32 height, u32 field
if (!interlaced)
{
m_host_display->EndSetDisplayPixels();
g_host_display->EndSetDisplayPixels();
}
else
{
m_host_display->SetDisplayPixels(display_format, width, height, m_display_texture_buffer.data(), output_stride);
g_host_display->SetDisplayPixels(display_format, width, height, m_display_texture_buffer.data(), output_stride);
}
}
@@ -327,7 +331,7 @@ void GPU_SW::CopyOut24Bit(u32 src_x, u32 src_y, u32 skip_x, u32 width, u32 heigh
if (!interlaced)
{
if (!m_host_display->BeginSetDisplayPixels(display_format, width, height, reinterpret_cast<void**>(&dst_ptr),
if (!g_host_display->BeginSetDisplayPixels(display_format, width, height, reinterpret_cast<void**>(&dst_ptr),
&dst_stride))
{
return;
@@ -443,11 +447,11 @@ void GPU_SW::CopyOut24Bit(u32 src_x, u32 src_y, u32 skip_x, u32 width, u32 heigh
if (!interlaced)
{
m_host_display->EndSetDisplayPixels();
g_host_display->EndSetDisplayPixels();
}
else
{
m_host_display->SetDisplayPixels(display_format, width, height, m_display_texture_buffer.data(), output_stride);
g_host_display->SetDisplayPixels(display_format, width, height, m_display_texture_buffer.data(), output_stride);
}
}
@@ -486,14 +490,14 @@ void GPU_SW::UpdateDisplay()
if (!g_settings.debugging.show_vram)
{
m_host_display->SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height,
g_host_display->SetDisplayParameters(m_crtc_state.display_width, m_crtc_state.display_height,
m_crtc_state.display_origin_left, m_crtc_state.display_origin_top,
m_crtc_state.display_vram_width, m_crtc_state.display_vram_height,
GetDisplayAspectRatio());
if (IsDisplayDisabled())
{
m_host_display->ClearDisplayTexture();
g_host_display->ClearDisplayTexture();
return;
}
@@ -534,7 +538,7 @@ void GPU_SW::UpdateDisplay()
else
{
CopyOut15Bit(m_16bit_display_format, 0, 0, VRAM_WIDTH, VRAM_HEIGHT, 0, false, false);
m_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
g_host_display->SetDisplayParameters(VRAM_WIDTH, VRAM_HEIGHT, 0, 0, VRAM_WIDTH, VRAM_HEIGHT,
static_cast<float>(VRAM_WIDTH) / static_cast<float>(VRAM_HEIGHT));
}
}
+8 -1
View File
@@ -7,6 +7,11 @@
#include <memory>
#include <vector>
namespace Threading
{
class Thread;
}
class HostDisplayTexture;
class GPU_SW final : public GPU
@@ -15,9 +20,11 @@ public:
GPU_SW();
~GPU_SW() override;
ALWAYS_INLINE const GPU_SW_Backend& GetBackend() const { return m_backend; }
GPURenderer GetRendererType() const override;
bool Initialize(HostDisplay* host_display) override;
bool Initialize() override;
bool DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display) override;
void Reset(bool clear_vram) override;
void UpdateSettings() override;
+3 -5
View File
@@ -5,7 +5,6 @@
#include "cpu_core.h"
#include "cpu_core_private.h"
#include "host_display.h"
#include "host_interface.h"
#include "pgxp.h"
#include "settings.h"
#include "timing_event.h"
@@ -188,15 +187,14 @@ void UpdateAspectRatio()
{
case DisplayAspectRatio::MatchWindow:
{
const HostDisplay* display = g_host_interface->GetDisplay();
if (!display)
if (!g_host_display)
{
s_aspect_ratio = DisplayAspectRatio::R4_3;
return;
}
num = display->GetWindowWidth();
denom = display->GetWindowHeight();
num = g_host_display->GetWindowWidth();
denom = g_host_display->GetWindowHeight();
}
break;
+273
View File
@@ -0,0 +1,273 @@
#include "guncon.h"
#include "common/assert.h"
#include "common/log.h"
#include "gpu.h"
#include "host.h"
#include "host_display.h"
#include "resources.h"
#include "system.h"
#include "util/state_wrapper.h"
#include <array>
Log_SetChannel(GunCon);
static constexpr std::array<u8, static_cast<size_t>(GunCon::Button::Count)> s_button_indices = {{13, 3, 14}};
GunCon::GunCon(u32 index) : Controller(index) {}
GunCon::~GunCon() = default;
ControllerType GunCon::GetType() const
{
return ControllerType::GunCon;
}
void GunCon::Reset()
{
m_transfer_state = TransferState::Idle;
}
bool GunCon::DoState(StateWrapper& sw, bool apply_input_state)
{
if (!Controller::DoState(sw, apply_input_state))
return false;
u16 button_state = m_button_state;
u16 position_x = m_position_x;
u16 position_y = m_position_y;
sw.Do(&button_state);
sw.Do(&position_x);
sw.Do(&position_y);
if (apply_input_state)
{
m_button_state = button_state;
m_position_x = position_x;
m_position_y = position_y;
}
sw.Do(&m_transfer_state);
return true;
}
float GunCon::GetBindState(u32 index) const
{
if (index >= s_button_indices.size())
return 0.0f;
const u32 bit = s_button_indices[index];
return static_cast<float>(((m_button_state >> bit) & 1u) ^ 1u);
}
void GunCon::SetBindState(u32 index, float value)
{
const bool pressed = (value >= 0.5f);
if (index == static_cast<u32>(Button::ShootOffscreen))
{
if (m_shoot_offscreen != pressed)
{
m_shoot_offscreen = pressed;
SetBindState(static_cast<u32>(Button::Trigger), pressed);
}
return;
}
if (pressed)
m_button_state &= ~(u16(1) << s_button_indices[static_cast<u8>(index)]);
else
m_button_state |= u16(1) << s_button_indices[static_cast<u8>(index)];
}
void GunCon::ResetTransferState()
{
m_transfer_state = TransferState::Idle;
}
bool GunCon::Transfer(const u8 data_in, u8* data_out)
{
static constexpr u16 ID = 0x5A63;
switch (m_transfer_state)
{
case TransferState::Idle:
{
*data_out = 0xFF;
if (data_in == 0x01)
{
m_transfer_state = TransferState::Ready;
return true;
}
return false;
}
case TransferState::Ready:
{
if (data_in == 0x42)
{
*data_out = Truncate8(ID);
m_transfer_state = TransferState::IDMSB;
return true;
}
*data_out = 0xFF;
return false;
}
case TransferState::IDMSB:
{
*data_out = Truncate8(ID >> 8);
m_transfer_state = TransferState::ButtonsLSB;
return true;
}
case TransferState::ButtonsLSB:
{
*data_out = Truncate8(m_button_state);
m_transfer_state = TransferState::ButtonsMSB;
return true;
}
case TransferState::ButtonsMSB:
{
*data_out = Truncate8(m_button_state >> 8);
m_transfer_state = TransferState::XLSB;
return true;
}
case TransferState::XLSB:
{
UpdatePosition();
*data_out = Truncate8(m_position_x);
m_transfer_state = TransferState::XMSB;
return true;
}
case TransferState::XMSB:
{
*data_out = Truncate8(m_position_x >> 8);
m_transfer_state = TransferState::YLSB;
return true;
}
case TransferState::YLSB:
{
*data_out = Truncate8(m_position_y);
m_transfer_state = TransferState::YMSB;
return true;
}
case TransferState::YMSB:
{
*data_out = Truncate8(m_position_y >> 8);
m_transfer_state = TransferState::Idle;
return false;
}
default:
{
UnreachableCode();
return false;
}
}
}
void GunCon::UpdatePosition()
{
// get screen coordinates
const s32 mouse_x = g_host_display->GetMousePositionX();
const s32 mouse_y = g_host_display->GetMousePositionY();
// are we within the active display area?
u32 tick, line;
if (mouse_x < 0 || mouse_y < 0 ||
!g_gpu->ConvertScreenCoordinatesToBeamTicksAndLines(mouse_x, mouse_y, m_x_scale, &tick, &line) ||
m_shoot_offscreen)
{
Log_DebugPrintf("Lightgun out of range for window coordinates %d,%d", mouse_x, mouse_y);
m_position_x = 0x01;
m_position_y = 0x0A;
return;
}
// 8MHz units for X = 44100*768*11/7 = 53222400 / 8000000 = 6.6528
const double divider = static_cast<double>(g_gpu->GetCRTCFrequency()) / 8000000.0;
m_position_x = static_cast<u16>(static_cast<float>(tick) / static_cast<float>(divider));
m_position_y = static_cast<u16>(line);
Log_DebugPrintf("Lightgun window coordinates %d,%d -> tick %u line %u 8mhz ticks %u", mouse_x, mouse_y, tick, line,
m_position_x);
}
std::unique_ptr<GunCon> GunCon::Create(u32 index)
{
return std::make_unique<GunCon>(index);
}
static const Controller::ControllerBindingInfo s_binding_info[] = {
#define BUTTON(name, display_name, button, genb) \
{ \
name, display_name, static_cast<u32>(button), Controller::ControllerBindingType::Button, genb \
}
BUTTON("Trigger", "Trigger", GunCon::Button::Trigger, GenericInputBinding::R2),
BUTTON("ShootOffscreen", "ShootOffscreen", GunCon::Button::ShootOffscreen, GenericInputBinding::L2),
BUTTON("A", "A", GunCon::Button::A, GenericInputBinding::Cross),
BUTTON("B", "B", GunCon::Button::B, GenericInputBinding::Circle),
#undef BUTTON
};
static const SettingInfo s_settings[] = {
{SettingInfo::Type::Path, "CrosshairImagePath", TRANSLATABLE("NamcoGunCon", "Crosshair Image Path"),
TRANSLATABLE("NamcoGunCon", "Path to an image to use as a crosshair/cursor.")},
{SettingInfo::Type::Float, "CrosshairScale", TRANSLATABLE("NamcoGunCon", "Crosshair Image Scale"),
TRANSLATABLE("NamcoGunCon", "Scale of crosshair image on screen."), "1.0", "0.0001", "100.0", "0.10"},
{SettingInfo::Type::Float, "XScale", TRANSLATABLE("NamcoGunCon", "X Scale"),
TRANSLATABLE("NamcoGunCon", "Scales X coordinates relative to the center of the screen."), "1.0", "0.01", "2.0",
"0.01"}};
const Controller::ControllerInfo GunCon::INFO = {ControllerType::GunCon,
"GunCon",
TRANSLATABLE("ControllerType", "GunCon"),
s_binding_info,
countof(s_binding_info),
s_settings,
countof(s_settings),
Controller::VibrationCapabilities::NoVibration};
void GunCon::LoadSettings(SettingsInterface& si, const char* section)
{
Controller::LoadSettings(si, section);
std::string path = si.GetStringValue(section, "CrosshairImagePath");
if (path != m_crosshair_image_path)
{
m_crosshair_image_path = std::move(path);
if (m_crosshair_image_path.empty() || !m_crosshair_image.LoadFromFile(m_crosshair_image_path.c_str()))
{
m_crosshair_image.Invalidate();
}
}
#ifndef __ANDROID__
if (!m_crosshair_image.IsValid())
{
m_crosshair_image.SetPixels(Resources::CROSSHAIR_IMAGE_WIDTH, Resources::CROSSHAIR_IMAGE_HEIGHT,
Resources::CROSSHAIR_IMAGE_DATA.data());
}
#endif
m_crosshair_image_scale = si.GetFloatValue(section, "CrosshairScale", 1.0f);
m_x_scale = si.GetFloatValue(section, "XScale", 1.0f);
}
bool GunCon::GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode)
{
if (!m_crosshair_image.IsValid())
return false;
*image = &m_crosshair_image;
*image_scale = m_crosshair_image_scale;
*relative_mode = false;
return true;
}
+10 -17
View File
@@ -4,7 +4,7 @@
#include <optional>
#include <string_view>
class NamcoGunCon final : public Controller
class GunCon final : public Controller
{
public:
enum class Button : u8
@@ -16,34 +16,27 @@ public:
Count
};
NamcoGunCon();
~NamcoGunCon() override;
static const Controller::ControllerInfo INFO;
static std::unique_ptr<NamcoGunCon> Create();
static std::optional<s32> StaticGetAxisCodeByName(std::string_view button_name);
static std::optional<s32> StaticGetButtonCodeByName(std::string_view button_name);
static AxisList StaticGetAxisNames();
static ButtonList StaticGetButtonNames();
static u32 StaticGetVibrationMotorCount();
static SettingList StaticGetSettings();
GunCon(u32 index);
~GunCon() override;
static std::unique_ptr<GunCon> Create(u32 index);
ControllerType GetType() const override;
std::optional<s32> GetAxisCodeByName(std::string_view axis_name) const override;
std::optional<s32> GetButtonCodeByName(std::string_view button_name) const override;
void Reset() override;
bool DoState(StateWrapper& sw, bool apply_input_state) override;
void LoadSettings(const char* section) override;
void LoadSettings(SettingsInterface& si, const char* section) override;
bool GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode) override;
bool GetButtonState(s32 button_code) const override;
void SetButtonState(s32 button_code, bool pressed) override;
float GetBindState(u32 index) const override;
void SetBindState(u32 index, float value) override;
void ResetTransferState() override;
bool Transfer(const u8 data_in, u8* data_out) override;
void SetButtonState(Button button, bool pressed);
private:
void UpdatePosition();
+32
View File
@@ -0,0 +1,32 @@
#include "host.h"
#include "common/string_util.h"
#include <cstdarg>
void Host::ReportFormattedErrorAsync(const std::string_view& title, const char* format, ...)
{
std::va_list ap;
va_start(ap, format);
std::string message(StringUtil::StdStringFromFormatV(format, ap));
va_end(ap);
ReportErrorAsync(title, message);
}
bool Host::ConfirmFormattedMessage(const std::string_view& title, const char* format, ...)
{
std::va_list ap;
va_start(ap, format);
std::string message = StringUtil::StdStringFromFormatV(format, ap);
va_end(ap);
return ConfirmMessage(title, message);
}
void Host::ReportFormattedDebuggerMessage(const char* format, ...)
{
std::va_list ap;
va_start(ap, format);
std::string message = StringUtil::StdStringFromFormatV(format, ap);
va_end(ap);
ReportDebuggerMessage(message);
}
+89
View File
@@ -1,12 +1,67 @@
#pragma once
#include "common/string.h"
#include "common/types.h"
#include <ctime>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
struct WindowInfo;
enum class AudioBackend : u8;
class AudioStream;
class CDImage;
/// Marks a core string as being translatable.
#define TRANSLATABLE(context, str) str
/// Generic input bindings. These roughly match a DualShock 4 or XBox One controller.
/// They are used for automatic binding to PS2 controller types, and for big picture mode navigation.
enum class GenericInputBinding : u8
{
Unknown,
DPadUp,
DPadRight,
DPadLeft,
DPadDown,
LeftStickUp,
LeftStickRight,
LeftStickDown,
LeftStickLeft,
L3,
RightStickUp,
RightStickRight,
RightStickDown,
RightStickLeft,
R3,
Triangle, // Y on XBox pads.
Circle, // B on XBox pads.
Cross, // A on XBox pads.
Square, // X on XBox pads.
Select, // Share on DS4, View on XBox pads.
Start, // Options on DS4, Menu on XBox pads.
System, // PS button on DS4, Guide button on XBox pads.
L1, // LB on Xbox pads.
L2, // Left trigger on XBox pads.
R1, // RB on XBox pads.
R2, // Right trigger on Xbox pads.
SmallMotor, // High frequency vibration.
LargeMotor, // Low frequency vibration.
Count,
};
namespace Host {
/// Reads a file from the resources directory of the application.
/// This may be outside of the "normal" filesystem on platforms such as Mac.
@@ -15,6 +70,18 @@ std::optional<std::vector<u8>> ReadResourceFile(const char* filename);
/// Reads a resource file file from the resources directory as a string.
std::optional<std::string> ReadResourceFileToString(const char* filename);
/// Returns the modified time of a resource.
std::optional<std::time_t> GetResourceFileTimestamp(const char* filename);
/// Translates a string to the current language.
TinyString TranslateString(const char* context, const char* str, const char* disambiguation = nullptr, int n = -1);
std::string TranslateStdString(const char* context, const char* str, const char* disambiguation = nullptr, int n = -1);
std::unique_ptr<AudioStream> CreateAudioStream(AudioBackend backend);
/// Returns the scale of OSD elements.
float GetOSDScale();
/// Adds OSD messages, duration is in seconds.
void AddOSDMessage(std::string message, float duration = 2.0f);
void AddKeyedOSDMessage(std::string key, std::string message, float duration = 2.0f);
@@ -26,4 +93,26 @@ void ClearOSDMessages();
/// Displays an asynchronous error on the UI thread, i.e. doesn't block the caller.
void ReportErrorAsync(const std::string_view& title, const std::string_view& message);
void ReportFormattedErrorAsync(const std::string_view& title, const char* format, ...);
/// Displays a synchronous confirmation on the UI thread, i.e. blocks the caller.
bool ConfirmMessage(const std::string_view& title, const std::string_view& message);
bool ConfirmFormattedMessage(const std::string_view& title, const char* format, ...);
/// Debugger feedback.
void ReportDebuggerMessage(const std::string_view& message);
void ReportFormattedDebuggerMessage(const char* format, ...);
/// Displays a loading screen with the logo, rendered with ImGui. Use when executing possibly-time-consuming tasks
/// such as compiling shaders when starting up.
void DisplayLoadingScreen(const char* message, int progress_min = -1, int progress_max = -1, int progress_value = -1);
/// Internal method used by pads to dispatch vibration updates to input sources.
/// Intensity is normalized from 0 to 1.
void SetPadVibrationIntensity(u32 pad_index, float large_or_single_motor_intensity, float small_motor_intensity);
/// Enables "relative" mouse mode, locking the cursor position and returning relative coordinates.
void SetMouseMode(bool relative, bool hide_cursor);
/// Safely executes a function on the VM thread.
void RunOnCPUThread(std::function<void()> function, bool block = false);
} // namespace Host
+62 -1
View File
@@ -5,7 +5,6 @@
#include "common/log.h"
#include "common/string_util.h"
#include "common/timer.h"
#include "host_interface.h"
#include "stb_image.h"
#include "stb_image_resize.h"
#include "stb_image_write.h"
@@ -16,10 +15,72 @@
#include <vector>
Log_SetChannel(HostDisplay);
std::unique_ptr<HostDisplay> g_host_display;
HostDisplayTexture::~HostDisplayTexture() = default;
HostDisplay::~HostDisplay() = default;
HostDisplay::RenderAPI HostDisplay::GetPreferredAPI()
{
#ifdef _WIN32
return RenderAPI::D3D11;
#else
return RenderAPI::OpenGL;
#endif
}
bool HostDisplay::ParseFullscreenMode(const std::string_view& mode, u32* width, u32* height, float* refresh_rate)
{
if (!mode.empty())
{
std::string_view::size_type sep1 = mode.find('x');
if (sep1 != std::string_view::npos)
{
std::optional<u32> owidth = StringUtil::FromChars<u32>(mode.substr(0, sep1));
sep1++;
while (sep1 < mode.length() && std::isspace(mode[sep1]))
sep1++;
if (owidth.has_value() && sep1 < mode.length())
{
std::string_view::size_type sep2 = mode.find('@', sep1);
if (sep2 != std::string_view::npos)
{
std::optional<u32> oheight = StringUtil::FromChars<u32>(mode.substr(sep1, sep2 - sep1));
sep2++;
while (sep2 < mode.length() && std::isspace(mode[sep2]))
sep2++;
if (oheight.has_value() && sep2 < mode.length())
{
std::optional<float> orefresh_rate = StringUtil::FromChars<float>(mode.substr(sep2));
if (orefresh_rate.has_value())
{
*width = owidth.value();
*height = oheight.value();
*refresh_rate = orefresh_rate.value();
return true;
}
}
}
}
}
}
*width = 0;
*height = 0;
*refresh_rate = 0;
return false;
}
std::string HostDisplay::GetFullscreenModeString(u32 width, u32 height, float refresh_rate)
{
return StringUtil::StdStringFromFormat("%u x %u @ %f hz", width, height, refresh_rate);
}
bool HostDisplay::UsesLowerLeftOrigin() const
{
const RenderAPI api = GetRenderAPI();
+31
View File
@@ -62,6 +62,15 @@ public:
virtual ~HostDisplay();
/// Returns the default/preferred API for the system.
static RenderAPI GetPreferredAPI();
/// Parses a fullscreen mode into its components (width * height @ refresh hz)
static bool ParseFullscreenMode(const std::string_view& mode, u32* width, u32* height, float* refresh_rate);
/// Converts a fullscreen mode to a string.
static std::string GetFullscreenModeString(u32 width, u32 height, float refresh_rate);
ALWAYS_INLINE const WindowInfo& GetWindowInfo() const { return m_window_info; }
ALWAYS_INLINE s32 GetWindowWidth() const { return static_cast<s32>(m_window_info.surface_width); }
ALWAYS_INLINE s32 GetWindowHeight() const { return static_cast<s32>(m_window_info.surface_height); }
@@ -290,3 +299,25 @@ protected:
bool m_display_integer_scaling = false;
bool m_display_stretch = false;
};
/// Returns a pointer to the current host display abstraction. Assumes AcquireHostDisplay() has been caled.
extern std::unique_ptr<HostDisplay> g_host_display;
namespace Host {
/// Creates the host display. This may create a new window. The API used depends on the current configuration.
bool AcquireHostDisplay(HostDisplay::RenderAPI api);
/// Destroys the host display. This may close the display window.
void ReleaseHostDisplay();
/// Returns false if the window was completely occluded. If frame_skip is set, the frame won't be
/// displayed, but the GPU command queue will still be flushed.
//bool BeginPresentFrame(bool frame_skip);
/// Presents the frame to the display, and renders OSD elements.
//void EndPresentFrame();
/// Provided by the host; renders the display.
void RenderDisplay();
void InvalidateDisplay();
} // namespace Host
File diff suppressed because it is too large Load Diff
-225
View File
@@ -1,225 +0,0 @@
#pragma once
#include "common/string.h"
#include "common/timer.h"
#include "settings.h"
#include "types.h"
#include <chrono>
#include <deque>
#include <functional>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <vector>
enum LOGLEVEL;
class AudioStream;
class ByteStream;
class CDImage;
class HostDisplay;
class GameList;
struct SystemBootParameters;
namespace BIOS {
struct ImageInfo;
}
class HostInterface
{
public:
enum : u32
{
AUDIO_SAMPLE_RATE = 44100,
AUDIO_CHANNELS = 2,
DEFAULT_AUDIO_BUFFER_SIZE = 2048
};
HostInterface();
virtual ~HostInterface();
/// Access to host display.
ALWAYS_INLINE HostDisplay* GetDisplay() const { return m_display.get(); }
ALWAYS_INLINE bool HasDisplay() const { return static_cast<bool>(m_display.get()); }
/// Access to host audio stream.
ALWAYS_INLINE AudioStream* GetAudioStream() const { return m_audio_stream.get(); }
/// Initializes the emulator frontend.
virtual bool Initialize();
/// Shuts down the emulator frontend.
virtual void Shutdown();
virtual bool BootSystem(std::shared_ptr<SystemBootParameters> parameters);
virtual void PauseSystem(bool paused);
virtual void ResetSystem();
virtual void DestroySystem();
/// Loads state from the specified filename.
bool LoadState(const char* filename);
virtual void ReportError(const char* message);
virtual void ReportMessage(const char* message);
virtual void ReportDebuggerMessage(const char* message);
virtual bool ConfirmMessage(const char* message);
void ReportFormattedError(const char* format, ...) printflike(2, 3);
void ReportFormattedMessage(const char* format, ...) printflike(2, 3);
void ReportFormattedDebuggerMessage(const char* format, ...) printflike(2, 3);
bool ConfirmFormattedMessage(const char* format, ...) printflike(2, 3);
/// Adds OSD messages, duration is in seconds.
virtual void AddOSDMessage(std::string message, float duration = 2.0f) = 0;
virtual void AddKeyedOSDMessage(std::string key, std::string message, float duration = 2.0f) = 0;
virtual void RemoveKeyedOSDMessage(std::string key) = 0;
void AddFormattedOSDMessage(float duration, const char* format, ...) printflike(3, 4);
void AddKeyedFormattedOSDMessage(std::string key, float duration, const char* format, ...) printflike(4, 5);
/// Returns the base user directory path.
ALWAYS_INLINE const std::string& GetUserDirectory() const { return m_user_directory; }
/// Returns a path relative to the user directory.
std::string GetUserDirectoryRelativePath(const char* format, ...) const printflike(2, 3);
/// Returns a path relative to the application directory (for system files).
std::string GetProgramDirectoryRelativePath(const char* format, ...) const printflike(2, 3);
/// Returns a string which can be used as part of a filename, based on the current date/time.
static TinyString GetTimestampStringForFileName();
/// Displays a loading screen with the logo, rendered with ImGui. Use when executing possibly-time-consuming tasks
/// such as compiling shaders when starting up.
virtual void DisplayLoadingScreen(const char* message, int progress_min = -1, int progress_max = -1,
int progress_value = -1) = 0;
/// Retrieves information about specified game from game list.
virtual void GetGameInfo(const char* path, CDImage* image, std::string* code, std::string* title) = 0;
/// Returns the directory where per-game memory cards will be saved.
virtual std::string GetMemoryCardDirectory() const;
/// Returns the default path to a memory card.
virtual std::string GetSharedMemoryCardPath(u32 slot) const;
/// Returns the default path to a memory card for a specific game.
virtual std::string GetGameMemoryCardPath(const char* game_code, u32 slot) const;
/// Returns the path to the shader cache directory.
virtual std::string GetShaderCacheBasePath() const;
/// Returns a setting value from the configuration.
virtual std::string GetStringSettingValue(const char* section, const char* key, const char* default_value = "") = 0;
/// Returns a boolean setting from the configuration.
virtual bool GetBoolSettingValue(const char* section, const char* key, bool default_value = false);
/// Returns an integer setting from the configuration.
virtual int GetIntSettingValue(const char* section, const char* key, int default_value = 0);
/// Returns a float setting from the configuration.
virtual float GetFloatSettingValue(const char* section, const char* key, float default_value = 0.0f);
/// Returns a string list from the configuration.
virtual std::vector<std::string> GetSettingStringList(const char* section, const char* key) = 0;
/// Returns the settings interface.
virtual SettingsInterface* GetSettingsInterface() = 0;
virtual std::lock_guard<std::recursive_mutex> GetSettingsLock() = 0;
/// Translates a string to the current language.
virtual TinyString TranslateString(const char* context, const char* str, const char* disambiguation = nullptr,
int n = -1) const;
virtual std::string TranslateStdString(const char* context, const char* str, const char* disambiguation = nullptr,
int n = -1) const;
/// Returns the path to the directory to search for BIOS images.
virtual std::string GetBIOSDirectory();
/// Loads the BIOS image for the specified region.
std::optional<std::vector<u8>> GetBIOSImage(ConsoleRegion region);
/// Searches for a BIOS image for the specified region in the specified directory. If no match is found, the first
/// BIOS image within 512KB and 4MB will be used.
std::optional<std::vector<u8>> FindBIOSImageInDirectory(ConsoleRegion region, const char* directory);
/// Returns a list of filenames and descriptions for BIOS images in a directory.
std::vector<std::pair<std::string, const BIOS::ImageInfo*>> FindBIOSImagesInDirectory(const char* directory);
/// Returns true if any BIOS images are found in the configured BIOS directory.
bool HasAnyBIOSImages();
/// Opens a file in the DuckStation "package".
/// This is the APK for Android builds, or the program directory for standalone builds.
virtual std::unique_ptr<ByteStream> OpenPackageFile(const char* path, u32 flags) = 0;
virtual void OnRunningGameChanged(const std::string& path, CDImage* image, const std::string& game_code,
const std::string& game_title) = 0;
virtual void OnSystemPerformanceCountersUpdated() = 0;
/// Called when the display is invalidated (e.g. a state is loaded).
virtual void OnDisplayInvalidated() = 0;
/// Called when achievements data is loaded.
virtual void OnAchievementsRefreshed() = 0;
protected:
virtual bool AcquireHostDisplay() = 0;
virtual void ReleaseHostDisplay() = 0;
virtual std::unique_ptr<AudioStream> CreateAudioStream(AudioBackend backend) = 0;
virtual s32 GetAudioOutputVolume() const;
virtual void OnSystemCreated() = 0;
virtual void OnSystemPaused(bool paused) = 0;
virtual void OnSystemDestroyed() = 0;
virtual void OnControllerTypeChanged(u32 slot) = 0;
/// Restores all settings to defaults.
virtual void SetDefaultSettings(SettingsInterface& si);
/// Loads settings to m_settings and any frontend-specific parameters.
virtual void LoadSettings(SettingsInterface& si);
/// Saves current settings variables to ini.
virtual void SaveSettings(SettingsInterface& si);
/// Checks and fixes up any incompatible settings.
virtual void FixIncompatibleSettings(bool display_osd_messages);
/// Checks for settings changes, std::move() the old settings away for comparing beforehand.
virtual void CheckForSettingsChanges(const Settings& old_settings);
/// Switches the GPU renderer by saving state, recreating the display window, and restoring state (if needed).
virtual void RecreateSystem();
/// Enables "relative" mouse mode, locking the cursor position and returning relative coordinates.
virtual void SetMouseMode(bool relative, bool hide_cursor) = 0;
/// Call when host display size changes, use with "match display" aspect ratio setting.
virtual void OnHostDisplayResized();
/// Sets the user directory to the program directory, i.e. "portable mode".
void SetUserDirectoryToProgramDirectory();
/// Quick switch between software and hardware rendering.
void ToggleSoftwareRendering();
/// Adjusts the internal (render) resolution of the hardware backends.
void ModifyResolutionScale(s32 increment);
/// Updates software cursor state, based on controllers.
void UpdateSoftwareCursor();
bool SaveState(const char* filename);
void CreateAudioStream();
std::unique_ptr<HostDisplay> m_display;
std::unique_ptr<AudioStream> m_audio_stream;
std::string m_program_directory;
std::string m_user_directory;
};
#define TRANSLATABLE(context, str) str
extern HostInterface* g_host_interface;
+24 -10
View File
@@ -1,10 +1,14 @@
#include "host_interface_progress_callback.h"
#include "common/log.h"
#include "host.h"
Log_SetChannel(HostInterfaceProgressCallback);
HostInterfaceProgressCallback::HostInterfaceProgressCallback() : BaseProgressCallback() {}
void HostInterfaceProgressCallback::PushState() { BaseProgressCallback::PushState(); }
void HostInterfaceProgressCallback::PushState()
{
BaseProgressCallback::PushState();
}
void HostInterfaceProgressCallback::PopState()
{
@@ -57,32 +61,42 @@ void HostInterfaceProgressCallback::Redraw(bool force)
return;
m_last_progress_percent = percent;
g_host_interface->DisplayLoadingScreen(m_status_text, 0, static_cast<int>(m_progress_range),
static_cast<int>(m_progress_value));
Host::DisplayLoadingScreen(m_status_text, 0, static_cast<int>(m_progress_range), static_cast<int>(m_progress_value));
}
void HostInterfaceProgressCallback::DisplayError(const char* message) { Log_ErrorPrint(message); }
void HostInterfaceProgressCallback::DisplayError(const char* message)
{
Log_ErrorPrint(message);
}
void HostInterfaceProgressCallback::DisplayWarning(const char* message) { Log_WarningPrint(message); }
void HostInterfaceProgressCallback::DisplayWarning(const char* message)
{
Log_WarningPrint(message);
}
void HostInterfaceProgressCallback::DisplayInformation(const char* message) { Log_InfoPrint(message); }
void HostInterfaceProgressCallback::DisplayInformation(const char* message)
{
Log_InfoPrint(message);
}
void HostInterfaceProgressCallback::DisplayDebugMessage(const char* message) { Log_DevPrint(message); }
void HostInterfaceProgressCallback::DisplayDebugMessage(const char* message)
{
Log_DevPrint(message);
}
void HostInterfaceProgressCallback::ModalError(const char* message)
{
Log_ErrorPrint(message);
g_host_interface->ReportError(message);
Host::ReportErrorAsync("Error", message);
}
bool HostInterfaceProgressCallback::ModalConfirmation(const char* message)
{
Log_InfoPrint(message);
return g_host_interface->ConfirmMessage(message);
return Host::ConfirmMessage("Confirm", message);
}
void HostInterfaceProgressCallback::ModalInformation(const char* message)
{
Log_InfoPrint(message);
g_host_interface->ReportMessage(message);
}
@@ -1,6 +1,5 @@
#pragma once
#include "common/progress_callback.h"
#include "host_interface.h"
class HostInterfaceProgressCallback final : public BaseProgressCallback
{
+4 -1
View File
@@ -18,13 +18,16 @@ double GetBaseDoubleSettingValue(const char* section, const char* key, double de
std::vector<std::string> GetBaseStringListSetting(const char* section, const char* key);
// Allows the emucore to write settings back to the frontend. Use with care.
// You should call CommitBaseSettingChanges() after finishing writing, or it may not be written to disk.
// You should call CommitBaseSettingChanges() if you directly write to the layer (i.e. not these functions), or it may
// not be written to disk.
void SetBaseBoolSettingValue(const char* section, const char* key, bool value);
void SetBaseIntSettingValue(const char* section, const char* key, s32 value);
void SetBaseUIntSettingValue(const char* section, const char* key, u32 value);
void SetBaseFloatSettingValue(const char* section, const char* key, float value);
void SetBaseStringSettingValue(const char* section, const char* key, const char* value);
void SetBaseStringListSettingValue(const char* section, const char* key, const std::vector<std::string>& values);
bool AddValueToBaseStringListSetting(const char* section, const char* key, const char* value);
bool RemoveValueFromBaseStringListSetting(const char* section, const char* key, const char* value);
void DeleteBaseSettingValue(const char* section, const char* key);
void CommitBaseSettingChanges();
File diff suppressed because it is too large Load Diff
-285
View File
@@ -1,285 +0,0 @@
#pragma once
#include "common/types.h"
#include "imgui.h"
#include <functional>
#include <memory>
#include <string>
#include <vector>
class HostDisplayTexture;
namespace ImGuiFullscreen {
#define HEX_TO_IMVEC4(hex, alpha) \
ImVec4(static_cast<float>((hex >> 16) & 0xFFu) / 255.0f, static_cast<float>((hex >> 8) & 0xFFu) / 255.0f, \
static_cast<float>(hex & 0xFFu) / 255.0f, static_cast<float>(alpha) / 255.0f)
using LoadTextureFunction = std::unique_ptr<HostDisplayTexture>(*)(const char* path);
static constexpr float LAYOUT_SCREEN_WIDTH = 1280.0f;
static constexpr float LAYOUT_SCREEN_HEIGHT = 720.0f;
static constexpr float LAYOUT_LARGE_FONT_SIZE = 26.0f;
static constexpr float LAYOUT_MEDIUM_FONT_SIZE = 16.0f;
static constexpr float LAYOUT_SMALL_FONT_SIZE = 10.0f;
static constexpr float LAYOUT_MENU_BUTTON_HEIGHT = 50.0f;
static constexpr float LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY = 26.0f;
static constexpr float LAYOUT_MENU_BUTTON_X_PADDING = 15.0f;
static constexpr float LAYOUT_MENU_BUTTON_Y_PADDING = 10.0f;
extern ImFont* g_standard_font;
extern ImFont* g_medium_font;
extern ImFont* g_large_font;
extern bool g_initialized;
extern float g_layout_scale;
extern float g_layout_padding_left;
extern float g_layout_padding_top;
extern float g_menu_bar_size;
static ALWAYS_INLINE bool IsInitialized()
{
return g_initialized;
}
static ALWAYS_INLINE float DPIScale(float v)
{
return ImGui::GetIO().DisplayFramebufferScale.x * v;
}
static ALWAYS_INLINE float DPIScale(int v)
{
return ImGui::GetIO().DisplayFramebufferScale.x * static_cast<float>(v);
}
static ALWAYS_INLINE ImVec2 DPIScale(const ImVec2& v)
{
const ImVec2& fbs = ImGui::GetIO().DisplayFramebufferScale;
return ImVec2(v.x * fbs.x, v.y * fbs.y);
}
static ALWAYS_INLINE float WindowWidthScale(float v)
{
return ImGui::GetWindowWidth() * v;
}
static ALWAYS_INLINE float WindowHeightScale(float v)
{
return ImGui::GetWindowHeight() * v;
}
static ALWAYS_INLINE float LayoutScale(float v)
{
return g_layout_scale * v;
}
static ALWAYS_INLINE ImVec2 LayoutScale(const ImVec2& v)
{
return ImVec2(v.x * g_layout_scale, v.y * g_layout_scale);
}
static ALWAYS_INLINE ImVec2 LayoutScale(float x, float y)
{
return ImVec2(x * g_layout_scale, y * g_layout_scale);
}
static ALWAYS_INLINE ImVec2 LayoutScaleAndOffset(float x, float y)
{
return ImVec2(g_layout_padding_left + x * g_layout_scale, g_layout_padding_top + y * g_layout_scale);
}
static ALWAYS_INLINE ImVec4 UIPrimaryColor()
{
return HEX_TO_IMVEC4(0x212121, 0xff);
}
static ALWAYS_INLINE ImVec4 UIPrimaryLightColor()
{
return HEX_TO_IMVEC4(0x484848, 0xff);
}
static ALWAYS_INLINE ImVec4 UIPrimaryDarkColor()
{
return HEX_TO_IMVEC4(0x484848, 0xff);
}
static ALWAYS_INLINE ImVec4 UIPrimaryTextColor()
{
return HEX_TO_IMVEC4(0xffffff, 0xff);
}
static ALWAYS_INLINE ImVec4 UIPrimaryDisabledTextColor()
{
return HEX_TO_IMVEC4(0xaaaaaa, 0xff);
}
static ALWAYS_INLINE ImVec4 UITextHighlightColor()
{
return HEX_TO_IMVEC4(0x90caf9, 0xff);
}
static ALWAYS_INLINE ImVec4 UIPrimaryLineColor()
{
return HEX_TO_IMVEC4(0xffffff, 0xff);
}
static ALWAYS_INLINE ImVec4 UISecondaryColor()
{
return HEX_TO_IMVEC4(0x1565c0, 0xff);
}
static ALWAYS_INLINE ImVec4 UISecondaryLightColor()
{
return HEX_TO_IMVEC4(0x5e92f3, 0xff);
}
static ALWAYS_INLINE ImVec4 UISecondaryDarkColor()
{
return HEX_TO_IMVEC4(0x003c8f, 0xff);
}
static ALWAYS_INLINE ImVec4 UISecondaryTextColor()
{
return HEX_TO_IMVEC4(0xffffff, 0xff);
}
/// Initializes, setting up any state.
bool Initialize();
/// Shuts down, clearing all state.
void Shutdown();
void SetFontFilename(std::string filename);
void SetFontData(std::vector<u8> data);
void SetIconFontFilename(std::string icon_font_filename);
void SetIconFontData(std::vector<u8> data);
void SetFontSize(float size_pixels);
void SetFontGlyphRanges(const ImWchar* glyph_ranges);
/// Changes the menu bar size. Don't forget to call UpdateLayoutScale() and UpdateFonts().
void SetMenuBarSize(float size);
/// Texture cache.
HostDisplayTexture* GetCachedTexture(const std::string& name);
bool InvalidateCachedTexture(const std::string& path);
void SetLoadTextureFunction(LoadTextureFunction callback);
/// Rebuilds fonts to a new scale if needed. Returns true if fonts have changed and the texture needs updating.
bool UpdateFonts();
/// Removes the fullscreen fonts, leaving only the standard font.
void ResetFonts();
bool UpdateLayoutScale();
void BeginLayout();
void EndLayout();
bool IsCancelButtonPressed();
void DrawWindowTitle(const char* title);
bool BeginFullscreenColumns(const char* title = nullptr);
void EndFullscreenColumns();
bool BeginFullscreenColumnWindow(float start, float end, const char* name,
const ImVec4& background = HEX_TO_IMVEC4(0x212121, 0xFF));
void EndFullscreenColumnWindow();
bool BeginFullscreenWindow(float left, float top, float width, float height, const char* name,
const ImVec4& background = HEX_TO_IMVEC4(0x212121, 0xFF), float rounding = 0.0f,
float padding = 0.0f, ImGuiWindowFlags flags = 0);
bool BeginFullscreenWindow(const ImVec2& position, const ImVec2& size, const char* name,
const ImVec4& background = HEX_TO_IMVEC4(0x212121, 0xFF), float rounding = 0.0f,
float padding = 0.0f, ImGuiWindowFlags flags = 0);
void EndFullscreenWindow();
void BeginMenuButtons(u32 num_items = 0, float y_align = 0.0f, float x_padding = LAYOUT_MENU_BUTTON_X_PADDING,
float y_padding = LAYOUT_MENU_BUTTON_Y_PADDING, float item_height = LAYOUT_MENU_BUTTON_HEIGHT);
void EndMenuButtons();
bool MenuButtonFrame(const char* str_id, bool enabled, float height, bool* visible, bool* hovered, ImVec2* min,
ImVec2* max, ImGuiButtonFlags flags = 0, float hover_alpha = 1.0f);
void MenuHeading(const char* title, bool draw_line = true);
bool MenuHeadingButton(const char* title, const char* value = nullptr, bool enabled = true, bool draw_line = true);
bool ActiveButton(const char* title, bool is_active, bool enabled = true,
float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImFont* font = g_large_font);
bool MenuButton(const char* title, const char* summary, bool enabled = true, float height = LAYOUT_MENU_BUTTON_HEIGHT,
ImFont* font = g_large_font, ImFont* summary_font = g_medium_font);
bool MenuButtonWithValue(const char* title, const char* summary, const char* value, bool enabled = true,
float height = LAYOUT_MENU_BUTTON_HEIGHT, ImFont* font = g_large_font,
ImFont* summary_font = g_medium_font);
bool MenuImageButton(const char* title, const char* summary, ImTextureID user_texture_id, const ImVec2& image_size,
bool enabled = true, float height = LAYOUT_MENU_BUTTON_HEIGHT,
const ImVec2& uv0 = ImVec2(0.0f, 0.0f), const ImVec2& uv1 = ImVec2(1.0f, 1.0f),
ImFont* font = g_large_font, ImFont* summary_font = g_medium_font);
bool FloatingButton(const char* text, float x, float y, float width = -1.0f,
float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, float anchor_x = 0.0f, float anchor_y = 0.0f,
bool enabled = true, ImFont* font = g_large_font, ImVec2* out_position = nullptr);
bool ToggleButton(const char* title, const char* summary, bool* v, bool enabled = true,
float height = LAYOUT_MENU_BUTTON_HEIGHT, ImFont* font = g_large_font,
ImFont* summary_font = g_medium_font);
bool RangeButton(const char* title, const char* summary, s32* value, s32 min, s32 max, s32 increment,
const char* format = "%d", bool enabled = true, float height = LAYOUT_MENU_BUTTON_HEIGHT,
ImFont* font = g_large_font, ImFont* summary_font = g_medium_font);
bool RangeButton(const char* title, const char* summary, float* value, float min, float max, float increment,
const char* format = "%f", bool enabled = true, float height = LAYOUT_MENU_BUTTON_HEIGHT,
ImFont* font = g_large_font, ImFont* summary_font = g_medium_font);
bool EnumChoiceButtonImpl(const char* title, const char* summary, s32* value_pointer,
const char* (*to_display_name_function)(s32 value, void* opaque), void* opaque, u32 count,
bool enabled, float height, ImFont* font, ImFont* summary_font);
template<typename DataType, typename CountType>
static ALWAYS_INLINE bool EnumChoiceButton(const char* title, const char* summary, DataType* value_pointer,
const char* (*to_display_name_function)(DataType value), CountType count,
bool enabled = true, float height = LAYOUT_MENU_BUTTON_HEIGHT,
ImFont* font = g_large_font, ImFont* summary_font = g_medium_font)
{
s32 value = static_cast<s32>(*value_pointer);
auto to_display_name_wrapper = [](s32 value, void* opaque) -> const char* {
return (*static_cast<decltype(to_display_name_function)*>(opaque))(static_cast<DataType>(value));
};
if (EnumChoiceButtonImpl(title, summary, &value, to_display_name_wrapper, &to_display_name_function,
static_cast<u32>(count), enabled, height, font, summary_font))
{
*value_pointer = static_cast<DataType>(value);
return true;
}
else
{
return false;
}
}
void BeginNavBar(float x_padding = LAYOUT_MENU_BUTTON_X_PADDING, float y_padding = LAYOUT_MENU_BUTTON_Y_PADDING);
void EndNavBar();
void NavTitle(const char* title, float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImFont* font = g_large_font);
void RightAlignNavButtons(u32 num_items = 0, float item_width = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY,
float item_height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY);
bool NavButton(const char* title, bool is_active, bool enabled = true, float width = -1.0f,
float height = LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, ImFont* font = g_large_font);
using FileSelectorCallback = std::function<void(const std::string& path)>;
using FileSelectorFilters = std::vector<std::string>;
bool IsFileSelectorOpen();
void OpenFileSelector(const char* title, bool select_directory, FileSelectorCallback callback,
FileSelectorFilters filters = FileSelectorFilters(),
std::string initial_directory = std::string());
void CloseFileSelector();
using ChoiceDialogCallback = std::function<void(s32 index, const std::string& title, bool checked)>;
using ChoiceDialogOptions = std::vector<std::pair<std::string, bool>>;
bool IsChoiceDialogOpen();
void OpenChoiceDialog(const char* title, bool checkable, ChoiceDialogOptions options, ChoiceDialogCallback callback);
void CloseChoiceDialog();
float GetNotificationVerticalPosition();
float GetNotificationVerticalDirection();
void SetNotificationVerticalPosition(float position, float direction);
void OpenBackgroundProgressDialog(const char* str_id, std::string message, s32 min, s32 max, s32 value);
void UpdateBackgroundProgressDialog(const char* str_id, std::string message, s32 min, s32 max, s32 value);
void CloseBackgroundProgressDialog(const char* str_id);
void AddNotification(float duration, std::string title, std::string text, std::string image_path);
void ClearNotifications();
} // namespace ImGuiFullscreen
-56
View File
@@ -1,56 +0,0 @@
#include "imgui_styles.h"
void ImGui::StyleColorsDarker(ImGuiStyle* dst)
{
ImGuiStyle* style = dst ? dst : &ImGui::GetStyle();
ImVec4* colors = style->Colors;
colors[ImGuiCol_Text] = ImVec4(0.95f, 0.96f, 0.98f, 1.00f);
colors[ImGuiCol_TextDisabled] = ImVec4(0.36f, 0.42f, 0.47f, 1.00f);
colors[ImGuiCol_WindowBg] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f);
colors[ImGuiCol_ChildBg] = ImVec4(0.15f, 0.18f, 0.22f, 1.00f);
colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f);
colors[ImGuiCol_Border] = ImVec4(0.08f, 0.10f, 0.12f, 1.00f);
colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_FrameBg] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f);
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.12f, 0.20f, 0.28f, 1.00f);
colors[ImGuiCol_FrameBgActive] = ImVec4(0.09f, 0.12f, 0.14f, 1.00f);
colors[ImGuiCol_TitleBg] = ImVec4(0.09f, 0.12f, 0.14f, 0.65f);
colors[ImGuiCol_TitleBgActive] = ImVec4(0.08f, 0.10f, 0.12f, 1.00f);
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 0.51f);
colors[ImGuiCol_MenuBarBg] = ImVec4(0.15f, 0.18f, 0.22f, 1.00f);
colors[ImGuiCol_ScrollbarBg] = ImVec4(0.02f, 0.02f, 0.02f, 0.39f);
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f);
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.18f, 0.22f, 0.25f, 1.00f);
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.09f, 0.21f, 0.31f, 1.00f);
colors[ImGuiCol_CheckMark] = ImVec4(0.28f, 0.56f, 1.00f, 1.00f);
colors[ImGuiCol_SliderGrab] = ImVec4(0.28f, 0.56f, 1.00f, 1.00f);
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.37f, 0.61f, 1.00f, 1.00f);
colors[ImGuiCol_Button] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f);
colors[ImGuiCol_ButtonHovered] = ImVec4(0.33f, 0.38f, 0.46f, 1.00f);
colors[ImGuiCol_ButtonActive] = ImVec4(0.27f, 0.32f, 0.38f, 1.00f);
colors[ImGuiCol_Header] = ImVec4(0.20f, 0.25f, 0.29f, 0.55f);
colors[ImGuiCol_HeaderHovered] = ImVec4(0.33f, 0.38f, 0.46f, 1.00f);
colors[ImGuiCol_HeaderActive] = ImVec4(0.27f, 0.32f, 0.38f, 1.00f);
colors[ImGuiCol_Separator] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f);
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.33f, 0.38f, 0.46f, 1.00f);
colors[ImGuiCol_SeparatorActive] = ImVec4(0.27f, 0.32f, 0.38f, 1.00f);
colors[ImGuiCol_ResizeGrip] = ImVec4(0.26f, 0.59f, 0.98f, 0.25f);
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.33f, 0.38f, 0.46f, 1.00f);
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.27f, 0.32f, 0.38f, 1.00f);
colors[ImGuiCol_Tab] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f);
colors[ImGuiCol_TabHovered] = ImVec4(0.33f, 0.38f, 0.46f, 1.00f);
colors[ImGuiCol_TabActive] = ImVec4(0.27f, 0.32f, 0.38f, 1.00f);
colors[ImGuiCol_TabUnfocused] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f);
colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f);
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
}
-6
View File
@@ -1,6 +0,0 @@
#pragma once
#include <imgui.h>
namespace ImGui {
void StyleColorsDarker(ImGuiStyle* dst = nullptr);
} // namespace ImGui
+2 -1
View File
@@ -2,6 +2,7 @@
#include "common/log.h"
#include "cpu_core.h"
#include "dma.h"
#include "host.h"
#include "imgui.h"
#include "interrupt_controller.h"
#include "system.h"
@@ -722,7 +723,7 @@ void MDEC::HandleSetScaleCommand()
void MDEC::DrawDebugStateWindow()
{
const float framebuffer_scale = ImGui::GetIO().DisplayFramebufferScale.x;
const float framebuffer_scale = Host::GetOSDScale();
ImGui::SetNextWindowSize(ImVec2(300.0f * framebuffer_scale, 350.0f * framebuffer_scale), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("MDEC State", nullptr))
+9 -7
View File
@@ -3,7 +3,7 @@
#include "common/file_system.h"
#include "common/log.h"
#include "common/string_util.h"
#include "host_interface.h"
#include "host.h"
#include "system.h"
#include "util/state_wrapper.h"
#include <cstdio>
@@ -273,8 +273,8 @@ std::unique_ptr<MemoryCard> MemoryCard::Open(std::string_view filename)
if (!mc->LoadFromFile())
{
Log_InfoPrintf("Memory card at '%s' could not be read, formatting.", mc->m_filename.c_str());
g_host_interface->AddFormattedOSDMessage(
5.0f, g_host_interface->TranslateString("OSDMessage", "Memory card at '%s' could not be read, formatting."),
Host::AddFormattedOSDMessage(
5.0f, Host::TranslateString("OSDMessage", "Memory card at '%s' could not be read, formatting."),
mc->m_filename.c_str());
mc->Format();
}
@@ -309,8 +309,8 @@ bool MemoryCard::SaveIfChanged(bool display_osd_message)
{
if (display_osd_message)
{
g_host_interface->AddFormattedOSDMessage(
20.0f, g_host_interface->TranslateString("OSDMessage", "Failed to save memory card to '%s'"),
Host::AddFormattedOSDMessage(
20.0f, Host::TranslateString("OSDMessage", "Failed to save memory card to '%s'"),
m_filename.c_str());
}
@@ -318,8 +318,10 @@ bool MemoryCard::SaveIfChanged(bool display_osd_message)
}
if (display_osd_message)
g_host_interface->AddFormattedOSDMessage(
2.0f, g_host_interface->TranslateString("OSDMessage", "Saved memory card to '%s'"), m_filename.c_str());
{
Host::AddFormattedOSDMessage(
2.0f, Host::TranslateString("OSDMessage", "Saved memory card to '%s'"), m_filename.c_str());
}
return true;
}
-1
View File
@@ -4,7 +4,6 @@
#include "common/log.h"
#include "common/path.h"
#include "common/string_util.h"
#include "host_interface.h"
#include "system.h"
#include "util/shiftjis.h"
#include "util/state_wrapper.h"
-314
View File
@@ -1,314 +0,0 @@
#include "namco_guncon.h"
#include "common/assert.h"
#include "common/log.h"
#include "gpu.h"
#include "host_display.h"
#include "host_interface.h"
#include "resources.h"
#include "system.h"
#include "util/state_wrapper.h"
#include <array>
Log_SetChannel(NamcoGunCon);
NamcoGunCon::NamcoGunCon() = default;
NamcoGunCon::~NamcoGunCon() = default;
ControllerType NamcoGunCon::GetType() const
{
return ControllerType::NamcoGunCon;
}
std::optional<s32> NamcoGunCon::GetAxisCodeByName(std::string_view axis_name) const
{
return StaticGetAxisCodeByName(axis_name);
}
std::optional<s32> NamcoGunCon::GetButtonCodeByName(std::string_view button_name) const
{
return StaticGetButtonCodeByName(button_name);
}
void NamcoGunCon::Reset()
{
m_transfer_state = TransferState::Idle;
}
bool NamcoGunCon::DoState(StateWrapper& sw, bool apply_input_state)
{
if (!Controller::DoState(sw, apply_input_state))
return false;
u16 button_state = m_button_state;
u16 position_x = m_position_x;
u16 position_y = m_position_y;
sw.Do(&button_state);
sw.Do(&position_x);
sw.Do(&position_y);
if (apply_input_state)
{
m_button_state = button_state;
m_position_x = position_x;
m_position_y = position_y;
}
sw.Do(&m_transfer_state);
return true;
}
bool NamcoGunCon::GetButtonState(s32 button_code) const
{
if (button_code < 0 || button_code > static_cast<s32>(Button::B))
return false;
const u16 bit = u16(1) << static_cast<u8>(button_code);
return ((m_button_state & bit) == 0);
}
void NamcoGunCon::SetButtonState(Button button, bool pressed)
{
if (button == Button::ShootOffscreen)
{
if (m_shoot_offscreen != pressed)
{
m_shoot_offscreen = pressed;
SetButtonState(Button::Trigger, pressed);
}
return;
}
static constexpr std::array<u8, static_cast<size_t>(Button::Count)> indices = {{13, 3, 14}};
if (pressed)
m_button_state &= ~(u16(1) << indices[static_cast<u8>(button)]);
else
m_button_state |= u16(1) << indices[static_cast<u8>(button)];
}
void NamcoGunCon::SetButtonState(s32 button_code, bool pressed)
{
if (button_code < 0 || button_code >= static_cast<s32>(Button::Count))
return;
SetButtonState(static_cast<Button>(button_code), pressed);
}
void NamcoGunCon::ResetTransferState()
{
m_transfer_state = TransferState::Idle;
}
bool NamcoGunCon::Transfer(const u8 data_in, u8* data_out)
{
static constexpr u16 ID = 0x5A63;
switch (m_transfer_state)
{
case TransferState::Idle:
{
*data_out = 0xFF;
if (data_in == 0x01)
{
m_transfer_state = TransferState::Ready;
return true;
}
return false;
}
case TransferState::Ready:
{
if (data_in == 0x42)
{
*data_out = Truncate8(ID);
m_transfer_state = TransferState::IDMSB;
return true;
}
*data_out = 0xFF;
return false;
}
case TransferState::IDMSB:
{
*data_out = Truncate8(ID >> 8);
m_transfer_state = TransferState::ButtonsLSB;
return true;
}
case TransferState::ButtonsLSB:
{
*data_out = Truncate8(m_button_state);
m_transfer_state = TransferState::ButtonsMSB;
return true;
}
case TransferState::ButtonsMSB:
{
*data_out = Truncate8(m_button_state >> 8);
m_transfer_state = TransferState::XLSB;
return true;
}
case TransferState::XLSB:
{
UpdatePosition();
*data_out = Truncate8(m_position_x);
m_transfer_state = TransferState::XMSB;
return true;
}
case TransferState::XMSB:
{
*data_out = Truncate8(m_position_x >> 8);
m_transfer_state = TransferState::YLSB;
return true;
}
case TransferState::YLSB:
{
*data_out = Truncate8(m_position_y);
m_transfer_state = TransferState::YMSB;
return true;
}
case TransferState::YMSB:
{
*data_out = Truncate8(m_position_y >> 8);
m_transfer_state = TransferState::Idle;
return false;
}
default:
{
UnreachableCode();
return false;
}
}
}
void NamcoGunCon::UpdatePosition()
{
// get screen coordinates
const HostDisplay* display = g_host_interface->GetDisplay();
const s32 mouse_x = display->GetMousePositionX();
const s32 mouse_y = display->GetMousePositionY();
// are we within the active display area?
u32 tick, line;
if (mouse_x < 0 || mouse_y < 0 ||
!g_gpu->ConvertScreenCoordinatesToBeamTicksAndLines(mouse_x, mouse_y, m_x_scale, &tick, &line) ||
m_shoot_offscreen)
{
Log_DebugPrintf("Lightgun out of range for window coordinates %d,%d", mouse_x, mouse_y);
m_position_x = 0x01;
m_position_y = 0x0A;
return;
}
// 8MHz units for X = 44100*768*11/7 = 53222400 / 8000000 = 6.6528
const double divider = static_cast<double>(g_gpu->GetCRTCFrequency()) / 8000000.0;
m_position_x = static_cast<u16>(static_cast<float>(tick) / static_cast<float>(divider));
m_position_y = static_cast<u16>(line);
Log_DebugPrintf("Lightgun window coordinates %d,%d -> tick %u line %u 8mhz ticks %u", mouse_x, mouse_y, tick, line,
m_position_x);
}
std::unique_ptr<NamcoGunCon> NamcoGunCon::Create()
{
return std::make_unique<NamcoGunCon>();
}
std::optional<s32> NamcoGunCon::StaticGetAxisCodeByName(std::string_view button_name)
{
return std::nullopt;
}
std::optional<s32> NamcoGunCon::StaticGetButtonCodeByName(std::string_view button_name)
{
#define BUTTON(name) \
if (button_name == #name) \
{ \
return static_cast<s32>(ZeroExtend32(static_cast<u8>(Button::name))); \
}
BUTTON(Trigger);
BUTTON(ShootOffscreen);
BUTTON(A);
BUTTON(B);
return std::nullopt;
#undef BUTTON
}
Controller::AxisList NamcoGunCon::StaticGetAxisNames()
{
return {};
}
Controller::ButtonList NamcoGunCon::StaticGetButtonNames()
{
return {{TRANSLATABLE("NamcoGunCon", "Trigger"), static_cast<s32>(Button::Trigger)},
{TRANSLATABLE("NamcoGunCon", "ShootOffscreen"), static_cast<s32>(Button::ShootOffscreen)},
{TRANSLATABLE("NamcoGunCon", "A"), static_cast<s32>(Button::A)},
{TRANSLATABLE("NamcoGunCon", "B"), static_cast<s32>(Button::B)}};
}
u32 NamcoGunCon::StaticGetVibrationMotorCount()
{
return 0;
}
Controller::SettingList NamcoGunCon::StaticGetSettings()
{
static constexpr std::array<SettingInfo, 3> settings = {
{{SettingInfo::Type::Path, "CrosshairImagePath", TRANSLATABLE("NamcoGunCon", "Crosshair Image Path"),
TRANSLATABLE("NamcoGunCon", "Path to an image to use as a crosshair/cursor.")},
{SettingInfo::Type::Float, "CrosshairScale", TRANSLATABLE("NamcoGunCon", "Crosshair Image Scale"),
TRANSLATABLE("NamcoGunCon", "Scale of crosshair image on screen."), "1.0", "0.0001", "100.0", "0.10"},
{SettingInfo::Type::Float, "XScale", TRANSLATABLE("NamcoGunCon", "X Scale"),
TRANSLATABLE("NamcoGunCon", "Scales X coordinates relative to the center of the screen."), "1.0", "0.01", "2.0",
"0.01"}}};
return SettingList(settings.begin(), settings.end());
}
void NamcoGunCon::LoadSettings(const char* section)
{
Controller::LoadSettings(section);
std::string path = g_host_interface->GetStringSettingValue(section, "CrosshairImagePath");
if (path != m_crosshair_image_path)
{
m_crosshair_image_path = std::move(path);
if (m_crosshair_image_path.empty() ||
!m_crosshair_image.LoadFromFile(m_crosshair_image_path.c_str()))
{
m_crosshair_image.Invalidate();
}
}
#ifndef __ANDROID__
if (!m_crosshair_image.IsValid())
{
m_crosshair_image.SetPixels(Resources::CROSSHAIR_IMAGE_WIDTH, Resources::CROSSHAIR_IMAGE_HEIGHT,
Resources::CROSSHAIR_IMAGE_DATA.data());
}
#endif
m_crosshair_image_scale = g_host_interface->GetFloatSettingValue(section, "CrosshairScale", 1.0f);
m_x_scale = g_host_interface->GetFloatSettingValue(section, "XScale", 1.0f);
}
bool NamcoGunCon::GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode)
{
if (!m_crosshair_image.IsValid())
return false;
*image = &m_crosshair_image;
*image_scale = m_crosshair_image_scale;
*relative_mode = false;
return true;
}
+108 -146
View File
@@ -1,12 +1,16 @@
#include "negcon.h"
#include "common/assert.h"
#include "common/log.h"
#include "host_interface.h"
#include "host.h"
#include "system.h"
#include "util/state_wrapper.h"
#include <array>
#include <cmath>
NeGcon::NeGcon()
// Mapping of Button to index of corresponding bit in m_button_state
static constexpr std::array<u8, static_cast<size_t>(NeGcon::Button::Count)> s_button_indices = {3, 4, 5, 6,
7, 11, 12, 13};
NeGcon::NeGcon(u32 index) : Controller(index)
{
m_axis_state.fill(0x00);
m_axis_state[static_cast<u8>(Axis::Steering)] = 0x80;
@@ -19,16 +23,6 @@ ControllerType NeGcon::GetType() const
return ControllerType::NeGcon;
}
std::optional<s32> NeGcon::GetAxisCodeByName(std::string_view axis_name) const
{
return StaticGetAxisCodeByName(axis_name);
}
std::optional<s32> NeGcon::GetButtonCodeByName(std::string_view button_name) const
{
return StaticGetButtonCodeByName(button_name);
}
void NeGcon::Reset()
{
m_transfer_state = TransferState::Idle;
@@ -48,73 +42,73 @@ bool NeGcon::DoState(StateWrapper& sw, bool apply_input_state)
return true;
}
float NeGcon::GetAxisState(s32 axis_code) const
float NeGcon::GetBindState(u32 index) const
{
if (axis_code < 0 || axis_code >= static_cast<s32>(Axis::Count))
return 0.0f;
if (axis_code == static_cast<s32>(Axis::Steering))
return (((static_cast<float>(m_axis_state[static_cast<s32>(Axis::Steering)]) / 255.0f) * 2.0f) - 1.0f);
else
return (static_cast<float>(m_axis_state[static_cast<s32>(axis_code)]) / 255.0f);
}
void NeGcon::SetAxisState(s32 axis_code, float value)
{
if (axis_code < 0 || axis_code >= static_cast<s32>(Axis::Count))
return;
// Steering Axis: -1..1 -> 0..255
if (axis_code == static_cast<s32>(Axis::Steering))
if (index == (static_cast<u32>(Button::Count) + static_cast<u32>(HalfAxis::SteeringLeft)) ||
index == (static_cast<u32>(Button::Count) + static_cast<u32>(HalfAxis::SteeringRight)))
{
const float float_value =
(std::abs(value) < m_steering_deadzone) ?
0.0f :
std::copysign((std::abs(value) - m_steering_deadzone) / (1.0f - m_steering_deadzone), value);
const u8 u8_value = static_cast<u8>(std::clamp(std::round(((float_value + 1.0f) / 2.0f) * 255.0f), 0.0f, 255.0f));
SetAxisState(static_cast<Axis>(axis_code), u8_value);
return;
return static_cast<float>(m_half_axis_state[index - static_cast<u32>(Button::Count)]) * (1.0f / 255.0f);
}
else if (index >= static_cast<u32>(Button::Count))
{
// less one because of the two steering axes
const u32 sub_index = index - (static_cast<u32>(Button::Count) + 1);
if (sub_index >= m_axis_state.size())
return 0.0f;
// I, II, L: -1..1 -> 0..255
const u8 u8_value = static_cast<u8>(std::clamp(value * 255.0f, 0.0f, 255.0f));
SetAxisState(static_cast<Axis>(axis_code), u8_value);
}
void NeGcon::SetAxisState(Axis axis, u8 value)
{
m_axis_state[static_cast<u8>(axis)] = value;
}
bool NeGcon::GetButtonState(s32 button_code) const
{
if (button_code < 0 || button_code >= static_cast<s32>(Button::Count))
return false;
const u16 bit = u16(1) << static_cast<u8>(button_code);
return ((m_button_state & bit) == 0);
}
void NeGcon::SetButtonState(s32 button_code, bool pressed)
{
if (button_code < 0 || button_code >= static_cast<s32>(Button::Count))
return;
SetButtonState(static_cast<Button>(button_code), pressed);
}
void NeGcon::SetButtonState(Button button, bool pressed)
{
// Mapping of Button to index of corresponding bit in m_button_state
static constexpr std::array<u8, static_cast<size_t>(Button::Count)> indices = {3, 4, 5, 6, 7, 11, 12, 13};
if (pressed)
m_button_state &= ~(u16(1) << indices[static_cast<u8>(button)]);
return static_cast<float>(m_axis_state[sub_index]) * (1.0f / 255.0f);
}
else
m_button_state |= u16(1) << indices[static_cast<u8>(button)];
{
const u32 bit = s_button_indices[index];
return static_cast<float>(((m_button_state >> bit) & 1u) ^ 1u);
}
}
void NeGcon::SetBindState(u32 index, float value)
{
// Steering Axis: -1..1 -> 0..255
if (index == (static_cast<u32>(Button::Count) + static_cast<u32>(HalfAxis::SteeringLeft)) ||
index == (static_cast<u32>(Button::Count) + static_cast<u32>(HalfAxis::SteeringRight)))
{
value = ApplyAnalogDeadzoneSensitivity(m_steering_deadzone, 1.0f, value);
m_half_axis_state[index - static_cast<u32>(Button::Count)] =
static_cast<u8>(std::clamp(value * 255.0f, 0.0f, 255.0f));
// Merge left/right. Seems to be inverted.
m_axis_state[static_cast<u32>(Axis::Steering)] =
((m_half_axis_state[1] != 0) ? (127u + ((m_half_axis_state[1] + 1u) / 2u)) :
(127u - (m_half_axis_state[0] / 2u)));
}
else if (index >= static_cast<u32>(Button::Count))
{
// less one because of the two steering axes
const u32 sub_index = index - (static_cast<u32>(Button::Count) + 1);
if (sub_index >= m_axis_state.size())
return;
m_axis_state[sub_index] = static_cast<u8>(std::clamp(value * 255.0f, 0.0f, 255.0f));
}
else if (index < static_cast<u32>(Button::Count))
{
const u16 bit = u16(1) << s_button_indices[static_cast<u8>(index)];
if (value >= 0.5f)
{
if (m_button_state & bit)
System::SetRunaheadReplayFlag();
m_button_state &= ~bit;
}
else
{
if (!(m_button_state & bit))
System::SetRunaheadReplayFlag();
m_button_state |= bit;
}
}
}
u32 NeGcon::GetButtonStateBits() const
@@ -221,87 +215,55 @@ bool NeGcon::Transfer(const u8 data_in, u8* data_out)
}
}
std::unique_ptr<NeGcon> NeGcon::Create()
std::unique_ptr<NeGcon> NeGcon::Create(u32 index)
{
return std::make_unique<NeGcon>();
return std::make_unique<NeGcon>(index);
}
std::optional<s32> NeGcon::StaticGetAxisCodeByName(std::string_view axis_name)
{
#define AXIS(name) \
if (axis_name == #name) \
static const Controller::ControllerBindingInfo s_binding_info[] = {
#define BUTTON(name, display_name, button, genb) \
{ \
return static_cast<s32>(ZeroExtend32(static_cast<u8>(Axis::name))); \
name, display_name, static_cast<u32>(button), Controller::ControllerBindingType::Button, genb \
}
#define AXIS(name, display_name, halfaxis, genb) \
{ \
name, display_name, static_cast<u32>(NeGcon::Button::Count) + static_cast<u32>(halfaxis), \
Controller::ControllerBindingType::HalfAxis, genb \
}
AXIS(Steering);
AXIS(I);
AXIS(II);
AXIS(L);
return std::nullopt;
BUTTON("Up", "D-Pad Up", NeGcon::Button::Up, GenericInputBinding::DPadUp),
BUTTON("Right", "D-Pad Right", NeGcon::Button::Right, GenericInputBinding::DPadRight),
BUTTON("Down", "D-Pad Down", NeGcon::Button::Down, GenericInputBinding::DPadDown),
BUTTON("Left", "D-Pad Left", NeGcon::Button::Left, GenericInputBinding::DPadLeft),
BUTTON("Start", "Start", NeGcon::Button::Start, GenericInputBinding::Start),
BUTTON("A", "A Button", NeGcon::Button::A, GenericInputBinding::Circle),
BUTTON("B", "B Button", NeGcon::Button::B, GenericInputBinding::Triangle),
AXIS("I", "I Button", NeGcon::HalfAxis::I, GenericInputBinding::L2),
AXIS("II", "II Button", NeGcon::HalfAxis::II, GenericInputBinding::R2),
AXIS("L", "Left Trigger", NeGcon::HalfAxis::L, GenericInputBinding::L1),
BUTTON("R", "Right Trigger", NeGcon::Button::R, GenericInputBinding::R1),
AXIS("SteeringLeft", "Steering (Twist) Left", NeGcon::HalfAxis::SteeringLeft, GenericInputBinding::LeftStickLeft),
AXIS("SteeringRight", "Steering (Twist) Right", NeGcon::HalfAxis::SteeringRight, GenericInputBinding::LeftStickRight),
#undef AXIS
}
std::optional<s32> NeGcon::StaticGetButtonCodeByName(std::string_view button_name)
{
#define BUTTON(name) \
if (button_name == #name) \
{ \
return static_cast<s32>(ZeroExtend32(static_cast<u8>(Button::name))); \
}
BUTTON(Up);
BUTTON(Down);
BUTTON(Left);
BUTTON(Right);
BUTTON(A);
BUTTON(B);
BUTTON(R);
BUTTON(Start);
return std::nullopt;
#undef BUTTON
}
};
Controller::AxisList NeGcon::StaticGetAxisNames()
static const SettingInfo s_settings[] = {
{SettingInfo::Type::Float, "SteeringDeadzone", TRANSLATABLE("NeGcon", "Steering Axis Deadzone"),
TRANSLATABLE("NeGcon", "Sets deadzone size for steering axis."), "0.00f", "0.00f", "0.99f", "0.01f"}};
const Controller::ControllerInfo NeGcon::INFO = {ControllerType::NeGcon,
"NeGcon",
TRANSLATABLE("ControllerType", "NeGcon"),
s_binding_info,
countof(s_binding_info),
s_settings,
countof(s_settings),
Controller::VibrationCapabilities::NoVibration};
void NeGcon::LoadSettings(SettingsInterface& si, const char* section)
{
return {{TRANSLATABLE("NeGcon", "Steering"), static_cast<s32>(Axis::Steering), AxisType::Full},
{TRANSLATABLE("NeGcon", "I"), static_cast<s32>(Axis::I), AxisType::Half},
{TRANSLATABLE("NeGcon", "II"), static_cast<s32>(Axis::II), AxisType::Half},
{TRANSLATABLE("NeGcon", "L"), static_cast<s32>(Axis::L), AxisType::Half}};
}
Controller::ButtonList NeGcon::StaticGetButtonNames()
{
return {{TRANSLATABLE("NeGcon", "Up"), static_cast<s32>(Button::Up)},
{TRANSLATABLE("NeGcon", "Down"), static_cast<s32>(Button::Down)},
{TRANSLATABLE("NeGcon", "Left"), static_cast<s32>(Button::Left)},
{TRANSLATABLE("NeGcon", "Right"), static_cast<s32>(Button::Right)},
{TRANSLATABLE("NeGcon", "A"), static_cast<s32>(Button::A)},
{TRANSLATABLE("NeGcon", "B"), static_cast<s32>(Button::B)},
{TRANSLATABLE("NeGcon", "R"), static_cast<s32>(Button::R)},
{TRANSLATABLE("NeGcon", "Start"), static_cast<s32>(Button::Start)}};
}
u32 NeGcon::StaticGetVibrationMotorCount()
{
return 0;
}
Controller::SettingList NeGcon::StaticGetSettings()
{
static constexpr std::array<SettingInfo, 1> settings = {
{{SettingInfo::Type::Float, "SteeringDeadzone", TRANSLATABLE("NeGcon", "Steering Axis Deadzone"),
TRANSLATABLE("NeGcon", "Sets deadzone size for steering axis."), "0.00f", "0.00f", "0.99f", "0.01f"}}};
return SettingList(settings.begin(), settings.end());
}
void NeGcon::LoadSettings(const char* section)
{
Controller::LoadSettings(section);
m_steering_deadzone = g_host_interface->GetFloatSettingValue(section, "SteeringDeadzone", 0.10f);
Controller::LoadSettings(si, section);
m_steering_deadzone = si.GetFloatValue(section, "SteeringDeadzone", 0.10f);
}
+21 -19
View File
@@ -30,40 +30,38 @@ public:
Count
};
NeGcon();
enum class HalfAxis : u8
{
SteeringLeft,
SteeringRight,
I,
II,
L,
Count
};
static const Controller::ControllerInfo INFO;
NeGcon(u32 index);
~NeGcon() override;
static std::unique_ptr<NeGcon> Create();
static std::optional<s32> StaticGetAxisCodeByName(std::string_view axis_name);
static std::optional<s32> StaticGetButtonCodeByName(std::string_view button_name);
static AxisList StaticGetAxisNames();
static ButtonList StaticGetButtonNames();
static u32 StaticGetVibrationMotorCount();
static SettingList StaticGetSettings();
static std::unique_ptr<NeGcon> Create(u32 index);
ControllerType GetType() const override;
std::optional<s32> GetAxisCodeByName(std::string_view axis_name) const override;
std::optional<s32> GetButtonCodeByName(std::string_view button_name) const override;
void Reset() override;
bool DoState(StateWrapper& sw, bool apply_input_state) override;
float GetAxisState(s32 axis_code) const override;
void SetAxisState(s32 axis_code, float value) override;
bool GetButtonState(s32 button_code) const override;
void SetButtonState(s32 button_code, bool pressed) override;
float GetBindState(u32 index) const override;
void SetBindState(u32 index, float value) override;
void ResetTransferState() override;
bool Transfer(const u8 data_in, u8* data_out) override;
void SetAxisState(Axis axis, u8 value);
void SetButtonState(Button button, bool pressed);
u32 GetButtonStateBits() const override;
std::optional<u32> GetAnalogInputBytes() const override;
void LoadSettings(const char* section) override;
void LoadSettings(SettingsInterface& si, const char* section) override;
private:
enum class TransferState : u8
@@ -81,10 +79,14 @@ private:
std::array<u8, static_cast<u8>(Axis::Count)> m_axis_state{};
// steering, merged to m_axis_state
std::array<u8, 2> m_half_axis_state{};
// buttons are active low; bits 0-2, 8-10, 14-15 are not used and are always high
u16 m_button_state = UINT16_C(0xFFFF);
TransferState m_transfer_state = TransferState::Idle;
float m_steering_deadzone = 0.00f;
};
+18 -21
View File
@@ -1,7 +1,7 @@
#include "pad.h"
#include "common/log.h"
#include "controller.h"
#include "host_interface.h"
#include "host.h"
#include "interrupt_controller.h"
#include "memory_card.h"
#include "multitap.h"
@@ -66,17 +66,17 @@ bool Pad::DoStateController(StateWrapper& sw, u32 i)
// UI notification portion is separated from emulation portion (intentional condition check redundancy)
if (g_settings.load_devices_from_save_states)
{
g_host_interface->AddFormattedOSDMessage(
Host::AddFormattedOSDMessage(
10.0f,
g_host_interface->TranslateString(
"OSDMessage", "Save state contains controller type %s in port %u, but %s is used. Switching."),
Host::TranslateString("OSDMessage",
"Save state contains controller type %s in port %u, but %s is used. Switching."),
Settings::GetControllerTypeName(state_controller_type), i + 1u,
Settings::GetControllerTypeName(controller_type));
}
else
{
g_host_interface->AddFormattedOSDMessage(
10.0f, g_host_interface->TranslateString("OSDMessage", "Ignoring mismatched controller type %s in port %u."),
Host::AddFormattedOSDMessage(
10.0f, Host::TranslateString("OSDMessage", "Ignoring mismatched controller type %s in port %u."),
Settings::GetControllerTypeName(state_controller_type), i + 1u);
}
@@ -129,10 +129,10 @@ bool Pad::DoStateMemcard(StateWrapper& sw, u32 i)
if (card_present_in_state && !m_memory_cards[i] && g_settings.load_devices_from_save_states)
{
g_host_interface->AddFormattedOSDMessage(
Host::AddFormattedOSDMessage(
20.0f,
g_host_interface->TranslateString(
"OSDMessage", "Memory card %u present in save state but not in system. Creating temporary card."),
Host::TranslateString("OSDMessage",
"Memory card %u present in save state but not in system. Creating temporary card."),
i + 1u);
m_memory_cards[i] = MemoryCard::Create();
}
@@ -170,10 +170,10 @@ bool Pad::DoStateMemcard(StateWrapper& sw, u32 i)
}
else
{
g_host_interface->AddFormattedOSDMessage(
Host::AddFormattedOSDMessage(
20.0f,
g_host_interface->TranslateString(
"OSDMessage", "Memory card %u from save state does match current card data. Simulating replugging."),
Host::TranslateString("OSDMessage",
"Memory card %u from save state does match current card data. Simulating replugging."),
i + 1u);
// this is a potentially serious issue - some games cache info from memcards and jumping around
@@ -188,10 +188,9 @@ bool Pad::DoStateMemcard(StateWrapper& sw, u32 i)
}
else
{
g_host_interface->AddFormattedOSDMessage(
Host::AddFormattedOSDMessage(
20.0f,
g_host_interface->TranslateString("OSDMessage",
"Memory card %u present in save state but not in system. Ignoring card."),
Host::TranslateString("OSDMessage", "Memory card %u present in save state but not in system. Ignoring card."),
i + 1u);
}
@@ -202,19 +201,17 @@ bool Pad::DoStateMemcard(StateWrapper& sw, u32 i)
{
if (g_settings.load_devices_from_save_states)
{
g_host_interface->AddFormattedOSDMessage(
Host::AddFormattedOSDMessage(
20.0f,
g_host_interface->TranslateString("OSDMessage",
"Memory card %u present in system but not in save state. Removing card."),
Host::TranslateString("OSDMessage", "Memory card %u present in system but not in save state. Removing card."),
i + 1u);
m_memory_cards[i].reset();
}
else
{
g_host_interface->AddFormattedOSDMessage(
Host::AddFormattedOSDMessage(
20.0f,
g_host_interface->TranslateString("OSDMessage",
"Memory card %u present in system but not in save state. Replugging card."),
Host::TranslateString("OSDMessage", "Memory card %u present in system but not in save state. Replugging card."),
i + 1u);
m_memory_cards[i]->Reset();
}
+44 -80
View File
@@ -2,17 +2,19 @@
#include "common/assert.h"
#include "common/log.h"
#include "gpu.h"
#include "host.h"
#include "host_display.h"
#include "host_interface.h"
#include "system.h"
#include "util/state_wrapper.h"
#include <array>
Log_SetChannel(PlayStationMouse);
PlayStationMouse::PlayStationMouse()
static constexpr std::array<u8, static_cast<size_t>(PlayStationMouse::Button::Count)> s_button_indices = {{11, 10}};
PlayStationMouse::PlayStationMouse(u32 index) : Controller(index)
{
m_last_host_position_x = g_host_interface->GetDisplay()->GetMousePositionX();
m_last_host_position_y = g_host_interface->GetDisplay()->GetMousePositionY();
m_last_host_position_x = g_host_display->GetMousePositionX();
m_last_host_position_y = g_host_display->GetMousePositionY();
}
PlayStationMouse::~PlayStationMouse() = default;
@@ -22,16 +24,6 @@ ControllerType PlayStationMouse::GetType() const
return ControllerType::PlayStationMouse;
}
std::optional<s32> PlayStationMouse::GetAxisCodeByName(std::string_view axis_name) const
{
return StaticGetAxisCodeByName(axis_name);
}
std::optional<s32> PlayStationMouse::GetButtonCodeByName(std::string_view button_name) const
{
return StaticGetButtonCodeByName(button_name);
}
void PlayStationMouse::Reset()
{
m_transfer_state = TransferState::Idle;
@@ -59,30 +51,24 @@ bool PlayStationMouse::DoState(StateWrapper& sw, bool apply_input_state)
return true;
}
bool PlayStationMouse::GetButtonState(s32 button_code) const
float PlayStationMouse::GetBindState(u32 index) const
{
if (button_code < 0 || button_code >= static_cast<s32>(Button::Count))
return false;
if (index >= s_button_indices.size())
return 0.0f;
const u16 bit = u16(1) << static_cast<u8>(button_code);
return ((m_button_state & bit) == 0);
const u32 bit = s_button_indices[index];
return static_cast<float>(((m_button_state >> bit) & 1u) ^ 1u);
}
void PlayStationMouse::SetButtonState(Button button, bool pressed)
void PlayStationMouse::SetBindState(u32 index, float value)
{
static constexpr std::array<u8, static_cast<size_t>(Button::Count)> indices = {{11, 10}};
if (pressed)
m_button_state &= ~(u16(1) << indices[static_cast<u8>(button)]);
else
m_button_state |= u16(1) << indices[static_cast<u8>(button)];
}
void PlayStationMouse::SetButtonState(s32 button_code, bool pressed)
{
if (button_code < 0 || button_code >= static_cast<s32>(Button::Count))
if (index > s_button_indices.size())
return;
SetButtonState(static_cast<Button>(button_code), pressed);
if (value >= 0.5f)
m_button_state &= ~(u16(1) << s_button_indices[index]);
else
m_button_state |= u16(1) << s_button_indices[index];
}
void PlayStationMouse::ResetTransferState()
@@ -168,9 +154,8 @@ bool PlayStationMouse::Transfer(const u8 data_in, u8* data_out)
void PlayStationMouse::UpdatePosition()
{
// get screen coordinates
const HostDisplay* display = g_host_interface->GetDisplay();
const s32 mouse_x = display->GetMousePositionX();
const s32 mouse_y = display->GetMousePositionY();
const s32 mouse_x = g_host_display->GetMousePositionX();
const s32 mouse_y = g_host_display->GetMousePositionY();
const s32 delta_x = mouse_x - m_last_host_position_x;
const s32 delta_y = mouse_y - m_last_host_position_y;
m_last_host_position_x = mouse_x;
@@ -183,63 +168,42 @@ void PlayStationMouse::UpdatePosition()
m_delta_y = static_cast<s8>(std::clamp<s32>(delta_y, std::numeric_limits<s8>::min(), std::numeric_limits<s8>::max()));
}
std::unique_ptr<PlayStationMouse> PlayStationMouse::Create()
std::unique_ptr<PlayStationMouse> PlayStationMouse::Create(u32 index)
{
return std::make_unique<PlayStationMouse>();
return std::make_unique<PlayStationMouse>(index);
}
std::optional<s32> PlayStationMouse::StaticGetAxisCodeByName(std::string_view button_name)
{
return std::nullopt;
}
std::optional<s32> PlayStationMouse::StaticGetButtonCodeByName(std::string_view button_name)
{
#define BUTTON(name) \
if (button_name == #name) \
static const Controller::ControllerBindingInfo s_binding_info[] = {
#define BUTTON(name, display_name, button, genb) \
{ \
return static_cast<s32>(ZeroExtend32(static_cast<u8>(Button::name))); \
name, display_name, static_cast<u32>(button), Controller::ControllerBindingType::Button, genb \
}
BUTTON(Left);
BUTTON(Right);
return std::nullopt;
BUTTON("Left", "Left Button", PlayStationMouse::Button::Left, GenericInputBinding::Cross),
BUTTON("Right", "Right Button", PlayStationMouse::Button::Right, GenericInputBinding::Circle),
#undef BUTTON
}
};
Controller::AxisList PlayStationMouse::StaticGetAxisNames()
static const SettingInfo s_settings[] = {
{SettingInfo::Type::Boolean, "RelativeMouseMode", TRANSLATABLE("PlayStationMouse", "Relative Mouse Mode"),
TRANSLATABLE("PlayStationMouse", "Locks the mouse cursor to the window, use for FPS games."), "false"},
};
const Controller::ControllerInfo PlayStationMouse::INFO = {ControllerType::PlayStationMouse,
"PlayStationMouse",
TRANSLATABLE("ControllerType", "PlayStation Mouse"),
s_binding_info,
countof(s_binding_info),
s_settings,
countof(s_settings),
Controller::VibrationCapabilities::NoVibration};
void PlayStationMouse::LoadSettings(SettingsInterface& si, const char* section)
{
return {};
}
Controller::LoadSettings(si, section);
Controller::ButtonList PlayStationMouse::StaticGetButtonNames()
{
return {{TRANSLATABLE("PlayStationMouse", "Left"), static_cast<s32>(Button::Left)},
{TRANSLATABLE("PlayStationMouse", "Right"), static_cast<s32>(Button::Right)}};
}
u32 PlayStationMouse::StaticGetVibrationMotorCount()
{
return 0;
}
Controller::SettingList PlayStationMouse::StaticGetSettings()
{
static constexpr std::array<SettingInfo, 1> settings = {{
{SettingInfo::Type::Boolean, "RelativeMouseMode", TRANSLATABLE("PlayStationMouse", "Relative Mouse Mode"),
TRANSLATABLE("PlayStationMouse", "Locks the mouse cursor to the window, use for FPS games."), "false"},
}};
return SettingList(settings.begin(), settings.end());
}
void PlayStationMouse::LoadSettings(const char* section)
{
Controller::LoadSettings(section);
m_use_relative_mode = g_host_interface->GetBoolSettingValue(section, "RelativeMouseMode");
m_use_relative_mode = si.GetBoolValue(section, "RelativeMouseMode", false);
}
bool PlayStationMouse::GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode)
+7 -15
View File
@@ -14,33 +14,25 @@ public:
Count
};
PlayStationMouse();
static const Controller::ControllerInfo INFO;
PlayStationMouse(u32 index);
~PlayStationMouse() override;
static std::unique_ptr<PlayStationMouse> Create();
static std::optional<s32> StaticGetAxisCodeByName(std::string_view button_name);
static std::optional<s32> StaticGetButtonCodeByName(std::string_view button_name);
static AxisList StaticGetAxisNames();
static ButtonList StaticGetButtonNames();
static u32 StaticGetVibrationMotorCount();
static SettingList StaticGetSettings();
static std::unique_ptr<PlayStationMouse> Create(u32 index);
ControllerType GetType() const override;
std::optional<s32> GetAxisCodeByName(std::string_view axis_name) const override;
std::optional<s32> GetButtonCodeByName(std::string_view button_name) const override;
void Reset() override;
bool DoState(StateWrapper& sw, bool apply_input_state) override;
bool GetButtonState(s32 button_code) const override;
void SetButtonState(s32 button_code, bool pressed) override;
float GetBindState(u32 index) const override;
void SetBindState(u32 index, float value) override;
void ResetTransferState() override;
bool Transfer(const u8 data_in, u8* data_out) override;
void SetButtonState(Button button, bool pressed);
void LoadSettings(const char* section) override;
void LoadSettings(SettingsInterface& si, const char* section) override;
bool GetSoftwareCursor(const Common::RGBA8Image** image, float* image_scale, bool* relative_mode) override;
private:
+305 -36
View File
@@ -1,14 +1,19 @@
#include "settings.h"
#include "achievements.h"
#include "common/assert.h"
#include "common/file_system.h"
#include "common/log.h"
#include "common/make_array.h"
#include "common/path.h"
#include "common/string_util.h"
#include "controller.h"
#include "host.h"
#include "host_display.h"
#include "host_interface.h"
#include <algorithm>
#include <array>
#include <cctype>
#include <numeric>
Log_SetChannel(Settings);
Settings g_settings;
@@ -68,7 +73,16 @@ float SettingInfo::FloatStepValue() const
return step_value ? StringUtil::FromChars<float>(step_value).value_or(fallback_value) : fallback_value;
}
Settings::Settings() = default;
Settings::Settings()
{
controller_types[0] = DEFAULT_CONTROLLER_1_TYPE;
memory_card_types[0] = DEFAULT_MEMORY_CARD_1_TYPE;
for (u32 i = 1; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
{
controller_types[i] = DEFAULT_CONTROLLER_2_TYPE;
memory_card_types[i] = DEFAULT_MEMORY_CARD_2_TYPE;
}
}
bool Settings::HasAnyPerGameMemoryCards() const
{
@@ -151,6 +165,7 @@ void Settings::Load(SettingsInterface& si)
pause_on_focus_loss = si.GetBoolValue("Main", "PauseOnFocusLoss", false);
pause_on_menu = si.GetBoolValue("Main", "PauseOnMenu", true);
save_state_on_exit = si.GetBoolValue("Main", "SaveStateOnExit", true);
create_save_state_backups = si.GetBoolValue("Main", "CreateSaveStateBackups", true);
confim_power_off = si.GetBoolValue("Main", "ConfirmPowerOff", true);
load_devices_from_save_states = si.GetBoolValue("Main", "LoadDevicesFromSaveStates", false);
apply_game_settings = si.GetBoolValue("Main", "ApplyGameSettings", true);
@@ -186,8 +201,8 @@ void Settings::Load(SettingsInterface& si)
gpu_use_thread = si.GetBoolValue("GPU", "UseThread", true);
gpu_use_software_renderer_for_readbacks = si.GetBoolValue("GPU", "UseSoftwareRendererForReadbacks", false);
gpu_threaded_presentation = si.GetBoolValue("GPU", "ThreadedPresentation", true);
gpu_true_color = si.GetBoolValue("GPU", "TrueColor", false);
gpu_scaled_dithering = si.GetBoolValue("GPU", "ScaledDithering", false);
gpu_true_color = si.GetBoolValue("GPU", "TrueColor", true);
gpu_scaled_dithering = si.GetBoolValue("GPU", "ScaledDithering", true);
gpu_texture_filter =
ParseTextureFilterName(
si.GetStringValue("GPU", "TextureFilter", GetTextureFilterName(DEFAULT_GPU_TEXTURE_FILTER)).c_str())
@@ -233,17 +248,21 @@ void Settings::Load(SettingsInterface& si)
display_post_processing = si.GetBoolValue("Display", "PostProcessing", false);
display_show_osd_messages = si.GetBoolValue("Display", "ShowOSDMessages", true);
display_show_fps = si.GetBoolValue("Display", "ShowFPS", false);
display_show_vps = si.GetBoolValue("Display", "ShowVPS", false);
display_show_speed = si.GetBoolValue("Display", "ShowSpeed", false);
display_show_resolution = si.GetBoolValue("Display", "ShowResolution", false);
display_show_cpu = si.GetBoolValue("Display", "ShowCPU", false);
display_show_status_indicators = si.GetBoolValue("Display", "ShowStatusIndicators", true);
display_show_inputs = si.GetBoolValue("Display", "ShowInputs", false);
display_show_enhancements = si.GetBoolValue("Display", "ShowEnhancements", false);
display_all_frames = si.GetBoolValue("Display", "DisplayAllFrames", false);
display_internal_resolution_screenshots = si.GetBoolValue("Display", "InternalResolutionScreenshots", false);
video_sync_enabled = si.GetBoolValue("Display", "VSync", DEFAULT_VSYNC_VALUE);
display_post_process_chain = si.GetStringValue("Display", "PostProcessChain", "");
display_max_fps = si.GetFloatValue("Display", "MaxFPS", DEFAULT_DISPLAY_MAX_FPS);
display_osd_scale = si.GetFloatValue("Display", "OSDScale", DEFAULT_OSD_SCALE);
cdrom_readahead_sectors = static_cast<u8>(si.GetIntValue("CDROM", "ReadaheadSectors", DEFAULT_CDROM_READAHEAD_SECTORS));
cdrom_readahead_sectors =
static_cast<u8>(si.GetIntValue("CDROM", "ReadaheadSectors", DEFAULT_CDROM_READAHEAD_SECTORS));
cdrom_region_check = si.GetBoolValue("CDROM", "RegionCheck", false);
cdrom_load_image_to_ram = si.GetBoolValue("CDROM", "LoadImageToRAM", false);
cdrom_mute_cd_audio = si.GetBoolValue("CDROM", "MuteCDAudio", false);
@@ -255,7 +274,7 @@ void Settings::Load(SettingsInterface& si)
.value_or(DEFAULT_AUDIO_BACKEND);
audio_output_volume = si.GetIntValue("Audio", "OutputVolume", 100);
audio_fast_forward_volume = si.GetIntValue("Audio", "FastForwardVolume", 100);
audio_buffer_size = si.GetIntValue("Audio", "BufferSize", HostInterface::DEFAULT_AUDIO_BUFFER_SIZE);
audio_buffer_size = si.GetIntValue("Audio", "BufferSize", DEFAULT_AUDIO_BUFFER_SIZE);
audio_resampling = si.GetBoolValue("Audio", "Resampling", true);
audio_output_muted = si.GetBoolValue("Audio", "OutputMuted", false);
audio_sync_enabled = si.GetBoolValue("Audio", "Sync", true);
@@ -269,18 +288,31 @@ void Settings::Load(SettingsInterface& si)
bios_patch_tty_enable = si.GetBoolValue("BIOS", "PatchTTYEnable", false);
bios_patch_fast_boot = si.GetBoolValue("BIOS", "PatchFastBoot", DEFAULT_FAST_BOOT_VALUE);
controller_types[0] =
ParseControllerTypeName(
si.GetStringValue("Controller1", "Type", GetControllerTypeName(DEFAULT_CONTROLLER_1_TYPE)).c_str())
.value_or(DEFAULT_CONTROLLER_1_TYPE);
multitap_mode =
ParseMultitapModeName(
si.GetStringValue("ControllerPorts", "MultitapMode", GetMultitapModeName(DEFAULT_MULTITAP_MODE)).c_str())
.value_or(DEFAULT_MULTITAP_MODE);
controller_types[0] = ParseControllerTypeName(si.GetStringValue(Controller::GetSettingsSection(0).c_str(), "Type",
GetControllerTypeName(DEFAULT_CONTROLLER_1_TYPE))
.c_str())
.value_or(DEFAULT_CONTROLLER_1_TYPE);
const std::array<bool, 2> mtap_enabled = {{IsPort1MultitapEnabled(), IsPort2MultitapEnabled()}};
for (u32 i = 1; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
{
controller_types[i] =
ParseControllerTypeName(si.GetStringValue(TinyString::FromFormat("Controller%u", i + 1u), "Type",
GetControllerTypeName(DEFAULT_CONTROLLER_2_TYPE))
.c_str())
.value_or(DEFAULT_CONTROLLER_2_TYPE);
// Ignore types when multitap not enabled
const auto [port, slot] = Controller::ConvertPadToPortAndSlot(i);
if (Controller::PadIsMultitapSlot(slot) && !mtap_enabled[port])
{
controller_types[i] = ControllerType::None;
continue;
}
controller_types[i] = ParseControllerTypeName(si.GetStringValue(Controller::GetSettingsSection(i).c_str(), "Type",
GetControllerTypeName(DEFAULT_CONTROLLER_2_TYPE))
.c_str())
.value_or(DEFAULT_CONTROLLER_2_TYPE);
}
memory_card_types[0] =
@@ -293,13 +325,14 @@ void Settings::Load(SettingsInterface& si)
.value_or(DEFAULT_MEMORY_CARD_2_TYPE);
memory_card_paths[0] = si.GetStringValue("MemoryCards", "Card1Path", "");
memory_card_paths[1] = si.GetStringValue("MemoryCards", "Card2Path", "");
memory_card_directory = si.GetStringValue("MemoryCards", "Directory", "");
memory_card_use_playlist_title = si.GetBoolValue("MemoryCards", "UsePlaylistTitle", true);
multitap_mode =
ParseMultitapModeName(
si.GetStringValue("ControllerPorts", "MultitapMode", GetMultitapModeName(DEFAULT_MULTITAP_MODE)).c_str())
.value_or(DEFAULT_MULTITAP_MODE);
achievements_enabled = si.GetBoolValue("Cheevos", "Enabled", false);
achievements_test_mode = si.GetBoolValue("Cheevos", "TestMode", false);
achievements_unofficial_test_mode = si.GetBoolValue("Cheevos", "UnofficialTestMode", false);
achievements_use_first_disc_from_playlist = si.GetBoolValue("Cheevos", "UseFirstDiscFromPlaylist", true);
achievements_rich_presence = si.GetBoolValue("Cheevos", "RichPresence", true);
achievements_challenge_mode = si.GetBoolValue("Cheevos", "ChallengeMode", false);
log_level = ParseLogLevelName(si.GetStringValue("Logging", "LogLevel", GetLogLevelName(DEFAULT_LOG_LEVEL)).c_str())
.value_or(DEFAULT_LOG_LEVEL);
@@ -349,6 +382,7 @@ void Settings::Save(SettingsInterface& si) const
si.SetBoolValue("Main", "PauseOnFocusLoss", pause_on_focus_loss);
si.SetBoolValue("Main", "PauseOnMenu", pause_on_menu);
si.SetBoolValue("Main", "SaveStateOnExit", save_state_on_exit);
si.SetBoolValue("Main", "CreateSaveStateBackups", create_save_state_backups);
si.SetBoolValue("Main", "ConfirmPowerOff", confim_power_off);
si.SetBoolValue("Main", "LoadDevicesFromSaveStates", load_devices_from_save_states);
si.SetBoolValue("Main", "ApplyGameSettings", apply_game_settings);
@@ -410,18 +444,21 @@ void Settings::Save(SettingsInterface& si) const
si.SetBoolValue("Display", "PostProcessing", display_post_processing);
si.SetBoolValue("Display", "ShowOSDMessages", display_show_osd_messages);
si.SetBoolValue("Display", "ShowFPS", display_show_fps);
si.SetBoolValue("Display", "ShowVPS", display_show_vps);
si.SetBoolValue("Display", "ShowSpeed", display_show_speed);
si.SetBoolValue("Display", "ShowResolution", display_show_resolution);
si.SetBoolValue("Display", "ShowCPU", display_show_cpu);
si.SetBoolValue("Display", "ShowStatusIndicators", display_show_status_indicators);
si.SetBoolValue("Display", "ShowInputs", display_show_inputs);
si.SetBoolValue("Display", "ShowEnhancements", display_show_enhancements);
si.SetBoolValue("Display", "DisplayAllFrames", display_all_frames);
si.SetBoolValue("Display", "InternalResolutionScreenshots", display_internal_resolution_screenshots);
si.SetBoolValue("Display", "VSync", video_sync_enabled);
if (display_post_process_chain.empty())
si.DeleteValue("Display", "PostProcessChain");
else
si.SetStringValue("Display", "PostProcessChain", display_post_process_chain.c_str());
si.SetFloatValue("Display", "MaxFPS", display_max_fps);
si.SetFloatValue("Display", "OSDScale", display_osd_scale);
si.SetIntValue("CDROM", "ReadaheadSectors", cdrom_readahead_sectors);
si.SetBoolValue("CDROM", "RegionCheck", cdrom_region_check);
@@ -465,14 +502,17 @@ void Settings::Save(SettingsInterface& si) const
else
si.DeleteValue("MemoryCards", "Card2Path");
if (!memory_card_directory.empty())
si.SetStringValue("MemoryCards", "Directory", memory_card_directory.c_str());
else
si.DeleteValue("MemoryCards", "Directory");
si.SetBoolValue("MemoryCards", "UsePlaylistTitle", memory_card_use_playlist_title);
si.SetStringValue("ControllerPorts", "MultitapMode", GetMultitapModeName(multitap_mode));
si.SetBoolValue("Cheevos", "Enabled", achievements_enabled);
si.SetBoolValue("Cheevos", "TestMode", achievements_test_mode);
si.SetBoolValue("Cheevos", "UnofficialTestMode", achievements_unofficial_test_mode);
si.SetBoolValue("Cheevos", "UseFirstDiscFromPlaylist", achievements_use_first_disc_from_playlist);
si.SetBoolValue("Cheevos", "RichPresence", achievements_rich_presence);
si.SetBoolValue("Cheevos", "ChallengeMode", achievements_challenge_mode);
si.SetStringValue("Logging", "LogLevel", GetLogLevelName(log_level));
si.SetStringValue("Logging", "LogFilter", log_filter.c_str());
si.SetBoolValue("Logging", "LogToConsole", log_to_console);
@@ -502,6 +542,104 @@ void Settings::Save(SettingsInterface& si) const
texture_replacements.dump_vram_write_height_threshold);
}
void Settings::FixIncompatibleSettings(bool display_osd_messages)
{
if (g_settings.disable_all_enhancements)
{
Log_WarningPrintf("All enhancements disabled by config setting.");
g_settings.cpu_overclock_enable = false;
g_settings.cpu_overclock_active = false;
g_settings.enable_8mb_ram = false;
g_settings.gpu_resolution_scale = 1;
g_settings.gpu_multisamples = 1;
g_settings.gpu_per_sample_shading = false;
g_settings.gpu_true_color = false;
g_settings.gpu_scaled_dithering = false;
g_settings.gpu_texture_filter = GPUTextureFilter::Nearest;
g_settings.gpu_disable_interlacing = false;
g_settings.gpu_force_ntsc_timings = false;
g_settings.gpu_widescreen_hack = false;
g_settings.gpu_pgxp_enable = false;
g_settings.gpu_24bit_chroma_smoothing = false;
g_settings.cdrom_read_speedup = 1;
g_settings.cdrom_seek_speedup = 1;
g_settings.cdrom_mute_cd_audio = false;
g_settings.texture_replacements.enable_vram_write_replacements = false;
g_settings.bios_patch_fast_boot = false;
g_settings.bios_patch_tty_enable = false;
}
if (g_settings.display_integer_scaling && g_settings.display_linear_filtering)
{
Log_WarningPrintf("Disabling linear filter due to integer upscaling.");
g_settings.display_linear_filtering = false;
}
if (g_settings.display_integer_scaling && g_settings.display_stretch)
{
Log_WarningPrintf("Disabling stretch due to integer upscaling.");
g_settings.display_stretch = false;
}
if (g_settings.gpu_pgxp_enable)
{
if (g_settings.gpu_renderer == GPURenderer::Software)
{
if (display_osd_messages)
{
Host::AddOSDMessage(
Host::TranslateStdString("OSDMessage", "PGXP is incompatible with the software renderer, disabling PGXP."),
10.0f);
}
g_settings.gpu_pgxp_enable = false;
}
}
#ifndef WITH_MMAP_FASTMEM
if (g_settings.cpu_fastmem_mode == CPUFastmemMode::MMap)
{
Log_WarningPrintf("mmap fastmem is not available on this platform, using LUT instead.");
g_settings.cpu_fastmem_mode = CPUFastmemMode::LUT;
}
#endif
#if defined(__ANDROID__) && defined(__arm__) && !defined(__aarch64__) && !defined(_M_ARM64)
if (g_settings.rewind_enable)
{
Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "Rewind is not supported on 32-bit ARM for Android."),
30.0f);
g_settings.rewind_enable = false;
}
#endif
// if challenge mode is enabled, disable things like rewind since they use save states
if (Achievements::ChallengeModeActive())
{
g_settings.emulation_speed =
(g_settings.emulation_speed != 0.0f) ? std::max(g_settings.emulation_speed, 1.0f) : 0.0f;
g_settings.fast_forward_speed =
(g_settings.fast_forward_speed != 0.0f) ? std::max(g_settings.fast_forward_speed, 1.0f) : 0.0f;
g_settings.turbo_speed = (g_settings.turbo_speed != 0.0f) ? std::max(g_settings.turbo_speed, 1.0f) : 0.0f;
g_settings.rewind_enable = false;
g_settings.auto_load_cheats = false;
if (g_settings.cpu_overclock_enable && g_settings.GetCPUOverclockPercent() < 100)
{
g_settings.cpu_overclock_enable = false;
g_settings.UpdateOverclockActive();
}
g_settings.debugging.enable_gdb_server = false;
g_settings.debugging.show_vram = false;
g_settings.debugging.show_gpu_state = false;
g_settings.debugging.show_cdrom_state = false;
g_settings.debugging.show_spu_state = false;
g_settings.debugging.show_timers_state = false;
g_settings.debugging.show_mdec_state = false;
g_settings.debugging.show_dma_state = false;
g_settings.debugging.dump_cpu_to_vram_copies = false;
g_settings.debugging.dump_vram_to_cpu_copies = false;
}
}
static std::array<const char*, LOGLEVEL_COUNT> s_log_level_names = {
{"None", "Error", "Warning", "Perf", "Info", "Verbose", "Dev", "Profile", "Debug", "Trace"}};
static std::array<const char*, LOGLEVEL_COUNT> s_log_level_display_names = {
@@ -655,14 +793,12 @@ const char* Settings::GetCPUFastmemModeDisplayName(CPUFastmemMode mode)
static constexpr auto s_gpu_renderer_names = make_array(
#ifdef _WIN32
"D3D11",
"D3D12",
"D3D11", "D3D12",
#endif
"Vulkan", "OpenGL", "Software");
static constexpr auto s_gpu_renderer_display_names = make_array(
#ifdef _WIN32
TRANSLATABLE("GPURenderer", "Hardware (D3D11)"),
TRANSLATABLE("GPURenderer", "Hardware (D3D12)"),
TRANSLATABLE("GPURenderer", "Hardware (D3D11)"), TRANSLATABLE("GPURenderer", "Hardware (D3D12)"),
#endif
TRANSLATABLE("GPURenderer", "Hardware (Vulkan)"), TRANSLATABLE("GPURenderer", "Hardware (OpenGL)"),
TRANSLATABLE("GPURenderer", "Software"));
@@ -812,12 +948,11 @@ float Settings::GetDisplayAspectRatioValue() const
{
case DisplayAspectRatio::MatchWindow:
{
const HostDisplay* display = g_host_interface->GetDisplay();
if (!display)
if (!g_host_display)
return s_display_aspect_ratio_values[static_cast<int>(DEFAULT_DISPLAY_ASPECT_RATIO)];
const u32 width = display->GetWindowWidth();
const u32 height = display->GetWindowHeight() - display->GetDisplayTopMargin();
const u32 width = g_host_display->GetWindowWidth();
const u32 height = g_host_display->GetWindowHeight() - g_host_display->GetDisplayTopMargin();
return static_cast<float>(width) / static_cast<float>(height);
}
@@ -887,11 +1022,11 @@ const char* Settings::GetAudioBackendDisplayName(AudioBackend backend)
}
static std::array<const char*, 7> s_controller_type_names = {
{"None", "DigitalController", "AnalogController", "AnalogJoystick", "NamcoGunCon", "PlayStationMouse", "NeGcon"}};
{"None", "DigitalController", "AnalogController", "AnalogJoystick", "GunCon", "PlayStationMouse", "NeGcon"}};
static std::array<const char*, 7> s_controller_display_names = {
{TRANSLATABLE("ControllerType", "None"), TRANSLATABLE("ControllerType", "Digital Controller"),
TRANSLATABLE("ControllerType", "Analog Controller (DualShock)"), TRANSLATABLE("ControllerType", "Analog Joystick"),
TRANSLATABLE("ControllerType", "Namco GunCon"), TRANSLATABLE("ControllerType", "PlayStation Mouse"),
TRANSLATABLE("ControllerType", "GunCon"), TRANSLATABLE("ControllerType", "PlayStation Mouse"),
TRANSLATABLE("ControllerType", "NeGcon")}};
std::optional<ControllerType> Settings::ParseControllerTypeName(const char* str)
@@ -951,6 +1086,30 @@ const char* Settings::GetMemoryCardTypeDisplayName(MemoryCardType type)
return s_memory_card_type_display_names[static_cast<int>(type)];
}
std::string Settings::GetDefaultSharedMemoryCardName(u32 slot)
{
return fmt::format("shared_card_{}.mcd", slot + 1);
}
std::string Settings::GetSharedMemoryCardPath(u32 slot) const
{
std::string ret;
if (memory_card_paths[slot].empty())
ret = Path::Combine(EmuFolders::MemoryCards, GetDefaultSharedMemoryCardName(slot));
else if (!Path::IsAbsolute(memory_card_paths[slot]))
ret = Path::Combine(EmuFolders::MemoryCards, memory_card_paths[slot]);
else
ret = memory_card_paths[slot];
return ret;
}
std::string Settings::GetGameMemoryCardPath(const char* game_code, u32 slot)
{
return Path::Combine(EmuFolders::MemoryCards, fmt::format("{}_{}.mcd", game_code, slot + 1));
}
static std::array<const char*, 4> s_multitap_enable_mode_names = {{"Disabled", "Port1Only", "Port2Only", "BothPorts"}};
static std::array<const char*, 4> s_multitap_enable_mode_display_names = {
{TRANSLATABLE("MultitapMode", "Disabled"), TRANSLATABLE("MultitapMode", "Enable on Port 1 Only"),
@@ -979,3 +1138,113 @@ const char* Settings::GetMultitapModeDisplayName(MultitapMode mode)
{
return s_multitap_enable_mode_display_names[static_cast<size_t>(mode)];
}
std::string EmuFolders::AppRoot;
std::string EmuFolders::DataRoot;
std::string EmuFolders::Bios;
std::string EmuFolders::Cache;
std::string EmuFolders::Cheats;
std::string EmuFolders::Covers;
std::string EmuFolders::Dumps;
std::string EmuFolders::GameSettings;
std::string EmuFolders::InputProfiles;
std::string EmuFolders::MemoryCards;
std::string EmuFolders::Resources;
std::string EmuFolders::SaveStates;
std::string EmuFolders::Screenshots;
std::string EmuFolders::Shaders;
std::string EmuFolders::Textures;
void EmuFolders::SetDefaults()
{
Bios = Path::Combine(DataRoot, "bios");
Cache = Path::Combine(DataRoot, "cache");
Cheats = Path::Combine(DataRoot, "cheats");
Covers = Path::Combine(DataRoot, "covers");
Dumps = Path::Combine(DataRoot, "dump");
GameSettings = Path::Combine(DataRoot, "gamesettings");
InputProfiles = Path::Combine(DataRoot, "inputprofiles");
MemoryCards = Path::Combine(DataRoot, "memcards");
SaveStates = Path::Combine(DataRoot, "savestates");
Screenshots = Path::Combine(DataRoot, "screenshots");
Shaders = Path::Combine(DataRoot, "shaders");
Textures = Path::Combine(DataRoot, "textures");
}
static std::string LoadPathFromSettings(SettingsInterface& si, const std::string& root, const char* section,
const char* name, const char* def)
{
std::string value = si.GetStringValue(section, name, def);
if (value.empty())
value = def;
if (!Path::IsAbsolute(value))
value = Path::Combine(root, value);
return value;
}
void EmuFolders::LoadConfig(SettingsInterface& si)
{
Bios = LoadPathFromSettings(si, DataRoot, "BIOS", "SearchDirectory", "bios");
Cache = LoadPathFromSettings(si, DataRoot, "Folders", "Cache", "cache");
Cheats = LoadPathFromSettings(si, DataRoot, "Folders", "Cheats", "cheats");
Covers = LoadPathFromSettings(si, DataRoot, "Folders", "Covers", "covers");
Dumps = LoadPathFromSettings(si, DataRoot, "Folders", "Dumps", "dump");
GameSettings = LoadPathFromSettings(si, DataRoot, "Folders", "GameSettings", "gamesettings");
InputProfiles = LoadPathFromSettings(si, DataRoot, "Folders", "InputProfiles", "inputprofiles");
MemoryCards = LoadPathFromSettings(si, DataRoot, "MemoryCards", "Directory", "memcards");
SaveStates = LoadPathFromSettings(si, DataRoot, "Folders", "Savestates", "savestates");
Screenshots = LoadPathFromSettings(si, DataRoot, "Folders", "Snapshots", "screenshots");
Shaders = LoadPathFromSettings(si, DataRoot, "Folders", "Snapshots", "shaders");
Textures = LoadPathFromSettings(si, DataRoot, "Folders", "Textures", "textures");
Log_DevPrintf("BIOS Directory: %s", Bios.c_str());
Log_DevPrintf("Cache Directory: %s", Cache.c_str());
Log_DevPrintf("Cheats Directory: %s", Cheats.c_str());
Log_DevPrintf("Covers Directory: %s", Covers.c_str());
Log_DevPrintf("Dumps Directory: %s", Dumps.c_str());
Log_DevPrintf("Game Settings Directory: %s", GameSettings.c_str());
Log_DevPrintf("Input Profile Directory: %s", InputProfiles.c_str());
Log_DevPrintf("MemoryCards Directory: %s", MemoryCards.c_str());
Log_DevPrintf("SaveStates Directory: %s", SaveStates.c_str());
Log_DevPrintf("Screenshots Directory: %s", Screenshots.c_str());
Log_DevPrintf("Shaders Directory: %s", Shaders.c_str());
Log_DevPrintf("Textures Directory: %s", Textures.c_str());
}
void EmuFolders::Save(SettingsInterface& si)
{
// convert back to relative
si.SetStringValue("BIOS", "SearchDirectory", Path::MakeRelative(Bios, DataRoot).c_str());
si.SetStringValue("Folders", "Cache", Path::MakeRelative(Cache, DataRoot).c_str());
si.SetStringValue("Folders", "Cheats", Path::MakeRelative(Cheats, DataRoot).c_str());
si.SetStringValue("Folders", "Covers", Path::MakeRelative(Covers, DataRoot).c_str());
si.SetStringValue("Folders", "Dumps", Path::MakeRelative(Dumps, DataRoot).c_str());
si.SetStringValue("Folders", "GameSettings", Path::MakeRelative(Dumps, GameSettings).c_str());
si.SetStringValue("Folders", "InputProfiles", Path::MakeRelative(InputProfiles, DataRoot).c_str());
si.SetStringValue("MemoryCards", "Directory", Path::MakeRelative(MemoryCards, DataRoot).c_str());
si.SetStringValue("Folders", "SaveStates", Path::MakeRelative(SaveStates, DataRoot).c_str());
si.SetStringValue("Folders", "Screenshots", Path::MakeRelative(Screenshots, DataRoot).c_str());
si.SetStringValue("Folders", "Shaders", Path::MakeRelative(Shaders, DataRoot).c_str());
si.SetStringValue("Folders", "Textures", Path::MakeRelative(Textures, DataRoot).c_str());
}
bool EmuFolders::EnsureFoldersExist()
{
bool result = FileSystem::EnsureDirectoryExists(Bios.c_str(), false);
result = FileSystem::EnsureDirectoryExists(Cache.c_str(), false) && result;
result = FileSystem::EnsureDirectoryExists(Path::Combine(Cache, "achievement_badge").c_str(), false) && result;
result = FileSystem::EnsureDirectoryExists(Path::Combine(Cache, "achievement_gameicon").c_str(), false) && result;
result = FileSystem::EnsureDirectoryExists(Cheats.c_str(), false) && result;
result = FileSystem::EnsureDirectoryExists(Covers.c_str(), false) && result;
result = FileSystem::EnsureDirectoryExists(Dumps.c_str(), false) && result;
result = FileSystem::EnsureDirectoryExists(Path::Combine(Dumps, "audio").c_str(), false) && result;
result = FileSystem::EnsureDirectoryExists(Path::Combine(Dumps, "textures").c_str(), false) && result;
result = FileSystem::EnsureDirectoryExists(GameSettings.c_str(), false) && result;
result = FileSystem::EnsureDirectoryExists(InputProfiles.c_str(), false) && result;
result = FileSystem::EnsureDirectoryExists(MemoryCards.c_str(), false) && result;
result = FileSystem::EnsureDirectoryExists(SaveStates.c_str(), false) && result;
result = FileSystem::EnsureDirectoryExists(Screenshots.c_str(), false) && result;
result = FileSystem::EnsureDirectoryExists(Shaders.c_str(), false) && result;
result = FileSystem::EnsureDirectoryExists(Textures.c_str(), false) && result;
return result;
}
+86 -28
View File
@@ -44,9 +44,9 @@ struct Settings
{
Settings();
ConsoleRegion region = ConsoleRegion::Auto;
ConsoleRegion region = DEFAULT_CONSOLE_REGION;
CPUExecutionMode cpu_execution_mode = CPUExecutionMode::Interpreter;
CPUExecutionMode cpu_execution_mode = DEFAULT_CPU_EXECUTION_MODE;
u32 cpu_overclock_numerator = 1;
u32 cpu_overclock_denominator = 1;
bool cpu_overclock_enable = false;
@@ -54,23 +54,24 @@ struct Settings
bool cpu_recompiler_memory_exceptions = false;
bool cpu_recompiler_block_linking = true;
bool cpu_recompiler_icache = false;
CPUFastmemMode cpu_fastmem_mode = CPUFastmemMode::Disabled;
CPUFastmemMode cpu_fastmem_mode = DEFAULT_CPU_FASTMEM_MODE;
float emulation_speed = 1.0f;
float fast_forward_speed = 0.0f;
float turbo_speed = 0.0f;
bool sync_to_host_refresh_rate = true;
bool sync_to_host_refresh_rate = false;
bool increase_timer_resolution = true;
bool inhibit_screensaver = false;
bool inhibit_screensaver = true;
bool start_paused = false;
bool start_fullscreen = false;
bool pause_on_focus_loss = false;
bool pause_on_menu = true;
bool save_state_on_exit = true;
bool create_save_state_backups = false;
bool confim_power_off = true;
bool load_devices_from_save_states = false;
bool apply_game_settings = true;
bool auto_load_cheats = false;
bool auto_load_cheats = true;
bool disable_all_enhancements = false;
bool rewind_enable = false;
@@ -78,7 +79,7 @@ struct Settings
u32 rewind_save_slots = 10;
u32 runahead_frames = 0;
GPURenderer gpu_renderer = GPURenderer::Software;
GPURenderer gpu_renderer = DEFAULT_GPU_RENDERER;
std::string gpu_adapter;
std::string display_post_process_chain;
u32 gpu_resolution_scale = 1;
@@ -88,10 +89,10 @@ struct Settings
bool gpu_threaded_presentation = true;
bool gpu_use_debug_device = false;
bool gpu_per_sample_shading = false;
bool gpu_true_color = false;
bool gpu_scaled_dithering = false;
GPUTextureFilter gpu_texture_filter = GPUTextureFilter::Nearest;
GPUDownsampleMode gpu_downsample_mode = GPUDownsampleMode::Disabled;
bool gpu_true_color = true;
bool gpu_scaled_dithering = true;
GPUTextureFilter gpu_texture_filter = DEFAULT_GPU_TEXTURE_FILTER;
GPUDownsampleMode gpu_downsample_mode = DEFAULT_GPU_DOWNSAMPLE_MODE;
bool gpu_disable_interlacing = true;
bool gpu_force_ntsc_timings = false;
bool gpu_widescreen_hack = false;
@@ -102,8 +103,8 @@ struct Settings
bool gpu_pgxp_cpu = false;
bool gpu_pgxp_preserve_proj_fp = false;
bool gpu_pgxp_depth_buffer = false;
DisplayCropMode display_crop_mode = DisplayCropMode::None;
DisplayAspectRatio display_aspect_ratio = DisplayAspectRatio::Auto;
DisplayCropMode display_crop_mode = DEFAULT_DISPLAY_CROP_MODE;
DisplayAspectRatio display_aspect_ratio = DEFAULT_DISPLAY_ASPECT_RATIO;
u16 display_aspect_ratio_custom_numerator = 0;
u16 display_aspect_ratio_custom_denominator = 0;
s16 display_active_start_offset = 0;
@@ -118,16 +119,19 @@ struct Settings
bool display_post_processing = false;
bool display_show_osd_messages = true;
bool display_show_fps = false;
bool display_show_vps = false;
bool display_show_speed = false;
bool display_show_resolution = false;
bool display_show_cpu = false;
bool display_show_status_indicators = true;
bool display_show_inputs = false;
bool display_show_enhancements = false;
bool display_all_frames = false;
bool display_internal_resolution_screenshots = false;
bool video_sync_enabled = DEFAULT_VSYNC_VALUE;
float display_osd_scale = 1.0f;
float display_max_fps = DEFAULT_DISPLAY_MAX_FPS;
float gpu_pgxp_tolerance = -1.0f;
float gpu_pgxp_depth_clear_threshold = 300.0f / 4096.0f;
float gpu_pgxp_depth_clear_threshold = DEFAULT_GPU_PGXP_DEPTH_THRESHOLD;
u8 cdrom_readahead_sectors = DEFAULT_CDROM_READAHEAD_SECTORS;
bool cdrom_region_check = false;
@@ -136,20 +140,30 @@ struct Settings
u32 cdrom_read_speedup = 1;
u32 cdrom_seek_speedup = 1;
AudioBackend audio_backend = AudioBackend::Cubeb;
AudioBackend audio_backend = DEFAULT_AUDIO_BACKEND;
s32 audio_output_volume = 100;
s32 audio_fast_forward_volume = 100;
u32 audio_buffer_size = 2048;
bool audio_resampling = false;
u32 audio_buffer_size = DEFAULT_AUDIO_BUFFER_SIZE;
bool audio_resampling = true;
bool audio_output_muted = false;
bool audio_sync_enabled = true;
bool audio_dump_on_boot = true;
bool audio_dump_on_boot = false;
// timing hacks section
TickCount dma_max_slice_ticks = 1000;
TickCount dma_halt_ticks = 100;
u32 gpu_fifo_size = 128;
TickCount gpu_max_run_ahead = 128;
TickCount dma_max_slice_ticks = DEFAULT_DMA_MAX_SLICE_TICKS;
TickCount dma_halt_ticks = DEFAULT_DMA_HALT_TICKS;
u32 gpu_fifo_size = DEFAULT_GPU_FIFO_SIZE;
TickCount gpu_max_run_ahead = DEFAULT_GPU_MAX_RUN_AHEAD;
#ifdef WITH_CHEEVOS
// achievements
bool achievements_enabled : 1;
bool achievements_test_mode : 1;
bool achievements_unofficial_test_mode : 1;
bool achievements_use_first_disc_from_playlist : 1;
bool achievements_rich_presence : 1;
bool achievements_challenge_mode : 1;
#endif
struct DebugSettings
{
@@ -191,7 +205,7 @@ struct Settings
// TODO: Controllers, memory cards, etc.
bool bios_patch_tty_enable = false;
bool bios_patch_fast_boot = false;
bool bios_patch_fast_boot = DEFAULT_FAST_BOOT_VALUE;
bool enable_8mb_ram = false;
std::array<ControllerType, NUM_CONTROLLER_AND_CARD_PORTS> controller_types{};
@@ -199,16 +213,15 @@ struct Settings
std::array<MemoryCardType, NUM_CONTROLLER_AND_CARD_PORTS> memory_card_types{};
std::array<std::string, NUM_CONTROLLER_AND_CARD_PORTS> memory_card_paths{};
std::string memory_card_directory;
bool memory_card_use_playlist_title = true;
MultitapMode multitap_mode = MultitapMode::Disabled;
MultitapMode multitap_mode = DEFAULT_MULTITAP_MODE;
std::array<TinyString, NUM_CONTROLLER_AND_CARD_PORTS> GeneratePortLabels() const;
LOGLEVEL log_level = LOGLEVEL_INFO;
LOGLEVEL log_level = DEFAULT_LOG_LEVEL;
std::string log_filter;
bool log_to_console = false;
bool log_to_console = DEFAULT_LOG_TO_CONSOLE;
bool log_to_debug = false;
bool log_to_window = false;
bool log_to_file = false;
@@ -241,6 +254,15 @@ struct Settings
float GetDisplayAspectRatioValue() const;
ALWAYS_INLINE bool IsPort1MultitapEnabled() const
{
return (multitap_mode == MultitapMode::Port1Only || multitap_mode == MultitapMode::BothPorts);
}
ALWAYS_INLINE bool IsPort2MultitapEnabled() const
{
return (multitap_mode == MultitapMode::Port1Only || multitap_mode == MultitapMode::BothPorts);
}
ALWAYS_INLINE static bool IsPerGameMemoryCardType(MemoryCardType type)
{
return (type == MemoryCardType::PerGame || type == MemoryCardType::PerGameTitle ||
@@ -248,6 +270,13 @@ struct Settings
}
bool HasAnyPerGameMemoryCards() const;
/// Returns the default path to a memory card.
static std::string GetDefaultSharedMemoryCardName(u32 slot);
std::string GetSharedMemoryCardPath(u32 slot) const;
/// Returns the default path to a memory card for a specific game.
static std::string GetGameMemoryCardPath(const char* game_code, u32 slot);
static void CPUOverclockPercentToFraction(u32 percent, u32* numerator, u32* denominator);
static u32 CPUOverclockFractionToPercent(u32 numerator, u32 denominator);
@@ -268,6 +297,8 @@ struct Settings
void Load(SettingsInterface& si);
void Save(SettingsInterface& si) const;
void FixIncompatibleSettings(bool display_osd_messages);
static std::optional<LOGLEVEL> ParseLogLevelName(const char* str);
static const char* GetLogLevelName(LOGLEVEL level);
static const char* GetLogLevelDisplayName(LOGLEVEL level);
@@ -356,6 +387,7 @@ struct Settings
static constexpr DisplayCropMode DEFAULT_DISPLAY_CROP_MODE = DisplayCropMode::Overscan;
static constexpr DisplayAspectRatio DEFAULT_DISPLAY_ASPECT_RATIO = DisplayAspectRatio::Auto;
static constexpr float DEFAULT_OSD_SCALE = 100.0f;
static constexpr u8 DEFAULT_CDROM_READAHEAD_SECTORS = 8;
@@ -367,6 +399,8 @@ struct Settings
static constexpr LOGLEVEL DEFAULT_LOG_LEVEL = LOGLEVEL_INFO;
static constexpr u32 DEFAULT_AUDIO_BUFFER_SIZE = 2048;
// Enable console logging by default on Linux platforms.
#if defined(__linux__) && !defined(__ANDROID__)
static constexpr bool DEFAULT_LOG_TO_CONSOLE = true;
@@ -387,3 +421,27 @@ struct Settings
};
extern Settings g_settings;
namespace EmuFolders {
extern std::string AppRoot;
extern std::string DataRoot;
extern std::string Bios;
extern std::string Cache;
extern std::string Cheats;
extern std::string Covers;
extern std::string Dumps;
extern std::string GameSettings;
extern std::string InputProfiles;
extern std::string MemoryCards;
extern std::string Resources;
extern std::string SaveStates;
extern std::string Screenshots;
extern std::string Shaders;
extern std::string Textures;
// Assumes that AppRoot and DataRoot have been initialized.
void SetDefaults();
bool EnsureFoldersExist();
void LoadConfig(SettingsInterface& si);
void Save(SettingsInterface& si);
} // namespace EmuFolders
-1
View File
@@ -1,7 +1,6 @@
#include "sio.h"
#include "common/log.h"
#include "controller.h"
#include "host_interface.h"
#include "interrupt_controller.h"
#include "memory_card.h"
#include "util/state_wrapper.h"
+35 -5
View File
@@ -3,7 +3,7 @@
#include "common/file_system.h"
#include "common/log.h"
#include "dma.h"
#include "host_interface.h"
#include "host.h"
#include "imgui.h"
#include "interrupt_controller.h"
#include "system.h"
@@ -30,11 +30,39 @@ void SPU::Initialize()
"SPU Transfer", TRANSFER_TICKS_PER_HALFWORD, TRANSFER_TICKS_PER_HALFWORD,
[](void* param, TickCount ticks, TickCount ticks_late) { static_cast<SPU*>(param)->ExecuteTransfer(ticks); }, this,
false);
m_audio_stream = g_host_interface->GetAudioStream();
m_null_audio_stream = AudioStream::CreateNullAudioStream();
m_null_audio_stream->Reconfigure(SAMPLE_RATE, SAMPLE_RATE, NUM_CHANNELS, Settings::DEFAULT_AUDIO_BUFFER_SIZE);
CreateOutputStream();
Reset();
}
void SPU::CreateOutputStream()
{
Log_InfoPrintf("Creating '%s' audio stream, sample rate = %u, channels = %u, buffer size = %u",
Settings::GetAudioBackendName(g_settings.audio_backend), SAMPLE_RATE, NUM_CHANNELS,
g_settings.audio_buffer_size);
m_audio_stream = Host::CreateAudioStream(g_settings.audio_backend);
if (!m_audio_stream ||
!m_audio_stream->Reconfigure(SAMPLE_RATE, SAMPLE_RATE, NUM_CHANNELS, g_settings.audio_buffer_size))
{
Host::ReportErrorAsync("Error", "Failed to create or configure audio stream, falling back to null output.");
m_audio_stream.reset();
m_audio_stream = AudioStream::CreateNullAudioStream();
m_audio_stream->Reconfigure(SAMPLE_RATE, SAMPLE_RATE, NUM_CHANNELS, g_settings.audio_buffer_size);
}
m_audio_stream->SetOutputVolume(System::GetAudioOutputVolume());
}
void SPU::RecreateOutputStream()
{
m_audio_stream.reset();
CreateOutputStream();
}
void SPU::CPUClockChanged()
{
// (X * D) / N / 768 -> (X * D) / (N * 768)
@@ -1781,11 +1809,13 @@ void SPU::Execute(TickCount ticks)
m_ticks_carry = (ticks + m_ticks_carry) % SYSCLK_TICKS_PER_SPU_TICK;
}
AudioStream* output_stream = m_audio_output_muted ? m_null_audio_stream.get() : m_audio_stream.get();
while (remaining_frames > 0)
{
s16* output_frame_start;
u32 output_frame_space = remaining_frames;
m_audio_stream->BeginWrite(&output_frame_start, &output_frame_space);
output_stream->BeginWrite(&output_frame_start, &output_frame_space);
s16* output_frame = output_frame_start;
const u32 frames_in_this_batch = std::min(remaining_frames, output_frame_space);
@@ -1888,7 +1918,7 @@ void SPU::Execute(TickCount ticks)
if (m_dump_writer)
m_dump_writer->WriteFrames(output_frame_start, frames_in_this_batch);
m_audio_stream->EndWrite(frames_in_this_batch);
output_stream->EndWrite(frames_in_this_batch);
remaining_frames -= frames_in_this_batch;
}
}
@@ -1898,7 +1928,7 @@ void SPU::UpdateEventInterval()
// Don't generate more than the audio buffer since in a single slice, otherwise we'll both overflow the buffers when
// we do write it, and the audio thread will underflow since it won't have enough data it the game isn't messing with
// the SPU state.
const u32 max_slice_frames = g_host_interface->GetAudioStream()->GetBufferSize();
const u32 max_slice_frames = m_audio_stream->GetBufferSize();
// TODO: Make this predict how long until the interrupt will be hit instead...
const u32 interval = (m_SPUCNT.enable && m_SPUCNT.irq9_enable) ? 1 : max_slice_frames;
+16 -3
View File
@@ -11,6 +11,8 @@
class StateWrapper;
class AudioStream;
namespace Common {
class WAVWriter;
}
@@ -24,6 +26,7 @@ public:
{
RAM_SIZE = 512 * 1024,
RAM_MASK = RAM_SIZE - 1,
SAMPLE_RATE = 44100,
};
SPU();
@@ -61,16 +64,21 @@ public:
std::array<u8, RAM_SIZE>& GetRAM() { return m_ram; }
/// Change output stream - used for runahead.
ALWAYS_INLINE void SetAudioStream(AudioStream* stream) { m_audio_stream = stream; }
// TODO: Make it use system "running ahead" flag
ALWAYS_INLINE bool IsAudioOutputMuted() const { return m_audio_output_muted; }
void SetAudioOutputMuted(bool muted) { m_audio_output_muted = muted; }
ALWAYS_INLINE AudioStream* GetOutputStream() const { return m_audio_stream.get(); }
void RecreateOutputStream();
private:
static constexpr u32 SPU_BASE = 0x1F801C00;
static constexpr u32 NUM_CHANNELS = 2;
static constexpr u32 NUM_VOICES = 24;
static constexpr u32 NUM_VOICE_REGISTERS = 8;
static constexpr u32 VOICE_ADDRESS_SHIFT = 3;
static constexpr u32 NUM_SAMPLES_PER_ADPCM_BLOCK = 28;
static constexpr u32 NUM_SAMPLES_FROM_LAST_ADPCM_BLOCK = 3;
static constexpr u32 SAMPLE_RATE = 44100;
static constexpr u32 SYSCLK_TICKS_PER_SPU_TICK = System::MASTER_CLOCK / SAMPLE_RATE; // 0x300
static constexpr s16 ENVELOPE_MIN_VOLUME = 0;
static constexpr s16 ENVELOPE_MAX_VOLUME = 0x7FFF;
@@ -376,10 +384,15 @@ private:
void UpdateTransferEvent();
void UpdateDMARequest();
void CreateOutputStream();
std::unique_ptr<TimingEvent> m_tick_event;
std::unique_ptr<TimingEvent> m_transfer_event;
std::unique_ptr<Common::WAVWriter> m_dump_writer;
AudioStream* m_audio_stream = nullptr;
std::unique_ptr<AudioStream> m_audio_stream;
std::unique_ptr<AudioStream> m_null_audio_stream;
bool m_audio_output_muted = false;
TickCount m_ticks_carry = 0;
TickCount m_cpu_ticks_per_spu_tick = 0;
TickCount m_cpu_tick_divider = 0;
+1969 -341
View File
File diff suppressed because it is too large Load Diff
+245 -16
View File
@@ -1,6 +1,5 @@
#pragma once
#include "common/timer.h"
#include "host_interface.h"
#include "settings.h"
#include "timing_event.h"
#include "types.h"
@@ -20,26 +19,50 @@ class CheatList;
struct SystemBootParameters
{
SystemBootParameters();
SystemBootParameters(SystemBootParameters&& other);
SystemBootParameters(const SystemBootParameters&);
SystemBootParameters(SystemBootParameters&&);
SystemBootParameters(std::string filename_);
~SystemBootParameters();
std::string filename;
std::string save_state;
std::optional<bool> override_fast_boot;
std::optional<bool> override_fullscreen;
std::optional<bool> override_start_paused;
std::unique_ptr<ByteStream> state_stream;
u32 media_playlist_index = 0;
bool load_image_to_ram = false;
bool force_software_renderer = false;
};
struct SaveStateInfo
{
std::string path;
std::time_t timestamp;
s32 slot;
bool global;
};
struct ExtendedSaveStateInfo
{
std::string title;
std::string game_code;
std::string media_path;
std::time_t timestamp;
u32 screenshot_width;
u32 screenshot_height;
std::vector<u32> screenshot_data;
};
namespace System {
enum : u32
{
// 5 megabytes is sufficient for now, at the moment they're around 4.3MB, or 10.3MB with 8MB RAM enabled.
MAX_SAVE_STATE_SIZE = 11 * 1024 * 1024
MAX_SAVE_STATE_SIZE = 11 * 1024 * 1024,
PER_GAME_SAVE_STATE_SLOTS = 10,
GLOBAL_SAVE_STATE_SLOTS = 10
};
enum : TickCount
@@ -58,13 +81,16 @@ enum class State
extern TickCount g_ticks_per_second;
/// Returns true if the filename is a PlayStation executable we can inject.
bool IsExeFileName(const char* path);
bool IsExeFileName(const std::string_view& path);
/// Returns true if the filename is a Portable Sound Format file we can uncompress/load.
bool IsPsfFileName(const char* path);
bool IsPsfFileName(const std::string_view& path);
/// Returns true if the filename is one we can load.
bool IsLoadableFilename(const char* path);
bool IsLoadableFilename(const std::string_view& path);
/// Returns true if the filename is a save state.
bool IsSaveStateFilename(const std::string_view& path);
/// Returns the preferred console type for a disc.
ConsoleRegion GetConsoleRegionForDiscRegion(DiscRegion region);
@@ -82,6 +108,12 @@ DiscRegion GetRegionForExe(const char* path);
DiscRegion GetRegionForPsf(const char* path);
std::optional<DiscRegion> GetRegionForPath(const char* image_path);
/// Returns the path for the game settings ini file for the specified serial.
std::string GetGameSettingsPath(const std::string_view& game_serial);
/// Returns the path for the input profile ini file with the specified name (may not exist).
std::string GetInputProfilePath(const std::string_view& name);
State GetState();
void SetState(State new_state);
bool IsRunning();
@@ -93,6 +125,7 @@ bool IsStartupCancelled();
void CancelPendingStartup();
ConsoleRegion GetRegion();
DiscRegion GetDiscRegion();
bool IsPALRegion();
ALWAYS_INLINE TickCount GetTicksPerSecond()
@@ -137,22 +170,44 @@ const std::string& GetRunningPath();
const std::string& GetRunningCode();
const std::string& GetRunningTitle();
// TODO: Move to PerformanceMetrics
float GetFPS();
float GetVPS();
float GetEmulationSpeed();
float GetAverageFrameTime();
float GetWorstFrameTime();
float GetThrottleFrequency();
float GetCPUThreadUsage();
float GetCPUThreadAverageTime();
float GetSWThreadUsage();
float GetSWThreadAverageTime();
bool Boot(const SystemBootParameters& params);
void Reset();
void Shutdown();
/// Loads global settings (i.e. EmuConfig).
void LoadSettings(bool display_osd_messages);
void SetDefaultSettings(SettingsInterface& si);
bool LoadState(ByteStream* state, bool update_display = true);
bool SaveState(ByteStream* state, u32 screenshot_size = 256);
/// Reloads settings, and applies any changes present.
void ApplySettings(bool display_osd_messages);
/// Reloads game specific settings, and applys any changes present.
bool ReloadGameSettings(bool display_osd_messages);
bool BootSystem(SystemBootParameters parameters);
void PauseSystem(bool paused);
void ResetSystem();
/// Loads state from the specified filename.
bool LoadState(const char* filename);
bool SaveState(const char* filename, bool backup_existing_save);
/// Runs the VM until the CPU execution is canceled.
void Execute();
/// Switches the GPU renderer by saving state, recreating the display window, and restoring state (if needed).
void RecreateSystem();
/// Recreates the GPU component, saving/loading the state so it is preserved. Call when the GPU renderer changes.
bool RecreateGPU(GPURenderer renderer, bool update_display = true);
bool RecreateGPU(GPURenderer renderer, bool force_recreate_display = false, bool update_display = true);
void SingleStepCPU();
void RunFrame();
@@ -160,7 +215,6 @@ void RunFrames();
/// Sets target emulation speed.
float GetTargetSpeed();
void SetTargetSpeed(float speed);
/// Adjusts the throttle frequency, i.e. how many times we should sleep per second.
void SetThrottleFrequency(float frequency);
@@ -183,7 +237,10 @@ void ResetControllers();
void UpdateMemoryCardTypes();
void UpdatePerGameMemoryCards();
bool HasMemoryCard(u32 slot);
/// Swaps memory cards in slot 1/2.
void SwapMemoryCards();
void UpdateMultitaps();
/// Dumps RAM to a file.
@@ -230,6 +287,132 @@ void ApplyCheatCode(const CheatCode& code);
/// Sets or clears the provided cheat list, applying every frame.
void SetCheatList(std::unique_ptr<CheatList> cheats);
/// Checks for settings changes, std::move() the old settings away for comparing beforehand.
void CheckForSettingsChanges(const Settings& old_settings);
/// Updates throttler.
void UpdateSpeedLimiterState();
/// Toggles fast forward state.
bool IsFastForwardEnabled();
void SetFastForwardEnabled(bool enabled);
/// Toggles turbo state.
bool IsTurboEnabled();
void SetTurboEnabled(bool enabled);
/// Toggles rewind state.
bool IsRewinding();
void SetRewindState(bool enabled);
void DoFrameStep();
void DoToggleCheats();
/// Returns the path to a save state file. Specifying an index of -1 is the "resume" save state.
std::string GetGameSaveStateFileName(const std::string_view& game_code, s32 slot);
/// Returns the path to a save state file. Specifying an index of -1 is the "resume" save state.
std::string GetGlobalSaveStateFileName(s32 slot);
/// Returns the most recent resume save state.
std::string GetMostRecentResumeSaveStatePath();
/// Returns the path to the cheat file for the specified game title.
std::string GetCheatFileName();
/// Powers off the system, optionally saving the resume state.
void ShutdownSystem(bool save_resume_state);
/// Returns true if an undo load state exists.
bool CanUndoLoadState();
/// Returns save state info for the undo slot, if present.
std::optional<ExtendedSaveStateInfo> GetUndoSaveStateInfo();
/// Undoes a load state, i.e. restores the state prior to the load.
bool UndoLoadState();
/// Returns a list of save states for the specified game code.
std::vector<SaveStateInfo> GetAvailableSaveStates(const char* game_code);
/// Returns save state info if present. If game_code is null or empty, assumes global state.
std::optional<SaveStateInfo> GetSaveStateInfo(const char* game_code, s32 slot);
/// Returns save state info from opened save state stream.
std::optional<ExtendedSaveStateInfo> GetExtendedSaveStateInfo(const char* path);
/// Deletes save states for the specified game code. If resume is set, the resume state is deleted too.
void DeleteSaveStates(const char* game_code, bool resume);
/// Returns intended output volume considering fast forwarding.
s32 GetAudioOutputVolume();
void UpdateVolume();
/// Returns true if currently dumping audio.
bool IsDumpingAudio();
/// Starts dumping audio to a file. If no file name is provided, one will be generated automatically.
bool StartDumpingAudio(const char* filename = nullptr);
/// Stops dumping audio to file if it has been started.
void StopDumpingAudio();
/// Saves a screenshot to the specified file. IF no file name is provided, one will be generated automatically.
bool SaveScreenshot(const char* filename = nullptr, bool full_resolution = true, bool apply_aspect_ratio = true,
bool compress_on_thread = true);
/// Loads the cheat list from the specified file.
bool LoadCheatList(const char* filename);
/// Loads the cheat list for the current game title from the user directory.
bool LoadCheatListFromGameTitle();
/// Loads the cheat list for the current game code from the built-in code database.
bool LoadCheatListFromDatabase();
/// Saves the current cheat list to the game title's file.
bool SaveCheatList();
/// Saves the current cheat list to the specified file.
bool SaveCheatList(const char* filename);
/// Deletes the cheat list, if present.
bool DeleteCheatList();
/// Removes all cheats from the cheat list.
void ClearCheatList(bool save_to_file);
/// Enables/disabled the specified cheat code.
void SetCheatCodeState(u32 index, bool enabled, bool save_to_file);
/// Immediately applies the specified cheat code.
void ApplyCheatCode(u32 index);
/// Temporarily toggles post-processing on/off.
void TogglePostProcessing();
/// Reloads post processing shaders with the current configuration.
void ReloadPostProcessingShaders();
/// Toggle Widescreen Hack and Aspect Ratio
void ToggleWidescreen();
/// Returns true if fast forwarding or slow motion is currently active.
bool IsRunningAtNonStandardSpeed();
/// Quick switch between software and hardware rendering.
void ToggleSoftwareRendering();
/// Updates software cursor state, based on controllers.
void UpdateSoftwareCursor();
/// Resizes the render window to the display size, with an optional scale.
/// If the scale is set to 0, the internal resolution will be used, otherwise it is treated as a multiplier to 1x.
void RequestDisplaySize(float scale = 0.0f);
/// Call when host display size changes, use with "match display" aspect ratio setting.
void HostDisplayResized();
//////////////////////////////////////////////////////////////////////////
// Memory Save States (Rewind and Runahead)
//////////////////////////////////////////////////////////////////////////
@@ -237,8 +420,54 @@ void CalculateRewindMemoryUsage(u32 num_saves, u64* ram_usage, u64* vram_usage);
void ClearMemorySaveStates();
void UpdateMemorySaveStateSettings();
bool LoadRewindState(u32 skip_saves = 0, bool consume_state = true);
bool IsRewinding();
void SetRewinding(bool enabled);
void SetRunaheadReplayFlag();
} // namespace System
namespace Host {
/// Called with the settings lock held, when system settings are being loaded (should load input sources, etc).
void LoadSettings(SettingsInterface& si, std::unique_lock<std::mutex>& lock);
/// Called after settings are updated.
void CheckForSettingsChanges(const Settings& old_settings);
/// Called when the VM is starting initialization, but has not been completed yet.
void OnSystemStarting();
/// Called when the VM is created.
void OnSystemStarted();
/// Called when the VM is shut down or destroyed.
void OnSystemDestroyed();
/// Called when the VM is paused.
void OnSystemPaused();
/// Called when the VM is resumed after being paused.
void OnSystemResumed();
/// Called when performance metrics are updated, approximately once a second.
void OnPerformanceCountersUpdated();
/// Provided by the host; called when the running executable changes.
void OnGameChanged(const std::string& disc_path, const std::string& game_serial, const std::string& game_name);
/// Provided by the host; called once per frame at guest vsync.
void PumpMessagesOnCPUThread();
/// Requests a specific display window size.
void RequestResizeHostDisplay(s32 width, s32 height);
/// Requests shut down and exit of the hosting application. This may not actually exit,
/// if the user cancels the shutdown confirmation.
void RequestExit(bool save_state_if_running);
/// Requests shut down of the current virtual machine.
void RequestSystemShutdown(bool allow_confirm, bool allow_save_state);
/// Returns true if the hosting application is currently fullscreen.
bool IsFullscreen();
/// Alters fullscreen state of hosting application.
void SetFullscreen(bool enabled);
} // namespace Host
+14 -12
View File
@@ -1,10 +1,12 @@
#include "texture_replacements.h"
#include "common/file_system.h"
#include "common/log.h"
#include "common/path.h"
#include "common/platform.h"
#include "common/string_util.h"
#include "common/timer.h"
#include "host_interface.h"
#include "fmt/format.h"
#include "host.h"
#include "settings.h"
#include "xxhash.h"
#if defined(CPU_X86) || defined(CPU_X64)
@@ -118,7 +120,12 @@ void TextureReplacements::Shutdown()
std::string TextureReplacements::GetSourceDirectory() const
{
return g_host_interface->GetUserDirectoryRelativePath("textures/%s", m_game_id.c_str());
return Path::Combine(EmuFolders::Textures, m_game_id);
}
std::string TextureReplacements::GetDumpDirectory() const
{
return Path::Combine(EmuFolders::Dumps, Path::Combine("textures", m_game_id));
}
TextureReplacementHash TextureReplacements::GetVRAMWriteHash(u32 width, u32 height, const void* pixels) const
@@ -133,19 +140,14 @@ std::string TextureReplacements::GetVRAMWriteDumpFilename(u32 width, u32 height,
return {};
const TextureReplacementHash hash = GetVRAMWriteHash(width, height, pixels);
std::string filename = g_host_interface->GetUserDirectoryRelativePath("dump/textures/%s/vram-write-%s.png",
m_game_id.c_str(), hash.ToString().c_str());
const std::string dump_directory(GetDumpDirectory());
std::string filename(Path::Combine(dump_directory, fmt::format("vram-write-{}.png", hash.ToString())));
if (FileSystem::FileExists(filename.c_str()))
return {};
const std::string dump_directory =
g_host_interface->GetUserDirectoryRelativePath("dump/textures/%s", m_game_id.c_str());
if (!FileSystem::DirectoryExists(dump_directory.c_str()) &&
!FileSystem::CreateDirectory(dump_directory.c_str(), false))
{
if (!FileSystem::EnsureDirectoryExists(dump_directory.c_str(), false))
return {};
}
return filename;
}
@@ -288,8 +290,8 @@ void TextureReplacements::PreloadTextures()
#define UPDATE_PROGRESS() \
if (last_update_time.GetTimeSeconds() >= UPDATE_INTERVAL) \
{ \
g_host_interface->DisplayLoadingScreen("Preloading replacement textures...", 0, static_cast<int>(total_textures), \
static_cast<int>(num_textures_loaded)); \
Host::DisplayLoadingScreen("Preloading replacement textures...", 0, static_cast<int>(total_textures), \
static_cast<int>(num_textures_loaded)); \
last_update_time.Reset(); \
}
+1
View File
@@ -69,6 +69,7 @@ private:
ReplacmentType* replacement_type);
std::string GetSourceDirectory() const;
std::string GetDumpDirectory() const;
TextureReplacementHash GetVRAMWriteHash(u32 width, u32 height, const void* pixels) const;
std::string GetVRAMWriteDumpFilename(u32 width, u32 height, const void* pixels) const;
+1 -1
View File
@@ -126,7 +126,7 @@ enum class ControllerType
DigitalController,
AnalogController,
AnalogJoystick,
NamcoGunCon,
GunCon,
PlayStationMouse,
NeGcon,
Count