summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar David2018-08-22 11:31:49 +1000
committerGravatar bunnei2018-08-21 21:31:49 -0400
commit99fc32428a598541a7b8c22fa92c26e75081f9f1 (patch)
tree842f53857a2dabdffc680e0497b139ab90f8d198 /src
parentMerge pull request #1145 from lioncash/fwd-decl (diff)
downloadyuzu-99fc32428a598541a7b8c22fa92c26e75081f9f1.tar.gz
yuzu-99fc32428a598541a7b8c22fa92c26e75081f9f1.tar.xz
yuzu-99fc32428a598541a7b8c22fa92c26e75081f9f1.zip
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.
Diffstat (limited to 'src')
-rw-r--r--src/core/hle/service/ns/pl_u.cpp165
1 files 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 @@
5#include "common/common_paths.h" 5#include "common/common_paths.h"
6#include "common/file_util.h" 6#include "common/file_util.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/file_sys/romfs.h"
8#include "core/hle/ipc_helpers.h" 9#include "core/hle/ipc_helpers.h"
10#include "core/hle/service/filesystem/filesystem.h"
9#include "core/hle/service/ns/pl_u.h" 11#include "core/hle/service/ns/pl_u.h"
10 12
11namespace Service::NS { 13namespace Service::NS {
12 14
15enum class FontArchives : u64 {
16 Extension = 0x0100000000000810,
17 Standard = 0x0100000000000811,
18 Korean = 0x0100000000000812,
19 ChineseTraditional = 0x0100000000000813,
20 ChineseSimple = 0x0100000000000814,
21};
22
13struct FontRegion { 23struct FontRegion {
14 u32 offset; 24 u32 offset;
15 u32 size; 25 u32 size;
16}; 26};
17 27
28static constexpr std::array<std::pair<FontArchives, const char*>, 7> SHARED_FONTS{
29 std::make_pair(FontArchives::Standard, "nintendo_udsg-r_std_003.bfttf"),
30 std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_org_zh-cn_003.bfttf"),
31 std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_ext_zh-cn_003.bfttf"),
32 std::make_pair(FontArchives::ChineseTraditional, "nintendo_udjxh-db_zh-tw_003.bfttf"),
33 std::make_pair(FontArchives::Korean, "nintendo_udsg-r_ko_003.bfttf"),
34 std::make_pair(FontArchives::Extension, "nintendo_ext_003.bfttf"),
35 std::make_pair(FontArchives::Extension, "nintendo_ext2_003.bfttf")};
36
18// The below data is specific to shared font data dumped from Switch on f/w 2.2 37// The below data is specific to shared font data dumped from Switch on f/w 2.2
19// Virtual address and offsets/sizes likely will vary by dump 38// Virtual address and offsets/sizes likely will vary by dump
20static constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL}; 39static constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL};
40static constexpr u32 EXPECTED_RESULT{
41 0x7f9a0218}; // What we expect the decrypted bfttf first 4 bytes to be
42static constexpr u32 EXPECTED_MAGIC{
43 0x36f81a1e}; // What we expect the encrypted bfttf first 4 bytes to be
21static constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000}; 44static constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000};
22static constexpr std::array<FontRegion, 6> SHARED_FONT_REGIONS{ 45static constexpr FontRegion EMPTY_REGION{0, 0};
23 FontRegion{0x00000008, 0x001fe764}, FontRegion{0x001fe774, 0x00773e58}, 46std::vector<FontRegion>
24 FontRegion{0x009725d4, 0x0001aca8}, FontRegion{0x0098d284, 0x00369cec}, 47 SHARED_FONT_REGIONS{}; // Automatically populated based on shared_fonts dump or system archives
25 FontRegion{0x00cf6f78, 0x0039b858}, FontRegion{0x010927d8, 0x00019e80}, 48
26}; 49const FontRegion& GetSharedFontRegion(size_t index) {
50 if (index >= SHARED_FONT_REGIONS.size() || SHARED_FONT_REGIONS.empty()) {
51 // No font fallback
52 return EMPTY_REGION;
53 }
54 return SHARED_FONT_REGIONS.at(index);
55}
27 56
28enum class LoadState : u32 { 57enum class LoadState : u32 {
29 Loading = 0, 58 Loading = 0,
30 Done = 1, 59 Done = 1,
31}; 60};
32 61
62void DecryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, size_t& offset) {
63 ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE,
64 "Shared fonts exceeds 17mb!");
65 ASSERT_MSG(input[0] == EXPECTED_MAGIC, "Failed to derive key, unexpected magic number");
66
67 const u32 KEY = input[0] ^ EXPECTED_RESULT; // Derive key using an inverse xor
68 std::vector<u32> transformed_font(input.size());
69 // TODO(ogniK): Figure out a better way to do this
70 std::transform(input.begin(), input.end(), transformed_font.begin(),
71 [&KEY](u32 font_data) { return Common::swap32(font_data ^ KEY); });
72 transformed_font[1] = Common::swap32(transformed_font[1]) ^ KEY; // "re-encrypt" the size
73 std::memcpy(output.data() + offset, transformed_font.data(),
74 transformed_font.size() * sizeof(u32));
75 offset += transformed_font.size() * sizeof(u32);
76}
77
78static u32 GetU32Swapped(const u8* data) {
79 u32 value;
80 std::memcpy(&value, data, sizeof(value));
81 return Common::swap32(value); // Helper function to make BuildSharedFontsRawRegions a bit nicer
82}
83
84void BuildSharedFontsRawRegions(const std::vector<u8>& input) {
85 unsigned cur_offset = 0; // As we can derive the xor key we can just populate the offsets based
86 // on the shared memory dump
87 for (size_t i = 0; i < SHARED_FONTS.size(); i++) {
88 // Out of shared fonts/Invalid font
89 if (GetU32Swapped(input.data() + cur_offset) != EXPECTED_RESULT)
90 break;
91 const u32 KEY = GetU32Swapped(input.data() + cur_offset) ^
92 EXPECTED_MAGIC; // Derive key withing inverse xor
93 const u32 SIZE = GetU32Swapped(input.data() + cur_offset + 4) ^ KEY;
94 SHARED_FONT_REGIONS.push_back(FontRegion{cur_offset + 8, SIZE});
95 cur_offset += SIZE + 8;
96 }
97}
98
33PL_U::PL_U() : ServiceFramework("pl:u") { 99PL_U::PL_U() : ServiceFramework("pl:u") {
34 static const FunctionInfo functions[] = { 100 static const FunctionInfo functions[] = {
35 {0, &PL_U::RequestLoad, "RequestLoad"}, 101 {0, &PL_U::RequestLoad, "RequestLoad"},
@@ -40,26 +106,78 @@ PL_U::PL_U() : ServiceFramework("pl:u") {
40 {5, &PL_U::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"}, 106 {5, &PL_U::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"},
41 }; 107 };
42 RegisterHandlers(functions); 108 RegisterHandlers(functions);
43
44 // Attempt to load shared font data from disk 109 // Attempt to load shared font data from disk
45 const std::string filepath{FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir) + SHARED_FONT}; 110 const auto nand = FileSystem::GetSystemNANDContents();
46 FileUtil::CreateFullPath(filepath); // Create path if not already created 111 // Rebuild shared fonts from data ncas
47 FileUtil::IOFile file(filepath, "rb"); 112 if (nand->HasEntry(static_cast<u64>(FontArchives::Standard),
48 113 FileSys::ContentRecordType::Data)) {
49 shared_font = std::make_shared<std::vector<u8>>(SHARED_FONT_MEM_SIZE); 114 size_t offset = 0;
50 if (file.IsOpen()) { 115 shared_font = std::make_shared<std::vector<u8>>(SHARED_FONT_MEM_SIZE);
51 // Read shared font data 116 for (auto font : SHARED_FONTS) {
52 ASSERT(file.GetSize() == SHARED_FONT_MEM_SIZE); 117 const auto nca =
53 file.ReadBytes(shared_font->data(), shared_font->size()); 118 nand->GetEntry(static_cast<u64>(font.first), FileSys::ContentRecordType::Data);
119 if (!nca) {
120 LOG_ERROR(Service_NS, "Failed to find {:016X}! Skipping",
121 static_cast<u64>(font.first));
122 continue;
123 }
124 const auto romfs = nca->GetRomFS();
125 if (!romfs) {
126 LOG_ERROR(Service_NS, "{:016X} has no RomFS! Skipping",
127 static_cast<u64>(font.first));
128 continue;
129 }
130 const auto extracted_romfs = FileSys::ExtractRomFS(romfs);
131 if (!extracted_romfs) {
132 LOG_ERROR(Service_NS, "Failed to extract RomFS for {:016X}! Skipping",
133 static_cast<u64>(font.first));
134 continue;
135 }
136 const auto font_fp = extracted_romfs->GetFile(font.second);
137 if (!font_fp) {
138 LOG_ERROR(Service_NS, "{:016X} has no file \"{}\"! Skipping",
139 static_cast<u64>(font.first), font.second);
140 continue;
141 }
142 std::vector<u32> font_data_u32(font_fp->GetSize() / sizeof(u32));
143 font_fp->ReadBytes<u32>(font_data_u32.data(), font_fp->GetSize());
144 // We need to be BigEndian as u32s for the xor encryption
145 std::transform(font_data_u32.begin(), font_data_u32.end(), font_data_u32.begin(),
146 Common::swap32);
147 FontRegion region{
148 static_cast<u32>(offset + 8),
149 static_cast<u32>((font_data_u32.size() * sizeof(u32)) -
150 8)}; // Font offset and size do not account for the header
151 DecryptSharedFont(font_data_u32, *shared_font, offset);
152 SHARED_FONT_REGIONS.push_back(region);
153 }
54 } else { 154 } else {
55 LOG_WARNING(Service_NS, "Unable to load shared font: {}", filepath); 155 const std::string filepath{FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir) +
156 SHARED_FONT};
157 // Create path if not already created
158 if (!FileUtil::CreateFullPath(filepath)) {
159 LOG_ERROR(Service_NS, "Failed to create sharedfonts path \"{}\"!", filepath);
160 return;
161 }
162 FileUtil::IOFile file(filepath, "rb");
163
164 shared_font = std::make_shared<std::vector<u8>>(
165 SHARED_FONT_MEM_SIZE); // Shared memory needs to always be allocated and a fixed size
166 if (file.IsOpen()) {
167 // Read shared font data
168 ASSERT(file.GetSize() == SHARED_FONT_MEM_SIZE);
169 file.ReadBytes(shared_font->data(), shared_font->size());
170 BuildSharedFontsRawRegions(*shared_font);
171 } else {
172 LOG_WARNING(Service_NS, "Unable to load shared font: {}", filepath);
173 }
56 } 174 }
57} 175}
58 176
59void PL_U::RequestLoad(Kernel::HLERequestContext& ctx) { 177void PL_U::RequestLoad(Kernel::HLERequestContext& ctx) {
60 IPC::RequestParser rp{ctx}; 178 IPC::RequestParser rp{ctx};
61 const u32 shared_font_type{rp.Pop<u32>()}; 179 const u32 shared_font_type{rp.Pop<u32>()};
62 180 // Games don't call this so all fonts should be loaded
63 LOG_DEBUG(Service_NS, "called, shared_font_type={}", shared_font_type); 181 LOG_DEBUG(Service_NS, "called, shared_font_type={}", shared_font_type);
64 IPC::ResponseBuilder rb{ctx, 2}; 182 IPC::ResponseBuilder rb{ctx, 2};
65 rb.Push(RESULT_SUCCESS); 183 rb.Push(RESULT_SUCCESS);
@@ -82,7 +200,7 @@ void PL_U::GetSize(Kernel::HLERequestContext& ctx) {
82 LOG_DEBUG(Service_NS, "called, font_id={}", font_id); 200 LOG_DEBUG(Service_NS, "called, font_id={}", font_id);
83 IPC::ResponseBuilder rb{ctx, 3}; 201 IPC::ResponseBuilder rb{ctx, 3};
84 rb.Push(RESULT_SUCCESS); 202 rb.Push(RESULT_SUCCESS);
85 rb.Push<u32>(SHARED_FONT_REGIONS[font_id].size); 203 rb.Push<u32>(GetSharedFontRegion(font_id).size);
86} 204}
87 205
88void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { 206void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) {
@@ -92,14 +210,10 @@ void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) {
92 LOG_DEBUG(Service_NS, "called, font_id={}", font_id); 210 LOG_DEBUG(Service_NS, "called, font_id={}", font_id);
93 IPC::ResponseBuilder rb{ctx, 3}; 211 IPC::ResponseBuilder rb{ctx, 3};
94 rb.Push(RESULT_SUCCESS); 212 rb.Push(RESULT_SUCCESS);
95 rb.Push<u32>(SHARED_FONT_REGIONS[font_id].offset); 213 rb.Push<u32>(GetSharedFontRegion(font_id).offset);
96} 214}
97 215
98void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { 216void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
99 // TODO(bunnei): This is a less-than-ideal solution to load a RAM dump of the Switch shared
100 // font data. This (likely) relies on exact address, size, and offsets from the original
101 // dump. In the future, we need to replace this with a more robust solution.
102
103 // Map backing memory for the font data 217 // Map backing memory for the font data
104 Core::CurrentProcess()->vm_manager.MapMemoryBlock( 218 Core::CurrentProcess()->vm_manager.MapMemoryBlock(
105 SHARED_FONT_MEM_VADDR, shared_font, 0, SHARED_FONT_MEM_SIZE, Kernel::MemoryState::Shared); 219 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) {
128 // TODO(ogniK): Have actual priority order 242 // TODO(ogniK): Have actual priority order
129 for (size_t i = 0; i < SHARED_FONT_REGIONS.size(); i++) { 243 for (size_t i = 0; i < SHARED_FONT_REGIONS.size(); i++) {
130 font_codes.push_back(static_cast<u32>(i)); 244 font_codes.push_back(static_cast<u32>(i));
131 font_offsets.push_back(SHARED_FONT_REGIONS[i].offset); 245 auto region = GetSharedFontRegion(i);
132 font_sizes.push_back(SHARED_FONT_REGIONS[i].size); 246 font_offsets.push_back(region.offset);
247 font_sizes.push_back(region.size);
133 } 248 }
134 249
135 ctx.WriteBuffer(font_codes, 0); 250 ctx.WriteBuffer(font_codes, 0);