Merge pull request #3665 from bunnei/device-save
FS: Improve emulation of device saves
This commit is contained in:
commit
65010607b7
|
@ -95,6 +95,10 @@ u32 NACP::GetSupportedLanguages() const {
|
|||
return raw.supported_languages;
|
||||
}
|
||||
|
||||
u64 NACP::GetDeviceSaveDataSize() const {
|
||||
return raw.device_save_data_size;
|
||||
}
|
||||
|
||||
std::vector<u8> NACP::GetRawBytes() const {
|
||||
std::vector<u8> out(sizeof(RawNACP));
|
||||
std::memcpy(out.data(), &raw, sizeof(RawNACP));
|
||||
|
|
|
@ -113,6 +113,7 @@ public:
|
|||
u32 GetSupportedLanguages() const;
|
||||
std::vector<u8> GetRawBytes() const;
|
||||
bool GetUserAccountSwitchLock() const;
|
||||
u64 GetDeviceSaveDataSize() const;
|
||||
|
||||
private:
|
||||
RawNACP raw{};
|
||||
|
|
|
@ -57,7 +57,8 @@ void PrintSaveDataDescriptorWarnings(SaveDataDescriptor meta) {
|
|||
bool ShouldSaveDataBeAutomaticallyCreated(SaveDataSpaceId space, const SaveDataDescriptor& desc) {
|
||||
return desc.type == SaveDataType::CacheStorage || desc.type == SaveDataType::TemporaryStorage ||
|
||||
(space == SaveDataSpaceId::NandUser && ///< Normal Save Data -- Current Title & User
|
||||
desc.type == SaveDataType::SaveData && desc.title_id == 0 && desc.save_id == 0);
|
||||
(desc.type == SaveDataType::SaveData || desc.type == SaveDataType::DeviceSaveData) &&
|
||||
desc.title_id == 0 && desc.save_id == 0);
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
@ -139,8 +140,10 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
|
|||
u128 user_id, u64 save_id) {
|
||||
// According to switchbrew, if a save is of type SaveData and the title id field is 0, it should
|
||||
// be interpreted as the title id of the current process.
|
||||
if (type == SaveDataType::SaveData && title_id == 0) {
|
||||
title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
|
||||
if (type == SaveDataType::SaveData || type == SaveDataType::DeviceSaveData) {
|
||||
if (title_id == 0) {
|
||||
title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
|
||||
}
|
||||
}
|
||||
|
||||
std::string out = GetSaveDataSpaceIdPath(space);
|
||||
|
|
|
@ -767,7 +767,7 @@ FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter)
|
|||
{1014, nullptr, "OutputMultiProgramTagAccessLog"},
|
||||
{1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"},
|
||||
{1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"},
|
||||
{1200, nullptr, "OpenMultiCommitManager"},
|
||||
{1200, &FSP_SRV::OpenMultiCommitManager, "OpenMultiCommitManager"},
|
||||
{1300, nullptr, "OpenBisWiper"},
|
||||
};
|
||||
// clang-format on
|
||||
|
@ -988,4 +988,40 @@ void FSP_SRV::GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx) {
|
|||
rb.Push(access_log_program_index);
|
||||
}
|
||||
|
||||
class IMultiCommitManager final : public ServiceFramework<IMultiCommitManager> {
|
||||
public:
|
||||
explicit IMultiCommitManager() : ServiceFramework("IMultiCommitManager") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{1, &IMultiCommitManager::Add, "Add"},
|
||||
{2, &IMultiCommitManager::Commit, "Commit"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
FileSys::VirtualFile backend;
|
||||
|
||||
void Add(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Commit(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
};
|
||||
|
||||
void FSP_SRV::OpenMultiCommitManager(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_FS, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IMultiCommitManager>(std::make_shared<IMultiCommitManager>());
|
||||
}
|
||||
|
||||
} // namespace Service::FileSystem
|
||||
|
|
|
@ -50,6 +50,7 @@ private:
|
|||
void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
|
||||
void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx);
|
||||
void GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx);
|
||||
void OpenMultiCommitManager(Kernel::HLERequestContext& ctx);
|
||||
|
||||
FileSystemController& fsc;
|
||||
|
||||
|
|
|
@ -488,11 +488,11 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, std::string pat
|
|||
auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
|
||||
navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0);
|
||||
|
||||
connect(open_save_location, &QAction::triggered, [this, program_id]() {
|
||||
emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData);
|
||||
connect(open_save_location, &QAction::triggered, [this, program_id, path]() {
|
||||
emit OpenFolderRequested(GameListOpenTarget::SaveData, path);
|
||||
});
|
||||
connect(open_lfs_location, &QAction::triggered, [this, program_id]() {
|
||||
emit OpenFolderRequested(program_id, GameListOpenTarget::ModData);
|
||||
connect(open_lfs_location, &QAction::triggered, [this, program_id, path]() {
|
||||
emit OpenFolderRequested(GameListOpenTarget::ModData, path);
|
||||
});
|
||||
connect(open_transferable_shader_cache, &QAction::triggered,
|
||||
[this, program_id]() { emit OpenTransferableShaderCacheRequested(program_id); });
|
||||
|
|
|
@ -73,7 +73,7 @@ public:
|
|||
signals:
|
||||
void GameChosen(QString game_path);
|
||||
void ShouldCancelWorker();
|
||||
void OpenFolderRequested(u64 program_id, GameListOpenTarget target);
|
||||
void OpenFolderRequested(GameListOpenTarget target, const std::string& game_path);
|
||||
void OpenTransferableShaderCacheRequested(u64 program_id);
|
||||
void DumpRomFSRequested(u64 program_id, const std::string& game_path);
|
||||
void CopyTIDRequested(u64 program_id);
|
||||
|
|
|
@ -1155,40 +1155,62 @@ void GMainWindow::OnGameListLoadFile(QString game_path) {
|
|||
BootGame(game_path);
|
||||
}
|
||||
|
||||
void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target) {
|
||||
void GMainWindow::OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path) {
|
||||
std::string path;
|
||||
QString open_target;
|
||||
|
||||
const auto v_file = Core::GetGameFileFromPath(vfs, game_path);
|
||||
const auto loader = Loader::GetLoader(v_file);
|
||||
FileSys::NACP control{};
|
||||
u64 program_id{};
|
||||
|
||||
loader->ReadControlData(control);
|
||||
loader->ReadProgramId(program_id);
|
||||
|
||||
const bool has_user_save{control.GetDefaultNormalSaveSize() > 0};
|
||||
const bool has_device_save{control.GetDeviceSaveDataSize() > 0};
|
||||
|
||||
ASSERT_MSG(has_user_save != has_device_save, "Game uses both user and device savedata?");
|
||||
|
||||
switch (target) {
|
||||
case GameListOpenTarget::SaveData: {
|
||||
open_target = tr("Save Data");
|
||||
const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
|
||||
ASSERT(program_id != 0);
|
||||
|
||||
const auto select_profile = [this] {
|
||||
QtProfileSelectionDialog dialog(this);
|
||||
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
|
||||
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
|
||||
dialog.setWindowModality(Qt::WindowModal);
|
||||
if (has_user_save) {
|
||||
// User save data
|
||||
const auto select_profile = [this] {
|
||||
QtProfileSelectionDialog dialog(this);
|
||||
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
|
||||
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
|
||||
dialog.setWindowModality(Qt::WindowModal);
|
||||
|
||||
if (dialog.exec() == QDialog::Rejected) {
|
||||
return -1;
|
||||
if (dialog.exec() == QDialog::Rejected) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return dialog.GetIndex();
|
||||
};
|
||||
|
||||
const auto index = select_profile();
|
||||
if (index == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
return dialog.GetIndex();
|
||||
};
|
||||
|
||||
const auto index = select_profile();
|
||||
if (index == -1) {
|
||||
return;
|
||||
Service::Account::ProfileManager manager;
|
||||
const auto user_id = manager.GetUser(static_cast<std::size_t>(index));
|
||||
ASSERT(user_id);
|
||||
path = nand_dir + FileSys::SaveDataFactory::GetFullPath(
|
||||
FileSys::SaveDataSpaceId::NandUser,
|
||||
FileSys::SaveDataType::SaveData, program_id, user_id->uuid, 0);
|
||||
} else {
|
||||
// Device save data
|
||||
path = nand_dir + FileSys::SaveDataFactory::GetFullPath(
|
||||
FileSys::SaveDataSpaceId::NandUser,
|
||||
FileSys::SaveDataType::SaveData, program_id, {}, 0);
|
||||
}
|
||||
|
||||
Service::Account::ProfileManager manager;
|
||||
const auto user_id = manager.GetUser(static_cast<std::size_t>(index));
|
||||
ASSERT(user_id);
|
||||
path = nand_dir + FileSys::SaveDataFactory::GetFullPath(FileSys::SaveDataSpaceId::NandUser,
|
||||
FileSys::SaveDataType::SaveData,
|
||||
program_id, user_id->uuid, 0);
|
||||
|
||||
if (!FileUtil::Exists(path)) {
|
||||
FileUtil::CreateFullPath(path);
|
||||
FileUtil::CreateDir(path);
|
||||
|
|
|
@ -183,7 +183,7 @@ private slots:
|
|||
void OnMenuReportCompatibility();
|
||||
/// Called whenever a user selects a game in the game list widget.
|
||||
void OnGameListLoadFile(QString game_path);
|
||||
void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target);
|
||||
void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path);
|
||||
void OnTransferableShaderCacheOpenFile(u64 program_id);
|
||||
void OnGameListDumpRomFS(u64 program_id, const std::string& game_path);
|
||||
void OnGameListCopyTID(u64 program_id);
|
||||
|
|
Reference in New Issue