mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-04-22 04:24:44 +00:00
SavaData: SaveDataMemory features
This commit is contained in:
parent
de0a38db0a
commit
0e61607f2e
13 changed files with 669 additions and 142 deletions
|
@ -236,6 +236,8 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp
|
|||
src/core/libraries/save_data/save_backup.h
|
||||
src/core/libraries/save_data/save_instance.cpp
|
||||
src/core/libraries/save_data/save_instance.h
|
||||
src/core/libraries/save_data/save_memory.cpp
|
||||
src/core/libraries/save_data/save_memory.h
|
||||
src/core/libraries/save_data/savedata.cpp
|
||||
src/core/libraries/save_data/savedata.h
|
||||
src/core/libraries/system/savedatadialog.cpp
|
||||
|
|
|
@ -135,7 +135,11 @@ bool PSF::Encode(const std::filesystem::path& filepath) const {
|
|||
|
||||
std::vector<u8> PSF::Encode() const {
|
||||
std::vector<u8> psf_buffer;
|
||||
Encode(psf_buffer);
|
||||
return psf_buffer;
|
||||
}
|
||||
|
||||
void PSF::Encode(std::vector<u8>& psf_buffer) const {
|
||||
psf_buffer.resize(sizeof(PSFHeader) + sizeof(PSFRawEntry) * entry_list.size());
|
||||
|
||||
{
|
||||
|
@ -197,8 +201,6 @@ std::vector<u8> PSF::Encode() const {
|
|||
ASSERT_MSG(additional_padding >= 0, "PSF entry max size mismatch");
|
||||
std::ranges::fill_n(std::back_inserter(psf_buffer), additional_padding, 0);
|
||||
}
|
||||
|
||||
return psf_buffer;
|
||||
}
|
||||
|
||||
std::optional<std::span<const u8>> PSF::GetBinary(std::string_view key) const {
|
||||
|
|
|
@ -60,6 +60,7 @@ public:
|
|||
bool Open(const std::vector<u8>& psf_buffer);
|
||||
|
||||
[[nodiscard]] std::vector<u8> Encode() const;
|
||||
void Encode(std::vector<u8>& buf) const;
|
||||
bool Encode(const std::filesystem::path& filepath) const;
|
||||
|
||||
std::optional<std::span<const u8>> GetBinary(std::string_view key) const;
|
||||
|
|
|
@ -188,6 +188,14 @@ std::optional<BackupRequest> PopLastEvent() {
|
|||
return req;
|
||||
}
|
||||
|
||||
void PushBackupEvent(BackupRequest&& req) {
|
||||
std::scoped_lock lk{g_backup_queue_mutex};
|
||||
g_result_queue.push_back(std::move(req));
|
||||
if (g_result_queue.size() > 20) {
|
||||
g_result_queue.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
float GetProgress() {
|
||||
return static_cast<float>(g_backup_progress) / 100.0f;
|
||||
}
|
||||
|
|
|
@ -53,6 +53,8 @@ std::filesystem::path MakeBackupPath(const std::filesystem::path& save_path);
|
|||
|
||||
std::optional<BackupRequest> PopLastEvent();
|
||||
|
||||
void PushBackupEvent(BackupRequest&& req);
|
||||
|
||||
float GetProgress();
|
||||
|
||||
void ClearProgress();
|
||||
|
|
|
@ -57,6 +57,7 @@ std::filesystem::path SaveInstance::MakeDirSavePath(OrbisUserServiceUserId user_
|
|||
return Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) / std::to_string(user_id) /
|
||||
game_serial / dir_name;
|
||||
}
|
||||
|
||||
int SaveInstance::GetMaxBlocks(const std::filesystem::path& save_path) {
|
||||
Common::FS::IOFile max_blocks_file{save_path / sce_sys / max_block_file_name,
|
||||
Common::FS::FileAccessMode::Read};
|
||||
|
@ -75,6 +76,25 @@ std::filesystem::path SaveInstance::GetParamSFOPath(const std::filesystem::path&
|
|||
return dir_path / sce_sys / "param.sfo";
|
||||
}
|
||||
|
||||
void SaveInstance::SetupDefaultParamSFO(PSF& param_sfo, std::string dir_name,
|
||||
std::string game_serial) {
|
||||
std::string locale = Config::getEmulatorLanguage();
|
||||
if (!default_title.contains(locale)) {
|
||||
locale = "en";
|
||||
}
|
||||
|
||||
#define P(type, key, ...) param_sfo.Add##type(std::string{key}, __VA_ARGS__)
|
||||
// TODO Link with user service
|
||||
P(Binary, SaveParams::ACCOUNT_ID, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
|
||||
P(String, SaveParams::MAINTITLE, default_title.at(locale));
|
||||
P(String, SaveParams::SUBTITLE, "");
|
||||
P(String, SaveParams::DETAIL, "");
|
||||
P(String, SaveParams::SAVEDATA_DIRECTORY, std::move(dir_name));
|
||||
P(Integer, SaveParams::SAVEDATA_LIST_PARAM, 0);
|
||||
P(String, SaveParams::TITLE_ID, std::move(game_serial));
|
||||
#undef P
|
||||
}
|
||||
|
||||
SaveInstance::SaveInstance(int slot_num, OrbisUserServiceUserId user_id, std::string _game_serial,
|
||||
std::string_view _dir_name, int max_blocks)
|
||||
: slot_num(slot_num), user_id(user_id), game_serial(std::move(_game_serial)),
|
||||
|
@ -190,21 +210,7 @@ void SaveInstance::CreateFiles() {
|
|||
const auto sce_sys_dir = save_path / sce_sys;
|
||||
fs::create_directories(sce_sys_dir);
|
||||
|
||||
std::string locale = Config::getEmulatorLanguage();
|
||||
if (!default_title.contains(locale)) {
|
||||
locale = "en";
|
||||
}
|
||||
|
||||
#define P(type, key, ...) param_sfo.Add##type(std::string{key}, __VA_ARGS__)
|
||||
// TODO Link with user service
|
||||
P(Binary, SaveParams::ACCOUNT_ID, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
|
||||
P(String, SaveParams::MAINTITLE, default_title.at(locale));
|
||||
P(String, SaveParams::SUBTITLE, "");
|
||||
P(String, SaveParams::DETAIL, "");
|
||||
P(String, SaveParams::SAVEDATA_DIRECTORY, dir_name);
|
||||
P(Integer, SaveParams::SAVEDATA_LIST_PARAM, 0);
|
||||
P(String, SaveParams::TITLE_ID, game_serial);
|
||||
#undef P
|
||||
SetupDefaultParamSFO(param_sfo, dir_name, game_serial);
|
||||
|
||||
const bool ok = param_sfo.Encode(param_sfo_path);
|
||||
if (!ok) {
|
||||
|
|
|
@ -67,6 +67,8 @@ public:
|
|||
// Get param.sfo path from a dir_path generated by MakeDirSavePath
|
||||
static std::filesystem::path GetParamSFOPath(const std::filesystem::path& dir_path);
|
||||
|
||||
static void SetupDefaultParamSFO(PSF& param_sfo, std::string dir_name, std::string game_serial);
|
||||
|
||||
explicit SaveInstance(int slot_num, OrbisUserServiceUserId user_id, std::string game_serial,
|
||||
std::string_view dir_name, int max_blocks = 0);
|
||||
|
||||
|
@ -126,7 +128,6 @@ public:
|
|||
return read_only;
|
||||
}
|
||||
|
||||
private:
|
||||
void CreateFiles();
|
||||
};
|
||||
|
||||
|
|
287
src/core/libraries/save_data/save_memory.cpp
Normal file
287
src/core/libraries/save_data/save_memory.cpp
Normal file
|
@ -0,0 +1,287 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "save_memory.h"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <filesystem>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <core/libraries/system/msgdialog_ui.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/polyfill_thread.h"
|
||||
#include "common/singleton.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "save_instance.h"
|
||||
|
||||
using Common::FS::IOFile;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
constexpr std::string_view sce_sys = "sce_sys"; // system folder inside save
|
||||
constexpr std::string_view DirnameSaveDataMemory = "sce_sdmemory";
|
||||
constexpr std::string_view FilenameSaveDataMemory = "memory.dat";
|
||||
|
||||
namespace Libraries::SaveData::SaveMemory {
|
||||
|
||||
static Core::FileSys::MntPoints* g_mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
||||
|
||||
static OrbisUserServiceUserId g_user_id{};
|
||||
static std::string g_game_serial{};
|
||||
static std::filesystem::path g_save_path{};
|
||||
static std::filesystem::path g_param_sfo_path{};
|
||||
static PSF g_param_sfo;
|
||||
|
||||
static bool g_save_memory_initialized = false;
|
||||
static std::mutex g_saving_memory_mutex;
|
||||
static std::vector<u8> g_save_memory;
|
||||
|
||||
static std::filesystem::path g_icon_path;
|
||||
static std::vector<u8> g_icon_memory;
|
||||
|
||||
static std::condition_variable g_trigger_save_memory;
|
||||
static std::atomic_bool g_saving_memory = false;
|
||||
static std::atomic_bool g_save_event = false;
|
||||
static std::jthread g_save_memory_thread;
|
||||
|
||||
static std::atomic_bool g_memory_dirty = false;
|
||||
static std::atomic_bool g_param_dirty = false;
|
||||
static std::atomic_bool g_icon_dirty = false;
|
||||
|
||||
static void SaveFileSafe(void* buf, size_t count, const std::filesystem::path& path) {
|
||||
const auto& dir = path.parent_path();
|
||||
const auto& name = path.filename();
|
||||
const auto tmp_path = dir / (name.string() + ".tmp");
|
||||
|
||||
IOFile file(tmp_path, Common::FS::FileAccessMode::Write);
|
||||
file.WriteRaw<u8>(buf, count);
|
||||
file.Close();
|
||||
|
||||
fs::remove(path);
|
||||
fs::rename(tmp_path, path);
|
||||
}
|
||||
|
||||
[[noreturn]] void SaveThreadLoop() {
|
||||
Common::SetCurrentThreadName("SaveData_SaveDataMemoryThread");
|
||||
std::mutex mtx;
|
||||
while (true) {
|
||||
{
|
||||
std::unique_lock lk{mtx};
|
||||
g_trigger_save_memory.wait(lk);
|
||||
}
|
||||
// Save the memory
|
||||
g_saving_memory = true;
|
||||
std::scoped_lock lk{g_saving_memory_mutex};
|
||||
try {
|
||||
LOG_DEBUG(Lib_SaveData, "Saving save data memory {}", g_save_path.string());
|
||||
|
||||
if (g_memory_dirty) {
|
||||
g_memory_dirty = false;
|
||||
SaveFileSafe(g_save_memory.data(), g_save_memory.size(),
|
||||
g_save_path / FilenameSaveDataMemory);
|
||||
}
|
||||
if (g_param_dirty) {
|
||||
g_param_dirty = false;
|
||||
static std::vector<u8> buf;
|
||||
g_param_sfo.Encode(buf);
|
||||
SaveFileSafe(buf.data(), buf.size(), g_param_sfo_path);
|
||||
}
|
||||
if (g_icon_dirty) {
|
||||
g_icon_dirty = false;
|
||||
SaveFileSafe(g_icon_memory.data(), g_icon_memory.size(), g_icon_path);
|
||||
}
|
||||
|
||||
if (g_save_event) {
|
||||
Backup::PushBackupEvent(Backup::BackupRequest{
|
||||
.user_id = g_user_id,
|
||||
.title_id = g_game_serial,
|
||||
.dir_name = std::string{DirnameSaveDataMemory},
|
||||
.origin = Backup::OrbisSaveDataEventType::SAVE_DATA_MEMORY_SYNC,
|
||||
.save_path = g_save_path,
|
||||
});
|
||||
g_save_event = false;
|
||||
}
|
||||
} catch (const fs::filesystem_error& e) {
|
||||
LOG_ERROR(Lib_SaveData, "Failed to save save data memory: {}", e.what());
|
||||
MsgDialog::ShowMsgDialog(MsgDialog::MsgDialogState{
|
||||
MsgDialog::MsgDialogState::UserState{
|
||||
.type = MsgDialog::ButtonType::OK,
|
||||
.msg = fmt::format("Failed to save save data memory.\nCode: <{}>\n{}",
|
||||
e.code().message(), e.what()),
|
||||
},
|
||||
});
|
||||
}
|
||||
g_saving_memory = false;
|
||||
}
|
||||
}
|
||||
|
||||
void SetDirectories(OrbisUserServiceUserId user_id, std::string _game_serial) {
|
||||
g_user_id = user_id;
|
||||
g_game_serial = std::move(_game_serial);
|
||||
g_save_path = SaveInstance::MakeDirSavePath(user_id, g_game_serial, DirnameSaveDataMemory);
|
||||
g_param_sfo_path = SaveInstance::GetParamSFOPath(g_save_path);
|
||||
g_param_sfo = PSF();
|
||||
g_icon_path = g_save_path / sce_sys / "icon0.png";
|
||||
}
|
||||
|
||||
const std::filesystem::path& GetSavePath() {
|
||||
return g_save_path;
|
||||
}
|
||||
|
||||
size_t CreateSaveMemory(size_t memory_size) {
|
||||
size_t existed_size = 0;
|
||||
|
||||
static std::once_flag init_save_thread_flag;
|
||||
std::call_once(init_save_thread_flag,
|
||||
[] { g_save_memory_thread = std::jthread{SaveThreadLoop}; });
|
||||
|
||||
g_save_memory.resize(memory_size);
|
||||
SaveInstance::SetupDefaultParamSFO(g_param_sfo, std::string{DirnameSaveDataMemory},
|
||||
g_game_serial);
|
||||
|
||||
g_save_memory_initialized = true;
|
||||
|
||||
if (!fs::exists(g_param_sfo_path)) {
|
||||
// Create save memory
|
||||
fs::create_directories(g_save_path / sce_sys);
|
||||
|
||||
IOFile memory_file{g_save_path / FilenameSaveDataMemory, Common::FS::FileAccessMode::Write};
|
||||
bool ok = memory_file.SetSize(memory_size);
|
||||
if (!ok) {
|
||||
LOG_ERROR(Lib_SaveData, "Failed to set memory size");
|
||||
throw std::filesystem::filesystem_error(
|
||||
"Failed to set save memory size", g_save_path / FilenameSaveDataMemory,
|
||||
std::make_error_code(std::errc::no_space_on_device));
|
||||
}
|
||||
memory_file.Close();
|
||||
} else {
|
||||
// Load save memory
|
||||
|
||||
bool ok = g_param_sfo.Open(g_param_sfo_path);
|
||||
if (!ok) {
|
||||
LOG_ERROR(Lib_SaveData, "Failed to open SFO at {}", g_param_sfo_path.string());
|
||||
throw std::filesystem::filesystem_error(
|
||||
"failed to open SFO", g_param_sfo_path,
|
||||
std::make_error_code(std::errc::illegal_byte_sequence));
|
||||
}
|
||||
|
||||
IOFile memory_file{g_save_path / FilenameSaveDataMemory, Common::FS::FileAccessMode::Read};
|
||||
if (!memory_file.IsOpen()) {
|
||||
LOG_ERROR(Lib_SaveData, "Failed to open save memory");
|
||||
throw std::filesystem::filesystem_error(
|
||||
"failed to open save memory", g_save_path / FilenameSaveDataMemory,
|
||||
std::make_error_code(std::errc::permission_denied));
|
||||
}
|
||||
size_t save_size = memory_file.GetSize();
|
||||
existed_size = save_size;
|
||||
memory_file.Seek(0);
|
||||
memory_file.ReadRaw<u8>(g_save_memory.data(), std::min(save_size, memory_size));
|
||||
memory_file.Close();
|
||||
}
|
||||
|
||||
return existed_size;
|
||||
}
|
||||
|
||||
void SetIcon(void* buf, size_t buf_size) {
|
||||
if (buf == nullptr) {
|
||||
const auto& src_icon = g_mnt->GetHostPath("/app0/sce_sys/save_data.png");
|
||||
if (fs::exists(src_icon)) {
|
||||
fs::copy_file(src_icon, g_icon_path);
|
||||
}
|
||||
IOFile file(g_icon_path, Common::FS::FileAccessMode::Read);
|
||||
size_t size = file.GetSize();
|
||||
file.Seek(0);
|
||||
g_icon_memory.resize(size);
|
||||
file.ReadRaw<u8>(g_icon_memory.data(), size);
|
||||
file.Close();
|
||||
} else {
|
||||
g_icon_memory.resize(buf_size);
|
||||
std::memcpy(g_icon_memory.data(), buf, buf_size);
|
||||
IOFile file(g_icon_path, Common::FS::FileAccessMode::Append);
|
||||
file.Seek(0);
|
||||
file.WriteRaw<u8>(g_icon_memory.data(), buf_size);
|
||||
file.Close();
|
||||
}
|
||||
}
|
||||
|
||||
void WriteIcon(void* buf, size_t buf_size) {
|
||||
if (buf_size != g_icon_memory.size()) {
|
||||
g_icon_memory.resize(buf_size);
|
||||
}
|
||||
std::memcpy(g_icon_memory.data(), buf, buf_size);
|
||||
g_icon_dirty = true;
|
||||
}
|
||||
|
||||
bool IsSaveMemoryInitialized() {
|
||||
return g_save_memory_initialized;
|
||||
}
|
||||
|
||||
PSF& GetParamSFO() {
|
||||
return g_param_sfo;
|
||||
}
|
||||
|
||||
std::span<u8> GetIcon() {
|
||||
return {g_icon_memory};
|
||||
}
|
||||
|
||||
void SaveSFO(bool sync) {
|
||||
if (!sync) {
|
||||
g_param_dirty = true;
|
||||
return;
|
||||
}
|
||||
const bool ok = g_param_sfo.Encode(g_param_sfo_path);
|
||||
if (!ok) {
|
||||
LOG_ERROR(Lib_SaveData, "Failed to encode param.sfo");
|
||||
throw std::filesystem::filesystem_error("Failed to write param.sfo", g_param_sfo_path,
|
||||
std::make_error_code(std::errc::permission_denied));
|
||||
}
|
||||
}
|
||||
bool IsSaving() {
|
||||
return g_saving_memory;
|
||||
}
|
||||
|
||||
bool TriggerSaveWithoutEvent() {
|
||||
if (g_saving_memory) {
|
||||
return false;
|
||||
}
|
||||
g_trigger_save_memory.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TriggerSave() {
|
||||
if (g_saving_memory) {
|
||||
return false;
|
||||
}
|
||||
g_save_event = true;
|
||||
g_trigger_save_memory.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReadMemory(void* buf, size_t buf_size, int64_t offset) {
|
||||
std::scoped_lock lk{g_saving_memory_mutex};
|
||||
if (offset > g_save_memory.size()) {
|
||||
UNREACHABLE_MSG("ReadMemory out of bounds");
|
||||
}
|
||||
if (offset + buf_size > g_save_memory.size()) {
|
||||
UNREACHABLE_MSG("ReadMemory out of bounds");
|
||||
}
|
||||
std::memcpy(buf, g_save_memory.data() + offset, buf_size);
|
||||
}
|
||||
|
||||
void WriteMemory(void* buf, size_t buf_size, int64_t offset) {
|
||||
std::scoped_lock lk{g_saving_memory_mutex};
|
||||
if (offset > g_save_memory.size()) {
|
||||
UNREACHABLE_MSG("WriteMemory out of bounds");
|
||||
}
|
||||
if (offset + buf_size > g_save_memory.size()) {
|
||||
UNREACHABLE_MSG("WriteMemory out of bounds");
|
||||
}
|
||||
std::memcpy(g_save_memory.data() + offset, buf, buf_size);
|
||||
g_memory_dirty = true;
|
||||
}
|
||||
|
||||
} // namespace Libraries::SaveData::SaveMemory
|
49
src/core/libraries/save_data/save_memory.h
Normal file
49
src/core/libraries/save_data/save_memory.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include "save_backup.h"
|
||||
|
||||
class PSF;
|
||||
|
||||
namespace Libraries::SaveData {
|
||||
using OrbisUserServiceUserId = s32;
|
||||
}
|
||||
|
||||
namespace Libraries::SaveData::SaveMemory {
|
||||
|
||||
void SetDirectories(OrbisUserServiceUserId user_id, std::string game_serial);
|
||||
|
||||
[[nodiscard]] const std::filesystem::path& GetSavePath();
|
||||
|
||||
// returns the size of the existed save memory
|
||||
size_t CreateSaveMemory(size_t memory_size);
|
||||
|
||||
// Initialize the icon. Set buf to null to read the standard icon.
|
||||
void SetIcon(void* buf, size_t buf_size);
|
||||
|
||||
// Update the icon
|
||||
void WriteIcon(void* buf, size_t buf_size);
|
||||
|
||||
[[nodiscard]] bool IsSaveMemoryInitialized();
|
||||
|
||||
[[nodiscard]] PSF& GetParamSFO();
|
||||
|
||||
[[nodiscard]] std::span<u8> GetIcon();
|
||||
|
||||
// Save now or wait for the background thread to save
|
||||
void SaveSFO(bool sync = false);
|
||||
|
||||
[[nodiscard]] bool IsSaving();
|
||||
|
||||
bool TriggerSaveWithoutEvent();
|
||||
|
||||
bool TriggerSave();
|
||||
|
||||
void ReadMemory(void* buf, size_t buf_size, int64_t offset);
|
||||
|
||||
void WriteMemory(void* buf, size_t buf_size, int64_t offset);
|
||||
|
||||
} // namespace Libraries::SaveData::SaveMemory
|
|
@ -4,6 +4,7 @@
|
|||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include <core/libraries/system/msgdialog_ui.h>
|
||||
#include <magic_enum.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
|
@ -18,8 +19,10 @@
|
|||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/save_data/savedata.h"
|
||||
#include "core/libraries/system/msgdialog.h"
|
||||
#include "save_backup.h"
|
||||
#include "save_instance.h"
|
||||
#include "save_memory.h"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
namespace chrono = std::chrono;
|
||||
|
@ -43,7 +46,16 @@ enum class Error : u32 {
|
|||
BAD_MOUNTED = 0x809F000D,
|
||||
BROKEN = 0x809F000F,
|
||||
INVALID_LOGIN_USER = 0x809F0011,
|
||||
MEMORY_NOT_READY = 0x809F0012,
|
||||
BACKUP_BUSY = 0x809F0013,
|
||||
BUSY_FOR_SAVING = 0x809F0016,
|
||||
};
|
||||
|
||||
enum class OrbisSaveDataSaveDataMemoryOption : u32 {
|
||||
NONE = 0,
|
||||
SET_PARAM = 1 << 0,
|
||||
DOUBLE_BUFFER = 1 << 1,
|
||||
UNLOCK_LIMITATIONS = 1 << 2,
|
||||
};
|
||||
|
||||
using OrbisUserServiceUserId = s32;
|
||||
|
@ -64,9 +76,6 @@ constexpr size_t OrbisSaveDataSubtitleMaxsize = 128; // Maximum subtitle name
|
|||
constexpr size_t OrbisSaveDataDetailMaxsize = 1024; // Maximum detail name size
|
||||
constexpr size_t OrbisSaveDataFingerprintDataSize = 65; // Maximum fingerprint size
|
||||
|
||||
constexpr std::string_view OrbisSaveDataDirnameSaveDataMemory = "sce_sdmemory";
|
||||
constexpr std::string_view OrbisSaveDataFilenameSaveDataMemory = "memory.dat";
|
||||
|
||||
enum class OrbisSaveDataMountMode : u32 {
|
||||
RDONLY = 1 << 0,
|
||||
RDWR = 1 << 1,
|
||||
|
@ -131,6 +140,14 @@ struct OrbisSaveDataParam {
|
|||
const auto time_since_epoch = sfo.GetLastWrite().time_since_epoch();
|
||||
mtime = chrono::duration_cast<chrono::seconds>(time_since_epoch).count();
|
||||
}
|
||||
|
||||
void ToSFO(PSF& sfo) const {
|
||||
sfo.AddString(std::string{SaveParams::MAINTITLE}, std::string{title}, true);
|
||||
sfo.AddString(std::string{SaveParams::SUBTITLE}, std::string{subTitle}, true);
|
||||
sfo.AddString(std::string{SaveParams::DETAIL}, std::string{detail}, true);
|
||||
sfo.AddInteger(std::string{SaveParams::SAVEDATA_LIST_PARAM}, static_cast<s32>(userParam),
|
||||
true);
|
||||
}
|
||||
};
|
||||
|
||||
struct OrbisSaveDataFingerprint {
|
||||
|
@ -187,6 +204,51 @@ struct OrbisSaveDataIcon {
|
|||
}
|
||||
};
|
||||
|
||||
struct OrbisSaveDataMemoryData {
|
||||
void* buf;
|
||||
size_t bufSize;
|
||||
s64 offset;
|
||||
u8 _reserved[40];
|
||||
};
|
||||
|
||||
struct OrbisSaveDataMemoryGet2 {
|
||||
OrbisUserServiceUserId userId;
|
||||
std::array<u8, 4> _pad;
|
||||
OrbisSaveDataMemoryData* data;
|
||||
OrbisSaveDataParam* param;
|
||||
OrbisSaveDataIcon* icon;
|
||||
std::array<u8, 32> _reserved;
|
||||
};
|
||||
|
||||
struct OrbisSaveDataMemorySet2 {
|
||||
OrbisUserServiceUserId userId;
|
||||
std::array<u8, 4> _pad;
|
||||
const OrbisSaveDataMemoryData* data;
|
||||
const OrbisSaveDataParam* param;
|
||||
const OrbisSaveDataIcon* icon;
|
||||
std::array<u8, 32> _reserved;
|
||||
};
|
||||
|
||||
struct OrbisSaveDataMemorySetup2 {
|
||||
OrbisSaveDataSaveDataMemoryOption option;
|
||||
OrbisUserServiceUserId userId;
|
||||
size_t memorySize;
|
||||
size_t iconMemorySize;
|
||||
const OrbisSaveDataParam* initParam;
|
||||
const OrbisSaveDataIcon* initIcon;
|
||||
std::array<u8, 24> _reserved;
|
||||
};
|
||||
|
||||
struct OrbisSaveDataMemorySetupResult {
|
||||
size_t existedMemorySize;
|
||||
std::array<u8, 16> _reserved;
|
||||
};
|
||||
|
||||
struct OrbisSaveDataMemorySync {
|
||||
OrbisUserServiceUserId userId;
|
||||
std::array<u8, 36> _reserved;
|
||||
};
|
||||
|
||||
struct OrbisSaveDataMount2 {
|
||||
OrbisUserServiceUserId userId;
|
||||
s32 : 32;
|
||||
|
@ -282,8 +344,6 @@ struct OrbisSaveDataEvent {
|
|||
std::array<u8, 40> _reserved;
|
||||
};
|
||||
|
||||
bool is_rw_mode = false;
|
||||
|
||||
static bool g_initialized = false;
|
||||
static std::string g_game_serial;
|
||||
static std::array<std::optional<SaveInstance>, 16> g_mount_slots;
|
||||
|
@ -402,6 +462,9 @@ static Error saveDataMount(const OrbisSaveDataMount2* mount_info,
|
|||
LOG_ERROR(Lib_SaveData, "Corrupted save data");
|
||||
return Error::BROKEN;
|
||||
}
|
||||
if (e.code() == std::errc::no_space_on_device) {
|
||||
return Error::NO_SPACE_FS;
|
||||
}
|
||||
LOG_ERROR(Lib_SaveData, "Failed to mount save data: {}", e.what());
|
||||
return Error::INTERNAL;
|
||||
}
|
||||
|
@ -871,7 +934,7 @@ Error PS4_SYSV_ABI sceSaveDataGetEventResult(const OrbisSaveDataEventParam*,
|
|||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
return Error::PARAMETER;
|
||||
}
|
||||
LOG_DEBUG(Lib_SaveData, "called");
|
||||
LOG_TRACE(Lib_SaveData, "called");
|
||||
|
||||
auto last_event = Backup::PopLastEvent();
|
||||
if (!last_event.has_value()) {
|
||||
|
@ -1017,25 +1080,56 @@ int PS4_SYSV_ABI sceSaveDataGetSaveDataCount() {
|
|||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataGetSaveDataMemory(const u32 userId, void* buf, const size_t bufSize,
|
||||
const int64_t offset) {
|
||||
const auto& mount_dir = Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) /
|
||||
std::to_string(userId) / g_game_serial / "sdmemory/save_mem1.sav";
|
||||
|
||||
Common::FS::IOFile file(mount_dir, Common::FS::FileAccessMode::Read);
|
||||
if (!file.IsOpen()) {
|
||||
return false;
|
||||
}
|
||||
file.Seek(offset);
|
||||
size_t nbytes = file.ReadRaw<u8>(buf, bufSize);
|
||||
LOG_INFO(Lib_SaveData, "called: bufSize = {}, offset = {}", bufSize, offset, nbytes);
|
||||
|
||||
return ORBIS_OK;
|
||||
Error PS4_SYSV_ABI sceSaveDataGetSaveDataMemory(const OrbisUserServiceUserId userId, void* buf,
|
||||
const size_t bufSize, const int64_t offset) {
|
||||
LOG_DEBUG(Lib_SaveData, "Redirecting to sceSaveDataGetSaveDataMemory2");
|
||||
OrbisSaveDataMemoryData data{};
|
||||
data.buf = buf;
|
||||
data.bufSize = bufSize;
|
||||
data.offset = offset;
|
||||
OrbisSaveDataMemoryGet2 param{};
|
||||
param.userId = userId;
|
||||
param.data = &data;
|
||||
param.param = nullptr;
|
||||
param.icon = nullptr;
|
||||
return sceSaveDataGetSaveDataMemory2(¶m);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataGetSaveDataMemory2(/*OrbisSaveDataMemoryGet2* getParam*/) {
|
||||
LOG_ERROR(Lib_SaveData, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
Error PS4_SYSV_ABI sceSaveDataGetSaveDataMemory2(OrbisSaveDataMemoryGet2* getParam) {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
}
|
||||
if (getParam == nullptr) {
|
||||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
return Error::PARAMETER;
|
||||
}
|
||||
if (!SaveMemory::IsSaveMemoryInitialized()) {
|
||||
LOG_INFO(Lib_SaveData, "called without save memory initialized");
|
||||
return Error::MEMORY_NOT_READY;
|
||||
}
|
||||
if (SaveMemory::IsSaving()) {
|
||||
LOG_TRACE(Lib_SaveData, "called while saving");
|
||||
return Error::BUSY_FOR_SAVING;
|
||||
}
|
||||
LOG_DEBUG(Lib_SaveData, "called");
|
||||
auto data = getParam->data;
|
||||
if (data != nullptr) {
|
||||
SaveMemory::ReadMemory(data->buf, data->bufSize, data->offset);
|
||||
}
|
||||
auto param = getParam->param;
|
||||
if (param != nullptr) {
|
||||
param->FromSFO(SaveMemory::GetParamSFO());
|
||||
}
|
||||
auto icon = getParam->icon;
|
||||
if (icon != nullptr) {
|
||||
auto icon_mem = SaveMemory::GetIcon();
|
||||
size_t total = std::min(icon->bufSize, icon_mem.size());
|
||||
std::memcpy(icon->buf, icon_mem.data(), total);
|
||||
icon->dataSize = total;
|
||||
}
|
||||
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataGetSaveDataRootDir() {
|
||||
|
@ -1317,13 +1411,7 @@ Error PS4_SYSV_ABI sceSaveDataSetParam(const OrbisSaveDataMountPoint* mountPoint
|
|||
case OrbisSaveDataParamType::ALL: {
|
||||
const auto param = static_cast<const OrbisSaveDataParam*>(paramBuf);
|
||||
ASSERT(paramBufSize == sizeof(OrbisSaveDataParam));
|
||||
|
||||
param_sfo->AddString(std::string{SaveParams::MAINTITLE}, std::string{param->title}, true);
|
||||
param_sfo->AddString(std::string{SaveParams::SUBTITLE}, std::string{param->subTitle}, true);
|
||||
param_sfo->AddString(std::string{SaveParams::DETAIL}, std::string{param->detail}, true);
|
||||
param_sfo->AddInteger(std::string{SaveParams::SAVEDATA_LIST_PARAM},
|
||||
static_cast<s32>(param->userParam), true);
|
||||
|
||||
param->ToSFO(*param_sfo);
|
||||
return Error::OK;
|
||||
} break;
|
||||
case OrbisSaveDataParamType::TITLE: {
|
||||
|
@ -1339,7 +1427,7 @@ Error PS4_SYSV_ABI sceSaveDataSetParam(const OrbisSaveDataMountPoint* mountPoint
|
|||
param_sfo->AddString(std::string{SaveParams::DETAIL}, {value}, true);
|
||||
} break;
|
||||
case OrbisSaveDataParamType::USER_PARAM: {
|
||||
const auto value = static_cast<const u32*>(paramBuf);
|
||||
const auto value = static_cast<const s32*>(paramBuf);
|
||||
param_sfo->AddInteger(std::string{SaveParams::SAVEDATA_LIST_PARAM}, *value, true);
|
||||
} break;
|
||||
default:
|
||||
|
@ -1354,15 +1442,53 @@ int PS4_SYSV_ABI sceSaveDataSetSaveDataLibraryUser() {
|
|||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataSetSaveDataMemory(const u32 userId, const void* buf,
|
||||
const size_t bufSize, const int64_t offset) {
|
||||
LOG_ERROR(Lib_SaveData, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
Error PS4_SYSV_ABI sceSaveDataSetSaveDataMemory(OrbisUserServiceUserId userId, void* buf,
|
||||
size_t bufSize, int64_t offset) {
|
||||
LOG_DEBUG(Lib_SaveData, "Redirecting to sceSaveDataSetSaveDataMemory2");
|
||||
OrbisSaveDataMemoryData data{};
|
||||
data.buf = buf;
|
||||
data.bufSize = bufSize;
|
||||
data.offset = offset;
|
||||
OrbisSaveDataMemorySet2 setParam{};
|
||||
setParam.userId = userId;
|
||||
setParam.data = &data;
|
||||
return sceSaveDataSetSaveDataMemory2(&setParam);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataSetSaveDataMemory2(/*const OrbisSaveDataMemorySet2* setParam*/) {
|
||||
LOG_ERROR(Lib_SaveData, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
Error PS4_SYSV_ABI sceSaveDataSetSaveDataMemory2(const OrbisSaveDataMemorySet2* setParam) {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
}
|
||||
if (setParam == nullptr) {
|
||||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
return Error::PARAMETER;
|
||||
}
|
||||
if (!SaveMemory::IsSaveMemoryInitialized()) {
|
||||
LOG_INFO(Lib_SaveData, "called without save memory initialized");
|
||||
return Error::MEMORY_NOT_READY;
|
||||
}
|
||||
if (SaveMemory::IsSaving()) {
|
||||
LOG_TRACE(Lib_SaveData, "called while saving");
|
||||
return Error::BUSY_FOR_SAVING;
|
||||
}
|
||||
LOG_DEBUG(Lib_SaveData, "called");
|
||||
auto data = setParam->data;
|
||||
if (data != nullptr) {
|
||||
SaveMemory::WriteMemory(data->buf, data->bufSize, data->offset);
|
||||
}
|
||||
auto param = setParam->param;
|
||||
if (param != nullptr) {
|
||||
param->ToSFO(SaveMemory::GetParamSFO());
|
||||
SaveMemory::SaveSFO();
|
||||
}
|
||||
auto icon = setParam->icon;
|
||||
if (icon != nullptr) {
|
||||
SaveMemory::WriteIcon(icon->buf, icon->bufSize);
|
||||
}
|
||||
|
||||
SaveMemory::TriggerSaveWithoutEvent();
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataSetupSaveDataMemory(/*u32 userId, size_t memorySize,
|
||||
|
@ -1371,10 +1497,59 @@ int PS4_SYSV_ABI sceSaveDataSetupSaveDataMemory(/*u32 userId, size_t memorySize,
|
|||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataSetupSaveDataMemory2(/*const OrbisSaveDataMemorySetup2* setupParam,
|
||||
OrbisSaveDataMemorySetupResult* result*/) {
|
||||
LOG_ERROR(Lib_SaveData, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
Error PS4_SYSV_ABI sceSaveDataSetupSaveDataMemory2(const OrbisSaveDataMemorySetup2* setupParam,
|
||||
OrbisSaveDataMemorySetupResult* result) {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
}
|
||||
if (setupParam == nullptr) {
|
||||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
return Error::PARAMETER;
|
||||
}
|
||||
LOG_DEBUG(Lib_SaveData, "called");
|
||||
|
||||
SaveMemory::SetDirectories(setupParam->userId, g_game_serial);
|
||||
|
||||
const auto& save_path = SaveMemory::GetSavePath();
|
||||
for (const auto& instance : g_mount_slots) {
|
||||
if (instance.has_value() && instance->GetSavePath() == save_path) {
|
||||
return Error::BUSY;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
size_t existed_size = SaveMemory::CreateSaveMemory(setupParam->memorySize);
|
||||
if (existed_size == 0) { // Just created
|
||||
if (setupParam->initParam != nullptr) {
|
||||
auto& sfo = SaveMemory::GetParamSFO();
|
||||
setupParam->initParam->ToSFO(sfo);
|
||||
}
|
||||
SaveMemory::SaveSFO();
|
||||
|
||||
auto init_icon = setupParam->initIcon;
|
||||
if (init_icon != nullptr) {
|
||||
SaveMemory::SetIcon(init_icon->buf, init_icon->bufSize);
|
||||
} else {
|
||||
SaveMemory::SetIcon(nullptr, 0);
|
||||
}
|
||||
}
|
||||
if (result != nullptr) {
|
||||
result->existedMemorySize = existed_size;
|
||||
}
|
||||
} catch (const fs::filesystem_error& e) {
|
||||
LOG_ERROR(Lib_SaveData, "Failed to create/load save memory: {}", e.what());
|
||||
|
||||
const MsgDialog::MsgDialogState dialog{MsgDialog::MsgDialogState::UserState{
|
||||
.type = MsgDialog::ButtonType::OK,
|
||||
.msg = "Failed to create or load save memory:\n" + std::string{e.what()},
|
||||
}};
|
||||
MsgDialog::ShowMsgDialog(dialog);
|
||||
|
||||
return Error::INTERNAL;
|
||||
}
|
||||
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataShutdownStart() {
|
||||
|
@ -1392,9 +1567,25 @@ int PS4_SYSV_ABI sceSaveDataSyncCloudList() {
|
|||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataSyncSaveDataMemory(/*OrbisSaveDataMemorySync* syncParam*/) {
|
||||
LOG_ERROR(Lib_SaveData, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
Error PS4_SYSV_ABI sceSaveDataSyncSaveDataMemory(OrbisSaveDataMemorySync* syncParam) {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
}
|
||||
if (syncParam == nullptr) {
|
||||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
return Error::PARAMETER;
|
||||
}
|
||||
if (!SaveMemory::IsSaveMemoryInitialized()) {
|
||||
LOG_INFO(Lib_SaveData, "called without save memory initialized");
|
||||
return Error::MEMORY_NOT_READY;
|
||||
}
|
||||
LOG_DEBUG(Lib_SaveData, "called");
|
||||
bool ok = SaveMemory::TriggerSave();
|
||||
if (!ok) {
|
||||
return Error::BUSY_FOR_SAVING;
|
||||
}
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
Error PS4_SYSV_ABI sceSaveDataTerminate() {
|
||||
|
|
|
@ -13,6 +13,8 @@ namespace Libraries::SaveData {
|
|||
enum class Error : u32;
|
||||
enum class OrbisSaveDataParamType : u32;
|
||||
|
||||
using OrbisUserServiceUserId = s32;
|
||||
|
||||
struct OrbisSaveDataBackup;
|
||||
struct OrbisSaveDataCheckBackupData;
|
||||
struct OrbisSaveDataDelete;
|
||||
|
@ -21,6 +23,11 @@ struct OrbisSaveDataDirNameSearchResult;
|
|||
struct OrbisSaveDataEvent;
|
||||
struct OrbisSaveDataEventParam;
|
||||
struct OrbisSaveDataIcon;
|
||||
struct OrbisSaveDataMemoryGet2;
|
||||
struct OrbisSaveDataMemorySet2;
|
||||
struct OrbisSaveDataMemorySetup2;
|
||||
struct OrbisSaveDataMemorySetupResult;
|
||||
struct OrbisSaveDataMemorySync;
|
||||
struct OrbisSaveDataMount2;
|
||||
struct OrbisSaveDataMount;
|
||||
struct OrbisSaveDataMountInfo;
|
||||
|
@ -28,74 +35,6 @@ struct OrbisSaveDataMountPoint;
|
|||
struct OrbisSaveDataMountResult;
|
||||
struct OrbisSaveDataRestoreBackupData;
|
||||
|
||||
/*typedef u32 OrbisSaveDataSaveDataMemoryOption;
|
||||
#define ORBIS_SAVE_DATA_MEMORY_OPTION_NONE (0x00000000)
|
||||
#define ORBIS_SAVE_DATA_MEMORY_OPTION_SET_PARAM (0x00000001 << 0)
|
||||
#define ORBIS_SAVE_DATA_MEMORY_OPTION_DOUBLE_BUFFER (0x00000001 << 1)
|
||||
|
||||
struct OrbisSaveDataMemorySetup2 {
|
||||
OrbisSaveDataSaveDataMemoryOption option;
|
||||
s32 userId;
|
||||
size_t memorySize;
|
||||
size_t iconMemorySize;
|
||||
const OrbisSaveDataParam* initParam;
|
||||
const OrbisSaveDataIcon* initIcon;
|
||||
u32 slotId;
|
||||
u8 reserved[20];
|
||||
};
|
||||
|
||||
struct OrbisSaveDataMemorySetupResult {
|
||||
size_t existedMemorySize;
|
||||
u8 reserved[16];
|
||||
};
|
||||
|
||||
struct OrbisSaveDataMemoryData {
|
||||
void* buf;
|
||||
size_t bufSize;
|
||||
off_t offset;
|
||||
u8 reserved[40];
|
||||
};
|
||||
|
||||
struct OrbisSaveDataMemoryGet2 {
|
||||
s32 userId;
|
||||
u8 padding[4];
|
||||
OrbisSaveDataMemoryData* data;
|
||||
OrbisSaveDataParam* param;
|
||||
OrbisSaveDataIcon* icon;
|
||||
u32 slotId;
|
||||
u8 reserved[28];
|
||||
};
|
||||
|
||||
struct OrbisSaveDataMemorySet2 {
|
||||
s32 userId;
|
||||
u8 padding[4];
|
||||
const OrbisSaveDataMemoryData* data;
|
||||
const OrbisSaveDataParam* param;
|
||||
const OrbisSaveDataIcon* icon;
|
||||
u32 dataNum;
|
||||
u8 slotId;
|
||||
u8 reserved[24];
|
||||
};
|
||||
|
||||
typedef u32 OrbisSaveDataMemorySyncOption;
|
||||
|
||||
#define SCE_SAVE_DATA_MEMORY_SYNC_OPTION_NONE (0x00000000)
|
||||
#define SCE_SAVE_DATA_MEMORY_SYNC_OPTION_BLOCKING (0x00000001 << 0)
|
||||
|
||||
struct OrbisSaveDataMemorySync {
|
||||
s32 userId;
|
||||
u32 slotId;
|
||||
OrbisSaveDataMemorySyncOption option;
|
||||
u8 reserved[28];
|
||||
};
|
||||
|
||||
constexpr int ORBIS_SAVE_DATA_PARAM_TYPE_ALL = 0;
|
||||
constexpr int ORBIS_SAVE_DATA_PARAM_TYPE_TITLE = 1;
|
||||
constexpr int ORBIS_SAVE_DATA_PARAM_TYPE_SUB_TITLE = 2;
|
||||
constexpr int ORBIS_SAVE_DATA_PARAM_TYPE_DETAIL = 3;
|
||||
constexpr int ORBIS_SAVE_DATA_PARAM_TYPE_USER_PARAM = 4;
|
||||
constexpr int ORBIS_SAVE_DATA_PARAM_TYPE_MTIME = 5;*/
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataAbort();
|
||||
Error PS4_SYSV_ABI sceSaveDataBackup(const OrbisSaveDataBackup* backup);
|
||||
int PS4_SYSV_ABI sceSaveDataBindPsnAccount();
|
||||
|
@ -150,9 +89,9 @@ Error PS4_SYSV_ABI sceSaveDataGetParam(const OrbisSaveDataMountPoint* mountPoint
|
|||
size_t paramBufSize, size_t* gotSize);
|
||||
Error PS4_SYSV_ABI sceSaveDataGetProgress(float* progress);
|
||||
int PS4_SYSV_ABI sceSaveDataGetSaveDataCount();
|
||||
int PS4_SYSV_ABI sceSaveDataGetSaveDataMemory(const u32 userId, void* buf, const size_t bufSize,
|
||||
const int64_t offset);
|
||||
int PS4_SYSV_ABI sceSaveDataGetSaveDataMemory2(/*OrbisSaveDataMemoryGet2* getParam*/);
|
||||
Error PS4_SYSV_ABI sceSaveDataGetSaveDataMemory(OrbisUserServiceUserId userId, void* buf,
|
||||
size_t bufSize, int64_t offset);
|
||||
Error PS4_SYSV_ABI sceSaveDataGetSaveDataMemory2(OrbisSaveDataMemoryGet2* getParam);
|
||||
int PS4_SYSV_ABI sceSaveDataGetSaveDataRootDir();
|
||||
int PS4_SYSV_ABI sceSaveDataGetSaveDataRootPath();
|
||||
int PS4_SYSV_ABI sceSaveDataGetSaveDataRootUsbPath();
|
||||
|
@ -187,17 +126,17 @@ Error PS4_SYSV_ABI sceSaveDataSetParam(const OrbisSaveDataMountPoint* mountPoint
|
|||
OrbisSaveDataParamType paramType, const void* paramBuf,
|
||||
size_t paramBufSize);
|
||||
int PS4_SYSV_ABI sceSaveDataSetSaveDataLibraryUser();
|
||||
int PS4_SYSV_ABI sceSaveDataSetSaveDataMemory(const u32 userId, const void* buf,
|
||||
const size_t bufSize, const int64_t offset);
|
||||
int PS4_SYSV_ABI sceSaveDataSetSaveDataMemory2(/*const OrbisSaveDataMemorySet2* setParam*/);
|
||||
Error PS4_SYSV_ABI sceSaveDataSetSaveDataMemory(OrbisUserServiceUserId userId, void* buf,
|
||||
size_t bufSize, int64_t offset);
|
||||
Error PS4_SYSV_ABI sceSaveDataSetSaveDataMemory2(const OrbisSaveDataMemorySet2* setParam);
|
||||
int PS4_SYSV_ABI sceSaveDataSetupSaveDataMemory(/*u32 userId, size_t memorySize,
|
||||
OrbisSaveDataParam* param*/);
|
||||
int PS4_SYSV_ABI sceSaveDataSetupSaveDataMemory2(/*const OrbisSaveDataMemorySetup2* setupParam,
|
||||
OrbisSaveDataMemorySetupResult* result*/);
|
||||
OrbisSaveDataParam* param*/);
|
||||
Error PS4_SYSV_ABI sceSaveDataSetupSaveDataMemory2(const OrbisSaveDataMemorySetup2* setupParam,
|
||||
OrbisSaveDataMemorySetupResult* result);
|
||||
int PS4_SYSV_ABI sceSaveDataShutdownStart();
|
||||
int PS4_SYSV_ABI sceSaveDataSupportedFakeBrokenStatus();
|
||||
int PS4_SYSV_ABI sceSaveDataSyncCloudList();
|
||||
int PS4_SYSV_ABI sceSaveDataSyncSaveDataMemory(/*OrbisSaveDataMemorySync* syncParam*/);
|
||||
Error PS4_SYSV_ABI sceSaveDataSyncSaveDataMemory(OrbisSaveDataMemorySync* syncParam);
|
||||
Error PS4_SYSV_ABI sceSaveDataTerminate();
|
||||
int PS4_SYSV_ABI sceSaveDataTransferringMount();
|
||||
Error PS4_SYSV_ABI sceSaveDataUmount(const OrbisSaveDataMountPoint* mountPoint);
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include <imgui.h>
|
||||
#include "common/assert.h"
|
||||
#include "imgui/imgui_std.h"
|
||||
|
@ -81,6 +83,21 @@ MsgDialogState::MsgDialogState(const OrbisParam& param) {
|
|||
}
|
||||
}
|
||||
|
||||
MsgDialogState::MsgDialogState(UserState mode) {
|
||||
this->mode = MsgDialogMode::USER_MSG;
|
||||
this->state = mode;
|
||||
}
|
||||
|
||||
MsgDialogState::MsgDialogState(ProgressState mode) {
|
||||
this->mode = MsgDialogMode::PROGRESS_BAR;
|
||||
this->state = mode;
|
||||
}
|
||||
|
||||
MsgDialogState::MsgDialogState(SystemState mode) {
|
||||
this->mode = MsgDialogMode::SYSTEM_MSG;
|
||||
this->state = mode;
|
||||
}
|
||||
|
||||
void MsgDialogUi::DrawUser() {
|
||||
const auto& [button_type, msg, btn_param1, btn_param2] =
|
||||
state->GetState<MsgDialogState::UserState>();
|
||||
|
@ -269,4 +286,16 @@ void MsgDialogUi::Draw() {
|
|||
End();
|
||||
|
||||
first_render = false;
|
||||
}
|
||||
}
|
||||
|
||||
DialogResult Libraries::MsgDialog::ShowMsgDialog(MsgDialogState state, bool block) {
|
||||
DialogResult result{};
|
||||
Status status = Status::RUNNING;
|
||||
MsgDialogUi dialog(&state, &status, &result);
|
||||
if (block) {
|
||||
while (status == Status::RUNNING) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
#include "common/fixed_value.h"
|
||||
|
@ -129,6 +130,11 @@ private:
|
|||
|
||||
public:
|
||||
explicit MsgDialogState(const OrbisParam& param);
|
||||
|
||||
explicit MsgDialogState(UserState mode);
|
||||
explicit MsgDialogState(ProgressState mode);
|
||||
explicit MsgDialogState(SystemState mode);
|
||||
|
||||
MsgDialogState() = default;
|
||||
|
||||
[[nodiscard]] OrbisUserServiceUserId GetUserId() const {
|
||||
|
@ -174,4 +180,8 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
// Utility function to show a message dialog
|
||||
// !!! This function can block !!!
|
||||
DialogResult ShowMsgDialog(MsgDialogState state, bool block = true);
|
||||
|
||||
}; // namespace Libraries::MsgDialog
|
Loading…
Add table
Reference in a new issue