mirror of
https://github.com/Atmosphere-NX/hac2l.git
synced 2025-06-28 14:02:41 +02:00
hac2l: recombine apps/patches (tracking kind), implement target selection for appfs
This commit is contained in:
parent
bec2e55748
commit
65843dec94
@ -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);
|
||||
}
|
||||
};
|
||||
|
@ -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); }),
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user