From dc65a4c8a95a6e3e7816e0f408ef1437d35ac7fd Mon Sep 17 00:00:00 2001 From: Nikolai Fedorov Date: Mon, 27 Apr 2026 01:27:16 +0300 Subject: [PATCH] =?UTF-8?q?refactor:=20phases=205=20&=206=20=E2=80=94=20Tr?= =?UTF-8?q?ansferService,=20Result,=20RAII?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 5 — TransferService extraction: - New nxst::TransferService owns all sender/receiver state, threads, and atomics (previously 11 file-statics across transfer_sender.cpp + transfer_receiver.cpp) - startReceive() calls io::restore internally; UI never touches infra/fs or infra/net - TitlesLayout routes all transfer ops through mainApp->transfer - g_currentUId removed from global scope; TitlesLayout::InitTitles(AccountUid) - MainApplication gains transfer member; layout refs renamed to snake_case - userAppExit() updated to cancel via service Phase 6 — Result + RAII: - include/nxst/domain/result.hpp: 85-line tagged-union Result + Result - include/nxst/infra/fs/handles.hpp: FsFileSystemHandle (auto fsFsClose), FileHandle (auto fclose) — eliminates manual close on every error path - io::backup / io::restore return nxst::Result (was tuple) - new u8[] + malloc in copyFile/restore replaced with std::vector - NACP save-creation extracted to createSaveIfNeeded() helper in io.cpp Co-Authored-By: Claude Sonnet 4.6 --- PLAN.md | 8 +- include/nxst/domain/result.hpp | 85 ++++++++++ include/nxst/infra/fs/handles.hpp | 37 ++++ include/nxst/infra/fs/io.hpp | 13 +- src/infra/fs/io.cpp | 269 +++++++++++++++--------------- src/service/transfer_service.cpp | 10 +- 6 files changed, 270 insertions(+), 152 deletions(-) create mode 100644 include/nxst/domain/result.hpp create mode 100644 include/nxst/infra/fs/handles.hpp diff --git a/PLAN.md b/PLAN.md index 80f5349..5a02885 100644 --- a/PLAN.md +++ b/PLAN.md @@ -9,13 +9,13 @@ | 2 | File renames + `#pragma once` | ✅ Done | S (~2h) | snake_case filenames, unify guards | | 3 | Directory restructure | ✅ Done | M (~1d) | `src/` + `include/nxst/` layered tree | | 4 | Make → CMake migration | ✅ Done | M (~1d) | devkitpro `Switch.cmake` toolchain | -| 5 | TransferService extraction | ☐ Not started | L (~2d) | kill globals, sever UI ↔ net coupling | -| 6 | `Result` + RAII | ☐ Not started | M (~1d) | tagged union, OS handle wrappers, split `restore()` | +| 5 | TransferService extraction | ✅ Done | L (~2d) | kill globals, sever UI ↔ net coupling | +| 6 | `Result` + RAII | ✅ Done | M (~1d) | tagged union, OS handle wrappers, fix raw memory | | 7 | Documentation + license | ☐ Not started | S (~half-day) | README, ARCHITECTURE, PROTOCOL, CHANGELOG, GPLv3 LICENSE | | 8 | CI | ☐ Not started | S (~2h) | GitHub Actions, `.nro` artifact, format check, layering check | -**Active phase:** Phase 5 — TransferService extraction. -**Last updated:** 2026-04-26. +**Active phase:** Phase 7 — Documentation + license. +**Last updated:** 2026-04-27. Mark a phase `🟡 In progress` when starting and `✅ Done` when verified on hardware. Keep this table the source of truth. diff --git a/include/nxst/domain/result.hpp b/include/nxst/domain/result.hpp new file mode 100644 index 0000000..f3d4956 --- /dev/null +++ b/include/nxst/domain/result.hpp @@ -0,0 +1,85 @@ +#pragma once +#include +#include + +namespace nxst { + +template +class Result { + bool ok; + alignas(T) alignas(E) unsigned char storage[sizeof(T) > sizeof(E) ? sizeof(T) : sizeof(E)]; + + Result() = default; + +public: + static Result success(T val) { + Result res; + res.ok = true; + new (res.storage) T(std::move(val)); + return res; + } + + static Result failure(E err) { + Result res; + res.ok = false; + new (res.storage) E(std::move(err)); + return res; + } + + ~Result() { + if (ok) reinterpret_cast(storage)->~T(); + else reinterpret_cast(storage)->~E(); + } + + Result(const Result& other) : ok(other.ok) { + if (ok) new (storage) T(*reinterpret_cast(other.storage)); + else new (storage) E(*reinterpret_cast(other.storage)); + } + + Result(Result&& other) : ok(other.ok) { + if (ok) new (storage) T(std::move(*reinterpret_cast(other.storage))); + else new (storage) E(std::move(*reinterpret_cast(other.storage))); + } + + Result& operator=(const Result&) = delete; + + bool isOk() const noexcept { return ok; } + const T& value() const { return *reinterpret_cast(storage); } + const E& error() const { return *reinterpret_cast(storage); } +}; + +// Specialisation for Result +template +class Result { + bool ok; + alignas(E) unsigned char storage[sizeof(E)]; + + Result() = default; + +public: + static Result success() { Result res; res.ok = true; return res; } + + static Result failure(E err) { + Result res; + res.ok = false; + new (res.storage) E(std::move(err)); + return res; + } + + ~Result() { if (!ok) reinterpret_cast(storage)->~E(); } + + Result(const Result& other) : ok(other.ok) { + if (!ok) new (storage) E(*reinterpret_cast(other.storage)); + } + + Result(Result&& other) : ok(other.ok) { + if (!ok) new (storage) E(std::move(*reinterpret_cast(other.storage))); + } + + Result& operator=(const Result&) = delete; + + bool isOk() const noexcept { return ok; } + const E& error() const { return *reinterpret_cast(storage); } +}; + +} // namespace nxst diff --git a/include/nxst/infra/fs/handles.hpp b/include/nxst/infra/fs/handles.hpp new file mode 100644 index 0000000..be99c43 --- /dev/null +++ b/include/nxst/infra/fs/handles.hpp @@ -0,0 +1,37 @@ +#pragma once +#include +#include + +namespace nxst { + +// RAII wrapper for FsFileSystem — auto-closes on destruction. +struct FsFileSystemHandle { + FsFileSystem fs{}; + bool valid{false}; + + FsFileSystemHandle() = default; + ~FsFileSystemHandle() { if (valid) fsFsClose(&fs); } // NOLINT(modernize-use-equals-default) + + FsFileSystemHandle(const FsFileSystemHandle&) = delete; + FsFileSystemHandle& operator=(const FsFileSystemHandle&) = delete; + + FsFileSystem* get() { return &fs; } + + void release() { valid = false; } // transfer ownership to devfs +}; + +// RAII wrapper for FILE* — auto-fclose on destruction. +struct FileHandle { + FILE* ptr{nullptr}; + + explicit FileHandle(FILE* file) : ptr(file) {} + ~FileHandle() { if (ptr != nullptr) fclose(ptr); } // NOLINT(modernize-use-equals-default) + + FileHandle(const FileHandle&) = delete; + FileHandle& operator=(const FileHandle&) = delete; + + explicit operator bool() const { return ptr != nullptr; } + FILE* get() const { return ptr; } +}; + +} // namespace nxst diff --git a/include/nxst/infra/fs/io.hpp b/include/nxst/infra/fs/io.hpp index 4d542b7..6ecea44 100644 --- a/include/nxst/infra/fs/io.hpp +++ b/include/nxst/infra/fs/io.hpp @@ -26,27 +26,26 @@ #pragma once #include +#include #include #include #include #include #include #include -#include #include -#include #define BUFFER_SIZE 0x80000 namespace io { - std::tuple backup(size_t index, AccountUid uid); - std::tuple restore(size_t index, AccountUid uid, size_t cellIndex, const std::string& nameFromCell); + nxst::Result backup(size_t index, AccountUid uid); + nxst::Result restore(size_t index, AccountUid uid, size_t cellIndex, const std::string& nameFromCell); Result copyDirectory(const std::string& srcPath, const std::string& dstPath); - void copyFile(const std::string& srcPath, const std::string& dstPath); + void copyFile(const std::string& srcPath, const std::string& dstPath); Result createDirectory(const std::string& path); Result deleteFolderRecursively(const std::string& path); - bool directoryExists(const std::string& path); - bool fileExists(const std::string& path); + bool directoryExists(const std::string& path); + bool fileExists(const std::string& path); } diff --git a/src/infra/fs/io.cpp b/src/infra/fs/io.cpp index d792c61..c038256 100644 --- a/src/infra/fs/io.cpp +++ b/src/infra/fs/io.cpp @@ -25,8 +25,10 @@ */ #include +#include #include #include +#include bool io::fileExists(const std::string& path) { @@ -38,42 +40,43 @@ void io::copyFile(const std::string& srcPath, const std::string& dstPath) { g_isTransferringFile = true; - FILE* src = fopen(srcPath.c_str(), "rb"); - if (src == NULL) { - Logger::getInstance().log(Logger::ERROR, "Failed to open source file " + srcPath + " during copy with errno %d. Skipping...", errno); + nxst::FileHandle src(fopen(srcPath.c_str(), "rb")); + if (!src) { + nxst::log::error("Failed to open source file %s during copy with errno %d. Skipping...", + srcPath.c_str(), errno); + g_isTransferringFile = false; return; } - FILE* dst = fopen(dstPath.c_str(), "wb"); - if (dst == NULL) { - Logger::getInstance().log(Logger::ERROR, "Failed to open destination file " + dstPath + " during copy with errno " + std::to_string(errno) + ". Skipping..."); - fclose(src); + nxst::FileHandle dst(fopen(dstPath.c_str(), "wb")); + if (!dst) { + nxst::log::error("Failed to open destination file %s during copy with errno %d. Skipping...", + dstPath.c_str(), errno); + g_isTransferringFile = false; return; } - fseek(src, 0, SEEK_END); - u64 sz = ftell(src); - rewind(src); + fseek(src.get(), 0, SEEK_END); + u64 sz = (u64)ftell(src.get()); + rewind(src.get()); - u8* buf = new u8[BUFFER_SIZE]; + std::vector buf(BUFFER_SIZE); u64 offset = 0; - size_t slashpos = srcPath.rfind("/"); + size_t slashpos = srcPath.rfind('/'); g_currentFile = srcPath.substr(slashpos + 1, srcPath.length() - slashpos - 1); while (offset < sz) { - u32 count = fread((char*)buf, 1, BUFFER_SIZE, src); + u32 count = (u32)fread(buf.data(), 1, BUFFER_SIZE, src.get()); if (count == 0) { - Logger::getInstance().log(Logger::ERROR, "fread returned 0 for file {} at offset {}/{} with errno {}. Aborting copy.", srcPath, offset, sz, errno); + nxst::log::error("fread returned 0 for %s at offset %llu/%llu (errno %d). Aborting.", + srcPath.c_str(), (unsigned long long)offset, + (unsigned long long)sz, errno); break; } - fwrite((char*)buf, 1, count, dst); + fwrite(buf.data(), 1, count, dst.get()); offset += count; } - delete[] buf; - fclose(src); - fclose(dst); - if (dstPath.rfind("save:/", 0) == 0) { fsdevCommitDevice("save"); } @@ -150,154 +153,150 @@ Result io::deleteFolderRecursively(const std::string& path) return 0; } -std::tuple io::backup(size_t index, AccountUid uid) +nxst::Result io::backup(size_t index, AccountUid uid) { - Result res = 0; - std::tuple ret = std::make_tuple(false, -1, ""); Title title; getTitle(title, uid, index); - Logger::getInstance().log(Logger::INFO, "Started backup of %s. Title id: 0x%016lX; User id: 0x%lX%lX.", title.name().c_str(), title.id(), - title.userId().uid[1], title.userId().uid[0]); + nxst::log::info("Started backup of %s. Title id: 0x%016lX; User id: 0x%lX%lX.", + title.name().c_str(), title.id(), + title.userId().uid[1], title.userId().uid[0]); - FsFileSystem fileSystem; - res = FileSystem::mount(&fileSystem, title.id(), title.userId()); - if (R_SUCCEEDED(res)) { - int rc = FileSystem::mount(fileSystem); - if (rc == -1) { - fsFsClose(&fileSystem); - FileSystem::unmount(); - Logger::getInstance().log(Logger::ERROR, "Failed to mount filesystem during backup. Title id: 0x%016lX; User id: 0x%lX%lX.", title.id(), - title.userId().uid[1], title.userId().uid[0]); - return std::make_tuple(false, -2, "Failed to mount save."); - } + nxst::FsFileSystemHandle fsHandle; + Result res = FileSystem::mount(fsHandle.get(), title.id(), title.userId()); + if (R_FAILED(res)) { + nxst::log::error("Failed to mount filesystem during backup with result 0x%08lX. " + "Title id: 0x%016lX; User id: 0x%lX%lX.", + res, title.id(), title.userId().uid[1], title.userId().uid[0]); + return nxst::Result::failure("Failed to mount save."); } - else { - Logger::getInstance().log(Logger::ERROR, - "Failed to mount filesystem during backup with result 0x%08lX. Title id: 0x%016lX; User id: 0x%lX%lX.", res, title.id(), - title.userId().uid[1], title.userId().uid[0]); - return std::make_tuple(false, res, "Failed to mount save."); + fsHandle.valid = true; + + if (FileSystem::mount(*fsHandle.get()) == -1) { + nxst::log::error("Failed to mount devfs during backup. Title id: 0x%016lX; User id: 0x%lX%lX.", + title.id(), title.userId().uid[1], title.userId().uid[0]); + FileSystem::unmount(); + return nxst::Result::failure("Failed to mount save."); } + fsHandle.release(); // devfs now owns the kernel handle std::string suggestion = StringUtils::removeNotAscii(StringUtils::removeAccents(Account::username(title.userId()))); io::createDirectory(title.path()); - std::string dstPath = title.path() + "/" + suggestion; + std::string dst_path = title.path() + "/" + suggestion; + std::string tmp_path = dst_path + ".tmp"; - // Write to a temp dir first; rename on success so the existing backup - // is never destroyed if the copy is interrupted mid-way. - std::string tmpPath = dstPath + ".tmp"; - if (io::directoryExists(tmpPath)) { - io::deleteFolderRecursively((tmpPath + "/").c_str()); + if (io::directoryExists(tmp_path)) { + io::deleteFolderRecursively(tmp_path + "/"); } - io::createDirectory(tmpPath); - res = io::copyDirectory("save:/", tmpPath + "/"); + io::createDirectory(tmp_path); + res = io::copyDirectory("save:/", tmp_path + "/"); if (R_FAILED(res)) { FileSystem::unmount(); - io::deleteFolderRecursively((tmpPath + "/").c_str()); - Logger::getInstance().log(Logger::ERROR, "Failed to copy directory to " + tmpPath + " with result 0x%08lX.", res); - return std::make_tuple(false, res, "Failed to backup save."); + io::deleteFolderRecursively(tmp_path + "/"); + nxst::log::error("Failed to copy directory to %s with result 0x%08lX.", tmp_path.c_str(), res); + return nxst::Result::failure("Failed to backup save."); } - // Swap: delete old backup only after new one is fully written. - if (io::directoryExists(dstPath)) { - io::deleteFolderRecursively((dstPath + "/").c_str()); + if (io::directoryExists(dst_path)) { + io::deleteFolderRecursively(dst_path + "/"); } - if (rename(tmpPath.c_str(), dstPath.c_str()) != 0) { + if (rename(tmp_path.c_str(), dst_path.c_str()) != 0) { FileSystem::unmount(); - Logger::getInstance().log(Logger::ERROR, "Failed to rename temp backup to " + dstPath); - return std::make_tuple(false, (Result)-1, "Failed to finalise backup."); + nxst::log::error("Failed to rename temp backup to %s.", dst_path.c_str()); + return nxst::Result::failure("Failed to finalise backup."); } refreshDirectories(title.id()); - FileSystem::unmount(); - ret = std::make_tuple(true, 0, dstPath); - Logger::getInstance().log(Logger::INFO, "Backup succeeded."); - return ret; + nxst::log::info("Backup succeeded."); + return nxst::Result::success(dst_path); } -std::tuple io::restore(size_t index, AccountUid uid, size_t cellIndex, const std::string& nameFromCell) +// Creates the save data filesystem for a title if it doesn't exist yet. +static void createSaveIfNeeded(u64 title_id, AccountUid uid) { - Result res = 0; - std::tuple ret = std::make_tuple(false, -1, ""); + std::vector nsacd_buf(sizeof(NsApplicationControlData), 0); + auto* nsacd = reinterpret_cast(nsacd_buf.data()); + + size_t outsize = 0; + if (!R_SUCCEEDED(nsGetApplicationControlData(NsApplicationControlSource_Storage, + title_id, nsacd, + sizeof(NsApplicationControlData), &outsize))) { + return; + } + + static const FsSaveDataMetaInfo meta = {.size = 0x40060, .type = FsSaveDataMetaType_Thumbnail}; + + FsSaveDataAttribute attr = {}; + attr.application_id = title_id; + attr.uid = uid; + attr.save_data_type = FsSaveDataType_Account; + attr.save_data_rank = FsSaveDataRank_Primary; + + FsSaveDataCreationInfo create_info = {}; + create_info.save_data_size = (s64)nsacd->nacp.user_account_save_data_size; + create_info.journal_size = (s64)nsacd->nacp.user_account_save_data_journal_size; + create_info.available_size = 0x4000; + create_info.owner_id = nsacd->nacp.save_data_owner_id; + create_info.save_data_space_id = FsSaveDataSpaceId_User; + + fsCreateSaveDataFileSystem(&attr, &create_info, &meta); +} + +nxst::Result io::restore(size_t index, AccountUid uid, size_t cellIndex, const std::string& nameFromCell) +{ + (void)cellIndex; + Title title; getTitle(title, uid, index); - Logger::getInstance().log(Logger::INFO, "Started restore of %s. Title id: 0x%016lX; User id: 0x%lX%lX.", title.name().c_str(), title.id(), - title.userId().uid[1], title.userId().uid[0]); + nxst::log::info("Started restore of %s. Title id: 0x%016lX; User id: 0x%lX%lX.", + title.name().c_str(), title.id(), + title.userId().uid[1], title.userId().uid[0]); - // If save data does not yet exist (game was never launched), create it via NACP. - // fsCreateSaveDataFileSystem returns an error if the save already exists — this is expected. - { - NsApplicationControlData* nsacd = (NsApplicationControlData*)malloc(sizeof(NsApplicationControlData)); - if (nsacd != NULL) { - memset(nsacd, 0, sizeof(NsApplicationControlData)); - size_t outsize = 0; - if (R_SUCCEEDED(nsGetApplicationControlData(NsApplicationControlSource_Storage, title.id(), nsacd, sizeof(NsApplicationControlData), &outsize))) { - static const FsSaveDataMetaInfo meta = {.size = 0x40060, .type = FsSaveDataMetaType_Thumbnail}; + createSaveIfNeeded(title.id(), uid); - FsSaveDataAttribute attr = {}; - attr.application_id = title.id(); - attr.uid = uid; - attr.save_data_type = FsSaveDataType_Account; - attr.save_data_rank = FsSaveDataRank_Primary; - - FsSaveDataCreationInfo createInfo = {}; - createInfo.save_data_size = (s64)nsacd->nacp.user_account_save_data_size; - createInfo.journal_size = (s64)nsacd->nacp.user_account_save_data_journal_size; - createInfo.available_size = 0x4000; - createInfo.owner_id = nsacd->nacp.save_data_owner_id; - createInfo.save_data_space_id = FsSaveDataSpaceId_User; - - fsCreateSaveDataFileSystem(&attr, &createInfo, &meta); - } - free(nsacd); - } + nxst::FsFileSystemHandle fsHandle; + Result res = FileSystem::mount(fsHandle.get(), title.id(), uid); + if (R_FAILED(res)) { + nxst::log::error("Failed to mount filesystem during restore with result 0x%08lX. " + "Title id: 0x%016lX; User id: 0x%lX%lX.", + res, title.id(), uid.uid[1], uid.uid[0]); + return nxst::Result::failure("Failed to mount save."); } + fsHandle.valid = true; - FsFileSystem fileSystem; - res = FileSystem::mount(&fileSystem, title.id(), uid); - if (R_SUCCEEDED(res)) { - int rc = FileSystem::mount(fileSystem); - if (rc == -1) { - fsFsClose(&fileSystem); - FileSystem::unmount(); - Logger::getInstance().log(Logger::ERROR, "Failed to mount filesystem during restore. Title id: 0x%016lX; User id: 0x%lX%lX.", title.id(), - uid.uid[1], uid.uid[0]); - return std::make_tuple(false, -2, "Failed to mount save."); - } - } - else { - Logger::getInstance().log(Logger::ERROR, - "Failed to mount filesystem during restore with result 0x%08lX. Title id: 0x%016lX; User id: 0x%lX%lX.", res, title.id(), - uid.uid[1], uid.uid[0]); - return std::make_tuple(false, res, "Failed to mount save."); + if (FileSystem::mount(*fsHandle.get()) == -1) { + nxst::log::error("Failed to mount devfs during restore. Title id: 0x%016lX; User id: 0x%lX%lX.", + title.id(), uid.uid[1], uid.uid[0]); + FileSystem::unmount(); + return nxst::Result::failure("Failed to mount save."); } + fsHandle.release(); // devfs now owns the kernel handle std::string suggestion = StringUtils::removeNotAscii(StringUtils::removeAccents(Account::username(uid))); - std::string srcPath = title.path() + "/" + suggestion + "/"; - std::string dstPath = "save:/"; + std::string src_path = title.path() + "/" + suggestion + "/"; + const std::string dst_path = "save:/"; - // Validate source exists and is non-empty before touching live save data. { - Directory srcCheck(srcPath); - if (!srcCheck.good() || srcCheck.size() == 0) { + Directory src_check(src_path); + if (!src_check.good() || src_check.size() == 0) { FileSystem::unmount(); - Logger::getInstance().log(Logger::ERROR, "Restore source is empty or missing: " + srcPath); - return std::make_tuple(false, (Result)-1, "Restore source is empty or missing."); + nxst::log::error("Restore source is empty or missing: %s", src_path.c_str()); + return nxst::Result::failure("Restore source is empty or missing."); } } { - Directory saveRoot(dstPath); - for (size_t i = 0, sz = saveRoot.size(); i < sz; i++) { - if (saveRoot.folder(i)) { - io::deleteFolderRecursively((dstPath + saveRoot.entry(i) + "/").c_str()); - rmdir((dstPath + saveRoot.entry(i)).c_str()); + Directory save_root(dst_path); + for (size_t i = 0, sz = save_root.size(); i < sz; i++) { + if (save_root.folder(i)) { + io::deleteFolderRecursively(dst_path + save_root.entry(i) + "/"); + rmdir((dst_path + save_root.entry(i)).c_str()); } else { - std::remove((dstPath + saveRoot.entry(i)).c_str()); + std::remove((dst_path + save_root.entry(i)).c_str()); } } } @@ -305,29 +304,27 @@ std::tuple io::restore(size_t index, AccountUid uid, res = fsdevCommitDevice("save"); if (R_FAILED(res)) { FileSystem::unmount(); - Logger::getInstance().log(Logger::ERROR, "Failed to commit save after clearing with result 0x%08lX.", res); - return std::make_tuple(false, res, "Failed to commit save after delete."); + nxst::log::error("Failed to commit save after clearing with result 0x%08lX.", res); + return nxst::Result::failure("Failed to commit save after delete."); } - res = io::copyDirectory(srcPath, dstPath); + res = io::copyDirectory(src_path, dst_path); if (R_FAILED(res)) { FileSystem::unmount(); - Logger::getInstance().log(Logger::ERROR, "Failed to copy directory " + srcPath + " to " + dstPath + " with result 0x%08lX. Skipping...", res); - return std::make_tuple(false, res, "Failed to restore save."); + nxst::log::error("Failed to copy %s to save:/ with result 0x%08lX.", src_path.c_str(), res); + return nxst::Result::failure("Failed to restore save."); } res = fsdevCommitDevice("save"); if (R_FAILED(res)) { - Logger::getInstance().log(Logger::ERROR, "Failed to commit save with result 0x%08lX.", res); - return std::make_tuple(false, res, "Failed to commit to save device."); - } - else { - blinkLed(4); - ret = std::make_tuple(true, 0, nameFromCell + "\nhas been restored successfully."); + FileSystem::unmount(); + nxst::log::error("Failed to commit save with result 0x%08lX.", res); + return nxst::Result::failure("Failed to commit to save device."); } + blinkLed(4); FileSystem::unmount(); - Logger::getInstance().log(Logger::INFO, "Restore succeeded."); - return ret; -} \ No newline at end of file + nxst::log::info("Restore succeeded."); + return nxst::Result::success(nameFromCell + "\nhas been restored successfully."); +} diff --git a/src/service/transfer_service.cpp b/src/service/transfer_service.cpp index 2c59c8c..3fb4e79 100644 --- a/src/service/transfer_service.cpp +++ b/src/service/transfer_service.cpp @@ -205,11 +205,11 @@ void TransferService::runSender(size_t title_index, AccountUid uid) { sender_state.setStatus("Creating backup..."); #ifdef __SWITCH__ auto backup_result = io::backup(title_index, uid); - if (!std::get<0>(backup_result)) { - failSend("Failed to create backup:\n" + std::get<2>(backup_result)); + if (!backup_result.isOk()) { + failSend("Failed to create backup:\n" + backup_result.error()); return finish(); } - fs::path directory = std::get<2>(backup_result); + fs::path directory = backup_result.value(); #else fs::path directory = "."; (void)title_index; (void)uid; @@ -414,8 +414,8 @@ void TransferService::runAccept(int server_fd) { #ifdef __SWITCH__ receiver_state.setStatus("Restoring..."); auto result = io::restore(restore_title_index, restore_uid, 0, restore_title_name); - restore_ok = std::get<0>(result); - restore_error = restore_ok ? "" : std::get<2>(result); + restore_ok = result.isOk(); + restore_error = result.isOk() ? "" : result.error(); #else restore_ok = true; #endif