mirror of
https://github.com/J-D-K/JKSV.git
synced 2026-04-25 07:57:04 -05:00
SaveImportState progress.
This commit is contained in:
parent
a78dd3456a
commit
9d542b4314
|
|
@ -3,6 +3,7 @@
|
|||
#include "appstates/BaseState.hpp"
|
||||
#include "data/data.hpp"
|
||||
#include "fslib.hpp"
|
||||
#include "sys/sys.hpp"
|
||||
#include "ui/ui.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
|
@ -29,13 +30,28 @@ class SaveImportState final : public BaseState
|
|||
|
||||
void render() override;
|
||||
|
||||
// clang-format off
|
||||
// Struct used to pass data to the task.
|
||||
struct DataStruct : sys::Task::DataStruct
|
||||
{
|
||||
data::User *user{};
|
||||
fslib::Path path{};
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
/// @brief Makes things easier to read and type.
|
||||
using TaskData = std::shared_ptr<SaveImportState::DataStruct>;
|
||||
|
||||
private:
|
||||
/// @brief Pointer to the target user.
|
||||
data::User *m_user{};
|
||||
|
||||
/// @brief
|
||||
/// @brief Directory listing of the saves directory.
|
||||
fslib::Directory m_saveDir{};
|
||||
|
||||
/// @brief This is used to pass data to the task function.
|
||||
static inline SaveImportState::TaskData sm_taskData{};
|
||||
|
||||
/// @brief Menu shared by all instances.
|
||||
static inline std::shared_ptr<ui::Menu> sm_saveMenu{};
|
||||
|
||||
|
|
@ -48,6 +64,9 @@ class SaveImportState final : public BaseState
|
|||
/// @brief Opens and
|
||||
void initialize_directory_menu();
|
||||
|
||||
/// @brief Imports a backup selected from the menu.
|
||||
void import_backup();
|
||||
|
||||
/// @brief Cleans up and deactivates the state.
|
||||
void deactivate_state();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,12 +6,6 @@
|
|||
|
||||
namespace fs
|
||||
{
|
||||
/// @brief This is the magic value written to the beginning.
|
||||
constexpr uint32_t SAVE_META_MAGIC = 0x56534B4A;
|
||||
|
||||
/// @brief This is the filename used for the save data meta info.
|
||||
static constexpr std::string_view NAME_SAVE_META = ".nx_save_meta.bin";
|
||||
|
||||
// clang-format off
|
||||
struct SaveMetaData
|
||||
{
|
||||
|
|
@ -23,6 +17,7 @@ namespace fs
|
|||
uint8_t saveDataType{};
|
||||
uint8_t saveDataRank{};
|
||||
uint16_t saveDataIndex{};
|
||||
uint8_t saveDataSpaceID{};
|
||||
uint64_t ownerID{};
|
||||
uint64_t timestamp{};
|
||||
uint32_t flags{};
|
||||
|
|
@ -32,6 +27,14 @@ namespace fs
|
|||
} __attribute__((packed));
|
||||
// clang-format on
|
||||
|
||||
/// @brief This is the magic value written to the beginning.
|
||||
constexpr uint32_t SAVE_META_MAGIC = 0x56534B4A;
|
||||
|
||||
/// @brief This is the filename used for the save data meta info.
|
||||
inline constexpr std::string_view NAME_SAVE_META = ".nx_save_meta.bin";
|
||||
|
||||
inline constexpr ssize_t SIZE_SAVE_META = sizeof(fs::SaveMetaData);
|
||||
|
||||
/// @brief Didn't feel like a whole new file just for this. Fills an fs::SaveMetaData struct.
|
||||
bool fill_save_meta_data(const FsSaveDataInfo *saveInfo, SaveMetaData &meta) noexcept;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
#include "data/data.hpp"
|
||||
#include "fs/SaveMetaData.hpp"
|
||||
|
||||
#include <switch.h>
|
||||
|
||||
|
|
@ -11,6 +12,9 @@ namespace fs
|
|||
/// @return True on success. False on failure.
|
||||
bool create_save_data_for(data::User *targetUser, data::TitleInfo *titleInfo) noexcept;
|
||||
|
||||
/// @brief Creates save data for the user passed using the meta data passed.
|
||||
bool create_save_data_for(data::User *targetUser, const fs::SaveMetaData &saveMeta) noexcept;
|
||||
|
||||
/// @brief Deletes the save data of the FsSaveDataInfo passed.
|
||||
/// @param saveInfo Save data to delete.
|
||||
/// @return True on success. False on failure.
|
||||
|
|
|
|||
8
include/tasks/saveimport.hpp
Normal file
8
include/tasks/saveimport.hpp
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
#include "sys/Task.hpp"
|
||||
|
||||
namespace tasks::saveimport
|
||||
{
|
||||
/// @brief Imports a random save backup according to the meta data.
|
||||
void import_save_backup(sys::threadpool::JobData taskData);
|
||||
}
|
||||
|
|
@ -1,7 +1,12 @@
|
|||
#include "appstates/SaveImportState.hpp"
|
||||
|
||||
#include "appstates/BackupMenuState.hpp"
|
||||
#include "appstates/ConfirmState.hpp"
|
||||
#include "config/config.hpp"
|
||||
#include "input.hpp"
|
||||
#include "strings/strings.hpp"
|
||||
#include "stringutil.hpp"
|
||||
#include "tasks/saveimport.hpp"
|
||||
|
||||
SaveImportState::SaveImportState(data::User *user)
|
||||
: m_user(user)
|
||||
|
|
@ -21,7 +26,7 @@ void SaveImportState::update()
|
|||
const bool aPressed = input::button_pressed(HidNpadButton_A);
|
||||
const bool bPressed = input::button_pressed(HidNpadButton_B);
|
||||
|
||||
if (aPressed) {}
|
||||
if (aPressed) { SaveImportState::import_backup(); }
|
||||
else if (bPressed) { sm_slidePanel->close(); }
|
||||
else if (sm_slidePanel->is_closed()) { SaveImportState::deactivate_state(); }
|
||||
}
|
||||
|
|
@ -38,7 +43,9 @@ void SaveImportState::initialize_static_members()
|
|||
{
|
||||
static constexpr int SIZE_PANEL_WIDTH = 512;
|
||||
|
||||
if (sm_saveMenu && sm_slidePanel) { return; }
|
||||
if (sm_taskData && sm_saveMenu && sm_slidePanel) { return; }
|
||||
|
||||
sm_taskData = std::make_shared<SaveImportState::DataStruct>();
|
||||
|
||||
sm_saveMenu = ui::Menu::create(8, 8, 492, 22, 720);
|
||||
sm_slidePanel = ui::SlideOutPanel::create(SIZE_PANEL_WIDTH, ui::SlideOutPanel::Side::Right);
|
||||
|
|
@ -56,6 +63,22 @@ void SaveImportState::initialize_directory_menu()
|
|||
for (const fslib::DirectoryEntry &entry : m_saveDir) { sm_saveMenu->add_option(entry.get_filename()); }
|
||||
}
|
||||
|
||||
void SaveImportState::import_backup()
|
||||
{
|
||||
const int selected = sm_saveMenu->get_selected();
|
||||
const bool holdRequired = config::get_by_key(config::keys::HOLD_FOR_RESTORATION);
|
||||
|
||||
const fslib::DirectoryEntry &target = m_saveDir[selected];
|
||||
const char *targetName = target.get_filename();
|
||||
sm_taskData->user = m_user;
|
||||
sm_taskData->path = config::get_working_directory() / "saves" / targetName;
|
||||
|
||||
const char *confirmFormat = strings::get_by_name(strings::names::BACKUPMENU_CONFS, 1);
|
||||
std::string query = stringutil::get_formatted_string(confirmFormat, targetName);
|
||||
|
||||
ConfirmProgress::create_and_push(query, holdRequired, tasks::saveimport::import_save_backup, sm_taskData);
|
||||
}
|
||||
|
||||
void SaveImportState::deactivate_state()
|
||||
{
|
||||
sm_saveMenu->reset();
|
||||
|
|
|
|||
|
|
@ -17,20 +17,21 @@ bool fs::fill_save_meta_data(const FsSaveDataInfo *saveInfo, fs::SaveMetaData &m
|
|||
const bool extraRead = fs::read_save_extra_data(saveInfo, extraData);
|
||||
if (!extraRead) { return false; }
|
||||
|
||||
meta = {.magic = fs::SAVE_META_MAGIC,
|
||||
.revision = 0x00,
|
||||
.applicationID = extraData.attr.application_id,
|
||||
.accountID = extraData.attr.uid,
|
||||
.systemSaveID = extraData.attr.system_save_data_id,
|
||||
.saveDataType = extraData.attr.save_data_type,
|
||||
.saveDataRank = extraData.attr.save_data_rank,
|
||||
.saveDataIndex = extraData.attr.save_data_index,
|
||||
.ownerID = extraData.owner_id,
|
||||
.timestamp = extraData.timestamp,
|
||||
.flags = extraData.flags,
|
||||
.saveDataSize = extraData.data_size,
|
||||
.journalSize = extraData.journal_size,
|
||||
.commitID = extraData.commit_id};
|
||||
meta = {.magic = fs::SAVE_META_MAGIC,
|
||||
.revision = 0x01,
|
||||
.applicationID = extraData.attr.application_id,
|
||||
.accountID = extraData.attr.uid,
|
||||
.systemSaveID = extraData.attr.system_save_data_id,
|
||||
.saveDataType = extraData.attr.save_data_type,
|
||||
.saveDataRank = extraData.attr.save_data_rank,
|
||||
.saveDataIndex = extraData.attr.save_data_index,
|
||||
.saveDataSpaceID = saveInfo->save_data_space_id,
|
||||
.ownerID = extraData.owner_id,
|
||||
.timestamp = extraData.timestamp,
|
||||
.flags = extraData.flags,
|
||||
.saveDataSize = extraData.data_size,
|
||||
.journalSize = extraData.journal_size,
|
||||
.commitID = extraData.commit_id};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,13 @@
|
|||
#include "error.hpp"
|
||||
#include "logging/logger.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
inline constexpr FsSaveDataMetaInfo SAVE_CREATE_META = {.size = 0x40060, .type = FsSaveDataMetaType_Thumbnail};
|
||||
}
|
||||
|
||||
bool fs::create_save_data_for(data::User *targetUser, data::TitleInfo *titleInfo) noexcept
|
||||
{
|
||||
static constexpr FsSaveDataMetaInfo saveMeta = {.size = 0x40060, .type = FsSaveDataMetaType_Thumbnail};
|
||||
|
||||
const uint8_t saveType = targetUser->get_account_save_type();
|
||||
const uint64_t applicationID = titleInfo->get_application_id();
|
||||
|
|
@ -29,7 +33,26 @@ bool fs::create_save_data_for(data::User *targetUser, data::TitleInfo *titleInfo
|
|||
.save_data_space_id = FsSaveDataSpaceId_User};
|
||||
|
||||
// I want this recorded.
|
||||
return error::libnx(fsCreateSaveDataFileSystem(&saveAttributes, &saveCreation, &saveMeta)) == false;
|
||||
return error::libnx(fsCreateSaveDataFileSystem(&saveAttributes, &saveCreation, &SAVE_CREATE_META)) == false;
|
||||
}
|
||||
|
||||
bool fs::create_save_data_for(data::User *targetUser, const fs::SaveMetaData &saveMeta) noexcept
|
||||
{
|
||||
const FsSaveDataAttribute saveAttributes = {.application_id = saveMeta.applicationID,
|
||||
.uid = targetUser->get_account_id(),
|
||||
.system_save_data_id = saveMeta.systemSaveID,
|
||||
.save_data_type = saveMeta.saveDataType,
|
||||
.save_data_rank = saveMeta.saveDataRank,
|
||||
.save_data_index = saveMeta.saveDataIndex};
|
||||
|
||||
const FsSaveDataCreationInfo saveCreation = {.save_data_size = saveMeta.saveDataSize,
|
||||
.journal_size = saveMeta.journalSize,
|
||||
.available_size = 0x4000,
|
||||
.owner_id = saveMeta.ownerID,
|
||||
.flags = 0,
|
||||
.save_data_space_id = saveMeta.saveDataSpaceID};
|
||||
|
||||
return error::libnx(fsCreateSaveDataFileSystem(&saveAttributes, &saveCreation, &SAVE_CREATE_META)) == false;
|
||||
}
|
||||
|
||||
bool fs::delete_save_data(const FsSaveDataInfo *saveInfo) noexcept
|
||||
|
|
@ -72,4 +95,4 @@ bool fs::read_save_extra_data(const FsSaveDataInfo *saveInfo, FsSaveDataExtraDat
|
|||
fsReadSaveDataFileSystemExtraDataBySaveDataSpaceId(&extraOut, EXTRA_SIZE, spaceID, saveInfo->save_data_id));
|
||||
|
||||
return !readError;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ namespace
|
|||
{
|
||||
constexpr const char *STRING_ZIP_EXT = ".zip";
|
||||
constexpr const char *PATH_JKSV_TEMP = "sdmc:/jksvTemp.zip"; // This is named this so if something fails, people know.
|
||||
constexpr size_t SIZE_SAVE_META = sizeof(fs::SaveMetaData);
|
||||
}
|
||||
|
||||
// Definitions at bottom.
|
||||
|
|
@ -267,8 +266,12 @@ void tasks::backup::restore_backup_local(sys::threadpool::JobData taskData)
|
|||
fs::copy_file_commit(target, fs::DEFAULT_SAVE_ROOT, journalSize, task);
|
||||
}
|
||||
|
||||
spawningState->save_data_written();
|
||||
spawningState->refresh();
|
||||
if (spawningState)
|
||||
{
|
||||
spawningState->save_data_written();
|
||||
spawningState->refresh();
|
||||
}
|
||||
|
||||
task->complete();
|
||||
}
|
||||
|
||||
|
|
@ -575,7 +578,7 @@ static bool read_and_process_meta(const fslib::Path &targetDir, BackupMenuState:
|
|||
}
|
||||
|
||||
fs::SaveMetaData metaData{};
|
||||
const bool metaRead = metaFile.read(&metaData, SIZE_SAVE_META) == SIZE_SAVE_META;
|
||||
const bool metaRead = metaFile.read(&metaData, fs::SIZE_SAVE_META) <= fs::SIZE_SAVE_META;
|
||||
const bool processed = metaRead && fs::process_save_meta_data(saveInfo, metaData);
|
||||
if (!metaRead || !processed)
|
||||
{
|
||||
|
|
@ -618,7 +621,7 @@ static bool read_and_process_meta(fs::MiniUnzip &unzip, BackupMenuState::TaskDat
|
|||
return false;
|
||||
}
|
||||
|
||||
const bool metaRead = metaFound && unzip.read(&saveMeta, SIZE_SAVE_META) == SIZE_SAVE_META;
|
||||
const bool metaRead = metaFound && unzip.read(&saveMeta, fs::SIZE_SAVE_META) <= fs::SIZE_SAVE_META;
|
||||
const bool metaProcessed = metaRead && fs::process_save_meta_data(saveInfo, saveMeta);
|
||||
if (!metaRead || !metaProcessed)
|
||||
{
|
||||
|
|
@ -638,14 +641,14 @@ static void write_meta_file(const fslib::Path &target, const FsSaveDataInfo *sav
|
|||
fs::SaveMetaData saveMeta{};
|
||||
const fslib::Path metaPath{target / fs::NAME_SAVE_META};
|
||||
const bool hasMeta = fs::fill_save_meta_data(saveInfo, saveMeta);
|
||||
fslib::File metaFile{metaPath, FsOpenMode_Create | FsOpenMode_Write, SIZE_SAVE_META};
|
||||
fslib::File metaFile{metaPath, FsOpenMode_Create | FsOpenMode_Write, fs::SIZE_SAVE_META};
|
||||
if (!metaFile.is_open() || !hasMeta)
|
||||
{
|
||||
ui::PopMessageManager::push_message(popTicks, popErrorWritingMeta);
|
||||
return;
|
||||
}
|
||||
|
||||
const bool metaWritten = metaFile.write(&saveMeta, SIZE_SAVE_META) == SIZE_SAVE_META;
|
||||
const bool metaWritten = metaFile.write(&saveMeta, fs::SIZE_SAVE_META) <= fs::SIZE_SAVE_META;
|
||||
if (!metaWritten) { ui::PopMessageManager::push_message(popTicks, popErrorWritingMeta); }
|
||||
}
|
||||
|
||||
|
|
@ -656,7 +659,7 @@ static void write_meta_zip(fs::MiniZip &zip, const FsSaveDataInfo *saveInfo)
|
|||
fs::SaveMetaData saveMeta{};
|
||||
const bool hasMeta = fs::fill_save_meta_data(saveInfo, saveMeta);
|
||||
const bool openMeta = hasMeta && zip.open_new_file(fs::NAME_SAVE_META);
|
||||
const bool writeMeta = openMeta && zip.write(&saveMeta, SIZE_SAVE_META);
|
||||
const bool writeMeta = openMeta && zip.write(&saveMeta, fs::SIZE_SAVE_META);
|
||||
const bool closeMeta = openMeta && zip.close_current_file();
|
||||
if (hasMeta && (!openMeta || !writeMeta || !closeMeta))
|
||||
{
|
||||
|
|
|
|||
104
source/tasks/saveimport.cpp
Normal file
104
source/tasks/saveimport.cpp
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
#include "tasks/saveimport.hpp"
|
||||
|
||||
#include "appstates/SaveImportState.hpp"
|
||||
#include "error.hpp"
|
||||
#include "fs/fs.hpp"
|
||||
#include "fslib.hpp"
|
||||
#include "strings/strings.hpp"
|
||||
#include "tasks/backup.hpp"
|
||||
#include "ui/PopMessageManager.hpp"
|
||||
|
||||
// Defined at bottom.
|
||||
static bool read_save_meta(const fslib::Path &path, fs::SaveMetaData &metaOut);
|
||||
static bool test_for_save(data::User *user, const fs::SaveMetaData &saveMeta);
|
||||
|
||||
void tasks::saveimport::import_save_backup(sys::threadpool::JobData taskData)
|
||||
{
|
||||
auto castData = std::static_pointer_cast<SaveImportState::DataStruct>(taskData);
|
||||
|
||||
sys::ProgressTask *task = static_cast<sys::ProgressTask *>(castData->task);
|
||||
if (error::is_null(task)) { return; }
|
||||
|
||||
data::User *user = castData->user;
|
||||
const fslib::Path &target = castData->path;
|
||||
|
||||
fs::SaveMetaData metaData{};
|
||||
const bool metaRead = read_save_meta(target, metaData);
|
||||
if (!metaRead) { TASK_FINISH_RETURN(task); }
|
||||
|
||||
const bool saveExists = test_for_save(user, metaData);
|
||||
if (!saveExists) { fs::create_save_data_for(user, metaData); }
|
||||
}
|
||||
|
||||
static bool read_save_meta(const fslib::Path &path, fs::SaveMetaData &metaOut)
|
||||
{
|
||||
const bool isDir = fslib::directory_exists(path);
|
||||
if (isDir)
|
||||
{
|
||||
const fslib::Path metaPath{path / fs::NAME_SAVE_META};
|
||||
const bool exists = fslib::file_exists(metaPath);
|
||||
if (!exists) { return false; }
|
||||
|
||||
fslib::File metaFile{metaPath, FsOpenMode_Read};
|
||||
const bool metaRead = metaFile.is_open() && metaFile.get_size() <= fs::SIZE_SAVE_META &&
|
||||
metaFile.read(&metaOut, fs::SIZE_SAVE_META) <= fs::SIZE_SAVE_META;
|
||||
const bool magicMatch = metaRead && metaOut.magic == fs::SAVE_META_MAGIC;
|
||||
if (!metaRead || !magicMatch) { return false; }
|
||||
}
|
||||
else
|
||||
{
|
||||
fs::MiniUnzip zip{path};
|
||||
if (!zip.is_open()) { return false; }
|
||||
|
||||
const bool located = zip.locate_file(fs::NAME_SAVE_META);
|
||||
const bool sizeMatch = located && zip.get_uncompressed_size() <= fs::SIZE_SAVE_META;
|
||||
const bool metaRead = sizeMatch && zip.read(&metaOut, fs::SIZE_SAVE_META) <= fs::SIZE_SAVE_META;
|
||||
const bool magicMatch = metaRead && metaOut.magic == fs::SAVE_META_MAGIC;
|
||||
if (!metaRead || !magicMatch) { return false; }
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool test_for_save(data::User *user, const fs::SaveMetaData &saveMeta)
|
||||
{
|
||||
const FsSaveDataType saveType = user->get_account_save_type();
|
||||
|
||||
bool mounted{};
|
||||
switch (saveType)
|
||||
{
|
||||
case FsSaveDataType_Account:
|
||||
{
|
||||
mounted =
|
||||
fslib::open_account_save_file_system(fs::DEFAULT_SAVE_MOUNT, saveMeta.applicationID, user->get_account_id());
|
||||
}
|
||||
break;
|
||||
|
||||
case FsSaveDataType_Bcat:
|
||||
{
|
||||
mounted = fslib::open_bcat_save_file_system(fs::DEFAULT_SAVE_MOUNT, saveMeta.applicationID);
|
||||
}
|
||||
break;
|
||||
|
||||
case FsSaveDataType_Device:
|
||||
{
|
||||
mounted = fslib::open_device_save_file_system(fs::DEFAULT_SAVE_MOUNT, saveMeta.applicationID);
|
||||
}
|
||||
break;
|
||||
|
||||
case FsSaveDataType_Cache:
|
||||
{
|
||||
mounted = fslib::open_cache_save_file_system(fs::DEFAULT_SAVE_MOUNT,
|
||||
saveMeta.applicationID,
|
||||
saveMeta.saveDataIndex,
|
||||
static_cast<FsSaveDataSpaceId>(saveMeta.saveDataIndex));
|
||||
}
|
||||
break;
|
||||
|
||||
default: return false;
|
||||
}
|
||||
|
||||
if (mounted) { fslib::close_file_system(fs::DEFAULT_SAVE_MOUNT); }
|
||||
|
||||
return mounted;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user