From 99fc32428a598541a7b8c22fa92c26e75081f9f1 Mon Sep 17 00:00:00 2001 From: David <25727384+ogniK5377@users.noreply.github.com> Date: Wed, 22 Aug 2018 11:31:49 +1000 Subject: PL:U Added BFTTF loading(Loading from System NAND dumps) (#1088) * Added bfttf loading We can now load system bfttf fonts from system archives AND shared memory dumps. This allows people who have installed their system nand dumps to yuzu to automatically get shared font support. We also now don't hard code the offsets or the sizes of the shared fonts and it's all calculated for us now. * Addressed plu fixups * Style changes for plu * Fixed logic error for plu and added more error checks. --- src/core/hle/service/ns/pl_u.cpp | 165 +++++++++++++++++++++++++++++++++------ 1 file changed, 140 insertions(+), 25 deletions(-) diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp index bad27894a..fd11e79da 100644 --- a/src/core/hle/service/ns/pl_u.cpp +++ b/src/core/hle/service/ns/pl_u.cpp @@ -5,31 +5,97 @@ #include "common/common_paths.h" #include "common/file_util.h" #include "core/core.h" +#include "core/file_sys/romfs.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/ns/pl_u.h" namespace Service::NS { +enum class FontArchives : u64 { + Extension = 0x0100000000000810, + Standard = 0x0100000000000811, + Korean = 0x0100000000000812, + ChineseTraditional = 0x0100000000000813, + ChineseSimple = 0x0100000000000814, +}; + struct FontRegion { u32 offset; u32 size; }; +static constexpr std::array, 7> SHARED_FONTS{ + std::make_pair(FontArchives::Standard, "nintendo_udsg-r_std_003.bfttf"), + std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_org_zh-cn_003.bfttf"), + std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_ext_zh-cn_003.bfttf"), + std::make_pair(FontArchives::ChineseTraditional, "nintendo_udjxh-db_zh-tw_003.bfttf"), + std::make_pair(FontArchives::Korean, "nintendo_udsg-r_ko_003.bfttf"), + std::make_pair(FontArchives::Extension, "nintendo_ext_003.bfttf"), + std::make_pair(FontArchives::Extension, "nintendo_ext2_003.bfttf")}; + // The below data is specific to shared font data dumped from Switch on f/w 2.2 // Virtual address and offsets/sizes likely will vary by dump static constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL}; +static constexpr u32 EXPECTED_RESULT{ + 0x7f9a0218}; // What we expect the decrypted bfttf first 4 bytes to be +static constexpr u32 EXPECTED_MAGIC{ + 0x36f81a1e}; // What we expect the encrypted bfttf first 4 bytes to be static constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000}; -static constexpr std::array SHARED_FONT_REGIONS{ - FontRegion{0x00000008, 0x001fe764}, FontRegion{0x001fe774, 0x00773e58}, - FontRegion{0x009725d4, 0x0001aca8}, FontRegion{0x0098d284, 0x00369cec}, - FontRegion{0x00cf6f78, 0x0039b858}, FontRegion{0x010927d8, 0x00019e80}, -}; +static constexpr FontRegion EMPTY_REGION{0, 0}; +std::vector + SHARED_FONT_REGIONS{}; // Automatically populated based on shared_fonts dump or system archives + +const FontRegion& GetSharedFontRegion(size_t index) { + if (index >= SHARED_FONT_REGIONS.size() || SHARED_FONT_REGIONS.empty()) { + // No font fallback + return EMPTY_REGION; + } + return SHARED_FONT_REGIONS.at(index); +} enum class LoadState : u32 { Loading = 0, Done = 1, }; +void DecryptSharedFont(const std::vector& input, std::vector& output, size_t& offset) { + ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE, + "Shared fonts exceeds 17mb!"); + ASSERT_MSG(input[0] == EXPECTED_MAGIC, "Failed to derive key, unexpected magic number"); + + const u32 KEY = input[0] ^ EXPECTED_RESULT; // Derive key using an inverse xor + std::vector transformed_font(input.size()); + // TODO(ogniK): Figure out a better way to do this + std::transform(input.begin(), input.end(), transformed_font.begin(), + [&KEY](u32 font_data) { return Common::swap32(font_data ^ KEY); }); + transformed_font[1] = Common::swap32(transformed_font[1]) ^ KEY; // "re-encrypt" the size + std::memcpy(output.data() + offset, transformed_font.data(), + transformed_font.size() * sizeof(u32)); + offset += transformed_font.size() * sizeof(u32); +} + +static u32 GetU32Swapped(const u8* data) { + u32 value; + std::memcpy(&value, data, sizeof(value)); + return Common::swap32(value); // Helper function to make BuildSharedFontsRawRegions a bit nicer +} + +void BuildSharedFontsRawRegions(const std::vector& input) { + unsigned cur_offset = 0; // As we can derive the xor key we can just populate the offsets based + // on the shared memory dump + for (size_t i = 0; i < SHARED_FONTS.size(); i++) { + // Out of shared fonts/Invalid font + if (GetU32Swapped(input.data() + cur_offset) != EXPECTED_RESULT) + break; + const u32 KEY = GetU32Swapped(input.data() + cur_offset) ^ + EXPECTED_MAGIC; // Derive key withing inverse xor + const u32 SIZE = GetU32Swapped(input.data() + cur_offset + 4) ^ KEY; + SHARED_FONT_REGIONS.push_back(FontRegion{cur_offset + 8, SIZE}); + cur_offset += SIZE + 8; + } +} + PL_U::PL_U() : ServiceFramework("pl:u") { static const FunctionInfo functions[] = { {0, &PL_U::RequestLoad, "RequestLoad"}, @@ -40,26 +106,78 @@ PL_U::PL_U() : ServiceFramework("pl:u") { {5, &PL_U::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"}, }; RegisterHandlers(functions); - // Attempt to load shared font data from disk - const std::string filepath{FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir) + SHARED_FONT}; - FileUtil::CreateFullPath(filepath); // Create path if not already created - FileUtil::IOFile file(filepath, "rb"); - - shared_font = std::make_shared>(SHARED_FONT_MEM_SIZE); - if (file.IsOpen()) { - // Read shared font data - ASSERT(file.GetSize() == SHARED_FONT_MEM_SIZE); - file.ReadBytes(shared_font->data(), shared_font->size()); + const auto nand = FileSystem::GetSystemNANDContents(); + // Rebuild shared fonts from data ncas + if (nand->HasEntry(static_cast(FontArchives::Standard), + FileSys::ContentRecordType::Data)) { + size_t offset = 0; + shared_font = std::make_shared>(SHARED_FONT_MEM_SIZE); + for (auto font : SHARED_FONTS) { + const auto nca = + nand->GetEntry(static_cast(font.first), FileSys::ContentRecordType::Data); + if (!nca) { + LOG_ERROR(Service_NS, "Failed to find {:016X}! Skipping", + static_cast(font.first)); + continue; + } + const auto romfs = nca->GetRomFS(); + if (!romfs) { + LOG_ERROR(Service_NS, "{:016X} has no RomFS! Skipping", + static_cast(font.first)); + continue; + } + const auto extracted_romfs = FileSys::ExtractRomFS(romfs); + if (!extracted_romfs) { + LOG_ERROR(Service_NS, "Failed to extract RomFS for {:016X}! Skipping", + static_cast(font.first)); + continue; + } + const auto font_fp = extracted_romfs->GetFile(font.second); + if (!font_fp) { + LOG_ERROR(Service_NS, "{:016X} has no file \"{}\"! Skipping", + static_cast(font.first), font.second); + continue; + } + std::vector font_data_u32(font_fp->GetSize() / sizeof(u32)); + font_fp->ReadBytes(font_data_u32.data(), font_fp->GetSize()); + // We need to be BigEndian as u32s for the xor encryption + std::transform(font_data_u32.begin(), font_data_u32.end(), font_data_u32.begin(), + Common::swap32); + FontRegion region{ + static_cast(offset + 8), + static_cast((font_data_u32.size() * sizeof(u32)) - + 8)}; // Font offset and size do not account for the header + DecryptSharedFont(font_data_u32, *shared_font, offset); + SHARED_FONT_REGIONS.push_back(region); + } } else { - LOG_WARNING(Service_NS, "Unable to load shared font: {}", filepath); + const std::string filepath{FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir) + + SHARED_FONT}; + // Create path if not already created + if (!FileUtil::CreateFullPath(filepath)) { + LOG_ERROR(Service_NS, "Failed to create sharedfonts path \"{}\"!", filepath); + return; + } + FileUtil::IOFile file(filepath, "rb"); + + shared_font = std::make_shared>( + SHARED_FONT_MEM_SIZE); // Shared memory needs to always be allocated and a fixed size + if (file.IsOpen()) { + // Read shared font data + ASSERT(file.GetSize() == SHARED_FONT_MEM_SIZE); + file.ReadBytes(shared_font->data(), shared_font->size()); + BuildSharedFontsRawRegions(*shared_font); + } else { + LOG_WARNING(Service_NS, "Unable to load shared font: {}", filepath); + } } } void PL_U::RequestLoad(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u32 shared_font_type{rp.Pop()}; - + // Games don't call this so all fonts should be loaded LOG_DEBUG(Service_NS, "called, shared_font_type={}", shared_font_type); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -82,7 +200,7 @@ void PL_U::GetSize(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NS, "called, font_id={}", font_id); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push(SHARED_FONT_REGIONS[font_id].size); + rb.Push(GetSharedFontRegion(font_id).size); } void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { @@ -92,14 +210,10 @@ void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NS, "called, font_id={}", font_id); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push(SHARED_FONT_REGIONS[font_id].offset); + rb.Push(GetSharedFontRegion(font_id).offset); } void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { - // TODO(bunnei): This is a less-than-ideal solution to load a RAM dump of the Switch shared - // font data. This (likely) relies on exact address, size, and offsets from the original - // dump. In the future, we need to replace this with a more robust solution. - // Map backing memory for the font data Core::CurrentProcess()->vm_manager.MapMemoryBlock( SHARED_FONT_MEM_VADDR, shared_font, 0, SHARED_FONT_MEM_SIZE, Kernel::MemoryState::Shared); @@ -128,8 +242,9 @@ void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) { // TODO(ogniK): Have actual priority order for (size_t i = 0; i < SHARED_FONT_REGIONS.size(); i++) { font_codes.push_back(static_cast(i)); - font_offsets.push_back(SHARED_FONT_REGIONS[i].offset); - font_sizes.push_back(SHARED_FONT_REGIONS[i].size); + auto region = GetSharedFontRegion(i); + font_offsets.push_back(region.offset); + font_sizes.push_back(region.size); } ctx.WriteBuffer(font_codes, 0); -- cgit v1.2.3