From 0a2c16db0cdc61636d0075d48c2ccc0aa286e4c2 Mon Sep 17 00:00:00 2001 From: ITotalJustice <47043333+ITotalJustice@users.noreply.github.com> Date: Sat, 20 Sep 2025 20:27:02 +0100 Subject: [PATCH] mtp: bump to 6e24502, fixes freezing if write blocks for too long, simplify stream install for mtp and ftp. see: https://github.com/ITotalJustice/libhaze/issues/1 --- sphaira/CMakeLists.txt | 2 +- sphaira/include/haze_helper.hpp | 4 +- sphaira/source/app.cpp | 8 +- sphaira/source/haze_helper.cpp | 389 +++++++++++------- .../ui/menus/install_stream_menu_base.cpp | 68 +-- sphaira/source/ui/menus/mtp_menu.cpp | 12 +- 6 files changed, 288 insertions(+), 195 deletions(-) diff --git a/sphaira/CMakeLists.txt b/sphaira/CMakeLists.txt index d322087..cf9dedd 100644 --- a/sphaira/CMakeLists.txt +++ b/sphaira/CMakeLists.txt @@ -399,7 +399,7 @@ endif() if (ENABLE_LIBHAZE) FetchContent_Declare(libhaze GIT_REPOSITORY https://github.com/ITotalJustice/libhaze.git - GIT_TAG d60ba60 + GIT_TAG 6e24502 ) FetchContent_MakeAvailable(libhaze) diff --git a/sphaira/include/haze_helper.hpp b/sphaira/include/haze_helper.hpp index 99ac39b..664e6c9 100644 --- a/sphaira/include/haze_helper.hpp +++ b/sphaira/include/haze_helper.hpp @@ -2,7 +2,7 @@ #include -namespace sphaira::haze { +namespace sphaira::libhaze { bool Init(); bool IsInit(); @@ -15,4 +15,4 @@ using OnInstallClose = std::function; void InitInstallMode(const OnInstallStart& on_start, const OnInstallWrite& on_write, const OnInstallClose& on_close); void DisableInstallMode(); -} // namespace sphaira::haze +} // namespace sphaira::libhaze diff --git a/sphaira/source/app.cpp b/sphaira/source/app.cpp index ff3efdb..a9af9fb 100644 --- a/sphaira/source/app.cpp +++ b/sphaira/source/app.cpp @@ -952,9 +952,9 @@ void App::SetMtpEnable(bool enable) { #ifdef ENABLE_LIBHAZE if (enable) { - haze::Init(); + libhaze::Init(); } else { - haze::Exit(); + libhaze::Exit(); } #endif // ENABLE_LIBHAZE } @@ -1632,7 +1632,7 @@ App::App(const char* argv0) { #ifdef ENABLE_LIBHAZE if (App::GetMtpEnable()) { SCOPED_TIMESTAMP("mtp init"); - haze::Init(); + libhaze::Init(); } #endif // ENABLE_LIBHAZE @@ -2336,7 +2336,7 @@ App::~App() { #ifdef ENABLE_LIBHAZE { SCOPED_TIMESTAMP("mtp exit"); - haze::Exit(); + libhaze::Exit(); } #endif // ENABLE_LIBHAZE diff --git a/sphaira/source/haze_helper.cpp b/sphaira/source/haze_helper.cpp index cbbf0b6..72dc82c 100644 --- a/sphaira/source/haze_helper.cpp +++ b/sphaira/source/haze_helper.cpp @@ -9,7 +9,7 @@ #include #include -namespace sphaira::haze { +namespace sphaira::libhaze { namespace { struct InstallSharedData { @@ -56,7 +56,7 @@ void on_thing() { } } -struct FsProxyBase : ::haze::FileSystemProxyImpl { +struct FsProxyBase : haze::FileSystemProxyImpl { FsProxyBase(const char* name, const char* display_name) : m_name{name}, m_display_name{display_name} { } @@ -65,8 +65,13 @@ struct FsProxyBase : ::haze::FileSystemProxyImpl { fs::FsPath buf; const auto len = std::strlen(GetName()); - if (len && !strncasecmp(path + 1, GetName(), len)) { - std::snprintf(buf, sizeof(buf), "%s/%s", base, path + 1 + len); + // if (!base || !base[0]) { + // std::strcpy(buf, path); + // return buf; + // } + + if (len && !strncasecmp(path, GetName(), len)) { + std::snprintf(buf, sizeof(buf), "%s/%s", base, path + len); } else { std::snprintf(buf, sizeof(buf), "%s/%s", base, path); // std::strcpy(buf, path); @@ -89,6 +94,10 @@ protected: }; struct FsProxy final : FsProxyBase { + using File = fs::File; + using Dir = fs::Dir; + using DirEntry = FsDirectoryEntry; + FsProxy(std::unique_ptr&& fs, const char* name, const char* display_name) : FsProxyBase{name, display_name} , m_fs{std::forward(fs)} { @@ -111,131 +120,173 @@ struct FsProxy final : FsProxyBase { auto fs = (fs::FsNative*)m_fs.get(); return fsFsGetTotalSpace(&fs->m_fs, FixPath(path), out); } + + // todo: use statvfs. + // then fallback to 256gb if not available. *out = 1024ULL * 1024ULL * 1024ULL * 256ULL; R_SUCCEED(); } + Result GetFreeSpace(const char *path, s64 *out) override { if (m_fs->IsNative()) { auto fs = (fs::FsNative*)m_fs.get(); return fsFsGetFreeSpace(&fs->m_fs, FixPath(path), out); } + + // todo: use statvfs. + // then fallback to 256gb if not available. *out = 1024ULL * 1024ULL * 1024ULL * 256ULL; R_SUCCEED(); } - Result GetEntryType(const char *path, FsDirEntryType *out_entry_type) override { - const auto rc = m_fs->GetEntryType(FixPath(path), out_entry_type); - log_write("[HAZE] GetEntryType(%s) 0x%X\n", path, rc); - return rc; + + Result GetEntryType(const char *path, haze::FileAttrType *out_entry_type) override { + FsDirEntryType type; + R_TRY(m_fs->GetEntryType(FixPath(path), &type)); + *out_entry_type = (type == FsDirEntryType_Dir) ? haze::FileAttrType_DIR : haze::FileAttrType_FILE; + R_SUCCEED(); } - Result CreateFile(const char* path, s64 size, u32 option) override { + + Result GetEntryAttributes(const char *path, haze::FileAttr *out) override { + FsDirEntryType type; + R_TRY(m_fs->GetEntryType(FixPath(path), &type)); + + if (type == FsDirEntryType_File) { + out->type = haze::FileAttrType_FILE; + + // it doesn't matter if this fails. + s64 size{}; + FsTimeStampRaw timestamp{}; + R_TRY(m_fs->FileGetSizeAndTimestamp(FixPath(path), ×tamp, &size)); + + out->size = size; + if (timestamp.is_valid) { + out->ctime = timestamp.created; + out->mtime = timestamp.modified; + } + } else { + out->type = haze::FileAttrType_DIR; + } + + if (IsReadOnly()) { + out->flag |= haze::FileAttrFlag_READ_ONLY; + } + + R_SUCCEED(); + } + + Result CreateFile(const char* path, s64 size) override { log_write("[HAZE] CreateFile(%s)\n", path); - return m_fs->CreateFile(FixPath(path), size, option); + return m_fs->CreateFile(FixPath(path), 0, 0); } + Result DeleteFile(const char* path) override { log_write("[HAZE] DeleteFile(%s)\n", path); return m_fs->DeleteFile(FixPath(path)); } + Result RenameFile(const char *old_path, const char *new_path) override { log_write("[HAZE] RenameFile(%s -> %s)\n", old_path, new_path); return m_fs->RenameFile(FixPath(old_path), FixPath(new_path)); } - Result OpenFile(const char *path, u32 mode, FsFile *out_file) override { - log_write("[HAZE] OpenFile(%s)\n", path); - auto fptr = new fs::File(); - const auto rc = m_fs->OpenFile(FixPath(path), mode, fptr); - if (R_SUCCEEDED(rc)) { - std::memcpy(&out_file->s, &fptr, sizeof(fptr)); - } else { - delete fptr; + Result OpenFile(const char *path, haze::FileOpenMode mode, haze::File *out_file) override { + log_write("[HAZE] OpenFile(%s)\n", path); + + u32 flags = FsOpenMode_Read; + if (mode == haze::FileOpenMode_WRITE) { + flags = FsOpenMode_Write | FsOpenMode_Append; } - return rc; + auto f = new File(); + const auto rc = m_fs->OpenFile(FixPath(path), flags, f); + if (R_FAILED(rc)) { + log_write("[HAZE] OpenFile(%s) failed: 0x%X\n", path, rc); + delete f; + return rc; + } + + + out_file->impl = f; + R_SUCCEED(); } - Result GetFileSize(FsFile *file, s64 *out_size) override { - log_write("[HAZE] GetFileSize()\n"); - fs::File* f; - std::memcpy(&f, &file->s, sizeof(f)); + + Result GetFileSize(haze::File *file, s64 *out_size) override { + auto f = static_cast(file->impl); return f->GetSize(out_size); } - Result SetFileSize(FsFile *file, s64 size) override { - log_write("[HAZE] SetFileSize(%zd)\n", size); - fs::File* f; - std::memcpy(&f, &file->s, sizeof(f)); + + Result SetFileSize(haze::File *file, s64 size) override { + auto f = static_cast(file->impl); return f->SetSize(size); } - Result ReadFile(FsFile *file, s64 off, void *buf, u64 read_size, u32 option, u64 *out_bytes_read) override { - log_write("[HAZE] ReadFile(%zd, %zu)\n", off, read_size); - fs::File* f; - std::memcpy(&f, &file->s, sizeof(f)); - return f->Read(off, buf, read_size, option, out_bytes_read); + + Result ReadFile(haze::File *file, s64 off, void *buf, u64 read_size, u64 *out_bytes_read) override { + auto f = static_cast(file->impl); + return f->Read(off, buf, read_size, FsReadOption_None, out_bytes_read); } - Result WriteFile(FsFile *file, s64 off, const void *buf, u64 write_size, u32 option) override { - log_write("[HAZE] WriteFile(%zd, %zu)\n", off, write_size); - fs::File* f; - std::memcpy(&f, &file->s, sizeof(f)); - return f->Write(off, buf, write_size, option); + + Result WriteFile(haze::File *file, s64 off, const void *buf, u64 write_size) override { + auto f = static_cast(file->impl); + return f->Write(off, buf, write_size, FsWriteOption_None); } - void CloseFile(FsFile *file) override { - log_write("[HAZE] CloseFile()\n"); - fs::File* f; - std::memcpy(&f, &file->s, sizeof(f)); + + void CloseFile(haze::File *file) override { + auto f = static_cast(file->impl); if (f) { delete f; + file->impl = nullptr; } - std::memset(file, 0, sizeof(*file)); } Result CreateDirectory(const char* path) override { - log_write("[HAZE] DeleteFile(%s)\n", path); return m_fs->CreateDirectory(FixPath(path)); } + Result DeleteDirectoryRecursively(const char* path) override { - log_write("[HAZE] DeleteDirectoryRecursively(%s)\n", path); return m_fs->DeleteDirectoryRecursively(FixPath(path)); } + Result RenameDirectory(const char *old_path, const char *new_path) override { - log_write("[HAZE] RenameDirectory(%s -> %s)\n", old_path, new_path); return m_fs->RenameDirectory(FixPath(old_path), FixPath(new_path)); } - Result OpenDirectory(const char *path, u32 mode, FsDir *out_dir) override { - auto fptr = new fs::Dir(); - const auto rc = m_fs->OpenDirectory(FixPath(path), mode, fptr); - if (R_SUCCEEDED(rc)) { - std::memcpy(&out_dir->s, &fptr, sizeof(fptr)); - } else { - delete fptr; + Result OpenDirectory(const char *path, haze::Dir *out_dir) override { + auto dir = new Dir(); + const auto rc = m_fs->OpenDirectory(FixPath(path), FsDirOpenMode_ReadDirs | FsDirOpenMode_ReadFiles | FsDirOpenMode_NoFileSize, dir); + if (R_FAILED(rc)) { + log_write("[HAZE] OpenDirectory(%s) failed: 0x%X\n", path, rc); + delete dir; + return rc; } - log_write("[HAZE] OpenDirectory(%s) 0x%X\n", path, rc); - return rc; + out_dir->impl = dir; + R_SUCCEED(); } - Result ReadDirectory(FsDir *d, s64 *out_total_entries, size_t max_entries, FsDirectoryEntry *buf) override { - fs::Dir* f; - std::memcpy(&f, &d->s, sizeof(f)); - const auto rc = f->Read(out_total_entries, max_entries, buf); - log_write("[HAZE] ReadDirectory(%zd) 0x%X\n", *out_total_entries, rc); - return rc; - } - Result GetDirectoryEntryCount(FsDir *d, s64 *out_count) override { - fs::Dir* f; - std::memcpy(&f, &d->s, sizeof(f)); - const auto rc = f->GetEntryCount(out_count); - log_write("[HAZE] GetDirectoryEntryCount(%zd) 0x%X\n", *out_count, rc); - return rc; - } - void CloseDirectory(FsDir *d) override { - log_write("[HAZE] CloseDirectory()\n"); - fs::Dir* f; - std::memcpy(&f, &d->s, sizeof(f)); - if (f) { - delete f; + + Result ReadDirectory(haze::Dir *d, s64 *out_total_entries, size_t max_entries, haze::DirEntry *buf) override { + auto dir = static_cast(d->impl); + + std::vector entries(max_entries); + R_TRY(dir->Read(out_total_entries, entries.size(), entries.data())); + + for (s64 i = 0; i < *out_total_entries; i++) { + std::strcpy(buf[i].name, entries[i].name); } - std::memset(d, 0, sizeof(*d)); + + R_SUCCEED(); } - virtual bool MultiThreadTransfer(s64 size, bool read) override { - return !App::IsFileBaseEmummc(); + + Result GetDirectoryEntryCount(haze::Dir *d, s64 *out_count) override { + auto dir = static_cast(d->impl); + return dir->GetEntryCount(out_count); + } + + void CloseDirectory(haze::Dir *d) override { + auto dir = static_cast(d->impl); + if (dir) { + delete dir; + d->impl = nullptr; + } } private: @@ -245,6 +296,15 @@ private: // fake fs that allows for files to create r/w on the root. // folders are not yet supported. struct FsProxyVfs : FsProxyBase { + struct File { + u64 index{}; + haze::FileOpenMode mode{}; + }; + + struct Dir { + u64 pos{}; + }; + using FsProxyBase::FsProxyBase; virtual ~FsProxyVfs() = default; @@ -260,9 +320,9 @@ struct FsProxyVfs : FsProxyBase { return file_name + 1; } - virtual Result GetEntryType(const char *path, FsDirEntryType *out_entry_type) { + virtual Result GetEntryType(const char *path, haze::FileAttrType *out_entry_type) { if (FixPath(path) == "/") { - *out_entry_type = FsDirEntryType_Dir; + *out_entry_type = haze::FileAttrType_DIR; R_SUCCEED(); } else { const auto file_name = GetFileName(path); @@ -273,11 +333,12 @@ struct FsProxyVfs : FsProxyBase { }); R_UNLESS(it != m_entries.end(), FsError_PathNotFound); - *out_entry_type = FsDirEntryType_File; + *out_entry_type = haze::FileAttrType_FILE; R_SUCCEED(); } } - virtual Result CreateFile(const char* path, s64 size, u32 option) { + + virtual Result CreateFile(const char* path, s64 size) { const auto file_name = GetFileName(path); R_UNLESS(file_name, FsError_PathNotFound); @@ -294,6 +355,7 @@ struct FsProxyVfs : FsProxyBase { m_entries.emplace_back(entry); R_SUCCEED(); } + virtual Result DeleteFile(const char* path) { const auto file_name = GetFileName(path); R_UNLESS(file_name, FsError_PathNotFound); @@ -306,6 +368,7 @@ struct FsProxyVfs : FsProxyBase { m_entries.erase(it); R_SUCCEED(); } + virtual Result RenameFile(const char *old_path, const char *new_path) { const auto file_name = GetFileName(old_path); R_UNLESS(file_name, FsError_PathNotFound); @@ -326,7 +389,8 @@ struct FsProxyVfs : FsProxyBase { std::strcpy(it->name, file_name_new); R_SUCCEED(); } - virtual Result OpenFile(const char *path, u32 mode, FsFile *out_file) { + + virtual Result OpenFile(const char *path, haze::FileOpenMode mode, haze::File *out_file) { const auto file_name = GetFileName(path); R_UNLESS(file_name, FsError_PathNotFound); @@ -335,65 +399,89 @@ struct FsProxyVfs : FsProxyBase { }); R_UNLESS(it != m_entries.end(), FsError_PathNotFound); - out_file->s.object_id = std::distance(m_entries.begin(), it); - out_file->s.own_handle = mode; + auto f = new File(); + f->index = std::distance(m_entries.begin(), it); + f->mode = mode; + out_file->impl = f; R_SUCCEED(); } - virtual Result GetFileSize(FsFile *file, s64 *out_size) { - auto& e = m_entries[file->s.object_id]; - *out_size = e.file_size; + + virtual Result GetFileSize(haze::File *file, s64 *out_size) { + auto f = static_cast(file->impl); + *out_size = m_entries[f->index].file_size; R_SUCCEED(); } - virtual Result SetFileSize(FsFile *file, s64 size) { - auto& e = m_entries[file->s.object_id]; - e.file_size = size; + + virtual Result SetFileSize(haze::File *file, s64 size) { + auto f = static_cast(file->impl); + m_entries[f->index].file_size = size; R_SUCCEED(); } - virtual Result ReadFile(FsFile *file, s64 off, void *buf, u64 read_size, u32 option, u64 *out_bytes_read) { + + virtual Result ReadFile(haze::File *file, s64 off, void *buf, u64 read_size, u64 *out_bytes_read) { // stub for now as it may confuse users who think that the returned file is valid. // the code below can be used to benchmark mtp reads. R_THROW(FsError_NotImplemented); - // auto& e = m_entries[file->s.object_id]; - // read_size = std::min(e.file_size - off, read_size); - // std::memset(buf, 0, read_size); - // *out_bytes_read = read_size; - // R_SUCCEED(); } - virtual Result WriteFile(FsFile *file, s64 off, const void *buf, u64 write_size, u32 option) { - auto& e = m_entries[file->s.object_id]; + + virtual Result WriteFile(haze::File *file, s64 off, const void *buf, u64 write_size) { + auto f = static_cast(file->impl); + auto& e = m_entries[f->index]; e.file_size = std::max(e.file_size, off + write_size); R_SUCCEED(); } - virtual void CloseFile(FsFile *file) { - std::memset(file, 0, sizeof(*file)); + + virtual void CloseFile(haze::File *file) { + auto f = static_cast(file->impl); + if (f) { + delete f; + file->impl = nullptr; + } } Result CreateDirectory(const char* path) override { R_THROW(FsError_NotImplemented); } + Result DeleteDirectoryRecursively(const char* path) override { R_THROW(FsError_NotImplemented); } + Result RenameDirectory(const char *old_path, const char *new_path) override { R_THROW(FsError_NotImplemented); } - Result OpenDirectory(const char *path, u32 mode, FsDir *out_dir) override { - std::memset(out_dir, 0, sizeof(*out_dir)); + + Result OpenDirectory(const char *path, haze::Dir *out_dir) override { + auto dir = new Dir(); + out_dir->impl = dir; R_SUCCEED(); } - Result ReadDirectory(FsDir *d, s64 *out_total_entries, size_t max_entries, FsDirectoryEntry *buf) override { - max_entries = std::min(m_entries.size()- d->s.object_id, max_entries); - std::memcpy(buf, m_entries.data() + d->s.object_id, max_entries * sizeof(*buf)); - d->s.object_id += max_entries; + + Result ReadDirectory(haze::Dir *d, s64 *out_total_entries, size_t max_entries, haze::DirEntry *buf) override { + auto dir = static_cast(d->impl); + + max_entries = std::min(m_entries.size() - dir->pos, max_entries); + + for (size_t i = 0; i < max_entries; i++) { + std::strcpy(buf[i].name, m_entries[dir->pos + i].name); + } + + dir->pos += max_entries; *out_total_entries = max_entries; R_SUCCEED(); } - Result GetDirectoryEntryCount(FsDir *d, s64 *out_count) override { + + Result GetDirectoryEntryCount(haze::Dir *d, s64 *out_count) override { *out_count = m_entries.size(); R_SUCCEED(); } - void CloseDirectory(FsDir *d) override { - std::memset(d, 0, sizeof(*d)); + + void CloseDirectory(haze::Dir *d) override { + auto dir = static_cast(d->impl); + if (dir) { + delete dir; + d->impl = nullptr; + } } protected: @@ -407,13 +495,11 @@ struct FsDevNullProxy final : FsProxyVfs { *out = 1024ULL * 1024ULL * 1024ULL * 256ULL; R_SUCCEED(); } + Result GetFreeSpace(const char *path, s64 *out) override { *out = 1024ULL * 1024ULL * 1024ULL * 256ULL; R_SUCCEED(); } - bool MultiThreadTransfer(s64 size, bool read) override { - return true; - } }; struct FsInstallProxy final : FsProxyVfs { @@ -456,6 +542,7 @@ struct FsInstallProxy final : FsProxyVfs { return fs::FsNativeContentStorage(FsContentStorageId_User).GetTotalSpace("/", out); } } + Result GetFreeSpace(const char *path, s64 *out) override { if (App::GetApp()->m_install_sd.Get()) { return fs::FsNativeContentStorage(FsContentStorageId_SdCard).GetFreeSpace("/", out); @@ -464,27 +551,30 @@ struct FsInstallProxy final : FsProxyVfs { } } - Result GetEntryType(const char *path, FsDirEntryType *out_entry_type) override { + Result GetEntryType(const char *path, haze::FileAttrType *out_entry_type) override { R_TRY(FsProxyVfs::GetEntryType(path, out_entry_type)); - if (*out_entry_type == FsDirEntryType_File) { + if (*out_entry_type == haze::FileAttrType_FILE) { R_TRY(FailedIfNotEnabled()); } R_SUCCEED(); } - Result CreateFile(const char* path, s64 size, u32 option) override { + + Result CreateFile(const char* path, s64 size) override { R_TRY(FailedIfNotEnabled()); R_TRY(IsValidFileType(path)); - R_TRY(FsProxyVfs::CreateFile(path, size, option)); + R_TRY(FsProxyVfs::CreateFile(path, size)); R_SUCCEED(); } - Result OpenFile(const char *path, u32 mode, FsFile *out_file) override { + + Result OpenFile(const char *path, haze::FileOpenMode mode, haze::File *out_file) override { R_TRY(FailedIfNotEnabled()); R_TRY(IsValidFileType(path)); R_TRY(FsProxyVfs::OpenFile(path, mode, out_file)); log_write("[MTP] done file open: %s mode: 0x%X\n", path, mode); - if (mode & FsOpenMode_Write) { - const auto& e = m_entries[out_file->s.object_id]; + if (mode == haze::FileOpenMode_WRITE) { + auto f = static_cast(out_file->impl); + const auto& e = m_entries[f->index]; // check if we already have this file queued. log_write("[MTP] checking if empty\n"); @@ -497,7 +587,8 @@ struct FsInstallProxy final : FsProxyVfs { log_write("[MTP] got file: %s\n", path); R_SUCCEED(); } - Result WriteFile(FsFile *file, s64 off, const void *buf, u64 write_size, u32 option) override { + + Result WriteFile(haze::File *file, s64 off, const void *buf, u64 write_size) override { SCOPED_MUTEX(&g_shared_data.mutex); if (!g_shared_data.enabled) { log_write("[MTP] failing as not enabled\n"); @@ -509,14 +600,20 @@ struct FsInstallProxy final : FsProxyVfs { R_THROW(FsError_NotImplemented); } - R_TRY(FsProxyVfs::WriteFile(file, off, buf, write_size, option)); + R_TRY(FsProxyVfs::WriteFile(file, off, buf, write_size)); R_SUCCEED(); } - void CloseFile(FsFile *file) override { + + void CloseFile(haze::File *file) override { + auto f = static_cast(file->impl); + if (!f) { + return; + } + bool update{}; { SCOPED_MUTEX(&g_shared_data.mutex); - if (file->s.own_handle & FsOpenMode_Write) { + if (f->mode == haze::FileOpenMode_WRITE) { log_write("[MTP] closing current file\n"); if (g_shared_data.on_close) { g_shared_data.on_close(); @@ -534,40 +631,36 @@ struct FsInstallProxy final : FsProxyVfs { FsProxyVfs::CloseFile(file); } - - // installs are already multi-threaded via yati. - bool MultiThreadTransfer(s64 size, bool read) override { - App::IsFileBaseEmummc(); - return false; - } }; -::haze::FsEntries g_fs_entries{}; +haze::FsEntries g_fs_entries{}; -void haze_callback(const ::haze::CallbackData *data) { +void haze_callback(const haze::CallbackData *data) { auto& e = *data; + #if 0 switch (e.type) { - case ::haze::CallbackType_OpenSession: log_write("[LIBHAZE] Opening Session\n"); break; - case ::haze::CallbackType_CloseSession: log_write("[LIBHAZE] Closing Session\n"); break; + case haze::CallbackType_OpenSession: log_write("[LIBHAZE] Opening Session\n"); break; + case haze::CallbackType_CloseSession: log_write("[LIBHAZE] Closing Session\n"); break; - case ::haze::CallbackType_CreateFile: log_write("[LIBHAZE] Creating File: %s\n", e.file.filename); break; - case ::haze::CallbackType_DeleteFile: log_write("[LIBHAZE] Deleting File: %s\n", e.file.filename); break; + case haze::CallbackType_CreateFile: log_write("[LIBHAZE] Creating File: %s\n", e.file.filename); break; + case haze::CallbackType_DeleteFile: log_write("[LIBHAZE] Deleting File: %s\n", e.file.filename); break; - case ::haze::CallbackType_RenameFile: log_write("[LIBHAZE] Rename File: %s -> %s\n", e.rename.filename, e.rename.newname); break; - case ::haze::CallbackType_RenameFolder: log_write("[LIBHAZE] Rename Folder: %s -> %s\n", e.rename.filename, e.rename.newname); break; + case haze::CallbackType_RenameFile: log_write("[LIBHAZE] Rename File: %s -> %s\n", e.rename.filename, e.rename.newname); break; + case haze::CallbackType_RenameFolder: log_write("[LIBHAZE] Rename Folder: %s -> %s\n", e.rename.filename, e.rename.newname); break; - case ::haze::CallbackType_CreateFolder: log_write("[LIBHAZE] Creating Folder: %s\n", e.file.filename); break; - case ::haze::CallbackType_DeleteFolder: log_write("[LIBHAZE] Deleting Folder: %s\n", e.file.filename); break; + case haze::CallbackType_CreateFolder: log_write("[LIBHAZE] Creating Folder: %s\n", e.file.filename); break; + case haze::CallbackType_DeleteFolder: log_write("[LIBHAZE] Deleting Folder: %s\n", e.file.filename); break; - case ::haze::CallbackType_ReadBegin: log_write("[LIBHAZE] Reading File Begin: %s \n", e.file.filename); break; - case ::haze::CallbackType_ReadProgress: log_write("\t[LIBHAZE] Reading File: offset: %lld size: %lld\n", e.progress.offset, e.progress.size); break; - case ::haze::CallbackType_ReadEnd: log_write("[LIBHAZE] Reading File Finished: %s\n", e.file.filename); break; + case haze::CallbackType_ReadBegin: log_write("[LIBHAZE] Reading File Begin: %s \n", e.file.filename); break; + case haze::CallbackType_ReadProgress: log_write("\t[LIBHAZE] Reading File: offset: %lld size: %lld\n", e.progress.offset, e.progress.size); break; + case haze::CallbackType_ReadEnd: log_write("[LIBHAZE] Reading File Finished: %s\n", e.file.filename); break; - case ::haze::CallbackType_WriteBegin: log_write("[LIBHAZE] Writing File Begin: %s \n", e.file.filename); break; - case ::haze::CallbackType_WriteProgress: log_write("\t[LIBHAZE] Writing File: offset: %lld size: %lld\n", e.progress.offset, e.progress.size); break; - case ::haze::CallbackType_WriteEnd: log_write("[LIBHAZE] Writing File Finished: %s\n", e.file.filename); break; + case haze::CallbackType_WriteBegin: log_write("[LIBHAZE] Writing File Begin: %s \n", e.file.filename); break; + case haze::CallbackType_WriteProgress: log_write("\t[LIBHAZE] Writing File: offset: %lld size: %lld\n", e.progress.offset, e.progress.size); break; + case haze::CallbackType_WriteEnd: log_write("[LIBHAZE] Writing File Finished: %s\n", e.file.filename); break; } + #endif App::NotifyFlashLed(); } @@ -588,7 +681,7 @@ bool Init() { g_fs_entries.emplace_back(std::make_shared("install", "Install (NSP, XCI, NSZ, XCZ)")); g_should_exit = false; - if (!::haze::Initialize(haze_callback, THREAD_PRIO, THREAD_CORE, g_fs_entries, App::GetApp()->m_mtp_vid.Get(), App::GetApp()->m_mtp_pid.Get())) { + if (!haze::Initialize(haze_callback, g_fs_entries, App::GetApp()->m_mtp_vid.Get(), App::GetApp()->m_mtp_pid.Get())) { return false; } @@ -607,7 +700,7 @@ void Exit() { return; } - ::haze::Exit(); + haze::Exit(); g_is_running = false; g_should_exit = true; g_fs_entries.clear(); @@ -628,4 +721,4 @@ void DisableInstallMode() { g_shared_data.enabled = false; } -} // namespace sphaira::haze +} // namespace sphaira::libhaze diff --git a/sphaira/source/ui/menus/install_stream_menu_base.cpp b/sphaira/source/ui/menus/install_stream_menu_base.cpp index 33fc770..d8c049f 100644 --- a/sphaira/source/ui/menus/install_stream_menu_base.cpp +++ b/sphaira/source/ui/menus/install_stream_menu_base.cpp @@ -16,34 +16,26 @@ enum class InstallState { Finished, }; -constexpr u64 MAX_BUFFER_SIZE = 1024ULL*1024ULL*8ULL; -constexpr u64 MAX_BUFFER_RESERVE_SIZE = 1024ULL*1024ULL*32ULL; +constexpr u64 MAX_BUFFER_SIZE = 1024ULL*1024ULL*1ULL; std::atomic INSTALL_STATE{InstallState::None}; -// don't use condivar here as windows mtp is very broken. -// stalling for too longer (3s+) and having too varied transfer speeds -// results in windows stalling the transfer for 1m until it kills it via timeout. -// the workaround is to always accept new data, but stall for 1s. -// UPDATE: it seems possible to trigger this bug during normal file transfer -// including using stock haze. -// it seems random, and ive been unable to trigger it personally. -// for this reason, use condivar rather than trying to work around the issue. -#define USE_CONDI_VAR 1 - } // namespace Stream::Stream(const fs::FsPath& path, std::stop_token token) { m_path = path; m_token = token; m_active = true; - m_buffer.reserve(MAX_BUFFER_RESERVE_SIZE); + m_buffer.reserve(MAX_BUFFER_SIZE); mutexInit(&m_mutex); condvarInit(&m_can_read); condvarInit(&m_can_write); } -Result Stream::ReadChunk(void* buf, s64 size, u64* bytes_read) { +Result Stream::ReadChunk(void* _buf, s64 size, u64* bytes_read) { + auto buf = static_cast(_buf); + *bytes_read = 0; + log_write("[Stream::ReadChunk] inside\n"); ON_SCOPE_EXIT( log_write("[Stream::ReadChunk] exiting\n"); @@ -59,18 +51,30 @@ Result Stream::ReadChunk(void* buf, s64 size, u64* bytes_read) { break; } - size = std::min(size, m_buffer.size()); - std::memcpy(buf, m_buffer.data(), size); - m_buffer.erase(m_buffer.begin(), m_buffer.begin() + size); - *bytes_read = size; - return condvarWakeOne(&m_can_write); + const auto rsize = std::min(size, m_buffer.size()); + std::memcpy(buf, m_buffer.data(), rsize); + m_buffer.erase(m_buffer.begin(), m_buffer.begin() + rsize); + condvarWakeOne(&m_can_write); + + size -= rsize; + buf += rsize; + *bytes_read += rsize; + + if (!size) { + R_SUCCEED(); + } } log_write("[Stream::ReadChunk] failed to read\n"); R_THROW(Result_TransferCancelled); } -bool Stream::Push(const void* buf, s64 size) { +bool Stream::Push(const void* _buf, s64 size) { + auto buf = static_cast(_buf); + if (!size) { + return true; + } + log_write("[Stream::Push] inside\n"); ON_SCOPE_EXIT( log_write("[Stream::Push] exiting\n"); @@ -83,31 +87,27 @@ bool Stream::Push(const void* buf, s64 size) { } SCOPED_MUTEX(&m_mutex); - #if USE_CONDI_VAR if (m_active && m_buffer.size() >= MAX_BUFFER_SIZE) { R_TRY(condvarWait(std::addressof(m_can_write), std::addressof(m_mutex))); } - #else - if (m_active && m_buffer.size() >= MAX_BUFFER_SIZE) { - // unlock the mutex and wait for 1s to bring transfer speed down to 1MiB/s. - log_write("[Stream::Push] buffer is full, delaying\n"); - mutexUnlock(&m_mutex); - ON_SCOPE_EXIT(mutexLock(&m_mutex)); - - svcSleepThread(1e+9); - } - #endif if (!m_active) { log_write("[Stream::Push] file not active\n"); break; } + const auto wsize = std::min(size, MAX_BUFFER_SIZE - m_buffer.size()); const auto offset = m_buffer.size(); - m_buffer.resize(offset + size); - std::memcpy(m_buffer.data() + offset, buf, size); + m_buffer.resize(offset + wsize); + + std::memcpy(m_buffer.data() + offset, buf, wsize); condvarWakeOne(&m_can_read); - return true; + + size -= wsize; + buf += wsize; + if (!size) { + return true; + } } log_write("[Stream::Push] failed to push\n"); diff --git a/sphaira/source/ui/menus/mtp_menu.cpp b/sphaira/source/ui/menus/mtp_menu.cpp index 62eb55b..71537e0 100644 --- a/sphaira/source/ui/menus/mtp_menu.cpp +++ b/sphaira/source/ui/menus/mtp_menu.cpp @@ -10,13 +10,13 @@ namespace sphaira::ui::menu::mtp { Menu::Menu(u32 flags) : stream::Menu{"MTP Install"_i18n, flags} { - m_was_mtp_enabled = haze::IsInit(); + m_was_mtp_enabled = libhaze::IsInit(); if (!m_was_mtp_enabled) { log_write("[MTP] wasn't enabled, forcefully enabling\n"); - haze::Init(); + libhaze::Init(); } - haze::InitInstallMode( + libhaze::InitInstallMode( [this](const char* path){ return OnInstallStart(path); }, [this](const void *buf, size_t size){ return OnInstallWrite(buf, size); }, [this](){ return OnInstallClose(); } @@ -25,11 +25,11 @@ Menu::Menu(u32 flags) : stream::Menu{"MTP Install"_i18n, flags} { Menu::~Menu() { // signal for thread to exit and wait. - haze::DisableInstallMode(); + libhaze::DisableInstallMode(); if (!m_was_mtp_enabled) { log_write("[MTP] disabling on exit\n"); - haze::Exit(); + libhaze::Exit(); } } @@ -53,7 +53,7 @@ void Menu::Update(Controller* controller, TouchInfo* touch) { } void Menu::OnDisableInstallMode() { - haze::DisableInstallMode(); + libhaze::DisableInstallMode(); } } // namespace sphaira::ui::menu::mtp