diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index f7ef0e5062..9b404c9623 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -76,6 +76,26 @@ atomic_t g_progr_fdone{0}; atomic_t g_progr_ptotal{0}; atomic_t g_progr_pdone{0}; +template<> +void fmt_class_string::format(std::string& out, u64 arg) +{ + format_enum(out, arg, [](game_boot_result value) + { + switch (value) + { + case game_boot_result::no_errors: return "No errors"; + case game_boot_result::generic_error: return "Generic error"; + case game_boot_result::nothing_to_boot: return "Nothing to boot"; + case game_boot_result::wrong_disc_location: return "Wrong disc location"; + case game_boot_result::invalid_file_or_folder: return "Invalid file or folder"; + case game_boot_result::install_failed: return "Game install failed"; + case game_boot_result::decryption_error: return "Failed to decrypt content"; + case game_boot_result::file_creation_error: return "Could not create important files"; + } + return unknown; + }); +} + void Emulator::Init() { jit_runtime::initialize(); @@ -493,7 +513,7 @@ void Emulator::LimitCacheSize() sys_log.success("Cleaned disk cache, removed %.2f MB", size / 1024.0 / 1024.0); } -bool Emulator::BootGame(const std::string& path, const std::string& title_id, bool direct, bool add_only, bool force_global_config) +game_boot_result Emulator::BootGame(const std::string& path, const std::string& title_id, bool direct, bool add_only, bool force_global_config) { if (g_cfg.vfs.limit_cache_size) LimitCacheSize(); @@ -512,11 +532,10 @@ bool Emulator::BootGame(const std::string& path, const std::string& title_id, bo if (direct && fs::exists(path)) { m_path = path; - Load(title_id, add_only, force_global_config); - return true; + return Load(title_id, add_only, force_global_config); } - bool success = false; + game_boot_result result = game_boot_result::nothing_to_boot; for (std::string elf : boot_list) { elf = path + elf; @@ -524,8 +543,7 @@ bool Emulator::BootGame(const std::string& path, const std::string& title_id, bo if (fs::is_file(elf)) { m_path = elf; - Load(title_id, add_only, force_global_config); - success = true; + result = Load(title_id, add_only, force_global_config); break; } } @@ -546,14 +564,15 @@ bool Emulator::BootGame(const std::string& path, const std::string& title_id, bo if (fs::is_file(elf)) { m_path = elf; - Load(title_id, add_only, force_global_config); - success = true; + if (const auto err = Load(title_id, add_only, force_global_config); err != game_boot_result::no_errors) + { + result = err; + } } } } } - - return success; + return result; } bool Emulator::InstallPkg(const std::string& path) @@ -696,7 +715,7 @@ void Emulator::SetForceBoot(bool force_boot) m_force_boot = force_boot; } -void Emulator::Load(const std::string& title_id, bool add_only, bool force_global_config, bool is_disc_patch) +game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool force_global_config, bool is_disc_patch) { m_force_global_config = force_global_config; @@ -773,8 +792,8 @@ void Emulator::Load(const std::string& title_id, bool add_only, bool force_globa if (!_psf.empty() && m_cat.empty()) { - sys_log.fatal("Corrupted PARAM.SFO found! Assuming category GD. Try reinstalling the game."); - m_cat = "GD"; + sys_log.fatal("Corrupted PARAM.SFO found! Try reinstalling the game."); + return game_boot_result::invalid_file_or_folder; } sys_log.notice("Title: %s", GetTitle()); @@ -985,7 +1004,7 @@ void Emulator::Load(const std::string& title_id, bool add_only, bool force_globa }); }); - return; + return game_boot_result::no_errors; } // Detect boot location @@ -1009,7 +1028,7 @@ void Emulator::Load(const std::string& title_id, bool add_only, bool force_globa else { sys_log.error("Failed to move disc game %s to /dev_hdd0/disc/ (%s)", m_title_id, fs::g_tls_error); - return; + return game_boot_result::wrong_disc_location; } } @@ -1037,6 +1056,7 @@ void Emulator::Load(const std::string& title_id, bool add_only, bool force_globa else { sys_log.fatal("Disc directory not found. Try to run the game from the actual game disc directory."); + return game_boot_result::invalid_file_or_folder; } } @@ -1054,7 +1074,7 @@ void Emulator::Load(const std::string& title_id, bool add_only, bool force_globa if (!sfb_file.open(vfs::get("/dev_bdvd/PS3_DISC.SFB")) || sfb_file.size() < 4 || sfb_file.read() != ".SFB"_u32) { sys_log.error("Invalid disc directory for the disc game %s", m_title_id); - return; + return game_boot_result::invalid_file_or_folder; } const std::string bdvd_title_id = psf::get_string(psf::load_object(fs::file{vfs::get("/dev_bdvd/PS3_GAME/PARAM.SFO")}), "TITLE_ID"); @@ -1062,7 +1082,7 @@ void Emulator::Load(const std::string& title_id, bool add_only, bool force_globa if (bdvd_title_id != m_title_id) { sys_log.error("Unexpected disc directory for the disc game %s (found %s)", m_title_id, bdvd_title_id); - return; + return game_boot_result::invalid_file_or_folder; } // Store /dev_bdvd/ location @@ -1114,7 +1134,7 @@ void Emulator::Load(const std::string& title_id, bool add_only, bool force_globa else if (disc.empty()) { sys_log.error("Failed to mount disc directory for the disc game %s", m_title_id); - return; + return game_boot_result::invalid_file_or_folder; } else { @@ -1128,7 +1148,7 @@ void Emulator::Load(const std::string& title_id, bool add_only, bool force_globa { sys_log.notice("Finished to add data to games.yml by boot for: %s", m_path); m_path = m_path_old; // Reset m_path to fix boot from gui - return; + return game_boot_result::no_errors; } // Install PKGDIR, INSDIR, PS3_EXTRA @@ -1155,7 +1175,7 @@ void Emulator::Load(const std::string& title_id, bool add_only, bool force_globa if (!entry.is_directory && entry.name.ends_with(".PKG") && !InstallPkg(pkg)) { sys_log.error("Failed to install %s", pkg); - return; + return game_boot_result::install_failed; } } } @@ -1173,7 +1193,7 @@ void Emulator::Load(const std::string& title_id, bool add_only, bool force_globa if (fs::is_file(pkg_file) && !InstallPkg(pkg_file)) { sys_log.error("Failed to install %s", pkg_file); - return; + return game_boot_result::install_failed; } } } @@ -1192,7 +1212,7 @@ void Emulator::Load(const std::string& title_id, bool add_only, bool force_globa if (fs::is_file(pkg_file) && !InstallPkg(pkg_file)) { sys_log.error("Failed to install %s", pkg_file); - return; + return game_boot_result::install_failed; } } } @@ -1250,7 +1270,7 @@ void Emulator::Load(const std::string& title_id, bool add_only, bool force_globa if (!elf_file) { sys_log.error("Failed to open executable: %s", elf_path); - return; + return game_boot_result::invalid_file_or_folder; } // Check SELF header @@ -1292,7 +1312,7 @@ void Emulator::Load(const std::string& title_id, bool add_only, bool force_globa if (!elf_file) { sys_log.error("Failed to decrypt SELF: %s", elf_path); - return; + return game_boot_result::decryption_error; } ppu_exec_object ppu_exec; @@ -1360,7 +1380,8 @@ void Emulator::Load(const std::string& title_id, bool add_only, bool force_globa if (!fs::create_path(_main->cache)) { - fmt::throw_exception("Failed to create cache directory: %s (%s)", _main->cache, fs::g_tls_error); + sys_log.error("Failed to create cache directory: %s (%s)", _main->cache, fs::g_tls_error); + return game_boot_result::file_creation_error; } else { @@ -1398,7 +1419,7 @@ void Emulator::Load(const std::string& title_id, bool add_only, bool force_globa sys_log.warning("** ppu_exec -> %s", ppu_exec.get_error()); sys_log.warning("** ppu_prx -> %s", ppu_prx.get_error()); sys_log.warning("** spu_exec -> %s", spu_exec.get_error()); - return; + return game_boot_result::invalid_file_or_folder; } if ((m_force_boot || g_cfg.misc.autostart) && IsReady()) @@ -1411,11 +1432,13 @@ void Emulator::Load(const std::string& title_id, bool add_only, bool force_globa m_state = system_state::ready; GetCallbacks().on_ready(); } + return game_boot_result::no_errors; } catch (const std::exception& e) { sys_log.fatal("%s thrown: %s", typeid(e).name(), e.what()); Stop(); + return game_boot_result::generic_error; } } @@ -1583,7 +1606,9 @@ void Emulator::Stop(bool restart) if (restart) { // Reload with prior configs. - return Load(m_title_id, false, m_force_global_config); + if (const auto error = Load(m_title_id, false, m_force_global_config); error != game_boot_result::no_errors) + sys_log.error("Restart failed: %s", error); + return; } m_force_boot = false; @@ -1636,7 +1661,9 @@ void Emulator::Stop(bool restart) if (restart) { // Reload with prior configs. - return Load(m_title_id, false, m_force_global_config); + if (const auto error = Load(m_title_id, false, m_force_global_config); error != game_boot_result::no_errors) + sys_log.error("Restart failed: %s", error); + return; } // Boot arg cleanup (preserved in the case restarting) diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index 2d6c7272b1..7a24f0e4c9 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -16,6 +16,18 @@ enum class system_state ready, }; +enum class game_boot_result : u32 +{ + no_errors, + generic_error, + nothing_to_boot, + wrong_disc_location, + invalid_file_or_folder, + install_failed, + decryption_error, + file_creation_error, +}; + struct EmuCallbacks { std::function)> call_after; @@ -153,7 +165,7 @@ public: std::string PPUCache() const; - bool BootGame(const std::string& path, const std::string& title_id = "", bool direct = false, bool add_only = false, bool force_global_config = false); + game_boot_result BootGame(const std::string& path, const std::string& title_id = "", bool direct = false, bool add_only = false, bool force_global_config = false); bool BootRsxCapture(const std::string& path); bool InstallPkg(const std::string& path); @@ -173,7 +185,7 @@ public: void SetForceBoot(bool force_boot); - void Load(const std::string& title_id = "", bool add_only = false, bool force_global_config = false, bool is_disc_patch = false); + game_boot_result Load(const std::string& title_id = "", bool add_only = false, bool force_global_config = false, bool is_disc_patch = false); void Run(bool start_playtime); bool Pause(); void Resume(); diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index 8467a9f696..082b49b18d 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -1244,17 +1244,14 @@ bool game_list_frame::CreatePPUCache(const game_info& game) Emu.SetForceBoot(true); Emu.Stop(); Emu.SetForceBoot(true); - const bool success = Emu.BootGame(game->info.path, game->info.serial, true); - if (success) + if (const auto error = Emu.BootGame(game->info.path, game->info.serial, true); error != game_boot_result::no_errors) { - game_list_log.warning("Creating PPU Cache for %s", game->info.path); + game_list_log.error("Could not create PPU Cache for %s, error: %s", game->info.path, error); + return false; } - else - { - game_list_log.error("Could not create PPU Cache for %s", game->info.path); - } - return success; + game_list_log.warning("Creating PPU Cache for %s", game->info.path); + return true; } bool game_list_frame::RemoveCustomConfiguration(const std::string& title_id, game_info game, bool is_interactive) diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index dcb95ec19b..4bec15a576 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -229,6 +229,49 @@ void main_window::OnPlayOrPause() } } +void main_window::show_boot_error(game_boot_result status) +{ + if (status == game_boot_result::no_errors) + { + return; + } + QString message; + switch (status) + { + case game_boot_result::nothing_to_boot: + message = tr("No bootable content was found."); + break; + case game_boot_result::wrong_disc_location: + message = tr("Disc could not be mounted properly. Make sure the disc is not in the dev_hdd0/game folder."); + break; + case game_boot_result::invalid_file_or_folder: + message = tr("The selected file or folder is invalid or corrupted."); + break; + case game_boot_result::install_failed: + message = tr("Additional content could not be installed."); + break; + case game_boot_result::decryption_error: + message = tr("Digital content could not be decrypted. This is usually caused by a missing or invalid license (RAP) file."); + break; + case game_boot_result::file_creation_error: + message = tr("The emulator could not create files required for booting."); + break; + case game_boot_result::generic_error: + default: + message = tr("Unknown error."); + break; + } + const QString link = tr("

For information on how to dump your PS3 games, read the quickstart guide."); + + QMessageBox msg; + msg.setWindowTitle(tr("Boot Failed")); + msg.setIcon(QMessageBox::Critical); + msg.setTextFormat(Qt::RichText); + msg.setStandardButtons(QMessageBox::Ok); + msg.setText(tr("Booting failed: %1 %2").arg(message).arg(link)); + msg.exec(); +} + void main_window::Boot(const std::string& path, const std::string& title_id, bool direct, bool add_only, bool force_global_config) { if (!Emu.IsStopped()) @@ -249,7 +292,12 @@ void main_window::Boot(const std::string& path, const std::string& title_id, boo Emu.SetForceBoot(true); Emu.Stop(); - if (Emu.BootGame(path, title_id, direct, add_only, force_global_config)) + if (const auto error = Emu.BootGame(path, title_id, direct, add_only, force_global_config); error != game_boot_result::no_errors) + { + gui_log.error("Boot failed: %s", path); + show_boot_error(error); + } + else { gui_log.success("Boot successful."); const std::string serial = Emu.GetTitleID().empty() ? "" : "[" + Emu.GetTitleID() + "] "; @@ -258,10 +306,6 @@ void main_window::Boot(const std::string& path, const std::string& title_id, boo AddRecentAction(gui::Recent_Game(qstr(Emu.GetBoot()), qstr(serial + Emu.GetTitle()))); } } - else - { - gui_log.error("Boot failed: %s", path); - } m_gameListFrame->Refresh(true); } @@ -1869,7 +1913,7 @@ void main_window::AddGamesFromDir(const QString& path) const std::string s_path = sstr(path); // search dropped path first or else the direct parent to an elf is wrongly skipped - if (Emu.BootGame(s_path, "", false, true)) + if (const auto error = Emu.BootGame(s_path, "", false, true); error == game_boot_result::no_errors) { gui_log.notice("Returned from game addition by drag and drop: %s", s_path); } @@ -1880,7 +1924,7 @@ void main_window::AddGamesFromDir(const QString& path) { std::string pth = sstr(dir_iter.next()); - if (Emu.BootGame(pth, "", false, true)) + if (const auto error = Emu.BootGame(pth, "", false, true); error == game_boot_result::no_errors) { gui_log.notice("Returned from game addition by drag and drop: %s", pth); } @@ -2023,14 +2067,20 @@ void main_window::dropEvent(QDropEvent* event) m_gameListFrame->Refresh(true); break; case drop_type::drop_game: // import valid games to gamelist (games.yaml) - if (Emu.BootGame(sstr(dropPaths.first()), "", true)) + if (const auto error = Emu.BootGame(sstr(dropPaths.first()), "", true); error != game_boot_result::no_errors) + { + gui_log.error("Boot failed: reason: %s, path: %s", error, sstr(dropPaths.first())); + show_boot_error(error); + } + else { gui_log.success("Elf Boot from drag and drop done: %s", sstr(dropPaths.first())); + m_gameListFrame->Refresh(true); } - m_gameListFrame->Refresh(true); break; case drop_type::drop_rrc: // replay a rsx capture file BootRsxCapture(sstr(dropPaths.first())); + break; default: gui_log.warning("Invalid dropType in gamelist dropEvent"); break; diff --git a/rpcs3/rpcs3qt/main_window.h b/rpcs3/rpcs3qt/main_window.h index f6aefb33c1..07e91e9e1c 100644 --- a/rpcs3/rpcs3qt/main_window.h +++ b/rpcs3/rpcs3qt/main_window.h @@ -21,6 +21,8 @@ class gui_settings; class emu_settings; class persistent_settings; +enum class game_boot_result : u32; + namespace Ui { class main_window; @@ -97,6 +99,7 @@ private Q_SLOTS: void BootGame(); void BootRsxCapture(std::string path = ""); void DecryptSPRXLibraries(); + void show_boot_error(game_boot_result result); void SaveWindowState(); void ConfigureGuiFromSettings(bool configure_all = false);