summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorGravatar liamwhite2024-01-25 14:19:01 -0500
committerGravatar GitHub2024-01-25 14:19:01 -0500
commitd45561ace069024f47ed710d1165b607644d1ec3 (patch)
treea316f59c5a722dc15fe5c49b3641d9801c264970 /src/core
parentMerge pull request #12781 from goldenx86/dozen (diff)
parentRework time service to fix time passing offline. (diff)
downloadyuzu-d45561ace069024f47ed710d1165b607644d1ec3.tar.gz
yuzu-d45561ace069024f47ed710d1165b607644d1ec3.tar.xz
yuzu-d45561ace069024f47ed710d1165b607644d1ec3.zip
Merge pull request #12499 from Kelebek1/time
Rework time services
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt94
-rw-r--r--src/core/core.cpp89
-rw-r--r--src/core/core.h7
-rw-r--r--src/core/core_timing.cpp2
-rw-r--r--src/core/file_sys/system_archive/system_archive.cpp12
-rw-r--r--src/core/file_sys/system_archive/time_zone_binary.cpp1
-rw-r--r--src/core/hle/service/caps/caps_manager.cpp47
-rw-r--r--src/core/hle/service/glue/glue.cpp19
-rw-r--r--src/core/hle/service/glue/time/alarm_worker.cpp82
-rw-r--r--src/core/hle/service/glue/time/alarm_worker.h53
-rw-r--r--src/core/hle/service/glue/time/file_timestamp_worker.cpp23
-rw-r--r--src/core/hle/service/glue/time/file_timestamp_worker.h28
-rw-r--r--src/core/hle/service/glue/time/manager.cpp277
-rw-r--r--src/core/hle/service/glue/time/manager.h42
-rw-r--r--src/core/hle/service/glue/time/pm_state_change_handler.cpp13
-rw-r--r--src/core/hle/service/glue/time/pm_state_change_handler.h18
-rw-r--r--src/core/hle/service/glue/time/standard_steady_clock_resource.cpp123
-rw-r--r--src/core/hle/service/glue/time/standard_steady_clock_resource.h41
-rw-r--r--src/core/hle/service/glue/time/static.cpp448
-rw-r--r--src/core/hle/service/glue/time/static.h110
-rw-r--r--src/core/hle/service/glue/time/time_zone.cpp377
-rw-r--r--src/core/hle/service/glue/time/time_zone.h95
-rw-r--r--src/core/hle/service/glue/time/time_zone_binary.cpp221
-rw-r--r--src/core/hle/service/glue/time/time_zone_binary.h32
-rw-r--r--src/core/hle/service/glue/time/worker.cpp338
-rw-r--r--src/core/hle/service/glue/time/worker.h64
-rw-r--r--src/core/hle/service/kernel_helpers.cpp3
-rw-r--r--src/core/hle/service/nfc/common/device.cpp42
-rw-r--r--src/core/hle/service/nfc/common/device.h7
-rw-r--r--src/core/hle/service/nfc/common/device_manager.cpp37
-rw-r--r--src/core/hle/service/nfc/common/device_manager.h5
-rw-r--r--src/core/hle/service/nfc/nfc_interface.cpp5
-rw-r--r--src/core/hle/service/ns/language.cpp2
-rw-r--r--src/core/hle/service/ns/language.h5
-rw-r--r--src/core/hle/service/psc/psc.cpp17
-rw-r--r--src/core/hle/service/psc/time/alarms.cpp209
-rw-r--r--src/core/hle/service/psc/time/alarms.h139
-rw-r--r--src/core/hle/service/psc/time/clocks/context_writers.cpp83
-rw-r--r--src/core/hle/service/psc/time/clocks/context_writers.h79
-rw-r--r--src/core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h21
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp20
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h23
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp42
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h30
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_steady_clock_core.cpp101
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_steady_clock_core.h54
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp63
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h55
-rw-r--r--src/core/hle/service/psc/time/clocks/steady_clock_core.h81
-rw-r--r--src/core/hle/service/psc/time/clocks/system_clock_core.cpp75
-rw-r--r--src/core/hle/service/psc/time/clocks/system_clock_core.h55
-rw-r--r--src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp43
-rw-r--r--src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h41
-rw-r--r--src/core/hle/service/psc/time/common.cpp16
-rw-r--r--src/core/hle/service/psc/time/common.h168
-rw-r--r--src/core/hle/service/psc/time/errors.h24
-rw-r--r--src/core/hle/service/psc/time/manager.h56
-rw-r--r--src/core/hle/service/psc/time/power_state_request_manager.cpp50
-rw-r--r--src/core/hle/service/psc/time/power_state_request_manager.h42
-rw-r--r--src/core/hle/service/psc/time/power_state_service.cpp49
-rw-r--r--src/core/hle/service/psc/time/power_state_service.h32
-rw-r--r--src/core/hle/service/psc/time/service_manager.cpp494
-rw-r--r--src/core/hle/service/psc/time/service_manager.h101
-rw-r--r--src/core/hle/service/psc/time/shared_memory.cpp84
-rw-r--r--src/core/hle/service/psc/time/shared_memory.h70
-rw-r--r--src/core/hle/service/psc/time/static.cpp500
-rw-r--r--src/core/hle/service/psc/time/static.h95
-rw-r--r--src/core/hle/service/psc/time/steady_clock.cpp164
-rw-r--r--src/core/hle/service/psc/time/steady_clock.h49
-rw-r--r--src/core/hle/service/psc/time/system_clock.cpp127
-rw-r--r--src/core/hle/service/psc/time/system_clock.h46
-rw-r--r--src/core/hle/service/psc/time/time_zone.cpp280
-rw-r--r--src/core/hle/service/psc/time/time_zone.h62
-rw-r--r--src/core/hle/service/psc/time/time_zone_service.cpp289
-rw-r--r--src/core/hle/service/psc/time/time_zone_service.h69
-rw-r--r--src/core/hle/service/service.cpp7
-rw-r--r--src/core/hle/service/set/private_settings.h72
-rw-r--r--src/core/hle/service/set/setting_formats/private_settings.h1
-rw-r--r--src/core/hle/service/set/setting_formats/system_settings.cpp2
-rw-r--r--src/core/hle/service/set/setting_formats/system_settings.h16
-rw-r--r--src/core/hle/service/set/settings_types.h8
-rw-r--r--src/core/hle/service/set/system_settings_server.cpp62
-rw-r--r--src/core/hle/service/set/system_settings_server.h25
-rw-r--r--src/core/hle/service/sm/sm.h15
-rw-r--r--src/core/hle/service/time/clock_types.h129
-rw-r--r--src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h15
-rw-r--r--src/core/hle/service/time/ephemeral_network_system_clock_core.h16
-rw-r--r--src/core/hle/service/time/errors.h21
-rw-r--r--src/core/hle/service/time/local_system_clock_context_writer.h26
-rw-r--r--src/core/hle/service/time/network_system_clock_context_writer.h27
-rw-r--r--src/core/hle/service/time/standard_local_system_clock_core.h16
-rw-r--r--src/core/hle/service/time/standard_network_system_clock_core.h45
-rw-r--r--src/core/hle/service/time/standard_steady_clock_core.cpp24
-rw-r--r--src/core/hle/service/time/standard_steady_clock_core.h41
-rw-r--r--src/core/hle/service/time/standard_user_system_clock_core.cpp81
-rw-r--r--src/core/hle/service/time/standard_user_system_clock_core.h63
-rw-r--r--src/core/hle/service/time/steady_clock_core.h55
-rw-r--r--src/core/hle/service/time/system_clock_context_update_callback.cpp54
-rw-r--r--src/core/hle/service/time/system_clock_context_update_callback.h43
-rw-r--r--src/core/hle/service/time/system_clock_core.cpp71
-rw-r--r--src/core/hle/service/time/system_clock_core.h72
-rw-r--r--src/core/hle/service/time/tick_based_steady_clock_core.cpp22
-rw-r--r--src/core/hle/service/time/tick_based_steady_clock_core.h28
-rw-r--r--src/core/hle/service/time/time.cpp412
-rw-r--r--src/core/hle/service/time/time.h51
-rw-r--r--src/core/hle/service/time/time_interface.cpp41
-rw-r--r--src/core/hle/service/time/time_interface.h20
-rw-r--r--src/core/hle/service/time/time_manager.cpp293
-rw-r--r--src/core/hle/service/time/time_manager.h74
-rw-r--r--src/core/hle/service/time/time_sharedmemory.cpp69
-rw-r--r--src/core/hle/service/time/time_sharedmemory.h89
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.cpp151
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.h49
-rw-r--r--src/core/hle/service/time/time_zone_manager.cpp1182
-rw-r--r--src/core/hle/service/time/time_zone_manager.h61
-rw-r--r--src/core/hle/service/time/time_zone_service.cpp217
-rw-r--r--src/core/hle/service/time/time_zone_service.h38
-rw-r--r--src/core/hle/service/time/time_zone_types.h86
118 files changed, 6872 insertions, 3878 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 4ff2c1bb7..f75b5e10a 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -515,6 +515,24 @@ add_library(core STATIC
515 hle/service/glue/glue_manager.h 515 hle/service/glue/glue_manager.h
516 hle/service/glue/notif.cpp 516 hle/service/glue/notif.cpp
517 hle/service/glue/notif.h 517 hle/service/glue/notif.h
518 hle/service/glue/time/alarm_worker.cpp
519 hle/service/glue/time/alarm_worker.h
520 hle/service/glue/time/file_timestamp_worker.cpp
521 hle/service/glue/time/file_timestamp_worker.h
522 hle/service/glue/time/manager.cpp
523 hle/service/glue/time/manager.h
524 hle/service/glue/time/pm_state_change_handler.cpp
525 hle/service/glue/time/pm_state_change_handler.h
526 hle/service/glue/time/standard_steady_clock_resource.cpp
527 hle/service/glue/time/standard_steady_clock_resource.h
528 hle/service/glue/time/static.cpp
529 hle/service/glue/time/static.h
530 hle/service/glue/time/time_zone.cpp
531 hle/service/glue/time/time_zone.h
532 hle/service/glue/time/time_zone_binary.cpp
533 hle/service/glue/time/time_zone_binary.h
534 hle/service/glue/time/worker.cpp
535 hle/service/glue/time/worker.h
518 hle/service/grc/grc.cpp 536 hle/service/grc/grc.cpp
519 hle/service/grc/grc.h 537 hle/service/grc/grc.h
520 hle/service/hid/hid.cpp 538 hle/service/hid/hid.cpp
@@ -693,6 +711,46 @@ add_library(core STATIC
693 hle/service/prepo/prepo.h 711 hle/service/prepo/prepo.h
694 hle/service/psc/psc.cpp 712 hle/service/psc/psc.cpp
695 hle/service/psc/psc.h 713 hle/service/psc/psc.h
714 hle/service/psc/time/alarms.cpp
715 hle/service/psc/time/alarms.h
716 hle/service/psc/time/clocks/context_writers.cpp
717 hle/service/psc/time/clocks/context_writers.h
718 hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h
719 hle/service/psc/time/clocks/standard_local_system_clock_core.cpp
720 hle/service/psc/time/clocks/standard_local_system_clock_core.h
721 hle/service/psc/time/clocks/standard_network_system_clock_core.cpp
722 hle/service/psc/time/clocks/standard_network_system_clock_core.h
723 hle/service/psc/time/clocks/standard_steady_clock_core.cpp
724 hle/service/psc/time/clocks/standard_steady_clock_core.h
725 hle/service/psc/time/clocks/standard_user_system_clock_core.cpp
726 hle/service/psc/time/clocks/standard_user_system_clock_core.h
727 hle/service/psc/time/clocks/steady_clock_core.h
728 hle/service/psc/time/clocks/system_clock_core.cpp
729 hle/service/psc/time/clocks/system_clock_core.h
730 hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp
731 hle/service/psc/time/clocks/tick_based_steady_clock_core.h
732 hle/service/psc/time/common.cpp
733 hle/service/psc/time/common.h
734 hle/service/psc/time/errors.h
735 hle/service/psc/time/shared_memory.cpp
736 hle/service/psc/time/shared_memory.h
737 hle/service/psc/time/static.cpp
738 hle/service/psc/time/static.h
739 hle/service/psc/time/manager.h
740 hle/service/psc/time/power_state_service.cpp
741 hle/service/psc/time/power_state_service.h
742 hle/service/psc/time/service_manager.cpp
743 hle/service/psc/time/service_manager.h
744 hle/service/psc/time/steady_clock.cpp
745 hle/service/psc/time/steady_clock.h
746 hle/service/psc/time/system_clock.cpp
747 hle/service/psc/time/system_clock.h
748 hle/service/psc/time/time_zone.cpp
749 hle/service/psc/time/time_zone.h
750 hle/service/psc/time/time_zone_service.cpp
751 hle/service/psc/time/time_zone_service.h
752 hle/service/psc/time/power_state_request_manager.cpp
753 hle/service/psc/time/power_state_request_manager.h
696 hle/service/ptm/psm.cpp 754 hle/service/ptm/psm.cpp
697 hle/service/ptm/psm.h 755 hle/service/ptm/psm.h
698 hle/service/ptm/ptm.cpp 756 hle/service/ptm/ptm.cpp
@@ -760,40 +818,6 @@ add_library(core STATIC
760 hle/service/ssl/ssl.cpp 818 hle/service/ssl/ssl.cpp
761 hle/service/ssl/ssl.h 819 hle/service/ssl/ssl.h
762 hle/service/ssl/ssl_backend.h 820 hle/service/ssl/ssl_backend.h
763 hle/service/time/clock_types.h
764 hle/service/time/ephemeral_network_system_clock_context_writer.h
765 hle/service/time/ephemeral_network_system_clock_core.h
766 hle/service/time/errors.h
767 hle/service/time/local_system_clock_context_writer.h
768 hle/service/time/network_system_clock_context_writer.h
769 hle/service/time/standard_local_system_clock_core.h
770 hle/service/time/standard_network_system_clock_core.h
771 hle/service/time/standard_steady_clock_core.cpp
772 hle/service/time/standard_steady_clock_core.h
773 hle/service/time/standard_user_system_clock_core.cpp
774 hle/service/time/standard_user_system_clock_core.h
775 hle/service/time/steady_clock_core.h
776 hle/service/time/system_clock_context_update_callback.cpp
777 hle/service/time/system_clock_context_update_callback.h
778 hle/service/time/system_clock_core.cpp
779 hle/service/time/system_clock_core.h
780 hle/service/time/tick_based_steady_clock_core.cpp
781 hle/service/time/tick_based_steady_clock_core.h
782 hle/service/time/time.cpp
783 hle/service/time/time.h
784 hle/service/time/time_interface.cpp
785 hle/service/time/time_interface.h
786 hle/service/time/time_manager.cpp
787 hle/service/time/time_manager.h
788 hle/service/time/time_sharedmemory.cpp
789 hle/service/time/time_sharedmemory.h
790 hle/service/time/time_zone_content_manager.cpp
791 hle/service/time/time_zone_content_manager.h
792 hle/service/time/time_zone_manager.cpp
793 hle/service/time/time_zone_manager.h
794 hle/service/time/time_zone_service.cpp
795 hle/service/time/time_zone_service.h
796 hle/service/time/time_zone_types.h
797 hle/service/usb/usb.cpp 821 hle/service/usb/usb.cpp
798 hle/service/usb/usb.h 822 hle/service/usb/usb.h
799 hle/service/vi/display/vi_display.cpp 823 hle/service/vi/display/vi_display.cpp
@@ -874,7 +898,7 @@ endif()
874 898
875create_target_directory_groups(core) 899create_target_directory_groups(core)
876 900
877target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb) 901target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb tz)
878target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls RenderDoc::API) 902target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls RenderDoc::API)
879if (MINGW) 903if (MINGW)
880 target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY}) 904 target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY})
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 2392fe136..dd9de948c 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -40,9 +40,14 @@
40#include "core/hle/service/apm/apm_controller.h" 40#include "core/hle/service/apm/apm_controller.h"
41#include "core/hle/service/filesystem/filesystem.h" 41#include "core/hle/service/filesystem/filesystem.h"
42#include "core/hle/service/glue/glue_manager.h" 42#include "core/hle/service/glue/glue_manager.h"
43#include "core/hle/service/glue/time/static.h"
44#include "core/hle/service/psc/time/static.h"
45#include "core/hle/service/psc/time/steady_clock.h"
46#include "core/hle/service/psc/time/system_clock.h"
47#include "core/hle/service/psc/time/time_zone_service.h"
43#include "core/hle/service/service.h" 48#include "core/hle/service/service.h"
49#include "core/hle/service/set/system_settings_server.h"
44#include "core/hle/service/sm/sm.h" 50#include "core/hle/service/sm/sm.h"
45#include "core/hle/service/time/time_manager.h"
46#include "core/internal_network/network.h" 51#include "core/internal_network/network.h"
47#include "core/loader/loader.h" 52#include "core/loader/loader.h"
48#include "core/memory.h" 53#include "core/memory.h"
@@ -130,8 +135,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
130 135
131struct System::Impl { 136struct System::Impl {
132 explicit Impl(System& system) 137 explicit Impl(System& system)
133 : kernel{system}, fs_controller{system}, hid_core{}, room_network{}, cpu_manager{system}, 138 : kernel{system}, fs_controller{system}, hid_core{}, room_network{},
134 reporter{system}, applet_manager{system}, profile_manager{}, time_manager{system} {} 139 cpu_manager{system}, reporter{system}, applet_manager{system}, profile_manager{} {}
135 140
136 void Initialize(System& system) { 141 void Initialize(System& system) {
137 device_memory = std::make_unique<Core::DeviceMemory>(); 142 device_memory = std::make_unique<Core::DeviceMemory>();
@@ -143,8 +148,6 @@ struct System::Impl {
143 core_timing.SetMulticore(is_multicore); 148 core_timing.SetMulticore(is_multicore);
144 core_timing.Initialize([&system]() { system.RegisterHostThread(); }); 149 core_timing.Initialize([&system]() { system.RegisterHostThread(); });
145 150
146 RefreshTime();
147
148 // Create a default fs if one doesn't already exist. 151 // Create a default fs if one doesn't already exist.
149 if (virtual_filesystem == nullptr) { 152 if (virtual_filesystem == nullptr) {
150 virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); 153 virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
@@ -182,14 +185,57 @@ struct System::Impl {
182 Initialize(system); 185 Initialize(system);
183 } 186 }
184 187
185 void RefreshTime() { 188 void RefreshTime(System& system) {
189 if (!system.IsPoweredOn()) {
190 return;
191 }
192
193 auto settings_service =
194 system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys",
195 true);
196 auto static_service_a =
197 system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:a", true);
198
199 auto static_service_s =
200 system.ServiceManager().GetService<Service::PSC::Time::StaticService>("time:s", true);
201
202 std::shared_ptr<Service::PSC::Time::SystemClock> user_clock;
203 static_service_a->GetStandardUserSystemClock(user_clock);
204
205 std::shared_ptr<Service::PSC::Time::SystemClock> local_clock;
206 static_service_a->GetStandardLocalSystemClock(local_clock);
207
208 std::shared_ptr<Service::PSC::Time::SystemClock> network_clock;
209 static_service_s->GetStandardNetworkSystemClock(network_clock);
210
211 std::shared_ptr<Service::Glue::Time::TimeZoneService> timezone_service;
212 static_service_a->GetTimeZoneService(timezone_service);
213
214 Service::PSC::Time::LocationName name{};
215 auto new_name = Settings::GetTimeZoneString(Settings::values.time_zone_index.GetValue());
216 std::memcpy(name.name.data(), new_name.data(), std::min(name.name.size(), new_name.size()));
217
218 timezone_service->SetDeviceLocation(name);
219
220 u64 time_offset = 0;
221 if (Settings::values.custom_rtc_enabled) {
222 time_offset = Settings::values.custom_rtc_offset.GetValue();
223 }
224
186 const auto posix_time = std::chrono::system_clock::now().time_since_epoch(); 225 const auto posix_time = std::chrono::system_clock::now().time_since_epoch();
187 const auto current_time = 226 const u64 current_time =
188 std::chrono::duration_cast<std::chrono::seconds>(posix_time).count(); 227 +std::chrono::duration_cast<std::chrono::seconds>(posix_time).count();
189 Settings::values.custom_rtc_differential = 228 const u64 new_time = current_time + time_offset;
190 (Settings::values.custom_rtc_enabled ? Settings::values.custom_rtc.GetValue() 229
191 : current_time) - 230 Service::PSC::Time::SystemClockContext context{};
192 current_time; 231 settings_service->SetUserSystemClockContext(context);
232 user_clock->SetCurrentTime(new_time);
233
234 local_clock->SetCurrentTime(new_time);
235
236 network_clock->GetSystemClockContext(context);
237 settings_service->SetNetworkSystemClockContext(context);
238 network_clock->SetCurrentTime(new_time);
193 } 239 }
194 240
195 void Run() { 241 void Run() {
@@ -265,9 +311,6 @@ struct System::Impl {
265 service_manager = std::make_shared<Service::SM::ServiceManager>(kernel); 311 service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
266 services = std::make_unique<Service::Services>(service_manager, system); 312 services = std::make_unique<Service::Services>(service_manager, system);
267 313
268 // Initialize time manager, which must happen after kernel is created
269 time_manager.Initialize();
270
271 is_powered_on = true; 314 is_powered_on = true;
272 exit_locked = false; 315 exit_locked = false;
273 exit_requested = false; 316 exit_requested = false;
@@ -417,7 +460,6 @@ struct System::Impl {
417 fs_controller.Reset(); 460 fs_controller.Reset();
418 cheat_engine.reset(); 461 cheat_engine.reset();
419 telemetry_session.reset(); 462 telemetry_session.reset();
420 time_manager.Shutdown();
421 core_timing.ClearPendingEvents(); 463 core_timing.ClearPendingEvents();
422 app_loader.reset(); 464 app_loader.reset();
423 audio_core.reset(); 465 audio_core.reset();
@@ -533,7 +575,6 @@ struct System::Impl {
533 /// Service State 575 /// Service State
534 Service::Glue::ARPManager arp_manager; 576 Service::Glue::ARPManager arp_manager;
535 Service::Account::ProfileManager profile_manager; 577 Service::Account::ProfileManager profile_manager;
536 Service::Time::TimeManager time_manager;
537 578
538 /// Service manager 579 /// Service manager
539 std::shared_ptr<Service::SM::ServiceManager> service_manager; 580 std::shared_ptr<Service::SM::ServiceManager> service_manager;
@@ -911,14 +952,6 @@ const Service::Account::ProfileManager& System::GetProfileManager() const {
911 return impl->profile_manager; 952 return impl->profile_manager;
912} 953}
913 954
914Service::Time::TimeManager& System::GetTimeManager() {
915 return impl->time_manager;
916}
917
918const Service::Time::TimeManager& System::GetTimeManager() const {
919 return impl->time_manager;
920}
921
922void System::SetExitLocked(bool locked) { 955void System::SetExitLocked(bool locked) {
923 impl->exit_locked = locked; 956 impl->exit_locked = locked;
924} 957}
@@ -1030,13 +1063,9 @@ void System::Exit() {
1030} 1063}
1031 1064
1032void System::ApplySettings() { 1065void System::ApplySettings() {
1033 impl->RefreshTime(); 1066 impl->RefreshTime(*this);
1034 1067
1035 if (IsPoweredOn()) { 1068 if (IsPoweredOn()) {
1036 if (Settings::values.custom_rtc_enabled) {
1037 const s64 posix_time{Settings::values.custom_rtc.GetValue()};
1038 GetTimeManager().UpdateLocalSystemClockTime(posix_time);
1039 }
1040 Renderer().RefreshBaseSettings(); 1069 Renderer().RefreshBaseSettings();
1041 } 1070 }
1042} 1071}
diff --git a/src/core/core.h b/src/core/core.h
index 80446f385..183410602 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -73,10 +73,6 @@ namespace SM {
73class ServiceManager; 73class ServiceManager;
74} // namespace SM 74} // namespace SM
75 75
76namespace Time {
77class TimeManager;
78} // namespace Time
79
80} // namespace Service 76} // namespace Service
81 77
82namespace Tegra { 78namespace Tegra {
@@ -381,9 +377,6 @@ public:
381 [[nodiscard]] Service::Account::ProfileManager& GetProfileManager(); 377 [[nodiscard]] Service::Account::ProfileManager& GetProfileManager();
382 [[nodiscard]] const Service::Account::ProfileManager& GetProfileManager() const; 378 [[nodiscard]] const Service::Account::ProfileManager& GetProfileManager() const;
383 379
384 [[nodiscard]] Service::Time::TimeManager& GetTimeManager();
385 [[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const;
386
387 [[nodiscard]] Core::Debugger& GetDebugger(); 380 [[nodiscard]] Core::Debugger& GetDebugger();
388 [[nodiscard]] const Core::Debugger& GetDebugger() const; 381 [[nodiscard]] const Core::Debugger& GetDebugger() const;
389 382
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index fc536413b..1abfa920c 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -157,7 +157,7 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
157 } 157 }
158 } 158 }
159 159
160 for (auto h : to_remove) { 160 for (auto& h : to_remove) {
161 event_queue.erase(h); 161 event_queue.erase(h);
162 } 162 }
163 163
diff --git a/src/core/file_sys/system_archive/system_archive.cpp b/src/core/file_sys/system_archive/system_archive.cpp
index 6abac793b..b53eef877 100644
--- a/src/core/file_sys/system_archive/system_archive.cpp
+++ b/src/core/file_sys/system_archive/system_archive.cpp
@@ -67,25 +67,29 @@ constexpr std::array<SystemArchiveDescriptor, SYSTEM_ARCHIVE_COUNT> SYSTEM_ARCHI
67}}; 67}};
68 68
69VirtualFile SynthesizeSystemArchive(const u64 title_id) { 69VirtualFile SynthesizeSystemArchive(const u64 title_id) {
70 if (title_id < SYSTEM_ARCHIVES.front().title_id || title_id > SYSTEM_ARCHIVES.back().title_id) 70 if (title_id < SYSTEM_ARCHIVES.front().title_id || title_id > SYSTEM_ARCHIVES.back().title_id) {
71 return nullptr; 71 return nullptr;
72 }
72 73
73 const auto& desc = SYSTEM_ARCHIVES[title_id - SYSTEM_ARCHIVE_BASE_TITLE_ID]; 74 const auto& desc = SYSTEM_ARCHIVES[title_id - SYSTEM_ARCHIVE_BASE_TITLE_ID];
74 75
75 LOG_INFO(Service_FS, "Synthesizing system archive '{}' (0x{:016X}).", desc.name, desc.title_id); 76 LOG_INFO(Service_FS, "Synthesizing system archive '{}' (0x{:016X}).", desc.name, desc.title_id);
76 77
77 if (desc.supplier == nullptr) 78 if (desc.supplier == nullptr) {
78 return nullptr; 79 return nullptr;
80 }
79 81
80 const auto dir = desc.supplier(); 82 const auto dir = desc.supplier();
81 83
82 if (dir == nullptr) 84 if (dir == nullptr) {
83 return nullptr; 85 return nullptr;
86 }
84 87
85 const auto romfs = CreateRomFS(dir); 88 const auto romfs = CreateRomFS(dir);
86 89
87 if (romfs == nullptr) 90 if (romfs == nullptr) {
88 return nullptr; 91 return nullptr;
92 }
89 93
90 LOG_INFO(Service_FS, " - System archive generation successful!"); 94 LOG_INFO(Service_FS, " - System archive generation successful!");
91 return romfs; 95 return romfs;
diff --git a/src/core/file_sys/system_archive/time_zone_binary.cpp b/src/core/file_sys/system_archive/time_zone_binary.cpp
index 7c17bbefa..d4d2eae76 100644
--- a/src/core/file_sys/system_archive/time_zone_binary.cpp
+++ b/src/core/file_sys/system_archive/time_zone_binary.cpp
@@ -6,7 +6,6 @@
6#include "common/swap.h" 6#include "common/swap.h"
7#include "core/file_sys/system_archive/time_zone_binary.h" 7#include "core/file_sys/system_archive/time_zone_binary.h"
8#include "core/file_sys/vfs_vector.h" 8#include "core/file_sys/vfs_vector.h"
9#include "core/hle/service/time/time_zone_types.h"
10 9
11#include "nx_tzdb.h" 10#include "nx_tzdb.h"
12 11
diff --git a/src/core/hle/service/caps/caps_manager.cpp b/src/core/hle/service/caps/caps_manager.cpp
index 261fc204c..e3b8ecf3e 100644
--- a/src/core/hle/service/caps/caps_manager.cpp
+++ b/src/core/hle/service/caps/caps_manager.cpp
@@ -10,8 +10,10 @@
10#include "core/core.h" 10#include "core/core.h"
11#include "core/hle/service/caps/caps_manager.h" 11#include "core/hle/service/caps/caps_manager.h"
12#include "core/hle/service/caps/caps_result.h" 12#include "core/hle/service/caps/caps_result.h"
13#include "core/hle/service/time/time_manager.h" 13#include "core/hle/service/glue/time/static.h"
14#include "core/hle/service/time/time_zone_content_manager.h" 14#include "core/hle/service/psc/time/system_clock.h"
15#include "core/hle/service/service.h"
16#include "core/hle/service/sm/sm.h"
15 17
16namespace Service::Capture { 18namespace Service::Capture {
17 19
@@ -239,10 +241,15 @@ Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry,
239 const ApplicationData& app_data, std::span<const u8> image_data, 241 const ApplicationData& app_data, std::span<const u8> image_data,
240 u64 aruid) { 242 u64 aruid) {
241 const u64 title_id = system.GetApplicationProcessProgramID(); 243 const u64 title_id = system.GetApplicationProcessProgramID();
242 const auto& user_clock = system.GetTimeManager().GetStandardUserSystemClockCore(); 244
245 auto static_service =
246 system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);
247
248 std::shared_ptr<Service::PSC::Time::SystemClock> user_clock{};
249 static_service->GetStandardUserSystemClock(user_clock);
243 250
244 s64 posix_time{}; 251 s64 posix_time{};
245 Result result = user_clock.GetCurrentTime(system, posix_time); 252 auto result = user_clock->GetCurrentTime(posix_time);
246 253
247 if (result.IsError()) { 254 if (result.IsError()) {
248 return result; 255 return result;
@@ -257,10 +264,14 @@ Result AlbumManager::SaveEditedScreenShot(ApplicationAlbumEntry& out_entry,
257 const ScreenShotAttribute& attribute, 264 const ScreenShotAttribute& attribute,
258 const AlbumFileId& file_id, 265 const AlbumFileId& file_id,
259 std::span<const u8> image_data) { 266 std::span<const u8> image_data) {
260 const auto& user_clock = system.GetTimeManager().GetStandardUserSystemClockCore(); 267 auto static_service =
268 system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);
269
270 std::shared_ptr<Service::PSC::Time::SystemClock> user_clock{};
271 static_service->GetStandardUserSystemClock(user_clock);
261 272
262 s64 posix_time{}; 273 s64 posix_time{};
263 Result result = user_clock.GetCurrentTime(system, posix_time); 274 auto result = user_clock->GetCurrentTime(posix_time);
264 275
265 if (result.IsError()) { 276 if (result.IsError()) {
266 return result; 277 return result;
@@ -455,19 +466,23 @@ Result AlbumManager::SaveImage(ApplicationAlbumEntry& out_entry, std::span<const
455} 466}
456 467
457AlbumFileDateTime AlbumManager::ConvertToAlbumDateTime(u64 posix_time) const { 468AlbumFileDateTime AlbumManager::ConvertToAlbumDateTime(u64 posix_time) const {
458 Time::TimeZone::CalendarInfo calendar_date{}; 469 auto static_service =
459 const auto& time_zone_manager = 470 system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);
460 system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager(); 471
472 std::shared_ptr<Service::Glue::Time::TimeZoneService> timezone_service{};
473 static_service->GetTimeZoneService(timezone_service);
461 474
462 time_zone_manager.ToCalendarTimeWithMyRules(posix_time, calendar_date); 475 Service::PSC::Time::CalendarTime calendar_time{};
476 Service::PSC::Time::CalendarAdditionalInfo additional_info{};
477 timezone_service->ToCalendarTimeWithMyRule(calendar_time, additional_info, posix_time);
463 478
464 return { 479 return {
465 .year = calendar_date.time.year, 480 .year = calendar_time.year,
466 .month = calendar_date.time.month, 481 .month = calendar_time.month,
467 .day = calendar_date.time.day, 482 .day = calendar_time.day,
468 .hour = calendar_date.time.hour, 483 .hour = calendar_time.hour,
469 .minute = calendar_date.time.minute, 484 .minute = calendar_time.minute,
470 .second = calendar_date.time.second, 485 .second = calendar_time.second,
471 .unique_id = 0, 486 .unique_id = 0,
472 }; 487 };
473} 488}
diff --git a/src/core/hle/service/glue/glue.cpp b/src/core/hle/service/glue/glue.cpp
index 993c3d21d..10376bfac 100644
--- a/src/core/hle/service/glue/glue.cpp
+++ b/src/core/hle/service/glue/glue.cpp
@@ -8,6 +8,9 @@
8#include "core/hle/service/glue/ectx.h" 8#include "core/hle/service/glue/ectx.h"
9#include "core/hle/service/glue/glue.h" 9#include "core/hle/service/glue/glue.h"
10#include "core/hle/service/glue/notif.h" 10#include "core/hle/service/glue/notif.h"
11#include "core/hle/service/glue/time/manager.h"
12#include "core/hle/service/glue/time/static.h"
13#include "core/hle/service/psc/time/common.h"
11#include "core/hle/service/server_manager.h" 14#include "core/hle/service/server_manager.h"
12 15
13namespace Service::Glue { 16namespace Service::Glue {
@@ -31,6 +34,22 @@ void LoopProcess(Core::System& system) {
31 // Notification Services for application 34 // Notification Services for application
32 server_manager->RegisterNamedService("notif:a", std::make_shared<NOTIF_A>(system)); 35 server_manager->RegisterNamedService("notif:a", std::make_shared<NOTIF_A>(system));
33 36
37 // Time
38 auto time = std::make_shared<Time::TimeManager>(system);
39
40 server_manager->RegisterNamedService(
41 "time:u",
42 std::make_shared<Time::StaticService>(
43 system, Service::PSC::Time::StaticServiceSetupInfo{0, 0, 0, 0, 0, 0}, time, "time:u"));
44 server_manager->RegisterNamedService(
45 "time:a",
46 std::make_shared<Time::StaticService>(
47 system, Service::PSC::Time::StaticServiceSetupInfo{1, 1, 0, 1, 0, 0}, time, "time:a"));
48 server_manager->RegisterNamedService(
49 "time:r",
50 std::make_shared<Time::StaticService>(
51 system, Service::PSC::Time::StaticServiceSetupInfo{0, 0, 0, 0, 1, 0}, time, "time:r"));
52
34 ServerManager::RunServer(std::move(server_manager)); 53 ServerManager::RunServer(std::move(server_manager));
35} 54}
36 55
diff --git a/src/core/hle/service/glue/time/alarm_worker.cpp b/src/core/hle/service/glue/time/alarm_worker.cpp
new file mode 100644
index 000000000..f549ed00a
--- /dev/null
+++ b/src/core/hle/service/glue/time/alarm_worker.cpp
@@ -0,0 +1,82 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/core_timing.h"
6#include "core/hle/kernel/svc.h"
7#include "core/hle/service/glue/time/alarm_worker.h"
8#include "core/hle/service/psc/time/service_manager.h"
9#include "core/hle/service/sm/sm.h"
10
11namespace Service::Glue::Time {
12
13AlarmWorker::AlarmWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource)
14 : m_system{system}, m_ctx{system, "Glue:AlarmWorker"}, m_steady_clock_resource{
15 steady_clock_resource} {}
16
17AlarmWorker::~AlarmWorker() {
18 m_system.CoreTiming().UnscheduleEvent(m_timer_timing_event);
19
20 m_ctx.CloseEvent(m_timer_event);
21}
22
23void AlarmWorker::Initialize(std::shared_ptr<Service::PSC::Time::ServiceManager> time_m) {
24 m_time_m = std::move(time_m);
25
26 m_timer_event = m_ctx.CreateEvent("Glue:AlarmWorker:TimerEvent");
27 m_timer_timing_event = Core::Timing::CreateEvent(
28 "Glue:AlarmWorker::AlarmTimer",
29 [this](s64 time,
30 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
31 m_timer_event->Signal();
32 return std::nullopt;
33 });
34
35 AttachToClosestAlarmEvent();
36}
37
38bool AlarmWorker::GetClosestAlarmInfo(Service::PSC::Time::AlarmInfo& out_alarm_info,
39 s64& out_time) {
40 bool is_valid{};
41 Service::PSC::Time::AlarmInfo alarm_info{};
42 s64 closest_time{};
43
44 auto res = m_time_m->GetClosestAlarmInfo(is_valid, alarm_info, closest_time);
45 ASSERT(res == ResultSuccess);
46
47 if (is_valid) {
48 out_alarm_info = alarm_info;
49 out_time = closest_time;
50 }
51
52 return is_valid;
53}
54
55void AlarmWorker::OnPowerStateChanged() {
56 Service::PSC::Time::AlarmInfo closest_alarm_info{};
57 s64 closest_time{};
58 if (!GetClosestAlarmInfo(closest_alarm_info, closest_time)) {
59 m_system.CoreTiming().UnscheduleEvent(m_timer_timing_event);
60 m_timer_event->Clear();
61 return;
62 }
63
64 if (closest_alarm_info.alert_time <= closest_time) {
65 m_time_m->CheckAndSignalAlarms();
66 } else {
67 auto next_time{closest_alarm_info.alert_time - closest_time};
68
69 m_system.CoreTiming().UnscheduleEvent(m_timer_timing_event);
70 m_timer_event->Clear();
71
72 m_system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds(next_time),
73 m_timer_timing_event);
74 }
75}
76
77Result AlarmWorker::AttachToClosestAlarmEvent() {
78 m_time_m->GetClosestAlarmUpdatedEvent(&m_event);
79 R_SUCCEED();
80}
81
82} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/alarm_worker.h b/src/core/hle/service/glue/time/alarm_worker.h
new file mode 100644
index 000000000..f269cffdb
--- /dev/null
+++ b/src/core/hle/service/glue/time/alarm_worker.h
@@ -0,0 +1,53 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hle/kernel/k_event.h"
8#include "core/hle/service/kernel_helpers.h"
9#include "core/hle/service/psc/time/common.h"
10
11namespace Core {
12class System;
13}
14
15namespace Service::PSC::Time {
16class ServiceManager;
17}
18
19namespace Service::Glue::Time {
20class StandardSteadyClockResource;
21
22class AlarmWorker {
23public:
24 explicit AlarmWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource);
25 ~AlarmWorker();
26
27 void Initialize(std::shared_ptr<Service::PSC::Time::ServiceManager> time_m);
28
29 Kernel::KEvent& GetEvent() {
30 return *m_event;
31 }
32
33 Kernel::KEvent& GetTimerEvent() {
34 return *m_timer_event;
35 }
36
37 void OnPowerStateChanged();
38
39private:
40 bool GetClosestAlarmInfo(Service::PSC::Time::AlarmInfo& out_alarm_info, s64& out_time);
41 Result AttachToClosestAlarmEvent();
42
43 Core::System& m_system;
44 KernelHelpers::ServiceContext m_ctx;
45 std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m;
46
47 Kernel::KEvent* m_event{};
48 Kernel::KEvent* m_timer_event{};
49 std::shared_ptr<Core::Timing::EventType> m_timer_timing_event;
50 StandardSteadyClockResource& m_steady_clock_resource;
51};
52
53} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/file_timestamp_worker.cpp b/src/core/hle/service/glue/time/file_timestamp_worker.cpp
new file mode 100644
index 000000000..5a6309549
--- /dev/null
+++ b/src/core/hle/service/glue/time/file_timestamp_worker.cpp
@@ -0,0 +1,23 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/glue/time/file_timestamp_worker.h"
5#include "core/hle/service/psc/time/common.h"
6#include "core/hle/service/psc/time/system_clock.h"
7#include "core/hle/service/psc/time/time_zone_service.h"
8
9namespace Service::Glue::Time {
10
11void FileTimestampWorker::SetFilesystemPosixTime() {
12 s64 time{};
13 Service::PSC::Time::CalendarTime calendar_time{};
14 Service::PSC::Time::CalendarAdditionalInfo additional_info{};
15
16 if (m_initialized && m_system_clock->GetCurrentTime(time) == ResultSuccess &&
17 m_time_zone->ToCalendarTimeWithMyRule(calendar_time, additional_info, time) ==
18 ResultSuccess) {
19 // TODO IFileSystemProxy::SetCurrentPosixTime
20 }
21}
22
23} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/file_timestamp_worker.h b/src/core/hle/service/glue/time/file_timestamp_worker.h
new file mode 100644
index 000000000..5f8b9b049
--- /dev/null
+++ b/src/core/hle/service/glue/time/file_timestamp_worker.h
@@ -0,0 +1,28 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7
8#include "common/common_types.h"
9
10namespace Service::PSC::Time {
11class SystemClock;
12class TimeZoneService;
13} // namespace Service::PSC::Time
14
15namespace Service::Glue::Time {
16
17class FileTimestampWorker {
18public:
19 FileTimestampWorker() = default;
20
21 void SetFilesystemPosixTime();
22
23 std::shared_ptr<Service::PSC::Time::SystemClock> m_system_clock{};
24 std::shared_ptr<Service::PSC::Time::TimeZoneService> m_time_zone{};
25 bool m_initialized{};
26};
27
28} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/manager.cpp b/src/core/hle/service/glue/time/manager.cpp
new file mode 100644
index 000000000..6423e5089
--- /dev/null
+++ b/src/core/hle/service/glue/time/manager.cpp
@@ -0,0 +1,277 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <chrono>
5
6#include "core/core.h"
7#include "core/core_timing.h"
8
9#include "common/settings.h"
10#include "common/time_zone.h"
11#include "core/file_sys/vfs.h"
12#include "core/hle/kernel/svc.h"
13#include "core/hle/service/glue/time/manager.h"
14#include "core/hle/service/glue/time/time_zone_binary.h"
15#include "core/hle/service/psc/time/service_manager.h"
16#include "core/hle/service/psc/time/static.h"
17#include "core/hle/service/psc/time/system_clock.h"
18#include "core/hle/service/psc/time/time_zone_service.h"
19#include "core/hle/service/set/system_settings_server.h"
20#include "core/hle/service/sm/sm.h"
21
22namespace Service::Glue::Time {
23namespace {
24
25template <typename T>
26T GetSettingsItemValue(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys,
27 const char* category, const char* name) {
28 std::vector<u8> interval_buf;
29 auto res = set_sys->GetSettingsItemValue(interval_buf, category, name);
30 ASSERT(res == ResultSuccess);
31
32 T v{};
33 std::memcpy(&v, interval_buf.data(), sizeof(T));
34 return v;
35}
36
37s64 CalendarTimeToEpoch(Service::PSC::Time::CalendarTime calendar) {
38 constexpr auto is_leap = [](s32 year) -> bool {
39 return (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0));
40 };
41 constexpr std::array<s32, 12> MonthStartDayOfYear{
42 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
43 };
44
45 s16 month_s16{calendar.month};
46 s8 month{static_cast<s8>(((month_s16 * 43) & ~std::numeric_limits<s16>::max()) +
47 ((month_s16 * 43) >> 9))};
48 s8 month_index{static_cast<s8>(calendar.month - 12 * month)};
49 if (month_index == 0) {
50 month_index = 12;
51 }
52 s32 year{(month + calendar.year) - !month_index};
53 s32 v8{year >= 0 ? year : year + 3};
54
55 s64 days_since_epoch = calendar.day + MonthStartDayOfYear[month_index - 1];
56 days_since_epoch += (year * 365) + (v8 / 4) - (year / 100) + (year / 400) - 365;
57
58 if (month_index <= 2 && is_leap(year)) {
59 days_since_epoch--;
60 }
61 auto epoch_s{((24ll * days_since_epoch + calendar.hour) * 60ll + calendar.minute) * 60ll +
62 calendar.second};
63 return epoch_s - 62135683200ll;
64}
65
66s64 GetEpochTimeFromInitialYear(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys) {
67 Service::PSC::Time::CalendarTime calendar{
68 .year = GetSettingsItemValue<s16>(set_sys, "time", "standard_user_clock_initial_year"),
69 .month = 1,
70 .day = 1,
71 .hour = 0,
72 .minute = 0,
73 .second = 0,
74 };
75 return CalendarTimeToEpoch(calendar);
76}
77
78Service::PSC::Time::LocationName GetTimeZoneString(Service::PSC::Time::LocationName& in_name) {
79 auto configured_zone = Settings::GetTimeZoneString(Settings::values.time_zone_index.GetValue());
80
81 Service::PSC::Time::LocationName configured_name{};
82 std::memcpy(configured_name.name.data(), configured_zone.data(),
83 std::min(configured_name.name.size(), configured_zone.size()));
84
85 if (!IsTimeZoneBinaryValid(configured_name)) {
86 configured_zone = Common::TimeZone::FindSystemTimeZone();
87 configured_name = {};
88 std::memcpy(configured_name.name.data(), configured_zone.data(),
89 std::min(configured_name.name.size(), configured_zone.size()));
90 }
91
92 ASSERT_MSG(IsTimeZoneBinaryValid(configured_name), "Invalid time zone {}!",
93 configured_name.name.data());
94
95 return configured_name;
96}
97
98} // namespace
99
100TimeManager::TimeManager(Core::System& system)
101 : m_steady_clock_resource{system}, m_worker{system, m_steady_clock_resource,
102 m_file_timestamp_worker} {
103 m_time_m =
104 system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true);
105
106 auto res = m_time_m->GetStaticServiceAsServiceManager(m_time_sm);
107 ASSERT(res == ResultSuccess);
108
109 m_set_sys =
110 system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
111
112 res = MountTimeZoneBinary(system);
113 ASSERT(res == ResultSuccess);
114
115 m_worker.Initialize(m_time_sm, m_set_sys);
116
117 res = m_time_sm->GetStandardUserSystemClock(m_file_timestamp_worker.m_system_clock);
118 ASSERT(res == ResultSuccess);
119
120 res = m_time_sm->GetTimeZoneService(m_file_timestamp_worker.m_time_zone);
121 ASSERT(res == ResultSuccess);
122
123 res = SetupStandardSteadyClockCore();
124 ASSERT(res == ResultSuccess);
125
126 Service::PSC::Time::SystemClockContext user_clock_context{};
127 res = m_set_sys->GetUserSystemClockContext(user_clock_context);
128 ASSERT(res == ResultSuccess);
129
130 // TODO the local clock should initialise with this epoch time, and be updated somewhere else on
131 // first boot to update it, but I haven't been able to find that point (likely via ntc's auto
132 // correct as it's defaulted to be enabled). So to get a time that isn't stuck in the past for
133 // first boot, grab the current real seconds.
134 auto epoch_time{GetEpochTimeFromInitialYear(m_set_sys)};
135 if (user_clock_context == Service::PSC::Time::SystemClockContext{}) {
136 m_steady_clock_resource.GetRtcTimeInSeconds(epoch_time);
137 }
138
139 res = m_time_m->SetupStandardLocalSystemClockCore(user_clock_context, epoch_time);
140 ASSERT(res == ResultSuccess);
141
142 Service::PSC::Time::SystemClockContext network_clock_context{};
143 res = m_set_sys->GetNetworkSystemClockContext(network_clock_context);
144 ASSERT(res == ResultSuccess);
145
146 auto network_accuracy_m{GetSettingsItemValue<s32>(
147 m_set_sys, "time", "standard_network_clock_sufficient_accuracy_minutes")};
148 auto one_minute_ns{
149 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::minutes(1)).count()};
150 s64 network_accuracy_ns{network_accuracy_m * one_minute_ns};
151
152 res = m_time_m->SetupStandardNetworkSystemClockCore(network_clock_context, network_accuracy_ns);
153 ASSERT(res == ResultSuccess);
154
155 bool is_automatic_correction_enabled{};
156 res = m_set_sys->IsUserSystemClockAutomaticCorrectionEnabled(is_automatic_correction_enabled);
157 ASSERT(res == ResultSuccess);
158
159 Service::PSC::Time::SteadyClockTimePoint automatic_correction_time_point{};
160 res = m_set_sys->GetUserSystemClockAutomaticCorrectionUpdatedTime(
161 automatic_correction_time_point);
162 ASSERT(res == ResultSuccess);
163
164 res = m_time_m->SetupStandardUserSystemClockCore(automatic_correction_time_point,
165 is_automatic_correction_enabled);
166 ASSERT(res == ResultSuccess);
167
168 res = m_time_m->SetupEphemeralNetworkSystemClockCore();
169 ASSERT(res == ResultSuccess);
170
171 res = SetupTimeZoneServiceCore();
172 ASSERT(res == ResultSuccess);
173
174 s64 rtc_time_s{};
175 res = m_steady_clock_resource.GetRtcTimeInSeconds(rtc_time_s);
176 ASSERT(res == ResultSuccess);
177
178 // TODO system report "launch"
179 // "rtc_reset" = m_steady_clock_resource.m_rtc_reset
180 // "rtc_value" = rtc_time_s
181
182 m_worker.StartThread();
183
184 m_file_timestamp_worker.m_initialized = true;
185
186 s64 system_clock_time{};
187 if (m_file_timestamp_worker.m_system_clock->GetCurrentTime(system_clock_time) ==
188 ResultSuccess) {
189 Service::PSC::Time::CalendarTime calendar_time{};
190 Service::PSC::Time::CalendarAdditionalInfo calendar_additional{};
191 if (m_file_timestamp_worker.m_time_zone->ToCalendarTimeWithMyRule(
192 calendar_time, calendar_additional, system_clock_time) == ResultSuccess) {
193 // TODO IFileSystemProxy::SetCurrentPosixTime(system_clock_time,
194 // calendar_additional.ut_offset)
195 }
196 }
197}
198
199Result TimeManager::SetupStandardSteadyClockCore() {
200 Common::UUID external_clock_source_id{};
201 auto res = m_set_sys->GetExternalSteadyClockSourceId(external_clock_source_id);
202 ASSERT(res == ResultSuccess);
203
204 s64 external_steady_clock_internal_offset_s{};
205 res = m_set_sys->GetExternalSteadyClockInternalOffset(external_steady_clock_internal_offset_s);
206 ASSERT(res == ResultSuccess);
207
208 auto one_second_ns{
209 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
210 s64 external_steady_clock_internal_offset_ns{external_steady_clock_internal_offset_s *
211 one_second_ns};
212
213 s32 standard_steady_clock_test_offset_m{
214 GetSettingsItemValue<s32>(m_set_sys, "time", "standard_steady_clock_test_offset_minutes")};
215 auto one_minute_ns{
216 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::minutes(1)).count()};
217 s64 standard_steady_clock_test_offset_ns{standard_steady_clock_test_offset_m * one_minute_ns};
218
219 auto reset_detected = m_steady_clock_resource.GetResetDetected();
220 if (reset_detected) {
221 external_clock_source_id = {};
222 }
223
224 Common::UUID clock_source_id{};
225 m_steady_clock_resource.Initialize(&clock_source_id, &external_clock_source_id);
226
227 if (clock_source_id != external_clock_source_id) {
228 m_set_sys->SetExternalSteadyClockSourceId(clock_source_id);
229 }
230
231 res = m_time_m->SetupStandardSteadyClockCore(clock_source_id, m_steady_clock_resource.GetTime(),
232 external_steady_clock_internal_offset_ns,
233 standard_steady_clock_test_offset_ns,
234 reset_detected);
235 ASSERT(res == ResultSuccess);
236 R_SUCCEED();
237}
238
239Result TimeManager::SetupTimeZoneServiceCore() {
240 Service::PSC::Time::LocationName name{};
241 auto res = m_set_sys->GetDeviceTimeZoneLocationName(name);
242 ASSERT(res == ResultSuccess);
243
244 auto configured_zone = GetTimeZoneString(name);
245
246 if (configured_zone.name != name.name) {
247 m_set_sys->SetDeviceTimeZoneLocationName(configured_zone);
248 name = configured_zone;
249
250 std::shared_ptr<Service::PSC::Time::SystemClock> local_clock;
251 m_time_sm->GetStandardLocalSystemClock(local_clock);
252 Service::PSC::Time::SystemClockContext context{};
253 local_clock->GetSystemClockContext(context);
254 m_set_sys->SetDeviceTimeZoneLocationUpdatedTime(context.steady_time_point);
255 }
256
257 Service::PSC::Time::SteadyClockTimePoint time_point{};
258 res = m_set_sys->GetDeviceTimeZoneLocationUpdatedTime(time_point);
259 ASSERT(res == ResultSuccess);
260
261 auto location_count = GetTimeZoneCount();
262 Service::PSC::Time::RuleVersion rule_version{};
263 GetTimeZoneVersion(rule_version);
264
265 std::span<const u8> rule_buffer{};
266 size_t rule_size{};
267 res = GetTimeZoneRule(rule_buffer, rule_size, name);
268 ASSERT(res == ResultSuccess);
269
270 res = m_time_m->SetupTimeZoneServiceCore(name, time_point, rule_version, location_count,
271 rule_buffer);
272 ASSERT(res == ResultSuccess);
273
274 R_SUCCEED();
275}
276
277} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/manager.h b/src/core/hle/service/glue/time/manager.h
new file mode 100644
index 000000000..a46ec6364
--- /dev/null
+++ b/src/core/hle/service/glue/time/manager.h
@@ -0,0 +1,42 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <functional>
7#include <string>
8
9#include "common/common_types.h"
10#include "core/file_sys/vfs_types.h"
11#include "core/hle/service/glue/time/file_timestamp_worker.h"
12#include "core/hle/service/glue/time/standard_steady_clock_resource.h"
13#include "core/hle/service/glue/time/worker.h"
14#include "core/hle/service/service.h"
15
16namespace Core {
17class System;
18}
19
20namespace Service::PSC::Time {
21class ServiceManager;
22class StaticService;
23} // namespace Service::PSC::Time
24
25namespace Service::Glue::Time {
26class TimeManager {
27public:
28 explicit TimeManager(Core::System& system);
29
30 std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
31
32 std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m{};
33 std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm{};
34 StandardSteadyClockResource m_steady_clock_resource;
35 FileTimestampWorker m_file_timestamp_worker;
36 TimeWorker m_worker;
37
38private:
39 Result SetupStandardSteadyClockCore();
40 Result SetupTimeZoneServiceCore();
41};
42} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/pm_state_change_handler.cpp b/src/core/hle/service/glue/time/pm_state_change_handler.cpp
new file mode 100644
index 000000000..7470fb225
--- /dev/null
+++ b/src/core/hle/service/glue/time/pm_state_change_handler.cpp
@@ -0,0 +1,13 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/glue/time/pm_state_change_handler.h"
5
6namespace Service::Glue::Time {
7
8PmStateChangeHandler::PmStateChangeHandler(AlarmWorker& alarm_worker)
9 : m_alarm_worker{alarm_worker} {
10 // TODO Initialize IPmModule, dependent on Rtc and Fs
11}
12
13} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/pm_state_change_handler.h b/src/core/hle/service/glue/time/pm_state_change_handler.h
new file mode 100644
index 000000000..27d9f7872
--- /dev/null
+++ b/src/core/hle/service/glue/time/pm_state_change_handler.h
@@ -0,0 +1,18 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7
8namespace Service::Glue::Time {
9class AlarmWorker;
10
11class PmStateChangeHandler {
12public:
13 explicit PmStateChangeHandler(AlarmWorker& alarm_worker);
14
15 AlarmWorker& m_alarm_worker;
16 s32 m_priority{};
17};
18} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/standard_steady_clock_resource.cpp b/src/core/hle/service/glue/time/standard_steady_clock_resource.cpp
new file mode 100644
index 000000000..5ebaa33e0
--- /dev/null
+++ b/src/core/hle/service/glue/time/standard_steady_clock_resource.cpp
@@ -0,0 +1,123 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <chrono>
5
6#include "common/settings.h"
7#include "core/core.h"
8#include "core/core_timing.h"
9#include "core/hle/kernel/svc.h"
10#include "core/hle/service/glue/time/standard_steady_clock_resource.h"
11#include "core/hle/service/psc/time/errors.h"
12
13namespace Service::Glue::Time {
14namespace {
15[[maybe_unused]] constexpr u32 Max77620PmicSession = 0x3A000001;
16[[maybe_unused]] constexpr u32 Max77620RtcSession = 0x3B000001;
17
18Result GetTimeInSeconds(Core::System& system, s64& out_time_s) {
19 out_time_s = std::chrono::duration_cast<std::chrono::seconds>(
20 std::chrono::system_clock::now().time_since_epoch())
21 .count();
22
23 if (Settings::values.custom_rtc_enabled) {
24 out_time_s += Settings::values.custom_rtc_offset.GetValue();
25 }
26 R_SUCCEED();
27}
28} // namespace
29
30StandardSteadyClockResource::StandardSteadyClockResource(Core::System& system) : m_system{system} {}
31
32void StandardSteadyClockResource::Initialize(Common::UUID* out_source_id,
33 Common::UUID* external_source_id) {
34 constexpr size_t NUM_TRIES{20};
35
36 size_t i{0};
37 Result res{ResultSuccess};
38 for (; i < NUM_TRIES; i++) {
39 res = SetCurrentTime();
40 if (res == ResultSuccess) {
41 break;
42 }
43 Kernel::Svc::SleepThread(m_system, std::chrono::duration_cast<std::chrono::nanoseconds>(
44 std::chrono::milliseconds(1))
45 .count());
46 }
47
48 if (i < NUM_TRIES) {
49 m_set_time_result = ResultSuccess;
50 if (*external_source_id != Service::PSC::Time::ClockSourceId{}) {
51 m_clock_source_id = *external_source_id;
52 } else {
53 m_clock_source_id = Common::UUID::MakeRandom();
54 }
55 } else {
56 m_set_time_result = res;
57 auto ticks{m_system.CoreTiming().GetClockTicks()};
58 m_time = -Service::PSC::Time::ConvertToTimeSpan(ticks).count();
59 m_clock_source_id = Common::UUID::MakeRandom();
60 }
61
62 if (out_source_id) {
63 *out_source_id = m_clock_source_id;
64 }
65}
66
67bool StandardSteadyClockResource::GetResetDetected() {
68 // TODO:
69 // call Rtc::GetRtcResetDetected(Max77620RtcSession)
70 // if detected:
71 // SetSys::SetExternalSteadyClockSourceId(invalid_id)
72 // Rtc::ClearRtcResetDetected(Max77620RtcSession)
73 // set m_rtc_reset to result
74 // Instead, only set reset to true if we're booting for the first time.
75 m_rtc_reset = false;
76 return m_rtc_reset;
77}
78
79Result StandardSteadyClockResource::SetCurrentTime() {
80 auto start_tick{m_system.CoreTiming().GetClockTicks()};
81
82 s64 rtc_time_s{};
83 // TODO R_TRY(Rtc::GetTimeInSeconds(rtc_time_s, Max77620RtcSession))
84 R_TRY(GetTimeInSeconds(m_system, rtc_time_s));
85
86 auto end_tick{m_system.CoreTiming().GetClockTicks()};
87 auto diff{Service::PSC::Time::ConvertToTimeSpan(end_tick - start_tick)};
88 // Why is this here?
89 R_UNLESS(diff < std::chrono::milliseconds(101), Service::PSC::Time::ResultRtcTimeout);
90
91 auto one_second_ns{
92 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
93 s64 boot_time{rtc_time_s * one_second_ns -
94 Service::PSC::Time::ConvertToTimeSpan(end_tick).count()};
95
96 std::scoped_lock l{m_mutex};
97 m_time = boot_time;
98 R_SUCCEED();
99}
100
101Result StandardSteadyClockResource::GetRtcTimeInSeconds(s64& out_time) {
102 // TODO
103 // R_TRY(Rtc::GetTimeInSeconds(time_s, Max77620RtcSession)
104 R_RETURN(GetTimeInSeconds(m_system, out_time));
105}
106
107void StandardSteadyClockResource::UpdateTime() {
108 constexpr size_t NUM_TRIES{3};
109
110 size_t i{0};
111 Result res{ResultSuccess};
112 for (; i < NUM_TRIES; i++) {
113 res = SetCurrentTime();
114 if (res == ResultSuccess) {
115 break;
116 }
117 Kernel::Svc::SleepThread(m_system, std::chrono::duration_cast<std::chrono::nanoseconds>(
118 std::chrono::milliseconds(1))
119 .count());
120 }
121}
122
123} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/standard_steady_clock_resource.h b/src/core/hle/service/glue/time/standard_steady_clock_resource.h
new file mode 100644
index 000000000..978d6b63b
--- /dev/null
+++ b/src/core/hle/service/glue/time/standard_steady_clock_resource.h
@@ -0,0 +1,41 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <mutex>
7
8#include "common/common_types.h"
9#include "core/hle/result.h"
10#include "core/hle/service/psc/time/common.h"
11
12namespace Core {
13class System;
14}
15
16namespace Service::Glue::Time {
17class StandardSteadyClockResource {
18public:
19 StandardSteadyClockResource(Core::System& system);
20
21 void Initialize(Common::UUID* out_source_id, Common::UUID* external_source_id);
22
23 s64 GetTime() const {
24 return m_time;
25 }
26
27 bool GetResetDetected();
28 Result SetCurrentTime();
29 Result GetRtcTimeInSeconds(s64& out_time);
30 void UpdateTime();
31
32private:
33 Core::System& m_system;
34
35 std::mutex m_mutex;
36 Service::PSC::Time::ClockSourceId m_clock_source_id{};
37 s64 m_time{};
38 Result m_set_time_result;
39 bool m_rtc_reset;
40};
41} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/static.cpp b/src/core/hle/service/glue/time/static.cpp
new file mode 100644
index 000000000..63b7d91da
--- /dev/null
+++ b/src/core/hle/service/glue/time/static.cpp
@@ -0,0 +1,448 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <chrono>
5
6#include "core/core.h"
7#include "core/hle/kernel/k_shared_memory.h"
8#include "core/hle/kernel/svc.h"
9#include "core/hle/service/glue/time/file_timestamp_worker.h"
10#include "core/hle/service/glue/time/static.h"
11#include "core/hle/service/psc/time/errors.h"
12#include "core/hle/service/psc/time/service_manager.h"
13#include "core/hle/service/psc/time/static.h"
14#include "core/hle/service/psc/time/steady_clock.h"
15#include "core/hle/service/psc/time/system_clock.h"
16#include "core/hle/service/psc/time/time_zone_service.h"
17#include "core/hle/service/set/system_settings_server.h"
18#include "core/hle/service/sm/sm.h"
19
20namespace Service::Glue::Time {
21namespace {
22template <typename T>
23T GetSettingsItemValue(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys,
24 const char* category, const char* name) {
25 std::vector<u8> interval_buf;
26 auto res = set_sys->GetSettingsItemValue(interval_buf, category, name);
27 ASSERT(res == ResultSuccess);
28
29 T v{};
30 std::memcpy(&v, interval_buf.data(), sizeof(T));
31 return v;
32}
33} // namespace
34
35StaticService::StaticService(Core::System& system_,
36 Service::PSC::Time::StaticServiceSetupInfo setup_info,
37 std::shared_ptr<TimeManager> time, const char* name)
38 : ServiceFramework{system_, name}, m_system{system_}, m_time_m{time->m_time_m},
39 m_setup_info{setup_info}, m_time_sm{time->m_time_sm},
40 m_file_timestamp_worker{time->m_file_timestamp_worker}, m_standard_steady_clock_resource{
41 time->m_steady_clock_resource} {
42 // clang-format off
43 static const FunctionInfo functions[] = {
44 {0, &StaticService::Handle_GetStandardUserSystemClock, "GetStandardUserSystemClock"},
45 {1, &StaticService::Handle_GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"},
46 {2, &StaticService::Handle_GetStandardSteadyClock, "GetStandardSteadyClock"},
47 {3, &StaticService::Handle_GetTimeZoneService, "GetTimeZoneService"},
48 {4, &StaticService::Handle_GetStandardLocalSystemClock, "GetStandardLocalSystemClock"},
49 {5, &StaticService::Handle_GetEphemeralNetworkSystemClock, "GetEphemeralNetworkSystemClock"},
50 {20, &StaticService::Handle_GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"},
51 {50, &StaticService::Handle_SetStandardSteadyClockInternalOffset, "SetStandardSteadyClockInternalOffset"},
52 {51, &StaticService::Handle_GetStandardSteadyClockRtcValue, "GetStandardSteadyClockRtcValue"},
53 {100, &StaticService::Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled, "IsStandardUserSystemClockAutomaticCorrectionEnabled"},
54 {101, &StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled, "SetStandardUserSystemClockAutomaticCorrectionEnabled"},
55 {102, &StaticService::Handle_GetStandardUserSystemClockInitialYear, "GetStandardUserSystemClockInitialYear"},
56 {200, &StaticService::Handle_IsStandardNetworkSystemClockAccuracySufficient, "IsStandardNetworkSystemClockAccuracySufficient"},
57 {201, &StaticService::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"},
58 {300, &StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint, "CalculateMonotonicSystemClockBaseTimePoint"},
59 {400, &StaticService::Handle_GetClockSnapshot, "GetClockSnapshot"},
60 {401, &StaticService::Handle_GetClockSnapshotFromSystemClockContext, "GetClockSnapshotFromSystemClockContext"},
61 {500, &StaticService::Handle_CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"},
62 {501, &StaticService::Handle_CalculateSpanBetween, "CalculateSpanBetween"},
63 };
64 // clang-format on
65
66 RegisterHandlers(functions);
67
68 m_set_sys =
69 m_system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
70
71 if (m_setup_info.can_write_local_clock && m_setup_info.can_write_user_clock &&
72 !m_setup_info.can_write_network_clock && m_setup_info.can_write_timezone_device_location &&
73 !m_setup_info.can_write_steady_clock && !m_setup_info.can_write_uninitialized_clock) {
74 m_time_m->GetStaticServiceAsAdmin(m_wrapped_service);
75 } else if (!m_setup_info.can_write_local_clock && !m_setup_info.can_write_user_clock &&
76 !m_setup_info.can_write_network_clock &&
77 !m_setup_info.can_write_timezone_device_location &&
78 !m_setup_info.can_write_steady_clock &&
79 !m_setup_info.can_write_uninitialized_clock) {
80 m_time_m->GetStaticServiceAsUser(m_wrapped_service);
81 } else if (!m_setup_info.can_write_local_clock && !m_setup_info.can_write_user_clock &&
82 !m_setup_info.can_write_network_clock &&
83 !m_setup_info.can_write_timezone_device_location &&
84 m_setup_info.can_write_steady_clock && !m_setup_info.can_write_uninitialized_clock) {
85 m_time_m->GetStaticServiceAsRepair(m_wrapped_service);
86 } else {
87 UNREACHABLE();
88 }
89
90 auto res = m_wrapped_service->GetTimeZoneService(m_time_zone);
91 ASSERT(res == ResultSuccess);
92}
93
94void StaticService::Handle_GetStandardUserSystemClock(HLERequestContext& ctx) {
95 LOG_DEBUG(Service_Time, "called.");
96
97 std::shared_ptr<Service::PSC::Time::SystemClock> service{};
98 auto res = GetStandardUserSystemClock(service);
99
100 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
101 rb.Push(res);
102 rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service));
103}
104
105void StaticService::Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx) {
106 LOG_DEBUG(Service_Time, "called.");
107
108 std::shared_ptr<Service::PSC::Time::SystemClock> service{};
109 auto res = GetStandardNetworkSystemClock(service);
110
111 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
112 rb.Push(res);
113 rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service));
114}
115
116void StaticService::Handle_GetStandardSteadyClock(HLERequestContext& ctx) {
117 LOG_DEBUG(Service_Time, "called.");
118
119 std::shared_ptr<Service::PSC::Time::SteadyClock> service{};
120 auto res = GetStandardSteadyClock(service);
121
122 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
123 rb.Push(res);
124 rb.PushIpcInterface(std::move(service));
125}
126
127void StaticService::Handle_GetTimeZoneService(HLERequestContext& ctx) {
128 LOG_DEBUG(Service_Time, "called.");
129
130 std::shared_ptr<TimeZoneService> service{};
131 auto res = GetTimeZoneService(service);
132
133 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
134 rb.Push(res);
135 rb.PushIpcInterface(std::move(service));
136}
137
138void StaticService::Handle_GetStandardLocalSystemClock(HLERequestContext& ctx) {
139 LOG_DEBUG(Service_Time, "called.");
140
141 std::shared_ptr<Service::PSC::Time::SystemClock> service{};
142 auto res = GetStandardLocalSystemClock(service);
143
144 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
145 rb.Push(res);
146 rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service));
147}
148
149void StaticService::Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx) {
150 LOG_DEBUG(Service_Time, "called.");
151
152 std::shared_ptr<Service::PSC::Time::SystemClock> service{};
153 auto res = GetEphemeralNetworkSystemClock(service);
154
155 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
156 rb.Push(res);
157 rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service));
158}
159
160void StaticService::Handle_GetSharedMemoryNativeHandle(HLERequestContext& ctx) {
161 LOG_DEBUG(Service_Time, "called.");
162
163 Kernel::KSharedMemory* shared_memory{};
164 auto res = GetSharedMemoryNativeHandle(&shared_memory);
165
166 IPC::ResponseBuilder rb{ctx, 2, 1};
167 rb.Push(res);
168 rb.PushCopyObjects(shared_memory);
169}
170
171void StaticService::Handle_SetStandardSteadyClockInternalOffset(HLERequestContext& ctx) {
172 LOG_DEBUG(Service_Time, "called.");
173
174 IPC::RequestParser rp{ctx};
175 auto offset_ns{rp.Pop<s64>()};
176
177 auto res = SetStandardSteadyClockInternalOffset(offset_ns);
178
179 IPC::ResponseBuilder rb{ctx, 2};
180 rb.Push(res);
181}
182
183void StaticService::Handle_GetStandardSteadyClockRtcValue(HLERequestContext& ctx) {
184 LOG_DEBUG(Service_Time, "called.");
185
186 s64 rtc_value{};
187 auto res = GetStandardSteadyClockRtcValue(rtc_value);
188
189 IPC::ResponseBuilder rb{ctx, 4};
190 rb.Push(res);
191 rb.Push(rtc_value);
192}
193
194void StaticService::Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled(
195 HLERequestContext& ctx) {
196 LOG_DEBUG(Service_Time, "called.");
197
198 bool is_enabled{};
199 auto res = IsStandardUserSystemClockAutomaticCorrectionEnabled(is_enabled);
200
201 IPC::ResponseBuilder rb{ctx, 3};
202 rb.Push(res);
203 rb.Push<bool>(is_enabled);
204}
205
206void StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled(
207 HLERequestContext& ctx) {
208 LOG_DEBUG(Service_Time, "called.");
209
210 IPC::RequestParser rp{ctx};
211 auto automatic_correction{rp.Pop<bool>()};
212
213 auto res = SetStandardUserSystemClockAutomaticCorrectionEnabled(automatic_correction);
214
215 IPC::ResponseBuilder rb{ctx, 2};
216 rb.Push(res);
217}
218
219void StaticService::Handle_GetStandardUserSystemClockInitialYear(HLERequestContext& ctx) {
220 LOG_DEBUG(Service_Time, "called.");
221
222 s32 initial_year{};
223 auto res = GetStandardUserSystemClockInitialYear(initial_year);
224
225 IPC::ResponseBuilder rb{ctx, 3};
226 rb.Push(res);
227 rb.Push(initial_year);
228}
229
230void StaticService::Handle_IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx) {
231 LOG_DEBUG(Service_Time, "called.");
232
233 bool is_sufficient{};
234 auto res = IsStandardNetworkSystemClockAccuracySufficient(is_sufficient);
235
236 IPC::ResponseBuilder rb{ctx, 3};
237 rb.Push(res);
238 rb.Push<bool>(is_sufficient);
239}
240
241void StaticService::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
242 HLERequestContext& ctx) {
243 LOG_DEBUG(Service_Time, "called.");
244
245 Service::PSC::Time::SteadyClockTimePoint time_point{};
246 auto res = GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
247
248 IPC::ResponseBuilder rb{ctx,
249 2 + sizeof(Service::PSC::Time::SteadyClockTimePoint) / sizeof(u32)};
250 rb.Push(res);
251 rb.PushRaw<Service::PSC::Time::SteadyClockTimePoint>(time_point);
252}
253
254void StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx) {
255 LOG_DEBUG(Service_Time, "called.");
256
257 IPC::RequestParser rp{ctx};
258 auto context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()};
259
260 s64 time{};
261 auto res = CalculateMonotonicSystemClockBaseTimePoint(time, context);
262
263 IPC::ResponseBuilder rb{ctx, 4};
264 rb.Push(res);
265 rb.Push<s64>(time);
266}
267
268void StaticService::Handle_GetClockSnapshot(HLERequestContext& ctx) {
269 LOG_DEBUG(Service_Time, "called.");
270
271 IPC::RequestParser rp{ctx};
272 auto type{rp.PopEnum<Service::PSC::Time::TimeType>()};
273
274 Service::PSC::Time::ClockSnapshot snapshot{};
275 auto res = GetClockSnapshot(snapshot, type);
276
277 ctx.WriteBuffer(snapshot);
278
279 IPC::ResponseBuilder rb{ctx, 2};
280 rb.Push(res);
281}
282
283void StaticService::Handle_GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx) {
284 LOG_DEBUG(Service_Time, "called.");
285
286 IPC::RequestParser rp{ctx};
287 auto clock_type{rp.PopEnum<Service::PSC::Time::TimeType>()};
288 [[maybe_unused]] auto alignment{rp.Pop<u32>()};
289 auto user_context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()};
290 auto network_context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()};
291
292 Service::PSC::Time::ClockSnapshot snapshot{};
293 auto res =
294 GetClockSnapshotFromSystemClockContext(snapshot, user_context, network_context, clock_type);
295
296 ctx.WriteBuffer(snapshot);
297
298 IPC::ResponseBuilder rb{ctx, 2};
299 rb.Push(res);
300}
301
302void StaticService::Handle_CalculateStandardUserSystemClockDifferenceByUser(
303 HLERequestContext& ctx) {
304 LOG_DEBUG(Service_Time, "called.");
305
306 Service::PSC::Time::ClockSnapshot a{};
307 Service::PSC::Time::ClockSnapshot b{};
308
309 auto a_buffer{ctx.ReadBuffer(0)};
310 auto b_buffer{ctx.ReadBuffer(1)};
311
312 std::memcpy(&a, a_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot));
313 std::memcpy(&b, b_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot));
314
315 s64 difference{};
316 auto res = CalculateStandardUserSystemClockDifferenceByUser(difference, a, b);
317
318 IPC::ResponseBuilder rb{ctx, 4};
319 rb.Push(res);
320 rb.Push(difference);
321}
322
323void StaticService::Handle_CalculateSpanBetween(HLERequestContext& ctx) {
324 LOG_DEBUG(Service_Time, "called.");
325
326 Service::PSC::Time::ClockSnapshot a{};
327 Service::PSC::Time::ClockSnapshot b{};
328
329 auto a_buffer{ctx.ReadBuffer(0)};
330 auto b_buffer{ctx.ReadBuffer(1)};
331
332 std::memcpy(&a, a_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot));
333 std::memcpy(&b, b_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot));
334
335 s64 time{};
336 auto res = CalculateSpanBetween(time, a, b);
337
338 IPC::ResponseBuilder rb{ctx, 4};
339 rb.Push(res);
340 rb.Push(time);
341}
342
343// =============================== Implementations ===========================
344
345Result StaticService::GetStandardUserSystemClock(
346 std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) {
347 R_RETURN(m_wrapped_service->GetStandardUserSystemClock(out_service));
348}
349
350Result StaticService::GetStandardNetworkSystemClock(
351 std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) {
352 R_RETURN(m_wrapped_service->GetStandardNetworkSystemClock(out_service));
353}
354
355Result StaticService::GetStandardSteadyClock(
356 std::shared_ptr<Service::PSC::Time::SteadyClock>& out_service) {
357 R_RETURN(m_wrapped_service->GetStandardSteadyClock(out_service));
358}
359
360Result StaticService::GetTimeZoneService(std::shared_ptr<TimeZoneService>& out_service) {
361 out_service = std::make_shared<TimeZoneService>(m_system, m_file_timestamp_worker,
362 m_setup_info.can_write_timezone_device_location,
363 m_time_zone);
364 R_SUCCEED();
365}
366
367Result StaticService::GetStandardLocalSystemClock(
368 std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) {
369 R_RETURN(m_wrapped_service->GetStandardLocalSystemClock(out_service));
370}
371
372Result StaticService::GetEphemeralNetworkSystemClock(
373 std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) {
374 R_RETURN(m_wrapped_service->GetEphemeralNetworkSystemClock(out_service));
375}
376
377Result StaticService::GetSharedMemoryNativeHandle(Kernel::KSharedMemory** out_shared_memory) {
378 R_RETURN(m_wrapped_service->GetSharedMemoryNativeHandle(out_shared_memory));
379}
380
381Result StaticService::SetStandardSteadyClockInternalOffset(s64 offset_ns) {
382 R_UNLESS(m_setup_info.can_write_steady_clock, Service::PSC::Time::ResultPermissionDenied);
383
384 R_RETURN(m_set_sys->SetExternalSteadyClockInternalOffset(
385 offset_ns /
386 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()));
387}
388
389Result StaticService::GetStandardSteadyClockRtcValue(s64& out_rtc_value) {
390 R_RETURN(m_standard_steady_clock_resource.GetRtcTimeInSeconds(out_rtc_value));
391}
392
393Result StaticService::IsStandardUserSystemClockAutomaticCorrectionEnabled(
394 bool& out_automatic_correction) {
395 R_RETURN(m_wrapped_service->IsStandardUserSystemClockAutomaticCorrectionEnabled(
396 out_automatic_correction));
397}
398
399Result StaticService::SetStandardUserSystemClockAutomaticCorrectionEnabled(
400 bool automatic_correction) {
401 R_RETURN(m_wrapped_service->SetStandardUserSystemClockAutomaticCorrectionEnabled(
402 automatic_correction));
403}
404
405Result StaticService::GetStandardUserSystemClockInitialYear(s32& out_year) {
406 out_year = GetSettingsItemValue<s32>(m_set_sys, "time", "standard_user_clock_initial_year");
407 R_SUCCEED();
408}
409
410Result StaticService::IsStandardNetworkSystemClockAccuracySufficient(bool& out_is_sufficient) {
411 R_RETURN(m_wrapped_service->IsStandardNetworkSystemClockAccuracySufficient(out_is_sufficient));
412}
413
414Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
415 Service::PSC::Time::SteadyClockTimePoint& out_time_point) {
416 R_RETURN(m_wrapped_service->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
417 out_time_point));
418}
419
420Result StaticService::CalculateMonotonicSystemClockBaseTimePoint(
421 s64& out_time, Service::PSC::Time::SystemClockContext& context) {
422 R_RETURN(m_wrapped_service->CalculateMonotonicSystemClockBaseTimePoint(out_time, context));
423}
424
425Result StaticService::GetClockSnapshot(Service::PSC::Time::ClockSnapshot& out_snapshot,
426 Service::PSC::Time::TimeType type) {
427 R_RETURN(m_wrapped_service->GetClockSnapshot(out_snapshot, type));
428}
429
430Result StaticService::GetClockSnapshotFromSystemClockContext(
431 Service::PSC::Time::ClockSnapshot& out_snapshot,
432 Service::PSC::Time::SystemClockContext& user_context,
433 Service::PSC::Time::SystemClockContext& network_context, Service::PSC::Time::TimeType type) {
434 R_RETURN(m_wrapped_service->GetClockSnapshotFromSystemClockContext(out_snapshot, user_context,
435 network_context, type));
436}
437
438Result StaticService::CalculateStandardUserSystemClockDifferenceByUser(
439 s64& out_time, Service::PSC::Time::ClockSnapshot& a, Service::PSC::Time::ClockSnapshot& b) {
440 R_RETURN(m_wrapped_service->CalculateStandardUserSystemClockDifferenceByUser(out_time, a, b));
441}
442
443Result StaticService::CalculateSpanBetween(s64& out_time, Service::PSC::Time::ClockSnapshot& a,
444 Service::PSC::Time::ClockSnapshot& b) {
445 R_RETURN(m_wrapped_service->CalculateSpanBetween(out_time, a, b));
446}
447
448} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/static.h b/src/core/hle/service/glue/time/static.h
new file mode 100644
index 000000000..75fe4e2cd
--- /dev/null
+++ b/src/core/hle/service/glue/time/static.h
@@ -0,0 +1,110 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hle/service/glue/time/manager.h"
8#include "core/hle/service/glue/time/time_zone.h"
9#include "core/hle/service/psc/time/common.h"
10
11namespace Core {
12class System;
13}
14
15namespace Service::Set {
16class ISystemSettingsServer;
17}
18
19namespace Service::PSC::Time {
20class StaticService;
21class SystemClock;
22class SteadyClock;
23class TimeZoneService;
24class ServiceManager;
25} // namespace Service::PSC::Time
26
27namespace Service::Glue::Time {
28class FileTimestampWorker;
29class StandardSteadyClockResource;
30
31class StaticService final : public ServiceFramework<StaticService> {
32public:
33 explicit StaticService(Core::System& system,
34 Service::PSC::Time::StaticServiceSetupInfo setup_info,
35 std::shared_ptr<TimeManager> time, const char* name);
36
37 ~StaticService() override = default;
38
39 Result GetStandardUserSystemClock(
40 std::shared_ptr<Service::PSC::Time::SystemClock>& out_service);
41 Result GetStandardNetworkSystemClock(
42 std::shared_ptr<Service::PSC::Time::SystemClock>& out_service);
43 Result GetStandardSteadyClock(std::shared_ptr<Service::PSC::Time::SteadyClock>& out_service);
44 Result GetTimeZoneService(std::shared_ptr<TimeZoneService>& out_service);
45 Result GetStandardLocalSystemClock(
46 std::shared_ptr<Service::PSC::Time::SystemClock>& out_service);
47 Result GetEphemeralNetworkSystemClock(
48 std::shared_ptr<Service::PSC::Time::SystemClock>& out_service);
49 Result GetSharedMemoryNativeHandle(Kernel::KSharedMemory** out_shared_memory);
50 Result SetStandardSteadyClockInternalOffset(s64 offset);
51 Result GetStandardSteadyClockRtcValue(s64& out_rtc_value);
52 Result IsStandardUserSystemClockAutomaticCorrectionEnabled(bool& out_automatic_correction);
53 Result SetStandardUserSystemClockAutomaticCorrectionEnabled(bool automatic_correction);
54 Result GetStandardUserSystemClockInitialYear(s32& out_year);
55 Result IsStandardNetworkSystemClockAccuracySufficient(bool& out_is_sufficient);
56 Result GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
57 Service::PSC::Time::SteadyClockTimePoint& out_time_point);
58 Result CalculateMonotonicSystemClockBaseTimePoint(
59 s64& out_time, Service::PSC::Time::SystemClockContext& context);
60 Result GetClockSnapshot(Service::PSC::Time::ClockSnapshot& out_snapshot,
61 Service::PSC::Time::TimeType type);
62 Result GetClockSnapshotFromSystemClockContext(
63 Service::PSC::Time::ClockSnapshot& out_snapshot,
64 Service::PSC::Time::SystemClockContext& user_context,
65 Service::PSC::Time::SystemClockContext& network_context, Service::PSC::Time::TimeType type);
66 Result CalculateStandardUserSystemClockDifferenceByUser(s64& out_time,
67 Service::PSC::Time::ClockSnapshot& a,
68 Service::PSC::Time::ClockSnapshot& b);
69 Result CalculateSpanBetween(s64& out_time, Service::PSC::Time::ClockSnapshot& a,
70 Service::PSC::Time::ClockSnapshot& b);
71
72private:
73 Result GetClockSnapshotImpl(Service::PSC::Time::ClockSnapshot& out_snapshot,
74 Service::PSC::Time::SystemClockContext& user_context,
75 Service::PSC::Time::SystemClockContext& network_context,
76 Service::PSC::Time::TimeType type);
77
78 void Handle_GetStandardUserSystemClock(HLERequestContext& ctx);
79 void Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx);
80 void Handle_GetStandardSteadyClock(HLERequestContext& ctx);
81 void Handle_GetTimeZoneService(HLERequestContext& ctx);
82 void Handle_GetStandardLocalSystemClock(HLERequestContext& ctx);
83 void Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx);
84 void Handle_GetSharedMemoryNativeHandle(HLERequestContext& ctx);
85 void Handle_SetStandardSteadyClockInternalOffset(HLERequestContext& ctx);
86 void Handle_GetStandardSteadyClockRtcValue(HLERequestContext& ctx);
87 void Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx);
88 void Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx);
89 void Handle_GetStandardUserSystemClockInitialYear(HLERequestContext& ctx);
90 void Handle_IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx);
91 void Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx);
92 void Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx);
93 void Handle_GetClockSnapshot(HLERequestContext& ctx);
94 void Handle_GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx);
95 void Handle_CalculateStandardUserSystemClockDifferenceByUser(HLERequestContext& ctx);
96 void Handle_CalculateSpanBetween(HLERequestContext& ctx);
97
98 Core::System& m_system;
99
100 std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
101 std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m;
102 std::shared_ptr<Service::PSC::Time::StaticService> m_wrapped_service;
103
104 Service::PSC::Time::StaticServiceSetupInfo m_setup_info;
105 std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm;
106 std::shared_ptr<Service::PSC::Time::TimeZoneService> m_time_zone;
107 FileTimestampWorker& m_file_timestamp_worker;
108 StandardSteadyClockResource& m_standard_steady_clock_resource;
109};
110} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/time_zone.cpp b/src/core/hle/service/glue/time/time_zone.cpp
new file mode 100644
index 000000000..503c327dd
--- /dev/null
+++ b/src/core/hle/service/glue/time/time_zone.cpp
@@ -0,0 +1,377 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <chrono>
5
6#include "core/core.h"
7#include "core/hle/kernel/svc.h"
8#include "core/hle/service/glue/time/file_timestamp_worker.h"
9#include "core/hle/service/glue/time/time_zone.h"
10#include "core/hle/service/glue/time/time_zone_binary.h"
11#include "core/hle/service/psc/time/time_zone_service.h"
12#include "core/hle/service/set/system_settings_server.h"
13#include "core/hle/service/sm/sm.h"
14
15namespace Service::Glue::Time {
16namespace {
17static std::mutex g_list_mutex;
18static Common::IntrusiveListBaseTraits<Service::PSC::Time::OperationEvent>::ListType g_list_nodes{};
19} // namespace
20
21TimeZoneService::TimeZoneService(
22 Core::System& system_, FileTimestampWorker& file_timestamp_worker,
23 bool can_write_timezone_device_location,
24 std::shared_ptr<Service::PSC::Time::TimeZoneService> time_zone_service)
25 : ServiceFramework{system_, "ITimeZoneService"}, m_system{system},
26 m_can_write_timezone_device_location{can_write_timezone_device_location},
27 m_file_timestamp_worker{file_timestamp_worker},
28 m_wrapped_service{std::move(time_zone_service)}, m_operation_event{m_system} {
29 // clang-format off
30 static const FunctionInfo functions[] = {
31 {0, &TimeZoneService::Handle_GetDeviceLocationName, "GetDeviceLocationName"},
32 {1, &TimeZoneService::Handle_SetDeviceLocationName, "SetDeviceLocationName"},
33 {2, &TimeZoneService::Handle_GetTotalLocationNameCount, "GetTotalLocationNameCount"},
34 {3, &TimeZoneService::Handle_LoadLocationNameList, "LoadLocationNameList"},
35 {4, &TimeZoneService::Handle_LoadTimeZoneRule, "LoadTimeZoneRule"},
36 {5, &TimeZoneService::Handle_GetTimeZoneRuleVersion, "GetTimeZoneRuleVersion"},
37 {6, &TimeZoneService::Handle_GetDeviceLocationNameAndUpdatedTime, "GetDeviceLocationNameAndUpdatedTime"},
38 {7, &TimeZoneService::Handle_SetDeviceLocationNameWithTimeZoneRule, "SetDeviceLocationNameWithTimeZoneRule"},
39 {8, &TimeZoneService::Handle_ParseTimeZoneBinary, "ParseTimeZoneBinary"},
40 {20, &TimeZoneService::Handle_GetDeviceLocationNameOperationEventReadableHandle, "GetDeviceLocationNameOperationEventReadableHandle"},
41 {100, &TimeZoneService::Handle_ToCalendarTime, "ToCalendarTime"},
42 {101, &TimeZoneService::Handle_ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
43 {201, &TimeZoneService::Handle_ToPosixTime, "ToPosixTime"},
44 {202, &TimeZoneService::Handle_ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"},
45 };
46 // clang-format on
47 RegisterHandlers(functions);
48
49 g_list_nodes.clear();
50 m_set_sys =
51 m_system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
52}
53
54TimeZoneService::~TimeZoneService() = default;
55
56void TimeZoneService::Handle_GetDeviceLocationName(HLERequestContext& ctx) {
57 LOG_DEBUG(Service_Time, "called.");
58
59 Service::PSC::Time::LocationName name{};
60 auto res = GetDeviceLocationName(name);
61
62 IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::LocationName) / sizeof(u32)};
63 rb.Push(res);
64 rb.PushRaw<Service::PSC::Time::LocationName>(name);
65}
66
67void TimeZoneService::Handle_SetDeviceLocationName(HLERequestContext& ctx) {
68 LOG_DEBUG(Service_Time, "called.");
69
70 IPC::RequestParser rp{ctx};
71 auto name{rp.PopRaw<Service::PSC::Time::LocationName>()};
72
73 auto res = SetDeviceLocation(name);
74
75 IPC::ResponseBuilder rb{ctx, 2};
76 rb.Push(res);
77}
78
79void TimeZoneService::Handle_GetTotalLocationNameCount(HLERequestContext& ctx) {
80 LOG_DEBUG(Service_Time, "called.");
81
82 u32 count{};
83 auto res = GetTotalLocationNameCount(count);
84
85 IPC::ResponseBuilder rb{ctx, 3};
86 rb.Push(res);
87 rb.Push(count);
88}
89
90void TimeZoneService::Handle_LoadLocationNameList(HLERequestContext& ctx) {
91 LOG_DEBUG(Service_Time, "called.");
92
93 IPC::RequestParser rp{ctx};
94 auto index{rp.Pop<u32>()};
95
96 auto max_names{ctx.GetWriteBufferSize() / sizeof(Service::PSC::Time::LocationName)};
97
98 std::vector<Service::PSC::Time::LocationName> names{};
99 u32 count{};
100 auto res = LoadLocationNameList(count, names, max_names, index);
101
102 ctx.WriteBuffer(names);
103
104 IPC::ResponseBuilder rb{ctx, 3};
105 rb.Push(res);
106 rb.Push(count);
107}
108
109void TimeZoneService::Handle_LoadTimeZoneRule(HLERequestContext& ctx) {
110 LOG_DEBUG(Service_Time, "called.");
111
112 IPC::RequestParser rp{ctx};
113 auto name{rp.PopRaw<Service::PSC::Time::LocationName>()};
114
115 Tz::Rule rule{};
116 auto res = LoadTimeZoneRule(rule, name);
117
118 ctx.WriteBuffer(rule);
119
120 IPC::ResponseBuilder rb{ctx, 2};
121 rb.Push(res);
122}
123
124void TimeZoneService::Handle_GetTimeZoneRuleVersion(HLERequestContext& ctx) {
125 LOG_DEBUG(Service_Time, "called.");
126
127 Service::PSC::Time::RuleVersion rule_version{};
128 auto res = GetTimeZoneRuleVersion(rule_version);
129
130 IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::RuleVersion) / sizeof(u32)};
131 rb.Push(res);
132 rb.PushRaw<Service::PSC::Time::RuleVersion>(rule_version);
133}
134
135void TimeZoneService::Handle_GetDeviceLocationNameAndUpdatedTime(HLERequestContext& ctx) {
136 LOG_DEBUG(Service_Time, "called.");
137
138 Service::PSC::Time::LocationName name{};
139 Service::PSC::Time::SteadyClockTimePoint time_point{};
140 auto res = GetDeviceLocationNameAndUpdatedTime(time_point, name);
141
142 IPC::ResponseBuilder rb{ctx,
143 2 + (sizeof(Service::PSC::Time::LocationName) / sizeof(u32)) +
144 (sizeof(Service::PSC::Time::SteadyClockTimePoint) / sizeof(u32))};
145 rb.Push(res);
146 rb.PushRaw<Service::PSC::Time::LocationName>(name);
147 rb.PushRaw<Service::PSC::Time::SteadyClockTimePoint>(time_point);
148}
149
150void TimeZoneService::Handle_SetDeviceLocationNameWithTimeZoneRule(HLERequestContext& ctx) {
151 LOG_DEBUG(Service_Time, "called.");
152
153 auto res = SetDeviceLocationNameWithTimeZoneRule();
154
155 IPC::ResponseBuilder rb{ctx, 2};
156 rb.Push(res);
157}
158
159void TimeZoneService::Handle_ParseTimeZoneBinary(HLERequestContext& ctx) {
160 LOG_DEBUG(Service_Time, "called.");
161
162 IPC::ResponseBuilder rb{ctx, 2};
163 rb.Push(Service::PSC::Time::ResultNotImplemented);
164}
165
166void TimeZoneService::Handle_GetDeviceLocationNameOperationEventReadableHandle(
167 HLERequestContext& ctx) {
168 LOG_DEBUG(Service_Time, "called.");
169
170 Kernel::KEvent* event{};
171 auto res = GetDeviceLocationNameOperationEventReadableHandle(&event);
172
173 IPC::ResponseBuilder rb{ctx, 2, 1};
174 rb.Push(res);
175 rb.PushCopyObjects(event->GetReadableEvent());
176}
177
178void TimeZoneService::Handle_ToCalendarTime(HLERequestContext& ctx) {
179 LOG_DEBUG(Service_Time, "called.");
180
181 IPC::RequestParser rp{ctx};
182 auto time{rp.Pop<s64>()};
183
184 auto rule_buffer{ctx.ReadBuffer()};
185 Tz::Rule rule{};
186 std::memcpy(&rule, rule_buffer.data(), sizeof(Tz::Rule));
187
188 Service::PSC::Time::CalendarTime calendar_time{};
189 Service::PSC::Time::CalendarAdditionalInfo additional_info{};
190 auto res = ToCalendarTime(calendar_time, additional_info, time, rule);
191
192 IPC::ResponseBuilder rb{ctx,
193 2 + (sizeof(Service::PSC::Time::CalendarTime) / sizeof(u32)) +
194 (sizeof(Service::PSC::Time::CalendarAdditionalInfo) / sizeof(u32))};
195 rb.Push(res);
196 rb.PushRaw<Service::PSC::Time::CalendarTime>(calendar_time);
197 rb.PushRaw<Service::PSC::Time::CalendarAdditionalInfo>(additional_info);
198}
199
200void TimeZoneService::Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx) {
201 IPC::RequestParser rp{ctx};
202 auto time{rp.Pop<s64>()};
203
204 LOG_DEBUG(Service_Time, "called. time={}", time);
205
206 Service::PSC::Time::CalendarTime calendar_time{};
207 Service::PSC::Time::CalendarAdditionalInfo additional_info{};
208 auto res = ToCalendarTimeWithMyRule(calendar_time, additional_info, time);
209
210 IPC::ResponseBuilder rb{ctx,
211 2 + (sizeof(Service::PSC::Time::CalendarTime) / sizeof(u32)) +
212 (sizeof(Service::PSC::Time::CalendarAdditionalInfo) / sizeof(u32))};
213 rb.Push(res);
214 rb.PushRaw<Service::PSC::Time::CalendarTime>(calendar_time);
215 rb.PushRaw<Service::PSC::Time::CalendarAdditionalInfo>(additional_info);
216}
217
218void TimeZoneService::Handle_ToPosixTime(HLERequestContext& ctx) {
219 IPC::RequestParser rp{ctx};
220 auto calendar{rp.PopRaw<Service::PSC::Time::CalendarTime>()};
221
222 LOG_DEBUG(Service_Time, "called. calendar year {} month {} day {} hour {} minute {} second {}",
223 calendar.year, calendar.month, calendar.day, calendar.hour, calendar.minute,
224 calendar.second);
225
226 auto binary{ctx.ReadBuffer()};
227
228 Tz::Rule rule{};
229 std::memcpy(&rule, binary.data(), sizeof(Tz::Rule));
230
231 u32 count{};
232 std::array<s64, 2> times{};
233 u32 times_count{static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(s64))};
234
235 auto res = ToPosixTime(count, times, times_count, calendar, rule);
236
237 ctx.WriteBuffer(times);
238
239 IPC::ResponseBuilder rb{ctx, 3};
240 rb.Push(res);
241 rb.Push(count);
242}
243
244void TimeZoneService::Handle_ToPosixTimeWithMyRule(HLERequestContext& ctx) {
245 LOG_DEBUG(Service_Time, "called.");
246
247 IPC::RequestParser rp{ctx};
248 auto calendar{rp.PopRaw<Service::PSC::Time::CalendarTime>()};
249
250 u32 count{};
251 std::array<s64, 2> times{};
252 u32 times_count{static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(s64))};
253
254 auto res = ToPosixTimeWithMyRule(count, times, times_count, calendar);
255
256 ctx.WriteBuffer(times);
257
258 IPC::ResponseBuilder rb{ctx, 3};
259 rb.Push(res);
260 rb.Push(count);
261}
262
263// =============================== Implementations ===========================
264
265Result TimeZoneService::GetDeviceLocationName(Service::PSC::Time::LocationName& out_location_name) {
266 R_RETURN(m_wrapped_service->GetDeviceLocationName(out_location_name));
267}
268
269Result TimeZoneService::SetDeviceLocation(Service::PSC::Time::LocationName& location_name) {
270 R_UNLESS(m_can_write_timezone_device_location, Service::PSC::Time::ResultPermissionDenied);
271 R_UNLESS(IsTimeZoneBinaryValid(location_name), Service::PSC::Time::ResultTimeZoneNotFound);
272
273 std::scoped_lock l{m_mutex};
274
275 std::span<const u8> binary{};
276 size_t binary_size{};
277 R_TRY(GetTimeZoneRule(binary, binary_size, location_name))
278
279 R_TRY(m_wrapped_service->SetDeviceLocationNameWithTimeZoneRule(location_name, binary));
280
281 m_file_timestamp_worker.SetFilesystemPosixTime();
282
283 Service::PSC::Time::SteadyClockTimePoint time_point{};
284 Service::PSC::Time::LocationName name{};
285 R_TRY(m_wrapped_service->GetDeviceLocationNameAndUpdatedTime(time_point, name));
286
287 m_set_sys->SetDeviceTimeZoneLocationName(name);
288 m_set_sys->SetDeviceTimeZoneLocationUpdatedTime(time_point);
289
290 std::scoped_lock m{g_list_mutex};
291 for (auto& operation_event : g_list_nodes) {
292 operation_event.m_event->Signal();
293 }
294 R_SUCCEED();
295}
296
297Result TimeZoneService::GetTotalLocationNameCount(u32& out_count) {
298 R_RETURN(m_wrapped_service->GetTotalLocationNameCount(out_count));
299}
300
301Result TimeZoneService::LoadLocationNameList(
302 u32& out_count, std::vector<Service::PSC::Time::LocationName>& out_names, size_t max_names,
303 u32 index) {
304 std::scoped_lock l{m_mutex};
305 R_RETURN(GetTimeZoneLocationList(out_count, out_names, max_names, index));
306}
307
308Result TimeZoneService::LoadTimeZoneRule(Tz::Rule& out_rule,
309 Service::PSC::Time::LocationName& name) {
310 std::scoped_lock l{m_mutex};
311 std::span<const u8> binary{};
312 size_t binary_size{};
313 R_TRY(GetTimeZoneRule(binary, binary_size, name))
314 R_RETURN(m_wrapped_service->ParseTimeZoneBinary(out_rule, binary));
315}
316
317Result TimeZoneService::GetTimeZoneRuleVersion(Service::PSC::Time::RuleVersion& out_rule_version) {
318 R_RETURN(m_wrapped_service->GetTimeZoneRuleVersion(out_rule_version));
319}
320
321Result TimeZoneService::GetDeviceLocationNameAndUpdatedTime(
322 Service::PSC::Time::SteadyClockTimePoint& out_time_point,
323 Service::PSC::Time::LocationName& location_name) {
324 R_RETURN(m_wrapped_service->GetDeviceLocationNameAndUpdatedTime(out_time_point, location_name));
325}
326
327Result TimeZoneService::SetDeviceLocationNameWithTimeZoneRule() {
328 R_UNLESS(m_can_write_timezone_device_location, Service::PSC::Time::ResultPermissionDenied);
329 R_RETURN(Service::PSC::Time::ResultNotImplemented);
330}
331
332Result TimeZoneService::GetDeviceLocationNameOperationEventReadableHandle(
333 Kernel::KEvent** out_event) {
334 if (!operation_event_initialized) {
335 operation_event_initialized = false;
336
337 m_operation_event.m_ctx.CloseEvent(m_operation_event.m_event);
338 m_operation_event.m_event =
339 m_operation_event.m_ctx.CreateEvent("Psc:TimeZoneService:OperationEvent");
340 operation_event_initialized = true;
341 std::scoped_lock l{m_mutex};
342 g_list_nodes.push_back(m_operation_event);
343 }
344
345 *out_event = m_operation_event.m_event;
346 R_SUCCEED();
347}
348
349Result TimeZoneService::ToCalendarTime(
350 Service::PSC::Time::CalendarTime& out_calendar_time,
351 Service::PSC::Time::CalendarAdditionalInfo& out_additional_info, s64 time, Tz::Rule& rule) {
352 R_RETURN(m_wrapped_service->ToCalendarTime(out_calendar_time, out_additional_info, time, rule));
353}
354
355Result TimeZoneService::ToCalendarTimeWithMyRule(
356 Service::PSC::Time::CalendarTime& out_calendar_time,
357 Service::PSC::Time::CalendarAdditionalInfo& out_additional_info, s64 time) {
358 R_RETURN(
359 m_wrapped_service->ToCalendarTimeWithMyRule(out_calendar_time, out_additional_info, time));
360}
361
362Result TimeZoneService::ToPosixTime(u32& out_count, std::span<s64, 2> out_times,
363 u32 out_times_count,
364 Service::PSC::Time::CalendarTime& calendar_time,
365 Tz::Rule& rule) {
366 R_RETURN(
367 m_wrapped_service->ToPosixTime(out_count, out_times, out_times_count, calendar_time, rule));
368}
369
370Result TimeZoneService::ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times,
371 u32 out_times_count,
372 Service::PSC::Time::CalendarTime& calendar_time) {
373 R_RETURN(m_wrapped_service->ToPosixTimeWithMyRule(out_count, out_times, out_times_count,
374 calendar_time));
375}
376
377} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/time_zone.h b/src/core/hle/service/glue/time/time_zone.h
new file mode 100644
index 000000000..3c8ae4bf8
--- /dev/null
+++ b/src/core/hle/service/glue/time/time_zone.h
@@ -0,0 +1,95 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <mutex>
8#include <span>
9#include <vector>
10
11#include "core/hle/service/ipc_helpers.h"
12#include "core/hle/service/psc/time/common.h"
13#include "core/hle/service/server_manager.h"
14#include "core/hle/service/service.h"
15
16namespace Core {
17class System;
18}
19
20namespace Tz {
21struct Rule;
22}
23
24namespace Service::Set {
25class ISystemSettingsServer;
26}
27
28namespace Service::PSC::Time {
29class TimeZoneService;
30}
31
32namespace Service::Glue::Time {
33class FileTimestampWorker;
34
35class TimeZoneService final : public ServiceFramework<TimeZoneService> {
36public:
37 explicit TimeZoneService(
38 Core::System& system, FileTimestampWorker& file_timestamp_worker,
39 bool can_write_timezone_device_location,
40 std::shared_ptr<Service::PSC::Time::TimeZoneService> time_zone_service);
41
42 ~TimeZoneService() override;
43
44 Result GetDeviceLocationName(Service::PSC::Time::LocationName& out_location_name);
45 Result SetDeviceLocation(Service::PSC::Time::LocationName& location_name);
46 Result GetTotalLocationNameCount(u32& out_count);
47 Result LoadLocationNameList(u32& out_count,
48 std::vector<Service::PSC::Time::LocationName>& out_names,
49 size_t max_names, u32 index);
50 Result LoadTimeZoneRule(Tz::Rule& out_rule, Service::PSC::Time::LocationName& name);
51 Result GetTimeZoneRuleVersion(Service::PSC::Time::RuleVersion& out_rule_version);
52 Result GetDeviceLocationNameAndUpdatedTime(
53 Service::PSC::Time::SteadyClockTimePoint& out_time_point,
54 Service::PSC::Time::LocationName& location_name);
55 Result SetDeviceLocationNameWithTimeZoneRule();
56 Result GetDeviceLocationNameOperationEventReadableHandle(Kernel::KEvent** out_event);
57 Result ToCalendarTime(Service::PSC::Time::CalendarTime& out_calendar_time,
58 Service::PSC::Time::CalendarAdditionalInfo& out_additional_info, s64 time,
59 Tz::Rule& rule);
60 Result ToCalendarTimeWithMyRule(Service::PSC::Time::CalendarTime& out_calendar_time,
61 Service::PSC::Time::CalendarAdditionalInfo& out_additional_info,
62 s64 time);
63 Result ToPosixTime(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
64 Service::PSC::Time::CalendarTime& calendar_time, Tz::Rule& rule);
65 Result ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
66 Service::PSC::Time::CalendarTime& calendar_time);
67
68private:
69 void Handle_GetDeviceLocationName(HLERequestContext& ctx);
70 void Handle_SetDeviceLocationName(HLERequestContext& ctx);
71 void Handle_GetTotalLocationNameCount(HLERequestContext& ctx);
72 void Handle_LoadLocationNameList(HLERequestContext& ctx);
73 void Handle_LoadTimeZoneRule(HLERequestContext& ctx);
74 void Handle_GetTimeZoneRuleVersion(HLERequestContext& ctx);
75 void Handle_GetDeviceLocationNameAndUpdatedTime(HLERequestContext& ctx);
76 void Handle_SetDeviceLocationNameWithTimeZoneRule(HLERequestContext& ctx);
77 void Handle_ParseTimeZoneBinary(HLERequestContext& ctx);
78 void Handle_GetDeviceLocationNameOperationEventReadableHandle(HLERequestContext& ctx);
79 void Handle_ToCalendarTime(HLERequestContext& ctx);
80 void Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx);
81 void Handle_ToPosixTime(HLERequestContext& ctx);
82 void Handle_ToPosixTimeWithMyRule(HLERequestContext& ctx);
83
84 Core::System& m_system;
85 std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
86
87 bool m_can_write_timezone_device_location;
88 FileTimestampWorker& m_file_timestamp_worker;
89 std::shared_ptr<Service::PSC::Time::TimeZoneService> m_wrapped_service;
90 std::mutex m_mutex;
91 bool operation_event_initialized{};
92 Service::PSC::Time::OperationEvent m_operation_event;
93};
94
95} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/time_zone_binary.cpp b/src/core/hle/service/glue/time/time_zone_binary.cpp
new file mode 100644
index 000000000..67969aa3f
--- /dev/null
+++ b/src/core/hle/service/glue/time/time_zone_binary.cpp
@@ -0,0 +1,221 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/file_sys/content_archive.h"
6#include "core/file_sys/nca_metadata.h"
7#include "core/file_sys/registered_cache.h"
8#include "core/file_sys/romfs.h"
9#include "core/file_sys/system_archive/system_archive.h"
10#include "core/file_sys/vfs.h"
11#include "core/hle/service/filesystem/filesystem.h"
12#include "core/hle/service/glue/time/time_zone_binary.h"
13
14namespace Service::Glue::Time {
15namespace {
16constexpr u64 TimeZoneBinaryId = 0x10000000000080E;
17
18static FileSys::VirtualDir g_time_zone_binary_romfs{};
19static Result g_time_zone_binary_mount_result{ResultUnknown};
20static std::vector<u8> g_time_zone_scratch_space(0x2800, 0);
21
22Result TimeZoneReadBinary(size_t& out_read_size, std::span<u8> out_buffer, size_t out_buffer_size,
23 std::string_view path) {
24 R_UNLESS(g_time_zone_binary_mount_result == ResultSuccess, g_time_zone_binary_mount_result);
25
26 auto vfs_file{g_time_zone_binary_romfs->GetFileRelative(path)};
27 R_UNLESS(vfs_file, ResultUnknown);
28
29 auto file_size{vfs_file->GetSize()};
30 R_UNLESS(file_size > 0, ResultUnknown);
31
32 R_UNLESS(file_size <= out_buffer_size, Service::PSC::Time::ResultFailed);
33
34 out_read_size = vfs_file->Read(out_buffer.data(), file_size);
35 R_UNLESS(out_read_size > 0, ResultUnknown);
36
37 R_SUCCEED();
38}
39} // namespace
40
41void ResetTimeZoneBinary() {
42 g_time_zone_binary_romfs = {};
43 g_time_zone_binary_mount_result = ResultUnknown;
44 g_time_zone_scratch_space.clear();
45 g_time_zone_scratch_space.resize(0x2800, 0);
46}
47
48Result MountTimeZoneBinary(Core::System& system) {
49 ResetTimeZoneBinary();
50
51 auto& fsc{system.GetFileSystemController()};
52 std::unique_ptr<FileSys::NCA> nca{};
53
54 auto* bis_system = fsc.GetSystemNANDContents();
55
56 R_UNLESS(bis_system, ResultUnknown);
57
58 nca = bis_system->GetEntry(TimeZoneBinaryId, FileSys::ContentRecordType::Data);
59
60 if (nca) {
61 g_time_zone_binary_romfs = FileSys::ExtractRomFS(nca->GetRomFS());
62 }
63
64 if (g_time_zone_binary_romfs) {
65 // Validate that the romfs is readable, using invalid firmware keys can cause this to get
66 // set but the files to be garbage. In that case, we want to hit the next path and
67 // synthesise them instead.
68 Service::PSC::Time::LocationName name{"Etc/GMT"};
69 if (!IsTimeZoneBinaryValid(name)) {
70 ResetTimeZoneBinary();
71 }
72 }
73
74 if (!g_time_zone_binary_romfs) {
75 g_time_zone_binary_romfs = FileSys::ExtractRomFS(
76 FileSys::SystemArchive::SynthesizeSystemArchive(TimeZoneBinaryId));
77 }
78
79 R_UNLESS(g_time_zone_binary_romfs, ResultUnknown);
80
81 g_time_zone_binary_mount_result = ResultSuccess;
82 R_SUCCEED();
83}
84
85void GetTimeZoneBinaryListPath(std::string& out_path) {
86 if (g_time_zone_binary_mount_result != ResultSuccess) {
87 return;
88 }
89 // out_path = fmt::format("{}:/binaryList.txt", "TimeZoneBinary");
90 out_path = "/binaryList.txt";
91}
92
93void GetTimeZoneBinaryVersionPath(std::string& out_path) {
94 if (g_time_zone_binary_mount_result != ResultSuccess) {
95 return;
96 }
97 // out_path = fmt::format("{}:/version.txt", "TimeZoneBinary");
98 out_path = "/version.txt";
99}
100
101void GetTimeZoneZonePath(std::string& out_path, Service::PSC::Time::LocationName& name) {
102 if (g_time_zone_binary_mount_result != ResultSuccess) {
103 return;
104 }
105 // out_path = fmt::format("{}:/zoneinfo/{}", "TimeZoneBinary", name);
106 out_path = fmt::format("/zoneinfo/{}", name.name.data());
107}
108
109bool IsTimeZoneBinaryValid(Service::PSC::Time::LocationName& name) {
110 std::string path{};
111 GetTimeZoneZonePath(path, name);
112
113 auto vfs_file{g_time_zone_binary_romfs->GetFileRelative(path)};
114 if (!vfs_file) {
115 LOG_INFO(Service_Time, "Could not find timezone file {}", path);
116 return false;
117 }
118 return vfs_file->GetSize() != 0;
119}
120
121u32 GetTimeZoneCount() {
122 std::string path{};
123 GetTimeZoneBinaryListPath(path);
124
125 size_t bytes_read{};
126 if (TimeZoneReadBinary(bytes_read, g_time_zone_scratch_space, 0x2800, path) != ResultSuccess) {
127 return 0;
128 }
129 if (bytes_read == 0) {
130 return 0;
131 }
132
133 auto chars = std::span(reinterpret_cast<char*>(g_time_zone_scratch_space.data()), bytes_read);
134 u32 count{};
135 for (auto chr : chars) {
136 if (chr == '\n') {
137 count++;
138 }
139 }
140 return count;
141}
142
143Result GetTimeZoneVersion(Service::PSC::Time::RuleVersion& out_rule_version) {
144 std::string path{};
145 GetTimeZoneBinaryVersionPath(path);
146
147 auto rule_version_buffer{std::span(reinterpret_cast<u8*>(&out_rule_version),
148 sizeof(Service::PSC::Time::RuleVersion))};
149 size_t bytes_read{};
150 R_TRY(TimeZoneReadBinary(bytes_read, rule_version_buffer, rule_version_buffer.size_bytes(),
151 path));
152
153 rule_version_buffer[bytes_read] = 0;
154 R_SUCCEED();
155}
156
157Result GetTimeZoneRule(std::span<const u8>& out_rule, size_t& out_rule_size,
158 Service::PSC::Time::LocationName& name) {
159 std::string path{};
160 GetTimeZoneZonePath(path, name);
161
162 size_t bytes_read{};
163 R_TRY(TimeZoneReadBinary(bytes_read, g_time_zone_scratch_space,
164 g_time_zone_scratch_space.size(), path));
165
166 out_rule = std::span(g_time_zone_scratch_space.data(), bytes_read);
167 out_rule_size = bytes_read;
168 R_SUCCEED();
169}
170
171Result GetTimeZoneLocationList(u32& out_count,
172 std::vector<Service::PSC::Time::LocationName>& out_names,
173 size_t max_names, u32 index) {
174 std::string path{};
175 GetTimeZoneBinaryListPath(path);
176
177 size_t bytes_read{};
178 R_TRY(TimeZoneReadBinary(bytes_read, g_time_zone_scratch_space,
179 g_time_zone_scratch_space.size(), path));
180
181 out_count = 0;
182 R_SUCCEED_IF(bytes_read == 0);
183
184 Service::PSC::Time::LocationName current_name{};
185 size_t current_name_len{};
186 std::span<const u8> chars{g_time_zone_scratch_space};
187 u32 name_count{};
188
189 for (auto chr : chars) {
190 if (chr == '\r') {
191 continue;
192 }
193
194 if (chr == '\n') {
195 if (name_count >= index) {
196 out_names.push_back(current_name);
197 out_count++;
198 if (out_count >= max_names) {
199 break;
200 }
201 }
202 name_count++;
203 current_name_len = 0;
204 current_name = {};
205 continue;
206 }
207
208 if (chr == '\0') {
209 break;
210 }
211
212 R_UNLESS(current_name_len <= current_name.name.size() - 2,
213 Service::PSC::Time::ResultFailed);
214
215 current_name.name[current_name_len++] = chr;
216 }
217
218 R_SUCCEED();
219}
220
221} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/time_zone_binary.h b/src/core/hle/service/glue/time/time_zone_binary.h
new file mode 100644
index 000000000..2cad6b458
--- /dev/null
+++ b/src/core/hle/service/glue/time/time_zone_binary.h
@@ -0,0 +1,32 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <span>
7#include <string>
8#include <string_view>
9
10#include "core/hle/service/psc/time/common.h"
11
12namespace Core {
13class System;
14}
15
16namespace Service::Glue::Time {
17
18void ResetTimeZoneBinary();
19Result MountTimeZoneBinary(Core::System& system);
20void GetTimeZoneBinaryListPath(std::string& out_path);
21void GetTimeZoneBinaryVersionPath(std::string& out_path);
22void GetTimeZoneZonePath(std::string& out_path, Service::PSC::Time::LocationName& name);
23bool IsTimeZoneBinaryValid(Service::PSC::Time::LocationName& name);
24u32 GetTimeZoneCount();
25Result GetTimeZoneVersion(Service::PSC::Time::RuleVersion& out_rule_version);
26Result GetTimeZoneRule(std::span<const u8>& out_rule, size_t& out_rule_size,
27 Service::PSC::Time::LocationName& name);
28Result GetTimeZoneLocationList(u32& out_count,
29 std::vector<Service::PSC::Time::LocationName>& out_names,
30 size_t max_names, u32 index);
31
32} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/worker.cpp b/src/core/hle/service/glue/time/worker.cpp
new file mode 100644
index 000000000..ea0e49b90
--- /dev/null
+++ b/src/core/hle/service/glue/time/worker.cpp
@@ -0,0 +1,338 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/core_timing.h"
7#include "core/hle/service/glue/time/file_timestamp_worker.h"
8#include "core/hle/service/glue/time/standard_steady_clock_resource.h"
9#include "core/hle/service/glue/time/worker.h"
10#include "core/hle/service/psc/time/common.h"
11#include "core/hle/service/psc/time/service_manager.h"
12#include "core/hle/service/psc/time/static.h"
13#include "core/hle/service/psc/time/system_clock.h"
14#include "core/hle/service/set/system_settings_server.h"
15#include "core/hle/service/sm/sm.h"
16
17namespace Service::Glue::Time {
18namespace {
19
20bool g_ig_report_network_clock_context_set{};
21Service::PSC::Time::SystemClockContext g_report_network_clock_context{};
22bool g_ig_report_ephemeral_clock_context_set{};
23Service::PSC::Time::SystemClockContext g_report_ephemeral_clock_context{};
24
25template <typename T>
26T GetSettingsItemValue(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys,
27 const char* category, const char* name) {
28 std::vector<u8> interval_buf;
29 auto res = set_sys->GetSettingsItemValue(interval_buf, category, name);
30 ASSERT(res == ResultSuccess);
31
32 T v{};
33 std::memcpy(&v, interval_buf.data(), sizeof(T));
34 return v;
35}
36
37} // namespace
38
39TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource,
40 FileTimestampWorker& file_timestamp_worker)
41 : m_system{system}, m_ctx{m_system, "Glue:58"}, m_event{m_ctx.CreateEvent("Glue:58:Event")},
42 m_steady_clock_resource{steady_clock_resource},
43 m_file_timestamp_worker{file_timestamp_worker}, m_timer_steady_clock{m_ctx.CreateEvent(
44 "Glue:58:SteadyClockTimerEvent")},
45 m_timer_file_system{m_ctx.CreateEvent("Glue:58:FileTimeTimerEvent")},
46 m_alarm_worker{m_system, m_steady_clock_resource}, m_pm_state_change_handler{m_alarm_worker} {
47 g_ig_report_network_clock_context_set = false;
48 g_report_network_clock_context = {};
49 g_ig_report_ephemeral_clock_context_set = false;
50 g_report_ephemeral_clock_context = {};
51
52 m_timer_steady_clock_timing_event = Core::Timing::CreateEvent(
53 "Time::SteadyClockEvent",
54 [this](s64 time,
55 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
56 m_timer_steady_clock->Signal();
57 return std::nullopt;
58 });
59
60 m_timer_file_system_timing_event = Core::Timing::CreateEvent(
61 "Time::SteadyClockEvent",
62 [this](s64 time,
63 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
64 m_timer_file_system->Signal();
65 return std::nullopt;
66 });
67}
68
69TimeWorker::~TimeWorker() {
70 m_local_clock_event->Signal();
71 m_network_clock_event->Signal();
72 m_ephemeral_clock_event->Signal();
73 std::this_thread::sleep_for(std::chrono::milliseconds(16));
74
75 m_thread.request_stop();
76 m_event->Signal();
77 m_thread.join();
78
79 m_ctx.CloseEvent(m_event);
80 m_system.CoreTiming().UnscheduleEvent(m_timer_steady_clock_timing_event);
81 m_ctx.CloseEvent(m_timer_steady_clock);
82 m_system.CoreTiming().UnscheduleEvent(m_timer_file_system_timing_event);
83 m_ctx.CloseEvent(m_timer_file_system);
84}
85
86void TimeWorker::Initialize(std::shared_ptr<Service::PSC::Time::StaticService> time_sm,
87 std::shared_ptr<Service::Set::ISystemSettingsServer> set_sys) {
88 m_set_sys = std::move(set_sys);
89 m_time_m =
90 m_system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true);
91 m_time_sm = std::move(time_sm);
92
93 m_alarm_worker.Initialize(m_time_m);
94
95 auto steady_clock_interval_m = GetSettingsItemValue<s32>(
96 m_set_sys, "time", "standard_steady_clock_rtc_update_interval_minutes");
97
98 auto one_minute_ns{
99 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::minutes(1)).count()};
100 s64 steady_clock_interval_ns{steady_clock_interval_m * one_minute_ns};
101
102 m_system.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds(0),
103 std::chrono::nanoseconds(steady_clock_interval_ns),
104 m_timer_steady_clock_timing_event);
105
106 auto fs_notify_time_s =
107 GetSettingsItemValue<s32>(m_set_sys, "time", "notify_time_to_fs_interval_seconds");
108 auto one_second_ns{
109 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
110 s64 fs_notify_time_ns{fs_notify_time_s * one_second_ns};
111
112 m_system.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds(0),
113 std::chrono::nanoseconds(fs_notify_time_ns),
114 m_timer_file_system_timing_event);
115
116 auto res = m_time_sm->GetStandardLocalSystemClock(m_local_clock);
117 ASSERT(res == ResultSuccess);
118 res = m_time_m->GetStandardLocalClockOperationEvent(&m_local_clock_event);
119 ASSERT(res == ResultSuccess);
120
121 res = m_time_sm->GetStandardNetworkSystemClock(m_network_clock);
122 ASSERT(res == ResultSuccess);
123 res = m_time_m->GetStandardNetworkClockOperationEventForServiceManager(&m_network_clock_event);
124 ASSERT(res == ResultSuccess);
125
126 res = m_time_sm->GetEphemeralNetworkSystemClock(m_ephemeral_clock);
127 ASSERT(res == ResultSuccess);
128 res =
129 m_time_m->GetEphemeralNetworkClockOperationEventForServiceManager(&m_ephemeral_clock_event);
130 ASSERT(res == ResultSuccess);
131
132 res = m_time_m->GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(
133 &m_standard_user_auto_correct_clock_event);
134 ASSERT(res == ResultSuccess);
135}
136
137void TimeWorker::StartThread() {
138 m_thread = std::jthread(std::bind_front(&TimeWorker::ThreadFunc, this));
139}
140
141void TimeWorker::ThreadFunc(std::stop_token stop_token) {
142 Common::SetCurrentThreadName("TimeWorker");
143 Common::SetCurrentThreadPriority(Common::ThreadPriority::Low);
144
145 enum class EventType {
146 Exit = 0,
147 IpmModuleService_GetEvent = 1,
148 PowerStateChange = 2,
149 SignalAlarms = 3,
150 UpdateLocalSystemClock = 4,
151 UpdateNetworkSystemClock = 5,
152 UpdateEphemeralSystemClock = 6,
153 UpdateSteadyClock = 7,
154 UpdateFileTimestamp = 8,
155 AutoCorrect = 9,
156 Max = 10,
157 };
158
159 s32 num_objs{};
160 std::array<Kernel::KSynchronizationObject*, static_cast<u32>(EventType::Max)> wait_objs{};
161 std::array<EventType, static_cast<u32>(EventType::Max)> wait_indices{};
162
163 const auto AddWaiter{
164 [&](Kernel::KSynchronizationObject* synchronization_object, EventType type) {
165 // Open a new reference to the object.
166 synchronization_object->Open();
167
168 // Insert into the list.
169 wait_indices[num_objs] = type;
170 wait_objs[num_objs++] = synchronization_object;
171 }};
172
173 while (!stop_token.stop_requested()) {
174 SCOPE_EXIT({
175 for (s32 i = 0; i < num_objs; i++) {
176 wait_objs[i]->Close();
177 }
178 });
179
180 num_objs = {};
181 wait_objs = {};
182 if (m_pm_state_change_handler.m_priority != 0) {
183 AddWaiter(&m_event->GetReadableEvent(), EventType::Exit);
184 // TODO
185 // AddWaiter(gIPmModuleService::GetEvent(), 1);
186 AddWaiter(&m_alarm_worker.GetEvent().GetReadableEvent(), EventType::PowerStateChange);
187 } else {
188 AddWaiter(&m_event->GetReadableEvent(), EventType::Exit);
189 // TODO
190 // AddWaiter(gIPmModuleService::GetEvent(), 1);
191 AddWaiter(&m_alarm_worker.GetEvent().GetReadableEvent(), EventType::PowerStateChange);
192 AddWaiter(&m_alarm_worker.GetTimerEvent().GetReadableEvent(), EventType::SignalAlarms);
193 AddWaiter(&m_local_clock_event->GetReadableEvent(), EventType::UpdateLocalSystemClock);
194 AddWaiter(&m_network_clock_event->GetReadableEvent(),
195 EventType::UpdateNetworkSystemClock);
196 AddWaiter(&m_ephemeral_clock_event->GetReadableEvent(),
197 EventType::UpdateEphemeralSystemClock);
198 AddWaiter(&m_timer_steady_clock->GetReadableEvent(), EventType::UpdateSteadyClock);
199 AddWaiter(&m_timer_file_system->GetReadableEvent(), EventType::UpdateFileTimestamp);
200 AddWaiter(&m_standard_user_auto_correct_clock_event->GetReadableEvent(),
201 EventType::AutoCorrect);
202 }
203
204 s32 out_index{-1};
205 Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index, wait_objs.data(),
206 num_objs, -1);
207 ASSERT(out_index >= 0 && out_index < num_objs);
208
209 if (stop_token.stop_requested()) {
210 return;
211 }
212
213 switch (wait_indices[out_index]) {
214 case EventType::Exit:
215 return;
216
217 case EventType::IpmModuleService_GetEvent:
218 // TODO
219 // IPmModuleService::GetEvent()
220 // clear the event
221 // Handle power state change event
222 break;
223
224 case EventType::PowerStateChange:
225 m_alarm_worker.GetEvent().Clear();
226 if (m_pm_state_change_handler.m_priority <= 1) {
227 m_alarm_worker.OnPowerStateChanged();
228 }
229 break;
230
231 case EventType::SignalAlarms:
232 m_alarm_worker.GetTimerEvent().Clear();
233 m_time_m->CheckAndSignalAlarms();
234 break;
235
236 case EventType::UpdateLocalSystemClock: {
237 m_local_clock_event->Clear();
238
239 Service::PSC::Time::SystemClockContext context{};
240 auto res = m_local_clock->GetSystemClockContext(context);
241 ASSERT(res == ResultSuccess);
242
243 m_set_sys->SetUserSystemClockContext(context);
244
245 m_file_timestamp_worker.SetFilesystemPosixTime();
246 } break;
247
248 case EventType::UpdateNetworkSystemClock: {
249 m_network_clock_event->Clear();
250 Service::PSC::Time::SystemClockContext context{};
251 auto res = m_network_clock->GetSystemClockContext(context);
252 ASSERT(res == ResultSuccess);
253 m_set_sys->SetNetworkSystemClockContext(context);
254
255 s64 time{};
256 if (m_network_clock->GetCurrentTime(time) != ResultSuccess) {
257 break;
258 }
259
260 [[maybe_unused]] auto offset_before{
261 g_ig_report_network_clock_context_set ? g_report_network_clock_context.offset : 0};
262 // TODO system report "standard_netclock_operation"
263 // "clock_time" = time
264 // "context_offset_before" = offset_before
265 // "context_offset_after" = context.offset
266 g_report_network_clock_context = context;
267 if (!g_ig_report_network_clock_context_set) {
268 g_ig_report_network_clock_context_set = true;
269 }
270
271 m_file_timestamp_worker.SetFilesystemPosixTime();
272 } break;
273
274 case EventType::UpdateEphemeralSystemClock: {
275 m_ephemeral_clock_event->Clear();
276
277 Service::PSC::Time::SystemClockContext context{};
278 auto res = m_ephemeral_clock->GetSystemClockContext(context);
279 if (res != ResultSuccess) {
280 break;
281 }
282
283 s64 time{};
284 res = m_ephemeral_clock->GetCurrentTime(time);
285 if (res != ResultSuccess) {
286 break;
287 }
288
289 [[maybe_unused]] auto offset_before{g_ig_report_ephemeral_clock_context_set
290 ? g_report_ephemeral_clock_context.offset
291 : 0};
292 // TODO system report "ephemeral_netclock_operation"
293 // "clock_time" = time
294 // "context_offset_before" = offset_before
295 // "context_offset_after" = context.offset
296 g_report_ephemeral_clock_context = context;
297 if (!g_ig_report_ephemeral_clock_context_set) {
298 g_ig_report_ephemeral_clock_context_set = true;
299 }
300 } break;
301
302 case EventType::UpdateSteadyClock:
303 m_timer_steady_clock->Clear();
304
305 m_steady_clock_resource.UpdateTime();
306 m_time_m->SetStandardSteadyClockBaseTime(m_steady_clock_resource.GetTime());
307 break;
308
309 case EventType::UpdateFileTimestamp:
310 m_timer_file_system->Clear();
311
312 m_file_timestamp_worker.SetFilesystemPosixTime();
313 break;
314
315 case EventType::AutoCorrect: {
316 m_standard_user_auto_correct_clock_event->Clear();
317
318 bool automatic_correction{};
319 auto res = m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled(
320 automatic_correction);
321 ASSERT(res == ResultSuccess);
322
323 Service::PSC::Time::SteadyClockTimePoint time_point{};
324 res = m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
325 ASSERT(res == ResultSuccess);
326
327 m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction);
328 m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
329 } break;
330
331 default:
332 UNREACHABLE();
333 break;
334 }
335 }
336}
337
338} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/worker.h b/src/core/hle/service/glue/time/worker.h
new file mode 100644
index 000000000..adbbe6b6d
--- /dev/null
+++ b/src/core/hle/service/glue/time/worker.h
@@ -0,0 +1,64 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hle/kernel/k_event.h"
8#include "core/hle/service/glue/time/alarm_worker.h"
9#include "core/hle/service/glue/time/pm_state_change_handler.h"
10#include "core/hle/service/kernel_helpers.h"
11
12namespace Service::Set {
13class ISystemSettingsServer;
14}
15
16namespace Service::PSC::Time {
17class StaticService;
18class SystemClock;
19} // namespace Service::PSC::Time
20
21namespace Service::Glue::Time {
22class FileTimestampWorker;
23class StandardSteadyClockResource;
24
25class TimeWorker {
26public:
27 explicit TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource,
28 FileTimestampWorker& file_timestamp_worker);
29 ~TimeWorker();
30
31 void Initialize(std::shared_ptr<Service::PSC::Time::StaticService> time_sm,
32 std::shared_ptr<Service::Set::ISystemSettingsServer> set_sys);
33
34 void StartThread();
35
36private:
37 void ThreadFunc(std::stop_token stop_token);
38
39 Core::System& m_system;
40 KernelHelpers::ServiceContext m_ctx;
41 std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
42
43 std::jthread m_thread;
44 Kernel::KEvent* m_event{};
45 std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m;
46 std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm;
47 std::shared_ptr<Service::PSC::Time::SystemClock> m_network_clock;
48 std::shared_ptr<Service::PSC::Time::SystemClock> m_local_clock;
49 std::shared_ptr<Service::PSC::Time::SystemClock> m_ephemeral_clock;
50 StandardSteadyClockResource& m_steady_clock_resource;
51 FileTimestampWorker& m_file_timestamp_worker;
52 Kernel::KEvent* m_local_clock_event{};
53 Kernel::KEvent* m_network_clock_event{};
54 Kernel::KEvent* m_ephemeral_clock_event{};
55 Kernel::KEvent* m_standard_user_auto_correct_clock_event{};
56 Kernel::KEvent* m_timer_steady_clock{};
57 std::shared_ptr<Core::Timing::EventType> m_timer_steady_clock_timing_event;
58 Kernel::KEvent* m_timer_file_system{};
59 std::shared_ptr<Core::Timing::EventType> m_timer_file_system_timing_event;
60 AlarmWorker m_alarm_worker;
61 PmStateChangeHandler m_pm_state_change_handler;
62};
63
64} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/kernel_helpers.cpp b/src/core/hle/service/kernel_helpers.cpp
index f51e63564..f080f7ffa 100644
--- a/src/core/hle/service/kernel_helpers.cpp
+++ b/src/core/hle/service/kernel_helpers.cpp
@@ -65,6 +65,9 @@ Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) {
65} 65}
66 66
67void ServiceContext::CloseEvent(Kernel::KEvent* event) { 67void ServiceContext::CloseEvent(Kernel::KEvent* event) {
68 if (!event) {
69 return;
70 }
68 event->GetReadableEvent().Close(); 71 event->GetReadableEvent().Close();
69 event->Close(); 72 event->Close();
70} 73}
diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp
index cc7776efc..1e2d2d212 100644
--- a/src/core/hle/service/nfc/common/device.cpp
+++ b/src/core/hle/service/nfc/common/device.cpp
@@ -1,6 +1,8 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/hle/service/glue/time/static.h"
5#include "core/hle/service/psc/time/steady_clock.h"
4#ifdef _MSC_VER 6#ifdef _MSC_VER
5#pragma warning(push) 7#pragma warning(push)
6#pragma warning(disable : 4701) // Potentially uninitialized local variable 'result' used 8#pragma warning(disable : 4701) // Potentially uninitialized local variable 'result' used
@@ -29,7 +31,8 @@
29#include "core/hle/service/nfc/common/device.h" 31#include "core/hle/service/nfc/common/device.h"
30#include "core/hle/service/nfc/mifare_result.h" 32#include "core/hle/service/nfc/mifare_result.h"
31#include "core/hle/service/nfc/nfc_result.h" 33#include "core/hle/service/nfc/nfc_result.h"
32#include "core/hle/service/time/time_manager.h" 34#include "core/hle/service/service.h"
35#include "core/hle/service/sm/sm.h"
33#include "hid_core/frontend/emulated_controller.h" 36#include "hid_core/frontend/emulated_controller.h"
34#include "hid_core/hid_core.h" 37#include "hid_core/hid_core.h"
35#include "hid_core/hid_types.h" 38#include "hid_core/hid_types.h"
@@ -393,8 +396,7 @@ Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> paramet
393 return result; 396 return result;
394} 397}
395 398
396Result NfcDevice::SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout, 399Result NfcDevice::SendCommandByPassThrough(const s64& timeout, std::span<const u8> command_data,
397 std::span<const u8> command_data,
398 std::span<u8> out_data) { 400 std::span<u8> out_data) {
399 // Not implemented 401 // Not implemented
400 return ResultSuccess; 402 return ResultSuccess;
@@ -1399,27 +1401,41 @@ void NfcDevice::SetAmiiboName(NFP::AmiiboSettings& settings,
1399} 1401}
1400 1402
1401NFP::AmiiboDate NfcDevice::GetAmiiboDate(s64 posix_time) const { 1403NFP::AmiiboDate NfcDevice::GetAmiiboDate(s64 posix_time) const {
1402 const auto& time_zone_manager = 1404 auto static_service =
1403 system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager(); 1405 system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);
1404 Time::TimeZone::CalendarInfo calendar_info{}; 1406
1407 std::shared_ptr<Service::Glue::Time::TimeZoneService> timezone_service{};
1408 static_service->GetTimeZoneService(timezone_service);
1409
1410 Service::PSC::Time::CalendarTime calendar_time{};
1411 Service::PSC::Time::CalendarAdditionalInfo additional_info{};
1412
1405 NFP::AmiiboDate amiibo_date{}; 1413 NFP::AmiiboDate amiibo_date{};
1406 1414
1407 amiibo_date.SetYear(2000); 1415 amiibo_date.SetYear(2000);
1408 amiibo_date.SetMonth(1); 1416 amiibo_date.SetMonth(1);
1409 amiibo_date.SetDay(1); 1417 amiibo_date.SetDay(1);
1410 1418
1411 if (time_zone_manager.ToCalendarTime({}, posix_time, calendar_info) == ResultSuccess) { 1419 if (timezone_service->ToCalendarTimeWithMyRule(calendar_time, additional_info, posix_time) ==
1412 amiibo_date.SetYear(calendar_info.time.year); 1420 ResultSuccess) {
1413 amiibo_date.SetMonth(calendar_info.time.month); 1421 amiibo_date.SetYear(calendar_time.year);
1414 amiibo_date.SetDay(calendar_info.time.day); 1422 amiibo_date.SetMonth(calendar_time.month);
1423 amiibo_date.SetDay(calendar_time.day);
1415 } 1424 }
1416 1425
1417 return amiibo_date; 1426 return amiibo_date;
1418} 1427}
1419 1428
1420u64 NfcDevice::GetCurrentPosixTime() const { 1429s64 NfcDevice::GetCurrentPosixTime() const {
1421 auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()}; 1430 auto static_service =
1422 return standard_steady_clock.GetCurrentTimePoint(system).time_point; 1431 system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);
1432
1433 std::shared_ptr<Service::PSC::Time::SteadyClock> steady_clock{};
1434 static_service->GetStandardSteadyClock(steady_clock);
1435
1436 Service::PSC::Time::SteadyClockTimePoint time_point{};
1437 R_ASSERT(steady_clock->GetCurrentTimePoint(time_point));
1438 return time_point.time_point;
1423} 1439}
1424 1440
1425u64 NfcDevice::RemoveVersionByte(u64 application_id) const { 1441u64 NfcDevice::RemoveVersionByte(u64 application_id) const {
diff --git a/src/core/hle/service/nfc/common/device.h b/src/core/hle/service/nfc/common/device.h
index 15f9b25da..d59202d18 100644
--- a/src/core/hle/service/nfc/common/device.h
+++ b/src/core/hle/service/nfc/common/device.h
@@ -11,7 +11,6 @@
11#include "core/hle/service/nfc/nfc_types.h" 11#include "core/hle/service/nfc/nfc_types.h"
12#include "core/hle/service/nfp/nfp_types.h" 12#include "core/hle/service/nfp/nfp_types.h"
13#include "core/hle/service/service.h" 13#include "core/hle/service/service.h"
14#include "core/hle/service/time/clock_types.h"
15 14
16namespace Kernel { 15namespace Kernel {
17class KEvent; 16class KEvent;
@@ -49,8 +48,8 @@ public:
49 48
50 Result WriteMifare(std::span<const MifareWriteBlockParameter> parameters); 49 Result WriteMifare(std::span<const MifareWriteBlockParameter> parameters);
51 50
52 Result SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout, 51 Result SendCommandByPassThrough(const s64& timeout, std::span<const u8> command_data,
53 std::span<const u8> command_data, std::span<u8> out_data); 52 std::span<u8> out_data);
54 53
55 Result Mount(NFP::ModelType model_type, NFP::MountTarget mount_target); 54 Result Mount(NFP::ModelType model_type, NFP::MountTarget mount_target);
56 Result Unmount(); 55 Result Unmount();
@@ -108,7 +107,7 @@ private:
108 NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const; 107 NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const;
109 void SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name) const; 108 void SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name) const;
110 NFP::AmiiboDate GetAmiiboDate(s64 posix_time) const; 109 NFP::AmiiboDate GetAmiiboDate(s64 posix_time) const;
111 u64 GetCurrentPosixTime() const; 110 s64 GetCurrentPosixTime() const;
112 u64 RemoveVersionByte(u64 application_id) const; 111 u64 RemoveVersionByte(u64 application_id) const;
113 void UpdateSettingsCrc(); 112 void UpdateSettingsCrc();
114 void UpdateRegisterInfoCrc(); 113 void UpdateRegisterInfoCrc();
diff --git a/src/core/hle/service/nfc/common/device_manager.cpp b/src/core/hle/service/nfc/common/device_manager.cpp
index 44f651b87..b60699c45 100644
--- a/src/core/hle/service/nfc/common/device_manager.cpp
+++ b/src/core/hle/service/nfc/common/device_manager.cpp
@@ -6,12 +6,14 @@
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/hle/kernel/k_event.h" 8#include "core/hle/kernel/k_event.h"
9#include "core/hle/service/glue/time/static.h"
9#include "core/hle/service/ipc_helpers.h" 10#include "core/hle/service/ipc_helpers.h"
10#include "core/hle/service/nfc/common/device.h" 11#include "core/hle/service/nfc/common/device.h"
11#include "core/hle/service/nfc/common/device_manager.h" 12#include "core/hle/service/nfc/common/device_manager.h"
12#include "core/hle/service/nfc/nfc_result.h" 13#include "core/hle/service/nfc/nfc_result.h"
13#include "core/hle/service/time/clock_types.h" 14#include "core/hle/service/psc/time/steady_clock.h"
14#include "core/hle/service/time/time_manager.h" 15#include "core/hle/service/service.h"
16#include "core/hle/service/sm/sm.h"
15#include "hid_core/hid_types.h" 17#include "hid_core/hid_types.h"
16#include "hid_core/hid_util.h" 18#include "hid_core/hid_util.h"
17 19
@@ -82,11 +84,19 @@ Result DeviceManager::ListDevices(std::vector<u64>& nfp_devices, std::size_t max
82 continue; 84 continue;
83 } 85 }
84 if (skip_fatal_errors) { 86 if (skip_fatal_errors) {
85 constexpr u64 MinimumRecoveryTime = 60; 87 constexpr s64 MinimumRecoveryTime = 60;
86 auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()};
87 const u64 elapsed_time = standard_steady_clock.GetCurrentTimePoint(system).time_point -
88 time_since_last_error;
89 88
89 auto static_service =
90 system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u",
91 true);
92
93 std::shared_ptr<Service::PSC::Time::SteadyClock> steady_clock{};
94 static_service->GetStandardSteadyClock(steady_clock);
95
96 Service::PSC::Time::SteadyClockTimePoint time_point{};
97 R_ASSERT(steady_clock->GetCurrentTimePoint(time_point));
98
99 const s64 elapsed_time = time_point.time_point - time_since_last_error;
90 if (time_since_last_error != 0 && elapsed_time < MinimumRecoveryTime) { 100 if (time_since_last_error != 0 && elapsed_time < MinimumRecoveryTime) {
91 continue; 101 continue;
92 } 102 }
@@ -250,8 +260,7 @@ Result DeviceManager::WriteMifare(u64 device_handle,
250 return result; 260 return result;
251} 261}
252 262
253Result DeviceManager::SendCommandByPassThrough(u64 device_handle, 263Result DeviceManager::SendCommandByPassThrough(u64 device_handle, const s64& timeout,
254 const Time::Clock::TimeSpanType& timeout,
255 std::span<const u8> command_data, 264 std::span<const u8> command_data,
256 std::span<u8> out_data) { 265 std::span<u8> out_data) {
257 std::scoped_lock lock{mutex}; 266 std::scoped_lock lock{mutex};
@@ -741,8 +750,16 @@ Result DeviceManager::VerifyDeviceResult(std::shared_ptr<NfcDevice> device,
741 750
742 if (operation_result == ResultUnknown112 || operation_result == ResultUnknown114 || 751 if (operation_result == ResultUnknown112 || operation_result == ResultUnknown114 ||
743 operation_result == ResultUnknown115) { 752 operation_result == ResultUnknown115) {
744 auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()}; 753 auto static_service =
745 time_since_last_error = standard_steady_clock.GetCurrentTimePoint(system).time_point; 754 system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);
755
756 std::shared_ptr<Service::PSC::Time::SteadyClock> steady_clock{};
757 static_service->GetStandardSteadyClock(steady_clock);
758
759 Service::PSC::Time::SteadyClockTimePoint time_point{};
760 R_ASSERT(steady_clock->GetCurrentTimePoint(time_point));
761
762 time_since_last_error = time_point.time_point;
746 } 763 }
747 764
748 return operation_result; 765 return operation_result;
diff --git a/src/core/hle/service/nfc/common/device_manager.h b/src/core/hle/service/nfc/common/device_manager.h
index f02bdccf5..c56a2fbda 100644
--- a/src/core/hle/service/nfc/common/device_manager.h
+++ b/src/core/hle/service/nfc/common/device_manager.h
@@ -13,7 +13,6 @@
13#include "core/hle/service/nfc/nfc_types.h" 13#include "core/hle/service/nfc/nfc_types.h"
14#include "core/hle/service/nfp/nfp_types.h" 14#include "core/hle/service/nfp/nfp_types.h"
15#include "core/hle/service/service.h" 15#include "core/hle/service/service.h"
16#include "core/hle/service/time/clock_types.h"
17#include "hid_core/hid_types.h" 16#include "hid_core/hid_types.h"
18 17
19namespace Service::NFC { 18namespace Service::NFC {
@@ -42,7 +41,7 @@ public:
42 std::span<MifareReadBlockData> read_data); 41 std::span<MifareReadBlockData> read_data);
43 Result WriteMifare(u64 device_handle, 42 Result WriteMifare(u64 device_handle,
44 std::span<const MifareWriteBlockParameter> write_parameters); 43 std::span<const MifareWriteBlockParameter> write_parameters);
45 Result SendCommandByPassThrough(u64 device_handle, const Time::Clock::TimeSpanType& timeout, 44 Result SendCommandByPassThrough(u64 device_handle, const s64& timeout,
46 std::span<const u8> command_data, std::span<u8> out_data); 45 std::span<const u8> command_data, std::span<u8> out_data);
47 46
48 // Nfp device manager 47 // Nfp device manager
@@ -92,7 +91,7 @@ private:
92 const std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle) const; 91 const std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle) const;
93 92
94 bool is_initialized = false; 93 bool is_initialized = false;
95 u64 time_since_last_error = 0; 94 s64 time_since_last_error = 0;
96 mutable std::mutex mutex; 95 mutable std::mutex mutex;
97 std::array<std::shared_ptr<NfcDevice>, 10> devices{}; 96 std::array<std::shared_ptr<NfcDevice>, 10> devices{};
98 97
diff --git a/src/core/hle/service/nfc/nfc_interface.cpp b/src/core/hle/service/nfc/nfc_interface.cpp
index a71cf74b8..207ac4efe 100644
--- a/src/core/hle/service/nfc/nfc_interface.cpp
+++ b/src/core/hle/service/nfc/nfc_interface.cpp
@@ -13,7 +13,6 @@
13#include "core/hle/service/nfc/nfc_result.h" 13#include "core/hle/service/nfc/nfc_result.h"
14#include "core/hle/service/nfc/nfc_types.h" 14#include "core/hle/service/nfc/nfc_types.h"
15#include "core/hle/service/nfp/nfp_result.h" 15#include "core/hle/service/nfp/nfp_result.h"
16#include "core/hle/service/time/clock_types.h"
17#include "hid_core/hid_types.h" 16#include "hid_core/hid_types.h"
18 17
19namespace Service::NFC { 18namespace Service::NFC {
@@ -261,10 +260,10 @@ void NfcInterface::WriteMifare(HLERequestContext& ctx) {
261void NfcInterface::SendCommandByPassThrough(HLERequestContext& ctx) { 260void NfcInterface::SendCommandByPassThrough(HLERequestContext& ctx) {
262 IPC::RequestParser rp{ctx}; 261 IPC::RequestParser rp{ctx};
263 const auto device_handle{rp.Pop<u64>()}; 262 const auto device_handle{rp.Pop<u64>()};
264 const auto timeout{rp.PopRaw<Time::Clock::TimeSpanType>()}; 263 const auto timeout{rp.PopRaw<s64>()};
265 const auto command_data{ctx.ReadBuffer()}; 264 const auto command_data{ctx.ReadBuffer()};
266 LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, timeout={}, data_size={}", 265 LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, timeout={}, data_size={}",
267 device_handle, timeout.ToSeconds(), command_data.size()); 266 device_handle, timeout, command_data.size());
268 267
269 std::vector<u8> out_data(1); 268 std::vector<u8> out_data(1);
270 auto result = 269 auto result =
diff --git a/src/core/hle/service/ns/language.cpp b/src/core/hle/service/ns/language.cpp
index b1a7686ff..d187be935 100644
--- a/src/core/hle/service/ns/language.cpp
+++ b/src/core/hle/service/ns/language.cpp
@@ -415,4 +415,4 @@ std::optional<Set::LanguageCode> ConvertToLanguageCode(const ApplicationLanguage
415 return std::nullopt; 415 return std::nullopt;
416 } 416 }
417} 417}
418} // namespace Service::NS \ No newline at end of file 418} // namespace Service::NS
diff --git a/src/core/hle/service/ns/language.h b/src/core/hle/service/ns/language.h
index ab6b71029..dad9934f2 100644
--- a/src/core/hle/service/ns/language.h
+++ b/src/core/hle/service/ns/language.h
@@ -5,10 +5,7 @@
5 5
6#include <optional> 6#include <optional>
7#include "common/common_types.h" 7#include "common/common_types.h"
8 8#include "core/hle/service/set/system_settings_server.h"
9namespace Service::Set {
10enum class LanguageCode : u64;
11}
12 9
13namespace Service::NS { 10namespace Service::NS {
14/// This is nn::ns::detail::ApplicationLanguage 11/// This is nn::ns::detail::ApplicationLanguage
diff --git a/src/core/hle/service/psc/psc.cpp b/src/core/hle/service/psc/psc.cpp
index cd0cc9287..44310756b 100644
--- a/src/core/hle/service/psc/psc.cpp
+++ b/src/core/hle/service/psc/psc.cpp
@@ -4,9 +4,13 @@
4#include <memory> 4#include <memory>
5 5
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/core.h"
7#include "core/hle/service/ipc_helpers.h" 8#include "core/hle/service/ipc_helpers.h"
8#include "core/hle/service/psc/psc.h" 9#include "core/hle/service/psc/psc.h"
9#include "core/hle/service/server_manager.h" 10#include "core/hle/service/psc/time/manager.h"
11#include "core/hle/service/psc/time/power_state_service.h"
12#include "core/hle/service/psc/time/service_manager.h"
13#include "core/hle/service/psc/time/static.h"
10#include "core/hle/service/service.h" 14#include "core/hle/service/service.h"
11 15
12namespace Service::PSC { 16namespace Service::PSC {
@@ -76,6 +80,17 @@ void LoopProcess(Core::System& system) {
76 80
77 server_manager->RegisterNamedService("psc:c", std::make_shared<IPmControl>(system)); 81 server_manager->RegisterNamedService("psc:c", std::make_shared<IPmControl>(system));
78 server_manager->RegisterNamedService("psc:m", std::make_shared<IPmService>(system)); 82 server_manager->RegisterNamedService("psc:m", std::make_shared<IPmService>(system));
83
84 auto time = std::make_shared<Time::TimeManager>(system);
85
86 server_manager->RegisterNamedService(
87 "time:m", std::make_shared<Time::ServiceManager>(system, time, server_manager.get()));
88 server_manager->RegisterNamedService(
89 "time:su", std::make_shared<Time::StaticService>(
90 system, Time::StaticServiceSetupInfo{0, 0, 0, 0, 0, 1}, time, "time:su"));
91 server_manager->RegisterNamedService("time:al",
92 std::make_shared<Time::IAlarmService>(system, time));
93
79 ServerManager::RunServer(std::move(server_manager)); 94 ServerManager::RunServer(std::move(server_manager));
80} 95}
81 96
diff --git a/src/core/hle/service/psc/time/alarms.cpp b/src/core/hle/service/psc/time/alarms.cpp
new file mode 100644
index 000000000..5e52c19f8
--- /dev/null
+++ b/src/core/hle/service/psc/time/alarms.cpp
@@ -0,0 +1,209 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/service/psc/time/alarms.h"
6#include "core/hle/service/psc/time/manager.h"
7
8namespace Service::PSC::Time {
9Alarm::Alarm(Core::System& system, KernelHelpers::ServiceContext& ctx, AlarmType type)
10 : m_ctx{ctx}, m_event{ctx.CreateEvent("Psc:Alarm:Event")} {
11 m_event->Clear();
12
13 switch (type) {
14 case WakeupAlarm:
15 m_priority = 1;
16 break;
17 case BackgroundTaskAlarm:
18 m_priority = 0;
19 break;
20 default:
21 UNREACHABLE();
22 return;
23 }
24}
25
26Alarm::~Alarm() {
27 m_ctx.CloseEvent(m_event);
28}
29
30Alarms::Alarms(Core::System& system, StandardSteadyClockCore& steady_clock,
31 PowerStateRequestManager& power_state_request_manager)
32 : m_system{system}, m_ctx{system, "Psc:Alarms"}, m_steady_clock{steady_clock},
33 m_power_state_request_manager{power_state_request_manager}, m_event{m_ctx.CreateEvent(
34 "Psc:Alarms:Event")} {}
35
36Alarms::~Alarms() {
37 m_ctx.CloseEvent(m_event);
38}
39
40Result Alarms::Enable(Alarm& alarm, s64 time) {
41 R_UNLESS(m_steady_clock.IsInitialized(), ResultClockUninitialized);
42
43 std::scoped_lock l{m_mutex};
44 R_UNLESS(alarm.IsLinked(), ResultAlarmNotRegistered);
45
46 auto time_ns{time + m_steady_clock.GetRawTime()};
47 auto one_second_ns{
48 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
49 time_ns = Common::AlignUp(time_ns, one_second_ns);
50 alarm.SetAlertTime(time_ns);
51
52 Insert(alarm);
53 R_RETURN(UpdateClosestAndSignal());
54}
55
56void Alarms::Disable(Alarm& alarm) {
57 std::scoped_lock l{m_mutex};
58 if (!alarm.IsLinked()) {
59 return;
60 }
61
62 Erase(alarm);
63 UpdateClosestAndSignal();
64}
65
66void Alarms::CheckAndSignal() {
67 std::scoped_lock l{m_mutex};
68 if (m_alarms.empty()) {
69 return;
70 }
71
72 bool alarm_signalled{false};
73 for (auto& alarm : m_alarms) {
74 if (m_steady_clock.GetRawTime() >= alarm.GetAlertTime()) {
75 alarm.Signal();
76 alarm.Lock();
77 Erase(alarm);
78
79 m_power_state_request_manager.UpdatePendingPowerStateRequestPriority(
80 alarm.GetPriority());
81 alarm_signalled = true;
82 }
83 }
84
85 if (!alarm_signalled) {
86 return;
87 }
88
89 m_power_state_request_manager.SignalPowerStateRequestAvailability();
90 UpdateClosestAndSignal();
91}
92
93bool Alarms::GetClosestAlarm(Alarm** out_alarm) {
94 std::scoped_lock l{m_mutex};
95 auto alarm = m_alarms.empty() ? nullptr : std::addressof(m_alarms.front());
96 *out_alarm = alarm;
97 return alarm != nullptr;
98}
99
100void Alarms::Insert(Alarm& alarm) {
101 // Alarms are sorted by alert time, then priority
102 auto it{m_alarms.begin()};
103 while (it != m_alarms.end()) {
104 if (alarm.GetAlertTime() < it->GetAlertTime() ||
105 (alarm.GetAlertTime() == it->GetAlertTime() &&
106 alarm.GetPriority() < it->GetPriority())) {
107 m_alarms.insert(it, alarm);
108 return;
109 }
110 it++;
111 }
112
113 m_alarms.push_back(alarm);
114}
115
116void Alarms::Erase(Alarm& alarm) {
117 m_alarms.erase(m_alarms.iterator_to(alarm));
118}
119
120Result Alarms::UpdateClosestAndSignal() {
121 m_closest_alarm = m_alarms.empty() ? nullptr : std::addressof(m_alarms.front());
122 R_SUCCEED_IF(m_closest_alarm == nullptr);
123
124 m_event->Signal();
125
126 R_SUCCEED();
127}
128
129IAlarmService::IAlarmService(Core::System& system_, std::shared_ptr<TimeManager> manager)
130 : ServiceFramework{system_, "time:al"}, m_system{system}, m_alarms{manager->m_alarms} {
131 // clang-format off
132 static const FunctionInfo functions[] = {
133 {0, &IAlarmService::CreateWakeupAlarm, "CreateWakeupAlarm"},
134 {1, &IAlarmService::CreateBackgroundTaskAlarm, "CreateBackgroundTaskAlarm"},
135 };
136 // clang-format on
137 RegisterHandlers(functions);
138}
139
140void IAlarmService::CreateWakeupAlarm(HLERequestContext& ctx) {
141 LOG_DEBUG(Service_Time, "called.");
142
143 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
144 rb.Push(ResultSuccess);
145 rb.PushIpcInterface<ISteadyClockAlarm>(system, m_alarms, AlarmType::WakeupAlarm);
146}
147
148void IAlarmService::CreateBackgroundTaskAlarm(HLERequestContext& ctx) {
149 LOG_DEBUG(Service_Time, "called.");
150
151 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
152 rb.Push(ResultSuccess);
153 rb.PushIpcInterface<ISteadyClockAlarm>(system, m_alarms, AlarmType::BackgroundTaskAlarm);
154}
155
156ISteadyClockAlarm::ISteadyClockAlarm(Core::System& system_, Alarms& alarms, AlarmType type)
157 : ServiceFramework{system_, "ISteadyClockAlarm"}, m_ctx{system, "Psc:ISteadyClockAlarm"},
158 m_alarms{alarms}, m_alarm{system, m_ctx, type} {
159 // clang-format off
160 static const FunctionInfo functions[] = {
161 {0, &ISteadyClockAlarm::GetAlarmEvent, "GetAlarmEvent"},
162 {1, &ISteadyClockAlarm::Enable, "Enable"},
163 {2, &ISteadyClockAlarm::Disable, "Disable"},
164 {3, &ISteadyClockAlarm::IsEnabled, "IsEnabled"},
165 {10, nullptr, "CreateWakeLock"},
166 {11, nullptr, "DestroyWakeLock"},
167 };
168 // clang-format on
169 RegisterHandlers(functions);
170}
171
172void ISteadyClockAlarm::GetAlarmEvent(HLERequestContext& ctx) {
173 LOG_DEBUG(Service_Time, "called.");
174
175 IPC::ResponseBuilder rb{ctx, 2, 1};
176 rb.Push(ResultSuccess);
177 rb.PushCopyObjects(m_alarm.GetEventHandle());
178}
179
180void ISteadyClockAlarm::Enable(HLERequestContext& ctx) {
181 LOG_DEBUG(Service_Time, "called.");
182
183 IPC::RequestParser rp{ctx};
184 auto time{rp.Pop<s64>()};
185
186 auto res = m_alarms.Enable(m_alarm, time);
187
188 IPC::ResponseBuilder rb{ctx, 2};
189 rb.Push(res);
190}
191
192void ISteadyClockAlarm::Disable(HLERequestContext& ctx) {
193 LOG_DEBUG(Service_Time, "called.");
194
195 m_alarms.Disable(m_alarm);
196
197 IPC::ResponseBuilder rb{ctx, 2};
198 rb.Push(ResultSuccess);
199}
200
201void ISteadyClockAlarm::IsEnabled(HLERequestContext& ctx) {
202 LOG_DEBUG(Service_Time, "called.");
203
204 IPC::ResponseBuilder rb{ctx, 3};
205 rb.Push(ResultSuccess);
206 rb.Push<bool>(m_alarm.IsLinked());
207}
208
209} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/alarms.h b/src/core/hle/service/psc/time/alarms.h
new file mode 100644
index 000000000..597770028
--- /dev/null
+++ b/src/core/hle/service/psc/time/alarms.h
@@ -0,0 +1,139 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <mutex>
7
8#include "core/hle/kernel/k_event.h"
9#include "core/hle/service/ipc_helpers.h"
10#include "core/hle/service/kernel_helpers.h"
11#include "core/hle/service/psc/time/clocks/standard_steady_clock_core.h"
12#include "core/hle/service/psc/time/common.h"
13#include "core/hle/service/psc/time/power_state_request_manager.h"
14#include "core/hle/service/server_manager.h"
15#include "core/hle/service/service.h"
16
17namespace Core {
18class System;
19}
20
21namespace Service::PSC::Time {
22class TimeManager;
23
24enum AlarmType : u32 {
25 WakeupAlarm = 0,
26 BackgroundTaskAlarm = 1,
27};
28
29struct Alarm : public Common::IntrusiveListBaseNode<Alarm> {
30 using AlarmList = Common::IntrusiveListBaseTraits<Alarm>::ListType;
31
32 Alarm(Core::System& system, KernelHelpers::ServiceContext& ctx, AlarmType type);
33 ~Alarm();
34
35 Kernel::KReadableEvent& GetEventHandle() {
36 return m_event->GetReadableEvent();
37 }
38
39 s64 GetAlertTime() const {
40 return m_alert_time;
41 }
42
43 void SetAlertTime(s64 time) {
44 m_alert_time = time;
45 }
46
47 u32 GetPriority() const {
48 return m_priority;
49 }
50
51 void Signal() {
52 m_event->Signal();
53 }
54
55 Result Lock() {
56 // TODO
57 // if (m_lock_service) {
58 // return m_lock_service->Lock();
59 // }
60 R_SUCCEED();
61 }
62
63 KernelHelpers::ServiceContext& m_ctx;
64
65 u32 m_priority;
66 Kernel::KEvent* m_event{};
67 s64 m_alert_time{};
68 // TODO
69 // nn::psc::sf::IPmStateLock* m_lock_service{};
70};
71
72class Alarms {
73public:
74 explicit Alarms(Core::System& system, StandardSteadyClockCore& steady_clock,
75 PowerStateRequestManager& power_state_request_manager);
76 ~Alarms();
77
78 Kernel::KEvent& GetEvent() {
79 return *m_event;
80 }
81
82 s64 GetRawTime() {
83 return m_steady_clock.GetRawTime();
84 }
85
86 Result Enable(Alarm& alarm, s64 time);
87 void Disable(Alarm& alarm);
88 void CheckAndSignal();
89 bool GetClosestAlarm(Alarm** out_alarm);
90
91private:
92 void Insert(Alarm& alarm);
93 void Erase(Alarm& alarm);
94 Result UpdateClosestAndSignal();
95
96 Core::System& m_system;
97 KernelHelpers::ServiceContext m_ctx;
98
99 StandardSteadyClockCore& m_steady_clock;
100 PowerStateRequestManager& m_power_state_request_manager;
101 Alarm::AlarmList m_alarms;
102 Kernel::KEvent* m_event{};
103 Alarm* m_closest_alarm{};
104 std::mutex m_mutex;
105};
106
107class IAlarmService final : public ServiceFramework<IAlarmService> {
108public:
109 explicit IAlarmService(Core::System& system, std::shared_ptr<TimeManager> manager);
110
111 ~IAlarmService() override = default;
112
113private:
114 void CreateWakeupAlarm(HLERequestContext& ctx);
115 void CreateBackgroundTaskAlarm(HLERequestContext& ctx);
116
117 Core::System& m_system;
118 Alarms& m_alarms;
119};
120
121class ISteadyClockAlarm final : public ServiceFramework<ISteadyClockAlarm> {
122public:
123 explicit ISteadyClockAlarm(Core::System& system, Alarms& alarms, AlarmType type);
124
125 ~ISteadyClockAlarm() override = default;
126
127private:
128 void GetAlarmEvent(HLERequestContext& ctx);
129 void Enable(HLERequestContext& ctx);
130 void Disable(HLERequestContext& ctx);
131 void IsEnabled(HLERequestContext& ctx);
132
133 KernelHelpers::ServiceContext m_ctx;
134
135 Alarms& m_alarms;
136 Alarm m_alarm;
137};
138
139} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/context_writers.cpp b/src/core/hle/service/psc/time/clocks/context_writers.cpp
new file mode 100644
index 000000000..ac8700f76
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/context_writers.cpp
@@ -0,0 +1,83 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/service/psc/time/clocks/context_writers.h"
6
7namespace Service::PSC::Time {
8
9void ContextWriter::SignalAllNodes() {
10 std::scoped_lock l{m_mutex};
11 for (auto& operation : m_operation_events) {
12 operation.m_event->Signal();
13 }
14}
15
16void ContextWriter::Link(OperationEvent& operation_event) {
17 std::scoped_lock l{m_mutex};
18 m_operation_events.push_back(operation_event);
19}
20
21LocalSystemClockContextWriter::LocalSystemClockContextWriter(Core::System& system,
22 SharedMemory& shared_memory)
23 : m_system{system}, m_shared_memory{shared_memory} {}
24
25Result LocalSystemClockContextWriter::Write(SystemClockContext& context) {
26 if (m_in_use) {
27 R_SUCCEED_IF(context == m_context);
28 m_context = context;
29 } else {
30 m_context = context;
31 m_in_use = true;
32 }
33
34 m_shared_memory.SetLocalSystemContext(context);
35
36 SignalAllNodes();
37
38 R_SUCCEED();
39}
40
41NetworkSystemClockContextWriter::NetworkSystemClockContextWriter(Core::System& system,
42 SharedMemory& shared_memory,
43 SystemClockCore& system_clock)
44 : m_system{system}, m_shared_memory{shared_memory}, m_system_clock{system_clock} {}
45
46Result NetworkSystemClockContextWriter::Write(SystemClockContext& context) {
47 s64 time{};
48 [[maybe_unused]] auto res = m_system_clock.GetCurrentTime(&time);
49
50 if (m_in_use) {
51 R_SUCCEED_IF(context == m_context);
52 m_context = context;
53 } else {
54 m_context = context;
55 m_in_use = true;
56 }
57
58 m_shared_memory.SetNetworkSystemContext(context);
59
60 SignalAllNodes();
61
62 R_SUCCEED();
63}
64
65EphemeralNetworkSystemClockContextWriter::EphemeralNetworkSystemClockContextWriter(
66 Core::System& system)
67 : m_system{system} {}
68
69Result EphemeralNetworkSystemClockContextWriter::Write(SystemClockContext& context) {
70 if (m_in_use) {
71 R_SUCCEED_IF(context == m_context);
72 m_context = context;
73 } else {
74 m_context = context;
75 m_in_use = true;
76 }
77
78 SignalAllNodes();
79
80 R_SUCCEED();
81}
82
83} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/context_writers.h b/src/core/hle/service/psc/time/clocks/context_writers.h
new file mode 100644
index 000000000..afd3725d4
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/context_writers.h
@@ -0,0 +1,79 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <list>
7
8#include "common/common_types.h"
9#include "core/hle/kernel/k_event.h"
10#include "core/hle/service/psc/time/clocks/system_clock_core.h"
11#include "core/hle/service/psc/time/common.h"
12#include "core/hle/service/psc/time/shared_memory.h"
13
14namespace Core {
15class System;
16}
17
18namespace Service::PSC::Time {
19
20class ContextWriter {
21private:
22 using OperationEventList = Common::IntrusiveListBaseTraits<OperationEvent>::ListType;
23
24public:
25 virtual ~ContextWriter() = default;
26
27 virtual Result Write(SystemClockContext& context) = 0;
28 void SignalAllNodes();
29 void Link(OperationEvent& operation_event);
30
31private:
32 OperationEventList m_operation_events;
33 std::mutex m_mutex;
34};
35
36class LocalSystemClockContextWriter : public ContextWriter {
37public:
38 explicit LocalSystemClockContextWriter(Core::System& system, SharedMemory& shared_memory);
39
40 Result Write(SystemClockContext& context) override;
41
42private:
43 Core::System& m_system;
44
45 SharedMemory& m_shared_memory;
46 bool m_in_use{};
47 SystemClockContext m_context{};
48};
49
50class NetworkSystemClockContextWriter : public ContextWriter {
51public:
52 explicit NetworkSystemClockContextWriter(Core::System& system, SharedMemory& shared_memory,
53 SystemClockCore& system_clock);
54
55 Result Write(SystemClockContext& context) override;
56
57private:
58 Core::System& m_system;
59
60 SharedMemory& m_shared_memory;
61 bool m_in_use{};
62 SystemClockContext m_context{};
63 SystemClockCore& m_system_clock;
64};
65
66class EphemeralNetworkSystemClockContextWriter : public ContextWriter {
67public:
68 EphemeralNetworkSystemClockContextWriter(Core::System& system);
69
70 Result Write(SystemClockContext& context) override;
71
72private:
73 Core::System& m_system;
74
75 bool m_in_use{};
76 SystemClockContext m_context{};
77};
78
79} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h b/src/core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h
new file mode 100644
index 000000000..0a68867d9
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h
@@ -0,0 +1,21 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/result.h"
7#include "core/hle/service/psc/time/clocks/context_writers.h"
8#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
9#include "core/hle/service/psc/time/clocks/system_clock_core.h"
10#include "core/hle/service/psc/time/common.h"
11
12namespace Service::PSC::Time {
13
14class EphemeralNetworkSystemClockCore : public SystemClockCore {
15public:
16 explicit EphemeralNetworkSystemClockCore(SteadyClockCore& steady_clock)
17 : SystemClockCore{steady_clock} {}
18 ~EphemeralNetworkSystemClockCore() override = default;
19};
20
21} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp
new file mode 100644
index 000000000..36dca6689
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp
@@ -0,0 +1,20 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h"
5
6namespace Service::PSC::Time {
7
8void StandardLocalSystemClockCore::Initialize(SystemClockContext& context, s64 time) {
9 SteadyClockTimePoint time_point{};
10 if (GetCurrentTimePoint(time_point) == ResultSuccess &&
11 context.steady_time_point.IdMatches(time_point)) {
12 SetContextAndWrite(context);
13 } else if (SetCurrentTime(time) != ResultSuccess) {
14 LOG_ERROR(Service_Time, "Failed to SetCurrentTime");
15 }
16
17 SetInitialized();
18}
19
20} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h
new file mode 100644
index 000000000..176ba3e94
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h
@@ -0,0 +1,23 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/result.h"
7#include "core/hle/service/psc/time/clocks/context_writers.h"
8#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
9#include "core/hle/service/psc/time/clocks/system_clock_core.h"
10#include "core/hle/service/psc/time/common.h"
11
12namespace Service::PSC::Time {
13
14class StandardLocalSystemClockCore : public SystemClockCore {
15public:
16 explicit StandardLocalSystemClockCore(SteadyClockCore& steady_clock)
17 : SystemClockCore{steady_clock} {}
18 ~StandardLocalSystemClockCore() override = default;
19
20 void Initialize(SystemClockContext& context, s64 time);
21};
22
23} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp
new file mode 100644
index 000000000..8d6cb7db1
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp
@@ -0,0 +1,42 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h"
5
6namespace Service::PSC::Time {
7
8void StandardNetworkSystemClockCore::Initialize(SystemClockContext& context, s64 accuracy) {
9 if (SetContextAndWrite(context) != ResultSuccess) {
10 LOG_ERROR(Service_Time, "Failed to SetContext");
11 }
12 m_sufficient_accuracy = accuracy;
13 SetInitialized();
14}
15
16bool StandardNetworkSystemClockCore::IsAccuracySufficient() {
17 if (!IsInitialized()) {
18 return false;
19 }
20
21 SystemClockContext context{};
22 SteadyClockTimePoint current_time_point{};
23 if (GetCurrentTimePoint(current_time_point) != ResultSuccess ||
24 GetContext(context) != ResultSuccess) {
25 return false;
26 }
27
28 s64 seconds{};
29 if (GetSpanBetweenTimePoints(&seconds, context.steady_time_point, current_time_point) !=
30 ResultSuccess) {
31 return false;
32 }
33
34 if (std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(seconds))
35 .count() < m_sufficient_accuracy) {
36 return true;
37 }
38
39 return false;
40}
41
42} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h
new file mode 100644
index 000000000..933d2c8e3
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h
@@ -0,0 +1,30 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <chrono>
7
8#include "core/hle/result.h"
9#include "core/hle/service/psc/time/clocks/context_writers.h"
10#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
11#include "core/hle/service/psc/time/clocks/system_clock_core.h"
12#include "core/hle/service/psc/time/common.h"
13
14namespace Service::PSC::Time {
15
16class StandardNetworkSystemClockCore : public SystemClockCore {
17public:
18 explicit StandardNetworkSystemClockCore(SteadyClockCore& steady_clock)
19 : SystemClockCore{steady_clock} {}
20 ~StandardNetworkSystemClockCore() override = default;
21
22 void Initialize(SystemClockContext& context, s64 accuracy);
23 bool IsAccuracySufficient();
24
25private:
26 s64 m_sufficient_accuracy{
27 std::chrono ::duration_cast<std::chrono::nanoseconds>(std::chrono::days(10)).count()};
28};
29
30} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.cpp
new file mode 100644
index 000000000..7a72d7aa2
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.cpp
@@ -0,0 +1,101 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <chrono>
5
6#include "core/core.h"
7#include "core/core_timing.h"
8#include "core/hle/service/psc/time/clocks/standard_steady_clock_core.h"
9
10namespace Service::PSC::Time {
11
12void StandardSteadyClockCore::Initialize(ClockSourceId clock_source_id, s64 rtc_offset,
13 s64 internal_offset, s64 test_offset,
14 bool is_rtc_reset_detected) {
15 m_clock_source_id = clock_source_id;
16 m_rtc_offset = rtc_offset;
17 m_internal_offset = internal_offset;
18 m_test_offset = test_offset;
19 if (is_rtc_reset_detected) {
20 SetResetDetected();
21 }
22 SetInitialized();
23}
24
25void StandardSteadyClockCore::SetRtcOffset(s64 offset) {
26 m_rtc_offset = offset;
27}
28
29void StandardSteadyClockCore::SetContinuousAdjustment(ClockSourceId clock_source_id, s64 time) {
30 auto ticks{m_system.CoreTiming().GetClockTicks()};
31
32 m_continuous_adjustment_time_point.rtc_offset = ConvertToTimeSpan(ticks).count();
33 m_continuous_adjustment_time_point.diff_scale = 0;
34 m_continuous_adjustment_time_point.shift_amount = 0;
35 m_continuous_adjustment_time_point.lower = time;
36 m_continuous_adjustment_time_point.upper = time;
37 m_continuous_adjustment_time_point.clock_source_id = clock_source_id;
38}
39
40void StandardSteadyClockCore::GetContinuousAdjustment(
41 ContinuousAdjustmentTimePoint& out_time_point) const {
42 out_time_point = m_continuous_adjustment_time_point;
43}
44
45void StandardSteadyClockCore::UpdateContinuousAdjustmentTime(s64 in_time) {
46 auto ticks{m_system.CoreTiming().GetClockTicks()};
47 auto uptime_ns{ConvertToTimeSpan(ticks).count()};
48 auto adjusted_time{((uptime_ns - m_continuous_adjustment_time_point.rtc_offset) *
49 m_continuous_adjustment_time_point.diff_scale) >>
50 m_continuous_adjustment_time_point.shift_amount};
51 auto expected_time{adjusted_time + m_continuous_adjustment_time_point.lower};
52
53 auto last_time_point{m_continuous_adjustment_time_point.upper};
54 m_continuous_adjustment_time_point.upper = in_time;
55 auto t1{std::min<s64>(expected_time, last_time_point)};
56 expected_time = std::max<s64>(expected_time, last_time_point);
57 expected_time = m_continuous_adjustment_time_point.diff_scale >= 0 ? t1 : expected_time;
58
59 auto new_diff{in_time < expected_time ? -55 : 55};
60
61 m_continuous_adjustment_time_point.rtc_offset = uptime_ns;
62 m_continuous_adjustment_time_point.shift_amount = expected_time == in_time ? 0 : 14;
63 m_continuous_adjustment_time_point.diff_scale = expected_time == in_time ? 0 : new_diff;
64 m_continuous_adjustment_time_point.lower = expected_time;
65}
66
67Result StandardSteadyClockCore::GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) {
68 auto current_time_ns = GetCurrentRawTimePointImpl();
69 auto current_time_s =
70 std::chrono::duration_cast<std::chrono::seconds>(std::chrono::nanoseconds(current_time_ns));
71 out_time_point.time_point = current_time_s.count();
72 out_time_point.clock_source_id = m_clock_source_id;
73 R_SUCCEED();
74}
75
76s64 StandardSteadyClockCore::GetCurrentRawTimePointImpl() {
77 std::scoped_lock l{m_mutex};
78 auto ticks{static_cast<s64>(m_system.CoreTiming().GetClockTicks())};
79 auto current_time_ns = m_rtc_offset + ConvertToTimeSpan(ticks).count();
80 auto time_point = std::max<s64>(current_time_ns, m_cached_time_point);
81 m_cached_time_point = time_point;
82 return time_point;
83}
84
85s64 StandardSteadyClockCore::GetTestOffsetImpl() const {
86 return m_test_offset;
87}
88
89void StandardSteadyClockCore::SetTestOffsetImpl(s64 offset) {
90 m_test_offset = offset;
91}
92
93s64 StandardSteadyClockCore::GetInternalOffsetImpl() const {
94 return m_internal_offset;
95}
96
97void StandardSteadyClockCore::SetInternalOffsetImpl(s64 offset) {
98 m_internal_offset = offset;
99}
100
101} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.h
new file mode 100644
index 000000000..bbf98fcd5
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.h
@@ -0,0 +1,54 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <mutex>
7
8#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
9
10namespace Core {
11class System;
12}
13
14namespace Service::PSC::Time {
15class StandardSteadyClockCore : public SteadyClockCore {
16public:
17 explicit StandardSteadyClockCore(Core::System& system) : m_system{system} {}
18 ~StandardSteadyClockCore() override = default;
19
20 void Initialize(ClockSourceId clock_source_id, s64 rtc_offset, s64 internal_offset,
21 s64 test_offset, bool is_rtc_reset_detected);
22
23 void SetRtcOffset(s64 offset);
24 void SetContinuousAdjustment(ClockSourceId clock_source_id, s64 time);
25 void GetContinuousAdjustment(ContinuousAdjustmentTimePoint& out_time_point) const;
26 void UpdateContinuousAdjustmentTime(s64 time);
27
28 Result GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) override;
29 s64 GetCurrentRawTimePointImpl() override;
30 s64 GetTestOffsetImpl() const override;
31 void SetTestOffsetImpl(s64 offset) override;
32 s64 GetInternalOffsetImpl() const override;
33 void SetInternalOffsetImpl(s64 offset) override;
34
35 Result GetRtcValueImpl(s64& out_value) override {
36 R_RETURN(ResultNotImplemented);
37 }
38
39 Result GetSetupResultValueImpl() override {
40 R_SUCCEED();
41 }
42
43private:
44 Core::System& m_system;
45
46 std::mutex m_mutex;
47 s64 m_test_offset{};
48 s64 m_internal_offset{};
49 ClockSourceId m_clock_source_id{};
50 s64 m_rtc_offset{};
51 s64 m_cached_time_point{};
52 ContinuousAdjustmentTimePoint m_continuous_adjustment_time_point{};
53};
54} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp
new file mode 100644
index 000000000..9e9be05d6
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp
@@ -0,0 +1,63 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/service/psc/time/clocks/standard_user_system_clock_core.h"
6
7namespace Service::PSC::Time {
8
9StandardUserSystemClockCore::StandardUserSystemClockCore(
10 Core::System& system, StandardLocalSystemClockCore& local_clock,
11 StandardNetworkSystemClockCore& network_clock)
12 : SystemClockCore{local_clock.GetSteadyClock()}, m_system{system},
13 m_ctx{m_system, "Psc:StandardUserSystemClockCore"}, m_local_system_clock{local_clock},
14 m_network_system_clock{network_clock}, m_event{m_ctx.CreateEvent(
15 "Psc:StandardUserSystemClockCore:Event")} {}
16
17StandardUserSystemClockCore::~StandardUserSystemClockCore() {
18 m_ctx.CloseEvent(m_event);
19}
20
21Result StandardUserSystemClockCore::SetAutomaticCorrection(bool automatic_correction) {
22 R_SUCCEED_IF(m_automatic_correction == automatic_correction);
23 R_SUCCEED_IF(!m_network_system_clock.CheckClockSourceMatches());
24
25 SystemClockContext context{};
26 R_TRY(m_network_system_clock.GetContext(context));
27 R_TRY(m_local_system_clock.SetContextAndWrite(context));
28
29 m_automatic_correction = automatic_correction;
30 R_SUCCEED();
31}
32
33Result StandardUserSystemClockCore::GetContext(SystemClockContext& out_context) const {
34 if (!m_automatic_correction) {
35 R_RETURN(m_local_system_clock.GetContext(out_context));
36 }
37
38 if (!m_network_system_clock.CheckClockSourceMatches()) {
39 R_RETURN(m_local_system_clock.GetContext(out_context));
40 }
41
42 SystemClockContext context{};
43 R_TRY(m_network_system_clock.GetContext(context));
44 R_TRY(m_local_system_clock.SetContextAndWrite(context));
45
46 R_RETURN(m_local_system_clock.GetContext(out_context));
47}
48
49Result StandardUserSystemClockCore::SetContext(SystemClockContext& context) {
50 R_RETURN(ResultNotImplemented);
51}
52
53Result StandardUserSystemClockCore::GetTimePoint(SteadyClockTimePoint& out_time_point) {
54 out_time_point = m_time_point;
55 R_SUCCEED();
56}
57
58void StandardUserSystemClockCore::SetTimePointAndSignal(SteadyClockTimePoint& time_point) {
59 m_time_point = time_point;
60 m_event->Signal();
61}
62
63} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h
new file mode 100644
index 000000000..a7fe7648d
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h
@@ -0,0 +1,55 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/kernel/k_event.h"
7#include "core/hle/result.h"
8#include "core/hle/service/kernel_helpers.h"
9#include "core/hle/service/psc/time/clocks/context_writers.h"
10#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h"
11#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h"
12#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
13#include "core/hle/service/psc/time/clocks/system_clock_core.h"
14#include "core/hle/service/psc/time/common.h"
15
16namespace Core {
17class System;
18}
19
20namespace Service::PSC::Time {
21
22class StandardUserSystemClockCore : public SystemClockCore {
23public:
24 explicit StandardUserSystemClockCore(Core::System& system,
25 StandardLocalSystemClockCore& local_clock,
26 StandardNetworkSystemClockCore& network_clock);
27 ~StandardUserSystemClockCore() override;
28
29 Kernel::KEvent& GetEvent() {
30 return *m_event;
31 }
32
33 bool GetAutomaticCorrection() const {
34 return m_automatic_correction;
35 }
36 Result SetAutomaticCorrection(bool automatic_correction);
37
38 Result GetContext(SystemClockContext& out_context) const override;
39 Result SetContext(SystemClockContext& context) override;
40
41 Result GetTimePoint(SteadyClockTimePoint& out_time_point);
42 void SetTimePointAndSignal(SteadyClockTimePoint& time_point);
43
44private:
45 Core::System& m_system;
46 KernelHelpers::ServiceContext m_ctx;
47
48 bool m_automatic_correction{};
49 StandardLocalSystemClockCore& m_local_system_clock;
50 StandardNetworkSystemClockCore& m_network_system_clock;
51 SteadyClockTimePoint m_time_point{};
52 Kernel::KEvent* m_event{};
53};
54
55} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/steady_clock_core.h b/src/core/hle/service/psc/time/clocks/steady_clock_core.h
new file mode 100644
index 000000000..f933cc15f
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/steady_clock_core.h
@@ -0,0 +1,81 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <chrono>
7
8#include "core/hle/result.h"
9#include "core/hle/service/psc/time/common.h"
10
11namespace Service::PSC::Time {
12
13class SteadyClockCore {
14public:
15 SteadyClockCore() = default;
16 virtual ~SteadyClockCore() = default;
17
18 void SetInitialized() {
19 m_initialized = true;
20 }
21
22 bool IsInitialized() const {
23 return m_initialized;
24 }
25
26 void SetResetDetected() {
27 m_reset_detected = true;
28 }
29
30 bool IsResetDetected() const {
31 return m_reset_detected;
32 }
33
34 Result GetCurrentTimePoint(SteadyClockTimePoint& out_time_point) {
35 R_TRY(GetCurrentTimePointImpl(out_time_point));
36
37 auto one_second_ns{
38 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
39 out_time_point.time_point += GetTestOffsetImpl() / one_second_ns;
40 out_time_point.time_point += GetInternalOffsetImpl() / one_second_ns;
41 R_SUCCEED();
42 }
43
44 s64 GetTestOffset() const {
45 return GetTestOffsetImpl();
46 }
47
48 void SetTestOffset(s64 offset) {
49 SetTestOffsetImpl(offset);
50 }
51
52 s64 GetInternalOffset() const {
53 return GetInternalOffsetImpl();
54 }
55
56 s64 GetRawTime() {
57 return GetCurrentRawTimePointImpl() + GetTestOffsetImpl() + GetInternalOffsetImpl();
58 }
59
60 Result GetRtcValue(s64& out_value) {
61 R_RETURN(GetRtcValueImpl(out_value));
62 }
63
64 Result GetSetupResultValue() {
65 R_RETURN(GetSetupResultValueImpl());
66 }
67
68private:
69 virtual Result GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) = 0;
70 virtual s64 GetCurrentRawTimePointImpl() = 0;
71 virtual s64 GetTestOffsetImpl() const = 0;
72 virtual void SetTestOffsetImpl(s64 offset) = 0;
73 virtual s64 GetInternalOffsetImpl() const = 0;
74 virtual void SetInternalOffsetImpl(s64 offset) = 0;
75 virtual Result GetRtcValueImpl(s64& out_value) = 0;
76 virtual Result GetSetupResultValueImpl() = 0;
77
78 bool m_initialized{};
79 bool m_reset_detected{};
80};
81} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/system_clock_core.cpp
new file mode 100644
index 000000000..c507ef517
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/system_clock_core.cpp
@@ -0,0 +1,75 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/psc/time/clocks/context_writers.h"
5#include "core/hle/service/psc/time/clocks/system_clock_core.h"
6
7namespace Service::PSC::Time {
8
9bool SystemClockCore::CheckClockSourceMatches() {
10 SystemClockContext context{};
11 if (GetContext(context) != ResultSuccess) {
12 return false;
13 }
14
15 SteadyClockTimePoint time_point{};
16 if (m_steady_clock.GetCurrentTimePoint(time_point) != ResultSuccess) {
17 return false;
18 }
19
20 return context.steady_time_point.IdMatches(time_point);
21}
22
23Result SystemClockCore::GetCurrentTime(s64* out_time) const {
24 R_UNLESS(out_time != nullptr, ResultInvalidArgument);
25
26 SystemClockContext context{};
27 SteadyClockTimePoint time_point{};
28
29 R_TRY(m_steady_clock.GetCurrentTimePoint(time_point));
30 R_TRY(GetContext(context));
31
32 R_UNLESS(context.steady_time_point.IdMatches(time_point), ResultClockMismatch);
33
34 *out_time = context.offset + time_point.time_point;
35 R_SUCCEED();
36}
37
38Result SystemClockCore::SetCurrentTime(s64 time) {
39 SteadyClockTimePoint time_point{};
40 R_TRY(m_steady_clock.GetCurrentTimePoint(time_point));
41
42 SystemClockContext context{
43 .offset = time - time_point.time_point,
44 .steady_time_point = time_point,
45 };
46 R_RETURN(SetContextAndWrite(context));
47}
48
49Result SystemClockCore::GetContext(SystemClockContext& out_context) const {
50 out_context = m_context;
51 R_SUCCEED();
52}
53
54Result SystemClockCore::SetContext(SystemClockContext& context) {
55 m_context = context;
56 R_SUCCEED();
57}
58
59Result SystemClockCore::SetContextAndWrite(SystemClockContext& context) {
60 R_TRY(SetContext(context));
61
62 if (m_context_writer) {
63 R_RETURN(m_context_writer->Write(context));
64 }
65
66 R_SUCCEED();
67}
68
69void SystemClockCore::LinkOperationEvent(OperationEvent& operation_event) {
70 if (m_context_writer) {
71 m_context_writer->Link(operation_event);
72 }
73}
74
75} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/system_clock_core.h b/src/core/hle/service/psc/time/clocks/system_clock_core.h
new file mode 100644
index 000000000..73811712e
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/system_clock_core.h
@@ -0,0 +1,55 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/result.h"
7#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
8#include "core/hle/service/psc/time/common.h"
9
10namespace Service::PSC::Time {
11class ContextWriter;
12
13class SystemClockCore {
14public:
15 explicit SystemClockCore(SteadyClockCore& steady_clock) : m_steady_clock{steady_clock} {}
16 virtual ~SystemClockCore() = default;
17
18 SteadyClockCore& GetSteadyClock() {
19 return m_steady_clock;
20 }
21
22 bool IsInitialized() const {
23 return m_initialized;
24 }
25
26 void SetInitialized() {
27 m_initialized = true;
28 }
29
30 void SetContextWriter(ContextWriter& context_writer) {
31 m_context_writer = &context_writer;
32 }
33
34 bool CheckClockSourceMatches();
35
36 Result GetCurrentTime(s64* out_time) const;
37 Result SetCurrentTime(s64 time);
38
39 Result GetCurrentTimePoint(SteadyClockTimePoint& out_time_point) {
40 R_RETURN(m_steady_clock.GetCurrentTimePoint(out_time_point));
41 }
42
43 virtual Result GetContext(SystemClockContext& out_context) const;
44 virtual Result SetContext(SystemClockContext& context);
45 Result SetContextAndWrite(SystemClockContext& context);
46
47 void LinkOperationEvent(OperationEvent& operation_event);
48
49private:
50 bool m_initialized{};
51 ContextWriter* m_context_writer{};
52 SteadyClockCore& m_steady_clock;
53 SystemClockContext m_context{};
54};
55} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp b/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp
new file mode 100644
index 000000000..22da1fbcc
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp
@@ -0,0 +1,43 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <chrono>
5
6#include "core/core.h"
7#include "core/core_timing.h"
8#include "core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h"
9
10namespace Service::PSC::Time {
11
12Result TickBasedSteadyClockCore::GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) {
13 auto ticks{m_system.CoreTiming().GetClockTicks()};
14 auto current_time_s =
15 std::chrono::duration_cast<std::chrono::seconds>(ConvertToTimeSpan(ticks)).count();
16 out_time_point.time_point = current_time_s;
17 out_time_point.clock_source_id = m_clock_source_id;
18 R_SUCCEED();
19}
20
21s64 TickBasedSteadyClockCore::GetCurrentRawTimePointImpl() {
22 SteadyClockTimePoint time_point{};
23 if (GetCurrentTimePointImpl(time_point) != ResultSuccess) {
24 LOG_ERROR(Service_Time, "Failed to GetCurrentTimePoint!");
25 }
26 return std::chrono::duration_cast<std::chrono::nanoseconds>(
27 std::chrono::seconds(time_point.time_point))
28 .count();
29}
30
31s64 TickBasedSteadyClockCore::GetTestOffsetImpl() const {
32 return 0;
33}
34
35void TickBasedSteadyClockCore::SetTestOffsetImpl(s64 offset) {}
36
37s64 TickBasedSteadyClockCore::GetInternalOffsetImpl() const {
38 return 0;
39}
40
41void TickBasedSteadyClockCore::SetInternalOffsetImpl(s64 offset) {}
42
43} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h b/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h
new file mode 100644
index 000000000..a7bea86a2
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h
@@ -0,0 +1,41 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <mutex>
7
8#include "common/uuid.h"
9#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
10
11namespace Core {
12class System;
13}
14
15namespace Service::PSC::Time {
16class TickBasedSteadyClockCore : public SteadyClockCore {
17public:
18 explicit TickBasedSteadyClockCore(Core::System& system) : m_system{system} {}
19 ~TickBasedSteadyClockCore() override = default;
20
21 Result GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) override;
22 s64 GetCurrentRawTimePointImpl() override;
23 s64 GetTestOffsetImpl() const override;
24 void SetTestOffsetImpl(s64 offset) override;
25 s64 GetInternalOffsetImpl() const override;
26 void SetInternalOffsetImpl(s64 offset) override;
27
28 Result GetRtcValueImpl(s64& out_value) override {
29 R_RETURN(ResultNotImplemented);
30 }
31
32 Result GetSetupResultValueImpl() override {
33 R_SUCCEED();
34 }
35
36private:
37 Core::System& m_system;
38
39 ClockSourceId m_clock_source_id{Common::UUID::MakeRandom()};
40};
41} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/common.cpp b/src/core/hle/service/psc/time/common.cpp
new file mode 100644
index 000000000..a6d9f02ca
--- /dev/null
+++ b/src/core/hle/service/psc/time/common.cpp
@@ -0,0 +1,16 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/service/psc/time/common.h"
6
7namespace Service::PSC::Time {
8OperationEvent::OperationEvent(Core::System& system)
9 : m_ctx{system, "Time:OperationEvent"}, m_event{
10 m_ctx.CreateEvent("Time:OperationEvent:Event")} {}
11
12OperationEvent::~OperationEvent() {
13 m_ctx.CloseEvent(m_event);
14}
15
16} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/common.h b/src/core/hle/service/psc/time/common.h
new file mode 100644
index 000000000..d17b31143
--- /dev/null
+++ b/src/core/hle/service/psc/time/common.h
@@ -0,0 +1,168 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <chrono>
8
9#include "common/common_types.h"
10#include "common/intrusive_list.h"
11#include "common/uuid.h"
12#include "common/wall_clock.h"
13#include "core/hle/kernel/k_event.h"
14#include "core/hle/service/kernel_helpers.h"
15#include "core/hle/service/psc/time/errors.h"
16
17namespace Core {
18class System;
19}
20
21namespace Service::PSC::Time {
22using ClockSourceId = Common::UUID;
23
24struct SteadyClockTimePoint {
25 constexpr bool IdMatches(SteadyClockTimePoint& other) {
26 return clock_source_id == other.clock_source_id;
27 }
28 bool operator==(const SteadyClockTimePoint& other) const = default;
29
30 s64 time_point;
31 ClockSourceId clock_source_id;
32};
33static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint has the wrong size!");
34static_assert(std::is_trivial_v<ClockSourceId>);
35
36struct SystemClockContext {
37 bool operator==(const SystemClockContext& other) const = default;
38
39 s64 offset;
40 SteadyClockTimePoint steady_time_point;
41};
42static_assert(sizeof(SystemClockContext) == 0x20, "SystemClockContext has the wrong size!");
43static_assert(std::is_trivial_v<SystemClockContext>);
44
45enum class TimeType : u8 {
46 UserSystemClock,
47 NetworkSystemClock,
48 LocalSystemClock,
49};
50
51struct CalendarTime {
52 s16 year;
53 s8 month;
54 s8 day;
55 s8 hour;
56 s8 minute;
57 s8 second;
58};
59static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime has the wrong size!");
60
61struct CalendarAdditionalInfo {
62 s32 day_of_week;
63 s32 day_of_year;
64 std::array<char, 8> name;
65 s32 is_dst;
66 s32 ut_offset;
67};
68static_assert(sizeof(CalendarAdditionalInfo) == 0x18, "CalendarAdditionalInfo has the wrong size!");
69
70struct LocationName {
71 std::array<char, 36> name;
72};
73static_assert(sizeof(LocationName) == 0x24, "LocationName has the wrong size!");
74
75struct RuleVersion {
76 std::array<char, 16> version;
77};
78static_assert(sizeof(RuleVersion) == 0x10, "RuleVersion has the wrong size!");
79
80struct ClockSnapshot {
81 SystemClockContext user_context;
82 SystemClockContext network_context;
83 s64 user_time;
84 s64 network_time;
85 CalendarTime user_calendar_time;
86 CalendarTime network_calendar_time;
87 CalendarAdditionalInfo user_calendar_additional_time;
88 CalendarAdditionalInfo network_calendar_additional_time;
89 SteadyClockTimePoint steady_clock_time_point;
90 LocationName location_name;
91 bool is_automatic_correction_enabled;
92 TimeType type;
93 u16 unk_CE;
94};
95static_assert(sizeof(ClockSnapshot) == 0xD0, "ClockSnapshot has the wrong size!");
96static_assert(std::is_trivial_v<ClockSnapshot>);
97
98struct ContinuousAdjustmentTimePoint {
99 s64 rtc_offset;
100 s64 diff_scale;
101 s64 shift_amount;
102 s64 lower;
103 s64 upper;
104 ClockSourceId clock_source_id;
105};
106static_assert(sizeof(ContinuousAdjustmentTimePoint) == 0x38,
107 "ContinuousAdjustmentTimePoint has the wrong size!");
108static_assert(std::is_trivial_v<ContinuousAdjustmentTimePoint>);
109
110struct AlarmInfo {
111 s64 alert_time;
112 u32 priority;
113};
114static_assert(sizeof(AlarmInfo) == 0x10, "AlarmInfo has the wrong size!");
115
116struct StaticServiceSetupInfo {
117 bool can_write_local_clock;
118 bool can_write_user_clock;
119 bool can_write_network_clock;
120 bool can_write_timezone_device_location;
121 bool can_write_steady_clock;
122 bool can_write_uninitialized_clock;
123};
124static_assert(sizeof(StaticServiceSetupInfo) == 0x6, "StaticServiceSetupInfo has the wrong size!");
125
126struct OperationEvent : public Common::IntrusiveListBaseNode<OperationEvent> {
127 using OperationEventList = Common::IntrusiveListBaseTraits<OperationEvent>::ListType;
128
129 OperationEvent(Core::System& system);
130 ~OperationEvent();
131
132 KernelHelpers::ServiceContext m_ctx;
133 Kernel::KEvent* m_event{};
134};
135
136constexpr inline std::chrono::nanoseconds ConvertToTimeSpan(s64 ticks) {
137 constexpr auto one_second_ns{
138 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
139
140 constexpr s64 max{Common::WallClock::CNTFRQ *
141 (std::numeric_limits<s64>::max() / one_second_ns)};
142
143 if (ticks > max) {
144 return std::chrono::nanoseconds(std::numeric_limits<s64>::max());
145 } else if (ticks < -max) {
146 return std::chrono::nanoseconds(std::numeric_limits<s64>::min());
147 }
148
149 auto a{ticks / Common::WallClock::CNTFRQ * one_second_ns};
150 auto b{((ticks % Common::WallClock::CNTFRQ) * one_second_ns) / Common::WallClock::CNTFRQ};
151
152 return std::chrono::nanoseconds(a + b);
153}
154
155constexpr inline Result GetSpanBetweenTimePoints(s64* out_seconds, SteadyClockTimePoint& a,
156 SteadyClockTimePoint& b) {
157 R_UNLESS(out_seconds, ResultInvalidArgument);
158 R_UNLESS(a.IdMatches(b), ResultInvalidArgument);
159 R_UNLESS(a.time_point >= 0 || b.time_point <= a.time_point + std::numeric_limits<s64>::max(),
160 ResultOverflow);
161 R_UNLESS(a.time_point < 0 || b.time_point >= a.time_point + std::numeric_limits<s64>::min(),
162 ResultOverflow);
163
164 *out_seconds = b.time_point - a.time_point;
165 R_SUCCEED();
166}
167
168} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/errors.h b/src/core/hle/service/psc/time/errors.h
new file mode 100644
index 000000000..6d833a006
--- /dev/null
+++ b/src/core/hle/service/psc/time/errors.h
@@ -0,0 +1,24 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/result.h"
7
8namespace Service::PSC::Time {
9
10constexpr Result ResultPermissionDenied{ErrorModule::Time, 1};
11constexpr Result ResultClockMismatch{ErrorModule::Time, 102};
12constexpr Result ResultClockUninitialized{ErrorModule::Time, 103};
13constexpr Result ResultTimeNotFound{ErrorModule::Time, 200};
14constexpr Result ResultOverflow{ErrorModule::Time, 201};
15constexpr Result ResultFailed{ErrorModule::Time, 801};
16constexpr Result ResultInvalidArgument{ErrorModule::Time, 901};
17constexpr Result ResultTimeZoneOutOfRange{ErrorModule::Time, 902};
18constexpr Result ResultTimeZoneParseFailed{ErrorModule::Time, 903};
19constexpr Result ResultRtcTimeout{ErrorModule::Time, 988};
20constexpr Result ResultTimeZoneNotFound{ErrorModule::Time, 989};
21constexpr Result ResultNotImplemented{ErrorModule::Time, 990};
22constexpr Result ResultAlarmNotRegistered{ErrorModule::Time, 1502};
23
24} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/manager.h b/src/core/hle/service/psc/time/manager.h
new file mode 100644
index 000000000..62ded1247
--- /dev/null
+++ b/src/core/hle/service/psc/time/manager.h
@@ -0,0 +1,56 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/psc/time/alarms.h"
7#include "core/hle/service/psc/time/clocks/context_writers.h"
8#include "core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h"
9#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h"
10#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h"
11#include "core/hle/service/psc/time/clocks/standard_steady_clock_core.h"
12#include "core/hle/service/psc/time/clocks/standard_user_system_clock_core.h"
13#include "core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h"
14#include "core/hle/service/psc/time/power_state_request_manager.h"
15#include "core/hle/service/psc/time/shared_memory.h"
16#include "core/hle/service/psc/time/time_zone.h"
17
18namespace Core {
19class System;
20}
21
22namespace Service::PSC::Time {
23class TimeManager {
24public:
25 explicit TimeManager(Core::System& system)
26 : m_system{system}, m_standard_steady_clock{system}, m_tick_based_steady_clock{m_system},
27 m_standard_local_system_clock{m_standard_steady_clock},
28 m_standard_network_system_clock{m_standard_steady_clock},
29 m_standard_user_system_clock{m_system, m_standard_local_system_clock,
30 m_standard_network_system_clock},
31 m_ephemeral_network_clock{m_tick_based_steady_clock}, m_shared_memory{m_system},
32 m_power_state_request_manager{m_system}, m_alarms{m_system, m_standard_steady_clock,
33 m_power_state_request_manager},
34 m_local_system_clock_context_writer{m_system, m_shared_memory},
35 m_network_system_clock_context_writer{m_system, m_shared_memory,
36 m_standard_user_system_clock},
37 m_ephemeral_network_clock_context_writer{m_system} {}
38
39 Core::System& m_system;
40
41 StandardSteadyClockCore m_standard_steady_clock;
42 TickBasedSteadyClockCore m_tick_based_steady_clock;
43 StandardLocalSystemClockCore m_standard_local_system_clock;
44 StandardNetworkSystemClockCore m_standard_network_system_clock;
45 StandardUserSystemClockCore m_standard_user_system_clock;
46 EphemeralNetworkSystemClockCore m_ephemeral_network_clock;
47 TimeZone m_time_zone;
48 SharedMemory m_shared_memory;
49 PowerStateRequestManager m_power_state_request_manager;
50 Alarms m_alarms;
51 LocalSystemClockContextWriter m_local_system_clock_context_writer;
52 NetworkSystemClockContextWriter m_network_system_clock_context_writer;
53 EphemeralNetworkSystemClockContextWriter m_ephemeral_network_clock_context_writer;
54};
55
56} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/power_state_request_manager.cpp b/src/core/hle/service/psc/time/power_state_request_manager.cpp
new file mode 100644
index 000000000..17de0bf4d
--- /dev/null
+++ b/src/core/hle/service/psc/time/power_state_request_manager.cpp
@@ -0,0 +1,50 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/service/psc/time/power_state_request_manager.h"
6
7namespace Service::PSC::Time {
8
9PowerStateRequestManager::PowerStateRequestManager(Core::System& system)
10 : m_system{system}, m_ctx{system, "Psc:PowerStateRequestManager"},
11 m_event{m_ctx.CreateEvent("Psc:PowerStateRequestManager:Event")} {}
12
13PowerStateRequestManager::~PowerStateRequestManager() {
14 m_ctx.CloseEvent(m_event);
15}
16
17void PowerStateRequestManager::UpdatePendingPowerStateRequestPriority(u32 priority) {
18 std::scoped_lock l{m_mutex};
19 if (m_has_pending_request) {
20 m_pending_request_priority = std::max(m_pending_request_priority, priority);
21 } else {
22 m_pending_request_priority = priority;
23 m_has_pending_request = true;
24 }
25}
26
27void PowerStateRequestManager::SignalPowerStateRequestAvailability() {
28 std::scoped_lock l{m_mutex};
29 if (m_has_pending_request) {
30 if (!m_has_available_request) {
31 m_has_available_request = true;
32 }
33 m_has_pending_request = false;
34 m_available_request_priority = m_pending_request_priority;
35 m_event->Signal();
36 }
37}
38
39bool PowerStateRequestManager::GetAndClearPowerStateRequest(u32& out_priority) {
40 std::scoped_lock l{m_mutex};
41 auto had_request{m_has_available_request};
42 if (m_has_available_request) {
43 out_priority = m_available_request_priority;
44 m_has_available_request = false;
45 m_event->Clear();
46 }
47 return had_request;
48}
49
50} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/power_state_request_manager.h b/src/core/hle/service/psc/time/power_state_request_manager.h
new file mode 100644
index 000000000..30a0c947d
--- /dev/null
+++ b/src/core/hle/service/psc/time/power_state_request_manager.h
@@ -0,0 +1,42 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <mutex>
7
8#include "core/hle/kernel/k_event.h"
9#include "core/hle/service/kernel_helpers.h"
10
11namespace Core {
12class System;
13}
14
15namespace Service::PSC::Time {
16
17class PowerStateRequestManager {
18public:
19 explicit PowerStateRequestManager(Core::System& system);
20 ~PowerStateRequestManager();
21
22 Kernel::KReadableEvent& GetReadableEvent() {
23 return m_event->GetReadableEvent();
24 }
25
26 void UpdatePendingPowerStateRequestPriority(u32 priority);
27 void SignalPowerStateRequestAvailability();
28 bool GetAndClearPowerStateRequest(u32& out_priority);
29
30private:
31 Core::System& m_system;
32 KernelHelpers::ServiceContext m_ctx;
33
34 Kernel::KEvent* m_event{};
35 bool m_has_pending_request{};
36 u32 m_pending_request_priority{};
37 bool m_has_available_request{};
38 u32 m_available_request_priority{};
39 std::mutex m_mutex;
40};
41
42} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/power_state_service.cpp b/src/core/hle/service/psc/time/power_state_service.cpp
new file mode 100644
index 000000000..b0ae71bf9
--- /dev/null
+++ b/src/core/hle/service/psc/time/power_state_service.cpp
@@ -0,0 +1,49 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/psc/time/power_state_service.h"
5
6namespace Service::PSC::Time {
7
8IPowerStateRequestHandler::IPowerStateRequestHandler(
9 Core::System& system_, PowerStateRequestManager& power_state_request_manager)
10 : ServiceFramework{system_, "time:p"}, m_system{system}, m_power_state_request_manager{
11 power_state_request_manager} {
12 // clang-format off
13 static const FunctionInfo functions[] = {
14 {0, &IPowerStateRequestHandler::GetPowerStateRequestEventReadableHandle, "GetPowerStateRequestEventReadableHandle"},
15 {1, &IPowerStateRequestHandler::GetAndClearPowerStateRequest, "GetAndClearPowerStateRequest"},
16 };
17 // clang-format on
18
19 RegisterHandlers(functions);
20}
21
22void IPowerStateRequestHandler::GetPowerStateRequestEventReadableHandle(HLERequestContext& ctx) {
23 LOG_DEBUG(Service_Time, "called.");
24
25 IPC::ResponseBuilder rb{ctx, 2, 1};
26 rb.Push(ResultSuccess);
27 rb.PushCopyObjects(m_power_state_request_manager.GetReadableEvent());
28}
29
30void IPowerStateRequestHandler::GetAndClearPowerStateRequest(HLERequestContext& ctx) {
31 LOG_DEBUG(Service_Time, "called.");
32
33 u32 priority{};
34 auto cleared = m_power_state_request_manager.GetAndClearPowerStateRequest(priority);
35
36 if (cleared) {
37 IPC::ResponseBuilder rb{ctx, 4};
38 rb.Push(ResultSuccess);
39 rb.Push(priority);
40 rb.Push(cleared);
41 return;
42 }
43
44 IPC::ResponseBuilder rb{ctx, 3};
45 rb.Push(ResultSuccess);
46 rb.Push(cleared);
47}
48
49} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/power_state_service.h b/src/core/hle/service/psc/time/power_state_service.h
new file mode 100644
index 000000000..3ebfddb79
--- /dev/null
+++ b/src/core/hle/service/psc/time/power_state_service.h
@@ -0,0 +1,32 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/ipc_helpers.h"
7#include "core/hle/service/psc/time/power_state_request_manager.h"
8#include "core/hle/service/server_manager.h"
9#include "core/hle/service/service.h"
10
11namespace Core {
12class System;
13}
14
15namespace Service::PSC::Time {
16
17class IPowerStateRequestHandler final : public ServiceFramework<IPowerStateRequestHandler> {
18public:
19 explicit IPowerStateRequestHandler(Core::System& system,
20 PowerStateRequestManager& power_state_request_manager);
21
22 ~IPowerStateRequestHandler() override = default;
23
24private:
25 void GetPowerStateRequestEventReadableHandle(HLERequestContext& ctx);
26 void GetAndClearPowerStateRequest(HLERequestContext& ctx);
27
28 Core::System& m_system;
29 PowerStateRequestManager& m_power_state_request_manager;
30};
31
32} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/service_manager.cpp b/src/core/hle/service/psc/time/service_manager.cpp
new file mode 100644
index 000000000..60820aa9b
--- /dev/null
+++ b/src/core/hle/service/psc/time/service_manager.cpp
@@ -0,0 +1,494 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/core_timing.h"
6#include "core/hle/service/psc/time/power_state_service.h"
7#include "core/hle/service/psc/time/service_manager.h"
8#include "core/hle/service/psc/time/static.h"
9
10namespace Service::PSC::Time {
11
12ServiceManager::ServiceManager(Core::System& system_, std::shared_ptr<TimeManager> time,
13 ServerManager* server_manager)
14 : ServiceFramework{system_, "time:m"}, m_system{system}, m_time{std::move(time)},
15 m_server_manager{*server_manager},
16 m_local_system_clock{m_time->m_standard_local_system_clock},
17 m_user_system_clock{m_time->m_standard_user_system_clock},
18 m_network_system_clock{m_time->m_standard_network_system_clock},
19 m_steady_clock{m_time->m_standard_steady_clock}, m_time_zone{m_time->m_time_zone},
20 m_ephemeral_network_clock{m_time->m_ephemeral_network_clock},
21 m_shared_memory{m_time->m_shared_memory}, m_alarms{m_time->m_alarms},
22 m_local_system_context_writer{m_time->m_local_system_clock_context_writer},
23 m_network_system_context_writer{m_time->m_network_system_clock_context_writer},
24 m_ephemeral_system_context_writer{m_time->m_ephemeral_network_clock_context_writer},
25 m_local_operation{m_system}, m_network_operation{m_system}, m_ephemeral_operation{m_system} {
26 // clang-format off
27 static const FunctionInfo functions[] = {
28 {0, &ServiceManager::Handle_GetStaticServiceAsUser, "GetStaticServiceAsUser"},
29 {5, &ServiceManager::Handle_GetStaticServiceAsAdmin, "GetStaticServiceAsAdmin"},
30 {6, &ServiceManager::Handle_GetStaticServiceAsRepair, "GetStaticServiceAsRepair"},
31 {9, &ServiceManager::Handle_GetStaticServiceAsServiceManager, "GetStaticServiceAsServiceManager"},
32 {10, &ServiceManager::Handle_SetupStandardSteadyClockCore, "SetupStandardSteadyClockCore"},
33 {11, &ServiceManager::Handle_SetupStandardLocalSystemClockCore, "SetupStandardLocalSystemClockCore"},
34 {12, &ServiceManager::Handle_SetupStandardNetworkSystemClockCore, "SetupStandardNetworkSystemClockCore"},
35 {13, &ServiceManager::Handle_SetupStandardUserSystemClockCore, "SetupStandardUserSystemClockCore"},
36 {14, &ServiceManager::Handle_SetupTimeZoneServiceCore, "SetupTimeZoneServiceCore"},
37 {15, &ServiceManager::Handle_SetupEphemeralNetworkSystemClockCore, "SetupEphemeralNetworkSystemClockCore"},
38 {50, &ServiceManager::Handle_GetStandardLocalClockOperationEvent, "GetStandardLocalClockOperationEvent"},
39 {51, &ServiceManager::Handle_GetStandardNetworkClockOperationEventForServiceManager, "GetStandardNetworkClockOperationEventForServiceManager"},
40 {52, &ServiceManager::Handle_GetEphemeralNetworkClockOperationEventForServiceManager, "GetEphemeralNetworkClockOperationEventForServiceManager"},
41 {60, &ServiceManager::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent, "GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent"},
42 {100, &ServiceManager::Handle_SetStandardSteadyClockBaseTime, "SetStandardSteadyClockBaseTime"},
43 {200, &ServiceManager::Handle_GetClosestAlarmUpdatedEvent, "GetClosestAlarmUpdatedEvent"},
44 {201, &ServiceManager::Handle_CheckAndSignalAlarms, "CheckAndSignalAlarms"},
45 {202, &ServiceManager::Handle_GetClosestAlarmInfo, "GetClosestAlarmInfo "},
46 };
47 // clang-format on
48 RegisterHandlers(functions);
49
50 m_local_system_context_writer.Link(m_local_operation);
51 m_network_system_context_writer.Link(m_network_operation);
52 m_ephemeral_system_context_writer.Link(m_ephemeral_operation);
53}
54
55void ServiceManager::SetupSAndP() {
56 if (!m_is_s_and_p_setup) {
57 m_is_s_and_p_setup = true;
58 m_server_manager.RegisterNamedService(
59 "time:s", std::make_shared<StaticService>(
60 m_system, StaticServiceSetupInfo{0, 0, 1, 0, 0, 0}, m_time, "time:s"));
61 m_server_manager.RegisterNamedService("time:p",
62 std::make_shared<IPowerStateRequestHandler>(
63 m_system, m_time->m_power_state_request_manager));
64 }
65}
66
67void ServiceManager::CheckAndSetupServicesSAndP() {
68 if (m_local_system_clock.IsInitialized() && m_user_system_clock.IsInitialized() &&
69 m_network_system_clock.IsInitialized() && m_steady_clock.IsInitialized() &&
70 m_time_zone.IsInitialized() && m_ephemeral_network_clock.IsInitialized()) {
71 SetupSAndP();
72 }
73}
74
75void ServiceManager::Handle_GetStaticServiceAsUser(HLERequestContext& ctx) {
76 LOG_DEBUG(Service_Time, "called.");
77
78 std::shared_ptr<StaticService> service{};
79 auto res = GetStaticServiceAsUser(service);
80
81 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
82 rb.Push(res);
83 rb.PushIpcInterface<StaticService>(std::move(service));
84}
85
86void ServiceManager::Handle_GetStaticServiceAsAdmin(HLERequestContext& ctx) {
87 LOG_DEBUG(Service_Time, "called.");
88
89 std::shared_ptr<StaticService> service{};
90 auto res = GetStaticServiceAsAdmin(service);
91
92 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
93 rb.Push(res);
94 rb.PushIpcInterface<StaticService>(std::move(service));
95}
96
97void ServiceManager::Handle_GetStaticServiceAsRepair(HLERequestContext& ctx) {
98 LOG_DEBUG(Service_Time, "called.");
99
100 std::shared_ptr<StaticService> service{};
101 auto res = GetStaticServiceAsRepair(service);
102
103 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
104 rb.Push(res);
105 rb.PushIpcInterface<StaticService>(std::move(service));
106}
107
108void ServiceManager::Handle_GetStaticServiceAsServiceManager(HLERequestContext& ctx) {
109 LOG_DEBUG(Service_Time, "called.");
110
111 std::shared_ptr<StaticService> service{};
112 auto res = GetStaticServiceAsServiceManager(service);
113
114 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
115 rb.Push(res);
116 rb.PushIpcInterface<StaticService>(std::move(service));
117}
118
119void ServiceManager::Handle_SetupStandardSteadyClockCore(HLERequestContext& ctx) {
120 LOG_DEBUG(Service_Time, "called.");
121
122 struct Parameters {
123 bool reset_detected;
124 Common::UUID clock_source_id;
125 s64 rtc_offset;
126 s64 internal_offset;
127 s64 test_offset;
128 };
129 static_assert(sizeof(Parameters) == 0x30);
130
131 IPC::RequestParser rp{ctx};
132 auto params{rp.PopRaw<Parameters>()};
133
134 auto res = SetupStandardSteadyClockCore(params.clock_source_id, params.rtc_offset,
135 params.internal_offset, params.test_offset,
136 params.reset_detected);
137
138 IPC::ResponseBuilder rb{ctx, 2};
139 rb.Push(res);
140}
141
142void ServiceManager::Handle_SetupStandardLocalSystemClockCore(HLERequestContext& ctx) {
143 LOG_DEBUG(Service_Time, "called.");
144
145 IPC::RequestParser rp{ctx};
146 auto context{rp.PopRaw<SystemClockContext>()};
147 auto time{rp.Pop<s64>()};
148
149 auto res = SetupStandardLocalSystemClockCore(context, time);
150
151 IPC::ResponseBuilder rb{ctx, 2};
152 rb.Push(res);
153}
154
155void ServiceManager::Handle_SetupStandardNetworkSystemClockCore(HLERequestContext& ctx) {
156 LOG_DEBUG(Service_Time, "called.");
157
158 IPC::RequestParser rp{ctx};
159 auto context{rp.PopRaw<SystemClockContext>()};
160 auto accuracy{rp.Pop<s64>()};
161
162 auto res = SetupStandardNetworkSystemClockCore(context, accuracy);
163
164 IPC::ResponseBuilder rb{ctx, 2};
165 rb.Push(res);
166}
167
168void ServiceManager::Handle_SetupStandardUserSystemClockCore(HLERequestContext& ctx) {
169 LOG_DEBUG(Service_Time, "called.");
170
171 struct Parameters {
172 bool automatic_correction;
173 SteadyClockTimePoint time_point;
174 };
175 static_assert(sizeof(Parameters) == 0x20);
176
177 IPC::RequestParser rp{ctx};
178 auto params{rp.PopRaw<Parameters>()};
179
180 auto res = SetupStandardUserSystemClockCore(params.time_point, params.automatic_correction);
181
182 IPC::ResponseBuilder rb{ctx, 2};
183 rb.Push(res);
184}
185
186void ServiceManager::Handle_SetupTimeZoneServiceCore(HLERequestContext& ctx) {
187 LOG_DEBUG(Service_Time, "called.");
188
189 struct Parameters {
190 u32 location_count;
191 LocationName name;
192 SteadyClockTimePoint time_point;
193 RuleVersion rule_version;
194 };
195 static_assert(sizeof(Parameters) == 0x50);
196
197 IPC::RequestParser rp{ctx};
198 auto params{rp.PopRaw<Parameters>()};
199
200 auto rule_buffer{ctx.ReadBuffer()};
201
202 auto res = SetupTimeZoneServiceCore(params.name, params.time_point, params.rule_version,
203 params.location_count, rule_buffer);
204
205 IPC::ResponseBuilder rb{ctx, 2};
206 rb.Push(res);
207}
208
209void ServiceManager::Handle_SetupEphemeralNetworkSystemClockCore(HLERequestContext& ctx) {
210 LOG_DEBUG(Service_Time, "called.");
211
212 auto res = SetupEphemeralNetworkSystemClockCore();
213
214 IPC::ResponseBuilder rb{ctx, 2};
215 rb.Push(res);
216}
217
218void ServiceManager::Handle_GetStandardLocalClockOperationEvent(HLERequestContext& ctx) {
219 LOG_DEBUG(Service_Time, "called.");
220
221 Kernel::KEvent* event{};
222 auto res = GetStandardLocalClockOperationEvent(&event);
223
224 IPC::ResponseBuilder rb{ctx, 2, 1};
225 rb.Push(res);
226 rb.PushCopyObjects(event->GetReadableEvent());
227}
228
229void ServiceManager::Handle_GetStandardNetworkClockOperationEventForServiceManager(
230 HLERequestContext& ctx) {
231 LOG_DEBUG(Service_Time, "called.");
232
233 Kernel::KEvent* event{};
234 auto res = GetStandardNetworkClockOperationEventForServiceManager(&event);
235
236 IPC::ResponseBuilder rb{ctx, 2, 1};
237 rb.Push(res);
238 rb.PushCopyObjects(event);
239}
240
241void ServiceManager::Handle_GetEphemeralNetworkClockOperationEventForServiceManager(
242 HLERequestContext& ctx) {
243 LOG_DEBUG(Service_Time, "called.");
244
245 Kernel::KEvent* event{};
246 auto res = GetEphemeralNetworkClockOperationEventForServiceManager(&event);
247
248 IPC::ResponseBuilder rb{ctx, 2, 1};
249 rb.Push(res);
250 rb.PushCopyObjects(event);
251}
252
253void ServiceManager::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(
254 HLERequestContext& ctx) {
255 LOG_DEBUG(Service_Time, "called.");
256
257 Kernel::KEvent* event{};
258 auto res = GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(&event);
259
260 IPC::ResponseBuilder rb{ctx, 2, 1};
261 rb.Push(res);
262 rb.PushCopyObjects(event);
263}
264
265void ServiceManager::Handle_SetStandardSteadyClockBaseTime(HLERequestContext& ctx) {
266 LOG_DEBUG(Service_Time, "called.");
267
268 IPC::RequestParser rp{ctx};
269 auto base_time{rp.Pop<s64>()};
270
271 auto res = SetStandardSteadyClockBaseTime(base_time);
272
273 IPC::ResponseBuilder rb{ctx, 2};
274 rb.Push(res);
275}
276
277void ServiceManager::Handle_GetClosestAlarmUpdatedEvent(HLERequestContext& ctx) {
278 LOG_DEBUG(Service_Time, "called.");
279
280 Kernel::KEvent* event{};
281 auto res = GetClosestAlarmUpdatedEvent(&event);
282
283 IPC::ResponseBuilder rb{ctx, 3};
284 rb.Push(res);
285 rb.PushCopyObjects(event->GetReadableEvent());
286}
287
288void ServiceManager::Handle_CheckAndSignalAlarms(HLERequestContext& ctx) {
289 LOG_DEBUG(Service_Time, "called.");
290
291 auto res = CheckAndSignalAlarms();
292
293 IPC::ResponseBuilder rb{ctx, 2};
294 rb.Push(res);
295}
296
297void ServiceManager::Handle_GetClosestAlarmInfo(HLERequestContext& ctx) {
298 LOG_DEBUG(Service_Time, "called.");
299
300 AlarmInfo alarm_info{};
301 bool is_valid{};
302 s64 time{};
303 auto res = GetClosestAlarmInfo(is_valid, alarm_info, time);
304
305 struct OutParameters {
306 bool is_valid;
307 AlarmInfo alarm_info;
308 s64 time;
309 };
310 static_assert(sizeof(OutParameters) == 0x20);
311
312 OutParameters out_params{
313 .is_valid = is_valid,
314 .alarm_info = alarm_info,
315 .time = time,
316 };
317
318 IPC::ResponseBuilder rb{ctx, 2 + sizeof(OutParameters) / sizeof(u32)};
319 rb.Push(res);
320 rb.PushRaw<OutParameters>(out_params);
321}
322
323// =============================== Implementations ===========================
324
325Result ServiceManager::GetStaticService(std::shared_ptr<StaticService>& out_service,
326 StaticServiceSetupInfo setup_info, const char* name) {
327 out_service = std::make_shared<StaticService>(m_system, setup_info, m_time, name);
328 R_SUCCEED();
329}
330
331Result ServiceManager::GetStaticServiceAsUser(std::shared_ptr<StaticService>& out_service) {
332 R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{0, 0, 0, 0, 0, 0}, "time:u"));
333}
334
335Result ServiceManager::GetStaticServiceAsAdmin(std::shared_ptr<StaticService>& out_service) {
336 R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{1, 1, 0, 1, 0, 0}, "time:a"));
337}
338
339Result ServiceManager::GetStaticServiceAsRepair(std::shared_ptr<StaticService>& out_service) {
340 R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{0, 0, 0, 0, 1, 0}, "time:r"));
341}
342
343Result ServiceManager::GetStaticServiceAsServiceManager(
344 std::shared_ptr<StaticService>& out_service) {
345 R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{1, 1, 1, 1, 1, 0}, "time:sm"));
346}
347
348Result ServiceManager::SetupStandardSteadyClockCore(Common::UUID& clock_source_id, s64 rtc_offset,
349 s64 internal_offset, s64 test_offset,
350 bool is_rtc_reset_detected) {
351 m_steady_clock.Initialize(clock_source_id, rtc_offset, internal_offset, test_offset,
352 is_rtc_reset_detected);
353 auto time = m_steady_clock.GetRawTime();
354 auto ticks = m_system.CoreTiming().GetClockTicks();
355 auto boot_time = time - ConvertToTimeSpan(ticks).count();
356 m_shared_memory.SetSteadyClockTimePoint(clock_source_id, boot_time);
357 m_steady_clock.SetContinuousAdjustment(clock_source_id, boot_time);
358
359 ContinuousAdjustmentTimePoint time_point{};
360 m_steady_clock.GetContinuousAdjustment(time_point);
361 m_shared_memory.SetContinuousAdjustment(time_point);
362
363 CheckAndSetupServicesSAndP();
364 R_SUCCEED();
365}
366
367Result ServiceManager::SetupStandardLocalSystemClockCore(SystemClockContext& context, s64 time) {
368 m_local_system_clock.SetContextWriter(m_local_system_context_writer);
369 m_local_system_clock.Initialize(context, time);
370
371 CheckAndSetupServicesSAndP();
372 R_SUCCEED();
373}
374
375Result ServiceManager::SetupStandardNetworkSystemClockCore(SystemClockContext& context,
376 s64 accuracy) {
377 // TODO this is a hack! The network clock should be updated independently, from the ntc service
378 // and maybe elsewhere. We do not do that, so fix the clock to the local clock on first boot
379 // to avoid it being stuck at 0.
380 if (context == Service::PSC::Time::SystemClockContext{}) {
381 m_local_system_clock.GetContext(context);
382 }
383
384 m_network_system_clock.SetContextWriter(m_network_system_context_writer);
385 m_network_system_clock.Initialize(context, accuracy);
386
387 CheckAndSetupServicesSAndP();
388 R_SUCCEED();
389}
390
391Result ServiceManager::SetupStandardUserSystemClockCore(SteadyClockTimePoint& time_point,
392 bool automatic_correction) {
393 // TODO this is a hack! The user clock should be updated independently, from the ntc service
394 // and maybe elsewhere. We do not do that, so fix the clock to the local clock on first boot
395 // to avoid it being stuck at 0.
396 if (time_point == Service::PSC::Time::SteadyClockTimePoint{}) {
397 m_local_system_clock.GetCurrentTimePoint(time_point);
398 }
399
400 m_user_system_clock.SetAutomaticCorrection(automatic_correction);
401 m_user_system_clock.SetTimePointAndSignal(time_point);
402 m_user_system_clock.SetInitialized();
403 m_shared_memory.SetAutomaticCorrection(automatic_correction);
404
405 CheckAndSetupServicesSAndP();
406 R_SUCCEED();
407}
408
409Result ServiceManager::SetupTimeZoneServiceCore(LocationName& name,
410 SteadyClockTimePoint& time_point,
411 RuleVersion& rule_version, u32 location_count,
412 std::span<const u8> rule_buffer) {
413 if (m_time_zone.ParseBinary(name, rule_buffer) != ResultSuccess) {
414 LOG_ERROR(Service_Time, "Failed to parse time zone binary!");
415 }
416
417 m_time_zone.SetTimePoint(time_point);
418 m_time_zone.SetTotalLocationNameCount(location_count);
419 m_time_zone.SetRuleVersion(rule_version);
420 m_time_zone.SetInitialized();
421
422 CheckAndSetupServicesSAndP();
423 R_SUCCEED();
424}
425
426Result ServiceManager::SetupEphemeralNetworkSystemClockCore() {
427 m_ephemeral_network_clock.SetContextWriter(m_ephemeral_system_context_writer);
428 m_ephemeral_network_clock.SetInitialized();
429
430 CheckAndSetupServicesSAndP();
431 R_SUCCEED();
432}
433
434Result ServiceManager::GetStandardLocalClockOperationEvent(Kernel::KEvent** out_event) {
435 *out_event = m_local_operation.m_event;
436 R_SUCCEED();
437}
438
439Result ServiceManager::GetStandardNetworkClockOperationEventForServiceManager(
440 Kernel::KEvent** out_event) {
441 *out_event = m_network_operation.m_event;
442 R_SUCCEED();
443}
444
445Result ServiceManager::GetEphemeralNetworkClockOperationEventForServiceManager(
446 Kernel::KEvent** out_event) {
447 *out_event = m_ephemeral_operation.m_event;
448 R_SUCCEED();
449}
450
451Result ServiceManager::GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(
452 Kernel::KEvent** out_event) {
453 *out_event = &m_user_system_clock.GetEvent();
454 R_SUCCEED();
455}
456
457Result ServiceManager::SetStandardSteadyClockBaseTime(s64 base_time) {
458 m_steady_clock.SetRtcOffset(base_time);
459 auto time = m_steady_clock.GetRawTime();
460 auto ticks = m_system.CoreTiming().GetClockTicks();
461 auto diff = time - ConvertToTimeSpan(ticks).count();
462 m_shared_memory.UpdateBaseTime(diff);
463 m_steady_clock.UpdateContinuousAdjustmentTime(diff);
464
465 ContinuousAdjustmentTimePoint time_point{};
466 m_steady_clock.GetContinuousAdjustment(time_point);
467 m_shared_memory.SetContinuousAdjustment(time_point);
468 R_SUCCEED();
469}
470
471Result ServiceManager::GetClosestAlarmUpdatedEvent(Kernel::KEvent** out_event) {
472 *out_event = &m_alarms.GetEvent();
473 R_SUCCEED();
474}
475
476Result ServiceManager::CheckAndSignalAlarms() {
477 m_alarms.CheckAndSignal();
478 R_SUCCEED();
479}
480
481Result ServiceManager::GetClosestAlarmInfo(bool& out_is_valid, AlarmInfo& out_info, s64& out_time) {
482 Alarm* alarm{nullptr};
483 out_is_valid = m_alarms.GetClosestAlarm(&alarm);
484 if (out_is_valid) {
485 out_info = {
486 .alert_time = alarm->GetAlertTime(),
487 .priority = alarm->GetPriority(),
488 };
489 out_time = m_alarms.GetRawTime();
490 }
491 R_SUCCEED();
492}
493
494} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/service_manager.h b/src/core/hle/service/psc/time/service_manager.h
new file mode 100644
index 000000000..1d9952317
--- /dev/null
+++ b/src/core/hle/service/psc/time/service_manager.h
@@ -0,0 +1,101 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <list>
7#include <memory>
8
9#include "core/hle/service/ipc_helpers.h"
10#include "core/hle/service/psc/time/common.h"
11#include "core/hle/service/psc/time/manager.h"
12#include "core/hle/service/server_manager.h"
13#include "core/hle/service/service.h"
14
15namespace Core {
16class System;
17}
18
19namespace Kernel {
20class KReadableEvent;
21}
22
23namespace Service::PSC::Time {
24class StaticService;
25
26class ServiceManager final : public ServiceFramework<ServiceManager> {
27public:
28 explicit ServiceManager(Core::System& system, std::shared_ptr<TimeManager> time,
29 ServerManager* server_manager);
30 ~ServiceManager() override = default;
31
32 Result GetStaticServiceAsUser(std::shared_ptr<StaticService>& out_service);
33 Result GetStaticServiceAsAdmin(std::shared_ptr<StaticService>& out_service);
34 Result GetStaticServiceAsRepair(std::shared_ptr<StaticService>& out_service);
35 Result GetStaticServiceAsServiceManager(std::shared_ptr<StaticService>& out_service);
36 Result SetupStandardSteadyClockCore(Common::UUID& clock_source_id, s64 rtc_offset,
37 s64 internal_offset, s64 test_offset,
38 bool is_rtc_reset_detected);
39 Result SetupStandardLocalSystemClockCore(SystemClockContext& context, s64 time);
40 Result SetupStandardNetworkSystemClockCore(SystemClockContext& context, s64 accuracy);
41 Result SetupStandardUserSystemClockCore(SteadyClockTimePoint& time_point,
42 bool automatic_correction);
43 Result SetupTimeZoneServiceCore(LocationName& name, SteadyClockTimePoint& time_point,
44 RuleVersion& rule_version, u32 location_count,
45 std::span<const u8> rule_buffer);
46 Result SetupEphemeralNetworkSystemClockCore();
47 Result GetStandardLocalClockOperationEvent(Kernel::KEvent** out_event);
48 Result GetStandardNetworkClockOperationEventForServiceManager(Kernel::KEvent** out_event);
49 Result GetEphemeralNetworkClockOperationEventForServiceManager(Kernel::KEvent** out_event);
50 Result GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(Kernel::KEvent** out_event);
51 Result SetStandardSteadyClockBaseTime(s64 base_time);
52 Result GetClosestAlarmUpdatedEvent(Kernel::KEvent** out_event);
53 Result CheckAndSignalAlarms();
54 Result GetClosestAlarmInfo(bool& out_is_valid, AlarmInfo& out_info, s64& out_time);
55
56private:
57 void CheckAndSetupServicesSAndP();
58 void SetupSAndP();
59 Result GetStaticService(std::shared_ptr<StaticService>& out_service,
60 StaticServiceSetupInfo setup_info, const char* name);
61
62 void Handle_GetStaticServiceAsUser(HLERequestContext& ctx);
63 void Handle_GetStaticServiceAsAdmin(HLERequestContext& ctx);
64 void Handle_GetStaticServiceAsRepair(HLERequestContext& ctx);
65 void Handle_GetStaticServiceAsServiceManager(HLERequestContext& ctx);
66 void Handle_SetupStandardSteadyClockCore(HLERequestContext& ctx);
67 void Handle_SetupStandardLocalSystemClockCore(HLERequestContext& ctx);
68 void Handle_SetupStandardNetworkSystemClockCore(HLERequestContext& ctx);
69 void Handle_SetupStandardUserSystemClockCore(HLERequestContext& ctx);
70 void Handle_SetupTimeZoneServiceCore(HLERequestContext& ctx);
71 void Handle_SetupEphemeralNetworkSystemClockCore(HLERequestContext& ctx);
72 void Handle_GetStandardLocalClockOperationEvent(HLERequestContext& ctx);
73 void Handle_GetStandardNetworkClockOperationEventForServiceManager(HLERequestContext& ctx);
74 void Handle_GetEphemeralNetworkClockOperationEventForServiceManager(HLERequestContext& ctx);
75 void Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(HLERequestContext& ctx);
76 void Handle_SetStandardSteadyClockBaseTime(HLERequestContext& ctx);
77 void Handle_GetClosestAlarmUpdatedEvent(HLERequestContext& ctx);
78 void Handle_CheckAndSignalAlarms(HLERequestContext& ctx);
79 void Handle_GetClosestAlarmInfo(HLERequestContext& ctx);
80
81 Core::System& m_system;
82 std::shared_ptr<TimeManager> m_time;
83 ServerManager& m_server_manager;
84 bool m_is_s_and_p_setup{};
85 StandardLocalSystemClockCore& m_local_system_clock;
86 StandardUserSystemClockCore& m_user_system_clock;
87 StandardNetworkSystemClockCore& m_network_system_clock;
88 StandardSteadyClockCore& m_steady_clock;
89 TimeZone& m_time_zone;
90 EphemeralNetworkSystemClockCore& m_ephemeral_network_clock;
91 SharedMemory& m_shared_memory;
92 Alarms& m_alarms;
93 LocalSystemClockContextWriter& m_local_system_context_writer;
94 NetworkSystemClockContextWriter& m_network_system_context_writer;
95 EphemeralNetworkSystemClockContextWriter& m_ephemeral_system_context_writer;
96 OperationEvent m_local_operation;
97 OperationEvent m_network_operation;
98 OperationEvent m_ephemeral_operation;
99};
100
101} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/shared_memory.cpp b/src/core/hle/service/psc/time/shared_memory.cpp
new file mode 100644
index 000000000..defaceebe
--- /dev/null
+++ b/src/core/hle/service/psc/time/shared_memory.cpp
@@ -0,0 +1,84 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_shared_memory.h"
6#include "core/hle/service/psc/time/shared_memory.h"
7
8namespace Service::PSC::Time {
9namespace {
10template <typename T>
11constexpr inline T ReadFromLockFreeAtomicType(const LockFreeAtomicType<T>* p) {
12 while (true) {
13 // Get the counter.
14 auto counter = p->m_counter;
15
16 // Get the value.
17 auto value = p->m_value[counter % 2];
18
19 // Fence memory.
20 std::atomic_thread_fence(std::memory_order_acquire);
21
22 // Check that the counter matches.
23 if (counter == p->m_counter) {
24 return value;
25 }
26 }
27}
28
29template <typename T>
30constexpr inline void WriteToLockFreeAtomicType(LockFreeAtomicType<T>* p, const T& value) {
31 // Get the current counter.
32 auto counter = p->m_counter;
33
34 // Increment the counter.
35 ++counter;
36
37 // Store the updated value.
38 p->m_value[counter % 2] = value;
39
40 // Fence memory.
41 std::atomic_thread_fence(std::memory_order_release);
42
43 // Set the updated counter.
44 p->m_counter = counter;
45}
46} // namespace
47
48SharedMemory::SharedMemory(Core::System& system)
49 : m_system{system}, m_k_shared_memory{m_system.Kernel().GetTimeSharedMem()},
50 m_shared_memory_ptr{reinterpret_cast<SharedMemoryStruct*>(m_k_shared_memory.GetPointer())} {
51 std::memset(m_shared_memory_ptr, 0, sizeof(*m_shared_memory_ptr));
52}
53
54void SharedMemory::SetLocalSystemContext(SystemClockContext& context) {
55 WriteToLockFreeAtomicType(&m_shared_memory_ptr->local_system_clock_contexts, context);
56}
57
58void SharedMemory::SetNetworkSystemContext(SystemClockContext& context) {
59 WriteToLockFreeAtomicType(&m_shared_memory_ptr->network_system_clock_contexts, context);
60}
61
62void SharedMemory::SetSteadyClockTimePoint(ClockSourceId clock_source_id, s64 time_point) {
63 WriteToLockFreeAtomicType(&m_shared_memory_ptr->steady_time_points,
64 {time_point, clock_source_id});
65}
66
67void SharedMemory::SetContinuousAdjustment(ContinuousAdjustmentTimePoint& time_point) {
68 WriteToLockFreeAtomicType(&m_shared_memory_ptr->continuous_adjustment_time_points, time_point);
69}
70
71void SharedMemory::SetAutomaticCorrection(bool automatic_correction) {
72 WriteToLockFreeAtomicType(&m_shared_memory_ptr->automatic_corrections, automatic_correction);
73}
74
75void SharedMemory::UpdateBaseTime(s64 time) {
76 SteadyClockTimePoint time_point{
77 ReadFromLockFreeAtomicType(&m_shared_memory_ptr->steady_time_points)};
78
79 time_point.time_point = time;
80
81 WriteToLockFreeAtomicType(&m_shared_memory_ptr->steady_time_points, time_point);
82}
83
84} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/shared_memory.h b/src/core/hle/service/psc/time/shared_memory.h
new file mode 100644
index 000000000..f9bf97d5c
--- /dev/null
+++ b/src/core/hle/service/psc/time/shared_memory.h
@@ -0,0 +1,70 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7
8#include "common/common_types.h"
9#include "core/hle/service/psc/time/common.h"
10
11namespace Core {
12class System;
13}
14
15namespace Kernel {
16class KSharedMemory;
17}
18
19namespace Service::PSC::Time {
20
21template <typename T>
22struct LockFreeAtomicType {
23 u32 m_counter;
24 std::array<T, 2> m_value;
25};
26
27struct SharedMemoryStruct {
28 LockFreeAtomicType<SteadyClockTimePoint> steady_time_points;
29 LockFreeAtomicType<SystemClockContext> local_system_clock_contexts;
30 LockFreeAtomicType<SystemClockContext> network_system_clock_contexts;
31 LockFreeAtomicType<bool> automatic_corrections;
32 LockFreeAtomicType<ContinuousAdjustmentTimePoint> continuous_adjustment_time_points;
33 std::array<char, 0xEB8> pad0148;
34};
35static_assert(offsetof(SharedMemoryStruct, steady_time_points) == 0x0,
36 "steady_time_points are in the wrong place!");
37static_assert(offsetof(SharedMemoryStruct, local_system_clock_contexts) == 0x38,
38 "local_system_clock_contexts are in the wrong place!");
39static_assert(offsetof(SharedMemoryStruct, network_system_clock_contexts) == 0x80,
40 "network_system_clock_contexts are in the wrong place!");
41static_assert(offsetof(SharedMemoryStruct, automatic_corrections) == 0xC8,
42 "automatic_corrections are in the wrong place!");
43static_assert(offsetof(SharedMemoryStruct, continuous_adjustment_time_points) == 0xD0,
44 "continuous_adjustment_time_points are in the wrong place!");
45static_assert(sizeof(SharedMemoryStruct) == 0x1000,
46 "Time's SharedMemoryStruct has the wrong size!");
47static_assert(std::is_trivial_v<SharedMemoryStruct>);
48
49class SharedMemory {
50public:
51 explicit SharedMemory(Core::System& system);
52
53 Kernel::KSharedMemory& GetKSharedMemory() {
54 return m_k_shared_memory;
55 }
56
57 void SetLocalSystemContext(SystemClockContext& context);
58 void SetNetworkSystemContext(SystemClockContext& context);
59 void SetSteadyClockTimePoint(ClockSourceId clock_source_id, s64 time_diff);
60 void SetContinuousAdjustment(ContinuousAdjustmentTimePoint& time_point);
61 void SetAutomaticCorrection(bool automatic_correction);
62 void UpdateBaseTime(s64 time);
63
64private:
65 Core::System& m_system;
66 Kernel::KSharedMemory& m_k_shared_memory;
67 SharedMemoryStruct* m_shared_memory_ptr;
68};
69
70} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/static.cpp b/src/core/hle/service/psc/time/static.cpp
new file mode 100644
index 000000000..6f8cf3f88
--- /dev/null
+++ b/src/core/hle/service/psc/time/static.cpp
@@ -0,0 +1,500 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/core_timing.h"
6#include "core/hle/kernel/k_shared_memory.h"
7#include "core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h"
8#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h"
9#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h"
10#include "core/hle/service/psc/time/clocks/standard_user_system_clock_core.h"
11#include "core/hle/service/psc/time/manager.h"
12#include "core/hle/service/psc/time/shared_memory.h"
13#include "core/hle/service/psc/time/static.h"
14#include "core/hle/service/psc/time/steady_clock.h"
15#include "core/hle/service/psc/time/system_clock.h"
16#include "core/hle/service/psc/time/time_zone.h"
17#include "core/hle/service/psc/time/time_zone_service.h"
18
19namespace Service::PSC::Time {
20namespace {
21constexpr Result GetTimeFromTimePointAndContext(s64* out_time, SteadyClockTimePoint& time_point,
22 SystemClockContext& context) {
23 R_UNLESS(out_time != nullptr, ResultInvalidArgument);
24 R_UNLESS(time_point.IdMatches(context.steady_time_point), ResultClockMismatch);
25
26 *out_time = context.offset + time_point.time_point;
27 R_SUCCEED();
28}
29} // namespace
30
31StaticService::StaticService(Core::System& system_, StaticServiceSetupInfo setup_info,
32 std::shared_ptr<TimeManager> time, const char* name)
33 : ServiceFramework{system_, name}, m_system{system}, m_setup_info{setup_info}, m_time{time},
34 m_local_system_clock{m_time->m_standard_local_system_clock},
35 m_user_system_clock{m_time->m_standard_user_system_clock},
36 m_network_system_clock{m_time->m_standard_network_system_clock},
37 m_time_zone{m_time->m_time_zone},
38 m_ephemeral_network_clock{m_time->m_ephemeral_network_clock}, m_shared_memory{
39 m_time->m_shared_memory} {
40 // clang-format off
41 static const FunctionInfo functions[] = {
42 {0, &StaticService::Handle_GetStandardUserSystemClock, "GetStandardUserSystemClock"},
43 {1, &StaticService::Handle_GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"},
44 {2, &StaticService::Handle_GetStandardSteadyClock, "GetStandardSteadyClock"},
45 {3, &StaticService::Handle_GetTimeZoneService, "GetTimeZoneService"},
46 {4, &StaticService::Handle_GetStandardLocalSystemClock, "GetStandardLocalSystemClock"},
47 {5, &StaticService::Handle_GetEphemeralNetworkSystemClock, "GetEphemeralNetworkSystemClock"},
48 {20, &StaticService::Handle_GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"},
49 {50, &StaticService::Handle_SetStandardSteadyClockInternalOffset, "SetStandardSteadyClockInternalOffset"},
50 {51, &StaticService::Handle_GetStandardSteadyClockRtcValue, "GetStandardSteadyClockRtcValue"},
51 {100, &StaticService::Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled, "IsStandardUserSystemClockAutomaticCorrectionEnabled"},
52 {101, &StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled, "SetStandardUserSystemClockAutomaticCorrectionEnabled"},
53 {102, &StaticService::Handle_GetStandardUserSystemClockInitialYear, "GetStandardUserSystemClockInitialYear"},
54 {200, &StaticService::Handle_IsStandardNetworkSystemClockAccuracySufficient, "IsStandardNetworkSystemClockAccuracySufficient"},
55 {201, &StaticService::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"},
56 {300, &StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint, "CalculateMonotonicSystemClockBaseTimePoint"},
57 {400, &StaticService::Handle_GetClockSnapshot, "GetClockSnapshot"},
58 {401, &StaticService::Handle_GetClockSnapshotFromSystemClockContext, "GetClockSnapshotFromSystemClockContext"},
59 {500, &StaticService::Handle_CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"},
60 {501, &StaticService::Handle_CalculateSpanBetween, "CalculateSpanBetween"},
61 };
62 // clang-format on
63
64 RegisterHandlers(functions);
65}
66
67Result StaticService::GetClockSnapshotImpl(ClockSnapshot& out_snapshot,
68 SystemClockContext& user_context,
69 SystemClockContext& network_context, TimeType type) {
70 out_snapshot.user_context = user_context;
71 out_snapshot.network_context = network_context;
72
73 R_TRY(
74 m_time->m_standard_steady_clock.GetCurrentTimePoint(out_snapshot.steady_clock_time_point));
75
76 out_snapshot.is_automatic_correction_enabled = m_user_system_clock.GetAutomaticCorrection();
77
78 R_TRY(m_time_zone.GetLocationName(out_snapshot.location_name));
79
80 R_TRY(GetTimeFromTimePointAndContext(
81 &out_snapshot.user_time, out_snapshot.steady_clock_time_point, out_snapshot.user_context));
82
83 R_TRY(m_time_zone.ToCalendarTimeWithMyRule(out_snapshot.user_calendar_time,
84 out_snapshot.user_calendar_additional_time,
85 out_snapshot.user_time));
86
87 if (GetTimeFromTimePointAndContext(&out_snapshot.network_time,
88 out_snapshot.steady_clock_time_point,
89 out_snapshot.network_context) != ResultSuccess) {
90 out_snapshot.network_time = 0;
91 }
92
93 R_TRY(m_time_zone.ToCalendarTimeWithMyRule(out_snapshot.network_calendar_time,
94 out_snapshot.network_calendar_additional_time,
95 out_snapshot.network_time));
96 out_snapshot.type = type;
97 out_snapshot.unk_CE = 0;
98 R_SUCCEED();
99}
100
101void StaticService::Handle_GetStandardUserSystemClock(HLERequestContext& ctx) {
102 LOG_DEBUG(Service_Time, "called.");
103
104 std::shared_ptr<SystemClock> service{};
105 auto res = GetStandardUserSystemClock(service);
106
107 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
108 rb.Push(res);
109 rb.PushIpcInterface<SystemClock>(std::move(service));
110}
111
112void StaticService::Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx) {
113 LOG_DEBUG(Service_Time, "called.");
114
115 std::shared_ptr<SystemClock> service{};
116 auto res = GetStandardNetworkSystemClock(service);
117
118 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
119 rb.Push(res);
120 rb.PushIpcInterface<SystemClock>(std::move(service));
121}
122
123void StaticService::Handle_GetStandardSteadyClock(HLERequestContext& ctx) {
124 LOG_DEBUG(Service_Time, "called.");
125
126 std::shared_ptr<SteadyClock> service{};
127 auto res = GetStandardSteadyClock(service);
128
129 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
130 rb.Push(res);
131 rb.PushIpcInterface(std::move(service));
132}
133
134void StaticService::Handle_GetTimeZoneService(HLERequestContext& ctx) {
135 LOG_DEBUG(Service_Time, "called.");
136
137 std::shared_ptr<TimeZoneService> service{};
138 auto res = GetTimeZoneService(service);
139
140 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
141 rb.Push(res);
142 rb.PushIpcInterface(std::move(service));
143}
144
145void StaticService::Handle_GetStandardLocalSystemClock(HLERequestContext& ctx) {
146 LOG_DEBUG(Service_Time, "called.");
147
148 std::shared_ptr<SystemClock> service{};
149 auto res = GetStandardLocalSystemClock(service);
150
151 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
152 rb.Push(res);
153 rb.PushIpcInterface<SystemClock>(std::move(service));
154}
155
156void StaticService::Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx) {
157 LOG_DEBUG(Service_Time, "called.");
158
159 std::shared_ptr<SystemClock> service{};
160 auto res = GetEphemeralNetworkSystemClock(service);
161
162 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
163 rb.Push(res);
164 rb.PushIpcInterface<SystemClock>(std::move(service));
165}
166
167void StaticService::Handle_GetSharedMemoryNativeHandle(HLERequestContext& ctx) {
168 LOG_DEBUG(Service_Time, "called.");
169
170 Kernel::KSharedMemory* shared_memory{};
171 auto res = GetSharedMemoryNativeHandle(&shared_memory);
172
173 IPC::ResponseBuilder rb{ctx, 2, 1};
174 rb.Push(res);
175 rb.PushCopyObjects(shared_memory);
176}
177
178void StaticService::Handle_SetStandardSteadyClockInternalOffset(HLERequestContext& ctx) {
179 LOG_DEBUG(Service_Time, "called.");
180
181 IPC::ResponseBuilder rb{ctx, 2};
182 rb.Push(m_setup_info.can_write_steady_clock ? ResultNotImplemented : ResultPermissionDenied);
183}
184
185void StaticService::Handle_GetStandardSteadyClockRtcValue(HLERequestContext& ctx) {
186 LOG_DEBUG(Service_Time, "called.");
187
188 IPC::ResponseBuilder rb{ctx, 2};
189 rb.Push(ResultNotImplemented);
190}
191
192void StaticService::Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled(
193 HLERequestContext& ctx) {
194 LOG_DEBUG(Service_Time, "called.");
195
196 bool is_enabled{};
197 auto res = IsStandardUserSystemClockAutomaticCorrectionEnabled(is_enabled);
198
199 IPC::ResponseBuilder rb{ctx, 3};
200 rb.Push(res);
201 rb.Push<bool>(is_enabled);
202}
203
204void StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled(
205 HLERequestContext& ctx) {
206 LOG_DEBUG(Service_Time, "called.");
207
208 IPC::RequestParser rp{ctx};
209 auto automatic_correction{rp.Pop<bool>()};
210
211 auto res = SetStandardUserSystemClockAutomaticCorrectionEnabled(automatic_correction);
212
213 IPC::ResponseBuilder rb{ctx, 2};
214 rb.Push(res);
215}
216
217void StaticService::Handle_GetStandardUserSystemClockInitialYear(HLERequestContext& ctx) {
218 LOG_DEBUG(Service_Time, "called.");
219
220 IPC::ResponseBuilder rb{ctx, 2};
221 rb.Push(ResultNotImplemented);
222}
223
224void StaticService::Handle_IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx) {
225 LOG_DEBUG(Service_Time, "called.");
226
227 bool is_sufficient{};
228 auto res = IsStandardNetworkSystemClockAccuracySufficient(is_sufficient);
229
230 IPC::ResponseBuilder rb{ctx, 3};
231 rb.Push(res);
232 rb.Push<bool>(is_sufficient);
233}
234
235void StaticService::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
236 HLERequestContext& ctx) {
237 LOG_DEBUG(Service_Time, "called.");
238
239 SteadyClockTimePoint time_point{};
240 auto res = GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
241
242 IPC::ResponseBuilder rb{ctx, 2 + sizeof(SteadyClockTimePoint) / sizeof(u32)};
243 rb.Push(res);
244 rb.PushRaw<SteadyClockTimePoint>(time_point);
245}
246
247void StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx) {
248 LOG_DEBUG(Service_Time, "called.");
249
250 IPC::RequestParser rp{ctx};
251 auto context{rp.PopRaw<SystemClockContext>()};
252
253 s64 time{};
254 auto res = CalculateMonotonicSystemClockBaseTimePoint(time, context);
255
256 IPC::ResponseBuilder rb{ctx, 4};
257 rb.Push(res);
258 rb.Push<s64>(time);
259}
260
261void StaticService::Handle_GetClockSnapshot(HLERequestContext& ctx) {
262 LOG_DEBUG(Service_Time, "called.");
263
264 IPC::RequestParser rp{ctx};
265 auto type{rp.PopEnum<TimeType>()};
266
267 ClockSnapshot snapshot{};
268 auto res = GetClockSnapshot(snapshot, type);
269
270 ctx.WriteBuffer(snapshot);
271
272 IPC::ResponseBuilder rb{ctx, 2};
273 rb.Push(res);
274}
275
276void StaticService::Handle_GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx) {
277 LOG_DEBUG(Service_Time, "called.");
278
279 IPC::RequestParser rp{ctx};
280 auto clock_type{rp.PopEnum<TimeType>()};
281 [[maybe_unused]] auto alignment{rp.Pop<u32>()};
282 auto user_context{rp.PopRaw<SystemClockContext>()};
283 auto network_context{rp.PopRaw<SystemClockContext>()};
284
285 ClockSnapshot snapshot{};
286 auto res =
287 GetClockSnapshotFromSystemClockContext(snapshot, user_context, network_context, clock_type);
288
289 ctx.WriteBuffer(snapshot);
290
291 IPC::ResponseBuilder rb{ctx, 2};
292 rb.Push(res);
293}
294
295void StaticService::Handle_CalculateStandardUserSystemClockDifferenceByUser(
296 HLERequestContext& ctx) {
297 LOG_DEBUG(Service_Time, "called.");
298
299 ClockSnapshot a{};
300 ClockSnapshot b{};
301
302 auto a_buffer{ctx.ReadBuffer(0)};
303 auto b_buffer{ctx.ReadBuffer(1)};
304
305 std::memcpy(&a, a_buffer.data(), sizeof(ClockSnapshot));
306 std::memcpy(&b, b_buffer.data(), sizeof(ClockSnapshot));
307
308 s64 difference{};
309 auto res = CalculateStandardUserSystemClockDifferenceByUser(difference, a, b);
310
311 IPC::ResponseBuilder rb{ctx, 4};
312 rb.Push(res);
313 rb.Push(difference);
314}
315
316void StaticService::Handle_CalculateSpanBetween(HLERequestContext& ctx) {
317 LOG_DEBUG(Service_Time, "called.");
318
319 ClockSnapshot a{};
320 ClockSnapshot b{};
321
322 auto a_buffer{ctx.ReadBuffer(0)};
323 auto b_buffer{ctx.ReadBuffer(1)};
324
325 std::memcpy(&a, a_buffer.data(), sizeof(ClockSnapshot));
326 std::memcpy(&b, b_buffer.data(), sizeof(ClockSnapshot));
327
328 s64 time{};
329 auto res = CalculateSpanBetween(time, a, b);
330
331 IPC::ResponseBuilder rb{ctx, 4};
332 rb.Push(res);
333 rb.Push(time);
334}
335
336// =============================== Implementations ===========================
337
338Result StaticService::GetStandardUserSystemClock(std::shared_ptr<SystemClock>& out_service) {
339 out_service = std::make_shared<SystemClock>(m_system, m_user_system_clock,
340 m_setup_info.can_write_user_clock,
341 m_setup_info.can_write_uninitialized_clock);
342 R_SUCCEED();
343}
344
345Result StaticService::GetStandardNetworkSystemClock(std::shared_ptr<SystemClock>& out_service) {
346 out_service = std::make_shared<SystemClock>(m_system, m_network_system_clock,
347 m_setup_info.can_write_network_clock,
348 m_setup_info.can_write_uninitialized_clock);
349 R_SUCCEED();
350}
351
352Result StaticService::GetStandardSteadyClock(std::shared_ptr<SteadyClock>& out_service) {
353 out_service =
354 std::make_shared<SteadyClock>(m_system, m_time, m_setup_info.can_write_steady_clock,
355 m_setup_info.can_write_uninitialized_clock);
356 R_SUCCEED();
357}
358
359Result StaticService::GetTimeZoneService(std::shared_ptr<TimeZoneService>& out_service) {
360 out_service =
361 std::make_shared<TimeZoneService>(m_system, m_time->m_standard_steady_clock, m_time_zone,
362 m_setup_info.can_write_timezone_device_location);
363 R_SUCCEED();
364}
365
366Result StaticService::GetStandardLocalSystemClock(std::shared_ptr<SystemClock>& out_service) {
367 out_service = std::make_shared<SystemClock>(m_system, m_local_system_clock,
368 m_setup_info.can_write_local_clock,
369 m_setup_info.can_write_uninitialized_clock);
370 R_SUCCEED();
371}
372
373Result StaticService::GetEphemeralNetworkSystemClock(std::shared_ptr<SystemClock>& out_service) {
374 out_service = std::make_shared<SystemClock>(m_system, m_ephemeral_network_clock,
375 m_setup_info.can_write_network_clock,
376 m_setup_info.can_write_uninitialized_clock);
377 R_SUCCEED();
378}
379
380Result StaticService::GetSharedMemoryNativeHandle(Kernel::KSharedMemory** out_shared_memory) {
381 *out_shared_memory = &m_shared_memory.GetKSharedMemory();
382 R_SUCCEED();
383}
384
385Result StaticService::IsStandardUserSystemClockAutomaticCorrectionEnabled(bool& out_is_enabled) {
386 R_UNLESS(m_user_system_clock.IsInitialized(), ResultClockUninitialized);
387
388 out_is_enabled = m_user_system_clock.GetAutomaticCorrection();
389 R_SUCCEED();
390}
391
392Result StaticService::SetStandardUserSystemClockAutomaticCorrectionEnabled(
393 bool automatic_correction) {
394 R_UNLESS(m_user_system_clock.IsInitialized() && m_time->m_standard_steady_clock.IsInitialized(),
395 ResultClockUninitialized);
396 R_UNLESS(m_setup_info.can_write_user_clock, ResultPermissionDenied);
397
398 R_TRY(m_user_system_clock.SetAutomaticCorrection(automatic_correction));
399
400 m_shared_memory.SetAutomaticCorrection(automatic_correction);
401
402 SteadyClockTimePoint time_point{};
403 R_TRY(m_time->m_standard_steady_clock.GetCurrentTimePoint(time_point));
404
405 m_user_system_clock.SetTimePointAndSignal(time_point);
406 m_user_system_clock.GetEvent().Signal();
407 R_SUCCEED();
408}
409
410Result StaticService::IsStandardNetworkSystemClockAccuracySufficient(bool& out_is_sufficient) {
411 out_is_sufficient = m_network_system_clock.IsAccuracySufficient();
412 R_SUCCEED();
413}
414
415Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
416 SteadyClockTimePoint& out_time_point) {
417 R_UNLESS(m_user_system_clock.IsInitialized(), ResultClockUninitialized);
418
419 m_user_system_clock.GetTimePoint(out_time_point);
420
421 R_SUCCEED();
422}
423
424Result StaticService::CalculateMonotonicSystemClockBaseTimePoint(s64& out_time,
425 SystemClockContext& context) {
426 R_UNLESS(m_time->m_standard_steady_clock.IsInitialized(), ResultClockUninitialized);
427
428 SteadyClockTimePoint time_point{};
429 R_TRY(m_time->m_standard_steady_clock.GetCurrentTimePoint(time_point));
430
431 R_UNLESS(time_point.IdMatches(context.steady_time_point), ResultClockMismatch);
432
433 auto one_second_ns{
434 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
435 auto ticks{m_system.CoreTiming().GetClockTicks()};
436 auto current_time{ConvertToTimeSpan(ticks).count()};
437 out_time = ((context.offset + time_point.time_point) - (current_time / one_second_ns));
438 R_SUCCEED();
439}
440
441Result StaticService::GetClockSnapshot(ClockSnapshot& out_snapshot, TimeType type) {
442 SystemClockContext user_context{};
443 R_TRY(m_user_system_clock.GetContext(user_context));
444
445 SystemClockContext network_context{};
446 R_TRY(m_network_system_clock.GetContext(network_context));
447
448 R_RETURN(GetClockSnapshotImpl(out_snapshot, user_context, network_context, type));
449}
450
451Result StaticService::GetClockSnapshotFromSystemClockContext(ClockSnapshot& out_snapshot,
452 SystemClockContext& user_context,
453 SystemClockContext& network_context,
454 TimeType type) {
455 R_RETURN(GetClockSnapshotImpl(out_snapshot, user_context, network_context, type));
456}
457
458Result StaticService::CalculateStandardUserSystemClockDifferenceByUser(s64& out_time,
459 ClockSnapshot& a,
460 ClockSnapshot& b) {
461 auto diff_s =
462 std::chrono::seconds(b.user_context.offset) - std::chrono::seconds(a.user_context.offset);
463
464 if (a.user_context == b.user_context ||
465 !a.user_context.steady_time_point.IdMatches(b.user_context.steady_time_point)) {
466 out_time = 0;
467 R_SUCCEED();
468 }
469
470 if (!a.is_automatic_correction_enabled || !b.is_automatic_correction_enabled) {
471 out_time = std::chrono::duration_cast<std::chrono::nanoseconds>(diff_s).count();
472 R_SUCCEED();
473 }
474
475 if (a.network_context.steady_time_point.IdMatches(a.steady_clock_time_point) ||
476 b.network_context.steady_time_point.IdMatches(b.steady_clock_time_point)) {
477 out_time = 0;
478 R_SUCCEED();
479 }
480
481 out_time = std::chrono::duration_cast<std::chrono::nanoseconds>(diff_s).count();
482 R_SUCCEED();
483}
484
485Result StaticService::CalculateSpanBetween(s64& out_time, ClockSnapshot& a, ClockSnapshot& b) {
486 s64 time_s{};
487 auto res =
488 GetSpanBetweenTimePoints(&time_s, a.steady_clock_time_point, b.steady_clock_time_point);
489
490 if (res != ResultSuccess) {
491 R_UNLESS(a.network_time != 0 && b.network_time != 0, ResultTimeNotFound);
492 time_s = b.network_time - a.network_time;
493 }
494
495 out_time =
496 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(time_s)).count();
497 R_SUCCEED();
498}
499
500} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/static.h b/src/core/hle/service/psc/time/static.h
new file mode 100644
index 000000000..498cd5ab5
--- /dev/null
+++ b/src/core/hle/service/psc/time/static.h
@@ -0,0 +1,95 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/ipc_helpers.h"
7#include "core/hle/service/psc/time/common.h"
8#include "core/hle/service/server_manager.h"
9#include "core/hle/service/service.h"
10
11namespace Core {
12class System;
13}
14
15namespace Kernel {
16class KSharedMemory;
17}
18
19namespace Service::PSC::Time {
20class TimeManager;
21class StandardLocalSystemClockCore;
22class StandardUserSystemClockCore;
23class StandardNetworkSystemClockCore;
24class TimeZone;
25class SystemClock;
26class SteadyClock;
27class TimeZoneService;
28class EphemeralNetworkSystemClockCore;
29class SharedMemory;
30
31class StaticService final : public ServiceFramework<StaticService> {
32public:
33 explicit StaticService(Core::System& system, StaticServiceSetupInfo setup_info,
34 std::shared_ptr<TimeManager> time, const char* name);
35
36 ~StaticService() override = default;
37
38 Result GetStandardUserSystemClock(std::shared_ptr<SystemClock>& out_service);
39 Result GetStandardNetworkSystemClock(std::shared_ptr<SystemClock>& out_service);
40 Result GetStandardSteadyClock(std::shared_ptr<SteadyClock>& out_service);
41 Result GetTimeZoneService(std::shared_ptr<TimeZoneService>& out_service);
42 Result GetStandardLocalSystemClock(std::shared_ptr<SystemClock>& out_service);
43 Result GetEphemeralNetworkSystemClock(std::shared_ptr<SystemClock>& out_service);
44 Result GetSharedMemoryNativeHandle(Kernel::KSharedMemory** out_shared_memory);
45 Result IsStandardUserSystemClockAutomaticCorrectionEnabled(bool& out_is_enabled);
46 Result SetStandardUserSystemClockAutomaticCorrectionEnabled(bool automatic_correction);
47 Result IsStandardNetworkSystemClockAccuracySufficient(bool& out_is_sufficient);
48 Result GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
49 SteadyClockTimePoint& out_time_point);
50 Result CalculateMonotonicSystemClockBaseTimePoint(s64& out_time, SystemClockContext& context);
51 Result GetClockSnapshot(ClockSnapshot& out_snapshot, TimeType type);
52 Result GetClockSnapshotFromSystemClockContext(ClockSnapshot& out_snapshot,
53 SystemClockContext& user_context,
54 SystemClockContext& network_context,
55 TimeType type);
56 Result CalculateStandardUserSystemClockDifferenceByUser(s64& out_time, ClockSnapshot& a,
57 ClockSnapshot& b);
58 Result CalculateSpanBetween(s64& out_time, ClockSnapshot& a, ClockSnapshot& b);
59
60private:
61 Result GetClockSnapshotImpl(ClockSnapshot& out_snapshot, SystemClockContext& user_context,
62 SystemClockContext& network_context, TimeType type);
63
64 void Handle_GetStandardUserSystemClock(HLERequestContext& ctx);
65 void Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx);
66 void Handle_GetStandardSteadyClock(HLERequestContext& ctx);
67 void Handle_GetTimeZoneService(HLERequestContext& ctx);
68 void Handle_GetStandardLocalSystemClock(HLERequestContext& ctx);
69 void Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx);
70 void Handle_GetSharedMemoryNativeHandle(HLERequestContext& ctx);
71 void Handle_SetStandardSteadyClockInternalOffset(HLERequestContext& ctx);
72 void Handle_GetStandardSteadyClockRtcValue(HLERequestContext& ctx);
73 void Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx);
74 void Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx);
75 void Handle_GetStandardUserSystemClockInitialYear(HLERequestContext& ctx);
76 void Handle_IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx);
77 void Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx);
78 void Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx);
79 void Handle_GetClockSnapshot(HLERequestContext& ctx);
80 void Handle_GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx);
81 void Handle_CalculateStandardUserSystemClockDifferenceByUser(HLERequestContext& ctx);
82 void Handle_CalculateSpanBetween(HLERequestContext& ctx);
83
84 Core::System& m_system;
85 StaticServiceSetupInfo m_setup_info;
86 std::shared_ptr<TimeManager> m_time;
87 StandardLocalSystemClockCore& m_local_system_clock;
88 StandardUserSystemClockCore& m_user_system_clock;
89 StandardNetworkSystemClockCore& m_network_system_clock;
90 TimeZone& m_time_zone;
91 EphemeralNetworkSystemClockCore& m_ephemeral_network_clock;
92 SharedMemory& m_shared_memory;
93};
94
95} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/steady_clock.cpp b/src/core/hle/service/psc/time/steady_clock.cpp
new file mode 100644
index 000000000..1ed5c7679
--- /dev/null
+++ b/src/core/hle/service/psc/time/steady_clock.cpp
@@ -0,0 +1,164 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/service/psc/time/steady_clock.h"
6
7namespace Service::PSC::Time {
8
9SteadyClock::SteadyClock(Core::System& system_, std::shared_ptr<TimeManager> manager,
10 bool can_write_steady_clock, bool can_write_uninitialized_clock)
11 : ServiceFramework{system_, "ISteadyClock"}, m_system{system},
12 m_clock_core{manager->m_standard_steady_clock},
13 m_can_write_steady_clock{can_write_steady_clock}, m_can_write_uninitialized_clock{
14 can_write_uninitialized_clock} {
15 // clang-format off
16 static const FunctionInfo functions[] = {
17 {0, &SteadyClock::Handle_GetCurrentTimePoint, "GetCurrentTimePoint"},
18 {2, &SteadyClock::Handle_GetTestOffset, "GetTestOffset"},
19 {3, &SteadyClock::Handle_SetTestOffset, "SetTestOffset"},
20 {100, &SteadyClock::Handle_GetRtcValue, "GetRtcValue"},
21 {101, &SteadyClock::Handle_IsRtcResetDetected, "IsRtcResetDetected"},
22 {102, &SteadyClock::Handle_GetSetupResultValue, "GetSetupResultValue"},
23 {200, &SteadyClock::Handle_GetInternalOffset, "GetInternalOffset"},
24 };
25 // clang-format on
26 RegisterHandlers(functions);
27}
28
29void SteadyClock::Handle_GetCurrentTimePoint(HLERequestContext& ctx) {
30 LOG_DEBUG(Service_Time, "called.");
31
32 SteadyClockTimePoint time_point{};
33 auto res = GetCurrentTimePoint(time_point);
34
35 IPC::ResponseBuilder rb{ctx, 2 + sizeof(SteadyClockTimePoint) / sizeof(u32)};
36 rb.Push(res);
37 rb.PushRaw<SteadyClockTimePoint>(time_point);
38}
39
40void SteadyClock::Handle_GetTestOffset(HLERequestContext& ctx) {
41 LOG_DEBUG(Service_Time, "called.");
42
43 s64 test_offset{};
44 auto res = GetTestOffset(test_offset);
45
46 IPC::ResponseBuilder rb{ctx, 4};
47 rb.Push(res);
48 rb.Push(test_offset);
49}
50
51void SteadyClock::Handle_SetTestOffset(HLERequestContext& ctx) {
52 LOG_DEBUG(Service_Time, "called.");
53
54 IPC::RequestParser rp{ctx};
55 auto test_offset{rp.Pop<s64>()};
56
57 auto res = SetTestOffset(test_offset);
58
59 IPC::ResponseBuilder rb{ctx, 2};
60 rb.Push(res);
61}
62
63void SteadyClock::Handle_GetRtcValue(HLERequestContext& ctx) {
64 LOG_DEBUG(Service_Time, "called.");
65
66 s64 rtc_value{};
67 auto res = GetRtcValue(rtc_value);
68
69 IPC::ResponseBuilder rb{ctx, 4};
70 rb.Push(res);
71 rb.Push(rtc_value);
72}
73
74void SteadyClock::Handle_IsRtcResetDetected(HLERequestContext& ctx) {
75 LOG_DEBUG(Service_Time, "called.");
76
77 bool reset_detected{false};
78 auto res = IsRtcResetDetected(reset_detected);
79
80 IPC::ResponseBuilder rb{ctx, 3};
81 rb.Push(res);
82 rb.Push(reset_detected);
83}
84
85void SteadyClock::Handle_GetSetupResultValue(HLERequestContext& ctx) {
86 LOG_DEBUG(Service_Time, "called.");
87
88 Result result_value{ResultSuccess};
89 auto res = GetSetupResultValue(result_value);
90
91 IPC::ResponseBuilder rb{ctx, 3};
92 rb.Push(res);
93 rb.Push(result_value);
94}
95
96void SteadyClock::Handle_GetInternalOffset(HLERequestContext& ctx) {
97 LOG_DEBUG(Service_Time, "called.");
98
99 s64 internal_offset{};
100 auto res = GetInternalOffset(internal_offset);
101
102 IPC::ResponseBuilder rb{ctx, 4};
103 rb.Push(res);
104 rb.Push(internal_offset);
105}
106
107// =============================== Implementations ===========================
108
109Result SteadyClock::GetCurrentTimePoint(SteadyClockTimePoint& out_time_point) {
110 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
111 ResultClockUninitialized);
112
113 R_RETURN(m_clock_core.GetCurrentTimePoint(out_time_point));
114}
115
116Result SteadyClock::GetTestOffset(s64& out_test_offset) {
117 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
118 ResultClockUninitialized);
119
120 out_test_offset = m_clock_core.GetTestOffset();
121 R_SUCCEED();
122}
123
124Result SteadyClock::SetTestOffset(s64 test_offset) {
125 R_UNLESS(m_can_write_steady_clock, ResultPermissionDenied);
126 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
127 ResultClockUninitialized);
128
129 m_clock_core.SetTestOffset(test_offset);
130 R_SUCCEED();
131}
132
133Result SteadyClock::GetRtcValue(s64& out_rtc_value) {
134 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
135 ResultClockUninitialized);
136
137 R_RETURN(m_clock_core.GetRtcValue(out_rtc_value));
138}
139
140Result SteadyClock::IsRtcResetDetected(bool& out_is_detected) {
141 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
142 ResultClockUninitialized);
143
144 out_is_detected = m_clock_core.IsResetDetected();
145 R_SUCCEED();
146}
147
148Result SteadyClock::GetSetupResultValue(Result& out_result) {
149 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
150 ResultClockUninitialized);
151
152 out_result = m_clock_core.GetSetupResultValue();
153 R_SUCCEED();
154}
155
156Result SteadyClock::GetInternalOffset(s64& out_internal_offset) {
157 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
158 ResultClockUninitialized);
159
160 out_internal_offset = m_clock_core.GetInternalOffset();
161 R_SUCCEED();
162}
163
164} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/steady_clock.h b/src/core/hle/service/psc/time/steady_clock.h
new file mode 100644
index 000000000..115e9b138
--- /dev/null
+++ b/src/core/hle/service/psc/time/steady_clock.h
@@ -0,0 +1,49 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/ipc_helpers.h"
7#include "core/hle/service/psc/time/common.h"
8#include "core/hle/service/psc/time/manager.h"
9#include "core/hle/service/server_manager.h"
10#include "core/hle/service/service.h"
11
12namespace Core {
13class System;
14}
15
16namespace Service::PSC::Time {
17
18class SteadyClock final : public ServiceFramework<SteadyClock> {
19public:
20 explicit SteadyClock(Core::System& system, std::shared_ptr<TimeManager> manager,
21 bool can_write_steady_clock, bool can_write_uninitialized_clock);
22
23 ~SteadyClock() override = default;
24
25 Result GetCurrentTimePoint(SteadyClockTimePoint& out_time_point);
26 Result GetTestOffset(s64& out_test_offset);
27 Result SetTestOffset(s64 test_offset);
28 Result GetRtcValue(s64& out_rtc_value);
29 Result IsRtcResetDetected(bool& out_is_detected);
30 Result GetSetupResultValue(Result& out_result);
31 Result GetInternalOffset(s64& out_internal_offset);
32
33private:
34 void Handle_GetCurrentTimePoint(HLERequestContext& ctx);
35 void Handle_GetTestOffset(HLERequestContext& ctx);
36 void Handle_SetTestOffset(HLERequestContext& ctx);
37 void Handle_GetRtcValue(HLERequestContext& ctx);
38 void Handle_IsRtcResetDetected(HLERequestContext& ctx);
39 void Handle_GetSetupResultValue(HLERequestContext& ctx);
40 void Handle_GetInternalOffset(HLERequestContext& ctx);
41
42 Core::System& m_system;
43
44 StandardSteadyClockCore& m_clock_core;
45 bool m_can_write_steady_clock;
46 bool m_can_write_uninitialized_clock;
47};
48
49} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/system_clock.cpp b/src/core/hle/service/psc/time/system_clock.cpp
new file mode 100644
index 000000000..13d2f1d11
--- /dev/null
+++ b/src/core/hle/service/psc/time/system_clock.cpp
@@ -0,0 +1,127 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/service/psc/time/system_clock.h"
6
7namespace Service::PSC::Time {
8
9SystemClock::SystemClock(Core::System& system_, SystemClockCore& clock_core, bool can_write_clock,
10 bool can_write_uninitialized_clock)
11 : ServiceFramework{system_, "ISystemClock"}, m_system{system}, m_clock_core{clock_core},
12 m_can_write_clock{can_write_clock}, m_can_write_uninitialized_clock{
13 can_write_uninitialized_clock} {
14 // clang-format off
15 static const FunctionInfo functions[] = {
16 {0, &SystemClock::Handle_GetCurrentTime, "GetCurrentTime"},
17 {1, &SystemClock::Handle_SetCurrentTime, "SetCurrentTime"},
18 {2, &SystemClock::Handle_GetSystemClockContext, "GetSystemClockContext"},
19 {3, &SystemClock::Handle_SetSystemClockContext, "SetSystemClockContext"},
20 {4, &SystemClock::Handle_GetOperationEventReadableHandle, "GetOperationEventReadableHandle"},
21 };
22 // clang-format on
23 RegisterHandlers(functions);
24}
25
26void SystemClock::Handle_GetCurrentTime(HLERequestContext& ctx) {
27 LOG_DEBUG(Service_Time, "called.");
28
29 s64 time{};
30 auto res = GetCurrentTime(time);
31
32 IPC::ResponseBuilder rb{ctx, 4};
33 rb.Push(res);
34 rb.Push<s64>(time);
35}
36
37void SystemClock::Handle_SetCurrentTime(HLERequestContext& ctx) {
38 LOG_DEBUG(Service_Time, "called.");
39
40 IPC::RequestParser rp{ctx};
41 auto time{rp.Pop<s64>()};
42
43 auto res = SetCurrentTime(time);
44
45 IPC::ResponseBuilder rb{ctx, 2};
46 rb.Push(res);
47}
48
49void SystemClock::Handle_GetSystemClockContext(HLERequestContext& ctx) {
50 LOG_DEBUG(Service_Time, "called.");
51
52 SystemClockContext context{};
53 auto res = GetSystemClockContext(context);
54
55 IPC::ResponseBuilder rb{ctx, 2 + sizeof(SystemClockContext) / sizeof(u32)};
56 rb.Push(res);
57 rb.PushRaw<SystemClockContext>(context);
58}
59
60void SystemClock::Handle_SetSystemClockContext(HLERequestContext& ctx) {
61 LOG_DEBUG(Service_Time, "called.");
62
63 IPC::RequestParser rp{ctx};
64 auto context{rp.PopRaw<SystemClockContext>()};
65
66 auto res = SetSystemClockContext(context);
67
68 IPC::ResponseBuilder rb{ctx, 2};
69 rb.Push(res);
70}
71
72void SystemClock::Handle_GetOperationEventReadableHandle(HLERequestContext& ctx) {
73 LOG_DEBUG(Service_Time, "called.");
74
75 Kernel::KEvent* event{};
76 auto res = GetOperationEventReadableHandle(&event);
77
78 IPC::ResponseBuilder rb{ctx, 2, 1};
79 rb.Push(res);
80 rb.PushCopyObjects(event->GetReadableEvent());
81}
82
83// =============================== Implementations ===========================
84
85Result SystemClock::GetCurrentTime(s64& out_time) {
86 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
87 ResultClockUninitialized);
88
89 R_RETURN(m_clock_core.GetCurrentTime(&out_time));
90}
91
92Result SystemClock::SetCurrentTime(s64 time) {
93 R_UNLESS(m_can_write_clock, ResultPermissionDenied);
94 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
95 ResultClockUninitialized);
96
97 R_RETURN(m_clock_core.SetCurrentTime(time));
98}
99
100Result SystemClock::GetSystemClockContext(SystemClockContext& out_context) {
101 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
102 ResultClockUninitialized);
103
104 R_RETURN(m_clock_core.GetContext(out_context));
105}
106
107Result SystemClock::SetSystemClockContext(SystemClockContext& context) {
108 R_UNLESS(m_can_write_clock, ResultPermissionDenied);
109 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
110 ResultClockUninitialized);
111
112 R_RETURN(m_clock_core.SetContextAndWrite(context));
113}
114
115Result SystemClock::GetOperationEventReadableHandle(Kernel::KEvent** out_event) {
116 if (!m_operation_event) {
117 m_operation_event = std::make_unique<OperationEvent>(m_system);
118 R_UNLESS(m_operation_event != nullptr, ResultFailed);
119
120 m_clock_core.LinkOperationEvent(*m_operation_event);
121 }
122
123 *out_event = m_operation_event->m_event;
124 R_SUCCEED();
125}
126
127} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/system_clock.h b/src/core/hle/service/psc/time/system_clock.h
new file mode 100644
index 000000000..f30027e7b
--- /dev/null
+++ b/src/core/hle/service/psc/time/system_clock.h
@@ -0,0 +1,46 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/ipc_helpers.h"
7#include "core/hle/service/psc/time/common.h"
8#include "core/hle/service/psc/time/manager.h"
9#include "core/hle/service/server_manager.h"
10#include "core/hle/service/service.h"
11
12namespace Core {
13class System;
14}
15
16namespace Service::PSC::Time {
17
18class SystemClock final : public ServiceFramework<SystemClock> {
19public:
20 explicit SystemClock(Core::System& system, SystemClockCore& system_clock_core,
21 bool can_write_clock, bool can_write_uninitialized_clock);
22
23 ~SystemClock() override = default;
24
25 Result GetCurrentTime(s64& out_time);
26 Result SetCurrentTime(s64 time);
27 Result GetSystemClockContext(SystemClockContext& out_context);
28 Result SetSystemClockContext(SystemClockContext& context);
29 Result GetOperationEventReadableHandle(Kernel::KEvent** out_event);
30
31private:
32 void Handle_GetCurrentTime(HLERequestContext& ctx);
33 void Handle_SetCurrentTime(HLERequestContext& ctx);
34 void Handle_GetSystemClockContext(HLERequestContext& ctx);
35 void Handle_SetSystemClockContext(HLERequestContext& ctx);
36 void Handle_GetOperationEventReadableHandle(HLERequestContext& ctx);
37
38 Core::System& m_system;
39
40 SystemClockCore& m_clock_core;
41 bool m_can_write_clock;
42 bool m_can_write_uninitialized_clock;
43 std::unique_ptr<OperationEvent> m_operation_event{};
44};
45
46} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/time_zone.cpp b/src/core/hle/service/psc/time/time_zone.cpp
new file mode 100644
index 000000000..cfee8f866
--- /dev/null
+++ b/src/core/hle/service/psc/time/time_zone.cpp
@@ -0,0 +1,280 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/psc/time/time_zone.h"
5
6namespace Service::PSC::Time {
7namespace {
8constexpr Result ValidateRule(Tz::Rule& rule) {
9 if (rule.typecnt > static_cast<s32>(Tz::TZ_MAX_TYPES) ||
10 rule.timecnt > static_cast<s32>(Tz::TZ_MAX_TIMES) ||
11 rule.charcnt > static_cast<s32>(Tz::TZ_MAX_CHARS)) {
12 R_RETURN(ResultTimeZoneOutOfRange);
13 }
14
15 for (s32 i = 0; i < rule.timecnt; i++) {
16 if (rule.types[i] >= rule.typecnt) {
17 R_RETURN(ResultTimeZoneOutOfRange);
18 }
19 }
20
21 for (s32 i = 0; i < rule.typecnt; i++) {
22 if (rule.ttis[i].tt_desigidx >= static_cast<s32>(rule.chars.size())) {
23 R_RETURN(ResultTimeZoneOutOfRange);
24 }
25 }
26 R_SUCCEED();
27}
28
29constexpr bool GetTimeZoneTime(s64& out_time, Tz::Rule& rule, s64 time, s32 index,
30 s32 index_offset) {
31 s32 found_idx{};
32 s32 expected_index{index + index_offset};
33 s64 time_to_find{time + rule.ttis[rule.types[index]].tt_utoff -
34 rule.ttis[rule.types[expected_index]].tt_utoff};
35
36 if (rule.timecnt > 1 && rule.ats[0] <= time_to_find) {
37 s32 low{1};
38 s32 high{rule.timecnt};
39
40 while (low < high) {
41 auto mid{(low + high) / 2};
42 if (rule.ats[mid] <= time_to_find) {
43 low = mid + 1;
44 } else if (rule.ats[mid] > time_to_find) {
45 high = mid;
46 }
47 }
48 found_idx = low - 1;
49 }
50
51 if (found_idx == expected_index) {
52 out_time = time_to_find;
53 }
54 return found_idx == expected_index;
55}
56} // namespace
57
58void TimeZone::SetTimePoint(SteadyClockTimePoint& time_point) {
59 std::scoped_lock l{m_mutex};
60 m_steady_clock_time_point = time_point;
61}
62
63void TimeZone::SetTotalLocationNameCount(u32 count) {
64 std::scoped_lock l{m_mutex};
65 m_total_location_name_count = count;
66}
67
68void TimeZone::SetRuleVersion(RuleVersion& rule_version) {
69 std::scoped_lock l{m_mutex};
70 m_rule_version = rule_version;
71}
72
73Result TimeZone::GetLocationName(LocationName& out_name) {
74 std::scoped_lock l{m_mutex};
75 R_UNLESS(m_initialized, ResultClockUninitialized);
76 out_name = m_location;
77 R_SUCCEED();
78}
79
80Result TimeZone::GetTotalLocationCount(u32& out_count) {
81 std::scoped_lock l{m_mutex};
82 if (!m_initialized) {
83 return ResultClockUninitialized;
84 }
85
86 out_count = m_total_location_name_count;
87 R_SUCCEED();
88}
89
90Result TimeZone::GetRuleVersion(RuleVersion& out_rule_version) {
91 std::scoped_lock l{m_mutex};
92 if (!m_initialized) {
93 return ResultClockUninitialized;
94 }
95 out_rule_version = m_rule_version;
96 R_SUCCEED();
97}
98
99Result TimeZone::GetTimePoint(SteadyClockTimePoint& out_time_point) {
100 std::scoped_lock l{m_mutex};
101 if (!m_initialized) {
102 return ResultClockUninitialized;
103 }
104 out_time_point = m_steady_clock_time_point;
105 R_SUCCEED();
106}
107
108Result TimeZone::ToCalendarTime(CalendarTime& out_calendar_time,
109 CalendarAdditionalInfo& out_additional_info, s64 time,
110 Tz::Rule& rule) {
111 std::scoped_lock l{m_mutex};
112 R_RETURN(ToCalendarTimeImpl(out_calendar_time, out_additional_info, time, rule));
113}
114
115Result TimeZone::ToCalendarTimeWithMyRule(CalendarTime& calendar_time,
116 CalendarAdditionalInfo& calendar_additional, s64 time) {
117 // This is checked outside the mutex. Bug?
118 if (!m_initialized) {
119 return ResultClockUninitialized;
120 }
121
122 std::scoped_lock l{m_mutex};
123 R_RETURN(ToCalendarTimeImpl(calendar_time, calendar_additional, time, m_my_rule));
124}
125
126Result TimeZone::ParseBinary(LocationName& name, std::span<const u8> binary) {
127 std::scoped_lock l{m_mutex};
128
129 Tz::Rule tmp_rule{};
130 R_TRY(ParseBinaryImpl(tmp_rule, binary));
131
132 m_my_rule = tmp_rule;
133 m_location = name;
134
135 R_SUCCEED();
136}
137
138Result TimeZone::ParseBinaryInto(Tz::Rule& out_rule, std::span<const u8> binary) {
139 std::scoped_lock l{m_mutex};
140 R_RETURN(ParseBinaryImpl(out_rule, binary));
141}
142
143Result TimeZone::ToPosixTime(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
144 CalendarTime& calendar, Tz::Rule& rule) {
145 std::scoped_lock l{m_mutex};
146
147 auto res = ToPosixTimeImpl(out_count, out_times, out_times_count, calendar, rule, -1);
148
149 if (res != ResultSuccess) {
150 if (res == ResultTimeZoneNotFound) {
151 res = ResultSuccess;
152 out_count = 0;
153 }
154 } else if (out_count == 2 && out_times[0] > out_times[1]) {
155 std::swap(out_times[0], out_times[1]);
156 }
157 R_RETURN(res);
158}
159
160Result TimeZone::ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times,
161 u32 out_times_count, CalendarTime& calendar) {
162 std::scoped_lock l{m_mutex};
163
164 auto res = ToPosixTimeImpl(out_count, out_times, out_times_count, calendar, m_my_rule, -1);
165
166 if (res != ResultSuccess) {
167 if (res == ResultTimeZoneNotFound) {
168 res = ResultSuccess;
169 out_count = 0;
170 }
171 } else if (out_count == 2 && out_times[0] > out_times[1]) {
172 std::swap(out_times[0], out_times[1]);
173 }
174 R_RETURN(res);
175}
176
177Result TimeZone::ParseBinaryImpl(Tz::Rule& out_rule, std::span<const u8> binary) {
178 if (Tz::ParseTimeZoneBinary(out_rule, binary)) {
179 R_RETURN(ResultTimeZoneParseFailed);
180 }
181 R_SUCCEED();
182}
183
184Result TimeZone::ToCalendarTimeImpl(CalendarTime& out_calendar_time,
185 CalendarAdditionalInfo& out_additional_info, s64 time,
186 Tz::Rule& rule) {
187 R_TRY(ValidateRule(rule));
188
189 Tz::CalendarTimeInternal calendar_internal{};
190 time_t time_tmp{static_cast<time_t>(time)};
191 if (Tz::localtime_rz(&calendar_internal, &rule, &time_tmp)) {
192 R_RETURN(ResultOverflow);
193 }
194
195 out_calendar_time.year = static_cast<s16>(calendar_internal.tm_year + 1900);
196 out_calendar_time.month = static_cast<s8>(calendar_internal.tm_mon + 1);
197 out_calendar_time.day = static_cast<s8>(calendar_internal.tm_mday);
198 out_calendar_time.hour = static_cast<s8>(calendar_internal.tm_hour);
199 out_calendar_time.minute = static_cast<s8>(calendar_internal.tm_min);
200 out_calendar_time.second = static_cast<s8>(calendar_internal.tm_sec);
201
202 out_additional_info.day_of_week = calendar_internal.tm_wday;
203 out_additional_info.day_of_year = calendar_internal.tm_yday;
204
205 std::memcpy(out_additional_info.name.data(), calendar_internal.tm_zone.data(),
206 out_additional_info.name.size());
207 out_additional_info.name[out_additional_info.name.size() - 1] = '\0';
208
209 out_additional_info.is_dst = calendar_internal.tm_isdst;
210 out_additional_info.ut_offset = calendar_internal.tm_utoff;
211
212 R_SUCCEED();
213}
214
215Result TimeZone::ToPosixTimeImpl(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
216 CalendarTime& calendar, Tz::Rule& rule, s32 is_dst) {
217 R_TRY(ValidateRule(rule));
218
219 calendar.month -= 1;
220 calendar.year -= 1900;
221
222 Tz::CalendarTimeInternal internal{
223 .tm_sec = calendar.second,
224 .tm_min = calendar.minute,
225 .tm_hour = calendar.hour,
226 .tm_mday = calendar.day,
227 .tm_mon = calendar.month,
228 .tm_year = calendar.year,
229 .tm_wday = 0,
230 .tm_yday = 0,
231 .tm_isdst = is_dst,
232 .tm_zone = {},
233 .tm_utoff = 0,
234 .time_index = 0,
235 };
236 time_t time_tmp{};
237 auto res = Tz::mktime_tzname(&time_tmp, &rule, &internal);
238 s64 time = static_cast<s64>(time_tmp);
239
240 if (res == 1) {
241 R_RETURN(ResultOverflow);
242 } else if (res == 2) {
243 R_RETURN(ResultTimeZoneNotFound);
244 }
245
246 if (internal.tm_sec != calendar.second || internal.tm_min != calendar.minute ||
247 internal.tm_hour != calendar.hour || internal.tm_mday != calendar.day ||
248 internal.tm_mon != calendar.month || internal.tm_year != calendar.year) {
249 R_RETURN(ResultTimeZoneNotFound);
250 }
251
252 if (res != 0) {
253 ASSERT(false);
254 }
255
256 out_times[0] = time;
257 if (out_times_count < 2) {
258 out_count = 1;
259 R_SUCCEED();
260 }
261
262 s64 time2{};
263 if (internal.time_index > 0 && GetTimeZoneTime(time2, rule, time, internal.time_index, -1)) {
264 out_times[1] = time2;
265 out_count = 2;
266 R_SUCCEED();
267 }
268
269 if (((internal.time_index + 1) < rule.timecnt) &&
270 GetTimeZoneTime(time2, rule, time, internal.time_index, 1)) {
271 out_times[1] = time2;
272 out_count = 2;
273 R_SUCCEED();
274 }
275
276 out_count = 1;
277 R_SUCCEED();
278}
279
280} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/time_zone.h b/src/core/hle/service/psc/time/time_zone.h
new file mode 100644
index 000000000..ce2acca17
--- /dev/null
+++ b/src/core/hle/service/psc/time/time_zone.h
@@ -0,0 +1,62 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <mutex>
7#include <span>
8
9#include <tz/tz.h>
10#include "core/hle/service/psc/time/common.h"
11
12namespace Service::PSC::Time {
13
14class TimeZone {
15public:
16 TimeZone() = default;
17
18 bool IsInitialized() const {
19 return m_initialized;
20 }
21
22 void SetInitialized() {
23 m_initialized = true;
24 }
25
26 void SetTimePoint(SteadyClockTimePoint& time_point);
27 void SetTotalLocationNameCount(u32 count);
28 void SetRuleVersion(RuleVersion& rule_version);
29 Result GetLocationName(LocationName& out_name);
30 Result GetTotalLocationCount(u32& out_count);
31 Result GetRuleVersion(RuleVersion& out_rule_version);
32 Result GetTimePoint(SteadyClockTimePoint& out_time_point);
33
34 Result ToCalendarTime(CalendarTime& out_calendar_time,
35 CalendarAdditionalInfo& out_additional_info, s64 time, Tz::Rule& rule);
36 Result ToCalendarTimeWithMyRule(CalendarTime& calendar_time,
37 CalendarAdditionalInfo& calendar_additional, s64 time);
38 Result ParseBinary(LocationName& name, std::span<const u8> binary);
39 Result ParseBinaryInto(Tz::Rule& out_rule, std::span<const u8> binary);
40 Result ToPosixTime(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
41 CalendarTime& calendar, Tz::Rule& rule);
42 Result ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
43 CalendarTime& calendar);
44
45private:
46 Result ParseBinaryImpl(Tz::Rule& out_rule, std::span<const u8> binary);
47 Result ToCalendarTimeImpl(CalendarTime& out_calendar_time,
48 CalendarAdditionalInfo& out_additional_info, s64 time,
49 Tz::Rule& rule);
50 Result ToPosixTimeImpl(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
51 CalendarTime& calendar, Tz::Rule& rule, s32 is_dst);
52
53 bool m_initialized{};
54 std::recursive_mutex m_mutex;
55 LocationName m_location{};
56 Tz::Rule m_my_rule{};
57 SteadyClockTimePoint m_steady_clock_time_point{};
58 u32 m_total_location_name_count{};
59 RuleVersion m_rule_version{};
60};
61
62} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/time_zone_service.cpp b/src/core/hle/service/psc/time/time_zone_service.cpp
new file mode 100644
index 000000000..e304c8387
--- /dev/null
+++ b/src/core/hle/service/psc/time/time_zone_service.cpp
@@ -0,0 +1,289 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <tz/tz.h>
5#include "core/core.h"
6#include "core/hle/service/psc/time/time_zone_service.h"
7
8namespace Service::PSC::Time {
9
10TimeZoneService::TimeZoneService(Core::System& system_, StandardSteadyClockCore& clock_core,
11 TimeZone& time_zone, bool can_write_timezone_device_location)
12 : ServiceFramework{system_, "ITimeZoneService"}, m_system{system}, m_clock_core{clock_core},
13 m_time_zone{time_zone}, m_can_write_timezone_device_location{
14 can_write_timezone_device_location} {
15 // clang-format off
16 static const FunctionInfo functions[] = {
17 {0, &TimeZoneService::Handle_GetDeviceLocationName, "GetDeviceLocationName"},
18 {1, &TimeZoneService::Handle_SetDeviceLocationName, "SetDeviceLocationName"},
19 {2, &TimeZoneService::Handle_GetTotalLocationNameCount, "GetTotalLocationNameCount"},
20 {3, &TimeZoneService::Handle_LoadLocationNameList, "LoadLocationNameList"},
21 {4, &TimeZoneService::Handle_LoadTimeZoneRule, "LoadTimeZoneRule"},
22 {5, &TimeZoneService::Handle_GetTimeZoneRuleVersion, "GetTimeZoneRuleVersion"},
23 {6, &TimeZoneService::Handle_GetDeviceLocationNameAndUpdatedTime, "GetDeviceLocationNameAndUpdatedTime"},
24 {7, &TimeZoneService::Handle_SetDeviceLocationNameWithTimeZoneRule, "SetDeviceLocationNameWithTimeZoneRule"},
25 {8, &TimeZoneService::Handle_ParseTimeZoneBinary, "ParseTimeZoneBinary"},
26 {20, &TimeZoneService::Handle_GetDeviceLocationNameOperationEventReadableHandle, "GetDeviceLocationNameOperationEventReadableHandle"},
27 {100, &TimeZoneService::Handle_ToCalendarTime, "ToCalendarTime"},
28 {101, &TimeZoneService::Handle_ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
29 {201, &TimeZoneService::Handle_ToPosixTime, "ToPosixTime"},
30 {202, &TimeZoneService::Handle_ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"},
31 };
32 // clang-format on
33 RegisterHandlers(functions);
34}
35
36void TimeZoneService::Handle_GetDeviceLocationName(HLERequestContext& ctx) {
37 LOG_DEBUG(Service_Time, "called.");
38
39 LocationName name{};
40 auto res = GetDeviceLocationName(name);
41
42 IPC::ResponseBuilder rb{ctx, 2 + sizeof(LocationName) / sizeof(u32)};
43 rb.Push(res);
44 rb.PushRaw<LocationName>(name);
45}
46
47void TimeZoneService::Handle_SetDeviceLocationName(HLERequestContext& ctx) {
48 LOG_DEBUG(Service_Time, "called.");
49
50 IPC::RequestParser rp{ctx};
51 [[maybe_unused]] auto name{rp.PopRaw<LocationName>()};
52
53 if (!m_can_write_timezone_device_location) {
54 IPC::ResponseBuilder rb{ctx, 2};
55 rb.Push(ResultPermissionDenied);
56 return;
57 }
58
59 IPC::ResponseBuilder rb{ctx, 2};
60 rb.Push(ResultNotImplemented);
61}
62
63void TimeZoneService::Handle_GetTotalLocationNameCount(HLERequestContext& ctx) {
64 LOG_DEBUG(Service_Time, "called.");
65
66 u32 count{};
67 auto res = GetTotalLocationNameCount(count);
68
69 IPC::ResponseBuilder rb{ctx, 3};
70 rb.Push(res);
71 rb.Push(count);
72}
73
74void TimeZoneService::Handle_LoadLocationNameList(HLERequestContext& ctx) {
75 LOG_DEBUG(Service_Time, "called.");
76
77 IPC::ResponseBuilder rb{ctx, 2};
78 rb.Push(ResultNotImplemented);
79}
80
81void TimeZoneService::Handle_LoadTimeZoneRule(HLERequestContext& ctx) {
82 LOG_DEBUG(Service_Time, "called.");
83
84 IPC::ResponseBuilder rb{ctx, 2};
85 rb.Push(ResultNotImplemented);
86}
87
88void TimeZoneService::Handle_GetTimeZoneRuleVersion(HLERequestContext& ctx) {
89 LOG_DEBUG(Service_Time, "called.");
90
91 RuleVersion rule_version{};
92 auto res = GetTimeZoneRuleVersion(rule_version);
93
94 IPC::ResponseBuilder rb{ctx, 2 + sizeof(RuleVersion) / sizeof(u32)};
95 rb.Push(res);
96 rb.PushRaw<RuleVersion>(rule_version);
97}
98
99void TimeZoneService::Handle_GetDeviceLocationNameAndUpdatedTime(HLERequestContext& ctx) {
100 LOG_DEBUG(Service_Time, "called.");
101
102 LocationName name{};
103 SteadyClockTimePoint time_point{};
104 auto res = GetDeviceLocationNameAndUpdatedTime(time_point, name);
105
106 IPC::ResponseBuilder rb{ctx, 2 + (sizeof(LocationName) / sizeof(u32)) +
107 (sizeof(SteadyClockTimePoint) / sizeof(u32))};
108 rb.Push(res);
109 rb.PushRaw<LocationName>(name);
110 rb.PushRaw<SteadyClockTimePoint>(time_point);
111}
112
113void TimeZoneService::Handle_SetDeviceLocationNameWithTimeZoneRule(HLERequestContext& ctx) {
114 LOG_DEBUG(Service_Time, "called.");
115
116 IPC::RequestParser rp{ctx};
117 auto name{rp.PopRaw<LocationName>()};
118
119 auto binary{ctx.ReadBuffer()};
120 auto res = SetDeviceLocationNameWithTimeZoneRule(name, binary);
121
122 IPC::ResponseBuilder rb{ctx, 2};
123 rb.Push(res);
124}
125
126void TimeZoneService::Handle_ParseTimeZoneBinary(HLERequestContext& ctx) {
127 LOG_DEBUG(Service_Time, "called.");
128
129 auto binary{ctx.ReadBuffer()};
130
131 Tz::Rule rule{};
132 auto res = ParseTimeZoneBinary(rule, binary);
133
134 ctx.WriteBuffer(rule);
135
136 IPC::ResponseBuilder rb{ctx, 2};
137 rb.Push(res);
138}
139
140void TimeZoneService::Handle_GetDeviceLocationNameOperationEventReadableHandle(
141 HLERequestContext& ctx) {
142 LOG_DEBUG(Service_Time, "called.");
143
144 IPC::ResponseBuilder rb{ctx, 2};
145 rb.Push(ResultNotImplemented);
146}
147
148void TimeZoneService::Handle_ToCalendarTime(HLERequestContext& ctx) {
149 LOG_DEBUG(Service_Time, "called.");
150
151 IPC::RequestParser rp{ctx};
152 auto time{rp.Pop<s64>()};
153
154 auto rule_buffer{ctx.ReadBuffer()};
155 Tz::Rule rule{};
156 std::memcpy(&rule, rule_buffer.data(), sizeof(Tz::Rule));
157
158 CalendarTime calendar_time{};
159 CalendarAdditionalInfo additional_info{};
160 auto res = ToCalendarTime(calendar_time, additional_info, time, rule);
161
162 IPC::ResponseBuilder rb{ctx, 2 + (sizeof(CalendarTime) / sizeof(u32)) +
163 (sizeof(CalendarAdditionalInfo) / sizeof(u32))};
164 rb.Push(res);
165 rb.PushRaw<CalendarTime>(calendar_time);
166 rb.PushRaw<CalendarAdditionalInfo>(additional_info);
167}
168
169void TimeZoneService::Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx) {
170 LOG_DEBUG(Service_Time, "called.");
171
172 IPC::RequestParser rp{ctx};
173 auto time{rp.Pop<s64>()};
174
175 CalendarTime calendar_time{};
176 CalendarAdditionalInfo additional_info{};
177 auto res = ToCalendarTimeWithMyRule(calendar_time, additional_info, time);
178
179 IPC::ResponseBuilder rb{ctx, 2 + (sizeof(CalendarTime) / sizeof(u32)) +
180 (sizeof(CalendarAdditionalInfo) / sizeof(u32))};
181 rb.Push(res);
182 rb.PushRaw<CalendarTime>(calendar_time);
183 rb.PushRaw<CalendarAdditionalInfo>(additional_info);
184}
185
186void TimeZoneService::Handle_ToPosixTime(HLERequestContext& ctx) {
187 LOG_DEBUG(Service_Time, "called.");
188
189 IPC::RequestParser rp{ctx};
190 auto calendar{rp.PopRaw<CalendarTime>()};
191
192 auto binary{ctx.ReadBuffer()};
193
194 Tz::Rule rule{};
195 std::memcpy(&rule, binary.data(), sizeof(Tz::Rule));
196
197 u32 count{};
198 std::array<s64, 2> times{};
199 u32 times_count{static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(s64))};
200
201 auto res = ToPosixTime(count, times, times_count, calendar, rule);
202
203 ctx.WriteBuffer(times);
204
205 IPC::ResponseBuilder rb{ctx, 3};
206 rb.Push(res);
207 rb.Push(count);
208}
209
210void TimeZoneService::Handle_ToPosixTimeWithMyRule(HLERequestContext& ctx) {
211 LOG_DEBUG(Service_Time, "called.");
212
213 IPC::RequestParser rp{ctx};
214 auto calendar{rp.PopRaw<CalendarTime>()};
215
216 u32 count{};
217 std::array<s64, 2> times{};
218 u32 times_count{static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(s64))};
219
220 auto res = ToPosixTimeWithMyRule(count, times, times_count, calendar);
221
222 ctx.WriteBuffer(times);
223
224 IPC::ResponseBuilder rb{ctx, 3};
225 rb.Push(res);
226 rb.Push(count);
227}
228
229// =============================== Implementations ===========================
230
231Result TimeZoneService::GetDeviceLocationName(LocationName& out_location_name) {
232 R_RETURN(m_time_zone.GetLocationName(out_location_name));
233}
234
235Result TimeZoneService::GetTotalLocationNameCount(u32& out_count) {
236 R_RETURN(m_time_zone.GetTotalLocationCount(out_count));
237}
238
239Result TimeZoneService::GetTimeZoneRuleVersion(RuleVersion& out_rule_version) {
240 R_RETURN(m_time_zone.GetRuleVersion(out_rule_version));
241}
242
243Result TimeZoneService::GetDeviceLocationNameAndUpdatedTime(SteadyClockTimePoint& out_time_point,
244 LocationName& location_name) {
245 R_TRY(m_time_zone.GetLocationName(location_name));
246 R_RETURN(m_time_zone.GetTimePoint(out_time_point));
247}
248
249Result TimeZoneService::SetDeviceLocationNameWithTimeZoneRule(LocationName& location_name,
250 std::span<const u8> binary) {
251 R_UNLESS(m_can_write_timezone_device_location, ResultPermissionDenied);
252 R_TRY(m_time_zone.ParseBinary(location_name, binary));
253
254 SteadyClockTimePoint time_point{};
255 R_TRY(m_clock_core.GetCurrentTimePoint(time_point));
256
257 m_time_zone.SetTimePoint(time_point);
258 R_SUCCEED();
259}
260
261Result TimeZoneService::ParseTimeZoneBinary(Tz::Rule& out_rule, std::span<const u8> binary) {
262 R_RETURN(m_time_zone.ParseBinaryInto(out_rule, binary));
263}
264
265Result TimeZoneService::ToCalendarTime(CalendarTime& out_calendar_time,
266 CalendarAdditionalInfo& out_additional_info, s64 time,
267 Tz::Rule& rule) {
268 R_RETURN(m_time_zone.ToCalendarTime(out_calendar_time, out_additional_info, time, rule));
269}
270
271Result TimeZoneService::ToCalendarTimeWithMyRule(CalendarTime& out_calendar_time,
272 CalendarAdditionalInfo& out_additional_info,
273 s64 time) {
274 R_RETURN(m_time_zone.ToCalendarTimeWithMyRule(out_calendar_time, out_additional_info, time));
275}
276
277Result TimeZoneService::ToPosixTime(u32& out_count, std::span<s64, 2> out_times,
278 u32 out_times_count, CalendarTime& calendar_time,
279 Tz::Rule& rule) {
280 R_RETURN(m_time_zone.ToPosixTime(out_count, out_times, out_times_count, calendar_time, rule));
281}
282
283Result TimeZoneService::ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times,
284 u32 out_times_count, CalendarTime& calendar_time) {
285 R_RETURN(
286 m_time_zone.ToPosixTimeWithMyRule(out_count, out_times, out_times_count, calendar_time));
287}
288
289} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/time_zone_service.h b/src/core/hle/service/psc/time/time_zone_service.h
new file mode 100644
index 000000000..074c1d4ae
--- /dev/null
+++ b/src/core/hle/service/psc/time/time_zone_service.h
@@ -0,0 +1,69 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/ipc_helpers.h"
7#include "core/hle/service/psc/time/common.h"
8#include "core/hle/service/psc/time/manager.h"
9#include "core/hle/service/server_manager.h"
10#include "core/hle/service/service.h"
11
12namespace Core {
13class System;
14}
15
16namespace Tz {
17struct Rule;
18}
19
20namespace Service::PSC::Time {
21
22class TimeZoneService final : public ServiceFramework<TimeZoneService> {
23public:
24 explicit TimeZoneService(Core::System& system, StandardSteadyClockCore& clock_core,
25 TimeZone& time_zone, bool can_write_timezone_device_location);
26
27 ~TimeZoneService() override = default;
28
29 Result GetDeviceLocationName(LocationName& out_location_name);
30 Result GetTotalLocationNameCount(u32& out_count);
31 Result GetTimeZoneRuleVersion(RuleVersion& out_rule_version);
32 Result GetDeviceLocationNameAndUpdatedTime(SteadyClockTimePoint& out_time_point,
33 LocationName& location_name);
34 Result SetDeviceLocationNameWithTimeZoneRule(LocationName& location_name,
35 std::span<const u8> binary);
36 Result ParseTimeZoneBinary(Tz::Rule& out_rule, std::span<const u8> binary);
37 Result ToCalendarTime(CalendarTime& out_calendar_time,
38 CalendarAdditionalInfo& out_additional_info, s64 time, Tz::Rule& rule);
39 Result ToCalendarTimeWithMyRule(CalendarTime& out_calendar_time,
40 CalendarAdditionalInfo& out_additional_info, s64 time);
41 Result ToPosixTime(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
42 CalendarTime& calendar_time, Tz::Rule& rule);
43 Result ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
44 CalendarTime& calendar_time);
45
46private:
47 void Handle_GetDeviceLocationName(HLERequestContext& ctx);
48 void Handle_SetDeviceLocationName(HLERequestContext& ctx);
49 void Handle_GetTotalLocationNameCount(HLERequestContext& ctx);
50 void Handle_LoadLocationNameList(HLERequestContext& ctx);
51 void Handle_LoadTimeZoneRule(HLERequestContext& ctx);
52 void Handle_GetTimeZoneRuleVersion(HLERequestContext& ctx);
53 void Handle_GetDeviceLocationNameAndUpdatedTime(HLERequestContext& ctx);
54 void Handle_SetDeviceLocationNameWithTimeZoneRule(HLERequestContext& ctx);
55 void Handle_ParseTimeZoneBinary(HLERequestContext& ctx);
56 void Handle_GetDeviceLocationNameOperationEventReadableHandle(HLERequestContext& ctx);
57 void Handle_ToCalendarTime(HLERequestContext& ctx);
58 void Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx);
59 void Handle_ToPosixTime(HLERequestContext& ctx);
60 void Handle_ToPosixTimeWithMyRule(HLERequestContext& ctx);
61
62 Core::System& m_system;
63
64 StandardSteadyClockCore& m_clock_core;
65 TimeZone& m_time_zone;
66 bool m_can_write_timezone_device_location;
67};
68
69} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 39124c5fd..06cbad268 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -66,7 +66,6 @@
66#include "core/hle/service/sockets/sockets.h" 66#include "core/hle/service/sockets/sockets.h"
67#include "core/hle/service/spl/spl_module.h" 67#include "core/hle/service/spl/spl_module.h"
68#include "core/hle/service/ssl/ssl.h" 68#include "core/hle/service/ssl/ssl.h"
69#include "core/hle/service/time/time.h"
70#include "core/hle/service/usb/usb.h" 69#include "core/hle/service/usb/usb.h"
71#include "core/hle/service/vi/vi.h" 70#include "core/hle/service/vi/vi.h"
72#include "core/reporter.h" 71#include "core/reporter.h"
@@ -246,6 +245,9 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
246 kernel.RunOnGuestCoreProcess("fatal", [&] { Fatal::LoopProcess(system); }); 245 kernel.RunOnGuestCoreProcess("fatal", [&] { Fatal::LoopProcess(system); });
247 kernel.RunOnGuestCoreProcess("fgm", [&] { FGM::LoopProcess(system); }); 246 kernel.RunOnGuestCoreProcess("fgm", [&] { FGM::LoopProcess(system); });
248 kernel.RunOnGuestCoreProcess("friends", [&] { Friend::LoopProcess(system); }); 247 kernel.RunOnGuestCoreProcess("friends", [&] { Friend::LoopProcess(system); });
248 // glue depends on settings and psc, so they must come first
249 kernel.RunOnGuestCoreProcess("settings", [&] { Set::LoopProcess(system); });
250 kernel.RunOnGuestCoreProcess("psc", [&] { PSC::LoopProcess(system); });
249 kernel.RunOnGuestCoreProcess("glue", [&] { Glue::LoopProcess(system); }); 251 kernel.RunOnGuestCoreProcess("glue", [&] { Glue::LoopProcess(system); });
250 kernel.RunOnGuestCoreProcess("grc", [&] { GRC::LoopProcess(system); }); 252 kernel.RunOnGuestCoreProcess("grc", [&] { GRC::LoopProcess(system); });
251 kernel.RunOnGuestCoreProcess("hid", [&] { HID::LoopProcess(system); }); 253 kernel.RunOnGuestCoreProcess("hid", [&] { HID::LoopProcess(system); });
@@ -269,13 +271,10 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
269 kernel.RunOnGuestCoreProcess("pcv", [&] { PCV::LoopProcess(system); }); 271 kernel.RunOnGuestCoreProcess("pcv", [&] { PCV::LoopProcess(system); });
270 kernel.RunOnGuestCoreProcess("prepo", [&] { PlayReport::LoopProcess(system); }); 272 kernel.RunOnGuestCoreProcess("prepo", [&] { PlayReport::LoopProcess(system); });
271 kernel.RunOnGuestCoreProcess("ProcessManager", [&] { PM::LoopProcess(system); }); 273 kernel.RunOnGuestCoreProcess("ProcessManager", [&] { PM::LoopProcess(system); });
272 kernel.RunOnGuestCoreProcess("psc", [&] { PSC::LoopProcess(system); });
273 kernel.RunOnGuestCoreProcess("ptm", [&] { PTM::LoopProcess(system); }); 274 kernel.RunOnGuestCoreProcess("ptm", [&] { PTM::LoopProcess(system); });
274 kernel.RunOnGuestCoreProcess("ro", [&] { RO::LoopProcess(system); }); 275 kernel.RunOnGuestCoreProcess("ro", [&] { RO::LoopProcess(system); });
275 kernel.RunOnGuestCoreProcess("settings", [&] { Set::LoopProcess(system); });
276 kernel.RunOnGuestCoreProcess("spl", [&] { SPL::LoopProcess(system); }); 276 kernel.RunOnGuestCoreProcess("spl", [&] { SPL::LoopProcess(system); });
277 kernel.RunOnGuestCoreProcess("ssl", [&] { SSL::LoopProcess(system); }); 277 kernel.RunOnGuestCoreProcess("ssl", [&] { SSL::LoopProcess(system); });
278 kernel.RunOnGuestCoreProcess("time", [&] { Time::LoopProcess(system); });
279 kernel.RunOnGuestCoreProcess("usb", [&] { USB::LoopProcess(system); }); 278 kernel.RunOnGuestCoreProcess("usb", [&] { USB::LoopProcess(system); });
280 // clang-format on 279 // clang-format on
281} 280}
diff --git a/src/core/hle/service/set/private_settings.h b/src/core/hle/service/set/private_settings.h
new file mode 100644
index 000000000..b02291ce7
--- /dev/null
+++ b/src/core/hle/service/set/private_settings.h
@@ -0,0 +1,72 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7
8#include "common/bit_field.h"
9#include "common/common_funcs.h"
10#include "common/common_types.h"
11#include "common/uuid.h"
12#include "core/hle/service/psc/time/common.h"
13
14namespace Service::Set {
15
16/// This is nn::settings::system::InitialLaunchFlag
17struct InitialLaunchFlag {
18 union {
19 u32 raw{};
20
21 BitField<0, 1, u32> InitialLaunchCompletionFlag;
22 BitField<8, 1, u32> InitialLaunchUserAdditionFlag;
23 BitField<16, 1, u32> InitialLaunchTimestampFlag;
24 };
25};
26static_assert(sizeof(InitialLaunchFlag) == 4, "InitialLaunchFlag is an invalid size");
27
28/// This is nn::settings::system::InitialLaunchSettings
29struct InitialLaunchSettings {
30 InitialLaunchFlag flags;
31 INSERT_PADDING_BYTES(0x4);
32 Service::PSC::Time::SteadyClockTimePoint timestamp;
33};
34static_assert(sizeof(InitialLaunchSettings) == 0x20, "InitialLaunchSettings is incorrect size");
35
36#pragma pack(push, 4)
37struct InitialLaunchSettingsPacked {
38 InitialLaunchFlag flags;
39 Service::PSC::Time::SteadyClockTimePoint timestamp;
40};
41#pragma pack(pop)
42static_assert(sizeof(InitialLaunchSettingsPacked) == 0x1C,
43 "InitialLaunchSettingsPacked is incorrect size");
44
45struct PrivateSettings {
46 std::array<u8, 0x10> reserved_00;
47
48 // nn::settings::system::InitialLaunchSettings
49 InitialLaunchSettings initial_launch_settings;
50
51 std::array<u8, 0x20> reserved_30;
52
53 Common::UUID external_clock_source_id;
54 s64 shutdown_rtc_value;
55 s64 external_steady_clock_internal_offset;
56
57 std::array<u8, 0x60> reserved_70;
58
59 // nn::settings::system::PlatformRegion
60 std::array<u8, 0x4> platform_region;
61
62 std::array<u8, 0x4> reserved_D4;
63};
64static_assert(offsetof(PrivateSettings, initial_launch_settings) == 0x10);
65static_assert(offsetof(PrivateSettings, external_clock_source_id) == 0x50);
66static_assert(offsetof(PrivateSettings, reserved_70) == 0x70);
67static_assert(offsetof(PrivateSettings, platform_region) == 0xD0);
68static_assert(sizeof(PrivateSettings) == 0xD8, "PrivateSettings has the wrong size!");
69
70PrivateSettings DefaultPrivateSettings();
71
72} // namespace Service::Set
diff --git a/src/core/hle/service/set/setting_formats/private_settings.h b/src/core/hle/service/set/setting_formats/private_settings.h
index 6c40f62e1..6579e95e0 100644
--- a/src/core/hle/service/set/setting_formats/private_settings.h
+++ b/src/core/hle/service/set/setting_formats/private_settings.h
@@ -8,7 +8,6 @@
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/uuid.h" 9#include "common/uuid.h"
10#include "core/hle/service/set/settings_types.h" 10#include "core/hle/service/set/settings_types.h"
11#include "core/hle/service/time/clock_types.h"
12 11
13namespace Service::Set { 12namespace Service::Set {
14 13
diff --git a/src/core/hle/service/set/setting_formats/system_settings.cpp b/src/core/hle/service/set/setting_formats/system_settings.cpp
index 66e57651e..ec00b90a6 100644
--- a/src/core/hle/service/set/setting_formats/system_settings.cpp
+++ b/src/core/hle/service/set/setting_formats/system_settings.cpp
@@ -45,7 +45,7 @@ SystemSettings DefaultSystemSettings() {
45 }; 45 };
46 46
47 settings.device_time_zone_location_name = {"UTC"}; 47 settings.device_time_zone_location_name = {"UTC"};
48 settings.user_system_clock_automatic_correction_enabled = false; 48 settings.user_system_clock_automatic_correction_enabled = true;
49 49
50 settings.primary_album_storage = PrimaryAlbumStorage::SdCard; 50 settings.primary_album_storage = PrimaryAlbumStorage::SdCard;
51 settings.battery_percentage_flag = true; 51 settings.battery_percentage_flag = true;
diff --git a/src/core/hle/service/set/setting_formats/system_settings.h b/src/core/hle/service/set/setting_formats/system_settings.h
index 14654f8b1..af5929fa9 100644
--- a/src/core/hle/service/set/setting_formats/system_settings.h
+++ b/src/core/hle/service/set/setting_formats/system_settings.h
@@ -12,7 +12,6 @@
12#include "common/vector_math.h" 12#include "common/vector_math.h"
13#include "core/hle/service/set/setting_formats/private_settings.h" 13#include "core/hle/service/set/setting_formats/private_settings.h"
14#include "core/hle/service/set/settings_types.h" 14#include "core/hle/service/set/settings_types.h"
15#include "core/hle/service/time/clock_types.h"
16 15
17namespace Service::Set { 16namespace Service::Set {
18 17
@@ -197,12 +196,14 @@ struct SystemSettings {
197 std::array<u8, 0x2C> backlight_settings_mixed_up; 196 std::array<u8, 0x2C> backlight_settings_mixed_up;
198 INSERT_PADDING_BYTES(0x64); // Reserved 197 INSERT_PADDING_BYTES(0x64); // Reserved
199 198
200 Service::Time::Clock::SystemClockContext user_system_clock_context; 199 // nn::time::SystemClockContext
201 Service::Time::Clock::SystemClockContext network_system_clock_context; 200 Service::PSC::Time::SystemClockContext user_system_clock_context;
201 Service::PSC::Time::SystemClockContext network_system_clock_context;
202 bool user_system_clock_automatic_correction_enabled; 202 bool user_system_clock_automatic_correction_enabled;
203 INSERT_PADDING_BYTES(0x3); 203 INSERT_PADDING_BYTES(0x3);
204 INSERT_PADDING_BYTES(0x4); // Reserved 204 INSERT_PADDING_BYTES(0x4); // Reserved
205 Service::Time::Clock::SteadyClockTimePoint 205 // nn::time::SteadyClockTimePoint
206 Service::PSC::Time::SteadyClockTimePoint
206 user_system_clock_automatic_correction_updated_time_point; 207 user_system_clock_automatic_correction_updated_time_point;
207 INSERT_PADDING_BYTES(0x10); // Reserved 208 INSERT_PADDING_BYTES(0x10); // Reserved
208 209
@@ -280,9 +281,12 @@ struct SystemSettings {
280 bool requires_run_repair_time_reviser; 281 bool requires_run_repair_time_reviser;
281 INSERT_PADDING_BYTES(0x6B); // Reserved 282 INSERT_PADDING_BYTES(0x6B); // Reserved
282 283
283 Service::Time::TimeZone::LocationName device_time_zone_location_name; 284 // nn::time::LocationName
285 Service::PSC::Time::LocationName device_time_zone_location_name;
284 INSERT_PADDING_BYTES(0x4); // Reserved 286 INSERT_PADDING_BYTES(0x4); // Reserved
285 Service::Time::Clock::SteadyClockTimePoint device_time_zone_location_updated_time; 287 // nn::time::SteadyClockTimePoint
288 Service::PSC::Time::SteadyClockTimePoint device_time_zone_location_updated_time;
289
286 INSERT_PADDING_BYTES(0xC0); // Reserved 290 INSERT_PADDING_BYTES(0xC0); // Reserved
287 291
288 // nn::settings::system::PrimaryAlbumStorage 292 // nn::settings::system::PrimaryAlbumStorage
diff --git a/src/core/hle/service/set/settings_types.h b/src/core/hle/service/set/settings_types.h
index 4dee202d7..f6f227fde 100644
--- a/src/core/hle/service/set/settings_types.h
+++ b/src/core/hle/service/set/settings_types.h
@@ -9,7 +9,7 @@
9#include "common/common_funcs.h" 9#include "common/common_funcs.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/uuid.h" 11#include "common/uuid.h"
12#include "core/hle/service/time/clock_types.h" 12#include "core/hle/service/psc/time/common.h"
13 13
14namespace Service::Set { 14namespace Service::Set {
15 15
@@ -365,7 +365,7 @@ struct EulaVersion {
365 EulaVersionClockType clock_type; 365 EulaVersionClockType clock_type;
366 INSERT_PADDING_BYTES(0x4); 366 INSERT_PADDING_BYTES(0x4);
367 s64 posix_time; 367 s64 posix_time;
368 Time::Clock::SteadyClockTimePoint timestamp; 368 Service::PSC::Time::SteadyClockTimePoint timestamp;
369}; 369};
370static_assert(sizeof(EulaVersion) == 0x30, "EulaVersion is incorrect size"); 370static_assert(sizeof(EulaVersion) == 0x30, "EulaVersion is incorrect size");
371 371
@@ -398,14 +398,14 @@ static_assert(sizeof(HomeMenuScheme) == 0x14, "HomeMenuScheme is incorrect size"
398struct InitialLaunchSettings { 398struct InitialLaunchSettings {
399 InitialLaunchFlag flags; 399 InitialLaunchFlag flags;
400 INSERT_PADDING_BYTES(0x4); 400 INSERT_PADDING_BYTES(0x4);
401 Service::Time::Clock::SteadyClockTimePoint timestamp; 401 Service::PSC::Time::SteadyClockTimePoint timestamp;
402}; 402};
403static_assert(sizeof(InitialLaunchSettings) == 0x20, "InitialLaunchSettings is incorrect size"); 403static_assert(sizeof(InitialLaunchSettings) == 0x20, "InitialLaunchSettings is incorrect size");
404 404
405#pragma pack(push, 4) 405#pragma pack(push, 4)
406struct InitialLaunchSettingsPacked { 406struct InitialLaunchSettingsPacked {
407 InitialLaunchFlag flags; 407 InitialLaunchFlag flags;
408 Service::Time::Clock::SteadyClockTimePoint timestamp; 408 Service::PSC::Time::SteadyClockTimePoint timestamp;
409}; 409};
410#pragma pack(pop) 410#pragma pack(pop)
411static_assert(sizeof(InitialLaunchSettingsPacked) == 0x1C, 411static_assert(sizeof(InitialLaunchSettingsPacked) == 0x1C,
diff --git a/src/core/hle/service/set/system_settings_server.cpp b/src/core/hle/service/set/system_settings_server.cpp
index 429e96d11..f40a1c8f3 100644
--- a/src/core/hle/service/set/system_settings_server.cpp
+++ b/src/core/hle/service/set/system_settings_server.cpp
@@ -489,11 +489,10 @@ void ISystemSettingsServer::SetExternalSteadyClockSourceId(HLERequestContext& ct
489void ISystemSettingsServer::GetUserSystemClockContext(HLERequestContext& ctx) { 489void ISystemSettingsServer::GetUserSystemClockContext(HLERequestContext& ctx) {
490 LOG_INFO(Service_SET, "called"); 490 LOG_INFO(Service_SET, "called");
491 491
492 Service::Time::Clock::SystemClockContext context{}; 492 Service::PSC::Time::SystemClockContext context{};
493 auto res = GetUserSystemClockContext(context); 493 auto res = GetUserSystemClockContext(context);
494 494
495 IPC::ResponseBuilder rb{ctx, 495 IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::SystemClockContext) / sizeof(u32)};
496 2 + sizeof(Service::Time::Clock::SystemClockContext) / sizeof(u32)};
497 rb.Push(res); 496 rb.Push(res);
498 rb.PushRaw(context); 497 rb.PushRaw(context);
499} 498}
@@ -502,7 +501,7 @@ void ISystemSettingsServer::SetUserSystemClockContext(HLERequestContext& ctx) {
502 LOG_INFO(Service_SET, "called"); 501 LOG_INFO(Service_SET, "called");
503 502
504 IPC::RequestParser rp{ctx}; 503 IPC::RequestParser rp{ctx};
505 auto context{rp.PopRaw<Service::Time::Clock::SystemClockContext>()}; 504 auto context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()};
506 505
507 auto res = SetUserSystemClockContext(context); 506 auto res = SetUserSystemClockContext(context);
508 507
@@ -809,19 +808,19 @@ void ISystemSettingsServer::GetQuestFlag(HLERequestContext& ctx) {
809void ISystemSettingsServer::GetDeviceTimeZoneLocationName(HLERequestContext& ctx) { 808void ISystemSettingsServer::GetDeviceTimeZoneLocationName(HLERequestContext& ctx) {
810 LOG_INFO(Service_SET, "called"); 809 LOG_INFO(Service_SET, "called");
811 810
812 Service::Time::TimeZone::LocationName name{}; 811 Service::PSC::Time::LocationName name{};
813 auto res = GetDeviceTimeZoneLocationName(name); 812 auto res = GetDeviceTimeZoneLocationName(name);
814 813
815 IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::Time::TimeZone::LocationName) / sizeof(u32)}; 814 IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::LocationName) / sizeof(u32)};
816 rb.Push(res); 815 rb.Push(res);
817 rb.PushRaw<Service::Time::TimeZone::LocationName>(name); 816 rb.PushRaw<Service::PSC::Time::LocationName>(name);
818} 817}
819 818
820void ISystemSettingsServer::SetDeviceTimeZoneLocationName(HLERequestContext& ctx) { 819void ISystemSettingsServer::SetDeviceTimeZoneLocationName(HLERequestContext& ctx) {
821 LOG_INFO(Service_SET, "called"); 820 LOG_INFO(Service_SET, "called");
822 821
823 IPC::RequestParser rp{ctx}; 822 IPC::RequestParser rp{ctx};
824 auto name{rp.PopRaw<Service::Time::TimeZone::LocationName>()}; 823 auto name{rp.PopRaw<Service::PSC::Time::LocationName>()};
825 824
826 auto res = SetDeviceTimeZoneLocationName(name); 825 auto res = SetDeviceTimeZoneLocationName(name);
827 826
@@ -843,11 +842,10 @@ void ISystemSettingsServer::SetRegionCode(HLERequestContext& ctx) {
843void ISystemSettingsServer::GetNetworkSystemClockContext(HLERequestContext& ctx) { 842void ISystemSettingsServer::GetNetworkSystemClockContext(HLERequestContext& ctx) {
844 LOG_INFO(Service_SET, "called"); 843 LOG_INFO(Service_SET, "called");
845 844
846 Service::Time::Clock::SystemClockContext context{}; 845 Service::PSC::Time::SystemClockContext context{};
847 auto res = GetNetworkSystemClockContext(context); 846 auto res = GetNetworkSystemClockContext(context);
848 847
849 IPC::ResponseBuilder rb{ctx, 848 IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::SystemClockContext) / sizeof(u32)};
850 2 + sizeof(Service::Time::Clock::SystemClockContext) / sizeof(u32)};
851 rb.Push(res); 849 rb.Push(res);
852 rb.PushRaw(context); 850 rb.PushRaw(context);
853} 851}
@@ -856,7 +854,7 @@ void ISystemSettingsServer::SetNetworkSystemClockContext(HLERequestContext& ctx)
856 LOG_INFO(Service_SET, "called"); 854 LOG_INFO(Service_SET, "called");
857 855
858 IPC::RequestParser rp{ctx}; 856 IPC::RequestParser rp{ctx};
859 auto context{rp.PopRaw<Service::Time::Clock::SystemClockContext>()}; 857 auto context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()};
860 858
861 auto res = SetNetworkSystemClockContext(context); 859 auto res = SetNetworkSystemClockContext(context);
862 860
@@ -1141,19 +1139,19 @@ void ISystemSettingsServer::GetKeyboardLayout(HLERequestContext& ctx) {
1141void ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) { 1139void ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) {
1142 LOG_INFO(Service_SET, "called"); 1140 LOG_INFO(Service_SET, "called");
1143 1141
1144 Service::Time::Clock::SteadyClockTimePoint time_point{}; 1142 Service::PSC::Time::SteadyClockTimePoint time_point{};
1145 auto res = GetDeviceTimeZoneLocationUpdatedTime(time_point); 1143 auto res = GetDeviceTimeZoneLocationUpdatedTime(time_point);
1146 1144
1147 IPC::ResponseBuilder rb{ctx, 4}; 1145 IPC::ResponseBuilder rb{ctx, 4};
1148 rb.Push(res); 1146 rb.Push(res);
1149 rb.PushRaw<Service::Time::Clock::SteadyClockTimePoint>(time_point); 1147 rb.PushRaw<Service::PSC::Time::SteadyClockTimePoint>(time_point);
1150} 1148}
1151 1149
1152void ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) { 1150void ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) {
1153 LOG_INFO(Service_SET, "called"); 1151 LOG_INFO(Service_SET, "called");
1154 1152
1155 IPC::RequestParser rp{ctx}; 1153 IPC::RequestParser rp{ctx};
1156 auto time_point{rp.PopRaw<Service::Time::Clock::SteadyClockTimePoint>()}; 1154 auto time_point{rp.PopRaw<Service::PSC::Time::SteadyClockTimePoint>()};
1157 1155
1158 auto res = SetDeviceTimeZoneLocationUpdatedTime(time_point); 1156 auto res = SetDeviceTimeZoneLocationUpdatedTime(time_point);
1159 1157
@@ -1165,12 +1163,12 @@ void ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime(
1165 HLERequestContext& ctx) { 1163 HLERequestContext& ctx) {
1166 LOG_INFO(Service_SET, "called"); 1164 LOG_INFO(Service_SET, "called");
1167 1165
1168 Service::Time::Clock::SteadyClockTimePoint time_point{}; 1166 Service::PSC::Time::SteadyClockTimePoint time_point{};
1169 auto res = GetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); 1167 auto res = GetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
1170 1168
1171 IPC::ResponseBuilder rb{ctx, 4}; 1169 IPC::ResponseBuilder rb{ctx, 4};
1172 rb.Push(res); 1170 rb.Push(res);
1173 rb.PushRaw<Service::Time::Clock::SteadyClockTimePoint>(time_point); 1171 rb.PushRaw<Service::PSC::Time::SteadyClockTimePoint>(time_point);
1174} 1172}
1175 1173
1176void ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime( 1174void ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime(
@@ -1178,7 +1176,7 @@ void ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime(
1178 LOG_INFO(Service_SET, "called"); 1176 LOG_INFO(Service_SET, "called");
1179 1177
1180 IPC::RequestParser rp{ctx}; 1178 IPC::RequestParser rp{ctx};
1181 auto time_point{rp.PopRaw<Service::Time::Clock::SteadyClockTimePoint>()}; 1179 auto time_point{rp.PopRaw<Service::PSC::Time::SteadyClockTimePoint>()};
1182 1180
1183 auto res = SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); 1181 auto res = SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
1184 1182
@@ -1257,25 +1255,25 @@ void ISystemSettingsServer::StoreSettings() {
1257 auto system_dir = 1255 auto system_dir =
1258 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000050"; 1256 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000050";
1259 if (!StoreSettingsFile(system_dir, m_system_settings)) { 1257 if (!StoreSettingsFile(system_dir, m_system_settings)) {
1260 LOG_ERROR(HW_GPU, "Failed to store System settings"); 1258 LOG_ERROR(Service_SET, "Failed to store System settings");
1261 } 1259 }
1262 1260
1263 auto private_dir = 1261 auto private_dir =
1264 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000052"; 1262 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000052";
1265 if (!StoreSettingsFile(private_dir, m_private_settings)) { 1263 if (!StoreSettingsFile(private_dir, m_private_settings)) {
1266 LOG_ERROR(HW_GPU, "Failed to store Private settings"); 1264 LOG_ERROR(Service_SET, "Failed to store Private settings");
1267 } 1265 }
1268 1266
1269 auto device_dir = 1267 auto device_dir =
1270 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000053"; 1268 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000053";
1271 if (!StoreSettingsFile(device_dir, m_device_settings)) { 1269 if (!StoreSettingsFile(device_dir, m_device_settings)) {
1272 LOG_ERROR(HW_GPU, "Failed to store Device settings"); 1270 LOG_ERROR(Service_SET, "Failed to store Device settings");
1273 } 1271 }
1274 1272
1275 auto appln_dir = 1273 auto appln_dir =
1276 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000054"; 1274 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000054";
1277 if (!StoreSettingsFile(appln_dir, m_appln_settings)) { 1275 if (!StoreSettingsFile(appln_dir, m_appln_settings)) {
1278 LOG_ERROR(HW_GPU, "Failed to store ApplLn settings"); 1276 LOG_ERROR(Service_SET, "Failed to store ApplLn settings");
1279 } 1277 }
1280} 1278}
1281 1279
@@ -1318,39 +1316,39 @@ Result ISystemSettingsServer::SetExternalSteadyClockSourceId(Common::UUID id) {
1318} 1316}
1319 1317
1320Result ISystemSettingsServer::GetUserSystemClockContext( 1318Result ISystemSettingsServer::GetUserSystemClockContext(
1321 Service::Time::Clock::SystemClockContext& out_context) { 1319 Service::PSC::Time::SystemClockContext& out_context) {
1322 out_context = m_system_settings.user_system_clock_context; 1320 out_context = m_system_settings.user_system_clock_context;
1323 R_SUCCEED(); 1321 R_SUCCEED();
1324} 1322}
1325 1323
1326Result ISystemSettingsServer::SetUserSystemClockContext( 1324Result ISystemSettingsServer::SetUserSystemClockContext(
1327 Service::Time::Clock::SystemClockContext& context) { 1325 Service::PSC::Time::SystemClockContext& context) {
1328 m_system_settings.user_system_clock_context = context; 1326 m_system_settings.user_system_clock_context = context;
1329 SetSaveNeeded(); 1327 SetSaveNeeded();
1330 R_SUCCEED(); 1328 R_SUCCEED();
1331} 1329}
1332 1330
1333Result ISystemSettingsServer::GetDeviceTimeZoneLocationName( 1331Result ISystemSettingsServer::GetDeviceTimeZoneLocationName(
1334 Service::Time::TimeZone::LocationName& out_name) { 1332 Service::PSC::Time::LocationName& out_name) {
1335 out_name = m_system_settings.device_time_zone_location_name; 1333 out_name = m_system_settings.device_time_zone_location_name;
1336 R_SUCCEED(); 1334 R_SUCCEED();
1337} 1335}
1338 1336
1339Result ISystemSettingsServer::SetDeviceTimeZoneLocationName( 1337Result ISystemSettingsServer::SetDeviceTimeZoneLocationName(
1340 Service::Time::TimeZone::LocationName& name) { 1338 Service::PSC::Time::LocationName& name) {
1341 m_system_settings.device_time_zone_location_name = name; 1339 m_system_settings.device_time_zone_location_name = name;
1342 SetSaveNeeded(); 1340 SetSaveNeeded();
1343 R_SUCCEED(); 1341 R_SUCCEED();
1344} 1342}
1345 1343
1346Result ISystemSettingsServer::GetNetworkSystemClockContext( 1344Result ISystemSettingsServer::GetNetworkSystemClockContext(
1347 Service::Time::Clock::SystemClockContext& out_context) { 1345 Service::PSC::Time::SystemClockContext& out_context) {
1348 out_context = m_system_settings.network_system_clock_context; 1346 out_context = m_system_settings.network_system_clock_context;
1349 R_SUCCEED(); 1347 R_SUCCEED();
1350} 1348}
1351 1349
1352Result ISystemSettingsServer::SetNetworkSystemClockContext( 1350Result ISystemSettingsServer::SetNetworkSystemClockContext(
1353 Service::Time::Clock::SystemClockContext& context) { 1351 Service::PSC::Time::SystemClockContext& context) {
1354 m_system_settings.network_system_clock_context = context; 1352 m_system_settings.network_system_clock_context = context;
1355 SetSaveNeeded(); 1353 SetSaveNeeded();
1356 R_SUCCEED(); 1354 R_SUCCEED();
@@ -1379,26 +1377,26 @@ Result ISystemSettingsServer::GetExternalSteadyClockInternalOffset(s64& out_offs
1379} 1377}
1380 1378
1381Result ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime( 1379Result ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime(
1382 Service::Time::Clock::SteadyClockTimePoint& out_time_point) { 1380 Service::PSC::Time::SteadyClockTimePoint& out_time_point) {
1383 out_time_point = m_system_settings.device_time_zone_location_updated_time; 1381 out_time_point = m_system_settings.device_time_zone_location_updated_time;
1384 R_SUCCEED(); 1382 R_SUCCEED();
1385} 1383}
1386 1384
1387Result ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime( 1385Result ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime(
1388 Service::Time::Clock::SteadyClockTimePoint& time_point) { 1386 Service::PSC::Time::SteadyClockTimePoint& time_point) {
1389 m_system_settings.device_time_zone_location_updated_time = time_point; 1387 m_system_settings.device_time_zone_location_updated_time = time_point;
1390 SetSaveNeeded(); 1388 SetSaveNeeded();
1391 R_SUCCEED(); 1389 R_SUCCEED();
1392} 1390}
1393 1391
1394Result ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime( 1392Result ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime(
1395 Service::Time::Clock::SteadyClockTimePoint& out_time_point) { 1393 Service::PSC::Time::SteadyClockTimePoint& out_time_point) {
1396 out_time_point = m_system_settings.user_system_clock_automatic_correction_updated_time_point; 1394 out_time_point = m_system_settings.user_system_clock_automatic_correction_updated_time_point;
1397 R_SUCCEED(); 1395 R_SUCCEED();
1398} 1396}
1399 1397
1400Result ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime( 1398Result ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime(
1401 Service::Time::Clock::SteadyClockTimePoint out_time_point) { 1399 Service::PSC::Time::SteadyClockTimePoint out_time_point) {
1402 m_system_settings.user_system_clock_automatic_correction_updated_time_point = out_time_point; 1400 m_system_settings.user_system_clock_automatic_correction_updated_time_point = out_time_point;
1403 SetSaveNeeded(); 1401 SetSaveNeeded();
1404 R_SUCCEED(); 1402 R_SUCCEED();
diff --git a/src/core/hle/service/set/system_settings_server.h b/src/core/hle/service/set/system_settings_server.h
index 32716f567..a2258d16d 100644
--- a/src/core/hle/service/set/system_settings_server.h
+++ b/src/core/hle/service/set/system_settings_server.h
@@ -11,14 +11,13 @@
11#include "common/polyfill_thread.h" 11#include "common/polyfill_thread.h"
12#include "common/uuid.h" 12#include "common/uuid.h"
13#include "core/hle/result.h" 13#include "core/hle/result.h"
14#include "core/hle/service/psc/time/common.h"
14#include "core/hle/service/service.h" 15#include "core/hle/service/service.h"
15#include "core/hle/service/set/setting_formats/appln_settings.h" 16#include "core/hle/service/set/setting_formats/appln_settings.h"
16#include "core/hle/service/set/setting_formats/device_settings.h" 17#include "core/hle/service/set/setting_formats/device_settings.h"
17#include "core/hle/service/set/setting_formats/private_settings.h" 18#include "core/hle/service/set/setting_formats/private_settings.h"
18#include "core/hle/service/set/setting_formats/system_settings.h" 19#include "core/hle/service/set/setting_formats/system_settings.h"
19#include "core/hle/service/set/settings_types.h" 20#include "core/hle/service/set/settings_types.h"
20#include "core/hle/service/time/clock_types.h"
21#include "core/hle/service/time/time_zone_types.h"
22 21
23namespace Core { 22namespace Core {
24class System; 23class System;
@@ -51,24 +50,24 @@ public:
51 50
52 Result GetExternalSteadyClockSourceId(Common::UUID& out_id); 51 Result GetExternalSteadyClockSourceId(Common::UUID& out_id);
53 Result SetExternalSteadyClockSourceId(Common::UUID id); 52 Result SetExternalSteadyClockSourceId(Common::UUID id);
54 Result GetUserSystemClockContext(Service::Time::Clock::SystemClockContext& out_context); 53 Result GetUserSystemClockContext(Service::PSC::Time::SystemClockContext& out_context);
55 Result SetUserSystemClockContext(Service::Time::Clock::SystemClockContext& context); 54 Result SetUserSystemClockContext(Service::PSC::Time::SystemClockContext& context);
56 Result GetDeviceTimeZoneLocationName(Service::Time::TimeZone::LocationName& out_name); 55 Result GetDeviceTimeZoneLocationName(Service::PSC::Time::LocationName& out_name);
57 Result SetDeviceTimeZoneLocationName(Service::Time::TimeZone::LocationName& name); 56 Result SetDeviceTimeZoneLocationName(Service::PSC::Time::LocationName& name);
58 Result GetNetworkSystemClockContext(Service::Time::Clock::SystemClockContext& out_context); 57 Result GetNetworkSystemClockContext(Service::PSC::Time::SystemClockContext& out_context);
59 Result SetNetworkSystemClockContext(Service::Time::Clock::SystemClockContext& context); 58 Result SetNetworkSystemClockContext(Service::PSC::Time::SystemClockContext& context);
60 Result IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled); 59 Result IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled);
61 Result SetUserSystemClockAutomaticCorrectionEnabled(bool enabled); 60 Result SetUserSystemClockAutomaticCorrectionEnabled(bool enabled);
62 Result SetExternalSteadyClockInternalOffset(s64 offset); 61 Result SetExternalSteadyClockInternalOffset(s64 offset);
63 Result GetExternalSteadyClockInternalOffset(s64& out_offset); 62 Result GetExternalSteadyClockInternalOffset(s64& out_offset);
64 Result GetDeviceTimeZoneLocationUpdatedTime( 63 Result GetDeviceTimeZoneLocationUpdatedTime(
65 Service::Time::Clock::SteadyClockTimePoint& out_time_point); 64 Service::PSC::Time::SteadyClockTimePoint& out_time_point);
66 Result SetDeviceTimeZoneLocationUpdatedTime( 65 Result SetDeviceTimeZoneLocationUpdatedTime(
67 Service::Time::Clock::SteadyClockTimePoint& time_point); 66 Service::PSC::Time::SteadyClockTimePoint& time_point);
68 Result GetUserSystemClockAutomaticCorrectionUpdatedTime( 67 Result GetUserSystemClockAutomaticCorrectionUpdatedTime(
69 Service::Time::Clock::SteadyClockTimePoint& out_time_point); 68 Service::PSC::Time::SteadyClockTimePoint& out_time_point);
70 Result SetUserSystemClockAutomaticCorrectionUpdatedTime( 69 Result SetUserSystemClockAutomaticCorrectionUpdatedTime(
71 Service::Time::Clock::SteadyClockTimePoint time_point); 70 Service::PSC::Time::SteadyClockTimePoint time_point);
72 71
73private: 72private:
74 void SetLanguageCode(HLERequestContext& ctx); 73 void SetLanguageCode(HLERequestContext& ctx);
@@ -147,8 +146,8 @@ private:
147 PrivateSettings m_private_settings{}; 146 PrivateSettings m_private_settings{};
148 DeviceSettings m_device_settings{}; 147 DeviceSettings m_device_settings{};
149 ApplnSettings m_appln_settings{}; 148 ApplnSettings m_appln_settings{};
150 std::jthread m_save_thread;
151 std::mutex m_save_needed_mutex; 149 std::mutex m_save_needed_mutex;
150 std::jthread m_save_thread;
152 bool m_save_needed{false}; 151 bool m_save_needed{false};
153}; 152};
154 153
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index 4ae32a9c1..32c218638 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -3,6 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <chrono>
6#include <memory> 7#include <memory>
7#include <mutex> 8#include <mutex>
8#include <string> 9#include <string>
@@ -10,6 +11,7 @@
10 11
11#include "common/concepts.h" 12#include "common/concepts.h"
12#include "core/hle/kernel/k_port.h" 13#include "core/hle/kernel/k_port.h"
14#include "core/hle/kernel/svc.h"
13#include "core/hle/result.h" 15#include "core/hle/result.h"
14#include "core/hle/service/service.h" 16#include "core/hle/service/service.h"
15 17
@@ -62,12 +64,21 @@ public:
62 Result GetServicePort(Kernel::KClientPort** out_client_port, const std::string& name); 64 Result GetServicePort(Kernel::KClientPort** out_client_port, const std::string& name);
63 65
64 template <Common::DerivedFrom<SessionRequestHandler> T> 66 template <Common::DerivedFrom<SessionRequestHandler> T>
65 std::shared_ptr<T> GetService(const std::string& service_name) const { 67 std::shared_ptr<T> GetService(const std::string& service_name, bool block = false) const {
66 auto service = registered_services.find(service_name); 68 auto service = registered_services.find(service_name);
67 if (service == registered_services.end()) { 69 if (service == registered_services.end() && !block) {
68 LOG_DEBUG(Service, "Can't find service: {}", service_name); 70 LOG_DEBUG(Service, "Can't find service: {}", service_name);
69 return nullptr; 71 return nullptr;
72 } else if (block) {
73 using namespace std::literals::chrono_literals;
74 while (service == registered_services.end()) {
75 Kernel::Svc::SleepThread(
76 kernel.System(),
77 std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count());
78 service = registered_services.find(service_name);
79 }
70 } 80 }
81
71 return std::static_pointer_cast<T>(service->second()); 82 return std::static_pointer_cast<T>(service->second());
72 } 83 }
73 84
diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h
deleted file mode 100644
index 7149fffeb..000000000
--- a/src/core/hle/service/time/clock_types.h
+++ /dev/null
@@ -1,129 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <ratio>
7
8#include "common/common_funcs.h"
9#include "common/common_types.h"
10#include "common/uuid.h"
11#include "core/hle/service/time/errors.h"
12#include "core/hle/service/time/time_zone_types.h"
13
14// Defined by WinBase.h on Windows
15#ifdef GetCurrentTime
16#undef GetCurrentTime
17#endif
18
19namespace Service::Time::Clock {
20
21enum class TimeType : u8 {
22 UserSystemClock,
23 NetworkSystemClock,
24 LocalSystemClock,
25};
26
27/// https://switchbrew.org/wiki/Glue_services#SteadyClockTimePoint
28struct SteadyClockTimePoint {
29 s64 time_point;
30 Common::UUID clock_source_id;
31
32 Result GetSpanBetween(SteadyClockTimePoint other, s64& span) const {
33 span = 0;
34
35 if (clock_source_id != other.clock_source_id) {
36 return ERROR_TIME_MISMATCH;
37 }
38
39 span = other.time_point - time_point;
40
41 return ResultSuccess;
42 }
43
44 static SteadyClockTimePoint GetRandom() {
45 return {0, Common::UUID::MakeRandom()};
46 }
47};
48static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size");
49static_assert(std::is_trivially_copyable_v<SteadyClockTimePoint>,
50 "SteadyClockTimePoint must be trivially copyable");
51
52struct SteadyClockContext {
53 u64 internal_offset;
54 Common::UUID steady_time_point;
55};
56static_assert(sizeof(SteadyClockContext) == 0x18, "SteadyClockContext is incorrect size");
57static_assert(std::is_trivially_copyable_v<SteadyClockContext>,
58 "SteadyClockContext must be trivially copyable");
59using StandardSteadyClockTimePointType = SteadyClockContext;
60
61struct SystemClockContext {
62 s64 offset;
63 SteadyClockTimePoint steady_time_point;
64};
65static_assert(sizeof(SystemClockContext) == 0x20, "SystemClockContext is incorrect size");
66static_assert(std::is_trivially_copyable_v<SystemClockContext>,
67 "SystemClockContext must be trivially copyable");
68
69struct ContinuousAdjustmentTimePoint {
70 s64 measurement_offset;
71 s64 diff_scale;
72 u32 shift_amount;
73 s64 lower;
74 s64 upper;
75 Common::UUID clock_source_id;
76};
77static_assert(sizeof(ContinuousAdjustmentTimePoint) == 0x38);
78static_assert(std::is_trivially_copyable_v<ContinuousAdjustmentTimePoint>,
79 "ContinuousAdjustmentTimePoint must be trivially copyable");
80
81/// https://switchbrew.org/wiki/Glue_services#TimeSpanType
82struct TimeSpanType {
83 s64 nanoseconds{};
84
85 s64 ToSeconds() const {
86 return nanoseconds / std::nano::den;
87 }
88
89 static TimeSpanType FromSeconds(s64 seconds) {
90 return {seconds * std::nano::den};
91 }
92
93 template <u64 Frequency>
94 static TimeSpanType FromTicks(u64 ticks) {
95 using TicksToNSRatio = std::ratio<std::nano::den, Frequency>;
96 return {static_cast<s64>(ticks * TicksToNSRatio::num / TicksToNSRatio::den)};
97 }
98};
99static_assert(sizeof(TimeSpanType) == 8, "TimeSpanType is incorrect size");
100
101struct ClockSnapshot {
102 SystemClockContext user_context;
103 SystemClockContext network_context;
104 s64 user_time;
105 s64 network_time;
106 TimeZone::CalendarTime user_calendar_time;
107 TimeZone::CalendarTime network_calendar_time;
108 TimeZone::CalendarAdditionalInfo user_calendar_additional_time;
109 TimeZone::CalendarAdditionalInfo network_calendar_additional_time;
110 SteadyClockTimePoint steady_clock_time_point;
111 TimeZone::LocationName location_name;
112 u8 is_automatic_correction_enabled;
113 TimeType type;
114 INSERT_PADDING_BYTES_NOINIT(0x2);
115
116 static Result GetCurrentTime(s64& current_time,
117 const SteadyClockTimePoint& steady_clock_time_point,
118 const SystemClockContext& context) {
119 if (steady_clock_time_point.clock_source_id != context.steady_time_point.clock_source_id) {
120 current_time = 0;
121 return ERROR_TIME_MISMATCH;
122 }
123 current_time = steady_clock_time_point.time_point + context.offset;
124 return ResultSuccess;
125 }
126};
127static_assert(sizeof(ClockSnapshot) == 0xD0, "ClockSnapshot is incorrect size");
128
129} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h b/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h
deleted file mode 100644
index 0f928a5a5..000000000
--- a/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h
+++ /dev/null
@@ -1,15 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/time/system_clock_context_update_callback.h"
7
8namespace Service::Time::Clock {
9
10class EphemeralNetworkSystemClockContextWriter final : public SystemClockContextUpdateCallback {
11public:
12 EphemeralNetworkSystemClockContextWriter() : SystemClockContextUpdateCallback{} {}
13};
14
15} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/ephemeral_network_system_clock_core.h b/src/core/hle/service/time/ephemeral_network_system_clock_core.h
deleted file mode 100644
index 0a5f5aafb..000000000
--- a/src/core/hle/service/time/ephemeral_network_system_clock_core.h
+++ /dev/null
@@ -1,16 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/time/system_clock_core.h"
7
8namespace Service::Time::Clock {
9
10class EphemeralNetworkSystemClockCore final : public SystemClockCore {
11public:
12 explicit EphemeralNetworkSystemClockCore(SteadyClockCore& steady_clock_core_)
13 : SystemClockCore{steady_clock_core_} {}
14};
15
16} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/errors.h b/src/core/hle/service/time/errors.h
deleted file mode 100644
index 6655d30e1..000000000
--- a/src/core/hle/service/time/errors.h
+++ /dev/null
@@ -1,21 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/result.h"
7
8namespace Service::Time {
9
10constexpr Result ERROR_PERMISSION_DENIED{ErrorModule::Time, 1};
11constexpr Result ERROR_TIME_MISMATCH{ErrorModule::Time, 102};
12constexpr Result ERROR_UNINITIALIZED_CLOCK{ErrorModule::Time, 103};
13constexpr Result ERROR_TIME_NOT_FOUND{ErrorModule::Time, 200};
14constexpr Result ERROR_OVERFLOW{ErrorModule::Time, 201};
15constexpr Result ERROR_LOCATION_NAME_TOO_LONG{ErrorModule::Time, 801};
16constexpr Result ERROR_OUT_OF_RANGE{ErrorModule::Time, 902};
17constexpr Result ERROR_TIME_ZONE_CONVERSION_FAILED{ErrorModule::Time, 903};
18constexpr Result ERROR_TIME_ZONE_NOT_FOUND{ErrorModule::Time, 989};
19constexpr Result ERROR_NOT_IMPLEMENTED{ErrorModule::Time, 990};
20
21} // namespace Service::Time
diff --git a/src/core/hle/service/time/local_system_clock_context_writer.h b/src/core/hle/service/time/local_system_clock_context_writer.h
deleted file mode 100644
index 1639ef2b9..000000000
--- a/src/core/hle/service/time/local_system_clock_context_writer.h
+++ /dev/null
@@ -1,26 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/time/system_clock_context_update_callback.h"
7#include "core/hle/service/time/time_sharedmemory.h"
8
9namespace Service::Time::Clock {
10
11class LocalSystemClockContextWriter final : public SystemClockContextUpdateCallback {
12public:
13 explicit LocalSystemClockContextWriter(SharedMemory& shared_memory_)
14 : SystemClockContextUpdateCallback{}, shared_memory{shared_memory_} {}
15
16protected:
17 Result Update() override {
18 shared_memory.UpdateLocalSystemClockContext(context);
19 return ResultSuccess;
20 }
21
22private:
23 SharedMemory& shared_memory;
24};
25
26} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/network_system_clock_context_writer.h b/src/core/hle/service/time/network_system_clock_context_writer.h
deleted file mode 100644
index 655e4c06d..000000000
--- a/src/core/hle/service/time/network_system_clock_context_writer.h
+++ /dev/null
@@ -1,27 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/time/errors.h"
7#include "core/hle/service/time/system_clock_context_update_callback.h"
8#include "core/hle/service/time/time_sharedmemory.h"
9
10namespace Service::Time::Clock {
11
12class NetworkSystemClockContextWriter final : public SystemClockContextUpdateCallback {
13public:
14 explicit NetworkSystemClockContextWriter(SharedMemory& shared_memory_)
15 : SystemClockContextUpdateCallback{}, shared_memory{shared_memory_} {}
16
17protected:
18 Result Update() override {
19 shared_memory.UpdateNetworkSystemClockContext(context);
20 return ResultSuccess;
21 }
22
23private:
24 SharedMemory& shared_memory;
25};
26
27} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/standard_local_system_clock_core.h b/src/core/hle/service/time/standard_local_system_clock_core.h
deleted file mode 100644
index ae2ff1bfd..000000000
--- a/src/core/hle/service/time/standard_local_system_clock_core.h
+++ /dev/null
@@ -1,16 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/time/system_clock_core.h"
7
8namespace Service::Time::Clock {
9
10class StandardLocalSystemClockCore final : public SystemClockCore {
11public:
12 explicit StandardLocalSystemClockCore(SteadyClockCore& steady_clock_core_)
13 : SystemClockCore{steady_clock_core_} {}
14};
15
16} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/standard_network_system_clock_core.h b/src/core/hle/service/time/standard_network_system_clock_core.h
deleted file mode 100644
index c1ec5252b..000000000
--- a/src/core/hle/service/time/standard_network_system_clock_core.h
+++ /dev/null
@@ -1,45 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/time/clock_types.h"
7#include "core/hle/service/time/steady_clock_core.h"
8#include "core/hle/service/time/system_clock_core.h"
9
10namespace Core {
11class System;
12}
13
14namespace Service::Time::Clock {
15
16class StandardNetworkSystemClockCore final : public SystemClockCore {
17public:
18 explicit StandardNetworkSystemClockCore(SteadyClockCore& steady_clock_core_)
19 : SystemClockCore{steady_clock_core_} {}
20
21 void SetStandardNetworkClockSufficientAccuracy(TimeSpanType value) {
22 standard_network_clock_sufficient_accuracy = value;
23 }
24
25 bool IsStandardNetworkSystemClockAccuracySufficient(Core::System& system) const {
26 SystemClockContext clock_ctx{};
27 if (GetClockContext(system, clock_ctx) != ResultSuccess) {
28 return {};
29 }
30
31 s64 span{};
32 if (clock_ctx.steady_time_point.GetSpanBetween(
33 GetSteadyClockCore().GetCurrentTimePoint(system), span) != ResultSuccess) {
34 return {};
35 }
36
37 return TimeSpanType{span}.nanoseconds <
38 standard_network_clock_sufficient_accuracy.nanoseconds;
39 }
40
41private:
42 TimeSpanType standard_network_clock_sufficient_accuracy{};
43};
44
45} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/standard_steady_clock_core.cpp b/src/core/hle/service/time/standard_steady_clock_core.cpp
deleted file mode 100644
index 5627b7003..000000000
--- a/src/core/hle/service/time/standard_steady_clock_core.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/core_timing.h"
6#include "core/hardware_properties.h"
7#include "core/hle/service/time/standard_steady_clock_core.h"
8
9namespace Service::Time::Clock {
10
11TimeSpanType StandardSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) {
12 const TimeSpanType ticks_time_span{
13 TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>(system.CoreTiming().GetClockTicks())};
14 TimeSpanType raw_time_point{setup_value.nanoseconds + ticks_time_span.nanoseconds};
15
16 if (raw_time_point.nanoseconds < cached_raw_time_point.nanoseconds) {
17 raw_time_point.nanoseconds = cached_raw_time_point.nanoseconds;
18 }
19
20 cached_raw_time_point = raw_time_point;
21 return raw_time_point;
22}
23
24} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/standard_steady_clock_core.h b/src/core/hle/service/time/standard_steady_clock_core.h
deleted file mode 100644
index 036463b87..000000000
--- a/src/core/hle/service/time/standard_steady_clock_core.h
+++ /dev/null
@@ -1,41 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/time/clock_types.h"
7#include "core/hle/service/time/steady_clock_core.h"
8
9namespace Core {
10class System;
11}
12
13namespace Service::Time::Clock {
14
15class StandardSteadyClockCore final : public SteadyClockCore {
16public:
17 SteadyClockTimePoint GetTimePoint(Core::System& system) override {
18 return {GetCurrentRawTimePoint(system).ToSeconds(), GetClockSourceId()};
19 }
20
21 TimeSpanType GetInternalOffset() const override {
22 return internal_offset;
23 }
24
25 void SetInternalOffset(TimeSpanType value) override {
26 internal_offset = value;
27 }
28
29 TimeSpanType GetCurrentRawTimePoint(Core::System& system) override;
30
31 void SetSetupValue(TimeSpanType value) {
32 setup_value = value;
33 }
34
35private:
36 TimeSpanType setup_value{};
37 TimeSpanType internal_offset{};
38 TimeSpanType cached_raw_time_point{};
39};
40
41} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/standard_user_system_clock_core.cpp b/src/core/hle/service/time/standard_user_system_clock_core.cpp
deleted file mode 100644
index b033757ed..000000000
--- a/src/core/hle/service/time/standard_user_system_clock_core.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/assert.h"
5#include "core/core.h"
6#include "core/hle/kernel/k_event.h"
7#include "core/hle/service/time/standard_local_system_clock_core.h"
8#include "core/hle/service/time/standard_network_system_clock_core.h"
9#include "core/hle/service/time/standard_user_system_clock_core.h"
10
11namespace Service::Time::Clock {
12
13StandardUserSystemClockCore::StandardUserSystemClockCore(
14 StandardLocalSystemClockCore& local_system_clock_core_,
15 StandardNetworkSystemClockCore& network_system_clock_core_, Core::System& system_)
16 : SystemClockCore(local_system_clock_core_.GetSteadyClockCore()),
17 local_system_clock_core{local_system_clock_core_},
18 network_system_clock_core{network_system_clock_core_},
19 auto_correction_time{SteadyClockTimePoint::GetRandom()}, service_context{
20 system_,
21 "StandardUserSystemClockCore"} {
22 auto_correction_event =
23 service_context.CreateEvent("StandardUserSystemClockCore:AutoCorrectionEvent");
24}
25
26StandardUserSystemClockCore::~StandardUserSystemClockCore() {
27 service_context.CloseEvent(auto_correction_event);
28}
29
30Result StandardUserSystemClockCore::SetAutomaticCorrectionEnabled(Core::System& system,
31 bool value) {
32 if (const Result result{ApplyAutomaticCorrection(system, value)}; result != ResultSuccess) {
33 return result;
34 }
35
36 auto_correction_enabled = value;
37
38 return ResultSuccess;
39}
40
41Result StandardUserSystemClockCore::GetClockContext(Core::System& system,
42 SystemClockContext& ctx) const {
43 if (const Result result{ApplyAutomaticCorrection(system, false)}; result != ResultSuccess) {
44 return result;
45 }
46
47 return local_system_clock_core.GetClockContext(system, ctx);
48}
49
50Result StandardUserSystemClockCore::Flush(const SystemClockContext&) {
51 UNIMPLEMENTED();
52 return ERROR_NOT_IMPLEMENTED;
53}
54
55Result StandardUserSystemClockCore::SetClockContext(const SystemClockContext&) {
56 UNIMPLEMENTED();
57 return ERROR_NOT_IMPLEMENTED;
58}
59
60Result StandardUserSystemClockCore::ApplyAutomaticCorrection(Core::System& system,
61 bool value) const {
62 if (auto_correction_enabled == value) {
63 return ResultSuccess;
64 }
65
66 if (!network_system_clock_core.IsClockSetup(system)) {
67 return ERROR_UNINITIALIZED_CLOCK;
68 }
69
70 SystemClockContext ctx{};
71 if (const Result result{network_system_clock_core.GetClockContext(system, ctx)};
72 result != ResultSuccess) {
73 return result;
74 }
75
76 local_system_clock_core.SetClockContext(ctx);
77
78 return ResultSuccess;
79}
80
81} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/standard_user_system_clock_core.h b/src/core/hle/service/time/standard_user_system_clock_core.h
deleted file mode 100644
index ee6e29487..000000000
--- a/src/core/hle/service/time/standard_user_system_clock_core.h
+++ /dev/null
@@ -1,63 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/kernel_helpers.h"
7#include "core/hle/service/time/clock_types.h"
8#include "core/hle/service/time/system_clock_core.h"
9
10namespace Core {
11class System;
12}
13
14namespace Kernel {
15class KEvent;
16}
17
18namespace Service::Time::Clock {
19
20class StandardLocalSystemClockCore;
21class StandardNetworkSystemClockCore;
22
23class StandardUserSystemClockCore final : public SystemClockCore {
24public:
25 StandardUserSystemClockCore(StandardLocalSystemClockCore& local_system_clock_core_,
26 StandardNetworkSystemClockCore& network_system_clock_core_,
27 Core::System& system_);
28
29 ~StandardUserSystemClockCore() override;
30
31 Result SetAutomaticCorrectionEnabled(Core::System& system, bool value);
32
33 Result GetClockContext(Core::System& system, SystemClockContext& ctx) const override;
34
35 bool IsAutomaticCorrectionEnabled() const {
36 return auto_correction_enabled;
37 }
38
39 void SetAutomaticCorrectionUpdatedTime(SteadyClockTimePoint steady_clock_time_point) {
40 auto_correction_time = steady_clock_time_point;
41 }
42
43protected:
44 Result Flush(const SystemClockContext&) override;
45
46 Result SetClockContext(const SystemClockContext&) override;
47
48 Result ApplyAutomaticCorrection(Core::System& system, bool value) const;
49
50 const SteadyClockTimePoint& GetAutomaticCorrectionUpdatedTime() const {
51 return auto_correction_time;
52 }
53
54private:
55 StandardLocalSystemClockCore& local_system_clock_core;
56 StandardNetworkSystemClockCore& network_system_clock_core;
57 bool auto_correction_enabled{};
58 SteadyClockTimePoint auto_correction_time;
59 KernelHelpers::ServiceContext service_context;
60 Kernel::KEvent* auto_correction_event;
61};
62
63} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/steady_clock_core.h b/src/core/hle/service/time/steady_clock_core.h
deleted file mode 100644
index 2867c351c..000000000
--- a/src/core/hle/service/time/steady_clock_core.h
+++ /dev/null
@@ -1,55 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/uuid.h"
7#include "core/hle/service/time/clock_types.h"
8
9namespace Core {
10class System;
11}
12
13namespace Service::Time::Clock {
14
15class SteadyClockCore {
16public:
17 SteadyClockCore() = default;
18 virtual ~SteadyClockCore() = default;
19
20 const Common::UUID& GetClockSourceId() const {
21 return clock_source_id;
22 }
23
24 void SetClockSourceId(const Common::UUID& value) {
25 clock_source_id = value;
26 }
27
28 virtual TimeSpanType GetInternalOffset() const = 0;
29
30 virtual void SetInternalOffset(TimeSpanType internal_offset) = 0;
31
32 virtual SteadyClockTimePoint GetTimePoint(Core::System& system) = 0;
33
34 virtual TimeSpanType GetCurrentRawTimePoint(Core::System& system) = 0;
35
36 SteadyClockTimePoint GetCurrentTimePoint(Core::System& system) {
37 SteadyClockTimePoint result{GetTimePoint(system)};
38 result.time_point += GetInternalOffset().ToSeconds();
39 return result;
40 }
41
42 bool IsInitialized() const {
43 return is_initialized;
44 }
45
46 void MarkAsInitialized() {
47 is_initialized = true;
48 }
49
50private:
51 Common::UUID clock_source_id{Common::UUID::MakeRandom()};
52 bool is_initialized{};
53};
54
55} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/system_clock_context_update_callback.cpp b/src/core/hle/service/time/system_clock_context_update_callback.cpp
deleted file mode 100644
index cafc04ee7..000000000
--- a/src/core/hle/service/time/system_clock_context_update_callback.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/k_event.h"
5#include "core/hle/service/time/errors.h"
6#include "core/hle/service/time/system_clock_context_update_callback.h"
7
8namespace Service::Time::Clock {
9
10SystemClockContextUpdateCallback::SystemClockContextUpdateCallback() = default;
11SystemClockContextUpdateCallback::~SystemClockContextUpdateCallback() = default;
12
13bool SystemClockContextUpdateCallback::NeedUpdate(const SystemClockContext& value) const {
14 if (has_context) {
15 return context.offset != value.offset ||
16 context.steady_time_point.clock_source_id != value.steady_time_point.clock_source_id;
17 }
18
19 return true;
20}
21
22void SystemClockContextUpdateCallback::RegisterOperationEvent(
23 std::shared_ptr<Kernel::KEvent>&& event) {
24 operation_event_list.emplace_back(std::move(event));
25}
26
27void SystemClockContextUpdateCallback::BroadcastOperationEvent() {
28 for (const auto& event : operation_event_list) {
29 event->Signal();
30 }
31}
32
33Result SystemClockContextUpdateCallback::Update(const SystemClockContext& value) {
34 Result result{ResultSuccess};
35
36 if (NeedUpdate(value)) {
37 context = value;
38 has_context = true;
39
40 result = Update();
41
42 if (result == ResultSuccess) {
43 BroadcastOperationEvent();
44 }
45 }
46
47 return result;
48}
49
50Result SystemClockContextUpdateCallback::Update() {
51 return ResultSuccess;
52}
53
54} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/system_clock_context_update_callback.h b/src/core/hle/service/time/system_clock_context_update_callback.h
deleted file mode 100644
index bf657acd9..000000000
--- a/src/core/hle/service/time/system_clock_context_update_callback.h
+++ /dev/null
@@ -1,43 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <vector>
8
9#include "core/hle/service/time/clock_types.h"
10
11namespace Kernel {
12class KEvent;
13}
14
15namespace Service::Time::Clock {
16
17// Parts of this implementation were based on Ryujinx (https://github.com/Ryujinx/Ryujinx/pull/783).
18// This code was released under public domain.
19
20class SystemClockContextUpdateCallback {
21public:
22 SystemClockContextUpdateCallback();
23 virtual ~SystemClockContextUpdateCallback();
24
25 bool NeedUpdate(const SystemClockContext& value) const;
26
27 void RegisterOperationEvent(std::shared_ptr<Kernel::KEvent>&& event);
28
29 void BroadcastOperationEvent();
30
31 Result Update(const SystemClockContext& value);
32
33protected:
34 virtual Result Update();
35
36 SystemClockContext context{};
37
38private:
39 bool has_context{};
40 std::vector<std::shared_ptr<Kernel::KEvent>> operation_event_list;
41};
42
43} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/system_clock_core.cpp b/src/core/hle/service/time/system_clock_core.cpp
deleted file mode 100644
index da078241f..000000000
--- a/src/core/hle/service/time/system_clock_core.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/time/steady_clock_core.h"
5#include "core/hle/service/time/system_clock_context_update_callback.h"
6#include "core/hle/service/time/system_clock_core.h"
7
8namespace Service::Time::Clock {
9
10SystemClockCore::SystemClockCore(SteadyClockCore& steady_clock_core_)
11 : steady_clock_core{steady_clock_core_} {
12 context.steady_time_point.clock_source_id = steady_clock_core.GetClockSourceId();
13}
14
15SystemClockCore::~SystemClockCore() = default;
16
17Result SystemClockCore::GetCurrentTime(Core::System& system, s64& posix_time) const {
18 posix_time = 0;
19
20 const SteadyClockTimePoint current_time_point{steady_clock_core.GetCurrentTimePoint(system)};
21
22 SystemClockContext clock_context{};
23 if (const Result result{GetClockContext(system, clock_context)}; result != ResultSuccess) {
24 return result;
25 }
26
27 if (current_time_point.clock_source_id != clock_context.steady_time_point.clock_source_id) {
28 return ERROR_TIME_MISMATCH;
29 }
30
31 posix_time = clock_context.offset + current_time_point.time_point;
32
33 return ResultSuccess;
34}
35
36Result SystemClockCore::SetCurrentTime(Core::System& system, s64 posix_time) {
37 const SteadyClockTimePoint current_time_point{steady_clock_core.GetCurrentTimePoint(system)};
38 const SystemClockContext clock_context{posix_time - current_time_point.time_point,
39 current_time_point};
40
41 if (const Result result{SetClockContext(clock_context)}; result != ResultSuccess) {
42 return result;
43 }
44 return Flush(clock_context);
45}
46
47Result SystemClockCore::Flush(const SystemClockContext& clock_context) {
48 if (!system_clock_context_update_callback) {
49 return ResultSuccess;
50 }
51 return system_clock_context_update_callback->Update(clock_context);
52}
53
54Result SystemClockCore::SetSystemClockContext(const SystemClockContext& clock_context) {
55 if (const Result result{SetClockContext(clock_context)}; result != ResultSuccess) {
56 return result;
57 }
58 return Flush(clock_context);
59}
60
61bool SystemClockCore::IsClockSetup(Core::System& system) const {
62 SystemClockContext value{};
63 if (GetClockContext(system, value) == ResultSuccess) {
64 const SteadyClockTimePoint steady_clock_time_point{
65 steady_clock_core.GetCurrentTimePoint(system)};
66 return steady_clock_time_point.clock_source_id == value.steady_time_point.clock_source_id;
67 }
68 return {};
69}
70
71} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/system_clock_core.h b/src/core/hle/service/time/system_clock_core.h
deleted file mode 100644
index 8cb34126f..000000000
--- a/src/core/hle/service/time/system_clock_core.h
+++ /dev/null
@@ -1,72 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7
8#include "common/common_types.h"
9#include "core/hle/service/time/clock_types.h"
10
11namespace Core {
12class System;
13}
14
15namespace Service::Time::Clock {
16
17class SteadyClockCore;
18class SystemClockContextUpdateCallback;
19
20// Parts of this implementation were based on Ryujinx (https://github.com/Ryujinx/Ryujinx/pull/783).
21// This code was released under public domain.
22
23class SystemClockCore {
24public:
25 explicit SystemClockCore(SteadyClockCore& steady_clock_core_);
26 virtual ~SystemClockCore();
27
28 SteadyClockCore& GetSteadyClockCore() const {
29 return steady_clock_core;
30 }
31
32 Result GetCurrentTime(Core::System& system, s64& posix_time) const;
33
34 Result SetCurrentTime(Core::System& system, s64 posix_time);
35
36 virtual Result GetClockContext([[maybe_unused]] Core::System& system,
37 SystemClockContext& value) const {
38 value = context;
39 return ResultSuccess;
40 }
41
42 virtual Result SetClockContext(const SystemClockContext& value) {
43 context = value;
44 return ResultSuccess;
45 }
46
47 virtual Result Flush(const SystemClockContext& clock_context);
48
49 void SetUpdateCallbackInstance(std::shared_ptr<SystemClockContextUpdateCallback> callback) {
50 system_clock_context_update_callback = std::move(callback);
51 }
52
53 Result SetSystemClockContext(const SystemClockContext& context);
54
55 bool IsInitialized() const {
56 return is_initialized;
57 }
58
59 void MarkAsInitialized() {
60 is_initialized = true;
61 }
62
63 bool IsClockSetup(Core::System& system) const;
64
65private:
66 SteadyClockCore& steady_clock_core;
67 SystemClockContext context{};
68 bool is_initialized{};
69 std::shared_ptr<SystemClockContextUpdateCallback> system_clock_context_update_callback;
70};
71
72} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.cpp b/src/core/hle/service/time/tick_based_steady_clock_core.cpp
deleted file mode 100644
index 0d9fb3143..000000000
--- a/src/core/hle/service/time/tick_based_steady_clock_core.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/core_timing.h"
6#include "core/hardware_properties.h"
7#include "core/hle/service/time/tick_based_steady_clock_core.h"
8
9namespace Service::Time::Clock {
10
11SteadyClockTimePoint TickBasedSteadyClockCore::GetTimePoint(Core::System& system) {
12 const TimeSpanType ticks_time_span{
13 TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>(system.CoreTiming().GetClockTicks())};
14
15 return {ticks_time_span.ToSeconds(), GetClockSourceId()};
16}
17
18TimeSpanType TickBasedSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) {
19 return TimeSpanType::FromSeconds(GetTimePoint(system).time_point);
20}
21
22} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.h b/src/core/hle/service/time/tick_based_steady_clock_core.h
deleted file mode 100644
index 491185dc3..000000000
--- a/src/core/hle/service/time/tick_based_steady_clock_core.h
+++ /dev/null
@@ -1,28 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/time/clock_types.h"
7#include "core/hle/service/time/steady_clock_core.h"
8
9namespace Core {
10class System;
11}
12
13namespace Service::Time::Clock {
14
15class TickBasedSteadyClockCore final : public SteadyClockCore {
16public:
17 TimeSpanType GetInternalOffset() const override {
18 return {};
19 }
20
21 void SetInternalOffset(TimeSpanType internal_offset) override {}
22
23 SteadyClockTimePoint GetTimePoint(Core::System& system) override;
24
25 TimeSpanType GetCurrentRawTimePoint(Core::System& system) override;
26};
27
28} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
deleted file mode 100644
index 7197ca30f..000000000
--- a/src/core/hle/service/time/time.cpp
+++ /dev/null
@@ -1,412 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "core/core.h"
6#include "core/core_timing.h"
7#include "core/hardware_properties.h"
8#include "core/hle/kernel/kernel.h"
9#include "core/hle/service/ipc_helpers.h"
10#include "core/hle/service/server_manager.h"
11#include "core/hle/service/time/time.h"
12#include "core/hle/service/time/time_interface.h"
13#include "core/hle/service/time/time_manager.h"
14#include "core/hle/service/time/time_sharedmemory.h"
15#include "core/hle/service/time/time_zone_service.h"
16
17namespace Service::Time {
18
19class ISystemClock final : public ServiceFramework<ISystemClock> {
20public:
21 explicit ISystemClock(Clock::SystemClockCore& clock_core_, Core::System& system_)
22 : ServiceFramework{system_, "ISystemClock"}, clock_core{clock_core_} {
23 // clang-format off
24 static const FunctionInfo functions[] = {
25 {0, &ISystemClock::GetCurrentTime, "GetCurrentTime"},
26 {1, nullptr, "SetCurrentTime"},
27 {2, &ISystemClock::GetSystemClockContext, "GetSystemClockContext"},
28 {3, nullptr, "SetSystemClockContext"},
29 {4, nullptr, "GetOperationEventReadableHandle"},
30 };
31 // clang-format on
32
33 RegisterHandlers(functions);
34 }
35
36private:
37 void GetCurrentTime(HLERequestContext& ctx) {
38 LOG_DEBUG(Service_Time, "called");
39
40 if (!clock_core.IsInitialized()) {
41 IPC::ResponseBuilder rb{ctx, 2};
42 rb.Push(ERROR_UNINITIALIZED_CLOCK);
43 return;
44 }
45
46 s64 posix_time{};
47 if (const Result result{clock_core.GetCurrentTime(system, posix_time)}; result.IsError()) {
48 IPC::ResponseBuilder rb{ctx, 2};
49 rb.Push(result);
50 return;
51 }
52
53 IPC::ResponseBuilder rb{ctx, 4};
54 rb.Push(ResultSuccess);
55 rb.Push<s64>(posix_time);
56 }
57
58 void GetSystemClockContext(HLERequestContext& ctx) {
59 LOG_DEBUG(Service_Time, "called");
60
61 if (!clock_core.IsInitialized()) {
62 IPC::ResponseBuilder rb{ctx, 2};
63 rb.Push(ERROR_UNINITIALIZED_CLOCK);
64 return;
65 }
66
67 Clock::SystemClockContext system_clock_context{};
68 if (const Result result{clock_core.GetClockContext(system, system_clock_context)};
69 result.IsError()) {
70 IPC::ResponseBuilder rb{ctx, 2};
71 rb.Push(result);
72 return;
73 }
74
75 IPC::ResponseBuilder rb{ctx, sizeof(Clock::SystemClockContext) / 4 + 2};
76 rb.Push(ResultSuccess);
77 rb.PushRaw(system_clock_context);
78 }
79
80 Clock::SystemClockCore& clock_core;
81};
82
83class ISteadyClock final : public ServiceFramework<ISteadyClock> {
84public:
85 explicit ISteadyClock(Clock::SteadyClockCore& clock_core_, Core::System& system_)
86 : ServiceFramework{system_, "ISteadyClock"}, clock_core{clock_core_} {
87 static const FunctionInfo functions[] = {
88 {0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"},
89 {2, nullptr, "GetTestOffset"},
90 {3, nullptr, "SetTestOffset"},
91 {100, nullptr, "GetRtcValue"},
92 {101, nullptr, "IsRtcResetDetected"},
93 {102, nullptr, "GetSetupResultValue"},
94 {200, nullptr, "GetInternalOffset"},
95 {201, nullptr, "SetInternalOffset"},
96 };
97 RegisterHandlers(functions);
98 }
99
100private:
101 void GetCurrentTimePoint(HLERequestContext& ctx) {
102 LOG_DEBUG(Service_Time, "called");
103
104 if (!clock_core.IsInitialized()) {
105 IPC::ResponseBuilder rb{ctx, 2};
106 rb.Push(ERROR_UNINITIALIZED_CLOCK);
107 return;
108 }
109
110 const Clock::SteadyClockTimePoint time_point{clock_core.GetCurrentTimePoint(system)};
111 IPC::ResponseBuilder rb{ctx, (sizeof(Clock::SteadyClockTimePoint) / 4) + 2};
112 rb.Push(ResultSuccess);
113 rb.PushRaw(time_point);
114 }
115
116 Clock::SteadyClockCore& clock_core;
117};
118
119Result Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
120 Kernel::KThread* thread, Clock::SystemClockContext user_context,
121 Clock::SystemClockContext network_context, Clock::TimeType type,
122 Clock::ClockSnapshot& clock_snapshot) {
123
124 auto& time_manager{system.GetTimeManager()};
125
126 clock_snapshot.steady_clock_time_point =
127 time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system);
128 clock_snapshot.is_automatic_correction_enabled =
129 time_manager.GetStandardUserSystemClockCore().IsAutomaticCorrectionEnabled();
130 clock_snapshot.type = type;
131
132 if (const Result result{
133 time_manager.GetTimeZoneContentManager().GetTimeZoneManager().GetDeviceLocationName(
134 clock_snapshot.location_name)};
135 result != ResultSuccess) {
136 return result;
137 }
138
139 clock_snapshot.user_context = user_context;
140
141 if (const Result result{Clock::ClockSnapshot::GetCurrentTime(
142 clock_snapshot.user_time, clock_snapshot.steady_clock_time_point,
143 clock_snapshot.user_context)};
144 result != ResultSuccess) {
145 return result;
146 }
147
148 TimeZone::CalendarInfo userCalendarInfo{};
149 if (const Result result{
150 time_manager.GetTimeZoneContentManager().GetTimeZoneManager().ToCalendarTimeWithMyRules(
151 clock_snapshot.user_time, userCalendarInfo)};
152 result != ResultSuccess) {
153 return result;
154 }
155
156 clock_snapshot.user_calendar_time = userCalendarInfo.time;
157 clock_snapshot.user_calendar_additional_time = userCalendarInfo.additional_info;
158
159 clock_snapshot.network_context = network_context;
160
161 if (Clock::ClockSnapshot::GetCurrentTime(clock_snapshot.network_time,
162 clock_snapshot.steady_clock_time_point,
163 clock_snapshot.network_context) != ResultSuccess) {
164 clock_snapshot.network_time = 0;
165 }
166
167 TimeZone::CalendarInfo networkCalendarInfo{};
168 if (const Result result{
169 time_manager.GetTimeZoneContentManager().GetTimeZoneManager().ToCalendarTimeWithMyRules(
170 clock_snapshot.network_time, networkCalendarInfo)};
171 result != ResultSuccess) {
172 return result;
173 }
174
175 clock_snapshot.network_calendar_time = networkCalendarInfo.time;
176 clock_snapshot.network_calendar_additional_time = networkCalendarInfo.additional_info;
177
178 return ResultSuccess;
179}
180
181void Module::Interface::GetStandardUserSystemClock(HLERequestContext& ctx) {
182 LOG_DEBUG(Service_Time, "called");
183 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
184 rb.Push(ResultSuccess);
185 rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardUserSystemClockCore(),
186 system);
187}
188
189void Module::Interface::GetStandardNetworkSystemClock(HLERequestContext& ctx) {
190 LOG_DEBUG(Service_Time, "called");
191 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
192 rb.Push(ResultSuccess);
193 rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardNetworkSystemClockCore(),
194 system);
195}
196
197void Module::Interface::GetStandardSteadyClock(HLERequestContext& ctx) {
198 LOG_DEBUG(Service_Time, "called");
199 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
200 rb.Push(ResultSuccess);
201 rb.PushIpcInterface<ISteadyClock>(system.GetTimeManager().GetStandardSteadyClockCore(), system);
202}
203
204void Module::Interface::GetTimeZoneService(HLERequestContext& ctx) {
205 LOG_DEBUG(Service_Time, "called");
206 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
207 rb.Push(ResultSuccess);
208 rb.PushIpcInterface<ITimeZoneService>(system,
209 system.GetTimeManager().GetTimeZoneContentManager());
210}
211
212void Module::Interface::GetStandardLocalSystemClock(HLERequestContext& ctx) {
213 LOG_DEBUG(Service_Time, "called");
214 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
215 rb.Push(ResultSuccess);
216 rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardLocalSystemClockCore(),
217 system);
218}
219
220void Module::Interface::IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx) {
221 LOG_DEBUG(Service_Time, "called");
222 auto& clock_core{system.GetTimeManager().GetStandardNetworkSystemClockCore()};
223 IPC::ResponseBuilder rb{ctx, 3};
224 rb.Push(ResultSuccess);
225 rb.Push<u32>(clock_core.IsStandardNetworkSystemClockAccuracySufficient(system));
226}
227
228void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx) {
229 LOG_DEBUG(Service_Time, "called");
230
231 auto& steady_clock_core{system.GetTimeManager().GetStandardSteadyClockCore()};
232 if (!steady_clock_core.IsInitialized()) {
233 IPC::ResponseBuilder rb{ctx, 2};
234 rb.Push(ERROR_UNINITIALIZED_CLOCK);
235 return;
236 }
237
238 IPC::RequestParser rp{ctx};
239 const auto context{rp.PopRaw<Clock::SystemClockContext>()};
240 const auto current_time_point{steady_clock_core.GetCurrentTimePoint(system)};
241
242 if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) {
243 const auto ticks{Clock::TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>(
244 system.CoreTiming().GetClockTicks())};
245 const s64 base_time_point{context.offset + current_time_point.time_point -
246 ticks.ToSeconds()};
247 IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2};
248 rb.Push(ResultSuccess);
249 rb.PushRaw(base_time_point);
250 return;
251 }
252
253 IPC::ResponseBuilder rb{ctx, 2};
254 rb.Push(ERROR_TIME_MISMATCH);
255}
256
257void Module::Interface::GetClockSnapshot(HLERequestContext& ctx) {
258 IPC::RequestParser rp{ctx};
259 const auto type{rp.PopEnum<Clock::TimeType>()};
260
261 LOG_DEBUG(Service_Time, "called, type={}", type);
262
263 Clock::SystemClockContext user_context{};
264 if (const Result result{
265 system.GetTimeManager().GetStandardUserSystemClockCore().GetClockContext(system,
266 user_context)};
267 result.IsError()) {
268 IPC::ResponseBuilder rb{ctx, 2};
269 rb.Push(result);
270 return;
271 }
272
273 Clock::SystemClockContext network_context{};
274 if (const Result result{
275 system.GetTimeManager().GetStandardNetworkSystemClockCore().GetClockContext(
276 system, network_context)};
277 result.IsError()) {
278 IPC::ResponseBuilder rb{ctx, 2};
279 rb.Push(result);
280 return;
281 }
282
283 Clock::ClockSnapshot clock_snapshot{};
284 if (const Result result{GetClockSnapshotFromSystemClockContextInternal(
285 &ctx.GetThread(), user_context, network_context, type, clock_snapshot)};
286 result.IsError()) {
287 IPC::ResponseBuilder rb{ctx, 2};
288 rb.Push(result);
289 return;
290 }
291
292 ctx.WriteBuffer(clock_snapshot);
293
294 IPC::ResponseBuilder rb{ctx, 2};
295 rb.Push(ResultSuccess);
296}
297
298void Module::Interface::GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx) {
299 IPC::RequestParser rp{ctx};
300 const auto type{rp.PopEnum<Clock::TimeType>()};
301
302 rp.Skip(1, false);
303
304 const Clock::SystemClockContext user_context{rp.PopRaw<Clock::SystemClockContext>()};
305 const Clock::SystemClockContext network_context{rp.PopRaw<Clock::SystemClockContext>()};
306
307 LOG_DEBUG(Service_Time, "called, type={}", type);
308
309 Clock::ClockSnapshot clock_snapshot{};
310 if (const Result result{GetClockSnapshotFromSystemClockContextInternal(
311 &ctx.GetThread(), user_context, network_context, type, clock_snapshot)};
312 result != ResultSuccess) {
313 IPC::ResponseBuilder rb{ctx, 2};
314 rb.Push(result);
315 return;
316 }
317
318 ctx.WriteBuffer(clock_snapshot);
319
320 IPC::ResponseBuilder rb{ctx, 2};
321 rb.Push(ResultSuccess);
322}
323
324void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser(HLERequestContext& ctx) {
325 LOG_DEBUG(Service_Time, "called");
326
327 Clock::ClockSnapshot snapshot_a;
328 Clock::ClockSnapshot snapshot_b;
329
330 const auto snapshot_a_data = ctx.ReadBuffer(0);
331 const auto snapshot_b_data = ctx.ReadBuffer(1);
332
333 std::memcpy(&snapshot_a, snapshot_a_data.data(), sizeof(Clock::ClockSnapshot));
334 std::memcpy(&snapshot_b, snapshot_b_data.data(), sizeof(Clock::ClockSnapshot));
335
336 auto time_span_type{Clock::TimeSpanType::FromSeconds(snapshot_b.user_context.offset -
337 snapshot_a.user_context.offset)};
338
339 if ((snapshot_b.user_context.steady_time_point.clock_source_id !=
340 snapshot_a.user_context.steady_time_point.clock_source_id) ||
341 (snapshot_b.is_automatic_correction_enabled &&
342 snapshot_a.is_automatic_correction_enabled)) {
343 time_span_type.nanoseconds = 0;
344 }
345
346 IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2};
347 rb.Push(ResultSuccess);
348 rb.PushRaw(time_span_type.nanoseconds);
349}
350
351void Module::Interface::CalculateSpanBetween(HLERequestContext& ctx) {
352 LOG_DEBUG(Service_Time, "called");
353
354 Clock::ClockSnapshot snapshot_a;
355 Clock::ClockSnapshot snapshot_b;
356
357 const auto snapshot_a_data = ctx.ReadBuffer(0);
358 const auto snapshot_b_data = ctx.ReadBuffer(1);
359
360 std::memcpy(&snapshot_a, snapshot_a_data.data(), sizeof(Clock::ClockSnapshot));
361 std::memcpy(&snapshot_b, snapshot_b_data.data(), sizeof(Clock::ClockSnapshot));
362
363 Clock::TimeSpanType time_span_type{};
364 s64 span{};
365
366 if (const Result result{snapshot_a.steady_clock_time_point.GetSpanBetween(
367 snapshot_b.steady_clock_time_point, span)};
368 result != ResultSuccess) {
369 if (snapshot_a.network_time && snapshot_b.network_time) {
370 time_span_type =
371 Clock::TimeSpanType::FromSeconds(snapshot_b.network_time - snapshot_a.network_time);
372 } else {
373 IPC::ResponseBuilder rb{ctx, 2};
374 rb.Push(ERROR_TIME_NOT_FOUND);
375 return;
376 }
377 } else {
378 time_span_type = Clock::TimeSpanType::FromSeconds(span);
379 }
380
381 IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2};
382 rb.Push(ResultSuccess);
383 rb.PushRaw(time_span_type.nanoseconds);
384}
385
386void Module::Interface::GetSharedMemoryNativeHandle(HLERequestContext& ctx) {
387 LOG_DEBUG(Service_Time, "called");
388 IPC::ResponseBuilder rb{ctx, 2, 1};
389 rb.Push(ResultSuccess);
390 rb.PushCopyObjects(&system.Kernel().GetTimeSharedMem());
391}
392
393Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_,
394 const char* name)
395 : ServiceFramework{system_, name}, module{std::move(module_)} {}
396
397Module::Interface::~Interface() = default;
398
399void LoopProcess(Core::System& system) {
400 auto server_manager = std::make_unique<ServerManager>(system);
401 auto module{std::make_shared<Module>()};
402
403 server_manager->RegisterNamedService("time:a",
404 std::make_shared<Time>(module, system, "time:a"));
405 server_manager->RegisterNamedService("time:s",
406 std::make_shared<Time>(module, system, "time:s"));
407 server_manager->RegisterNamedService("time:u",
408 std::make_shared<Time>(module, system, "time:u"));
409 ServerManager::RunServer(std::move(server_manager));
410}
411
412} // namespace Service::Time
diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h
deleted file mode 100644
index b2d754ef3..000000000
--- a/src/core/hle/service/time/time.h
+++ /dev/null
@@ -1,51 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7#include "core/hle/service/time/clock_types.h"
8
9namespace Core {
10class System;
11}
12
13namespace Service::Time {
14
15class Module final {
16public:
17 Module() = default;
18
19 class Interface : public ServiceFramework<Interface> {
20 public:
21 explicit Interface(std::shared_ptr<Module> module_, Core::System& system_,
22 const char* name);
23 ~Interface() override;
24
25 void GetStandardUserSystemClock(HLERequestContext& ctx);
26 void GetStandardNetworkSystemClock(HLERequestContext& ctx);
27 void GetStandardSteadyClock(HLERequestContext& ctx);
28 void GetTimeZoneService(HLERequestContext& ctx);
29 void GetStandardLocalSystemClock(HLERequestContext& ctx);
30 void IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx);
31 void CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx);
32 void GetClockSnapshot(HLERequestContext& ctx);
33 void GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx);
34 void CalculateStandardUserSystemClockDifferenceByUser(HLERequestContext& ctx);
35 void CalculateSpanBetween(HLERequestContext& ctx);
36 void GetSharedMemoryNativeHandle(HLERequestContext& ctx);
37
38 private:
39 Result GetClockSnapshotFromSystemClockContextInternal(
40 Kernel::KThread* thread, Clock::SystemClockContext user_context,
41 Clock::SystemClockContext network_context, Clock::TimeType type,
42 Clock::ClockSnapshot& cloc_snapshot);
43
44 protected:
45 std::shared_ptr<Module> module;
46 };
47};
48
49void LoopProcess(Core::System& system);
50
51} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_interface.cpp b/src/core/hle/service/time/time_interface.cpp
deleted file mode 100644
index 0c53e98ee..000000000
--- a/src/core/hle/service/time/time_interface.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/time/time_interface.h"
5
6namespace Service::Time {
7
8Time::Time(std::shared_ptr<Module> module_, Core::System& system_, const char* name_)
9 : Interface{std::move(module_), system_, name_} {
10 // clang-format off
11 static const FunctionInfo functions[] = {
12 {0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"},
13 {1, &Time::GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"},
14 {2, &Time::GetStandardSteadyClock, "GetStandardSteadyClock"},
15 {3, &Time::GetTimeZoneService, "GetTimeZoneService"},
16 {4, &Time::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"},
17 {5, nullptr, "GetEphemeralNetworkSystemClock"},
18 {20, &Time::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"},
19 {30, nullptr, "GetStandardNetworkClockOperationEventReadableHandle"},
20 {31, nullptr, "GetEphemeralNetworkClockOperationEventReadableHandle"},
21 {50, nullptr, "SetStandardSteadyClockInternalOffset"},
22 {51, nullptr, "GetStandardSteadyClockRtcValue"},
23 {100, nullptr, "IsStandardUserSystemClockAutomaticCorrectionEnabled"},
24 {101, nullptr, "SetStandardUserSystemClockAutomaticCorrectionEnabled"},
25 {102, nullptr, "GetStandardUserSystemClockInitialYear"},
26 {200, &Time::IsStandardNetworkSystemClockAccuracySufficient, "IsStandardNetworkSystemClockAccuracySufficient"},
27 {201, nullptr, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"},
28 {300, &Time::CalculateMonotonicSystemClockBaseTimePoint, "CalculateMonotonicSystemClockBaseTimePoint"},
29 {400, &Time::GetClockSnapshot, "GetClockSnapshot"},
30 {401, &Time::GetClockSnapshotFromSystemClockContext, "GetClockSnapshotFromSystemClockContext"},
31 {500, &Time::CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"},
32 {501, &Time::CalculateSpanBetween, "CalculateSpanBetween"},
33 };
34 // clang-format on
35
36 RegisterHandlers(functions);
37}
38
39Time::~Time() = default;
40
41} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_interface.h b/src/core/hle/service/time/time_interface.h
deleted file mode 100644
index ceeb0e5ef..000000000
--- a/src/core/hle/service/time/time_interface.h
+++ /dev/null
@@ -1,20 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/time/time.h"
7
8namespace Core {
9class System;
10}
11
12namespace Service::Time {
13
14class Time final : public Module::Interface {
15public:
16 explicit Time(std::shared_ptr<Module> time, Core::System& system_, const char* name_);
17 ~Time() override;
18};
19
20} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_manager.cpp b/src/core/hle/service/time/time_manager.cpp
deleted file mode 100644
index fa0fd0531..000000000
--- a/src/core/hle/service/time/time_manager.cpp
+++ /dev/null
@@ -1,293 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <chrono>
5#include <ctime>
6
7#include "common/settings.h"
8#include "common/time_zone.h"
9#include "core/hle/service/time/ephemeral_network_system_clock_context_writer.h"
10#include "core/hle/service/time/ephemeral_network_system_clock_core.h"
11#include "core/hle/service/time/local_system_clock_context_writer.h"
12#include "core/hle/service/time/network_system_clock_context_writer.h"
13#include "core/hle/service/time/tick_based_steady_clock_core.h"
14#include "core/hle/service/time/time_manager.h"
15
16namespace Service::Time {
17namespace {
18constexpr Clock::TimeSpanType standard_network_clock_accuracy{0x0009356907420000ULL};
19
20s64 GetSecondsSinceEpoch() {
21 const auto time_since_epoch = std::chrono::system_clock::now().time_since_epoch();
22 return std::chrono::duration_cast<std::chrono::seconds>(time_since_epoch).count() +
23 Settings::values.custom_rtc_differential;
24}
25} // Anonymous namespace
26
27struct TimeManager::Impl final {
28 explicit Impl(Core::System& system)
29 : shared_memory{system}, standard_local_system_clock_core{standard_steady_clock_core},
30 standard_network_system_clock_core{standard_steady_clock_core},
31 standard_user_system_clock_core{standard_local_system_clock_core,
32 standard_network_system_clock_core, system},
33 ephemeral_network_system_clock_core{tick_based_steady_clock_core},
34 local_system_clock_context_writer{
35 std::make_shared<Clock::LocalSystemClockContextWriter>(shared_memory)},
36 network_system_clock_context_writer{
37 std::make_shared<Clock::NetworkSystemClockContextWriter>(shared_memory)},
38 ephemeral_network_system_clock_context_writer{
39 std::make_shared<Clock::EphemeralNetworkSystemClockContextWriter>()},
40 time_zone_content_manager{system} {
41
42 const auto system_time{Clock::TimeSpanType::FromSeconds(GetSecondsSinceEpoch())};
43 SetupStandardSteadyClock(system, Common::UUID::MakeRandom(), system_time, {}, {});
44 SetupStandardLocalSystemClock(system, {}, system_time.ToSeconds());
45
46 Clock::SystemClockContext clock_context{};
47 standard_local_system_clock_core.GetClockContext(system, clock_context);
48
49 SetupStandardNetworkSystemClock(clock_context, standard_network_clock_accuracy);
50 SetupStandardUserSystemClock(system, {}, Clock::SteadyClockTimePoint::GetRandom());
51 SetupEphemeralNetworkSystemClock();
52 }
53
54 ~Impl() = default;
55
56 Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() {
57 return standard_steady_clock_core;
58 }
59
60 const Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() const {
61 return standard_steady_clock_core;
62 }
63
64 Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() {
65 return standard_local_system_clock_core;
66 }
67
68 const Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() const {
69 return standard_local_system_clock_core;
70 }
71
72 Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() {
73 return standard_network_system_clock_core;
74 }
75
76 const Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() const {
77 return standard_network_system_clock_core;
78 }
79
80 Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() {
81 return standard_user_system_clock_core;
82 }
83
84 const Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() const {
85 return standard_user_system_clock_core;
86 }
87
88 TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() {
89 return time_zone_content_manager;
90 }
91
92 const TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() const {
93 return time_zone_content_manager;
94 }
95
96 SharedMemory& GetSharedMemory() {
97 return shared_memory;
98 }
99
100 const SharedMemory& GetSharedMemory() const {
101 return shared_memory;
102 }
103
104 void SetupTimeZoneManager(std::string location_name,
105 Clock::SteadyClockTimePoint time_zone_updated_time_point,
106 std::vector<std::string> location_names, u128 time_zone_rule_version,
107 FileSys::VirtualFile& vfs_file) {
108 if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule(
109 location_name, vfs_file) != ResultSuccess) {
110 ASSERT(false);
111 return;
112 }
113
114 time_zone_content_manager.GetTimeZoneManager().SetUpdatedTime(time_zone_updated_time_point);
115 time_zone_content_manager.GetTimeZoneManager().SetTotalLocationNameCount(
116 location_names.size());
117 time_zone_content_manager.GetTimeZoneManager().SetLocationNames(location_names);
118 time_zone_content_manager.GetTimeZoneManager().SetTimeZoneRuleVersion(
119 time_zone_rule_version);
120 time_zone_content_manager.GetTimeZoneManager().MarkAsInitialized();
121 }
122
123 void SetupStandardSteadyClock(Core::System& system_, Common::UUID clock_source_id,
124 Clock::TimeSpanType setup_value,
125 Clock::TimeSpanType internal_offset, bool is_rtc_reset_detected) {
126 standard_steady_clock_core.SetClockSourceId(clock_source_id);
127 standard_steady_clock_core.SetSetupValue(setup_value);
128 standard_steady_clock_core.SetInternalOffset(internal_offset);
129 standard_steady_clock_core.MarkAsInitialized();
130
131 const auto current_time_point{standard_steady_clock_core.GetCurrentRawTimePoint(system_)};
132 shared_memory.SetupStandardSteadyClock(clock_source_id, current_time_point);
133 }
134
135 void SetupStandardLocalSystemClock(Core::System& system_,
136 Clock::SystemClockContext clock_context, s64 posix_time) {
137 standard_local_system_clock_core.SetUpdateCallbackInstance(
138 local_system_clock_context_writer);
139
140 const auto current_time_point{
141 standard_local_system_clock_core.GetSteadyClockCore().GetCurrentTimePoint(system_)};
142 if (current_time_point.clock_source_id == clock_context.steady_time_point.clock_source_id) {
143 standard_local_system_clock_core.SetSystemClockContext(clock_context);
144 } else {
145 if (standard_local_system_clock_core.SetCurrentTime(system_, posix_time) !=
146 ResultSuccess) {
147 ASSERT(false);
148 return;
149 }
150 }
151
152 standard_local_system_clock_core.MarkAsInitialized();
153 }
154
155 void SetupStandardNetworkSystemClock(Clock::SystemClockContext clock_context,
156 Clock::TimeSpanType sufficient_accuracy) {
157 standard_network_system_clock_core.SetUpdateCallbackInstance(
158 network_system_clock_context_writer);
159
160 if (standard_network_system_clock_core.SetSystemClockContext(clock_context) !=
161 ResultSuccess) {
162 ASSERT(false);
163 return;
164 }
165
166 standard_network_system_clock_core.SetStandardNetworkClockSufficientAccuracy(
167 sufficient_accuracy);
168 standard_network_system_clock_core.MarkAsInitialized();
169 }
170
171 void SetupStandardUserSystemClock(Core::System& system_, bool is_automatic_correction_enabled,
172 Clock::SteadyClockTimePoint steady_clock_time_point) {
173 if (standard_user_system_clock_core.SetAutomaticCorrectionEnabled(
174 system_, is_automatic_correction_enabled) != ResultSuccess) {
175 ASSERT(false);
176 return;
177 }
178
179 standard_user_system_clock_core.SetAutomaticCorrectionUpdatedTime(steady_clock_time_point);
180 standard_user_system_clock_core.MarkAsInitialized();
181 shared_memory.SetAutomaticCorrectionEnabled(is_automatic_correction_enabled);
182 }
183
184 void SetupEphemeralNetworkSystemClock() {
185 ephemeral_network_system_clock_core.SetUpdateCallbackInstance(
186 ephemeral_network_system_clock_context_writer);
187 ephemeral_network_system_clock_core.MarkAsInitialized();
188 }
189
190 void UpdateLocalSystemClockTime(Core::System& system_, s64 posix_time) {
191 const auto timespan{Clock::TimeSpanType::FromSeconds(posix_time)};
192 if (GetStandardLocalSystemClockCore()
193 .SetCurrentTime(system_, timespan.ToSeconds())
194 .IsError()) {
195 ASSERT(false);
196 return;
197 }
198 }
199
200 SharedMemory shared_memory;
201
202 Clock::StandardSteadyClockCore standard_steady_clock_core;
203 Clock::TickBasedSteadyClockCore tick_based_steady_clock_core;
204 Clock::StandardLocalSystemClockCore standard_local_system_clock_core;
205 Clock::StandardNetworkSystemClockCore standard_network_system_clock_core;
206 Clock::StandardUserSystemClockCore standard_user_system_clock_core;
207 Clock::EphemeralNetworkSystemClockCore ephemeral_network_system_clock_core;
208
209 std::shared_ptr<Clock::LocalSystemClockContextWriter> local_system_clock_context_writer;
210 std::shared_ptr<Clock::NetworkSystemClockContextWriter> network_system_clock_context_writer;
211 std::shared_ptr<Clock::EphemeralNetworkSystemClockContextWriter>
212 ephemeral_network_system_clock_context_writer;
213
214 TimeZone::TimeZoneContentManager time_zone_content_manager;
215};
216
217TimeManager::TimeManager(Core::System& system_) : system{system_} {}
218
219TimeManager::~TimeManager() = default;
220
221void TimeManager::Initialize() {
222 impl = std::make_unique<Impl>(system);
223
224 // Time zones can only be initialized after impl is valid
225 impl->time_zone_content_manager.Initialize(*this);
226}
227
228Clock::StandardSteadyClockCore& TimeManager::GetStandardSteadyClockCore() {
229 return impl->standard_steady_clock_core;
230}
231
232const Clock::StandardSteadyClockCore& TimeManager::GetStandardSteadyClockCore() const {
233 return impl->standard_steady_clock_core;
234}
235
236Clock::StandardLocalSystemClockCore& TimeManager::GetStandardLocalSystemClockCore() {
237 return impl->standard_local_system_clock_core;
238}
239
240const Clock::StandardLocalSystemClockCore& TimeManager::GetStandardLocalSystemClockCore() const {
241 return impl->standard_local_system_clock_core;
242}
243
244Clock::StandardNetworkSystemClockCore& TimeManager::GetStandardNetworkSystemClockCore() {
245 return impl->standard_network_system_clock_core;
246}
247
248const Clock::StandardNetworkSystemClockCore& TimeManager::GetStandardNetworkSystemClockCore()
249 const {
250 return impl->standard_network_system_clock_core;
251}
252
253Clock::StandardUserSystemClockCore& TimeManager::GetStandardUserSystemClockCore() {
254 return impl->standard_user_system_clock_core;
255}
256
257const Clock::StandardUserSystemClockCore& TimeManager::GetStandardUserSystemClockCore() const {
258 return impl->standard_user_system_clock_core;
259}
260
261TimeZone::TimeZoneContentManager& TimeManager::GetTimeZoneContentManager() {
262 return impl->time_zone_content_manager;
263}
264
265const TimeZone::TimeZoneContentManager& TimeManager::GetTimeZoneContentManager() const {
266 return impl->time_zone_content_manager;
267}
268
269SharedMemory& TimeManager::GetSharedMemory() {
270 return impl->shared_memory;
271}
272
273const SharedMemory& TimeManager::GetSharedMemory() const {
274 return impl->shared_memory;
275}
276
277void TimeManager::Shutdown() {
278 impl.reset();
279}
280
281void TimeManager::UpdateLocalSystemClockTime(s64 posix_time) {
282 impl->UpdateLocalSystemClockTime(system, posix_time);
283}
284
285void TimeManager::SetupTimeZoneManager(std::string location_name,
286 Clock::SteadyClockTimePoint time_zone_updated_time_point,
287 std::vector<std::string> location_names,
288 u128 time_zone_rule_version,
289 FileSys::VirtualFile& vfs_file) {
290 impl->SetupTimeZoneManager(location_name, time_zone_updated_time_point, location_names,
291 time_zone_rule_version, vfs_file);
292}
293} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_manager.h b/src/core/hle/service/time/time_manager.h
deleted file mode 100644
index 84572dbfa..000000000
--- a/src/core/hle/service/time/time_manager.h
+++ /dev/null
@@ -1,74 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/file_sys/vfs_types.h"
8#include "core/hle/service/time/clock_types.h"
9#include "core/hle/service/time/standard_local_system_clock_core.h"
10#include "core/hle/service/time/standard_network_system_clock_core.h"
11#include "core/hle/service/time/standard_steady_clock_core.h"
12#include "core/hle/service/time/standard_user_system_clock_core.h"
13#include "core/hle/service/time/time_sharedmemory.h"
14#include "core/hle/service/time/time_zone_content_manager.h"
15
16namespace Service::Time {
17
18namespace Clock {
19class EphemeralNetworkSystemClockContextWriter;
20class LocalSystemClockContextWriter;
21class NetworkSystemClockContextWriter;
22} // namespace Clock
23
24// Parts of this implementation were based on Ryujinx (https://github.com/Ryujinx/Ryujinx/pull/783).
25// This code was released under public domain.
26
27class TimeManager final {
28public:
29 explicit TimeManager(Core::System& system_);
30 ~TimeManager();
31
32 void Initialize();
33
34 Clock::StandardSteadyClockCore& GetStandardSteadyClockCore();
35
36 const Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() const;
37
38 Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore();
39
40 const Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() const;
41
42 Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore();
43
44 const Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() const;
45
46 Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore();
47
48 const Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() const;
49
50 TimeZone::TimeZoneContentManager& GetTimeZoneContentManager();
51
52 const TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() const;
53
54 void UpdateLocalSystemClockTime(s64 posix_time);
55
56 SharedMemory& GetSharedMemory();
57
58 const SharedMemory& GetSharedMemory() const;
59
60 void Shutdown();
61
62 void SetupTimeZoneManager(std::string location_name,
63 Clock::SteadyClockTimePoint time_zone_updated_time_point,
64 std::vector<std::string> location_names, u128 time_zone_rule_version,
65 FileSys::VirtualFile& vfs_file);
66
67private:
68 Core::System& system;
69
70 struct Impl;
71 std::unique_ptr<Impl> impl;
72};
73
74} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp
deleted file mode 100644
index a00676669..000000000
--- a/src/core/hle/service/time/time_sharedmemory.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/core_timing.h"
6#include "core/hardware_properties.h"
7#include "core/hle/kernel/kernel.h"
8#include "core/hle/service/time/clock_types.h"
9#include "core/hle/service/time/steady_clock_core.h"
10#include "core/hle/service/time/time_sharedmemory.h"
11
12namespace Service::Time {
13
14static constexpr std::size_t SHARED_MEMORY_SIZE{0x1000};
15
16SharedMemory::SharedMemory(Core::System& system_) : system(system_) {
17 std::memset(system.Kernel().GetTimeSharedMem().GetPointer(), 0, SHARED_MEMORY_SIZE);
18}
19
20SharedMemory::~SharedMemory() = default;
21
22void SharedMemory::SetupStandardSteadyClock(const Common::UUID& clock_source_id,
23 Clock::TimeSpanType current_time_point) {
24 const Clock::TimeSpanType ticks_time_span{
25 Clock::TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>(
26 system.CoreTiming().GetClockTicks())};
27 const Clock::SteadyClockContext context{
28 static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds),
29 clock_source_id};
30 StoreToLockFreeAtomicType(&GetFormat()->standard_steady_clock_timepoint, context);
31}
32
33void SharedMemory::UpdateLocalSystemClockContext(const Clock::SystemClockContext& context) {
34 // lower and upper are related to the measurement point for the steady time point,
35 // and compare equal on boot
36 const s64 time_point_ns = context.steady_time_point.time_point * 1'000'000'000LL;
37
38 // This adjusts for some sort of time skew
39 // Both 0 on boot
40 const s64 diff_scale = 0;
41 const u32 shift_amount = 0;
42
43 const Clock::ContinuousAdjustmentTimePoint adjustment{
44 .measurement_offset = system.CoreTiming().GetGlobalTimeNs().count(),
45 .diff_scale = diff_scale,
46 .shift_amount = shift_amount,
47 .lower = time_point_ns,
48 .upper = time_point_ns,
49 .clock_source_id = context.steady_time_point.clock_source_id,
50 };
51
52 StoreToLockFreeAtomicType(&GetFormat()->continuous_adjustment_timepoint, adjustment);
53 StoreToLockFreeAtomicType(&GetFormat()->standard_local_system_clock_context, context);
54}
55
56void SharedMemory::UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context) {
57 StoreToLockFreeAtomicType(&GetFormat()->standard_network_system_clock_context, context);
58}
59
60void SharedMemory::SetAutomaticCorrectionEnabled(bool is_enabled) {
61 StoreToLockFreeAtomicType(
62 &GetFormat()->is_standard_user_system_clock_automatic_correction_enabled, is_enabled);
63}
64
65SharedMemory::Format* SharedMemory::GetFormat() {
66 return reinterpret_cast<SharedMemory::Format*>(system.Kernel().GetTimeSharedMem().GetPointer());
67}
68
69} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_sharedmemory.h b/src/core/hle/service/time/time_sharedmemory.h
deleted file mode 100644
index c89be9765..000000000
--- a/src/core/hle/service/time/time_sharedmemory.h
+++ /dev/null
@@ -1,89 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "common/uuid.h"
8#include "core/hle/kernel/k_shared_memory.h"
9#include "core/hle/service/time/clock_types.h"
10
11namespace Service::Time {
12
13// Note: this type is not safe for concurrent writes.
14template <typename T>
15struct LockFreeAtomicType {
16 u32 counter_;
17 std::array<T, 2> value_;
18};
19
20template <typename T>
21static inline void StoreToLockFreeAtomicType(LockFreeAtomicType<T>* p, const T& value) {
22 // Get the current counter.
23 auto counter = p->counter_;
24
25 // Increment the counter.
26 ++counter;
27
28 // Store the updated value.
29 p->value_[counter % 2] = value;
30
31 // Fence memory.
32 std::atomic_thread_fence(std::memory_order_release);
33
34 // Set the updated counter.
35 p->counter_ = counter;
36}
37
38template <typename T>
39static inline T LoadFromLockFreeAtomicType(const LockFreeAtomicType<T>* p) {
40 while (true) {
41 // Get the counter.
42 auto counter = p->counter_;
43
44 // Get the value.
45 auto value = p->value_[counter % 2];
46
47 // Fence memory.
48 std::atomic_thread_fence(std::memory_order_acquire);
49
50 // Check that the counter matches.
51 if (counter == p->counter_) {
52 return value;
53 }
54 }
55}
56
57class SharedMemory final {
58public:
59 explicit SharedMemory(Core::System& system_);
60 ~SharedMemory();
61
62 // Shared memory format
63 struct Format {
64 LockFreeAtomicType<Clock::StandardSteadyClockTimePointType> standard_steady_clock_timepoint;
65 LockFreeAtomicType<Clock::SystemClockContext> standard_local_system_clock_context;
66 LockFreeAtomicType<Clock::SystemClockContext> standard_network_system_clock_context;
67 LockFreeAtomicType<bool> is_standard_user_system_clock_automatic_correction_enabled;
68 LockFreeAtomicType<Clock::ContinuousAdjustmentTimePoint> continuous_adjustment_timepoint;
69 };
70 static_assert(offsetof(Format, standard_steady_clock_timepoint) == 0x0);
71 static_assert(offsetof(Format, standard_local_system_clock_context) == 0x38);
72 static_assert(offsetof(Format, standard_network_system_clock_context) == 0x80);
73 static_assert(offsetof(Format, is_standard_user_system_clock_automatic_correction_enabled) ==
74 0xc8);
75 static_assert(offsetof(Format, continuous_adjustment_timepoint) == 0xd0);
76 static_assert(sizeof(Format) == 0x148, "Format is an invalid size");
77
78 void SetupStandardSteadyClock(const Common::UUID& clock_source_id,
79 Clock::TimeSpanType current_time_point);
80 void UpdateLocalSystemClockContext(const Clock::SystemClockContext& context);
81 void UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context);
82 void SetAutomaticCorrectionEnabled(bool is_enabled);
83 Format* GetFormat();
84
85private:
86 Core::System& system;
87};
88
89} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_zone_content_manager.cpp b/src/core/hle/service/time/time_zone_content_manager.cpp
deleted file mode 100644
index 1b96de37a..000000000
--- a/src/core/hle/service/time/time_zone_content_manager.cpp
+++ /dev/null
@@ -1,151 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <chrono>
5#include <sstream>
6#include <utility>
7
8#include "common/logging/log.h"
9#include "common/settings.h"
10#include "common/time_zone.h"
11#include "core/core.h"
12#include "core/file_sys/content_archive.h"
13#include "core/file_sys/nca_metadata.h"
14#include "core/file_sys/registered_cache.h"
15#include "core/file_sys/romfs.h"
16#include "core/file_sys/system_archive/system_archive.h"
17#include "core/file_sys/vfs.h"
18#include "core/file_sys/vfs_types.h"
19#include "core/hle/result.h"
20#include "core/hle/service/filesystem/filesystem.h"
21#include "core/hle/service/time/errors.h"
22#include "core/hle/service/time/time_manager.h"
23#include "core/hle/service/time/time_zone_content_manager.h"
24
25namespace Service::Time::TimeZone {
26
27constexpr u64 time_zone_binary_titleid{0x010000000000080E};
28
29static FileSys::VirtualDir GetTimeZoneBinary(Core::System& system) {
30 const auto* nand{system.GetFileSystemController().GetSystemNANDContents()};
31 const auto nca{nand->GetEntry(time_zone_binary_titleid, FileSys::ContentRecordType::Data)};
32
33 FileSys::VirtualFile romfs;
34 if (nca) {
35 romfs = nca->GetRomFS();
36 }
37
38 if (!romfs) {
39 romfs = FileSys::SystemArchive::SynthesizeSystemArchive(time_zone_binary_titleid);
40 }
41
42 if (!romfs) {
43 LOG_ERROR(Service_Time, "Failed to find or synthesize {:016X!}", time_zone_binary_titleid);
44 return {};
45 }
46
47 return FileSys::ExtractRomFS(romfs);
48}
49
50static std::vector<std::string> BuildLocationNameCache(
51 const FileSys::VirtualDir& time_zone_binary) {
52 if (!time_zone_binary) {
53 LOG_ERROR(Service_Time, "Failed to extract RomFS for {:016X}!", time_zone_binary_titleid);
54 return {};
55 }
56
57 const FileSys::VirtualFile binary_list{time_zone_binary->GetFile("binaryList.txt")};
58 if (!binary_list) {
59 LOG_ERROR(Service_Time, "{:016X} has no file binaryList.txt!", time_zone_binary_titleid);
60 return {};
61 }
62
63 std::vector<char> raw_data(binary_list->GetSize() + 1);
64 binary_list->ReadBytes<char>(raw_data.data(), binary_list->GetSize());
65
66 std::stringstream data_stream{raw_data.data()};
67 std::string name;
68 std::vector<std::string> location_name_cache;
69 while (std::getline(data_stream, name)) {
70 name.pop_back(); // Remove carriage return
71 location_name_cache.emplace_back(std::move(name));
72 }
73 return location_name_cache;
74}
75
76TimeZoneContentManager::TimeZoneContentManager(Core::System& system_)
77 : system{system_}, time_zone_binary{GetTimeZoneBinary(system)},
78 location_name_cache{BuildLocationNameCache(time_zone_binary)} {}
79
80void TimeZoneContentManager::Initialize(TimeManager& time_manager) {
81 const auto timezone_setting =
82 Settings::GetTimeZoneString(Settings::values.time_zone_index.GetValue());
83
84 if (FileSys::VirtualFile vfs_file;
85 GetTimeZoneInfoFile(timezone_setting, vfs_file) == ResultSuccess) {
86 const auto time_point{
87 time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system)};
88 time_manager.SetupTimeZoneManager(timezone_setting, time_point, location_name_cache, {},
89 vfs_file);
90 } else {
91 time_zone_manager.MarkAsInitialized();
92 }
93}
94
95Result TimeZoneContentManager::LoadTimeZoneRule(TimeZoneRule& rules,
96 const std::string& location_name) const {
97 FileSys::VirtualFile vfs_file;
98 if (const Result result{GetTimeZoneInfoFile(location_name, vfs_file)};
99 result != ResultSuccess) {
100 return result;
101 }
102
103 return time_zone_manager.ParseTimeZoneRuleBinary(rules, vfs_file);
104}
105
106bool TimeZoneContentManager::IsLocationNameValid(const std::string& location_name) const {
107 return std::find(location_name_cache.begin(), location_name_cache.end(), location_name) !=
108 location_name_cache.end();
109}
110
111Result TimeZoneContentManager::GetTimeZoneInfoFile(const std::string& location_name,
112 FileSys::VirtualFile& vfs_file) const {
113 if (!IsLocationNameValid(location_name)) {
114 return ERROR_TIME_NOT_FOUND;
115 }
116
117 if (!time_zone_binary) {
118 LOG_ERROR(Service_Time, "Failed to extract RomFS for {:016X}!", time_zone_binary_titleid);
119 return ERROR_TIME_NOT_FOUND;
120 }
121
122 const FileSys::VirtualDir zoneinfo_dir{time_zone_binary->GetSubdirectory("zoneinfo")};
123 if (!zoneinfo_dir) {
124 LOG_ERROR(Service_Time, "{:016X} has no directory zoneinfo!", time_zone_binary_titleid);
125 return ERROR_TIME_NOT_FOUND;
126 }
127
128 vfs_file = zoneinfo_dir->GetFileRelative(location_name);
129 if (!vfs_file) {
130 LOG_WARNING(Service_Time, "{:016X} has no file \"{}\"! Using system timezone.",
131 time_zone_binary_titleid, location_name);
132 const std::string system_time_zone{Common::TimeZone::FindSystemTimeZone()};
133 vfs_file = zoneinfo_dir->GetFile(system_time_zone);
134 }
135
136 if (!vfs_file) {
137 LOG_WARNING(Service_Time, "{:016X} has no file \"{}\"! Using default timezone.",
138 time_zone_binary_titleid, location_name);
139 vfs_file = zoneinfo_dir->GetFile(Common::TimeZone::GetDefaultTimeZone());
140 }
141
142 if (!vfs_file) {
143 LOG_ERROR(Service_Time, "{:016X} has no file \"{}\"!", time_zone_binary_titleid,
144 location_name);
145 return ERROR_TIME_NOT_FOUND;
146 }
147
148 return ResultSuccess;
149}
150
151} // namespace Service::Time::TimeZone
diff --git a/src/core/hle/service/time/time_zone_content_manager.h b/src/core/hle/service/time/time_zone_content_manager.h
deleted file mode 100644
index a6f9698bc..000000000
--- a/src/core/hle/service/time/time_zone_content_manager.h
+++ /dev/null
@@ -1,49 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7#include <vector>
8
9#include "core/file_sys/vfs_types.h"
10#include "core/hle/service/time/time_zone_manager.h"
11
12namespace Core {
13class System;
14}
15
16namespace Service::Time {
17class TimeManager;
18}
19
20namespace Service::Time::TimeZone {
21
22class TimeZoneContentManager final {
23public:
24 explicit TimeZoneContentManager(Core::System& system_);
25
26 void Initialize(TimeManager& time_manager);
27
28 TimeZoneManager& GetTimeZoneManager() {
29 return time_zone_manager;
30 }
31
32 const TimeZoneManager& GetTimeZoneManager() const {
33 return time_zone_manager;
34 }
35
36 Result LoadTimeZoneRule(TimeZoneRule& rules, const std::string& location_name) const;
37
38private:
39 bool IsLocationNameValid(const std::string& location_name) const;
40 Result GetTimeZoneInfoFile(const std::string& location_name,
41 FileSys::VirtualFile& vfs_file) const;
42
43 Core::System& system;
44 TimeZoneManager time_zone_manager;
45 const FileSys::VirtualDir time_zone_binary;
46 const std::vector<std::string> location_name_cache;
47};
48
49} // namespace Service::Time::TimeZone
diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp
deleted file mode 100644
index 205371a26..000000000
--- a/src/core/hle/service/time/time_zone_manager.cpp
+++ /dev/null
@@ -1,1182 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <climits>
5#include <limits>
6
7#include "common/assert.h"
8#include "common/logging/log.h"
9#include "core/file_sys/content_archive.h"
10#include "core/file_sys/nca_metadata.h"
11#include "core/file_sys/registered_cache.h"
12#include "core/hle/service/time/time_zone_manager.h"
13#include "core/hle/service/time/time_zone_types.h"
14
15namespace Service::Time::TimeZone {
16
17static constexpr s32 epoch_year{1970};
18static constexpr s32 year_base{1900};
19static constexpr s32 epoch_week_day{4};
20static constexpr s32 seconds_per_minute{60};
21static constexpr s32 minutes_per_hour{60};
22static constexpr s32 hours_per_day{24};
23static constexpr s32 days_per_week{7};
24static constexpr s32 days_per_normal_year{365};
25static constexpr s32 days_per_leap_year{366};
26static constexpr s32 months_per_year{12};
27static constexpr s32 seconds_per_hour{seconds_per_minute * minutes_per_hour};
28static constexpr s32 seconds_per_day{seconds_per_hour * hours_per_day};
29static constexpr s32 years_per_repeat{400};
30static constexpr s64 average_seconds_per_year{31556952};
31static constexpr s64 seconds_per_repeat{years_per_repeat * average_seconds_per_year};
32
33struct Rule {
34 enum class Type : u32 { JulianDay, DayOfYear, MonthNthDayOfWeek };
35 Type rule_type{};
36 s32 day{};
37 s32 week{};
38 s32 month{};
39 s32 transition_time{};
40};
41
42struct CalendarTimeInternal {
43 s64 year{};
44 s8 month{};
45 s8 day{};
46 s8 hour{};
47 s8 minute{};
48 s8 second{};
49 int Compare(const CalendarTimeInternal& other) const {
50 if (year != other.year) {
51 if (year < other.year) {
52 return -1;
53 }
54 return 1;
55 }
56 if (month != other.month) {
57 return month - other.month;
58 }
59 if (day != other.day) {
60 return day - other.day;
61 }
62 if (hour != other.hour) {
63 return hour - other.hour;
64 }
65 if (minute != other.minute) {
66 return minute - other.minute;
67 }
68 if (second != other.second) {
69 return second - other.second;
70 }
71 return {};
72 }
73};
74
75template <typename TResult, typename TOperand>
76static bool SafeAdd(TResult& result, TOperand op) {
77 result = result + op;
78 return true;
79}
80
81template <typename TResult, typename TUnit, typename TBase>
82static bool SafeNormalize(TResult& result, TUnit& unit, TBase base) {
83 TUnit delta{};
84 if (unit >= 0) {
85 delta = unit / base;
86 } else {
87 delta = -1 - (-1 - unit) / base;
88 }
89 unit -= delta * base;
90 return SafeAdd(result, delta);
91}
92
93template <typename T>
94static constexpr bool IsLeapYear(T year) {
95 return ((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0);
96}
97
98template <typename T>
99static constexpr T GetYearLengthInDays(T year) {
100 return IsLeapYear(year) ? days_per_leap_year : days_per_normal_year;
101}
102
103static constexpr s64 GetLeapDaysFromYearPositive(s64 year) {
104 return year / 4 - year / 100 + year / years_per_repeat;
105}
106
107static constexpr s64 GetLeapDaysFromYear(s64 year) {
108 if (year < 0) {
109 return -1 - GetLeapDaysFromYearPositive(-1 - year);
110 } else {
111 return GetLeapDaysFromYearPositive(year);
112 }
113}
114
115static constexpr s8 GetMonthLength(bool is_leap_year, int month) {
116 constexpr std::array<s8, 12> month_lengths{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
117 constexpr std::array<s8, 12> month_lengths_leap{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
118 return is_leap_year ? month_lengths_leap[month] : month_lengths[month];
119}
120
121static constexpr bool IsDigit(char value) {
122 return value >= '0' && value <= '9';
123}
124
125static constexpr int GetQZName(const char* name, int offset, char delimiter) {
126 while (name[offset] != '\0' && name[offset] != delimiter) {
127 offset++;
128 }
129 return offset;
130}
131
132static constexpr int GetTZName(const char* name, int offset) {
133 char c;
134
135 while ((c = name[offset]) != '\0' && !IsDigit(c) && c != ',' && c != '-' && c != '+') {
136 ++offset;
137 }
138 return offset;
139}
140
141static constexpr bool GetInteger(const char* name, int& offset, int& value, int min, int max) {
142 value = 0;
143 char temp{name[offset]};
144 if (!IsDigit(temp)) {
145 return {};
146 }
147 do {
148 value = value * 10 + (temp - '0');
149 if (value > max) {
150 return {};
151 }
152 offset++;
153 temp = name[offset];
154 } while (IsDigit(temp));
155
156 return value >= min;
157}
158
159static constexpr bool GetSeconds(const char* name, int& offset, int& seconds) {
160 seconds = 0;
161 int value{};
162 if (!GetInteger(name, offset, value, 0, hours_per_day * days_per_week - 1)) {
163 return {};
164 }
165 seconds = value * seconds_per_hour;
166
167 if (name[offset] == ':') {
168 offset++;
169 if (!GetInteger(name, offset, value, 0, minutes_per_hour - 1)) {
170 return {};
171 }
172 seconds += value * seconds_per_minute;
173 if (name[offset] == ':') {
174 offset++;
175 if (!GetInteger(name, offset, value, 0, seconds_per_minute)) {
176 return {};
177 }
178 seconds += value;
179 }
180 }
181 return true;
182}
183
184static constexpr bool GetOffset(const char* name, int& offset, int& value) {
185 bool is_negative{};
186 if (name[offset] == '-') {
187 is_negative = true;
188 offset++;
189 } else if (name[offset] == '+') {
190 offset++;
191 }
192 if (!GetSeconds(name, offset, value)) {
193 return {};
194 }
195 if (is_negative) {
196 value = -value;
197 }
198 return true;
199}
200
201static constexpr bool GetRule(const char* name, int& position, Rule& rule) {
202 bool is_valid{};
203 if (name[position] == 'J') {
204 position++;
205 rule.rule_type = Rule::Type::JulianDay;
206 is_valid = GetInteger(name, position, rule.day, 1, days_per_normal_year);
207 } else if (name[position] == 'M') {
208 position++;
209 rule.rule_type = Rule::Type::MonthNthDayOfWeek;
210 is_valid = GetInteger(name, position, rule.month, 1, months_per_year);
211 if (!is_valid) {
212 return {};
213 }
214 if (name[position++] != '.') {
215 return {};
216 }
217 is_valid = GetInteger(name, position, rule.week, 1, 5);
218 if (!is_valid) {
219 return {};
220 }
221 if (name[position++] != '.') {
222 return {};
223 }
224 is_valid = GetInteger(name, position, rule.day, 0, days_per_week - 1);
225 } else if (isdigit(name[position])) {
226 rule.rule_type = Rule::Type::DayOfYear;
227 is_valid = GetInteger(name, position, rule.day, 0, days_per_leap_year - 1);
228 } else {
229 return {};
230 }
231 if (!is_valid) {
232 return {};
233 }
234 if (name[position] == '/') {
235 position++;
236 return GetOffset(name, position, rule.transition_time);
237 } else {
238 rule.transition_time = 2 * seconds_per_hour;
239 }
240 return true;
241}
242
243static constexpr int TransitionTime(int year, Rule rule, int offset) {
244 int value{};
245 switch (rule.rule_type) {
246 case Rule::Type::JulianDay:
247 value = (rule.day - 1) * seconds_per_day;
248 if (IsLeapYear(year) && rule.day >= 60) {
249 value += seconds_per_day;
250 }
251 break;
252 case Rule::Type::DayOfYear:
253 value = rule.day * seconds_per_day;
254 break;
255 case Rule::Type::MonthNthDayOfWeek: {
256 // Use Zeller's Congruence (https://en.wikipedia.org/wiki/Zeller%27s_congruence) to
257 // calculate the day of the week for any Julian or Gregorian calendar date.
258 const int m1{(rule.month + 9) % 12 + 1};
259 const int yy0{(rule.month <= 2) ? (year - 1) : year};
260 const int yy1{yy0 / 100};
261 const int yy2{yy0 % 100};
262 int day_of_week{((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7};
263
264 if (day_of_week < 0) {
265 day_of_week += days_per_week;
266 }
267 int day{rule.day - day_of_week};
268 if (day < 0) {
269 day += days_per_week;
270 }
271 for (int i{1}; i < rule.week; i++) {
272 if (day + days_per_week >= GetMonthLength(IsLeapYear(year), rule.month - 1)) {
273 break;
274 }
275 day += days_per_week;
276 }
277
278 value = day * seconds_per_day;
279 for (int index{}; index < rule.month - 1; ++index) {
280 value += GetMonthLength(IsLeapYear(year), index) * seconds_per_day;
281 }
282 break;
283 }
284 default:
285 ASSERT(false);
286 break;
287 }
288 return value + rule.transition_time + offset;
289}
290
291static bool ParsePosixName(const char* name, TimeZoneRule& rule) {
292 static constexpr char default_rule[]{",M4.1.0,M10.5.0"};
293 const char* std_name{name};
294 int std_len{};
295 int offset{};
296 int std_offset{};
297
298 if (name[offset] == '<') {
299 offset++;
300 std_name = name + offset;
301 const int std_name_offset{offset};
302 offset = GetQZName(name, offset, '>');
303 if (name[offset] != '>') {
304 return {};
305 }
306 std_len = offset - std_name_offset;
307 offset++;
308 } else {
309 offset = GetTZName(name, offset);
310 std_len = offset;
311 }
312 if (std_len == 0) {
313 return {};
314 }
315 if (!GetOffset(name, offset, std_offset)) {
316 return {};
317 }
318
319 int char_count{std_len + 1};
320 int dest_len{};
321 int dest_offset{};
322 const char* dest_name{name + offset};
323 if (rule.chars.size() < std::size_t(char_count)) {
324 return {};
325 }
326
327 if (name[offset] != '\0') {
328 if (name[offset] == '<') {
329 dest_name = name + (++offset);
330 const int dest_name_offset{offset};
331 offset = GetQZName(name, offset, '>');
332 if (name[offset] != '>') {
333 return {};
334 }
335 dest_len = offset - dest_name_offset;
336 offset++;
337 } else {
338 dest_name = name + (offset);
339 offset = GetTZName(name, offset);
340 dest_len = offset;
341 }
342 if (dest_len == 0) {
343 return {};
344 }
345 char_count += dest_len + 1;
346 if (rule.chars.size() < std::size_t(char_count)) {
347 return {};
348 }
349 if (name[offset] != '\0' && name[offset] != ',' && name[offset] != ';') {
350 if (!GetOffset(name, offset, dest_offset)) {
351 return {};
352 }
353 } else {
354 dest_offset = std_offset - seconds_per_hour;
355 }
356 if (name[offset] == '\0') {
357 name = default_rule;
358 offset = 0;
359 }
360 if (name[offset] == ',' || name[offset] == ';') {
361 offset++;
362
363 Rule start{};
364 if (!GetRule(name, offset, start)) {
365 return {};
366 }
367 if (name[offset++] != ',') {
368 return {};
369 }
370
371 Rule end{};
372 if (!GetRule(name, offset, end)) {
373 return {};
374 }
375 if (name[offset] != '\0') {
376 return {};
377 }
378
379 rule.type_count = 2;
380 rule.ttis[0].gmt_offset = -dest_offset;
381 rule.ttis[0].is_dst = true;
382 rule.ttis[0].abbreviation_list_index = std_len + 1;
383 rule.ttis[1].gmt_offset = -std_offset;
384 rule.ttis[1].is_dst = false;
385 rule.ttis[1].abbreviation_list_index = 0;
386 rule.default_type = 0;
387
388 s64 jan_first{};
389 int time_count{};
390 int jan_offset{};
391 int year_beginning{epoch_year};
392 do {
393 const int year_seconds{GetYearLengthInDays(year_beginning - 1) * seconds_per_day};
394 year_beginning--;
395 if (!SafeAdd(jan_first, -year_seconds)) {
396 jan_offset = -year_seconds;
397 break;
398 }
399 } while (epoch_year - years_per_repeat / 2 < year_beginning);
400
401 int year_limit{year_beginning + years_per_repeat + 1};
402 int year{};
403 for (year = year_beginning; year < year_limit; year++) {
404 int start_time{TransitionTime(year, start, std_offset)};
405 int end_time{TransitionTime(year, end, dest_offset)};
406 const int year_seconds{GetYearLengthInDays(year) * seconds_per_day};
407 const bool is_reversed{end_time < start_time};
408 if (is_reversed) {
409 int swap{start_time};
410 start_time = end_time;
411 end_time = swap;
412 }
413
414 if (is_reversed ||
415 (start_time < end_time &&
416 (end_time - start_time < (year_seconds + (std_offset - dest_offset))))) {
417 if (rule.ats.size() - 2 < std::size_t(time_count)) {
418 break;
419 }
420
421 rule.ats[time_count] = jan_first;
422 if (SafeAdd(rule.ats[time_count], jan_offset + start_time)) {
423 rule.types[time_count++] = is_reversed ? 1 : 0;
424 } else if (jan_offset != 0) {
425 rule.default_type = is_reversed ? 1 : 0;
426 }
427
428 rule.ats[time_count] = jan_first;
429 if (SafeAdd(rule.ats[time_count], jan_offset + end_time)) {
430 rule.types[time_count++] = is_reversed ? 0 : 1;
431 year_limit = year + years_per_repeat + 1;
432 } else if (jan_offset != 0) {
433 rule.default_type = is_reversed ? 0 : 1;
434 }
435 }
436 if (!SafeAdd(jan_first, jan_offset + year_seconds)) {
437 break;
438 }
439 jan_offset = 0;
440 }
441 rule.time_count = time_count;
442 if (time_count == 0) {
443 rule.type_count = 1;
444 } else if (years_per_repeat < year - year_beginning) {
445 rule.go_back = true;
446 rule.go_ahead = true;
447 }
448 } else {
449 if (name[offset] == '\0') {
450 return {};
451 }
452
453 s64 their_std_offset{};
454 for (int index{}; index < rule.time_count; ++index) {
455 const s8 type{rule.types[index]};
456 if (rule.ttis[type].is_standard_time_daylight) {
457 their_std_offset = -rule.ttis[type].gmt_offset;
458 }
459 }
460
461 s64 their_offset{their_std_offset};
462 for (int index{}; index < rule.time_count; ++index) {
463 const s8 type{rule.types[index]};
464 rule.types[index] = rule.ttis[type].is_dst ? 1 : 0;
465 if (!rule.ttis[type].is_gmt) {
466 if (!rule.ttis[type].is_standard_time_daylight) {
467 rule.ats[index] += dest_offset - their_std_offset;
468 } else {
469 rule.ats[index] += std_offset - their_std_offset;
470 }
471 }
472 their_offset = -rule.ttis[type].gmt_offset;
473 if (!rule.ttis[type].is_dst) {
474 their_std_offset = their_offset;
475 }
476 }
477
478 if (rule.time_count > 0) {
479 UNIMPLEMENTED();
480 // TODO (lat9nq): Implement eggert/tz/localtime.c:tzparse:1329
481 // Seems to be unused in yuzu for now: I never hit the UNIMPLEMENTED in testing
482 }
483
484 rule.ttis[0].gmt_offset = -std_offset;
485 rule.ttis[0].is_dst = false;
486 rule.ttis[0].abbreviation_list_index = 0;
487 rule.ttis[1].gmt_offset = -dest_offset;
488 rule.ttis[1].is_dst = true;
489 rule.ttis[1].abbreviation_list_index = std_len + 1;
490 rule.type_count = 2;
491 rule.default_type = 0;
492 }
493 } else {
494 // Default is standard time
495 rule.type_count = 1;
496 rule.time_count = 0;
497 rule.default_type = 0;
498 rule.ttis[0].gmt_offset = -std_offset;
499 rule.ttis[0].is_dst = false;
500 rule.ttis[0].abbreviation_list_index = 0;
501 }
502
503 rule.char_count = char_count;
504 for (int index{}; index < std_len; ++index) {
505 rule.chars[index] = std_name[index];
506 }
507
508 rule.chars[std_len++] = '\0';
509 if (dest_len != 0) {
510 for (int index{}; index < dest_len; ++index) {
511 rule.chars[std_len + index] = dest_name[index];
512 }
513 rule.chars[std_len + dest_len] = '\0';
514 }
515
516 return true;
517}
518
519static bool ParseTimeZoneBinary(TimeZoneRule& time_zone_rule, FileSys::VirtualFile& vfs_file) {
520 TzifHeader header{};
521 if (vfs_file->ReadObject<TzifHeader>(&header) != sizeof(TzifHeader)) {
522 return {};
523 }
524
525 constexpr s32 time_zone_max_leaps{50};
526 constexpr s32 time_zone_max_chars{50};
527 constexpr s32 time_zone_max_times{1000};
528 if (!(0 <= header.leap_count && header.leap_count < time_zone_max_leaps &&
529 0 < header.type_count && header.type_count < s32(time_zone_rule.ttis.size()) &&
530 0 <= header.time_count && header.time_count < s32(time_zone_rule.ats.size()) &&
531 0 <= header.char_count && header.char_count < time_zone_max_chars &&
532 (header.ttis_std_count == header.type_count || header.ttis_std_count == 0) &&
533 (header.ttis_gmt_count == header.type_count || header.ttis_gmt_count == 0))) {
534 return {};
535 }
536 time_zone_rule.time_count = header.time_count;
537 time_zone_rule.type_count = header.type_count;
538 time_zone_rule.char_count = header.char_count;
539
540 int time_count{};
541 u64 read_offset = sizeof(TzifHeader);
542 for (int index{}; index < time_zone_rule.time_count; ++index) {
543 s64_be at{};
544 vfs_file->ReadObject<s64_be>(&at, read_offset);
545 time_zone_rule.types[index] = 1;
546 if (time_count != 0 && at <= time_zone_rule.ats[time_count - 1]) {
547 if (at < time_zone_rule.ats[time_count - 1]) {
548 return {};
549 }
550 time_zone_rule.types[index - 1] = 0;
551 time_count--;
552 }
553 time_zone_rule.ats[time_count++] = at;
554 read_offset += sizeof(s64_be);
555 }
556 time_count = 0;
557 for (int index{}; index < time_zone_rule.time_count; ++index) {
558 const u8 type{*vfs_file->ReadByte(read_offset)};
559 read_offset += sizeof(u8);
560 if (time_zone_rule.type_count <= type) {
561 return {};
562 }
563 if (time_zone_rule.types[index] != 0) {
564 time_zone_rule.types[time_count++] = type;
565 }
566 }
567 time_zone_rule.time_count = time_count;
568 for (int index{}; index < time_zone_rule.type_count; ++index) {
569 TimeTypeInfo& ttis{time_zone_rule.ttis[index]};
570 u32_be gmt_offset{};
571 vfs_file->ReadObject<u32_be>(&gmt_offset, read_offset);
572 read_offset += sizeof(u32_be);
573 ttis.gmt_offset = gmt_offset;
574
575 const u8 dst{*vfs_file->ReadByte(read_offset)};
576 read_offset += sizeof(u8);
577 if (dst >= 2) {
578 return {};
579 }
580 ttis.is_dst = dst != 0;
581
582 const s32 abbreviation_list_index{*vfs_file->ReadByte(read_offset)};
583 read_offset += sizeof(u8);
584 if (abbreviation_list_index >= time_zone_rule.char_count) {
585 return {};
586 }
587 ttis.abbreviation_list_index = abbreviation_list_index;
588 }
589
590 vfs_file->ReadArray(time_zone_rule.chars.data(), time_zone_rule.char_count, read_offset);
591 time_zone_rule.chars[time_zone_rule.char_count] = '\0';
592 read_offset += time_zone_rule.char_count;
593 for (int index{}; index < time_zone_rule.type_count; ++index) {
594 if (header.ttis_std_count == 0) {
595 time_zone_rule.ttis[index].is_standard_time_daylight = false;
596 } else {
597 const u8 dst{*vfs_file->ReadByte(read_offset)};
598 read_offset += sizeof(u8);
599 if (dst >= 2) {
600 return {};
601 }
602 time_zone_rule.ttis[index].is_standard_time_daylight = dst != 0;
603 }
604 }
605
606 for (int index{}; index < time_zone_rule.type_count; ++index) {
607 if (header.ttis_std_count == 0) {
608 time_zone_rule.ttis[index].is_gmt = false;
609 } else {
610 const u8 dst{*vfs_file->ReadByte(read_offset)};
611 read_offset += sizeof(u8);
612 if (dst >= 2) {
613 return {};
614 }
615 time_zone_rule.ttis[index].is_gmt = dst != 0;
616 }
617 }
618
619 const u64 position{(read_offset - sizeof(TzifHeader))};
620 const s64 bytes_read = s64(vfs_file->GetSize() - sizeof(TzifHeader) - position);
621 if (bytes_read < 0) {
622 return {};
623 }
624 constexpr s32 time_zone_name_max{255};
625 if (bytes_read > (time_zone_name_max + 1)) {
626 return {};
627 }
628
629 std::array<char, time_zone_name_max + 1> temp_name{};
630 vfs_file->ReadArray(temp_name.data(), bytes_read, read_offset);
631 if (bytes_read > 2 && temp_name[0] == '\n' && temp_name[bytes_read - 1] == '\n' &&
632 std::size_t(time_zone_rule.type_count) + 2 <= time_zone_rule.ttis.size()) {
633 temp_name[bytes_read - 1] = '\0';
634
635 std::array<char, time_zone_name_max> name{};
636 std::memcpy(name.data(), temp_name.data() + 1, std::size_t(bytes_read - 1));
637
638 // Fill in computed transition times with temp rule
639 TimeZoneRule temp_rule;
640 if (ParsePosixName(name.data(), temp_rule)) {
641 int have_abbreviation = 0;
642 int char_count = time_zone_rule.char_count;
643
644 for (int i = 0; i < temp_rule.type_count; i++) {
645 char* temp_abbreviation =
646 temp_rule.chars.data() + temp_rule.ttis[i].abbreviation_list_index;
647 int j;
648 for (j = 0; j < char_count; j++) {
649 if (std::strcmp(time_zone_rule.chars.data() + j, temp_abbreviation) == 0) {
650 temp_rule.ttis[i].abbreviation_list_index = j;
651 have_abbreviation++;
652 break;
653 }
654 }
655 if (j >= char_count) {
656 int temp_abbreviation_length = static_cast<int>(std::strlen(temp_abbreviation));
657 if (j + temp_abbreviation_length < time_zone_max_chars) {
658 std::strcpy(time_zone_rule.chars.data() + j, temp_abbreviation);
659 char_count = j + temp_abbreviation_length + 1;
660 temp_rule.ttis[i].abbreviation_list_index = j;
661 have_abbreviation++;
662 }
663 }
664 }
665
666 if (have_abbreviation == temp_rule.type_count) {
667 time_zone_rule.char_count = char_count;
668
669 // Original comment:
670 /* Ignore any trailing, no-op transitions generated
671 by zic as they don't help here and can run afoul
672 of bugs in zic 2016j or earlier. */
673 // This is possibly unnecessary for yuzu, since Nintendo doesn't run zic
674 while (1 < time_zone_rule.time_count &&
675 (time_zone_rule.types[time_zone_rule.time_count - 1] ==
676 time_zone_rule.types[time_zone_rule.time_count - 2])) {
677 time_zone_rule.time_count--;
678 }
679
680 for (int i = 0;
681 i < temp_rule.time_count && time_zone_rule.time_count < time_zone_max_times;
682 i++) {
683 const s64 transition_time = temp_rule.ats[i];
684 if (0 < time_zone_rule.time_count &&
685 transition_time <= time_zone_rule.ats[time_zone_rule.time_count - 1]) {
686 continue;
687 }
688
689 time_zone_rule.ats[time_zone_rule.time_count] = transition_time;
690 time_zone_rule.types[time_zone_rule.time_count] =
691 static_cast<s8>(time_zone_rule.type_count + temp_rule.types[i]);
692 time_zone_rule.time_count++;
693 }
694 for (int i = 0; i < temp_rule.type_count; i++) {
695 time_zone_rule.ttis[time_zone_rule.type_count++] = temp_rule.ttis[i];
696 }
697 }
698 }
699 }
700
701 const auto typesequiv = [](TimeZoneRule& rule, int a, int b) -> bool {
702 if (a < 0 || a >= rule.type_count || b < 0 || b >= rule.type_count) {
703 return {};
704 }
705
706 const struct TimeTypeInfo* ap = &rule.ttis[a];
707 const struct TimeTypeInfo* bp = &rule.ttis[b];
708
709 return (ap->gmt_offset == bp->gmt_offset && ap->is_dst == bp->is_dst &&
710 (std::strcmp(&rule.chars[ap->abbreviation_list_index],
711 &rule.chars[bp->abbreviation_list_index]) == 0));
712 };
713
714 if (time_zone_rule.type_count == 0) {
715 return {};
716 }
717 if (time_zone_rule.time_count > 1) {
718 if (time_zone_rule.ats[0] <= std::numeric_limits<s64>::max() - seconds_per_repeat) {
719 s64 repeatat = time_zone_rule.ats[0] + seconds_per_repeat;
720 int repeatattype = time_zone_rule.types[0];
721 for (int i = 1; i < time_zone_rule.time_count; ++i) {
722 if (time_zone_rule.ats[i] == repeatat &&
723 typesequiv(time_zone_rule, time_zone_rule.types[i], repeatattype)) {
724 time_zone_rule.go_back = true;
725 break;
726 }
727 }
728 }
729 if (std::numeric_limits<s64>::min() + seconds_per_repeat <=
730 time_zone_rule.ats[time_zone_rule.time_count - 1]) {
731 s64 repeatat = time_zone_rule.ats[time_zone_rule.time_count - 1] - seconds_per_repeat;
732 int repeatattype = time_zone_rule.types[time_zone_rule.time_count - 1];
733 for (int i = time_zone_rule.time_count; i >= 0; --i) {
734 if (time_zone_rule.ats[i] == repeatat &&
735 typesequiv(time_zone_rule, time_zone_rule.types[i], repeatattype)) {
736 time_zone_rule.go_ahead = true;
737 break;
738 }
739 }
740 }
741 }
742
743 s32 default_type{};
744
745 for (default_type = 0; default_type < time_zone_rule.time_count; default_type++) {
746 if (time_zone_rule.types[default_type] == 0) {
747 break;
748 }
749 }
750
751 default_type = default_type < time_zone_rule.time_count ? -1 : 0;
752 if (default_type < 0 && time_zone_rule.time_count > 0 &&
753 time_zone_rule.ttis[time_zone_rule.types[0]].is_dst) {
754 default_type = time_zone_rule.types[0];
755 while (--default_type >= 0) {
756 if (!time_zone_rule.ttis[default_type].is_dst) {
757 break;
758 }
759 }
760 }
761 if (default_type < 0) {
762 default_type = 0;
763 while (time_zone_rule.ttis[default_type].is_dst) {
764 if (++default_type >= time_zone_rule.type_count) {
765 default_type = 0;
766 break;
767 }
768 }
769 }
770 time_zone_rule.default_type = default_type;
771 return true;
772}
773
774static Result CreateCalendarTime(s64 time, int gmt_offset, CalendarTimeInternal& calendar_time,
775 CalendarAdditionalInfo& calendar_additional_info) {
776 s64 year{epoch_year};
777 s64 time_days{time / seconds_per_day};
778 s64 remaining_seconds{time % seconds_per_day};
779 while (time_days < 0 || time_days >= GetYearLengthInDays(year)) {
780 s64 delta = time_days / days_per_leap_year;
781 if (!delta) {
782 delta = time_days < 0 ? -1 : 1;
783 }
784 s64 new_year{year};
785 if (!SafeAdd(new_year, delta)) {
786 return ERROR_OUT_OF_RANGE;
787 }
788 time_days -= (new_year - year) * days_per_normal_year;
789 time_days -= GetLeapDaysFromYear(new_year - 1) - GetLeapDaysFromYear(year - 1);
790 year = new_year;
791 }
792
793 s64 day_of_year{time_days};
794 remaining_seconds += gmt_offset;
795 while (remaining_seconds < 0) {
796 remaining_seconds += seconds_per_day;
797 day_of_year--;
798 }
799
800 while (remaining_seconds >= seconds_per_day) {
801 remaining_seconds -= seconds_per_day;
802 day_of_year++;
803 }
804
805 while (day_of_year < 0) {
806 if (!SafeAdd(year, -1)) {
807 return ERROR_OUT_OF_RANGE;
808 }
809 day_of_year += GetYearLengthInDays(year);
810 }
811
812 while (day_of_year >= GetYearLengthInDays(year)) {
813 day_of_year -= GetYearLengthInDays(year);
814 if (!SafeAdd(year, 1)) {
815 return ERROR_OUT_OF_RANGE;
816 }
817 }
818
819 calendar_time.year = year;
820 calendar_additional_info.day_of_year = static_cast<u32>(day_of_year);
821 s64 day_of_week{
822 (epoch_week_day +
823 ((year - epoch_year) % days_per_week) * (days_per_normal_year % days_per_week) +
824 GetLeapDaysFromYear(year - 1) - GetLeapDaysFromYear(epoch_year - 1) + day_of_year) %
825 days_per_week};
826 if (day_of_week < 0) {
827 day_of_week += days_per_week;
828 }
829
830 calendar_additional_info.day_of_week = static_cast<u32>(day_of_week);
831 calendar_time.hour = static_cast<s8>((remaining_seconds / seconds_per_hour) % seconds_per_hour);
832 remaining_seconds %= seconds_per_hour;
833 calendar_time.minute = static_cast<s8>(remaining_seconds / seconds_per_minute);
834 calendar_time.second = static_cast<s8>(remaining_seconds % seconds_per_minute);
835
836 for (calendar_time.month = 0;
837 day_of_year >= GetMonthLength(IsLeapYear(year), calendar_time.month);
838 ++calendar_time.month) {
839 day_of_year -= GetMonthLength(IsLeapYear(year), calendar_time.month);
840 }
841
842 calendar_time.day = static_cast<s8>(day_of_year + 1);
843 calendar_additional_info.is_dst = false;
844 calendar_additional_info.gmt_offset = gmt_offset;
845
846 return ResultSuccess;
847}
848
849static Result ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time,
850 CalendarTimeInternal& calendar_time,
851 CalendarAdditionalInfo& calendar_additional_info) {
852 ASSERT(rules.go_ahead ? rules.time_count > 0 : true);
853 if ((rules.go_back && time < rules.ats[0]) ||
854 (rules.go_ahead && time > rules.ats[rules.time_count - 1])) {
855 s64 seconds{};
856 if (time < rules.ats[0]) {
857 seconds = rules.ats[0] - time;
858 } else {
859 seconds = time - rules.ats[rules.time_count - 1];
860 }
861 seconds--;
862
863 const s64 years{(seconds / seconds_per_repeat + 1) * years_per_repeat};
864 seconds = years * average_seconds_per_year;
865
866 s64 new_time{time};
867 if (time < rules.ats[0]) {
868 new_time += seconds;
869 } else {
870 new_time -= seconds;
871 }
872 if (new_time < rules.ats[0] && new_time > rules.ats[rules.time_count - 1]) {
873 return ERROR_TIME_NOT_FOUND;
874 }
875 if (const Result result{
876 ToCalendarTimeInternal(rules, new_time, calendar_time, calendar_additional_info)};
877 result != ResultSuccess) {
878 return result;
879 }
880 if (time < rules.ats[0]) {
881 calendar_time.year -= years;
882 } else {
883 calendar_time.year += years;
884 }
885
886 return ResultSuccess;
887 }
888
889 s32 tti_index{};
890 if (rules.time_count == 0 || time < rules.ats[0]) {
891 tti_index = rules.default_type;
892 } else {
893 s32 low{1};
894 s32 high{rules.time_count};
895 while (low < high) {
896 s32 mid{(low + high) >> 1};
897 if (time < rules.ats[mid]) {
898 high = mid;
899 } else {
900 low = mid + 1;
901 }
902 }
903 tti_index = rules.types[low - 1];
904 }
905
906 if (const Result result{CreateCalendarTime(time, rules.ttis[tti_index].gmt_offset,
907 calendar_time, calendar_additional_info)};
908 result != ResultSuccess) {
909 return result;
910 }
911
912 calendar_additional_info.is_dst = rules.ttis[tti_index].is_dst;
913 const char* time_zone{&rules.chars[rules.ttis[tti_index].abbreviation_list_index]};
914 u32 index;
915 for (index = 0; time_zone[index] != '\0' && time_zone[index] != ',' &&
916 index < calendar_additional_info.timezone_name.size() - 1;
917 ++index) {
918 calendar_additional_info.timezone_name[index] = time_zone[index];
919 }
920 calendar_additional_info.timezone_name[index] = '\0';
921 return ResultSuccess;
922}
923
924static Result ToCalendarTimeImpl(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) {
925 CalendarTimeInternal calendar_time{};
926 const Result result{
927 ToCalendarTimeInternal(rules, time, calendar_time, calendar.additional_info)};
928 calendar.time.year = static_cast<s16>(calendar_time.year);
929
930 // Internal impl. uses 0-indexed month
931 calendar.time.month = static_cast<s8>(calendar_time.month + 1);
932
933 calendar.time.day = calendar_time.day;
934 calendar.time.hour = calendar_time.hour;
935 calendar.time.minute = calendar_time.minute;
936 calendar.time.second = calendar_time.second;
937 return result;
938}
939
940TimeZoneManager::TimeZoneManager() = default;
941TimeZoneManager::~TimeZoneManager() = default;
942
943Result TimeZoneManager::ToCalendarTime(const TimeZoneRule& rules, s64 time,
944 CalendarInfo& calendar) const {
945 return ToCalendarTimeImpl(rules, time, calendar);
946}
947
948Result TimeZoneManager::SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name,
949 FileSys::VirtualFile& vfs_file) {
950 TimeZoneRule rule{};
951 if (ParseTimeZoneBinary(rule, vfs_file)) {
952 device_location_name = location_name;
953 time_zone_rule = rule;
954 return ResultSuccess;
955 }
956 return ERROR_TIME_ZONE_CONVERSION_FAILED;
957}
958
959Result TimeZoneManager::SetUpdatedTime(const Clock::SteadyClockTimePoint& value) {
960 time_zone_update_time_point = value;
961 return ResultSuccess;
962}
963
964Result TimeZoneManager::ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const {
965 if (is_initialized) {
966 return ToCalendarTime(time_zone_rule, time, calendar);
967 } else {
968 return ERROR_UNINITIALIZED_CLOCK;
969 }
970}
971
972Result TimeZoneManager::ParseTimeZoneRuleBinary(TimeZoneRule& rules,
973 FileSys::VirtualFile& vfs_file) const {
974 if (!ParseTimeZoneBinary(rules, vfs_file)) {
975 return ERROR_TIME_ZONE_CONVERSION_FAILED;
976 }
977 return ResultSuccess;
978}
979
980Result TimeZoneManager::ToPosixTime(const TimeZoneRule& rules, const CalendarTime& calendar_time,
981 s64& posix_time) const {
982 posix_time = 0;
983
984 CalendarTimeInternal internal_time{
985 .year = calendar_time.year,
986 // Internal impl. uses 0-indexed month
987 .month = static_cast<s8>(calendar_time.month - 1),
988 .day = calendar_time.day,
989 .hour = calendar_time.hour,
990 .minute = calendar_time.minute,
991 .second = calendar_time.second,
992 };
993
994 s32 hour{internal_time.hour};
995 s32 minute{internal_time.minute};
996 if (!SafeNormalize(hour, minute, minutes_per_hour)) {
997 return ERROR_OVERFLOW;
998 }
999 internal_time.minute = static_cast<s8>(minute);
1000
1001 s32 day{internal_time.day};
1002 if (!SafeNormalize(day, hour, hours_per_day)) {
1003 return ERROR_OVERFLOW;
1004 }
1005 internal_time.day = static_cast<s8>(day);
1006 internal_time.hour = static_cast<s8>(hour);
1007
1008 s64 year{internal_time.year};
1009 s64 month{internal_time.month};
1010 if (!SafeNormalize(year, month, months_per_year)) {
1011 return ERROR_OVERFLOW;
1012 }
1013 internal_time.month = static_cast<s8>(month);
1014
1015 if (!SafeAdd(year, year_base)) {
1016 return ERROR_OVERFLOW;
1017 }
1018
1019 while (day <= 0) {
1020 if (!SafeAdd(year, -1)) {
1021 return ERROR_OVERFLOW;
1022 }
1023 s64 temp_year{year};
1024 if (1 < internal_time.month) {
1025 ++temp_year;
1026 }
1027 day += static_cast<s32>(GetYearLengthInDays(temp_year));
1028 }
1029
1030 while (day > days_per_leap_year) {
1031 s64 temp_year{year};
1032 if (1 < internal_time.month) {
1033 temp_year++;
1034 }
1035 day -= static_cast<s32>(GetYearLengthInDays(temp_year));
1036 if (!SafeAdd(year, 1)) {
1037 return ERROR_OVERFLOW;
1038 }
1039 }
1040
1041 while (true) {
1042 const s32 month_length{GetMonthLength(IsLeapYear(year), internal_time.month)};
1043 if (day <= month_length) {
1044 break;
1045 }
1046 day -= month_length;
1047 internal_time.month++;
1048 if (internal_time.month >= months_per_year) {
1049 internal_time.month = 0;
1050 if (!SafeAdd(year, 1)) {
1051 return ERROR_OVERFLOW;
1052 }
1053 }
1054 }
1055 internal_time.day = static_cast<s8>(day);
1056
1057 if (!SafeAdd(year, -year_base)) {
1058 return ERROR_OVERFLOW;
1059 }
1060 internal_time.year = year;
1061
1062 s32 saved_seconds{};
1063 if (internal_time.second >= 0 && internal_time.second < seconds_per_minute) {
1064 saved_seconds = 0;
1065 } else if (year + year_base < epoch_year) {
1066 s32 second{internal_time.second};
1067 if (!SafeAdd(second, 1 - seconds_per_minute)) {
1068 return ERROR_OVERFLOW;
1069 }
1070 saved_seconds = second;
1071 internal_time.second = 1 - seconds_per_minute;
1072 } else {
1073 saved_seconds = internal_time.second;
1074 internal_time.second = 0;
1075 }
1076
1077 s64 low{LLONG_MIN};
1078 s64 high{LLONG_MAX};
1079 while (true) {
1080 s64 pivot{low / 2 + high / 2};
1081 if (pivot < low) {
1082 pivot = low;
1083 } else if (pivot > high) {
1084 pivot = high;
1085 }
1086 s32 direction{};
1087 CalendarTimeInternal candidate_calendar_time{};
1088 CalendarAdditionalInfo unused{};
1089 if (ToCalendarTimeInternal(rules, pivot, candidate_calendar_time, unused) !=
1090 ResultSuccess) {
1091 if (pivot > 0) {
1092 direction = 1;
1093 } else {
1094 direction = -1;
1095 }
1096 } else {
1097 direction = candidate_calendar_time.Compare(internal_time);
1098 }
1099 if (!direction) {
1100 const s64 time_result{pivot + saved_seconds};
1101 if ((time_result < pivot) != (saved_seconds < 0)) {
1102 return ERROR_OVERFLOW;
1103 }
1104 posix_time = time_result;
1105 break;
1106 } else {
1107 if (pivot == low) {
1108 if (pivot == LLONG_MAX) {
1109 return ERROR_TIME_NOT_FOUND;
1110 }
1111 pivot++;
1112 low++;
1113 } else if (pivot == high) {
1114 if (pivot == LLONG_MIN) {
1115 return ERROR_TIME_NOT_FOUND;
1116 }
1117 pivot--;
1118 high--;
1119 }
1120 if (low > high) {
1121 return ERROR_TIME_NOT_FOUND;
1122 }
1123 if (direction > 0) {
1124 high = pivot;
1125 } else {
1126 low = pivot;
1127 }
1128 }
1129 }
1130 return ResultSuccess;
1131}
1132
1133Result TimeZoneManager::ToPosixTimeWithMyRule(const CalendarTime& calendar_time,
1134 s64& posix_time) const {
1135 if (is_initialized) {
1136 return ToPosixTime(time_zone_rule, calendar_time, posix_time);
1137 }
1138 posix_time = 0;
1139 return ERROR_UNINITIALIZED_CLOCK;
1140}
1141
1142Result TimeZoneManager::GetDeviceLocationName(LocationName& value) const {
1143 if (!is_initialized) {
1144 return ERROR_UNINITIALIZED_CLOCK;
1145 }
1146 std::memcpy(value.data(), device_location_name.c_str(), device_location_name.size());
1147 return ResultSuccess;
1148}
1149
1150Result TimeZoneManager::GetTotalLocationNameCount(s32& count) const {
1151 if (!is_initialized) {
1152 return ERROR_UNINITIALIZED_CLOCK;
1153 }
1154 count = static_cast<u32>(total_location_name_count);
1155
1156 return ResultSuccess;
1157}
1158
1159Result TimeZoneManager::GetTimeZoneRuleVersion(u128& version) const {
1160 if (!is_initialized) {
1161 return ERROR_UNINITIALIZED_CLOCK;
1162 }
1163 version = time_zone_rule_version;
1164
1165 return ResultSuccess;
1166}
1167
1168Result TimeZoneManager::LoadLocationNameList(std::vector<LocationName>& values) const {
1169 if (!is_initialized) {
1170 return ERROR_UNINITIALIZED_CLOCK;
1171 }
1172
1173 for (const auto& name : total_location_names) {
1174 LocationName entry{};
1175 std::memcpy(entry.data(), name.c_str(), name.size());
1176 values.push_back(entry);
1177 }
1178
1179 return ResultSuccess;
1180}
1181
1182} // namespace Service::Time::TimeZone
diff --git a/src/core/hle/service/time/time_zone_manager.h b/src/core/hle/service/time/time_zone_manager.h
deleted file mode 100644
index 8664f28d1..000000000
--- a/src/core/hle/service/time/time_zone_manager.h
+++ /dev/null
@@ -1,61 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7
8#include "common/common_types.h"
9#include "core/file_sys/vfs_types.h"
10#include "core/hle/service/time/clock_types.h"
11#include "core/hle/service/time/time_zone_types.h"
12
13namespace Service::Time::TimeZone {
14
15class TimeZoneManager final {
16public:
17 TimeZoneManager();
18 ~TimeZoneManager();
19
20 void SetTotalLocationNameCount(std::size_t value) {
21 total_location_name_count = value;
22 }
23
24 void SetLocationNames(std::vector<std::string> location_names) {
25 total_location_names = location_names;
26 }
27
28 void SetTimeZoneRuleVersion(const u128& value) {
29 time_zone_rule_version = value;
30 }
31
32 void MarkAsInitialized() {
33 is_initialized = true;
34 }
35
36 Result SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name,
37 FileSys::VirtualFile& vfs_file);
38 Result SetUpdatedTime(const Clock::SteadyClockTimePoint& value);
39 Result GetDeviceLocationName(TimeZone::LocationName& value) const;
40 Result GetTotalLocationNameCount(s32& count) const;
41 Result GetTimeZoneRuleVersion(u128& version) const;
42 Result LoadLocationNameList(std::vector<TimeZone::LocationName>& values) const;
43 Result ToCalendarTime(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) const;
44 Result ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const;
45 Result ParseTimeZoneRuleBinary(TimeZoneRule& rules, FileSys::VirtualFile& vfs_file) const;
46 Result ToPosixTime(const TimeZoneRule& rules, const CalendarTime& calendar_time,
47 s64& posix_time) const;
48 Result ToPosixTimeWithMyRule(const CalendarTime& calendar_time, s64& posix_time) const;
49
50private:
51 bool is_initialized{};
52 TimeZoneRule time_zone_rule{};
53 std::string device_location_name{"GMT"};
54 u128 time_zone_rule_version{};
55 std::size_t total_location_name_count{};
56 std::vector<std::string> total_location_names{};
57 Clock::SteadyClockTimePoint time_zone_update_time_point{
58 Clock::SteadyClockTimePoint::GetRandom()};
59};
60
61} // namespace Service::Time::TimeZone
diff --git a/src/core/hle/service/time/time_zone_service.cpp b/src/core/hle/service/time/time_zone_service.cpp
deleted file mode 100644
index 8171c82a5..000000000
--- a/src/core/hle/service/time/time_zone_service.cpp
+++ /dev/null
@@ -1,217 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "core/hle/service/ipc_helpers.h"
6#include "core/hle/service/time/time_zone_content_manager.h"
7#include "core/hle/service/time/time_zone_service.h"
8#include "core/hle/service/time/time_zone_types.h"
9
10namespace Service::Time {
11
12ITimeZoneService::ITimeZoneService(Core::System& system_,
13 TimeZone::TimeZoneContentManager& time_zone_manager_)
14 : ServiceFramework{system_, "ITimeZoneService"}, time_zone_content_manager{time_zone_manager_} {
15 static const FunctionInfo functions[] = {
16 {0, &ITimeZoneService::GetDeviceLocationName, "GetDeviceLocationName"},
17 {1, nullptr, "SetDeviceLocationName"},
18 {2, &ITimeZoneService::GetTotalLocationNameCount, "GetTotalLocationNameCount"},
19 {3, &ITimeZoneService::LoadLocationNameList, "LoadLocationNameList"},
20 {4, &ITimeZoneService::LoadTimeZoneRule, "LoadTimeZoneRule"},
21 {5, &ITimeZoneService::GetTimeZoneRuleVersion, "GetTimeZoneRuleVersion"},
22 {6, nullptr, "GetDeviceLocationNameAndUpdatedTime"},
23 {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"},
24 {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
25 {201, &ITimeZoneService::ToPosixTime, "ToPosixTime"},
26 {202, &ITimeZoneService::ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"},
27 };
28 RegisterHandlers(functions);
29}
30
31void ITimeZoneService::GetDeviceLocationName(HLERequestContext& ctx) {
32 LOG_DEBUG(Service_Time, "called");
33
34 TimeZone::LocationName location_name{};
35 if (const Result result{
36 time_zone_content_manager.GetTimeZoneManager().GetDeviceLocationName(location_name)};
37 result != ResultSuccess) {
38 IPC::ResponseBuilder rb{ctx, 2};
39 rb.Push(result);
40 return;
41 }
42
43 IPC::ResponseBuilder rb{ctx, (sizeof(location_name) / 4) + 2};
44 rb.Push(ResultSuccess);
45 rb.PushRaw(location_name);
46}
47
48void ITimeZoneService::GetTotalLocationNameCount(HLERequestContext& ctx) {
49 LOG_DEBUG(Service_Time, "called");
50
51 s32 count{};
52 if (const Result result{
53 time_zone_content_manager.GetTimeZoneManager().GetTotalLocationNameCount(count)};
54 result != ResultSuccess) {
55 IPC::ResponseBuilder rb{ctx, 2};
56 rb.Push(result);
57 return;
58 }
59
60 IPC::ResponseBuilder rb{ctx, 3};
61 rb.Push(ResultSuccess);
62 rb.Push(count);
63}
64
65void ITimeZoneService::LoadLocationNameList(HLERequestContext& ctx) {
66 LOG_DEBUG(Service_Time, "called");
67
68 std::vector<TimeZone::LocationName> location_names{};
69 if (const Result result{
70 time_zone_content_manager.GetTimeZoneManager().LoadLocationNameList(location_names)};
71 result != ResultSuccess) {
72 IPC::ResponseBuilder rb{ctx, 2};
73 rb.Push(result);
74 return;
75 }
76
77 ctx.WriteBuffer(location_names);
78 IPC::ResponseBuilder rb{ctx, 3};
79 rb.Push(ResultSuccess);
80 rb.Push(static_cast<s32>(location_names.size()));
81}
82void ITimeZoneService::GetTimeZoneRuleVersion(HLERequestContext& ctx) {
83 LOG_DEBUG(Service_Time, "called");
84
85 u128 rule_version{};
86 if (const Result result{
87 time_zone_content_manager.GetTimeZoneManager().GetTimeZoneRuleVersion(rule_version)};
88 result != ResultSuccess) {
89 IPC::ResponseBuilder rb{ctx, 2};
90 rb.Push(result);
91 return;
92 }
93
94 IPC::ResponseBuilder rb{ctx, 6};
95 rb.Push(ResultSuccess);
96 rb.PushRaw(rule_version);
97}
98
99void ITimeZoneService::LoadTimeZoneRule(HLERequestContext& ctx) {
100 IPC::RequestParser rp{ctx};
101 const auto raw_location_name{rp.PopRaw<std::array<u8, 0x24>>()};
102
103 std::string location_name;
104 for (const auto& byte : raw_location_name) {
105 // Strip extra bytes
106 if (byte == '\0') {
107 break;
108 }
109 location_name.push_back(byte);
110 }
111
112 LOG_DEBUG(Service_Time, "called, location_name={}", location_name);
113
114 TimeZone::TimeZoneRule time_zone_rule{};
115 const Result result{time_zone_content_manager.LoadTimeZoneRule(time_zone_rule, location_name)};
116
117 std::vector<u8> time_zone_rule_outbuffer(sizeof(TimeZone::TimeZoneRule));
118 std::memcpy(time_zone_rule_outbuffer.data(), &time_zone_rule, sizeof(TimeZone::TimeZoneRule));
119 ctx.WriteBuffer(time_zone_rule_outbuffer);
120
121 IPC::ResponseBuilder rb{ctx, 2};
122 rb.Push(result);
123}
124
125void ITimeZoneService::ToCalendarTime(HLERequestContext& ctx) {
126 IPC::RequestParser rp{ctx};
127 const auto posix_time{rp.Pop<s64>()};
128
129 LOG_DEBUG(Service_Time, "called, posix_time=0x{:016X}", posix_time);
130
131 TimeZone::TimeZoneRule time_zone_rule{};
132 const auto buffer{ctx.ReadBuffer()};
133 std::memcpy(&time_zone_rule, buffer.data(), buffer.size());
134
135 TimeZone::CalendarInfo calendar_info{};
136 if (const Result result{time_zone_content_manager.GetTimeZoneManager().ToCalendarTime(
137 time_zone_rule, posix_time, calendar_info)};
138 result != ResultSuccess) {
139 IPC::ResponseBuilder rb{ctx, 2};
140 rb.Push(result);
141 return;
142 }
143
144 IPC::ResponseBuilder rb{ctx, 2 + (sizeof(TimeZone::CalendarInfo) / 4)};
145 rb.Push(ResultSuccess);
146 rb.PushRaw(calendar_info);
147}
148
149void ITimeZoneService::ToCalendarTimeWithMyRule(HLERequestContext& ctx) {
150 IPC::RequestParser rp{ctx};
151 const auto posix_time{rp.Pop<s64>()};
152
153 LOG_DEBUG(Service_Time, "called, posix_time=0x{:016X}", posix_time);
154
155 TimeZone::CalendarInfo calendar_info{};
156 if (const Result result{
157 time_zone_content_manager.GetTimeZoneManager().ToCalendarTimeWithMyRules(
158 posix_time, calendar_info)};
159 result != ResultSuccess) {
160 IPC::ResponseBuilder rb{ctx, 2};
161 rb.Push(result);
162 return;
163 }
164
165 IPC::ResponseBuilder rb{ctx, 2 + (sizeof(TimeZone::CalendarInfo) / 4)};
166 rb.Push(ResultSuccess);
167 rb.PushRaw(calendar_info);
168}
169
170void ITimeZoneService::ToPosixTime(HLERequestContext& ctx) {
171 LOG_DEBUG(Service_Time, "called");
172
173 IPC::RequestParser rp{ctx};
174 const auto calendar_time{rp.PopRaw<TimeZone::CalendarTime>()};
175 TimeZone::TimeZoneRule time_zone_rule{};
176 std::memcpy(&time_zone_rule, ctx.ReadBuffer().data(), sizeof(TimeZone::TimeZoneRule));
177
178 s64 posix_time{};
179 if (const Result result{time_zone_content_manager.GetTimeZoneManager().ToPosixTime(
180 time_zone_rule, calendar_time, posix_time)};
181 result != ResultSuccess) {
182 IPC::ResponseBuilder rb{ctx, 2};
183 rb.Push(result);
184 return;
185 }
186
187 ctx.WriteBuffer(posix_time);
188
189 // TODO(bunnei): Handle multiple times
190 IPC::ResponseBuilder rb{ctx, 3};
191 rb.Push(ResultSuccess);
192 rb.PushRaw<u32>(1); // Number of times we're returning
193}
194
195void ITimeZoneService::ToPosixTimeWithMyRule(HLERequestContext& ctx) {
196 LOG_DEBUG(Service_Time, "called");
197
198 IPC::RequestParser rp{ctx};
199 const auto calendar_time{rp.PopRaw<TimeZone::CalendarTime>()};
200
201 s64 posix_time{};
202 if (const Result result{time_zone_content_manager.GetTimeZoneManager().ToPosixTimeWithMyRule(
203 calendar_time, posix_time)};
204 result != ResultSuccess) {
205 IPC::ResponseBuilder rb{ctx, 2};
206 rb.Push(result);
207 return;
208 }
209
210 ctx.WriteBuffer(posix_time);
211
212 IPC::ResponseBuilder rb{ctx, 3};
213 rb.Push(ResultSuccess);
214 rb.PushRaw<u32>(1); // Number of times we're returning
215}
216
217} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_zone_service.h b/src/core/hle/service/time/time_zone_service.h
deleted file mode 100644
index 952fcb0e2..000000000
--- a/src/core/hle/service/time/time_zone_service.h
+++ /dev/null
@@ -1,38 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Core {
9class System;
10}
11
12namespace Service::Time {
13
14namespace TimeZone {
15class TimeZoneContentManager;
16}
17
18class ITimeZoneService final : public ServiceFramework<ITimeZoneService> {
19public:
20 explicit ITimeZoneService(Core::System& system_,
21 TimeZone::TimeZoneContentManager& time_zone_manager_);
22
23private:
24 void GetDeviceLocationName(HLERequestContext& ctx);
25 void GetTotalLocationNameCount(HLERequestContext& ctx);
26 void LoadLocationNameList(HLERequestContext& ctx);
27 void GetTimeZoneRuleVersion(HLERequestContext& ctx);
28 void LoadTimeZoneRule(HLERequestContext& ctx);
29 void ToCalendarTime(HLERequestContext& ctx);
30 void ToCalendarTimeWithMyRule(HLERequestContext& ctx);
31 void ToPosixTime(HLERequestContext& ctx);
32 void ToPosixTimeWithMyRule(HLERequestContext& ctx);
33
34private:
35 TimeZone::TimeZoneContentManager& time_zone_content_manager;
36};
37
38} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_zone_types.h b/src/core/hle/service/time/time_zone_types.h
deleted file mode 100644
index eb4fb52d1..000000000
--- a/src/core/hle/service/time/time_zone_types.h
+++ /dev/null
@@ -1,86 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7
8#include "common/common_funcs.h"
9#include "common/common_types.h"
10#include "common/swap.h"
11
12namespace Service::Time::TimeZone {
13
14using LocationName = std::array<char, 0x24>;
15
16/// https://switchbrew.org/wiki/Glue_services#ttinfo
17struct TimeTypeInfo {
18 s32 gmt_offset{};
19 u8 is_dst{};
20 INSERT_PADDING_BYTES(3);
21 s32 abbreviation_list_index{};
22 u8 is_standard_time_daylight{};
23 u8 is_gmt{};
24 INSERT_PADDING_BYTES(2);
25};
26static_assert(sizeof(TimeTypeInfo) == 0x10, "TimeTypeInfo is incorrect size");
27
28/// https://switchbrew.org/wiki/Glue_services#TimeZoneRule
29struct TimeZoneRule {
30 s32 time_count{};
31 s32 type_count{};
32 s32 char_count{};
33 u8 go_back{};
34 u8 go_ahead{};
35 INSERT_PADDING_BYTES(2);
36 std::array<s64, 1000> ats{};
37 std::array<s8, 1000> types{};
38 std::array<TimeTypeInfo, 128> ttis{};
39 std::array<char, 512> chars{};
40 s32 default_type{};
41 INSERT_PADDING_BYTES(0x12C4);
42};
43static_assert(sizeof(TimeZoneRule) == 0x4000, "TimeZoneRule is incorrect size");
44
45/// https://switchbrew.org/wiki/Glue_services#CalendarAdditionalInfo
46struct CalendarAdditionalInfo {
47 u32 day_of_week;
48 u32 day_of_year;
49 std::array<char, 8> timezone_name;
50 u32 is_dst;
51 s32 gmt_offset;
52};
53static_assert(sizeof(CalendarAdditionalInfo) == 0x18, "CalendarAdditionalInfo is incorrect size");
54
55/// https://switchbrew.org/wiki/Glue_services#CalendarTime
56struct CalendarTime {
57 s16 year;
58 s8 month;
59 s8 day;
60 s8 hour;
61 s8 minute;
62 s8 second;
63 INSERT_PADDING_BYTES_NOINIT(1);
64};
65static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime is incorrect size");
66
67struct CalendarInfo {
68 CalendarTime time;
69 CalendarAdditionalInfo additional_info;
70};
71static_assert(sizeof(CalendarInfo) == 0x20, "CalendarInfo is incorrect size");
72
73struct TzifHeader {
74 u32_be magic{};
75 u8 version{};
76 INSERT_PADDING_BYTES(15);
77 s32_be ttis_gmt_count{};
78 s32_be ttis_std_count{};
79 s32_be leap_count{};
80 s32_be time_count{};
81 s32_be type_count{};
82 s32_be char_count{};
83};
84static_assert(sizeof(TzifHeader) == 0x2C, "TzifHeader is incorrect size");
85
86} // namespace Service::Time::TimeZone