Add CIA building

Quite a lot of code, yeah.

The built CIA is almost identical to GM9, with the following differences:
1. Paddings are zeroed out
2. Title key is not written (GM9 gets it from support data/ticket db)
3. Ticket content index is slightly different (GM9 likely takes it from the legit ticket, while we are building a fake one)

The 2, 3 points can be fixed probably.
This commit is contained in:
zhupengfei
2020-08-07 08:58:09 +08:00
parent 769162e95c
commit 49ddd86b7a
8 changed files with 514 additions and 41 deletions
+65
View File
@@ -12,6 +12,7 @@
#include "core/importer.h"
#include "core/inner_fat.h"
#include "core/key/key.h"
#include "core/ncch/cia_builder.h"
#include "core/ncch/ncch_container.h"
#include "core/ncch/seed_db.h"
#include "core/ncch/smdh.h"
@@ -53,6 +54,7 @@ bool SDMCImporter::Init() {
}
decryptor = std::make_unique<SDMCDecryptor>(config.sdmc_path);
cia_builder = std::make_unique<CIABuilder>();
FileUtil::SetUserPath(config.user_path);
return true;
@@ -493,6 +495,69 @@ void SDMCImporter::AbortDumpCXI() {
dump_cxi_ncch->AbortDecryptToFile();
}
bool SDMCImporter::BuildCIA(const ContentSpecifier& specifier, const std::string& destination,
const ProgressCallback& callback) {
if (config.certs_db_path.empty()) {
LOG_ERROR(Core, "Missing certs.db");
return false;
}
if (specifier.type != ContentType::Application && specifier.type != ContentType::Update &&
specifier.type != ContentType::DLC) {
LOG_ERROR(Core, "Unsupported specifier type {}", static_cast<int>(specifier.type));
return false;
}
// Load TMD
const auto path = fmt::format("/title/{:08x}/{:08x}/content/", (specifier.id >> 32),
(specifier.id & 0xFFFFFFFF));
TitleMetadata tmd;
if (!LoadTMD(config.sdmc_path, path, *decryptor, tmd)) {
LOG_ERROR(Core, "Failed to load TMD from {}", path);
return false;
}
const auto physical_path = config.sdmc_path + path.substr(1);
bool ret = cia_builder->Init(destination, std::move(tmd), config.certs_db_path,
FileUtil::GetDirectoryTreeSize(physical_path), callback);
if (!ret) {
return false;
}
ret = FileUtil::ForeachDirectoryEntry(
nullptr, physical_path,
[this, path](u64* /*num_entries_out*/, const std::string& directory,
const std::string& virtual_name) {
if (FileUtil::IsDirectory(directory + virtual_name + "/")) {
return true;
}
static const std::regex app_regex{"([0-9a-f]{8})\\.app"};
std::smatch match;
if (!std::regex_match(virtual_name, match, app_regex)) {
return true;
}
ASSERT(match.size() >= 2);
const u32 id = static_cast<u32>(std::stoul(match[1], nullptr, 16));
NCCHContainer ncch(
std::make_shared<SDMCFile>(config.sdmc_path, path + virtual_name, "rb"));
return cia_builder->AddContent(id, ncch);
});
if (!ret) {
return false;
}
return cia_builder->Finalize();
}
void SDMCImporter::AbortBuildCIA() {
cia_builder->Abort();
}
void SDMCImporter::ListTitle(std::vector<ContentSpecifier>& out) const {
const auto ProcessDirectory = [this, &out, &sdmc_path = config.sdmc_path](ContentType type,
u64 high_id) {