Allow emulation to work without firmware (#9367)

* Allow emulation to work without firmware
* Fix HLE prx path detection.
* Fix manual list loading bugs.
* Fix HLE gcm
* GUI: Fix fonts search
* GUI: Hardcode sprx list
Do not depend on /dev_flash/sys/external/ contents.
This commit is contained in:
Eladash 2020-12-07 19:10:34 +02:00 committed by GitHub
parent 51dcb4a79d
commit 2602be426f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 257 additions and 159 deletions

View file

@ -1077,7 +1077,7 @@ error_code GcmUnmapIoAddress(ppu_thread& ppu, gcm_config* cfg, u32 io)
{ {
if (u32 ea = cfg->offsetTable.eaAddress[io >>= 20], size = cfg->IoMapTable[ea]; size) if (u32 ea = cfg->offsetTable.eaAddress[io >>= 20], size = cfg->IoMapTable[ea]; size)
{ {
if (auto error = sys_rsx_context_iounmap(ppu, 0x55555555, io, size << 20)) if (auto error = sys_rsx_context_iounmap(ppu, 0x55555555, io << 20, size << 20))
{ {
return error; return error;
} }

View file

@ -1387,26 +1387,27 @@ void ppu_load_exec(const ppu_exec_object& elf)
// Initialize process // Initialize process
std::vector<std::shared_ptr<lv2_prx>> loaded_modules; std::vector<std::shared_ptr<lv2_prx>> loaded_modules;
// Get LLE module list // Module list to load at startup
std::set<std::string> load_libs; std::set<std::string> load_libs;
if (g_cfg.core.lib_loading == lib_loading_type::manual) if ((g_cfg.core.lib_loading != lib_loading_type::hybrid && g_cfg.core.lib_loading != lib_loading_type::manual) || g_cfg.core.load_libraries.get_set().count("liblv2.sprx"))
{ {
// Load required set of modules (lib_loading_type::both processed in sys_prx.cpp) // Will load libsysmodule.sprx internally
load_libs = g_cfg.core.load_libraries.get_set(); load_libs.emplace("liblv2.sprx");
} }
else else if (g_cfg.core.lib_loading == lib_loading_type::hybrid)
{ {
if (g_cfg.core.lib_loading != lib_loading_type::hybrid || g_cfg.core.load_libraries.get_set().count("liblv2.sprx")) // Load only libsysmodule.sprx
{ load_libs.emplace("libsysmodule.sprx");
// Will load libsysmodule.sprx internally }
load_libs.emplace("liblv2.sprx");
} const std::string lle_dir = vfs::get("/dev_flash/sys/external/");
else
{ if (!fs::is_file(lle_dir + "liblv2.sprx"))
// Load only libsysmodule.sprx {
load_libs.emplace("libsysmodule.sprx"); ppu_loader.error("PS3 firmware is not installed or the installed firmware is invalid."
} "\nYou should install the PS3 Firmware (Menu: File -> Install Firmware)."
"\nVisit https://rpcs3.net/ for Quickstart Guide and more information.");
} }
// Program entry // Program entry
@ -1414,15 +1415,6 @@ void ppu_load_exec(const ppu_exec_object& elf)
if (!load_libs.empty()) if (!load_libs.empty())
{ {
const std::string lle_dir = vfs::get("/dev_flash/sys/external/");
if (!fs::is_dir(lle_dir) || !fs::is_file(lle_dir + "libsysmodule.sprx"))
{
ppu_loader.error("PS3 firmware is not installed or the installed firmware is invalid."
"\nYou should install the PS3 Firmware (Menu: File -> Install Firmware)."
"\nVisit https://rpcs3.net/ for Quickstart Guide and more information.");
}
for (const auto& name : load_libs) for (const auto& name : load_libs)
{ {
const ppu_prx_object obj = decrypt_self(fs::file(lle_dir + name)); const ppu_prx_object obj = decrypt_self(fs::file(lle_dir + name));
@ -1455,7 +1447,7 @@ void ppu_load_exec(const ppu_exec_object& elf)
} }
else else
{ {
fmt::throw_exception("Failed to load /dev_flash/sys/external/%s: %s", name, obj.get_error()); ppu_loader.error("Failed to load /dev_flash/sys/external/%s: %s (forcing HLE implementation)", name, obj.get_error());
} }
} }
} }

View file

@ -21,67 +21,150 @@ extern void ppu_initialize(const ppu_module&);
LOG_CHANNEL(sys_prx); LOG_CHANNEL(sys_prx);
static const std::unordered_map<std::string, int> s_prx_ignore extern const std::unordered_map<std::string_view, int> g_prx_list
{ {
{ "/dev_flash/sys/external/libaudio.sprx", 0 }, { "libaacenc.sprx", 0 },
{ "/dev_flash/sys/external/libcamera.sprx", 0 }, { "libaacenc_spurs.sprx", 0 },
{ "/dev_flash/sys/external/libgem.sprx", 0 }, { "libac3dec.sprx", 0 },
{ "/dev_flash/sys/external/libio.sprx", 0 }, { "libac3dec2.sprx", 0 },
{ "/dev_flash/sys/external/libmedi.sprx", 0 }, { "libadec.sprx", 0 },
{ "/dev_flash/sys/external/libmic.sprx", 0 }, { "libadec2.sprx", 0 },
{ "/dev_flash/sys/external/libnetctl.sprx", 0 }, { "libadec_internal.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil.sprx", 0 }, { "libad_async.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_ap.sprx", 0 }, { "libad_billboard_util.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_authdialog.sprx", 0 }, { "libad_core.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_avc_ext.sprx", 0 }, { "libapostsrc_mini.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_avc2.sprx", 0 }, { "libasfparser2_astd.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_avconf_ext.sprx", 0 }, { "libat3dec.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_bgdl.sprx", 0 }, { "libat3multidec.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_cross_controller.sprx", 0 }, { "libatrac3multi.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_dec_psnvideo.sprx", 0 }, { "libatrac3plus.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_dtcp_ip.sprx", 0 }, { "libatxdec.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_game.sprx", 0 }, { "libatxdec2.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_game_exec.sprx", 0 }, { "libaudio.sprx", 1 },
{ "/dev_flash/sys/external/libsysutil_imejp.sprx", 0 }, { "libavcdec.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_misc.sprx", 0 }, { "libavcenc.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_music.sprx", 0 }, { "libavcenc_small.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_music_decode.sprx", 0 }, { "libavchatjpgdec.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_music_export.sprx", 0 }, { "libbeisobmf.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_np.sprx", 0 }, { "libbemp2sys.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_np_clans.sprx", 0 }, { "libcamera.sprx", 1 },
{ "/dev_flash/sys/external/libsysutil_np_commerce2.sprx", 0 }, { "libcelp8dec.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_np_eula.sprx", 0 }, { "libcelp8enc.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_np_installer.sprx", 0 }, { "libcelpdec.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_np_sns.sprx", 0 }, { "libcelpenc.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_np_trophy.sprx", 0 }, { "libddpdec.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_np_tus.sprx", 0 }, { "libdivxdec.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_np_util.sprx", 0 }, { "libdmux.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_np2.sprx", 0 }, { "libdmuxpamf.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_oskdialog_ext.sprx", 0 }, { "libdtslbrdec.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_pesm.sprx", 0 }, { "libfiber.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_photo_decode.sprx", 0 }, { "libfont.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_photo_export.sprx", 0 }, { "libfontFT.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_photo_export2.sprx", 0 }, { "libfreetype.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_photo_import.sprx", 0 }, { "libfreetypeTT.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_photo_network_sharing.sprx", 0 }, { "libfs.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_print.sprx", 0 }, { "libfs_155.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_rec.sprx", 0 }, { "libgcm_sys.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_remoteplay.sprx", 0 }, { "libgem.sprx", 1 },
{ "/dev_flash/sys/external/libsysutil_rtcalarm.sprx", 0 }, { "libgifdec.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_savedata.sprx", 0 }, { "libhttp.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_savedata_psp.sprx", 0 }, { "libio.sprx", 1 },
{ "/dev_flash/sys/external/libsysutil_screenshot.sprx", 0 }, { "libjpgdec.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_search.sprx", 0 }, { "libjpgenc.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_storagedata.sprx", 0 }, { "libkey2char.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_subdisplay.sprx", 0 }, { "libl10n.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_syschat.sprx", 0 }, { "liblv2.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_sysconf_ext.sprx", 0 }, { "liblv2coredump.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_userinfo.sprx", 0 }, { "liblv2dbg_for_cex.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_video_export.sprx", 0 }, { "libm2bcdec.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_video_player.sprx", 0 }, { "libm4aacdec.sprx", 0 },
{ "/dev_flash/sys/external/libsysutil_video_upload.sprx", 0 }, { "libm4aacdec2ch.sprx", 0 },
{ "/dev_flash/sys/external/libvdec.sprx", 0 }, { "libm4hdenc.sprx", 0 },
{ "/dev_flash/sys/external/libvoice.sprx", 0 }, { "libm4venc.sprx", 0 },
{ "libmedi.sprx", 1 },
{ "libmic.sprx", 1 },
{ "libmp3dec.sprx", 0 },
{ "libmp4.sprx", 0 },
{ "libmpl1dec.sprx", 0 },
{ "libmvcdec.sprx", 0 },
{ "libnet.sprx", 0 },
{ "libnetctl.sprx", 1 },
{ "libpamf.sprx", 0 },
{ "libpngdec.sprx", 0 },
{ "libpngenc.sprx", 0 },
{ "libresc.sprx", 0 },
{ "librtc.sprx", 0 },
{ "librudp.sprx", 0 },
{ "libsail.sprx", 0 },
{ "libsail_avi.sprx", 0 },
{ "libsail_rec.sprx", 0 },
{ "libsjvtd.sprx", 0 },
{ "libsmvd2.sprx", 0 },
{ "libsmvd4.sprx", 0 },
{ "libspurs_jq.sprx", 0 },
{ "libsre.sprx", 0 },
{ "libssl.sprx", 0 },
{ "libsvc1d.sprx", 0 },
{ "libsync2.sprx", 0 },
{ "libsysmodule.sprx", 0 },
{ "libsysutil.sprx", 1 },
{ "libsysutil_ap.sprx", 1 },
{ "libsysutil_authdialog.sprx", 1 },
{ "libsysutil_avc2.sprx", 1 },
{ "libsysutil_avconf_ext.sprx", 1 },
{ "libsysutil_avc_ext.sprx", 1 },
{ "libsysutil_bgdl.sprx", 1 },
{ "libsysutil_cross_controller.sprx", 1 },
{ "libsysutil_dec_psnvideo.sprx", 1 },
{ "libsysutil_dtcp_ip.sprx", 1 },
{ "libsysutil_game.sprx", 1 },
{ "libsysutil_game_exec.sprx", 1 },
{ "libsysutil_imejp.sprx", 1 },
{ "libsysutil_misc.sprx", 1 },
{ "libsysutil_music.sprx", 1 },
{ "libsysutil_music_decode.sprx", 1 },
{ "libsysutil_music_export.sprx", 1 },
{ "libsysutil_np.sprx", 1 },
{ "libsysutil_np2.sprx", 1 },
{ "libsysutil_np_clans.sprx", 1 },
{ "libsysutil_np_commerce2.sprx", 1 },
{ "libsysutil_np_eula.sprx", 1 },
{ "libsysutil_np_installer.sprx", 1 },
{ "libsysutil_np_sns.sprx", 1 },
{ "libsysutil_np_trophy.sprx", 1 },
{ "libsysutil_np_tus.sprx", 1 },
{ "libsysutil_np_util.sprx", 1 },
{ "libsysutil_oskdialog_ext.sprx", 1 },
{ "libsysutil_pesm.sprx", 1 },
{ "libsysutil_photo_decode.sprx", 1 },
{ "libsysutil_photo_export.sprx", 1 },
{ "libsysutil_photo_export2.sprx", 1 },
{ "libsysutil_photo_import.sprx", 1 },
{ "libsysutil_photo_network_sharing.sprx", 1 },
{ "libsysutil_print.sprx", 1 },
{ "libsysutil_rec.sprx", 1 },
{ "libsysutil_remoteplay.sprx", 1 },
{ "libsysutil_rtcalarm.sprx", 1 },
{ "libsysutil_savedata.sprx", 1 },
{ "libsysutil_savedata_psp.sprx", 1 },
{ "libsysutil_screenshot.sprx", 1 },
{ "libsysutil_search.sprx", 1 },
{ "libsysutil_storagedata.sprx", 1 },
{ "libsysutil_subdisplay.sprx", 1 },
{ "libsysutil_syschat.sprx", 1 },
{ "libsysutil_sysconf_ext.sprx", 1 },
{ "libsysutil_userinfo.sprx", 1 },
{ "libsysutil_video_export.sprx", 1 },
{ "libsysutil_video_player.sprx", 1 },
{ "libsysutil_video_upload.sprx", 1 },
{ "libusbd.sprx", 0 },
{ "libusbpspcm.sprx", 0 },
{ "libvdec.sprx", 1 },
{ "libvoice.sprx", 1 },
{ "libvpost.sprx", 0 },
{ "libvpost2.sprx", 0 },
{ "libwmadec.sprx", 0 },
}; };
static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptr<sys_prx_load_module_option_t> pOpt, fs::file src = {}) static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptr<sys_prx_load_module_option_t> pOpt, fs::file src = {})
@ -101,17 +184,13 @@ static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptr<s
fmt::throw_exception("sys_prx: Unimplemented fixed address allocations" HERE); fmt::throw_exception("sys_prx: Unimplemented fixed address allocations" HERE);
} }
std::string name = vpath.substr(vpath.find_last_of('/') + 1); std::string vpath0;
std::string path = vfs::get(vpath); const std::string path = vfs::get(vpath, nullptr, &vpath0);
const std::string name = vpath0.substr(vpath0.find_last_of('/') + 1);
const auto existing = idm::select<lv2_obj, lv2_prx>([&](u32, lv2_prx& prx) const auto existing = idm::select<lv2_obj, lv2_prx>([&](u32, lv2_prx& prx)
{ {
if (prx.name == name && prx.path == path) return prx.path == path;
{
return true;
}
return false;
}); });
if (existing) if (existing)
@ -121,28 +200,32 @@ static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptr<s
bool ignore = false; bool ignore = false;
if (g_cfg.core.lib_loading == lib_loading_type::liblv2list) constexpr std::string_view firmware_sprx_dir = "/dev_flash/sys/external/";
const bool is_firmware_sprx = vpath0.starts_with(firmware_sprx_dir) && g_prx_list.count(std::string_view(vpath0).substr(firmware_sprx_dir.size()));
if (is_firmware_sprx)
{ {
if (vpath.starts_with("/dev_flash/sys/external/") && vpath != "/dev_flash/sys/external/libsysmodule.sprx"sv) // First condition, LLE for selected libs
ignore = g_cfg.core.load_libraries.get_set().count(name) == 0;
if (g_cfg.core.lib_loading != lib_loading_type::liblv2list && g_cfg.core.lib_loading != lib_loading_type::manual)
{ {
ignore = g_cfg.core.load_libraries.get_set().count(name) == 0; // Override list setting condition for liblv2only
// For the other modes g_prx_list is a second condition which filters HLE selected libs by list setting
if (ignore || g_cfg.core.lib_loading == lib_loading_type::liblv2only)
{
ignore = g_prx_list.at(name) != 0;
}
} }
} }
else else if (vpath0.starts_with("/"))
{ {
ignore = s_prx_ignore.count(vpath) != 0; // Special case (currently unused): HLE for files outside of "/dev_flash/sys/external/"
// Have to specify full path for them
ignore = g_prx_list.count(vpath0) && g_prx_list.at(vpath0);
} }
if (ignore && (g_cfg.core.lib_loading == lib_loading_type::hybrid || g_cfg.core.lib_loading == lib_loading_type::liblv2both)) auto hle_load = [&]()
{
// Ignore ignore list if the library is selected in 'both' mode
if (g_cfg.core.load_libraries.get_set().count(name) != 0)
{
ignore = false;
}
}
if (ignore)
{ {
const auto prx = idm::make_ptr<lv2_obj, lv2_prx>(); const auto prx = idm::make_ptr<lv2_obj, lv2_prx>();
@ -152,6 +235,11 @@ static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptr<s
sys_prx.warning(u8"Ignored module: “%s” (id=0x%x)", vpath, idm::last_id()); sys_prx.warning(u8"Ignored module: “%s” (id=0x%x)", vpath, idm::last_id());
return not_an_error(idm::last_id()); return not_an_error(idm::last_id());
};
if (ignore)
{
return hle_load();
} }
if (!src) if (!src)
@ -160,6 +248,12 @@ static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptr<s
if (fs_error) if (fs_error)
{ {
if (fs_error + 0u == CELL_ENOENT && is_firmware_sprx)
{
sys_prx.error(u8"firmware SPRX not found: “%s” (forcing HLE implementation)", vpath, idm::last_id());
return hle_load();
}
return {fs_error, vpath}; return {fs_error, vpath};
} }

View file

@ -270,7 +270,6 @@ error_code sys_rsx_context_allocate(cpu_thread& cpu, vm::ptr<u32> context_id, vm
render->current_display_buffer = 0; render->current_display_buffer = 0;
render->label_addr = vm::cast(*lpar_reports, HERE); render->label_addr = vm::cast(*lpar_reports, HERE);
render->device_addr = rsx_cfg->device_addr; render->device_addr = rsx_cfg->device_addr;
render->dma_address = rsx_cfg->dma_address;
render->local_mem_size = rsx_cfg->memory_size; render->local_mem_size = rsx_cfg->memory_size;
render->init(vm::cast(*lpar_dma_control, HERE)); render->init(vm::cast(*lpar_dma_control, HERE));

View file

@ -119,6 +119,7 @@ namespace rsx
case language_class::default_: case language_class::default_:
{ {
result.font_names.emplace_back("Arial.ttf"); result.font_names.emplace_back("Arial.ttf");
result.font_names.emplace_back("arial.ttf");
#ifndef _WIN32 #ifndef _WIN32
result.font_names.emplace_back("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"); // ubuntu result.font_names.emplace_back("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"); // ubuntu
result.font_names.emplace_back("/usr/share/fonts/TTF/DejaVuSans.ttf"); // arch result.font_names.emplace_back("/usr/share/fonts/TTF/DejaVuSans.ttf"); // arch
@ -137,6 +138,7 @@ namespace rsx
// Known system font as last fallback // Known system font as last fallback
result.font_names.emplace_back("Yu Gothic.ttf"); result.font_names.emplace_back("Yu Gothic.ttf");
result.font_names.emplace_back("YuGothR.ttc");
break; break;
} }
case language_class::hangul: case language_class::hangul:
@ -149,6 +151,7 @@ namespace rsx
// Known system font as last fallback // Known system font as last fallback
result.font_names.emplace_back("Malgun Gothic.ttf"); result.font_names.emplace_back("Malgun Gothic.ttf");
result.font_names.emplace_back("malgun.ttf");
break; break;
} }
} }

View file

@ -452,6 +452,12 @@ namespace rsx
// Wait for startup (TODO) // Wait for startup (TODO)
while (m_rsx_thread_exiting) while (m_rsx_thread_exiting)
{ {
// Wait for external pause events
if (external_interrupt_lock)
{
wait_pause();
}
thread_ctrl::wait_for(1000); thread_ctrl::wait_for(1000);
if (Emu.IsStopped()) if (Emu.IsStopped())
@ -1957,6 +1963,7 @@ namespace rsx
void thread::init(u32 ctrlAddress) void thread::init(u32 ctrlAddress)
{ {
dma_address = ctrlAddress;
ctrl = vm::_ptr<RsxDmaControl>(ctrlAddress); ctrl = vm::_ptr<RsxDmaControl>(ctrlAddress);
flip_status = CELL_GCM_DISPLAY_FLIP_STATUS_DONE; flip_status = CELL_GCM_DISPLAY_FLIP_STATUS_DONE;

View file

@ -1059,11 +1059,6 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool
sys_log.notice("Hdd1: %s", vfs::get("/dev_hdd1")); sys_log.notice("Hdd1: %s", vfs::get("/dev_hdd1"));
} }
if (!fs::is_file(g_cfg.vfs.get_dev_flash() + "sys/external/liblv2.sprx"))
{
return game_boot_result::firmware_missing;
}
// Special boot mode (directory scan) // Special boot mode (directory scan)
if (!add_only && fs::is_dir(m_path)) if (!add_only && fs::is_dir(m_path))
{ {
@ -1682,6 +1677,15 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool
{ {
if (ppu_exec == elf_error::ok) if (ppu_exec == elf_error::ok)
{ {
if (!fs::is_file(g_cfg.vfs.get_dev_flash() + "sys/external/liblv2.sprx"))
{
if (!GetCallbacks().on_missing_fw())
{
Stop();
return game_boot_result::firmware_missing;
}
}
Run(true); Run(true);
} }

View file

@ -41,6 +41,7 @@ struct EmuCallbacks
std::function<void()> on_resume; std::function<void()> on_resume;
std::function<void()> on_stop; std::function<void()> on_stop;
std::function<void()> on_ready; std::function<void()> on_ready;
std::function<bool()> on_missing_fw;
std::function<bool(bool)> exit; // (force_quit) close RPCS3 std::function<bool(bool)> exit; // (force_quit) close RPCS3
std::function<void(s32, s32)> handle_taskbar_progress; // (type, value) type: 0 for reset, 1 for increment, 2 for set_limit std::function<void(s32, s32)> handle_taskbar_progress; // (type, value) type: 0 for reset, 1 for increment, 2 for set_limit
std::function<void()> init_kb_handler; std::function<void()> init_kb_handler;

View file

@ -11,7 +11,7 @@ void fmt_class_string<elf_error>::format(std::string& out, u64 arg)
{ {
case elf_error::ok: return "OK"; case elf_error::ok: return "OK";
case elf_error::stream: return "Invalid stream or file not found"; case elf_error::stream: return "File not found";
case elf_error::stream_header: return "Failed to read ELF header"; case elf_error::stream_header: return "Failed to read ELF header";
case elf_error::stream_phdrs: return "Failed to read ELF program headers"; case elf_error::stream_phdrs: return "Failed to read ELF program headers";
case elf_error::stream_shdrs: return "Failed to read ELF section headers"; case elf_error::stream_shdrs: return "Failed to read ELF section headers";

View file

@ -344,6 +344,11 @@ void gui_application::InitializeCallbacks()
callbacks.on_stop = [this]() { OnEmulatorStop(); }; callbacks.on_stop = [this]() { OnEmulatorStop(); };
callbacks.on_ready = [this]() { OnEmulatorReady(); }; callbacks.on_ready = [this]() { OnEmulatorReady(); };
callbacks.on_missing_fw = [this]()
{
return m_gui_settings->GetBootConfirmation(m_main_window, gui::ib_confirm_fw);
};
callbacks.handle_taskbar_progress = [this](s32 type, s32 value) callbacks.handle_taskbar_progress = [this](s32 type, s32 value)
{ {
if (m_game_window) if (m_game_window)

View file

@ -154,9 +154,9 @@ void gui_settings::SetCategoryVisibility(int cat, const bool& val)
SetValue(value, val); SetValue(value, val);
} }
void gui_settings::ShowBox(bool confirm, const QString& title, const QString& text, const gui_save& entry, int* result = nullptr, QWidget* parent = nullptr, bool always_on_top = false) void gui_settings::ShowBox(QMessageBox::Icon icon, const QString& title, const QString& text, const gui_save& entry, int* result = nullptr, QWidget* parent = nullptr, bool always_on_top = false)
{ {
const std::string dialog_type = confirm ? "Confirmation" : "Info"; const std::string dialog_type = icon != QMessageBox::Information ? "Confirmation" : "Info";
const bool has_gui_setting = !entry.name.isEmpty(); const bool has_gui_setting = !entry.name.isEmpty();
if (has_gui_setting && !GetValue(entry).toBool()) if (has_gui_setting && !GetValue(entry).toBool())
@ -165,13 +165,13 @@ void gui_settings::ShowBox(bool confirm, const QString& title, const QString& te
return; return;
} }
const QFlags<QMessageBox::StandardButton> buttons = confirm ? QMessageBox::Yes | QMessageBox::No : QMessageBox::Ok; const QFlags<QMessageBox::StandardButton> buttons = icon != QMessageBox::Information ? QMessageBox::Yes | QMessageBox::No : QMessageBox::Ok;
const QMessageBox::Icon icon = confirm ? QMessageBox::Question : QMessageBox::Information;
QMessageBox* mb = new QMessageBox(icon, title, text, buttons, parent, Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint | (always_on_top ? Qt::WindowStaysOnTopHint : Qt::Widget)); QMessageBox* mb = new QMessageBox(icon, title, text, buttons, parent, Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint | (always_on_top ? Qt::WindowStaysOnTopHint : Qt::Widget));
mb->deleteLater(); mb->deleteLater();
mb->setTextFormat(Qt::RichText);
if (has_gui_setting) if (has_gui_setting && icon != QMessageBox::Critical)
{ {
mb->setCheckBox(new QCheckBox(tr("Don't show again"))); mb->setCheckBox(new QCheckBox(tr("Don't show again")));
} }
@ -182,7 +182,10 @@ void gui_settings::ShowBox(bool confirm, const QString& title, const QString& te
{ {
*result = res; *result = res;
} }
if (has_gui_setting && mb->checkBox()->isChecked())
const auto checkBox = mb->checkBox();
if (checkBox && checkBox->isChecked())
{ {
SetValue(entry, false); SetValue(entry, false);
cfg_log.notice("%s Dialog for Entry %s is now disabled", dialog_type, sstr(entry.name)); cfg_log.notice("%s Dialog for Entry %s is now disabled", dialog_type, sstr(entry.name));
@ -194,12 +197,12 @@ void gui_settings::ShowBox(bool confirm, const QString& title, const QString& te
void gui_settings::ShowConfirmationBox(const QString& title, const QString& text, const gui_save& entry, int* result = nullptr, QWidget* parent = nullptr) void gui_settings::ShowConfirmationBox(const QString& title, const QString& text, const gui_save& entry, int* result = nullptr, QWidget* parent = nullptr)
{ {
ShowBox(true, title, text, entry, result, parent, true); ShowBox(QMessageBox::Question, title, text, entry, result, parent, true);
} }
void gui_settings::ShowInfoBox(const QString& title, const QString& text, const gui_save& entry, QWidget* parent = nullptr) void gui_settings::ShowInfoBox(const QString& title, const QString& text, const gui_save& entry, QWidget* parent = nullptr)
{ {
ShowBox(false, title, text, entry, nullptr, parent, false); ShowBox(QMessageBox::Information, title, text, entry, nullptr, parent, false);
} }
bool gui_settings::GetBootConfirmation(QWidget* parent, const gui_save& gui_save_entry) bool gui_settings::GetBootConfirmation(QWidget* parent, const gui_save& gui_save_entry)
@ -208,6 +211,7 @@ bool gui_settings::GetBootConfirmation(QWidget* parent, const gui_save& gui_save
{ {
QString title = tr("Close Running Game?"); QString title = tr("Close Running Game?");
QString message = tr("Performing this action will close the current game.\nDo you really want to continue?\n\nAny unsaved progress will be lost!\n"); QString message = tr("Performing this action will close the current game.\nDo you really want to continue?\n\nAny unsaved progress will be lost!\n");
auto icon = QMessageBox::Question;
if (gui_save_entry == gui::ib_confirm_boot) if (gui_save_entry == gui::ib_confirm_boot)
{ {
@ -218,10 +222,18 @@ bool gui_settings::GetBootConfirmation(QWidget* parent, const gui_save& gui_save
title = tr("Exit RPCS3?"); title = tr("Exit RPCS3?");
message = tr("A game is currently running. Do you really want to close RPCS3?\n\nAny unsaved progress will be lost!\n"); message = tr("A game is currently running. Do you really want to close RPCS3?\n\nAny unsaved progress will be lost!\n");
} }
else if (gui_save_entry == gui::ib_confirm_fw)
{
title = tr("Missing Firmware Detected!");
message = tr("Install the PS3 Firmware (Menu: File -> Install Firmware)."
"\n<br>For more information read the <a href=\"https://rpcs3.net/quickstart\">quickstart guide</a>."
"\nCommercial games do not work without firmware! Do you wish to continue!?");
icon = QMessageBox::Critical;
}
int result = QMessageBox::Yes; int result = QMessageBox::Yes;
ShowConfirmationBox(title, message, gui_save_entry, &result, parent); ShowBox(icon, title, message, gui_save_entry, &result, parent);
if (result != QMessageBox::Yes) if (result != QMessageBox::Yes)
{ {

View file

@ -6,6 +6,7 @@
#include <QVariant> #include <QVariant>
#include <QSize> #include <QSize>
#include <QColor> #include <QColor>
#include <QMessageBox>
namespace gui namespace gui
{ {
@ -120,6 +121,7 @@ namespace gui
const gui_save ib_show_welcome = gui_save(main_window, "infoBoxEnabledWelcome", true); const gui_save ib_show_welcome = gui_save(main_window, "infoBoxEnabledWelcome", true);
const gui_save ib_confirm_exit = gui_save(main_window, "confirmationBoxExitGame", true); const gui_save ib_confirm_exit = gui_save(main_window, "confirmationBoxExitGame", true);
const gui_save ib_confirm_boot = gui_save(main_window, "confirmationBoxBootGame", true); const gui_save ib_confirm_boot = gui_save(main_window, "confirmationBoxBootGame", true);
const gui_save ib_confirm_fw = gui_save(main_window, "confirmationMissingFW", true);
const gui_save fd_install_pkg = gui_save(main_window, "lastExplorePathPKG", ""); const gui_save fd_install_pkg = gui_save(main_window, "lastExplorePathPKG", "");
const gui_save fd_install_pup = gui_save(main_window, "lastExplorePathPUP", ""); const gui_save fd_install_pup = gui_save(main_window, "lastExplorePathPUP", "");
@ -273,7 +275,7 @@ public Q_SLOTS:
private: private:
void SaveConfigNameToDefault(const QString& config_name); void SaveConfigNameToDefault(const QString& config_name);
void BackupSettingsToTarget(const QString& config_name); void BackupSettingsToTarget(const QString& config_name);
void ShowBox(bool confirm, const QString& title, const QString& text, const gui_save& entry, int* result, QWidget* parent, bool always_on_top); void ShowBox(QMessageBox::Icon icon, const QString& title, const QString& text, const gui_save& entry, int* result, QWidget* parent, bool always_on_top);
QString m_current_name; QString m_current_name;
}; };

View file

@ -300,10 +300,6 @@ void main_window::OnPlayOrPause()
void main_window::show_boot_error(game_boot_result status) void main_window::show_boot_error(game_boot_result status)
{ {
if (status == game_boot_result::no_errors)
{
return;
}
QString message; QString message;
switch (status) switch (status)
{ {
@ -325,9 +321,9 @@ void main_window::show_boot_error(game_boot_result status)
case game_boot_result::file_creation_error: case game_boot_result::file_creation_error:
message = tr("The emulator could not create files required for booting."); message = tr("The emulator could not create files required for booting.");
break; break;
case game_boot_result::firmware_missing: case game_boot_result::firmware_missing: // Handled elsewhere
message = tr("Firmware has not been installed. Install firmware with the \"File > Install Firmware\" menu option."); case game_boot_result::no_errors:
break; return;
case game_boot_result::generic_error: case game_boot_result::generic_error:
default: default:
message = tr("Unknown error."); message = tr("Unknown error.");

View file

@ -30,6 +30,7 @@
#include "Crypto/unself.h" #include "Crypto/unself.h"
#include "Utilities/sysinfo.h" #include "Utilities/sysinfo.h"
#include <set>
#include <unordered_set> #include <unordered_set>
#include <thread> #include <thread>
@ -41,6 +42,7 @@ LOG_CHANNEL(cfg_log, "CFG");
inline std::string sstr(const QString& _in) { return _in.toStdString(); } inline std::string sstr(const QString& _in) { return _in.toStdString(); }
inline std::string sstr(const QVariant& _in) { return sstr(_in.toString()); } inline std::string sstr(const QVariant& _in) { return sstr(_in.toString()); }
inline QString qsv(std::string_view sv) { return QString(sv.data()); }
settings_dialog::settings_dialog(std::shared_ptr<gui_settings> gui_settings, std::shared_ptr<emu_settings> emu_settings, const int& tab_index, QWidget *parent, const GameInfo* game) settings_dialog::settings_dialog(std::shared_ptr<gui_settings> gui_settings, std::shared_ptr<emu_settings> emu_settings, const int& tab_index, QWidget *parent, const GameInfo* game)
: QDialog(parent) : QDialog(parent)
@ -1026,47 +1028,28 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> gui_settings, std
m_emu_settings->EnhanceRadioButton(lib_mode_bg, emu_settings_type::LibLoadOptions); m_emu_settings->EnhanceRadioButton(lib_mode_bg, emu_settings_type::LibLoadOptions);
// Sort string vector alphabetically
static const auto sort_string_vector = [](std::vector<std::string>& vec)
{
std::sort(vec.begin(), vec.end(), [](const std::string &str1, const std::string &str2) { return str1 < str2; });
};
std::vector<std::string> loadedLibs = m_emu_settings->GetLoadedLibraries(); std::vector<std::string> loadedLibs = m_emu_settings->GetLoadedLibraries();
sort_string_vector(loadedLibs); std::set<std::string_view> set(loadedLibs.begin(), loadedLibs.end());
for (const auto& lib : loadedLibs) for (const auto& lib : set)
{ {
QListWidgetItem* item = new QListWidgetItem(qstr(lib), ui->lleList); QListWidgetItem* item = new QListWidgetItem(qsv(lib), ui->lleList);
item->setFlags(item->flags() | Qt::ItemIsUserCheckable); // set checkable flag item->setFlags(item->flags() | Qt::ItemIsUserCheckable); // set checkable flag
item->setCheckState(Qt::Checked); // AND initialize check state item->setCheckState(Qt::Checked); // AND initialize check state
ui->lleList->addItem(item); ui->lleList->addItem(item);
} }
const std::string lle_dir = g_cfg.vfs.get_dev_flash() + "sys/external/"; extern const std::unordered_map<std::string_view, int> g_prx_list;
std::unordered_set<std::string> set(loadedLibs.begin(), loadedLibs.end()); for (const auto& lib : g_prx_list)
std::vector<std::string> lle_module_list_unselected;
for (const auto& prxf : fs::dir(lle_dir))
{ {
// List found unselected modules if (set.count(lib.first))
if (prxf.is_directory || (prxf.name.substr(std::max<size_t>(size_t(3), prxf.name.length()) - 4)) != "sprx")
{ {
continue; continue;
} }
if (verify_npdrm_self_headers(fs::file(lle_dir + prxf.name)) && !set.count(prxf.name))
{
lle_module_list_unselected.push_back(prxf.name);
}
}
sort_string_vector(lle_module_list_unselected); QListWidgetItem* item = new QListWidgetItem(qsv(lib.first), ui->lleList);
for (const auto& lib : lle_module_list_unselected)
{
QListWidgetItem* item = new QListWidgetItem(qstr(lib), ui->lleList);
item->setFlags(item->flags() | Qt::ItemIsUserCheckable); // set checkable flag item->setFlags(item->flags() | Qt::ItemIsUserCheckable); // set checkable flag
item->setCheckState(Qt::Unchecked); // AND initialize check state item->setCheckState(Qt::Unchecked); // AND initialize check state
ui->lleList->addItem(item); ui->lleList->addItem(item);