diff --git a/sphaira/include/defines.hpp b/sphaira/include/defines.hpp index 36f04e3..335eb8c 100644 --- a/sphaira/include/defines.hpp +++ b/sphaira/include/defines.hpp @@ -595,7 +595,10 @@ enum class SphairaResult : Result { UsbBadMagic, UsbBadVersion, UsbBadCount, + UsbBadBufferAlign, UsbBadTransferSize, + UsbEmptyTransferSize, + UsbOverflowTransferSize, UsbBadTotalSize, UsbUploadBadMagic, @@ -726,8 +729,10 @@ enum : Result { MAKE_SPHAIRA_RESULT_ENUM(UsbBadMagic), MAKE_SPHAIRA_RESULT_ENUM(UsbBadVersion), MAKE_SPHAIRA_RESULT_ENUM(UsbBadCount), + MAKE_SPHAIRA_RESULT_ENUM(UsbBadBufferAlign), MAKE_SPHAIRA_RESULT_ENUM(UsbBadTransferSize), - MAKE_SPHAIRA_RESULT_ENUM(UsbBadTotalSize), + MAKE_SPHAIRA_RESULT_ENUM(UsbEmptyTransferSize), + MAKE_SPHAIRA_RESULT_ENUM(UsbOverflowTransferSize), MAKE_SPHAIRA_RESULT_ENUM(UsbUploadBadMagic), MAKE_SPHAIRA_RESULT_ENUM(UsbUploadExit), MAKE_SPHAIRA_RESULT_ENUM(UsbUploadBadCount), diff --git a/sphaira/include/ui/menus/usb_menu.hpp b/sphaira/include/ui/menus/usb_menu.hpp index fd31166..38fb514 100644 --- a/sphaira/include/ui/menus/usb_menu.hpp +++ b/sphaira/include/ui/menus/usb_menu.hpp @@ -27,19 +27,16 @@ struct Menu final : MenuBase { auto GetShortTitle() const -> const char* override { return "USB"; }; void Update(Controller* controller, TouchInfo* touch) override; void Draw(NVGcontext* vg, Theme* theme) override; - void OnFocusGained() override; -// this should be private -// private: + void ThreadFunction(); + +private: std::unique_ptr m_usb_source{}; bool m_was_mtp_enabled{}; Thread m_thread{}; - Mutex m_mutex{}; - // the below are shared across threads, lock with the above mutex! - State m_state{State::None}; + std::atomic m_state{State::None}; std::vector m_names{}; - bool m_usb_has_connection{}; }; } // namespace sphaira::ui::menu::usb diff --git a/sphaira/include/ui/notification.hpp b/sphaira/include/ui/notification.hpp index 847ac75..66e3497 100644 --- a/sphaira/include/ui/notification.hpp +++ b/sphaira/include/ui/notification.hpp @@ -45,6 +45,7 @@ private: private: void Draw(NVGcontext* vg, Theme* theme, Entries& entries); + auto GetEntries(NotifEntry::Side side) -> Entries&; private: Entries m_entries_left{}; diff --git a/sphaira/include/usb/base.hpp b/sphaira/include/usb/base.hpp index 77de2bb..7f40a36 100644 --- a/sphaira/include/usb/base.hpp +++ b/sphaira/include/usb/base.hpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include namespace sphaira::usb { @@ -39,43 +39,10 @@ struct Base { ueventSignal(GetCancelEvent()); } - auto& GetTransferBuffer() { - return m_aligned; - } - auto GetTransferTimeout() const { return m_transfer_timeout; } -public: - // custom allocator for std::vector that respects alignment. - // https://en.cppreference.com/w/cpp/named_req/Allocator - template - struct CustomVectorAllocator { - public: - // https://en.cppreference.com/w/cpp/memory/new/operator_new - auto allocate(std::size_t n) -> T* { - n = (n + (Align - 1)) &~ (Align - 1); - return new(align) T[n]; - } - - // https://en.cppreference.com/w/cpp/memory/new/operator_delete - auto deallocate(T* p, std::size_t n) noexcept -> void { - // ::operator delete[] (p, n, align); - ::operator delete[] (p, align); - } - - private: - static constexpr inline std::align_val_t align{Align}; - }; - - template - struct PageAllocator : CustomVectorAllocator { - using value_type = T; // used by std::vector - }; - - using PageAlignedVector = std::vector>; - protected: enum UsbSessionEndpoint { UsbSessionEndpoint_In = 0, @@ -90,7 +57,7 @@ protected: private: u64 m_transfer_timeout{}; UEvent m_uevent{}; - PageAlignedVector m_aligned{}; + std::unique_ptr m_aligned{}; }; } // namespace sphaira::usb diff --git a/sphaira/include/usb/usb_uploader.hpp b/sphaira/include/usb/usb_uploader.hpp index 2111f83..3ea9bfe 100644 --- a/sphaira/include/usb/usb_uploader.hpp +++ b/sphaira/include/usb/usb_uploader.hpp @@ -31,6 +31,7 @@ private: private: std::unique_ptr m_usb; + std::vector m_buf; }; } // namespace sphaira::usb::upload diff --git a/sphaira/include/usb/usbds.hpp b/sphaira/include/usb/usbds.hpp index 4a742fc..5ff1216 100644 --- a/sphaira/include/usb/usbds.hpp +++ b/sphaira/include/usb/usbds.hpp @@ -7,6 +7,9 @@ enum { UsbDeviceSpeed_None = 0x0 }; enum { UsbDeviceSpeed_Low = 0x1 }; Result usbDsGetSpeed(UsbDeviceSpeed *out); +auto GetUsbDsStateStr(UsbState state) -> const char*; +auto GetUsbDsSpeedStr(UsbDeviceSpeed speed) -> const char*; + namespace sphaira::usb { // Device Host diff --git a/sphaira/source/ui/error_box.cpp b/sphaira/source/ui/error_box.cpp index 56a557f..2ce28a7 100644 --- a/sphaira/source/ui/error_box.cpp +++ b/sphaira/source/ui/error_box.cpp @@ -114,8 +114,10 @@ auto GetCodeMessage(Result rc) -> const char* { case Result_UsbBadMagic: return "SphairaError_UsbBadMagic"; case Result_UsbBadVersion: return "SphairaError_UsbBadVersion"; case Result_UsbBadCount: return "SphairaError_UsbBadCount"; + case Result_UsbBadBufferAlign: return "SphairaError_UsbBadBufferAlign"; case Result_UsbBadTransferSize: return "SphairaError_UsbBadTransferSize"; - case Result_UsbBadTotalSize: return "SphairaError_UsbBadTotalSize"; + case Result_UsbEmptyTransferSize: return "SphairaError_UsbEmptyTransferSize"; + case Result_UsbOverflowTransferSize: return "SphairaError_UsbOverflowTransferSize"; case Result_UsbUploadBadMagic: return "SphairaError_UsbUploadBadMagic"; case Result_UsbUploadExit: return "SphairaError_UsbUploadExit"; case Result_UsbUploadBadCount: return "SphairaError_UsbUploadBadCount"; diff --git a/sphaira/source/ui/menus/install_stream_menu_base.cpp b/sphaira/source/ui/menus/install_stream_menu_base.cpp index c70c5f6..d67fa03 100644 --- a/sphaira/source/ui/menus/install_stream_menu_base.cpp +++ b/sphaira/source/ui/menus/install_stream_menu_base.cpp @@ -41,7 +41,7 @@ Result Stream::ReadChunk(void* buf, s64 size, u64* bytes_read) { while (!m_token.stop_requested()) { SCOPED_MUTEX(&m_mutex); if (m_active && m_buffer.empty()) { - condvarWait(&m_can_read, &m_mutex); + R_TRY(condvarWait(&m_can_read, &m_mutex)); } if ((!m_active && m_buffer.empty()) || m_token.stop_requested()) { diff --git a/sphaira/source/ui/menus/mtp_menu.cpp b/sphaira/source/ui/menus/mtp_menu.cpp index 79ab210..be64f99 100644 --- a/sphaira/source/ui/menus/mtp_menu.cpp +++ b/sphaira/source/ui/menus/mtp_menu.cpp @@ -8,36 +8,6 @@ #include "haze_helper.hpp" namespace sphaira::ui::menu::mtp { -namespace { - -auto GetUsbStateStr(UsbState state) -> const char* { - switch (state) { - case UsbState_Detached: return "Detached"; - case UsbState_Attached: return "Attached"; - case UsbState_Powered: return "Powered"; - case UsbState_Default: return "Default"; - case UsbState_Address: return "Address"; - case UsbState_Configured: return "Configured"; - case UsbState_Suspended: return "Suspended"; - } - - return "Unknown"; -} - -auto GetUsbSpeedStr(UsbDeviceSpeed speed) -> const char* { - // todo: remove this cast when libnx pr is merged. - switch ((u32)speed) { - case UsbDeviceSpeed_None: return "None"; - case UsbDeviceSpeed_Low: return "USB 1.0 Low Speed"; - case UsbDeviceSpeed_Full: return "USB 1.1 Full Speed"; - case UsbDeviceSpeed_High: return "USB 2.0 High Speed"; - case UsbDeviceSpeed_Super: return "USB 3.0 Super Speed"; - } - - return "Unknown"; -} - -} // namespace Menu::Menu(u32 flags) : stream::Menu{"MTP Install"_i18n, flags} { m_was_mtp_enabled = App::GetMtpEnable(); @@ -77,7 +47,7 @@ void Menu::Update(Controller* controller, TouchInfo* touch) { usbDsGetSpeed(&speed); char buf[128]; - std::snprintf(buf, sizeof(buf), "State: %s | Speed: %s", i18n::get(GetUsbStateStr(state)).c_str(), i18n::get(GetUsbSpeedStr(speed)).c_str()); + std::snprintf(buf, sizeof(buf), "State: %s | Speed: %s", i18n::get(GetUsbDsStateStr(state)).c_str(), i18n::get(GetUsbDsSpeedStr(speed)).c_str()); SetSubHeading(buf); } } diff --git a/sphaira/source/ui/menus/usb_menu.cpp b/sphaira/source/ui/menus/usb_menu.cpp index e0960ef..ac4a7aa 100644 --- a/sphaira/source/ui/menus/usb_menu.cpp +++ b/sphaira/source/ui/menus/usb_menu.cpp @@ -16,37 +16,7 @@ constexpr u64 FINISHED_TIMEOUT = 1e+9 * 3; // 3 seconds. void thread_func(void* user) { auto app = static_cast(user); - - for (;;) { - if (app->GetToken().stop_requested()) { - break; - } - - const auto rc = app->m_usb_source->IsUsbConnected(CONNECTION_TIMEOUT); - if (rc == Result_UsbCancelled) { - break; - } - - // set connected status - mutexLock(&app->m_mutex); - if (R_SUCCEEDED(rc)) { - app->m_state = State::Connected_WaitForFileList; - } else { - app->m_state = State::None; - } - mutexUnlock(&app->m_mutex); - - if (R_SUCCEEDED(rc)) { - std::vector names; - if (R_SUCCEEDED(app->m_usb_source->WaitForConnection(CONNECTION_TIMEOUT, names))) { - mutexLock(&app->m_mutex); - ON_SCOPE_EXIT(mutexUnlock(&app->m_mutex)); - app->m_state = State::Connected_StartingTransfer; - app->m_names = names; - break; - } - } - } + app->ThreadFunction(); } } // namespace @@ -74,8 +44,6 @@ Menu::Menu(u32 flags) : MenuBase{"USB"_i18n, flags} { m_state = State::Failed; } - mutexInit(&m_mutex); - if (m_state != State::Failed) { threadCreate(&m_thread, thread_func, this, nullptr, 1024*32, PRIO_PREEMPTIVE, 1); threadStart(&m_thread); @@ -102,8 +70,20 @@ Menu::~Menu() { void Menu::Update(Controller* controller, TouchInfo* touch) { MenuBase::Update(controller, touch); - mutexLock(&m_mutex); - ON_SCOPE_EXIT(mutexUnlock(&m_mutex)); + static TimeStamp poll_ts; + if (poll_ts.GetSeconds() >= 1) { + poll_ts.Update(); + + UsbState state{UsbState_Detached}; + usbDsGetState(&state); + + UsbDeviceSpeed speed{(UsbDeviceSpeed)UsbDeviceSpeed_None}; + usbDsGetSpeed(&speed); + + char buf[128]; + std::snprintf(buf, sizeof(buf), "State: %s | Speed: %s", i18n::get(GetUsbDsStateStr(state)).c_str(), i18n::get(GetUsbDsSpeedStr(speed)).c_str()); + SetSubHeading(buf); + } if (m_state == State::Connected_StartingTransfer) { log_write("set to progress\n"); @@ -152,9 +132,6 @@ void Menu::Update(Controller* controller, TouchInfo* touch) { void Menu::Draw(NVGcontext* vg, Theme* theme) { MenuBase::Draw(vg, theme); - mutexLock(&m_mutex); - ON_SCOPE_EXIT(mutexUnlock(&m_mutex)); - switch (m_state) { case State::None: gfx::drawTextArgs(vg, SCREEN_WIDTH / 2.f, SCREEN_HEIGHT / 2.f, 36.f, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE, theme->GetColour(ThemeEntryID_TEXT_INFO), "Waiting for connection..."_i18n.c_str()); @@ -182,8 +159,33 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) { } } -void Menu::OnFocusGained() { - MenuBase::OnFocusGained(); +void Menu::ThreadFunction() { + for (;;) { + if (GetToken().stop_requested()) { + break; + } + + const auto rc = m_usb_source->IsUsbConnected(CONNECTION_TIMEOUT); + if (rc == Result_UsbCancelled) { + break; + } + + // set connected status + if (R_SUCCEEDED(rc)) { + m_state = State::Connected_WaitForFileList; + } else { + m_state = State::None; + } + + if (R_SUCCEEDED(rc)) { + std::vector names; + if (R_SUCCEEDED(m_usb_source->WaitForConnection(CONNECTION_TIMEOUT, names))) { + m_names = names; + m_state = State::Connected_StartingTransfer; + break; + } + } + } } } // namespace sphaira::ui::menu::usb diff --git a/sphaira/source/ui/notification.cpp b/sphaira/source/ui/notification.cpp index fcd64bb..1b9de71 100644 --- a/sphaira/source/ui/notification.cpp +++ b/sphaira/source/ui/notification.cpp @@ -5,6 +5,11 @@ #include namespace sphaira::ui { +namespace { + +constexpr u64 MAX_ENTRIES = 9; + +} // namespace NotifEntry::NotifEntry(std::string text, Side side) : m_text{std::move(text)} @@ -77,13 +82,12 @@ auto NotifMananger::Push(const NotifEntry& entry) -> void { mutexLock(&m_mutex); ON_SCOPE_EXIT(mutexUnlock(&m_mutex)); - switch (entry.GetSide()) { - case NotifEntry::Side::LEFT: - m_entries_left.emplace_front(entry); - break; - case NotifEntry::Side::RIGHT: - m_entries_right.emplace_front(entry); - break; + auto& entries = GetEntries(entry.GetSide()); + entries.emplace_front(entry); + + // check if we have too many entries, start removing old ones. + if (entries.size() >= MAX_ENTRIES) { + entries.erase(entries.begin() + MAX_ENTRIES, entries.end()); } } @@ -91,32 +95,14 @@ auto NotifMananger::Pop(NotifEntry::Side side) -> void { mutexLock(&m_mutex); ON_SCOPE_EXIT(mutexUnlock(&m_mutex)); - switch (side) { - case NotifEntry::Side::LEFT: - if (!m_entries_left.empty()) { - m_entries_left.clear(); - } - break; - case NotifEntry::Side::RIGHT: - if (!m_entries_right.empty()) { - m_entries_right.clear(); - } - break; - } + GetEntries(side).pop_back(); } auto NotifMananger::Clear(NotifEntry::Side side) -> void { mutexLock(&m_mutex); ON_SCOPE_EXIT(mutexUnlock(&m_mutex)); - switch (side) { - case NotifEntry::Side::LEFT: - m_entries_left.clear(); - break; - case NotifEntry::Side::RIGHT: - m_entries_right.clear(); - break; - } + GetEntries(side).clear(); } auto NotifMananger::Clear() -> void { @@ -127,4 +113,12 @@ auto NotifMananger::Clear() -> void { m_entries_right.clear(); } +auto NotifMananger::GetEntries(NotifEntry::Side side) -> Entries& { + if (side == NotifEntry::Side::LEFT) { + return m_entries_left; + } else { + return m_entries_right; + } +} + } // namespace sphaira::ui diff --git a/sphaira/source/usb/base.cpp b/sphaira/source/usb/base.cpp index 8c2c5c3..5f732ab 100644 --- a/sphaira/source/usb/base.cpp +++ b/sphaira/source/usb/base.cpp @@ -24,14 +24,20 @@ #include namespace sphaira::usb { +namespace { + +constexpr u64 TRANSFER_ALIGN = 0x1000; +constexpr u64 TRANSFER_MAX = 1024*1024*16; +static_assert(!(TRANSFER_MAX % TRANSFER_ALIGN)); + +} // namespace Base::Base(u64 transfer_timeout) { App::SetAutoSleepDisabled(true); m_transfer_timeout = transfer_timeout; ueventCreate(GetCancelEvent(), true); - // this avoids allocations during transfers. - m_aligned.reserve(1024 * 1024 * 16); + m_aligned = std::make_unique(new(std::align_val_t{TRANSFER_ALIGN}) u8[TRANSFER_MAX]); } Base::~Base() { @@ -63,39 +69,28 @@ Result Base::TransferPacketImpl(bool read, void *page, u32 remaining, u32 size, // an changes are made. // yati already goes to great lengths to be zero-copy during installing // by swapping buffers and inflating in-place. - -// NOTE: it is now possible to request the transfer buffer using GetTransferBuffer(), -// which will always be aligned and have the size aligned. -// this allows for zero-copy transferrs to take place. -// this is used in usb_upload.cpp. -// do note that this relies of the host sending / receiving buffers of an aligned size. Result Base::TransferAll(bool read, void *data, u32 size, u64 timeout) { auto buf = static_cast(data); - auto transfer_buf = m_aligned.data(); - const auto alias = buf == transfer_buf; + auto transfer_buf = *m_aligned; - if (!alias) { - m_aligned.resize(size); - } + R_UNLESS(!((u64)transfer_buf & 0xFFF), Result_UsbBadBufferAlign); + R_UNLESS(size <= TRANSFER_MAX, Result_UsbBadTransferSize); while (size) { - if (!alias && !read) { + if (!read) { std::memcpy(transfer_buf, buf, size); } u32 out_size_transferred; R_TRY(TransferPacketImpl(read, transfer_buf, size, size, &out_size_transferred, timeout)); + R_UNLESS(out_size_transferred > 0, Result_UsbEmptyTransferSize); + R_UNLESS(out_size_transferred <= size, Result_UsbOverflowTransferSize); - if (!alias && read) { + if (read) { std::memcpy(buf, transfer_buf, out_size_transferred); } - if (alias) { - transfer_buf += out_size_transferred; - } else { - buf += out_size_transferred; - } - + buf += out_size_transferred; size -= out_size_transferred; } diff --git a/sphaira/source/usb/usb_uploader.cpp b/sphaira/source/usb/usb_uploader.cpp index 5fd3c46..93d3018 100644 --- a/sphaira/source/usb/usb_uploader.cpp +++ b/sphaira/source/usb/usb_uploader.cpp @@ -92,9 +92,7 @@ Result Usb::FileRangeCmd(u64 data_size) { s64 end_off = header.size; s64 read_size = header.size; - // use transfer buffer directly to avoid copy overhead. - auto& buf = m_usb->GetTransferBuffer(); - buf.resize(header.size); + m_buf.resize(header.size); while (curr_off < end_off) { if (curr_off + read_size >= end_off) { @@ -102,8 +100,8 @@ Result Usb::FileRangeCmd(u64 data_size) { } u64 bytes_read; - R_TRY(Read(path, buf.data(), header.offset + curr_off, read_size, &bytes_read)); - R_TRY(m_usb->TransferAll(false, buf.data(), bytes_read)); + R_TRY(Read(path, m_buf.data(), header.offset + curr_off, read_size, &bytes_read)); + R_TRY(m_usb->TransferAll(false, m_buf.data(), bytes_read)); curr_off += bytes_read; } diff --git a/sphaira/source/usb/usbds.cpp b/sphaira/source/usb/usbds.cpp index ed0cad7..40fe262 100644 --- a/sphaira/source/usb/usbds.cpp +++ b/sphaira/source/usb/usbds.cpp @@ -13,6 +13,33 @@ Result usbDsGetSpeed(UsbDeviceSpeed *out) { return serviceDispatchOut(usbDsGetServiceSession(), hosversionAtLeast(11,0,0) ? 11 : 12, *out); } +auto GetUsbDsStateStr(UsbState state) -> const char* { + switch (state) { + case UsbState_Detached: return "Detached"; + case UsbState_Attached: return "Attached"; + case UsbState_Powered: return "Powered"; + case UsbState_Default: return "Default"; + case UsbState_Address: return "Address"; + case UsbState_Configured: return "Configured"; + case UsbState_Suspended: return "Suspended"; + } + + return "Unknown"; +} + +auto GetUsbDsSpeedStr(UsbDeviceSpeed speed) -> const char* { + // todo: remove this cast when libnx pr is merged. + switch ((u32)speed) { + case UsbDeviceSpeed_None: return "None"; + case UsbDeviceSpeed_Low: return "USB 1.0 Low Speed"; + case UsbDeviceSpeed_Full: return "USB 1.1 Full Speed"; + case UsbDeviceSpeed_High: return "USB 2.0 High Speed"; + case UsbDeviceSpeed_Super: return "USB 3.0 Super Speed"; + } + + return "Unknown"; +} + namespace sphaira::usb { namespace { @@ -210,9 +237,12 @@ Result UsbDs::WaitUntilConfigured(u64 timeout) { eventClear(usbDsGetStateChangeEvent()); // check if we got one of the cancel events. - if (R_SUCCEEDED(rc) && idx == waiters.size() - 1) { - rc = Result_UsbCancelled; - break; + if (R_SUCCEEDED(rc)) { + if (waiters[idx].handle == waiterForUEvent(GetCancelEvent()).handle) { + log_write("got usb cancel event\n"); + rc = Result_UsbCancelled; + break; + } } rc = usbDsGetState(&state); @@ -268,18 +298,19 @@ Result UsbDs::WaitTransferCompletion(UsbSessionEndpoint ep, u64 timeout) { auto rc = waitObjects(&idx, waiters.data(), waiters.size(), timeout); // check if we got one of the cancel events. - if (R_SUCCEEDED(rc) && idx == waiters.size() - 1) { - log_write("got usb cancel event\n"); - rc = Result_UsbCancelled; - } else if (R_SUCCEEDED(rc) && idx == waiters.size() - 2) { - log_write("got usbDsGetStateChangeEvent() event\n"); - m_max_packet_size = 0; - rc = KERNELRESULT(TimedOut); + if (R_SUCCEEDED(rc)) { + if (waiters[idx].handle == waiterForEvent(usbDsGetStateChangeEvent()).handle) { + log_write("got usbDsGetStateChangeEvent() event\n"); + m_max_packet_size = 0; + rc = KERNELRESULT(TimedOut); + } else if (waiters[idx].handle == waiterForUEvent(GetCancelEvent()).handle) { + log_write("got usb cancel event\n"); + rc = Result_UsbCancelled; + } } - if (R_FAILED(rc)) { - R_TRY(usbDsEndpoint_Cancel(m_endpoints[ep])); + usbDsEndpoint_Cancel(m_endpoints[ep]); eventClear(GetCompletionEvent(ep)); eventClear(usbDsGetStateChangeEvent()); } @@ -289,7 +320,7 @@ Result UsbDs::WaitTransferCompletion(UsbSessionEndpoint ep, u64 timeout) { Result UsbDs::TransferAsync(UsbSessionEndpoint ep, void *buffer, u32 remaining, u32 size, u32 *out_urb_id) { if (ep == UsbSessionEndpoint_In) { - if (remaining == size && !(size % (u32)m_max_packet_size)) { + if (size && remaining == size && !(size % (u32)m_max_packet_size)) { log_write("[USBDS] SetZlt(true)\n"); R_TRY(usbDsEndpoint_SetZlt(m_endpoints[ep], true)); } else { diff --git a/sphaira/source/yati/source/usb.cpp b/sphaira/source/yati/source/usb.cpp index 4533332..ceb1b84 100644 --- a/sphaira/source/yati/source/usb.cpp +++ b/sphaira/source/yati/source/usb.cpp @@ -71,7 +71,7 @@ Result Usb::SendFileRangeCmd(u64 off, u64 size, u64 timeout) { R_TRY(SendCmdHeader(tinfoil::USBCmdId::FILE_RANGE, sizeof(fRangeHeader) + fRangeHeader.nspNameLen, timeout)); R_TRY(m_usb->TransferAll(false, &fRangeHeader, sizeof(fRangeHeader), timeout)); - R_TRY(m_usb->TransferAll(false, m_transfer_file_name.data(), m_transfer_file_name.size(), timeout)); + R_TRY(m_usb->TransferAll(false, m_transfer_file_name.data(), fRangeHeader.nspNameLen, timeout)); tinfoil::USBCmdHeader responseHeader; R_TRY(m_usb->TransferAll(true, &responseHeader, sizeof(responseHeader), timeout));