cellGame: implement disc change callbacks

This commit is contained in:
Megamouse 2022-06-21 22:13:22 +02:00
parent bc3a899acf
commit 87762a9b17
19 changed files with 728 additions and 86 deletions

View file

@ -105,6 +105,7 @@ target_sources(rpcs3_emu PRIVATE
# Loader
target_sources(rpcs3_emu PRIVATE
../Loader/disc.cpp
../Loader/ELF.cpp
../Loader/mself.cpp
../Loader/PSF.cpp

View file

@ -199,6 +199,162 @@ void fmt_class_string<content_permission::check_mode>::format(std::string& out,
});
}
template<>
void fmt_class_string<disc_change_manager::eject_state>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](auto error)
{
switch (error)
{
STR_CASE(disc_change_manager::eject_state::inserted);
STR_CASE(disc_change_manager::eject_state::ejected);
STR_CASE(disc_change_manager::eject_state::busy);
}
return unknown;
});
}
disc_change_manager::disc_change_manager()
{
Emu.GetCallbacks().enable_disc_eject(false);
Emu.GetCallbacks().enable_disc_insert(false);
}
disc_change_manager::~disc_change_manager()
{
Emu.GetCallbacks().enable_disc_eject(false);
Emu.GetCallbacks().enable_disc_insert(false);
}
error_code disc_change_manager::register_callbacks(vm::ptr<CellGameDiscEjectCallback> func_eject, vm::ptr<CellGameDiscInsertCallback> func_insert)
{
if (!func_eject || !func_insert)
{
return CELL_GAME_ERROR_PARAM;
}
std::lock_guard lock(mtx);
eject_callback = func_eject;
insert_callback = func_insert;
Emu.GetCallbacks().enable_disc_eject(true);
Emu.GetCallbacks().enable_disc_insert(false);
return CELL_OK;
}
error_code disc_change_manager::unregister_callbacks()
{
const auto unregister = [this]() -> void
{
eject_callback = vm::null;
insert_callback = vm::null;
Emu.GetCallbacks().enable_disc_eject(false);
Emu.GetCallbacks().enable_disc_insert(false);
};
if (is_inserting)
{
// NOTE: The insert_callback is known to call cellGameUnregisterDiscChangeCallback.
// So we keep it out of the mutex lock until it proves to be an issue.
unregister();
}
else
{
std::lock_guard lock(mtx);
unregister();
}
return CELL_OK;
}
void disc_change_manager::eject_disc()
{
cellGame.notice("Ejecting disc...");
std::lock_guard lock(mtx);
if (state != eject_state::inserted)
{
cellGame.fatal("Can not eject disc in the current state. (state=%s)", state.load());
return;
}
state = eject_state::busy;
Emu.GetCallbacks().enable_disc_eject(false);
ensure(eject_callback);
sysutil_register_cb([](ppu_thread& cb_ppu) -> s32
{
auto& dcm = g_fxo->get<disc_change_manager>();
std::lock_guard lock(dcm.mtx);
cellGame.notice("Executing eject_callback...");
dcm.eject_callback(cb_ppu);
ensure(vfs::unmount("/dev_bdvd"));
ensure(vfs::unmount("/dev_ps2disc"));
dcm.state = eject_state::ejected;
Emu.GetCallbacks().enable_disc_insert(true);
return CELL_OK;
});
}
void disc_change_manager::insert_disc(u32 disc_type, std::string title_id)
{
cellGame.notice("Inserting disc...");
std::lock_guard lock(mtx);
if (state != eject_state::ejected)
{
cellGame.fatal("Can not insert disc in the current state. (state=%s)", state.load());
return;
}
state = eject_state::busy;
Emu.GetCallbacks().enable_disc_insert(false);
ensure(insert_callback);
is_inserting = true;
sysutil_register_cb([disc_type, title_id = std::move(title_id)](ppu_thread& cb_ppu) -> s32
{
auto& dcm = g_fxo->get<disc_change_manager>();
std::lock_guard lock(dcm.mtx);
if (disc_type == CELL_GAME_DISCTYPE_PS3)
{
vm::var<char[]> _title_id = vm::make_str(title_id);
cellGame.notice("Executing insert_callback for title '%s' with disc_type %d...", _title_id.get_ptr(), disc_type);
dcm.insert_callback(cb_ppu, disc_type, _title_id);
}
else
{
cellGame.notice("Executing insert_callback with disc_type %d...", disc_type);
dcm.insert_callback(cb_ppu, disc_type, vm::null);
}
dcm.state = eject_state::inserted;
// Re-enable disc ejection only if the callback is still registered
Emu.GetCallbacks().enable_disc_eject(!!dcm.eject_callback);
dcm.is_inserting = false;
return CELL_OK;
});
}
error_code cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr<char> dirName, u32 errDialog, vm::ptr<CellHddGameStatCallback> funcStat, u32 container)
{
cellGame.warning("cellHddGameCheck(version=%d, dirName=%s, errDialog=%d, funcStat=*0x%x, container=%d)", version, dirName, errDialog, funcStat, container);
@ -1566,30 +1722,30 @@ error_code cellDiscGameGetBootDiscInfo(vm::ptr<CellDiscGameSystemFileParam> getP
error_code cellDiscGameRegisterDiscChangeCallback(vm::ptr<CellDiscGameDiscEjectCallback> funcEject, vm::ptr<CellDiscGameDiscInsertCallback> funcInsert)
{
cellGame.todo("cellDiscGameRegisterDiscChangeCallback(funcEject=*0x%x, funcInsert=*0x%x)", funcEject, funcInsert);
cellGame.warning("cellDiscGameRegisterDiscChangeCallback(funcEject=*0x%x, funcInsert=*0x%x)", funcEject, funcInsert);
return CELL_OK;
return g_fxo->get<disc_change_manager>().register_callbacks(funcEject, funcInsert);
}
error_code cellDiscGameUnregisterDiscChangeCallback()
{
cellGame.todo("cellDiscGameUnregisterDiscChangeCallback()");
cellGame.warning("cellDiscGameUnregisterDiscChangeCallback()");
return CELL_OK;
return g_fxo->get<disc_change_manager>().unregister_callbacks();
}
error_code cellGameRegisterDiscChangeCallback(vm::ptr<CellGameDiscEjectCallback> funcEject, vm::ptr<CellGameDiscInsertCallback> funcInsert)
{
cellGame.todo("cellGameRegisterDiscChangeCallback(funcEject=*0x%x, funcInsert=*0x%x)", funcEject, funcInsert);
cellGame.warning("cellGameRegisterDiscChangeCallback(funcEject=*0x%x, funcInsert=*0x%x)", funcEject, funcInsert);
return CELL_OK;
return g_fxo->get<disc_change_manager>().register_callbacks(funcEject, funcInsert);
}
error_code cellGameUnregisterDiscChangeCallback()
{
cellGame.todo("cellGameUnregisterDiscChangeCallback()");
cellGame.warning("cellGameUnregisterDiscChangeCallback()");
return CELL_OK;
return g_fxo->get<disc_change_manager>().unregister_callbacks();
}
void cellSysutil_GameData_init()

View file

@ -318,5 +318,30 @@ typedef void(CellHddGameStatCallback)(vm::ptr<CellHddGameCBResult> cbResult, vm:
typedef s32(CellGameThemeInstallCallback)(u32 fileOffset, u32 readSize, vm::ptr<void> buf);
typedef void(CellGameDiscEjectCallback)();
typedef void(CellGameDiscInsertCallback)(u32 discType, vm::ptr<char> titleId);
typedef void(CellDiscGameDiscEjectCallback)();
typedef void(CellDiscGameDiscInsertCallback)(u32 discType, vm::ptr<char> titleId);
using CellDiscGameDiscEjectCallback = CellGameDiscEjectCallback;
using CellDiscGameDiscInsertCallback = CellGameDiscInsertCallback;
struct disc_change_manager
{
disc_change_manager();
virtual ~disc_change_manager();
std::mutex mtx;
atomic_t<bool> is_inserting = false;
vm::ptr<CellGameDiscEjectCallback> eject_callback = vm::null;
vm::ptr<CellGameDiscInsertCallback> insert_callback = vm::null;
enum class eject_state
{
inserted,
ejected,
busy
};
atomic_t<eject_state> state = eject_state::inserted;
error_code register_callbacks(vm::ptr<CellGameDiscEjectCallback> func_eject, vm::ptr<CellGameDiscInsertCallback> func_insert);
error_code unregister_callbacks();
void eject_disc();
void insert_disc(u32 disc_type, std::string title_id);
};

View file

@ -28,6 +28,7 @@
#include "Loader/PSF.h"
#include "Loader/ELF.h"
#include "Loader/disc.h"
#include "Utilities/StrUtil.h"
@ -543,6 +544,35 @@ bool Emulator::BootRsxCapture(const std::string& path)
return true;
}
game_boot_result Emulator::GetElfPathFromDir(std::string& elf_path, const std::string& path)
{
if (!fs::is_dir(path))
{
return game_boot_result::invalid_file_or_folder;
}
static const char* boot_list[] =
{
"/EBOOT.BIN",
"/USRDIR/EBOOT.BIN",
"/USRDIR/ISO.BIN.EDAT",
"/PS3_GAME/USRDIR/EBOOT.BIN",
};
for (std::string elf : boot_list)
{
elf = path + elf;
if (fs::is_file(elf))
{
elf_path = elf;
return game_boot_result::no_errors;
}
}
return game_boot_result::invalid_file_or_folder;
}
game_boot_result Emulator::BootGame(const std::string& path, const std::string& title_id, bool direct, bool add_only, cfg_mode config_mode, const std::string& config_path)
{
if (!fs::exists(path))
@ -563,24 +593,12 @@ game_boot_result Emulator::BootGame(const std::string& path, const std::string&
game_boot_result result = game_boot_result::nothing_to_boot;
static const char* boot_list[] =
std::string elf;
if (const game_boot_result res = GetElfPathFromDir(elf, path); res == game_boot_result::no_errors)
{
"/EBOOT.BIN",
"/USRDIR/EBOOT.BIN",
"/USRDIR/ISO.BIN.EDAT",
"/PS3_GAME/USRDIR/EBOOT.BIN",
};
for (std::string elf : boot_list)
{
elf = path + elf;
if (fs::is_file(elf))
{
m_path = elf;
result = Load(title_id, add_only);
break;
}
ensure(!elf.empty());
m_path = elf;
result = Load(title_id, add_only);
}
if (add_only)
@ -1012,62 +1030,26 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool
// Mount /dev_bdvd/ if necessary
if (bdvd_dir.empty() && disc.empty())
{
// Find disc directory by searching a valid PS3_DISC.SFB closest to root directory
std::string sfb_dir, main_dir;
std::string_view main_dir_name;
std::string sfb_dir;
GetBdvdDir(bdvd_dir, sfb_dir, m_game_dir, elf_dir);
for (std::string search_dir = elf_dir;;)
if (!sfb_dir.empty() && from_hdd0_game)
{
std::string parent_dir = fs::get_parent_dir(search_dir);
// Booting disc game from wrong location
sys_log.error("Disc game %s found at invalid location /dev_hdd0/game/", m_title_id);
if (parent_dir.size() == search_dir.size())
const std::string dst_dir = hdd0_disc + sfb_dir.substr(hdd0_game.size());
// Move and retry from correct location
if (fs::create_path(fs::get_parent_dir(dst_dir)) && fs::rename(sfb_dir, dst_dir, false))
{
// Keep looking until root directory is reached
break;
sys_log.success("Disc game %s moved to special location /dev_hdd0/disc/", m_title_id);
m_path = hdd0_disc + m_path.substr(hdd0_game.size());
return Load(m_title_id, add_only);
}
if (fs::file sfb_file{parent_dir + "/PS3_DISC.SFB", fs::read + fs::isfile}; sfb_file && sfb_file.size() >= 4 && sfb_file.read<u32>() == ".SFB"_u32)
{
main_dir_name = std::string_view{search_dir}.substr(search_dir.find_last_of(fs::delim) + 1);
if (main_dir_name == "PS3_GAME" || std::regex_match(main_dir_name.begin(), main_dir_name.end(), std::regex("^PS3_GM[[:digit:]]{2}$")))
{
// Remember valid disc directory
main_dir = search_dir;
sfb_dir = parent_dir;
main_dir_name = std::string_view{main_dir}.substr(main_dir.find_last_of(fs::delim) + 1);
}
}
search_dir = std::move(parent_dir);
}
if (!sfb_dir.empty())
{
{
if (from_hdd0_game)
{
// 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 dst_dir = hdd0_disc + sfb_dir.substr(hdd0_game.size());
// Move and retry from correct location
if (fs::create_path(fs::get_parent_dir(dst_dir)) && fs::rename(sfb_dir, dst_dir, false))
{
sys_log.success("Disc game %s moved to special location /dev_hdd0/disc/", m_title_id);
return m_path = hdd0_disc + m_path.substr(hdd0_game.size()), Load(m_title_id, add_only);
}
sys_log.error("Failed to move disc game %s to /dev_hdd0/disc/ (%s)", m_title_id, fs::g_tls_error);
return game_boot_result::wrong_disc_location;
}
bdvd_dir = sfb_dir + "/";
// Set game dir
m_game_dir = std::string{main_dir_name};
}
sys_log.error("Failed to move disc game %s to /dev_hdd0/disc/ (%s)", m_title_id, fs::g_tls_error);
return game_boot_result::wrong_disc_location;
}
}
@ -1315,7 +1297,8 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool
{
// Booting game update
sys_log.success("Updates found at /dev_hdd0/game/%s/", m_title_id);
return m_path = hdd0_boot, Load(m_title_id, false, true);
m_path = hdd0_boot;
return Load(m_title_id, false, true);
}
if (!disc_psf_obj.empty())
@ -2257,4 +2240,153 @@ const std::string Emulator::GetSfoDir(bool prefer_disc_sfo) const
return m_sfo_dir;
}
void Emulator::GetBdvdDir(std::string& bdvd_dir, std::string& sfb_dir, std::string& game_dir, const std::string& elf_dir)
{
// Find disc directory by searching a valid PS3_DISC.SFB closest to root directory
std::string main_dir;
std::string_view main_dir_name;
for (std::string search_dir = elf_dir;;)
{
std::string parent_dir = fs::get_parent_dir(search_dir);
if (parent_dir.size() == search_dir.size())
{
// Keep looking until root directory is reached
break;
}
if (fs::file sfb_file{parent_dir + "/PS3_DISC.SFB", fs::read + fs::isfile}; sfb_file && sfb_file.size() >= 4 && sfb_file.read<u32>() == ".SFB"_u32)
{
main_dir_name = std::string_view{search_dir}.substr(search_dir.find_last_of(fs::delim) + 1);
if (main_dir_name == "PS3_GAME" || std::regex_match(main_dir_name.begin(), main_dir_name.end(), std::regex("^PS3_GM[[:digit:]]{2}$")))
{
// Remember valid disc directory
main_dir = search_dir;
sfb_dir = parent_dir;
main_dir_name = std::string_view{main_dir}.substr(main_dir.find_last_of(fs::delim) + 1);
}
}
search_dir = std::move(parent_dir);
}
if (!sfb_dir.empty())
{
bdvd_dir = sfb_dir + "/";
game_dir = std::string{main_dir_name};
}
}
void Emulator::EjectDisc()
{
if (!Emu.IsRunning())
{
sys_log.error("Can not eject disc if the Emulator is not running!");
return;
}
if (vfs::get("/dev_bdvd").empty())
{
sys_log.error("Can not eject disc if dev_bdvd is not mounted!");
return;
}
sys_log.notice("Ejecting disc...");
m_sfo_dir.clear();
if (g_fxo->is_init<disc_change_manager>())
{
g_fxo->get<disc_change_manager>().eject_disc();
}
}
game_boot_result Emulator::InsertDisc(const std::string& path)
{
if (!Emu.IsRunning())
{
sys_log.error("Can not insert disc if the Emulator is not running!");
return game_boot_result::generic_error;
}
sys_log.notice("Inserting disc... (path='%s')", path);
const std::string hdd0_game = vfs::get("/dev_hdd0/game/");
const bool from_hdd0_game = IsPathInsideDir(path, hdd0_game);
if (from_hdd0_game)
{
sys_log.error("Inserting disc failed: Can not mount discs from '/dev_hdd0/game/'. (path='%s')", path);
return game_boot_result::wrong_disc_location;
}
std::string disc_root;
std::string ps3_game_dir;
const disc::disc_type disc_type = disc::get_disc_type(path, disc_root, ps3_game_dir);
if (disc_type == disc::disc_type::invalid)
{
sys_log.error("Inserting disc failed: not a disc (path='%s')", path);
return game_boot_result::wrong_disc_location;
}
ensure(!disc_root.empty());
u32 type = CELL_GAME_DISCTYPE_OTHER;
std::string title_id;
if (disc_type == disc::disc_type::ps3)
{
type = CELL_GAME_DISCTYPE_PS3;
// Double check PARAM.SFO
const std::string sfo_dir = rpcs3::utils::get_sfo_dir_from_game_path(disc_root);
const psf::registry _psf = psf::load_object(fs::file(sfo_dir + "/PARAM.SFO"));
if (_psf.empty())
{
sys_log.error("Inserting disc failed: Corrupted PARAM.SFO found! (path='%s/PARAM.SFO')", sfo_dir);
return game_boot_result::invalid_file_or_folder;
}
title_id = std::string(psf::get_string(_psf, "TITLE_ID"));
if (title_id.empty())
{
sys_log.error("Inserting disc failed: Corrupted PARAM.SFO found! TITLE_ID empty (path='%s/PARAM.SFO')", sfo_dir);
return game_boot_result::invalid_file_or_folder;
}
m_sfo_dir = sfo_dir;
m_game_dir = ps3_game_dir;
sys_log.notice("New sfo dir: %s", m_sfo_dir);
sys_log.notice("New game dir: %s", m_game_dir);
ensure(vfs::mount("/dev_bdvd", disc_root));
ensure(vfs::mount("/dev_bdvd/PS3_GAME", disc_root + m_game_dir + "/"));
}
else if (disc_type == disc::disc_type::ps2)
{
type = CELL_GAME_DISCTYPE_PS2;
ensure(vfs::mount("/dev_ps2disc", disc_root));
}
else
{
// TODO: find out where other discs are mounted
sys_log.todo("Mounting non-ps2/ps3 disc in dev_bdvd. Is this correct? (path='%s')", disc_root);
ensure(vfs::mount("/dev_bdvd", disc_root));
}
if (g_fxo->is_init<disc_change_manager>())
{
g_fxo->get<disc_change_manager>().insert_disc(type, std::move(title_id));
}
return game_boot_result::no_errors;
}
Emulator Emu;

View file

@ -63,6 +63,8 @@ struct EmuCallbacks
std::function<void()> on_stop;
std::function<void()> on_ready;
std::function<bool()> on_missing_fw;
std::function<void(bool enabled)> enable_disc_eject;
std::function<void(bool enabled)> enable_disc_insert;
std::function<bool(bool, std::function<void()>)> try_to_quit; // (force_quit, on_exit) Try to close RPCS3
std::function<void(s32, s32)> handle_taskbar_progress; // (type, value) type: 0 for reset, 1 for increment, 2 for set_limit, 3 for set_value
std::function<void()> init_kb_handler;
@ -296,6 +298,12 @@ public:
// Check if path is inside the specified directory
bool IsPathInsideDir(std::string_view path, std::string_view dir) const;
void EjectDisc();
game_boot_result InsertDisc(const std::string& path);
static game_boot_result GetElfPathFromDir(std::string& elf_path, const std::string& path);
static void GetBdvdDir(std::string& bdvd_dir, std::string& sfb_dir, std::string& game_dir, const std::string& elf_dir);
};
extern Emulator Emu;

View file

@ -35,6 +35,13 @@ struct vfs_manager
bool vfs::mount(std::string_view vpath, std::string_view path)
{
if (vpath.empty())
{
// Empty relative path, should set relative path base; unsupported
vfs_log.error("Cannot mount empty path to \"%s\"", path);
return false;
}
// Workaround
g_fxo->need<vfs_manager>();
@ -44,13 +51,6 @@ bool vfs::mount(std::string_view vpath, std::string_view path)
std::lock_guard lock(table.mutex);
if (vpath.empty())
{
// Empty relative path, should set relative path base; unsupported
vfs_log.error("Cannot mount empty path to \"%s\"", path);
return false;
}
const std::string_view vpath_backup = vpath;
for (std::vector<vfs_directory*> list{&table.root};;)
@ -116,6 +116,69 @@ bool vfs::mount(std::string_view vpath, std::string_view path)
}
}
bool vfs::unmount(std::string_view vpath)
{
if (vpath.empty())
{
vfs_log.error("Cannot unmount empty path");
return false;
}
const std::vector<std::string> entry_list = fmt::split(vpath, {"/"});
if (entry_list.empty())
{
vfs_log.error("Cannot unmount path: '%s'", vpath);
return false;
}
vfs_log.notice("About to unmount '%s'", vpath);
// Workaround
g_fxo->need<vfs_manager>();
auto& table = g_fxo->get<vfs_manager>();
std::lock_guard lock(table.mutex);
// Search entry recursively and remove it (including all children)
std::function<void(vfs_directory&, int)> unmount_children;
unmount_children = [&entry_list, &unmount_children](vfs_directory& dir, usz depth) -> void
{
if (depth >= entry_list.size())
{
return;
}
// Get the current name based on the depth
const std::string& name = entry_list.at(depth);
// Go through all children of this node
for (auto it = dir.dirs.begin(); it != dir.dirs.end();)
{
// Find the matching node
if (it->first == name)
{
// Remove the matching node if we reached the maximum depth
if (depth + 1 == entry_list.size())
{
vfs_log.notice("Unmounting '%s' = '%s'", it->first, it->second.path);
it = dir.dirs.erase(it);
continue;
}
// Otherwise continue searching in the next level of depth
unmount_children(it->second, depth + 1);
}
++it;
}
};
unmount_children(table.root, 0);
return true;
}
std::string vfs::get(std::string_view vpath, std::vector<std::string>* out_dir, std::string* out_path)
{
auto& table = g_fxo->get<vfs_manager>();

View file

@ -11,6 +11,9 @@ namespace vfs
// Mount VFS device
bool mount(std::string_view vpath, std::string_view path);
// Unmount VFS device
bool unmount(std::string_view vpath);
// Convert VFS path to fs path, optionally listing directories mounted in it
std::string get(std::string_view vpath, std::vector<std::string>* out_dir = nullptr, std::string* out_path = nullptr);

149
rpcs3/Loader/disc.cpp Normal file
View file

@ -0,0 +1,149 @@
#include "stdafx.h"
#include "disc.h"
#include "PSF.h"
#include "util/logs.hpp"
#include "Utilities/StrUtil.h"
#include "Emu/System.h"
#include "Emu/system_utils.hpp"
LOG_CHANNEL(disc_log, "DISC");
namespace disc
{
disc_type get_disc_type(std::string path, std::string& disc_root, std::string& ps3_game_dir)
{
disc_type type = disc_type::unknown;
disc_root.clear();
ps3_game_dir.clear();
if (path.empty())
{
disc_log.error("Can not determine disc type. Path is empty.");
return disc_type::invalid;
}
if (!fs::is_dir(path))
{
disc_log.error("Can not determine disc type. Path not a directory: '%s'", path);
return disc_type::invalid;
}
// Check for PS3 game first.
std::string elf_path;
if (const game_boot_result result = Emulator::GetElfPathFromDir(elf_path, path);
result == game_boot_result::no_errors)
{
// Every error past this point is considered a corrupt disc.
std::string sfb_dir;
const std::string elf_dir = fs::get_parent_dir(elf_path);
Emulator::GetBdvdDir(disc_root, sfb_dir, ps3_game_dir, elf_dir);
if (!fs::is_dir(disc_root) || !fs::is_dir(sfb_dir) || ps3_game_dir.empty())
{
disc_log.error("Not a PS3 disc: invalid folder (bdvd_dir='%s', sfb_dir='%s', game_dir='%s')", disc_root, sfb_dir, ps3_game_dir);
return disc_type::invalid;
}
// Load PARAM.SFO
const std::string sfo_dir = rpcs3::utils::get_sfo_dir_from_game_path(elf_dir + "/../", "");
const psf::registry _psf = psf::load_object(fs::file(sfo_dir + "/PARAM.SFO"));
if (_psf.empty())
{
disc_log.error("Not a PS3 disc: Corrupted PARAM.SFO found! (path='%s/PARAM.SFO')", sfo_dir);
return disc_type::invalid;
}
const std::string cat = std::string(psf::get_string(_psf, "CATEGORY"));
if (cat != "DG")
{
disc_log.error("Not a PS3 disc: Wrong category '%s'.", cat);
return disc_type::invalid;
}
return disc_type::ps3;
}
disc_log.notice("Not a PS3 disc: Elf not found. Looking for SYSTEM.CNF... (path='%s')", path);
std::vector<std::string> lines;
// Try to find SYSTEM.CNF
for (std::string search_dir = path;;)
{
if (fs::file file(search_dir + "/SYSTEM.CNF"); file)
{
disc_root = search_dir + "/";
lines = fmt::split(file.to_string(), {"\n"});
break;
}
std::string parent_dir = fs::get_parent_dir(search_dir);
if (parent_dir.size() == search_dir.size())
{
// Keep looking until root directory is reached
disc_log.error("SYSTEM.CNF not found in path: '%s'", path);
return disc_type::invalid;
}
search_dir = std::move(parent_dir);
}
for (usz i = 0; i < lines.size(); i++)
{
const std::string& line = lines[i];
const usz pos = line.find('=');
if (pos == umax)
{
continue;
}
const std::string key = fmt::trim(line.substr(0, pos));
std::string value;
if (pos != (line.size() - 1))
{
value = fmt::trim(line.substr(pos + 1));
}
if (value.empty() && i != (lines.size() - 1) && line.size() != 1)
{
// Some games have a character on the last line of the file, don't print the error in those cases.
disc_log.warning("Unusual or malformed entry in SYSTEM.CNF ignored: %s", line);
continue;
}
if (key == "BOOT2")
{
disc_log.notice("SYSTEM.CNF - Detected PS2 Disc = %s", value);
type = disc_type::ps2;
}
else if (key == "BOOT")
{
disc_log.notice("SYSTEM.CNF - Detected PSX/PSone Disc = %s", value);
type = disc_type::ps1;
}
else if (key == "VMODE")
{
disc_log.notice("SYSTEM.CNF - Disc region type = %s", value);
}
else if (key == "VER")
{
disc_log.notice("SYSTEM.CNF - Software version = %s", value);
}
}
if (type == disc_type::unknown)
{
disc_log.error("SYSTEM.CNF - Disc is not a PSX/PSone or PS2 game!");
}
return type;
}
}

15
rpcs3/Loader/disc.h Normal file
View file

@ -0,0 +1,15 @@
#pragma once
namespace disc
{
enum class disc_type
{
invalid,
unknown,
ps1,
ps2,
ps3
};
disc_type get_disc_type(std::string path, std::string& disc_root, std::string& ps3_game_dir);
}

View file

@ -107,6 +107,7 @@
<ClCompile Include="Emu\NP\np_structs_extra.cpp" />
<ClCompile Include="Emu\NP\rpcn_client.cpp" />
<ClCompile Include="Emu\vfs_config.cpp" />
<ClCompile Include="Loader\disc.cpp" />
<ClCompile Include="util\atomic.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
@ -538,6 +539,7 @@
<ClInclude Include="Emu\system_config.h" />
<ClInclude Include="Emu\system_config_types.h" />
<ClInclude Include="Emu\vfs_config.h" />
<ClInclude Include="Loader\disc.h" />
<ClInclude Include="Loader\mself.hpp" />
<ClInclude Include="util\atomic.hpp" />
<ClInclude Include="util\media_utils.h" />

View file

@ -1078,6 +1078,9 @@
<ClCompile Include="Emu\IPC_config.cpp">
<Filter>Emu</Filter>
</ClCompile>
<ClCompile Include="Loader\disc.cpp">
<Filter>Loader</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Crypto\aes.h">
@ -2143,6 +2146,9 @@
<ClInclude Include="Emu\IPC_config.h">
<Filter>Emu</Filter>
</ClInclude>
<ClInclude Include="Loader\disc.h">
<Filter>Loader</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="Emu\RSX\Program\GLSLSnippets\GPUDeswizzle.glsl">

View file

@ -138,6 +138,9 @@ void headless_application::InitializeCallbacks()
callbacks.on_stop = []() {};
callbacks.on_ready = []() {};
callbacks.enable_disc_eject = [](bool) {};
callbacks.enable_disc_insert = [](bool) {};
callbacks.on_missing_fw = []() { return false; };
callbacks.handle_taskbar_progress = [](s32, s32) {};

View file

@ -246,6 +246,8 @@ void gui_application::InitializeConnects()
connect(this, &gui_application::OnEmulatorPause, m_main_window, &main_window::OnEmuPause);
connect(this, &gui_application::OnEmulatorResume, m_main_window, &main_window::OnEmuResume);
connect(this, &gui_application::OnEmulatorReady, m_main_window, &main_window::OnEmuReady);
connect(this, &gui_application::OnEnableDiscEject, m_main_window, &main_window::OnEnableDiscEject);
connect(this, &gui_application::OnEnableDiscInsert, m_main_window, &main_window::OnEnableDiscInsert);
}
#ifdef WITH_DISCORD_RPC
@ -414,6 +416,21 @@ void gui_application::InitializeCallbacks()
callbacks.on_stop = [this]() { OnEmulatorStop(); };
callbacks.on_ready = [this]() { OnEmulatorReady(); };
callbacks.enable_disc_eject = [this](bool enabled)
{
Emu.CallFromMainThread([this, enabled]()
{
OnEnableDiscEject(enabled);
});
};
callbacks.enable_disc_insert = [this](bool enabled)
{
Emu.CallFromMainThread([this, enabled]()
{
OnEnableDiscInsert(enabled);
});
};
callbacks.on_missing_fw = [this]()
{
if (!m_main_window) return false;

View file

@ -92,6 +92,8 @@ Q_SIGNALS:
void OnEmulatorResume(bool start_playtime);
void OnEmulatorStop();
void OnEmulatorReady();
void OnEnableDiscEject(bool enabled);
void OnEnableDiscInsert(bool enabled);
void RequestCallFromMainThread(const std::function<void()>& func);

View file

@ -140,6 +140,7 @@ namespace gui
const gui_save fd_log_viewer = gui_save(main_window, "lastExplorePathLOG", "");
const gui_save fd_ext_mself = gui_save(main_window, "lastExplorePathExMSELF", "");
const gui_save fd_ext_tar = gui_save(main_window, "lastExplorePathExTAR", "");
const gui_save fd_insert_disc = gui_save(main_window, "lastExplorePathDISC", "");
const gui_save mw_debugger = gui_save(main_window, "debuggerVisible", false);
const gui_save mw_logger = gui_save(main_window, "loggerVisible", true);

View file

@ -1692,6 +1692,16 @@ void main_window::EnableMenus(bool enabled) const
ui->actionCreate_RSX_Capture->setEnabled(enabled);
}
void main_window::OnEnableDiscEject(bool enabled) const
{
ui->ejectDiscAct->setEnabled(enabled);
}
void main_window::OnEnableDiscInsert(bool enabled) const
{
ui->insertDiscAct->setEnabled(enabled);
}
void main_window::BootRecentAction(const QAction* act)
{
if (Emu.IsRunning())
@ -2053,6 +2063,34 @@ void main_window::CreateConnects()
Emu.Restart();
});
connect(ui->ejectDiscAct, &QAction::triggered, this, []()
{
gui_log.notice("User triggered eject disc action in menu bar");
Emu.EjectDisc();
});
connect(ui->insertDiscAct, &QAction::triggered, this, [this]()
{
gui_log.notice("User triggered insert disc action in menu bar");
const QString path_last_game = m_gui_settings->GetValue(gui::fd_insert_disc).toString();
const QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Disc Game Folder"), path_last_game, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
if (dir_path.isEmpty())
{
return;
}
const game_boot_result result = Emu.InsertDisc(dir_path.toStdString());
if (result != game_boot_result::no_errors)
{
QMessageBox::warning(this, tr("Failed to insert disc"), tr("Make sure that the emulation is running and that the selected path belongs to a valid disc game."));
return;
}
m_gui_settings->SetValue(gui::fd_insert_disc, QFileInfo(dir_path).path());
});
connect(ui->sysSendOpenMenuAct, &QAction::triggered, this, [this]()
{
if (Emu.IsStopped()) return;

View file

@ -101,6 +101,8 @@ public Q_SLOTS:
void OnEmuResume() const;
void OnEmuPause() const;
void OnEmuReady() const;
void OnEnableDiscEject(bool enabled) const;
void OnEnableDiscInsert(bool enabled) const;
void RepaintGui();
void RetranslateUI(const QStringList& language_codes, const QString& language);

View file

@ -222,6 +222,9 @@
<addaction name="sysStopAct"/>
<addaction name="sysRebootAct"/>
<addaction name="separator"/>
<addaction name="insertDiscAct"/>
<addaction name="ejectDiscAct"/>
<addaction name="separator"/>
<addaction name="sysSendOpenMenuAct"/>
</widget>
<widget class="QMenu" name="menuConfiguration">
@ -1199,6 +1202,22 @@
<string>Cameras</string>
</property>
</action>
<action name="ejectDiscAct">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Eject Disc</string>
</property>
</action>
<action name="insertDiscAct">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Insert Disc</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources>

View file

@ -100,7 +100,7 @@ void qt_camera_handler::open_camera()
if (const std::string camera_id = g_cfg.io.camera_id.to_string();
m_camera_id != camera_id)
{
camera_log.notice("Switching camera from %s to %s", camera_id, m_camera_id);
camera_log.notice("Switching camera from %s to %s", m_camera_id, camera_id);
camera_log.notice("Unloading old camera...");
if (m_camera) m_camera->unload();
m_camera_id = camera_id;