mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-15 21:19:47 +00:00
7758f70833
For reasons I am not knowledgeable enough to answer, NACP language and nn::settings::Language don't match. PR #3587 however erroneously matched language_index directly to NACP language which caused those weird mismatches (since idx 2 = French actually matches up to an NACP language of Japanese). So to get around that just go back to the mapping of language idx -> nacp language Signed-off-by: crueter <crueter@eden-emu.dev> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3629 Reviewed-by: MaranBr <maranbr@eden-emu.dev> Reviewed-by: DraVee <dravee@eden-emu.dev>
216 lines
6.8 KiB
C++
216 lines
6.8 KiB
C++
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include <array>
|
|
#include <cstddef>
|
|
#include <cstring>
|
|
#include <limits>
|
|
#include <span>
|
|
#include <zlib.h>
|
|
|
|
#include "common/settings.h"
|
|
#include "common/settings_enums.h"
|
|
#include "common/string_util.h"
|
|
#include "common/swap.h"
|
|
#include "core/file_sys/control_metadata.h"
|
|
#include "core/file_sys/vfs/vfs.h"
|
|
|
|
namespace FileSys {
|
|
|
|
const std::array<const char*, size_t(Language::Count)> LANGUAGE_NAMES{{
|
|
"AmericanEnglish",
|
|
"BritishEnglish",
|
|
"Japanese",
|
|
"French",
|
|
"German",
|
|
"LatinAmericanSpanish",
|
|
"Spanish",
|
|
"Italian",
|
|
"Dutch",
|
|
"CanadianFrench",
|
|
"Portuguese",
|
|
"Russian",
|
|
"Korean",
|
|
"TraditionalChinese",
|
|
"SimplifiedChinese",
|
|
"BrazilianPortuguese",
|
|
"Polish",
|
|
"Thai",
|
|
}};
|
|
|
|
namespace
|
|
{
|
|
constexpr std::size_t MAX_EXPANDED_LANG_SIZE = sizeof(LanguageEntry) * 32;
|
|
|
|
|
|
bool InflateRawDeflate(std::span<const u8> compressed, std::vector<u8>& out)
|
|
{
|
|
if (compressed.empty()) return false;
|
|
|
|
z_stream stream{};
|
|
stream.next_in = const_cast<Bytef*>(reinterpret_cast<const Bytef*>(compressed.data()));
|
|
stream.avail_in = static_cast<uInt>(compressed.size());
|
|
if (inflateInit2(&stream, -MAX_WBITS) != Z_OK) {
|
|
return false;
|
|
}
|
|
|
|
out.resize(MAX_EXPANDED_LANG_SIZE);
|
|
stream.next_out = reinterpret_cast<Bytef*>(out.data());
|
|
stream.avail_out = static_cast<uInt>(out.size());
|
|
|
|
int ret = inflate(&stream, Z_FINISH);
|
|
inflateEnd(&stream);
|
|
|
|
if (ret != Z_STREAM_END && ret != Z_OK) {
|
|
return false;
|
|
}
|
|
|
|
// Shrink to actual decompressed size
|
|
out.resize(stream.total_out);
|
|
return true;
|
|
}
|
|
} // namespace
|
|
|
|
std::string LanguageEntry::GetApplicationName() const {
|
|
return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(), application_name.size());
|
|
}
|
|
|
|
std::string LanguageEntry::GetDeveloperName() const {
|
|
return Common::StringFromFixedZeroTerminatedBuffer(developer_name.data(), developer_name.size());
|
|
}
|
|
|
|
NACP::NACP() = default;
|
|
|
|
NACP::NACP(VirtualFile file)
|
|
{
|
|
file->ReadObject(&raw);
|
|
if (raw.titles_data_format == TitleDataFormat::Compressed) {
|
|
const u16 compressed_size = raw.language_entries.compressed_data.buffer_size;
|
|
std::span<const u8> compressed_payload{raw.language_entries.compressed_data.buffer,
|
|
compressed_size};
|
|
|
|
std::vector<u8> decompressed;
|
|
if (InflateRawDeflate(compressed_payload, decompressed)) {
|
|
const size_t entry_count = decompressed.size() / sizeof(LanguageEntry);
|
|
language_entries.resize(entry_count);
|
|
std::memcpy(language_entries.data(), decompressed.data(), decompressed.size());
|
|
}
|
|
} else {
|
|
language_entries.resize(16);
|
|
std::memcpy(language_entries.data(), raw.language_entries.language_entries.data(),
|
|
sizeof(raw.language_entries.language_entries));
|
|
}
|
|
}
|
|
|
|
NACP::~NACP() = default;
|
|
|
|
const LanguageEntry& NACP::GetLanguageEntry() const {
|
|
|
|
auto const language = []{
|
|
switch (Settings::values.language_index.GetValue()) {
|
|
case Settings::Language::Chinese: return Language::SimplifiedChinese;
|
|
case Settings::Language::ChineseSimplified: return Language::SimplifiedChinese;
|
|
case Settings::Language::ChineseTraditional: return Language::TraditionalChinese;
|
|
case Settings::Language::Dutch: return Language::Dutch;
|
|
case Settings::Language::EnglishAmerican: return Language::AmericanEnglish;
|
|
case Settings::Language::EnglishBritish: return Language::BritishEnglish;
|
|
case Settings::Language::French: return Language::French;
|
|
case Settings::Language::FrenchCanadian: return Language::CanadianFrench;
|
|
case Settings::Language::German: return Language::German;
|
|
case Settings::Language::Italian: return Language::Italian;
|
|
case Settings::Language::Korean: return Language::Korean;
|
|
case Settings::Language::Japanese: return Language::Japanese;
|
|
case Settings::Language::Portuguese: return Language::Portuguese;
|
|
case Settings::Language::PortugueseBrazilian: return Language::BrazilianPortuguese;
|
|
case Settings::Language::Russian: return Language::Russian;
|
|
case Settings::Language::Spanish: return Language::Spanish;
|
|
case Settings::Language::SpanishLatin: return Language::LatinAmericanSpanish;
|
|
case Settings::Language::Taiwanese: return Language::SimplifiedChinese;
|
|
case Settings::Language::Thai: return Language::Thai;
|
|
case Settings::Language::Polish: return Language::Polish;
|
|
default: return Language::AmericanEnglish;
|
|
}
|
|
}();
|
|
|
|
u32 index = u32(language);
|
|
|
|
if (index < language_entries.size() && !language_entries[index].GetApplicationName().empty()) {
|
|
return language_entries[index];
|
|
}
|
|
|
|
for (const auto& entry : language_entries) {
|
|
if (!entry.GetApplicationName().empty())
|
|
return entry;
|
|
}
|
|
|
|
return language_entries.at(static_cast<u8>(Language::AmericanEnglish));
|
|
}
|
|
|
|
std::vector<std::string> NACP::GetApplicationNames() const {
|
|
std::vector<std::string> names;
|
|
names.reserve(language_entries.size());
|
|
for (const auto& entry : language_entries) {
|
|
names.push_back(entry.GetApplicationName());
|
|
}
|
|
return names;
|
|
}
|
|
|
|
std::string NACP::GetApplicationName() const {
|
|
return GetLanguageEntry().GetApplicationName();
|
|
}
|
|
|
|
std::string NACP::GetDeveloperName() const {
|
|
return GetLanguageEntry().GetDeveloperName();
|
|
}
|
|
|
|
u64 NACP::GetTitleId() const {
|
|
return raw.save_data_owner_id;
|
|
}
|
|
|
|
u64 NACP::GetDLCBaseTitleId() const {
|
|
return raw.dlc_base_title_id;
|
|
}
|
|
|
|
std::string NACP::GetVersionString() const {
|
|
return Common::StringFromFixedZeroTerminatedBuffer(raw.version_string.data(),
|
|
raw.version_string.size());
|
|
}
|
|
|
|
u64 NACP::GetDefaultNormalSaveSize() const {
|
|
return raw.user_account_save_data_size;
|
|
}
|
|
|
|
u64 NACP::GetDefaultJournalSaveSize() const {
|
|
return raw.user_account_save_data_journal_size;
|
|
}
|
|
|
|
bool NACP::GetUserAccountSwitchLock() const {
|
|
return raw.user_account_switch_lock != 0;
|
|
}
|
|
|
|
u32 NACP::GetSupportedLanguages() const {
|
|
return u32(raw.supported_languages);
|
|
}
|
|
|
|
u64 NACP::GetDeviceSaveDataSize() const {
|
|
return raw.device_save_data_size;
|
|
}
|
|
|
|
u32 NACP::GetParentalControlFlag() const {
|
|
return raw.parental_control;
|
|
}
|
|
|
|
const std::array<u8, 0x20>& NACP::GetRatingAge() const {
|
|
return raw.rating_age;
|
|
}
|
|
|
|
std::vector<u8> NACP::GetRawBytes() const {
|
|
std::vector<u8> out(sizeof(RawNACP));
|
|
std::memcpy(out.data(), &raw, sizeof(RawNACP));
|
|
return out;
|
|
}
|
|
} // namespace FileSys
|