// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "core/core.h" #include "core/file_sys/content_archive.h" #include "core/file_sys/nca_metadata.h" #include "core/file_sys/registered_cache.h" #include "core/file_sys/romfs.h" #include "core/file_sys/system_archive/system_archive.h" #include "core/file_sys/vfs.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/glue/time/time_zone_binary.h" namespace Service::Glue::Time { namespace { constexpr u64 TimeZoneBinaryId = 0x10000000000080E; static FileSys::VirtualDir g_time_zone_binary_romfs{}; static Result g_time_zone_binary_mount_result{ResultUnknown}; static std::vector g_time_zone_scratch_space(0x2800, 0); Result TimeZoneReadBinary(size_t& out_read_size, std::span out_buffer, size_t out_buffer_size, std::string_view path) { R_UNLESS(g_time_zone_binary_mount_result == ResultSuccess, g_time_zone_binary_mount_result); auto vfs_file{g_time_zone_binary_romfs->GetFileRelative(path)}; R_UNLESS(vfs_file, ResultUnknown); auto file_size{vfs_file->GetSize()}; R_UNLESS(file_size > 0, ResultUnknown); R_UNLESS(file_size <= out_buffer_size, Service::PSC::Time::ResultFailed); out_read_size = vfs_file->Read(out_buffer.data(), file_size); R_UNLESS(out_read_size > 0, ResultUnknown); R_SUCCEED(); } } // namespace void ResetTimeZoneBinary() { g_time_zone_binary_romfs = {}; g_time_zone_binary_mount_result = ResultUnknown; g_time_zone_scratch_space.clear(); g_time_zone_scratch_space.resize(0x2800, 0); } Result MountTimeZoneBinary(Core::System& system) { auto& fsc{system.GetFileSystemController()}; std::unique_ptr nca{}; auto* bis_system = fsc.GetSystemNANDContents(); R_UNLESS(bis_system, ResultUnknown); nca = bis_system->GetEntry(TimeZoneBinaryId, FileSys::ContentRecordType::Data); R_UNLESS(nca, ResultUnknown); g_time_zone_binary_romfs = FileSys::ExtractRomFS(nca->GetRomFS()); if (!g_time_zone_binary_romfs) { g_time_zone_binary_romfs = FileSys::ExtractRomFS( FileSys::SystemArchive::SynthesizeSystemArchive(TimeZoneBinaryId)); } R_UNLESS(g_time_zone_binary_romfs, ResultUnknown); g_time_zone_binary_mount_result = ResultSuccess; R_SUCCEED(); } void GetTimeZoneBinaryListPath(std::string& out_path) { if (g_time_zone_binary_mount_result != ResultSuccess) { return; } // out_path = fmt::format("{}:/binaryList.txt", "TimeZoneBinary"); out_path = "/binaryList.txt"; } void GetTimeZoneBinaryVersionPath(std::string& out_path) { if (g_time_zone_binary_mount_result != ResultSuccess) { return; } // out_path = fmt::format("{}:/version.txt", "TimeZoneBinary"); out_path = "/version.txt"; } void GetTimeZoneZonePath(std::string& out_path, Service::PSC::Time::LocationName& name) { if (g_time_zone_binary_mount_result != ResultSuccess) { return; } // out_path = fmt::format("{}:/zoneinfo/{}", "TimeZoneBinary", name); out_path = fmt::format("/zoneinfo/{}", name.name.data()); } bool IsTimeZoneBinaryValid(Service::PSC::Time::LocationName& name) { std::string path{}; GetTimeZoneZonePath(path, name); auto vfs_file{g_time_zone_binary_romfs->GetFileRelative(path)}; return vfs_file->GetSize() != 0; } u32 GetTimeZoneCount() { std::string path{}; GetTimeZoneBinaryListPath(path); size_t bytes_read{}; if (TimeZoneReadBinary(bytes_read, g_time_zone_scratch_space, 0x2800, path) != ResultSuccess) { return 0; } if (bytes_read == 0) { return 0; } auto chars = std::span(reinterpret_cast(g_time_zone_scratch_space.data()), bytes_read); u32 count{}; for (auto chr : chars) { if (chr == '\n') { count++; } } return count; } Result GetTimeZoneVersion(Service::PSC::Time::RuleVersion& out_rule_version) { std::string path{}; GetTimeZoneBinaryVersionPath(path); auto rule_version_buffer{std::span(reinterpret_cast(&out_rule_version), sizeof(Service::PSC::Time::RuleVersion))}; size_t bytes_read{}; R_TRY(TimeZoneReadBinary(bytes_read, rule_version_buffer, rule_version_buffer.size_bytes(), path)); rule_version_buffer[bytes_read] = 0; R_SUCCEED(); } Result GetTimeZoneRule(std::span& out_rule, size_t& out_rule_size, Service::PSC::Time::LocationName& name) { std::string path{}; GetTimeZoneZonePath(path, name); size_t bytes_read{}; R_TRY(TimeZoneReadBinary(bytes_read, g_time_zone_scratch_space, g_time_zone_scratch_space.size(), path)); out_rule = std::span(g_time_zone_scratch_space.data(), bytes_read); out_rule_size = bytes_read; R_SUCCEED(); } Result GetTimeZoneLocationList(u32& out_count, std::vector& out_names, size_t max_names, u32 index) { std::string path{}; GetTimeZoneBinaryListPath(path); size_t bytes_read{}; R_TRY(TimeZoneReadBinary(bytes_read, g_time_zone_scratch_space, g_time_zone_scratch_space.size(), path)); out_count = 0; R_SUCCEED_IF(bytes_read == 0); Service::PSC::Time::LocationName current_name{}; size_t current_name_len{}; std::span chars{g_time_zone_scratch_space}; u32 name_count{}; for (auto chr : chars) { if (chr == '\r') { continue; } if (chr == '\n') { if (name_count >= index) { out_names.push_back(current_name); out_count++; if (out_count >= max_names) { break; } } name_count++; current_name_len = 0; current_name = {}; continue; } if (chr == '\0') { break; } R_UNLESS(current_name_len <= current_name.name.size() - 2, Service::PSC::Time::ResultFailed); current_name.name[current_name_len++] = chr; } R_SUCCEED(); } } // namespace Service::Glue::Time