diff --git a/src/common/assert.cpp b/src/common/assert.cpp index bcd3aa054..e96132b2a 100644 --- a/src/common/assert.cpp +++ b/src/common/assert.cpp @@ -3,6 +3,7 @@ #include "common/arch.h" #include "common/assert.h" +#include "emulator.h" #if defined(ARCH_X86_64) #define Crash() __asm__ __volatile__("int $3") @@ -13,13 +14,12 @@ #endif void assert_fail_impl() { - Common::Log::Flush(); + Common::Singleton::Instance()->Shutdown(); Crash(); } [[noreturn]] void unreachable_impl() { - Common::Log::Flush(); - Crash(); + assert_fail_impl(); throw std::runtime_error("Unreachable code"); } diff --git a/src/core/libraries/kernel/threads/exception.cpp b/src/core/libraries/kernel/threads/exception.cpp index fc32ee705..0ac820c86 100644 --- a/src/core/libraries/kernel/threads/exception.cpp +++ b/src/core/libraries/kernel/threads/exception.cpp @@ -548,7 +548,7 @@ int PS4_SYSV_ABI sceKernelRaiseException(PthreadT thread, int signum) { return ret; } -s32 PS4_SYSV_ABI sceKernelDebugRaiseException(s32 error, s64 unk) { +s32 PS4_SYSV_ABI sceKernelDebugRaiseException(u32 error, s64 unk) { if (unk != 0) { return ORBIS_KERNEL_ERROR_EINVAL; } @@ -556,7 +556,7 @@ s32 PS4_SYSV_ABI sceKernelDebugRaiseException(s32 error, s64 unk) { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceKernelDebugRaiseExceptionOnReleaseMode(s32 error, s64 unk) { +s32 PS4_SYSV_ABI sceKernelDebugRaiseExceptionOnReleaseMode(u32 error, s64 unk) { if (unk != 0) { return ORBIS_KERNEL_ERROR_EINVAL; } diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index 6b8ccfcd8..1d2039a73 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -556,18 +556,12 @@ int PS4_SYSV_ABI scePadResetLightBar(s32 handle) { s32 colour_index = u ? u->user_color - 1 : 0; Input::Colour colour{255, 0, 0}; if (colour_index >= 0 && colour_index <= 3) { - static constexpr Input::Colour colours[4]{ - {0, 0, 255}, // blue - {255, 0, 0}, // red - {0, 255, 0}, // green - {255, 0, 255}, // pink - }; - colour = colours[colour_index]; + colour = Input::g_user_colours[colour_index]; } else { LOG_ERROR(Lib_Pad, "Invalid user colour value {} for controller {}, falling back to blue", colour_index, handle); } - controller.SetLightBarRGB(colour.r, colour.g, colour.b); + controller.SetLightBarRGB(colour); return ORBIS_OK; } diff --git a/src/core/signals.cpp b/src/core/signals.cpp index c4db17808..4763c4606 100644 --- a/src/core/signals.cpp +++ b/src/core/signals.cpp @@ -7,6 +7,7 @@ #include "common/signal_context.h" #include "core/libraries/kernel/threads/exception.h" #include "core/signals.h" +#include "emulator.h" #ifdef _WIN32 #include @@ -52,10 +53,6 @@ static LONG WINAPI SignalHandler(EXCEPTION_POINTERS* pExp) noexcept { case DBG_PRINTEXCEPTION_WIDE_C: // Used by OutputDebugString functions. return EXCEPTION_CONTINUE_EXECUTION; - case EXCEPTION_BREAKPOINT: - // This is almost certainly coming from our asserts/unreachables, no need to log it again. - Common::Log::Flush(); - return EXCEPTION_CONTINUE_SEARCH; default: break; } @@ -64,8 +61,11 @@ static LONG WINAPI SignalHandler(EXCEPTION_POINTERS* pExp) noexcept { return EXCEPTION_CONTINUE_EXECUTION; } - LOG_CRITICAL(Debug, "Unhandled Exception code {:#x} at {}", code, address); - Common::Log::Flush(); + // Breakpoints almost certainly come from our asserts/unreachables, no need to log it again. + if (code != EXCEPTION_BREAKPOINT) { + LOG_CRITICAL(Debug, "Unhandled Exception code {:#x} at {}", code, address); + Common::Singleton::Instance()->Shutdown(); + } return EXCEPTION_CONTINUE_SEARCH; } diff --git a/src/emulator.cpp b/src/emulator.cpp index 2edf2a334..4bbfdafff 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -57,6 +57,8 @@ Frontend::WindowSDL* g_window = nullptr; namespace Core { +std::mutex exit_mutex{}; + Emulator::Emulator() { // Initialize NT API functions, set high priority and disable WER #ifdef _WIN32 @@ -68,10 +70,26 @@ Emulator::Emulator() { WSADATA wsaData; WSAStartup(versionWanted, &wsaData); #endif + std::at_quick_exit([]() { Common::Singleton::Instance()->Shutdown(); }); } Emulator::~Emulator() {} +void Emulator::Shutdown() { + static bool exit_done = false; + std::scoped_lock l{exit_mutex}; + if (exit_done) { + return; + } + Common::Log::Flush(); + if (controllers) { + controllers->ResetLightbarColors(); + // need to give SDL time to do this before the runtime exits + std::this_thread::sleep_for(std::chrono::milliseconds{10}); + } + exit_done = true; +} + s32 ReadCompiledSdkVersion(const std::filesystem::path& file) { Core::Loader::Elf elf; elf.Open(file); diff --git a/src/emulator.h b/src/emulator.h index ab39019e9..3395a6c0d 100644 --- a/src/emulator.h +++ b/src/emulator.h @@ -29,6 +29,7 @@ public: void Run(std::filesystem::path file, std::vector args = {}, std::optional game_folder = {}); void UpdatePlayTime(const std::string& serial); + void Shutdown(); /** * This will kill the current process and launch a new process with the same configuration diff --git a/src/input/controller.cpp b/src/input/controller.cpp index 4042525f8..5cbf01aad 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -150,7 +150,7 @@ void GameController::UpdateAxisSmoothing() { m_state.UpdateAxisSmoothing(); } -void GameController::SetLightBarRGB(u8 r, u8 g, u8 b) { +void GameController::SetLightBarRGB(u8 const r, u8 const g, u8 const b) { if (override_colour.has_value()) { return; } @@ -160,6 +160,10 @@ void GameController::SetLightBarRGB(u8 r, u8 g, u8 b) { } } +void GameController::SetLightBarRGB(Colour const c) { + SetLightBarRGB(c.r, c.g, c.b); +} + Colour GameController::GetLightBarRGB() { return colour; } @@ -170,6 +174,22 @@ void GameController::PollLightColour() { } } +void GameControllers::ResetLightbarColors() { + for (auto& c : controllers) { + auto const* u = UserManagement.GetUserByID(c->user_id); + if (!u || !c->m_sdl_gamepad) { + continue; + } + auto const i = u->user_color - 1; + if (i < 0 || i > 3) { + continue; + } + auto const& col = g_user_colours[i]; + c->override_colour = std::nullopt; + c->SetLightBarRGB(col); + } +} + bool GameController::SetVibration(u8 smallMotor, u8 largeMotor) { if (m_sdl_gamepad != nullptr) { return SDL_RumbleGamepad(m_sdl_gamepad, (smallMotor / 255.0f) * 0xFFFF, diff --git a/src/input/controller.h b/src/input/controller.h index 5b65ad53c..2a2e13524 100644 --- a/src/input/controller.h +++ b/src/input/controller.h @@ -43,6 +43,12 @@ struct TouchpadEntry { struct Colour { u8 r, g, b; }; +static constexpr Input::Colour g_user_colours[4]{ + {0, 0, 255}, // blue + {255, 0, 0}, // red + {0, 255, 0}, // green + {255, 0, 255}, // pink +}; struct State { private: @@ -127,7 +133,8 @@ public: void UpdateGyro(const float gyro[3]); void UpdateAcceleration(const float acceleration[3]); void UpdateAxisSmoothing(); - void SetLightBarRGB(u8 r, u8 g, u8 b); + void SetLightBarRGB(u8 const r, u8 const g, u8 const b); + void SetLightBarRGB(Colour const c); Colour GetLightBarRGB(); void PollLightColour(); bool SetVibration(u8 smallMotor, u8 largeMotor); @@ -205,6 +212,7 @@ public: controllers[i]->SetLightBarRGB(r, g, b); controllers[i]->override_colour = {r, g, b}; } + void ResetLightbarColors(); }; } // namespace Input diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b12850aee..14f580c16 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -25,6 +25,7 @@ set(SETTINGS_TEST_SOURCES # Stubs that replace dependencies stubs/common_stub.cpp + stubs/core_stub.cpp stubs/scm_rev_stub.cpp stubs/sdl_stub.cpp @@ -115,6 +116,8 @@ set(GCN_TEST_SOURCES stubs/common_stub.cpp stubs/resource_tracking_pass_stub.cpp stubs/scm_rev_stub.cpp + stubs/sdl_stub.cpp + stubs/core_stub.cpp gcn/gcn_test_runner.hpp gcn/gcn_test_runner.cpp @@ -226,6 +229,7 @@ set(HTTP_TEST_SOURCES stubs/scm_rev_stub.cpp stubs/sdl_stub.cpp stubs/loader_stub.cpp + stubs/core_stub.cpp stubs/kernel_stub.cpp # Tests diff --git a/tests/stubs/core_stub.cpp b/tests/stubs/core_stub.cpp new file mode 100644 index 000000000..b324d7942 --- /dev/null +++ b/tests/stubs/core_stub.cpp @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "emulator.h" + +namespace Core { + +Emulator::Emulator() {} +Emulator::~Emulator() {} +void Emulator::Shutdown() {} + +} // namespace Core diff --git a/tests/stubs/sdl_stub.cpp b/tests/stubs/sdl_stub.cpp index 859147696..8e637c91d 100644 --- a/tests/stubs/sdl_stub.cpp +++ b/tests/stubs/sdl_stub.cpp @@ -2,17 +2,23 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include "sdl_window.h" + +namespace Frontend { +WindowSDL::~WindowSDL() = default; +} extern "C" { - bool SDL_ShowMessageBox(const SDL_MessageBoxData* /* messageboxdata */, int* buttonid) { - if (buttonid) *buttonid = 0; // "No",skip migration - return true; - } +bool SDL_ShowMessageBox(const SDL_MessageBoxData* /* messageboxdata */, int* buttonid) { + if (buttonid) + *buttonid = 0; // "No",skip migration + return true; +} - bool SDL_ShowSimpleMessageBox(SDL_MessageBoxFlags /* flags */, const char* /* title */, - const char* /* message */, SDL_Window* /* window */) { - return true; - } +bool SDL_ShowSimpleMessageBox(SDL_MessageBoxFlags /* flags */, const char* /* title */, + const char* /* message */, SDL_Window* /* window */) { + return true; +} } // extern "C"