mirror of
https://github.com/Atmosphere-NX/hac2l.git
synced 2025-06-20 18:52:39 +02:00
312 lines
13 KiB
C++
312 lines
13 KiB
C++
/*
|
|
* Copyright (c) Atmosphère-NX
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU General Public License,
|
|
* version 2, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#pragma once
|
|
#include <stratosphere.hpp>
|
|
#include "hactool_options.hpp"
|
|
#include "hactool_application_list.hpp"
|
|
|
|
namespace ams::hactool {
|
|
|
|
class Processor {
|
|
NON_COPYABLE(Processor);
|
|
NON_MOVEABLE(Processor);
|
|
private:
|
|
static constexpr size_t WidthToPrintFieldValue = 52;
|
|
static constexpr size_t BytesPerLine = 32;
|
|
private:
|
|
class ScopedIndentHolder {
|
|
NON_COPYABLE(ScopedIndentHolder);
|
|
NON_MOVEABLE(ScopedIndentHolder);
|
|
private:
|
|
char *m_p;
|
|
public:
|
|
ScopedIndentHolder(char *p) : m_p(p) { /* ... */ }
|
|
~ScopedIndentHolder() { *m_p = 0; }
|
|
};
|
|
|
|
struct ProcessAsNpdmContext {
|
|
std::shared_ptr<fs::IStorage> storage;
|
|
|
|
std::unique_ptr<u8[]> raw_data;
|
|
|
|
const ldr::Npdm *npdm = nullptr;
|
|
const ldr::Acid *acid = nullptr;
|
|
const ldr::Aci *aci = nullptr;
|
|
|
|
const void *acid_fac = nullptr;
|
|
const void *acid_sac = nullptr;
|
|
const void *acid_kac = nullptr;
|
|
|
|
const void *aci_fah = nullptr;
|
|
const void *aci_sac = nullptr;
|
|
const void *aci_kac = nullptr;
|
|
|
|
const void *modulus = nullptr;
|
|
};
|
|
|
|
struct ProcessAsNcaContext {
|
|
std::shared_ptr<fs::IStorage> storage;
|
|
std::shared_ptr<fssystem::NcaReader> reader;
|
|
std::shared_ptr<fssystem::NcaReader> base_reader;
|
|
s32 exefs_index = -1;
|
|
s32 romfs_index = -1;
|
|
std::array<bool, fssystem::NcaHeader::FsCountMax> has_sections{};
|
|
std::array<bool, fssystem::NcaHeader::FsCountMax> has_real_sections{};
|
|
std::array<bool, fssystem::NcaHeader::FsCountMax> is_mounted{};
|
|
std::array<std::shared_ptr<fs::IStorage>, fssystem::NcaHeader::FsCountMax> raw_sections{};
|
|
std::array<std::shared_ptr<fs::IStorage>, fssystem::NcaHeader::FsCountMax> sections{};
|
|
std::array<std::shared_ptr<fssystem::IAsynchronousAccessSplitter>, fssystem::NcaHeader::FsCountMax> splitters{};
|
|
std::array<fssystem::NcaFsHeaderReader, fssystem::NcaHeader::FsCountMax> header_readers{};
|
|
std::array<fssystem::NcaFileSystemDriver::StorageContext, fssystem::NcaHeader::FsCountMax> storage_contexts{};
|
|
std::array<std::shared_ptr<fs::fsa::IFileSystem>, fssystem::NcaHeader::FsCountMax> file_systems{};
|
|
|
|
ProcessAsNpdmContext npdm_ctx;
|
|
};
|
|
|
|
struct ProcessAsApplicationFileSystemContext {
|
|
std::shared_ptr<fs::fsa::IFileSystem> fs;
|
|
|
|
struct ApplicationEntryData {
|
|
std::shared_ptr<fs::IStorage> storage;
|
|
};
|
|
|
|
ApplicationContentsHolder<ApplicationEntryData> apps;
|
|
|
|
bool has_target;
|
|
ncm::ApplicationId target_app_id;
|
|
u32 target_version;
|
|
u8 target_index;
|
|
|
|
ProcessAsNcaContext app_nca_ctx;
|
|
ProcessAsNcaContext app_base_nca_ctx;
|
|
};
|
|
|
|
struct ProcessAsXciContext {
|
|
std::shared_ptr<fs::IStorage> storage;
|
|
|
|
std::shared_ptr<fs::IStorage> key_area_storage;
|
|
std::shared_ptr<fs::IStorage> body_storage;
|
|
|
|
struct CardData {
|
|
gc::impl::CardInitialData initial_data;
|
|
gc::impl::CardHeaderWithSignature header;
|
|
gc::impl::CardHeaderWithSignature decrypted_header;
|
|
gc::impl::CardHeaderWithSignature header_for_hash;
|
|
gc::impl::CardHeaderWithSignature decrypted_header_for_hash;
|
|
gc::impl::T1CardCertificate t1_certificate;
|
|
gc::impl::Ca10Certificate ca10_certificate;
|
|
};
|
|
|
|
CardData card_data;
|
|
|
|
struct PartitionData {
|
|
std::shared_ptr<fs::IStorage> storage;
|
|
std::shared_ptr<fs::fsa::IFileSystem> fs;
|
|
};
|
|
|
|
PartitionData root_partition;
|
|
PartitionData update_partition;
|
|
PartitionData logo_partition;
|
|
PartitionData normal_partition;
|
|
PartitionData secure_partition;
|
|
|
|
ProcessAsApplicationFileSystemContext app_ctx;
|
|
};
|
|
|
|
struct ProcessAsNspContext {
|
|
std::shared_ptr<fs::IStorage> storage;
|
|
std::shared_ptr<fs::fsa::IFileSystem> fs;
|
|
|
|
u32 magic;
|
|
bool is_exefs;
|
|
|
|
ProcessAsNpdmContext npdm_ctx;
|
|
ProcessAsApplicationFileSystemContext app_ctx;
|
|
};
|
|
|
|
struct ProcessAsRomfsContext {
|
|
std::shared_ptr<fs::IStorage> storage;
|
|
std::shared_ptr<fs::fsa::IFileSystem> fs;
|
|
};
|
|
private:
|
|
Options m_options;
|
|
fssrv::impl::ExternalKeyManager m_external_nca_key_manager;
|
|
std::shared_ptr<fs::fsa::IFileSystem> m_local_fs;
|
|
|
|
bool m_has_base_nca;
|
|
bool m_has_base_xci;
|
|
bool m_has_base_nsp;
|
|
bool m_has_base_appfs;
|
|
ProcessAsNcaContext m_base_nca_ctx;
|
|
ProcessAsXciContext m_base_xci_ctx;
|
|
ProcessAsNspContext m_base_nsp_ctx;
|
|
ProcessAsApplicationFileSystemContext m_base_appfs_ctx;
|
|
|
|
os::SdkMutex m_print_lock;
|
|
char m_indent_buffer[1_KB];
|
|
public:
|
|
Processor(const Options &options);
|
|
|
|
Result Process();
|
|
private:
|
|
/* Printing. */
|
|
[[nodiscard]] ScopedIndentHolder IncreaseIndentation() {
|
|
static constexpr const char Indentation[] = " ";
|
|
const auto len = std::strlen(m_indent_buffer);
|
|
AMS_ABORT_UNLESS(len + sizeof(Indentation) < sizeof(m_indent_buffer));
|
|
|
|
std::memcpy(m_indent_buffer + len, Indentation, sizeof(Indentation));
|
|
|
|
return ScopedIndentHolder(m_indent_buffer + len);
|
|
}
|
|
|
|
void PrintLineImpl(const char *fmt, ...) const __attribute__((format(printf, 2, 3)));
|
|
|
|
void PrintFormat(const char *field_name, const char *fmt, ...) const __attribute__((format(printf, 3, 4)));
|
|
|
|
[[nodiscard]] ScopedIndentHolder PrintHeader(const char *field_name) { this->PrintFormat(field_name, ""); return this->IncreaseIndentation(); }
|
|
|
|
void PrintString(const char *field_name, const char *str) const { this->PrintFormat(field_name, "%s", str); }
|
|
|
|
void PrintInteger(const char *field_name, s64 v) const { return this->PrintFormat(field_name, "%" PRId64, v); }
|
|
|
|
void PrintMagic(u32 magic) {
|
|
char magic_str[5];
|
|
*reinterpret_cast<u32 *>(magic_str) = magic;
|
|
magic_str[4] = 0;
|
|
this->PrintString("Magic", magic_str);
|
|
}
|
|
|
|
static void MakeVerifyFieldName(char *dst, size_t dst_size, const char *name, bool verified) {
|
|
util::TSNPrintf(dst, dst_size, "%s %s", name, verified ? "(GOOD)" : "(FAIL)");
|
|
}
|
|
|
|
void PrintHex(const char *field_name, u64 v) const { this->PrintFormat(field_name, "0x%" PRIX64, v); }
|
|
void PrintHex2(const char *field_name, u64 v) const { this->PrintFormat(field_name, "0x%02" PRIX64, v); }
|
|
void PrintHex4(const char *field_name, u64 v) const { this->PrintFormat(field_name, "0x%04" PRIX64, v); }
|
|
void PrintHex8(const char *field_name, u64 v) const { this->PrintFormat(field_name, "0x%08" PRIX64, v); }
|
|
void PrintHex12(const char *field_name, u64 v) const { this->PrintFormat(field_name, "0x%012" PRIX64, v); }
|
|
void PrintHex16(const char *field_name, u64 v) const { this->PrintFormat(field_name, "0x%016" PRIX64, v); }
|
|
|
|
void PrintId64(const char *field_name, u64 v) const { this->PrintFormat(field_name, "%016" PRIX64, v); }
|
|
|
|
void PrintBool(const char *field_name, bool v) const { this->PrintFormat(field_name, "%d", v); }
|
|
|
|
void PrintBytes(const char *field_name, const void *src, size_t src_size) {
|
|
char byte_str[2 * BytesPerLine + 1] = {};
|
|
size_t byte_str_len = 0;
|
|
|
|
const u8 *src8 = static_cast<const u8 *>(src);
|
|
for (size_t i = 0; i < src_size; ++i) {
|
|
util::TSNPrintf(byte_str + byte_str_len, 3, "%02" PRIX8, src8[i]);
|
|
byte_str_len += 2;
|
|
|
|
if (byte_str_len == sizeof(byte_str) - 1) {
|
|
this->PrintFormat(field_name, "%s", byte_str);
|
|
field_name = "";
|
|
byte_str_len = 0;
|
|
}
|
|
}
|
|
|
|
if (byte_str_len > 0) {
|
|
this->PrintFormat(field_name, "%s", byte_str);
|
|
}
|
|
}
|
|
|
|
void PrintBytesWithVerify(const char *field_name, bool verified, const void *src, size_t src_size) {
|
|
char verif_field_name[0x40];
|
|
MakeVerifyFieldName(verif_field_name, sizeof(verif_field_name), field_name, verified);
|
|
|
|
return this->PrintBytes(verif_field_name, src, src_size);
|
|
}
|
|
|
|
/* Utility/management. */
|
|
void PresetInternalKeys();
|
|
void PrintInternalKeys();
|
|
|
|
/* Procesing. */
|
|
Result ProcessAsNca(std::shared_ptr<fs::IStorage> storage, ProcessAsNcaContext *ctx = nullptr);
|
|
Result ProcessAsNpdm(std::shared_ptr<fs::IStorage> storage, ProcessAsNpdmContext *ctx = nullptr);
|
|
Result ProcessAsXci(std::shared_ptr<fs::IStorage> storage, ProcessAsXciContext *ctx = nullptr);
|
|
Result ProcessAsNsp(std::shared_ptr<fs::IStorage> storage, ProcessAsNspContext *ctx = nullptr);
|
|
Result ProcessAsRomfs(std::shared_ptr<fs::IStorage> storage, ProcessAsRomfsContext *ctx = nullptr);
|
|
Result ProcessAsApplicationFileSystem(std::shared_ptr<fs::fsa::IFileSystem> fs, ProcessAsApplicationFileSystemContext *ctx = nullptr);
|
|
|
|
/* Printing. */
|
|
void PrintAsNca(ProcessAsNcaContext &ctx);
|
|
void PrintAsNpdm(ProcessAsNpdmContext &ctx);
|
|
void PrintAsXci(ProcessAsXciContext &ctx);
|
|
void PrintAsNsp(ProcessAsNspContext &ctx);
|
|
void PrintAsRomfs(ProcessAsRomfsContext &ctx);
|
|
void PrintAsApplicationFileSystem(ProcessAsApplicationFileSystemContext &ctx);
|
|
|
|
/* Saving. */
|
|
void SaveAsNca(ProcessAsNcaContext &ctx);
|
|
void SaveAsNpdm(ProcessAsNpdmContext &ctx);
|
|
void SaveAsXci(ProcessAsXciContext &ctx);
|
|
void SaveAsNsp(ProcessAsNspContext &ctx);
|
|
void SaveAsRomfs(ProcessAsRomfsContext &ctx);
|
|
void SaveAsApplicationFileSystem(ProcessAsApplicationFileSystemContext &ctx);
|
|
};
|
|
|
|
inline void Processor::PrintLineImpl(const char *fmt, ...) const {
|
|
char print_buf[4_KB];
|
|
{
|
|
std::memcpy(print_buf, m_indent_buffer, sizeof(m_indent_buffer));
|
|
const auto l = std::strlen(print_buf);
|
|
|
|
std::va_list vl;
|
|
va_start(vl, fmt);
|
|
util::VSNPrintf(print_buf + l, sizeof(print_buf) - l, fmt, vl);
|
|
va_end(vl);
|
|
}
|
|
|
|
const auto l = std::strlen(print_buf);
|
|
if (print_buf[l - 1] != '\n') {
|
|
AMS_ABORT_UNLESS(l < sizeof(print_buf) - 1);
|
|
print_buf[l] = '\n';
|
|
print_buf[l + 1] = 0;
|
|
}
|
|
|
|
std::printf("%s", print_buf);
|
|
}
|
|
|
|
inline void Processor::PrintFormat(const char *field_name, const char *fmt, ...) const {
|
|
const auto l = std::strlen(m_indent_buffer);
|
|
AMS_ABORT_UNLESS(l < WidthToPrintFieldValue);
|
|
|
|
char line_fmt[0x40];
|
|
util::TSNPrintf(line_fmt, sizeof(line_fmt), "%%-%ds" "%%s", static_cast<int>(WidthToPrintFieldValue - l));
|
|
|
|
char field_colon[0x40];
|
|
if (field_name[0] == 0 || field_name[std::strlen(field_name) - 1] == ':') {
|
|
util::TSNPrintf(field_colon, sizeof(field_colon), "%s", field_name);
|
|
} else {
|
|
util::TSNPrintf(field_colon, sizeof(field_colon), "%s:", field_name);
|
|
}
|
|
|
|
char value_str[4_KB];
|
|
{
|
|
std::va_list vl;
|
|
va_start(vl, fmt);
|
|
util::VSNPrintf(value_str, sizeof(value_str), fmt, vl);
|
|
va_end(vl);
|
|
}
|
|
|
|
this->PrintLineImpl(line_fmt, field_colon, value_str);
|
|
}
|
|
|
|
} |