Add system archives import

This commit is contained in:
zhupengfei
2019-10-02 16:00:32 +08:00
parent 9f9d1d85db
commit 5d675d605a
7 changed files with 204 additions and 14 deletions
+75
View File
@@ -72,6 +72,8 @@ bool SDMCImporter::ImportContent(const ContentSpecifier& specifier,
return ImportSavegame(specifier.id, callback);
case ContentType::Extdata:
return ImportExtdata(specifier.id, callback);
case ContentType::SystemArchive:
return ImportSystemArchive(specifier.id, callback);
case ContentType::Sysdata:
return ImportSysdata(specifier.id, callback);
default:
@@ -128,6 +130,45 @@ bool SDMCImporter::ImportExtdata(u64 id, [[maybe_unused]] const ProgressCallback
"Nintendo 3DS/00000000000000000000000000000000/00000000000000000000000000000000/" + path);
}
bool SDMCImporter::ImportSystemArchive(u64 id, [[maybe_unused]] const ProgressCallback& callback) {
const auto path = fmt::format("{}{:08x}/{:08x}.app", config.system_archives_path, (id >> 32),
(id & 0xFFFFFFFF));
FileUtil::IOFile file(path, "rb");
if (!file) {
LOG_ERROR(Core, "Could not open {}", path);
return false;
}
std::vector<u8> data(file.GetSize());
if (file.ReadBytes(data.data(), data.size()) != data.size()) {
LOG_ERROR(Core, "Failed to read from {}", path);
return false;
}
const auto& romfs = LoadSharedRomFS(data);
const auto target_path = fmt::format(
"{}00000000000000000000000000000000/title/{:08x}/{:08x}/content/00000000.app.romfs",
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), (id >> 32), (id & 0xFFFFFFFF));
if (!FileUtil::CreateFullPath(target_path)) {
LOG_ERROR(Core, "Could not create path {}", target_path);
return false;
}
FileUtil::IOFile target(target_path, "wb");
if (!target) {
LOG_ERROR(Core, "Could not open {}", target_path);
return false;
}
if (target.WriteBytes(romfs.data(), romfs.size()) != romfs.size()) {
LOG_ERROR(Core, "Failed to write to {}", target_path);
return false;
}
return true;
}
bool SDMCImporter::ImportSysdata(u64 id, [[maybe_unused]] const ProgressCallback& callback) {
switch (id) {
case 0: { // boot9.bin
@@ -207,6 +248,7 @@ std::vector<ContentSpecifier> SDMCImporter::ListContent() const {
std::vector<ContentSpecifier> content_list;
ListTitle(content_list);
ListExtdata(content_list);
ListSystemArchive(content_list);
ListSysdata(content_list);
return content_list;
}
@@ -361,6 +403,30 @@ void SDMCImporter::ListExtdata(std::vector<ContentSpecifier>& out) const {
});
}
void SDMCImporter::ListSystemArchive(std::vector<ContentSpecifier>& out) const {
constexpr std::array<std::pair<u64, const char*>, 8> SystemArchives{{
{0x0004009b'00010202, "Mii Data"},
{0x0004009b'00010402, "Region Manifest"},
{0x0004009b'00014002, "Shared Font (JPN/EUR/USA)"},
{0x0004009b'00014102, "Shared Font (CHN)"},
{0x0004009b'00014202, "Shared Font (KOR)"},
{0x0004009b'00014302, "Shared Font (TWN)"},
{0x000400db'00010302, "Bad word list"},
}};
for (const auto& [id, name] : SystemArchives) {
const auto path = fmt::format("{}{:08x}/{:08x}.app", config.system_archives_path,
(id >> 32), (id & 0xFFFFFFFF));
if (FileUtil::Exists(path)) {
const auto target_path = fmt::format(
"{}00000000000000000000000000000000/title/{:08x}/{:08x}/content/",
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), (id >> 32), (id & 0xFFFFFFFF));
out.push_back({ContentType::SystemArchive, id, FileUtil::Exists(target_path),
FileUtil::GetSize(path), name});
}
}
}
void SDMCImporter::ListSysdata(std::vector<ContentSpecifier>& out) const {
#define CHECK_CONTENT(id, var_path, citra_path, display_name) \
if (!var_path.empty()) { \
@@ -418,6 +484,8 @@ void SDMCImporter::DeleteContent(const ContentSpecifier& specifier) {
return DeleteSavegame(specifier.id);
case ContentType::Extdata:
return DeleteExtdata(specifier.id);
case ContentType::SystemArchive:
return DeleteSystemArchive(specifier.id);
case ContentType::Sysdata:
return DeleteSysdata(specifier.id);
default:
@@ -449,6 +517,12 @@ void SDMCImporter::DeleteExtdata(u64 id) const {
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), (id >> 32), (id & 0xFFFFFFFF)));
}
void SDMCImporter::DeleteSystemArchive(u64 id) const {
FileUtil::DeleteDirRecursively(fmt::format(
"{}00000000000000000000000000000000/title/{:08x}/{:08x}/content/",
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), (id >> 32), (id & 0xFFFFFFFF)));
}
void SDMCImporter::DeleteSysdata(u64 id) const {
switch (id) {
case 0: { // boot9.bin
@@ -501,6 +575,7 @@ std::vector<Config> LoadPresetConfig(std::string mount_point) {
LOAD_DATA(safe_mode_firm_path, "firm/");
LOAD_DATA(seed_db_path, SEED_DB);
LOAD_DATA(secret_sector_path, SECRET_SECTOR);
LOAD_DATA(system_archives_path, "sysarchives/");
#undef LOAD_DATA
}
+8 -3
View File
@@ -23,6 +23,7 @@ enum class ContentType {
DLC,
Savegame,
Extdata,
SystemArchive,
Sysdata,
};
@@ -53,9 +54,10 @@ struct Config {
// The following system files are optional for importing and are only copied so that Citra
// will be able to decrypt imported encrypted ROMs.
std::string safe_mode_firm_path; ///< Path to safe mode firm (A folder) (Sysdata 1)
std::string seed_db_path; ///< Path to seeddb.bin (Sysdata 2)
std::string secret_sector_path; ///< Path to secret sector (New3DS only) (Sysdata 3)
std::string safe_mode_firm_path; ///< Path to safe mode firm (A folder) (Sysdata 1)
std::string seed_db_path; ///< Path to seeddb.bin (Sysdata 2)
std::string secret_sector_path; ///< Path to secret sector (New3DS only) (Sysdata 3)
std::string system_archives_path; ///< Path to system archives.
// Sysdata 4 is aes_keys.db (slot0x25KeyX)
};
@@ -108,15 +110,18 @@ private:
bool ImportTitle(u64 id, const ProgressCallback& callback);
bool ImportSavegame(u64 id, const ProgressCallback& callback);
bool ImportExtdata(u64 id, const ProgressCallback& callback);
bool ImportSystemArchive(u64 id, const ProgressCallback& callback);
bool ImportSysdata(u64 id, const ProgressCallback& callback);
void ListTitle(std::vector<ContentSpecifier>& out) const;
void ListExtdata(std::vector<ContentSpecifier>& out) const;
void ListSystemArchive(std::vector<ContentSpecifier>& out) const;
void ListSysdata(std::vector<ContentSpecifier>& out) const;
void DeleteTitle(u64 id) const;
void DeleteSavegame(u64 id) const;
void DeleteExtdata(u64 id) const;
void DeleteSystemArchive(u64 id) const;
void DeleteSysdata(u64 id) const;
/**
+41
View File
@@ -3,13 +3,18 @@
// Refer to the license.txt file included.
#include <cinttypes>
#include <cmath>
#include <cstring>
#include <memory>
#include <cryptopp/aes.h>
#include <cryptopp/modes.h>
#include <cryptopp/sha.h>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/data_container.h"
#include "core/key/key.h"
#include "core/ncch/ncch_container.h"
@@ -304,4 +309,40 @@ bool NCCHContainer::HasExHeader() {
return has_exheader;
}
#pragma pack(push, 1)
struct RomFSIVFCHeader {
u32_le magic;
u32_le version;
u32_le master_hash_size;
std::array<LevelDescriptor, 3> levels;
INSERT_PADDING_BYTES(0xC);
};
static_assert(sizeof(RomFSIVFCHeader) == 0x60, "Size of RomFSIVFCHeader is incorrect");
#pragma pack(pop)
std::vector<u8> LoadSharedRomFS(const std::vector<u8>& data) {
NCCH_Header header;
ASSERT_MSG(data.size() >= sizeof(header), "NCCH size is too small");
std::memcpy(&header, data.data(), sizeof(header));
const std::size_t offset = header.romfs_offset * 0x200; // 0x200: Media unit
RomFSIVFCHeader ivfc;
ASSERT_MSG(data.size() >= offset + sizeof(ivfc), "NCCH size is too small");
std::memcpy(&ivfc, data.data() + offset, sizeof(ivfc));
ASSERT_MSG(ivfc.magic == MakeMagic('I', 'V', 'F', 'C'), "IVFC magic is incorrect");
ASSERT_MSG(ivfc.version == 0x10000, "IVFC version is incorrect");
std::vector<u8> result(ivfc.levels[2].size);
// Calculation from ctrtool
const std::size_t data_offset =
offset + Common::AlignUp(sizeof(ivfc) + ivfc.master_hash_size,
std::pow(2, ivfc.levels[2].block_size));
ASSERT_MSG(data.size() >= data_offset + ivfc.levels[2].size);
std::memcpy(result.data(), data.data() + data_offset, ivfc.levels[2].size);
return result;
}
} // namespace Core
+6
View File
@@ -277,4 +277,10 @@ private:
SDMCFile exefs_file;
};
/**
* Extracts the shared RomFS from a NCCH image.
* Used for handling system archives.
*/
std::vector<u8> LoadSharedRomFS(const std::vector<u8>& data);
} // namespace Core