duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

system.cpp (188369B)


      1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
      3 
      4 #include "system.h"
      5 #include "achievements.h"
      6 #include "bios.h"
      7 #include "bus.h"
      8 #include "cdrom.h"
      9 #include "cheats.h"
     10 #include "controller.h"
     11 #include "cpu_code_cache.h"
     12 #include "cpu_core.h"
     13 #include "cpu_pgxp.h"
     14 #include "dma.h"
     15 #include "fullscreen_ui.h"
     16 #include "game_database.h"
     17 #include "game_list.h"
     18 #include "gpu.h"
     19 #include "gte.h"
     20 #include "host.h"
     21 #include "host_interface_progress_callback.h"
     22 #include "imgui_overlays.h"
     23 #include "interrupt_controller.h"
     24 #include "mdec.h"
     25 #include "memory_card.h"
     26 #include "multitap.h"
     27 #include "pad.h"
     28 #include "pcdrv.h"
     29 #include "psf_loader.h"
     30 #include "save_state_version.h"
     31 #include "sio.h"
     32 #include "spu.h"
     33 #include "texture_replacements.h"
     34 #include "timers.h"
     35 
     36 #include "scmversion/scmversion.h"
     37 
     38 #include "util/audio_stream.h"
     39 #include "util/cd_image.h"
     40 #include "util/gpu_device.h"
     41 #include "util/imgui_manager.h"
     42 #include "util/ini_settings_interface.h"
     43 #include "util/input_manager.h"
     44 #include "util/iso_reader.h"
     45 #include "util/media_capture.h"
     46 #include "util/platform_misc.h"
     47 #include "util/postprocessing.h"
     48 #include "util/sockets.h"
     49 #include "util/state_wrapper.h"
     50 
     51 #include "common/align.h"
     52 #include "common/binary_reader_writer.h"
     53 #include "common/dynamic_library.h"
     54 #include "common/error.h"
     55 #include "common/file_system.h"
     56 #include "common/layered_settings_interface.h"
     57 #include "common/log.h"
     58 #include "common/path.h"
     59 #include "common/string_util.h"
     60 #include "common/threading.h"
     61 
     62 #include "IconsEmoji.h"
     63 #include "IconsFontAwesome5.h"
     64 
     65 #include "cpuinfo.h"
     66 #include "fmt/chrono.h"
     67 #include "fmt/format.h"
     68 #include "imgui.h"
     69 #include "xxhash.h"
     70 
     71 #include <cctype>
     72 #include <cinttypes>
     73 #include <cmath>
     74 #include <cstdio>
     75 #include <deque>
     76 #include <fstream>
     77 #include <limits>
     78 #include <thread>
     79 #include <zlib.h>
     80 #include <zstd.h>
     81 #include <zstd_errors.h>
     82 
     83 Log_SetChannel(System);
     84 
     85 #ifdef _WIN32
     86 #include "common/windows_headers.h"
     87 #include <Objbase.h>
     88 #include <mmsystem.h>
     89 #include <objbase.h>
     90 #endif
     91 
     92 #ifndef __ANDROID__
     93 #define ENABLE_DISCORD_PRESENCE 1
     94 #define ENABLE_PINE_SERVER 1
     95 #define ENABLE_GDB_SERVER 1
     96 #define ENABLE_SOCKET_MULTIPLEXER 1
     97 #include "gdb_server.h"
     98 #include "pine_server.h"
     99 #endif
    100 
    101 // #define PROFILE_MEMORY_SAVE_STATES 1
    102 
    103 SystemBootParameters::SystemBootParameters() = default;
    104 
    105 SystemBootParameters::SystemBootParameters(const SystemBootParameters&) = default;
    106 
    107 SystemBootParameters::SystemBootParameters(SystemBootParameters&& other) = default;
    108 
    109 SystemBootParameters::SystemBootParameters(std::string filename_) : filename(std::move(filename_))
    110 {
    111 }
    112 
    113 SystemBootParameters::~SystemBootParameters() = default;
    114 
    115 namespace System {
    116 /// Memory save states - only for internal use.
    117 namespace {
    118 struct SaveStateBuffer
    119 {
    120   std::string serial;
    121   std::string title;
    122   std::string media_path;
    123   u32 media_subimage_index;
    124   u32 version;
    125   RGBA8Image screenshot;
    126   DynamicHeapArray<u8> state_data;
    127   size_t state_size;
    128 };
    129 struct MemorySaveState
    130 {
    131   std::unique_ptr<GPUTexture> vram_texture;
    132   DynamicHeapArray<u8> state_data;
    133 #ifdef PROFILE_MEMORY_SAVE_STATES
    134   size_t state_size;
    135 #endif
    136 };
    137 } // namespace
    138 
    139 static void CheckCacheLineSize();
    140 static void LogStartupInformation();
    141 
    142 static LayeredSettingsInterface GetControllerSettingsLayers(std::unique_lock<std::mutex>& lock);
    143 static LayeredSettingsInterface GetHotkeySettingsLayer(std::unique_lock<std::mutex>& lock);
    144 
    145 static std::string GetExecutableNameForImage(IsoReader& iso, bool strip_subdirectories);
    146 static bool ReadExecutableFromImage(IsoReader& iso, std::string* out_executable_name,
    147                                     std::vector<u8>* out_executable_data);
    148 static GameHash GetGameHashFromBuffer(std::string_view exe_name, std::span<const u8> exe_buffer,
    149                                       const IsoReader::ISOPrimaryVolumeDescriptor& iso_pvd, u32 track_1_length);
    150 
    151 /// Checks for settings changes, std::move() the old settings away for comparing beforehand.
    152 static void CheckForSettingsChanges(const Settings& old_settings);
    153 static void WarnAboutUnsafeSettings();
    154 static void LogUnsafeSettingsToConsole(const SmallStringBase& messages);
    155 
    156 static bool Initialize(bool force_software_renderer, Error* error);
    157 static bool LoadBIOS(Error* error);
    158 static bool SetBootMode(BootMode new_boot_mode, Error* error);
    159 static void InternalReset();
    160 static void ClearRunningGame();
    161 static void DestroySystem();
    162 
    163 static bool CreateGPU(GPURenderer renderer, bool is_switching, Error* error);
    164 static bool RecreateGPU(GPURenderer renderer, bool force_recreate_device = false, bool update_display = true);
    165 
    166 /// Updates the throttle period, call when target emulation speed changes.
    167 static void UpdateThrottlePeriod();
    168 static void ResetThrottler();
    169 
    170 /// Throttles the system, i.e. sleeps until it's time to execute the next frame.
    171 static void Throttle(Common::Timer::Value current_time);
    172 static void UpdatePerformanceCounters();
    173 static void AccumulatePreFrameSleepTime();
    174 static void UpdatePreFrameSleepTime();
    175 static void UpdateDisplayVSync();
    176 static void ResetPerformanceCounters();
    177 
    178 static bool UpdateGameSettingsLayer();
    179 static void UpdateRunningGame(const std::string_view path, CDImage* image, bool booting);
    180 static bool CheckForSBIFile(CDImage* image, Error* error);
    181 
    182 static void UpdateControllers();
    183 static void ResetControllers();
    184 static void UpdatePerGameMemoryCards();
    185 static std::unique_ptr<MemoryCard> GetMemoryCardForSlot(u32 slot, MemoryCardType type);
    186 static void UpdateMultitaps();
    187 
    188 /// Returns the maximum size of a save state, considering the current configuration.
    189 static size_t GetMaxSaveStateSize();
    190 
    191 static std::string GetMediaPathFromSaveState(const char* path);
    192 static bool SaveUndoLoadState();
    193 static void UpdateMemorySaveStateSettings();
    194 static bool LoadRewindState(u32 skip_saves = 0, bool consume_state = true);
    195 static bool SaveMemoryState(MemorySaveState* mss);
    196 static bool LoadMemoryState(const MemorySaveState& mss);
    197 static bool LoadStateFromBuffer(const SaveStateBuffer& buffer, Error* error, bool update_display);
    198 static bool LoadStateBufferFromFile(SaveStateBuffer* buffer, std::FILE* fp, Error* error, bool read_title,
    199                                     bool read_media_path, bool read_screenshot, bool read_data);
    200 static bool ReadAndDecompressStateData(std::FILE* fp, std::span<u8> dst, u32 file_offset, u32 compressed_size,
    201                                        SAVE_STATE_HEADER::CompressionType method, Error* error);
    202 static bool SaveStateToBuffer(SaveStateBuffer* buffer, Error* error, u32 screenshot_size = 256);
    203 static bool SaveStateBufferToFile(const SaveStateBuffer& buffer, std::FILE* fp, Error* error,
    204                                   SaveStateCompressionMode compression_mode);
    205 static u32 CompressAndWriteStateData(std::FILE* fp, std::span<const u8> src, SaveStateCompressionMode method,
    206                                      u32* header_type, Error* error);
    207 static bool DoState(StateWrapper& sw, GPUTexture** host_texture, bool update_display, bool is_memory_state);
    208 
    209 static bool IsExecutionInterrupted();
    210 static void CheckForAndExitExecution();
    211 
    212 static void SetRewinding(bool enabled);
    213 static bool SaveRewindState();
    214 static void DoRewind();
    215 
    216 static void SaveRunaheadState();
    217 static bool DoRunahead();
    218 
    219 static void UpdateSessionTime(const std::string& prev_serial);
    220 
    221 static void SetTimerResolutionIncreased(bool enabled);
    222 
    223 #ifdef ENABLE_DISCORD_PRESENCE
    224 static void InitializeDiscordPresence();
    225 static void ShutdownDiscordPresence();
    226 static void PollDiscordPresence();
    227 #endif
    228 } // namespace System
    229 
    230 static constexpr const float PERFORMANCE_COUNTER_UPDATE_INTERVAL = 1.0f;
    231 static constexpr const char FALLBACK_EXE_NAME[] = "PSX.EXE";
    232 static constexpr u32 MAX_SKIPPED_DUPLICATE_FRAME_COUNT = 2; // 20fps minimum
    233 static constexpr u32 MAX_SKIPPED_TIMEOUT_FRAME_COUNT = 1;   // 30fps minimum
    234 
    235 static std::unique_ptr<INISettingsInterface> s_game_settings_interface;
    236 static std::unique_ptr<INISettingsInterface> s_input_settings_interface;
    237 static std::string s_input_profile_name;
    238 
    239 static System::State s_state = System::State::Shutdown;
    240 static std::atomic_bool s_startup_cancelled{false};
    241 static bool s_keep_gpu_device_on_shutdown = false;
    242 
    243 static ConsoleRegion s_region = ConsoleRegion::NTSC_U;
    244 TickCount System::g_ticks_per_second = System::MASTER_CLOCK;
    245 static TickCount s_max_slice_ticks = System::MASTER_CLOCK / 10;
    246 static u32 s_frame_number = 1;
    247 static u32 s_internal_frame_number = 1;
    248 static const BIOS::ImageInfo* s_bios_image_info = nullptr;
    249 static BIOS::ImageInfo::Hash s_bios_hash = {};
    250 
    251 static std::string s_running_game_path;
    252 static std::string s_running_game_serial;
    253 static std::string s_running_game_title;
    254 static std::string s_exe_override;
    255 static const GameDatabase::Entry* s_running_game_entry = nullptr;
    256 static System::GameHash s_running_game_hash;
    257 static System::BootMode s_boot_mode = System::BootMode::None;
    258 static bool s_running_game_custom_title = false;
    259 
    260 static bool s_system_executing = false;
    261 static bool s_system_interrupted = false;
    262 static bool s_frame_step_request = false;
    263 static bool s_fast_forward_enabled = false;
    264 static bool s_turbo_enabled = false;
    265 static bool s_throttler_enabled = false;
    266 static bool s_optimal_frame_pacing = false;
    267 static bool s_pre_frame_sleep = false;
    268 static bool s_can_sync_to_host = false;
    269 static bool s_syncing_to_host = false;
    270 static bool s_syncing_to_host_with_vsync = false;
    271 static bool s_skip_presenting_duplicate_frames = false;
    272 static u32 s_skipped_frame_count = 0;
    273 static u32 s_last_presented_internal_frame_number = 0;
    274 
    275 static float s_throttle_frequency = 0.0f;
    276 static float s_target_speed = 0.0f;
    277 
    278 static Common::Timer::Value s_frame_period = 0;
    279 static Common::Timer::Value s_next_frame_time = 0;
    280 
    281 static Common::Timer::Value s_frame_start_time = 0;
    282 static Common::Timer::Value s_last_active_frame_time = 0;
    283 static Common::Timer::Value s_pre_frame_sleep_time = 0;
    284 static Common::Timer::Value s_max_active_frame_time = 0;
    285 
    286 static float s_average_frame_time_accumulator = 0.0f;
    287 static float s_minimum_frame_time_accumulator = 0.0f;
    288 static float s_maximum_frame_time_accumulator = 0.0f;
    289 
    290 static float s_vps = 0.0f;
    291 static float s_fps = 0.0f;
    292 static float s_speed = 0.0f;
    293 static float s_minimum_frame_time = 0.0f;
    294 static float s_maximum_frame_time = 0.0f;
    295 static float s_average_frame_time = 0.0f;
    296 static float s_cpu_thread_usage = 0.0f;
    297 static float s_cpu_thread_time = 0.0f;
    298 static float s_sw_thread_usage = 0.0f;
    299 static float s_sw_thread_time = 0.0f;
    300 static float s_average_gpu_time = 0.0f;
    301 static float s_accumulated_gpu_time = 0.0f;
    302 static float s_gpu_usage = 0.0f;
    303 static System::FrameTimeHistory s_frame_time_history;
    304 static u32 s_frame_time_history_pos = 0;
    305 static u32 s_last_frame_number = 0;
    306 static u32 s_last_internal_frame_number = 0;
    307 static GlobalTicks s_last_global_tick_counter = 0;
    308 static u64 s_last_cpu_time = 0;
    309 static u64 s_last_sw_time = 0;
    310 static u32 s_presents_since_last_update = 0;
    311 static Common::Timer s_fps_timer;
    312 static Common::Timer s_frame_timer;
    313 static Threading::ThreadHandle s_cpu_thread_handle;
    314 
    315 static std::unique_ptr<CheatList> s_cheat_list;
    316 static std::unique_ptr<MediaCapture> s_media_capture;
    317 
    318 // temporary save state, created when loading, used to undo load state
    319 static std::optional<System::SaveStateBuffer> s_undo_load_state;
    320 
    321 static bool s_memory_saves_enabled = false;
    322 
    323 static std::deque<System::MemorySaveState> s_rewind_states;
    324 static s32 s_rewind_load_frequency = -1;
    325 static s32 s_rewind_load_counter = -1;
    326 static s32 s_rewind_save_frequency = -1;
    327 static s32 s_rewind_save_counter = -1;
    328 static bool s_rewinding_first_save = false;
    329 
    330 static std::deque<System::MemorySaveState> s_runahead_states;
    331 static bool s_runahead_replay_pending = false;
    332 static u32 s_runahead_frames = 0;
    333 static u32 s_runahead_replay_frames = 0;
    334 
    335 // Used to track play time. We use a monotonic timer here, in case of clock changes.
    336 static u64 s_session_start_time = 0;
    337 
    338 #ifdef ENABLE_SOCKET_MULTIPLEXER
    339 static std::unique_ptr<SocketMultiplexer> s_socket_multiplexer;
    340 #endif
    341 
    342 #ifdef ENABLE_DISCORD_PRESENCE
    343 static bool s_discord_presence_active = false;
    344 static time_t s_discord_presence_time_epoch;
    345 #endif
    346 
    347 static TinyString GetTimestampStringForFileName()
    348 {
    349   return TinyString::from_format("{:%Y-%m-%d-%H-%M-%S}", fmt::localtime(std::time(nullptr)));
    350 }
    351 
    352 bool System::Internal::PerformEarlyHardwareChecks(Error* error)
    353 {
    354   // This shouldn't fail... if it does, just hope for the best.
    355   cpuinfo_initialize();
    356 
    357 #ifdef CPU_ARCH_X64
    358   if (!cpuinfo_has_x86_sse4_1())
    359   {
    360     Error::SetStringFmt(error, "Your CPU does not support the SSE4.1 instruction set.\n"
    361                                "A CPU from 2008 or newer is required to run DuckStation.");
    362     return false;
    363   }
    364 #endif
    365 
    366   // Check page size. If it doesn't match, it is a fatal error.
    367   const size_t runtime_host_page_size = PlatformMisc::GetRuntimePageSize();
    368   if (runtime_host_page_size == 0)
    369   {
    370     Error::SetStringFmt(error, "Cannot determine size of page. Continuing with expectation of {} byte pages.",
    371                         runtime_host_page_size);
    372   }
    373   else if (HOST_PAGE_SIZE != runtime_host_page_size)
    374   {
    375     Error::SetStringFmt(
    376       error, "Page size mismatch. This build was compiled with {} byte pages, but the system has {} byte pages.",
    377       HOST_PAGE_SIZE, runtime_host_page_size);
    378     CPUThreadShutdown();
    379     return false;
    380   }
    381 
    382   return true;
    383 }
    384 
    385 void System::CheckCacheLineSize()
    386 {
    387   u32 max_line_size = 0;
    388   if (cpuinfo_initialize())
    389   {
    390     const u32 num_l1is = cpuinfo_get_l1i_caches_count();
    391     const u32 num_l1ds = cpuinfo_get_l1d_caches_count();
    392     const u32 num_l2s = cpuinfo_get_l2_caches_count();
    393     for (u32 i = 0; i < num_l1is; i++)
    394     {
    395       const cpuinfo_cache* cache = cpuinfo_get_l1i_cache(i);
    396       if (cache)
    397         max_line_size = std::max(max_line_size, cache->line_size);
    398     }
    399     for (u32 i = 0; i < num_l1ds; i++)
    400     {
    401       const cpuinfo_cache* cache = cpuinfo_get_l1d_cache(i);
    402       if (cache)
    403         max_line_size = std::max(max_line_size, cache->line_size);
    404     }
    405     for (u32 i = 0; i < num_l2s; i++)
    406     {
    407       const cpuinfo_cache* cache = cpuinfo_get_l2_cache(i);
    408       if (cache)
    409         max_line_size = std::max(max_line_size, cache->line_size);
    410     }
    411   }
    412 
    413   if (max_line_size == 0)
    414   {
    415     ERROR_LOG("Cannot determine size of cache line. Continuing with expectation of {} byte lines.",
    416               HOST_CACHE_LINE_SIZE);
    417   }
    418   else if (HOST_CACHE_LINE_SIZE != max_line_size)
    419   {
    420     // Not fatal, but does have performance implications.
    421     WARNING_LOG(
    422       "Cache line size mismatch. This build was compiled with {} byte lines, but the system has {} byte lines.",
    423       HOST_CACHE_LINE_SIZE, max_line_size);
    424   }
    425 }
    426 
    427 void System::LogStartupInformation()
    428 {
    429   INFO_LOG("DuckStation Version {} [{}]", g_scm_tag_str, g_scm_branch_str);
    430   INFO_LOG("SCM Timestamp: {}", g_scm_date_str);
    431   INFO_LOG("Build Timestamp: {} {}", __DATE__, __TIME__);
    432   if (const cpuinfo_package* package = cpuinfo_get_package(0)) [[likely]]
    433   {
    434     INFO_LOG("Host CPU: {}", package->name);
    435     INFO_LOG("CPU has {} logical processor(s) and {} core(s) across {} cluster(s).", package->processor_count,
    436              package->core_count, package->cluster_count);
    437   }
    438 }
    439 
    440 bool System::Internal::ProcessStartup(Error* error)
    441 {
    442   Common::Timer timer;
    443 
    444   // Allocate JIT memory as soon as possible.
    445   if (!CPU::CodeCache::ProcessStartup(error))
    446     return false;
    447 
    448   // g_settings is not valid at this point, query global config directly.
    449   const bool export_shared_memory = Host::GetBoolSettingValue("Hacks", "ExportSharedMemory", false);
    450 
    451   // Fastmem alloc *must* come after JIT alloc, otherwise it tends to eat the 4GB region after the executable on MacOS.
    452   if (!Bus::AllocateMemory(export_shared_memory, error))
    453   {
    454     CPU::CodeCache::ProcessShutdown();
    455     return false;
    456   }
    457 
    458   VERBOSE_LOG("Memory allocation took {} ms.", timer.GetTimeMilliseconds());
    459 
    460   CheckCacheLineSize();
    461 
    462   return true;
    463 }
    464 
    465 void System::Internal::ProcessShutdown()
    466 {
    467   Bus::ReleaseMemory();
    468   CPU::CodeCache::ProcessShutdown();
    469 }
    470 
    471 bool System::Internal::CPUThreadInitialize(Error* error)
    472 {
    473   Threading::SetNameOfCurrentThread("CPU Thread");
    474 
    475 #ifdef _WIN32
    476   // On Win32, we have a bunch of things which use COM (e.g. SDL, Cubeb, etc).
    477   // We need to initialize COM first, before anything else does, because otherwise they might
    478   // initialize it in single-threaded/apartment mode, which can't be changed to multithreaded.
    479   HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
    480   if (FAILED(hr))
    481   {
    482     Error::SetHResult(error, "CoInitializeEx() failed: ", hr);
    483     return false;
    484   }
    485 #endif
    486 
    487   // This will call back to Host::LoadSettings() -> ReloadSources().
    488   LoadSettings(false);
    489 
    490   LogStartupInformation();
    491 
    492   if (g_settings.achievements_enabled)
    493     Achievements::Initialize();
    494 
    495 #ifdef ENABLE_DISCORD_PRESENCE
    496   if (g_settings.enable_discord_presence)
    497     InitializeDiscordPresence();
    498 #endif
    499 
    500 #ifdef ENABLE_PINE_SERVER
    501   if (g_settings.pine_enable)
    502     PINEServer::Initialize(g_settings.pine_slot);
    503 #endif
    504 
    505   return true;
    506 }
    507 
    508 void System::Internal::CPUThreadShutdown()
    509 {
    510 #ifdef ENABLE_PINE_SERVER
    511   PINEServer::Shutdown();
    512 #endif
    513 
    514 #ifdef ENABLE_DISCORD_PRESENCE
    515   ShutdownDiscordPresence();
    516 #endif
    517 
    518   Achievements::Shutdown(false);
    519 
    520   InputManager::CloseSources();
    521 
    522 #ifdef _WIN32
    523   CoUninitialize();
    524 #endif
    525 }
    526 
    527 void System::Internal::IdlePollUpdate()
    528 {
    529   InputManager::PollSources();
    530 
    531 #ifdef ENABLE_DISCORD_PRESENCE
    532   PollDiscordPresence();
    533 #endif
    534 
    535   Achievements::IdleUpdate();
    536 
    537 #ifdef ENABLE_SOCKET_MULTIPLEXER
    538   if (s_socket_multiplexer)
    539     s_socket_multiplexer->PollEventsWithTimeout(0);
    540 #endif
    541 }
    542 
    543 System::State System::GetState()
    544 {
    545   return s_state;
    546 }
    547 
    548 void System::SetState(State new_state)
    549 {
    550   if (s_state == new_state)
    551     return;
    552 
    553   Assert(s_state == State::Paused || s_state == State::Running);
    554   Assert(new_state == State::Paused || new_state == State::Running);
    555   s_state = new_state;
    556 }
    557 
    558 bool System::IsRunning()
    559 {
    560   return s_state == State::Running;
    561 }
    562 
    563 ALWAYS_INLINE bool System::IsExecutionInterrupted()
    564 {
    565   return s_state != State::Running || s_system_interrupted;
    566 }
    567 
    568 ALWAYS_INLINE_RELEASE void System::CheckForAndExitExecution()
    569 {
    570   if (IsExecutionInterrupted()) [[unlikely]]
    571   {
    572     s_system_interrupted = false;
    573 
    574     TimingEvents::CancelRunningEvent();
    575     CPU::ExitExecution();
    576   }
    577 }
    578 
    579 bool System::IsPaused()
    580 {
    581   return s_state == State::Paused;
    582 }
    583 
    584 bool System::IsShutdown()
    585 {
    586   return s_state == State::Shutdown;
    587 }
    588 
    589 bool System::IsValid()
    590 {
    591   return s_state == State::Running || s_state == State::Paused;
    592 }
    593 
    594 bool System::IsValidOrInitializing()
    595 {
    596   return s_state == State::Starting || s_state == State::Running || s_state == State::Paused;
    597 }
    598 
    599 bool System::IsExecuting()
    600 {
    601   DebugAssert(s_state != State::Shutdown);
    602   return s_system_executing;
    603 }
    604 
    605 bool System::IsStartupCancelled()
    606 {
    607   return s_startup_cancelled.load();
    608 }
    609 
    610 void System::CancelPendingStartup()
    611 {
    612   if (s_state == State::Starting)
    613     s_startup_cancelled.store(true);
    614 }
    615 
    616 void System::InterruptExecution()
    617 {
    618   if (s_system_executing)
    619     s_system_interrupted = true;
    620 }
    621 
    622 ConsoleRegion System::GetRegion()
    623 {
    624   return s_region;
    625 }
    626 
    627 DiscRegion System::GetDiscRegion()
    628 {
    629   return CDROM::GetDiscRegion();
    630 }
    631 
    632 bool System::IsPALRegion()
    633 {
    634   return s_region == ConsoleRegion::PAL;
    635 }
    636 
    637 TickCount System::GetMaxSliceTicks()
    638 {
    639   return s_max_slice_ticks;
    640 }
    641 
    642 void System::UpdateOverclock()
    643 {
    644   g_ticks_per_second = ScaleTicksToOverclock(MASTER_CLOCK);
    645   s_max_slice_ticks = ScaleTicksToOverclock(MASTER_CLOCK / 10);
    646   SPU::CPUClockChanged();
    647   CDROM::CPUClockChanged();
    648   g_gpu->CPUClockChanged();
    649   Timers::CPUClocksChanged();
    650   UpdateThrottlePeriod();
    651 }
    652 
    653 GlobalTicks System::GetGlobalTickCounter()
    654 {
    655   // When running events, the counter actually goes backwards, because the pending ticks are added in chunks.
    656   // So, we need to return the counter with all pending ticks added in such cases.
    657   return TimingEvents::IsRunningEvents() ? TimingEvents::GetEventRunTickCounter() :
    658                                            (TimingEvents::GetGlobalTickCounter() + CPU::GetPendingTicks());
    659 }
    660 
    661 u32 System::GetFrameNumber()
    662 {
    663   return s_frame_number;
    664 }
    665 
    666 u32 System::GetInternalFrameNumber()
    667 {
    668   return s_internal_frame_number;
    669 }
    670 
    671 const std::string& System::GetDiscPath()
    672 {
    673   return s_running_game_path;
    674 }
    675 const std::string& System::GetGameSerial()
    676 {
    677   return s_running_game_serial;
    678 }
    679 
    680 const std::string& System::GetGameTitle()
    681 {
    682   return s_running_game_title;
    683 }
    684 
    685 const std::string& System::GetExeOverride()
    686 {
    687   return s_exe_override;
    688 }
    689 
    690 const GameDatabase::Entry* System::GetGameDatabaseEntry()
    691 {
    692   return s_running_game_entry;
    693 }
    694 
    695 System::GameHash System::GetGameHash()
    696 {
    697   return s_running_game_hash;
    698 }
    699 
    700 bool System::IsRunningUnknownGame()
    701 {
    702   return !s_running_game_entry;
    703 }
    704 
    705 System::BootMode System::GetBootMode()
    706 {
    707   return s_boot_mode;
    708 }
    709 
    710 const BIOS::ImageInfo* System::GetBIOSImageInfo()
    711 {
    712   return s_bios_image_info;
    713 }
    714 
    715 float System::GetFPS()
    716 {
    717   return s_fps;
    718 }
    719 float System::GetVPS()
    720 {
    721   return s_vps;
    722 }
    723 float System::GetEmulationSpeed()
    724 {
    725   return s_speed;
    726 }
    727 float System::GetAverageFrameTime()
    728 {
    729   return s_average_frame_time;
    730 }
    731 float System::GetMinimumFrameTime()
    732 {
    733   return s_minimum_frame_time;
    734 }
    735 float System::GetMaximumFrameTime()
    736 {
    737   return s_maximum_frame_time;
    738 }
    739 float System::GetThrottleFrequency()
    740 {
    741   return s_throttle_frequency;
    742 }
    743 float System::GetCPUThreadUsage()
    744 {
    745   return s_cpu_thread_usage;
    746 }
    747 float System::GetCPUThreadAverageTime()
    748 {
    749   return s_cpu_thread_time;
    750 }
    751 float System::GetSWThreadUsage()
    752 {
    753   return s_sw_thread_usage;
    754 }
    755 float System::GetSWThreadAverageTime()
    756 {
    757   return s_sw_thread_time;
    758 }
    759 float System::GetGPUUsage()
    760 {
    761   return s_gpu_usage;
    762 }
    763 float System::GetGPUAverageTime()
    764 {
    765   return s_average_gpu_time;
    766 }
    767 const System::FrameTimeHistory& System::GetFrameTimeHistory()
    768 {
    769   return s_frame_time_history;
    770 }
    771 u32 System::GetFrameTimeHistoryPos()
    772 {
    773   return s_frame_time_history_pos;
    774 }
    775 
    776 bool System::IsExeFileName(std::string_view path)
    777 {
    778   return (StringUtil::EndsWithNoCase(path, ".exe") || StringUtil::EndsWithNoCase(path, ".psexe") ||
    779           StringUtil::EndsWithNoCase(path, ".ps-exe"));
    780 }
    781 
    782 bool System::IsPsfFileName(std::string_view path)
    783 {
    784   return (StringUtil::EndsWithNoCase(path, ".psf") || StringUtil::EndsWithNoCase(path, ".minipsf"));
    785 }
    786 
    787 bool System::IsLoadableFilename(std::string_view path)
    788 {
    789   static constexpr const std::array extensions = {
    790     ".bin", ".cue",     ".img",    ".iso", ".chd", ".ecm", ".mds", // discs
    791     ".exe", ".psexe",   ".ps-exe",                                 // exes
    792     ".psf", ".minipsf",                                            // psf
    793     ".m3u",                                                        // playlists
    794     ".pbp",
    795   };
    796 
    797   for (const char* test_extension : extensions)
    798   {
    799     if (StringUtil::EndsWithNoCase(path, test_extension))
    800       return true;
    801   }
    802 
    803   return false;
    804 }
    805 
    806 bool System::IsSaveStateFilename(std::string_view path)
    807 {
    808   return StringUtil::EndsWithNoCase(path, ".sav");
    809 }
    810 
    811 ConsoleRegion System::GetConsoleRegionForDiscRegion(DiscRegion region)
    812 {
    813   switch (region)
    814   {
    815     case DiscRegion::NTSC_J:
    816       return ConsoleRegion::NTSC_J;
    817 
    818     case DiscRegion::NTSC_U:
    819     case DiscRegion::Other:
    820     case DiscRegion::NonPS1:
    821     default:
    822       return ConsoleRegion::NTSC_U;
    823 
    824     case DiscRegion::PAL:
    825       return ConsoleRegion::PAL;
    826   }
    827 }
    828 
    829 std::string System::GetGameHashId(GameHash hash)
    830 {
    831   return fmt::format("HASH-{:X}", hash);
    832 }
    833 
    834 bool System::GetGameDetailsFromImage(CDImage* cdi, std::string* out_id, GameHash* out_hash)
    835 {
    836   IsoReader iso;
    837   if (!iso.Open(cdi, 1))
    838   {
    839     if (out_id)
    840       out_id->clear();
    841     if (out_hash)
    842       *out_hash = 0;
    843     return false;
    844   }
    845 
    846   std::string id;
    847   std::string exe_name;
    848   std::vector<u8> exe_buffer;
    849   if (!ReadExecutableFromImage(iso, &exe_name, &exe_buffer))
    850   {
    851     if (out_id)
    852       out_id->clear();
    853     if (out_hash)
    854       *out_hash = 0;
    855     return false;
    856   }
    857 
    858   // Always compute the hash.
    859   const GameHash hash = GetGameHashFromBuffer(exe_name, exe_buffer, iso.GetPVD(), cdi->GetTrackLength(1));
    860   DEV_LOG("Hash for '{}' - {:016X}", exe_name, hash);
    861 
    862   if (exe_name != FALLBACK_EXE_NAME)
    863   {
    864     // Strip off any subdirectories.
    865     const std::string::size_type slash = exe_name.rfind('\\');
    866     if (slash != std::string::npos)
    867       id = std::string_view(exe_name).substr(slash + 1);
    868     else
    869       id = exe_name;
    870 
    871     // SCES_123.45 -> SCES-12345
    872     for (std::string::size_type pos = 0; pos < id.size();)
    873     {
    874       if (id[pos] == '.')
    875       {
    876         id.erase(pos, 1);
    877         continue;
    878       }
    879 
    880       if (id[pos] == '_')
    881         id[pos] = '-';
    882       else
    883         id[pos] = static_cast<char>(std::toupper(id[pos]));
    884 
    885       pos++;
    886     }
    887   }
    888 
    889   if (out_id)
    890   {
    891     if (id.empty())
    892       *out_id = GetGameHashId(hash);
    893     else
    894       *out_id = std::move(id);
    895   }
    896 
    897   if (out_hash)
    898     *out_hash = hash;
    899 
    900   return true;
    901 }
    902 
    903 System::GameHash System::GetGameHashFromFile(const char* path)
    904 {
    905   const std::optional<DynamicHeapArray<u8>> data = FileSystem::ReadBinaryFile(path);
    906   if (!data)
    907     return 0;
    908 
    909   const std::string display_name = FileSystem::GetDisplayNameFromPath(path);
    910   return GetGameHashFromBuffer(display_name, data->cspan(), IsoReader::ISOPrimaryVolumeDescriptor{}, 0);
    911 }
    912 
    913 std::string System::GetExecutableNameForImage(IsoReader& iso, bool strip_subdirectories)
    914 {
    915   // Read SYSTEM.CNF
    916   std::vector<u8> system_cnf_data;
    917   if (!iso.ReadFile("SYSTEM.CNF", &system_cnf_data))
    918     return FALLBACK_EXE_NAME;
    919 
    920   // Parse lines
    921   std::vector<std::pair<std::string, std::string>> lines;
    922   std::pair<std::string, std::string> current_line;
    923   bool reading_value = false;
    924   for (size_t pos = 0; pos < system_cnf_data.size(); pos++)
    925   {
    926     const char ch = static_cast<char>(system_cnf_data[pos]);
    927     if (ch == '\r' || ch == '\n')
    928     {
    929       if (!current_line.first.empty())
    930       {
    931         lines.push_back(std::move(current_line));
    932         current_line = {};
    933         reading_value = false;
    934       }
    935     }
    936     else if (ch == ' ' || (ch >= 0x09 && ch <= 0x0D))
    937     {
    938       continue;
    939     }
    940     else if (ch == '=' && !reading_value)
    941     {
    942       reading_value = true;
    943     }
    944     else
    945     {
    946       if (reading_value)
    947         current_line.second.push_back(ch);
    948       else
    949         current_line.first.push_back(ch);
    950     }
    951   }
    952 
    953   if (!current_line.first.empty())
    954     lines.push_back(std::move(current_line));
    955 
    956   // Find the BOOT line
    957   auto iter = std::find_if(lines.begin(), lines.end(),
    958                            [](const auto& it) { return StringUtil::Strcasecmp(it.first.c_str(), "boot") == 0; });
    959   if (iter == lines.end())
    960   {
    961     // Fallback to PSX.EXE
    962     return FALLBACK_EXE_NAME;
    963   }
    964 
    965   std::string code = iter->second;
    966   std::string::size_type pos;
    967   if (strip_subdirectories)
    968   {
    969     // cdrom:\SCES_123.45;1
    970     pos = code.rfind('\\');
    971     if (pos != std::string::npos)
    972     {
    973       code.erase(0, pos + 1);
    974     }
    975     else
    976     {
    977       // cdrom:SCES_123.45;1
    978       pos = code.rfind(':');
    979       if (pos != std::string::npos)
    980         code.erase(0, pos + 1);
    981     }
    982   }
    983   else
    984   {
    985     if (code.compare(0, 6, "cdrom:") == 0)
    986       code.erase(0, 6);
    987     else
    988       WARNING_LOG("Unknown prefix in executable path: '{}'", code);
    989 
    990     // remove leading slashes
    991     while (code[0] == '/' || code[0] == '\\')
    992       code.erase(0, 1);
    993   }
    994 
    995   // strip off ; or version number
    996   pos = code.rfind(';');
    997   if (pos != std::string::npos)
    998     code.erase(pos);
    999 
   1000   return code;
   1001 }
   1002 
   1003 std::string System::GetExecutableNameForImage(CDImage* cdi, bool strip_subdirectories)
   1004 {
   1005   IsoReader iso;
   1006   if (!iso.Open(cdi, 1))
   1007     return {};
   1008 
   1009   return GetExecutableNameForImage(iso, strip_subdirectories);
   1010 }
   1011 
   1012 bool System::ReadExecutableFromImage(CDImage* cdi, std::string* out_executable_name,
   1013                                      std::vector<u8>* out_executable_data)
   1014 {
   1015   IsoReader iso;
   1016   if (!iso.Open(cdi, 1))
   1017     return false;
   1018 
   1019   return ReadExecutableFromImage(iso, out_executable_name, out_executable_data);
   1020 }
   1021 
   1022 bool System::ReadExecutableFromImage(IsoReader& iso, std::string* out_executable_name,
   1023                                      std::vector<u8>* out_executable_data)
   1024 {
   1025   std::string executable_path = GetExecutableNameForImage(iso, false);
   1026   DEV_LOG("Executable path: '{}'", executable_path);
   1027   if (!executable_path.empty() && out_executable_data)
   1028   {
   1029     if (!iso.ReadFile(executable_path, out_executable_data))
   1030     {
   1031       ERROR_LOG("Failed to read executable '{}' from disc", executable_path);
   1032       return false;
   1033     }
   1034   }
   1035 
   1036   if (out_executable_name)
   1037     *out_executable_name = std::move(executable_path);
   1038 
   1039   return true;
   1040 }
   1041 
   1042 System::GameHash System::GetGameHashFromBuffer(std::string_view exe_name, std::span<const u8> exe_buffer,
   1043                                                const IsoReader::ISOPrimaryVolumeDescriptor& iso_pvd, u32 track_1_length)
   1044 {
   1045   XXH64_state_t* state = XXH64_createState();
   1046   XXH64_reset(state, 0x4242D00C);
   1047   XXH64_update(state, exe_name.data(), exe_name.size());
   1048   XXH64_update(state, exe_buffer.data(), exe_buffer.size());
   1049   XXH64_update(state, &iso_pvd, sizeof(IsoReader::ISOPrimaryVolumeDescriptor));
   1050   XXH64_update(state, &track_1_length, sizeof(track_1_length));
   1051   const GameHash hash = XXH64_digest(state);
   1052   XXH64_freeState(state);
   1053   return hash;
   1054 }
   1055 
   1056 DiscRegion System::GetRegionForSerial(const std::string_view serial)
   1057 {
   1058   static constexpr const std::pair<const char*, DiscRegion> region_prefixes[] = {
   1059     {"sces", DiscRegion::PAL},    {"sced", DiscRegion::PAL},    {"sles", DiscRegion::PAL},
   1060     {"sled", DiscRegion::PAL},
   1061 
   1062     {"scps", DiscRegion::NTSC_J}, {"slps", DiscRegion::NTSC_J}, {"slpm", DiscRegion::NTSC_J},
   1063     {"sczs", DiscRegion::NTSC_J}, {"papx", DiscRegion::NTSC_J},
   1064 
   1065     {"scus", DiscRegion::NTSC_U}, {"slus", DiscRegion::NTSC_U},
   1066   };
   1067 
   1068   for (const auto& [prefix, region] : region_prefixes)
   1069   {
   1070     if (StringUtil::StartsWithNoCase(serial, prefix))
   1071       return region;
   1072   }
   1073 
   1074   return DiscRegion::Other;
   1075 }
   1076 
   1077 DiscRegion System::GetRegionFromSystemArea(CDImage* cdi)
   1078 {
   1079   // The license code is on sector 4 of the disc.
   1080   u8 sector[CDImage::DATA_SECTOR_SIZE];
   1081   if (!cdi->Seek(1, 4) || cdi->Read(CDImage::ReadMode::DataOnly, 1, sector) != 1)
   1082     return DiscRegion::Other;
   1083 
   1084   static constexpr char ntsc_u_string[] = "          Licensed  by          Sony Computer Entertainment Amer  ica ";
   1085   static constexpr char ntsc_j_string[] = "          Licensed  by          Sony Computer Entertainment Inc.";
   1086   static constexpr char pal_string[] = "          Licensed  by          Sony Computer Entertainment Euro pe";
   1087 
   1088   // subtract one for the terminating null
   1089   if (std::equal(ntsc_u_string, ntsc_u_string + countof(ntsc_u_string) - 1, sector))
   1090     return DiscRegion::NTSC_U;
   1091   else if (std::equal(ntsc_j_string, ntsc_j_string + countof(ntsc_j_string) - 1, sector))
   1092     return DiscRegion::NTSC_J;
   1093   else if (std::equal(pal_string, pal_string + countof(pal_string) - 1, sector))
   1094     return DiscRegion::PAL;
   1095   else
   1096     return DiscRegion::Other;
   1097 }
   1098 
   1099 DiscRegion System::GetRegionForImage(CDImage* cdi)
   1100 {
   1101   const DiscRegion system_area_region = GetRegionFromSystemArea(cdi);
   1102   if (system_area_region != DiscRegion::Other)
   1103     return system_area_region;
   1104 
   1105   IsoReader iso;
   1106   if (!iso.Open(cdi, 1))
   1107     return DiscRegion::NonPS1;
   1108 
   1109   // The executable must exist, because this just returns PSX.EXE if it doesn't.
   1110   const std::string exename = GetExecutableNameForImage(iso, false);
   1111   if (exename.empty() || !iso.FileExists(exename.c_str()))
   1112     return DiscRegion::NonPS1;
   1113 
   1114   // Strip off any subdirectories.
   1115   const std::string::size_type slash = exename.rfind('\\');
   1116   if (slash != std::string::npos)
   1117     return GetRegionForSerial(std::string_view(exename).substr(slash + 1));
   1118   else
   1119     return GetRegionForSerial(exename);
   1120 }
   1121 
   1122 DiscRegion System::GetRegionForExe(const char* path)
   1123 {
   1124   auto fp = FileSystem::OpenManagedCFile(path, "rb");
   1125   if (!fp)
   1126     return DiscRegion::Other;
   1127 
   1128   BIOS::PSEXEHeader header;
   1129   if (std::fread(&header, sizeof(header), 1, fp.get()) != 1)
   1130     return DiscRegion::Other;
   1131 
   1132   return BIOS::GetPSExeDiscRegion(header);
   1133 }
   1134 
   1135 DiscRegion System::GetRegionForPsf(const char* path)
   1136 {
   1137   PSFLoader::File psf;
   1138   if (!psf.Load(path, nullptr))
   1139     return DiscRegion::Other;
   1140 
   1141   return psf.GetRegion();
   1142 }
   1143 
   1144 std::string System::GetGameSettingsPath(std::string_view game_serial)
   1145 {
   1146   // multi-disc games => always use the first disc
   1147   const GameDatabase::Entry* entry = GameDatabase::GetEntryForSerial(game_serial);
   1148   const std::string_view serial_for_path =
   1149     (entry && !entry->disc_set_serials.empty()) ? entry->disc_set_serials.front() : game_serial;
   1150   return Path::Combine(EmuFolders::GameSettings, fmt::format("{}.ini", Path::SanitizeFileName(serial_for_path)));
   1151 }
   1152 
   1153 std::string System::GetInputProfilePath(std::string_view name)
   1154 {
   1155   return Path::Combine(EmuFolders::InputProfiles, fmt::format("{}.ini", name));
   1156 }
   1157 
   1158 bool System::RecreateGPU(GPURenderer renderer, bool force_recreate_device, bool update_display /* = true*/)
   1159 {
   1160   ClearMemorySaveStates();
   1161   g_gpu->RestoreDeviceContext();
   1162 
   1163   // save current state
   1164   DynamicHeapArray<u8> state_data(GetMaxSaveStateSize());
   1165   {
   1166     StateWrapper sw(state_data.span(), StateWrapper::Mode::Write, SAVE_STATE_VERSION);
   1167     if (!g_gpu->DoState(sw, nullptr, false) || !TimingEvents::DoState(sw))
   1168     {
   1169       ERROR_LOG("Failed to save old GPU state when switching renderers");
   1170       state_data.deallocate();
   1171     }
   1172   }
   1173 
   1174   // create new renderer
   1175   g_gpu.reset();
   1176   if (force_recreate_device)
   1177   {
   1178     PostProcessing::Shutdown();
   1179     Host::ReleaseGPUDevice();
   1180   }
   1181 
   1182   Error error;
   1183   if (!CreateGPU(renderer, true, &error))
   1184   {
   1185     if (!IsStartupCancelled())
   1186       Host::ReportErrorAsync("Error", error.GetDescription());
   1187 
   1188     DestroySystem();
   1189     return false;
   1190   }
   1191 
   1192   if (!state_data.empty())
   1193   {
   1194     StateWrapper sw(state_data.span(), StateWrapper::Mode::Read, SAVE_STATE_VERSION);
   1195     g_gpu->RestoreDeviceContext();
   1196     g_gpu->DoState(sw, nullptr, update_display);
   1197     TimingEvents::DoState(sw);
   1198   }
   1199 
   1200   // fix up vsync etc
   1201   UpdateSpeedLimiterState();
   1202   return true;
   1203 }
   1204 
   1205 void System::LoadSettings(bool display_osd_messages)
   1206 {
   1207   std::unique_lock<std::mutex> lock = Host::GetSettingsLock();
   1208   SettingsInterface& si = *Host::GetSettingsInterface();
   1209   LayeredSettingsInterface controller_si = GetControllerSettingsLayers(lock);
   1210   LayeredSettingsInterface hotkey_si = GetHotkeySettingsLayer(lock);
   1211   g_settings.Load(si, controller_si);
   1212   g_settings.UpdateLogSettings();
   1213 
   1214   Host::LoadSettings(si, lock);
   1215   InputManager::ReloadSources(controller_si, lock);
   1216   InputManager::ReloadBindings(controller_si, hotkey_si);
   1217   WarnAboutUnsafeSettings();
   1218 
   1219   // apply compatibility settings
   1220   if (g_settings.apply_compatibility_settings && !s_running_game_serial.empty())
   1221   {
   1222     const GameDatabase::Entry* entry = GameDatabase::GetEntryForSerial(s_running_game_serial);
   1223     if (entry)
   1224       entry->ApplySettings(g_settings, display_osd_messages);
   1225   }
   1226 
   1227   g_settings.FixIncompatibleSettings(display_osd_messages);
   1228 }
   1229 
   1230 void System::ReloadInputSources()
   1231 {
   1232   std::unique_lock<std::mutex> lock = Host::GetSettingsLock();
   1233   LayeredSettingsInterface controller_si = GetControllerSettingsLayers(lock);
   1234   InputManager::ReloadSources(controller_si, lock);
   1235 
   1236   // skip loading bindings if we're not running, since it'll get done on startup anyway
   1237   if (IsValid())
   1238   {
   1239     LayeredSettingsInterface hotkey_si = GetHotkeySettingsLayer(lock);
   1240     InputManager::ReloadBindings(controller_si, hotkey_si);
   1241   }
   1242 }
   1243 
   1244 void System::ReloadInputBindings()
   1245 {
   1246   // skip loading bindings if we're not running, since it'll get done on startup anyway
   1247   if (!IsValid())
   1248     return;
   1249 
   1250   std::unique_lock<std::mutex> lock = Host::GetSettingsLock();
   1251   LayeredSettingsInterface controller_si = GetControllerSettingsLayers(lock);
   1252   LayeredSettingsInterface hotkey_si = GetHotkeySettingsLayer(lock);
   1253   InputManager::ReloadBindings(controller_si, hotkey_si);
   1254 }
   1255 
   1256 LayeredSettingsInterface System::GetControllerSettingsLayers(std::unique_lock<std::mutex>& lock)
   1257 {
   1258   LayeredSettingsInterface ret;
   1259   ret.SetLayer(LayeredSettingsInterface::Layer::LAYER_BASE, Host::Internal::GetBaseSettingsLayer());
   1260 
   1261   // Select input profile _or_ game settings, not both.
   1262   if (SettingsInterface* isi = Host::Internal::GetInputSettingsLayer())
   1263   {
   1264     ret.SetLayer(LayeredSettingsInterface::Layer::LAYER_INPUT, Host::Internal::GetInputSettingsLayer());
   1265   }
   1266   else if (SettingsInterface* gsi = Host::Internal::GetGameSettingsLayer();
   1267            gsi && gsi->GetBoolValue("ControllerPorts", "UseGameSettingsForController", false))
   1268   {
   1269     ret.SetLayer(LayeredSettingsInterface::Layer::LAYER_GAME, gsi);
   1270   }
   1271 
   1272   return ret;
   1273 }
   1274 
   1275 LayeredSettingsInterface System::GetHotkeySettingsLayer(std::unique_lock<std::mutex>& lock)
   1276 {
   1277   LayeredSettingsInterface ret;
   1278   ret.SetLayer(LayeredSettingsInterface::Layer::LAYER_BASE, Host::Internal::GetBaseSettingsLayer());
   1279 
   1280   // Only add input profile layer if the option is enabled.
   1281   if (SettingsInterface* isi = Host::Internal::GetInputSettingsLayer();
   1282       isi && isi->GetBoolValue("ControllerPorts", "UseProfileHotkeyBindings", false))
   1283   {
   1284     ret.SetLayer(LayeredSettingsInterface::Layer::LAYER_INPUT, Host::Internal::GetInputSettingsLayer());
   1285   }
   1286 
   1287   return ret;
   1288 }
   1289 
   1290 void System::SetDefaultSettings(SettingsInterface& si)
   1291 {
   1292   Settings temp;
   1293 
   1294   // we don't want to reset some things (e.g. OSD)
   1295   temp.display_show_osd_messages = g_settings.display_show_osd_messages;
   1296   temp.display_show_fps = g_settings.display_show_fps;
   1297   temp.display_show_speed = g_settings.display_show_speed;
   1298   temp.display_show_gpu_stats = g_settings.display_show_gpu_stats;
   1299   temp.display_show_resolution = g_settings.display_show_resolution;
   1300   temp.display_show_cpu_usage = g_settings.display_show_cpu_usage;
   1301   temp.display_show_gpu_usage = g_settings.display_show_gpu_usage;
   1302   temp.display_show_frame_times = g_settings.display_show_frame_times;
   1303 
   1304   // keep controller, we reset it elsewhere
   1305   for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
   1306     temp.controller_types[i] = g_settings.controller_types[i];
   1307 
   1308   temp.Save(si, false);
   1309 
   1310 #ifndef __ANDROID__
   1311   si.SetStringValue("MediaCapture", "Backend", MediaCapture::GetBackendName(Settings::DEFAULT_MEDIA_CAPTURE_BACKEND));
   1312   si.SetStringValue("MediaCapture", "Container", Settings::DEFAULT_MEDIA_CAPTURE_CONTAINER);
   1313   si.SetBoolValue("MediaCapture", "VideoCapture", true);
   1314   si.SetUIntValue("MediaCapture", "VideoWidth", Settings::DEFAULT_MEDIA_CAPTURE_VIDEO_WIDTH);
   1315   si.SetUIntValue("MediaCapture", "VideoHeight", Settings::DEFAULT_MEDIA_CAPTURE_VIDEO_HEIGHT);
   1316   si.SetBoolValue("MediaCapture", "VideoAutoSize", false);
   1317   si.SetUIntValue("MediaCapture", "VideoBitrate", Settings::DEFAULT_MEDIA_CAPTURE_VIDEO_BITRATE);
   1318   si.SetStringValue("MediaCapture", "VideoCodec", "");
   1319   si.SetBoolValue("MediaCapture", "VideoCodecUseArgs", false);
   1320   si.SetStringValue("MediaCapture", "AudioCodecArgs", "");
   1321   si.SetBoolValue("MediaCapture", "AudioCapture", true);
   1322   si.SetUIntValue("MediaCapture", "AudioBitrate", Settings::DEFAULT_MEDIA_CAPTURE_AUDIO_BITRATE);
   1323   si.SetStringValue("MediaCapture", "AudioCodec", "");
   1324   si.SetBoolValue("MediaCapture", "AudioCodecUseArgs", false);
   1325   si.SetStringValue("MediaCapture", "AudioCodecArgs", "");
   1326 #endif
   1327 }
   1328 
   1329 void System::ApplySettings(bool display_osd_messages)
   1330 {
   1331   DEV_LOG("Applying settings...");
   1332 
   1333   const Settings old_config(std::move(g_settings));
   1334   g_settings = Settings();
   1335   LoadSettings(display_osd_messages);
   1336 
   1337   // If we've disabled/enabled game settings, we need to reload without it.
   1338   if (g_settings.apply_game_settings != old_config.apply_game_settings)
   1339   {
   1340     UpdateGameSettingsLayer();
   1341     LoadSettings(display_osd_messages);
   1342   }
   1343 
   1344   CheckForSettingsChanges(old_config);
   1345   Host::CheckForSettingsChanges(old_config);
   1346 
   1347   if (IsValid())
   1348   {
   1349     ResetPerformanceCounters();
   1350     InterruptExecution();
   1351   }
   1352 }
   1353 
   1354 bool System::ReloadGameSettings(bool display_osd_messages)
   1355 {
   1356   if (!IsValid() || !UpdateGameSettingsLayer())
   1357     return false;
   1358 
   1359   ApplySettings(display_osd_messages);
   1360   return true;
   1361 }
   1362 
   1363 bool System::UpdateGameSettingsLayer()
   1364 {
   1365   std::unique_ptr<INISettingsInterface> new_interface;
   1366   if (g_settings.apply_game_settings && !s_running_game_serial.empty())
   1367   {
   1368     std::string filename(GetGameSettingsPath(s_running_game_serial));
   1369     if (FileSystem::FileExists(filename.c_str()))
   1370     {
   1371       INFO_LOG("Loading game settings from '{}'...", Path::GetFileName(filename));
   1372       new_interface = std::make_unique<INISettingsInterface>(std::move(filename));
   1373       if (!new_interface->Load())
   1374       {
   1375         ERROR_LOG("Failed to parse game settings ini '{}'", new_interface->GetFileName());
   1376         new_interface.reset();
   1377       }
   1378     }
   1379     else
   1380     {
   1381       INFO_LOG("No game settings found (tried '{}')", Path::GetFileName(filename));
   1382     }
   1383   }
   1384 
   1385   std::string input_profile_name;
   1386   if (new_interface)
   1387   {
   1388     if (!new_interface->GetBoolValue("ControllerPorts", "UseGameSettingsForController", false))
   1389       new_interface->GetStringValue("ControllerPorts", "InputProfileName", &input_profile_name);
   1390   }
   1391 
   1392   if (!s_game_settings_interface && !new_interface && s_input_profile_name == input_profile_name)
   1393     return false;
   1394 
   1395   auto lock = Host::GetSettingsLock();
   1396   Host::Internal::SetGameSettingsLayer(new_interface.get(), lock);
   1397   s_game_settings_interface = std::move(new_interface);
   1398 
   1399   std::unique_ptr<INISettingsInterface> input_interface;
   1400   if (!input_profile_name.empty())
   1401   {
   1402     std::string filename = GetInputProfilePath(input_profile_name);
   1403     if (FileSystem::FileExists(filename.c_str()))
   1404     {
   1405       INFO_LOG("Loading input profile from '{}'...", Path::GetFileName(filename));
   1406       input_interface = std::make_unique<INISettingsInterface>(std::move(filename));
   1407       if (!input_interface->Load())
   1408       {
   1409         ERROR_LOG("Failed to parse input profile ini '{}'", Path::GetFileName(input_interface->GetFileName()));
   1410         input_interface.reset();
   1411         input_profile_name = {};
   1412       }
   1413     }
   1414     else
   1415     {
   1416       WARNING_LOG("No input profile found (tried '{}')", Path::GetFileName(filename));
   1417       input_profile_name = {};
   1418     }
   1419   }
   1420 
   1421   Host::Internal::SetInputSettingsLayer(input_interface.get(), lock);
   1422   s_input_settings_interface = std::move(input_interface);
   1423   s_input_profile_name = std::move(input_profile_name);
   1424   return true;
   1425 }
   1426 
   1427 void System::ResetSystem()
   1428 {
   1429   if (!IsValid())
   1430     return;
   1431 
   1432   if (!Achievements::ConfirmSystemReset())
   1433     return;
   1434 
   1435   if (Achievements::ResetHardcoreMode(false))
   1436   {
   1437     // Make sure a pre-existing cheat file hasn't been loaded when resetting
   1438     // after enabling HC mode.
   1439     s_cheat_list.reset();
   1440     ApplySettings(false);
   1441   }
   1442 
   1443   InternalReset();
   1444 
   1445   // Reset boot mode/reload BIOS if needed. Preserve exe/psf boot.
   1446   const BootMode new_boot_mode = (s_boot_mode == BootMode::BootEXE || s_boot_mode == BootMode::BootPSF) ?
   1447                                    s_boot_mode :
   1448                                    (g_settings.bios_patch_fast_boot ? BootMode::FastBoot : BootMode::FullBoot);
   1449   if (Error error; !SetBootMode(new_boot_mode, &error))
   1450     ERROR_LOG("Failed to reload BIOS on boot mode change, the system may be unstable: {}", error.GetDescription());
   1451 
   1452   ResetPerformanceCounters();
   1453   ResetThrottler();
   1454   Host::AddIconOSDMessage("system_reset", ICON_FA_POWER_OFF, TRANSLATE_STR("OSDMessage", "System reset."),
   1455                           Host::OSD_QUICK_DURATION);
   1456 
   1457   InterruptExecution();
   1458 }
   1459 
   1460 void System::PauseSystem(bool paused)
   1461 {
   1462   if (paused == IsPaused() || !IsValid())
   1463     return;
   1464 
   1465   SetState(paused ? State::Paused : State::Running);
   1466   SPU::GetOutputStream()->SetPaused(paused);
   1467 
   1468   if (paused)
   1469   {
   1470     // Make sure the GPU is flushed, otherwise the VB might still be mapped.
   1471     g_gpu->FlushRender();
   1472 
   1473     FullscreenUI::OnSystemPaused();
   1474 
   1475     InputManager::PauseVibration();
   1476     InputManager::UpdateHostMouseMode();
   1477 
   1478     Achievements::OnSystemPaused(true);
   1479 
   1480     if (g_settings.inhibit_screensaver)
   1481       PlatformMisc::ResumeScreensaver();
   1482 
   1483 #ifdef ENABLE_GDB_SERVER
   1484     GDBServer::OnSystemPaused();
   1485 #endif
   1486 
   1487     Host::OnSystemPaused();
   1488     Host::OnIdleStateChanged();
   1489     UpdateDisplayVSync();
   1490     InvalidateDisplay();
   1491   }
   1492   else
   1493   {
   1494     FullscreenUI::OnSystemResumed();
   1495 
   1496     InputManager::UpdateHostMouseMode();
   1497 
   1498     Achievements::OnSystemPaused(false);
   1499 
   1500     if (g_settings.inhibit_screensaver)
   1501       PlatformMisc::SuspendScreensaver();
   1502 
   1503 #ifdef ENABLE_GDB_SERVER
   1504     GDBServer::OnSystemResumed();
   1505 #endif
   1506 
   1507     Host::OnSystemResumed();
   1508     Host::OnIdleStateChanged();
   1509 
   1510     UpdateDisplayVSync();
   1511     ResetPerformanceCounters();
   1512     ResetThrottler();
   1513   }
   1514 }
   1515 
   1516 bool System::SaveResumeState(Error* error)
   1517 {
   1518   if (s_running_game_serial.empty())
   1519   {
   1520     Error::SetStringView(error, "Cannot save resume state without serial.");
   1521     return false;
   1522   }
   1523 
   1524   const std::string path(GetGameSaveStateFileName(s_running_game_serial, -1));
   1525   return SaveState(path.c_str(), error, false);
   1526 }
   1527 
   1528 bool System::BootSystem(SystemBootParameters parameters, Error* error)
   1529 {
   1530   if (!parameters.save_state.empty())
   1531   {
   1532     // loading a state, so pull the media path from the save state to avoid a double change
   1533     std::string state_media(GetMediaPathFromSaveState(parameters.save_state.c_str()));
   1534     if (FileSystem::FileExists(state_media.c_str()))
   1535       parameters.filename = std::move(state_media);
   1536   }
   1537 
   1538   if (parameters.filename.empty())
   1539     INFO_LOG("Boot Filename: <BIOS/Shell>");
   1540   else
   1541     INFO_LOG("Boot Filename: {}", parameters.filename);
   1542 
   1543   Assert(s_state == State::Shutdown);
   1544   s_state = State::Starting;
   1545   s_startup_cancelled.store(false);
   1546   s_keep_gpu_device_on_shutdown = static_cast<bool>(g_gpu_device);
   1547   s_region = g_settings.region;
   1548   Host::OnSystemStarting();
   1549 
   1550   // Load CD image up and detect region.
   1551   std::unique_ptr<CDImage> disc;
   1552   DiscRegion disc_region = DiscRegion::NonPS1;
   1553   BootMode boot_mode = BootMode::FullBoot;
   1554   std::string exe_override;
   1555   if (!parameters.filename.empty())
   1556   {
   1557     if (IsExeFileName(parameters.filename))
   1558     {
   1559       boot_mode = BootMode::BootEXE;
   1560       exe_override = parameters.filename;
   1561     }
   1562     else if (IsPsfFileName(parameters.filename))
   1563     {
   1564       boot_mode = BootMode::BootPSF;
   1565       exe_override = parameters.filename;
   1566     }
   1567     if (boot_mode == BootMode::BootEXE || boot_mode == BootMode::BootPSF)
   1568     {
   1569       if (s_region == ConsoleRegion::Auto)
   1570       {
   1571         const DiscRegion file_region =
   1572           ((boot_mode == BootMode::BootEXE) ? GetRegionForExe(parameters.filename.c_str()) :
   1573                                               GetRegionForPsf(parameters.filename.c_str()));
   1574         INFO_LOG("EXE/PSF Region: {}", Settings::GetDiscRegionDisplayName(file_region));
   1575         s_region = GetConsoleRegionForDiscRegion(file_region);
   1576       }
   1577     }
   1578     else
   1579     {
   1580       INFO_LOG("Loading CD image '{}'...", Path::GetFileName(parameters.filename));
   1581       disc = CDImage::Open(parameters.filename.c_str(), g_settings.cdrom_load_image_patches, error);
   1582       if (!disc)
   1583       {
   1584         Error::AddPrefixFmt(error, "Failed to open CD image '{}':\n", Path::GetFileName(parameters.filename));
   1585         s_state = State::Shutdown;
   1586         Host::OnSystemDestroyed();
   1587         Host::OnIdleStateChanged();
   1588         return false;
   1589       }
   1590 
   1591       disc_region = GameList::GetCustomRegionForPath(parameters.filename).value_or(GetRegionForImage(disc.get()));
   1592       if (s_region == ConsoleRegion::Auto)
   1593       {
   1594         if (disc_region != DiscRegion::Other)
   1595         {
   1596           s_region = GetConsoleRegionForDiscRegion(disc_region);
   1597           INFO_LOG("Auto-detected console {} region for '{}' (region {})", Settings::GetConsoleRegionName(s_region),
   1598                    parameters.filename, Settings::GetDiscRegionName(disc_region));
   1599         }
   1600         else
   1601         {
   1602           s_region = ConsoleRegion::NTSC_U;
   1603           WARNING_LOG("Could not determine console region for disc region {}. Defaulting to {}.",
   1604                       Settings::GetDiscRegionName(disc_region), Settings::GetConsoleRegionName(s_region));
   1605         }
   1606       }
   1607 
   1608       const bool wants_fast_boot =
   1609         parameters.override_fast_boot.value_or(static_cast<bool>(g_settings.bios_patch_fast_boot));
   1610       if (wants_fast_boot)
   1611       {
   1612         if (disc_region == DiscRegion::NonPS1)
   1613           ERROR_LOG("Not fast booting non-PS1 disc.");
   1614         else
   1615           boot_mode = BootMode::FastBoot;
   1616       }
   1617     }
   1618   }
   1619   else
   1620   {
   1621     // Default to NTSC for BIOS boot.
   1622     if (s_region == ConsoleRegion::Auto)
   1623       s_region = ConsoleRegion::NTSC_U;
   1624   }
   1625 
   1626   INFO_LOG("Console Region: {}", Settings::GetConsoleRegionDisplayName(s_region));
   1627 
   1628   // Switch subimage.
   1629   if (disc && parameters.media_playlist_index != 0 && !disc->SwitchSubImage(parameters.media_playlist_index, error))
   1630   {
   1631     Error::AddPrefixFmt(error, "Failed to switch to subimage {} in '{}':\n", parameters.media_playlist_index,
   1632                         Path::GetFileName(parameters.filename));
   1633     s_state = State::Shutdown;
   1634     Host::OnSystemDestroyed();
   1635     Host::OnIdleStateChanged();
   1636     return false;
   1637   }
   1638 
   1639   // Update running game, this will apply settings as well.
   1640   UpdateRunningGame(disc ? disc->GetFileName().c_str() : parameters.filename.c_str(), disc.get(), true);
   1641 
   1642   // Get boot EXE override.
   1643   if (!parameters.override_exe.empty())
   1644   {
   1645     if (!FileSystem::FileExists(parameters.override_exe.c_str()) || !IsExeFileName(parameters.override_exe))
   1646     {
   1647       Error::SetStringFmt(error, "File '{}' is not a valid executable to boot.",
   1648                           Path::GetFileName(parameters.override_exe));
   1649       s_state = State::Shutdown;
   1650       Host::OnSystemDestroyed();
   1651       Host::OnIdleStateChanged();
   1652       return false;
   1653     }
   1654 
   1655     INFO_LOG("Overriding boot executable: '{}'", parameters.override_exe);
   1656     boot_mode = BootMode::BootEXE;
   1657     exe_override = std::move(parameters.override_exe);
   1658   }
   1659 
   1660   // Check for SBI.
   1661   if (!CheckForSBIFile(disc.get(), error))
   1662   {
   1663     s_state = State::Shutdown;
   1664     ClearRunningGame();
   1665     Host::OnSystemDestroyed();
   1666     Host::OnIdleStateChanged();
   1667     return false;
   1668   }
   1669 
   1670   // Check for resuming with hardcore mode.
   1671   if (parameters.disable_achievements_hardcore_mode)
   1672     Achievements::DisableHardcoreMode();
   1673   if ((!parameters.save_state.empty() || !exe_override.empty()) && Achievements::IsHardcoreModeActive())
   1674   {
   1675     const bool is_exe_override_boot = parameters.save_state.empty();
   1676     bool cancelled;
   1677     if (FullscreenUI::IsInitialized())
   1678     {
   1679       Achievements::ConfirmHardcoreModeDisableAsync(is_exe_override_boot ?
   1680                                                       TRANSLATE("Achievements", "Overriding executable") :
   1681                                                       TRANSLATE("Achievements", "Resuming state"),
   1682                                                     [parameters = std::move(parameters)](bool approved) mutable {
   1683                                                       if (approved)
   1684                                                       {
   1685                                                         parameters.disable_achievements_hardcore_mode = true;
   1686                                                         BootSystem(std::move(parameters), nullptr);
   1687                                                       }
   1688                                                     });
   1689       cancelled = true;
   1690     }
   1691     else
   1692     {
   1693       cancelled = !Achievements::ConfirmHardcoreModeDisable(is_exe_override_boot ?
   1694                                                               TRANSLATE("Achievements", "Overriding executable") :
   1695                                                               TRANSLATE("Achievements", "Resuming state"));
   1696     }
   1697 
   1698     if (cancelled)
   1699     {
   1700       s_state = State::Shutdown;
   1701       ClearRunningGame();
   1702       Host::OnSystemDestroyed();
   1703       Host::OnIdleStateChanged();
   1704 
   1705       // Technically a failure, but user-initiated. Returning false here would try to display a non-existent error.
   1706       return true;
   1707     }
   1708   }
   1709 
   1710   // Load BIOS image.
   1711   if (!SetBootMode(boot_mode, error))
   1712   {
   1713     s_state = State::Shutdown;
   1714     ClearRunningGame();
   1715     Host::OnSystemDestroyed();
   1716     Host::OnIdleStateChanged();
   1717     return false;
   1718   }
   1719 
   1720   // Component setup.
   1721   if (!Initialize(parameters.force_software_renderer, error))
   1722   {
   1723     s_boot_mode = System::BootMode::None;
   1724     s_state = State::Shutdown;
   1725     ClearRunningGame();
   1726     Host::OnSystemDestroyed();
   1727     Host::OnIdleStateChanged();
   1728     return false;
   1729   }
   1730 
   1731   // Insert disc.
   1732   if (disc)
   1733     CDROM::InsertMedia(std::move(disc), disc_region);
   1734 
   1735   s_exe_override = std::move(exe_override);
   1736 
   1737   UpdateControllers();
   1738   UpdateMemoryCardTypes();
   1739   UpdateMultitaps();
   1740   InternalReset();
   1741 
   1742   // Texture replacement preloading.
   1743   // TODO: Move this and everything else below OnSystemStarted().
   1744   TextureReplacements::SetGameID(s_running_game_serial);
   1745 
   1746   // Good to go.
   1747   s_state = State::Running;
   1748   SPU::GetOutputStream()->SetPaused(false);
   1749 
   1750   FullscreenUI::OnSystemStarted();
   1751 
   1752   InputManager::UpdateHostMouseMode();
   1753 
   1754   if (g_settings.inhibit_screensaver)
   1755     PlatformMisc::SuspendScreensaver();
   1756 
   1757 #ifdef ENABLE_GDB_SERVER
   1758   if (g_settings.debugging.enable_gdb_server)
   1759     GDBServer::Initialize(g_settings.debugging.gdb_server_port);
   1760 #endif
   1761 
   1762   Host::OnSystemStarted();
   1763   Host::OnIdleStateChanged();
   1764 
   1765   // try to load the state, if it fails, bail out
   1766   if (!parameters.save_state.empty() && !LoadState(parameters.save_state.c_str(), error, false))
   1767   {
   1768     Error::AddPrefixFmt(error, "Failed to load save state file '{}' for booting:\n",
   1769                         Path::GetFileName(parameters.save_state));
   1770     DestroySystem();
   1771     return false;
   1772   }
   1773 
   1774   if (parameters.load_image_to_ram || g_settings.cdrom_load_image_to_ram)
   1775     CDROM::PrecacheMedia();
   1776 
   1777   if (parameters.start_media_capture)
   1778     StartMediaCapture({});
   1779 
   1780   if (g_settings.start_paused || parameters.override_start_paused.value_or(false))
   1781     PauseSystem(true);
   1782 
   1783   UpdateSpeedLimiterState();
   1784   ResetPerformanceCounters();
   1785   return true;
   1786 }
   1787 
   1788 bool System::Initialize(bool force_software_renderer, Error* error)
   1789 {
   1790   g_ticks_per_second = ScaleTicksToOverclock(MASTER_CLOCK);
   1791   s_max_slice_ticks = ScaleTicksToOverclock(MASTER_CLOCK / 10);
   1792   s_frame_number = 1;
   1793   s_internal_frame_number = 1;
   1794 
   1795   s_target_speed = g_settings.emulation_speed;
   1796   s_throttle_frequency = 60.0f;
   1797   s_frame_period = 0;
   1798   s_next_frame_time = 0;
   1799   s_turbo_enabled = false;
   1800   s_fast_forward_enabled = false;
   1801 
   1802   s_rewind_load_frequency = -1;
   1803   s_rewind_load_counter = -1;
   1804   s_rewinding_first_save = true;
   1805 
   1806   s_average_frame_time_accumulator = 0.0f;
   1807   s_minimum_frame_time_accumulator = 0.0f;
   1808   s_maximum_frame_time_accumulator = 0.0f;
   1809 
   1810   s_vps = 0.0f;
   1811   s_fps = 0.0f;
   1812   s_speed = 0.0f;
   1813   s_minimum_frame_time = 0.0f;
   1814   s_maximum_frame_time = 0.0f;
   1815   s_average_frame_time = 0.0f;
   1816   s_cpu_thread_usage = 0.0f;
   1817   s_cpu_thread_time = 0.0f;
   1818   s_sw_thread_usage = 0.0f;
   1819   s_sw_thread_time = 0.0f;
   1820   s_average_gpu_time = 0.0f;
   1821   s_accumulated_gpu_time = 0.0f;
   1822   s_gpu_usage = 0.0f;
   1823   s_last_frame_number = 0;
   1824   s_last_internal_frame_number = 0;
   1825   s_last_global_tick_counter = 0;
   1826   s_presents_since_last_update = 0;
   1827   s_last_cpu_time = 0;
   1828   s_fps_timer.Reset();
   1829   s_frame_timer.Reset();
   1830   s_frame_time_history.fill(0.0f);
   1831   s_frame_time_history_pos = 0;
   1832 
   1833   TimingEvents::Initialize();
   1834 
   1835   CPU::Initialize();
   1836 
   1837   if (!Bus::Initialize())
   1838   {
   1839     CPU::Shutdown();
   1840     return false;
   1841   }
   1842 
   1843   CPU::CodeCache::Initialize();
   1844 
   1845   if (!CreateGPU(force_software_renderer ? GPURenderer::Software : g_settings.gpu_renderer, false, error))
   1846   {
   1847     Bus::Shutdown();
   1848     CPU::Shutdown();
   1849     return false;
   1850   }
   1851 
   1852   GTE::UpdateAspectRatio();
   1853 
   1854   if (g_settings.gpu_pgxp_enable)
   1855     CPU::PGXP::Initialize();
   1856 
   1857   // Was startup cancelled? (e.g. shading compilers took too long and the user closed the application)
   1858   if (IsStartupCancelled())
   1859   {
   1860     g_gpu.reset();
   1861     if (!s_keep_gpu_device_on_shutdown)
   1862     {
   1863       Host::ReleaseGPUDevice();
   1864       Host::ReleaseRenderWindow();
   1865     }
   1866     if (g_settings.gpu_pgxp_enable)
   1867       CPU::PGXP::Shutdown();
   1868     CPU::Shutdown();
   1869     Bus::Shutdown();
   1870     return false;
   1871   }
   1872 
   1873   DMA::Initialize();
   1874   CDROM::Initialize();
   1875   Pad::Initialize();
   1876   Timers::Initialize();
   1877   SPU::Initialize();
   1878   MDEC::Initialize();
   1879   SIO::Initialize();
   1880   PCDrv::Initialize();
   1881   PostProcessing::Initialize();
   1882 
   1883   s_cpu_thread_handle = Threading::ThreadHandle::GetForCallingThread();
   1884 
   1885   UpdateThrottlePeriod();
   1886   UpdateMemorySaveStateSettings();
   1887   return true;
   1888 }
   1889 
   1890 void System::DestroySystem()
   1891 {
   1892   DebugAssert(!s_system_executing);
   1893   if (s_state == State::Shutdown)
   1894     return;
   1895 
   1896   if (s_media_capture)
   1897     StopMediaCapture();
   1898 
   1899   s_undo_load_state.reset();
   1900 
   1901 #ifdef ENABLE_GDB_SERVER
   1902   GDBServer::Shutdown();
   1903 #endif
   1904 
   1905   Host::ClearOSDMessages();
   1906 
   1907   PostProcessing::Shutdown();
   1908 
   1909   SaveStateSelectorUI::Clear();
   1910   FullscreenUI::OnSystemDestroyed();
   1911 
   1912   InputManager::PauseVibration();
   1913   InputManager::UpdateHostMouseMode();
   1914 
   1915   if (g_settings.inhibit_screensaver)
   1916     PlatformMisc::ResumeScreensaver();
   1917 
   1918   SetTimerResolutionIncreased(false);
   1919 
   1920   s_cpu_thread_usage = {};
   1921 
   1922   ClearMemorySaveStates();
   1923 
   1924   TextureReplacements::Shutdown();
   1925 
   1926   PCDrv::Shutdown();
   1927   SIO::Shutdown();
   1928   MDEC::Shutdown();
   1929   SPU::Shutdown();
   1930   Timers::Shutdown();
   1931   Pad::Shutdown();
   1932   CDROM::Shutdown();
   1933   g_gpu.reset();
   1934   DMA::Shutdown();
   1935   CPU::PGXP::Shutdown();
   1936   CPU::CodeCache::Shutdown();
   1937   Bus::Shutdown();
   1938   CPU::Shutdown();
   1939   TimingEvents::Shutdown();
   1940   ClearRunningGame();
   1941 
   1942   // Restore present-all-frames behavior.
   1943   if (s_keep_gpu_device_on_shutdown && g_gpu_device)
   1944   {
   1945     UpdateDisplayVSync();
   1946   }
   1947   else
   1948   {
   1949     Host::ReleaseGPUDevice();
   1950     Host::ReleaseRenderWindow();
   1951   }
   1952 
   1953   s_bios_hash = {};
   1954   s_bios_image_info = nullptr;
   1955   s_exe_override = {};
   1956   s_boot_mode = BootMode::None;
   1957   s_cheat_list.reset();
   1958 
   1959   s_state = State::Shutdown;
   1960 
   1961   Host::OnSystemDestroyed();
   1962   Host::OnIdleStateChanged();
   1963 }
   1964 
   1965 void System::ClearRunningGame()
   1966 {
   1967   UpdateSessionTime(s_running_game_serial);
   1968 
   1969   s_running_game_serial.clear();
   1970   s_running_game_path.clear();
   1971   s_running_game_title.clear();
   1972   s_running_game_entry = nullptr;
   1973   s_running_game_hash = 0;
   1974 
   1975   Host::OnGameChanged(s_running_game_path, s_running_game_serial, s_running_game_title);
   1976 
   1977   Achievements::GameChanged(s_running_game_path, nullptr);
   1978 
   1979   UpdateRichPresence(true);
   1980 }
   1981 
   1982 void System::Execute()
   1983 {
   1984   for (;;)
   1985   {
   1986     switch (s_state)
   1987     {
   1988       case State::Running:
   1989       {
   1990         s_system_executing = true;
   1991 
   1992         // TODO: Purge reset/restore
   1993         g_gpu->RestoreDeviceContext();
   1994         TimingEvents::CommitLeftoverTicks();
   1995 
   1996         if (s_rewind_load_counter >= 0)
   1997           DoRewind();
   1998         else
   1999           CPU::Execute();
   2000 
   2001         s_system_executing = false;
   2002         continue;
   2003       }
   2004 
   2005       case State::Stopping:
   2006       {
   2007         DestroySystem();
   2008         return;
   2009       }
   2010 
   2011       case State::Paused:
   2012       default:
   2013         return;
   2014     }
   2015   }
   2016 }
   2017 
   2018 void System::FrameDone()
   2019 {
   2020   s_frame_number++;
   2021 
   2022   // Vertex buffer is shared, need to flush what we have.
   2023   g_gpu->FlushRender();
   2024 
   2025   // Generate any pending samples from the SPU before sleeping, this way we reduce the chances of underruns.
   2026   // TODO: when running ahead, we can skip this (and the flush above)
   2027   SPU::GeneratePendingSamples();
   2028 
   2029   if (s_cheat_list)
   2030     s_cheat_list->Apply();
   2031 
   2032   if (Achievements::IsActive())
   2033     Achievements::FrameUpdate();
   2034 
   2035 #ifdef ENABLE_DISCORD_PRESENCE
   2036   PollDiscordPresence();
   2037 #endif
   2038 
   2039 #ifdef ENABLE_SOCKET_MULTIPLEXER
   2040   if (s_socket_multiplexer)
   2041     s_socket_multiplexer->PollEventsWithTimeout(0);
   2042 #endif
   2043 
   2044   Host::FrameDone();
   2045 
   2046   if (s_frame_step_request)
   2047   {
   2048     s_frame_step_request = false;
   2049     PauseSystem(true);
   2050   }
   2051 
   2052   // Save states for rewind and runahead.
   2053   if (s_rewind_save_counter >= 0)
   2054   {
   2055     if (s_rewind_save_counter == 0)
   2056     {
   2057       SaveRewindState();
   2058       s_rewind_save_counter = s_rewind_save_frequency;
   2059     }
   2060     else
   2061     {
   2062       s_rewind_save_counter--;
   2063     }
   2064   }
   2065   else if (s_runahead_frames > 0)
   2066   {
   2067     // We don't want to poll during replay, because otherwise we'll lose frames.
   2068     if (s_runahead_replay_frames == 0)
   2069     {
   2070       // For runahead, poll input early, that way we can use the remainder of this frame to replay.
   2071       // *technically* this means higher input latency (by less than a frame), but runahead itself
   2072       // counter-acts that.
   2073       Host::PumpMessagesOnCPUThread();
   2074       InputManager::PollSources();
   2075       g_gpu->RestoreDeviceContext();
   2076       CheckForAndExitExecution();
   2077     }
   2078 
   2079     if (DoRunahead())
   2080     {
   2081       // running ahead, get it done as soon as possible
   2082       return;
   2083     }
   2084 
   2085     SaveRunaheadState();
   2086   }
   2087 
   2088   // Kick off media capture early, might take a while.
   2089   if (s_media_capture && s_media_capture->IsCapturingVideo()) [[unlikely]]
   2090   {
   2091     if (s_media_capture->GetVideoFPS() != GetThrottleFrequency()) [[unlikely]]
   2092     {
   2093       const std::string next_capture_path = s_media_capture->GetNextCapturePath();
   2094       INFO_LOG("Video frame rate changed, switching to new capture file {}", Path::GetFileName(next_capture_path));
   2095 
   2096       const bool was_capturing_audio = s_media_capture->IsCapturingAudio();
   2097       StopMediaCapture();
   2098       if (StartMediaCapture(std::move(next_capture_path), true, was_capturing_audio) &&
   2099           !g_gpu->SendDisplayToMediaCapture(s_media_capture.get())) [[unlikely]]
   2100       {
   2101         StopMediaCapture();
   2102       }
   2103     }
   2104     else
   2105     {
   2106       if (!g_gpu->SendDisplayToMediaCapture(s_media_capture.get())) [[unlikely]]
   2107         StopMediaCapture();
   2108     }
   2109   }
   2110 
   2111   Common::Timer::Value current_time = Common::Timer::GetCurrentValue();
   2112 
   2113   // pre-frame sleep accounting (input lag reduction)
   2114   const Common::Timer::Value pre_frame_sleep_until = s_next_frame_time + s_pre_frame_sleep_time;
   2115   s_last_active_frame_time = current_time - s_frame_start_time;
   2116   if (s_pre_frame_sleep)
   2117     AccumulatePreFrameSleepTime();
   2118 
   2119   // explicit present (frame pacing)
   2120   const bool is_unique_frame = (s_last_presented_internal_frame_number != s_internal_frame_number);
   2121   s_last_presented_internal_frame_number = s_internal_frame_number;
   2122 
   2123   const bool skip_this_frame = (((s_skip_presenting_duplicate_frames && !is_unique_frame &&
   2124                                   s_skipped_frame_count < MAX_SKIPPED_DUPLICATE_FRAME_COUNT) ||
   2125                                  (!s_optimal_frame_pacing && current_time > s_next_frame_time &&
   2126                                   s_skipped_frame_count < MAX_SKIPPED_TIMEOUT_FRAME_COUNT) ||
   2127                                  g_gpu_device->ShouldSkipPresentingFrame()) &&
   2128                                 !s_syncing_to_host_with_vsync && !IsExecutionInterrupted());
   2129   if (!skip_this_frame)
   2130   {
   2131     s_skipped_frame_count = 0;
   2132 
   2133     const bool throttle_before_present = (s_optimal_frame_pacing && s_throttler_enabled && !IsExecutionInterrupted());
   2134     const bool explicit_present = (throttle_before_present && g_gpu_device->GetFeatures().explicit_present);
   2135     if (explicit_present)
   2136     {
   2137       const bool do_present = PresentDisplay(false, true);
   2138       Throttle(current_time);
   2139       if (do_present)
   2140         g_gpu_device->SubmitPresent();
   2141     }
   2142     else
   2143     {
   2144       if (throttle_before_present)
   2145         Throttle(current_time);
   2146 
   2147       PresentDisplay(false, false);
   2148 
   2149       if (!throttle_before_present && s_throttler_enabled && !IsExecutionInterrupted())
   2150         Throttle(current_time);
   2151     }
   2152   }
   2153   else
   2154   {
   2155     DEBUG_LOG("Skipping displaying frame");
   2156     s_skipped_frame_count++;
   2157     if (s_throttler_enabled)
   2158       Throttle(current_time);
   2159   }
   2160 
   2161   // pre-frame sleep (input lag reduction)
   2162   current_time = Common::Timer::GetCurrentValue();
   2163   if (s_pre_frame_sleep)
   2164   {
   2165     // don't sleep if it's under 1ms, because we're just going to overshoot (or spin).
   2166     if (pre_frame_sleep_until > current_time &&
   2167         Common::Timer::ConvertValueToMilliseconds(pre_frame_sleep_until - current_time) >= 1)
   2168     {
   2169       Common::Timer::SleepUntil(pre_frame_sleep_until, true);
   2170       current_time = Common::Timer::GetCurrentValue();
   2171     }
   2172   }
   2173 
   2174   s_frame_start_time = current_time;
   2175 
   2176   // Input poll already done above
   2177   if (s_runahead_frames == 0)
   2178   {
   2179     Host::PumpMessagesOnCPUThread();
   2180     InputManager::PollSources();
   2181     CheckForAndExitExecution();
   2182   }
   2183 
   2184   g_gpu->RestoreDeviceContext();
   2185 
   2186   // Update perf counters *after* throttling, we want to measure from start-of-frame
   2187   // to start-of-frame, not end-of-frame to end-of-frame (will be noisy due to different
   2188   // amounts of computation happening in each frame).
   2189   System::UpdatePerformanceCounters();
   2190 }
   2191 
   2192 void System::SetThrottleFrequency(float frequency)
   2193 {
   2194   if (s_throttle_frequency == frequency)
   2195     return;
   2196 
   2197   s_throttle_frequency = frequency;
   2198   UpdateThrottlePeriod();
   2199 }
   2200 
   2201 void System::UpdateThrottlePeriod()
   2202 {
   2203   if (s_target_speed > std::numeric_limits<double>::epsilon())
   2204   {
   2205     const double target_speed = std::max(static_cast<double>(s_target_speed), std::numeric_limits<double>::epsilon());
   2206     s_frame_period =
   2207       Common::Timer::ConvertSecondsToValue(1.0 / (static_cast<double>(s_throttle_frequency) * target_speed));
   2208   }
   2209   else
   2210   {
   2211     s_frame_period = 1;
   2212   }
   2213 
   2214   ResetThrottler();
   2215 }
   2216 
   2217 void System::ResetThrottler()
   2218 {
   2219   s_next_frame_time = Common::Timer::GetCurrentValue() + s_frame_period;
   2220   s_pre_frame_sleep_time = 0;
   2221 }
   2222 
   2223 void System::Throttle(Common::Timer::Value current_time)
   2224 {
   2225   // If we're running too slow, advance the next frame time based on the time we lost. Effectively skips
   2226   // running those frames at the intended time, because otherwise if we pause in the debugger, we'll run
   2227   // hundreds of frames when we resume.
   2228   if (current_time > s_next_frame_time)
   2229   {
   2230     const Common::Timer::Value diff = static_cast<s64>(current_time) - static_cast<s64>(s_next_frame_time);
   2231     s_next_frame_time += (diff / s_frame_period) * s_frame_period + s_frame_period;
   2232     return;
   2233   }
   2234 
   2235 #ifdef ENABLE_SOCKET_MULTIPLEXER
   2236   // If we are using the socket multiplier, and have clients, then use it to sleep instead.
   2237   // That way in a query->response->query->response chain, we don't process only one message per frame.
   2238   if (s_socket_multiplexer && s_socket_multiplexer->HasAnyClientSockets())
   2239   {
   2240     Common::Timer::Value poll_start_time = current_time;
   2241     for (;;)
   2242     {
   2243       const u32 sleep_ms =
   2244         static_cast<u32>(Common::Timer::ConvertValueToMilliseconds(s_next_frame_time - poll_start_time));
   2245       s_socket_multiplexer->PollEventsWithTimeout(sleep_ms);
   2246       poll_start_time = Common::Timer::GetCurrentValue();
   2247       if (poll_start_time >= s_next_frame_time || (!g_settings.display_optimal_frame_pacing && sleep_ms == 0))
   2248         break;
   2249     }
   2250   }
   2251   else
   2252   {
   2253     // Use a spinwait if we undersleep for all platforms except android.. don't want to burn battery.
   2254     // Linux also seems to do a much better job of waking up at the requested time.
   2255 #if !defined(__linux__)
   2256     Common::Timer::SleepUntil(s_next_frame_time, g_settings.display_optimal_frame_pacing);
   2257 #else
   2258     Common::Timer::SleepUntil(s_next_frame_time, false);
   2259 #endif
   2260   }
   2261 #else
   2262   // No spinwait on Android, see above.
   2263   Common::Timer::SleepUntil(s_next_frame_time, false);
   2264 #endif
   2265 
   2266 #if 0
   2267   DEV_LOG("Asked for {:.2f} ms, slept for {:.2f} ms, {:.2f} ms late",
   2268           Common::Timer::ConvertValueToMilliseconds(s_next_frame_time - current_time),
   2269           Common::Timer::ConvertValueToMilliseconds(Common::Timer::GetCurrentValue() - current_time),
   2270           Common::Timer::ConvertValueToMilliseconds(Common::Timer::GetCurrentValue() - s_next_frame_time));
   2271 #endif
   2272 
   2273   s_next_frame_time += s_frame_period;
   2274 }
   2275 
   2276 void System::SingleStepCPU()
   2277 {
   2278   CPU::SetSingleStepFlag();
   2279 
   2280   // If this gets called when the system is executing, we're not going to end up here..
   2281   if (IsPaused())
   2282     PauseSystem(false);
   2283 }
   2284 
   2285 void System::IncrementInternalFrameNumber()
   2286 {
   2287   s_internal_frame_number++;
   2288 }
   2289 
   2290 bool System::CreateGPU(GPURenderer renderer, bool is_switching, Error* error)
   2291 {
   2292   const RenderAPI api = Settings::GetRenderAPIForRenderer(renderer);
   2293 
   2294   if (!g_gpu_device ||
   2295       (renderer != GPURenderer::Software && !GPUDevice::IsSameRenderAPI(g_gpu_device->GetRenderAPI(), api)))
   2296   {
   2297     if (g_gpu_device)
   2298     {
   2299       WARNING_LOG("Recreating GPU device, expecting {} got {}", GPUDevice::RenderAPIToString(api),
   2300                   GPUDevice::RenderAPIToString(g_gpu_device->GetRenderAPI()));
   2301       PostProcessing::Shutdown();
   2302     }
   2303 
   2304     Host::ReleaseGPUDevice();
   2305     if (!Host::CreateGPUDevice(api, error))
   2306     {
   2307       Host::ReleaseRenderWindow();
   2308       return false;
   2309     }
   2310 
   2311     if (is_switching)
   2312       PostProcessing::Initialize();
   2313   }
   2314 
   2315   if (renderer == GPURenderer::Software)
   2316     g_gpu = GPU::CreateSoftwareRenderer();
   2317   else
   2318     g_gpu = GPU::CreateHardwareRenderer();
   2319 
   2320   if (!g_gpu)
   2321   {
   2322     ERROR_LOG("Failed to initialize {} renderer, falling back to software renderer",
   2323               Settings::GetRendererName(renderer));
   2324     Host::AddOSDMessage(
   2325       fmt::format(TRANSLATE_FS("System", "Failed to initialize {} renderer, falling back to software renderer."),
   2326                   Settings::GetRendererName(renderer)),
   2327       Host::OSD_CRITICAL_ERROR_DURATION);
   2328     g_gpu.reset();
   2329     g_gpu = GPU::CreateSoftwareRenderer();
   2330     if (!g_gpu)
   2331     {
   2332       ERROR_LOG("Failed to create fallback software renderer.");
   2333       if (!s_keep_gpu_device_on_shutdown)
   2334       {
   2335         PostProcessing::Shutdown();
   2336         Host::ReleaseGPUDevice();
   2337         Host::ReleaseRenderWindow();
   2338       }
   2339       return false;
   2340     }
   2341   }
   2342 
   2343   return true;
   2344 }
   2345 
   2346 bool System::DoState(StateWrapper& sw, GPUTexture** host_texture, bool update_display, bool is_memory_state)
   2347 {
   2348   if (!sw.DoMarker("System"))
   2349     return false;
   2350 
   2351   sw.Do(&s_region);
   2352   sw.Do(&s_frame_number);
   2353   sw.Do(&s_internal_frame_number);
   2354 
   2355   // Don't bother checking this at all for memory states, since they won't have a different BIOS...
   2356   if (!is_memory_state)
   2357   {
   2358     BIOS::ImageInfo::Hash bios_hash = s_bios_hash;
   2359     sw.DoBytesEx(bios_hash.data(), BIOS::ImageInfo::HASH_SIZE, 58, s_bios_hash.data());
   2360     if (bios_hash != s_bios_hash)
   2361     {
   2362       WARNING_LOG("BIOS hash mismatch: System: {} | State: {}", BIOS::ImageInfo::GetHashString(s_bios_hash),
   2363                   BIOS::ImageInfo::GetHashString(bios_hash));
   2364       Host::AddIconOSDMessage(
   2365         "StateBIOSMismatch", ICON_FA_EXCLAMATION_TRIANGLE,
   2366         TRANSLATE_STR("System", "This save state was created with a different BIOS. This may cause stability issues."),
   2367         Host::OSD_WARNING_DURATION);
   2368     }
   2369   }
   2370 
   2371   if (!sw.DoMarker("CPU") || !CPU::DoState(sw))
   2372     return false;
   2373 
   2374   if (sw.IsReading())
   2375   {
   2376     if (is_memory_state)
   2377       CPU::CodeCache::InvalidateAllRAMBlocks();
   2378     else
   2379       CPU::CodeCache::Reset();
   2380   }
   2381 
   2382   // only reset pgxp if we're not runahead-rollbacking. the value checks will save us from broken rendering, and it
   2383   // saves using imprecise values for a frame in 30fps games.
   2384   if (sw.IsReading() && g_settings.gpu_pgxp_enable && !is_memory_state)
   2385     CPU::PGXP::Reset();
   2386 
   2387   if (!sw.DoMarker("Bus") || !Bus::DoState(sw))
   2388     return false;
   2389 
   2390   if (!sw.DoMarker("DMA") || !DMA::DoState(sw))
   2391     return false;
   2392 
   2393   if (!sw.DoMarker("InterruptController") || !InterruptController::DoState(sw))
   2394     return false;
   2395 
   2396   g_gpu->RestoreDeviceContext();
   2397   if (!sw.DoMarker("GPU") || !g_gpu->DoState(sw, host_texture, update_display))
   2398     return false;
   2399 
   2400   if (!sw.DoMarker("CDROM") || !CDROM::DoState(sw))
   2401     return false;
   2402 
   2403   if (!sw.DoMarker("Pad") || !Pad::DoState(sw, is_memory_state))
   2404     return false;
   2405 
   2406   if (!sw.DoMarker("Timers") || !Timers::DoState(sw))
   2407     return false;
   2408 
   2409   if (!sw.DoMarker("SPU") || !SPU::DoState(sw))
   2410     return false;
   2411 
   2412   if (!sw.DoMarker("MDEC") || !MDEC::DoState(sw))
   2413     return false;
   2414 
   2415   if (!sw.DoMarker("SIO") || !SIO::DoState(sw))
   2416     return false;
   2417 
   2418   if (!sw.DoMarker("Events") || !TimingEvents::DoState(sw))
   2419     return false;
   2420 
   2421   if (!sw.DoMarker("Overclock"))
   2422     return false;
   2423 
   2424   bool cpu_overclock_active = g_settings.cpu_overclock_active;
   2425   u32 cpu_overclock_numerator = g_settings.cpu_overclock_numerator;
   2426   u32 cpu_overclock_denominator = g_settings.cpu_overclock_denominator;
   2427   sw.Do(&cpu_overclock_active);
   2428   sw.Do(&cpu_overclock_numerator);
   2429   sw.Do(&cpu_overclock_denominator);
   2430 
   2431   if (sw.IsReading() && (cpu_overclock_active != g_settings.cpu_overclock_active ||
   2432                          (cpu_overclock_active && (g_settings.cpu_overclock_numerator != cpu_overclock_numerator ||
   2433                                                    g_settings.cpu_overclock_denominator != cpu_overclock_denominator))))
   2434   {
   2435     Host::AddIconOSDMessage(
   2436       "state_overclock_difference", ICON_FA_EXCLAMATION_TRIANGLE,
   2437       fmt::format(TRANSLATE_FS("System", "WARNING: CPU overclock ({}%) was different in save state ({}%)."),
   2438                   g_settings.cpu_overclock_enable ? g_settings.GetCPUOverclockPercent() : 100u,
   2439                   cpu_overclock_active ?
   2440                     Settings::CPUOverclockFractionToPercent(cpu_overclock_numerator, cpu_overclock_denominator) :
   2441                     100u),
   2442       Host::OSD_WARNING_DURATION);
   2443     UpdateOverclock();
   2444   }
   2445 
   2446   if (!is_memory_state)
   2447   {
   2448     if (sw.GetVersion() >= 56) [[unlikely]]
   2449     {
   2450       if (!sw.DoMarker("Cheevos"))
   2451         return false;
   2452 
   2453       if (!Achievements::DoState(sw))
   2454         return false;
   2455     }
   2456     else
   2457     {
   2458       // loading an old state without cheevos, so reset the runtime
   2459       Achievements::ResetClient();
   2460     }
   2461   }
   2462 
   2463   return !sw.HasError();
   2464 }
   2465 
   2466 bool System::LoadBIOS(Error* error)
   2467 {
   2468   std::optional<BIOS::Image> bios_image = BIOS::GetBIOSImage(s_region, error);
   2469   if (!bios_image.has_value())
   2470     return false;
   2471 
   2472   s_bios_image_info = bios_image->info;
   2473   s_bios_hash = bios_image->hash;
   2474   if (s_bios_image_info)
   2475     INFO_LOG("Using BIOS: {}", s_bios_image_info->description);
   2476   else
   2477     WARNING_LOG("Using an unknown BIOS: {}", BIOS::ImageInfo::GetHashString(s_bios_hash));
   2478 
   2479   std::memcpy(Bus::g_bios, bios_image->data.data(), Bus::BIOS_SIZE);
   2480   return true;
   2481 }
   2482 
   2483 void System::InternalReset()
   2484 {
   2485   if (IsShutdown())
   2486     return;
   2487 
   2488   TimingEvents::Reset();
   2489   CPU::Reset();
   2490   CPU::CodeCache::Reset();
   2491   if (g_settings.gpu_pgxp_enable)
   2492     CPU::PGXP::Initialize();
   2493 
   2494   Bus::Reset();
   2495   DMA::Reset();
   2496   InterruptController::Reset();
   2497   g_gpu->Reset(true);
   2498   CDROM::Reset();
   2499   Pad::Reset();
   2500   Timers::Reset();
   2501   SPU::Reset();
   2502   MDEC::Reset();
   2503   SIO::Reset();
   2504   PCDrv::Reset();
   2505   Achievements::ResetClient();
   2506   s_frame_number = 1;
   2507   s_internal_frame_number = 0;
   2508 }
   2509 
   2510 bool System::SetBootMode(BootMode new_boot_mode, Error* error)
   2511 {
   2512   // Can we actually fast boot? If starting, s_bios_image_info won't be valid.
   2513   const bool can_fast_boot =
   2514     (CDROM::IsMediaPS1Disc() &&
   2515      (s_state == State::Starting || (s_bios_image_info && s_bios_image_info->SupportsFastBoot())));
   2516   const System::BootMode actual_new_boot_mode =
   2517     (new_boot_mode == BootMode::FastBoot) ? (can_fast_boot ? BootMode::FastBoot : BootMode::FullBoot) : new_boot_mode;
   2518   if (actual_new_boot_mode == s_boot_mode)
   2519     return true;
   2520 
   2521   // Need to reload the BIOS to wipe out the patching.
   2522   if (!LoadBIOS(error))
   2523     return false;
   2524 
   2525   s_boot_mode = actual_new_boot_mode;
   2526   if (s_boot_mode == BootMode::FastBoot)
   2527   {
   2528     if (s_bios_image_info && s_bios_image_info->SupportsFastBoot())
   2529     {
   2530       // Patch BIOS, this sucks.
   2531       INFO_LOG("Patching BIOS for fast boot.");
   2532       if (!BIOS::PatchBIOSFastBoot(Bus::g_bios, Bus::BIOS_SIZE, s_bios_image_info->fastboot_patch))
   2533         s_boot_mode = BootMode::FullBoot;
   2534     }
   2535     else
   2536     {
   2537       ERROR_LOG("Cannot fast boot, BIOS is incompatible.");
   2538       s_boot_mode = BootMode::FullBoot;
   2539     }
   2540   }
   2541 
   2542   return true;
   2543 }
   2544 
   2545 size_t System::GetMaxSaveStateSize()
   2546 {
   2547   // 5 megabytes is sufficient for now, at the moment they're around 4.3MB, or 10.3MB with 8MB RAM enabled.
   2548   static constexpr u32 MAX_2MB_SAVE_STATE_SIZE = 5 * 1024 * 1024;
   2549   static constexpr u32 MAX_8MB_SAVE_STATE_SIZE = 11 * 1024 * 1024;
   2550   const bool is_8mb_ram = (System::IsValid() ? (Bus::g_ram_size > Bus::RAM_2MB_SIZE) : g_settings.enable_8mb_ram);
   2551   return is_8mb_ram ? MAX_8MB_SAVE_STATE_SIZE : MAX_2MB_SAVE_STATE_SIZE;
   2552 }
   2553 
   2554 std::string System::GetMediaPathFromSaveState(const char* path)
   2555 {
   2556   SaveStateBuffer buffer;
   2557   auto fp = FileSystem::OpenManagedCFile(path, "rb", nullptr);
   2558   if (fp)
   2559     LoadStateBufferFromFile(&buffer, fp.get(), nullptr, false, true, false, false);
   2560 
   2561   return std::move(buffer.media_path);
   2562 }
   2563 
   2564 bool System::LoadState(const char* path, Error* error, bool save_undo_state)
   2565 {
   2566   if (!IsValid())
   2567   {
   2568     Error::SetStringView(error, "System is not booted.");
   2569     return false;
   2570   }
   2571 
   2572   if (Achievements::IsHardcoreModeActive())
   2573   {
   2574     Achievements::ConfirmHardcoreModeDisableAsync(TRANSLATE("Achievements", "Loading state"),
   2575                                                   [path = std::string(path), save_undo_state](bool approved) {
   2576                                                     if (approved)
   2577                                                       LoadState(path.c_str(), nullptr, save_undo_state);
   2578                                                   });
   2579     return true;
   2580   }
   2581 
   2582   Common::Timer load_timer;
   2583 
   2584   auto fp = FileSystem::OpenManagedCFile(path, "rb", error);
   2585   if (!fp)
   2586   {
   2587     Error::AddPrefixFmt(error, "Failed to open '{}': ", Path::GetFileName(path));
   2588     return false;
   2589   }
   2590 
   2591   INFO_LOG("Loading state from '{}'...", path);
   2592 
   2593   Host::AddIconOSDMessage(
   2594     "load_state", ICON_EMOJI_OPEN_THE_FOLDER,
   2595     fmt::format(TRANSLATE_FS("OSDMessage", "Loading state from '{}'..."), Path::GetFileName(path)),
   2596     Host::OSD_INFO_DURATION);
   2597 
   2598   if (save_undo_state)
   2599     SaveUndoLoadState();
   2600 
   2601   SaveStateBuffer buffer;
   2602   if (!LoadStateBufferFromFile(&buffer, fp.get(), error, false, true, false, true) ||
   2603       !LoadStateFromBuffer(buffer, error, true))
   2604   {
   2605     if (save_undo_state)
   2606       UndoLoadState();
   2607 
   2608     return false;
   2609   }
   2610 
   2611   ResetPerformanceCounters();
   2612   ResetThrottler();
   2613 
   2614   if (IsPaused())
   2615     InvalidateDisplay();
   2616 
   2617   VERBOSE_LOG("Loading state took {:.2f} msec", load_timer.GetTimeMilliseconds());
   2618   return true;
   2619 }
   2620 
   2621 bool System::LoadStateFromBuffer(const SaveStateBuffer& buffer, Error* error, bool update_display)
   2622 {
   2623   Assert(IsValid());
   2624 
   2625   std::unique_ptr<CDImage> media;
   2626   std::unique_ptr<CDImage> old_media = CDROM::RemoveMedia(false);
   2627   std::string_view media_path = buffer.media_path;
   2628   u32 media_subimage_index = buffer.media_subimage_index;
   2629   if (old_media && old_media->GetFileName() == buffer.media_path)
   2630   {
   2631     INFO_LOG("Re-using same media '{}'", buffer.media_path);
   2632     media = std::move(old_media);
   2633   }
   2634   else if (!buffer.media_path.empty())
   2635   {
   2636     Error local_error;
   2637     media = CDImage::Open(buffer.media_path.c_str(), g_settings.cdrom_load_image_patches, error ? error : &local_error);
   2638     if (!media)
   2639     {
   2640       if (old_media)
   2641       {
   2642         Host::AddOSDMessage(
   2643           fmt::format(TRANSLATE_FS("OSDMessage", "Failed to open CD image from save state '{}': {}.\nUsing "
   2644                                                  "existing image '{}', this may result in instability."),
   2645                       buffer.media_path, error ? error->GetDescription() : local_error.GetDescription(),
   2646                       old_media->GetFileName()),
   2647           Host::OSD_CRITICAL_ERROR_DURATION);
   2648         media = std::move(old_media);
   2649         media_path = media->GetFileName();
   2650         media_subimage_index = media->GetCurrentSubImage();
   2651       }
   2652       else
   2653       {
   2654         Error::AddPrefixFmt(error, TRANSLATE_FS("System", "Failed to open CD image '{}' used by save state:\n"),
   2655                             Path::GetFileName(buffer.media_path));
   2656         return false;
   2657       }
   2658     }
   2659   }
   2660 
   2661   if (media && buffer.version >= 51)
   2662   {
   2663     const u32 num_subimages = media->HasSubImages() ? media->GetSubImageCount() : 1;
   2664     if (media_subimage_index >= num_subimages ||
   2665         (media->HasSubImages() && media->GetCurrentSubImage() != media_subimage_index &&
   2666          !media->SwitchSubImage(media_subimage_index, error)))
   2667     {
   2668       Error::AddPrefixFmt(
   2669         error, TRANSLATE_FS("System", "Failed to switch to subimage {} in CD image '{}' used by save state:\n"),
   2670         media_subimage_index + 1u, Path::GetFileName(media_path));
   2671       return false;
   2672     }
   2673     else
   2674     {
   2675       INFO_LOG("Switched to subimage {} in '{}'", media_subimage_index, buffer.media_path.c_str());
   2676     }
   2677   }
   2678 
   2679   // Skip updating media if there is none, and none in the state. That way we don't wipe out EXE boot.
   2680   if (media)
   2681     UpdateRunningGame(media_path, media.get(), false);
   2682 
   2683   CDROM::Reset();
   2684   if (media)
   2685   {
   2686     const DiscRegion region = GameList::GetCustomRegionForPath(media_path).value_or(GetRegionForImage(media.get()));
   2687     CDROM::InsertMedia(std::move(media), region);
   2688     if (g_settings.cdrom_load_image_to_ram)
   2689       CDROM::PrecacheMedia();
   2690   }
   2691 
   2692   // ensure the correct card is loaded
   2693   if (g_settings.HasAnyPerGameMemoryCards())
   2694     UpdatePerGameMemoryCards();
   2695 
   2696   ClearMemorySaveStates();
   2697 
   2698   // Updating game/loading settings can turn on hardcore mode. Catch this.
   2699   Achievements::DisableHardcoreMode();
   2700 
   2701   StateWrapper sw(buffer.state_data.cspan(0, buffer.state_size), StateWrapper::Mode::Read, buffer.version);
   2702   if (!DoState(sw, nullptr, update_display, false))
   2703   {
   2704     Error::SetStringView(error, "Save state stream is corrupted.");
   2705     return false;
   2706   }
   2707 
   2708   InterruptExecution();
   2709   ResetPerformanceCounters();
   2710   ResetThrottler();
   2711   return true;
   2712 }
   2713 
   2714 bool System::LoadStateBufferFromFile(SaveStateBuffer* buffer, std::FILE* fp, Error* error, bool read_title,
   2715                                      bool read_media_path, bool read_screenshot, bool read_data)
   2716 {
   2717   const s64 file_size = FileSystem::FSize64(fp, error);
   2718   if (file_size < 0)
   2719     return false;
   2720 
   2721   DebugAssert(FileSystem::FTell64(fp) == 0);
   2722 
   2723   SAVE_STATE_HEADER header;
   2724   if (std::fread(&header, sizeof(header), 1, fp) != 1 || header.magic != SAVE_STATE_MAGIC) [[unlikely]]
   2725   {
   2726     Error::SetErrno(error, "fread() for header failed: ", errno);
   2727     return false;
   2728   }
   2729 
   2730   if (header.version < SAVE_STATE_MINIMUM_VERSION)
   2731   {
   2732     Error::SetStringFmt(
   2733       error, TRANSLATE_FS("System", "Save state is incompatible: minimum version is {0} but state is version {1}."),
   2734       SAVE_STATE_MINIMUM_VERSION, header.version);
   2735     return false;
   2736   }
   2737 
   2738   if (header.version > SAVE_STATE_VERSION)
   2739   {
   2740     Error::SetStringFmt(
   2741       error, TRANSLATE_FS("System", "Save state is incompatible: maximum version is {0} but state is version {1}."),
   2742       SAVE_STATE_VERSION, header.version);
   2743     return false;
   2744   }
   2745 
   2746   // Validate offsets.
   2747   if ((static_cast<s64>(header.offset_to_media_path) + header.media_path_length) > file_size ||
   2748       (static_cast<s64>(header.offset_to_screenshot) + header.screenshot_compressed_size) > file_size ||
   2749       header.screenshot_width >= 32768 || header.screenshot_height >= 32768 ||
   2750       (static_cast<s64>(header.offset_to_data) + header.data_compressed_size) > file_size ||
   2751       header.data_uncompressed_size > SAVE_STATE_HEADER::MAX_SAVE_STATE_SIZE) [[unlikely]]
   2752   {
   2753     Error::SetStringView(error, "Save state header is corrupted.");
   2754     return false;
   2755   }
   2756 
   2757   buffer->version = header.version;
   2758 
   2759   if (read_title)
   2760   {
   2761     buffer->title.assign(header.title, StringUtil::Strnlen(header.title, std::size(header.title)));
   2762     buffer->serial.assign(header.serial, StringUtil::Strnlen(header.serial, std::size(header.serial)));
   2763   }
   2764 
   2765   // Read media path.
   2766   if (read_media_path)
   2767   {
   2768     buffer->media_path.resize(header.media_path_length);
   2769     buffer->media_subimage_index = header.media_subimage_index;
   2770     if (header.media_path_length > 0)
   2771     {
   2772       if (!FileSystem::FSeek64(fp, header.offset_to_media_path, SEEK_SET, error)) [[unlikely]]
   2773         return false;
   2774 
   2775       if (std::fread(buffer->media_path.data(), buffer->media_path.length(), 1, fp) != 1) [[unlikely]]
   2776       {
   2777         Error::SetErrno(error, "fread() for media path failed: ", errno);
   2778         return false;
   2779       }
   2780     }
   2781   }
   2782 
   2783   // Read screenshot if requested.
   2784   if (read_screenshot)
   2785   {
   2786     buffer->screenshot.SetSize(header.screenshot_width, header.screenshot_height);
   2787     const u32 uncompressed_size = buffer->screenshot.GetPitch() * buffer->screenshot.GetHeight();
   2788     const u32 compressed_size = (header.version >= 69) ? header.screenshot_compressed_size : uncompressed_size;
   2789     const SAVE_STATE_HEADER::CompressionType compression_type =
   2790       (header.version >= 69) ? static_cast<SAVE_STATE_HEADER::CompressionType>(header.screenshot_compression_type) :
   2791                                SAVE_STATE_HEADER::CompressionType::None;
   2792     if (!ReadAndDecompressStateData(
   2793           fp, std::span<u8>(reinterpret_cast<u8*>(buffer->screenshot.GetPixels()), uncompressed_size),
   2794           header.offset_to_screenshot, compressed_size, compression_type, error)) [[unlikely]]
   2795     {
   2796       return false;
   2797     }
   2798   }
   2799 
   2800   // Decompress state data.
   2801   if (read_data)
   2802   {
   2803     buffer->state_data.resize(header.data_uncompressed_size);
   2804     buffer->state_size = header.data_uncompressed_size;
   2805     if (!ReadAndDecompressStateData(fp, buffer->state_data.span(), header.offset_to_data, header.data_compressed_size,
   2806                                     static_cast<SAVE_STATE_HEADER::CompressionType>(header.data_compression_type),
   2807                                     error)) [[unlikely]]
   2808     {
   2809       return false;
   2810     }
   2811   }
   2812 
   2813   return true;
   2814 }
   2815 
   2816 bool System::ReadAndDecompressStateData(std::FILE* fp, std::span<u8> dst, u32 file_offset, u32 compressed_size,
   2817                                         SAVE_STATE_HEADER::CompressionType method, Error* error)
   2818 {
   2819   if (!FileSystem::FSeek64(fp, file_offset, SEEK_SET, error))
   2820     return false;
   2821 
   2822   if (method == SAVE_STATE_HEADER::CompressionType::None)
   2823   {
   2824     // Feed through.
   2825     if (std::fread(dst.data(), dst.size(), 1, fp) != 1) [[unlikely]]
   2826     {
   2827       Error::SetErrno(error, "fread() failed: ", errno);
   2828       return false;
   2829     }
   2830 
   2831     return true;
   2832   }
   2833 
   2834   DynamicHeapArray<u8> compressed_data(compressed_size);
   2835   if (std::fread(compressed_data.data(), compressed_data.size(), 1, fp) != 1)
   2836   {
   2837     Error::SetErrno(error, "fread() failed: ", errno);
   2838     return false;
   2839   }
   2840 
   2841   if (method == SAVE_STATE_HEADER::CompressionType::Deflate)
   2842   {
   2843     uLong source_len = compressed_size;
   2844     uLong dest_len = static_cast<uLong>(dst.size());
   2845     const int err = uncompress2(dst.data(), &dest_len, compressed_data.data(), &source_len);
   2846     if (err != Z_OK) [[unlikely]]
   2847     {
   2848       Error::SetStringFmt(error, "uncompress2() failed: ", err);
   2849       return false;
   2850     }
   2851     else if (dest_len < dst.size()) [[unlikely]]
   2852     {
   2853       Error::SetStringFmt(error, "Only decompressed {} of {} bytes", dest_len, dst.size());
   2854       return false;
   2855     }
   2856 
   2857     if (source_len < compressed_size) [[unlikely]]
   2858       WARNING_LOG("Only consumed {} of {} compressed bytes", source_len, compressed_size);
   2859 
   2860     return true;
   2861   }
   2862   else if (method == SAVE_STATE_HEADER::CompressionType::Zstandard)
   2863   {
   2864     const size_t result = ZSTD_decompress(dst.data(), dst.size(), compressed_data.data(), compressed_size);
   2865     if (ZSTD_isError(result)) [[unlikely]]
   2866     {
   2867       const char* errstr = ZSTD_getErrorString(ZSTD_getErrorCode(result));
   2868       Error::SetStringFmt(error, "ZSTD_decompress() failed: {}", errstr ? errstr : "<unknown>");
   2869       return false;
   2870     }
   2871     else if (result < dst.size())
   2872     {
   2873       Error::SetStringFmt(error, "Only decompressed {} of {} bytes", result, dst.size());
   2874       return false;
   2875     }
   2876 
   2877     return true;
   2878   }
   2879   else [[unlikely]]
   2880   {
   2881     Error::SetStringView(error, "Unknown method.");
   2882     return false;
   2883   }
   2884 }
   2885 
   2886 bool System::SaveState(const char* path, Error* error, bool backup_existing_save)
   2887 {
   2888   if (IsSavingMemoryCards())
   2889   {
   2890     Error::SetStringView(error, TRANSLATE_SV("System", "Cannot save state while memory card is being saved."));
   2891     return false;
   2892   }
   2893 
   2894   Common::Timer save_timer;
   2895 
   2896   SaveStateBuffer buffer;
   2897   if (!SaveStateToBuffer(&buffer, error, 256))
   2898     return false;
   2899 
   2900   // TODO: Do this on a thread pool
   2901 
   2902   if (backup_existing_save && FileSystem::FileExists(path))
   2903   {
   2904     Error backup_error;
   2905     const std::string backup_filename = Path::ReplaceExtension(path, "bak");
   2906     if (!FileSystem::RenamePath(path, backup_filename.c_str(), &backup_error))
   2907     {
   2908       ERROR_LOG("Failed to rename save state backup '{}': {}", Path::GetFileName(backup_filename),
   2909                 backup_error.GetDescription());
   2910     }
   2911   }
   2912 
   2913   auto fp = FileSystem::CreateAtomicRenamedFile(path, "wb", error);
   2914   if (!fp)
   2915   {
   2916     Error::AddPrefixFmt(error, "Cannot open '{}': ", Path::GetFileName(path));
   2917     return false;
   2918   }
   2919 
   2920   INFO_LOG("Saving state to '{}'...", path);
   2921 
   2922   if (!SaveStateBufferToFile(buffer, fp.get(), error, g_settings.save_state_compression))
   2923   {
   2924     FileSystem::DiscardAtomicRenamedFile(fp);
   2925     return false;
   2926   }
   2927 
   2928   Host::AddIconOSDMessage("save_state", ICON_EMOJI_FLOPPY_DISK,
   2929                           fmt::format(TRANSLATE_FS("OSDMessage", "State saved to '{}'."), Path::GetFileName(path)),
   2930                           5.0f);
   2931 
   2932   VERBOSE_LOG("Saving state took {:.2f} msec", save_timer.GetTimeMilliseconds());
   2933   return true;
   2934 }
   2935 
   2936 bool System::SaveStateToBuffer(SaveStateBuffer* buffer, Error* error, u32 screenshot_size /* = 256 */)
   2937 {
   2938   if (IsShutdown()) [[unlikely]]
   2939   {
   2940     Error::SetStringView(error, "System is invalid.");
   2941     return 0;
   2942   }
   2943 
   2944   buffer->title = s_running_game_title;
   2945   buffer->serial = s_running_game_serial;
   2946   buffer->version = SAVE_STATE_VERSION;
   2947   buffer->media_subimage_index = 0;
   2948 
   2949   if (CDROM::HasMedia())
   2950   {
   2951     buffer->media_path = CDROM::GetMediaFileName();
   2952     buffer->media_subimage_index = CDROM::GetMedia()->HasSubImages() ? CDROM::GetMedia()->GetCurrentSubImage() : 0;
   2953   }
   2954 
   2955   // save screenshot
   2956   if (screenshot_size > 0)
   2957   {
   2958     // assume this size is the width
   2959     GSVector4i screenshot_display_rect, screenshot_draw_rect;
   2960     g_gpu->CalculateDrawRect(screenshot_size, screenshot_size, true, true, &screenshot_display_rect,
   2961                              &screenshot_draw_rect);
   2962 
   2963     const u32 screenshot_width = static_cast<u32>(screenshot_display_rect.width());
   2964     const u32 screenshot_height = static_cast<u32>(screenshot_display_rect.height());
   2965     screenshot_draw_rect = screenshot_draw_rect.sub32(screenshot_display_rect.xyxy());
   2966     screenshot_display_rect = screenshot_display_rect.sub32(screenshot_display_rect.xyxy());
   2967     VERBOSE_LOG("Saving {}x{} screenshot for state", screenshot_width, screenshot_height);
   2968 
   2969     std::vector<u32> screenshot_buffer;
   2970     u32 screenshot_stride;
   2971     GPUTexture::Format screenshot_format;
   2972     if (g_gpu->RenderScreenshotToBuffer(screenshot_width, screenshot_height, screenshot_display_rect,
   2973                                         screenshot_draw_rect, false, &screenshot_buffer, &screenshot_stride,
   2974                                         &screenshot_format) &&
   2975         GPUTexture::ConvertTextureDataToRGBA8(screenshot_width, screenshot_height, screenshot_buffer, screenshot_stride,
   2976                                               screenshot_format))
   2977     {
   2978       if (screenshot_stride != (screenshot_width * sizeof(u32)))
   2979       {
   2980         WARNING_LOG("Failed to save {}x{} screenshot for save state due to incorrect stride({})", screenshot_width,
   2981                     screenshot_height, screenshot_stride);
   2982       }
   2983       else
   2984       {
   2985         if (g_gpu_device->UsesLowerLeftOrigin())
   2986         {
   2987           GPUTexture::FlipTextureDataRGBA8(screenshot_width, screenshot_height,
   2988                                            reinterpret_cast<u8*>(screenshot_buffer.data()), screenshot_stride);
   2989         }
   2990 
   2991         buffer->screenshot.SetPixels(screenshot_width, screenshot_height, std::move(screenshot_buffer));
   2992       }
   2993     }
   2994     else
   2995     {
   2996       WARNING_LOG("Failed to save {}x{} screenshot for save state due to render/conversion failure", screenshot_width,
   2997                   screenshot_height);
   2998     }
   2999   }
   3000 
   3001   // write data
   3002   if (buffer->state_data.empty())
   3003     buffer->state_data.resize(GetMaxSaveStateSize());
   3004 
   3005   g_gpu->RestoreDeviceContext();
   3006   StateWrapper sw(buffer->state_data.span(), StateWrapper::Mode::Write, SAVE_STATE_VERSION);
   3007   if (!DoState(sw, nullptr, false, false))
   3008   {
   3009     Error::SetStringView(error, "DoState() failed");
   3010     return false;
   3011   }
   3012 
   3013   buffer->state_size = sw.GetPosition();
   3014   return true;
   3015 }
   3016 
   3017 bool System::SaveStateBufferToFile(const SaveStateBuffer& buffer, std::FILE* fp, Error* error,
   3018                                    SaveStateCompressionMode compression)
   3019 {
   3020   // Header gets rewritten below.
   3021   SAVE_STATE_HEADER header = {};
   3022   header.magic = SAVE_STATE_MAGIC;
   3023   header.version = SAVE_STATE_VERSION;
   3024   StringUtil::Strlcpy(header.title, s_running_game_title.c_str(), sizeof(header.title));
   3025   StringUtil::Strlcpy(header.serial, s_running_game_serial.c_str(), sizeof(header.serial));
   3026 
   3027   u32 file_position = 0;
   3028   DebugAssert(FileSystem::FTell64(fp) == static_cast<s64>(file_position));
   3029   if (std::fwrite(&header, sizeof(header), 1, fp) != 1)
   3030   {
   3031     Error::SetErrno(error, "fwrite() for header failed: ", errno);
   3032     return false;
   3033   }
   3034   file_position += sizeof(header);
   3035 
   3036   if (!buffer.media_path.empty())
   3037   {
   3038     DebugAssert(FileSystem::FTell64(fp) == static_cast<s64>(file_position));
   3039     header.media_path_length = static_cast<u32>(buffer.media_path.length());
   3040     header.offset_to_media_path = file_position;
   3041     if (std::fwrite(buffer.media_path.data(), buffer.media_path.length(), 1, fp) != 1)
   3042     {
   3043       Error::SetErrno(error, "fwrite() for media path failed: ", errno);
   3044       return false;
   3045     }
   3046     file_position += static_cast<u32>(buffer.media_path.length());
   3047   }
   3048 
   3049   if (buffer.screenshot.IsValid())
   3050   {
   3051     DebugAssert(FileSystem::FTell64(fp) == static_cast<s64>(file_position));
   3052     header.screenshot_width = buffer.screenshot.GetWidth();
   3053     header.screenshot_height = buffer.screenshot.GetHeight();
   3054     header.offset_to_screenshot = file_position;
   3055     header.screenshot_compressed_size =
   3056       CompressAndWriteStateData(fp,
   3057                                 std::span<const u8>(reinterpret_cast<const u8*>(buffer.screenshot.GetPixels()),
   3058                                                     buffer.screenshot.GetPitch() * buffer.screenshot.GetHeight()),
   3059                                 compression, &header.screenshot_compression_type, error);
   3060     if (header.screenshot_compressed_size == 0)
   3061       return false;
   3062     file_position += header.screenshot_compressed_size;
   3063   }
   3064 
   3065   DebugAssert(buffer.state_size > 0);
   3066   header.offset_to_data = file_position;
   3067   header.data_uncompressed_size = static_cast<u32>(buffer.state_size);
   3068   header.data_compressed_size = CompressAndWriteStateData(fp, buffer.state_data.cspan(0, buffer.state_size),
   3069                                                           compression, &header.data_compression_type, error);
   3070   if (header.data_compressed_size == 0)
   3071     return false;
   3072 
   3073   INFO_LOG("Save state compression: screenshot {} => {} bytes, data {} => {} bytes",
   3074            buffer.screenshot.GetPitch() * buffer.screenshot.GetHeight(), header.screenshot_compressed_size,
   3075            buffer.state_size, header.data_compressed_size);
   3076 
   3077   if (!FileSystem::FSeek64(fp, 0, SEEK_SET, error))
   3078     return false;
   3079 
   3080   // re-write header
   3081   if (std::fwrite(&header, sizeof(header), 1, fp) != 1 || std::fflush(fp) != 0)
   3082   {
   3083     Error::SetErrno(error, "fwrite()/fflush() to rewrite header failed: {}", errno);
   3084     return false;
   3085   }
   3086 
   3087   return true;
   3088 }
   3089 
   3090 u32 System::CompressAndWriteStateData(std::FILE* fp, std::span<const u8> src, SaveStateCompressionMode method,
   3091                                       u32* header_type, Error* error)
   3092 {
   3093   if (method == SaveStateCompressionMode::Uncompressed)
   3094   {
   3095     if (std::fwrite(src.data(), src.size(), 1, fp) != 1) [[unlikely]]
   3096     {
   3097       Error::SetStringFmt(error, "fwrite() failed: {}", errno);
   3098       return 0;
   3099     }
   3100 
   3101     *header_type = static_cast<u32>(SAVE_STATE_HEADER::CompressionType::None);
   3102     return static_cast<u32>(src.size());
   3103   }
   3104 
   3105   DynamicHeapArray<u8> buffer;
   3106   u32 write_size;
   3107   if (method >= SaveStateCompressionMode::DeflateLow && method <= SaveStateCompressionMode::DeflateHigh)
   3108   {
   3109     const size_t buffer_size = compressBound(static_cast<uLong>(src.size()));
   3110     buffer.resize(buffer_size);
   3111 
   3112     uLongf compressed_size = static_cast<uLongf>(buffer_size);
   3113     const int level =
   3114       ((method == SaveStateCompressionMode::DeflateLow) ?
   3115          Z_BEST_SPEED :
   3116          ((method == SaveStateCompressionMode::DeflateHigh) ? Z_BEST_COMPRESSION : Z_DEFAULT_COMPRESSION));
   3117     const int err = compress2(buffer.data(), &compressed_size, src.data(), static_cast<uLong>(src.size()), level);
   3118     if (err != Z_OK) [[unlikely]]
   3119     {
   3120       Error::SetStringFmt(error, "compress2() failed: {}", err);
   3121       return 0;
   3122     }
   3123 
   3124     *header_type = static_cast<u32>(SAVE_STATE_HEADER::CompressionType::Deflate);
   3125     write_size = static_cast<u32>(compressed_size);
   3126   }
   3127   else if (method >= SaveStateCompressionMode::ZstLow && method <= SaveStateCompressionMode::ZstHigh)
   3128   {
   3129     const size_t buffer_size = ZSTD_compressBound(src.size());
   3130     buffer.resize(buffer_size);
   3131 
   3132     const int level =
   3133       ((method == SaveStateCompressionMode::ZstLow) ? 1 : ((method == SaveStateCompressionMode::ZstHigh) ? 19 : 0));
   3134     const size_t compressed_size = ZSTD_compress(buffer.data(), buffer_size, src.data(), src.size(), level);
   3135     if (ZSTD_isError(compressed_size)) [[unlikely]]
   3136     {
   3137       const char* errstr = ZSTD_getErrorString(ZSTD_getErrorCode(compressed_size));
   3138       Error::SetStringFmt(error, "ZSTD_compress() failed: {}", errstr ? errstr : "<unknown>");
   3139       return 0;
   3140     }
   3141 
   3142     *header_type = static_cast<u32>(SAVE_STATE_HEADER::CompressionType::Zstandard);
   3143     write_size = static_cast<u32>(compressed_size);
   3144   }
   3145   else [[unlikely]]
   3146   {
   3147     Error::SetStringView(error, "Unknown method.");
   3148     return 0;
   3149   }
   3150 
   3151   if (std::fwrite(buffer.data(), write_size, 1, fp) != 1) [[unlikely]]
   3152   {
   3153     Error::SetStringFmt(error, "fwrite() failed: {}", errno);
   3154     return 0;
   3155   }
   3156 
   3157   return write_size;
   3158 }
   3159 
   3160 float System::GetTargetSpeed()
   3161 {
   3162   return s_target_speed;
   3163 }
   3164 
   3165 float System::GetAudioNominalRate()
   3166 {
   3167   return (s_throttler_enabled || s_syncing_to_host_with_vsync) ? s_target_speed : 1.0f;
   3168 }
   3169 
   3170 void System::UpdatePerformanceCounters()
   3171 {
   3172   const float frame_time = static_cast<float>(s_frame_timer.GetTimeMillisecondsAndReset());
   3173   s_minimum_frame_time_accumulator =
   3174     (s_minimum_frame_time_accumulator == 0.0f) ? frame_time : std::min(s_minimum_frame_time_accumulator, frame_time);
   3175   s_average_frame_time_accumulator += frame_time;
   3176   s_maximum_frame_time_accumulator = std::max(s_maximum_frame_time_accumulator, frame_time);
   3177   s_frame_time_history[s_frame_time_history_pos] = frame_time;
   3178   s_frame_time_history_pos = (s_frame_time_history_pos + 1) % NUM_FRAME_TIME_SAMPLES;
   3179 
   3180   // update fps counter
   3181   const Common::Timer::Value now_ticks = Common::Timer::GetCurrentValue();
   3182   const Common::Timer::Value ticks_diff = now_ticks - s_fps_timer.GetStartValue();
   3183   const float time = static_cast<float>(Common::Timer::ConvertValueToSeconds(ticks_diff));
   3184   if (time < PERFORMANCE_COUNTER_UPDATE_INTERVAL)
   3185     return;
   3186 
   3187   const u32 frames_run = s_frame_number - s_last_frame_number;
   3188   const float frames_runf = static_cast<float>(frames_run);
   3189   const GlobalTicks global_tick_counter = GetGlobalTickCounter();
   3190 
   3191   // TODO: Make the math here less rubbish
   3192   const double pct_divider =
   3193     100.0 * (1.0 / ((static_cast<double>(ticks_diff) * static_cast<double>(Threading::GetThreadTicksPerSecond())) /
   3194                     Common::Timer::GetFrequency() / 1000000000.0));
   3195   const double time_divider = 1000.0 * (1.0 / static_cast<double>(Threading::GetThreadTicksPerSecond())) *
   3196                               (1.0 / static_cast<double>(frames_runf));
   3197 
   3198   s_minimum_frame_time = std::exchange(s_minimum_frame_time_accumulator, 0.0f);
   3199   s_average_frame_time = std::exchange(s_average_frame_time_accumulator, 0.0f) / frames_runf;
   3200   s_maximum_frame_time = std::exchange(s_maximum_frame_time_accumulator, 0.0f);
   3201 
   3202   s_vps = static_cast<float>(frames_runf / time);
   3203   s_last_frame_number = s_frame_number;
   3204   s_fps = static_cast<float>(s_internal_frame_number - s_last_internal_frame_number) / time;
   3205   s_last_internal_frame_number = s_internal_frame_number;
   3206   s_speed = static_cast<float>(static_cast<double>(global_tick_counter - s_last_global_tick_counter) /
   3207                                (static_cast<double>(g_ticks_per_second) * time)) *
   3208             100.0f;
   3209   s_last_global_tick_counter = global_tick_counter;
   3210 
   3211   const Threading::Thread* sw_thread = g_gpu->GetSWThread();
   3212   const u64 cpu_time = s_cpu_thread_handle ? s_cpu_thread_handle.GetCPUTime() : 0;
   3213   const u64 sw_time = sw_thread ? sw_thread->GetCPUTime() : 0;
   3214   const u64 cpu_delta = cpu_time - s_last_cpu_time;
   3215   const u64 sw_delta = sw_time - s_last_sw_time;
   3216   s_last_cpu_time = cpu_time;
   3217   s_last_sw_time = sw_time;
   3218 
   3219   s_cpu_thread_usage = static_cast<float>(static_cast<double>(cpu_delta) * pct_divider);
   3220   s_cpu_thread_time = static_cast<float>(static_cast<double>(cpu_delta) * time_divider);
   3221   s_sw_thread_usage = static_cast<float>(static_cast<double>(sw_delta) * pct_divider);
   3222   s_sw_thread_time = static_cast<float>(static_cast<double>(sw_delta) * time_divider);
   3223 
   3224   if (s_media_capture)
   3225     s_media_capture->UpdateCaptureThreadUsage(pct_divider, time_divider);
   3226 
   3227   s_fps_timer.ResetTo(now_ticks);
   3228 
   3229   if (g_gpu_device->IsGPUTimingEnabled())
   3230   {
   3231     s_average_gpu_time = s_accumulated_gpu_time / static_cast<float>(std::max(s_presents_since_last_update, 1u));
   3232     s_gpu_usage = s_accumulated_gpu_time / (time * 10.0f);
   3233   }
   3234   s_accumulated_gpu_time = 0.0f;
   3235   s_presents_since_last_update = 0;
   3236 
   3237   if (g_settings.display_show_gpu_stats)
   3238     g_gpu->UpdateStatistics(frames_run);
   3239 
   3240   if (s_pre_frame_sleep)
   3241     UpdatePreFrameSleepTime();
   3242 
   3243   VERBOSE_LOG("FPS: {:.2f} VPS: {:.2f} CPU: {:.2f} GPU: {:.2f} Average: {:.2f}ms Min: {:.2f}ms Max: {:.2f}ms", s_fps,
   3244               s_vps, s_cpu_thread_usage, s_gpu_usage, s_average_frame_time, s_minimum_frame_time, s_maximum_frame_time);
   3245 
   3246   Host::OnPerformanceCountersUpdated();
   3247 }
   3248 
   3249 void System::ResetPerformanceCounters()
   3250 {
   3251   s_last_frame_number = s_frame_number;
   3252   s_last_internal_frame_number = s_internal_frame_number;
   3253   s_last_global_tick_counter = GetGlobalTickCounter();
   3254   s_last_cpu_time = s_cpu_thread_handle ? s_cpu_thread_handle.GetCPUTime() : 0;
   3255   if (const Threading::Thread* sw_thread = g_gpu->GetSWThread(); sw_thread)
   3256     s_last_sw_time = sw_thread->GetCPUTime();
   3257   else
   3258     s_last_sw_time = 0;
   3259 
   3260   s_average_frame_time_accumulator = 0.0f;
   3261   s_minimum_frame_time_accumulator = 0.0f;
   3262   s_maximum_frame_time_accumulator = 0.0f;
   3263   s_frame_timer.Reset();
   3264   s_fps_timer.Reset();
   3265   ResetThrottler();
   3266 }
   3267 
   3268 void System::AccumulatePreFrameSleepTime()
   3269 {
   3270   DebugAssert(s_pre_frame_sleep);
   3271 
   3272   s_max_active_frame_time = std::max(s_max_active_frame_time, s_last_active_frame_time);
   3273 
   3274   // in case one frame runs over, adjust to compensate
   3275   const Common::Timer::Value max_sleep_time_for_this_frame =
   3276     s_frame_period - std::min(s_last_active_frame_time, s_frame_period);
   3277   if (max_sleep_time_for_this_frame < s_pre_frame_sleep_time)
   3278   {
   3279     s_pre_frame_sleep_time = Common::AlignDown(max_sleep_time_for_this_frame,
   3280                                                static_cast<unsigned int>(Common::Timer::ConvertMillisecondsToValue(1)));
   3281     DEV_LOG("Adjust pre-frame time to {} ms due to overrun of {} ms",
   3282             Common::Timer::ConvertValueToMilliseconds(s_pre_frame_sleep_time),
   3283             Common::Timer::ConvertValueToMilliseconds(s_last_active_frame_time));
   3284   }
   3285 }
   3286 
   3287 void System::UpdatePreFrameSleepTime()
   3288 {
   3289   DebugAssert(s_pre_frame_sleep);
   3290 
   3291   const Common::Timer::Value expected_frame_time =
   3292     s_max_active_frame_time + Common::Timer::ConvertMillisecondsToValue(g_settings.display_pre_frame_sleep_buffer);
   3293   s_pre_frame_sleep_time = Common::AlignDown(s_frame_period - std::min(expected_frame_time, s_frame_period),
   3294                                              static_cast<unsigned int>(Common::Timer::ConvertMillisecondsToValue(1)));
   3295   DEV_LOG("Set pre-frame time to {} ms (expected frame time of {} ms)",
   3296           Common::Timer::ConvertValueToMilliseconds(s_pre_frame_sleep_time),
   3297           Common::Timer::ConvertValueToMilliseconds(expected_frame_time));
   3298 
   3299   s_max_active_frame_time = 0;
   3300 }
   3301 
   3302 void System::FormatLatencyStats(SmallStringBase& str)
   3303 {
   3304   AudioStream* audio_stream = SPU::GetOutputStream();
   3305   const u32 audio_latency =
   3306     AudioStream::GetMSForBufferSize(audio_stream->GetSampleRate(), audio_stream->GetBufferedFramesRelaxed());
   3307 
   3308   const double active_frame_time = std::ceil(Common::Timer::ConvertValueToMilliseconds(s_last_active_frame_time));
   3309   const double pre_frame_time = std::ceil(Common::Timer::ConvertValueToMilliseconds(s_pre_frame_sleep_time));
   3310   const double input_latency = std::ceil(
   3311     Common::Timer::ConvertValueToMilliseconds(s_frame_period - s_pre_frame_sleep_time) -
   3312     Common::Timer::ConvertValueToMilliseconds(static_cast<Common::Timer::Value>(s_runahead_frames) * s_frame_period));
   3313 
   3314   str.format("AF: {:.0f}ms | PF: {:.0f}ms | IL: {:.0f}ms | AL: {}ms", active_frame_time, pre_frame_time, input_latency,
   3315              audio_latency);
   3316 }
   3317 
   3318 void System::UpdateSpeedLimiterState()
   3319 {
   3320   DebugAssert(IsValid());
   3321 
   3322   s_target_speed = s_turbo_enabled ?
   3323                      g_settings.turbo_speed :
   3324                      (s_fast_forward_enabled ? g_settings.fast_forward_speed : g_settings.emulation_speed);
   3325   s_throttler_enabled = (s_target_speed != 0.0f);
   3326   s_optimal_frame_pacing = (s_throttler_enabled && g_settings.display_optimal_frame_pacing);
   3327   s_skip_presenting_duplicate_frames = s_throttler_enabled && g_settings.display_skip_presenting_duplicate_frames;
   3328   s_pre_frame_sleep = s_optimal_frame_pacing && g_settings.display_pre_frame_sleep;
   3329   s_can_sync_to_host = false;
   3330   s_syncing_to_host = false;
   3331   s_syncing_to_host_with_vsync = false;
   3332 
   3333   if (g_settings.sync_to_host_refresh_rate)
   3334   {
   3335     const float host_refresh_rate = g_gpu_device->GetWindowInfo().surface_refresh_rate;
   3336     if (host_refresh_rate > 0.0f)
   3337     {
   3338       const float ratio = host_refresh_rate / System::GetThrottleFrequency();
   3339       s_can_sync_to_host = (ratio >= 0.95f && ratio <= 1.05f);
   3340       INFO_LOG("Refresh rate: Host={}hz Guest={}hz Ratio={} - {}", host_refresh_rate, System::GetThrottleFrequency(),
   3341                ratio, s_can_sync_to_host ? "can sync" : "can't sync");
   3342 
   3343       s_syncing_to_host = (s_can_sync_to_host && g_settings.sync_to_host_refresh_rate && s_target_speed == 1.0f);
   3344       if (s_syncing_to_host)
   3345       {
   3346         s_target_speed = ratio;
   3347 
   3348         // When syncing to host and using vsync, we don't need to sleep.
   3349         s_syncing_to_host_with_vsync = g_settings.display_vsync;
   3350         if (s_syncing_to_host_with_vsync)
   3351         {
   3352           INFO_LOG("Using host vsync for throttling.");
   3353           s_throttler_enabled = false;
   3354         }
   3355       }
   3356     }
   3357   }
   3358 
   3359   VERBOSE_LOG("Target speed: {}%", s_target_speed * 100.0f);
   3360   VERBOSE_LOG("Preset timing: {}", s_optimal_frame_pacing ? "consistent" : "immediate");
   3361 
   3362   // Update audio output.
   3363   AudioStream* stream = SPU::GetOutputStream();
   3364   stream->SetOutputVolume(GetAudioOutputVolume());
   3365   stream->SetNominalRate(GetAudioNominalRate());
   3366 
   3367   UpdateThrottlePeriod();
   3368   ResetThrottler();
   3369   UpdateDisplayVSync();
   3370 
   3371   if (g_settings.increase_timer_resolution)
   3372     SetTimerResolutionIncreased(s_throttler_enabled);
   3373 }
   3374 
   3375 void System::UpdateDisplayVSync()
   3376 {
   3377   static constexpr std::array<const char*, static_cast<size_t>(GPUVSyncMode::Count)> vsync_modes = {{
   3378     "Disabled",
   3379     "FIFO",
   3380     "Mailbox",
   3381   }};
   3382 
   3383   // Avoid flipping vsync on and off by manually throttling when vsync is on.
   3384   const GPUVSyncMode vsync_mode = GetEffectiveVSyncMode();
   3385   const bool allow_present_throttle = ShouldAllowPresentThrottle();
   3386   VERBOSE_LOG("VSync: {}{}{}", vsync_modes[static_cast<size_t>(vsync_mode)],
   3387               s_syncing_to_host_with_vsync ? " (for throttling)" : "",
   3388               allow_present_throttle ? " (present throttle allowed)" : "");
   3389 
   3390   g_gpu_device->SetVSyncMode(vsync_mode, allow_present_throttle);
   3391 }
   3392 
   3393 GPUVSyncMode System::GetEffectiveVSyncMode()
   3394 {
   3395   // Vsync off => always disabled.
   3396   if (!g_settings.display_vsync)
   3397     return GPUVSyncMode::Disabled;
   3398 
   3399   // If there's no VM, or we're using vsync for timing, then we always use double-buffered (blocking).
   3400   // Try to keep the same present mode whether we're running or not, since it'll avoid flicker.
   3401   const bool valid_vm = (s_state != State::Shutdown && s_state != State::Stopping);
   3402   if (s_can_sync_to_host || (!valid_vm && g_settings.sync_to_host_refresh_rate) ||
   3403       g_settings.display_disable_mailbox_presentation)
   3404   {
   3405     return GPUVSyncMode::FIFO;
   3406   }
   3407 
   3408   // For PAL games, we always want to triple buffer, because otherwise we'll be tearing.
   3409   // Or for when we aren't using sync-to-host-refresh, to avoid dropping frames.
   3410   // Allow present skipping when running outside of normal speed, if mailbox isn't supported.
   3411   return GPUVSyncMode::Mailbox;
   3412 }
   3413 
   3414 bool System::ShouldAllowPresentThrottle()
   3415 {
   3416   const bool valid_vm = (s_state != State::Shutdown && s_state != State::Stopping);
   3417   return !valid_vm || IsRunningAtNonStandardSpeed();
   3418 }
   3419 
   3420 bool System::IsFastForwardEnabled()
   3421 {
   3422   return s_fast_forward_enabled;
   3423 }
   3424 
   3425 void System::SetFastForwardEnabled(bool enabled)
   3426 {
   3427   if (!IsValid())
   3428     return;
   3429 
   3430   s_fast_forward_enabled = enabled;
   3431   UpdateSpeedLimiterState();
   3432 }
   3433 
   3434 bool System::IsTurboEnabled()
   3435 {
   3436   return s_turbo_enabled;
   3437 }
   3438 
   3439 void System::SetTurboEnabled(bool enabled)
   3440 {
   3441   if (!IsValid())
   3442     return;
   3443 
   3444   s_turbo_enabled = enabled;
   3445   UpdateSpeedLimiterState();
   3446 }
   3447 
   3448 void System::SetRewindState(bool enabled)
   3449 {
   3450   if (!System::IsValid())
   3451     return;
   3452 
   3453   if (!g_settings.rewind_enable)
   3454   {
   3455     if (enabled)
   3456       Host::AddKeyedOSDMessage("SetRewindState", TRANSLATE_STR("OSDMessage", "Rewinding is not enabled."), 5.0f);
   3457 
   3458     return;
   3459   }
   3460 
   3461   if (Achievements::IsHardcoreModeActive() && enabled)
   3462   {
   3463     Achievements::ConfirmHardcoreModeDisableAsync("Rewinding", [](bool approved) {
   3464       if (approved)
   3465         SetRewindState(true);
   3466     });
   3467     return;
   3468   }
   3469 
   3470   System::SetRewinding(enabled);
   3471   UpdateSpeedLimiterState();
   3472 }
   3473 
   3474 void System::DoFrameStep()
   3475 {
   3476   if (!IsValid())
   3477     return;
   3478 
   3479   if (Achievements::IsHardcoreModeActive())
   3480   {
   3481     Achievements::ConfirmHardcoreModeDisableAsync("Frame stepping", [](bool approved) {
   3482       if (approved)
   3483         DoFrameStep();
   3484     });
   3485     return;
   3486   }
   3487 
   3488   s_frame_step_request = true;
   3489   PauseSystem(false);
   3490 }
   3491 
   3492 void System::DoToggleCheats()
   3493 {
   3494   if (!System::IsValid())
   3495     return;
   3496 
   3497   if (Achievements::IsHardcoreModeActive())
   3498   {
   3499     Achievements::ConfirmHardcoreModeDisableAsync("Toggling cheats", [](bool approved) { DoToggleCheats(); });
   3500     return;
   3501   }
   3502 
   3503   CheatList* cl = GetCheatList();
   3504   if (!cl)
   3505   {
   3506     Host::AddKeyedOSDMessage("ToggleCheats", TRANSLATE_STR("OSDMessage", "No cheats are loaded."), 10.0f);
   3507     return;
   3508   }
   3509 
   3510   cl->SetMasterEnable(!cl->GetMasterEnable());
   3511   Host::AddIconOSDMessage(
   3512     "ToggleCheats", ICON_FA_EXCLAMATION_TRIANGLE,
   3513     cl->GetMasterEnable() ?
   3514       TRANSLATE_PLURAL_STR("System", "%n cheat(s) are now active.", "", cl->GetEnabledCodeCount()) :
   3515       TRANSLATE_PLURAL_STR("System", "%n cheat(s) are now inactive.", "", cl->GetEnabledCodeCount()),
   3516     Host::OSD_QUICK_DURATION);
   3517 }
   3518 
   3519 #if 0
   3520 // currently not used until EXP1 is implemented
   3521 
   3522 bool SetExpansionROM(const char* filename)
   3523 {
   3524   std::FILE* fp = FileSystem::OpenCFile(filename, "rb");
   3525   if (!fp)
   3526   {
   3527     ERROR_LOG("Failed to open '{}'", Path::GetFileName(filename));
   3528     return false;
   3529   }
   3530 
   3531   std::fseek(fp, 0, SEEK_END);
   3532   const u32 size = static_cast<u32>(std::ftell(fp));
   3533   std::fseek(fp, 0, SEEK_SET);
   3534 
   3535   std::vector<u8> data(size);
   3536   if (std::fread(data.data(), size, 1, fp) != 1)
   3537   {
   3538     ERROR_LOG("Failed to read ROM data from '{}'", Path::GetFileName(filename))
   3539     std::fclose(fp);
   3540     return false;
   3541   }
   3542 
   3543   std::fclose(fp);
   3544 
   3545   INFO_LOG("Loaded expansion ROM from '{}': {} bytes", Path::GetFileName(filename), size);
   3546   Bus::SetExpansionROM(std::move(data));
   3547   return true;
   3548 }
   3549 
   3550 #endif
   3551 
   3552 Controller* System::GetController(u32 slot)
   3553 {
   3554   return Pad::GetController(slot);
   3555 }
   3556 
   3557 void System::UpdateControllers()
   3558 {
   3559   auto lock = Host::GetSettingsLock();
   3560 
   3561   for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
   3562   {
   3563     Pad::SetController(i, nullptr);
   3564 
   3565     const ControllerType type = g_settings.controller_types[i];
   3566     if (type != ControllerType::None)
   3567     {
   3568       std::unique_ptr<Controller> controller = Controller::Create(type, i);
   3569       if (controller)
   3570       {
   3571         controller->LoadSettings(*Host::GetSettingsInterface(), Controller::GetSettingsSection(i).c_str(), true);
   3572         Pad::SetController(i, std::move(controller));
   3573       }
   3574     }
   3575   }
   3576 }
   3577 
   3578 void System::UpdateControllerSettings()
   3579 {
   3580   auto lock = Host::GetSettingsLock();
   3581 
   3582   for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
   3583   {
   3584     Controller* controller = Pad::GetController(i);
   3585     if (controller)
   3586       controller->LoadSettings(*Host::GetSettingsInterface(), Controller::GetSettingsSection(i).c_str(), false);
   3587   }
   3588 }
   3589 
   3590 void System::ResetControllers()
   3591 {
   3592   for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
   3593   {
   3594     Controller* controller = Pad::GetController(i);
   3595     if (controller)
   3596       controller->Reset();
   3597   }
   3598 }
   3599 
   3600 std::unique_ptr<MemoryCard> System::GetMemoryCardForSlot(u32 slot, MemoryCardType type)
   3601 {
   3602   // Disable memory cards when running PSFs.
   3603   const bool is_running_psf = !s_running_game_path.empty() && IsPsfFileName(s_running_game_path.c_str());
   3604   if (is_running_psf)
   3605     return nullptr;
   3606 
   3607   std::string message_key = fmt::format("MemoryCard{}SharedWarning", slot);
   3608 
   3609   switch (type)
   3610   {
   3611     case MemoryCardType::PerGame:
   3612     {
   3613       if (s_running_game_serial.empty())
   3614       {
   3615         Host::AddIconOSDMessage(
   3616           std::move(message_key), ICON_FA_SD_CARD,
   3617           fmt::format(TRANSLATE_FS("System", "Per-game memory card cannot be used for slot {} as the running "
   3618                                              "game has no code. Using shared card instead."),
   3619                       slot + 1u),
   3620           Host::OSD_INFO_DURATION);
   3621         return MemoryCard::Open(g_settings.GetSharedMemoryCardPath(slot));
   3622       }
   3623       else
   3624       {
   3625         Host::RemoveKeyedOSDMessage(std::move(message_key));
   3626         return MemoryCard::Open(g_settings.GetGameMemoryCardPath(s_running_game_serial.c_str(), slot));
   3627       }
   3628     }
   3629 
   3630     case MemoryCardType::PerGameTitle:
   3631     {
   3632       if (s_running_game_title.empty())
   3633       {
   3634         Host::AddIconOSDMessage(
   3635           std::move(message_key), ICON_FA_SD_CARD,
   3636           fmt::format(TRANSLATE_FS("System", "Per-game memory card cannot be used for slot {} as the running "
   3637                                              "game has no title. Using shared card instead."),
   3638                       slot + 1u),
   3639           Host::OSD_INFO_DURATION);
   3640         return MemoryCard::Open(g_settings.GetSharedMemoryCardPath(slot));
   3641       }
   3642       else
   3643       {
   3644         std::string card_path;
   3645 
   3646         // Playlist - use title if different.
   3647         if (HasMediaSubImages() && s_running_game_entry && s_running_game_title != s_running_game_entry->title)
   3648         {
   3649           card_path = g_settings.GetGameMemoryCardPath(Path::SanitizeFileName(s_running_game_title), slot);
   3650         }
   3651         // Multi-disc game - use disc set name.
   3652         else if (s_running_game_entry && !s_running_game_entry->disc_set_name.empty())
   3653         {
   3654           card_path =
   3655             g_settings.GetGameMemoryCardPath(Path::SanitizeFileName(s_running_game_entry->disc_set_name), slot);
   3656         }
   3657 
   3658         // But prefer a disc-specific card if one already exists.
   3659         std::string disc_card_path = g_settings.GetGameMemoryCardPath(
   3660           Path::SanitizeFileName((s_running_game_entry && !s_running_game_custom_title) ? s_running_game_entry->title :
   3661                                                                                           s_running_game_title),
   3662           slot);
   3663         if (disc_card_path != card_path)
   3664         {
   3665           if (card_path.empty() || !g_settings.memory_card_use_playlist_title ||
   3666               FileSystem::FileExists(disc_card_path.c_str()))
   3667           {
   3668             if (g_settings.memory_card_use_playlist_title && !card_path.empty())
   3669             {
   3670               Host::AddIconOSDMessage(
   3671                 fmt::format("DiscSpecificMC{}", slot), ICON_FA_SD_CARD,
   3672                 fmt::format(TRANSLATE_FS("System", "Using disc-specific memory card '{}' instead of per-game card."),
   3673                             Path::GetFileName(disc_card_path)),
   3674                 Host::OSD_INFO_DURATION);
   3675             }
   3676 
   3677             card_path = std::move(disc_card_path);
   3678           }
   3679         }
   3680 
   3681         Host::RemoveKeyedOSDMessage(std::move(message_key));
   3682         return MemoryCard::Open(card_path.c_str());
   3683       }
   3684     }
   3685 
   3686     case MemoryCardType::PerGameFileTitle:
   3687     {
   3688       const std::string display_name(FileSystem::GetDisplayNameFromPath(s_running_game_path));
   3689       const std::string_view file_title(Path::GetFileTitle(display_name));
   3690       if (file_title.empty())
   3691       {
   3692         Host::AddIconOSDMessage(
   3693           std::move(message_key), ICON_FA_SD_CARD,
   3694           fmt::format(TRANSLATE_FS("System", "Per-game memory card cannot be used for slot {} as the running "
   3695                                              "game has no path. Using shared card instead."),
   3696                       slot + 1u));
   3697         return MemoryCard::Open(g_settings.GetSharedMemoryCardPath(slot));
   3698       }
   3699       else
   3700       {
   3701         Host::RemoveKeyedOSDMessage(std::move(message_key));
   3702         return MemoryCard::Open(g_settings.GetGameMemoryCardPath(Path::SanitizeFileName(file_title).c_str(), slot));
   3703       }
   3704     }
   3705 
   3706     case MemoryCardType::Shared:
   3707     {
   3708       Host::RemoveKeyedOSDMessage(std::move(message_key));
   3709       return MemoryCard::Open(g_settings.GetSharedMemoryCardPath(slot));
   3710     }
   3711 
   3712     case MemoryCardType::NonPersistent:
   3713     {
   3714       Host::RemoveKeyedOSDMessage(std::move(message_key));
   3715       return MemoryCard::Create();
   3716     }
   3717 
   3718     case MemoryCardType::None:
   3719     default:
   3720     {
   3721       Host::RemoveKeyedOSDMessage(std::move(message_key));
   3722       return nullptr;
   3723     }
   3724   }
   3725 }
   3726 
   3727 void System::UpdateMemoryCardTypes()
   3728 {
   3729   for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
   3730   {
   3731     Pad::SetMemoryCard(i, nullptr);
   3732 
   3733     const MemoryCardType type = g_settings.memory_card_types[i];
   3734     std::unique_ptr<MemoryCard> card = GetMemoryCardForSlot(i, type);
   3735     if (card)
   3736     {
   3737       if (const std::string& filename = card->GetFilename(); !filename.empty())
   3738         INFO_LOG("Memory Card Slot {}: {}", i + 1, filename);
   3739 
   3740       Pad::SetMemoryCard(i, std::move(card));
   3741     }
   3742   }
   3743 }
   3744 
   3745 void System::UpdatePerGameMemoryCards()
   3746 {
   3747   for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
   3748   {
   3749     const MemoryCardType type = g_settings.memory_card_types[i];
   3750     if (!Settings::IsPerGameMemoryCardType(type))
   3751       continue;
   3752 
   3753     Pad::SetMemoryCard(i, nullptr);
   3754 
   3755     std::unique_ptr<MemoryCard> card = GetMemoryCardForSlot(i, type);
   3756     if (card)
   3757     {
   3758       if (const std::string& filename = card->GetFilename(); !filename.empty())
   3759         INFO_LOG("Memory Card Slot {}: {}", i + 1, filename);
   3760 
   3761       Pad::SetMemoryCard(i, std::move(card));
   3762     }
   3763   }
   3764 }
   3765 
   3766 bool System::HasMemoryCard(u32 slot)
   3767 {
   3768   return (Pad::GetMemoryCard(slot) != nullptr);
   3769 }
   3770 
   3771 bool System::IsSavingMemoryCards()
   3772 {
   3773   for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
   3774   {
   3775     MemoryCard* card = Pad::GetMemoryCard(i);
   3776     if (card && card->IsOrWasRecentlyWriting())
   3777       return true;
   3778   }
   3779 
   3780   return false;
   3781 }
   3782 
   3783 void System::SwapMemoryCards()
   3784 {
   3785   if (!IsValid())
   3786     return;
   3787 
   3788   std::unique_ptr<MemoryCard> first = Pad::RemoveMemoryCard(0);
   3789   std::unique_ptr<MemoryCard> second = Pad::RemoveMemoryCard(1);
   3790   Pad::SetMemoryCard(0, std::move(second));
   3791   Pad::SetMemoryCard(1, std::move(first));
   3792 
   3793   if (HasMemoryCard(0) && HasMemoryCard(1))
   3794   {
   3795     Host::AddOSDMessage(TRANSLATE_STR("OSDMessage", "Swapped memory card ports. Both ports have a memory card."),
   3796                         10.0f);
   3797   }
   3798   else if (HasMemoryCard(1))
   3799   {
   3800     Host::AddOSDMessage(
   3801       TRANSLATE_STR("OSDMessage", "Swapped memory card ports. Port 2 has a memory card, Port 1 is empty."), 10.0f);
   3802   }
   3803   else if (HasMemoryCard(0))
   3804   {
   3805     Host::AddOSDMessage(
   3806       TRANSLATE_STR("OSDMessage", "Swapped memory card ports. Port 1 has a memory card, Port 2 is empty."), 10.0f);
   3807   }
   3808   else
   3809   {
   3810     Host::AddOSDMessage(TRANSLATE_STR("OSDMessage", "Swapped memory card ports. Neither port has a memory card."),
   3811                         10.0f);
   3812   }
   3813 }
   3814 
   3815 void System::UpdateMultitaps()
   3816 {
   3817   switch (g_settings.multitap_mode)
   3818   {
   3819     case MultitapMode::Disabled:
   3820     {
   3821       Pad::GetMultitap(0)->SetEnable(false, 0);
   3822       Pad::GetMultitap(1)->SetEnable(false, 0);
   3823     }
   3824     break;
   3825 
   3826     case MultitapMode::Port1Only:
   3827     {
   3828       Pad::GetMultitap(0)->SetEnable(true, 0);
   3829       Pad::GetMultitap(1)->SetEnable(false, 0);
   3830     }
   3831     break;
   3832 
   3833     case MultitapMode::Port2Only:
   3834     {
   3835       Pad::GetMultitap(0)->SetEnable(false, 0);
   3836       Pad::GetMultitap(1)->SetEnable(true, 1);
   3837     }
   3838     break;
   3839 
   3840     case MultitapMode::BothPorts:
   3841     {
   3842       Pad::GetMultitap(0)->SetEnable(true, 0);
   3843       Pad::GetMultitap(1)->SetEnable(true, 4);
   3844     }
   3845     break;
   3846 
   3847     default:
   3848       UnreachableCode();
   3849       break;
   3850   }
   3851 }
   3852 
   3853 bool System::DumpRAM(const char* filename)
   3854 {
   3855   if (!IsValid())
   3856     return false;
   3857 
   3858   return FileSystem::WriteBinaryFile(filename, Bus::g_unprotected_ram, Bus::g_ram_size);
   3859 }
   3860 
   3861 bool System::DumpVRAM(const char* filename)
   3862 {
   3863   if (!IsValid())
   3864     return false;
   3865 
   3866   g_gpu->RestoreDeviceContext();
   3867   return g_gpu->DumpVRAMToFile(filename);
   3868 }
   3869 
   3870 bool System::DumpSPURAM(const char* filename)
   3871 {
   3872   if (!IsValid())
   3873     return false;
   3874 
   3875   return FileSystem::WriteBinaryFile(filename, SPU::GetRAM().data(), SPU::RAM_SIZE);
   3876 }
   3877 
   3878 bool System::HasMedia()
   3879 {
   3880   return CDROM::HasMedia();
   3881 }
   3882 
   3883 std::string System::GetMediaFileName()
   3884 {
   3885   if (!CDROM::HasMedia())
   3886     return {};
   3887 
   3888   return CDROM::GetMediaFileName();
   3889 }
   3890 
   3891 bool System::InsertMedia(const char* path)
   3892 {
   3893   Error error;
   3894   std::unique_ptr<CDImage> image = CDImage::Open(path, g_settings.cdrom_load_image_patches, &error);
   3895   if (!image)
   3896   {
   3897     Host::AddIconOSDMessage(
   3898       "DiscInserted", ICON_FA_COMPACT_DISC,
   3899       fmt::format(TRANSLATE_FS("OSDMessage", "Failed to open disc image '{}': {}."), path, error.GetDescription()),
   3900       Host::OSD_ERROR_DURATION);
   3901     return false;
   3902   }
   3903 
   3904   const DiscRegion region = GameList::GetCustomRegionForPath(path).value_or(GetRegionForImage(image.get()));
   3905   UpdateRunningGame(path, image.get(), false);
   3906   CDROM::InsertMedia(std::move(image), region);
   3907   INFO_LOG("Inserted media from {} ({}, {})", s_running_game_path, s_running_game_serial, s_running_game_title);
   3908   if (g_settings.cdrom_load_image_to_ram)
   3909     CDROM::PrecacheMedia();
   3910 
   3911   Host::AddIconOSDMessage(
   3912     "DiscInserted", ICON_FA_COMPACT_DISC,
   3913     fmt::format(TRANSLATE_FS("OSDMessage", "Inserted disc '{}' ({})."), s_running_game_title, s_running_game_serial),
   3914     Host::OSD_INFO_DURATION);
   3915 
   3916   if (g_settings.HasAnyPerGameMemoryCards())
   3917   {
   3918     Host::AddIconOSDMessage("ReloadMemoryCardsFromGameChange", ICON_FA_SD_CARD,
   3919                             TRANSLATE_STR("System", "Game changed, reloading memory cards."), Host::OSD_INFO_DURATION);
   3920     UpdatePerGameMemoryCards();
   3921   }
   3922 
   3923   ClearMemorySaveStates();
   3924   return true;
   3925 }
   3926 
   3927 void System::RemoveMedia()
   3928 {
   3929   CDROM::RemoveMedia(false);
   3930   ClearMemorySaveStates();
   3931 }
   3932 
   3933 void System::UpdateRunningGame(const std::string_view path, CDImage* image, bool booting)
   3934 {
   3935   if (!booting && s_running_game_path == path)
   3936     return;
   3937 
   3938   const std::string prev_serial = std::move(s_running_game_serial);
   3939 
   3940   s_running_game_path.clear();
   3941   s_running_game_serial = {};
   3942   s_running_game_title.clear();
   3943   s_running_game_entry = nullptr;
   3944   s_running_game_hash = 0;
   3945   s_running_game_custom_title = false;
   3946 
   3947   if (!path.empty())
   3948   {
   3949     s_running_game_path = path;
   3950     s_running_game_title = GameList::GetCustomTitleForPath(s_running_game_path);
   3951     s_running_game_custom_title = !s_running_game_title.empty();
   3952 
   3953     if (IsExeFileName(path))
   3954     {
   3955       if (s_running_game_title.empty())
   3956         s_running_game_title = Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(path));
   3957 
   3958       s_running_game_hash = GetGameHashFromFile(s_running_game_path.c_str());
   3959       if (s_running_game_hash != 0)
   3960         s_running_game_serial = GetGameHashId(s_running_game_hash);
   3961     }
   3962     else if (IsPsfFileName(path))
   3963     {
   3964       // TODO: We could pull the title from the PSF.
   3965       if (s_running_game_title.empty())
   3966         s_running_game_title = Path::GetFileTitle(path);
   3967     }
   3968     // Check for an audio CD. Those shouldn't set any title.
   3969     else if (image && image->GetTrack(1).mode != CDImage::TrackMode::Audio)
   3970     {
   3971       std::string id;
   3972       GetGameDetailsFromImage(image, &id, &s_running_game_hash);
   3973 
   3974       s_running_game_entry = GameDatabase::GetEntryForGameDetails(id, s_running_game_hash);
   3975       if (s_running_game_entry)
   3976       {
   3977         s_running_game_serial = s_running_game_entry->serial;
   3978         if (s_running_game_title.empty())
   3979           s_running_game_title = s_running_game_entry->title;
   3980       }
   3981       else
   3982       {
   3983         s_running_game_serial = std::move(id);
   3984         if (s_running_game_title.empty())
   3985           s_running_game_title = Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(path));
   3986       }
   3987 
   3988       if (image->HasSubImages())
   3989       {
   3990         std::string image_title = image->GetMetadata("title");
   3991         if (!image_title.empty())
   3992         {
   3993           s_running_game_title = std::move(image_title);
   3994           s_running_game_custom_title = false;
   3995         }
   3996       }
   3997     }
   3998   }
   3999 
   4000   if (!booting)
   4001     TextureReplacements::SetGameID(s_running_game_serial);
   4002 
   4003   if (booting)
   4004     Achievements::ResetHardcoreMode(true);
   4005 
   4006   Achievements::GameChanged(s_running_game_path, image);
   4007 
   4008   UpdateGameSettingsLayer();
   4009   ApplySettings(true);
   4010 
   4011   s_cheat_list.reset();
   4012   if (g_settings.enable_cheats)
   4013     LoadCheatList();
   4014 
   4015   if (s_running_game_serial != prev_serial)
   4016     UpdateSessionTime(prev_serial);
   4017 
   4018   if (SaveStateSelectorUI::IsOpen())
   4019     SaveStateSelectorUI::RefreshList(s_running_game_serial);
   4020   else
   4021     SaveStateSelectorUI::ClearList();
   4022 
   4023   UpdateRichPresence(booting);
   4024 
   4025   Host::OnGameChanged(s_running_game_path, s_running_game_serial, s_running_game_title);
   4026 }
   4027 
   4028 bool System::CheckForSBIFile(CDImage* image, Error* error)
   4029 {
   4030   if (!s_running_game_entry || !s_running_game_entry->HasTrait(GameDatabase::Trait::IsLibCryptProtected) || !image ||
   4031       image->HasNonStandardSubchannel())
   4032   {
   4033     return true;
   4034   }
   4035 
   4036   WARNING_LOG("SBI file missing but required for {} ({})", s_running_game_serial, s_running_game_title);
   4037 
   4038   if (Host::GetBoolSettingValue("CDROM", "AllowBootingWithoutSBIFile", false))
   4039   {
   4040     if (Host::ConfirmMessage(
   4041           "Confirm Unsupported Configuration",
   4042           LargeString::from_format(
   4043             TRANSLATE_FS("System", "You are attempting to run a libcrypt protected game without an SBI file:\n\n{0}: "
   4044                                    "{1}\n\nThe game will likely not run properly.\n\nPlease check the README for "
   4045                                    "instructions on how to add an SBI file.\n\nDo you wish to continue?"),
   4046             s_running_game_serial, s_running_game_title)))
   4047     {
   4048       return true;
   4049     }
   4050   }
   4051 #ifndef __ANDROID__
   4052   Error::SetStringFmt(
   4053     error,
   4054     TRANSLATE_FS("System", "You are attempting to run a libcrypt protected game without an SBI file:\n\n{0}: "
   4055                            "{1}\n\nYour dump is incomplete, you must add the SBI file to run this game. \n\nThe "
   4056                            "name of the SBI file must match the name of the disc image."),
   4057     s_running_game_serial, s_running_game_title);
   4058 #else
   4059   // Shorter because no confirm messages.
   4060   Error::SetStringView(error, "Missing SBI file.", "The selected game requires a SBI file to run properly.");
   4061 #endif
   4062 
   4063   return false;
   4064 }
   4065 
   4066 bool System::HasMediaSubImages()
   4067 {
   4068   const CDImage* cdi = CDROM::GetMedia();
   4069   return cdi ? cdi->HasSubImages() : false;
   4070 }
   4071 
   4072 u32 System::GetMediaSubImageCount()
   4073 {
   4074   const CDImage* cdi = CDROM::GetMedia();
   4075   return cdi ? cdi->GetSubImageCount() : 0;
   4076 }
   4077 
   4078 u32 System::GetMediaSubImageIndex()
   4079 {
   4080   const CDImage* cdi = CDROM::GetMedia();
   4081   return cdi ? cdi->GetCurrentSubImage() : 0;
   4082 }
   4083 
   4084 u32 System::GetMediaSubImageIndexForTitle(std::string_view title)
   4085 {
   4086   const CDImage* cdi = CDROM::GetMedia();
   4087   if (!cdi)
   4088     return 0;
   4089 
   4090   const u32 count = cdi->GetSubImageCount();
   4091   for (u32 i = 0; i < count; i++)
   4092   {
   4093     if (title == cdi->GetSubImageMetadata(i, "title"))
   4094       return i;
   4095   }
   4096 
   4097   return std::numeric_limits<u32>::max();
   4098 }
   4099 
   4100 std::string System::GetMediaSubImageTitle(u32 index)
   4101 {
   4102   const CDImage* cdi = CDROM::GetMedia();
   4103   if (!cdi)
   4104     return {};
   4105 
   4106   return cdi->GetSubImageMetadata(index, "title");
   4107 }
   4108 
   4109 bool System::SwitchMediaSubImage(u32 index)
   4110 {
   4111   if (!CDROM::HasMedia())
   4112     return false;
   4113 
   4114   std::unique_ptr<CDImage> image = CDROM::RemoveMedia(true);
   4115   Assert(image);
   4116 
   4117   Error error;
   4118   if (!image->SwitchSubImage(index, &error))
   4119   {
   4120     Host::AddIconOSDMessage("media_switch_subimage", ICON_FA_COMPACT_DISC,
   4121                             fmt::format(TRANSLATE_FS("System", "Failed to switch to subimage {} in '{}': {}."),
   4122                                         index + 1u, Path::GetFileName(image->GetFileName()), error.GetDescription()),
   4123                             Host::OSD_INFO_DURATION);
   4124 
   4125     const DiscRegion region = GetRegionForImage(image.get());
   4126     CDROM::InsertMedia(std::move(image), region);
   4127     return false;
   4128   }
   4129 
   4130   Host::AddIconOSDMessage("media_switch_subimage", ICON_FA_COMPACT_DISC,
   4131                           fmt::format(TRANSLATE_FS("System", "Switched to sub-image {} ({}) in '{}'."),
   4132                                       image->GetSubImageMetadata(index, "title"), index + 1u,
   4133                                       image->GetMetadata("title")),
   4134                           Host::OSD_INFO_DURATION);
   4135   const DiscRegion region = GetRegionForImage(image.get());
   4136   CDROM::InsertMedia(std::move(image), region);
   4137 
   4138   ClearMemorySaveStates();
   4139   return true;
   4140 }
   4141 
   4142 bool System::HasCheatList()
   4143 {
   4144   return static_cast<bool>(s_cheat_list);
   4145 }
   4146 
   4147 CheatList* System::GetCheatList()
   4148 {
   4149   return s_cheat_list.get();
   4150 }
   4151 
   4152 void System::ApplyCheatCode(const CheatCode& code)
   4153 {
   4154   Assert(!IsShutdown());
   4155   code.Apply();
   4156 }
   4157 
   4158 void System::SetCheatList(std::unique_ptr<CheatList> cheats)
   4159 {
   4160   Assert(!IsShutdown());
   4161   s_cheat_list = std::move(cheats);
   4162 
   4163   if (s_cheat_list && s_cheat_list->GetEnabledCodeCount() > 0)
   4164   {
   4165     Host::AddIconOSDMessage("CheatsLoadWarning", ICON_FA_EXCLAMATION_TRIANGLE,
   4166                             TRANSLATE_PLURAL_STR("System", "%n cheat(s) are enabled. This may crash games.", "",
   4167                                                  s_cheat_list->GetEnabledCodeCount()),
   4168                             Host::OSD_WARNING_DURATION);
   4169   }
   4170   else
   4171   {
   4172     Host::RemoveKeyedOSDMessage("CheatsLoadWarning");
   4173   }
   4174 }
   4175 
   4176 void System::CheckForSettingsChanges(const Settings& old_settings)
   4177 {
   4178   if (IsValid() &&
   4179       (g_settings.gpu_renderer != old_settings.gpu_renderer ||
   4180        g_settings.gpu_use_debug_device != old_settings.gpu_use_debug_device ||
   4181        g_settings.gpu_threaded_presentation != old_settings.gpu_threaded_presentation ||
   4182        g_settings.gpu_disable_shader_cache != old_settings.gpu_disable_shader_cache ||
   4183        g_settings.gpu_disable_dual_source_blend != old_settings.gpu_disable_dual_source_blend ||
   4184        g_settings.gpu_disable_framebuffer_fetch != old_settings.gpu_disable_framebuffer_fetch ||
   4185        g_settings.gpu_disable_texture_buffers != old_settings.gpu_disable_texture_buffers ||
   4186        g_settings.gpu_disable_texture_copy_to_self != old_settings.gpu_disable_texture_copy_to_self ||
   4187        g_settings.gpu_disable_memory_import != old_settings.gpu_disable_memory_import ||
   4188        g_settings.gpu_disable_raster_order_views != old_settings.gpu_disable_raster_order_views ||
   4189        g_settings.display_exclusive_fullscreen_control != old_settings.display_exclusive_fullscreen_control))
   4190   {
   4191     // if debug device/threaded presentation change, we need to recreate the whole display
   4192     const bool recreate_device =
   4193       (g_settings.gpu_use_debug_device != old_settings.gpu_use_debug_device ||
   4194        g_settings.gpu_threaded_presentation != old_settings.gpu_threaded_presentation ||
   4195        g_settings.gpu_disable_shader_cache != old_settings.gpu_disable_shader_cache ||
   4196        g_settings.gpu_disable_dual_source_blend != old_settings.gpu_disable_dual_source_blend ||
   4197        g_settings.gpu_disable_framebuffer_fetch != old_settings.gpu_disable_framebuffer_fetch ||
   4198        g_settings.gpu_disable_texture_buffers != old_settings.gpu_disable_texture_buffers ||
   4199        g_settings.gpu_disable_texture_copy_to_self != old_settings.gpu_disable_texture_copy_to_self ||
   4200        g_settings.gpu_disable_memory_import != old_settings.gpu_disable_memory_import ||
   4201        g_settings.gpu_disable_raster_order_views != old_settings.gpu_disable_raster_order_views ||
   4202        g_settings.display_exclusive_fullscreen_control != old_settings.display_exclusive_fullscreen_control);
   4203 
   4204     Host::AddIconOSDMessage("RendererSwitch", ICON_FA_PAINT_ROLLER,
   4205                             fmt::format(TRANSLATE_FS("OSDMessage", "Switching to {}{} GPU renderer."),
   4206                                         Settings::GetRendererName(g_settings.gpu_renderer),
   4207                                         g_settings.gpu_use_debug_device ? " (debug)" : ""),
   4208                             Host::OSD_INFO_DURATION);
   4209     RecreateGPU(g_settings.gpu_renderer, recreate_device);
   4210   }
   4211 
   4212   if (IsValid())
   4213   {
   4214     ClearMemorySaveStates();
   4215 
   4216     if (g_settings.cpu_overclock_active != old_settings.cpu_overclock_active ||
   4217         (g_settings.cpu_overclock_active &&
   4218          (g_settings.cpu_overclock_numerator != old_settings.cpu_overclock_numerator ||
   4219           g_settings.cpu_overclock_denominator != old_settings.cpu_overclock_denominator)))
   4220     {
   4221       UpdateOverclock();
   4222     }
   4223 
   4224     if (g_settings.audio_backend != old_settings.audio_backend ||
   4225         g_settings.audio_driver != old_settings.audio_driver ||
   4226         g_settings.audio_output_device != old_settings.audio_output_device)
   4227     {
   4228       if (g_settings.audio_backend != old_settings.audio_backend)
   4229       {
   4230         Host::AddIconOSDMessage("AudioBackendSwitch", ICON_FA_HEADPHONES,
   4231                                 fmt::format(TRANSLATE_FS("OSDMessage", "Switching to {} audio backend."),
   4232                                             AudioStream::GetBackendDisplayName(g_settings.audio_backend)),
   4233                                 Host::OSD_INFO_DURATION);
   4234       }
   4235 
   4236       SPU::RecreateOutputStream();
   4237     }
   4238     if (g_settings.audio_stream_parameters.stretch_mode != old_settings.audio_stream_parameters.stretch_mode)
   4239       SPU::GetOutputStream()->SetStretchMode(g_settings.audio_stream_parameters.stretch_mode);
   4240     if (g_settings.audio_stream_parameters != old_settings.audio_stream_parameters)
   4241     {
   4242       SPU::RecreateOutputStream();
   4243       UpdateSpeedLimiterState();
   4244     }
   4245 
   4246     if (g_settings.emulation_speed != old_settings.emulation_speed)
   4247       UpdateThrottlePeriod();
   4248 
   4249     if (g_settings.cpu_execution_mode != old_settings.cpu_execution_mode ||
   4250         g_settings.cpu_fastmem_mode != old_settings.cpu_fastmem_mode)
   4251     {
   4252       Host::AddIconOSDMessage("CPUExecutionModeSwitch", ICON_FA_MICROCHIP,
   4253                               fmt::format(TRANSLATE_FS("OSDMessage", "Switching to {} CPU execution mode."),
   4254                                           TRANSLATE_SV("CPUExecutionMode", Settings::GetCPUExecutionModeDisplayName(
   4255                                                                              g_settings.cpu_execution_mode))),
   4256                               Host::OSD_INFO_DURATION);
   4257       CPU::ExecutionModeChanged();
   4258       if (old_settings.cpu_execution_mode != CPUExecutionMode::Interpreter)
   4259         CPU::CodeCache::Shutdown();
   4260       if (g_settings.cpu_execution_mode != CPUExecutionMode::Interpreter)
   4261         CPU::CodeCache::Initialize();
   4262     }
   4263 
   4264     if (CPU::CodeCache::IsUsingAnyRecompiler() &&
   4265         (g_settings.cpu_recompiler_memory_exceptions != old_settings.cpu_recompiler_memory_exceptions ||
   4266          g_settings.cpu_recompiler_block_linking != old_settings.cpu_recompiler_block_linking ||
   4267          g_settings.cpu_recompiler_icache != old_settings.cpu_recompiler_icache ||
   4268          g_settings.bios_tty_logging != old_settings.bios_tty_logging))
   4269     {
   4270       Host::AddIconOSDMessage("CPUFlushAllBlocks", ICON_FA_MICROCHIP,
   4271                               TRANSLATE_STR("OSDMessage", "Recompiler options changed, flushing all blocks."),
   4272                               Host::OSD_INFO_DURATION);
   4273       CPU::ExecutionModeChanged();
   4274     }
   4275     else if (g_settings.cpu_execution_mode == CPUExecutionMode::Interpreter &&
   4276              g_settings.bios_tty_logging != old_settings.bios_tty_logging)
   4277     {
   4278       CPU::UpdateDebugDispatcherFlag();
   4279     }
   4280 
   4281     if (g_settings.enable_cheats != old_settings.enable_cheats)
   4282     {
   4283       if (g_settings.enable_cheats)
   4284         LoadCheatList();
   4285       else
   4286         SetCheatList(nullptr);
   4287     }
   4288 
   4289     SPU::GetOutputStream()->SetOutputVolume(GetAudioOutputVolume());
   4290 
   4291     if (g_settings.gpu_resolution_scale != old_settings.gpu_resolution_scale ||
   4292         g_settings.gpu_multisamples != old_settings.gpu_multisamples ||
   4293         g_settings.gpu_per_sample_shading != old_settings.gpu_per_sample_shading ||
   4294         g_settings.gpu_use_thread != old_settings.gpu_use_thread ||
   4295         g_settings.gpu_use_software_renderer_for_readbacks != old_settings.gpu_use_software_renderer_for_readbacks ||
   4296         g_settings.gpu_fifo_size != old_settings.gpu_fifo_size ||
   4297         g_settings.gpu_max_run_ahead != old_settings.gpu_max_run_ahead ||
   4298         g_settings.gpu_true_color != old_settings.gpu_true_color ||
   4299         g_settings.gpu_debanding != old_settings.gpu_debanding ||
   4300         g_settings.gpu_scaled_dithering != old_settings.gpu_scaled_dithering ||
   4301         g_settings.gpu_force_round_texcoords != old_settings.gpu_force_round_texcoords ||
   4302         g_settings.gpu_accurate_blending != old_settings.gpu_accurate_blending ||
   4303         g_settings.gpu_texture_filter != old_settings.gpu_texture_filter ||
   4304         g_settings.gpu_sprite_texture_filter != old_settings.gpu_sprite_texture_filter ||
   4305         g_settings.gpu_line_detect_mode != old_settings.gpu_line_detect_mode ||
   4306         g_settings.gpu_disable_interlacing != old_settings.gpu_disable_interlacing ||
   4307         g_settings.gpu_force_ntsc_timings != old_settings.gpu_force_ntsc_timings ||
   4308         g_settings.gpu_downsample_mode != old_settings.gpu_downsample_mode ||
   4309         g_settings.gpu_downsample_scale != old_settings.gpu_downsample_scale ||
   4310         g_settings.gpu_wireframe_mode != old_settings.gpu_wireframe_mode ||
   4311         g_settings.display_deinterlacing_mode != old_settings.display_deinterlacing_mode ||
   4312         g_settings.display_24bit_chroma_smoothing != old_settings.display_24bit_chroma_smoothing ||
   4313         g_settings.display_crop_mode != old_settings.display_crop_mode ||
   4314         g_settings.display_aspect_ratio != old_settings.display_aspect_ratio ||
   4315         g_settings.display_scaling != old_settings.display_scaling ||
   4316         g_settings.display_show_gpu_usage != old_settings.display_show_gpu_usage ||
   4317         g_settings.gpu_pgxp_enable != old_settings.gpu_pgxp_enable ||
   4318         g_settings.gpu_pgxp_texture_correction != old_settings.gpu_pgxp_texture_correction ||
   4319         g_settings.gpu_pgxp_color_correction != old_settings.gpu_pgxp_color_correction ||
   4320         g_settings.gpu_pgxp_depth_buffer != old_settings.gpu_pgxp_depth_buffer ||
   4321         g_settings.display_active_start_offset != old_settings.display_active_start_offset ||
   4322         g_settings.display_active_end_offset != old_settings.display_active_end_offset ||
   4323         g_settings.display_line_start_offset != old_settings.display_line_start_offset ||
   4324         g_settings.display_line_end_offset != old_settings.display_line_end_offset ||
   4325         g_settings.rewind_enable != old_settings.rewind_enable ||
   4326         g_settings.runahead_frames != old_settings.runahead_frames)
   4327     {
   4328       g_gpu->UpdateSettings(old_settings);
   4329       if (IsPaused())
   4330         InvalidateDisplay();
   4331     }
   4332 
   4333     if (g_settings.gpu_widescreen_hack != old_settings.gpu_widescreen_hack ||
   4334         g_settings.display_aspect_ratio != old_settings.display_aspect_ratio ||
   4335         (g_settings.display_aspect_ratio == DisplayAspectRatio::Custom &&
   4336          (g_settings.display_aspect_ratio_custom_numerator != old_settings.display_aspect_ratio_custom_numerator ||
   4337           g_settings.display_aspect_ratio_custom_denominator != old_settings.display_aspect_ratio_custom_denominator)))
   4338     {
   4339       GTE::UpdateAspectRatio();
   4340     }
   4341 
   4342     if (g_settings.gpu_pgxp_enable != old_settings.gpu_pgxp_enable ||
   4343         (g_settings.gpu_pgxp_enable && (g_settings.gpu_pgxp_culling != old_settings.gpu_pgxp_culling ||
   4344                                         g_settings.gpu_pgxp_vertex_cache != old_settings.gpu_pgxp_vertex_cache ||
   4345                                         g_settings.gpu_pgxp_cpu != old_settings.gpu_pgxp_cpu)))
   4346     {
   4347       if (old_settings.gpu_pgxp_enable)
   4348         CPU::PGXP::Shutdown();
   4349 
   4350       if (g_settings.gpu_pgxp_enable)
   4351         CPU::PGXP::Initialize();
   4352 
   4353       CPU::CodeCache::Reset();
   4354     }
   4355 
   4356     if (g_settings.display_show_gpu_stats != old_settings.display_show_gpu_stats)
   4357       g_gpu->ResetStatistics();
   4358 
   4359     if (g_settings.cdrom_readahead_sectors != old_settings.cdrom_readahead_sectors)
   4360       CDROM::SetReadaheadSectors(g_settings.cdrom_readahead_sectors);
   4361 
   4362     if (g_settings.memory_card_types != old_settings.memory_card_types ||
   4363         g_settings.memory_card_paths != old_settings.memory_card_paths ||
   4364         (g_settings.memory_card_use_playlist_title != old_settings.memory_card_use_playlist_title))
   4365     {
   4366       UpdateMemoryCardTypes();
   4367     }
   4368 
   4369     if (g_settings.rewind_enable != old_settings.rewind_enable ||
   4370         g_settings.rewind_save_frequency != old_settings.rewind_save_frequency ||
   4371         g_settings.rewind_save_slots != old_settings.rewind_save_slots ||
   4372         g_settings.runahead_frames != old_settings.runahead_frames)
   4373     {
   4374       UpdateMemorySaveStateSettings();
   4375     }
   4376 
   4377     if (g_settings.texture_replacements.enable_vram_write_replacements !=
   4378           old_settings.texture_replacements.enable_vram_write_replacements ||
   4379         g_settings.texture_replacements.preload_textures != old_settings.texture_replacements.preload_textures)
   4380     {
   4381       TextureReplacements::Reload();
   4382     }
   4383 
   4384     if (g_settings.audio_backend != old_settings.audio_backend ||
   4385         g_settings.increase_timer_resolution != old_settings.increase_timer_resolution ||
   4386         g_settings.emulation_speed != old_settings.emulation_speed ||
   4387         g_settings.fast_forward_speed != old_settings.fast_forward_speed ||
   4388         g_settings.display_optimal_frame_pacing != old_settings.display_optimal_frame_pacing ||
   4389         g_settings.display_skip_presenting_duplicate_frames != old_settings.display_skip_presenting_duplicate_frames ||
   4390         g_settings.display_pre_frame_sleep != old_settings.display_pre_frame_sleep ||
   4391         g_settings.display_pre_frame_sleep_buffer != old_settings.display_pre_frame_sleep_buffer ||
   4392         g_settings.display_vsync != old_settings.display_vsync ||
   4393         g_settings.display_disable_mailbox_presentation != old_settings.display_disable_mailbox_presentation ||
   4394         g_settings.sync_to_host_refresh_rate != old_settings.sync_to_host_refresh_rate)
   4395     {
   4396       UpdateSpeedLimiterState();
   4397     }
   4398 
   4399     if (g_settings.inhibit_screensaver != old_settings.inhibit_screensaver)
   4400     {
   4401       if (g_settings.inhibit_screensaver)
   4402         PlatformMisc::SuspendScreensaver();
   4403       else
   4404         PlatformMisc::ResumeScreensaver();
   4405     }
   4406 
   4407     PostProcessing::UpdateSettings();
   4408 
   4409 #ifdef ENABLE_GDB_SERVER
   4410     if (g_settings.debugging.enable_gdb_server != old_settings.debugging.enable_gdb_server ||
   4411         g_settings.debugging.gdb_server_port != old_settings.debugging.gdb_server_port)
   4412     {
   4413       GDBServer::Shutdown();
   4414       if (g_settings.debugging.enable_gdb_server)
   4415         GDBServer::Initialize(g_settings.debugging.gdb_server_port);
   4416     }
   4417 #endif
   4418   }
   4419   else
   4420   {
   4421     if (g_gpu_device)
   4422     {
   4423       if (g_settings.display_vsync != old_settings.display_vsync ||
   4424           g_settings.display_disable_mailbox_presentation != old_settings.display_disable_mailbox_presentation)
   4425       {
   4426         UpdateDisplayVSync();
   4427       }
   4428     }
   4429   }
   4430 
   4431   if (g_gpu_device)
   4432   {
   4433     if (g_settings.display_osd_scale != old_settings.display_osd_scale)
   4434       ImGuiManager::SetGlobalScale(g_settings.display_osd_scale / 100.0f);
   4435     if (g_settings.display_show_osd_messages != old_settings.display_show_osd_messages)
   4436       ImGuiManager::SetShowOSDMessages(g_settings.display_show_osd_messages);
   4437   }
   4438 
   4439   bool controllers_updated = false;
   4440   for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
   4441   {
   4442     if (g_settings.controller_types[i] != old_settings.controller_types[i])
   4443     {
   4444       if (IsValid() && !controllers_updated)
   4445       {
   4446         UpdateControllers();
   4447         ResetControllers();
   4448         controllers_updated = true;
   4449       }
   4450     }
   4451   }
   4452 
   4453   if (IsValid() && !controllers_updated)
   4454     UpdateControllerSettings();
   4455 
   4456   if (g_settings.multitap_mode != old_settings.multitap_mode)
   4457     UpdateMultitaps();
   4458 
   4459   Achievements::UpdateSettings(old_settings);
   4460 
   4461   FullscreenUI::CheckForConfigChanges(old_settings);
   4462 
   4463 #ifdef ENABLE_DISCORD_PRESENCE
   4464   if (g_settings.enable_discord_presence != old_settings.enable_discord_presence)
   4465   {
   4466     if (g_settings.enable_discord_presence)
   4467       InitializeDiscordPresence();
   4468     else
   4469       ShutdownDiscordPresence();
   4470   }
   4471 #endif
   4472 
   4473 #ifdef ENABLE_PINE_SERVER
   4474   if (g_settings.pine_enable != old_settings.pine_enable || g_settings.pine_slot != old_settings.pine_slot)
   4475   {
   4476     PINEServer::Shutdown();
   4477     if (g_settings.pine_enable)
   4478       PINEServer::Initialize(g_settings.pine_slot);
   4479     else
   4480       ReleaseSocketMultiplexer();
   4481   }
   4482 #endif
   4483 
   4484   if (g_settings.export_shared_memory != old_settings.export_shared_memory) [[unlikely]]
   4485   {
   4486     Error error;
   4487     if (!Bus::ReallocateMemoryMap(g_settings.export_shared_memory, &error)) [[unlikely]]
   4488     {
   4489       ERROR_LOG(error.GetDescription());
   4490       Panic("Failed to reallocate memory map. The log may contain more information.");
   4491     }
   4492   }
   4493 
   4494   if (g_settings.log_level != old_settings.log_level || g_settings.log_filter != old_settings.log_filter ||
   4495       g_settings.log_timestamps != old_settings.log_timestamps ||
   4496       g_settings.log_to_console != old_settings.log_to_console ||
   4497       g_settings.log_to_debug != old_settings.log_to_debug || g_settings.log_to_window != old_settings.log_to_window ||
   4498       g_settings.log_to_file != old_settings.log_to_file)
   4499   {
   4500     g_settings.UpdateLogSettings();
   4501   }
   4502 }
   4503 
   4504 void System::WarnAboutUnsafeSettings()
   4505 {
   4506   LargeString messages;
   4507   auto append = [&messages](const char* icon, std::string_view msg) { messages.append_format("{} {}\n", icon, msg); };
   4508 
   4509   if (!g_settings.disable_all_enhancements)
   4510   {
   4511     if (g_settings.cpu_overclock_active)
   4512     {
   4513       append(ICON_EMOJI_WARNING,
   4514              SmallString::from_format(
   4515                TRANSLATE_FS("System", "CPU clock speed is set to {}% ({} / {}). This may crash games."),
   4516                g_settings.GetCPUOverclockPercent(), g_settings.cpu_overclock_numerator,
   4517                g_settings.cpu_overclock_denominator));
   4518     }
   4519     if (g_settings.cdrom_read_speedup > 1)
   4520     {
   4521       append(ICON_EMOJI_WARNING,
   4522              SmallString::from_format(
   4523                TRANSLATE_FS("System", "CD-ROM read speedup set to {}x (effective speed {}x). This may crash games."),
   4524                g_settings.cdrom_read_speedup, g_settings.cdrom_read_speedup * 2));
   4525     }
   4526     if (g_settings.cdrom_seek_speedup != 1)
   4527     {
   4528       append(ICON_EMOJI_WARNING,
   4529              SmallString::from_format(TRANSLATE_FS("System", "CD-ROM seek speedup set to {}. This may crash games."),
   4530                                       (g_settings.cdrom_seek_speedup == 0) ?
   4531                                         TinyString(TRANSLATE_SV("System", "Instant")) :
   4532                                         TinyString::from_format("{}x", g_settings.cdrom_seek_speedup)));
   4533     }
   4534     if (g_settings.gpu_force_ntsc_timings)
   4535     {
   4536       append(ICON_FA_TV, TRANSLATE_SV("System", "Force NTSC timings is enabled. Games may run at incorrect speeds."));
   4537     }
   4538     if (!g_settings.IsUsingSoftwareRenderer())
   4539     {
   4540       if (g_settings.gpu_multisamples != 1)
   4541       {
   4542         append(ICON_EMOJI_WARNING,
   4543                TRANSLATE_SV("System", "Multisample anti-aliasing is enabled, some games may not render correctly."));
   4544       }
   4545       if (g_settings.gpu_resolution_scale > 1 && g_settings.gpu_force_round_texcoords)
   4546       {
   4547         append(
   4548           ICON_EMOJI_WARNING,
   4549           TRANSLATE_SV("System", "Round upscaled texture coordinates is enabled. This may cause rendering errors."));
   4550       }
   4551     }
   4552     if (g_settings.enable_8mb_ram)
   4553     {
   4554       append(ICON_EMOJI_WARNING,
   4555         TRANSLATE_SV("System", "8MB RAM is enabled, this may be incompatible with some games."));
   4556     }
   4557   }
   4558   else
   4559   {
   4560     append(ICON_FA_COGS, TRANSLATE_SV("System", "All enhancements are currently disabled."));
   4561   }
   4562 
   4563   if (!g_settings.apply_compatibility_settings)
   4564   {
   4565     append(ICON_EMOJI_WARNING,
   4566            TRANSLATE_STR("System", "Compatibility settings are not enabled. Some games may not function correctly."));
   4567   }
   4568 
   4569   if (!messages.empty())
   4570   {
   4571     if (messages.back() == '\n')
   4572       messages.pop_back();
   4573 
   4574     LogUnsafeSettingsToConsole(messages);
   4575     Host::AddKeyedOSDMessage("performance_settings_warning", std::string(messages.view()), Host::OSD_WARNING_DURATION);
   4576   }
   4577   else
   4578   {
   4579     Host::RemoveKeyedOSDMessage("performance_settings_warning");
   4580   }
   4581 }
   4582 
   4583 void System::LogUnsafeSettingsToConsole(const SmallStringBase& messages)
   4584 {
   4585   // a not-great way of getting rid of the icons for the console message
   4586   LargeString console_messages = messages;
   4587   for (;;)
   4588   {
   4589     const s32 pos = console_messages.find("\xef");
   4590     if (pos >= 0)
   4591     {
   4592       console_messages.erase(pos, 3);
   4593       console_messages.insert(pos, "[Unsafe Settings]");
   4594     }
   4595     else
   4596     {
   4597       break;
   4598     }
   4599   }
   4600   WARNING_LOG(console_messages);
   4601 }
   4602 
   4603 void System::CalculateRewindMemoryUsage(u32 num_saves, u32 resolution_scale, u64* ram_usage, u64* vram_usage)
   4604 {
   4605   const u64 real_resolution_scale = std::max<u64>(g_settings.gpu_resolution_scale, 1u);
   4606   *ram_usage = GetMaxSaveStateSize() * static_cast<u64>(num_saves);
   4607   *vram_usage = ((VRAM_WIDTH * real_resolution_scale) * (VRAM_HEIGHT * real_resolution_scale) * 4) *
   4608                 static_cast<u64>(g_settings.gpu_multisamples) * static_cast<u64>(num_saves);
   4609 }
   4610 
   4611 void System::ClearMemorySaveStates()
   4612 {
   4613   s_rewind_states.clear();
   4614   s_runahead_states.clear();
   4615 }
   4616 
   4617 void System::UpdateMemorySaveStateSettings()
   4618 {
   4619   ClearMemorySaveStates();
   4620 
   4621   s_memory_saves_enabled = g_settings.rewind_enable;
   4622 
   4623   if (g_settings.rewind_enable)
   4624   {
   4625     s_rewind_save_frequency = static_cast<s32>(std::ceil(g_settings.rewind_save_frequency * s_throttle_frequency));
   4626     s_rewind_save_counter = 0;
   4627 
   4628     u64 ram_usage, vram_usage;
   4629     CalculateRewindMemoryUsage(g_settings.rewind_save_slots, g_settings.gpu_resolution_scale, &ram_usage, &vram_usage);
   4630     INFO_LOG("Rewind is enabled, saving every {} frames, with {} slots and {}MB RAM and {}MB VRAM usage",
   4631              std::max(s_rewind_save_frequency, 1), g_settings.rewind_save_slots, ram_usage / 1048576,
   4632              vram_usage / 1048576);
   4633   }
   4634   else
   4635   {
   4636     s_rewind_save_frequency = -1;
   4637     s_rewind_save_counter = -1;
   4638   }
   4639 
   4640   s_rewind_load_frequency = -1;
   4641   s_rewind_load_counter = -1;
   4642 
   4643   s_runahead_frames = g_settings.runahead_frames;
   4644   s_runahead_replay_pending = false;
   4645   if (s_runahead_frames > 0)
   4646     INFO_LOG("Runahead is active with {} frames", s_runahead_frames);
   4647 }
   4648 
   4649 bool System::LoadMemoryState(const MemorySaveState& mss)
   4650 {
   4651   StateWrapper sw(mss.state_data.cspan(), StateWrapper::Mode::Read, SAVE_STATE_VERSION);
   4652   GPUTexture* host_texture = mss.vram_texture.get();
   4653   if (!DoState(sw, &host_texture, true, true)) [[unlikely]]
   4654   {
   4655     Host::ReportErrorAsync("Error", "Failed to load memory save state, resetting.");
   4656     ResetSystem();
   4657     return false;
   4658   }
   4659 
   4660   return true;
   4661 }
   4662 
   4663 bool System::SaveMemoryState(MemorySaveState* mss)
   4664 {
   4665   if (mss->state_data.empty())
   4666     mss->state_data.resize(GetMaxSaveStateSize());
   4667 
   4668   GPUTexture* host_texture = mss->vram_texture.release();
   4669   StateWrapper sw(mss->state_data.span(), StateWrapper::Mode::Write, SAVE_STATE_VERSION);
   4670   if (!DoState(sw, &host_texture, false, true))
   4671   {
   4672     ERROR_LOG("Failed to create rewind state.");
   4673     delete host_texture;
   4674     return false;
   4675   }
   4676 
   4677 #ifdef PROFILE_MEMORY_SAVE_STATES
   4678   mss->state_size = sw.GetPosition();
   4679 #endif
   4680 
   4681   mss->vram_texture.reset(host_texture);
   4682   return true;
   4683 }
   4684 
   4685 bool System::SaveRewindState()
   4686 {
   4687 #ifdef PROFILE_MEMORY_SAVE_STATES
   4688   Common::Timer save_timer;
   4689 #endif
   4690 
   4691   // try to reuse the frontmost slot
   4692   const u32 save_slots = g_settings.rewind_save_slots;
   4693   MemorySaveState mss;
   4694   while (s_rewind_states.size() >= save_slots)
   4695   {
   4696     mss = std::move(s_rewind_states.front());
   4697     s_rewind_states.pop_front();
   4698   }
   4699 
   4700   if (!SaveMemoryState(&mss))
   4701     return false;
   4702 
   4703   s_rewind_states.push_back(std::move(mss));
   4704 
   4705 #ifdef PROFILE_MEMORY_SAVE_STATES
   4706   DEV_LOG("Saved rewind state ({} bytes, took {:.4f} ms)", s_rewind_states.back().state_size,
   4707           save_timer.GetTimeMilliseconds());
   4708 #endif
   4709 
   4710   return true;
   4711 }
   4712 
   4713 bool System::LoadRewindState(u32 skip_saves /*= 0*/, bool consume_state /*=true */)
   4714 {
   4715   while (skip_saves > 0 && !s_rewind_states.empty())
   4716   {
   4717     g_gpu_device->RecycleTexture(std::move(s_rewind_states.back().vram_texture));
   4718     s_rewind_states.pop_back();
   4719     skip_saves--;
   4720   }
   4721 
   4722   if (s_rewind_states.empty())
   4723     return false;
   4724 
   4725 #ifdef PROFILE_MEMORY_SAVE_STATES
   4726   Common::Timer load_timer;
   4727 #endif
   4728 
   4729   if (!LoadMemoryState(s_rewind_states.back()))
   4730     return false;
   4731 
   4732   if (consume_state)
   4733     s_rewind_states.pop_back();
   4734 
   4735 #ifdef PROFILE_MEMORY_SAVE_STATES
   4736   DEV_LOG("Rewind load took {:.4f} ms", load_timer.GetTimeMilliseconds());
   4737 #endif
   4738 
   4739   return true;
   4740 }
   4741 
   4742 bool System::IsRewinding()
   4743 {
   4744   return (s_rewind_load_frequency >= 0);
   4745 }
   4746 
   4747 void System::SetRewinding(bool enabled)
   4748 {
   4749   if (enabled)
   4750   {
   4751     const bool was_enabled = IsRewinding();
   4752 
   4753     // Try to rewind at the replay speed, or one per second maximum.
   4754     const float load_frequency = std::min(g_settings.rewind_save_frequency, 1.0f);
   4755     s_rewind_load_frequency = static_cast<s32>(std::ceil(load_frequency * s_throttle_frequency));
   4756     s_rewind_load_counter = 0;
   4757 
   4758     if (!was_enabled && s_system_executing)
   4759       s_system_interrupted = true;
   4760   }
   4761   else
   4762   {
   4763     s_rewind_load_frequency = -1;
   4764     s_rewind_load_counter = -1;
   4765     s_rewinding_first_save = true;
   4766   }
   4767 }
   4768 
   4769 void System::DoRewind()
   4770 {
   4771   if (s_rewind_load_counter == 0)
   4772   {
   4773     const u32 skip_saves = BoolToUInt32(!s_rewinding_first_save);
   4774     s_rewinding_first_save = false;
   4775     LoadRewindState(skip_saves, false);
   4776     ResetPerformanceCounters();
   4777     s_rewind_load_counter = s_rewind_load_frequency;
   4778   }
   4779   else
   4780   {
   4781     s_rewind_load_counter--;
   4782   }
   4783 
   4784   InvalidateDisplay();
   4785   Host::PumpMessagesOnCPUThread();
   4786   Internal::IdlePollUpdate();
   4787 
   4788   Throttle(Common::Timer::GetCurrentValue());
   4789 }
   4790 
   4791 void System::SaveRunaheadState()
   4792 {
   4793   // try to reuse the frontmost slot
   4794   MemorySaveState mss;
   4795   while (s_runahead_states.size() >= s_runahead_frames)
   4796   {
   4797     mss = std::move(s_runahead_states.front());
   4798     s_runahead_states.pop_front();
   4799   }
   4800 
   4801   if (!SaveMemoryState(&mss))
   4802   {
   4803     ERROR_LOG("Failed to save runahead state.");
   4804     return;
   4805   }
   4806 
   4807   s_runahead_states.push_back(std::move(mss));
   4808 }
   4809 
   4810 bool System::DoRunahead()
   4811 {
   4812 #ifdef PROFILE_MEMORY_SAVE_STATES
   4813   static Common::Timer replay_timer;
   4814 #endif
   4815 
   4816   if (s_runahead_replay_pending)
   4817   {
   4818 #ifdef PROFILE_MEMORY_SAVE_STATES
   4819     DEV_LOG("runahead starting at frame {}", s_frame_number);
   4820     replay_timer.Reset();
   4821 #endif
   4822 
   4823     // we need to replay and catch up - load the state,
   4824     s_runahead_replay_pending = false;
   4825     if (s_runahead_states.empty() || !LoadMemoryState(s_runahead_states.front()))
   4826     {
   4827       s_runahead_states.clear();
   4828       return false;
   4829     }
   4830 
   4831     // figure out how many frames we need to run to catch up
   4832     s_runahead_replay_frames = static_cast<u32>(s_runahead_states.size());
   4833 
   4834     // and throw away all the states, forcing us to catch up below
   4835     s_runahead_states.clear();
   4836 
   4837     // run the frames with no audio
   4838     SPU::SetAudioOutputMuted(true);
   4839 
   4840 #ifdef PROFILE_MEMORY_SAVE_STATES
   4841     VERBOSE_LOG("Rewound to frame {}, took {:.2f} ms", s_frame_number, replay_timer.GetTimeMilliseconds());
   4842 #endif
   4843 
   4844     // we don't want to save the frame we just loaded. but we are "one frame ahead", because the frame we just tossed
   4845     // was never saved, so return but don't decrement the counter
   4846     InterruptExecution();
   4847     CheckForAndExitExecution();
   4848     return true;
   4849   }
   4850   else if (s_runahead_replay_frames == 0)
   4851   {
   4852     return false;
   4853   }
   4854 
   4855   s_runahead_replay_frames--;
   4856   if (s_runahead_replay_frames > 0)
   4857   {
   4858     // keep running ahead
   4859     SaveRunaheadState();
   4860     return true;
   4861   }
   4862 
   4863 #ifdef PROFILE_MEMORY_SAVE_STATES
   4864   VERBOSE_LOG("Running {} frames to catch up took {:.2f} ms", s_runahead_frames, replay_timer.GetTimeMilliseconds());
   4865 #endif
   4866 
   4867   // we're all caught up. this frame gets saved in DoMemoryStates().
   4868   SPU::SetAudioOutputMuted(false);
   4869 
   4870 #ifdef PROFILE_MEMORY_SAVE_STATES
   4871   DEV_LOG("runahead ending at frame {}, took {:.2f} ms", s_frame_number, replay_timer.GetTimeMilliseconds());
   4872 #endif
   4873 
   4874   return false;
   4875 }
   4876 
   4877 void System::SetRunaheadReplayFlag()
   4878 {
   4879   if (s_runahead_frames == 0 || s_runahead_states.empty())
   4880     return;
   4881 
   4882 #ifdef PROFILE_MEMORY_SAVE_STATES
   4883   DEV_LOG("Runahead rewind pending...");
   4884 #endif
   4885 
   4886   s_runahead_replay_pending = true;
   4887 }
   4888 
   4889 void System::ShutdownSystem(bool save_resume_state)
   4890 {
   4891   if (!IsValid())
   4892     return;
   4893 
   4894   if (save_resume_state)
   4895   {
   4896     Error error;
   4897     if (!SaveResumeState(&error))
   4898     {
   4899       Host::ReportErrorAsync(
   4900         TRANSLATE_SV("System", "Error"),
   4901         fmt::format(TRANSLATE_FS("System", "Failed to save resume state: {}"), error.GetDescription()));
   4902     }
   4903   }
   4904 
   4905   s_state = State::Stopping;
   4906   if (!s_system_executing)
   4907     DestroySystem();
   4908 }
   4909 
   4910 bool System::CanUndoLoadState()
   4911 {
   4912   return s_undo_load_state.has_value();
   4913 }
   4914 
   4915 std::optional<ExtendedSaveStateInfo> System::GetUndoSaveStateInfo()
   4916 {
   4917   std::optional<ExtendedSaveStateInfo> ssi;
   4918   if (s_undo_load_state.has_value())
   4919   {
   4920     ssi.emplace();
   4921     ssi->title = s_undo_load_state->title;
   4922     ssi->serial = s_undo_load_state->serial;
   4923     ssi->media_path = s_undo_load_state->media_path;
   4924     ssi->screenshot = s_undo_load_state->screenshot;
   4925     ssi->timestamp = 0;
   4926   }
   4927 
   4928   return ssi;
   4929 }
   4930 
   4931 bool System::UndoLoadState()
   4932 {
   4933   if (!s_undo_load_state.has_value())
   4934     return false;
   4935 
   4936   Assert(IsValid());
   4937 
   4938   Error error;
   4939   if (!LoadStateFromBuffer(s_undo_load_state.value(), &error, true))
   4940   {
   4941     Host::ReportErrorAsync("Error",
   4942                            fmt::format("Failed to load undo state, resetting system:\n", error.GetDescription()));
   4943     s_undo_load_state.reset();
   4944     ResetSystem();
   4945     return false;
   4946   }
   4947 
   4948   INFO_LOG("Loaded undo save state.");
   4949   s_undo_load_state.reset();
   4950   return true;
   4951 }
   4952 
   4953 bool System::SaveUndoLoadState()
   4954 {
   4955   if (!s_undo_load_state.has_value())
   4956     s_undo_load_state.emplace();
   4957 
   4958   Error error;
   4959   if (!SaveStateToBuffer(&s_undo_load_state.value(), &error))
   4960   {
   4961     Host::AddOSDMessage(
   4962       fmt::format(TRANSLATE_FS("OSDMessage", "Failed to save undo load state:\n{}"), error.GetDescription()),
   4963       Host::OSD_CRITICAL_ERROR_DURATION);
   4964     s_undo_load_state.reset();
   4965     return false;
   4966   }
   4967 
   4968   INFO_LOG("Saved undo load state: {} bytes", s_undo_load_state->state_size);
   4969   return true;
   4970 }
   4971 
   4972 bool System::IsRunningAtNonStandardSpeed()
   4973 {
   4974   if (!IsValid())
   4975     return false;
   4976 
   4977   return (s_target_speed != 1.0f && !s_syncing_to_host);
   4978 }
   4979 
   4980 s32 System::GetAudioOutputVolume()
   4981 {
   4982   return g_settings.GetAudioOutputVolume(IsRunningAtNonStandardSpeed());
   4983 }
   4984 
   4985 void System::UpdateVolume()
   4986 {
   4987   if (!IsValid())
   4988     return;
   4989 
   4990   SPU::GetOutputStream()->SetOutputVolume(GetAudioOutputVolume());
   4991 }
   4992 
   4993 bool System::SaveScreenshot(const char* filename, DisplayScreenshotMode mode, DisplayScreenshotFormat format,
   4994                             u8 quality, bool compress_on_thread)
   4995 {
   4996   if (!System::IsValid())
   4997     return false;
   4998 
   4999   std::string auto_filename;
   5000   if (!filename)
   5001   {
   5002     const std::string sanitized_name = Path::SanitizeFileName(System::GetGameTitle());
   5003     const char* extension = Settings::GetDisplayScreenshotFormatExtension(format);
   5004     std::string basename;
   5005     if (sanitized_name.empty())
   5006       basename = fmt::format("{}", GetTimestampStringForFileName());
   5007     else
   5008       basename = fmt::format("{} {}", sanitized_name, GetTimestampStringForFileName());
   5009 
   5010     auto_filename = fmt::format("{}" FS_OSPATH_SEPARATOR_STR "{}.{}", EmuFolders::Screenshots, basename, extension);
   5011 
   5012     // handle quick screenshots to the same filename
   5013     u32 next_suffix = 1;
   5014     while (FileSystem::FileExists(auto_filename.c_str()))
   5015     {
   5016       auto_filename = fmt::format("{}" FS_OSPATH_SEPARATOR_STR "{} ({}).{}", EmuFolders::Screenshots, basename,
   5017                                   next_suffix, extension);
   5018       next_suffix++;
   5019     }
   5020 
   5021     filename = auto_filename.c_str();
   5022   }
   5023 
   5024   return g_gpu->RenderScreenshotToFile(filename, mode, quality, compress_on_thread, true);
   5025 }
   5026 
   5027 static std::string_view GetCaptureTypeForMessage(bool capture_video, bool capture_audio)
   5028 {
   5029   return capture_video ? (capture_audio ? TRANSLATE_SV("System", "capturing audio and video") :
   5030                                           TRANSLATE_SV("System", "capturing video")) :
   5031                          TRANSLATE_SV("System", "capturing audio");
   5032 }
   5033 
   5034 MediaCapture* System::GetMediaCapture()
   5035 {
   5036   return s_media_capture.get();
   5037 }
   5038 
   5039 std::string System::GetNewMediaCapturePath(const std::string_view title, const std::string_view container)
   5040 {
   5041   const std::string sanitized_name = Path::SanitizeFileName(title);
   5042   std::string path;
   5043   if (sanitized_name.empty())
   5044   {
   5045     path = Path::Combine(EmuFolders::Videos, fmt::format("{}.{}", GetTimestampStringForFileName(), container));
   5046   }
   5047   else
   5048   {
   5049     path = Path::Combine(EmuFolders::Videos,
   5050                          fmt::format("{} {}.{}", sanitized_name, GetTimestampStringForFileName(), container));
   5051   }
   5052 
   5053   return path;
   5054 }
   5055 
   5056 bool System::StartMediaCapture(std::string path)
   5057 {
   5058   const bool capture_video = Host::GetBoolSettingValue("MediaCapture", "VideoCapture", true);
   5059   const bool capture_audio = Host::GetBoolSettingValue("MediaCapture", "AudioCapture", true);
   5060   return StartMediaCapture(std::move(path), capture_video, capture_audio);
   5061 }
   5062 
   5063 bool System::StartMediaCapture(std::string path, bool capture_video, bool capture_audio)
   5064 {
   5065   if (!IsValid())
   5066     return false;
   5067 
   5068   if (s_media_capture)
   5069     StopMediaCapture();
   5070 
   5071   // Need to work out the size.
   5072   u32 capture_width =
   5073     Host::GetUIntSettingValue("MediaCapture", "VideoWidth", Settings::DEFAULT_MEDIA_CAPTURE_VIDEO_WIDTH);
   5074   u32 capture_height =
   5075     Host::GetUIntSettingValue("MediaCapture", "VideoHeight", Settings::DEFAULT_MEDIA_CAPTURE_VIDEO_HEIGHT);
   5076   const GPUTexture::Format capture_format =
   5077     g_gpu_device->HasSurface() ? g_gpu_device->GetWindowFormat() : GPUTexture::Format::RGBA8;
   5078   const float fps = System::GetThrottleFrequency();
   5079   if (capture_video)
   5080   {
   5081     // TODO: This will be a mess with GPU thread.
   5082     if (Host::GetBoolSettingValue("MediaCapture", "VideoAutoSize", false))
   5083     {
   5084       GSVector4i unused_display_rect, unused_draw_rect;
   5085       g_gpu->CalculateScreenshotSize(DisplayScreenshotMode::InternalResolution, &capture_width, &capture_height,
   5086                                      &unused_display_rect, &unused_draw_rect);
   5087     }
   5088 
   5089     MediaCapture::AdjustVideoSize(&capture_width, &capture_height);
   5090   }
   5091 
   5092   // TODO: Render anamorphic capture instead?
   5093   constexpr float aspect = 1.0f;
   5094 
   5095   if (path.empty())
   5096   {
   5097     path =
   5098       GetNewMediaCapturePath(GetGameTitle(), Host::GetStringSettingValue("MediaCapture", "Container",
   5099                                                                          Settings::DEFAULT_MEDIA_CAPTURE_CONTAINER));
   5100   }
   5101 
   5102   const MediaCaptureBackend backend =
   5103     MediaCapture::ParseBackendName(
   5104       Host::GetStringSettingValue("MediaCapture", "Backend",
   5105                                   MediaCapture::GetBackendName(Settings::DEFAULT_MEDIA_CAPTURE_BACKEND))
   5106         .c_str())
   5107       .value_or(Settings::DEFAULT_MEDIA_CAPTURE_BACKEND);
   5108 
   5109   Error error;
   5110   s_media_capture = MediaCapture::Create(backend, &error);
   5111   if (!s_media_capture ||
   5112       !s_media_capture->BeginCapture(
   5113         fps, aspect, capture_width, capture_height, capture_format, SPU::SAMPLE_RATE, std::move(path), capture_video,
   5114         Host::GetSmallStringSettingValue("MediaCapture", "VideoCodec"),
   5115         Host::GetUIntSettingValue("MediaCapture", "VideoBitrate", Settings::DEFAULT_MEDIA_CAPTURE_VIDEO_BITRATE),
   5116         Host::GetBoolSettingValue("MediaCapture", "VideoCodecUseArgs", false) ?
   5117           Host::GetStringSettingValue("MediaCapture", "AudioCodecArgs") :
   5118           std::string(),
   5119         capture_audio, Host::GetSmallStringSettingValue("MediaCapture", "AudioCodec"),
   5120         Host::GetUIntSettingValue("MediaCapture", "AudioBitrate", Settings::DEFAULT_MEDIA_CAPTURE_AUDIO_BITRATE),
   5121         Host::GetBoolSettingValue("MediaCapture", "AudioCodecUseArgs", false) ?
   5122           Host::GetStringSettingValue("MediaCapture", "AudioCodecArgs") :
   5123           std::string(),
   5124         &error))
   5125   {
   5126     Host::AddIconOSDMessage(
   5127       "MediaCapture", ICON_FA_EXCLAMATION_TRIANGLE,
   5128       fmt::format(TRANSLATE_FS("System", "Failed to create media capture: {0}"), error.GetDescription()),
   5129       Host::OSD_ERROR_DURATION);
   5130     s_media_capture.reset();
   5131     Host::OnMediaCaptureStopped();
   5132     return false;
   5133   }
   5134 
   5135   Host::AddIconOSDMessage(
   5136     "MediaCapture", ICON_FA_CAMERA,
   5137     fmt::format(TRANSLATE_FS("System", "Starting {0} to '{1}'."),
   5138                 GetCaptureTypeForMessage(s_media_capture->IsCapturingVideo(), s_media_capture->IsCapturingAudio()),
   5139                 Path::GetFileName(s_media_capture->GetPath())),
   5140     Host::OSD_INFO_DURATION);
   5141 
   5142   Host::OnMediaCaptureStarted();
   5143   return true;
   5144 }
   5145 
   5146 void System::StopMediaCapture()
   5147 {
   5148   if (!s_media_capture)
   5149     return;
   5150 
   5151   const bool was_capturing_audio = s_media_capture->IsCapturingAudio();
   5152   const bool was_capturing_video = s_media_capture->IsCapturingVideo();
   5153 
   5154   Error error;
   5155   if (s_media_capture->EndCapture(&error))
   5156   {
   5157     Host::AddIconOSDMessage("MediaCapture", ICON_FA_CAMERA,
   5158                             fmt::format(TRANSLATE_FS("System", "Stopped {0} to '{1}'."),
   5159                                         GetCaptureTypeForMessage(was_capturing_video, was_capturing_audio),
   5160                                         Path::GetFileName(s_media_capture->GetPath())),
   5161                             Host::OSD_INFO_DURATION);
   5162   }
   5163   else
   5164   {
   5165     Host::AddIconOSDMessage(
   5166       "MediaCapture", ICON_FA_EXCLAMATION_TRIANGLE,
   5167       fmt::format(TRANSLATE_FS("System", "Stopped {0}: {1}."),
   5168                   GetCaptureTypeForMessage(s_media_capture->IsCapturingVideo(), s_media_capture->IsCapturingAudio()),
   5169                   error.GetDescription()),
   5170       Host::OSD_INFO_DURATION);
   5171   }
   5172   s_media_capture.reset();
   5173 
   5174   Host::OnMediaCaptureStopped();
   5175 }
   5176 
   5177 std::string System::GetGameSaveStateFileName(std::string_view serial, s32 slot)
   5178 {
   5179   if (slot < 0)
   5180     return Path::Combine(EmuFolders::SaveStates, fmt::format("{}_resume.sav", serial));
   5181   else
   5182     return Path::Combine(EmuFolders::SaveStates, fmt::format("{}_{}.sav", serial, slot));
   5183 }
   5184 
   5185 std::string System::GetGlobalSaveStateFileName(s32 slot)
   5186 {
   5187   if (slot < 0)
   5188     return Path::Combine(EmuFolders::SaveStates, "resume.sav");
   5189   else
   5190     return Path::Combine(EmuFolders::SaveStates, fmt::format("savestate_{}.sav", slot));
   5191 }
   5192 
   5193 std::vector<SaveStateInfo> System::GetAvailableSaveStates(const char* serial)
   5194 {
   5195   std::vector<SaveStateInfo> si;
   5196   std::string path;
   5197 
   5198   auto add_path = [&si](std::string path, s32 slot, bool global) {
   5199     FILESYSTEM_STAT_DATA sd;
   5200     if (!FileSystem::StatFile(path.c_str(), &sd))
   5201       return;
   5202 
   5203     si.push_back(SaveStateInfo{std::move(path), sd.ModificationTime, static_cast<s32>(slot), global});
   5204   };
   5205 
   5206   if (serial && std::strlen(serial) > 0)
   5207   {
   5208     add_path(GetGameSaveStateFileName(serial, -1), -1, false);
   5209     for (s32 i = 1; i <= PER_GAME_SAVE_STATE_SLOTS; i++)
   5210       add_path(GetGameSaveStateFileName(serial, i), i, false);
   5211   }
   5212 
   5213   for (s32 i = 1; i <= GLOBAL_SAVE_STATE_SLOTS; i++)
   5214     add_path(GetGlobalSaveStateFileName(i), i, true);
   5215 
   5216   return si;
   5217 }
   5218 
   5219 std::optional<SaveStateInfo> System::GetSaveStateInfo(const char* serial, s32 slot)
   5220 {
   5221   const bool global = (!serial || serial[0] == 0);
   5222   std::string path = global ? GetGlobalSaveStateFileName(slot) : GetGameSaveStateFileName(serial, slot);
   5223 
   5224   FILESYSTEM_STAT_DATA sd;
   5225   if (!FileSystem::StatFile(path.c_str(), &sd))
   5226     return std::nullopt;
   5227 
   5228   return SaveStateInfo{std::move(path), sd.ModificationTime, slot, global};
   5229 }
   5230 
   5231 std::optional<ExtendedSaveStateInfo> System::GetExtendedSaveStateInfo(const char* path)
   5232 {
   5233   std::optional<ExtendedSaveStateInfo> ssi;
   5234 
   5235   Error error;
   5236   auto fp = FileSystem::OpenManagedCFile(path, "rb", &error);
   5237   if (fp)
   5238   {
   5239     ssi.emplace();
   5240 
   5241     SaveStateBuffer buffer;
   5242     if (LoadStateBufferFromFile(&buffer, fp.get(), &error, true, true, true, false)) [[likely]]
   5243     {
   5244       ssi->title = std::move(buffer.title);
   5245       ssi->serial = std::move(buffer.serial);
   5246       ssi->media_path = std::move(buffer.media_path);
   5247       ssi->screenshot = std::move(buffer.screenshot);
   5248 
   5249       FILESYSTEM_STAT_DATA sd;
   5250       ssi->timestamp = FileSystem::StatFile(fp.get(), &sd) ? sd.ModificationTime : 0;
   5251     }
   5252     else
   5253     {
   5254       ssi->title = error.GetDescription();
   5255       ssi->timestamp = 0;
   5256     }
   5257   }
   5258 
   5259   return ssi;
   5260 }
   5261 
   5262 void System::DeleteSaveStates(const char* serial, bool resume)
   5263 {
   5264   const std::vector<SaveStateInfo> states(GetAvailableSaveStates(serial));
   5265   for (const SaveStateInfo& si : states)
   5266   {
   5267     if (si.global || (!resume && si.slot < 0))
   5268       continue;
   5269 
   5270     INFO_LOG("Removing save state '{}'", Path::GetFileName(si.path));
   5271 
   5272     Error error;
   5273     if (!FileSystem::DeleteFile(si.path.c_str(), &error)) [[unlikely]]
   5274       ERROR_LOG("Failed to delete save state file '{}': {}", Path::GetFileName(si.path), error.GetDescription());
   5275   }
   5276 }
   5277 
   5278 std::string System::GetGameMemoryCardPath(std::string_view serial, std::string_view path, u32 slot,
   5279                                           MemoryCardType* out_type)
   5280 {
   5281   const char* section = "MemoryCards";
   5282   const TinyString type_key = TinyString::from_format("Card{}Type", slot + 1);
   5283   const MemoryCardType default_type =
   5284     (slot == 0) ? Settings::DEFAULT_MEMORY_CARD_1_TYPE : Settings::DEFAULT_MEMORY_CARD_2_TYPE;
   5285   const MemoryCardType global_type =
   5286     Settings::ParseMemoryCardTypeName(
   5287       Host::GetBaseTinyStringSettingValue(section, type_key, Settings::GetMemoryCardTypeName(default_type)))
   5288       .value_or(default_type);
   5289 
   5290   MemoryCardType type = global_type;
   5291   std::unique_ptr<INISettingsInterface> ini;
   5292   if (!serial.empty())
   5293   {
   5294     std::string game_settings_path = GetGameSettingsPath(serial);
   5295     if (FileSystem::FileExists(game_settings_path.c_str()))
   5296     {
   5297       ini = std::make_unique<INISettingsInterface>(std::move(game_settings_path));
   5298       if (!ini->Load())
   5299       {
   5300         ini.reset();
   5301       }
   5302       else if (ini->ContainsValue(section, type_key))
   5303       {
   5304         type = Settings::ParseMemoryCardTypeName(
   5305                  ini->GetTinyStringValue(section, type_key, Settings::GetMemoryCardTypeName(global_type)))
   5306                  .value_or(global_type);
   5307       }
   5308     }
   5309   }
   5310   else if (type == MemoryCardType::PerGame)
   5311   {
   5312     // always shared without serial
   5313     type = MemoryCardType::Shared;
   5314   }
   5315 
   5316   if (out_type)
   5317     *out_type = type;
   5318 
   5319   std::string ret;
   5320   switch (type)
   5321   {
   5322     case MemoryCardType::None:
   5323       break;
   5324 
   5325     case MemoryCardType::Shared:
   5326     {
   5327       const TinyString path_key = TinyString::from_format("Card{}Path", slot + 1);
   5328       std::string global_path =
   5329         Host::GetBaseStringSettingValue(section, path_key, Settings::GetDefaultSharedMemoryCardName(slot + 1).c_str());
   5330       if (ini && ini->ContainsValue(section, path_key))
   5331         ret = ini->GetStringValue(section, path_key, global_path.c_str());
   5332       else
   5333         ret = std::move(global_path);
   5334 
   5335       if (!Path::IsAbsolute(ret))
   5336         ret = Path::Combine(EmuFolders::MemoryCards, ret);
   5337     }
   5338     break;
   5339 
   5340     case MemoryCardType::PerGame:
   5341       ret = g_settings.GetGameMemoryCardPath(serial, slot);
   5342       break;
   5343 
   5344     case MemoryCardType::PerGameTitle:
   5345     {
   5346       const GameDatabase::Entry* entry = GameDatabase::GetEntryForSerial(serial);
   5347       if (entry)
   5348       {
   5349         ret = g_settings.GetGameMemoryCardPath(Path::SanitizeFileName(entry->title), slot);
   5350 
   5351         // Use disc set name if there isn't a per-disc card present.
   5352         const bool global_use_playlist_title = Host::GetBaseBoolSettingValue(section, "UsePlaylistTitle", true);
   5353         const bool use_playlist_title =
   5354           ini ? ini->GetBoolValue(section, "UsePlaylistTitle", global_use_playlist_title) : global_use_playlist_title;
   5355         if (!entry->disc_set_name.empty() && use_playlist_title && !FileSystem::FileExists(ret.c_str()))
   5356           ret = g_settings.GetGameMemoryCardPath(Path::SanitizeFileName(entry->disc_set_name), slot);
   5357       }
   5358       else
   5359       {
   5360         ret = g_settings.GetGameMemoryCardPath(
   5361           Path::SanitizeFileName(Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(path))), slot);
   5362       }
   5363     }
   5364     break;
   5365 
   5366     case MemoryCardType::PerGameFileTitle:
   5367     {
   5368       ret = g_settings.GetGameMemoryCardPath(
   5369         Path::SanitizeFileName(Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(path))), slot);
   5370     }
   5371     break;
   5372     default:
   5373       break;
   5374   }
   5375 
   5376   return ret;
   5377 }
   5378 
   5379 std::string System::GetMostRecentResumeSaveStatePath()
   5380 {
   5381   std::vector<FILESYSTEM_FIND_DATA> files;
   5382   if (!FileSystem::FindFiles(EmuFolders::SaveStates.c_str(), "*resume.sav", FILESYSTEM_FIND_FILES, &files) ||
   5383       files.empty())
   5384   {
   5385     return {};
   5386   }
   5387 
   5388   FILESYSTEM_FIND_DATA* most_recent = &files[0];
   5389   for (FILESYSTEM_FIND_DATA& file : files)
   5390   {
   5391     if (file.ModificationTime > most_recent->ModificationTime)
   5392       most_recent = &file;
   5393   }
   5394 
   5395   return std::move(most_recent->FileName);
   5396 }
   5397 
   5398 std::string System::GetCheatFileName()
   5399 {
   5400   std::string ret;
   5401 
   5402   const std::string& title = System::GetGameTitle();
   5403   if (!title.empty())
   5404     ret = Path::Combine(EmuFolders::Cheats, fmt::format("{}.cht", title.c_str()));
   5405 
   5406   return ret;
   5407 }
   5408 
   5409 bool System::LoadCheatList()
   5410 {
   5411   // Called when booting, needs to test for shutdown.
   5412   if (IsShutdown() || !g_settings.enable_cheats)
   5413     return false;
   5414 
   5415   const std::string filename(GetCheatFileName());
   5416   if (filename.empty() || !FileSystem::FileExists(filename.c_str()))
   5417     return false;
   5418 
   5419   std::unique_ptr<CheatList> cl = std::make_unique<CheatList>();
   5420   if (!cl->LoadFromFile(filename.c_str(), CheatList::Format::Autodetect))
   5421   {
   5422     Host::AddIconOSDMessage(
   5423       "cheats_loaded", ICON_FA_EXCLAMATION_TRIANGLE,
   5424       fmt::format(TRANSLATE_FS("System", "Failed to load cheats from '{}'."), Path::GetFileName(filename)));
   5425     return false;
   5426   }
   5427 
   5428   SetCheatList(std::move(cl));
   5429   return true;
   5430 }
   5431 
   5432 bool System::LoadCheatListFromDatabase()
   5433 {
   5434   if (IsShutdown() || s_running_game_serial.empty() || Achievements::IsHardcoreModeActive())
   5435     return false;
   5436 
   5437   std::unique_ptr<CheatList> cl = std::make_unique<CheatList>();
   5438   if (!cl->LoadFromPackage(s_running_game_serial))
   5439     return false;
   5440 
   5441   INFO_LOG("Loaded {} cheats from database.", cl->GetCodeCount());
   5442   SetCheatList(std::move(cl));
   5443   return true;
   5444 }
   5445 
   5446 bool System::SaveCheatList()
   5447 {
   5448   if (!System::IsValid() || !System::HasCheatList())
   5449     return false;
   5450 
   5451   const std::string filename(GetCheatFileName());
   5452   if (filename.empty())
   5453     return false;
   5454 
   5455   if (!System::GetCheatList()->SaveToPCSXRFile(filename.c_str()))
   5456   {
   5457     Host::AddIconOSDMessage(
   5458       "cheat_save_error", ICON_FA_EXCLAMATION_TRIANGLE,
   5459       fmt::format(TRANSLATE_FS("System", "Failed to save cheat list to '{}'."), Path::GetFileName(filename)),
   5460       Host::OSD_ERROR_DURATION);
   5461   }
   5462 
   5463   return true;
   5464 }
   5465 
   5466 bool System::DeleteCheatList()
   5467 {
   5468   if (!System::IsValid())
   5469     return false;
   5470 
   5471   const std::string filename(GetCheatFileName());
   5472   if (!filename.empty())
   5473   {
   5474     if (!FileSystem::DeleteFile(filename.c_str()))
   5475       return false;
   5476 
   5477     Host::AddIconOSDMessage(
   5478       "cheat_delete", ICON_FA_EXCLAMATION_TRIANGLE,
   5479       fmt::format(TRANSLATE_FS("System", "Deleted cheat list '{}'."), Path::GetFileName(filename)),
   5480       Host::OSD_INFO_DURATION);
   5481   }
   5482 
   5483   System::SetCheatList(nullptr);
   5484   return true;
   5485 }
   5486 
   5487 void System::ClearCheatList(bool save_to_file)
   5488 {
   5489   if (!System::IsValid())
   5490     return;
   5491 
   5492   CheatList* cl = System::GetCheatList();
   5493   if (!cl)
   5494     return;
   5495 
   5496   while (cl->GetCodeCount() > 0)
   5497     cl->RemoveCode(cl->GetCodeCount() - 1);
   5498 
   5499   if (save_to_file)
   5500     SaveCheatList();
   5501 }
   5502 
   5503 void System::SetCheatCodeState(u32 index, bool enabled)
   5504 {
   5505   if (!System::IsValid() || !System::HasCheatList())
   5506     return;
   5507 
   5508   CheatList* cl = System::GetCheatList();
   5509   if (index >= cl->GetCodeCount())
   5510     return;
   5511 
   5512   CheatCode& cc = cl->GetCode(index);
   5513   if (cc.enabled == enabled)
   5514     return;
   5515 
   5516   cc.enabled = enabled;
   5517   if (!enabled)
   5518     cc.ApplyOnDisable();
   5519 
   5520   if (enabled)
   5521   {
   5522     Host::AddIconOSDMessage(fmt::format("cheat_{}_state", index), ICON_FA_EXCLAMATION_TRIANGLE,
   5523                             fmt::format(TRANSLATE_FS("System", "Cheat '{}' enabled."), cc.description),
   5524                             Host::OSD_INFO_DURATION);
   5525   }
   5526   else
   5527   {
   5528     Host::AddIconOSDMessage(fmt::format("cheat_{}_state", index), ICON_FA_EXCLAMATION_TRIANGLE,
   5529                             fmt::format(TRANSLATE_FS("System", "Cheat '{}' disabled."), cc.description),
   5530                             Host::OSD_INFO_DURATION);
   5531   }
   5532 
   5533   SaveCheatList();
   5534 }
   5535 
   5536 void System::ApplyCheatCode(u32 index)
   5537 {
   5538   if (!System::HasCheatList() || index >= System::GetCheatList()->GetCodeCount())
   5539     return;
   5540 
   5541   const CheatCode& cc = System::GetCheatList()->GetCode(index);
   5542   if (!cc.enabled)
   5543   {
   5544     cc.Apply();
   5545     Host::AddIconOSDMessage(fmt::format("cheat_{}_state", index), ICON_FA_EXCLAMATION_TRIANGLE,
   5546                             fmt::format(TRANSLATE_FS("System", "Applied cheat '{}'."), cc.description),
   5547                             Host::OSD_INFO_DURATION);
   5548   }
   5549   else
   5550   {
   5551     Host::AddIconOSDMessage(fmt::format("cheat_{}_state", index), ICON_FA_EXCLAMATION_TRIANGLE,
   5552                             fmt::format(TRANSLATE_FS("System", "Cheat '{}' is already enabled."), cc.description),
   5553                             Host::OSD_INFO_DURATION);
   5554   }
   5555 }
   5556 
   5557 void System::ToggleWidescreen()
   5558 {
   5559   g_settings.gpu_widescreen_hack = !g_settings.gpu_widescreen_hack;
   5560 
   5561   const DisplayAspectRatio user_ratio =
   5562     Settings::ParseDisplayAspectRatio(
   5563       Host::GetStringSettingValue("Display", "AspectRatio",
   5564                                   Settings::GetDisplayAspectRatioName(Settings::DEFAULT_DISPLAY_ASPECT_RATIO))
   5565         .c_str())
   5566       .value_or(DisplayAspectRatio::Auto);
   5567   ;
   5568 
   5569   if (user_ratio == DisplayAspectRatio::Auto || user_ratio == DisplayAspectRatio::PAR1_1 ||
   5570       user_ratio == DisplayAspectRatio::R4_3)
   5571   {
   5572     g_settings.display_aspect_ratio = g_settings.gpu_widescreen_hack ? DisplayAspectRatio::R16_9 : user_ratio;
   5573   }
   5574   else
   5575   {
   5576     g_settings.display_aspect_ratio = g_settings.gpu_widescreen_hack ? user_ratio : DisplayAspectRatio::Auto;
   5577   }
   5578 
   5579   if (g_settings.gpu_widescreen_hack)
   5580   {
   5581     Host::AddKeyedOSDMessage(
   5582       "WidescreenHack",
   5583       fmt::format(TRANSLATE_FS("OSDMessage", "Widescreen hack is now enabled, and aspect ratio is set to {}."),
   5584                   Settings::GetDisplayAspectRatioDisplayName(g_settings.display_aspect_ratio)),
   5585       5.0f);
   5586   }
   5587   else
   5588   {
   5589     Host::AddKeyedOSDMessage(
   5590       "WidescreenHack",
   5591       fmt::format(TRANSLATE_FS("OSDMessage", "Widescreen hack is now disabled, and aspect ratio is set to {}."),
   5592                   Settings::GetDisplayAspectRatioDisplayName(g_settings.display_aspect_ratio), 5.0f));
   5593   }
   5594 
   5595   GTE::UpdateAspectRatio();
   5596 }
   5597 
   5598 void System::ToggleSoftwareRendering()
   5599 {
   5600   if (IsShutdown() || g_settings.gpu_renderer == GPURenderer::Software)
   5601     return;
   5602 
   5603   const GPURenderer new_renderer = g_gpu->IsHardwareRenderer() ? GPURenderer::Software : g_settings.gpu_renderer;
   5604 
   5605   Host::AddIconOSDMessage("SoftwareRendering", ICON_FA_PAINT_ROLLER,
   5606                           fmt::format(TRANSLATE_FS("OSDMessage", "Switching to {} renderer..."),
   5607                                       Settings::GetRendererDisplayName(new_renderer)),
   5608                           Host::OSD_QUICK_DURATION);
   5609   RecreateGPU(new_renderer);
   5610   ResetPerformanceCounters();
   5611 }
   5612 
   5613 void System::RequestDisplaySize(float scale /*= 0.0f*/)
   5614 {
   5615   if (!IsValid())
   5616     return;
   5617 
   5618   if (scale == 0.0f)
   5619     scale = g_gpu->IsHardwareRenderer() ? static_cast<float>(g_settings.gpu_resolution_scale) : 1.0f;
   5620 
   5621   const float y_scale =
   5622     (static_cast<float>(g_gpu->GetCRTCDisplayWidth()) / static_cast<float>(g_gpu->GetCRTCDisplayHeight())) /
   5623     g_gpu->ComputeDisplayAspectRatio();
   5624 
   5625   u32 requested_width =
   5626     std::max<u32>(static_cast<u32>(std::ceil(static_cast<float>(g_gpu->GetCRTCDisplayWidth()) * scale)), 1);
   5627   u32 requested_height =
   5628     std::max<u32>(static_cast<u32>(std::ceil(static_cast<float>(g_gpu->GetCRTCDisplayHeight()) * y_scale * scale)), 1);
   5629 
   5630   if (g_settings.display_rotation == DisplayRotation::Rotate90 ||
   5631       g_settings.display_rotation == DisplayRotation::Rotate270)
   5632     std::swap(requested_width, requested_height);
   5633 
   5634   Host::RequestResizeHostDisplay(static_cast<s32>(requested_width), static_cast<s32>(requested_height));
   5635 }
   5636 
   5637 void System::HostDisplayResized()
   5638 {
   5639   if (!IsValid())
   5640     return;
   5641 
   5642   if (g_settings.gpu_widescreen_hack && g_settings.display_aspect_ratio == DisplayAspectRatio::MatchWindow)
   5643     GTE::UpdateAspectRatio();
   5644 
   5645   g_gpu->UpdateResolutionScale();
   5646 }
   5647 
   5648 bool System::PresentDisplay(bool skip_present, bool explicit_present)
   5649 {
   5650   // acquire for IO.MousePos.
   5651   std::atomic_thread_fence(std::memory_order_acquire);
   5652 
   5653   if (!skip_present)
   5654   {
   5655     FullscreenUI::Render();
   5656     ImGuiManager::RenderTextOverlays();
   5657     ImGuiManager::RenderOSDMessages();
   5658 
   5659     if (s_state == State::Running)
   5660       ImGuiManager::RenderSoftwareCursors();
   5661   }
   5662 
   5663   // Debug windows are always rendered, otherwise mouse input breaks on skip.
   5664   ImGuiManager::RenderOverlayWindows();
   5665   ImGuiManager::RenderDebugWindows();
   5666 
   5667   bool do_present;
   5668   if (g_gpu && !skip_present)
   5669     do_present = g_gpu->PresentDisplay();
   5670   else
   5671     do_present = g_gpu_device->BeginPresent(skip_present);
   5672 
   5673   if (do_present)
   5674   {
   5675     g_gpu_device->RenderImGui();
   5676     g_gpu_device->EndPresent(explicit_present);
   5677 
   5678     if (g_gpu_device->IsGPUTimingEnabled())
   5679     {
   5680       s_accumulated_gpu_time += g_gpu_device->GetAndResetAccumulatedGPUTime();
   5681       s_presents_since_last_update++;
   5682     }
   5683   }
   5684   else
   5685   {
   5686     // Still need to kick ImGui or it gets cranky.
   5687     ImGui::Render();
   5688   }
   5689 
   5690   ImGuiManager::NewFrame();
   5691 
   5692   return do_present;
   5693 }
   5694 
   5695 void System::InvalidateDisplay()
   5696 {
   5697   PresentDisplay(false, false);
   5698 
   5699   if (g_gpu)
   5700     g_gpu->RestoreDeviceContext();
   5701 }
   5702 
   5703 void System::SetTimerResolutionIncreased(bool enabled)
   5704 {
   5705 #if defined(_WIN32)
   5706   static bool current_state = false;
   5707   if (current_state == enabled)
   5708     return;
   5709 
   5710   current_state = enabled;
   5711 
   5712   if (enabled)
   5713     timeBeginPeriod(1);
   5714   else
   5715     timeEndPeriod(1);
   5716 #endif
   5717 }
   5718 
   5719 void System::UpdateSessionTime(const std::string& prev_serial)
   5720 {
   5721   const u64 ctime = Common::Timer::GetCurrentValue();
   5722   if (!prev_serial.empty() && GameList::IsGameListLoaded())
   5723   {
   5724     // round up to seconds
   5725     const std::time_t etime =
   5726       static_cast<std::time_t>(std::round(Common::Timer::ConvertValueToSeconds(ctime - s_session_start_time)));
   5727     const std::time_t wtime = std::time(nullptr);
   5728     GameList::AddPlayedTimeForSerial(prev_serial, wtime, etime);
   5729   }
   5730 
   5731   s_session_start_time = ctime;
   5732 }
   5733 
   5734 u64 System::GetSessionPlayedTime()
   5735 {
   5736   const u64 ctime = Common::Timer::GetCurrentValue();
   5737   return static_cast<u64>(std::round(Common::Timer::ConvertValueToSeconds(ctime - s_session_start_time)));
   5738 }
   5739 
   5740 SocketMultiplexer* System::GetSocketMultiplexer()
   5741 {
   5742 #ifdef ENABLE_SOCKET_MULTIPLEXER
   5743   if (s_socket_multiplexer)
   5744     return s_socket_multiplexer.get();
   5745 
   5746   Error error;
   5747   s_socket_multiplexer = SocketMultiplexer::Create(&error);
   5748   if (s_socket_multiplexer)
   5749     INFO_LOG("Created socket multiplexer.");
   5750   else
   5751     ERROR_LOG("Failed to create socket multiplexer: {}", error.GetDescription());
   5752 
   5753   return s_socket_multiplexer.get();
   5754 #else
   5755   ERROR_LOG("This build does not support sockets.");
   5756   return nullptr;
   5757 #endif
   5758 }
   5759 
   5760 void System::ReleaseSocketMultiplexer()
   5761 {
   5762 #ifdef ENABLE_SOCKET_MULTIPLEXER
   5763   if (!s_socket_multiplexer || s_socket_multiplexer->HasAnyOpenSockets())
   5764     return;
   5765 
   5766   INFO_LOG("Destroying socket multiplexer.");
   5767   s_socket_multiplexer.reset();
   5768 #endif
   5769 }
   5770 
   5771 #ifdef ENABLE_DISCORD_PRESENCE
   5772 
   5773 #include "discord_rpc.h"
   5774 
   5775 #define DISCORD_RPC_FUNCTIONS(X)                                                                                       \
   5776   X(Discord_Initialize)                                                                                                \
   5777   X(Discord_Shutdown)                                                                                                  \
   5778   X(Discord_RunCallbacks)                                                                                              \
   5779   X(Discord_UpdatePresence)                                                                                            \
   5780   X(Discord_ClearPresence)
   5781 
   5782 namespace dyn_libs {
   5783 static bool OpenDiscordRPC(Error* error);
   5784 static void CloseDiscordRPC();
   5785 
   5786 static DynamicLibrary s_discord_rpc_library;
   5787 
   5788 #define ADD_FUNC(F) static decltype(&::F) F;
   5789 DISCORD_RPC_FUNCTIONS(ADD_FUNC)
   5790 #undef ADD_FUNC
   5791 } // namespace dyn_libs
   5792 
   5793 bool dyn_libs::OpenDiscordRPC(Error* error)
   5794 {
   5795   if (s_discord_rpc_library.IsOpen())
   5796     return true;
   5797 
   5798   const std::string libname = DynamicLibrary::GetVersionedFilename("discord-rpc");
   5799   if (!s_discord_rpc_library.Open(libname.c_str(), error))
   5800   {
   5801     Error::AddPrefix(error, "Failed to load discord-rpc: ");
   5802     return false;
   5803   }
   5804 
   5805 #define LOAD_FUNC(F)                                                                                                   \
   5806   if (!s_discord_rpc_library.GetSymbol(#F, &F))                                                                        \
   5807   {                                                                                                                    \
   5808     Error::SetStringFmt(error, "Failed to find function {}", #F);                                                      \
   5809     CloseDiscordRPC();                                                                                                 \
   5810     return false;                                                                                                      \
   5811   }
   5812   DISCORD_RPC_FUNCTIONS(LOAD_FUNC)
   5813 #undef LOAD_FUNC
   5814 
   5815   return true;
   5816 }
   5817 
   5818 void dyn_libs::CloseDiscordRPC()
   5819 {
   5820   if (!s_discord_rpc_library.IsOpen())
   5821     return;
   5822 
   5823 #define UNLOAD_FUNC(F) F = nullptr;
   5824   DISCORD_RPC_FUNCTIONS(UNLOAD_FUNC)
   5825 #undef UNLOAD_FUNC
   5826 
   5827   s_discord_rpc_library.Close();
   5828 }
   5829 
   5830 void System::InitializeDiscordPresence()
   5831 {
   5832   if (s_discord_presence_active)
   5833     return;
   5834 
   5835   Error error;
   5836   if (!dyn_libs::OpenDiscordRPC(&error))
   5837   {
   5838     ERROR_LOG("Failed to open discord-rpc: {}", error.GetDescription());
   5839     return;
   5840   }
   5841 
   5842   DiscordEventHandlers handlers = {};
   5843   dyn_libs::Discord_Initialize("705325712680288296", &handlers, 0, nullptr);
   5844   s_discord_presence_active = true;
   5845 
   5846   UpdateRichPresence(true);
   5847 }
   5848 
   5849 void System::ShutdownDiscordPresence()
   5850 {
   5851   if (!s_discord_presence_active)
   5852     return;
   5853 
   5854   dyn_libs::Discord_ClearPresence();
   5855   dyn_libs::Discord_Shutdown();
   5856   dyn_libs::CloseDiscordRPC();
   5857 
   5858   s_discord_presence_active = false;
   5859 }
   5860 
   5861 void System::UpdateRichPresence(bool update_session_time)
   5862 {
   5863   if (!s_discord_presence_active)
   5864     return;
   5865 
   5866   if (update_session_time)
   5867     s_discord_presence_time_epoch = std::time(nullptr);
   5868 
   5869   // https://discord.com/developers/docs/rich-presence/how-to#updating-presence-update-presence-payload-fields
   5870   DiscordRichPresence rp = {};
   5871   rp.largeImageKey = "duckstation_logo";
   5872   rp.largeImageText = "DuckStation PS1/PSX Emulator";
   5873   rp.startTimestamp = s_discord_presence_time_epoch;
   5874   rp.details = "No Game Running";
   5875   if (IsValidOrInitializing())
   5876   {
   5877     // Use disc set name if it's not a custom title.
   5878     if (s_running_game_entry && !s_running_game_entry->disc_set_name.empty() &&
   5879         s_running_game_title == s_running_game_entry->title)
   5880     {
   5881       rp.details = s_running_game_entry->disc_set_name.c_str();
   5882     }
   5883     else
   5884     {
   5885       rp.details = s_running_game_title.empty() ? "Unknown Game" : s_running_game_title.c_str();
   5886     }
   5887   }
   5888 
   5889   std::string state_string;
   5890   if (Achievements::HasRichPresence())
   5891   {
   5892     const auto lock = Achievements::GetLock();
   5893     state_string = StringUtil::Ellipsise(Achievements::GetRichPresenceString(), 128);
   5894     rp.state = state_string.c_str();
   5895   }
   5896 
   5897   dyn_libs::Discord_UpdatePresence(&rp);
   5898 }
   5899 
   5900 void System::PollDiscordPresence()
   5901 {
   5902   if (!s_discord_presence_active)
   5903     return;
   5904 
   5905   dyn_libs::Discord_RunCallbacks();
   5906 }
   5907 
   5908 #else
   5909 
   5910 void System::UpdateRichPresence(bool update_session_time)
   5911 {
   5912 }
   5913 
   5914 #endif