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