mikage-dev/tools/embed_title/main.cpp
2024-03-08 10:54:13 +01:00

82 lines
3.2 KiB
C++

#include <framework/formats.hpp>
#include <platform/file_formats/ncch.hpp>
#include <range/v3/algorithm/find.hpp>
#include <boost/program_options.hpp>
#include <boost/filesystem.hpp>
#include <fmt/fmt.h>
#include <iostream>
#include <fstream>
int main(int argc, char* argv[]) {
std::string in_filename;
std::string target_path;
bool force_overwrite;
{
namespace bpo = boost::program_options;
bpo::options_description desc;
desc.add_options()
("help,h", "Print this help")
("input,i", bpo::value<std::string>(&in_filename)->required(), "Path to input NCCH file")
("base,b", bpo::value<std::string>(&target_path)->required(), "Path to target NAND tree")
("force,f", bpo::bool_switch(&force_overwrite)->default_value(false), "Force overwrite if any output path already exists");
try {
auto positional_arguments = bpo::positional_options_description{}.add("input", -1);
bpo::variables_map var_map;
bpo::store(bpo::command_line_parser(argc, argv).options(desc).positional(positional_arguments).run(),
var_map);
if (var_map.count("help") ) {
std::cout << desc << std::endl;
return 0;
}
bpo::notify(var_map);
} catch (bpo::error error) {
std::cout << desc << std::endl;
return 1;
}
}
if (!boost::filesystem::exists(target_path)) {
std::cout << "Target base directory does not exist!" << std::endl;
return 1;
}
auto [header, exheader] = Meta::invoke([&]() {
std::ifstream file(in_filename, std::ios_base::binary);
file.exceptions(std::ifstream::badbit | std::ifstream::failbit | std::ifstream::eofbit);
auto strbuf_it = std::istreambuf_iterator<char>(file.rdbuf());
auto stream = FileFormat::MakeStreamInFromContainer(strbuf_it, decltype(strbuf_it){});
auto header = FileFormat::Load<FileFormat::NCCHHeader>(stream);
// NOTE: The given exheader_size only refers to the hashed exheader region rather than the full exheader size
assert(header.exheader_size <= FileFormat::ExHeader::Tags::expected_serialized_size);
auto exheader = FileFormat::Load<FileFormat::ExHeader>(stream);
return std::make_tuple(header, exheader);
});
//exheader.application_title;
std::string title(exheader.application_title.begin(), exheader.application_title.end());
title.resize(ranges::find(title, '\0') - ranges::begin(title));
if (title.empty())
title = "(no_title)";
uint32_t titleid_high = header.program_id >> 32;
uint32_t titleid_low = static_cast<uint32_t>(header.program_id);
auto out_filename = boost::filesystem::path(target_path);
out_filename += boost::filesystem::path(fmt::format("./{:08x}/{:08x}/content/", titleid_high, titleid_low));
boost::filesystem::create_directories(out_filename);
out_filename += boost::filesystem::path("./00000000.cxi");
boost::filesystem::copy(in_filename, out_filename);
std::cout << fmt::format("{:08x} {:08x} {} {}", titleid_high, titleid_low, title, out_filename.c_str()) << std::endl;
}