Use Config Savegame to determine system language and change display title accordingly

This commit is contained in:
Pengfei
2021-09-01 18:29:35 +08:00
parent 101b5b7cee
commit 34d352b3cc
7 changed files with 145 additions and 5 deletions
+2
View File
@@ -12,6 +12,8 @@ add_library(core STATIC
file_sys/certificate.cpp
file_sys/certificate.h
file_sys/cia_common.h
file_sys/config_savegame.cpp
file_sys/config_savegame.h
file_sys/data/data_container.cpp
file_sys/data/data_container.h
file_sys/data/extdata.cpp
+37
View File
@@ -0,0 +1,37 @@
// Copyright 2021 Pengfei Zhu
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cstring>
#include "common/logging/log.h"
#include "core/file_sys/config_savegame.h"
namespace Core {
ConfigSavegame::ConfigSavegame() = default;
ConfigSavegame::~ConfigSavegame() = default;
bool ConfigSavegame::Init(std::vector<u8> data) {
if (data.size() != ConfigSavegameSize) {
LOG_ERROR(Core, "Config savegame is of incorrect size");
return false;
}
std::memcpy(&header, data.data(), sizeof(header));
return true;
}
u8 ConfigSavegame::GetSystemLanguage() const {
static constexpr u32 LanguageBlockID = 0x000A0002;
const auto iter = std::find_if(
header.block_entries.begin(), header.block_entries.end(),
[](const ConfigSavegameBlockEntry& entry) { return entry.block_id == LanguageBlockID; });
if (iter == header.block_entries.end()) {
LOG_ERROR(Core, "Cannot find Language config block, returning default (English)");
return 1;
}
return static_cast<u8>(iter->offset_or_data);
}
} // namespace Core
+48
View File
@@ -0,0 +1,48 @@
// Copyright 2014 Citra Emulator Project / 2021 Pengfei Zhu
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include "common/swap.h"
namespace Core {
constexpr u32 ConfigSavegameSize = 0x8000;
/// The maximum number of block entries that can exist in the config file
constexpr u32 ConfigSavegameMaxEntries = 1479;
/// Block header in the config savedata file
struct ConfigSavegameBlockEntry {
u32_le block_id; ///< The id of the current block
u32_le offset_or_data; ///< This is the absolute offset to the block data if the size is greater
/// than 4 bytes, otherwise it contains the data itself
u16_le size; ///< The size of the block
u16_le flags; ///< The flags of the block, possibly used for access control
};
struct ConfigSavegameHeader {
u16_le total_entries; ///< The total number of set entries in the config file
u16_le data_entries_offset; ///< The offset where the data for the blocks start
/// The block headers, the maximum possible value is 1479 as per hardware
std::array<ConfigSavegameBlockEntry, ConfigSavegameMaxEntries> block_entries;
u32_le unknown; ///< This field is unknown, possibly padding, 0 has been observed in hardware
};
static_assert(sizeof(ConfigSavegameHeader) == 0x455C,
"Config Savegame header must be exactly 0x455C bytes");
// This is not the config savegame itself, but rather the `config` file inside.
class ConfigSavegame {
public:
explicit ConfigSavegame();
~ConfigSavegame();
bool Init(std::vector<u8> data);
u8 GetSystemLanguage() const;
private:
ConfigSavegameHeader header;
};
} // namespace Core
+1
View File
@@ -25,6 +25,7 @@ private:
friend class Archive<Savegame>;
friend class InnerFAT<Savegame>;
friend class SDMCImporter; // Needed to read config savegame
};
} // namespace Core
+47 -4
View File
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include <map>
#include <regex>
#include <cryptopp/sha.h>
@@ -14,6 +15,7 @@
#include "core/db/seed_db.h"
#include "core/db/title_db.h"
#include "core/file_sys/certificate.h"
#include "core/file_sys/config_savegame.h"
#include "core/file_sys/data/data_container.h"
#include "core/file_sys/data/extdata.h"
#include "core/file_sys/data/savegame.h"
@@ -76,6 +78,8 @@ bool SDMCImporter::Init() {
}
}
LoadSystemLanguage();
// Create children
sdmc_decryptor = std::make_unique<SDMCDecryptor>(config.sdmc_path);
cia_builder = std::make_unique<CIABuilder>(config, ticket_db);
@@ -94,6 +98,41 @@ bool SDMCImporter::Init() {
return true;
}
void SDMCImporter::LoadSystemLanguage() {
FileUtil::IOFile file(nand_config.data_path + "sysdata/00010017/00000000", "rb");
if (!file) {
LOG_ERROR(Core, "Could not open config savegame");
return;
}
DataContainer container(file.GetData());
std::vector<std::vector<u8>> raw_data;
if (!container.IsGood() || !container.GetIVFCLevel4Data(raw_data)) {
return;
}
Savegame save(raw_data);
// Find index of the 'config' file
for (std::size_t i = 0; i < save.file_entry_table.size(); ++i) {
if (std::strncmp(save.file_entry_table[i].name.data(), "config", 16) != 0) {
continue;
}
// Load 'config' file
std::vector<u8> data;
if (!save.GetFileData(data, i)) {
return;
}
ConfigSavegame config;
if (!config.Init(std::move(data))) {
return;
}
system_language = static_cast<SMDH::TitleLanguage>(config.GetSystemLanguage());
break;
}
}
bool SDMCImporter::IsGood() const {
return is_good;
}
@@ -438,7 +477,7 @@ struct TitleData {
u64 extdata_id;
std::vector<u16> icon;
};
static TitleData LoadTitleData(NCCHContainer& ncch) {
static TitleData LoadTitleData(NCCHContainer& ncch, SMDH::TitleLanguage language) {
static const std::unordered_map<u64, const char*> NamedTitles{{
// System Applications (to avoid confusion)
{0x00040010'2002c800, "New 3DS HOME Menu manual (JPN)"},
@@ -501,7 +540,11 @@ static TitleData LoadTitleData(NCCHContainer& ncch) {
SMDH smdh;
std::memcpy(&smdh, smdh_buffer.data(), smdh_buffer.size());
if (!NamedTitles.count(program_id)) { // Name was not overridden
title_name = Common::UTF16BufferToUTF8(smdh.GetShortTitle(SMDH::TitleLanguage::English));
title_name = Common::UTF16BufferToUTF8(smdh.GetShortTitle(language));
if (title_name.empty()) { // No title in the language for some reason
title_name =
Common::UTF16BufferToUTF8(smdh.GetShortTitle(SMDH::TitleLanguage::English));
}
}
return TitleData{std::move(title_name), extdata_id, smdh.GetIcon(false)};
}
@@ -803,7 +846,7 @@ void SDMCImporter::ListTitle(std::vector<ContentSpecifier>& out) const {
break;
}
const auto& [name, extdata_id, icon] = LoadTitleData(ncch);
const auto& [name, extdata_id, icon] = LoadTitleData(ncch, system_language);
const auto size =
FileUtil::GetDirectoryTreeSize(directory + virtual_name + "/content/") +
TitleSizeAllowance;
@@ -879,7 +922,7 @@ void SDMCImporter::ListNandTitle(std::vector<ContentSpecifier>& out) const {
break;
}
const auto& [name, extdata_id, icon] = LoadTitleData(ncch);
const auto& [name, extdata_id, icon] = LoadTitleData(ncch, system_language);
const auto size =
FileUtil::GetDirectoryTreeSize(directory + virtual_name + "/content/") +
TitleSizeAllowance;
+9
View File
@@ -13,6 +13,7 @@
#include "common/progress_callback.h"
#include "core/file_decryptor.h"
#include "core/file_sys/cia_common.h"
#include "core/file_sys/smdh.h"
namespace Core {
@@ -196,8 +197,13 @@ public:
return ticket_db;
}
SMDH::TitleLanguage GetSystemLanguage() const {
return system_language;
}
private:
bool Init();
void LoadSystemLanguage();
// Impl of ImportContent without deleting mechanism.
bool ImportContentImpl(
@@ -230,6 +236,9 @@ private:
bool is_good{};
Config config;
Config::NandConfig nand_config; // Main NAND config
// System language, determined from config savegame. Used to return the title's names.
SMDH::TitleLanguage system_language{SMDH::TitleLanguage::English};
std::unique_ptr<SDMCDecryptor> sdmc_decryptor;
FileDecryptor file_decryptor;
+1 -1
View File
@@ -145,7 +145,7 @@ void TitleInfoDialog::InitializeLanguageComboBox() {
}
ui->languageComboBox->addItem(tr(LanguageNames.at(i)), static_cast<int>(i));
if (i == 1) { // English
if (i == static_cast<u8>(importer.GetSystemLanguage())) {
ui->languageComboBox->setCurrentIndex(ui->languageComboBox->count() - 1);
}
}