Skip to content

Commit

Permalink
Savestates Support For PS3 Emulation (#10478)
Browse files Browse the repository at this point in the history
  • Loading branch information
elad335 committed Jul 4, 2022
1 parent 969b9eb commit fcd297f
Show file tree
Hide file tree
Showing 154 changed files with 4,932 additions and 619 deletions.
36 changes: 33 additions & 3 deletions Utilities/File.h
Expand Up @@ -71,6 +71,10 @@ namespace fs
s64 atime;
s64 mtime;
s64 ctime;

using enable_bitcopy = std::true_type;

constexpr bool operator==(const stat_t&) const = default;
};

// Helper, layout is equal to iovec struct
Expand Down Expand Up @@ -105,6 +109,8 @@ namespace fs
: stat_t{}
{
}

using enable_bitcopy = std::false_type;
};

// Directory handle base
Expand Down Expand Up @@ -681,9 +687,10 @@ namespace fs
T obj;
u64 pos;

container_stream(T&& obj)
container_stream(T&& obj, const stat_t& init_stat = {})
: obj(std::forward<T>(obj))
, pos(0)
, m_stat(init_stat)
{
}

Expand All @@ -694,6 +701,7 @@ namespace fs
bool trunc(u64 length) override
{
obj.resize(length);
update_time(true);
return true;
}

Expand All @@ -708,6 +716,7 @@ namespace fs
{
std::copy(obj.cbegin() + pos, obj.cbegin() + pos + max, static_cast<value_type*>(buffer));
pos = pos + max;
update_time();
return max;
}
}
Expand Down Expand Up @@ -743,6 +752,7 @@ namespace fs
obj.insert(obj.end(), src + overlap, src + size);
pos += size;

if (size) update_time(true);
return size;
}

Expand All @@ -767,13 +777,33 @@ namespace fs
{
return obj.size();
}

stat_t stat() override
{
return m_stat;
}

private:
stat_t m_stat{};

void update_time(bool write = false)
{
// TODO: Accurate timestamps
m_stat.atime++;

if (write)
{
m_stat.mtime = std::max(m_stat.atime, ++m_stat.mtime);
m_stat.ctime = m_stat.mtime;
}
}
};

template <typename T>
file make_stream(T&& container = T{})
file make_stream(T&& container = T{}, const stat_t& stat = stat_t{})
{
file result;
result.reset(std::make_unique<container_stream<T>>(std::forward<T>(container)));
result.reset(std::make_unique<container_stream<T>>(std::forward<T>(container), stat));
return result;
}

Expand Down
10 changes: 8 additions & 2 deletions rpcs3/Crypto/unedat.h
Expand Up @@ -26,11 +26,17 @@ struct loaded_npdrm_keys
}

// TODO: Check if correct for ELF files usage
u128 last_key() const
u128 last_key(usz backwards = 0) const
{
backwards++;
const usz pos = dec_keys_pos;
return pos ? dec_keys[(pos - 1) % std::size(dec_keys)].load() : u128{};
return pos >= backwards ? dec_keys[(pos - backwards) % std::size(dec_keys)].load() : u128{};
}

SAVESTATE_INIT_POS(2);
loaded_npdrm_keys() = default;
loaded_npdrm_keys(utils::serial& ar);
void save(utils::serial& ar);
};

struct NPD_HEADER
Expand Down
3 changes: 2 additions & 1 deletion rpcs3/Emu/CPU/CPUThread.cpp
Expand Up @@ -49,6 +49,7 @@ void fmt_class_string<cpu_flag>::format(std::string& out, u64 arg)
case cpu_flag::pause: return "p";
case cpu_flag::suspend: return "s";
case cpu_flag::ret: return "ret";
case cpu_flag::again: return "a";
case cpu_flag::signal: return "sig";
case cpu_flag::memory: return "mem";
case cpu_flag::pending: return "pend";
Expand Down Expand Up @@ -720,7 +721,7 @@ bool cpu_thread::check_state() noexcept
}

// Atomically clean wait flag and escape
if (!(flags & (cpu_flag::exit + cpu_flag::ret + cpu_flag::stop)))
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)))
Expand Down
3 changes: 2 additions & 1 deletion rpcs3/Emu/CPU/CPUThread.h
Expand Up @@ -19,6 +19,7 @@ enum class cpu_flag : u32
pause, // Thread suspended by suspend_all technique
suspend, // Thread suspended
ret, // Callback return requested
again, // Thread must complete the syscall after deserialization
signal, // Thread received a signal (HLE)
memory, // Thread must unlock memory mutex
pending, // Thread has postponed work
Expand All @@ -34,7 +35,7 @@ enum class cpu_flag : u32
// Test stopped state
constexpr bool is_stopped(bs_t<cpu_flag> state)
{
return !!(state & (cpu_flag::stop + cpu_flag::exit));
return !!(state & (cpu_flag::stop + cpu_flag::exit + cpu_flag::again));
}

// Test paused state
Expand Down
2 changes: 2 additions & 0 deletions rpcs3/Emu/Cell/MFC.h
Expand Up @@ -88,6 +88,8 @@ enum : u32

struct alignas(16) spu_mfc_cmd
{
ENABLE_BITWISE_SERIALIZATION;

MFC cmd;
u8 tag;
u16 size;
Expand Down
51 changes: 51 additions & 0 deletions rpcs3/Emu/Cell/Modules/cellAudio.cpp
Expand Up @@ -342,6 +342,52 @@ void audio_port::tag(s32 offset)
prev_touched_tag_nr = -1;
}

cell_audio_thread::cell_audio_thread(utils::serial& ar)
: cell_audio_thread()
{
ar(init);

if (!init)
{
return;
}

ar(key_count, event_period);

keys.resize(ar);

for (key_info& k : keys)
{
ar(k.start_period, k.flags, k.source);
k.port = lv2_event_queue::load_ptr(ar, k.port);
}

ar(ports);
}

void cell_audio_thread::save(utils::serial& ar)
{
ar(init);

if (!init)
{
return;
}

USING_SERIALIZATION_VERSION(cellAudio);

ar(key_count, event_period);
ar(keys.size());

for (const key_info& k : keys)
{
ar(k.start_period, k.flags, k.source);
lv2_event_queue::save_ptr(ar, k.port.get());
}

ar(ports);
}

std::tuple<u32, u32, u32, u32> cell_audio_thread::count_port_buffer_tags()
{
AUDIT(cfg.buffering_enabled);
Expand Down Expand Up @@ -615,6 +661,11 @@ void cell_audio_thread::operator()()

thread_ctrl::scoped_priority high_prio(+1);

while (Emu.IsPaused())
{
thread_ctrl::wait_for(5000);
}

u32 untouched_expected = 0;

// Main cellAudio loop
Expand Down
20 changes: 17 additions & 3 deletions rpcs3/Emu/Cell/Modules/cellAudio.h
Expand Up @@ -189,6 +189,16 @@ struct audio_port
f32 last_tag_value[PORT_BUFFER_TAG_COUNT] = { 0 };

void tag(s32 offset = 0);

audio_port() = default;

// Handle copy ctor of atomic var
audio_port(const audio_port& r)
{
std::memcpy(this, &r, sizeof(r));
}

ENABLE_BITWISE_SERIALIZATION;
};

struct cell_audio_config
Expand Down Expand Up @@ -366,7 +376,7 @@ class cell_audio_thread
atomic_t<audio_backend_update> m_update_configuration = audio_backend_update::NONE;

shared_mutex mutex{};
atomic_t<u32> init = 0;
atomic_t<u8> init = 0;

u32 key_count = 0;
u8 event_period = 0;
Expand All @@ -390,10 +400,14 @@ class cell_audio_thread
bool m_backend_failed = false;
bool m_audio_should_restart = false;

cell_audio_thread();

void operator()();

SAVESTATE_INIT_POS(9);

cell_audio_thread();
cell_audio_thread(utils::serial& ar);
void save(utils::serial& ar);

audio_port* open_port();

static constexpr auto thread_name = "cellAudio Thread"sv;
Expand Down
19 changes: 19 additions & 0 deletions rpcs3/Emu/Cell/Modules/cellCamera.cpp
Expand Up @@ -131,6 +131,25 @@ static const char* get_camera_attr_name(s32 value)
return nullptr;
}

camera_context::camera_context(utils::serial& ar)
{
save(ar);
}

void camera_context::save(utils::serial& ar)
{
ar(init);

if (!init)
{
return;
}

USING_SERIALIZATION_VERSION_COND(ar.is_writing(), cellCamera);

ar(notify_data_map, start_timestamp, read_mode, is_streaming, is_attached, is_open, info, attr, frame_num);
}

static bool check_dev_num(s32 dev_num)
{
return dev_num == 0;
Expand Down
15 changes: 14 additions & 1 deletion rpcs3/Emu/Cell/Modules/cellCamera.h
Expand Up @@ -375,6 +375,8 @@ struct CellCameraInfoEx
be_t<u32> container;
be_t<s32> read_mode;
vm::bptr<u8> pbuf[2];

ENABLE_BITWISE_SERIALIZATION;
};

struct CellCameraReadEx
Expand All @@ -392,6 +394,8 @@ class camera_context
{
u64 source;
u64 flag;

ENABLE_BITWISE_SERIALIZATION;
};

public:
Expand Down Expand Up @@ -433,14 +437,23 @@ class camera_context
struct attr_t
{
u32 v1, v2;

ENABLE_BITWISE_SERIALIZATION;
};

attr_t attr[500]{};
atomic_t<bool> has_new_frame = false;
atomic_t<u32> frame_num = 0;
atomic_t<u32> frame_timestamp = 0;
atomic_t<u32> bytes_read = 0;

atomic_t<u32> init = 0;
atomic_t<u8> init = 0;

SAVESTATE_INIT_POS(16);

camera_context() = default;
camera_context(utils::serial& ar);
void save(utils::serial& ar);

static constexpr auto thread_name = "Camera Thread"sv;

Expand Down
1 change: 1 addition & 0 deletions rpcs3/Emu/Cell/Modules/cellDmux.cpp
Expand Up @@ -165,6 +165,7 @@ class ElementaryStream
static const u32 id_base = 1;
static const u32 id_step = 1;
static const u32 id_count = 1023;
SAVESTATE_INIT_POS(34);

ElementaryStream(Demuxer* dmux, u32 addr, u32 size, u32 fidMajor, u32 fidMinor, u32 sup1, u32 sup2, vm::ptr<CellDmuxCbEsMsg> cbFunc, u32 cbArg, u32 spec);

Expand Down
30 changes: 28 additions & 2 deletions rpcs3/Emu/Cell/Modules/cellGem.cpp
Expand Up @@ -94,7 +94,7 @@ struct gem_config_data

static constexpr auto thread_name = "Gem Thread"sv;

atomic_t<u32> state = 0;
atomic_t<u8> state = 0;

struct gem_color
{
Expand Down Expand Up @@ -131,6 +131,8 @@ struct gem_config_data
u64 calibration_start_us{0}; // The start timestamp of the calibration in microseconds

static constexpr u64 calibration_time_us = 500000; // The calibration supposedly takes 0.5 seconds (500000 microseconds)

ENABLE_BITWISE_SERIALIZATION;
};

CellGemAttribute attribute = {};
Expand Down Expand Up @@ -202,6 +204,30 @@ struct gem_config_data
controllers[gem_num].port = 7u - gem_num;
}
}

gem_config_data() = default;

SAVESTATE_INIT_POS(15);

void save(utils::serial& ar)
{
ar(state);

if (!state)
{
return;
}

USING_SERIALIZATION_VERSION_COND(ar.is_writing(), cellGem);

ar(attribute, vc_attribute, status_flags, enable_pitch_correction, inertial_counter, controllers
, connected_controllers, update_started, camera_frame, memory_ptr, start_timestamp);
}

gem_config_data(utils::serial& ar)
{
save(ar);
}
};

static inline int32_t cellGemGetVideoConvertSize(s32 output_format)
Expand Down Expand Up @@ -854,7 +880,7 @@ error_code cellGemEnd(ppu_thread& ppu)

if (gem.state.compare_and_swap_test(1, 0))
{
if (u32 addr = gem.memory_ptr)
if (u32 addr = std::exchange(gem.memory_ptr, 0))
{
sys_memory_free(ppu, addr);
}
Expand Down

0 comments on commit fcd297f

Please sign in to comment.