|
|
|
@ -19,6 +19,7 @@ |
|
|
|
#include "core/hle/service/apt/apt_s.h"
|
|
|
|
#include "core/hle/service/apt/apt_u.h"
|
|
|
|
#include "core/hle/service/apt/bcfnt/bcfnt.h"
|
|
|
|
#include "core/hle/service/cfg/cfg.h"
|
|
|
|
#include "core/hle/service/fs/archive.h"
|
|
|
|
#include "core/hle/service/ptm/ptm.h"
|
|
|
|
#include "core/hle/service/service.h"
|
|
|
|
@ -198,6 +199,143 @@ void Initialize(Service::Interface* self) { |
|
|
|
Kernel::g_handle_table.Create(slot_data->parameter_event).Unwrap()); |
|
|
|
} |
|
|
|
|
|
|
|
static u32 DecompressLZ11(const u8* in, u8* out) { |
|
|
|
u32_le decompressed_size; |
|
|
|
memcpy(&decompressed_size, in, sizeof(u32)); |
|
|
|
in += 4; |
|
|
|
|
|
|
|
u8 type = decompressed_size & 0xFF; |
|
|
|
ASSERT(type == 0x11); |
|
|
|
decompressed_size >>= 8; |
|
|
|
|
|
|
|
u32 current_out_size = 0; |
|
|
|
u8 flags = 0, mask = 1; |
|
|
|
while (current_out_size < decompressed_size) { |
|
|
|
if (mask == 1) { |
|
|
|
flags = *(in++); |
|
|
|
mask = 0x80; |
|
|
|
} else { |
|
|
|
mask >>= 1; |
|
|
|
} |
|
|
|
|
|
|
|
if (flags & mask) { |
|
|
|
u8 byte1 = *(in++); |
|
|
|
u32 length = byte1 >> 4; |
|
|
|
u32 offset; |
|
|
|
if (length == 0) { |
|
|
|
u8 byte2 = *(in++); |
|
|
|
u8 byte3 = *(in++); |
|
|
|
length = (((byte1 & 0x0F) << 4) | (byte2 >> 4)) + 0x11; |
|
|
|
offset = (((byte2 & 0x0F) << 8) | byte3) + 0x1; |
|
|
|
} else if (length == 1) { |
|
|
|
u8 byte2 = *(in++); |
|
|
|
u8 byte3 = *(in++); |
|
|
|
u8 byte4 = *(in++); |
|
|
|
length = (((byte1 & 0x0F) << 12) | (byte2 << 4) | (byte3 >> 4)) + 0x111; |
|
|
|
offset = (((byte3 & 0x0F) << 8) | byte4) + 0x1; |
|
|
|
} else { |
|
|
|
u8 byte2 = *(in++); |
|
|
|
length = (byte1 >> 4) + 0x1; |
|
|
|
offset = (((byte1 & 0x0F) << 8) | byte2) + 0x1; |
|
|
|
} |
|
|
|
|
|
|
|
for (u32 i = 0; i < length; i++) { |
|
|
|
*out = *(out - offset); |
|
|
|
++out; |
|
|
|
} |
|
|
|
|
|
|
|
current_out_size += length; |
|
|
|
} else { |
|
|
|
*(out++) = *(in++); |
|
|
|
current_out_size++; |
|
|
|
} |
|
|
|
} |
|
|
|
return decompressed_size; |
|
|
|
} |
|
|
|
|
|
|
|
static bool LoadSharedFont() { |
|
|
|
u8 font_region_code; |
|
|
|
switch (CFG::GetRegionValue()) { |
|
|
|
case 4: // CHN
|
|
|
|
font_region_code = 2; |
|
|
|
break; |
|
|
|
case 5: // KOR
|
|
|
|
font_region_code = 3; |
|
|
|
break; |
|
|
|
case 6: // TWN
|
|
|
|
font_region_code = 4; |
|
|
|
break; |
|
|
|
default: // JPN/EUR/USA
|
|
|
|
font_region_code = 1; |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
const u64_le shared_font_archive_id_low = 0x0004009b00014002 | ((font_region_code - 1) << 8); |
|
|
|
const u64_le shared_font_archive_id_high = 0x00000001ffffff00; |
|
|
|
std::vector<u8> shared_font_archive_id(16); |
|
|
|
std::memcpy(&shared_font_archive_id[0], &shared_font_archive_id_low, sizeof(u64)); |
|
|
|
std::memcpy(&shared_font_archive_id[8], &shared_font_archive_id_high, sizeof(u64)); |
|
|
|
FileSys::Path archive_path(shared_font_archive_id); |
|
|
|
auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::NCCH, archive_path); |
|
|
|
if (archive_result.Failed()) |
|
|
|
return false; |
|
|
|
|
|
|
|
std::vector<u8> romfs_path(20, 0); // 20-byte all zero path for opening RomFS
|
|
|
|
FileSys::Path file_path(romfs_path); |
|
|
|
FileSys::Mode open_mode = {}; |
|
|
|
open_mode.read_flag.Assign(1); |
|
|
|
auto file_result = Service::FS::OpenFileFromArchive(*archive_result, file_path, open_mode); |
|
|
|
if (file_result.Failed()) |
|
|
|
return false; |
|
|
|
|
|
|
|
auto romfs = std::move(file_result).Unwrap(); |
|
|
|
std::vector<u8> romfs_buffer(romfs->backend->GetSize()); |
|
|
|
romfs->backend->Read(0, romfs_buffer.size(), romfs_buffer.data()); |
|
|
|
romfs->backend->Close(); |
|
|
|
|
|
|
|
const char16_t* file_name[4] = {u"cbf_std.bcfnt.lz", u"cbf_zh-Hans-CN.bcfnt.lz", |
|
|
|
u"cbf_ko-Hang-KR.bcfnt.lz", u"cbf_zh-Hant-TW.bcfnt.lz"}; |
|
|
|
const u8* font_file = |
|
|
|
RomFS::GetFilePointer(romfs_buffer.data(), {file_name[font_region_code - 1]}); |
|
|
|
if (font_file == nullptr) |
|
|
|
return false; |
|
|
|
|
|
|
|
struct { |
|
|
|
u32_le status; |
|
|
|
u32_le region; |
|
|
|
u32_le decompressed_size; |
|
|
|
INSERT_PADDING_WORDS(0x1D); |
|
|
|
} shared_font_header{}; |
|
|
|
static_assert(sizeof(shared_font_header) == 0x80, "shared_font_header has incorrect size"); |
|
|
|
|
|
|
|
shared_font_header.status = 2; // successfully loaded
|
|
|
|
shared_font_header.region = font_region_code; |
|
|
|
shared_font_header.decompressed_size = |
|
|
|
DecompressLZ11(font_file, shared_font_mem->GetPointer(0x80)); |
|
|
|
std::memcpy(shared_font_mem->GetPointer(), &shared_font_header, sizeof(shared_font_header)); |
|
|
|
*shared_font_mem->GetPointer(0x83) = 'U'; // Change the magic from "CFNT" to "CFNU"
|
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
static bool LoadLegacySharedFont() { |
|
|
|
// This is the legacy method to load shared font.
|
|
|
|
// The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header
|
|
|
|
// generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided
|
|
|
|
// a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file
|
|
|
|
// "shared_font.bin" in the Citra "sysdata" directory.
|
|
|
|
std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT; |
|
|
|
|
|
|
|
FileUtil::CreateFullPath(filepath); // Create path if not already created
|
|
|
|
FileUtil::IOFile file(filepath, "rb"); |
|
|
|
if (file.IsOpen()) { |
|
|
|
file.ReadBytes(shared_font_mem->GetPointer(), file.GetSize()); |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
void GetSharedFont(Service::Interface* self) { |
|
|
|
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x44, 0, 0); // 0x00440000
|
|
|
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); |
|
|
|
@ -206,11 +344,20 @@ void GetSharedFont(Service::Interface* self) { |
|
|
|
Core::Telemetry().AddField(Telemetry::FieldType::Session, "RequiresSharedFont", true); |
|
|
|
|
|
|
|
if (!shared_font_loaded) { |
|
|
|
LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds"); |
|
|
|
rb.Push<u32>(-1); // TODO: Find the right error code
|
|
|
|
rb.Skip(1 + 2, true); |
|
|
|
Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSharedFont); |
|
|
|
return; |
|
|
|
// On real 3DS, font loading happens on booting. However, we load it on demand to coordinate
|
|
|
|
// with CFG region auto configuration, which happens later than APT initialization.
|
|
|
|
if (LoadSharedFont()) { |
|
|
|
shared_font_loaded = true; |
|
|
|
} else if (LoadLegacySharedFont()) { |
|
|
|
LOG_WARNING(Service_APT, "Loaded shared font by legacy method"); |
|
|
|
shared_font_loaded = true; |
|
|
|
} else { |
|
|
|
LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds"); |
|
|
|
rb.Push<u32>(-1); // TODO: Find the right error code
|
|
|
|
rb.Skip(1 + 2, true); |
|
|
|
Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSharedFont); |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 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) { |
|
|
|
LOG_WARNING(Service_APT, "(STUBBED) called"); |
|
|
|
} |
|
|
|
|
|
|
|
static u32 DecompressLZ11(const u8* in, u8* out) { |
|
|
|
u32_le decompressed_size; |
|
|
|
memcpy(&decompressed_size, in, sizeof(u32)); |
|
|
|
in += 4; |
|
|
|
|
|
|
|
u8 type = decompressed_size & 0xFF; |
|
|
|
ASSERT(type == 0x11); |
|
|
|
decompressed_size >>= 8; |
|
|
|
|
|
|
|
u32 current_out_size = 0; |
|
|
|
u8 flags = 0, mask = 1; |
|
|
|
while (current_out_size < decompressed_size) { |
|
|
|
if (mask == 1) { |
|
|
|
flags = *(in++); |
|
|
|
mask = 0x80; |
|
|
|
} else { |
|
|
|
mask >>= 1; |
|
|
|
} |
|
|
|
|
|
|
|
if (flags & mask) { |
|
|
|
u8 byte1 = *(in++); |
|
|
|
u32 length = byte1 >> 4; |
|
|
|
u32 offset; |
|
|
|
if (length == 0) { |
|
|
|
u8 byte2 = *(in++); |
|
|
|
u8 byte3 = *(in++); |
|
|
|
length = (((byte1 & 0x0F) << 4) | (byte2 >> 4)) + 0x11; |
|
|
|
offset = (((byte2 & 0x0F) << 8) | byte3) + 0x1; |
|
|
|
} else if (length == 1) { |
|
|
|
u8 byte2 = *(in++); |
|
|
|
u8 byte3 = *(in++); |
|
|
|
u8 byte4 = *(in++); |
|
|
|
length = (((byte1 & 0x0F) << 12) | (byte2 << 4) | (byte3 >> 4)) + 0x111; |
|
|
|
offset = (((byte3 & 0x0F) << 8) | byte4) + 0x1; |
|
|
|
} else { |
|
|
|
u8 byte2 = *(in++); |
|
|
|
length = (byte1 >> 4) + 0x1; |
|
|
|
offset = (((byte1 & 0x0F) << 8) | byte2) + 0x1; |
|
|
|
} |
|
|
|
|
|
|
|
for (u32 i = 0; i < length; i++) { |
|
|
|
*out = *(out - offset); |
|
|
|
++out; |
|
|
|
} |
|
|
|
|
|
|
|
current_out_size += length; |
|
|
|
} else { |
|
|
|
*(out++) = *(in++); |
|
|
|
current_out_size++; |
|
|
|
} |
|
|
|
} |
|
|
|
return decompressed_size; |
|
|
|
} |
|
|
|
|
|
|
|
static bool LoadSharedFont() { |
|
|
|
// TODO (wwylele): load different font archive for region CHN/KOR/TWN
|
|
|
|
const u64_le shared_font_archive_id_low = 0x0004009b00014002; |
|
|
|
const u64_le shared_font_archive_id_high = 0x00000001ffffff00; |
|
|
|
std::vector<u8> shared_font_archive_id(16); |
|
|
|
std::memcpy(&shared_font_archive_id[0], &shared_font_archive_id_low, sizeof(u64)); |
|
|
|
std::memcpy(&shared_font_archive_id[8], &shared_font_archive_id_high, sizeof(u64)); |
|
|
|
FileSys::Path archive_path(shared_font_archive_id); |
|
|
|
auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::NCCH, archive_path); |
|
|
|
if (archive_result.Failed()) |
|
|
|
return false; |
|
|
|
|
|
|
|
std::vector<u8> romfs_path(20, 0); // 20-byte all zero path for opening RomFS
|
|
|
|
FileSys::Path file_path(romfs_path); |
|
|
|
FileSys::Mode open_mode = {}; |
|
|
|
open_mode.read_flag.Assign(1); |
|
|
|
auto file_result = Service::FS::OpenFileFromArchive(*archive_result, file_path, open_mode); |
|
|
|
if (file_result.Failed()) |
|
|
|
return false; |
|
|
|
|
|
|
|
auto romfs = std::move(file_result).Unwrap(); |
|
|
|
std::vector<u8> romfs_buffer(romfs->backend->GetSize()); |
|
|
|
romfs->backend->Read(0, romfs_buffer.size(), romfs_buffer.data()); |
|
|
|
romfs->backend->Close(); |
|
|
|
|
|
|
|
const u8* font_file = RomFS::GetFilePointer(romfs_buffer.data(), {u"cbf_std.bcfnt.lz"}); |
|
|
|
if (font_file == nullptr) |
|
|
|
return false; |
|
|
|
|
|
|
|
struct { |
|
|
|
u32_le status; |
|
|
|
u32_le region; |
|
|
|
u32_le decompressed_size; |
|
|
|
INSERT_PADDING_WORDS(0x1D); |
|
|
|
} shared_font_header{}; |
|
|
|
static_assert(sizeof(shared_font_header) == 0x80, "shared_font_header has incorrect size"); |
|
|
|
|
|
|
|
shared_font_header.status = 2; // successfully loaded
|
|
|
|
shared_font_header.region = 1; // region JPN/EUR/USA
|
|
|
|
shared_font_header.decompressed_size = |
|
|
|
DecompressLZ11(font_file, shared_font_mem->GetPointer(0x80)); |
|
|
|
std::memcpy(shared_font_mem->GetPointer(), &shared_font_header, sizeof(shared_font_header)); |
|
|
|
*shared_font_mem->GetPointer(0x83) = 'U'; // Change the magic from "CFNT" to "CFNU"
|
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
static bool LoadLegacySharedFont() { |
|
|
|
// This is the legacy method to load shared font.
|
|
|
|
// The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header
|
|
|
|
// generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided
|
|
|
|
// a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file
|
|
|
|
// "shared_font.bin" in the Citra "sysdata" directory.
|
|
|
|
std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT; |
|
|
|
|
|
|
|
FileUtil::CreateFullPath(filepath); // Create path if not already created
|
|
|
|
FileUtil::IOFile file(filepath, "rb"); |
|
|
|
if (file.IsOpen()) { |
|
|
|
file.ReadBytes(shared_font_mem->GetPointer(), file.GetSize()); |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
void Init() { |
|
|
|
AddService(new APT_A_Interface); |
|
|
|
AddService(new APT_S_Interface); |
|
|
|
@ -995,16 +1023,6 @@ void Init() { |
|
|
|
MemoryPermission::ReadWrite, MemoryPermission::Read, 0, |
|
|
|
Kernel::MemoryRegion::SYSTEM, "APT:SharedFont"); |
|
|
|
|
|
|
|
if (LoadSharedFont()) { |
|
|
|
shared_font_loaded = true; |
|
|
|
} else if (LoadLegacySharedFont()) { |
|
|
|
LOG_WARNING(Service_APT, "Loaded shared font by legacy method"); |
|
|
|
shared_font_loaded = true; |
|
|
|
} else { |
|
|
|
LOG_WARNING(Service_APT, "Unable to load shared font"); |
|
|
|
shared_font_loaded = false; |
|
|
|
} |
|
|
|
|
|
|
|
lock = Kernel::Mutex::Create(false, "APT_U:Lock"); |
|
|
|
|
|
|
|
cpu_percent = 0; |
|
|
|
|