mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-09 00:11:24 +12:00
Savestates: HLE state saving POC in sys_lwmutex
This commit is contained in:
parent
2815aecd0c
commit
b692108f1e
12 changed files with 159 additions and 63 deletions
|
@ -39,6 +39,8 @@ public:
|
||||||
{
|
{
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ENABLE_BITWISE_SERIALIZATION;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum CellNotAnError : s32
|
enum CellNotAnError : s32
|
||||||
|
|
|
@ -69,10 +69,7 @@ error_code sys_lwcond_signal(ppu_thread& ppu, vm::ptr<sys_lwcond_t> lwcond)
|
||||||
// call the syscall
|
// call the syscall
|
||||||
if (error_code res = _sys_lwcond_signal(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, u32{umax}, 1))
|
if (error_code res = _sys_lwcond_signal(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, u32{umax}, 1))
|
||||||
{
|
{
|
||||||
if (ppu.test_stopped())
|
static_cast<void>(ppu.test_stopped());
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
lwmutex->all_info--;
|
lwmutex->all_info--;
|
||||||
|
|
||||||
|
@ -108,10 +105,7 @@ error_code sys_lwcond_signal(ppu_thread& ppu, vm::ptr<sys_lwcond_t> lwcond)
|
||||||
// call the syscall
|
// call the syscall
|
||||||
if (error_code res = _sys_lwcond_signal(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, u32{umax}, 3))
|
if (error_code res = _sys_lwcond_signal(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, u32{umax}, 3))
|
||||||
{
|
{
|
||||||
if (ppu.test_stopped())
|
static_cast<void>(ppu.test_stopped());
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
lwmutex->lock_var.atomic_op([&](sys_lwmutex_t::sync_var_t& var)
|
lwmutex->lock_var.atomic_op([&](sys_lwmutex_t::sync_var_t& var)
|
||||||
{
|
{
|
||||||
|
@ -158,10 +152,7 @@ error_code sys_lwcond_signal_all(ppu_thread& ppu, vm::ptr<sys_lwcond_t> lwcond)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ppu.test_stopped())
|
static_cast<void>(ppu.test_stopped());
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
lwmutex->all_info += +res;
|
lwmutex->all_info += +res;
|
||||||
return CELL_OK;
|
return CELL_OK;
|
||||||
|
@ -183,10 +174,7 @@ error_code sys_lwcond_signal_all(ppu_thread& ppu, vm::ptr<sys_lwcond_t> lwcond)
|
||||||
// if locking succeeded, call the syscall
|
// if locking succeeded, call the syscall
|
||||||
error_code res = _sys_lwcond_signal_all(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, 1);
|
error_code res = _sys_lwcond_signal_all(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, 1);
|
||||||
|
|
||||||
if (ppu.test_stopped())
|
static_cast<void>(ppu.test_stopped());
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res > 0)
|
if (res > 0)
|
||||||
{
|
{
|
||||||
|
@ -225,10 +213,7 @@ error_code sys_lwcond_signal_to(ppu_thread& ppu, vm::ptr<sys_lwcond_t> lwcond, u
|
||||||
// call the syscall
|
// call the syscall
|
||||||
if (error_code res = _sys_lwcond_signal(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, ppu_thread_id, 1))
|
if (error_code res = _sys_lwcond_signal(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, ppu_thread_id, 1))
|
||||||
{
|
{
|
||||||
if (ppu.test_stopped())
|
static_cast<void>(ppu.test_stopped());
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
lwmutex->all_info--;
|
lwmutex->all_info--;
|
||||||
|
|
||||||
|
@ -261,10 +246,7 @@ error_code sys_lwcond_signal_to(ppu_thread& ppu, vm::ptr<sys_lwcond_t> lwcond, u
|
||||||
// call the syscall
|
// call the syscall
|
||||||
if (error_code res = _sys_lwcond_signal(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, ppu_thread_id, 3))
|
if (error_code res = _sys_lwcond_signal(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, ppu_thread_id, 3))
|
||||||
{
|
{
|
||||||
if (ppu.test_stopped())
|
static_cast<void>(ppu.test_stopped());
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
lwmutex->lock_var.atomic_op([&](sys_lwmutex_t::sync_var_t& var)
|
lwmutex->lock_var.atomic_op([&](sys_lwmutex_t::sync_var_t& var)
|
||||||
{
|
{
|
||||||
|
@ -290,6 +272,9 @@ error_code sys_lwcond_wait(ppu_thread& ppu, vm::ptr<sys_lwcond_t> lwcond, u64 ti
|
||||||
return sys_cond_wait(ppu, lwcond->lwcond_queue, timeout);
|
return sys_cond_wait(ppu, lwcond->lwcond_queue, timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto& sstate = *ppu.optional_savestate_state;
|
||||||
|
const auto lwcond_ec = sstate.try_read<error_code>().second;
|
||||||
|
|
||||||
const be_t<u32> tid(ppu.id);
|
const be_t<u32> tid(ppu.id);
|
||||||
|
|
||||||
const vm::ptr<sys_lwmutex_t> lwmutex = lwcond->lwmutex;
|
const vm::ptr<sys_lwmutex_t> lwmutex = lwcond->lwmutex;
|
||||||
|
@ -301,18 +286,22 @@ error_code sys_lwcond_wait(ppu_thread& ppu, vm::ptr<sys_lwcond_t> lwcond, u64 ti
|
||||||
}
|
}
|
||||||
|
|
||||||
// save old recursive value
|
// save old recursive value
|
||||||
const be_t<u32> recursive_value = lwmutex->recursive_count;
|
const be_t<u32> recursive_value = !lwcond_ec ? lwmutex->recursive_count : sstate.operator be_t<u32>();
|
||||||
|
|
||||||
// set special value
|
// set special value
|
||||||
lwmutex->vars.owner = lwmutex_reserved;
|
lwmutex->vars.owner = lwmutex_reserved;
|
||||||
lwmutex->recursive_count = 0;
|
lwmutex->recursive_count = 0;
|
||||||
|
|
||||||
// call the syscall
|
// call the syscall
|
||||||
const error_code res = _sys_lwcond_queue_wait(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, timeout);
|
const error_code res = !lwcond_ec ? _sys_lwcond_queue_wait(ppu, lwcond->lwcond_queue, lwmutex->sleep_queue, timeout) : lwcond_ec;
|
||||||
|
|
||||||
if (ppu.test_stopped())
|
static_cast<void>(ppu.test_stopped());
|
||||||
|
|
||||||
|
if (ppu.state & cpu_flag::again)
|
||||||
{
|
{
|
||||||
return 0;
|
sstate.pos = 0;
|
||||||
|
sstate(error_code{}, recursive_value); // Not aborted on mutex sleep
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res == CELL_OK || res + 0u == CELL_ESRCH)
|
if (res == CELL_OK || res + 0u == CELL_ESRCH)
|
||||||
|
@ -341,6 +330,13 @@ error_code sys_lwcond_wait(ppu_thread& ppu, vm::ptr<sys_lwcond_t> lwcond, u64 ti
|
||||||
return res2;
|
return res2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ppu.state & cpu_flag::again)
|
||||||
|
{
|
||||||
|
sstate.pos = 0;
|
||||||
|
sstate(res, recursive_value);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
// if successfully locked, restore recursive value
|
// if successfully locked, restore recursive value
|
||||||
lwmutex->recursive_count = recursive_value;
|
lwmutex->recursive_count = recursive_value;
|
||||||
|
|
||||||
|
|
|
@ -99,6 +99,15 @@ error_code sys_lwmutex_lock(ppu_thread& ppu, vm::ptr<sys_lwmutex_t> lwmutex, u64
|
||||||
return sys_mutex_lock(ppu, lwmutex->sleep_queue, timeout);
|
return sys_mutex_lock(ppu, lwmutex->sleep_queue, timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto& sstate = *ppu.optional_savestate_state;
|
||||||
|
const bool aborted = sstate.try_read<bool>().second;
|
||||||
|
|
||||||
|
if (aborted)
|
||||||
|
{
|
||||||
|
// Restore timeout (SYS_SYNC_RETRY mode)
|
||||||
|
sstate(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
const be_t<u32> tid(ppu.id);
|
const be_t<u32> tid(ppu.id);
|
||||||
|
|
||||||
// try to lock lightweight mutex
|
// try to lock lightweight mutex
|
||||||
|
@ -154,7 +163,10 @@ error_code sys_lwmutex_lock(ppu_thread& ppu, vm::ptr<sys_lwmutex_t> lwmutex, u64
|
||||||
}
|
}
|
||||||
|
|
||||||
// atomically increment waiter value using 64 bit op
|
// atomically increment waiter value using 64 bit op
|
||||||
lwmutex->all_info++;
|
if (!aborted)
|
||||||
|
{
|
||||||
|
lwmutex->all_info++;
|
||||||
|
}
|
||||||
|
|
||||||
if (lwmutex->vars.owner.compare_and_swap_test(lwmutex_free, tid))
|
if (lwmutex->vars.owner.compare_and_swap_test(lwmutex_free, tid))
|
||||||
{
|
{
|
||||||
|
@ -167,9 +179,13 @@ error_code sys_lwmutex_lock(ppu_thread& ppu, vm::ptr<sys_lwmutex_t> lwmutex, u64
|
||||||
// lock using the syscall
|
// lock using the syscall
|
||||||
const error_code res = _sys_lwmutex_lock(ppu, lwmutex->sleep_queue, timeout);
|
const error_code res = _sys_lwmutex_lock(ppu, lwmutex->sleep_queue, timeout);
|
||||||
|
|
||||||
if (ppu.test_stopped())
|
static_cast<void>(ppu.test_stopped());
|
||||||
|
|
||||||
|
if (ppu.state & cpu_flag::again)
|
||||||
{
|
{
|
||||||
return 0;
|
sstate.pos = 0;
|
||||||
|
sstate(true, timeout); // Aborted
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
lwmutex->all_info--;
|
lwmutex->all_info--;
|
||||||
|
@ -216,9 +232,13 @@ error_code sys_lwmutex_lock(ppu_thread& ppu, vm::ptr<sys_lwmutex_t> lwmutex, u64
|
||||||
|
|
||||||
const error_code res_ = _sys_lwmutex_lock(ppu, lwmutex->sleep_queue, timeout);
|
const error_code res_ = _sys_lwmutex_lock(ppu, lwmutex->sleep_queue, timeout);
|
||||||
|
|
||||||
if (ppu.test_stopped())
|
static_cast<void>(ppu.test_stopped());
|
||||||
|
|
||||||
|
if (ppu.state & cpu_flag::again)
|
||||||
{
|
{
|
||||||
return 0;
|
sstate.pos = 0;
|
||||||
|
sstate(true, timeout); // Aborted
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res_ == CELL_OK)
|
if (res_ == CELL_OK)
|
||||||
|
|
|
@ -24,7 +24,8 @@ error_code sys_spinlock_lock(ppu_thread& ppu, vm::ptr<atomic_be_t<u32>> lock)
|
||||||
{
|
{
|
||||||
if (ppu.test_stopped())
|
if (ppu.test_stopped())
|
||||||
{
|
{
|
||||||
return 0;
|
ppu.state += cpu_flag::again;
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1396,7 +1396,7 @@ void ppu_thread::cpu_task()
|
||||||
thread_ctrl::wait_on<atomic_wait::op_ne>(g_progr_ptotal, 0);
|
thread_ctrl::wait_on<atomic_wait::op_ne>(g_progr_ptotal, 0);
|
||||||
g_fxo->get<progress_dialog_workaround>().skip_the_progress_dialog = true;
|
g_fxo->get<progress_dialog_workaround>().skip_the_progress_dialog = true;
|
||||||
|
|
||||||
// Sadly we can't postpone initializing guest time because we need ti run PPU threads
|
// Sadly we can't postpone initializing guest time because we need to run PPU threads
|
||||||
// (the farther it's postponed, the less accuracy of guest time has been lost)
|
// (the farther it's postponed, the less accuracy of guest time has been lost)
|
||||||
Emu.FixGuestTime();
|
Emu.FixGuestTime();
|
||||||
|
|
||||||
|
@ -1527,6 +1527,8 @@ ppu_thread::ppu_thread(const ppu_thread_params& param, std::string_view name, u3
|
||||||
gpr[4] = param.arg1;
|
gpr[4] = param.arg1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
optional_savestate_state = std::make_shared<utils::serial>();
|
||||||
|
|
||||||
// Trigger the scheduler
|
// Trigger the scheduler
|
||||||
state += cpu_flag::suspend;
|
state += cpu_flag::suspend;
|
||||||
|
|
||||||
|
@ -1575,10 +1577,15 @@ bool ppu_thread::savable() const
|
||||||
|
|
||||||
void ppu_thread::serialize_common(utils::serial& ar)
|
void ppu_thread::serialize_common(utils::serial& ar)
|
||||||
{
|
{
|
||||||
ar(gpr, fpr, cr, fpscr.bits, lr, ctr, vrsave, cia, xer, sat, nj, prio, optional_syscall_state);
|
ar(gpr, fpr, cr, fpscr.bits, lr, ctr, vrsave, cia, xer, sat, nj, prio, optional_savestate_state);
|
||||||
|
|
||||||
for (v128& reg : vr)
|
for (v128& reg : vr)
|
||||||
ar(reg._bytes);
|
ar(reg._bytes);
|
||||||
|
|
||||||
|
if (optional_savestate_state->data.empty())
|
||||||
|
{
|
||||||
|
optional_savestate_state->clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ppu_thread::ppu_thread(utils::serial& ar)
|
ppu_thread::ppu_thread(utils::serial& ar)
|
||||||
|
@ -1669,8 +1676,12 @@ ppu_thread::ppu_thread(utils::serial& ar)
|
||||||
|
|
||||||
+[](ppu_thread& ppu) -> bool
|
+[](ppu_thread& ppu) -> bool
|
||||||
{
|
{
|
||||||
|
const u32 op = vm::read32(ppu.cia);
|
||||||
|
const auto& table = g_fxo->get<ppu_interpreter_rt>();
|
||||||
ppu.loaded_from_savestate = true;
|
ppu.loaded_from_savestate = true;
|
||||||
ppu_execute_syscall(ppu, ppu.gpr[11]);
|
table.decode(op)(ppu, {op}, vm::_ptr<u32>(ppu.cia), &ppu_ret);
|
||||||
|
|
||||||
|
ppu.optional_savestate_state->clear(); // Reset to writing state
|
||||||
ppu.loaded_from_savestate = false;
|
ppu.loaded_from_savestate = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1708,6 +1719,8 @@ ppu_thread::ppu_thread(utils::serial& ar)
|
||||||
|
|
||||||
void ppu_thread::save(utils::serial& ar)
|
void ppu_thread::save(utils::serial& ar)
|
||||||
{
|
{
|
||||||
|
USING_SERIALIZATION_VERSION(ppu);
|
||||||
|
|
||||||
const u64 entry = std::bit_cast<u64>(entry_func);
|
const u64 entry = std::bit_cast<u64>(entry_func);
|
||||||
|
|
||||||
ppu_join_status _joiner = joiner;
|
ppu_join_status _joiner = joiner;
|
||||||
|
@ -1870,7 +1883,7 @@ void ppu_thread::fast_call(u32 addr, u64 rtoc)
|
||||||
}
|
}
|
||||||
else if (old_cia)
|
else if (old_cia)
|
||||||
{
|
{
|
||||||
if (state & cpu_flag::exit)
|
if (state & cpu_flag::again)
|
||||||
{
|
{
|
||||||
ppu_log.error("HLE callstack savestate is not implemented!");
|
ppu_log.error("HLE callstack savestate is not implemented!");
|
||||||
}
|
}
|
||||||
|
|
|
@ -318,9 +318,9 @@ public:
|
||||||
} thread_name{ this };
|
} thread_name{ this };
|
||||||
|
|
||||||
// For savestates
|
// For savestates
|
||||||
bool stop_flag_removal_protection = false; // If set, Emulator::Run won't remove stop flag
|
bool stop_flag_removal_protection = false; // If set, Emulator::Run won't remove stop flag
|
||||||
bool loaded_from_savestate = false; // Indicates the thread had just started straight from savestate load
|
bool loaded_from_savestate = false; // Indicates the thread had just started straight from savestate load
|
||||||
u64 optional_syscall_state{};
|
std::shared_ptr<utils::serial> optional_savestate_state;
|
||||||
bool interrupt_thread_executing = false;
|
bool interrupt_thread_executing = false;
|
||||||
|
|
||||||
be_t<u64>* get_stack_arg(s32 i, u64 align = alignof(u64));
|
be_t<u64>* get_stack_arg(s32 i, u64 align = alignof(u64));
|
||||||
|
|
|
@ -285,6 +285,8 @@ error_code sys_cond_wait(ppu_thread& ppu, u32 cond_id, u64 timeout)
|
||||||
// Further function result
|
// Further function result
|
||||||
ppu.gpr[3] = CELL_OK;
|
ppu.gpr[3] = CELL_OK;
|
||||||
|
|
||||||
|
auto& sstate = *ppu.optional_savestate_state;
|
||||||
|
|
||||||
const auto cond = idm::get<lv2_obj, lv2_cond>(cond_id, [&](lv2_cond& cond) -> s64
|
const auto cond = idm::get<lv2_obj, lv2_cond>(cond_id, [&](lv2_cond& cond) -> s64
|
||||||
{
|
{
|
||||||
if (!ppu.loaded_from_savestate && cond.mutex->owner >> 1 != ppu.id)
|
if (!ppu.loaded_from_savestate && cond.mutex->owner >> 1 != ppu.id)
|
||||||
|
@ -294,7 +296,10 @@ error_code sys_cond_wait(ppu_thread& ppu, u32 cond_id, u64 timeout)
|
||||||
|
|
||||||
std::lock_guard lock(cond.mutex->mutex);
|
std::lock_guard lock(cond.mutex->mutex);
|
||||||
|
|
||||||
if (ppu.loaded_from_savestate && ppu.optional_syscall_state & 1)
|
const u64 syscall_state = sstate.try_read<u64>().second;
|
||||||
|
sstate.clear();
|
||||||
|
|
||||||
|
if (syscall_state & 1)
|
||||||
{
|
{
|
||||||
// Mutex sleep
|
// Mutex sleep
|
||||||
ensure(!cond.mutex->try_own(ppu, ppu.id));
|
ensure(!cond.mutex->try_own(ppu, ppu.id));
|
||||||
|
@ -309,7 +314,7 @@ error_code sys_cond_wait(ppu_thread& ppu, u32 cond_id, u64 timeout)
|
||||||
if (ppu.loaded_from_savestate)
|
if (ppu.loaded_from_savestate)
|
||||||
{
|
{
|
||||||
cond.sleep(ppu, timeout);
|
cond.sleep(ppu, timeout);
|
||||||
return static_cast<u32>(ppu.optional_syscall_state >> 32);
|
return static_cast<u32>(syscall_state >> 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlock the mutex
|
// Unlock the mutex
|
||||||
|
@ -356,7 +361,9 @@ error_code sys_cond_wait(ppu_thread& ppu, u32 cond_id, u64 timeout)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ppu.optional_syscall_state = u32{mutex_sleep} | (u64{static_cast<u32>(cond.ret)} << 32);
|
const u64 optional_syscall_state = u32{mutex_sleep} | (u64{static_cast<u32>(cond.ret)} << 32);
|
||||||
|
sstate(optional_syscall_state);
|
||||||
|
|
||||||
ppu.state += cpu_flag::again;
|
ppu.state += cpu_flag::again;
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -321,6 +321,8 @@ error_code _sys_lwcond_queue_wait(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id
|
||||||
|
|
||||||
std::shared_ptr<lv2_lwmutex> mutex;
|
std::shared_ptr<lv2_lwmutex> mutex;
|
||||||
|
|
||||||
|
auto& sstate = *ppu.optional_savestate_state;
|
||||||
|
|
||||||
const auto cond = idm::get<lv2_obj, lv2_lwcond>(lwcond_id, [&](lv2_lwcond& cond)
|
const auto cond = idm::get<lv2_obj, lv2_lwcond>(lwcond_id, [&](lv2_lwcond& cond)
|
||||||
{
|
{
|
||||||
mutex = idm::get_unlocked<lv2_obj, lv2_lwmutex>(lwmutex_id);
|
mutex = idm::get_unlocked<lv2_obj, lv2_lwmutex>(lwmutex_id);
|
||||||
|
@ -335,7 +337,10 @@ error_code _sys_lwcond_queue_wait(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id
|
||||||
|
|
||||||
std::lock_guard lock(cond.mutex);
|
std::lock_guard lock(cond.mutex);
|
||||||
|
|
||||||
if (ppu.loaded_from_savestate && ppu.optional_syscall_state)
|
const bool mutex_sleep = sstate.try_read<bool>().second;
|
||||||
|
sstate.clear();
|
||||||
|
|
||||||
|
if (mutex_sleep)
|
||||||
{
|
{
|
||||||
// Special: loading state from the point of waiting on lwmutex sleep queue
|
// Special: loading state from the point of waiting on lwmutex sleep queue
|
||||||
std::lock_guard lock2(mutex->mutex);
|
std::lock_guard lock2(mutex->mutex);
|
||||||
|
@ -403,7 +408,7 @@ error_code _sys_lwcond_queue_wait(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ppu.optional_syscall_state = +mutex_sleep;
|
sstate(mutex_sleep);
|
||||||
ppu.state += cpu_flag::again;
|
ppu.state += cpu_flag::again;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,19 @@ namespace id_manager
|
||||||
thread_local u32 g_id = 0;
|
thread_local u32 g_id = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool serialize<std::shared_ptr<utils::serial>>(utils::serial& ar, std::shared_ptr<utils::serial>& o)
|
||||||
|
{
|
||||||
|
if (!o || !ar.is_writing())
|
||||||
|
{
|
||||||
|
o.reset();
|
||||||
|
o = std::make_shared<utils::serial>();
|
||||||
|
}
|
||||||
|
|
||||||
|
ar(o->data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::pair<u128, id_manager::typeinfo>>& id_manager::get_typeinfo_map()
|
std::vector<std::pair<u128, id_manager::typeinfo>>& id_manager::get_typeinfo_map()
|
||||||
{
|
{
|
||||||
// Magic static
|
// Magic static
|
||||||
|
|
|
@ -1569,7 +1569,7 @@ namespace vm
|
||||||
if (!(ar.data.back() & page_writable) && is_memory_read_only_of_executable(addr))
|
if (!(ar.data.back() & page_writable) && is_memory_read_only_of_executable(addr))
|
||||||
{
|
{
|
||||||
// Revert changes
|
// Revert changes
|
||||||
ar.data.resize(ar.data.size() - (sizeof(u32) * 2 + sizeof(memory_page)));
|
ar.data.resize(ar.seek_end(sizeof(u32) * 2 + sizeof(memory_page)));
|
||||||
vm_log.success("Removed read-only memory block of the executable from savestate. (addr=0x%x, size=0x%x)", addr, shm.first);
|
vm_log.success("Removed read-only memory block of the executable from savestate. (addr=0x%x, size=0x%x)", addr, shm.first);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,7 +71,7 @@ struct serial_ver_t
|
||||||
std::set<u32> compatible_versions;
|
std::set<u32> compatible_versions;
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::array<serial_ver_t, 18> s_serial_versions;
|
static std::array<serial_ver_t, 22> s_serial_versions;
|
||||||
|
|
||||||
#define SERIALIZATION_VER(name, identifier, ...) \
|
#define SERIALIZATION_VER(name, identifier, ...) \
|
||||||
\
|
\
|
||||||
|
@ -88,10 +88,10 @@ static std::array<serial_ver_t, 18> s_serial_versions;
|
||||||
return ::s_serial_versions[identifier].current_version;\
|
return ::s_serial_versions[identifier].current_version;\
|
||||||
}
|
}
|
||||||
|
|
||||||
SERIALIZATION_VER(global_version, 0, 10) // For stuff not listed here
|
SERIALIZATION_VER(global_version, 0, 11) // For stuff not listed here
|
||||||
SERIALIZATION_VER(ppu, 1, 1)
|
SERIALIZATION_VER(ppu, 1, 1)
|
||||||
SERIALIZATION_VER(spu, 2, 1, 2)
|
SERIALIZATION_VER(spu, 2, 1, 2)
|
||||||
SERIALIZATION_VER(lv2_sync, 3, 1)
|
SERIALIZATION_VER(lv2_sync, 3, 1)
|
||||||
SERIALIZATION_VER(lv2_vm, 4, 1)
|
SERIALIZATION_VER(lv2_vm, 4, 1)
|
||||||
SERIALIZATION_VER(lv2_net, 5, 1)
|
SERIALIZATION_VER(lv2_net, 5, 1)
|
||||||
SERIALIZATION_VER(lv2_fs, 6, 1)
|
SERIALIZATION_VER(lv2_fs, 6, 1)
|
||||||
|
@ -121,7 +121,10 @@ SERIALIZATION_VER(cellCamera, 14, 1)
|
||||||
SERIALIZATION_VER(cellGem, 15, 1)
|
SERIALIZATION_VER(cellGem, 15, 1)
|
||||||
SERIALIZATION_VER(sceNpTrophy, 16, 1)
|
SERIALIZATION_VER(sceNpTrophy, 16, 1)
|
||||||
SERIALIZATION_VER(cellMusic, 17, 1)
|
SERIALIZATION_VER(cellMusic, 17, 1)
|
||||||
SERIALIZATION_VER(cellVoice, 15, 1)
|
SERIALIZATION_VER(cellVoice, 18, 1)
|
||||||
|
SERIALIZATION_VER(cellGcm, 19, 1)
|
||||||
|
SERIALIZATION_VER(sysPrxForUser, 20, 1)
|
||||||
|
SERIALIZATION_VER(cellSaveData, 21, 1)
|
||||||
|
|
||||||
#undef SERIALIZATION_VER
|
#undef SERIALIZATION_VER
|
||||||
|
|
||||||
|
@ -857,12 +860,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool
|
||||||
nse_t<u64, 1> offset;
|
nse_t<u64, 1> offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (m_ar->data.size() <= sizeof(file_header))
|
const auto header = m_ar->try_read<file_header>().second;
|
||||||
{
|
|
||||||
return game_boot_result::savestate_corrupted;
|
|
||||||
}
|
|
||||||
|
|
||||||
const file_header header = m_ar->operator file_header();
|
|
||||||
|
|
||||||
if (header.magic != "RPCS3SAV"_u64)
|
if (header.magic != "RPCS3SAV"_u64)
|
||||||
{
|
{
|
||||||
|
@ -2376,6 +2374,7 @@ void Emulator::Kill(bool allow_autoexit, bool savestate)
|
||||||
ar(usz{}); // Reserve memory to be patched later with correct size
|
ar(usz{}); // Reserve memory to be patched later with correct size
|
||||||
const usz old_size = ar.data.size();
|
const usz old_size = ar.data.size();
|
||||||
ar.data = tar_object::save_directory(path, std::move(ar.data));
|
ar.data = tar_object::save_directory(path, std::move(ar.data));
|
||||||
|
ar.seek_end();
|
||||||
const usz tar_size = ar.data.size() - old_size;
|
const usz tar_size = ar.data.size() - old_size;
|
||||||
std::memcpy(ar.data.data() + old_size - sizeof(usz), &tar_size, sizeof(usz));
|
std::memcpy(ar.data.data() + old_size - sizeof(usz), &tar_size, sizeof(usz));
|
||||||
sys_log.success("Saved the contents of directory '%s' (size=0x%x)", path, tar_size);
|
sys_log.success("Saved the contents of directory '%s' (size=0x%x)", path, tar_size);
|
||||||
|
@ -2511,7 +2510,7 @@ void Emulator::Kill(bool allow_autoexit, bool savestate)
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& ar = *m_ar;
|
auto& ar = *m_ar;
|
||||||
const usz pos = ar.data.size();
|
const usz pos = ar.seek_end();
|
||||||
std::memcpy(&ar.data[10], &pos, 8);// Set offset
|
std::memcpy(&ar.data[10], &pos, 8);// Set offset
|
||||||
ar(used_serial);
|
ar(used_serial);
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,8 @@ namespace utils
|
||||||
struct serial
|
struct serial
|
||||||
{
|
{
|
||||||
std::vector<u8> data;
|
std::vector<u8> data;
|
||||||
usz pos = umax;
|
usz pos = 0;
|
||||||
|
bool m_is_writing = true;
|
||||||
|
|
||||||
serial() = default;
|
serial() = default;
|
||||||
serial(const serial&) = delete;
|
serial(const serial&) = delete;
|
||||||
|
@ -44,7 +45,7 @@ namespace utils
|
||||||
// Checks if this instance is currently used for serialization
|
// Checks if this instance is currently used for serialization
|
||||||
bool is_writing() const
|
bool is_writing() const
|
||||||
{
|
{
|
||||||
return pos == umax;
|
return m_is_writing;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reserve memory for serialization
|
// Reserve memory for serialization
|
||||||
|
@ -61,7 +62,8 @@ namespace utils
|
||||||
{
|
{
|
||||||
if (is_writing())
|
if (is_writing())
|
||||||
{
|
{
|
||||||
data.insert(data.end(), static_cast<const u8*>(ptr), static_cast<const u8*>(ptr) + size);
|
data.insert(data.begin() + pos, static_cast<const u8*>(ptr), static_cast<const u8*>(ptr) + size);
|
||||||
|
pos += size;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,7 +289,7 @@ namespace utils
|
||||||
, serialize(const_cast<std::remove_cvref_t<Args>&>(static_cast<const Args&>(args)))), ...);
|
, serialize(const_cast<std::remove_cvref_t<Args>&>(static_cast<const Args&>(args)))), ...);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert serialization manager to deserializion manager (can't go the other way)
|
// Convert serialization manager to deserializion manager
|
||||||
// If no arg is provided reuse saved buffer
|
// If no arg is provided reuse saved buffer
|
||||||
void set_reading_state(std::vector<u8>&& _data = std::vector<u8>{})
|
void set_reading_state(std::vector<u8>&& _data = std::vector<u8>{})
|
||||||
{
|
{
|
||||||
|
@ -296,9 +298,25 @@ namespace utils
|
||||||
data = std::move(_data);
|
data = std::move(_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_is_writing = false;
|
||||||
pos = 0;
|
pos = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset to empty serialization manager
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
data.clear();
|
||||||
|
m_is_writing = true;
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
usz seek_end(usz backwards = 0)
|
||||||
|
{
|
||||||
|
ensure(pos >= backwards);
|
||||||
|
pos = data.size() - backwards;
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T> requires (std::is_copy_constructible_v<std::remove_const_t<T>>) && (std::is_constructible_v<std::remove_const_t<T>> || Bitcopy<std::remove_const_t<T>> ||
|
template <typename T> requires (std::is_copy_constructible_v<std::remove_const_t<T>>) && (std::is_constructible_v<std::remove_const_t<T>> || Bitcopy<std::remove_const_t<T>> ||
|
||||||
std::is_constructible_v<std::remove_const_t<T>, stx::exact_t<serial&>> || TupleAlike<std::remove_const_t<T>>)
|
std::is_constructible_v<std::remove_const_t<T>, stx::exact_t<serial&>> || TupleAlike<std::remove_const_t<T>>)
|
||||||
operator T()
|
operator T()
|
||||||
|
@ -331,10 +349,32 @@ namespace utils
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if writable or readable and valid
|
template <typename T> requires (std::is_copy_constructible_v<T> && std::is_constructible_v<T> && Bitcopy<T>)
|
||||||
|
std::pair<bool, T> try_read()
|
||||||
|
{
|
||||||
|
if (is_writing())
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const usz left = data.size() - pos;
|
||||||
|
using type = std::remove_const_t<T>;
|
||||||
|
|
||||||
|
if (left >= sizeof(type))
|
||||||
|
{
|
||||||
|
u8 buf[sizeof(type)]{};
|
||||||
|
ensure(raw_serialize(buf, sizeof(buf)));
|
||||||
|
return {true, std::bit_cast<type>(buf)};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if valid, can be invalidated by setting pos to umax
|
||||||
|
// Used when an invalid state is encountered somewhere in a place we can't check success code such as constructor)
|
||||||
bool is_valid() const
|
bool is_valid() const
|
||||||
{
|
{
|
||||||
return is_writing() || pos < data.size();
|
return pos <= data.size();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue