add game dumping, add game transfer (switch2switch) via usb, add multi game selecting, fix bugs (see below).

- added more es commands.
- fixed usb install potential hang if the exit command is sent, but the client stops responding (timeout is now 3s).
- added multi select to the games menu.
- added game dumping.
- added switch2switch support by having a switch act as a usb client to transfer games.
- replace std::find with std::ranges (in a few places).
- fix rounding of icon in progress box being too round.
- fix file copy helper in progress box not updating the progress bar.
This commit is contained in:
ITotalJustice
2025-05-18 13:46:10 +01:00
parent 544272925d
commit bd7eadc6a0
24 changed files with 2018 additions and 485 deletions

View File

@@ -275,7 +275,6 @@ struct Yati {
NcmContentMetaDatabase db{};
NcmStorageId storage_id{};
Service es{};
Service ns_app{};
std::unique_ptr<container::Base> container{};
Config config{};
@@ -452,7 +451,7 @@ Result Yati::decompressFuncInternal(ThreadData* t) {
for (s64 off = 0; off < size;) {
// log_write("looking for section\n");
if (!ncz_section || !ncz_section->InRange(written)) {
auto it = std::find_if(t->ncz_sections.cbegin(), t->ncz_sections.cend(), [written](auto& e){
auto it = std::ranges::find_if(t->ncz_sections, [written](auto& e){
return e.InRange(written);
});
@@ -504,6 +503,8 @@ Result Yati::decompressFuncInternal(ThreadData* t) {
if (!is_ncz || !decompress_buf_off) {
// check nca header
if (!decompress_buf_off) {
log_write("reading nca header\n");
nca::Header header{};
crypto::cryptoAes128Xts(buf.data(), std::addressof(header), keys.header_key, 0, 0x200, sizeof(header), false);
log_write("verifying nca header magic\n");
@@ -522,6 +523,7 @@ Result Yati::decompressFuncInternal(ThreadData* t) {
}
t->write_size = header.size;
log_write("setting placeholder size: %zu\n", header.size);
R_TRY(ncmContentStorageSetPlaceHolderSize(std::addressof(cs), std::addressof(t->nca->placeholder_id), header.size));
if (!config.ignore_distribution_bit && header.distribution_type == nca::DistributionType_GameCard) {
@@ -531,11 +533,13 @@ Result Yati::decompressFuncInternal(ThreadData* t) {
TikCollection* ticket = nullptr;
if (isRightsIdValid(header.rights_id)) {
auto it = std::find_if(t->tik.begin(), t->tik.end(), [&header](auto& e){
auto it = std::ranges::find_if(t->tik, [&header](auto& e){
return !std::memcmp(&header.rights_id, &e.rights_id, sizeof(e.rights_id));
});
log_write("looking for ticket %s\n", hexIdToStr(header.rights_id).str);
R_UNLESS(it != t->tik.end(), Result_TicketNotFound);
log_write("ticket found\n");
it->required = true;
ticket = &(*it);
}
@@ -607,7 +611,7 @@ Result Yati::decompressFuncInternal(ThreadData* t) {
// todo: blocks need to use read offset, as the offset + size is compressed range.
if (t->ncz_blocks.size()) {
if (!ncz_block || !ncz_block->InRange(decompress_buf_off)) {
auto it = std::find_if(t->ncz_blocks.cbegin(), t->ncz_blocks.cend(), [decompress_buf_off](auto& e){
auto it = std::ranges::find_if(t->ncz_blocks, [decompress_buf_off](auto& e){
return e.InRange(decompress_buf_off);
});
@@ -757,13 +761,13 @@ Yati::~Yati() {
splCryptoExit();
serviceClose(std::addressof(ns_app));
nsExit();
es::Exit();
for (size_t i = 0; i < std::size(NCM_STORAGE_IDS); i++) {
ncmContentMetaDatabaseClose(std::addressof(ncm_db[i]));
ncmContentStorageClose(std::addressof(ncm_cs[i]));
}
serviceClose(std::addressof(es));
appletSetMediaPlaybackState(false);
if (config.boost_mode) {
@@ -799,7 +803,7 @@ Result Yati::Setup(const ConfigOverride& override) {
R_TRY(splCryptoInitialize());
R_TRY(nsInitialize());
R_TRY(nsGetApplicationManagerInterface(std::addressof(ns_app)));
R_TRY(smGetService(std::addressof(es), "es"));
R_TRY(es::Initialize());
for (size_t i = 0; i < std::size(NCM_STORAGE_IDS); i++) {
R_TRY(ncmOpenContentMetaDatabase(std::addressof(ncm_db[i]), NCM_STORAGE_IDS[i]));
@@ -960,7 +964,7 @@ Result Yati::InstallCnmtNca(std::span<TikCollection> tickets, CnmtCollection& cn
}
const auto str = hexIdToStr(info.content_id);
const auto it = std::find_if(collections.cbegin(), collections.cend(), [&str](auto& e){
const auto it = std::ranges::find_if(collections, [&str](auto& e){
return e.name.find(str.str) != e.name.npos;
});
@@ -1004,7 +1008,7 @@ Result Yati::InstallCnmtNca(std::span<TikCollection> tickets, CnmtCollection& cn
return lhs.type > rhs.type;
};
std::sort(cnmt.ncas.begin(), cnmt.ncas.end(), sorter);
std::ranges::sort(cnmt.ncas, sorter);
log_write("found all cnmts\n");
R_SUCCEED();
@@ -1017,7 +1021,7 @@ Result Yati::ParseTicketsIntoCollection(std::vector<TikCollection>& tickets, con
keys::parse_hex_key(entry.rights_id.c, collection.name.c_str());
const auto str = collection.name.substr(0, collection.name.length() - 4) + ".cert";
const auto cert = std::find_if(collections.cbegin(), collections.cend(), [&str](auto& e){
const auto cert = std::ranges::find_if(collections, [&str](auto& e){
return e.name.find(str) != e.name.npos;
});
@@ -1117,7 +1121,7 @@ Result Yati::ImportTickets(std::span<TikCollection> tickets) {
log_write("patching ticket\n");
R_TRY(es::PatchTicket(ticket.ticket, keys));
log_write("installing ticket\n");
R_TRY(es::ImportTicket(std::addressof(es), ticket.ticket.data(), ticket.ticket.size(), ticket.cert.data(), ticket.cert.size()));
R_TRY(es::ImportTicket(ticket.ticket.data(), ticket.ticket.size(), ticket.cert.data(), ticket.cert.size()));
ticket.required = false;
}
}
@@ -1317,7 +1321,7 @@ Result InstallInternalStream(ui::ProgressBox* pbox, std::shared_ptr<source::Base
return lhs.offset < rhs.offset;
};
std::sort(collections.begin(), collections.end(), sorter);
std::ranges::sort(collections, sorter);
for (const auto& collection : collections) {
if (collection.name.ends_with(".nca") || collection.name.ends_with(".ncz")) {
@@ -1334,7 +1338,7 @@ Result InstallInternalStream(ui::ProgressBox* pbox, std::shared_ptr<source::Base
keys::parse_hex_key(rights_id.c, collection.name.c_str());
const auto str = collection.name.substr(0, collection.name.length() - 4) + ".cert";
auto entry = std::find_if(tickets.begin(), tickets.end(), [&rights_id](auto& e){
auto entry = std::ranges::find_if(tickets, [&rights_id](auto& e){
return !std::memcmp(&rights_id, &e.rights_id, sizeof(rights_id));
});
@@ -1353,7 +1357,7 @@ Result InstallInternalStream(ui::ProgressBox* pbox, std::shared_ptr<source::Base
for (auto& cnmt : cnmts) {
// copy nca structs into cnmt.
for (auto& cnmt_nca : cnmt.ncas) {
auto it = std::find_if(ncas.cbegin(), ncas.cend(), [&cnmt_nca](auto& e){
auto it = std::ranges::find_if(ncas, [&cnmt_nca](auto& e){
return e.name == cnmt_nca.name;
});