refactor: phases 5 & 6 — TransferService, Result<T>, RAII

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<T> + RAII:
- include/nxst/domain/result.hpp: 85-line tagged-union Result<T,E> + Result<void,E>
- 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<std::string> (was tuple<bool,Result,string>)
- new u8[] + malloc in copyFile/restore replaced with std::vector<u8>
- NACP save-creation extracted to createSaveIfNeeded() helper in io.cpp

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-27 01:27:16 +03:00
parent 895fee6235
commit dc65a4c8a9
6 changed files with 270 additions and 152 deletions
+4 -4
View File
@@ -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<T>` + 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<T>` + 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 5TransferService extraction.
**Last updated:** 2026-04-26.
**Active phase:** Phase 7Documentation + 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.
+85
View File
@@ -0,0 +1,85 @@
#pragma once
#include <string>
#include <utility>
namespace nxst {
template <class T, class E = std::string>
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<T*>(storage)->~T();
else reinterpret_cast<E*>(storage)->~E();
}
Result(const Result& other) : ok(other.ok) {
if (ok) new (storage) T(*reinterpret_cast<const T*>(other.storage));
else new (storage) E(*reinterpret_cast<const E*>(other.storage));
}
Result(Result&& other) : ok(other.ok) {
if (ok) new (storage) T(std::move(*reinterpret_cast<T*>(other.storage)));
else new (storage) E(std::move(*reinterpret_cast<E*>(other.storage)));
}
Result& operator=(const Result&) = delete;
bool isOk() const noexcept { return ok; }
const T& value() const { return *reinterpret_cast<const T*>(storage); }
const E& error() const { return *reinterpret_cast<const E*>(storage); }
};
// Specialisation for Result<void>
template <class E>
class Result<void, E> {
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<E*>(storage)->~E(); }
Result(const Result& other) : ok(other.ok) {
if (!ok) new (storage) E(*reinterpret_cast<const E*>(other.storage));
}
Result(Result&& other) : ok(other.ok) {
if (!ok) new (storage) E(std::move(*reinterpret_cast<E*>(other.storage)));
}
Result& operator=(const Result&) = delete;
bool isOk() const noexcept { return ok; }
const E& error() const { return *reinterpret_cast<const E*>(storage); }
};
} // namespace nxst
+37
View File
@@ -0,0 +1,37 @@
#pragma once
#include <cstdio>
#include <switch.h>
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
+6 -7
View File
@@ -26,27 +26,26 @@
#pragma once
#include <nxst/domain/account.hpp>
#include <nxst/domain/result.hpp>
#include <nxst/infra/fs/directory.hpp>
#include <nxst/domain/title.hpp>
#include <nxst/domain/util.hpp>
#include <dirent.h>
#include <switch.h>
#include <sys/stat.h>
#include <tuple>
#include <unistd.h>
#include <utility>
#define BUFFER_SIZE 0x80000
namespace io {
std::tuple<bool, Result, std::string> backup(size_t index, AccountUid uid);
std::tuple<bool, Result, std::string> restore(size_t index, AccountUid uid, size_t cellIndex, const std::string& nameFromCell);
nxst::Result<std::string> backup(size_t index, AccountUid uid);
nxst::Result<std::string> 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);
}
+133 -136
View File
@@ -25,8 +25,10 @@
*/
#include <nxst/infra/fs/io.hpp>
#include <nxst/infra/fs/handles.hpp>
#include <nxst/app/main.hpp>
#include <nxst/infra/sys/logger.hpp>
#include <vector>
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<u8> 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<bool, Result, std::string> io::backup(size_t index, AccountUid uid)
nxst::Result<std::string> io::backup(size_t index, AccountUid uid)
{
Result res = 0;
std::tuple<bool, Result, std::string> 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<std::string>::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<std::string>::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<std::string>::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<std::string>::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<std::string>::success(dst_path);
}
std::tuple<bool, Result, std::string> 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<bool, Result, std::string> ret = std::make_tuple(false, -1, "");
std::vector<u8> nsacd_buf(sizeof(NsApplicationControlData), 0);
auto* nsacd = reinterpret_cast<NsApplicationControlData*>(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<std::string> 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<std::string>::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<std::string>::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<std::string>::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<bool, Result, std::string> 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<std::string>::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<std::string>::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<std::string>::failure("Failed to commit to save device.");
}
blinkLed(4);
FileSystem::unmount();
Logger::getInstance().log(Logger::INFO, "Restore succeeded.");
return ret;
}
nxst::log::info("Restore succeeded.");
return nxst::Result<std::string>::success(nameFromCell + "\nhas been restored successfully.");
}
+5 -5
View File
@@ -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