send file name and size via usb, add requirements.txt for usb.py
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
#include "yati/source/base.hpp"
|
#include "yati/source/base.hpp"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
|
|
||||||
namespace sphaira::yati::container {
|
namespace sphaira::yati::container {
|
||||||
@@ -28,12 +29,15 @@ using Collections = std::vector<CollectionEntry>;
|
|||||||
struct Base {
|
struct Base {
|
||||||
using Source = source::Base;
|
using Source = source::Base;
|
||||||
|
|
||||||
Base(Source* source) : m_source{source} { }
|
Base(std::shared_ptr<Source> source) : m_source{source} { }
|
||||||
virtual ~Base() = default;
|
virtual ~Base() = default;
|
||||||
virtual Result GetCollections(Collections& out) = 0;
|
virtual Result GetCollections(Collections& out) = 0;
|
||||||
|
auto GetSource() const {
|
||||||
|
return m_source;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Source* m_source;
|
std::shared_ptr<Source> m_source;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace sphaira::yati::container
|
} // namespace sphaira::yati::container
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
namespace sphaira::yati::source {
|
namespace sphaira::yati::source {
|
||||||
|
|
||||||
struct Stdio final : Base {
|
struct Stdio final : Base {
|
||||||
Stdio(const char* path);
|
Stdio(const fs::FsPath& path);
|
||||||
~Stdio();
|
~Stdio();
|
||||||
|
|
||||||
Result Read(void* buf, s64 off, s64 size, u64* bytes_read) override;
|
Result Read(void* buf, s64 off, s64 size, u64* bytes_read) override;
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ struct Usb final : Base {
|
|||||||
|
|
||||||
Result Init();
|
Result Init();
|
||||||
Result WaitForConnection(u64 timeout, u32& speed, u32& count);
|
Result WaitForConnection(u64 timeout, u32& speed, u32& count);
|
||||||
|
Result GetFileInfo(std::string& name_out, u64& size_out);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum UsbSessionEndpoint {
|
enum UsbSessionEndpoint {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include "fs.hpp"
|
#include "fs.hpp"
|
||||||
#include "source/base.hpp"
|
#include "source/base.hpp"
|
||||||
|
#include "container/base.hpp"
|
||||||
#include "ui/progress_box.hpp"
|
#include "ui/progress_box.hpp"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
@@ -116,12 +117,10 @@ struct Config {
|
|||||||
bool lower_system_version{};
|
bool lower_system_version{};
|
||||||
};
|
};
|
||||||
|
|
||||||
Result InstallFromFile(FsFileSystem* fs, const fs::FsPath& path);
|
|
||||||
Result InstallFromStdioFile(const char* path);
|
|
||||||
Result InstallFromSource(std::shared_ptr<source::Base> source);
|
|
||||||
|
|
||||||
Result InstallFromFile(ui::ProgressBox* pbox, FsFileSystem* fs, const fs::FsPath& path);
|
Result InstallFromFile(ui::ProgressBox* pbox, FsFileSystem* fs, const fs::FsPath& path);
|
||||||
Result InstallFromStdioFile(ui::ProgressBox* pbox, const char* path);
|
Result InstallFromStdioFile(ui::ProgressBox* pbox, const fs::FsPath& path);
|
||||||
Result InstallFromSource(ui::ProgressBox* pbox, std::shared_ptr<source::Base> source);
|
Result InstallFromSource(ui::ProgressBox* pbox, std::shared_ptr<source::Base> source, const fs::FsPath& path);
|
||||||
|
Result InstallFromContainer(ui::ProgressBox* pbox, std::shared_ptr<container::Base> container);
|
||||||
|
Result InstallFromCollections(ui::ProgressBox* pbox, std::shared_ptr<source::Base> source, const container::Collections& collections);
|
||||||
|
|
||||||
} // namespace sphaira::yati
|
} // namespace sphaira::yati
|
||||||
|
|||||||
@@ -102,7 +102,15 @@ void Menu::Update(Controller* controller, TouchInfo* touch) {
|
|||||||
App::Push(std::make_shared<ui::ProgressBox>("Installing App"_i18n, [this](auto pbox) mutable -> bool {
|
App::Push(std::make_shared<ui::ProgressBox>("Installing App"_i18n, [this](auto pbox) mutable -> bool {
|
||||||
log_write("inside progress box\n");
|
log_write("inside progress box\n");
|
||||||
for (u32 i = 0; i < m_usb_count; i++) {
|
for (u32 i = 0; i < m_usb_count; i++) {
|
||||||
const auto rc = yati::InstallFromSource(pbox, m_usb_source);
|
std::string file_name;
|
||||||
|
u64 file_size;
|
||||||
|
if (R_FAILED(m_usb_source->GetFileInfo(file_name, file_size))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_write("got file name: %s size: %lX\n", file_name.c_str(), file_size);
|
||||||
|
|
||||||
|
const auto rc = yati::InstallFromSource(pbox, m_usb_source, file_name);
|
||||||
if (R_FAILED(rc)) {
|
if (R_FAILED(rc)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,12 +70,12 @@ Result Xci::Validate(source::Base* source) {
|
|||||||
|
|
||||||
Result Xci::GetCollections(Collections& out) {
|
Result Xci::GetCollections(Collections& out) {
|
||||||
Hfs0 root{};
|
Hfs0 root{};
|
||||||
R_TRY(Hfs0GetPartition(m_source, HFS0_HEADER_OFFSET, root));
|
R_TRY(Hfs0GetPartition(m_source.get(), HFS0_HEADER_OFFSET, root));
|
||||||
|
|
||||||
for (u32 i = 0; i < root.header.total_files; i++) {
|
for (u32 i = 0; i < root.header.total_files; i++) {
|
||||||
if (root.string_table[i] == "secure") {
|
if (root.string_table[i] == "secure") {
|
||||||
Hfs0 secure{};
|
Hfs0 secure{};
|
||||||
R_TRY(Hfs0GetPartition(m_source, root.data_offset + root.file_table[i].data_offset, secure));
|
R_TRY(Hfs0GetPartition(m_source.get(), root.data_offset + root.file_table[i].data_offset, secure));
|
||||||
|
|
||||||
for (u32 i = 0; i < secure.header.total_files; i++) {
|
for (u32 i = 0; i < secure.header.total_files; i++) {
|
||||||
CollectionEntry entry;
|
CollectionEntry entry;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace sphaira::yati::source {
|
namespace sphaira::yati::source {
|
||||||
|
|
||||||
Stdio::Stdio(const char* path) {
|
Stdio::Stdio(const fs::FsPath& path) {
|
||||||
m_file = std::fopen(path, "rb");
|
m_file = std::fopen(path, "rb");
|
||||||
if (!m_file) {
|
if (!m_file) {
|
||||||
m_open_result = fsdevGetLastResult();
|
m_open_result = fsdevGetLastResult();
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace sphaira::yati::source {
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr u32 MAGIC = 0x53504841;
|
constexpr u32 MAGIC = 0x53504841;
|
||||||
constexpr u32 VERSION = 1;
|
constexpr u32 VERSION = 2;
|
||||||
|
|
||||||
struct SendHeader {
|
struct SendHeader {
|
||||||
u32 magic;
|
u32 magic;
|
||||||
@@ -224,6 +224,28 @@ Result Usb::WaitForConnection(u64 timeout, u32& speed, u32& count) {
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result Usb::GetFileInfo(std::string& name_out, u64& size_out) {
|
||||||
|
struct {
|
||||||
|
u64 size;
|
||||||
|
u64 name_length;
|
||||||
|
} file_info_meta;
|
||||||
|
|
||||||
|
alignas(0x1000) u8 aligned[0x1000]{};
|
||||||
|
|
||||||
|
// receive meta.
|
||||||
|
u32 transferredSize;
|
||||||
|
R_TRY(TransferPacketImpl(true, aligned, sizeof(file_info_meta), &transferredSize, m_transfer_timeout));
|
||||||
|
std::memcpy(&file_info_meta, aligned, sizeof(file_info_meta));
|
||||||
|
R_UNLESS(file_info_meta.name_length < sizeof(aligned), 0x1);
|
||||||
|
|
||||||
|
R_TRY(TransferPacketImpl(true, aligned, file_info_meta.name_length, &transferredSize, m_transfer_timeout));
|
||||||
|
name_out.resize(file_info_meta.name_length);
|
||||||
|
std::memcpy(name_out.data(), aligned, name_out.size());
|
||||||
|
|
||||||
|
size_out = file_info_meta.size;
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
bool Usb::GetConfigured() const {
|
bool Usb::GetConfigured() const {
|
||||||
UsbState usb_state;
|
UsbState usb_state;
|
||||||
usbDsGetState(std::addressof(usb_state));
|
usbDsGetState(std::addressof(usb_state));
|
||||||
|
|||||||
@@ -284,7 +284,7 @@ struct Yati {
|
|||||||
Yati(ui::ProgressBox*, std::shared_ptr<source::Base>);
|
Yati(ui::ProgressBox*, std::shared_ptr<source::Base>);
|
||||||
~Yati();
|
~Yati();
|
||||||
|
|
||||||
Result Setup(container::Collections& out);
|
Result Setup();
|
||||||
Result InstallNca(std::span<TikCollection> tickets, NcaCollection& nca);
|
Result InstallNca(std::span<TikCollection> tickets, NcaCollection& nca);
|
||||||
Result InstallCnmtNca(std::span<TikCollection> tickets, CnmtCollection& cnmt, const container::Collections& collections);
|
Result InstallCnmtNca(std::span<TikCollection> tickets, CnmtCollection& cnmt, const container::Collections& collections);
|
||||||
Result InstallControlNca(std::span<TikCollection> tickets, const CnmtCollection& cnmt, NcaCollection& nca);
|
Result InstallControlNca(std::span<TikCollection> tickets, const CnmtCollection& cnmt, NcaCollection& nca);
|
||||||
@@ -778,7 +778,7 @@ Yati::~Yati() {
|
|||||||
appletSetMediaPlaybackState(false);
|
appletSetMediaPlaybackState(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Yati::Setup(container::Collections& out) {
|
Result Yati::Setup() {
|
||||||
config.sd_card_install = App::GetApp()->m_install_sd.Get();
|
config.sd_card_install = App::GetApp()->m_install_sd.Get();
|
||||||
config.allow_downgrade = App::GetApp()->m_allow_downgrade.Get();
|
config.allow_downgrade = App::GetApp()->m_allow_downgrade.Get();
|
||||||
config.skip_if_already_installed = App::GetApp()->m_skip_if_already_installed.Get();
|
config.skip_if_already_installed = App::GetApp()->m_skip_if_already_installed.Get();
|
||||||
@@ -812,19 +812,6 @@ Result Yati::Setup(container::Collections& out) {
|
|||||||
cs = ncm_cs[config.sd_card_install];
|
cs = ncm_cs[config.sd_card_install];
|
||||||
db = ncm_db[config.sd_card_install];
|
db = ncm_db[config.sd_card_install];
|
||||||
|
|
||||||
if (R_SUCCEEDED(container::Nsp::Validate(source.get()))) {
|
|
||||||
log_write("found nsp\n");
|
|
||||||
container = std::make_unique<container::Nsp>(source.get());
|
|
||||||
} else if (R_SUCCEEDED(container::Xci::Validate(source.get()))) {
|
|
||||||
log_write("found xci\n");
|
|
||||||
container = std::make_unique<container::Xci>(source.get());
|
|
||||||
} else {
|
|
||||||
log_write("found unknown container\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
R_UNLESS(container, Result_ContainerNotFound);
|
|
||||||
R_TRY(container->GetCollections(out));
|
|
||||||
|
|
||||||
R_TRY(parse_keys(keys, true));
|
R_TRY(parse_keys(keys, true));
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
@@ -1040,11 +1027,9 @@ Result Yati::InstallControlNca(std::span<TikCollection> tickets, const CnmtColle
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result InstallInternal(ui::ProgressBox* pbox, std::shared_ptr<source::Base> source) {
|
Result InstallInternal(ui::ProgressBox* pbox, std::shared_ptr<source::Base> source, const container::Collections& collections) {
|
||||||
auto yati = std::make_unique<Yati>(pbox, source);
|
auto yati = std::make_unique<Yati>(pbox, source);
|
||||||
|
R_TRY(yati->Setup());
|
||||||
container::Collections collections{};
|
|
||||||
R_TRY(yati->Setup(collections));
|
|
||||||
|
|
||||||
std::vector<TikCollection> tickets{};
|
std::vector<TikCollection> tickets{};
|
||||||
for (const auto& collection : collections) {
|
for (const auto& collection : collections) {
|
||||||
@@ -1287,31 +1272,35 @@ Result InstallInternal(ui::ProgressBox* pbox, std::shared_ptr<source::Base> sour
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Result InstallFromFile(FsFileSystem* fs, const fs::FsPath& path) {
|
|
||||||
return InstallFromSource(std::make_shared<source::File>(fs, path));
|
|
||||||
}
|
|
||||||
|
|
||||||
Result InstallFromStdioFile(const char* path) {
|
|
||||||
return InstallFromSource(std::make_shared<source::Stdio>(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
Result InstallFromSource(std::shared_ptr<source::Base> source) {
|
|
||||||
App::Push(std::make_shared<ui::ProgressBox>("Installing App"_i18n, [source](auto pbox) mutable -> bool {
|
|
||||||
return R_SUCCEEDED(InstallFromSource(pbox, source));
|
|
||||||
}));
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result InstallFromFile(ui::ProgressBox* pbox, FsFileSystem* fs, const fs::FsPath& path) {
|
Result InstallFromFile(ui::ProgressBox* pbox, FsFileSystem* fs, const fs::FsPath& path) {
|
||||||
return InstallFromSource(pbox, std::make_shared<source::File>(fs, path));
|
return InstallFromSource(pbox, std::make_shared<source::File>(fs, path), path);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result InstallFromStdioFile(ui::ProgressBox* pbox, const char* path) {
|
Result InstallFromStdioFile(ui::ProgressBox* pbox, const fs::FsPath& path) {
|
||||||
return InstallFromSource(pbox, std::make_shared<source::Stdio>(path));
|
return InstallFromSource(pbox, std::make_shared<source::Stdio>(path), path);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result InstallFromSource(ui::ProgressBox* pbox, std::shared_ptr<source::Base> source) {
|
Result InstallFromSource(ui::ProgressBox* pbox, std::shared_ptr<source::Base> source, const fs::FsPath& path) {
|
||||||
return InstallInternal(pbox, source);
|
if (R_SUCCEEDED(container::Nsp::Validate(source.get()))) {
|
||||||
|
log_write("found nsp\n");
|
||||||
|
return InstallFromContainer(pbox, std::make_unique<container::Nsp>(source));
|
||||||
|
} else if (R_SUCCEEDED(container::Xci::Validate(source.get()))) {
|
||||||
|
log_write("found xci\n");
|
||||||
|
return InstallFromContainer(pbox, std::make_unique<container::Xci>(source));
|
||||||
|
} else {
|
||||||
|
log_write("found unknown container\n");
|
||||||
|
R_THROW(Result_ContainerNotFound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InstallFromContainer(ui::ProgressBox* pbox, std::shared_ptr<container::Base> container) {
|
||||||
|
container::Collections collections;
|
||||||
|
R_TRY(container->GetCollections(collections));
|
||||||
|
return InstallFromCollections(pbox, container->GetSource(), collections);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InstallFromCollections(ui::ProgressBox* pbox, std::shared_ptr<source::Base> source, const container::Collections& collections) {
|
||||||
|
return InstallInternal(pbox, source, collections);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace sphaira::yati
|
} // namespace sphaira::yati
|
||||||
|
|||||||
1
tools/requirements.txt
Normal file
1
tools/requirements.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pyusb
|
||||||
@@ -6,11 +6,12 @@ import usb.core
|
|||||||
import usb.util
|
import usb.util
|
||||||
import time
|
import time
|
||||||
import glob
|
import glob
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
# magic number (SPHA) for the script and switch.
|
# magic number (SPHA) for the script and switch.
|
||||||
MAGIC = 0x53504841
|
MAGIC = 0x53504841
|
||||||
# version of the usb script.
|
# version of the usb script.
|
||||||
VERSION = 1
|
VERSION = 2
|
||||||
# list of supported extensions.
|
# list of supported extensions.
|
||||||
EXTS = (".nsp", ".xci", ".nsz", ".xcz")
|
EXTS = (".nsp", ".xci", ".nsz", ".xcz")
|
||||||
|
|
||||||
@@ -28,6 +29,15 @@ def verify_switch(bcdUSB, count, in_ep, out_ep):
|
|||||||
send_data = struct.pack('<IIII', MAGIC, VERSION, bcdUSB, count)
|
send_data = struct.pack('<IIII', MAGIC, VERSION, bcdUSB, count)
|
||||||
out_ep.write(data=send_data, timeout=0)
|
out_ep.write(data=send_data, timeout=0)
|
||||||
|
|
||||||
|
def send_file_info(path, in_ep, out_ep):
|
||||||
|
file_name = Path(path).name
|
||||||
|
file_size = Path(path).stat().st_size
|
||||||
|
file_name_len = len(file_name)
|
||||||
|
|
||||||
|
send_data = struct.pack('<QQ', file_size, file_name_len)
|
||||||
|
out_ep.write(data=send_data, timeout=0)
|
||||||
|
out_ep.write(data=file_name, timeout=0)
|
||||||
|
|
||||||
def wait_for_input(path, in_ep, out_ep):
|
def wait_for_input(path, in_ep, out_ep):
|
||||||
buf = None
|
buf = None
|
||||||
predicted_off = 0
|
predicted_off = 0
|
||||||
@@ -124,6 +134,7 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
for file in files:
|
for file in files:
|
||||||
print("installing file: {}".format(file))
|
print("installing file: {}".format(file))
|
||||||
|
send_file_info(file, in_ep, out_ep)
|
||||||
wait_for_input(file, in_ep, out_ep)
|
wait_for_input(file, in_ep, out_ep)
|
||||||
dev.reset()
|
dev.reset()
|
||||||
except Exception as inst:
|
except Exception as inst:
|
||||||
|
|||||||
Reference in New Issue
Block a user