simplify title cache loading.

This commit is contained in:
ITotalJustice
2025-06-21 15:32:55 +01:00
parent 4d27bf5492
commit 1f7179e941
9 changed files with 714 additions and 1015 deletions

View File

@@ -14,6 +14,7 @@
#include "download.hpp"
#include "dumper.hpp"
#include "image.hpp"
#include "title_info.hpp"
#include <cstring>
#include <algorithm>
@@ -62,46 +63,6 @@ auto GetXciSizeFromRomSize(u8 rom_size) -> s64 {
return 0;
}
// taken from nxdumptool.
void utilsReplaceIllegalCharacters(char *str, bool ascii_only)
{
static const char g_illegalFileSystemChars[] = "\\/:*?\"<>|";
size_t str_size = 0, cur_pos = 0;
if (!str || !(str_size = strlen(str))) return;
u8 *ptr1 = (u8*)str, *ptr2 = ptr1;
ssize_t units = 0;
u32 code = 0;
bool repl = false;
while(cur_pos < str_size)
{
units = decode_utf8(&code, ptr1);
if (units < 0) break;
if (code < 0x20 || (!ascii_only && code == 0x7F) || (ascii_only && code >= 0x7F) || \
(units == 1 && memchr(g_illegalFileSystemChars, (int)code, std::size(g_illegalFileSystemChars))))
{
if (!repl)
{
*ptr2++ = '_';
repl = true;
}
} else {
if (ptr2 != ptr1) memmove(ptr2, ptr1, (size_t)units);
ptr2 += units;
repl = false;
}
ptr1 += units;
cur_pos += (size_t)units;
}
*ptr2 = '\0';
}
struct DebugEventInfo {
u32 event_type;
u32 flags;
@@ -132,7 +93,7 @@ auto GetDumpTypeStr(u8 type) -> const char* {
auto BuildXciName(const ApplicationEntry& e) -> fs::FsPath {
fs::FsPath name_buf = e.lang_entry.name;
utilsReplaceIllegalCharacters(name_buf, true);
title::utilsReplaceIllegalCharacters(name_buf, true);
fs::FsPath path;
std::snprintf(path, sizeof(path), "%s [%016lX][v%u]", name_buf.s, e.app_id, e.version);
@@ -475,18 +436,18 @@ Menu::Menu(u32 flags) : MenuBase{"GameCard"_i18n, flags} {
const Vec2 pad{0, 125 - v.h};
m_list = std::make_unique<List>(1, 3, m_pos, v, pad);
nsInitialize();
fsOpenDeviceOperator(std::addressof(m_dev_op));
fsOpenGameCardDetectionEventNotifier(std::addressof(m_event_notifier));
fsEventNotifierGetEventHandle(std::addressof(m_event_notifier), std::addressof(m_event), true);
title::Init();
}
Menu::~Menu() {
title::Exit();
GcUnmount();
eventClose(std::addressof(m_event));
fsEventNotifierClose(std::addressof(m_event_notifier));
fsDeviceOperatorClose(std::addressof(m_dev_op));
nsExit();
}
void Menu::Update(Controller* controller, TouchInfo* touch) {
@@ -683,13 +644,6 @@ Result Menu::GcMount() {
// load all control data, icons are loaded when displayed.
for (auto& e : m_entries) {
R_TRY(LoadControlData(e));
NacpLanguageEntry* lang_entry{};
R_TRY(nacpGetLanguageEntry(&e.control->nacp, &lang_entry));
if (lang_entry) {
e.lang_entry = *lang_entry;
}
}
if (m_entries.size() > 1) {
@@ -909,47 +863,13 @@ void Menu::FreeImage() {
}
Result Menu::LoadControlData(ApplicationEntry& e) {
const auto id = e.app_id;
e.control = std::make_unique<NsApplicationControlData>();
const auto data = title::LoadControlEntry(e.app_id);
R_UNLESS(data.status == title::NacpLoadStatus::Loaded, 0x1);
if (hosversionBefore(20,0,0)) {
TimeStamp ts;
if (R_SUCCEEDED(nsGetApplicationControlData(NsApplicationControlSource_CacheOnly, id, e.control.get(), sizeof(NsApplicationControlData), &e.control_size))) {
log_write("\t\t[ns control cache] time taken: %.2fs %zums\n", ts.GetSecondsD(), ts.GetMs());
R_SUCCEED();
}
}
// nsGetApplicationControlData() will fail if it's the first time
// mounting a gamecard if the image is not already cached.
// waiting 1-2s after mount, then calling seems to work.
// however, we can just manually parse the nca to get the data we need,
// which always works and *is* faster too ;)
for (const auto& app : e.application) {
for (const auto& collection : app) {
if (collection.type == NcmContentType_Control) {
const auto path = BuildGcPath(collection.name.c_str(), &m_handle);
u64 program_id = id | collection.id_offset;
if (hosversionAtLeast(17, 0, 0)) {
fsGetProgramId(&program_id, path, FsContentAttributes_All);
}
TimeStamp ts;
std::vector<u8> icon;
if (R_SUCCEEDED(nca::ParseControl(path, program_id, &e.control->nacp, sizeof(e.control->nacp), &icon))) {
std::memcpy(e.control->icon, icon.data(), icon.size());
e.control_size = sizeof(e.control->nacp) + icon.size();
log_write("\t\tnca::ParseControl(): %.2fs %zums\n", ts.GetSecondsD(), ts.GetMs());
R_SUCCEED();
} else {
log_write("\tFAILED to parse control nca %s\n", path.s);
}
}
}
}
return 0x1;
e.control = data.control;
e.jpeg_size = data.jpeg_size;
e.lang_entry = data.lang;
R_SUCCEED();
}
void Menu::OnChangeIndex(s64 new_index) {
@@ -963,7 +883,7 @@ void Menu::OnChangeIndex(s64 new_index) {
this->SetSubHeading(std::to_string(index) + " / " + std::to_string(m_entries.size()));
const auto& e = m_entries[m_entry_index];
const auto jpeg_size = e.control_size - sizeof(NacpStruct);
const auto jpeg_size = e.jpeg_size;
TimeStamp ts;
const auto image = ImageLoadFromMemory({e.control->icon, jpeg_size}, ImageFlag_JPEG);