diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/hle/service/ns/pl_u.cpp | 165 |
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 | ||
| 11 | namespace Service::NS { | 13 | namespace Service::NS { |
| 12 | 14 | ||
| 15 | enum class FontArchives : u64 { | ||
| 16 | Extension = 0x0100000000000810, | ||
| 17 | Standard = 0x0100000000000811, | ||
| 18 | Korean = 0x0100000000000812, | ||
| 19 | ChineseTraditional = 0x0100000000000813, | ||
| 20 | ChineseSimple = 0x0100000000000814, | ||
| 21 | }; | ||
| 22 | |||
| 13 | struct FontRegion { | 23 | struct FontRegion { |
| 14 | u32 offset; | 24 | u32 offset; |
| 15 | u32 size; | 25 | u32 size; |
| 16 | }; | 26 | }; |
| 17 | 27 | ||
| 28 | static 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 |
| 20 | static constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL}; | 39 | static constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL}; |
| 40 | static constexpr u32 EXPECTED_RESULT{ | ||
| 41 | 0x7f9a0218}; // What we expect the decrypted bfttf first 4 bytes to be | ||
| 42 | static constexpr u32 EXPECTED_MAGIC{ | ||
| 43 | 0x36f81a1e}; // What we expect the encrypted bfttf first 4 bytes to be | ||
| 21 | static constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000}; | 44 | static constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000}; |
| 22 | static constexpr std::array<FontRegion, 6> SHARED_FONT_REGIONS{ | 45 | static constexpr FontRegion EMPTY_REGION{0, 0}; |
| 23 | FontRegion{0x00000008, 0x001fe764}, FontRegion{0x001fe774, 0x00773e58}, | 46 | std::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 | }; | 49 | const 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 | ||
| 28 | enum class LoadState : u32 { | 57 | enum class LoadState : u32 { |
| 29 | Loading = 0, | 58 | Loading = 0, |
| 30 | Done = 1, | 59 | Done = 1, |
| 31 | }; | 60 | }; |
| 32 | 61 | ||
| 62 | void 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 | |||
| 78 | static 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 | |||
| 84 | void 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 | |||
| 33 | PL_U::PL_U() : ServiceFramework("pl:u") { | 99 | PL_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 | ||
| 59 | void PL_U::RequestLoad(Kernel::HLERequestContext& ctx) { | 177 | void 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 | ||
| 88 | void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { | 206 | void 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 | ||
| 98 | void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { | 216 | void 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); |