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