mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-05-21 02:45:00 +00:00
Save fixes (#1031)
* Add ElfInfo to track current game info in a singleton * SaveData compatibility with old firmwares * sceKernelOpen: fix for write-only mode * imgui: add font to render non-ascii characters * save_data: fix Backup Job including old backup in the new backup * Save backup: fix to avoid filling the queue Also limiting 1 backup / 10sec * Save backup: fix search not handling empty pattern *backup time improv
This commit is contained in:
parent
a5001d11a8
commit
10d29cc007
17 changed files with 421 additions and 105 deletions
72
src/common/elf_info.h
Normal file
72
src/common/elf_info.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "assert.h"
|
||||
#include "singleton.h"
|
||||
#include "types.h"
|
||||
|
||||
namespace Core {
|
||||
class Emulator;
|
||||
}
|
||||
|
||||
namespace Common {
|
||||
|
||||
class ElfInfo {
|
||||
friend class Core::Emulator;
|
||||
|
||||
bool initialized = false;
|
||||
|
||||
std::string game_serial{};
|
||||
std::string title{};
|
||||
std::string app_ver{};
|
||||
u32 firmware_ver = 0;
|
||||
u32 raw_firmware_ver = 0;
|
||||
|
||||
public:
|
||||
static constexpr u32 FW_15 = 0x1500000;
|
||||
static constexpr u32 FW_16 = 0x1600000;
|
||||
static constexpr u32 FW_17 = 0x1700000;
|
||||
static constexpr u32 FW_20 = 0x2000000;
|
||||
static constexpr u32 FW_25 = 0x2500000;
|
||||
static constexpr u32 FW_30 = 0x3000000;
|
||||
static constexpr u32 FW_40 = 0x4000000;
|
||||
static constexpr u32 FW_45 = 0x4500000;
|
||||
static constexpr u32 FW_50 = 0x5000000;
|
||||
static constexpr u32 FW_80 = 0x8000000;
|
||||
|
||||
static ElfInfo& Instance() {
|
||||
return *Singleton<ElfInfo>::Instance();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string_view GameSerial() const {
|
||||
ASSERT(initialized);
|
||||
return Instance().game_serial;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string_view Title() const {
|
||||
ASSERT(initialized);
|
||||
return title;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string_view AppVer() const {
|
||||
ASSERT(initialized);
|
||||
return app_ver;
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 FirmwareVer() const {
|
||||
ASSERT(initialized);
|
||||
return firmware_ver;
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 RawFirmwareVer() const {
|
||||
ASSERT(initialized);
|
||||
return raw_firmware_ver;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Common
|
|
@ -89,6 +89,8 @@ int PS4_SYSV_ABI sceKernelOpen(const char* path, int flags, u16 mode) {
|
|||
}
|
||||
// RW, then scekernelWrite is called and savedata is written just fine now.
|
||||
e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::ReadWrite);
|
||||
} else if (write) {
|
||||
e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Write);
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "common/assert.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/elf_info.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/polyfill_thread.h"
|
||||
#include "common/singleton.h"
|
||||
|
@ -243,8 +244,7 @@ int PS4_SYSV_ABI sceKernelConvertUtcToLocaltime(time_t time, time_t* local_time,
|
|||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver) {
|
||||
auto* param_sfo = Common::Singleton<PSF>::Instance();
|
||||
int version = param_sfo->GetInteger("SYSTEM_VER").value_or(0x4700000);
|
||||
int version = Common::ElfInfo::Instance().RawFirmwareVer();
|
||||
LOG_INFO(Kernel, "returned system version = {:#x}", version);
|
||||
*ver = version;
|
||||
return (version > 0) ? ORBIS_OK : ORBIS_KERNEL_ERROR_EINVAL;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/elf_info.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/system/commondialog.h"
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <imgui.h>
|
||||
#include <magic_enum.hpp>
|
||||
|
||||
#include "common/elf_info.h"
|
||||
#include "common/singleton.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
|
@ -14,6 +15,7 @@
|
|||
|
||||
using namespace ImGui;
|
||||
using namespace Libraries::CommonDialog;
|
||||
using Common::ElfInfo;
|
||||
|
||||
constexpr u32 OrbisSaveDataBlockSize = 32768; // 32 KiB
|
||||
|
||||
|
@ -47,7 +49,7 @@ void SaveDialogResult::CopyTo(OrbisSaveDataDialogResult& result) const {
|
|||
result.mode = this->mode;
|
||||
result.result = this->result;
|
||||
result.buttonId = this->button_id;
|
||||
if (has_item) {
|
||||
if (mode == SaveDataDialogMode::LIST || ElfInfo::Instance().FirmwareVer() >= ElfInfo::FW_45) {
|
||||
if (result.dirName != nullptr) {
|
||||
result.dirName->data.FromString(this->dir_name);
|
||||
}
|
||||
|
@ -66,9 +68,7 @@ SaveDialogState::SaveDialogState(const OrbisSaveDataDialogParam& param) {
|
|||
this->enable_back = {param.optionParam->back == OptionBack::ENABLE};
|
||||
}
|
||||
|
||||
const auto content_id = Common::Singleton<PSF>::Instance()->GetString("CONTENT_ID");
|
||||
ASSERT_MSG(content_id.has_value(), "Failed to get CONTENT_ID");
|
||||
static std::string game_serial{*content_id, 7, 9};
|
||||
const auto& game_serial = Common::ElfInfo::Instance().GameSerial();
|
||||
|
||||
const auto item = param.items;
|
||||
this->user_id = item->userId;
|
||||
|
@ -203,6 +203,7 @@ SaveDialogState::SystemState::SystemState(const SaveDialogState& state,
|
|||
auto& sys = *param.sysMsgParam;
|
||||
switch (sys.msgType) {
|
||||
case SystemMessageType::NODATA: {
|
||||
return_cancel = true;
|
||||
this->msg = "There is no saved data";
|
||||
} break;
|
||||
case SystemMessageType::CONFIRM:
|
||||
|
@ -215,6 +216,7 @@ SaveDialogState::SystemState::SystemState(const SaveDialogState& state,
|
|||
M("Do you want to overwrite the existing saved data?", "##UNKNOWN##", "##UNKNOWN##");
|
||||
break;
|
||||
case SystemMessageType::NOSPACE:
|
||||
return_cancel = true;
|
||||
M(fmt::format(
|
||||
"There is not enough space to save the data. To continue {} free space is required.",
|
||||
SpaceSizeToString(sys.value * OrbisSaveDataBlockSize)),
|
||||
|
@ -226,12 +228,15 @@ SaveDialogState::SystemState::SystemState(const SaveDialogState& state,
|
|||
M("Saving...", "Loading...", "Deleting...");
|
||||
break;
|
||||
case SystemMessageType::FILE_CORRUPTED:
|
||||
return_cancel = true;
|
||||
this->msg = "The saved data is corrupted.";
|
||||
break;
|
||||
case SystemMessageType::FINISHED:
|
||||
return_cancel = true;
|
||||
M("Saved successfully.", "Loading complete.", "Deletion complete.");
|
||||
break;
|
||||
case SystemMessageType::NOSPACE_CONTINUABLE:
|
||||
return_cancel = true;
|
||||
M(fmt::format("There is not enough space to save the data. {} free space is required.",
|
||||
SpaceSizeToString(sys.value * OrbisSaveDataBlockSize)),
|
||||
"##UNKNOWN##", "##UNKNOWN##");
|
||||
|
@ -283,29 +288,36 @@ SaveDialogState::ErrorCodeState::ErrorCodeState(const OrbisSaveDataDialogParam&
|
|||
}
|
||||
SaveDialogState::ProgressBarState::ProgressBarState(const SaveDialogState& state,
|
||||
const OrbisSaveDataDialogParam& param) {
|
||||
static auto fw_ver = ElfInfo::Instance().FirmwareVer();
|
||||
|
||||
this->progress = 0;
|
||||
|
||||
auto& bar = *param.progressBarParam;
|
||||
switch (bar.sysMsgType) {
|
||||
case ProgressSystemMessageType::INVALID:
|
||||
this->msg = bar.msg != nullptr ? std::string{bar.msg} : std::string{};
|
||||
break;
|
||||
case ProgressSystemMessageType::PROGRESS:
|
||||
switch (state.type) {
|
||||
case DialogType::SAVE:
|
||||
this->msg = "Saving...";
|
||||
|
||||
if (bar.msg != nullptr) {
|
||||
this->msg = std::string{bar.msg};
|
||||
} else {
|
||||
switch (bar.sysMsgType) {
|
||||
case ProgressSystemMessageType::INVALID:
|
||||
this->msg = "INVALID";
|
||||
break;
|
||||
case DialogType::LOAD:
|
||||
this->msg = "Loading...";
|
||||
case ProgressSystemMessageType::PROGRESS:
|
||||
switch (state.type) {
|
||||
case DialogType::SAVE:
|
||||
this->msg = "Saving...";
|
||||
break;
|
||||
case DialogType::LOAD:
|
||||
this->msg = "Loading...";
|
||||
break;
|
||||
case DialogType::DELETE:
|
||||
this->msg = "Deleting...";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case DialogType::DELETE:
|
||||
this->msg = "Deleting...";
|
||||
case ProgressSystemMessageType::RESTORE:
|
||||
this->msg = "Restoring saved data...";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ProgressSystemMessageType::RESTORE:
|
||||
this->msg = "Restoring saved data...";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -353,11 +365,8 @@ void SaveDialogUi::Finish(ButtonId buttonId, Result r) {
|
|||
result->result = r;
|
||||
result->button_id = buttonId;
|
||||
result->user_data = this->state->user_data;
|
||||
if (state) {
|
||||
if (state->mode != SaveDataDialogMode::LIST && !state->save_list.empty()) {
|
||||
result->dir_name = state->save_list.front().dir_name;
|
||||
}
|
||||
result->has_item = state->mode == SaveDataDialogMode::LIST || !state->save_list.empty();
|
||||
if (state && state->mode != SaveDataDialogMode::LIST && !state->save_list.empty()) {
|
||||
result->dir_name = state->save_list.front().dir_name;
|
||||
}
|
||||
}
|
||||
if (status) {
|
||||
|
@ -385,7 +394,7 @@ void SaveDialogUi::Draw() {
|
|||
};
|
||||
} else {
|
||||
window_size = ImVec2{
|
||||
std::min(io.DisplaySize.x, 500.0f),
|
||||
std::min(io.DisplaySize.x, 600.0f),
|
||||
std::min(io.DisplaySize.y, 300.0f),
|
||||
};
|
||||
}
|
||||
|
@ -453,7 +462,7 @@ void SaveDialogUi::Draw() {
|
|||
}
|
||||
|
||||
void SaveDialogUi::DrawItem(int _id, const SaveDialogState::Item& item, bool clickable) {
|
||||
constexpr auto text_spacing = 1.2f;
|
||||
constexpr auto text_spacing = 0.95f;
|
||||
|
||||
auto& ctx = *GetCurrentContext();
|
||||
auto& window = *ctx.CurrentWindow;
|
||||
|
@ -502,18 +511,20 @@ void SaveDialogUi::DrawItem(int _id, const SaveDialogState::Item& item, bool cli
|
|||
if (!item.title.empty()) {
|
||||
const char* begin = &item.title.front();
|
||||
const char* end = &item.title.back() + 1;
|
||||
SetWindowFontScale(2.0f);
|
||||
SetWindowFontScale(1.5f);
|
||||
RenderText(pos + ImVec2{pos_x, pos_y}, begin, end, false);
|
||||
if (item.is_corrupted) {
|
||||
float width = CalcTextSize(begin, end).x + 10.0f;
|
||||
PushStyleColor(ImGuiCol_Text, 0xFF0000FF);
|
||||
RenderText(pos + ImVec2{pos_x + width, pos_y}, "- Corrupted", nullptr, false);
|
||||
PopStyleColor();
|
||||
}
|
||||
pos_y += ctx.FontSize * text_spacing;
|
||||
}
|
||||
SetWindowFontScale(1.1f);
|
||||
|
||||
SetWindowFontScale(1.3f);
|
||||
if (item.is_corrupted) {
|
||||
pos_y -= ctx.FontSize * text_spacing * 0.3f;
|
||||
const auto bright = (int)std::abs(std::sin(ctx.Time) * 0.15f * 255.0f);
|
||||
PushStyleColor(ImGuiCol_Text, IM_COL32(bright + 216, bright, bright, 0xFF));
|
||||
RenderText(pos + ImVec2{pos_x, pos_y}, "Corrupted", nullptr, false);
|
||||
PopStyleColor();
|
||||
pos_y += ctx.FontSize * text_spacing * 0.8f;
|
||||
}
|
||||
|
||||
if (state->style == ItemStyle::TITLE_SUBTITLE_DATESIZE) {
|
||||
if (!item.subtitle.empty()) {
|
||||
|
@ -643,6 +654,8 @@ void SaveDialogUi::DrawUser() {
|
|||
|
||||
if (!state->save_list.empty()) {
|
||||
DrawItem(0, state->save_list.front(), false);
|
||||
} else if (state->new_item) {
|
||||
DrawItem(0, *state->new_item, false);
|
||||
}
|
||||
|
||||
auto has_btn = btn_type != ButtonType::NONE;
|
||||
|
@ -667,7 +680,7 @@ void SaveDialogUi::DrawUser() {
|
|||
|
||||
if (has_btn) {
|
||||
int count = 1;
|
||||
if (btn_type == ButtonType::YESNO || btn_type == ButtonType::ONCANCEL) {
|
||||
if (btn_type == ButtonType::YESNO || btn_type == ButtonType::OKCANCEL) {
|
||||
++count;
|
||||
}
|
||||
|
||||
|
@ -683,19 +696,28 @@ void SaveDialogUi::DrawUser() {
|
|||
}
|
||||
SameLine();
|
||||
if (Button("No", BUTTON_SIZE)) {
|
||||
Finish(ButtonId::NO);
|
||||
if (ElfInfo::Instance().FirmwareVer() < ElfInfo::FW_45) {
|
||||
Finish(ButtonId::INVALID, Result::USER_CANCELED);
|
||||
} else {
|
||||
Finish(ButtonId::NO);
|
||||
}
|
||||
}
|
||||
if (first_render || IsKeyPressed(ImGuiKey_GamepadFaceRight)) {
|
||||
SetItemCurrentNavFocus();
|
||||
}
|
||||
} else {
|
||||
if (Button("OK", BUTTON_SIZE)) {
|
||||
Finish(ButtonId::OK);
|
||||
if (btn_type == ButtonType::OK &&
|
||||
ElfInfo::Instance().FirmwareVer() < ElfInfo::FW_45) {
|
||||
Finish(ButtonId::INVALID, Result::USER_CANCELED);
|
||||
} else {
|
||||
Finish(ButtonId::OK);
|
||||
}
|
||||
}
|
||||
if (first_render) {
|
||||
SetItemCurrentNavFocus();
|
||||
}
|
||||
if (btn_type == ButtonType::ONCANCEL) {
|
||||
if (btn_type == ButtonType::OKCANCEL) {
|
||||
SameLine();
|
||||
if (Button("Cancel", BUTTON_SIZE)) {
|
||||
Finish(ButtonId::INVALID, Result::USER_CANCELED);
|
||||
|
@ -714,6 +736,8 @@ void SaveDialogUi::DrawSystemMessage() {
|
|||
|
||||
if (!state->save_list.empty()) {
|
||||
DrawItem(0, state->save_list.front(), false);
|
||||
} else if (state->new_item) {
|
||||
DrawItem(0, *state->new_item, false);
|
||||
}
|
||||
|
||||
const auto ws = GetWindowSize();
|
||||
|
@ -737,12 +761,20 @@ void SaveDialogUi::DrawSystemMessage() {
|
|||
});
|
||||
BeginGroup();
|
||||
if (Button(sys_state.show_no ? "Yes" : "OK", BUTTON_SIZE)) {
|
||||
Finish(ButtonId::YES);
|
||||
if (sys_state.return_cancel && ElfInfo::Instance().FirmwareVer() < ElfInfo::FW_45) {
|
||||
Finish(ButtonId::INVALID, Result::USER_CANCELED);
|
||||
} else {
|
||||
Finish(ButtonId::YES);
|
||||
}
|
||||
}
|
||||
SameLine();
|
||||
if (sys_state.show_no) {
|
||||
if (Button("No", BUTTON_SIZE)) {
|
||||
Finish(ButtonId::NO);
|
||||
if (ElfInfo::Instance().FirmwareVer() < ElfInfo::FW_45) {
|
||||
Finish(ButtonId::INVALID, Result::USER_CANCELED);
|
||||
} else {
|
||||
Finish(ButtonId::NO);
|
||||
}
|
||||
}
|
||||
} else if (sys_state.show_cancel) {
|
||||
if (Button("Cancel", BUTTON_SIZE)) {
|
||||
|
@ -760,6 +792,8 @@ void SaveDialogUi::DrawErrorCode() {
|
|||
|
||||
if (!state->save_list.empty()) {
|
||||
DrawItem(0, state->save_list.front(), false);
|
||||
} else if (state->new_item) {
|
||||
DrawItem(0, *state->new_item, false);
|
||||
}
|
||||
|
||||
const auto ws = GetWindowSize();
|
||||
|
@ -775,7 +809,11 @@ void SaveDialogUi::DrawErrorCode() {
|
|||
ws.y - FOOTER_HEIGHT + 5.0f,
|
||||
});
|
||||
if (Button("OK", BUTTON_SIZE)) {
|
||||
Finish(ButtonId::OK);
|
||||
if (ElfInfo::Instance().FirmwareVer() < ElfInfo::FW_45) {
|
||||
Finish(ButtonId::INVALID, Result::USER_CANCELED);
|
||||
} else {
|
||||
Finish(ButtonId::OK);
|
||||
}
|
||||
}
|
||||
if (first_render) {
|
||||
SetItemCurrentNavFocus();
|
||||
|
@ -789,6 +827,8 @@ void SaveDialogUi::DrawProgressBar() {
|
|||
|
||||
if (!state->save_list.empty()) {
|
||||
DrawItem(0, state->save_list.front(), false);
|
||||
} else if (state->new_item) {
|
||||
DrawItem(0, *state->new_item, false);
|
||||
}
|
||||
|
||||
const auto& msg = bar_state.msg;
|
||||
|
|
|
@ -48,7 +48,7 @@ enum class ButtonType : u32 {
|
|||
OK = 0,
|
||||
YESNO = 1,
|
||||
NONE = 2,
|
||||
ONCANCEL = 3,
|
||||
OKCANCEL = 3,
|
||||
};
|
||||
|
||||
enum class UserMessageType : u32 {
|
||||
|
@ -201,7 +201,6 @@ struct SaveDialogResult {
|
|||
std::string dir_name{};
|
||||
PSF param{};
|
||||
void* user_data{};
|
||||
bool has_item{false};
|
||||
|
||||
void CopyTo(OrbisSaveDataDialogResult& result) const;
|
||||
};
|
||||
|
@ -223,6 +222,8 @@ public:
|
|||
bool show_no{}; // Yes instead of OK
|
||||
bool show_cancel{};
|
||||
|
||||
bool return_cancel{};
|
||||
|
||||
SystemState(const SaveDialogState& state, const OrbisSaveDataDialogParam& param);
|
||||
};
|
||||
struct ErrorCodeState {
|
||||
|
|
|
@ -52,7 +52,7 @@ static void backup(const std::filesystem::path& dir_name) {
|
|||
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) {
|
||||
if (filename != ::backup_dir) {
|
||||
backup_files.push_back(entry.path());
|
||||
}
|
||||
}
|
||||
|
@ -80,18 +80,33 @@ static void backup(const std::filesystem::path& dir_name) {
|
|||
|
||||
static void BackupThreadBody() {
|
||||
Common::SetCurrentThreadName("SaveData_BackupThread");
|
||||
while (true) {
|
||||
while (g_backup_status != WorkerStatus::Stopping) {
|
||||
g_backup_status = WorkerStatus::Waiting;
|
||||
g_backup_thread_semaphore.acquire();
|
||||
|
||||
bool wait;
|
||||
BackupRequest req;
|
||||
{
|
||||
std::scoped_lock lk{g_backup_queue_mutex};
|
||||
req = g_backup_queue.front();
|
||||
wait = g_backup_queue.empty();
|
||||
if (!wait) {
|
||||
req = g_backup_queue.front();
|
||||
}
|
||||
}
|
||||
if (wait) {
|
||||
g_backup_thread_semaphore.acquire();
|
||||
{
|
||||
std::scoped_lock lk{g_backup_queue_mutex};
|
||||
if (g_backup_queue.empty()) {
|
||||
continue;
|
||||
}
|
||||
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());
|
||||
try {
|
||||
backup(req.save_path);
|
||||
|
@ -100,6 +115,11 @@ static void BackupThreadBody() {
|
|||
}
|
||||
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.front().done = true;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::seconds(10)); // Don't backup too often
|
||||
{
|
||||
std::scoped_lock lk{g_backup_queue_mutex};
|
||||
g_backup_queue.pop_front();
|
||||
|
@ -117,8 +137,8 @@ void StartThread() {
|
|||
return;
|
||||
}
|
||||
LOG_DEBUG(Lib_SaveData, "Starting backup thread");
|
||||
g_backup_thread = std::jthread{BackupThreadBody};
|
||||
g_backup_status = WorkerStatus::Waiting;
|
||||
g_backup_thread = std::jthread{BackupThreadBody};
|
||||
}
|
||||
|
||||
void StopThread() {
|
||||
|
@ -145,6 +165,12 @@ bool NewRequest(OrbisUserServiceUserId user_id, std::string_view title_id,
|
|||
}
|
||||
{
|
||||
std::scoped_lock lk{g_backup_queue_mutex};
|
||||
for (const auto& it : g_backup_queue) {
|
||||
if (it.dir_name == dir_name) {
|
||||
LOG_TRACE(Lib_SaveData, "Backup request to {} ignored. Already queued", dir_name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
g_backup_queue.push_back(BackupRequest{
|
||||
.user_id = user_id,
|
||||
.title_id = std::string{title_id},
|
||||
|
@ -184,8 +210,9 @@ WorkerStatus GetWorkerStatus() {
|
|||
|
||||
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();
|
||||
const auto& it =
|
||||
std::ranges::find(g_backup_queue, save_path, [](const auto& v) { return v.save_path; });
|
||||
return it != g_backup_queue.end() && !it->done;
|
||||
}
|
||||
|
||||
std::filesystem::path MakeBackupPath(const std::filesystem::path& save_path) {
|
||||
|
|
|
@ -28,6 +28,8 @@ enum class OrbisSaveDataEventType : u32 {
|
|||
};
|
||||
|
||||
struct BackupRequest {
|
||||
bool done{};
|
||||
|
||||
OrbisUserServiceUserId user_id{};
|
||||
std::string title_id{};
|
||||
std::string dir_name{};
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
|
||||
#include "common/assert.h"
|
||||
#include "common/cstring.h"
|
||||
#include "common/elf_info.h"
|
||||
#include "common/enum.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/path_util.h"
|
||||
#include "common/singleton.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/file_format/psf.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
|
@ -28,11 +28,13 @@ namespace fs = std::filesystem;
|
|||
namespace chrono = std::chrono;
|
||||
|
||||
using Common::CString;
|
||||
using Common::ElfInfo;
|
||||
|
||||
namespace Libraries::SaveData {
|
||||
|
||||
enum class Error : u32 {
|
||||
OK = 0,
|
||||
USER_SERVICE_NOT_INITIALIZED = 0x80960002,
|
||||
PARAMETER = 0x809F0000,
|
||||
NOT_INITIALIZED = 0x809F0001,
|
||||
OUT_OF_MEMORY = 0x809F0002,
|
||||
|
@ -191,7 +193,9 @@ struct OrbisSaveDataMemorySetup2 {
|
|||
OrbisUserServiceUserId userId;
|
||||
size_t memorySize;
|
||||
size_t iconMemorySize;
|
||||
// +4.5
|
||||
const OrbisSaveDataParam* initParam;
|
||||
// +4.5
|
||||
const OrbisSaveDataIcon* initIcon;
|
||||
std::array<u8, 24> _reserved;
|
||||
};
|
||||
|
@ -241,6 +245,7 @@ struct OrbisSaveDataMountResult {
|
|||
OrbisSaveDataMountPoint mount_point;
|
||||
OrbisSaveDataBlocks required_blocks;
|
||||
u32 _unused;
|
||||
// +4.5
|
||||
OrbisSaveDataMountStatus mount_status;
|
||||
std::array<u8, 28> _reserved;
|
||||
s32 : 32;
|
||||
|
@ -278,8 +283,11 @@ struct OrbisSaveDataDirNameSearchResult {
|
|||
int : 32;
|
||||
OrbisSaveDataDirName* dirNames;
|
||||
u32 dirNamesNum;
|
||||
// +1.7
|
||||
u32 setNum;
|
||||
// +1.7
|
||||
OrbisSaveDataParam* params;
|
||||
// +2.5
|
||||
OrbisSaveDataSearchInfo* infos;
|
||||
std::array<u8, 12> _reserved;
|
||||
int : 32;
|
||||
|
@ -303,14 +311,13 @@ struct OrbisSaveDataEvent {
|
|||
|
||||
static bool g_initialized = false;
|
||||
static std::string g_game_serial;
|
||||
static u32 g_fw_ver;
|
||||
static std::array<std::optional<SaveInstance>, 16> g_mount_slots;
|
||||
|
||||
static void initialize() {
|
||||
g_initialized = true;
|
||||
static auto* param_sfo = Common::Singleton<PSF>::Instance();
|
||||
const auto content_id = param_sfo->GetString("CONTENT_ID");
|
||||
ASSERT_MSG(content_id.has_value(), "Failed to get CONTENT_ID");
|
||||
g_game_serial = std::string(*content_id, 7, 9);
|
||||
g_game_serial = ElfInfo::Instance().GameSerial();
|
||||
g_fw_ver = ElfInfo::Instance().FirmwareVer();
|
||||
}
|
||||
|
||||
// game_00other | game*other
|
||||
|
@ -339,6 +346,16 @@ static bool match(std::string_view str, std::string_view pattern) {
|
|||
return str_it == str.end() && pat_it == pattern.end();
|
||||
}
|
||||
|
||||
static Error setNotInitializedError() {
|
||||
if (g_fw_ver < ElfInfo::FW_20) {
|
||||
return Error::INTERNAL;
|
||||
}
|
||||
if (g_fw_ver < ElfInfo::FW_25) {
|
||||
return Error::USER_SERVICE_NOT_INITIALIZED;
|
||||
}
|
||||
return Error::NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
static Error saveDataMount(const OrbisSaveDataMount2* mount_info,
|
||||
OrbisSaveDataMountResult* mount_result) {
|
||||
|
||||
|
@ -354,7 +371,7 @@ static Error saveDataMount(const OrbisSaveDataMount2* mount_info,
|
|||
{
|
||||
const auto save_path = SaveInstance::MakeDirSavePath(mount_info->userId, g_game_serial,
|
||||
mount_info->dirName->data);
|
||||
if (Backup::IsBackupExecutingFor(save_path)) {
|
||||
if (Backup::IsBackupExecutingFor(save_path) && g_fw_ver) {
|
||||
return Error::BACKUP_BUSY;
|
||||
}
|
||||
}
|
||||
|
@ -363,11 +380,14 @@ static Error saveDataMount(const OrbisSaveDataMount2* mount_info,
|
|||
const bool is_ro = True(mount_mode & OrbisSaveDataMountMode::RDONLY);
|
||||
|
||||
const bool create = True(mount_mode & OrbisSaveDataMountMode::CREATE);
|
||||
const bool create_if_not_exist = True(mount_mode & OrbisSaveDataMountMode::CREATE2);
|
||||
const bool create_if_not_exist =
|
||||
True(mount_mode & OrbisSaveDataMountMode::CREATE2) && g_fw_ver >= ElfInfo::FW_45;
|
||||
ASSERT(!create || !create_if_not_exist); // Can't have both
|
||||
|
||||
const bool copy_icon = True(mount_mode & OrbisSaveDataMountMode::COPY_ICON);
|
||||
const bool ignore_corrupt = True(mount_mode & OrbisSaveDataMountMode::DESTRUCT_OFF);
|
||||
|
||||
const bool ignore_corrupt =
|
||||
True(mount_mode & OrbisSaveDataMountMode::DESTRUCT_OFF) || g_fw_ver < ElfInfo::FW_16;
|
||||
|
||||
const std::string_view dir_name{mount_info->dirName->data};
|
||||
|
||||
|
@ -439,9 +459,11 @@ static Error saveDataMount(const OrbisSaveDataMount2* mount_info,
|
|||
|
||||
mount_result->mount_point.data.FromString(save_instance.GetMountPoint());
|
||||
|
||||
mount_result->mount_status = create_if_not_exist && to_be_created
|
||||
? OrbisSaveDataMountStatus::CREATED
|
||||
: OrbisSaveDataMountStatus::NOTHING;
|
||||
if (g_fw_ver >= ElfInfo::FW_45) {
|
||||
mount_result->mount_status = create_if_not_exist && to_be_created
|
||||
? OrbisSaveDataMountStatus::CREATED
|
||||
: OrbisSaveDataMountStatus::NOTHING;
|
||||
}
|
||||
|
||||
g_mount_slots[slot_num].emplace(std::move(save_instance));
|
||||
|
||||
|
@ -451,7 +473,7 @@ static Error saveDataMount(const OrbisSaveDataMount2* mount_info,
|
|||
static Error Umount(const OrbisSaveDataMountPoint* mountPoint, bool call_backup = false) {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
return setNotInitializedError();
|
||||
}
|
||||
if (mountPoint == nullptr) {
|
||||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
|
@ -504,7 +526,7 @@ int PS4_SYSV_ABI sceSaveDataAbort() {
|
|||
Error PS4_SYSV_ABI sceSaveDataBackup(const OrbisSaveDataBackup* backup) {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
return setNotInitializedError();
|
||||
}
|
||||
if (backup == nullptr || backup->dirName == nullptr) {
|
||||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
|
@ -553,12 +575,13 @@ int PS4_SYSV_ABI sceSaveDataChangeInternal() {
|
|||
Error PS4_SYSV_ABI sceSaveDataCheckBackupData(const OrbisSaveDataCheckBackupData* check) {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
return setNotInitializedError();
|
||||
}
|
||||
if (check == nullptr || check->dirName == nullptr) {
|
||||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
return Error::PARAMETER;
|
||||
}
|
||||
LOG_DEBUG(Lib_SaveData, "called with titleId={}", check->titleId->data.to_view());
|
||||
|
||||
const std::string_view title{check->titleId != nullptr ? std::string_view{check->titleId->data}
|
||||
: std::string_view{g_game_serial}};
|
||||
|
@ -638,7 +661,7 @@ int PS4_SYSV_ABI sceSaveDataCheckSaveDataVersionLatest() {
|
|||
Error PS4_SYSV_ABI sceSaveDataClearProgress() {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
return setNotInitializedError();
|
||||
}
|
||||
LOG_DEBUG(Lib_SaveData, "called");
|
||||
Backup::ClearProgress();
|
||||
|
@ -693,7 +716,7 @@ int PS4_SYSV_ABI sceSaveDataDebugTarget() {
|
|||
Error PS4_SYSV_ABI sceSaveDataDelete(const OrbisSaveDataDelete* del) {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
return setNotInitializedError();
|
||||
}
|
||||
if (del == nullptr) {
|
||||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
|
@ -745,7 +768,7 @@ Error PS4_SYSV_ABI sceSaveDataDirNameSearch(const OrbisSaveDataDirNameSearchCond
|
|||
OrbisSaveDataDirNameSearchResult* result) {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
return setNotInitializedError();
|
||||
}
|
||||
if (cond == nullptr || result == nullptr || cond->key > OrbisSaveDataSortKey::FREE_BLOCKS ||
|
||||
cond->order > OrbisSaveDataSortOrder::DESCENT) {
|
||||
|
@ -760,7 +783,9 @@ Error PS4_SYSV_ABI sceSaveDataDirNameSearch(const OrbisSaveDataDirNameSearchCond
|
|||
|
||||
if (!fs::exists(save_path)) {
|
||||
result->hitNum = 0;
|
||||
result->setNum = 0;
|
||||
if (g_fw_ver >= ElfInfo::FW_17) {
|
||||
result->setNum = 0;
|
||||
}
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
|
@ -777,9 +802,11 @@ Error PS4_SYSV_ABI sceSaveDataDirNameSearch(const OrbisSaveDataDirNameSearchCond
|
|||
if (cond->dirName != nullptr) {
|
||||
// Filter names
|
||||
const auto pat = Common::ToLower(std::string_view{cond->dirName->data});
|
||||
std::erase_if(dir_list, [&](const std::string& dir_name) {
|
||||
return !match(Common::ToLower(dir_name), pat);
|
||||
});
|
||||
if (!pat.empty()) {
|
||||
std::erase_if(dir_list, [&](const std::string& dir_name) {
|
||||
return !match(Common::ToLower(dir_name), pat);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, PSF> map_dir_sfo;
|
||||
|
@ -828,21 +855,25 @@ Error PS4_SYSV_ABI sceSaveDataDirNameSearch(const OrbisSaveDataDirNameSearchCond
|
|||
std::ranges::reverse(dir_list);
|
||||
}
|
||||
|
||||
result->hitNum = dir_list.size();
|
||||
size_t max_count = std::min(static_cast<size_t>(result->dirNamesNum), dir_list.size());
|
||||
result->setNum = max_count;
|
||||
if (g_fw_ver >= ElfInfo::FW_17) {
|
||||
result->hitNum = dir_list.size();
|
||||
result->setNum = max_count;
|
||||
} else {
|
||||
result->hitNum = max_count;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < max_count; i++) {
|
||||
auto& name_data = result->dirNames[i].data;
|
||||
name_data.FromString(dir_list[i]);
|
||||
|
||||
if (result->params != nullptr) {
|
||||
if (g_fw_ver >= ElfInfo::FW_17 && result->params != nullptr) {
|
||||
auto& sfo = map_dir_sfo.at(dir_list[i]);
|
||||
auto& param_data = result->params[i];
|
||||
param_data.FromSFO(sfo);
|
||||
}
|
||||
|
||||
if (result->infos != nullptr) {
|
||||
if (g_fw_ver >= ElfInfo::FW_25 && result->infos != nullptr) {
|
||||
auto& info = result->infos[i];
|
||||
info.blocks = map_max_blocks.at(dir_list[i]);
|
||||
info.freeBlocks = map_free_size.at(dir_list[i]);
|
||||
|
@ -916,7 +947,7 @@ Error PS4_SYSV_ABI sceSaveDataGetEventResult(const OrbisSaveDataEventParam*,
|
|||
OrbisSaveDataEvent* event) {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
return setNotInitializedError();
|
||||
}
|
||||
if (event == nullptr) {
|
||||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
|
@ -952,7 +983,7 @@ Error PS4_SYSV_ABI sceSaveDataGetMountInfo(const OrbisSaveDataMountPoint* mountP
|
|||
OrbisSaveDataMountInfo* info) {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
return setNotInitializedError();
|
||||
}
|
||||
if (mountPoint == nullptr || info == nullptr) {
|
||||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
|
@ -977,7 +1008,7 @@ Error PS4_SYSV_ABI sceSaveDataGetParam(const OrbisSaveDataMountPoint* mountPoint
|
|||
size_t paramBufSize, size_t* gotSize) {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
return setNotInitializedError();
|
||||
}
|
||||
if (paramType > OrbisSaveDataParamType::MTIME || paramBuf == nullptr) {
|
||||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
|
@ -1052,7 +1083,7 @@ Error PS4_SYSV_ABI sceSaveDataGetParam(const OrbisSaveDataMountPoint* mountPoint
|
|||
Error PS4_SYSV_ABI sceSaveDataGetProgress(float* progress) {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
return setNotInitializedError();
|
||||
}
|
||||
if (progress == nullptr) {
|
||||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
|
@ -1086,7 +1117,7 @@ Error PS4_SYSV_ABI sceSaveDataGetSaveDataMemory(const OrbisUserServiceUserId use
|
|||
Error PS4_SYSV_ABI sceSaveDataGetSaveDataMemory2(OrbisSaveDataMemoryGet2* getParam) {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
return setNotInitializedError();
|
||||
}
|
||||
if (getParam == nullptr) {
|
||||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
|
@ -1182,7 +1213,7 @@ Error PS4_SYSV_ABI sceSaveDataLoadIcon(const OrbisSaveDataMountPoint* mountPoint
|
|||
OrbisSaveDataIcon* icon) {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
return setNotInitializedError();
|
||||
}
|
||||
if (mountPoint == nullptr || icon == nullptr || icon->buf == nullptr) {
|
||||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
|
@ -1211,7 +1242,7 @@ Error PS4_SYSV_ABI sceSaveDataMount(const OrbisSaveDataMount* mount,
|
|||
OrbisSaveDataMountResult* mount_result) {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
return setNotInitializedError();
|
||||
}
|
||||
if (mount == nullptr && mount->dirName != nullptr) {
|
||||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
|
@ -1232,7 +1263,7 @@ Error PS4_SYSV_ABI sceSaveDataMount2(const OrbisSaveDataMount2* mount,
|
|||
OrbisSaveDataMountResult* mount_result) {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
return setNotInitializedError();
|
||||
}
|
||||
if (mount == nullptr && mount->dirName != nullptr) {
|
||||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
|
@ -1276,7 +1307,7 @@ int PS4_SYSV_ABI sceSaveDataRegisterEventCallback() {
|
|||
Error PS4_SYSV_ABI sceSaveDataRestoreBackupData(const OrbisSaveDataRestoreBackupData* restore) {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
return setNotInitializedError();
|
||||
}
|
||||
if (restore == nullptr || restore->dirName == nullptr) {
|
||||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
|
@ -1327,7 +1358,7 @@ Error PS4_SYSV_ABI sceSaveDataSaveIcon(const OrbisSaveDataMountPoint* mountPoint
|
|||
const OrbisSaveDataIcon* icon) {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
return setNotInitializedError();
|
||||
}
|
||||
if (mountPoint == nullptr || icon == nullptr || icon->buf == nullptr) {
|
||||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
|
@ -1375,7 +1406,7 @@ Error PS4_SYSV_ABI sceSaveDataSetParam(const OrbisSaveDataMountPoint* mountPoint
|
|||
size_t paramBufSize) {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
return setNotInitializedError();
|
||||
}
|
||||
if (paramType > OrbisSaveDataParamType::USER_PARAM || mountPoint == nullptr ||
|
||||
paramBuf == nullptr) {
|
||||
|
@ -1440,13 +1471,15 @@ Error PS4_SYSV_ABI sceSaveDataSetSaveDataMemory(OrbisUserServiceUserId userId, v
|
|||
OrbisSaveDataMemorySet2 setParam{};
|
||||
setParam.userId = userId;
|
||||
setParam.data = &data;
|
||||
setParam.param = nullptr;
|
||||
setParam.icon = nullptr;
|
||||
return sceSaveDataSetSaveDataMemory2(&setParam);
|
||||
}
|
||||
|
||||
Error PS4_SYSV_ABI sceSaveDataSetSaveDataMemory2(const OrbisSaveDataMemorySet2* setParam) {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
return setNotInitializedError();
|
||||
}
|
||||
if (setParam == nullptr) {
|
||||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
|
@ -1479,17 +1512,35 @@ Error PS4_SYSV_ABI sceSaveDataSetSaveDataMemory2(const OrbisSaveDataMemorySet2*
|
|||
return Error::OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataSetupSaveDataMemory(/*u32 userId, size_t memorySize,
|
||||
OrbisSaveDataParam* param*/) {
|
||||
LOG_ERROR(Lib_SaveData, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
Error PS4_SYSV_ABI sceSaveDataSetupSaveDataMemory(OrbisUserServiceUserId userId, size_t memorySize,
|
||||
OrbisSaveDataParam* param) {
|
||||
LOG_DEBUG(Lib_SaveData, "called: userId = {}, memorySize = {}", userId, memorySize);
|
||||
OrbisSaveDataMemorySetup2 setupParam{};
|
||||
setupParam.userId = userId;
|
||||
setupParam.memorySize = memorySize;
|
||||
setupParam.initParam = nullptr;
|
||||
setupParam.initIcon = nullptr;
|
||||
OrbisSaveDataMemorySetupResult result{};
|
||||
const auto res = sceSaveDataSetupSaveDataMemory2(&setupParam, &result);
|
||||
if (res != Error::OK) {
|
||||
return res;
|
||||
}
|
||||
if (param != nullptr) {
|
||||
OrbisSaveDataMemorySet2 setParam{};
|
||||
setParam.userId = userId;
|
||||
setParam.data = nullptr;
|
||||
setParam.param = param;
|
||||
setParam.icon = nullptr;
|
||||
sceSaveDataSetSaveDataMemory2(&setParam);
|
||||
}
|
||||
return Error::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;
|
||||
return setNotInitializedError();
|
||||
}
|
||||
if (setupParam == nullptr) {
|
||||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
|
@ -1509,20 +1560,20 @@ Error PS4_SYSV_ABI sceSaveDataSetupSaveDataMemory2(const OrbisSaveDataMemorySetu
|
|||
try {
|
||||
size_t existed_size = SaveMemory::CreateSaveMemory(setupParam->memorySize);
|
||||
if (existed_size == 0) { // Just created
|
||||
if (setupParam->initParam != nullptr) {
|
||||
if (g_fw_ver >= ElfInfo::FW_45 && setupParam->initParam != nullptr) {
|
||||
auto& sfo = SaveMemory::GetParamSFO();
|
||||
setupParam->initParam->ToSFO(sfo);
|
||||
}
|
||||
SaveMemory::SaveSFO();
|
||||
|
||||
auto init_icon = setupParam->initIcon;
|
||||
if (init_icon != nullptr) {
|
||||
if (g_fw_ver >= ElfInfo::FW_45 && init_icon != nullptr) {
|
||||
SaveMemory::SetIcon(init_icon->buf, init_icon->bufSize);
|
||||
} else {
|
||||
SaveMemory::SetIcon(nullptr, 0);
|
||||
}
|
||||
}
|
||||
if (result != nullptr) {
|
||||
if (g_fw_ver >= ElfInfo::FW_45 && result != nullptr) {
|
||||
result->existedMemorySize = existed_size;
|
||||
}
|
||||
} catch (const fs::filesystem_error& e) {
|
||||
|
@ -1558,7 +1609,7 @@ int PS4_SYSV_ABI sceSaveDataSyncCloudList() {
|
|||
Error PS4_SYSV_ABI sceSaveDataSyncSaveDataMemory(OrbisSaveDataMemorySync* syncParam) {
|
||||
if (!g_initialized) {
|
||||
LOG_INFO(Lib_SaveData, "called without initialize");
|
||||
return Error::NOT_INITIALIZED;
|
||||
return setNotInitializedError();
|
||||
}
|
||||
if (syncParam == nullptr) {
|
||||
LOG_INFO(Lib_SaveData, "called with invalid parameter");
|
||||
|
@ -1579,11 +1630,15 @@ Error PS4_SYSV_ABI sceSaveDataSyncSaveDataMemory(OrbisSaveDataMemorySync* syncPa
|
|||
Error PS4_SYSV_ABI sceSaveDataTerminate() {
|
||||
LOG_DEBUG(Lib_SaveData, "called");
|
||||
if (!g_initialized) {
|
||||
return Error::NOT_INITIALIZED;
|
||||
return setNotInitializedError();
|
||||
}
|
||||
for (const auto& instance : g_mount_slots) {
|
||||
for (auto& instance : g_mount_slots) {
|
||||
if (instance.has_value()) {
|
||||
return Error::BUSY;
|
||||
if (g_fw_ver >= ElfInfo::FW_40) {
|
||||
return Error::BUSY;
|
||||
}
|
||||
instance->Umount();
|
||||
instance.reset();
|
||||
}
|
||||
}
|
||||
g_initialized = false;
|
||||
|
|
|
@ -165,8 +165,8 @@ int PS4_SYSV_ABI sceSaveDataSetSaveDataLibraryUser();
|
|||
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*/);
|
||||
Error PS4_SYSV_ABI sceSaveDataSetupSaveDataMemory(OrbisUserServiceUserId userId, size_t memorySize,
|
||||
OrbisSaveDataParam* param);
|
||||
Error PS4_SYSV_ABI sceSaveDataSetupSaveDataMemory2(const OrbisSaveDataMemorySetup2* setupParam,
|
||||
OrbisSaveDataMemorySetupResult* result);
|
||||
int PS4_SYSV_ABI sceSaveDataShutdownStart();
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "common/memory_patcher.h"
|
||||
#endif
|
||||
#include "common/assert.h"
|
||||
#include "common/elf_info.h"
|
||||
#include "common/ntapi.h"
|
||||
#include "common/path_util.h"
|
||||
#include "common/polyfill_thread.h"
|
||||
|
@ -91,10 +92,14 @@ void Emulator::Run(const std::filesystem::path& file) {
|
|||
// Certain games may use /hostapp as well such as CUSA001100
|
||||
mnt->Mount(file.parent_path(), "/hostapp");
|
||||
|
||||
auto& game_info = Common::ElfInfo::Instance();
|
||||
|
||||
// Loading param.sfo file if exists
|
||||
std::string id;
|
||||
std::string title;
|
||||
std::string app_version;
|
||||
u32 fw_version;
|
||||
|
||||
std::filesystem::path sce_sys_folder = file.parent_path() / "sce_sys";
|
||||
if (std::filesystem::is_directory(sce_sys_folder)) {
|
||||
for (const auto& entry : std::filesystem::directory_iterator(sce_sys_folder)) {
|
||||
|
@ -119,7 +124,7 @@ void Emulator::Run(const std::filesystem::path& file) {
|
|||
#endif
|
||||
title = param_sfo->GetString("TITLE").value_or("Unknown title");
|
||||
LOG_INFO(Loader, "Game id: {} Title: {}", id, title);
|
||||
u32 fw_version = param_sfo->GetInteger("SYSTEM_VER").value_or(0x4700000);
|
||||
fw_version = param_sfo->GetInteger("SYSTEM_VER").value_or(0x4700000);
|
||||
app_version = param_sfo->GetString("APP_VER").value_or("Unknown version");
|
||||
LOG_INFO(Loader, "Fw: {:#x} App Version: {}", fw_version, app_version);
|
||||
} else if (entry.path().filename() == "playgo-chunk.dat") {
|
||||
|
@ -141,6 +146,13 @@ void Emulator::Run(const std::filesystem::path& file) {
|
|||
}
|
||||
}
|
||||
|
||||
game_info.initialized = true;
|
||||
game_info.game_serial = id;
|
||||
game_info.title = title;
|
||||
game_info.app_ver = app_version;
|
||||
game_info.firmware_ver = fw_version & 0xFFF00000;
|
||||
game_info.raw_firmware_ver = fw_version;
|
||||
|
||||
std::string game_title = fmt::format("{} - {} <{}>", id, title, app_version);
|
||||
std::string window_title = "";
|
||||
if (Common::isRelease) {
|
||||
|
|
32
src/imgui/renderer/CMakeLists.txt
Normal file
32
src/imgui/renderer/CMakeLists.txt
Normal file
|
@ -0,0 +1,32 @@
|
|||
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
project(ImGui_Resources)
|
||||
|
||||
add_executable(Dear_ImGui_FontEmbed ${CMAKE_SOURCE_DIR}/externals/dear_imgui/misc/fonts/binary_to_compressed_c.cpp)
|
||||
|
||||
set(FONT_LIST
|
||||
NotoSansJP-Regular.ttf
|
||||
)
|
||||
|
||||
set(OutputList "")
|
||||
FOREACH (FONT_FILE ${FONT_LIST})
|
||||
string(REGEX REPLACE "-" "_" fontname ${FONT_FILE})
|
||||
string(TOLOWER ${fontname} fontname)
|
||||
string(REGEX REPLACE ".ttf" "" fontname_cpp ${fontname})
|
||||
set(fontname_cpp "imgui_font_${fontname_cpp}")
|
||||
|
||||
MESSAGE(STATUS "Embedding font ${FONT_FILE}")
|
||||
set(OUTPUT "generated_fonts/imgui_fonts/${fontname}")
|
||||
add_custom_command(
|
||||
OUTPUT "${OUTPUT}.g.cpp"
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "generated_fonts/imgui_fonts"
|
||||
COMMAND $<TARGET_FILE:Dear_ImGui_FontEmbed> -nostatic "${CMAKE_CURRENT_SOURCE_DIR}/fonts/${FONT_FILE}" ${fontname_cpp} > "${OUTPUT}.g.cpp"
|
||||
DEPENDS Dear_ImGui_FontEmbed "fonts/${FONT_FILE}"
|
||||
USES_TERMINAL
|
||||
)
|
||||
list(APPEND OutputList "${OUTPUT}.g.cpp")
|
||||
ENDFOREACH ()
|
||||
|
||||
add_library(ImGui_Resources STATIC ${OutputList})
|
||||
set(IMGUI_RESOURCES_INCLUDE ${CMAKE_CURRENT_BINARY_DIR}/generated_fonts PARENT_SCOPE)
|
BIN
src/imgui/renderer/fonts/NotoSansJP-Regular.ttf
Normal file
BIN
src/imgui/renderer/fonts/NotoSansJP-Regular.ttf
Normal file
Binary file not shown.
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <SDL3/SDL_events.h>
|
||||
#include <imgui.h>
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/path_util.h"
|
||||
#include "imgui/imgui_layer.h"
|
||||
|
@ -14,6 +15,8 @@
|
|||
#include "texture_manager.h"
|
||||
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
||||
|
||||
#include "imgui_fonts/notosansjp_regular.ttf.g.cpp"
|
||||
|
||||
static void CheckVkResult(const vk::Result err) {
|
||||
LOG_ERROR(ImGui, "Vulkan error {}", vk::to_string(err));
|
||||
}
|
||||
|
@ -50,6 +53,22 @@ void Initialize(const ::Vulkan::Instance& instance, const Frontend::WindowSDL& w
|
|||
io.DisplaySize = ImVec2((float)window.getWidth(), (float)window.getHeight());
|
||||
io.IniFilename = SDL_strdup(config_path.string().c_str());
|
||||
io.LogFilename = SDL_strdup(log_path.string().c_str());
|
||||
|
||||
ImFontGlyphRangesBuilder rb{};
|
||||
rb.AddRanges(io.Fonts->GetGlyphRangesDefault());
|
||||
rb.AddRanges(io.Fonts->GetGlyphRangesGreek());
|
||||
rb.AddRanges(io.Fonts->GetGlyphRangesKorean());
|
||||
rb.AddRanges(io.Fonts->GetGlyphRangesJapanese());
|
||||
rb.AddRanges(io.Fonts->GetGlyphRangesCyrillic());
|
||||
ImVector<ImWchar> ranges{};
|
||||
rb.BuildRanges(&ranges);
|
||||
ImFontConfig font_cfg{};
|
||||
font_cfg.OversampleH = 2;
|
||||
font_cfg.OversampleV = 1;
|
||||
io.Fonts->AddFontFromMemoryCompressedTTF(imgui_font_notosansjp_regular_compressed_data,
|
||||
imgui_font_notosansjp_regular_compressed_size, 16.0f,
|
||||
&font_cfg, ranges.Data);
|
||||
|
||||
StyleColorsDark();
|
||||
|
||||
Sdl::Init(window.GetSdlWindow());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue