diff --git a/Utilities/StrUtil.h b/Utilities/StrUtil.h index 12448f60cf..35af8fb58f 100644 --- a/Utilities/StrUtil.h +++ b/Utilities/StrUtil.h @@ -24,6 +24,12 @@ inline void strcpy_trunc(char (&dst)[N], const char (&src)[N2]) dst[count] = '\0'; } +template +inline bool ends_with(const std::string& src, const char (&end)[N]) +{ + return src.size() >= N - 1 && src.compare(src.size() - (N - 1), N - 1, end, N - 1) == 0; +} + namespace fmt { std::string replace_first(const std::string& src, const std::string& from, const std::string& to); diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 2908fc74b3..baaa12585e 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -22,6 +22,7 @@ #include "Utilities/StrUtil.h" #include "../Crypto/unself.h" +#include "../Crypto/unpkg.h" #include "yaml-cpp/yaml.h" #include @@ -212,6 +213,7 @@ void Emulator::Init() fs::create_dir(dev_hdd0 + "game/"); fs::create_dir(dev_hdd0 + "game/TEST12345/"); fs::create_dir(dev_hdd0 + "game/TEST12345/USRDIR/"); + fs::create_dir(dev_hdd0 + "game/.locks/"); fs::create_dir(dev_hdd0 + "home/"); fs::create_dir(dev_hdd0 + "home/00000001/"); fs::create_dir(dev_hdd0 + "home/00000001/exdata/"); @@ -223,7 +225,7 @@ void Emulator::Init() fs::create_dir(dev_hdd1 + "game/"); fs::create_path(dev_hdd1); fs::create_path(dev_usb); - + #ifdef WITH_GDB_DEBUGGER fxm::make(); #endif @@ -265,6 +267,51 @@ bool Emulator::BootGame(const std::string& path, bool direct, bool add_only) return false; } +bool Emulator::InstallPkg(const std::string& path) +{ + LOG_SUCCESS(GENERAL, "Installing package: %s", path); + + atomic_t progress(0.); + int int_progress = 0; + { + // Run PKG unpacking asynchronously + scope_thread worker("PKG Installer", [&] + { + if (pkg_install(path, progress)) + { + progress = 1.; + return; + } + + progress = -1.; + }); + + // Wait for the completion + while (std::this_thread::sleep_for(5ms), std::abs(progress) < 1.) + { + // TODO: update unified progress dialog + double pval = progress; + pval < 0 ? pval += 1. : pval; + pval *= 100.; + + if (static_cast(pval) > int_progress) + { + int_progress = static_cast(pval); + LOG_SUCCESS(GENERAL, "... %u%%", int_progress); + } + + m_cb.process_events(); + } + } + + if (progress >= 1.) + { + return true; + } + + return false; +} + std::string Emulator::GetHddDir() { const std::string& emu_dir_ = g_cfg.vfs.emulator_dir; @@ -297,7 +344,7 @@ void Emulator::Load(bool add_only) { Init(); - // Load game list (maps ABCD12345 IDs to /dev_bdvd/ locations) + // Load game list (maps ABCD12345 IDs to /dev_bdvd/ locations) YAML::Node games = YAML::Load(fs::file{fs::get_config_dir() + "/games.yml", fs::read + fs::create}.to_string()); if (!games.IsMap()) @@ -453,7 +500,8 @@ void Emulator::Load(bool add_only) } else if (!Emu.disc.empty()) { - vfs::mount("dev_bdvd", Emu.disc); + bdvd_dir = Emu.disc; + vfs::mount("dev_bdvd", bdvd_dir); LOG_NOTICE(LOADER, "Disk: %s", vfs::get("/dev_bdvd")); } else if (_cat == "DG" || _cat == "GD") @@ -468,6 +516,73 @@ void Emulator::Load(bool add_only) return; } + // Install PKGDIR, INSDIR, PS3_EXTRA + if (!bdvd_dir.empty()) + { + const std::string ins_dir = vfs::get("/dev_bdvd/PS3_GAME/INSDIR/"); + const std::string pkg_dir = vfs::get("/dev_bdvd/PS3_GAME/PKGDIR/"); + const std::string extra_dir = vfs::get("/dev_bdvd/PS3_GAME/PS3_EXTRA/"); + fs::file lock_file; + + if (fs::is_dir(ins_dir) || fs::is_dir(pkg_dir) || fs::is_dir(extra_dir)) + { + // Create lock file to prevent double installation + lock_file.open(hdd0_game + ".locks/" + m_title_id, fs::read + fs::create + fs::excl); + } + + if (lock_file && fs::is_dir(ins_dir)) + { + LOG_NOTICE(LOADER, "Found INSDIR: %s", ins_dir); + + for (auto&& entry : fs::dir{ins_dir}) + { + if (!entry.is_directory && ends_with(entry.name, ".PKG") && !InstallPkg(ins_dir + entry.name)) + { + LOG_ERROR(LOADER, "Failed to install /dev_bdvd/PS3_GAME/INSDIR/%s", entry.name); + return; + } + } + } + + if (lock_file && fs::is_dir(pkg_dir)) + { + LOG_NOTICE(LOADER, "Found PKGDIR: %s", pkg_dir); + + for (auto&& entry : fs::dir{pkg_dir}) + { + if (entry.is_directory && entry.name.compare(0, 3, "PKG", 3) == 0) + { + const std::string pkg_file = pkg_dir + entry.name + "/INSTALL.PKG"; + + if (fs::is_file(pkg_file) && !InstallPkg(pkg_file)) + { + LOG_ERROR(LOADER, "Failed to install /dev_bdvd/PS3_GAME/PKGDIR/%s/INSTALL.PKG", entry.name); + return; + } + } + } + } + + if (lock_file && fs::is_dir(extra_dir)) + { + LOG_NOTICE(LOADER, "Found PS3_EXTRA: %s", extra_dir); + + for (auto&& entry : fs::dir{extra_dir}) + { + if (entry.is_directory && entry.name[0] == 'D') + { + const std::string pkg_file = extra_dir + entry.name + "/DATA000.PKG"; + + if (fs::is_file(pkg_file) && !InstallPkg(pkg_file)) + { + LOG_ERROR(LOADER, "Failed to install /dev_bdvd/PS3_GAME/PKGDIR/%s/DATA000.PKG", entry.name); + return; + } + } + } + } + } + // Check game updates const std::string hdd0_boot = hdd0_game + m_title_id + "/USRDIR/EBOOT.BIN"; diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index dbec6ce2df..fa45db4970 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -245,6 +245,7 @@ public: } bool BootGame(const std::string& path, bool direct = false, bool add_only = false); + bool InstallPkg(const std::string& path); static std::string GetHddDir(); static std::string GetLibDir(); @@ -372,7 +373,7 @@ struct cfg_root : cfg::node cfg::_int<2, 128> frames{this, "Buffer Count", 32}; } audio{this}; - + struct node_io : cfg::node { node_io(cfg::node* _this) : cfg::node(_this, "Input/Output") {} @@ -384,7 +385,7 @@ struct cfg_root : cfg::node cfg::_enum camera_type{this, "Camera type", fake_camera_type::unknown}; } io{this}; - + struct node_sys : cfg::node { node_sys(cfg::node* _this) : cfg::node(_this, "System") {} @@ -392,7 +393,7 @@ struct cfg_root : cfg::node cfg::_enum language{this, "Language"}; } sys{this}; - + struct node_net : cfg::node { node_net(cfg::node* _this) : cfg::node(_this, "Net") {} @@ -405,7 +406,7 @@ struct cfg_root : cfg::node struct node_misc : cfg::node { node_misc(cfg::node* _this) : cfg::node(_this, "Miscellaneous") {} - + cfg::_bool autostart{this, "Automatically start games after boot", true}; cfg::_bool autoexit{this, "Exit RPCS3 when process finishes"}; cfg::_bool start_fullscreen{ this, "Start games in fullscreen mode" }; diff --git a/rpcs3/rpcs3_app.cpp b/rpcs3/rpcs3_app.cpp index 91cd545c6e..f838f37153 100644 --- a/rpcs3/rpcs3_app.cpp +++ b/rpcs3/rpcs3_app.cpp @@ -105,7 +105,7 @@ void rpcs3_app::InitializeCallbacks() quit(); }; callbacks.call_after = [=](std::function func) - { + { RequestCallAfter(std::move(func)); }; @@ -176,7 +176,7 @@ void rpcs3_app::InitializeCallbacks() gameWindow = ret; return std::unique_ptr(ret); } - case video_renderer::opengl: + case video_renderer::opengl: { gl_gs_frame* ret = new gl_gs_frame(w, h, RPCS3MainWin->GetAppIcon(), disableMouse); gameWindow = ret; diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 39103ec1b5..496f953c0f 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -38,6 +38,7 @@ #include "Loader/PUP.h" #include "Loader/TAR.h" +#include "Loader/PSF.h" #include "Utilities/Thread.h" #include "Utilities/StrUtil.h" @@ -67,7 +68,7 @@ auto Pause = []() else if (!Emu.GetBoot().empty()) Emu.Load(); }; -/* An init method is used so that RPCS3App can create the necessary connects before calling init (specifically the stylesheet connect). +/* An init method is used so that RPCS3App can create the necessary connects before calling init (specifically the stylesheet connect). * Simplifies logic a bit. */ void main_window::Init() @@ -108,7 +109,7 @@ void main_window::Init() Q_EMIT RequestGlobalStylesheetChange(guiSettings->GetCurrentStylesheetPath()); ConfigureGuiFromSettings(true); - + if (!utils::has_ssse3()) { QMessageBox::critical(this, "SSSE3 Error (with three S, not two)", @@ -252,7 +253,7 @@ void main_window::BootElf() "SELF files (EBOOT.BIN *.self);;" "BOOT files (*BOOT.BIN);;" "BIN files (*.bin);;" - "All files (*.*)"), + "All files (*.*)"), Q_NULLPTR, QFileDialog::DontResolveSymlinks); if (filePath == NULL) @@ -603,7 +604,7 @@ void main_window::DecryptSPRXLibraries() LOG_NOTICE(GENERAL, "Finished decrypting all SPRX libraries."); } -/** Needed so that when a backup occurs of window state in guisettings, the state is current. +/** Needed so that when a backup occurs of window state in guisettings, the state is current. * Also, so that on close, the window state is preserved. */ void main_window::SaveWindowState() @@ -622,7 +623,7 @@ void main_window::SaveWindowState() void main_window::RepaintThumbnailIcons() { QColor newColor = gui::get_Label_Color("thumbnail_icon_color"); - + auto icon = [&newColor](const QString& path) { return gui_settings::colorizedIcon(QPixmap::fromImage(gui_settings::GetOpaqueImageArea(path)), gui::mw_tool_icon_color, newColor); @@ -918,7 +919,7 @@ QAction* main_window::CreateRecentAction(const q_string_pair& entry, const uint& act->setData(entry.first); act->setToolTip(entry.second); act->setShortcut(tr("Ctrl+%1").arg(sc_idx)); - + // truncate if too long if (shown_name.length() > 60) { @@ -977,7 +978,7 @@ void main_window::AddRecentAction(const q_string_pair& entry) m_rg_entries.prepend(entry); m_recentGameActs.prepend(act); } - + // refill menu with actions for (int i = 0; i < m_recentGameActs.count(); i++) { @@ -1040,7 +1041,7 @@ void main_window::CreateActions() ui->toolbar_start->setEnabled(false); ui->toolbar_stop->setEnabled(false); - m_categoryVisibleActGroup = new QActionGroup(this); + m_categoryVisibleActGroup = new QActionGroup(this); m_categoryVisibleActGroup->addAction(ui->showCatHDDGameAct); m_categoryVisibleActGroup->addAction(ui->showCatDiscGameAct); m_categoryVisibleActGroup->addAction(ui->showCatHomeAct); @@ -1530,7 +1531,7 @@ void main_window::mouseDoubleClickEvent(QMouseEvent *event) } } -/** Override the Qt close event to have the emulator stop and the application die. May add a warning dialog in future. +/** Override the Qt close event to have the emulator stop and the application die. May add a warning dialog in future. */ void main_window::closeEvent(QCloseEvent* closeEvent) {