summaryrefslogtreecommitdiffstats
path: root/src/core/hle/service/time/time_manager.cpp
blob: b4dfe45e5ef5305186eae335b85802f2cca12551 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.

#include <chrono>
#include <ctime>

#include "common/time_zone.h"
#include "core/hle/service/time/ephemeral_network_system_clock_context_writer.h"
#include "core/hle/service/time/local_system_clock_context_writer.h"
#include "core/hle/service/time/network_system_clock_context_writer.h"
#include "core/hle/service/time/time_manager.h"
#include "core/settings.h"

namespace Service::Time {

constexpr Clock::TimeSpanType standard_network_clock_accuracy{0x0009356907420000ULL};

static std::chrono::seconds GetSecondsSinceEpoch() {
    return std::chrono::duration_cast<std::chrono::seconds>(
               std::chrono::system_clock::now().time_since_epoch()) +
           Settings::values.custom_rtc_differential;
}

static s64 GetExternalTimeZoneOffset() {
    // With "auto" timezone setting, we use the external system's timezone offset
    if (Settings::GetTimeZoneString() == "auto") {
        return Common::TimeZone::GetCurrentOffsetSeconds().count();
    }
    return 0;
}

static s64 GetExternalRtcValue() {
    return GetSecondsSinceEpoch().count() + GetExternalTimeZoneOffset();
}

TimeManager::TimeManager(Core::System& system)
    : shared_memory{system}, standard_local_system_clock_core{standard_steady_clock_core},
      standard_network_system_clock_core{standard_steady_clock_core},
      standard_user_system_clock_core{standard_local_system_clock_core,
                                      standard_network_system_clock_core, system},
      ephemeral_network_system_clock_core{tick_based_steady_clock_core},
      local_system_clock_context_writer{
          std::make_shared<Clock::LocalSystemClockContextWriter>(shared_memory)},
      network_system_clock_context_writer{
          std::make_shared<Clock::NetworkSystemClockContextWriter>(shared_memory)},
      ephemeral_network_system_clock_context_writer{
          std::make_shared<Clock::EphemeralNetworkSystemClockContextWriter>()},
      time_zone_content_manager{*this, system} {

    const auto system_time{Clock::TimeSpanType::FromSeconds(GetExternalRtcValue())};
    SetupStandardSteadyClock(system, Common::UUID::Generate(), system_time, {}, {});
    SetupStandardLocalSystemClock(system, {}, system_time.ToSeconds());
    SetupStandardNetworkSystemClock({}, standard_network_clock_accuracy);
    SetupStandardUserSystemClock(system, {}, Clock::SteadyClockTimePoint::GetRandom());
    SetupEphemeralNetworkSystemClock();
}

TimeManager::~TimeManager() = default;

void TimeManager::SetupTimeZoneManager(std::string location_name,
                                       Clock::SteadyClockTimePoint time_zone_updated_time_point,
                                       std::size_t total_location_name_count,
                                       u128 time_zone_rule_version,
                                       FileSys::VirtualFile& vfs_file) {
    if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule(
            location_name, vfs_file) != RESULT_SUCCESS) {
        UNREACHABLE();
        return;
    }

    time_zone_content_manager.GetTimeZoneManager().SetUpdatedTime(time_zone_updated_time_point);
    time_zone_content_manager.GetTimeZoneManager().SetTotalLocationNameCount(
        total_location_name_count);
    time_zone_content_manager.GetTimeZoneManager().SetTimeZoneRuleVersion(time_zone_rule_version);
    time_zone_content_manager.GetTimeZoneManager().MarkAsInitialized();
}

void TimeManager::SetupStandardSteadyClock(Core::System& system, Common::UUID clock_source_id,
                                           Clock::TimeSpanType setup_value,
                                           Clock::TimeSpanType internal_offset,
                                           bool is_rtc_reset_detected) {
    standard_steady_clock_core.SetClockSourceId(clock_source_id);
    standard_steady_clock_core.SetSetupValue(setup_value);
    standard_steady_clock_core.SetInternalOffset(internal_offset);
    standard_steady_clock_core.MarkAsInitialized();

    const auto current_time_point{standard_steady_clock_core.GetCurrentRawTimePoint(system)};
    shared_memory.SetupStandardSteadyClock(system, clock_source_id, current_time_point);
}

void TimeManager::SetupStandardLocalSystemClock(Core::System& system,
                                                Clock::SystemClockContext clock_context,
                                                s64 posix_time) {
    standard_local_system_clock_core.SetUpdateCallbackInstance(local_system_clock_context_writer);

    const auto current_time_point{
        standard_local_system_clock_core.GetSteadyClockCore().GetCurrentTimePoint(system)};
    if (current_time_point.clock_source_id == clock_context.steady_time_point.clock_source_id) {
        standard_local_system_clock_core.SetSystemClockContext(clock_context);
    } else {
        if (standard_local_system_clock_core.SetCurrentTime(system, posix_time) != RESULT_SUCCESS) {
            UNREACHABLE();
            return;
        }
    }

    standard_local_system_clock_core.MarkAsInitialized();
}

void TimeManager::SetupStandardNetworkSystemClock(Clock::SystemClockContext clock_context,
                                                  Clock::TimeSpanType sufficient_accuracy) {
    standard_network_system_clock_core.SetUpdateCallbackInstance(
        network_system_clock_context_writer);

    if (standard_network_system_clock_core.SetSystemClockContext(clock_context) != RESULT_SUCCESS) {
        UNREACHABLE();
        return;
    }

    standard_network_system_clock_core.SetStandardNetworkClockSufficientAccuracy(
        sufficient_accuracy);
    standard_network_system_clock_core.MarkAsInitialized();
}

void TimeManager::SetupStandardUserSystemClock(
    Core::System& system, bool is_automatic_correction_enabled,
    Clock::SteadyClockTimePoint steady_clock_time_point) {
    if (standard_user_system_clock_core.SetAutomaticCorrectionEnabled(
            system, is_automatic_correction_enabled) != RESULT_SUCCESS) {
        UNREACHABLE();
        return;
    }

    standard_user_system_clock_core.SetAutomaticCorrectionUpdatedTime(steady_clock_time_point);
    standard_user_system_clock_core.MarkAsInitialized();
    shared_memory.SetAutomaticCorrectionEnabled(is_automatic_correction_enabled);
}

void TimeManager::SetupEphemeralNetworkSystemClock() {
    ephemeral_network_system_clock_core.SetUpdateCallbackInstance(
        ephemeral_network_system_clock_context_writer);
    ephemeral_network_system_clock_core.MarkAsInitialized();
}

} // namespace Service::Time