add gc_menu, add progress, icon, time remaining to progress bar (see full commit message).
- fix ignore distribution bit doing nothing. - fix yati failing to parse control nca causing the transfer to abort. - yati now uses ncm rather than ns to get the latest app version. - improve ui::list input handling (it handles directional buttons now). - progress bar displays speed and time remaining. - added gc menu (taken from my gc installer nx and gci).
This commit is contained in:
@@ -151,4 +151,30 @@ Result VerifyFixedKey(const Header& header) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
auto GetKeyGenStr(u8 key_gen) -> const char* {
|
||||
switch (key_gen) {
|
||||
case KeyGenerationOld_100: return "1.0.0";
|
||||
case KeyGenerationOld_300: return "3.0.0";
|
||||
case KeyGeneration_301: return "3.0.1";
|
||||
case KeyGeneration_400: return "4.0.0";
|
||||
case KeyGeneration_500: return "5.0.0";
|
||||
case KeyGeneration_600: return "6.0.0";
|
||||
case KeyGeneration_620: return "6.2.0";
|
||||
case KeyGeneration_700: return "7.0.0";
|
||||
case KeyGeneration_810: return "8.1.0";
|
||||
case KeyGeneration_900: return "9.0.0";
|
||||
case KeyGeneration_910: return "9.1.0";
|
||||
case KeyGeneration_1210: return "12.1.0";
|
||||
case KeyGeneration_1300: return "13.0.0";
|
||||
case KeyGeneration_1400: return "14.0.0";
|
||||
case KeyGeneration_1500: return "15.0.0";
|
||||
case KeyGeneration_1600: return "16.0.0";
|
||||
case KeyGeneration_1700: return "17.0.0";
|
||||
case KeyGeneration_1800: return "18.0.0";
|
||||
case KeyGeneration_1900: return "19.0.0";
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
} // namespace sphaira::nca
|
||||
|
||||
@@ -7,16 +7,24 @@ namespace {
|
||||
|
||||
} // namespace
|
||||
|
||||
auto GetAppId(const NcmContentMetaKey& key) -> u64 {
|
||||
if (key.type == NcmContentMetaType_Patch) {
|
||||
return key.id ^ 0x800;
|
||||
} else if (key.type == NcmContentMetaType_AddOnContent) {
|
||||
return (key.id ^ 0x1000) & ~0xFFF;
|
||||
auto GetAppId(u8 meta_type, u64 id) -> u64 {
|
||||
if (meta_type == NcmContentMetaType_Patch) {
|
||||
return id ^ 0x800;
|
||||
} else if (meta_type == NcmContentMetaType_AddOnContent) {
|
||||
return (id ^ 0x1000) & ~0xFFF;
|
||||
} else {
|
||||
return key.id;
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
auto GetAppId(const NcmContentMetaKey& key) -> u64 {
|
||||
return GetAppId(key.type, key.id);
|
||||
}
|
||||
|
||||
auto GetAppId(const PackagedContentMeta& meta) -> u64 {
|
||||
return GetAppId(meta.meta_type, meta.title_id);
|
||||
}
|
||||
|
||||
Result Delete(NcmContentStorage* cs, const NcmContentId *content_id) {
|
||||
bool has;
|
||||
R_TRY(ncmContentStorageHas(cs, std::addressof(has), content_id));
|
||||
|
||||
@@ -274,7 +274,7 @@ struct Yati {
|
||||
Yati(ui::ProgressBox*, std::shared_ptr<source::Base>);
|
||||
~Yati();
|
||||
|
||||
Result Setup();
|
||||
Result Setup(const ConfigOverride& override);
|
||||
Result InstallNca(std::span<TikCollection> tickets, NcaCollection& nca);
|
||||
Result InstallCnmtNca(std::span<TikCollection> tickets, CnmtCollection& cnmt, const container::Collections& collections);
|
||||
Result InstallControlNca(std::span<TikCollection> tickets, const CnmtCollection& cnmt, NcaCollection& nca);
|
||||
@@ -549,7 +549,7 @@ Result Yati::decompressFuncInternal(ThreadData* t) {
|
||||
t->write_size = header.size;
|
||||
R_TRY(ncmContentStorageSetPlaceHolderSize(std::addressof(cs), std::addressof(t->nca->placeholder_id), header.size));
|
||||
|
||||
if (header.distribution_type == nca::DistributionType_GameCard) {
|
||||
if (!config.ignore_distribution_bit && header.distribution_type == nca::DistributionType_GameCard) {
|
||||
header.distribution_type = nca::DistributionType_System;
|
||||
t->nca->modified = true;
|
||||
}
|
||||
@@ -792,8 +792,8 @@ Yati::~Yati() {
|
||||
appletSetMediaPlaybackState(false);
|
||||
}
|
||||
|
||||
Result Yati::Setup() {
|
||||
config.sd_card_install = App::GetApp()->m_install_sd.Get();
|
||||
Result Yati::Setup(const ConfigOverride& override) {
|
||||
config.sd_card_install = override.sd_card_install.value_or(App::GetApp()->m_install_sd.Get());
|
||||
config.allow_downgrade = App::GetApp()->m_allow_downgrade.Get();
|
||||
config.skip_if_already_installed = App::GetApp()->m_skip_if_already_installed.Get();
|
||||
config.ticket_only = App::GetApp()->m_ticket_only.Get();
|
||||
@@ -802,13 +802,13 @@ Result Yati::Setup() {
|
||||
config.skip_addon = App::GetApp()->m_skip_addon.Get();
|
||||
config.skip_data_patch = App::GetApp()->m_skip_data_patch.Get();
|
||||
config.skip_ticket = App::GetApp()->m_skip_ticket.Get();
|
||||
config.skip_nca_hash_verify = App::GetApp()->m_skip_nca_hash_verify.Get();
|
||||
config.skip_rsa_header_fixed_key_verify = App::GetApp()->m_skip_rsa_header_fixed_key_verify.Get();
|
||||
config.skip_rsa_npdm_fixed_key_verify = App::GetApp()->m_skip_rsa_npdm_fixed_key_verify.Get();
|
||||
config.ignore_distribution_bit = App::GetApp()->m_ignore_distribution_bit.Get();
|
||||
config.convert_to_standard_crypto = App::GetApp()->m_convert_to_standard_crypto.Get();
|
||||
config.lower_master_key = App::GetApp()->m_lower_master_key.Get();
|
||||
config.lower_system_version = App::GetApp()->m_lower_system_version.Get();
|
||||
config.skip_nca_hash_verify = override.skip_nca_hash_verify.value_or(App::GetApp()->m_skip_nca_hash_verify.Get());
|
||||
config.skip_rsa_header_fixed_key_verify = override.skip_rsa_header_fixed_key_verify.value_or(App::GetApp()->m_skip_rsa_header_fixed_key_verify.Get());
|
||||
config.skip_rsa_npdm_fixed_key_verify = override.skip_rsa_npdm_fixed_key_verify.value_or(App::GetApp()->m_skip_rsa_npdm_fixed_key_verify.Get());
|
||||
config.ignore_distribution_bit = override.ignore_distribution_bit.value_or(App::GetApp()->m_ignore_distribution_bit.Get());
|
||||
config.convert_to_standard_crypto = override.convert_to_standard_crypto.value_or(App::GetApp()->m_convert_to_standard_crypto.Get());
|
||||
config.lower_master_key = override.lower_master_key.value_or(App::GetApp()->m_lower_master_key.Get());
|
||||
config.lower_system_version = override.lower_system_version.value_or(App::GetApp()->m_lower_system_version.Get());
|
||||
storage_id = config.sd_card_install ? NcmStorageId_SdCard : NcmStorageId_BuiltInUser;
|
||||
|
||||
R_TRY(source->GetOpenResult());
|
||||
@@ -925,37 +925,9 @@ Result Yati::InstallCnmtNca(std::span<TikCollection> tickets, CnmtCollection& cn
|
||||
R_TRY(ncmContentStorageFlushPlaceHolder(std::addressof(cs)));
|
||||
R_TRY(ncmContentStorageGetPlaceHolderPath(std::addressof(cs), path, sizeof(path), std::addressof(cnmt.placeholder_id)));
|
||||
|
||||
FsFileSystem fs;
|
||||
R_TRY(fsOpenFileSystem(std::addressof(fs), FsFileSystemType_ContentMeta, path));
|
||||
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;
|
||||
ncm::PackagedContentMeta header;
|
||||
R_TRY(fsFileRead(std::addressof(file), offset, std::addressof(header), sizeof(header), 0, std::addressof(bytes_read)));
|
||||
offset += bytes_read;
|
||||
|
||||
// read extended header
|
||||
cnmt.extended_header.resize(header.meta_header.extended_header_size);
|
||||
R_TRY(fsFileRead(std::addressof(file), offset, cnmt.extended_header.data(), cnmt.extended_header.size(), 0, std::addressof(bytes_read)));
|
||||
offset += bytes_read;
|
||||
|
||||
// read infos.
|
||||
std::vector<NcmPackagedContentInfo> infos(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;
|
||||
std::vector<NcmPackagedContentInfo> infos;
|
||||
R_TRY(ParseCnmtNca(path, header, cnmt.extended_header, infos));
|
||||
|
||||
for (const auto& info : infos) {
|
||||
if (info.info.content_type == NcmContentType_DeltaFragment) {
|
||||
@@ -1020,21 +992,15 @@ Result Yati::InstallControlNca(std::span<TikCollection> tickets, const CnmtColle
|
||||
R_TRY(ncmContentStorageFlushPlaceHolder(std::addressof(cs)));
|
||||
R_TRY(ncmContentStorageGetPlaceHolderPath(std::addressof(cs), path, sizeof(path), std::addressof(nca.placeholder_id)));
|
||||
|
||||
log_write("got control path: %s\n", path.s);
|
||||
FsFileSystem fs;
|
||||
R_TRY(fsOpenFileSystemWithId(std::addressof(fs), ncm::GetAppId(cnmt.key), FsFileSystemType_ContentControl, path, FsContentAttributes_All));
|
||||
ON_SCOPE_EXIT(fsFsClose(std::addressof(fs)));
|
||||
log_write("opened control path fs: %s\n", path.s);
|
||||
|
||||
FsFile file;
|
||||
R_TRY(fsFsOpenFile(std::addressof(fs), fs::FsPath{"/control.nacp"}, FsOpenMode_Read, std::addressof(file)));
|
||||
ON_SCOPE_EXIT(fsFileClose(std::addressof(file)));
|
||||
log_write("got control path file: %s\n", path.s);
|
||||
|
||||
// this can fail if it's not a valid control nca, examples are mario 3d all stars.
|
||||
// there are 4 control ncas, only 1 is valid (InvalidNcaId 0x235E02).
|
||||
NacpLanguageEntry entry;
|
||||
u64 bytes_read;
|
||||
R_TRY(fsFileRead(&file, 0, &entry, sizeof(entry), 0, &bytes_read));
|
||||
pbox->SetTitle("Installing "_i18n + entry.name);
|
||||
std::vector<u8> icon;
|
||||
if (R_SUCCEEDED(yati::ParseControlNca(path, ncm::GetAppId(cnmt.key), &entry, sizeof(entry), &icon))) {
|
||||
pbox->SetTitle(entry.name).SetImageData(icon);
|
||||
} else {
|
||||
log_write("\tWARNING: failed to parse control nca!\n");
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
@@ -1070,41 +1036,37 @@ Result Yati::ParseTicketsIntoCollection(std::vector<TikCollection>& tickets, con
|
||||
|
||||
Result Yati::GetLatestVersion(const CnmtCollection& cnmt, u32& version_out, bool& skip) {
|
||||
const auto app_id = ncm::GetAppId(cnmt.key);
|
||||
|
||||
bool has_records;
|
||||
R_TRY(nsIsAnyApplicationEntityInstalled(app_id, &has_records));
|
||||
|
||||
// TODO: fix this when gamecard is inserted as it will only return records
|
||||
// for the gamecard...
|
||||
// may have to use ncm directly to get the keys, then parse that.
|
||||
version_out = cnmt.key.version;
|
||||
if (has_records) {
|
||||
s32 meta_count{};
|
||||
R_TRY(nsCountApplicationContentMeta(app_id, &meta_count));
|
||||
R_UNLESS(meta_count > 0, 0x1);
|
||||
|
||||
std::vector<ncm::ContentStorageRecord> records(meta_count);
|
||||
s32 count;
|
||||
R_TRY(ns::ListApplicationRecordContentMeta(std::addressof(ns_app), 0, app_id, records.data(), records.size(), &count));
|
||||
R_UNLESS(count == records.size(), 0x1);
|
||||
|
||||
for (auto& record : records) {
|
||||
log_write("found record: 0x%016lX type: %u version: %u\n", record.key.id, record.key.type, record.key.version);
|
||||
log_write("cnmt record: 0x%016lX type: %u version: %u\n", cnmt.key.id, cnmt.key.type, cnmt.key.version);
|
||||
|
||||
if (record.key.id == cnmt.key.id && cnmt.key.version == record.key.version && config.skip_if_already_installed) {
|
||||
log_write("skipping as already installed\n");
|
||||
skip = true;
|
||||
for (auto& db : ncm_db) {
|
||||
s32 db_list_total;
|
||||
s32 db_list_count;
|
||||
std::vector<NcmContentMetaKey> keys(1);
|
||||
if (R_SUCCEEDED(ncmContentMetaDatabaseList(std::addressof(db), std::addressof(db_list_total), std::addressof(db_list_count), keys.data(), keys.size(), NcmContentMetaType_Unknown, app_id, 0, UINT64_MAX, NcmContentInstallType_Full))) {
|
||||
if (db_list_total != keys.size()) {
|
||||
keys.resize(db_list_total);
|
||||
if (keys.size()) {
|
||||
R_TRY(ncmContentMetaDatabaseList(std::addressof(db), std::addressof(db_list_total), std::addressof(db_list_count), keys.data(), keys.size(), NcmContentMetaType_Unknown, app_id, 0, UINT64_MAX, NcmContentInstallType_Full));
|
||||
}
|
||||
}
|
||||
|
||||
// check if we are downgrading
|
||||
if (cnmt.key.type == NcmContentMetaType_Patch) {
|
||||
if (cnmt.key.type == record.key.type && cnmt.key.version < record.key.version && !config.allow_downgrade) {
|
||||
log_write("skipping due to it being lower\n");
|
||||
for (auto& key : keys) {
|
||||
log_write("found record: %016lX type: %u version: %u\n", key.id, key.type, key.version);
|
||||
|
||||
if (key.id == cnmt.key.id && cnmt.key.version == key.version && config.skip_if_already_installed) {
|
||||
log_write("skipping as already installed\n");
|
||||
skip = true;
|
||||
}
|
||||
} else {
|
||||
version_out = std::max(version_out, record.key.version);
|
||||
|
||||
// check if we are downgrading
|
||||
if (cnmt.key.type == NcmContentMetaType_Patch) {
|
||||
if (cnmt.key.type == key.type && cnmt.key.version < key.version && !config.allow_downgrade) {
|
||||
log_write("skipping due to it being lower\n");
|
||||
skip = true;
|
||||
}
|
||||
} else {
|
||||
version_out = std::max(version_out, key.version);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1160,7 +1122,6 @@ Result Yati::RemoveInstalledNcas(const CnmtCollection& cnmt) {
|
||||
s32 db_list_count;
|
||||
u64 id_min = cnmt.key.id;
|
||||
u64 id_max = cnmt.key.id;
|
||||
std::vector<NcmContentMetaKey> keys(1);
|
||||
|
||||
// if installing a patch, remove all previously installed patches.
|
||||
if (cnmt.key.type == NcmContentMetaType_Patch) {
|
||||
@@ -1263,9 +1224,9 @@ Result Yati::RegisterNcasAndPushRecord(const CnmtCollection& cnmt, u32 latest_ve
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result InstallInternal(ui::ProgressBox* pbox, std::shared_ptr<source::Base> source, const container::Collections& collections) {
|
||||
Result InstallInternal(ui::ProgressBox* pbox, std::shared_ptr<source::Base> source, const container::Collections& collections, const ConfigOverride& override) {
|
||||
auto yati = std::make_unique<Yati>(pbox, source);
|
||||
R_TRY(yati->Setup());
|
||||
R_TRY(yati->Setup(override));
|
||||
|
||||
std::vector<TikCollection> tickets{};
|
||||
R_TRY(yati->ParseTicketsIntoCollection(tickets, collections, true));
|
||||
@@ -1318,9 +1279,9 @@ Result InstallInternal(ui::ProgressBox* pbox, std::shared_ptr<source::Base> sour
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result InstallInternalStream(ui::ProgressBox* pbox, std::shared_ptr<source::Base> source, container::Collections collections) {
|
||||
Result InstallInternalStream(ui::ProgressBox* pbox, std::shared_ptr<source::Base> source, container::Collections collections, const ConfigOverride& override) {
|
||||
auto yati = std::make_unique<Yati>(pbox, source);
|
||||
R_TRY(yati->Setup());
|
||||
R_TRY(yati->Setup(override));
|
||||
|
||||
// not supported with stream installs (yet).
|
||||
yati->config.convert_to_standard_crypto = false;
|
||||
@@ -1415,40 +1376,107 @@ Result InstallInternalStream(ui::ProgressBox* pbox, std::shared_ptr<source::Base
|
||||
|
||||
} // namespace
|
||||
|
||||
Result InstallFromFile(ui::ProgressBox* pbox, FsFileSystem* fs, const fs::FsPath& path) {
|
||||
return InstallFromSource(pbox, std::make_shared<source::File>(fs, path), path);
|
||||
// return InstallFromSource(pbox, std::make_shared<source::StreamFile>(fs, path), path);
|
||||
Result InstallFromFile(ui::ProgressBox* pbox, FsFileSystem* fs, const fs::FsPath& path, const ConfigOverride& override) {
|
||||
return InstallFromSource(pbox, std::make_shared<source::File>(fs, path), path, override);
|
||||
// return InstallFromSource(pbox, std::make_shared<source::StreamFile>(fs, path), path, override);
|
||||
}
|
||||
|
||||
Result InstallFromStdioFile(ui::ProgressBox* pbox, const fs::FsPath& path) {
|
||||
return InstallFromSource(pbox, std::make_shared<source::Stdio>(path), path);
|
||||
Result InstallFromStdioFile(ui::ProgressBox* pbox, const fs::FsPath& path, const ConfigOverride& override) {
|
||||
return InstallFromSource(pbox, std::make_shared<source::Stdio>(path), path, override);
|
||||
}
|
||||
|
||||
Result InstallFromSource(ui::ProgressBox* pbox, std::shared_ptr<source::Base> source, const fs::FsPath& path) {
|
||||
Result InstallFromSource(ui::ProgressBox* pbox, std::shared_ptr<source::Base> source, const fs::FsPath& path, const ConfigOverride& override) {
|
||||
const auto ext = std::strrchr(path.s, '.');
|
||||
R_UNLESS(ext, Result_ContainerNotFound);
|
||||
|
||||
if (!strcasecmp(ext, ".nsp") || !strcasecmp(ext, ".nsz")) {
|
||||
return InstallFromContainer(pbox, std::make_unique<container::Nsp>(source));
|
||||
return InstallFromContainer(pbox, std::make_unique<container::Nsp>(source), override);
|
||||
} else if (!strcasecmp(ext, ".xci") || !strcasecmp(ext, ".xcz")) {
|
||||
return InstallFromContainer(pbox, std::make_unique<container::Xci>(source));
|
||||
return InstallFromContainer(pbox, std::make_unique<container::Xci>(source), override);
|
||||
}
|
||||
|
||||
R_THROW(Result_ContainerNotFound);
|
||||
}
|
||||
|
||||
Result InstallFromContainer(ui::ProgressBox* pbox, std::shared_ptr<container::Base> container) {
|
||||
Result InstallFromContainer(ui::ProgressBox* pbox, std::shared_ptr<container::Base> container, const ConfigOverride& override) {
|
||||
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) {
|
||||
Result InstallFromCollections(ui::ProgressBox* pbox, std::shared_ptr<source::Base> source, const container::Collections& collections, const ConfigOverride& override) {
|
||||
if (source->IsStream()) {
|
||||
return InstallInternalStream(pbox, source, collections);
|
||||
return InstallInternalStream(pbox, source, collections, override);
|
||||
} else {
|
||||
return InstallInternal(pbox, source, collections);
|
||||
return InstallInternal(pbox, source, collections, override);
|
||||
}
|
||||
}
|
||||
|
||||
Result ParseCnmtNca(const fs::FsPath& path, ncm::PackagedContentMeta& header, std::vector<u8>& extended_header, std::vector<NcmPackagedContentInfo>& infos) {
|
||||
FsFileSystem fs;
|
||||
R_TRY(fsOpenFileSystem(std::addressof(fs), FsFileSystemType_ContentMeta, path));
|
||||
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 id, void* nacp_out, s64 nacp_size, std::vector<u8>* icon_out) {
|
||||
FsFileSystem fs;
|
||||
R_TRY(fsOpenFileSystemWithId(std::addressof(fs), 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
|
||||
|
||||
Reference in New Issue
Block a user