Merge branch 'master' into master

This commit is contained in:
hoholee12 2023-07-28 12:44:23 +09:00 committed by GitHub
commit 328d73c1a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 420 additions and 210 deletions

View File

@ -502,7 +502,7 @@ std::string_view fs::get_parent_dir_view(std::string_view path, u32 parent_level
return result;
}
bool fs::stat(const std::string& path, stat_t& info)
bool fs::get_stat(const std::string& path, stat_t& info)
{
// Ensure consistent information on failure
info = {};
@ -618,13 +618,13 @@ bool fs::stat(const std::string& path, stat_t& info)
bool fs::exists(const std::string& path)
{
fs::stat_t info{};
return fs::stat(path, info);
return fs::get_stat(path, info);
}
bool fs::is_file(const std::string& path)
{
fs::stat_t info{};
if (!fs::stat(path, info))
if (!fs::get_stat(path, info))
{
return false;
}
@ -641,7 +641,7 @@ bool fs::is_file(const std::string& path)
bool fs::is_dir(const std::string& path)
{
fs::stat_t info{};
if (!fs::stat(path, info))
if (!fs::get_stat(path, info))
{
return false;
}

View File

@ -186,7 +186,7 @@ namespace fs
}
// Get file information
bool stat(const std::string& path, stat_t& info);
bool get_stat(const std::string& path, stat_t& info);
// Check whether a file or a directory exists (not recommended, use is_file() or is_dir() instead)
bool exists(const std::string& path);

View File

@ -726,7 +726,7 @@ bool patch_engine::add_patch_data(YAML::Node node, patch_info& info, u32 modifie
break;
default:
{
const u32 offset = get_yaml_node_value<u32>(addr_node, error_message);
get_yaml_node_value<u32>(addr_node, error_message);
if (!error_message.empty())
{
error_message = fmt::format("Skipping patch data entry: [ %s, 0x%.8x, %s ] (key: %s, location: %s) Invalid patch offset '%s' (not a valid u32 or overflow)",

View File

@ -682,6 +682,7 @@ static atomic_t<u32> s_dummy_atomic = 0;
bool cpu_thread::check_state() noexcept
{
bool cpu_sleep_called = false;
bool cpu_memory_checked = false;
bool cpu_can_stop = true;
bool escape{}, retval{};
@ -770,7 +771,7 @@ bool cpu_thread::check_state() noexcept
if (!is_stopped(flags) && flags.none_of(cpu_flag::ret))
{
// Check pause flags which hold thread inside check_state (ignore suspend/debug flags on cpu_flag::temp)
if (flags & (cpu_flag::pause + cpu_flag::memory) || (cpu_can_stop && flags & (cpu_flag::dbg_global_pause + cpu_flag::dbg_pause + cpu_flag::suspend + cpu_flag::yield + cpu_flag::preempt)))
if (flags & cpu_flag::pause || (!cpu_memory_checked && flags & cpu_flag::memory) || (cpu_can_stop && flags & (cpu_flag::dbg_global_pause + cpu_flag::dbg_pause + cpu_flag::suspend + cpu_flag::yield + cpu_flag::preempt)))
{
if (!(flags & cpu_flag::wait))
{
@ -789,12 +790,18 @@ bool cpu_thread::check_state() noexcept
return store;
}
if (flags & (cpu_flag::wait + cpu_flag::memory))
{
flags -= (cpu_flag::wait + cpu_flag::memory);
store = true;
}
if (s_tls_thread_slot == umax)
{
if (cpu_flag::wait - state)
if (cpu_flag::wait - this->state.load())
{
// Force wait flag (must be set during ownership of s_cpu_lock), this makes the atomic op fail as a side effect
state += cpu_flag::wait;
this->state += cpu_flag::wait;
store = true;
}
@ -802,12 +809,6 @@ bool cpu_thread::check_state() noexcept
cpu_counter::add(this);
}
if (flags & cpu_flag::wait)
{
flags -= cpu_flag::wait;
store = true;
}
retval = false;
}
else
@ -856,6 +857,14 @@ bool cpu_thread::check_state() noexcept
if (escape)
{
if (vm::g_range_lock_bits[1] && vm::g_tls_locked && *vm::g_tls_locked == this)
{
state += cpu_flag::wait + cpu_flag::memory;
cpu_sleep_called = false;
cpu_memory_checked = false;
continue;
}
if (cpu_can_stop && state0 & cpu_flag::pending)
{
// Execute pending work
@ -866,6 +875,7 @@ bool cpu_thread::check_state() noexcept
// Work could have changed flags
// Reset internal flags as if check_state() has just been called
cpu_sleep_called = false;
cpu_memory_checked = false;
continue;
}
}
@ -883,6 +893,7 @@ bool cpu_thread::check_state() noexcept
{
cpu_sleep();
cpu_sleep_called = true;
cpu_memory_checked = false;
if (s_tls_thread_slot != umax)
{
@ -907,6 +918,7 @@ bool cpu_thread::check_state() noexcept
if (state0 & cpu_flag::memory)
{
vm::passive_lock(*this);
cpu_memory_checked = true;
continue;
}

View File

@ -70,5 +70,5 @@ void fmt_class_string<spu_mfc_cmd>::format(std::string& out, u64 arg)
const u8 tag = cmd.tag;
fmt::append(out, "%s #%02u 0x%05x:0x%08llx 0x%x%s", cmd.cmd, tag & 0x7f, cmd.lsa, u64{cmd.eah} << 32 | cmd.eal, cmd.size, (tag & 0x80) ? " (stalled)" : "");
fmt::append(out, "%-8s #%02u 0x%05x:0x%08llx 0x%x%s", cmd.cmd, tag & 0x7f, cmd.lsa, u64{cmd.eah} << 32 | cmd.eal, cmd.size, (tag & 0x80) ? " (stalled)" : "");
}

View File

@ -155,7 +155,7 @@ error_code select_photo(std::string dst_dir)
{
fs::stat_t f_info{};
if (!fs::stat(info.path, f_info) || f_info.is_directory)
if (!fs::get_stat(info.path, f_info) || f_info.is_directory)
{
cellPhotoImportUtil.error("Path does not belong to a valid file: '%s'", info.path);
result = CELL_PHOTO_IMPORT_ERROR_ACCESS_ERROR; // TODO: is this correct ?

View File

@ -1380,6 +1380,7 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
bool recreated = false;
lv2_sleep(ppu, 250);
ppu.state += cpu_flag::wait;
// Check if RPCS3_BLIST section exist in PARAM.SFO
// This section contains the list of files in the save ordered as they would be in BSD filesystem
@ -1397,7 +1398,7 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
}
fs::stat_t dir_info{};
if (!fs::stat(dir_path, dir_info))
if (!fs::get_stat(dir_path, dir_info))
{
// funcStat is called even if the directory doesn't exist.
}
@ -1523,6 +1524,7 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
// Stat Callback
funcStat(ppu, result, statGet, statSet);
ppu.state += cpu_flag::wait;
if (const s32 res = result->result; res != CELL_SAVEDATA_CBRESULT_OK_NEXT)
{
@ -1689,6 +1691,7 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
std::memset(result.get_ptr(), 0, ::offset32(&CellSaveDataCBResult::userdata));
funcFile(ppu, result, fileGet, fileSet);
ppu.state += cpu_flag::wait;
if (const s32 res = result->result; res != CELL_SAVEDATA_CBRESULT_OK_NEXT)
{
@ -2099,7 +2102,7 @@ static NEVER_INLINE error_code savedata_get_list_item(vm::cptr<char> dirName, vm
if (dir)
{
fs::stat_t dir_info{};
if (!fs::stat(save_path, dir_info))
if (!fs::get_stat(save_path, dir_info))
{
return CELL_SAVEDATA_ERROR_INTERNAL;
}
@ -2406,8 +2409,10 @@ error_code cellSaveDataFixedExport(ppu_thread& ppu, vm::cptr<char> dirName, u32
return CELL_OK;
}
error_code cellSaveDataGetListItem(vm::cptr<char> dirName, vm::ptr<CellSaveDataDirStat> dir, vm::ptr<CellSaveDataSystemFileParam> sysFileParam, vm::ptr<u32> bind, vm::ptr<u32> sizeKB)
error_code cellSaveDataGetListItem(ppu_thread& ppu, vm::cptr<char> dirName, vm::ptr<CellSaveDataDirStat> dir, vm::ptr<CellSaveDataSystemFileParam> sysFileParam, vm::ptr<u32> bind, vm::ptr<u32> sizeKB)
{
ppu.state += cpu_flag::wait;
cellSaveData.warning("cellSaveDataGetListItem(dirName=%s, dir=*0x%x, sysFileParam=*0x%x, bind=*0x%x, sizeKB=*0x%x)", dirName, dir, sysFileParam, bind, sizeKB);
return savedata_get_list_item(dirName, dir, sysFileParam, bind, sizeKB, 0);

View File

@ -244,6 +244,15 @@ void fmt_class_string<SceNpCommunicationSignature>::format(std::string& out, u64
fmt::append(out, "%s", sign.data);
}
template <>
void fmt_class_string<SceNpCommunicationId>::format(std::string& out, u64 arg)
{
const auto& id = get_object(arg);
const u8 term = id.data[9];
fmt::append(out, "{ data='%s', term='%s' (0x%x), num=%d, dummy=%d }", id.data, std::isprint(term) ? fmt::format("%c", term) : "", term, id.num, id.dummy);
}
// Helpers
static error_code NpTrophyGetTrophyInfo(const trophy_context_t* ctxt, s32 trophyId, SceNpTrophyDetails* details, SceNpTrophyData* data);
@ -442,27 +451,48 @@ error_code sceNpTrophyCreateContext(vm::ptr<u32> context, vm::cptr<SceNpCommunic
return SCE_NP_TROPHY_ERROR_NOT_SUPPORTED;
}
sceNpTrophy.warning("sceNpTrophyCreateContext(): commId = %s", *commId);
// rough checks for further fmt::format call
if (commId->num > 99)
const s32 comm_num = commId->num;
if (comm_num > 99)
{
return SCE_NP_TROPHY_ERROR_INVALID_NP_COMM_ID;
}
// NOTE: commId->term is unused in our code (at least until someone finds out if we need to account for it)
// generate trophy context name, limited to 9 characters
std::string_view name_sv(commId->data, 9);
// Generate trophy context name, limited to 9 characters
// Read once for thread-safety reasons
std::string name_str(commId->data, 9);
// resize the name if it was shorter than expected
if (const auto pos = name_sv.find_first_of('\0'); pos != std::string_view::npos)
if (const auto pos = name_str.find_first_of('\0'); pos != std::string_view::npos)
{
name_sv = name_sv.substr(0, pos);
name_str = name_str.substr(0, pos);
}
sceNpTrophy.warning("sceNpTrophyCreateContext(): data='%s' term=0x%x (0x%x) num=%d", name_sv, commId->data[9], commId->data[9], commId->num);
const SceNpCommunicationSignature commSign_data = *commSign;
if (read_from_ptr<be_t<u32>>(commSign_data.data, 0) != NP_TROPHY_COMM_SIGN_MAGIC)
{
return SCE_NP_TROPHY_ERROR_INVALID_NP_COMM_ID;
}
if (std::basic_string_view<u8>(&commSign_data.data[6], 6).find_first_not_of('\0') != umax)
{
// 6 padding bytes - must be 0
return SCE_NP_TROPHY_ERROR_INVALID_NP_COMM_ID;
}
if (read_from_ptr<be_t<u16>>(commSign_data.data, 4) != 0x100)
{
// Signifies version (1.00), although only one constant is allowed
return SCE_NP_TROPHY_ERROR_INVALID_NP_COMM_ID;
}
// append the commId number as "_xx"
std::string name = fmt::format("%s_%02d", name_sv, commId->num);
std::string name = fmt::format("%s_%02d", name_str, comm_num);
// create trophy context
const auto ctxt = idm::make_ptr<trophy_context_t>();
@ -546,12 +576,28 @@ error_code sceNpTrophyRegisterContext(ppu_thread& ppu, u32 context, u32 handle,
}
};
// TODO: Callbacks
// From RE-ing a game's state machine, it seems the possible order is one of the following:
// * Install (Not installed) - Setup - Progress * ? - Finalize - Complete - Installed
// * Reinstall (Corrupted) - Setup - Progress * ? - Finalize - Complete - Installed
// * Update (Required update) - Setup - Progress * ? - Finalize - Complete - Installed
// * Installed
// open trophy pack file
std::string trp_path = vfs::get(Emu.GetDir() + "TROPDIR/" + ctxt->trp_name + "/TROPHY.TRP");
fs::file stream(trp_path);
if (!stream && Emu.GetCat() == "GD")
{
sceNpTrophy.warning("sceNpTrophyRegisterContext failed to open trophy file from boot path: '%s' (%s)", trp_path, fs::g_tls_error);
trp_path = vfs::get("/dev_bdvd/PS3_GAME/TROPDIR/" + ctxt->trp_name + "/TROPHY.TRP");
stream.open(trp_path);
}
// check if exists and opened
if (!stream)
{
const std::string msg = fmt::format("Failed to open trophy file: '%s' (%s)", trp_path, fs::g_tls_error);
return {SCE_NP_TROPHY_ERROR_CONF_DOES_NOT_EXIST, msg};
}
// TODO:
// SCE_NP_TROPHY_STATUS_DATA_CORRUPT -> reinstall
// SCE_NP_TROPHY_STATUS_REQUIRES_UPDATE -> reinstall (for example if a patch has updates for the trophy data)
// SCE_NP_TROPHY_STATUS_CHANGES_DETECTED -> reinstall (only possible in dev mode)
const std::string trophyPath = "/dev_hdd0/home/" + Emu.GetUsr() + "/trophy/" + ctxt->trp_name;
const s32 trp_status = fs::is_dir(vfs::get(trophyPath)) ? SCE_NP_TROPHY_STATUS_INSTALLED : SCE_NP_TROPHY_STATUS_NOT_INSTALLED;
@ -560,6 +606,7 @@ error_code sceNpTrophyRegisterContext(ppu_thread& ppu, u32 context, u32 handle,
sceNpTrophy.notice("sceNpTrophyRegisterContext(): Callback is being called (trp_status=%u)", trp_status);
// "Ask permission" to install the trophy data.
// The callback is called once and then if it returns >= 0 the cb is called through events(coming from vsh) that are passed to the CB through cellSysutilCheckCallback
if (statusCb(ppu, context, trp_status, 0, 0, arg) < 0)
{
@ -599,24 +646,6 @@ error_code sceNpTrophyRegisterContext(ppu_thread& ppu, u32 context, u32 handle,
return SCE_NP_TROPHY_ERROR_UNKNOWN_HANDLE;
}
// open trophy pack file
std::string trp_path = vfs::get(Emu.GetDir() + "TROPDIR/" + ctxt->trp_name + "/TROPHY.TRP");
fs::file stream(trp_path);
if (!stream && Emu.GetCat() == "GD")
{
sceNpTrophy.warning("sceNpTrophyRegisterContext failed to open trophy file from boot path: '%s' (%s)", trp_path, fs::g_tls_error);
trp_path = vfs::get("/dev_bdvd/PS3_GAME/TROPDIR/" + ctxt->trp_name + "/TROPHY.TRP");
stream.open(trp_path);
}
// check if exists and opened
if (!stream)
{
const std::string msg = fmt::format("Failed to open trophy file: '%s' (%s)", trp_path, fs::g_tls_error);
return {SCE_NP_TROPHY_ERROR_CONF_DOES_NOT_EXIST, msg};
}
TRPLoader trp(stream);
if (!trp.LoadHeader())
{
@ -696,6 +725,7 @@ error_code sceNpTrophyRegisterContext(ppu_thread& ppu, u32 context, u32 handle,
{
sysutil_register_cb([statusCb, status, context, completed, arg, wkptr](ppu_thread& cb_ppu) -> s32
{
// TODO: it is possible that we need to check the return value here as well.
statusCb(cb_ppu, context, status.first, completed, status.second, arg);
const auto queued = wkptr.lock();

View File

@ -151,6 +151,11 @@ enum
SCE_NP_TROPHY_STATUS_CHANGES_DETECTED = 9,
};
enum : u32
{
NP_TROPHY_COMM_SIGN_MAGIC = 0xB9DDE13B,
};
using SceNpTrophyStatusCallback = s32(u32 context, u32 status, s32 completed, s32 total, vm::ptr<void> arg);
// Forward declare this function since trophyunlock needs it

View File

@ -3847,16 +3847,24 @@ void spu_recompiler_base::dump(const spu_program& result, std::string& out)
fmt::append(hash, "%s", fmt::base57(output));
}
fmt::append(out, "========== SPU BLOCK 0x%05x (size %u, %s) ==========\n", result.entry_point, result.data.size(), hash);
fmt::append(out, "========== SPU BLOCK 0x%05x (size %u, %s) ==========\n\n", result.entry_point, result.data.size(), hash);
for (auto& bb : m_bbs)
{
for (u32 pos = bb.first, end = bb.first + bb.second.size * 4; pos < end; pos += 4)
{
dis_asm.disasm(pos);
fmt::append(out, ">%s\n", dis_asm.last_opcode);
if (!dis_asm.last_opcode.ends_with('\n'))
{
dis_asm.last_opcode += '\n';
}
fmt::append(out, ">%s", dis_asm.last_opcode);
}
out += '\n';
if (m_block_info[bb.first / 4])
{
fmt::append(out, "A: [0x%05x] %s\n", bb.first, m_entry_info[bb.first / 4] ? (m_ret_info[bb.first / 4] ? "Chunk" : "Entry") : "Block");
@ -5823,7 +5831,7 @@ public:
if (g_cfg.core.spu_debug)
{
out.flush();
fs::file(m_spurt->get_cache_path() + "spu-ir.log", fs::write + fs::append).write(log);
fs::write_file(m_spurt->get_cache_path() + "spu-ir.log", fs::create + fs::write + fs::append, log);
}
#if defined(__APPLE__)
@ -6241,7 +6249,7 @@ public:
if (g_cfg.core.spu_debug)
{
fs::file(m_spurt->get_cache_path() + "spu-ir.log", fs::write + fs::append).write(log);
fs::write_file(m_spurt->get_cache_path() + "spu-ir.log", fs::create + fs::write + fs::append, log);
}
fmt::throw_exception("Compilation failed");
@ -6276,7 +6284,7 @@ public:
if (g_cfg.core.spu_debug)
{
out.flush();
fs::file(m_spurt->get_cache_path() + "spu-ir.log", fs::write + fs::append).write(log);
fs::write_file(m_spurt->get_cache_path() + "spu-ir.log", fs::create + fs::write + fs::append, log);
}
return spu_runtime::g_interpreter;
@ -11243,7 +11251,7 @@ struct spu_fast : public spu_recompiler_base
{
std::string log;
this->dump(func, log);
fs::file(m_spurt->get_cache_path() + "spu.log", fs::write + fs::append).write(log);
fs::write_file(m_spurt->get_cache_path() + "spu.log", fs::create + fs::write + fs::append, log);
}
// Allocate executable area with necessary size

View File

@ -326,6 +326,29 @@ extern thread_local u64 g_tls_fault_spu;
const spu_decoder<spu_itype> s_spu_itype;
namespace vm
{
extern atomic_t<u64, 64> g_range_lock_set[64];
// Defined here for performance reasons
writer_lock::~writer_lock() noexcept
{
if (range_lock)
{
if (!*range_lock)
{
return;
}
g_range_lock_bits[1] &= ~(1ull << (range_lock - g_range_lock_set));
range_lock->release(0);
return;
}
g_range_lock_bits[1].release(0);
}
}
namespace spu
{
namespace scheduler
@ -3548,19 +3571,18 @@ bool spu_thread::do_putllc(const spu_mfc_cmd& args)
{
// Full lock (heavyweight)
// TODO: vm::check_addr
vm::writer_lock lock(addr);
vm::writer_lock lock(addr, range_lock);
if (cmp_rdata(rdata, super_data))
{
mov_rdata(super_data, to_write);
res += 64;
return true;
}
res -= 64;
return false;
}();
res += success ? 64 : 0 - 64;
return success;
}())
{
@ -3695,7 +3717,8 @@ void do_cell_atomic_128_store(u32 addr, const void* to_write)
vm::_ref<atomic_t<u32>>(addr) += 0;
// Hard lock
vm::writer_lock lock(addr);
auto spu = cpu ? cpu->try_get<spu_thread>() : nullptr;
vm::writer_lock lock(addr, spu ? spu->range_lock : nullptr);
mov_rdata(sdata, *static_cast<const spu_rdata_t*>(to_write));
vm::reservation_acquire(addr) += 32;
}
@ -4461,7 +4484,7 @@ bool spu_thread::reservation_check(u32 addr, const decltype(rdata)& data) const
// Set range_lock first optimistically
range_lock->store(u64{128} << 32 | addr);
u64 lock_val = vm::g_range_lock;
u64 lock_val = *std::prev(std::end(vm::g_range_lock_set));
u64 old_lock = 0;
while (lock_val != old_lock)
@ -4516,7 +4539,7 @@ bool spu_thread::reservation_check(u32 addr, const decltype(rdata)& data) const
break;
}
old_lock = std::exchange(lock_val, vm::g_range_lock);
old_lock = std::exchange(lock_val, *std::prev(std::end(vm::g_range_lock_set)));
}
if (!range_lock->load()) [[unlikely]]

View File

@ -1488,7 +1488,7 @@ error_code sys_fs_stat(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<CellFsStat>
fs::stat_t info{};
if (!fs::stat(local_path, info))
if (!fs::get_stat(local_path, info))
{
switch (auto error = fs::g_tls_error)
{
@ -1499,7 +1499,7 @@ error_code sys_fs_stat(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<CellFsStat>
for (u32 i = 66601; i <= 66699; i++)
{
if (fs::stat(fmt::format("%s.%u", local_path, i), info) && !info.is_directory)
if (fs::get_stat(fmt::format("%s.%u", local_path, i), info) && !info.is_directory)
{
total_size += info.size;
}
@ -1510,7 +1510,7 @@ error_code sys_fs_stat(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<CellFsStat>
}
// Use attributes from the first fragment (consistently with sys_fs_open+fstat)
if (fs::stat(local_path + ".66600", info) && !info.is_directory)
if (fs::get_stat(local_path + ".66600", info) && !info.is_directory)
{
// Success
info.size += total_size;
@ -2064,27 +2064,18 @@ error_code sys_fs_fcntl(ppu_thread& ppu, u32 fd, u32 op, vm::ptr<void> _arg, u32
{
const auto arg = vm::static_ptr_cast<lv2_file_c0000007>(_arg);
std::string_view device{arg->device.get_ptr(), arg->device_size};
// Trim trailing '\0'
if (const auto trim_pos = device.find('\0'); trim_pos != umax)
device.remove_suffix(device.size() - trim_pos);
if (device != "CELL_FS_IOS:ATA_HDD"sv)
{
arg->out_code = CELL_ENOTSUP;
return {CELL_ENOTSUP, device};
}
const auto model = g_cfg.sys.hdd_model.to_string();
const auto serial = g_cfg.sys.hdd_serial.to_string();
strcpy_trunc(std::span(arg->model.get_ptr(), arg->model_size), model);
strcpy_trunc(std::span(arg->serial.get_ptr(), arg->serial_size), serial);
arg->out_code = CELL_OK;
sys_fs.trace("sys_fs_fcntl(0xc0000007): found device \"%s\" (model=\"%s\", serial=\"%s\")", device, model, serial);
if (const auto size = arg->model_size; size > 0)
strcpy_trunc(std::span(arg->model.get_ptr(), size),
fmt::format("%-*s", size - 1, g_cfg.sys.hdd_model.to_string())); // Example: "TOSHIBA MK3265GSX H "
if (const auto size = arg->serial_size; size > 0)
strcpy_trunc(std::span(arg->serial.get_ptr(), size),
fmt::format("%*s", size - 1, g_cfg.sys.hdd_serial.to_string())); // Example: " 0A1B2C3D4"
else
return CELL_EFAULT; // CELL_EFAULT is returned only when arg->serial_size == 0
return CELL_OK;
}
@ -2190,39 +2181,37 @@ error_code sys_fs_fcntl(ppu_thread& ppu, u32 fd, u32 op, vm::ptr<void> _arg, u32
break;
}
std::string_view vpath{arg->name.get_ptr(), arg->name_size};
std::string_view vpath{arg->path.get_ptr(), arg->path_size};
if (vpath.size() == 0)
return CELL_ENOMEM;
// Trim trailing '\0'
if (const auto trim_pos = vpath.find('\0'); trim_pos != umax)
vpath.remove_suffix(vpath.size() - trim_pos);
if (vfs::get(vpath).empty())
arg->out_code = CELL_ENOTMOUNTED; // arg->out_code is set to CELL_ENOTMOUNTED on real hardware when the device doesn't exist or when the device isn't USB
if (!vfs::get(vpath).empty())
{
arg->out_code = CELL_ENOTMOUNTED;
return {CELL_ENOTMOUNTED, vpath};
if (const auto& mp = g_fxo->get<lv2_fs_mount_info_map>().lookup(vpath, true); mp == &g_mp_sys_dev_usb)
{
const cfg::device_info device = g_cfg_vfs.get_device(g_cfg_vfs.dev_usb, fmt::format("%s%s", mp->root, mp.device.substr(mp->device.size())));
const auto usb_ids = device.get_usb_ids();
std::tie(arg->vendorID, arg->productID) = usb_ids;
if (with_serial)
{
const auto arg_c000001c = vm::static_ptr_cast<lv2_file_c000001c>(_arg);
const std::u16string serial = utf8_to_utf16(device.serial); // Serial needs to be encoded to utf-16 BE
std::copy_n(serial.begin(), std::min(serial.size(), sizeof(arg_c000001c->serial) / sizeof(u16)), arg_c000001c->serial);
}
arg->out_code = CELL_OK;
sys_fs.trace("sys_fs_fcntl(0x%08x): found device \"%s\" (vid=0x%04x, pid=0x%04x, serial=\"%s\")", op, mp.device, usb_ids.first, usb_ids.second, device.serial);
}
}
const auto& mp = g_fxo->get<lv2_fs_mount_info_map>().lookup(vpath, true);
if (mp != &g_mp_sys_dev_usb)
{
arg->out_code = CELL_ENOTSUP;
return {CELL_ENOTSUP, vpath};
}
const cfg::device_info device = g_cfg_vfs.get_device(g_cfg_vfs.dev_usb, fmt::format("%s%s", mp->root, mp.device.substr(mp->device.size())));
std::tie(arg->vendorID, arg->productID) = device.get_usb_ids();
if (with_serial)
{
const auto arg_c000001c = vm::static_ptr_cast<lv2_file_c000001c>(_arg);
const std::u16string serial = utf8_to_utf16(device.serial); // Serial needs to be encoded to utf-16 BE
std::copy_n(serial.begin(), std::min(serial.size(), sizeof(arg_c000001c->serial) / sizeof(u16)), arg_c000001c->serial);
}
arg->out_code = CELL_OK;
sys_fs.trace("sys_fs_fcntl(0x%08x): found device \"%s\" (vid=0x%04x, pid=0x%04x, serial=\"%s\")", op, mp.device, arg->vendorID, arg->productID, device.serial);
return CELL_OK;
}

View File

@ -529,8 +529,8 @@ CHECK_SIZE(lv2_file_c0000006, 0x20);
// sys_fs_fcntl: cellFsArcadeHddSerialNumber
struct lv2_file_c0000007 : lv2_file_op
{
be_t<u32> out_code;
vm::bcptr<char> device;
be_t<u32> out_code; // set to 0
vm::bcptr<char> device; // CELL_FS_IOS:ATA_HDD
be_t<u32> device_size; // 0x14
vm::bptr<char> model;
be_t<u32> model_size; // 0x29
@ -562,8 +562,8 @@ struct lv2_file_c0000015 : lv2_file_op
be_t<u32> size; // 0x20
be_t<u32> _x4; // 0x10
be_t<u32> _x8; // 0x18 - offset of out_code
be_t<u32> name_size;
vm::bcptr<char> name;
be_t<u32> path_size;
vm::bcptr<char> path;
be_t<u32> _x14; //
be_t<u16> vendorID;
be_t<u16> productID;
@ -590,8 +590,8 @@ struct lv2_file_c000001c : lv2_file_op
be_t<u32> size; // 0x60
be_t<u32> _x4; // 0x10
be_t<u32> _x8; // 0x18 - offset of out_code
be_t<u32> name_size;
vm::bcptr<char> name;
be_t<u32> path_size;
vm::bcptr<char> path;
be_t<u32> unk1;
be_t<u16> vendorID;
be_t<u16> productID;

View File

@ -79,15 +79,7 @@ constexpr u32 c_max_ppu_name_size = 28;
void _sys_ppu_thread_exit(ppu_thread& ppu, u64 errorcode)
{
ppu.state += cpu_flag::wait;
// Need to wait until the current writer finish
if (ppu.state & cpu_flag::memory)
{
while (vm::g_range_lock)
{
busy_wait(200);
}
}
u64 writer_mask = 0;
sys_ppu_thread.trace("_sys_ppu_thread_exit(errorcode=0x%llx)", errorcode);
@ -126,6 +118,9 @@ void _sys_ppu_thread_exit(ppu_thread& ppu, u64 errorcode)
old_ppu = g_fxo->get<ppu_thread_cleaner>().clean(std::move(idm::find_unlocked<named_thread<ppu_thread>>(ppu.id)->second));
}
// Get writers mask (wait for all current writers to quit)
writer_mask = vm::g_range_lock_bits[1];
// Unqueue
lv2_obj::sleep(ppu);
notify.cleanup();
@ -154,6 +149,15 @@ void _sys_ppu_thread_exit(ppu_thread& ppu, u64 errorcode)
// It is detached from IDM now so join must be done explicitly now
*static_cast<named_thread<ppu_thread>*>(old_ppu.get()) = thread_state::finished;
}
// Need to wait until the current writers finish
if (ppu.state & cpu_flag::memory)
{
for (; writer_mask; writer_mask &= vm::g_range_lock_bits[1])
{
busy_wait(200);
}
}
}
s32 sys_ppu_thread_yield(ppu_thread& ppu)

View File

@ -92,6 +92,8 @@ std::string dump_useful_thread_info();
error_code sys_tty_write([[maybe_unused]] ppu_thread& ppu, s32 ch, vm::cptr<char> buf, u32 len, vm::ptr<u32> pwritelen)
{
ppu.state += cpu_flag::wait;
sys_tty.notice("sys_tty_write(ch=%d, buf=*0x%x, len=%d, pwritelen=*0x%x)", ch, buf, len, pwritelen);
std::string msg;

View File

@ -334,7 +334,7 @@ void usb_device_usio::translate_input_tekken()
}
break;
case usio_btn::down:
if ( pressed)
if (pressed)
{
digital_input |= 0x100000ULL << shift;
if (player == 0)

View File

@ -70,14 +70,16 @@ namespace vm
// Memory mutex acknowledgement
thread_local atomic_t<cpu_thread*>* g_tls_locked = nullptr;
// "Unique locked" range lock, as opposed to "shared" range locks from set
atomic_t<u64> g_range_lock = 0;
// Memory mutex: passive locks
std::array<atomic_t<cpu_thread*>, g_cfg.core.ppu_threads.max> g_locks{};
// Range lock slot allocation bits
atomic_t<u64> g_range_lock_bits{};
atomic_t<u64, 64> g_range_lock_bits[2]{};
auto& get_range_lock_bits(bool is_exclusive_range)
{
return g_range_lock_bits[+is_exclusive_range];
}
// Memory range lock slots (sparse atomics)
atomic_t<u64, 64> g_range_lock_set[64]{};
@ -138,9 +140,10 @@ namespace vm
atomic_t<u64, 64>* alloc_range_lock()
{
const auto [bits, ok] = g_range_lock_bits.fetch_op([](u64& bits)
const auto [bits, ok] = get_range_lock_bits(false).fetch_op([](u64& bits)
{
if (~bits) [[likely]]
// MSB is reserved for locking with memory setting changes
if ((~(bits | (bits + 1))) << 1) [[likely]]
{
bits |= bits + 1;
return true;
@ -157,6 +160,9 @@ namespace vm
return &g_range_lock_set[std::countr_one(bits)];
}
template <typename F>
static u64 for_all_range_locks(u64 input, F func);
void range_lock_internal(atomic_t<u64, 64>* range_lock, u32 begin, u32 size)
{
perf_meter<"RHW_LOCK"_u64> perf0(0);
@ -168,32 +174,44 @@ namespace vm
range_lock->store(to_store);
}
for (u64 i = 0;; i++)
for (u64 i = 0, to_clear = umax;; i++)
{
const u64 lock_val = g_range_lock.load();
const u64 is_share = g_shmem[begin >> 16].load();
to_clear &= get_range_lock_bits(true);
u64 lock_addr = static_cast<u32>(lock_val); // -> u64
u32 lock_size = static_cast<u32>(lock_val << range_bits >> (range_bits + 32));
u64 addr = begin;
if ((lock_val & range_full_mask) == range_locked) [[likely]]
const u64 busy = for_all_range_locks(to_clear, [&](u64 addr_exec, u32 size_exec)
{
lock_size = 128;
u64 addr = begin;
if (is_share)
if ((size_exec & (range_full_mask >> 32)) == (range_locked >> 32)) [[likely]]
{
addr = static_cast<u16>(addr) | is_share;
lock_addr = lock_val;
size_exec = 128;
if (is_share)
{
addr = static_cast<u16>(addr) | is_share;
}
}
}
if (addr + size <= lock_addr || addr >= lock_addr + lock_size) [[likely]]
size_exec = (size_exec << range_bits) >> range_bits;
// TODO (currently not possible): handle 2 64K pages (inverse range), or more pages
if (u64 is_shared = g_shmem[addr_exec >> 16]) [[unlikely]]
{
addr_exec = static_cast<u16>(addr_exec) | is_shared;
}
if (addr <= addr_exec + size_exec - 1 && addr_exec <= addr + size - 1) [[unlikely]]
{
return 1;
}
return 0;
});
if (!busy) [[likely]]
{
const u64 new_lock_val = g_range_lock.load();
if (vm::check_addr(begin, vm::page_readable, size) && (!new_lock_val || new_lock_val == lock_val)) [[likely]]
if (vm::check_addr(begin, vm::page_readable, size)) [[likely]]
{
break;
}
@ -265,7 +283,7 @@ namespace vm
// Use ptr difference to determine location
const auto diff = range_lock - g_range_lock_set;
g_range_lock_bits &= ~(1ull << diff);
g_range_lock_bits[0] &= ~(1ull << diff);
}
template <typename F>
@ -295,13 +313,13 @@ namespace vm
return result;
}
static void _lock_main_range_lock(u64 flags, u32 addr, u32 size)
static atomic_t<u64, 64>* _lock_main_range_lock(u64 flags, u32 addr, u32 size)
{
// Shouldn't really happen
if (size == 0)
{
vm_log.warning("Tried to lock empty range (flags=0x%x, addr=0x%x)", flags >> 32, addr);
return;
return {};
}
// Limit to <512 MiB at once; make sure if it operates on big amount of data, it's page-aligned
@ -311,7 +329,8 @@ namespace vm
}
// Block or signal new range locks
g_range_lock = addr | u64{size} << 32 | flags;
auto range_lock = &*std::prev(std::end(vm::g_range_lock_set));
*range_lock = addr | u64{size} << 32 | flags;
utils::prefetch_read(g_range_lock_set + 0);
utils::prefetch_read(g_range_lock_set + 2);
@ -319,7 +338,7 @@ namespace vm
const auto range = utils::address_range::start_length(addr, size);
u64 to_clear = g_range_lock_bits.load();
u64 to_clear = get_range_lock_bits(false).load();
while (to_clear)
{
@ -340,22 +359,21 @@ namespace vm
utils::pause();
}
return range_lock;
}
void passive_lock(cpu_thread& cpu)
{
ensure(cpu.state & cpu_flag::wait);
bool ok = true;
if (!g_tls_locked || *g_tls_locked != &cpu) [[unlikely]]
{
_register_lock(&cpu);
if (cpu.state & cpu_flag::memory) [[likely]]
{
cpu.state -= cpu_flag::memory;
}
if (!g_range_lock)
if (!get_range_lock_bits(true))
{
return;
}
@ -367,19 +385,18 @@ namespace vm
{
for (u64 i = 0;; i++)
{
if (cpu.is_paused())
{
// Assume called from cpu_thread::check_state(), it can handle the pause flags better
return;
}
if (i < 100)
busy_wait(200);
else
std::this_thread::yield();
if (g_range_lock)
{
continue;
}
cpu.state -= cpu_flag::memory;
if (!g_range_lock) [[likely]]
if (!get_range_lock_bits(true)) [[likely]]
{
return;
}
@ -427,18 +444,22 @@ namespace vm
}
}
writer_lock::writer_lock()
: writer_lock(0, 1)
writer_lock::writer_lock() noexcept
: writer_lock(0, nullptr, 1)
{
}
writer_lock::writer_lock(u32 const addr, u32 const size, u64 const flags)
writer_lock::writer_lock(u32 const addr, atomic_t<u64, 64>* range_lock, u32 const size, u64 const flags) noexcept
: range_lock(range_lock)
{
auto cpu = get_current_cpu_thread();
cpu_thread* cpu{};
if (cpu)
if (g_tls_locked)
{
if (!g_tls_locked || *g_tls_locked != cpu || cpu->state & cpu_flag::wait)
cpu = get_current_cpu_thread();
AUDIT(cpu);
if (*g_tls_locked != cpu || cpu->state & cpu_flag::wait)
{
cpu = nullptr;
}
@ -448,18 +469,51 @@ namespace vm
}
}
bool to_prepare_memory = addr >= 0x10000;
for (u64 i = 0;; i++)
{
if (g_range_lock || !g_range_lock.compare_and_swap_test(0, addr | u64{size} << 32 | flags))
auto& bits = get_range_lock_bits(true);
if (!range_lock || addr < 0x10000)
{
if (i < 100)
busy_wait(200);
else
std::this_thread::yield();
if (!bits && bits.compare_and_swap_test(0, u64{umax}))
{
break;
}
}
else
{
break;
range_lock->release(addr | u64{size} << 32 | flags);
const auto diff = range_lock - g_range_lock_set;
if (bits != umax && !bits.bit_test_set(diff))
{
break;
}
range_lock->release(0);
}
if (i < 100)
{
if (to_prepare_memory)
{
// We have some spare time, prepare cache lines (todo: reservation tests here)
utils::prefetch_write(vm::get_super_ptr(addr));
utils::prefetch_write(vm::get_super_ptr(addr) + 64);
to_prepare_memory = false;
}
busy_wait(200);
}
else
{
std::this_thread::yield();
// Thread may have been switched or the cache clue has been undermined, cache needs to be prapred again
to_prepare_memory = true;
}
}
@ -469,7 +523,7 @@ namespace vm
for (auto lock = g_locks.cbegin(), end = lock + g_cfg.core.ppu_threads; lock != end; lock++)
{
if (auto ptr = +*lock; ptr && !(ptr->state & cpu_flag::memory))
if (auto ptr = +*lock; ptr && ptr->state.none_of(cpu_flag::wait + cpu_flag::memory))
{
ptr->state.test_and_set(cpu_flag::memory);
}
@ -487,13 +541,13 @@ namespace vm
utils::prefetch_read(g_range_lock_set + 2);
utils::prefetch_read(g_range_lock_set + 4);
u64 to_clear = g_range_lock_bits.load();
u64 to_clear = get_range_lock_bits(false);
u64 point = addr1 / 128;
while (true)
{
to_clear = for_all_range_locks(to_clear, [&](u64 addr2, u32 size2)
to_clear = for_all_range_locks(to_clear & ~get_range_lock_bits(true), [&](u64 addr2, u32 size2)
{
// Split and check every 64K page separately
for (u64 hi = addr2 >> 16, max = (addr2 + size2 - 1) >> 16; hi <= max; hi++)
@ -523,6 +577,13 @@ namespace vm
break;
}
if (to_prepare_memory)
{
utils::prefetch_write(vm::get_super_ptr(addr));
utils::prefetch_write(vm::get_super_ptr(addr) + 64);
to_prepare_memory = false;
}
utils::pause();
}
@ -532,6 +593,13 @@ namespace vm
{
while (!(ptr->state & cpu_flag::wait))
{
if (to_prepare_memory)
{
utils::prefetch_write(vm::get_super_ptr(addr));
utils::prefetch_write(vm::get_super_ptr(addr) + 64);
to_prepare_memory = false;
}
utils::pause();
}
}
@ -544,11 +612,6 @@ namespace vm
}
}
writer_lock::~writer_lock()
{
g_range_lock = 0;
}
u64 reservation_lock_internal(u32 addr, atomic_t<u64>& res)
{
for (u64 i = 0;; i++)
@ -672,7 +735,7 @@ namespace vm
const bool is_noop = bflags & page_size_4k && utils::c_page_size > 4096;
// Lock range being mapped
_lock_main_range_lock(range_allocation, addr, size);
auto range_lock = _lock_main_range_lock(range_allocation, addr, size);
if (shm && shm->flags() != 0 && shm->info++)
{
@ -788,6 +851,8 @@ namespace vm
fmt::throw_exception("Concurrent access (addr=0x%x, size=0x%x, flags=0x%x, current_addr=0x%x)", addr, size, flags, i * 4096);
}
}
range_lock->release(0);
}
bool page_protect(u32 addr, u32 size, u8 flags_test, u8 flags_set, u8 flags_clear)
@ -845,7 +910,7 @@ namespace vm
safe_bits |= range_writable;
// Protect range locks from observing changes in memory protection
_lock_main_range_lock(safe_bits, start * 4096, page_size);
auto range_lock = _lock_main_range_lock(safe_bits, start * 4096, page_size);
for (u32 j = start; j < i; j++)
{
@ -857,6 +922,8 @@ namespace vm
const auto protection = start_value & page_writable ? utils::protection::rw : (start_value & page_readable ? utils::protection::ro : utils::protection::no);
utils::memory_protect(g_base_addr + start * 4096, page_size, protection);
}
range_lock->release(0);
}
start_value = new_val;
@ -904,7 +971,7 @@ namespace vm
}
// Protect range locks from actual memory protection changes
_lock_main_range_lock(range_allocation, addr, size);
auto range_lock = _lock_main_range_lock(range_allocation, addr, size);
if (shm && shm->flags() != 0 && g_shmem[addr >> 16])
{
@ -965,6 +1032,7 @@ namespace vm
}
}
range_lock->release(0);
return size;
}
@ -1966,11 +2034,13 @@ namespace vm
{
auto* range_lock = alloc_range_lock(); // Released at the end of function
range_lock->store(begin | (u64{size} << 32));
auto mem_lock = &*std::prev(std::end(vm::g_range_lock_set));
while (true)
{
const u64 lock_val = g_range_lock.load();
range_lock->store(begin | (u64{size} << 32));
const u64 lock_val = mem_lock->load();
const u64 is_share = g_shmem[begin >> 16].load();
u64 lock_addr = static_cast<u32>(lock_val); // -> u64
@ -1993,7 +2063,7 @@ namespace vm
{
if (vm::check_addr(begin, is_write ? page_writable : page_readable, size)) [[likely]]
{
const u64 new_lock_val = g_range_lock.load();
const u64 new_lock_val = mem_lock->load();
if (!new_lock_val || new_lock_val == lock_val) [[likely]]
{
@ -2026,8 +2096,6 @@ namespace vm
range_lock->release(0);
busy_wait(200);
range_lock->store(begin | (u64{size} << 32));
}
const bool result = try_access_internal(begin, ptr, size, is_write);
@ -2071,7 +2139,7 @@ namespace vm
std::memset(g_reservations, 0, sizeof(g_reservations));
std::memset(g_shmem, 0, sizeof(g_shmem));
std::memset(g_range_lock_set, 0, sizeof(g_range_lock_set));
g_range_lock_bits = 0;
std::memset(g_range_lock_bits, 0, sizeof(g_range_lock_bits));
#ifdef _WIN32
utils::memory_release(g_hook_addr, 0x800000000);
@ -2104,7 +2172,7 @@ namespace vm
#endif
std::memset(g_range_lock_set, 0, sizeof(g_range_lock_set));
g_range_lock_bits = 0;
std::memset(g_range_lock_bits, 0, sizeof(g_range_lock_bits));
}
void save(utils::serial& ar)
@ -2209,8 +2277,6 @@ namespace vm
loc = std::make_shared<block_t>(ar, shared);
}
}
g_range_lock = 0;
}
u32 get_shm_addr(const std::shared_ptr<utils::shm>& shared)

View File

@ -27,7 +27,7 @@ namespace vm
range_bits = 3,
};
extern atomic_t<u64> g_range_lock;
extern atomic_t<u64, 64> g_range_lock_bits[2];
extern atomic_t<u64> g_shmem[];
@ -61,7 +61,7 @@ namespace vm
__asm__(""); // Tiny barrier
#endif
if (!g_range_lock)
if (!g_range_lock_bits[1]) [[likely]]
{
return;
}
@ -82,10 +82,12 @@ namespace vm
struct writer_lock final
{
atomic_t<u64, 64>* range_lock;
writer_lock(const writer_lock&) = delete;
writer_lock& operator=(const writer_lock&) = delete;
writer_lock();
writer_lock(u32 addr, u32 size = 0, u64 flags = range_locked);
~writer_lock();
writer_lock() noexcept;
writer_lock(u32 addr, atomic_t<u64, 64>* range_lock = nullptr, u32 size = 128, u64 flags = range_locked) noexcept;
~writer_lock() noexcept;
};
} // namespace vm

View File

@ -296,12 +296,12 @@ namespace rsx
// Writeback to cache either if file does not exist or it is invalid (unexpected size)
// Note: fs::write_file is not atomic, if the process is terminated in the middle an empty file is created
if (fs::stat_t s{}; !fs::stat(fp_name, s) || s.size != fp.ucode_length)
if (fs::stat_t s{}; !fs::get_stat(fp_name, s) || s.size != fp.ucode_length)
{
fs::write_file(fp_name, fs::rewrite, fp.get_data(), fp.ucode_length);
}
if (fs::stat_t s{}; !fs::stat(vp_name, s) || s.size != vp.data.size() * sizeof(u32))
if (fs::stat_t s{}; !fs::get_stat(vp_name, s) || s.size != vp.data.size() * sizeof(u32))
{
fs::write_file(vp_name, fs::rewrite, vp.data);
}

View File

@ -277,7 +277,7 @@ void Emulator::Init()
const std::string cfg_path = fs::get_config_dir() + "/config.yml";
// Save new global config if it doesn't exist or is empty
if (fs::stat_t info{}; !fs::stat(cfg_path, info) || info.size == 0)
if (fs::stat_t info{}; !fs::get_stat(cfg_path, info) || info.size == 0)
{
Emulator::SaveSettings(g_cfg_defaults, {});
}
@ -2125,6 +2125,8 @@ void Emulator::Run(bool start_playtime)
m_pause_start_time = 0;
m_pause_amend_time = 0;
m_tty_file_init_pos = g_tty ? g_tty.pos() : usz{umax};
rpcs3::utils::configure_logs();
m_state = system_state::starting;
@ -2897,6 +2899,8 @@ void Emulator::Kill(bool allow_autoexit, bool savestate)
// Final termination from main thread (move the last ownership of join thread in order to destroy it)
CallFromMainThread([join_thread = std::move(join_thread), allow_autoexit, this]() mutable
{
const std::string cache_path = rpcs3::cache::get_ppu_cache();
cpu_thread::cleanup();
initialize_timebased_time(0, true);
@ -2957,6 +2961,64 @@ void Emulator::Kill(bool allow_autoexit, bool savestate)
m_state = system_state::stopped;
GetCallbacks().on_stop();
if (g_tty && sys_log.notice)
{
// Write merged TTY output after emulation has been safely stopped
if (usz attempted_read_size = utils::sub_saturate<usz>(g_tty.pos(), m_tty_file_init_pos))
{
if (fs::file tty_read_fd{fs::get_cache_dir() + "TTY.log"})
{
// Enfore an arbitrary limit for now to avoid OOM in case the guest code has bombarded TTY
// 16MB, this should be enough
constexpr usz c_max_tty_spill_size = 0x10'0000;
std::string tty_buffer(std::min<usz>(attempted_read_size, c_max_tty_spill_size), '\0');
tty_buffer.resize(tty_read_fd.read_at(m_tty_file_init_pos, tty_buffer.data(), tty_buffer.size()));
tty_read_fd.close();
if (!tty_buffer.empty())
{
// Mark start and end very clearly with RPCS3 put in it
sys_log.notice("\nAccumulated RPCS3 TTY:\n\n\n%s\n\n\nEnd RPCS3 TTY Section.\n", tty_buffer);
}
}
}
}
if (g_cfg.core.spu_debug && sys_log.notice)
{
if (fs::file spu_log{cache_path + "/spu.log"})
{
// 96MB limit, this may be a lot but this only has an effect when enabling the debug option
constexpr usz c_max_tty_spill_size = 0x60'0000;
std::string log_buffer(std::min<usz>(spu_log.size(), c_max_tty_spill_size), '\0');
log_buffer.resize(spu_log.read(log_buffer.data(), log_buffer.size()));
spu_log.close();
if (!log_buffer.empty())
{
usz to_remove = 0;
usz part_ctr = 1;
for (std::string_view not_logged = log_buffer; !not_logged.empty(); part_ctr++, not_logged.remove_prefix(to_remove))
{
std::string_view to_log = not_logged;
to_log = to_log.substr(0, 0x8'0000);
to_log = to_log.substr(0, utils::add_saturate<usz>(to_log.rfind("\n========== SPU BLOCK"sv), 1));
to_remove = to_log.size();
// Cannot log it all at once due to technical reasons, split it to 8MB at maximum of whole functions
// Assume the block prefix exists because it is created by RPCS3 (or log it in an ugly manner if it does not exist)
sys_log.notice("Logging spu.log part %u:\n\n%s\n", part_ctr, to_log);
}
sys_log.notice("End spu.log");
}
}
}
// Always Enable display sleep, not only if it was prevented.
enable_display_sleep();

View File

@ -139,6 +139,8 @@ class Emulator final
bool m_state_inspection_savestate = false;
usz m_tty_file_init_pos = umax;
std::vector<std::shared_ptr<atomic_t<u32>>> m_pause_msgs_refs;
std::vector<std::function<void()>> deferred_deserialization;

View File

@ -263,7 +263,7 @@ std::vector<u8> tar_object::save_directory(const std::string& src_dir, std::vect
const std::string& target_path = full_path.empty() ? src_dir : full_path;
fs::stat_t stat{};
if (!fs::stat(target_path, stat))
if (!fs::get_stat(target_path, stat))
{
return std::move(init);
}

View File

@ -2384,7 +2384,7 @@ void main_window::CreateConnects()
fs::stat_t raw_stat{};
fs::stat_t archived_stat{};
if ((!fs::stat(raw_file_path, raw_stat) || raw_stat.is_directory) || (!fs::stat(archived_path, archived_stat) || archived_stat.is_directory) || (raw_stat.size == 0 && archived_stat.size == 0))
if ((!fs::get_stat(raw_file_path, raw_stat) || raw_stat.is_directory) || (!fs::get_stat(archived_path, archived_stat) || archived_stat.is_directory) || (raw_stat.size == 0 && archived_stat.size == 0))
{
QMessageBox::warning(this, tr("Failed to locate log"), tr("Failed to locate log files.\nMake sure that RPCS3.log and RPCS3.log.gz are writable and can be created without permission issues."));
return;