mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-08-09 01:28:41 +00:00
SavaData: backup features
This commit is contained in:
parent
3df2ac60d1
commit
de0a38db0a
9 changed files with 589 additions and 142 deletions
|
@ -232,6 +232,8 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp
|
|||
src/core/libraries/system/msgdialog_ui.cpp
|
||||
src/core/libraries/system/posix.cpp
|
||||
src/core/libraries/system/posix.h
|
||||
src/core/libraries/save_data/save_backup.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/savedata.cpp
|
||||
|
|
|
@ -51,6 +51,8 @@ PSF& PSF::operator=(PSF&& other) noexcept {
|
|||
}
|
||||
|
||||
bool PSF::Open(const std::filesystem::path& filepath) {
|
||||
last_write = std::filesystem::last_write_time(filepath);
|
||||
|
||||
Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Read);
|
||||
if (!file.IsOpen()) {
|
||||
return false;
|
||||
|
@ -125,6 +127,8 @@ bool PSF::Encode(const std::filesystem::path& filepath) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
last_write = std::filesystem::file_time_type::clock::now();
|
||||
|
||||
const auto psf_buffer = Encode();
|
||||
return file.Write(psf_buffer) == psf_buffer.size();
|
||||
}
|
||||
|
|
|
@ -70,11 +70,17 @@ public:
|
|||
void AddString(std::string key, std::string value, bool update = false);
|
||||
void AddInteger(std::string key, s32 value, bool update = false);
|
||||
|
||||
[[nodiscard]] std::filesystem::file_time_type GetLastWrite() const {
|
||||
return last_write;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<Entry>& GetEntries() const {
|
||||
return entry_list;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::filesystem::file_time_type last_write;
|
||||
|
||||
std::vector<Entry> entry_list;
|
||||
|
||||
std::unordered_map<size_t, std::vector<u8>> map_binaries;
|
||||
|
|
199
src/core/libraries/save_data/save_backup.cpp
Normal file
199
src/core/libraries/save_data/save_backup.cpp
Normal file
|
@ -0,0 +1,199 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <semaphore>
|
||||
|
||||
#include <magic_enum.hpp>
|
||||
|
||||
#include "save_backup.h"
|
||||
#include "save_instance.h"
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/logging/log_entry.h"
|
||||
#include "common/polyfill_thread.h"
|
||||
#include "common/thread.h"
|
||||
|
||||
constexpr std::string_view sce_sys = "sce_sys"; // system folder inside save
|
||||
constexpr std::string_view backup_dir = "sce_backup"; // backup folder
|
||||
constexpr std::string_view backup_dir_tmp = "sce_backup_tmp"; // in-progress backup folder
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace Libraries::SaveData::Backup {
|
||||
|
||||
static std::jthread g_backup_thread;
|
||||
static std::counting_semaphore g_backup_thread_semaphore{0};
|
||||
|
||||
static std::mutex g_backup_queue_mutex;
|
||||
static std::deque<BackupRequest> g_backup_queue;
|
||||
static std::deque<BackupRequest> g_result_queue;
|
||||
|
||||
static std::atomic_int g_backup_progress = 0;
|
||||
static std::atomic g_backup_status = WorkerStatus::NotStarted;
|
||||
|
||||
static void backup(const std::filesystem::path& dir_name) {
|
||||
if (!fs::exists(dir_name)) {
|
||||
return;
|
||||
}
|
||||
std::vector<std::filesystem::path> backup_files;
|
||||
for (const auto& entry : fs::directory_iterator(dir_name)) {
|
||||
const auto filename = entry.path().filename();
|
||||
if (filename != backup_dir && filename != backup_dir_tmp) {
|
||||
backup_files.push_back(entry.path());
|
||||
}
|
||||
}
|
||||
const auto backup_dir = dir_name / ::backup_dir;
|
||||
const auto backup_dir_tmp = dir_name / ::backup_dir_tmp;
|
||||
|
||||
g_backup_progress = 0;
|
||||
|
||||
int total_count = static_cast<int>(backup_files.size());
|
||||
int current_count = 0;
|
||||
|
||||
fs::remove_all(backup_dir_tmp);
|
||||
fs::create_directory(backup_dir_tmp);
|
||||
for (const auto& file : backup_files) {
|
||||
fs::copy(file, backup_dir_tmp / file.filename(), fs::copy_options::recursive);
|
||||
current_count++;
|
||||
g_backup_progress = current_count * 100 / total_count;
|
||||
}
|
||||
bool has_existing = fs::exists(backup_dir);
|
||||
if (has_existing) {
|
||||
fs::rename(backup_dir, dir_name / "sce_backup_old");
|
||||
}
|
||||
fs::rename(backup_dir_tmp, backup_dir);
|
||||
if (has_existing) {
|
||||
fs::remove_all(dir_name / "sce_backup_old");
|
||||
}
|
||||
}
|
||||
|
||||
static void BackupThreadBody() {
|
||||
Common::SetCurrentThreadName("SaveData_BackupThread");
|
||||
while (true) {
|
||||
g_backup_status = WorkerStatus::Waiting;
|
||||
g_backup_thread_semaphore.acquire();
|
||||
BackupRequest req;
|
||||
{
|
||||
std::scoped_lock lk{g_backup_queue_mutex};
|
||||
req = g_backup_queue.front();
|
||||
}
|
||||
if (req.save_path.empty()) {
|
||||
break;
|
||||
}
|
||||
g_backup_status = WorkerStatus::Running;
|
||||
LOG_INFO(Lib_SaveData, "Backing up the following directory: {}", req.save_path.string());
|
||||
backup(req.save_path);
|
||||
LOG_DEBUG(Lib_SaveData, "Backing up the following directory: {} finished",
|
||||
req.save_path.string());
|
||||
{
|
||||
std::scoped_lock lk{g_backup_queue_mutex};
|
||||
g_backup_queue.pop_front();
|
||||
g_result_queue.push_back(std::move(req));
|
||||
if (g_result_queue.size() > 20) {
|
||||
g_result_queue.pop_front();
|
||||
}
|
||||
}
|
||||
}
|
||||
g_backup_status = WorkerStatus::NotStarted;
|
||||
}
|
||||
|
||||
void StartThread() {
|
||||
if (g_backup_status != WorkerStatus::NotStarted) {
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG(Lib_SaveData, "Starting backup thread");
|
||||
g_backup_thread = std::jthread{BackupThreadBody};
|
||||
g_backup_status = WorkerStatus::Waiting;
|
||||
}
|
||||
|
||||
void StopThread() {
|
||||
if (g_backup_status == WorkerStatus::NotStarted || g_backup_status == WorkerStatus::Stopping) {
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG(Lib_SaveData, "Stopping backup thread");
|
||||
{
|
||||
std::scoped_lock lk{g_backup_queue_mutex};
|
||||
g_backup_queue.emplace_back(BackupRequest{});
|
||||
}
|
||||
g_backup_thread_semaphore.release();
|
||||
g_backup_status = WorkerStatus::Stopping;
|
||||
}
|
||||
|
||||
bool NewRequest(OrbisUserServiceUserId user_id, std::string_view title_id,
|
||||
std::string_view dir_name, OrbisSaveDataEventType origin) {
|
||||
auto save_path = SaveInstance::MakeDirSavePath(user_id, title_id, dir_name);
|
||||
|
||||
if (g_backup_status != WorkerStatus::Waiting && g_backup_status != WorkerStatus::Running) {
|
||||
LOG_ERROR(Lib_SaveData, "Called backup while status is {}. Backup request to {} ignored",
|
||||
magic_enum::enum_name(g_backup_status.load()), save_path.string());
|
||||
return false;
|
||||
}
|
||||
{
|
||||
std::scoped_lock lk{g_backup_queue_mutex};
|
||||
g_backup_queue.push_back(BackupRequest{
|
||||
.user_id = user_id,
|
||||
.title_id = std::string{title_id},
|
||||
.dir_name = std::string{dir_name},
|
||||
.origin = origin,
|
||||
.save_path = std::move(save_path),
|
||||
});
|
||||
}
|
||||
g_backup_thread_semaphore.release();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Restore(const std::filesystem::path& save_path) {
|
||||
LOG_INFO(Lib_SaveData, "Restoring backup for {}", save_path.string());
|
||||
if (!fs::exists(save_path) || !fs::exists(save_path / backup_dir)) {
|
||||
return false;
|
||||
}
|
||||
for (const auto& entry : fs::directory_iterator(save_path)) {
|
||||
const auto filename = entry.path().filename();
|
||||
if (filename != backup_dir) {
|
||||
fs::remove_all(entry.path());
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& entry : fs::directory_iterator(save_path / backup_dir)) {
|
||||
const auto filename = entry.path().filename();
|
||||
fs::copy(entry.path(), save_path / filename, fs::copy_options::recursive);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
WorkerStatus GetWorkerStatus() {
|
||||
return g_backup_status;
|
||||
}
|
||||
|
||||
bool IsBackupExecutingFor(const std::filesystem::path& save_path) {
|
||||
std::scoped_lock lk{g_backup_queue_mutex};
|
||||
return std::ranges::find(g_backup_queue, save_path,
|
||||
[](const auto& v) { return v.save_path; }) != g_backup_queue.end();
|
||||
}
|
||||
|
||||
std::filesystem::path MakeBackupPath(const std::filesystem::path& save_path) {
|
||||
return save_path / backup_dir;
|
||||
}
|
||||
|
||||
std::optional<BackupRequest> PopLastEvent() {
|
||||
std::scoped_lock lk{g_backup_queue_mutex};
|
||||
if (g_result_queue.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
auto req = std::move(g_result_queue.front());
|
||||
g_result_queue.pop_front();
|
||||
return req;
|
||||
}
|
||||
|
||||
float GetProgress() {
|
||||
return static_cast<float>(g_backup_progress) / 100.0f;
|
||||
}
|
||||
|
||||
void ClearProgress() {
|
||||
g_backup_progress = 0;
|
||||
}
|
||||
|
||||
} // namespace Libraries::SaveData::Backup
|
62
src/core/libraries/save_data/save_backup.h
Normal file
62
src/core/libraries/save_data/save_backup.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Libraries::SaveData {
|
||||
|
||||
using OrbisUserServiceUserId = s32;
|
||||
|
||||
namespace Backup {
|
||||
|
||||
enum class WorkerStatus {
|
||||
NotStarted,
|
||||
Waiting,
|
||||
Running,
|
||||
Stopping,
|
||||
};
|
||||
|
||||
enum class OrbisSaveDataEventType : u32 {
|
||||
UMOUNT_BACKUP = 1,
|
||||
BACKUP = 2,
|
||||
SAVE_DATA_MEMORY_SYNC = 3,
|
||||
};
|
||||
|
||||
struct BackupRequest {
|
||||
OrbisUserServiceUserId user_id{};
|
||||
std::string title_id{};
|
||||
std::string dir_name{};
|
||||
OrbisSaveDataEventType origin{};
|
||||
|
||||
std::filesystem::path save_path{};
|
||||
};
|
||||
|
||||
// No problem calling this function if the backup thread is already running
|
||||
void StartThread();
|
||||
|
||||
void StopThread();
|
||||
|
||||
bool NewRequest(OrbisUserServiceUserId user_id, std::string_view title_id,
|
||||
std::string_view dir_name, OrbisSaveDataEventType origin);
|
||||
|
||||
bool Restore(const std::filesystem::path& save_path);
|
||||
|
||||
WorkerStatus GetWorkerStatus();
|
||||
|
||||
bool IsBackupExecutingFor(const std::filesystem::path& save_path);
|
||||
|
||||
std::filesystem::path MakeBackupPath(const std::filesystem::path& save_path);
|
||||
|
||||
std::optional<BackupRequest> PopLastEvent();
|
||||
|
||||
float GetProgress();
|
||||
|
||||
void ClearProgress();
|
||||
|
||||
} // namespace Backup
|
||||
|
||||
} // namespace Libraries::SaveData
|
|
@ -113,7 +113,6 @@ SaveInstance& SaveInstance::operator=(SaveInstance&& other) noexcept {
|
|||
param_sfo_path = std::move(other.param_sfo_path);
|
||||
corrupt_file_path = std::move(other.corrupt_file_path);
|
||||
corrupt_file = std::move(other.corrupt_file);
|
||||
last_write = other.last_write;
|
||||
param_sfo = std::move(other.param_sfo);
|
||||
mount_point = std::move(other.mount_point);
|
||||
max_blocks = other.max_blocks;
|
||||
|
@ -140,14 +139,12 @@ void SaveInstance::SetupAndMount(bool read_only, bool copy_icon, bool ignore_cor
|
|||
}
|
||||
}
|
||||
exists = true;
|
||||
last_write = std::filesystem::file_time_type::clock::now();
|
||||
} else {
|
||||
if (!ignore_corrupt && fs::exists(corrupt_file_path)) {
|
||||
throw std::filesystem::filesystem_error(
|
||||
"Corrupted save data", corrupt_file_path,
|
||||
std::make_error_code(std::errc::illegal_byte_sequence));
|
||||
}
|
||||
last_write = fs::last_write_time(param_sfo_path);
|
||||
if (!param_sfo.Open(param_sfo_path)) {
|
||||
throw std::filesystem::filesystem_error(
|
||||
"Failed to read param.sfo", param_sfo_path,
|
||||
|
@ -155,7 +152,7 @@ void SaveInstance::SetupAndMount(bool read_only, bool copy_icon, bool ignore_cor
|
|||
}
|
||||
}
|
||||
|
||||
if (!ignore_corrupt) {
|
||||
if (!ignore_corrupt && !read_only) {
|
||||
int err = corrupt_file.Open(corrupt_file_path, Common::FS::FileAccessMode::Write);
|
||||
if (err != 0) {
|
||||
throw std::filesystem::filesystem_error(
|
||||
|
@ -183,7 +180,6 @@ void SaveInstance::Umount() {
|
|||
throw std::filesystem::filesystem_error("Failed to write param.sfo", param_sfo_path,
|
||||
std::make_error_code(std::errc::permission_denied));
|
||||
}
|
||||
last_write = std::filesystem::file_time_type::clock::now();
|
||||
param_sfo = PSF();
|
||||
|
||||
corrupt_file.Close();
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <filesystem>
|
||||
|
||||
#include "common/io_file.h"
|
||||
#include "core/file_format/psf.h"
|
||||
|
||||
namespace Core::FileSys {
|
||||
|
@ -43,7 +44,6 @@ class SaveInstance {
|
|||
|
||||
Common::FS::IOFile corrupt_file;
|
||||
|
||||
std::filesystem::file_time_type last_write;
|
||||
PSF param_sfo;
|
||||
std::string mount_point;
|
||||
|
||||
|
@ -90,6 +90,14 @@ public:
|
|||
return exists;
|
||||
}
|
||||
|
||||
[[nodiscard]] OrbisUserServiceUserId GetUserId() const noexcept {
|
||||
return user_id;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string_view GetTitleId() const noexcept {
|
||||
return game_serial;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string& GetDirName() const noexcept {
|
||||
return dir_name;
|
||||
}
|
||||
|
@ -98,10 +106,6 @@ public:
|
|||
return save_path;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::filesystem::file_time_type& GetLastWrite() const noexcept {
|
||||
return last_write;
|
||||
}
|
||||
|
||||
[[nodiscard]] const PSF& GetParamSFO() const noexcept {
|
||||
return param_sfo;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/save_data/savedata.h"
|
||||
#include "save_backup.h"
|
||||
#include "save_instance.h"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
@ -56,12 +57,12 @@ constexpr u32 OrbisSaveDataBlocksMax = 32768; // 1 GiB
|
|||
constexpr size_t OrbisSaveDataMountPointDataMaxsize = 16;
|
||||
// Maximum size for a title ID (4 uppercase letters + 5 digits)
|
||||
constexpr int OrbisSaveDataTitleIdDataSize = 10;
|
||||
// Maximum size for a save data directory name
|
||||
constexpr int OrbisSaveDataDirnameDataMaxsize = 32;
|
||||
|
||||
constexpr size_t OrbisSaveDataTitleMaxsize = 128;
|
||||
constexpr size_t OrbisSaveDataSubtitleMaxsize = 128;
|
||||
constexpr size_t OrbisSaveDataDetailMaxsize = 1024;
|
||||
constexpr int OrbisSaveDataDirnameDataMaxsize = 32; // Maximum save directory name size
|
||||
constexpr size_t OrbisSaveDataTitleMaxsize = 128; // Maximum title name size
|
||||
constexpr size_t OrbisSaveDataSubtitleMaxsize = 128; // Maximum subtitle name size
|
||||
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";
|
||||
|
@ -120,6 +121,40 @@ struct OrbisSaveDataParam {
|
|||
int : 32;
|
||||
time_t mtime;
|
||||
std::array<u8, 32> _reserved;
|
||||
|
||||
void FromSFO(const PSF& sfo) {
|
||||
memset(this, 0, sizeof(OrbisSaveDataParam));
|
||||
title.FromString(*sfo.GetString(SaveParams::MAINTITLE));
|
||||
subTitle.FromString(*sfo.GetString(SaveParams::SUBTITLE));
|
||||
detail.FromString(*sfo.GetString(SaveParams::DETAIL));
|
||||
userParam = sfo.GetInteger(SaveParams::SAVEDATA_LIST_PARAM).value_or(0);
|
||||
const auto time_since_epoch = sfo.GetLastWrite().time_since_epoch();
|
||||
mtime = chrono::duration_cast<chrono::seconds>(time_since_epoch).count();
|
||||
}
|
||||
};
|
||||
|
||||
struct OrbisSaveDataFingerprint {
|
||||
CString<OrbisSaveDataFingerprintDataSize> data;
|
||||
std::array<u8, 15> _pad;
|
||||
};
|
||||
|
||||
struct OrbisSaveDataBackup {
|
||||
OrbisUserServiceUserId userId;
|
||||
s32 : 32;
|
||||
const OrbisSaveDataTitleId* titleId;
|
||||
const OrbisSaveDataDirName* dirName;
|
||||
const OrbisSaveDataFingerprint* param;
|
||||
std::array<u8, 32> _reserved;
|
||||
};
|
||||
|
||||
struct OrbisSaveDataCheckBackupData {
|
||||
OrbisUserServiceUserId userId;
|
||||
s32 : 32;
|
||||
const OrbisSaveDataTitleId* titleId;
|
||||
const OrbisSaveDataDirName* dirName;
|
||||
OrbisSaveDataParam* param;
|
||||
OrbisSaveDataIcon* icon;
|
||||
std::array<u8, 32> _reserved;
|
||||
};
|
||||
|
||||
struct OrbisSaveDataDelete {
|
||||
|
@ -137,6 +172,19 @@ struct OrbisSaveDataIcon {
|
|||
size_t bufSize;
|
||||
size_t dataSize;
|
||||
std::array<u8, 32> _reserved;
|
||||
|
||||
Error LoadIcon(const std::filesystem::path& icon_path) {
|
||||
try {
|
||||
const Common::FS::IOFile file(icon_path, Common::FS::FileAccessMode::Read);
|
||||
dataSize = file.GetSize();
|
||||
file.Seek(0);
|
||||
file.ReadRaw<u8>(buf, std::min(bufSize, dataSize));
|
||||
} catch (const fs::filesystem_error& e) {
|
||||
LOG_ERROR(Lib_SaveData, "Failed to load icon: {}", e.what());
|
||||
return Error::INTERNAL;
|
||||
}
|
||||
return Error::OK;
|
||||
}
|
||||
};
|
||||
|
||||
struct OrbisSaveDataMount2 {
|
||||
|
@ -179,6 +227,17 @@ struct OrbisSaveDataMountResult {
|
|||
s32 : 32;
|
||||
};
|
||||
|
||||
struct OrbisSaveDataRestoreBackupData {
|
||||
OrbisUserServiceUserId userId;
|
||||
s32 : 32;
|
||||
const OrbisSaveDataTitleId* titleId;
|
||||
const OrbisSaveDataDirName* dirName;
|
||||
const OrbisSaveDataFingerprint* fingerprint;
|
||||
u32 _unused;
|
||||
std::array<u8, 32> _reserved;
|
||||
s32 : 32;
|
||||
};
|
||||
|
||||
struct OrbisSaveDataDirNameSearchCond {
|
||||
OrbisUserServiceUserId userId;
|
||||
int : 32;
|
||||
|
@ -207,6 +266,22 @@ struct OrbisSaveDataDirNameSearchResult {
|
|||
int : 32;
|
||||
};
|
||||
|
||||
struct OrbisSaveDataEventParam { // dummy structure
|
||||
OrbisSaveDataEventParam() = delete;
|
||||
};
|
||||
|
||||
using OrbisSaveDataEventType = Backup::OrbisSaveDataEventType;
|
||||
|
||||
struct OrbisSaveDataEvent {
|
||||
OrbisSaveDataEventType type;
|
||||
s32 errorCode;
|
||||
OrbisUserServiceUserId userId;
|
||||
std::array<u8, 4> _pad;
|
||||
OrbisSaveDataTitleId titleId;
|
||||
OrbisSaveDataDirName dirName;
|
||||
std::array<u8, 40> _reserved;
|
||||
};
|
||||
|
||||
bool is_rw_mode = false;
|
||||
|
||||
static bool g_initialized = false;
|
||||
|
@ -252,16 +327,23 @@ static Error saveDataMount(const OrbisSaveDataMount2* mount_info,
|
|||
return Error::INVALID_LOGIN_USER;
|
||||
}
|
||||
if (mount_info->blocks < OrbisSaveDataBlocksMin2 ||
|
||||
mount_info->blocks > OrbisSaveDataBlocksMax) {
|
||||
mount_info->blocks > OrbisSaveDataBlocksMax || mount_info->dirName == nullptr) {
|
||||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
return Error::PARAMETER;
|
||||
}
|
||||
|
||||
// check backup status
|
||||
{
|
||||
const auto save_path = SaveInstance::MakeDirSavePath(mount_info->userId, g_game_serial,
|
||||
mount_info->dirName->data);
|
||||
if (Backup::IsBackupExecutingFor(save_path)) {
|
||||
return Error::BACKUP_BUSY;
|
||||
}
|
||||
}
|
||||
|
||||
auto mount_mode = mount_info->mountMode;
|
||||
const bool is_ro = True(mount_mode & OrbisSaveDataMountMode::RDONLY);
|
||||
if (!is_ro) {
|
||||
ASSERT(True(mount_mode & OrbisSaveDataMountMode::RDWR));
|
||||
}
|
||||
|
||||
const bool create = True(mount_mode & OrbisSaveDataMountMode::CREATE);
|
||||
const bool create_if_not_exist = True(mount_mode & OrbisSaveDataMountMode::CREATE2);
|
||||
ASSERT(!create || !create_if_not_exist); // Can't have both
|
||||
|
@ -335,14 +417,69 @@ static Error saveDataMount(const OrbisSaveDataMount2* mount_info,
|
|||
return Error::OK;
|
||||
}
|
||||
|
||||
static Error Umount(const OrbisSaveDataMountPoint* mountPoint, bool call_backup = false) {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
}
|
||||
if (mountPoint == nullptr) {
|
||||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
return Error::PARAMETER;
|
||||
}
|
||||
LOG_DEBUG(Lib_SaveData, "Umount mountPoint:{}", mountPoint->data.to_view());
|
||||
const std::string_view mount_point_str{mountPoint->data};
|
||||
for (auto& instance : g_mount_slots) {
|
||||
if (instance.has_value()) {
|
||||
const auto& slot_name = instance->GetMountPoint();
|
||||
if (slot_name == mount_point_str) {
|
||||
if (call_backup) {
|
||||
Backup::StartThread();
|
||||
Backup::NewRequest(instance->GetUserId(), instance->GetTitleId(),
|
||||
instance->GetDirName(),
|
||||
OrbisSaveDataEventType::UMOUNT_BACKUP);
|
||||
}
|
||||
// TODO: check if is busy
|
||||
instance->Umount();
|
||||
instance.reset();
|
||||
return Error::OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Error::NOT_FOUND;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataAbort() {
|
||||
LOG_ERROR(Lib_SaveData, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataBackup() {
|
||||
LOG_ERROR(Lib_SaveData, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
Error PS4_SYSV_ABI sceSaveDataBackup(const OrbisSaveDataBackup* backup) {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
}
|
||||
if (backup == nullptr || backup->dirName == nullptr) {
|
||||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
return Error::PARAMETER;
|
||||
}
|
||||
|
||||
const std::string_view dir_name{backup->dirName->data};
|
||||
LOG_DEBUG(Lib_SaveData, "called dirName: {}", dir_name);
|
||||
|
||||
std::string_view title{backup->titleId != nullptr ? std::string_view{backup->titleId->data}
|
||||
: std::string_view{g_game_serial}};
|
||||
|
||||
for (const auto& instance : g_mount_slots) {
|
||||
if (instance.has_value() && instance->GetUserId() == backup->userId &&
|
||||
instance->GetTitleId() == title && instance->GetDirName() == dir_name) {
|
||||
return Error::BUSY;
|
||||
}
|
||||
}
|
||||
|
||||
Backup::StartThread();
|
||||
Backup::NewRequest(backup->userId, title, dir_name, OrbisSaveDataEventType::BACKUP);
|
||||
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataBindPsnAccount() {
|
||||
|
@ -365,9 +502,54 @@ int PS4_SYSV_ABI sceSaveDataChangeInternal() {
|
|||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataCheckBackupData(/*const OrbisSaveDataCheckBackupData* check*/) {
|
||||
LOG_ERROR(Lib_SaveData, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
Error PS4_SYSV_ABI sceSaveDataCheckBackupData(const OrbisSaveDataCheckBackupData* check) {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
}
|
||||
if (check == nullptr || check->dirName == nullptr) {
|
||||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
return Error::PARAMETER;
|
||||
}
|
||||
|
||||
const std::string_view title{check->titleId != nullptr ? std::string_view{check->titleId->data}
|
||||
: std::string_view{g_game_serial}};
|
||||
|
||||
const auto save_path =
|
||||
SaveInstance::MakeDirSavePath(check->userId, title, check->dirName->data);
|
||||
|
||||
for (const auto& instance : g_mount_slots) {
|
||||
if (instance.has_value() && instance->GetSavePath() == save_path) {
|
||||
return Error::BUSY;
|
||||
}
|
||||
}
|
||||
|
||||
if (Backup::IsBackupExecutingFor(save_path)) {
|
||||
return Error::BACKUP_BUSY;
|
||||
}
|
||||
|
||||
const auto backup_path = Backup::MakeBackupPath(save_path);
|
||||
if (!fs::exists(backup_path)) {
|
||||
return Error::NOT_FOUND;
|
||||
}
|
||||
|
||||
if (check->param != nullptr) {
|
||||
PSF sfo;
|
||||
if (!sfo.Open(backup_path / "sce_sys" / "param.sfo")) {
|
||||
LOG_ERROR(Lib_SaveData, "Failed to read SFO at {}", backup_path.string());
|
||||
return Error::INTERNAL;
|
||||
}
|
||||
check->param->FromSFO(sfo);
|
||||
}
|
||||
|
||||
if (check->icon != nullptr) {
|
||||
const auto icon_path = backup_path / "sce_sys" / "icon0.png";
|
||||
if (fs::exists(icon_path) && check->icon->LoadIcon(icon_path) != Error::OK) {
|
||||
return Error::INTERNAL;
|
||||
}
|
||||
}
|
||||
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataCheckBackupDataForCdlg() {
|
||||
|
@ -405,9 +587,14 @@ int PS4_SYSV_ABI sceSaveDataCheckSaveDataVersionLatest() {
|
|||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataClearProgress() {
|
||||
LOG_ERROR(Lib_SaveData, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
Error PS4_SYSV_ABI sceSaveDataClearProgress() {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
}
|
||||
LOG_DEBUG(Lib_SaveData, "called");
|
||||
Backup::ClearProgress();
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataCopy5() {
|
||||
|
@ -546,7 +733,6 @@ Error PS4_SYSV_ABI sceSaveDataDirNameSearch(const OrbisSaveDataDirNameSearchCond
|
|||
|
||||
std::unordered_map<std::string, PSF> map_dir_sfo;
|
||||
std::unordered_map<std::string, int> map_max_blocks;
|
||||
std::unordered_map<std::string, time_t> map_mtime;
|
||||
std::unordered_map<std::string, OrbisSaveDataBlocks> map_free_size;
|
||||
|
||||
for (const auto& dir_name : dir_list) {
|
||||
|
@ -563,10 +749,6 @@ Error PS4_SYSV_ABI sceSaveDataDirNameSearch(const OrbisSaveDataDirNameSearchCond
|
|||
size_t total = SaveInstance::GetMaxBlocks(dir_path);
|
||||
map_free_size.emplace(dir_name, total - size / OrbisSaveDataBlockSize);
|
||||
map_max_blocks.emplace(dir_name, total);
|
||||
|
||||
const auto last_write = fs::last_write_time(sfo_path).time_since_epoch();
|
||||
size_t mtime = chrono::duration_cast<chrono::seconds>(last_write).count();
|
||||
map_mtime.emplace(dir_name, mtime);
|
||||
}
|
||||
|
||||
#define PROJ(x) [&](const auto& v) { return x; }
|
||||
|
@ -586,7 +768,7 @@ Error PS4_SYSV_ABI sceSaveDataDirNameSearch(const OrbisSaveDataDirNameSearchCond
|
|||
std::ranges::stable_sort(dir_list, {}, PROJ(map_free_size.at(v)));
|
||||
break;
|
||||
case OrbisSaveDataSortKey::MTIME:
|
||||
std::ranges::stable_sort(dir_list, {}, PROJ(map_mtime.at(v)));
|
||||
std::ranges::stable_sort(dir_list, {}, PROJ(map_dir_sfo.at(v).GetLastWrite()));
|
||||
break;
|
||||
}
|
||||
#undef PROJ
|
||||
|
@ -605,14 +787,8 @@ Error PS4_SYSV_ABI sceSaveDataDirNameSearch(const OrbisSaveDataDirNameSearchCond
|
|||
|
||||
if (result->params != nullptr) {
|
||||
auto& sfo = map_dir_sfo.at(dir_list[i]);
|
||||
auto& last_write = map_mtime.at(dir_list[i]);
|
||||
auto& param_data = result->params[i];
|
||||
memset(¶m_data, 0, sizeof(OrbisSaveDataParam));
|
||||
param_data.title.FromString(*sfo.GetString(SaveParams::MAINTITLE));
|
||||
param_data.subTitle.FromString(*sfo.GetString(SaveParams::SUBTITLE));
|
||||
param_data.detail.FromString(*sfo.GetString(SaveParams::DETAIL));
|
||||
param_data.userParam = sfo.GetInteger(SaveParams::SAVEDATA_LIST_PARAM).value_or(0);
|
||||
param_data.mtime = last_write;
|
||||
param_data.FromSFO(sfo);
|
||||
}
|
||||
|
||||
if (result->infos != nullptr) {
|
||||
|
@ -685,10 +861,30 @@ int PS4_SYSV_ABI sceSaveDataGetEventInfo() {
|
|||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataGetEventResult(/*const OrbisSaveDataEventParam* eventParam,
|
||||
OrbisSaveDataEvent* event*/) {
|
||||
LOG_ERROR(Lib_SaveData, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
Error PS4_SYSV_ABI sceSaveDataGetEventResult(const OrbisSaveDataEventParam*,
|
||||
OrbisSaveDataEvent* event) {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
}
|
||||
if (event == nullptr) {
|
||||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
return Error::PARAMETER;
|
||||
}
|
||||
LOG_DEBUG(Lib_SaveData, "called");
|
||||
|
||||
auto last_event = Backup::PopLastEvent();
|
||||
if (!last_event.has_value()) {
|
||||
return Error::NOT_FOUND;
|
||||
}
|
||||
|
||||
event->type = last_event->origin;
|
||||
event->errorCode = 0;
|
||||
event->userId = last_event->user_id;
|
||||
event->titleId.data.FromString(last_event->title_id);
|
||||
event->dirName.data.FromString(last_event->dir_name);
|
||||
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataGetFormat() {
|
||||
|
@ -738,13 +934,11 @@ Error PS4_SYSV_ABI sceSaveDataGetParam(const OrbisSaveDataMountPoint* mountPoint
|
|||
}
|
||||
LOG_DEBUG(Lib_SaveData, "called: paramType = {}", magic_enum::enum_name(paramType));
|
||||
const PSF* param_sfo = nullptr;
|
||||
std::filesystem::file_time_type last_write{};
|
||||
|
||||
const std::string_view mount_point_str{mountPoint->data};
|
||||
for (const auto& instance : g_mount_slots) {
|
||||
if (instance.has_value() && instance->GetMountPoint() == mount_point_str) {
|
||||
param_sfo = &instance->GetParamSFO();
|
||||
last_write = instance->GetLastWrite();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -756,13 +950,7 @@ Error PS4_SYSV_ABI sceSaveDataGetParam(const OrbisSaveDataMountPoint* mountPoint
|
|||
case OrbisSaveDataParamType::ALL: {
|
||||
const auto param = static_cast<OrbisSaveDataParam*>(paramBuf);
|
||||
ASSERT(paramBufSize == sizeof(OrbisSaveDataParam));
|
||||
memset(param, 0, sizeof(OrbisSaveDataParam));
|
||||
param->title.FromString(*param_sfo->GetString(SaveParams::MAINTITLE));
|
||||
param->subTitle.FromString(*param_sfo->GetString(SaveParams::SUBTITLE));
|
||||
param->detail.FromString(*param_sfo->GetString(SaveParams::DETAIL));
|
||||
param->userParam = param_sfo->GetInteger(SaveParams::SAVEDATA_LIST_PARAM).value_or(0);
|
||||
param->mtime =
|
||||
chrono::duration_cast<chrono::seconds>(last_write.time_since_epoch()).count();
|
||||
param->FromSFO(*param_sfo);
|
||||
if (gotSize != nullptr) {
|
||||
*gotSize = sizeof(OrbisSaveDataParam);
|
||||
}
|
||||
|
@ -797,7 +985,8 @@ Error PS4_SYSV_ABI sceSaveDataGetParam(const OrbisSaveDataMountPoint* mountPoint
|
|||
} break;
|
||||
case OrbisSaveDataParamType::MTIME: {
|
||||
const auto param = static_cast<time_t*>(paramBuf);
|
||||
*param = chrono::duration_cast<chrono::seconds>(last_write.time_since_epoch()).count();
|
||||
const auto last_write = param_sfo->GetLastWrite().time_since_epoch();
|
||||
*param = chrono::duration_cast<chrono::seconds>(last_write).count();
|
||||
if (gotSize != nullptr) {
|
||||
*gotSize = sizeof(time_t);
|
||||
}
|
||||
|
@ -809,9 +998,18 @@ Error PS4_SYSV_ABI sceSaveDataGetParam(const OrbisSaveDataMountPoint* mountPoint
|
|||
return Error::OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataGetProgress() {
|
||||
LOG_ERROR(Lib_SaveData, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
Error PS4_SYSV_ABI sceSaveDataGetProgress(float* progress) {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
}
|
||||
if (progress == nullptr) {
|
||||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
return Error::PARAMETER;
|
||||
}
|
||||
LOG_DEBUG(Lib_SaveData, "called");
|
||||
*progress = Backup::GetProgress();
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataGetSaveDataCount() {
|
||||
|
@ -924,17 +1122,7 @@ Error PS4_SYSV_ABI sceSaveDataLoadIcon(const OrbisSaveDataMountPoint* mountPoint
|
|||
return Error::NOT_FOUND;
|
||||
}
|
||||
|
||||
try {
|
||||
const Common::FS::IOFile file(path, Common::FS::FileAccessMode::Read);
|
||||
icon->dataSize = file.GetSize();
|
||||
file.Seek(0);
|
||||
file.ReadRaw<u8>(icon->buf, std::min(icon->bufSize, icon->dataSize));
|
||||
} catch (const fs::filesystem_error& e) {
|
||||
LOG_ERROR(Lib_SaveData, "Failed to load icon: {}", e.what());
|
||||
return Error::INTERNAL;
|
||||
}
|
||||
|
||||
return Error::OK;
|
||||
return icon->LoadIcon(path);
|
||||
}
|
||||
|
||||
Error PS4_SYSV_ABI sceSaveDataMount(const OrbisSaveDataMount* mount,
|
||||
|
@ -1003,9 +1191,44 @@ int PS4_SYSV_ABI sceSaveDataRegisterEventCallback() {
|
|||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataRestoreBackupData() {
|
||||
LOG_ERROR(Lib_SaveData, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
Error PS4_SYSV_ABI sceSaveDataRestoreBackupData(const OrbisSaveDataRestoreBackupData* restore) {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
}
|
||||
if (restore == nullptr || restore->dirName == nullptr) {
|
||||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
return Error::PARAMETER;
|
||||
}
|
||||
|
||||
const std::string_view dir_name{restore->dirName->data};
|
||||
LOG_DEBUG(Lib_SaveData, "called dirName: {}", dir_name);
|
||||
|
||||
std::string_view title{restore->titleId != nullptr ? std::string_view{restore->titleId->data}
|
||||
: std::string_view{g_game_serial}};
|
||||
|
||||
const auto save_path = SaveInstance::MakeDirSavePath(restore->userId, title, dir_name);
|
||||
|
||||
for (const auto& instance : g_mount_slots) {
|
||||
if (instance.has_value() && instance->GetSavePath() == save_path) {
|
||||
return Error::BUSY;
|
||||
}
|
||||
}
|
||||
if (Backup::IsBackupExecutingFor(save_path)) {
|
||||
return Error::BACKUP_BUSY;
|
||||
}
|
||||
|
||||
const auto backup_path = Backup::MakeBackupPath(save_path);
|
||||
if (!fs::exists(backup_path)) {
|
||||
return Error::NOT_FOUND;
|
||||
}
|
||||
|
||||
const bool ok = Backup::Restore(save_path);
|
||||
if (!ok) {
|
||||
return Error::INTERNAL;
|
||||
}
|
||||
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataRestoreBackupDataForCdlg() {
|
||||
|
@ -1185,6 +1408,7 @@ Error PS4_SYSV_ABI sceSaveDataTerminate() {
|
|||
}
|
||||
}
|
||||
g_initialized = false;
|
||||
Backup::StopThread();
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
|
@ -1194,28 +1418,8 @@ int PS4_SYSV_ABI sceSaveDataTransferringMount() {
|
|||
}
|
||||
|
||||
Error PS4_SYSV_ABI sceSaveDataUmount(const OrbisSaveDataMountPoint* mountPoint) {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
}
|
||||
if (mountPoint == nullptr) {
|
||||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
return Error::PARAMETER;
|
||||
}
|
||||
LOG_DEBUG(Lib_SaveData, "called mountPoint:{}", mountPoint->data.to_view());
|
||||
const std::string_view mount_point_str{mountPoint->data};
|
||||
for (auto& instance : g_mount_slots) {
|
||||
if (instance.has_value()) {
|
||||
const auto& slot_name = instance->GetMountPoint();
|
||||
if (slot_name == mount_point_str) {
|
||||
// TODO: check if is busy
|
||||
instance->Umount();
|
||||
instance.reset();
|
||||
return Error::OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Error::NOT_FOUND;
|
||||
LOG_DEBUG(Lib_SaveData, "called");
|
||||
return Umount(mountPoint);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataUmountSys() {
|
||||
|
@ -1223,9 +1427,9 @@ int PS4_SYSV_ABI sceSaveDataUmountSys() {
|
|||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataUmountWithBackup(const OrbisSaveDataMountPoint* mountPoint) {
|
||||
LOG_ERROR(Lib_SaveData, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
Error PS4_SYSV_ABI sceSaveDataUmountWithBackup(const OrbisSaveDataMountPoint* mountPoint) {
|
||||
LOG_DEBUG(Lib_SaveData, "called");
|
||||
return Umount(mountPoint, true);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataUnregisterEventCallback() {
|
||||
|
|
|
@ -10,25 +10,23 @@ class SymbolsResolver;
|
|||
}
|
||||
|
||||
namespace Libraries::SaveData {
|
||||
|
||||
constexpr int ORBIS_SAVE_DATA_FINGERPRINT_DATA_SIZE = 65;
|
||||
struct OrbisSaveDataFingerprint {
|
||||
char data[ORBIS_SAVE_DATA_FINGERPRINT_DATA_SIZE];
|
||||
char padding[15];
|
||||
};
|
||||
|
||||
enum class Error : u32;
|
||||
enum class OrbisSaveDataParamType : u32;
|
||||
|
||||
struct OrbisSaveDataBackup;
|
||||
struct OrbisSaveDataCheckBackupData;
|
||||
struct OrbisSaveDataDelete;
|
||||
struct OrbisSaveDataDirNameSearchCond;
|
||||
struct OrbisSaveDataDirNameSearchResult;
|
||||
struct OrbisSaveDataEvent;
|
||||
struct OrbisSaveDataEventParam;
|
||||
struct OrbisSaveDataIcon;
|
||||
struct OrbisSaveDataMount2;
|
||||
struct OrbisSaveDataMount;
|
||||
struct OrbisSaveDataMountInfo;
|
||||
struct OrbisSaveDataMountPoint;
|
||||
struct OrbisSaveDataMountResult;
|
||||
struct OrbisSaveDataDirNameSearchCond;
|
||||
struct OrbisSaveDataDirNameSearchResult;
|
||||
struct OrbisSaveDataRestoreBackupData;
|
||||
|
||||
/*typedef u32 OrbisSaveDataSaveDataMemoryOption;
|
||||
#define ORBIS_SAVE_DATA_MEMORY_OPTION_NONE (0x00000000)
|
||||
|
@ -51,22 +49,6 @@ struct OrbisSaveDataMemorySetupResult {
|
|||
u8 reserved[16];
|
||||
};
|
||||
|
||||
typedef u32 OrbisSaveDataEventType;
|
||||
#define SCE_SAVE_DATA_EVENT_TYPE_INVALID (0)
|
||||
#define SCE_SAVE_DATA_EVENT_TYPE_UMOUNT_BACKUP_END (1)
|
||||
#define SCE_SAVE_DATA_EVENT_TYPE_BACKUP_END (2)
|
||||
#define SCE_SAVE_DATA_EVENT_TYPE_SAVE_DATA_MEMORY_SYNC_END (3)
|
||||
|
||||
struct OrbisSaveDataEvent {
|
||||
OrbisSaveDataEventType type;
|
||||
s32 errorCode;
|
||||
s32 userId;
|
||||
u8 padding[4];
|
||||
OrbisSaveDataTitleId titleId;
|
||||
OrbisSaveDataDirName dirName;
|
||||
u8 reserved[40];
|
||||
};
|
||||
|
||||
struct OrbisSaveDataMemoryData {
|
||||
void* buf;
|
||||
size_t bufSize;
|
||||
|
@ -95,18 +77,6 @@ struct OrbisSaveDataMemorySet2 {
|
|||
u8 reserved[24];
|
||||
};
|
||||
|
||||
struct OrbisSaveDataCheckBackupData {
|
||||
s32 userId;
|
||||
int : 32;
|
||||
const OrbisSaveDataTitleId* titleId;
|
||||
const OrbisSaveDataDirName* dirName;
|
||||
OrbisSaveDataParam* param;
|
||||
OrbisSaveDataIcon* icon;
|
||||
u8 reserved[32];
|
||||
};
|
||||
|
||||
typedef struct _OrbisSaveDataEventParam OrbisSaveDataEventParam;
|
||||
|
||||
typedef u32 OrbisSaveDataMemorySyncOption;
|
||||
|
||||
#define SCE_SAVE_DATA_MEMORY_SYNC_OPTION_NONE (0x00000000)
|
||||
|
@ -127,12 +97,12 @@ constexpr int ORBIS_SAVE_DATA_PARAM_TYPE_USER_PARAM = 4;
|
|||
constexpr int ORBIS_SAVE_DATA_PARAM_TYPE_MTIME = 5;*/
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataAbort();
|
||||
int PS4_SYSV_ABI sceSaveDataBackup();
|
||||
Error PS4_SYSV_ABI sceSaveDataBackup(const OrbisSaveDataBackup* backup);
|
||||
int PS4_SYSV_ABI sceSaveDataBindPsnAccount();
|
||||
int PS4_SYSV_ABI sceSaveDataBindPsnAccountForSystemBackup();
|
||||
int PS4_SYSV_ABI sceSaveDataChangeDatabase();
|
||||
int PS4_SYSV_ABI sceSaveDataChangeInternal();
|
||||
int PS4_SYSV_ABI sceSaveDataCheckBackupData(/*const OrbisSaveDataCheckBackupData* check*/);
|
||||
Error PS4_SYSV_ABI sceSaveDataCheckBackupData(const OrbisSaveDataCheckBackupData* check);
|
||||
int PS4_SYSV_ABI sceSaveDataCheckBackupDataForCdlg();
|
||||
int PS4_SYSV_ABI sceSaveDataCheckBackupDataInternal();
|
||||
int PS4_SYSV_ABI sceSaveDataCheckCloudData();
|
||||
|
@ -140,7 +110,7 @@ int PS4_SYSV_ABI sceSaveDataCheckIpmiIfSize();
|
|||
int PS4_SYSV_ABI sceSaveDataCheckSaveDataBroken();
|
||||
int PS4_SYSV_ABI sceSaveDataCheckSaveDataVersion();
|
||||
int PS4_SYSV_ABI sceSaveDataCheckSaveDataVersionLatest();
|
||||
int PS4_SYSV_ABI sceSaveDataClearProgress();
|
||||
Error PS4_SYSV_ABI sceSaveDataClearProgress();
|
||||
int PS4_SYSV_ABI sceSaveDataCopy5();
|
||||
int PS4_SYSV_ABI sceSaveDataCreateUploadData();
|
||||
int PS4_SYSV_ABI sceSaveDataDebug();
|
||||
|
@ -169,8 +139,8 @@ int PS4_SYSV_ABI sceSaveDataGetClientThreadPriority();
|
|||
int PS4_SYSV_ABI sceSaveDataGetCloudQuotaInfo();
|
||||
int PS4_SYSV_ABI sceSaveDataGetDataBaseFilePath();
|
||||
int PS4_SYSV_ABI sceSaveDataGetEventInfo();
|
||||
int PS4_SYSV_ABI sceSaveDataGetEventResult(/*const OrbisSaveDataEventParam* eventParam,
|
||||
OrbisSaveDataEvent* event*/);
|
||||
Error PS4_SYSV_ABI sceSaveDataGetEventResult(const OrbisSaveDataEventParam* eventParam,
|
||||
OrbisSaveDataEvent* event);
|
||||
int PS4_SYSV_ABI sceSaveDataGetFormat();
|
||||
int PS4_SYSV_ABI sceSaveDataGetMountedSaveDataCount();
|
||||
Error PS4_SYSV_ABI sceSaveDataGetMountInfo(const OrbisSaveDataMountPoint* mountPoint,
|
||||
|
@ -178,7 +148,7 @@ Error PS4_SYSV_ABI sceSaveDataGetMountInfo(const OrbisSaveDataMountPoint* mountP
|
|||
Error PS4_SYSV_ABI sceSaveDataGetParam(const OrbisSaveDataMountPoint* mountPoint,
|
||||
OrbisSaveDataParamType paramType, void* paramBuf,
|
||||
size_t paramBufSize, size_t* gotSize);
|
||||
int PS4_SYSV_ABI sceSaveDataGetProgress();
|
||||
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);
|
||||
|
@ -206,7 +176,7 @@ int PS4_SYSV_ABI sceSaveDataMountSys();
|
|||
int PS4_SYSV_ABI sceSaveDataPromote5();
|
||||
int PS4_SYSV_ABI sceSaveDataRebuildDatabase();
|
||||
int PS4_SYSV_ABI sceSaveDataRegisterEventCallback();
|
||||
int PS4_SYSV_ABI sceSaveDataRestoreBackupData();
|
||||
Error PS4_SYSV_ABI sceSaveDataRestoreBackupData(const OrbisSaveDataRestoreBackupData* restore);
|
||||
int PS4_SYSV_ABI sceSaveDataRestoreBackupDataForCdlg();
|
||||
int PS4_SYSV_ABI sceSaveDataRestoreLoadSaveDataMemory();
|
||||
Error PS4_SYSV_ABI sceSaveDataSaveIcon(const OrbisSaveDataMountPoint* mountPoint,
|
||||
|
@ -232,7 +202,7 @@ Error PS4_SYSV_ABI sceSaveDataTerminate();
|
|||
int PS4_SYSV_ABI sceSaveDataTransferringMount();
|
||||
Error PS4_SYSV_ABI sceSaveDataUmount(const OrbisSaveDataMountPoint* mountPoint);
|
||||
int PS4_SYSV_ABI sceSaveDataUmountSys();
|
||||
int PS4_SYSV_ABI sceSaveDataUmountWithBackup(const OrbisSaveDataMountPoint* mountPoint);
|
||||
Error PS4_SYSV_ABI sceSaveDataUmountWithBackup(const OrbisSaveDataMountPoint* mountPoint);
|
||||
int PS4_SYSV_ABI sceSaveDataUnregisterEventCallback();
|
||||
int PS4_SYSV_ABI sceSaveDataUpload();
|
||||
int PS4_SYSV_ABI Func_02E4C4D201716422();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue