mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-20 19:45:20 +00:00
cellGame: implement disc change callbacks
This commit is contained in:
parent
bc3a899acf
commit
87762a9b17
19 changed files with 728 additions and 86 deletions
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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
149
rpcs3/Loader/disc.cpp
Normal 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
15
rpcs3/Loader/disc.h
Normal 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);
|
||||
}
|
|
@ -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" />
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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) {};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue