mirror of
https://github.com/J-D-K/JKSV.git
synced 2026-08-28 05:24:31 -05:00
Implement processing save data meta files.
This commit is contained in:
@@ -6,12 +6,13 @@
|
||||
namespace fs
|
||||
{
|
||||
/// @brief This is the magic value written to the beginning.
|
||||
constexpr uint64_t SAVE_META_MAGIC = 0x56534B4A;
|
||||
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 = ".jksv_save_meta.bin";
|
||||
|
||||
/// @brief This struct is for storing the data necessary to restore saves to a different console.
|
||||
/// @note Some of this data isn't really needed. Just rather be safe than sorry.
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
/// @brief Magic.
|
||||
@@ -32,8 +33,8 @@ namespace fs
|
||||
int64_t m_journalSize;
|
||||
/// @brief Maximum journaling size.
|
||||
int64_t m_journalSizeMax;
|
||||
/// @brief Total size of the files in the backup. For ZIP, this is uncompressed.
|
||||
uint64_t m_totalSaveSize;
|
||||
/// @brief Total size of the container upon backup.
|
||||
int64_t m_totalSaveSize;
|
||||
} SaveMetaData;
|
||||
|
||||
/// @brief Didn't feel like a whole new file just for this. Fills an fs::SaveMetaData struct using the passed TitleInfo pointer.
|
||||
@@ -41,4 +42,10 @@ namespace fs
|
||||
/// @param info Reference to FsSaveDataInfo struct to use to fill out the meta struct.
|
||||
/// @param meta Struct to fill.
|
||||
void create_save_meta_data(data::TitleInfo *titleInfo, const FsSaveDataInfo *saveInfo, SaveMetaData &meta);
|
||||
|
||||
/// @brief Processes the save meta data and applies it to the passed saveInfo pointer.
|
||||
/// @param saveInfo FsSaveDataInfo to apply the meta to.
|
||||
/// @param meta Save meta data to apply.
|
||||
bool process_save_meta_data(const FsSaveDataInfo *saveInfo, SaveMetaData &meta);
|
||||
|
||||
} // namespace fs
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace strings
|
||||
static constexpr std::string_view BACKUP_MENU = "BackupMenu";
|
||||
static constexpr std::string_view COPYING_FILES = "CopyingFiles";
|
||||
static constexpr std::string_view BACKUPMENU_CONFIRMATIONS = "BackupMenuConfirmations";
|
||||
static constexpr std::string_view BACKUPMENU_STATUS = "BackupMenuStatus";
|
||||
static constexpr std::string_view BACKUPMENU_POPS = "BackupMenuPops";
|
||||
static constexpr std::string_view DELETING_FILES = "DeletingFiles";
|
||||
static constexpr std::string_view KEYBOARD_STRINGS = "KeyboardStrings";
|
||||
|
||||
@@ -95,6 +95,9 @@
|
||||
"Are you sure you really want to restore #%s#?",
|
||||
"Are you sure you really want to delete #%s#?"
|
||||
],
|
||||
"BackupMenuStatus": [
|
||||
"Processing save data meta file..."
|
||||
],
|
||||
"BackupMenuPops": [
|
||||
"System save data cannot be restored to the system!"
|
||||
],
|
||||
@@ -142,7 +145,7 @@
|
||||
"Deleting all backups for #%s#.",
|
||||
"Resetting save data for #%s#.",
|
||||
"Deleting #%s#'s save data for #%s#...",
|
||||
"Extending #%s#'s save data for #%s#..."
|
||||
"Extending `%s`'s save data for #%s#..."
|
||||
],
|
||||
"TitleOptionPops": [
|
||||
"All backups deleted for `%s`!",
|
||||
|
||||
@@ -185,6 +185,8 @@ void BackupMenuState::update(void)
|
||||
}
|
||||
|
||||
std::shared_ptr<TargetStruct> dataStruct(new TargetStruct);
|
||||
dataStruct->m_user = m_user;
|
||||
dataStruct->m_titleInfo = m_titleInfo;
|
||||
dataStruct->m_targetPath = m_directoryPath / m_directoryListing[selected];
|
||||
dataStruct->m_journalSize = m_titleInfo->get_journal_size(m_saveType);
|
||||
dataStruct->m_spawningState = this;
|
||||
@@ -234,7 +236,6 @@ void BackupMenuState::update(void)
|
||||
|
||||
// Update title scrolling.
|
||||
m_titleScroll.update(hasFocus);
|
||||
|
||||
// Update panel.
|
||||
sm_slidePanel->update(hasFocus);
|
||||
// This state bypasses the Slideout panel's normal behavior because it kind of has to.
|
||||
@@ -447,6 +448,15 @@ static void overwrite_backup(sys::ProgressTask *task, std::shared_ptr<TargetStru
|
||||
|
||||
static void restore_backup(sys::ProgressTask *task, std::shared_ptr<TargetStruct> dataStruct)
|
||||
{
|
||||
// Going to need this later.
|
||||
FsSaveDataInfo *saveInfo = dataStruct->m_user->get_save_info_by_id(dataStruct->m_titleInfo->get_application_id());
|
||||
if (!saveInfo)
|
||||
{
|
||||
// To do: Log this.
|
||||
task->finished();
|
||||
return;
|
||||
}
|
||||
|
||||
// Wipe the save root first. Forgot to commit the changes before. Oops.
|
||||
if (!fslib::delete_directory_recursively(fs::DEFAULT_SAVE_ROOT) ||
|
||||
!fslib::commit_data_to_file_system(fs::DEFAULT_SAVE_MOUNT))
|
||||
@@ -460,6 +470,19 @@ static void restore_backup(sys::ProgressTask *task, std::shared_ptr<TargetStruct
|
||||
|
||||
if (fslib::directory_exists(dataStruct->m_targetPath))
|
||||
{
|
||||
{
|
||||
// Process the save meta if it's there.
|
||||
fs::SaveMetaData meta = {0};
|
||||
fslib::Path saveMetaPath = dataStruct->m_targetPath / fs::NAME_SAVE_META;
|
||||
fslib::File saveMetaFile(saveMetaPath, FsOpenMode_Read);
|
||||
if (saveMetaFile && saveMetaFile.read(&meta, sizeof(fs::SaveMetaData)) == sizeof(fs::SaveMetaData))
|
||||
{
|
||||
// Set this so at least the user knows something is going on. Extending saves can take a decent chunk of time.
|
||||
task->set_status(strings::get_by_name(strings::names::BACKUPMENU_STATUS, 0));
|
||||
fs::process_save_meta_data(saveInfo, meta);
|
||||
}
|
||||
}
|
||||
|
||||
fs::copy_directory(dataStruct->m_targetPath,
|
||||
fs::DEFAULT_SAVE_ROOT,
|
||||
dataStruct->m_journalSize,
|
||||
@@ -477,6 +500,19 @@ static void restore_backup(sys::ProgressTask *task, std::shared_ptr<TargetStruct
|
||||
task->finished();
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
// I'm not sure if this is risky or not. Guess we'll find out...
|
||||
fs::SaveMetaData meta = {0};
|
||||
// The locate_file_in_zip should pinpoint the meta file.
|
||||
if (fs::locate_file_in_zip(targetZip, fs::NAME_SAVE_META) && unzOpenCurrentFile(targetZip) == UNZ_OK &&
|
||||
unzReadCurrentFile(targetZip, &meta, sizeof(fs::SaveMetaData)) == sizeof(fs::SaveMetaData))
|
||||
{
|
||||
task->set_status(strings::get_by_name(strings::names::BACKUPMENU_STATUS, 0));
|
||||
fs::process_save_meta_data(saveInfo, meta);
|
||||
}
|
||||
}
|
||||
|
||||
fs::copy_zip_to_directory(targetZip,
|
||||
fs::DEFAULT_SAVE_ROOT,
|
||||
dataStruct->m_journalSize,
|
||||
|
||||
@@ -435,7 +435,7 @@ static void extend_save_data(sys::Task *task, std::shared_ptr<TargetStruct> data
|
||||
// Get how much to extend.
|
||||
char buffer[5] = {0};
|
||||
// No default. Maybe change this later?
|
||||
if (!keyboard::get_input(SwkbdType_NumPad, {}, keyboardString, buffer, 4))
|
||||
if (!keyboard::get_input(SwkbdType_NumPad, {}, keyboardString, buffer, 5))
|
||||
{
|
||||
task->finished();
|
||||
return;
|
||||
|
||||
@@ -377,9 +377,6 @@ static void load_save_data_info(void)
|
||||
s_titleInfoMap.emplace(applicationID, applicationID);
|
||||
}
|
||||
|
||||
// Grab a reference to the Title info so we don't try to load stats for titles that don't really exist.
|
||||
data::TitleInfo &titleInfo = s_titleInfoMap.at(applicationID);
|
||||
|
||||
// I feel weird allcating space for this even if it's not used, but whatever.
|
||||
PdmPlayStatistics stats = {0};
|
||||
// This should be an OKish way to filter out system titles...
|
||||
|
||||
@@ -1,9 +1,28 @@
|
||||
#include "fs/SaveMetaData.hpp"
|
||||
#include "fs/directory_functions.hpp"
|
||||
#include "fs/save_data_functions.hpp"
|
||||
#include "fs/save_mount.hpp"
|
||||
#include "fslib.hpp"
|
||||
#include "logger.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
/// @brief This is the string template for errors here.
|
||||
constexpr std::string_view STRING_ERROR_TEMPLATE = "Error processing save meta for %016llX: %s";
|
||||
} // namespace
|
||||
|
||||
void fs::create_save_meta_data(data::TitleInfo *titleInfo, const FsSaveDataInfo *saveInfo, fs::SaveMetaData &meta)
|
||||
{
|
||||
// I'm assuming this is opened and good because we're making a backup.
|
||||
int64_t containerSize = 0;
|
||||
if (!fslib::get_device_total_space(fs::DEFAULT_SAVE_ROOT, containerSize))
|
||||
{
|
||||
// Log and fall back to file size count.
|
||||
logger::log("Error getting save container's total size. Defaulting to file size count.");
|
||||
containerSize = fs::get_directory_total_size(fs::DEFAULT_SAVE_ROOT);
|
||||
}
|
||||
|
||||
// Fill out the meta struct.
|
||||
meta = {.m_magic = fs::SAVE_META_MAGIC,
|
||||
.m_applicationID = titleInfo->get_application_id(),
|
||||
.m_saveType = saveInfo->save_data_type,
|
||||
@@ -13,5 +32,53 @@ void fs::create_save_meta_data(data::TitleInfo *titleInfo, const FsSaveDataInfo
|
||||
.m_saveDataSizeMax = titleInfo->get_journal_size_max(saveInfo->save_data_type),
|
||||
.m_journalSize = titleInfo->get_journal_size(saveInfo->save_data_type),
|
||||
.m_journalSizeMax = titleInfo->get_journal_size_max(saveInfo->save_data_type),
|
||||
.m_totalSaveSize = fs::get_directory_total_size(fs::DEFAULT_SAVE_ROOT)};
|
||||
.m_totalSaveSize = containerSize};
|
||||
}
|
||||
|
||||
bool fs::process_save_meta_data(const FsSaveDataInfo *saveInfo, SaveMetaData &meta)
|
||||
{
|
||||
if (meta.m_magic != SAVE_META_MAGIC || saveInfo->application_id != meta.m_applicationID)
|
||||
{
|
||||
logger::log(STRING_ERROR_TEMPLATE.data(), meta.m_applicationID, "Invalid magic or mismatched application ID.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// To do: I'm assuming this function will only be called once the save container is already opened here...
|
||||
int64_t totalSpace = 0;
|
||||
if (!fslib::get_device_total_space(fs::DEFAULT_SAVE_ROOT, totalSpace))
|
||||
{
|
||||
logger::log(STRING_ERROR_TEMPLATE.data(), meta.m_applicationID, "get_device_total_space");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (totalSpace >= meta.m_totalSaveSize)
|
||||
{
|
||||
// Gonna return true here, because there's no need to do anything to the container.
|
||||
return true;
|
||||
}
|
||||
|
||||
// First we need to temporarily close the save.
|
||||
if (!fslib::close_file_system(fs::DEFAULT_SAVE_MOUNT))
|
||||
{
|
||||
logger::log(STRING_ERROR_TEMPLATE.data(), meta.m_applicationID, "close_file_system");
|
||||
// We can't go any further at this point. You can't extend the save while it's open.
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is where we finally extend the container. Using the large of the two journal sizes
|
||||
if (!fs::extend_save_data(saveInfo, meta.m_totalSaveSize, meta.m_journalSizeMax))
|
||||
{
|
||||
logger::log(STRING_ERROR_TEMPLATE.data(), meta.m_applicationID, "Error extending save data container.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// If this fails, we're super screwed.
|
||||
if (!fslib::open_save_data_with_save_info(fs::DEFAULT_SAVE_MOUNT, *saveInfo))
|
||||
{
|
||||
logger::log(STRING_ERROR_TEMPLATE.data(), meta.m_applicationID, "open_save_data");
|
||||
return false;
|
||||
}
|
||||
|
||||
// More later if needed.
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -111,22 +111,39 @@ void fs::copy_file(const fslib::Path &source,
|
||||
journalCount = 0;
|
||||
// Close destination file, commit.
|
||||
destinationFile.close();
|
||||
fslib::commit_data_to_file_system(commitDevice);
|
||||
|
||||
// Need to try to commit before going over the journaling space limit.
|
||||
if (!fslib::commit_data_to_file_system(commitDevice))
|
||||
{
|
||||
logger::log("First, %s", fslib::get_error_string());
|
||||
// I guess break the loop here?
|
||||
break;
|
||||
}
|
||||
|
||||
// Reopen and seek to previous position since we created it with a size earlier.
|
||||
destinationFile.open(destination, FsOpenMode_Write);
|
||||
destinationFile.seek(writeCount, destinationFile.BEGINNING);
|
||||
destinationFile.open(destination, FsOpenMode_Append);
|
||||
}
|
||||
|
||||
// Write to destination
|
||||
destinationFile.write(localBuffer.get(), readCount);
|
||||
|
||||
// Update write and journal count.
|
||||
writeCount += readCount;
|
||||
journalCount += readCount;
|
||||
|
||||
// Update task if passed.
|
||||
if (task)
|
||||
{
|
||||
task->update_current(static_cast<double>(writeCount));
|
||||
}
|
||||
}
|
||||
|
||||
// One last commit for good luck.
|
||||
if (!fslib::commit_data_to_file_system(commitDevice))
|
||||
{
|
||||
logger::log("Second, %s", fslib::get_error_string());
|
||||
}
|
||||
|
||||
// Wait for read thread and free it.
|
||||
readThread.join();
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ bool fs::create_save_data_for(data::User *targetUser, data::TitleInfo *titleInfo
|
||||
.save_data_rank = FsSaveDataRank_Primary,
|
||||
.save_data_index = 0};
|
||||
|
||||
// For just creating it, we're using the safe baseline values.
|
||||
FsSaveDataCreationInfo saveCreation = {
|
||||
.save_data_size = titleInfo->get_save_data_size(targetUser->get_account_save_type()),
|
||||
.journal_size = titleInfo->get_journal_size(targetUser->get_account_save_type()),
|
||||
|
||||
@@ -40,19 +40,16 @@ void ui::TextScroll::create(std::string_view text,
|
||||
// Set the X coordinate to 8 and make sure this knows it needs to scroll.
|
||||
m_x = 8;
|
||||
m_textScrolling = true;
|
||||
logger::log("Scrolling needed?");
|
||||
}
|
||||
else if (center)
|
||||
{
|
||||
// Just center it.
|
||||
m_x = (availableWidth / 2) - (m_textWidth / 2);
|
||||
logger::log("Centered.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just set this to 8. To do: Figure out how to make this cleaner.
|
||||
m_x = 8;
|
||||
logger::log("Aligned.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +58,6 @@ void ui::TextScroll::update(bool hasFocus)
|
||||
// I don't think needs to care about having focus.
|
||||
if (m_textScrolling && m_scrollTimer.is_triggered())
|
||||
{
|
||||
logger::log("Text scroll triggered?");
|
||||
m_x -= 2;
|
||||
m_textScrollTriggered = true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user