webusb: add support for exporting. usb: block requests with no timeout, using pbox to cancel if the user presses B.
This commit is contained in:
@@ -11,6 +11,7 @@ namespace sphaira::ui {
|
||||
struct ProgressBox;
|
||||
using ProgressBoxCallback = std::function<Result(ProgressBox*)>;
|
||||
using ProgressBoxDoneCallback = std::function<void(Result rc)>;
|
||||
// using CancelCallback = std::function<void()>;
|
||||
|
||||
struct ProgressBox final : Widget {
|
||||
ProgressBox(
|
||||
@@ -39,6 +40,9 @@ struct ProgressBox final : Widget {
|
||||
auto ShouldExit() -> bool;
|
||||
auto ShouldExitResult() -> Result;
|
||||
|
||||
void AddCancelEvent(UEvent* event);
|
||||
void RemoveCancelEvent(const UEvent* event);
|
||||
|
||||
// helper functions
|
||||
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, bool single_threaded = false) -> Result;
|
||||
@@ -82,6 +86,7 @@ private:
|
||||
Thread m_thread{};
|
||||
ThreadData m_thread_data{};
|
||||
ProgressBoxDoneCallback m_done{};
|
||||
std::vector<UEvent*> m_cancel_events{};
|
||||
|
||||
// shared data start.
|
||||
std::string m_action{};
|
||||
|
||||
@@ -24,6 +24,14 @@ struct Usb {
|
||||
// Result OpenFile(u32 index, s64& file_size);
|
||||
Result CloseFile();
|
||||
|
||||
auto GetOpenResult() const {
|
||||
return m_open_result;
|
||||
}
|
||||
|
||||
auto GetCancelEvent() {
|
||||
return m_usb->GetCancelEvent();
|
||||
}
|
||||
|
||||
private:
|
||||
Result SendAndVerify(const void* data, u32 size, u64 timeout, api::ResultPacket* out = nullptr);
|
||||
Result SendAndVerify(const void* data, u32 size, api::ResultPacket* out = nullptr);
|
||||
|
||||
@@ -25,6 +25,14 @@ struct Usb {
|
||||
Result OpenFile(u32 index, s64& file_size);
|
||||
Result CloseFile();
|
||||
|
||||
auto GetOpenResult() const {
|
||||
return m_open_result;
|
||||
}
|
||||
|
||||
auto GetCancelEvent() {
|
||||
return m_usb->GetCancelEvent();
|
||||
}
|
||||
|
||||
private:
|
||||
Result SendAndVerify(const void* data, u32 size, u64 timeout, api::ResultPacket* out = nullptr);
|
||||
Result SendAndVerify(const void* data, u32 size, api::ResultPacket* out = nullptr);
|
||||
|
||||
@@ -29,6 +29,14 @@ struct Usb {
|
||||
|
||||
Result file_transfer_loop();
|
||||
|
||||
auto GetOpenResult() const {
|
||||
return m_open_result;
|
||||
}
|
||||
|
||||
auto GetCancelEvent() {
|
||||
return m_usb->GetCancelEvent();
|
||||
}
|
||||
|
||||
private:
|
||||
Result SendResult(u32 result, u32 arg3 = 0, u32 arg4 = 0);
|
||||
|
||||
|
||||
@@ -43,6 +43,14 @@ struct Usb final : Base {
|
||||
return m_usb->CloseFile();
|
||||
}
|
||||
|
||||
auto GetOpenResult() const {
|
||||
return m_usb->GetOpenResult();
|
||||
}
|
||||
|
||||
auto GetCancelEvent() {
|
||||
return m_usb->GetCancelEvent();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<usb::install::Usb> m_usb{};
|
||||
};
|
||||
|
||||
@@ -165,6 +165,14 @@ struct WriteUsbSource final : WriteSource {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
auto GetOpenResult() const {
|
||||
return m_usb->GetOpenResult();
|
||||
}
|
||||
|
||||
auto GetCancelEvent() {
|
||||
return m_usb->GetCancelEvent();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<usb::dump::Usb> m_usb{};
|
||||
bool m_was_mtp_enabled{};
|
||||
@@ -178,8 +186,8 @@ constexpr DumpLocationEntry DUMP_LOCATIONS[]{
|
||||
};
|
||||
|
||||
struct UsbTest final : usb::upload::Usb, yati::source::Stream {
|
||||
UsbTest(ui::ProgressBox* pbox, BaseSource* source, std::span<const fs::FsPath> paths)
|
||||
: Usb{UINT64_MAX}
|
||||
UsbTest(ui::ProgressBox* pbox, BaseSource* source, std::span<const fs::FsPath> paths, u64 timeout)
|
||||
: Usb{timeout}
|
||||
, m_pbox{pbox}
|
||||
, m_source{source}
|
||||
, m_paths{paths} {
|
||||
@@ -248,6 +256,10 @@ struct UsbTest final : usb::upload::Usb, yati::source::Stream {
|
||||
return m_pull_offset;
|
||||
}
|
||||
|
||||
auto GetOpenResult() const {
|
||||
return Usb::GetOpenResult();
|
||||
}
|
||||
|
||||
private:
|
||||
ui::ProgressBox* m_pbox{};
|
||||
BaseSource* m_source{};
|
||||
@@ -261,7 +273,14 @@ private:
|
||||
};
|
||||
|
||||
Result DumpToUsb(ui::ProgressBox* pbox, BaseSource* source, std::span<const fs::FsPath> paths, const CustomTransfer& custom_transfer) {
|
||||
auto write_source = std::make_unique<WriteUsbSource>(3e+9);
|
||||
// create write source and verify that it opened.
|
||||
constexpr u64 timeout = UINT64_MAX;
|
||||
auto write_source = std::make_unique<WriteUsbSource>(timeout);
|
||||
R_TRY(write_source->GetOpenResult());
|
||||
|
||||
// add cancel event.
|
||||
pbox->AddCancelEvent(write_source->GetCancelEvent());
|
||||
ON_SCOPE_EXIT(pbox->RemoveCancelEvent(write_source->GetCancelEvent()));
|
||||
|
||||
for (const auto& path : paths) {
|
||||
const auto file_size = source->GetSize(path);
|
||||
@@ -273,7 +292,7 @@ Result DumpToUsb(ui::ProgressBox* pbox, BaseSource* source, std::span<const fs::
|
||||
while (true) {
|
||||
R_TRY(pbox->ShouldExitResult());
|
||||
|
||||
const auto rc = write_source->WaitForConnection(path, 3e+9);
|
||||
const auto rc = write_source->WaitForConnection(path, timeout);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
break;
|
||||
}
|
||||
@@ -398,8 +417,14 @@ Result DumpToUsbS2S(ui::ProgressBox* pbox, BaseSource* source, std::span<const f
|
||||
file_list.emplace_back(path);
|
||||
}
|
||||
|
||||
auto usb = std::make_unique<UsbTest>(pbox, source, paths);
|
||||
constexpr u64 timeout = 3e+9;
|
||||
// create usb test instance and verify that it opened.
|
||||
constexpr u64 timeout = UINT64_MAX;
|
||||
auto usb = std::make_unique<UsbTest>(pbox, source, paths, timeout);
|
||||
R_TRY(usb->GetOpenResult());
|
||||
|
||||
// add cancel event.
|
||||
pbox->AddCancelEvent(usb->GetCancelEvent());
|
||||
ON_SCOPE_EXIT(pbox->RemoveCancelEvent(usb->GetCancelEvent()));
|
||||
|
||||
while (!pbox->ShouldExit()) {
|
||||
if (R_SUCCEEDED(usb->IsUsbConnected(timeout))) {
|
||||
|
||||
@@ -97,6 +97,9 @@ void Menu::Update(Controller* controller, TouchInfo* touch) {
|
||||
m_state = State::Progress;
|
||||
log_write("got connection\n");
|
||||
App::Push<ui::ProgressBox>(0, "Installing "_i18n, "", [this](auto pbox) -> Result {
|
||||
pbox->AddCancelEvent(m_usb_source->GetCancelEvent());
|
||||
ON_SCOPE_EXIT(pbox->RemoveCancelEvent(m_usb_source->GetCancelEvent()));
|
||||
|
||||
log_write("inside progress box\n");
|
||||
for (u32 i = 0; i < std::size(m_names); i++) {
|
||||
const auto& file_name = m_names[i];
|
||||
|
||||
@@ -232,79 +232,72 @@ auto ProgressBox::Draw(NVGcontext* vg, Theme* theme) -> void {
|
||||
}
|
||||
|
||||
auto ProgressBox::SetActionName(const std::string& action) -> ProgressBox& {
|
||||
mutexLock(&m_mutex);
|
||||
SCOPED_MUTEX(&m_mutex);
|
||||
m_action = action;
|
||||
mutexUnlock(&m_mutex);
|
||||
Yield();
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto ProgressBox::SetTitle(const std::string& title) -> ProgressBox& {
|
||||
mutexLock(&m_mutex);
|
||||
SCOPED_MUTEX(&m_mutex);
|
||||
m_title = title;
|
||||
mutexUnlock(&m_mutex);
|
||||
Yield();
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto ProgressBox::NewTransfer(const std::string& transfer) -> ProgressBox& {
|
||||
mutexLock(&m_mutex);
|
||||
SCOPED_MUTEX(&m_mutex);
|
||||
m_transfer = transfer;
|
||||
m_size = 0;
|
||||
m_offset = 0;
|
||||
m_last_offset = 0;
|
||||
m_timestamp.Update();
|
||||
mutexUnlock(&m_mutex);
|
||||
Yield();
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto ProgressBox::ResetTranfser() -> ProgressBox& {
|
||||
mutexLock(&m_mutex);
|
||||
SCOPED_MUTEX(&m_mutex);
|
||||
m_size = 0;
|
||||
m_offset = 0;
|
||||
m_last_offset = 0;
|
||||
m_timestamp.Update();
|
||||
mutexUnlock(&m_mutex);
|
||||
Yield();
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto ProgressBox::UpdateTransfer(s64 offset, s64 size) -> ProgressBox& {
|
||||
mutexLock(&m_mutex);
|
||||
SCOPED_MUTEX(&m_mutex);
|
||||
m_size = size;
|
||||
m_offset = offset;
|
||||
mutexUnlock(&m_mutex);
|
||||
Yield();
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto ProgressBox::SetImage(int image) -> ProgressBox& {
|
||||
mutexLock(&m_mutex);
|
||||
SCOPED_MUTEX(&m_mutex);
|
||||
m_image_pending = image;
|
||||
m_is_image_pending = true;
|
||||
mutexUnlock(&m_mutex);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto ProgressBox::SetImageData(std::vector<u8>& data) -> ProgressBox& {
|
||||
mutexLock(&m_mutex);
|
||||
SCOPED_MUTEX(&m_mutex);
|
||||
std::swap(m_image_data, data);
|
||||
mutexUnlock(&m_mutex);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto ProgressBox::SetImageDataConst(std::span<const u8> data) -> ProgressBox& {
|
||||
mutexLock(&m_mutex);
|
||||
SCOPED_MUTEX(&m_mutex);
|
||||
m_image_data.resize(data.size());
|
||||
std::memcpy(m_image_data.data(), data.data(), m_image_data.size());
|
||||
mutexUnlock(&m_mutex);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void ProgressBox::RequestExit() {
|
||||
SCOPED_MUTEX(&m_mutex);
|
||||
m_stop_source.request_stop();
|
||||
ueventSignal(GetCancelEvent());
|
||||
|
||||
// cancel any registered events.
|
||||
for (auto& e : m_cancel_events) {
|
||||
ueventSignal(e);
|
||||
}
|
||||
}
|
||||
|
||||
auto ProgressBox::ShouldExit() -> bool {
|
||||
@@ -318,6 +311,26 @@ auto ProgressBox::ShouldExitResult() -> Result {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void ProgressBox::AddCancelEvent(UEvent* event) {
|
||||
if (!event) {
|
||||
return;
|
||||
}
|
||||
|
||||
SCOPED_MUTEX(&m_mutex);
|
||||
if (std::ranges::find(m_cancel_events, event) == m_cancel_events.end()) {
|
||||
m_cancel_events.emplace_back(event);
|
||||
}
|
||||
}
|
||||
|
||||
void ProgressBox::RemoveCancelEvent(const UEvent* event) {
|
||||
if (!event) {
|
||||
return;
|
||||
}
|
||||
|
||||
SCOPED_MUTEX(&m_mutex);
|
||||
m_cancel_events.erase(std::remove(m_cancel_events.begin(), m_cancel_events.end(), event), m_cancel_events.end());
|
||||
}
|
||||
|
||||
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_both_native = fs_src->IsNative() && fs_dst->IsNative();
|
||||
|
||||
@@ -36,7 +36,7 @@ Base::Base(u64 transfer_timeout) {
|
||||
App::SetAutoSleepDisabled(true);
|
||||
|
||||
m_transfer_timeout = transfer_timeout;
|
||||
ueventCreate(GetCancelEvent(), true);
|
||||
ueventCreate(GetCancelEvent(), false);
|
||||
m_aligned = std::make_unique<u8*>(new(std::align_val_t{TRANSFER_ALIGN}) u8[TRANSFER_MAX]);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ Usb::Usb(u64 transfer_timeout) {
|
||||
Usb::~Usb() {
|
||||
if (m_was_connected && R_SUCCEEDED(m_usb->IsUsbConnected(0))) {
|
||||
const auto send_header = SendPacket::Build(CMD_QUIT);
|
||||
SendAndVerify(&send_header, sizeof(send_header));
|
||||
SendAndVerify(&send_header, sizeof(send_header), 1e+9);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ Usb::Usb(u64 transfer_timeout) {
|
||||
Usb::~Usb() {
|
||||
if (m_was_connected && R_SUCCEEDED(m_usb->IsUsbConnected(0))) {
|
||||
const auto send_header = SendPacket::Build(CMD_QUIT);
|
||||
SendAndVerify(&send_header, sizeof(send_header));
|
||||
SendAndVerify(&send_header, sizeof(send_header), 1e+9);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user