diff options
Diffstat (limited to '')
| -rw-r--r-- | src/core/hle/service/apt/apt.cpp | 286 | ||||
| -rw-r--r-- | src/core/hle/service/cfg/cfg.cpp | 2 | ||||
| -rw-r--r-- | src/core/hle/service/cfg/cfg.h | 2 |
3 files changed, 155 insertions, 135 deletions
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 58d94768c..8c0ba73f2 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp | |||
| @@ -19,6 +19,7 @@ | |||
| 19 | #include "core/hle/service/apt/apt_s.h" | 19 | #include "core/hle/service/apt/apt_s.h" |
| 20 | #include "core/hle/service/apt/apt_u.h" | 20 | #include "core/hle/service/apt/apt_u.h" |
| 21 | #include "core/hle/service/apt/bcfnt/bcfnt.h" | 21 | #include "core/hle/service/apt/bcfnt/bcfnt.h" |
| 22 | #include "core/hle/service/cfg/cfg.h" | ||
| 22 | #include "core/hle/service/fs/archive.h" | 23 | #include "core/hle/service/fs/archive.h" |
| 23 | #include "core/hle/service/ptm/ptm.h" | 24 | #include "core/hle/service/ptm/ptm.h" |
| 24 | #include "core/hle/service/service.h" | 25 | #include "core/hle/service/service.h" |
| @@ -198,6 +199,143 @@ void Initialize(Service::Interface* self) { | |||
| 198 | Kernel::g_handle_table.Create(slot_data->parameter_event).Unwrap()); | 199 | Kernel::g_handle_table.Create(slot_data->parameter_event).Unwrap()); |
| 199 | } | 200 | } |
| 200 | 201 | ||
| 202 | static u32 DecompressLZ11(const u8* in, u8* out) { | ||
| 203 | u32_le decompressed_size; | ||
| 204 | memcpy(&decompressed_size, in, sizeof(u32)); | ||
| 205 | in += 4; | ||
| 206 | |||
| 207 | u8 type = decompressed_size & 0xFF; | ||
| 208 | ASSERT(type == 0x11); | ||
| 209 | decompressed_size >>= 8; | ||
| 210 | |||
| 211 | u32 current_out_size = 0; | ||
| 212 | u8 flags = 0, mask = 1; | ||
| 213 | while (current_out_size < decompressed_size) { | ||
| 214 | if (mask == 1) { | ||
| 215 | flags = *(in++); | ||
| 216 | mask = 0x80; | ||
| 217 | } else { | ||
| 218 | mask >>= 1; | ||
| 219 | } | ||
| 220 | |||
| 221 | if (flags & mask) { | ||
| 222 | u8 byte1 = *(in++); | ||
| 223 | u32 length = byte1 >> 4; | ||
| 224 | u32 offset; | ||
| 225 | if (length == 0) { | ||
| 226 | u8 byte2 = *(in++); | ||
| 227 | u8 byte3 = *(in++); | ||
| 228 | length = (((byte1 & 0x0F) << 4) | (byte2 >> 4)) + 0x11; | ||
| 229 | offset = (((byte2 & 0x0F) << 8) | byte3) + 0x1; | ||
| 230 | } else if (length == 1) { | ||
| 231 | u8 byte2 = *(in++); | ||
| 232 | u8 byte3 = *(in++); | ||
| 233 | u8 byte4 = *(in++); | ||
| 234 | length = (((byte1 & 0x0F) << 12) | (byte2 << 4) | (byte3 >> 4)) + 0x111; | ||
| 235 | offset = (((byte3 & 0x0F) << 8) | byte4) + 0x1; | ||
| 236 | } else { | ||
| 237 | u8 byte2 = *(in++); | ||
| 238 | length = (byte1 >> 4) + 0x1; | ||
| 239 | offset = (((byte1 & 0x0F) << 8) | byte2) + 0x1; | ||
| 240 | } | ||
| 241 | |||
| 242 | for (u32 i = 0; i < length; i++) { | ||
| 243 | *out = *(out - offset); | ||
| 244 | ++out; | ||
| 245 | } | ||
| 246 | |||
| 247 | current_out_size += length; | ||
| 248 | } else { | ||
| 249 | *(out++) = *(in++); | ||
| 250 | current_out_size++; | ||
| 251 | } | ||
| 252 | } | ||
| 253 | return decompressed_size; | ||
| 254 | } | ||
| 255 | |||
| 256 | static bool LoadSharedFont() { | ||
| 257 | u8 font_region_code; | ||
| 258 | switch (CFG::GetRegionValue()) { | ||
| 259 | case 4: // CHN | ||
| 260 | font_region_code = 2; | ||
| 261 | break; | ||
| 262 | case 5: // KOR | ||
| 263 | font_region_code = 3; | ||
| 264 | break; | ||
| 265 | case 6: // TWN | ||
| 266 | font_region_code = 4; | ||
| 267 | break; | ||
| 268 | default: // JPN/EUR/USA | ||
| 269 | font_region_code = 1; | ||
| 270 | break; | ||
| 271 | } | ||
| 272 | |||
| 273 | const u64_le shared_font_archive_id_low = 0x0004009b00014002 | ((font_region_code - 1) << 8); | ||
| 274 | const u64_le shared_font_archive_id_high = 0x00000001ffffff00; | ||
| 275 | std::vector<u8> shared_font_archive_id(16); | ||
| 276 | std::memcpy(&shared_font_archive_id[0], &shared_font_archive_id_low, sizeof(u64)); | ||
| 277 | std::memcpy(&shared_font_archive_id[8], &shared_font_archive_id_high, sizeof(u64)); | ||
| 278 | FileSys::Path archive_path(shared_font_archive_id); | ||
| 279 | auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::NCCH, archive_path); | ||
| 280 | if (archive_result.Failed()) | ||
| 281 | return false; | ||
| 282 | |||
| 283 | std::vector<u8> romfs_path(20, 0); // 20-byte all zero path for opening RomFS | ||
| 284 | FileSys::Path file_path(romfs_path); | ||
| 285 | FileSys::Mode open_mode = {}; | ||
| 286 | open_mode.read_flag.Assign(1); | ||
| 287 | auto file_result = Service::FS::OpenFileFromArchive(*archive_result, file_path, open_mode); | ||
| 288 | if (file_result.Failed()) | ||
| 289 | return false; | ||
| 290 | |||
| 291 | auto romfs = std::move(file_result).Unwrap(); | ||
| 292 | std::vector<u8> romfs_buffer(romfs->backend->GetSize()); | ||
| 293 | romfs->backend->Read(0, romfs_buffer.size(), romfs_buffer.data()); | ||
| 294 | romfs->backend->Close(); | ||
| 295 | |||
| 296 | const char16_t* file_name[4] = {u"cbf_std.bcfnt.lz", u"cbf_zh-Hans-CN.bcfnt.lz", | ||
| 297 | u"cbf_ko-Hang-KR.bcfnt.lz", u"cbf_zh-Hant-TW.bcfnt.lz"}; | ||
| 298 | const u8* font_file = | ||
| 299 | RomFS::GetFilePointer(romfs_buffer.data(), {file_name[font_region_code - 1]}); | ||
| 300 | if (font_file == nullptr) | ||
| 301 | return false; | ||
| 302 | |||
| 303 | struct { | ||
| 304 | u32_le status; | ||
| 305 | u32_le region; | ||
| 306 | u32_le decompressed_size; | ||
| 307 | INSERT_PADDING_WORDS(0x1D); | ||
| 308 | } shared_font_header{}; | ||
| 309 | static_assert(sizeof(shared_font_header) == 0x80, "shared_font_header has incorrect size"); | ||
| 310 | |||
| 311 | shared_font_header.status = 2; // successfully loaded | ||
| 312 | shared_font_header.region = font_region_code; | ||
| 313 | shared_font_header.decompressed_size = | ||
| 314 | DecompressLZ11(font_file, shared_font_mem->GetPointer(0x80)); | ||
| 315 | std::memcpy(shared_font_mem->GetPointer(), &shared_font_header, sizeof(shared_font_header)); | ||
| 316 | *shared_font_mem->GetPointer(0x83) = 'U'; // Change the magic from "CFNT" to "CFNU" | ||
| 317 | |||
| 318 | return true; | ||
| 319 | } | ||
| 320 | |||
| 321 | static bool LoadLegacySharedFont() { | ||
| 322 | // This is the legacy method to load shared font. | ||
| 323 | // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header | ||
| 324 | // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided | ||
| 325 | // a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file | ||
| 326 | // "shared_font.bin" in the Citra "sysdata" directory. | ||
| 327 | std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT; | ||
| 328 | |||
| 329 | FileUtil::CreateFullPath(filepath); // Create path if not already created | ||
| 330 | FileUtil::IOFile file(filepath, "rb"); | ||
| 331 | if (file.IsOpen()) { | ||
| 332 | file.ReadBytes(shared_font_mem->GetPointer(), file.GetSize()); | ||
| 333 | return true; | ||
| 334 | } | ||
| 335 | |||
| 336 | return false; | ||
| 337 | } | ||
| 338 | |||
| 201 | void GetSharedFont(Service::Interface* self) { | 339 | void GetSharedFont(Service::Interface* self) { |
| 202 | IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x44, 0, 0); // 0x00440000 | 340 | IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x44, 0, 0); // 0x00440000 |
| 203 | IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); | 341 | IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); |
| @@ -206,11 +344,20 @@ void GetSharedFont(Service::Interface* self) { | |||
| 206 | Core::Telemetry().AddField(Telemetry::FieldType::Session, "RequiresSharedFont", true); | 344 | Core::Telemetry().AddField(Telemetry::FieldType::Session, "RequiresSharedFont", true); |
| 207 | 345 | ||
| 208 | if (!shared_font_loaded) { | 346 | if (!shared_font_loaded) { |
| 209 | LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds"); | 347 | // On real 3DS, font loading happens on booting. However, we load it on demand to coordinate |
| 210 | rb.Push<u32>(-1); // TODO: Find the right error code | 348 | // with CFG region auto configuration, which happens later than APT initialization. |
| 211 | rb.Skip(1 + 2, true); | 349 | if (LoadSharedFont()) { |
| 212 | Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSharedFont); | 350 | shared_font_loaded = true; |
| 213 | return; | 351 | } else if (LoadLegacySharedFont()) { |
| 352 | LOG_WARNING(Service_APT, "Loaded shared font by legacy method"); | ||
| 353 | shared_font_loaded = true; | ||
| 354 | } else { | ||
| 355 | LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds"); | ||
| 356 | rb.Push<u32>(-1); // TODO: Find the right error code | ||
| 357 | rb.Skip(1 + 2, true); | ||
| 358 | Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSharedFont); | ||
| 359 | return; | ||
| 360 | } | ||
| 214 | } | 361 | } |
| 215 | 362 | ||
| 216 | // The shared font has to be relocated to the new address before being passed to the | 363 | // The shared font has to be relocated to the new address before being passed to the |
| @@ -863,125 +1010,6 @@ void CheckNew3DS(Service::Interface* self) { | |||
| 863 | LOG_WARNING(Service_APT, "(STUBBED) called"); | 1010 | LOG_WARNING(Service_APT, "(STUBBED) called"); |
| 864 | } | 1011 | } |
| 865 | 1012 | ||
| 866 | static u32 DecompressLZ11(const u8* in, u8* out) { | ||
| 867 | u32_le decompressed_size; | ||
| 868 | memcpy(&decompressed_size, in, sizeof(u32)); | ||
| 869 | in += 4; | ||
| 870 | |||
| 871 | u8 type = decompressed_size & 0xFF; | ||
| 872 | ASSERT(type == 0x11); | ||
| 873 | decompressed_size >>= 8; | ||
| 874 | |||
| 875 | u32 current_out_size = 0; | ||
| 876 | u8 flags = 0, mask = 1; | ||
| 877 | while (current_out_size < decompressed_size) { | ||
| 878 | if (mask == 1) { | ||
| 879 | flags = *(in++); | ||
| 880 | mask = 0x80; | ||
| 881 | } else { | ||
| 882 | mask >>= 1; | ||
| 883 | } | ||
| 884 | |||
| 885 | if (flags & mask) { | ||
| 886 | u8 byte1 = *(in++); | ||
| 887 | u32 length = byte1 >> 4; | ||
| 888 | u32 offset; | ||
| 889 | if (length == 0) { | ||
| 890 | u8 byte2 = *(in++); | ||
| 891 | u8 byte3 = *(in++); | ||
| 892 | length = (((byte1 & 0x0F) << 4) | (byte2 >> 4)) + 0x11; | ||
| 893 | offset = (((byte2 & 0x0F) << 8) | byte3) + 0x1; | ||
| 894 | } else if (length == 1) { | ||
| 895 | u8 byte2 = *(in++); | ||
| 896 | u8 byte3 = *(in++); | ||
| 897 | u8 byte4 = *(in++); | ||
| 898 | length = (((byte1 & 0x0F) << 12) | (byte2 << 4) | (byte3 >> 4)) + 0x111; | ||
| 899 | offset = (((byte3 & 0x0F) << 8) | byte4) + 0x1; | ||
| 900 | } else { | ||
| 901 | u8 byte2 = *(in++); | ||
| 902 | length = (byte1 >> 4) + 0x1; | ||
| 903 | offset = (((byte1 & 0x0F) << 8) | byte2) + 0x1; | ||
| 904 | } | ||
| 905 | |||
| 906 | for (u32 i = 0; i < length; i++) { | ||
| 907 | *out = *(out - offset); | ||
| 908 | ++out; | ||
| 909 | } | ||
| 910 | |||
| 911 | current_out_size += length; | ||
| 912 | } else { | ||
| 913 | *(out++) = *(in++); | ||
| 914 | current_out_size++; | ||
| 915 | } | ||
| 916 | } | ||
| 917 | return decompressed_size; | ||
| 918 | } | ||
| 919 | |||
| 920 | static bool LoadSharedFont() { | ||
| 921 | // TODO (wwylele): load different font archive for region CHN/KOR/TWN | ||
| 922 | const u64_le shared_font_archive_id_low = 0x0004009b00014002; | ||
| 923 | const u64_le shared_font_archive_id_high = 0x00000001ffffff00; | ||
| 924 | std::vector<u8> shared_font_archive_id(16); | ||
| 925 | std::memcpy(&shared_font_archive_id[0], &shared_font_archive_id_low, sizeof(u64)); | ||
| 926 | std::memcpy(&shared_font_archive_id[8], &shared_font_archive_id_high, sizeof(u64)); | ||
| 927 | FileSys::Path archive_path(shared_font_archive_id); | ||
| 928 | auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::NCCH, archive_path); | ||
| 929 | if (archive_result.Failed()) | ||
| 930 | return false; | ||
| 931 | |||
| 932 | std::vector<u8> romfs_path(20, 0); // 20-byte all zero path for opening RomFS | ||
| 933 | FileSys::Path file_path(romfs_path); | ||
| 934 | FileSys::Mode open_mode = {}; | ||
| 935 | open_mode.read_flag.Assign(1); | ||
| 936 | auto file_result = Service::FS::OpenFileFromArchive(*archive_result, file_path, open_mode); | ||
| 937 | if (file_result.Failed()) | ||
| 938 | return false; | ||
| 939 | |||
| 940 | auto romfs = std::move(file_result).Unwrap(); | ||
| 941 | std::vector<u8> romfs_buffer(romfs->backend->GetSize()); | ||
| 942 | romfs->backend->Read(0, romfs_buffer.size(), romfs_buffer.data()); | ||
| 943 | romfs->backend->Close(); | ||
| 944 | |||
| 945 | const u8* font_file = RomFS::GetFilePointer(romfs_buffer.data(), {u"cbf_std.bcfnt.lz"}); | ||
| 946 | if (font_file == nullptr) | ||
| 947 | return false; | ||
| 948 | |||
| 949 | struct { | ||
| 950 | u32_le status; | ||
| 951 | u32_le region; | ||
| 952 | u32_le decompressed_size; | ||
| 953 | INSERT_PADDING_WORDS(0x1D); | ||
| 954 | } shared_font_header{}; | ||
| 955 | static_assert(sizeof(shared_font_header) == 0x80, "shared_font_header has incorrect size"); | ||
| 956 | |||
| 957 | shared_font_header.status = 2; // successfully loaded | ||
| 958 | shared_font_header.region = 1; // region JPN/EUR/USA | ||
| 959 | shared_font_header.decompressed_size = | ||
| 960 | DecompressLZ11(font_file, shared_font_mem->GetPointer(0x80)); | ||
| 961 | std::memcpy(shared_font_mem->GetPointer(), &shared_font_header, sizeof(shared_font_header)); | ||
| 962 | *shared_font_mem->GetPointer(0x83) = 'U'; // Change the magic from "CFNT" to "CFNU" | ||
| 963 | |||
| 964 | return true; | ||
| 965 | } | ||
| 966 | |||
| 967 | static bool LoadLegacySharedFont() { | ||
| 968 | // This is the legacy method to load shared font. | ||
| 969 | // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header | ||
| 970 | // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided | ||
| 971 | // a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file | ||
| 972 | // "shared_font.bin" in the Citra "sysdata" directory. | ||
| 973 | std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT; | ||
| 974 | |||
| 975 | FileUtil::CreateFullPath(filepath); // Create path if not already created | ||
| 976 | FileUtil::IOFile file(filepath, "rb"); | ||
| 977 | if (file.IsOpen()) { | ||
| 978 | file.ReadBytes(shared_font_mem->GetPointer(), file.GetSize()); | ||
| 979 | return true; | ||
| 980 | } | ||
| 981 | |||
| 982 | return false; | ||
| 983 | } | ||
| 984 | |||
| 985 | void Init() { | 1013 | void Init() { |
| 986 | AddService(new APT_A_Interface); | 1014 | AddService(new APT_A_Interface); |
| 987 | AddService(new APT_S_Interface); | 1015 | AddService(new APT_S_Interface); |
| @@ -995,16 +1023,6 @@ void Init() { | |||
| 995 | MemoryPermission::ReadWrite, MemoryPermission::Read, 0, | 1023 | MemoryPermission::ReadWrite, MemoryPermission::Read, 0, |
| 996 | Kernel::MemoryRegion::SYSTEM, "APT:SharedFont"); | 1024 | Kernel::MemoryRegion::SYSTEM, "APT:SharedFont"); |
| 997 | 1025 | ||
| 998 | if (LoadSharedFont()) { | ||
| 999 | shared_font_loaded = true; | ||
| 1000 | } else if (LoadLegacySharedFont()) { | ||
| 1001 | LOG_WARNING(Service_APT, "Loaded shared font by legacy method"); | ||
| 1002 | shared_font_loaded = true; | ||
| 1003 | } else { | ||
| 1004 | LOG_WARNING(Service_APT, "Unable to load shared font"); | ||
| 1005 | shared_font_loaded = false; | ||
| 1006 | } | ||
| 1007 | |||
| 1008 | lock = Kernel::Mutex::Create(false, "APT_U:Lock"); | 1026 | lock = Kernel::Mutex::Create(false, "APT_U:Lock"); |
| 1009 | 1027 | ||
| 1010 | cpu_percent = 0; | 1028 | cpu_percent = 0; |
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index 3dbeb27cc..f26a1f65f 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp | |||
| @@ -168,7 +168,7 @@ void GetCountryCodeID(Service::Interface* self) { | |||
| 168 | cmd_buff[2] = country_code_id; | 168 | cmd_buff[2] = country_code_id; |
| 169 | } | 169 | } |
| 170 | 170 | ||
| 171 | static u32 GetRegionValue() { | 171 | u32 GetRegionValue() { |
| 172 | if (Settings::values.region_value == Settings::REGION_VALUE_AUTO_SELECT) | 172 | if (Settings::values.region_value == Settings::REGION_VALUE_AUTO_SELECT) |
| 173 | return preferred_region_code; | 173 | return preferred_region_code; |
| 174 | 174 | ||
diff --git a/src/core/hle/service/cfg/cfg.h b/src/core/hle/service/cfg/cfg.h index 1659ebf32..282b6936b 100644 --- a/src/core/hle/service/cfg/cfg.h +++ b/src/core/hle/service/cfg/cfg.h | |||
| @@ -101,6 +101,8 @@ void GetCountryCodeString(Service::Interface* self); | |||
| 101 | */ | 101 | */ |
| 102 | void GetCountryCodeID(Service::Interface* self); | 102 | void GetCountryCodeID(Service::Interface* self); |
| 103 | 103 | ||
| 104 | u32 GetRegionValue(); | ||
| 105 | |||
| 104 | /** | 106 | /** |
| 105 | * CFG::SecureInfoGetRegion service function | 107 | * CFG::SecureInfoGetRegion service function |
| 106 | * Inputs: | 108 | * Inputs: |