optimise game menu for fw 20

- loading the control data is ran on its own thread, it does not block the main thread. allows for smooth scrolling like nintendos home menu.
- on fw20+, sphaira manually parses the control data, rather than using ns. manually parsing takes 20-40ms, which is faster than ms which can take 50-500ms.
- on fw19 and below, if the control data is not in ns cache, sphaira will manually parse the data as its twice as fast as ns. You can see how fast this is by loading the gamecard menu as that manually parses everything, and it loads the gamecard faster than the home menu
This commit is contained in:
ITotalJustice
2025-05-13 23:51:06 +01:00
parent b98ccb927e
commit 97d3fd396e
7 changed files with 363 additions and 124 deletions

View File

@@ -151,6 +151,74 @@ Result VerifyFixedKey(const Header& header) {
R_SUCCEED();
}
Result ParseCnmt(const fs::FsPath& path, u64 program_id, ncm::PackagedContentMeta& header, std::vector<u8>& extended_header, std::vector<NcmPackagedContentInfo>& infos) {
FsFileSystem fs;
R_TRY(fsOpenFileSystemWithId(std::addressof(fs), program_id, FsFileSystemType_ContentMeta, path, FsContentAttributes_All));
ON_SCOPE_EXIT(fsFsClose(std::addressof(fs)));
FsDir dir;
R_TRY(fsFsOpenDirectory(std::addressof(fs), fs::FsPath{"/"}, FsDirOpenMode_ReadFiles, std::addressof(dir)));
ON_SCOPE_EXIT(fsDirClose(std::addressof(dir)));
s64 total_entries;
FsDirectoryEntry buf;
R_TRY(fsDirRead(std::addressof(dir), std::addressof(total_entries), 1, std::addressof(buf)));
FsFile file;
R_TRY(fsFsOpenFile(std::addressof(fs), fs::AppendPath("/", buf.name), FsOpenMode_Read, std::addressof(file)));
ON_SCOPE_EXIT(fsFileClose(std::addressof(file)));
s64 offset{};
u64 bytes_read;
R_TRY(fsFileRead(std::addressof(file), offset, std::addressof(header), sizeof(header), 0, std::addressof(bytes_read)));
offset += bytes_read;
// read extended header
extended_header.resize(header.meta_header.extended_header_size);
R_TRY(fsFileRead(std::addressof(file), offset, extended_header.data(), extended_header.size(), 0, std::addressof(bytes_read)));
offset += bytes_read;
// read infos.
infos.resize(header.meta_header.content_count);
R_TRY(fsFileRead(std::addressof(file), offset, infos.data(), infos.size() * sizeof(NcmPackagedContentInfo), 0, std::addressof(bytes_read)));
offset += bytes_read;
R_SUCCEED();
}
Result ParseControl(const fs::FsPath& path, u64 program_id, void* nacp_out, s64 nacp_size, std::vector<u8>* icon_out) {
FsFileSystem fs;
R_TRY(fsOpenFileSystemWithId(std::addressof(fs), program_id, FsFileSystemType_ContentControl, path, FsContentAttributes_All));
ON_SCOPE_EXIT(fsFsClose(std::addressof(fs)));
// read nacp.
if (nacp_out) {
FsFile file;
R_TRY(fsFsOpenFile(std::addressof(fs), fs::FsPath{"/control.nacp"}, FsOpenMode_Read, std::addressof(file)));
ON_SCOPE_EXIT(fsFileClose(std::addressof(file)));
u64 bytes_read;
R_TRY(fsFileRead(&file, 0, nacp_out, nacp_size, 0, &bytes_read));
}
// read icon.
if (icon_out) {
// todo: use matching icon based on the language version.
FsFile file;
R_TRY(fsFsOpenFile(std::addressof(fs), fs::FsPath{"/icon_AmericanEnglish.dat"}, FsOpenMode_Read, std::addressof(file)));
ON_SCOPE_EXIT(fsFileClose(std::addressof(file)));
s64 size;
R_TRY(fsFileGetSize(std::addressof(file), std::addressof(size)));
icon_out->resize(size);
u64 bytes_read;
R_TRY(fsFileRead(&file, 0, icon_out->data(), icon_out->size(), 0, &bytes_read));
}
R_SUCCEED();
}
auto GetKeyGenStr(u8 key_gen) -> const char* {
switch (key_gen) {
case KeyGenerationOld_100: return "1.0.0";

View File

@@ -929,7 +929,7 @@ Result Yati::InstallNca(std::span<TikCollection> tickets, NcaCollection& nca) {
} else if (nca.header.content_type == nca::ContentType_Control) {
NacpLanguageEntry entry;
std::vector<u8> icon;
R_TRY(yati::ParseControlNca(path, nca.header.program_id, &entry, sizeof(entry), &icon));
R_TRY(nca::ParseControl(path, nca.header.program_id, &entry, sizeof(entry), &icon));
pbox->SetTitle(entry.name).SetImageData(icon);
}
@@ -949,7 +949,7 @@ Result Yati::InstallCnmtNca(std::span<TikCollection> tickets, CnmtCollection& cn
ncm::PackagedContentMeta header;
std::vector<NcmPackagedContentInfo> infos;
R_TRY(ParseCnmtNca(path, cnmt.header.program_id, header, cnmt.extended_header, infos));
R_TRY(nca::ParseCnmt(path, cnmt.header.program_id, header, cnmt.extended_header, infos));
for (const auto& packed_info : infos) {
const auto& info = packed_info.info;
@@ -1418,71 +1418,4 @@ Result InstallFromCollections(ui::ProgressBox* pbox, std::shared_ptr<source::Bas
}
}
Result ParseCnmtNca(const fs::FsPath& path, u64 program_id, ncm::PackagedContentMeta& header, std::vector<u8>& extended_header, std::vector<NcmPackagedContentInfo>& infos) {
FsFileSystem fs;
R_TRY(fsOpenFileSystemWithId(std::addressof(fs), program_id, FsFileSystemType_ContentMeta, path, FsContentAttributes_All));
ON_SCOPE_EXIT(fsFsClose(std::addressof(fs)));
FsDir dir;
R_TRY(fsFsOpenDirectory(std::addressof(fs), fs::FsPath{"/"}, FsDirOpenMode_ReadFiles, std::addressof(dir)));
ON_SCOPE_EXIT(fsDirClose(std::addressof(dir)));
s64 total_entries;
FsDirectoryEntry buf;
R_TRY(fsDirRead(std::addressof(dir), std::addressof(total_entries), 1, std::addressof(buf)));
FsFile file;
R_TRY(fsFsOpenFile(std::addressof(fs), fs::AppendPath("/", buf.name), FsOpenMode_Read, std::addressof(file)));
ON_SCOPE_EXIT(fsFileClose(std::addressof(file)));
s64 offset{};
u64 bytes_read;
R_TRY(fsFileRead(std::addressof(file), offset, std::addressof(header), sizeof(header), 0, std::addressof(bytes_read)));
offset += bytes_read;
// read extended header
extended_header.resize(header.meta_header.extended_header_size);
R_TRY(fsFileRead(std::addressof(file), offset, extended_header.data(), extended_header.size(), 0, std::addressof(bytes_read)));
offset += bytes_read;
// read infos.
infos.resize(header.meta_header.content_count);
R_TRY(fsFileRead(std::addressof(file), offset, infos.data(), infos.size() * sizeof(NcmPackagedContentInfo), 0, std::addressof(bytes_read)));
offset += bytes_read;
R_SUCCEED();
}
Result ParseControlNca(const fs::FsPath& path, u64 program_id, void* nacp_out, s64 nacp_size, std::vector<u8>* icon_out) {
FsFileSystem fs;
R_TRY(fsOpenFileSystemWithId(std::addressof(fs), program_id, FsFileSystemType_ContentControl, path, FsContentAttributes_All));
ON_SCOPE_EXIT(fsFsClose(std::addressof(fs)));
// read nacp.
if (nacp_out) {
FsFile file;
R_TRY(fsFsOpenFile(std::addressof(fs), fs::FsPath{"/control.nacp"}, FsOpenMode_Read, std::addressof(file)));
ON_SCOPE_EXIT(fsFileClose(std::addressof(file)));
u64 bytes_read;
R_TRY(fsFileRead(&file, 0, nacp_out, nacp_size, 0, &bytes_read));
}
// read icon.
if (icon_out) {
FsFile file;
R_TRY(fsFsOpenFile(std::addressof(fs), fs::FsPath{"/icon_AmericanEnglish.dat"}, FsOpenMode_Read, std::addressof(file)));
ON_SCOPE_EXIT(fsFileClose(std::addressof(file)));
s64 size;
R_TRY(fsFileGetSize(std::addressof(file), std::addressof(size)));
icon_out->resize(size);
u64 bytes_read;
R_TRY(fsFileRead(&file, 0, icon_out->data(), icon_out->size(), 0, &bytes_read));
}
R_SUCCEED();
}
} // namespace sphaira::yati