hac2l: recombine apps/patches (tracking kind), implement target selection for appfs

This commit is contained in:
Michael Scire 2022-06-10 15:54:14 -07:00
parent bec2e55748
commit 65843dec94
5 changed files with 95 additions and 36 deletions

View File

@ -25,9 +25,10 @@ namespace ams::hactool {
u32 m_version;
u8 m_id_offset;
ncm::ContentType m_type;
ncm::ContentMetaType m_meta_type;
UserData m_data;
public:
ApplicationContentTreeEntry(ncm::ApplicationId id, u32 v, u8 o, ncm::ContentType t) : m_id(id), m_version(v), m_id_offset(o), m_type(t), m_data() {
ApplicationContentTreeEntry(ncm::ApplicationId id, u32 v, u8 o, ncm::ContentType t, ncm::ContentMetaType m) : m_id(id), m_version(v), m_id_offset(o), m_type(t), m_meta_type(m), m_data() {
/* ... */
}
@ -47,6 +48,10 @@ namespace ams::hactool {
return m_type;
}
ncm::ContentMetaType GetMetaType() const {
return m_meta_type;
}
const UserData &GetData() const { return m_data; }
UserData &GetData() { return m_data; }
@ -59,13 +64,15 @@ namespace ams::hactool {
const auto a_v = a.GetVersion();
const auto a_o = a.GetIdOffset();
const auto a_t = a.GetType();
const auto a_m = a.GetMetaType();
const auto b_i = b.GetId();
const auto b_v = b.GetVersion();
const auto b_o = b.GetIdOffset();
const auto b_t = b.GetType();
if (std::tie(a_i, a_v, a_o, a_t) < std::tie(b_i, b_v, b_o, b_t)) {
const auto b_m = b.GetMetaType();
if (std::tie(a_i, a_v, a_o, a_t, a_m) < std::tie(b_i, b_v, b_o, b_t, b_m)) {
return -1;
} else if (std::tie(a_i, a_v, a_o, a_t) > std::tie(b_i, b_v, b_o, b_t)) {
} else if (std::tie(a_i, a_v, a_o, a_t, a_m) > std::tie(b_i, b_v, b_o, b_t, b_m)) {
return 1;
} else {
return 0;
@ -96,8 +103,8 @@ namespace ams::hactool {
}
}
ApplicationContentTreeEntry<T> *Insert(ncm::ApplicationId id, u32 v, u8 o, ncm::ContentType t) {
auto *entry = new ApplicationContentTreeEntry<T>(id, v, o, t);
ApplicationContentTreeEntry<T> *Insert(ncm::ApplicationId id, u32 v, u8 o, ncm::ContentType t, ncm::ContentMetaType m) {
auto *entry = new ApplicationContentTreeEntry<T>(id, v, o, t, m);
m_tree.insert(*entry);
return entry;
}
@ -105,8 +112,8 @@ namespace ams::hactool {
auto begin() const { return m_tree.begin(); }
auto end() const { return m_tree.end(); }
auto Find(ncm::ApplicationId id, u32 v, u8 o, ncm::ContentType t) {
ApplicationContentTreeEntry<T> dummy(id, v, o, t);
auto Find(ncm::ApplicationId id, u32 v, u8 o, ncm::ContentType t, ncm::ContentMetaType m) {
ApplicationContentTreeEntry<T> dummy(id, v, o, t, m);
return m_tree.find(dummy);
}
};

View File

@ -63,6 +63,17 @@ namespace ams::hactool {
return true;
}
bool ParseIntegerArgument(int *out, const char *argument) {
char *parse_end = nullptr;
const auto val = std::strtol(argument, std::addressof(parse_end), 0);
if (parse_end != nullptr && parse_end != argument) {
*out = val;
return true;
} else {
return false;
}
}
using OptionHandlerFunction = util::IFunction<bool(Options &, const char *)>;
struct OptionHandler {
@ -153,6 +164,9 @@ namespace ams::hactool {
MakeOptionHandler("logodir", [] (Options &options, const char *arg) { return CreateFilePath(std::addressof(options.logo_partition_out_dir), arg); }),
MakeOptionHandler("listromfs", [] (Options &options) { options.list_romfs = true; }),
MakeOptionHandler("listupdate", [] (Options &options) { options.list_update = true; }),
MakeOptionHandler("appindex", [] (Options &options, const char *arg) { return ParseIntegerArgument(std::addressof(options.preferred_app_index), arg); }),
MakeOptionHandler("programindex", [] (Options &options, const char *arg) { return ParseIntegerArgument(std::addressof(options.preferred_program_index), arg); }),
MakeOptionHandler("appversion", [] (Options &options, const char *arg) { return ParseIntegerArgument(std::addressof(options.preferred_version), arg); }),
};
}

View File

@ -264,9 +264,6 @@ namespace ams::hactool {
/* Get the version. */
const auto version = meta_header->version;
/* We'll want to insert into the appropriate tracking holder. */
auto &target = meta_header->type == ncm::ContentMetaType::Patch ? ctx->patches : ctx->apps;
/* Add all the content metas. */
for (size_t i = 0; i < meta_reader.GetContentCount(); ++i) {
const auto &info = *meta_reader.GetContentInfo(i);
@ -277,8 +274,8 @@ namespace ams::hactool {
}
/* Check that we don't already have an info for the content. */
if (auto existing = target.Find(*app_id, version, info.GetIdOffset(), info.GetType()); existing != target.end()) {
fprintf(stderr, "[Warning]: Ignoring duplicate entry { %016" PRIX64 ", %" PRIu32 ", %d, %d }\n", app_id->value, version, static_cast<int>(info.GetIdOffset()), static_cast<int>(info.GetType()));
if (auto existing = ctx->apps.Find(*app_id, version, info.GetIdOffset(), info.GetType(), meta_header->type); existing != ctx->apps.end()) {
fprintf(stderr, "[Warning]: Ignoring duplicate entry { %016" PRIX64 ", %" PRIu32 ", %d, %d, %s }\n", app_id->value, version, static_cast<int>(info.GetIdOffset()), static_cast<int>(info.GetType()), meta_header->type == ncm::ContentMetaType::Patch ? "Patch" : "App");
continue;
}
@ -304,7 +301,7 @@ namespace ams::hactool {
}
/* Add the new version for the content. */
auto *entry = target.Insert(*app_id, version, info.GetIdOffset(), info.GetType());
auto *entry = ctx->apps.Insert(*app_id, version, info.GetIdOffset(), info.GetType(), meta_header->type);
entry->GetData().storage = std::move(storage);
}
@ -316,6 +313,59 @@ namespace ams::hactool {
}
}
/* Determine the target. */
{
/* Start with no target. */
ctx->has_target = false;
s32 app_idx = m_options.preferred_app_index;
s32 prog_idx = m_options.preferred_program_index;
s32 version = m_options.preferred_version;
/* Determine the application id. */
if (app_idx < 0) {
app_idx = 0;
}
{
s32 cur_app_idx = -1;
ncm::ApplicationId cur_app_id{};
for (const auto &entry : ctx->apps) {
if (entry.GetType() != ncm::ContentType::Program) {
continue;
}
if (cur_app_idx == -1 || cur_app_id != entry.GetId()) {
++cur_app_idx;
cur_app_id = entry.GetId();
}
if (app_idx == cur_app_idx) {
ctx->target_app_id = entry.GetId();
if (prog_idx < 0) {
prog_idx = entry.GetIdOffset();
}
break;
}
}
}
/* Find a matching version. */
if (ctx->target_app_id != ncm::ApplicationId{}) {
for (const auto &entry : ctx->apps) {
/* We only care about matching program entries. */
if (entry.GetType() != ncm::ContentType::Program || entry.GetId() != ctx->target_app_id || entry.GetIdOffset() != prog_idx) {
continue;
}
if ((version < 0 && entry.GetVersion() >= ctx->target_version) || (version >= 0 && static_cast<u32>(version) == entry.GetVersion())) {
ctx->has_target = true;
ctx->target_version = entry.GetVersion();
ctx->target_index = entry.GetIdOffset();
}
}
}
}
/* TODO: Recursive processing? */
/* Print. */
@ -348,20 +398,12 @@ namespace ams::hactool {
cur_app_id = entry.GetId();
}
this->PrintFormat(field_name, "{ Idx=%d, ProgramId=%016" PRIX64 ", Version=0x%08" PRIX32 ", IdOffset=%02" PRIX32 " }", app_idx, entry.GetId().value, entry.GetVersion(), entry.GetIdOffset());
this->PrintFormat(field_name, "{ Idx=%d, ProgramId=%016" PRIX64 ", Version=0x%08" PRIX32 ", IdOffset=%02" PRIX32 ", MetaType=%s }", app_idx, entry.GetId().value, entry.GetVersion(), entry.GetIdOffset(), entry.GetMetaType() == ncm::ContentMetaType::Patch ? "Patch" : "App");
field_name = "";
}
if (ctx.patches.begin() != ctx.patches.end()) {
field_name = "Patches";
for (const auto &entry : ctx.patches) {
if (entry.GetType() != ncm::ContentType::Program) {
continue;
}
this->PrintFormat(field_name, "{ ProgramId=%016" PRIX64 ", Version=0x%08" PRIX32 ", IdOffset=%02" PRIX32 " }", entry.GetId().value, entry.GetVersion(), entry.GetIdOffset());
field_name = "";
}
if (ctx.has_target) {
this->PrintFormat("Target", "{ ProgramId=%016" PRIX64 ", Version=0x%08" PRIX32 ", IdOffset=%02" PRIX32 " }", ctx.target_app_id.value, ctx.target_version, ctx.target_index);
}
}

View File

@ -83,7 +83,11 @@ namespace ams::hactool {
};
ApplicationContentsHolder<ApplicationEntryData> apps;
ApplicationContentsHolder<ApplicationEntryData> patches;
bool has_target;
ncm::ApplicationId target_app_id;
u32 target_version;
u8 target_index;
};
struct ProcessAsXciContext {

View File

@ -372,20 +372,12 @@ namespace ams::hactool {
cur_app_id = entry.GetId();
}
this->PrintFormat(field_name, "{ Idx=%d, ProgramId=%016" PRIX64 ", Version=0x%08" PRIX32 ", IdOffset=%02" PRIX32 " }", app_idx, entry.GetId().value, entry.GetVersion(), entry.GetIdOffset());
this->PrintFormat(field_name, "{ Idx=%d, ProgramId=%016" PRIX64 ", Version=0x%08" PRIX32 ", IdOffset=%02" PRIX32 ", MetaType=%s }", app_idx, entry.GetId().value, entry.GetVersion(), entry.GetIdOffset(), entry.GetMetaType() == ncm::ContentMetaType::Patch ? "Patch" : "App");
field_name = "";
}
if (ctx.app_ctx.patches.begin() != ctx.app_ctx.patches.end()) {
field_name = "Patches";
for (const auto &entry : ctx.app_ctx.patches) {
if (entry.GetType() != ncm::ContentType::Program) {
continue;
}
this->PrintFormat(field_name, "{ ProgramId=%016" PRIX64 ", Version=0x%08" PRIX32 ", IdOffset=%02" PRIX32 " }", entry.GetId().value, entry.GetVersion(), entry.GetIdOffset());
field_name = "";
}
if (ctx.app_ctx.has_target) {
this->PrintFormat("Target", "{ ProgramId=%016" PRIX64 ", Version=0x%08" PRIX32 ", IdOffset=%02" PRIX32 " }", ctx.app_ctx.target_app_id.value, ctx.app_ctx.target_version, ctx.app_ctx.target_index);
}
}