disable hdd->hdd threading. only open log write on write to allow for reads. log fw/ams version.

hdd->hdd threading is disabled due to a bug in libusbhsfs which causes a deadlock if 2 fs calls happen at the same time.
This commit is contained in:
ITotalJustice
2025-06-17 00:57:46 +01:00
parent 6b099de63c
commit 99c1db3655
11 changed files with 133 additions and 79 deletions

View File

@@ -198,7 +198,7 @@ FetchContent_Declare(zstd
FetchContent_Declare(libusbhsfs FetchContent_Declare(libusbhsfs
GIT_REPOSITORY https://github.com/ITotalJustice/libusbhsfs.git GIT_REPOSITORY https://github.com/ITotalJustice/libusbhsfs.git
GIT_TAG db2bf2a GIT_TAG d0a973e
) )
FetchContent_Declare(libnxtc FetchContent_Declare(libnxtc

View File

@@ -12,10 +12,14 @@ namespace fs {
struct FsPath { struct FsPath {
FsPath() = default; FsPath() = default;
constexpr FsPath(const auto& str) { From(str); }
constexpr FsPath(const FsPath& p) { From(p); }
constexpr FsPath(const char* str) { From(str); }
constexpr FsPath(const std::string& str) { From(str); }
constexpr FsPath(const std::string_view& str) { From(str); }
constexpr void From(const FsPath& p) { constexpr void From(const FsPath& p) {
*this = p; From(p.s);
} }
constexpr void From(const char* str) { constexpr void From(const char* str) {
@@ -40,6 +44,10 @@ struct FsPath {
return s; return s;
} }
constexpr auto starts_with(std::string_view str) const -> bool {
return !strncasecmp(s, str.data(), str.length());
}
constexpr auto empty() const { constexpr auto empty() const {
return s[0] == '\0'; return s[0] == '\0';
} }
@@ -65,6 +73,11 @@ struct FsPath {
constexpr char& operator[](std::size_t idx) { return s[idx]; } constexpr char& operator[](std::size_t idx) { return s[idx]; }
constexpr const char& operator[](std::size_t idx) const { return s[idx]; } constexpr const char& operator[](std::size_t idx) const { return s[idx]; }
constexpr FsPath& operator=(const FsPath& p) noexcept {
From(p.s);
return *this;
}
constexpr FsPath operator+(const FsPath& v) const noexcept { constexpr FsPath operator+(const FsPath& v) const noexcept {
FsPath r{*this}; FsPath r{*this};
return r += v; return r += v;
@@ -186,9 +199,6 @@ struct File {
FsFile m_native{}; FsFile m_native{};
std::FILE* m_stdio{}; std::FILE* m_stdio{};
s64 m_stdio_off{}; s64 m_stdio_off{};
// sadly, fatfs doesn't support fstat, so we have to manually
// stat the file to get it's size.
FsPath m_path{};
}; };
struct Dir { struct Dir {

View File

@@ -12,6 +12,8 @@ extern "C" {
bool log_file_init(); bool log_file_init();
bool log_nxlink_init(); bool log_nxlink_init();
void log_file_exit(); void log_file_exit();
bool log_is_init();
void log_nxlink_exit(); void log_nxlink_exit();
void log_write(const char* s, ...) __attribute__ ((format (printf, 1, 2))); void log_write(const char* s, ...) __attribute__ ((format (printf, 1, 2)));
void log_write_arg(const char* s, va_list* v); void log_write_arg(const char* s, va_list* v);

View File

@@ -40,17 +40,17 @@ Result TransferPull(ui::ProgressBox* pbox, s64 size, ReadCallback rfunc, StartCa
// helper for extract zips. // helper for extract zips.
// this will multi-thread unzip if size >= 512KiB, otherwise it'll single pass. // this will multi-thread unzip if size >= 512KiB, otherwise it'll single pass.
Result TransferUnzip(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs::FsPath& path, s64 size, u32 crc32 = 0); Result TransferUnzip(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs::FsPath& path, s64 size, u32 crc32 = 0, Mode mode = Mode::SingleThreadedIfSmaller);
// same as above but for zipping files. // same as above but for zipping files.
Result TransferZip(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs::FsPath& path, u32* crc32 = nullptr); Result TransferZip(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs::FsPath& path, u32* crc32 = nullptr, Mode mode = Mode::SingleThreadedIfSmaller);
// passes the name inside the zip an final output path. // passes the name inside the zip an final output path.
using UnzipAllFilter = std::function<bool(const fs::FsPath& name, fs::FsPath& path)>; using UnzipAllFilter = std::function<bool(const fs::FsPath& name, fs::FsPath& path)>;
// helper all-in-one unzip function that unzips a zip (either open or path provided). // helper all-in-one unzip function that unzips a zip (either open or path provided).
// the filter function can be used to modify the path and filter out unwanted files. // the filter function can be used to modify the path and filter out unwanted files.
Result TransferUnzipAll(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs::FsPath& base_path, UnzipAllFilter filter = nullptr); Result TransferUnzipAll(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs::FsPath& base_path, UnzipAllFilter filter = nullptr, Mode mode = Mode::SingleThreadedIfSmaller);
Result TransferUnzipAll(ui::ProgressBox* pbox, const fs::FsPath& zip_out, fs::Fs* fs, const fs::FsPath& base_path, UnzipAllFilter filter = nullptr); Result TransferUnzipAll(ui::ProgressBox* pbox, const fs::FsPath& zip_out, fs::Fs* fs, const fs::FsPath& base_path, UnzipAllFilter filter = nullptr, Mode mode = Mode::SingleThreadedIfSmaller);
} // namespace sphaira::thread } // namespace sphaira::thread

View File

@@ -39,9 +39,9 @@ struct ProgressBox final : Widget {
auto ShouldExitResult() -> Result; auto ShouldExitResult() -> Result;
// helper functions // helper functions
auto CopyFile(fs::Fs* fs_src, fs::Fs* fs_dst, const fs::FsPath& src, const fs::FsPath& dst) -> Result; auto CopyFile(fs::Fs* fs_src, fs::Fs* fs_dst, const fs::FsPath& src, const fs::FsPath& dst, bool single_threaded = false) -> Result;
auto CopyFile(fs::Fs* fs, const fs::FsPath& src, const fs::FsPath& dst) -> Result; auto CopyFile(fs::Fs* fs, const fs::FsPath& src, const fs::FsPath& dst, bool single_threaded = false) -> Result;
auto CopyFile(const fs::FsPath& src, const fs::FsPath& dst) -> Result; auto CopyFile(const fs::FsPath& src, const fs::FsPath& dst, bool single_threaded = false) -> Result;
void Yield(); void Yield();
auto GetCpuId() const { auto GetCpuId() const {

View File

@@ -1292,21 +1292,6 @@ App::App(const char* argv0) {
__nx_applet_exit_mode = 1; __nx_applet_exit_mode = 1;
} }
// get emummc config.
alignas(0x1000) AmsEmummcPaths paths{};
SecmonArgs args{};
args.X[0] = 0xF0000404; /* smcAmsGetEmunandConfig */
args.X[1] = 0; /* EXO_EMUMMC_MMC_NAND*/
args.X[2] = (u64)&paths; /* out path */
svcCallSecureMonitor(&args);
m_emummc_paths = paths;
log_write("emummc : %u\n", App::IsEmummc());
if (App::IsEmummc()) {
log_write("[emummc] file based path: %s\n", m_emummc_paths.file_based_path);
log_write("[emummc] nintendo path: %s\n", m_emummc_paths.nintendo);
}
fs::FsNativeSd fs; fs::FsNativeSd fs;
fs.CreateDirectoryRecursively("/config/sphaira"); fs.CreateDirectoryRecursively("/config/sphaira");
fs.CreateDirectory("/config/sphaira/assoc"); fs.CreateDirectory("/config/sphaira/assoc");
@@ -1370,6 +1355,48 @@ App::App(const char* argv0) {
App::Notify("Warning! Logs are enabled, Sphaira will run slowly!"_i18n); App::Notify("Warning! Logs are enabled, Sphaira will run slowly!"_i18n);
} }
if (log_is_init()) {
SetSysFirmwareVersion fw_version{};
setsysInitialize();
ON_SCOPE_EXIT(setsysExit());
setsysGetFirmwareVersion(&fw_version);
log_write("[version] platform: %s\n", fw_version.platform);
log_write("[version] version_hash: %s\n", fw_version.version_hash);
log_write("[version] display_version: %s\n", fw_version.display_version);
log_write("[version] display_title: %s\n", fw_version.display_title);
splInitialize();
ON_SCOPE_EXIT(splExit());
u64 out{};
splGetConfig((SplConfigItem)65000, &out);
log_write("[ams] version: %lu.%lu.%lu\n", (out >> 56) & 0xFF, (out >> 48) & 0xFF, (out >> 40) & 0xFF);
log_write("[ams] target version: %lu.%lu.%lu\n", (out >> 24) & 0xFF, (out >> 16) & 0xFF, (out >> 8) & 0xFF);
log_write("[ams] key gen: %lu\n", (out >> 32) & 0xFF);
splGetConfig((SplConfigItem)65003, &out);
log_write("[ams] hash: %lx\n", out);
splGetConfig((SplConfigItem)65010, &out);
log_write("[ams] usb 3.0 enabled: %lu\n", out);
}
// get emummc config.
alignas(0x1000) AmsEmummcPaths paths{};
SecmonArgs args{};
args.X[0] = 0xF0000404; /* smcAmsGetEmunandConfig */
args.X[1] = 0; /* EXO_EMUMMC_MMC_NAND*/
args.X[2] = (u64)&paths; /* out path */
svcCallSecureMonitor(&args);
m_emummc_paths = paths;
log_write("[emummc] enabled: %u\n", App::IsEmummc());
if (App::IsEmummc()) {
log_write("[emummc] file based path: %s\n", m_emummc_paths.file_based_path);
log_write("[emummc] nintendo path: %s\n", m_emummc_paths.nintendo);
}
if (App::GetMtpEnable()) { if (App::GetMtpEnable()) {
haze::Init(); haze::Init();
} }

View File

@@ -500,7 +500,6 @@ Result OpenFile(fs::Fs* fs, const fs::FsPath& path, u32 mode, File* f) {
} }
R_UNLESS(f->m_stdio, Result_FsUnknownStdioError); R_UNLESS(f->m_stdio, Result_FsUnknownStdioError);
std::strcpy(f->m_path, path);
} }
R_SUCCEED(); R_SUCCEED();
@@ -580,17 +579,7 @@ Result File::GetSize(s64* out) {
R_TRY(fsFileGetSize(&m_native, out)); R_TRY(fsFileGetSize(&m_native, out));
} else { } else {
struct stat st; struct stat st;
const auto fd = fileno(m_stdio); R_UNLESS(!fstat(fileno(m_stdio), &st), Result_FsUnknownStdioError);
bool did_stat{};
if (fd && !fstat(fd, &st)) {
did_stat = true;
}
if (!did_stat) {
R_UNLESS(!lstat(m_path, &st), Result_FsUnknownStdioError);
}
*out = st.st_size; *out = st.st_size;
} }

View File

@@ -10,17 +10,27 @@ namespace {
constexpr const char* logpath = "/config/sphaira/log.txt"; constexpr const char* logpath = "/config/sphaira/log.txt";
std::FILE* file{};
int nxlink_socket{}; int nxlink_socket{};
bool g_file_open{};
std::mutex mutex{}; std::mutex mutex{};
void log_write_arg_internal(const char* s, std::va_list* v) { void log_write_arg_internal(const char* s, std::va_list* v) {
const auto t = std::time(nullptr);
const auto tm = std::localtime(&t);
static char buf[512];
const auto len = std::snprintf(buf, sizeof(buf), "[%02u:%02u:%02u] -> ", tm->tm_hour, tm->tm_min, tm->tm_sec);
std::vsnprintf(buf + len, sizeof(buf) - len, s, *v);
if (g_file_open) {
auto file = std::fopen(logpath, "a");
if (file) { if (file) {
std::vfprintf(file, s, *v); std::fprintf(file, "%s", buf);
std::fflush(file); std::fclose(file);
}
} }
if (nxlink_socket) { if (nxlink_socket) {
std::vprintf(s, *v); std::printf("%s", buf);
} }
} }
@@ -30,12 +40,18 @@ extern "C" {
auto log_file_init() -> bool { auto log_file_init() -> bool {
std::scoped_lock lock{mutex}; std::scoped_lock lock{mutex};
if (file) { if (g_file_open) {
return false; return false;
} }
file = std::fopen(logpath, "w"); auto file = std::fopen(logpath, "w");
return file != nullptr; if (file) {
g_file_open = true;
std::fclose(file);
return true;
}
return false;
} }
auto log_nxlink_init() -> bool { auto log_nxlink_init() -> bool {
@@ -50,9 +66,8 @@ auto log_nxlink_init() -> bool {
void log_file_exit() { void log_file_exit() {
std::scoped_lock lock{mutex}; std::scoped_lock lock{mutex};
if (file) { if (g_file_open) {
std::fclose(file); g_file_open = false;
file = nullptr;
} }
} }
@@ -64,12 +79,17 @@ void log_nxlink_exit() {
} }
} }
void log_write(const char* s, ...) { bool log_is_init() {
std::scoped_lock lock{mutex}; std::scoped_lock lock{mutex};
if (!file && !nxlink_socket) { return g_file_open || nxlink_socket;
}
void log_write(const char* s, ...) {
if (!log_is_init()) {
return; return;
} }
std::scoped_lock lock{mutex};
std::va_list v{}; std::va_list v{};
va_start(v, s); va_start(v, s);
log_write_arg_internal(s, &v); log_write_arg_internal(s, &v);
@@ -77,11 +97,11 @@ void log_write(const char* s, ...) {
} }
void log_write_arg(const char* s, va_list* v) { void log_write_arg(const char* s, va_list* v) {
std::scoped_lock lock{mutex}; if (!log_is_init()) {
if (!file && !nxlink_socket) {
return; return;
} }
std::scoped_lock lock{mutex};
log_write_arg_internal(s, v); log_write_arg_internal(s, v);
} }

View File

@@ -416,21 +416,21 @@ Result TransferInternal(ui::ProgressBox* pbox, s64 size, ReadCallback rfunc, Wri
} // namespace } // namespace
Result Transfer(ui::ProgressBox* pbox, s64 size, ReadCallback rfunc, WriteCallback wfunc, Mode mode) { Result Transfer(ui::ProgressBox* pbox, s64 size, ReadCallback rfunc, WriteCallback wfunc, Mode mode) {
return TransferInternal(pbox, size, rfunc, wfunc, nullptr, Mode::MultiThreaded); return TransferInternal(pbox, size, rfunc, wfunc, nullptr, mode);
} }
Result TransferPull(ui::ProgressBox* pbox, s64 size, ReadCallback rfunc, StartCallback sfunc, Mode mode) { Result TransferPull(ui::ProgressBox* pbox, s64 size, ReadCallback rfunc, StartCallback sfunc, Mode mode) {
return TransferInternal(pbox, size, rfunc, nullptr, [sfunc](StartThreadCallback start, PullCallback pull) -> Result { return TransferInternal(pbox, size, rfunc, nullptr, [sfunc](StartThreadCallback start, PullCallback pull) -> Result {
R_TRY(start()); R_TRY(start());
return sfunc(pull); return sfunc(pull);
}, Mode::MultiThreaded); }, mode);
} }
Result TransferPull(ui::ProgressBox* pbox, s64 size, ReadCallback rfunc, StartCallback2 sfunc, Mode mode) { Result TransferPull(ui::ProgressBox* pbox, s64 size, ReadCallback rfunc, StartCallback2 sfunc, Mode mode) {
return TransferInternal(pbox, size, rfunc, nullptr, sfunc, Mode::MultiThreaded); return TransferInternal(pbox, size, rfunc, nullptr, sfunc, mode);
} }
Result TransferUnzip(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs::FsPath& path, s64 size, u32 crc32) { Result TransferUnzip(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs::FsPath& path, s64 size, u32 crc32, Mode mode) {
Result rc; Result rc;
if (R_FAILED(rc = fs->CreateDirectoryRecursivelyWithPath(path)) && rc != FsError_PathAlreadyExists) { if (R_FAILED(rc = fs->CreateDirectoryRecursivelyWithPath(path)) && rc != FsError_PathAlreadyExists) {
log_write("failed to create folder: %s 0x%04X\n", path.s, rc); log_write("failed to create folder: %s 0x%04X\n", path.s, rc);
@@ -471,7 +471,7 @@ Result TransferUnzip(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs::F
[&](const void* data, s64 off, s64 size) -> Result { [&](const void* data, s64 off, s64 size) -> Result {
return f.Write(off, data, size, FsWriteOption_None); return f.Write(off, data, size, FsWriteOption_None);
}, },
nullptr, Mode::SingleThreadedIfSmaller, SMALL_BUFFER_SIZE nullptr, mode, SMALL_BUFFER_SIZE
)); ));
// validate crc32 (if set in the info). // validate crc32 (if set in the info).
@@ -480,7 +480,7 @@ Result TransferUnzip(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs::F
R_SUCCEED(); R_SUCCEED();
} }
Result TransferZip(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs::FsPath& path, u32* crc32) { Result TransferZip(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs::FsPath& path, u32* crc32, Mode mode) {
fs::File f; fs::File f;
R_TRY(fs->OpenFile(path, FsOpenMode_Read, &f)); R_TRY(fs->OpenFile(path, FsOpenMode_Read, &f));
@@ -506,11 +506,11 @@ Result TransferZip(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs::FsP
} }
R_SUCCEED(); R_SUCCEED();
}, },
nullptr, Mode::SingleThreadedIfSmaller, SMALL_BUFFER_SIZE nullptr, mode, SMALL_BUFFER_SIZE
); );
} }
Result TransferUnzipAll(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs::FsPath& base_path, UnzipAllFilter filter) { Result TransferUnzipAll(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs::FsPath& base_path, UnzipAllFilter filter, Mode mode) {
unz_global_info64 ginfo; unz_global_info64 ginfo;
if (UNZ_OK != unzGetGlobalInfo64(zfile, &ginfo)) { if (UNZ_OK != unzGetGlobalInfo64(zfile, &ginfo)) {
R_THROW(Result_UnzGetGlobalInfo64); R_THROW(Result_UnzGetGlobalInfo64);
@@ -560,14 +560,14 @@ Result TransferUnzipAll(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs
R_THROW(rc); R_THROW(rc);
} }
} else { } else {
R_TRY(TransferUnzip(pbox, zfile, fs, path, info.uncompressed_size, info.crc)); R_TRY(TransferUnzip(pbox, zfile, fs, path, info.uncompressed_size, info.crc, mode));
} }
} }
R_SUCCEED(); R_SUCCEED();
} }
Result TransferUnzipAll(ui::ProgressBox* pbox, const fs::FsPath& zip_out, fs::Fs* fs, const fs::FsPath& base_path, UnzipAllFilter filter) { Result TransferUnzipAll(ui::ProgressBox* pbox, const fs::FsPath& zip_out, fs::Fs* fs, const fs::FsPath& base_path, UnzipAllFilter filter, Mode mode) {
zlib_filefunc64_def file_func; zlib_filefunc64_def file_func;
mz::FileFuncStdio(&file_func); mz::FileFuncStdio(&file_func);
@@ -575,7 +575,7 @@ Result TransferUnzipAll(ui::ProgressBox* pbox, const fs::FsPath& zip_out, fs::Fs
R_UNLESS(zfile, Result_UnzOpen2_64); R_UNLESS(zfile, Result_UnzOpen2_64);
ON_SCOPE_EXIT(unzClose(zfile)); ON_SCOPE_EXIT(unzClose(zfile));
return TransferUnzipAll(pbox, zfile, fs, base_path, filter); return TransferUnzipAll(pbox, zfile, fs, base_path, filter, mode);
} }
} // namespace::thread } // namespace::thread

View File

@@ -442,6 +442,8 @@ FsView::~FsView() {
} }
void FsView::Update(Controller* controller, TouchInfo* touch) { void FsView::Update(Controller* controller, TouchInfo* touch) {
Widget::Update(controller, touch);
m_list->OnUpdate(controller, touch, m_index, m_entries_current.size(), [this](bool touch, auto i) { m_list->OnUpdate(controller, touch, m_index, m_entries_current.size(), [this](bool touch, auto i) {
if (touch && m_index == i) { if (touch && m_index == i) {
FireAction(Button::A); FireAction(Button::A);
@@ -728,10 +730,12 @@ void FsView::UnzipFiles(fs::FsPath dir_path) {
} }
App::Push(std::make_shared<ui::ProgressBox>(0, "Extracting "_i18n, "", [this, dir_path, targets](auto pbox) -> Result { App::Push(std::make_shared<ui::ProgressBox>(0, "Extracting "_i18n, "", [this, dir_path, targets](auto pbox) -> Result {
const auto is_hdd_fs = m_fs->Root().starts_with("ums");
for (auto& e : targets) { for (auto& e : targets) {
pbox->SetTitle(e.GetName()); pbox->SetTitle(e.GetName());
const auto zip_out = GetNewPath(e); const auto zip_out = GetNewPath(e);
R_TRY(thread::TransferUnzipAll(pbox, zip_out, m_fs.get(), dir_path)); R_TRY(thread::TransferUnzipAll(pbox, zip_out, m_fs.get(), dir_path, nullptr, is_hdd_fs ? thread::Mode::SingleThreaded : thread::Mode::SingleThreadedIfSmaller));
} }
R_SUCCEED(); R_SUCCEED();
@@ -786,6 +790,7 @@ void FsView::ZipFiles(fs::FsPath zip_out) {
App::Push(std::make_shared<ui::ProgressBox>(0, "Compressing "_i18n, "", [this, zip_out, targets](auto pbox) -> Result { App::Push(std::make_shared<ui::ProgressBox>(0, "Compressing "_i18n, "", [this, zip_out, targets](auto pbox) -> Result {
const auto t = std::time(NULL); const auto t = std::time(NULL);
const auto tm = std::localtime(&t); const auto tm = std::localtime(&t);
const auto is_hdd_fs = m_fs->Root().starts_with("ums");
// pre-calculate the time rather than calculate it in the loop. // pre-calculate the time rather than calculate it in the loop.
zip_fileinfo zip_info{}; zip_fileinfo zip_info{};
@@ -825,7 +830,7 @@ void FsView::ZipFiles(fs::FsPath zip_out) {
} }
ON_SCOPE_EXIT(zipCloseFileInZip(zfile)); ON_SCOPE_EXIT(zipCloseFileInZip(zfile));
return thread::TransferZip(pbox, zfile, m_fs.get(), file_path); return thread::TransferZip(pbox, zfile, m_fs.get(), file_path, nullptr, is_hdd_fs ? thread::Mode::SingleThreaded : thread::Mode::SingleThreadedIfSmaller);
}; };
for (auto& e : targets) { for (auto& e : targets) {
@@ -1207,6 +1212,7 @@ void FsView::OnPasteCallback() {
App::Push(std::make_shared<ProgressBox>(0, "Pasting"_i18n, "", [this](auto pbox) -> Result { App::Push(std::make_shared<ProgressBox>(0, "Pasting"_i18n, "", [this](auto pbox) -> Result {
auto& selected = m_menu->m_selected; auto& selected = m_menu->m_selected;
auto src_fs = selected.m_view->GetFs(); auto src_fs = selected.m_view->GetFs();
const auto is_same_fs = selected.SameFs(this);
if (selected.SameFs(this) && selected.m_type == SelectedType::Cut) { if (selected.SameFs(this) && selected.m_type == SelectedType::Cut) {
for (const auto& p : selected.m_files) { for (const auto& p : selected.m_files) {
@@ -1271,7 +1277,7 @@ void FsView::OnPasteCallback() {
} else { } else {
pbox->SetTitle(p.name); pbox->SetTitle(p.name);
pbox->NewTransfer("Copying "_i18n + src_path); pbox->NewTransfer("Copying "_i18n + src_path);
R_TRY(pbox->CopyFile(src_fs, m_fs.get(), src_path, dst_path)); R_TRY(pbox->CopyFile(src_fs, m_fs.get(), src_path, dst_path, is_same_fs));
R_TRY(on_paste_file(src_path, dst_path)); R_TRY(on_paste_file(src_path, dst_path));
} }
} }
@@ -1301,7 +1307,7 @@ void FsView::OnPasteCallback() {
pbox->SetTitle(p.name); pbox->SetTitle(p.name);
pbox->NewTransfer("Copying "_i18n + src_path); pbox->NewTransfer("Copying "_i18n + src_path);
R_TRY(pbox->CopyFile(src_fs, m_fs.get(), src_path, dst_path)); R_TRY(pbox->CopyFile(src_fs, m_fs.get(), src_path, dst_path, is_same_fs));
R_TRY(on_paste_file(src_path, dst_path)); R_TRY(on_paste_file(src_path, dst_path));
} }
} }
@@ -1880,9 +1886,9 @@ void Menu::Update(Controller* controller, TouchInfo* touch) {
// workaround the buttons not being display properly. // workaround the buttons not being display properly.
// basically, inherit all actions from the view, draw them, // basically, inherit all actions from the view, draw them,
// then restore state after. // then restore state after.
const auto actions_copy = GetActions(); // const auto actions_copy = GetActions();
ON_SCOPE_EXIT(m_actions = actions_copy); // ON_SCOPE_EXIT(m_actions = actions_copy);
m_actions.insert_range(view->GetActions()); // m_actions.insert_range(view->GetActions());
MenuBase::Update(controller, touch); MenuBase::Update(controller, touch);
view->Update(controller, touch); view->Update(controller, touch);

View File

@@ -263,7 +263,7 @@ auto ProgressBox::ShouldExitResult() -> Result {
R_SUCCEED(); R_SUCCEED();
} }
auto ProgressBox::CopyFile(fs::Fs* fs_src, fs::Fs* fs_dst, const fs::FsPath& src_path, const fs::FsPath& dst_path) -> Result { auto ProgressBox::CopyFile(fs::Fs* fs_src, fs::Fs* fs_dst, const fs::FsPath& src_path, const fs::FsPath& dst_path, bool single_threaded) -> Result {
const auto is_file_based_emummc = App::IsFileBaseEmummc(); const auto is_file_based_emummc = App::IsFileBaseEmummc();
const auto is_both_native = fs_src->IsNative() && fs_dst->IsNative(); const auto is_both_native = fs_src->IsNative() && fs_dst->IsNative();
@@ -300,20 +300,20 @@ auto ProgressBox::CopyFile(fs::Fs* fs_src, fs::Fs* fs_dst, const fs::FsPath& src
} }
return rc; return rc;
} }, single_threaded ? thread::Mode::SingleThreaded : thread::Mode::MultiThreaded
)); ));
R_SUCCEED(); R_SUCCEED();
} }
auto ProgressBox::CopyFile(fs::Fs* fs, const fs::FsPath& src_path, const fs::FsPath& dst_path) -> Result { auto ProgressBox::CopyFile(fs::Fs* fs, const fs::FsPath& src_path, const fs::FsPath& dst_path, bool single_threaded) -> Result {
return CopyFile(fs, fs, src_path, dst_path); return CopyFile(fs, fs, src_path, dst_path, single_threaded);
} }
auto ProgressBox::CopyFile(const fs::FsPath& src_path, const fs::FsPath& dst_path) -> Result { auto ProgressBox::CopyFile(const fs::FsPath& src_path, const fs::FsPath& dst_path, bool single_threaded) -> Result {
fs::FsNativeSd fs; fs::FsNativeSd fs;
R_TRY(fs.GetFsOpenResult()); R_TRY(fs.GetFsOpenResult());
return CopyFile(&fs, src_path, dst_path); return CopyFile(&fs, src_path, dst_path, single_threaded);
} }
void ProgressBox::Yield() { void ProgressBox::Yield() {