summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--externals/CMakeLists.txt3
-rw-r--r--externals/tz/tz/tz.cpp1636
-rw-r--r--externals/tz/tz/tz.h81
-rw-r--r--src/common/arm64/native_clock.cpp24
-rw-r--r--src/common/arm64/native_clock.h10
-rw-r--r--src/common/memory_detect.h2
-rw-r--r--src/common/settings.h13
-rw-r--r--src/common/time_zone.cpp12
-rw-r--r--src/common/uuid.h12
-rw-r--r--src/common/wall_clock.cpp32
-rw-r--r--src/common/wall_clock.h9
-rw-r--r--src/common/x64/native_clock.cpp26
-rw-r--r--src/common/x64/native_clock.h9
-rw-r--r--src/core/CMakeLists.txt94
-rw-r--r--src/core/core.cpp89
-rw-r--r--src/core/core.h7
-rw-r--r--src/core/core_timing.cpp2
-rw-r--r--src/core/file_sys/system_archive/system_archive.cpp12
-rw-r--r--src/core/file_sys/system_archive/time_zone_binary.cpp1
-rw-r--r--src/core/hle/service/caps/caps_manager.cpp47
-rw-r--r--src/core/hle/service/glue/glue.cpp19
-rw-r--r--src/core/hle/service/glue/time/alarm_worker.cpp82
-rw-r--r--src/core/hle/service/glue/time/alarm_worker.h53
-rw-r--r--src/core/hle/service/glue/time/file_timestamp_worker.cpp23
-rw-r--r--src/core/hle/service/glue/time/file_timestamp_worker.h28
-rw-r--r--src/core/hle/service/glue/time/manager.cpp277
-rw-r--r--src/core/hle/service/glue/time/manager.h42
-rw-r--r--src/core/hle/service/glue/time/pm_state_change_handler.cpp13
-rw-r--r--src/core/hle/service/glue/time/pm_state_change_handler.h18
-rw-r--r--src/core/hle/service/glue/time/standard_steady_clock_resource.cpp123
-rw-r--r--src/core/hle/service/glue/time/standard_steady_clock_resource.h41
-rw-r--r--src/core/hle/service/glue/time/static.cpp448
-rw-r--r--src/core/hle/service/glue/time/static.h110
-rw-r--r--src/core/hle/service/glue/time/time_zone.cpp377
-rw-r--r--src/core/hle/service/glue/time/time_zone.h95
-rw-r--r--src/core/hle/service/glue/time/time_zone_binary.cpp221
-rw-r--r--src/core/hle/service/glue/time/time_zone_binary.h32
-rw-r--r--src/core/hle/service/glue/time/worker.cpp338
-rw-r--r--src/core/hle/service/glue/time/worker.h64
-rw-r--r--src/core/hle/service/kernel_helpers.cpp3
-rw-r--r--src/core/hle/service/nfc/common/device.cpp42
-rw-r--r--src/core/hle/service/nfc/common/device.h7
-rw-r--r--src/core/hle/service/nfc/common/device_manager.cpp37
-rw-r--r--src/core/hle/service/nfc/common/device_manager.h5
-rw-r--r--src/core/hle/service/nfc/nfc_interface.cpp5
-rw-r--r--src/core/hle/service/ns/language.cpp2
-rw-r--r--src/core/hle/service/ns/language.h5
-rw-r--r--src/core/hle/service/psc/psc.cpp17
-rw-r--r--src/core/hle/service/psc/time/alarms.cpp209
-rw-r--r--src/core/hle/service/psc/time/alarms.h139
-rw-r--r--src/core/hle/service/psc/time/clocks/context_writers.cpp83
-rw-r--r--src/core/hle/service/psc/time/clocks/context_writers.h79
-rw-r--r--src/core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h21
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp20
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h23
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp42
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h30
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_steady_clock_core.cpp101
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_steady_clock_core.h54
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp63
-rw-r--r--src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h55
-rw-r--r--src/core/hle/service/psc/time/clocks/steady_clock_core.h81
-rw-r--r--src/core/hle/service/psc/time/clocks/system_clock_core.cpp75
-rw-r--r--src/core/hle/service/psc/time/clocks/system_clock_core.h55
-rw-r--r--src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp43
-rw-r--r--src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h41
-rw-r--r--src/core/hle/service/psc/time/common.cpp16
-rw-r--r--src/core/hle/service/psc/time/common.h168
-rw-r--r--src/core/hle/service/psc/time/errors.h24
-rw-r--r--src/core/hle/service/psc/time/manager.h56
-rw-r--r--src/core/hle/service/psc/time/power_state_request_manager.cpp50
-rw-r--r--src/core/hle/service/psc/time/power_state_request_manager.h42
-rw-r--r--src/core/hle/service/psc/time/power_state_service.cpp49
-rw-r--r--src/core/hle/service/psc/time/power_state_service.h32
-rw-r--r--src/core/hle/service/psc/time/service_manager.cpp494
-rw-r--r--src/core/hle/service/psc/time/service_manager.h101
-rw-r--r--src/core/hle/service/psc/time/shared_memory.cpp84
-rw-r--r--src/core/hle/service/psc/time/shared_memory.h70
-rw-r--r--src/core/hle/service/psc/time/static.cpp500
-rw-r--r--src/core/hle/service/psc/time/static.h95
-rw-r--r--src/core/hle/service/psc/time/steady_clock.cpp164
-rw-r--r--src/core/hle/service/psc/time/steady_clock.h49
-rw-r--r--src/core/hle/service/psc/time/system_clock.cpp127
-rw-r--r--src/core/hle/service/psc/time/system_clock.h46
-rw-r--r--src/core/hle/service/psc/time/time_zone.cpp280
-rw-r--r--src/core/hle/service/psc/time/time_zone.h62
-rw-r--r--src/core/hle/service/psc/time/time_zone_service.cpp289
-rw-r--r--src/core/hle/service/psc/time/time_zone_service.h69
-rw-r--r--src/core/hle/service/service.cpp7
-rw-r--r--src/core/hle/service/set/private_settings.h72
-rw-r--r--src/core/hle/service/set/setting_formats/private_settings.h1
-rw-r--r--src/core/hle/service/set/setting_formats/system_settings.cpp2
-rw-r--r--src/core/hle/service/set/setting_formats/system_settings.h16
-rw-r--r--src/core/hle/service/set/settings_types.h8
-rw-r--r--src/core/hle/service/set/system_settings_server.cpp62
-rw-r--r--src/core/hle/service/set/system_settings_server.h25
-rw-r--r--src/core/hle/service/sm/sm.h15
-rw-r--r--src/core/hle/service/time/clock_types.h129
-rw-r--r--src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h15
-rw-r--r--src/core/hle/service/time/ephemeral_network_system_clock_core.h16
-rw-r--r--src/core/hle/service/time/errors.h21
-rw-r--r--src/core/hle/service/time/local_system_clock_context_writer.h26
-rw-r--r--src/core/hle/service/time/network_system_clock_context_writer.h27
-rw-r--r--src/core/hle/service/time/standard_local_system_clock_core.h16
-rw-r--r--src/core/hle/service/time/standard_network_system_clock_core.h45
-rw-r--r--src/core/hle/service/time/standard_steady_clock_core.cpp24
-rw-r--r--src/core/hle/service/time/standard_steady_clock_core.h41
-rw-r--r--src/core/hle/service/time/standard_user_system_clock_core.cpp81
-rw-r--r--src/core/hle/service/time/standard_user_system_clock_core.h63
-rw-r--r--src/core/hle/service/time/steady_clock_core.h55
-rw-r--r--src/core/hle/service/time/system_clock_context_update_callback.cpp54
-rw-r--r--src/core/hle/service/time/system_clock_context_update_callback.h43
-rw-r--r--src/core/hle/service/time/system_clock_core.cpp71
-rw-r--r--src/core/hle/service/time/system_clock_core.h72
-rw-r--r--src/core/hle/service/time/tick_based_steady_clock_core.cpp22
-rw-r--r--src/core/hle/service/time/tick_based_steady_clock_core.h28
-rw-r--r--src/core/hle/service/time/time.cpp412
-rw-r--r--src/core/hle/service/time/time.h51
-rw-r--r--src/core/hle/service/time/time_interface.cpp41
-rw-r--r--src/core/hle/service/time/time_interface.h20
-rw-r--r--src/core/hle/service/time/time_manager.cpp293
-rw-r--r--src/core/hle/service/time/time_manager.h74
-rw-r--r--src/core/hle/service/time/time_sharedmemory.cpp69
-rw-r--r--src/core/hle/service/time/time_sharedmemory.h89
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.cpp151
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.h49
-rw-r--r--src/core/hle/service/time/time_zone_manager.cpp1182
-rw-r--r--src/core/hle/service/time/time_zone_manager.h61
-rw-r--r--src/core/hle/service/time/time_zone_service.cpp217
-rw-r--r--src/core/hle/service/time/time_zone_service.h38
-rw-r--r--src/core/hle/service/time/time_zone_types.h86
-rw-r--r--src/tests/video_core/memory_tracker.cpp2
-rw-r--r--src/video_core/query_cache/query_base.h2
-rw-r--r--src/video_core/query_cache/query_cache_base.h2
-rw-r--r--src/video_core/query_cache/query_stream.h2
-rw-r--r--src/video_core/query_cache/types.h2
-rw-r--r--src/video_core/rasterizer_download_area.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_descriptor_pool.h2
-rw-r--r--src/video_core/texture_cache/accelerated_swizzle.cpp2
-rw-r--r--src/yuzu/configuration/configure_ringcon.cpp2
-rw-r--r--src/yuzu/configuration/configure_system.cpp57
-rw-r--r--src/yuzu/configuration/configure_system.h6
-rw-r--r--src/yuzu/configuration/shared_translation.cpp4
-rw-r--r--src/yuzu/debugger/console.h2
144 files changed, 8734 insertions, 3972 deletions
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index 407c5c640..15b444338 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -178,6 +178,9 @@ if (NOT TARGET stb::headers)
178 add_library(stb::headers ALIAS stb) 178 add_library(stb::headers ALIAS stb)
179endif() 179endif()
180 180
181add_library(tz tz/tz/tz.cpp)
182target_include_directories(tz PUBLIC ./tz)
183
181add_library(bc_decoder bc_decoder/bc_decoder.cpp) 184add_library(bc_decoder bc_decoder/bc_decoder.cpp)
182target_include_directories(bc_decoder PUBLIC ./bc_decoder) 185target_include_directories(bc_decoder PUBLIC ./bc_decoder)
183 186
diff --git a/externals/tz/tz/tz.cpp b/externals/tz/tz/tz.cpp
new file mode 100644
index 000000000..0c8b68217
--- /dev/null
+++ b/externals/tz/tz/tz.cpp
@@ -0,0 +1,1636 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-FileCopyrightText: 1996 Arthur David Olson
3// SPDX-License-Identifier: BSD-2-Clause
4
5#include <climits>
6#include <cstring>
7#include <ctime>
8
9#include "tz.h"
10
11namespace Tz {
12
13namespace {
14#define EINVAL 22
15
16static Rule gmtmem{};
17static Rule* const gmtptr = &gmtmem;
18
19struct TzifHeader {
20 std::array<char, 4> tzh_magic; // "TZif"
21 std::array<char, 1> tzh_version;
22 std::array<char, 15> tzh_reserved;
23 std::array<char, 4> tzh_ttisutcnt;
24 std::array<char, 4> tzh_ttisstdcnt;
25 std::array<char, 4> tzh_leapcnt;
26 std::array<char, 4> tzh_timecnt;
27 std::array<char, 4> tzh_typecnt;
28 std::array<char, 4> tzh_charcnt;
29};
30static_assert(sizeof(TzifHeader) == 0x2C, "TzifHeader has the wrong size!");
31
32struct local_storage {
33 // Binary layout:
34 // char buf[2 * sizeof(TzifHeader) + 2 * sizeof(Rule) + 4 * TZ_MAX_TIMES];
35 std::span<const u8> binary;
36 Rule state;
37};
38static local_storage tzloadbody_local_storage;
39
40enum rtype : s32 {
41 JULIAN_DAY = 0,
42 DAY_OF_YEAR = 1,
43 MONTH_NTH_DAY_OF_WEEK = 2,
44};
45
46struct tzrule {
47 rtype r_type;
48 int r_day;
49 int r_week;
50 int r_mon;
51 s64 r_time;
52};
53static_assert(sizeof(tzrule) == 0x18, "tzrule has the wrong size!");
54
55constexpr static char UNSPEC[] = "-00";
56constexpr static char TZDEFRULESTRING[] = ",M3.2.0,M11.1.0";
57
58enum {
59 SECSPERMIN = 60,
60 MINSPERHOUR = 60,
61 SECSPERHOUR = SECSPERMIN * MINSPERHOUR,
62 HOURSPERDAY = 24,
63 DAYSPERWEEK = 7,
64 DAYSPERNYEAR = 365,
65 DAYSPERLYEAR = DAYSPERNYEAR + 1,
66 MONSPERYEAR = 12,
67 YEARSPERREPEAT = 400 /* years before a Gregorian repeat */
68};
69
70#define SECSPERDAY ((s64)SECSPERHOUR * HOURSPERDAY)
71
72#define DAYSPERREPEAT ((s64)400 * 365 + 100 - 4 + 1)
73#define SECSPERREPEAT ((int_fast64_t)DAYSPERREPEAT * SECSPERDAY)
74#define AVGSECSPERYEAR (SECSPERREPEAT / YEARSPERREPEAT)
75
76enum {
77 TM_SUNDAY,
78 TM_MONDAY,
79 TM_TUESDAY,
80 TM_WEDNESDAY,
81 TM_THURSDAY,
82 TM_FRIDAY,
83 TM_SATURDAY,
84};
85
86enum {
87 TM_JANUARY,
88 TM_FEBRUARY,
89 TM_MARCH,
90 TM_APRIL,
91 TM_MAY,
92 TM_JUNE,
93 TM_JULY,
94 TM_AUGUST,
95 TM_SEPTEMBER,
96 TM_OCTOBER,
97 TM_NOVEMBER,
98 TM_DECEMBER,
99};
100
101constexpr s32 TM_YEAR_BASE = 1900;
102constexpr s32 TM_WDAY_BASE = TM_MONDAY;
103constexpr s32 EPOCH_YEAR = 1970;
104constexpr s32 EPOCH_WDAY = TM_THURSDAY;
105
106#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
107
108static constexpr std::array<std::array<int, MONSPERYEAR>, 2> mon_lengths = { {
109 {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
110 {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
111} };
112
113static constexpr std::array<int, 2> year_lengths = {
114 DAYSPERNYEAR,
115 DAYSPERLYEAR,
116};
117
118constexpr static time_t leaps_thru_end_of_nonneg(time_t y) {
119 return y / 4 - y / 100 + y / 400;
120}
121
122constexpr static time_t leaps_thru_end_of(time_t y) {
123 return (y < 0 ? -1 - leaps_thru_end_of_nonneg(-1 - y) : leaps_thru_end_of_nonneg(y));
124}
125
126#define TWOS_COMPLEMENT(t) ((t) ~(t)0 < 0)
127
128s32 detzcode(const char* const codep) {
129 s32 result;
130 int i;
131 s32 one = 1;
132 s32 halfmaxval = one << (32 - 2);
133 s32 maxval = halfmaxval - 1 + halfmaxval;
134 s32 minval = -1 - maxval;
135
136 result = codep[0] & 0x7f;
137 for (i = 1; i < 4; ++i) {
138 result = (result << 8) | (codep[i] & 0xff);
139 }
140
141 if (codep[0] & 0x80) {
142 /* Do two's-complement negation even on non-two's-complement machines.
143 If the result would be minval - 1, return minval. */
144 result -= !TWOS_COMPLEMENT(s32) && result != 0;
145 result += minval;
146 }
147 return result;
148}
149
150int_fast64_t detzcode64(const char* const codep) {
151 int_fast64_t result;
152 int i;
153 int_fast64_t one = 1;
154 int_fast64_t halfmaxval = one << (64 - 2);
155 int_fast64_t maxval = halfmaxval - 1 + halfmaxval;
156 int_fast64_t minval = -static_cast<int_fast64_t>(TWOS_COMPLEMENT(int_fast64_t)) - maxval;
157
158 result = codep[0] & 0x7f;
159 for (i = 1; i < 8; ++i) {
160 result = (result << 8) | (codep[i] & 0xff);
161 }
162
163 if (codep[0] & 0x80) {
164 /* Do two's-complement negation even on non-two's-complement machines.
165 If the result would be minval - 1, return minval. */
166 result -= !TWOS_COMPLEMENT(int_fast64_t) && result != 0;
167 result += minval;
168 }
169 return result;
170}
171
172/* Initialize *S to a value based on UTOFF, ISDST, and DESIGIDX. */
173constexpr void init_ttinfo(ttinfo* s, s64 utoff, bool isdst, int desigidx) {
174 s->tt_utoff = static_cast<s32>(utoff);
175 s->tt_isdst = isdst;
176 s->tt_desigidx = desigidx;
177 s->tt_ttisstd = false;
178 s->tt_ttisut = false;
179}
180
181/* Return true if SP's time type I does not specify local time. */
182bool ttunspecified(struct Rule const* sp, int i) {
183 char const* abbr = &sp->chars[sp->ttis[i].tt_desigidx];
184 /* memcmp is likely faster than strcmp, and is safe due to CHARS_EXTRA. */
185 return memcmp(abbr, UNSPEC, sizeof(UNSPEC)) == 0;
186}
187
188bool typesequiv(const Rule* sp, int a, int b) {
189 bool result;
190
191 if (sp == nullptr || a < 0 || a >= sp->typecnt || b < 0 || b >= sp->typecnt) {
192 result = false;
193 }
194 else {
195 /* Compare the relevant members of *AP and *BP.
196 Ignore tt_ttisstd and tt_ttisut, as they are
197 irrelevant now and counting them could cause
198 sp->goahead to mistakenly remain false. */
199 const ttinfo* ap = &sp->ttis[a];
200 const ttinfo* bp = &sp->ttis[b];
201 result = (ap->tt_utoff == bp->tt_utoff && ap->tt_isdst == bp->tt_isdst &&
202 (strcmp(&sp->chars[ap->tt_desigidx], &sp->chars[bp->tt_desigidx]) == 0));
203 }
204 return result;
205}
206
207constexpr const char* getqzname(const char* strp, const int delim) {
208 int c;
209
210 while ((c = *strp) != '\0' && c != delim) {
211 ++strp;
212 }
213 return strp;
214}
215
216/* Is C an ASCII digit? */
217constexpr bool is_digit(char c) {
218 return '0' <= c && c <= '9';
219}
220
221/*
222** Given a pointer into a timezone string, scan until a character that is not
223** a valid character in a time zone abbreviation is found.
224** Return a pointer to that character.
225*/
226
227constexpr const char* getzname(const char* strp) {
228 char c;
229
230 while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' && c != '+') {
231 ++strp;
232 }
233 return strp;
234}
235
236static const char* getnum(const char* strp, int* const nump, const int min, const int max) {
237 char c;
238 int num;
239
240 if (strp == nullptr || !is_digit(c = *strp)) {
241 return nullptr;
242 }
243 num = 0;
244 do {
245 num = num * 10 + (c - '0');
246 if (num > max) {
247 return nullptr; /* illegal value */
248 }
249 c = *++strp;
250 } while (is_digit(c));
251 if (num < min) {
252 return nullptr; /* illegal value */
253 }
254 *nump = num;
255 return strp;
256}
257
258/*
259** Given a pointer into a timezone string, extract a number of seconds,
260** in hh[:mm[:ss]] form, from the string.
261** If any error occurs, return NULL.
262** Otherwise, return a pointer to the first character not part of the number
263** of seconds.
264*/
265
266const char* getsecs(const char* strp, s64* const secsp) {
267 int num;
268 s64 secsperhour = SECSPERHOUR;
269
270 /*
271 ** 'HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
272 ** "M10.4.6/26", which does not conform to Posix,
273 ** but which specifies the equivalent of
274 ** "02:00 on the first Sunday on or after 23 Oct".
275 */
276 strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
277 if (strp == nullptr) {
278 return nullptr;
279 }
280 *secsp = num * secsperhour;
281 if (*strp == ':') {
282 ++strp;
283 strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
284 if (strp == nullptr) {
285 return nullptr;
286 }
287 *secsp += num * SECSPERMIN;
288 if (*strp == ':') {
289 ++strp;
290 /* 'SECSPERMIN' allows for leap seconds. */
291 strp = getnum(strp, &num, 0, SECSPERMIN);
292 if (strp == nullptr) {
293 return nullptr;
294 }
295 *secsp += num;
296 }
297 }
298 return strp;
299}
300
301/*
302** Given a pointer into a timezone string, extract an offset, in
303** [+-]hh[:mm[:ss]] form, from the string.
304** If any error occurs, return NULL.
305** Otherwise, return a pointer to the first character not part of the time.
306*/
307
308const char* getoffset(const char* strp, s64* const offsetp) {
309 bool neg = false;
310
311 if (*strp == '-') {
312 neg = true;
313 ++strp;
314 }
315 else if (*strp == '+') {
316 ++strp;
317 }
318 strp = getsecs(strp, offsetp);
319 if (strp == nullptr) {
320 return nullptr; /* illegal time */
321 }
322 if (neg) {
323 *offsetp = -*offsetp;
324 }
325 return strp;
326}
327
328constexpr const char* getrule(const char* strp, tzrule* const rulep) {
329 if (*strp == 'J') {
330 /*
331 ** Julian day.
332 */
333 rulep->r_type = JULIAN_DAY;
334 ++strp;
335 strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);
336 }
337 else if (*strp == 'M') {
338 /*
339 ** Month, week, day.
340 */
341 rulep->r_type = MONTH_NTH_DAY_OF_WEEK;
342 ++strp;
343 strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);
344 if (strp == nullptr) {
345 return nullptr;
346 }
347 if (*strp++ != '.') {
348 return nullptr;
349 }
350 strp = getnum(strp, &rulep->r_week, 1, 5);
351 if (strp == nullptr) {
352 return nullptr;
353 }
354 if (*strp++ != '.') {
355 return nullptr;
356 }
357 strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);
358 }
359 else if (is_digit(*strp)) {
360 /*
361 ** Day of year.
362 */
363 rulep->r_type = DAY_OF_YEAR;
364 strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);
365 }
366 else {
367 return nullptr;
368 } /* invalid format */
369 if (strp == nullptr) {
370 return nullptr;
371 }
372 if (*strp == '/') {
373 /*
374 ** Time specified.
375 */
376 ++strp;
377 strp = getoffset(strp, &rulep->r_time);
378 }
379 else {
380 rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */
381 }
382 return strp;
383}
384
385constexpr bool increment_overflow(int* ip, int j) {
386 int const i = *ip;
387
388 /*
389 ** If i >= 0 there can only be overflow if i + j > INT_MAX
390 ** or if j > INT_MAX - i; given i >= 0, INT_MAX - i cannot overflow.
391 ** If i < 0 there can only be overflow if i + j < INT_MIN
392 ** or if j < INT_MIN - i; given i < 0, INT_MIN - i cannot overflow.
393 */
394 if ((i >= 0) ? (j > INT_MAX - i) : (j < INT_MIN - i)) {
395 return true;
396 }
397 *ip += j;
398 return false;
399}
400
401constexpr bool increment_overflow32(s64* const lp, int const m) {
402 s64 const l = *lp;
403
404 if ((l >= 0) ? (m > INT_FAST32_MAX - l) : (m < INT_FAST32_MIN - l))
405 return true;
406 *lp += m;
407 return false;
408}
409
410constexpr bool increment_overflow_time(time_t* tp, s64 j) {
411 /*
412 ** This is like
413 ** 'if (! (TIME_T_MIN <= *tp + j && *tp + j <= TIME_T_MAX)) ...',
414 ** except that it does the right thing even if *tp + j would overflow.
415 */
416 if (!(j < 0 ? (std::is_signed_v<time_t> ? TIME_T_MIN - j <= *tp : -1 - j < *tp)
417 : *tp <= TIME_T_MAX - j)) {
418 return true;
419 }
420 *tp += j;
421 return false;
422}
423
424CalendarTimeInternal* timesub(const time_t* timep, s64 offset, const Rule* sp,
425 CalendarTimeInternal* tmp) {
426 time_t tdays;
427 const int* ip;
428 s64 idays, rem, dayoff, dayrem;
429 time_t y;
430
431 /* Calculate the year, avoiding integer overflow even if
432 time_t is unsigned. */
433 tdays = *timep / SECSPERDAY;
434 rem = *timep % SECSPERDAY;
435 rem += offset % SECSPERDAY + 3 * SECSPERDAY;
436 dayoff = offset / SECSPERDAY + rem / SECSPERDAY - 3;
437 rem %= SECSPERDAY;
438 /* y = (EPOCH_YEAR
439 + floor((tdays + dayoff) / DAYSPERREPEAT) * YEARSPERREPEAT),
440 sans overflow. But calculate against 1570 (EPOCH_YEAR -
441 YEARSPERREPEAT) instead of against 1970 so that things work
442 for localtime values before 1970 when time_t is unsigned. */
443 dayrem = tdays % DAYSPERREPEAT;
444 dayrem += dayoff % DAYSPERREPEAT;
445 y = (EPOCH_YEAR - YEARSPERREPEAT +
446 ((1ull + dayoff / DAYSPERREPEAT + dayrem / DAYSPERREPEAT - ((dayrem % DAYSPERREPEAT) < 0) +
447 tdays / DAYSPERREPEAT) *
448 YEARSPERREPEAT));
449 /* idays = (tdays + dayoff) mod DAYSPERREPEAT, sans overflow. */
450 idays = tdays % DAYSPERREPEAT;
451 idays += dayoff % DAYSPERREPEAT + 2 * DAYSPERREPEAT;
452 idays %= DAYSPERREPEAT;
453 /* Increase Y and decrease IDAYS until IDAYS is in range for Y. */
454 while (year_lengths[isleap(y)] <= idays) {
455 s64 tdelta = idays / DAYSPERLYEAR;
456 s64 ydelta = tdelta + !tdelta;
457 time_t newy = y + ydelta;
458 int leapdays;
459 leapdays = static_cast<s32>(leaps_thru_end_of(newy - 1) - leaps_thru_end_of(y - 1));
460 idays -= ydelta * DAYSPERNYEAR;
461 idays -= leapdays;
462 y = newy;
463 }
464
465 if constexpr (!std::is_signed_v<time_t> && y < TM_YEAR_BASE) {
466 int signed_y = static_cast<s32>(y);
467 tmp->tm_year = signed_y - TM_YEAR_BASE;
468 }
469 else if ((!std::is_signed_v<time_t> || std::numeric_limits<s32>::min() + TM_YEAR_BASE <= y) &&
470 y - TM_YEAR_BASE <= std::numeric_limits<s32>::max()) {
471 tmp->tm_year = static_cast<s32>(y - TM_YEAR_BASE);
472 }
473 else {
474 // errno = EOVERFLOW;
475 return nullptr;
476 }
477
478 tmp->tm_yday = static_cast<s32>(idays);
479 /*
480 ** The "extra" mods below avoid overflow problems.
481 */
482 tmp->tm_wday = static_cast<s32>(
483 TM_WDAY_BASE + ((tmp->tm_year % DAYSPERWEEK) * (DAYSPERNYEAR % DAYSPERWEEK)) +
484 leaps_thru_end_of(y - 1) - leaps_thru_end_of(TM_YEAR_BASE - 1) + idays);
485 tmp->tm_wday %= DAYSPERWEEK;
486 if (tmp->tm_wday < 0) {
487 tmp->tm_wday += DAYSPERWEEK;
488 }
489 tmp->tm_hour = static_cast<s32>(rem / SECSPERHOUR);
490 rem %= SECSPERHOUR;
491 tmp->tm_min = static_cast<s32>(rem / SECSPERMIN);
492 tmp->tm_sec = static_cast<s32>(rem % SECSPERMIN);
493
494 ip = mon_lengths[isleap(y)].data();
495 for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon)) {
496 idays -= ip[tmp->tm_mon];
497 }
498 tmp->tm_mday = static_cast<s32>(idays + 1);
499 tmp->tm_isdst = 0;
500 return tmp;
501}
502
503CalendarTimeInternal* gmtsub([[maybe_unused]] Rule const* sp, time_t const* timep,
504 s64 offset, CalendarTimeInternal* tmp) {
505 CalendarTimeInternal* result;
506
507 result = timesub(timep, offset, gmtptr, tmp);
508 return result;
509}
510
511CalendarTimeInternal* localsub(Rule const* sp, time_t const* timep, s64 setname,
512 CalendarTimeInternal* const tmp) {
513 const ttinfo* ttisp;
514 int i;
515 CalendarTimeInternal* result;
516 const time_t t = *timep;
517
518 if (sp == nullptr) {
519 /* Don't bother to set tzname etc.; tzset has already done it. */
520 return gmtsub(gmtptr, timep, 0, tmp);
521 }
522 if ((sp->goback && t < sp->ats[0]) || (sp->goahead && t > sp->ats[sp->timecnt - 1])) {
523 time_t newt;
524 time_t seconds;
525 time_t years;
526
527 if (t < sp->ats[0]) {
528 seconds = sp->ats[0] - t;
529 }
530 else {
531 seconds = t - sp->ats[sp->timecnt - 1];
532 }
533 --seconds;
534
535 /* Beware integer overflow, as SECONDS might
536 be close to the maximum time_t. */
537 years = seconds / SECSPERREPEAT * YEARSPERREPEAT;
538 seconds = years * AVGSECSPERYEAR;
539 years += YEARSPERREPEAT;
540 if (t < sp->ats[0]) {
541 newt = t + seconds + SECSPERREPEAT;
542 }
543 else {
544 newt = t - seconds - SECSPERREPEAT;
545 }
546
547 if (newt < sp->ats[0] || newt > sp->ats[sp->timecnt - 1]) {
548 return nullptr; /* "cannot happen" */
549 }
550 result = localsub(sp, &newt, setname, tmp);
551 if (result) {
552 int_fast64_t newy;
553
554 newy = result->tm_year;
555 if (t < sp->ats[0]) {
556 newy -= years;
557 }
558 else {
559 newy += years;
560 }
561 if (!(std::numeric_limits<s32>::min() <= newy &&
562 newy <= std::numeric_limits<s32>::max())) {
563 return nullptr;
564 }
565 result->tm_year = static_cast<s32>(newy);
566 }
567 return result;
568 }
569 if (sp->timecnt == 0 || t < sp->ats[0]) {
570 i = sp->defaulttype;
571 }
572 else {
573 int lo = 1;
574 int hi = sp->timecnt;
575
576 while (lo < hi) {
577 int mid = (lo + hi) >> 1;
578
579 if (t < sp->ats[mid])
580 hi = mid;
581 else
582 lo = mid + 1;
583 }
584 i = sp->types[lo - 1];
585 }
586 ttisp = &sp->ttis[i];
587 /*
588 ** To get (wrong) behavior that's compatible with System V Release 2.0
589 ** you'd replace the statement below with
590 ** t += ttisp->tt_utoff;
591 ** timesub(&t, 0L, sp, tmp);
592 */
593 result = timesub(&t, ttisp->tt_utoff, sp, tmp);
594 if (result) {
595 result->tm_isdst = ttisp->tt_isdst;
596
597 if (ttisp->tt_desigidx > static_cast<s32>(sp->chars.size() - CHARS_EXTRA)) {
598 return nullptr;
599 }
600
601 auto num_chars_to_copy{
602 std::min(sp->chars.size() - ttisp->tt_desigidx, result->tm_zone.size()) - 1 };
603 std::strncpy(result->tm_zone.data(), &sp->chars[ttisp->tt_desigidx], num_chars_to_copy);
604 result->tm_zone[num_chars_to_copy] = '\0';
605
606 auto original_size{ std::strlen(&sp->chars[ttisp->tt_desigidx]) };
607 if (original_size > num_chars_to_copy) {
608 return nullptr;
609 }
610
611 result->tm_utoff = ttisp->tt_utoff;
612 result->time_index = i;
613 }
614 return result;
615}
616
617/*
618** Given a year, a rule, and the offset from UT at the time that rule takes
619** effect, calculate the year-relative time that rule takes effect.
620*/
621
622constexpr s64 transtime(const int year, const tzrule* const rulep,
623 const s64 offset) {
624 bool leapyear;
625 s64 value;
626 int i;
627 int d, m1, yy0, yy1, yy2, dow;
628
629 leapyear = isleap(year);
630 switch (rulep->r_type) {
631 case JULIAN_DAY:
632 /*
633 ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap
634 ** years.
635 ** In non-leap years, or if the day number is 59 or less, just
636 ** add SECSPERDAY times the day number-1 to the time of
637 ** January 1, midnight, to get the day.
638 */
639 value = (rulep->r_day - 1) * SECSPERDAY;
640 if (leapyear && rulep->r_day >= 60) {
641 value += SECSPERDAY;
642 }
643 break;
644
645 case DAY_OF_YEAR:
646 /*
647 ** n - day of year.
648 ** Just add SECSPERDAY times the day number to the time of
649 ** January 1, midnight, to get the day.
650 */
651 value = rulep->r_day * SECSPERDAY;
652 break;
653
654 case MONTH_NTH_DAY_OF_WEEK:
655 /*
656 ** Mm.n.d - nth "dth day" of month m.
657 */
658
659 /*
660 ** Use Zeller's Congruence to get day-of-week of first day of
661 ** month.
662 */
663 m1 = (rulep->r_mon + 9) % 12 + 1;
664 yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;
665 yy1 = yy0 / 100;
666 yy2 = yy0 % 100;
667 dow = ((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
668 if (dow < 0) {
669 dow += DAYSPERWEEK;
670 }
671
672 /*
673 ** "dow" is the day-of-week of the first day of the month. Get
674 ** the day-of-month (zero-origin) of the first "dow" day of the
675 ** month.
676 */
677 d = rulep->r_day - dow;
678 if (d < 0) {
679 d += DAYSPERWEEK;
680 }
681 for (i = 1; i < rulep->r_week; ++i) {
682 if (d + DAYSPERWEEK >= mon_lengths[leapyear][rulep->r_mon - 1]) {
683 break;
684 }
685 d += DAYSPERWEEK;
686 }
687
688 /*
689 ** "d" is the day-of-month (zero-origin) of the day we want.
690 */
691 value = d * SECSPERDAY;
692 for (i = 0; i < rulep->r_mon - 1; ++i) {
693 value += mon_lengths[leapyear][i] * SECSPERDAY;
694 }
695 break;
696
697 default:
698 //UNREACHABLE();
699 break;
700 }
701
702 /*
703 ** "value" is the year-relative time of 00:00:00 UT on the day in
704 ** question. To get the year-relative time of the specified local
705 ** time on that day, add the transition time and the current offset
706 ** from UT.
707 */
708 return value + rulep->r_time + offset;
709}
710
711bool tzparse(const char* name, Rule* sp) {
712 const char* stdname{};
713 const char* dstname{};
714 s64 stdoffset;
715 s64 dstoffset;
716 char* cp;
717 ptrdiff_t stdlen;
718 ptrdiff_t dstlen{};
719 ptrdiff_t charcnt;
720 time_t atlo = TIME_T_MIN, leaplo = TIME_T_MIN;
721
722 stdname = name;
723 if (*name == '<') {
724 name++;
725 stdname = name;
726 name = getqzname(name, '>');
727 if (*name != '>') {
728 return false;
729 }
730 stdlen = name - stdname;
731 name++;
732 }
733 else {
734 name = getzname(name);
735 stdlen = name - stdname;
736 }
737 if (!(0 < stdlen && stdlen <= TZNAME_MAXIMUM)) {
738 return false;
739 }
740 name = getoffset(name, &stdoffset);
741 if (name == nullptr) {
742 return false;
743 }
744 charcnt = stdlen + 1;
745 if (charcnt > TZ_MAX_CHARS) {
746 return false;
747 }
748 if (*name != '\0') {
749 if (*name == '<') {
750 dstname = ++name;
751 name = getqzname(name, '>');
752 if (*name != '>')
753 return false;
754 dstlen = name - dstname;
755 name++;
756 }
757 else {
758 dstname = name;
759 name = getzname(name);
760 dstlen = name - dstname; /* length of DST abbr. */
761 }
762 if (!(0 < dstlen && dstlen <= TZNAME_MAXIMUM)) {
763 return false;
764 }
765 charcnt += dstlen + 1;
766 if (charcnt > TZ_MAX_CHARS) {
767 return false;
768 }
769 if (*name != '\0' && *name != ',' && *name != ';') {
770 name = getoffset(name, &dstoffset);
771 if (name == nullptr) {
772 return false;
773 }
774 }
775 else {
776 dstoffset = stdoffset - SECSPERHOUR;
777 }
778 if (*name == '\0') {
779 name = TZDEFRULESTRING;
780 }
781 if (*name == ',' || *name == ';') {
782 struct tzrule start;
783 struct tzrule end;
784 int year;
785 int timecnt;
786 time_t janfirst;
787 s64 janoffset = 0;
788 int yearbeg, yearlim;
789
790 ++name;
791 if ((name = getrule(name, &start)) == nullptr) {
792 return false;
793 }
794 if (*name++ != ',') {
795 return false;
796 }
797 if ((name = getrule(name, &end)) == nullptr) {
798 return false;
799 }
800 if (*name != '\0') {
801 return false;
802 }
803 sp->typecnt = 2; /* standard time and DST */
804 /*
805 ** Two transitions per year, from EPOCH_YEAR forward.
806 */
807 init_ttinfo(&sp->ttis[0], -stdoffset, false, 0);
808 init_ttinfo(&sp->ttis[1], -dstoffset, true, static_cast<s32>(stdlen + 1));
809 sp->defaulttype = 0;
810 timecnt = 0;
811 janfirst = 0;
812 yearbeg = EPOCH_YEAR;
813
814 do {
815 s64 yearsecs = year_lengths[isleap(yearbeg - 1)] * SECSPERDAY;
816 yearbeg--;
817 if (increment_overflow_time(&janfirst, -yearsecs)) {
818 janoffset = -yearsecs;
819 break;
820 }
821 } while (atlo < janfirst && EPOCH_YEAR - YEARSPERREPEAT / 2 < yearbeg);
822
823 while (true) {
824 s64 yearsecs = year_lengths[isleap(yearbeg)] * SECSPERDAY;
825 int yearbeg1 = yearbeg;
826 time_t janfirst1 = janfirst;
827 if (increment_overflow_time(&janfirst1, yearsecs) ||
828 increment_overflow(&yearbeg1, 1) || atlo <= janfirst1) {
829 break;
830 }
831 yearbeg = yearbeg1;
832 janfirst = janfirst1;
833 }
834
835 yearlim = yearbeg;
836 if (increment_overflow(&yearlim, YEARSPERREPEAT + 1)) {
837 yearlim = INT_MAX;
838 }
839 for (year = yearbeg; year < yearlim; year++) {
840 s64 starttime = transtime(year, &start, stdoffset),
841 endtime = transtime(year, &end, dstoffset);
842 s64 yearsecs = (year_lengths[isleap(year)] * SECSPERDAY);
843 bool reversed = endtime < starttime;
844 if (reversed) {
845 s64 swap = starttime;
846 starttime = endtime;
847 endtime = swap;
848 }
849 if (reversed || (starttime < endtime && endtime - starttime < yearsecs)) {
850 if (TZ_MAX_TIMES - 2 < timecnt) {
851 break;
852 }
853 sp->ats[timecnt] = janfirst;
854 if (!increment_overflow_time(reinterpret_cast<time_t*>(&sp->ats[timecnt]), janoffset + starttime) &&
855 atlo <= sp->ats[timecnt]) {
856 sp->types[timecnt++] = !reversed;
857 }
858 sp->ats[timecnt] = janfirst;
859 if (!increment_overflow_time(reinterpret_cast<time_t*>(&sp->ats[timecnt]), janoffset + endtime) &&
860 atlo <= sp->ats[timecnt]) {
861 sp->types[timecnt++] = reversed;
862 }
863 }
864 if (endtime < leaplo) {
865 yearlim = year;
866 if (increment_overflow(&yearlim, YEARSPERREPEAT + 1)) {
867 yearlim = INT_MAX;
868 }
869 }
870 if (increment_overflow_time(&janfirst, janoffset + yearsecs)) {
871 break;
872 }
873 janoffset = 0;
874 }
875 sp->timecnt = timecnt;
876 if (!timecnt) {
877 sp->ttis[0] = sp->ttis[1];
878 sp->typecnt = 1; /* Perpetual DST. */
879 }
880 else if (YEARSPERREPEAT < year - yearbeg) {
881 sp->goback = sp->goahead = true;
882 }
883 }
884 else {
885 s64 theirstdoffset;
886 s64 theirdstoffset;
887 s64 theiroffset;
888 bool isdst;
889 int i;
890 int j;
891
892 if (*name != '\0') {
893 return false;
894 }
895 /*
896 ** Initial values of theirstdoffset and theirdstoffset.
897 */
898 theirstdoffset = 0;
899 for (i = 0; i < sp->timecnt; ++i) {
900 j = sp->types[i];
901 if (!sp->ttis[j].tt_isdst) {
902 theirstdoffset = -sp->ttis[j].tt_utoff;
903 break;
904 }
905 }
906 theirdstoffset = 0;
907 for (i = 0; i < sp->timecnt; ++i) {
908 j = sp->types[i];
909 if (sp->ttis[j].tt_isdst) {
910 theirdstoffset = -sp->ttis[j].tt_utoff;
911 break;
912 }
913 }
914 /*
915 ** Initially we're assumed to be in standard time.
916 */
917 isdst = false;
918 /*
919 ** Now juggle transition times and types
920 ** tracking offsets as you do.
921 */
922 for (i = 0; i < sp->timecnt; ++i) {
923 j = sp->types[i];
924 sp->types[i] = sp->ttis[j].tt_isdst;
925 if (sp->ttis[j].tt_ttisut) {
926 /* No adjustment to transition time */
927 }
928 else {
929 /*
930 ** If daylight saving time is in
931 ** effect, and the transition time was
932 ** not specified as standard time, add
933 ** the daylight saving time offset to
934 ** the transition time; otherwise, add
935 ** the standard time offset to the
936 ** transition time.
937 */
938 /*
939 ** Transitions from DST to DDST
940 ** will effectively disappear since
941 ** POSIX provides for only one DST
942 ** offset.
943 */
944 if (isdst && !sp->ttis[j].tt_ttisstd) {
945 sp->ats[i] += dstoffset - theirdstoffset;
946 }
947 else {
948 sp->ats[i] += stdoffset - theirstdoffset;
949 }
950 }
951 theiroffset = -sp->ttis[j].tt_utoff;
952 if (sp->ttis[j].tt_isdst) {
953 theirdstoffset = theiroffset;
954 }
955 else {
956 theirstdoffset = theiroffset;
957 }
958 }
959 /*
960 ** Finally, fill in ttis.
961 */
962 init_ttinfo(&sp->ttis[0], -stdoffset, false, 0);
963 init_ttinfo(&sp->ttis[1], -dstoffset, true, static_cast<s32>(stdlen + 1));
964 sp->typecnt = 2;
965 sp->defaulttype = 0;
966 }
967 }
968 else {
969 dstlen = 0;
970 sp->typecnt = 1; /* only standard time */
971 sp->timecnt = 0;
972 init_ttinfo(&sp->ttis[0], -stdoffset, false, 0);
973 sp->defaulttype = 0;
974 }
975 sp->charcnt = static_cast<s32>(charcnt);
976 cp = &sp->chars[0];
977 memcpy(cp, stdname, stdlen);
978 cp += stdlen;
979 *cp++ = '\0';
980 if (dstlen != 0) {
981 memcpy(cp, dstname, dstlen);
982 *(cp + dstlen) = '\0';
983 }
984 return true;
985}
986
987int tzloadbody(Rule* sp, local_storage& local_storage) {
988 int i;
989 int stored;
990 size_t nread{ local_storage.binary.size_bytes() };
991 int tzheadsize = sizeof(struct TzifHeader);
992 TzifHeader header{};
993
994 //ASSERT(local_storage.binary.size_bytes() >= sizeof(TzifHeader));
995 std::memcpy(&header, local_storage.binary.data(), sizeof(TzifHeader));
996
997 sp->goback = sp->goahead = false;
998
999 for (stored = 8; stored <= 8; stored *= 2) {
1000 s64 datablock_size;
1001 s32 ttisstdcnt = detzcode(header.tzh_ttisstdcnt.data());
1002 s32 ttisutcnt = detzcode(header.tzh_ttisutcnt.data());
1003 s32 leapcnt = detzcode(header.tzh_leapcnt.data());
1004 s32 timecnt = detzcode(header.tzh_timecnt.data());
1005 s32 typecnt = detzcode(header.tzh_typecnt.data());
1006 s32 charcnt = detzcode(header.tzh_charcnt.data());
1007 /* Although tzfile(5) currently requires typecnt to be nonzero,
1008 support future formats that may allow zero typecnt
1009 in files that have a TZ string and no transitions. */
1010 if (!(0 <= leapcnt && leapcnt < TZ_MAX_LEAPS && 0 <= typecnt && typecnt < TZ_MAX_TYPES &&
1011 0 <= timecnt && timecnt < TZ_MAX_TIMES && 0 <= charcnt && charcnt < TZ_MAX_CHARS &&
1012 0 <= ttisstdcnt && ttisstdcnt < TZ_MAX_TYPES && 0 <= ttisutcnt &&
1013 ttisutcnt < TZ_MAX_TYPES)) {
1014 return EINVAL;
1015 }
1016 datablock_size = (timecnt * stored /* ats */
1017 + timecnt /* types */
1018 + typecnt * 6 /* ttinfos */
1019 + charcnt /* chars */
1020 + leapcnt * (stored + 4) /* lsinfos */
1021 + ttisstdcnt /* ttisstds */
1022 + ttisutcnt); /* ttisuts */
1023 if (static_cast<s32>(local_storage.binary.size_bytes()) < tzheadsize + datablock_size) {
1024 return EINVAL;
1025 }
1026 if (!((ttisstdcnt == typecnt || ttisstdcnt == 0) &&
1027 (ttisutcnt == typecnt || ttisutcnt == 0))) {
1028 return EINVAL;
1029 }
1030
1031 char const* p = (const char*)local_storage.binary.data() + tzheadsize;
1032
1033 sp->timecnt = timecnt;
1034 sp->typecnt = typecnt;
1035 sp->charcnt = charcnt;
1036
1037 /* Read transitions, discarding those out of time_t range.
1038 But pretend the last transition before TIME_T_MIN
1039 occurred at TIME_T_MIN. */
1040 timecnt = 0;
1041 for (i = 0; i < sp->timecnt; ++i) {
1042 int_fast64_t at = stored == 4 ? detzcode(p) : detzcode64(p);
1043 sp->types[i] = at <= TIME_T_MAX;
1044 if (sp->types[i]) {
1045 time_t attime =
1046 ((std::is_signed_v<time_t> ? at < TIME_T_MIN : at < 0) ? TIME_T_MIN : at);
1047 if (timecnt && attime <= sp->ats[timecnt - 1]) {
1048 if (attime < sp->ats[timecnt - 1])
1049 return EINVAL;
1050 sp->types[i - 1] = 0;
1051 timecnt--;
1052 }
1053 sp->ats[timecnt++] = attime;
1054 }
1055 p += stored;
1056 }
1057
1058 timecnt = 0;
1059 for (i = 0; i < sp->timecnt; ++i) {
1060 unsigned char typ = *p++;
1061 if (sp->typecnt <= typ) {
1062 return EINVAL;
1063 }
1064 if (sp->types[i]) {
1065 sp->types[timecnt++] = typ;
1066 }
1067 }
1068 sp->timecnt = timecnt;
1069 for (i = 0; i < sp->typecnt; ++i) {
1070 struct ttinfo* ttisp;
1071 unsigned char isdst, desigidx;
1072
1073 ttisp = &sp->ttis[i];
1074 ttisp->tt_utoff = detzcode(p);
1075 p += 4;
1076 isdst = *p++;
1077 if (!(isdst < 2)) {
1078 return EINVAL;
1079 }
1080 ttisp->tt_isdst = isdst != 0;
1081 desigidx = *p++;
1082 if (!(desigidx < sp->charcnt)) {
1083 return EINVAL;
1084 }
1085 ttisp->tt_desigidx = desigidx;
1086 }
1087 for (i = 0; i < sp->charcnt; ++i) {
1088 sp->chars[i] = *p++;
1089 }
1090 /* Ensure '\0'-terminated, and make it safe to call
1091 ttunspecified later. */
1092 memset(&sp->chars[i], 0, CHARS_EXTRA);
1093
1094 for (i = 0; i < sp->typecnt; ++i) {
1095 struct ttinfo* ttisp;
1096
1097 ttisp = &sp->ttis[i];
1098 if (ttisstdcnt == 0) {
1099 ttisp->tt_ttisstd = false;
1100 }
1101 else {
1102 if (*(bool*)p != true && *(bool*)p != false) {
1103 return EINVAL;
1104 }
1105 ttisp->tt_ttisstd = *(bool*)p++;
1106 }
1107 }
1108 for (i = 0; i < sp->typecnt; ++i) {
1109 struct ttinfo* ttisp;
1110
1111 ttisp = &sp->ttis[i];
1112 if (ttisutcnt == 0) {
1113 ttisp->tt_ttisut = false;
1114 }
1115 else {
1116 if (*(bool*)p != true && *(bool*)p != false) {
1117 return EINVAL;
1118 }
1119 ttisp->tt_ttisut = *(bool*)p++;
1120 }
1121 }
1122
1123 nread += (ptrdiff_t)local_storage.binary.data() - (ptrdiff_t)p;
1124 if (nread < 0) {
1125 return EINVAL;
1126 }
1127 }
1128
1129 std::array<char, 256> buf{};
1130 if (nread > buf.size()) {
1131 //ASSERT(false);
1132 return EINVAL;
1133 }
1134 memmove(buf.data(), &local_storage.binary[local_storage.binary.size_bytes() - nread], nread);
1135
1136 if (nread > 2 && buf[0] == '\n' && buf[nread - 1] == '\n' && sp->typecnt + 2 <= TZ_MAX_TYPES) {
1137 Rule* ts = &local_storage.state;
1138
1139 buf[nread - 1] = '\0';
1140 if (tzparse(&buf[1], ts) && local_storage.state.typecnt == 2) {
1141
1142 /* Attempt to reuse existing abbreviations.
1143 Without this, America/Anchorage would be right on
1144 the edge after 2037 when TZ_MAX_CHARS is 50, as
1145 sp->charcnt equals 40 (for LMT AST AWT APT AHST
1146 AHDT YST AKDT AKST) and ts->charcnt equals 10
1147 (for AKST AKDT). Reusing means sp->charcnt can
1148 stay 40 in this example. */
1149 int gotabbr = 0;
1150 int charcnt = sp->charcnt;
1151 for (i = 0; i < ts->typecnt; i++) {
1152 char* tsabbr = &ts->chars[ts->ttis[i].tt_desigidx];
1153 int j;
1154 for (j = 0; j < charcnt; j++)
1155 if (strcmp(&sp->chars[j], tsabbr) == 0) {
1156 ts->ttis[i].tt_desigidx = j;
1157 gotabbr++;
1158 break;
1159 }
1160 if (!(j < charcnt)) {
1161 int tsabbrlen = static_cast<s32>(strlen(tsabbr));
1162 if (j + tsabbrlen < TZ_MAX_CHARS) {
1163 strcpy(&sp->chars[j], tsabbr);
1164 charcnt = j + tsabbrlen + 1;
1165 ts->ttis[i].tt_desigidx = j;
1166 gotabbr++;
1167 }
1168 }
1169 }
1170 if (gotabbr == ts->typecnt) {
1171 sp->charcnt = charcnt;
1172
1173 /* Ignore any trailing, no-op transitions generated
1174 by zic as they don't help here and can run afoul
1175 of bugs in zic 2016j or earlier. */
1176 while (1 < sp->timecnt &&
1177 (sp->types[sp->timecnt - 1] == sp->types[sp->timecnt - 2])) {
1178 sp->timecnt--;
1179 }
1180
1181 for (i = 0; i < ts->timecnt && sp->timecnt < TZ_MAX_TIMES; i++) {
1182 time_t t = ts->ats[i];
1183 if (0 < sp->timecnt && t <= sp->ats[sp->timecnt - 1]) {
1184 continue;
1185 }
1186 sp->ats[sp->timecnt] = t;
1187 sp->types[sp->timecnt] = static_cast<u8>(sp->typecnt + ts->types[i]);
1188 sp->timecnt++;
1189 }
1190 for (i = 0; i < ts->typecnt; i++) {
1191 sp->ttis[sp->typecnt++] = ts->ttis[i];
1192 }
1193 }
1194 }
1195 }
1196 if (sp->typecnt == 0) {
1197 return EINVAL;
1198 }
1199
1200 if (sp->timecnt > 1) {
1201 if (sp->ats[0] <= TIME_T_MAX - SECSPERREPEAT) {
1202 time_t repeatat = sp->ats[0] + SECSPERREPEAT;
1203 int repeattype = sp->types[0];
1204 for (i = 1; i < sp->timecnt; ++i) {
1205 if (sp->ats[i] == repeatat && typesequiv(sp, sp->types[i], repeattype)) {
1206 sp->goback = true;
1207 break;
1208 }
1209 }
1210 }
1211 if (TIME_T_MIN + SECSPERREPEAT <= sp->ats[sp->timecnt - 1]) {
1212 time_t repeatat = sp->ats[sp->timecnt - 1] - SECSPERREPEAT;
1213 int repeattype = sp->types[sp->timecnt - 1];
1214 for (i = sp->timecnt - 2; i >= 0; --i) {
1215 if (sp->ats[i] == repeatat && typesequiv(sp, sp->types[i], repeattype)) {
1216 sp->goahead = true;
1217 break;
1218 }
1219 }
1220 }
1221 }
1222
1223 /* Infer sp->defaulttype from the data. Although this default
1224 type is always zero for data from recent tzdb releases,
1225 things are trickier for data from tzdb 2018e or earlier.
1226
1227 The first set of heuristics work around bugs in 32-bit data
1228 generated by tzdb 2013c or earlier. The workaround is for
1229 zones like Australia/Macquarie where timestamps before the
1230 first transition have a time type that is not the earliest
1231 standard-time type. See:
1232 https://mm.icann.org/pipermail/tz/2013-May/019368.html */
1233 /*
1234 ** If type 0 does not specify local time, or is unused in transitions,
1235 ** it's the type to use for early times.
1236 */
1237 for (i = 0; i < sp->timecnt; ++i) {
1238 if (sp->types[i] == 0) {
1239 break;
1240 }
1241 }
1242 i = i < sp->timecnt && !ttunspecified(sp, 0) ? -1 : 0;
1243 /*
1244 ** Absent the above,
1245 ** if there are transition times
1246 ** and the first transition is to a daylight time
1247 ** find the standard type less than and closest to
1248 ** the type of the first transition.
1249 */
1250 if (i < 0 && sp->timecnt > 0 && sp->ttis[sp->types[0]].tt_isdst) {
1251 i = sp->types[0];
1252 while (--i >= 0) {
1253 if (!sp->ttis[i].tt_isdst) {
1254 break;
1255 }
1256 }
1257 }
1258 /* The next heuristics are for data generated by tzdb 2018e or
1259 earlier, for zones like EST5EDT where the first transition
1260 is to DST. */
1261 /*
1262 ** If no result yet, find the first standard type.
1263 ** If there is none, punt to type zero.
1264 */
1265 if (i < 0) {
1266 i = 0;
1267 while (sp->ttis[i].tt_isdst) {
1268 if (++i >= sp->typecnt) {
1269 i = 0;
1270 break;
1271 }
1272 }
1273 }
1274 /* A simple 'sp->defaulttype = 0;' would suffice here if we
1275 didn't have to worry about 2018e-or-earlier data. Even
1276 simpler would be to remove the defaulttype member and just
1277 use 0 in its place. */
1278 sp->defaulttype = i;
1279
1280 return 0;
1281}
1282
1283constexpr int tmcomp(const CalendarTimeInternal* const atmp,
1284 const CalendarTimeInternal* const btmp) {
1285 int result;
1286
1287 if (atmp->tm_year != btmp->tm_year) {
1288 return atmp->tm_year < btmp->tm_year ? -1 : 1;
1289 }
1290 if ((result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&
1291 (result = (atmp->tm_mday - btmp->tm_mday)) == 0 &&
1292 (result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
1293 (result = (atmp->tm_min - btmp->tm_min)) == 0) {
1294 result = atmp->tm_sec - btmp->tm_sec;
1295 }
1296 return result;
1297}
1298
1299/* Copy to *DEST from *SRC. Copy only the members needed for mktime,
1300 as other members might not be initialized. */
1301constexpr void mktmcpy(struct CalendarTimeInternal* dest, struct CalendarTimeInternal const* src) {
1302 dest->tm_sec = src->tm_sec;
1303 dest->tm_min = src->tm_min;
1304 dest->tm_hour = src->tm_hour;
1305 dest->tm_mday = src->tm_mday;
1306 dest->tm_mon = src->tm_mon;
1307 dest->tm_year = src->tm_year;
1308 dest->tm_isdst = src->tm_isdst;
1309 dest->tm_zone = src->tm_zone;
1310 dest->tm_utoff = src->tm_utoff;
1311 dest->time_index = src->time_index;
1312}
1313
1314constexpr bool normalize_overflow(int* const tensptr, int* const unitsptr, const int base) {
1315 int tensdelta;
1316
1317 tensdelta = (*unitsptr >= 0) ? (*unitsptr / base) : (-1 - (-1 - *unitsptr) / base);
1318 *unitsptr -= tensdelta * base;
1319 return increment_overflow(tensptr, tensdelta);
1320}
1321
1322constexpr bool normalize_overflow32(s64* tensptr, int* unitsptr, int base) {
1323 int tensdelta;
1324
1325 tensdelta = (*unitsptr >= 0) ? (*unitsptr / base) : (-1 - (-1 - *unitsptr) / base);
1326 *unitsptr -= tensdelta * base;
1327 return increment_overflow32(tensptr, tensdelta);
1328}
1329
1330int time2sub(time_t* out_time, CalendarTimeInternal* const tmp,
1331 CalendarTimeInternal* (*funcp)(Rule const*, time_t const*, s64,
1332 CalendarTimeInternal*),
1333 Rule const* sp, const s64 offset, bool* okayp, bool do_norm_secs) {
1334 int dir;
1335 int i, j;
1336 int saved_seconds;
1337 s64 li;
1338 time_t lo;
1339 time_t hi;
1340 s64 y;
1341 time_t newt;
1342 time_t t;
1343 CalendarTimeInternal yourtm, mytm;
1344
1345 *okayp = false;
1346 mktmcpy(&yourtm, tmp);
1347
1348 if (do_norm_secs) {
1349 if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec, SECSPERMIN)) {
1350 return 1;
1351 }
1352 }
1353 if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR)) {
1354 return 1;
1355 }
1356 if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY)) {
1357 return 1;
1358 }
1359 y = yourtm.tm_year;
1360 if (normalize_overflow32(&y, &yourtm.tm_mon, MONSPERYEAR)) {
1361 return 1;
1362 }
1363 /*
1364 ** Turn y into an actual year number for now.
1365 ** It is converted back to an offset from TM_YEAR_BASE later.
1366 */
1367 if (increment_overflow32(&y, TM_YEAR_BASE)) {
1368 return 1;
1369 }
1370 while (yourtm.tm_mday <= 0) {
1371 if (increment_overflow32(&y, -1)) {
1372 return 1;
1373 }
1374 li = y + (1 < yourtm.tm_mon);
1375 yourtm.tm_mday += year_lengths[isleap(li)];
1376 }
1377 while (yourtm.tm_mday > DAYSPERLYEAR) {
1378 li = y + (1 < yourtm.tm_mon);
1379 yourtm.tm_mday -= year_lengths[isleap(li)];
1380 if (increment_overflow32(&y, 1)) {
1381 return 1;
1382 }
1383 }
1384 for (;;) {
1385 i = mon_lengths[isleap(y)][yourtm.tm_mon];
1386 if (yourtm.tm_mday <= i) {
1387 break;
1388 }
1389 yourtm.tm_mday -= i;
1390 if (++yourtm.tm_mon >= MONSPERYEAR) {
1391 yourtm.tm_mon = 0;
1392 if (increment_overflow32(&y, 1)) {
1393 return 1;
1394 }
1395 }
1396 }
1397
1398 if (increment_overflow32(&y, -TM_YEAR_BASE)) {
1399 return 1;
1400 }
1401 if (!(INT_MIN <= y && y <= INT_MAX)) {
1402 return 1;
1403 }
1404 yourtm.tm_year = static_cast<s32>(y);
1405
1406 if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN) {
1407 saved_seconds = 0;
1408 }
1409 else if (yourtm.tm_year < EPOCH_YEAR - TM_YEAR_BASE) {
1410 /*
1411 ** We can't set tm_sec to 0, because that might push the
1412 ** time below the minimum representable time.
1413 ** Set tm_sec to 59 instead.
1414 ** This assumes that the minimum representable time is
1415 ** not in the same minute that a leap second was deleted from,
1416 ** which is a safer assumption than using 58 would be.
1417 */
1418 if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN)) {
1419 return 1;
1420 }
1421 saved_seconds = yourtm.tm_sec;
1422 yourtm.tm_sec = SECSPERMIN - 1;
1423 }
1424 else {
1425 saved_seconds = yourtm.tm_sec;
1426 yourtm.tm_sec = 0;
1427 }
1428 /*
1429 ** Do a binary search (this works whatever time_t's type is).
1430 */
1431 lo = TIME_T_MIN;
1432 hi = TIME_T_MAX;
1433 for (;;) {
1434 t = lo / 2 + hi / 2;
1435 if (t < lo) {
1436 t = lo;
1437 }
1438 else if (t > hi) {
1439 t = hi;
1440 }
1441 if (!funcp(sp, &t, offset, &mytm)) {
1442 /*
1443 ** Assume that t is too extreme to be represented in
1444 ** a struct tm; arrange things so that it is less
1445 ** extreme on the next pass.
1446 */
1447 dir = (t > 0) ? 1 : -1;
1448 }
1449 else {
1450 dir = tmcomp(&mytm, &yourtm);
1451 }
1452 if (dir != 0) {
1453 if (t == lo) {
1454 if (t == TIME_T_MAX) {
1455 return 2;
1456 }
1457 ++t;
1458 ++lo;
1459 }
1460 else if (t == hi) {
1461 if (t == TIME_T_MIN) {
1462 return 2;
1463 }
1464 --t;
1465 --hi;
1466 }
1467 if (lo > hi) {
1468 return 2;
1469 }
1470 if (dir > 0) {
1471 hi = t;
1472 }
1473 else {
1474 lo = t;
1475 }
1476 continue;
1477 }
1478
1479 if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) {
1480 break;
1481 }
1482 /*
1483 ** Right time, wrong type.
1484 ** Hunt for right time, right type.
1485 ** It's okay to guess wrong since the guess
1486 ** gets checked.
1487 */
1488 if (sp == nullptr) {
1489 return 2;
1490 }
1491 for (i = sp->typecnt - 1; i >= 0; --i) {
1492 if (sp->ttis[i].tt_isdst != static_cast<bool>(yourtm.tm_isdst)) {
1493 continue;
1494 }
1495 for (j = sp->typecnt - 1; j >= 0; --j) {
1496 if (sp->ttis[j].tt_isdst == static_cast<bool>(yourtm.tm_isdst)) {
1497 continue;
1498 }
1499 if (ttunspecified(sp, j)) {
1500 continue;
1501 }
1502 newt = (t + sp->ttis[j].tt_utoff - sp->ttis[i].tt_utoff);
1503 if (!funcp(sp, &newt, offset, &mytm)) {
1504 continue;
1505 }
1506 if (tmcomp(&mytm, &yourtm) != 0) {
1507 continue;
1508 }
1509 if (mytm.tm_isdst != yourtm.tm_isdst) {
1510 continue;
1511 }
1512 /*
1513 ** We have a match.
1514 */
1515 t = newt;
1516 goto label;
1517 }
1518 }
1519 return 2;
1520 }
1521label:
1522 newt = t + saved_seconds;
1523 t = newt;
1524 if (funcp(sp, &t, offset, tmp) || *okayp) {
1525 *okayp = true;
1526 *out_time = t;
1527 return 0;
1528 }
1529 return 2;
1530}
1531
1532int time2(time_t* out_time, struct CalendarTimeInternal* const tmp,
1533 struct CalendarTimeInternal* (*funcp)(struct Rule const*, time_t const*, s64,
1534 struct CalendarTimeInternal*),
1535 struct Rule const* sp, const s64 offset, bool* okayp) {
1536 int res;
1537
1538 /*
1539 ** First try without normalization of seconds
1540 ** (in case tm_sec contains a value associated with a leap second).
1541 ** If that fails, try with normalization of seconds.
1542 */
1543 res = time2sub(out_time, tmp, funcp, sp, offset, okayp, false);
1544 return *okayp ? res : time2sub(out_time, tmp, funcp, sp, offset, okayp, true);
1545}
1546
1547int time1(time_t* out_time, CalendarTimeInternal* const tmp,
1548 CalendarTimeInternal* (*funcp)(Rule const*, time_t const*, s64,
1549 CalendarTimeInternal*),
1550 Rule const* sp, const s64 offset) {
1551 int samei, otheri;
1552 int sameind, otherind;
1553 int i;
1554 int nseen;
1555 char seen[TZ_MAX_TYPES];
1556 unsigned char types[TZ_MAX_TYPES];
1557 bool okay;
1558
1559 if (tmp->tm_isdst > 1) {
1560 tmp->tm_isdst = 1;
1561 }
1562 auto res = time2(out_time, tmp, funcp, sp, offset, &okay);
1563 if (res == 0) {
1564 return res;
1565 }
1566 if (tmp->tm_isdst < 0) {
1567 return res;
1568 }
1569 /*
1570 ** We're supposed to assume that somebody took a time of one type
1571 ** and did some math on it that yielded a "struct tm" that's bad.
1572 ** We try to divine the type they started from and adjust to the
1573 ** type they need.
1574 */
1575 for (i = 0; i < sp->typecnt; ++i) {
1576 seen[i] = false;
1577 }
1578
1579 if (sp->timecnt < 1) {
1580 return 2;
1581 }
1582
1583 nseen = 0;
1584 for (i = sp->timecnt - 1; i >= 0; --i) {
1585 if (!seen[sp->types[i]] && !ttunspecified(sp, sp->types[i])) {
1586 seen[sp->types[i]] = true;
1587 types[nseen++] = sp->types[i];
1588 }
1589 }
1590
1591 if (nseen < 1) {
1592 return 2;
1593 }
1594
1595 for (sameind = 0; sameind < nseen; ++sameind) {
1596 samei = types[sameind];
1597 if (sp->ttis[samei].tt_isdst != static_cast<bool>(tmp->tm_isdst)) {
1598 continue;
1599 }
1600 for (otherind = 0; otherind < nseen; ++otherind) {
1601 otheri = types[otherind];
1602 if (sp->ttis[otheri].tt_isdst == static_cast<bool>(tmp->tm_isdst)) {
1603 continue;
1604 }
1605 tmp->tm_sec += (sp->ttis[otheri].tt_utoff - sp->ttis[samei].tt_utoff);
1606 tmp->tm_isdst = !tmp->tm_isdst;
1607 res = time2(out_time, tmp, funcp, sp, offset, &okay);
1608 if (res == 0) {
1609 return res;
1610 }
1611 tmp->tm_sec -= (sp->ttis[otheri].tt_utoff - sp->ttis[samei].tt_utoff);
1612 tmp->tm_isdst = !tmp->tm_isdst;
1613 }
1614 }
1615 return 2;
1616}
1617
1618} // namespace
1619
1620s32 ParseTimeZoneBinary(Rule& out_rule, std::span<const u8> binary) {
1621 tzloadbody_local_storage.binary = binary;
1622 if (tzloadbody(&out_rule, tzloadbody_local_storage)) {
1623 return 3;
1624 }
1625 return 0;
1626}
1627
1628bool localtime_rz(CalendarTimeInternal* tmp, Rule* sp, time_t* timep) {
1629 return localsub(sp, timep, 0, tmp) == nullptr;
1630}
1631
1632u32 mktime_tzname(time_t* out_time, Rule* sp, CalendarTimeInternal* tmp) {
1633 return time1(out_time, tmp, localsub, sp, 0);
1634}
1635
1636} // namespace Tz
diff --git a/externals/tz/tz/tz.h b/externals/tz/tz/tz.h
new file mode 100644
index 000000000..38605cfb1
--- /dev/null
+++ b/externals/tz/tz/tz.h
@@ -0,0 +1,81 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-FileCopyrightText: 1996 Arthur David Olson
3// SPDX-License-Identifier: BSD-2-Clause
4
5#pragma once
6
7#include <cstdint>
8#include <limits>
9#include <span>
10#include <array>
11#include <time.h>
12
13namespace Tz {
14using u8 = uint8_t;
15using s8 = int8_t;
16using u16 = uint16_t;
17using s16 = int16_t;
18using u32 = uint32_t;
19using s32 = int32_t;
20using u64 = uint64_t;
21using s64 = int64_t;
22
23constexpr size_t TZ_MAX_TIMES = 1000;
24constexpr size_t TZ_MAX_TYPES = 128;
25constexpr size_t TZ_MAX_CHARS = 50;
26constexpr size_t MY_TZNAME_MAX = 255;
27constexpr size_t TZNAME_MAXIMUM = 255;
28constexpr size_t TZ_MAX_LEAPS = 50;
29constexpr s64 TIME_T_MAX = std::numeric_limits<s64>::max();
30constexpr s64 TIME_T_MIN = std::numeric_limits<s64>::min();
31constexpr size_t CHARS_EXTRA = 3;
32constexpr size_t MAX_ZONE_CHARS = std::max(TZ_MAX_CHARS + CHARS_EXTRA, sizeof("UTC"));
33constexpr size_t MAX_TZNAME_CHARS = 2 * (MY_TZNAME_MAX + 1);
34
35struct ttinfo {
36 s32 tt_utoff;
37 bool tt_isdst;
38 s32 tt_desigidx;
39 bool tt_ttisstd;
40 bool tt_ttisut;
41};
42static_assert(sizeof(ttinfo) == 0x10, "ttinfo has the wrong size!");
43
44struct Rule {
45 s32 timecnt;
46 s32 typecnt;
47 s32 charcnt;
48 bool goback;
49 bool goahead;
50 std::array <u8, 0x2> padding0;
51 std::array<s64, TZ_MAX_TIMES> ats;
52 std::array<u8, TZ_MAX_TIMES> types;
53 std::array<ttinfo, TZ_MAX_TYPES> ttis;
54 std::array<char, std::max(MAX_ZONE_CHARS, MAX_TZNAME_CHARS)> chars;
55 s32 defaulttype;
56 std::array <u8, 0x12C4> padding1;
57};
58static_assert(sizeof(Rule) == 0x4000, "Rule has the wrong size!");
59
60struct CalendarTimeInternal {
61 s32 tm_sec;
62 s32 tm_min;
63 s32 tm_hour;
64 s32 tm_mday;
65 s32 tm_mon;
66 s32 tm_year;
67 s32 tm_wday;
68 s32 tm_yday;
69 s32 tm_isdst;
70 std::array<char, 16> tm_zone;
71 s32 tm_utoff;
72 s32 time_index;
73};
74static_assert(sizeof(CalendarTimeInternal) == 0x3C, "CalendarTimeInternal has the wrong size!");
75
76s32 ParseTimeZoneBinary(Rule& out_rule, std::span<const u8> binary);
77
78bool localtime_rz(CalendarTimeInternal* tmp, Rule* sp, time_t* timep);
79u32 mktime_tzname(time_t* out_time, Rule* sp, CalendarTimeInternal* tmp);
80
81} // namespace Tz
diff --git a/src/common/arm64/native_clock.cpp b/src/common/arm64/native_clock.cpp
index f437d7187..76ffb74ba 100644
--- a/src/common/arm64/native_clock.cpp
+++ b/src/common/arm64/native_clock.cpp
@@ -30,27 +30,27 @@ NativeClock::NativeClock() {
30} 30}
31 31
32std::chrono::nanoseconds NativeClock::GetTimeNS() const { 32std::chrono::nanoseconds NativeClock::GetTimeNS() const {
33 return std::chrono::nanoseconds{MultiplyHigh(GetHostTicksElapsed(), ns_cntfrq_factor)}; 33 return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_cntfrq_factor)};
34} 34}
35 35
36std::chrono::microseconds NativeClock::GetTimeUS() const { 36std::chrono::microseconds NativeClock::GetTimeUS() const {
37 return std::chrono::microseconds{MultiplyHigh(GetHostTicksElapsed(), us_cntfrq_factor)}; 37 return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_cntfrq_factor)};
38} 38}
39 39
40std::chrono::milliseconds NativeClock::GetTimeMS() const { 40std::chrono::milliseconds NativeClock::GetTimeMS() const {
41 return std::chrono::milliseconds{MultiplyHigh(GetHostTicksElapsed(), ms_cntfrq_factor)}; 41 return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_cntfrq_factor)};
42} 42}
43 43
44u64 NativeClock::GetCNTPCT() const { 44s64 NativeClock::GetCNTPCT() const {
45 return MultiplyHigh(GetHostTicksElapsed(), guest_cntfrq_factor); 45 return MultiplyHigh(GetUptime(), guest_cntfrq_factor);
46} 46}
47 47
48u64 NativeClock::GetGPUTick() const { 48s64 NativeClock::GetGPUTick() const {
49 return MultiplyHigh(GetHostTicksElapsed(), gputick_cntfrq_factor); 49 return MultiplyHigh(GetUptime(), gputick_cntfrq_factor);
50} 50}
51 51
52u64 NativeClock::GetHostTicksNow() const { 52s64 NativeClock::GetUptime() const {
53 u64 cntvct_el0 = 0; 53 s64 cntvct_el0 = 0;
54 asm volatile("dsb ish\n\t" 54 asm volatile("dsb ish\n\t"
55 "mrs %[cntvct_el0], cntvct_el0\n\t" 55 "mrs %[cntvct_el0], cntvct_el0\n\t"
56 "dsb ish\n\t" 56 "dsb ish\n\t"
@@ -58,15 +58,11 @@ u64 NativeClock::GetHostTicksNow() const {
58 return cntvct_el0; 58 return cntvct_el0;
59} 59}
60 60
61u64 NativeClock::GetHostTicksElapsed() const {
62 return GetHostTicksNow();
63}
64
65bool NativeClock::IsNative() const { 61bool NativeClock::IsNative() const {
66 return true; 62 return true;
67} 63}
68 64
69u64 NativeClock::GetHostCNTFRQ() { 65s64 NativeClock::GetHostCNTFRQ() {
70 u64 cntfrq_el0 = 0; 66 u64 cntfrq_el0 = 0;
71 std::string_view board{""}; 67 std::string_view board{""};
72#ifdef ANDROID 68#ifdef ANDROID
diff --git a/src/common/arm64/native_clock.h b/src/common/arm64/native_clock.h
index a28b419f2..94bc1882e 100644
--- a/src/common/arm64/native_clock.h
+++ b/src/common/arm64/native_clock.h
@@ -17,17 +17,15 @@ public:
17 17
18 std::chrono::milliseconds GetTimeMS() const override; 18 std::chrono::milliseconds GetTimeMS() const override;
19 19
20 u64 GetCNTPCT() const override; 20 s64 GetCNTPCT() const override;
21 21
22 u64 GetGPUTick() const override; 22 s64 GetGPUTick() const override;
23 23
24 u64 GetHostTicksNow() const override; 24 s64 GetUptime() const override;
25
26 u64 GetHostTicksElapsed() const override;
27 25
28 bool IsNative() const override; 26 bool IsNative() const override;
29 27
30 static u64 GetHostCNTFRQ(); 28 static s64 GetHostCNTFRQ();
31 29
32public: 30public:
33 using FactorType = unsigned __int128; 31 using FactorType = unsigned __int128;
diff --git a/src/common/memory_detect.h b/src/common/memory_detect.h
index a345e6d28..c8f239aed 100644
--- a/src/common/memory_detect.h
+++ b/src/common/memory_detect.h
@@ -18,4 +18,4 @@ struct MemoryInfo {
18 */ 18 */
19[[nodiscard]] const MemoryInfo& GetMemInfo(); 19[[nodiscard]] const MemoryInfo& GetMemInfo();
20 20
21} // namespace Common \ No newline at end of file 21} // namespace Common
diff --git a/src/common/settings.h b/src/common/settings.h
index 07dba53ab..16749ab68 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -419,9 +419,16 @@ struct Values {
419 linkage, false, "custom_rtc_enabled", Category::System, Specialization::Paired, true, true}; 419 linkage, false, "custom_rtc_enabled", Category::System, Specialization::Paired, true, true};
420 SwitchableSetting<s64> custom_rtc{ 420 SwitchableSetting<s64> custom_rtc{
421 linkage, 0, "custom_rtc", Category::System, Specialization::Time, 421 linkage, 0, "custom_rtc", Category::System, Specialization::Time,
422 true, true, &custom_rtc_enabled}; 422 false, true, &custom_rtc_enabled};
423 // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc` 423 SwitchableSetting<s64, true> custom_rtc_offset{linkage,
424 s64 custom_rtc_differential; 424 0,
425 std::numeric_limits<int>::min(),
426 std::numeric_limits<int>::max(),
427 "custom_rtc_offset",
428 Category::System,
429 Specialization::Countable,
430 true,
431 true};
425 SwitchableSetting<bool> rng_seed_enabled{ 432 SwitchableSetting<bool> rng_seed_enabled{
426 linkage, false, "rng_seed_enabled", Category::System, Specialization::Paired, true, true}; 433 linkage, false, "rng_seed_enabled", Category::System, Specialization::Paired, true, true};
427 SwitchableSetting<u32> rng_seed{ 434 SwitchableSetting<u32> rng_seed{
diff --git a/src/common/time_zone.cpp b/src/common/time_zone.cpp
index 69e728a9d..f77df604f 100644
--- a/src/common/time_zone.cpp
+++ b/src/common/time_zone.cpp
@@ -88,7 +88,17 @@ std::string FindSystemTimeZone() {
88 LOG_ERROR(Common, "Time zone {} not handled, defaulting to hour offset.", tz_index); 88 LOG_ERROR(Common, "Time zone {} not handled, defaulting to hour offset.", tz_index);
89 } 89 }
90 } 90 }
91 return fmt::format("Etc/GMT{:s}{:d}", hours > 0 ? "-" : "+", std::abs(hours)); 91
92 // For some reason the Etc/GMT times are reversed. GMT+6 contains -21600 as its offset,
93 // -6 hours instead of +6 hours, so these signs are purposefully reversed to fix it.
94 std::string postfix{""};
95 if (hours > 0) {
96 postfix = fmt::format("-{:d}", std::abs(hours));
97 } else if (hours < 0) {
98 postfix = fmt::format("+{:d}", std::abs(hours));
99 }
100
101 return fmt::format("Etc/GMT{:s}", postfix);
92} 102}
93 103
94} // namespace Common::TimeZone 104} // namespace Common::TimeZone
diff --git a/src/common/uuid.h b/src/common/uuid.h
index 7172ca165..81bfefbbb 100644
--- a/src/common/uuid.h
+++ b/src/common/uuid.h
@@ -12,9 +12,8 @@
12namespace Common { 12namespace Common {
13 13
14struct UUID { 14struct UUID {
15 std::array<u8, 0x10> uuid{}; 15 std::array<u8, 0x10> uuid;
16 16
17 /// Constructs an invalid UUID.
18 constexpr UUID() = default; 17 constexpr UUID() = default;
19 18
20 /// Constructs a UUID from a reference to a 128 bit array. 19 /// Constructs a UUID from a reference to a 128 bit array.
@@ -34,14 +33,6 @@ struct UUID {
34 */ 33 */
35 explicit UUID(std::string_view uuid_string); 34 explicit UUID(std::string_view uuid_string);
36 35
37 ~UUID() = default;
38
39 constexpr UUID(const UUID&) noexcept = default;
40 constexpr UUID(UUID&&) noexcept = default;
41
42 constexpr UUID& operator=(const UUID&) noexcept = default;
43 constexpr UUID& operator=(UUID&&) noexcept = default;
44
45 /** 36 /**
46 * Returns whether the stored UUID is valid or not. 37 * Returns whether the stored UUID is valid or not.
47 * 38 *
@@ -121,6 +112,7 @@ struct UUID {
121 friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default; 112 friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default;
122}; 113};
123static_assert(sizeof(UUID) == 0x10, "UUID has incorrect size."); 114static_assert(sizeof(UUID) == 0x10, "UUID has incorrect size.");
115static_assert(std::is_trivial_v<UUID>);
124 116
125/// An invalid UUID. This UUID has all its bytes set to 0. 117/// An invalid UUID. This UUID has all its bytes set to 0.
126constexpr UUID InvalidUUID = {}; 118constexpr UUID InvalidUUID = {};
diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp
index 012fdc1e0..e14bf3e65 100644
--- a/src/common/wall_clock.cpp
+++ b/src/common/wall_clock.cpp
@@ -18,42 +18,40 @@ namespace Common {
18 18
19class StandardWallClock final : public WallClock { 19class StandardWallClock final : public WallClock {
20public: 20public:
21 explicit StandardWallClock() : start_time{SteadyClock::Now()} {} 21 explicit StandardWallClock() {}
22 22
23 std::chrono::nanoseconds GetTimeNS() const override { 23 std::chrono::nanoseconds GetTimeNS() const override {
24 return SteadyClock::Now() - start_time; 24 return std::chrono::duration_cast<std::chrono::nanoseconds>(
25 std::chrono::system_clock::now().time_since_epoch());
25 } 26 }
26 27
27 std::chrono::microseconds GetTimeUS() const override { 28 std::chrono::microseconds GetTimeUS() const override {
28 return static_cast<std::chrono::microseconds>(GetHostTicksElapsed() / NsToUsRatio::den); 29 return std::chrono::duration_cast<std::chrono::microseconds>(
30 std::chrono::system_clock::now().time_since_epoch());
29 } 31 }
30 32
31 std::chrono::milliseconds GetTimeMS() const override { 33 std::chrono::milliseconds GetTimeMS() const override {
32 return static_cast<std::chrono::milliseconds>(GetHostTicksElapsed() / NsToMsRatio::den); 34 return std::chrono::duration_cast<std::chrono::milliseconds>(
35 std::chrono::system_clock::now().time_since_epoch());
33 } 36 }
34 37
35 u64 GetCNTPCT() const override { 38 s64 GetCNTPCT() const override {
36 return GetHostTicksElapsed() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den; 39 return GetUptime() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
37 } 40 }
38 41
39 u64 GetGPUTick() const override { 42 s64 GetGPUTick() const override {
40 return GetHostTicksElapsed() * NsToGPUTickRatio::num / NsToGPUTickRatio::den; 43 return GetUptime() * NsToGPUTickRatio::num / NsToGPUTickRatio::den;
41 } 44 }
42 45
43 u64 GetHostTicksNow() const override { 46 s64 GetUptime() const override {
44 return static_cast<u64>(SteadyClock::Now().time_since_epoch().count()); 47 return std::chrono::duration_cast<std::chrono::nanoseconds>(
45 } 48 std::chrono::steady_clock::now().time_since_epoch())
46 49 .count();
47 u64 GetHostTicksElapsed() const override {
48 return static_cast<u64>(GetTimeNS().count());
49 } 50 }
50 51
51 bool IsNative() const override { 52 bool IsNative() const override {
52 return false; 53 return false;
53 } 54 }
54
55private:
56 SteadyClock::time_point start_time;
57}; 55};
58 56
59std::unique_ptr<WallClock> CreateOptimalClock() { 57std::unique_ptr<WallClock> CreateOptimalClock() {
diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h
index f45d3d8c5..3a0c43909 100644
--- a/src/common/wall_clock.h
+++ b/src/common/wall_clock.h
@@ -29,16 +29,13 @@ public:
29 virtual std::chrono::milliseconds GetTimeMS() const = 0; 29 virtual std::chrono::milliseconds GetTimeMS() const = 0;
30 30
31 /// @returns The guest CNTPCT ticks since the construction of this clock. 31 /// @returns The guest CNTPCT ticks since the construction of this clock.
32 virtual u64 GetCNTPCT() const = 0; 32 virtual s64 GetCNTPCT() const = 0;
33 33
34 /// @returns The guest GPU ticks since the construction of this clock. 34 /// @returns The guest GPU ticks since the construction of this clock.
35 virtual u64 GetGPUTick() const = 0; 35 virtual s64 GetGPUTick() const = 0;
36 36
37 /// @returns The raw host timer ticks since an indeterminate epoch. 37 /// @returns The raw host timer ticks since an indeterminate epoch.
38 virtual u64 GetHostTicksNow() const = 0; 38 virtual s64 GetUptime() const = 0;
39
40 /// @returns The raw host timer ticks since the construction of this clock.
41 virtual u64 GetHostTicksElapsed() const = 0;
42 39
43 /// @returns Whether the clock directly uses the host's hardware clock. 40 /// @returns Whether the clock directly uses the host's hardware clock.
44 virtual bool IsNative() const = 0; 41 virtual bool IsNative() const = 0;
diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp
index 7d2a26bd9..d2d27fafe 100644
--- a/src/common/x64/native_clock.cpp
+++ b/src/common/x64/native_clock.cpp
@@ -8,39 +8,35 @@
8namespace Common::X64 { 8namespace Common::X64 {
9 9
10NativeClock::NativeClock(u64 rdtsc_frequency_) 10NativeClock::NativeClock(u64 rdtsc_frequency_)
11 : start_ticks{FencedRDTSC()}, rdtsc_frequency{rdtsc_frequency_}, 11 : rdtsc_frequency{rdtsc_frequency_}, ns_rdtsc_factor{GetFixedPoint64Factor(NsRatio::den,
12 ns_rdtsc_factor{GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency)}, 12 rdtsc_frequency)},
13 us_rdtsc_factor{GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency)}, 13 us_rdtsc_factor{GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency)},
14 ms_rdtsc_factor{GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency)}, 14 ms_rdtsc_factor{GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency)},
15 cntpct_rdtsc_factor{GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency)}, 15 cntpct_rdtsc_factor{GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency)},
16 gputick_rdtsc_factor{GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency)} {} 16 gputick_rdtsc_factor{GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency)} {}
17 17
18std::chrono::nanoseconds NativeClock::GetTimeNS() const { 18std::chrono::nanoseconds NativeClock::GetTimeNS() const {
19 return std::chrono::nanoseconds{MultiplyHigh(GetHostTicksElapsed(), ns_rdtsc_factor)}; 19 return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_rdtsc_factor)};
20} 20}
21 21
22std::chrono::microseconds NativeClock::GetTimeUS() const { 22std::chrono::microseconds NativeClock::GetTimeUS() const {
23 return std::chrono::microseconds{MultiplyHigh(GetHostTicksElapsed(), us_rdtsc_factor)}; 23 return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_rdtsc_factor)};
24} 24}
25 25
26std::chrono::milliseconds NativeClock::GetTimeMS() const { 26std::chrono::milliseconds NativeClock::GetTimeMS() const {
27 return std::chrono::milliseconds{MultiplyHigh(GetHostTicksElapsed(), ms_rdtsc_factor)}; 27 return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_rdtsc_factor)};
28} 28}
29 29
30u64 NativeClock::GetCNTPCT() const { 30s64 NativeClock::GetCNTPCT() const {
31 return MultiplyHigh(GetHostTicksElapsed(), cntpct_rdtsc_factor); 31 return MultiplyHigh(GetUptime(), cntpct_rdtsc_factor);
32} 32}
33 33
34u64 NativeClock::GetGPUTick() const { 34s64 NativeClock::GetGPUTick() const {
35 return MultiplyHigh(GetHostTicksElapsed(), gputick_rdtsc_factor); 35 return MultiplyHigh(GetUptime(), gputick_rdtsc_factor);
36} 36}
37 37
38u64 NativeClock::GetHostTicksNow() const { 38s64 NativeClock::GetUptime() const {
39 return FencedRDTSC(); 39 return static_cast<s64>(FencedRDTSC());
40}
41
42u64 NativeClock::GetHostTicksElapsed() const {
43 return FencedRDTSC() - start_ticks;
44} 40}
45 41
46bool NativeClock::IsNative() const { 42bool NativeClock::IsNative() const {
diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h
index 334415eff..b2629b031 100644
--- a/src/common/x64/native_clock.h
+++ b/src/common/x64/native_clock.h
@@ -17,18 +17,15 @@ public:
17 17
18 std::chrono::milliseconds GetTimeMS() const override; 18 std::chrono::milliseconds GetTimeMS() const override;
19 19
20 u64 GetCNTPCT() const override; 20 s64 GetCNTPCT() const override;
21 21
22 u64 GetGPUTick() const override; 22 s64 GetGPUTick() const override;
23 23
24 u64 GetHostTicksNow() const override; 24 s64 GetUptime() const override;
25
26 u64 GetHostTicksElapsed() const override;
27 25
28 bool IsNative() const override; 26 bool IsNative() const override;
29 27
30private: 28private:
31 u64 start_ticks;
32 u64 rdtsc_frequency; 29 u64 rdtsc_frequency;
33 30
34 u64 ns_rdtsc_factor; 31 u64 ns_rdtsc_factor;
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 16ddb5e90..a630c257f 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -513,6 +513,24 @@ add_library(core STATIC
513 hle/service/glue/glue_manager.h 513 hle/service/glue/glue_manager.h
514 hle/service/glue/notif.cpp 514 hle/service/glue/notif.cpp
515 hle/service/glue/notif.h 515 hle/service/glue/notif.h
516 hle/service/glue/time/alarm_worker.cpp
517 hle/service/glue/time/alarm_worker.h
518 hle/service/glue/time/file_timestamp_worker.cpp
519 hle/service/glue/time/file_timestamp_worker.h
520 hle/service/glue/time/manager.cpp
521 hle/service/glue/time/manager.h
522 hle/service/glue/time/pm_state_change_handler.cpp
523 hle/service/glue/time/pm_state_change_handler.h
524 hle/service/glue/time/standard_steady_clock_resource.cpp
525 hle/service/glue/time/standard_steady_clock_resource.h
526 hle/service/glue/time/static.cpp
527 hle/service/glue/time/static.h
528 hle/service/glue/time/time_zone.cpp
529 hle/service/glue/time/time_zone.h
530 hle/service/glue/time/time_zone_binary.cpp
531 hle/service/glue/time/time_zone_binary.h
532 hle/service/glue/time/worker.cpp
533 hle/service/glue/time/worker.h
516 hle/service/grc/grc.cpp 534 hle/service/grc/grc.cpp
517 hle/service/grc/grc.h 535 hle/service/grc/grc.h
518 hle/service/hid/hid.cpp 536 hle/service/hid/hid.cpp
@@ -689,6 +707,46 @@ add_library(core STATIC
689 hle/service/prepo/prepo.h 707 hle/service/prepo/prepo.h
690 hle/service/psc/psc.cpp 708 hle/service/psc/psc.cpp
691 hle/service/psc/psc.h 709 hle/service/psc/psc.h
710 hle/service/psc/time/alarms.cpp
711 hle/service/psc/time/alarms.h
712 hle/service/psc/time/clocks/context_writers.cpp
713 hle/service/psc/time/clocks/context_writers.h
714 hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h
715 hle/service/psc/time/clocks/standard_local_system_clock_core.cpp
716 hle/service/psc/time/clocks/standard_local_system_clock_core.h
717 hle/service/psc/time/clocks/standard_network_system_clock_core.cpp
718 hle/service/psc/time/clocks/standard_network_system_clock_core.h
719 hle/service/psc/time/clocks/standard_steady_clock_core.cpp
720 hle/service/psc/time/clocks/standard_steady_clock_core.h
721 hle/service/psc/time/clocks/standard_user_system_clock_core.cpp
722 hle/service/psc/time/clocks/standard_user_system_clock_core.h
723 hle/service/psc/time/clocks/steady_clock_core.h
724 hle/service/psc/time/clocks/system_clock_core.cpp
725 hle/service/psc/time/clocks/system_clock_core.h
726 hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp
727 hle/service/psc/time/clocks/tick_based_steady_clock_core.h
728 hle/service/psc/time/common.cpp
729 hle/service/psc/time/common.h
730 hle/service/psc/time/errors.h
731 hle/service/psc/time/shared_memory.cpp
732 hle/service/psc/time/shared_memory.h
733 hle/service/psc/time/static.cpp
734 hle/service/psc/time/static.h
735 hle/service/psc/time/manager.h
736 hle/service/psc/time/power_state_service.cpp
737 hle/service/psc/time/power_state_service.h
738 hle/service/psc/time/service_manager.cpp
739 hle/service/psc/time/service_manager.h
740 hle/service/psc/time/steady_clock.cpp
741 hle/service/psc/time/steady_clock.h
742 hle/service/psc/time/system_clock.cpp
743 hle/service/psc/time/system_clock.h
744 hle/service/psc/time/time_zone.cpp
745 hle/service/psc/time/time_zone.h
746 hle/service/psc/time/time_zone_service.cpp
747 hle/service/psc/time/time_zone_service.h
748 hle/service/psc/time/power_state_request_manager.cpp
749 hle/service/psc/time/power_state_request_manager.h
692 hle/service/ptm/psm.cpp 750 hle/service/ptm/psm.cpp
693 hle/service/ptm/psm.h 751 hle/service/ptm/psm.h
694 hle/service/ptm/ptm.cpp 752 hle/service/ptm/ptm.cpp
@@ -756,40 +814,6 @@ add_library(core STATIC
756 hle/service/ssl/ssl.cpp 814 hle/service/ssl/ssl.cpp
757 hle/service/ssl/ssl.h 815 hle/service/ssl/ssl.h
758 hle/service/ssl/ssl_backend.h 816 hle/service/ssl/ssl_backend.h
759 hle/service/time/clock_types.h
760 hle/service/time/ephemeral_network_system_clock_context_writer.h
761 hle/service/time/ephemeral_network_system_clock_core.h
762 hle/service/time/errors.h
763 hle/service/time/local_system_clock_context_writer.h
764 hle/service/time/network_system_clock_context_writer.h
765 hle/service/time/standard_local_system_clock_core.h
766 hle/service/time/standard_network_system_clock_core.h
767 hle/service/time/standard_steady_clock_core.cpp
768 hle/service/time/standard_steady_clock_core.h
769 hle/service/time/standard_user_system_clock_core.cpp
770 hle/service/time/standard_user_system_clock_core.h
771 hle/service/time/steady_clock_core.h
772 hle/service/time/system_clock_context_update_callback.cpp
773 hle/service/time/system_clock_context_update_callback.h
774 hle/service/time/system_clock_core.cpp
775 hle/service/time/system_clock_core.h
776 hle/service/time/tick_based_steady_clock_core.cpp
777 hle/service/time/tick_based_steady_clock_core.h
778 hle/service/time/time.cpp
779 hle/service/time/time.h
780 hle/service/time/time_interface.cpp
781 hle/service/time/time_interface.h
782 hle/service/time/time_manager.cpp
783 hle/service/time/time_manager.h
784 hle/service/time/time_sharedmemory.cpp
785 hle/service/time/time_sharedmemory.h
786 hle/service/time/time_zone_content_manager.cpp
787 hle/service/time/time_zone_content_manager.h
788 hle/service/time/time_zone_manager.cpp
789 hle/service/time/time_zone_manager.h
790 hle/service/time/time_zone_service.cpp
791 hle/service/time/time_zone_service.h
792 hle/service/time/time_zone_types.h
793 hle/service/usb/usb.cpp 817 hle/service/usb/usb.cpp
794 hle/service/usb/usb.h 818 hle/service/usb/usb.h
795 hle/service/vi/display/vi_display.cpp 819 hle/service/vi/display/vi_display.cpp
@@ -870,7 +894,7 @@ endif()
870 894
871create_target_directory_groups(core) 895create_target_directory_groups(core)
872 896
873target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb) 897target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb tz)
874target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls RenderDoc::API) 898target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls RenderDoc::API)
875if (MINGW) 899if (MINGW)
876 target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY}) 900 target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY})
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 461eea9c8..33afc6049 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -39,9 +39,14 @@
39#include "core/hle/service/apm/apm_controller.h" 39#include "core/hle/service/apm/apm_controller.h"
40#include "core/hle/service/filesystem/filesystem.h" 40#include "core/hle/service/filesystem/filesystem.h"
41#include "core/hle/service/glue/glue_manager.h" 41#include "core/hle/service/glue/glue_manager.h"
42#include "core/hle/service/glue/time/static.h"
43#include "core/hle/service/psc/time/static.h"
44#include "core/hle/service/psc/time/steady_clock.h"
45#include "core/hle/service/psc/time/system_clock.h"
46#include "core/hle/service/psc/time/time_zone_service.h"
42#include "core/hle/service/service.h" 47#include "core/hle/service/service.h"
48#include "core/hle/service/set/system_settings_server.h"
43#include "core/hle/service/sm/sm.h" 49#include "core/hle/service/sm/sm.h"
44#include "core/hle/service/time/time_manager.h"
45#include "core/internal_network/network.h" 50#include "core/internal_network/network.h"
46#include "core/loader/loader.h" 51#include "core/loader/loader.h"
47#include "core/memory.h" 52#include "core/memory.h"
@@ -129,8 +134,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
129 134
130struct System::Impl { 135struct System::Impl {
131 explicit Impl(System& system) 136 explicit Impl(System& system)
132 : kernel{system}, fs_controller{system}, hid_core{}, room_network{}, cpu_manager{system}, 137 : kernel{system}, fs_controller{system}, hid_core{}, room_network{},
133 reporter{system}, applet_manager{system}, profile_manager{}, time_manager{system} {} 138 cpu_manager{system}, reporter{system}, applet_manager{system}, profile_manager{} {}
134 139
135 void Initialize(System& system) { 140 void Initialize(System& system) {
136 device_memory = std::make_unique<Core::DeviceMemory>(); 141 device_memory = std::make_unique<Core::DeviceMemory>();
@@ -142,8 +147,6 @@ struct System::Impl {
142 core_timing.SetMulticore(is_multicore); 147 core_timing.SetMulticore(is_multicore);
143 core_timing.Initialize([&system]() { system.RegisterHostThread(); }); 148 core_timing.Initialize([&system]() { system.RegisterHostThread(); });
144 149
145 RefreshTime();
146
147 // Create a default fs if one doesn't already exist. 150 // Create a default fs if one doesn't already exist.
148 if (virtual_filesystem == nullptr) { 151 if (virtual_filesystem == nullptr) {
149 virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); 152 virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
@@ -181,14 +184,57 @@ struct System::Impl {
181 Initialize(system); 184 Initialize(system);
182 } 185 }
183 186
184 void RefreshTime() { 187 void RefreshTime(System& system) {
188 if (!system.IsPoweredOn()) {
189 return;
190 }
191
192 auto settings_service =
193 system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys",
194 true);
195 auto static_service_a =
196 system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:a", true);
197
198 auto static_service_s =
199 system.ServiceManager().GetService<Service::PSC::Time::StaticService>("time:s", true);
200
201 std::shared_ptr<Service::PSC::Time::SystemClock> user_clock;
202 static_service_a->GetStandardUserSystemClock(user_clock);
203
204 std::shared_ptr<Service::PSC::Time::SystemClock> local_clock;
205 static_service_a->GetStandardLocalSystemClock(local_clock);
206
207 std::shared_ptr<Service::PSC::Time::SystemClock> network_clock;
208 static_service_s->GetStandardNetworkSystemClock(network_clock);
209
210 std::shared_ptr<Service::Glue::Time::TimeZoneService> timezone_service;
211 static_service_a->GetTimeZoneService(timezone_service);
212
213 Service::PSC::Time::LocationName name{};
214 auto new_name = Settings::GetTimeZoneString(Settings::values.time_zone_index.GetValue());
215 std::memcpy(name.name.data(), new_name.data(), std::min(name.name.size(), new_name.size()));
216
217 timezone_service->SetDeviceLocation(name);
218
219 u64 time_offset = 0;
220 if (Settings::values.custom_rtc_enabled) {
221 time_offset = Settings::values.custom_rtc_offset.GetValue();
222 }
223
185 const auto posix_time = std::chrono::system_clock::now().time_since_epoch(); 224 const auto posix_time = std::chrono::system_clock::now().time_since_epoch();
186 const auto current_time = 225 const u64 current_time =
187 std::chrono::duration_cast<std::chrono::seconds>(posix_time).count(); 226 +std::chrono::duration_cast<std::chrono::seconds>(posix_time).count();
188 Settings::values.custom_rtc_differential = 227 const u64 new_time = current_time + time_offset;
189 (Settings::values.custom_rtc_enabled ? Settings::values.custom_rtc.GetValue() 228
190 : current_time) - 229 Service::PSC::Time::SystemClockContext context{};
191 current_time; 230 settings_service->SetUserSystemClockContext(context);
231 user_clock->SetCurrentTime(new_time);
232
233 local_clock->SetCurrentTime(new_time);
234
235 network_clock->GetSystemClockContext(context);
236 settings_service->SetNetworkSystemClockContext(context);
237 network_clock->SetCurrentTime(new_time);
192 } 238 }
193 239
194 void Run() { 240 void Run() {
@@ -264,9 +310,6 @@ struct System::Impl {
264 service_manager = std::make_shared<Service::SM::ServiceManager>(kernel); 310 service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
265 services = std::make_unique<Service::Services>(service_manager, system); 311 services = std::make_unique<Service::Services>(service_manager, system);
266 312
267 // Initialize time manager, which must happen after kernel is created
268 time_manager.Initialize();
269
270 is_powered_on = true; 313 is_powered_on = true;
271 exit_locked = false; 314 exit_locked = false;
272 exit_requested = false; 315 exit_requested = false;
@@ -416,7 +459,6 @@ struct System::Impl {
416 fs_controller.Reset(); 459 fs_controller.Reset();
417 cheat_engine.reset(); 460 cheat_engine.reset();
418 telemetry_session.reset(); 461 telemetry_session.reset();
419 time_manager.Shutdown();
420 core_timing.ClearPendingEvents(); 462 core_timing.ClearPendingEvents();
421 app_loader.reset(); 463 app_loader.reset();
422 audio_core.reset(); 464 audio_core.reset();
@@ -532,7 +574,6 @@ struct System::Impl {
532 /// Service State 574 /// Service State
533 Service::Glue::ARPManager arp_manager; 575 Service::Glue::ARPManager arp_manager;
534 Service::Account::ProfileManager profile_manager; 576 Service::Account::ProfileManager profile_manager;
535 Service::Time::TimeManager time_manager;
536 577
537 /// Service manager 578 /// Service manager
538 std::shared_ptr<Service::SM::ServiceManager> service_manager; 579 std::shared_ptr<Service::SM::ServiceManager> service_manager;
@@ -901,14 +942,6 @@ const Service::Account::ProfileManager& System::GetProfileManager() const {
901 return impl->profile_manager; 942 return impl->profile_manager;
902} 943}
903 944
904Service::Time::TimeManager& System::GetTimeManager() {
905 return impl->time_manager;
906}
907
908const Service::Time::TimeManager& System::GetTimeManager() const {
909 return impl->time_manager;
910}
911
912void System::SetExitLocked(bool locked) { 945void System::SetExitLocked(bool locked) {
913 impl->exit_locked = locked; 946 impl->exit_locked = locked;
914} 947}
@@ -1020,13 +1053,9 @@ void System::Exit() {
1020} 1053}
1021 1054
1022void System::ApplySettings() { 1055void System::ApplySettings() {
1023 impl->RefreshTime(); 1056 impl->RefreshTime(*this);
1024 1057
1025 if (IsPoweredOn()) { 1058 if (IsPoweredOn()) {
1026 if (Settings::values.custom_rtc_enabled) {
1027 const s64 posix_time{Settings::values.custom_rtc.GetValue()};
1028 GetTimeManager().UpdateLocalSystemClockTime(posix_time);
1029 }
1030 Renderer().RefreshBaseSettings(); 1059 Renderer().RefreshBaseSettings();
1031 } 1060 }
1032} 1061}
diff --git a/src/core/core.h b/src/core/core.h
index ba5add0dc..7d49e5028 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -72,10 +72,6 @@ namespace SM {
72class ServiceManager; 72class ServiceManager;
73} // namespace SM 73} // namespace SM
74 74
75namespace Time {
76class TimeManager;
77} // namespace Time
78
79} // namespace Service 75} // namespace Service
80 76
81namespace Tegra { 77namespace Tegra {
@@ -377,9 +373,6 @@ public:
377 [[nodiscard]] Service::Account::ProfileManager& GetProfileManager(); 373 [[nodiscard]] Service::Account::ProfileManager& GetProfileManager();
378 [[nodiscard]] const Service::Account::ProfileManager& GetProfileManager() const; 374 [[nodiscard]] const Service::Account::ProfileManager& GetProfileManager() const;
379 375
380 [[nodiscard]] Service::Time::TimeManager& GetTimeManager();
381 [[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const;
382
383 [[nodiscard]] Core::Debugger& GetDebugger(); 376 [[nodiscard]] Core::Debugger& GetDebugger();
384 [[nodiscard]] const Core::Debugger& GetDebugger() const; 377 [[nodiscard]] const Core::Debugger& GetDebugger() const;
385 378
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index fc536413b..1abfa920c 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -157,7 +157,7 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
157 } 157 }
158 } 158 }
159 159
160 for (auto h : to_remove) { 160 for (auto& h : to_remove) {
161 event_queue.erase(h); 161 event_queue.erase(h);
162 } 162 }
163 163
diff --git a/src/core/file_sys/system_archive/system_archive.cpp b/src/core/file_sys/system_archive/system_archive.cpp
index 6abac793b..b53eef877 100644
--- a/src/core/file_sys/system_archive/system_archive.cpp
+++ b/src/core/file_sys/system_archive/system_archive.cpp
@@ -67,25 +67,29 @@ constexpr std::array<SystemArchiveDescriptor, SYSTEM_ARCHIVE_COUNT> SYSTEM_ARCHI
67}}; 67}};
68 68
69VirtualFile SynthesizeSystemArchive(const u64 title_id) { 69VirtualFile SynthesizeSystemArchive(const u64 title_id) {
70 if (title_id < SYSTEM_ARCHIVES.front().title_id || title_id > SYSTEM_ARCHIVES.back().title_id) 70 if (title_id < SYSTEM_ARCHIVES.front().title_id || title_id > SYSTEM_ARCHIVES.back().title_id) {
71 return nullptr; 71 return nullptr;
72 }
72 73
73 const auto& desc = SYSTEM_ARCHIVES[title_id - SYSTEM_ARCHIVE_BASE_TITLE_ID]; 74 const auto& desc = SYSTEM_ARCHIVES[title_id - SYSTEM_ARCHIVE_BASE_TITLE_ID];
74 75
75 LOG_INFO(Service_FS, "Synthesizing system archive '{}' (0x{:016X}).", desc.name, desc.title_id); 76 LOG_INFO(Service_FS, "Synthesizing system archive '{}' (0x{:016X}).", desc.name, desc.title_id);
76 77
77 if (desc.supplier == nullptr) 78 if (desc.supplier == nullptr) {
78 return nullptr; 79 return nullptr;
80 }
79 81
80 const auto dir = desc.supplier(); 82 const auto dir = desc.supplier();
81 83
82 if (dir == nullptr) 84 if (dir == nullptr) {
83 return nullptr; 85 return nullptr;
86 }
84 87
85 const auto romfs = CreateRomFS(dir); 88 const auto romfs = CreateRomFS(dir);
86 89
87 if (romfs == nullptr) 90 if (romfs == nullptr) {
88 return nullptr; 91 return nullptr;
92 }
89 93
90 LOG_INFO(Service_FS, " - System archive generation successful!"); 94 LOG_INFO(Service_FS, " - System archive generation successful!");
91 return romfs; 95 return romfs;
diff --git a/src/core/file_sys/system_archive/time_zone_binary.cpp b/src/core/file_sys/system_archive/time_zone_binary.cpp
index 7c17bbefa..d4d2eae76 100644
--- a/src/core/file_sys/system_archive/time_zone_binary.cpp
+++ b/src/core/file_sys/system_archive/time_zone_binary.cpp
@@ -6,7 +6,6 @@
6#include "common/swap.h" 6#include "common/swap.h"
7#include "core/file_sys/system_archive/time_zone_binary.h" 7#include "core/file_sys/system_archive/time_zone_binary.h"
8#include "core/file_sys/vfs_vector.h" 8#include "core/file_sys/vfs_vector.h"
9#include "core/hle/service/time/time_zone_types.h"
10 9
11#include "nx_tzdb.h" 10#include "nx_tzdb.h"
12 11
diff --git a/src/core/hle/service/caps/caps_manager.cpp b/src/core/hle/service/caps/caps_manager.cpp
index 261fc204c..e3b8ecf3e 100644
--- a/src/core/hle/service/caps/caps_manager.cpp
+++ b/src/core/hle/service/caps/caps_manager.cpp
@@ -10,8 +10,10 @@
10#include "core/core.h" 10#include "core/core.h"
11#include "core/hle/service/caps/caps_manager.h" 11#include "core/hle/service/caps/caps_manager.h"
12#include "core/hle/service/caps/caps_result.h" 12#include "core/hle/service/caps/caps_result.h"
13#include "core/hle/service/time/time_manager.h" 13#include "core/hle/service/glue/time/static.h"
14#include "core/hle/service/time/time_zone_content_manager.h" 14#include "core/hle/service/psc/time/system_clock.h"
15#include "core/hle/service/service.h"
16#include "core/hle/service/sm/sm.h"
15 17
16namespace Service::Capture { 18namespace Service::Capture {
17 19
@@ -239,10 +241,15 @@ Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry,
239 const ApplicationData& app_data, std::span<const u8> image_data, 241 const ApplicationData& app_data, std::span<const u8> image_data,
240 u64 aruid) { 242 u64 aruid) {
241 const u64 title_id = system.GetApplicationProcessProgramID(); 243 const u64 title_id = system.GetApplicationProcessProgramID();
242 const auto& user_clock = system.GetTimeManager().GetStandardUserSystemClockCore(); 244
245 auto static_service =
246 system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);
247
248 std::shared_ptr<Service::PSC::Time::SystemClock> user_clock{};
249 static_service->GetStandardUserSystemClock(user_clock);
243 250
244 s64 posix_time{}; 251 s64 posix_time{};
245 Result result = user_clock.GetCurrentTime(system, posix_time); 252 auto result = user_clock->GetCurrentTime(posix_time);
246 253
247 if (result.IsError()) { 254 if (result.IsError()) {
248 return result; 255 return result;
@@ -257,10 +264,14 @@ Result AlbumManager::SaveEditedScreenShot(ApplicationAlbumEntry& out_entry,
257 const ScreenShotAttribute& attribute, 264 const ScreenShotAttribute& attribute,
258 const AlbumFileId& file_id, 265 const AlbumFileId& file_id,
259 std::span<const u8> image_data) { 266 std::span<const u8> image_data) {
260 const auto& user_clock = system.GetTimeManager().GetStandardUserSystemClockCore(); 267 auto static_service =
268 system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);
269
270 std::shared_ptr<Service::PSC::Time::SystemClock> user_clock{};
271 static_service->GetStandardUserSystemClock(user_clock);
261 272
262 s64 posix_time{}; 273 s64 posix_time{};
263 Result result = user_clock.GetCurrentTime(system, posix_time); 274 auto result = user_clock->GetCurrentTime(posix_time);
264 275
265 if (result.IsError()) { 276 if (result.IsError()) {
266 return result; 277 return result;
@@ -455,19 +466,23 @@ Result AlbumManager::SaveImage(ApplicationAlbumEntry& out_entry, std::span<const
455} 466}
456 467
457AlbumFileDateTime AlbumManager::ConvertToAlbumDateTime(u64 posix_time) const { 468AlbumFileDateTime AlbumManager::ConvertToAlbumDateTime(u64 posix_time) const {
458 Time::TimeZone::CalendarInfo calendar_date{}; 469 auto static_service =
459 const auto& time_zone_manager = 470 system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);
460 system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager(); 471
472 std::shared_ptr<Service::Glue::Time::TimeZoneService> timezone_service{};
473 static_service->GetTimeZoneService(timezone_service);
461 474
462 time_zone_manager.ToCalendarTimeWithMyRules(posix_time, calendar_date); 475 Service::PSC::Time::CalendarTime calendar_time{};
476 Service::PSC::Time::CalendarAdditionalInfo additional_info{};
477 timezone_service->ToCalendarTimeWithMyRule(calendar_time, additional_info, posix_time);
463 478
464 return { 479 return {
465 .year = calendar_date.time.year, 480 .year = calendar_time.year,
466 .month = calendar_date.time.month, 481 .month = calendar_time.month,
467 .day = calendar_date.time.day, 482 .day = calendar_time.day,
468 .hour = calendar_date.time.hour, 483 .hour = calendar_time.hour,
469 .minute = calendar_date.time.minute, 484 .minute = calendar_time.minute,
470 .second = calendar_date.time.second, 485 .second = calendar_time.second,
471 .unique_id = 0, 486 .unique_id = 0,
472 }; 487 };
473} 488}
diff --git a/src/core/hle/service/glue/glue.cpp b/src/core/hle/service/glue/glue.cpp
index 993c3d21d..10376bfac 100644
--- a/src/core/hle/service/glue/glue.cpp
+++ b/src/core/hle/service/glue/glue.cpp
@@ -8,6 +8,9 @@
8#include "core/hle/service/glue/ectx.h" 8#include "core/hle/service/glue/ectx.h"
9#include "core/hle/service/glue/glue.h" 9#include "core/hle/service/glue/glue.h"
10#include "core/hle/service/glue/notif.h" 10#include "core/hle/service/glue/notif.h"
11#include "core/hle/service/glue/time/manager.h"
12#include "core/hle/service/glue/time/static.h"
13#include "core/hle/service/psc/time/common.h"
11#include "core/hle/service/server_manager.h" 14#include "core/hle/service/server_manager.h"
12 15
13namespace Service::Glue { 16namespace Service::Glue {
@@ -31,6 +34,22 @@ void LoopProcess(Core::System& system) {
31 // Notification Services for application 34 // Notification Services for application
32 server_manager->RegisterNamedService("notif:a", std::make_shared<NOTIF_A>(system)); 35 server_manager->RegisterNamedService("notif:a", std::make_shared<NOTIF_A>(system));
33 36
37 // Time
38 auto time = std::make_shared<Time::TimeManager>(system);
39
40 server_manager->RegisterNamedService(
41 "time:u",
42 std::make_shared<Time::StaticService>(
43 system, Service::PSC::Time::StaticServiceSetupInfo{0, 0, 0, 0, 0, 0}, time, "time:u"));
44 server_manager->RegisterNamedService(
45 "time:a",
46 std::make_shared<Time::StaticService>(
47 system, Service::PSC::Time::StaticServiceSetupInfo{1, 1, 0, 1, 0, 0}, time, "time:a"));
48 server_manager->RegisterNamedService(
49 "time:r",
50 std::make_shared<Time::StaticService>(
51 system, Service::PSC::Time::StaticServiceSetupInfo{0, 0, 0, 0, 1, 0}, time, "time:r"));
52
34 ServerManager::RunServer(std::move(server_manager)); 53 ServerManager::RunServer(std::move(server_manager));
35} 54}
36 55
diff --git a/src/core/hle/service/glue/time/alarm_worker.cpp b/src/core/hle/service/glue/time/alarm_worker.cpp
new file mode 100644
index 000000000..f549ed00a
--- /dev/null
+++ b/src/core/hle/service/glue/time/alarm_worker.cpp
@@ -0,0 +1,82 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/core_timing.h"
6#include "core/hle/kernel/svc.h"
7#include "core/hle/service/glue/time/alarm_worker.h"
8#include "core/hle/service/psc/time/service_manager.h"
9#include "core/hle/service/sm/sm.h"
10
11namespace Service::Glue::Time {
12
13AlarmWorker::AlarmWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource)
14 : m_system{system}, m_ctx{system, "Glue:AlarmWorker"}, m_steady_clock_resource{
15 steady_clock_resource} {}
16
17AlarmWorker::~AlarmWorker() {
18 m_system.CoreTiming().UnscheduleEvent(m_timer_timing_event);
19
20 m_ctx.CloseEvent(m_timer_event);
21}
22
23void AlarmWorker::Initialize(std::shared_ptr<Service::PSC::Time::ServiceManager> time_m) {
24 m_time_m = std::move(time_m);
25
26 m_timer_event = m_ctx.CreateEvent("Glue:AlarmWorker:TimerEvent");
27 m_timer_timing_event = Core::Timing::CreateEvent(
28 "Glue:AlarmWorker::AlarmTimer",
29 [this](s64 time,
30 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
31 m_timer_event->Signal();
32 return std::nullopt;
33 });
34
35 AttachToClosestAlarmEvent();
36}
37
38bool AlarmWorker::GetClosestAlarmInfo(Service::PSC::Time::AlarmInfo& out_alarm_info,
39 s64& out_time) {
40 bool is_valid{};
41 Service::PSC::Time::AlarmInfo alarm_info{};
42 s64 closest_time{};
43
44 auto res = m_time_m->GetClosestAlarmInfo(is_valid, alarm_info, closest_time);
45 ASSERT(res == ResultSuccess);
46
47 if (is_valid) {
48 out_alarm_info = alarm_info;
49 out_time = closest_time;
50 }
51
52 return is_valid;
53}
54
55void AlarmWorker::OnPowerStateChanged() {
56 Service::PSC::Time::AlarmInfo closest_alarm_info{};
57 s64 closest_time{};
58 if (!GetClosestAlarmInfo(closest_alarm_info, closest_time)) {
59 m_system.CoreTiming().UnscheduleEvent(m_timer_timing_event);
60 m_timer_event->Clear();
61 return;
62 }
63
64 if (closest_alarm_info.alert_time <= closest_time) {
65 m_time_m->CheckAndSignalAlarms();
66 } else {
67 auto next_time{closest_alarm_info.alert_time - closest_time};
68
69 m_system.CoreTiming().UnscheduleEvent(m_timer_timing_event);
70 m_timer_event->Clear();
71
72 m_system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds(next_time),
73 m_timer_timing_event);
74 }
75}
76
77Result AlarmWorker::AttachToClosestAlarmEvent() {
78 m_time_m->GetClosestAlarmUpdatedEvent(&m_event);
79 R_SUCCEED();
80}
81
82} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/alarm_worker.h b/src/core/hle/service/glue/time/alarm_worker.h
new file mode 100644
index 000000000..f269cffdb
--- /dev/null
+++ b/src/core/hle/service/glue/time/alarm_worker.h
@@ -0,0 +1,53 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hle/kernel/k_event.h"
8#include "core/hle/service/kernel_helpers.h"
9#include "core/hle/service/psc/time/common.h"
10
11namespace Core {
12class System;
13}
14
15namespace Service::PSC::Time {
16class ServiceManager;
17}
18
19namespace Service::Glue::Time {
20class StandardSteadyClockResource;
21
22class AlarmWorker {
23public:
24 explicit AlarmWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource);
25 ~AlarmWorker();
26
27 void Initialize(std::shared_ptr<Service::PSC::Time::ServiceManager> time_m);
28
29 Kernel::KEvent& GetEvent() {
30 return *m_event;
31 }
32
33 Kernel::KEvent& GetTimerEvent() {
34 return *m_timer_event;
35 }
36
37 void OnPowerStateChanged();
38
39private:
40 bool GetClosestAlarmInfo(Service::PSC::Time::AlarmInfo& out_alarm_info, s64& out_time);
41 Result AttachToClosestAlarmEvent();
42
43 Core::System& m_system;
44 KernelHelpers::ServiceContext m_ctx;
45 std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m;
46
47 Kernel::KEvent* m_event{};
48 Kernel::KEvent* m_timer_event{};
49 std::shared_ptr<Core::Timing::EventType> m_timer_timing_event;
50 StandardSteadyClockResource& m_steady_clock_resource;
51};
52
53} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/file_timestamp_worker.cpp b/src/core/hle/service/glue/time/file_timestamp_worker.cpp
new file mode 100644
index 000000000..5a6309549
--- /dev/null
+++ b/src/core/hle/service/glue/time/file_timestamp_worker.cpp
@@ -0,0 +1,23 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/glue/time/file_timestamp_worker.h"
5#include "core/hle/service/psc/time/common.h"
6#include "core/hle/service/psc/time/system_clock.h"
7#include "core/hle/service/psc/time/time_zone_service.h"
8
9namespace Service::Glue::Time {
10
11void FileTimestampWorker::SetFilesystemPosixTime() {
12 s64 time{};
13 Service::PSC::Time::CalendarTime calendar_time{};
14 Service::PSC::Time::CalendarAdditionalInfo additional_info{};
15
16 if (m_initialized && m_system_clock->GetCurrentTime(time) == ResultSuccess &&
17 m_time_zone->ToCalendarTimeWithMyRule(calendar_time, additional_info, time) ==
18 ResultSuccess) {
19 // TODO IFileSystemProxy::SetCurrentPosixTime
20 }
21}
22
23} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/file_timestamp_worker.h b/src/core/hle/service/glue/time/file_timestamp_worker.h
new file mode 100644
index 000000000..5f8b9b049
--- /dev/null
+++ b/src/core/hle/service/glue/time/file_timestamp_worker.h
@@ -0,0 +1,28 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7
8#include "common/common_types.h"
9
10namespace Service::PSC::Time {
11class SystemClock;
12class TimeZoneService;
13} // namespace Service::PSC::Time
14
15namespace Service::Glue::Time {
16
17class FileTimestampWorker {
18public:
19 FileTimestampWorker() = default;
20
21 void SetFilesystemPosixTime();
22
23 std::shared_ptr<Service::PSC::Time::SystemClock> m_system_clock{};
24 std::shared_ptr<Service::PSC::Time::TimeZoneService> m_time_zone{};
25 bool m_initialized{};
26};
27
28} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/manager.cpp b/src/core/hle/service/glue/time/manager.cpp
new file mode 100644
index 000000000..6423e5089
--- /dev/null
+++ b/src/core/hle/service/glue/time/manager.cpp
@@ -0,0 +1,277 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <chrono>
5
6#include "core/core.h"
7#include "core/core_timing.h"
8
9#include "common/settings.h"
10#include "common/time_zone.h"
11#include "core/file_sys/vfs.h"
12#include "core/hle/kernel/svc.h"
13#include "core/hle/service/glue/time/manager.h"
14#include "core/hle/service/glue/time/time_zone_binary.h"
15#include "core/hle/service/psc/time/service_manager.h"
16#include "core/hle/service/psc/time/static.h"
17#include "core/hle/service/psc/time/system_clock.h"
18#include "core/hle/service/psc/time/time_zone_service.h"
19#include "core/hle/service/set/system_settings_server.h"
20#include "core/hle/service/sm/sm.h"
21
22namespace Service::Glue::Time {
23namespace {
24
25template <typename T>
26T GetSettingsItemValue(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys,
27 const char* category, const char* name) {
28 std::vector<u8> interval_buf;
29 auto res = set_sys->GetSettingsItemValue(interval_buf, category, name);
30 ASSERT(res == ResultSuccess);
31
32 T v{};
33 std::memcpy(&v, interval_buf.data(), sizeof(T));
34 return v;
35}
36
37s64 CalendarTimeToEpoch(Service::PSC::Time::CalendarTime calendar) {
38 constexpr auto is_leap = [](s32 year) -> bool {
39 return (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0));
40 };
41 constexpr std::array<s32, 12> MonthStartDayOfYear{
42 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
43 };
44
45 s16 month_s16{calendar.month};
46 s8 month{static_cast<s8>(((month_s16 * 43) & ~std::numeric_limits<s16>::max()) +
47 ((month_s16 * 43) >> 9))};
48 s8 month_index{static_cast<s8>(calendar.month - 12 * month)};
49 if (month_index == 0) {
50 month_index = 12;
51 }
52 s32 year{(month + calendar.year) - !month_index};
53 s32 v8{year >= 0 ? year : year + 3};
54
55 s64 days_since_epoch = calendar.day + MonthStartDayOfYear[month_index - 1];
56 days_since_epoch += (year * 365) + (v8 / 4) - (year / 100) + (year / 400) - 365;
57
58 if (month_index <= 2 && is_leap(year)) {
59 days_since_epoch--;
60 }
61 auto epoch_s{((24ll * days_since_epoch + calendar.hour) * 60ll + calendar.minute) * 60ll +
62 calendar.second};
63 return epoch_s - 62135683200ll;
64}
65
66s64 GetEpochTimeFromInitialYear(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys) {
67 Service::PSC::Time::CalendarTime calendar{
68 .year = GetSettingsItemValue<s16>(set_sys, "time", "standard_user_clock_initial_year"),
69 .month = 1,
70 .day = 1,
71 .hour = 0,
72 .minute = 0,
73 .second = 0,
74 };
75 return CalendarTimeToEpoch(calendar);
76}
77
78Service::PSC::Time::LocationName GetTimeZoneString(Service::PSC::Time::LocationName& in_name) {
79 auto configured_zone = Settings::GetTimeZoneString(Settings::values.time_zone_index.GetValue());
80
81 Service::PSC::Time::LocationName configured_name{};
82 std::memcpy(configured_name.name.data(), configured_zone.data(),
83 std::min(configured_name.name.size(), configured_zone.size()));
84
85 if (!IsTimeZoneBinaryValid(configured_name)) {
86 configured_zone = Common::TimeZone::FindSystemTimeZone();
87 configured_name = {};
88 std::memcpy(configured_name.name.data(), configured_zone.data(),
89 std::min(configured_name.name.size(), configured_zone.size()));
90 }
91
92 ASSERT_MSG(IsTimeZoneBinaryValid(configured_name), "Invalid time zone {}!",
93 configured_name.name.data());
94
95 return configured_name;
96}
97
98} // namespace
99
100TimeManager::TimeManager(Core::System& system)
101 : m_steady_clock_resource{system}, m_worker{system, m_steady_clock_resource,
102 m_file_timestamp_worker} {
103 m_time_m =
104 system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true);
105
106 auto res = m_time_m->GetStaticServiceAsServiceManager(m_time_sm);
107 ASSERT(res == ResultSuccess);
108
109 m_set_sys =
110 system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
111
112 res = MountTimeZoneBinary(system);
113 ASSERT(res == ResultSuccess);
114
115 m_worker.Initialize(m_time_sm, m_set_sys);
116
117 res = m_time_sm->GetStandardUserSystemClock(m_file_timestamp_worker.m_system_clock);
118 ASSERT(res == ResultSuccess);
119
120 res = m_time_sm->GetTimeZoneService(m_file_timestamp_worker.m_time_zone);
121 ASSERT(res == ResultSuccess);
122
123 res = SetupStandardSteadyClockCore();
124 ASSERT(res == ResultSuccess);
125
126 Service::PSC::Time::SystemClockContext user_clock_context{};
127 res = m_set_sys->GetUserSystemClockContext(user_clock_context);
128 ASSERT(res == ResultSuccess);
129
130 // TODO the local clock should initialise with this epoch time, and be updated somewhere else on
131 // first boot to update it, but I haven't been able to find that point (likely via ntc's auto
132 // correct as it's defaulted to be enabled). So to get a time that isn't stuck in the past for
133 // first boot, grab the current real seconds.
134 auto epoch_time{GetEpochTimeFromInitialYear(m_set_sys)};
135 if (user_clock_context == Service::PSC::Time::SystemClockContext{}) {
136 m_steady_clock_resource.GetRtcTimeInSeconds(epoch_time);
137 }
138
139 res = m_time_m->SetupStandardLocalSystemClockCore(user_clock_context, epoch_time);
140 ASSERT(res == ResultSuccess);
141
142 Service::PSC::Time::SystemClockContext network_clock_context{};
143 res = m_set_sys->GetNetworkSystemClockContext(network_clock_context);
144 ASSERT(res == ResultSuccess);
145
146 auto network_accuracy_m{GetSettingsItemValue<s32>(
147 m_set_sys, "time", "standard_network_clock_sufficient_accuracy_minutes")};
148 auto one_minute_ns{
149 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::minutes(1)).count()};
150 s64 network_accuracy_ns{network_accuracy_m * one_minute_ns};
151
152 res = m_time_m->SetupStandardNetworkSystemClockCore(network_clock_context, network_accuracy_ns);
153 ASSERT(res == ResultSuccess);
154
155 bool is_automatic_correction_enabled{};
156 res = m_set_sys->IsUserSystemClockAutomaticCorrectionEnabled(is_automatic_correction_enabled);
157 ASSERT(res == ResultSuccess);
158
159 Service::PSC::Time::SteadyClockTimePoint automatic_correction_time_point{};
160 res = m_set_sys->GetUserSystemClockAutomaticCorrectionUpdatedTime(
161 automatic_correction_time_point);
162 ASSERT(res == ResultSuccess);
163
164 res = m_time_m->SetupStandardUserSystemClockCore(automatic_correction_time_point,
165 is_automatic_correction_enabled);
166 ASSERT(res == ResultSuccess);
167
168 res = m_time_m->SetupEphemeralNetworkSystemClockCore();
169 ASSERT(res == ResultSuccess);
170
171 res = SetupTimeZoneServiceCore();
172 ASSERT(res == ResultSuccess);
173
174 s64 rtc_time_s{};
175 res = m_steady_clock_resource.GetRtcTimeInSeconds(rtc_time_s);
176 ASSERT(res == ResultSuccess);
177
178 // TODO system report "launch"
179 // "rtc_reset" = m_steady_clock_resource.m_rtc_reset
180 // "rtc_value" = rtc_time_s
181
182 m_worker.StartThread();
183
184 m_file_timestamp_worker.m_initialized = true;
185
186 s64 system_clock_time{};
187 if (m_file_timestamp_worker.m_system_clock->GetCurrentTime(system_clock_time) ==
188 ResultSuccess) {
189 Service::PSC::Time::CalendarTime calendar_time{};
190 Service::PSC::Time::CalendarAdditionalInfo calendar_additional{};
191 if (m_file_timestamp_worker.m_time_zone->ToCalendarTimeWithMyRule(
192 calendar_time, calendar_additional, system_clock_time) == ResultSuccess) {
193 // TODO IFileSystemProxy::SetCurrentPosixTime(system_clock_time,
194 // calendar_additional.ut_offset)
195 }
196 }
197}
198
199Result TimeManager::SetupStandardSteadyClockCore() {
200 Common::UUID external_clock_source_id{};
201 auto res = m_set_sys->GetExternalSteadyClockSourceId(external_clock_source_id);
202 ASSERT(res == ResultSuccess);
203
204 s64 external_steady_clock_internal_offset_s{};
205 res = m_set_sys->GetExternalSteadyClockInternalOffset(external_steady_clock_internal_offset_s);
206 ASSERT(res == ResultSuccess);
207
208 auto one_second_ns{
209 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
210 s64 external_steady_clock_internal_offset_ns{external_steady_clock_internal_offset_s *
211 one_second_ns};
212
213 s32 standard_steady_clock_test_offset_m{
214 GetSettingsItemValue<s32>(m_set_sys, "time", "standard_steady_clock_test_offset_minutes")};
215 auto one_minute_ns{
216 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::minutes(1)).count()};
217 s64 standard_steady_clock_test_offset_ns{standard_steady_clock_test_offset_m * one_minute_ns};
218
219 auto reset_detected = m_steady_clock_resource.GetResetDetected();
220 if (reset_detected) {
221 external_clock_source_id = {};
222 }
223
224 Common::UUID clock_source_id{};
225 m_steady_clock_resource.Initialize(&clock_source_id, &external_clock_source_id);
226
227 if (clock_source_id != external_clock_source_id) {
228 m_set_sys->SetExternalSteadyClockSourceId(clock_source_id);
229 }
230
231 res = m_time_m->SetupStandardSteadyClockCore(clock_source_id, m_steady_clock_resource.GetTime(),
232 external_steady_clock_internal_offset_ns,
233 standard_steady_clock_test_offset_ns,
234 reset_detected);
235 ASSERT(res == ResultSuccess);
236 R_SUCCEED();
237}
238
239Result TimeManager::SetupTimeZoneServiceCore() {
240 Service::PSC::Time::LocationName name{};
241 auto res = m_set_sys->GetDeviceTimeZoneLocationName(name);
242 ASSERT(res == ResultSuccess);
243
244 auto configured_zone = GetTimeZoneString(name);
245
246 if (configured_zone.name != name.name) {
247 m_set_sys->SetDeviceTimeZoneLocationName(configured_zone);
248 name = configured_zone;
249
250 std::shared_ptr<Service::PSC::Time::SystemClock> local_clock;
251 m_time_sm->GetStandardLocalSystemClock(local_clock);
252 Service::PSC::Time::SystemClockContext context{};
253 local_clock->GetSystemClockContext(context);
254 m_set_sys->SetDeviceTimeZoneLocationUpdatedTime(context.steady_time_point);
255 }
256
257 Service::PSC::Time::SteadyClockTimePoint time_point{};
258 res = m_set_sys->GetDeviceTimeZoneLocationUpdatedTime(time_point);
259 ASSERT(res == ResultSuccess);
260
261 auto location_count = GetTimeZoneCount();
262 Service::PSC::Time::RuleVersion rule_version{};
263 GetTimeZoneVersion(rule_version);
264
265 std::span<const u8> rule_buffer{};
266 size_t rule_size{};
267 res = GetTimeZoneRule(rule_buffer, rule_size, name);
268 ASSERT(res == ResultSuccess);
269
270 res = m_time_m->SetupTimeZoneServiceCore(name, time_point, rule_version, location_count,
271 rule_buffer);
272 ASSERT(res == ResultSuccess);
273
274 R_SUCCEED();
275}
276
277} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/manager.h b/src/core/hle/service/glue/time/manager.h
new file mode 100644
index 000000000..a46ec6364
--- /dev/null
+++ b/src/core/hle/service/glue/time/manager.h
@@ -0,0 +1,42 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <functional>
7#include <string>
8
9#include "common/common_types.h"
10#include "core/file_sys/vfs_types.h"
11#include "core/hle/service/glue/time/file_timestamp_worker.h"
12#include "core/hle/service/glue/time/standard_steady_clock_resource.h"
13#include "core/hle/service/glue/time/worker.h"
14#include "core/hle/service/service.h"
15
16namespace Core {
17class System;
18}
19
20namespace Service::PSC::Time {
21class ServiceManager;
22class StaticService;
23} // namespace Service::PSC::Time
24
25namespace Service::Glue::Time {
26class TimeManager {
27public:
28 explicit TimeManager(Core::System& system);
29
30 std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
31
32 std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m{};
33 std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm{};
34 StandardSteadyClockResource m_steady_clock_resource;
35 FileTimestampWorker m_file_timestamp_worker;
36 TimeWorker m_worker;
37
38private:
39 Result SetupStandardSteadyClockCore();
40 Result SetupTimeZoneServiceCore();
41};
42} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/pm_state_change_handler.cpp b/src/core/hle/service/glue/time/pm_state_change_handler.cpp
new file mode 100644
index 000000000..7470fb225
--- /dev/null
+++ b/src/core/hle/service/glue/time/pm_state_change_handler.cpp
@@ -0,0 +1,13 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/glue/time/pm_state_change_handler.h"
5
6namespace Service::Glue::Time {
7
8PmStateChangeHandler::PmStateChangeHandler(AlarmWorker& alarm_worker)
9 : m_alarm_worker{alarm_worker} {
10 // TODO Initialize IPmModule, dependent on Rtc and Fs
11}
12
13} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/pm_state_change_handler.h b/src/core/hle/service/glue/time/pm_state_change_handler.h
new file mode 100644
index 000000000..27d9f7872
--- /dev/null
+++ b/src/core/hle/service/glue/time/pm_state_change_handler.h
@@ -0,0 +1,18 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7
8namespace Service::Glue::Time {
9class AlarmWorker;
10
11class PmStateChangeHandler {
12public:
13 explicit PmStateChangeHandler(AlarmWorker& alarm_worker);
14
15 AlarmWorker& m_alarm_worker;
16 s32 m_priority{};
17};
18} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/standard_steady_clock_resource.cpp b/src/core/hle/service/glue/time/standard_steady_clock_resource.cpp
new file mode 100644
index 000000000..5ebaa33e0
--- /dev/null
+++ b/src/core/hle/service/glue/time/standard_steady_clock_resource.cpp
@@ -0,0 +1,123 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <chrono>
5
6#include "common/settings.h"
7#include "core/core.h"
8#include "core/core_timing.h"
9#include "core/hle/kernel/svc.h"
10#include "core/hle/service/glue/time/standard_steady_clock_resource.h"
11#include "core/hle/service/psc/time/errors.h"
12
13namespace Service::Glue::Time {
14namespace {
15[[maybe_unused]] constexpr u32 Max77620PmicSession = 0x3A000001;
16[[maybe_unused]] constexpr u32 Max77620RtcSession = 0x3B000001;
17
18Result GetTimeInSeconds(Core::System& system, s64& out_time_s) {
19 out_time_s = std::chrono::duration_cast<std::chrono::seconds>(
20 std::chrono::system_clock::now().time_since_epoch())
21 .count();
22
23 if (Settings::values.custom_rtc_enabled) {
24 out_time_s += Settings::values.custom_rtc_offset.GetValue();
25 }
26 R_SUCCEED();
27}
28} // namespace
29
30StandardSteadyClockResource::StandardSteadyClockResource(Core::System& system) : m_system{system} {}
31
32void StandardSteadyClockResource::Initialize(Common::UUID* out_source_id,
33 Common::UUID* external_source_id) {
34 constexpr size_t NUM_TRIES{20};
35
36 size_t i{0};
37 Result res{ResultSuccess};
38 for (; i < NUM_TRIES; i++) {
39 res = SetCurrentTime();
40 if (res == ResultSuccess) {
41 break;
42 }
43 Kernel::Svc::SleepThread(m_system, std::chrono::duration_cast<std::chrono::nanoseconds>(
44 std::chrono::milliseconds(1))
45 .count());
46 }
47
48 if (i < NUM_TRIES) {
49 m_set_time_result = ResultSuccess;
50 if (*external_source_id != Service::PSC::Time::ClockSourceId{}) {
51 m_clock_source_id = *external_source_id;
52 } else {
53 m_clock_source_id = Common::UUID::MakeRandom();
54 }
55 } else {
56 m_set_time_result = res;
57 auto ticks{m_system.CoreTiming().GetClockTicks()};
58 m_time = -Service::PSC::Time::ConvertToTimeSpan(ticks).count();
59 m_clock_source_id = Common::UUID::MakeRandom();
60 }
61
62 if (out_source_id) {
63 *out_source_id = m_clock_source_id;
64 }
65}
66
67bool StandardSteadyClockResource::GetResetDetected() {
68 // TODO:
69 // call Rtc::GetRtcResetDetected(Max77620RtcSession)
70 // if detected:
71 // SetSys::SetExternalSteadyClockSourceId(invalid_id)
72 // Rtc::ClearRtcResetDetected(Max77620RtcSession)
73 // set m_rtc_reset to result
74 // Instead, only set reset to true if we're booting for the first time.
75 m_rtc_reset = false;
76 return m_rtc_reset;
77}
78
79Result StandardSteadyClockResource::SetCurrentTime() {
80 auto start_tick{m_system.CoreTiming().GetClockTicks()};
81
82 s64 rtc_time_s{};
83 // TODO R_TRY(Rtc::GetTimeInSeconds(rtc_time_s, Max77620RtcSession))
84 R_TRY(GetTimeInSeconds(m_system, rtc_time_s));
85
86 auto end_tick{m_system.CoreTiming().GetClockTicks()};
87 auto diff{Service::PSC::Time::ConvertToTimeSpan(end_tick - start_tick)};
88 // Why is this here?
89 R_UNLESS(diff < std::chrono::milliseconds(101), Service::PSC::Time::ResultRtcTimeout);
90
91 auto one_second_ns{
92 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
93 s64 boot_time{rtc_time_s * one_second_ns -
94 Service::PSC::Time::ConvertToTimeSpan(end_tick).count()};
95
96 std::scoped_lock l{m_mutex};
97 m_time = boot_time;
98 R_SUCCEED();
99}
100
101Result StandardSteadyClockResource::GetRtcTimeInSeconds(s64& out_time) {
102 // TODO
103 // R_TRY(Rtc::GetTimeInSeconds(time_s, Max77620RtcSession)
104 R_RETURN(GetTimeInSeconds(m_system, out_time));
105}
106
107void StandardSteadyClockResource::UpdateTime() {
108 constexpr size_t NUM_TRIES{3};
109
110 size_t i{0};
111 Result res{ResultSuccess};
112 for (; i < NUM_TRIES; i++) {
113 res = SetCurrentTime();
114 if (res == ResultSuccess) {
115 break;
116 }
117 Kernel::Svc::SleepThread(m_system, std::chrono::duration_cast<std::chrono::nanoseconds>(
118 std::chrono::milliseconds(1))
119 .count());
120 }
121}
122
123} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/standard_steady_clock_resource.h b/src/core/hle/service/glue/time/standard_steady_clock_resource.h
new file mode 100644
index 000000000..978d6b63b
--- /dev/null
+++ b/src/core/hle/service/glue/time/standard_steady_clock_resource.h
@@ -0,0 +1,41 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <mutex>
7
8#include "common/common_types.h"
9#include "core/hle/result.h"
10#include "core/hle/service/psc/time/common.h"
11
12namespace Core {
13class System;
14}
15
16namespace Service::Glue::Time {
17class StandardSteadyClockResource {
18public:
19 StandardSteadyClockResource(Core::System& system);
20
21 void Initialize(Common::UUID* out_source_id, Common::UUID* external_source_id);
22
23 s64 GetTime() const {
24 return m_time;
25 }
26
27 bool GetResetDetected();
28 Result SetCurrentTime();
29 Result GetRtcTimeInSeconds(s64& out_time);
30 void UpdateTime();
31
32private:
33 Core::System& m_system;
34
35 std::mutex m_mutex;
36 Service::PSC::Time::ClockSourceId m_clock_source_id{};
37 s64 m_time{};
38 Result m_set_time_result;
39 bool m_rtc_reset;
40};
41} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/static.cpp b/src/core/hle/service/glue/time/static.cpp
new file mode 100644
index 000000000..63b7d91da
--- /dev/null
+++ b/src/core/hle/service/glue/time/static.cpp
@@ -0,0 +1,448 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <chrono>
5
6#include "core/core.h"
7#include "core/hle/kernel/k_shared_memory.h"
8#include "core/hle/kernel/svc.h"
9#include "core/hle/service/glue/time/file_timestamp_worker.h"
10#include "core/hle/service/glue/time/static.h"
11#include "core/hle/service/psc/time/errors.h"
12#include "core/hle/service/psc/time/service_manager.h"
13#include "core/hle/service/psc/time/static.h"
14#include "core/hle/service/psc/time/steady_clock.h"
15#include "core/hle/service/psc/time/system_clock.h"
16#include "core/hle/service/psc/time/time_zone_service.h"
17#include "core/hle/service/set/system_settings_server.h"
18#include "core/hle/service/sm/sm.h"
19
20namespace Service::Glue::Time {
21namespace {
22template <typename T>
23T GetSettingsItemValue(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys,
24 const char* category, const char* name) {
25 std::vector<u8> interval_buf;
26 auto res = set_sys->GetSettingsItemValue(interval_buf, category, name);
27 ASSERT(res == ResultSuccess);
28
29 T v{};
30 std::memcpy(&v, interval_buf.data(), sizeof(T));
31 return v;
32}
33} // namespace
34
35StaticService::StaticService(Core::System& system_,
36 Service::PSC::Time::StaticServiceSetupInfo setup_info,
37 std::shared_ptr<TimeManager> time, const char* name)
38 : ServiceFramework{system_, name}, m_system{system_}, m_time_m{time->m_time_m},
39 m_setup_info{setup_info}, m_time_sm{time->m_time_sm},
40 m_file_timestamp_worker{time->m_file_timestamp_worker}, m_standard_steady_clock_resource{
41 time->m_steady_clock_resource} {
42 // clang-format off
43 static const FunctionInfo functions[] = {
44 {0, &StaticService::Handle_GetStandardUserSystemClock, "GetStandardUserSystemClock"},
45 {1, &StaticService::Handle_GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"},
46 {2, &StaticService::Handle_GetStandardSteadyClock, "GetStandardSteadyClock"},
47 {3, &StaticService::Handle_GetTimeZoneService, "GetTimeZoneService"},
48 {4, &StaticService::Handle_GetStandardLocalSystemClock, "GetStandardLocalSystemClock"},
49 {5, &StaticService::Handle_GetEphemeralNetworkSystemClock, "GetEphemeralNetworkSystemClock"},
50 {20, &StaticService::Handle_GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"},
51 {50, &StaticService::Handle_SetStandardSteadyClockInternalOffset, "SetStandardSteadyClockInternalOffset"},
52 {51, &StaticService::Handle_GetStandardSteadyClockRtcValue, "GetStandardSteadyClockRtcValue"},
53 {100, &StaticService::Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled, "IsStandardUserSystemClockAutomaticCorrectionEnabled"},
54 {101, &StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled, "SetStandardUserSystemClockAutomaticCorrectionEnabled"},
55 {102, &StaticService::Handle_GetStandardUserSystemClockInitialYear, "GetStandardUserSystemClockInitialYear"},
56 {200, &StaticService::Handle_IsStandardNetworkSystemClockAccuracySufficient, "IsStandardNetworkSystemClockAccuracySufficient"},
57 {201, &StaticService::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"},
58 {300, &StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint, "CalculateMonotonicSystemClockBaseTimePoint"},
59 {400, &StaticService::Handle_GetClockSnapshot, "GetClockSnapshot"},
60 {401, &StaticService::Handle_GetClockSnapshotFromSystemClockContext, "GetClockSnapshotFromSystemClockContext"},
61 {500, &StaticService::Handle_CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"},
62 {501, &StaticService::Handle_CalculateSpanBetween, "CalculateSpanBetween"},
63 };
64 // clang-format on
65
66 RegisterHandlers(functions);
67
68 m_set_sys =
69 m_system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
70
71 if (m_setup_info.can_write_local_clock && m_setup_info.can_write_user_clock &&
72 !m_setup_info.can_write_network_clock && m_setup_info.can_write_timezone_device_location &&
73 !m_setup_info.can_write_steady_clock && !m_setup_info.can_write_uninitialized_clock) {
74 m_time_m->GetStaticServiceAsAdmin(m_wrapped_service);
75 } else if (!m_setup_info.can_write_local_clock && !m_setup_info.can_write_user_clock &&
76 !m_setup_info.can_write_network_clock &&
77 !m_setup_info.can_write_timezone_device_location &&
78 !m_setup_info.can_write_steady_clock &&
79 !m_setup_info.can_write_uninitialized_clock) {
80 m_time_m->GetStaticServiceAsUser(m_wrapped_service);
81 } else if (!m_setup_info.can_write_local_clock && !m_setup_info.can_write_user_clock &&
82 !m_setup_info.can_write_network_clock &&
83 !m_setup_info.can_write_timezone_device_location &&
84 m_setup_info.can_write_steady_clock && !m_setup_info.can_write_uninitialized_clock) {
85 m_time_m->GetStaticServiceAsRepair(m_wrapped_service);
86 } else {
87 UNREACHABLE();
88 }
89
90 auto res = m_wrapped_service->GetTimeZoneService(m_time_zone);
91 ASSERT(res == ResultSuccess);
92}
93
94void StaticService::Handle_GetStandardUserSystemClock(HLERequestContext& ctx) {
95 LOG_DEBUG(Service_Time, "called.");
96
97 std::shared_ptr<Service::PSC::Time::SystemClock> service{};
98 auto res = GetStandardUserSystemClock(service);
99
100 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
101 rb.Push(res);
102 rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service));
103}
104
105void StaticService::Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx) {
106 LOG_DEBUG(Service_Time, "called.");
107
108 std::shared_ptr<Service::PSC::Time::SystemClock> service{};
109 auto res = GetStandardNetworkSystemClock(service);
110
111 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
112 rb.Push(res);
113 rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service));
114}
115
116void StaticService::Handle_GetStandardSteadyClock(HLERequestContext& ctx) {
117 LOG_DEBUG(Service_Time, "called.");
118
119 std::shared_ptr<Service::PSC::Time::SteadyClock> service{};
120 auto res = GetStandardSteadyClock(service);
121
122 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
123 rb.Push(res);
124 rb.PushIpcInterface(std::move(service));
125}
126
127void StaticService::Handle_GetTimeZoneService(HLERequestContext& ctx) {
128 LOG_DEBUG(Service_Time, "called.");
129
130 std::shared_ptr<TimeZoneService> service{};
131 auto res = GetTimeZoneService(service);
132
133 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
134 rb.Push(res);
135 rb.PushIpcInterface(std::move(service));
136}
137
138void StaticService::Handle_GetStandardLocalSystemClock(HLERequestContext& ctx) {
139 LOG_DEBUG(Service_Time, "called.");
140
141 std::shared_ptr<Service::PSC::Time::SystemClock> service{};
142 auto res = GetStandardLocalSystemClock(service);
143
144 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
145 rb.Push(res);
146 rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service));
147}
148
149void StaticService::Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx) {
150 LOG_DEBUG(Service_Time, "called.");
151
152 std::shared_ptr<Service::PSC::Time::SystemClock> service{};
153 auto res = GetEphemeralNetworkSystemClock(service);
154
155 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
156 rb.Push(res);
157 rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service));
158}
159
160void StaticService::Handle_GetSharedMemoryNativeHandle(HLERequestContext& ctx) {
161 LOG_DEBUG(Service_Time, "called.");
162
163 Kernel::KSharedMemory* shared_memory{};
164 auto res = GetSharedMemoryNativeHandle(&shared_memory);
165
166 IPC::ResponseBuilder rb{ctx, 2, 1};
167 rb.Push(res);
168 rb.PushCopyObjects(shared_memory);
169}
170
171void StaticService::Handle_SetStandardSteadyClockInternalOffset(HLERequestContext& ctx) {
172 LOG_DEBUG(Service_Time, "called.");
173
174 IPC::RequestParser rp{ctx};
175 auto offset_ns{rp.Pop<s64>()};
176
177 auto res = SetStandardSteadyClockInternalOffset(offset_ns);
178
179 IPC::ResponseBuilder rb{ctx, 2};
180 rb.Push(res);
181}
182
183void StaticService::Handle_GetStandardSteadyClockRtcValue(HLERequestContext& ctx) {
184 LOG_DEBUG(Service_Time, "called.");
185
186 s64 rtc_value{};
187 auto res = GetStandardSteadyClockRtcValue(rtc_value);
188
189 IPC::ResponseBuilder rb{ctx, 4};
190 rb.Push(res);
191 rb.Push(rtc_value);
192}
193
194void StaticService::Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled(
195 HLERequestContext& ctx) {
196 LOG_DEBUG(Service_Time, "called.");
197
198 bool is_enabled{};
199 auto res = IsStandardUserSystemClockAutomaticCorrectionEnabled(is_enabled);
200
201 IPC::ResponseBuilder rb{ctx, 3};
202 rb.Push(res);
203 rb.Push<bool>(is_enabled);
204}
205
206void StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled(
207 HLERequestContext& ctx) {
208 LOG_DEBUG(Service_Time, "called.");
209
210 IPC::RequestParser rp{ctx};
211 auto automatic_correction{rp.Pop<bool>()};
212
213 auto res = SetStandardUserSystemClockAutomaticCorrectionEnabled(automatic_correction);
214
215 IPC::ResponseBuilder rb{ctx, 2};
216 rb.Push(res);
217}
218
219void StaticService::Handle_GetStandardUserSystemClockInitialYear(HLERequestContext& ctx) {
220 LOG_DEBUG(Service_Time, "called.");
221
222 s32 initial_year{};
223 auto res = GetStandardUserSystemClockInitialYear(initial_year);
224
225 IPC::ResponseBuilder rb{ctx, 3};
226 rb.Push(res);
227 rb.Push(initial_year);
228}
229
230void StaticService::Handle_IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx) {
231 LOG_DEBUG(Service_Time, "called.");
232
233 bool is_sufficient{};
234 auto res = IsStandardNetworkSystemClockAccuracySufficient(is_sufficient);
235
236 IPC::ResponseBuilder rb{ctx, 3};
237 rb.Push(res);
238 rb.Push<bool>(is_sufficient);
239}
240
241void StaticService::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
242 HLERequestContext& ctx) {
243 LOG_DEBUG(Service_Time, "called.");
244
245 Service::PSC::Time::SteadyClockTimePoint time_point{};
246 auto res = GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
247
248 IPC::ResponseBuilder rb{ctx,
249 2 + sizeof(Service::PSC::Time::SteadyClockTimePoint) / sizeof(u32)};
250 rb.Push(res);
251 rb.PushRaw<Service::PSC::Time::SteadyClockTimePoint>(time_point);
252}
253
254void StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx) {
255 LOG_DEBUG(Service_Time, "called.");
256
257 IPC::RequestParser rp{ctx};
258 auto context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()};
259
260 s64 time{};
261 auto res = CalculateMonotonicSystemClockBaseTimePoint(time, context);
262
263 IPC::ResponseBuilder rb{ctx, 4};
264 rb.Push(res);
265 rb.Push<s64>(time);
266}
267
268void StaticService::Handle_GetClockSnapshot(HLERequestContext& ctx) {
269 LOG_DEBUG(Service_Time, "called.");
270
271 IPC::RequestParser rp{ctx};
272 auto type{rp.PopEnum<Service::PSC::Time::TimeType>()};
273
274 Service::PSC::Time::ClockSnapshot snapshot{};
275 auto res = GetClockSnapshot(snapshot, type);
276
277 ctx.WriteBuffer(snapshot);
278
279 IPC::ResponseBuilder rb{ctx, 2};
280 rb.Push(res);
281}
282
283void StaticService::Handle_GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx) {
284 LOG_DEBUG(Service_Time, "called.");
285
286 IPC::RequestParser rp{ctx};
287 auto clock_type{rp.PopEnum<Service::PSC::Time::TimeType>()};
288 [[maybe_unused]] auto alignment{rp.Pop<u32>()};
289 auto user_context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()};
290 auto network_context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()};
291
292 Service::PSC::Time::ClockSnapshot snapshot{};
293 auto res =
294 GetClockSnapshotFromSystemClockContext(snapshot, user_context, network_context, clock_type);
295
296 ctx.WriteBuffer(snapshot);
297
298 IPC::ResponseBuilder rb{ctx, 2};
299 rb.Push(res);
300}
301
302void StaticService::Handle_CalculateStandardUserSystemClockDifferenceByUser(
303 HLERequestContext& ctx) {
304 LOG_DEBUG(Service_Time, "called.");
305
306 Service::PSC::Time::ClockSnapshot a{};
307 Service::PSC::Time::ClockSnapshot b{};
308
309 auto a_buffer{ctx.ReadBuffer(0)};
310 auto b_buffer{ctx.ReadBuffer(1)};
311
312 std::memcpy(&a, a_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot));
313 std::memcpy(&b, b_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot));
314
315 s64 difference{};
316 auto res = CalculateStandardUserSystemClockDifferenceByUser(difference, a, b);
317
318 IPC::ResponseBuilder rb{ctx, 4};
319 rb.Push(res);
320 rb.Push(difference);
321}
322
323void StaticService::Handle_CalculateSpanBetween(HLERequestContext& ctx) {
324 LOG_DEBUG(Service_Time, "called.");
325
326 Service::PSC::Time::ClockSnapshot a{};
327 Service::PSC::Time::ClockSnapshot b{};
328
329 auto a_buffer{ctx.ReadBuffer(0)};
330 auto b_buffer{ctx.ReadBuffer(1)};
331
332 std::memcpy(&a, a_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot));
333 std::memcpy(&b, b_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot));
334
335 s64 time{};
336 auto res = CalculateSpanBetween(time, a, b);
337
338 IPC::ResponseBuilder rb{ctx, 4};
339 rb.Push(res);
340 rb.Push(time);
341}
342
343// =============================== Implementations ===========================
344
345Result StaticService::GetStandardUserSystemClock(
346 std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) {
347 R_RETURN(m_wrapped_service->GetStandardUserSystemClock(out_service));
348}
349
350Result StaticService::GetStandardNetworkSystemClock(
351 std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) {
352 R_RETURN(m_wrapped_service->GetStandardNetworkSystemClock(out_service));
353}
354
355Result StaticService::GetStandardSteadyClock(
356 std::shared_ptr<Service::PSC::Time::SteadyClock>& out_service) {
357 R_RETURN(m_wrapped_service->GetStandardSteadyClock(out_service));
358}
359
360Result StaticService::GetTimeZoneService(std::shared_ptr<TimeZoneService>& out_service) {
361 out_service = std::make_shared<TimeZoneService>(m_system, m_file_timestamp_worker,
362 m_setup_info.can_write_timezone_device_location,
363 m_time_zone);
364 R_SUCCEED();
365}
366
367Result StaticService::GetStandardLocalSystemClock(
368 std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) {
369 R_RETURN(m_wrapped_service->GetStandardLocalSystemClock(out_service));
370}
371
372Result StaticService::GetEphemeralNetworkSystemClock(
373 std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) {
374 R_RETURN(m_wrapped_service->GetEphemeralNetworkSystemClock(out_service));
375}
376
377Result StaticService::GetSharedMemoryNativeHandle(Kernel::KSharedMemory** out_shared_memory) {
378 R_RETURN(m_wrapped_service->GetSharedMemoryNativeHandle(out_shared_memory));
379}
380
381Result StaticService::SetStandardSteadyClockInternalOffset(s64 offset_ns) {
382 R_UNLESS(m_setup_info.can_write_steady_clock, Service::PSC::Time::ResultPermissionDenied);
383
384 R_RETURN(m_set_sys->SetExternalSteadyClockInternalOffset(
385 offset_ns /
386 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()));
387}
388
389Result StaticService::GetStandardSteadyClockRtcValue(s64& out_rtc_value) {
390 R_RETURN(m_standard_steady_clock_resource.GetRtcTimeInSeconds(out_rtc_value));
391}
392
393Result StaticService::IsStandardUserSystemClockAutomaticCorrectionEnabled(
394 bool& out_automatic_correction) {
395 R_RETURN(m_wrapped_service->IsStandardUserSystemClockAutomaticCorrectionEnabled(
396 out_automatic_correction));
397}
398
399Result StaticService::SetStandardUserSystemClockAutomaticCorrectionEnabled(
400 bool automatic_correction) {
401 R_RETURN(m_wrapped_service->SetStandardUserSystemClockAutomaticCorrectionEnabled(
402 automatic_correction));
403}
404
405Result StaticService::GetStandardUserSystemClockInitialYear(s32& out_year) {
406 out_year = GetSettingsItemValue<s32>(m_set_sys, "time", "standard_user_clock_initial_year");
407 R_SUCCEED();
408}
409
410Result StaticService::IsStandardNetworkSystemClockAccuracySufficient(bool& out_is_sufficient) {
411 R_RETURN(m_wrapped_service->IsStandardNetworkSystemClockAccuracySufficient(out_is_sufficient));
412}
413
414Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
415 Service::PSC::Time::SteadyClockTimePoint& out_time_point) {
416 R_RETURN(m_wrapped_service->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
417 out_time_point));
418}
419
420Result StaticService::CalculateMonotonicSystemClockBaseTimePoint(
421 s64& out_time, Service::PSC::Time::SystemClockContext& context) {
422 R_RETURN(m_wrapped_service->CalculateMonotonicSystemClockBaseTimePoint(out_time, context));
423}
424
425Result StaticService::GetClockSnapshot(Service::PSC::Time::ClockSnapshot& out_snapshot,
426 Service::PSC::Time::TimeType type) {
427 R_RETURN(m_wrapped_service->GetClockSnapshot(out_snapshot, type));
428}
429
430Result StaticService::GetClockSnapshotFromSystemClockContext(
431 Service::PSC::Time::ClockSnapshot& out_snapshot,
432 Service::PSC::Time::SystemClockContext& user_context,
433 Service::PSC::Time::SystemClockContext& network_context, Service::PSC::Time::TimeType type) {
434 R_RETURN(m_wrapped_service->GetClockSnapshotFromSystemClockContext(out_snapshot, user_context,
435 network_context, type));
436}
437
438Result StaticService::CalculateStandardUserSystemClockDifferenceByUser(
439 s64& out_time, Service::PSC::Time::ClockSnapshot& a, Service::PSC::Time::ClockSnapshot& b) {
440 R_RETURN(m_wrapped_service->CalculateStandardUserSystemClockDifferenceByUser(out_time, a, b));
441}
442
443Result StaticService::CalculateSpanBetween(s64& out_time, Service::PSC::Time::ClockSnapshot& a,
444 Service::PSC::Time::ClockSnapshot& b) {
445 R_RETURN(m_wrapped_service->CalculateSpanBetween(out_time, a, b));
446}
447
448} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/static.h b/src/core/hle/service/glue/time/static.h
new file mode 100644
index 000000000..75fe4e2cd
--- /dev/null
+++ b/src/core/hle/service/glue/time/static.h
@@ -0,0 +1,110 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hle/service/glue/time/manager.h"
8#include "core/hle/service/glue/time/time_zone.h"
9#include "core/hle/service/psc/time/common.h"
10
11namespace Core {
12class System;
13}
14
15namespace Service::Set {
16class ISystemSettingsServer;
17}
18
19namespace Service::PSC::Time {
20class StaticService;
21class SystemClock;
22class SteadyClock;
23class TimeZoneService;
24class ServiceManager;
25} // namespace Service::PSC::Time
26
27namespace Service::Glue::Time {
28class FileTimestampWorker;
29class StandardSteadyClockResource;
30
31class StaticService final : public ServiceFramework<StaticService> {
32public:
33 explicit StaticService(Core::System& system,
34 Service::PSC::Time::StaticServiceSetupInfo setup_info,
35 std::shared_ptr<TimeManager> time, const char* name);
36
37 ~StaticService() override = default;
38
39 Result GetStandardUserSystemClock(
40 std::shared_ptr<Service::PSC::Time::SystemClock>& out_service);
41 Result GetStandardNetworkSystemClock(
42 std::shared_ptr<Service::PSC::Time::SystemClock>& out_service);
43 Result GetStandardSteadyClock(std::shared_ptr<Service::PSC::Time::SteadyClock>& out_service);
44 Result GetTimeZoneService(std::shared_ptr<TimeZoneService>& out_service);
45 Result GetStandardLocalSystemClock(
46 std::shared_ptr<Service::PSC::Time::SystemClock>& out_service);
47 Result GetEphemeralNetworkSystemClock(
48 std::shared_ptr<Service::PSC::Time::SystemClock>& out_service);
49 Result GetSharedMemoryNativeHandle(Kernel::KSharedMemory** out_shared_memory);
50 Result SetStandardSteadyClockInternalOffset(s64 offset);
51 Result GetStandardSteadyClockRtcValue(s64& out_rtc_value);
52 Result IsStandardUserSystemClockAutomaticCorrectionEnabled(bool& out_automatic_correction);
53 Result SetStandardUserSystemClockAutomaticCorrectionEnabled(bool automatic_correction);
54 Result GetStandardUserSystemClockInitialYear(s32& out_year);
55 Result IsStandardNetworkSystemClockAccuracySufficient(bool& out_is_sufficient);
56 Result GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
57 Service::PSC::Time::SteadyClockTimePoint& out_time_point);
58 Result CalculateMonotonicSystemClockBaseTimePoint(
59 s64& out_time, Service::PSC::Time::SystemClockContext& context);
60 Result GetClockSnapshot(Service::PSC::Time::ClockSnapshot& out_snapshot,
61 Service::PSC::Time::TimeType type);
62 Result GetClockSnapshotFromSystemClockContext(
63 Service::PSC::Time::ClockSnapshot& out_snapshot,
64 Service::PSC::Time::SystemClockContext& user_context,
65 Service::PSC::Time::SystemClockContext& network_context, Service::PSC::Time::TimeType type);
66 Result CalculateStandardUserSystemClockDifferenceByUser(s64& out_time,
67 Service::PSC::Time::ClockSnapshot& a,
68 Service::PSC::Time::ClockSnapshot& b);
69 Result CalculateSpanBetween(s64& out_time, Service::PSC::Time::ClockSnapshot& a,
70 Service::PSC::Time::ClockSnapshot& b);
71
72private:
73 Result GetClockSnapshotImpl(Service::PSC::Time::ClockSnapshot& out_snapshot,
74 Service::PSC::Time::SystemClockContext& user_context,
75 Service::PSC::Time::SystemClockContext& network_context,
76 Service::PSC::Time::TimeType type);
77
78 void Handle_GetStandardUserSystemClock(HLERequestContext& ctx);
79 void Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx);
80 void Handle_GetStandardSteadyClock(HLERequestContext& ctx);
81 void Handle_GetTimeZoneService(HLERequestContext& ctx);
82 void Handle_GetStandardLocalSystemClock(HLERequestContext& ctx);
83 void Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx);
84 void Handle_GetSharedMemoryNativeHandle(HLERequestContext& ctx);
85 void Handle_SetStandardSteadyClockInternalOffset(HLERequestContext& ctx);
86 void Handle_GetStandardSteadyClockRtcValue(HLERequestContext& ctx);
87 void Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx);
88 void Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx);
89 void Handle_GetStandardUserSystemClockInitialYear(HLERequestContext& ctx);
90 void Handle_IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx);
91 void Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx);
92 void Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx);
93 void Handle_GetClockSnapshot(HLERequestContext& ctx);
94 void Handle_GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx);
95 void Handle_CalculateStandardUserSystemClockDifferenceByUser(HLERequestContext& ctx);
96 void Handle_CalculateSpanBetween(HLERequestContext& ctx);
97
98 Core::System& m_system;
99
100 std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
101 std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m;
102 std::shared_ptr<Service::PSC::Time::StaticService> m_wrapped_service;
103
104 Service::PSC::Time::StaticServiceSetupInfo m_setup_info;
105 std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm;
106 std::shared_ptr<Service::PSC::Time::TimeZoneService> m_time_zone;
107 FileTimestampWorker& m_file_timestamp_worker;
108 StandardSteadyClockResource& m_standard_steady_clock_resource;
109};
110} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/time_zone.cpp b/src/core/hle/service/glue/time/time_zone.cpp
new file mode 100644
index 000000000..503c327dd
--- /dev/null
+++ b/src/core/hle/service/glue/time/time_zone.cpp
@@ -0,0 +1,377 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <chrono>
5
6#include "core/core.h"
7#include "core/hle/kernel/svc.h"
8#include "core/hle/service/glue/time/file_timestamp_worker.h"
9#include "core/hle/service/glue/time/time_zone.h"
10#include "core/hle/service/glue/time/time_zone_binary.h"
11#include "core/hle/service/psc/time/time_zone_service.h"
12#include "core/hle/service/set/system_settings_server.h"
13#include "core/hle/service/sm/sm.h"
14
15namespace Service::Glue::Time {
16namespace {
17static std::mutex g_list_mutex;
18static Common::IntrusiveListBaseTraits<Service::PSC::Time::OperationEvent>::ListType g_list_nodes{};
19} // namespace
20
21TimeZoneService::TimeZoneService(
22 Core::System& system_, FileTimestampWorker& file_timestamp_worker,
23 bool can_write_timezone_device_location,
24 std::shared_ptr<Service::PSC::Time::TimeZoneService> time_zone_service)
25 : ServiceFramework{system_, "ITimeZoneService"}, m_system{system},
26 m_can_write_timezone_device_location{can_write_timezone_device_location},
27 m_file_timestamp_worker{file_timestamp_worker},
28 m_wrapped_service{std::move(time_zone_service)}, m_operation_event{m_system} {
29 // clang-format off
30 static const FunctionInfo functions[] = {
31 {0, &TimeZoneService::Handle_GetDeviceLocationName, "GetDeviceLocationName"},
32 {1, &TimeZoneService::Handle_SetDeviceLocationName, "SetDeviceLocationName"},
33 {2, &TimeZoneService::Handle_GetTotalLocationNameCount, "GetTotalLocationNameCount"},
34 {3, &TimeZoneService::Handle_LoadLocationNameList, "LoadLocationNameList"},
35 {4, &TimeZoneService::Handle_LoadTimeZoneRule, "LoadTimeZoneRule"},
36 {5, &TimeZoneService::Handle_GetTimeZoneRuleVersion, "GetTimeZoneRuleVersion"},
37 {6, &TimeZoneService::Handle_GetDeviceLocationNameAndUpdatedTime, "GetDeviceLocationNameAndUpdatedTime"},
38 {7, &TimeZoneService::Handle_SetDeviceLocationNameWithTimeZoneRule, "SetDeviceLocationNameWithTimeZoneRule"},
39 {8, &TimeZoneService::Handle_ParseTimeZoneBinary, "ParseTimeZoneBinary"},
40 {20, &TimeZoneService::Handle_GetDeviceLocationNameOperationEventReadableHandle, "GetDeviceLocationNameOperationEventReadableHandle"},
41 {100, &TimeZoneService::Handle_ToCalendarTime, "ToCalendarTime"},
42 {101, &TimeZoneService::Handle_ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
43 {201, &TimeZoneService::Handle_ToPosixTime, "ToPosixTime"},
44 {202, &TimeZoneService::Handle_ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"},
45 };
46 // clang-format on
47 RegisterHandlers(functions);
48
49 g_list_nodes.clear();
50 m_set_sys =
51 m_system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
52}
53
54TimeZoneService::~TimeZoneService() = default;
55
56void TimeZoneService::Handle_GetDeviceLocationName(HLERequestContext& ctx) {
57 LOG_DEBUG(Service_Time, "called.");
58
59 Service::PSC::Time::LocationName name{};
60 auto res = GetDeviceLocationName(name);
61
62 IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::LocationName) / sizeof(u32)};
63 rb.Push(res);
64 rb.PushRaw<Service::PSC::Time::LocationName>(name);
65}
66
67void TimeZoneService::Handle_SetDeviceLocationName(HLERequestContext& ctx) {
68 LOG_DEBUG(Service_Time, "called.");
69
70 IPC::RequestParser rp{ctx};
71 auto name{rp.PopRaw<Service::PSC::Time::LocationName>()};
72
73 auto res = SetDeviceLocation(name);
74
75 IPC::ResponseBuilder rb{ctx, 2};
76 rb.Push(res);
77}
78
79void TimeZoneService::Handle_GetTotalLocationNameCount(HLERequestContext& ctx) {
80 LOG_DEBUG(Service_Time, "called.");
81
82 u32 count{};
83 auto res = GetTotalLocationNameCount(count);
84
85 IPC::ResponseBuilder rb{ctx, 3};
86 rb.Push(res);
87 rb.Push(count);
88}
89
90void TimeZoneService::Handle_LoadLocationNameList(HLERequestContext& ctx) {
91 LOG_DEBUG(Service_Time, "called.");
92
93 IPC::RequestParser rp{ctx};
94 auto index{rp.Pop<u32>()};
95
96 auto max_names{ctx.GetWriteBufferSize() / sizeof(Service::PSC::Time::LocationName)};
97
98 std::vector<Service::PSC::Time::LocationName> names{};
99 u32 count{};
100 auto res = LoadLocationNameList(count, names, max_names, index);
101
102 ctx.WriteBuffer(names);
103
104 IPC::ResponseBuilder rb{ctx, 3};
105 rb.Push(res);
106 rb.Push(count);
107}
108
109void TimeZoneService::Handle_LoadTimeZoneRule(HLERequestContext& ctx) {
110 LOG_DEBUG(Service_Time, "called.");
111
112 IPC::RequestParser rp{ctx};
113 auto name{rp.PopRaw<Service::PSC::Time::LocationName>()};
114
115 Tz::Rule rule{};
116 auto res = LoadTimeZoneRule(rule, name);
117
118 ctx.WriteBuffer(rule);
119
120 IPC::ResponseBuilder rb{ctx, 2};
121 rb.Push(res);
122}
123
124void TimeZoneService::Handle_GetTimeZoneRuleVersion(HLERequestContext& ctx) {
125 LOG_DEBUG(Service_Time, "called.");
126
127 Service::PSC::Time::RuleVersion rule_version{};
128 auto res = GetTimeZoneRuleVersion(rule_version);
129
130 IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::RuleVersion) / sizeof(u32)};
131 rb.Push(res);
132 rb.PushRaw<Service::PSC::Time::RuleVersion>(rule_version);
133}
134
135void TimeZoneService::Handle_GetDeviceLocationNameAndUpdatedTime(HLERequestContext& ctx) {
136 LOG_DEBUG(Service_Time, "called.");
137
138 Service::PSC::Time::LocationName name{};
139 Service::PSC::Time::SteadyClockTimePoint time_point{};
140 auto res = GetDeviceLocationNameAndUpdatedTime(time_point, name);
141
142 IPC::ResponseBuilder rb{ctx,
143 2 + (sizeof(Service::PSC::Time::LocationName) / sizeof(u32)) +
144 (sizeof(Service::PSC::Time::SteadyClockTimePoint) / sizeof(u32))};
145 rb.Push(res);
146 rb.PushRaw<Service::PSC::Time::LocationName>(name);
147 rb.PushRaw<Service::PSC::Time::SteadyClockTimePoint>(time_point);
148}
149
150void TimeZoneService::Handle_SetDeviceLocationNameWithTimeZoneRule(HLERequestContext& ctx) {
151 LOG_DEBUG(Service_Time, "called.");
152
153 auto res = SetDeviceLocationNameWithTimeZoneRule();
154
155 IPC::ResponseBuilder rb{ctx, 2};
156 rb.Push(res);
157}
158
159void TimeZoneService::Handle_ParseTimeZoneBinary(HLERequestContext& ctx) {
160 LOG_DEBUG(Service_Time, "called.");
161
162 IPC::ResponseBuilder rb{ctx, 2};
163 rb.Push(Service::PSC::Time::ResultNotImplemented);
164}
165
166void TimeZoneService::Handle_GetDeviceLocationNameOperationEventReadableHandle(
167 HLERequestContext& ctx) {
168 LOG_DEBUG(Service_Time, "called.");
169
170 Kernel::KEvent* event{};
171 auto res = GetDeviceLocationNameOperationEventReadableHandle(&event);
172
173 IPC::ResponseBuilder rb{ctx, 2, 1};
174 rb.Push(res);
175 rb.PushCopyObjects(event->GetReadableEvent());
176}
177
178void TimeZoneService::Handle_ToCalendarTime(HLERequestContext& ctx) {
179 LOG_DEBUG(Service_Time, "called.");
180
181 IPC::RequestParser rp{ctx};
182 auto time{rp.Pop<s64>()};
183
184 auto rule_buffer{ctx.ReadBuffer()};
185 Tz::Rule rule{};
186 std::memcpy(&rule, rule_buffer.data(), sizeof(Tz::Rule));
187
188 Service::PSC::Time::CalendarTime calendar_time{};
189 Service::PSC::Time::CalendarAdditionalInfo additional_info{};
190 auto res = ToCalendarTime(calendar_time, additional_info, time, rule);
191
192 IPC::ResponseBuilder rb{ctx,
193 2 + (sizeof(Service::PSC::Time::CalendarTime) / sizeof(u32)) +
194 (sizeof(Service::PSC::Time::CalendarAdditionalInfo) / sizeof(u32))};
195 rb.Push(res);
196 rb.PushRaw<Service::PSC::Time::CalendarTime>(calendar_time);
197 rb.PushRaw<Service::PSC::Time::CalendarAdditionalInfo>(additional_info);
198}
199
200void TimeZoneService::Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx) {
201 IPC::RequestParser rp{ctx};
202 auto time{rp.Pop<s64>()};
203
204 LOG_DEBUG(Service_Time, "called. time={}", time);
205
206 Service::PSC::Time::CalendarTime calendar_time{};
207 Service::PSC::Time::CalendarAdditionalInfo additional_info{};
208 auto res = ToCalendarTimeWithMyRule(calendar_time, additional_info, time);
209
210 IPC::ResponseBuilder rb{ctx,
211 2 + (sizeof(Service::PSC::Time::CalendarTime) / sizeof(u32)) +
212 (sizeof(Service::PSC::Time::CalendarAdditionalInfo) / sizeof(u32))};
213 rb.Push(res);
214 rb.PushRaw<Service::PSC::Time::CalendarTime>(calendar_time);
215 rb.PushRaw<Service::PSC::Time::CalendarAdditionalInfo>(additional_info);
216}
217
218void TimeZoneService::Handle_ToPosixTime(HLERequestContext& ctx) {
219 IPC::RequestParser rp{ctx};
220 auto calendar{rp.PopRaw<Service::PSC::Time::CalendarTime>()};
221
222 LOG_DEBUG(Service_Time, "called. calendar year {} month {} day {} hour {} minute {} second {}",
223 calendar.year, calendar.month, calendar.day, calendar.hour, calendar.minute,
224 calendar.second);
225
226 auto binary{ctx.ReadBuffer()};
227
228 Tz::Rule rule{};
229 std::memcpy(&rule, binary.data(), sizeof(Tz::Rule));
230
231 u32 count{};
232 std::array<s64, 2> times{};
233 u32 times_count{static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(s64))};
234
235 auto res = ToPosixTime(count, times, times_count, calendar, rule);
236
237 ctx.WriteBuffer(times);
238
239 IPC::ResponseBuilder rb{ctx, 3};
240 rb.Push(res);
241 rb.Push(count);
242}
243
244void TimeZoneService::Handle_ToPosixTimeWithMyRule(HLERequestContext& ctx) {
245 LOG_DEBUG(Service_Time, "called.");
246
247 IPC::RequestParser rp{ctx};
248 auto calendar{rp.PopRaw<Service::PSC::Time::CalendarTime>()};
249
250 u32 count{};
251 std::array<s64, 2> times{};
252 u32 times_count{static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(s64))};
253
254 auto res = ToPosixTimeWithMyRule(count, times, times_count, calendar);
255
256 ctx.WriteBuffer(times);
257
258 IPC::ResponseBuilder rb{ctx, 3};
259 rb.Push(res);
260 rb.Push(count);
261}
262
263// =============================== Implementations ===========================
264
265Result TimeZoneService::GetDeviceLocationName(Service::PSC::Time::LocationName& out_location_name) {
266 R_RETURN(m_wrapped_service->GetDeviceLocationName(out_location_name));
267}
268
269Result TimeZoneService::SetDeviceLocation(Service::PSC::Time::LocationName& location_name) {
270 R_UNLESS(m_can_write_timezone_device_location, Service::PSC::Time::ResultPermissionDenied);
271 R_UNLESS(IsTimeZoneBinaryValid(location_name), Service::PSC::Time::ResultTimeZoneNotFound);
272
273 std::scoped_lock l{m_mutex};
274
275 std::span<const u8> binary{};
276 size_t binary_size{};
277 R_TRY(GetTimeZoneRule(binary, binary_size, location_name))
278
279 R_TRY(m_wrapped_service->SetDeviceLocationNameWithTimeZoneRule(location_name, binary));
280
281 m_file_timestamp_worker.SetFilesystemPosixTime();
282
283 Service::PSC::Time::SteadyClockTimePoint time_point{};
284 Service::PSC::Time::LocationName name{};
285 R_TRY(m_wrapped_service->GetDeviceLocationNameAndUpdatedTime(time_point, name));
286
287 m_set_sys->SetDeviceTimeZoneLocationName(name);
288 m_set_sys->SetDeviceTimeZoneLocationUpdatedTime(time_point);
289
290 std::scoped_lock m{g_list_mutex};
291 for (auto& operation_event : g_list_nodes) {
292 operation_event.m_event->Signal();
293 }
294 R_SUCCEED();
295}
296
297Result TimeZoneService::GetTotalLocationNameCount(u32& out_count) {
298 R_RETURN(m_wrapped_service->GetTotalLocationNameCount(out_count));
299}
300
301Result TimeZoneService::LoadLocationNameList(
302 u32& out_count, std::vector<Service::PSC::Time::LocationName>& out_names, size_t max_names,
303 u32 index) {
304 std::scoped_lock l{m_mutex};
305 R_RETURN(GetTimeZoneLocationList(out_count, out_names, max_names, index));
306}
307
308Result TimeZoneService::LoadTimeZoneRule(Tz::Rule& out_rule,
309 Service::PSC::Time::LocationName& name) {
310 std::scoped_lock l{m_mutex};
311 std::span<const u8> binary{};
312 size_t binary_size{};
313 R_TRY(GetTimeZoneRule(binary, binary_size, name))
314 R_RETURN(m_wrapped_service->ParseTimeZoneBinary(out_rule, binary));
315}
316
317Result TimeZoneService::GetTimeZoneRuleVersion(Service::PSC::Time::RuleVersion& out_rule_version) {
318 R_RETURN(m_wrapped_service->GetTimeZoneRuleVersion(out_rule_version));
319}
320
321Result TimeZoneService::GetDeviceLocationNameAndUpdatedTime(
322 Service::PSC::Time::SteadyClockTimePoint& out_time_point,
323 Service::PSC::Time::LocationName& location_name) {
324 R_RETURN(m_wrapped_service->GetDeviceLocationNameAndUpdatedTime(out_time_point, location_name));
325}
326
327Result TimeZoneService::SetDeviceLocationNameWithTimeZoneRule() {
328 R_UNLESS(m_can_write_timezone_device_location, Service::PSC::Time::ResultPermissionDenied);
329 R_RETURN(Service::PSC::Time::ResultNotImplemented);
330}
331
332Result TimeZoneService::GetDeviceLocationNameOperationEventReadableHandle(
333 Kernel::KEvent** out_event) {
334 if (!operation_event_initialized) {
335 operation_event_initialized = false;
336
337 m_operation_event.m_ctx.CloseEvent(m_operation_event.m_event);
338 m_operation_event.m_event =
339 m_operation_event.m_ctx.CreateEvent("Psc:TimeZoneService:OperationEvent");
340 operation_event_initialized = true;
341 std::scoped_lock l{m_mutex};
342 g_list_nodes.push_back(m_operation_event);
343 }
344
345 *out_event = m_operation_event.m_event;
346 R_SUCCEED();
347}
348
349Result TimeZoneService::ToCalendarTime(
350 Service::PSC::Time::CalendarTime& out_calendar_time,
351 Service::PSC::Time::CalendarAdditionalInfo& out_additional_info, s64 time, Tz::Rule& rule) {
352 R_RETURN(m_wrapped_service->ToCalendarTime(out_calendar_time, out_additional_info, time, rule));
353}
354
355Result TimeZoneService::ToCalendarTimeWithMyRule(
356 Service::PSC::Time::CalendarTime& out_calendar_time,
357 Service::PSC::Time::CalendarAdditionalInfo& out_additional_info, s64 time) {
358 R_RETURN(
359 m_wrapped_service->ToCalendarTimeWithMyRule(out_calendar_time, out_additional_info, time));
360}
361
362Result TimeZoneService::ToPosixTime(u32& out_count, std::span<s64, 2> out_times,
363 u32 out_times_count,
364 Service::PSC::Time::CalendarTime& calendar_time,
365 Tz::Rule& rule) {
366 R_RETURN(
367 m_wrapped_service->ToPosixTime(out_count, out_times, out_times_count, calendar_time, rule));
368}
369
370Result TimeZoneService::ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times,
371 u32 out_times_count,
372 Service::PSC::Time::CalendarTime& calendar_time) {
373 R_RETURN(m_wrapped_service->ToPosixTimeWithMyRule(out_count, out_times, out_times_count,
374 calendar_time));
375}
376
377} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/time_zone.h b/src/core/hle/service/glue/time/time_zone.h
new file mode 100644
index 000000000..3c8ae4bf8
--- /dev/null
+++ b/src/core/hle/service/glue/time/time_zone.h
@@ -0,0 +1,95 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <mutex>
8#include <span>
9#include <vector>
10
11#include "core/hle/service/ipc_helpers.h"
12#include "core/hle/service/psc/time/common.h"
13#include "core/hle/service/server_manager.h"
14#include "core/hle/service/service.h"
15
16namespace Core {
17class System;
18}
19
20namespace Tz {
21struct Rule;
22}
23
24namespace Service::Set {
25class ISystemSettingsServer;
26}
27
28namespace Service::PSC::Time {
29class TimeZoneService;
30}
31
32namespace Service::Glue::Time {
33class FileTimestampWorker;
34
35class TimeZoneService final : public ServiceFramework<TimeZoneService> {
36public:
37 explicit TimeZoneService(
38 Core::System& system, FileTimestampWorker& file_timestamp_worker,
39 bool can_write_timezone_device_location,
40 std::shared_ptr<Service::PSC::Time::TimeZoneService> time_zone_service);
41
42 ~TimeZoneService() override;
43
44 Result GetDeviceLocationName(Service::PSC::Time::LocationName& out_location_name);
45 Result SetDeviceLocation(Service::PSC::Time::LocationName& location_name);
46 Result GetTotalLocationNameCount(u32& out_count);
47 Result LoadLocationNameList(u32& out_count,
48 std::vector<Service::PSC::Time::LocationName>& out_names,
49 size_t max_names, u32 index);
50 Result LoadTimeZoneRule(Tz::Rule& out_rule, Service::PSC::Time::LocationName& name);
51 Result GetTimeZoneRuleVersion(Service::PSC::Time::RuleVersion& out_rule_version);
52 Result GetDeviceLocationNameAndUpdatedTime(
53 Service::PSC::Time::SteadyClockTimePoint& out_time_point,
54 Service::PSC::Time::LocationName& location_name);
55 Result SetDeviceLocationNameWithTimeZoneRule();
56 Result GetDeviceLocationNameOperationEventReadableHandle(Kernel::KEvent** out_event);
57 Result ToCalendarTime(Service::PSC::Time::CalendarTime& out_calendar_time,
58 Service::PSC::Time::CalendarAdditionalInfo& out_additional_info, s64 time,
59 Tz::Rule& rule);
60 Result ToCalendarTimeWithMyRule(Service::PSC::Time::CalendarTime& out_calendar_time,
61 Service::PSC::Time::CalendarAdditionalInfo& out_additional_info,
62 s64 time);
63 Result ToPosixTime(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
64 Service::PSC::Time::CalendarTime& calendar_time, Tz::Rule& rule);
65 Result ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
66 Service::PSC::Time::CalendarTime& calendar_time);
67
68private:
69 void Handle_GetDeviceLocationName(HLERequestContext& ctx);
70 void Handle_SetDeviceLocationName(HLERequestContext& ctx);
71 void Handle_GetTotalLocationNameCount(HLERequestContext& ctx);
72 void Handle_LoadLocationNameList(HLERequestContext& ctx);
73 void Handle_LoadTimeZoneRule(HLERequestContext& ctx);
74 void Handle_GetTimeZoneRuleVersion(HLERequestContext& ctx);
75 void Handle_GetDeviceLocationNameAndUpdatedTime(HLERequestContext& ctx);
76 void Handle_SetDeviceLocationNameWithTimeZoneRule(HLERequestContext& ctx);
77 void Handle_ParseTimeZoneBinary(HLERequestContext& ctx);
78 void Handle_GetDeviceLocationNameOperationEventReadableHandle(HLERequestContext& ctx);
79 void Handle_ToCalendarTime(HLERequestContext& ctx);
80 void Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx);
81 void Handle_ToPosixTime(HLERequestContext& ctx);
82 void Handle_ToPosixTimeWithMyRule(HLERequestContext& ctx);
83
84 Core::System& m_system;
85 std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
86
87 bool m_can_write_timezone_device_location;
88 FileTimestampWorker& m_file_timestamp_worker;
89 std::shared_ptr<Service::PSC::Time::TimeZoneService> m_wrapped_service;
90 std::mutex m_mutex;
91 bool operation_event_initialized{};
92 Service::PSC::Time::OperationEvent m_operation_event;
93};
94
95} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/time_zone_binary.cpp b/src/core/hle/service/glue/time/time_zone_binary.cpp
new file mode 100644
index 000000000..67969aa3f
--- /dev/null
+++ b/src/core/hle/service/glue/time/time_zone_binary.cpp
@@ -0,0 +1,221 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/file_sys/content_archive.h"
6#include "core/file_sys/nca_metadata.h"
7#include "core/file_sys/registered_cache.h"
8#include "core/file_sys/romfs.h"
9#include "core/file_sys/system_archive/system_archive.h"
10#include "core/file_sys/vfs.h"
11#include "core/hle/service/filesystem/filesystem.h"
12#include "core/hle/service/glue/time/time_zone_binary.h"
13
14namespace Service::Glue::Time {
15namespace {
16constexpr u64 TimeZoneBinaryId = 0x10000000000080E;
17
18static FileSys::VirtualDir g_time_zone_binary_romfs{};
19static Result g_time_zone_binary_mount_result{ResultUnknown};
20static std::vector<u8> g_time_zone_scratch_space(0x2800, 0);
21
22Result TimeZoneReadBinary(size_t& out_read_size, std::span<u8> out_buffer, size_t out_buffer_size,
23 std::string_view path) {
24 R_UNLESS(g_time_zone_binary_mount_result == ResultSuccess, g_time_zone_binary_mount_result);
25
26 auto vfs_file{g_time_zone_binary_romfs->GetFileRelative(path)};
27 R_UNLESS(vfs_file, ResultUnknown);
28
29 auto file_size{vfs_file->GetSize()};
30 R_UNLESS(file_size > 0, ResultUnknown);
31
32 R_UNLESS(file_size <= out_buffer_size, Service::PSC::Time::ResultFailed);
33
34 out_read_size = vfs_file->Read(out_buffer.data(), file_size);
35 R_UNLESS(out_read_size > 0, ResultUnknown);
36
37 R_SUCCEED();
38}
39} // namespace
40
41void ResetTimeZoneBinary() {
42 g_time_zone_binary_romfs = {};
43 g_time_zone_binary_mount_result = ResultUnknown;
44 g_time_zone_scratch_space.clear();
45 g_time_zone_scratch_space.resize(0x2800, 0);
46}
47
48Result MountTimeZoneBinary(Core::System& system) {
49 ResetTimeZoneBinary();
50
51 auto& fsc{system.GetFileSystemController()};
52 std::unique_ptr<FileSys::NCA> nca{};
53
54 auto* bis_system = fsc.GetSystemNANDContents();
55
56 R_UNLESS(bis_system, ResultUnknown);
57
58 nca = bis_system->GetEntry(TimeZoneBinaryId, FileSys::ContentRecordType::Data);
59
60 if (nca) {
61 g_time_zone_binary_romfs = FileSys::ExtractRomFS(nca->GetRomFS());
62 }
63
64 if (g_time_zone_binary_romfs) {
65 // Validate that the romfs is readable, using invalid firmware keys can cause this to get
66 // set but the files to be garbage. In that case, we want to hit the next path and
67 // synthesise them instead.
68 Service::PSC::Time::LocationName name{"Etc/GMT"};
69 if (!IsTimeZoneBinaryValid(name)) {
70 ResetTimeZoneBinary();
71 }
72 }
73
74 if (!g_time_zone_binary_romfs) {
75 g_time_zone_binary_romfs = FileSys::ExtractRomFS(
76 FileSys::SystemArchive::SynthesizeSystemArchive(TimeZoneBinaryId));
77 }
78
79 R_UNLESS(g_time_zone_binary_romfs, ResultUnknown);
80
81 g_time_zone_binary_mount_result = ResultSuccess;
82 R_SUCCEED();
83}
84
85void GetTimeZoneBinaryListPath(std::string& out_path) {
86 if (g_time_zone_binary_mount_result != ResultSuccess) {
87 return;
88 }
89 // out_path = fmt::format("{}:/binaryList.txt", "TimeZoneBinary");
90 out_path = "/binaryList.txt";
91}
92
93void GetTimeZoneBinaryVersionPath(std::string& out_path) {
94 if (g_time_zone_binary_mount_result != ResultSuccess) {
95 return;
96 }
97 // out_path = fmt::format("{}:/version.txt", "TimeZoneBinary");
98 out_path = "/version.txt";
99}
100
101void GetTimeZoneZonePath(std::string& out_path, Service::PSC::Time::LocationName& name) {
102 if (g_time_zone_binary_mount_result != ResultSuccess) {
103 return;
104 }
105 // out_path = fmt::format("{}:/zoneinfo/{}", "TimeZoneBinary", name);
106 out_path = fmt::format("/zoneinfo/{}", name.name.data());
107}
108
109bool IsTimeZoneBinaryValid(Service::PSC::Time::LocationName& name) {
110 std::string path{};
111 GetTimeZoneZonePath(path, name);
112
113 auto vfs_file{g_time_zone_binary_romfs->GetFileRelative(path)};
114 if (!vfs_file) {
115 LOG_INFO(Service_Time, "Could not find timezone file {}", path);
116 return false;
117 }
118 return vfs_file->GetSize() != 0;
119}
120
121u32 GetTimeZoneCount() {
122 std::string path{};
123 GetTimeZoneBinaryListPath(path);
124
125 size_t bytes_read{};
126 if (TimeZoneReadBinary(bytes_read, g_time_zone_scratch_space, 0x2800, path) != ResultSuccess) {
127 return 0;
128 }
129 if (bytes_read == 0) {
130 return 0;
131 }
132
133 auto chars = std::span(reinterpret_cast<char*>(g_time_zone_scratch_space.data()), bytes_read);
134 u32 count{};
135 for (auto chr : chars) {
136 if (chr == '\n') {
137 count++;
138 }
139 }
140 return count;
141}
142
143Result GetTimeZoneVersion(Service::PSC::Time::RuleVersion& out_rule_version) {
144 std::string path{};
145 GetTimeZoneBinaryVersionPath(path);
146
147 auto rule_version_buffer{std::span(reinterpret_cast<u8*>(&out_rule_version),
148 sizeof(Service::PSC::Time::RuleVersion))};
149 size_t bytes_read{};
150 R_TRY(TimeZoneReadBinary(bytes_read, rule_version_buffer, rule_version_buffer.size_bytes(),
151 path));
152
153 rule_version_buffer[bytes_read] = 0;
154 R_SUCCEED();
155}
156
157Result GetTimeZoneRule(std::span<const u8>& out_rule, size_t& out_rule_size,
158 Service::PSC::Time::LocationName& name) {
159 std::string path{};
160 GetTimeZoneZonePath(path, name);
161
162 size_t bytes_read{};
163 R_TRY(TimeZoneReadBinary(bytes_read, g_time_zone_scratch_space,
164 g_time_zone_scratch_space.size(), path));
165
166 out_rule = std::span(g_time_zone_scratch_space.data(), bytes_read);
167 out_rule_size = bytes_read;
168 R_SUCCEED();
169}
170
171Result GetTimeZoneLocationList(u32& out_count,
172 std::vector<Service::PSC::Time::LocationName>& out_names,
173 size_t max_names, u32 index) {
174 std::string path{};
175 GetTimeZoneBinaryListPath(path);
176
177 size_t bytes_read{};
178 R_TRY(TimeZoneReadBinary(bytes_read, g_time_zone_scratch_space,
179 g_time_zone_scratch_space.size(), path));
180
181 out_count = 0;
182 R_SUCCEED_IF(bytes_read == 0);
183
184 Service::PSC::Time::LocationName current_name{};
185 size_t current_name_len{};
186 std::span<const u8> chars{g_time_zone_scratch_space};
187 u32 name_count{};
188
189 for (auto chr : chars) {
190 if (chr == '\r') {
191 continue;
192 }
193
194 if (chr == '\n') {
195 if (name_count >= index) {
196 out_names.push_back(current_name);
197 out_count++;
198 if (out_count >= max_names) {
199 break;
200 }
201 }
202 name_count++;
203 current_name_len = 0;
204 current_name = {};
205 continue;
206 }
207
208 if (chr == '\0') {
209 break;
210 }
211
212 R_UNLESS(current_name_len <= current_name.name.size() - 2,
213 Service::PSC::Time::ResultFailed);
214
215 current_name.name[current_name_len++] = chr;
216 }
217
218 R_SUCCEED();
219}
220
221} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/time_zone_binary.h b/src/core/hle/service/glue/time/time_zone_binary.h
new file mode 100644
index 000000000..2cad6b458
--- /dev/null
+++ b/src/core/hle/service/glue/time/time_zone_binary.h
@@ -0,0 +1,32 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <span>
7#include <string>
8#include <string_view>
9
10#include "core/hle/service/psc/time/common.h"
11
12namespace Core {
13class System;
14}
15
16namespace Service::Glue::Time {
17
18void ResetTimeZoneBinary();
19Result MountTimeZoneBinary(Core::System& system);
20void GetTimeZoneBinaryListPath(std::string& out_path);
21void GetTimeZoneBinaryVersionPath(std::string& out_path);
22void GetTimeZoneZonePath(std::string& out_path, Service::PSC::Time::LocationName& name);
23bool IsTimeZoneBinaryValid(Service::PSC::Time::LocationName& name);
24u32 GetTimeZoneCount();
25Result GetTimeZoneVersion(Service::PSC::Time::RuleVersion& out_rule_version);
26Result GetTimeZoneRule(std::span<const u8>& out_rule, size_t& out_rule_size,
27 Service::PSC::Time::LocationName& name);
28Result GetTimeZoneLocationList(u32& out_count,
29 std::vector<Service::PSC::Time::LocationName>& out_names,
30 size_t max_names, u32 index);
31
32} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/worker.cpp b/src/core/hle/service/glue/time/worker.cpp
new file mode 100644
index 000000000..ea0e49b90
--- /dev/null
+++ b/src/core/hle/service/glue/time/worker.cpp
@@ -0,0 +1,338 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/core_timing.h"
7#include "core/hle/service/glue/time/file_timestamp_worker.h"
8#include "core/hle/service/glue/time/standard_steady_clock_resource.h"
9#include "core/hle/service/glue/time/worker.h"
10#include "core/hle/service/psc/time/common.h"
11#include "core/hle/service/psc/time/service_manager.h"
12#include "core/hle/service/psc/time/static.h"
13#include "core/hle/service/psc/time/system_clock.h"
14#include "core/hle/service/set/system_settings_server.h"
15#include "core/hle/service/sm/sm.h"
16
17namespace Service::Glue::Time {
18namespace {
19
20bool g_ig_report_network_clock_context_set{};
21Service::PSC::Time::SystemClockContext g_report_network_clock_context{};
22bool g_ig_report_ephemeral_clock_context_set{};
23Service::PSC::Time::SystemClockContext g_report_ephemeral_clock_context{};
24
25template <typename T>
26T GetSettingsItemValue(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys,
27 const char* category, const char* name) {
28 std::vector<u8> interval_buf;
29 auto res = set_sys->GetSettingsItemValue(interval_buf, category, name);
30 ASSERT(res == ResultSuccess);
31
32 T v{};
33 std::memcpy(&v, interval_buf.data(), sizeof(T));
34 return v;
35}
36
37} // namespace
38
39TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource,
40 FileTimestampWorker& file_timestamp_worker)
41 : m_system{system}, m_ctx{m_system, "Glue:58"}, m_event{m_ctx.CreateEvent("Glue:58:Event")},
42 m_steady_clock_resource{steady_clock_resource},
43 m_file_timestamp_worker{file_timestamp_worker}, m_timer_steady_clock{m_ctx.CreateEvent(
44 "Glue:58:SteadyClockTimerEvent")},
45 m_timer_file_system{m_ctx.CreateEvent("Glue:58:FileTimeTimerEvent")},
46 m_alarm_worker{m_system, m_steady_clock_resource}, m_pm_state_change_handler{m_alarm_worker} {
47 g_ig_report_network_clock_context_set = false;
48 g_report_network_clock_context = {};
49 g_ig_report_ephemeral_clock_context_set = false;
50 g_report_ephemeral_clock_context = {};
51
52 m_timer_steady_clock_timing_event = Core::Timing::CreateEvent(
53 "Time::SteadyClockEvent",
54 [this](s64 time,
55 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
56 m_timer_steady_clock->Signal();
57 return std::nullopt;
58 });
59
60 m_timer_file_system_timing_event = Core::Timing::CreateEvent(
61 "Time::SteadyClockEvent",
62 [this](s64 time,
63 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
64 m_timer_file_system->Signal();
65 return std::nullopt;
66 });
67}
68
69TimeWorker::~TimeWorker() {
70 m_local_clock_event->Signal();
71 m_network_clock_event->Signal();
72 m_ephemeral_clock_event->Signal();
73 std::this_thread::sleep_for(std::chrono::milliseconds(16));
74
75 m_thread.request_stop();
76 m_event->Signal();
77 m_thread.join();
78
79 m_ctx.CloseEvent(m_event);
80 m_system.CoreTiming().UnscheduleEvent(m_timer_steady_clock_timing_event);
81 m_ctx.CloseEvent(m_timer_steady_clock);
82 m_system.CoreTiming().UnscheduleEvent(m_timer_file_system_timing_event);
83 m_ctx.CloseEvent(m_timer_file_system);
84}
85
86void TimeWorker::Initialize(std::shared_ptr<Service::PSC::Time::StaticService> time_sm,
87 std::shared_ptr<Service::Set::ISystemSettingsServer> set_sys) {
88 m_set_sys = std::move(set_sys);
89 m_time_m =
90 m_system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true);
91 m_time_sm = std::move(time_sm);
92
93 m_alarm_worker.Initialize(m_time_m);
94
95 auto steady_clock_interval_m = GetSettingsItemValue<s32>(
96 m_set_sys, "time", "standard_steady_clock_rtc_update_interval_minutes");
97
98 auto one_minute_ns{
99 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::minutes(1)).count()};
100 s64 steady_clock_interval_ns{steady_clock_interval_m * one_minute_ns};
101
102 m_system.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds(0),
103 std::chrono::nanoseconds(steady_clock_interval_ns),
104 m_timer_steady_clock_timing_event);
105
106 auto fs_notify_time_s =
107 GetSettingsItemValue<s32>(m_set_sys, "time", "notify_time_to_fs_interval_seconds");
108 auto one_second_ns{
109 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
110 s64 fs_notify_time_ns{fs_notify_time_s * one_second_ns};
111
112 m_system.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds(0),
113 std::chrono::nanoseconds(fs_notify_time_ns),
114 m_timer_file_system_timing_event);
115
116 auto res = m_time_sm->GetStandardLocalSystemClock(m_local_clock);
117 ASSERT(res == ResultSuccess);
118 res = m_time_m->GetStandardLocalClockOperationEvent(&m_local_clock_event);
119 ASSERT(res == ResultSuccess);
120
121 res = m_time_sm->GetStandardNetworkSystemClock(m_network_clock);
122 ASSERT(res == ResultSuccess);
123 res = m_time_m->GetStandardNetworkClockOperationEventForServiceManager(&m_network_clock_event);
124 ASSERT(res == ResultSuccess);
125
126 res = m_time_sm->GetEphemeralNetworkSystemClock(m_ephemeral_clock);
127 ASSERT(res == ResultSuccess);
128 res =
129 m_time_m->GetEphemeralNetworkClockOperationEventForServiceManager(&m_ephemeral_clock_event);
130 ASSERT(res == ResultSuccess);
131
132 res = m_time_m->GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(
133 &m_standard_user_auto_correct_clock_event);
134 ASSERT(res == ResultSuccess);
135}
136
137void TimeWorker::StartThread() {
138 m_thread = std::jthread(std::bind_front(&TimeWorker::ThreadFunc, this));
139}
140
141void TimeWorker::ThreadFunc(std::stop_token stop_token) {
142 Common::SetCurrentThreadName("TimeWorker");
143 Common::SetCurrentThreadPriority(Common::ThreadPriority::Low);
144
145 enum class EventType {
146 Exit = 0,
147 IpmModuleService_GetEvent = 1,
148 PowerStateChange = 2,
149 SignalAlarms = 3,
150 UpdateLocalSystemClock = 4,
151 UpdateNetworkSystemClock = 5,
152 UpdateEphemeralSystemClock = 6,
153 UpdateSteadyClock = 7,
154 UpdateFileTimestamp = 8,
155 AutoCorrect = 9,
156 Max = 10,
157 };
158
159 s32 num_objs{};
160 std::array<Kernel::KSynchronizationObject*, static_cast<u32>(EventType::Max)> wait_objs{};
161 std::array<EventType, static_cast<u32>(EventType::Max)> wait_indices{};
162
163 const auto AddWaiter{
164 [&](Kernel::KSynchronizationObject* synchronization_object, EventType type) {
165 // Open a new reference to the object.
166 synchronization_object->Open();
167
168 // Insert into the list.
169 wait_indices[num_objs] = type;
170 wait_objs[num_objs++] = synchronization_object;
171 }};
172
173 while (!stop_token.stop_requested()) {
174 SCOPE_EXIT({
175 for (s32 i = 0; i < num_objs; i++) {
176 wait_objs[i]->Close();
177 }
178 });
179
180 num_objs = {};
181 wait_objs = {};
182 if (m_pm_state_change_handler.m_priority != 0) {
183 AddWaiter(&m_event->GetReadableEvent(), EventType::Exit);
184 // TODO
185 // AddWaiter(gIPmModuleService::GetEvent(), 1);
186 AddWaiter(&m_alarm_worker.GetEvent().GetReadableEvent(), EventType::PowerStateChange);
187 } else {
188 AddWaiter(&m_event->GetReadableEvent(), EventType::Exit);
189 // TODO
190 // AddWaiter(gIPmModuleService::GetEvent(), 1);
191 AddWaiter(&m_alarm_worker.GetEvent().GetReadableEvent(), EventType::PowerStateChange);
192 AddWaiter(&m_alarm_worker.GetTimerEvent().GetReadableEvent(), EventType::SignalAlarms);
193 AddWaiter(&m_local_clock_event->GetReadableEvent(), EventType::UpdateLocalSystemClock);
194 AddWaiter(&m_network_clock_event->GetReadableEvent(),
195 EventType::UpdateNetworkSystemClock);
196 AddWaiter(&m_ephemeral_clock_event->GetReadableEvent(),
197 EventType::UpdateEphemeralSystemClock);
198 AddWaiter(&m_timer_steady_clock->GetReadableEvent(), EventType::UpdateSteadyClock);
199 AddWaiter(&m_timer_file_system->GetReadableEvent(), EventType::UpdateFileTimestamp);
200 AddWaiter(&m_standard_user_auto_correct_clock_event->GetReadableEvent(),
201 EventType::AutoCorrect);
202 }
203
204 s32 out_index{-1};
205 Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index, wait_objs.data(),
206 num_objs, -1);
207 ASSERT(out_index >= 0 && out_index < num_objs);
208
209 if (stop_token.stop_requested()) {
210 return;
211 }
212
213 switch (wait_indices[out_index]) {
214 case EventType::Exit:
215 return;
216
217 case EventType::IpmModuleService_GetEvent:
218 // TODO
219 // IPmModuleService::GetEvent()
220 // clear the event
221 // Handle power state change event
222 break;
223
224 case EventType::PowerStateChange:
225 m_alarm_worker.GetEvent().Clear();
226 if (m_pm_state_change_handler.m_priority <= 1) {
227 m_alarm_worker.OnPowerStateChanged();
228 }
229 break;
230
231 case EventType::SignalAlarms:
232 m_alarm_worker.GetTimerEvent().Clear();
233 m_time_m->CheckAndSignalAlarms();
234 break;
235
236 case EventType::UpdateLocalSystemClock: {
237 m_local_clock_event->Clear();
238
239 Service::PSC::Time::SystemClockContext context{};
240 auto res = m_local_clock->GetSystemClockContext(context);
241 ASSERT(res == ResultSuccess);
242
243 m_set_sys->SetUserSystemClockContext(context);
244
245 m_file_timestamp_worker.SetFilesystemPosixTime();
246 } break;
247
248 case EventType::UpdateNetworkSystemClock: {
249 m_network_clock_event->Clear();
250 Service::PSC::Time::SystemClockContext context{};
251 auto res = m_network_clock->GetSystemClockContext(context);
252 ASSERT(res == ResultSuccess);
253 m_set_sys->SetNetworkSystemClockContext(context);
254
255 s64 time{};
256 if (m_network_clock->GetCurrentTime(time) != ResultSuccess) {
257 break;
258 }
259
260 [[maybe_unused]] auto offset_before{
261 g_ig_report_network_clock_context_set ? g_report_network_clock_context.offset : 0};
262 // TODO system report "standard_netclock_operation"
263 // "clock_time" = time
264 // "context_offset_before" = offset_before
265 // "context_offset_after" = context.offset
266 g_report_network_clock_context = context;
267 if (!g_ig_report_network_clock_context_set) {
268 g_ig_report_network_clock_context_set = true;
269 }
270
271 m_file_timestamp_worker.SetFilesystemPosixTime();
272 } break;
273
274 case EventType::UpdateEphemeralSystemClock: {
275 m_ephemeral_clock_event->Clear();
276
277 Service::PSC::Time::SystemClockContext context{};
278 auto res = m_ephemeral_clock->GetSystemClockContext(context);
279 if (res != ResultSuccess) {
280 break;
281 }
282
283 s64 time{};
284 res = m_ephemeral_clock->GetCurrentTime(time);
285 if (res != ResultSuccess) {
286 break;
287 }
288
289 [[maybe_unused]] auto offset_before{g_ig_report_ephemeral_clock_context_set
290 ? g_report_ephemeral_clock_context.offset
291 : 0};
292 // TODO system report "ephemeral_netclock_operation"
293 // "clock_time" = time
294 // "context_offset_before" = offset_before
295 // "context_offset_after" = context.offset
296 g_report_ephemeral_clock_context = context;
297 if (!g_ig_report_ephemeral_clock_context_set) {
298 g_ig_report_ephemeral_clock_context_set = true;
299 }
300 } break;
301
302 case EventType::UpdateSteadyClock:
303 m_timer_steady_clock->Clear();
304
305 m_steady_clock_resource.UpdateTime();
306 m_time_m->SetStandardSteadyClockBaseTime(m_steady_clock_resource.GetTime());
307 break;
308
309 case EventType::UpdateFileTimestamp:
310 m_timer_file_system->Clear();
311
312 m_file_timestamp_worker.SetFilesystemPosixTime();
313 break;
314
315 case EventType::AutoCorrect: {
316 m_standard_user_auto_correct_clock_event->Clear();
317
318 bool automatic_correction{};
319 auto res = m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled(
320 automatic_correction);
321 ASSERT(res == ResultSuccess);
322
323 Service::PSC::Time::SteadyClockTimePoint time_point{};
324 res = m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
325 ASSERT(res == ResultSuccess);
326
327 m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction);
328 m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
329 } break;
330
331 default:
332 UNREACHABLE();
333 break;
334 }
335 }
336}
337
338} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/glue/time/worker.h b/src/core/hle/service/glue/time/worker.h
new file mode 100644
index 000000000..adbbe6b6d
--- /dev/null
+++ b/src/core/hle/service/glue/time/worker.h
@@ -0,0 +1,64 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/hle/kernel/k_event.h"
8#include "core/hle/service/glue/time/alarm_worker.h"
9#include "core/hle/service/glue/time/pm_state_change_handler.h"
10#include "core/hle/service/kernel_helpers.h"
11
12namespace Service::Set {
13class ISystemSettingsServer;
14}
15
16namespace Service::PSC::Time {
17class StaticService;
18class SystemClock;
19} // namespace Service::PSC::Time
20
21namespace Service::Glue::Time {
22class FileTimestampWorker;
23class StandardSteadyClockResource;
24
25class TimeWorker {
26public:
27 explicit TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource,
28 FileTimestampWorker& file_timestamp_worker);
29 ~TimeWorker();
30
31 void Initialize(std::shared_ptr<Service::PSC::Time::StaticService> time_sm,
32 std::shared_ptr<Service::Set::ISystemSettingsServer> set_sys);
33
34 void StartThread();
35
36private:
37 void ThreadFunc(std::stop_token stop_token);
38
39 Core::System& m_system;
40 KernelHelpers::ServiceContext m_ctx;
41 std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
42
43 std::jthread m_thread;
44 Kernel::KEvent* m_event{};
45 std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m;
46 std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm;
47 std::shared_ptr<Service::PSC::Time::SystemClock> m_network_clock;
48 std::shared_ptr<Service::PSC::Time::SystemClock> m_local_clock;
49 std::shared_ptr<Service::PSC::Time::SystemClock> m_ephemeral_clock;
50 StandardSteadyClockResource& m_steady_clock_resource;
51 FileTimestampWorker& m_file_timestamp_worker;
52 Kernel::KEvent* m_local_clock_event{};
53 Kernel::KEvent* m_network_clock_event{};
54 Kernel::KEvent* m_ephemeral_clock_event{};
55 Kernel::KEvent* m_standard_user_auto_correct_clock_event{};
56 Kernel::KEvent* m_timer_steady_clock{};
57 std::shared_ptr<Core::Timing::EventType> m_timer_steady_clock_timing_event;
58 Kernel::KEvent* m_timer_file_system{};
59 std::shared_ptr<Core::Timing::EventType> m_timer_file_system_timing_event;
60 AlarmWorker m_alarm_worker;
61 PmStateChangeHandler m_pm_state_change_handler;
62};
63
64} // namespace Service::Glue::Time
diff --git a/src/core/hle/service/kernel_helpers.cpp b/src/core/hle/service/kernel_helpers.cpp
index f51e63564..f080f7ffa 100644
--- a/src/core/hle/service/kernel_helpers.cpp
+++ b/src/core/hle/service/kernel_helpers.cpp
@@ -65,6 +65,9 @@ Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) {
65} 65}
66 66
67void ServiceContext::CloseEvent(Kernel::KEvent* event) { 67void ServiceContext::CloseEvent(Kernel::KEvent* event) {
68 if (!event) {
69 return;
70 }
68 event->GetReadableEvent().Close(); 71 event->GetReadableEvent().Close();
69 event->Close(); 72 event->Close();
70} 73}
diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp
index cc7776efc..1e2d2d212 100644
--- a/src/core/hle/service/nfc/common/device.cpp
+++ b/src/core/hle/service/nfc/common/device.cpp
@@ -1,6 +1,8 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/hle/service/glue/time/static.h"
5#include "core/hle/service/psc/time/steady_clock.h"
4#ifdef _MSC_VER 6#ifdef _MSC_VER
5#pragma warning(push) 7#pragma warning(push)
6#pragma warning(disable : 4701) // Potentially uninitialized local variable 'result' used 8#pragma warning(disable : 4701) // Potentially uninitialized local variable 'result' used
@@ -29,7 +31,8 @@
29#include "core/hle/service/nfc/common/device.h" 31#include "core/hle/service/nfc/common/device.h"
30#include "core/hle/service/nfc/mifare_result.h" 32#include "core/hle/service/nfc/mifare_result.h"
31#include "core/hle/service/nfc/nfc_result.h" 33#include "core/hle/service/nfc/nfc_result.h"
32#include "core/hle/service/time/time_manager.h" 34#include "core/hle/service/service.h"
35#include "core/hle/service/sm/sm.h"
33#include "hid_core/frontend/emulated_controller.h" 36#include "hid_core/frontend/emulated_controller.h"
34#include "hid_core/hid_core.h" 37#include "hid_core/hid_core.h"
35#include "hid_core/hid_types.h" 38#include "hid_core/hid_types.h"
@@ -393,8 +396,7 @@ Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> paramet
393 return result; 396 return result;
394} 397}
395 398
396Result NfcDevice::SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout, 399Result NfcDevice::SendCommandByPassThrough(const s64& timeout, std::span<const u8> command_data,
397 std::span<const u8> command_data,
398 std::span<u8> out_data) { 400 std::span<u8> out_data) {
399 // Not implemented 401 // Not implemented
400 return ResultSuccess; 402 return ResultSuccess;
@@ -1399,27 +1401,41 @@ void NfcDevice::SetAmiiboName(NFP::AmiiboSettings& settings,
1399} 1401}
1400 1402
1401NFP::AmiiboDate NfcDevice::GetAmiiboDate(s64 posix_time) const { 1403NFP::AmiiboDate NfcDevice::GetAmiiboDate(s64 posix_time) const {
1402 const auto& time_zone_manager = 1404 auto static_service =
1403 system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager(); 1405 system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);
1404 Time::TimeZone::CalendarInfo calendar_info{}; 1406
1407 std::shared_ptr<Service::Glue::Time::TimeZoneService> timezone_service{};
1408 static_service->GetTimeZoneService(timezone_service);
1409
1410 Service::PSC::Time::CalendarTime calendar_time{};
1411 Service::PSC::Time::CalendarAdditionalInfo additional_info{};
1412
1405 NFP::AmiiboDate amiibo_date{}; 1413 NFP::AmiiboDate amiibo_date{};
1406 1414
1407 amiibo_date.SetYear(2000); 1415 amiibo_date.SetYear(2000);
1408 amiibo_date.SetMonth(1); 1416 amiibo_date.SetMonth(1);
1409 amiibo_date.SetDay(1); 1417 amiibo_date.SetDay(1);
1410 1418
1411 if (time_zone_manager.ToCalendarTime({}, posix_time, calendar_info) == ResultSuccess) { 1419 if (timezone_service->ToCalendarTimeWithMyRule(calendar_time, additional_info, posix_time) ==
1412 amiibo_date.SetYear(calendar_info.time.year); 1420 ResultSuccess) {
1413 amiibo_date.SetMonth(calendar_info.time.month); 1421 amiibo_date.SetYear(calendar_time.year);
1414 amiibo_date.SetDay(calendar_info.time.day); 1422 amiibo_date.SetMonth(calendar_time.month);
1423 amiibo_date.SetDay(calendar_time.day);
1415 } 1424 }
1416 1425
1417 return amiibo_date; 1426 return amiibo_date;
1418} 1427}
1419 1428
1420u64 NfcDevice::GetCurrentPosixTime() const { 1429s64 NfcDevice::GetCurrentPosixTime() const {
1421 auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()}; 1430 auto static_service =
1422 return standard_steady_clock.GetCurrentTimePoint(system).time_point; 1431 system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);
1432
1433 std::shared_ptr<Service::PSC::Time::SteadyClock> steady_clock{};
1434 static_service->GetStandardSteadyClock(steady_clock);
1435
1436 Service::PSC::Time::SteadyClockTimePoint time_point{};
1437 R_ASSERT(steady_clock->GetCurrentTimePoint(time_point));
1438 return time_point.time_point;
1423} 1439}
1424 1440
1425u64 NfcDevice::RemoveVersionByte(u64 application_id) const { 1441u64 NfcDevice::RemoveVersionByte(u64 application_id) const {
diff --git a/src/core/hle/service/nfc/common/device.h b/src/core/hle/service/nfc/common/device.h
index 15f9b25da..d59202d18 100644
--- a/src/core/hle/service/nfc/common/device.h
+++ b/src/core/hle/service/nfc/common/device.h
@@ -11,7 +11,6 @@
11#include "core/hle/service/nfc/nfc_types.h" 11#include "core/hle/service/nfc/nfc_types.h"
12#include "core/hle/service/nfp/nfp_types.h" 12#include "core/hle/service/nfp/nfp_types.h"
13#include "core/hle/service/service.h" 13#include "core/hle/service/service.h"
14#include "core/hle/service/time/clock_types.h"
15 14
16namespace Kernel { 15namespace Kernel {
17class KEvent; 16class KEvent;
@@ -49,8 +48,8 @@ public:
49 48
50 Result WriteMifare(std::span<const MifareWriteBlockParameter> parameters); 49 Result WriteMifare(std::span<const MifareWriteBlockParameter> parameters);
51 50
52 Result SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout, 51 Result SendCommandByPassThrough(const s64& timeout, std::span<const u8> command_data,
53 std::span<const u8> command_data, std::span<u8> out_data); 52 std::span<u8> out_data);
54 53
55 Result Mount(NFP::ModelType model_type, NFP::MountTarget mount_target); 54 Result Mount(NFP::ModelType model_type, NFP::MountTarget mount_target);
56 Result Unmount(); 55 Result Unmount();
@@ -108,7 +107,7 @@ private:
108 NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const; 107 NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const;
109 void SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name) const; 108 void SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name) const;
110 NFP::AmiiboDate GetAmiiboDate(s64 posix_time) const; 109 NFP::AmiiboDate GetAmiiboDate(s64 posix_time) const;
111 u64 GetCurrentPosixTime() const; 110 s64 GetCurrentPosixTime() const;
112 u64 RemoveVersionByte(u64 application_id) const; 111 u64 RemoveVersionByte(u64 application_id) const;
113 void UpdateSettingsCrc(); 112 void UpdateSettingsCrc();
114 void UpdateRegisterInfoCrc(); 113 void UpdateRegisterInfoCrc();
diff --git a/src/core/hle/service/nfc/common/device_manager.cpp b/src/core/hle/service/nfc/common/device_manager.cpp
index 44f651b87..b60699c45 100644
--- a/src/core/hle/service/nfc/common/device_manager.cpp
+++ b/src/core/hle/service/nfc/common/device_manager.cpp
@@ -6,12 +6,14 @@
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/hle/kernel/k_event.h" 8#include "core/hle/kernel/k_event.h"
9#include "core/hle/service/glue/time/static.h"
9#include "core/hle/service/ipc_helpers.h" 10#include "core/hle/service/ipc_helpers.h"
10#include "core/hle/service/nfc/common/device.h" 11#include "core/hle/service/nfc/common/device.h"
11#include "core/hle/service/nfc/common/device_manager.h" 12#include "core/hle/service/nfc/common/device_manager.h"
12#include "core/hle/service/nfc/nfc_result.h" 13#include "core/hle/service/nfc/nfc_result.h"
13#include "core/hle/service/time/clock_types.h" 14#include "core/hle/service/psc/time/steady_clock.h"
14#include "core/hle/service/time/time_manager.h" 15#include "core/hle/service/service.h"
16#include "core/hle/service/sm/sm.h"
15#include "hid_core/hid_types.h" 17#include "hid_core/hid_types.h"
16#include "hid_core/hid_util.h" 18#include "hid_core/hid_util.h"
17 19
@@ -82,11 +84,19 @@ Result DeviceManager::ListDevices(std::vector<u64>& nfp_devices, std::size_t max
82 continue; 84 continue;
83 } 85 }
84 if (skip_fatal_errors) { 86 if (skip_fatal_errors) {
85 constexpr u64 MinimumRecoveryTime = 60; 87 constexpr s64 MinimumRecoveryTime = 60;
86 auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()};
87 const u64 elapsed_time = standard_steady_clock.GetCurrentTimePoint(system).time_point -
88 time_since_last_error;
89 88
89 auto static_service =
90 system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u",
91 true);
92
93 std::shared_ptr<Service::PSC::Time::SteadyClock> steady_clock{};
94 static_service->GetStandardSteadyClock(steady_clock);
95
96 Service::PSC::Time::SteadyClockTimePoint time_point{};
97 R_ASSERT(steady_clock->GetCurrentTimePoint(time_point));
98
99 const s64 elapsed_time = time_point.time_point - time_since_last_error;
90 if (time_since_last_error != 0 && elapsed_time < MinimumRecoveryTime) { 100 if (time_since_last_error != 0 && elapsed_time < MinimumRecoveryTime) {
91 continue; 101 continue;
92 } 102 }
@@ -250,8 +260,7 @@ Result DeviceManager::WriteMifare(u64 device_handle,
250 return result; 260 return result;
251} 261}
252 262
253Result DeviceManager::SendCommandByPassThrough(u64 device_handle, 263Result DeviceManager::SendCommandByPassThrough(u64 device_handle, const s64& timeout,
254 const Time::Clock::TimeSpanType& timeout,
255 std::span<const u8> command_data, 264 std::span<const u8> command_data,
256 std::span<u8> out_data) { 265 std::span<u8> out_data) {
257 std::scoped_lock lock{mutex}; 266 std::scoped_lock lock{mutex};
@@ -741,8 +750,16 @@ Result DeviceManager::VerifyDeviceResult(std::shared_ptr<NfcDevice> device,
741 750
742 if (operation_result == ResultUnknown112 || operation_result == ResultUnknown114 || 751 if (operation_result == ResultUnknown112 || operation_result == ResultUnknown114 ||
743 operation_result == ResultUnknown115) { 752 operation_result == ResultUnknown115) {
744 auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()}; 753 auto static_service =
745 time_since_last_error = standard_steady_clock.GetCurrentTimePoint(system).time_point; 754 system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);
755
756 std::shared_ptr<Service::PSC::Time::SteadyClock> steady_clock{};
757 static_service->GetStandardSteadyClock(steady_clock);
758
759 Service::PSC::Time::SteadyClockTimePoint time_point{};
760 R_ASSERT(steady_clock->GetCurrentTimePoint(time_point));
761
762 time_since_last_error = time_point.time_point;
746 } 763 }
747 764
748 return operation_result; 765 return operation_result;
diff --git a/src/core/hle/service/nfc/common/device_manager.h b/src/core/hle/service/nfc/common/device_manager.h
index f02bdccf5..c56a2fbda 100644
--- a/src/core/hle/service/nfc/common/device_manager.h
+++ b/src/core/hle/service/nfc/common/device_manager.h
@@ -13,7 +13,6 @@
13#include "core/hle/service/nfc/nfc_types.h" 13#include "core/hle/service/nfc/nfc_types.h"
14#include "core/hle/service/nfp/nfp_types.h" 14#include "core/hle/service/nfp/nfp_types.h"
15#include "core/hle/service/service.h" 15#include "core/hle/service/service.h"
16#include "core/hle/service/time/clock_types.h"
17#include "hid_core/hid_types.h" 16#include "hid_core/hid_types.h"
18 17
19namespace Service::NFC { 18namespace Service::NFC {
@@ -42,7 +41,7 @@ public:
42 std::span<MifareReadBlockData> read_data); 41 std::span<MifareReadBlockData> read_data);
43 Result WriteMifare(u64 device_handle, 42 Result WriteMifare(u64 device_handle,
44 std::span<const MifareWriteBlockParameter> write_parameters); 43 std::span<const MifareWriteBlockParameter> write_parameters);
45 Result SendCommandByPassThrough(u64 device_handle, const Time::Clock::TimeSpanType& timeout, 44 Result SendCommandByPassThrough(u64 device_handle, const s64& timeout,
46 std::span<const u8> command_data, std::span<u8> out_data); 45 std::span<const u8> command_data, std::span<u8> out_data);
47 46
48 // Nfp device manager 47 // Nfp device manager
@@ -92,7 +91,7 @@ private:
92 const std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle) const; 91 const std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle) const;
93 92
94 bool is_initialized = false; 93 bool is_initialized = false;
95 u64 time_since_last_error = 0; 94 s64 time_since_last_error = 0;
96 mutable std::mutex mutex; 95 mutable std::mutex mutex;
97 std::array<std::shared_ptr<NfcDevice>, 10> devices{}; 96 std::array<std::shared_ptr<NfcDevice>, 10> devices{};
98 97
diff --git a/src/core/hle/service/nfc/nfc_interface.cpp b/src/core/hle/service/nfc/nfc_interface.cpp
index a71cf74b8..207ac4efe 100644
--- a/src/core/hle/service/nfc/nfc_interface.cpp
+++ b/src/core/hle/service/nfc/nfc_interface.cpp
@@ -13,7 +13,6 @@
13#include "core/hle/service/nfc/nfc_result.h" 13#include "core/hle/service/nfc/nfc_result.h"
14#include "core/hle/service/nfc/nfc_types.h" 14#include "core/hle/service/nfc/nfc_types.h"
15#include "core/hle/service/nfp/nfp_result.h" 15#include "core/hle/service/nfp/nfp_result.h"
16#include "core/hle/service/time/clock_types.h"
17#include "hid_core/hid_types.h" 16#include "hid_core/hid_types.h"
18 17
19namespace Service::NFC { 18namespace Service::NFC {
@@ -261,10 +260,10 @@ void NfcInterface::WriteMifare(HLERequestContext& ctx) {
261void NfcInterface::SendCommandByPassThrough(HLERequestContext& ctx) { 260void NfcInterface::SendCommandByPassThrough(HLERequestContext& ctx) {
262 IPC::RequestParser rp{ctx}; 261 IPC::RequestParser rp{ctx};
263 const auto device_handle{rp.Pop<u64>()}; 262 const auto device_handle{rp.Pop<u64>()};
264 const auto timeout{rp.PopRaw<Time::Clock::TimeSpanType>()}; 263 const auto timeout{rp.PopRaw<s64>()};
265 const auto command_data{ctx.ReadBuffer()}; 264 const auto command_data{ctx.ReadBuffer()};
266 LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, timeout={}, data_size={}", 265 LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, timeout={}, data_size={}",
267 device_handle, timeout.ToSeconds(), command_data.size()); 266 device_handle, timeout, command_data.size());
268 267
269 std::vector<u8> out_data(1); 268 std::vector<u8> out_data(1);
270 auto result = 269 auto result =
diff --git a/src/core/hle/service/ns/language.cpp b/src/core/hle/service/ns/language.cpp
index b1a7686ff..d187be935 100644
--- a/src/core/hle/service/ns/language.cpp
+++ b/src/core/hle/service/ns/language.cpp
@@ -415,4 +415,4 @@ std::optional<Set::LanguageCode> ConvertToLanguageCode(const ApplicationLanguage
415 return std::nullopt; 415 return std::nullopt;
416 } 416 }
417} 417}
418} // namespace Service::NS \ No newline at end of file 418} // namespace Service::NS
diff --git a/src/core/hle/service/ns/language.h b/src/core/hle/service/ns/language.h
index ab6b71029..dad9934f2 100644
--- a/src/core/hle/service/ns/language.h
+++ b/src/core/hle/service/ns/language.h
@@ -5,10 +5,7 @@
5 5
6#include <optional> 6#include <optional>
7#include "common/common_types.h" 7#include "common/common_types.h"
8 8#include "core/hle/service/set/system_settings_server.h"
9namespace Service::Set {
10enum class LanguageCode : u64;
11}
12 9
13namespace Service::NS { 10namespace Service::NS {
14/// This is nn::ns::detail::ApplicationLanguage 11/// This is nn::ns::detail::ApplicationLanguage
diff --git a/src/core/hle/service/psc/psc.cpp b/src/core/hle/service/psc/psc.cpp
index cd0cc9287..44310756b 100644
--- a/src/core/hle/service/psc/psc.cpp
+++ b/src/core/hle/service/psc/psc.cpp
@@ -4,9 +4,13 @@
4#include <memory> 4#include <memory>
5 5
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "core/core.h"
7#include "core/hle/service/ipc_helpers.h" 8#include "core/hle/service/ipc_helpers.h"
8#include "core/hle/service/psc/psc.h" 9#include "core/hle/service/psc/psc.h"
9#include "core/hle/service/server_manager.h" 10#include "core/hle/service/psc/time/manager.h"
11#include "core/hle/service/psc/time/power_state_service.h"
12#include "core/hle/service/psc/time/service_manager.h"
13#include "core/hle/service/psc/time/static.h"
10#include "core/hle/service/service.h" 14#include "core/hle/service/service.h"
11 15
12namespace Service::PSC { 16namespace Service::PSC {
@@ -76,6 +80,17 @@ void LoopProcess(Core::System& system) {
76 80
77 server_manager->RegisterNamedService("psc:c", std::make_shared<IPmControl>(system)); 81 server_manager->RegisterNamedService("psc:c", std::make_shared<IPmControl>(system));
78 server_manager->RegisterNamedService("psc:m", std::make_shared<IPmService>(system)); 82 server_manager->RegisterNamedService("psc:m", std::make_shared<IPmService>(system));
83
84 auto time = std::make_shared<Time::TimeManager>(system);
85
86 server_manager->RegisterNamedService(
87 "time:m", std::make_shared<Time::ServiceManager>(system, time, server_manager.get()));
88 server_manager->RegisterNamedService(
89 "time:su", std::make_shared<Time::StaticService>(
90 system, Time::StaticServiceSetupInfo{0, 0, 0, 0, 0, 1}, time, "time:su"));
91 server_manager->RegisterNamedService("time:al",
92 std::make_shared<Time::IAlarmService>(system, time));
93
79 ServerManager::RunServer(std::move(server_manager)); 94 ServerManager::RunServer(std::move(server_manager));
80} 95}
81 96
diff --git a/src/core/hle/service/psc/time/alarms.cpp b/src/core/hle/service/psc/time/alarms.cpp
new file mode 100644
index 000000000..5e52c19f8
--- /dev/null
+++ b/src/core/hle/service/psc/time/alarms.cpp
@@ -0,0 +1,209 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/service/psc/time/alarms.h"
6#include "core/hle/service/psc/time/manager.h"
7
8namespace Service::PSC::Time {
9Alarm::Alarm(Core::System& system, KernelHelpers::ServiceContext& ctx, AlarmType type)
10 : m_ctx{ctx}, m_event{ctx.CreateEvent("Psc:Alarm:Event")} {
11 m_event->Clear();
12
13 switch (type) {
14 case WakeupAlarm:
15 m_priority = 1;
16 break;
17 case BackgroundTaskAlarm:
18 m_priority = 0;
19 break;
20 default:
21 UNREACHABLE();
22 return;
23 }
24}
25
26Alarm::~Alarm() {
27 m_ctx.CloseEvent(m_event);
28}
29
30Alarms::Alarms(Core::System& system, StandardSteadyClockCore& steady_clock,
31 PowerStateRequestManager& power_state_request_manager)
32 : m_system{system}, m_ctx{system, "Psc:Alarms"}, m_steady_clock{steady_clock},
33 m_power_state_request_manager{power_state_request_manager}, m_event{m_ctx.CreateEvent(
34 "Psc:Alarms:Event")} {}
35
36Alarms::~Alarms() {
37 m_ctx.CloseEvent(m_event);
38}
39
40Result Alarms::Enable(Alarm& alarm, s64 time) {
41 R_UNLESS(m_steady_clock.IsInitialized(), ResultClockUninitialized);
42
43 std::scoped_lock l{m_mutex};
44 R_UNLESS(alarm.IsLinked(), ResultAlarmNotRegistered);
45
46 auto time_ns{time + m_steady_clock.GetRawTime()};
47 auto one_second_ns{
48 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
49 time_ns = Common::AlignUp(time_ns, one_second_ns);
50 alarm.SetAlertTime(time_ns);
51
52 Insert(alarm);
53 R_RETURN(UpdateClosestAndSignal());
54}
55
56void Alarms::Disable(Alarm& alarm) {
57 std::scoped_lock l{m_mutex};
58 if (!alarm.IsLinked()) {
59 return;
60 }
61
62 Erase(alarm);
63 UpdateClosestAndSignal();
64}
65
66void Alarms::CheckAndSignal() {
67 std::scoped_lock l{m_mutex};
68 if (m_alarms.empty()) {
69 return;
70 }
71
72 bool alarm_signalled{false};
73 for (auto& alarm : m_alarms) {
74 if (m_steady_clock.GetRawTime() >= alarm.GetAlertTime()) {
75 alarm.Signal();
76 alarm.Lock();
77 Erase(alarm);
78
79 m_power_state_request_manager.UpdatePendingPowerStateRequestPriority(
80 alarm.GetPriority());
81 alarm_signalled = true;
82 }
83 }
84
85 if (!alarm_signalled) {
86 return;
87 }
88
89 m_power_state_request_manager.SignalPowerStateRequestAvailability();
90 UpdateClosestAndSignal();
91}
92
93bool Alarms::GetClosestAlarm(Alarm** out_alarm) {
94 std::scoped_lock l{m_mutex};
95 auto alarm = m_alarms.empty() ? nullptr : std::addressof(m_alarms.front());
96 *out_alarm = alarm;
97 return alarm != nullptr;
98}
99
100void Alarms::Insert(Alarm& alarm) {
101 // Alarms are sorted by alert time, then priority
102 auto it{m_alarms.begin()};
103 while (it != m_alarms.end()) {
104 if (alarm.GetAlertTime() < it->GetAlertTime() ||
105 (alarm.GetAlertTime() == it->GetAlertTime() &&
106 alarm.GetPriority() < it->GetPriority())) {
107 m_alarms.insert(it, alarm);
108 return;
109 }
110 it++;
111 }
112
113 m_alarms.push_back(alarm);
114}
115
116void Alarms::Erase(Alarm& alarm) {
117 m_alarms.erase(m_alarms.iterator_to(alarm));
118}
119
120Result Alarms::UpdateClosestAndSignal() {
121 m_closest_alarm = m_alarms.empty() ? nullptr : std::addressof(m_alarms.front());
122 R_SUCCEED_IF(m_closest_alarm == nullptr);
123
124 m_event->Signal();
125
126 R_SUCCEED();
127}
128
129IAlarmService::IAlarmService(Core::System& system_, std::shared_ptr<TimeManager> manager)
130 : ServiceFramework{system_, "time:al"}, m_system{system}, m_alarms{manager->m_alarms} {
131 // clang-format off
132 static const FunctionInfo functions[] = {
133 {0, &IAlarmService::CreateWakeupAlarm, "CreateWakeupAlarm"},
134 {1, &IAlarmService::CreateBackgroundTaskAlarm, "CreateBackgroundTaskAlarm"},
135 };
136 // clang-format on
137 RegisterHandlers(functions);
138}
139
140void IAlarmService::CreateWakeupAlarm(HLERequestContext& ctx) {
141 LOG_DEBUG(Service_Time, "called.");
142
143 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
144 rb.Push(ResultSuccess);
145 rb.PushIpcInterface<ISteadyClockAlarm>(system, m_alarms, AlarmType::WakeupAlarm);
146}
147
148void IAlarmService::CreateBackgroundTaskAlarm(HLERequestContext& ctx) {
149 LOG_DEBUG(Service_Time, "called.");
150
151 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
152 rb.Push(ResultSuccess);
153 rb.PushIpcInterface<ISteadyClockAlarm>(system, m_alarms, AlarmType::BackgroundTaskAlarm);
154}
155
156ISteadyClockAlarm::ISteadyClockAlarm(Core::System& system_, Alarms& alarms, AlarmType type)
157 : ServiceFramework{system_, "ISteadyClockAlarm"}, m_ctx{system, "Psc:ISteadyClockAlarm"},
158 m_alarms{alarms}, m_alarm{system, m_ctx, type} {
159 // clang-format off
160 static const FunctionInfo functions[] = {
161 {0, &ISteadyClockAlarm::GetAlarmEvent, "GetAlarmEvent"},
162 {1, &ISteadyClockAlarm::Enable, "Enable"},
163 {2, &ISteadyClockAlarm::Disable, "Disable"},
164 {3, &ISteadyClockAlarm::IsEnabled, "IsEnabled"},
165 {10, nullptr, "CreateWakeLock"},
166 {11, nullptr, "DestroyWakeLock"},
167 };
168 // clang-format on
169 RegisterHandlers(functions);
170}
171
172void ISteadyClockAlarm::GetAlarmEvent(HLERequestContext& ctx) {
173 LOG_DEBUG(Service_Time, "called.");
174
175 IPC::ResponseBuilder rb{ctx, 2, 1};
176 rb.Push(ResultSuccess);
177 rb.PushCopyObjects(m_alarm.GetEventHandle());
178}
179
180void ISteadyClockAlarm::Enable(HLERequestContext& ctx) {
181 LOG_DEBUG(Service_Time, "called.");
182
183 IPC::RequestParser rp{ctx};
184 auto time{rp.Pop<s64>()};
185
186 auto res = m_alarms.Enable(m_alarm, time);
187
188 IPC::ResponseBuilder rb{ctx, 2};
189 rb.Push(res);
190}
191
192void ISteadyClockAlarm::Disable(HLERequestContext& ctx) {
193 LOG_DEBUG(Service_Time, "called.");
194
195 m_alarms.Disable(m_alarm);
196
197 IPC::ResponseBuilder rb{ctx, 2};
198 rb.Push(ResultSuccess);
199}
200
201void ISteadyClockAlarm::IsEnabled(HLERequestContext& ctx) {
202 LOG_DEBUG(Service_Time, "called.");
203
204 IPC::ResponseBuilder rb{ctx, 3};
205 rb.Push(ResultSuccess);
206 rb.Push<bool>(m_alarm.IsLinked());
207}
208
209} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/alarms.h b/src/core/hle/service/psc/time/alarms.h
new file mode 100644
index 000000000..597770028
--- /dev/null
+++ b/src/core/hle/service/psc/time/alarms.h
@@ -0,0 +1,139 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <mutex>
7
8#include "core/hle/kernel/k_event.h"
9#include "core/hle/service/ipc_helpers.h"
10#include "core/hle/service/kernel_helpers.h"
11#include "core/hle/service/psc/time/clocks/standard_steady_clock_core.h"
12#include "core/hle/service/psc/time/common.h"
13#include "core/hle/service/psc/time/power_state_request_manager.h"
14#include "core/hle/service/server_manager.h"
15#include "core/hle/service/service.h"
16
17namespace Core {
18class System;
19}
20
21namespace Service::PSC::Time {
22class TimeManager;
23
24enum AlarmType : u32 {
25 WakeupAlarm = 0,
26 BackgroundTaskAlarm = 1,
27};
28
29struct Alarm : public Common::IntrusiveListBaseNode<Alarm> {
30 using AlarmList = Common::IntrusiveListBaseTraits<Alarm>::ListType;
31
32 Alarm(Core::System& system, KernelHelpers::ServiceContext& ctx, AlarmType type);
33 ~Alarm();
34
35 Kernel::KReadableEvent& GetEventHandle() {
36 return m_event->GetReadableEvent();
37 }
38
39 s64 GetAlertTime() const {
40 return m_alert_time;
41 }
42
43 void SetAlertTime(s64 time) {
44 m_alert_time = time;
45 }
46
47 u32 GetPriority() const {
48 return m_priority;
49 }
50
51 void Signal() {
52 m_event->Signal();
53 }
54
55 Result Lock() {
56 // TODO
57 // if (m_lock_service) {
58 // return m_lock_service->Lock();
59 // }
60 R_SUCCEED();
61 }
62
63 KernelHelpers::ServiceContext& m_ctx;
64
65 u32 m_priority;
66 Kernel::KEvent* m_event{};
67 s64 m_alert_time{};
68 // TODO
69 // nn::psc::sf::IPmStateLock* m_lock_service{};
70};
71
72class Alarms {
73public:
74 explicit Alarms(Core::System& system, StandardSteadyClockCore& steady_clock,
75 PowerStateRequestManager& power_state_request_manager);
76 ~Alarms();
77
78 Kernel::KEvent& GetEvent() {
79 return *m_event;
80 }
81
82 s64 GetRawTime() {
83 return m_steady_clock.GetRawTime();
84 }
85
86 Result Enable(Alarm& alarm, s64 time);
87 void Disable(Alarm& alarm);
88 void CheckAndSignal();
89 bool GetClosestAlarm(Alarm** out_alarm);
90
91private:
92 void Insert(Alarm& alarm);
93 void Erase(Alarm& alarm);
94 Result UpdateClosestAndSignal();
95
96 Core::System& m_system;
97 KernelHelpers::ServiceContext m_ctx;
98
99 StandardSteadyClockCore& m_steady_clock;
100 PowerStateRequestManager& m_power_state_request_manager;
101 Alarm::AlarmList m_alarms;
102 Kernel::KEvent* m_event{};
103 Alarm* m_closest_alarm{};
104 std::mutex m_mutex;
105};
106
107class IAlarmService final : public ServiceFramework<IAlarmService> {
108public:
109 explicit IAlarmService(Core::System& system, std::shared_ptr<TimeManager> manager);
110
111 ~IAlarmService() override = default;
112
113private:
114 void CreateWakeupAlarm(HLERequestContext& ctx);
115 void CreateBackgroundTaskAlarm(HLERequestContext& ctx);
116
117 Core::System& m_system;
118 Alarms& m_alarms;
119};
120
121class ISteadyClockAlarm final : public ServiceFramework<ISteadyClockAlarm> {
122public:
123 explicit ISteadyClockAlarm(Core::System& system, Alarms& alarms, AlarmType type);
124
125 ~ISteadyClockAlarm() override = default;
126
127private:
128 void GetAlarmEvent(HLERequestContext& ctx);
129 void Enable(HLERequestContext& ctx);
130 void Disable(HLERequestContext& ctx);
131 void IsEnabled(HLERequestContext& ctx);
132
133 KernelHelpers::ServiceContext m_ctx;
134
135 Alarms& m_alarms;
136 Alarm m_alarm;
137};
138
139} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/context_writers.cpp b/src/core/hle/service/psc/time/clocks/context_writers.cpp
new file mode 100644
index 000000000..ac8700f76
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/context_writers.cpp
@@ -0,0 +1,83 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/service/psc/time/clocks/context_writers.h"
6
7namespace Service::PSC::Time {
8
9void ContextWriter::SignalAllNodes() {
10 std::scoped_lock l{m_mutex};
11 for (auto& operation : m_operation_events) {
12 operation.m_event->Signal();
13 }
14}
15
16void ContextWriter::Link(OperationEvent& operation_event) {
17 std::scoped_lock l{m_mutex};
18 m_operation_events.push_back(operation_event);
19}
20
21LocalSystemClockContextWriter::LocalSystemClockContextWriter(Core::System& system,
22 SharedMemory& shared_memory)
23 : m_system{system}, m_shared_memory{shared_memory} {}
24
25Result LocalSystemClockContextWriter::Write(SystemClockContext& context) {
26 if (m_in_use) {
27 R_SUCCEED_IF(context == m_context);
28 m_context = context;
29 } else {
30 m_context = context;
31 m_in_use = true;
32 }
33
34 m_shared_memory.SetLocalSystemContext(context);
35
36 SignalAllNodes();
37
38 R_SUCCEED();
39}
40
41NetworkSystemClockContextWriter::NetworkSystemClockContextWriter(Core::System& system,
42 SharedMemory& shared_memory,
43 SystemClockCore& system_clock)
44 : m_system{system}, m_shared_memory{shared_memory}, m_system_clock{system_clock} {}
45
46Result NetworkSystemClockContextWriter::Write(SystemClockContext& context) {
47 s64 time{};
48 [[maybe_unused]] auto res = m_system_clock.GetCurrentTime(&time);
49
50 if (m_in_use) {
51 R_SUCCEED_IF(context == m_context);
52 m_context = context;
53 } else {
54 m_context = context;
55 m_in_use = true;
56 }
57
58 m_shared_memory.SetNetworkSystemContext(context);
59
60 SignalAllNodes();
61
62 R_SUCCEED();
63}
64
65EphemeralNetworkSystemClockContextWriter::EphemeralNetworkSystemClockContextWriter(
66 Core::System& system)
67 : m_system{system} {}
68
69Result EphemeralNetworkSystemClockContextWriter::Write(SystemClockContext& context) {
70 if (m_in_use) {
71 R_SUCCEED_IF(context == m_context);
72 m_context = context;
73 } else {
74 m_context = context;
75 m_in_use = true;
76 }
77
78 SignalAllNodes();
79
80 R_SUCCEED();
81}
82
83} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/context_writers.h b/src/core/hle/service/psc/time/clocks/context_writers.h
new file mode 100644
index 000000000..afd3725d4
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/context_writers.h
@@ -0,0 +1,79 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <list>
7
8#include "common/common_types.h"
9#include "core/hle/kernel/k_event.h"
10#include "core/hle/service/psc/time/clocks/system_clock_core.h"
11#include "core/hle/service/psc/time/common.h"
12#include "core/hle/service/psc/time/shared_memory.h"
13
14namespace Core {
15class System;
16}
17
18namespace Service::PSC::Time {
19
20class ContextWriter {
21private:
22 using OperationEventList = Common::IntrusiveListBaseTraits<OperationEvent>::ListType;
23
24public:
25 virtual ~ContextWriter() = default;
26
27 virtual Result Write(SystemClockContext& context) = 0;
28 void SignalAllNodes();
29 void Link(OperationEvent& operation_event);
30
31private:
32 OperationEventList m_operation_events;
33 std::mutex m_mutex;
34};
35
36class LocalSystemClockContextWriter : public ContextWriter {
37public:
38 explicit LocalSystemClockContextWriter(Core::System& system, SharedMemory& shared_memory);
39
40 Result Write(SystemClockContext& context) override;
41
42private:
43 Core::System& m_system;
44
45 SharedMemory& m_shared_memory;
46 bool m_in_use{};
47 SystemClockContext m_context{};
48};
49
50class NetworkSystemClockContextWriter : public ContextWriter {
51public:
52 explicit NetworkSystemClockContextWriter(Core::System& system, SharedMemory& shared_memory,
53 SystemClockCore& system_clock);
54
55 Result Write(SystemClockContext& context) override;
56
57private:
58 Core::System& m_system;
59
60 SharedMemory& m_shared_memory;
61 bool m_in_use{};
62 SystemClockContext m_context{};
63 SystemClockCore& m_system_clock;
64};
65
66class EphemeralNetworkSystemClockContextWriter : public ContextWriter {
67public:
68 EphemeralNetworkSystemClockContextWriter(Core::System& system);
69
70 Result Write(SystemClockContext& context) override;
71
72private:
73 Core::System& m_system;
74
75 bool m_in_use{};
76 SystemClockContext m_context{};
77};
78
79} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h b/src/core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h
new file mode 100644
index 000000000..0a68867d9
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h
@@ -0,0 +1,21 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/result.h"
7#include "core/hle/service/psc/time/clocks/context_writers.h"
8#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
9#include "core/hle/service/psc/time/clocks/system_clock_core.h"
10#include "core/hle/service/psc/time/common.h"
11
12namespace Service::PSC::Time {
13
14class EphemeralNetworkSystemClockCore : public SystemClockCore {
15public:
16 explicit EphemeralNetworkSystemClockCore(SteadyClockCore& steady_clock)
17 : SystemClockCore{steady_clock} {}
18 ~EphemeralNetworkSystemClockCore() override = default;
19};
20
21} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp
new file mode 100644
index 000000000..36dca6689
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp
@@ -0,0 +1,20 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h"
5
6namespace Service::PSC::Time {
7
8void StandardLocalSystemClockCore::Initialize(SystemClockContext& context, s64 time) {
9 SteadyClockTimePoint time_point{};
10 if (GetCurrentTimePoint(time_point) == ResultSuccess &&
11 context.steady_time_point.IdMatches(time_point)) {
12 SetContextAndWrite(context);
13 } else if (SetCurrentTime(time) != ResultSuccess) {
14 LOG_ERROR(Service_Time, "Failed to SetCurrentTime");
15 }
16
17 SetInitialized();
18}
19
20} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h
new file mode 100644
index 000000000..176ba3e94
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h
@@ -0,0 +1,23 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/result.h"
7#include "core/hle/service/psc/time/clocks/context_writers.h"
8#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
9#include "core/hle/service/psc/time/clocks/system_clock_core.h"
10#include "core/hle/service/psc/time/common.h"
11
12namespace Service::PSC::Time {
13
14class StandardLocalSystemClockCore : public SystemClockCore {
15public:
16 explicit StandardLocalSystemClockCore(SteadyClockCore& steady_clock)
17 : SystemClockCore{steady_clock} {}
18 ~StandardLocalSystemClockCore() override = default;
19
20 void Initialize(SystemClockContext& context, s64 time);
21};
22
23} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp
new file mode 100644
index 000000000..8d6cb7db1
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp
@@ -0,0 +1,42 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h"
5
6namespace Service::PSC::Time {
7
8void StandardNetworkSystemClockCore::Initialize(SystemClockContext& context, s64 accuracy) {
9 if (SetContextAndWrite(context) != ResultSuccess) {
10 LOG_ERROR(Service_Time, "Failed to SetContext");
11 }
12 m_sufficient_accuracy = accuracy;
13 SetInitialized();
14}
15
16bool StandardNetworkSystemClockCore::IsAccuracySufficient() {
17 if (!IsInitialized()) {
18 return false;
19 }
20
21 SystemClockContext context{};
22 SteadyClockTimePoint current_time_point{};
23 if (GetCurrentTimePoint(current_time_point) != ResultSuccess ||
24 GetContext(context) != ResultSuccess) {
25 return false;
26 }
27
28 s64 seconds{};
29 if (GetSpanBetweenTimePoints(&seconds, context.steady_time_point, current_time_point) !=
30 ResultSuccess) {
31 return false;
32 }
33
34 if (std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(seconds))
35 .count() < m_sufficient_accuracy) {
36 return true;
37 }
38
39 return false;
40}
41
42} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h
new file mode 100644
index 000000000..933d2c8e3
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h
@@ -0,0 +1,30 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <chrono>
7
8#include "core/hle/result.h"
9#include "core/hle/service/psc/time/clocks/context_writers.h"
10#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
11#include "core/hle/service/psc/time/clocks/system_clock_core.h"
12#include "core/hle/service/psc/time/common.h"
13
14namespace Service::PSC::Time {
15
16class StandardNetworkSystemClockCore : public SystemClockCore {
17public:
18 explicit StandardNetworkSystemClockCore(SteadyClockCore& steady_clock)
19 : SystemClockCore{steady_clock} {}
20 ~StandardNetworkSystemClockCore() override = default;
21
22 void Initialize(SystemClockContext& context, s64 accuracy);
23 bool IsAccuracySufficient();
24
25private:
26 s64 m_sufficient_accuracy{
27 std::chrono ::duration_cast<std::chrono::nanoseconds>(std::chrono::days(10)).count()};
28};
29
30} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.cpp
new file mode 100644
index 000000000..7a72d7aa2
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.cpp
@@ -0,0 +1,101 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <chrono>
5
6#include "core/core.h"
7#include "core/core_timing.h"
8#include "core/hle/service/psc/time/clocks/standard_steady_clock_core.h"
9
10namespace Service::PSC::Time {
11
12void StandardSteadyClockCore::Initialize(ClockSourceId clock_source_id, s64 rtc_offset,
13 s64 internal_offset, s64 test_offset,
14 bool is_rtc_reset_detected) {
15 m_clock_source_id = clock_source_id;
16 m_rtc_offset = rtc_offset;
17 m_internal_offset = internal_offset;
18 m_test_offset = test_offset;
19 if (is_rtc_reset_detected) {
20 SetResetDetected();
21 }
22 SetInitialized();
23}
24
25void StandardSteadyClockCore::SetRtcOffset(s64 offset) {
26 m_rtc_offset = offset;
27}
28
29void StandardSteadyClockCore::SetContinuousAdjustment(ClockSourceId clock_source_id, s64 time) {
30 auto ticks{m_system.CoreTiming().GetClockTicks()};
31
32 m_continuous_adjustment_time_point.rtc_offset = ConvertToTimeSpan(ticks).count();
33 m_continuous_adjustment_time_point.diff_scale = 0;
34 m_continuous_adjustment_time_point.shift_amount = 0;
35 m_continuous_adjustment_time_point.lower = time;
36 m_continuous_adjustment_time_point.upper = time;
37 m_continuous_adjustment_time_point.clock_source_id = clock_source_id;
38}
39
40void StandardSteadyClockCore::GetContinuousAdjustment(
41 ContinuousAdjustmentTimePoint& out_time_point) const {
42 out_time_point = m_continuous_adjustment_time_point;
43}
44
45void StandardSteadyClockCore::UpdateContinuousAdjustmentTime(s64 in_time) {
46 auto ticks{m_system.CoreTiming().GetClockTicks()};
47 auto uptime_ns{ConvertToTimeSpan(ticks).count()};
48 auto adjusted_time{((uptime_ns - m_continuous_adjustment_time_point.rtc_offset) *
49 m_continuous_adjustment_time_point.diff_scale) >>
50 m_continuous_adjustment_time_point.shift_amount};
51 auto expected_time{adjusted_time + m_continuous_adjustment_time_point.lower};
52
53 auto last_time_point{m_continuous_adjustment_time_point.upper};
54 m_continuous_adjustment_time_point.upper = in_time;
55 auto t1{std::min<s64>(expected_time, last_time_point)};
56 expected_time = std::max<s64>(expected_time, last_time_point);
57 expected_time = m_continuous_adjustment_time_point.diff_scale >= 0 ? t1 : expected_time;
58
59 auto new_diff{in_time < expected_time ? -55 : 55};
60
61 m_continuous_adjustment_time_point.rtc_offset = uptime_ns;
62 m_continuous_adjustment_time_point.shift_amount = expected_time == in_time ? 0 : 14;
63 m_continuous_adjustment_time_point.diff_scale = expected_time == in_time ? 0 : new_diff;
64 m_continuous_adjustment_time_point.lower = expected_time;
65}
66
67Result StandardSteadyClockCore::GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) {
68 auto current_time_ns = GetCurrentRawTimePointImpl();
69 auto current_time_s =
70 std::chrono::duration_cast<std::chrono::seconds>(std::chrono::nanoseconds(current_time_ns));
71 out_time_point.time_point = current_time_s.count();
72 out_time_point.clock_source_id = m_clock_source_id;
73 R_SUCCEED();
74}
75
76s64 StandardSteadyClockCore::GetCurrentRawTimePointImpl() {
77 std::scoped_lock l{m_mutex};
78 auto ticks{static_cast<s64>(m_system.CoreTiming().GetClockTicks())};
79 auto current_time_ns = m_rtc_offset + ConvertToTimeSpan(ticks).count();
80 auto time_point = std::max<s64>(current_time_ns, m_cached_time_point);
81 m_cached_time_point = time_point;
82 return time_point;
83}
84
85s64 StandardSteadyClockCore::GetTestOffsetImpl() const {
86 return m_test_offset;
87}
88
89void StandardSteadyClockCore::SetTestOffsetImpl(s64 offset) {
90 m_test_offset = offset;
91}
92
93s64 StandardSteadyClockCore::GetInternalOffsetImpl() const {
94 return m_internal_offset;
95}
96
97void StandardSteadyClockCore::SetInternalOffsetImpl(s64 offset) {
98 m_internal_offset = offset;
99}
100
101} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.h
new file mode 100644
index 000000000..bbf98fcd5
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.h
@@ -0,0 +1,54 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <mutex>
7
8#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
9
10namespace Core {
11class System;
12}
13
14namespace Service::PSC::Time {
15class StandardSteadyClockCore : public SteadyClockCore {
16public:
17 explicit StandardSteadyClockCore(Core::System& system) : m_system{system} {}
18 ~StandardSteadyClockCore() override = default;
19
20 void Initialize(ClockSourceId clock_source_id, s64 rtc_offset, s64 internal_offset,
21 s64 test_offset, bool is_rtc_reset_detected);
22
23 void SetRtcOffset(s64 offset);
24 void SetContinuousAdjustment(ClockSourceId clock_source_id, s64 time);
25 void GetContinuousAdjustment(ContinuousAdjustmentTimePoint& out_time_point) const;
26 void UpdateContinuousAdjustmentTime(s64 time);
27
28 Result GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) override;
29 s64 GetCurrentRawTimePointImpl() override;
30 s64 GetTestOffsetImpl() const override;
31 void SetTestOffsetImpl(s64 offset) override;
32 s64 GetInternalOffsetImpl() const override;
33 void SetInternalOffsetImpl(s64 offset) override;
34
35 Result GetRtcValueImpl(s64& out_value) override {
36 R_RETURN(ResultNotImplemented);
37 }
38
39 Result GetSetupResultValueImpl() override {
40 R_SUCCEED();
41 }
42
43private:
44 Core::System& m_system;
45
46 std::mutex m_mutex;
47 s64 m_test_offset{};
48 s64 m_internal_offset{};
49 ClockSourceId m_clock_source_id{};
50 s64 m_rtc_offset{};
51 s64 m_cached_time_point{};
52 ContinuousAdjustmentTimePoint m_continuous_adjustment_time_point{};
53};
54} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp
new file mode 100644
index 000000000..9e9be05d6
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp
@@ -0,0 +1,63 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/service/psc/time/clocks/standard_user_system_clock_core.h"
6
7namespace Service::PSC::Time {
8
9StandardUserSystemClockCore::StandardUserSystemClockCore(
10 Core::System& system, StandardLocalSystemClockCore& local_clock,
11 StandardNetworkSystemClockCore& network_clock)
12 : SystemClockCore{local_clock.GetSteadyClock()}, m_system{system},
13 m_ctx{m_system, "Psc:StandardUserSystemClockCore"}, m_local_system_clock{local_clock},
14 m_network_system_clock{network_clock}, m_event{m_ctx.CreateEvent(
15 "Psc:StandardUserSystemClockCore:Event")} {}
16
17StandardUserSystemClockCore::~StandardUserSystemClockCore() {
18 m_ctx.CloseEvent(m_event);
19}
20
21Result StandardUserSystemClockCore::SetAutomaticCorrection(bool automatic_correction) {
22 R_SUCCEED_IF(m_automatic_correction == automatic_correction);
23 R_SUCCEED_IF(!m_network_system_clock.CheckClockSourceMatches());
24
25 SystemClockContext context{};
26 R_TRY(m_network_system_clock.GetContext(context));
27 R_TRY(m_local_system_clock.SetContextAndWrite(context));
28
29 m_automatic_correction = automatic_correction;
30 R_SUCCEED();
31}
32
33Result StandardUserSystemClockCore::GetContext(SystemClockContext& out_context) const {
34 if (!m_automatic_correction) {
35 R_RETURN(m_local_system_clock.GetContext(out_context));
36 }
37
38 if (!m_network_system_clock.CheckClockSourceMatches()) {
39 R_RETURN(m_local_system_clock.GetContext(out_context));
40 }
41
42 SystemClockContext context{};
43 R_TRY(m_network_system_clock.GetContext(context));
44 R_TRY(m_local_system_clock.SetContextAndWrite(context));
45
46 R_RETURN(m_local_system_clock.GetContext(out_context));
47}
48
49Result StandardUserSystemClockCore::SetContext(SystemClockContext& context) {
50 R_RETURN(ResultNotImplemented);
51}
52
53Result StandardUserSystemClockCore::GetTimePoint(SteadyClockTimePoint& out_time_point) {
54 out_time_point = m_time_point;
55 R_SUCCEED();
56}
57
58void StandardUserSystemClockCore::SetTimePointAndSignal(SteadyClockTimePoint& time_point) {
59 m_time_point = time_point;
60 m_event->Signal();
61}
62
63} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h
new file mode 100644
index 000000000..a7fe7648d
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h
@@ -0,0 +1,55 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/kernel/k_event.h"
7#include "core/hle/result.h"
8#include "core/hle/service/kernel_helpers.h"
9#include "core/hle/service/psc/time/clocks/context_writers.h"
10#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h"
11#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h"
12#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
13#include "core/hle/service/psc/time/clocks/system_clock_core.h"
14#include "core/hle/service/psc/time/common.h"
15
16namespace Core {
17class System;
18}
19
20namespace Service::PSC::Time {
21
22class StandardUserSystemClockCore : public SystemClockCore {
23public:
24 explicit StandardUserSystemClockCore(Core::System& system,
25 StandardLocalSystemClockCore& local_clock,
26 StandardNetworkSystemClockCore& network_clock);
27 ~StandardUserSystemClockCore() override;
28
29 Kernel::KEvent& GetEvent() {
30 return *m_event;
31 }
32
33 bool GetAutomaticCorrection() const {
34 return m_automatic_correction;
35 }
36 Result SetAutomaticCorrection(bool automatic_correction);
37
38 Result GetContext(SystemClockContext& out_context) const override;
39 Result SetContext(SystemClockContext& context) override;
40
41 Result GetTimePoint(SteadyClockTimePoint& out_time_point);
42 void SetTimePointAndSignal(SteadyClockTimePoint& time_point);
43
44private:
45 Core::System& m_system;
46 KernelHelpers::ServiceContext m_ctx;
47
48 bool m_automatic_correction{};
49 StandardLocalSystemClockCore& m_local_system_clock;
50 StandardNetworkSystemClockCore& m_network_system_clock;
51 SteadyClockTimePoint m_time_point{};
52 Kernel::KEvent* m_event{};
53};
54
55} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/steady_clock_core.h b/src/core/hle/service/psc/time/clocks/steady_clock_core.h
new file mode 100644
index 000000000..f933cc15f
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/steady_clock_core.h
@@ -0,0 +1,81 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <chrono>
7
8#include "core/hle/result.h"
9#include "core/hle/service/psc/time/common.h"
10
11namespace Service::PSC::Time {
12
13class SteadyClockCore {
14public:
15 SteadyClockCore() = default;
16 virtual ~SteadyClockCore() = default;
17
18 void SetInitialized() {
19 m_initialized = true;
20 }
21
22 bool IsInitialized() const {
23 return m_initialized;
24 }
25
26 void SetResetDetected() {
27 m_reset_detected = true;
28 }
29
30 bool IsResetDetected() const {
31 return m_reset_detected;
32 }
33
34 Result GetCurrentTimePoint(SteadyClockTimePoint& out_time_point) {
35 R_TRY(GetCurrentTimePointImpl(out_time_point));
36
37 auto one_second_ns{
38 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
39 out_time_point.time_point += GetTestOffsetImpl() / one_second_ns;
40 out_time_point.time_point += GetInternalOffsetImpl() / one_second_ns;
41 R_SUCCEED();
42 }
43
44 s64 GetTestOffset() const {
45 return GetTestOffsetImpl();
46 }
47
48 void SetTestOffset(s64 offset) {
49 SetTestOffsetImpl(offset);
50 }
51
52 s64 GetInternalOffset() const {
53 return GetInternalOffsetImpl();
54 }
55
56 s64 GetRawTime() {
57 return GetCurrentRawTimePointImpl() + GetTestOffsetImpl() + GetInternalOffsetImpl();
58 }
59
60 Result GetRtcValue(s64& out_value) {
61 R_RETURN(GetRtcValueImpl(out_value));
62 }
63
64 Result GetSetupResultValue() {
65 R_RETURN(GetSetupResultValueImpl());
66 }
67
68private:
69 virtual Result GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) = 0;
70 virtual s64 GetCurrentRawTimePointImpl() = 0;
71 virtual s64 GetTestOffsetImpl() const = 0;
72 virtual void SetTestOffsetImpl(s64 offset) = 0;
73 virtual s64 GetInternalOffsetImpl() const = 0;
74 virtual void SetInternalOffsetImpl(s64 offset) = 0;
75 virtual Result GetRtcValueImpl(s64& out_value) = 0;
76 virtual Result GetSetupResultValueImpl() = 0;
77
78 bool m_initialized{};
79 bool m_reset_detected{};
80};
81} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/system_clock_core.cpp
new file mode 100644
index 000000000..c507ef517
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/system_clock_core.cpp
@@ -0,0 +1,75 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/psc/time/clocks/context_writers.h"
5#include "core/hle/service/psc/time/clocks/system_clock_core.h"
6
7namespace Service::PSC::Time {
8
9bool SystemClockCore::CheckClockSourceMatches() {
10 SystemClockContext context{};
11 if (GetContext(context) != ResultSuccess) {
12 return false;
13 }
14
15 SteadyClockTimePoint time_point{};
16 if (m_steady_clock.GetCurrentTimePoint(time_point) != ResultSuccess) {
17 return false;
18 }
19
20 return context.steady_time_point.IdMatches(time_point);
21}
22
23Result SystemClockCore::GetCurrentTime(s64* out_time) const {
24 R_UNLESS(out_time != nullptr, ResultInvalidArgument);
25
26 SystemClockContext context{};
27 SteadyClockTimePoint time_point{};
28
29 R_TRY(m_steady_clock.GetCurrentTimePoint(time_point));
30 R_TRY(GetContext(context));
31
32 R_UNLESS(context.steady_time_point.IdMatches(time_point), ResultClockMismatch);
33
34 *out_time = context.offset + time_point.time_point;
35 R_SUCCEED();
36}
37
38Result SystemClockCore::SetCurrentTime(s64 time) {
39 SteadyClockTimePoint time_point{};
40 R_TRY(m_steady_clock.GetCurrentTimePoint(time_point));
41
42 SystemClockContext context{
43 .offset = time - time_point.time_point,
44 .steady_time_point = time_point,
45 };
46 R_RETURN(SetContextAndWrite(context));
47}
48
49Result SystemClockCore::GetContext(SystemClockContext& out_context) const {
50 out_context = m_context;
51 R_SUCCEED();
52}
53
54Result SystemClockCore::SetContext(SystemClockContext& context) {
55 m_context = context;
56 R_SUCCEED();
57}
58
59Result SystemClockCore::SetContextAndWrite(SystemClockContext& context) {
60 R_TRY(SetContext(context));
61
62 if (m_context_writer) {
63 R_RETURN(m_context_writer->Write(context));
64 }
65
66 R_SUCCEED();
67}
68
69void SystemClockCore::LinkOperationEvent(OperationEvent& operation_event) {
70 if (m_context_writer) {
71 m_context_writer->Link(operation_event);
72 }
73}
74
75} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/system_clock_core.h b/src/core/hle/service/psc/time/clocks/system_clock_core.h
new file mode 100644
index 000000000..73811712e
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/system_clock_core.h
@@ -0,0 +1,55 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/result.h"
7#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
8#include "core/hle/service/psc/time/common.h"
9
10namespace Service::PSC::Time {
11class ContextWriter;
12
13class SystemClockCore {
14public:
15 explicit SystemClockCore(SteadyClockCore& steady_clock) : m_steady_clock{steady_clock} {}
16 virtual ~SystemClockCore() = default;
17
18 SteadyClockCore& GetSteadyClock() {
19 return m_steady_clock;
20 }
21
22 bool IsInitialized() const {
23 return m_initialized;
24 }
25
26 void SetInitialized() {
27 m_initialized = true;
28 }
29
30 void SetContextWriter(ContextWriter& context_writer) {
31 m_context_writer = &context_writer;
32 }
33
34 bool CheckClockSourceMatches();
35
36 Result GetCurrentTime(s64* out_time) const;
37 Result SetCurrentTime(s64 time);
38
39 Result GetCurrentTimePoint(SteadyClockTimePoint& out_time_point) {
40 R_RETURN(m_steady_clock.GetCurrentTimePoint(out_time_point));
41 }
42
43 virtual Result GetContext(SystemClockContext& out_context) const;
44 virtual Result SetContext(SystemClockContext& context);
45 Result SetContextAndWrite(SystemClockContext& context);
46
47 void LinkOperationEvent(OperationEvent& operation_event);
48
49private:
50 bool m_initialized{};
51 ContextWriter* m_context_writer{};
52 SteadyClockCore& m_steady_clock;
53 SystemClockContext m_context{};
54};
55} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp b/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp
new file mode 100644
index 000000000..22da1fbcc
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp
@@ -0,0 +1,43 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <chrono>
5
6#include "core/core.h"
7#include "core/core_timing.h"
8#include "core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h"
9
10namespace Service::PSC::Time {
11
12Result TickBasedSteadyClockCore::GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) {
13 auto ticks{m_system.CoreTiming().GetClockTicks()};
14 auto current_time_s =
15 std::chrono::duration_cast<std::chrono::seconds>(ConvertToTimeSpan(ticks)).count();
16 out_time_point.time_point = current_time_s;
17 out_time_point.clock_source_id = m_clock_source_id;
18 R_SUCCEED();
19}
20
21s64 TickBasedSteadyClockCore::GetCurrentRawTimePointImpl() {
22 SteadyClockTimePoint time_point{};
23 if (GetCurrentTimePointImpl(time_point) != ResultSuccess) {
24 LOG_ERROR(Service_Time, "Failed to GetCurrentTimePoint!");
25 }
26 return std::chrono::duration_cast<std::chrono::nanoseconds>(
27 std::chrono::seconds(time_point.time_point))
28 .count();
29}
30
31s64 TickBasedSteadyClockCore::GetTestOffsetImpl() const {
32 return 0;
33}
34
35void TickBasedSteadyClockCore::SetTestOffsetImpl(s64 offset) {}
36
37s64 TickBasedSteadyClockCore::GetInternalOffsetImpl() const {
38 return 0;
39}
40
41void TickBasedSteadyClockCore::SetInternalOffsetImpl(s64 offset) {}
42
43} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h b/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h
new file mode 100644
index 000000000..a7bea86a2
--- /dev/null
+++ b/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h
@@ -0,0 +1,41 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <mutex>
7
8#include "common/uuid.h"
9#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
10
11namespace Core {
12class System;
13}
14
15namespace Service::PSC::Time {
16class TickBasedSteadyClockCore : public SteadyClockCore {
17public:
18 explicit TickBasedSteadyClockCore(Core::System& system) : m_system{system} {}
19 ~TickBasedSteadyClockCore() override = default;
20
21 Result GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) override;
22 s64 GetCurrentRawTimePointImpl() override;
23 s64 GetTestOffsetImpl() const override;
24 void SetTestOffsetImpl(s64 offset) override;
25 s64 GetInternalOffsetImpl() const override;
26 void SetInternalOffsetImpl(s64 offset) override;
27
28 Result GetRtcValueImpl(s64& out_value) override {
29 R_RETURN(ResultNotImplemented);
30 }
31
32 Result GetSetupResultValueImpl() override {
33 R_SUCCEED();
34 }
35
36private:
37 Core::System& m_system;
38
39 ClockSourceId m_clock_source_id{Common::UUID::MakeRandom()};
40};
41} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/common.cpp b/src/core/hle/service/psc/time/common.cpp
new file mode 100644
index 000000000..a6d9f02ca
--- /dev/null
+++ b/src/core/hle/service/psc/time/common.cpp
@@ -0,0 +1,16 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/service/psc/time/common.h"
6
7namespace Service::PSC::Time {
8OperationEvent::OperationEvent(Core::System& system)
9 : m_ctx{system, "Time:OperationEvent"}, m_event{
10 m_ctx.CreateEvent("Time:OperationEvent:Event")} {}
11
12OperationEvent::~OperationEvent() {
13 m_ctx.CloseEvent(m_event);
14}
15
16} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/common.h b/src/core/hle/service/psc/time/common.h
new file mode 100644
index 000000000..d17b31143
--- /dev/null
+++ b/src/core/hle/service/psc/time/common.h
@@ -0,0 +1,168 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <chrono>
8
9#include "common/common_types.h"
10#include "common/intrusive_list.h"
11#include "common/uuid.h"
12#include "common/wall_clock.h"
13#include "core/hle/kernel/k_event.h"
14#include "core/hle/service/kernel_helpers.h"
15#include "core/hle/service/psc/time/errors.h"
16
17namespace Core {
18class System;
19}
20
21namespace Service::PSC::Time {
22using ClockSourceId = Common::UUID;
23
24struct SteadyClockTimePoint {
25 constexpr bool IdMatches(SteadyClockTimePoint& other) {
26 return clock_source_id == other.clock_source_id;
27 }
28 bool operator==(const SteadyClockTimePoint& other) const = default;
29
30 s64 time_point;
31 ClockSourceId clock_source_id;
32};
33static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint has the wrong size!");
34static_assert(std::is_trivial_v<ClockSourceId>);
35
36struct SystemClockContext {
37 bool operator==(const SystemClockContext& other) const = default;
38
39 s64 offset;
40 SteadyClockTimePoint steady_time_point;
41};
42static_assert(sizeof(SystemClockContext) == 0x20, "SystemClockContext has the wrong size!");
43static_assert(std::is_trivial_v<SystemClockContext>);
44
45enum class TimeType : u8 {
46 UserSystemClock,
47 NetworkSystemClock,
48 LocalSystemClock,
49};
50
51struct CalendarTime {
52 s16 year;
53 s8 month;
54 s8 day;
55 s8 hour;
56 s8 minute;
57 s8 second;
58};
59static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime has the wrong size!");
60
61struct CalendarAdditionalInfo {
62 s32 day_of_week;
63 s32 day_of_year;
64 std::array<char, 8> name;
65 s32 is_dst;
66 s32 ut_offset;
67};
68static_assert(sizeof(CalendarAdditionalInfo) == 0x18, "CalendarAdditionalInfo has the wrong size!");
69
70struct LocationName {
71 std::array<char, 36> name;
72};
73static_assert(sizeof(LocationName) == 0x24, "LocationName has the wrong size!");
74
75struct RuleVersion {
76 std::array<char, 16> version;
77};
78static_assert(sizeof(RuleVersion) == 0x10, "RuleVersion has the wrong size!");
79
80struct ClockSnapshot {
81 SystemClockContext user_context;
82 SystemClockContext network_context;
83 s64 user_time;
84 s64 network_time;
85 CalendarTime user_calendar_time;
86 CalendarTime network_calendar_time;
87 CalendarAdditionalInfo user_calendar_additional_time;
88 CalendarAdditionalInfo network_calendar_additional_time;
89 SteadyClockTimePoint steady_clock_time_point;
90 LocationName location_name;
91 bool is_automatic_correction_enabled;
92 TimeType type;
93 u16 unk_CE;
94};
95static_assert(sizeof(ClockSnapshot) == 0xD0, "ClockSnapshot has the wrong size!");
96static_assert(std::is_trivial_v<ClockSnapshot>);
97
98struct ContinuousAdjustmentTimePoint {
99 s64 rtc_offset;
100 s64 diff_scale;
101 s64 shift_amount;
102 s64 lower;
103 s64 upper;
104 ClockSourceId clock_source_id;
105};
106static_assert(sizeof(ContinuousAdjustmentTimePoint) == 0x38,
107 "ContinuousAdjustmentTimePoint has the wrong size!");
108static_assert(std::is_trivial_v<ContinuousAdjustmentTimePoint>);
109
110struct AlarmInfo {
111 s64 alert_time;
112 u32 priority;
113};
114static_assert(sizeof(AlarmInfo) == 0x10, "AlarmInfo has the wrong size!");
115
116struct StaticServiceSetupInfo {
117 bool can_write_local_clock;
118 bool can_write_user_clock;
119 bool can_write_network_clock;
120 bool can_write_timezone_device_location;
121 bool can_write_steady_clock;
122 bool can_write_uninitialized_clock;
123};
124static_assert(sizeof(StaticServiceSetupInfo) == 0x6, "StaticServiceSetupInfo has the wrong size!");
125
126struct OperationEvent : public Common::IntrusiveListBaseNode<OperationEvent> {
127 using OperationEventList = Common::IntrusiveListBaseTraits<OperationEvent>::ListType;
128
129 OperationEvent(Core::System& system);
130 ~OperationEvent();
131
132 KernelHelpers::ServiceContext m_ctx;
133 Kernel::KEvent* m_event{};
134};
135
136constexpr inline std::chrono::nanoseconds ConvertToTimeSpan(s64 ticks) {
137 constexpr auto one_second_ns{
138 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
139
140 constexpr s64 max{Common::WallClock::CNTFRQ *
141 (std::numeric_limits<s64>::max() / one_second_ns)};
142
143 if (ticks > max) {
144 return std::chrono::nanoseconds(std::numeric_limits<s64>::max());
145 } else if (ticks < -max) {
146 return std::chrono::nanoseconds(std::numeric_limits<s64>::min());
147 }
148
149 auto a{ticks / Common::WallClock::CNTFRQ * one_second_ns};
150 auto b{((ticks % Common::WallClock::CNTFRQ) * one_second_ns) / Common::WallClock::CNTFRQ};
151
152 return std::chrono::nanoseconds(a + b);
153}
154
155constexpr inline Result GetSpanBetweenTimePoints(s64* out_seconds, SteadyClockTimePoint& a,
156 SteadyClockTimePoint& b) {
157 R_UNLESS(out_seconds, ResultInvalidArgument);
158 R_UNLESS(a.IdMatches(b), ResultInvalidArgument);
159 R_UNLESS(a.time_point >= 0 || b.time_point <= a.time_point + std::numeric_limits<s64>::max(),
160 ResultOverflow);
161 R_UNLESS(a.time_point < 0 || b.time_point >= a.time_point + std::numeric_limits<s64>::min(),
162 ResultOverflow);
163
164 *out_seconds = b.time_point - a.time_point;
165 R_SUCCEED();
166}
167
168} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/errors.h b/src/core/hle/service/psc/time/errors.h
new file mode 100644
index 000000000..6d833a006
--- /dev/null
+++ b/src/core/hle/service/psc/time/errors.h
@@ -0,0 +1,24 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/result.h"
7
8namespace Service::PSC::Time {
9
10constexpr Result ResultPermissionDenied{ErrorModule::Time, 1};
11constexpr Result ResultClockMismatch{ErrorModule::Time, 102};
12constexpr Result ResultClockUninitialized{ErrorModule::Time, 103};
13constexpr Result ResultTimeNotFound{ErrorModule::Time, 200};
14constexpr Result ResultOverflow{ErrorModule::Time, 201};
15constexpr Result ResultFailed{ErrorModule::Time, 801};
16constexpr Result ResultInvalidArgument{ErrorModule::Time, 901};
17constexpr Result ResultTimeZoneOutOfRange{ErrorModule::Time, 902};
18constexpr Result ResultTimeZoneParseFailed{ErrorModule::Time, 903};
19constexpr Result ResultRtcTimeout{ErrorModule::Time, 988};
20constexpr Result ResultTimeZoneNotFound{ErrorModule::Time, 989};
21constexpr Result ResultNotImplemented{ErrorModule::Time, 990};
22constexpr Result ResultAlarmNotRegistered{ErrorModule::Time, 1502};
23
24} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/manager.h b/src/core/hle/service/psc/time/manager.h
new file mode 100644
index 000000000..62ded1247
--- /dev/null
+++ b/src/core/hle/service/psc/time/manager.h
@@ -0,0 +1,56 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/psc/time/alarms.h"
7#include "core/hle/service/psc/time/clocks/context_writers.h"
8#include "core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h"
9#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h"
10#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h"
11#include "core/hle/service/psc/time/clocks/standard_steady_clock_core.h"
12#include "core/hle/service/psc/time/clocks/standard_user_system_clock_core.h"
13#include "core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h"
14#include "core/hle/service/psc/time/power_state_request_manager.h"
15#include "core/hle/service/psc/time/shared_memory.h"
16#include "core/hle/service/psc/time/time_zone.h"
17
18namespace Core {
19class System;
20}
21
22namespace Service::PSC::Time {
23class TimeManager {
24public:
25 explicit TimeManager(Core::System& system)
26 : m_system{system}, m_standard_steady_clock{system}, m_tick_based_steady_clock{m_system},
27 m_standard_local_system_clock{m_standard_steady_clock},
28 m_standard_network_system_clock{m_standard_steady_clock},
29 m_standard_user_system_clock{m_system, m_standard_local_system_clock,
30 m_standard_network_system_clock},
31 m_ephemeral_network_clock{m_tick_based_steady_clock}, m_shared_memory{m_system},
32 m_power_state_request_manager{m_system}, m_alarms{m_system, m_standard_steady_clock,
33 m_power_state_request_manager},
34 m_local_system_clock_context_writer{m_system, m_shared_memory},
35 m_network_system_clock_context_writer{m_system, m_shared_memory,
36 m_standard_user_system_clock},
37 m_ephemeral_network_clock_context_writer{m_system} {}
38
39 Core::System& m_system;
40
41 StandardSteadyClockCore m_standard_steady_clock;
42 TickBasedSteadyClockCore m_tick_based_steady_clock;
43 StandardLocalSystemClockCore m_standard_local_system_clock;
44 StandardNetworkSystemClockCore m_standard_network_system_clock;
45 StandardUserSystemClockCore m_standard_user_system_clock;
46 EphemeralNetworkSystemClockCore m_ephemeral_network_clock;
47 TimeZone m_time_zone;
48 SharedMemory m_shared_memory;
49 PowerStateRequestManager m_power_state_request_manager;
50 Alarms m_alarms;
51 LocalSystemClockContextWriter m_local_system_clock_context_writer;
52 NetworkSystemClockContextWriter m_network_system_clock_context_writer;
53 EphemeralNetworkSystemClockContextWriter m_ephemeral_network_clock_context_writer;
54};
55
56} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/power_state_request_manager.cpp b/src/core/hle/service/psc/time/power_state_request_manager.cpp
new file mode 100644
index 000000000..17de0bf4d
--- /dev/null
+++ b/src/core/hle/service/psc/time/power_state_request_manager.cpp
@@ -0,0 +1,50 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/service/psc/time/power_state_request_manager.h"
6
7namespace Service::PSC::Time {
8
9PowerStateRequestManager::PowerStateRequestManager(Core::System& system)
10 : m_system{system}, m_ctx{system, "Psc:PowerStateRequestManager"},
11 m_event{m_ctx.CreateEvent("Psc:PowerStateRequestManager:Event")} {}
12
13PowerStateRequestManager::~PowerStateRequestManager() {
14 m_ctx.CloseEvent(m_event);
15}
16
17void PowerStateRequestManager::UpdatePendingPowerStateRequestPriority(u32 priority) {
18 std::scoped_lock l{m_mutex};
19 if (m_has_pending_request) {
20 m_pending_request_priority = std::max(m_pending_request_priority, priority);
21 } else {
22 m_pending_request_priority = priority;
23 m_has_pending_request = true;
24 }
25}
26
27void PowerStateRequestManager::SignalPowerStateRequestAvailability() {
28 std::scoped_lock l{m_mutex};
29 if (m_has_pending_request) {
30 if (!m_has_available_request) {
31 m_has_available_request = true;
32 }
33 m_has_pending_request = false;
34 m_available_request_priority = m_pending_request_priority;
35 m_event->Signal();
36 }
37}
38
39bool PowerStateRequestManager::GetAndClearPowerStateRequest(u32& out_priority) {
40 std::scoped_lock l{m_mutex};
41 auto had_request{m_has_available_request};
42 if (m_has_available_request) {
43 out_priority = m_available_request_priority;
44 m_has_available_request = false;
45 m_event->Clear();
46 }
47 return had_request;
48}
49
50} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/power_state_request_manager.h b/src/core/hle/service/psc/time/power_state_request_manager.h
new file mode 100644
index 000000000..30a0c947d
--- /dev/null
+++ b/src/core/hle/service/psc/time/power_state_request_manager.h
@@ -0,0 +1,42 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <mutex>
7
8#include "core/hle/kernel/k_event.h"
9#include "core/hle/service/kernel_helpers.h"
10
11namespace Core {
12class System;
13}
14
15namespace Service::PSC::Time {
16
17class PowerStateRequestManager {
18public:
19 explicit PowerStateRequestManager(Core::System& system);
20 ~PowerStateRequestManager();
21
22 Kernel::KReadableEvent& GetReadableEvent() {
23 return m_event->GetReadableEvent();
24 }
25
26 void UpdatePendingPowerStateRequestPriority(u32 priority);
27 void SignalPowerStateRequestAvailability();
28 bool GetAndClearPowerStateRequest(u32& out_priority);
29
30private:
31 Core::System& m_system;
32 KernelHelpers::ServiceContext m_ctx;
33
34 Kernel::KEvent* m_event{};
35 bool m_has_pending_request{};
36 u32 m_pending_request_priority{};
37 bool m_has_available_request{};
38 u32 m_available_request_priority{};
39 std::mutex m_mutex;
40};
41
42} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/power_state_service.cpp b/src/core/hle/service/psc/time/power_state_service.cpp
new file mode 100644
index 000000000..b0ae71bf9
--- /dev/null
+++ b/src/core/hle/service/psc/time/power_state_service.cpp
@@ -0,0 +1,49 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/psc/time/power_state_service.h"
5
6namespace Service::PSC::Time {
7
8IPowerStateRequestHandler::IPowerStateRequestHandler(
9 Core::System& system_, PowerStateRequestManager& power_state_request_manager)
10 : ServiceFramework{system_, "time:p"}, m_system{system}, m_power_state_request_manager{
11 power_state_request_manager} {
12 // clang-format off
13 static const FunctionInfo functions[] = {
14 {0, &IPowerStateRequestHandler::GetPowerStateRequestEventReadableHandle, "GetPowerStateRequestEventReadableHandle"},
15 {1, &IPowerStateRequestHandler::GetAndClearPowerStateRequest, "GetAndClearPowerStateRequest"},
16 };
17 // clang-format on
18
19 RegisterHandlers(functions);
20}
21
22void IPowerStateRequestHandler::GetPowerStateRequestEventReadableHandle(HLERequestContext& ctx) {
23 LOG_DEBUG(Service_Time, "called.");
24
25 IPC::ResponseBuilder rb{ctx, 2, 1};
26 rb.Push(ResultSuccess);
27 rb.PushCopyObjects(m_power_state_request_manager.GetReadableEvent());
28}
29
30void IPowerStateRequestHandler::GetAndClearPowerStateRequest(HLERequestContext& ctx) {
31 LOG_DEBUG(Service_Time, "called.");
32
33 u32 priority{};
34 auto cleared = m_power_state_request_manager.GetAndClearPowerStateRequest(priority);
35
36 if (cleared) {
37 IPC::ResponseBuilder rb{ctx, 4};
38 rb.Push(ResultSuccess);
39 rb.Push(priority);
40 rb.Push(cleared);
41 return;
42 }
43
44 IPC::ResponseBuilder rb{ctx, 3};
45 rb.Push(ResultSuccess);
46 rb.Push(cleared);
47}
48
49} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/power_state_service.h b/src/core/hle/service/psc/time/power_state_service.h
new file mode 100644
index 000000000..3ebfddb79
--- /dev/null
+++ b/src/core/hle/service/psc/time/power_state_service.h
@@ -0,0 +1,32 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/ipc_helpers.h"
7#include "core/hle/service/psc/time/power_state_request_manager.h"
8#include "core/hle/service/server_manager.h"
9#include "core/hle/service/service.h"
10
11namespace Core {
12class System;
13}
14
15namespace Service::PSC::Time {
16
17class IPowerStateRequestHandler final : public ServiceFramework<IPowerStateRequestHandler> {
18public:
19 explicit IPowerStateRequestHandler(Core::System& system,
20 PowerStateRequestManager& power_state_request_manager);
21
22 ~IPowerStateRequestHandler() override = default;
23
24private:
25 void GetPowerStateRequestEventReadableHandle(HLERequestContext& ctx);
26 void GetAndClearPowerStateRequest(HLERequestContext& ctx);
27
28 Core::System& m_system;
29 PowerStateRequestManager& m_power_state_request_manager;
30};
31
32} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/service_manager.cpp b/src/core/hle/service/psc/time/service_manager.cpp
new file mode 100644
index 000000000..60820aa9b
--- /dev/null
+++ b/src/core/hle/service/psc/time/service_manager.cpp
@@ -0,0 +1,494 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/core_timing.h"
6#include "core/hle/service/psc/time/power_state_service.h"
7#include "core/hle/service/psc/time/service_manager.h"
8#include "core/hle/service/psc/time/static.h"
9
10namespace Service::PSC::Time {
11
12ServiceManager::ServiceManager(Core::System& system_, std::shared_ptr<TimeManager> time,
13 ServerManager* server_manager)
14 : ServiceFramework{system_, "time:m"}, m_system{system}, m_time{std::move(time)},
15 m_server_manager{*server_manager},
16 m_local_system_clock{m_time->m_standard_local_system_clock},
17 m_user_system_clock{m_time->m_standard_user_system_clock},
18 m_network_system_clock{m_time->m_standard_network_system_clock},
19 m_steady_clock{m_time->m_standard_steady_clock}, m_time_zone{m_time->m_time_zone},
20 m_ephemeral_network_clock{m_time->m_ephemeral_network_clock},
21 m_shared_memory{m_time->m_shared_memory}, m_alarms{m_time->m_alarms},
22 m_local_system_context_writer{m_time->m_local_system_clock_context_writer},
23 m_network_system_context_writer{m_time->m_network_system_clock_context_writer},
24 m_ephemeral_system_context_writer{m_time->m_ephemeral_network_clock_context_writer},
25 m_local_operation{m_system}, m_network_operation{m_system}, m_ephemeral_operation{m_system} {
26 // clang-format off
27 static const FunctionInfo functions[] = {
28 {0, &ServiceManager::Handle_GetStaticServiceAsUser, "GetStaticServiceAsUser"},
29 {5, &ServiceManager::Handle_GetStaticServiceAsAdmin, "GetStaticServiceAsAdmin"},
30 {6, &ServiceManager::Handle_GetStaticServiceAsRepair, "GetStaticServiceAsRepair"},
31 {9, &ServiceManager::Handle_GetStaticServiceAsServiceManager, "GetStaticServiceAsServiceManager"},
32 {10, &ServiceManager::Handle_SetupStandardSteadyClockCore, "SetupStandardSteadyClockCore"},
33 {11, &ServiceManager::Handle_SetupStandardLocalSystemClockCore, "SetupStandardLocalSystemClockCore"},
34 {12, &ServiceManager::Handle_SetupStandardNetworkSystemClockCore, "SetupStandardNetworkSystemClockCore"},
35 {13, &ServiceManager::Handle_SetupStandardUserSystemClockCore, "SetupStandardUserSystemClockCore"},
36 {14, &ServiceManager::Handle_SetupTimeZoneServiceCore, "SetupTimeZoneServiceCore"},
37 {15, &ServiceManager::Handle_SetupEphemeralNetworkSystemClockCore, "SetupEphemeralNetworkSystemClockCore"},
38 {50, &ServiceManager::Handle_GetStandardLocalClockOperationEvent, "GetStandardLocalClockOperationEvent"},
39 {51, &ServiceManager::Handle_GetStandardNetworkClockOperationEventForServiceManager, "GetStandardNetworkClockOperationEventForServiceManager"},
40 {52, &ServiceManager::Handle_GetEphemeralNetworkClockOperationEventForServiceManager, "GetEphemeralNetworkClockOperationEventForServiceManager"},
41 {60, &ServiceManager::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent, "GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent"},
42 {100, &ServiceManager::Handle_SetStandardSteadyClockBaseTime, "SetStandardSteadyClockBaseTime"},
43 {200, &ServiceManager::Handle_GetClosestAlarmUpdatedEvent, "GetClosestAlarmUpdatedEvent"},
44 {201, &ServiceManager::Handle_CheckAndSignalAlarms, "CheckAndSignalAlarms"},
45 {202, &ServiceManager::Handle_GetClosestAlarmInfo, "GetClosestAlarmInfo "},
46 };
47 // clang-format on
48 RegisterHandlers(functions);
49
50 m_local_system_context_writer.Link(m_local_operation);
51 m_network_system_context_writer.Link(m_network_operation);
52 m_ephemeral_system_context_writer.Link(m_ephemeral_operation);
53}
54
55void ServiceManager::SetupSAndP() {
56 if (!m_is_s_and_p_setup) {
57 m_is_s_and_p_setup = true;
58 m_server_manager.RegisterNamedService(
59 "time:s", std::make_shared<StaticService>(
60 m_system, StaticServiceSetupInfo{0, 0, 1, 0, 0, 0}, m_time, "time:s"));
61 m_server_manager.RegisterNamedService("time:p",
62 std::make_shared<IPowerStateRequestHandler>(
63 m_system, m_time->m_power_state_request_manager));
64 }
65}
66
67void ServiceManager::CheckAndSetupServicesSAndP() {
68 if (m_local_system_clock.IsInitialized() && m_user_system_clock.IsInitialized() &&
69 m_network_system_clock.IsInitialized() && m_steady_clock.IsInitialized() &&
70 m_time_zone.IsInitialized() && m_ephemeral_network_clock.IsInitialized()) {
71 SetupSAndP();
72 }
73}
74
75void ServiceManager::Handle_GetStaticServiceAsUser(HLERequestContext& ctx) {
76 LOG_DEBUG(Service_Time, "called.");
77
78 std::shared_ptr<StaticService> service{};
79 auto res = GetStaticServiceAsUser(service);
80
81 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
82 rb.Push(res);
83 rb.PushIpcInterface<StaticService>(std::move(service));
84}
85
86void ServiceManager::Handle_GetStaticServiceAsAdmin(HLERequestContext& ctx) {
87 LOG_DEBUG(Service_Time, "called.");
88
89 std::shared_ptr<StaticService> service{};
90 auto res = GetStaticServiceAsAdmin(service);
91
92 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
93 rb.Push(res);
94 rb.PushIpcInterface<StaticService>(std::move(service));
95}
96
97void ServiceManager::Handle_GetStaticServiceAsRepair(HLERequestContext& ctx) {
98 LOG_DEBUG(Service_Time, "called.");
99
100 std::shared_ptr<StaticService> service{};
101 auto res = GetStaticServiceAsRepair(service);
102
103 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
104 rb.Push(res);
105 rb.PushIpcInterface<StaticService>(std::move(service));
106}
107
108void ServiceManager::Handle_GetStaticServiceAsServiceManager(HLERequestContext& ctx) {
109 LOG_DEBUG(Service_Time, "called.");
110
111 std::shared_ptr<StaticService> service{};
112 auto res = GetStaticServiceAsServiceManager(service);
113
114 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
115 rb.Push(res);
116 rb.PushIpcInterface<StaticService>(std::move(service));
117}
118
119void ServiceManager::Handle_SetupStandardSteadyClockCore(HLERequestContext& ctx) {
120 LOG_DEBUG(Service_Time, "called.");
121
122 struct Parameters {
123 bool reset_detected;
124 Common::UUID clock_source_id;
125 s64 rtc_offset;
126 s64 internal_offset;
127 s64 test_offset;
128 };
129 static_assert(sizeof(Parameters) == 0x30);
130
131 IPC::RequestParser rp{ctx};
132 auto params{rp.PopRaw<Parameters>()};
133
134 auto res = SetupStandardSteadyClockCore(params.clock_source_id, params.rtc_offset,
135 params.internal_offset, params.test_offset,
136 params.reset_detected);
137
138 IPC::ResponseBuilder rb{ctx, 2};
139 rb.Push(res);
140}
141
142void ServiceManager::Handle_SetupStandardLocalSystemClockCore(HLERequestContext& ctx) {
143 LOG_DEBUG(Service_Time, "called.");
144
145 IPC::RequestParser rp{ctx};
146 auto context{rp.PopRaw<SystemClockContext>()};
147 auto time{rp.Pop<s64>()};
148
149 auto res = SetupStandardLocalSystemClockCore(context, time);
150
151 IPC::ResponseBuilder rb{ctx, 2};
152 rb.Push(res);
153}
154
155void ServiceManager::Handle_SetupStandardNetworkSystemClockCore(HLERequestContext& ctx) {
156 LOG_DEBUG(Service_Time, "called.");
157
158 IPC::RequestParser rp{ctx};
159 auto context{rp.PopRaw<SystemClockContext>()};
160 auto accuracy{rp.Pop<s64>()};
161
162 auto res = SetupStandardNetworkSystemClockCore(context, accuracy);
163
164 IPC::ResponseBuilder rb{ctx, 2};
165 rb.Push(res);
166}
167
168void ServiceManager::Handle_SetupStandardUserSystemClockCore(HLERequestContext& ctx) {
169 LOG_DEBUG(Service_Time, "called.");
170
171 struct Parameters {
172 bool automatic_correction;
173 SteadyClockTimePoint time_point;
174 };
175 static_assert(sizeof(Parameters) == 0x20);
176
177 IPC::RequestParser rp{ctx};
178 auto params{rp.PopRaw<Parameters>()};
179
180 auto res = SetupStandardUserSystemClockCore(params.time_point, params.automatic_correction);
181
182 IPC::ResponseBuilder rb{ctx, 2};
183 rb.Push(res);
184}
185
186void ServiceManager::Handle_SetupTimeZoneServiceCore(HLERequestContext& ctx) {
187 LOG_DEBUG(Service_Time, "called.");
188
189 struct Parameters {
190 u32 location_count;
191 LocationName name;
192 SteadyClockTimePoint time_point;
193 RuleVersion rule_version;
194 };
195 static_assert(sizeof(Parameters) == 0x50);
196
197 IPC::RequestParser rp{ctx};
198 auto params{rp.PopRaw<Parameters>()};
199
200 auto rule_buffer{ctx.ReadBuffer()};
201
202 auto res = SetupTimeZoneServiceCore(params.name, params.time_point, params.rule_version,
203 params.location_count, rule_buffer);
204
205 IPC::ResponseBuilder rb{ctx, 2};
206 rb.Push(res);
207}
208
209void ServiceManager::Handle_SetupEphemeralNetworkSystemClockCore(HLERequestContext& ctx) {
210 LOG_DEBUG(Service_Time, "called.");
211
212 auto res = SetupEphemeralNetworkSystemClockCore();
213
214 IPC::ResponseBuilder rb{ctx, 2};
215 rb.Push(res);
216}
217
218void ServiceManager::Handle_GetStandardLocalClockOperationEvent(HLERequestContext& ctx) {
219 LOG_DEBUG(Service_Time, "called.");
220
221 Kernel::KEvent* event{};
222 auto res = GetStandardLocalClockOperationEvent(&event);
223
224 IPC::ResponseBuilder rb{ctx, 2, 1};
225 rb.Push(res);
226 rb.PushCopyObjects(event->GetReadableEvent());
227}
228
229void ServiceManager::Handle_GetStandardNetworkClockOperationEventForServiceManager(
230 HLERequestContext& ctx) {
231 LOG_DEBUG(Service_Time, "called.");
232
233 Kernel::KEvent* event{};
234 auto res = GetStandardNetworkClockOperationEventForServiceManager(&event);
235
236 IPC::ResponseBuilder rb{ctx, 2, 1};
237 rb.Push(res);
238 rb.PushCopyObjects(event);
239}
240
241void ServiceManager::Handle_GetEphemeralNetworkClockOperationEventForServiceManager(
242 HLERequestContext& ctx) {
243 LOG_DEBUG(Service_Time, "called.");
244
245 Kernel::KEvent* event{};
246 auto res = GetEphemeralNetworkClockOperationEventForServiceManager(&event);
247
248 IPC::ResponseBuilder rb{ctx, 2, 1};
249 rb.Push(res);
250 rb.PushCopyObjects(event);
251}
252
253void ServiceManager::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(
254 HLERequestContext& ctx) {
255 LOG_DEBUG(Service_Time, "called.");
256
257 Kernel::KEvent* event{};
258 auto res = GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(&event);
259
260 IPC::ResponseBuilder rb{ctx, 2, 1};
261 rb.Push(res);
262 rb.PushCopyObjects(event);
263}
264
265void ServiceManager::Handle_SetStandardSteadyClockBaseTime(HLERequestContext& ctx) {
266 LOG_DEBUG(Service_Time, "called.");
267
268 IPC::RequestParser rp{ctx};
269 auto base_time{rp.Pop<s64>()};
270
271 auto res = SetStandardSteadyClockBaseTime(base_time);
272
273 IPC::ResponseBuilder rb{ctx, 2};
274 rb.Push(res);
275}
276
277void ServiceManager::Handle_GetClosestAlarmUpdatedEvent(HLERequestContext& ctx) {
278 LOG_DEBUG(Service_Time, "called.");
279
280 Kernel::KEvent* event{};
281 auto res = GetClosestAlarmUpdatedEvent(&event);
282
283 IPC::ResponseBuilder rb{ctx, 3};
284 rb.Push(res);
285 rb.PushCopyObjects(event->GetReadableEvent());
286}
287
288void ServiceManager::Handle_CheckAndSignalAlarms(HLERequestContext& ctx) {
289 LOG_DEBUG(Service_Time, "called.");
290
291 auto res = CheckAndSignalAlarms();
292
293 IPC::ResponseBuilder rb{ctx, 2};
294 rb.Push(res);
295}
296
297void ServiceManager::Handle_GetClosestAlarmInfo(HLERequestContext& ctx) {
298 LOG_DEBUG(Service_Time, "called.");
299
300 AlarmInfo alarm_info{};
301 bool is_valid{};
302 s64 time{};
303 auto res = GetClosestAlarmInfo(is_valid, alarm_info, time);
304
305 struct OutParameters {
306 bool is_valid;
307 AlarmInfo alarm_info;
308 s64 time;
309 };
310 static_assert(sizeof(OutParameters) == 0x20);
311
312 OutParameters out_params{
313 .is_valid = is_valid,
314 .alarm_info = alarm_info,
315 .time = time,
316 };
317
318 IPC::ResponseBuilder rb{ctx, 2 + sizeof(OutParameters) / sizeof(u32)};
319 rb.Push(res);
320 rb.PushRaw<OutParameters>(out_params);
321}
322
323// =============================== Implementations ===========================
324
325Result ServiceManager::GetStaticService(std::shared_ptr<StaticService>& out_service,
326 StaticServiceSetupInfo setup_info, const char* name) {
327 out_service = std::make_shared<StaticService>(m_system, setup_info, m_time, name);
328 R_SUCCEED();
329}
330
331Result ServiceManager::GetStaticServiceAsUser(std::shared_ptr<StaticService>& out_service) {
332 R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{0, 0, 0, 0, 0, 0}, "time:u"));
333}
334
335Result ServiceManager::GetStaticServiceAsAdmin(std::shared_ptr<StaticService>& out_service) {
336 R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{1, 1, 0, 1, 0, 0}, "time:a"));
337}
338
339Result ServiceManager::GetStaticServiceAsRepair(std::shared_ptr<StaticService>& out_service) {
340 R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{0, 0, 0, 0, 1, 0}, "time:r"));
341}
342
343Result ServiceManager::GetStaticServiceAsServiceManager(
344 std::shared_ptr<StaticService>& out_service) {
345 R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{1, 1, 1, 1, 1, 0}, "time:sm"));
346}
347
348Result ServiceManager::SetupStandardSteadyClockCore(Common::UUID& clock_source_id, s64 rtc_offset,
349 s64 internal_offset, s64 test_offset,
350 bool is_rtc_reset_detected) {
351 m_steady_clock.Initialize(clock_source_id, rtc_offset, internal_offset, test_offset,
352 is_rtc_reset_detected);
353 auto time = m_steady_clock.GetRawTime();
354 auto ticks = m_system.CoreTiming().GetClockTicks();
355 auto boot_time = time - ConvertToTimeSpan(ticks).count();
356 m_shared_memory.SetSteadyClockTimePoint(clock_source_id, boot_time);
357 m_steady_clock.SetContinuousAdjustment(clock_source_id, boot_time);
358
359 ContinuousAdjustmentTimePoint time_point{};
360 m_steady_clock.GetContinuousAdjustment(time_point);
361 m_shared_memory.SetContinuousAdjustment(time_point);
362
363 CheckAndSetupServicesSAndP();
364 R_SUCCEED();
365}
366
367Result ServiceManager::SetupStandardLocalSystemClockCore(SystemClockContext& context, s64 time) {
368 m_local_system_clock.SetContextWriter(m_local_system_context_writer);
369 m_local_system_clock.Initialize(context, time);
370
371 CheckAndSetupServicesSAndP();
372 R_SUCCEED();
373}
374
375Result ServiceManager::SetupStandardNetworkSystemClockCore(SystemClockContext& context,
376 s64 accuracy) {
377 // TODO this is a hack! The network clock should be updated independently, from the ntc service
378 // and maybe elsewhere. We do not do that, so fix the clock to the local clock on first boot
379 // to avoid it being stuck at 0.
380 if (context == Service::PSC::Time::SystemClockContext{}) {
381 m_local_system_clock.GetContext(context);
382 }
383
384 m_network_system_clock.SetContextWriter(m_network_system_context_writer);
385 m_network_system_clock.Initialize(context, accuracy);
386
387 CheckAndSetupServicesSAndP();
388 R_SUCCEED();
389}
390
391Result ServiceManager::SetupStandardUserSystemClockCore(SteadyClockTimePoint& time_point,
392 bool automatic_correction) {
393 // TODO this is a hack! The user clock should be updated independently, from the ntc service
394 // and maybe elsewhere. We do not do that, so fix the clock to the local clock on first boot
395 // to avoid it being stuck at 0.
396 if (time_point == Service::PSC::Time::SteadyClockTimePoint{}) {
397 m_local_system_clock.GetCurrentTimePoint(time_point);
398 }
399
400 m_user_system_clock.SetAutomaticCorrection(automatic_correction);
401 m_user_system_clock.SetTimePointAndSignal(time_point);
402 m_user_system_clock.SetInitialized();
403 m_shared_memory.SetAutomaticCorrection(automatic_correction);
404
405 CheckAndSetupServicesSAndP();
406 R_SUCCEED();
407}
408
409Result ServiceManager::SetupTimeZoneServiceCore(LocationName& name,
410 SteadyClockTimePoint& time_point,
411 RuleVersion& rule_version, u32 location_count,
412 std::span<const u8> rule_buffer) {
413 if (m_time_zone.ParseBinary(name, rule_buffer) != ResultSuccess) {
414 LOG_ERROR(Service_Time, "Failed to parse time zone binary!");
415 }
416
417 m_time_zone.SetTimePoint(time_point);
418 m_time_zone.SetTotalLocationNameCount(location_count);
419 m_time_zone.SetRuleVersion(rule_version);
420 m_time_zone.SetInitialized();
421
422 CheckAndSetupServicesSAndP();
423 R_SUCCEED();
424}
425
426Result ServiceManager::SetupEphemeralNetworkSystemClockCore() {
427 m_ephemeral_network_clock.SetContextWriter(m_ephemeral_system_context_writer);
428 m_ephemeral_network_clock.SetInitialized();
429
430 CheckAndSetupServicesSAndP();
431 R_SUCCEED();
432}
433
434Result ServiceManager::GetStandardLocalClockOperationEvent(Kernel::KEvent** out_event) {
435 *out_event = m_local_operation.m_event;
436 R_SUCCEED();
437}
438
439Result ServiceManager::GetStandardNetworkClockOperationEventForServiceManager(
440 Kernel::KEvent** out_event) {
441 *out_event = m_network_operation.m_event;
442 R_SUCCEED();
443}
444
445Result ServiceManager::GetEphemeralNetworkClockOperationEventForServiceManager(
446 Kernel::KEvent** out_event) {
447 *out_event = m_ephemeral_operation.m_event;
448 R_SUCCEED();
449}
450
451Result ServiceManager::GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(
452 Kernel::KEvent** out_event) {
453 *out_event = &m_user_system_clock.GetEvent();
454 R_SUCCEED();
455}
456
457Result ServiceManager::SetStandardSteadyClockBaseTime(s64 base_time) {
458 m_steady_clock.SetRtcOffset(base_time);
459 auto time = m_steady_clock.GetRawTime();
460 auto ticks = m_system.CoreTiming().GetClockTicks();
461 auto diff = time - ConvertToTimeSpan(ticks).count();
462 m_shared_memory.UpdateBaseTime(diff);
463 m_steady_clock.UpdateContinuousAdjustmentTime(diff);
464
465 ContinuousAdjustmentTimePoint time_point{};
466 m_steady_clock.GetContinuousAdjustment(time_point);
467 m_shared_memory.SetContinuousAdjustment(time_point);
468 R_SUCCEED();
469}
470
471Result ServiceManager::GetClosestAlarmUpdatedEvent(Kernel::KEvent** out_event) {
472 *out_event = &m_alarms.GetEvent();
473 R_SUCCEED();
474}
475
476Result ServiceManager::CheckAndSignalAlarms() {
477 m_alarms.CheckAndSignal();
478 R_SUCCEED();
479}
480
481Result ServiceManager::GetClosestAlarmInfo(bool& out_is_valid, AlarmInfo& out_info, s64& out_time) {
482 Alarm* alarm{nullptr};
483 out_is_valid = m_alarms.GetClosestAlarm(&alarm);
484 if (out_is_valid) {
485 out_info = {
486 .alert_time = alarm->GetAlertTime(),
487 .priority = alarm->GetPriority(),
488 };
489 out_time = m_alarms.GetRawTime();
490 }
491 R_SUCCEED();
492}
493
494} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/service_manager.h b/src/core/hle/service/psc/time/service_manager.h
new file mode 100644
index 000000000..1d9952317
--- /dev/null
+++ b/src/core/hle/service/psc/time/service_manager.h
@@ -0,0 +1,101 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <list>
7#include <memory>
8
9#include "core/hle/service/ipc_helpers.h"
10#include "core/hle/service/psc/time/common.h"
11#include "core/hle/service/psc/time/manager.h"
12#include "core/hle/service/server_manager.h"
13#include "core/hle/service/service.h"
14
15namespace Core {
16class System;
17}
18
19namespace Kernel {
20class KReadableEvent;
21}
22
23namespace Service::PSC::Time {
24class StaticService;
25
26class ServiceManager final : public ServiceFramework<ServiceManager> {
27public:
28 explicit ServiceManager(Core::System& system, std::shared_ptr<TimeManager> time,
29 ServerManager* server_manager);
30 ~ServiceManager() override = default;
31
32 Result GetStaticServiceAsUser(std::shared_ptr<StaticService>& out_service);
33 Result GetStaticServiceAsAdmin(std::shared_ptr<StaticService>& out_service);
34 Result GetStaticServiceAsRepair(std::shared_ptr<StaticService>& out_service);
35 Result GetStaticServiceAsServiceManager(std::shared_ptr<StaticService>& out_service);
36 Result SetupStandardSteadyClockCore(Common::UUID& clock_source_id, s64 rtc_offset,
37 s64 internal_offset, s64 test_offset,
38 bool is_rtc_reset_detected);
39 Result SetupStandardLocalSystemClockCore(SystemClockContext& context, s64 time);
40 Result SetupStandardNetworkSystemClockCore(SystemClockContext& context, s64 accuracy);
41 Result SetupStandardUserSystemClockCore(SteadyClockTimePoint& time_point,
42 bool automatic_correction);
43 Result SetupTimeZoneServiceCore(LocationName& name, SteadyClockTimePoint& time_point,
44 RuleVersion& rule_version, u32 location_count,
45 std::span<const u8> rule_buffer);
46 Result SetupEphemeralNetworkSystemClockCore();
47 Result GetStandardLocalClockOperationEvent(Kernel::KEvent** out_event);
48 Result GetStandardNetworkClockOperationEventForServiceManager(Kernel::KEvent** out_event);
49 Result GetEphemeralNetworkClockOperationEventForServiceManager(Kernel::KEvent** out_event);
50 Result GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(Kernel::KEvent** out_event);
51 Result SetStandardSteadyClockBaseTime(s64 base_time);
52 Result GetClosestAlarmUpdatedEvent(Kernel::KEvent** out_event);
53 Result CheckAndSignalAlarms();
54 Result GetClosestAlarmInfo(bool& out_is_valid, AlarmInfo& out_info, s64& out_time);
55
56private:
57 void CheckAndSetupServicesSAndP();
58 void SetupSAndP();
59 Result GetStaticService(std::shared_ptr<StaticService>& out_service,
60 StaticServiceSetupInfo setup_info, const char* name);
61
62 void Handle_GetStaticServiceAsUser(HLERequestContext& ctx);
63 void Handle_GetStaticServiceAsAdmin(HLERequestContext& ctx);
64 void Handle_GetStaticServiceAsRepair(HLERequestContext& ctx);
65 void Handle_GetStaticServiceAsServiceManager(HLERequestContext& ctx);
66 void Handle_SetupStandardSteadyClockCore(HLERequestContext& ctx);
67 void Handle_SetupStandardLocalSystemClockCore(HLERequestContext& ctx);
68 void Handle_SetupStandardNetworkSystemClockCore(HLERequestContext& ctx);
69 void Handle_SetupStandardUserSystemClockCore(HLERequestContext& ctx);
70 void Handle_SetupTimeZoneServiceCore(HLERequestContext& ctx);
71 void Handle_SetupEphemeralNetworkSystemClockCore(HLERequestContext& ctx);
72 void Handle_GetStandardLocalClockOperationEvent(HLERequestContext& ctx);
73 void Handle_GetStandardNetworkClockOperationEventForServiceManager(HLERequestContext& ctx);
74 void Handle_GetEphemeralNetworkClockOperationEventForServiceManager(HLERequestContext& ctx);
75 void Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(HLERequestContext& ctx);
76 void Handle_SetStandardSteadyClockBaseTime(HLERequestContext& ctx);
77 void Handle_GetClosestAlarmUpdatedEvent(HLERequestContext& ctx);
78 void Handle_CheckAndSignalAlarms(HLERequestContext& ctx);
79 void Handle_GetClosestAlarmInfo(HLERequestContext& ctx);
80
81 Core::System& m_system;
82 std::shared_ptr<TimeManager> m_time;
83 ServerManager& m_server_manager;
84 bool m_is_s_and_p_setup{};
85 StandardLocalSystemClockCore& m_local_system_clock;
86 StandardUserSystemClockCore& m_user_system_clock;
87 StandardNetworkSystemClockCore& m_network_system_clock;
88 StandardSteadyClockCore& m_steady_clock;
89 TimeZone& m_time_zone;
90 EphemeralNetworkSystemClockCore& m_ephemeral_network_clock;
91 SharedMemory& m_shared_memory;
92 Alarms& m_alarms;
93 LocalSystemClockContextWriter& m_local_system_context_writer;
94 NetworkSystemClockContextWriter& m_network_system_context_writer;
95 EphemeralNetworkSystemClockContextWriter& m_ephemeral_system_context_writer;
96 OperationEvent m_local_operation;
97 OperationEvent m_network_operation;
98 OperationEvent m_ephemeral_operation;
99};
100
101} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/shared_memory.cpp b/src/core/hle/service/psc/time/shared_memory.cpp
new file mode 100644
index 000000000..defaceebe
--- /dev/null
+++ b/src/core/hle/service/psc/time/shared_memory.cpp
@@ -0,0 +1,84 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_shared_memory.h"
6#include "core/hle/service/psc/time/shared_memory.h"
7
8namespace Service::PSC::Time {
9namespace {
10template <typename T>
11constexpr inline T ReadFromLockFreeAtomicType(const LockFreeAtomicType<T>* p) {
12 while (true) {
13 // Get the counter.
14 auto counter = p->m_counter;
15
16 // Get the value.
17 auto value = p->m_value[counter % 2];
18
19 // Fence memory.
20 std::atomic_thread_fence(std::memory_order_acquire);
21
22 // Check that the counter matches.
23 if (counter == p->m_counter) {
24 return value;
25 }
26 }
27}
28
29template <typename T>
30constexpr inline void WriteToLockFreeAtomicType(LockFreeAtomicType<T>* p, const T& value) {
31 // Get the current counter.
32 auto counter = p->m_counter;
33
34 // Increment the counter.
35 ++counter;
36
37 // Store the updated value.
38 p->m_value[counter % 2] = value;
39
40 // Fence memory.
41 std::atomic_thread_fence(std::memory_order_release);
42
43 // Set the updated counter.
44 p->m_counter = counter;
45}
46} // namespace
47
48SharedMemory::SharedMemory(Core::System& system)
49 : m_system{system}, m_k_shared_memory{m_system.Kernel().GetTimeSharedMem()},
50 m_shared_memory_ptr{reinterpret_cast<SharedMemoryStruct*>(m_k_shared_memory.GetPointer())} {
51 std::memset(m_shared_memory_ptr, 0, sizeof(*m_shared_memory_ptr));
52}
53
54void SharedMemory::SetLocalSystemContext(SystemClockContext& context) {
55 WriteToLockFreeAtomicType(&m_shared_memory_ptr->local_system_clock_contexts, context);
56}
57
58void SharedMemory::SetNetworkSystemContext(SystemClockContext& context) {
59 WriteToLockFreeAtomicType(&m_shared_memory_ptr->network_system_clock_contexts, context);
60}
61
62void SharedMemory::SetSteadyClockTimePoint(ClockSourceId clock_source_id, s64 time_point) {
63 WriteToLockFreeAtomicType(&m_shared_memory_ptr->steady_time_points,
64 {time_point, clock_source_id});
65}
66
67void SharedMemory::SetContinuousAdjustment(ContinuousAdjustmentTimePoint& time_point) {
68 WriteToLockFreeAtomicType(&m_shared_memory_ptr->continuous_adjustment_time_points, time_point);
69}
70
71void SharedMemory::SetAutomaticCorrection(bool automatic_correction) {
72 WriteToLockFreeAtomicType(&m_shared_memory_ptr->automatic_corrections, automatic_correction);
73}
74
75void SharedMemory::UpdateBaseTime(s64 time) {
76 SteadyClockTimePoint time_point{
77 ReadFromLockFreeAtomicType(&m_shared_memory_ptr->steady_time_points)};
78
79 time_point.time_point = time;
80
81 WriteToLockFreeAtomicType(&m_shared_memory_ptr->steady_time_points, time_point);
82}
83
84} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/shared_memory.h b/src/core/hle/service/psc/time/shared_memory.h
new file mode 100644
index 000000000..f9bf97d5c
--- /dev/null
+++ b/src/core/hle/service/psc/time/shared_memory.h
@@ -0,0 +1,70 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7
8#include "common/common_types.h"
9#include "core/hle/service/psc/time/common.h"
10
11namespace Core {
12class System;
13}
14
15namespace Kernel {
16class KSharedMemory;
17}
18
19namespace Service::PSC::Time {
20
21template <typename T>
22struct LockFreeAtomicType {
23 u32 m_counter;
24 std::array<T, 2> m_value;
25};
26
27struct SharedMemoryStruct {
28 LockFreeAtomicType<SteadyClockTimePoint> steady_time_points;
29 LockFreeAtomicType<SystemClockContext> local_system_clock_contexts;
30 LockFreeAtomicType<SystemClockContext> network_system_clock_contexts;
31 LockFreeAtomicType<bool> automatic_corrections;
32 LockFreeAtomicType<ContinuousAdjustmentTimePoint> continuous_adjustment_time_points;
33 std::array<char, 0xEB8> pad0148;
34};
35static_assert(offsetof(SharedMemoryStruct, steady_time_points) == 0x0,
36 "steady_time_points are in the wrong place!");
37static_assert(offsetof(SharedMemoryStruct, local_system_clock_contexts) == 0x38,
38 "local_system_clock_contexts are in the wrong place!");
39static_assert(offsetof(SharedMemoryStruct, network_system_clock_contexts) == 0x80,
40 "network_system_clock_contexts are in the wrong place!");
41static_assert(offsetof(SharedMemoryStruct, automatic_corrections) == 0xC8,
42 "automatic_corrections are in the wrong place!");
43static_assert(offsetof(SharedMemoryStruct, continuous_adjustment_time_points) == 0xD0,
44 "continuous_adjustment_time_points are in the wrong place!");
45static_assert(sizeof(SharedMemoryStruct) == 0x1000,
46 "Time's SharedMemoryStruct has the wrong size!");
47static_assert(std::is_trivial_v<SharedMemoryStruct>);
48
49class SharedMemory {
50public:
51 explicit SharedMemory(Core::System& system);
52
53 Kernel::KSharedMemory& GetKSharedMemory() {
54 return m_k_shared_memory;
55 }
56
57 void SetLocalSystemContext(SystemClockContext& context);
58 void SetNetworkSystemContext(SystemClockContext& context);
59 void SetSteadyClockTimePoint(ClockSourceId clock_source_id, s64 time_diff);
60 void SetContinuousAdjustment(ContinuousAdjustmentTimePoint& time_point);
61 void SetAutomaticCorrection(bool automatic_correction);
62 void UpdateBaseTime(s64 time);
63
64private:
65 Core::System& m_system;
66 Kernel::KSharedMemory& m_k_shared_memory;
67 SharedMemoryStruct* m_shared_memory_ptr;
68};
69
70} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/static.cpp b/src/core/hle/service/psc/time/static.cpp
new file mode 100644
index 000000000..6f8cf3f88
--- /dev/null
+++ b/src/core/hle/service/psc/time/static.cpp
@@ -0,0 +1,500 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/core_timing.h"
6#include "core/hle/kernel/k_shared_memory.h"
7#include "core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h"
8#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h"
9#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h"
10#include "core/hle/service/psc/time/clocks/standard_user_system_clock_core.h"
11#include "core/hle/service/psc/time/manager.h"
12#include "core/hle/service/psc/time/shared_memory.h"
13#include "core/hle/service/psc/time/static.h"
14#include "core/hle/service/psc/time/steady_clock.h"
15#include "core/hle/service/psc/time/system_clock.h"
16#include "core/hle/service/psc/time/time_zone.h"
17#include "core/hle/service/psc/time/time_zone_service.h"
18
19namespace Service::PSC::Time {
20namespace {
21constexpr Result GetTimeFromTimePointAndContext(s64* out_time, SteadyClockTimePoint& time_point,
22 SystemClockContext& context) {
23 R_UNLESS(out_time != nullptr, ResultInvalidArgument);
24 R_UNLESS(time_point.IdMatches(context.steady_time_point), ResultClockMismatch);
25
26 *out_time = context.offset + time_point.time_point;
27 R_SUCCEED();
28}
29} // namespace
30
31StaticService::StaticService(Core::System& system_, StaticServiceSetupInfo setup_info,
32 std::shared_ptr<TimeManager> time, const char* name)
33 : ServiceFramework{system_, name}, m_system{system}, m_setup_info{setup_info}, m_time{time},
34 m_local_system_clock{m_time->m_standard_local_system_clock},
35 m_user_system_clock{m_time->m_standard_user_system_clock},
36 m_network_system_clock{m_time->m_standard_network_system_clock},
37 m_time_zone{m_time->m_time_zone},
38 m_ephemeral_network_clock{m_time->m_ephemeral_network_clock}, m_shared_memory{
39 m_time->m_shared_memory} {
40 // clang-format off
41 static const FunctionInfo functions[] = {
42 {0, &StaticService::Handle_GetStandardUserSystemClock, "GetStandardUserSystemClock"},
43 {1, &StaticService::Handle_GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"},
44 {2, &StaticService::Handle_GetStandardSteadyClock, "GetStandardSteadyClock"},
45 {3, &StaticService::Handle_GetTimeZoneService, "GetTimeZoneService"},
46 {4, &StaticService::Handle_GetStandardLocalSystemClock, "GetStandardLocalSystemClock"},
47 {5, &StaticService::Handle_GetEphemeralNetworkSystemClock, "GetEphemeralNetworkSystemClock"},
48 {20, &StaticService::Handle_GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"},
49 {50, &StaticService::Handle_SetStandardSteadyClockInternalOffset, "SetStandardSteadyClockInternalOffset"},
50 {51, &StaticService::Handle_GetStandardSteadyClockRtcValue, "GetStandardSteadyClockRtcValue"},
51 {100, &StaticService::Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled, "IsStandardUserSystemClockAutomaticCorrectionEnabled"},
52 {101, &StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled, "SetStandardUserSystemClockAutomaticCorrectionEnabled"},
53 {102, &StaticService::Handle_GetStandardUserSystemClockInitialYear, "GetStandardUserSystemClockInitialYear"},
54 {200, &StaticService::Handle_IsStandardNetworkSystemClockAccuracySufficient, "IsStandardNetworkSystemClockAccuracySufficient"},
55 {201, &StaticService::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"},
56 {300, &StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint, "CalculateMonotonicSystemClockBaseTimePoint"},
57 {400, &StaticService::Handle_GetClockSnapshot, "GetClockSnapshot"},
58 {401, &StaticService::Handle_GetClockSnapshotFromSystemClockContext, "GetClockSnapshotFromSystemClockContext"},
59 {500, &StaticService::Handle_CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"},
60 {501, &StaticService::Handle_CalculateSpanBetween, "CalculateSpanBetween"},
61 };
62 // clang-format on
63
64 RegisterHandlers(functions);
65}
66
67Result StaticService::GetClockSnapshotImpl(ClockSnapshot& out_snapshot,
68 SystemClockContext& user_context,
69 SystemClockContext& network_context, TimeType type) {
70 out_snapshot.user_context = user_context;
71 out_snapshot.network_context = network_context;
72
73 R_TRY(
74 m_time->m_standard_steady_clock.GetCurrentTimePoint(out_snapshot.steady_clock_time_point));
75
76 out_snapshot.is_automatic_correction_enabled = m_user_system_clock.GetAutomaticCorrection();
77
78 R_TRY(m_time_zone.GetLocationName(out_snapshot.location_name));
79
80 R_TRY(GetTimeFromTimePointAndContext(
81 &out_snapshot.user_time, out_snapshot.steady_clock_time_point, out_snapshot.user_context));
82
83 R_TRY(m_time_zone.ToCalendarTimeWithMyRule(out_snapshot.user_calendar_time,
84 out_snapshot.user_calendar_additional_time,
85 out_snapshot.user_time));
86
87 if (GetTimeFromTimePointAndContext(&out_snapshot.network_time,
88 out_snapshot.steady_clock_time_point,
89 out_snapshot.network_context) != ResultSuccess) {
90 out_snapshot.network_time = 0;
91 }
92
93 R_TRY(m_time_zone.ToCalendarTimeWithMyRule(out_snapshot.network_calendar_time,
94 out_snapshot.network_calendar_additional_time,
95 out_snapshot.network_time));
96 out_snapshot.type = type;
97 out_snapshot.unk_CE = 0;
98 R_SUCCEED();
99}
100
101void StaticService::Handle_GetStandardUserSystemClock(HLERequestContext& ctx) {
102 LOG_DEBUG(Service_Time, "called.");
103
104 std::shared_ptr<SystemClock> service{};
105 auto res = GetStandardUserSystemClock(service);
106
107 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
108 rb.Push(res);
109 rb.PushIpcInterface<SystemClock>(std::move(service));
110}
111
112void StaticService::Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx) {
113 LOG_DEBUG(Service_Time, "called.");
114
115 std::shared_ptr<SystemClock> service{};
116 auto res = GetStandardNetworkSystemClock(service);
117
118 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
119 rb.Push(res);
120 rb.PushIpcInterface<SystemClock>(std::move(service));
121}
122
123void StaticService::Handle_GetStandardSteadyClock(HLERequestContext& ctx) {
124 LOG_DEBUG(Service_Time, "called.");
125
126 std::shared_ptr<SteadyClock> service{};
127 auto res = GetStandardSteadyClock(service);
128
129 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
130 rb.Push(res);
131 rb.PushIpcInterface(std::move(service));
132}
133
134void StaticService::Handle_GetTimeZoneService(HLERequestContext& ctx) {
135 LOG_DEBUG(Service_Time, "called.");
136
137 std::shared_ptr<TimeZoneService> service{};
138 auto res = GetTimeZoneService(service);
139
140 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
141 rb.Push(res);
142 rb.PushIpcInterface(std::move(service));
143}
144
145void StaticService::Handle_GetStandardLocalSystemClock(HLERequestContext& ctx) {
146 LOG_DEBUG(Service_Time, "called.");
147
148 std::shared_ptr<SystemClock> service{};
149 auto res = GetStandardLocalSystemClock(service);
150
151 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
152 rb.Push(res);
153 rb.PushIpcInterface<SystemClock>(std::move(service));
154}
155
156void StaticService::Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx) {
157 LOG_DEBUG(Service_Time, "called.");
158
159 std::shared_ptr<SystemClock> service{};
160 auto res = GetEphemeralNetworkSystemClock(service);
161
162 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
163 rb.Push(res);
164 rb.PushIpcInterface<SystemClock>(std::move(service));
165}
166
167void StaticService::Handle_GetSharedMemoryNativeHandle(HLERequestContext& ctx) {
168 LOG_DEBUG(Service_Time, "called.");
169
170 Kernel::KSharedMemory* shared_memory{};
171 auto res = GetSharedMemoryNativeHandle(&shared_memory);
172
173 IPC::ResponseBuilder rb{ctx, 2, 1};
174 rb.Push(res);
175 rb.PushCopyObjects(shared_memory);
176}
177
178void StaticService::Handle_SetStandardSteadyClockInternalOffset(HLERequestContext& ctx) {
179 LOG_DEBUG(Service_Time, "called.");
180
181 IPC::ResponseBuilder rb{ctx, 2};
182 rb.Push(m_setup_info.can_write_steady_clock ? ResultNotImplemented : ResultPermissionDenied);
183}
184
185void StaticService::Handle_GetStandardSteadyClockRtcValue(HLERequestContext& ctx) {
186 LOG_DEBUG(Service_Time, "called.");
187
188 IPC::ResponseBuilder rb{ctx, 2};
189 rb.Push(ResultNotImplemented);
190}
191
192void StaticService::Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled(
193 HLERequestContext& ctx) {
194 LOG_DEBUG(Service_Time, "called.");
195
196 bool is_enabled{};
197 auto res = IsStandardUserSystemClockAutomaticCorrectionEnabled(is_enabled);
198
199 IPC::ResponseBuilder rb{ctx, 3};
200 rb.Push(res);
201 rb.Push<bool>(is_enabled);
202}
203
204void StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled(
205 HLERequestContext& ctx) {
206 LOG_DEBUG(Service_Time, "called.");
207
208 IPC::RequestParser rp{ctx};
209 auto automatic_correction{rp.Pop<bool>()};
210
211 auto res = SetStandardUserSystemClockAutomaticCorrectionEnabled(automatic_correction);
212
213 IPC::ResponseBuilder rb{ctx, 2};
214 rb.Push(res);
215}
216
217void StaticService::Handle_GetStandardUserSystemClockInitialYear(HLERequestContext& ctx) {
218 LOG_DEBUG(Service_Time, "called.");
219
220 IPC::ResponseBuilder rb{ctx, 2};
221 rb.Push(ResultNotImplemented);
222}
223
224void StaticService::Handle_IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx) {
225 LOG_DEBUG(Service_Time, "called.");
226
227 bool is_sufficient{};
228 auto res = IsStandardNetworkSystemClockAccuracySufficient(is_sufficient);
229
230 IPC::ResponseBuilder rb{ctx, 3};
231 rb.Push(res);
232 rb.Push<bool>(is_sufficient);
233}
234
235void StaticService::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
236 HLERequestContext& ctx) {
237 LOG_DEBUG(Service_Time, "called.");
238
239 SteadyClockTimePoint time_point{};
240 auto res = GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
241
242 IPC::ResponseBuilder rb{ctx, 2 + sizeof(SteadyClockTimePoint) / sizeof(u32)};
243 rb.Push(res);
244 rb.PushRaw<SteadyClockTimePoint>(time_point);
245}
246
247void StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx) {
248 LOG_DEBUG(Service_Time, "called.");
249
250 IPC::RequestParser rp{ctx};
251 auto context{rp.PopRaw<SystemClockContext>()};
252
253 s64 time{};
254 auto res = CalculateMonotonicSystemClockBaseTimePoint(time, context);
255
256 IPC::ResponseBuilder rb{ctx, 4};
257 rb.Push(res);
258 rb.Push<s64>(time);
259}
260
261void StaticService::Handle_GetClockSnapshot(HLERequestContext& ctx) {
262 LOG_DEBUG(Service_Time, "called.");
263
264 IPC::RequestParser rp{ctx};
265 auto type{rp.PopEnum<TimeType>()};
266
267 ClockSnapshot snapshot{};
268 auto res = GetClockSnapshot(snapshot, type);
269
270 ctx.WriteBuffer(snapshot);
271
272 IPC::ResponseBuilder rb{ctx, 2};
273 rb.Push(res);
274}
275
276void StaticService::Handle_GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx) {
277 LOG_DEBUG(Service_Time, "called.");
278
279 IPC::RequestParser rp{ctx};
280 auto clock_type{rp.PopEnum<TimeType>()};
281 [[maybe_unused]] auto alignment{rp.Pop<u32>()};
282 auto user_context{rp.PopRaw<SystemClockContext>()};
283 auto network_context{rp.PopRaw<SystemClockContext>()};
284
285 ClockSnapshot snapshot{};
286 auto res =
287 GetClockSnapshotFromSystemClockContext(snapshot, user_context, network_context, clock_type);
288
289 ctx.WriteBuffer(snapshot);
290
291 IPC::ResponseBuilder rb{ctx, 2};
292 rb.Push(res);
293}
294
295void StaticService::Handle_CalculateStandardUserSystemClockDifferenceByUser(
296 HLERequestContext& ctx) {
297 LOG_DEBUG(Service_Time, "called.");
298
299 ClockSnapshot a{};
300 ClockSnapshot b{};
301
302 auto a_buffer{ctx.ReadBuffer(0)};
303 auto b_buffer{ctx.ReadBuffer(1)};
304
305 std::memcpy(&a, a_buffer.data(), sizeof(ClockSnapshot));
306 std::memcpy(&b, b_buffer.data(), sizeof(ClockSnapshot));
307
308 s64 difference{};
309 auto res = CalculateStandardUserSystemClockDifferenceByUser(difference, a, b);
310
311 IPC::ResponseBuilder rb{ctx, 4};
312 rb.Push(res);
313 rb.Push(difference);
314}
315
316void StaticService::Handle_CalculateSpanBetween(HLERequestContext& ctx) {
317 LOG_DEBUG(Service_Time, "called.");
318
319 ClockSnapshot a{};
320 ClockSnapshot b{};
321
322 auto a_buffer{ctx.ReadBuffer(0)};
323 auto b_buffer{ctx.ReadBuffer(1)};
324
325 std::memcpy(&a, a_buffer.data(), sizeof(ClockSnapshot));
326 std::memcpy(&b, b_buffer.data(), sizeof(ClockSnapshot));
327
328 s64 time{};
329 auto res = CalculateSpanBetween(time, a, b);
330
331 IPC::ResponseBuilder rb{ctx, 4};
332 rb.Push(res);
333 rb.Push(time);
334}
335
336// =============================== Implementations ===========================
337
338Result StaticService::GetStandardUserSystemClock(std::shared_ptr<SystemClock>& out_service) {
339 out_service = std::make_shared<SystemClock>(m_system, m_user_system_clock,
340 m_setup_info.can_write_user_clock,
341 m_setup_info.can_write_uninitialized_clock);
342 R_SUCCEED();
343}
344
345Result StaticService::GetStandardNetworkSystemClock(std::shared_ptr<SystemClock>& out_service) {
346 out_service = std::make_shared<SystemClock>(m_system, m_network_system_clock,
347 m_setup_info.can_write_network_clock,
348 m_setup_info.can_write_uninitialized_clock);
349 R_SUCCEED();
350}
351
352Result StaticService::GetStandardSteadyClock(std::shared_ptr<SteadyClock>& out_service) {
353 out_service =
354 std::make_shared<SteadyClock>(m_system, m_time, m_setup_info.can_write_steady_clock,
355 m_setup_info.can_write_uninitialized_clock);
356 R_SUCCEED();
357}
358
359Result StaticService::GetTimeZoneService(std::shared_ptr<TimeZoneService>& out_service) {
360 out_service =
361 std::make_shared<TimeZoneService>(m_system, m_time->m_standard_steady_clock, m_time_zone,
362 m_setup_info.can_write_timezone_device_location);
363 R_SUCCEED();
364}
365
366Result StaticService::GetStandardLocalSystemClock(std::shared_ptr<SystemClock>& out_service) {
367 out_service = std::make_shared<SystemClock>(m_system, m_local_system_clock,
368 m_setup_info.can_write_local_clock,
369 m_setup_info.can_write_uninitialized_clock);
370 R_SUCCEED();
371}
372
373Result StaticService::GetEphemeralNetworkSystemClock(std::shared_ptr<SystemClock>& out_service) {
374 out_service = std::make_shared<SystemClock>(m_system, m_ephemeral_network_clock,
375 m_setup_info.can_write_network_clock,
376 m_setup_info.can_write_uninitialized_clock);
377 R_SUCCEED();
378}
379
380Result StaticService::GetSharedMemoryNativeHandle(Kernel::KSharedMemory** out_shared_memory) {
381 *out_shared_memory = &m_shared_memory.GetKSharedMemory();
382 R_SUCCEED();
383}
384
385Result StaticService::IsStandardUserSystemClockAutomaticCorrectionEnabled(bool& out_is_enabled) {
386 R_UNLESS(m_user_system_clock.IsInitialized(), ResultClockUninitialized);
387
388 out_is_enabled = m_user_system_clock.GetAutomaticCorrection();
389 R_SUCCEED();
390}
391
392Result StaticService::SetStandardUserSystemClockAutomaticCorrectionEnabled(
393 bool automatic_correction) {
394 R_UNLESS(m_user_system_clock.IsInitialized() && m_time->m_standard_steady_clock.IsInitialized(),
395 ResultClockUninitialized);
396 R_UNLESS(m_setup_info.can_write_user_clock, ResultPermissionDenied);
397
398 R_TRY(m_user_system_clock.SetAutomaticCorrection(automatic_correction));
399
400 m_shared_memory.SetAutomaticCorrection(automatic_correction);
401
402 SteadyClockTimePoint time_point{};
403 R_TRY(m_time->m_standard_steady_clock.GetCurrentTimePoint(time_point));
404
405 m_user_system_clock.SetTimePointAndSignal(time_point);
406 m_user_system_clock.GetEvent().Signal();
407 R_SUCCEED();
408}
409
410Result StaticService::IsStandardNetworkSystemClockAccuracySufficient(bool& out_is_sufficient) {
411 out_is_sufficient = m_network_system_clock.IsAccuracySufficient();
412 R_SUCCEED();
413}
414
415Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
416 SteadyClockTimePoint& out_time_point) {
417 R_UNLESS(m_user_system_clock.IsInitialized(), ResultClockUninitialized);
418
419 m_user_system_clock.GetTimePoint(out_time_point);
420
421 R_SUCCEED();
422}
423
424Result StaticService::CalculateMonotonicSystemClockBaseTimePoint(s64& out_time,
425 SystemClockContext& context) {
426 R_UNLESS(m_time->m_standard_steady_clock.IsInitialized(), ResultClockUninitialized);
427
428 SteadyClockTimePoint time_point{};
429 R_TRY(m_time->m_standard_steady_clock.GetCurrentTimePoint(time_point));
430
431 R_UNLESS(time_point.IdMatches(context.steady_time_point), ResultClockMismatch);
432
433 auto one_second_ns{
434 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
435 auto ticks{m_system.CoreTiming().GetClockTicks()};
436 auto current_time{ConvertToTimeSpan(ticks).count()};
437 out_time = ((context.offset + time_point.time_point) - (current_time / one_second_ns));
438 R_SUCCEED();
439}
440
441Result StaticService::GetClockSnapshot(ClockSnapshot& out_snapshot, TimeType type) {
442 SystemClockContext user_context{};
443 R_TRY(m_user_system_clock.GetContext(user_context));
444
445 SystemClockContext network_context{};
446 R_TRY(m_network_system_clock.GetContext(network_context));
447
448 R_RETURN(GetClockSnapshotImpl(out_snapshot, user_context, network_context, type));
449}
450
451Result StaticService::GetClockSnapshotFromSystemClockContext(ClockSnapshot& out_snapshot,
452 SystemClockContext& user_context,
453 SystemClockContext& network_context,
454 TimeType type) {
455 R_RETURN(GetClockSnapshotImpl(out_snapshot, user_context, network_context, type));
456}
457
458Result StaticService::CalculateStandardUserSystemClockDifferenceByUser(s64& out_time,
459 ClockSnapshot& a,
460 ClockSnapshot& b) {
461 auto diff_s =
462 std::chrono::seconds(b.user_context.offset) - std::chrono::seconds(a.user_context.offset);
463
464 if (a.user_context == b.user_context ||
465 !a.user_context.steady_time_point.IdMatches(b.user_context.steady_time_point)) {
466 out_time = 0;
467 R_SUCCEED();
468 }
469
470 if (!a.is_automatic_correction_enabled || !b.is_automatic_correction_enabled) {
471 out_time = std::chrono::duration_cast<std::chrono::nanoseconds>(diff_s).count();
472 R_SUCCEED();
473 }
474
475 if (a.network_context.steady_time_point.IdMatches(a.steady_clock_time_point) ||
476 b.network_context.steady_time_point.IdMatches(b.steady_clock_time_point)) {
477 out_time = 0;
478 R_SUCCEED();
479 }
480
481 out_time = std::chrono::duration_cast<std::chrono::nanoseconds>(diff_s).count();
482 R_SUCCEED();
483}
484
485Result StaticService::CalculateSpanBetween(s64& out_time, ClockSnapshot& a, ClockSnapshot& b) {
486 s64 time_s{};
487 auto res =
488 GetSpanBetweenTimePoints(&time_s, a.steady_clock_time_point, b.steady_clock_time_point);
489
490 if (res != ResultSuccess) {
491 R_UNLESS(a.network_time != 0 && b.network_time != 0, ResultTimeNotFound);
492 time_s = b.network_time - a.network_time;
493 }
494
495 out_time =
496 std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(time_s)).count();
497 R_SUCCEED();
498}
499
500} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/static.h b/src/core/hle/service/psc/time/static.h
new file mode 100644
index 000000000..498cd5ab5
--- /dev/null
+++ b/src/core/hle/service/psc/time/static.h
@@ -0,0 +1,95 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/ipc_helpers.h"
7#include "core/hle/service/psc/time/common.h"
8#include "core/hle/service/server_manager.h"
9#include "core/hle/service/service.h"
10
11namespace Core {
12class System;
13}
14
15namespace Kernel {
16class KSharedMemory;
17}
18
19namespace Service::PSC::Time {
20class TimeManager;
21class StandardLocalSystemClockCore;
22class StandardUserSystemClockCore;
23class StandardNetworkSystemClockCore;
24class TimeZone;
25class SystemClock;
26class SteadyClock;
27class TimeZoneService;
28class EphemeralNetworkSystemClockCore;
29class SharedMemory;
30
31class StaticService final : public ServiceFramework<StaticService> {
32public:
33 explicit StaticService(Core::System& system, StaticServiceSetupInfo setup_info,
34 std::shared_ptr<TimeManager> time, const char* name);
35
36 ~StaticService() override = default;
37
38 Result GetStandardUserSystemClock(std::shared_ptr<SystemClock>& out_service);
39 Result GetStandardNetworkSystemClock(std::shared_ptr<SystemClock>& out_service);
40 Result GetStandardSteadyClock(std::shared_ptr<SteadyClock>& out_service);
41 Result GetTimeZoneService(std::shared_ptr<TimeZoneService>& out_service);
42 Result GetStandardLocalSystemClock(std::shared_ptr<SystemClock>& out_service);
43 Result GetEphemeralNetworkSystemClock(std::shared_ptr<SystemClock>& out_service);
44 Result GetSharedMemoryNativeHandle(Kernel::KSharedMemory** out_shared_memory);
45 Result IsStandardUserSystemClockAutomaticCorrectionEnabled(bool& out_is_enabled);
46 Result SetStandardUserSystemClockAutomaticCorrectionEnabled(bool automatic_correction);
47 Result IsStandardNetworkSystemClockAccuracySufficient(bool& out_is_sufficient);
48 Result GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
49 SteadyClockTimePoint& out_time_point);
50 Result CalculateMonotonicSystemClockBaseTimePoint(s64& out_time, SystemClockContext& context);
51 Result GetClockSnapshot(ClockSnapshot& out_snapshot, TimeType type);
52 Result GetClockSnapshotFromSystemClockContext(ClockSnapshot& out_snapshot,
53 SystemClockContext& user_context,
54 SystemClockContext& network_context,
55 TimeType type);
56 Result CalculateStandardUserSystemClockDifferenceByUser(s64& out_time, ClockSnapshot& a,
57 ClockSnapshot& b);
58 Result CalculateSpanBetween(s64& out_time, ClockSnapshot& a, ClockSnapshot& b);
59
60private:
61 Result GetClockSnapshotImpl(ClockSnapshot& out_snapshot, SystemClockContext& user_context,
62 SystemClockContext& network_context, TimeType type);
63
64 void Handle_GetStandardUserSystemClock(HLERequestContext& ctx);
65 void Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx);
66 void Handle_GetStandardSteadyClock(HLERequestContext& ctx);
67 void Handle_GetTimeZoneService(HLERequestContext& ctx);
68 void Handle_GetStandardLocalSystemClock(HLERequestContext& ctx);
69 void Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx);
70 void Handle_GetSharedMemoryNativeHandle(HLERequestContext& ctx);
71 void Handle_SetStandardSteadyClockInternalOffset(HLERequestContext& ctx);
72 void Handle_GetStandardSteadyClockRtcValue(HLERequestContext& ctx);
73 void Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx);
74 void Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx);
75 void Handle_GetStandardUserSystemClockInitialYear(HLERequestContext& ctx);
76 void Handle_IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx);
77 void Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx);
78 void Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx);
79 void Handle_GetClockSnapshot(HLERequestContext& ctx);
80 void Handle_GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx);
81 void Handle_CalculateStandardUserSystemClockDifferenceByUser(HLERequestContext& ctx);
82 void Handle_CalculateSpanBetween(HLERequestContext& ctx);
83
84 Core::System& m_system;
85 StaticServiceSetupInfo m_setup_info;
86 std::shared_ptr<TimeManager> m_time;
87 StandardLocalSystemClockCore& m_local_system_clock;
88 StandardUserSystemClockCore& m_user_system_clock;
89 StandardNetworkSystemClockCore& m_network_system_clock;
90 TimeZone& m_time_zone;
91 EphemeralNetworkSystemClockCore& m_ephemeral_network_clock;
92 SharedMemory& m_shared_memory;
93};
94
95} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/steady_clock.cpp b/src/core/hle/service/psc/time/steady_clock.cpp
new file mode 100644
index 000000000..1ed5c7679
--- /dev/null
+++ b/src/core/hle/service/psc/time/steady_clock.cpp
@@ -0,0 +1,164 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/service/psc/time/steady_clock.h"
6
7namespace Service::PSC::Time {
8
9SteadyClock::SteadyClock(Core::System& system_, std::shared_ptr<TimeManager> manager,
10 bool can_write_steady_clock, bool can_write_uninitialized_clock)
11 : ServiceFramework{system_, "ISteadyClock"}, m_system{system},
12 m_clock_core{manager->m_standard_steady_clock},
13 m_can_write_steady_clock{can_write_steady_clock}, m_can_write_uninitialized_clock{
14 can_write_uninitialized_clock} {
15 // clang-format off
16 static const FunctionInfo functions[] = {
17 {0, &SteadyClock::Handle_GetCurrentTimePoint, "GetCurrentTimePoint"},
18 {2, &SteadyClock::Handle_GetTestOffset, "GetTestOffset"},
19 {3, &SteadyClock::Handle_SetTestOffset, "SetTestOffset"},
20 {100, &SteadyClock::Handle_GetRtcValue, "GetRtcValue"},
21 {101, &SteadyClock::Handle_IsRtcResetDetected, "IsRtcResetDetected"},
22 {102, &SteadyClock::Handle_GetSetupResultValue, "GetSetupResultValue"},
23 {200, &SteadyClock::Handle_GetInternalOffset, "GetInternalOffset"},
24 };
25 // clang-format on
26 RegisterHandlers(functions);
27}
28
29void SteadyClock::Handle_GetCurrentTimePoint(HLERequestContext& ctx) {
30 LOG_DEBUG(Service_Time, "called.");
31
32 SteadyClockTimePoint time_point{};
33 auto res = GetCurrentTimePoint(time_point);
34
35 IPC::ResponseBuilder rb{ctx, 2 + sizeof(SteadyClockTimePoint) / sizeof(u32)};
36 rb.Push(res);
37 rb.PushRaw<SteadyClockTimePoint>(time_point);
38}
39
40void SteadyClock::Handle_GetTestOffset(HLERequestContext& ctx) {
41 LOG_DEBUG(Service_Time, "called.");
42
43 s64 test_offset{};
44 auto res = GetTestOffset(test_offset);
45
46 IPC::ResponseBuilder rb{ctx, 4};
47 rb.Push(res);
48 rb.Push(test_offset);
49}
50
51void SteadyClock::Handle_SetTestOffset(HLERequestContext& ctx) {
52 LOG_DEBUG(Service_Time, "called.");
53
54 IPC::RequestParser rp{ctx};
55 auto test_offset{rp.Pop<s64>()};
56
57 auto res = SetTestOffset(test_offset);
58
59 IPC::ResponseBuilder rb{ctx, 2};
60 rb.Push(res);
61}
62
63void SteadyClock::Handle_GetRtcValue(HLERequestContext& ctx) {
64 LOG_DEBUG(Service_Time, "called.");
65
66 s64 rtc_value{};
67 auto res = GetRtcValue(rtc_value);
68
69 IPC::ResponseBuilder rb{ctx, 4};
70 rb.Push(res);
71 rb.Push(rtc_value);
72}
73
74void SteadyClock::Handle_IsRtcResetDetected(HLERequestContext& ctx) {
75 LOG_DEBUG(Service_Time, "called.");
76
77 bool reset_detected{false};
78 auto res = IsRtcResetDetected(reset_detected);
79
80 IPC::ResponseBuilder rb{ctx, 3};
81 rb.Push(res);
82 rb.Push(reset_detected);
83}
84
85void SteadyClock::Handle_GetSetupResultValue(HLERequestContext& ctx) {
86 LOG_DEBUG(Service_Time, "called.");
87
88 Result result_value{ResultSuccess};
89 auto res = GetSetupResultValue(result_value);
90
91 IPC::ResponseBuilder rb{ctx, 3};
92 rb.Push(res);
93 rb.Push(result_value);
94}
95
96void SteadyClock::Handle_GetInternalOffset(HLERequestContext& ctx) {
97 LOG_DEBUG(Service_Time, "called.");
98
99 s64 internal_offset{};
100 auto res = GetInternalOffset(internal_offset);
101
102 IPC::ResponseBuilder rb{ctx, 4};
103 rb.Push(res);
104 rb.Push(internal_offset);
105}
106
107// =============================== Implementations ===========================
108
109Result SteadyClock::GetCurrentTimePoint(SteadyClockTimePoint& out_time_point) {
110 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
111 ResultClockUninitialized);
112
113 R_RETURN(m_clock_core.GetCurrentTimePoint(out_time_point));
114}
115
116Result SteadyClock::GetTestOffset(s64& out_test_offset) {
117 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
118 ResultClockUninitialized);
119
120 out_test_offset = m_clock_core.GetTestOffset();
121 R_SUCCEED();
122}
123
124Result SteadyClock::SetTestOffset(s64 test_offset) {
125 R_UNLESS(m_can_write_steady_clock, ResultPermissionDenied);
126 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
127 ResultClockUninitialized);
128
129 m_clock_core.SetTestOffset(test_offset);
130 R_SUCCEED();
131}
132
133Result SteadyClock::GetRtcValue(s64& out_rtc_value) {
134 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
135 ResultClockUninitialized);
136
137 R_RETURN(m_clock_core.GetRtcValue(out_rtc_value));
138}
139
140Result SteadyClock::IsRtcResetDetected(bool& out_is_detected) {
141 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
142 ResultClockUninitialized);
143
144 out_is_detected = m_clock_core.IsResetDetected();
145 R_SUCCEED();
146}
147
148Result SteadyClock::GetSetupResultValue(Result& out_result) {
149 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
150 ResultClockUninitialized);
151
152 out_result = m_clock_core.GetSetupResultValue();
153 R_SUCCEED();
154}
155
156Result SteadyClock::GetInternalOffset(s64& out_internal_offset) {
157 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
158 ResultClockUninitialized);
159
160 out_internal_offset = m_clock_core.GetInternalOffset();
161 R_SUCCEED();
162}
163
164} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/steady_clock.h b/src/core/hle/service/psc/time/steady_clock.h
new file mode 100644
index 000000000..115e9b138
--- /dev/null
+++ b/src/core/hle/service/psc/time/steady_clock.h
@@ -0,0 +1,49 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/ipc_helpers.h"
7#include "core/hle/service/psc/time/common.h"
8#include "core/hle/service/psc/time/manager.h"
9#include "core/hle/service/server_manager.h"
10#include "core/hle/service/service.h"
11
12namespace Core {
13class System;
14}
15
16namespace Service::PSC::Time {
17
18class SteadyClock final : public ServiceFramework<SteadyClock> {
19public:
20 explicit SteadyClock(Core::System& system, std::shared_ptr<TimeManager> manager,
21 bool can_write_steady_clock, bool can_write_uninitialized_clock);
22
23 ~SteadyClock() override = default;
24
25 Result GetCurrentTimePoint(SteadyClockTimePoint& out_time_point);
26 Result GetTestOffset(s64& out_test_offset);
27 Result SetTestOffset(s64 test_offset);
28 Result GetRtcValue(s64& out_rtc_value);
29 Result IsRtcResetDetected(bool& out_is_detected);
30 Result GetSetupResultValue(Result& out_result);
31 Result GetInternalOffset(s64& out_internal_offset);
32
33private:
34 void Handle_GetCurrentTimePoint(HLERequestContext& ctx);
35 void Handle_GetTestOffset(HLERequestContext& ctx);
36 void Handle_SetTestOffset(HLERequestContext& ctx);
37 void Handle_GetRtcValue(HLERequestContext& ctx);
38 void Handle_IsRtcResetDetected(HLERequestContext& ctx);
39 void Handle_GetSetupResultValue(HLERequestContext& ctx);
40 void Handle_GetInternalOffset(HLERequestContext& ctx);
41
42 Core::System& m_system;
43
44 StandardSteadyClockCore& m_clock_core;
45 bool m_can_write_steady_clock;
46 bool m_can_write_uninitialized_clock;
47};
48
49} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/system_clock.cpp b/src/core/hle/service/psc/time/system_clock.cpp
new file mode 100644
index 000000000..13d2f1d11
--- /dev/null
+++ b/src/core/hle/service/psc/time/system_clock.cpp
@@ -0,0 +1,127 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/service/psc/time/system_clock.h"
6
7namespace Service::PSC::Time {
8
9SystemClock::SystemClock(Core::System& system_, SystemClockCore& clock_core, bool can_write_clock,
10 bool can_write_uninitialized_clock)
11 : ServiceFramework{system_, "ISystemClock"}, m_system{system}, m_clock_core{clock_core},
12 m_can_write_clock{can_write_clock}, m_can_write_uninitialized_clock{
13 can_write_uninitialized_clock} {
14 // clang-format off
15 static const FunctionInfo functions[] = {
16 {0, &SystemClock::Handle_GetCurrentTime, "GetCurrentTime"},
17 {1, &SystemClock::Handle_SetCurrentTime, "SetCurrentTime"},
18 {2, &SystemClock::Handle_GetSystemClockContext, "GetSystemClockContext"},
19 {3, &SystemClock::Handle_SetSystemClockContext, "SetSystemClockContext"},
20 {4, &SystemClock::Handle_GetOperationEventReadableHandle, "GetOperationEventReadableHandle"},
21 };
22 // clang-format on
23 RegisterHandlers(functions);
24}
25
26void SystemClock::Handle_GetCurrentTime(HLERequestContext& ctx) {
27 LOG_DEBUG(Service_Time, "called.");
28
29 s64 time{};
30 auto res = GetCurrentTime(time);
31
32 IPC::ResponseBuilder rb{ctx, 4};
33 rb.Push(res);
34 rb.Push<s64>(time);
35}
36
37void SystemClock::Handle_SetCurrentTime(HLERequestContext& ctx) {
38 LOG_DEBUG(Service_Time, "called.");
39
40 IPC::RequestParser rp{ctx};
41 auto time{rp.Pop<s64>()};
42
43 auto res = SetCurrentTime(time);
44
45 IPC::ResponseBuilder rb{ctx, 2};
46 rb.Push(res);
47}
48
49void SystemClock::Handle_GetSystemClockContext(HLERequestContext& ctx) {
50 LOG_DEBUG(Service_Time, "called.");
51
52 SystemClockContext context{};
53 auto res = GetSystemClockContext(context);
54
55 IPC::ResponseBuilder rb{ctx, 2 + sizeof(SystemClockContext) / sizeof(u32)};
56 rb.Push(res);
57 rb.PushRaw<SystemClockContext>(context);
58}
59
60void SystemClock::Handle_SetSystemClockContext(HLERequestContext& ctx) {
61 LOG_DEBUG(Service_Time, "called.");
62
63 IPC::RequestParser rp{ctx};
64 auto context{rp.PopRaw<SystemClockContext>()};
65
66 auto res = SetSystemClockContext(context);
67
68 IPC::ResponseBuilder rb{ctx, 2};
69 rb.Push(res);
70}
71
72void SystemClock::Handle_GetOperationEventReadableHandle(HLERequestContext& ctx) {
73 LOG_DEBUG(Service_Time, "called.");
74
75 Kernel::KEvent* event{};
76 auto res = GetOperationEventReadableHandle(&event);
77
78 IPC::ResponseBuilder rb{ctx, 2, 1};
79 rb.Push(res);
80 rb.PushCopyObjects(event->GetReadableEvent());
81}
82
83// =============================== Implementations ===========================
84
85Result SystemClock::GetCurrentTime(s64& out_time) {
86 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
87 ResultClockUninitialized);
88
89 R_RETURN(m_clock_core.GetCurrentTime(&out_time));
90}
91
92Result SystemClock::SetCurrentTime(s64 time) {
93 R_UNLESS(m_can_write_clock, ResultPermissionDenied);
94 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
95 ResultClockUninitialized);
96
97 R_RETURN(m_clock_core.SetCurrentTime(time));
98}
99
100Result SystemClock::GetSystemClockContext(SystemClockContext& out_context) {
101 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
102 ResultClockUninitialized);
103
104 R_RETURN(m_clock_core.GetContext(out_context));
105}
106
107Result SystemClock::SetSystemClockContext(SystemClockContext& context) {
108 R_UNLESS(m_can_write_clock, ResultPermissionDenied);
109 R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
110 ResultClockUninitialized);
111
112 R_RETURN(m_clock_core.SetContextAndWrite(context));
113}
114
115Result SystemClock::GetOperationEventReadableHandle(Kernel::KEvent** out_event) {
116 if (!m_operation_event) {
117 m_operation_event = std::make_unique<OperationEvent>(m_system);
118 R_UNLESS(m_operation_event != nullptr, ResultFailed);
119
120 m_clock_core.LinkOperationEvent(*m_operation_event);
121 }
122
123 *out_event = m_operation_event->m_event;
124 R_SUCCEED();
125}
126
127} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/system_clock.h b/src/core/hle/service/psc/time/system_clock.h
new file mode 100644
index 000000000..f30027e7b
--- /dev/null
+++ b/src/core/hle/service/psc/time/system_clock.h
@@ -0,0 +1,46 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/ipc_helpers.h"
7#include "core/hle/service/psc/time/common.h"
8#include "core/hle/service/psc/time/manager.h"
9#include "core/hle/service/server_manager.h"
10#include "core/hle/service/service.h"
11
12namespace Core {
13class System;
14}
15
16namespace Service::PSC::Time {
17
18class SystemClock final : public ServiceFramework<SystemClock> {
19public:
20 explicit SystemClock(Core::System& system, SystemClockCore& system_clock_core,
21 bool can_write_clock, bool can_write_uninitialized_clock);
22
23 ~SystemClock() override = default;
24
25 Result GetCurrentTime(s64& out_time);
26 Result SetCurrentTime(s64 time);
27 Result GetSystemClockContext(SystemClockContext& out_context);
28 Result SetSystemClockContext(SystemClockContext& context);
29 Result GetOperationEventReadableHandle(Kernel::KEvent** out_event);
30
31private:
32 void Handle_GetCurrentTime(HLERequestContext& ctx);
33 void Handle_SetCurrentTime(HLERequestContext& ctx);
34 void Handle_GetSystemClockContext(HLERequestContext& ctx);
35 void Handle_SetSystemClockContext(HLERequestContext& ctx);
36 void Handle_GetOperationEventReadableHandle(HLERequestContext& ctx);
37
38 Core::System& m_system;
39
40 SystemClockCore& m_clock_core;
41 bool m_can_write_clock;
42 bool m_can_write_uninitialized_clock;
43 std::unique_ptr<OperationEvent> m_operation_event{};
44};
45
46} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/time_zone.cpp b/src/core/hle/service/psc/time/time_zone.cpp
new file mode 100644
index 000000000..cfee8f866
--- /dev/null
+++ b/src/core/hle/service/psc/time/time_zone.cpp
@@ -0,0 +1,280 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/psc/time/time_zone.h"
5
6namespace Service::PSC::Time {
7namespace {
8constexpr Result ValidateRule(Tz::Rule& rule) {
9 if (rule.typecnt > static_cast<s32>(Tz::TZ_MAX_TYPES) ||
10 rule.timecnt > static_cast<s32>(Tz::TZ_MAX_TIMES) ||
11 rule.charcnt > static_cast<s32>(Tz::TZ_MAX_CHARS)) {
12 R_RETURN(ResultTimeZoneOutOfRange);
13 }
14
15 for (s32 i = 0; i < rule.timecnt; i++) {
16 if (rule.types[i] >= rule.typecnt) {
17 R_RETURN(ResultTimeZoneOutOfRange);
18 }
19 }
20
21 for (s32 i = 0; i < rule.typecnt; i++) {
22 if (rule.ttis[i].tt_desigidx >= static_cast<s32>(rule.chars.size())) {
23 R_RETURN(ResultTimeZoneOutOfRange);
24 }
25 }
26 R_SUCCEED();
27}
28
29constexpr bool GetTimeZoneTime(s64& out_time, Tz::Rule& rule, s64 time, s32 index,
30 s32 index_offset) {
31 s32 found_idx{};
32 s32 expected_index{index + index_offset};
33 s64 time_to_find{time + rule.ttis[rule.types[index]].tt_utoff -
34 rule.ttis[rule.types[expected_index]].tt_utoff};
35
36 if (rule.timecnt > 1 && rule.ats[0] <= time_to_find) {
37 s32 low{1};
38 s32 high{rule.timecnt};
39
40 while (low < high) {
41 auto mid{(low + high) / 2};
42 if (rule.ats[mid] <= time_to_find) {
43 low = mid + 1;
44 } else if (rule.ats[mid] > time_to_find) {
45 high = mid;
46 }
47 }
48 found_idx = low - 1;
49 }
50
51 if (found_idx == expected_index) {
52 out_time = time_to_find;
53 }
54 return found_idx == expected_index;
55}
56} // namespace
57
58void TimeZone::SetTimePoint(SteadyClockTimePoint& time_point) {
59 std::scoped_lock l{m_mutex};
60 m_steady_clock_time_point = time_point;
61}
62
63void TimeZone::SetTotalLocationNameCount(u32 count) {
64 std::scoped_lock l{m_mutex};
65 m_total_location_name_count = count;
66}
67
68void TimeZone::SetRuleVersion(RuleVersion& rule_version) {
69 std::scoped_lock l{m_mutex};
70 m_rule_version = rule_version;
71}
72
73Result TimeZone::GetLocationName(LocationName& out_name) {
74 std::scoped_lock l{m_mutex};
75 R_UNLESS(m_initialized, ResultClockUninitialized);
76 out_name = m_location;
77 R_SUCCEED();
78}
79
80Result TimeZone::GetTotalLocationCount(u32& out_count) {
81 std::scoped_lock l{m_mutex};
82 if (!m_initialized) {
83 return ResultClockUninitialized;
84 }
85
86 out_count = m_total_location_name_count;
87 R_SUCCEED();
88}
89
90Result TimeZone::GetRuleVersion(RuleVersion& out_rule_version) {
91 std::scoped_lock l{m_mutex};
92 if (!m_initialized) {
93 return ResultClockUninitialized;
94 }
95 out_rule_version = m_rule_version;
96 R_SUCCEED();
97}
98
99Result TimeZone::GetTimePoint(SteadyClockTimePoint& out_time_point) {
100 std::scoped_lock l{m_mutex};
101 if (!m_initialized) {
102 return ResultClockUninitialized;
103 }
104 out_time_point = m_steady_clock_time_point;
105 R_SUCCEED();
106}
107
108Result TimeZone::ToCalendarTime(CalendarTime& out_calendar_time,
109 CalendarAdditionalInfo& out_additional_info, s64 time,
110 Tz::Rule& rule) {
111 std::scoped_lock l{m_mutex};
112 R_RETURN(ToCalendarTimeImpl(out_calendar_time, out_additional_info, time, rule));
113}
114
115Result TimeZone::ToCalendarTimeWithMyRule(CalendarTime& calendar_time,
116 CalendarAdditionalInfo& calendar_additional, s64 time) {
117 // This is checked outside the mutex. Bug?
118 if (!m_initialized) {
119 return ResultClockUninitialized;
120 }
121
122 std::scoped_lock l{m_mutex};
123 R_RETURN(ToCalendarTimeImpl(calendar_time, calendar_additional, time, m_my_rule));
124}
125
126Result TimeZone::ParseBinary(LocationName& name, std::span<const u8> binary) {
127 std::scoped_lock l{m_mutex};
128
129 Tz::Rule tmp_rule{};
130 R_TRY(ParseBinaryImpl(tmp_rule, binary));
131
132 m_my_rule = tmp_rule;
133 m_location = name;
134
135 R_SUCCEED();
136}
137
138Result TimeZone::ParseBinaryInto(Tz::Rule& out_rule, std::span<const u8> binary) {
139 std::scoped_lock l{m_mutex};
140 R_RETURN(ParseBinaryImpl(out_rule, binary));
141}
142
143Result TimeZone::ToPosixTime(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
144 CalendarTime& calendar, Tz::Rule& rule) {
145 std::scoped_lock l{m_mutex};
146
147 auto res = ToPosixTimeImpl(out_count, out_times, out_times_count, calendar, rule, -1);
148
149 if (res != ResultSuccess) {
150 if (res == ResultTimeZoneNotFound) {
151 res = ResultSuccess;
152 out_count = 0;
153 }
154 } else if (out_count == 2 && out_times[0] > out_times[1]) {
155 std::swap(out_times[0], out_times[1]);
156 }
157 R_RETURN(res);
158}
159
160Result TimeZone::ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times,
161 u32 out_times_count, CalendarTime& calendar) {
162 std::scoped_lock l{m_mutex};
163
164 auto res = ToPosixTimeImpl(out_count, out_times, out_times_count, calendar, m_my_rule, -1);
165
166 if (res != ResultSuccess) {
167 if (res == ResultTimeZoneNotFound) {
168 res = ResultSuccess;
169 out_count = 0;
170 }
171 } else if (out_count == 2 && out_times[0] > out_times[1]) {
172 std::swap(out_times[0], out_times[1]);
173 }
174 R_RETURN(res);
175}
176
177Result TimeZone::ParseBinaryImpl(Tz::Rule& out_rule, std::span<const u8> binary) {
178 if (Tz::ParseTimeZoneBinary(out_rule, binary)) {
179 R_RETURN(ResultTimeZoneParseFailed);
180 }
181 R_SUCCEED();
182}
183
184Result TimeZone::ToCalendarTimeImpl(CalendarTime& out_calendar_time,
185 CalendarAdditionalInfo& out_additional_info, s64 time,
186 Tz::Rule& rule) {
187 R_TRY(ValidateRule(rule));
188
189 Tz::CalendarTimeInternal calendar_internal{};
190 time_t time_tmp{static_cast<time_t>(time)};
191 if (Tz::localtime_rz(&calendar_internal, &rule, &time_tmp)) {
192 R_RETURN(ResultOverflow);
193 }
194
195 out_calendar_time.year = static_cast<s16>(calendar_internal.tm_year + 1900);
196 out_calendar_time.month = static_cast<s8>(calendar_internal.tm_mon + 1);
197 out_calendar_time.day = static_cast<s8>(calendar_internal.tm_mday);
198 out_calendar_time.hour = static_cast<s8>(calendar_internal.tm_hour);
199 out_calendar_time.minute = static_cast<s8>(calendar_internal.tm_min);
200 out_calendar_time.second = static_cast<s8>(calendar_internal.tm_sec);
201
202 out_additional_info.day_of_week = calendar_internal.tm_wday;
203 out_additional_info.day_of_year = calendar_internal.tm_yday;
204
205 std::memcpy(out_additional_info.name.data(), calendar_internal.tm_zone.data(),
206 out_additional_info.name.size());
207 out_additional_info.name[out_additional_info.name.size() - 1] = '\0';
208
209 out_additional_info.is_dst = calendar_internal.tm_isdst;
210 out_additional_info.ut_offset = calendar_internal.tm_utoff;
211
212 R_SUCCEED();
213}
214
215Result TimeZone::ToPosixTimeImpl(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
216 CalendarTime& calendar, Tz::Rule& rule, s32 is_dst) {
217 R_TRY(ValidateRule(rule));
218
219 calendar.month -= 1;
220 calendar.year -= 1900;
221
222 Tz::CalendarTimeInternal internal{
223 .tm_sec = calendar.second,
224 .tm_min = calendar.minute,
225 .tm_hour = calendar.hour,
226 .tm_mday = calendar.day,
227 .tm_mon = calendar.month,
228 .tm_year = calendar.year,
229 .tm_wday = 0,
230 .tm_yday = 0,
231 .tm_isdst = is_dst,
232 .tm_zone = {},
233 .tm_utoff = 0,
234 .time_index = 0,
235 };
236 time_t time_tmp{};
237 auto res = Tz::mktime_tzname(&time_tmp, &rule, &internal);
238 s64 time = static_cast<s64>(time_tmp);
239
240 if (res == 1) {
241 R_RETURN(ResultOverflow);
242 } else if (res == 2) {
243 R_RETURN(ResultTimeZoneNotFound);
244 }
245
246 if (internal.tm_sec != calendar.second || internal.tm_min != calendar.minute ||
247 internal.tm_hour != calendar.hour || internal.tm_mday != calendar.day ||
248 internal.tm_mon != calendar.month || internal.tm_year != calendar.year) {
249 R_RETURN(ResultTimeZoneNotFound);
250 }
251
252 if (res != 0) {
253 ASSERT(false);
254 }
255
256 out_times[0] = time;
257 if (out_times_count < 2) {
258 out_count = 1;
259 R_SUCCEED();
260 }
261
262 s64 time2{};
263 if (internal.time_index > 0 && GetTimeZoneTime(time2, rule, time, internal.time_index, -1)) {
264 out_times[1] = time2;
265 out_count = 2;
266 R_SUCCEED();
267 }
268
269 if (((internal.time_index + 1) < rule.timecnt) &&
270 GetTimeZoneTime(time2, rule, time, internal.time_index, 1)) {
271 out_times[1] = time2;
272 out_count = 2;
273 R_SUCCEED();
274 }
275
276 out_count = 1;
277 R_SUCCEED();
278}
279
280} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/time_zone.h b/src/core/hle/service/psc/time/time_zone.h
new file mode 100644
index 000000000..ce2acca17
--- /dev/null
+++ b/src/core/hle/service/psc/time/time_zone.h
@@ -0,0 +1,62 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <mutex>
7#include <span>
8
9#include <tz/tz.h>
10#include "core/hle/service/psc/time/common.h"
11
12namespace Service::PSC::Time {
13
14class TimeZone {
15public:
16 TimeZone() = default;
17
18 bool IsInitialized() const {
19 return m_initialized;
20 }
21
22 void SetInitialized() {
23 m_initialized = true;
24 }
25
26 void SetTimePoint(SteadyClockTimePoint& time_point);
27 void SetTotalLocationNameCount(u32 count);
28 void SetRuleVersion(RuleVersion& rule_version);
29 Result GetLocationName(LocationName& out_name);
30 Result GetTotalLocationCount(u32& out_count);
31 Result GetRuleVersion(RuleVersion& out_rule_version);
32 Result GetTimePoint(SteadyClockTimePoint& out_time_point);
33
34 Result ToCalendarTime(CalendarTime& out_calendar_time,
35 CalendarAdditionalInfo& out_additional_info, s64 time, Tz::Rule& rule);
36 Result ToCalendarTimeWithMyRule(CalendarTime& calendar_time,
37 CalendarAdditionalInfo& calendar_additional, s64 time);
38 Result ParseBinary(LocationName& name, std::span<const u8> binary);
39 Result ParseBinaryInto(Tz::Rule& out_rule, std::span<const u8> binary);
40 Result ToPosixTime(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
41 CalendarTime& calendar, Tz::Rule& rule);
42 Result ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
43 CalendarTime& calendar);
44
45private:
46 Result ParseBinaryImpl(Tz::Rule& out_rule, std::span<const u8> binary);
47 Result ToCalendarTimeImpl(CalendarTime& out_calendar_time,
48 CalendarAdditionalInfo& out_additional_info, s64 time,
49 Tz::Rule& rule);
50 Result ToPosixTimeImpl(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
51 CalendarTime& calendar, Tz::Rule& rule, s32 is_dst);
52
53 bool m_initialized{};
54 std::recursive_mutex m_mutex;
55 LocationName m_location{};
56 Tz::Rule m_my_rule{};
57 SteadyClockTimePoint m_steady_clock_time_point{};
58 u32 m_total_location_name_count{};
59 RuleVersion m_rule_version{};
60};
61
62} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/time_zone_service.cpp b/src/core/hle/service/psc/time/time_zone_service.cpp
new file mode 100644
index 000000000..e304c8387
--- /dev/null
+++ b/src/core/hle/service/psc/time/time_zone_service.cpp
@@ -0,0 +1,289 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <tz/tz.h>
5#include "core/core.h"
6#include "core/hle/service/psc/time/time_zone_service.h"
7
8namespace Service::PSC::Time {
9
10TimeZoneService::TimeZoneService(Core::System& system_, StandardSteadyClockCore& clock_core,
11 TimeZone& time_zone, bool can_write_timezone_device_location)
12 : ServiceFramework{system_, "ITimeZoneService"}, m_system{system}, m_clock_core{clock_core},
13 m_time_zone{time_zone}, m_can_write_timezone_device_location{
14 can_write_timezone_device_location} {
15 // clang-format off
16 static const FunctionInfo functions[] = {
17 {0, &TimeZoneService::Handle_GetDeviceLocationName, "GetDeviceLocationName"},
18 {1, &TimeZoneService::Handle_SetDeviceLocationName, "SetDeviceLocationName"},
19 {2, &TimeZoneService::Handle_GetTotalLocationNameCount, "GetTotalLocationNameCount"},
20 {3, &TimeZoneService::Handle_LoadLocationNameList, "LoadLocationNameList"},
21 {4, &TimeZoneService::Handle_LoadTimeZoneRule, "LoadTimeZoneRule"},
22 {5, &TimeZoneService::Handle_GetTimeZoneRuleVersion, "GetTimeZoneRuleVersion"},
23 {6, &TimeZoneService::Handle_GetDeviceLocationNameAndUpdatedTime, "GetDeviceLocationNameAndUpdatedTime"},
24 {7, &TimeZoneService::Handle_SetDeviceLocationNameWithTimeZoneRule, "SetDeviceLocationNameWithTimeZoneRule"},
25 {8, &TimeZoneService::Handle_ParseTimeZoneBinary, "ParseTimeZoneBinary"},
26 {20, &TimeZoneService::Handle_GetDeviceLocationNameOperationEventReadableHandle, "GetDeviceLocationNameOperationEventReadableHandle"},
27 {100, &TimeZoneService::Handle_ToCalendarTime, "ToCalendarTime"},
28 {101, &TimeZoneService::Handle_ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
29 {201, &TimeZoneService::Handle_ToPosixTime, "ToPosixTime"},
30 {202, &TimeZoneService::Handle_ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"},
31 };
32 // clang-format on
33 RegisterHandlers(functions);
34}
35
36void TimeZoneService::Handle_GetDeviceLocationName(HLERequestContext& ctx) {
37 LOG_DEBUG(Service_Time, "called.");
38
39 LocationName name{};
40 auto res = GetDeviceLocationName(name);
41
42 IPC::ResponseBuilder rb{ctx, 2 + sizeof(LocationName) / sizeof(u32)};
43 rb.Push(res);
44 rb.PushRaw<LocationName>(name);
45}
46
47void TimeZoneService::Handle_SetDeviceLocationName(HLERequestContext& ctx) {
48 LOG_DEBUG(Service_Time, "called.");
49
50 IPC::RequestParser rp{ctx};
51 [[maybe_unused]] auto name{rp.PopRaw<LocationName>()};
52
53 if (!m_can_write_timezone_device_location) {
54 IPC::ResponseBuilder rb{ctx, 2};
55 rb.Push(ResultPermissionDenied);
56 return;
57 }
58
59 IPC::ResponseBuilder rb{ctx, 2};
60 rb.Push(ResultNotImplemented);
61}
62
63void TimeZoneService::Handle_GetTotalLocationNameCount(HLERequestContext& ctx) {
64 LOG_DEBUG(Service_Time, "called.");
65
66 u32 count{};
67 auto res = GetTotalLocationNameCount(count);
68
69 IPC::ResponseBuilder rb{ctx, 3};
70 rb.Push(res);
71 rb.Push(count);
72}
73
74void TimeZoneService::Handle_LoadLocationNameList(HLERequestContext& ctx) {
75 LOG_DEBUG(Service_Time, "called.");
76
77 IPC::ResponseBuilder rb{ctx, 2};
78 rb.Push(ResultNotImplemented);
79}
80
81void TimeZoneService::Handle_LoadTimeZoneRule(HLERequestContext& ctx) {
82 LOG_DEBUG(Service_Time, "called.");
83
84 IPC::ResponseBuilder rb{ctx, 2};
85 rb.Push(ResultNotImplemented);
86}
87
88void TimeZoneService::Handle_GetTimeZoneRuleVersion(HLERequestContext& ctx) {
89 LOG_DEBUG(Service_Time, "called.");
90
91 RuleVersion rule_version{};
92 auto res = GetTimeZoneRuleVersion(rule_version);
93
94 IPC::ResponseBuilder rb{ctx, 2 + sizeof(RuleVersion) / sizeof(u32)};
95 rb.Push(res);
96 rb.PushRaw<RuleVersion>(rule_version);
97}
98
99void TimeZoneService::Handle_GetDeviceLocationNameAndUpdatedTime(HLERequestContext& ctx) {
100 LOG_DEBUG(Service_Time, "called.");
101
102 LocationName name{};
103 SteadyClockTimePoint time_point{};
104 auto res = GetDeviceLocationNameAndUpdatedTime(time_point, name);
105
106 IPC::ResponseBuilder rb{ctx, 2 + (sizeof(LocationName) / sizeof(u32)) +
107 (sizeof(SteadyClockTimePoint) / sizeof(u32))};
108 rb.Push(res);
109 rb.PushRaw<LocationName>(name);
110 rb.PushRaw<SteadyClockTimePoint>(time_point);
111}
112
113void TimeZoneService::Handle_SetDeviceLocationNameWithTimeZoneRule(HLERequestContext& ctx) {
114 LOG_DEBUG(Service_Time, "called.");
115
116 IPC::RequestParser rp{ctx};
117 auto name{rp.PopRaw<LocationName>()};
118
119 auto binary{ctx.ReadBuffer()};
120 auto res = SetDeviceLocationNameWithTimeZoneRule(name, binary);
121
122 IPC::ResponseBuilder rb{ctx, 2};
123 rb.Push(res);
124}
125
126void TimeZoneService::Handle_ParseTimeZoneBinary(HLERequestContext& ctx) {
127 LOG_DEBUG(Service_Time, "called.");
128
129 auto binary{ctx.ReadBuffer()};
130
131 Tz::Rule rule{};
132 auto res = ParseTimeZoneBinary(rule, binary);
133
134 ctx.WriteBuffer(rule);
135
136 IPC::ResponseBuilder rb{ctx, 2};
137 rb.Push(res);
138}
139
140void TimeZoneService::Handle_GetDeviceLocationNameOperationEventReadableHandle(
141 HLERequestContext& ctx) {
142 LOG_DEBUG(Service_Time, "called.");
143
144 IPC::ResponseBuilder rb{ctx, 2};
145 rb.Push(ResultNotImplemented);
146}
147
148void TimeZoneService::Handle_ToCalendarTime(HLERequestContext& ctx) {
149 LOG_DEBUG(Service_Time, "called.");
150
151 IPC::RequestParser rp{ctx};
152 auto time{rp.Pop<s64>()};
153
154 auto rule_buffer{ctx.ReadBuffer()};
155 Tz::Rule rule{};
156 std::memcpy(&rule, rule_buffer.data(), sizeof(Tz::Rule));
157
158 CalendarTime calendar_time{};
159 CalendarAdditionalInfo additional_info{};
160 auto res = ToCalendarTime(calendar_time, additional_info, time, rule);
161
162 IPC::ResponseBuilder rb{ctx, 2 + (sizeof(CalendarTime) / sizeof(u32)) +
163 (sizeof(CalendarAdditionalInfo) / sizeof(u32))};
164 rb.Push(res);
165 rb.PushRaw<CalendarTime>(calendar_time);
166 rb.PushRaw<CalendarAdditionalInfo>(additional_info);
167}
168
169void TimeZoneService::Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx) {
170 LOG_DEBUG(Service_Time, "called.");
171
172 IPC::RequestParser rp{ctx};
173 auto time{rp.Pop<s64>()};
174
175 CalendarTime calendar_time{};
176 CalendarAdditionalInfo additional_info{};
177 auto res = ToCalendarTimeWithMyRule(calendar_time, additional_info, time);
178
179 IPC::ResponseBuilder rb{ctx, 2 + (sizeof(CalendarTime) / sizeof(u32)) +
180 (sizeof(CalendarAdditionalInfo) / sizeof(u32))};
181 rb.Push(res);
182 rb.PushRaw<CalendarTime>(calendar_time);
183 rb.PushRaw<CalendarAdditionalInfo>(additional_info);
184}
185
186void TimeZoneService::Handle_ToPosixTime(HLERequestContext& ctx) {
187 LOG_DEBUG(Service_Time, "called.");
188
189 IPC::RequestParser rp{ctx};
190 auto calendar{rp.PopRaw<CalendarTime>()};
191
192 auto binary{ctx.ReadBuffer()};
193
194 Tz::Rule rule{};
195 std::memcpy(&rule, binary.data(), sizeof(Tz::Rule));
196
197 u32 count{};
198 std::array<s64, 2> times{};
199 u32 times_count{static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(s64))};
200
201 auto res = ToPosixTime(count, times, times_count, calendar, rule);
202
203 ctx.WriteBuffer(times);
204
205 IPC::ResponseBuilder rb{ctx, 3};
206 rb.Push(res);
207 rb.Push(count);
208}
209
210void TimeZoneService::Handle_ToPosixTimeWithMyRule(HLERequestContext& ctx) {
211 LOG_DEBUG(Service_Time, "called.");
212
213 IPC::RequestParser rp{ctx};
214 auto calendar{rp.PopRaw<CalendarTime>()};
215
216 u32 count{};
217 std::array<s64, 2> times{};
218 u32 times_count{static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(s64))};
219
220 auto res = ToPosixTimeWithMyRule(count, times, times_count, calendar);
221
222 ctx.WriteBuffer(times);
223
224 IPC::ResponseBuilder rb{ctx, 3};
225 rb.Push(res);
226 rb.Push(count);
227}
228
229// =============================== Implementations ===========================
230
231Result TimeZoneService::GetDeviceLocationName(LocationName& out_location_name) {
232 R_RETURN(m_time_zone.GetLocationName(out_location_name));
233}
234
235Result TimeZoneService::GetTotalLocationNameCount(u32& out_count) {
236 R_RETURN(m_time_zone.GetTotalLocationCount(out_count));
237}
238
239Result TimeZoneService::GetTimeZoneRuleVersion(RuleVersion& out_rule_version) {
240 R_RETURN(m_time_zone.GetRuleVersion(out_rule_version));
241}
242
243Result TimeZoneService::GetDeviceLocationNameAndUpdatedTime(SteadyClockTimePoint& out_time_point,
244 LocationName& location_name) {
245 R_TRY(m_time_zone.GetLocationName(location_name));
246 R_RETURN(m_time_zone.GetTimePoint(out_time_point));
247}
248
249Result TimeZoneService::SetDeviceLocationNameWithTimeZoneRule(LocationName& location_name,
250 std::span<const u8> binary) {
251 R_UNLESS(m_can_write_timezone_device_location, ResultPermissionDenied);
252 R_TRY(m_time_zone.ParseBinary(location_name, binary));
253
254 SteadyClockTimePoint time_point{};
255 R_TRY(m_clock_core.GetCurrentTimePoint(time_point));
256
257 m_time_zone.SetTimePoint(time_point);
258 R_SUCCEED();
259}
260
261Result TimeZoneService::ParseTimeZoneBinary(Tz::Rule& out_rule, std::span<const u8> binary) {
262 R_RETURN(m_time_zone.ParseBinaryInto(out_rule, binary));
263}
264
265Result TimeZoneService::ToCalendarTime(CalendarTime& out_calendar_time,
266 CalendarAdditionalInfo& out_additional_info, s64 time,
267 Tz::Rule& rule) {
268 R_RETURN(m_time_zone.ToCalendarTime(out_calendar_time, out_additional_info, time, rule));
269}
270
271Result TimeZoneService::ToCalendarTimeWithMyRule(CalendarTime& out_calendar_time,
272 CalendarAdditionalInfo& out_additional_info,
273 s64 time) {
274 R_RETURN(m_time_zone.ToCalendarTimeWithMyRule(out_calendar_time, out_additional_info, time));
275}
276
277Result TimeZoneService::ToPosixTime(u32& out_count, std::span<s64, 2> out_times,
278 u32 out_times_count, CalendarTime& calendar_time,
279 Tz::Rule& rule) {
280 R_RETURN(m_time_zone.ToPosixTime(out_count, out_times, out_times_count, calendar_time, rule));
281}
282
283Result TimeZoneService::ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times,
284 u32 out_times_count, CalendarTime& calendar_time) {
285 R_RETURN(
286 m_time_zone.ToPosixTimeWithMyRule(out_count, out_times, out_times_count, calendar_time));
287}
288
289} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/psc/time/time_zone_service.h b/src/core/hle/service/psc/time/time_zone_service.h
new file mode 100644
index 000000000..074c1d4ae
--- /dev/null
+++ b/src/core/hle/service/psc/time/time_zone_service.h
@@ -0,0 +1,69 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/ipc_helpers.h"
7#include "core/hle/service/psc/time/common.h"
8#include "core/hle/service/psc/time/manager.h"
9#include "core/hle/service/server_manager.h"
10#include "core/hle/service/service.h"
11
12namespace Core {
13class System;
14}
15
16namespace Tz {
17struct Rule;
18}
19
20namespace Service::PSC::Time {
21
22class TimeZoneService final : public ServiceFramework<TimeZoneService> {
23public:
24 explicit TimeZoneService(Core::System& system, StandardSteadyClockCore& clock_core,
25 TimeZone& time_zone, bool can_write_timezone_device_location);
26
27 ~TimeZoneService() override = default;
28
29 Result GetDeviceLocationName(LocationName& out_location_name);
30 Result GetTotalLocationNameCount(u32& out_count);
31 Result GetTimeZoneRuleVersion(RuleVersion& out_rule_version);
32 Result GetDeviceLocationNameAndUpdatedTime(SteadyClockTimePoint& out_time_point,
33 LocationName& location_name);
34 Result SetDeviceLocationNameWithTimeZoneRule(LocationName& location_name,
35 std::span<const u8> binary);
36 Result ParseTimeZoneBinary(Tz::Rule& out_rule, std::span<const u8> binary);
37 Result ToCalendarTime(CalendarTime& out_calendar_time,
38 CalendarAdditionalInfo& out_additional_info, s64 time, Tz::Rule& rule);
39 Result ToCalendarTimeWithMyRule(CalendarTime& out_calendar_time,
40 CalendarAdditionalInfo& out_additional_info, s64 time);
41 Result ToPosixTime(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
42 CalendarTime& calendar_time, Tz::Rule& rule);
43 Result ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count,
44 CalendarTime& calendar_time);
45
46private:
47 void Handle_GetDeviceLocationName(HLERequestContext& ctx);
48 void Handle_SetDeviceLocationName(HLERequestContext& ctx);
49 void Handle_GetTotalLocationNameCount(HLERequestContext& ctx);
50 void Handle_LoadLocationNameList(HLERequestContext& ctx);
51 void Handle_LoadTimeZoneRule(HLERequestContext& ctx);
52 void Handle_GetTimeZoneRuleVersion(HLERequestContext& ctx);
53 void Handle_GetDeviceLocationNameAndUpdatedTime(HLERequestContext& ctx);
54 void Handle_SetDeviceLocationNameWithTimeZoneRule(HLERequestContext& ctx);
55 void Handle_ParseTimeZoneBinary(HLERequestContext& ctx);
56 void Handle_GetDeviceLocationNameOperationEventReadableHandle(HLERequestContext& ctx);
57 void Handle_ToCalendarTime(HLERequestContext& ctx);
58 void Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx);
59 void Handle_ToPosixTime(HLERequestContext& ctx);
60 void Handle_ToPosixTimeWithMyRule(HLERequestContext& ctx);
61
62 Core::System& m_system;
63
64 StandardSteadyClockCore& m_clock_core;
65 TimeZone& m_time_zone;
66 bool m_can_write_timezone_device_location;
67};
68
69} // namespace Service::PSC::Time
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 39124c5fd..06cbad268 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -66,7 +66,6 @@
66#include "core/hle/service/sockets/sockets.h" 66#include "core/hle/service/sockets/sockets.h"
67#include "core/hle/service/spl/spl_module.h" 67#include "core/hle/service/spl/spl_module.h"
68#include "core/hle/service/ssl/ssl.h" 68#include "core/hle/service/ssl/ssl.h"
69#include "core/hle/service/time/time.h"
70#include "core/hle/service/usb/usb.h" 69#include "core/hle/service/usb/usb.h"
71#include "core/hle/service/vi/vi.h" 70#include "core/hle/service/vi/vi.h"
72#include "core/reporter.h" 71#include "core/reporter.h"
@@ -246,6 +245,9 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
246 kernel.RunOnGuestCoreProcess("fatal", [&] { Fatal::LoopProcess(system); }); 245 kernel.RunOnGuestCoreProcess("fatal", [&] { Fatal::LoopProcess(system); });
247 kernel.RunOnGuestCoreProcess("fgm", [&] { FGM::LoopProcess(system); }); 246 kernel.RunOnGuestCoreProcess("fgm", [&] { FGM::LoopProcess(system); });
248 kernel.RunOnGuestCoreProcess("friends", [&] { Friend::LoopProcess(system); }); 247 kernel.RunOnGuestCoreProcess("friends", [&] { Friend::LoopProcess(system); });
248 // glue depends on settings and psc, so they must come first
249 kernel.RunOnGuestCoreProcess("settings", [&] { Set::LoopProcess(system); });
250 kernel.RunOnGuestCoreProcess("psc", [&] { PSC::LoopProcess(system); });
249 kernel.RunOnGuestCoreProcess("glue", [&] { Glue::LoopProcess(system); }); 251 kernel.RunOnGuestCoreProcess("glue", [&] { Glue::LoopProcess(system); });
250 kernel.RunOnGuestCoreProcess("grc", [&] { GRC::LoopProcess(system); }); 252 kernel.RunOnGuestCoreProcess("grc", [&] { GRC::LoopProcess(system); });
251 kernel.RunOnGuestCoreProcess("hid", [&] { HID::LoopProcess(system); }); 253 kernel.RunOnGuestCoreProcess("hid", [&] { HID::LoopProcess(system); });
@@ -269,13 +271,10 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
269 kernel.RunOnGuestCoreProcess("pcv", [&] { PCV::LoopProcess(system); }); 271 kernel.RunOnGuestCoreProcess("pcv", [&] { PCV::LoopProcess(system); });
270 kernel.RunOnGuestCoreProcess("prepo", [&] { PlayReport::LoopProcess(system); }); 272 kernel.RunOnGuestCoreProcess("prepo", [&] { PlayReport::LoopProcess(system); });
271 kernel.RunOnGuestCoreProcess("ProcessManager", [&] { PM::LoopProcess(system); }); 273 kernel.RunOnGuestCoreProcess("ProcessManager", [&] { PM::LoopProcess(system); });
272 kernel.RunOnGuestCoreProcess("psc", [&] { PSC::LoopProcess(system); });
273 kernel.RunOnGuestCoreProcess("ptm", [&] { PTM::LoopProcess(system); }); 274 kernel.RunOnGuestCoreProcess("ptm", [&] { PTM::LoopProcess(system); });
274 kernel.RunOnGuestCoreProcess("ro", [&] { RO::LoopProcess(system); }); 275 kernel.RunOnGuestCoreProcess("ro", [&] { RO::LoopProcess(system); });
275 kernel.RunOnGuestCoreProcess("settings", [&] { Set::LoopProcess(system); });
276 kernel.RunOnGuestCoreProcess("spl", [&] { SPL::LoopProcess(system); }); 276 kernel.RunOnGuestCoreProcess("spl", [&] { SPL::LoopProcess(system); });
277 kernel.RunOnGuestCoreProcess("ssl", [&] { SSL::LoopProcess(system); }); 277 kernel.RunOnGuestCoreProcess("ssl", [&] { SSL::LoopProcess(system); });
278 kernel.RunOnGuestCoreProcess("time", [&] { Time::LoopProcess(system); });
279 kernel.RunOnGuestCoreProcess("usb", [&] { USB::LoopProcess(system); }); 278 kernel.RunOnGuestCoreProcess("usb", [&] { USB::LoopProcess(system); });
280 // clang-format on 279 // clang-format on
281} 280}
diff --git a/src/core/hle/service/set/private_settings.h b/src/core/hle/service/set/private_settings.h
new file mode 100644
index 000000000..b02291ce7
--- /dev/null
+++ b/src/core/hle/service/set/private_settings.h
@@ -0,0 +1,72 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7
8#include "common/bit_field.h"
9#include "common/common_funcs.h"
10#include "common/common_types.h"
11#include "common/uuid.h"
12#include "core/hle/service/psc/time/common.h"
13
14namespace Service::Set {
15
16/// This is nn::settings::system::InitialLaunchFlag
17struct InitialLaunchFlag {
18 union {
19 u32 raw{};
20
21 BitField<0, 1, u32> InitialLaunchCompletionFlag;
22 BitField<8, 1, u32> InitialLaunchUserAdditionFlag;
23 BitField<16, 1, u32> InitialLaunchTimestampFlag;
24 };
25};
26static_assert(sizeof(InitialLaunchFlag) == 4, "InitialLaunchFlag is an invalid size");
27
28/// This is nn::settings::system::InitialLaunchSettings
29struct InitialLaunchSettings {
30 InitialLaunchFlag flags;
31 INSERT_PADDING_BYTES(0x4);
32 Service::PSC::Time::SteadyClockTimePoint timestamp;
33};
34static_assert(sizeof(InitialLaunchSettings) == 0x20, "InitialLaunchSettings is incorrect size");
35
36#pragma pack(push, 4)
37struct InitialLaunchSettingsPacked {
38 InitialLaunchFlag flags;
39 Service::PSC::Time::SteadyClockTimePoint timestamp;
40};
41#pragma pack(pop)
42static_assert(sizeof(InitialLaunchSettingsPacked) == 0x1C,
43 "InitialLaunchSettingsPacked is incorrect size");
44
45struct PrivateSettings {
46 std::array<u8, 0x10> reserved_00;
47
48 // nn::settings::system::InitialLaunchSettings
49 InitialLaunchSettings initial_launch_settings;
50
51 std::array<u8, 0x20> reserved_30;
52
53 Common::UUID external_clock_source_id;
54 s64 shutdown_rtc_value;
55 s64 external_steady_clock_internal_offset;
56
57 std::array<u8, 0x60> reserved_70;
58
59 // nn::settings::system::PlatformRegion
60 std::array<u8, 0x4> platform_region;
61
62 std::array<u8, 0x4> reserved_D4;
63};
64static_assert(offsetof(PrivateSettings, initial_launch_settings) == 0x10);
65static_assert(offsetof(PrivateSettings, external_clock_source_id) == 0x50);
66static_assert(offsetof(PrivateSettings, reserved_70) == 0x70);
67static_assert(offsetof(PrivateSettings, platform_region) == 0xD0);
68static_assert(sizeof(PrivateSettings) == 0xD8, "PrivateSettings has the wrong size!");
69
70PrivateSettings DefaultPrivateSettings();
71
72} // namespace Service::Set
diff --git a/src/core/hle/service/set/setting_formats/private_settings.h b/src/core/hle/service/set/setting_formats/private_settings.h
index 6c40f62e1..6579e95e0 100644
--- a/src/core/hle/service/set/setting_formats/private_settings.h
+++ b/src/core/hle/service/set/setting_formats/private_settings.h
@@ -8,7 +8,6 @@
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/uuid.h" 9#include "common/uuid.h"
10#include "core/hle/service/set/settings_types.h" 10#include "core/hle/service/set/settings_types.h"
11#include "core/hle/service/time/clock_types.h"
12 11
13namespace Service::Set { 12namespace Service::Set {
14 13
diff --git a/src/core/hle/service/set/setting_formats/system_settings.cpp b/src/core/hle/service/set/setting_formats/system_settings.cpp
index 66e57651e..ec00b90a6 100644
--- a/src/core/hle/service/set/setting_formats/system_settings.cpp
+++ b/src/core/hle/service/set/setting_formats/system_settings.cpp
@@ -45,7 +45,7 @@ SystemSettings DefaultSystemSettings() {
45 }; 45 };
46 46
47 settings.device_time_zone_location_name = {"UTC"}; 47 settings.device_time_zone_location_name = {"UTC"};
48 settings.user_system_clock_automatic_correction_enabled = false; 48 settings.user_system_clock_automatic_correction_enabled = true;
49 49
50 settings.primary_album_storage = PrimaryAlbumStorage::SdCard; 50 settings.primary_album_storage = PrimaryAlbumStorage::SdCard;
51 settings.battery_percentage_flag = true; 51 settings.battery_percentage_flag = true;
diff --git a/src/core/hle/service/set/setting_formats/system_settings.h b/src/core/hle/service/set/setting_formats/system_settings.h
index 14654f8b1..af5929fa9 100644
--- a/src/core/hle/service/set/setting_formats/system_settings.h
+++ b/src/core/hle/service/set/setting_formats/system_settings.h
@@ -12,7 +12,6 @@
12#include "common/vector_math.h" 12#include "common/vector_math.h"
13#include "core/hle/service/set/setting_formats/private_settings.h" 13#include "core/hle/service/set/setting_formats/private_settings.h"
14#include "core/hle/service/set/settings_types.h" 14#include "core/hle/service/set/settings_types.h"
15#include "core/hle/service/time/clock_types.h"
16 15
17namespace Service::Set { 16namespace Service::Set {
18 17
@@ -197,12 +196,14 @@ struct SystemSettings {
197 std::array<u8, 0x2C> backlight_settings_mixed_up; 196 std::array<u8, 0x2C> backlight_settings_mixed_up;
198 INSERT_PADDING_BYTES(0x64); // Reserved 197 INSERT_PADDING_BYTES(0x64); // Reserved
199 198
200 Service::Time::Clock::SystemClockContext user_system_clock_context; 199 // nn::time::SystemClockContext
201 Service::Time::Clock::SystemClockContext network_system_clock_context; 200 Service::PSC::Time::SystemClockContext user_system_clock_context;
201 Service::PSC::Time::SystemClockContext network_system_clock_context;
202 bool user_system_clock_automatic_correction_enabled; 202 bool user_system_clock_automatic_correction_enabled;
203 INSERT_PADDING_BYTES(0x3); 203 INSERT_PADDING_BYTES(0x3);
204 INSERT_PADDING_BYTES(0x4); // Reserved 204 INSERT_PADDING_BYTES(0x4); // Reserved
205 Service::Time::Clock::SteadyClockTimePoint 205 // nn::time::SteadyClockTimePoint
206 Service::PSC::Time::SteadyClockTimePoint
206 user_system_clock_automatic_correction_updated_time_point; 207 user_system_clock_automatic_correction_updated_time_point;
207 INSERT_PADDING_BYTES(0x10); // Reserved 208 INSERT_PADDING_BYTES(0x10); // Reserved
208 209
@@ -280,9 +281,12 @@ struct SystemSettings {
280 bool requires_run_repair_time_reviser; 281 bool requires_run_repair_time_reviser;
281 INSERT_PADDING_BYTES(0x6B); // Reserved 282 INSERT_PADDING_BYTES(0x6B); // Reserved
282 283
283 Service::Time::TimeZone::LocationName device_time_zone_location_name; 284 // nn::time::LocationName
285 Service::PSC::Time::LocationName device_time_zone_location_name;
284 INSERT_PADDING_BYTES(0x4); // Reserved 286 INSERT_PADDING_BYTES(0x4); // Reserved
285 Service::Time::Clock::SteadyClockTimePoint device_time_zone_location_updated_time; 287 // nn::time::SteadyClockTimePoint
288 Service::PSC::Time::SteadyClockTimePoint device_time_zone_location_updated_time;
289
286 INSERT_PADDING_BYTES(0xC0); // Reserved 290 INSERT_PADDING_BYTES(0xC0); // Reserved
287 291
288 // nn::settings::system::PrimaryAlbumStorage 292 // nn::settings::system::PrimaryAlbumStorage
diff --git a/src/core/hle/service/set/settings_types.h b/src/core/hle/service/set/settings_types.h
index 4dee202d7..f6f227fde 100644
--- a/src/core/hle/service/set/settings_types.h
+++ b/src/core/hle/service/set/settings_types.h
@@ -9,7 +9,7 @@
9#include "common/common_funcs.h" 9#include "common/common_funcs.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/uuid.h" 11#include "common/uuid.h"
12#include "core/hle/service/time/clock_types.h" 12#include "core/hle/service/psc/time/common.h"
13 13
14namespace Service::Set { 14namespace Service::Set {
15 15
@@ -365,7 +365,7 @@ struct EulaVersion {
365 EulaVersionClockType clock_type; 365 EulaVersionClockType clock_type;
366 INSERT_PADDING_BYTES(0x4); 366 INSERT_PADDING_BYTES(0x4);
367 s64 posix_time; 367 s64 posix_time;
368 Time::Clock::SteadyClockTimePoint timestamp; 368 Service::PSC::Time::SteadyClockTimePoint timestamp;
369}; 369};
370static_assert(sizeof(EulaVersion) == 0x30, "EulaVersion is incorrect size"); 370static_assert(sizeof(EulaVersion) == 0x30, "EulaVersion is incorrect size");
371 371
@@ -398,14 +398,14 @@ static_assert(sizeof(HomeMenuScheme) == 0x14, "HomeMenuScheme is incorrect size"
398struct InitialLaunchSettings { 398struct InitialLaunchSettings {
399 InitialLaunchFlag flags; 399 InitialLaunchFlag flags;
400 INSERT_PADDING_BYTES(0x4); 400 INSERT_PADDING_BYTES(0x4);
401 Service::Time::Clock::SteadyClockTimePoint timestamp; 401 Service::PSC::Time::SteadyClockTimePoint timestamp;
402}; 402};
403static_assert(sizeof(InitialLaunchSettings) == 0x20, "InitialLaunchSettings is incorrect size"); 403static_assert(sizeof(InitialLaunchSettings) == 0x20, "InitialLaunchSettings is incorrect size");
404 404
405#pragma pack(push, 4) 405#pragma pack(push, 4)
406struct InitialLaunchSettingsPacked { 406struct InitialLaunchSettingsPacked {
407 InitialLaunchFlag flags; 407 InitialLaunchFlag flags;
408 Service::Time::Clock::SteadyClockTimePoint timestamp; 408 Service::PSC::Time::SteadyClockTimePoint timestamp;
409}; 409};
410#pragma pack(pop) 410#pragma pack(pop)
411static_assert(sizeof(InitialLaunchSettingsPacked) == 0x1C, 411static_assert(sizeof(InitialLaunchSettingsPacked) == 0x1C,
diff --git a/src/core/hle/service/set/system_settings_server.cpp b/src/core/hle/service/set/system_settings_server.cpp
index 87242ae68..688c54b58 100644
--- a/src/core/hle/service/set/system_settings_server.cpp
+++ b/src/core/hle/service/set/system_settings_server.cpp
@@ -489,11 +489,10 @@ void ISystemSettingsServer::SetExternalSteadyClockSourceId(HLERequestContext& ct
489void ISystemSettingsServer::GetUserSystemClockContext(HLERequestContext& ctx) { 489void ISystemSettingsServer::GetUserSystemClockContext(HLERequestContext& ctx) {
490 LOG_INFO(Service_SET, "called"); 490 LOG_INFO(Service_SET, "called");
491 491
492 Service::Time::Clock::SystemClockContext context{}; 492 Service::PSC::Time::SystemClockContext context{};
493 auto res = GetUserSystemClockContext(context); 493 auto res = GetUserSystemClockContext(context);
494 494
495 IPC::ResponseBuilder rb{ctx, 495 IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::SystemClockContext) / sizeof(u32)};
496 2 + sizeof(Service::Time::Clock::SystemClockContext) / sizeof(u32)};
497 rb.Push(res); 496 rb.Push(res);
498 rb.PushRaw(context); 497 rb.PushRaw(context);
499} 498}
@@ -502,7 +501,7 @@ void ISystemSettingsServer::SetUserSystemClockContext(HLERequestContext& ctx) {
502 LOG_INFO(Service_SET, "called"); 501 LOG_INFO(Service_SET, "called");
503 502
504 IPC::RequestParser rp{ctx}; 503 IPC::RequestParser rp{ctx};
505 auto context{rp.PopRaw<Service::Time::Clock::SystemClockContext>()}; 504 auto context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()};
506 505
507 auto res = SetUserSystemClockContext(context); 506 auto res = SetUserSystemClockContext(context);
508 507
@@ -809,19 +808,19 @@ void ISystemSettingsServer::GetQuestFlag(HLERequestContext& ctx) {
809void ISystemSettingsServer::GetDeviceTimeZoneLocationName(HLERequestContext& ctx) { 808void ISystemSettingsServer::GetDeviceTimeZoneLocationName(HLERequestContext& ctx) {
810 LOG_INFO(Service_SET, "called"); 809 LOG_INFO(Service_SET, "called");
811 810
812 Service::Time::TimeZone::LocationName name{}; 811 Service::PSC::Time::LocationName name{};
813 auto res = GetDeviceTimeZoneLocationName(name); 812 auto res = GetDeviceTimeZoneLocationName(name);
814 813
815 IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::Time::TimeZone::LocationName) / sizeof(u32)}; 814 IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::LocationName) / sizeof(u32)};
816 rb.Push(res); 815 rb.Push(res);
817 rb.PushRaw<Service::Time::TimeZone::LocationName>(name); 816 rb.PushRaw<Service::PSC::Time::LocationName>(name);
818} 817}
819 818
820void ISystemSettingsServer::SetDeviceTimeZoneLocationName(HLERequestContext& ctx) { 819void ISystemSettingsServer::SetDeviceTimeZoneLocationName(HLERequestContext& ctx) {
821 LOG_INFO(Service_SET, "called"); 820 LOG_INFO(Service_SET, "called");
822 821
823 IPC::RequestParser rp{ctx}; 822 IPC::RequestParser rp{ctx};
824 auto name{rp.PopRaw<Service::Time::TimeZone::LocationName>()}; 823 auto name{rp.PopRaw<Service::PSC::Time::LocationName>()};
825 824
826 auto res = SetDeviceTimeZoneLocationName(name); 825 auto res = SetDeviceTimeZoneLocationName(name);
827 826
@@ -843,11 +842,10 @@ void ISystemSettingsServer::SetRegionCode(HLERequestContext& ctx) {
843void ISystemSettingsServer::GetNetworkSystemClockContext(HLERequestContext& ctx) { 842void ISystemSettingsServer::GetNetworkSystemClockContext(HLERequestContext& ctx) {
844 LOG_INFO(Service_SET, "called"); 843 LOG_INFO(Service_SET, "called");
845 844
846 Service::Time::Clock::SystemClockContext context{}; 845 Service::PSC::Time::SystemClockContext context{};
847 auto res = GetNetworkSystemClockContext(context); 846 auto res = GetNetworkSystemClockContext(context);
848 847
849 IPC::ResponseBuilder rb{ctx, 848 IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::SystemClockContext) / sizeof(u32)};
850 2 + sizeof(Service::Time::Clock::SystemClockContext) / sizeof(u32)};
851 rb.Push(res); 849 rb.Push(res);
852 rb.PushRaw(context); 850 rb.PushRaw(context);
853} 851}
@@ -856,7 +854,7 @@ void ISystemSettingsServer::SetNetworkSystemClockContext(HLERequestContext& ctx)
856 LOG_INFO(Service_SET, "called"); 854 LOG_INFO(Service_SET, "called");
857 855
858 IPC::RequestParser rp{ctx}; 856 IPC::RequestParser rp{ctx};
859 auto context{rp.PopRaw<Service::Time::Clock::SystemClockContext>()}; 857 auto context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()};
860 858
861 auto res = SetNetworkSystemClockContext(context); 859 auto res = SetNetworkSystemClockContext(context);
862 860
@@ -1136,19 +1134,19 @@ void ISystemSettingsServer::GetKeyboardLayout(HLERequestContext& ctx) {
1136void ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) { 1134void ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) {
1137 LOG_INFO(Service_SET, "called"); 1135 LOG_INFO(Service_SET, "called");
1138 1136
1139 Service::Time::Clock::SteadyClockTimePoint time_point{}; 1137 Service::PSC::Time::SteadyClockTimePoint time_point{};
1140 auto res = GetDeviceTimeZoneLocationUpdatedTime(time_point); 1138 auto res = GetDeviceTimeZoneLocationUpdatedTime(time_point);
1141 1139
1142 IPC::ResponseBuilder rb{ctx, 4}; 1140 IPC::ResponseBuilder rb{ctx, 4};
1143 rb.Push(res); 1141 rb.Push(res);
1144 rb.PushRaw<Service::Time::Clock::SteadyClockTimePoint>(time_point); 1142 rb.PushRaw<Service::PSC::Time::SteadyClockTimePoint>(time_point);
1145} 1143}
1146 1144
1147void ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) { 1145void ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) {
1148 LOG_INFO(Service_SET, "called"); 1146 LOG_INFO(Service_SET, "called");
1149 1147
1150 IPC::RequestParser rp{ctx}; 1148 IPC::RequestParser rp{ctx};
1151 auto time_point{rp.PopRaw<Service::Time::Clock::SteadyClockTimePoint>()}; 1149 auto time_point{rp.PopRaw<Service::PSC::Time::SteadyClockTimePoint>()};
1152 1150
1153 auto res = SetDeviceTimeZoneLocationUpdatedTime(time_point); 1151 auto res = SetDeviceTimeZoneLocationUpdatedTime(time_point);
1154 1152
@@ -1160,12 +1158,12 @@ void ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime(
1160 HLERequestContext& ctx) { 1158 HLERequestContext& ctx) {
1161 LOG_INFO(Service_SET, "called"); 1159 LOG_INFO(Service_SET, "called");
1162 1160
1163 Service::Time::Clock::SteadyClockTimePoint time_point{}; 1161 Service::PSC::Time::SteadyClockTimePoint time_point{};
1164 auto res = GetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); 1162 auto res = GetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
1165 1163
1166 IPC::ResponseBuilder rb{ctx, 4}; 1164 IPC::ResponseBuilder rb{ctx, 4};
1167 rb.Push(res); 1165 rb.Push(res);
1168 rb.PushRaw<Service::Time::Clock::SteadyClockTimePoint>(time_point); 1166 rb.PushRaw<Service::PSC::Time::SteadyClockTimePoint>(time_point);
1169} 1167}
1170 1168
1171void ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime( 1169void ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime(
@@ -1173,7 +1171,7 @@ void ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime(
1173 LOG_INFO(Service_SET, "called"); 1171 LOG_INFO(Service_SET, "called");
1174 1172
1175 IPC::RequestParser rp{ctx}; 1173 IPC::RequestParser rp{ctx};
1176 auto time_point{rp.PopRaw<Service::Time::Clock::SteadyClockTimePoint>()}; 1174 auto time_point{rp.PopRaw<Service::PSC::Time::SteadyClockTimePoint>()};
1177 1175
1178 auto res = SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); 1176 auto res = SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
1179 1177
@@ -1252,25 +1250,25 @@ void ISystemSettingsServer::StoreSettings() {
1252 auto system_dir = 1250 auto system_dir =
1253 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000050"; 1251 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000050";
1254 if (!StoreSettingsFile(system_dir, m_system_settings)) { 1252 if (!StoreSettingsFile(system_dir, m_system_settings)) {
1255 LOG_ERROR(HW_GPU, "Failed to store System settings"); 1253 LOG_ERROR(Service_SET, "Failed to store System settings");
1256 } 1254 }
1257 1255
1258 auto private_dir = 1256 auto private_dir =
1259 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000052"; 1257 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000052";
1260 if (!StoreSettingsFile(private_dir, m_private_settings)) { 1258 if (!StoreSettingsFile(private_dir, m_private_settings)) {
1261 LOG_ERROR(HW_GPU, "Failed to store Private settings"); 1259 LOG_ERROR(Service_SET, "Failed to store Private settings");
1262 } 1260 }
1263 1261
1264 auto device_dir = 1262 auto device_dir =
1265 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000053"; 1263 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000053";
1266 if (!StoreSettingsFile(device_dir, m_device_settings)) { 1264 if (!StoreSettingsFile(device_dir, m_device_settings)) {
1267 LOG_ERROR(HW_GPU, "Failed to store Device settings"); 1265 LOG_ERROR(Service_SET, "Failed to store Device settings");
1268 } 1266 }
1269 1267
1270 auto appln_dir = 1268 auto appln_dir =
1271 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000054"; 1269 Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000054";
1272 if (!StoreSettingsFile(appln_dir, m_appln_settings)) { 1270 if (!StoreSettingsFile(appln_dir, m_appln_settings)) {
1273 LOG_ERROR(HW_GPU, "Failed to store ApplLn settings"); 1271 LOG_ERROR(Service_SET, "Failed to store ApplLn settings");
1274 } 1272 }
1275} 1273}
1276 1274
@@ -1313,39 +1311,39 @@ Result ISystemSettingsServer::SetExternalSteadyClockSourceId(Common::UUID id) {
1313} 1311}
1314 1312
1315Result ISystemSettingsServer::GetUserSystemClockContext( 1313Result ISystemSettingsServer::GetUserSystemClockContext(
1316 Service::Time::Clock::SystemClockContext& out_context) { 1314 Service::PSC::Time::SystemClockContext& out_context) {
1317 out_context = m_system_settings.user_system_clock_context; 1315 out_context = m_system_settings.user_system_clock_context;
1318 R_SUCCEED(); 1316 R_SUCCEED();
1319} 1317}
1320 1318
1321Result ISystemSettingsServer::SetUserSystemClockContext( 1319Result ISystemSettingsServer::SetUserSystemClockContext(
1322 Service::Time::Clock::SystemClockContext& context) { 1320 Service::PSC::Time::SystemClockContext& context) {
1323 m_system_settings.user_system_clock_context = context; 1321 m_system_settings.user_system_clock_context = context;
1324 SetSaveNeeded(); 1322 SetSaveNeeded();
1325 R_SUCCEED(); 1323 R_SUCCEED();
1326} 1324}
1327 1325
1328Result ISystemSettingsServer::GetDeviceTimeZoneLocationName( 1326Result ISystemSettingsServer::GetDeviceTimeZoneLocationName(
1329 Service::Time::TimeZone::LocationName& out_name) { 1327 Service::PSC::Time::LocationName& out_name) {
1330 out_name = m_system_settings.device_time_zone_location_name; 1328 out_name = m_system_settings.device_time_zone_location_name;
1331 R_SUCCEED(); 1329 R_SUCCEED();
1332} 1330}
1333 1331
1334Result ISystemSettingsServer::SetDeviceTimeZoneLocationName( 1332Result ISystemSettingsServer::SetDeviceTimeZoneLocationName(
1335 Service::Time::TimeZone::LocationName& name) { 1333 Service::PSC::Time::LocationName& name) {
1336 m_system_settings.device_time_zone_location_name = name; 1334 m_system_settings.device_time_zone_location_name = name;
1337 SetSaveNeeded(); 1335 SetSaveNeeded();
1338 R_SUCCEED(); 1336 R_SUCCEED();
1339} 1337}
1340 1338
1341Result ISystemSettingsServer::GetNetworkSystemClockContext( 1339Result ISystemSettingsServer::GetNetworkSystemClockContext(
1342 Service::Time::Clock::SystemClockContext& out_context) { 1340 Service::PSC::Time::SystemClockContext& out_context) {
1343 out_context = m_system_settings.network_system_clock_context; 1341 out_context = m_system_settings.network_system_clock_context;
1344 R_SUCCEED(); 1342 R_SUCCEED();
1345} 1343}
1346 1344
1347Result ISystemSettingsServer::SetNetworkSystemClockContext( 1345Result ISystemSettingsServer::SetNetworkSystemClockContext(
1348 Service::Time::Clock::SystemClockContext& context) { 1346 Service::PSC::Time::SystemClockContext& context) {
1349 m_system_settings.network_system_clock_context = context; 1347 m_system_settings.network_system_clock_context = context;
1350 SetSaveNeeded(); 1348 SetSaveNeeded();
1351 R_SUCCEED(); 1349 R_SUCCEED();
@@ -1374,26 +1372,26 @@ Result ISystemSettingsServer::GetExternalSteadyClockInternalOffset(s64& out_offs
1374} 1372}
1375 1373
1376Result ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime( 1374Result ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime(
1377 Service::Time::Clock::SteadyClockTimePoint& out_time_point) { 1375 Service::PSC::Time::SteadyClockTimePoint& out_time_point) {
1378 out_time_point = m_system_settings.device_time_zone_location_updated_time; 1376 out_time_point = m_system_settings.device_time_zone_location_updated_time;
1379 R_SUCCEED(); 1377 R_SUCCEED();
1380} 1378}
1381 1379
1382Result ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime( 1380Result ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime(
1383 Service::Time::Clock::SteadyClockTimePoint& time_point) { 1381 Service::PSC::Time::SteadyClockTimePoint& time_point) {
1384 m_system_settings.device_time_zone_location_updated_time = time_point; 1382 m_system_settings.device_time_zone_location_updated_time = time_point;
1385 SetSaveNeeded(); 1383 SetSaveNeeded();
1386 R_SUCCEED(); 1384 R_SUCCEED();
1387} 1385}
1388 1386
1389Result ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime( 1387Result ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime(
1390 Service::Time::Clock::SteadyClockTimePoint& out_time_point) { 1388 Service::PSC::Time::SteadyClockTimePoint& out_time_point) {
1391 out_time_point = m_system_settings.user_system_clock_automatic_correction_updated_time_point; 1389 out_time_point = m_system_settings.user_system_clock_automatic_correction_updated_time_point;
1392 R_SUCCEED(); 1390 R_SUCCEED();
1393} 1391}
1394 1392
1395Result ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime( 1393Result ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime(
1396 Service::Time::Clock::SteadyClockTimePoint out_time_point) { 1394 Service::PSC::Time::SteadyClockTimePoint out_time_point) {
1397 m_system_settings.user_system_clock_automatic_correction_updated_time_point = out_time_point; 1395 m_system_settings.user_system_clock_automatic_correction_updated_time_point = out_time_point;
1398 SetSaveNeeded(); 1396 SetSaveNeeded();
1399 R_SUCCEED(); 1397 R_SUCCEED();
diff --git a/src/core/hle/service/set/system_settings_server.h b/src/core/hle/service/set/system_settings_server.h
index 32716f567..a2258d16d 100644
--- a/src/core/hle/service/set/system_settings_server.h
+++ b/src/core/hle/service/set/system_settings_server.h
@@ -11,14 +11,13 @@
11#include "common/polyfill_thread.h" 11#include "common/polyfill_thread.h"
12#include "common/uuid.h" 12#include "common/uuid.h"
13#include "core/hle/result.h" 13#include "core/hle/result.h"
14#include "core/hle/service/psc/time/common.h"
14#include "core/hle/service/service.h" 15#include "core/hle/service/service.h"
15#include "core/hle/service/set/setting_formats/appln_settings.h" 16#include "core/hle/service/set/setting_formats/appln_settings.h"
16#include "core/hle/service/set/setting_formats/device_settings.h" 17#include "core/hle/service/set/setting_formats/device_settings.h"
17#include "core/hle/service/set/setting_formats/private_settings.h" 18#include "core/hle/service/set/setting_formats/private_settings.h"
18#include "core/hle/service/set/setting_formats/system_settings.h" 19#include "core/hle/service/set/setting_formats/system_settings.h"
19#include "core/hle/service/set/settings_types.h" 20#include "core/hle/service/set/settings_types.h"
20#include "core/hle/service/time/clock_types.h"
21#include "core/hle/service/time/time_zone_types.h"
22 21
23namespace Core { 22namespace Core {
24class System; 23class System;
@@ -51,24 +50,24 @@ public:
51 50
52 Result GetExternalSteadyClockSourceId(Common::UUID& out_id); 51 Result GetExternalSteadyClockSourceId(Common::UUID& out_id);
53 Result SetExternalSteadyClockSourceId(Common::UUID id); 52 Result SetExternalSteadyClockSourceId(Common::UUID id);
54 Result GetUserSystemClockContext(Service::Time::Clock::SystemClockContext& out_context); 53 Result GetUserSystemClockContext(Service::PSC::Time::SystemClockContext& out_context);
55 Result SetUserSystemClockContext(Service::Time::Clock::SystemClockContext& context); 54 Result SetUserSystemClockContext(Service::PSC::Time::SystemClockContext& context);
56 Result GetDeviceTimeZoneLocationName(Service::Time::TimeZone::LocationName& out_name); 55 Result GetDeviceTimeZoneLocationName(Service::PSC::Time::LocationName& out_name);
57 Result SetDeviceTimeZoneLocationName(Service::Time::TimeZone::LocationName& name); 56 Result SetDeviceTimeZoneLocationName(Service::PSC::Time::LocationName& name);
58 Result GetNetworkSystemClockContext(Service::Time::Clock::SystemClockContext& out_context); 57 Result GetNetworkSystemClockContext(Service::PSC::Time::SystemClockContext& out_context);
59 Result SetNetworkSystemClockContext(Service::Time::Clock::SystemClockContext& context); 58 Result SetNetworkSystemClockContext(Service::PSC::Time::SystemClockContext& context);
60 Result IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled); 59 Result IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled);
61 Result SetUserSystemClockAutomaticCorrectionEnabled(bool enabled); 60 Result SetUserSystemClockAutomaticCorrectionEnabled(bool enabled);
62 Result SetExternalSteadyClockInternalOffset(s64 offset); 61 Result SetExternalSteadyClockInternalOffset(s64 offset);
63 Result GetExternalSteadyClockInternalOffset(s64& out_offset); 62 Result GetExternalSteadyClockInternalOffset(s64& out_offset);
64 Result GetDeviceTimeZoneLocationUpdatedTime( 63 Result GetDeviceTimeZoneLocationUpdatedTime(
65 Service::Time::Clock::SteadyClockTimePoint& out_time_point); 64 Service::PSC::Time::SteadyClockTimePoint& out_time_point);
66 Result SetDeviceTimeZoneLocationUpdatedTime( 65 Result SetDeviceTimeZoneLocationUpdatedTime(
67 Service::Time::Clock::SteadyClockTimePoint& time_point); 66 Service::PSC::Time::SteadyClockTimePoint& time_point);
68 Result GetUserSystemClockAutomaticCorrectionUpdatedTime( 67 Result GetUserSystemClockAutomaticCorrectionUpdatedTime(
69 Service::Time::Clock::SteadyClockTimePoint& out_time_point); 68 Service::PSC::Time::SteadyClockTimePoint& out_time_point);
70 Result SetUserSystemClockAutomaticCorrectionUpdatedTime( 69 Result SetUserSystemClockAutomaticCorrectionUpdatedTime(
71 Service::Time::Clock::SteadyClockTimePoint time_point); 70 Service::PSC::Time::SteadyClockTimePoint time_point);
72 71
73private: 72private:
74 void SetLanguageCode(HLERequestContext& ctx); 73 void SetLanguageCode(HLERequestContext& ctx);
@@ -147,8 +146,8 @@ private:
147 PrivateSettings m_private_settings{}; 146 PrivateSettings m_private_settings{};
148 DeviceSettings m_device_settings{}; 147 DeviceSettings m_device_settings{};
149 ApplnSettings m_appln_settings{}; 148 ApplnSettings m_appln_settings{};
150 std::jthread m_save_thread;
151 std::mutex m_save_needed_mutex; 149 std::mutex m_save_needed_mutex;
150 std::jthread m_save_thread;
152 bool m_save_needed{false}; 151 bool m_save_needed{false};
153}; 152};
154 153
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index 4ae32a9c1..32c218638 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -3,6 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <chrono>
6#include <memory> 7#include <memory>
7#include <mutex> 8#include <mutex>
8#include <string> 9#include <string>
@@ -10,6 +11,7 @@
10 11
11#include "common/concepts.h" 12#include "common/concepts.h"
12#include "core/hle/kernel/k_port.h" 13#include "core/hle/kernel/k_port.h"
14#include "core/hle/kernel/svc.h"
13#include "core/hle/result.h" 15#include "core/hle/result.h"
14#include "core/hle/service/service.h" 16#include "core/hle/service/service.h"
15 17
@@ -62,12 +64,21 @@ public:
62 Result GetServicePort(Kernel::KClientPort** out_client_port, const std::string& name); 64 Result GetServicePort(Kernel::KClientPort** out_client_port, const std::string& name);
63 65
64 template <Common::DerivedFrom<SessionRequestHandler> T> 66 template <Common::DerivedFrom<SessionRequestHandler> T>
65 std::shared_ptr<T> GetService(const std::string& service_name) const { 67 std::shared_ptr<T> GetService(const std::string& service_name, bool block = false) const {
66 auto service = registered_services.find(service_name); 68 auto service = registered_services.find(service_name);
67 if (service == registered_services.end()) { 69 if (service == registered_services.end() && !block) {
68 LOG_DEBUG(Service, "Can't find service: {}", service_name); 70 LOG_DEBUG(Service, "Can't find service: {}", service_name);
69 return nullptr; 71 return nullptr;
72 } else if (block) {
73 using namespace std::literals::chrono_literals;
74 while (service == registered_services.end()) {
75 Kernel::Svc::SleepThread(
76 kernel.System(),
77 std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count());
78 service = registered_services.find(service_name);
79 }
70 } 80 }
81
71 return std::static_pointer_cast<T>(service->second()); 82 return std::static_pointer_cast<T>(service->second());
72 } 83 }
73 84
diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h
deleted file mode 100644
index 7149fffeb..000000000
--- a/src/core/hle/service/time/clock_types.h
+++ /dev/null
@@ -1,129 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <ratio>
7
8#include "common/common_funcs.h"
9#include "common/common_types.h"
10#include "common/uuid.h"
11#include "core/hle/service/time/errors.h"
12#include "core/hle/service/time/time_zone_types.h"
13
14// Defined by WinBase.h on Windows
15#ifdef GetCurrentTime
16#undef GetCurrentTime
17#endif
18
19namespace Service::Time::Clock {
20
21enum class TimeType : u8 {
22 UserSystemClock,
23 NetworkSystemClock,
24 LocalSystemClock,
25};
26
27/// https://switchbrew.org/wiki/Glue_services#SteadyClockTimePoint
28struct SteadyClockTimePoint {
29 s64 time_point;
30 Common::UUID clock_source_id;
31
32 Result GetSpanBetween(SteadyClockTimePoint other, s64& span) const {
33 span = 0;
34
35 if (clock_source_id != other.clock_source_id) {
36 return ERROR_TIME_MISMATCH;
37 }
38
39 span = other.time_point - time_point;
40
41 return ResultSuccess;
42 }
43
44 static SteadyClockTimePoint GetRandom() {
45 return {0, Common::UUID::MakeRandom()};
46 }
47};
48static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size");
49static_assert(std::is_trivially_copyable_v<SteadyClockTimePoint>,
50 "SteadyClockTimePoint must be trivially copyable");
51
52struct SteadyClockContext {
53 u64 internal_offset;
54 Common::UUID steady_time_point;
55};
56static_assert(sizeof(SteadyClockContext) == 0x18, "SteadyClockContext is incorrect size");
57static_assert(std::is_trivially_copyable_v<SteadyClockContext>,
58 "SteadyClockContext must be trivially copyable");
59using StandardSteadyClockTimePointType = SteadyClockContext;
60
61struct SystemClockContext {
62 s64 offset;
63 SteadyClockTimePoint steady_time_point;
64};
65static_assert(sizeof(SystemClockContext) == 0x20, "SystemClockContext is incorrect size");
66static_assert(std::is_trivially_copyable_v<SystemClockContext>,
67 "SystemClockContext must be trivially copyable");
68
69struct ContinuousAdjustmentTimePoint {
70 s64 measurement_offset;
71 s64 diff_scale;
72 u32 shift_amount;
73 s64 lower;
74 s64 upper;
75 Common::UUID clock_source_id;
76};
77static_assert(sizeof(ContinuousAdjustmentTimePoint) == 0x38);
78static_assert(std::is_trivially_copyable_v<ContinuousAdjustmentTimePoint>,
79 "ContinuousAdjustmentTimePoint must be trivially copyable");
80
81/// https://switchbrew.org/wiki/Glue_services#TimeSpanType
82struct TimeSpanType {
83 s64 nanoseconds{};
84
85 s64 ToSeconds() const {
86 return nanoseconds / std::nano::den;
87 }
88
89 static TimeSpanType FromSeconds(s64 seconds) {
90 return {seconds * std::nano::den};
91 }
92
93 template <u64 Frequency>
94 static TimeSpanType FromTicks(u64 ticks) {
95 using TicksToNSRatio = std::ratio<std::nano::den, Frequency>;
96 return {static_cast<s64>(ticks * TicksToNSRatio::num / TicksToNSRatio::den)};
97 }
98};
99static_assert(sizeof(TimeSpanType) == 8, "TimeSpanType is incorrect size");
100
101struct ClockSnapshot {
102 SystemClockContext user_context;
103 SystemClockContext network_context;
104 s64 user_time;
105 s64 network_time;
106 TimeZone::CalendarTime user_calendar_time;
107 TimeZone::CalendarTime network_calendar_time;
108 TimeZone::CalendarAdditionalInfo user_calendar_additional_time;
109 TimeZone::CalendarAdditionalInfo network_calendar_additional_time;
110 SteadyClockTimePoint steady_clock_time_point;
111 TimeZone::LocationName location_name;
112 u8 is_automatic_correction_enabled;
113 TimeType type;
114 INSERT_PADDING_BYTES_NOINIT(0x2);
115
116 static Result GetCurrentTime(s64& current_time,
117 const SteadyClockTimePoint& steady_clock_time_point,
118 const SystemClockContext& context) {
119 if (steady_clock_time_point.clock_source_id != context.steady_time_point.clock_source_id) {
120 current_time = 0;
121 return ERROR_TIME_MISMATCH;
122 }
123 current_time = steady_clock_time_point.time_point + context.offset;
124 return ResultSuccess;
125 }
126};
127static_assert(sizeof(ClockSnapshot) == 0xD0, "ClockSnapshot is incorrect size");
128
129} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h b/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h
deleted file mode 100644
index 0f928a5a5..000000000
--- a/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h
+++ /dev/null
@@ -1,15 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/time/system_clock_context_update_callback.h"
7
8namespace Service::Time::Clock {
9
10class EphemeralNetworkSystemClockContextWriter final : public SystemClockContextUpdateCallback {
11public:
12 EphemeralNetworkSystemClockContextWriter() : SystemClockContextUpdateCallback{} {}
13};
14
15} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/ephemeral_network_system_clock_core.h b/src/core/hle/service/time/ephemeral_network_system_clock_core.h
deleted file mode 100644
index 0a5f5aafb..000000000
--- a/src/core/hle/service/time/ephemeral_network_system_clock_core.h
+++ /dev/null
@@ -1,16 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/time/system_clock_core.h"
7
8namespace Service::Time::Clock {
9
10class EphemeralNetworkSystemClockCore final : public SystemClockCore {
11public:
12 explicit EphemeralNetworkSystemClockCore(SteadyClockCore& steady_clock_core_)
13 : SystemClockCore{steady_clock_core_} {}
14};
15
16} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/errors.h b/src/core/hle/service/time/errors.h
deleted file mode 100644
index 6655d30e1..000000000
--- a/src/core/hle/service/time/errors.h
+++ /dev/null
@@ -1,21 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/result.h"
7
8namespace Service::Time {
9
10constexpr Result ERROR_PERMISSION_DENIED{ErrorModule::Time, 1};
11constexpr Result ERROR_TIME_MISMATCH{ErrorModule::Time, 102};
12constexpr Result ERROR_UNINITIALIZED_CLOCK{ErrorModule::Time, 103};
13constexpr Result ERROR_TIME_NOT_FOUND{ErrorModule::Time, 200};
14constexpr Result ERROR_OVERFLOW{ErrorModule::Time, 201};
15constexpr Result ERROR_LOCATION_NAME_TOO_LONG{ErrorModule::Time, 801};
16constexpr Result ERROR_OUT_OF_RANGE{ErrorModule::Time, 902};
17constexpr Result ERROR_TIME_ZONE_CONVERSION_FAILED{ErrorModule::Time, 903};
18constexpr Result ERROR_TIME_ZONE_NOT_FOUND{ErrorModule::Time, 989};
19constexpr Result ERROR_NOT_IMPLEMENTED{ErrorModule::Time, 990};
20
21} // namespace Service::Time
diff --git a/src/core/hle/service/time/local_system_clock_context_writer.h b/src/core/hle/service/time/local_system_clock_context_writer.h
deleted file mode 100644
index 1639ef2b9..000000000
--- a/src/core/hle/service/time/local_system_clock_context_writer.h
+++ /dev/null
@@ -1,26 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/time/system_clock_context_update_callback.h"
7#include "core/hle/service/time/time_sharedmemory.h"
8
9namespace Service::Time::Clock {
10
11class LocalSystemClockContextWriter final : public SystemClockContextUpdateCallback {
12public:
13 explicit LocalSystemClockContextWriter(SharedMemory& shared_memory_)
14 : SystemClockContextUpdateCallback{}, shared_memory{shared_memory_} {}
15
16protected:
17 Result Update() override {
18 shared_memory.UpdateLocalSystemClockContext(context);
19 return ResultSuccess;
20 }
21
22private:
23 SharedMemory& shared_memory;
24};
25
26} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/network_system_clock_context_writer.h b/src/core/hle/service/time/network_system_clock_context_writer.h
deleted file mode 100644
index 655e4c06d..000000000
--- a/src/core/hle/service/time/network_system_clock_context_writer.h
+++ /dev/null
@@ -1,27 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/time/errors.h"
7#include "core/hle/service/time/system_clock_context_update_callback.h"
8#include "core/hle/service/time/time_sharedmemory.h"
9
10namespace Service::Time::Clock {
11
12class NetworkSystemClockContextWriter final : public SystemClockContextUpdateCallback {
13public:
14 explicit NetworkSystemClockContextWriter(SharedMemory& shared_memory_)
15 : SystemClockContextUpdateCallback{}, shared_memory{shared_memory_} {}
16
17protected:
18 Result Update() override {
19 shared_memory.UpdateNetworkSystemClockContext(context);
20 return ResultSuccess;
21 }
22
23private:
24 SharedMemory& shared_memory;
25};
26
27} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/standard_local_system_clock_core.h b/src/core/hle/service/time/standard_local_system_clock_core.h
deleted file mode 100644
index ae2ff1bfd..000000000
--- a/src/core/hle/service/time/standard_local_system_clock_core.h
+++ /dev/null
@@ -1,16 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/time/system_clock_core.h"
7
8namespace Service::Time::Clock {
9
10class StandardLocalSystemClockCore final : public SystemClockCore {
11public:
12 explicit StandardLocalSystemClockCore(SteadyClockCore& steady_clock_core_)
13 : SystemClockCore{steady_clock_core_} {}
14};
15
16} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/standard_network_system_clock_core.h b/src/core/hle/service/time/standard_network_system_clock_core.h
deleted file mode 100644
index c1ec5252b..000000000
--- a/src/core/hle/service/time/standard_network_system_clock_core.h
+++ /dev/null
@@ -1,45 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/time/clock_types.h"
7#include "core/hle/service/time/steady_clock_core.h"
8#include "core/hle/service/time/system_clock_core.h"
9
10namespace Core {
11class System;
12}
13
14namespace Service::Time::Clock {
15
16class StandardNetworkSystemClockCore final : public SystemClockCore {
17public:
18 explicit StandardNetworkSystemClockCore(SteadyClockCore& steady_clock_core_)
19 : SystemClockCore{steady_clock_core_} {}
20
21 void SetStandardNetworkClockSufficientAccuracy(TimeSpanType value) {
22 standard_network_clock_sufficient_accuracy = value;
23 }
24
25 bool IsStandardNetworkSystemClockAccuracySufficient(Core::System& system) const {
26 SystemClockContext clock_ctx{};
27 if (GetClockContext(system, clock_ctx) != ResultSuccess) {
28 return {};
29 }
30
31 s64 span{};
32 if (clock_ctx.steady_time_point.GetSpanBetween(
33 GetSteadyClockCore().GetCurrentTimePoint(system), span) != ResultSuccess) {
34 return {};
35 }
36
37 return TimeSpanType{span}.nanoseconds <
38 standard_network_clock_sufficient_accuracy.nanoseconds;
39 }
40
41private:
42 TimeSpanType standard_network_clock_sufficient_accuracy{};
43};
44
45} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/standard_steady_clock_core.cpp b/src/core/hle/service/time/standard_steady_clock_core.cpp
deleted file mode 100644
index 5627b7003..000000000
--- a/src/core/hle/service/time/standard_steady_clock_core.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/core_timing.h"
6#include "core/hardware_properties.h"
7#include "core/hle/service/time/standard_steady_clock_core.h"
8
9namespace Service::Time::Clock {
10
11TimeSpanType StandardSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) {
12 const TimeSpanType ticks_time_span{
13 TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>(system.CoreTiming().GetClockTicks())};
14 TimeSpanType raw_time_point{setup_value.nanoseconds + ticks_time_span.nanoseconds};
15
16 if (raw_time_point.nanoseconds < cached_raw_time_point.nanoseconds) {
17 raw_time_point.nanoseconds = cached_raw_time_point.nanoseconds;
18 }
19
20 cached_raw_time_point = raw_time_point;
21 return raw_time_point;
22}
23
24} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/standard_steady_clock_core.h b/src/core/hle/service/time/standard_steady_clock_core.h
deleted file mode 100644
index 036463b87..000000000
--- a/src/core/hle/service/time/standard_steady_clock_core.h
+++ /dev/null
@@ -1,41 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/time/clock_types.h"
7#include "core/hle/service/time/steady_clock_core.h"
8
9namespace Core {
10class System;
11}
12
13namespace Service::Time::Clock {
14
15class StandardSteadyClockCore final : public SteadyClockCore {
16public:
17 SteadyClockTimePoint GetTimePoint(Core::System& system) override {
18 return {GetCurrentRawTimePoint(system).ToSeconds(), GetClockSourceId()};
19 }
20
21 TimeSpanType GetInternalOffset() const override {
22 return internal_offset;
23 }
24
25 void SetInternalOffset(TimeSpanType value) override {
26 internal_offset = value;
27 }
28
29 TimeSpanType GetCurrentRawTimePoint(Core::System& system) override;
30
31 void SetSetupValue(TimeSpanType value) {
32 setup_value = value;
33 }
34
35private:
36 TimeSpanType setup_value{};
37 TimeSpanType internal_offset{};
38 TimeSpanType cached_raw_time_point{};
39};
40
41} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/standard_user_system_clock_core.cpp b/src/core/hle/service/time/standard_user_system_clock_core.cpp
deleted file mode 100644
index b033757ed..000000000
--- a/src/core/hle/service/time/standard_user_system_clock_core.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/assert.h"
5#include "core/core.h"
6#include "core/hle/kernel/k_event.h"
7#include "core/hle/service/time/standard_local_system_clock_core.h"
8#include "core/hle/service/time/standard_network_system_clock_core.h"
9#include "core/hle/service/time/standard_user_system_clock_core.h"
10
11namespace Service::Time::Clock {
12
13StandardUserSystemClockCore::StandardUserSystemClockCore(
14 StandardLocalSystemClockCore& local_system_clock_core_,
15 StandardNetworkSystemClockCore& network_system_clock_core_, Core::System& system_)
16 : SystemClockCore(local_system_clock_core_.GetSteadyClockCore()),
17 local_system_clock_core{local_system_clock_core_},
18 network_system_clock_core{network_system_clock_core_},
19 auto_correction_time{SteadyClockTimePoint::GetRandom()}, service_context{
20 system_,
21 "StandardUserSystemClockCore"} {
22 auto_correction_event =
23 service_context.CreateEvent("StandardUserSystemClockCore:AutoCorrectionEvent");
24}
25
26StandardUserSystemClockCore::~StandardUserSystemClockCore() {
27 service_context.CloseEvent(auto_correction_event);
28}
29
30Result StandardUserSystemClockCore::SetAutomaticCorrectionEnabled(Core::System& system,
31 bool value) {
32 if (const Result result{ApplyAutomaticCorrection(system, value)}; result != ResultSuccess) {
33 return result;
34 }
35
36 auto_correction_enabled = value;
37
38 return ResultSuccess;
39}
40
41Result StandardUserSystemClockCore::GetClockContext(Core::System& system,
42 SystemClockContext& ctx) const {
43 if (const Result result{ApplyAutomaticCorrection(system, false)}; result != ResultSuccess) {
44 return result;
45 }
46
47 return local_system_clock_core.GetClockContext(system, ctx);
48}
49
50Result StandardUserSystemClockCore::Flush(const SystemClockContext&) {
51 UNIMPLEMENTED();
52 return ERROR_NOT_IMPLEMENTED;
53}
54
55Result StandardUserSystemClockCore::SetClockContext(const SystemClockContext&) {
56 UNIMPLEMENTED();
57 return ERROR_NOT_IMPLEMENTED;
58}
59
60Result StandardUserSystemClockCore::ApplyAutomaticCorrection(Core::System& system,
61 bool value) const {
62 if (auto_correction_enabled == value) {
63 return ResultSuccess;
64 }
65
66 if (!network_system_clock_core.IsClockSetup(system)) {
67 return ERROR_UNINITIALIZED_CLOCK;
68 }
69
70 SystemClockContext ctx{};
71 if (const Result result{network_system_clock_core.GetClockContext(system, ctx)};
72 result != ResultSuccess) {
73 return result;
74 }
75
76 local_system_clock_core.SetClockContext(ctx);
77
78 return ResultSuccess;
79}
80
81} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/standard_user_system_clock_core.h b/src/core/hle/service/time/standard_user_system_clock_core.h
deleted file mode 100644
index ee6e29487..000000000
--- a/src/core/hle/service/time/standard_user_system_clock_core.h
+++ /dev/null
@@ -1,63 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/kernel_helpers.h"
7#include "core/hle/service/time/clock_types.h"
8#include "core/hle/service/time/system_clock_core.h"
9
10namespace Core {
11class System;
12}
13
14namespace Kernel {
15class KEvent;
16}
17
18namespace Service::Time::Clock {
19
20class StandardLocalSystemClockCore;
21class StandardNetworkSystemClockCore;
22
23class StandardUserSystemClockCore final : public SystemClockCore {
24public:
25 StandardUserSystemClockCore(StandardLocalSystemClockCore& local_system_clock_core_,
26 StandardNetworkSystemClockCore& network_system_clock_core_,
27 Core::System& system_);
28
29 ~StandardUserSystemClockCore() override;
30
31 Result SetAutomaticCorrectionEnabled(Core::System& system, bool value);
32
33 Result GetClockContext(Core::System& system, SystemClockContext& ctx) const override;
34
35 bool IsAutomaticCorrectionEnabled() const {
36 return auto_correction_enabled;
37 }
38
39 void SetAutomaticCorrectionUpdatedTime(SteadyClockTimePoint steady_clock_time_point) {
40 auto_correction_time = steady_clock_time_point;
41 }
42
43protected:
44 Result Flush(const SystemClockContext&) override;
45
46 Result SetClockContext(const SystemClockContext&) override;
47
48 Result ApplyAutomaticCorrection(Core::System& system, bool value) const;
49
50 const SteadyClockTimePoint& GetAutomaticCorrectionUpdatedTime() const {
51 return auto_correction_time;
52 }
53
54private:
55 StandardLocalSystemClockCore& local_system_clock_core;
56 StandardNetworkSystemClockCore& network_system_clock_core;
57 bool auto_correction_enabled{};
58 SteadyClockTimePoint auto_correction_time;
59 KernelHelpers::ServiceContext service_context;
60 Kernel::KEvent* auto_correction_event;
61};
62
63} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/steady_clock_core.h b/src/core/hle/service/time/steady_clock_core.h
deleted file mode 100644
index 2867c351c..000000000
--- a/src/core/hle/service/time/steady_clock_core.h
+++ /dev/null
@@ -1,55 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/uuid.h"
7#include "core/hle/service/time/clock_types.h"
8
9namespace Core {
10class System;
11}
12
13namespace Service::Time::Clock {
14
15class SteadyClockCore {
16public:
17 SteadyClockCore() = default;
18 virtual ~SteadyClockCore() = default;
19
20 const Common::UUID& GetClockSourceId() const {
21 return clock_source_id;
22 }
23
24 void SetClockSourceId(const Common::UUID& value) {
25 clock_source_id = value;
26 }
27
28 virtual TimeSpanType GetInternalOffset() const = 0;
29
30 virtual void SetInternalOffset(TimeSpanType internal_offset) = 0;
31
32 virtual SteadyClockTimePoint GetTimePoint(Core::System& system) = 0;
33
34 virtual TimeSpanType GetCurrentRawTimePoint(Core::System& system) = 0;
35
36 SteadyClockTimePoint GetCurrentTimePoint(Core::System& system) {
37 SteadyClockTimePoint result{GetTimePoint(system)};
38 result.time_point += GetInternalOffset().ToSeconds();
39 return result;
40 }
41
42 bool IsInitialized() const {
43 return is_initialized;
44 }
45
46 void MarkAsInitialized() {
47 is_initialized = true;
48 }
49
50private:
51 Common::UUID clock_source_id{Common::UUID::MakeRandom()};
52 bool is_initialized{};
53};
54
55} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/system_clock_context_update_callback.cpp b/src/core/hle/service/time/system_clock_context_update_callback.cpp
deleted file mode 100644
index cafc04ee7..000000000
--- a/src/core/hle/service/time/system_clock_context_update_callback.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/k_event.h"
5#include "core/hle/service/time/errors.h"
6#include "core/hle/service/time/system_clock_context_update_callback.h"
7
8namespace Service::Time::Clock {
9
10SystemClockContextUpdateCallback::SystemClockContextUpdateCallback() = default;
11SystemClockContextUpdateCallback::~SystemClockContextUpdateCallback() = default;
12
13bool SystemClockContextUpdateCallback::NeedUpdate(const SystemClockContext& value) const {
14 if (has_context) {
15 return context.offset != value.offset ||
16 context.steady_time_point.clock_source_id != value.steady_time_point.clock_source_id;
17 }
18
19 return true;
20}
21
22void SystemClockContextUpdateCallback::RegisterOperationEvent(
23 std::shared_ptr<Kernel::KEvent>&& event) {
24 operation_event_list.emplace_back(std::move(event));
25}
26
27void SystemClockContextUpdateCallback::BroadcastOperationEvent() {
28 for (const auto& event : operation_event_list) {
29 event->Signal();
30 }
31}
32
33Result SystemClockContextUpdateCallback::Update(const SystemClockContext& value) {
34 Result result{ResultSuccess};
35
36 if (NeedUpdate(value)) {
37 context = value;
38 has_context = true;
39
40 result = Update();
41
42 if (result == ResultSuccess) {
43 BroadcastOperationEvent();
44 }
45 }
46
47 return result;
48}
49
50Result SystemClockContextUpdateCallback::Update() {
51 return ResultSuccess;
52}
53
54} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/system_clock_context_update_callback.h b/src/core/hle/service/time/system_clock_context_update_callback.h
deleted file mode 100644
index bf657acd9..000000000
--- a/src/core/hle/service/time/system_clock_context_update_callback.h
+++ /dev/null
@@ -1,43 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <vector>
8
9#include "core/hle/service/time/clock_types.h"
10
11namespace Kernel {
12class KEvent;
13}
14
15namespace Service::Time::Clock {
16
17// Parts of this implementation were based on Ryujinx (https://github.com/Ryujinx/Ryujinx/pull/783).
18// This code was released under public domain.
19
20class SystemClockContextUpdateCallback {
21public:
22 SystemClockContextUpdateCallback();
23 virtual ~SystemClockContextUpdateCallback();
24
25 bool NeedUpdate(const SystemClockContext& value) const;
26
27 void RegisterOperationEvent(std::shared_ptr<Kernel::KEvent>&& event);
28
29 void BroadcastOperationEvent();
30
31 Result Update(const SystemClockContext& value);
32
33protected:
34 virtual Result Update();
35
36 SystemClockContext context{};
37
38private:
39 bool has_context{};
40 std::vector<std::shared_ptr<Kernel::KEvent>> operation_event_list;
41};
42
43} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/system_clock_core.cpp b/src/core/hle/service/time/system_clock_core.cpp
deleted file mode 100644
index da078241f..000000000
--- a/src/core/hle/service/time/system_clock_core.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/time/steady_clock_core.h"
5#include "core/hle/service/time/system_clock_context_update_callback.h"
6#include "core/hle/service/time/system_clock_core.h"
7
8namespace Service::Time::Clock {
9
10SystemClockCore::SystemClockCore(SteadyClockCore& steady_clock_core_)
11 : steady_clock_core{steady_clock_core_} {
12 context.steady_time_point.clock_source_id = steady_clock_core.GetClockSourceId();
13}
14
15SystemClockCore::~SystemClockCore() = default;
16
17Result SystemClockCore::GetCurrentTime(Core::System& system, s64& posix_time) const {
18 posix_time = 0;
19
20 const SteadyClockTimePoint current_time_point{steady_clock_core.GetCurrentTimePoint(system)};
21
22 SystemClockContext clock_context{};
23 if (const Result result{GetClockContext(system, clock_context)}; result != ResultSuccess) {
24 return result;
25 }
26
27 if (current_time_point.clock_source_id != clock_context.steady_time_point.clock_source_id) {
28 return ERROR_TIME_MISMATCH;
29 }
30
31 posix_time = clock_context.offset + current_time_point.time_point;
32
33 return ResultSuccess;
34}
35
36Result SystemClockCore::SetCurrentTime(Core::System& system, s64 posix_time) {
37 const SteadyClockTimePoint current_time_point{steady_clock_core.GetCurrentTimePoint(system)};
38 const SystemClockContext clock_context{posix_time - current_time_point.time_point,
39 current_time_point};
40
41 if (const Result result{SetClockContext(clock_context)}; result != ResultSuccess) {
42 return result;
43 }
44 return Flush(clock_context);
45}
46
47Result SystemClockCore::Flush(const SystemClockContext& clock_context) {
48 if (!system_clock_context_update_callback) {
49 return ResultSuccess;
50 }
51 return system_clock_context_update_callback->Update(clock_context);
52}
53
54Result SystemClockCore::SetSystemClockContext(const SystemClockContext& clock_context) {
55 if (const Result result{SetClockContext(clock_context)}; result != ResultSuccess) {
56 return result;
57 }
58 return Flush(clock_context);
59}
60
61bool SystemClockCore::IsClockSetup(Core::System& system) const {
62 SystemClockContext value{};
63 if (GetClockContext(system, value) == ResultSuccess) {
64 const SteadyClockTimePoint steady_clock_time_point{
65 steady_clock_core.GetCurrentTimePoint(system)};
66 return steady_clock_time_point.clock_source_id == value.steady_time_point.clock_source_id;
67 }
68 return {};
69}
70
71} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/system_clock_core.h b/src/core/hle/service/time/system_clock_core.h
deleted file mode 100644
index 8cb34126f..000000000
--- a/src/core/hle/service/time/system_clock_core.h
+++ /dev/null
@@ -1,72 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7
8#include "common/common_types.h"
9#include "core/hle/service/time/clock_types.h"
10
11namespace Core {
12class System;
13}
14
15namespace Service::Time::Clock {
16
17class SteadyClockCore;
18class SystemClockContextUpdateCallback;
19
20// Parts of this implementation were based on Ryujinx (https://github.com/Ryujinx/Ryujinx/pull/783).
21// This code was released under public domain.
22
23class SystemClockCore {
24public:
25 explicit SystemClockCore(SteadyClockCore& steady_clock_core_);
26 virtual ~SystemClockCore();
27
28 SteadyClockCore& GetSteadyClockCore() const {
29 return steady_clock_core;
30 }
31
32 Result GetCurrentTime(Core::System& system, s64& posix_time) const;
33
34 Result SetCurrentTime(Core::System& system, s64 posix_time);
35
36 virtual Result GetClockContext([[maybe_unused]] Core::System& system,
37 SystemClockContext& value) const {
38 value = context;
39 return ResultSuccess;
40 }
41
42 virtual Result SetClockContext(const SystemClockContext& value) {
43 context = value;
44 return ResultSuccess;
45 }
46
47 virtual Result Flush(const SystemClockContext& clock_context);
48
49 void SetUpdateCallbackInstance(std::shared_ptr<SystemClockContextUpdateCallback> callback) {
50 system_clock_context_update_callback = std::move(callback);
51 }
52
53 Result SetSystemClockContext(const SystemClockContext& context);
54
55 bool IsInitialized() const {
56 return is_initialized;
57 }
58
59 void MarkAsInitialized() {
60 is_initialized = true;
61 }
62
63 bool IsClockSetup(Core::System& system) const;
64
65private:
66 SteadyClockCore& steady_clock_core;
67 SystemClockContext context{};
68 bool is_initialized{};
69 std::shared_ptr<SystemClockContextUpdateCallback> system_clock_context_update_callback;
70};
71
72} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.cpp b/src/core/hle/service/time/tick_based_steady_clock_core.cpp
deleted file mode 100644
index 0d9fb3143..000000000
--- a/src/core/hle/service/time/tick_based_steady_clock_core.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/core_timing.h"
6#include "core/hardware_properties.h"
7#include "core/hle/service/time/tick_based_steady_clock_core.h"
8
9namespace Service::Time::Clock {
10
11SteadyClockTimePoint TickBasedSteadyClockCore::GetTimePoint(Core::System& system) {
12 const TimeSpanType ticks_time_span{
13 TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>(system.CoreTiming().GetClockTicks())};
14
15 return {ticks_time_span.ToSeconds(), GetClockSourceId()};
16}
17
18TimeSpanType TickBasedSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) {
19 return TimeSpanType::FromSeconds(GetTimePoint(system).time_point);
20}
21
22} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.h b/src/core/hle/service/time/tick_based_steady_clock_core.h
deleted file mode 100644
index 491185dc3..000000000
--- a/src/core/hle/service/time/tick_based_steady_clock_core.h
+++ /dev/null
@@ -1,28 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/time/clock_types.h"
7#include "core/hle/service/time/steady_clock_core.h"
8
9namespace Core {
10class System;
11}
12
13namespace Service::Time::Clock {
14
15class TickBasedSteadyClockCore final : public SteadyClockCore {
16public:
17 TimeSpanType GetInternalOffset() const override {
18 return {};
19 }
20
21 void SetInternalOffset(TimeSpanType internal_offset) override {}
22
23 SteadyClockTimePoint GetTimePoint(Core::System& system) override;
24
25 TimeSpanType GetCurrentRawTimePoint(Core::System& system) override;
26};
27
28} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
deleted file mode 100644
index 7197ca30f..000000000
--- a/src/core/hle/service/time/time.cpp
+++ /dev/null
@@ -1,412 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "core/core.h"
6#include "core/core_timing.h"
7#include "core/hardware_properties.h"
8#include "core/hle/kernel/kernel.h"
9#include "core/hle/service/ipc_helpers.h"
10#include "core/hle/service/server_manager.h"
11#include "core/hle/service/time/time.h"
12#include "core/hle/service/time/time_interface.h"
13#include "core/hle/service/time/time_manager.h"
14#include "core/hle/service/time/time_sharedmemory.h"
15#include "core/hle/service/time/time_zone_service.h"
16
17namespace Service::Time {
18
19class ISystemClock final : public ServiceFramework<ISystemClock> {
20public:
21 explicit ISystemClock(Clock::SystemClockCore& clock_core_, Core::System& system_)
22 : ServiceFramework{system_, "ISystemClock"}, clock_core{clock_core_} {
23 // clang-format off
24 static const FunctionInfo functions[] = {
25 {0, &ISystemClock::GetCurrentTime, "GetCurrentTime"},
26 {1, nullptr, "SetCurrentTime"},
27 {2, &ISystemClock::GetSystemClockContext, "GetSystemClockContext"},
28 {3, nullptr, "SetSystemClockContext"},
29 {4, nullptr, "GetOperationEventReadableHandle"},
30 };
31 // clang-format on
32
33 RegisterHandlers(functions);
34 }
35
36private:
37 void GetCurrentTime(HLERequestContext& ctx) {
38 LOG_DEBUG(Service_Time, "called");
39
40 if (!clock_core.IsInitialized()) {
41 IPC::ResponseBuilder rb{ctx, 2};
42 rb.Push(ERROR_UNINITIALIZED_CLOCK);
43 return;
44 }
45
46 s64 posix_time{};
47 if (const Result result{clock_core.GetCurrentTime(system, posix_time)}; result.IsError()) {
48 IPC::ResponseBuilder rb{ctx, 2};
49 rb.Push(result);
50 return;
51 }
52
53 IPC::ResponseBuilder rb{ctx, 4};
54 rb.Push(ResultSuccess);
55 rb.Push<s64>(posix_time);
56 }
57
58 void GetSystemClockContext(HLERequestContext& ctx) {
59 LOG_DEBUG(Service_Time, "called");
60
61 if (!clock_core.IsInitialized()) {
62 IPC::ResponseBuilder rb{ctx, 2};
63 rb.Push(ERROR_UNINITIALIZED_CLOCK);
64 return;
65 }
66
67 Clock::SystemClockContext system_clock_context{};
68 if (const Result result{clock_core.GetClockContext(system, system_clock_context)};
69 result.IsError()) {
70 IPC::ResponseBuilder rb{ctx, 2};
71 rb.Push(result);
72 return;
73 }
74
75 IPC::ResponseBuilder rb{ctx, sizeof(Clock::SystemClockContext) / 4 + 2};
76 rb.Push(ResultSuccess);
77 rb.PushRaw(system_clock_context);
78 }
79
80 Clock::SystemClockCore& clock_core;
81};
82
83class ISteadyClock final : public ServiceFramework<ISteadyClock> {
84public:
85 explicit ISteadyClock(Clock::SteadyClockCore& clock_core_, Core::System& system_)
86 : ServiceFramework{system_, "ISteadyClock"}, clock_core{clock_core_} {
87 static const FunctionInfo functions[] = {
88 {0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"},
89 {2, nullptr, "GetTestOffset"},
90 {3, nullptr, "SetTestOffset"},
91 {100, nullptr, "GetRtcValue"},
92 {101, nullptr, "IsRtcResetDetected"},
93 {102, nullptr, "GetSetupResultValue"},
94 {200, nullptr, "GetInternalOffset"},
95 {201, nullptr, "SetInternalOffset"},
96 };
97 RegisterHandlers(functions);
98 }
99
100private:
101 void GetCurrentTimePoint(HLERequestContext& ctx) {
102 LOG_DEBUG(Service_Time, "called");
103
104 if (!clock_core.IsInitialized()) {
105 IPC::ResponseBuilder rb{ctx, 2};
106 rb.Push(ERROR_UNINITIALIZED_CLOCK);
107 return;
108 }
109
110 const Clock::SteadyClockTimePoint time_point{clock_core.GetCurrentTimePoint(system)};
111 IPC::ResponseBuilder rb{ctx, (sizeof(Clock::SteadyClockTimePoint) / 4) + 2};
112 rb.Push(ResultSuccess);
113 rb.PushRaw(time_point);
114 }
115
116 Clock::SteadyClockCore& clock_core;
117};
118
119Result Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
120 Kernel::KThread* thread, Clock::SystemClockContext user_context,
121 Clock::SystemClockContext network_context, Clock::TimeType type,
122 Clock::ClockSnapshot& clock_snapshot) {
123
124 auto& time_manager{system.GetTimeManager()};
125
126 clock_snapshot.steady_clock_time_point =
127 time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system);
128 clock_snapshot.is_automatic_correction_enabled =
129 time_manager.GetStandardUserSystemClockCore().IsAutomaticCorrectionEnabled();
130 clock_snapshot.type = type;
131
132 if (const Result result{
133 time_manager.GetTimeZoneContentManager().GetTimeZoneManager().GetDeviceLocationName(
134 clock_snapshot.location_name)};
135 result != ResultSuccess) {
136 return result;
137 }
138
139 clock_snapshot.user_context = user_context;
140
141 if (const Result result{Clock::ClockSnapshot::GetCurrentTime(
142 clock_snapshot.user_time, clock_snapshot.steady_clock_time_point,
143 clock_snapshot.user_context)};
144 result != ResultSuccess) {
145 return result;
146 }
147
148 TimeZone::CalendarInfo userCalendarInfo{};
149 if (const Result result{
150 time_manager.GetTimeZoneContentManager().GetTimeZoneManager().ToCalendarTimeWithMyRules(
151 clock_snapshot.user_time, userCalendarInfo)};
152 result != ResultSuccess) {
153 return result;
154 }
155
156 clock_snapshot.user_calendar_time = userCalendarInfo.time;
157 clock_snapshot.user_calendar_additional_time = userCalendarInfo.additional_info;
158
159 clock_snapshot.network_context = network_context;
160
161 if (Clock::ClockSnapshot::GetCurrentTime(clock_snapshot.network_time,
162 clock_snapshot.steady_clock_time_point,
163 clock_snapshot.network_context) != ResultSuccess) {
164 clock_snapshot.network_time = 0;
165 }
166
167 TimeZone::CalendarInfo networkCalendarInfo{};
168 if (const Result result{
169 time_manager.GetTimeZoneContentManager().GetTimeZoneManager().ToCalendarTimeWithMyRules(
170 clock_snapshot.network_time, networkCalendarInfo)};
171 result != ResultSuccess) {
172 return result;
173 }
174
175 clock_snapshot.network_calendar_time = networkCalendarInfo.time;
176 clock_snapshot.network_calendar_additional_time = networkCalendarInfo.additional_info;
177
178 return ResultSuccess;
179}
180
181void Module::Interface::GetStandardUserSystemClock(HLERequestContext& ctx) {
182 LOG_DEBUG(Service_Time, "called");
183 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
184 rb.Push(ResultSuccess);
185 rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardUserSystemClockCore(),
186 system);
187}
188
189void Module::Interface::GetStandardNetworkSystemClock(HLERequestContext& ctx) {
190 LOG_DEBUG(Service_Time, "called");
191 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
192 rb.Push(ResultSuccess);
193 rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardNetworkSystemClockCore(),
194 system);
195}
196
197void Module::Interface::GetStandardSteadyClock(HLERequestContext& ctx) {
198 LOG_DEBUG(Service_Time, "called");
199 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
200 rb.Push(ResultSuccess);
201 rb.PushIpcInterface<ISteadyClock>(system.GetTimeManager().GetStandardSteadyClockCore(), system);
202}
203
204void Module::Interface::GetTimeZoneService(HLERequestContext& ctx) {
205 LOG_DEBUG(Service_Time, "called");
206 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
207 rb.Push(ResultSuccess);
208 rb.PushIpcInterface<ITimeZoneService>(system,
209 system.GetTimeManager().GetTimeZoneContentManager());
210}
211
212void Module::Interface::GetStandardLocalSystemClock(HLERequestContext& ctx) {
213 LOG_DEBUG(Service_Time, "called");
214 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
215 rb.Push(ResultSuccess);
216 rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardLocalSystemClockCore(),
217 system);
218}
219
220void Module::Interface::IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx) {
221 LOG_DEBUG(Service_Time, "called");
222 auto& clock_core{system.GetTimeManager().GetStandardNetworkSystemClockCore()};
223 IPC::ResponseBuilder rb{ctx, 3};
224 rb.Push(ResultSuccess);
225 rb.Push<u32>(clock_core.IsStandardNetworkSystemClockAccuracySufficient(system));
226}
227
228void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx) {
229 LOG_DEBUG(Service_Time, "called");
230
231 auto& steady_clock_core{system.GetTimeManager().GetStandardSteadyClockCore()};
232 if (!steady_clock_core.IsInitialized()) {
233 IPC::ResponseBuilder rb{ctx, 2};
234 rb.Push(ERROR_UNINITIALIZED_CLOCK);
235 return;
236 }
237
238 IPC::RequestParser rp{ctx};
239 const auto context{rp.PopRaw<Clock::SystemClockContext>()};
240 const auto current_time_point{steady_clock_core.GetCurrentTimePoint(system)};
241
242 if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) {
243 const auto ticks{Clock::TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>(
244 system.CoreTiming().GetClockTicks())};
245 const s64 base_time_point{context.offset + current_time_point.time_point -
246 ticks.ToSeconds()};
247 IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2};
248 rb.Push(ResultSuccess);
249 rb.PushRaw(base_time_point);
250 return;
251 }
252
253 IPC::ResponseBuilder rb{ctx, 2};
254 rb.Push(ERROR_TIME_MISMATCH);
255}
256
257void Module::Interface::GetClockSnapshot(HLERequestContext& ctx) {
258 IPC::RequestParser rp{ctx};
259 const auto type{rp.PopEnum<Clock::TimeType>()};
260
261 LOG_DEBUG(Service_Time, "called, type={}", type);
262
263 Clock::SystemClockContext user_context{};
264 if (const Result result{
265 system.GetTimeManager().GetStandardUserSystemClockCore().GetClockContext(system,
266 user_context)};
267 result.IsError()) {
268 IPC::ResponseBuilder rb{ctx, 2};
269 rb.Push(result);
270 return;
271 }
272
273 Clock::SystemClockContext network_context{};
274 if (const Result result{
275 system.GetTimeManager().GetStandardNetworkSystemClockCore().GetClockContext(
276 system, network_context)};
277 result.IsError()) {
278 IPC::ResponseBuilder rb{ctx, 2};
279 rb.Push(result);
280 return;
281 }
282
283 Clock::ClockSnapshot clock_snapshot{};
284 if (const Result result{GetClockSnapshotFromSystemClockContextInternal(
285 &ctx.GetThread(), user_context, network_context, type, clock_snapshot)};
286 result.IsError()) {
287 IPC::ResponseBuilder rb{ctx, 2};
288 rb.Push(result);
289 return;
290 }
291
292 ctx.WriteBuffer(clock_snapshot);
293
294 IPC::ResponseBuilder rb{ctx, 2};
295 rb.Push(ResultSuccess);
296}
297
298void Module::Interface::GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx) {
299 IPC::RequestParser rp{ctx};
300 const auto type{rp.PopEnum<Clock::TimeType>()};
301
302 rp.Skip(1, false);
303
304 const Clock::SystemClockContext user_context{rp.PopRaw<Clock::SystemClockContext>()};
305 const Clock::SystemClockContext network_context{rp.PopRaw<Clock::SystemClockContext>()};
306
307 LOG_DEBUG(Service_Time, "called, type={}", type);
308
309 Clock::ClockSnapshot clock_snapshot{};
310 if (const Result result{GetClockSnapshotFromSystemClockContextInternal(
311 &ctx.GetThread(), user_context, network_context, type, clock_snapshot)};
312 result != ResultSuccess) {
313 IPC::ResponseBuilder rb{ctx, 2};
314 rb.Push(result);
315 return;
316 }
317
318 ctx.WriteBuffer(clock_snapshot);
319
320 IPC::ResponseBuilder rb{ctx, 2};
321 rb.Push(ResultSuccess);
322}
323
324void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser(HLERequestContext& ctx) {
325 LOG_DEBUG(Service_Time, "called");
326
327 Clock::ClockSnapshot snapshot_a;
328 Clock::ClockSnapshot snapshot_b;
329
330 const auto snapshot_a_data = ctx.ReadBuffer(0);
331 const auto snapshot_b_data = ctx.ReadBuffer(1);
332
333 std::memcpy(&snapshot_a, snapshot_a_data.data(), sizeof(Clock::ClockSnapshot));
334 std::memcpy(&snapshot_b, snapshot_b_data.data(), sizeof(Clock::ClockSnapshot));
335
336 auto time_span_type{Clock::TimeSpanType::FromSeconds(snapshot_b.user_context.offset -
337 snapshot_a.user_context.offset)};
338
339 if ((snapshot_b.user_context.steady_time_point.clock_source_id !=
340 snapshot_a.user_context.steady_time_point.clock_source_id) ||
341 (snapshot_b.is_automatic_correction_enabled &&
342 snapshot_a.is_automatic_correction_enabled)) {
343 time_span_type.nanoseconds = 0;
344 }
345
346 IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2};
347 rb.Push(ResultSuccess);
348 rb.PushRaw(time_span_type.nanoseconds);
349}
350
351void Module::Interface::CalculateSpanBetween(HLERequestContext& ctx) {
352 LOG_DEBUG(Service_Time, "called");
353
354 Clock::ClockSnapshot snapshot_a;
355 Clock::ClockSnapshot snapshot_b;
356
357 const auto snapshot_a_data = ctx.ReadBuffer(0);
358 const auto snapshot_b_data = ctx.ReadBuffer(1);
359
360 std::memcpy(&snapshot_a, snapshot_a_data.data(), sizeof(Clock::ClockSnapshot));
361 std::memcpy(&snapshot_b, snapshot_b_data.data(), sizeof(Clock::ClockSnapshot));
362
363 Clock::TimeSpanType time_span_type{};
364 s64 span{};
365
366 if (const Result result{snapshot_a.steady_clock_time_point.GetSpanBetween(
367 snapshot_b.steady_clock_time_point, span)};
368 result != ResultSuccess) {
369 if (snapshot_a.network_time && snapshot_b.network_time) {
370 time_span_type =
371 Clock::TimeSpanType::FromSeconds(snapshot_b.network_time - snapshot_a.network_time);
372 } else {
373 IPC::ResponseBuilder rb{ctx, 2};
374 rb.Push(ERROR_TIME_NOT_FOUND);
375 return;
376 }
377 } else {
378 time_span_type = Clock::TimeSpanType::FromSeconds(span);
379 }
380
381 IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2};
382 rb.Push(ResultSuccess);
383 rb.PushRaw(time_span_type.nanoseconds);
384}
385
386void Module::Interface::GetSharedMemoryNativeHandle(HLERequestContext& ctx) {
387 LOG_DEBUG(Service_Time, "called");
388 IPC::ResponseBuilder rb{ctx, 2, 1};
389 rb.Push(ResultSuccess);
390 rb.PushCopyObjects(&system.Kernel().GetTimeSharedMem());
391}
392
393Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_,
394 const char* name)
395 : ServiceFramework{system_, name}, module{std::move(module_)} {}
396
397Module::Interface::~Interface() = default;
398
399void LoopProcess(Core::System& system) {
400 auto server_manager = std::make_unique<ServerManager>(system);
401 auto module{std::make_shared<Module>()};
402
403 server_manager->RegisterNamedService("time:a",
404 std::make_shared<Time>(module, system, "time:a"));
405 server_manager->RegisterNamedService("time:s",
406 std::make_shared<Time>(module, system, "time:s"));
407 server_manager->RegisterNamedService("time:u",
408 std::make_shared<Time>(module, system, "time:u"));
409 ServerManager::RunServer(std::move(server_manager));
410}
411
412} // namespace Service::Time
diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h
deleted file mode 100644
index b2d754ef3..000000000
--- a/src/core/hle/service/time/time.h
+++ /dev/null
@@ -1,51 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7#include "core/hle/service/time/clock_types.h"
8
9namespace Core {
10class System;
11}
12
13namespace Service::Time {
14
15class Module final {
16public:
17 Module() = default;
18
19 class Interface : public ServiceFramework<Interface> {
20 public:
21 explicit Interface(std::shared_ptr<Module> module_, Core::System& system_,
22 const char* name);
23 ~Interface() override;
24
25 void GetStandardUserSystemClock(HLERequestContext& ctx);
26 void GetStandardNetworkSystemClock(HLERequestContext& ctx);
27 void GetStandardSteadyClock(HLERequestContext& ctx);
28 void GetTimeZoneService(HLERequestContext& ctx);
29 void GetStandardLocalSystemClock(HLERequestContext& ctx);
30 void IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx);
31 void CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx);
32 void GetClockSnapshot(HLERequestContext& ctx);
33 void GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx);
34 void CalculateStandardUserSystemClockDifferenceByUser(HLERequestContext& ctx);
35 void CalculateSpanBetween(HLERequestContext& ctx);
36 void GetSharedMemoryNativeHandle(HLERequestContext& ctx);
37
38 private:
39 Result GetClockSnapshotFromSystemClockContextInternal(
40 Kernel::KThread* thread, Clock::SystemClockContext user_context,
41 Clock::SystemClockContext network_context, Clock::TimeType type,
42 Clock::ClockSnapshot& cloc_snapshot);
43
44 protected:
45 std::shared_ptr<Module> module;
46 };
47};
48
49void LoopProcess(Core::System& system);
50
51} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_interface.cpp b/src/core/hle/service/time/time_interface.cpp
deleted file mode 100644
index 0c53e98ee..000000000
--- a/src/core/hle/service/time/time_interface.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/time/time_interface.h"
5
6namespace Service::Time {
7
8Time::Time(std::shared_ptr<Module> module_, Core::System& system_, const char* name_)
9 : Interface{std::move(module_), system_, name_} {
10 // clang-format off
11 static const FunctionInfo functions[] = {
12 {0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"},
13 {1, &Time::GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"},
14 {2, &Time::GetStandardSteadyClock, "GetStandardSteadyClock"},
15 {3, &Time::GetTimeZoneService, "GetTimeZoneService"},
16 {4, &Time::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"},
17 {5, nullptr, "GetEphemeralNetworkSystemClock"},
18 {20, &Time::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"},
19 {30, nullptr, "GetStandardNetworkClockOperationEventReadableHandle"},
20 {31, nullptr, "GetEphemeralNetworkClockOperationEventReadableHandle"},
21 {50, nullptr, "SetStandardSteadyClockInternalOffset"},
22 {51, nullptr, "GetStandardSteadyClockRtcValue"},
23 {100, nullptr, "IsStandardUserSystemClockAutomaticCorrectionEnabled"},
24 {101, nullptr, "SetStandardUserSystemClockAutomaticCorrectionEnabled"},
25 {102, nullptr, "GetStandardUserSystemClockInitialYear"},
26 {200, &Time::IsStandardNetworkSystemClockAccuracySufficient, "IsStandardNetworkSystemClockAccuracySufficient"},
27 {201, nullptr, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"},
28 {300, &Time::CalculateMonotonicSystemClockBaseTimePoint, "CalculateMonotonicSystemClockBaseTimePoint"},
29 {400, &Time::GetClockSnapshot, "GetClockSnapshot"},
30 {401, &Time::GetClockSnapshotFromSystemClockContext, "GetClockSnapshotFromSystemClockContext"},
31 {500, &Time::CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"},
32 {501, &Time::CalculateSpanBetween, "CalculateSpanBetween"},
33 };
34 // clang-format on
35
36 RegisterHandlers(functions);
37}
38
39Time::~Time() = default;
40
41} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_interface.h b/src/core/hle/service/time/time_interface.h
deleted file mode 100644
index ceeb0e5ef..000000000
--- a/src/core/hle/service/time/time_interface.h
+++ /dev/null
@@ -1,20 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/time/time.h"
7
8namespace Core {
9class System;
10}
11
12namespace Service::Time {
13
14class Time final : public Module::Interface {
15public:
16 explicit Time(std::shared_ptr<Module> time, Core::System& system_, const char* name_);
17 ~Time() override;
18};
19
20} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_manager.cpp b/src/core/hle/service/time/time_manager.cpp
deleted file mode 100644
index fa0fd0531..000000000
--- a/src/core/hle/service/time/time_manager.cpp
+++ /dev/null
@@ -1,293 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <chrono>
5#include <ctime>
6
7#include "common/settings.h"
8#include "common/time_zone.h"
9#include "core/hle/service/time/ephemeral_network_system_clock_context_writer.h"
10#include "core/hle/service/time/ephemeral_network_system_clock_core.h"
11#include "core/hle/service/time/local_system_clock_context_writer.h"
12#include "core/hle/service/time/network_system_clock_context_writer.h"
13#include "core/hle/service/time/tick_based_steady_clock_core.h"
14#include "core/hle/service/time/time_manager.h"
15
16namespace Service::Time {
17namespace {
18constexpr Clock::TimeSpanType standard_network_clock_accuracy{0x0009356907420000ULL};
19
20s64 GetSecondsSinceEpoch() {
21 const auto time_since_epoch = std::chrono::system_clock::now().time_since_epoch();
22 return std::chrono::duration_cast<std::chrono::seconds>(time_since_epoch).count() +
23 Settings::values.custom_rtc_differential;
24}
25} // Anonymous namespace
26
27struct TimeManager::Impl final {
28 explicit Impl(Core::System& system)
29 : shared_memory{system}, standard_local_system_clock_core{standard_steady_clock_core},
30 standard_network_system_clock_core{standard_steady_clock_core},
31 standard_user_system_clock_core{standard_local_system_clock_core,
32 standard_network_system_clock_core, system},
33 ephemeral_network_system_clock_core{tick_based_steady_clock_core},
34 local_system_clock_context_writer{
35 std::make_shared<Clock::LocalSystemClockContextWriter>(shared_memory)},
36 network_system_clock_context_writer{
37 std::make_shared<Clock::NetworkSystemClockContextWriter>(shared_memory)},
38 ephemeral_network_system_clock_context_writer{
39 std::make_shared<Clock::EphemeralNetworkSystemClockContextWriter>()},
40 time_zone_content_manager{system} {
41
42 const auto system_time{Clock::TimeSpanType::FromSeconds(GetSecondsSinceEpoch())};
43 SetupStandardSteadyClock(system, Common::UUID::MakeRandom(), system_time, {}, {});
44 SetupStandardLocalSystemClock(system, {}, system_time.ToSeconds());
45
46 Clock::SystemClockContext clock_context{};
47 standard_local_system_clock_core.GetClockContext(system, clock_context);
48
49 SetupStandardNetworkSystemClock(clock_context, standard_network_clock_accuracy);
50 SetupStandardUserSystemClock(system, {}, Clock::SteadyClockTimePoint::GetRandom());
51 SetupEphemeralNetworkSystemClock();
52 }
53
54 ~Impl() = default;
55
56 Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() {
57 return standard_steady_clock_core;
58 }
59
60 const Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() const {
61 return standard_steady_clock_core;
62 }
63
64 Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() {
65 return standard_local_system_clock_core;
66 }
67
68 const Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() const {
69 return standard_local_system_clock_core;
70 }
71
72 Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() {
73 return standard_network_system_clock_core;
74 }
75
76 const Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() const {
77 return standard_network_system_clock_core;
78 }
79
80 Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() {
81 return standard_user_system_clock_core;
82 }
83
84 const Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() const {
85 return standard_user_system_clock_core;
86 }
87
88 TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() {
89 return time_zone_content_manager;
90 }
91
92 const TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() const {
93 return time_zone_content_manager;
94 }
95
96 SharedMemory& GetSharedMemory() {
97 return shared_memory;
98 }
99
100 const SharedMemory& GetSharedMemory() const {
101 return shared_memory;
102 }
103
104 void SetupTimeZoneManager(std::string location_name,
105 Clock::SteadyClockTimePoint time_zone_updated_time_point,
106 std::vector<std::string> location_names, u128 time_zone_rule_version,
107 FileSys::VirtualFile& vfs_file) {
108 if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule(
109 location_name, vfs_file) != ResultSuccess) {
110 ASSERT(false);
111 return;
112 }
113
114 time_zone_content_manager.GetTimeZoneManager().SetUpdatedTime(time_zone_updated_time_point);
115 time_zone_content_manager.GetTimeZoneManager().SetTotalLocationNameCount(
116 location_names.size());
117 time_zone_content_manager.GetTimeZoneManager().SetLocationNames(location_names);
118 time_zone_content_manager.GetTimeZoneManager().SetTimeZoneRuleVersion(
119 time_zone_rule_version);
120 time_zone_content_manager.GetTimeZoneManager().MarkAsInitialized();
121 }
122
123 void SetupStandardSteadyClock(Core::System& system_, Common::UUID clock_source_id,
124 Clock::TimeSpanType setup_value,
125 Clock::TimeSpanType internal_offset, bool is_rtc_reset_detected) {
126 standard_steady_clock_core.SetClockSourceId(clock_source_id);
127 standard_steady_clock_core.SetSetupValue(setup_value);
128 standard_steady_clock_core.SetInternalOffset(internal_offset);
129 standard_steady_clock_core.MarkAsInitialized();
130
131 const auto current_time_point{standard_steady_clock_core.GetCurrentRawTimePoint(system_)};
132 shared_memory.SetupStandardSteadyClock(clock_source_id, current_time_point);
133 }
134
135 void SetupStandardLocalSystemClock(Core::System& system_,
136 Clock::SystemClockContext clock_context, s64 posix_time) {
137 standard_local_system_clock_core.SetUpdateCallbackInstance(
138 local_system_clock_context_writer);
139
140 const auto current_time_point{
141 standard_local_system_clock_core.GetSteadyClockCore().GetCurrentTimePoint(system_)};
142 if (current_time_point.clock_source_id == clock_context.steady_time_point.clock_source_id) {
143 standard_local_system_clock_core.SetSystemClockContext(clock_context);
144 } else {
145 if (standard_local_system_clock_core.SetCurrentTime(system_, posix_time) !=
146 ResultSuccess) {
147 ASSERT(false);
148 return;
149 }
150 }
151
152 standard_local_system_clock_core.MarkAsInitialized();
153 }
154
155 void SetupStandardNetworkSystemClock(Clock::SystemClockContext clock_context,
156 Clock::TimeSpanType sufficient_accuracy) {
157 standard_network_system_clock_core.SetUpdateCallbackInstance(
158 network_system_clock_context_writer);
159
160 if (standard_network_system_clock_core.SetSystemClockContext(clock_context) !=
161 ResultSuccess) {
162 ASSERT(false);
163 return;
164 }
165
166 standard_network_system_clock_core.SetStandardNetworkClockSufficientAccuracy(
167 sufficient_accuracy);
168 standard_network_system_clock_core.MarkAsInitialized();
169 }
170
171 void SetupStandardUserSystemClock(Core::System& system_, bool is_automatic_correction_enabled,
172 Clock::SteadyClockTimePoint steady_clock_time_point) {
173 if (standard_user_system_clock_core.SetAutomaticCorrectionEnabled(
174 system_, is_automatic_correction_enabled) != ResultSuccess) {
175 ASSERT(false);
176 return;
177 }
178
179 standard_user_system_clock_core.SetAutomaticCorrectionUpdatedTime(steady_clock_time_point);
180 standard_user_system_clock_core.MarkAsInitialized();
181 shared_memory.SetAutomaticCorrectionEnabled(is_automatic_correction_enabled);
182 }
183
184 void SetupEphemeralNetworkSystemClock() {
185 ephemeral_network_system_clock_core.SetUpdateCallbackInstance(
186 ephemeral_network_system_clock_context_writer);
187 ephemeral_network_system_clock_core.MarkAsInitialized();
188 }
189
190 void UpdateLocalSystemClockTime(Core::System& system_, s64 posix_time) {
191 const auto timespan{Clock::TimeSpanType::FromSeconds(posix_time)};
192 if (GetStandardLocalSystemClockCore()
193 .SetCurrentTime(system_, timespan.ToSeconds())
194 .IsError()) {
195 ASSERT(false);
196 return;
197 }
198 }
199
200 SharedMemory shared_memory;
201
202 Clock::StandardSteadyClockCore standard_steady_clock_core;
203 Clock::TickBasedSteadyClockCore tick_based_steady_clock_core;
204 Clock::StandardLocalSystemClockCore standard_local_system_clock_core;
205 Clock::StandardNetworkSystemClockCore standard_network_system_clock_core;
206 Clock::StandardUserSystemClockCore standard_user_system_clock_core;
207 Clock::EphemeralNetworkSystemClockCore ephemeral_network_system_clock_core;
208
209 std::shared_ptr<Clock::LocalSystemClockContextWriter> local_system_clock_context_writer;
210 std::shared_ptr<Clock::NetworkSystemClockContextWriter> network_system_clock_context_writer;
211 std::shared_ptr<Clock::EphemeralNetworkSystemClockContextWriter>
212 ephemeral_network_system_clock_context_writer;
213
214 TimeZone::TimeZoneContentManager time_zone_content_manager;
215};
216
217TimeManager::TimeManager(Core::System& system_) : system{system_} {}
218
219TimeManager::~TimeManager() = default;
220
221void TimeManager::Initialize() {
222 impl = std::make_unique<Impl>(system);
223
224 // Time zones can only be initialized after impl is valid
225 impl->time_zone_content_manager.Initialize(*this);
226}
227
228Clock::StandardSteadyClockCore& TimeManager::GetStandardSteadyClockCore() {
229 return impl->standard_steady_clock_core;
230}
231
232const Clock::StandardSteadyClockCore& TimeManager::GetStandardSteadyClockCore() const {
233 return impl->standard_steady_clock_core;
234}
235
236Clock::StandardLocalSystemClockCore& TimeManager::GetStandardLocalSystemClockCore() {
237 return impl->standard_local_system_clock_core;
238}
239
240const Clock::StandardLocalSystemClockCore& TimeManager::GetStandardLocalSystemClockCore() const {
241 return impl->standard_local_system_clock_core;
242}
243
244Clock::StandardNetworkSystemClockCore& TimeManager::GetStandardNetworkSystemClockCore() {
245 return impl->standard_network_system_clock_core;
246}
247
248const Clock::StandardNetworkSystemClockCore& TimeManager::GetStandardNetworkSystemClockCore()
249 const {
250 return impl->standard_network_system_clock_core;
251}
252
253Clock::StandardUserSystemClockCore& TimeManager::GetStandardUserSystemClockCore() {
254 return impl->standard_user_system_clock_core;
255}
256
257const Clock::StandardUserSystemClockCore& TimeManager::GetStandardUserSystemClockCore() const {
258 return impl->standard_user_system_clock_core;
259}
260
261TimeZone::TimeZoneContentManager& TimeManager::GetTimeZoneContentManager() {
262 return impl->time_zone_content_manager;
263}
264
265const TimeZone::TimeZoneContentManager& TimeManager::GetTimeZoneContentManager() const {
266 return impl->time_zone_content_manager;
267}
268
269SharedMemory& TimeManager::GetSharedMemory() {
270 return impl->shared_memory;
271}
272
273const SharedMemory& TimeManager::GetSharedMemory() const {
274 return impl->shared_memory;
275}
276
277void TimeManager::Shutdown() {
278 impl.reset();
279}
280
281void TimeManager::UpdateLocalSystemClockTime(s64 posix_time) {
282 impl->UpdateLocalSystemClockTime(system, posix_time);
283}
284
285void TimeManager::SetupTimeZoneManager(std::string location_name,
286 Clock::SteadyClockTimePoint time_zone_updated_time_point,
287 std::vector<std::string> location_names,
288 u128 time_zone_rule_version,
289 FileSys::VirtualFile& vfs_file) {
290 impl->SetupTimeZoneManager(location_name, time_zone_updated_time_point, location_names,
291 time_zone_rule_version, vfs_file);
292}
293} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_manager.h b/src/core/hle/service/time/time_manager.h
deleted file mode 100644
index 84572dbfa..000000000
--- a/src/core/hle/service/time/time_manager.h
+++ /dev/null
@@ -1,74 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/file_sys/vfs_types.h"
8#include "core/hle/service/time/clock_types.h"
9#include "core/hle/service/time/standard_local_system_clock_core.h"
10#include "core/hle/service/time/standard_network_system_clock_core.h"
11#include "core/hle/service/time/standard_steady_clock_core.h"
12#include "core/hle/service/time/standard_user_system_clock_core.h"
13#include "core/hle/service/time/time_sharedmemory.h"
14#include "core/hle/service/time/time_zone_content_manager.h"
15
16namespace Service::Time {
17
18namespace Clock {
19class EphemeralNetworkSystemClockContextWriter;
20class LocalSystemClockContextWriter;
21class NetworkSystemClockContextWriter;
22} // namespace Clock
23
24// Parts of this implementation were based on Ryujinx (https://github.com/Ryujinx/Ryujinx/pull/783).
25// This code was released under public domain.
26
27class TimeManager final {
28public:
29 explicit TimeManager(Core::System& system_);
30 ~TimeManager();
31
32 void Initialize();
33
34 Clock::StandardSteadyClockCore& GetStandardSteadyClockCore();
35
36 const Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() const;
37
38 Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore();
39
40 const Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() const;
41
42 Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore();
43
44 const Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() const;
45
46 Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore();
47
48 const Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() const;
49
50 TimeZone::TimeZoneContentManager& GetTimeZoneContentManager();
51
52 const TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() const;
53
54 void UpdateLocalSystemClockTime(s64 posix_time);
55
56 SharedMemory& GetSharedMemory();
57
58 const SharedMemory& GetSharedMemory() const;
59
60 void Shutdown();
61
62 void SetupTimeZoneManager(std::string location_name,
63 Clock::SteadyClockTimePoint time_zone_updated_time_point,
64 std::vector<std::string> location_names, u128 time_zone_rule_version,
65 FileSys::VirtualFile& vfs_file);
66
67private:
68 Core::System& system;
69
70 struct Impl;
71 std::unique_ptr<Impl> impl;
72};
73
74} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp
deleted file mode 100644
index a00676669..000000000
--- a/src/core/hle/service/time/time_sharedmemory.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/core_timing.h"
6#include "core/hardware_properties.h"
7#include "core/hle/kernel/kernel.h"
8#include "core/hle/service/time/clock_types.h"
9#include "core/hle/service/time/steady_clock_core.h"
10#include "core/hle/service/time/time_sharedmemory.h"
11
12namespace Service::Time {
13
14static constexpr std::size_t SHARED_MEMORY_SIZE{0x1000};
15
16SharedMemory::SharedMemory(Core::System& system_) : system(system_) {
17 std::memset(system.Kernel().GetTimeSharedMem().GetPointer(), 0, SHARED_MEMORY_SIZE);
18}
19
20SharedMemory::~SharedMemory() = default;
21
22void SharedMemory::SetupStandardSteadyClock(const Common::UUID& clock_source_id,
23 Clock::TimeSpanType current_time_point) {
24 const Clock::TimeSpanType ticks_time_span{
25 Clock::TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>(
26 system.CoreTiming().GetClockTicks())};
27 const Clock::SteadyClockContext context{
28 static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds),
29 clock_source_id};
30 StoreToLockFreeAtomicType(&GetFormat()->standard_steady_clock_timepoint, context);
31}
32
33void SharedMemory::UpdateLocalSystemClockContext(const Clock::SystemClockContext& context) {
34 // lower and upper are related to the measurement point for the steady time point,
35 // and compare equal on boot
36 const s64 time_point_ns = context.steady_time_point.time_point * 1'000'000'000LL;
37
38 // This adjusts for some sort of time skew
39 // Both 0 on boot
40 const s64 diff_scale = 0;
41 const u32 shift_amount = 0;
42
43 const Clock::ContinuousAdjustmentTimePoint adjustment{
44 .measurement_offset = system.CoreTiming().GetGlobalTimeNs().count(),
45 .diff_scale = diff_scale,
46 .shift_amount = shift_amount,
47 .lower = time_point_ns,
48 .upper = time_point_ns,
49 .clock_source_id = context.steady_time_point.clock_source_id,
50 };
51
52 StoreToLockFreeAtomicType(&GetFormat()->continuous_adjustment_timepoint, adjustment);
53 StoreToLockFreeAtomicType(&GetFormat()->standard_local_system_clock_context, context);
54}
55
56void SharedMemory::UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context) {
57 StoreToLockFreeAtomicType(&GetFormat()->standard_network_system_clock_context, context);
58}
59
60void SharedMemory::SetAutomaticCorrectionEnabled(bool is_enabled) {
61 StoreToLockFreeAtomicType(
62 &GetFormat()->is_standard_user_system_clock_automatic_correction_enabled, is_enabled);
63}
64
65SharedMemory::Format* SharedMemory::GetFormat() {
66 return reinterpret_cast<SharedMemory::Format*>(system.Kernel().GetTimeSharedMem().GetPointer());
67}
68
69} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_sharedmemory.h b/src/core/hle/service/time/time_sharedmemory.h
deleted file mode 100644
index c89be9765..000000000
--- a/src/core/hle/service/time/time_sharedmemory.h
+++ /dev/null
@@ -1,89 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "common/uuid.h"
8#include "core/hle/kernel/k_shared_memory.h"
9#include "core/hle/service/time/clock_types.h"
10
11namespace Service::Time {
12
13// Note: this type is not safe for concurrent writes.
14template <typename T>
15struct LockFreeAtomicType {
16 u32 counter_;
17 std::array<T, 2> value_;
18};
19
20template <typename T>
21static inline void StoreToLockFreeAtomicType(LockFreeAtomicType<T>* p, const T& value) {
22 // Get the current counter.
23 auto counter = p->counter_;
24
25 // Increment the counter.
26 ++counter;
27
28 // Store the updated value.
29 p->value_[counter % 2] = value;
30
31 // Fence memory.
32 std::atomic_thread_fence(std::memory_order_release);
33
34 // Set the updated counter.
35 p->counter_ = counter;
36}
37
38template <typename T>
39static inline T LoadFromLockFreeAtomicType(const LockFreeAtomicType<T>* p) {
40 while (true) {
41 // Get the counter.
42 auto counter = p->counter_;
43
44 // Get the value.
45 auto value = p->value_[counter % 2];
46
47 // Fence memory.
48 std::atomic_thread_fence(std::memory_order_acquire);
49
50 // Check that the counter matches.
51 if (counter == p->counter_) {
52 return value;
53 }
54 }
55}
56
57class SharedMemory final {
58public:
59 explicit SharedMemory(Core::System& system_);
60 ~SharedMemory();
61
62 // Shared memory format
63 struct Format {
64 LockFreeAtomicType<Clock::StandardSteadyClockTimePointType> standard_steady_clock_timepoint;
65 LockFreeAtomicType<Clock::SystemClockContext> standard_local_system_clock_context;
66 LockFreeAtomicType<Clock::SystemClockContext> standard_network_system_clock_context;
67 LockFreeAtomicType<bool> is_standard_user_system_clock_automatic_correction_enabled;
68 LockFreeAtomicType<Clock::ContinuousAdjustmentTimePoint> continuous_adjustment_timepoint;
69 };
70 static_assert(offsetof(Format, standard_steady_clock_timepoint) == 0x0);
71 static_assert(offsetof(Format, standard_local_system_clock_context) == 0x38);
72 static_assert(offsetof(Format, standard_network_system_clock_context) == 0x80);
73 static_assert(offsetof(Format, is_standard_user_system_clock_automatic_correction_enabled) ==
74 0xc8);
75 static_assert(offsetof(Format, continuous_adjustment_timepoint) == 0xd0);
76 static_assert(sizeof(Format) == 0x148, "Format is an invalid size");
77
78 void SetupStandardSteadyClock(const Common::UUID& clock_source_id,
79 Clock::TimeSpanType current_time_point);
80 void UpdateLocalSystemClockContext(const Clock::SystemClockContext& context);
81 void UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context);
82 void SetAutomaticCorrectionEnabled(bool is_enabled);
83 Format* GetFormat();
84
85private:
86 Core::System& system;
87};
88
89} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_zone_content_manager.cpp b/src/core/hle/service/time/time_zone_content_manager.cpp
deleted file mode 100644
index 1b96de37a..000000000
--- a/src/core/hle/service/time/time_zone_content_manager.cpp
+++ /dev/null
@@ -1,151 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <chrono>
5#include <sstream>
6#include <utility>
7
8#include "common/logging/log.h"
9#include "common/settings.h"
10#include "common/time_zone.h"
11#include "core/core.h"
12#include "core/file_sys/content_archive.h"
13#include "core/file_sys/nca_metadata.h"
14#include "core/file_sys/registered_cache.h"
15#include "core/file_sys/romfs.h"
16#include "core/file_sys/system_archive/system_archive.h"
17#include "core/file_sys/vfs.h"
18#include "core/file_sys/vfs_types.h"
19#include "core/hle/result.h"
20#include "core/hle/service/filesystem/filesystem.h"
21#include "core/hle/service/time/errors.h"
22#include "core/hle/service/time/time_manager.h"
23#include "core/hle/service/time/time_zone_content_manager.h"
24
25namespace Service::Time::TimeZone {
26
27constexpr u64 time_zone_binary_titleid{0x010000000000080E};
28
29static FileSys::VirtualDir GetTimeZoneBinary(Core::System& system) {
30 const auto* nand{system.GetFileSystemController().GetSystemNANDContents()};
31 const auto nca{nand->GetEntry(time_zone_binary_titleid, FileSys::ContentRecordType::Data)};
32
33 FileSys::VirtualFile romfs;
34 if (nca) {
35 romfs = nca->GetRomFS();
36 }
37
38 if (!romfs) {
39 romfs = FileSys::SystemArchive::SynthesizeSystemArchive(time_zone_binary_titleid);
40 }
41
42 if (!romfs) {
43 LOG_ERROR(Service_Time, "Failed to find or synthesize {:016X!}", time_zone_binary_titleid);
44 return {};
45 }
46
47 return FileSys::ExtractRomFS(romfs);
48}
49
50static std::vector<std::string> BuildLocationNameCache(
51 const FileSys::VirtualDir& time_zone_binary) {
52 if (!time_zone_binary) {
53 LOG_ERROR(Service_Time, "Failed to extract RomFS for {:016X}!", time_zone_binary_titleid);
54 return {};
55 }
56
57 const FileSys::VirtualFile binary_list{time_zone_binary->GetFile("binaryList.txt")};
58 if (!binary_list) {
59 LOG_ERROR(Service_Time, "{:016X} has no file binaryList.txt!", time_zone_binary_titleid);
60 return {};
61 }
62
63 std::vector<char> raw_data(binary_list->GetSize() + 1);
64 binary_list->ReadBytes<char>(raw_data.data(), binary_list->GetSize());
65
66 std::stringstream data_stream{raw_data.data()};
67 std::string name;
68 std::vector<std::string> location_name_cache;
69 while (std::getline(data_stream, name)) {
70 name.pop_back(); // Remove carriage return
71 location_name_cache.emplace_back(std::move(name));
72 }
73 return location_name_cache;
74}
75
76TimeZoneContentManager::TimeZoneContentManager(Core::System& system_)
77 : system{system_}, time_zone_binary{GetTimeZoneBinary(system)},
78 location_name_cache{BuildLocationNameCache(time_zone_binary)} {}
79
80void TimeZoneContentManager::Initialize(TimeManager& time_manager) {
81 const auto timezone_setting =
82 Settings::GetTimeZoneString(Settings::values.time_zone_index.GetValue());
83
84 if (FileSys::VirtualFile vfs_file;
85 GetTimeZoneInfoFile(timezone_setting, vfs_file) == ResultSuccess) {
86 const auto time_point{
87 time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system)};
88 time_manager.SetupTimeZoneManager(timezone_setting, time_point, location_name_cache, {},
89 vfs_file);
90 } else {
91 time_zone_manager.MarkAsInitialized();
92 }
93}
94
95Result TimeZoneContentManager::LoadTimeZoneRule(TimeZoneRule& rules,
96 const std::string& location_name) const {
97 FileSys::VirtualFile vfs_file;
98 if (const Result result{GetTimeZoneInfoFile(location_name, vfs_file)};
99 result != ResultSuccess) {
100 return result;
101 }
102
103 return time_zone_manager.ParseTimeZoneRuleBinary(rules, vfs_file);
104}
105
106bool TimeZoneContentManager::IsLocationNameValid(const std::string& location_name) const {
107 return std::find(location_name_cache.begin(), location_name_cache.end(), location_name) !=
108 location_name_cache.end();
109}
110
111Result TimeZoneContentManager::GetTimeZoneInfoFile(const std::string& location_name,
112 FileSys::VirtualFile& vfs_file) const {
113 if (!IsLocationNameValid(location_name)) {
114 return ERROR_TIME_NOT_FOUND;
115 }
116
117 if (!time_zone_binary) {
118 LOG_ERROR(Service_Time, "Failed to extract RomFS for {:016X}!", time_zone_binary_titleid);
119 return ERROR_TIME_NOT_FOUND;
120 }
121
122 const FileSys::VirtualDir zoneinfo_dir{time_zone_binary->GetSubdirectory("zoneinfo")};
123 if (!zoneinfo_dir) {
124 LOG_ERROR(Service_Time, "{:016X} has no directory zoneinfo!", time_zone_binary_titleid);
125 return ERROR_TIME_NOT_FOUND;
126 }
127
128 vfs_file = zoneinfo_dir->GetFileRelative(location_name);
129 if (!vfs_file) {
130 LOG_WARNING(Service_Time, "{:016X} has no file \"{}\"! Using system timezone.",
131 time_zone_binary_titleid, location_name);
132 const std::string system_time_zone{Common::TimeZone::FindSystemTimeZone()};
133 vfs_file = zoneinfo_dir->GetFile(system_time_zone);
134 }
135
136 if (!vfs_file) {
137 LOG_WARNING(Service_Time, "{:016X} has no file \"{}\"! Using default timezone.",
138 time_zone_binary_titleid, location_name);
139 vfs_file = zoneinfo_dir->GetFile(Common::TimeZone::GetDefaultTimeZone());
140 }
141
142 if (!vfs_file) {
143 LOG_ERROR(Service_Time, "{:016X} has no file \"{}\"!", time_zone_binary_titleid,
144 location_name);
145 return ERROR_TIME_NOT_FOUND;
146 }
147
148 return ResultSuccess;
149}
150
151} // namespace Service::Time::TimeZone
diff --git a/src/core/hle/service/time/time_zone_content_manager.h b/src/core/hle/service/time/time_zone_content_manager.h
deleted file mode 100644
index a6f9698bc..000000000
--- a/src/core/hle/service/time/time_zone_content_manager.h
+++ /dev/null
@@ -1,49 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7#include <vector>
8
9#include "core/file_sys/vfs_types.h"
10#include "core/hle/service/time/time_zone_manager.h"
11
12namespace Core {
13class System;
14}
15
16namespace Service::Time {
17class TimeManager;
18}
19
20namespace Service::Time::TimeZone {
21
22class TimeZoneContentManager final {
23public:
24 explicit TimeZoneContentManager(Core::System& system_);
25
26 void Initialize(TimeManager& time_manager);
27
28 TimeZoneManager& GetTimeZoneManager() {
29 return time_zone_manager;
30 }
31
32 const TimeZoneManager& GetTimeZoneManager() const {
33 return time_zone_manager;
34 }
35
36 Result LoadTimeZoneRule(TimeZoneRule& rules, const std::string& location_name) const;
37
38private:
39 bool IsLocationNameValid(const std::string& location_name) const;
40 Result GetTimeZoneInfoFile(const std::string& location_name,
41 FileSys::VirtualFile& vfs_file) const;
42
43 Core::System& system;
44 TimeZoneManager time_zone_manager;
45 const FileSys::VirtualDir time_zone_binary;
46 const std::vector<std::string> location_name_cache;
47};
48
49} // namespace Service::Time::TimeZone
diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp
deleted file mode 100644
index 205371a26..000000000
--- a/src/core/hle/service/time/time_zone_manager.cpp
+++ /dev/null
@@ -1,1182 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <climits>
5#include <limits>
6
7#include "common/assert.h"
8#include "common/logging/log.h"
9#include "core/file_sys/content_archive.h"
10#include "core/file_sys/nca_metadata.h"
11#include "core/file_sys/registered_cache.h"
12#include "core/hle/service/time/time_zone_manager.h"
13#include "core/hle/service/time/time_zone_types.h"
14
15namespace Service::Time::TimeZone {
16
17static constexpr s32 epoch_year{1970};
18static constexpr s32 year_base{1900};
19static constexpr s32 epoch_week_day{4};
20static constexpr s32 seconds_per_minute{60};
21static constexpr s32 minutes_per_hour{60};
22static constexpr s32 hours_per_day{24};
23static constexpr s32 days_per_week{7};
24static constexpr s32 days_per_normal_year{365};
25static constexpr s32 days_per_leap_year{366};
26static constexpr s32 months_per_year{12};
27static constexpr s32 seconds_per_hour{seconds_per_minute * minutes_per_hour};
28static constexpr s32 seconds_per_day{seconds_per_hour * hours_per_day};
29static constexpr s32 years_per_repeat{400};
30static constexpr s64 average_seconds_per_year{31556952};
31static constexpr s64 seconds_per_repeat{years_per_repeat * average_seconds_per_year};
32
33struct Rule {
34 enum class Type : u32 { JulianDay, DayOfYear, MonthNthDayOfWeek };
35 Type rule_type{};
36 s32 day{};
37 s32 week{};
38 s32 month{};
39 s32 transition_time{};
40};
41
42struct CalendarTimeInternal {
43 s64 year{};
44 s8 month{};
45 s8 day{};
46 s8 hour{};
47 s8 minute{};
48 s8 second{};
49 int Compare(const CalendarTimeInternal& other) const {
50 if (year != other.year) {
51 if (year < other.year) {
52 return -1;
53 }
54 return 1;
55 }
56 if (month != other.month) {
57 return month - other.month;
58 }
59 if (day != other.day) {
60 return day - other.day;
61 }
62 if (hour != other.hour) {
63 return hour - other.hour;
64 }
65 if (minute != other.minute) {
66 return minute - other.minute;
67 }
68 if (second != other.second) {
69 return second - other.second;
70 }
71 return {};
72 }
73};
74
75template <typename TResult, typename TOperand>
76static bool SafeAdd(TResult& result, TOperand op) {
77 result = result + op;
78 return true;
79}
80
81template <typename TResult, typename TUnit, typename TBase>
82static bool SafeNormalize(TResult& result, TUnit& unit, TBase base) {
83 TUnit delta{};
84 if (unit >= 0) {
85 delta = unit / base;
86 } else {
87 delta = -1 - (-1 - unit) / base;
88 }
89 unit -= delta * base;
90 return SafeAdd(result, delta);
91}
92
93template <typename T>
94static constexpr bool IsLeapYear(T year) {
95 return ((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0);
96}
97
98template <typename T>
99static constexpr T GetYearLengthInDays(T year) {
100 return IsLeapYear(year) ? days_per_leap_year : days_per_normal_year;
101}
102
103static constexpr s64 GetLeapDaysFromYearPositive(s64 year) {
104 return year / 4 - year / 100 + year / years_per_repeat;
105}
106
107static constexpr s64 GetLeapDaysFromYear(s64 year) {
108 if (year < 0) {
109 return -1 - GetLeapDaysFromYearPositive(-1 - year);
110 } else {
111 return GetLeapDaysFromYearPositive(year);
112 }
113}
114
115static constexpr s8 GetMonthLength(bool is_leap_year, int month) {
116 constexpr std::array<s8, 12> month_lengths{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
117 constexpr std::array<s8, 12> month_lengths_leap{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
118 return is_leap_year ? month_lengths_leap[month] : month_lengths[month];
119}
120
121static constexpr bool IsDigit(char value) {
122 return value >= '0' && value <= '9';
123}
124
125static constexpr int GetQZName(const char* name, int offset, char delimiter) {
126 while (name[offset] != '\0' && name[offset] != delimiter) {
127 offset++;
128 }
129 return offset;
130}
131
132static constexpr int GetTZName(const char* name, int offset) {
133 char c;
134
135 while ((c = name[offset]) != '\0' && !IsDigit(c) && c != ',' && c != '-' && c != '+') {
136 ++offset;
137 }
138 return offset;
139}
140
141static constexpr bool GetInteger(const char* name, int& offset, int& value, int min, int max) {
142 value = 0;
143 char temp{name[offset]};
144 if (!IsDigit(temp)) {
145 return {};
146 }
147 do {
148 value = value * 10 + (temp - '0');
149 if (value > max) {
150 return {};
151 }
152 offset++;
153 temp = name[offset];
154 } while (IsDigit(temp));
155
156 return value >= min;
157}
158
159static constexpr bool GetSeconds(const char* name, int& offset, int& seconds) {
160 seconds = 0;
161 int value{};
162 if (!GetInteger(name, offset, value, 0, hours_per_day * days_per_week - 1)) {
163 return {};
164 }
165 seconds = value * seconds_per_hour;
166
167 if (name[offset] == ':') {
168 offset++;
169 if (!GetInteger(name, offset, value, 0, minutes_per_hour - 1)) {
170 return {};
171 }
172 seconds += value * seconds_per_minute;
173 if (name[offset] == ':') {
174 offset++;
175 if (!GetInteger(name, offset, value, 0, seconds_per_minute)) {
176 return {};
177 }
178 seconds += value;
179 }
180 }
181 return true;
182}
183
184static constexpr bool GetOffset(const char* name, int& offset, int& value) {
185 bool is_negative{};
186 if (name[offset] == '-') {
187 is_negative = true;
188 offset++;
189 } else if (name[offset] == '+') {
190 offset++;
191 }
192 if (!GetSeconds(name, offset, value)) {
193 return {};
194 }
195 if (is_negative) {
196 value = -value;
197 }
198 return true;
199}
200
201static constexpr bool GetRule(const char* name, int& position, Rule& rule) {
202 bool is_valid{};
203 if (name[position] == 'J') {
204 position++;
205 rule.rule_type = Rule::Type::JulianDay;
206 is_valid = GetInteger(name, position, rule.day, 1, days_per_normal_year);
207 } else if (name[position] == 'M') {
208 position++;
209 rule.rule_type = Rule::Type::MonthNthDayOfWeek;
210 is_valid = GetInteger(name, position, rule.month, 1, months_per_year);
211 if (!is_valid) {
212 return {};
213 }
214 if (name[position++] != '.') {
215 return {};
216 }
217 is_valid = GetInteger(name, position, rule.week, 1, 5);
218 if (!is_valid) {
219 return {};
220 }
221 if (name[position++] != '.') {
222 return {};
223 }
224 is_valid = GetInteger(name, position, rule.day, 0, days_per_week - 1);
225 } else if (isdigit(name[position])) {
226 rule.rule_type = Rule::Type::DayOfYear;
227 is_valid = GetInteger(name, position, rule.day, 0, days_per_leap_year - 1);
228 } else {
229 return {};
230 }
231 if (!is_valid) {
232 return {};
233 }
234 if (name[position] == '/') {
235 position++;
236 return GetOffset(name, position, rule.transition_time);
237 } else {
238 rule.transition_time = 2 * seconds_per_hour;
239 }
240 return true;
241}
242
243static constexpr int TransitionTime(int year, Rule rule, int offset) {
244 int value{};
245 switch (rule.rule_type) {
246 case Rule::Type::JulianDay:
247 value = (rule.day - 1) * seconds_per_day;
248 if (IsLeapYear(year) && rule.day >= 60) {
249 value += seconds_per_day;
250 }
251 break;
252 case Rule::Type::DayOfYear:
253 value = rule.day * seconds_per_day;
254 break;
255 case Rule::Type::MonthNthDayOfWeek: {
256 // Use Zeller's Congruence (https://en.wikipedia.org/wiki/Zeller%27s_congruence) to
257 // calculate the day of the week for any Julian or Gregorian calendar date.
258 const int m1{(rule.month + 9) % 12 + 1};
259 const int yy0{(rule.month <= 2) ? (year - 1) : year};
260 const int yy1{yy0 / 100};
261 const int yy2{yy0 % 100};
262 int day_of_week{((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7};
263
264 if (day_of_week < 0) {
265 day_of_week += days_per_week;
266 }
267 int day{rule.day - day_of_week};
268 if (day < 0) {
269 day += days_per_week;
270 }
271 for (int i{1}; i < rule.week; i++) {
272 if (day + days_per_week >= GetMonthLength(IsLeapYear(year), rule.month - 1)) {
273 break;
274 }
275 day += days_per_week;
276 }
277
278 value = day * seconds_per_day;
279 for (int index{}; index < rule.month - 1; ++index) {
280 value += GetMonthLength(IsLeapYear(year), index) * seconds_per_day;
281 }
282 break;
283 }
284 default:
285 ASSERT(false);
286 break;
287 }
288 return value + rule.transition_time + offset;
289}
290
291static bool ParsePosixName(const char* name, TimeZoneRule& rule) {
292 static constexpr char default_rule[]{",M4.1.0,M10.5.0"};
293 const char* std_name{name};
294 int std_len{};
295 int offset{};
296 int std_offset{};
297
298 if (name[offset] == '<') {
299 offset++;
300 std_name = name + offset;
301 const int std_name_offset{offset};
302 offset = GetQZName(name, offset, '>');
303 if (name[offset] != '>') {
304 return {};
305 }
306 std_len = offset - std_name_offset;
307 offset++;
308 } else {
309 offset = GetTZName(name, offset);
310 std_len = offset;
311 }
312 if (std_len == 0) {
313 return {};
314 }
315 if (!GetOffset(name, offset, std_offset)) {
316 return {};
317 }
318
319 int char_count{std_len + 1};
320 int dest_len{};
321 int dest_offset{};
322 const char* dest_name{name + offset};
323 if (rule.chars.size() < std::size_t(char_count)) {
324 return {};
325 }
326
327 if (name[offset] != '\0') {
328 if (name[offset] == '<') {
329 dest_name = name + (++offset);
330 const int dest_name_offset{offset};
331 offset = GetQZName(name, offset, '>');
332 if (name[offset] != '>') {
333 return {};
334 }
335 dest_len = offset - dest_name_offset;
336 offset++;
337 } else {
338 dest_name = name + (offset);
339 offset = GetTZName(name, offset);
340 dest_len = offset;
341 }
342 if (dest_len == 0) {
343 return {};
344 }
345 char_count += dest_len + 1;
346 if (rule.chars.size() < std::size_t(char_count)) {
347 return {};
348 }
349 if (name[offset] != '\0' && name[offset] != ',' && name[offset] != ';') {
350 if (!GetOffset(name, offset, dest_offset)) {
351 return {};
352 }
353 } else {
354 dest_offset = std_offset - seconds_per_hour;
355 }
356 if (name[offset] == '\0') {
357 name = default_rule;
358 offset = 0;
359 }
360 if (name[offset] == ',' || name[offset] == ';') {
361 offset++;
362
363 Rule start{};
364 if (!GetRule(name, offset, start)) {
365 return {};
366 }
367 if (name[offset++] != ',') {
368 return {};
369 }
370
371 Rule end{};
372 if (!GetRule(name, offset, end)) {
373 return {};
374 }
375 if (name[offset] != '\0') {
376 return {};
377 }
378
379 rule.type_count = 2;
380 rule.ttis[0].gmt_offset = -dest_offset;
381 rule.ttis[0].is_dst = true;
382 rule.ttis[0].abbreviation_list_index = std_len + 1;
383 rule.ttis[1].gmt_offset = -std_offset;
384 rule.ttis[1].is_dst = false;
385 rule.ttis[1].abbreviation_list_index = 0;
386 rule.default_type = 0;
387
388 s64 jan_first{};
389 int time_count{};
390 int jan_offset{};
391 int year_beginning{epoch_year};
392 do {
393 const int year_seconds{GetYearLengthInDays(year_beginning - 1) * seconds_per_day};
394 year_beginning--;
395 if (!SafeAdd(jan_first, -year_seconds)) {
396 jan_offset = -year_seconds;
397 break;
398 }
399 } while (epoch_year - years_per_repeat / 2 < year_beginning);
400
401 int year_limit{year_beginning + years_per_repeat + 1};
402 int year{};
403 for (year = year_beginning; year < year_limit; year++) {
404 int start_time{TransitionTime(year, start, std_offset)};
405 int end_time{TransitionTime(year, end, dest_offset)};
406 const int year_seconds{GetYearLengthInDays(year) * seconds_per_day};
407 const bool is_reversed{end_time < start_time};
408 if (is_reversed) {
409 int swap{start_time};
410 start_time = end_time;
411 end_time = swap;
412 }
413
414 if (is_reversed ||
415 (start_time < end_time &&
416 (end_time - start_time < (year_seconds + (std_offset - dest_offset))))) {
417 if (rule.ats.size() - 2 < std::size_t(time_count)) {
418 break;
419 }
420
421 rule.ats[time_count] = jan_first;
422 if (SafeAdd(rule.ats[time_count], jan_offset + start_time)) {
423 rule.types[time_count++] = is_reversed ? 1 : 0;
424 } else if (jan_offset != 0) {
425 rule.default_type = is_reversed ? 1 : 0;
426 }
427
428 rule.ats[time_count] = jan_first;
429 if (SafeAdd(rule.ats[time_count], jan_offset + end_time)) {
430 rule.types[time_count++] = is_reversed ? 0 : 1;
431 year_limit = year + years_per_repeat + 1;
432 } else if (jan_offset != 0) {
433 rule.default_type = is_reversed ? 0 : 1;
434 }
435 }
436 if (!SafeAdd(jan_first, jan_offset + year_seconds)) {
437 break;
438 }
439 jan_offset = 0;
440 }
441 rule.time_count = time_count;
442 if (time_count == 0) {
443 rule.type_count = 1;
444 } else if (years_per_repeat < year - year_beginning) {
445 rule.go_back = true;
446 rule.go_ahead = true;
447 }
448 } else {
449 if (name[offset] == '\0') {
450 return {};
451 }
452
453 s64 their_std_offset{};
454 for (int index{}; index < rule.time_count; ++index) {
455 const s8 type{rule.types[index]};
456 if (rule.ttis[type].is_standard_time_daylight) {
457 their_std_offset = -rule.ttis[type].gmt_offset;
458 }
459 }
460
461 s64 their_offset{their_std_offset};
462 for (int index{}; index < rule.time_count; ++index) {
463 const s8 type{rule.types[index]};
464 rule.types[index] = rule.ttis[type].is_dst ? 1 : 0;
465 if (!rule.ttis[type].is_gmt) {
466 if (!rule.ttis[type].is_standard_time_daylight) {
467 rule.ats[index] += dest_offset - their_std_offset;
468 } else {
469 rule.ats[index] += std_offset - their_std_offset;
470 }
471 }
472 their_offset = -rule.ttis[type].gmt_offset;
473 if (!rule.ttis[type].is_dst) {
474 their_std_offset = their_offset;
475 }
476 }
477
478 if (rule.time_count > 0) {
479 UNIMPLEMENTED();
480 // TODO (lat9nq): Implement eggert/tz/localtime.c:tzparse:1329
481 // Seems to be unused in yuzu for now: I never hit the UNIMPLEMENTED in testing
482 }
483
484 rule.ttis[0].gmt_offset = -std_offset;
485 rule.ttis[0].is_dst = false;
486 rule.ttis[0].abbreviation_list_index = 0;
487 rule.ttis[1].gmt_offset = -dest_offset;
488 rule.ttis[1].is_dst = true;
489 rule.ttis[1].abbreviation_list_index = std_len + 1;
490 rule.type_count = 2;
491 rule.default_type = 0;
492 }
493 } else {
494 // Default is standard time
495 rule.type_count = 1;
496 rule.time_count = 0;
497 rule.default_type = 0;
498 rule.ttis[0].gmt_offset = -std_offset;
499 rule.ttis[0].is_dst = false;
500 rule.ttis[0].abbreviation_list_index = 0;
501 }
502
503 rule.char_count = char_count;
504 for (int index{}; index < std_len; ++index) {
505 rule.chars[index] = std_name[index];
506 }
507
508 rule.chars[std_len++] = '\0';
509 if (dest_len != 0) {
510 for (int index{}; index < dest_len; ++index) {
511 rule.chars[std_len + index] = dest_name[index];
512 }
513 rule.chars[std_len + dest_len] = '\0';
514 }
515
516 return true;
517}
518
519static bool ParseTimeZoneBinary(TimeZoneRule& time_zone_rule, FileSys::VirtualFile& vfs_file) {
520 TzifHeader header{};
521 if (vfs_file->ReadObject<TzifHeader>(&header) != sizeof(TzifHeader)) {
522 return {};
523 }
524
525 constexpr s32 time_zone_max_leaps{50};
526 constexpr s32 time_zone_max_chars{50};
527 constexpr s32 time_zone_max_times{1000};
528 if (!(0 <= header.leap_count && header.leap_count < time_zone_max_leaps &&
529 0 < header.type_count && header.type_count < s32(time_zone_rule.ttis.size()) &&
530 0 <= header.time_count && header.time_count < s32(time_zone_rule.ats.size()) &&
531 0 <= header.char_count && header.char_count < time_zone_max_chars &&
532 (header.ttis_std_count == header.type_count || header.ttis_std_count == 0) &&
533 (header.ttis_gmt_count == header.type_count || header.ttis_gmt_count == 0))) {
534 return {};
535 }
536 time_zone_rule.time_count = header.time_count;
537 time_zone_rule.type_count = header.type_count;
538 time_zone_rule.char_count = header.char_count;
539
540 int time_count{};
541 u64 read_offset = sizeof(TzifHeader);
542 for (int index{}; index < time_zone_rule.time_count; ++index) {
543 s64_be at{};
544 vfs_file->ReadObject<s64_be>(&at, read_offset);
545 time_zone_rule.types[index] = 1;
546 if (time_count != 0 && at <= time_zone_rule.ats[time_count - 1]) {
547 if (at < time_zone_rule.ats[time_count - 1]) {
548 return {};
549 }
550 time_zone_rule.types[index - 1] = 0;
551 time_count--;
552 }
553 time_zone_rule.ats[time_count++] = at;
554 read_offset += sizeof(s64_be);
555 }
556 time_count = 0;
557 for (int index{}; index < time_zone_rule.time_count; ++index) {
558 const u8 type{*vfs_file->ReadByte(read_offset)};
559 read_offset += sizeof(u8);
560 if (time_zone_rule.type_count <= type) {
561 return {};
562 }
563 if (time_zone_rule.types[index] != 0) {
564 time_zone_rule.types[time_count++] = type;
565 }
566 }
567 time_zone_rule.time_count = time_count;
568 for (int index{}; index < time_zone_rule.type_count; ++index) {
569 TimeTypeInfo& ttis{time_zone_rule.ttis[index]};
570 u32_be gmt_offset{};
571 vfs_file->ReadObject<u32_be>(&gmt_offset, read_offset);
572 read_offset += sizeof(u32_be);
573 ttis.gmt_offset = gmt_offset;
574
575 const u8 dst{*vfs_file->ReadByte(read_offset)};
576 read_offset += sizeof(u8);
577 if (dst >= 2) {
578 return {};
579 }
580 ttis.is_dst = dst != 0;
581
582 const s32 abbreviation_list_index{*vfs_file->ReadByte(read_offset)};
583 read_offset += sizeof(u8);
584 if (abbreviation_list_index >= time_zone_rule.char_count) {
585 return {};
586 }
587 ttis.abbreviation_list_index = abbreviation_list_index;
588 }
589
590 vfs_file->ReadArray(time_zone_rule.chars.data(), time_zone_rule.char_count, read_offset);
591 time_zone_rule.chars[time_zone_rule.char_count] = '\0';
592 read_offset += time_zone_rule.char_count;
593 for (int index{}; index < time_zone_rule.type_count; ++index) {
594 if (header.ttis_std_count == 0) {
595 time_zone_rule.ttis[index].is_standard_time_daylight = false;
596 } else {
597 const u8 dst{*vfs_file->ReadByte(read_offset)};
598 read_offset += sizeof(u8);
599 if (dst >= 2) {
600 return {};
601 }
602 time_zone_rule.ttis[index].is_standard_time_daylight = dst != 0;
603 }
604 }
605
606 for (int index{}; index < time_zone_rule.type_count; ++index) {
607 if (header.ttis_std_count == 0) {
608 time_zone_rule.ttis[index].is_gmt = false;
609 } else {
610 const u8 dst{*vfs_file->ReadByte(read_offset)};
611 read_offset += sizeof(u8);
612 if (dst >= 2) {
613 return {};
614 }
615 time_zone_rule.ttis[index].is_gmt = dst != 0;
616 }
617 }
618
619 const u64 position{(read_offset - sizeof(TzifHeader))};
620 const s64 bytes_read = s64(vfs_file->GetSize() - sizeof(TzifHeader) - position);
621 if (bytes_read < 0) {
622 return {};
623 }
624 constexpr s32 time_zone_name_max{255};
625 if (bytes_read > (time_zone_name_max + 1)) {
626 return {};
627 }
628
629 std::array<char, time_zone_name_max + 1> temp_name{};
630 vfs_file->ReadArray(temp_name.data(), bytes_read, read_offset);
631 if (bytes_read > 2 && temp_name[0] == '\n' && temp_name[bytes_read - 1] == '\n' &&
632 std::size_t(time_zone_rule.type_count) + 2 <= time_zone_rule.ttis.size()) {
633 temp_name[bytes_read - 1] = '\0';
634
635 std::array<char, time_zone_name_max> name{};
636 std::memcpy(name.data(), temp_name.data() + 1, std::size_t(bytes_read - 1));
637
638 // Fill in computed transition times with temp rule
639 TimeZoneRule temp_rule;
640 if (ParsePosixName(name.data(), temp_rule)) {
641 int have_abbreviation = 0;
642 int char_count = time_zone_rule.char_count;
643
644 for (int i = 0; i < temp_rule.type_count; i++) {
645 char* temp_abbreviation =
646 temp_rule.chars.data() + temp_rule.ttis[i].abbreviation_list_index;
647 int j;
648 for (j = 0; j < char_count; j++) {
649 if (std::strcmp(time_zone_rule.chars.data() + j, temp_abbreviation) == 0) {
650 temp_rule.ttis[i].abbreviation_list_index = j;
651 have_abbreviation++;
652 break;
653 }
654 }
655 if (j >= char_count) {
656 int temp_abbreviation_length = static_cast<int>(std::strlen(temp_abbreviation));
657 if (j + temp_abbreviation_length < time_zone_max_chars) {
658 std::strcpy(time_zone_rule.chars.data() + j, temp_abbreviation);
659 char_count = j + temp_abbreviation_length + 1;
660 temp_rule.ttis[i].abbreviation_list_index = j;
661 have_abbreviation++;
662 }
663 }
664 }
665
666 if (have_abbreviation == temp_rule.type_count) {
667 time_zone_rule.char_count = char_count;
668
669 // Original comment:
670 /* Ignore any trailing, no-op transitions generated
671 by zic as they don't help here and can run afoul
672 of bugs in zic 2016j or earlier. */
673 // This is possibly unnecessary for yuzu, since Nintendo doesn't run zic
674 while (1 < time_zone_rule.time_count &&
675 (time_zone_rule.types[time_zone_rule.time_count - 1] ==
676 time_zone_rule.types[time_zone_rule.time_count - 2])) {
677 time_zone_rule.time_count--;
678 }
679
680 for (int i = 0;
681 i < temp_rule.time_count && time_zone_rule.time_count < time_zone_max_times;
682 i++) {
683 const s64 transition_time = temp_rule.ats[i];
684 if (0 < time_zone_rule.time_count &&
685 transition_time <= time_zone_rule.ats[time_zone_rule.time_count - 1]) {
686 continue;
687 }
688
689 time_zone_rule.ats[time_zone_rule.time_count] = transition_time;
690 time_zone_rule.types[time_zone_rule.time_count] =
691 static_cast<s8>(time_zone_rule.type_count + temp_rule.types[i]);
692 time_zone_rule.time_count++;
693 }
694 for (int i = 0; i < temp_rule.type_count; i++) {
695 time_zone_rule.ttis[time_zone_rule.type_count++] = temp_rule.ttis[i];
696 }
697 }
698 }
699 }
700
701 const auto typesequiv = [](TimeZoneRule& rule, int a, int b) -> bool {
702 if (a < 0 || a >= rule.type_count || b < 0 || b >= rule.type_count) {
703 return {};
704 }
705
706 const struct TimeTypeInfo* ap = &rule.ttis[a];
707 const struct TimeTypeInfo* bp = &rule.ttis[b];
708
709 return (ap->gmt_offset == bp->gmt_offset && ap->is_dst == bp->is_dst &&
710 (std::strcmp(&rule.chars[ap->abbreviation_list_index],
711 &rule.chars[bp->abbreviation_list_index]) == 0));
712 };
713
714 if (time_zone_rule.type_count == 0) {
715 return {};
716 }
717 if (time_zone_rule.time_count > 1) {
718 if (time_zone_rule.ats[0] <= std::numeric_limits<s64>::max() - seconds_per_repeat) {
719 s64 repeatat = time_zone_rule.ats[0] + seconds_per_repeat;
720 int repeatattype = time_zone_rule.types[0];
721 for (int i = 1; i < time_zone_rule.time_count; ++i) {
722 if (time_zone_rule.ats[i] == repeatat &&
723 typesequiv(time_zone_rule, time_zone_rule.types[i], repeatattype)) {
724 time_zone_rule.go_back = true;
725 break;
726 }
727 }
728 }
729 if (std::numeric_limits<s64>::min() + seconds_per_repeat <=
730 time_zone_rule.ats[time_zone_rule.time_count - 1]) {
731 s64 repeatat = time_zone_rule.ats[time_zone_rule.time_count - 1] - seconds_per_repeat;
732 int repeatattype = time_zone_rule.types[time_zone_rule.time_count - 1];
733 for (int i = time_zone_rule.time_count; i >= 0; --i) {
734 if (time_zone_rule.ats[i] == repeatat &&
735 typesequiv(time_zone_rule, time_zone_rule.types[i], repeatattype)) {
736 time_zone_rule.go_ahead = true;
737 break;
738 }
739 }
740 }
741 }
742
743 s32 default_type{};
744
745 for (default_type = 0; default_type < time_zone_rule.time_count; default_type++) {
746 if (time_zone_rule.types[default_type] == 0) {
747 break;
748 }
749 }
750
751 default_type = default_type < time_zone_rule.time_count ? -1 : 0;
752 if (default_type < 0 && time_zone_rule.time_count > 0 &&
753 time_zone_rule.ttis[time_zone_rule.types[0]].is_dst) {
754 default_type = time_zone_rule.types[0];
755 while (--default_type >= 0) {
756 if (!time_zone_rule.ttis[default_type].is_dst) {
757 break;
758 }
759 }
760 }
761 if (default_type < 0) {
762 default_type = 0;
763 while (time_zone_rule.ttis[default_type].is_dst) {
764 if (++default_type >= time_zone_rule.type_count) {
765 default_type = 0;
766 break;
767 }
768 }
769 }
770 time_zone_rule.default_type = default_type;
771 return true;
772}
773
774static Result CreateCalendarTime(s64 time, int gmt_offset, CalendarTimeInternal& calendar_time,
775 CalendarAdditionalInfo& calendar_additional_info) {
776 s64 year{epoch_year};
777 s64 time_days{time / seconds_per_day};
778 s64 remaining_seconds{time % seconds_per_day};
779 while (time_days < 0 || time_days >= GetYearLengthInDays(year)) {
780 s64 delta = time_days / days_per_leap_year;
781 if (!delta) {
782 delta = time_days < 0 ? -1 : 1;
783 }
784 s64 new_year{year};
785 if (!SafeAdd(new_year, delta)) {
786 return ERROR_OUT_OF_RANGE;
787 }
788 time_days -= (new_year - year) * days_per_normal_year;
789 time_days -= GetLeapDaysFromYear(new_year - 1) - GetLeapDaysFromYear(year - 1);
790 year = new_year;
791 }
792
793 s64 day_of_year{time_days};
794 remaining_seconds += gmt_offset;
795 while (remaining_seconds < 0) {
796 remaining_seconds += seconds_per_day;
797 day_of_year--;
798 }
799
800 while (remaining_seconds >= seconds_per_day) {
801 remaining_seconds -= seconds_per_day;
802 day_of_year++;
803 }
804
805 while (day_of_year < 0) {
806 if (!SafeAdd(year, -1)) {
807 return ERROR_OUT_OF_RANGE;
808 }
809 day_of_year += GetYearLengthInDays(year);
810 }
811
812 while (day_of_year >= GetYearLengthInDays(year)) {
813 day_of_year -= GetYearLengthInDays(year);
814 if (!SafeAdd(year, 1)) {
815 return ERROR_OUT_OF_RANGE;
816 }
817 }
818
819 calendar_time.year = year;
820 calendar_additional_info.day_of_year = static_cast<u32>(day_of_year);
821 s64 day_of_week{
822 (epoch_week_day +
823 ((year - epoch_year) % days_per_week) * (days_per_normal_year % days_per_week) +
824 GetLeapDaysFromYear(year - 1) - GetLeapDaysFromYear(epoch_year - 1) + day_of_year) %
825 days_per_week};
826 if (day_of_week < 0) {
827 day_of_week += days_per_week;
828 }
829
830 calendar_additional_info.day_of_week = static_cast<u32>(day_of_week);
831 calendar_time.hour = static_cast<s8>((remaining_seconds / seconds_per_hour) % seconds_per_hour);
832 remaining_seconds %= seconds_per_hour;
833 calendar_time.minute = static_cast<s8>(remaining_seconds / seconds_per_minute);
834 calendar_time.second = static_cast<s8>(remaining_seconds % seconds_per_minute);
835
836 for (calendar_time.month = 0;
837 day_of_year >= GetMonthLength(IsLeapYear(year), calendar_time.month);
838 ++calendar_time.month) {
839 day_of_year -= GetMonthLength(IsLeapYear(year), calendar_time.month);
840 }
841
842 calendar_time.day = static_cast<s8>(day_of_year + 1);
843 calendar_additional_info.is_dst = false;
844 calendar_additional_info.gmt_offset = gmt_offset;
845
846 return ResultSuccess;
847}
848
849static Result ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time,
850 CalendarTimeInternal& calendar_time,
851 CalendarAdditionalInfo& calendar_additional_info) {
852 ASSERT(rules.go_ahead ? rules.time_count > 0 : true);
853 if ((rules.go_back && time < rules.ats[0]) ||
854 (rules.go_ahead && time > rules.ats[rules.time_count - 1])) {
855 s64 seconds{};
856 if (time < rules.ats[0]) {
857 seconds = rules.ats[0] - time;
858 } else {
859 seconds = time - rules.ats[rules.time_count - 1];
860 }
861 seconds--;
862
863 const s64 years{(seconds / seconds_per_repeat + 1) * years_per_repeat};
864 seconds = years * average_seconds_per_year;
865
866 s64 new_time{time};
867 if (time < rules.ats[0]) {
868 new_time += seconds;
869 } else {
870 new_time -= seconds;
871 }
872 if (new_time < rules.ats[0] && new_time > rules.ats[rules.time_count - 1]) {
873 return ERROR_TIME_NOT_FOUND;
874 }
875 if (const Result result{
876 ToCalendarTimeInternal(rules, new_time, calendar_time, calendar_additional_info)};
877 result != ResultSuccess) {
878 return result;
879 }
880 if (time < rules.ats[0]) {
881 calendar_time.year -= years;
882 } else {
883 calendar_time.year += years;
884 }
885
886 return ResultSuccess;
887 }
888
889 s32 tti_index{};
890 if (rules.time_count == 0 || time < rules.ats[0]) {
891 tti_index = rules.default_type;
892 } else {
893 s32 low{1};
894 s32 high{rules.time_count};
895 while (low < high) {
896 s32 mid{(low + high) >> 1};
897 if (time < rules.ats[mid]) {
898 high = mid;
899 } else {
900 low = mid + 1;
901 }
902 }
903 tti_index = rules.types[low - 1];
904 }
905
906 if (const Result result{CreateCalendarTime(time, rules.ttis[tti_index].gmt_offset,
907 calendar_time, calendar_additional_info)};
908 result != ResultSuccess) {
909 return result;
910 }
911
912 calendar_additional_info.is_dst = rules.ttis[tti_index].is_dst;
913 const char* time_zone{&rules.chars[rules.ttis[tti_index].abbreviation_list_index]};
914 u32 index;
915 for (index = 0; time_zone[index] != '\0' && time_zone[index] != ',' &&
916 index < calendar_additional_info.timezone_name.size() - 1;
917 ++index) {
918 calendar_additional_info.timezone_name[index] = time_zone[index];
919 }
920 calendar_additional_info.timezone_name[index] = '\0';
921 return ResultSuccess;
922}
923
924static Result ToCalendarTimeImpl(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) {
925 CalendarTimeInternal calendar_time{};
926 const Result result{
927 ToCalendarTimeInternal(rules, time, calendar_time, calendar.additional_info)};
928 calendar.time.year = static_cast<s16>(calendar_time.year);
929
930 // Internal impl. uses 0-indexed month
931 calendar.time.month = static_cast<s8>(calendar_time.month + 1);
932
933 calendar.time.day = calendar_time.day;
934 calendar.time.hour = calendar_time.hour;
935 calendar.time.minute = calendar_time.minute;
936 calendar.time.second = calendar_time.second;
937 return result;
938}
939
940TimeZoneManager::TimeZoneManager() = default;
941TimeZoneManager::~TimeZoneManager() = default;
942
943Result TimeZoneManager::ToCalendarTime(const TimeZoneRule& rules, s64 time,
944 CalendarInfo& calendar) const {
945 return ToCalendarTimeImpl(rules, time, calendar);
946}
947
948Result TimeZoneManager::SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name,
949 FileSys::VirtualFile& vfs_file) {
950 TimeZoneRule rule{};
951 if (ParseTimeZoneBinary(rule, vfs_file)) {
952 device_location_name = location_name;
953 time_zone_rule = rule;
954 return ResultSuccess;
955 }
956 return ERROR_TIME_ZONE_CONVERSION_FAILED;
957}
958
959Result TimeZoneManager::SetUpdatedTime(const Clock::SteadyClockTimePoint& value) {
960 time_zone_update_time_point = value;
961 return ResultSuccess;
962}
963
964Result TimeZoneManager::ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const {
965 if (is_initialized) {
966 return ToCalendarTime(time_zone_rule, time, calendar);
967 } else {
968 return ERROR_UNINITIALIZED_CLOCK;
969 }
970}
971
972Result TimeZoneManager::ParseTimeZoneRuleBinary(TimeZoneRule& rules,
973 FileSys::VirtualFile& vfs_file) const {
974 if (!ParseTimeZoneBinary(rules, vfs_file)) {
975 return ERROR_TIME_ZONE_CONVERSION_FAILED;
976 }
977 return ResultSuccess;
978}
979
980Result TimeZoneManager::ToPosixTime(const TimeZoneRule& rules, const CalendarTime& calendar_time,
981 s64& posix_time) const {
982 posix_time = 0;
983
984 CalendarTimeInternal internal_time{
985 .year = calendar_time.year,
986 // Internal impl. uses 0-indexed month
987 .month = static_cast<s8>(calendar_time.month - 1),
988 .day = calendar_time.day,
989 .hour = calendar_time.hour,
990 .minute = calendar_time.minute,
991 .second = calendar_time.second,
992 };
993
994 s32 hour{internal_time.hour};
995 s32 minute{internal_time.minute};
996 if (!SafeNormalize(hour, minute, minutes_per_hour)) {
997 return ERROR_OVERFLOW;
998 }
999 internal_time.minute = static_cast<s8>(minute);
1000
1001 s32 day{internal_time.day};
1002 if (!SafeNormalize(day, hour, hours_per_day)) {
1003 return ERROR_OVERFLOW;
1004 }
1005 internal_time.day = static_cast<s8>(day);
1006 internal_time.hour = static_cast<s8>(hour);
1007
1008 s64 year{internal_time.year};
1009 s64 month{internal_time.month};
1010 if (!SafeNormalize(year, month, months_per_year)) {
1011 return ERROR_OVERFLOW;
1012 }
1013 internal_time.month = static_cast<s8>(month);
1014
1015 if (!SafeAdd(year, year_base)) {
1016 return ERROR_OVERFLOW;
1017 }
1018
1019 while (day <= 0) {
1020 if (!SafeAdd(year, -1)) {
1021 return ERROR_OVERFLOW;
1022 }
1023 s64 temp_year{year};
1024 if (1 < internal_time.month) {
1025 ++temp_year;
1026 }
1027 day += static_cast<s32>(GetYearLengthInDays(temp_year));
1028 }
1029
1030 while (day > days_per_leap_year) {
1031 s64 temp_year{year};
1032 if (1 < internal_time.month) {
1033 temp_year++;
1034 }
1035 day -= static_cast<s32>(GetYearLengthInDays(temp_year));
1036 if (!SafeAdd(year, 1)) {
1037 return ERROR_OVERFLOW;
1038 }
1039 }
1040
1041 while (true) {
1042 const s32 month_length{GetMonthLength(IsLeapYear(year), internal_time.month)};
1043 if (day <= month_length) {
1044 break;
1045 }
1046 day -= month_length;
1047 internal_time.month++;
1048 if (internal_time.month >= months_per_year) {
1049 internal_time.month = 0;
1050 if (!SafeAdd(year, 1)) {
1051 return ERROR_OVERFLOW;
1052 }
1053 }
1054 }
1055 internal_time.day = static_cast<s8>(day);
1056
1057 if (!SafeAdd(year, -year_base)) {
1058 return ERROR_OVERFLOW;
1059 }
1060 internal_time.year = year;
1061
1062 s32 saved_seconds{};
1063 if (internal_time.second >= 0 && internal_time.second < seconds_per_minute) {
1064 saved_seconds = 0;
1065 } else if (year + year_base < epoch_year) {
1066 s32 second{internal_time.second};
1067 if (!SafeAdd(second, 1 - seconds_per_minute)) {
1068 return ERROR_OVERFLOW;
1069 }
1070 saved_seconds = second;
1071 internal_time.second = 1 - seconds_per_minute;
1072 } else {
1073 saved_seconds = internal_time.second;
1074 internal_time.second = 0;
1075 }
1076
1077 s64 low{LLONG_MIN};
1078 s64 high{LLONG_MAX};
1079 while (true) {
1080 s64 pivot{low / 2 + high / 2};
1081 if (pivot < low) {
1082 pivot = low;
1083 } else if (pivot > high) {
1084 pivot = high;
1085 }
1086 s32 direction{};
1087 CalendarTimeInternal candidate_calendar_time{};
1088 CalendarAdditionalInfo unused{};
1089 if (ToCalendarTimeInternal(rules, pivot, candidate_calendar_time, unused) !=
1090 ResultSuccess) {
1091 if (pivot > 0) {
1092 direction = 1;
1093 } else {
1094 direction = -1;
1095 }
1096 } else {
1097 direction = candidate_calendar_time.Compare(internal_time);
1098 }
1099 if (!direction) {
1100 const s64 time_result{pivot + saved_seconds};
1101 if ((time_result < pivot) != (saved_seconds < 0)) {
1102 return ERROR_OVERFLOW;
1103 }
1104 posix_time = time_result;
1105 break;
1106 } else {
1107 if (pivot == low) {
1108 if (pivot == LLONG_MAX) {
1109 return ERROR_TIME_NOT_FOUND;
1110 }
1111 pivot++;
1112 low++;
1113 } else if (pivot == high) {
1114 if (pivot == LLONG_MIN) {
1115 return ERROR_TIME_NOT_FOUND;
1116 }
1117 pivot--;
1118 high--;
1119 }
1120 if (low > high) {
1121 return ERROR_TIME_NOT_FOUND;
1122 }
1123 if (direction > 0) {
1124 high = pivot;
1125 } else {
1126 low = pivot;
1127 }
1128 }
1129 }
1130 return ResultSuccess;
1131}
1132
1133Result TimeZoneManager::ToPosixTimeWithMyRule(const CalendarTime& calendar_time,
1134 s64& posix_time) const {
1135 if (is_initialized) {
1136 return ToPosixTime(time_zone_rule, calendar_time, posix_time);
1137 }
1138 posix_time = 0;
1139 return ERROR_UNINITIALIZED_CLOCK;
1140}
1141
1142Result TimeZoneManager::GetDeviceLocationName(LocationName& value) const {
1143 if (!is_initialized) {
1144 return ERROR_UNINITIALIZED_CLOCK;
1145 }
1146 std::memcpy(value.data(), device_location_name.c_str(), device_location_name.size());
1147 return ResultSuccess;
1148}
1149
1150Result TimeZoneManager::GetTotalLocationNameCount(s32& count) const {
1151 if (!is_initialized) {
1152 return ERROR_UNINITIALIZED_CLOCK;
1153 }
1154 count = static_cast<u32>(total_location_name_count);
1155
1156 return ResultSuccess;
1157}
1158
1159Result TimeZoneManager::GetTimeZoneRuleVersion(u128& version) const {
1160 if (!is_initialized) {
1161 return ERROR_UNINITIALIZED_CLOCK;
1162 }
1163 version = time_zone_rule_version;
1164
1165 return ResultSuccess;
1166}
1167
1168Result TimeZoneManager::LoadLocationNameList(std::vector<LocationName>& values) const {
1169 if (!is_initialized) {
1170 return ERROR_UNINITIALIZED_CLOCK;
1171 }
1172
1173 for (const auto& name : total_location_names) {
1174 LocationName entry{};
1175 std::memcpy(entry.data(), name.c_str(), name.size());
1176 values.push_back(entry);
1177 }
1178
1179 return ResultSuccess;
1180}
1181
1182} // namespace Service::Time::TimeZone
diff --git a/src/core/hle/service/time/time_zone_manager.h b/src/core/hle/service/time/time_zone_manager.h
deleted file mode 100644
index 8664f28d1..000000000
--- a/src/core/hle/service/time/time_zone_manager.h
+++ /dev/null
@@ -1,61 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7
8#include "common/common_types.h"
9#include "core/file_sys/vfs_types.h"
10#include "core/hle/service/time/clock_types.h"
11#include "core/hle/service/time/time_zone_types.h"
12
13namespace Service::Time::TimeZone {
14
15class TimeZoneManager final {
16public:
17 TimeZoneManager();
18 ~TimeZoneManager();
19
20 void SetTotalLocationNameCount(std::size_t value) {
21 total_location_name_count = value;
22 }
23
24 void SetLocationNames(std::vector<std::string> location_names) {
25 total_location_names = location_names;
26 }
27
28 void SetTimeZoneRuleVersion(const u128& value) {
29 time_zone_rule_version = value;
30 }
31
32 void MarkAsInitialized() {
33 is_initialized = true;
34 }
35
36 Result SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name,
37 FileSys::VirtualFile& vfs_file);
38 Result SetUpdatedTime(const Clock::SteadyClockTimePoint& value);
39 Result GetDeviceLocationName(TimeZone::LocationName& value) const;
40 Result GetTotalLocationNameCount(s32& count) const;
41 Result GetTimeZoneRuleVersion(u128& version) const;
42 Result LoadLocationNameList(std::vector<TimeZone::LocationName>& values) const;
43 Result ToCalendarTime(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) const;
44 Result ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const;
45 Result ParseTimeZoneRuleBinary(TimeZoneRule& rules, FileSys::VirtualFile& vfs_file) const;
46 Result ToPosixTime(const TimeZoneRule& rules, const CalendarTime& calendar_time,
47 s64& posix_time) const;
48 Result ToPosixTimeWithMyRule(const CalendarTime& calendar_time, s64& posix_time) const;
49
50private:
51 bool is_initialized{};
52 TimeZoneRule time_zone_rule{};
53 std::string device_location_name{"GMT"};
54 u128 time_zone_rule_version{};
55 std::size_t total_location_name_count{};
56 std::vector<std::string> total_location_names{};
57 Clock::SteadyClockTimePoint time_zone_update_time_point{
58 Clock::SteadyClockTimePoint::GetRandom()};
59};
60
61} // namespace Service::Time::TimeZone
diff --git a/src/core/hle/service/time/time_zone_service.cpp b/src/core/hle/service/time/time_zone_service.cpp
deleted file mode 100644
index 8171c82a5..000000000
--- a/src/core/hle/service/time/time_zone_service.cpp
+++ /dev/null
@@ -1,217 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "core/hle/service/ipc_helpers.h"
6#include "core/hle/service/time/time_zone_content_manager.h"
7#include "core/hle/service/time/time_zone_service.h"
8#include "core/hle/service/time/time_zone_types.h"
9
10namespace Service::Time {
11
12ITimeZoneService::ITimeZoneService(Core::System& system_,
13 TimeZone::TimeZoneContentManager& time_zone_manager_)
14 : ServiceFramework{system_, "ITimeZoneService"}, time_zone_content_manager{time_zone_manager_} {
15 static const FunctionInfo functions[] = {
16 {0, &ITimeZoneService::GetDeviceLocationName, "GetDeviceLocationName"},
17 {1, nullptr, "SetDeviceLocationName"},
18 {2, &ITimeZoneService::GetTotalLocationNameCount, "GetTotalLocationNameCount"},
19 {3, &ITimeZoneService::LoadLocationNameList, "LoadLocationNameList"},
20 {4, &ITimeZoneService::LoadTimeZoneRule, "LoadTimeZoneRule"},
21 {5, &ITimeZoneService::GetTimeZoneRuleVersion, "GetTimeZoneRuleVersion"},
22 {6, nullptr, "GetDeviceLocationNameAndUpdatedTime"},
23 {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"},
24 {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
25 {201, &ITimeZoneService::ToPosixTime, "ToPosixTime"},
26 {202, &ITimeZoneService::ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"},
27 };
28 RegisterHandlers(functions);
29}
30
31void ITimeZoneService::GetDeviceLocationName(HLERequestContext& ctx) {
32 LOG_DEBUG(Service_Time, "called");
33
34 TimeZone::LocationName location_name{};
35 if (const Result result{
36 time_zone_content_manager.GetTimeZoneManager().GetDeviceLocationName(location_name)};
37 result != ResultSuccess) {
38 IPC::ResponseBuilder rb{ctx, 2};
39 rb.Push(result);
40 return;
41 }
42
43 IPC::ResponseBuilder rb{ctx, (sizeof(location_name) / 4) + 2};
44 rb.Push(ResultSuccess);
45 rb.PushRaw(location_name);
46}
47
48void ITimeZoneService::GetTotalLocationNameCount(HLERequestContext& ctx) {
49 LOG_DEBUG(Service_Time, "called");
50
51 s32 count{};
52 if (const Result result{
53 time_zone_content_manager.GetTimeZoneManager().GetTotalLocationNameCount(count)};
54 result != ResultSuccess) {
55 IPC::ResponseBuilder rb{ctx, 2};
56 rb.Push(result);
57 return;
58 }
59
60 IPC::ResponseBuilder rb{ctx, 3};
61 rb.Push(ResultSuccess);
62 rb.Push(count);
63}
64
65void ITimeZoneService::LoadLocationNameList(HLERequestContext& ctx) {
66 LOG_DEBUG(Service_Time, "called");
67
68 std::vector<TimeZone::LocationName> location_names{};
69 if (const Result result{
70 time_zone_content_manager.GetTimeZoneManager().LoadLocationNameList(location_names)};
71 result != ResultSuccess) {
72 IPC::ResponseBuilder rb{ctx, 2};
73 rb.Push(result);
74 return;
75 }
76
77 ctx.WriteBuffer(location_names);
78 IPC::ResponseBuilder rb{ctx, 3};
79 rb.Push(ResultSuccess);
80 rb.Push(static_cast<s32>(location_names.size()));
81}
82void ITimeZoneService::GetTimeZoneRuleVersion(HLERequestContext& ctx) {
83 LOG_DEBUG(Service_Time, "called");
84
85 u128 rule_version{};
86 if (const Result result{
87 time_zone_content_manager.GetTimeZoneManager().GetTimeZoneRuleVersion(rule_version)};
88 result != ResultSuccess) {
89 IPC::ResponseBuilder rb{ctx, 2};
90 rb.Push(result);
91 return;
92 }
93
94 IPC::ResponseBuilder rb{ctx, 6};
95 rb.Push(ResultSuccess);
96 rb.PushRaw(rule_version);
97}
98
99void ITimeZoneService::LoadTimeZoneRule(HLERequestContext& ctx) {
100 IPC::RequestParser rp{ctx};
101 const auto raw_location_name{rp.PopRaw<std::array<u8, 0x24>>()};
102
103 std::string location_name;
104 for (const auto& byte : raw_location_name) {
105 // Strip extra bytes
106 if (byte == '\0') {
107 break;
108 }
109 location_name.push_back(byte);
110 }
111
112 LOG_DEBUG(Service_Time, "called, location_name={}", location_name);
113
114 TimeZone::TimeZoneRule time_zone_rule{};
115 const Result result{time_zone_content_manager.LoadTimeZoneRule(time_zone_rule, location_name)};
116
117 std::vector<u8> time_zone_rule_outbuffer(sizeof(TimeZone::TimeZoneRule));
118 std::memcpy(time_zone_rule_outbuffer.data(), &time_zone_rule, sizeof(TimeZone::TimeZoneRule));
119 ctx.WriteBuffer(time_zone_rule_outbuffer);
120
121 IPC::ResponseBuilder rb{ctx, 2};
122 rb.Push(result);
123}
124
125void ITimeZoneService::ToCalendarTime(HLERequestContext& ctx) {
126 IPC::RequestParser rp{ctx};
127 const auto posix_time{rp.Pop<s64>()};
128
129 LOG_DEBUG(Service_Time, "called, posix_time=0x{:016X}", posix_time);
130
131 TimeZone::TimeZoneRule time_zone_rule{};
132 const auto buffer{ctx.ReadBuffer()};
133 std::memcpy(&time_zone_rule, buffer.data(), buffer.size());
134
135 TimeZone::CalendarInfo calendar_info{};
136 if (const Result result{time_zone_content_manager.GetTimeZoneManager().ToCalendarTime(
137 time_zone_rule, posix_time, calendar_info)};
138 result != ResultSuccess) {
139 IPC::ResponseBuilder rb{ctx, 2};
140 rb.Push(result);
141 return;
142 }
143
144 IPC::ResponseBuilder rb{ctx, 2 + (sizeof(TimeZone::CalendarInfo) / 4)};
145 rb.Push(ResultSuccess);
146 rb.PushRaw(calendar_info);
147}
148
149void ITimeZoneService::ToCalendarTimeWithMyRule(HLERequestContext& ctx) {
150 IPC::RequestParser rp{ctx};
151 const auto posix_time{rp.Pop<s64>()};
152
153 LOG_DEBUG(Service_Time, "called, posix_time=0x{:016X}", posix_time);
154
155 TimeZone::CalendarInfo calendar_info{};
156 if (const Result result{
157 time_zone_content_manager.GetTimeZoneManager().ToCalendarTimeWithMyRules(
158 posix_time, calendar_info)};
159 result != ResultSuccess) {
160 IPC::ResponseBuilder rb{ctx, 2};
161 rb.Push(result);
162 return;
163 }
164
165 IPC::ResponseBuilder rb{ctx, 2 + (sizeof(TimeZone::CalendarInfo) / 4)};
166 rb.Push(ResultSuccess);
167 rb.PushRaw(calendar_info);
168}
169
170void ITimeZoneService::ToPosixTime(HLERequestContext& ctx) {
171 LOG_DEBUG(Service_Time, "called");
172
173 IPC::RequestParser rp{ctx};
174 const auto calendar_time{rp.PopRaw<TimeZone::CalendarTime>()};
175 TimeZone::TimeZoneRule time_zone_rule{};
176 std::memcpy(&time_zone_rule, ctx.ReadBuffer().data(), sizeof(TimeZone::TimeZoneRule));
177
178 s64 posix_time{};
179 if (const Result result{time_zone_content_manager.GetTimeZoneManager().ToPosixTime(
180 time_zone_rule, calendar_time, posix_time)};
181 result != ResultSuccess) {
182 IPC::ResponseBuilder rb{ctx, 2};
183 rb.Push(result);
184 return;
185 }
186
187 ctx.WriteBuffer(posix_time);
188
189 // TODO(bunnei): Handle multiple times
190 IPC::ResponseBuilder rb{ctx, 3};
191 rb.Push(ResultSuccess);
192 rb.PushRaw<u32>(1); // Number of times we're returning
193}
194
195void ITimeZoneService::ToPosixTimeWithMyRule(HLERequestContext& ctx) {
196 LOG_DEBUG(Service_Time, "called");
197
198 IPC::RequestParser rp{ctx};
199 const auto calendar_time{rp.PopRaw<TimeZone::CalendarTime>()};
200
201 s64 posix_time{};
202 if (const Result result{time_zone_content_manager.GetTimeZoneManager().ToPosixTimeWithMyRule(
203 calendar_time, posix_time)};
204 result != ResultSuccess) {
205 IPC::ResponseBuilder rb{ctx, 2};
206 rb.Push(result);
207 return;
208 }
209
210 ctx.WriteBuffer(posix_time);
211
212 IPC::ResponseBuilder rb{ctx, 3};
213 rb.Push(ResultSuccess);
214 rb.PushRaw<u32>(1); // Number of times we're returning
215}
216
217} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_zone_service.h b/src/core/hle/service/time/time_zone_service.h
deleted file mode 100644
index 952fcb0e2..000000000
--- a/src/core/hle/service/time/time_zone_service.h
+++ /dev/null
@@ -1,38 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Core {
9class System;
10}
11
12namespace Service::Time {
13
14namespace TimeZone {
15class TimeZoneContentManager;
16}
17
18class ITimeZoneService final : public ServiceFramework<ITimeZoneService> {
19public:
20 explicit ITimeZoneService(Core::System& system_,
21 TimeZone::TimeZoneContentManager& time_zone_manager_);
22
23private:
24 void GetDeviceLocationName(HLERequestContext& ctx);
25 void GetTotalLocationNameCount(HLERequestContext& ctx);
26 void LoadLocationNameList(HLERequestContext& ctx);
27 void GetTimeZoneRuleVersion(HLERequestContext& ctx);
28 void LoadTimeZoneRule(HLERequestContext& ctx);
29 void ToCalendarTime(HLERequestContext& ctx);
30 void ToCalendarTimeWithMyRule(HLERequestContext& ctx);
31 void ToPosixTime(HLERequestContext& ctx);
32 void ToPosixTimeWithMyRule(HLERequestContext& ctx);
33
34private:
35 TimeZone::TimeZoneContentManager& time_zone_content_manager;
36};
37
38} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_zone_types.h b/src/core/hle/service/time/time_zone_types.h
deleted file mode 100644
index eb4fb52d1..000000000
--- a/src/core/hle/service/time/time_zone_types.h
+++ /dev/null
@@ -1,86 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7
8#include "common/common_funcs.h"
9#include "common/common_types.h"
10#include "common/swap.h"
11
12namespace Service::Time::TimeZone {
13
14using LocationName = std::array<char, 0x24>;
15
16/// https://switchbrew.org/wiki/Glue_services#ttinfo
17struct TimeTypeInfo {
18 s32 gmt_offset{};
19 u8 is_dst{};
20 INSERT_PADDING_BYTES(3);
21 s32 abbreviation_list_index{};
22 u8 is_standard_time_daylight{};
23 u8 is_gmt{};
24 INSERT_PADDING_BYTES(2);
25};
26static_assert(sizeof(TimeTypeInfo) == 0x10, "TimeTypeInfo is incorrect size");
27
28/// https://switchbrew.org/wiki/Glue_services#TimeZoneRule
29struct TimeZoneRule {
30 s32 time_count{};
31 s32 type_count{};
32 s32 char_count{};
33 u8 go_back{};
34 u8 go_ahead{};
35 INSERT_PADDING_BYTES(2);
36 std::array<s64, 1000> ats{};
37 std::array<s8, 1000> types{};
38 std::array<TimeTypeInfo, 128> ttis{};
39 std::array<char, 512> chars{};
40 s32 default_type{};
41 INSERT_PADDING_BYTES(0x12C4);
42};
43static_assert(sizeof(TimeZoneRule) == 0x4000, "TimeZoneRule is incorrect size");
44
45/// https://switchbrew.org/wiki/Glue_services#CalendarAdditionalInfo
46struct CalendarAdditionalInfo {
47 u32 day_of_week;
48 u32 day_of_year;
49 std::array<char, 8> timezone_name;
50 u32 is_dst;
51 s32 gmt_offset;
52};
53static_assert(sizeof(CalendarAdditionalInfo) == 0x18, "CalendarAdditionalInfo is incorrect size");
54
55/// https://switchbrew.org/wiki/Glue_services#CalendarTime
56struct CalendarTime {
57 s16 year;
58 s8 month;
59 s8 day;
60 s8 hour;
61 s8 minute;
62 s8 second;
63 INSERT_PADDING_BYTES_NOINIT(1);
64};
65static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime is incorrect size");
66
67struct CalendarInfo {
68 CalendarTime time;
69 CalendarAdditionalInfo additional_info;
70};
71static_assert(sizeof(CalendarInfo) == 0x20, "CalendarInfo is incorrect size");
72
73struct TzifHeader {
74 u32_be magic{};
75 u8 version{};
76 INSERT_PADDING_BYTES(15);
77 s32_be ttis_gmt_count{};
78 s32_be ttis_std_count{};
79 s32_be leap_count{};
80 s32_be time_count{};
81 s32_be type_count{};
82 s32_be char_count{};
83};
84static_assert(sizeof(TzifHeader) == 0x2C, "TzifHeader is incorrect size");
85
86} // namespace Service::Time::TimeZone
diff --git a/src/tests/video_core/memory_tracker.cpp b/src/tests/video_core/memory_tracker.cpp
index 618793668..f15fefe2d 100644
--- a/src/tests/video_core/memory_tracker.cpp
+++ b/src/tests/video_core/memory_tracker.cpp
@@ -546,4 +546,4 @@ TEST_CASE("MemoryTracker: Cached write downloads") {
546 REQUIRE(!memory_track->IsRegionGpuModified(c + PAGE, PAGE)); 546 REQUIRE(!memory_track->IsRegionGpuModified(c + PAGE, PAGE));
547 memory_track->MarkRegionAsCpuModified(c, WORD); 547 memory_track->MarkRegionAsCpuModified(c, WORD);
548 REQUIRE(rasterizer.Count() == 0); 548 REQUIRE(rasterizer.Count() == 0);
549} \ No newline at end of file 549}
diff --git a/src/video_core/query_cache/query_base.h b/src/video_core/query_cache/query_base.h
index 1d786b3a7..68f1d38c1 100644
--- a/src/video_core/query_cache/query_base.h
+++ b/src/video_core/query_cache/query_base.h
@@ -67,4 +67,4 @@ public:
67 size_t size_slots{}; 67 size_t size_slots{};
68}; 68};
69 69
70} // namespace VideoCommon \ No newline at end of file 70} // namespace VideoCommon
diff --git a/src/video_core/query_cache/query_cache_base.h b/src/video_core/query_cache/query_cache_base.h
index 07be421c6..5a88e7f0a 100644
--- a/src/video_core/query_cache/query_cache_base.h
+++ b/src/video_core/query_cache/query_cache_base.h
@@ -178,4 +178,4 @@ protected:
178 std::unique_ptr<QueryCacheBaseImpl> impl; 178 std::unique_ptr<QueryCacheBaseImpl> impl;
179}; 179};
180 180
181} // namespace VideoCommon \ No newline at end of file 181} // namespace VideoCommon
diff --git a/src/video_core/query_cache/query_stream.h b/src/video_core/query_cache/query_stream.h
index d9040acd2..1d11b1275 100644
--- a/src/video_core/query_cache/query_stream.h
+++ b/src/video_core/query_cache/query_stream.h
@@ -146,4 +146,4 @@ protected:
146 std::deque<size_t> old_queries; 146 std::deque<size_t> old_queries;
147}; 147};
148 148
149} // namespace VideoCommon \ No newline at end of file 149} // namespace VideoCommon
diff --git a/src/video_core/query_cache/types.h b/src/video_core/query_cache/types.h
index e9226bbfc..0c6a882e2 100644
--- a/src/video_core/query_cache/types.h
+++ b/src/video_core/query_cache/types.h
@@ -71,4 +71,4 @@ enum class ReductionOp : u32 {
71 MaxReductionOp, 71 MaxReductionOp,
72}; 72};
73 73
74} // namespace VideoCommon \ No newline at end of file 74} // namespace VideoCommon
diff --git a/src/video_core/rasterizer_download_area.h b/src/video_core/rasterizer_download_area.h
index 2d7425c79..d28826043 100644
--- a/src/video_core/rasterizer_download_area.h
+++ b/src/video_core/rasterizer_download_area.h
@@ -13,4 +13,4 @@ struct RasterizerDownloadArea {
13 bool preemtive; 13 bool preemtive;
14}; 14};
15 15
16} // namespace VideoCore \ No newline at end of file 16} // namespace VideoCore
diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.h b/src/video_core/renderer_vulkan/vk_descriptor_pool.h
index bd6696b07..4aada5a00 100644
--- a/src/video_core/renderer_vulkan/vk_descriptor_pool.h
+++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.h
@@ -84,4 +84,4 @@ private:
84 std::vector<std::unique_ptr<DescriptorBank>> banks; 84 std::vector<std::unique_ptr<DescriptorBank>> banks;
85}; 85};
86 86
87} // namespace Vulkan \ No newline at end of file 87} // namespace Vulkan
diff --git a/src/video_core/texture_cache/accelerated_swizzle.cpp b/src/video_core/texture_cache/accelerated_swizzle.cpp
index 70be1657e..4c3f724d7 100644
--- a/src/video_core/texture_cache/accelerated_swizzle.cpp
+++ b/src/video_core/texture_cache/accelerated_swizzle.cpp
@@ -66,4 +66,4 @@ BlockLinearSwizzle3DParams MakeBlockLinearSwizzle3DParams(const SwizzleParameter
66 }; 66 };
67} 67}
68 68
69} // namespace VideoCommon::Accelerated \ No newline at end of file 69} // namespace VideoCommon::Accelerated
diff --git a/src/yuzu/configuration/configure_ringcon.cpp b/src/yuzu/configuration/configure_ringcon.cpp
index 3a7f6101d..9fd094ab6 100644
--- a/src/yuzu/configuration/configure_ringcon.cpp
+++ b/src/yuzu/configuration/configure_ringcon.cpp
@@ -494,4 +494,4 @@ QString ConfigureRingController::AnalogToText(const Common::ParamPackage& param,
494 } 494 }
495 495
496 return QObject::tr("[unknown]"); 496 return QObject::tr("[unknown]");
497} \ No newline at end of file 497}
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index b0b84f967..e193b5f95 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -12,9 +12,10 @@
12#include <QGraphicsItem> 12#include <QGraphicsItem>
13#include <QLineEdit> 13#include <QLineEdit>
14#include <QMessageBox> 14#include <QMessageBox>
15#include <QSpinBox>
16
15#include "common/settings.h" 17#include "common/settings.h"
16#include "core/core.h" 18#include "core/core.h"
17#include "core/hle/service/time/time_manager.h"
18#include "ui_configure_system.h" 19#include "ui_configure_system.h"
19#include "yuzu/configuration/configuration_shared.h" 20#include "yuzu/configuration/configuration_shared.h"
20#include "yuzu/configuration/configure_system.h" 21#include "yuzu/configuration/configure_system.h"
@@ -49,6 +50,11 @@ ConfigureSystem::ConfigureSystem(Core::System& system_,
49 : Tab(group_, parent), ui{std::make_unique<Ui::ConfigureSystem>()}, system{system_} { 50 : Tab(group_, parent), ui{std::make_unique<Ui::ConfigureSystem>()}, system{system_} {
50 ui->setupUi(this); 51 ui->setupUi(this);
51 52
53 const auto posix_time = std::chrono::system_clock::now().time_since_epoch();
54 const auto current_time_s =
55 std::chrono::duration_cast<std::chrono::seconds>(posix_time).count();
56 previous_time = current_time_s + Settings::values.custom_rtc_offset.GetValue();
57
52 Setup(builder); 58 Setup(builder);
53 59
54 const auto locale_check = [this]() { 60 const auto locale_check = [this]() {
@@ -64,13 +70,28 @@ ConfigureSystem::ConfigureSystem(Core::System& system_,
64 } 70 }
65 }; 71 };
66 72
73 const auto update_date_offset = [this]() {
74 if (!checkbox_rtc->isChecked()) {
75 return;
76 }
77 auto offset = date_rtc_offset->value();
78 offset += date_rtc->dateTime().toSecsSinceEpoch() - previous_time;
79 previous_time = date_rtc->dateTime().toSecsSinceEpoch();
80 date_rtc_offset->setValue(offset);
81 };
82 const auto update_rtc_date = [this]() { UpdateRtcTime(); };
83
67 connect(combo_language, qOverload<int>(&QComboBox::currentIndexChanged), this, locale_check); 84 connect(combo_language, qOverload<int>(&QComboBox::currentIndexChanged), this, locale_check);
68 connect(combo_region, qOverload<int>(&QComboBox::currentIndexChanged), this, locale_check); 85 connect(combo_region, qOverload<int>(&QComboBox::currentIndexChanged), this, locale_check);
86 connect(checkbox_rtc, qOverload<int>(&QCheckBox::stateChanged), this, update_rtc_date);
87 connect(date_rtc_offset, qOverload<int>(&QSpinBox::valueChanged), this, update_rtc_date);
88 connect(date_rtc, &QDateTimeEdit::dateTimeChanged, this, update_date_offset);
69 89
70 ui->label_warn_invalid_locale->setVisible(false); 90 ui->label_warn_invalid_locale->setVisible(false);
71 locale_check(); 91 locale_check();
72 92
73 SetConfiguration(); 93 SetConfiguration();
94 UpdateRtcTime();
74} 95}
75 96
76ConfigureSystem::~ConfigureSystem() = default; 97ConfigureSystem::~ConfigureSystem() = default;
@@ -120,14 +141,28 @@ void ConfigureSystem::Setup(const ConfigurationShared::Builder& builder) {
120 continue; 141 continue;
121 } 142 }
122 143
144 // Keep track of the region_index (and language_index) combobox to validate the selected
145 // settings
123 if (setting->Id() == Settings::values.region_index.Id()) { 146 if (setting->Id() == Settings::values.region_index.Id()) {
124 // Keep track of the region_index (and language_index) combobox to validate the selected
125 // settings
126 combo_region = widget->combobox; 147 combo_region = widget->combobox;
127 } else if (setting->Id() == Settings::values.language_index.Id()) { 148 }
149
150 if (setting->Id() == Settings::values.language_index.Id()) {
128 combo_language = widget->combobox; 151 combo_language = widget->combobox;
129 } 152 }
130 153
154 if (setting->Id() == Settings::values.custom_rtc.Id()) {
155 checkbox_rtc = widget->checkbox;
156 }
157
158 if (setting->Id() == Settings::values.custom_rtc.Id()) {
159 date_rtc = widget->date_time_edit;
160 }
161
162 if (setting->Id() == Settings::values.custom_rtc_offset.Id()) {
163 date_rtc_offset = widget->spinbox;
164 }
165
131 switch (setting->GetCategory()) { 166 switch (setting->GetCategory()) {
132 case Settings::Category::Core: 167 case Settings::Category::Core:
133 core_hold.emplace(setting->Id(), widget); 168 core_hold.emplace(setting->Id(), widget);
@@ -147,6 +182,19 @@ void ConfigureSystem::Setup(const ConfigurationShared::Builder& builder) {
147 } 182 }
148} 183}
149 184
185void ConfigureSystem::UpdateRtcTime() {
186 const auto posix_time = std::chrono::system_clock::now().time_since_epoch();
187 previous_time = std::chrono::duration_cast<std::chrono::seconds>(posix_time).count();
188 date_rtc_offset->setEnabled(checkbox_rtc->isChecked());
189
190 if (checkbox_rtc->isChecked()) {
191 previous_time += date_rtc_offset->value();
192 }
193
194 const auto date = QDateTime::fromSecsSinceEpoch(previous_time);
195 date_rtc->setDateTime(date);
196}
197
150void ConfigureSystem::SetConfiguration() {} 198void ConfigureSystem::SetConfiguration() {}
151 199
152void ConfigureSystem::ApplyConfiguration() { 200void ConfigureSystem::ApplyConfiguration() {
@@ -154,4 +202,5 @@ void ConfigureSystem::ApplyConfiguration() {
154 for (const auto& func : apply_funcs) { 202 for (const auto& func : apply_funcs) {
155 func(powered_on); 203 func(powered_on);
156 } 204 }
205 UpdateRtcTime();
157} 206}
diff --git a/src/yuzu/configuration/configure_system.h b/src/yuzu/configuration/configure_system.h
index eab99a48a..4334211f9 100644
--- a/src/yuzu/configuration/configure_system.h
+++ b/src/yuzu/configuration/configure_system.h
@@ -43,6 +43,8 @@ private:
43 43
44 void Setup(const ConfigurationShared::Builder& builder); 44 void Setup(const ConfigurationShared::Builder& builder);
45 45
46 void UpdateRtcTime();
47
46 std::vector<std::function<void(bool)>> apply_funcs{}; 48 std::vector<std::function<void(bool)>> apply_funcs{};
47 49
48 std::unique_ptr<Ui::ConfigureSystem> ui; 50 std::unique_ptr<Ui::ConfigureSystem> ui;
@@ -52,4 +54,8 @@ private:
52 54
53 QComboBox* combo_region; 55 QComboBox* combo_region;
54 QComboBox* combo_language; 56 QComboBox* combo_language;
57 QCheckBox* checkbox_rtc;
58 QDateTimeEdit* date_rtc;
59 QSpinBox* date_rtc_offset;
60 u64 previous_time;
55}; 61};
diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp
index 922eb1b1a..ed9c7d859 100644
--- a/src/yuzu/configuration/shared_translation.cpp
+++ b/src/yuzu/configuration/shared_translation.cpp
@@ -143,8 +143,10 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
143 INSERT(Settings, rng_seed, tr("RNG Seed"), QStringLiteral()); 143 INSERT(Settings, rng_seed, tr("RNG Seed"), QStringLiteral());
144 INSERT(Settings, rng_seed_enabled, QStringLiteral(), QStringLiteral()); 144 INSERT(Settings, rng_seed_enabled, QStringLiteral(), QStringLiteral());
145 INSERT(Settings, device_name, tr("Device Name"), QStringLiteral()); 145 INSERT(Settings, device_name, tr("Device Name"), QStringLiteral());
146 INSERT(Settings, custom_rtc, tr("Custom RTC"), QStringLiteral()); 146 INSERT(Settings, custom_rtc, tr("Custom RTC Date:"), QStringLiteral());
147 INSERT(Settings, custom_rtc_enabled, QStringLiteral(), QStringLiteral()); 147 INSERT(Settings, custom_rtc_enabled, QStringLiteral(), QStringLiteral());
148 INSERT(Settings, custom_rtc_offset, QStringLiteral(" "),
149 QStringLiteral("The number of seconds from the current unix time"));
148 INSERT(Settings, language_index, tr("Language:"), 150 INSERT(Settings, language_index, tr("Language:"),
149 tr("Note: this can be overridden when region setting is auto-select")); 151 tr("Note: this can be overridden when region setting is auto-select"));
150 INSERT(Settings, region_index, tr("Region:"), QStringLiteral()); 152 INSERT(Settings, region_index, tr("Region:"), QStringLiteral());
diff --git a/src/yuzu/debugger/console.h b/src/yuzu/debugger/console.h
index fdb7d174c..2491d1ec1 100644
--- a/src/yuzu/debugger/console.h
+++ b/src/yuzu/debugger/console.h
@@ -10,4 +10,4 @@ namespace Debugger {
10 * get a real qt logging window which would work for all platforms. 10 * get a real qt logging window which would work for all platforms.
11 */ 11 */
12void ToggleConsole(); 12void ToggleConsole();
13} // namespace Debugger \ No newline at end of file 13} // namespace Debugger