Add RPCS3/games/ for automatic games detection, support PSN games outside HDD0 (#12982)

* SFO: Do not load PARAM.SFO with illegal TITLE_ID
* Add support for PSN games outside HDD0
* Add RPCS3/games/ for automatic game detection
This commit is contained in:
Elad Ashkenazi 2022-12-02 12:18:07 +02:00 committed by GitHub
parent 8d9dd1d19c
commit ad3ea966cb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 135 additions and 22 deletions

View file

@ -374,7 +374,10 @@ void Emulator::Init(bool add_only)
if (!fs::create_path(path))
{
sys_log.fatal("Failed to create path: %s (%s)", path, fs::g_tls_error);
return false;
}
return true;
};
const std::string save_path = dev_hdd0 + "home/" + m_usr + "/savedata/";
@ -419,6 +422,11 @@ void Emulator::Init(bool add_only)
make_path_verbose(fs::get_config_dir() + "sounds/");
make_path_verbose(patch_engine::get_patches_path());
if (const std::string games_common = fs::get_config_dir() + "/games"; make_path_verbose(games_common))
{
fs::write_file(user_path + "/Disc Games Can Be Put Here For Automatic Detection.txt", fs::create + fs::excl + fs::write, ""s);
}
if (add_only)
{
// We don't need to initialize the rest if we only add games
@ -961,6 +969,21 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool
{
m_path = rpcs3::utils::get_hdd0_dir();
m_path += std::string_view(argv[0]).substr(9);
constexpr auto game0_path = "/dev_hdd0/game/"sv;
if (argv[0].starts_with(game0_path) && !fs::is_file(vfs::get(argv[0])))
{
std::string title_id = argv[0].substr(game0_path.size());
title_id = title_id.substr(0, title_id.find_last_not_of('/'));
// Try to load game directory from list if available
if (auto node = (title_id.empty() ? YAML::Node{} : games[title_id]))
{
disc = node.Scalar();
m_path = disc + argv[0].substr(game0_path.size() + title_id.size());
}
}
}
else if (argv[0].starts_with("/dev_flash"sv))
{
@ -1332,7 +1355,6 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool
// Detect boot location
const std::string hdd0_game = vfs::get("/dev_hdd0/game/");
const std::string hdd0_disc = vfs::get("/dev_hdd0/disc/");
const bool from_hdd0_game = IsPathInsideDir(m_path, hdd0_game);
const bool from_dev_flash = IsPathInsideDir(m_path, g_cfg_vfs.get_dev_flash());
@ -1358,6 +1380,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool
// Booting disc game from wrong location
sys_log.error("Disc game %s found at invalid location /dev_hdd0/game/", m_title_id);
const std::string hdd0_disc = vfs::get("/dev_hdd0/disc/");
const std::string dst_dir = hdd0_disc + sfb_dir.substr(hdd0_game.size());
// Move and retry from correct location
@ -1483,6 +1506,23 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool
else if (m_cat != "DG" && m_cat != "GD")
{
// Don't need /dev_bdvd
if (!m_title_id.empty() && !from_hdd0_game && m_cat == "HG")
{
// Add HG games not in HDD0 to games.yml
games[m_title_id] = m_sfo_dir;
YAML::Emitter out;
out << games;
fs::pending_file temp(fs::get_config_dir() + "/games.yml");
if (!temp.file || temp.file.write(out.c_str(), out.size()), !temp.commit())
{
sys_log.error("Failed to save HG game location of title '%s' (%s)", m_title_id, fs::g_tls_error);
}
vfs::mount("/dev_hdd0/game/" + m_title_id, m_sfo_dir + '/');
}
}
else if (m_cat == "DG" && from_hdd0_game && disc.empty())
{
@ -1812,6 +1852,12 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool
argv[0] = "/dev_flash" + resolved_path.substr(GetCallbacks().resolve_path(g_cfg_vfs.get_dev_flash()).size());
m_dir = fs::get_parent_dir(argv[0]) + '/';
}
else if (!m_title_id.empty() && m_cat == "HG")
{
m_dir = "/dev_hdd0/game/" + m_title_id + '/';
argv[0] = m_dir + unescape(resolved_path.substr(GetCallbacks().resolve_path(m_sfo_dir).size()));
sys_log.notice("Boot path: %s", m_dir);
}
else if (g_cfg.vfs.host_root)
{
// For homebrew
@ -2899,6 +2945,61 @@ std::set<std::string> Emulator::GetGameDirs() const
return dirs;
}
void Emulator::AddGamesFromDir(const std::string& path)
{
if (!IsStopped())
return;
const std::string games_yml = fs::get_cache_dir() + "/games.yml";
std::string content_before, content_after;
if (fs::file fd{games_yml})
{
content_before = fd.to_string();
}
// search dropped path first or else the direct parent to an elf is wrongly skipped
if (const auto error = BootGame(path, "", false, true); error == game_boot_result::no_errors)
{
if (fs::file fd{games_yml, fs::read + fs::isfile})
{
content_after = fd.to_string();
}
if (content_before != content_after)
{
sys_log.notice("Registered game directory: %s", path);
content_before = content_after;
}
}
// search direct subdirectories, that way we can drop one folder containing all games
for (auto&& dir_entry : fs::dir(path))
{
if (!dir_entry.is_directory || dir_entry.name == "." || dir_entry.name == "..")
{
continue;
}
const std::string dir_path = path + '/' + dir_entry.name;
if (const auto error = BootGame(dir_path, "", false, true); error == game_boot_result::no_errors)
{
if (fs::file fd{games_yml, fs::read + fs::isfile})
{
content_after = fd.to_string();
}
if (content_before != content_after)
{
sys_log.notice("Registered game directory: %s", dir_path);
content_before = content_after;
}
}
}
}
bool Emulator::IsPathInsideDir(std::string_view path, std::string_view dir) const
{
const std::string dir_path = GetCallbacks().resolve_path(dir);