summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar bunnei2019-12-22 17:49:51 -0500
committerGravatar bunnei2020-01-04 13:48:29 -0500
commit78f977c980e125e92b86261335447d0a254f18ee (patch)
treeeca0bcfdcabe32dd737fc545b1ce42395cdad2fc /src
parentcore: Initialize several structs that make use of Common::UUID. (diff)
downloadyuzu-78f977c980e125e92b86261335447d0a254f18ee.tar.gz
yuzu-78f977c980e125e92b86261335447d0a254f18ee.tar.xz
yuzu-78f977c980e125e92b86261335447d0a254f18ee.zip
service: time: Rewrite implementation of glue services.
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt28
-rw-r--r--src/core/hle/service/time/clock_types.h91
-rw-r--r--src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h16
-rw-r--r--src/core/hle/service/time/ephemeral_network_system_clock_core.h17
-rw-r--r--src/core/hle/service/time/errors.h22
-rw-r--r--src/core/hle/service/time/interface.cpp17
-rw-r--r--src/core/hle/service/time/interface.h11
-rw-r--r--src/core/hle/service/time/local_system_clock_context_writer.h28
-rw-r--r--src/core/hle/service/time/network_system_clock_context_writer.h28
-rw-r--r--src/core/hle/service/time/standard_local_system_clock_core.h17
-rw-r--r--src/core/hle/service/time/standard_network_system_clock_core.h26
-rw-r--r--src/core/hle/service/time/standard_steady_clock_core.cpp26
-rw-r--r--src/core/hle/service/time/standard_steady_clock_core.h42
-rw-r--r--src/core/hle/service/time/standard_user_system_clock_core.cpp77
-rw-r--r--src/core/hle/service/time/standard_user_system_clock_core.h57
-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.cpp55
-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.cpp72
-rw-r--r--src/core/hle/service/time/system_clock_core.h71
-rw-r--r--src/core/hle/service/time/tick_based_steady_clock_core.cpp24
-rw-r--r--src/core/hle/service/time/tick_based_steady_clock_core.h29
-rw-r--r--src/core/hle/service/time/time.cpp457
-rw-r--r--src/core/hle/service/time/time.h100
-rw-r--r--src/core/hle/service/time/time_manager.cpp137
-rw-r--r--src/core/hle/service/time/time_manager.h117
-rw-r--r--src/core/hle/service/time/time_sharedmemory.cpp53
-rw-r--r--src/core/hle/service/time/time_sharedmemory.h35
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.cpp125
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.h46
-rw-r--r--src/core/hle/service/time/time_zone_manager.cpp1038
-rw-r--r--src/core/hle/service/time/time_zone_manager.h53
-rw-r--r--src/core/hle/service/time/time_zone_service.cpp148
-rw-r--r--src/core/hle/service/time/time_zone_service.h30
-rw-r--r--src/core/hle/service/time/time_zone_types.h87
35 files changed, 2834 insertions, 444 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 7fd226050..23d88d747 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -461,12 +461,40 @@ add_library(core STATIC
461 hle/service/spl/spl.h 461 hle/service/spl/spl.h
462 hle/service/ssl/ssl.cpp 462 hle/service/ssl/ssl.cpp
463 hle/service/ssl/ssl.h 463 hle/service/ssl/ssl.h
464 hle/service/time/clock_types.h
465 hle/service/time/ephemeral_network_system_clock_context_writer.h
466 hle/service/time/ephemeral_network_system_clock_core.h
467 hle/service/time/errors.h
464 hle/service/time/interface.cpp 468 hle/service/time/interface.cpp
465 hle/service/time/interface.h 469 hle/service/time/interface.h
470 hle/service/time/local_system_clock_context_writer.h
471 hle/service/time/network_system_clock_context_writer.h
472 hle/service/time/standard_local_system_clock_core.h
473 hle/service/time/standard_network_system_clock_core.h
474 hle/service/time/standard_steady_clock_core.cpp
475 hle/service/time/standard_steady_clock_core.h
476 hle/service/time/standard_user_system_clock_core.cpp
477 hle/service/time/standard_user_system_clock_core.h
478 hle/service/time/steady_clock_core.h
479 hle/service/time/system_clock_context_update_callback.cpp
480 hle/service/time/system_clock_context_update_callback.h
481 hle/service/time/system_clock_core.cpp
482 hle/service/time/system_clock_core.h
483 hle/service/time/tick_based_steady_clock_core.cpp
484 hle/service/time/tick_based_steady_clock_core.h
466 hle/service/time/time.cpp 485 hle/service/time/time.cpp
467 hle/service/time/time.h 486 hle/service/time/time.h
487 hle/service/time/time_manager.cpp
488 hle/service/time/time_manager.h
468 hle/service/time/time_sharedmemory.cpp 489 hle/service/time/time_sharedmemory.cpp
469 hle/service/time/time_sharedmemory.h 490 hle/service/time/time_sharedmemory.h
491 hle/service/time/time_zone_content_manager.cpp
492 hle/service/time/time_zone_content_manager.h
493 hle/service/time/time_zone_manager.cpp
494 hle/service/time/time_zone_manager.h
495 hle/service/time/time_zone_service.cpp
496 hle/service/time/time_zone_service.h
497 hle/service/time/time_zone_types.h
470 hle/service/usb/usb.cpp 498 hle/service/usb/usb.cpp
471 hle/service/usb/usb.h 499 hle/service/usb/usb.h
472 hle/service/vi/display/vi_display.cpp 500 hle/service/vi/display/vi_display.cpp
diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h
new file mode 100644
index 000000000..f2ef3ec53
--- /dev/null
+++ b/src/core/hle/service/time/clock_types.h
@@ -0,0 +1,91 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_funcs.h"
8#include "common/common_types.h"
9#include "common/uuid.h"
10#include "core/hle/service/time/errors.h"
11#include "core/hle/service/time/time_zone_types.h"
12
13namespace Service::Time::Clock {
14
15/// https://switchbrew.org/wiki/Glue_services#SteadyClockTimePoint
16struct SteadyClockTimePoint {
17 s64 time_point;
18 Common::UUID clock_source_id;
19
20 static SteadyClockTimePoint GetRandom() {
21 return {0, Common::UUID::Generate()};
22 }
23};
24static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size");
25static_assert(std::is_trivially_copyable_v<SteadyClockTimePoint>,
26 "SteadyClockTimePoint must be trivially copyable");
27
28struct SteadyClockContext {
29 u64 internal_offset;
30 Common::UUID steady_time_point;
31};
32static_assert(sizeof(SteadyClockContext) == 0x18, "SteadyClockContext is incorrect size");
33static_assert(std::is_trivially_copyable_v<SteadyClockContext>,
34 "SteadyClockContext must be trivially copyable");
35
36struct SystemClockContext {
37 s64 offset;
38 SteadyClockTimePoint steady_time_point;
39};
40static_assert(sizeof(SystemClockContext) == 0x20, "SystemClockContext is incorrect size");
41static_assert(std::is_trivially_copyable_v<SystemClockContext>,
42 "SystemClockContext must be trivially copyable");
43
44/// https://switchbrew.org/wiki/Glue_services#TimeSpanType
45struct TimeSpanType {
46 s64 nanoseconds{};
47 static constexpr s64 ns_per_second{1000000000ULL};
48
49 s64 ToSeconds() const {
50 return nanoseconds / ns_per_second;
51 }
52
53 static TimeSpanType FromSeconds(s64 seconds) {
54 return {seconds * ns_per_second};
55 }
56
57 static TimeSpanType FromTicks(u64 ticks, u64 frequency) {
58 return FromSeconds(static_cast<s64>(ticks) / static_cast<s64>(frequency));
59 }
60};
61static_assert(sizeof(TimeSpanType) == 8, "TimeSpanType is incorrect size");
62
63struct ClockSnapshot {
64 SystemClockContext user_context{};
65 SystemClockContext network_context{};
66 s64 user_time{};
67 s64 network_time{};
68 TimeZone::CalendarTime user_calendar_time{};
69 TimeZone::CalendarTime network_calendar_time{};
70 TimeZone::CalendarAdditionalInfo user_calendar_additional_time{};
71 TimeZone::CalendarAdditionalInfo network_calendar_additional_time{};
72 SteadyClockTimePoint steady_clock_time_point{};
73 TimeZone::LocationName location_name{};
74 u8 is_automatic_correction_enabled{};
75 u8 type{};
76 INSERT_PADDING_BYTES(0x2);
77
78 static ResultCode GetCurrentTime(s64& current_time,
79 const SteadyClockTimePoint& steady_clock_time_point,
80 const SystemClockContext& context) {
81 if (steady_clock_time_point.clock_source_id != context.steady_time_point.clock_source_id) {
82 current_time = 0;
83 return ERROR_TIME_MISMATCH;
84 }
85 current_time = steady_clock_time_point.time_point + context.offset;
86 return RESULT_SUCCESS;
87 }
88};
89static_assert(sizeof(ClockSnapshot) == 0xD0, "ClockSnapshot is incorrect size");
90
91} // 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
new file mode 100644
index 000000000..42893e3f6
--- /dev/null
+++ b/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h
@@ -0,0 +1,16 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/time/system_clock_context_update_callback.h"
8
9namespace Service::Time::Clock {
10
11class EphemeralNetworkSystemClockContextWriter final : public SystemClockContextUpdateCallback {
12public:
13 EphemeralNetworkSystemClockContextWriter() : SystemClockContextUpdateCallback{} {}
14};
15
16} // 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
new file mode 100644
index 000000000..4c6cdef86
--- /dev/null
+++ b/src/core/hle/service/time/ephemeral_network_system_clock_core.h
@@ -0,0 +1,17 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/time/system_clock_core.h"
8
9namespace Service::Time::Clock {
10
11class EphemeralNetworkSystemClockCore final : public SystemClockCore {
12public:
13 explicit EphemeralNetworkSystemClockCore(SteadyClockCore& steady_clock_core)
14 : SystemClockCore{steady_clock_core} {}
15};
16
17} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/errors.h b/src/core/hle/service/time/errors.h
new file mode 100644
index 000000000..8501a3e8c
--- /dev/null
+++ b/src/core/hle/service/time/errors.h
@@ -0,0 +1,22 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/result.h"
8
9namespace Service::Time {
10
11constexpr ResultCode ERROR_PERMISSION_DENIED{ErrorModule::Time, 1};
12constexpr ResultCode ERROR_TIME_MISMATCH{ErrorModule::Time, 102};
13constexpr ResultCode ERROR_UNINITIALIZED_CLOCK{ErrorModule::Time, 103};
14constexpr ResultCode ERROR_TIME_NOT_FOUND{ErrorModule::Time, 200};
15constexpr ResultCode ERROR_OVERFLOW{ErrorModule::Time, 201};
16constexpr ResultCode ERROR_LOCATION_NAME_TOO_LONG{ErrorModule::Time, 801};
17constexpr ResultCode ERROR_OUT_OF_RANGE{ErrorModule::Time, 902};
18constexpr ResultCode ERROR_TIME_ZONE_CONVERSION_FAILED{ErrorModule::Time, 903};
19constexpr ResultCode ERROR_TIME_ZONE_NOT_FOUND{ErrorModule::Time, 989};
20constexpr ResultCode ERROR_NOT_IMPLEMENTED{ErrorModule::Time, 990};
21
22} // namespace Service::Time
diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp
index bc74f1e1d..b1307a434 100644
--- a/src/core/hle/service/time/interface.cpp
+++ b/src/core/hle/service/time/interface.cpp
@@ -1,4 +1,4 @@
1// Copyright 2018 yuzu emulator team 1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
@@ -6,31 +6,30 @@
6 6
7namespace Service::Time { 7namespace Service::Time {
8 8
9Time::Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory, 9Time::Time(std::shared_ptr<Module> module, Core::System& system, const char* name)
10 Core::System& system, const char* name) 10 : Module::Interface(std::move(module), system, name) {
11 : Module::Interface(std::move(time), std::move(shared_memory), system, name) {
12 // clang-format off 11 // clang-format off
13 static const FunctionInfo functions[] = { 12 static const FunctionInfo functions[] = {
14 {0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"}, 13 {0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"},
15 {1, &Time::GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"}, 14 {1, &Time::GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"},
16 {2, &Time::GetStandardSteadyClock, "GetStandardSteadyClock"}, 15 {2, &Time::GetStandardSteadyClock, "GetStandardSteadyClock"},
17 {3, &Time::GetTimeZoneService, "GetTimeZoneService"}, 16 {3, &Time::GetTimeZoneService, "GetTimeZoneService"},
18 {4, &Time::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"}, 17 {4, nullptr, "GetStandardLocalSystemClock"},
19 {5, nullptr, "GetEphemeralNetworkSystemClock"}, 18 {5, nullptr, "GetEphemeralNetworkSystemClock"},
20 {20, &Time::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}, 19 {20, &Time::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"},
21 {30, nullptr, "GetStandardNetworkClockOperationEventReadableHandle"}, 20 {30, nullptr, "GetStandardNetworkClockOperationEventReadableHandle"},
22 {31, nullptr, "GetEphemeralNetworkClockOperationEventReadableHandle"}, 21 {31, nullptr, "GetEphemeralNetworkClockOperationEventReadableHandle"},
23 {50, nullptr, "SetStandardSteadyClockInternalOffset"}, 22 {50, nullptr, "SetStandardSteadyClockInternalOffset"},
24 {51, nullptr, "GetStandardSteadyClockRtcValue"}, 23 {51, nullptr, "GetStandardSteadyClockRtcValue"},
25 {100, &Time::IsStandardUserSystemClockAutomaticCorrectionEnabled, "IsStandardUserSystemClockAutomaticCorrectionEnabled"}, 24 {100, nullptr, "IsStandardUserSystemClockAutomaticCorrectionEnabled"},
26 {101, &Time::SetStandardUserSystemClockAutomaticCorrectionEnabled, "SetStandardUserSystemClockAutomaticCorrectionEnabled"}, 25 {101, nullptr, "SetStandardUserSystemClockAutomaticCorrectionEnabled"},
27 {102, nullptr, "GetStandardUserSystemClockInitialYear"}, 26 {102, nullptr, "GetStandardUserSystemClockInitialYear"},
28 {200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"}, 27 {200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"},
29 {201, nullptr, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"}, 28 {201, nullptr, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"},
30 {300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"}, 29 {300, &Time::CalculateMonotonicSystemClockBaseTimePoint, "CalculateMonotonicSystemClockBaseTimePoint"},
31 {400, &Time::GetClockSnapshot, "GetClockSnapshot"}, 30 {400, &Time::GetClockSnapshot, "GetClockSnapshot"},
32 {401, nullptr, "GetClockSnapshotFromSystemClockContext"}, 31 {401, nullptr, "GetClockSnapshotFromSystemClockContext"},
33 {500, &Time::CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"}, 32 {500, nullptr, "CalculateStandardUserSystemClockDifferenceByUser"},
34 {501, nullptr, "CalculateSpanBetween"}, 33 {501, nullptr, "CalculateSpanBetween"},
35 }; 34 };
36 // clang-format on 35 // clang-format on
diff --git a/src/core/hle/service/time/interface.h b/src/core/hle/service/time/interface.h
index 5c63a07f4..4f49e1f07 100644
--- a/src/core/hle/service/time/interface.h
+++ b/src/core/hle/service/time/interface.h
@@ -1,4 +1,4 @@
1// Copyright 2018 yuzu emulator team 1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
@@ -6,14 +6,15 @@
6 6
7#include "core/hle/service/time/time.h" 7#include "core/hle/service/time/time.h"
8 8
9namespace Service::Time { 9namespace Core {
10class System;
11}
10 12
11class SharedMemory; 13namespace Service::Time {
12 14
13class Time final : public Module::Interface { 15class Time final : public Module::Interface {
14public: 16public:
15 explicit Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory, 17 explicit Time(std::shared_ptr<Module> time, Core::System& system, const char* name);
16 Core::System& system, const char* name);
17 ~Time() override; 18 ~Time() override;
18}; 19};
19 20
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
new file mode 100644
index 000000000..7050844c6
--- /dev/null
+++ b/src/core/hle/service/time/local_system_clock_context_writer.h
@@ -0,0 +1,28 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/time/errors.h"
8#include "core/hle/service/time/system_clock_context_update_callback.h"
9#include "core/hle/service/time/time_sharedmemory.h"
10
11namespace Service::Time::Clock {
12
13class LocalSystemClockContextWriter final : public SystemClockContextUpdateCallback {
14public:
15 explicit LocalSystemClockContextWriter(SharedMemory& shared_memory)
16 : SystemClockContextUpdateCallback{}, shared_memory{shared_memory} {}
17
18protected:
19 ResultCode Update() override {
20 shared_memory.UpdateLocalSystemClockContext(context);
21 return RESULT_SUCCESS;
22 }
23
24private:
25 SharedMemory& shared_memory;
26};
27
28} // 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
new file mode 100644
index 000000000..94d8788ff
--- /dev/null
+++ b/src/core/hle/service/time/network_system_clock_context_writer.h
@@ -0,0 +1,28 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/time/errors.h"
8#include "core/hle/service/time/system_clock_context_update_callback.h"
9#include "core/hle/service/time/time_sharedmemory.h"
10
11namespace Service::Time::Clock {
12
13class NetworkSystemClockContextWriter final : public SystemClockContextUpdateCallback {
14public:
15 explicit NetworkSystemClockContextWriter(SharedMemory& shared_memory)
16 : SystemClockContextUpdateCallback{}, shared_memory{shared_memory} {}
17
18protected:
19 ResultCode Update() override {
20 shared_memory.UpdateNetworkSystemClockContext(context);
21 return RESULT_SUCCESS;
22 }
23
24private:
25 SharedMemory& shared_memory;
26};
27
28} // 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
new file mode 100644
index 000000000..8c1882eb1
--- /dev/null
+++ b/src/core/hle/service/time/standard_local_system_clock_core.h
@@ -0,0 +1,17 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/time/system_clock_core.h"
8
9namespace Service::Time::Clock {
10
11class StandardLocalSystemClockCore final : public SystemClockCore {
12public:
13 explicit StandardLocalSystemClockCore(SteadyClockCore& steady_clock_core)
14 : SystemClockCore{steady_clock_core} {}
15};
16
17} // 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
new file mode 100644
index 000000000..467285160
--- /dev/null
+++ b/src/core/hle/service/time/standard_network_system_clock_core.h
@@ -0,0 +1,26 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/time/clock_types.h"
8#include "core/hle/service/time/steady_clock_core.h"
9#include "core/hle/service/time/system_clock_core.h"
10
11namespace Service::Time::Clock {
12
13class StandardNetworkSystemClockCore final : public SystemClockCore {
14public:
15 explicit StandardNetworkSystemClockCore(SteadyClockCore& steady_clock_core)
16 : SystemClockCore{steady_clock_core} {}
17
18 void SetStandardNetworkClockSufficientAccuracy(TimeSpanType value) {
19 standard_network_clock_sufficient_accuracy = value;
20 }
21
22private:
23 TimeSpanType standard_network_clock_sufficient_accuracy{};
24};
25
26} // 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
new file mode 100644
index 000000000..ca1a783fc
--- /dev/null
+++ b/src/core/hle/service/time/standard_steady_clock_core.cpp
@@ -0,0 +1,26 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/core.h"
6#include "core/core_timing.h"
7#include "core/core_timing_util.h"
8#include "core/hle/service/time/standard_steady_clock_core.h"
9
10namespace Service::Time::Clock {
11
12TimeSpanType StandardSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) {
13 const TimeSpanType ticks_time_span{TimeSpanType::FromTicks(
14 Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()),
15 Core::Timing::CNTFREQ)};
16 TimeSpanType raw_time_point{setup_value.nanoseconds + ticks_time_span.nanoseconds};
17
18 if (raw_time_point.nanoseconds < cached_raw_time_point.nanoseconds) {
19 raw_time_point.nanoseconds = cached_raw_time_point.nanoseconds;
20 }
21
22 cached_raw_time_point = raw_time_point;
23 return raw_time_point;
24}
25
26} // 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
new file mode 100644
index 000000000..f56f3fd95
--- /dev/null
+++ b/src/core/hle/service/time/standard_steady_clock_core.h
@@ -0,0 +1,42 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/time/clock_types.h"
8#include "core/hle/service/time/steady_clock_core.h"
9
10namespace Core {
11class System;
12}
13
14namespace Service::Time::Clock {
15
16class StandardSteadyClockCore final : public SteadyClockCore {
17public:
18 SteadyClockTimePoint GetTimePoint(Core::System& system) override {
19 return {GetCurrentRawTimePoint(system).ToSeconds(), GetClockSourceId()};
20 }
21
22 TimeSpanType GetInternalOffset() const override {
23 return internal_offset;
24 }
25
26 void SetInternalOffset(TimeSpanType value) override {
27 internal_offset = value;
28 }
29
30 TimeSpanType GetCurrentRawTimePoint(Core::System& system) override;
31
32 void SetSetupValue(TimeSpanType value) {
33 setup_value = value;
34 }
35
36private:
37 TimeSpanType setup_value{};
38 TimeSpanType internal_offset{};
39 TimeSpanType cached_raw_time_point{};
40};
41
42} // 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
new file mode 100644
index 000000000..8af17091c
--- /dev/null
+++ b/src/core/hle/service/time/standard_user_system_clock_core.cpp
@@ -0,0 +1,77 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "core/core.h"
7#include "core/hle/kernel/writable_event.h"
8#include "core/hle/service/time/standard_local_system_clock_core.h"
9#include "core/hle/service/time/standard_network_system_clock_core.h"
10#include "core/hle/service/time/standard_user_system_clock_core.h"
11
12namespace Service::Time::Clock {
13
14StandardUserSystemClockCore::StandardUserSystemClockCore(
15 StandardLocalSystemClockCore& local_system_clock_core,
16 StandardNetworkSystemClockCore& network_system_clock_core, Core::System& system)
17 : SystemClockCore(local_system_clock_core.GetSteadyClockCore()),
18 local_system_clock_core{local_system_clock_core},
19 network_system_clock_core{network_system_clock_core}, auto_correction_enabled{},
20 auto_correction_time{SteadyClockTimePoint::GetRandom()},
21 auto_correction_event{Kernel::WritableEvent::CreateEventPair(
22 system.Kernel(), "StandardUserSystemClockCore:AutoCorrectionEvent")} {}
23
24ResultCode StandardUserSystemClockCore::SetAutomaticCorrectionEnabled(Core::System& system,
25 bool value) {
26 if (const ResultCode result{ApplyAutomaticCorrection(system, value)};
27 result != RESULT_SUCCESS) {
28 return result;
29 }
30
31 auto_correction_enabled = value;
32
33 return RESULT_SUCCESS;
34}
35
36ResultCode StandardUserSystemClockCore::GetClockContext(Core::System& system,
37 SystemClockContext& context) const {
38 if (const ResultCode result{ApplyAutomaticCorrection(system, false)};
39 result != RESULT_SUCCESS) {
40 return result;
41 }
42
43 return local_system_clock_core.GetClockContext(system, context);
44}
45
46ResultCode StandardUserSystemClockCore::Flush(const SystemClockContext& context) {
47 UNREACHABLE();
48 return ERROR_NOT_IMPLEMENTED;
49}
50
51ResultCode StandardUserSystemClockCore::SetClockContext(const SystemClockContext& context) {
52 UNREACHABLE();
53 return ERROR_NOT_IMPLEMENTED;
54}
55
56ResultCode StandardUserSystemClockCore::ApplyAutomaticCorrection(Core::System& system,
57 bool value) const {
58 if (auto_correction_enabled == value) {
59 return RESULT_SUCCESS;
60 }
61
62 if (!network_system_clock_core.IsClockSetup(system)) {
63 return ERROR_UNINITIALIZED_CLOCK;
64 }
65
66 SystemClockContext context{};
67 if (const ResultCode result{network_system_clock_core.GetClockContext(system, context)};
68 result != RESULT_SUCCESS) {
69 return result;
70 }
71
72 local_system_clock_core.SetClockContext(context);
73
74 return RESULT_SUCCESS;
75}
76
77} // 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
new file mode 100644
index 000000000..ef3d468b7
--- /dev/null
+++ b/src/core/hle/service/time/standard_user_system_clock_core.h
@@ -0,0 +1,57 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/kernel/writable_event.h"
8#include "core/hle/service/time/clock_types.h"
9#include "core/hle/service/time/system_clock_core.h"
10
11namespace Core {
12class System;
13}
14
15namespace Service::Time::Clock {
16
17class StandardLocalSystemClockCore;
18class StandardNetworkSystemClockCore;
19
20class StandardUserSystemClockCore final : public SystemClockCore {
21public:
22 StandardUserSystemClockCore(StandardLocalSystemClockCore& local_system_clock_core,
23 StandardNetworkSystemClockCore& network_system_clock_core,
24 Core::System& system);
25
26 ResultCode SetAutomaticCorrectionEnabled(Core::System& system, bool value);
27
28 ResultCode GetClockContext(Core::System& system, SystemClockContext& context) const override;
29
30 bool IsAutomaticCorrectionEnabled() const {
31 return auto_correction_enabled;
32 }
33
34 void SetAutomaticCorrectionUpdatedTime(SteadyClockTimePoint steady_clock_time_point) {
35 auto_correction_time = steady_clock_time_point;
36 }
37
38protected:
39 ResultCode Flush(const SystemClockContext& context) override;
40
41 ResultCode SetClockContext(const SystemClockContext&) override;
42
43 ResultCode ApplyAutomaticCorrection(Core::System& system, bool value) const;
44
45 const SteadyClockTimePoint& GetAutomaticCorrectionUpdatedTime() const {
46 return auto_correction_time;
47 }
48
49private:
50 StandardLocalSystemClockCore& local_system_clock_core;
51 StandardNetworkSystemClockCore& network_system_clock_core;
52 bool auto_correction_enabled{};
53 SteadyClockTimePoint auto_correction_time;
54 Kernel::EventPair auto_correction_event;
55};
56
57} // 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
new file mode 100644
index 000000000..84af3d105
--- /dev/null
+++ b/src/core/hle/service/time/steady_clock_core.h
@@ -0,0 +1,55 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/uuid.h"
8#include "core/hle/service/time/clock_types.h"
9
10namespace Core {
11class System;
12}
13
14namespace Service::Time::Clock {
15
16class SteadyClockCore {
17public:
18 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::Generate()};
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
new file mode 100644
index 000000000..5cdb80703
--- /dev/null
+++ b/src/core/hle/service/time/system_clock_context_update_callback.cpp
@@ -0,0 +1,55 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/kernel/writable_event.h"
6#include "core/hle/service/time/errors.h"
7#include "core/hle/service/time/system_clock_context_update_callback.h"
8
9namespace Service::Time::Clock {
10
11SystemClockContextUpdateCallback::SystemClockContextUpdateCallback() = default;
12SystemClockContextUpdateCallback::~SystemClockContextUpdateCallback() = default;
13
14bool SystemClockContextUpdateCallback::NeedUpdate(const SystemClockContext& value) const {
15 if (has_context) {
16 return context.offset != value.offset ||
17 context.steady_time_point.clock_source_id != value.steady_time_point.clock_source_id;
18 }
19
20 return true;
21}
22
23void SystemClockContextUpdateCallback::RegisterOperationEvent(
24 std::shared_ptr<Kernel::WritableEvent>&& writable_event) {
25 operation_event_list.emplace_back(std::move(writable_event));
26}
27
28void SystemClockContextUpdateCallback::BroadcastOperationEvent() {
29 for (const auto& writable_event : operation_event_list) {
30 writable_event->Signal();
31 }
32}
33
34ResultCode SystemClockContextUpdateCallback::Update(const SystemClockContext& value) {
35 ResultCode result{RESULT_SUCCESS};
36
37 if (NeedUpdate(value)) {
38 context = value;
39 has_context = true;
40
41 result = Update();
42
43 if (result == RESULT_SUCCESS) {
44 BroadcastOperationEvent();
45 }
46 }
47
48 return result;
49}
50
51ResultCode SystemClockContextUpdateCallback::Update() {
52 return RESULT_SUCCESS;
53}
54
55} // 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
new file mode 100644
index 000000000..6260de6c3
--- /dev/null
+++ b/src/core/hle/service/time/system_clock_context_update_callback.h
@@ -0,0 +1,43 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <vector>
8
9#include "core/hle/service/time/clock_types.h"
10
11namespace Kernel {
12class WritableEvent;
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 ~SystemClockContextUpdateCallback();
24
25 bool NeedUpdate(const SystemClockContext& value) const;
26
27 void RegisterOperationEvent(std::shared_ptr<Kernel::WritableEvent>&& writable_event);
28
29 void BroadcastOperationEvent();
30
31 ResultCode Update(const SystemClockContext& value);
32
33protected:
34 virtual ResultCode Update();
35
36 SystemClockContext context{};
37
38private:
39 bool has_context{};
40 std::vector<std::shared_ptr<Kernel::WritableEvent>> 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
new file mode 100644
index 000000000..1a3ab8cfa
--- /dev/null
+++ b/src/core/hle/service/time/system_clock_core.cpp
@@ -0,0 +1,72 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/time/steady_clock_core.h"
6#include "core/hle/service/time/system_clock_context_update_callback.h"
7#include "core/hle/service/time/system_clock_core.h"
8
9namespace Service::Time::Clock {
10
11SystemClockCore::SystemClockCore(SteadyClockCore& steady_clock_core)
12 : steady_clock_core{steady_clock_core}, is_initialized{} {
13 context.steady_time_point.clock_source_id = steady_clock_core.GetClockSourceId();
14}
15
16SystemClockCore ::~SystemClockCore() = default;
17
18ResultCode SystemClockCore::GetCurrentTime(Core::System& system, s64& posix_time) const {
19 posix_time = 0;
20
21 const SteadyClockTimePoint current_time_point{steady_clock_core.GetCurrentTimePoint(system)};
22
23 SystemClockContext clock_context{};
24 if (const ResultCode result{GetClockContext(system, clock_context)}; result != RESULT_SUCCESS) {
25 return result;
26 }
27
28 if (current_time_point.clock_source_id != clock_context.steady_time_point.clock_source_id) {
29 return ERROR_TIME_MISMATCH;
30 }
31
32 posix_time = clock_context.offset + current_time_point.time_point;
33
34 return RESULT_SUCCESS;
35}
36
37ResultCode SystemClockCore::SetCurrentTime(Core::System& system, s64 posix_time) {
38 const SteadyClockTimePoint current_time_point{steady_clock_core.GetCurrentTimePoint(system)};
39 const SystemClockContext clock_context{posix_time - current_time_point.time_point,
40 current_time_point};
41
42 if (const ResultCode result{SetClockContext(clock_context)}; result != RESULT_SUCCESS) {
43 return result;
44 }
45 return Flush(clock_context);
46}
47
48ResultCode SystemClockCore::Flush(const SystemClockContext& context) {
49 if (!system_clock_context_update_callback) {
50 return RESULT_SUCCESS;
51 }
52 return system_clock_context_update_callback->Update(context);
53}
54
55ResultCode SystemClockCore::SetSystemClockContext(const SystemClockContext& context) {
56 if (const ResultCode result{SetClockContext(context)}; result != RESULT_SUCCESS) {
57 return result;
58 }
59 return Flush(context);
60}
61
62bool SystemClockCore::IsClockSetup(Core::System& system) const {
63 SystemClockContext value{};
64 if (GetClockContext(system, value) == RESULT_SUCCESS) {
65 const SteadyClockTimePoint steady_clock_time_point{
66 steady_clock_core.GetCurrentTimePoint(system)};
67 return steady_clock_time_point.clock_source_id == value.steady_time_point.clock_source_id;
68 }
69 return {};
70}
71
72} // 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
new file mode 100644
index 000000000..54407a6c5
--- /dev/null
+++ b/src/core/hle/service/time/system_clock_core.h
@@ -0,0 +1,71 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "core/hle/service/time/clock_types.h"
9
10namespace Core {
11class System;
12}
13
14namespace Service::Time::Clock {
15
16class SteadyClockCore;
17class SystemClockContextUpdateCallback;
18
19// Parts of this implementation were based on Ryujinx (https://github.com/Ryujinx/Ryujinx/pull/783).
20// This code was released under public domain.
21
22class SystemClockCore {
23public:
24 explicit SystemClockCore(SteadyClockCore& steady_clock_core);
25 ~SystemClockCore();
26
27 SteadyClockCore& GetSteadyClockCore() const {
28 return steady_clock_core;
29 }
30
31 ResultCode GetCurrentTime(Core::System& system, s64& posix_time) const;
32
33 ResultCode SetCurrentTime(Core::System& system, s64 posix_time);
34
35 virtual ResultCode GetClockContext([[maybe_unused]] Core::System& system,
36 SystemClockContext& value) const {
37 value = context;
38 return RESULT_SUCCESS;
39 }
40
41 virtual ResultCode SetClockContext(const SystemClockContext& value) {
42 context = value;
43 return RESULT_SUCCESS;
44 }
45
46 virtual ResultCode Flush(const SystemClockContext& context);
47
48 void SetUpdateCallbackInstance(std::shared_ptr<SystemClockContextUpdateCallback> callback) {
49 system_clock_context_update_callback = std::move(callback);
50 }
51
52 ResultCode SetSystemClockContext(const SystemClockContext& context);
53
54 bool IsInitialized() const {
55 return is_initialized;
56 }
57
58 void MarkAsInitialized() {
59 is_initialized = true;
60 }
61
62 bool IsClockSetup(Core::System& system) const;
63
64private:
65 SteadyClockCore& steady_clock_core;
66 SystemClockContext context{};
67 bool is_initialized{};
68 std::shared_ptr<SystemClockContextUpdateCallback> system_clock_context_update_callback;
69};
70
71} // 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
new file mode 100644
index 000000000..c77b98189
--- /dev/null
+++ b/src/core/hle/service/time/tick_based_steady_clock_core.cpp
@@ -0,0 +1,24 @@
1// Copyright 2020 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/core.h"
6#include "core/core_timing.h"
7#include "core/core_timing_util.h"
8#include "core/hle/service/time/tick_based_steady_clock_core.h"
9
10namespace Service::Time::Clock {
11
12SteadyClockTimePoint TickBasedSteadyClockCore::GetTimePoint(Core::System& system) {
13 const TimeSpanType ticks_time_span{TimeSpanType::FromTicks(
14 Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()),
15 Core::Timing::CNTFREQ)};
16
17 return {ticks_time_span.ToSeconds(), GetClockSourceId()};
18}
19
20TimeSpanType TickBasedSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) {
21 return TimeSpanType::FromSeconds(GetTimePoint(system).time_point);
22}
23
24} // 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
new file mode 100644
index 000000000..1a5a53fd7
--- /dev/null
+++ b/src/core/hle/service/time/tick_based_steady_clock_core.h
@@ -0,0 +1,29 @@
1// Copyright 2020 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/time/clock_types.h"
8#include "core/hle/service/time/steady_clock_core.h"
9
10namespace Core {
11class System;
12}
13
14namespace Service::Time::Clock {
15
16class TickBasedSteadyClockCore final : public SteadyClockCore {
17public:
18 TimeSpanType GetInternalOffset() const override {
19 return {};
20 }
21
22 void SetInternalOffset(TimeSpanType internal_offset) override {}
23
24 SteadyClockTimePoint GetTimePoint(Core::System& system) override;
25
26 TimeSpanType GetCurrentRawTimePoint(Core::System& system) override;
27};
28
29} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index 6ee77c5f9..970aed0bb 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -1,9 +1,7 @@
1// Copyright 2018 yuzu emulator team 1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <chrono>
6#include <ctime>
7#include "common/logging/log.h" 5#include "common/logging/log.h"
8#include "core/core.h" 6#include "core/core.h"
9#include "core/core_timing.h" 7#include "core/core_timing.h"
@@ -11,429 +9,282 @@
11#include "core/hle/ipc_helpers.h" 9#include "core/hle/ipc_helpers.h"
12#include "core/hle/kernel/client_port.h" 10#include "core/hle/kernel/client_port.h"
13#include "core/hle/kernel/client_session.h" 11#include "core/hle/kernel/client_session.h"
12#include "core/hle/kernel/scheduler.h"
14#include "core/hle/service/time/interface.h" 13#include "core/hle/service/time/interface.h"
15#include "core/hle/service/time/time.h" 14#include "core/hle/service/time/time.h"
16#include "core/hle/service/time/time_sharedmemory.h" 15#include "core/hle/service/time/time_sharedmemory.h"
17#include "core/settings.h" 16#include "core/hle/service/time/time_zone_service.h"
18 17
19namespace Service::Time { 18namespace Service::Time {
20 19
21static std::chrono::seconds GetSecondsSinceEpoch() {
22 return std::chrono::duration_cast<std::chrono::seconds>(
23 std::chrono::system_clock::now().time_since_epoch()) +
24 Settings::values.custom_rtc_differential;
25}
26
27static void PosixToCalendar(u64 posix_time, CalendarTime& calendar_time,
28 CalendarAdditionalInfo& additional_info,
29 [[maybe_unused]] const TimeZoneRule& /*rule*/) {
30 const std::time_t time(posix_time);
31 const std::tm* tm = std::localtime(&time);
32 if (tm == nullptr) {
33 calendar_time = {};
34 additional_info = {};
35 return;
36 }
37 calendar_time.year = static_cast<u16_le>(tm->tm_year + 1900);
38 calendar_time.month = static_cast<u8>(tm->tm_mon + 1);
39 calendar_time.day = static_cast<u8>(tm->tm_mday);
40 calendar_time.hour = static_cast<u8>(tm->tm_hour);
41 calendar_time.minute = static_cast<u8>(tm->tm_min);
42 calendar_time.second = static_cast<u8>(tm->tm_sec);
43
44 additional_info.day_of_week = tm->tm_wday;
45 additional_info.day_of_year = tm->tm_yday;
46 std::memcpy(additional_info.name.data(), "UTC", sizeof("UTC"));
47 additional_info.utc_offset = 0;
48}
49
50static u64 CalendarToPosix(const CalendarTime& calendar_time,
51 [[maybe_unused]] const TimeZoneRule& /*rule*/) {
52 std::tm time{};
53 time.tm_year = calendar_time.year - 1900;
54 time.tm_mon = calendar_time.month - 1;
55 time.tm_mday = calendar_time.day;
56
57 time.tm_hour = calendar_time.hour;
58 time.tm_min = calendar_time.minute;
59 time.tm_sec = calendar_time.second;
60
61 std::time_t epoch_time = std::mktime(&time);
62 return static_cast<u64>(epoch_time);
63}
64
65enum class ClockContextType {
66 StandardSteady,
67 StandardUserSystem,
68 StandardNetworkSystem,
69 StandardLocalSystem,
70};
71
72class ISystemClock final : public ServiceFramework<ISystemClock> { 20class ISystemClock final : public ServiceFramework<ISystemClock> {
73public: 21public:
74 ISystemClock(std::shared_ptr<Service::Time::SharedMemory> shared_memory, 22 ISystemClock(Clock::SystemClockCore& clock_core)
75 ClockContextType clock_type) 23 : ServiceFramework("ISystemClock"), clock_core{clock_core} {
76 : ServiceFramework("ISystemClock"), shared_memory(shared_memory), clock_type(clock_type) {
77 // clang-format off 24 // clang-format off
78 static const FunctionInfo functions[] = { 25 static const FunctionInfo functions[] = {
79 {0, &ISystemClock::GetCurrentTime, "GetCurrentTime"}, 26 {0, &ISystemClock::GetCurrentTime, "GetCurrentTime"},
80 {1, nullptr, "SetCurrentTime"}, 27 {1, nullptr, "SetCurrentTime"},
81 {2, &ISystemClock::GetSystemClockContext, "GetSystemClockContext"}, 28 {2, &ISystemClock::GetSystemClockContext, "GetSystemClockContext"},
82 {3, nullptr, "SetSystemClockContext"}, 29 {3, nullptr, "SetSystemClockContext"},
83 {4, nullptr, "GetOperationEventReadableHandle"}, 30 {4, nullptr, "GetOperationEventReadableHandle"},
84 }; 31 };
85 // clang-format on 32 // clang-format on
86 33
87 RegisterHandlers(functions); 34 RegisterHandlers(functions);
88 UpdateSharedMemoryContext(system_clock_context);
89 } 35 }
90 36
91private: 37private:
92 void GetCurrentTime(Kernel::HLERequestContext& ctx) { 38 void GetCurrentTime(Kernel::HLERequestContext& ctx) {
93 const s64 time_since_epoch{GetSecondsSinceEpoch().count()};
94 LOG_DEBUG(Service_Time, "called"); 39 LOG_DEBUG(Service_Time, "called");
95 40
41 if (!clock_core.IsInitialized()) {
42 IPC::ResponseBuilder rb{ctx, 2};
43 rb.Push(ERROR_UNINITIALIZED_CLOCK);
44 return;
45 }
46
47 s64 posix_time{};
48 if (const ResultCode result{
49 clock_core.GetCurrentTime(Core::System::GetInstance(), posix_time)};
50 result != RESULT_SUCCESS) {
51 IPC::ResponseBuilder rb{ctx, 2};
52 rb.Push(result);
53 return;
54 }
55
96 IPC::ResponseBuilder rb{ctx, 4}; 56 IPC::ResponseBuilder rb{ctx, 4};
97 rb.Push(RESULT_SUCCESS); 57 rb.Push(RESULT_SUCCESS);
98 rb.Push<u64>(time_since_epoch); 58 rb.Push<s64>(posix_time);
99 } 59 }
100 60
101 void GetSystemClockContext(Kernel::HLERequestContext& ctx) { 61 void GetSystemClockContext(Kernel::HLERequestContext& ctx) {
102 LOG_WARNING(Service_Time, "(STUBBED) called"); 62 LOG_DEBUG(Service_Time, "called");
103 63
104 // TODO(ogniK): This should be updated periodically however since we have it stubbed we'll 64 if (!clock_core.IsInitialized()) {
105 // only update when we get a new context 65 IPC::ResponseBuilder rb{ctx, 2};
106 UpdateSharedMemoryContext(system_clock_context); 66 rb.Push(ERROR_UNINITIALIZED_CLOCK);
67 return;
68 }
107 69
108 IPC::ResponseBuilder rb{ctx, (sizeof(SystemClockContext) / 4) + 2}; 70 Clock::SystemClockContext system_clock_context{};
71 if (const ResultCode result{
72 clock_core.GetClockContext(Core::System::GetInstance(), system_clock_context)};
73 result != RESULT_SUCCESS) {
74 IPC::ResponseBuilder rb{ctx, 2};
75 rb.Push(result);
76 return;
77 }
78
79 IPC::ResponseBuilder rb{ctx, sizeof(Clock::SystemClockContext) / 4 + 2};
109 rb.Push(RESULT_SUCCESS); 80 rb.Push(RESULT_SUCCESS);
110 rb.PushRaw(system_clock_context); 81 rb.PushRaw(system_clock_context);
111 } 82 }
112 83
113 void UpdateSharedMemoryContext(const SystemClockContext& clock_context) { 84 Clock::SystemClockCore& clock_core;
114 switch (clock_type) {
115 case ClockContextType::StandardLocalSystem:
116 shared_memory->SetStandardLocalSystemClockContext(clock_context);
117 break;
118 case ClockContextType::StandardNetworkSystem:
119 shared_memory->SetStandardNetworkSystemClockContext(clock_context);
120 break;
121 }
122 }
123
124 SystemClockContext system_clock_context{};
125 std::shared_ptr<Service::Time::SharedMemory> shared_memory;
126 ClockContextType clock_type;
127}; 85};
128 86
129class ISteadyClock final : public ServiceFramework<ISteadyClock> { 87class ISteadyClock final : public ServiceFramework<ISteadyClock> {
130public: 88public:
131 ISteadyClock(std::shared_ptr<SharedMemory> shared_memory, Core::System& system) 89 ISteadyClock(Clock::SteadyClockCore& clock_core)
132 : ServiceFramework("ISteadyClock"), shared_memory(shared_memory), system(system) { 90 : ServiceFramework("ISteadyClock"), clock_core{clock_core} {
133 static const FunctionInfo functions[] = { 91 static const FunctionInfo functions[] = {
134 {0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"}, 92 {0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"},
135 }; 93 };
136 RegisterHandlers(functions); 94 RegisterHandlers(functions);
137
138 shared_memory->SetStandardSteadyClockTimepoint(GetCurrentTimePoint());
139 } 95 }
140 96
141private: 97private:
142 void GetCurrentTimePoint(Kernel::HLERequestContext& ctx) { 98 void GetCurrentTimePoint(Kernel::HLERequestContext& ctx) {
143 LOG_DEBUG(Service_Time, "called"); 99 LOG_DEBUG(Service_Time, "called");
144 100
145 const auto time_point = GetCurrentTimePoint(); 101 if (!clock_core.IsInitialized()) {
146 // TODO(ogniK): This should be updated periodically 102 IPC::ResponseBuilder rb{ctx, 2};
147 shared_memory->SetStandardSteadyClockTimepoint(time_point); 103 rb.Push(ERROR_UNINITIALIZED_CLOCK);
104 return;
105 }
148 106
149 IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2}; 107 const Clock::SteadyClockTimePoint time_point{
108 clock_core.GetCurrentTimePoint(Core::System::GetInstance())};
109 IPC::ResponseBuilder rb{ctx, (sizeof(Clock::SteadyClockTimePoint) / 4) + 2};
150 rb.Push(RESULT_SUCCESS); 110 rb.Push(RESULT_SUCCESS);
151 rb.PushRaw(time_point); 111 rb.PushRaw(time_point);
152 } 112 }
153 113
154 SteadyClockTimePoint GetCurrentTimePoint() const { 114 Clock::SteadyClockCore& clock_core;
155 const auto& core_timing = system.CoreTiming();
156 const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks());
157 return {static_cast<u64_le>(ms.count() / 1000), {}};
158 }
159
160 std::shared_ptr<SharedMemory> shared_memory;
161 Core::System& system;
162}; 115};
163 116
164class ITimeZoneService final : public ServiceFramework<ITimeZoneService> { 117ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
165public: 118 Kernel::Thread* thread, Clock::SystemClockContext user_context,
166 ITimeZoneService() : ServiceFramework("ITimeZoneService") { 119 Clock::SystemClockContext network_context, u8 type, Clock::ClockSnapshot& clock_snapshot) {
167 // clang-format off
168 static const FunctionInfo functions[] = {
169 {0, &ITimeZoneService::GetDeviceLocationName, "GetDeviceLocationName"},
170 {1, nullptr, "SetDeviceLocationName"},
171 {2, &ITimeZoneService::GetTotalLocationNameCount, "GetTotalLocationNameCount"},
172 {3, nullptr, "LoadLocationNameList"},
173 {4, &ITimeZoneService::LoadTimeZoneRule, "LoadTimeZoneRule"},
174 {5, nullptr, "GetTimeZoneRuleVersion"},
175 {6, nullptr, "GetDeviceLocationNameAndUpdatedTime"},
176 {7, nullptr, "SetDeviceLocationNameWithTimeZoneRule"},
177 {8, nullptr, "ParseTimeZoneBinary"},
178 {20, nullptr, "GetDeviceLocationNameOperationEventReadableHandle"},
179 {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"},
180 {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
181 {201, &ITimeZoneService::ToPosixTime, "ToPosixTime"},
182 {202, &ITimeZoneService::ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"},
183 };
184 // clang-format on
185
186 RegisterHandlers(functions);
187 }
188 120
189private: 121 auto& time_manager{module->GetTimeManager()};
190 LocationName location_name{"UTC"};
191 TimeZoneRule my_time_zone_rule{};
192 122
193 void GetDeviceLocationName(Kernel::HLERequestContext& ctx) { 123 clock_snapshot.is_automatic_correction_enabled =
194 LOG_DEBUG(Service_Time, "called"); 124 time_manager.GetStandardUserSystemClockCore().IsAutomaticCorrectionEnabled();
125 clock_snapshot.user_context = user_context;
126 clock_snapshot.network_context = network_context;
195 127
196 IPC::ResponseBuilder rb{ctx, (sizeof(LocationName) / 4) + 2}; 128 if (const ResultCode result{
197 rb.Push(RESULT_SUCCESS); 129 time_manager.GetTimeZoneContentManager().GetTimeZoneManager().GetDeviceLocationName(
198 rb.PushRaw(location_name); 130 clock_snapshot.location_name)};
131 result != RESULT_SUCCESS) {
132 return result;
199 } 133 }
200 134
201 void GetTotalLocationNameCount(Kernel::HLERequestContext& ctx) { 135 const auto current_time_point{
202 LOG_WARNING(Service_Time, "(STUBBED) called"); 136 time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(Core::System::GetInstance())};
203 137 if (const ResultCode result{Clock::ClockSnapshot::GetCurrentTime(
204 IPC::ResponseBuilder rb{ctx, 3}; 138 clock_snapshot.user_time, current_time_point, clock_snapshot.user_context)};
205 rb.Push(RESULT_SUCCESS); 139 result != RESULT_SUCCESS) {
206 rb.Push<u32>(0); 140 return result;
207 } 141 }
208 142
209 void LoadTimeZoneRule(Kernel::HLERequestContext& ctx) { 143 TimeZone::CalendarInfo userCalendarInfo{};
210 LOG_WARNING(Service_Time, "(STUBBED) called"); 144 if (const ResultCode result{
211 145 time_manager.GetTimeZoneContentManager().GetTimeZoneManager().ToCalendarTimeWithMyRules(
212 ctx.WriteBuffer(&my_time_zone_rule, sizeof(TimeZoneRule)); 146 clock_snapshot.user_time, userCalendarInfo)};
213 147 result != RESULT_SUCCESS) {
214 IPC::ResponseBuilder rb{ctx, 2}; 148 return result;
215 rb.Push(RESULT_SUCCESS);
216 } 149 }
217 150
218 void ToCalendarTime(Kernel::HLERequestContext& ctx) { 151 clock_snapshot.user_calendar_time = userCalendarInfo.time;
219 IPC::RequestParser rp{ctx}; 152 clock_snapshot.user_calendar_additional_time = userCalendarInfo.additiona_info;
220 const u64 posix_time = rp.Pop<u64>();
221 LOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x{:016X}", posix_time);
222
223 TimeZoneRule time_zone_rule{};
224 auto buffer = ctx.ReadBuffer();
225 std::memcpy(&time_zone_rule, buffer.data(), buffer.size());
226
227 CalendarTime calendar_time{2018, 1, 1, 0, 0, 0};
228 CalendarAdditionalInfo additional_info{};
229 153
230 PosixToCalendar(posix_time, calendar_time, additional_info, time_zone_rule); 154 if (Clock::ClockSnapshot::GetCurrentTime(clock_snapshot.network_time, current_time_point,
231 155 clock_snapshot.network_context) != RESULT_SUCCESS) {
232 IPC::ResponseBuilder rb{ctx, 10}; 156 clock_snapshot.network_time = 0;
233 rb.Push(RESULT_SUCCESS);
234 rb.PushRaw(calendar_time);
235 rb.PushRaw(additional_info);
236 } 157 }
237 158
238 void ToCalendarTimeWithMyRule(Kernel::HLERequestContext& ctx) { 159 TimeZone::CalendarInfo networkCalendarInfo{};
239 IPC::RequestParser rp{ctx}; 160 if (const ResultCode result{
240 const u64 posix_time = rp.Pop<u64>(); 161 time_manager.GetTimeZoneContentManager().GetTimeZoneManager().ToCalendarTimeWithMyRules(
241 LOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x{:016X}", posix_time); 162 clock_snapshot.network_time, networkCalendarInfo)};
242 163 result != RESULT_SUCCESS) {
243 CalendarTime calendar_time{2018, 1, 1, 0, 0, 0}; 164 return result;
244 CalendarAdditionalInfo additional_info{};
245
246 PosixToCalendar(posix_time, calendar_time, additional_info, my_time_zone_rule);
247
248 IPC::ResponseBuilder rb{ctx, 10};
249 rb.Push(RESULT_SUCCESS);
250 rb.PushRaw(calendar_time);
251 rb.PushRaw(additional_info);
252 }
253
254 void ToPosixTime(Kernel::HLERequestContext& ctx) {
255 // TODO(ogniK): Figure out how to handle multiple times
256 LOG_WARNING(Service_Time, "(STUBBED) called");
257
258 IPC::RequestParser rp{ctx};
259 auto calendar_time = rp.PopRaw<CalendarTime>();
260 auto posix_time = CalendarToPosix(calendar_time, {});
261
262 IPC::ResponseBuilder rb{ctx, 3};
263 rb.Push(RESULT_SUCCESS);
264 rb.PushRaw<u32>(1); // Amount of times we're returning
265 ctx.WriteBuffer(&posix_time, sizeof(u64));
266 } 165 }
267 166
268 void ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) { 167 clock_snapshot.network_calendar_time = networkCalendarInfo.time;
269 LOG_WARNING(Service_Time, "(STUBBED) called"); 168 clock_snapshot.network_calendar_additional_time = networkCalendarInfo.additiona_info;
169 clock_snapshot.type = type;
270 170
271 IPC::RequestParser rp{ctx}; 171 return RESULT_SUCCESS;
272 auto calendar_time = rp.PopRaw<CalendarTime>(); 172}
273 auto posix_time = CalendarToPosix(calendar_time, {});
274
275 IPC::ResponseBuilder rb{ctx, 3};
276 rb.Push(RESULT_SUCCESS);
277 rb.PushRaw<u32>(1); // Amount of times we're returning
278 ctx.WriteBuffer(&posix_time, sizeof(u64));
279 }
280};
281 173
282void Module::Interface::GetStandardUserSystemClock(Kernel::HLERequestContext& ctx) { 174void Module::Interface::GetStandardUserSystemClock(Kernel::HLERequestContext& ctx) {
283 LOG_DEBUG(Service_Time, "called"); 175 LOG_DEBUG(Service_Time, "called");
284
285 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 176 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
286 rb.Push(RESULT_SUCCESS); 177 rb.Push(RESULT_SUCCESS);
287 rb.PushIpcInterface<ISystemClock>(shared_memory, ClockContextType::StandardUserSystem); 178 rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardUserSystemClockCore());
288} 179}
289 180
290void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx) { 181void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx) {
291 LOG_DEBUG(Service_Time, "called"); 182 LOG_DEBUG(Service_Time, "called");
292
293 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 183 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
294 rb.Push(RESULT_SUCCESS); 184 rb.Push(RESULT_SUCCESS);
295 rb.PushIpcInterface<ISystemClock>(shared_memory, ClockContextType::StandardNetworkSystem); 185 rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardNetworkSystemClockCore());
296} 186}
297 187
298void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) { 188void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) {
299 LOG_DEBUG(Service_Time, "called"); 189 LOG_DEBUG(Service_Time, "called");
300
301 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 190 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
302 rb.Push(RESULT_SUCCESS); 191 rb.Push(RESULT_SUCCESS);
303 rb.PushIpcInterface<ISteadyClock>(shared_memory, system); 192 rb.PushIpcInterface<ISteadyClock>(module->GetTimeManager().GetStandardSteadyClockCore());
304} 193}
305 194
306void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) { 195void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) {
307 LOG_DEBUG(Service_Time, "called"); 196 LOG_DEBUG(Service_Time, "called");
308
309 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 197 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
310 rb.Push(RESULT_SUCCESS); 198 rb.Push(RESULT_SUCCESS);
311 rb.PushIpcInterface<ITimeZoneService>(); 199 rb.PushIpcInterface<ITimeZoneService>(module->GetTimeManager().GetTimeZoneContentManager());
312} 200}
313 201
314void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx) { 202void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERequestContext& ctx) {
315 LOG_DEBUG(Service_Time, "called"); 203 LOG_DEBUG(Service_Time, "called");
316 204
317 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 205 auto& steady_clock_core{module->GetTimeManager().GetStandardSteadyClockCore()};
318 rb.Push(RESULT_SUCCESS); 206 if (!steady_clock_core.IsInitialized()) {
319 rb.PushIpcInterface<ISystemClock>(shared_memory, ClockContextType::StandardLocalSystem); 207 IPC::ResponseBuilder rb{ctx, 2};
208 rb.Push(ERROR_UNINITIALIZED_CLOCK);
209 return;
210 }
211
212 IPC::RequestParser rp{ctx};
213 const auto context{rp.PopRaw<Clock::SystemClockContext>()};
214 const auto current_time_point{
215 steady_clock_core.GetCurrentTimePoint(Core::System::GetInstance())};
216
217 if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) {
218 const auto ticks{Clock::TimeSpanType::FromTicks(
219 Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()),
220 Core::Timing::CNTFREQ)};
221 const s64 base_time_point{context.offset + current_time_point.time_point -
222 ticks.ToSeconds()};
223 IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2};
224 rb.Push(RESULT_SUCCESS);
225 rb.PushRaw(base_time_point);
226 return;
227 }
228
229 IPC::ResponseBuilder rb{ctx, 2};
230 rb.Push(ERROR_TIME_MISMATCH);
320} 231}
321 232
322void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { 233void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
323 LOG_DEBUG(Service_Time, "called"); 234 LOG_DEBUG(Service_Time, "called");
324
325 IPC::RequestParser rp{ctx}; 235 IPC::RequestParser rp{ctx};
326 const auto initial_type = rp.PopRaw<u8>(); 236 const auto type = rp.PopRaw<u8>();
327 237
328 const s64 time_since_epoch{GetSecondsSinceEpoch().count()}; 238 Clock::SystemClockContext user_context{};
329 const std::time_t time(time_since_epoch); 239 if (const ResultCode result{
330 const std::tm* tm = std::localtime(&time); 240 module->GetTimeManager().GetStandardUserSystemClockCore().GetClockContext(
331 if (tm == nullptr) { 241 Core::System::GetInstance(), user_context)};
332 LOG_ERROR(Service_Time, "tm is a nullptr"); 242 result != RESULT_SUCCESS) {
333 IPC::ResponseBuilder rb{ctx, 2}; 243 IPC::ResponseBuilder rb{ctx, 2};
334 rb.Push(RESULT_UNKNOWN); // TODO(ogniK): Find appropriate error code 244 rb.Push(result);
245 return;
246 }
247 Clock::SystemClockContext network_context{};
248 if (const ResultCode result{
249 module->GetTimeManager().GetStandardNetworkSystemClockCore().GetClockContext(
250 Core::System::GetInstance(), network_context)};
251 result != RESULT_SUCCESS) {
252 IPC::ResponseBuilder rb{ctx, 2};
253 rb.Push(result);
335 return; 254 return;
336 } 255 }
337 256
338 const auto& core_timing = system.CoreTiming(); 257 Clock::ClockSnapshot clock_snapshot{};
339 const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks()); 258 if (const ResultCode result{GetClockSnapshotFromSystemClockContextInternal(
340 const SteadyClockTimePoint steady_clock_time_point{static_cast<u64_le>(ms.count() / 1000), {}}; 259 &ctx.GetThread(), user_context, network_context, type, clock_snapshot)};
341 260 result != RESULT_SUCCESS) {
342 CalendarTime calendar_time{}; 261 IPC::ResponseBuilder rb{ctx, 2};
343 calendar_time.year = static_cast<u16_le>(tm->tm_year + 1900); 262 rb.Push(result);
344 calendar_time.month = static_cast<u8>(tm->tm_mon + 1); 263 return;
345 calendar_time.day = static_cast<u8>(tm->tm_mday); 264 }
346 calendar_time.hour = static_cast<u8>(tm->tm_hour);
347 calendar_time.minute = static_cast<u8>(tm->tm_min);
348 calendar_time.second = static_cast<u8>(tm->tm_sec);
349
350 ClockSnapshot clock_snapshot{};
351 clock_snapshot.system_posix_time = time_since_epoch;
352 clock_snapshot.network_posix_time = time_since_epoch;
353 clock_snapshot.system_calendar_time = calendar_time;
354 clock_snapshot.network_calendar_time = calendar_time;
355
356 CalendarAdditionalInfo additional_info{};
357 PosixToCalendar(time_since_epoch, calendar_time, additional_info, {});
358
359 clock_snapshot.system_calendar_info = additional_info;
360 clock_snapshot.network_calendar_info = additional_info;
361
362 clock_snapshot.steady_clock_timepoint = steady_clock_time_point;
363 clock_snapshot.location_name = LocationName{"UTC"};
364 clock_snapshot.clock_auto_adjustment_enabled = 1;
365 clock_snapshot.type = initial_type;
366 265
367 IPC::ResponseBuilder rb{ctx, 2}; 266 IPC::ResponseBuilder rb{ctx, 2};
368 rb.Push(RESULT_SUCCESS); 267 rb.Push(RESULT_SUCCESS);
369 ctx.WriteBuffer(&clock_snapshot, sizeof(ClockSnapshot)); 268 ctx.WriteBuffer(&clock_snapshot, sizeof(Clock::ClockSnapshot));
370}
371
372void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser(
373 Kernel::HLERequestContext& ctx) {
374 LOG_DEBUG(Service_Time, "called");
375
376 IPC::RequestParser rp{ctx};
377 const auto snapshot_a = rp.PopRaw<ClockSnapshot>();
378 const auto snapshot_b = rp.PopRaw<ClockSnapshot>();
379 const u64 difference =
380 snapshot_b.user_clock_context.offset - snapshot_a.user_clock_context.offset;
381
382 IPC::ResponseBuilder rb{ctx, 4};
383 rb.Push(RESULT_SUCCESS);
384 rb.PushRaw<u64>(difference);
385} 269}
386 270
387void Module::Interface::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { 271void Module::Interface::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
388 LOG_DEBUG(Service_Time, "called"); 272 LOG_DEBUG(Service_Time, "called");
389 IPC::ResponseBuilder rb{ctx, 2, 1}; 273 IPC::ResponseBuilder rb{ctx, 2, 1};
390 rb.Push(RESULT_SUCCESS); 274 rb.Push(RESULT_SUCCESS);
391 rb.PushCopyObjects(shared_memory->GetSharedMemoryHolder()); 275 rb.PushCopyObjects(module->GetTimeManager().GetSharedMemory().GetSharedMemoryHolder());
392}
393
394void Module::Interface::IsStandardUserSystemClockAutomaticCorrectionEnabled(
395 Kernel::HLERequestContext& ctx) {
396 // ogniK(TODO): When clock contexts are implemented, the value should be read from the context
397 // instead of our shared memory holder
398 LOG_DEBUG(Service_Time, "called");
399
400 IPC::ResponseBuilder rb{ctx, 3};
401 rb.Push(RESULT_SUCCESS);
402 rb.Push<u8>(shared_memory->GetStandardUserSystemClockAutomaticCorrectionEnabled());
403}
404
405void Module::Interface::SetStandardUserSystemClockAutomaticCorrectionEnabled(
406 Kernel::HLERequestContext& ctx) {
407 IPC::RequestParser rp{ctx};
408 const auto enabled = rp.Pop<u8>();
409
410 LOG_WARNING(Service_Time, "(PARTIAL IMPLEMENTATION) called");
411
412 // TODO(ogniK): Update clock contexts and correct timespans
413
414 shared_memory->SetStandardUserSystemClockAutomaticCorrectionEnabled(enabled > 0);
415 IPC::ResponseBuilder rb{ctx, 2};
416 rb.Push(RESULT_SUCCESS);
417} 276}
418 277
419Module::Interface::Interface(std::shared_ptr<Module> time, 278Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name)
420 std::shared_ptr<SharedMemory> shared_memory, Core::System& system, 279 : ServiceFramework(name), module{std::move(module)}, system{system} {}
421 const char* name)
422 : ServiceFramework(name), time(std::move(time)), shared_memory(std::move(shared_memory)),
423 system(system) {}
424 280
425Module::Interface::~Interface() = default; 281Module::Interface::~Interface() = default;
426 282
427void InstallInterfaces(Core::System& system) { 283void InstallInterfaces(Core::System& system) {
428 auto time = std::make_shared<Module>(); 284 auto module = std::make_shared<Module>(system);
429 auto shared_mem = std::make_shared<SharedMemory>(system); 285 std::make_shared<Time>(module, system, "time:a")->InstallAsService(system.ServiceManager());
430 286 std::make_shared<Time>(module, system, "time:s")->InstallAsService(system.ServiceManager());
431 std::make_shared<Time>(time, shared_mem, system, "time:a") 287 std::make_shared<Time>(module, system, "time:u")->InstallAsService(system.ServiceManager());
432 ->InstallAsService(system.ServiceManager());
433 std::make_shared<Time>(time, shared_mem, system, "time:s")
434 ->InstallAsService(system.ServiceManager());
435 std::make_shared<Time>(std::move(time), shared_mem, system, "time:u")
436 ->InstallAsService(system.ServiceManager());
437} 288}
438 289
439} // namespace Service::Time 290} // namespace Service::Time
diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h
index c32d32860..7b77ac7ea 100644
--- a/src/core/hle/service/time/time.h
+++ b/src/core/hle/service/time/time.h
@@ -4,102 +4,50 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include "common/common_funcs.h"
9#include "core/hle/service/service.h" 7#include "core/hle/service/service.h"
8#include "core/hle/service/time/clock_types.h"
9#include "core/hle/service/time/time_manager.h"
10 10
11namespace Service::Time { 11namespace Core {
12 12class System;
13class SharedMemory; 13}
14
15struct LocationName {
16 std::array<u8, 0x24> name;
17};
18static_assert(sizeof(LocationName) == 0x24, "LocationName is incorrect size");
19
20struct CalendarTime {
21 u16_le year;
22 u8 month; // Starts at 1
23 u8 day; // Starts at 1
24 u8 hour;
25 u8 minute;
26 u8 second;
27};
28static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime structure has incorrect size");
29
30struct CalendarAdditionalInfo {
31 u32_le day_of_week;
32 u32_le day_of_year;
33 std::array<u8, 8> name;
34 u8 is_dst;
35 s32_le utc_offset;
36};
37static_assert(sizeof(CalendarAdditionalInfo) == 0x18,
38 "CalendarAdditionalInfo structure has incorrect size");
39
40// TODO(mailwl) RE this structure
41struct TimeZoneRule {
42 INSERT_PADDING_BYTES(0x4000);
43};
44
45struct SteadyClockTimePoint {
46 using SourceID = std::array<u8, 16>;
47 14
48 u64_le value; 15namespace Service::Time {
49 SourceID source_id;
50};
51static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size");
52
53struct SystemClockContext {
54 u64_le offset;
55 SteadyClockTimePoint time_point;
56};
57static_assert(sizeof(SystemClockContext) == 0x20,
58 "SystemClockContext structure has incorrect size");
59
60struct ClockSnapshot {
61 SystemClockContext user_clock_context;
62 SystemClockContext network_clock_context;
63 s64_le system_posix_time;
64 s64_le network_posix_time;
65 CalendarTime system_calendar_time;
66 CalendarTime network_calendar_time;
67 CalendarAdditionalInfo system_calendar_info;
68 CalendarAdditionalInfo network_calendar_info;
69 SteadyClockTimePoint steady_clock_timepoint;
70 LocationName location_name;
71 u8 clock_auto_adjustment_enabled;
72 u8 type;
73 u8 version;
74 INSERT_PADDING_BYTES(1);
75};
76static_assert(sizeof(ClockSnapshot) == 0xd0, "ClockSnapshot is an invalid size");
77 16
78class Module final { 17class Module final {
79public: 18public:
19 Module(Core::System& system) : time_manager{system} {}
20
80 class Interface : public ServiceFramework<Interface> { 21 class Interface : public ServiceFramework<Interface> {
81 public: 22 public:
82 explicit Interface(std::shared_ptr<Module> time, 23 explicit Interface(std::shared_ptr<Module> module, Core::System& system, const char* name);
83 std::shared_ptr<SharedMemory> shared_memory, Core::System& system,
84 const char* name);
85 ~Interface() override; 24 ~Interface() override;
86 25
87 void GetStandardUserSystemClock(Kernel::HLERequestContext& ctx); 26 void GetStandardUserSystemClock(Kernel::HLERequestContext& ctx);
88 void GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx); 27 void GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx);
89 void GetStandardSteadyClock(Kernel::HLERequestContext& ctx); 28 void GetStandardSteadyClock(Kernel::HLERequestContext& ctx);
90 void GetTimeZoneService(Kernel::HLERequestContext& ctx); 29 void GetTimeZoneService(Kernel::HLERequestContext& ctx);
91 void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx); 30 void CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERequestContext& ctx);
92 void GetClockSnapshot(Kernel::HLERequestContext& ctx); 31 void GetClockSnapshot(Kernel::HLERequestContext& ctx);
93 void CalculateStandardUserSystemClockDifferenceByUser(Kernel::HLERequestContext& ctx);
94 void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx); 32 void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx);
95 void IsStandardUserSystemClockAutomaticCorrectionEnabled(Kernel::HLERequestContext& ctx); 33
96 void SetStandardUserSystemClockAutomaticCorrectionEnabled(Kernel::HLERequestContext& ctx); 34 private:
35 ResultCode GetClockSnapshotFromSystemClockContextInternal(
36 Kernel::Thread* thread, Clock::SystemClockContext user_context,
37 Clock::SystemClockContext network_context, u8 type,
38 Clock::ClockSnapshot& cloc_snapshot);
97 39
98 protected: 40 protected:
99 std::shared_ptr<Module> time; 41 std::shared_ptr<Module> module;
100 std::shared_ptr<SharedMemory> shared_memory;
101 Core::System& system; 42 Core::System& system;
102 }; 43 };
44
45 TimeManager& GetTimeManager() {
46 return time_manager;
47 }
48
49private:
50 TimeManager time_manager;
103}; 51};
104 52
105/// Registers all Time services with the specified service manager. 53/// Registers all Time services with the specified service manager.
diff --git a/src/core/hle/service/time/time_manager.cpp b/src/core/hle/service/time/time_manager.cpp
new file mode 100644
index 000000000..9d6c55865
--- /dev/null
+++ b/src/core/hle/service/time/time_manager.cpp
@@ -0,0 +1,137 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <chrono>
6#include <ctime>
7
8#include "core/hle/service/time/ephemeral_network_system_clock_context_writer.h"
9#include "core/hle/service/time/local_system_clock_context_writer.h"
10#include "core/hle/service/time/network_system_clock_context_writer.h"
11#include "core/hle/service/time/time_manager.h"
12#include "core/settings.h"
13
14namespace Service::Time {
15
16constexpr Clock::TimeSpanType standard_network_clock_accuracy{0x0009356907420000ULL};
17
18static std::chrono::seconds GetSecondsSinceEpoch() {
19 return std::chrono::duration_cast<std::chrono::seconds>(
20 std::chrono::system_clock::now().time_since_epoch()) +
21 Settings::values.custom_rtc_differential;
22}
23
24static s64 GetExternalRtcValue() {
25 return GetSecondsSinceEpoch().count();
26}
27
28TimeManager::TimeManager(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{*this, system} {
41
42 const auto system_time{Clock::TimeSpanType::FromSeconds(GetExternalRtcValue())};
43 SetupStandardSteadyClock(system, Common::UUID::Generate(), system_time, {}, {});
44 SetupStandardLocalSystemClock(system, {}, system_time.ToSeconds());
45 SetupStandardNetworkSystemClock({}, standard_network_clock_accuracy);
46 SetupStandardUserSystemClock(system, {}, Clock::SteadyClockTimePoint::GetRandom());
47 SetupEphemeralNetworkSystemClock();
48}
49
50TimeManager::~TimeManager() = default;
51
52void TimeManager::SetupTimeZoneManager(std::string location_name,
53 Clock::SteadyClockTimePoint time_zone_updated_time_point,
54 std::size_t total_location_name_count,
55 u128 time_zone_rule_version,
56 FileSys::VirtualFile& vfs_file) {
57 if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule(
58 location_name, vfs_file) != RESULT_SUCCESS) {
59 UNREACHABLE();
60 return;
61 }
62
63 time_zone_content_manager.GetTimeZoneManager().SetUpdatedTime(time_zone_updated_time_point);
64 time_zone_content_manager.GetTimeZoneManager().SetTotalLocationNameCount(
65 total_location_name_count);
66 time_zone_content_manager.GetTimeZoneManager().SetTimeZoneRuleVersion(time_zone_rule_version);
67 time_zone_content_manager.GetTimeZoneManager().MarkAsInitialized();
68}
69
70void TimeManager::SetupStandardSteadyClock(Core::System& system, Common::UUID clock_source_id,
71 Clock::TimeSpanType setup_value,
72 Clock::TimeSpanType internal_offset,
73 bool is_rtc_reset_detected) {
74 standard_steady_clock_core.SetClockSourceId(clock_source_id);
75 standard_steady_clock_core.SetSetupValue(setup_value);
76 standard_steady_clock_core.SetInternalOffset(internal_offset);
77 standard_steady_clock_core.MarkAsInitialized();
78
79 const auto current_time_point{standard_steady_clock_core.GetCurrentRawTimePoint(system)};
80 shared_memory.SetupStandardSteadyClock(system, clock_source_id, current_time_point);
81}
82
83void TimeManager::SetupStandardLocalSystemClock(Core::System& system,
84 Clock::SystemClockContext clock_context,
85 s64 posix_time) {
86 standard_local_system_clock_core.SetUpdateCallbackInstance(local_system_clock_context_writer);
87
88 const auto current_time_point{
89 standard_local_system_clock_core.GetSteadyClockCore().GetCurrentTimePoint(system)};
90 if (current_time_point.clock_source_id == clock_context.steady_time_point.clock_source_id) {
91 standard_local_system_clock_core.SetSystemClockContext(clock_context);
92 } else {
93 if (standard_local_system_clock_core.SetCurrentTime(system, posix_time) != RESULT_SUCCESS) {
94 UNREACHABLE();
95 return;
96 }
97 }
98
99 standard_local_system_clock_core.MarkAsInitialized();
100}
101
102void TimeManager::SetupStandardNetworkSystemClock(Clock::SystemClockContext clock_context,
103 Clock::TimeSpanType sufficient_accuracy) {
104 standard_network_system_clock_core.SetUpdateCallbackInstance(
105 network_system_clock_context_writer);
106
107 if (standard_network_system_clock_core.SetSystemClockContext(clock_context) != RESULT_SUCCESS) {
108 UNREACHABLE();
109 return;
110 }
111
112 standard_network_system_clock_core.SetStandardNetworkClockSufficientAccuracy(
113 sufficient_accuracy);
114 standard_network_system_clock_core.MarkAsInitialized();
115}
116
117void TimeManager::SetupStandardUserSystemClock(
118 Core::System& system, bool is_automatic_correction_enabled,
119 Clock::SteadyClockTimePoint steady_clock_time_point) {
120 if (standard_user_system_clock_core.SetAutomaticCorrectionEnabled(
121 system, is_automatic_correction_enabled) != RESULT_SUCCESS) {
122 UNREACHABLE();
123 return;
124 }
125
126 standard_user_system_clock_core.SetAutomaticCorrectionUpdatedTime(steady_clock_time_point);
127 standard_user_system_clock_core.MarkAsInitialized();
128 shared_memory.SetAutomaticCorrectionEnabled(is_automatic_correction_enabled);
129}
130
131void TimeManager::SetupEphemeralNetworkSystemClock() {
132 ephemeral_network_system_clock_core.SetUpdateCallbackInstance(
133 ephemeral_network_system_clock_context_writer);
134 ephemeral_network_system_clock_core.MarkAsInitialized();
135}
136
137} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_manager.h b/src/core/hle/service/time/time_manager.h
new file mode 100644
index 000000000..8e65f0d22
--- /dev/null
+++ b/src/core/hle/service/time/time_manager.h
@@ -0,0 +1,117 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "core/file_sys/vfs_types.h"
9#include "core/hle/service/time/clock_types.h"
10#include "core/hle/service/time/ephemeral_network_system_clock_core.h"
11#include "core/hle/service/time/standard_local_system_clock_core.h"
12#include "core/hle/service/time/standard_network_system_clock_core.h"
13#include "core/hle/service/time/standard_steady_clock_core.h"
14#include "core/hle/service/time/standard_user_system_clock_core.h"
15#include "core/hle/service/time/tick_based_steady_clock_core.h"
16#include "core/hle/service/time/time_sharedmemory.h"
17#include "core/hle/service/time/time_zone_content_manager.h"
18
19namespace Service::Time {
20
21namespace Clock {
22class EphemeralNetworkSystemClockContextWriter;
23class LocalSystemClockContextWriter;
24class NetworkSystemClockContextWriter;
25} // namespace Clock
26
27// Parts of this implementation were based on Ryujinx (https://github.com/Ryujinx/Ryujinx/pull/783).
28// This code was released under public domain.
29
30class TimeManager final {
31public:
32 explicit TimeManager(Core::System& system);
33 ~TimeManager();
34
35 Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() {
36 return standard_steady_clock_core;
37 }
38
39 const Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() const {
40 return standard_steady_clock_core;
41 }
42
43 Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() {
44 return standard_local_system_clock_core;
45 }
46
47 const Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() const {
48 return standard_local_system_clock_core;
49 }
50
51 Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() {
52 return standard_network_system_clock_core;
53 }
54
55 const Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() const {
56 return standard_network_system_clock_core;
57 }
58
59 Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() {
60 return standard_user_system_clock_core;
61 }
62
63 const Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() const {
64 return standard_user_system_clock_core;
65 }
66
67 TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() {
68 return time_zone_content_manager;
69 }
70
71 const TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() const {
72 return time_zone_content_manager;
73 }
74
75 SharedMemory& GetSharedMemory() {
76 return shared_memory;
77 }
78
79 const SharedMemory& GetSharedMemory() const {
80 return shared_memory;
81 }
82
83 void SetupTimeZoneManager(std::string location_name,
84 Clock::SteadyClockTimePoint time_zone_updated_time_point,
85 std::size_t total_location_name_count, u128 time_zone_rule_version,
86 FileSys::VirtualFile& vfs_file);
87
88private:
89 void SetupStandardSteadyClock(Core::System& system, Common::UUID clock_source_id,
90 Clock::TimeSpanType setup_value,
91 Clock::TimeSpanType internal_offset, bool is_rtc_reset_detected);
92 void SetupStandardLocalSystemClock(Core::System& system,
93 Clock::SystemClockContext clock_context, s64 posix_time);
94 void SetupStandardNetworkSystemClock(Clock::SystemClockContext clock_context,
95 Clock::TimeSpanType sufficient_accuracy);
96 void SetupStandardUserSystemClock(Core::System& system, bool is_automatic_correction_enabled,
97 Clock::SteadyClockTimePoint steady_clock_time_point);
98 void SetupEphemeralNetworkSystemClock();
99
100 SharedMemory shared_memory;
101
102 Clock::StandardSteadyClockCore standard_steady_clock_core;
103 Clock::TickBasedSteadyClockCore tick_based_steady_clock_core;
104 Clock::StandardLocalSystemClockCore standard_local_system_clock_core;
105 Clock::StandardNetworkSystemClockCore standard_network_system_clock_core;
106 Clock::StandardUserSystemClockCore standard_user_system_clock_core;
107 Clock::EphemeralNetworkSystemClockCore ephemeral_network_system_clock_core;
108
109 std::shared_ptr<Clock::LocalSystemClockContextWriter> local_system_clock_context_writer;
110 std::shared_ptr<Clock::NetworkSystemClockContextWriter> network_system_clock_context_writer;
111 std::shared_ptr<Clock::EphemeralNetworkSystemClockContextWriter>
112 ephemeral_network_system_clock_context_writer;
113
114 TimeZone::TimeZoneContentManager time_zone_content_manager;
115};
116
117} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp
index 4035f5072..9b03191bf 100644
--- a/src/core/hle/service/time/time_sharedmemory.cpp
+++ b/src/core/hle/service/time/time_sharedmemory.cpp
@@ -3,20 +3,21 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/core.h" 5#include "core/core.h"
6#include "core/core_timing.h"
7#include "core/core_timing_util.h"
8#include "core/hle/service/time/clock_types.h"
9#include "core/hle/service/time/steady_clock_core.h"
6#include "core/hle/service/time/time_sharedmemory.h" 10#include "core/hle/service/time/time_sharedmemory.h"
7 11
8namespace Service::Time { 12namespace Service::Time {
9const std::size_t SHARED_MEMORY_SIZE = 0x1000; 13
14static constexpr std::size_t SHARED_MEMORY_SIZE{0x1000};
10 15
11SharedMemory::SharedMemory(Core::System& system) : system(system) { 16SharedMemory::SharedMemory(Core::System& system) : system(system) {
12 shared_memory_holder = Kernel::SharedMemory::Create( 17 shared_memory_holder = Kernel::SharedMemory::Create(
13 system.Kernel(), nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite, 18 system.Kernel(), nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite,
14 Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "Time:SharedMemory"); 19 Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "Time:SharedMemory");
15 20 std::memset(shared_memory_holder->GetPointer(), 0, SHARED_MEMORY_SIZE);
16 // Seems static from 1.0.0 -> 8.1.0. Specific games seem to check this value and crash
17 // if it's set to anything else
18 shared_memory_format.format_version = 14;
19 std::memcpy(shared_memory_holder->GetPointer(), &shared_memory_format, sizeof(Format));
20} 21}
21 22
22SharedMemory::~SharedMemory() = default; 23SharedMemory::~SharedMemory() = default;
@@ -25,44 +26,32 @@ std::shared_ptr<Kernel::SharedMemory> SharedMemory::GetSharedMemoryHolder() cons
25 return shared_memory_holder; 26 return shared_memory_holder;
26} 27}
27 28
28void SharedMemory::SetStandardSteadyClockTimepoint(const SteadyClockTimePoint& timepoint) { 29void SharedMemory::SetupStandardSteadyClock(Core::System& system,
30 const Common::UUID& clock_source_id,
31 Clock::TimeSpanType current_time_point) {
32 const Clock::TimeSpanType ticks_time_span{Clock::TimeSpanType::FromTicks(
33 Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()),
34 Core::Timing::CNTFREQ)};
35 const Clock::SteadyClockContext context{
36 static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds),
37 clock_source_id};
29 shared_memory_format.standard_steady_clock_timepoint.StoreData( 38 shared_memory_format.standard_steady_clock_timepoint.StoreData(
30 shared_memory_holder->GetPointer(), timepoint); 39 shared_memory_holder->GetPointer(), context);
31} 40}
32 41
33void SharedMemory::SetStandardLocalSystemClockContext(const SystemClockContext& context) { 42void SharedMemory::UpdateLocalSystemClockContext(const Clock::SystemClockContext& context) {
34 shared_memory_format.standard_local_system_clock_context.StoreData( 43 shared_memory_format.standard_local_system_clock_context.StoreData(
35 shared_memory_holder->GetPointer(), context); 44 shared_memory_holder->GetPointer(), context);
36} 45}
37 46
38void SharedMemory::SetStandardNetworkSystemClockContext(const SystemClockContext& context) { 47void SharedMemory::UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context) {
39 shared_memory_format.standard_network_system_clock_context.StoreData( 48 shared_memory_format.standard_network_system_clock_context.StoreData(
40 shared_memory_holder->GetPointer(), context); 49 shared_memory_holder->GetPointer(), context);
41} 50}
42 51
43void SharedMemory::SetStandardUserSystemClockAutomaticCorrectionEnabled(bool enabled) { 52void SharedMemory::SetAutomaticCorrectionEnabled(bool is_enabled) {
44 shared_memory_format.standard_user_system_clock_automatic_correction.StoreData( 53 shared_memory_format.standard_user_system_clock_automatic_correction.StoreData(
45 shared_memory_holder->GetPointer(), enabled); 54 shared_memory_holder->GetPointer(), is_enabled);
46}
47
48SteadyClockTimePoint SharedMemory::GetStandardSteadyClockTimepoint() {
49 return shared_memory_format.standard_steady_clock_timepoint.ReadData(
50 shared_memory_holder->GetPointer());
51}
52
53SystemClockContext SharedMemory::GetStandardLocalSystemClockContext() {
54 return shared_memory_format.standard_local_system_clock_context.ReadData(
55 shared_memory_holder->GetPointer());
56}
57
58SystemClockContext SharedMemory::GetStandardNetworkSystemClockContext() {
59 return shared_memory_format.standard_network_system_clock_context.ReadData(
60 shared_memory_holder->GetPointer());
61}
62
63bool SharedMemory::GetStandardUserSystemClockAutomaticCorrectionEnabled() {
64 return shared_memory_format.standard_user_system_clock_automatic_correction.ReadData(
65 shared_memory_holder->GetPointer());
66} 55}
67 56
68} // namespace Service::Time 57} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_sharedmemory.h b/src/core/hle/service/time/time_sharedmemory.h
index 904a96430..5976b2046 100644
--- a/src/core/hle/service/time/time_sharedmemory.h
+++ b/src/core/hle/service/time/time_sharedmemory.h
@@ -5,11 +5,14 @@
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "common/uuid.h"
8#include "core/hle/kernel/shared_memory.h" 9#include "core/hle/kernel/shared_memory.h"
9#include "core/hle/service/time/time.h" 10#include "core/hle/kernel/thread.h"
11#include "core/hle/service/time/clock_types.h"
10 12
11namespace Service::Time { 13namespace Service::Time {
12class SharedMemory { 14
15class SharedMemory final {
13public: 16public:
14 explicit SharedMemory(Core::System& system); 17 explicit SharedMemory(Core::System& system);
15 ~SharedMemory(); 18 ~SharedMemory();
@@ -17,22 +20,10 @@ public:
17 // Return the shared memory handle 20 // Return the shared memory handle
18 std::shared_ptr<Kernel::SharedMemory> GetSharedMemoryHolder() const; 21 std::shared_ptr<Kernel::SharedMemory> GetSharedMemoryHolder() const;
19 22
20 // Set memory barriers in shared memory and update them
21 void SetStandardSteadyClockTimepoint(const SteadyClockTimePoint& timepoint);
22 void SetStandardLocalSystemClockContext(const SystemClockContext& context);
23 void SetStandardNetworkSystemClockContext(const SystemClockContext& context);
24 void SetStandardUserSystemClockAutomaticCorrectionEnabled(bool enabled);
25
26 // Pull from memory barriers in the shared memory
27 SteadyClockTimePoint GetStandardSteadyClockTimepoint();
28 SystemClockContext GetStandardLocalSystemClockContext();
29 SystemClockContext GetStandardNetworkSystemClockContext();
30 bool GetStandardUserSystemClockAutomaticCorrectionEnabled();
31
32 // TODO(ogniK): We have to properly simulate memory barriers, how are we going to do this? 23 // TODO(ogniK): We have to properly simulate memory barriers, how are we going to do this?
33 template <typename T, std::size_t Offset> 24 template <typename T, std::size_t Offset>
34 struct MemoryBarrier { 25 struct MemoryBarrier {
35 static_assert(std::is_trivially_constructible_v<T>, "T must be trivially constructable"); 26 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
36 u32_le read_attempt{}; 27 u32_le read_attempt{};
37 std::array<T, 2> data{}; 28 std::array<T, 2> data{};
38 29
@@ -57,16 +48,22 @@ public:
57 48
58 // Shared memory format 49 // Shared memory format
59 struct Format { 50 struct Format {
60 MemoryBarrier<SteadyClockTimePoint, 0x0> standard_steady_clock_timepoint; 51 MemoryBarrier<Clock::SteadyClockContext, 0x0> standard_steady_clock_timepoint;
61 MemoryBarrier<SystemClockContext, 0x38> standard_local_system_clock_context; 52 MemoryBarrier<Clock::SystemClockContext, 0x38> standard_local_system_clock_context;
62 MemoryBarrier<SystemClockContext, 0x80> standard_network_system_clock_context; 53 MemoryBarrier<Clock::SystemClockContext, 0x80> standard_network_system_clock_context;
63 MemoryBarrier<bool, 0xc8> standard_user_system_clock_automatic_correction; 54 MemoryBarrier<bool, 0xc8> standard_user_system_clock_automatic_correction;
64 u32_le format_version; 55 u32_le format_version;
65 }; 56 };
66 static_assert(sizeof(Format) == 0xd8, "Format is an invalid size"); 57 static_assert(sizeof(Format) == 0xd8, "Format is an invalid size");
67 58
59 void SetupStandardSteadyClock(Core::System& system, const Common::UUID& clock_source_id,
60 Clock::TimeSpanType currentTimePoint);
61 void UpdateLocalSystemClockContext(const Clock::SystemClockContext& context);
62 void UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context);
63 void SetAutomaticCorrectionEnabled(bool is_enabled);
64
68private: 65private:
69 std::shared_ptr<Kernel::SharedMemory> shared_memory_holder{}; 66 std::shared_ptr<Kernel::SharedMemory> shared_memory_holder;
70 Core::System& system; 67 Core::System& system;
71 Format shared_memory_format{}; 68 Format shared_memory_format{};
72}; 69};
diff --git a/src/core/hle/service/time/time_zone_content_manager.cpp b/src/core/hle/service/time/time_zone_content_manager.cpp
new file mode 100644
index 000000000..57b1a2bca
--- /dev/null
+++ b/src/core/hle/service/time/time_zone_content_manager.cpp
@@ -0,0 +1,125 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <sstream>
6
7#include "common/logging/log.h"
8#include "core/core.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/file_sys/romfs.h"
13#include "core/file_sys/system_archive/system_archive.h"
14#include "core/hle/service/filesystem/filesystem.h"
15#include "core/hle/service/time/time_manager.h"
16#include "core/hle/service/time/time_zone_content_manager.h"
17
18namespace Service::Time::TimeZone {
19
20constexpr u64 time_zone_binary_titleid{0x010000000000080E};
21
22static FileSys::VirtualDir GetTimeZoneBinary(Core::System& system) {
23 const auto* nand{system.GetFileSystemController().GetSystemNANDContents()};
24 const auto nca{nand->GetEntry(time_zone_binary_titleid, FileSys::ContentRecordType::Data)};
25
26 FileSys::VirtualFile romfs;
27 if (nca) {
28 romfs = nca->GetRomFS();
29 }
30
31 if (!romfs) {
32 romfs = FileSys::SystemArchive::SynthesizeSystemArchive(time_zone_binary_titleid);
33 }
34
35 if (!romfs) {
36 LOG_ERROR(Service_Time, "Failed to find or synthesize {:016X!}", time_zone_binary_titleid);
37 return {};
38 }
39
40 return FileSys::ExtractRomFS(romfs);
41}
42
43static std::vector<std::string> BuildLocationNameCache(Core::System& system) {
44 const FileSys::VirtualDir extracted_romfs{GetTimeZoneBinary(system)};
45 if (!extracted_romfs) {
46 LOG_ERROR(Service_Time, "Failed to extract RomFS for {:016X}!", time_zone_binary_titleid);
47 return {};
48 }
49
50 const FileSys::VirtualFile binary_list{extracted_romfs->GetFile("binaryList.txt")};
51 if (!binary_list) {
52 LOG_ERROR(Service_Time, "{:016X} has no file binaryList.txt!", time_zone_binary_titleid);
53 return {};
54 }
55
56 std::vector<char> raw_data(binary_list->GetSize());
57 binary_list->ReadBytes<char>(raw_data.data(), binary_list->GetSize());
58
59 std::stringstream data_stream{raw_data.data()};
60 std::string name;
61 std::vector<std::string> location_name_cache;
62 while (std::getline(data_stream, name)) {
63 name.pop_back(); // Remove carriage return
64 location_name_cache.emplace_back(std::move(name));
65 }
66 return location_name_cache;
67}
68
69TimeZoneContentManager::TimeZoneContentManager(TimeManager& time_manager, Core::System& system)
70 : system{system}, location_name_cache{BuildLocationNameCache(system)} {
71 if (FileSys::VirtualFile vfs_file; GetTimeZoneInfoFile("GMT", vfs_file) == RESULT_SUCCESS) {
72 const auto time_point{
73 time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system)};
74 time_manager.SetupTimeZoneManager("GMT", time_point, location_name_cache.size(), {},
75 vfs_file);
76 } else {
77 time_zone_manager.MarkAsInitialized();
78 }
79}
80
81ResultCode TimeZoneContentManager::LoadTimeZoneRule(TimeZoneRule& rules,
82 const std::string& location_name) const {
83 FileSys::VirtualFile vfs_file;
84 if (const ResultCode result{GetTimeZoneInfoFile(location_name, vfs_file)};
85 result != RESULT_SUCCESS) {
86 return result;
87 }
88
89 return time_zone_manager.ParseTimeZoneRuleBinary(rules, vfs_file);
90}
91
92bool TimeZoneContentManager::IsLocationNameValid(const std::string& location_name) const {
93 return std::find(location_name_cache.begin(), location_name_cache.end(), location_name) !=
94 location_name_cache.end();
95}
96
97ResultCode TimeZoneContentManager::GetTimeZoneInfoFile(const std::string& location_name,
98 FileSys::VirtualFile& vfs_file) const {
99 if (!IsLocationNameValid(location_name)) {
100 return ERROR_TIME_NOT_FOUND;
101 }
102
103 const FileSys::VirtualDir extracted_romfs{GetTimeZoneBinary(system)};
104 if (!extracted_romfs) {
105 LOG_ERROR(Service_Time, "Failed to extract RomFS for {:016X}!", time_zone_binary_titleid);
106 return ERROR_TIME_NOT_FOUND;
107 }
108
109 const FileSys::VirtualDir zoneinfo_dir{extracted_romfs->GetSubdirectory("zoneinfo")};
110 if (!zoneinfo_dir) {
111 LOG_ERROR(Service_Time, "{:016X} has no directory zoneinfo!", time_zone_binary_titleid);
112 return ERROR_TIME_NOT_FOUND;
113 }
114
115 vfs_file = zoneinfo_dir->GetFile(location_name);
116 if (!vfs_file) {
117 LOG_ERROR(Service_Time, "{:016X} has no file \"{}\"!", time_zone_binary_titleid,
118 location_name);
119 return ERROR_TIME_NOT_FOUND;
120 }
121
122 return RESULT_SUCCESS;
123}
124
125} // 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
new file mode 100644
index 000000000..4f302c3b9
--- /dev/null
+++ b/src/core/hle/service/time/time_zone_content_manager.h
@@ -0,0 +1,46 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <string>
8#include <vector>
9
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 TimeZoneContentManager(TimeManager& time_manager, Core::System& system);
25
26 TimeZoneManager& GetTimeZoneManager() {
27 return time_zone_manager;
28 }
29
30 const TimeZoneManager& GetTimeZoneManager() const {
31 return time_zone_manager;
32 }
33
34 ResultCode LoadTimeZoneRule(TimeZoneRule& rules, const std::string& location_name) const;
35
36private:
37 bool IsLocationNameValid(const std::string& location_name) const;
38 ResultCode GetTimeZoneInfoFile(const std::string& location_name,
39 FileSys::VirtualFile& vfs_file) const;
40
41 Core::System& system;
42 TimeZoneManager time_zone_manager;
43 const std::vector<std::string> location_name_cache;
44};
45
46} // 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
new file mode 100644
index 000000000..c06ca538a
--- /dev/null
+++ b/src/core/hle/service/time/time_zone_manager.cpp
@@ -0,0 +1,1038 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <climits>
6
7#include <boost/safe_numerics/safe_integer.hpp>
8
9#include "common/assert.h"
10#include "common/logging/log.h"
11#include "core/file_sys/content_archive.h"
12#include "core/file_sys/nca_metadata.h"
13#include "core/file_sys/registered_cache.h"
14#include "core/file_sys/romfs.h"
15#include "core/file_sys/system_archive/system_archive.h"
16#include "core/hle/service/time/time_zone_manager.h"
17
18namespace Service::Time::TimeZone {
19
20static constexpr s32 epoch_year{1970};
21static constexpr s32 year_base{1900};
22static constexpr s32 epoch_week_day{4};
23static constexpr s32 seconds_per_minute{60};
24static constexpr s32 minutes_per_hour{60};
25static constexpr s32 hours_per_day{24};
26static constexpr s32 days_per_week{7};
27static constexpr s32 days_per_normal_year{365};
28static constexpr s32 days_per_leap_year{366};
29static constexpr s32 months_per_year{12};
30static constexpr s32 seconds_per_hour{seconds_per_minute * minutes_per_hour};
31static constexpr s32 seconds_per_day{seconds_per_hour * hours_per_day};
32static constexpr s32 years_per_repeat{400};
33static constexpr s64 average_seconds_per_year{31556952};
34static constexpr s64 seconds_per_repeat{years_per_repeat * average_seconds_per_year};
35
36struct Rule {
37 enum class Type : u32 { JulianDay, DayOfYear, MonthNthDayOfWeek };
38 Type rule_type{};
39 s32 day{};
40 s32 week{};
41 s32 month{};
42 s32 transition_time{};
43};
44
45struct CalendarTimeInternal {
46 s64 year{};
47 s8 month{};
48 s8 day{};
49 s8 hour{};
50 s8 minute{};
51 s8 second{};
52 int Compare(const CalendarTimeInternal& other) const {
53 if (year != other.year) {
54 if (year < other.year) {
55 return -1;
56 }
57 return 1;
58 }
59 if (month != other.month) {
60 return month - other.month;
61 }
62 if (day != other.day) {
63 return day - other.day;
64 }
65 if (hour != other.hour) {
66 return hour - other.hour;
67 }
68 if (minute != other.minute) {
69 return minute - other.minute;
70 }
71 if (second != other.second) {
72 return second - other.second;
73 }
74 return {};
75 }
76};
77
78template <typename TResult, typename TOperand>
79static bool SafeAdd(TResult& result, TOperand op) {
80 const boost::safe_numerics::safe<TResult> safe_result{result};
81 const boost::safe_numerics::safe<TOperand> safe_op{op};
82 try {
83 result = safe_result + safe_op;
84 } catch (const std::exception&) {
85 return {}; // Failed with undefined behavior
86 }
87 return true;
88}
89
90template <typename TResult, typename TUnit, typename TBase>
91static bool SafeNormalize(TResult& result, TUnit& unit, TBase base) {
92 TUnit delta{};
93 if (unit >= 0) {
94 delta = unit / base;
95 } else {
96 delta = -1 - (-1 - unit) / base;
97 }
98 unit -= delta * base;
99 return SafeAdd(result, delta);
100}
101
102template <typename T>
103static constexpr bool IsLeapYear(T year) {
104 return ((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0);
105}
106
107template <typename T>
108static constexpr T GetYearLengthInDays(T year) {
109 return IsLeapYear(year) ? days_per_leap_year : days_per_normal_year;
110}
111
112static constexpr s64 GetLeapDaysFromYearPositive(s64 year) {
113 return year / 4 - year / 100 + year / years_per_repeat;
114}
115
116static constexpr s64 GetLeapDaysFromYear(s64 year) {
117 if (year < 0) {
118 return -1 - GetLeapDaysFromYearPositive(-1 - year);
119 } else {
120 return GetLeapDaysFromYearPositive(year);
121 }
122}
123
124static constexpr int GetMonthLength(bool is_leap_year, int month) {
125 constexpr std::array<int, 12> month_lengths{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
126 constexpr std::array<int, 12> month_lengths_leap{31, 29, 31, 30, 31, 30,
127 31, 31, 30, 31, 30, 31};
128 return is_leap_year ? month_lengths_leap[month] : month_lengths[month];
129}
130
131static constexpr bool IsDigit(char value) {
132 return value >= '0' && value <= '9';
133}
134
135static constexpr int GetQZName(const char* name, int offset, char delimiter) {
136 while (name[offset] != '\0' && name[offset] != delimiter) {
137 offset++;
138 }
139 return offset;
140}
141
142static constexpr int GetTZName(const char* name, int offset) {
143 for (char value{name[offset]};
144 value != '\0' && !IsDigit(value) && value != ',' && value != '-' && value != '+';
145 offset++) {
146 value = name[offset];
147 }
148 return offset;
149}
150
151static constexpr bool GetInteger(const char* name, int& offset, int& value, int min, int max) {
152 value = 0;
153 char temp{name[offset]};
154 if (!IsDigit(temp)) {
155 return {};
156 }
157 do {
158 value = value * 10 + (temp - '0');
159 if (value > max) {
160 return {};
161 }
162 temp = name[offset];
163 } while (IsDigit(temp));
164
165 return value >= min;
166}
167
168static constexpr bool GetSeconds(const char* name, int& offset, int& seconds) {
169 seconds = 0;
170 int value{};
171 if (!GetInteger(name, offset, value, 0, hours_per_day * days_per_week - 1)) {
172 return {};
173 }
174 seconds = value * seconds_per_hour;
175
176 if (name[offset] == ':') {
177 offset++;
178 if (!GetInteger(name, offset, value, 0, minutes_per_hour - 1)) {
179 return {};
180 }
181 seconds += value * seconds_per_minute;
182 if (name[offset] == ':') {
183 offset++;
184 if (!GetInteger(name, offset, value, 0, seconds_per_minute)) {
185 return {};
186 }
187 seconds += value;
188 }
189 }
190 return true;
191}
192
193static constexpr bool GetOffset(const char* name, int& offset, int& value) {
194 bool is_negative{};
195 if (name[offset] == '-') {
196 is_negative = true;
197 offset++;
198 } else if (name[offset] == '+') {
199 offset++;
200 }
201 if (!GetSeconds(name, offset, value)) {
202 return {};
203 }
204 if (is_negative) {
205 value = -value;
206 }
207 return true;
208}
209
210static constexpr bool GetRule(const char* name, int& position, Rule& rule) {
211 bool is_valid{};
212 if (name[position] == 'J') {
213 position++;
214 rule.rule_type = Rule::Type::JulianDay;
215 is_valid = GetInteger(name, position, rule.day, 1, days_per_normal_year);
216 } else if (name[position] == 'M') {
217 position++;
218 rule.rule_type = Rule::Type::MonthNthDayOfWeek;
219 is_valid = GetInteger(name, position, rule.month, 1, months_per_year);
220 if (!is_valid) {
221 return {};
222 }
223 if (name[position++] != '.') {
224 return {};
225 }
226 is_valid = GetInteger(name, position, rule.week, 1, 5);
227 if (!is_valid) {
228 return {};
229 }
230 if (name[position++] != '.') {
231 return {};
232 }
233 is_valid = GetInteger(name, position, rule.day, 0, days_per_week - 1);
234 } else if (isdigit(name[position])) {
235 rule.rule_type = Rule::Type::DayOfYear;
236 is_valid = GetInteger(name, position, rule.day, 0, days_per_leap_year - 1);
237 } else {
238 return {};
239 }
240 if (!is_valid) {
241 return {};
242 }
243 if (name[position] == '/') {
244 position++;
245 return GetOffset(name, position, rule.transition_time);
246 } else {
247 rule.transition_time = 2 * seconds_per_hour;
248 }
249 return true;
250}
251
252static constexpr int TransitionTime(int year, Rule rule, int offset) {
253 int value{};
254 switch (rule.rule_type) {
255 case Rule::Type::JulianDay:
256 value = (rule.day - 1) * seconds_per_day;
257 if (IsLeapYear(year) && rule.day >= 60) {
258 value += seconds_per_day;
259 }
260 break;
261 case Rule::Type::DayOfYear:
262 value = rule.day * seconds_per_day;
263 break;
264 case Rule::Type::MonthNthDayOfWeek: {
265 // Use Zeller's Congruence (https://en.wikipedia.org/wiki/Zeller%27s_congruence) to
266 // calculate the day of the week for any Julian or Gregorian calendar date.
267 const int m1{(rule.month + 9) % 12 + 1};
268 const int yy0{(rule.month <= 2) ? (year - 1) : year};
269 const int yy1{yy0 / 100};
270 const int yy2{yy0 % 100};
271 int day_of_week{((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7};
272
273 if (day_of_week < 0) {
274 day_of_week += days_per_week;
275 }
276 int day{rule.day - day_of_week};
277 if (day < 0) {
278 day += days_per_week;
279 }
280 for (int i{1}; i < rule.week; i++) {
281 if (day + days_per_week >= GetMonthLength(IsLeapYear(year), rule.month - 1)) {
282 break;
283 }
284 day += days_per_week;
285 }
286
287 value = day * seconds_per_day;
288 for (int index{}; index < rule.month - 1; ++index) {
289 value += GetMonthLength(IsLeapYear(year), index) * seconds_per_day;
290 }
291 break;
292 }
293 default:
294 UNREACHABLE();
295 }
296 return value + rule.transition_time + offset;
297}
298
299static bool ParsePosixName(const char* name, TimeZoneRule& rule) {
300 constexpr char default_rule[]{",M4.1.0,M10.5.0"};
301 const char* std_name{name};
302 int std_len{};
303 int offset{};
304 int std_offset{};
305
306 if (name[offset] == '<') {
307 offset++;
308 std_name = name + offset;
309 const int std_name_offset{offset};
310 offset = GetQZName(name, offset, '>');
311 if (name[offset] != '>') {
312 return {};
313 }
314 std_len = offset - std_name_offset;
315 offset++;
316 } else {
317 offset = GetTZName(name, offset);
318 std_len = offset;
319 }
320 if (!std_len) {
321 return {};
322 }
323 if (!GetOffset(name, offset, std_offset)) {
324 return {};
325 }
326
327 int char_count{std_len + 1};
328 int dest_len{};
329 int dest_offset{};
330 const char* dest_name{name + offset};
331 if (rule.chars.size() < char_count) {
332 return {};
333 }
334
335 if (name[offset] != '\0') {
336 if (name[offset] == '<') {
337 dest_name = name + (++offset);
338 const int dest_name_offset{offset};
339 offset = GetQZName(name, offset, '>');
340 if (name[offset] != '>') {
341 return {};
342 }
343 dest_len = offset - dest_name_offset;
344 offset++;
345 } else {
346 dest_name = name + (offset);
347 offset = GetTZName(name, offset);
348 dest_len = offset;
349 }
350 if (dest_len == 0) {
351 return {};
352 }
353 char_count += dest_len + 1;
354 if (rule.chars.size() < char_count) {
355 return {};
356 }
357 if (name[offset] != '\0' && name[offset] != ',' && name[offset] != ';') {
358 if (!GetOffset(name, offset, dest_offset)) {
359 return {};
360 }
361 } else {
362 dest_offset = std_offset - seconds_per_hour;
363 }
364 if (name[offset] == '\0') {
365 name = default_rule;
366 offset = 0;
367 }
368 if (name[offset] == ',' || name[offset] == ';') {
369 offset++;
370
371 Rule start{};
372 if (!GetRule(name, offset, start)) {
373 return {};
374 }
375 if (name[offset++] != ',') {
376 return {};
377 }
378
379 Rule end{};
380 if (!GetRule(name, offset, end)) {
381 return {};
382 }
383 if (name[offset] != '\0') {
384 return {};
385 }
386
387 rule.type_count = 2;
388 rule.ttis[0].gmt_offset = -dest_offset;
389 rule.ttis[0].is_dst = true;
390 rule.ttis[0].abbreviation_list_index = std_len + 1;
391 rule.ttis[1].gmt_offset = -std_offset;
392 rule.ttis[1].is_dst = false;
393 rule.ttis[1].abbreviation_list_index = 0;
394 rule.default_type = 0;
395
396 s64 jan_first{};
397 int time_count{};
398 int jan_offset{};
399 int year_beginning{epoch_year};
400 do {
401 const int year_seconds{GetYearLengthInDays(year_beginning - 1) * seconds_per_day};
402 year_beginning--;
403 if (!SafeAdd(jan_first, -year_seconds)) {
404 jan_offset = -year_seconds;
405 break;
406 }
407 } while (epoch_year - years_per_repeat / 2 < year_beginning);
408
409 int year_limit{year_beginning + years_per_repeat + 1};
410 int year{};
411 for (year = year_beginning; year < year_limit; year++) {
412 int start_time{TransitionTime(year, start, std_offset)};
413 int end_time{TransitionTime(year, end, dest_offset)};
414 const int year_seconds{GetYearLengthInDays(year) * seconds_per_day};
415 const bool is_reversed{end_time < start_time};
416 if (is_reversed) {
417 int swap{start_time};
418 start_time = end_time;
419 end_time = swap;
420 }
421
422 if (is_reversed ||
423 (start_time < end_time &&
424 (end_time - start_time < (year_seconds + (std_offset - dest_offset))))) {
425 if (rule.ats.size() - 2 < time_count) {
426 break;
427 }
428
429 rule.ats[time_count] = jan_first;
430 if (SafeAdd(rule.ats[time_count], jan_offset + start_time)) {
431 rule.types[time_count++] = is_reversed ? 1 : 0;
432 } else if (jan_offset != 0) {
433 rule.default_type = is_reversed ? 1 : 0;
434 }
435
436 rule.ats[time_count] = jan_first;
437 if (SafeAdd(rule.ats[time_count], jan_offset + end_time)) {
438 rule.types[time_count++] = is_reversed ? 0 : 1;
439 year_limit = year + years_per_repeat + 1;
440 } else if (jan_offset != 0) {
441 rule.default_type = is_reversed ? 0 : 1;
442 }
443 }
444 if (!SafeAdd(jan_first, jan_offset + year_seconds)) {
445 break;
446 }
447 jan_offset = 0;
448 }
449 rule.time_count = time_count;
450 if (time_count == 0) {
451 rule.type_count = 1;
452 } else if (years_per_repeat < year - year_beginning) {
453 rule.go_back = true;
454 rule.go_ahead = true;
455 }
456 } else {
457 if (name[offset] == '\0') {
458 return {};
459 }
460
461 s64 their_std_offset{};
462 for (int index{}; index < rule.time_count; ++index) {
463 const s8 type{rule.types[index]};
464 if (rule.ttis[type].is_standard_time_daylight) {
465 their_std_offset = -rule.ttis[type].gmt_offset;
466 }
467 }
468
469 s64 their_offset{their_std_offset};
470 for (int index{}; index < rule.time_count; ++index) {
471 const s8 type{rule.types[index]};
472 rule.types[index] = rule.ttis[type].is_dst ? 1 : 0;
473 if (!rule.ttis[type].is_gmt) {
474 if (!rule.ttis[type].is_standard_time_daylight) {
475 rule.ats[index] += dest_offset - their_std_offset;
476 } else {
477 rule.ats[index] += std_offset - their_std_offset;
478 }
479 }
480 their_offset = -rule.ttis[type].gmt_offset;
481 if (!rule.ttis[type].is_dst) {
482 their_std_offset = their_offset;
483 }
484 }
485 rule.ttis[0].gmt_offset = -std_offset;
486 rule.ttis[0].is_dst = false;
487 rule.ttis[0].abbreviation_list_index = 0;
488 rule.ttis[1].gmt_offset = -dest_offset;
489 rule.ttis[1].is_dst = true;
490 rule.ttis[1].abbreviation_list_index = std_len + 1;
491 rule.type_count = 2;
492 rule.default_type = 0;
493 }
494 } else {
495 // Default is standard time
496 rule.type_count = 1;
497 rule.time_count = 0;
498 rule.default_type = 0;
499 rule.ttis[0].gmt_offset = -std_offset;
500 rule.ttis[0].is_dst = false;
501 rule.ttis[0].abbreviation_list_index = 0;
502 }
503
504 rule.char_count = char_count;
505 for (int index{}; index < std_len; ++index) {
506 rule.chars[index] = std_name[index];
507 }
508
509 rule.chars[std_len++] = '\0';
510 if (dest_len != 0) {
511 for (int index{}; index < dest_len; ++index) {
512 rule.chars[std_len + index] = dest_name[index];
513 }
514 rule.chars[std_len + dest_len] = '\0';
515 }
516
517 return true;
518}
519
520static bool ParseTimeZoneBinary(TimeZoneRule& time_zone_rule, FileSys::VirtualFile& vfs_file) {
521 TzifHeader header{};
522 if (vfs_file->ReadObject<TzifHeader>(&header) != sizeof(TzifHeader)) {
523 return {};
524 }
525
526 constexpr s32 time_zone_max_leaps{50};
527 constexpr s32 time_zone_max_chars{50};
528 if (!(0 <= header.leap_count && header.leap_count < time_zone_max_leaps &&
529 0 < header.type_count && header.type_count < time_zone_rule.ttis.size() &&
530 0 <= header.time_count && header.time_count < 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.time_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 std::size_t bytes_read{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 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, bytes_read - 1);
637
638 TimeZoneRule temp_rule;
639 if (ParsePosixName(name.data(), temp_rule)) {
640 UNIMPLEMENTED();
641 }
642 }
643 if (time_zone_rule.type_count == 0) {
644 return {};
645 }
646 if (time_zone_rule.time_count > 1) {
647 UNIMPLEMENTED();
648 }
649
650 s32 default_type{};
651
652 for (default_type = 0; default_type < time_zone_rule.time_count; default_type++) {
653 if (time_zone_rule.types[default_type] == 0) {
654 break;
655 }
656 }
657
658 default_type = default_type < time_zone_rule.time_count ? -1 : 0;
659 if (default_type < 0 && time_zone_rule.time_count > 0 &&
660 time_zone_rule.ttis[time_zone_rule.types[0]].is_dst) {
661 default_type = time_zone_rule.types[0];
662 while (--default_type >= 0) {
663 if (!time_zone_rule.ttis[default_type].is_dst) {
664 break;
665 }
666 }
667 }
668 if (default_type < 0) {
669 default_type = 0;
670 while (time_zone_rule.ttis[default_type].is_dst) {
671 if (++default_type >= time_zone_rule.type_count) {
672 default_type = 0;
673 break;
674 }
675 }
676 }
677 time_zone_rule.default_type = default_type;
678 return true;
679}
680
681static ResultCode CreateCalendarTime(s64 time, int gmt_offset, CalendarTimeInternal& calendar_time,
682 CalendarAdditionalInfo& calendar_additional_info) {
683 s64 year{epoch_year};
684 s64 time_days{time / seconds_per_day};
685 s64 remaining_seconds{time % seconds_per_day};
686 while (time_days < 0 || time_days >= GetYearLengthInDays(year)) {
687 s64 delta = time_days / days_per_leap_year;
688 if (!delta) {
689 delta = time_days < 0 ? -1 : 1;
690 }
691 s64 new_year{year};
692 if (!SafeAdd(new_year, delta)) {
693 return ERROR_OUT_OF_RANGE;
694 }
695 time_days -= (new_year - year) * days_per_normal_year;
696 time_days -= GetLeapDaysFromYear(new_year - 1) - GetLeapDaysFromYear(year - 1);
697 year = new_year;
698 }
699
700 s64 day_of_year{time_days};
701 remaining_seconds += gmt_offset;
702 while (remaining_seconds < 0) {
703 remaining_seconds += seconds_per_day;
704 day_of_year--;
705 }
706
707 while (remaining_seconds >= seconds_per_day) {
708 remaining_seconds -= seconds_per_day;
709 day_of_year++;
710 }
711
712 while (day_of_year < 0) {
713 if (!SafeAdd(year, -1)) {
714 return ERROR_OUT_OF_RANGE;
715 }
716 day_of_year += GetYearLengthInDays(year);
717 }
718
719 while (day_of_year >= GetYearLengthInDays(year)) {
720 day_of_year -= GetYearLengthInDays(year);
721 if (!SafeAdd(year, 1)) {
722 return ERROR_OUT_OF_RANGE;
723 }
724 }
725
726 calendar_time.year = year;
727 calendar_additional_info.day_of_year = static_cast<u32>(day_of_year);
728 s64 day_of_week{
729 (epoch_week_day +
730 ((year - epoch_year) % days_per_week) * (days_per_normal_year % days_per_week) +
731 GetLeapDaysFromYear(year - 1) - GetLeapDaysFromYear(epoch_year - 1) + day_of_year) %
732 days_per_week};
733 if (day_of_week < 0) {
734 day_of_week += days_per_week;
735 }
736
737 calendar_additional_info.day_of_week = static_cast<u32>(day_of_week);
738 calendar_time.hour = static_cast<s8>((remaining_seconds / seconds_per_hour) % seconds_per_hour);
739 remaining_seconds %= seconds_per_hour;
740 calendar_time.minute = static_cast<s8>(remaining_seconds / seconds_per_minute);
741 calendar_time.second = static_cast<s8>(remaining_seconds % seconds_per_minute);
742
743 for (calendar_time.month = 0;
744 day_of_year >= GetMonthLength(IsLeapYear(year), calendar_time.month);
745 ++calendar_time.month) {
746 day_of_year -= GetMonthLength(IsLeapYear(year), calendar_time.month);
747 }
748
749 calendar_time.day = static_cast<s8>(day_of_year + 1);
750 calendar_additional_info.is_dst = false;
751 calendar_additional_info.gmt_offset = gmt_offset;
752
753 return RESULT_SUCCESS;
754}
755
756static ResultCode ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time,
757 CalendarTimeInternal& calendar_time,
758 CalendarAdditionalInfo& calendar_additional_info) {
759 if ((rules.go_ahead && time < rules.ats[0]) ||
760 (rules.go_back && time > rules.ats[rules.time_count - 1])) {
761 s64 seconds{};
762 if (time < rules.ats[0]) {
763 seconds = rules.ats[0] - time;
764 } else {
765 seconds = time - rules.ats[rules.time_count - 1];
766 }
767 seconds--;
768
769 const s64 years{(seconds / seconds_per_repeat + 1) * years_per_repeat};
770 seconds = years * average_seconds_per_year;
771
772 s64 new_time{time};
773 if (time < rules.ats[0]) {
774 new_time += seconds;
775 } else {
776 new_time -= seconds;
777 }
778 if (new_time < rules.ats[0] && new_time > rules.ats[rules.time_count - 1]) {
779 return ERROR_TIME_NOT_FOUND;
780 }
781 if (const ResultCode result{
782 ToCalendarTimeInternal(rules, new_time, calendar_time, calendar_additional_info)};
783 result != RESULT_SUCCESS) {
784 return result;
785 }
786 if (time < rules.ats[0]) {
787 calendar_time.year -= years;
788 } else {
789 calendar_time.year += years;
790 }
791
792 return RESULT_SUCCESS;
793 }
794
795 s32 tti_index{};
796 if (rules.time_count == 0 || time < rules.ats[0]) {
797 tti_index = rules.default_type;
798 } else {
799 s32 low{1};
800 s32 high{rules.time_count};
801 while (low < high) {
802 s32 mid{(low + high) >> 1};
803 if (time < rules.ats[mid]) {
804 high = mid;
805 } else {
806 low = mid + 1;
807 }
808 }
809 tti_index = rules.types[low - 1];
810 }
811
812 if (const ResultCode result{CreateCalendarTime(time, rules.ttis[tti_index].gmt_offset,
813 calendar_time, calendar_additional_info)};
814 result != RESULT_SUCCESS) {
815 return result;
816 }
817
818 calendar_additional_info.is_dst = rules.ttis[tti_index].is_dst;
819 const char* time_zone{&rules.chars[rules.ttis[tti_index].abbreviation_list_index]};
820 for (int index{}; time_zone[index] != '\0'; ++index) {
821 calendar_additional_info.timezone_name[index] = time_zone[index];
822 }
823 return RESULT_SUCCESS;
824}
825
826static ResultCode ToCalendarTimeImpl(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) {
827 CalendarTimeInternal calendar_time{};
828 const ResultCode result{
829 ToCalendarTimeInternal(rules, time, calendar_time, calendar.additiona_info)};
830 calendar.time.year = static_cast<s16>(calendar_time.year);
831 calendar.time.month = calendar_time.month;
832 calendar.time.day = calendar_time.day;
833 calendar.time.hour = calendar_time.hour;
834 calendar.time.minute = calendar_time.minute;
835 calendar.time.second = calendar_time.second;
836 return result;
837}
838
839TimeZoneManager::TimeZoneManager() = default;
840TimeZoneManager::~TimeZoneManager() = default;
841
842ResultCode TimeZoneManager::ToCalendarTime(const TimeZoneRule& rules, s64 time,
843 CalendarInfo& calendar) const {
844 return ToCalendarTimeImpl(rules, time, calendar);
845}
846
847ResultCode TimeZoneManager::SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name,
848 FileSys::VirtualFile& vfs_file) {
849 TimeZoneRule rule{};
850 if (ParseTimeZoneBinary(rule, vfs_file)) {
851 device_location_name = location_name;
852 time_zone_rule = rule;
853 return RESULT_SUCCESS;
854 }
855 return ERROR_TIME_ZONE_CONVERSION_FAILED;
856}
857
858ResultCode TimeZoneManager::SetUpdatedTime(const Clock::SteadyClockTimePoint& value) {
859 time_zone_update_time_point = value;
860 return RESULT_SUCCESS;
861}
862
863ResultCode TimeZoneManager::ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const {
864 if (is_initialized) {
865 return ToCalendarTime(time_zone_rule, time, calendar);
866 } else {
867 return ERROR_UNINITIALIZED_CLOCK;
868 }
869}
870
871ResultCode TimeZoneManager::ParseTimeZoneRuleBinary(TimeZoneRule& rules,
872 FileSys::VirtualFile& vfs_file) const {
873 if (!ParseTimeZoneBinary(rules, vfs_file)) {
874 return ERROR_TIME_ZONE_CONVERSION_FAILED;
875 }
876 return RESULT_SUCCESS;
877}
878
879ResultCode TimeZoneManager::ToPosixTime(const TimeZoneRule& rules,
880 const CalendarTime& calendar_time, s64& posix_time) const {
881 posix_time = 0;
882
883 CalendarTimeInternal internal_time{};
884 internal_time.year = calendar_time.year;
885 internal_time.month = calendar_time.month;
886 internal_time.day = calendar_time.day;
887 internal_time.hour = calendar_time.hour;
888 internal_time.minute = calendar_time.minute;
889 internal_time.second = calendar_time.second;
890
891 s32 hour{internal_time.hour};
892 s32 minute{internal_time.minute};
893 if (!SafeNormalize(hour, minute, minutes_per_hour)) {
894 return ERROR_OVERFLOW;
895 }
896 internal_time.minute = static_cast<s8>(minute);
897
898 s32 day{internal_time.day};
899 if (!SafeNormalize(day, hour, hours_per_day)) {
900 return ERROR_OVERFLOW;
901 }
902 internal_time.day = static_cast<s8>(day);
903 internal_time.hour = static_cast<s8>(hour);
904
905 s64 year{internal_time.year};
906 s64 month{internal_time.month};
907 if (!SafeNormalize(year, month, months_per_year)) {
908 return ERROR_OVERFLOW;
909 }
910 internal_time.month = static_cast<s8>(month);
911
912 if (!SafeAdd(year, year_base)) {
913 return ERROR_OVERFLOW;
914 }
915
916 while (day <= 0) {
917 if (!SafeAdd(year, -1)) {
918 return ERROR_OVERFLOW;
919 }
920 s64 temp_year{year};
921 if (1 < internal_time.month) {
922 ++temp_year;
923 }
924 day += static_cast<s32>(GetYearLengthInDays(temp_year));
925 }
926
927 while (day > days_per_leap_year) {
928 s64 temp_year{year};
929 if (1 < internal_time.month) {
930 temp_year++;
931 }
932 day -= static_cast<s32>(GetYearLengthInDays(temp_year));
933 if (!SafeAdd(year, 1)) {
934 return ERROR_OVERFLOW;
935 }
936 }
937
938 while (true) {
939 const s32 month_length{GetMonthLength(IsLeapYear(year), internal_time.month)};
940 if (day <= month_length) {
941 break;
942 }
943 day -= month_length;
944 internal_time.month++;
945 if (internal_time.month >= months_per_year) {
946 internal_time.month = 0;
947 if (!SafeAdd(year, 1)) {
948 return ERROR_OVERFLOW;
949 }
950 }
951 }
952 internal_time.day = static_cast<s8>(day);
953
954 if (!SafeAdd(year, -year_base)) {
955 return ERROR_OVERFLOW;
956 }
957 internal_time.year = year;
958
959 s32 saved_seconds{};
960 if (internal_time.second >= 0 && internal_time.second < seconds_per_minute) {
961 saved_seconds = 0;
962 } else if (year + year_base < epoch_year) {
963 s32 second{internal_time.second};
964 if (!SafeAdd(second, 1 - seconds_per_minute)) {
965 return ERROR_OVERFLOW;
966 }
967 saved_seconds = second;
968 internal_time.second = 1 - seconds_per_minute;
969 } else {
970 saved_seconds = internal_time.second;
971 internal_time.second = 0;
972 }
973
974 s64 low{LLONG_MIN};
975 s64 high{LLONG_MAX};
976 while (true) {
977 s64 pivot{low / 2 + high / 2};
978 if (pivot < low) {
979 pivot = low;
980 } else if (pivot > high) {
981 pivot = high;
982 }
983 s32 direction{};
984 CalendarTimeInternal candidate_calendar_time{};
985 CalendarAdditionalInfo unused{};
986 if (ToCalendarTimeInternal(rules, pivot, candidate_calendar_time, unused) !=
987 RESULT_SUCCESS) {
988 if (pivot > 0) {
989 direction = 1;
990 } else {
991 direction = -1;
992 }
993 } else {
994 direction = candidate_calendar_time.Compare(internal_time);
995 }
996 if (!direction) {
997 const s64 time_result{pivot + saved_seconds};
998 if ((time_result < pivot) != (saved_seconds < 0)) {
999 return ERROR_OVERFLOW;
1000 }
1001 posix_time = time_result;
1002 break;
1003 } else {
1004 if (pivot == low) {
1005 if (pivot == LLONG_MAX) {
1006 return ERROR_TIME_NOT_FOUND;
1007 }
1008 pivot++;
1009 low++;
1010 } else if (pivot == high) {
1011 if (pivot == LLONG_MIN) {
1012 return ERROR_TIME_NOT_FOUND;
1013 }
1014 pivot--;
1015 high--;
1016 }
1017 if (low > high) {
1018 return ERROR_TIME_NOT_FOUND;
1019 }
1020 if (direction > 0) {
1021 high = pivot;
1022 } else {
1023 low = pivot;
1024 }
1025 }
1026 }
1027 return RESULT_SUCCESS;
1028}
1029
1030ResultCode TimeZoneManager::GetDeviceLocationName(LocationName& value) const {
1031 if (!is_initialized) {
1032 return ERROR_UNINITIALIZED_CLOCK;
1033 }
1034 std::memcpy(value.data(), device_location_name.c_str(), device_location_name.size());
1035 return RESULT_SUCCESS;
1036}
1037
1038} // 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
new file mode 100644
index 000000000..7c6f975ae
--- /dev/null
+++ b/src/core/hle/service/time/time_zone_manager.h
@@ -0,0 +1,53 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <string>
8
9#include "common/common_types.h"
10#include "core/file_sys/vfs_types.h"
11#include "core/hle/service/time/clock_types.h"
12#include "core/hle/service/time/time_zone_types.h"
13
14namespace Service::Time::TimeZone {
15
16class TimeZoneManager final {
17public:
18 TimeZoneManager();
19 ~TimeZoneManager();
20
21 void SetTotalLocationNameCount(std::size_t value) {
22 total_location_name_count = value;
23 }
24
25 void SetTimeZoneRuleVersion(const u128& value) {
26 time_zone_rule_version = value;
27 }
28
29 void MarkAsInitialized() {
30 is_initialized = true;
31 }
32
33 ResultCode SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name,
34 FileSys::VirtualFile& vfs_file);
35 ResultCode SetUpdatedTime(const Clock::SteadyClockTimePoint& value);
36 ResultCode GetDeviceLocationName(TimeZone::LocationName& value) const;
37 ResultCode ToCalendarTime(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) const;
38 ResultCode ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const;
39 ResultCode ParseTimeZoneRuleBinary(TimeZoneRule& rules, FileSys::VirtualFile& vfs_file) const;
40 ResultCode ToPosixTime(const TimeZoneRule& rules, const CalendarTime& calendar_time,
41 s64& posix_time) const;
42
43private:
44 bool is_initialized{};
45 TimeZoneRule time_zone_rule{};
46 std::string device_location_name{"GMT"};
47 u128 time_zone_rule_version{};
48 std::size_t total_location_name_count{};
49 Clock::SteadyClockTimePoint time_zone_update_time_point{
50 Clock::SteadyClockTimePoint::GetRandom()};
51};
52
53} // 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
new file mode 100644
index 000000000..1566e778e
--- /dev/null
+++ b/src/core/hle/service/time/time_zone_service.cpp
@@ -0,0 +1,148 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/logging/log.h"
6#include "core/hle/ipc_helpers.h"
7#include "core/hle/service/time/time_zone_content_manager.h"
8#include "core/hle/service/time/time_zone_service.h"
9#include "core/hle/service/time/time_zone_types.h"
10
11namespace Service::Time {
12
13ITimeZoneService ::ITimeZoneService(TimeZone::TimeZoneContentManager& time_zone_content_manager)
14 : ServiceFramework("ITimeZoneService"), time_zone_content_manager{time_zone_content_manager} {
15 static const FunctionInfo functions[] = {
16 {0, &ITimeZoneService::GetDeviceLocationName, "GetDeviceLocationName"},
17 {1, nullptr, "SetDeviceLocationName"},
18 {2, nullptr, "GetTotalLocationNameCount"},
19 {3, nullptr, "LoadLocationNameList"},
20 {4, &ITimeZoneService::LoadTimeZoneRule, "LoadTimeZoneRule"},
21 {5, nullptr, "GetTimeZoneRuleVersion"},
22 {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"},
23 {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
24 {201, &ITimeZoneService::ToPosixTime, "ToPosixTime"},
25 {202, nullptr, "ToPosixTimeWithMyRule"},
26 };
27 RegisterHandlers(functions);
28}
29
30void ITimeZoneService::GetDeviceLocationName(Kernel::HLERequestContext& ctx) {
31 LOG_DEBUG(Service_Time, "called");
32
33 TimeZone::LocationName location_name{};
34 if (const ResultCode result{
35 time_zone_content_manager.GetTimeZoneManager().GetDeviceLocationName(location_name)};
36 result != RESULT_SUCCESS) {
37 IPC::ResponseBuilder rb{ctx, 2};
38 rb.Push(result);
39 return;
40 }
41
42 IPC::ResponseBuilder rb{ctx, (sizeof(location_name) / 4) + 2};
43 rb.Push(RESULT_SUCCESS);
44 rb.PushRaw(location_name);
45}
46
47void ITimeZoneService::LoadTimeZoneRule(Kernel::HLERequestContext& ctx) {
48 IPC::RequestParser rp{ctx};
49 const auto raw_location_name{rp.PopRaw<std::array<u8, 0x24>>()};
50
51 std::string location_name;
52 for (const auto& byte : raw_location_name) {
53 // Strip extra bytes
54 if (byte == '\0') {
55 break;
56 }
57 location_name.push_back(byte);
58 }
59
60 LOG_DEBUG(Service_Time, "called, location_name={}", location_name);
61
62 TimeZone::TimeZoneRule time_zone_rule{};
63 if (const ResultCode result{
64 time_zone_content_manager.LoadTimeZoneRule(time_zone_rule, location_name)};
65 result != RESULT_SUCCESS) {
66 IPC::ResponseBuilder rb{ctx, 2};
67 rb.Push(result);
68 return;
69 }
70
71 std::vector<u8> time_zone_rule_outbuffer(sizeof(TimeZone::TimeZoneRule));
72 std::memcpy(time_zone_rule_outbuffer.data(), &time_zone_rule, sizeof(TimeZone::TimeZoneRule));
73 ctx.WriteBuffer(time_zone_rule_outbuffer);
74
75 IPC::ResponseBuilder rb{ctx, 2};
76 rb.Push(RESULT_SUCCESS);
77}
78
79void ITimeZoneService::ToCalendarTime(Kernel::HLERequestContext& ctx) {
80 IPC::RequestParser rp{ctx};
81 const auto posix_time{rp.Pop<s64>()};
82
83 LOG_DEBUG(Service_Time, "called, posix_time=0x{:016X}", posix_time);
84
85 TimeZone::TimeZoneRule time_zone_rule{};
86 const auto buffer{ctx.ReadBuffer()};
87 std::memcpy(&time_zone_rule, buffer.data(), buffer.size());
88
89 TimeZone::CalendarInfo calendar_info{};
90 if (const ResultCode result{time_zone_content_manager.GetTimeZoneManager().ToCalendarTime(
91 time_zone_rule, posix_time, calendar_info)};
92 result != RESULT_SUCCESS) {
93 IPC::ResponseBuilder rb{ctx, 2};
94 rb.Push(result);
95 return;
96 }
97
98 IPC::ResponseBuilder rb{ctx, 2 + (sizeof(TimeZone::CalendarInfo) / 4)};
99 rb.Push(RESULT_SUCCESS);
100 rb.PushRaw(calendar_info);
101}
102
103void ITimeZoneService::ToCalendarTimeWithMyRule(Kernel::HLERequestContext& ctx) {
104 IPC::RequestParser rp{ctx};
105 const auto posix_time{rp.Pop<s64>()};
106
107 LOG_DEBUG(Service_Time, "called, posix_time=0x{:016X}", posix_time);
108
109 TimeZone::CalendarInfo calendar_info{};
110 if (const ResultCode result{
111 time_zone_content_manager.GetTimeZoneManager().ToCalendarTimeWithMyRules(
112 posix_time, calendar_info)};
113 result != RESULT_SUCCESS) {
114 IPC::ResponseBuilder rb{ctx, 2};
115 rb.Push(result);
116 return;
117 }
118
119 IPC::ResponseBuilder rb{ctx, 2 + (sizeof(TimeZone::CalendarInfo) / 4)};
120 rb.Push(RESULT_SUCCESS);
121 rb.PushRaw(calendar_info);
122}
123
124void ITimeZoneService::ToPosixTime(Kernel::HLERequestContext& ctx) {
125 LOG_DEBUG(Service_Time, "called");
126
127 IPC::RequestParser rp{ctx};
128 const auto calendar_time{rp.PopRaw<TimeZone::CalendarTime>()};
129 TimeZone::TimeZoneRule time_zone_rule{};
130 std::memcpy(&time_zone_rule, ctx.ReadBuffer().data(), sizeof(TimeZone::TimeZoneRule));
131
132 s64 posix_time{};
133 if (const ResultCode result{time_zone_content_manager.GetTimeZoneManager().ToPosixTime(
134 time_zone_rule, calendar_time, posix_time)};
135 result != RESULT_SUCCESS) {
136 IPC::ResponseBuilder rb{ctx, 2};
137 rb.Push(result);
138 return;
139 }
140
141 // TODO(bunnei): Handle multiple times
142 IPC::ResponseBuilder rb{ctx, 3};
143 rb.Push(RESULT_SUCCESS);
144 rb.PushRaw<u32>(1); // Number of times we're returning
145 ctx.WriteBuffer(&posix_time, sizeof(s64));
146}
147
148} // 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
new file mode 100644
index 000000000..a92b4312b
--- /dev/null
+++ b/src/core/hle/service/time/time_zone_service.h
@@ -0,0 +1,30 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9namespace Service::Time {
10
11namespace TimeZone {
12class TimeZoneContentManager;
13}
14
15class ITimeZoneService final : public ServiceFramework<ITimeZoneService> {
16public:
17 explicit ITimeZoneService(TimeZone::TimeZoneContentManager& time_zone_manager);
18
19private:
20 void GetDeviceLocationName(Kernel::HLERequestContext& ctx);
21 void LoadTimeZoneRule(Kernel::HLERequestContext& ctx);
22 void ToCalendarTime(Kernel::HLERequestContext& ctx);
23 void ToCalendarTimeWithMyRule(Kernel::HLERequestContext& ctx);
24 void ToPosixTime(Kernel::HLERequestContext& ctx);
25
26private:
27 TimeZone::TimeZoneContentManager& time_zone_content_manager;
28};
29
30} // 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
new file mode 100644
index 000000000..9be15b53e
--- /dev/null
+++ b/src/core/hle/service/time/time_zone_types.h
@@ -0,0 +1,87 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8
9#include "common/common_funcs.h"
10#include "common/common_types.h"
11#include "common/swap.h"
12
13namespace Service::Time::TimeZone {
14
15using LocationName = std::array<char, 0x24>;
16
17/// https://switchbrew.org/wiki/Glue_services#ttinfo
18struct TimeTypeInfo {
19 s32 gmt_offset{};
20 u8 is_dst{};
21 INSERT_PADDING_BYTES(3);
22 s32 abbreviation_list_index{};
23 u8 is_standard_time_daylight{};
24 u8 is_gmt{};
25 INSERT_PADDING_BYTES(2);
26};
27static_assert(sizeof(TimeTypeInfo) == 0x10, "TimeTypeInfo is incorrect size");
28
29/// https://switchbrew.org/wiki/Glue_services#TimeZoneRule
30struct TimeZoneRule {
31 s32 time_count{};
32 s32 type_count{};
33 s32 char_count{};
34 u8 go_back{};
35 u8 go_ahead{};
36 INSERT_PADDING_BYTES(2);
37 std::array<s64, 1000> ats{};
38 std::array<s8, 1000> types{};
39 std::array<TimeTypeInfo, 128> ttis{};
40 std::array<char, 512> chars{};
41 s32 default_type{};
42 INSERT_PADDING_BYTES(0x12C4);
43};
44static_assert(sizeof(TimeZoneRule) == 0x4000, "TimeZoneRule is incorrect size");
45
46/// https://switchbrew.org/wiki/Glue_services#CalendarAdditionalInfo
47struct CalendarAdditionalInfo {
48 u32 day_of_week{};
49 u32 day_of_year{};
50 std::array<char, 8> timezone_name;
51 u32 is_dst{};
52 s32 gmt_offset{};
53};
54static_assert(sizeof(CalendarAdditionalInfo) == 0x18, "CalendarAdditionalInfo is incorrect size");
55
56/// https://switchbrew.org/wiki/Glue_services#CalendarTime
57struct CalendarTime {
58 s16 year{};
59 s8 month{};
60 s8 day{};
61 s8 hour{};
62 s8 minute{};
63 s8 second{};
64 INSERT_PADDING_BYTES(1);
65};
66static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime is incorrect size");
67
68struct CalendarInfo {
69 CalendarTime time{};
70 CalendarAdditionalInfo additiona_info{};
71};
72static_assert(sizeof(CalendarInfo) == 0x20, "CalendarInfo is incorrect size");
73
74struct TzifHeader {
75 u32_be magic{};
76 u8 version{};
77 INSERT_PADDING_BYTES(15);
78 s32_be ttis_gmt_count{};
79 s32_be ttis_std_count{};
80 s32_be leap_count{};
81 s32_be time_count{};
82 s32_be type_count{};
83 s32_be char_count{};
84};
85static_assert(sizeof(TzifHeader) == 0x2C, "TzifHeader is incorrect size");
86
87} // namespace Service::Time::TimeZone