From 266eaf0ee67f26db41e625687b4eb92b3520ff5e Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Wed, 13 Jul 2022 22:49:40 +1000 Subject: [PATCH] more work --- src/core/analog_controller.cpp | 85 +- src/core/analog_controller.h | 26 +- src/core/bus.cpp | 4 +- src/core/cheats.cpp | 1 + src/core/cheevos.h | 35 + src/core/controller.cpp | 5 + src/core/controller.h | 4 + src/core/core.vcxproj | 2 + src/core/core.vcxproj.filters | 2 + src/core/cpu_core.cpp | 42 +- src/core/digital_controller.cpp | 36 +- src/core/gpu_hw.cpp | 46 +- src/core/gpu_hw_opengl.cpp | 9 +- src/core/gte.cpp | 2 +- src/core/host.cpp | 32 + src/core/host.h | 30 +- src/core/host_display.h | 29 + src/core/host_interface.cpp | 827 +------ src/core/host_interface.h | 104 - src/core/host_interface_progress_callback.cpp | 6 +- src/core/host_settings.h | 5 +- src/core/memory_card.cpp | 15 +- src/core/pad.cpp | 39 +- src/core/settings.cpp | 128 +- src/core/settings.h | 57 +- src/core/spu.cpp | 37 +- src/core/spu.h | 19 +- src/core/system.cpp | 2035 ++++++++++++++--- src/core/system.h | 275 ++- src/duckstation-qt/advancedsettingswidget.cpp | 10 +- src/duckstation-qt/audiosettingswidget.cpp | 14 +- src/duckstation-qt/autoupdaterdialog.cpp | 4 +- src/duckstation-qt/cheatmanagerdialog.cpp | 96 +- src/duckstation-qt/consolesettingswidget.cpp | 2 +- ...ntrollerbindingwidget_analog_controller.ui | 10 +- ...trollerbindingwidget_digital_controller.ui | 737 ++++++ .../controllerbindingwidgets.cpp | 74 +- src/duckstation-qt/controllerbindingwidgets.h | 18 + .../controllersettingsdialog.cpp | 40 +- .../controllersettingwidgetbinder.h | 10 +- src/duckstation-qt/duckstation-qt.vcxproj | 3 + .../gamelistsearchdirectoriesmodel.cpp | 12 +- src/duckstation-qt/gamelistsettingswidget.cpp | 6 +- src/duckstation-qt/gamelistwidget.cpp | 35 +- src/duckstation-qt/inputbindingdialog.cpp | 4 +- src/duckstation-qt/inputbindingwidgets.cpp | 8 +- src/duckstation-qt/mainwindow.cpp | 110 +- src/duckstation-qt/mainwindow.h | 5 +- src/duckstation-qt/memorycardeditordialog.cpp | 1 + src/duckstation-qt/qthostinterface.cpp | 679 +++--- src/duckstation-qt/qthostinterface.h | 91 +- .../controllers/analog_controller.svg | 422 ++++ .../controllers/digital_controller.svg | 372 +++ .../resources/controllers/guncon.svg | 466 ++++ .../resources/controllers/negcon.svg | 288 +++ src/duckstation-qt/resources/generate.sh | 13 + .../resources/icons/black/index.theme | 6 + .../icons/black/svg/arrow-left-right-line.svg | 1 + .../icons/black/svg/artboard-2-line.svg | 6 + .../icons/black/svg/book-open-line.svg | 6 + .../resources/icons/black/svg/brush-line.svg | 6 + .../resources/icons/black/svg/close-line.svg | 6 + .../icons/black/svg/dashboard-line.svg | 6 + .../resources/icons/black/svg/disc-line.svg | 6 + .../icons/black/svg/door-open-line.svg | 6 + .../icons/black/svg/download-2-line.svg | 6 + .../resources/icons/black/svg/dvd-line.svg | 6 + .../resources/icons/black/svg/eject-line.svg | 6 + .../icons/black/svg/file-add-line.svg | 6 + .../resources/icons/black/svg/file-line.svg | 6 + .../icons/black/svg/file-list-line.svg | 6 + .../icons/black/svg/file-reduce-line.svg | 6 + .../icons/black/svg/file-search-line.svg | 6 + .../icons/black/svg/file-settings-line.svg | 6 + .../resources/icons/black/svg/flask-line.svg | 6 + .../icons/black/svg/folder-add-line.svg | 6 + .../icons/black/svg/folder-open-line.svg | 6 + .../icons/black/svg/folder-reduce-line.svg | 6 + .../icons/black/svg/folder-settings-line.svg | 6 + .../icons/black/svg/fullscreen-line.svg | 6 + .../icons/black/svg/function-line.svg | 6 + .../icons/black/svg/gamepad-line.svg | 6 + .../icons/black/svg/hard-drive-2-line.svg | 6 + .../icons/black/svg/keyboard-line.svg | 6 + .../icons/black/svg/layout-grid-line.svg | 6 + .../resources/icons/black/svg/list-check.svg | 6 + .../resources/icons/black/svg/pause-line.svg | 6 + .../resources/icons/black/svg/play-line.svg | 6 + .../icons/black/svg/refresh-line.svg | 6 + .../icons/black/svg/restart-line.svg | 6 + .../resources/icons/black/svg/save-3-line.svg | 6 + .../icons/black/svg/screenshot-2-line.svg | 6 + .../icons/black/svg/sd-card-line.svg | 6 + .../icons/black/svg/settings-3-line.svg | 6 + .../icons/black/svg/shut-down-line.svg | 6 + .../resources/icons/black/svg/tv-2-line.svg | 6 + .../icons/black/svg/volume-up-line.svg | 6 + .../icons/black/svg/window-2-line.svg | 6 + .../resources/icons/white/index.theme | 6 + .../icons/white/svg/arrow-left-right-line.svg | 1 + .../icons/white/svg/artboard-2-line.svg | 6 + .../icons/white/svg/book-open-line.svg | 6 + .../resources/icons/white/svg/brush-line.svg | 6 + .../resources/icons/white/svg/close-line.svg | 6 + .../icons/white/svg/dashboard-line.svg | 6 + .../resources/icons/white/svg/disc-line.svg | 6 + .../icons/white/svg/door-open-line.svg | 6 + .../icons/white/svg/download-2-line.svg | 6 + .../resources/icons/white/svg/dvd-line.svg | 6 + .../resources/icons/white/svg/eject-line.svg | 6 + .../icons/white/svg/file-add-line.svg | 6 + .../resources/icons/white/svg/file-line.svg | 6 + .../icons/white/svg/file-list-line.svg | 6 + .../icons/white/svg/file-reduce-line.svg | 6 + .../icons/white/svg/file-search-line.svg | 6 + .../icons/white/svg/file-settings-line.svg | 6 + .../resources/icons/white/svg/flask-line.svg | 6 + .../icons/white/svg/folder-add-line.svg | 6 + .../icons/white/svg/folder-open-line.svg | 6 + .../icons/white/svg/folder-reduce-line.svg | 6 + .../icons/white/svg/folder-settings-line.svg | 6 + .../icons/white/svg/fullscreen-line.svg | 6 + .../icons/white/svg/function-line.svg | 6 + .../icons/white/svg/gamepad-line.svg | 6 + .../icons/white/svg/hard-drive-2-line.svg | 6 + .../icons/white/svg/keyboard-line.svg | 6 + .../icons/white/svg/layout-grid-line.svg | 6 + .../resources/icons/white/svg/list-check.svg | 6 + .../resources/icons/white/svg/pause-line.svg | 6 + .../resources/icons/white/svg/play-line.svg | 6 + .../icons/white/svg/refresh-line.svg | 6 + .../icons/white/svg/restart-line.svg | 6 + .../resources/icons/white/svg/save-3-line.svg | 6 + .../icons/white/svg/screenshot-2-line.svg | 6 + .../icons/white/svg/sd-card-line.svg | 6 + .../icons/white/svg/settings-3-line.svg | 6 + .../icons/white/svg/shut-down-line.svg | 6 + .../resources/icons/white/svg/tv-2-line.svg | 6 + .../icons/white/svg/volume-up-line.svg | 6 + .../icons/white/svg/window-2-line.svg | 6 + src/duckstation-qt/resources/resources.qrc | 1332 ++++++----- src/duckstation-qt/settingsdialog.cpp | 18 +- src/duckstation-qt/settingwidgetbinder.h | 22 +- src/frontend-common/cheevos.cpp | 48 +- src/frontend-common/cheevos.h | 6 +- src/frontend-common/common_host_interface.cpp | 1377 +---------- src/frontend-common/common_host_interface.h | 264 +-- src/frontend-common/d3d11_host_display.cpp | 4 +- src/frontend-common/frontend-common.props | 4 +- src/frontend-common/frontend-common.vcxproj | 2 - .../frontend-common.vcxproj.filters | 2 - .../fullscreen_ui_progress_callback.cpp | 7 +- src/frontend-common/game_list.cpp | 2 +- src/frontend-common/game_settings.cpp | 61 +- src/frontend-common/host_settings.cpp | 48 - src/frontend-common/imgui_fullscreen.cpp | 6 +- src/frontend-common/imgui_manager.cpp | 19 +- src/frontend-common/input_manager.cpp | 50 +- .../save_state_selector_ui.cpp | 64 +- src/frontend-common/save_state_selector_ui.h | 4 +- .../ini_settings_interface.cpp | 0 .../ini_settings_interface.h | 0 src/util/util.props | 4 +- src/util/util.vcxproj | 2 + src/util/util.vcxproj.filters | 2 + 165 files changed, 7001 insertions(+), 4308 deletions(-) create mode 100644 src/core/cheevos.h create mode 100644 src/core/host.cpp create mode 100644 src/duckstation-qt/controllerbindingwidget_digital_controller.ui create mode 100644 src/duckstation-qt/resources/controllers/analog_controller.svg create mode 100644 src/duckstation-qt/resources/controllers/digital_controller.svg create mode 100644 src/duckstation-qt/resources/controllers/guncon.svg create mode 100644 src/duckstation-qt/resources/controllers/negcon.svg create mode 100644 src/duckstation-qt/resources/generate.sh create mode 100644 src/duckstation-qt/resources/icons/black/svg/arrow-left-right-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/artboard-2-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/book-open-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/brush-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/close-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/dashboard-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/disc-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/door-open-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/download-2-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/dvd-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/eject-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/file-add-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/file-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/file-list-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/file-reduce-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/file-search-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/file-settings-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/flask-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/folder-add-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/folder-open-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/folder-reduce-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/folder-settings-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/fullscreen-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/function-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/gamepad-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/hard-drive-2-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/keyboard-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/layout-grid-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/list-check.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/pause-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/play-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/refresh-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/restart-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/save-3-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/screenshot-2-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/sd-card-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/settings-3-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/shut-down-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/tv-2-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/volume-up-line.svg create mode 100644 src/duckstation-qt/resources/icons/black/svg/window-2-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/arrow-left-right-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/artboard-2-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/book-open-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/brush-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/close-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/dashboard-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/disc-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/door-open-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/download-2-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/dvd-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/eject-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/file-add-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/file-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/file-list-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/file-reduce-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/file-search-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/file-settings-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/flask-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/folder-add-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/folder-open-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/folder-reduce-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/folder-settings-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/fullscreen-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/function-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/gamepad-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/hard-drive-2-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/keyboard-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/layout-grid-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/list-check.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/pause-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/play-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/refresh-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/restart-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/save-3-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/screenshot-2-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/sd-card-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/settings-3-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/shut-down-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/tv-2-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/volume-up-line.svg create mode 100644 src/duckstation-qt/resources/icons/white/svg/window-2-line.svg rename src/{frontend-common => util}/ini_settings_interface.cpp (100%) rename src/{frontend-common => util}/ini_settings_interface.h (100%) diff --git a/src/core/analog_controller.cpp b/src/core/analog_controller.cpp index d388e0b3e..6014d9d0b 100644 --- a/src/core/analog_controller.cpp +++ b/src/core/analog_controller.cpp @@ -41,8 +41,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); } @@ -89,11 +89,11 @@ 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."), + Host::TranslateString("AnalogController", "Controller %u switched to analog mode.") : + Host::TranslateString("AnalogController", "Controller %u switched to digital mode."), m_index + 1u); } } @@ -113,7 +113,7 @@ void AnalogController::SetBindState(u32 index, float value) if (index == static_cast(Button::Analog)) { // analog toggle - if (value > 0.0f) + if (value >= 0.5f) { if (m_command == Command::Idle) ProcessAnalogModeToggle(); @@ -172,7 +172,7 @@ void AnalogController::SetBindState(u32 index, float value) const u16 bit = u16(1) << static_cast(index); // todo: deadzone - if (value > 0.0f) + if (value >= 0.5f) { if (m_button_state & bit) System::SetRunaheadReplayFlag(); @@ -240,10 +240,10 @@ void AnalogController::SetAnalogMode(bool enabled) return; Log_InfoPrintf("Controller %u switched to %s mode.", m_index + 1u, enabled ? "analog" : "digital"); - g_host_interface->AddFormattedOSDMessage( + Host::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."), + 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 +252,11 @@ 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."), + 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 @@ -707,41 +707,42 @@ std::unique_ptr AnalogController::Create(u32 index) } static const Controller::ControllerBindingInfo s_binding_info[] = { -#define BUTTON(name, display_name, genb) \ +#define BUTTON(name, display_name, button, genb) \ { \ - name, display_name, Controller::ControllerBindingType::Button, genb \ + name, display_name, static_cast(button), Controller::ControllerBindingType::Button, genb \ } -#define AXIS(name, display_name, genb) \ +#define AXIS(name, display_name, halfaxis, genb) \ { \ - name, display_name, Controller::ControllerBindingType::HalfAxis, genb \ + name, display_name, static_cast(AnalogController::Button::Count) + static_cast(halfaxis), \ + Controller::ControllerBindingType::HalfAxis, genb \ } - BUTTON("Select", "Select", GenericInputBinding::Select), - BUTTON("L3", "Select", GenericInputBinding::L3), - BUTTON("R3", "Select", GenericInputBinding::R3), - BUTTON("Start", "Select", GenericInputBinding::Start), - BUTTON("Up", "D-Pad Up", GenericInputBinding::DPadUp), - BUTTON("Right", "D-Pad Right", GenericInputBinding::DPadRight), - BUTTON("Down", "D-Pad Down", GenericInputBinding::DPadDown), - BUTTON("Left", "D-Pad Left", GenericInputBinding::DPadLeft), - BUTTON("L2", "L2", GenericInputBinding::L2), - BUTTON("R2", "R2", GenericInputBinding::R2), - BUTTON("L1", "L1", GenericInputBinding::L1), - BUTTON("R1", "R1", GenericInputBinding::R1), - BUTTON("Triangle", "Triangle", GenericInputBinding::Triangle), - BUTTON("Circle", "Circle", GenericInputBinding::Circle), - BUTTON("Cross", "Cross", GenericInputBinding::Cross), - BUTTON("Square", "Square", GenericInputBinding::Square), - BUTTON("Analog", "Analog Toggle", GenericInputBinding::System), + BUTTON("Up", "D-Pad Up", AnalogController::Button::Up, GenericInputBinding::DPadUp), + BUTTON("Right", "D-Pad Right", AnalogController::Button::Down, 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), - AXIS("LLeft", "Left Stick Left", GenericInputBinding::LeftStickLeft), - AXIS("LRight", "Left Stick Right", GenericInputBinding::LeftStickRight), - AXIS("LDown", "Left Stick Down", GenericInputBinding::LeftStickDown), - AXIS("LUp", "Left Stick Up", GenericInputBinding::LeftStickUp), - AXIS("RLeft", "Right Stick Left", GenericInputBinding::LeftStickLeft), - AXIS("RRight", "Right Stick Right", GenericInputBinding::LeftStickRight), - AXIS("RDown", "Right Stick Down", GenericInputBinding::LeftStickDown), - AXIS("RUp", "Right Stick Up", GenericInputBinding::LeftStickUp), + 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::LeftStickLeft), + AXIS("RRight", "Right Stick Right", AnalogController::HalfAxis::RRight, GenericInputBinding::LeftStickRight), + AXIS("RDown", "Right Stick Down", AnalogController::HalfAxis::RDown, GenericInputBinding::LeftStickDown), + AXIS("RUp", "Right Stick Up", AnalogController::HalfAxis::RUp, GenericInputBinding::LeftStickUp), #undef AXIS #undef BUTTON diff --git a/src/core/analog_controller.h b/src/core/analog_controller.h index 1aacd75d9..caba4ec00 100644 --- a/src/core/analog_controller.h +++ b/src/core/analog_controller.h @@ -39,6 +39,19 @@ 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; @@ -82,19 +95,6 @@ private: GetSetRumble // 0x4D }; - enum class HalfAxis : u8 - { - LLeft, - LRight, - LDown, - LUp, - RLeft, - RRight, - RDown, - RUp, - Count - }; - Command m_command = Command::Idle; int m_command_step = 0; diff --git a/src/core/bus.cpp b/src/core/bus.cpp index 638f6214a..20eca2cd8 100644 --- a/src/core/bus.cpp +++ b/src/core/bus.cpp @@ -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; } diff --git a/src/core/cheats.cpp b/src/core/cheats.cpp index 3519058ef..33ab0e7fd 100644 --- a/src/core/cheats.cpp +++ b/src/core/cheats.cpp @@ -9,6 +9,7 @@ #include "controller.h" #include "cpu_code_cache.h" #include "cpu_core.h" +#include "host.h" #include "host_interface.h" #include "system.h" #include diff --git a/src/core/cheevos.h b/src/core/cheevos.h new file mode 100644 index 000000000..f13cd1ca1 --- /dev/null +++ b/src/core/cheevos.h @@ -0,0 +1,35 @@ +#pragma once +#include "common/types.h" + +class StateWrapper; + +namespace Cheevos { + +#ifdef WITH_CHEEVOS + +// Implemented in Host. +extern void Reset(); +extern bool DoState(StateWrapper& sw); + +/// Returns true if features such as save states should be disabled. +extern bool IsChallengeModeActive(); + +extern void DisplayBlockedByChallengeModeMessage(); + +#else + +// Make noops when compiling without cheevos. +static inline void Reset() {} +static inline bool DoState(StateWrapper& sw) +{ + return true; +} +static constexpr inline bool IsChallengeModeActive() +{ + return false; +} +static inline void DisplayBlockedByChallengeModeMessage() {} + +#endif + +} // namespace Cheevos diff --git a/src/core/controller.cpp b/src/core/controller.cpp index dfcfbbbd9..171d7a233 100644 --- a/src/core/controller.cpp +++ b/src/core/controller.cpp @@ -217,3 +217,8 @@ bool Controller::PortAndSlotIsMultitap(u32 port, u32 slot) { return (slot != 0); } + +std::string Controller::GetSettingsSection(u32 pad) +{ + return fmt::format("Pad{}", pad + 1u); +} diff --git a/src/core/controller.h b/src/core/controller.h index c92f0b568..9de254398 100644 --- a/src/core/controller.h +++ b/src/core/controller.h @@ -40,6 +40,7 @@ public: { const char* name; const char* display_name; + u32 bind_index; ControllerBindingType type; GenericInputBinding generic_mapping; }; @@ -133,6 +134,9 @@ public: 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); + protected: u32 m_index; }; diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj index 6a56f741a..252381cc9 100644 --- a/src/core/core.vcxproj +++ b/src/core/core.vcxproj @@ -46,6 +46,7 @@ + @@ -80,6 +81,7 @@ + diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters index 5d003ab72..44ccbcef4 100644 --- a/src/core/core.vcxproj.filters +++ b/src/core/core.vcxproj.filters @@ -58,6 +58,7 @@ + @@ -122,5 +123,6 @@ + \ No newline at end of file diff --git a/src/core/cpu_core.cpp b/src/core/cpu_core.cpp index 92665d89b..ca29ae482 100644 --- a/src/core/cpu_core.cpp +++ b/src/core/cpu_core.cpp @@ -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 { diff --git a/src/core/digital_controller.cpp b/src/core/digital_controller.cpp index be25ef10a..5d2bcb979 100644 --- a/src/core/digital_controller.cpp +++ b/src/core/digital_controller.cpp @@ -38,7 +38,7 @@ void DigitalController::SetBindState(u32 index, float value) if (index >= static_cast(Button::Count)) return; - const bool pressed = (value > 0.0f); + const bool pressed = (value >= 0.5f); const u16 bit = u16(1) << static_cast(index); if (pressed) { @@ -130,27 +130,25 @@ std::unique_ptr DigitalController::Create(u32 index) } static const Controller::ControllerBindingInfo s_binding_info[] = { -#define BUTTON(name, display_name, genb) \ +#define BUTTON(name, display_name, button, genb) \ { \ - name, display_name, Controller::ControllerBindingType::Button, genb \ + name, display_name, static_cast(button), Controller::ControllerBindingType::Button, genb \ } - BUTTON("Select", "Select", GenericInputBinding::Select), - BUTTON("L3", "Select", GenericInputBinding::L3), - BUTTON("R3", "Select", GenericInputBinding::R3), - BUTTON("Start", "Select", GenericInputBinding::Start), - BUTTON("Up", "D-Pad Up", GenericInputBinding::DPadUp), - BUTTON("Right", "D-Pad Right", GenericInputBinding::DPadRight), - BUTTON("Down", "D-Pad Down", GenericInputBinding::DPadDown), - BUTTON("Left", "D-Pad Left", GenericInputBinding::DPadLeft), - BUTTON("L2", "L2", GenericInputBinding::L2), - BUTTON("R2", "R2", GenericInputBinding::R2), - BUTTON("L1", "L1", GenericInputBinding::L1), - BUTTON("R1", "R1", GenericInputBinding::R1), - BUTTON("Triangle", "Triangle", GenericInputBinding::Triangle), - BUTTON("Circle", "Circle", GenericInputBinding::Circle), - BUTTON("Cross", "Cross", GenericInputBinding::Cross), - BUTTON("Square", "Square", GenericInputBinding::Square), + 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 }; diff --git a/src/core/gpu_hw.cpp b/src/core/gpu_hw.cpp index 7601f425f..6cd66dfbc 100644 --- a/src/core/gpu_hw.cpp +++ b/src/core/gpu_hw.cpp @@ -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" @@ -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); } } @@ -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); } diff --git a/src/core/gpu_hw_opengl.cpp b/src/core/gpu_hw_opengl.cpp index e2d7b3e9a..a7da10aea 100644 --- a/src/core/gpu_hw_opengl.cpp +++ b/src/core/gpu_hw_opengl.cpp @@ -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" @@ -55,10 +56,10 @@ bool GPU_HW_OpenGL::Initialize(HostDisplay* host_display) (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; } diff --git a/src/core/gte.cpp b/src/core/gte.cpp index aad5db186..c6a433ff6 100644 --- a/src/core/gte.cpp +++ b/src/core/gte.cpp @@ -188,7 +188,7 @@ void UpdateAspectRatio() { case DisplayAspectRatio::MatchWindow: { - const HostDisplay* display = g_host_interface->GetDisplay(); + const HostDisplay* display = Host::GetHostDisplay(); if (!display) { s_aspect_ratio = DisplayAspectRatio::R4_3; diff --git a/src/core/host.cpp b/src/core/host.cpp new file mode 100644 index 000000000..db38b4c58 --- /dev/null +++ b/src/core/host.cpp @@ -0,0 +1,32 @@ +#include "host.h" +#include "common/string_util.h" +#include + +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); +} diff --git a/src/core/host.h b/src/core/host.h index def921654..2abd57d4c 100644 --- a/src/core/host.h +++ b/src/core/host.h @@ -1,12 +1,21 @@ #pragma once +#include "common/string.h" #include "common/types.h" +#include #include #include #include #include +struct WindowInfo; +enum class AudioBackend : u8; +class AudioStream; + +/// 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 @@ -58,6 +67,12 @@ std::optional> ReadResourceFile(const char* filename); /// Reads a resource file file from the resources directory as a string. std::optional ReadResourceFileToString(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 CreateAudioStream(AudioBackend backend); + /// 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); @@ -70,14 +85,15 @@ void ClearOSDMessages(); 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, ...) printflike(2, 3); + /// 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); - -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 ShowToast(std::string title, std::string message, float duration = 10.0f); } // namespace Host diff --git a/src/core/host_display.h b/src/core/host_display.h index 17dac6a80..f7db736a3 100644 --- a/src/core/host_display.h +++ b/src/core/host_display.h @@ -290,3 +290,32 @@ protected: bool m_display_integer_scaling = false; bool m_display_stretch = false; }; + +namespace Host { +/// Creates the host display. This may create a new window. The API used depends on the current configuration. +HostDisplay* AcquireHostDisplay(/*HostDisplay::RenderAPI api*/); + +/// Destroys the host display. This may close the display window. +void ReleaseHostDisplay(); + +/// Returns a pointer to the current host display abstraction. Assumes AcquireHostDisplay() has been caled. +HostDisplay* GetHostDisplay(); + +/// 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(); + +/// Called on the MTGS thread when a resize request is received. +//void ResizeHostDisplay(u32 new_window_width, u32 new_window_height, float new_window_scale); + +/// Called on the MTGS thread when a request to update the display is received. +/// This could be a fullscreen transition, for example. +//void UpdateHostDisplay(); + +/// Provided by the host; renders the display. +void RenderDisplay(); +void InvalidateDisplay(); +} // namespace Host diff --git a/src/core/host_interface.cpp b/src/core/host_interface.cpp index 03853dd77..69d4e3014 100644 --- a/src/core/host_interface.cpp +++ b/src/core/host_interface.cpp @@ -16,6 +16,7 @@ #include "gte.h" #include "host.h" #include "host_display.h" +#include "host_settings.h" #include "pgxp.h" #include "save_state_version.h" #include "spu.h" @@ -42,7 +43,6 @@ HostInterface::HostInterface() HostInterface::~HostInterface() { // system should be shut down prior to the destructor - Assert(System::IsShutdown() && !m_audio_stream && !m_display); Assert(g_host_interface == this); g_host_interface = nullptr; } @@ -54,222 +54,11 @@ bool HostInterface::Initialize() void HostInterface::Shutdown() { - if (!System::IsShutdown()) - System::Shutdown(); -} - -void HostInterface::CreateAudioStream() -{ - Log_InfoPrintf("Creating '%s' audio stream, sample rate = %u, channels = %u, buffer size = %u", - Settings::GetAudioBackendName(g_settings.audio_backend), AUDIO_SAMPLE_RATE, AUDIO_CHANNELS, - g_settings.audio_buffer_size); - - m_audio_stream = CreateAudioStream(g_settings.audio_backend); - - if (!m_audio_stream || - !m_audio_stream->Reconfigure(AUDIO_SAMPLE_RATE, AUDIO_SAMPLE_RATE, AUDIO_CHANNELS, g_settings.audio_buffer_size)) - { - ReportFormattedError("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(AUDIO_SAMPLE_RATE, AUDIO_SAMPLE_RATE, AUDIO_CHANNELS, g_settings.audio_buffer_size); - } - - m_audio_stream->SetOutputVolume(GetAudioOutputVolume()); - - if (System::IsValid()) - g_spu.SetAudioStream(m_audio_stream.get()); -} - -s32 HostInterface::GetAudioOutputVolume() const -{ - return g_settings.audio_output_muted ? 0 : g_settings.audio_output_volume; -} - -bool HostInterface::BootSystem(std::shared_ptr parameters) -{ - if (!parameters->state_stream) - { - if (parameters->filename.empty()) - Log_InfoPrintf("Boot Filename: "); - else - Log_InfoPrintf("Boot Filename: %s", parameters->filename.c_str()); - } - - if (!AcquireHostDisplay()) - { - ReportError(g_host_interface->TranslateString("System", "Failed to acquire host display.")); - OnSystemDestroyed(); - return false; - } - - // set host display settings - m_display->SetDisplayLinearFiltering(g_settings.display_linear_filtering); - m_display->SetDisplayIntegerScaling(g_settings.display_integer_scaling); - m_display->SetDisplayStretch(g_settings.display_stretch); - - // create the audio stream. this will never fail, since we'll just fall back to null - CreateAudioStream(); - - if (!System::Boot(*parameters)) - { - if (!System::IsStartupCancelled()) - { - ReportError( - g_host_interface->TranslateString("System", "System failed to boot. The log may contain more information.")); - } - - OnSystemDestroyed(); - m_audio_stream.reset(); - ReleaseHostDisplay(); - return false; - } - - UpdateSoftwareCursor(); - OnSystemCreated(); - - m_audio_stream->PauseOutput(false); - return true; -} - -void HostInterface::ResetSystem() -{ - System::Reset(); - System::ResetPerformanceCounters(); - System::ResetThrottler(); - AddOSDMessage(TranslateStdString("OSDMessage", "System reset.")); -} - -void HostInterface::PauseSystem(bool paused) -{ - if (paused == System::IsPaused() || System::IsShutdown()) - return; - - System::SetState(paused ? System::State::Paused : System::State::Running); - if (!paused) - m_audio_stream->EmptyBuffers(); - m_audio_stream->PauseOutput(paused); - - OnSystemPaused(paused); - - if (!paused) - { - System::ResetPerformanceCounters(); - System::ResetThrottler(); - } -} - -void HostInterface::DestroySystem() -{ - if (System::IsShutdown()) - return; - - System::Shutdown(); - m_audio_stream.reset(); - UpdateSoftwareCursor(); - ReleaseHostDisplay(); - OnSystemDestroyed(); -} - -void HostInterface::ReportError(const char* message) -{ - Log_ErrorPrint(message); -} - -void HostInterface::ReportMessage(const char* message) -{ - Log_InfoPrint(message); -} - -void HostInterface::ReportDebuggerMessage(const char* message) -{ - Log_InfoPrintf("(Debugger) %s", message); -} - -bool HostInterface::ConfirmMessage(const char* message) -{ - Log_WarningPrintf("ConfirmMessage(\"%s\") -> Yes", message); - return true; -} - -void HostInterface::ReportFormattedError(const char* format, ...) -{ - std::va_list ap; - va_start(ap, format); - std::string message = StringUtil::StdStringFromFormatV(format, ap); - va_end(ap); - - ReportError(message.c_str()); -} - -void HostInterface::ReportFormattedMessage(const char* format, ...) -{ - std::va_list ap; - va_start(ap, format); - std::string message = StringUtil::StdStringFromFormatV(format, ap); - va_end(ap); - - ReportMessage(message.c_str()); -} - -void HostInterface::ReportFormattedDebuggerMessage(const char* format, ...) -{ - std::va_list ap; - va_start(ap, format); - std::string message = StringUtil::StdStringFromFormatV(format, ap); - va_end(ap); - - ReportDebuggerMessage(message.c_str()); -} - -bool HostInterface::ConfirmFormattedMessage(const char* format, ...) -{ - std::va_list ap; - va_start(ap, format); - std::string message = StringUtil::StdStringFromFormatV(format, ap); - va_end(ap); - - return ConfirmMessage(message.c_str()); -} - -void HostInterface::AddOSDMessage(std::string message, float duration /*= 2.0f*/) -{ - Host::AddOSDMessage(std::move(message), duration); -} - -void HostInterface::AddKeyedOSDMessage(std::string key, std::string message, float duration /*= 2.0f*/) -{ - Host::AddKeyedOSDMessage(std::move(key), std::move(message), duration); -} - -void HostInterface::RemoveKeyedOSDMessage(std::string key) -{ - Host::RemoveKeyedOSDMessage(std::move(key)); -} - -void HostInterface::AddFormattedOSDMessage(float duration, const char* format, ...) -{ - std::va_list ap; - va_start(ap, format); - std::string message = StringUtil::StdStringFromFormatV(format, ap); - va_end(ap); - - Host::AddOSDMessage(std::move(message), duration); -} - -void HostInterface::AddKeyedFormattedOSDMessage(std::string key, float duration, const char* format, ...) -{ - std::va_list ap; - va_start(ap, format); - std::string message = StringUtil::StdStringFromFormatV(format, ap); - va_end(ap); - - Host::AddKeyedOSDMessage(std::move(key), std::move(message), duration); } std::string HostInterface::GetBIOSDirectory() { - std::string dir = GetStringSettingValue("BIOS", "SearchDirectory", ""); + std::string dir = Host::GetStringSettingValue("BIOS", "SearchDirectory", ""); if (!dir.empty()) return dir; @@ -283,16 +72,16 @@ std::optional> HostInterface::GetBIOSImage(ConsoleRegion region) switch (region) { case ConsoleRegion::NTSC_J: - bios_name = GetStringSettingValue("BIOS", "PathNTSCJ", ""); + bios_name = Host::GetStringSettingValue("BIOS", "PathNTSCJ", ""); break; case ConsoleRegion::PAL: - bios_name = GetStringSettingValue("BIOS", "PathPAL", ""); + bios_name = Host::GetStringSettingValue("BIOS", "PathPAL", ""); break; case ConsoleRegion::NTSC_U: default: - bios_name = GetStringSettingValue("BIOS", "PathNTSCU", ""); + bios_name = Host::GetStringSettingValue("BIOS", "PathNTSCU", ""); break; } @@ -307,9 +96,8 @@ std::optional> HostInterface::GetBIOSImage(ConsoleRegion region) StringUtil::StdStringFromFormat("%s" FS_OSPATH_SEPARATOR_STR "%s", bios_dir.c_str(), bios_name.c_str()).c_str()); if (!image.has_value()) { - g_host_interface->ReportFormattedError( - g_host_interface->TranslateString("HostInterface", "Failed to load configured BIOS file '%s'"), - bios_name.c_str()); + Host::ReportFormattedErrorAsync( + "Error", Host::TranslateString("HostInterface", "Failed to load configured BIOS file '%s'"), bios_name.c_str()); return std::nullopt; } @@ -371,9 +159,9 @@ std::optional> HostInterface::FindBIOSImageInDirectory(ConsoleRe if (!fallback_image.has_value()) { - g_host_interface->ReportFormattedError( - g_host_interface->TranslateString("HostInterface", "No BIOS image found for %s region"), - Settings::GetConsoleRegionDisplayName(region)); + Host::ReportFormattedErrorAsync("Error", + Host::TranslateString("HostInterface", "No BIOS image found for %s region"), + Settings::GetConsoleRegionDisplayName(region)); return std::nullopt; } @@ -425,488 +213,11 @@ bool HostInterface::HasAnyBIOSImages() return (FindBIOSImageInDirectory(ConsoleRegion::Auto, dir.c_str()).has_value()); } -bool HostInterface::LoadState(const char* filename) -{ - std::unique_ptr stream = ByteStream::OpenFile(filename, BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED); - if (!stream) - return false; - - AddFormattedOSDMessage(5.0f, TranslateString("OSDMessage", "Loading state from '%s'..."), filename); - - if (!System::IsShutdown()) - { - if (!System::LoadState(stream.get())) - { - ReportFormattedError(TranslateString("OSDMessage", "Loading state from '%s' failed. Resetting."), filename); - ResetSystem(); - return false; - } - } - else - { - auto boot_params = std::make_shared(); - boot_params->state_stream = std::move(stream); - if (!BootSystem(std::move(boot_params))) - return false; - } - - System::ResetPerformanceCounters(); - System::ResetThrottler(); - OnDisplayInvalidated(); - return true; -} - -bool HostInterface::SaveState(const char* filename) -{ - std::unique_ptr stream = - ByteStream::OpenFile(filename, BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_WRITE | BYTESTREAM_OPEN_TRUNCATE | - BYTESTREAM_OPEN_ATOMIC_UPDATE | BYTESTREAM_OPEN_STREAMED); - if (!stream) - return false; - - const bool result = System::SaveState(stream.get()); - if (!result) - { - ReportFormattedError(TranslateString("OSDMessage", "Saving state to '%s' failed."), filename); - stream->Discard(); - } - else - { - AddFormattedOSDMessage(5.0f, TranslateString("OSDMessage", "State saved to '%s'."), filename); - stream->Commit(); - } - - return result; -} - std::string HostInterface::GetShaderCacheBasePath() const { return GetUserDirectoryRelativePath("cache/"); } -void HostInterface::SetDefaultSettings(SettingsInterface& si) -{ - si.SetStringValue("Console", "Region", Settings::GetConsoleRegionName(Settings::DEFAULT_CONSOLE_REGION)); - si.SetBoolValue("Console", "Enable8MBRAM", false); - - si.SetFloatValue("Main", "EmulationSpeed", 1.0f); - si.SetFloatValue("Main", "FastForwardSpeed", 0.0f); - si.SetFloatValue("Main", "TurboSpeed", 0.0f); - si.SetBoolValue("Main", "SyncToHostRefreshRate", false); - si.SetBoolValue("Main", "IncreaseTimerResolution", true); - si.SetBoolValue("Main", "InhibitScreensaver", true); - si.SetBoolValue("Main", "StartPaused", false); - si.SetBoolValue("Main", "StartFullscreen", false); - si.SetBoolValue("Main", "PauseOnFocusLoss", false); - si.SetBoolValue("Main", "PauseOnMenu", true); - si.SetBoolValue("Main", "SaveStateOnExit", true); - si.SetBoolValue("Main", "ConfirmPowerOff", true); - si.SetBoolValue("Main", "LoadDevicesFromSaveStates", false); - si.SetBoolValue("Main", "ApplyGameSettings", true); - si.SetBoolValue("Main", "AutoLoadCheats", true); - si.SetBoolValue("Main", "DisableAllEnhancements", false); - si.SetBoolValue("Main", "RewindEnable", false); - si.SetFloatValue("Main", "RewindFrequency", 10.0f); - si.SetIntValue("Main", "RewindSaveSlots", 10); - si.SetFloatValue("Main", "RunaheadFrameCount", 0); - - si.SetStringValue("CPU", "ExecutionMode", Settings::GetCPUExecutionModeName(Settings::DEFAULT_CPU_EXECUTION_MODE)); - si.SetIntValue("CPU", "OverclockNumerator", 1); - si.SetIntValue("CPU", "OverclockDenominator", 1); - si.SetBoolValue("CPU", "OverclockEnable", false); - si.SetBoolValue("CPU", "RecompilerMemoryExceptions", false); - si.SetBoolValue("CPU", "RecompilerBlockLinking", true); - si.SetBoolValue("CPU", "ICache", false); - si.SetBoolValue("CPU", "FastmemMode", Settings::GetCPUFastmemModeName(Settings::DEFAULT_CPU_FASTMEM_MODE)); - - si.SetStringValue("GPU", "Renderer", Settings::GetRendererName(Settings::DEFAULT_GPU_RENDERER)); - si.SetIntValue("GPU", "ResolutionScale", 1); - si.SetIntValue("GPU", "Multisamples", 1); - si.SetBoolValue("GPU", "UseDebugDevice", false); - si.SetBoolValue("GPU", "UseSoftwareRendererForReadbacks", false); - si.SetBoolValue("GPU", "PerSampleShading", false); - si.SetBoolValue("GPU", "UseThread", true); - si.SetBoolValue("GPU", "ThreadedPresentation", true); - si.SetBoolValue("GPU", "TrueColor", false); - si.SetBoolValue("GPU", "ScaledDithering", true); - si.SetStringValue("GPU", "TextureFilter", Settings::GetTextureFilterName(Settings::DEFAULT_GPU_TEXTURE_FILTER)); - si.SetStringValue("GPU", "DownsampleMode", Settings::GetDownsampleModeName(Settings::DEFAULT_GPU_DOWNSAMPLE_MODE)); - si.SetBoolValue("GPU", "DisableInterlacing", true); - si.SetBoolValue("GPU", "ForceNTSCTimings", false); - si.SetBoolValue("GPU", "WidescreenHack", false); - si.SetBoolValue("GPU", "ChromaSmoothing24Bit", false); - si.SetBoolValue("GPU", "PGXPEnable", false); - si.SetBoolValue("GPU", "PGXPCulling", true); - si.SetBoolValue("GPU", "PGXPTextureCorrection", true); - si.SetBoolValue("GPU", "PGXPVertexCache", false); - si.SetBoolValue("GPU", "PGXPCPU", false); - si.SetBoolValue("GPU", "PGXPPreserveProjFP", false); - si.SetFloatValue("GPU", "PGXPTolerance", -1.0f); - si.SetBoolValue("GPU", "PGXPDepthBuffer", false); - si.SetFloatValue("GPU", "PGXPDepthClearThreshold", Settings::DEFAULT_GPU_PGXP_DEPTH_THRESHOLD); - - si.SetStringValue("Display", "CropMode", Settings::GetDisplayCropModeName(Settings::DEFAULT_DISPLAY_CROP_MODE)); - si.SetIntValue("Display", "ActiveStartOffset", 0); - si.SetIntValue("Display", "ActiveEndOffset", 0); - si.SetIntValue("Display", "LineStartOffset", 0); - si.SetIntValue("Display", "LineEndOffset", 0); - si.SetStringValue("Display", "AspectRatio", - Settings::GetDisplayAspectRatioName(Settings::DEFAULT_DISPLAY_ASPECT_RATIO)); - si.SetIntValue("Display", "CustomAspectRatioNumerator", 4); - si.GetIntValue("Display", "CustomAspectRatioDenominator", 3); - si.SetBoolValue("Display", "Force4_3For24Bit", false); - si.SetBoolValue("Display", "LinearFiltering", true); - si.SetBoolValue("Display", "IntegerScaling", false); - si.SetBoolValue("Display", "Stretch", false); - si.SetBoolValue("Display", "PostProcessing", false); - si.SetBoolValue("Display", "ShowOSDMessages", true); - si.SetBoolValue("Display", "ShowFPS", false); - si.SetBoolValue("Display", "ShowVPS", false); - si.SetBoolValue("Display", "ShowSpeed", false); - si.SetBoolValue("Display", "ShowResolution", false); - si.SetBoolValue("Display", "ShowStatusIndicators", true); - si.SetBoolValue("Display", "ShowEnhancements", false); - si.SetBoolValue("Display", "Fullscreen", false); - si.SetBoolValue("Display", "VSync", Settings::DEFAULT_VSYNC_VALUE); - si.SetBoolValue("Display", "DisplayAllFrames", false); - si.SetStringValue("Display", "PostProcessChain", ""); - si.SetFloatValue("Display", "MaxFPS", Settings::DEFAULT_DISPLAY_MAX_FPS); - - si.SetIntValue("CDROM", "ReadaheadSectors", Settings::DEFAULT_CDROM_READAHEAD_SECTORS); - si.SetBoolValue("CDROM", "RegionCheck", false); - si.SetBoolValue("CDROM", "LoadImageToRAM", false); - si.SetBoolValue("CDROM", "MuteCDAudio", false); - si.SetIntValue("CDROM", "ReadSpeedup", 1); - si.SetIntValue("CDROM", "SeekSpeedup", 1); - - si.SetStringValue("Audio", "Backend", Settings::GetAudioBackendName(Settings::DEFAULT_AUDIO_BACKEND)); - si.SetIntValue("Audio", "OutputVolume", 100); - si.SetIntValue("Audio", "FastForwardVolume", 100); - si.SetIntValue("Audio", "BufferSize", DEFAULT_AUDIO_BUFFER_SIZE); - si.SetBoolValue("Audio", "Resampling", true); - si.SetIntValue("Audio", "OutputMuted", false); - si.SetBoolValue("Audio", "Sync", true); - si.SetBoolValue("Audio", "DumpOnBoot", false); - - si.SetStringValue("BIOS", "SearchDirectory", ""); - si.SetStringValue("BIOS", "PathNTSCU", ""); - si.SetStringValue("BIOS", "PathNTSCJ", ""); - si.SetStringValue("BIOS", "PathPAL", ""); - si.SetBoolValue("BIOS", "PatchTTYEnable", false); - si.SetBoolValue("BIOS", "PatchFastBoot", Settings::DEFAULT_FAST_BOOT_VALUE); - - si.SetStringValue("Controller1", "Type", Settings::GetControllerTypeName(Settings::DEFAULT_CONTROLLER_1_TYPE)); - - for (u32 i = 1; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) - { - si.SetStringValue(TinyString::FromFormat("Controller%u", i + 1u), "Type", - Settings::GetControllerTypeName(Settings::DEFAULT_CONTROLLER_2_TYPE)); - } - - si.SetStringValue("MemoryCards", "Card1Type", Settings::GetMemoryCardTypeName(Settings::DEFAULT_MEMORY_CARD_1_TYPE)); - si.SetStringValue("MemoryCards", "Card2Type", Settings::GetMemoryCardTypeName(Settings::DEFAULT_MEMORY_CARD_2_TYPE)); - si.DeleteValue("MemoryCards", "Card1Path"); - si.DeleteValue("MemoryCards", "Card2Path"); - si.DeleteValue("MemoryCards", "Directory"); - si.SetBoolValue("MemoryCards", "UsePlaylistTitle", true); - - si.SetStringValue("ControllerPorts", "MultitapMode", Settings::GetMultitapModeName(Settings::DEFAULT_MULTITAP_MODE)); - - si.SetStringValue("Logging", "LogLevel", Settings::GetLogLevelName(Settings::DEFAULT_LOG_LEVEL)); - si.SetStringValue("Logging", "LogFilter", ""); - si.SetBoolValue("Logging", "LogToConsole", Settings::DEFAULT_LOG_TO_CONSOLE); - si.SetBoolValue("Logging", "LogToDebug", false); - si.SetBoolValue("Logging", "LogToWindow", false); - si.SetBoolValue("Logging", "LogToFile", false); - - si.SetBoolValue("Debug", "ShowVRAM", false); - si.SetBoolValue("Debug", "DumpCPUToVRAMCopies", false); - si.SetBoolValue("Debug", "DumpVRAMToCPUCopies", false); - si.SetBoolValue("Debug", "ShowGPUState", false); - si.SetBoolValue("Debug", "ShowCDROMState", false); - si.SetBoolValue("Debug", "ShowSPUState", false); - si.SetBoolValue("Debug", "ShowTimersState", false); - si.SetBoolValue("Debug", "ShowMDECState", false); - si.SetBoolValue("Debug", "ShowDMAState", false); - - si.SetIntValue("Hacks", "DMAMaxSliceTicks", static_cast(Settings::DEFAULT_DMA_MAX_SLICE_TICKS)); - si.SetIntValue("Hacks", "DMAHaltTicks", static_cast(Settings::DEFAULT_DMA_HALT_TICKS)); - si.SetIntValue("Hacks", "GPUFIFOSize", static_cast(Settings::DEFAULT_GPU_FIFO_SIZE)); - si.SetIntValue("Hacks", "GPUMaxRunAhead", static_cast(Settings::DEFAULT_GPU_MAX_RUN_AHEAD)); -} - -void HostInterface::LoadSettings(SettingsInterface& si) -{ - g_settings.Load(si); -} - -void HostInterface::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) - { - AddOSDMessage( - 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) - { - AddOSDMessage(TranslateStdString("OSDMessage", "Rewind is not supported on 32-bit ARM for Android."), 30.0f); - g_settings.rewind_enable = false; - } -#endif -} - -void HostInterface::SaveSettings(SettingsInterface& si) -{ - g_settings.Save(si); -} - -void HostInterface::CheckForSettingsChanges(const Settings& old_settings) -{ - if (System::IsValid() && (g_settings.gpu_renderer != old_settings.gpu_renderer || - g_settings.gpu_use_debug_device != old_settings.gpu_use_debug_device || - g_settings.gpu_threaded_presentation != old_settings.gpu_threaded_presentation)) - { - AddFormattedOSDMessage(5.0f, TranslateString("OSDMessage", "Switching to %s%s GPU renderer."), - Settings::GetRendererName(g_settings.gpu_renderer), - g_settings.gpu_use_debug_device ? " (debug)" : ""); - RecreateSystem(); - } - - if (System::IsValid()) - { - System::ClearMemorySaveStates(); - - if (g_settings.cpu_overclock_active != old_settings.cpu_overclock_active || - (g_settings.cpu_overclock_active && - (g_settings.cpu_overclock_numerator != old_settings.cpu_overclock_numerator || - g_settings.cpu_overclock_denominator != old_settings.cpu_overclock_denominator))) - { - System::UpdateOverclock(); - } - - if (g_settings.audio_backend != old_settings.audio_backend || - g_settings.audio_buffer_size != old_settings.audio_buffer_size) - { - if (g_settings.audio_backend != old_settings.audio_backend) - { - AddFormattedOSDMessage(5.0f, TranslateString("OSDMessage", "Switching to %s audio backend."), - Settings::GetAudioBackendName(g_settings.audio_backend)); - } - DebugAssert(m_audio_stream); - m_audio_stream.reset(); - CreateAudioStream(); - m_audio_stream->PauseOutput(System::IsPaused()); - } - - if (g_settings.emulation_speed != old_settings.emulation_speed) - System::UpdateThrottlePeriod(); - - if (g_settings.cpu_execution_mode != old_settings.cpu_execution_mode || - g_settings.cpu_fastmem_mode != old_settings.cpu_fastmem_mode) - { - AddFormattedOSDMessage( - 5.0f, TranslateString("OSDMessage", "Switching to %s CPU execution mode."), - TranslateString("CPUExecutionMode", Settings::GetCPUExecutionModeDisplayName(g_settings.cpu_execution_mode)) - .GetCharArray()); - CPU::CodeCache::Reinitialize(); - CPU::ClearICache(); - } - - if (g_settings.cpu_execution_mode == CPUExecutionMode::Recompiler && - (g_settings.cpu_recompiler_memory_exceptions != old_settings.cpu_recompiler_memory_exceptions || - g_settings.cpu_recompiler_block_linking != old_settings.cpu_recompiler_block_linking || - g_settings.cpu_recompiler_icache != old_settings.cpu_recompiler_icache)) - { - AddOSDMessage(TranslateStdString("OSDMessage", "Recompiler options changed, flushing all blocks."), 5.0f); - CPU::CodeCache::Flush(); - - if (g_settings.cpu_recompiler_icache != old_settings.cpu_recompiler_icache) - CPU::ClearICache(); - } - - m_audio_stream->SetOutputVolume(GetAudioOutputVolume()); - - if (g_settings.gpu_resolution_scale != old_settings.gpu_resolution_scale || - g_settings.gpu_multisamples != old_settings.gpu_multisamples || - g_settings.gpu_per_sample_shading != old_settings.gpu_per_sample_shading || - g_settings.gpu_use_thread != old_settings.gpu_use_thread || - g_settings.gpu_use_software_renderer_for_readbacks != old_settings.gpu_use_software_renderer_for_readbacks || - g_settings.gpu_fifo_size != old_settings.gpu_fifo_size || - g_settings.gpu_max_run_ahead != old_settings.gpu_max_run_ahead || - g_settings.gpu_true_color != old_settings.gpu_true_color || - g_settings.gpu_scaled_dithering != old_settings.gpu_scaled_dithering || - g_settings.gpu_texture_filter != old_settings.gpu_texture_filter || - g_settings.gpu_disable_interlacing != old_settings.gpu_disable_interlacing || - g_settings.gpu_force_ntsc_timings != old_settings.gpu_force_ntsc_timings || - g_settings.gpu_24bit_chroma_smoothing != old_settings.gpu_24bit_chroma_smoothing || - g_settings.gpu_downsample_mode != old_settings.gpu_downsample_mode || - g_settings.display_crop_mode != old_settings.display_crop_mode || - g_settings.display_aspect_ratio != old_settings.display_aspect_ratio || - g_settings.gpu_pgxp_enable != old_settings.gpu_pgxp_enable || - g_settings.gpu_pgxp_depth_buffer != old_settings.gpu_pgxp_depth_buffer || - g_settings.display_active_start_offset != old_settings.display_active_start_offset || - g_settings.display_active_end_offset != old_settings.display_active_end_offset || - g_settings.display_line_start_offset != old_settings.display_line_start_offset || - g_settings.display_line_end_offset != old_settings.display_line_end_offset || - g_settings.rewind_enable != old_settings.rewind_enable || - g_settings.runahead_frames != old_settings.runahead_frames) - { - g_gpu->UpdateSettings(); - OnDisplayInvalidated(); - } - - if (g_settings.gpu_widescreen_hack != old_settings.gpu_widescreen_hack || - g_settings.display_aspect_ratio != old_settings.display_aspect_ratio || - (g_settings.display_aspect_ratio == DisplayAspectRatio::Custom && - (g_settings.display_aspect_ratio_custom_numerator != old_settings.display_aspect_ratio_custom_numerator || - g_settings.display_aspect_ratio_custom_denominator != old_settings.display_aspect_ratio_custom_denominator))) - { - GTE::UpdateAspectRatio(); - } - - if (g_settings.gpu_pgxp_enable != old_settings.gpu_pgxp_enable || - (g_settings.gpu_pgxp_enable && (g_settings.gpu_pgxp_culling != old_settings.gpu_pgxp_culling || - g_settings.gpu_pgxp_vertex_cache != old_settings.gpu_pgxp_vertex_cache || - g_settings.gpu_pgxp_cpu != old_settings.gpu_pgxp_cpu))) - { - if (g_settings.IsUsingCodeCache()) - { - AddOSDMessage(g_settings.gpu_pgxp_enable ? - TranslateStdString("OSDMessage", "PGXP enabled, recompiling all blocks.") : - TranslateStdString("OSDMessage", "PGXP disabled, recompiling all blocks."), - 5.0f); - CPU::CodeCache::Flush(); - } - - if (old_settings.gpu_pgxp_enable) - PGXP::Shutdown(); - - if (g_settings.gpu_pgxp_enable) - PGXP::Initialize(); - } - - if (g_settings.cdrom_readahead_sectors != old_settings.cdrom_readahead_sectors) - g_cdrom.SetReadaheadSectors(g_settings.cdrom_readahead_sectors); - - if (g_settings.memory_card_types != old_settings.memory_card_types || - g_settings.memory_card_paths != old_settings.memory_card_paths || - (g_settings.memory_card_use_playlist_title != old_settings.memory_card_use_playlist_title && - System::HasMediaSubImages()) || - g_settings.memory_card_directory != old_settings.memory_card_directory) - { - System::UpdateMemoryCardTypes(); - } - - if (g_settings.rewind_enable != old_settings.rewind_enable || - g_settings.rewind_save_frequency != old_settings.rewind_save_frequency || - g_settings.rewind_save_slots != old_settings.rewind_save_slots || - g_settings.runahead_frames != old_settings.runahead_frames) - { - System::UpdateMemorySaveStateSettings(); - } - - if (g_settings.texture_replacements.enable_vram_write_replacements != - old_settings.texture_replacements.enable_vram_write_replacements || - g_settings.texture_replacements.preload_textures != old_settings.texture_replacements.preload_textures) - { - g_texture_replacements.Reload(); - } - - g_dma.SetMaxSliceTicks(g_settings.dma_max_slice_ticks); - g_dma.SetHaltTicks(g_settings.dma_halt_ticks); - } - - bool controllers_updated = false; - for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) - { - if (g_settings.controller_types[i] != old_settings.controller_types[i]) - { - if (!System::IsShutdown() && !controllers_updated) - { - System::UpdateControllers(); - System::ResetControllers(); - UpdateSoftwareCursor(); - controllers_updated = true; - } - - OnControllerTypeChanged(i); - } - - if (!System::IsShutdown() && !controllers_updated) - { - System::UpdateControllerSettings(); - UpdateSoftwareCursor(); - } - } - - if (g_settings.multitap_mode != old_settings.multitap_mode) - System::UpdateMultitaps(); - - if (m_display && (g_settings.display_linear_filtering != old_settings.display_linear_filtering || - g_settings.display_integer_scaling != old_settings.display_integer_scaling || - g_settings.display_stretch != old_settings.display_stretch)) - { - m_display->SetDisplayLinearFiltering(g_settings.display_linear_filtering); - m_display->SetDisplayIntegerScaling(g_settings.display_integer_scaling); - m_display->SetDisplayStretch(g_settings.display_stretch); - OnDisplayInvalidated(); - } -} - void HostInterface::SetUserDirectoryToProgramDirectory() { std::string program_path(FileSystem::GetProgramPath()); @@ -1004,83 +315,6 @@ std::string HostInterface::GetGameMemoryCardPath(const char* game_code, u32 slot } } -bool HostInterface::GetBoolSettingValue(const char* section, const char* key, bool default_value /*= false*/) -{ - std::string value = GetStringSettingValue(section, key, ""); - if (value.empty()) - return default_value; - - std::optional bool_value = StringUtil::FromChars(value); - return bool_value.value_or(default_value); -} - -int HostInterface::GetIntSettingValue(const char* section, const char* key, int default_value /*= 0*/) -{ - std::string value = GetStringSettingValue(section, key, ""); - if (value.empty()) - return default_value; - - std::optional int_value = StringUtil::FromChars(value); - return int_value.value_or(default_value); -} - -float HostInterface::GetFloatSettingValue(const char* section, const char* key, float default_value /*= 0.0f*/) -{ - std::string value = GetStringSettingValue(section, key, ""); - if (value.empty()) - return default_value; - - std::optional float_value = StringUtil::FromChars(value); - return float_value.value_or(default_value); -} - -TinyString HostInterface::TranslateString(const char* context, const char* str, - const char* disambiguation /*= nullptr*/, int n /*= -1*/) const -{ - TinyString result(str); - if (n >= 0) - { - const std::string number = std::to_string(n); - result.Replace("%n", number.c_str()); - result.Replace("%Ln", number.c_str()); - } - return result; -} - -std::string HostInterface::TranslateStdString(const char* context, const char* str, - const char* disambiguation /*= nullptr*/, int n /*= -1*/) const -{ - std::string result(str); - if (n >= 0) - { - const std::string number = std::to_string(n); - // Made to mimick Qt's behaviour - // https://github.com/qt/qtbase/blob/255459250d450286a8c5c492dab3f6d3652171c9/src/corelib/kernel/qcoreapplication.cpp#L2099 - size_t percent_pos = 0; - size_t len = 0; - while ((percent_pos = result.find('%', percent_pos + len)) != std::string::npos) - { - len = 1; - if (percent_pos + len == result.length()) - break; - if (result[percent_pos + len] == 'L') - { - ++len; - if (percent_pos + len == result.length()) - break; - } - if (result[percent_pos + len] == 'n') - { - ++len; - result.replace(percent_pos, len, number); - len = number.length(); - } - } - } - - return result; -} - void HostInterface::ToggleSoftwareRendering() { if (System::IsShutdown() || g_settings.gpu_renderer == GPURenderer::Software) @@ -1088,10 +322,11 @@ void HostInterface::ToggleSoftwareRendering() const GPURenderer new_renderer = g_gpu->IsHardwareRenderer() ? GPURenderer::Software : g_settings.gpu_renderer; - AddKeyedFormattedOSDMessage("SoftwareRendering", 5.0f, TranslateString("OSDMessage", "Switching to %s renderer..."), - Settings::GetRendererDisplayName(new_renderer)); + Host::AddKeyedFormattedOSDMessage("SoftwareRendering", 5.0f, + Host::TranslateString("OSDMessage", "Switching to %s renderer..."), + Settings::GetRendererDisplayName(new_renderer)); System::RecreateGPU(new_renderer); - OnDisplayInvalidated(); + Host::InvalidateDisplay(); } void HostInterface::ModifyResolutionScale(s32 increment) @@ -1109,7 +344,7 @@ void HostInterface::ModifyResolutionScale(s32 increment) g_gpu->UpdateSettings(); g_gpu->ResetGraphicsAPIState(); System::ClearMemorySaveStates(); - OnDisplayInvalidated(); + Host::InvalidateDisplay(); } } @@ -1118,7 +353,7 @@ void HostInterface::UpdateSoftwareCursor() if (System::IsShutdown()) { SetMouseMode(false, false); - m_display->ClearSoftwareCursor(); + Host::GetHostDisplay()->ClearSoftwareCursor(); return; } @@ -1141,38 +376,12 @@ void HostInterface::UpdateSoftwareCursor() if (image && image->IsValid()) { - m_display->SetSoftwareCursor(image->GetPixels(), image->GetWidth(), image->GetHeight(), image->GetByteStride(), + Host::GetHostDisplay()->SetSoftwareCursor(image->GetPixels(), image->GetWidth(), image->GetHeight(), image->GetByteStride(), image_scale); } else { - m_display->ClearSoftwareCursor(); + Host::GetHostDisplay()->ClearSoftwareCursor(); } } -void HostInterface::RecreateSystem() -{ - Assert(!System::IsShutdown()); - - std::unique_ptr stream = ByteStream::CreateGrowableMemoryStream(nullptr, 8 * 1024); - if (!System::SaveState(stream.get(), 0) || !stream->SeekAbsolute(0)) - { - ReportError("Failed to save state before system recreation. Shutting down."); - DestroySystem(); - return; - } - - DestroySystem(); - - auto boot_params = std::make_shared(); - boot_params->state_stream = std::move(stream); - if (!BootSystem(std::move(boot_params))) - { - ReportError("Failed to boot system after recreation."); - return; - } - - System::ResetPerformanceCounters(); - System::ResetThrottler(); - OnDisplayInvalidated(); -} diff --git a/src/core/host_interface.h b/src/core/host_interface.h index 20d608a82..7a017d161 100644 --- a/src/core/host_interface.h +++ b/src/core/host_interface.h @@ -14,7 +14,6 @@ enum LOGLEVEL; -class AudioStream; class ByteStream; class CDImage; class HostDisplay; @@ -29,54 +28,15 @@ 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(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 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. - void AddOSDMessage(std::string message, float duration = 2.0f); - void AddKeyedOSDMessage(std::string key, std::string message, float duration = 2.0f); - void RemoveKeyedOSDMessage(std::string key); - 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; } @@ -109,27 +69,6 @@ public: /// 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 GetSettingStringList(const char* section, const char* key) = 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(); @@ -150,45 +89,9 @@ public: /// This is the APK for Android builds, or the program directory for standalone builds. virtual std::unique_ptr 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 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; @@ -207,15 +110,8 @@ protected: /// Updates software cursor state, based on controllers. void UpdateSoftwareCursor(); - bool SaveState(const char* filename); - void CreateAudioStream(); - - std::unique_ptr m_display; - std::unique_ptr m_audio_stream; std::string m_program_directory; std::string m_user_directory; }; -#define TRANSLATABLE(context, str) str - extern HostInterface* g_host_interface; diff --git a/src/core/host_interface_progress_callback.cpp b/src/core/host_interface_progress_callback.cpp index 897899d01..6b17dc91d 100644 --- a/src/core/host_interface_progress_callback.cpp +++ b/src/core/host_interface_progress_callback.cpp @@ -1,4 +1,5 @@ #include "host_interface_progress_callback.h" +#include "host.h" #include "common/log.h" Log_SetChannel(HostInterfaceProgressCallback); @@ -72,17 +73,16 @@ void HostInterfaceProgressCallback::DisplayDebugMessage(const char* message) { L 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); } diff --git a/src/core/host_settings.h b/src/core/host_settings.h index 8d70221ab..b35c9c385 100644 --- a/src/core/host_settings.h +++ b/src/core/host_settings.h @@ -18,13 +18,16 @@ double GetBaseDoubleSettingValue(const char* section, const char* key, double de std::vector 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& 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(); diff --git a/src/core/memory_card.cpp b/src/core/memory_card.cpp index d306bf4d7..51d1f04dd 100644 --- a/src/core/memory_card.cpp +++ b/src/core/memory_card.cpp @@ -3,6 +3,7 @@ #include "common/file_system.h" #include "common/log.h" #include "common/string_util.h" +#include "host.h" #include "host_interface.h" #include "system.h" #include "util/state_wrapper.h" @@ -273,8 +274,8 @@ std::unique_ptr 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 +310,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 +319,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; } diff --git a/src/core/pad.cpp b/src/core/pad.cpp index f1c04245e..d026f9a17 100644 --- a/src/core/pad.cpp +++ b/src/core/pad.cpp @@ -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(); } diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 93227ca3f..404eefb2a 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -1,14 +1,18 @@ #include "settings.h" +#include "cheevos.h" #include "common/assert.h" #include "common/file_system.h" +#include "common/log.h" #include "common/make_array.h" #include "common/string_util.h" +#include "host.h" #include "host_display.h" #include "host_interface.h" #include #include #include #include +Log_SetChannel(Settings); Settings g_settings; @@ -68,7 +72,16 @@ float SettingInfo::FloatStepValue() const return step_value ? StringUtil::FromChars(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 { @@ -239,11 +252,13 @@ void Settings::Load(SettingsInterface& si) display_show_status_indicators = si.GetBoolValue("Display", "ShowStatusIndicators", true); 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); - cdrom_readahead_sectors = static_cast(si.GetIntValue("CDROM", "ReadaheadSectors", DEFAULT_CDROM_READAHEAD_SECTORS)); + cdrom_readahead_sectors = + static_cast(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 +270,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); @@ -416,6 +431,7 @@ void Settings::Save(SettingsInterface& si) const si.SetBoolValue("Display", "ShowStatusIndicators", display_show_status_indicators); 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"); @@ -502,6 +518,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 (Cheevos::IsChallengeModeActive()) + { + 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 s_log_level_names = { {"None", "Error", "Warning", "Perf", "Info", "Verbose", "Dev", "Profile", "Debug", "Trace"}}; static std::array s_log_level_display_names = { @@ -655,14 +769,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,7 +924,7 @@ float Settings::GetDisplayAspectRatioValue() const { case DisplayAspectRatio::MatchWindow: { - const HostDisplay* display = g_host_interface->GetDisplay(); + const HostDisplay* display = Host::GetHostDisplay(); if (!display) return s_display_aspect_ratio_values[static_cast(DEFAULT_DISPLAY_ASPECT_RATIO)]; diff --git a/src/core/settings.h b/src/core/settings.h index 1c1c50214..2b9c0fa49 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -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,14 +54,14 @@ 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; @@ -70,7 +70,7 @@ struct Settings 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 +78,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 +88,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 +102,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; @@ -124,10 +124,11 @@ struct Settings bool display_show_status_indicators = true; 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_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 +137,20 @@ 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; struct DebugSettings { @@ -191,7 +192,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 controller_types{}; @@ -202,13 +203,13 @@ struct Settings 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 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; @@ -268,6 +269,8 @@ struct Settings void Load(SettingsInterface& si); void Save(SettingsInterface& si) const; + void FixIncompatibleSettings(bool display_osd_messages); + static std::optional ParseLogLevelName(const char* str); static const char* GetLogLevelName(LOGLEVEL level); static const char* GetLogLevelDisplayName(LOGLEVEL level); @@ -367,6 +370,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; diff --git a/src/core/spu.cpp b/src/core/spu.cpp index 4918c20d7..e04290ee6 100644 --- a/src/core/spu.cpp +++ b/src/core/spu.cpp @@ -3,6 +3,7 @@ #include "common/file_system.h" #include "common/log.h" #include "dma.h" +#include "host.h" #include "host_interface.h" #include "imgui.h" #include "interrupt_controller.h" @@ -30,11 +31,37 @@ void SPU::Initialize() "SPU Transfer", TRANSFER_TICKS_PER_HALFWORD, TRANSFER_TICKS_PER_HALFWORD, [](void* param, TickCount ticks, TickCount ticks_late) { static_cast(param)->ExecuteTransfer(ticks); }, this, false); - m_audio_stream = g_host_interface->GetAudioStream(); + 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 +1808,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 +1917,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 +1927,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; diff --git a/src/core/spu.h b/src/core/spu.h index 65be67f3c..5e8c8e5f4 100644 --- a/src/core/spu.h +++ b/src/core/spu.h @@ -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& 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 m_tick_event; std::unique_ptr m_transfer_event; std::unique_ptr m_dump_writer; - AudioStream* m_audio_stream = nullptr; + std::unique_ptr m_audio_stream; + std::unique_ptr 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; diff --git a/src/core/system.cpp b/src/core/system.cpp index a82873ab9..fb5ac6350 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -3,6 +3,7 @@ #include "bus.h" #include "cdrom.h" #include "cheats.h" +#include "cheevos.h" #include "common/error.h" #include "common/file_system.h" #include "common/log.h" @@ -16,6 +17,7 @@ #include "dma.h" #include "gpu.h" #include "gte.h" +#include "host.h" #include "host_display.h" #include "host_interface.h" #include "host_interface_progress_callback.h" @@ -34,6 +36,7 @@ #include "texture_replacements.h" #include "timers.h" #include "util/audio_stream.h" +#include "util/ini_settings_interface.h" #include "util/iso_reader.h" #include "util/state_wrapper.h" #include "xxhash.h" @@ -57,26 +60,22 @@ SystemBootParameters::SystemBootParameters(std::string filename_) : filename(std SystemBootParameters::~SystemBootParameters() = default; -#ifdef WITH_CHEEVOS -namespace Cheevos { -extern void Reset(); -extern bool DoState(StateWrapper& sw); -} // namespace Cheevos -#endif - -namespace System { - struct MemorySaveState { std::unique_ptr vram_texture; std::unique_ptr state_stream; }; +namespace System { +static bool InternalLoadState(ByteStream* state, bool update_display = true); +static bool InternalSaveState(ByteStream* state, u32 screenshot_size = 256); static bool SaveMemoryState(MemorySaveState* mss); static bool LoadMemoryState(const MemorySaveState& mss); static bool LoadEXE(const char* filename); +static std::string GetExecutableNameForImage(ISOReader& iso, bool strip_subdirectories); + /// Opens CD image, preloading if needed. static std::unique_ptr OpenCDImage(const char* path, Common::Error* error, bool force_preload, bool check_for_patches); @@ -84,11 +83,18 @@ static bool ReadExecutableFromImage(ISOReader& iso, std::string* out_executable_ std::vector* out_executable_data); static bool ShouldCheckForImagePatches(); +static void StallCPU(TickCount ticks); + +static bool InternalBoot(const SystemBootParameters& params); +static void InternalReset(); +static void InternalShutdown(); static bool DoLoadState(ByteStream* stream, bool force_software_renderer, bool update_display); static bool DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display, bool is_memory_state); static void DoRunFrame(); static bool CreateGPU(GPURenderer renderer); +static bool SaveUndoLoadState(); +static void SetRewinding(bool enabled); static bool SaveRewindState(); static void DoRewind(); @@ -99,15 +105,22 @@ static void DoMemorySaveStates(); static bool Initialize(bool force_software_renderer); +static bool UpdateGameSettingsLayer(); static void UpdateRunningGame(const char* path, CDImage* image); static bool CheckForSBIFile(CDImage* image); +static std::unique_ptr GetMemoryCardForSlot(u32 slot, MemoryCardType type); +} // namespace System -static State s_state = State::Shutdown; +static std::unique_ptr s_game_settings_interface; +static std::unique_ptr s_input_settings_interface; +static std::string s_input_profile_name; + +static System::State s_state = System::State::Shutdown; static std::atomic_bool s_startup_cancelled{false}; static ConsoleRegion s_region = ConsoleRegion::NTSC_U; -TickCount g_ticks_per_second = MASTER_CLOCK; -static TickCount s_max_slice_ticks = MASTER_CLOCK / 10; +TickCount System::g_ticks_per_second = System::MASTER_CLOCK; +static TickCount s_max_slice_ticks = System::MASTER_CLOCK / 10; static u32 s_frame_number = 1; static u32 s_internal_frame_number = 1; @@ -120,6 +133,12 @@ static float s_target_speed = 1.0f; static Common::Timer::Value s_frame_period = 0; static Common::Timer::Value s_next_frame_time = 0; +static bool m_frame_step_request = false; +static bool m_fast_forward_enabled = false; +static bool m_turbo_enabled = false; +static bool m_throttler_enabled = true; +static bool m_display_all_frames = true; + static float s_average_frame_time_accumulator = 0.0f; static float s_worst_frame_time_accumulator = 0.0f; @@ -140,6 +159,9 @@ static Threading::ThreadHandle s_cpu_thread_handle; static std::unique_ptr s_cheat_list; +// temporary save state, created when loading, used to undo load state +static std::unique_ptr m_undo_load_state; + static bool s_memory_saves_enabled = false; static std::deque s_rewind_states; @@ -150,16 +172,15 @@ static s32 s_rewind_save_counter = -1; static bool s_rewinding_first_save = false; static std::deque s_runahead_states; -static std::unique_ptr s_runahead_audio_stream; static bool s_runahead_replay_pending = false; static u32 s_runahead_frames = 0; -State GetState() +System::State System::GetState() { return s_state; } -void SetState(State new_state) +void System::SetState(State new_state) { if (s_state == new_state) return; @@ -172,53 +193,53 @@ void SetState(State new_state) CPU::ForceDispatcherExit(); } -bool IsRunning() +bool System::IsRunning() { return s_state == State::Running; } -bool IsPaused() +bool System::IsPaused() { return s_state == State::Paused; } -bool IsShutdown() +bool System::IsShutdown() { return s_state == State::Shutdown; } -bool IsValid() +bool System::IsValid() { return s_state != State::Shutdown && s_state != State::Starting; } -bool IsStartupCancelled() +bool System::IsStartupCancelled() { return s_startup_cancelled.load(); } -void CancelPendingStartup() +void System::CancelPendingStartup() { if (s_state == State::Starting) s_startup_cancelled.store(true); } -ConsoleRegion GetRegion() +ConsoleRegion System::GetRegion() { return s_region; } -bool IsPALRegion() +bool System::IsPALRegion() { return s_region == ConsoleRegion::PAL; } -TickCount GetMaxSliceTicks() +TickCount System::GetMaxSliceTicks() { return s_max_slice_ticks; } -void UpdateOverclock() +void System::UpdateOverclock() { g_ticks_per_second = ScaleTicksToOverclock(MASTER_CLOCK); s_max_slice_ticks = ScaleTicksToOverclock(MASTER_CLOCK / 10); @@ -229,90 +250,90 @@ void UpdateOverclock() UpdateThrottlePeriod(); } -u32 GetFrameNumber() +u32 System::GetFrameNumber() { return s_frame_number; } -u32 GetInternalFrameNumber() +u32 System::GetInternalFrameNumber() { return s_internal_frame_number; } -void FrameDone() +void System::FrameDone() { s_frame_number++; CPU::g_state.frame_done = true; CPU::g_state.downcount = 0; } -void IncrementInternalFrameNumber() +void System::IncrementInternalFrameNumber() { s_internal_frame_number++; } -const std::string& GetRunningPath() +const std::string& System::GetRunningPath() { return s_running_game_path; } -const std::string& GetRunningCode() +const std::string& System::GetRunningCode() { return s_running_game_code; } -const std::string& GetRunningTitle() +const std::string& System::GetRunningTitle() { return s_running_game_title; } -float GetFPS() +float System::GetFPS() { return s_fps; } -float GetVPS() +float System::GetVPS() { return s_vps; } -float GetEmulationSpeed() +float System::GetEmulationSpeed() { return s_speed; } -float GetAverageFrameTime() +float System::GetAverageFrameTime() { return s_average_frame_time; } -float GetWorstFrameTime() +float System::GetWorstFrameTime() { return s_worst_frame_time; } -float GetThrottleFrequency() +float System::GetThrottleFrequency() { return s_throttle_frequency; } -float GetCPUThreadUsage() +float System::GetCPUThreadUsage() { return s_cpu_thread_usage; } -float GetCPUThreadAverageTime() +float System::GetCPUThreadAverageTime() { return s_cpu_thread_time; } -bool IsExeFileName(const char* path) +bool System::IsExeFileName(const char* path) { const char* extension = std::strrchr(path, '.'); return (extension && (StringUtil::Strcasecmp(extension, ".exe") == 0 || StringUtil::Strcasecmp(extension, ".psexe") == 0)); } -bool IsPsfFileName(const char* path) +bool System::IsPsfFileName(const char* path) { const char* extension = std::strrchr(path, '.'); return (extension && (StringUtil::Strcasecmp(extension, ".psf") == 0 || StringUtil::Strcasecmp(extension, ".minipsf") == 0)); } -bool IsLoadableFilename(const char* path) +bool System::IsLoadableFilename(const char* path) { static constexpr auto extensions = make_array(".bin", ".cue", ".img", ".iso", ".chd", ".ecm", ".mds", // discs ".exe", ".psexe", // exes @@ -332,7 +353,7 @@ bool IsLoadableFilename(const char* path) return false; } -ConsoleRegion GetConsoleRegionForDiscRegion(DiscRegion region) +ConsoleRegion System::GetConsoleRegionForDiscRegion(DiscRegion region) { switch (region) { @@ -349,7 +370,7 @@ ConsoleRegion GetConsoleRegionForDiscRegion(DiscRegion region) } } -std::string GetGameCodeForPath(const char* image_path, bool fallback_to_hash) +std::string System::GetGameCodeForPath(const char* image_path, bool fallback_to_hash) { std::unique_ptr cdi = CDImage::Open(image_path, nullptr); if (!cdi) @@ -358,7 +379,7 @@ std::string GetGameCodeForPath(const char* image_path, bool fallback_to_hash) return GetGameCodeForImage(cdi.get(), fallback_to_hash); } -std::string GetGameCodeForImage(CDImage* cdi, bool fallback_to_hash) +std::string System::GetGameCodeForImage(CDImage* cdi, bool fallback_to_hash) { std::string code(GetExecutableNameForImage(cdi)); if (!code.empty()) @@ -389,7 +410,7 @@ std::string GetGameCodeForImage(CDImage* cdi, bool fallback_to_hash) return GetGameHashCodeForImage(cdi); } -std::string GetGameHashCodeForImage(CDImage* cdi) +std::string System::GetGameHashCodeForImage(CDImage* cdi) { ISOReader iso; if (!iso.Open(cdi, 1)) @@ -415,7 +436,7 @@ std::string GetGameHashCodeForImage(CDImage* cdi) return StringUtil::StdStringFromFormat("HASH-%" PRIX64, hash); } -static std::string GetExecutableNameForImage(ISOReader& iso, bool strip_subdirectories) +std::string System::GetExecutableNameForImage(ISOReader& iso, bool strip_subdirectories) { // Read SYSTEM.CNF std::vector system_cnf_data; @@ -502,7 +523,7 @@ static std::string GetExecutableNameForImage(ISOReader& iso, bool strip_subdirec return code; } -std::string GetExecutableNameForImage(CDImage* cdi) +std::string System::GetExecutableNameForImage(CDImage* cdi) { ISOReader iso; if (!iso.Open(cdi, 1)) @@ -511,7 +532,8 @@ std::string GetExecutableNameForImage(CDImage* cdi) return GetExecutableNameForImage(iso, true); } -bool ReadExecutableFromImage(ISOReader& iso, std::string* out_executable_name, std::vector* out_executable_data) +bool System::ReadExecutableFromImage(ISOReader& iso, std::string* out_executable_name, + std::vector* out_executable_data) { bool result = false; @@ -542,7 +564,8 @@ bool ReadExecutableFromImage(ISOReader& iso, std::string* out_executable_name, s return true; } -bool ReadExecutableFromImage(CDImage* cdi, std::string* out_executable_name, std::vector* out_executable_data) +bool System::ReadExecutableFromImage(CDImage* cdi, std::string* out_executable_name, + std::vector* out_executable_data) { ISOReader iso; if (!iso.Open(cdi, 1)) @@ -551,7 +574,7 @@ bool ReadExecutableFromImage(CDImage* cdi, std::string* out_executable_name, std return ReadExecutableFromImage(iso, out_executable_name, out_executable_data); } -DiscRegion GetRegionForCode(std::string_view code) +DiscRegion System::GetRegionForCode(std::string_view code) { std::string prefix; for (size_t pos = 0; pos < code.length(); pos++) @@ -573,7 +596,7 @@ DiscRegion GetRegionForCode(std::string_view code) return DiscRegion::Other; } -DiscRegion GetRegionFromSystemArea(CDImage* cdi) +DiscRegion System::GetRegionFromSystemArea(CDImage* cdi) { // The license code is on sector 4 of the disc. u8 sector[CDImage::DATA_SECTOR_SIZE]; @@ -595,7 +618,7 @@ DiscRegion GetRegionFromSystemArea(CDImage* cdi) return DiscRegion::Other; } -DiscRegion GetRegionForImage(CDImage* cdi) +DiscRegion System::GetRegionForImage(CDImage* cdi) { DiscRegion system_area_region = GetRegionFromSystemArea(cdi); if (system_area_region != DiscRegion::Other) @@ -608,7 +631,7 @@ DiscRegion GetRegionForImage(CDImage* cdi) return GetRegionForCode(code); } -DiscRegion GetRegionForExe(const char* path) +DiscRegion System::GetRegionForExe(const char* path) { auto fp = FileSystem::OpenManagedCFile(path, "rb"); if (!fp) @@ -621,7 +644,7 @@ DiscRegion GetRegionForExe(const char* path) return BIOS::GetPSExeDiscRegion(header); } -DiscRegion GetRegionForPsf(const char* path) +DiscRegion System::GetRegionForPsf(const char* path) { PSFLoader::File psf; if (!psf.Load(path)) @@ -630,7 +653,7 @@ DiscRegion GetRegionForPsf(const char* path) return psf.GetRegion(); } -std::optional GetRegionForPath(const char* image_path) +std::optional System::GetRegionForPath(const char* image_path) { if (IsExeFileName(image_path)) return GetRegionForExe(image_path); @@ -644,7 +667,20 @@ std::optional GetRegionForPath(const char* image_path) return GetRegionForImage(cdi.get()); } -bool RecreateGPU(GPURenderer renderer, bool update_display /* = true*/) +std::string System::GetGameSettingsPath(const std::string_view& game_serial) +{ + std::string sanitized_serial(game_serial); + Path::SanitizeFileName(sanitized_serial); + +#if 0 + // FIXME + return Path::Combine(EmuFolders::GameSettings, fmt::format("{}.ini", sanitized_serial)); +#else + return {}; +#endif +} + +bool System::RecreateGPU(GPURenderer renderer, bool update_display /* = true*/) { ClearMemorySaveStates(); g_gpu->RestoreGraphicsAPIState(); @@ -663,9 +699,9 @@ bool RecreateGPU(GPURenderer renderer, bool update_display /* = true*/) if (!CreateGPU(renderer)) { if (!IsStartupCancelled()) - g_host_interface->ReportError("Failed to recreate GPU."); + Host::ReportErrorAsync("Error", "Failed to recreate GPU."); - System::Shutdown(); + InternalShutdown(); return false; } @@ -682,7 +718,8 @@ bool RecreateGPU(GPURenderer renderer, bool update_display /* = true*/) return true; } -std::unique_ptr OpenCDImage(const char* path, Common::Error* error, bool force_preload, bool check_for_patches) +std::unique_ptr System::OpenCDImage(const char* path, Common::Error* error, bool force_preload, + bool check_for_patches) { std::unique_ptr media = CDImage::Open(path, error); if (!media) @@ -692,9 +729,8 @@ std::unique_ptr OpenCDImage(const char* path, Common::Error* error, boo { if (media->HasSubImages() && media->GetSubImageCount() > 1) { - g_host_interface->AddFormattedOSDMessage( - 15.0f, - g_host_interface->TranslateString("OSDMessage", "CD image preloading not available for multi-disc image '%s'"), + Host::AddFormattedOSDMessage( + 15.0f, Host::TranslateString("OSDMessage", "CD image preloading not available for multi-disc image '%s'"), FileSystem::GetDisplayNameFromPath(media->GetFileName()).c_str()); } else @@ -712,9 +748,8 @@ std::unique_ptr OpenCDImage(const char* path, Common::Error* error, boo } else if (res != CDImage::PrecacheResult::Success) { - g_host_interface->AddOSDMessage( - g_host_interface->TranslateStdString("OSDMessage", "Precaching CD image failed, it may be unreliable."), - 15.0f); + Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "Precaching CD image failed, it may be unreliable."), + 15.0f); } } } @@ -728,10 +763,8 @@ std::unique_ptr OpenCDImage(const char* path, Common::Error* error, boo media = CDImage::OverlayPPFPatch(ppf_filename.c_str(), std::move(media)); if (!media) { - g_host_interface->AddFormattedOSDMessage( - 30.0f, - g_host_interface->TranslateString("OSDMessage", - "Failed to apply ppf patch from '%s', using unpatched image."), + Host::AddFormattedOSDMessage( + 30.0f, Host::TranslateString("OSDMessage", "Failed to apply ppf patch from '%s', using unpatched image."), ppf_filename.c_str()); return OpenCDImage(path, error, force_preload, false); } @@ -741,12 +774,280 @@ std::unique_ptr OpenCDImage(const char* path, Common::Error* error, boo return media; } -bool ShouldCheckForImagePatches() +bool System::ShouldCheckForImagePatches() { - return g_host_interface->GetBoolSettingValue("CDROM", "LoadImagePatches", false); + return Host::GetBoolSettingValue("CDROM", "LoadImagePatches", false); } -bool Boot(const SystemBootParameters& params) +void System::LoadSettings(bool display_osd_messages) +{ + std::unique_lock lock = Host::GetSettingsLock(); + SettingsInterface& si = *Host::GetSettingsInterface(); + g_settings.Load(si); + Host::LoadSettings(si, lock); + + g_settings.FixIncompatibleSettings(display_osd_messages); +} + +void System::SetDefaultSettings(SettingsInterface& si) +{ + Settings temp; + temp.Save(si); +} + +void System::ApplySettings(bool display_osd_messages) +{ + Log_DevPrint("Applying settings..."); + + const Settings old_config(std::move(g_settings)); + g_settings = Settings(); + LoadSettings(display_osd_messages); + + // no need to check for changes when we're not running + if (IsValid()) + { + CheckForSettingsChanges(old_config); + Host::CheckForSettingsChanges(old_config); + } +} + +bool System::ReloadGameSettings(bool display_osd_messages) +{ + if (!UpdateGameSettingsLayer()) + return false; + + ApplySettings(display_osd_messages); + return true; +} + +bool System::UpdateGameSettingsLayer() +{ + std::unique_ptr new_interface; + if (!s_running_game_code.empty()) + { + std::string filename(GetGameSettingsPath(s_running_game_code)); + if (FileSystem::FileExists(filename.c_str())) + { + Log_InfoPrintf("Loading game settings from '%s'...", filename.c_str()); + new_interface = std::make_unique(std::move(filename)); + if (!new_interface->Load()) + { + Log_ErrorPrintf("Failed to parse game settings ini '%s'", new_interface->GetFileName().c_str()); + new_interface.reset(); + } + } + else + { + Log_InfoPrintf("No game settings found (tried '%s')", filename.c_str()); + } + } + + std::string input_profile_name; + if (new_interface) + new_interface->GetStringValue("EmuCore", "InputProfileName", &input_profile_name); + + if (!s_game_settings_interface && !new_interface && s_input_profile_name == input_profile_name) + return false; + + Host::Internal::SetGameSettingsLayer(new_interface.get()); + s_game_settings_interface = std::move(new_interface); + + // TODO: Move me to frontend? +#if 0 + std::unique_ptr input_interface; + if (!input_profile_name.empty()) + { + const std::string filename(GetInputProfilePath(input_profile_name)); + if (FileSystem::FileExists(filename.c_str())) + { + Log_InfoPrintf("Loading input profile from '%s'...", filename.c_str()); + input_interface = std::make_unique(std::move(filename)); + if (!input_interface->Load()) + { + Log_ErrorPrintf("Failed to parse input profile ini '%s'", input_interface->GetFileName().c_str()); + input_interface.reset(); + input_profile_name = {}; + } + } + else + { + Log_InfoPrintf("No input profile found (tried '%s')", filename.c_str()); + input_profile_name = {}; + } + } + + Host::Internal::SetInputSettingsLayer(input_interface.get()); + s_input_settings_interface = std::move(input_interface); + s_input_profile_name = std::move(input_profile_name); +#endif + + return true; +} + +bool System::BootSystem(std::shared_ptr parameters) +{ + Host::OnSystemStarting(); + + if (!parameters->state_stream) + { + if (parameters->filename.empty()) + Log_InfoPrintf("Boot Filename: "); + else + Log_InfoPrintf("Boot Filename: %s", parameters->filename.c_str()); + } + + // In Challenge mode, do not allow loading a save state under any circumstances + // If it's present, drop it + if (Cheevos::IsChallengeModeActive()) + parameters->state_stream.reset(); + + if (!Host::AcquireHostDisplay()) + { + Host::ReportErrorAsync(Host::TranslateString("System", "Error"), + Host::TranslateString("System", "Failed to acquire host display.")); + Host::OnSystemDestroyed(); + return false; + } + + // set host display settings + HostDisplay* display = Host::GetHostDisplay(); + display->SetDisplayLinearFiltering(g_settings.display_linear_filtering); + display->SetDisplayIntegerScaling(g_settings.display_integer_scaling); + display->SetDisplayStretch(g_settings.display_stretch); + if (g_settings.display_post_processing && !display->SetPostProcessingChain(g_settings.display_post_process_chain)) + Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "Failed to load post processing shader chain."), 20.0f); + + if (!System::InternalBoot(*parameters)) + { + if (!System::IsStartupCancelled()) + { + Host::ReportErrorAsync( + Host::TranslateString("System", "Error"), + Host::TranslateString("System", "System failed to boot. The log may contain more information.")); + } + + Host::OnSystemDestroyed(); + Host::ReleaseHostDisplay(); + return false; + } + + g_host_interface->UpdateSoftwareCursor(); + g_spu.GetOutputStream()->PauseOutput(false); + Host::OnSystemStarted(); + return true; +} + +void System::ResetSystem() +{ + InternalReset(); + ResetPerformanceCounters(); + ResetThrottler(); + Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "System reset.")); +} + +void System::PauseSystem(bool paused) +{ + if (paused == IsPaused() || !IsValid()) + return; + + SetState(paused ? State::Paused : State::Running); + if (!paused) + g_spu.GetOutputStream()->EmptyBuffers(); + g_spu.GetOutputStream()->PauseOutput(paused); + + if (paused) + { + Host::OnSystemPaused(); + } + else + { + Host::OnSystemResumed(); + ResetPerformanceCounters(); + ResetThrottler(); + } +} + +void System::DestroySystem() +{ + if (!IsValid()) + return; + +#if 0 + // Restore present-all-frames behavior. + if (m_display) + m_display->SetDisplayMaxFPS(0.0f); +#endif + + InternalShutdown(); + g_host_interface->UpdateSoftwareCursor(); + Host::ReleaseHostDisplay(); + + Host::OnSystemDestroyed(); +} + +bool System::LoadState(const char* filename) +{ + std::unique_ptr stream = ByteStream::OpenFile(filename, BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED); + if (!stream) + return false; + + Host::AddFormattedOSDMessage(5.0f, Host::TranslateString("OSDMessage", "Loading state from '%s'..."), filename); + + if (!IsValid()) + { + SaveUndoLoadState(); + + if (!InternalLoadState(stream.get())) + { + Host::ReportFormattedErrorAsync("Load State Error", + Host::TranslateString("OSDMessage", "Loading state from '%s' failed. Resetting."), + filename); + + if (m_undo_load_state) + UndoLoadState(); + + return false; + } + } + else + { + auto boot_params = std::make_shared(); + boot_params->state_stream = std::move(stream); + if (!BootSystem(std::move(boot_params))) + return false; + } + + ResetPerformanceCounters(); + ResetThrottler(); + Host::RenderDisplay(); + return true; +} + +bool System::SaveState(const char* filename) +{ + std::unique_ptr stream = + ByteStream::OpenFile(filename, BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_WRITE | BYTESTREAM_OPEN_TRUNCATE | + BYTESTREAM_OPEN_ATOMIC_UPDATE | BYTESTREAM_OPEN_STREAMED); + if (!stream) + return false; + + const bool result = InternalSaveState(stream.get()); + if (!result) + { + Host::ReportFormattedErrorAsync(Host::TranslateString("OSDMessage", "Save State"), + Host::TranslateString("OSDMessage", "Saving state to '%s' failed."), filename); + stream->Discard(); + } + else + { + Host::AddFormattedOSDMessage(5.0f, Host::TranslateString("OSDMessage", "State saved to '%s'."), filename); + stream->Commit(); + } + + return result; +} + +bool System::InternalBoot(const SystemBootParameters& params) { Assert(s_state == State::Shutdown); s_state = State::Starting; @@ -757,7 +1058,7 @@ bool Boot(const SystemBootParameters& params) { if (!DoLoadState(params.state_stream.get(), params.force_software_renderer, true)) { - Shutdown(); + InternalShutdown(); return false; } @@ -795,9 +1096,9 @@ bool Boot(const SystemBootParameters& params) media = OpenCDImage(params.filename.c_str(), &error, params.load_image_to_ram, ShouldCheckForImagePatches()); if (!media) { - g_host_interface->ReportFormattedError("Failed to load CD image '%s': %s", params.filename.c_str(), - error.GetCodeAndMessage().GetCharArray()); - Shutdown(); + Host::ReportErrorAsync("Error", fmt::format("Failed to load CD image '{}': {}", + Path::GetFileName(params.filename), error.GetCodeAndMessage())); + InternalShutdown(); return false; } @@ -833,9 +1134,9 @@ bool Boot(const SystemBootParameters& params) std::optional bios_image = g_host_interface->GetBIOSImage(s_region); if (!bios_image) { - g_host_interface->ReportFormattedError(g_host_interface->TranslateString("System", "Failed to load %s BIOS."), - Settings::GetConsoleRegionName(s_region)); - Shutdown(); + Host::ReportFormattedErrorAsync("Error", Host::TranslateString("System", "Failed to load %s BIOS."), + Settings::GetConsoleRegionName(s_region)); + InternalShutdown(); return false; } @@ -845,23 +1146,23 @@ bool Boot(const SystemBootParameters& params) // Check for SBI. if (!CheckForSBIFile(media.get())) { - Shutdown(); + InternalShutdown(); return false; } // Switch subimage. if (media && params.media_playlist_index != 0 && !media->SwitchSubImage(params.media_playlist_index, &error)) { - g_host_interface->ReportFormattedError("Failed to switch to subimage %u in '%s': %s", params.media_playlist_index, - params.filename.c_str(), error.GetCodeAndMessage().GetCharArray()); - Shutdown(); + Host::ReportFormattedErrorAsync("Error", "Failed to switch to subimage %u in '%s': %s", params.media_playlist_index, + params.filename.c_str(), error.GetCodeAndMessage().GetCharArray()); + InternalShutdown(); return false; } // Component setup. if (!Initialize(params.force_software_renderer)) { - Shutdown(); + InternalShutdown(); return false; } @@ -869,7 +1170,7 @@ bool Boot(const SystemBootParameters& params) UpdateControllers(); UpdateMemoryCardTypes(); UpdateMultitaps(); - Reset(); + InternalReset(); // Enable tty by patching bios. const BIOS::Hash bios_hash = BIOS::GetHash(*bios_image); @@ -879,14 +1180,14 @@ bool Boot(const SystemBootParameters& params) // Load EXE late after BIOS. if (exe_boot && !LoadEXE(params.filename.c_str())) { - g_host_interface->ReportFormattedError("Failed to load EXE file '%s'", params.filename.c_str()); - Shutdown(); + Host::ReportFormattedErrorAsync("Error", "Failed to load EXE file '%s'", params.filename.c_str()); + InternalShutdown(); return false; } else if (psf_boot && !PSFLoader::Load(params.filename.c_str())) { - g_host_interface->ReportFormattedError("Failed to load PSF file '%s'", params.filename.c_str()); - Shutdown(); + Host::ReportFormattedErrorAsync("Error", "Failed to load PSF file '%s'", params.filename.c_str()); + InternalShutdown(); return false; } @@ -904,7 +1205,7 @@ bool Boot(const SystemBootParameters& params) return true; } -bool Initialize(bool force_software_renderer) +bool System::Initialize(bool force_software_renderer) { g_ticks_per_second = ScaleTicksToOverclock(MASTER_CLOCK); s_max_slice_ticks = ScaleTicksToOverclock(MASTER_CLOCK / 10); @@ -954,7 +1255,7 @@ bool Initialize(bool force_software_renderer) // Was startup cancelled? (e.g. shading compilers took too long and the user closed the application) if (IsStartupCancelled()) { - Shutdown(); + InternalShutdown(); return false; } @@ -975,35 +1276,32 @@ bool Initialize(bool force_software_renderer) if (g_settings.cpu_overclock_active) { - g_host_interface->AddFormattedOSDMessage( + Host::AddFormattedOSDMessage( WARNING_DURATION, - g_host_interface->TranslateString("OSDMessage", - "CPU clock speed is set to %u%% (%u / %u). This may result in instability."), + Host::TranslateString("OSDMessage", "CPU clock speed is set to %u%% (%u / %u). This may result in instability."), g_settings.GetCPUOverclockPercent(), g_settings.cpu_overclock_numerator, g_settings.cpu_overclock_denominator); } if (g_settings.cdrom_read_speedup > 1) { - g_host_interface->AddFormattedOSDMessage( + Host::AddFormattedOSDMessage( WARNING_DURATION, - g_host_interface->TranslateString( - "OSDMessage", "CD-ROM read speedup set to %ux (effective speed %ux). This may result in instability."), + Host::TranslateString("OSDMessage", + "CD-ROM read speedup set to %ux (effective speed %ux). This may result in instability."), g_settings.cdrom_read_speedup, g_settings.cdrom_read_speedup * 2); } if (g_settings.cdrom_seek_speedup != 1) { if (g_settings.cdrom_seek_speedup == 0) { - g_host_interface->AddOSDMessage( - g_host_interface->TranslateStdString("OSDMessage", - "CD-ROM seek speedup set to instant. This may result in instability."), + Host::AddOSDMessage( + Host::TranslateStdString("OSDMessage", "CD-ROM seek speedup set to instant. This may result in instability."), WARNING_DURATION); } else { - g_host_interface->AddFormattedOSDMessage( + Host::AddFormattedOSDMessage( WARNING_DURATION, - g_host_interface->TranslateString("OSDMessage", - "CD-ROM seek speedup set to %ux. This may result in instability."), + Host::TranslateString("OSDMessage", "CD-ROM seek speedup set to %ux. This may result in instability."), g_settings.cdrom_seek_speedup); } } @@ -1015,7 +1313,7 @@ bool Initialize(bool force_software_renderer) return true; } -void Shutdown() +void System::InternalShutdown() { if (s_state == State::Shutdown) return; @@ -1023,7 +1321,6 @@ void Shutdown() s_cpu_thread_usage = {}; ClearMemorySaveStates(); - s_runahead_audio_stream.reset(); g_texture_replacements.Shutdown(); @@ -1047,10 +1344,70 @@ void Shutdown() s_cheat_list.reset(); s_state = State::Shutdown; - g_host_interface->OnRunningGameChanged(s_running_game_path, nullptr, s_running_game_code, s_running_game_title); + Host::OnGameChanged(s_running_game_path, s_running_game_code, s_running_game_title); } -bool CreateGPU(GPURenderer renderer) +void System::Execute() +{ + while (System::IsRunning()) + { + if (m_display_all_frames) + System::RunFrame(); + else + System::RunFrames(); + + // this can shut us down + Host::PumpMessagesOnCPUThread(); + if (!IsValid()) + return; + + if (m_frame_step_request) + { + m_frame_step_request = false; + PauseSystem(true); + } + + Host::RenderDisplay(); + + System::UpdatePerformanceCounters(); + + if (m_throttler_enabled) + System::Throttle(); + } +} + +void System::RecreateSystem() +{ + Assert(!IsShutdown()); + + const bool was_paused = System::IsPaused(); + std::unique_ptr stream = ByteStream::CreateGrowableMemoryStream(nullptr, 8 * 1024); + if (!System::InternalSaveState(stream.get(), 0) || !stream->SeekAbsolute(0)) + { + Host::ReportErrorAsync("Error", "Failed to save state before system recreation. Shutting down."); + DestroySystem(); + return; + } + + DestroySystem(); + + auto boot_params = std::make_shared(); + boot_params->state_stream = std::move(stream); + if (!BootSystem(std::move(boot_params))) + { + Host::ReportErrorAsync("Error", "Failed to boot system after recreation."); + return; + } + + ResetPerformanceCounters(); + ResetThrottler(); + Host::RenderDisplay(); + + if (was_paused) + PauseSystem(true); +} + +bool System::CreateGPU(GPURenderer renderer) { switch (renderer) { @@ -1077,25 +1434,24 @@ bool CreateGPU(GPURenderer renderer) break; } - if (!g_gpu || !g_gpu->Initialize(g_host_interface->GetDisplay())) + if (!g_gpu || !g_gpu->Initialize(Host::GetHostDisplay())) { Log_ErrorPrintf("Failed to initialize %s renderer, falling back to software renderer", Settings::GetRendererName(renderer)); - g_host_interface->AddFormattedOSDMessage( + Host::AddFormattedOSDMessage( 30.0f, - g_host_interface->TranslateString("OSDMessage", - "Failed to initialize %s renderer, falling back to software renderer."), + Host::TranslateString("OSDMessage", "Failed to initialize %s renderer, falling back to software renderer."), Settings::GetRendererName(renderer)); g_gpu.reset(); g_gpu = GPU::CreateSoftwareRenderer(); - if (!g_gpu->Initialize(g_host_interface->GetDisplay())) + if (!g_gpu->Initialize(Host::GetHostDisplay())) return false; } return true; } -bool DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display, bool is_memory_state) +bool System::DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_display, bool is_memory_state) { if (!sw.DoMarker("System")) return false; @@ -1170,10 +1526,8 @@ bool DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_di (cpu_overclock_active && (g_settings.cpu_overclock_numerator != cpu_overclock_numerator || g_settings.cpu_overclock_denominator != cpu_overclock_denominator)))) { - g_host_interface->AddFormattedOSDMessage( - 10.0f, - g_host_interface->TranslateString("OSDMessage", - "WARNING: CPU overclock (%u%%) was different in save state (%u%%)."), + Host::AddFormattedOSDMessage( + 10.0f, Host::TranslateString("OSDMessage", "WARNING: CPU overclock (%u%%) was different in save state (%u%%)."), g_settings.cpu_overclock_enable ? g_settings.GetCPUOverclockPercent() : 100u, cpu_overclock_active ? Settings::CPUOverclockFractionToPercent(cpu_overclock_numerator, cpu_overclock_denominator) : @@ -1211,7 +1565,7 @@ bool DoState(StateWrapper& sw, HostDisplayTexture** host_texture, bool update_di return !sw.HasError(); } -void Reset() +void System::InternalReset() { if (IsShutdown()) return; @@ -1245,7 +1599,7 @@ void Reset() g_gpu->ResetGraphicsAPIState(); } -bool LoadState(ByteStream* state, bool update_display) +bool System::InternalLoadState(ByteStream* state, bool update_display) { if (IsShutdown()) return false; @@ -1253,7 +1607,7 @@ bool LoadState(ByteStream* state, bool update_display) return DoLoadState(state, false, update_display); } -bool DoLoadState(ByteStream* state, bool force_software_renderer, bool update_display) +bool System::DoLoadState(ByteStream* state, bool force_software_renderer, bool update_display) { SAVE_STATE_HEADER header; if (!state->Read2(&header, sizeof(header))) @@ -1264,18 +1618,18 @@ bool DoLoadState(ByteStream* state, bool force_software_renderer, bool update_di if (header.version < SAVE_STATE_MINIMUM_VERSION) { - g_host_interface->ReportFormattedError( - g_host_interface->TranslateString("System", - "Save state is incompatible: minimum version is %u but state is version %u."), + Host::ReportFormattedErrorAsync( + "Error", + Host::TranslateString("System", "Save state is incompatible: minimum version is %u but state is version %u."), SAVE_STATE_MINIMUM_VERSION, header.version); return false; } if (header.version > SAVE_STATE_VERSION) { - g_host_interface->ReportFormattedError( - g_host_interface->TranslateString("System", - "Save state is incompatible: maximum version is %u but state is version %u."), + Host::ReportFormattedErrorAsync( + "Error", + Host::TranslateString("System", "Save state is incompatible: maximum version is %u but state is version %u."), SAVE_STATE_VERSION, header.version); return false; } @@ -1305,17 +1659,17 @@ bool DoLoadState(ByteStream* state, bool force_software_renderer, bool update_di { if (old_media) { - g_host_interface->AddFormattedOSDMessage( + Host::AddFormattedOSDMessage( 30.0f, - g_host_interface->TranslateString("OSDMessage", "Failed to open CD image from save state '%s': %s. Using " - "existing image '%s', this may result in instability."), + Host::TranslateString("OSDMessage", "Failed to open CD image from save state '%s': %s. Using " + "existing image '%s', this may result in instability."), media_filename.c_str(), error.GetCodeAndMessage().GetCharArray(), old_media->GetFileName().c_str()); media = std::move(old_media); } else { - g_host_interface->ReportFormattedError( - g_host_interface->TranslateString("System", "Failed to open CD image '%s' used by save state: %s."), + Host::ReportFormattedErrorAsync( + "Error", Host::TranslateString("System", "Failed to open CD image '%s' used by save state: %s."), media_filename.c_str(), error.GetCodeAndMessage().GetCharArray()); return false; } @@ -1332,9 +1686,9 @@ bool DoLoadState(ByteStream* state, bool force_software_renderer, bool update_di (media->HasSubImages() && media->GetCurrentSubImage() != header.media_subimage_index && !media->SwitchSubImage(header.media_subimage_index, &error))) { - g_host_interface->ReportFormattedError( - g_host_interface->TranslateString("System", - "Failed to switch to subimage %u in CD image '%s' used by save state: %s."), + Host::ReportFormattedErrorAsync( + "Error", + Host::TranslateString("System", "Failed to switch to subimage %u in CD image '%s' used by save state: %s."), header.media_subimage_index + 1u, media_filename.c_str(), error.GetCodeAndMessage().GetCharArray()); return false; } @@ -1357,7 +1711,7 @@ bool DoLoadState(ByteStream* state, bool force_software_renderer, bool update_di UpdateControllers(); UpdateMemoryCardTypes(); UpdateMultitaps(); - Reset(); + InternalReset(); } else { @@ -1374,7 +1728,7 @@ bool DoLoadState(ByteStream* state, bool force_software_renderer, bool update_di if (header.data_compression_type != 0) { - g_host_interface->ReportFormattedError("Unknown save state compression type %u", header.data_compression_type); + Host::ReportFormattedErrorAsync("Error", "Unknown save state compression type %u", header.data_compression_type); return false; } @@ -1388,11 +1742,11 @@ bool DoLoadState(ByteStream* state, bool force_software_renderer, bool update_di if (s_state == State::Starting) s_state = State::Running; - g_host_interface->GetAudioStream()->EmptyBuffers(); + g_spu.GetOutputStream()->EmptyBuffers(); return true; } -bool SaveState(ByteStream* state, u32 screenshot_size /* = 256 */) +bool System::InternalSaveState(ByteStream* state, u32 screenshot_size /* = 256 */) { if (IsShutdown()) return false; @@ -1423,7 +1777,7 @@ bool SaveState(ByteStream* state, u32 screenshot_size /* = 256 */) if (screenshot_size > 0) { // assume this size is the width - HostDisplay* display = g_host_interface->GetDisplay(); + HostDisplay* display = Host::GetHostDisplay(); const float display_aspect_ratio = display->GetDisplayAspectRatio(); const u32 screenshot_width = screenshot_size; const u32 screenshot_height = @@ -1493,7 +1847,7 @@ bool SaveState(ByteStream* state, u32 screenshot_size /* = 256 */) return true; } -void SingleStepCPU() +void System::SingleStepCPU() { const u32 old_frame_number = s_frame_number; @@ -1511,7 +1865,7 @@ void SingleStepCPU() g_gpu->ResetGraphicsAPIState(); } -void DoRunFrame() +void System::DoRunFrame() { g_gpu->RestoreGraphicsAPIState(); @@ -1551,7 +1905,7 @@ void DoRunFrame() g_gpu->ResetGraphicsAPIState(); } -void RunFrame() +void System::RunFrame() { s_frame_timer.Reset(); @@ -1572,24 +1926,18 @@ void RunFrame() DoMemorySaveStates(); } -float GetTargetSpeed() +float System::GetTargetSpeed() { return s_target_speed; } -void SetTargetSpeed(float speed) -{ - s_target_speed = speed; - UpdateThrottlePeriod(); -} - -void SetThrottleFrequency(float frequency) +void System::SetThrottleFrequency(float frequency) { s_throttle_frequency = frequency; UpdateThrottlePeriod(); } -void UpdateThrottlePeriod() +void System::UpdateThrottlePeriod() { if (s_target_speed > std::numeric_limits::epsilon()) { @@ -1605,15 +1953,15 @@ void UpdateThrottlePeriod() ResetThrottler(); } -void ResetThrottler() +void System::ResetThrottler() { s_next_frame_time = Common::Timer::GetCurrentValue(); } -void Throttle() +void System::Throttle() { // Reset the throttler on audio buffer overflow, so we don't end up out of phase. - if (g_host_interface->GetAudioStream()->DidUnderflow() && s_target_speed >= 1.0f) + if (g_spu.GetOutputStream()->DidUnderflow() && s_target_speed >= 1.0f) { Log_VerbosePrintf("Audio buffer underflowed, resetting throttler"); ResetThrottler(); @@ -1646,7 +1994,7 @@ void Throttle() } } -void RunFrames() +void System::RunFrames() { // If we're running more than this in a single loop... we're in for a bad time. const u32 max_frames_to_run = 2; @@ -1668,7 +2016,7 @@ void RunFrames() Log_VerbosePrintf("Ran %u frames in a single host frame", frames_run); } -void UpdatePerformanceCounters() +void System::UpdatePerformanceCounters() { const float frame_time = static_cast(s_frame_timer.GetTimeMilliseconds()); s_average_frame_time_accumulator += frame_time; @@ -1677,7 +2025,7 @@ void UpdatePerformanceCounters() // update fps counter const Common::Timer::Value now_ticks = Common::Timer::GetCurrentValue(); const Common::Timer::Value ticks_diff = now_ticks - s_fps_timer.GetStartValue(); - const float time = Common::Timer::ConvertValueToSeconds(ticks_diff); + const float time = static_cast(Common::Timer::ConvertValueToSeconds(ticks_diff)); if (time < 1.0f) return; @@ -1716,10 +2064,10 @@ void UpdatePerformanceCounters() Log_VerbosePrintf("FPS: %.2f VPS: %.2f CPU: %.2f Average: %.2fms Worst: %.2fms", s_fps, s_vps, s_cpu_thread_usage, s_average_frame_time, s_worst_frame_time); - g_host_interface->OnSystemPerformanceCountersUpdated(); + Host::OnPerformanceMetricsUpdated(); } -void ResetPerformanceCounters() +void System::ResetPerformanceCounters() { s_last_frame_number = s_frame_number; s_last_internal_frame_number = s_internal_frame_number; @@ -1733,6 +2081,166 @@ void ResetPerformanceCounters() ResetThrottler(); } +void System::UpdateSpeedLimiterState() +{ + HostDisplay* display = Host::GetHostDisplay(); + + float target_speed = m_turbo_enabled ? + g_settings.turbo_speed : + (m_fast_forward_enabled ? g_settings.fast_forward_speed : g_settings.emulation_speed); + m_throttler_enabled = (target_speed != 0.0f); + m_display_all_frames = !m_throttler_enabled || g_settings.display_all_frames; + + bool syncing_to_host = false; + if (g_settings.sync_to_host_refresh_rate && g_settings.audio_resampling && target_speed == 1.0f && IsRunning()) + { + float host_refresh_rate; + if (display->GetHostRefreshRate(&host_refresh_rate)) + { + const float ratio = host_refresh_rate / System::GetThrottleFrequency(); + syncing_to_host = (ratio >= 0.95f && ratio <= 1.05f); + Log_InfoPrintf("Refresh rate: Host=%fhz Guest=%fhz Ratio=%f - %s", host_refresh_rate, + System::GetThrottleFrequency(), ratio, syncing_to_host ? "can sync" : "can't sync"); + if (syncing_to_host) + target_speed *= ratio; + } + } + + const bool is_non_standard_speed = (std::abs(target_speed - 1.0f) > 0.05f); + const bool audio_sync_enabled = + !IsRunning() || (m_throttler_enabled && g_settings.audio_sync_enabled && !is_non_standard_speed); + const bool video_sync_enabled = + !IsRunning() || (m_throttler_enabled && g_settings.video_sync_enabled && !is_non_standard_speed); + const float max_display_fps = (!IsRunning() || m_throttler_enabled) ? 0.0f : g_settings.display_max_fps; + Log_InfoPrintf("Target speed: %f%%", target_speed * 100.0f); + Log_InfoPrintf("Syncing to %s%s", audio_sync_enabled ? "audio" : "", + (audio_sync_enabled && video_sync_enabled) ? " and video" : (video_sync_enabled ? "video" : "")); + Log_InfoPrintf("Max display fps: %f (%s)", max_display_fps, + m_display_all_frames ? "displaying all frames" : "skipping displaying frames when needed"); + + if (IsValid()) + { + s_target_speed = target_speed; + UpdateThrottlePeriod(); + ResetThrottler(); + + const u32 input_sample_rate = (target_speed == 0.0f || !g_settings.audio_resampling) ? + SPU::SAMPLE_RATE : + static_cast(static_cast(SPU::SAMPLE_RATE) * target_speed); + Log_InfoPrintf("Audio input sample rate: %u hz", input_sample_rate); + + AudioStream* stream = g_spu.GetOutputStream(); + stream->SetInputSampleRate(input_sample_rate); + stream->SetWaitForBufferFill(true); + + if (g_settings.audio_fast_forward_volume != g_settings.audio_output_volume) + stream->SetOutputVolume(GetAudioOutputVolume()); + + stream->SetSync(audio_sync_enabled); + if (audio_sync_enabled) + stream->EmptyBuffers(); + } + + display->SetDisplayMaxFPS(max_display_fps); + display->SetVSync(video_sync_enabled); + +#if 0 + // FIXME + if (g_settings.increase_timer_resolution) + SetTimerResolutionIncreased(m_throttler_enabled); +#endif + + // When syncing to host and using vsync, we don't need to sleep. + if (syncing_to_host && video_sync_enabled && m_display_all_frames) + { + Log_InfoPrintf("Using host vsync for throttling."); + m_throttler_enabled = false; + } +} + +bool System::IsFastForwardEnabled() +{ + return m_fast_forward_enabled; +} + +void System::SetFastForwardEnabled(bool enabled) +{ + if (!IsValid()) + return; + + m_fast_forward_enabled = enabled; + UpdateSpeedLimiterState(); +} + +bool System::IsTurboEnabled() +{ + return m_turbo_enabled; +} + +void System::SetTurboEnabled(bool enabled) +{ + if (!IsValid()) + return; + + m_turbo_enabled = enabled; + UpdateSpeedLimiterState(); +} + +void System::SetRewindState(bool enabled) +{ + if (!System::IsValid()) + return; + + if (!Cheevos::IsChallengeModeActive()) + { + if (!g_settings.rewind_enable) + { + if (enabled) + Host::AddKeyedOSDMessage("SetRewindState", Host::TranslateStdString("OSDMessage", "Rewinding is not enabled."), + 5.0f); + + return; + } + + System::SetRewinding(enabled); + UpdateSpeedLimiterState(); + } + else + { + Cheevos::DisplayBlockedByChallengeModeMessage(); + } +} + +void System::DoFrameStep() +{ + if (!IsValid()) + return; + + m_frame_step_request = true; + PauseSystem(false); +} + +void System::DoToggleCheats() +{ + if (!System::IsValid()) + return; + + CheatList* cl = GetCheatList(); + if (!cl) + { + Host::AddKeyedOSDMessage("ToggleCheats", Host::TranslateStdString("OSDMessage", "No cheats are loaded."), 10.0f); + return; + } + + cl->SetMasterEnable(!cl->GetMasterEnable()); + Host::AddKeyedOSDMessage( + "ToggleCheats", + cl->GetMasterEnable() ? + Host::TranslateStdString("OSDMessage", "%n cheats are now active.", "", cl->GetEnabledCodeCount()) : + Host::TranslateStdString("OSDMessage", "%n cheats are now inactive.", "", cl->GetEnabledCodeCount()), + 10.0f); +} + static bool LoadEXEToRAM(const char* filename, bool patch_bios) { std::FILE* fp = FileSystem::OpenCFile(filename, "rb"); @@ -1794,7 +2302,7 @@ static bool LoadEXEToRAM(const char* filename, bool patch_bios) return BIOS::PatchBIOSForEXE(Bus::g_bios, Bus::BIOS_SIZE, r_pc, r_gp, r_sp, r_fp); } -bool LoadEXE(const char* filename) +bool System::LoadEXE(const char* filename) { const std::string libps_path(Path::BuildRelativePath(filename, "libps.exe")); if (!libps_path.empty() && FileSystem::FileExists(libps_path.c_str()) && !LoadEXEToRAM(libps_path.c_str(), false)) @@ -1806,7 +2314,7 @@ bool LoadEXE(const char* filename) return LoadEXEToRAM(filename, true); } -bool InjectEXEFromBuffer(const void* buffer, u32 buffer_size, bool patch_bios) +bool System::InjectEXEFromBuffer(const void* buffer, u32 buffer_size, bool patch_bios) { const u8* buffer_ptr = static_cast(buffer); const u8* buffer_end = static_cast(buffer) + buffer_size; @@ -1895,19 +2403,19 @@ bool SetExpansionROM(const char* filename) Bus::SetExpansionROM(std::move(data)); return true; } -#endif -void StallCPU(TickCount ticks) +void System::StallCPU(TickCount ticks) { CPU::AddPendingTicks(ticks); } +#endif -Controller* GetController(u32 slot) +Controller* System::GetController(u32 slot) { return g_pad.GetController(slot); } -void UpdateControllers() +void System::UpdateControllers() { auto lock = Host::GetSettingsLock(); @@ -1922,28 +2430,27 @@ void UpdateControllers() if (controller) { // TODO: This should use the input profile. - controller->LoadSettings(*Host::GetSettingsInterface(), TinyString::FromFormat("Pad%u", i + 1u)); + controller->LoadSettings(*Host::GetSettingsInterface(), Controller::GetSettingsSection(i).c_str()); g_pad.SetController(i, std::move(controller)); } } } } -void UpdateControllerSettings() +void System::UpdateControllerSettings() { -#if 0 + // TODO: This should use the input profile too. + auto lock = Host::GetSettingsLock(); + for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) { Controller* controller = g_pad.GetController(i); if (controller) - controller->LoadSettings(TinyString::FromFormat("Controller%u", i + 1u)); + controller->LoadSettings(*Host::GetSettingsInterface(), Controller::GetSettingsSection(i).c_str()); } -#else - Panic("Fixme"); -#endif } -void ResetControllers() +void System::ResetControllers() { for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) { @@ -1953,7 +2460,7 @@ void ResetControllers() } } -static std::unique_ptr GetMemoryCardForSlot(u32 slot, MemoryCardType type) +std::unique_ptr System::GetMemoryCardForSlot(u32 slot, MemoryCardType type) { // Disable memory cards when running PSFs. const bool is_running_psf = !s_running_game_path.empty() && IsPsfFileName(s_running_game_path.c_str()); @@ -1966,10 +2473,10 @@ static std::unique_ptr GetMemoryCardForSlot(u32 slot, MemoryCardType { if (s_running_game_code.empty()) { - g_host_interface->AddFormattedOSDMessage( + Host::AddFormattedOSDMessage( 5.0f, - g_host_interface->TranslateString("System", "Per-game memory card cannot be used for slot %u as the running " - "game has no code. Using shared card instead."), + Host::TranslateString("System", "Per-game memory card cannot be used for slot %u as the running " + "game has no code. Using shared card instead."), slot + 1u); return MemoryCard::Open(g_host_interface->GetSharedMemoryCardPath(slot)); } @@ -1983,10 +2490,10 @@ static std::unique_ptr GetMemoryCardForSlot(u32 slot, MemoryCardType { if (s_running_game_title.empty()) { - g_host_interface->AddFormattedOSDMessage( + Host::AddFormattedOSDMessage( 5.0f, - g_host_interface->TranslateString("System", "Per-game memory card cannot be used for slot %u as the running " - "game has no title. Using shared card instead."), + Host::TranslateString("System", "Per-game memory card cannot be used for slot %u as the running " + "game has no title. Using shared card instead."), slot + 1u); return MemoryCard::Open(g_host_interface->GetSharedMemoryCardPath(slot)); } @@ -2003,10 +2510,10 @@ static std::unique_ptr GetMemoryCardForSlot(u32 slot, MemoryCardType const std::string_view file_title(Path::GetFileTitle(display_name)); if (file_title.empty()) { - g_host_interface->AddFormattedOSDMessage( + Host::AddFormattedOSDMessage( 5.0f, - g_host_interface->TranslateString("System", "Per-game memory card cannot be used for slot %u as the running " - "game has no path. Using shared card instead."), + Host::TranslateString("System", "Per-game memory card cannot be used for slot %u as the running " + "game has no path. Using shared card instead."), slot + 1u); return MemoryCard::Open(g_host_interface->GetSharedMemoryCardPath(slot)); } @@ -2034,7 +2541,7 @@ static std::unique_ptr GetMemoryCardForSlot(u32 slot, MemoryCardType } } -void UpdateMemoryCardTypes() +void System::UpdateMemoryCardTypes() { for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) { @@ -2047,7 +2554,7 @@ void UpdateMemoryCardTypes() } } -void UpdatePerGameMemoryCards() +void System::UpdatePerGameMemoryCards() { for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) { @@ -2063,20 +2570,43 @@ void UpdatePerGameMemoryCards() } } -bool HasMemoryCard(u32 slot) +bool System::HasMemoryCard(u32 slot) { return (g_pad.GetMemoryCard(slot) != nullptr); } -void SwapMemoryCards() +void System::SwapMemoryCards() { std::unique_ptr first = g_pad.RemoveMemoryCard(0); std::unique_ptr second = g_pad.RemoveMemoryCard(1); g_pad.SetMemoryCard(0, std::move(second)); g_pad.SetMemoryCard(1, std::move(first)); + + if (HasMemoryCard(0) && HasMemoryCard(1)) + { + Host::AddOSDMessage( + Host::TranslateStdString("OSDMessage", "Swapped memory card ports. Both ports have a memory card."), 10.0f); + } + else if (HasMemoryCard(1)) + { + Host::AddOSDMessage( + Host::TranslateStdString("OSDMessage", "Swapped memory card ports. Port 2 has a memory card, Port 1 is empty."), + 10.0f); + } + else if (HasMemoryCard(0)) + { + Host::AddOSDMessage( + Host::TranslateStdString("OSDMessage", "Swapped memory card ports. Port 1 has a memory card, Port 2 is empty."), + 10.0f); + } + else + { + Host::AddOSDMessage( + Host::TranslateStdString("OSDMessage", "Swapped memory card ports. Neither port has a memory card."), 10.0f); + } } -void UpdateMultitaps() +void System::UpdateMultitaps() { switch (g_settings.multitap_mode) { @@ -2110,7 +2640,7 @@ void UpdateMultitaps() } } -bool DumpRAM(const char* filename) +bool System::DumpRAM(const char* filename) { if (!IsValid()) return false; @@ -2118,7 +2648,7 @@ bool DumpRAM(const char* filename) return FileSystem::WriteBinaryFile(filename, Bus::g_ram, Bus::g_ram_size); } -bool DumpVRAM(const char* filename) +bool System::DumpVRAM(const char* filename) { if (!IsValid()) return false; @@ -2130,7 +2660,7 @@ bool DumpVRAM(const char* filename) return result; } -bool DumpSPURAM(const char* filename) +bool System::DumpSPURAM(const char* filename) { if (!IsValid()) return false; @@ -2138,12 +2668,12 @@ bool DumpSPURAM(const char* filename) return FileSystem::WriteBinaryFile(filename, g_spu.GetRAM().data(), SPU::RAM_SIZE); } -bool HasMedia() +bool System::HasMedia() { return g_cdrom.HasMedia(); } -std::string GetMediaFileName() +std::string System::GetMediaFileName() { if (!g_cdrom.HasMedia()) return {}; @@ -2151,15 +2681,14 @@ std::string GetMediaFileName() return g_cdrom.GetMediaFileName(); } -bool InsertMedia(const char* path) +bool System::InsertMedia(const char* path) { Common::Error error; std::unique_ptr image = OpenCDImage(path, &error, false, ShouldCheckForImagePatches()); if (!image) { - g_host_interface->AddFormattedOSDMessage( - 10.0f, g_host_interface->TranslateString("OSDMessage", "Failed to open disc image '%s': %s."), path, - error.GetCodeAndMessage().GetCharArray()); + Host::AddFormattedOSDMessage(10.0f, Host::TranslateString("OSDMessage", "Failed to open disc image '%s': %s."), + path, error.GetCodeAndMessage().GetCharArray()); return false; } @@ -2167,14 +2696,12 @@ bool InsertMedia(const char* path) g_cdrom.InsertMedia(std::move(image)); Log_InfoPrintf("Inserted media from %s (%s, %s)", s_running_game_path.c_str(), s_running_game_code.c_str(), s_running_game_title.c_str()); - g_host_interface->AddFormattedOSDMessage(10.0f, - g_host_interface->TranslateString("OSDMessage", "Inserted disc '%s' (%s)."), - s_running_game_title.c_str(), s_running_game_code.c_str()); + Host::AddFormattedOSDMessage(10.0f, Host::TranslateString("OSDMessage", "Inserted disc '%s' (%s)."), + s_running_game_title.c_str(), s_running_game_code.c_str()); if (g_settings.HasAnyPerGameMemoryCards()) { - g_host_interface->AddOSDMessage( - g_host_interface->TranslateStdString("System", "Game changed, reloading memory cards."), 10.0f); + Host::AddOSDMessage(Host::TranslateStdString("System", "Game changed, reloading memory cards."), 10.0f); UpdatePerGameMemoryCards(); } @@ -2182,13 +2709,13 @@ bool InsertMedia(const char* path) return true; } -void RemoveMedia() +void System::RemoveMedia() { g_cdrom.RemoveMedia(); ClearMemorySaveStates(); } -void UpdateRunningGame(const char* path, CDImage* image) +void System::UpdateRunningGame(const char* path, CDImage* image) { if (s_running_game_path == path) return; @@ -2212,10 +2739,17 @@ void UpdateRunningGame(const char* path, CDImage* image) g_texture_replacements.SetGameID(s_running_game_code); - g_host_interface->OnRunningGameChanged(s_running_game_path, image, s_running_game_code, s_running_game_title); + s_cheat_list.reset(); + if (g_settings.auto_load_cheats && !Cheevos::IsChallengeModeActive()) + LoadCheatListFromGameTitle(); + + if (UpdateGameSettingsLayer()) + ApplySettings(false); + + Host::OnGameChanged(s_running_game_path, s_running_game_code, s_running_game_title); } -bool CheckForSBIFile(CDImage* image) +bool System::CheckForSBIFile(CDImage* image) { if (s_running_game_code.empty() || !LibcryptGameList::IsLibcryptGameCode(s_running_game_code) || !image || image->HasNonStandardSubchannel()) @@ -2226,11 +2760,12 @@ bool CheckForSBIFile(CDImage* image) Log_WarningPrintf("SBI file missing but required for %s (%s)", s_running_game_code.c_str(), s_running_game_title.c_str()); - if (g_host_interface->GetBoolSettingValue("CDROM", "AllowBootingWithoutSBIFile", false)) + if (Host::GetBoolSettingValue("CDROM", "AllowBootingWithoutSBIFile", false)) { - return g_host_interface->ConfirmMessage( + return Host::ConfirmMessage( + "Confirm Unsupported Configuration", StringUtil::StdStringFromFormat( - g_host_interface->TranslateString( + Host::TranslateString( "System", "You are attempting to run a libcrypt protected game without an SBI file:\n\n%s: %s\n\nThe game will " "likely not run properly.\n\nPlease check the README for instructions on how to add an SBI file.\n\nDo " @@ -2240,35 +2775,38 @@ bool CheckForSBIFile(CDImage* image) } else { - g_host_interface->ReportError(SmallString::FromFormat( - g_host_interface->TranslateString( - "System", "You are attempting to run a libcrypt protected game without an SBI file:\n\n%s: %s\n\nYour dump is " - "incomplete, you must add the SBI file to run this game. \n\n" - "The name of the SBI file must match the name of the disc image."), - s_running_game_code.c_str(), s_running_game_title.c_str())); + Host::ReportErrorAsync( + Host::TranslateString("System", "Error"), + SmallString::FromFormat( + Host::TranslateString( + "System", + "You are attempting to run a libcrypt protected game without an SBI file:\n\n%s: %s\n\nYour dump is " + "incomplete, you must add the SBI file to run this game. \n\n" + "The name of the SBI file must match the name of the disc image."), + s_running_game_code.c_str(), s_running_game_title.c_str())); return false; } } -bool HasMediaSubImages() +bool System::HasMediaSubImages() { const CDImage* cdi = g_cdrom.GetMedia(); return cdi ? cdi->HasSubImages() : false; } -u32 GetMediaSubImageCount() +u32 System::GetMediaSubImageCount() { const CDImage* cdi = g_cdrom.GetMedia(); return cdi ? cdi->GetSubImageCount() : 0; } -u32 GetMediaSubImageIndex() +u32 System::GetMediaSubImageIndex() { const CDImage* cdi = g_cdrom.GetMedia(); return cdi ? cdi->GetCurrentSubImage() : 0; } -u32 GetMediaSubImageIndexForTitle(const std::string_view& title) +u32 System::GetMediaSubImageIndexForTitle(const std::string_view& title) { const CDImage* cdi = g_cdrom.GetMedia(); if (!cdi) @@ -2284,7 +2822,7 @@ u32 GetMediaSubImageIndexForTitle(const std::string_view& title) return std::numeric_limits::max(); } -std::string GetMediaSubImageTitle(u32 index) +std::string System::GetMediaSubImageTitle(u32 index) { const CDImage* cdi = g_cdrom.GetMedia(); if (!cdi) @@ -2293,7 +2831,7 @@ std::string GetMediaSubImageTitle(u32 index) return cdi->GetSubImageMetadata(index, "title"); } -bool SwitchMediaSubImage(u32 index) +bool System::SwitchMediaSubImage(u32 index) { if (!g_cdrom.HasMedia()) return false; @@ -2304,58 +2842,280 @@ bool SwitchMediaSubImage(u32 index) Common::Error error; if (!image->SwitchSubImage(index, &error)) { - g_host_interface->AddFormattedOSDMessage( - 10.0f, g_host_interface->TranslateString("OSDMessage", "Failed to switch to subimage %u in '%s': %s."), - index + 1u, image->GetFileName().c_str(), error.GetCodeAndMessage().GetCharArray()); + Host::AddFormattedOSDMessage(10.0f, + Host::TranslateString("OSDMessage", "Failed to switch to subimage %u in '%s': %s."), + index + 1u, image->GetFileName().c_str(), error.GetCodeAndMessage().GetCharArray()); g_cdrom.InsertMedia(std::move(image)); return false; } - g_host_interface->AddFormattedOSDMessage( - 20.0f, g_host_interface->TranslateString("OSDMessage", "Switched to sub-image %s (%u) in '%s'."), - image->GetSubImageMetadata(index, "title").c_str(), index + 1u, image->GetMetadata("title").c_str()); + Host::AddFormattedOSDMessage(20.0f, Host::TranslateString("OSDMessage", "Switched to sub-image %s (%u) in '%s'."), + image->GetSubImageMetadata(index, "title").c_str(), index + 1u, + image->GetMetadata("title").c_str()); g_cdrom.InsertMedia(std::move(image)); ClearMemorySaveStates(); return true; } -bool HasCheatList() +bool System::HasCheatList() { return static_cast(s_cheat_list); } -CheatList* GetCheatList() +CheatList* System::GetCheatList() { return s_cheat_list.get(); } -void ApplyCheatCode(const CheatCode& code) +void System::ApplyCheatCode(const CheatCode& code) { Assert(!IsShutdown()); code.Apply(); } -void SetCheatList(std::unique_ptr cheats) +void System::SetCheatList(std::unique_ptr cheats) { Assert(!IsShutdown()); s_cheat_list = std::move(cheats); } -void CalculateRewindMemoryUsage(u32 num_saves, u64* ram_usage, u64* vram_usage) +void System::CheckForSettingsChanges(const Settings& old_settings) +{ + if (IsValid() && (g_settings.gpu_renderer != old_settings.gpu_renderer || + g_settings.gpu_use_debug_device != old_settings.gpu_use_debug_device || + g_settings.gpu_threaded_presentation != old_settings.gpu_threaded_presentation)) + { + Host::AddFormattedOSDMessage(5.0f, Host::TranslateString("OSDMessage", "Switching to %s%s GPU renderer."), + Settings::GetRendererName(g_settings.gpu_renderer), + g_settings.gpu_use_debug_device ? " (debug)" : ""); + RecreateSystem(); + } + + if (IsValid()) + { + ClearMemorySaveStates(); + + if (g_settings.cpu_overclock_active != old_settings.cpu_overclock_active || + (g_settings.cpu_overclock_active && + (g_settings.cpu_overclock_numerator != old_settings.cpu_overclock_numerator || + g_settings.cpu_overclock_denominator != old_settings.cpu_overclock_denominator))) + { + UpdateOverclock(); + } + + if (g_settings.audio_backend != old_settings.audio_backend || + g_settings.audio_buffer_size != old_settings.audio_buffer_size) + { + if (g_settings.audio_backend != old_settings.audio_backend) + { + Host::AddFormattedOSDMessage(5.0f, Host::TranslateString("OSDMessage", "Switching to %s audio backend."), + Settings::GetAudioBackendName(g_settings.audio_backend)); + } + + g_spu.RecreateOutputStream(); + g_spu.GetOutputStream()->PauseOutput(IsPaused()); + } + + if (g_settings.emulation_speed != old_settings.emulation_speed) + UpdateThrottlePeriod(); + + if (g_settings.cpu_execution_mode != old_settings.cpu_execution_mode || + g_settings.cpu_fastmem_mode != old_settings.cpu_fastmem_mode) + { + Host::AddFormattedOSDMessage(5.0f, Host::TranslateString("OSDMessage", "Switching to %s CPU execution mode."), + Host::TranslateString("CPUExecutionMode", Settings::GetCPUExecutionModeDisplayName( + g_settings.cpu_execution_mode)) + .GetCharArray()); + CPU::CodeCache::Reinitialize(); + CPU::ClearICache(); + } + + if (g_settings.cpu_execution_mode == CPUExecutionMode::Recompiler && + (g_settings.cpu_recompiler_memory_exceptions != old_settings.cpu_recompiler_memory_exceptions || + g_settings.cpu_recompiler_block_linking != old_settings.cpu_recompiler_block_linking || + g_settings.cpu_recompiler_icache != old_settings.cpu_recompiler_icache)) + { + Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "Recompiler options changed, flushing all blocks."), + 5.0f); + CPU::CodeCache::Flush(); + + if (g_settings.cpu_recompiler_icache != old_settings.cpu_recompiler_icache) + CPU::ClearICache(); + } + + g_spu.GetOutputStream()->SetOutputVolume(GetAudioOutputVolume()); + + if (g_settings.gpu_resolution_scale != old_settings.gpu_resolution_scale || + g_settings.gpu_multisamples != old_settings.gpu_multisamples || + g_settings.gpu_per_sample_shading != old_settings.gpu_per_sample_shading || + g_settings.gpu_use_thread != old_settings.gpu_use_thread || + g_settings.gpu_use_software_renderer_for_readbacks != old_settings.gpu_use_software_renderer_for_readbacks || + g_settings.gpu_fifo_size != old_settings.gpu_fifo_size || + g_settings.gpu_max_run_ahead != old_settings.gpu_max_run_ahead || + g_settings.gpu_true_color != old_settings.gpu_true_color || + g_settings.gpu_scaled_dithering != old_settings.gpu_scaled_dithering || + g_settings.gpu_texture_filter != old_settings.gpu_texture_filter || + g_settings.gpu_disable_interlacing != old_settings.gpu_disable_interlacing || + g_settings.gpu_force_ntsc_timings != old_settings.gpu_force_ntsc_timings || + g_settings.gpu_24bit_chroma_smoothing != old_settings.gpu_24bit_chroma_smoothing || + g_settings.gpu_downsample_mode != old_settings.gpu_downsample_mode || + g_settings.display_crop_mode != old_settings.display_crop_mode || + g_settings.display_aspect_ratio != old_settings.display_aspect_ratio || + g_settings.gpu_pgxp_enable != old_settings.gpu_pgxp_enable || + g_settings.gpu_pgxp_depth_buffer != old_settings.gpu_pgxp_depth_buffer || + g_settings.display_active_start_offset != old_settings.display_active_start_offset || + g_settings.display_active_end_offset != old_settings.display_active_end_offset || + g_settings.display_line_start_offset != old_settings.display_line_start_offset || + g_settings.display_line_end_offset != old_settings.display_line_end_offset || + g_settings.rewind_enable != old_settings.rewind_enable || + g_settings.runahead_frames != old_settings.runahead_frames) + { + g_gpu->UpdateSettings(); + Host::InvalidateDisplay(); + } + + if (g_settings.gpu_widescreen_hack != old_settings.gpu_widescreen_hack || + g_settings.display_aspect_ratio != old_settings.display_aspect_ratio || + (g_settings.display_aspect_ratio == DisplayAspectRatio::Custom && + (g_settings.display_aspect_ratio_custom_numerator != old_settings.display_aspect_ratio_custom_numerator || + g_settings.display_aspect_ratio_custom_denominator != old_settings.display_aspect_ratio_custom_denominator))) + { + GTE::UpdateAspectRatio(); + } + + if (g_settings.gpu_pgxp_enable != old_settings.gpu_pgxp_enable || + (g_settings.gpu_pgxp_enable && (g_settings.gpu_pgxp_culling != old_settings.gpu_pgxp_culling || + g_settings.gpu_pgxp_vertex_cache != old_settings.gpu_pgxp_vertex_cache || + g_settings.gpu_pgxp_cpu != old_settings.gpu_pgxp_cpu))) + { + if (g_settings.IsUsingCodeCache()) + { + Host::AddOSDMessage(g_settings.gpu_pgxp_enable ? + Host::TranslateStdString("OSDMessage", "PGXP enabled, recompiling all blocks.") : + Host::TranslateStdString("OSDMessage", "PGXP disabled, recompiling all blocks."), + 5.0f); + CPU::CodeCache::Flush(); + } + + if (old_settings.gpu_pgxp_enable) + PGXP::Shutdown(); + + if (g_settings.gpu_pgxp_enable) + PGXP::Initialize(); + } + + if (g_settings.cdrom_readahead_sectors != old_settings.cdrom_readahead_sectors) + g_cdrom.SetReadaheadSectors(g_settings.cdrom_readahead_sectors); + + if (g_settings.memory_card_types != old_settings.memory_card_types || + g_settings.memory_card_paths != old_settings.memory_card_paths || + (g_settings.memory_card_use_playlist_title != old_settings.memory_card_use_playlist_title && + HasMediaSubImages()) || + g_settings.memory_card_directory != old_settings.memory_card_directory) + { + UpdateMemoryCardTypes(); + } + + if (g_settings.rewind_enable != old_settings.rewind_enable || + g_settings.rewind_save_frequency != old_settings.rewind_save_frequency || + g_settings.rewind_save_slots != old_settings.rewind_save_slots || + g_settings.runahead_frames != old_settings.runahead_frames) + { + UpdateMemorySaveStateSettings(); + } + + if (g_settings.texture_replacements.enable_vram_write_replacements != + old_settings.texture_replacements.enable_vram_write_replacements || + g_settings.texture_replacements.preload_textures != old_settings.texture_replacements.preload_textures) + { + g_texture_replacements.Reload(); + } + + g_dma.SetMaxSliceTicks(g_settings.dma_max_slice_ticks); + g_dma.SetHaltTicks(g_settings.dma_halt_ticks); + + if (g_settings.audio_backend != old_settings.audio_backend || + g_settings.audio_buffer_size != old_settings.audio_buffer_size || + g_settings.video_sync_enabled != old_settings.video_sync_enabled || + g_settings.audio_sync_enabled != old_settings.audio_sync_enabled || + g_settings.increase_timer_resolution != old_settings.increase_timer_resolution || + g_settings.emulation_speed != old_settings.emulation_speed || + g_settings.fast_forward_speed != old_settings.fast_forward_speed || + g_settings.display_max_fps != old_settings.display_max_fps || + g_settings.display_all_frames != old_settings.display_all_frames || + g_settings.audio_resampling != old_settings.audio_resampling || + g_settings.sync_to_host_refresh_rate != old_settings.sync_to_host_refresh_rate) + { + UpdateSpeedLimiterState(); + } + + if (g_settings.display_post_processing != old_settings.display_post_processing || + g_settings.display_post_process_chain != old_settings.display_post_process_chain) + { + if (g_settings.display_post_processing) + { + if (!Host::GetHostDisplay()->SetPostProcessingChain(g_settings.display_post_process_chain)) + Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "Failed to load post processing shader chain."), + 20.0f); + } + else + { + Host::GetHostDisplay()->SetPostProcessingChain({}); + } + } + } + + bool controllers_updated = false; + for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) + { + if (g_settings.controller_types[i] != old_settings.controller_types[i]) + { + if (!IsShutdown() && !controllers_updated) + { + UpdateControllers(); + ResetControllers(); + g_host_interface->UpdateSoftwareCursor(); + controllers_updated = true; + } + } + + if (!IsShutdown() && !controllers_updated) + { + UpdateControllerSettings(); + g_host_interface->UpdateSoftwareCursor(); + } + } + + if (g_settings.multitap_mode != old_settings.multitap_mode) + UpdateMultitaps(); + + HostDisplay* display = Host::GetHostDisplay(); + if (display && (g_settings.display_linear_filtering != old_settings.display_linear_filtering || + g_settings.display_integer_scaling != old_settings.display_integer_scaling || + g_settings.display_stretch != old_settings.display_stretch)) + { + display->SetDisplayLinearFiltering(g_settings.display_linear_filtering); + display->SetDisplayIntegerScaling(g_settings.display_integer_scaling); + display->SetDisplayStretch(g_settings.display_stretch); + Host::InvalidateDisplay(); + } +} + +void System::CalculateRewindMemoryUsage(u32 num_saves, u64* ram_usage, u64* vram_usage) { *ram_usage = MAX_SAVE_STATE_SIZE * static_cast(num_saves); *vram_usage = (VRAM_WIDTH * VRAM_HEIGHT * 4) * static_cast(std::max(g_settings.gpu_resolution_scale, 1u)) * static_cast(g_settings.gpu_multisamples) * static_cast(num_saves); } -void ClearMemorySaveStates() +void System::ClearMemorySaveStates() { s_rewind_states.clear(); s_runahead_states.clear(); } -void UpdateMemorySaveStateSettings() +void System::UpdateMemorySaveStateSettings() { ClearMemorySaveStates(); @@ -2384,24 +3144,10 @@ void UpdateMemorySaveStateSettings() s_runahead_frames = g_settings.runahead_frames; s_runahead_replay_pending = false; if (s_runahead_frames > 0) - { Log_InfoPrintf("Runahead is active with %u frames", s_runahead_frames); - - if (!s_runahead_audio_stream) - { - // doesn't matter if it's not resampled here since it eats everything anyway, nom nom nom. - s_runahead_audio_stream = AudioStream::CreateNullAudioStream(); - s_runahead_audio_stream->Reconfigure(HostInterface::AUDIO_SAMPLE_RATE, HostInterface::AUDIO_SAMPLE_RATE, - HostInterface::AUDIO_CHANNELS); - } - } - else - { - s_runahead_audio_stream.reset(); - } } -bool LoadMemoryState(const MemorySaveState& mss) +bool System::LoadMemoryState(const MemorySaveState& mss) { mss.state_stream->SeekAbsolute(0); @@ -2409,15 +3155,15 @@ bool LoadMemoryState(const MemorySaveState& mss) HostDisplayTexture* host_texture = mss.vram_texture.get(); if (!DoState(sw, &host_texture, true, true)) { - g_host_interface->ReportError("Failed to load memory save state, resetting."); - Reset(); + Host::ReportErrorAsync("Error", "Failed to load memory save state, resetting."); + InternalReset(); return false; } return true; } -bool SaveMemoryState(MemorySaveState* mss) +bool System::SaveMemoryState(MemorySaveState* mss) { if (!mss->state_stream) mss->state_stream = std::make_unique(nullptr, MAX_SAVE_STATE_SIZE); @@ -2437,7 +3183,7 @@ bool SaveMemoryState(MemorySaveState* mss) return true; } -bool SaveRewindState() +bool System::SaveRewindState() { #ifdef PROFILE_MEMORY_SAVE_STATES Common::Timer save_timer; @@ -2465,7 +3211,7 @@ bool SaveRewindState() return true; } -bool LoadRewindState(u32 skip_saves /*= 0*/, bool consume_state /*=true */) +bool System::LoadRewindState(u32 skip_saves /*= 0*/, bool consume_state /*=true */) { while (skip_saves > 0 && !s_rewind_states.empty()) { @@ -2493,12 +3239,12 @@ bool LoadRewindState(u32 skip_saves /*= 0*/, bool consume_state /*=true */) return true; } -bool IsRewinding() +bool System::IsRewinding() { return (s_rewind_load_frequency >= 0); } -void SetRewinding(bool enabled) +void System::SetRewinding(bool enabled) { if (enabled) { @@ -2516,7 +3262,7 @@ void SetRewinding(bool enabled) s_rewinding_first_save = true; } -void DoRewind() +void System::DoRewind() { s_frame_timer.Reset(); @@ -2536,7 +3282,7 @@ void DoRewind() s_next_frame_time += s_frame_period; } -void SaveRunaheadState() +void System::SaveRunaheadState() { // try to reuse the frontmost slot MemorySaveState mss; @@ -2555,7 +3301,7 @@ void SaveRunaheadState() s_runahead_states.push_back(std::move(mss)); } -void DoRunahead() +void System::DoRunahead() { #ifdef PROFILE_MEMORY_SAVE_STATES Common::Timer timer; @@ -2590,7 +3336,7 @@ void DoRunahead() const s32 temp = frames_to_run; #endif - g_spu.SetAudioStream(s_runahead_audio_stream.get()); + g_spu.SetAudioOutputMuted(true); while (frames_to_run > 0) { @@ -2599,7 +3345,7 @@ void DoRunahead() frames_to_run--; } - g_spu.SetAudioStream(g_host_interface->GetAudioStream()); + g_spu.SetAudioOutputMuted(false); #ifdef PROFILE_MEMORY_SAVE_STATES Log_VerbosePrintf("Running %d frames to catch up took %.2f ms", temp, timer2.GetTimeMilliseconds()); @@ -2616,7 +3362,7 @@ void DoRunahead() #endif } -void DoMemorySaveStates() +void System::DoMemorySaveStates() { if (s_rewind_save_counter >= 0) { @@ -2635,7 +3381,7 @@ void DoMemorySaveStates() SaveRunaheadState(); } -void SetRunaheadReplayFlag() +void System::SetRunaheadReplayFlag() { if (s_runahead_frames == 0 || s_runahead_states.empty()) return; @@ -2647,4 +3393,761 @@ void SetRunaheadReplayFlag() s_runahead_replay_pending = true; } -} // namespace System +bool System::SaveResumeSaveState() +{ + if (System::IsShutdown()) + return false; + + const bool global = System::GetRunningCode().empty(); + return SaveStateToSlot(global, -1); +} + +void System::PowerOffSystem(bool save_resume_state) +{ + if (!IsValid()) + return; + + if (save_resume_state) + SaveResumeSaveState(); + + DestroySystem(); +} + +bool System::CanUndoLoadState() +{ + return static_cast(m_undo_load_state); +} + +std::optional System::GetUndoSaveStateInfo() +{ + std::optional ssi; + if (m_undo_load_state) + { + m_undo_load_state->SeekAbsolute(0); + ssi = GetExtendedSaveStateInfo(m_undo_load_state.get()); + m_undo_load_state->SeekAbsolute(0); + + if (ssi) + { + ssi->timestamp = 0; + ssi->slot = 0; + ssi->global = false; + } + } + + return ssi; +} + +bool System::UndoLoadState() +{ + if (!m_undo_load_state) + return false; + + Assert(IsValid()); + + m_undo_load_state->SeekAbsolute(0); + if (!InternalLoadState(m_undo_load_state.get())) + { + Host::ReportErrorAsync("Error", "Failed to load undo state, resetting system."); + m_undo_load_state.reset(); + ResetSystem(); + return false; + } + + ResetPerformanceCounters(); + ResetThrottler(); + + Log_InfoPrintf("Loaded undo save state."); + m_undo_load_state.reset(); + return true; +} + +bool System::SaveUndoLoadState() +{ + if (m_undo_load_state) + m_undo_load_state.reset(); + + m_undo_load_state = ByteStream::CreateGrowableMemoryStream(nullptr, System::MAX_SAVE_STATE_SIZE); + if (!InternalSaveState(m_undo_load_state.get())) + { + Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "Failed to save undo load state."), 15.0f); + m_undo_load_state.reset(); + return false; + } + + Log_InfoPrintf("Saved undo load state: %" PRIu64 " bytes", m_undo_load_state->GetSize()); + return true; +} + +bool System::LoadStateFromSlot(bool global, s32 slot) +{ + if (!global && (System::IsShutdown() || System::GetRunningCode().empty())) + { + Host::ReportErrorAsync("Error", "Can't save per-game state without a running game code."); + return false; + } + + std::string save_path = + global ? GetGlobalSaveStateFileName(slot) : GetGameSaveStateFileName(System::GetRunningCode().c_str(), slot); + return LoadState(save_path.c_str()); +} + +bool System::SaveStateToSlot(bool global, s32 slot) +{ + const std::string& code = System::GetRunningCode(); + if (!global && code.empty()) + { + Host::ReportErrorAsync("Error", "Can't save per-game state without a running game code."); + return false; + } + + std::string save_path = global ? GetGlobalSaveStateFileName(slot) : GetGameSaveStateFileName(code.c_str(), slot); + RenameCurrentSaveStateToBackup(save_path.c_str()); + return SaveState(save_path.c_str()); +} + +bool System::CanResumeSystemFromFile(const char* filename) +{ + if (Host::GetBaseBoolSettingValue("Main", "SaveStateOnExit", true) && !Cheevos::IsChallengeModeActive()) + { +#if 0 + const GameListEntry* entry = m_game_list->GetEntryForPath(filename); + if (entry) + return !entry->code.empty(); + else + return !System::GetGameCodeForPath(filename, true).empty(); +#else + Panic("Fixme"); +#endif + } + + return false; +} + +bool System::ResumeSystemFromState(const char* filename, bool boot_on_failure) +{ + if (!BootSystem(std::make_shared(filename))) + return false; + + const bool global = System::GetRunningCode().empty(); + if (global) + { + Host::ReportErrorAsync("Error", + fmt::format("Cannot resume system with undetectable game code from '{}'.", filename)); + if (!boot_on_failure) + { + DestroySystem(); + return true; + } + } + else + { + const std::string path = GetGameSaveStateFileName(System::GetRunningCode().c_str(), -1); + if (FileSystem::FileExists(path.c_str())) + { + if (!LoadState(path.c_str()) && !boot_on_failure) + { + DestroySystem(); + return false; + } + } + else if (!boot_on_failure) + { + Host::ReportErrorAsync("Error", fmt::format("Resume save state not found for '{}' ('{}').", + System::GetRunningCode(), System::GetRunningTitle())); + DestroySystem(); + return false; + } + } + + return true; +} + +bool System::ResumeSystemFromMostRecentState() +{ + const std::string path = GetMostRecentResumeSaveStatePath(); + if (path.empty()) + { + Host::ReportErrorAsync("Error", "No resume save state found."); + return false; + } + + return LoadState(path.c_str()); +} + +bool System::ShouldSaveResumeState() +{ + return g_settings.save_state_on_exit; +} + +bool System::IsRunningAtNonStandardSpeed() +{ + if (!IsValid()) + return false; + + const float target_speed = System::GetTargetSpeed(); + return (target_speed <= 0.95f || target_speed >= 1.05f); +} + +s32 System::GetAudioOutputVolume() +{ + return g_settings.GetAudioOutputVolume(IsRunningAtNonStandardSpeed()); +} + +void System::UpdateVolume() +{ + if (!IsValid()) + return; + + g_spu.GetOutputStream()->SetOutputVolume(GetAudioOutputVolume()); +} + +bool System::IsDumpingAudio() +{ + return g_spu.IsDumpingAudio(); +} + +bool System::StartDumpingAudio(const char* filename) +{ + if (System::IsShutdown()) + return false; + + std::string auto_filename; + if (!filename) + { + const auto& code = System::GetRunningCode(); + if (code.empty()) + { + auto_filename = g_host_interface->GetUserDirectoryRelativePath( + "dump/audio/%s.wav", g_host_interface->GetTimestampStringForFileName().GetCharArray()); + } + else + { + auto_filename = g_host_interface->GetUserDirectoryRelativePath( + "dump/audio/%s_%s.wav", code.c_str(), g_host_interface->GetTimestampStringForFileName().GetCharArray()); + } + + filename = auto_filename.c_str(); + } + + if (g_spu.StartDumpingAudio(filename)) + { + Host::AddFormattedOSDMessage(5.0f, Host::TranslateString("OSDMessage", "Started dumping audio to '%s'."), filename); + return true; + } + else + { + Host::AddFormattedOSDMessage(10.0f, Host::TranslateString("OSDMessage", "Failed to start dumping audio to '%s'."), + filename); + return false; + } +} + +void System::StopDumpingAudio() +{ + if (System::IsShutdown() || !g_spu.StopDumpingAudio()) + return; + + Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "Stopped dumping audio."), 5.0f); +} + +bool System::SaveScreenshot(const char* filename /* = nullptr */, bool full_resolution /* = true */, + bool apply_aspect_ratio /* = true */, bool compress_on_thread /* = true */) +{ + if (System::IsShutdown()) + return false; + + std::string auto_filename; + if (!filename) + { + const auto& code = System::GetRunningCode(); + const char* extension = "png"; + if (code.empty()) + { + auto_filename = g_host_interface->GetUserDirectoryRelativePath( + "screenshots" FS_OSPATH_SEPARATOR_STR "%s.%s", g_host_interface->GetTimestampStringForFileName().GetCharArray(), + extension); + } + else + { + auto_filename = g_host_interface->GetUserDirectoryRelativePath( + "screenshots" FS_OSPATH_SEPARATOR_STR "%s_%s.%s", code.c_str(), + g_host_interface->GetTimestampStringForFileName().GetCharArray(), extension); + } + + filename = auto_filename.c_str(); + } + + if (FileSystem::FileExists(filename)) + { + Host::AddFormattedOSDMessage(10.0f, Host::TranslateString("OSDMessage", "Screenshot file '%s' already exists."), + filename); + return false; + } + + HostDisplay* display = Host::GetHostDisplay(); + const bool screenshot_saved = + g_settings.display_internal_resolution_screenshots ? + display->WriteDisplayTextureToFile(filename, full_resolution, apply_aspect_ratio, compress_on_thread) : + display->WriteScreenshotToFile(filename, compress_on_thread); + + if (!screenshot_saved) + { + Host::AddFormattedOSDMessage(10.0f, Host::TranslateString("OSDMessage", "Failed to save screenshot to '%s'"), + filename); + return false; + } + + Host::AddFormattedOSDMessage(5.0f, Host::TranslateString("OSDMessage", "Screenshot saved to '%s'."), filename); + return true; +} + +std::string System::GetGameSaveStateFileName(const char* game_code, s32 slot) +{ + if (slot < 0) + return g_host_interface->GetUserDirectoryRelativePath("savestates" FS_OSPATH_SEPARATOR_STR "%s_resume.sav", + game_code); + else + return g_host_interface->GetUserDirectoryRelativePath("savestates" FS_OSPATH_SEPARATOR_STR "%s_%d.sav", game_code, + slot); +} + +std::string System::GetGlobalSaveStateFileName(s32 slot) +{ + if (slot < 0) + return g_host_interface->GetUserDirectoryRelativePath("savestates" FS_OSPATH_SEPARATOR_STR "resume.sav"); + else + return g_host_interface->GetUserDirectoryRelativePath("savestates" FS_OSPATH_SEPARATOR_STR "savestate_%d.sav", + slot); +} + +void System::RenameCurrentSaveStateToBackup(const char* filename) +{ + if (!Host::GetBaseBoolSettingValue("General", "CreateSaveStateBackups", false)) + return; + + if (!FileSystem::FileExists(filename)) + return; + + const std::string backup_filename(Path::ReplaceExtension(filename, "bak")); + if (!FileSystem::RenamePath(filename, backup_filename.c_str())) + { + Log_ErrorPrintf("Failed to rename save state backup '%s'", backup_filename.c_str()); + return; + } + + Log_InfoPrintf("Renamed save state '%s' to '%s'", filename, backup_filename.c_str()); +} + +std::vector System::GetAvailableSaveStates(const char* game_code) +{ + std::vector si; + std::string path; + + auto add_path = [&si](std::string path, s32 slot, bool global) { + FILESYSTEM_STAT_DATA sd; + if (!FileSystem::StatFile(path.c_str(), &sd)) + return; + + si.push_back(SaveStateInfo{std::move(path), sd.ModificationTime, static_cast(slot), global}); + }; + + if (game_code && std::strlen(game_code) > 0) + { + add_path(GetGameSaveStateFileName(game_code, -1), -1, false); + for (s32 i = 1; i <= PER_GAME_SAVE_STATE_SLOTS; i++) + add_path(GetGameSaveStateFileName(game_code, i), i, false); + } + + for (s32 i = 1; i <= GLOBAL_SAVE_STATE_SLOTS; i++) + add_path(GetGlobalSaveStateFileName(i), i, true); + + return si; +} + +std::optional System::GetSaveStateInfo(const char* game_code, s32 slot) +{ + const bool global = (!game_code || game_code[0] == 0); + std::string path = global ? GetGlobalSaveStateFileName(slot) : GetGameSaveStateFileName(game_code, slot); + + FILESYSTEM_STAT_DATA sd; + if (!FileSystem::StatFile(path.c_str(), &sd)) + return std::nullopt; + + return SaveStateInfo{std::move(path), sd.ModificationTime, slot, global}; +} + +std::optional System::GetExtendedSaveStateInfo(ByteStream* stream) +{ + SAVE_STATE_HEADER header; + if (!stream->Read(&header, sizeof(header)) || header.magic != SAVE_STATE_MAGIC) + return std::nullopt; + + ExtendedSaveStateInfo ssi; + if (header.version < SAVE_STATE_MINIMUM_VERSION || header.version > SAVE_STATE_VERSION) + { + ssi.title = StringUtil::StdStringFromFormat( + Host::TranslateString("CommonHostInterface", "Invalid version %u (%s version %u)"), header.version, + header.version > SAVE_STATE_VERSION ? "maximum" : "minimum", + header.version > SAVE_STATE_VERSION ? SAVE_STATE_VERSION : SAVE_STATE_MINIMUM_VERSION); + return ssi; + } + + header.title[sizeof(header.title) - 1] = 0; + ssi.title = header.title; + header.game_code[sizeof(header.game_code) - 1] = 0; + ssi.game_code = header.game_code; + + if (header.media_filename_length > 0 && + (header.offset_to_media_filename + header.media_filename_length) <= stream->GetSize()) + { + stream->SeekAbsolute(header.offset_to_media_filename); + ssi.media_path.resize(header.media_filename_length); + if (!stream->Read2(ssi.media_path.data(), header.media_filename_length)) + std::string().swap(ssi.media_path); + } + + if (header.screenshot_width > 0 && header.screenshot_height > 0 && header.screenshot_size > 0 && + (static_cast(header.offset_to_screenshot) + static_cast(header.screenshot_size)) <= stream->GetSize()) + { + stream->SeekAbsolute(header.offset_to_screenshot); + ssi.screenshot_data.resize((header.screenshot_size + 3u) / 4u); + if (stream->Read2(ssi.screenshot_data.data(), header.screenshot_size)) + { + ssi.screenshot_width = header.screenshot_width; + ssi.screenshot_height = header.screenshot_height; + } + else + { + decltype(ssi.screenshot_data)().swap(ssi.screenshot_data); + } + } + + return ssi; +} + +std::optional System::GetExtendedSaveStateInfo(const char* game_code, s32 slot) +{ + const bool global = (!game_code || game_code[0] == 0); + std::string path = global ? GetGlobalSaveStateFileName(slot) : GetGameSaveStateFileName(game_code, slot); + + FILESYSTEM_STAT_DATA sd; + if (!FileSystem::StatFile(path.c_str(), &sd)) + return std::nullopt; + + std::unique_ptr stream = + ByteStream::OpenFile(path.c_str(), BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_SEEKABLE); + if (!stream) + return std::nullopt; + + std::optional ssi = GetExtendedSaveStateInfo(stream.get()); + if (!ssi) + return std::nullopt; + + ssi->path = std::move(path); + ssi->timestamp = sd.ModificationTime; + ssi->slot = slot; + ssi->global = global; + + return ssi; +} + +void System::DeleteSaveStates(const char* game_code, bool resume) +{ + const std::vector states(GetAvailableSaveStates(game_code)); + for (const SaveStateInfo& si : states) + { + if (si.global || (!resume && si.slot < 0)) + continue; + + Log_InfoPrintf("Removing save state at '%s'", si.path.c_str()); + if (!FileSystem::DeleteFile(si.path.c_str())) + Log_ErrorPrintf("Failed to delete save state file '%s'", si.path.c_str()); + } +} + +std::string System::GetMostRecentResumeSaveStatePath() +{ + std::vector files; + if (!FileSystem::FindFiles(g_host_interface->GetUserDirectoryRelativePath("savestates").c_str(), "*resume.sav", + FILESYSTEM_FIND_FILES, &files) || + files.empty()) + { + return {}; + } + + FILESYSTEM_FIND_DATA* most_recent = &files[0]; + for (FILESYSTEM_FIND_DATA& file : files) + { + if (file.ModificationTime > most_recent->ModificationTime) + most_recent = &file; + } + + return std::move(most_recent->FileName); +} + +std::string System::GetCheatFileName() +{ + const std::string& title = System::GetRunningTitle(); + if (title.empty()) + return {}; + + return g_host_interface->GetUserDirectoryRelativePath("cheats/%s.cht", title.c_str()); +} + +bool System::LoadCheatList(const char* filename) +{ + if (System::IsShutdown()) + return false; + + std::unique_ptr cl = std::make_unique(); + if (!cl->LoadFromFile(filename, CheatList::Format::Autodetect)) + { + Host::AddFormattedOSDMessage(15.0f, Host::TranslateString("OSDMessage", "Failed to load cheats from '%s'."), + filename); + return false; + } + + if (cl->GetEnabledCodeCount() > 0) + { + Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "%n cheats are enabled. This may result in instability.", + "", cl->GetEnabledCodeCount()), + 30.0f); + } + + System::SetCheatList(std::move(cl)); + return true; +} + +bool System::LoadCheatListFromGameTitle() +{ + if (!IsValid() || Cheevos::IsChallengeModeActive()) + return false; + + const std::string filename(GetCheatFileName()); + if (filename.empty() || !FileSystem::FileExists(filename.c_str())) + return false; + + return LoadCheatList(filename.c_str()); +} + +bool System::LoadCheatListFromDatabase() +{ + if (System::GetRunningCode().empty() || Cheevos::IsChallengeModeActive()) + return false; + + std::unique_ptr cl = std::make_unique(); + if (!cl->LoadFromPackage(System::GetRunningCode())) + return false; + + Log_InfoPrintf("Loaded %u cheats from database.", cl->GetCodeCount()); + System::SetCheatList(std::move(cl)); + return true; +} + +bool System::SaveCheatList() +{ + if (!System::IsValid() || !System::HasCheatList()) + return false; + + const std::string filename(GetCheatFileName()); + if (filename.empty()) + return false; + + if (!System::GetCheatList()->SaveToPCSXRFile(filename.c_str())) + { + Host::AddFormattedOSDMessage(15.0f, Host::TranslateString("OSDMessage", "Failed to save cheat list to '%s'"), + filename.c_str()); + } + + return true; +} + +bool System::SaveCheatList(const char* filename) +{ + if (!System::IsValid() || !System::HasCheatList()) + return false; + + if (!System::GetCheatList()->SaveToPCSXRFile(filename)) + return false; + + // This shouldn't be needed, but lupdate doesn't gather this string otherwise... + const u32 code_count = System::GetCheatList()->GetCodeCount(); + Host::AddFormattedOSDMessage(5.0f, Host::TranslateString("OSDMessage", "Saved %n cheats to '%s'.", "", code_count), + filename); + return true; +} + +bool System::DeleteCheatList() +{ + if (!System::IsValid()) + return false; + + const std::string filename(GetCheatFileName()); + if (!filename.empty()) + { + if (!FileSystem::DeleteFile(filename.c_str())) + return false; + + Host::AddFormattedOSDMessage(5.0f, Host::TranslateString("OSDMessage", "Deleted cheat list '%s'."), + filename.c_str()); + } + + System::SetCheatList(nullptr); + return true; +} + +void System::ClearCheatList(bool save_to_file) +{ + if (!System::IsValid()) + return; + + CheatList* cl = System::GetCheatList(); + if (!cl) + return; + + while (cl->GetCodeCount() > 0) + cl->RemoveCode(cl->GetCodeCount() - 1); + + if (save_to_file) + SaveCheatList(); +} + +void System::SetCheatCodeState(u32 index, bool enabled, bool save_to_file) +{ + if (!System::IsValid() || !System::HasCheatList()) + return; + + CheatList* cl = System::GetCheatList(); + if (index >= cl->GetCodeCount()) + return; + + CheatCode& cc = cl->GetCode(index); + if (cc.enabled == enabled) + return; + + cc.enabled = enabled; + if (!enabled) + cc.ApplyOnDisable(); + + if (enabled) + { + Host::AddFormattedOSDMessage(5.0f, Host::TranslateString("OSDMessage", "Cheat '%s' enabled."), + cc.description.c_str()); + } + else + { + Host::AddFormattedOSDMessage(5.0f, Host::TranslateString("OSDMessage", "Cheat '%s' disabled."), + cc.description.c_str()); + } + + if (save_to_file) + SaveCheatList(); +} + +void System::ApplyCheatCode(u32 index) +{ + if (!System::HasCheatList() || index >= System::GetCheatList()->GetCodeCount()) + return; + + const CheatCode& cc = System::GetCheatList()->GetCode(index); + if (!cc.enabled) + { + cc.Apply(); + Host::AddFormattedOSDMessage(5.0f, Host::TranslateString("OSDMessage", "Applied cheat '%s'."), + cc.description.c_str()); + } + else + { + Host::AddFormattedOSDMessage(5.0f, Host::TranslateString("OSDMessage", "Cheat '%s' is already enabled."), + cc.description.c_str()); + } +} + +void System::TogglePostProcessing() +{ + if (!IsValid()) + return; + + g_settings.display_post_processing = !g_settings.display_post_processing; + if (g_settings.display_post_processing) + { + Host::AddKeyedOSDMessage("PostProcessing", + Host::TranslateStdString("OSDMessage", "Post-processing is now enabled."), 10.0f); + + if (!Host::GetHostDisplay()->SetPostProcessingChain(g_settings.display_post_process_chain)) + Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "Failed to load post processing shader chain."), + 20.0f); + } + else + { + Host::AddKeyedOSDMessage("PostProcessing", + Host::TranslateStdString("OSDMessage", "Post-processing is now disabled."), 10.0f); + Host::GetHostDisplay()->SetPostProcessingChain({}); + } +} + +void System::ReloadPostProcessingShaders() +{ + if (!IsValid() || !g_settings.display_post_processing) + return; + + if (!Host::GetHostDisplay()->SetPostProcessingChain(g_settings.display_post_process_chain)) + Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "Failed to load post-processing shader chain."), 20.0f); + else + Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "Post-processing shaders reloaded."), 10.0f); +} + +void System::ToggleWidescreen() +{ + Panic("Fixme"); +#if 0 + g_settings.gpu_widescreen_hack = !g_settings.gpu_widescreen_hack; + + const GameSettings::Entry* gs = m_game_list->GetGameSettings(System::GetRunningPath(), System::GetRunningCode()); + DisplayAspectRatio user_ratio; + if (gs && gs->display_aspect_ratio.has_value()) + { + user_ratio = gs->display_aspect_ratio.value(); + } + else + { + std::lock_guard guard(m_settings_mutex); + user_ratio = Settings::ParseDisplayAspectRatio( + m_settings_interface + ->GetStringValue("Display", "AspectRatio", + Settings::GetDisplayAspectRatioName(Settings::DEFAULT_DISPLAY_ASPECT_RATIO)) + .c_str()) + .value_or(DisplayAspectRatio::Auto); + } + + if (user_ratio == DisplayAspectRatio::Auto || user_ratio == DisplayAspectRatio::PAR1_1 || + user_ratio == DisplayAspectRatio::R4_3) + { + g_settings.display_aspect_ratio = g_settings.gpu_widescreen_hack ? DisplayAspectRatio::R16_9 : user_ratio; + } + else + { + g_settings.display_aspect_ratio = g_settings.gpu_widescreen_hack ? user_ratio : DisplayAspectRatio::Auto; + } + + if (g_settings.gpu_widescreen_hack) + { + Host::AddKeyedFormattedOSDMessage( + "WidescreenHack", 5.0f, + Host::TranslateString("OSDMessage", "Widescreen hack is now enabled, and aspect ratio is set to %s."), + Host::TranslateString("DisplayAspectRatio", Settings::GetDisplayAspectRatioName(g_settings.display_aspect_ratio)) + .GetCharArray()); + } + else + { + Host::AddKeyedFormattedOSDMessage( + "WidescreenHack", 5.0f, + Host::TranslateString("OSDMessage", "Widescreen hack is now disabled, and aspect ratio is set to %s."), + Host::TranslateString("DisplayAspectRatio", Settings::GetDisplayAspectRatioName(g_settings.display_aspect_ratio)) + .GetCharArray()); + } + + GTE::UpdateAspectRatio(); +#endif +} diff --git a/src/core/system.h b/src/core/system.h index 3d77f2a56..6c9b0e4ba 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -34,12 +34,38 @@ struct SystemBootParameters bool force_software_renderer = false; }; +struct SaveStateInfo +{ + std::string path; + std::time_t timestamp; + s32 slot; + bool global; +}; + +struct ExtendedSaveStateInfo +{ + std::string path; + std::string title; + std::string game_code; + std::string media_path; + std::time_t timestamp; + s32 slot; + bool global; + + u32 screenshot_width; + u32 screenshot_height; + std::vector 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 @@ -82,6 +108,9 @@ DiscRegion GetRegionForExe(const char* path); DiscRegion GetRegionForPsf(const char* path); std::optional 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); + State GetState(); void SetState(State new_state); bool IsRunning(); @@ -147,12 +176,36 @@ float GetThrottleFrequency(); float GetCPUThreadUsage(); float GetCPUThreadAverageTime(); -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(std::shared_ptr parameters); +void PauseSystem(bool paused); +void ResetSystem(); +void DestroySystem(); + +/// Loads state from the specified filename. +bool LoadState(const char* filename); +bool SaveState(const char* filename); + +/// Loads the current emulation state from file. Specifying a slot of -1 loads the "resume" game state. +bool LoadStateFromSlot(bool global, s32 slot); + +/// Saves the current emulation state to a file. Specifying a slot of -1 saves the "resume" save state. +bool SaveStateToSlot(bool global, s32 slot); + +/// 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); @@ -163,7 +216,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); @@ -186,7 +238,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. @@ -233,6 +288,140 @@ void ApplyCheatCode(const CheatCode& code); /// Sets or clears the provided cheat list, applying every frame. void SetCheatList(std::unique_ptr 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 char* 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); + +/// Moves the current save state file to a backup name, if it exists. +void RenameCurrentSaveStateToBackup(const char* filename); + +/// 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 PowerOffSystem(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 GetUndoSaveStateInfo(); + +/// Undoes a load state, i.e. restores the state prior to the load. +bool UndoLoadState(); + +/// Returns true if the specified file/disc image is resumable. +bool CanResumeSystemFromFile(const char* filename); + +/// Loads the resume save state for the given game. Optionally boots the game anyway if loading fails. +bool ResumeSystemFromState(const char* filename, bool boot_on_failure); + +/// Loads the most recent resume save state. This may be global or per-game. +bool ResumeSystemFromMostRecentState(); + +/// Saves the resume save state, call when shutting down. +bool SaveResumeSaveState(); + +/// Returns a list of save states for the specified game code. +std::vector GetAvailableSaveStates(const char* game_code); + +/// Returns save state info if present. If game_code is null or empty, assumes global state. +std::optional GetSaveStateInfo(const char* game_code, s32 slot); + +/// Returns save state info from opened save state stream. +std::optional GetExtendedSaveStateInfo(ByteStream* stream); + +/// Returns save state info if present. If game_code is null or empty, assumes global state. +std::optional GetExtendedSaveStateInfo(const char* game_code, s32 slot); + +/// 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 the state should be saved on shutdown. +bool ShouldSaveResumeState(); + +/// Returns true if fast forwarding or slow motion is currently active. +bool IsRunningAtNonStandardSpeed(); + ////////////////////////////////////////////////////////////////////////// // Memory Save States (Rewind and Runahead) ////////////////////////////////////////////////////////////////////////// @@ -240,8 +429,76 @@ 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& 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 OnPerformanceMetricsUpdated(); + +/// Called when a save state is loading, before the file is processed. +// void OnSaveStateLoading(const std::string_view& filename); + +/// Called after a save state is successfully loaded. If the save state was invalid, was_successful will be false. +// void OnSaveStateLoaded(const std::string_view& filename, bool was_successful); + +/// Called when a save state is being created/saved. The compression/write to disk is asynchronous, so this callback +/// just signifies that the save has started, not necessarily completed. +// void OnSaveStateSaved(const std::string_view& filename); + +/// 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(); + +/// Provided by the host; called when a state is saved, and the frontend should invalidate its save state cache. +// void InvalidateSaveStateCache(); + +/// Requests a specific display window size. +// void RequestResizeHostDisplay(s32 width, s32 height); + +/// Safely executes a function on the VM thread. +// void RunOnCPUThread(std::function function, bool block = false); + +/// Asynchronously starts refreshing the game list. +// void RefreshGameListAsync(bool invalidate_cache); + +/// Cancels game list refresh, if there is one in progress. +// void CancelGameListRefresh(); + +/// 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 RequestVMShutdown(bool 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 diff --git a/src/duckstation-qt/advancedsettingswidget.cpp b/src/duckstation-qt/advancedsettingswidget.cpp index 59795afae..e4cc12618 100644 --- a/src/duckstation-qt/advancedsettingswidget.cpp +++ b/src/duckstation-qt/advancedsettingswidget.cpp @@ -155,17 +155,17 @@ static void addMSAATweakOption(SettingsDialog* dialog, QTableWidget* table, cons QComboBox* msaa = new QComboBox(table); QtUtils::FillComboBoxWithMSAAModes(msaa); const QVariant current_msaa_mode(QtUtils::GetMSAAModeValue( - static_cast(QtHostInterface::GetInstance()->GetIntSettingValue("GPU", "Multisamples", 1)), - QtHostInterface::GetInstance()->GetBoolSettingValue("GPU", "PerSampleShading", false))); + static_cast(dialog->getEffectiveIntValue("GPU", "Multisamples", 1)), + dialog->getEffectiveBoolValue("GPU", "PerSampleShading", false))); const int current_msaa_index = msaa->findData(current_msaa_mode); if (current_msaa_index >= 0) msaa->setCurrentIndex(current_msaa_index); - msaa->connect(msaa, QOverload::of(&QComboBox::currentIndexChanged), [msaa](int index) { + msaa->connect(msaa, QOverload::of(&QComboBox::currentIndexChanged), [dialog, msaa](int index) { uint multisamples; bool ssaa; QtUtils::DecodeMSAAModeValue(msaa->itemData(index), &multisamples, &ssaa); - QtHostInterface::GetInstance()->SetIntSettingValue("GPU", "Multisamples", static_cast(multisamples)); - QtHostInterface::GetInstance()->SetBoolSettingValue("GPU", "PerSampleShading", ssaa); + dialog->setIntSettingValue("GPU", "Multisamples", static_cast(multisamples)); + dialog->setBoolSettingValue("GPU", "PerSampleShading", ssaa); QtHostInterface::GetInstance()->applySettings(false); }); diff --git a/src/duckstation-qt/audiosettingswidget.cpp b/src/duckstation-qt/audiosettingswidget.cpp index 6edd9c37e..2315b1373 100644 --- a/src/duckstation-qt/audiosettingswidget.cpp +++ b/src/duckstation-qt/audiosettingswidget.cpp @@ -1,11 +1,11 @@ #include "audiosettingswidget.h" +#include "core/spu.h" #include "settingsdialog.h" #include "settingwidgetbinder.h" #include "util/audio_stream.h" #include -AudioSettingsWidget::AudioSettingsWidget(SettingsDialog* dialog, QWidget* parent) - : QWidget(parent), m_dialog(dialog) +AudioSettingsWidget::AudioSettingsWidget(SettingsDialog* dialog, QWidget* parent) : QWidget(parent), m_dialog(dialog) { SettingsInterface* sif = dialog->getSettingsInterface(); @@ -17,11 +17,11 @@ AudioSettingsWidget::AudioSettingsWidget(SettingsDialog* dialog, QWidget* parent qApp->translate("AudioBackend", Settings::GetAudioBackendDisplayName(static_cast(i)))); } - SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.audioBackend, "Audio", "Backend", - &Settings::ParseAudioBackend, &Settings::GetAudioBackendName, - Settings::DEFAULT_AUDIO_BACKEND); + SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.audioBackend, "Audio", "Backend", &Settings::ParseAudioBackend, + &Settings::GetAudioBackendName, Settings::DEFAULT_AUDIO_BACKEND); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.syncToOutput, "Audio", "Sync", true); - SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.bufferSize, "Audio", "BufferSize", HostInterface::DEFAULT_AUDIO_BUFFER_SIZE); + SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.bufferSize, "Audio", "BufferSize", + Settings::DEFAULT_AUDIO_BUFFER_SIZE); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.startDumpingOnBoot, "Audio", "DumpOnBoot", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.muteCDAudio, "CDROM", "MuteCDAudio", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.resampling, "Audio", "Resampling", true); @@ -84,7 +84,7 @@ void AudioSettingsWidget::updateBufferingLabel() return; } - const float max_latency = AudioStream::GetMaxLatency(HostInterface::AUDIO_SAMPLE_RATE, actual_buffer_size); + const float max_latency = AudioStream::GetMaxLatency(SPU::SAMPLE_RATE, actual_buffer_size); m_ui.bufferingLabel->setText(tr("Maximum Latency: %n frames (%1ms)", "", actual_buffer_size) .arg(static_cast(max_latency) * 1000.0, 0, 'f', 2)); } diff --git a/src/duckstation-qt/autoupdaterdialog.cpp b/src/duckstation-qt/autoupdaterdialog.cpp index d6ec64be6..0be3bd56d 100644 --- a/src/duckstation-qt/autoupdaterdialog.cpp +++ b/src/duckstation-qt/autoupdaterdialog.cpp @@ -418,7 +418,7 @@ void AutoUpdaterDialog::downloadUpdateClicked() bool AutoUpdaterDialog::updateNeeded() const { QString last_checked_sha = - QString::fromStdString(m_host_interface->GetStringSettingValue("AutoUpdater", "LastVersion")); + QString::fromStdString(Host::GetBaseStringSettingValue("AutoUpdater", "LastVersion")); Log_InfoPrintf("Current SHA: %s", g_scm_hash_str); Log_InfoPrintf("Latest SHA: %s", m_latest_sha.toUtf8().constData()); @@ -435,7 +435,7 @@ bool AutoUpdaterDialog::updateNeeded() const void AutoUpdaterDialog::skipThisUpdateClicked() { - m_host_interface->SetStringSettingValue("AutoUpdater", "LastVersion", m_latest_sha.toUtf8().constData()); + Host::SetBaseStringSettingValue("AutoUpdater", "LastVersion", m_latest_sha.toUtf8().constData()); done(0); } diff --git a/src/duckstation-qt/cheatmanagerdialog.cpp b/src/duckstation-qt/cheatmanagerdialog.cpp index 735adacd4..9bb8a2be0 100644 --- a/src/duckstation-qt/cheatmanagerdialog.cpp +++ b/src/duckstation-qt/cheatmanagerdialog.cpp @@ -4,6 +4,7 @@ #include "common/string_util.h" #include "core/bus.h" #include "core/cpu_core.h" +#include "core/host.h" #include "core/system.h" #include "qthostinterface.h" #include "qtutils.h" @@ -45,26 +46,33 @@ static QString formatHexAndDecValue(u32 value, u8 size, bool is_signed) return QStringLiteral("0x%1 (%2)").arg(static_cast(value), size, 16, QChar('0')).arg(static_cast(value)); } - static QString formatCheatCode(u32 address, u32 value, const MemoryAccessSize size) { - if (size == MemoryAccessSize::Byte && address <= 0x00200000) - return QStringLiteral("CHEAT CODE: %1 %2") - .arg(static_cast(address) + 0x30000000, 8, 16, QChar('0')).toUpper() - .arg(static_cast(value), 4, 16, QChar('0')).toUpper(); - else if (size == MemoryAccessSize::HalfWord && address <= 0x001FFFFE) - return QStringLiteral("CHEAT CODE: %1 %2") - .arg(static_cast(address) + 0x80000000, 8, 16, QChar('0')).toUpper() - .arg(static_cast(value), 4, 16, QChar('0')).toUpper(); - else if (size == MemoryAccessSize::Word && address <= 0x001FFFFC) - return QStringLiteral("CHEAT CODE: %1 %2") - .arg(static_cast(address) + 0x90000000, 8, 16, QChar('0')).toUpper() - .arg(static_cast(value), 8, 16, QChar('0')).toUpper(); - else - return QStringLiteral("OUTSIDE RAM RANGE. POKE %1 with %2") - .arg(static_cast(address), 8, 16, QChar('0')).toUpper() - .arg(static_cast(value), 8, 16, QChar('0')).toUpper(); + if (size == MemoryAccessSize::Byte && address <= 0x00200000) + return QStringLiteral("CHEAT CODE: %1 %2") + .arg(static_cast(address) + 0x30000000, 8, 16, QChar('0')) + .toUpper() + .arg(static_cast(value), 4, 16, QChar('0')) + .toUpper(); + else if (size == MemoryAccessSize::HalfWord && address <= 0x001FFFFE) + return QStringLiteral("CHEAT CODE: %1 %2") + .arg(static_cast(address) + 0x80000000, 8, 16, QChar('0')) + .toUpper() + .arg(static_cast(value), 4, 16, QChar('0')) + .toUpper(); + else if (size == MemoryAccessSize::Word && address <= 0x001FFFFC) + return QStringLiteral("CHEAT CODE: %1 %2") + .arg(static_cast(address) + 0x90000000, 8, 16, QChar('0')) + .toUpper() + .arg(static_cast(value), 8, 16, QChar('0')) + .toUpper(); + else + return QStringLiteral("OUTSIDE RAM RANGE. POKE %1 with %2") + .arg(static_cast(address), 8, 16, QChar('0')) + .toUpper() + .arg(static_cast(value), 8, 16, QChar('0')) + .toUpper(); } static QString formatValue(u32 value, bool is_signed) @@ -180,7 +188,8 @@ void CheatManagerDialog::connectUi() connect(m_ui.scanTable, &QTableWidget::itemChanged, this, &CheatManagerDialog::scanItemChanged); connect(m_ui.watchTable, &QTableWidget::itemChanged, this, &CheatManagerDialog::watchItemChanged); - connect(QtHostInterface::GetInstance(), &QtHostInterface::cheatEnabled, this, &CheatManagerDialog::setCheatCheckState); + connect(QtHostInterface::GetInstance(), &QtHostInterface::cheatEnabled, this, + &CheatManagerDialog::setCheatCheckState); } void CheatManagerDialog::showEvent(QShowEvent* event) @@ -332,12 +341,12 @@ CheatList* CheatManagerDialog::getCheatList() const CheatList* list = System::GetCheatList(); if (!list) { - QtHostInterface::GetInstance()->LoadCheatListFromGameTitle(); + System::LoadCheatListFromGameTitle(); list = System::GetCheatList(); } if (!list) { - QtHostInterface::GetInstance()->LoadCheatListFromDatabase(); + System::LoadCheatListFromDatabase(); list = System::GetCheatList(); } if (!list) @@ -408,7 +417,7 @@ void CheatManagerDialog::fillItemForCheatCode(QTreeWidgetItem* item, u32 index, void CheatManagerDialog::saveCheatList() { - QtHostInterface::GetInstance()->executeOnEmulationThread([]() { QtHostInterface::GetInstance()->SaveCheatList(); }); + QtHostInterface::GetInstance()->executeOnEmulationThread([]() { System::SaveCheatList(); }); } void CheatManagerDialog::cheatListCurrentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous) @@ -472,7 +481,7 @@ void CheatManagerDialog::cheatListItemChanged(QTreeWidgetItem* item, int column) QtHostInterface::GetInstance()->executeOnEmulationThread([index, new_enabled]() { System::GetCheatList()->SetCodeEnabled(static_cast(index), new_enabled); - QtHostInterface::GetInstance()->SaveCheatList(); + System::SaveCheatList(); }); } @@ -494,7 +503,7 @@ void CheatManagerDialog::activateCheat(u32 index) QtHostInterface::GetInstance()->executeOnEmulationThread([index, new_enabled]() { System::GetCheatList()->SetCodeEnabled(index, new_enabled); - QtHostInterface::GetInstance()->SaveCheatList(); + System::SaveCheatList(); }); } @@ -545,7 +554,7 @@ void CheatManagerDialog::addCodeClicked() QtHostInterface::GetInstance()->executeOnEmulationThread( [this, &new_code]() { System::GetCheatList()->AddCode(std::move(new_code)); - QtHostInterface::GetInstance()->SaveCheatList(); + System::SaveCheatList(); }, true); } @@ -591,7 +600,7 @@ void CheatManagerDialog::editCodeClicked() QtHostInterface::GetInstance()->executeOnEmulationThread( [index, &new_code]() { System::GetCheatList()->SetCode(static_cast(index), std::move(new_code)); - QtHostInterface::GetInstance()->SaveCheatList(); + System::SaveCheatList(); }, true); } @@ -617,7 +626,7 @@ void CheatManagerDialog::deleteCodeClicked() QtHostInterface::GetInstance()->executeOnEmulationThread( [index]() { System::GetCheatList()->RemoveCode(static_cast(index)); - QtHostInterface::GetInstance()->SaveCheatList(); + System::SaveCheatList(); }, true); updateCheatList(); @@ -658,7 +667,7 @@ void CheatManagerDialog::importFromFileTriggered() [&new_cheats]() { DebugAssert(System::HasCheatList()); System::GetCheatList()->MergeList(new_cheats); - QtHostInterface::GetInstance()->SaveCheatList(); + System::SaveCheatList(); }, true); updateCheatList(); @@ -681,7 +690,7 @@ void CheatManagerDialog::importFromTextTriggered() [&new_cheats]() { DebugAssert(System::HasCheatList()); System::GetCheatList()->MergeList(new_cheats); - QtHostInterface::GetInstance()->SaveCheatList(); + System::SaveCheatList(); }, true); updateCheatList(); @@ -707,8 +716,7 @@ void CheatManagerDialog::clearClicked() return; } - QtHostInterface::GetInstance()->executeOnEmulationThread([] { QtHostInterface::GetInstance()->ClearCheatList(true); }, - true); + QtHostInterface::GetInstance()->executeOnEmulationThread([] { System::ClearCheatList(true); }, true); updateCheatList(); } @@ -723,8 +731,7 @@ void CheatManagerDialog::resetClicked() return; } - QtHostInterface::GetInstance()->executeOnEmulationThread([] { QtHostInterface::GetInstance()->DeleteCheatList(); }, - true); + QtHostInterface::GetInstance()->executeOnEmulationThread([] { System::DeleteCheatList(); }, true); updateCheatList(); } @@ -737,11 +744,11 @@ void CheatManagerDialog::addToWatchClicked() for (int index = indexFirst; index <= indexLast; index++) { - const MemoryScan::Result& res = m_scanner.GetResults()[static_cast(index)]; - m_watch.AddEntry(StringUtil::StdStringFromFormat("0x%08x", res.address), res.address, m_scanner.GetSize(), m_scanner.GetValueSigned(), false); - updateWatch(); -} - + const MemoryScan::Result& res = m_scanner.GetResults()[static_cast(index)]; + m_watch.AddEntry(StringUtil::StdStringFromFormat("0x%08x", res.address), res.address, m_scanner.GetSize(), + m_scanner.GetValueSigned(), false); + updateWatch(); + } } void CheatManagerDialog::addManualWatchAddressClicked() @@ -779,9 +786,9 @@ void CheatManagerDialog::removeWatchClicked() for (int index = indexLast; index >= indexFirst; index--) { - m_watch.RemoveEntry(static_cast(index)); - updateWatch(); -} + m_watch.RemoveEntry(static_cast(index)); + updateWatch(); + } } void CheatManagerDialog::scanCurrentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous) @@ -856,9 +863,9 @@ void CheatManagerDialog::watchItemChanged(QTableWidgetItem* item) { uint value; if (item->text()[1] == 'x' || item->text()[1] == 'X') - value = item->text().toUInt(&value_ok, 16); + value = item->text().toUInt(&value_ok, 16); else - value = item->text().toUInt(&value_ok); + value = item->text().toUInt(&value_ok); if (value_ok) m_watch.SetEntryValue(index, static_cast(value)); } @@ -930,7 +937,6 @@ void CheatManagerDialog::updateResults() row++; } m_ui.scanResultsCount->setText(QString::number(m_scanner.GetResultCount())); - } else m_ui.scanResultsCount->setText("0"); @@ -1003,12 +1009,12 @@ void CheatManagerDialog::updateWatch() value_item = new QTableWidgetItem(formatHexAndDecValue(res.value, 8, res.is_signed)); m_ui.watchTable->setItem(row, 3, value_item); - + QTableWidgetItem* freeze_item = new QTableWidgetItem(); freeze_item->setFlags(freeze_item->flags() | (Qt::ItemIsEditable | Qt::ItemIsUserCheckable)); freeze_item->setCheckState(res.freeze ? Qt::Checked : Qt::Unchecked); m_ui.watchTable->setItem(row, 4, freeze_item); - + row++; } } diff --git a/src/duckstation-qt/consolesettingswidget.cpp b/src/duckstation-qt/consolesettingswidget.cpp index 96a85c6e0..b3057480b 100644 --- a/src/duckstation-qt/consolesettingswidget.cpp +++ b/src/duckstation-qt/consolesettingswidget.cpp @@ -140,7 +140,7 @@ void ConsoleSettingsWidget::onEnableCPUClockSpeedControlChecked(int state) return; } - QtHost::SetBaseBoolSettingValue("UI", "CPUOverclockingWarningShown", true); + Host::SetBaseBoolSettingValue("UI", "CPUOverclockingWarningShown", true); } m_ui.cpuClockSpeed->setEnabled(state == Qt::Checked); diff --git a/src/duckstation-qt/controllerbindingwidget_analog_controller.ui b/src/duckstation-qt/controllerbindingwidget_analog_controller.ui index 41d915b11..660e6ef29 100644 --- a/src/duckstation-qt/controllerbindingwidget_analog_controller.ui +++ b/src/duckstation-qt/controllerbindingwidget_analog_controller.ui @@ -624,13 +624,6 @@ - - - - UNUSED - - - @@ -1108,7 +1101,7 @@ - :/images/dualshock-2.png + :/controllers/analog_controller.svg true @@ -1316,6 +1309,7 @@ + diff --git a/src/duckstation-qt/controllerbindingwidget_digital_controller.ui b/src/duckstation-qt/controllerbindingwidget_digital_controller.ui new file mode 100644 index 000000000..a3456d4fe --- /dev/null +++ b/src/duckstation-qt/controllerbindingwidget_digital_controller.ui @@ -0,0 +1,737 @@ + + + ControllerBindingWidget_DigitalController + + + + 0 + 0 + 1100 + 500 + + + + + 0 + 0 + + + + + 1100 + 500 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + L1 + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + L2 + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + R2 + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + R1 + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Face Buttons + + + + + + Cross + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Square + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Triangle + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Circle + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 400 + 266 + + + + + + + :/controllers/digital_controller.svg + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + D-Pad + + + + + + Down + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Left + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Up + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Right + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Select + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Start + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + + + + InputBindingWidget + QPushButton +
inputbindingwidgets.h
+
+
+ + + + + +
diff --git a/src/duckstation-qt/controllerbindingwidgets.cpp b/src/duckstation-qt/controllerbindingwidgets.cpp index 0a63663f1..e47f139eb 100644 --- a/src/duckstation-qt/controllerbindingwidgets.cpp +++ b/src/duckstation-qt/controllerbindingwidgets.cpp @@ -23,10 +23,6 @@ ControllerBindingWidget::ControllerBindingWidget(QWidget* parent, ControllerSett populateControllerTypes(); onTypeChanged(); - ControllerSettingWidgetBinder::BindWidgetToInputProfileString(m_dialog->getProfileSettingsInterface(), - m_ui.controllerType, m_config_section, "Type", - Controller::GetDefaultPadType(port)); - connect(m_ui.controllerType, QOverload::of(&QComboBox::currentIndexChanged), this, &ControllerBindingWidget::onTypeChanged); connect(m_ui.automaticBinding, &QPushButton::clicked, this, &ControllerBindingWidget::doAutomaticBinding); @@ -51,30 +47,32 @@ void ControllerBindingWidget::populateControllerTypes() m_ui.controllerType->addItem(qApp->translate("ControllerType", cinfo->display_name), QVariant(static_cast(i))); } -} -void ControllerBindingWidget::onTypeChanged() -{ - const bool is_initializing = (m_current_widget == nullptr); const std::string controller_type_name( m_dialog->getStringValue(m_config_section.c_str(), "Type", Controller::GetDefaultPadType(m_port_number))); m_controller_type = Settings::ParseControllerTypeName(controller_type_name.c_str()).value_or(ControllerType::None); - if (!is_initializing) - { - m_ui.verticalLayout->removeWidget(m_current_widget); - delete m_current_widget; - m_current_widget = nullptr; - } - const int index = m_ui.controllerType->findData(QVariant(static_cast(m_controller_type))); if (index >= 0 && index != m_ui.controllerType->currentIndex()) { QSignalBlocker sb(m_ui.controllerType); m_ui.controllerType->setCurrentIndex(index); } +} - if (m_controller_type == ControllerType::AnalogController) +void ControllerBindingWidget::populateBindingWidget() +{ + const bool is_initializing = (m_current_widget == nullptr); + if (!is_initializing) + { + m_ui.verticalLayout->removeWidget(m_current_widget); + delete m_current_widget; + m_current_widget = nullptr; + } + + if (m_controller_type == ControllerType::DigitalController) + m_current_widget = ControllerBindingWidget_DigitalController::createInstance(this); + else if (m_controller_type == ControllerType::AnalogController) m_current_widget = ControllerBindingWidget_AnalogController::createInstance(this); else m_current_widget = new ControllerBindingWidget_Base(this); @@ -86,6 +84,27 @@ void ControllerBindingWidget::onTypeChanged() m_dialog->updateListDescription(m_port_number, this); } +void ControllerBindingWidget::onTypeChanged() +{ + bool ok; + const int index = m_ui.controllerType->currentData().toInt(&ok); + if (!ok || index < 0 || index >= static_cast(ControllerType::Count)) + return; + + m_controller_type = static_cast(index); + + SettingsInterface* sif = m_dialog->getProfileSettingsInterface(); + if (sif) + sif->SetStringValue(m_config_section.c_str(), "Type", Settings::GetControllerTypeName(m_controller_type)); + else + Host::SetBaseStringSettingValue(m_config_section.c_str(), "Type", Settings::GetControllerTypeName(m_controller_type)); + + // TODO: reloadInputProfile() ? + QtHostInterface::GetInstance()->applySettings(); + + populateBindingWidget(); +} + void ControllerBindingWidget::doAutomaticBinding() { QMenu menu(this); @@ -271,6 +290,29 @@ void ControllerBindingWidget_Base::initBindingWidgets() Controller::DEFAULT_MOTOR_SCALE); } +////////////////////////////////////////////////////////////////////////// + +ControllerBindingWidget_DigitalController::ControllerBindingWidget_DigitalController(ControllerBindingWidget* parent) + : ControllerBindingWidget_Base(parent) +{ + m_ui.setupUi(this); + initBindingWidgets(); +} + +ControllerBindingWidget_DigitalController::~ControllerBindingWidget_DigitalController() {} + +QIcon ControllerBindingWidget_DigitalController::getIcon() const +{ + return QIcon::fromTheme("gamepad-line"); +} + +ControllerBindingWidget_Base* ControllerBindingWidget_DigitalController::createInstance(ControllerBindingWidget* parent) +{ + return new ControllerBindingWidget_DigitalController(parent); +} + +////////////////////////////////////////////////////////////////////////// + ControllerBindingWidget_AnalogController::ControllerBindingWidget_AnalogController(ControllerBindingWidget* parent) : ControllerBindingWidget_Base(parent) { diff --git a/src/duckstation-qt/controllerbindingwidgets.h b/src/duckstation-qt/controllerbindingwidgets.h index e631e2ab3..02606f7cd 100644 --- a/src/duckstation-qt/controllerbindingwidgets.h +++ b/src/duckstation-qt/controllerbindingwidgets.h @@ -5,6 +5,7 @@ #include "ui_controllerbindingwidget.h" #include "ui_controllerbindingwidget_analog_controller.h" +#include "ui_controllerbindingwidget_digital_controller.h" class InputBindingWidget; class ControllerSettingsDialog; @@ -32,6 +33,7 @@ private Q_SLOTS: private: void populateControllerTypes(); + void populateBindingWidget(); void doDeviceAutomaticBinding(const QString& device); void saveAndRefresh(); @@ -74,6 +76,22 @@ protected: void initBindingWidgets(); }; +class ControllerBindingWidget_DigitalController final : public ControllerBindingWidget_Base +{ + Q_OBJECT + +public: + ControllerBindingWidget_DigitalController(ControllerBindingWidget* parent); + ~ControllerBindingWidget_DigitalController(); + + QIcon getIcon() const override; + + static ControllerBindingWidget_Base* createInstance(ControllerBindingWidget* parent); + +private: + Ui::ControllerBindingWidget_DigitalController m_ui; +}; + class ControllerBindingWidget_AnalogController final : public ControllerBindingWidget_Base { Q_OBJECT diff --git a/src/duckstation-qt/controllersettingsdialog.cpp b/src/duckstation-qt/controllersettingsdialog.cpp index cae69ed06..c1164735c 100644 --- a/src/duckstation-qt/controllersettingsdialog.cpp +++ b/src/duckstation-qt/controllersettingsdialog.cpp @@ -5,7 +5,7 @@ #include "controllerglobalsettingswidget.h" #include "core/controller.h" #include "core/host_settings.h" -#include "frontend-common/ini_settings_interface.h" +#include "util/ini_settings_interface.h" #include "frontend-common/input_manager.h" #include "hotkeysettingswidget.h" #include "qthostinterface.h" @@ -37,16 +37,18 @@ ControllerSettingsDialog::ControllerSettingsDialog(QWidget* parent /* = nullptr connect(m_ui.deleteProfile, &QPushButton::clicked, this, &ControllerSettingsDialog::onDeleteProfileClicked); connect(m_ui.restoreDefaults, &QPushButton::clicked, this, &ControllerSettingsDialog::onRestoreDefaultsClicked); - // connect(g_emu_thread, &EmuThread::onInputDevicesEnumerated, this, - // &ControllerSettingsDialog::onInputDevicesEnumerated); connect(g_emu_thread, &EmuThread::onInputDeviceConnected, - // this, &ControllerSettingsDialog::onInputDeviceConnected); connect(g_emu_thread, - // &EmuThread::onInputDeviceDisconnected, this, &ControllerSettingsDialog::onInputDeviceDisconnected); - // connect(g_emu_thread, &EmuThread::onVibrationMotorsEnumerated, this, - // &ControllerSettingsDialog::onVibrationMotorsEnumerated); + connect(QtHostInterface::GetInstance(), &QtHostInterface::onInputDevicesEnumerated, this, + &ControllerSettingsDialog::onInputDevicesEnumerated); + connect(QtHostInterface::GetInstance(), &QtHostInterface::onInputDeviceConnected, this, + &ControllerSettingsDialog::onInputDeviceConnected); + connect(QtHostInterface::GetInstance(), &QtHostInterface::onInputDeviceDisconnected, this, + &ControllerSettingsDialog::onInputDeviceDisconnected); + connect(QtHostInterface::GetInstance(), &QtHostInterface::onVibrationMotorsEnumerated, this, + &ControllerSettingsDialog::onVibrationMotorsEnumerated); // trigger a device enumeration to populate the device list - // g_emu_thread->enumerateInputDevices(); - // g_emu_thread->enumerateVibrationMotors(); + QtHostInterface::GetInstance()->enumerateInputDevices(); + QtHostInterface::GetInstance()->enumerateVibrationMotors(); } ControllerSettingsDialog::~ControllerSettingsDialog() = default; @@ -212,9 +214,7 @@ void ControllerSettingsDialog::onInputDeviceConnected(const QString& identifier, { m_device_list.emplace_back(identifier, device_name); m_global_settings->addDeviceToList(identifier, device_name); -#if 0 - g_emu_thread->enumerateVibrationMotors(); -#endif + QtHostInterface::GetInstance()->enumerateVibrationMotors(); } void ControllerSettingsDialog::onInputDeviceDisconnected(const QString& identifier) @@ -229,9 +229,7 @@ void ControllerSettingsDialog::onInputDeviceDisconnected(const QString& identifi } m_global_settings->removeDeviceFromList(identifier); -#if 0 - g_emu_thread->enumerateVibrationMotors(); -#endif + QtHostInterface::GetInstance()->enumerateVibrationMotors(); } void ControllerSettingsDialog::onVibrationMotorsEnumerated(const QList& motors) @@ -276,7 +274,7 @@ void ControllerSettingsDialog::setBoolValue(const char* section, const char* key } else { - QtHost::SetBaseBoolSettingValue(section, key, value); + Host::SetBaseBoolSettingValue(section, key, value); QtHostInterface::GetInstance()->applySettings(); } } @@ -291,7 +289,7 @@ void ControllerSettingsDialog::setStringValue(const char* section, const char* k } else { - QtHost::SetBaseStringSettingValue(key, section, value); + Host::SetBaseStringSettingValue(key, section, value); QtHostInterface::GetInstance()->applySettings(); } } @@ -306,7 +304,7 @@ void ControllerSettingsDialog::clearSettingValue(const char* section, const char } else { - QtHost::RemoveBaseSettingValue(section, key); + Host::DeleteBaseSettingValue(section, key); QtHostInterface::GetInstance()->applySettings(); } } @@ -395,11 +393,11 @@ void ControllerSettingsDialog::updateListDescription(u32 global_slot, Controller for (int i = 0; i < m_ui.settingsCategory->count(); i++) { QListWidgetItem* item = m_ui.settingsCategory->item(i); - const QVariant data(item->data(Qt::UserRole)); + const QVariant item_data(item->data(Qt::UserRole)); bool is_ok; - if (data.toUInt(&is_ok) == global_slot && is_ok) + if (item_data.toUInt(&is_ok) == global_slot && is_ok) { - const bool is_mtap_port = Controller::PadIsMultitapSlot(global_slot); + //const bool is_mtap_port = Controller::PadIsMultitapSlot(global_slot); const auto [port, slot] = Controller::ConvertPadToPortAndSlot(global_slot); const bool mtap_enabled = getBoolValue("Pad", (port == 0) ? "MultitapPort1" : "MultitapPort2", false); diff --git a/src/duckstation-qt/controllersettingwidgetbinder.h b/src/duckstation-qt/controllersettingwidgetbinder.h index 28caad36b..46d528722 100644 --- a/src/duckstation-qt/controllersettingwidgetbinder.h +++ b/src/duckstation-qt/controllersettingwidgetbinder.h @@ -45,7 +45,7 @@ static void BindWidgetToInputProfileBool(SettingsInterface* sif, WidgetType* wid Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() { const bool new_value = Accessor::getBoolValue(widget); - QtHost::SetBaseBoolSettingValue(section.c_str(), key.c_str(), new_value); + Host::SetBaseBoolSettingValue(section.c_str(), key.c_str(), new_value); QtHostInterface::GetInstance()->applySettings(); }); } @@ -77,7 +77,7 @@ static void BindWidgetToInputProfileFloat(SettingsInterface* sif, WidgetType* wi Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() { const float new_value = Accessor::getFloatValue(widget); - QtHost::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value); + Host::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value); QtHostInterface::GetInstance()->applySettings(); }); } @@ -109,7 +109,7 @@ static void BindWidgetToInputProfileNormalized(SettingsInterface* sif, WidgetTyp Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key), range]() { const float new_value = (static_cast(Accessor::getIntValue(widget)) / range); - QtHost::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value); + Host::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value); QtHostInterface::GetInstance()->applySettings(); }); } @@ -150,9 +150,9 @@ static void BindWidgetToInputProfileString(SettingsInterface* sif, WidgetType* w Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() { const QString new_value = Accessor::getStringValue(widget); if (!new_value.isEmpty()) - QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), new_value.toUtf8().constData()); + Host::SetBaseStringSettingValue(section.c_str(), key.c_str(), new_value.toUtf8().constData()); else - QtHost::RemoveBaseSettingValue(section.c_str(), key.c_str()); + Host::DeleteBaseSettingValue(section.c_str(), key.c_str()); QtHostInterface::GetInstance()->applySettings(); }); diff --git a/src/duckstation-qt/duckstation-qt.vcxproj b/src/duckstation-qt/duckstation-qt.vcxproj index 46d42a100..c6b3237fc 100644 --- a/src/duckstation-qt/duckstation-qt.vcxproj +++ b/src/duckstation-qt/duckstation-qt.vcxproj @@ -170,6 +170,9 @@ Document + + Document + Document diff --git a/src/duckstation-qt/gamelistsearchdirectoriesmodel.cpp b/src/duckstation-qt/gamelistsearchdirectoriesmodel.cpp index de2317307..3ef586602 100644 --- a/src/duckstation-qt/gamelistsearchdirectoriesmodel.cpp +++ b/src/duckstation-qt/gamelistsearchdirectoriesmodel.cpp @@ -144,11 +144,11 @@ void GameListSearchDirectoriesModel::openEntryInExplorer(QWidget* parent, int ro void GameListSearchDirectoriesModel::loadFromSettings() { - std::vector path_list = m_host_interface->GetSettingStringList("GameList", "Paths"); + std::vector path_list = Host::GetBaseStringListSetting("GameList", "Paths"); for (std::string& entry : path_list) m_entries.push_back({QString::fromStdString(entry), false}); - path_list = m_host_interface->GetSettingStringList("GameList", "RecursivePaths"); + path_list = Host::GetBaseStringListSetting("GameList", "RecursivePaths"); for (std::string& entry : path_list) m_entries.push_back({QString::fromStdString(entry), true}); } @@ -167,12 +167,12 @@ void GameListSearchDirectoriesModel::saveToSettings() } if (paths.empty()) - m_host_interface->RemoveSettingValue("GameList", "Paths"); + Host::DeleteBaseSettingValue("GameList", "Paths"); else - m_host_interface->SetStringListSettingValue("GameList", "Paths", paths); + Host::SetBaseStringListSettingValue("GameList", "Paths", paths); if (recursive_paths.empty()) - m_host_interface->RemoveSettingValue("GameList", "RecursivePaths"); + Host::DeleteBaseSettingValue("GameList", "RecursivePaths"); else - m_host_interface->SetStringListSettingValue("GameList", "RecursivePaths", recursive_paths); + Host::SetBaseStringListSettingValue("GameList", "RecursivePaths", recursive_paths); } diff --git a/src/duckstation-qt/gamelistsettingswidget.cpp b/src/duckstation-qt/gamelistsettingswidget.cpp index 86a7a0e6f..26afccf0e 100644 --- a/src/duckstation-qt/gamelistsettingswidget.cpp +++ b/src/duckstation-qt/gamelistsettingswidget.cpp @@ -52,7 +52,7 @@ GameListSettingsWidget::~GameListSettingsWidget() = default; bool GameListSettingsWidget::addExcludedPath(const std::string& path) { - if (!QtHostInterface::GetInstance()->AddValueToStringList("GameList", "ExcludedPaths", path.c_str())) + if (!Host::AddValueToBaseStringListSetting("GameList", "ExcludedPaths", path.c_str())) return false; m_ui.excludedPaths->addItem(QString::fromStdString(path)); @@ -64,7 +64,7 @@ void GameListSettingsWidget::refreshExclusionList() { m_ui.excludedPaths->clear(); - const std::vector paths(QtHostInterface::GetInstance()->GetSettingStringList("GameList", "ExcludedPaths")); + const std::vector paths(Host::GetBaseStringListSetting("GameList", "ExcludedPaths")); for (const std::string& path : paths) m_ui.excludedPaths->addItem(QString::fromStdString(path)); } @@ -158,7 +158,7 @@ void GameListSettingsWidget::onRemoveExcludedPathButtonClicked() if (!item) return; - QtHostInterface::GetInstance()->RemoveValueFromStringList("GameList", "ExcludedPaths", item->text().toUtf8().constData()); + Host::RemoveValueFromBaseStringListSetting("GameList", "ExcludedPaths", item->text().toUtf8().constData()); delete item; QtHostInterface::GetInstance()->refreshGameList(); diff --git a/src/duckstation-qt/gamelistwidget.cpp b/src/duckstation-qt/gamelistwidget.cpp index 9e66dd8e0..4720fdb86 100644 --- a/src/duckstation-qt/gamelistwidget.cpp +++ b/src/duckstation-qt/gamelistwidget.cpp @@ -1,5 +1,6 @@ #include "gamelistwidget.h" #include "common/string_util.h" +#include "core/host_settings.h" #include "core/settings.h" #include "frontend-common/game_list.h" #include "gamelistmodel.h" @@ -43,8 +44,8 @@ void GameListWidget::initialize(QtHostInterface* host_interface) connect(m_host_interface, &QtHostInterface::gameListRefreshed, this, &GameListWidget::onGameListRefreshed); m_model = new GameListModel(m_game_list, this); - m_model->setCoverScale(host_interface->GetFloatSettingValue("UI", "GameListCoverArtScale", 0.45f)); - m_model->setShowCoverTitles(host_interface->GetBoolSettingValue("UI", "GameListShowCoverTitles", true)); + m_model->setCoverScale(Host::GetBaseFloatSettingValue("UI", "GameListCoverArtScale", 0.45f)); + m_model->setShowCoverTitles(Host::GetBaseBoolSettingValue("UI", "GameListShowCoverTitles", true)); m_sort_model = new GameListSortModel(m_model); m_sort_model->setSourceModel(m_model); @@ -98,7 +99,7 @@ void GameListWidget::initialize(QtHostInterface* host_interface) insertWidget(1, m_list_view); - if (m_host_interface->GetBoolSettingValue("UI", "GameListGridView", false)) + if (Host::GetBaseBoolSettingValue("UI", "GameListGridView", false)) setCurrentIndex(1); else setCurrentIndex(0); @@ -210,7 +211,7 @@ void GameListWidget::listZoom(float delta) static constexpr float MAX_SCALE = 2.0f; const float new_scale = std::clamp(m_model->getCoverScale() + delta, MIN_SCALE, MAX_SCALE); - m_host_interface->SetFloatSettingValue("UI", "GameListCoverArtScale", new_scale); + Host::SetBaseFloatSettingValue("UI", "GameListCoverArtScale", new_scale); m_model->setCoverScale(new_scale); updateListFont(); @@ -237,7 +238,7 @@ void GameListWidget::showGameList() if (currentIndex() == 0) return; - m_host_interface->SetBoolSettingValue("UI", "GameListGridView", false); + Host::SetBaseBoolSettingValue("UI", "GameListGridView", false); setCurrentIndex(0); resizeTableViewColumnsToFit(); } @@ -247,7 +248,7 @@ void GameListWidget::showGameGrid() if (currentIndex() == 1) return; - m_host_interface->SetBoolSettingValue("UI", "GameListGridView", true); + Host::SetBaseBoolSettingValue("UI", "GameListGridView", true); setCurrentIndex(1); } @@ -256,7 +257,7 @@ void GameListWidget::setShowCoverTitles(bool enabled) if (m_model->getShowCoverTitles() == enabled) return; - m_host_interface->SetBoolSettingValue("UI", "GameListShowCoverTitles", enabled); + Host::SetBaseBoolSettingValue("UI", "GameListShowCoverTitles", enabled); m_model->setShowCoverTitles(enabled); if (isShowingGameGrid()) m_model->refresh(); @@ -287,7 +288,7 @@ void GameListWidget::resizeTableViewColumnsToFit() 200, // genre 50, // year 100, // players - 80, // size + 80, // size 50, // region 100 // compatibility }); @@ -317,8 +318,8 @@ void GameListWidget::loadTableViewColumnVisibilitySettings() for (int column = 0; column < GameListModel::Column_Count; column++) { - const bool visible = m_host_interface->GetBoolSettingValue( - "GameListTableView", getColumnVisibilitySettingsKeyName(column), DEFAULT_VISIBILITY[column]); + const bool visible = Host::GetBaseBoolSettingValue("GameListTableView", getColumnVisibilitySettingsKeyName(column), + DEFAULT_VISIBILITY[column]); m_table_view->setColumnHidden(column, !visible); } } @@ -328,14 +329,14 @@ void GameListWidget::saveTableViewColumnVisibilitySettings() for (int column = 0; column < GameListModel::Column_Count; column++) { const bool visible = !m_table_view->isColumnHidden(column); - m_host_interface->SetBoolSettingValue("GameListTableView", getColumnVisibilitySettingsKeyName(column), visible); + Host::SetBaseBoolSettingValue("GameListTableView", getColumnVisibilitySettingsKeyName(column), visible); } } void GameListWidget::saveTableViewColumnVisibilitySettings(int column) { const bool visible = !m_table_view->isColumnHidden(column); - m_host_interface->SetBoolSettingValue("GameListTableView", getColumnVisibilitySettingsKeyName(column), visible); + Host::SetBaseBoolSettingValue("GameListTableView", getColumnVisibilitySettingsKeyName(column), visible); } void GameListWidget::loadTableViewColumnSortSettings() @@ -344,10 +345,10 @@ void GameListWidget::loadTableViewColumnSortSettings() const bool DEFAULT_SORT_DESCENDING = false; const GameListModel::Column sort_column = - GameListModel::getColumnIdForName(m_host_interface->GetStringSettingValue("GameListTableView", "SortColumn")) + GameListModel::getColumnIdForName(Host::GetBaseStringSettingValue("GameListTableView", "SortColumn")) .value_or(DEFAULT_SORT_COLUMN); const bool sort_descending = - m_host_interface->GetBoolSettingValue("GameListTableView", "SortDescending", DEFAULT_SORT_DESCENDING); + Host::GetBaseBoolSettingValue("GameListTableView", "SortDescending", DEFAULT_SORT_DESCENDING); m_sort_model->sort(sort_column, sort_descending ? Qt::DescendingOrder : Qt::AscendingOrder); } @@ -358,11 +359,11 @@ void GameListWidget::saveTableViewColumnSortSettings() if (sort_column >= 0 && sort_column < GameListModel::Column_Count) { - m_host_interface->SetStringSettingValue( - "GameListTableView", "SortColumn", GameListModel::getColumnName(static_cast(sort_column))); + Host::SetBaseStringSettingValue("GameListTableView", "SortColumn", + GameListModel::getColumnName(static_cast(sort_column))); } - m_host_interface->SetBoolSettingValue("GameListTableView", "SortDescending", sort_descending); + Host::SetBaseBoolSettingValue("GameListTableView", "SortDescending", sort_descending); } const GameListEntry* GameListWidget::getSelectedEntry() const diff --git a/src/duckstation-qt/inputbindingdialog.cpp b/src/duckstation-qt/inputbindingdialog.cpp index 2c2b8fc95..e62bb065c 100644 --- a/src/duckstation-qt/inputbindingdialog.cpp +++ b/src/duckstation-qt/inputbindingdialog.cpp @@ -235,9 +235,9 @@ void InputBindingDialog::saveListToSettings() else { if (!m_bindings.empty()) - QtHost::SetBaseStringListSettingValue(m_section_name.c_str(), m_key_name.c_str(), m_bindings); + Host::SetBaseStringListSettingValue(m_section_name.c_str(), m_key_name.c_str(), m_bindings); else - QtHost::RemoveBaseSettingValue(m_section_name.c_str(), m_key_name.c_str()); + Host::DeleteBaseSettingValue(m_section_name.c_str(), m_key_name.c_str()); QtHostInterface::GetInstance()->reloadInputBindings(); } } diff --git a/src/duckstation-qt/inputbindingwidgets.cpp b/src/duckstation-qt/inputbindingwidgets.cpp index 81c29af3c..f4bc2af81 100644 --- a/src/duckstation-qt/inputbindingwidgets.cpp +++ b/src/duckstation-qt/inputbindingwidgets.cpp @@ -214,7 +214,7 @@ void InputBindingWidget::setNewBinding() } else { - QtHost::SetBaseStringSettingValue(m_section_name.c_str(), m_key_name.c_str(), new_binding.c_str()); + Host::SetBaseStringSettingValue(m_section_name.c_str(), m_key_name.c_str(), new_binding.c_str()); QtHostInterface::GetInstance()->reloadInputBindings(); } } @@ -234,7 +234,7 @@ void InputBindingWidget::clearBinding() } else { - QtHost::RemoveBaseSettingValue(m_section_name.c_str(), m_key_name.c_str()); + Host::DeleteBaseSettingValue(m_section_name.c_str(), m_key_name.c_str()); QtHostInterface::GetInstance()->reloadInputBindings(); } reloadBinding(); @@ -390,7 +390,7 @@ void InputVibrationBindingWidget::setKey(ControllerSettingsDialog* dialog, std:: void InputVibrationBindingWidget::clearBinding() { m_binding = {}; - QtHost::RemoveBaseSettingValue(m_section_name.c_str(), m_key_name.c_str()); + Host::DeleteBaseSettingValue(m_section_name.c_str(), m_key_name.c_str()); QtHostInterface::GetInstance()->reloadInputBindings(); setText(QString()); } @@ -427,7 +427,7 @@ void InputVibrationBindingWidget::onClicked() const QString new_value(input_dialog.textValue()); m_binding = new_value.toStdString(); - QtHost::SetBaseStringSettingValue(m_section_name.c_str(), m_key_name.c_str(), m_binding.c_str()); + Host::SetBaseStringSettingValue(m_section_name.c_str(), m_key_name.c_str(), m_binding.c_str()); setText(new_value); } diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index 75c0cf87a..7f282c035 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -3,6 +3,7 @@ #include "autoupdaterdialog.h" #include "cheatmanagerdialog.h" #include "common/assert.h" +#include "core/host.h" #include "core/host_display.h" #include "core/settings.h" #include "core/system.h" @@ -113,20 +114,15 @@ void MainWindow::initializeAndShow() #endif } -void MainWindow::reportError(const QString& message) +void MainWindow::reportError(const QString& title, const QString& message) { - QMessageBox::critical(this, tr("DuckStation"), message, QMessageBox::Ok); + QMessageBox::critical(this, title, message, QMessageBox::Ok); focusDisplayWidget(); } -void MainWindow::reportMessage(const QString& message) +bool MainWindow::confirmMessage(const QString& title, const QString& message) { - m_ui.statusBar->showMessage(message, 2000); -} - -bool MainWindow::confirmMessage(const QString& message) -{ - const int result = QMessageBox::question(this, tr("DuckStation"), message); + const int result = QMessageBox::question(this, title, message); focusDisplayWidget(); return (result == QMessageBox::Yes); @@ -134,7 +130,7 @@ bool MainWindow::confirmMessage(const QString& message) bool MainWindow::shouldHideCursorInFullscreen() const { - return g_host_interface->GetBoolSettingValue("Main", "HideCursorInFullscreen", true); + return Host::GetBoolSettingValue("Main", "HideCursorInFullscreen", true); } QtDisplayWidget* MainWindow::createDisplay(QThread* worker_thread, bool fullscreen, bool render_to_main) @@ -145,11 +141,11 @@ QtDisplayWidget* MainWindow::createDisplay(QThread* worker_thread, bool fullscre m_host_display = m_host_interface->createHostDisplay(); if (!m_host_display) { - reportError(tr("Failed to create host display.")); + reportError(tr("Error"), tr("Failed to create host display.")); return nullptr; } - const std::string fullscreen_mode = m_host_interface->GetStringSettingValue("GPU", "FullscreenMode", ""); + const std::string fullscreen_mode = Host::GetStringSettingValue("GPU", "FullscreenMode", ""); const bool is_exclusive_fullscreen = (fullscreen && !fullscreen_mode.empty() && m_host_display->SupportsFullscreen()); QWidget* container; @@ -197,7 +193,7 @@ QtDisplayWidget* MainWindow::createDisplay(QThread* worker_thread, bool fullscre std::optional wi = m_display_widget->getWindowInfo(); if (!wi.has_value()) { - reportError(QStringLiteral("Failed to get window info from widget")); + reportError(tr("Error"), QStringLiteral("Failed to get window info from widget")); destroyDisplayWidget(); m_host_display = nullptr; return nullptr; @@ -206,7 +202,7 @@ QtDisplayWidget* MainWindow::createDisplay(QThread* worker_thread, bool fullscre if (!m_host_display->CreateRenderDevice(wi.value(), g_settings.gpu_adapter, g_settings.gpu_use_debug_device, g_settings.gpu_threaded_presentation)) { - reportError(tr("Failed to create host display device context.")); + reportError(tr("Error"), QStringLiteral("Failed to create host display device context.")); destroyDisplayWidget(); m_host_display = nullptr; return nullptr; @@ -223,7 +219,7 @@ QtDisplayWidget* MainWindow::updateDisplay(QThread* worker_thread, bool fullscre { const bool is_fullscreen = m_display_widget->isFullScreen(); const bool is_rendering_to_main = (!is_fullscreen && m_display_widget->parent()); - const std::string fullscreen_mode = m_host_interface->GetStringSettingValue("GPU", "FullscreenMode", ""); + const std::string fullscreen_mode = Host::GetStringSettingValue("GPU", "FullscreenMode", ""); const bool is_exclusive_fullscreen = (fullscreen && !fullscreen_mode.empty() && m_host_display->SupportsFullscreen()); if (fullscreen == is_fullscreen && is_rendering_to_main == render_to_main) return m_display_widget; @@ -301,7 +297,7 @@ QtDisplayWidget* MainWindow::updateDisplay(QThread* worker_thread, bool fullscre std::optional wi = m_display_widget->getWindowInfo(); if (!wi.has_value()) { - reportError(QStringLiteral("Failed to get new window info from widget")); + reportError(tr("Error"), QStringLiteral("Failed to get new window info from widget")); destroyDisplayWidget(); return nullptr; } @@ -330,13 +326,13 @@ void MainWindow::setDisplayFullscreen(const std::string& fullscreen_mode) result = m_host_display->SetFullscreen(true, width, height, refresh_rate); if (result) { - m_host_interface->AddOSDMessage( - m_host_interface->TranslateStdString("OSDMessage", "Acquired exclusive fullscreen."), 10.0f); + Host::AddOSDMessage( + Host::TranslateStdString("OSDMessage", "Acquired exclusive fullscreen."), 10.0f); } else { - m_host_interface->AddOSDMessage( - m_host_interface->TranslateStdString("OSDMessage", "Failed to acquire exclusive fullscreen."), 10.0f); + Host::AddOSDMessage( + Host::TranslateStdString("OSDMessage", "Failed to acquire exclusive fullscreen."), 10.0f); } } } @@ -431,7 +427,7 @@ void MainWindow::updateMouseMode(bool paused) void MainWindow::onEmulationStarting() { m_emulation_running = true; - updateEmulationActions(true, false, m_host_interface->IsCheevosChallengeModeActive()); + updateEmulationActions(true, false, Cheevos::IsChallengeModeActive()); // ensure it gets updated, since the boot can take a while QGuiApplication::processEvents(QEventLoop::ExcludeUserInputEvents); @@ -439,13 +435,13 @@ void MainWindow::onEmulationStarting() void MainWindow::onEmulationStarted() { - updateEmulationActions(false, true, m_host_interface->IsCheevosChallengeModeActive()); + updateEmulationActions(false, true, Cheevos::IsChallengeModeActive()); } void MainWindow::onEmulationStopped() { m_emulation_running = false; - updateEmulationActions(false, false, m_host_interface->IsCheevosChallengeModeActive()); + updateEmulationActions(false, false, Cheevos::IsChallengeModeActive()); switchToGameListView(); if (m_cheat_manager_dialog) @@ -672,13 +668,13 @@ void MainWindow::onViewToolbarActionToggled(bool checked) void MainWindow::onViewLockToolbarActionToggled(bool checked) { - m_host_interface->SetBoolSettingValue("UI", "LockToolbar", checked); + Host::SetBaseBoolSettingValue("UI", "LockToolbar", checked); m_ui.toolBar->setMovable(!checked); } void MainWindow::onViewStatusBarActionToggled(bool checked) { - m_host_interface->SetBoolSettingValue("UI", "ShowStatusBar", checked); + Host::SetBaseBoolSettingValue("UI", "ShowStatusBar", checked); m_ui.statusBar->setVisible(checked); } @@ -808,7 +804,7 @@ void MainWindow::onGameListContextMenuRequested(const QPoint& point, const GameL m_host_interface->bootSystem(std::move(boot_params)); }); - if (m_ui.menuDebug->menuAction()->isVisible() && !m_host_interface->IsCheevosChallengeModeActive()) + if (m_ui.menuDebug->menuAction()->isVisible() && !Cheevos::IsChallengeModeActive()) { connect(menu.addAction(tr("Boot and Debug")), &QAction::triggered, [this, entry]() { m_open_debugger_on_start = true; @@ -881,11 +877,11 @@ void MainWindow::setupAdditionalUi() { setWindowTitle(getWindowTitle(QString())); - const bool status_bar_visible = m_host_interface->GetBoolSettingValue("UI", "ShowStatusBar", true); + const bool status_bar_visible = Host::GetBaseBoolSettingValue("UI", "ShowStatusBar", true); m_ui.actionViewStatusBar->setChecked(status_bar_visible); m_ui.statusBar->setVisible(status_bar_visible); - const bool toolbars_locked = m_host_interface->GetBoolSettingValue("UI", "LockToolbar", false); + const bool toolbars_locked = Host::GetBaseBoolSettingValue("UI", "LockToolbar", false); m_ui.actionViewLockToolbar->setChecked(toolbars_locked); m_ui.toolBar->setMovable(!toolbars_locked); m_ui.toolBar->setContextMenuPolicy(Qt::PreventContextMenu); @@ -931,7 +927,7 @@ void MainWindow::setupAdditionalUi() qApp->translate("CPUExecutionMode", Settings::GetCPUExecutionModeDisplayName(mode))); action->setCheckable(true); connect(action, &QAction::triggered, [this, mode]() { - m_host_interface->SetStringSettingValue("CPU", "ExecutionMode", Settings::GetCPUExecutionModeName(mode)); + Host::SetBaseBoolSettingValue("CPU", "ExecutionMode", Settings::GetCPUExecutionModeName(mode)); m_host_interface->applySettings(); updateDebugMenuCPUExecutionMode(); }); @@ -945,7 +941,7 @@ void MainWindow::setupAdditionalUi() m_ui.menuRenderer->addAction(qApp->translate("GPURenderer", Settings::GetRendererDisplayName(renderer))); action->setCheckable(true); connect(action, &QAction::triggered, [this, renderer]() { - m_host_interface->SetStringSettingValue("GPU", "Renderer", Settings::GetRendererName(renderer)); + Host::SetBaseStringSettingValue("GPU", "Renderer", Settings::GetRendererName(renderer)); m_host_interface->applySettings(); updateDebugMenuGPURenderer(); }); @@ -959,7 +955,7 @@ void MainWindow::setupAdditionalUi() qApp->translate("DisplayCropMode", Settings::GetDisplayCropModeDisplayName(crop_mode))); action->setCheckable(true); connect(action, &QAction::triggered, [this, crop_mode]() { - m_host_interface->SetStringSettingValue("Display", "CropMode", Settings::GetDisplayCropModeName(crop_mode)); + Host::SetBaseStringSettingValue("Display", "CropMode", Settings::GetDisplayCropModeName(crop_mode)); m_host_interface->applySettings(); updateDebugMenuCropMode(); }); @@ -967,7 +963,7 @@ void MainWindow::setupAdditionalUi() updateDebugMenuCropMode(); const QString current_language( - QString::fromStdString(m_host_interface->GetStringSettingValue("Main", "Language", ""))); + QString::fromStdString(Host::GetBaseStringSettingValue("Main", "Language", ""))); QActionGroup* language_group = new QActionGroup(m_ui.menuSettingsLanguage); for (const std::pair& it : m_host_interface->getAvailableLanguageList()) { @@ -989,7 +985,7 @@ void MainWindow::setupAdditionalUi() action->setData(it.second); connect(action, &QAction::triggered, [this, action]() { const QString new_language = action->data().toString(); - m_host_interface->SetStringSettingValue("Main", "Language", new_language.toUtf8().constData()); + Host::SetBaseStringSettingValue("Main", "Language", new_language.toUtf8().constData()); m_host_interface->reinstallTranslator(); recreate(); }); @@ -1152,7 +1148,7 @@ void MainWindow::startGameOrChangeDiscs(const std::string& path) // if we're not running, boot the system, otherwise swap discs if (!m_emulation_running) { - if (m_host_interface->CanResumeSystemFromFile(path.c_str())) + if (System::CanResumeSystemFromFile(path.c_str())) m_host_interface->resumeSystemFromState(QString::fromStdString(path), true); else m_host_interface->bootSystem(std::make_shared(path)); @@ -1167,7 +1163,7 @@ void MainWindow::startGameOrChangeDiscs(const std::string& path) void MainWindow::connectSignals() { - updateEmulationActions(false, false, m_host_interface->IsCheevosChallengeModeActive()); + updateEmulationActions(false, false, Cheevos::IsChallengeModeActive()); onEmulationPaused(false); connect(qApp, &QGuiApplication::applicationStateChanged, this, &MainWindow::onApplicationStateChanged); @@ -1267,7 +1263,6 @@ void MainWindow::connectSignals() connect(m_host_interface, &QtHostInterface::settingsResetToDefault, this, &MainWindow::onSettingsResetToDefault); connect(m_host_interface, &QtHostInterface::errorReported, this, &MainWindow::reportError, Qt::BlockingQueuedConnection); - connect(m_host_interface, &QtHostInterface::messageReported, this, &MainWindow::reportMessage); connect(m_host_interface, &QtHostInterface::messageConfirmed, this, &MainWindow::confirmMessage, Qt::BlockingQueuedConnection); connect(m_host_interface, &QtHostInterface::createDisplayRequested, this, &MainWindow::createDisplay, @@ -1362,7 +1357,7 @@ void MainWindow::addThemeToMenu(const QString& name, const QString& key) void MainWindow::setTheme(const QString& theme) { - m_host_interface->SetStringSettingValue("UI", "Theme", theme.toUtf8().constData()); + Host::SetBaseStringSettingValue("UI", "Theme", theme.toUtf8().constData()); setStyleFromSettings(); setIconThemeFromSettings(); updateMenuSelectedTheme(); @@ -1371,7 +1366,7 @@ void MainWindow::setTheme(const QString& theme) void MainWindow::setStyleFromSettings() { - const std::string theme(m_host_interface->GetStringSettingValue("UI", "Theme", DEFAULT_THEME_NAME)); + const std::string theme(Host::GetBaseStringSettingValue("UI", "Theme", DEFAULT_THEME_NAME)); if (theme == "qdarkstyle") { @@ -1469,7 +1464,7 @@ void MainWindow::setStyleFromSettings() void MainWindow::setIconThemeFromSettings() { - const std::string theme(m_host_interface->GetStringSettingValue("UI", "Theme", DEFAULT_THEME_NAME)); + const std::string theme(Host::GetBaseStringSettingValue("UI", "Theme", DEFAULT_THEME_NAME)); QString icon_theme; if (theme == "qdarkstyle" || theme == "darkfusion" || theme == "darkfusionblue") @@ -1508,31 +1503,31 @@ void MainWindow::saveStateToConfig() { const QByteArray geometry = saveGeometry(); const QByteArray geometry_b64 = geometry.toBase64(); - const std::string old_geometry_b64 = m_host_interface->GetStringSettingValue("UI", "MainWindowGeometry"); + const std::string old_geometry_b64 = Host::GetBaseStringSettingValue("UI", "MainWindowGeometry"); if (old_geometry_b64 != geometry_b64.constData()) - m_host_interface->SetStringSettingValue("UI", "MainWindowGeometry", geometry_b64.constData()); + Host::SetBaseStringSettingValue("UI", "MainWindowGeometry", geometry_b64.constData()); } { const QByteArray state = saveState(); const QByteArray state_b64 = state.toBase64(); - const std::string old_state_b64 = m_host_interface->GetStringSettingValue("UI", "MainWindowState"); + const std::string old_state_b64 = Host::GetBaseStringSettingValue("UI", "MainWindowState"); if (old_state_b64 != state_b64.constData()) - m_host_interface->SetStringSettingValue("UI", "MainWindowState", state_b64.constData()); + Host::SetBaseStringSettingValue("UI", "MainWindowState", state_b64.constData()); } } void MainWindow::restoreStateFromConfig() { { - const std::string geometry_b64 = m_host_interface->GetStringSettingValue("UI", "MainWindowGeometry"); + const std::string geometry_b64 = Host::GetBaseStringSettingValue("UI", "MainWindowGeometry"); const QByteArray geometry = QByteArray::fromBase64(QByteArray::fromStdString(geometry_b64)); if (!geometry.isEmpty()) restoreGeometry(geometry); } { - const std::string state_b64 = m_host_interface->GetStringSettingValue("UI", "MainWindowState"); + const std::string state_b64 = Host::GetBaseStringSettingValue("UI", "MainWindowState"); const QByteArray state = QByteArray::fromBase64(QByteArray::fromStdString(state_b64)); if (!state.isEmpty()) restoreState(state); @@ -1552,14 +1547,14 @@ void MainWindow::saveDisplayWindowGeometryToConfig() { const QByteArray geometry = getDisplayContainer()->saveGeometry(); const QByteArray geometry_b64 = geometry.toBase64(); - const std::string old_geometry_b64 = m_host_interface->GetStringSettingValue("UI", "DisplayWindowGeometry"); + const std::string old_geometry_b64 = Host::GetBaseStringSettingValue("UI", "DisplayWindowGeometry"); if (old_geometry_b64 != geometry_b64.constData()) - m_host_interface->SetStringSettingValue("UI", "DisplayWindowGeometry", geometry_b64.constData()); + Host::SetBaseStringSettingValue("UI", "DisplayWindowGeometry", geometry_b64.constData()); } void MainWindow::restoreDisplayWindowGeometryFromConfig() { - const std::string geometry_b64 = m_host_interface->GetStringSettingValue("UI", "DisplayWindowGeometry"); + const std::string geometry_b64 = Host::GetBaseStringSettingValue("UI", "DisplayWindowGeometry"); const QByteArray geometry = QByteArray::fromBase64(QByteArray::fromStdString(geometry_b64)); QWidget* container = getDisplayContainer(); if (!geometry.isEmpty()) @@ -1614,7 +1609,7 @@ void MainWindow::doControllerSettings( void MainWindow::updateDebugMenuCPUExecutionMode() { std::optional current_mode = - Settings::ParseCPUExecutionMode(m_host_interface->GetStringSettingValue("CPU", "ExecutionMode").c_str()); + Settings::ParseCPUExecutionMode(Host::GetBaseStringSettingValue("CPU", "ExecutionMode").c_str()); if (!current_mode.has_value()) return; @@ -1632,7 +1627,7 @@ void MainWindow::updateDebugMenuGPURenderer() { // update the menu with the new selected renderer std::optional current_renderer = - Settings::ParseRendererName(m_host_interface->GetStringSettingValue("GPU", "Renderer").c_str()); + Settings::ParseRendererName(Host::GetBaseStringSettingValue("GPU", "Renderer").c_str()); if (!current_renderer.has_value()) return; @@ -1649,7 +1644,7 @@ void MainWindow::updateDebugMenuGPURenderer() void MainWindow::updateDebugMenuCropMode() { std::optional current_crop_mode = - Settings::ParseDisplayCropMode(m_host_interface->GetStringSettingValue("Display", "CropMode").c_str()); + Settings::ParseDisplayCropMode(Host::GetBaseStringSettingValue("Display", "CropMode").c_str()); if (!current_crop_mode.has_value()) return; @@ -1665,7 +1660,7 @@ void MainWindow::updateDebugMenuCropMode() void MainWindow::updateMenuSelectedTheme() { - QString theme = QString::fromStdString(m_host_interface->GetStringSettingValue("UI", "Theme", DEFAULT_THEME_NAME)); + QString theme = QString::fromStdString(Host::GetBaseStringSettingValue("UI", "Theme", DEFAULT_THEME_NAME)); for (QObject* obj : m_ui.menuSettingsTheme->children()) { @@ -1751,7 +1746,7 @@ void MainWindow::dropEvent(QDropEvent* event) void MainWindow::startupUpdateCheck() { - if (!m_host_interface->GetBoolSettingValue("AutoUpdater", "CheckAtStartup", true)) + if (!Host::GetBaseBoolSettingValue("AutoUpdater", "CheckAtStartup", true)) return; checkForUpdates(false); @@ -1759,14 +1754,14 @@ void MainWindow::startupUpdateCheck() void MainWindow::updateDebugMenuVisibility() { - const bool visible = m_host_interface->GetBoolSettingValue("Main", "ShowDebugMenu", false); + const bool visible = Host::GetBaseBoolSettingValue("Main", "ShowDebugMenu", false); m_ui.menuDebug->menuAction()->setVisible(visible); } void MainWindow::onCheckForUpdatesActionTriggered() { // Wipe out the last version, that way it displays the update if we've previously skipped it. - m_host_interface->RemoveSettingValue("AutoUpdater", "LastVersion"); + Host::DeleteBaseSettingValue("AutoUpdater", "LastVersion"); checkForUpdates(true); } @@ -1848,7 +1843,7 @@ void MainWindow::onToolsCheatManagerTriggered() { if (!m_cheat_manager_dialog) { - if (m_host_interface->GetBoolSettingValue("UI", "DisplayCheatWarning", true)) + if (Host::GetBaseBoolSettingValue("UI", "DisplayCheatWarning", true)) { QCheckBox* cb = new QCheckBox(tr("Do not show again")); QMessageBox mb(this); @@ -1866,8 +1861,7 @@ void MainWindow::onToolsCheatManagerTriggered() mb.setCheckBox(cb); connect(cb, &QCheckBox::stateChanged, [](int state) { - QtHostInterface::GetInstance()->SetBoolSettingValue("UI", "DisplayCheatWarning", - (state != Qt::CheckState::Checked)); + Host::SetBaseBoolSettingValue("UI", "DisplayCheatWarning", (state != Qt::CheckState::Checked)); }); if (mb.exec() == QMessageBox::No) diff --git a/src/duckstation-qt/mainwindow.h b/src/duckstation-qt/mainwindow.h index 343c7883b..4ed7ce734 100644 --- a/src/duckstation-qt/mainwindow.h +++ b/src/duckstation-qt/mainwindow.h @@ -53,9 +53,8 @@ public Q_SLOTS: void checkForUpdates(bool display_message); private Q_SLOTS: - void reportError(const QString& message); - void reportMessage(const QString& message); - bool confirmMessage(const QString& message); + void reportError(const QString& title, const QString& message); + bool confirmMessage(const QString& title, const QString& message); QtDisplayWidget* createDisplay(QThread* worker_thread, bool fullscreen, bool render_to_main); QtDisplayWidget* updateDisplay(QThread* worker_thread, bool fullscreen, bool render_to_main); void displaySizeRequested(qint32 width, qint32 height); diff --git a/src/duckstation-qt/memorycardeditordialog.cpp b/src/duckstation-qt/memorycardeditordialog.cpp index a21aa82a6..c24ad87f2 100644 --- a/src/duckstation-qt/memorycardeditordialog.cpp +++ b/src/duckstation-qt/memorycardeditordialog.cpp @@ -1,6 +1,7 @@ #include "memorycardeditordialog.h" #include "common/file_system.h" #include "common/string_util.h" +#include "core/host.h" #include "core/host_interface.h" #include "qtutils.h" #include diff --git a/src/duckstation-qt/qthostinterface.cpp b/src/duckstation-qt/qthostinterface.cpp index 5c647c6d5..7b542bfaf 100644 --- a/src/duckstation-qt/qthostinterface.cpp +++ b/src/duckstation-qt/qthostinterface.cpp @@ -11,11 +11,11 @@ #include "core/host.h" #include "core/host_settings.h" #include "core/memory_card.h" +#include "core/spu.h" #include "core/system.h" #include "frontend-common/fullscreen_ui.h" #include "frontend-common/game_list.h" #include "frontend-common/imgui_manager.h" -#include "frontend-common/ini_settings_interface.h" #include "frontend-common/input_manager.h" #include "frontend-common/opengl_host_display.h" #include "frontend-common/sdl_audio_stream.h" @@ -26,6 +26,7 @@ #include "qtprogresscallback.h" #include "qtutils.h" #include "util/audio_stream.h" +#include "util/ini_settings_interface.h" #include #include #include @@ -68,16 +69,22 @@ static void SaveSettings(); static std::unique_ptr s_base_settings_interface; static std::unique_ptr s_settings_save_timer; +static std::unique_ptr s_host_display; + +QtHostInterface* g_emu_thread; + QtHostInterface::QtHostInterface(QObject* parent) : QObject(parent), CommonHostInterface() { qRegisterMetaType>(); qRegisterMetaType(); qRegisterMetaType(); + g_emu_thread = this; } QtHostInterface::~QtHostInterface() { - Assert(!m_display); + Assert(!s_host_display); + g_emu_thread = nullptr; } const char* QtHostInterface::GetFrontendName() const @@ -130,17 +137,21 @@ bool QtHostInterface::initializeOnThread() const bool loaded = s_base_settings_interface->Load(); if (!loaded) - SetDefaultSettings(*s_base_settings_interface.get()); + { + Panic("fixme"); + // SetDefaultSettings(*s_base_settings_interface.get()); + } const int settings_version = s_base_settings_interface->GetIntValue("Main", "SettingsVersion", -1); if (settings_version != SETTINGS_VERSION) { - ReportFormattedError("Settings version %d does not match expected version %d, resetting", settings_version, - SETTINGS_VERSION); + Host::ReportFormattedErrorAsync("Error", "Settings version %d does not match expected version %d, resetting", + settings_version, SETTINGS_VERSION); s_base_settings_interface->Clear(); s_base_settings_interface->SetIntValue("Main", "SettingsVersion", SETTINGS_VERSION); - SetDefaultSettings(*s_base_settings_interface); + // SetDefaultSettings(*s_base_settings_interface); + Panic("Fixme"); s_base_settings_interface->Save(); } @@ -164,7 +175,7 @@ void QtHostInterface::shutdownOnThread() void QtHostInterface::installTranslator() { - const QString language(QString::fromStdString(GetStringSettingValue("Main", "Language", "en"))); + const QString language(QString::fromStdString(Host::GetBaseStringSettingValue("Main", "Language", "en"))); // install the base qt translation first const QString base_dir(QStringLiteral("%1/translations").arg(qApp->applicationDirPath())); @@ -222,99 +233,6 @@ void QtHostInterface::reinstallTranslator() installTranslator(); } -void QtHostInterface::ReportError(const char* message) -{ - HostInterface::ReportError(message); - - const bool was_fullscreen = m_is_fullscreen; - if (was_fullscreen) - SetFullscreen(false); - - emit errorReported(QString::fromUtf8(message)); - - if (was_fullscreen) - SetFullscreen(true); -} - -void QtHostInterface::ReportMessage(const char* message) -{ - HostInterface::ReportMessage(message); - - emit messageReported(QString::fromUtf8(message)); -} - -void QtHostInterface::ReportDebuggerMessage(const char* message) -{ - HostInterface::ReportDebuggerMessage(message); - - emit debuggerMessageReported(QString::fromUtf8(message)); -} - -bool QtHostInterface::ConfirmMessage(const char* message) -{ - const bool was_fullscreen = m_is_fullscreen; - if (was_fullscreen) - SetFullscreen(false); - - const bool result = messageConfirmed(QString::fromUtf8(message)); - - if (was_fullscreen) - SetFullscreen(true); - - return result; -} - -void QtHostInterface::SetBoolSettingValue(const char* section, const char* key, bool value) -{ - QtHost::SetBaseBoolSettingValue(section, key, value); -} - -void QtHostInterface::SetIntSettingValue(const char* section, const char* key, int value) -{ - QtHost::SetBaseIntSettingValue(section, key, value); -} - -void QtHostInterface::SetFloatSettingValue(const char* section, const char* key, float value) -{ - QtHost::SetBaseFloatSettingValue(section, key, value); -} - -void QtHostInterface::SetStringSettingValue(const char* section, const char* key, const char* value) -{ - QtHost::SetBaseStringSettingValue(section, key, value); -} - -void QtHostInterface::SetStringListSettingValue(const char* section, const char* key, - const std::vector& values) -{ - QtHost::SetBaseStringListSettingValue(section, key, values); -} - -bool QtHostInterface::AddValueToStringList(const char* section, const char* key, const char* value) -{ - auto lock = Host::GetSettingsLock(); - if (!Host::Internal::GetBaseSettingsLayer()->AddToStringList(section, key, value)) - return false; - - queueSettingsSave(); - return true; -} - -bool QtHostInterface::RemoveValueFromStringList(const char* section, const char* key, const char* value) -{ - auto lock = Host::GetSettingsLock(); - if (!Host::Internal::GetBaseSettingsLayer()->RemoveFromStringList(section, key, value)) - return false; - - queueSettingsSave(); - return true; -} - -void QtHostInterface::RemoveSettingValue(const char* section, const char* key) -{ - QtHost::RemoveBaseSettingValue(section, key); -} - void QtHostInterface::queueSettingsSave() { QtHost::QueueSettingsSave(); @@ -328,9 +246,10 @@ void QtHostInterface::setDefaultSettings() return; } - SetDefaultSettings(); + Panic("Fixme"); } +#if 0 void QtHostInterface::SetDefaultSettings() { CommonHostInterface::SetDefaultSettings(); @@ -338,6 +257,7 @@ void QtHostInterface::SetDefaultSettings() queueSettingsSave(); emit settingsResetToDefault(); } +#endif void QtHostInterface::applySettings(bool display_osd_messages /* = false */) { @@ -347,7 +267,8 @@ void QtHostInterface::applySettings(bool display_osd_messages /* = false */) return; } - ApplySettings(display_osd_messages); + checkRenderToMainState(); + System::ApplySettings(display_osd_messages); } void QtHostInterface::reloadGameSettings() @@ -355,24 +276,13 @@ void QtHostInterface::reloadGameSettings() Panic("IMPLEMENT ME"); } -void QtHostInterface::reloadInputBindings() -{ - Panic("IMPLEMENT ME"); -} - -void QtHostInterface::ApplySettings(bool display_osd_messages) -{ - CommonHostInterface::ApplySettings(display_osd_messages); - checkRenderToMainState(); -} - void QtHostInterface::checkRenderToMainState() { // detect when render-to-main flag changes if (!System::IsShutdown()) { const bool render_to_main = Host::GetBaseBoolSettingValue("Main", "RenderToMainWindow", true); - if (m_display && !m_is_fullscreen && render_to_main != m_is_rendering_to_main) + if (s_host_display && !m_is_fullscreen && render_to_main != m_is_rendering_to_main) { m_is_rendering_to_main = render_to_main; updateDisplayState(); @@ -412,7 +322,7 @@ void QtHostInterface::bootSystem(std::shared_ptr params) } emit emulationStarting(); - if (!BootSystem(std::move(params))) + if (!System::BootSystem(std::move(params))) return; // force a frame to be drawn to repaint the window @@ -430,17 +340,17 @@ void QtHostInterface::resumeSystemFromState(const QString& filename, bool boot_o emit emulationStarting(); if (filename.isEmpty()) - ResumeSystemFromMostRecentState(); + System::ResumeSystemFromMostRecentState(); else - ResumeSystemFromState(filename.toStdString().c_str(), boot_on_failure); + System::ResumeSystemFromState(filename.toStdString().c_str(), boot_on_failure); } void QtHostInterface::resumeSystemFromMostRecentState() { - std::string state_filename = GetMostRecentResumeSaveStatePath(); + std::string state_filename = System::GetMostRecentResumeSaveStatePath(); if (state_filename.empty()) { - emit errorReported(tr("No resume save state found.")); + emit errorReported(tr("Error"), tr("No resume save state found.")); return; } @@ -451,15 +361,16 @@ void QtHostInterface::onDisplayWindowKeyEvent(int key, bool pressed) { DebugAssert(isOnWorkerThread()); - InputManager::InvokeEvents(InputManager::MakeHostKeyboardKey(key), static_cast(pressed), GenericInputBinding::Unknown); + InputManager::InvokeEvents(InputManager::MakeHostKeyboardKey(key), static_cast(pressed), + GenericInputBinding::Unknown); } void QtHostInterface::onDisplayWindowMouseMoveEvent(float x, float y) { // display might be null here if the event happened after shutdown DebugAssert(isOnWorkerThread()); - if (m_display) - m_display->SetMousePosition(static_cast(x), static_cast(y)); + if (s_host_display) + s_host_display->SetMousePosition(static_cast(x), static_cast(y)); InputManager::UpdatePointerAbsolutePosition(0, x, y); } @@ -468,7 +379,8 @@ void QtHostInterface::onDisplayWindowMouseButtonEvent(int button, bool pressed) { DebugAssert(isOnWorkerThread()); - InputManager::InvokeEvents(InputManager::MakePointerButtonKey(0, button), static_cast(pressed), GenericInputBinding::Unknown); + InputManager::InvokeEvents(InputManager::MakePointerButtonKey(0, button), static_cast(pressed), + GenericInputBinding::Unknown); } void QtHostInterface::onDisplayWindowMouseWheelEvent(const QPoint& delta_angle) @@ -487,20 +399,20 @@ void QtHostInterface::onDisplayWindowMouseWheelEvent(const QPoint& delta_angle) void QtHostInterface::onDisplayWindowResized(int width, int height) { // this can be null if it was destroyed and the main thread is late catching up - if (!m_display) + if (!s_host_display) return; Log_DevPrintf("Display window resized to %dx%d", width, height); - m_display->ResizeRenderWindow(width, height); + s_host_display->ResizeRenderWindow(width, height); OnHostDisplayResized(); // re-render the display, since otherwise it will be out of date and stretched if paused if (!System::IsShutdown()) { - if (m_is_exclusive_fullscreen && !m_display->IsFullscreen()) + if (m_is_exclusive_fullscreen && !s_host_display->IsFullscreen()) { // we lost exclusive fullscreen, switch to borderless - AddOSDMessage(TranslateStdString("OSDMessage", "Lost exclusive fullscreen."), 10.0f); + Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "Lost exclusive fullscreen."), 10.0f); m_is_exclusive_fullscreen = false; m_is_fullscreen = false; m_lost_exclusive_fullscreen = true; @@ -514,7 +426,7 @@ void QtHostInterface::onDisplayWindowResized(int width, int height) void QtHostInterface::onDisplayWindowFocused() { - if (!m_display || !m_lost_exclusive_fullscreen) + if (!s_host_display || !m_lost_exclusive_fullscreen) return; // try to restore exclusive fullscreen @@ -532,7 +444,7 @@ void QtHostInterface::redrawDisplayWindow() return; } - if (!m_display || System::IsShutdown()) + if (!s_host_display || System::IsShutdown()) return; renderDisplay(); @@ -549,35 +461,35 @@ void QtHostInterface::toggleFullscreen() SetFullscreen(!m_is_fullscreen); } -bool QtHostInterface::AcquireHostDisplay() +HostDisplay* QtHostInterface::acquireHostDisplay() { - Assert(!m_display); + Assert(!s_host_display); m_is_rendering_to_main = Host::GetBaseBoolSettingValue("Main", "RenderToMainWindow", true); QtDisplayWidget* display_widget = createDisplayRequested(m_worker_thread, m_is_fullscreen, m_is_rendering_to_main); - if (!display_widget || !m_display->HasRenderDevice()) + if (!display_widget || !s_host_display->HasRenderDevice()) { emit destroyDisplayRequested(); - m_display.reset(); - return false; + s_host_display.reset(); + return nullptr; } - if (!m_display->MakeRenderContextCurrent() || - !m_display->InitializeRenderDevice(GetShaderCacheBasePath(), g_settings.gpu_use_debug_device, - g_settings.gpu_threaded_presentation) || + if (!s_host_display->MakeRenderContextCurrent() || + !s_host_display->InitializeRenderDevice(GetShaderCacheBasePath(), g_settings.gpu_use_debug_device, + g_settings.gpu_threaded_presentation) || !ImGuiManager::Initialize() || !CreateHostDisplayResources()) { ImGuiManager::Shutdown(); ReleaseHostDisplayResources(); - m_display->DestroyRenderDevice(); + s_host_display->DestroyRenderDevice(); emit destroyDisplayRequested(); - m_display.reset(); - return false; + s_host_display.reset(); + return nullptr; } - m_is_exclusive_fullscreen = m_display->IsFullscreen(); - return true; + m_is_exclusive_fullscreen = s_host_display->IsFullscreen(); + return s_host_display.get(); } HostDisplay* QtHostInterface::createHostDisplay() @@ -585,29 +497,29 @@ HostDisplay* QtHostInterface::createHostDisplay() switch (g_settings.gpu_renderer) { case GPURenderer::HardwareVulkan: - m_display = std::make_unique(); + s_host_display = std::make_unique(); break; case GPURenderer::HardwareOpenGL: #ifndef _WIN32 default: #endif - m_display = std::make_unique(); + s_host_display = std::make_unique(); break; #ifdef _WIN32 case GPURenderer::HardwareD3D12: - m_display = std::make_unique(); + s_host_display = std::make_unique(); break; case GPURenderer::HardwareD3D11: default: - m_display = std::make_unique(); + s_host_display = std::make_unique(); break; #endif } - return m_display.get(); + return s_host_display.get(); } void QtHostInterface::connectDisplaySignals(QtDisplayWidget* widget) @@ -627,18 +539,18 @@ void QtHostInterface::connectDisplaySignals(QtDisplayWidget* widget) void QtHostInterface::updateDisplayState() { - if (!m_display) + if (!s_host_display) return; // this expects the context to get moved back to us afterwards - m_display->DoneRenderContextCurrent(); + s_host_display->DoneRenderContextCurrent(); QtDisplayWidget* display_widget = updateDisplayRequested(m_worker_thread, m_is_fullscreen, m_is_rendering_to_main && !m_is_fullscreen); - if (!display_widget || !m_display->MakeRenderContextCurrent()) + if (!display_widget || !s_host_display->MakeRenderContextCurrent()) Panic("Failed to make device context current after updating"); - m_is_exclusive_fullscreen = m_display->IsFullscreen(); + m_is_exclusive_fullscreen = s_host_display->IsFullscreen(); OnHostDisplayResized(); @@ -650,18 +562,18 @@ void QtHostInterface::updateDisplayState() redrawDisplayWindow(); } - UpdateSpeedLimiterState(); + System::UpdateSpeedLimiterState(); } -void QtHostInterface::ReleaseHostDisplay() +void QtHostInterface::releaseHostDisplay() { - Assert(m_display); + Assert(s_host_display); ReleaseHostDisplayResources(); ImGuiManager::Shutdown(); - m_display->DestroyRenderDevice(); + s_host_display->DestroyRenderDevice(); emit destroyDisplayRequested(); - m_display.reset(); + s_host_display.reset(); m_is_fullscreen = false; } @@ -699,10 +611,8 @@ void QtHostInterface::RequestExit() emit exitRequested(); } -void QtHostInterface::OnSystemCreated() +void QtHostInterface::onSystemStarted() { - CommonHostInterface::OnSystemCreated(); - wakeThread(); stopBackgroundControllerPollTimer(); @@ -710,82 +620,38 @@ void QtHostInterface::OnSystemCreated() emit emulationPaused(false); } -void QtHostInterface::OnSystemPaused(bool paused) +void QtHostInterface::onSystemPaused() { - CommonHostInterface::OnSystemPaused(paused); - - emit emulationPaused(paused); - - if (!paused) - { - wakeThread(); - stopBackgroundControllerPollTimer(); - emit focusDisplayWidgetRequested(); - } - else - { - startBackgroundControllerPollTimer(); - renderDisplay(); - } + emit emulationPaused(true); + startBackgroundControllerPollTimer(); + renderDisplay(); } -void QtHostInterface::OnSystemDestroyed() +void QtHostInterface::onSystemResumed() { - CommonHostInterface::OnSystemDestroyed(); + emit emulationPaused(false); - Host::ClearOSDMessages(); + wakeThread(); + stopBackgroundControllerPollTimer(); + emit focusDisplayWidgetRequested(); +} + +void QtHostInterface::onSystemDestroyed() +{ startBackgroundControllerPollTimer(); emit emulationStopped(); } -void QtHostInterface::OnSystemPerformanceCountersUpdated() -{ - GPURenderer renderer = GPURenderer::Count; - u32 render_width = 0; - u32 render_height = 0; - bool render_interlaced = false; - - if (g_gpu) - { - renderer = g_gpu->GetRendererType(); - std::tie(render_width, render_height) = g_gpu->GetEffectiveDisplayResolution(); - render_interlaced = g_gpu->IsInterlacedDisplayEnabled(); - } - - emit systemPerformanceCountersUpdated(System::GetEmulationSpeed(), System::GetFPS(), System::GetVPS(), - System::GetAverageFrameTime(), System::GetWorstFrameTime(), renderer, - render_width, render_height, render_interlaced); -} - -void QtHostInterface::OnRunningGameChanged(const std::string& path, CDImage* image, const std::string& game_code, - const std::string& game_title) -{ - CommonHostInterface::OnRunningGameChanged(path, image, game_code, game_title); - - if (!System::IsShutdown()) - { - emit runningGameChanged(QString::fromStdString(System::GetRunningPath()), - QString::fromStdString(System::GetRunningCode()), - QString::fromStdString(System::GetRunningTitle())); - } - else - { - emit runningGameChanged(QString(), QString(), QString()); - } -} - +#if 0 void QtHostInterface::SetDefaultSettings(SettingsInterface& si) { CommonHostInterface::SetDefaultSettings(si); - si.SetStringValue("Hotkeys", "PowerOff", "Keyboard/Escape"); - si.SetStringValue("Hotkeys", "LoadSelectedSaveState", "Keyboard/F1"); - si.SetStringValue("Hotkeys", "SaveSelectedSaveState", "Keyboard/F2"); - si.SetStringValue("Hotkeys", "SelectPreviousSaveStateSlot", "Keyboard/F3"); - si.SetStringValue("Hotkeys", "SelectNextSaveStateSlot", "Keyboard/F4"); + si.SetBoolValue("Main", "RenderToMainWindow", true); } +#endif void QtHostInterface::SetMouseMode(bool relative, bool hide_cursor) { @@ -805,6 +671,69 @@ void QtHostInterface::applyInputProfile(const QString& profile_path) emit inputProfileLoaded(); } +void QtHostInterface::reloadInputSources() +{ + if (!isOnWorkerThread()) + { + QMetaObject::invokeMethod(this, &QtHostInterface::reloadInputSources, Qt::QueuedConnection); + return; + } + + std::unique_lock lock = Host::GetSettingsLock(); + SettingsInterface* si = Host::GetSettingsInterface(); + SettingsInterface* bindings_si = Host::GetSettingsInterfaceForBindings(); + InputManager::ReloadSources(*si, lock); + InputManager::ReloadBindings(*si, *bindings_si); +} + +void QtHostInterface::reloadInputBindings() +{ + if (!isOnWorkerThread()) + { + QMetaObject::invokeMethod(this, &QtHostInterface::reloadInputBindings, Qt::QueuedConnection); + return; + } + + auto lock = Host::GetSettingsLock(); + SettingsInterface* si = Host::GetSettingsInterface(); + SettingsInterface* bindings_si = Host::GetSettingsInterfaceForBindings(); + InputManager::ReloadBindings(*si, *bindings_si); +} + +void QtHostInterface::enumerateInputDevices() +{ + if (!isOnWorkerThread()) + { + QMetaObject::invokeMethod(this, &QtHostInterface::enumerateInputDevices, Qt::QueuedConnection); + return; + } + + const std::vector> devs(InputManager::EnumerateDevices()); + QList> qdevs; + qdevs.reserve(devs.size()); + for (const std::pair& dev : devs) + qdevs.emplace_back(QString::fromStdString(dev.first), QString::fromStdString(dev.second)); + + onInputDevicesEnumerated(qdevs); +} + +void QtHostInterface::enumerateVibrationMotors() +{ + if (!isOnWorkerThread()) + { + QMetaObject::invokeMethod(this, &QtHostInterface::enumerateVibrationMotors, Qt::QueuedConnection); + return; + } + + const std::vector motors(InputManager::EnumerateMotors()); + QList qmotors; + qmotors.reserve(motors.size()); + for (InputBindingKey key : motors) + qmotors.push_back(key); + + onVibrationMotorsEnumerated(qmotors); +} + void QtHostInterface::saveInputProfile(const QString& profile_name) { // std::lock_guard lock(m_settings_mutex); @@ -842,7 +771,7 @@ void QtHostInterface::powerOffSystem() return; } - PowerOffSystem(ShouldSaveResumeState()); + System::PowerOffSystem(System::ShouldSaveResumeState()); } void QtHostInterface::powerOffSystemWithoutSaving() @@ -854,7 +783,7 @@ void QtHostInterface::powerOffSystemWithoutSaving() return; } - PowerOffSystem(false); + System::PowerOffSystem(false); } void QtHostInterface::synchronousPowerOffSystem() @@ -884,7 +813,7 @@ void QtHostInterface::resetSystem() return; } - HostInterface::ResetSystem(); + System::ResetSystem(); } void QtHostInterface::pauseSystem(bool paused, bool wait_until_paused /* = false */) @@ -897,7 +826,7 @@ void QtHostInterface::pauseSystem(bool paused, bool wait_until_paused /* = false return; } - CommonHostInterface::PauseSystem(paused); + System::PauseSystem(paused); } void QtHostInterface::changeDisc(const QString& new_disc_filename) @@ -929,7 +858,7 @@ void QtHostInterface::changeDiscFromPlaylist(quint32 index) return; if (!System::SwitchMediaSubImage(index)) - ReportFormattedError("Failed to switch to subimage %u", index); + Host::ReportFormattedErrorAsync("Error", "Failed to switch to subimage %u", index); } static QString FormatTimestampForSaveStateMenu(u64 timestamp) @@ -941,7 +870,7 @@ static QString FormatTimestampForSaveStateMenu(u64 timestamp) void QtHostInterface::populateLoadStateMenu(const char* game_code, QMenu* menu) { auto add_slot = [this, game_code, menu](const QString& title, const QString& empty_title, bool global, s32 slot) { - std::optional ssi = GetSaveStateInfo(global ? nullptr : game_code, slot); + std::optional ssi = System::GetSaveStateInfo(global ? nullptr : game_code, slot); const QString menu_title = ssi.has_value() ? title.arg(slot).arg(FormatTimestampForSaveStateMenu(ssi->timestamp)) : empty_title.arg(slot); @@ -966,26 +895,26 @@ void QtHostInterface::populateLoadStateMenu(const char* game_code, QMenu* menu) loadState(path); }); QAction* load_from_state = menu->addAction(tr("Undo Load State")); - load_from_state->setEnabled(CanUndoLoadState()); + load_from_state->setEnabled(System::CanUndoLoadState()); connect(load_from_state, &QAction::triggered, this, &QtHostInterface::undoLoadState); menu->addSeparator(); if (game_code && std::strlen(game_code) > 0) { - for (u32 slot = 1; slot <= PER_GAME_SAVE_STATE_SLOTS; slot++) + for (u32 slot = 1; slot <= System::PER_GAME_SAVE_STATE_SLOTS; slot++) add_slot(tr("Game Save %1 (%2)"), tr("Game Save %1 (Empty)"), false, static_cast(slot)); menu->addSeparator(); } - for (u32 slot = 1; slot <= GLOBAL_SAVE_STATE_SLOTS; slot++) + for (u32 slot = 1; slot <= System::GLOBAL_SAVE_STATE_SLOTS; slot++) add_slot(tr("Global Save %1 (%2)"), tr("Global Save %1 (Empty)"), true, static_cast(slot)); } void QtHostInterface::populateSaveStateMenu(const char* game_code, QMenu* menu) { auto add_slot = [this, game_code, menu](const QString& title, const QString& empty_title, bool global, s32 slot) { - std::optional ssi = GetSaveStateInfo(global ? nullptr : game_code, slot); + std::optional ssi = System::GetSaveStateInfo(global ? nullptr : game_code, slot); const QString menu_title = ssi.has_value() ? title.arg(slot).arg(FormatTimestampForSaveStateMenu(ssi->timestamp)) : empty_title.arg(slot); @@ -1011,13 +940,13 @@ void QtHostInterface::populateSaveStateMenu(const char* game_code, QMenu* menu) if (game_code && std::strlen(game_code) > 0) { - for (u32 slot = 1; slot <= PER_GAME_SAVE_STATE_SLOTS; slot++) + for (u32 slot = 1; slot <= System::PER_GAME_SAVE_STATE_SLOTS; slot++) add_slot(tr("Game Save %1 (%2)"), tr("Game Save %1 (Empty)"), false, static_cast(slot)); menu->addSeparator(); } - for (u32 slot = 1; slot <= GLOBAL_SAVE_STATE_SLOTS; slot++) + for (u32 slot = 1; slot <= System::GLOBAL_SAVE_STATE_SLOTS; slot++) add_slot(tr("Global Save %1 (%2)"), tr("Global Save %1 (Empty)"), true, static_cast(slot)); } @@ -1031,9 +960,9 @@ void QtHostInterface::populateGameListContextMenu(const GameListEntry* entry, QW if (!entry->code.empty()) { - const std::vector available_states(GetAvailableSaveStates(entry->code.c_str())); + const std::vector available_states(System::GetAvailableSaveStates(entry->code.c_str())); const QString timestamp_format = QLocale::system().dateTimeFormat(QLocale::ShortFormat); - const bool challenge_mode = IsCheevosChallengeModeActive(); + const bool challenge_mode = Cheevos::IsChallengeModeActive(); for (const SaveStateInfo& ssi : available_states) { if (ssi.global) @@ -1124,7 +1053,7 @@ void QtHostInterface::populateGameListContextMenu(const GameListEntry* entry, QW return; } - DeleteSaveStates(entry->code.c_str(), true); + System::DeleteSaveStates(entry->code.c_str(), true); }); } } @@ -1210,7 +1139,7 @@ void QtHostInterface::loadCheatList(const QString& filename) return; } - LoadCheatList(filename.toUtf8().constData()); + System::LoadCheatList(filename.toUtf8().constData()); } void QtHostInterface::setCheatEnabled(quint32 index, bool enabled) @@ -1222,7 +1151,7 @@ void QtHostInterface::setCheatEnabled(quint32 index, bool enabled) return; } - SetCheatCodeState(index, enabled, g_settings.auto_load_cheats); + System::SetCheatCodeState(index, enabled, g_settings.auto_load_cheats); emit cheatEnabled(index, enabled); } @@ -1234,7 +1163,7 @@ void QtHostInterface::applyCheat(quint32 index) return; } - ApplyCheatCode(index); + System::ApplyCheatCode(index); } void QtHostInterface::reloadPostProcessingShaders() @@ -1245,7 +1174,7 @@ void QtHostInterface::reloadPostProcessingShaders() return; } - ReloadPostProcessingShaders(); + System::ReloadPostProcessingShaders(); } void QtHostInterface::requestRenderWindowScale(qreal scale) @@ -1298,7 +1227,7 @@ void QtHostInterface::loadState(const QString& filename) if (System::IsShutdown()) emit emulationStarting(); - LoadState(filename.toStdString().c_str()); + System::LoadState(filename.toStdString().c_str()); } void QtHostInterface::loadState(bool global, qint32 slot) @@ -1309,7 +1238,7 @@ void QtHostInterface::loadState(bool global, qint32 slot) return; } - LoadState(global, slot); + System::LoadStateFromSlot(global, slot); } void QtHostInterface::saveState(const QString& filename, bool block_until_done /* = false */) @@ -1322,7 +1251,7 @@ void QtHostInterface::saveState(const QString& filename, bool block_until_done / } if (!System::IsShutdown()) - SaveState(filename.toUtf8().data()); + System::SaveState(filename.toUtf8().data()); } void QtHostInterface::saveState(bool global, qint32 slot, bool block_until_done /* = false */) @@ -1335,7 +1264,7 @@ void QtHostInterface::saveState(bool global, qint32 slot, bool block_until_done } if (!System::IsShutdown()) - SaveState(global, slot); + System::SaveStateToSlot(global, slot); } void QtHostInterface::undoLoadState() @@ -1346,7 +1275,7 @@ void QtHostInterface::undoLoadState() return; } - UndoLoadState(); + System::UndoLoadState(); } void QtHostInterface::setAudioOutputVolume(int volume, int fast_forward_volume) @@ -1360,9 +1289,7 @@ void QtHostInterface::setAudioOutputVolume(int volume, int fast_forward_volume) g_settings.audio_output_volume = volume; g_settings.audio_fast_forward_volume = fast_forward_volume; - - if (m_audio_stream) - m_audio_stream->SetOutputVolume(GetAudioOutputVolume()); + System::UpdateVolume(); } void QtHostInterface::setAudioOutputMuted(bool muted) @@ -1374,9 +1301,7 @@ void QtHostInterface::setAudioOutputMuted(bool muted) } g_settings.audio_output_muted = muted; - - if (m_audio_stream) - m_audio_stream->SetOutputVolume(GetAudioOutputVolume()); + System::UpdateVolume(); } void QtHostInterface::startDumpingAudio() @@ -1387,7 +1312,7 @@ void QtHostInterface::startDumpingAudio() return; } - StartDumpingAudio(); + System::StartDumpingAudio(); } void QtHostInterface::stopDumpingAudio() @@ -1398,7 +1323,7 @@ void QtHostInterface::stopDumpingAudio() return; } - StopDumpingAudio(); + System::StopDumpingAudio(); } void QtHostInterface::singleStepCPU() @@ -1426,9 +1351,9 @@ void QtHostInterface::dumpRAM(const QString& filename) const std::string filename_str = filename.toStdString(); if (System::DumpRAM(filename_str.c_str())) - ReportFormattedMessage("RAM dumped to '%s'", filename_str.c_str()); + Host::AddOSDMessage(fmt::format("RAM dumped to '{}'", filename_str), 10.0f); else - ReportFormattedMessage("Failed to dump RAM to '%s'", filename_str.c_str()); + Host::ReportErrorAsync("Error", fmt::format("Failed to dump RAM to '{}'", filename_str)); } void QtHostInterface::dumpVRAM(const QString& filename) @@ -1441,9 +1366,9 @@ void QtHostInterface::dumpVRAM(const QString& filename) const std::string filename_str = filename.toStdString(); if (System::DumpVRAM(filename_str.c_str())) - ReportFormattedMessage("VRAM dumped to '%s'", filename_str.c_str()); + Host::AddOSDMessage(fmt::format("VRAM dumped to '{}'", filename_str), 10.0f); else - ReportFormattedMessage("Failed to dump VRAM to '%s'", filename_str.c_str()); + Host::ReportErrorAsync("Error", fmt::format("Failed to dump VRAM to '{}'", filename_str)); } void QtHostInterface::dumpSPURAM(const QString& filename) @@ -1456,9 +1381,9 @@ void QtHostInterface::dumpSPURAM(const QString& filename) const std::string filename_str = filename.toStdString(); if (System::DumpSPURAM(filename_str.c_str())) - ReportFormattedMessage("SPU RAM dumped to '%s'", filename_str.c_str()); + Host::AddOSDMessage(fmt::format("SPU RAM dumped to '{}'", filename_str), 10.0f); else - ReportFormattedMessage("Failed to dump SPU RAM to '%s'", filename_str.c_str()); + Host::ReportErrorAsync("Error", fmt::format("Failed to dump SPU RAM to '{}'", filename_str)); } void QtHostInterface::saveScreenshot() @@ -1469,7 +1394,7 @@ void QtHostInterface::saveScreenshot() return; } - SaveScreenshot(nullptr, true, true); + System::SaveScreenshot(nullptr, true, true); } void QtHostInterface::OnAchievementsRefreshed() @@ -1507,14 +1432,9 @@ void QtHostInterface::OnAchievementsRefreshed() #endif } -void QtHostInterface::OnDisplayInvalidated() -{ - renderDisplay(); -} - void QtHostInterface::doBackgroundControllerPoll() { - PollAndUpdate(); + InputManager::PollSources(); } void QtHostInterface::createBackgroundControllerPollTimer() @@ -1582,24 +1502,7 @@ void QtHostInterface::threadEntryPoint() { if (System::IsRunning()) { - if (m_display_all_frames) - System::RunFrame(); - else - System::RunFrames(); - - InputManager::PollSources(); - if (m_frame_step_request) - { - m_frame_step_request = false; - PauseSystem(true); - } - - renderDisplay(); - - System::UpdatePerformanceCounters(); - - if (m_throttler_enabled) - System::Throttle(); + System::Execute(); } else { @@ -1611,11 +1514,10 @@ void QtHostInterface::threadEntryPoint() continue; } + m_worker_thread_event_loop->processEvents(QEventLoop::AllEvents); + InputManager::PollSources(); renderDisplay(); } - - m_worker_thread_event_loop->processEvents(QEventLoop::AllEvents); - PollAndUpdate(); } shutdownOnThread(); @@ -1635,7 +1537,7 @@ void QtHostInterface::threadEntryPoint() void QtHostInterface::renderDisplay() { ImGuiManager::RenderOSD(); - m_display->Render(); + s_host_display->Render(); ImGuiManager::NewFrame(); } @@ -1666,7 +1568,7 @@ static std::string GetFontPath(const char* name) void QtHostInterface::setImGuiFont() { - std::string language(GetStringSettingValue("Main", "Language", "")); + std::string language(Host::GetBaseStringSettingValue("Main", "Language", "")); std::string path; const ImWchar* range = nullptr; @@ -1695,15 +1597,15 @@ void QtHostInterface::setImGuiFont() Panic("Fixme"); } -TinyString QtHostInterface::TranslateString(const char* context, const char* str, - const char* disambiguation /*= nullptr*/, int n /*= -1*/) const +TinyString Host::TranslateString(const char* context, const char* str, const char* disambiguation /*= nullptr*/, + int n /*= -1*/) { const QByteArray bytes(qApp->translate(context, str, disambiguation, n).toUtf8()); return TinyString(bytes.constData(), bytes.size()); } -std::string QtHostInterface::TranslateStdString(const char* context, const char* str, - const char* disambiguation /*= nullptr*/, int n /*= -1*/) const +std::string Host::TranslateStdString(const char* context, const char* str, const char* disambiguation /*= nullptr*/, + int n /*= -1*/) { return qApp->translate(context, str, disambiguation, n).toStdString(); } @@ -1731,20 +1633,66 @@ bool QtHostInterface::Thread::waitForInit() return m_init_result.load(); } +void Host::ReportErrorAsync(const std::string_view& title, const std::string_view& message) +{ + if (!title.empty() && !message.empty()) + { + Log_ErrorPrintf("ReportErrorAsync: %.*s: %.*s", static_cast(title.size()), title.data(), + static_cast(message.size()), message.data()); + } + else if (!message.empty()) + { + Log_ErrorPrintf("ReportErrorAsync: %.*s", static_cast(message.size()), message.data()); + } + + QMetaObject::invokeMethod( + QtHostInterface::GetInstance()->getMainWindow(), "reportError", Qt::QueuedConnection, + Q_ARG(const QString&, title.empty() ? QString() : QString::fromUtf8(title.data(), title.size())), + Q_ARG(const QString&, message.empty() ? QString() : QString::fromUtf8(message.data(), message.size()))); +} + +bool Host::ConfirmMessage(const std::string_view& title, const std::string_view& message) +{ +#if 0 + const bool was_fullscreen = m_is_fullscreen; + if (was_fullscreen) + SetFullscreen(false); +#endif + + const bool result = emit QtHostInterface::GetInstance()->messageConfirmed( + QString::fromUtf8(title.data(), title.size()), QString::fromUtf8(message.data(), message.size())); + +#if 0 + if (was_fullscreen) + SetFullscreen(true); +#endif + + return result; +} + +void Host::ReportDebuggerMessage(const std::string_view& message) +{ + emit QtHostInterface::GetInstance()->debuggerMessageReported(QString::fromUtf8(message)); +} + void Host::OnInputDeviceConnected(const std::string_view& identifier, const std::string_view& device_name) { - + emit QtHostInterface::GetInstance()->onInputDeviceConnected( + identifier.empty() ? QString() : QString::fromUtf8(identifier.data(), identifier.size()), + device_name.empty() ? QString() : QString::fromUtf8(device_name.data(), device_name.size())); } void Host::OnInputDeviceDisconnected(const std::string_view& identifier) { - + emit QtHostInterface::GetInstance()->onInputDeviceDisconnected( + identifier.empty() ? QString() : QString::fromUtf8(identifier.data(), identifier.size())); } std::optional> Host::ReadResourceFile(const char* filename) { - //const std::string path(Path::Combine(EmuFolders::Resources, filename)); - const std::string path(QtHostInterface::GetInstance()->GetProgramDirectoryRelativePath("resources" FS_OSPATH_SEPARATOR_STR "%s", filename)); + // const std::string path(Path::Combine(EmuFolders::Resources, filename)); + const std::string path(QtHostInterface::GetInstance()->GetProgramDirectoryRelativePath( + "resources" FS_OSPATH_SEPARATOR_STR "%s", filename)); std::optional> ret(FileSystem::ReadBinaryFile(path.c_str())); if (!ret.has_value()) Log_ErrorPrintf("Failed to read resource file '%s'", filename); @@ -1753,73 +1701,177 @@ std::optional> Host::ReadResourceFile(const char* filename) std::optional Host::ReadResourceFileToString(const char* filename) { - const std::string path(QtHostInterface::GetInstance()->GetProgramDirectoryRelativePath("resources" FS_OSPATH_SEPARATOR_STR "%s", filename)); + const std::string path(QtHostInterface::GetInstance()->GetProgramDirectoryRelativePath( + "resources" FS_OSPATH_SEPARATOR_STR "%s", filename)); std::optional ret(FileSystem::ReadFileToString(path.c_str())); if (!ret.has_value()) Log_ErrorPrintf("Failed to read resource file to string '%s'", filename); return ret; } -void QtHost::SetBaseBoolSettingValue(const char* section, const char* key, bool value) +void Host::SetBaseBoolSettingValue(const char* section, const char* key, bool value) { auto lock = Host::GetSettingsLock(); s_base_settings_interface->SetBoolValue(section, key, value); - QueueSettingsSave(); + QtHost::QueueSettingsSave(); } -void QtHost::SetBaseIntSettingValue(const char* section, const char* key, int value) +void Host::SetBaseIntSettingValue(const char* section, const char* key, int value) { auto lock = Host::GetSettingsLock(); s_base_settings_interface->SetIntValue(section, key, value); - QueueSettingsSave(); + QtHost::QueueSettingsSave(); } -void QtHost::SetBaseFloatSettingValue(const char* section, const char* key, float value) +void Host::SetBaseFloatSettingValue(const char* section, const char* key, float value) { auto lock = Host::GetSettingsLock(); s_base_settings_interface->SetFloatValue(section, key, value); - QueueSettingsSave(); + QtHost::QueueSettingsSave(); } -void QtHost::SetBaseStringSettingValue(const char* section, const char* key, const char* value) +void Host::SetBaseStringSettingValue(const char* section, const char* key, const char* value) { auto lock = Host::GetSettingsLock(); s_base_settings_interface->SetStringValue(section, key, value); - QueueSettingsSave(); + QtHost::QueueSettingsSave(); } -void QtHost::SetBaseStringListSettingValue(const char* section, const char* key, const std::vector& values) +void Host::SetBaseStringListSettingValue(const char* section, const char* key, const std::vector& values) { auto lock = Host::GetSettingsLock(); s_base_settings_interface->SetStringList(section, key, values); - QueueSettingsSave(); + QtHost::QueueSettingsSave(); } -bool QtHost::AddBaseValueToStringList(const char* section, const char* key, const char* value) +bool Host::AddValueToBaseStringListSetting(const char* section, const char* key, const char* value) { auto lock = Host::GetSettingsLock(); if (!s_base_settings_interface->AddToStringList(section, key, value)) return false; - QueueSettingsSave(); + QtHost::QueueSettingsSave(); return true; } -bool QtHost::RemoveBaseValueFromStringList(const char* section, const char* key, const char* value) +bool Host::RemoveValueFromBaseStringListSetting(const char* section, const char* key, const char* value) { auto lock = Host::GetSettingsLock(); if (!s_base_settings_interface->RemoveFromStringList(section, key, value)) return false; - QueueSettingsSave(); + QtHost::QueueSettingsSave(); return true; } -void QtHost::RemoveBaseSettingValue(const char* section, const char* key) +void Host::DeleteBaseSettingValue(const char* section, const char* key) { auto lock = Host::GetSettingsLock(); s_base_settings_interface->DeleteValue(section, key); - QueueSettingsSave(); + QtHost::QueueSettingsSave(); +} + +void Host::CommitBaseSettingChanges() +{ + // TODO: Implement me, should run on UI thread + Panic("Fixme"); +} + +HostDisplay* Host::AcquireHostDisplay() +{ + return g_emu_thread->acquireHostDisplay(); +} + +void Host::ReleaseHostDisplay() +{ + g_emu_thread->releaseHostDisplay(); +} + +HostDisplay* Host::GetHostDisplay() +{ + return s_host_display.get(); +} + +void Host::InvalidateDisplay() +{ + g_emu_thread->renderDisplay(); +} + +void Host::RenderDisplay() +{ + g_emu_thread->renderDisplay(); +} + +void Host::LoadSettings(SettingsInterface& si, std::unique_lock& lock) +{ + CommonHost::LoadSettings(si, lock); +} + +void Host::CheckForSettingsChanges(const Settings& old_settings) +{ + CommonHost::CheckForSettingsChanges(old_settings); + // TODO, e.g. render to main +} + +void Host::OnSystemStarting() +{ + CommonHost::OnSystemStarting(); + // g_emu_thread->onSystemStarting(); +} + +void Host::OnSystemStarted() +{ + CommonHost::OnSystemStarted(); + g_emu_thread->onSystemStarted(); +} + +void Host::OnSystemPaused() +{ + CommonHost::OnSystemPaused(); + g_emu_thread->onSystemPaused(); +} + +void Host::OnSystemResumed() +{ + CommonHost::OnSystemResumed(); + g_emu_thread->onSystemResumed(); +} + +void Host::OnSystemDestroyed() +{ + CommonHost::OnSystemDestroyed(); + g_emu_thread->onSystemDestroyed(); +} + +void Host::OnPerformanceMetricsUpdated() +{ + GPURenderer renderer = GPURenderer::Count; + u32 render_width = 0; + u32 render_height = 0; + bool render_interlaced = false; + + if (g_gpu) + { + renderer = g_gpu->GetRendererType(); + std::tie(render_width, render_height) = g_gpu->GetEffectiveDisplayResolution(); + render_interlaced = g_gpu->IsInterlacedDisplayEnabled(); + } + + emit g_emu_thread->systemPerformanceCountersUpdated(System::GetEmulationSpeed(), System::GetFPS(), System::GetVPS(), + System::GetAverageFrameTime(), System::GetWorstFrameTime(), + renderer, render_width, render_height, render_interlaced); +} + +void Host::OnGameChanged(const std::string& disc_path, const std::string& game_serial, const std::string& game_name) +{ + emit g_emu_thread->runningGameChanged(QString::fromStdString(disc_path), QString::fromStdString(game_serial), + QString::fromStdString(game_name)); +} + +void Host::PumpMessagesOnCPUThread() +{ + g_emu_thread->getEventLoop()->processEvents(QEventLoop::AllEvents); + CommonHost::PumpMessagesOnCPUThread(); // calls InputManager::PollSources() } void QtHost::SaveSettings() @@ -1841,6 +1893,9 @@ void QtHost::QueueSettingsSave() if (s_settings_save_timer) return; + // TODO: Thread check here + Assert(!QtHostInterface::GetInstance()->isOnWorkerThread()); + s_settings_save_timer = std::make_unique(); s_settings_save_timer->connect(s_settings_save_timer.get(), &QTimer::timeout, SaveSettings); s_settings_save_timer->setSingleShot(true); diff --git a/src/duckstation-qt/qthostinterface.h b/src/duckstation-qt/qthostinterface.h index cdd8ad739..14d45964b 100644 --- a/src/duckstation-qt/qthostinterface.h +++ b/src/duckstation-qt/qthostinterface.h @@ -1,9 +1,11 @@ #pragma once #include "common/event.h" #include "core/host_interface.h" +#include "core/host_settings.h" #include "core/system.h" #include "frontend-common/common_host_interface.h" #include "frontend-common/game_list.h" +#include "frontend-common/input_manager.h" #include "qtutils.h" #include #include @@ -53,28 +55,7 @@ public: void RunLater(std::function func) override; -public Q_SLOTS: - void ReportError(const char* message) override; - void ReportMessage(const char* message) override; - void ReportDebuggerMessage(const char* message) override; - bool ConfirmMessage(const char* message) override; - public: - /// Thread-safe settings access. - void SetBoolSettingValue(const char* section, const char* key, bool value); - void SetIntSettingValue(const char* section, const char* key, int value); - void SetFloatSettingValue(const char* section, const char* key, float value); - void SetStringSettingValue(const char* section, const char* key, const char* value); - void SetStringListSettingValue(const char* section, const char* key, const std::vector& values); - bool AddValueToStringList(const char* section, const char* key, const char* value); - bool RemoveValueFromStringList(const char* section, const char* key, const char* value); - void RemoveSettingValue(const char* section, const char* key); - - TinyString TranslateString(const char* context, const char* str, const char* disambiguation = nullptr, - int n = -1) const override; - std::string TranslateStdString(const char* context, const char* str, const char* disambiguation = nullptr, - int n = -1) const override; - bool RequestRenderWindowSize(s32 new_window_width, s32 new_window_height) override; void* GetTopLevelWindowHandle() const override; @@ -89,8 +70,10 @@ public: ALWAYS_INLINE MainWindow* getMainWindow() const { return m_main_window; } void setMainWindow(MainWindow* window); - HostDisplay* createHostDisplay(); - void connectDisplaySignals(QtDisplayWidget* widget); + + + ALWAYS_INLINE QEventLoop* getEventLoop() const { return m_worker_thread_event_loop; } + void reinstallTranslator(); void populateLoadStateMenu(const char* game_code, QMenu* menu); @@ -119,12 +102,28 @@ public: /// Returns program directory as a QString. QString getProgramDirectory() const; + /// Called back from the GS thread when the display state changes (e.g. fullscreen, render to main). + static HostDisplay* createHostDisplay(); + HostDisplay* acquireHostDisplay(); + void connectDisplaySignals(QtDisplayWidget* widget); + void releaseHostDisplay(); + void updateDisplay(); + void renderDisplay(); + + void onSystemStarted(); + void onSystemPaused(); + void onSystemResumed(); + void onSystemDestroyed(); + Q_SIGNALS: - void errorReported(const QString& message); - void messageReported(const QString& message); + void errorReported(const QString& title, const QString& message); + bool messageConfirmed(const QString& title, const QString& message); void debuggerMessageReported(const QString& message); - bool messageConfirmed(const QString& message); void settingsResetToDefault(); + void onInputDevicesEnumerated(const QList>& devices); + void onInputDeviceConnected(const QString& identifier, const QString& device_name); + void onInputDeviceDisconnected(const QString& identifier); + void onVibrationMotorsEnumerated(const QList& motors); void emulationStarting(); void emulationStarted(); void emulationStopped(); @@ -149,8 +148,11 @@ public Q_SLOTS: void setDefaultSettings(); void applySettings(bool display_osd_messages = false); void reloadGameSettings(); - void reloadInputBindings(); void applyInputProfile(const QString& profile_path); + void reloadInputSources(); + void reloadInputBindings(); + void enumerateInputDevices(); + void enumerateVibrationMotors(); void bootSystem(std::shared_ptr params); void resumeSystemFromState(const QString& filename, bool boot_on_failure); void resumeSystemFromMostRecentState(); @@ -184,7 +186,6 @@ public Q_SLOTS: void requestRenderWindowScale(qreal scale); void executeOnEmulationThread(std::function callback, bool wait = false); void OnAchievementsRefreshed() override; - void OnDisplayInvalidated() override; private Q_SLOTS: void doStopThread(); @@ -197,24 +198,11 @@ private Q_SLOTS: void doBackgroundControllerPoll(); protected: - bool AcquireHostDisplay() override; - void ReleaseHostDisplay() override; bool IsFullscreen() const override; bool SetFullscreen(bool enabled) override; void RequestExit() override; - void OnSystemCreated() override; - void OnSystemPaused(bool paused) override; - void OnSystemDestroyed() override; - void OnSystemPerformanceCountersUpdated() override; - void OnRunningGameChanged(const std::string& path, CDImage* image, const std::string& game_code, - const std::string& game_title) override; - - void SetDefaultSettings(SettingsInterface& si) override; - void SetDefaultSettings() override; - void ApplySettings(bool display_osd_messages) override; - void SetMouseMode(bool relative, bool hide_cursor) override; private: @@ -258,7 +246,6 @@ private: bool initializeOnThread(); void shutdownOnThread(); void installTranslator(); - void renderDisplay(); void checkRenderToMainState(); void updateDisplayState(); void queueSettingsSave(); @@ -281,31 +268,25 @@ private: bool m_lost_exclusive_fullscreen = false; }; +extern QtHostInterface* g_emu_thread; + namespace QtHost { /// Sets batch mode (exit after game shutdown). -//bool InBatchMode(); -//void SetBatchMode(bool enabled); +// bool InBatchMode(); +// void SetBatchMode(bool enabled); /// Executes a function on the UI thread. void RunOnUIThread(const std::function& func, bool block = false); /// Returns the application name and version, optionally including debug/devel config indicator. -//QString GetAppNameAndVersion(); +// QString GetAppNameAndVersion(); /// Returns the debug/devel config indicator. -//QString GetAppConfigSuffix(); +// QString GetAppConfigSuffix(); /// Returns the base path for resources. This may be : prefixed, if we're using embedded resources. -//QString GetResourcesBasePath(); +// QString GetResourcesBasePath(); /// Thread-safe settings access. -void SetBaseBoolSettingValue(const char* section, const char* key, bool value); -void SetBaseIntSettingValue(const char* section, const char* key, int 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& values); -bool AddBaseValueToStringList(const char* section, const char* key, const char* value); -bool RemoveBaseValueFromStringList(const char* section, const char* key, const char* value); -void RemoveBaseSettingValue(const char* section, const char* key); void QueueSettingsSave(); } // namespace QtHost diff --git a/src/duckstation-qt/resources/controllers/analog_controller.svg b/src/duckstation-qt/resources/controllers/analog_controller.svg new file mode 100644 index 000000000..ba5442a2b --- /dev/null +++ b/src/duckstation-qt/resources/controllers/analog_controller.svg @@ -0,0 +1,422 @@ + +1122LR diff --git a/src/duckstation-qt/resources/controllers/digital_controller.svg b/src/duckstation-qt/resources/controllers/digital_controller.svg new file mode 100644 index 000000000..b48e282ee --- /dev/null +++ b/src/duckstation-qt/resources/controllers/digital_controller.svg @@ -0,0 +1,372 @@ + +1122LR diff --git a/src/duckstation-qt/resources/controllers/guncon.svg b/src/duckstation-qt/resources/controllers/guncon.svg new file mode 100644 index 000000000..60acbb915 --- /dev/null +++ b/src/duckstation-qt/resources/controllers/guncon.svg @@ -0,0 +1,466 @@ + + diff --git a/src/duckstation-qt/resources/controllers/negcon.svg b/src/duckstation-qt/resources/controllers/negcon.svg new file mode 100644 index 000000000..bab42cda5 --- /dev/null +++ b/src/duckstation-qt/resources/controllers/negcon.svg @@ -0,0 +1,288 @@ + +BIIIA diff --git a/src/duckstation-qt/resources/generate.sh b/src/duckstation-qt/resources/generate.sh new file mode 100644 index 000000000..7fc10c012 --- /dev/null +++ b/src/duckstation-qt/resources/generate.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +IFS=" +" + +printf "\n" +printf "\t\n" +for i in $(find . -not -iname '*.sh' -not -iname '*.qrc' -type f | cut -d'/' -f2-99); do + printf "\t\t%s\n" "$i" +done +printf "\t\n" +printf "\n" + diff --git a/src/duckstation-qt/resources/icons/black/index.theme b/src/duckstation-qt/resources/icons/black/index.theme index 97fdbceaf..297bf455c 100644 --- a/src/duckstation-qt/resources/icons/black/index.theme +++ b/src/duckstation-qt/resources/icons/black/index.theme @@ -13,3 +13,9 @@ Type=Fixed [64] Size=64 Type=Fixed + +[svg] +Size=64 +Type=Scalable +MinSize=64 +MaxSize=1024 diff --git a/src/duckstation-qt/resources/icons/black/svg/arrow-left-right-line.svg b/src/duckstation-qt/resources/icons/black/svg/arrow-left-right-line.svg new file mode 100644 index 000000000..c8686183f --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/arrow-left-right-line.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/duckstation-qt/resources/icons/black/svg/artboard-2-line.svg b/src/duckstation-qt/resources/icons/black/svg/artboard-2-line.svg new file mode 100644 index 000000000..7523e10a1 --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/artboard-2-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/book-open-line.svg b/src/duckstation-qt/resources/icons/black/svg/book-open-line.svg new file mode 100644 index 000000000..cbcbbfb95 --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/book-open-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/brush-line.svg b/src/duckstation-qt/resources/icons/black/svg/brush-line.svg new file mode 100644 index 000000000..164b0eaad --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/brush-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/close-line.svg b/src/duckstation-qt/resources/icons/black/svg/close-line.svg new file mode 100644 index 000000000..0ef4f305a --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/close-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/dashboard-line.svg b/src/duckstation-qt/resources/icons/black/svg/dashboard-line.svg new file mode 100644 index 000000000..1d2279e5d --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/dashboard-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/disc-line.svg b/src/duckstation-qt/resources/icons/black/svg/disc-line.svg new file mode 100644 index 000000000..ce505c56c --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/disc-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/door-open-line.svg b/src/duckstation-qt/resources/icons/black/svg/door-open-line.svg new file mode 100644 index 000000000..0a907eb9e --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/door-open-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/download-2-line.svg b/src/duckstation-qt/resources/icons/black/svg/download-2-line.svg new file mode 100644 index 000000000..42702f57d --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/download-2-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/dvd-line.svg b/src/duckstation-qt/resources/icons/black/svg/dvd-line.svg new file mode 100644 index 000000000..7167e65e5 --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/dvd-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/eject-line.svg b/src/duckstation-qt/resources/icons/black/svg/eject-line.svg new file mode 100644 index 000000000..7d865ff27 --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/eject-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/file-add-line.svg b/src/duckstation-qt/resources/icons/black/svg/file-add-line.svg new file mode 100644 index 000000000..cbf03b45f --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/file-add-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/file-line.svg b/src/duckstation-qt/resources/icons/black/svg/file-line.svg new file mode 100644 index 000000000..faa32efa5 --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/file-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/file-list-line.svg b/src/duckstation-qt/resources/icons/black/svg/file-list-line.svg new file mode 100644 index 000000000..41fddce0c --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/file-list-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/file-reduce-line.svg b/src/duckstation-qt/resources/icons/black/svg/file-reduce-line.svg new file mode 100644 index 000000000..f82b9b9fb --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/file-reduce-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/file-search-line.svg b/src/duckstation-qt/resources/icons/black/svg/file-search-line.svg new file mode 100644 index 000000000..6f8cce6ec --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/file-search-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/file-settings-line.svg b/src/duckstation-qt/resources/icons/black/svg/file-settings-line.svg new file mode 100644 index 000000000..8a2573108 --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/file-settings-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/flask-line.svg b/src/duckstation-qt/resources/icons/black/svg/flask-line.svg new file mode 100644 index 000000000..a0a8d662b --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/flask-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/folder-add-line.svg b/src/duckstation-qt/resources/icons/black/svg/folder-add-line.svg new file mode 100644 index 000000000..11dd5c5fd --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/folder-add-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/folder-open-line.svg b/src/duckstation-qt/resources/icons/black/svg/folder-open-line.svg new file mode 100644 index 000000000..0936516fd --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/folder-open-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/folder-reduce-line.svg b/src/duckstation-qt/resources/icons/black/svg/folder-reduce-line.svg new file mode 100644 index 000000000..196cf14ce --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/folder-reduce-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/folder-settings-line.svg b/src/duckstation-qt/resources/icons/black/svg/folder-settings-line.svg new file mode 100644 index 000000000..be22b8e06 --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/folder-settings-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/fullscreen-line.svg b/src/duckstation-qt/resources/icons/black/svg/fullscreen-line.svg new file mode 100644 index 000000000..b1ddc2874 --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/fullscreen-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/function-line.svg b/src/duckstation-qt/resources/icons/black/svg/function-line.svg new file mode 100644 index 000000000..198aeb330 --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/function-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/gamepad-line.svg b/src/duckstation-qt/resources/icons/black/svg/gamepad-line.svg new file mode 100644 index 000000000..3d4649f4c --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/gamepad-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/hard-drive-2-line.svg b/src/duckstation-qt/resources/icons/black/svg/hard-drive-2-line.svg new file mode 100644 index 000000000..05530193a --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/hard-drive-2-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/keyboard-line.svg b/src/duckstation-qt/resources/icons/black/svg/keyboard-line.svg new file mode 100644 index 000000000..e878b9981 --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/keyboard-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/layout-grid-line.svg b/src/duckstation-qt/resources/icons/black/svg/layout-grid-line.svg new file mode 100644 index 000000000..a07ffd2f1 --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/layout-grid-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/list-check.svg b/src/duckstation-qt/resources/icons/black/svg/list-check.svg new file mode 100644 index 000000000..a43c5afef --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/list-check.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/pause-line.svg b/src/duckstation-qt/resources/icons/black/svg/pause-line.svg new file mode 100644 index 000000000..81cffce1d --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/pause-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/play-line.svg b/src/duckstation-qt/resources/icons/black/svg/play-line.svg new file mode 100644 index 000000000..b587d9e52 --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/play-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/refresh-line.svg b/src/duckstation-qt/resources/icons/black/svg/refresh-line.svg new file mode 100644 index 000000000..ff25835eb --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/refresh-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/restart-line.svg b/src/duckstation-qt/resources/icons/black/svg/restart-line.svg new file mode 100644 index 000000000..4b18470d2 --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/restart-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/save-3-line.svg b/src/duckstation-qt/resources/icons/black/svg/save-3-line.svg new file mode 100644 index 000000000..f3c740c0f --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/save-3-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/screenshot-2-line.svg b/src/duckstation-qt/resources/icons/black/svg/screenshot-2-line.svg new file mode 100644 index 000000000..b4cfebad4 --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/screenshot-2-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/sd-card-line.svg b/src/duckstation-qt/resources/icons/black/svg/sd-card-line.svg new file mode 100644 index 000000000..3ccb3dac3 --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/sd-card-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/settings-3-line.svg b/src/duckstation-qt/resources/icons/black/svg/settings-3-line.svg new file mode 100644 index 000000000..19cf2c242 --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/settings-3-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/shut-down-line.svg b/src/duckstation-qt/resources/icons/black/svg/shut-down-line.svg new file mode 100644 index 000000000..5f453822f --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/shut-down-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/tv-2-line.svg b/src/duckstation-qt/resources/icons/black/svg/tv-2-line.svg new file mode 100644 index 000000000..4f58b00a9 --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/tv-2-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/volume-up-line.svg b/src/duckstation-qt/resources/icons/black/svg/volume-up-line.svg new file mode 100644 index 000000000..1e529e14e --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/volume-up-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/black/svg/window-2-line.svg b/src/duckstation-qt/resources/icons/black/svg/window-2-line.svg new file mode 100644 index 000000000..7d7f7e056 --- /dev/null +++ b/src/duckstation-qt/resources/icons/black/svg/window-2-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/index.theme b/src/duckstation-qt/resources/icons/white/index.theme index 912dc8572..ed25855a6 100644 --- a/src/duckstation-qt/resources/icons/white/index.theme +++ b/src/duckstation-qt/resources/icons/white/index.theme @@ -13,3 +13,9 @@ Type=Fixed [64] Size=64 Type=Fixed + +[svg] +Size=64 +Type=Scalable +MinSize=64 +MaxSize=1024 diff --git a/src/duckstation-qt/resources/icons/white/svg/arrow-left-right-line.svg b/src/duckstation-qt/resources/icons/white/svg/arrow-left-right-line.svg new file mode 100644 index 000000000..a9f9121b1 --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/arrow-left-right-line.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/duckstation-qt/resources/icons/white/svg/artboard-2-line.svg b/src/duckstation-qt/resources/icons/white/svg/artboard-2-line.svg new file mode 100644 index 000000000..ad7abab03 --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/artboard-2-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/book-open-line.svg b/src/duckstation-qt/resources/icons/white/svg/book-open-line.svg new file mode 100644 index 000000000..e2e77c5a2 --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/book-open-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/brush-line.svg b/src/duckstation-qt/resources/icons/white/svg/brush-line.svg new file mode 100644 index 000000000..c02f20914 --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/brush-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/close-line.svg b/src/duckstation-qt/resources/icons/white/svg/close-line.svg new file mode 100644 index 000000000..fddbc847d --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/close-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/dashboard-line.svg b/src/duckstation-qt/resources/icons/white/svg/dashboard-line.svg new file mode 100644 index 000000000..4979faeab --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/dashboard-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/disc-line.svg b/src/duckstation-qt/resources/icons/white/svg/disc-line.svg new file mode 100644 index 000000000..217b1493f --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/disc-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/door-open-line.svg b/src/duckstation-qt/resources/icons/white/svg/door-open-line.svg new file mode 100644 index 000000000..6a1d39e60 --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/door-open-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/download-2-line.svg b/src/duckstation-qt/resources/icons/white/svg/download-2-line.svg new file mode 100644 index 000000000..81db21b91 --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/download-2-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/dvd-line.svg b/src/duckstation-qt/resources/icons/white/svg/dvd-line.svg new file mode 100644 index 000000000..e55acf9c9 --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/dvd-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/eject-line.svg b/src/duckstation-qt/resources/icons/white/svg/eject-line.svg new file mode 100644 index 000000000..4b4a40a09 --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/eject-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/file-add-line.svg b/src/duckstation-qt/resources/icons/white/svg/file-add-line.svg new file mode 100644 index 000000000..e5d65ee7e --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/file-add-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/file-line.svg b/src/duckstation-qt/resources/icons/white/svg/file-line.svg new file mode 100644 index 000000000..57b85a625 --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/file-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/file-list-line.svg b/src/duckstation-qt/resources/icons/white/svg/file-list-line.svg new file mode 100644 index 000000000..d996aeaa2 --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/file-list-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/file-reduce-line.svg b/src/duckstation-qt/resources/icons/white/svg/file-reduce-line.svg new file mode 100644 index 000000000..45507fb8b --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/file-reduce-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/file-search-line.svg b/src/duckstation-qt/resources/icons/white/svg/file-search-line.svg new file mode 100644 index 000000000..c6e275173 --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/file-search-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/file-settings-line.svg b/src/duckstation-qt/resources/icons/white/svg/file-settings-line.svg new file mode 100644 index 000000000..796b5ad7f --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/file-settings-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/flask-line.svg b/src/duckstation-qt/resources/icons/white/svg/flask-line.svg new file mode 100644 index 000000000..78b569ad4 --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/flask-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/folder-add-line.svg b/src/duckstation-qt/resources/icons/white/svg/folder-add-line.svg new file mode 100644 index 000000000..33342f97e --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/folder-add-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/folder-open-line.svg b/src/duckstation-qt/resources/icons/white/svg/folder-open-line.svg new file mode 100644 index 000000000..f4d21c6ce --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/folder-open-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/folder-reduce-line.svg b/src/duckstation-qt/resources/icons/white/svg/folder-reduce-line.svg new file mode 100644 index 000000000..06dbfee57 --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/folder-reduce-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/folder-settings-line.svg b/src/duckstation-qt/resources/icons/white/svg/folder-settings-line.svg new file mode 100644 index 000000000..59414d624 --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/folder-settings-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/fullscreen-line.svg b/src/duckstation-qt/resources/icons/white/svg/fullscreen-line.svg new file mode 100644 index 000000000..a2cce91fb --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/fullscreen-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/function-line.svg b/src/duckstation-qt/resources/icons/white/svg/function-line.svg new file mode 100644 index 000000000..37e4982d4 --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/function-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/gamepad-line.svg b/src/duckstation-qt/resources/icons/white/svg/gamepad-line.svg new file mode 100644 index 000000000..2a1bd0bf0 --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/gamepad-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/hard-drive-2-line.svg b/src/duckstation-qt/resources/icons/white/svg/hard-drive-2-line.svg new file mode 100644 index 000000000..1538ac93c --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/hard-drive-2-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/keyboard-line.svg b/src/duckstation-qt/resources/icons/white/svg/keyboard-line.svg new file mode 100644 index 000000000..0039fb0e0 --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/keyboard-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/layout-grid-line.svg b/src/duckstation-qt/resources/icons/white/svg/layout-grid-line.svg new file mode 100644 index 000000000..b05840c76 --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/layout-grid-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/list-check.svg b/src/duckstation-qt/resources/icons/white/svg/list-check.svg new file mode 100644 index 000000000..ac0761b4d --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/list-check.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/pause-line.svg b/src/duckstation-qt/resources/icons/white/svg/pause-line.svg new file mode 100644 index 000000000..061528c6a --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/pause-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/play-line.svg b/src/duckstation-qt/resources/icons/white/svg/play-line.svg new file mode 100644 index 000000000..a0da121e7 --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/play-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/refresh-line.svg b/src/duckstation-qt/resources/icons/white/svg/refresh-line.svg new file mode 100644 index 000000000..4c27929bf --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/refresh-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/restart-line.svg b/src/duckstation-qt/resources/icons/white/svg/restart-line.svg new file mode 100644 index 000000000..00fd375cc --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/restart-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/save-3-line.svg b/src/duckstation-qt/resources/icons/white/svg/save-3-line.svg new file mode 100644 index 000000000..a44772876 --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/save-3-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/screenshot-2-line.svg b/src/duckstation-qt/resources/icons/white/svg/screenshot-2-line.svg new file mode 100644 index 000000000..7ce64dcc3 --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/screenshot-2-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/sd-card-line.svg b/src/duckstation-qt/resources/icons/white/svg/sd-card-line.svg new file mode 100644 index 000000000..e80e0df43 --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/sd-card-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/settings-3-line.svg b/src/duckstation-qt/resources/icons/white/svg/settings-3-line.svg new file mode 100644 index 000000000..63815fa40 --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/settings-3-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/shut-down-line.svg b/src/duckstation-qt/resources/icons/white/svg/shut-down-line.svg new file mode 100644 index 000000000..30897e962 --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/shut-down-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/tv-2-line.svg b/src/duckstation-qt/resources/icons/white/svg/tv-2-line.svg new file mode 100644 index 000000000..789a795a3 --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/tv-2-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/volume-up-line.svg b/src/duckstation-qt/resources/icons/white/svg/volume-up-line.svg new file mode 100644 index 000000000..86f306402 --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/volume-up-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/icons/white/svg/window-2-line.svg b/src/duckstation-qt/resources/icons/white/svg/window-2-line.svg new file mode 100644 index 000000000..3080e3aa0 --- /dev/null +++ b/src/duckstation-qt/resources/icons/white/svg/window-2-line.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/duckstation-qt/resources/resources.qrc b/src/duckstation-qt/resources/resources.qrc index c031b8876..e69d5df4c 100644 --- a/src/duckstation-qt/resources/resources.qrc +++ b/src/duckstation-qt/resources/resources.qrc @@ -1,625 +1,711 @@ - - icons/address-book-new-22.png - icons/address-book-new-22@2x.png - icons/antialias-icon.png - icons/antialias-icon@2x.png - icons/applications-development.png - icons/applications-development@2x.png - icons/applications-graphics.png - icons/applications-graphics@2x.png - icons/applications-internet.png - icons/applications-other.png - icons/applications-other@2x.png - icons/applications-system-24.png - icons/applications-system-24@2x.png - icons/applications-system.png - icons/applications-system@2x.png - icons/audio-card.png - icons/audio-card@2x.png - icons/black/16/AchievementsSettings.png - icons/black/16/AddGameDirectory.png - icons/black/16/AdvancedSettings.png - icons/black/16/AudioSettings.png - icons/black/16/BIOSSettings.png - icons/black/16/ChangeDisc.png - icons/black/16/Cheats.png - icons/black/16/Clear.png - icons/black/16/ConsoleSettings.png - icons/black/16/ControllerSettings.png - icons/black/16/DisplaySettings.png - icons/black/16/EmulationSettings.png - icons/black/16/EnhancementSettings.png - icons/black/16/Exit.png - icons/black/16/Fullscreen.png - icons/black/16/GameGrid.png - icons/black/16/GameList.png - icons/black/16/GamelistSettings.png - icons/black/16/GeneralSettings.png - icons/black/16/HotkeySettings.png - icons/black/16/Language.png - icons/black/16/LoadState.png - icons/black/16/LockToolbar.png - icons/black/16/MemorycardSettings.png - icons/black/16/MoveDown.png - icons/black/16/MoveUp.png - icons/black/16/Options.png - icons/black/16/Pause.png - icons/black/16/PostProcessingAdd.png - icons/black/16/PostProcessingRemove.png - icons/black/16/PostprocessingSettings.png - icons/black/16/PowerOff.png - icons/black/16/PoweroffWsaving.png - icons/black/16/RescanAllGames.png - icons/black/16/Reset.png - icons/black/16/Resume.png - icons/black/16/SaveState.png - icons/black/16/ScanForGames.png - icons/black/16/Screenshot.png - icons/black/16/StartdiscSettings.png - icons/black/16/StartfileSettings.png - icons/black/32/AchievementsSettings.png - icons/black/32/AddGameDirectory.png - icons/black/32/AdvancedSettings.png - icons/black/32/AudioSettings.png - icons/black/32/BIOSSettings.png - icons/black/32/ChangeDisc.png - icons/black/32/Cheats.png - icons/black/32/Clear.png - icons/black/32/ConsoleSettings.png - icons/black/32/ControllerSettings.png - icons/black/32/DisplaySettings.png - icons/black/32/EmulationSettings.png - icons/black/32/EnhancementSettings.png - icons/black/32/Exit.png - icons/black/32/Fullscreen.png - icons/black/32/GameGrid.png - icons/black/32/GameList.png - icons/black/32/GamelistSettings.png - icons/black/32/GeneralSettings.png - icons/black/32/HotkeySettings.png - icons/black/32/Language.png - icons/black/32/LoadState.png - icons/black/32/LockToolbar.png - icons/black/32/MemorycardSettings.png - icons/black/32/MoveDown.png - icons/black/32/MoveUp.png - icons/black/32/Options.png - icons/black/32/Pause.png - icons/black/32/PostProcessingAdd.png - icons/black/32/PostProcessingRemove.png - icons/black/32/PostprocessingSettings.png - icons/black/32/PowerOff.png - icons/black/32/PoweroffWsaving.png - icons/black/32/RescanAllGames.png - icons/black/32/Reset.png - icons/black/32/Resume.png - icons/black/32/SaveState.png - icons/black/32/ScanForGames.png - icons/black/32/Screenshot.png - icons/black/32/StartdiscSettings.png - icons/black/32/StartfileSettings.png - icons/black/64/AchievementsSettings.png - icons/black/64/AddGameDirectory.png - icons/black/64/AdvancedSettings.png - icons/black/64/AudioSettings.png - icons/black/64/BIOSSettings.png - icons/black/64/ChangeDisc.png - icons/black/64/Cheats.png - icons/black/64/Clear.png - icons/black/64/ConsoleSettings.png - icons/black/64/ControllerSettings.png - icons/black/64/DisplaySettings.png - icons/black/64/EmulationSettings.png - icons/black/64/EnhancementSettings.png - icons/black/64/Exit.png - icons/black/64/Fullscreen.png - icons/black/64/GameGrid.png - icons/black/64/GameList.png - icons/black/64/GamelistSettings.png - icons/black/64/GeneralSettings.png - icons/black/64/HotkeySettings.png - icons/black/64/Language.png - icons/black/64/LoadState.png - icons/black/64/LockToolbar.png - icons/black/64/MemorycardSettings.png - icons/black/64/MoveDown.png - icons/black/64/MoveUp.png - icons/black/64/Options.png - icons/black/64/Pause.png - icons/black/64/PostProcessingAdd.png - icons/black/64/PostProcessingRemove.png - icons/black/64/PostprocessingSettings.png - icons/black/64/PowerOff.png - icons/black/64/PoweroffWsaving.png - icons/black/64/RescanAllGames.png - icons/black/64/Reset.png - icons/black/64/Resume.png - icons/black/64/SaveState.png - icons/black/64/ScanForGames.png - icons/black/64/Screenshot.png - icons/black/64/StartdiscSettings.png - icons/black/64/StartfileSettings.png - icons/black/index.theme - icons/camera-photo.png - icons/camera-photo@2x.png - icons/camera-video.png - icons/camera-video@2x.png - icons/conical-flask-red.png - icons/conical-flask-red@2x.png - icons/cover-placeholder.png - icons/debug-execute-from-cursor.png - icons/debug-execute-to-cursor.png - icons/debug-pc.png - icons/debug-pc@2x.png - icons/debug-run-cursor.png - icons/debug-run.png - icons/debug-step-instruction.png - icons/debug-step-into-instruction.png - icons/debug-step-into.png - icons/debug-step-out.png - icons/debug-step-over.png - icons/debug-trace.png - icons/discord.png - icons/document-new.png - icons/document-new@2x.png - icons/document-open.png - icons/document-open@2x.png - icons/document-save.png - icons/document-save@2x.png - icons/drive-optical.png - icons/drive-optical@2x.png - icons/drive-removable-media.png - icons/drive-removable-media@2x.png - icons/duck.png - icons/duck_128.png - icons/duck_64.png - icons/edit-clear-16.png - icons/edit-clear-16@2x.png - icons/edit-find.png - icons/emblem-person-blue.png - icons/emblem-person-blue@2x.png - icons/flag-eu.png - icons/flag-eu.svg - icons/flag-eu@2x.png - icons/flag-jp.png - icons/flag-jp.svg - icons/flag-jp@2x.png - icons/flag-other.png - icons/flag-other@2x.png - icons/flag-uc.png - icons/flag-uc.svg - icons/flag-uc@2x.png - icons/flag-us.png - icons/flag-us.svg - icons/flag-us@2x.png - icons/flags/de.png - icons/flags/de@2x.png - icons/flags/en.png - icons/flags/en@2x.png - icons/flags/es.png - icons/flags/es@2x.png - icons/flags/fr.png - icons/flags/fr@2x.png - icons/flags/he.png - icons/flags/he@2x.png - icons/flags/it.png - icons/flags/it@2x.png - icons/flags/ja.png - icons/flags/ja@2x.png - icons/flags/nl.png - icons/flags/nl@2x.png - icons/flags/pl.png - icons/flags/pl@2x.png - icons/flags/pt-br.png - icons/flags/pt-br@2x.png - icons/flags/pt-pt.png - icons/flags/pt-pt@2x.png - icons/flags/ru.png - icons/flags/ru@2x.png - icons/flags/tr.png - icons/flags/tr@2x.png - icons/flags/zh-cn.png - icons/flags/zh-cn@2x.png - icons/folder-open.png - icons/folder-open@2x.png - icons/github.png - icons/go-down-16.png - icons/go-down-16@2x.png - icons/go-up-16.png - icons/go-up-16@2x.png - icons/input-gaming.png - icons/input-gaming@2x.png - icons/IssueTracker.png - icons/list-add.png - icons/list-add@2x.png - icons/list-remove.png - icons/list-remove@2x.png - icons/media-flash-2.png - icons/media-flash-24.png - icons/media-flash-24@2x.png - icons/media-flash-2@2x.png - icons/media-flash.png - icons/media-flash@2x.png - icons/media-optical-24.png - icons/media-optical-24@2x.png - icons/media-optical-gear-24.png - icons/media-optical-gear-24@2x.png - icons/media-optical.png - icons/media-optical@2x.png - icons/media-playback-pause.png - icons/media-playback-pause@2x.png - icons/media-playback-start.png - icons/media-playback-start@2x.png - icons/media-record.png - icons/media-record@2x.png - icons/multimedia-player.png - icons/multimedia-player@2x.png - icons/preferences-desktop-keyboard-shortcuts.png - icons/preferences-desktop-keyboard-shortcuts@2x.png - icons/preferences-system.png - icons/preferences-system@2x.png - icons/process-stop.png - icons/process-stop@2x.png - icons/QT.png - icons/software-update-available.png - icons/software-update-available@2x.png - icons/star-0.png - icons/star-1.png - icons/star-2.png - icons/star-3.png - icons/star-4.png - icons/star-5.png - icons/system-file-manager.png - icons/system-file-manager@2x.png - icons/system-search.png - icons/system-search@2x.png - icons/system-shutdown.png - icons/system-shutdown@2x.png - icons/trophy.png - icons/trophy@2x.png - icons/update.png - icons/UpdateDuck.png - icons/utilities-system-monitor.png - icons/utilities-system-monitor@2x.png - icons/video-display.png - icons/video-display@2x.png - icons/view-fullscreen.png - icons/view-fullscreen@2x.png - icons/view-refresh.png - icons/view-refresh@2x.png - icons/white/16/AchievementsSettings.png - icons/white/16/AddGameDirectory.png - icons/white/16/AdvancedSettings.png - icons/white/16/AudioSettings.png - icons/white/16/BIOSSettings.png - icons/white/16/ChangeDisc.png - icons/white/16/Cheats.png - icons/white/16/Clear.png - icons/white/16/ConsoleSettings.png - icons/white/16/ControllerSettings.png - icons/white/16/DisplaySettings.png - icons/white/16/EmulationSettings.png - icons/white/16/EnhancementSettings.png - icons/white/16/Exit.png - icons/white/16/Fullscreen.png - icons/white/16/GameGrid.png - icons/white/16/GameList.png - icons/white/16/GamelistSettings.png - icons/white/16/GeneralSettings.png - icons/white/16/HotkeySettings.png - icons/white/16/Language.png - icons/white/16/LoadState.png - icons/white/16/LockToolbar.png - icons/white/16/MemorycardSettings.png - icons/white/16/MoveDown.png - icons/white/16/MoveUp.png - icons/white/16/Options.png - icons/white/16/Pause.png - icons/white/16/PostProcessingAdd.png - icons/white/16/PostProcessingRemove.png - icons/white/16/PostprocessingSettings.png - icons/white/16/PowerOff.png - icons/white/16/PoweroffWsaving.png - icons/white/16/RescanAllGames.png - icons/white/16/Reset.png - icons/white/16/Resume.png - icons/white/16/SaveState.png - icons/white/16/ScanForGames.png - icons/white/16/Screenshot.png - icons/white/16/StartdiscSettings.png - icons/white/16/StartfileSettings.png - icons/white/32/AchievementsSettings.png - icons/white/32/AddGameDirectory.png - icons/white/32/AdvancedSettings.png - icons/white/32/AudioSettings.png - icons/white/32/BIOSSettings.png - icons/white/32/ChangeDisc.png - icons/white/32/Cheats.png - icons/white/32/Clear.png - icons/white/32/ConsoleSettings.png - icons/white/32/ControllerSettings.png - icons/white/32/DisplaySettings.png - icons/white/32/EmulationSettings.png - icons/white/32/EnhancementSettings.png - icons/white/32/Exit.png - icons/white/32/Fullscreen.png - icons/white/32/GameGrid.png - icons/white/32/GameList.png - icons/white/32/GamelistSettings.png - icons/white/32/GeneralSettings.png - icons/white/32/HotkeySettings.png - icons/white/32/Language.png - icons/white/32/LoadState.png - icons/white/32/LockToolbar.png - icons/white/32/MemorycardSettings.png - icons/white/32/MoveDown.png - icons/white/32/MoveUp.png - icons/white/32/Options.png - icons/white/32/Pause.png - icons/white/32/PostProcessingAdd.png - icons/white/32/PostProcessingRemove.png - icons/white/32/PostprocessingSettings.png - icons/white/32/PowerOff.png - icons/white/32/PoweroffWsaving.png - icons/white/32/RescanAllGames.png - icons/white/32/Reset.png - icons/white/32/Resume.png - icons/white/32/SaveState.png - icons/white/32/ScanForGames.png - icons/white/32/Screenshot.png - icons/white/32/StartdiscSettings.png - icons/white/32/StartfileSettings.png - icons/white/64/AchievementsSettings.png - icons/white/64/AddGameDirectory.png - icons/white/64/AdvancedSettings.png - icons/white/64/AudioSettings.png - icons/white/64/BIOSSettings.png - icons/white/64/ChangeDisc.png - icons/white/64/Cheats.png - icons/white/64/Clear.png - icons/white/64/ConsoleSettings.png - icons/white/64/ControllerSettings.png - icons/white/64/DisplaySettings.png - icons/white/64/EmulationSettings.png - icons/white/64/EnhancementSettings.png - icons/white/64/Exit.png - icons/white/64/Fullscreen.png - icons/white/64/GameGrid.png - icons/white/64/GameList.png - icons/white/64/GamelistSettings.png - icons/white/64/GeneralSettings.png - icons/white/64/HotkeySettings.png - icons/white/64/Language.png - icons/white/64/LoadState.png - icons/white/64/LockToolbar.png - icons/white/64/MemorycardSettings.png - icons/white/64/MoveDown.png - icons/white/64/MoveUp.png - icons/white/64/Options.png - icons/white/64/Pause.png - icons/white/64/PostProcessingAdd.png - icons/white/64/PostProcessingRemove.png - icons/white/64/PostprocessingSettings.png - icons/white/64/PowerOff.png - icons/white/64/PoweroffWsaving.png - icons/white/64/RescanAllGames.png - icons/white/64/Reset.png - icons/white/64/Resume.png - icons/white/64/SaveState.png - icons/white/64/ScanForGames.png - icons/white/64/Screenshot.png - icons/white/64/StartdiscSettings.png - icons/white/64/StartfileSettings.png - icons/white/index.theme - qdarkstyle/arrow_down.png - qdarkstyle/arrow_down@2x.png - qdarkstyle/arrow_down_disabled.png - qdarkstyle/arrow_down_disabled@2x.png - qdarkstyle/arrow_down_focus.png - qdarkstyle/arrow_down_focus@2x.png - qdarkstyle/arrow_down_pressed.png - qdarkstyle/arrow_down_pressed@2x.png - qdarkstyle/arrow_left.png - qdarkstyle/arrow_left@2x.png - qdarkstyle/arrow_left_disabled.png - qdarkstyle/arrow_left_disabled@2x.png - qdarkstyle/arrow_left_focus.png - qdarkstyle/arrow_left_focus@2x.png - qdarkstyle/arrow_left_pressed.png - qdarkstyle/arrow_left_pressed@2x.png - qdarkstyle/arrow_right.png - qdarkstyle/arrow_right@2x.png - qdarkstyle/arrow_right_disabled.png - qdarkstyle/arrow_right_disabled@2x.png - qdarkstyle/arrow_right_focus.png - qdarkstyle/arrow_right_focus@2x.png - qdarkstyle/arrow_right_pressed.png - qdarkstyle/arrow_right_pressed@2x.png - qdarkstyle/arrow_up.png - qdarkstyle/arrow_up@2x.png - qdarkstyle/arrow_up_disabled.png - qdarkstyle/arrow_up_disabled@2x.png - qdarkstyle/arrow_up_focus.png - qdarkstyle/arrow_up_focus@2x.png - qdarkstyle/arrow_up_pressed.png - qdarkstyle/arrow_up_pressed@2x.png - qdarkstyle/base_icon.png - qdarkstyle/base_icon@2x.png - qdarkstyle/base_icon_disabled.png - qdarkstyle/base_icon_disabled@2x.png - qdarkstyle/base_icon_focus.png - qdarkstyle/base_icon_focus@2x.png - qdarkstyle/base_icon_pressed.png - qdarkstyle/base_icon_pressed@2x.png - qdarkstyle/branch_closed.png - qdarkstyle/branch_closed@2x.png - qdarkstyle/branch_closed_disabled.png - qdarkstyle/branch_closed_disabled@2x.png - qdarkstyle/branch_closed_focus.png - qdarkstyle/branch_closed_focus@2x.png - qdarkstyle/branch_closed_pressed.png - qdarkstyle/branch_closed_pressed@2x.png - qdarkstyle/branch_end.png - qdarkstyle/branch_end@2x.png - qdarkstyle/branch_end_disabled.png - qdarkstyle/branch_end_disabled@2x.png - qdarkstyle/branch_end_focus.png - qdarkstyle/branch_end_focus@2x.png - qdarkstyle/branch_end_pressed.png - qdarkstyle/branch_end_pressed@2x.png - qdarkstyle/branch_line.png - qdarkstyle/branch_line@2x.png - qdarkstyle/branch_line_disabled.png - qdarkstyle/branch_line_disabled@2x.png - qdarkstyle/branch_line_focus.png - qdarkstyle/branch_line_focus@2x.png - qdarkstyle/branch_line_pressed.png - qdarkstyle/branch_line_pressed@2x.png - qdarkstyle/branch_more.png - qdarkstyle/branch_more@2x.png - qdarkstyle/branch_more_disabled.png - qdarkstyle/branch_more_disabled@2x.png - qdarkstyle/branch_more_focus.png - qdarkstyle/branch_more_focus@2x.png - qdarkstyle/branch_more_pressed.png - qdarkstyle/branch_more_pressed@2x.png - qdarkstyle/branch_open.png - qdarkstyle/branch_open@2x.png - qdarkstyle/branch_open_disabled.png - qdarkstyle/branch_open_disabled@2x.png - qdarkstyle/branch_open_focus.png - qdarkstyle/branch_open_focus@2x.png - qdarkstyle/branch_open_pressed.png - qdarkstyle/branch_open_pressed@2x.png - qdarkstyle/checkbox_checked.png - qdarkstyle/checkbox_checked@2x.png - qdarkstyle/checkbox_checked_disabled.png - qdarkstyle/checkbox_checked_disabled@2x.png - qdarkstyle/checkbox_checked_focus.png - qdarkstyle/checkbox_checked_focus@2x.png - qdarkstyle/checkbox_checked_pressed.png - qdarkstyle/checkbox_checked_pressed@2x.png - qdarkstyle/checkbox_indeterminate.png - qdarkstyle/checkbox_indeterminate@2x.png - qdarkstyle/checkbox_indeterminate_disabled.png - qdarkstyle/checkbox_indeterminate_disabled@2x.png - qdarkstyle/checkbox_indeterminate_focus.png - qdarkstyle/checkbox_indeterminate_focus@2x.png - qdarkstyle/checkbox_indeterminate_pressed.png - qdarkstyle/checkbox_indeterminate_pressed@2x.png - qdarkstyle/checkbox_unchecked.png - qdarkstyle/checkbox_unchecked@2x.png - qdarkstyle/checkbox_unchecked_disabled.png - qdarkstyle/checkbox_unchecked_disabled@2x.png - qdarkstyle/checkbox_unchecked_focus.png - qdarkstyle/checkbox_unchecked_focus@2x.png - qdarkstyle/checkbox_unchecked_pressed.png - qdarkstyle/checkbox_unchecked_pressed@2x.png - qdarkstyle/line_horizontal.png - qdarkstyle/line_horizontal@2x.png - qdarkstyle/line_horizontal_disabled.png - qdarkstyle/line_horizontal_disabled@2x.png - qdarkstyle/line_horizontal_focus.png - qdarkstyle/line_horizontal_focus@2x.png - qdarkstyle/line_horizontal_pressed.png - qdarkstyle/line_horizontal_pressed@2x.png - qdarkstyle/line_vertical.png - qdarkstyle/line_vertical@2x.png - qdarkstyle/line_vertical_disabled.png - qdarkstyle/line_vertical_disabled@2x.png - qdarkstyle/line_vertical_focus.png - qdarkstyle/line_vertical_focus@2x.png - qdarkstyle/line_vertical_pressed.png - qdarkstyle/line_vertical_pressed@2x.png - qdarkstyle/radio_checked.png - qdarkstyle/radio_checked@2x.png - qdarkstyle/radio_checked_disabled.png - qdarkstyle/radio_checked_disabled@2x.png - qdarkstyle/radio_checked_focus.png - qdarkstyle/radio_checked_focus@2x.png - qdarkstyle/radio_checked_pressed.png - qdarkstyle/radio_checked_pressed@2x.png - qdarkstyle/radio_unchecked.png - qdarkstyle/radio_unchecked@2x.png - qdarkstyle/radio_unchecked_disabled.png - qdarkstyle/radio_unchecked_disabled@2x.png - qdarkstyle/radio_unchecked_focus.png - qdarkstyle/radio_unchecked_focus@2x.png - qdarkstyle/radio_unchecked_pressed.png - qdarkstyle/radio_unchecked_pressed@2x.png - qdarkstyle/style.qss - qdarkstyle/toolbar_move_horizontal.png - qdarkstyle/toolbar_move_horizontal@2x.png - qdarkstyle/toolbar_move_horizontal_disabled.png - qdarkstyle/toolbar_move_horizontal_disabled@2x.png - qdarkstyle/toolbar_move_horizontal_focus.png - qdarkstyle/toolbar_move_horizontal_focus@2x.png - qdarkstyle/toolbar_move_horizontal_pressed.png - qdarkstyle/toolbar_move_horizontal_pressed@2x.png - qdarkstyle/toolbar_move_vertical.png - qdarkstyle/toolbar_move_vertical@2x.png - qdarkstyle/toolbar_move_vertical_disabled.png - qdarkstyle/toolbar_move_vertical_disabled@2x.png - qdarkstyle/toolbar_move_vertical_focus.png - qdarkstyle/toolbar_move_vertical_focus@2x.png - qdarkstyle/toolbar_move_vertical_pressed.png - qdarkstyle/toolbar_move_vertical_pressed@2x.png - qdarkstyle/toolbar_separator_horizontal.png - qdarkstyle/toolbar_separator_horizontal@2x.png - qdarkstyle/toolbar_separator_horizontal_disabled.png - qdarkstyle/toolbar_separator_horizontal_disabled@2x.png - qdarkstyle/toolbar_separator_horizontal_focus.png - qdarkstyle/toolbar_separator_horizontal_focus@2x.png - qdarkstyle/toolbar_separator_horizontal_pressed.png - qdarkstyle/toolbar_separator_horizontal_pressed@2x.png - qdarkstyle/toolbar_separator_vertical.png - qdarkstyle/toolbar_separator_vertical@2x.png - qdarkstyle/toolbar_separator_vertical_disabled.png - qdarkstyle/toolbar_separator_vertical_disabled@2x.png - qdarkstyle/toolbar_separator_vertical_focus.png - qdarkstyle/toolbar_separator_vertical_focus@2x.png - qdarkstyle/toolbar_separator_vertical_pressed.png - qdarkstyle/toolbar_separator_vertical_pressed@2x.png - qdarkstyle/transparent.png - qdarkstyle/transparent@2x.png - qdarkstyle/transparent_disabled.png - qdarkstyle/transparent_disabled@2x.png - qdarkstyle/transparent_focus.png - qdarkstyle/transparent_focus@2x.png - qdarkstyle/transparent_pressed.png - qdarkstyle/transparent_pressed@2x.png - qdarkstyle/window_close.png - qdarkstyle/window_close@2x.png - qdarkstyle/window_close_disabled.png - qdarkstyle/window_close_disabled@2x.png - qdarkstyle/window_close_focus.png - qdarkstyle/window_close_focus@2x.png - qdarkstyle/window_close_pressed.png - qdarkstyle/window_close_pressed@2x.png - qdarkstyle/window_grip.png - qdarkstyle/window_grip@2x.png - qdarkstyle/window_grip_disabled.png - qdarkstyle/window_grip_disabled@2x.png - qdarkstyle/window_grip_focus.png - qdarkstyle/window_grip_focus@2x.png - qdarkstyle/window_grip_pressed.png - qdarkstyle/window_grip_pressed@2x.png - qdarkstyle/window_minimize.png - qdarkstyle/window_minimize@2x.png - qdarkstyle/window_minimize_disabled.png - qdarkstyle/window_minimize_disabled@2x.png - qdarkstyle/window_minimize_focus.png - qdarkstyle/window_minimize_focus@2x.png - qdarkstyle/window_minimize_pressed.png - qdarkstyle/window_minimize_pressed@2x.png - qdarkstyle/window_undock.png - qdarkstyle/window_undock@2x.png - qdarkstyle/window_undock_disabled.png - qdarkstyle/window_undock_disabled@2x.png - qdarkstyle/window_undock_focus.png - qdarkstyle/window_undock_focus@2x.png - qdarkstyle/window_undock_pressed.png - qdarkstyle/window_undock_pressed@2x.png - + + controllers/analog_controller.svg + controllers/digital_controller.svg + controllers/guncon.svg + controllers/negcon.svg + icons/address-book-new-22.png + icons/address-book-new-22@2x.png + icons/antialias-icon.png + icons/antialias-icon@2x.png + icons/applications-development.png + icons/applications-development@2x.png + icons/applications-graphics.png + icons/applications-graphics@2x.png + icons/applications-internet.png + icons/applications-other.png + icons/applications-other@2x.png + icons/applications-system-24.png + icons/applications-system-24@2x.png + icons/applications-system.png + icons/applications-system@2x.png + icons/audio-card.png + icons/audio-card@2x.png + icons/black/16/AchievementsSettings.png + icons/black/16/AddGameDirectory.png + icons/black/16/AdvancedSettings.png + icons/black/16/AudioSettings.png + icons/black/16/BIOSSettings.png + icons/black/16/ChangeDisc.png + icons/black/16/Cheats.png + icons/black/16/Clear.png + icons/black/16/ConsoleSettings.png + icons/black/16/ControllerSettings.png + icons/black/16/DisplaySettings.png + icons/black/16/EmulationSettings.png + icons/black/16/EnhancementSettings.png + icons/black/16/Exit.png + icons/black/16/Fullscreen.png + icons/black/16/GameGrid.png + icons/black/16/GameList.png + icons/black/16/GamelistSettings.png + icons/black/16/GeneralSettings.png + icons/black/16/HotkeySettings.png + icons/black/16/Language.png + icons/black/16/LoadState.png + icons/black/16/LockToolbar.png + icons/black/16/MemorycardSettings.png + icons/black/16/MoveDown.png + icons/black/16/MoveUp.png + icons/black/16/Options.png + icons/black/16/Pause.png + icons/black/16/PostProcessingAdd.png + icons/black/16/PostProcessingRemove.png + icons/black/16/PostprocessingSettings.png + icons/black/16/PowerOff.png + icons/black/16/PoweroffWsaving.png + icons/black/16/RescanAllGames.png + icons/black/16/Reset.png + icons/black/16/Resume.png + icons/black/16/SaveState.png + icons/black/16/ScanForGames.png + icons/black/16/Screenshot.png + icons/black/16/StartdiscSettings.png + icons/black/16/StartfileSettings.png + icons/black/32/AchievementsSettings.png + icons/black/32/AddGameDirectory.png + icons/black/32/AdvancedSettings.png + icons/black/32/AudioSettings.png + icons/black/32/BIOSSettings.png + icons/black/32/ChangeDisc.png + icons/black/32/Cheats.png + icons/black/32/Clear.png + icons/black/32/ConsoleSettings.png + icons/black/32/ControllerSettings.png + icons/black/32/DisplaySettings.png + icons/black/32/EmulationSettings.png + icons/black/32/EnhancementSettings.png + icons/black/32/Exit.png + icons/black/32/Fullscreen.png + icons/black/32/GameGrid.png + icons/black/32/GameList.png + icons/black/32/GamelistSettings.png + icons/black/32/GeneralSettings.png + icons/black/32/HotkeySettings.png + icons/black/32/Language.png + icons/black/32/LoadState.png + icons/black/32/LockToolbar.png + icons/black/32/MemorycardSettings.png + icons/black/32/MoveDown.png + icons/black/32/MoveUp.png + icons/black/32/Options.png + icons/black/32/Pause.png + icons/black/32/PostProcessingAdd.png + icons/black/32/PostProcessingRemove.png + icons/black/32/PostprocessingSettings.png + icons/black/32/PowerOff.png + icons/black/32/PoweroffWsaving.png + icons/black/32/RescanAllGames.png + icons/black/32/Reset.png + icons/black/32/Resume.png + icons/black/32/SaveState.png + icons/black/32/ScanForGames.png + icons/black/32/Screenshot.png + icons/black/32/StartdiscSettings.png + icons/black/32/StartfileSettings.png + icons/black/64/AchievementsSettings.png + icons/black/64/AddGameDirectory.png + icons/black/64/AdvancedSettings.png + icons/black/64/AudioSettings.png + icons/black/64/BIOSSettings.png + icons/black/64/ChangeDisc.png + icons/black/64/Cheats.png + icons/black/64/Clear.png + icons/black/64/ConsoleSettings.png + icons/black/64/ControllerSettings.png + icons/black/64/DisplaySettings.png + icons/black/64/EmulationSettings.png + icons/black/64/EnhancementSettings.png + icons/black/64/Exit.png + icons/black/64/Fullscreen.png + icons/black/64/GameGrid.png + icons/black/64/GameList.png + icons/black/64/GamelistSettings.png + icons/black/64/GeneralSettings.png + icons/black/64/HotkeySettings.png + icons/black/64/Language.png + icons/black/64/LoadState.png + icons/black/64/LockToolbar.png + icons/black/64/MemorycardSettings.png + icons/black/64/MoveDown.png + icons/black/64/MoveUp.png + icons/black/64/Options.png + icons/black/64/Pause.png + icons/black/64/PostProcessingAdd.png + icons/black/64/PostProcessingRemove.png + icons/black/64/PostprocessingSettings.png + icons/black/64/PowerOff.png + icons/black/64/PoweroffWsaving.png + icons/black/64/RescanAllGames.png + icons/black/64/Reset.png + icons/black/64/Resume.png + icons/black/64/SaveState.png + icons/black/64/ScanForGames.png + icons/black/64/Screenshot.png + icons/black/64/StartdiscSettings.png + icons/black/64/StartfileSettings.png + icons/black/index.theme + icons/black/svg/arrow-left-right-line.svg + icons/black/svg/artboard-2-line.svg + icons/black/svg/book-open-line.svg + icons/black/svg/brush-line.svg + icons/black/svg/close-line.svg + icons/black/svg/dashboard-line.svg + icons/black/svg/disc-line.svg + icons/black/svg/door-open-line.svg + icons/black/svg/download-2-line.svg + icons/black/svg/dvd-line.svg + icons/black/svg/eject-line.svg + icons/black/svg/file-add-line.svg + icons/black/svg/file-line.svg + icons/black/svg/file-list-line.svg + icons/black/svg/file-reduce-line.svg + icons/black/svg/file-search-line.svg + icons/black/svg/file-settings-line.svg + icons/black/svg/flask-line.svg + icons/black/svg/folder-add-line.svg + icons/black/svg/folder-open-line.svg + icons/black/svg/folder-reduce-line.svg + icons/black/svg/folder-settings-line.svg + icons/black/svg/fullscreen-line.svg + icons/black/svg/function-line.svg + icons/black/svg/gamepad-line.svg + icons/black/svg/hard-drive-2-line.svg + icons/black/svg/keyboard-line.svg + icons/black/svg/layout-grid-line.svg + icons/black/svg/list-check.svg + icons/black/svg/pause-line.svg + icons/black/svg/play-line.svg + icons/black/svg/refresh-line.svg + icons/black/svg/restart-line.svg + icons/black/svg/save-3-line.svg + icons/black/svg/screenshot-2-line.svg + icons/black/svg/sd-card-line.svg + icons/black/svg/settings-3-line.svg + icons/black/svg/shut-down-line.svg + icons/black/svg/tv-2-line.svg + icons/black/svg/volume-up-line.svg + icons/black/svg/window-2-line.svg + icons/camera-photo.png + icons/camera-photo@2x.png + icons/camera-video.png + icons/camera-video@2x.png + icons/conical-flask-red.png + icons/conical-flask-red@2x.png + icons/cover-placeholder.png + icons/debug-execute-from-cursor.png + icons/debug-execute-to-cursor.png + icons/debug-pc.png + icons/debug-pc@2x.png + icons/debug-run-cursor.png + icons/debug-run.png + icons/debug-step-instruction.png + icons/debug-step-into-instruction.png + icons/debug-step-into.png + icons/debug-step-out.png + icons/debug-step-over.png + icons/debug-trace.png + icons/discord.png + icons/document-new.png + icons/document-new@2x.png + icons/document-open.png + icons/document-open@2x.png + icons/document-save.png + icons/document-save@2x.png + icons/drive-optical.png + icons/drive-optical@2x.png + icons/drive-removable-media.png + icons/drive-removable-media@2x.png + icons/duck.png + icons/duck_128.png + icons/duck_64.png + icons/edit-clear-16.png + icons/edit-clear-16@2x.png + icons/edit-find.png + icons/emblem-person-blue.png + icons/emblem-person-blue@2x.png + icons/flag-eu.png + icons/flag-eu.svg + icons/flag-eu@2x.png + icons/flag-jp.png + icons/flag-jp.svg + icons/flag-jp@2x.png + icons/flag-other.png + icons/flag-other@2x.png + icons/flag-uc.png + icons/flag-uc.svg + icons/flag-uc@2x.png + icons/flag-us.png + icons/flag-us.svg + icons/flag-us@2x.png + icons/flags/de.png + icons/flags/de@2x.png + icons/flags/en.png + icons/flags/en@2x.png + icons/flags/es.png + icons/flags/es@2x.png + icons/flags/fr.png + icons/flags/fr@2x.png + icons/flags/he.png + icons/flags/he@2x.png + icons/flags/it.png + icons/flags/it@2x.png + icons/flags/ja.png + icons/flags/ja@2x.png + icons/flags/nl.png + icons/flags/nl@2x.png + icons/flags/pl.png + icons/flags/pl@2x.png + icons/flags/pt-br.png + icons/flags/pt-br@2x.png + icons/flags/pt-pt.png + icons/flags/pt-pt@2x.png + icons/flags/ru.png + icons/flags/ru@2x.png + icons/flags/tr.png + icons/flags/tr@2x.png + icons/flags/zh-cn.png + icons/flags/zh-cn@2x.png + icons/folder-open.png + icons/folder-open@2x.png + icons/github.png + icons/go-down-16.png + icons/go-down-16@2x.png + icons/go-up-16.png + icons/go-up-16@2x.png + icons/input-gaming.png + icons/input-gaming@2x.png + icons/IssueTracker.png + icons/list-add.png + icons/list-add@2x.png + icons/list-remove.png + icons/list-remove@2x.png + icons/media-flash-2.png + icons/media-flash-24.png + icons/media-flash-24@2x.png + icons/media-flash-2@2x.png + icons/media-flash.png + icons/media-flash@2x.png + icons/media-optical-24.png + icons/media-optical-24@2x.png + icons/media-optical-gear-24.png + icons/media-optical-gear-24@2x.png + icons/media-optical.png + icons/media-optical@2x.png + icons/media-playback-pause.png + icons/media-playback-pause@2x.png + icons/media-playback-start.png + icons/media-playback-start@2x.png + icons/media-record.png + icons/media-record@2x.png + icons/multimedia-player.png + icons/multimedia-player@2x.png + icons/preferences-desktop-keyboard-shortcuts.png + icons/preferences-desktop-keyboard-shortcuts@2x.png + icons/preferences-system.png + icons/preferences-system@2x.png + icons/process-stop.png + icons/process-stop@2x.png + icons/QT.png + icons/software-update-available.png + icons/software-update-available@2x.png + icons/star-0.png + icons/star-1.png + icons/star-2.png + icons/star-3.png + icons/star-4.png + icons/star-5.png + icons/system-file-manager.png + icons/system-file-manager@2x.png + icons/system-search.png + icons/system-search@2x.png + icons/system-shutdown.png + icons/system-shutdown@2x.png + icons/trophy.png + icons/trophy@2x.png + icons/update.png + icons/UpdateDuck.png + icons/utilities-system-monitor.png + icons/utilities-system-monitor@2x.png + icons/video-display.png + icons/video-display@2x.png + icons/view-fullscreen.png + icons/view-fullscreen@2x.png + icons/view-refresh.png + icons/view-refresh@2x.png + icons/white/16/AchievementsSettings.png + icons/white/16/AddGameDirectory.png + icons/white/16/AdvancedSettings.png + icons/white/16/AudioSettings.png + icons/white/16/BIOSSettings.png + icons/white/16/ChangeDisc.png + icons/white/16/Cheats.png + icons/white/16/Clear.png + icons/white/16/ConsoleSettings.png + icons/white/16/ControllerSettings.png + icons/white/16/DisplaySettings.png + icons/white/16/EmulationSettings.png + icons/white/16/EnhancementSettings.png + icons/white/16/Exit.png + icons/white/16/Fullscreen.png + icons/white/16/GameGrid.png + icons/white/16/GameList.png + icons/white/16/GamelistSettings.png + icons/white/16/GeneralSettings.png + icons/white/16/HotkeySettings.png + icons/white/16/Language.png + icons/white/16/LoadState.png + icons/white/16/LockToolbar.png + icons/white/16/MemorycardSettings.png + icons/white/16/MoveDown.png + icons/white/16/MoveUp.png + icons/white/16/Options.png + icons/white/16/Pause.png + icons/white/16/PostProcessingAdd.png + icons/white/16/PostProcessingRemove.png + icons/white/16/PostprocessingSettings.png + icons/white/16/PowerOff.png + icons/white/16/PoweroffWsaving.png + icons/white/16/RescanAllGames.png + icons/white/16/Reset.png + icons/white/16/Resume.png + icons/white/16/SaveState.png + icons/white/16/ScanForGames.png + icons/white/16/Screenshot.png + icons/white/16/StartdiscSettings.png + icons/white/16/StartfileSettings.png + icons/white/32/AchievementsSettings.png + icons/white/32/AddGameDirectory.png + icons/white/32/AdvancedSettings.png + icons/white/32/AudioSettings.png + icons/white/32/BIOSSettings.png + icons/white/32/ChangeDisc.png + icons/white/32/Cheats.png + icons/white/32/Clear.png + icons/white/32/ConsoleSettings.png + icons/white/32/ControllerSettings.png + icons/white/32/DisplaySettings.png + icons/white/32/EmulationSettings.png + icons/white/32/EnhancementSettings.png + icons/white/32/Exit.png + icons/white/32/Fullscreen.png + icons/white/32/GameGrid.png + icons/white/32/GameList.png + icons/white/32/GamelistSettings.png + icons/white/32/GeneralSettings.png + icons/white/32/HotkeySettings.png + icons/white/32/Language.png + icons/white/32/LoadState.png + icons/white/32/LockToolbar.png + icons/white/32/MemorycardSettings.png + icons/white/32/MoveDown.png + icons/white/32/MoveUp.png + icons/white/32/Options.png + icons/white/32/Pause.png + icons/white/32/PostProcessingAdd.png + icons/white/32/PostProcessingRemove.png + icons/white/32/PostprocessingSettings.png + icons/white/32/PowerOff.png + icons/white/32/PoweroffWsaving.png + icons/white/32/RescanAllGames.png + icons/white/32/Reset.png + icons/white/32/Resume.png + icons/white/32/SaveState.png + icons/white/32/ScanForGames.png + icons/white/32/Screenshot.png + icons/white/32/StartdiscSettings.png + icons/white/32/StartfileSettings.png + icons/white/64/AchievementsSettings.png + icons/white/64/AddGameDirectory.png + icons/white/64/AdvancedSettings.png + icons/white/64/AudioSettings.png + icons/white/64/BIOSSettings.png + icons/white/64/ChangeDisc.png + icons/white/64/Cheats.png + icons/white/64/Clear.png + icons/white/64/ConsoleSettings.png + icons/white/64/ControllerSettings.png + icons/white/64/DisplaySettings.png + icons/white/64/EmulationSettings.png + icons/white/64/EnhancementSettings.png + icons/white/64/Exit.png + icons/white/64/Fullscreen.png + icons/white/64/GameGrid.png + icons/white/64/GameList.png + icons/white/64/GamelistSettings.png + icons/white/64/GeneralSettings.png + icons/white/64/HotkeySettings.png + icons/white/64/Language.png + icons/white/64/LoadState.png + icons/white/64/LockToolbar.png + icons/white/64/MemorycardSettings.png + icons/white/64/MoveDown.png + icons/white/64/MoveUp.png + icons/white/64/Options.png + icons/white/64/Pause.png + icons/white/64/PostProcessingAdd.png + icons/white/64/PostProcessingRemove.png + icons/white/64/PostprocessingSettings.png + icons/white/64/PowerOff.png + icons/white/64/PoweroffWsaving.png + icons/white/64/RescanAllGames.png + icons/white/64/Reset.png + icons/white/64/Resume.png + icons/white/64/SaveState.png + icons/white/64/ScanForGames.png + icons/white/64/Screenshot.png + icons/white/64/StartdiscSettings.png + icons/white/64/StartfileSettings.png + icons/white/index.theme + icons/white/svg/arrow-left-right-line.svg + icons/white/svg/artboard-2-line.svg + icons/white/svg/book-open-line.svg + icons/white/svg/brush-line.svg + icons/white/svg/close-line.svg + icons/white/svg/dashboard-line.svg + icons/white/svg/disc-line.svg + icons/white/svg/door-open-line.svg + icons/white/svg/download-2-line.svg + icons/white/svg/dvd-line.svg + icons/white/svg/eject-line.svg + icons/white/svg/file-add-line.svg + icons/white/svg/file-line.svg + icons/white/svg/file-list-line.svg + icons/white/svg/file-reduce-line.svg + icons/white/svg/file-search-line.svg + icons/white/svg/file-settings-line.svg + icons/white/svg/flask-line.svg + icons/white/svg/folder-add-line.svg + icons/white/svg/folder-open-line.svg + icons/white/svg/folder-reduce-line.svg + icons/white/svg/folder-settings-line.svg + icons/white/svg/fullscreen-line.svg + icons/white/svg/function-line.svg + icons/white/svg/gamepad-line.svg + icons/white/svg/hard-drive-2-line.svg + icons/white/svg/keyboard-line.svg + icons/white/svg/layout-grid-line.svg + icons/white/svg/list-check.svg + icons/white/svg/pause-line.svg + icons/white/svg/play-line.svg + icons/white/svg/refresh-line.svg + icons/white/svg/restart-line.svg + icons/white/svg/save-3-line.svg + icons/white/svg/screenshot-2-line.svg + icons/white/svg/sd-card-line.svg + icons/white/svg/settings-3-line.svg + icons/white/svg/shut-down-line.svg + icons/white/svg/tv-2-line.svg + icons/white/svg/volume-up-line.svg + icons/white/svg/window-2-line.svg + qdarkstyle/arrow_down.png + qdarkstyle/arrow_down@2x.png + qdarkstyle/arrow_down_disabled.png + qdarkstyle/arrow_down_disabled@2x.png + qdarkstyle/arrow_down_focus.png + qdarkstyle/arrow_down_focus@2x.png + qdarkstyle/arrow_down_pressed.png + qdarkstyle/arrow_down_pressed@2x.png + qdarkstyle/arrow_left.png + qdarkstyle/arrow_left@2x.png + qdarkstyle/arrow_left_disabled.png + qdarkstyle/arrow_left_disabled@2x.png + qdarkstyle/arrow_left_focus.png + qdarkstyle/arrow_left_focus@2x.png + qdarkstyle/arrow_left_pressed.png + qdarkstyle/arrow_left_pressed@2x.png + qdarkstyle/arrow_right.png + qdarkstyle/arrow_right@2x.png + qdarkstyle/arrow_right_disabled.png + qdarkstyle/arrow_right_disabled@2x.png + qdarkstyle/arrow_right_focus.png + qdarkstyle/arrow_right_focus@2x.png + qdarkstyle/arrow_right_pressed.png + qdarkstyle/arrow_right_pressed@2x.png + qdarkstyle/arrow_up.png + qdarkstyle/arrow_up@2x.png + qdarkstyle/arrow_up_disabled.png + qdarkstyle/arrow_up_disabled@2x.png + qdarkstyle/arrow_up_focus.png + qdarkstyle/arrow_up_focus@2x.png + qdarkstyle/arrow_up_pressed.png + qdarkstyle/arrow_up_pressed@2x.png + qdarkstyle/base_icon.png + qdarkstyle/base_icon@2x.png + qdarkstyle/base_icon_disabled.png + qdarkstyle/base_icon_disabled@2x.png + qdarkstyle/base_icon_focus.png + qdarkstyle/base_icon_focus@2x.png + qdarkstyle/base_icon_pressed.png + qdarkstyle/base_icon_pressed@2x.png + qdarkstyle/branch_closed.png + qdarkstyle/branch_closed@2x.png + qdarkstyle/branch_closed_disabled.png + qdarkstyle/branch_closed_disabled@2x.png + qdarkstyle/branch_closed_focus.png + qdarkstyle/branch_closed_focus@2x.png + qdarkstyle/branch_closed_pressed.png + qdarkstyle/branch_closed_pressed@2x.png + qdarkstyle/branch_end.png + qdarkstyle/branch_end@2x.png + qdarkstyle/branch_end_disabled.png + qdarkstyle/branch_end_disabled@2x.png + qdarkstyle/branch_end_focus.png + qdarkstyle/branch_end_focus@2x.png + qdarkstyle/branch_end_pressed.png + qdarkstyle/branch_end_pressed@2x.png + qdarkstyle/branch_line.png + qdarkstyle/branch_line@2x.png + qdarkstyle/branch_line_disabled.png + qdarkstyle/branch_line_disabled@2x.png + qdarkstyle/branch_line_focus.png + qdarkstyle/branch_line_focus@2x.png + qdarkstyle/branch_line_pressed.png + qdarkstyle/branch_line_pressed@2x.png + qdarkstyle/branch_more.png + qdarkstyle/branch_more@2x.png + qdarkstyle/branch_more_disabled.png + qdarkstyle/branch_more_disabled@2x.png + qdarkstyle/branch_more_focus.png + qdarkstyle/branch_more_focus@2x.png + qdarkstyle/branch_more_pressed.png + qdarkstyle/branch_more_pressed@2x.png + qdarkstyle/branch_open.png + qdarkstyle/branch_open@2x.png + qdarkstyle/branch_open_disabled.png + qdarkstyle/branch_open_disabled@2x.png + qdarkstyle/branch_open_focus.png + qdarkstyle/branch_open_focus@2x.png + qdarkstyle/branch_open_pressed.png + qdarkstyle/branch_open_pressed@2x.png + qdarkstyle/checkbox_checked.png + qdarkstyle/checkbox_checked@2x.png + qdarkstyle/checkbox_checked_disabled.png + qdarkstyle/checkbox_checked_disabled@2x.png + qdarkstyle/checkbox_checked_focus.png + qdarkstyle/checkbox_checked_focus@2x.png + qdarkstyle/checkbox_checked_pressed.png + qdarkstyle/checkbox_checked_pressed@2x.png + qdarkstyle/checkbox_indeterminate.png + qdarkstyle/checkbox_indeterminate@2x.png + qdarkstyle/checkbox_indeterminate_disabled.png + qdarkstyle/checkbox_indeterminate_disabled@2x.png + qdarkstyle/checkbox_indeterminate_focus.png + qdarkstyle/checkbox_indeterminate_focus@2x.png + qdarkstyle/checkbox_indeterminate_pressed.png + qdarkstyle/checkbox_indeterminate_pressed@2x.png + qdarkstyle/checkbox_unchecked.png + qdarkstyle/checkbox_unchecked@2x.png + qdarkstyle/checkbox_unchecked_disabled.png + qdarkstyle/checkbox_unchecked_disabled@2x.png + qdarkstyle/checkbox_unchecked_focus.png + qdarkstyle/checkbox_unchecked_focus@2x.png + qdarkstyle/checkbox_unchecked_pressed.png + qdarkstyle/checkbox_unchecked_pressed@2x.png + qdarkstyle/line_horizontal.png + qdarkstyle/line_horizontal@2x.png + qdarkstyle/line_horizontal_disabled.png + qdarkstyle/line_horizontal_disabled@2x.png + qdarkstyle/line_horizontal_focus.png + qdarkstyle/line_horizontal_focus@2x.png + qdarkstyle/line_horizontal_pressed.png + qdarkstyle/line_horizontal_pressed@2x.png + qdarkstyle/line_vertical.png + qdarkstyle/line_vertical@2x.png + qdarkstyle/line_vertical_disabled.png + qdarkstyle/line_vertical_disabled@2x.png + qdarkstyle/line_vertical_focus.png + qdarkstyle/line_vertical_focus@2x.png + qdarkstyle/line_vertical_pressed.png + qdarkstyle/line_vertical_pressed@2x.png + qdarkstyle/radio_checked.png + qdarkstyle/radio_checked@2x.png + qdarkstyle/radio_checked_disabled.png + qdarkstyle/radio_checked_disabled@2x.png + qdarkstyle/radio_checked_focus.png + qdarkstyle/radio_checked_focus@2x.png + qdarkstyle/radio_checked_pressed.png + qdarkstyle/radio_checked_pressed@2x.png + qdarkstyle/radio_unchecked.png + qdarkstyle/radio_unchecked@2x.png + qdarkstyle/radio_unchecked_disabled.png + qdarkstyle/radio_unchecked_disabled@2x.png + qdarkstyle/radio_unchecked_focus.png + qdarkstyle/radio_unchecked_focus@2x.png + qdarkstyle/radio_unchecked_pressed.png + qdarkstyle/radio_unchecked_pressed@2x.png + qdarkstyle/style.qss + qdarkstyle/toolbar_move_horizontal.png + qdarkstyle/toolbar_move_horizontal@2x.png + qdarkstyle/toolbar_move_horizontal_disabled.png + qdarkstyle/toolbar_move_horizontal_disabled@2x.png + qdarkstyle/toolbar_move_horizontal_focus.png + qdarkstyle/toolbar_move_horizontal_focus@2x.png + qdarkstyle/toolbar_move_horizontal_pressed.png + qdarkstyle/toolbar_move_horizontal_pressed@2x.png + qdarkstyle/toolbar_move_vertical.png + qdarkstyle/toolbar_move_vertical@2x.png + qdarkstyle/toolbar_move_vertical_disabled.png + qdarkstyle/toolbar_move_vertical_disabled@2x.png + qdarkstyle/toolbar_move_vertical_focus.png + qdarkstyle/toolbar_move_vertical_focus@2x.png + qdarkstyle/toolbar_move_vertical_pressed.png + qdarkstyle/toolbar_move_vertical_pressed@2x.png + qdarkstyle/toolbar_separator_horizontal.png + qdarkstyle/toolbar_separator_horizontal@2x.png + qdarkstyle/toolbar_separator_horizontal_disabled.png + qdarkstyle/toolbar_separator_horizontal_disabled@2x.png + qdarkstyle/toolbar_separator_horizontal_focus.png + qdarkstyle/toolbar_separator_horizontal_focus@2x.png + qdarkstyle/toolbar_separator_horizontal_pressed.png + qdarkstyle/toolbar_separator_horizontal_pressed@2x.png + qdarkstyle/toolbar_separator_vertical.png + qdarkstyle/toolbar_separator_vertical@2x.png + qdarkstyle/toolbar_separator_vertical_disabled.png + qdarkstyle/toolbar_separator_vertical_disabled@2x.png + qdarkstyle/toolbar_separator_vertical_focus.png + qdarkstyle/toolbar_separator_vertical_focus@2x.png + qdarkstyle/toolbar_separator_vertical_pressed.png + qdarkstyle/toolbar_separator_vertical_pressed@2x.png + qdarkstyle/transparent.png + qdarkstyle/transparent@2x.png + qdarkstyle/transparent_disabled.png + qdarkstyle/transparent_disabled@2x.png + qdarkstyle/transparent_focus.png + qdarkstyle/transparent_focus@2x.png + qdarkstyle/transparent_pressed.png + qdarkstyle/transparent_pressed@2x.png + qdarkstyle/window_close.png + qdarkstyle/window_close@2x.png + qdarkstyle/window_close_disabled.png + qdarkstyle/window_close_disabled@2x.png + qdarkstyle/window_close_focus.png + qdarkstyle/window_close_focus@2x.png + qdarkstyle/window_close_pressed.png + qdarkstyle/window_close_pressed@2x.png + qdarkstyle/window_grip.png + qdarkstyle/window_grip@2x.png + qdarkstyle/window_grip_disabled.png + qdarkstyle/window_grip_disabled@2x.png + qdarkstyle/window_grip_focus.png + qdarkstyle/window_grip_focus@2x.png + qdarkstyle/window_grip_pressed.png + qdarkstyle/window_grip_pressed@2x.png + qdarkstyle/window_minimize.png + qdarkstyle/window_minimize@2x.png + qdarkstyle/window_minimize_disabled.png + qdarkstyle/window_minimize_disabled@2x.png + qdarkstyle/window_minimize_focus.png + qdarkstyle/window_minimize_focus@2x.png + qdarkstyle/window_minimize_pressed.png + qdarkstyle/window_minimize_pressed@2x.png + qdarkstyle/window_undock.png + qdarkstyle/window_undock@2x.png + qdarkstyle/window_undock_disabled.png + qdarkstyle/window_undock_disabled@2x.png + qdarkstyle/window_undock_focus.png + qdarkstyle/window_undock_focus@2x.png + qdarkstyle/window_undock_pressed.png + qdarkstyle/window_undock_pressed@2x.png + diff --git a/src/duckstation-qt/settingsdialog.cpp b/src/duckstation-qt/settingsdialog.cpp index 00a5c2aac..8905897b1 100644 --- a/src/duckstation-qt/settingsdialog.cpp +++ b/src/duckstation-qt/settingsdialog.cpp @@ -351,8 +351,8 @@ void SettingsDialog::setBoolSettingValue(const char* section, const char* key, s } else { - value.has_value() ? QtHost::SetBaseBoolSettingValue(section, key, value.value()) : - QtHost::RemoveBaseSettingValue(section, key); + value.has_value() ? Host::SetBaseBoolSettingValue(section, key, value.value()) : + Host::DeleteBaseSettingValue(section, key); QtHostInterface::GetInstance()->applySettings(); } } @@ -367,8 +367,8 @@ void SettingsDialog::setIntSettingValue(const char* section, const char* key, st } else { - value.has_value() ? QtHost::SetBaseIntSettingValue(section, key, value.value()) : - QtHost::RemoveBaseSettingValue(section, key); + value.has_value() ? Host::SetBaseIntSettingValue(section, key, value.value()) : + Host::DeleteBaseSettingValue(section, key); QtHostInterface::GetInstance()->applySettings(); } } @@ -383,8 +383,8 @@ void SettingsDialog::setFloatSettingValue(const char* section, const char* key, } else { - value.has_value() ? QtHost::SetBaseFloatSettingValue(section, key, value.value()) : - QtHost::RemoveBaseSettingValue(section, key); + value.has_value() ? Host::SetBaseFloatSettingValue(section, key, value.value()) : + Host::DeleteBaseSettingValue(section, key); QtHostInterface::GetInstance()->applySettings(); } } @@ -399,8 +399,8 @@ void SettingsDialog::setStringSettingValue(const char* section, const char* key, } else { - value.has_value() ? QtHost::SetBaseStringSettingValue(section, key, value.value()) : - QtHost::RemoveBaseSettingValue(section, key); + value.has_value() ? Host::SetBaseStringSettingValue(section, key, value.value()) : + Host::DeleteBaseSettingValue(section, key); QtHostInterface::GetInstance()->applySettings(); } } @@ -415,7 +415,7 @@ void SettingsDialog::removeSettingValue(const char* section, const char* key) } else { - QtHost::RemoveBaseSettingValue(section, key); + Host::DeleteBaseSettingValue(section, key); QtHostInterface::GetInstance()->applySettings(); } } diff --git a/src/duckstation-qt/settingwidgetbinder.h b/src/duckstation-qt/settingwidgetbinder.h index 5fafd6f9a..8fe1a888e 100644 --- a/src/duckstation-qt/settingwidgetbinder.h +++ b/src/duckstation-qt/settingwidgetbinder.h @@ -475,7 +475,7 @@ static void BindWidgetToBoolSetting(SettingsInterface* sif, WidgetType* widget, Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() { const bool new_value = Accessor::getBoolValue(widget); - QtHost::SetBaseBoolSettingValue(section.c_str(), key.c_str(), new_value); + Host::SetBaseBoolSettingValue(section.c_str(), key.c_str(), new_value); QtHostInterface::GetInstance()->applySettings(); }); } @@ -518,7 +518,7 @@ static void BindWidgetToIntSetting(SettingsInterface* sif, WidgetType* widget, s Accessor::connectValueChanged( widget, [widget, section = std::move(section), key = std::move(key), option_offset]() { const int new_value = Accessor::getIntValue(widget); - QtHost::SetBaseIntSettingValue(section.c_str(), key.c_str(), new_value + option_offset); + Host::SetBaseIntSettingValue(section.c_str(), key.c_str(), new_value + option_offset); QtHostInterface::GetInstance()->applySettings(); }); } @@ -558,7 +558,7 @@ static void BindWidgetToFloatSetting(SettingsInterface* sif, WidgetType* widget, Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() { const float new_value = Accessor::getFloatValue(widget); - QtHost::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value); + Host::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value); QtHostInterface::GetInstance()->applySettings(); }); } @@ -598,7 +598,7 @@ static void BindWidgetToNormalizedSetting(SettingsInterface* sif, WidgetType* wi Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key), range]() { const float new_value = (static_cast(Accessor::getIntValue(widget)) / range); - QtHost::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value); + Host::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value); QtHostInterface::GetInstance()->applySettings(); }); } @@ -640,9 +640,9 @@ static void BindWidgetToStringSetting(SettingsInterface* sif, WidgetType* widget Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() { const QString new_value = Accessor::getStringValue(widget); if (!new_value.isEmpty()) - QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), new_value.toUtf8().constData()); + Host::SetBaseStringSettingValue(section.c_str(), key.c_str(), new_value.toUtf8().constData()); else - QtHost::RemoveBaseSettingValue(section.c_str(), key.c_str()); + Host::DeleteBaseSettingValue(section.c_str(), key.c_str()); QtHostInterface::GetInstance()->applySettings(); }); @@ -708,7 +708,7 @@ static void BindWidgetToEnumSetting(SettingsInterface* sif, WidgetType* widget, widget, [widget, section = std::move(section), key = std::move(key), to_string_function]() { const DataType value = static_cast(static_cast(Accessor::getIntValue(widget))); const char* string_value = to_string_function(value); - QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), string_value); + Host::SetBaseStringSettingValue(section.c_str(), key.c_str(), string_value); QtHostInterface::GetInstance()->applySettings(); }); } @@ -770,7 +770,7 @@ static void BindWidgetToEnumSetting(SettingsInterface* sif, WidgetType* widget, Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key), enum_names]() { const UnderlyingType value = static_cast(Accessor::getIntValue(widget)); - QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), enum_names[value]); + Host::SetBaseStringSettingValue(section.c_str(), key.c_str(), enum_names[value]); QtHostInterface::GetInstance()->applySettings(); }); } @@ -834,7 +834,7 @@ static void BindWidgetToEnumSetting(SettingsInterface* sif, WidgetType* widget, Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key), enum_values]() { const int value = Accessor::getIntValue(widget); - QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), enum_values[value]); + Host::SetBaseStringSettingValue(section.c_str(), key.c_str(), enum_values[value]); QtHostInterface::GetInstance()->applySettings(); }); } @@ -870,11 +870,11 @@ static void BindWidgetToFolderSetting(SettingsInterface* sif, WidgetType* widget if (!new_value.empty()) { std::string relative_path(Path::MakeRelative(new_value, EmuFolders::DataRoot)); - QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), relative_path.c_str()); + Host::SetBaseStringSettingValue(section.c_str(), key.c_str(), relative_path.c_str()); } else { - QtHost::RemoveBaseSettingValue(section.c_str(), key.c_str()); + Host::DeleteBaseSettingValue(section.c_str(), key.c_str()); } QtHostInterface::GetInstance()->updateEmuFolders(); diff --git a/src/frontend-common/cheevos.cpp b/src/frontend-common/cheevos.cpp index 010ec51c5..d4cca5430 100644 --- a/src/frontend-common/cheevos.cpp +++ b/src/frontend-common/cheevos.cpp @@ -10,6 +10,7 @@ #include "core/bios.h" #include "core/bus.h" #include "core/cpu_core.h" +#include "core/host.h" #include "core/host_display.h" #include "core/host_interface.h" #include "core/host_settings.h" @@ -164,7 +165,7 @@ void Cheevos::FormattedError(const char* format, ...) va_end(ap); - g_host_interface->AddOSDMessage(str.GetCharArray(), 10.0f); + Host::AddOSDMessage(str.GetCharArray(), 10.0f); Log_ErrorPrint(str.GetCharArray()); } @@ -302,8 +303,8 @@ bool Cheevos::Initialize(bool test_mode, bool use_first_disc_from_playlist, bool rc_runtime_init(&s_rcheevos_runtime); s_last_ping_time.Reset(); - s_username = g_host_interface->GetStringSettingValue("Cheevos", "Username"); - s_login_token = g_host_interface->GetStringSettingValue("Cheevos", "Token"); + s_username = Host::GetBaseStringSettingValue("Cheevos", "Username"); + s_login_token = Host::GetBaseStringSettingValue("Cheevos", "Token"); s_logged_in = (!s_username.empty() && !s_login_token.empty()); if (IsLoggedIn() && System::IsValid()) @@ -515,6 +516,16 @@ bool Cheevos::IsRichPresenceEnabled() return s_rich_presence_enabled; } +bool Cheevos::IsChallengeModeActive() +{ + return g_active && g_challenge_mode; +} + +void Cheevos::DisplayBlockedByChallengeModeMessage() +{ + Panic("Fixme"); +} + const std::string& Cheevos::GetUsername() { return s_username; @@ -547,7 +558,6 @@ void Cheevos::LoginCallback(s32 status_code, const FrontendCommon::HTTPDownloade Host::SetBaseStringSettingValue("Cheevos", "Token", login_token.c_str()); Host::SetBaseStringSettingValue("Cheevos", "LoginTimestamp", TinyString::FromFormat("%" PRIu64, static_cast(std::time(nullptr)))); - Host::CommitBaseSettingChanges(); } if (g_active) @@ -590,8 +600,7 @@ bool Cheevos::LoginAsync(const char* username, const char* password) if (FullscreenUI::IsInitialized()) { ImGuiFullscreen::OpenBackgroundProgressDialog( - "cheevos_async_login", g_host_interface->TranslateStdString("Cheevos", "Logging in to RetroAchivements..."), 0, 1, - 0); + "cheevos_async_login", Host::TranslateStdString("Cheevos", "Logging in to RetroAchivements..."), 0, 1, 0); } SendLogin(username, password, s_http_downloader.get(), LoginASyncCallback); @@ -623,7 +632,7 @@ bool Cheevos::Login(const char* username, const char* password) SendLogin(username, password, http_downloader.get(), LoginCallback); http_downloader->WaitForAllRequests(); - return !g_host_interface->GetStringSettingValue("Cheevos", "Token").empty(); + return !Host::GetBaseStringSettingValue("Cheevos", "Token").empty(); } void Cheevos::Logout() @@ -647,7 +656,6 @@ void Cheevos::Logout() Host::DeleteBaseSettingValue("Cheevos", "Username"); Host::DeleteBaseSettingValue("Cheevos", "Token"); Host::DeleteBaseSettingValue("Cheevos", "LoginTimestamp"); - Host::CommitBaseSettingChanges(); } } @@ -672,7 +680,7 @@ void Cheevos::UpdateImageDownloadProgress() if (!FullscreenUI::IsInitialized()) return; - std::string message(g_host_interface->TranslateStdString("Cheevos", "Downloading achievement resources...")); + std::string message(Host::TranslateStdString("Cheevos", "Downloading achievement resources...")); if (!s_image_download_progress_active) { ImGuiFullscreen::OpenBackgroundProgressDialog(str_id, std::move(message), 0, @@ -750,30 +758,29 @@ void Cheevos::DisplayAchievementSummary() { std::string title = s_game_title; if (g_challenge_mode) - title += g_host_interface->TranslateString("Cheevos", " (Hardcore Mode)"); + title += Host::TranslateString("Cheevos", " (Hardcore Mode)"); std::string summary; if (GetAchievementCount() > 0) { summary = StringUtil::StdStringFromFormat( - g_host_interface->TranslateString("Cheevos", "You have earned %u of %u achievements, and %u of %u points."), + Host::TranslateString("Cheevos", "You have earned %u of %u achievements, and %u of %u points."), GetUnlockedAchiementCount(), GetAchievementCount(), GetCurrentPointsForGame(), GetMaximumPointsForGame()); } else { - summary = g_host_interface->TranslateString("Cheevos", "This game has no achievements."); + summary = Host::TranslateString("Cheevos", "This game has no achievements."); } if (GetLeaderboardCount() > 0) { summary.push_back('\n'); if (g_challenge_mode) { - summary.append(g_host_interface->TranslateString("Cheevos", "Leaderboards are enabled.")); + summary.append(Host::TranslateString("Cheevos", "Leaderboards are enabled.")); } else { - summary.append( - g_host_interface->TranslateString("Cheevos", "Leaderboards are DISABLED because Hardcore Mode is off.")); + summary.append(Host::TranslateString("Cheevos", "Leaderboards are DISABLED because Hardcore Mode is off.")); } } @@ -1201,9 +1208,8 @@ void Cheevos::GameChanged(const std::string& path, CDImage* image) if (s_game_hash.empty()) { - g_host_interface->AddOSDMessage( - g_host_interface->TranslateStdString("OSDMessage", "Failed to read executable from disc. Achievements disabled."), - 10.0f); + Host::AddOSDMessage( + Host::TranslateStdString("OSDMessage", "Failed to read executable from disc. Achievements disabled."), 10.0f); return; } @@ -1751,13 +1757,13 @@ int Cheevos::RAIntegration::RACallbackIsActive() void Cheevos::RAIntegration::RACallbackCauseUnpause() { if (System::IsValid()) - g_host_interface->PauseSystem(false); + System::PauseSystem(false); } void Cheevos::RAIntegration::RACallbackCausePause() { if (System::IsValid()) - g_host_interface->PauseSystem(true); + System::PauseSystem(true); } void Cheevos::RAIntegration::RACallbackRebuildMenu() @@ -1774,7 +1780,7 @@ void Cheevos::RAIntegration::RACallbackResetEmulator() { g_challenge_mode = RA_HardcoreModeIsActive() != 0; if (System::IsValid()) - g_host_interface->ResetSystem(); + System::ResetSystem(); } void Cheevos::RAIntegration::RACallbackLoadROM(const char* unused) diff --git a/src/frontend-common/cheevos.h b/src/frontend-common/cheevos.h index 3e193b1f2..dba40b1a7 100644 --- a/src/frontend-common/cheevos.h +++ b/src/frontend-common/cheevos.h @@ -1,6 +1,7 @@ #pragma once #include "common/string.h" #include "core/types.h" +#include "core/cheevos.h" #include #include #include @@ -82,10 +83,7 @@ ALWAYS_INLINE bool IsChallengeModeEnabled() return g_challenge_mode; } -ALWAYS_INLINE bool IsChallengeModeActive() -{ - return g_active && g_challenge_mode; -} +bool IsChallengeModeActive(); ALWAYS_INLINE bool HasActiveGame() { diff --git a/src/frontend-common/common_host_interface.cpp b/src/frontend-common/common_host_interface.cpp index f3f6a6532..409d8e790 100644 --- a/src/frontend-common/common_host_interface.cpp +++ b/src/frontend-common/common_host_interface.cpp @@ -14,9 +14,8 @@ #include "core/gpu.h" #include "core/gte.h" #include "core/host.h" -#include "core/host_settings.h" #include "core/host_display.h" -#include "imgui_fullscreen.h" +#include "core/host_settings.h" #include "core/mdec.h" #include "core/pgxp.h" #include "core/save_state_version.h" @@ -29,14 +28,15 @@ #include "game_list.h" #include "icon.h" #include "imgui.h" +#include "imgui_fullscreen.h" #include "imgui_manager.h" #include "inhibit_screensaver.h" -#include "ini_settings_interface.h" #include "input_manager.h" #include "input_overlay_ui.h" #include "save_state_selector_ui.h" #include "scmversion/scmversion.h" #include "util/audio_stream.h" +#include "util/ini_settings_interface.h" #include #include #include @@ -76,9 +76,33 @@ std::unique_ptr CreateXAudio2AudioStream(); Log_SetChannel(CommonHostInterface); +namespace CommonHost { +static void UpdateLogSettings(LOGLEVEL level, const char* filter, bool log_to_console, bool log_to_debug, + bool log_to_window, bool log_to_file); +#ifdef WITH_DISCORD_PRESENCE +static void SetDiscordPresenceEnabled(bool enabled); +static void InitializeDiscordPresence(); +static void ShutdownDiscordPresence(); +static void UpdateDiscordPresence(bool rich_presence_only); +static void PollDiscordPresence(); +#endif +#ifdef WITH_CHEEVOS +static void UpdateCheevosActive(SettingsInterface& si); +#endif +} // namespace CommonHost + static std::string s_settings_filename; static std::unique_ptr s_input_overlay_ui; +#ifdef WITH_DISCORD_PRESENCE +// discord rich presence +bool m_discord_presence_enabled = false; +bool m_discord_presence_active = false; +#ifdef WITH_CHEEVOS +std::string m_discord_presence_cheevos_string; +#endif +#endif + CommonHostInterface::CommonHostInterface() = default; CommonHostInterface::~CommonHostInterface() = default; @@ -97,26 +121,23 @@ bool CommonHostInterface::Initialize() // Set crash handler to dump to user directory, because of permissions. CrashHandler::SetWriteDirectory(m_user_directory); - LoadSettings(*Host::GetSettingsInterface()); - FixIncompatibleSettings(false); - UpdateLogSettings(g_settings.log_level, g_settings.log_filter.empty() ? nullptr : g_settings.log_filter.c_str(), - g_settings.log_to_console, g_settings.log_to_debug, g_settings.log_to_window, - g_settings.log_to_file); + System::LoadSettings(false); + CommonHost::UpdateLogSettings( + g_settings.log_level, g_settings.log_filter.empty() ? nullptr : g_settings.log_filter.c_str(), + g_settings.log_to_console, g_settings.log_to_debug, g_settings.log_to_window, g_settings.log_to_file); m_game_list = std::make_unique(); m_game_list->SetCacheFilename(GetUserDirectoryRelativePath("cache/gamelist.cache")); m_game_list->SetUserCompatibilityListFilename(GetUserDirectoryRelativePath("compatibility.xml")); m_game_list->SetUserGameSettingsFilename(GetUserDirectoryRelativePath("gamesettings.ini")); - m_save_state_selector_ui = std::make_unique(this); - #ifdef WITH_CHEEVOS #ifdef WITH_RAINTEGRATION - if (GetBoolSettingValue("Cheevos", "UseRAIntegration", false)) + if (Host::GetBaseBoolSettingValue("Cheevos", "UseRAIntegration", false)) Cheevos::SwitchToRAIntegration(); #endif - UpdateCheevosActive(*Host::GetSettingsInterface()); + CommonHost::UpdateCheevosActive(*Host::GetSettingsInterface()); #endif { @@ -134,7 +155,7 @@ void CommonHostInterface::Shutdown() HostInterface::Shutdown(); #ifdef WITH_DISCORD_PRESENCE - ShutdownDiscordPresence(); + CommonHost::ShutdownDiscordPresence(); #endif #ifdef WITH_CHEEVOS @@ -160,98 +181,33 @@ void CommonHostInterface::InitializeUserDirectory() bool result = true; - result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("bios").c_str(), false); - result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("cache").c_str(), false); - result &= FileSystem::CreateDirectory( + result &= FileSystem::EnsureDirectoryExists(GetUserDirectoryRelativePath("bios").c_str(), false); + result &= FileSystem::EnsureDirectoryExists(GetUserDirectoryRelativePath("cache").c_str(), false); + result &= FileSystem::EnsureDirectoryExists( GetUserDirectoryRelativePath("cache" FS_OSPATH_SEPARATOR_STR "achievement_badge").c_str(), false); - result &= FileSystem::CreateDirectory( + result &= FileSystem::EnsureDirectoryExists( GetUserDirectoryRelativePath("cache" FS_OSPATH_SEPARATOR_STR "achievement_gameicon").c_str(), false); - result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("cheats").c_str(), false); - result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("covers").c_str(), false); - result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("dump").c_str(), false); - result &= - FileSystem::CreateDirectory(GetUserDirectoryRelativePath("dump" FS_OSPATH_SEPARATOR_STR "audio").c_str(), false); - result &= - FileSystem::CreateDirectory(GetUserDirectoryRelativePath("dump" FS_OSPATH_SEPARATOR_STR "textures").c_str(), false); - result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("inputprofiles").c_str(), false); - result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("memcards").c_str(), false); - result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("savestates").c_str(), false); - result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("screenshots").c_str(), false); - result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("shaders").c_str(), false); - result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("textures").c_str(), false); + result &= FileSystem::EnsureDirectoryExists(GetUserDirectoryRelativePath("cheats").c_str(), false); + result &= FileSystem::EnsureDirectoryExists(GetUserDirectoryRelativePath("covers").c_str(), false); + result &= FileSystem::EnsureDirectoryExists(GetUserDirectoryRelativePath("dump").c_str(), false); + result &= FileSystem::EnsureDirectoryExists( + GetUserDirectoryRelativePath("dump" FS_OSPATH_SEPARATOR_STR "audio").c_str(), false); + result &= FileSystem::EnsureDirectoryExists( + GetUserDirectoryRelativePath("dump" FS_OSPATH_SEPARATOR_STR "textures").c_str(), false); + result &= FileSystem::EnsureDirectoryExists(GetUserDirectoryRelativePath("inputprofiles").c_str(), false); + result &= FileSystem::EnsureDirectoryExists(GetUserDirectoryRelativePath("memcards").c_str(), false); + result &= FileSystem::EnsureDirectoryExists(GetUserDirectoryRelativePath("savestates").c_str(), false); + result &= FileSystem::EnsureDirectoryExists(GetUserDirectoryRelativePath("screenshots").c_str(), false); + result &= FileSystem::EnsureDirectoryExists(GetUserDirectoryRelativePath("shaders").c_str(), false); + result &= FileSystem::EnsureDirectoryExists(GetUserDirectoryRelativePath("textures").c_str(), false); // Games directory for UWP because it's a pain to create them manually. #ifdef _UWP - result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("games").c_str(), false); + result &= FileSystem::EnsureDirectoryExists(GetUserDirectoryRelativePath("games").c_str(), false); #endif if (!result) - ReportError("Failed to create one or more user directories. This may cause issues at runtime."); -} - -bool CommonHostInterface::BootSystem(std::shared_ptr parameters) -{ - // If the fullscreen UI is enabled, make sure it's finished loading the game list so we don't race it. - if (m_display && FullscreenUI::IsInitialized()) - FullscreenUI::EnsureGameListLoaded(); - - // In Challenge mode, do not allow loading a save state under any circumstances - // If it's present, drop it - if (IsCheevosChallengeModeActive()) - parameters->state_stream.reset(); - - ApplyRendererFromGameSettings(parameters->filename); - - if (!HostInterface::BootSystem(parameters)) - { - // if in batch mode, exit immediately if booting failed - if (InBatchMode()) - RequestExit(); - - return false; - } - - // enter fullscreen if requested in the parameters - if (!g_settings.start_paused && ((parameters->override_fullscreen.has_value() && *parameters->override_fullscreen) || - (!parameters->override_fullscreen.has_value() && g_settings.start_fullscreen))) - { - SetFullscreen(true); - } - - if (g_settings.audio_dump_on_boot) - StartDumpingAudio(); - - UpdateSpeedLimiterState(); - return true; -} - -void CommonHostInterface::DestroySystem() -{ - m_undo_load_state.reset(); - SetTimerResolutionIncreased(false); - m_save_state_selector_ui->Close(); - m_display->SetPostProcessingChain({}); - - HostInterface::DestroySystem(); -} - -void CommonHostInterface::PowerOffSystem(bool save_resume_state) -{ - if (System::IsShutdown()) - return; - - if (save_resume_state) - SaveResumeSaveState(); - - DestroySystem(); - - if (InBatchMode()) - RequestExit(); -} - -void CommonHostInterface::ResetSystem() -{ - HostInterface::ResetSystem(); + Host::ReportErrorAsync("Error", "Failed to create one or more user directories. This may cause issues at runtime."); } static void PrintCommandLineVersion(const char* frontend_name) @@ -434,9 +390,9 @@ bool CommonHostInterface::ParseCommandLineParameters(int argc, char* argv[], { // loading a global state. if this is -1, we're loading the most recent resume state if (*state_index < 0) - state_filename = GetMostRecentResumeSaveStatePath(); + state_filename = System::GetMostRecentResumeSaveStatePath(); else - state_filename = GetGlobalSaveStateFileName(*state_index); + state_filename = System::GetGlobalSaveStateFileName(*state_index); if (state_filename.empty() || !FileSystem::FileExists(state_filename.c_str())) { @@ -455,7 +411,7 @@ bool CommonHostInterface::ParseCommandLineParameters(int argc, char* argv[], } else { - state_filename = GetGameSaveStateFileName(game_code.c_str(), *state_index); + state_filename = System::GetGameSaveStateFileName(game_code.c_str(), *state_index); if (state_filename.empty() || !FileSystem::FileExists(state_filename.c_str())) { if (state_index >= 0) // Do not exit if -resume is specified, but resume save state does not exist @@ -501,7 +457,7 @@ void CommonHostInterface::OnAchievementsRefreshed() // noop } -void CommonHostInterface::PollAndUpdate() +void CommonHost::PumpMessagesOnCPUThread() { InputManager::PollSources(); @@ -548,7 +504,7 @@ void CommonHostInterface::OnHostDisplayResized() g_gpu->UpdateResolutionScale(); } -std::unique_ptr CommonHostInterface::CreateAudioStream(AudioBackend backend) +std::unique_ptr Host::CreateAudioStream(AudioBackend backend) { switch (backend) { @@ -575,264 +531,8 @@ std::unique_ptr CommonHostInterface::CreateAudioStream(AudioBackend } } -s32 CommonHostInterface::GetAudioOutputVolume() const -{ - return g_settings.GetAudioOutputVolume(IsRunningAtNonStandardSpeed()); -} - -bool CommonHostInterface::UndoLoadState() -{ - if (!m_undo_load_state) - return false; - - Assert(System::IsValid()); - - m_undo_load_state->SeekAbsolute(0); - if (!System::LoadState(m_undo_load_state.get())) - { - ReportError("Failed to load undo state, resetting system."); - m_undo_load_state.reset(); - ResetSystem(); - return false; - } - - System::ResetPerformanceCounters(); - System::ResetThrottler(); - - Log_InfoPrintf("Loaded undo save state."); - m_undo_load_state.reset(); - return true; -} - -bool CommonHostInterface::SaveUndoLoadState() -{ - if (m_undo_load_state) - m_undo_load_state.reset(); - - m_undo_load_state = ByteStream::CreateGrowableMemoryStream(nullptr, System::MAX_SAVE_STATE_SIZE); - if (!System::SaveState(m_undo_load_state.get())) - { - AddOSDMessage(TranslateStdString("OSDMessage", "Failed to save undo load state."), 15.0f); - m_undo_load_state.reset(); - return false; - } - - Log_InfoPrintf("Saved undo load state: %" PRIu64 " bytes", m_undo_load_state->GetSize()); - return true; -} - -bool CommonHostInterface::LoadState(const char* filename) -{ - const bool system_was_valid = System::IsValid(); - if (system_was_valid) - SaveUndoLoadState(); - - const bool result = HostInterface::LoadState(filename); - if (system_was_valid || !result) - { -#ifdef WITH_CHEEVOS - Cheevos::Reset(); -#endif - } - - if (!result && CanUndoLoadState()) - UndoLoadState(); - - return result; -} - -bool CommonHostInterface::LoadState(bool global, s32 slot) -{ - if (!global && (System::IsShutdown() || System::GetRunningCode().empty())) - { - ReportFormattedError("Can't save per-game state without a running game code."); - return false; - } - - std::string save_path = - global ? GetGlobalSaveStateFileName(slot) : GetGameSaveStateFileName(System::GetRunningCode().c_str(), slot); - return LoadState(save_path.c_str()); -} - -bool CommonHostInterface::SaveState(bool global, s32 slot) -{ - const std::string& code = System::GetRunningCode(); - if (!global && code.empty()) - { - ReportFormattedError("Can't save per-game state without a running game code."); - return false; - } - - std::string save_path = global ? GetGlobalSaveStateFileName(slot) : GetGameSaveStateFileName(code.c_str(), slot); - RenameCurrentSaveStateToBackup(save_path.c_str()); - return SaveState(save_path.c_str()); -} - -bool CommonHostInterface::CanResumeSystemFromFile(const char* filename) -{ - if (GetBoolSettingValue("Main", "SaveStateOnExit", true) && !IsCheevosChallengeModeActive()) - { - const GameListEntry* entry = m_game_list->GetEntryForPath(filename); - if (entry) - return !entry->code.empty(); - else - return !System::GetGameCodeForPath(filename, true).empty(); - } - - return false; -} - -bool CommonHostInterface::ResumeSystemFromState(const char* filename, bool boot_on_failure) -{ - if (!BootSystem(std::make_shared(filename))) - return false; - - const bool global = System::GetRunningCode().empty(); - if (global) - { - ReportFormattedError("Cannot resume system with undetectable game code from '%s'.", filename); - if (!boot_on_failure) - { - DestroySystem(); - return true; - } - } - else - { - const std::string path = GetGameSaveStateFileName(System::GetRunningCode().c_str(), -1); - if (FileSystem::FileExists(path.c_str())) - { - if (!LoadState(path.c_str()) && !boot_on_failure) - { - DestroySystem(); - return false; - } - } - else if (!boot_on_failure) - { - ReportFormattedError("Resume save state not found for '%s' ('%s').", System::GetRunningCode().c_str(), - System::GetRunningTitle().c_str()); - DestroySystem(); - return false; - } - } - - return true; -} - -bool CommonHostInterface::ResumeSystemFromMostRecentState() -{ - const std::string path = GetMostRecentResumeSaveStatePath(); - if (path.empty()) - { - ReportError("No resume save state found."); - return false; - } - - return LoadState(path.c_str()); -} - -bool CommonHostInterface::ShouldSaveResumeState() const -{ - return g_settings.save_state_on_exit; -} - -bool CommonHostInterface::IsRunningAtNonStandardSpeed() const -{ - if (!System::IsValid()) - return false; - - const float target_speed = System::GetTargetSpeed(); - return (target_speed <= 0.95f || target_speed >= 1.05f); -} - -void CommonHostInterface::UpdateSpeedLimiterState() -{ - float target_speed = m_turbo_enabled ? - g_settings.turbo_speed : - (m_fast_forward_enabled ? g_settings.fast_forward_speed : g_settings.emulation_speed); - m_throttler_enabled = (target_speed != 0.0f); - m_display_all_frames = !m_throttler_enabled || g_settings.display_all_frames; - - bool syncing_to_host = false; - if (g_settings.sync_to_host_refresh_rate && g_settings.audio_resampling && target_speed == 1.0f && m_display && - System::IsRunning()) - { - float host_refresh_rate; - if (m_display->GetHostRefreshRate(&host_refresh_rate)) - { - const float ratio = host_refresh_rate / System::GetThrottleFrequency(); - syncing_to_host = (ratio >= 0.95f && ratio <= 1.05f); - Log_InfoPrintf("Refresh rate: Host=%fhz Guest=%fhz Ratio=%f - %s", host_refresh_rate, - System::GetThrottleFrequency(), ratio, syncing_to_host ? "can sync" : "can't sync"); - if (syncing_to_host) - target_speed *= ratio; - } - } - - const bool is_non_standard_speed = (std::abs(target_speed - 1.0f) > 0.05f); - const bool audio_sync_enabled = - !System::IsRunning() || (m_throttler_enabled && g_settings.audio_sync_enabled && !is_non_standard_speed); - const bool video_sync_enabled = - !System::IsRunning() || (m_throttler_enabled && g_settings.video_sync_enabled && !is_non_standard_speed); - const float max_display_fps = (!System::IsRunning() || m_throttler_enabled) ? 0.0f : g_settings.display_max_fps; - Log_InfoPrintf("Target speed: %f%%", target_speed * 100.0f); - Log_InfoPrintf("Syncing to %s%s", audio_sync_enabled ? "audio" : "", - (audio_sync_enabled && video_sync_enabled) ? " and video" : (video_sync_enabled ? "video" : "")); - Log_InfoPrintf("Max display fps: %f (%s)", max_display_fps, - m_display_all_frames ? "displaying all frames" : "skipping displaying frames when needed"); - - if (System::IsValid()) - { - System::SetTargetSpeed(target_speed); - System::ResetThrottler(); - } - - if (m_audio_stream) - { - const u32 input_sample_rate = (target_speed == 0.0f || !g_settings.audio_resampling) ? - AUDIO_SAMPLE_RATE : - static_cast(static_cast(AUDIO_SAMPLE_RATE) * target_speed); - Log_InfoPrintf("Audio input sample rate: %u hz", input_sample_rate); - - m_audio_stream->SetInputSampleRate(input_sample_rate); - m_audio_stream->SetWaitForBufferFill(true); - - if (g_settings.audio_fast_forward_volume != g_settings.audio_output_volume) - m_audio_stream->SetOutputVolume(GetAudioOutputVolume()); - - m_audio_stream->SetSync(audio_sync_enabled); - if (audio_sync_enabled) - m_audio_stream->EmptyBuffers(); - } - - if (m_display) - { - m_display->SetDisplayMaxFPS(max_display_fps); - m_display->SetVSync(video_sync_enabled); - } - - if (g_settings.increase_timer_resolution) - SetTimerResolutionIncreased(m_throttler_enabled); - - // When syncing to host and using vsync, we don't need to sleep. - if (syncing_to_host && video_sync_enabled && m_display_all_frames) - { - Log_InfoPrintf("Using host vsync for throttling."); - m_throttler_enabled = false; - } -} - -void CommonHostInterface::RecreateSystem() -{ - const bool was_paused = System::IsPaused(); - HostInterface::RecreateSystem(); - if (was_paused) - PauseSystem(true); -} - -void CommonHostInterface::UpdateLogSettings(LOGLEVEL level, const char* filter, bool log_to_console, bool log_to_debug, - bool log_to_window, bool log_to_file) +void CommonHost::UpdateLogSettings(LOGLEVEL level, const char* filter, bool log_to_console, bool log_to_debug, + bool log_to_window, bool log_to_file) { Log::SetFilterLevel(level); Log::SetConsoleOutputParams(g_settings.log_to_console, filter, level); @@ -840,8 +540,9 @@ void CommonHostInterface::UpdateLogSettings(LOGLEVEL level, const char* filter, if (log_to_file) { - Log::SetFileOutputParams(g_settings.log_to_file, GetUserDirectoryRelativePath("duckstation.log").c_str(), true, - filter, level); + Log::SetFileOutputParams(g_settings.log_to_file, + g_host_interface->GetUserDirectoryRelativePath("duckstation.log").c_str(), true, filter, + level); } else { @@ -909,178 +610,65 @@ void CommonHostInterface::SetUserDirectory() } } -void CommonHostInterface::OnSystemCreated() +void CommonHost::OnSystemStarting() +{ + // +} + +void CommonHost::OnSystemStarted() { if (FullscreenUI::IsInitialized()) FullscreenUI::SystemCreated(); - if (g_settings.display_post_processing && !m_display->SetPostProcessingChain(g_settings.display_post_process_chain)) - AddOSDMessage(TranslateStdString("OSDMessage", "Failed to load post processing shader chain."), 20.0f); + if (g_settings.inhibit_screensaver) + FrontendCommon::SuspendScreensaver(Host::GetHostDisplay()->GetWindowInfo()); +} + +void CommonHost::OnSystemPaused() +{ +#if 0 + if (IsFullscreen() && !FullscreenUI::IsInitialized()) + SetFullscreen(false); +#endif + + InputManager::PauseVibration(); if (g_settings.inhibit_screensaver) - FrontendCommon::SuspendScreensaver(m_display->GetWindowInfo()); -} - -void CommonHostInterface::OnSystemPaused(bool paused) -{ - if (paused) - { - if (IsFullscreen() && !FullscreenUI::IsInitialized()) - SetFullscreen(false); - - InputManager::PauseVibration(); FrontendCommon::ResumeScreensaver(); - } - else - { - if (g_settings.inhibit_screensaver) - FrontendCommon::SuspendScreensaver(m_display->GetWindowInfo()); - } - - UpdateSpeedLimiterState(); } -void CommonHostInterface::OnSystemDestroyed() +void CommonHost::OnSystemResumed() { - // Restore present-all-frames behavior. - if (m_display) - m_display->SetDisplayMaxFPS(0.0f); + if (g_settings.inhibit_screensaver) + FrontendCommon::SuspendScreensaver(Host::GetHostDisplay()->GetWindowInfo()); +} + +void CommonHost::OnSystemDestroyed() +{ + Host::ClearOSDMessages(); if (FullscreenUI::IsInitialized()) FullscreenUI::SystemDestroyed(); InputManager::PauseVibration(); - FrontendCommon::ResumeScreensaver(); + + if (g_settings.inhibit_screensaver) + FrontendCommon::ResumeScreensaver(); } -void CommonHostInterface::OnRunningGameChanged(const std::string& path, CDImage* image, const std::string& game_code, - const std::string& game_title) +void CommonHost::OnGameChanged(const std::string& disc_path, const std::string& game_serial, + const std::string& game_name) { - if (g_settings.apply_game_settings) - ApplySettings(true); - - if (!System::IsShutdown()) - { - System::SetCheatList(nullptr); - if (g_settings.auto_load_cheats) - { - DebugAssert(!IsCheevosChallengeModeActive()); - LoadCheatListFromGameTitle(); - } - } - #ifdef WITH_DISCORD_PRESENCE UpdateDiscordPresence(false); #endif #ifdef WITH_CHEEVOS - if (Cheevos::IsLoggedIn()) - Cheevos::GameChanged(path, image); + // if (Cheevos::IsLoggedIn()) + // Cheevos::GameChanged(path, image); #endif } -void CommonHostInterface::OnControllerTypeChanged(u32 slot) -{ - { - auto lock = Host::GetSettingsLock(); - InputManager::ReloadBindings(*Host::GetSettingsInterface(), *Host::GetSettingsInterface()); - } -} - -bool CommonHostInterface::IsCheevosChallengeModeActive() const -{ -#ifdef WITH_CHEEVOS - return Cheevos::IsChallengeModeActive(); -#else - return false; -#endif -} - -void CommonHostInterface::DoFrameStep() -{ - if (System::IsShutdown()) - return; - - m_frame_step_request = true; - PauseSystem(false); -} - -void CommonHostInterface::DoToggleCheats() -{ - if (System::IsShutdown()) - return; - - CheatList* cl = System::GetCheatList(); - if (!cl) - { - AddKeyedOSDMessage("ToggleCheats", TranslateStdString("OSDMessage", "No cheats are loaded."), 10.0f); - return; - } - - cl->SetMasterEnable(!cl->GetMasterEnable()); - AddKeyedOSDMessage("ToggleCheats", - cl->GetMasterEnable() ? - TranslateStdString("OSDMessage", "%n cheats are now active.", "", cl->GetEnabledCodeCount()) : - TranslateStdString("OSDMessage", "%n cheats are now inactive.", "", cl->GetEnabledCodeCount()), - 10.0f); -} - -static void DisplayHotkeyBlockedByChallengeModeMessage() -{ - g_host_interface->AddOSDMessage(g_host_interface->TranslateStdString( - "OSDMessage", "Hotkey unavailable because achievements hardcore mode is active.")); -} - -void CommonHostInterface::SetFastForwardEnabled(bool enabled) -{ - if (!System::IsValid()) - return; - - m_fast_forward_enabled = enabled; - UpdateSpeedLimiterState(); -} - -void CommonHostInterface::SetTurboEnabled(bool enabled) -{ - if (!System::IsValid()) - return; - - m_turbo_enabled = enabled; - UpdateSpeedLimiterState(); -} - -void CommonHostInterface::SetRewindState(bool enabled) -{ - if (!System::IsValid()) - return; - - if (!IsCheevosChallengeModeActive()) - { - if (!g_settings.rewind_enable) - { - if (enabled) - AddKeyedOSDMessage("SetRewindState", TranslateStdString("OSDMessage", "Rewinding is not enabled."), 5.0f); - - return; - } - - if (!FullscreenUI::IsInitialized()) - { - AddKeyedOSDMessage("SetRewindState", - enabled ? TranslateStdString("OSDMessage", "Rewinding...") : - TranslateStdString("OSDMessage", "Stopped rewinding."), - 5.0f); - } - - System::SetRewinding(enabled); - UpdateSpeedLimiterState(); - } - else - { - DisplayHotkeyBlockedByChallengeModeMessage(); - } -} - std::string CommonHostInterface::GetSettingsFileName() const { std::string filename; @@ -1092,217 +680,10 @@ std::string CommonHostInterface::GetSettingsFileName() const return filename; } -std::string CommonHostInterface::GetGameSaveStateFileName(const char* game_code, s32 slot) const +void CommonHost::SetDefaultSettings(SettingsInterface& si) { - if (slot < 0) - return GetUserDirectoryRelativePath("savestates" FS_OSPATH_SEPARATOR_STR "%s_resume.sav", game_code); - else - return GetUserDirectoryRelativePath("savestates" FS_OSPATH_SEPARATOR_STR "%s_%d.sav", game_code, slot); -} - -std::string CommonHostInterface::GetGlobalSaveStateFileName(s32 slot) const -{ - if (slot < 0) - return GetUserDirectoryRelativePath("savestates" FS_OSPATH_SEPARATOR_STR "resume.sav"); - else - return GetUserDirectoryRelativePath("savestates" FS_OSPATH_SEPARATOR_STR "savestate_%d.sav", slot); -} - -void CommonHostInterface::RenameCurrentSaveStateToBackup(const char* filename) -{ - if (!GetBoolSettingValue("General", "CreateSaveStateBackups", false)) - return; - - if (!FileSystem::FileExists(filename)) - return; - - const std::string backup_filename(Path::ReplaceExtension(filename, "bak")); - if (!FileSystem::RenamePath(filename, backup_filename.c_str())) - { - Log_ErrorPrintf("Failed to rename save state backup '%s'", backup_filename.c_str()); - return; - } - - Log_InfoPrintf("Renamed save state '%s' to '%s'", filename, backup_filename.c_str()); -} - -std::vector CommonHostInterface::GetAvailableSaveStates(const char* game_code) const -{ - std::vector si; - std::string path; - - auto add_path = [&si](std::string path, s32 slot, bool global) { - FILESYSTEM_STAT_DATA sd; - if (!FileSystem::StatFile(path.c_str(), &sd)) - return; - - si.push_back(SaveStateInfo{std::move(path), sd.ModificationTime, static_cast(slot), global}); - }; - - if (game_code && std::strlen(game_code) > 0) - { - add_path(GetGameSaveStateFileName(game_code, -1), -1, false); - for (s32 i = 1; i <= PER_GAME_SAVE_STATE_SLOTS; i++) - add_path(GetGameSaveStateFileName(game_code, i), i, false); - } - - for (s32 i = 1; i <= GLOBAL_SAVE_STATE_SLOTS; i++) - add_path(GetGlobalSaveStateFileName(i), i, true); - - return si; -} - -std::optional CommonHostInterface::GetSaveStateInfo(const char* game_code, s32 slot) -{ - const bool global = (!game_code || game_code[0] == 0); - std::string path = global ? GetGlobalSaveStateFileName(slot) : GetGameSaveStateFileName(game_code, slot); - - FILESYSTEM_STAT_DATA sd; - if (!FileSystem::StatFile(path.c_str(), &sd)) - return std::nullopt; - - return SaveStateInfo{std::move(path), sd.ModificationTime, slot, global}; -} - -std::optional -CommonHostInterface::GetExtendedSaveStateInfo(ByteStream* stream) -{ - SAVE_STATE_HEADER header; - if (!stream->Read(&header, sizeof(header)) || header.magic != SAVE_STATE_MAGIC) - return std::nullopt; - - ExtendedSaveStateInfo ssi; - if (header.version < SAVE_STATE_MINIMUM_VERSION || header.version > SAVE_STATE_VERSION) - { - ssi.title = StringUtil::StdStringFromFormat( - TranslateString("CommonHostInterface", "Invalid version %u (%s version %u)"), header.version, - header.version > SAVE_STATE_VERSION ? "maximum" : "minimum", - header.version > SAVE_STATE_VERSION ? SAVE_STATE_VERSION : SAVE_STATE_MINIMUM_VERSION); - return ssi; - } - - header.title[sizeof(header.title) - 1] = 0; - ssi.title = header.title; - header.game_code[sizeof(header.game_code) - 1] = 0; - ssi.game_code = header.game_code; - - if (header.media_filename_length > 0 && - (header.offset_to_media_filename + header.media_filename_length) <= stream->GetSize()) - { - stream->SeekAbsolute(header.offset_to_media_filename); - ssi.media_path.resize(header.media_filename_length); - if (!stream->Read2(ssi.media_path.data(), header.media_filename_length)) - std::string().swap(ssi.media_path); - } - - if (header.screenshot_width > 0 && header.screenshot_height > 0 && header.screenshot_size > 0 && - (static_cast(header.offset_to_screenshot) + static_cast(header.screenshot_size)) <= stream->GetSize()) - { - stream->SeekAbsolute(header.offset_to_screenshot); - ssi.screenshot_data.resize((header.screenshot_size + 3u) / 4u); - if (stream->Read2(ssi.screenshot_data.data(), header.screenshot_size)) - { - ssi.screenshot_width = header.screenshot_width; - ssi.screenshot_height = header.screenshot_height; - } - else - { - decltype(ssi.screenshot_data)().swap(ssi.screenshot_data); - } - } - - return ssi; -} - -std::optional -CommonHostInterface::GetExtendedSaveStateInfo(const char* game_code, s32 slot) -{ - const bool global = (!game_code || game_code[0] == 0); - std::string path = global ? GetGlobalSaveStateFileName(slot) : GetGameSaveStateFileName(game_code, slot); - - FILESYSTEM_STAT_DATA sd; - if (!FileSystem::StatFile(path.c_str(), &sd)) - return std::nullopt; - - std::unique_ptr stream = - ByteStream::OpenFile(path.c_str(), BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_SEEKABLE); - if (!stream) - return std::nullopt; - - std::optional ssi = GetExtendedSaveStateInfo(stream.get()); - if (!ssi) - return std::nullopt; - - ssi->path = std::move(path); - ssi->timestamp = sd.ModificationTime; - ssi->slot = slot; - ssi->global = global; - - return ssi; -} - -std::optional CommonHostInterface::GetUndoSaveStateInfo() -{ - std::optional ssi; - if (m_undo_load_state) - { - m_undo_load_state->SeekAbsolute(0); - ssi = GetExtendedSaveStateInfo(m_undo_load_state.get()); - m_undo_load_state->SeekAbsolute(0); - - if (ssi) - { - ssi->timestamp = 0; - ssi->slot = 0; - ssi->global = false; - } - } - - return ssi; -} - -void CommonHostInterface::DeleteSaveStates(const char* game_code, bool resume) -{ - const std::vector states(GetAvailableSaveStates(game_code)); - for (const SaveStateInfo& si : states) - { - if (si.global || (!resume && si.slot < 0)) - continue; - - Log_InfoPrintf("Removing save state at '%s'", si.path.c_str()); - if (!FileSystem::DeleteFile(si.path.c_str())) - Log_ErrorPrintf("Failed to delete save state file '%s'", si.path.c_str()); - } -} - -std::string CommonHostInterface::GetMostRecentResumeSaveStatePath() const -{ - std::vector files; - if (!FileSystem::FindFiles(GetUserDirectoryRelativePath("savestates").c_str(), "*resume.sav", FILESYSTEM_FIND_FILES, - &files) || - files.empty()) - { - return {}; - } - - FILESYSTEM_FIND_DATA* most_recent = &files[0]; - for (FILESYSTEM_FIND_DATA& file : files) - { - if (file.ModificationTime > most_recent->ModificationTime) - most_recent = &file; - } - - return std::move(most_recent->FileName); -} - -void CommonHostInterface::SetDefaultSettings(SettingsInterface& si) -{ - HostInterface::SetDefaultSettings(si); - InputManager::SetDefaultConfig(si); - si.SetBoolValue("Display", "InternalResolutionScreenshots", false); - #ifdef WITH_DISCORD_PRESENCE si.SetBoolValue("Main", "EnableDiscordPresence", false); #endif @@ -1321,9 +702,10 @@ void CommonHostInterface::SetDefaultSettings(SettingsInterface& si) #endif } -void CommonHostInterface::LoadSettings(SettingsInterface& si) +void CommonHost::LoadSettings(SettingsInterface& si, std::unique_lock& lock) { - HostInterface::LoadSettings(si); + InputManager::ReloadSources(si, lock); + InputManager::ReloadBindings(si, *Host::GetSettingsInterfaceForBindings()); #ifdef WITH_DISCORD_PRESENCE SetDiscordPresenceEnabled(si.GetBoolValue("Main", "EnableDiscordPresence", false)); @@ -1343,113 +725,14 @@ void CommonHostInterface::LoadSettings(SettingsInterface& si) s_input_overlay_ui.reset(); } -void CommonHostInterface::SaveSettings(SettingsInterface& si) +void CommonHost::CheckForSettingsChanges(const Settings& old_settings) { - HostInterface::SaveSettings(si); -} - -void CommonHostInterface::FixIncompatibleSettings(bool display_osd_messages) -{ - // if challenge mode is enabled, disable things like rewind since they use save states - if (IsCheevosChallengeModeActive()) - { - 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; - } - - HostInterface::FixIncompatibleSettings(display_osd_messages); -} - -void CommonHostInterface::ApplySettings(bool display_osd_messages) -{ - Settings old_settings(std::move(g_settings)); - { - auto lock = Host::GetSettingsLock(); - LoadSettings(*Host::GetSettingsInterface()); - ApplyGameSettings(display_osd_messages); - FixIncompatibleSettings(display_osd_messages); - InputManager::ReloadSources(*Host::GetSettingsInterface(), lock); - InputManager::ReloadBindings(*Host::GetSettingsInterface(), *Host::GetSettingsInterface()); - } - - CheckForSettingsChanges(old_settings); -} - -void CommonHostInterface::SetDefaultSettings() -{ - Settings old_settings(std::move(g_settings)); - { - auto lock = Host::GetSettingsLock(); - SetDefaultSettings(*Host::GetSettingsInterface()); - LoadSettings(*Host::GetSettingsInterface()); - InputManager::ReloadSources(*Host::GetSettingsInterface(), lock); - InputManager::ReloadBindings(*Host::GetSettingsInterface(), *Host::GetSettingsInterface()); - ApplyGameSettings(true); - FixIncompatibleSettings(true); - } - - CheckForSettingsChanges(old_settings); -} - -void CommonHostInterface::CheckForSettingsChanges(const Settings& old_settings) -{ - HostInterface::CheckForSettingsChanges(old_settings); - if (System::IsValid()) { - if (g_settings.audio_backend != old_settings.audio_backend || - g_settings.audio_buffer_size != old_settings.audio_buffer_size || - g_settings.video_sync_enabled != old_settings.video_sync_enabled || - g_settings.audio_sync_enabled != old_settings.audio_sync_enabled || - g_settings.increase_timer_resolution != old_settings.increase_timer_resolution || - g_settings.emulation_speed != old_settings.emulation_speed || - g_settings.fast_forward_speed != old_settings.fast_forward_speed || - g_settings.display_max_fps != old_settings.display_max_fps || - g_settings.display_all_frames != old_settings.display_all_frames || - g_settings.audio_resampling != old_settings.audio_resampling || - g_settings.sync_to_host_refresh_rate != old_settings.sync_to_host_refresh_rate) - { - UpdateSpeedLimiterState(); - } - - if (g_settings.display_post_processing != old_settings.display_post_processing || - g_settings.display_post_process_chain != old_settings.display_post_process_chain) - { - if (g_settings.display_post_processing) - { - if (!m_display->SetPostProcessingChain(g_settings.display_post_process_chain)) - AddOSDMessage(TranslateStdString("OSDMessage", "Failed to load post processing shader chain."), 20.0f); - } - else - { - m_display->SetPostProcessingChain({}); - } - } - if (g_settings.inhibit_screensaver != old_settings.inhibit_screensaver) { if (g_settings.inhibit_screensaver) - FrontendCommon::SuspendScreensaver(m_display->GetWindowInfo()); + FrontendCommon::SuspendScreensaver(Host::GetHostDisplay()->GetWindowInfo()); else FrontendCommon::ResumeScreensaver(); } @@ -1466,32 +749,6 @@ void CommonHostInterface::CheckForSettingsChanges(const Settings& old_settings) } } -std::string CommonHostInterface::GetStringSettingValue(const char* section, const char* key, - const char* default_value /*= ""*/) -{ - return Host::GetStringSettingValue(section, key, default_value); -} - -bool CommonHostInterface::GetBoolSettingValue(const char* section, const char* key, bool default_value /* = false */) -{ - return Host::GetBoolSettingValue(section, key, default_value); -} - -int CommonHostInterface::GetIntSettingValue(const char* section, const char* key, int default_value /* = 0 */) -{ - return Host::GetIntSettingValue(section, key, default_value); -} - -float CommonHostInterface::GetFloatSettingValue(const char* section, const char* key, float default_value /* = 0.0f */) -{ - return Host::GetFloatSettingValue(section, key, default_value); -} - -std::vector CommonHostInterface::GetSettingStringList(const char* section, const char* key) -{ - return Host::GetStringListSetting(section, key); -} - void CommonHostInterface::SetTimerResolutionIncreased(bool enabled) { #if defined(_WIN32) && !defined(_UWP) @@ -1562,7 +819,7 @@ void CommonHostInterface::DisplayLoadingScreen(const char* message, int progress ImGui::End(); ImGui::EndFrame(); - m_display->Render(); + Host::GetHostDisplay()->Render(); } void CommonHostInterface::GetGameInfo(const char* path, CDImage* image, std::string* code, std::string* title) @@ -1592,109 +849,6 @@ void CommonHostInterface::GetGameInfo(const char* path, CDImage* image, std::str *title = Path::GetFileTitle(display_name); } -bool CommonHostInterface::SaveResumeSaveState() -{ - if (System::IsShutdown()) - return false; - - const bool global = System::GetRunningCode().empty(); - return SaveState(global, -1); -} - -bool CommonHostInterface::IsDumpingAudio() const -{ - return g_spu.IsDumpingAudio(); -} - -bool CommonHostInterface::StartDumpingAudio(const char* filename) -{ - if (System::IsShutdown()) - return false; - - std::string auto_filename; - if (!filename) - { - const auto& code = System::GetRunningCode(); - if (code.empty()) - { - auto_filename = GetUserDirectoryRelativePath("dump/audio/%s.wav", GetTimestampStringForFileName().GetCharArray()); - } - else - { - auto_filename = GetUserDirectoryRelativePath("dump/audio/%s_%s.wav", code.c_str(), - GetTimestampStringForFileName().GetCharArray()); - } - - filename = auto_filename.c_str(); - } - - if (g_spu.StartDumpingAudio(filename)) - { - AddFormattedOSDMessage(5.0f, TranslateString("OSDMessage", "Started dumping audio to '%s'."), filename); - return true; - } - else - { - AddFormattedOSDMessage(10.0f, TranslateString("OSDMessage", "Failed to start dumping audio to '%s'."), filename); - return false; - } -} - -void CommonHostInterface::StopDumpingAudio() -{ - if (System::IsShutdown() || !g_spu.StopDumpingAudio()) - return; - - AddOSDMessage(TranslateStdString("OSDMessage", "Stopped dumping audio."), 5.0f); -} - -bool CommonHostInterface::SaveScreenshot(const char* filename /* = nullptr */, bool full_resolution /* = true */, - bool apply_aspect_ratio /* = true */, bool compress_on_thread /* = true */) -{ - if (System::IsShutdown()) - return false; - - std::string auto_filename; - if (!filename) - { - const auto& code = System::GetRunningCode(); - const char* extension = "png"; - if (code.empty()) - { - auto_filename = GetUserDirectoryRelativePath("screenshots" FS_OSPATH_SEPARATOR_STR "%s.%s", - GetTimestampStringForFileName().GetCharArray(), extension); - } - else - { - auto_filename = GetUserDirectoryRelativePath("screenshots" FS_OSPATH_SEPARATOR_STR "%s_%s.%s", code.c_str(), - GetTimestampStringForFileName().GetCharArray(), extension); - } - - filename = auto_filename.c_str(); - } - - if (FileSystem::FileExists(filename)) - { - AddFormattedOSDMessage(10.0f, TranslateString("OSDMessage", "Screenshot file '%s' already exists."), filename); - return false; - } - - const bool internal_resolution = GetBoolSettingValue("Display", "InternalResolutionScreenshots", false); - const bool screenshot_saved = - internal_resolution ? - m_display->WriteDisplayTextureToFile(filename, full_resolution, apply_aspect_ratio, compress_on_thread) : - m_display->WriteScreenshotToFile(filename, compress_on_thread); - - if (!screenshot_saved) - { - AddFormattedOSDMessage(10.0f, TranslateString("OSDMessage", "Failed to save screenshot to '%s'"), filename); - return false; - } - - AddFormattedOSDMessage(5.0f, TranslateString("OSDMessage", "Screenshot saved to '%s'."), filename); - return true; -} - void CommonHostInterface::ApplyGameSettings(bool display_osd_messages) { g_settings.controller_disable_analog_mode_forcing = false; @@ -1774,14 +928,15 @@ void CommonHostInterface::ApplyControllerCompatibilitySettings(u64 controller_ma supported_controller_string.AppendString(", "); supported_controller_string.AppendString( - TranslateString("ControllerType", Settings::GetControllerTypeDisplayName(supported_ctype))); + Host::TranslateString("ControllerType", Settings::GetControllerTypeDisplayName(supported_ctype))); } - AddFormattedOSDMessage( + Host::AddFormattedOSDMessage( 30.0f, - TranslateString("OSDMessage", "Controller in port %u (%s) is not supported for %s.\nSupported controllers: " - "%s\nPlease configure a supported controller from the list above."), - i + 1u, TranslateString("ControllerType", Settings::GetControllerTypeDisplayName(ctype)).GetCharArray(), + Host::TranslateString("OSDMessage", + "Controller in port %u (%s) is not supported for %s.\nSupported controllers: " + "%s\nPlease configure a supported controller from the list above."), + i + 1u, Host::TranslateString("ControllerType", Settings::GetControllerTypeDisplayName(ctype)).GetCharArray(), System::GetRunningTitle().c_str(), supported_controller_string.GetCharArray()); } } @@ -1827,294 +982,6 @@ bool CommonHostInterface::UpdateControllerInputMapFromGameSettings() } #endif -std::string CommonHostInterface::GetCheatFileName() const -{ - const std::string& title = System::GetRunningTitle(); - if (title.empty()) - return {}; - - return GetUserDirectoryRelativePath("cheats/%s.cht", title.c_str()); -} - -bool CommonHostInterface::LoadCheatList(const char* filename) -{ - if (System::IsShutdown()) - return false; - - std::unique_ptr cl = std::make_unique(); - if (!cl->LoadFromFile(filename, CheatList::Format::Autodetect)) - { - AddFormattedOSDMessage(15.0f, TranslateString("OSDMessage", "Failed to load cheats from '%s'."), filename); - return false; - } - - if (cl->GetEnabledCodeCount() > 0) - { - AddOSDMessage(TranslateStdString("OSDMessage", "%n cheats are enabled. This may result in instability.", "", - cl->GetEnabledCodeCount()), - 30.0f); - } - - System::SetCheatList(std::move(cl)); - return true; -} - -bool CommonHostInterface::LoadCheatListFromGameTitle() -{ - if (IsCheevosChallengeModeActive()) - return false; - - const std::string filename(GetCheatFileName()); - if (filename.empty() || !FileSystem::FileExists(filename.c_str())) - return false; - - return LoadCheatList(filename.c_str()); -} - -bool CommonHostInterface::LoadCheatListFromDatabase() -{ - if (System::GetRunningCode().empty() || IsCheevosChallengeModeActive()) - return false; - - std::unique_ptr cl = std::make_unique(); - if (!cl->LoadFromPackage(System::GetRunningCode())) - return false; - - Log_InfoPrintf("Loaded %u cheats from database.", cl->GetCodeCount()); - System::SetCheatList(std::move(cl)); - return true; -} - -bool CommonHostInterface::SaveCheatList() -{ - if (!System::IsValid() || !System::HasCheatList()) - return false; - - const std::string filename(GetCheatFileName()); - if (filename.empty()) - return false; - - if (!System::GetCheatList()->SaveToPCSXRFile(filename.c_str())) - { - AddFormattedOSDMessage(15.0f, TranslateString("OSDMessage", "Failed to save cheat list to '%s'"), filename.c_str()); - } - - return true; -} - -bool CommonHostInterface::SaveCheatList(const char* filename) -{ - if (!System::IsValid() || !System::HasCheatList()) - return false; - - if (!System::GetCheatList()->SaveToPCSXRFile(filename)) - return false; - - // This shouldn't be needed, but lupdate doesn't gather this string otherwise... - const u32 code_count = System::GetCheatList()->GetCodeCount(); - AddFormattedOSDMessage(5.0f, TranslateString("OSDMessage", "Saved %n cheats to '%s'.", "", code_count), filename); - return true; -} - -bool CommonHostInterface::DeleteCheatList() -{ - if (!System::IsValid()) - return false; - - const std::string filename(GetCheatFileName()); - if (!filename.empty()) - { - if (!FileSystem::DeleteFile(filename.c_str())) - return false; - - AddFormattedOSDMessage(5.0f, TranslateString("OSDMessage", "Deleted cheat list '%s'."), filename.c_str()); - } - - System::SetCheatList(nullptr); - return true; -} - -void CommonHostInterface::ClearCheatList(bool save_to_file) -{ - if (!System::IsValid()) - return; - - CheatList* cl = System::GetCheatList(); - if (!cl) - return; - - while (cl->GetCodeCount() > 0) - cl->RemoveCode(cl->GetCodeCount() - 1); - - if (save_to_file) - SaveCheatList(); -} - -void CommonHostInterface::SetCheatCodeState(u32 index, bool enabled, bool save_to_file) -{ - if (!System::IsValid() || !System::HasCheatList()) - return; - - CheatList* cl = System::GetCheatList(); - if (index >= cl->GetCodeCount()) - return; - - CheatCode& cc = cl->GetCode(index); - if (cc.enabled == enabled) - return; - - cc.enabled = enabled; - if (!enabled) - cc.ApplyOnDisable(); - - if (enabled) - { - AddFormattedOSDMessage(5.0f, TranslateString("OSDMessage", "Cheat '%s' enabled."), cc.description.c_str()); - } - else - { - AddFormattedOSDMessage(5.0f, TranslateString("OSDMessage", "Cheat '%s' disabled."), cc.description.c_str()); - } - - if (save_to_file) - SaveCheatList(); -} - -void CommonHostInterface::ApplyCheatCode(u32 index) -{ - if (!System::HasCheatList() || index >= System::GetCheatList()->GetCodeCount()) - return; - - const CheatCode& cc = System::GetCheatList()->GetCode(index); - if (!cc.enabled) - { - cc.Apply(); - AddFormattedOSDMessage(5.0f, TranslateString("OSDMessage", "Applied cheat '%s'."), cc.description.c_str()); - } - else - { - AddFormattedOSDMessage(5.0f, TranslateString("OSDMessage", "Cheat '%s' is already enabled."), - cc.description.c_str()); - } -} - -void CommonHostInterface::TogglePostProcessing() -{ - if (!m_display) - return; - - g_settings.display_post_processing = !g_settings.display_post_processing; - if (g_settings.display_post_processing) - { - AddKeyedOSDMessage("PostProcessing", TranslateStdString("OSDMessage", "Post-processing is now enabled."), 10.0f); - - if (!m_display->SetPostProcessingChain(g_settings.display_post_process_chain)) - AddOSDMessage(TranslateStdString("OSDMessage", "Failed to load post processing shader chain."), 20.0f); - } - else - { - AddKeyedOSDMessage("PostProcessing", TranslateStdString("OSDMessage", "Post-processing is now disabled."), 10.0f); - m_display->SetPostProcessingChain({}); - } -} - -void CommonHostInterface::ReloadPostProcessingShaders() -{ - if (!m_display || !g_settings.display_post_processing) - return; - - if (!m_display->SetPostProcessingChain(g_settings.display_post_process_chain)) - AddOSDMessage(TranslateStdString("OSDMessage", "Failed to load post-processing shader chain."), 20.0f); - else - AddOSDMessage(TranslateStdString("OSDMessage", "Post-processing shaders reloaded."), 10.0f); -} - -void CommonHostInterface::ToggleWidescreen() -{ - Panic("Fixme"); -#if 0 - g_settings.gpu_widescreen_hack = !g_settings.gpu_widescreen_hack; - - const GameSettings::Entry* gs = m_game_list->GetGameSettings(System::GetRunningPath(), System::GetRunningCode()); - DisplayAspectRatio user_ratio; - if (gs && gs->display_aspect_ratio.has_value()) - { - user_ratio = gs->display_aspect_ratio.value(); - } - else - { - std::lock_guard guard(m_settings_mutex); - user_ratio = Settings::ParseDisplayAspectRatio( - m_settings_interface - ->GetStringValue("Display", "AspectRatio", - Settings::GetDisplayAspectRatioName(Settings::DEFAULT_DISPLAY_ASPECT_RATIO)) - .c_str()) - .value_or(DisplayAspectRatio::Auto); - } - - if (user_ratio == DisplayAspectRatio::Auto || user_ratio == DisplayAspectRatio::PAR1_1 || - user_ratio == DisplayAspectRatio::R4_3) - { - g_settings.display_aspect_ratio = g_settings.gpu_widescreen_hack ? DisplayAspectRatio::R16_9 : user_ratio; - } - else - { - g_settings.display_aspect_ratio = g_settings.gpu_widescreen_hack ? user_ratio : DisplayAspectRatio::Auto; - } - - if (g_settings.gpu_widescreen_hack) - { - AddKeyedFormattedOSDMessage( - "WidescreenHack", 5.0f, - TranslateString("OSDMessage", "Widescreen hack is now enabled, and aspect ratio is set to %s."), - TranslateString("DisplayAspectRatio", Settings::GetDisplayAspectRatioName(g_settings.display_aspect_ratio)) - .GetCharArray()); - } - else - { - AddKeyedFormattedOSDMessage( - "WidescreenHack", 5.0f, - TranslateString("OSDMessage", "Widescreen hack is now disabled, and aspect ratio is set to %s."), - TranslateString("DisplayAspectRatio", Settings::GetDisplayAspectRatioName(g_settings.display_aspect_ratio)) - .GetCharArray()); - } - - GTE::UpdateAspectRatio(); -#endif -} - -void CommonHostInterface::SwapMemoryCards() -{ - System::SwapMemoryCards(); - - if (System::HasMemoryCard(0) && System::HasMemoryCard(1)) - { - g_host_interface->AddOSDMessage( - g_host_interface->TranslateStdString("OSDMessage", "Swapped memory card ports. Both ports have a memory card."), - 10.0f); - } - else if (System::HasMemoryCard(1)) - { - g_host_interface->AddOSDMessage( - g_host_interface->TranslateStdString("OSDMessage", - "Swapped memory card ports. Port 2 has a memory card, Port 1 is empty."), - 10.0f); - } - else if (System::HasMemoryCard(0)) - { - g_host_interface->AddOSDMessage( - g_host_interface->TranslateStdString("OSDMessage", - "Swapped memory card ports. Port 1 has a memory card, Port 2 is empty."), - 10.0f); - } - else - { - g_host_interface->AddOSDMessage( - g_host_interface->TranslateStdString("OSDMessage", "Swapped memory card ports. Neither port has a memory card."), - 10.0f); - } -} - bool CommonHostInterface::ParseFullscreenMode(const std::string_view& mode, u32* width, u32* height, float* refresh_rate) { @@ -2177,14 +1044,15 @@ bool CommonHostInterface::RequestRenderWindowScale(float scale) if (!System::IsValid() || scale == 0) return false; + HostDisplay* display = Host::GetHostDisplay(); const float y_scale = - (static_cast(m_display->GetDisplayWidth()) / static_cast(m_display->GetDisplayHeight())) / - m_display->GetDisplayAspectRatio(); + (static_cast(display->GetDisplayWidth()) / static_cast(display->GetDisplayHeight())) / + display->GetDisplayAspectRatio(); const u32 requested_width = - std::max(static_cast(std::ceil(static_cast(m_display->GetDisplayWidth()) * scale)), 1); + std::max(static_cast(std::ceil(static_cast(display->GetDisplayWidth()) * scale)), 1); const u32 requested_height = - std::max(static_cast(std::ceil(static_cast(m_display->GetDisplayHeight()) * y_scale * scale)), 1); + std::max(static_cast(std::ceil(static_cast(display->GetDisplayHeight()) * y_scale * scale)), 1); return RequestRenderWindowSize(static_cast(requested_width), static_cast(requested_height)); } @@ -2206,7 +1074,7 @@ std::unique_ptr CommonHostInterface::OpenPackageFile(const char* pat #ifdef WITH_DISCORD_PRESENCE -void CommonHostInterface::SetDiscordPresenceEnabled(bool enabled) +void CommonHost::SetDiscordPresenceEnabled(bool enabled) { if (m_discord_presence_enabled == enabled) return; @@ -2218,7 +1086,7 @@ void CommonHostInterface::SetDiscordPresenceEnabled(bool enabled) ShutdownDiscordPresence(); } -void CommonHostInterface::InitializeDiscordPresence() +void CommonHost::InitializeDiscordPresence() { if (m_discord_presence_active) return; @@ -2230,7 +1098,7 @@ void CommonHostInterface::InitializeDiscordPresence() UpdateDiscordPresence(false); } -void CommonHostInterface::ShutdownDiscordPresence() +void CommonHost::ShutdownDiscordPresence() { if (!m_discord_presence_active) return; @@ -2243,7 +1111,7 @@ void CommonHostInterface::ShutdownDiscordPresence() #endif } -void CommonHostInterface::UpdateDiscordPresence(bool rich_presence_only) +void CommonHost::UpdateDiscordPresence(bool rich_presence_only) { if (!m_discord_presence_active) return; @@ -2301,7 +1169,7 @@ void CommonHostInterface::UpdateDiscordPresence(bool rich_presence_only) Discord_UpdatePresence(&rp); } -void CommonHostInterface::PollDiscordPresence() +void CommonHost::PollDiscordPresence() { if (!m_discord_presence_active) return; @@ -2315,7 +1183,7 @@ void CommonHostInterface::PollDiscordPresence() #ifdef WITH_CHEEVOS -void CommonHostInterface::UpdateCheevosActive(SettingsInterface& si) +void CommonHost::UpdateCheevosActive(SettingsInterface& si) { const bool cheevos_enabled = si.GetBoolValue("Cheevos", "Enabled", false); const bool cheevos_test_mode = si.GetBoolValue("Cheevos", "TestMode", false); @@ -2340,7 +1208,9 @@ void CommonHostInterface::UpdateCheevosActive(SettingsInterface& si) { if (!Cheevos::Initialize(cheevos_test_mode, cheevos_use_first_disc_from_playlist, cheevos_rich_presence, cheevos_hardcore, cheevos_unofficial_test_mode)) - ReportError("Failed to initialize cheevos after settings change."); + { + Host::ReportErrorAsync("Error", "Failed to initialize cheevos after settings change."); + } } } } @@ -2350,7 +1220,6 @@ void CommonHostInterface::UpdateCheevosActive(SettingsInterface& si) BEGIN_HOTKEY_LIST(g_common_hotkeys) END_HOTKEY_LIST() - #if 0 void CommonHostInterface::RegisterGeneralHotkeys() diff --git a/src/frontend-common/common_host_interface.h b/src/frontend-common/common_host_interface.h index 6c31adac4..b5bdc6c83 100644 --- a/src/frontend-common/common_host_interface.h +++ b/src/frontend-common/common_host_interface.h @@ -26,35 +26,6 @@ class SaveStateSelectorUI; class CommonHostInterface : public HostInterface { public: - enum : s32 - { - PER_GAME_SAVE_STATE_SLOTS = 10, - GLOBAL_SAVE_STATE_SLOTS = 10 - }; - struct SaveStateInfo - { - std::string path; - std::time_t timestamp; - s32 slot; - bool global; - }; - - struct ExtendedSaveStateInfo - { - std::string path; - std::string title; - std::string game_code; - std::string media_path; - std::time_t timestamp; - s32 slot; - bool global; - - u32 screenshot_width; - u32 screenshot_height; - std::vector screenshot_data; - }; - - using HostInterface::SaveState; /// Returns the name of the frontend. virtual const char* GetFrontendName() const = 0; @@ -65,16 +36,6 @@ public: /// Runs an event next frame as part of the event loop. virtual void RunLater(std::function func) = 0; - /// Thread-safe settings access. - std::string GetStringSettingValue(const char* section, const char* key, const char* default_value = "") override; - bool GetBoolSettingValue(const char* section, const char* key, bool default_value = false) override; - int GetIntSettingValue(const char* section, const char* key, int default_value = 0) override; - float GetFloatSettingValue(const char* section, const char* key, float default_value = 0.0f) override; - std::vector GetSettingStringList(const char* section, const char* key) override; - - /// Loads new settings and applies them. - virtual void ApplySettings(bool display_osd_messages); - virtual bool IsFullscreen() const; virtual bool SetFullscreen(bool enabled); @@ -82,66 +43,16 @@ public: virtual void Shutdown() override; - virtual bool BootSystem(std::shared_ptr parameters) override; - virtual void ResetSystem() override; - virtual void DestroySystem() override; - /// Returns the game list. ALWAYS_INLINE GameList* GetGameList() const { return m_game_list.get(); } /// Returns true if running in batch mode, i.e. exit after emulation. ALWAYS_INLINE bool InBatchMode() const { return m_flags.batch_mode; } - /// Returns true if an undo load state exists. - ALWAYS_INLINE bool CanUndoLoadState() const { return static_cast(m_undo_load_state); } - /// Parses command line parameters for all frontends. bool ParseCommandLineParameters(int argc, char* argv[], std::unique_ptr* out_boot_params); - /// Powers off the system, optionally saving the resume state. - void PowerOffSystem(bool save_resume_state); - /// Undoes a load state, i.e. restores the state prior to the load. - bool UndoLoadState(); - - /// Loads state from the specified filename. - bool LoadState(const char* filename); - - /// Loads the current emulation state from file. Specifying a slot of -1 loads the "resume" game state. - bool LoadState(bool global, s32 slot); - - /// Saves the current emulation state to a file. Specifying a slot of -1 saves the "resume" save state. - bool SaveState(bool global, s32 slot); - - /// Returns true if the specified file/disc image is resumable. - bool CanResumeSystemFromFile(const char* filename); - - /// Loads the resume save state for the given game. Optionally boots the game anyway if loading fails. - bool ResumeSystemFromState(const char* filename, bool boot_on_failure); - - /// Loads the most recent resume save state. This may be global or per-game. - bool ResumeSystemFromMostRecentState(); - - /// Saves the resume save state, call when shutting down. - bool SaveResumeSaveState(); - - /// Returns a list of save states for the specified game code. - std::vector GetAvailableSaveStates(const char* game_code) const; - - /// Returns save state info if present. If game_code is null or empty, assumes global state. - std::optional GetSaveStateInfo(const char* game_code, s32 slot); - - /// Returns save state info from opened save state stream. - std::optional GetExtendedSaveStateInfo(ByteStream* stream); - - /// Returns save state info if present. If game_code is null or empty, assumes global state. - std::optional GetExtendedSaveStateInfo(const char* game_code, s32 slot); - - /// Returns save state info for the undo slot, if present. - std::optional GetUndoSaveStateInfo(); - - /// 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); /// Displays a loading screen with the logo, rendered with ImGui. Use when executing possibly-time-consuming tasks /// such as compiling shaders when starting up. @@ -151,69 +62,13 @@ public: /// Retrieves information about specified game from game list. void GetGameInfo(const char* path, CDImage* image, std::string* code, std::string* title) override; - /// Returns true if currently dumping audio. - bool IsDumpingAudio() const; - - /// 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(); - - /// Swaps memory cards in slot 1/2. - void SwapMemoryCards(); - /// 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); - /// Returns true if the state should be saved on shutdown. - bool ShouldSaveResumeState() const; - /// Returns true if fast forwarding or slow motion is currently active. - bool IsRunningAtNonStandardSpeed() const; /// Requests the specified size for the render window. Not guaranteed to succeed (e.g. if in fullscreen). virtual bool RequestRenderWindowSize(s32 new_window_width, s32 new_window_height); @@ -231,20 +86,6 @@ public: /// This is the APK for Android builds, or the program directory for standalone builds. virtual std::unique_ptr OpenPackageFile(const char* path, u32 flags) override; - /// Toggles fast forward state. - bool IsFastForwardEnabled() const { return m_fast_forward_enabled; } - void SetFastForwardEnabled(bool enabled); - - /// Toggles turbo state. - bool IsTurboEnabled() const { return m_turbo_enabled; } - void SetTurboEnabled(bool enabled); - - /// Toggles rewind state. - void SetRewindState(bool enabled); - - /// Returns true if features such as save states should be disabled. - bool IsCheevosChallengeModeActive() const; - protected: enum : u32 { @@ -262,71 +103,15 @@ protected: CommonHostInterface(); ~CommonHostInterface(); - /// Executes per-frame tasks such as controller polling. - virtual void PollAndUpdate(); - - /// Saves the undo load state, so it can be restored. - bool SaveUndoLoadState(); - - virtual std::unique_ptr CreateAudioStream(AudioBackend backend) override; - virtual s32 GetAudioOutputVolume() const override; - - virtual void OnSystemCreated() override; - virtual void OnSystemPaused(bool paused) override; - virtual void OnSystemDestroyed() override; - virtual void OnRunningGameChanged(const std::string& path, CDImage* image, const std::string& game_code, - const std::string& game_title) override; - virtual void OnControllerTypeChanged(u32 slot) override; - /// Returns the path of the settings file. std::string GetSettingsFileName() const; - /// Returns the path to a save state file. Specifying an index of -1 is the "resume" save state. - std::string GetGameSaveStateFileName(const char* game_code, s32 slot) const; - - /// Returns the path to a save state file. Specifying an index of -1 is the "resume" save state. - std::string GetGlobalSaveStateFileName(s32 slot) const; - - /// Moves the current save state file to a backup name, if it exists. - void RenameCurrentSaveStateToBackup(const char* filename); - /// Sets the base path for the user directory. Can be overridden by platform/frontend/command line. virtual void SetUserDirectory(); - /// Updates logging settings. - virtual void UpdateLogSettings(LOGLEVEL level, const char* filter, bool log_to_console, bool log_to_debug, - bool log_to_window, bool log_to_file); - - /// Returns the most recent resume save state. - std::string GetMostRecentResumeSaveStatePath() const; - - /// Returns the path to the cheat file for the specified game title. - std::string GetCheatFileName() const; - - /// Restores all settings to defaults. - virtual void SetDefaultSettings(SettingsInterface& si) override; - - /// Resets known settings to default. - virtual void SetDefaultSettings(); - - /// Loads settings to m_settings and any frontend-specific parameters. - virtual void LoadSettings(SettingsInterface& si) override; - - /// Saves current settings variables to ini. - virtual void SaveSettings(SettingsInterface& si) override; - - /// Checks and fixes up any incompatible settings. - virtual void FixIncompatibleSettings(bool display_osd_messages) override; - - /// Checks for settings changes, std::move() the old settings away for comparing beforehand. - virtual void CheckForSettingsChanges(const Settings& old_settings) override; - /// Increases timer resolution when supported by the host OS. void SetTimerResolutionIncreased(bool enabled); - void UpdateSpeedLimiterState(); - - void RecreateSystem() override; void OnHostDisplayResized() override; void ApplyGameSettings(bool display_osd_messages); @@ -336,19 +121,10 @@ protected: bool CreateHostDisplayResources(); void ReleaseHostDisplayResources(); - void DoFrameStep(); - void DoToggleCheats(); - std::unique_ptr m_game_list; std::unique_ptr m_logo_texture; - bool m_frame_step_request = false; - bool m_fast_forward_enabled = false; - bool m_turbo_enabled = false; - bool m_throttler_enabled = true; - bool m_display_all_frames = true; - union { u8 bits; @@ -368,30 +144,18 @@ protected: private: void InitializeUserDirectory(); - -#ifdef WITH_DISCORD_PRESENCE - void SetDiscordPresenceEnabled(bool enabled); - void InitializeDiscordPresence(); - void ShutdownDiscordPresence(); - void UpdateDiscordPresence(bool rich_presence_only); - void PollDiscordPresence(); -#endif - -#ifdef WITH_CHEEVOS - void UpdateCheevosActive(SettingsInterface& si); -#endif - - std::unique_ptr m_save_state_selector_ui; - - // temporary save state, created when loading, used to undo load state - std::unique_ptr m_undo_load_state; - -#ifdef WITH_DISCORD_PRESENCE - // discord rich presence - bool m_discord_presence_enabled = false; - bool m_discord_presence_active = false; -#ifdef WITH_CHEEVOS - std::string m_discord_presence_cheevos_string; -#endif -#endif }; + +namespace CommonHost +{ +void SetDefaultSettings(SettingsInterface& si); +void LoadSettings(SettingsInterface& si, std::unique_lock& lock); +void CheckForSettingsChanges(const Settings& old_settings); +void OnSystemStarting(); +void OnSystemStarted(); +void OnSystemDestroyed(); +void OnSystemPaused(); +void OnSystemResumed(); +void OnGameChanged(const std::string& disc_path, const std::string& game_serial, const std::string& game_name); +void PumpMessagesOnCPUThread(); +} diff --git a/src/frontend-common/d3d11_host_display.cpp b/src/frontend-common/d3d11_host_display.cpp index 756947a24..fed1ae3a5 100644 --- a/src/frontend-common/d3d11_host_display.cpp +++ b/src/frontend-common/d3d11_host_display.cpp @@ -6,6 +6,7 @@ #include "common/string_util.h" #include "common_host_interface.h" #include "core/host_interface.h" +#include "core/host_settings.h" #include "core/settings.h" #include "core/shader_cache_version.h" #include "display_ps.hlsl.h" @@ -395,8 +396,7 @@ bool D3D11HostDisplay::CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode) if (m_window_info.type != WindowInfo::Type::Win32) return false; - m_using_flip_model_swap_chain = - fullscreen_mode || !g_host_interface->GetBoolSettingValue("Display", "UseBlitSwapChain", false); + m_using_flip_model_swap_chain = fullscreen_mode || !Host::GetBoolSettingValue("Display", "UseBlitSwapChain", false); const HWND window_hwnd = reinterpret_cast(m_window_info.window_handle); RECT client_rc{}; diff --git a/src/frontend-common/frontend-common.props b/src/frontend-common/frontend-common.props index f55b5bdba..509b97905 100644 --- a/src/frontend-common/frontend-common.props +++ b/src/frontend-common/frontend-common.props @@ -4,10 +4,10 @@ - $(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\tinyxml2\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) - $(RootBuildDir)simpleini\simpleini.lib;$(RootBuildDir)tinyxml2\tinyxml2.lib;$(RootBuildDir)core\core.lib;$(RootBuildDir)scmversion\scmversion.lib;%(AdditionalDependencies) + $(RootBuildDir)tinyxml2\tinyxml2.lib;$(RootBuildDir)core\core.lib;$(RootBuildDir)scmversion\scmversion.lib;%(AdditionalDependencies) diff --git a/src/frontend-common/frontend-common.vcxproj b/src/frontend-common/frontend-common.vcxproj index 733906f6c..8dc0bff8e 100644 --- a/src/frontend-common/frontend-common.vcxproj +++ b/src/frontend-common/frontend-common.vcxproj @@ -23,7 +23,6 @@ - @@ -65,7 +64,6 @@ - diff --git a/src/frontend-common/frontend-common.vcxproj.filters b/src/frontend-common/frontend-common.vcxproj.filters index 35ea13ced..5ab4c7146 100644 --- a/src/frontend-common/frontend-common.vcxproj.filters +++ b/src/frontend-common/frontend-common.vcxproj.filters @@ -5,7 +5,6 @@ - @@ -42,7 +41,6 @@ - diff --git a/src/frontend-common/fullscreen_ui_progress_callback.cpp b/src/frontend-common/fullscreen_ui_progress_callback.cpp index 3a4ad67fd..efc666487 100644 --- a/src/frontend-common/fullscreen_ui_progress_callback.cpp +++ b/src/frontend-common/fullscreen_ui_progress_callback.cpp @@ -1,6 +1,6 @@ #include "fullscreen_ui_progress_callback.h" #include "common/log.h" -#include "core/host_interface.h" +#include "core/host.h" #include "imgui_fullscreen.h" Log_SetChannel(ProgressCallback); @@ -98,19 +98,18 @@ void ProgressCallback::DisplayDebugMessage(const char* message) void ProgressCallback::ModalError(const char* message) { Log_ErrorPrint(message); - g_host_interface->ReportError(message); + Host::ReportErrorAsync("Error", message); } bool ProgressCallback::ModalConfirmation(const char* message) { Log_InfoPrint(message); - return g_host_interface->ConfirmMessage(message); + return Host::ConfirmMessage("Confirm", message); } void ProgressCallback::ModalInformation(const char* message) { Log_InfoPrint(message); - g_host_interface->ReportMessage(message); } } // namespace FullscreenUI \ No newline at end of file diff --git a/src/frontend-common/game_list.cpp b/src/frontend-common/game_list.cpp index 9d6e5c88d..e4d899e8e 100644 --- a/src/frontend-common/game_list.cpp +++ b/src/frontend-common/game_list.cpp @@ -8,7 +8,7 @@ #include "common/progress_callback.h" #include "common/string_util.h" #include "core/bios.h" -#include "core/host_interface.h" +#include "core/host.h" #include "core/psf_loader.h" #include "core/settings.h" #include "core/system.h" diff --git a/src/frontend-common/game_settings.cpp b/src/frontend-common/game_settings.cpp index 9cc3746d8..a79a5375f 100644 --- a/src/frontend-common/game_settings.cpp +++ b/src/frontend-common/game_settings.cpp @@ -5,7 +5,7 @@ #include "common/log.h" #include "common/string.h" #include "common/string_util.h" -#include "core/host_interface.h" +#include "core/host.h" #include "core/settings.h" #include #include @@ -1266,8 +1266,8 @@ void Entry::ApplySettings(bool display_osd_messages) const { if (display_osd_messages && g_settings.cpu_execution_mode != CPUExecutionMode::Interpreter) { - g_host_interface->AddOSDMessage( - g_host_interface->TranslateStdString("OSDMessage", "CPU interpreter forced by game settings."), osd_duration); + Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "CPU interpreter forced by game settings."), + osd_duration); } g_settings.cpu_execution_mode = CPUExecutionMode::Interpreter; @@ -1277,8 +1277,8 @@ void Entry::ApplySettings(bool display_osd_messages) const { if (display_osd_messages && g_settings.gpu_renderer != GPURenderer::Software) { - g_host_interface->AddOSDMessage( - g_host_interface->TranslateStdString("OSDMessage", "Software renderer forced by game settings."), osd_duration); + Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "Software renderer forced by game settings."), + osd_duration); } g_settings.gpu_renderer = GPURenderer::Software; @@ -1288,8 +1288,7 @@ void Entry::ApplySettings(bool display_osd_messages) const { if (display_osd_messages && g_settings.gpu_disable_interlacing) { - g_host_interface->AddOSDMessage( - g_host_interface->TranslateStdString("OSDMessage", "Interlacing forced by game settings."), osd_duration); + Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "Interlacing forced by game settings."), osd_duration); } g_settings.gpu_disable_interlacing = false; @@ -1299,8 +1298,8 @@ void Entry::ApplySettings(bool display_osd_messages) const { if (display_osd_messages && g_settings.gpu_true_color) { - g_host_interface->AddOSDMessage( - g_host_interface->TranslateStdString("OSDMessage", "True color disabled by game settings."), osd_duration); + Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "True color disabled by game settings."), + osd_duration); } g_settings.gpu_true_color = false; @@ -1310,8 +1309,7 @@ void Entry::ApplySettings(bool display_osd_messages) const { if (display_osd_messages && g_settings.gpu_resolution_scale > 1) { - g_host_interface->AddOSDMessage( - g_host_interface->TranslateStdString("OSDMessage", "Upscaling disabled by game settings."), osd_duration); + Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "Upscaling disabled by game settings."), osd_duration); } g_settings.gpu_resolution_scale = 1; @@ -1321,9 +1319,8 @@ void Entry::ApplySettings(bool display_osd_messages) const { if (display_osd_messages && g_settings.gpu_scaled_dithering) { - g_host_interface->AddOSDMessage( - g_host_interface->TranslateStdString("OSDMessage", "Scaled dithering disabled by game settings."), - osd_duration); + Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "Scaled dithering disabled by game settings."), + osd_duration); } g_settings.gpu_scaled_dithering = false; @@ -1334,8 +1331,8 @@ void Entry::ApplySettings(bool display_osd_messages) const if (display_osd_messages && (g_settings.display_aspect_ratio == DisplayAspectRatio::R16_9 || g_settings.gpu_widescreen_hack)) { - g_host_interface->AddOSDMessage( - g_host_interface->TranslateStdString("OSDMessage", "Widescreen disabled by game settings."), osd_duration); + Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "Widescreen disabled by game settings."), + osd_duration); } g_settings.display_aspect_ratio = DisplayAspectRatio::R4_3; @@ -1346,9 +1343,8 @@ void Entry::ApplySettings(bool display_osd_messages) const { if (display_osd_messages && g_settings.gpu_force_ntsc_timings) { - g_host_interface->AddOSDMessage( - g_host_interface->TranslateStdString("OSDMessage", "Forcing NTSC Timings disallowed by game settings."), - osd_duration); + Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "Forcing NTSC Timings disallowed by game settings."), + osd_duration); } g_settings.gpu_force_ntsc_timings = false; @@ -1358,9 +1354,8 @@ void Entry::ApplySettings(bool display_osd_messages) const { if (display_osd_messages && g_settings.gpu_pgxp_enable) { - g_host_interface->AddOSDMessage( - g_host_interface->TranslateStdString("OSDMessage", "PGXP geometry correction disabled by game settings."), - osd_duration); + Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "PGXP geometry correction disabled by game settings."), + osd_duration); } g_settings.gpu_pgxp_enable = false; @@ -1370,8 +1365,8 @@ void Entry::ApplySettings(bool display_osd_messages) const { if (display_osd_messages && g_settings.gpu_pgxp_enable && g_settings.gpu_pgxp_culling) { - g_host_interface->AddOSDMessage( - g_host_interface->TranslateStdString("OSDMessage", "PGXP culling disabled by game settings."), osd_duration); + Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "PGXP culling disabled by game settings."), + osd_duration); } g_settings.gpu_pgxp_culling = false; @@ -1381,9 +1376,8 @@ void Entry::ApplySettings(bool display_osd_messages) const { if (display_osd_messages && g_settings.gpu_pgxp_enable && g_settings.gpu_pgxp_texture_correction) { - g_host_interface->AddOSDMessage( - g_host_interface->TranslateStdString("OSDMessage", "PGXP texture correction disabled by game settings."), - osd_duration); + Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "PGXP texture correction disabled by game settings."), + osd_duration); } g_settings.gpu_pgxp_texture_correction = false; @@ -1393,8 +1387,8 @@ void Entry::ApplySettings(bool display_osd_messages) const { if (display_osd_messages && g_settings.gpu_pgxp_enable && !g_settings.gpu_pgxp_vertex_cache) { - g_host_interface->AddOSDMessage( - g_host_interface->TranslateStdString("OSDMessage", "PGXP vertex cache forced by game settings."), osd_duration); + Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "PGXP vertex cache forced by game settings."), + osd_duration); } g_settings.gpu_pgxp_vertex_cache = true; @@ -1404,8 +1398,8 @@ void Entry::ApplySettings(bool display_osd_messages) const { if (display_osd_messages && g_settings.gpu_pgxp_enable && !g_settings.gpu_pgxp_cpu) { - g_host_interface->AddOSDMessage( - g_host_interface->TranslateStdString("OSDMessage", "PGXP CPU mode forced by game settings."), osd_duration); + Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "PGXP CPU mode forced by game settings."), + osd_duration); } g_settings.gpu_pgxp_cpu = true; @@ -1415,9 +1409,8 @@ void Entry::ApplySettings(bool display_osd_messages) const { if (display_osd_messages && g_settings.gpu_pgxp_enable && g_settings.gpu_pgxp_depth_buffer) { - g_host_interface->AddOSDMessage( - g_host_interface->TranslateStdString("OSDMessage", "PGXP Depth Buffer disabled by game settings."), - osd_duration); + Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "PGXP Depth Buffer disabled by game settings."), + osd_duration); } g_settings.gpu_pgxp_depth_buffer = false; diff --git a/src/frontend-common/host_settings.cpp b/src/frontend-common/host_settings.cpp index 1e9ef11aa..a4d01a8e5 100644 --- a/src/frontend-common/host_settings.cpp +++ b/src/frontend-common/host_settings.cpp @@ -64,54 +64,6 @@ std::vector Host::GetBaseStringListSetting(const char* section, con return s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->GetStringList(section, key); } -void Host::SetBaseBoolSettingValue(const char* section, const char* key, bool value) -{ - std::unique_lock lock(s_settings_mutex); - s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetBoolValue(section, key, value); -} - -void Host::SetBaseIntSettingValue(const char* section, const char* key, s32 value) -{ - std::unique_lock lock(s_settings_mutex); - s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetIntValue(section, key, value); -} - -void Host::SetBaseUIntSettingValue(const char* section, const char* key, u32 value) -{ - std::unique_lock lock(s_settings_mutex); - s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetUIntValue(section, key, value); -} - -void Host::SetBaseFloatSettingValue(const char* section, const char* key, float value) -{ - std::unique_lock lock(s_settings_mutex); - s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetFloatValue(section, key, value); -} - -void Host::SetBaseStringSettingValue(const char* section, const char* key, const char* value) -{ - std::unique_lock lock(s_settings_mutex); - s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetStringValue(section, key, value); -} - -void Host::SetBaseStringListSettingValue(const char* section, const char* key, const std::vector& values) -{ - std::unique_lock lock(s_settings_mutex); - s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetStringList(section, key, values); -} - -void Host::DeleteBaseSettingValue(const char* section, const char* key) -{ - std::unique_lock lock(s_settings_mutex); - s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->DeleteValue(section, key); -} - -void Host::CommitBaseSettingChanges() -{ - std::unique_lock lock(s_settings_mutex); - s_layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->Save(); -} - std::string Host::GetStringSettingValue(const char* section, const char* key, const char* default_value /*= ""*/) { std::unique_lock lock(s_settings_mutex); diff --git a/src/frontend-common/imgui_fullscreen.cpp b/src/frontend-common/imgui_fullscreen.cpp index ce20d44b7..e72f3cfa9 100644 --- a/src/frontend-common/imgui_fullscreen.cpp +++ b/src/frontend-common/imgui_fullscreen.cpp @@ -240,9 +240,9 @@ std::optional ImGuiFullscreen::LoadTextureImage(const char* std::shared_ptr ImGuiFullscreen::UploadTexture(const char* path, const Common::RGBA8Image& image) { - std::unique_ptr texture = g_host_interface->GetDisplay()->CreateTexture( - image.GetWidth(), image.GetHeight(), 1, 1, 1, HostDisplayPixelFormat::RGBA8, image.GetPixels(), - image.GetByteStride()); + std::unique_ptr texture = + Host::GetHostDisplay()->CreateTexture(image.GetWidth(), image.GetHeight(), 1, 1, 1, HostDisplayPixelFormat::RGBA8, + image.GetPixels(), image.GetByteStride()); if (!texture) { Log_ErrorPrintf("failed to create %ux%u texture for resource", image.GetWidth(), image.GetHeight()); diff --git a/src/frontend-common/imgui_manager.cpp b/src/frontend-common/imgui_manager.cpp index 0d0d23b64..0ef57aef4 100644 --- a/src/frontend-common/imgui_manager.cpp +++ b/src/frontend-common/imgui_manager.cpp @@ -70,8 +70,7 @@ bool ImGuiManager::Initialize() return false; } - // HostDisplay* display = Host::GetHostDisplay(); - HostDisplay* display = g_host_interface->GetDisplay(); + HostDisplay* display = Host::GetHostDisplay(); s_global_scale = std::max(1.0f, display->GetWindowScale() * static_cast(/*EmuConfig.GS.OsdScale*/ 100.0 / 100.0)); @@ -128,7 +127,7 @@ void ImGuiManager::Shutdown() { FullscreenUI::Shutdown(); - HostDisplay* display = g_host_interface->GetDisplay(); + HostDisplay* display = Host::GetHostDisplay(); if (display) display->DestroyImGuiContext(); if (ImGui::GetCurrentContext()) @@ -145,7 +144,7 @@ void ImGuiManager::Shutdown() void ImGuiManager::WindowResized() { - HostDisplay* display = g_host_interface->GetDisplay(); + HostDisplay* display = Host::GetHostDisplay(); const u32 new_width = display ? display->GetWindowWidth() : 0; const u32 new_height = display ? display->GetWindowHeight() : 0; @@ -161,7 +160,7 @@ void ImGuiManager::WindowResized() void ImGuiManager::UpdateScale() { - HostDisplay* display = g_host_interface->GetDisplay(); + HostDisplay* display = Host::GetHostDisplay(); const float window_scale = display ? display->GetWindowScale() : 1.0f; const float scale = std::max(window_scale * static_cast(/*EmuConfig.GS.OsdScale*/ 100.0 / 100.0), 1.0f); @@ -527,7 +526,7 @@ bool ImGuiManager::AddFullscreenFontsIfMissing() AddImGuiFonts(false); } - g_host_interface->GetDisplay()->UpdateImGuiFontTexture(); + Host::GetHostDisplay()->UpdateImGuiFontTexture(); NewFrame(); return HasFullscreenFonts(); @@ -708,9 +707,8 @@ void ImGuiManager::FormatProcessorStat(String& text, double usage, double time) void ImGuiManager::DrawPerformanceOverlay() { if (!(g_settings.display_show_fps | g_settings.display_show_vps | g_settings.display_show_speed | - g_settings.display_show_resolution | System::IsPaused() | - static_cast(g_host_interface)->IsFastForwardEnabled() | - static_cast(g_host_interface)->IsTurboEnabled())) + g_settings.display_show_resolution | System::IsPaused() | System::IsFastForwardEnabled() | + System::IsTurboEnabled())) { return; } @@ -794,8 +792,7 @@ void ImGuiManager::DrawPerformanceOverlay() if (/*g_settings.display_show_cpu*/ true) { text.Clear(); - text.AppendFmtString("{:.2f}ms ({:.2f}ms worst)", System::GetAverageFrameTime(), - System::GetWorstFrameTime()); + text.AppendFmtString("{:.2f}ms ({:.2f}ms worst)", System::GetAverageFrameTime(), System::GetWorstFrameTime()); DRAW_LINE(s_fixed_font, text, IM_COL32(255, 255, 255, 255)); text.Clear(); diff --git a/src/frontend-common/input_manager.cpp b/src/frontend-common/input_manager.cpp index 07ee49139..47c399e65 100644 --- a/src/frontend-common/input_manager.cpp +++ b/src/frontend-common/input_manager.cpp @@ -112,7 +112,6 @@ static bool ParseBindingAndGetSource(const std::string_view& binding, InputBindi static bool IsAxisHandler(const InputEventHandler& handler); -static std::string GetPadSettingsSection(u32 pad); static void AddHotkeyBindings(SettingsInterface& si); static void AddPadBindings(SettingsInterface& si, u32 pad, const char* default_type); static void UpdateContinuedVibration(); @@ -525,11 +524,6 @@ std::vector InputManager::GetHotkeyList() return ret; } -std::string InputManager::GetPadSettingsSection(u32 pad) -{ - return fmt::format("Pad{}", pad + 1u); -} - void InputManager::AddHotkeyBindings(SettingsInterface& si) { for (const HotkeyInfo* hotkey_list : s_hotkey_list) @@ -547,27 +541,27 @@ void InputManager::AddHotkeyBindings(SettingsInterface& si) void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index, const char* default_type) { - const std::string section(GetPadSettingsSection(pad_index)); + const std::string section(Controller::GetSettingsSection(pad_index)); const std::string type(si.GetStringValue(section.c_str(), "Type", default_type)); std::optional ctype = Settings::ParseControllerTypeName(type.c_str()); if (!ctype.has_value() || ctype == ControllerType::None) return; - const std::vector bind_names = Controller::GetControllerBinds(type); - if (!bind_names.empty()) + const Controller::ControllerInfo* cinfo = Controller::GetControllerInfo(ctype.value()); + if (!cinfo) + return; + + for (u32 i = 0; i < cinfo->num_bindings; i++) { - for (u32 bind_index = 0; bind_index < static_cast(bind_names.size()); bind_index++) + const Controller::ControllerBindingInfo& bi = cinfo->bindings[i]; + const std::vector bindings(si.GetStringList(section.c_str(), bi.name)); + if (!bindings.empty()) { - const std::string& bind_name = bind_names[bind_index]; - const std::vector bindings(si.GetStringList(section.c_str(), bind_name.c_str())); - if (!bindings.empty()) - { - AddBindings(bindings, InputAxisEventHandler{[pad_index, bind_index, bind_names](float value) { - Controller* c = System::GetController(pad_index); - if (c) - c->SetBindState(bind_index, value); - }}); - } + AddBindings(bindings, InputAxisEventHandler{[pad_index, bind_index = bi.bind_index](float value) { + Controller* c = System::GetController(pad_index); + if (c) + c->SetBindState(bind_index, value); + }}); } } @@ -875,7 +869,7 @@ void InputManager::SetDefaultConfig(SettingsInterface& si) // Default pad types and parameters. for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) { - const std::string section(GetPadSettingsSection(i)); + const std::string section(Controller::GetSettingsSection(i)); si.ClearSection(section.c_str()); si.SetStringValue(section.c_str(), "Type", Controller::GetDefaultPadType(i)); si.SetFloatValue(section.c_str(), "Deadzone", Controller::DEFAULT_STICK_DEADZONE); @@ -891,11 +885,17 @@ void InputManager::SetDefaultConfig(SettingsInterface& si) si.SetStringValue("Hotkeys", "TogglePause", "Keyboard/Space"); si.SetStringValue("Hotkeys", "ToggleFullscreen", "Keyboard/Alt+Return"); si.SetStringValue("Hotkeys", "Screenshot", "Keyboard/F10"); + + si.SetStringValue("Hotkeys", "PowerOff", "Keyboard/Escape"); + si.SetStringValue("Hotkeys", "LoadSelectedSaveState", "Keyboard/F1"); + si.SetStringValue("Hotkeys", "SaveSelectedSaveState", "Keyboard/F2"); + si.SetStringValue("Hotkeys", "SelectPreviousSaveStateSlot", "Keyboard/F3"); + si.SetStringValue("Hotkeys", "SelectNextSaveStateSlot", "Keyboard/F4"); } void InputManager::ClearPortBindings(SettingsInterface& si, u32 port) { - const std::string section(GetPadSettingsSection(port)); + const std::string section(Controller::GetSettingsSection(port)); const std::string type(si.GetStringValue(section.c_str(), "Type", Controller::GetDefaultPadType(port))); const Controller::ControllerInfo* info = Controller::GetControllerInfo(type); @@ -918,7 +918,7 @@ void InputManager::CopyConfiguration(SettingsInterface* dest_si, const SettingsI for (u32 port = 0; port < NUM_CONTROLLER_AND_CARD_PORTS; port++) { - const std::string section(GetPadSettingsSection(port)); + const std::string section(Controller::GetSettingsSection(port)); const std::string type(src_si.GetStringValue(section.c_str(), "Type", Controller::GetDefaultPadType(port))); if (copy_pad_config) dest_si->SetStringValue(section.c_str(), "Type", type.c_str()); @@ -996,7 +996,7 @@ static u32 TryMapGenericMapping(SettingsInterface& si, const std::string& sectio bool InputManager::MapController(SettingsInterface& si, u32 controller, const std::vector>& mapping) { - const std::string section(GetPadSettingsSection(controller)); + const std::string section(Controller::GetSettingsSection(controller)); const std::string type(si.GetStringValue(section.c_str(), "Type", Controller::GetDefaultPadType(controller))); const Controller::ControllerInfo* info = Controller::GetControllerInfo(type); if (!info) @@ -1027,14 +1027,12 @@ bool InputManager::MapController(SettingsInterface& si, u32 controller, return (num_mappings > 0); } - std::string InputManager::GetInputProfilePath(const std::string_view& name) { Panic("Fixme"); return {}; } - std::vector InputManager::GetInputProfileNames() { #if 0 diff --git a/src/frontend-common/save_state_selector_ui.cpp b/src/frontend-common/save_state_selector_ui.cpp index 8001af8cc..80b3de746 100644 --- a/src/frontend-common/save_state_selector_ui.cpp +++ b/src/frontend-common/save_state_selector_ui.cpp @@ -1,7 +1,10 @@ #include "save_state_selector_ui.h" #include "common/log.h" #include "common/string_util.h" +#include "core/cheevos.h" +#include "core/host.h" #include "core/host_display.h" +#include "core/host_settings.h" #include "core/system.h" #include "fmt/chrono.h" #include "fmt/format.h" @@ -48,10 +51,9 @@ void SaveStateSelectorUI::RefreshList() if (!System::GetRunningCode().empty()) { - for (s32 i = 1; i <= CommonHostInterface::PER_GAME_SAVE_STATE_SLOTS; i++) + for (s32 i = 1; i <= System::PER_GAME_SAVE_STATE_SLOTS; i++) { - std::optional ssi = - m_host_interface->GetExtendedSaveStateInfo(System::GetRunningCode().c_str(), i); + std::optional ssi = System::GetExtendedSaveStateInfo(System::GetRunningCode().c_str(), i); ListEntry li; if (ssi) @@ -63,10 +65,9 @@ void SaveStateSelectorUI::RefreshList() } } - for (s32 i = 1; i <= CommonHostInterface::GLOBAL_SAVE_STATE_SLOTS; i++) + for (s32 i = 1; i <= System::GLOBAL_SAVE_STATE_SLOTS; i++) { - std::optional ssi = - m_host_interface->GetExtendedSaveStateInfo(nullptr, i); + std::optional ssi = System::GetExtendedSaveStateInfo(nullptr, i); ListEntry li; if (ssi) @@ -97,14 +98,14 @@ void SaveStateSelectorUI::RefreshHotkeyLegend() static_cast(caption.size()), caption.data()); }; - m_load_legend = format_legend_entry(m_host_interface->GetStringSettingValue("Hotkeys", "LoadSelectedSaveState"), - m_host_interface->TranslateStdString("SaveStateSelectorUI", "Load")); - m_save_legend = format_legend_entry(m_host_interface->GetStringSettingValue("Hotkeys", "SaveSelectedSaveState"), - m_host_interface->TranslateStdString("SaveStateSelectorUI", "Save")); - m_prev_legend = format_legend_entry(m_host_interface->GetStringSettingValue("Hotkeys", "SelectPreviousSaveStateSlot"), - m_host_interface->TranslateStdString("SaveStateSelectorUI", "Select Previous")); - m_next_legend = format_legend_entry(m_host_interface->GetStringSettingValue("Hotkeys", "SelectNextSaveStateSlot"), - m_host_interface->TranslateStdString("SaveStateSelectorUI", "Select Next")); + m_load_legend = format_legend_entry(Host::GetStringSettingValue("Hotkeys", "LoadSelectedSaveState"), + Host::TranslateStdString("SaveStateSelectorUI", "Load")); + m_save_legend = format_legend_entry(Host::GetStringSettingValue("Hotkeys", "SaveSelectedSaveState"), + Host::TranslateStdString("SaveStateSelectorUI", "Save")); + m_prev_legend = format_legend_entry(Host::GetStringSettingValue("Hotkeys", "SelectPreviousSaveStateSlot"), + Host::TranslateStdString("SaveStateSelectorUI", "Select Previous")); + m_next_legend = format_legend_entry(Host::GetStringSettingValue("Hotkeys", "SelectNextSaveStateSlot"), + Host::TranslateStdString("SaveStateSelectorUI", "Select Next")); } const char* SaveStateSelectorUI::GetSelectedStatePath() const @@ -142,7 +143,7 @@ void SaveStateSelectorUI::SelectPreviousSlot() (m_current_selection == 0) ? (static_cast(m_slots.size()) - 1u) : (m_current_selection - 1); } -void SaveStateSelectorUI::InitializeListEntry(ListEntry* li, CommonHostInterface::ExtendedSaveStateInfo* ssi) +void SaveStateSelectorUI::InitializeListEntry(ListEntry* li, ExtendedSaveStateInfo* ssi) { li->title = std::move(ssi->title); li->game_code = std::move(ssi->game_code); @@ -154,15 +155,15 @@ void SaveStateSelectorUI::InitializeListEntry(ListEntry* li, CommonHostInterface li->preview_texture.reset(); if (ssi && !ssi->screenshot_data.empty()) { - li->preview_texture = m_host_interface->GetDisplay()->CreateTexture( + li->preview_texture = Host::GetHostDisplay()->CreateTexture( ssi->screenshot_width, ssi->screenshot_height, 1, 1, 1, HostDisplayPixelFormat::RGBA8, ssi->screenshot_data.data(), sizeof(u32) * ssi->screenshot_width, false); } else { - li->preview_texture = m_host_interface->GetDisplay()->CreateTexture( - PLACEHOLDER_ICON_WIDTH, PLACEHOLDER_ICON_HEIGHT, 1, 1, 1, HostDisplayPixelFormat::RGBA8, PLACEHOLDER_ICON_DATA, - sizeof(u32) * PLACEHOLDER_ICON_WIDTH, false); + li->preview_texture = Host::GetHostDisplay()->CreateTexture(PLACEHOLDER_ICON_WIDTH, PLACEHOLDER_ICON_HEIGHT, 1, 1, + 1, HostDisplayPixelFormat::RGBA8, PLACEHOLDER_ICON_DATA, + sizeof(u32) * PLACEHOLDER_ICON_WIDTH, false); } if (!li->preview_texture) @@ -171,26 +172,26 @@ void SaveStateSelectorUI::InitializeListEntry(ListEntry* li, CommonHostInterface std::pair SaveStateSelectorUI::GetSlotTypeFromSelection(u32 selection) const { - if (selection < CommonHostInterface::PER_GAME_SAVE_STATE_SLOTS) + if (selection < System::PER_GAME_SAVE_STATE_SLOTS) { return {selection + 1, false}; } - return {selection - CommonHostInterface::PER_GAME_SAVE_STATE_SLOTS + 1, true}; + return {selection - System::PER_GAME_SAVE_STATE_SLOTS + 1, true}; } void SaveStateSelectorUI::InitializePlaceholderListEntry(ListEntry* li, s32 slot, bool global) { - li->title = m_host_interface->TranslateStdString("SaveStateSelectorUI", "No Save State"); + li->title = Host::TranslateStdString("SaveStateSelectorUI", "No Save State"); std::string().swap(li->game_code); std::string().swap(li->path); std::string().swap(li->formatted_timestamp); li->slot = slot; li->global = global; - li->preview_texture = m_host_interface->GetDisplay()->CreateTexture( - PLACEHOLDER_ICON_WIDTH, PLACEHOLDER_ICON_HEIGHT, 1, 1, 1, HostDisplayPixelFormat::RGBA8, PLACEHOLDER_ICON_DATA, - sizeof(u32) * PLACEHOLDER_ICON_WIDTH, false); + li->preview_texture = Host::GetHostDisplay()->CreateTexture(PLACEHOLDER_ICON_WIDTH, PLACEHOLDER_ICON_HEIGHT, 1, 1, 1, + HostDisplayPixelFormat::RGBA8, PLACEHOLDER_ICON_DATA, + sizeof(u32) * PLACEHOLDER_ICON_WIDTH, false); if (!li->preview_texture) Log_ErrorPrintf("Failed to upload save state image to GPU"); @@ -252,16 +253,15 @@ void SaveStateSelectorUI::Draw() if (entry.global) { - ImGui::Text(m_host_interface->TranslateString("SaveStateSelectorUI", "Global Slot %d"), entry.slot); + ImGui::Text(Host::TranslateString("SaveStateSelectorUI", "Global Slot %d"), entry.slot); } else if (entry.game_code.empty()) { - ImGui::Text(m_host_interface->TranslateString("SaveStateSelectorUI", "Game Slot %d"), entry.slot); + ImGui::Text(Host::TranslateString("SaveStateSelectorUI", "Game Slot %d"), entry.slot); } else { - ImGui::Text(m_host_interface->TranslateString("SaveStateSelectorUI", "%s Slot %d"), entry.game_code.c_str(), - entry.slot); + ImGui::Text(Host::TranslateString("SaveStateSelectorUI", "%s Slot %d"), entry.game_code.c_str(), entry.slot); } ImGui::TextUnformatted(entry.title.c_str()); ImGui::TextUnformatted(entry.formatted_timestamp.c_str()); @@ -279,7 +279,7 @@ void SaveStateSelectorUI::Draw() ImGui::SetCursorPosX(padding); ImGui::BeginTable("table", 2); - const bool hide_load_button = m_host_interface->IsCheevosChallengeModeActive(); + const bool hide_load_button = Cheevos::IsChallengeModeActive(); ImGui::TableNextColumn(); ImGui::TextUnformatted(!hide_load_button ? m_load_legend.c_str() : m_save_legend.c_str()); ImGui::TableNextColumn(); @@ -309,14 +309,14 @@ void SaveStateSelectorUI::Draw() void SaveStateSelectorUI::LoadCurrentSlot() { const auto slot_info = GetSlotTypeFromSelection(m_current_selection); - m_host_interface->LoadState(slot_info.second, slot_info.first); + System::LoadStateFromSlot(slot_info.second, slot_info.first); Close(); } void SaveStateSelectorUI::SaveCurrentSlot() { const auto slot_info = GetSlotTypeFromSelection(m_current_selection); - m_host_interface->SaveState(slot_info.second, slot_info.first); + System::SaveStateToSlot(slot_info.second, slot_info.first); Close(); } diff --git a/src/frontend-common/save_state_selector_ui.h b/src/frontend-common/save_state_selector_ui.h index dbb88ebaf..1b8f1a0b7 100644 --- a/src/frontend-common/save_state_selector_ui.h +++ b/src/frontend-common/save_state_selector_ui.h @@ -5,6 +5,8 @@ class HostDisplayTexture; +struct ExtendedSaveStateInfo; + namespace FrontendCommon { class SaveStateSelectorUI @@ -50,7 +52,7 @@ private: }; void InitializePlaceholderListEntry(ListEntry* li, s32 slot, bool global); - void InitializeListEntry(ListEntry* li, CommonHostInterface::ExtendedSaveStateInfo* ssi); + void InitializeListEntry(ListEntry* li, ExtendedSaveStateInfo* ssi); std::pair GetSlotTypeFromSelection(u32 selection) const; std::string m_load_legend; diff --git a/src/frontend-common/ini_settings_interface.cpp b/src/util/ini_settings_interface.cpp similarity index 100% rename from src/frontend-common/ini_settings_interface.cpp rename to src/util/ini_settings_interface.cpp diff --git a/src/frontend-common/ini_settings_interface.h b/src/util/ini_settings_interface.h similarity index 100% rename from src/frontend-common/ini_settings_interface.h rename to src/util/ini_settings_interface.h diff --git a/src/util/util.props b/src/util/util.props index 6784be997..641511f68 100644 --- a/src/util/util.props +++ b/src/util/util.props @@ -4,13 +4,13 @@ - $(SolutionDir)dep\libsamplerate\include;$(SolutionDir)dep\libchdr\include;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\libsamplerate\include;$(SolutionDir)dep\libchdr\include;%(AdditionalIncludeDirectories) - $(RootBuildDir)libchdr\libchdr.lib;$(RootBuildDir)libsamplerate\libsamplerate.lib;%(AdditionalDependencies) + $(RootBuildDir)simpleini\simpleini.lib;$(RootBuildDir)libchdr\libchdr.lib;$(RootBuildDir)libsamplerate\libsamplerate.lib;%(AdditionalDependencies) diff --git a/src/util/util.vcxproj b/src/util/util.vcxproj index d8a455f4e..135c3362a 100644 --- a/src/util/util.vcxproj +++ b/src/util/util.vcxproj @@ -6,6 +6,7 @@ + @@ -33,6 +34,7 @@ + diff --git a/src/util/util.vcxproj.filters b/src/util/util.vcxproj.filters index 86e36285e..d2f5a56d3 100644 --- a/src/util/util.vcxproj.filters +++ b/src/util/util.vcxproj.filters @@ -16,6 +16,7 @@ + @@ -42,5 +43,6 @@ + \ No newline at end of file