mirror of
https://github.com/Dark98/threeSD.git
synced 2026-07-02 16:49:04 +00:00
Use Config Savegame to determine system language and change display title accordingly
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user