code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/core.h"
#include "core/hle/service/psc/time/alarms.h"
#include "core/hle/service/psc/time/manager.h"

namespace Service::PSC::Time {
Alarm::Alarm(Core::System& system, KernelHelpers::ServiceContext& ctx, AlarmType type)
    : m_ctx{ctx}, m_event{ctx.CreateEvent("Psc:Alarm:Event")} {
    m_event->Clear();

    switch (type) {
    case WakeupAlarm:
        m_priority = 1;
        break;
    case BackgroundTaskAlarm:
        m_priority = 0;
        break;
    default:
        UNREACHABLE();
        return;
    }
}

Alarm::~Alarm() {
    m_ctx.CloseEvent(m_event);
}

Alarms::Alarms(Core::System& system, StandardSteadyClockCore& steady_clock,
               PowerStateRequestManager& power_state_request_manager)
    : m_system{system}, m_ctx{system, "Psc:Alarms"}, m_steady_clock{steady_clock},
      m_power_state_request_manager{power_state_request_manager}, m_event{m_ctx.CreateEvent(
                                                                      "Psc:Alarms:Event")} {}

Alarms::~Alarms() {
    m_ctx.CloseEvent(m_event);
}

Result Alarms::Enable(Alarm& alarm, s64 time) {
    R_UNLESS(m_steady_clock.IsInitialized(), ResultClockUninitialized);

    std::scoped_lock l{m_mutex};
    R_UNLESS(alarm.IsLinked(), ResultAlarmNotRegistered);

    auto time_ns{time + m_steady_clock.GetRawTime()};
    auto one_second_ns{
        std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
    time_ns = Common::AlignUp(time_ns, one_second_ns);
    alarm.SetAlertTime(time_ns);

    Insert(alarm);
    R_RETURN(UpdateClosestAndSignal());
}

void Alarms::Disable(Alarm& alarm) {
    std::scoped_lock l{m_mutex};
    if (!alarm.IsLinked()) {
        return;
    }

    Erase(alarm);
    UpdateClosestAndSignal();
}

void Alarms::CheckAndSignal() {
    std::scoped_lock l{m_mutex};
    if (m_alarms.empty()) {
        return;
    }

    bool alarm_signalled{false};
    for (auto& alarm : m_alarms) {
        if (m_steady_clock.GetRawTime() >= alarm.GetAlertTime()) {
            alarm.Signal();
            alarm.Lock();
            Erase(alarm);

            m_power_state_request_manager.UpdatePendingPowerStateRequestPriority(
                alarm.GetPriority());
            alarm_signalled = true;
        }
    }

    if (!alarm_signalled) {
        return;
    }

    m_power_state_request_manager.SignalPowerStateRequestAvailability();
    UpdateClosestAndSignal();
}

bool Alarms::GetClosestAlarm(Alarm** out_alarm) {
    std::scoped_lock l{m_mutex};
    auto alarm = m_alarms.empty() ? nullptr : std::addressof(m_alarms.front());
    *out_alarm = alarm;
    return alarm != nullptr;
}

void Alarms::Insert(Alarm& alarm) {
    // Alarms are sorted by alert time, then priority
    auto it{m_alarms.begin()};
    while (it != m_alarms.end()) {
        if (alarm.GetAlertTime() < it->GetAlertTime() ||
            (alarm.GetAlertTime() == it->GetAlertTime() &&
             alarm.GetPriority() < it->GetPriority())) {
            m_alarms.insert(it, alarm);
            return;
        }
        it++;
    }

    m_alarms.push_back(alarm);
}

void Alarms::Erase(Alarm& alarm) {
    m_alarms.erase(m_alarms.iterator_to(alarm));
}

Result Alarms::UpdateClosestAndSignal() {
    m_closest_alarm = m_alarms.empty() ? nullptr : std::addressof(m_alarms.front());
    R_SUCCEED_IF(m_closest_alarm == nullptr);

    m_event->Signal();

    R_SUCCEED();
}

IAlarmService::IAlarmService(Core::System& system_, std::shared_ptr<TimeManager> manager)
    : ServiceFramework{system_, "time:al"}, m_system{system}, m_alarms{manager->m_alarms} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {0, &IAlarmService::CreateWakeupAlarm, "CreateWakeupAlarm"},
        {1, &IAlarmService::CreateBackgroundTaskAlarm, "CreateBackgroundTaskAlarm"},
    };
    // clang-format on
    RegisterHandlers(functions);
}

void IAlarmService::CreateWakeupAlarm(HLERequestContext& ctx) {
    LOG_DEBUG(Service_Time, "called.");

    IPC::ResponseBuilder rb{ctx, 2, 0, 1};
    rb.Push(ResultSuccess);
    rb.PushIpcInterface<ISteadyClockAlarm>(system, m_alarms, AlarmType::WakeupAlarm);
}

void IAlarmService::CreateBackgroundTaskAlarm(HLERequestContext& ctx) {
    LOG_DEBUG(Service_Time, "called.");

    IPC::ResponseBuilder rb{ctx, 2, 0, 1};
    rb.Push(ResultSuccess);
    rb.PushIpcInterface<ISteadyClockAlarm>(system, m_alarms, AlarmType::BackgroundTaskAlarm);
}

ISteadyClockAlarm::ISteadyClockAlarm(Core::System& system_, Alarms& alarms, AlarmType type)
    : ServiceFramework{system_, "ISteadyClockAlarm"}, m_ctx{system, "Psc:ISteadyClockAlarm"},
      m_alarms{alarms}, m_alarm{system, m_ctx, type} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {0,  &ISteadyClockAlarm::GetAlarmEvent, "GetAlarmEvent"},
        {1,  &ISteadyClockAlarm::Enable, "Enable"},
        {2,  &ISteadyClockAlarm::Disable, "Disable"},
        {3,  &ISteadyClockAlarm::IsEnabled, "IsEnabled"},
        {10, nullptr, "CreateWakeLock"},
        {11, nullptr, "DestroyWakeLock"},
    };
    // clang-format on
    RegisterHandlers(functions);
}

void ISteadyClockAlarm::GetAlarmEvent(HLERequestContext& ctx) {
    LOG_DEBUG(Service_Time, "called.");

    IPC::ResponseBuilder rb{ctx, 2, 1};
    rb.Push(ResultSuccess);
    rb.PushCopyObjects(m_alarm.GetEventHandle());
}

void ISteadyClockAlarm::Enable(HLERequestContext& ctx) {
    LOG_DEBUG(Service_Time, "called.");

    IPC::RequestParser rp{ctx};
    auto time{rp.Pop<s64>()};

    auto res = m_alarms.Enable(m_alarm, time);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(res);
}

void ISteadyClockAlarm::Disable(HLERequestContext& ctx) {
    LOG_DEBUG(Service_Time, "called.");

    m_alarms.Disable(m_alarm);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void ISteadyClockAlarm::IsEnabled(HLERequestContext& ctx) {
    LOG_DEBUG(Service_Time, "called.");

    IPC::ResponseBuilder rb{ctx, 3};
    rb.Push(ResultSuccess);
    rb.Push<bool>(m_alarm.IsLinked());
}

} // namespace Service::PSC::Time


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <mutex>

#include "core/hle/kernel/k_event.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/psc/time/clocks/standard_steady_clock_core.h"
#include "core/hle/service/psc/time/common.h"
#include "core/hle/service/psc/time/power_state_request_manager.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"

namespace Core {
class System;
}

namespace Service::PSC::Time {
class TimeManager;

enum AlarmType : u32 {
    WakeupAlarm = 0,
    BackgroundTaskAlarm = 1,
};

struct Alarm : public Common::IntrusiveListBaseNode<Alarm> {
    using AlarmList = Common::IntrusiveListBaseTraits<Alarm>::ListType;

    Alarm(Core::System& system, KernelHelpers::ServiceContext& ctx, AlarmType type);
    ~Alarm();

    Kernel::KReadableEvent& GetEventHandle() {
        return m_event->GetReadableEvent();
    }

    s64 GetAlertTime() const {
        return m_alert_time;
    }

    void SetAlertTime(s64 time) {
        m_alert_time = time;
    }

    u32 GetPriority() const {
        return m_priority;
    }

    void Signal() {
        m_event->Signal();
    }

    Result Lock() {
        // TODO
        // if (m_lock_service) {
        //     return m_lock_service->Lock();
        // }
        R_SUCCEED();
    }

    KernelHelpers::ServiceContext& m_ctx;

    u32 m_priority;
    Kernel::KEvent* m_event{};
    s64 m_alert_time{};
    // TODO
    // nn::psc::sf::IPmStateLock* m_lock_service{};
};

class Alarms {
public:
    explicit Alarms(Core::System& system, StandardSteadyClockCore& steady_clock,
                    PowerStateRequestManager& power_state_request_manager);
    ~Alarms();

    Kernel::KEvent& GetEvent() {
        return *m_event;
    }

    s64 GetRawTime() {
        return m_steady_clock.GetRawTime();
    }

    Result Enable(Alarm& alarm, s64 time);
    void Disable(Alarm& alarm);
    void CheckAndSignal();
    bool GetClosestAlarm(Alarm** out_alarm);

private:
    void Insert(Alarm& alarm);
    void Erase(Alarm& alarm);
    Result UpdateClosestAndSignal();

    Core::System& m_system;
    KernelHelpers::ServiceContext m_ctx;

    StandardSteadyClockCore& m_steady_clock;
    PowerStateRequestManager& m_power_state_request_manager;
    Alarm::AlarmList m_alarms;
    Kernel::KEvent* m_event{};
    Alarm* m_closest_alarm{};
    std::mutex m_mutex;
};

class IAlarmService final : public ServiceFramework<IAlarmService> {
public:
    explicit IAlarmService(Core::System& system, std::shared_ptr<TimeManager> manager);

    ~IAlarmService() override = default;

private:
    void CreateWakeupAlarm(HLERequestContext& ctx);
    void CreateBackgroundTaskAlarm(HLERequestContext& ctx);

    Core::System& m_system;
    Alarms& m_alarms;
};

class ISteadyClockAlarm final : public ServiceFramework<ISteadyClockAlarm> {
public:
    explicit ISteadyClockAlarm(Core::System& system, Alarms& alarms, AlarmType type);

    ~ISteadyClockAlarm() override = default;

private:
    void GetAlarmEvent(HLERequestContext& ctx);
    void Enable(HLERequestContext& ctx);
    void Disable(HLERequestContext& ctx);
    void IsEnabled(HLERequestContext& ctx);

    KernelHelpers::ServiceContext m_ctx;

    Alarms& m_alarms;
    Alarm m_alarm;
};

} // namespace Service::PSC::Time

repo_name: litucks/torzu

license: mit


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/core.h"
#include "core/hle/service/psc/time/clocks/context_writers.h"

namespace Service::PSC::Time {

void ContextWriter::SignalAllNodes() {
    std::scoped_lock l{m_mutex};
    for (auto& operation : m_operation_events) {
        operation.m_event->Signal();
    }
}

void ContextWriter::Link(OperationEvent& operation_event) {
    std::scoped_lock l{m_mutex};
    m_operation_events.push_back(operation_event);
}

LocalSystemClockContextWriter::LocalSystemClockContextWriter(Core::System& system,
                                                             SharedMemory& shared_memory)
    : m_system{system}, m_shared_memory{shared_memory} {}

Result LocalSystemClockContextWriter::Write(const SystemClockContext& context) {
    if (m_in_use) {
        R_SUCCEED_IF(context == m_context);
        m_context = context;
    } else {
        m_context = context;
        m_in_use = true;
    }

    m_shared_memory.SetLocalSystemContext(context);

    SignalAllNodes();

    R_SUCCEED();
}

NetworkSystemClockContextWriter::NetworkSystemClockContextWriter(Core::System& system,
                                                                 SharedMemory& shared_memory,
                                                                 SystemClockCore& system_clock)
    : m_system{system}, m_shared_memory{shared_memory}, m_system_clock{system_clock} {}

Result NetworkSystemClockContextWriter::Write(const SystemClockContext& context) {
    s64 time{};
    [[maybe_unused]] auto res = m_system_clock.GetCurrentTime(&time);

    if (m_in_use) {
        R_SUCCEED_IF(context == m_context);
        m_context = context;
    } else {
        m_context = context;
        m_in_use = true;
    }

    m_shared_memory.SetNetworkSystemContext(context);

    SignalAllNodes();

    R_SUCCEED();
}

EphemeralNetworkSystemClockContextWriter::EphemeralNetworkSystemClockContextWriter(
    Core::System& system)
    : m_system{system} {}

Result EphemeralNetworkSystemClockContextWriter::Write(const SystemClockContext& context) {
    if (m_in_use) {
        R_SUCCEED_IF(context == m_context);
        m_context = context;
    } else {
        m_context = context;
        m_in_use = true;
    }

    SignalAllNodes();

    R_SUCCEED();
}

} // namespace Service::PSC::Time


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <list>

#include "common/common_types.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/psc/time/clocks/system_clock_core.h"
#include "core/hle/service/psc/time/common.h"
#include "core/hle/service/psc/time/shared_memory.h"

namespace Core {
class System;
}

namespace Service::PSC::Time {

class ContextWriter {
private:
    using OperationEventList = Common::IntrusiveListBaseTraits<OperationEvent>::ListType;

public:
    virtual ~ContextWriter() = default;

    virtual Result Write(const SystemClockContext& context) = 0;
    void SignalAllNodes();
    void Link(OperationEvent& operation_event);

private:
    OperationEventList m_operation_events;
    std::mutex m_mutex;
};

class LocalSystemClockContextWriter : public ContextWriter {
public:
    explicit LocalSystemClockContextWriter(Core::System& system, SharedMemory& shared_memory);

    Result Write(const SystemClockContext& context) override;

private:
    Core::System& m_system;

    SharedMemory& m_shared_memory;
    bool m_in_use{};
    SystemClockContext m_context{};
};

class NetworkSystemClockContextWriter : public ContextWriter {
public:
    explicit NetworkSystemClockContextWriter(Core::System& system, SharedMemory& shared_memory,
                                             SystemClockCore& system_clock);

    Result Write(const SystemClockContext& context) override;

private:
    Core::System& m_system;

    SharedMemory& m_shared_memory;
    bool m_in_use{};
    SystemClockContext m_context{};
    SystemClockCore& m_system_clock;
};

class EphemeralNetworkSystemClockContextWriter : public ContextWriter {
public:
    EphemeralNetworkSystemClockContextWriter(Core::System& system);

    Result Write(const SystemClockContext& context) override;

private:
    Core::System& m_system;

    bool m_in_use{};
    SystemClockContext m_context{};
};

} // namespace Service::PSC::Time


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/result.h"
#include "core/hle/service/psc/time/clocks/context_writers.h"
#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
#include "core/hle/service/psc/time/clocks/system_clock_core.h"
#include "core/hle/service/psc/time/common.h"

namespace Service::PSC::Time {

class EphemeralNetworkSystemClockCore : public SystemClockCore {
public:
    explicit EphemeralNetworkSystemClockCore(SteadyClockCore& steady_clock)
        : SystemClockCore{steady_clock} {}
    ~EphemeralNetworkSystemClockCore() override = default;
};

} // namespace Service::PSC::Time


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h"

namespace Service::PSC::Time {

void StandardLocalSystemClockCore::Initialize(const SystemClockContext& context, s64 time) {
    SteadyClockTimePoint time_point{};
    if (GetCurrentTimePoint(time_point) == ResultSuccess &&
        context.steady_time_point.IdMatches(time_point)) {
        SetContextAndWrite(context);
    } else if (SetCurrentTime(time) != ResultSuccess) {
        LOG_ERROR(Service_Time, "Failed to SetCurrentTime");
    }

    SetInitialized();
}

} // namespace Service::PSC::Time


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/result.h"
#include "core/hle/service/psc/time/clocks/context_writers.h"
#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
#include "core/hle/service/psc/time/clocks/system_clock_core.h"
#include "core/hle/service/psc/time/common.h"

namespace Service::PSC::Time {

class StandardLocalSystemClockCore : public SystemClockCore {
public:
    explicit StandardLocalSystemClockCore(SteadyClockCore& steady_clock)
        : SystemClockCore{steady_clock} {}
    ~StandardLocalSystemClockCore() override = default;

    void Initialize(const SystemClockContext& context, s64 time);
};

} // namespace Service::PSC::Time


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h"

namespace Service::PSC::Time {

void StandardNetworkSystemClockCore::Initialize(const SystemClockContext& context, s64 accuracy) {
    if (SetContextAndWrite(context) != ResultSuccess) {
        LOG_ERROR(Service_Time, "Failed to SetContext");
    }
    m_sufficient_accuracy = accuracy;
    SetInitialized();
}

bool StandardNetworkSystemClockCore::IsAccuracySufficient() {
    if (!IsInitialized()) {
        return false;
    }

    SystemClockContext context{};
    SteadyClockTimePoint current_time_point{};
    if (GetCurrentTimePoint(current_time_point) != ResultSuccess ||
        GetContext(context) != ResultSuccess) {
        return false;
    }

    s64 seconds{};
    if (GetSpanBetweenTimePoints(&seconds, context.steady_time_point, current_time_point) !=
        ResultSuccess) {
        return false;
    }

    if (std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(seconds))
            .count() < m_sufficient_accuracy) {
        return true;
    }

    return false;
}

} // namespace Service::PSC::Time


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <chrono>

#include "core/hle/result.h"
#include "core/hle/service/psc/time/clocks/context_writers.h"
#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
#include "core/hle/service/psc/time/clocks/system_clock_core.h"
#include "core/hle/service/psc/time/common.h"

namespace Service::PSC::Time {

class StandardNetworkSystemClockCore : public SystemClockCore {
public:
    explicit StandardNetworkSystemClockCore(SteadyClockCore& steady_clock)
        : SystemClockCore{steady_clock} {}
    ~StandardNetworkSystemClockCore() override = default;

    void Initialize(const SystemClockContext& context, s64 accuracy);
    bool IsAccuracySufficient();

private:
    s64 m_sufficient_accuracy{
        std::chrono ::duration_cast<std::chrono::nanoseconds>(std::chrono::days(10)).count()};
};

} // namespace Service::PSC::Time


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <chrono>

#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/service/psc/time/clocks/standard_steady_clock_core.h"

namespace Service::PSC::Time {

void StandardSteadyClockCore::Initialize(ClockSourceId clock_source_id, s64 rtc_offset,
                                         s64 internal_offset, s64 test_offset,
                                         bool is_rtc_reset_detected) {
    m_clock_source_id = clock_source_id;
    m_rtc_offset = rtc_offset;
    m_internal_offset = internal_offset;
    m_test_offset = test_offset;
    if (is_rtc_reset_detected) {
        SetResetDetected();
    }
    SetInitialized();
}

void StandardSteadyClockCore::SetRtcOffset(s64 offset) {
    m_rtc_offset = offset;
}

void StandardSteadyClockCore::SetContinuousAdjustment(ClockSourceId clock_source_id, s64 time) {
    auto ticks{m_system.CoreTiming().GetClockTicks()};

    m_continuous_adjustment_time_point.rtc_offset = ConvertToTimeSpan(ticks).count();
    m_continuous_adjustment_time_point.diff_scale = 0;
    m_continuous_adjustment_time_point.shift_amount = 0;
    m_continuous_adjustment_time_point.lower = time;
    m_continuous_adjustment_time_point.upper = time;
    m_continuous_adjustment_time_point.clock_source_id = clock_source_id;
}

void StandardSteadyClockCore::GetContinuousAdjustment(
    ContinuousAdjustmentTimePoint& out_time_point) const {
    out_time_point = m_continuous_adjustment_time_point;
}

void StandardSteadyClockCore::UpdateContinuousAdjustmentTime(s64 in_time) {
    auto ticks{m_system.CoreTiming().GetClockTicks()};
    auto uptime_ns{ConvertToTimeSpan(ticks).count()};
    auto adjusted_time{((uptime_ns - m_continuous_adjustment_time_point.rtc_offset) *
                        m_continuous_adjustment_time_point.diff_scale) >>
                       m_continuous_adjustment_time_point.shift_amount};
    auto expected_time{adjusted_time + m_continuous_adjustment_time_point.lower};

    auto last_time_point{m_continuous_adjustment_time_point.upper};
    m_continuous_adjustment_time_point.upper = in_time;
    auto t1{std::min<s64>(expected_time, last_time_point)};
    expected_time = std::max<s64>(expected_time, last_time_point);
    expected_time = m_continuous_adjustment_time_point.diff_scale >= 0 ? t1 : expected_time;

    auto new_diff{in_time < expected_time ? -55 : 55};

    m_continuous_adjustment_time_point.rtc_offset = uptime_ns;
    m_continuous_adjustment_time_point.shift_amount = expected_time == in_time ? 0 : 14;
    m_continuous_adjustment_time_point.diff_scale = expected_time == in_time ? 0 : new_diff;
    m_continuous_adjustment_time_point.lower = expected_time;
}

Result StandardSteadyClockCore::GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) {
    auto current_time_ns = GetCurrentRawTimePointImpl();
    auto current_time_s =
        std::chrono::duration_cast<std::chrono::seconds>(std::chrono::nanoseconds(current_time_ns));
    out_time_point.time_point = current_time_s.count();
    out_time_point.clock_source_id = m_clock_source_id;
    R_SUCCEED();
}

s64 StandardSteadyClockCore::GetCurrentRawTimePointImpl() {
    std::scoped_lock l{m_mutex};
    auto ticks{static_cast<s64>(m_system.CoreTiming().GetClockTicks())};
    auto current_time_ns = m_rtc_offset + ConvertToTimeSpan(ticks).count();
    auto time_point = std::max<s64>(current_time_ns, m_cached_time_point);
    m_cached_time_point = time_point;
    return time_point;
}

s64 StandardSteadyClockCore::GetTestOffsetImpl() const {
    return m_test_offset;
}

void StandardSteadyClockCore::SetTestOffsetImpl(s64 offset) {
    m_test_offset = offset;
}

s64 StandardSteadyClockCore::GetInternalOffsetImpl() const {
    return m_internal_offset;
}

void StandardSteadyClockCore::SetInternalOffsetImpl(s64 offset) {
    m_internal_offset = offset;
}

} // namespace Service::PSC::Time


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <mutex>

#include "core/hle/service/psc/time/clocks/steady_clock_core.h"

namespace Core {
class System;
}

namespace Service::PSC::Time {
class StandardSteadyClockCore : public SteadyClockCore {
public:
    explicit StandardSteadyClockCore(Core::System& system) : m_system{system} {}
    ~StandardSteadyClockCore() override = default;

    void Initialize(ClockSourceId clock_source_id, s64 rtc_offset, s64 internal_offset,
                    s64 test_offset, bool is_rtc_reset_detected);

    void SetRtcOffset(s64 offset);
    void SetContinuousAdjustment(ClockSourceId clock_source_id, s64 time);
    void GetContinuousAdjustment(ContinuousAdjustmentTimePoint& out_time_point) const;
    void UpdateContinuousAdjustmentTime(s64 time);

    Result GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) override;
    s64 GetCurrentRawTimePointImpl() override;
    s64 GetTestOffsetImpl() const override;
    void SetTestOffsetImpl(s64 offset) override;
    s64 GetInternalOffsetImpl() const override;
    void SetInternalOffsetImpl(s64 offset) override;

    Result GetRtcValueImpl(s64& out_value) override {
        R_RETURN(ResultNotImplemented);
    }

    Result GetSetupResultValueImpl() override {
        R_SUCCEED();
    }

private:
    Core::System& m_system;

    std::mutex m_mutex;
    s64 m_test_offset{};
    s64 m_internal_offset{};
    ClockSourceId m_clock_source_id{};
    s64 m_rtc_offset{};
    s64 m_cached_time_point{};
    ContinuousAdjustmentTimePoint m_continuous_adjustment_time_point{};
};
} // namespace Service::PSC::Time


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/core.h"
#include "core/hle/service/psc/time/clocks/standard_user_system_clock_core.h"

namespace Service::PSC::Time {

StandardUserSystemClockCore::StandardUserSystemClockCore(
    Core::System& system, StandardLocalSystemClockCore& local_clock,
    StandardNetworkSystemClockCore& network_clock)
    : SystemClockCore{local_clock.GetSteadyClock()}, m_system{system},
      m_ctx{m_system, "Psc:StandardUserSystemClockCore"}, m_local_system_clock{local_clock},
      m_network_system_clock{network_clock}, m_event{m_ctx.CreateEvent(
                                                 "Psc:StandardUserSystemClockCore:Event")} {}

StandardUserSystemClockCore::~StandardUserSystemClockCore() {
    m_ctx.CloseEvent(m_event);
}

Result StandardUserSystemClockCore::SetAutomaticCorrection(bool automatic_correction) {
    R_SUCCEED_IF(m_automatic_correction == automatic_correction);
    R_SUCCEED_IF(!m_network_system_clock.CheckClockSourceMatches());

    SystemClockContext context{};
    R_TRY(m_network_system_clock.GetContext(context));
    R_TRY(m_local_system_clock.SetContextAndWrite(context));

    m_automatic_correction = automatic_correction;
    R_SUCCEED();
}

Result StandardUserSystemClockCore::GetContext(SystemClockContext& out_context) const {
    if (!m_automatic_correction) {
        R_RETURN(m_local_system_clock.GetContext(out_context));
    }

    if (!m_network_system_clock.CheckClockSourceMatches()) {
        R_RETURN(m_local_system_clock.GetContext(out_context));
    }

    SystemClockContext context{};
    R_TRY(m_network_system_clock.GetContext(context));
    R_TRY(m_local_system_clock.SetContextAndWrite(context));

    R_RETURN(m_local_system_clock.GetContext(out_context));
}

Result StandardUserSystemClockCore::SetContext(const SystemClockContext& context) {
    R_RETURN(ResultNotImplemented);
}

Result StandardUserSystemClockCore::GetTimePoint(SteadyClockTimePoint& out_time_point) {
    out_time_point = m_time_point;
    R_SUCCEED();
}

void StandardUserSystemClockCore::SetTimePointAndSignal(SteadyClockTimePoint& time_point) {
    m_time_point = time_point;
    m_event->Signal();
}

} // namespace Service::PSC::Time


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/kernel/k_event.h"
#include "core/hle/result.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/psc/time/clocks/context_writers.h"
#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h"
#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h"
#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
#include "core/hle/service/psc/time/clocks/system_clock_core.h"
#include "core/hle/service/psc/time/common.h"

namespace Core {
class System;
}

namespace Service::PSC::Time {

class StandardUserSystemClockCore : public SystemClockCore {
public:
    explicit StandardUserSystemClockCore(Core::System& system,
                                         StandardLocalSystemClockCore& local_clock,
                                         StandardNetworkSystemClockCore& network_clock);
    ~StandardUserSystemClockCore() override;

    Kernel::KEvent& GetEvent() {
        return *m_event;
    }

    bool GetAutomaticCorrection() const {
        return m_automatic_correction;
    }
    Result SetAutomaticCorrection(bool automatic_correction);

    Result GetContext(SystemClockContext& out_context) const override;
    Result SetContext(const SystemClockContext& context) override;

    Result GetTimePoint(SteadyClockTimePoint& out_time_point);
    void SetTimePointAndSignal(SteadyClockTimePoint& time_point);

private:
    Core::System& m_system;
    KernelHelpers::ServiceContext m_ctx;

    bool m_automatic_correction{};
    StandardLocalSystemClockCore& m_local_system_clock;
    StandardNetworkSystemClockCore& m_network_system_clock;
    SteadyClockTimePoint m_time_point{};
    Kernel::KEvent* m_event{};
};

} // namespace Service::PSC::Time


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <chrono>

#include "core/hle/result.h"
#include "core/hle/service/psc/time/common.h"

namespace Service::PSC::Time {

class SteadyClockCore {
public:
    SteadyClockCore() = default;
    virtual ~SteadyClockCore() = default;

    void SetInitialized() {
        m_initialized = true;
    }

    bool IsInitialized() const {
        return m_initialized;
    }

    void SetResetDetected() {
        m_reset_detected = true;
    }

    bool IsResetDetected() const {
        return m_reset_detected;
    }

    Result GetCurrentTimePoint(SteadyClockTimePoint& out_time_point) {
        R_TRY(GetCurrentTimePointImpl(out_time_point));

        auto one_second_ns{
            std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
        out_time_point.time_point += GetTestOffsetImpl() / one_second_ns;
        out_time_point.time_point += GetInternalOffsetImpl() / one_second_ns;
        R_SUCCEED();
    }

    s64 GetTestOffset() const {
        return GetTestOffsetImpl();
    }

    void SetTestOffset(s64 offset) {
        SetTestOffsetImpl(offset);
    }

    s64 GetInternalOffset() const {
        return GetInternalOffsetImpl();
    }

    s64 GetRawTime() {
        return GetCurrentRawTimePointImpl() + GetTestOffsetImpl() + GetInternalOffsetImpl();
    }

    Result GetRtcValue(s64& out_value) {
        R_RETURN(GetRtcValueImpl(out_value));
    }

    Result GetSetupResultValue() {
        R_RETURN(GetSetupResultValueImpl());
    }

private:
    virtual Result GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) = 0;
    virtual s64 GetCurrentRawTimePointImpl() = 0;
    virtual s64 GetTestOffsetImpl() const = 0;
    virtual void SetTestOffsetImpl(s64 offset) = 0;
    virtual s64 GetInternalOffsetImpl() const = 0;
    virtual void SetInternalOffsetImpl(s64 offset) = 0;
    virtual Result GetRtcValueImpl(s64& out_value) = 0;
    virtual Result GetSetupResultValueImpl() = 0;

    bool m_initialized{};
    bool m_reset_detected{};
};
} // namespace Service::PSC::Time


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/service/psc/time/clocks/context_writers.h"
#include "core/hle/service/psc/time/clocks/system_clock_core.h"

namespace Service::PSC::Time {

bool SystemClockCore::CheckClockSourceMatches() {
    SystemClockContext context{};
    if (GetContext(context) != ResultSuccess) {
        return false;
    }

    SteadyClockTimePoint time_point{};
    if (m_steady_clock.GetCurrentTimePoint(time_point) != ResultSuccess) {
        return false;
    }

    return context.steady_time_point.IdMatches(time_point);
}

Result SystemClockCore::GetCurrentTime(s64* out_time) const {
    R_UNLESS(out_time != nullptr, ResultInvalidArgument);

    SystemClockContext context{};
    SteadyClockTimePoint time_point{};

    R_TRY(m_steady_clock.GetCurrentTimePoint(time_point));
    R_TRY(GetContext(context));

    R_UNLESS(context.steady_time_point.IdMatches(time_point), ResultClockMismatch);

    *out_time = context.offset + time_point.time_point;
    R_SUCCEED();
}

Result SystemClockCore::SetCurrentTime(s64 time) {
    SteadyClockTimePoint time_point{};
    R_TRY(m_steady_clock.GetCurrentTimePoint(time_point));

    SystemClockContext context{
        .offset = time - time_point.time_point,
        .steady_time_point = time_point,
    };
    R_RETURN(SetContextAndWrite(context));
}

Result SystemClockCore::GetContext(SystemClockContext& out_context) const {
    out_context = m_context;
    R_SUCCEED();
}

Result SystemClockCore::SetContext(const SystemClockContext& context) {
    m_context = context;
    R_SUCCEED();
}

Result SystemClockCore::SetContextAndWrite(const SystemClockContext& context) {
    R_TRY(SetContext(context));

    if (m_context_writer) {
        R_RETURN(m_context_writer->Write(context));
    }

    R_SUCCEED();
}

void SystemClockCore::LinkOperationEvent(OperationEvent& operation_event) {
    if (m_context_writer) {
        m_context_writer->Link(operation_event);
    }
}

} // namespace Service::PSC::Time


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/result.h"
#include "core/hle/service/psc/time/clocks/steady_clock_core.h"
#include "core/hle/service/psc/time/common.h"

namespace Service::PSC::Time {
class ContextWriter;

class SystemClockCore {
public:
    explicit SystemClockCore(SteadyClockCore& steady_clock) : m_steady_clock{steady_clock} {}
    virtual ~SystemClockCore() = default;

    SteadyClockCore& GetSteadyClock() {
        return m_steady_clock;
    }

    bool IsInitialized() const {
        return m_initialized;
    }

    void SetInitialized() {
        m_initialized = true;
    }

    void SetContextWriter(ContextWriter& context_writer) {
        m_context_writer = &context_writer;
    }

    bool CheckClockSourceMatches();

    Result GetCurrentTime(s64* out_time) const;
    Result SetCurrentTime(s64 time);

    Result GetCurrentTimePoint(SteadyClockTimePoint& out_time_point) {
        R_RETURN(m_steady_clock.GetCurrentTimePoint(out_time_point));
    }

    virtual Result GetContext(SystemClockContext& out_context) const;
    virtual Result SetContext(const SystemClockContext& context);
    Result SetContextAndWrite(const SystemClockContext& context);

    void LinkOperationEvent(OperationEvent& operation_event);

private:
    bool m_initialized{};
    ContextWriter* m_context_writer{};
    SteadyClockCore& m_steady_clock;
    SystemClockContext m_context{};
};
} // namespace Service::PSC::Time


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <chrono>

#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h"

namespace Service::PSC::Time {

Result TickBasedSteadyClockCore::GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) {
    auto ticks{m_system.CoreTiming().GetClockTicks()};
    auto current_time_s =
        std::chrono::duration_cast<std::chrono::seconds>(ConvertToTimeSpan(ticks)).count();
    out_time_point.time_point = current_time_s;
    out_time_point.clock_source_id = m_clock_source_id;
    R_SUCCEED();
}

s64 TickBasedSteadyClockCore::GetCurrentRawTimePointImpl() {
    SteadyClockTimePoint time_point{};
    if (GetCurrentTimePointImpl(time_point) != ResultSuccess) {
        LOG_ERROR(Service_Time, "Failed to GetCurrentTimePoint!");
    }
    return std::chrono::duration_cast<std::chrono::nanoseconds>(
               std::chrono::seconds(time_point.time_point))
        .count();
}

s64 TickBasedSteadyClockCore::GetTestOffsetImpl() const {
    return 0;
}

void TickBasedSteadyClockCore::SetTestOffsetImpl(s64 offset) {}

s64 TickBasedSteadyClockCore::GetInternalOffsetImpl() const {
    return 0;
}

void TickBasedSteadyClockCore::SetInternalOffsetImpl(s64 offset) {}

} // namespace Service::PSC::Time


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <mutex>

#include "common/uuid.h"
#include "core/hle/service/psc/time/clocks/steady_clock_core.h"

namespace Core {
class System;
}

namespace Service::PSC::Time {
class TickBasedSteadyClockCore : public SteadyClockCore {
public:
    explicit TickBasedSteadyClockCore(Core::System& system) : m_system{system} {}
    ~TickBasedSteadyClockCore() override = default;

    Result GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) override;
    s64 GetCurrentRawTimePointImpl() override;
    s64 GetTestOffsetImpl() const override;
    void SetTestOffsetImpl(s64 offset) override;
    s64 GetInternalOffsetImpl() const override;
    void SetInternalOffsetImpl(s64 offset) override;

    Result GetRtcValueImpl(s64& out_value) override {
        R_RETURN(ResultNotImplemented);
    }

    Result GetSetupResultValueImpl() override {
        R_SUCCEED();
    }

private:
    Core::System& m_system;

    ClockSourceId m_clock_source_id{Common::UUID::MakeRandom()};
};
} // namespace Service::PSC::Time


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/core.h"
#include "core/hle/service/psc/time/common.h"

namespace Service::PSC::Time {
OperationEvent::OperationEvent(Core::System& system)
    : m_ctx{system, "Time:OperationEvent"}, m_event{
                                                m_ctx.CreateEvent("Time:OperationEvent:Event")} {}

OperationEvent::~OperationEvent() {
    m_ctx.CloseEvent(m_event);
}

} // namespace Service::PSC::Time


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <array>
#include <chrono>
#include <fmt/format.h>

#include "common/common_types.h"
#include "common/intrusive_list.h"
#include "common/uuid.h"
#include "common/wall_clock.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/psc/time/errors.h"

namespace Core {
class System;
}

namespace Service::PSC::Time {
using ClockSourceId = Common::UUID;

enum class TimeType : u8 {
    UserSystemClock = 0,
    NetworkSystemClock = 1,
    LocalSystemClock = 2,
};

struct SteadyClockTimePoint {
    constexpr bool IdMatches(const SteadyClockTimePoint& other) const {
        return clock_source_id == other.clock_source_id;
    }
    bool operator==(const SteadyClockTimePoint& other) const = default;

    s64 time_point;
    ClockSourceId clock_source_id;
};
static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint has the wrong size!");
static_assert(std::is_trivial_v<ClockSourceId>);

struct SystemClockContext {
    bool operator==(const SystemClockContext& other) const = default;

    s64 offset;
    SteadyClockTimePoint steady_time_point;
};
static_assert(sizeof(SystemClockContext) == 0x20, "SystemClockContext has the wrong size!");
static_assert(std::is_trivial_v<SystemClockContext>);

struct CalendarTime {
    s16 year;
    s8 month;
    s8 day;
    s8 hour;
    s8 minute;
    s8 second;
};
static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime has the wrong size!");

struct CalendarAdditionalInfo {
    s32 day_of_week;
    s32 day_of_year;
    std::array<char, 8> name;
    s32 is_dst;
    s32 ut_offset;
};
static_assert(sizeof(CalendarAdditionalInfo) == 0x18, "CalendarAdditionalInfo has the wrong size!");

using LocationName = std::array<char, 0x24>;
static_assert(sizeof(LocationName) == 0x24, "LocationName has the wrong size!");

using RuleVersion = std::array<char, 0x10>;
static_assert(sizeof(RuleVersion) == 0x10, "RuleVersion has the wrong size!");

struct ClockSnapshot {
    SystemClockContext user_context;
    SystemClockContext network_context;
    s64 user_time;
    s64 network_time;
    CalendarTime user_calendar_time;
    CalendarTime network_calendar_time;
    CalendarAdditionalInfo user_calendar_additional_time;
    CalendarAdditionalInfo network_calendar_additional_time;
    SteadyClockTimePoint steady_clock_time_point;
    LocationName location_name;
    bool is_automatic_correction_enabled;
    TimeType type;
    u16 unk_CE;
};
static_assert(sizeof(ClockSnapshot) == 0xD0, "ClockSnapshot has the wrong size!");
static_assert(std::is_trivial_v<ClockSnapshot>);

struct ContinuousAdjustmentTimePoint {
    s64 rtc_offset;
    s64 diff_scale;
    s64 shift_amount;
    s64 lower;
    s64 upper;
    ClockSourceId clock_source_id;
};
static_assert(sizeof(ContinuousAdjustmentTimePoint) == 0x38,
              "ContinuousAdjustmentTimePoint has the wrong size!");
static_assert(std::is_trivial_v<ContinuousAdjustmentTimePoint>);

struct AlarmInfo {
    s64 alert_time;
    u32 priority;
};
static_assert(sizeof(AlarmInfo) == 0x10, "AlarmInfo has the wrong size!");

struct StaticServiceSetupInfo {
    bool can_write_local_clock;
    bool can_write_user_clock;
    bool can_write_network_clock;
    bool can_write_timezone_device_location;
    bool can_write_steady_clock;
    bool can_write_uninitialized_clock;
};
static_assert(sizeof(StaticServiceSetupInfo) == 0x6, "StaticServiceSetupInfo has the wrong size!");

struct OperationEvent : public Common::IntrusiveListBaseNode<OperationEvent> {
    using OperationEventList = Common::IntrusiveListBaseTraits<OperationEvent>::ListType;

    OperationEvent(Core::System& system);
    ~OperationEvent();

    KernelHelpers::ServiceContext m_ctx;
    Kernel::KEvent* m_event{};
};

constexpr inline std::chrono::nanoseconds ConvertToTimeSpan(s64 ticks) {
    constexpr auto one_second_ns{
        std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};

    constexpr s64 max{Common::WallClock::CNTFRQ *
                      (std::numeric_limits<s64>::max() / one_second_ns)};

    if (ticks > max) {
        return std::chrono::nanoseconds(std::numeric_limits<s64>::max());
    } else if (ticks < -max) {
        return std::chrono::nanoseconds(std::numeric_limits<s64>::min());
    }

    auto a{ticks / Common::WallClock::CNTFRQ * one_second_ns};
    auto b{((ticks % Common::WallClock::CNTFRQ) * one_second_ns) / Common::WallClock::CNTFRQ};

    return std::chrono::nanoseconds(a + b);
}

constexpr inline Result GetSpanBetweenTimePoints(s64* out_seconds, const SteadyClockTimePoint& a,
                                                 const SteadyClockTimePoint& b) {
    R_UNLESS(out_seconds, ResultInvalidArgument);
    R_UNLESS(a.IdMatches(b), ResultInvalidArgument);
    R_UNLESS(a.time_point >= 0 || b.time_point <= a.time_point + std::numeric_limits<s64>::max(),
             ResultOverflow);
    R_UNLESS(a.time_point < 0 || b.time_point >= a.time_point + std::numeric_limits<s64>::min(),
             ResultOverflow);

    *out_seconds = b.time_point - a.time_point;
    R_SUCCEED();
}

} // namespace Service::PSC::Time

template <>
struct fmt::formatter<Service::PSC::Time::TimeType> : fmt::formatter<fmt::string_view> {
    template <typename FormatContext>
    auto format(Service::PSC::Time::TimeType type, FormatContext& ctx) {
        const string_view name = [type] {
            using Service::PSC::Time::TimeType;
            switch (type) {
            case TimeType::UserSystemClock:
                return "UserSystemClock";
            case TimeType::NetworkSystemClock:
                return "NetworkSystemClock";
            case TimeType::LocalSystemClock:
                return "LocalSystemClock";
            }
            return "Invalid";
        }();
        return formatter<string_view>::format(name, ctx);
    }
};

template <>
struct fmt::formatter<Service::PSC::Time::SteadyClockTimePoint> : fmt::formatter<fmt::string_view> {
    template <typename FormatContext>
    auto format(const Service::PSC::Time::SteadyClockTimePoint& time_point,
                FormatContext& ctx) const {
        return fmt::format_to(ctx.out(), "[time_point={}]", time_point.time_point);
    }
};

template <>
struct fmt::formatter<Service::PSC::Time::SystemClockContext> : fmt::formatter<fmt::string_view> {
    template <typename FormatContext>
    auto format(const Service::PSC::Time::SystemClockContext& context, FormatContext& ctx) const {
        return fmt::format_to(ctx.out(), "[offset={} steady_time_point={}]", context.offset,
                              context.steady_time_point.time_point);
    }
};

template <>
struct fmt::formatter<Service::PSC::Time::CalendarTime> : fmt::formatter<fmt::string_view> {
    template <typename FormatContext>
    auto format(const Service::PSC::Time::CalendarTime& calendar, FormatContext& ctx) const {
        return fmt::format_to(ctx.out(), "[{:02}/{:02}/{:04} {:02}:{:02}:{:02}]", calendar.day,
                              calendar.month, calendar.year, calendar.hour, calendar.minute,
                              calendar.second);
    }
};

template <>
struct fmt::formatter<Service::PSC::Time::CalendarAdditionalInfo>
    : fmt::formatter<fmt::string_view> {
    template <typename FormatContext>
    auto format(const Service::PSC::Time::CalendarAdditionalInfo& additional,
                FormatContext& ctx) const {
        return fmt::format_to(ctx.out(), "[weekday={} yearday={} name={} is_dst={} ut_offset={}]",
                              additional.day_of_week, additional.day_of_year,
                              additional.name.data(), additional.is_dst, additional.ut_offset);
    }
};

template <>
struct fmt::formatter<Service::PSC::Time::LocationName> : fmt::formatter<fmt::string_view> {
    template <typename FormatContext>
    auto format(const Service::PSC::Time::LocationName& name, FormatContext& ctx) const {
        return formatter<string_view>::format(name.data(), ctx);
    }
};

template <>
struct fmt::formatter<Service::PSC::Time::RuleVersion> : fmt::formatter<fmt::string_view> {
    template <typename FormatContext>
    auto format(const Service::PSC::Time::RuleVersion& version, FormatContext& ctx) const {
        return formatter<string_view>::format(version.data(), ctx);
    }
};

template <>
struct fmt::formatter<Service::PSC::Time::ClockSnapshot> : fmt::formatter<fmt::string_view> {
    template <typename FormatContext>
    auto format(const Service::PSC::Time::ClockSnapshot& snapshot, FormatContext& ctx) const {
        return fmt::format_to(
            ctx.out(),
            "[user_context={} network_context={} user_time={} network_time={} "
            "user_calendar_time={} "
            "network_calendar_time={} user_calendar_additional_time={} "
            "network_calendar_additional_time={} steady_clock_time_point={} location={} "
            "is_automatic_correction_enabled={} type={}]",
            snapshot.user_context, snapshot.network_context, snapshot.user_time,
            snapshot.network_time, snapshot.user_calendar_time, snapshot.network_calendar_time,
            snapshot.user_calendar_additional_time, snapshot.network_calendar_additional_time,
            snapshot.steady_clock_time_point, snapshot.location_name,
            snapshot.is_automatic_correction_enabled, snapshot.type);
    }
};

template <>
struct fmt::formatter<Service::PSC::Time::ContinuousAdjustmentTimePoint>
    : fmt::formatter<fmt::string_view> {
    template <typename FormatContext>
    auto format(const Service::PSC::Time::ContinuousAdjustmentTimePoint& time_point,
                FormatContext& ctx) const {
        return fmt::format_to(ctx.out(),
                              "[rtc_offset={} diff_scale={} shift_amount={} lower={} upper={}]",
                              time_point.rtc_offset, time_point.diff_scale, time_point.shift_amount,
                              time_point.lower, time_point.upper);
    }
};


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/result.h"

namespace Service::PSC::Time {

constexpr Result ResultPermissionDenied{ErrorModule::Time, 1};
constexpr Result ResultClockMismatch{ErrorModule::Time, 102};
constexpr Result ResultClockUninitialized{ErrorModule::Time, 103};
constexpr Result ResultTimeNotFound{ErrorModule::Time, 200};
constexpr Result ResultOverflow{ErrorModule::Time, 201};
constexpr Result ResultFailed{ErrorModule::Time, 801};
constexpr Result ResultInvalidArgument{ErrorModule::Time, 901};
constexpr Result ResultTimeZoneOutOfRange{ErrorModule::Time, 902};
constexpr Result ResultTimeZoneParseFailed{ErrorModule::Time, 903};
constexpr Result ResultRtcTimeout{ErrorModule::Time, 988};
constexpr Result ResultTimeZoneNotFound{ErrorModule::Time, 989};
constexpr Result ResultNotImplemented{ErrorModule::Time, 990};
constexpr Result ResultAlarmNotRegistered{ErrorModule::Time, 1502};

} // namespace Service::PSC::Time


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/service/psc/time/alarms.h"
#include "core/hle/service/psc/time/clocks/context_writers.h"
#include "core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h"
#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h"
#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h"
#include "core/hle/service/psc/time/clocks/standard_steady_clock_core.h"
#include "core/hle/service/psc/time/clocks/standard_user_system_clock_core.h"
#include "core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h"
#include "core/hle/service/psc/time/power_state_request_manager.h"
#include "core/hle/service/psc/time/shared_memory.h"
#include "core/hle/service/psc/time/time_zone.h"

namespace Core {
class System;
}

namespace Service::PSC::Time {
class TimeManager {
public:
    explicit TimeManager(Core::System& system)
        : m_system{system}, m_standard_steady_clock{system}, m_tick_based_steady_clock{m_system},
          m_standard_local_system_clock{m_standard_steady_clock},
          m_standard_network_system_clock{m_standard_steady_clock},
          m_standard_user_system_clock{m_system, m_standard_local_system_clock,
                                       m_standard_network_system_clock},
          m_ephemeral_network_clock{m_tick_based_steady_clock}, m_shared_memory{m_system},
          m_power_state_request_manager{m_system}, m_alarms{m_system, m_standard_steady_clock,
                                                            m_power_state_request_manager},
          m_local_system_clock_context_writer{m_system, m_shared_memory},
          m_network_system_clock_context_writer{m_system, m_shared_memory,
                                                m_standard_user_system_clock},
          m_ephemeral_network_clock_context_writer{m_system} {}

    Core::System& m_system;

    StandardSteadyClockCore m_standard_steady_clock;
    TickBasedSteadyClockCore m_tick_based_steady_clock;
    StandardLocalSystemClockCore m_standard_local_system_clock;
    StandardNetworkSystemClockCore m_standard_network_system_clock;
    StandardUserSystemClockCore m_standard_user_system_clock;
    EphemeralNetworkSystemClockCore m_ephemeral_network_clock;
    TimeZone m_time_zone;
    SharedMemory m_shared_memory;
    PowerStateRequestManager m_power_state_request_manager;
    Alarms m_alarms;
    LocalSystemClockContextWriter m_local_system_clock_context_writer;
    NetworkSystemClockContextWriter m_network_system_clock_context_writer;
    EphemeralNetworkSystemClockContextWriter m_ephemeral_network_clock_context_writer;
};

} // namespace Service::PSC::Time


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/core.h"
#include "core/hle/service/psc/time/power_state_request_manager.h"

namespace Service::PSC::Time {

PowerStateRequestManager::PowerStateRequestManager(Core::System& system)
    : m_system{system}, m_ctx{system, "Psc:PowerStateRequestManager"},
      m_event{m_ctx.CreateEvent("Psc:PowerStateRequestManager:Event")} {}

PowerStateRequestManager::~PowerStateRequestManager() {
    m_ctx.CloseEvent(m_event);
}

void PowerStateRequestManager::UpdatePendingPowerStateRequestPriority(u32 priority) {
    std::scoped_lock l{m_mutex};
    if (m_has_pending_request) {
        m_pending_request_priority = std::max(m_pending_request_priority, priority);
    } else {
        m_pending_request_priority = priority;
        m_has_pending_request = true;
    }
}

void PowerStateRequestManager::SignalPowerStateRequestAvailability() {
    std::scoped_lock l{m_mutex};
    if (m_has_pending_request) {
        if (!m_has_available_request) {
            m_has_available_request = true;
        }
        m_has_pending_request = false;
        m_available_request_priority = m_pending_request_priority;
        m_event->Signal();
    }
}

bool PowerStateRequestManager::GetAndClearPowerStateRequest(u32& out_priority) {
    std::scoped_lock l{m_mutex};
    auto had_request{m_has_available_request};
    if (m_has_available_request) {
        out_priority = m_available_request_priority;
        m_has_available_request = false;
        m_event->Clear();
    }
    return had_request;
}

} // namespace Service::PSC::Time


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <mutex>

#include "core/hle/kernel/k_event.h"
#include "core/hle/service/kernel_helpers.h"

namespace Core {
class System;
}

namespace Service::PSC::Time {

class PowerStateRequestManager {
public:
    explicit PowerStateRequestManager(Core::System& system);
    ~PowerStateRequestManager();

    Kernel::KReadableEvent& GetReadableEvent() {
        return m_event->GetReadableEvent();
    }

    void UpdatePendingPowerStateRequestPriority(u32 priority);
    void SignalPowerStateRequestAvailability();
    bool GetAndClearPowerStateRequest(u32& out_priority);

private:
    Core::System& m_system;
    KernelHelpers::ServiceContext m_ctx;

    Kernel::KEvent* m_event{};
    bool m_has_pending_request{};
    u32 m_pending_request_priority{};
    bool m_has_available_request{};
    u32 m_available_request_priority{};
    std::mutex m_mutex;
};

} // namespace Service::PSC::Time


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/psc/time/power_state_service.h"

namespace Service::PSC::Time {

IPowerStateRequestHandler::IPowerStateRequestHandler(
    Core::System& system_, PowerStateRequestManager& power_state_request_manager)
    : ServiceFramework{system_, "time:p"}, m_system{system}, m_power_state_request_manager{
                                                                 power_state_request_manager} {
    // clang-format off
        static const FunctionInfo functions[] = {
            {0, D<&IPowerStateRequestHandler::GetPowerStateRequestEventReadableHandle>, "GetPowerStateRequestEventReadableHandle"},
            {1, D<&IPowerStateRequestHandler::GetAndClearPowerStateRequest>, "GetAndClearPowerStateRequest"},
        };
    // clang-format on

    RegisterHandlers(functions);
}

Result IPowerStateRequestHandler::GetPowerStateRequestEventReadableHandle(
    OutCopyHandle<Kernel::KReadableEvent> out_event) {
    LOG_DEBUG(Service_Time, "called.");

    *out_event = &m_power_state_request_manager.GetReadableEvent();
    R_SUCCEED();
}

Result IPowerStateRequestHandler::GetAndClearPowerStateRequest(Out<bool> out_cleared,
                                                               Out<u32> out_priority) {
    LOG_DEBUG(Service_Time, "called.");

    u32 priority{};
    auto cleared = m_power_state_request_manager.GetAndClearPowerStateRequest(priority);
    *out_cleared = cleared;

    if (cleared) {
        *out_priority = priority;
    }
    R_SUCCEED();
}

} // namespace Service::PSC::Time


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/service/cmif_types.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/psc/time/power_state_request_manager.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"

namespace Core {
class System;
}

namespace Service::PSC::Time {

class IPowerStateRequestHandler final : public ServiceFramework<IPowerStateRequestHandler> {
public:
    explicit IPowerStateRequestHandler(Core::System& system,
                                       PowerStateRequestManager& power_state_request_manager);

    ~IPowerStateRequestHandler() override = default;

    Result GetPowerStateRequestEventReadableHandle(OutCopyHandle<Kernel::KReadableEvent> out_event);
    Result GetAndClearPowerStateRequest(Out<bool> out_cleared, Out<u32> out_priority);

private:
    Core::System& m_system;
    PowerStateRequestManager& m_power_state_request_manager;
};

} // namespace Service::PSC::Time


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/psc/time/power_state_service.h"
#include "core/hle/service/psc/time/service_manager.h"
#include "core/hle/service/psc/time/static.h"

namespace Service::PSC::Time {

ServiceManager::ServiceManager(Core::System& system_, std::shared_ptr<TimeManager> time,
                               ServerManager* server_manager)
    : ServiceFramework{system_, "time:m"}, m_system{system}, m_time{std::move(time)},
      m_server_manager{*server_manager},
      m_local_system_clock{m_time->m_standard_local_system_clock},
      m_user_system_clock{m_time->m_standard_user_system_clock},
      m_network_system_clock{m_time->m_standard_network_system_clock},
      m_steady_clock{m_time->m_standard_steady_clock}, m_time_zone{m_time->m_time_zone},
      m_ephemeral_network_clock{m_time->m_ephemeral_network_clock},
      m_shared_memory{m_time->m_shared_memory}, m_alarms{m_time->m_alarms},
      m_local_system_context_writer{m_time->m_local_system_clock_context_writer},
      m_network_system_context_writer{m_time->m_network_system_clock_context_writer},
      m_ephemeral_system_context_writer{m_time->m_ephemeral_network_clock_context_writer},
      m_local_operation{m_system}, m_network_operation{m_system}, m_ephemeral_operation{m_system} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {0,   D<&ServiceManager::GetStaticServiceAsUser>, "GetStaticServiceAsUser"},
        {5,   D<&ServiceManager::GetStaticServiceAsAdmin>, "GetStaticServiceAsAdmin"},
        {6,   D<&ServiceManager::GetStaticServiceAsRepair>, "GetStaticServiceAsRepair"},
        {9,   D<&ServiceManager::GetStaticServiceAsServiceManager>, "GetStaticServiceAsServiceManager"},
        {10,  D<&ServiceManager::SetupStandardSteadyClockCore>, "SetupStandardSteadyClockCore"},
        {11,  D<&ServiceManager::SetupStandardLocalSystemClockCore>, "SetupStandardLocalSystemClockCore"},
        {12,  D<&ServiceManager::SetupStandardNetworkSystemClockCore>, "SetupStandardNetworkSystemClockCore"},
        {13,  D<&ServiceManager::SetupStandardUserSystemClockCore>, "SetupStandardUserSystemClockCore"},
        {14,  D<&ServiceManager::SetupTimeZoneServiceCore>, "SetupTimeZoneServiceCore"},
        {15,  D<&ServiceManager::SetupEphemeralNetworkSystemClockCore>, "SetupEphemeralNetworkSystemClockCore"},
        {50,  D<&ServiceManager::GetStandardLocalClockOperationEvent>, "GetStandardLocalClockOperationEvent"},
        {51,  D<&ServiceManager::GetStandardNetworkClockOperationEventForServiceManager>, "GetStandardNetworkClockOperationEventForServiceManager"},
        {52,  D<&ServiceManager::GetEphemeralNetworkClockOperationEventForServiceManager>, "GetEphemeralNetworkClockOperationEventForServiceManager"},
        {60,  D<&ServiceManager::GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent>, "GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent"},
        {100, D<&ServiceManager::SetStandardSteadyClockBaseTime>, "SetStandardSteadyClockBaseTime"},
        {200, D<&ServiceManager::GetClosestAlarmUpdatedEvent>, "GetClosestAlarmUpdatedEvent"},
        {201, D<&ServiceManager::CheckAndSignalAlarms>, "CheckAndSignalAlarms"},
        {202, D<&ServiceManager::GetClosestAlarmInfo>, "GetClosestAlarmInfo "},
    };
    // clang-format on
    RegisterHandlers(functions);

    m_local_system_context_writer.Link(m_local_operation);
    m_network_system_context_writer.Link(m_network_operation);
    m_ephemeral_system_context_writer.Link(m_ephemeral_operation);
}

Result ServiceManager::GetStaticServiceAsUser(OutInterface<StaticService> out_service) {
    LOG_DEBUG(Service_Time, "called.");

    R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{0, 0, 0, 0, 0, 0}, "time:u"));
}

Result ServiceManager::GetStaticServiceAsAdmin(OutInterface<StaticService> out_service) {
    LOG_DEBUG(Service_Time, "called.");

    R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{1, 1, 0, 1, 0, 0}, "time:a"));
}

Result ServiceManager::GetStaticServiceAsRepair(OutInterface<StaticService> out_service) {
    LOG_DEBUG(Service_Time, "called.");

    R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{0, 0, 0, 0, 1, 0}, "time:r"));
}

Result ServiceManager::GetStaticServiceAsServiceManager(OutInterface<StaticService> out_service) {
    LOG_DEBUG(Service_Time, "called.");

    R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{1, 1, 1, 1, 1, 0}, "time:sm"));
}

Result ServiceManager::SetupStandardSteadyClockCore(bool is_rtc_reset_detected,
                                                    const Common::UUID& clock_source_id,
                                                    s64 rtc_offset, s64 internal_offset,
                                                    s64 test_offset) {
    LOG_DEBUG(Service_Time,
              "called. is_rtc_reset_detected={} clock_source_id={} rtc_offset={} "
              "internal_offset={} test_offset={}",
              is_rtc_reset_detected, clock_source_id.RawString(), rtc_offset, internal_offset,
              test_offset);

    m_steady_clock.Initialize(clock_source_id, rtc_offset, internal_offset, test_offset,
                              is_rtc_reset_detected);
    auto time = m_steady_clock.GetRawTime();
    auto ticks = m_system.CoreTiming().GetClockTicks();
    auto boot_time = time - ConvertToTimeSpan(ticks).count();
    m_shared_memory.SetSteadyClockTimePoint(clock_source_id, boot_time);
    m_steady_clock.SetContinuousAdjustment(clock_source_id, boot_time);

    ContinuousAdjustmentTimePoint time_point{};
    m_steady_clock.GetContinuousAdjustment(time_point);
    m_shared_memory.SetContinuousAdjustment(time_point);

    CheckAndSetupServicesSAndP();
    R_SUCCEED();
}

Result ServiceManager::SetupStandardLocalSystemClockCore(const SystemClockContext& context,
                                                         s64 time) {
    LOG_DEBUG(Service_Time,
              "called. context={} context.steady_time_point.clock_source_id={} time={}", context,
              context.steady_time_point.clock_source_id.RawString(), time);

    m_local_system_clock.SetContextWriter(m_local_system_context_writer);
    m_local_system_clock.Initialize(context, time);

    CheckAndSetupServicesSAndP();
    R_SUCCEED();
}

Result ServiceManager::SetupStandardNetworkSystemClockCore(SystemClockContext context,
                                                           s64 accuracy) {
    LOG_DEBUG(Service_Time, "called. context={} steady_time_point.clock_source_id={} accuracy={}",
              context, context.steady_time_point.clock_source_id.RawString(), accuracy);

    // TODO this is a hack! The network clock should be updated independently, from the ntc service
    // and maybe elsewhere. We do not do that, so fix the clock to the local clock.
    m_local_system_clock.GetContext(context);

    m_network_system_clock.SetContextWriter(m_network_system_context_writer);
    m_network_system_clock.Initialize(context, accuracy);

    CheckAndSetupServicesSAndP();
    R_SUCCEED();
}

Result ServiceManager::SetupStandardUserSystemClockCore(bool automatic_correction,
                                                        SteadyClockTimePoint time_point) {
    LOG_DEBUG(Service_Time, "called. automatic_correction={} time_point={} clock_source_id={}",
              automatic_correction, time_point, time_point.clock_source_id.RawString());

    m_user_system_clock.SetAutomaticCorrection(automatic_correction);
    m_user_system_clock.SetTimePointAndSignal(time_point);
    m_user_system_clock.SetInitialized();
    m_shared_memory.SetAutomaticCorrection(automatic_correction);

    CheckAndSetupServicesSAndP();
    R_SUCCEED();
}

Result ServiceManager::SetupTimeZoneServiceCore(const LocationName& name,
                                                const RuleVersion& rule_version, u32 location_count,
                                                const SteadyClockTimePoint& time_point,
                                                InBuffer<BufferAttr_HipcAutoSelect> rule_buffer) {
    LOG_DEBUG(Service_Time,
              "called. name={} rule_version={} location_count={} time_point={} "
              "clock_source_id={}",
              name, rule_version, location_count, time_point,
              time_point.clock_source_id.RawString());

    if (m_time_zone.ParseBinary(name, rule_buffer) != ResultSuccess) {
        LOG_ERROR(Service_Time, "Failed to parse time zone binary!");
    }

    m_time_zone.SetTimePoint(time_point);
    m_time_zone.SetTotalLocationNameCount(location_count);
    m_time_zone.SetRuleVersion(rule_version);
    m_time_zone.SetInitialized();

    CheckAndSetupServicesSAndP();
    R_SUCCEED();
}

Result ServiceManager::SetupEphemeralNetworkSystemClockCore() {
    LOG_DEBUG(Service_Time, "called.");

    m_ephemeral_network_clock.SetContextWriter(m_ephemeral_system_context_writer);
    m_ephemeral_network_clock.SetInitialized();

    CheckAndSetupServicesSAndP();
    R_SUCCEED();
}

Result ServiceManager::GetStandardLocalClockOperationEvent(
    OutCopyHandle<Kernel::KReadableEvent> out_event) {
    LOG_DEBUG(Service_Time, "called.");

    *out_event = &m_local_operation.m_event->GetReadableEvent();
    R_SUCCEED();
}

Result ServiceManager::GetStandardNetworkClockOperationEventForServiceManager(
    OutCopyHandle<Kernel::KReadableEvent> out_event) {
    LOG_DEBUG(Service_Time, "called.");

    *out_event = &m_network_operation.m_event->GetReadableEvent();
    R_SUCCEED();
}

Result ServiceManager::GetEphemeralNetworkClockOperationEventForServiceManager(
    OutCopyHandle<Kernel::KReadableEvent> out_event) {
    LOG_DEBUG(Service_Time, "called.");

    *out_event = &m_ephemeral_operation.m_event->GetReadableEvent();
    R_SUCCEED();
}

Result ServiceManager::GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(
    OutCopyHandle<Kernel::KReadableEvent> out_event) {
    LOG_DEBUG(Service_Time, "called.");

    *out_event = &m_user_system_clock.GetEvent().GetReadableEvent();
    R_SUCCEED();
}

Result ServiceManager::SetStandardSteadyClockBaseTime(s64 base_time) {
    LOG_DEBUG(Service_Time, "called. base_time={}", base_time);

    m_steady_clock.SetRtcOffset(base_time);
    auto time = m_steady_clock.GetRawTime();
    auto ticks = m_system.CoreTiming().GetClockTicks();
    auto diff = time - ConvertToTimeSpan(ticks).count();
    m_shared_memory.UpdateBaseTime(diff);
    m_steady_clock.UpdateContinuousAdjustmentTime(diff);

    ContinuousAdjustmentTimePoint time_point{};
    m_steady_clock.GetContinuousAdjustment(time_point);
    m_shared_memory.SetContinuousAdjustment(time_point);
    R_SUCCEED();
}

Result ServiceManager::GetClosestAlarmUpdatedEvent(
    OutCopyHandle<Kernel::KReadableEvent> out_event) {
    LOG_DEBUG(Service_Time, "called.");

    *out_event = &m_alarms.GetEvent().GetReadableEvent();
    R_SUCCEED();
}

Result ServiceManager::CheckAndSignalAlarms() {
    LOG_DEBUG(Service_Time, "called.");

    m_alarms.CheckAndSignal();
    R_SUCCEED();
}

Result ServiceManager::GetClosestAlarmInfo(Out<bool> out_is_valid, Out<AlarmInfo> out_info,
                                           Out<s64> out_time) {
    Alarm* alarm{nullptr};
    *out_is_valid = m_alarms.GetClosestAlarm(&alarm);
    if (*out_is_valid) {
        *out_info = {
            .alert_time = alarm->GetAlertTime(),
            .priority = alarm->GetPriority(),
        };
        *out_time = m_alarms.GetRawTime();
    }

    LOG_DEBUG(Service_Time,
              "called. out_is_valid={} out_info.alert_time={} out_info.priority={}, out_time={}",
              *out_is_valid, out_info->alert_time, out_info->priority, *out_time);

    R_SUCCEED();
}

void ServiceManager::CheckAndSetupServicesSAndP() {
    if (m_local_system_clock.IsInitialized() && m_user_system_clock.IsInitialized() &&
        m_network_system_clock.IsInitialized() && m_steady_clock.IsInitialized() &&
        m_time_zone.IsInitialized() && m_ephemeral_network_clock.IsInitialized()) {
        SetupSAndP();
    }
}

void ServiceManager::SetupSAndP() {
    if (!m_is_s_and_p_setup) {
        m_is_s_and_p_setup = true;
        m_server_manager.RegisterNamedService(
            "time:s", std::make_shared<StaticService>(
                          m_system, StaticServiceSetupInfo{0, 0, 1, 0, 0, 0}, m_time, "time:s"));
        m_server_manager.RegisterNamedService("time:p",
                                              std::make_shared<IPowerStateRequestHandler>(
                                                  m_system, m_time->m_power_state_request_manager));
    }
}

Result ServiceManager::GetStaticService(OutInterface<StaticService> out_service,
                                        StaticServiceSetupInfo setup_info, const char* name) {
    *out_service = std::make_shared<StaticService>(m_system, setup_info, m_time, name);
    R_SUCCEED();
}

} // namespace Service::PSC::Time


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <list>
#include <memory>

#include "core/hle/service/cmif_types.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/psc/time/common.h"
#include "core/hle/service/psc/time/manager.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"

namespace Core {
class System;
}

namespace Kernel {
class KReadableEvent;
}

namespace Service::PSC::Time {
class StaticService;

class ServiceManager final : public ServiceFramework<ServiceManager> {
public:
    explicit ServiceManager(Core::System& system, std::shared_ptr<TimeManager> time,
                            ServerManager* server_manager);
    ~ServiceManager() override = default;

    Result GetStaticServiceAsUser(OutInterface<StaticService> out_service);
    Result GetStaticServiceAsAdmin(OutInterface<StaticService> out_service);
    Result GetStaticServiceAsRepair(OutInterface<StaticService> out_service);
    Result GetStaticServiceAsServiceManager(OutInterface<StaticService> out_service);
    Result SetupStandardSteadyClockCore(bool is_rtc_reset_detected,
                                        const Common::UUID& clock_source_id, s64 rtc_offset,
                                        s64 internal_offset, s64 test_offset);
    Result SetupStandardLocalSystemClockCore(const SystemClockContext& context, s64 time);
    Result SetupStandardNetworkSystemClockCore(SystemClockContext context, s64 accuracy);
    Result SetupStandardUserSystemClockCore(bool automatic_correction,
                                            SteadyClockTimePoint time_point);
    Result SetupTimeZoneServiceCore(const LocationName& name, const RuleVersion& rule_version,
                                    u32 location_count, const SteadyClockTimePoint& time_point,
                                    InBuffer<BufferAttr_HipcAutoSelect> rule_buffer);
    Result SetupEphemeralNetworkSystemClockCore();
    Result GetStandardLocalClockOperationEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
    Result GetStandardNetworkClockOperationEventForServiceManager(
        OutCopyHandle<Kernel::KReadableEvent> out_event);
    Result GetEphemeralNetworkClockOperationEventForServiceManager(
        OutCopyHandle<Kernel::KReadableEvent> out_event);
    Result GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(
        OutCopyHandle<Kernel::KReadableEvent> out_event);
    Result SetStandardSteadyClockBaseTime(s64 base_time);
    Result GetClosestAlarmUpdatedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
    Result CheckAndSignalAlarms();
    Result GetClosestAlarmInfo(Out<bool> out_is_valid, Out<AlarmInfo> out_info, Out<s64> out_time);

private:
    void CheckAndSetupServicesSAndP();
    void SetupSAndP();
    Result GetStaticService(OutInterface<StaticService> out_service,
                            StaticServiceSetupInfo setup_info, const char* name);

    Core::System& m_system;
    std::shared_ptr<TimeManager> m_time;
    ServerManager& m_server_manager;
    bool m_is_s_and_p_setup{};
    StandardLocalSystemClockCore& m_local_system_clock;
    StandardUserSystemClockCore& m_user_system_clock;
    StandardNetworkSystemClockCore& m_network_system_clock;
    StandardSteadyClockCore& m_steady_clock;
    TimeZone& m_time_zone;
    EphemeralNetworkSystemClockCore& m_ephemeral_network_clock;
    SharedMemory& m_shared_memory;
    Alarms& m_alarms;
    LocalSystemClockContextWriter& m_local_system_context_writer;
    NetworkSystemClockContextWriter& m_network_system_context_writer;
    EphemeralNetworkSystemClockContextWriter& m_ephemeral_system_context_writer;
    OperationEvent m_local_operation;
    OperationEvent m_network_operation;
    OperationEvent m_ephemeral_operation;
};

} // namespace Service::PSC::Time


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/core.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/service/psc/time/shared_memory.h"

namespace Service::PSC::Time {
namespace {
template <typename T>
constexpr inline T ReadFromLockFreeAtomicType(const LockFreeAtomicType<T>* p) {
    while (true) {
        // Get the counter.
        auto counter = p->m_counter;

        // Get the value.
        auto value = p->m_value[counter % 2];

        // Fence memory.
        std::atomic_thread_fence(std::memory_order_acquire);

        // Check that the counter matches.
        if (counter == p->m_counter) {
            return value;
        }
    }
}

template <typename T>
constexpr inline void WriteToLockFreeAtomicType(LockFreeAtomicType<T>* p, const T& value) {
    // Get the current counter.
    auto counter = p->m_counter;

    // Increment the counter.
    ++counter;

    // Store the updated value.
    p->m_value[counter % 2] = value;

    // Fence memory.
    std::atomic_thread_fence(std::memory_order_release);

    // Set the updated counter.
    p->m_counter = counter;
}
} // namespace

SharedMemory::SharedMemory(Core::System& system)
    : m_system{system}, m_k_shared_memory{m_system.Kernel().GetTimeSharedMem()},
      m_shared_memory_ptr{reinterpret_cast<SharedMemoryStruct*>(m_k_shared_memory.GetPointer())} {
    std::memset(m_shared_memory_ptr, 0, sizeof(*m_shared_memory_ptr));
}

void SharedMemory::SetLocalSystemContext(const SystemClockContext& context) {
    WriteToLockFreeAtomicType(&m_shared_memory_ptr->local_system_clock_contexts, context);
}

void SharedMemory::SetNetworkSystemContext(const SystemClockContext& context) {
    WriteToLockFreeAtomicType(&m_shared_memory_ptr->network_system_clock_contexts, context);
}

void SharedMemory::SetSteadyClockTimePoint(ClockSourceId clock_source_id, s64 time_point) {
    WriteToLockFreeAtomicType(&m_shared_memory_ptr->steady_time_points,
                              {time_point, clock_source_id});
}

void SharedMemory::SetContinuousAdjustment(const ContinuousAdjustmentTimePoint& time_point) {
    WriteToLockFreeAtomicType(&m_shared_memory_ptr->continuous_adjustment_time_points, time_point);
}

void SharedMemory::SetAutomaticCorrection(bool automatic_correction) {
    WriteToLockFreeAtomicType(&m_shared_memory_ptr->automatic_corrections, automatic_correction);
}

void SharedMemory::UpdateBaseTime(s64 time) {
    SteadyClockTimePoint time_point{
        ReadFromLockFreeAtomicType(&m_shared_memory_ptr->steady_time_points)};

    time_point.time_point = time;

    WriteToLockFreeAtomicType(&m_shared_memory_ptr->steady_time_points, time_point);
}

} // namespace Service::PSC::Time


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <array>

#include "common/common_types.h"
#include "core/hle/service/psc/time/common.h"

namespace Core {
class System;
}

namespace Kernel {
class KSharedMemory;
}

namespace Service::PSC::Time {

template <typename T>
struct LockFreeAtomicType {
    u32 m_counter;
    std::array<T, 2> m_value;
};

struct SharedMemoryStruct {
    LockFreeAtomicType<SteadyClockTimePoint> steady_time_points;
    LockFreeAtomicType<SystemClockContext> local_system_clock_contexts;
    LockFreeAtomicType<SystemClockContext> network_system_clock_contexts;
    LockFreeAtomicType<bool> automatic_corrections;
    LockFreeAtomicType<ContinuousAdjustmentTimePoint> continuous_adjustment_time_points;
    std::array<char, 0xEB8> pad0148;
};
static_assert(offsetof(SharedMemoryStruct, steady_time_points) == 0x0,
              "steady_time_points are in the wrong place!");
static_assert(offsetof(SharedMemoryStruct, local_system_clock_contexts) == 0x38,
              "local_system_clock_contexts are in the wrong place!");
static_assert(offsetof(SharedMemoryStruct, network_system_clock_contexts) == 0x80,
              "network_system_clock_contexts are in the wrong place!");
static_assert(offsetof(SharedMemoryStruct, automatic_corrections) == 0xC8,
              "automatic_corrections are in the wrong place!");
static_assert(offsetof(SharedMemoryStruct, continuous_adjustment_time_points) == 0xD0,
              "continuous_adjustment_time_points are in the wrong place!");
static_assert(sizeof(SharedMemoryStruct) == 0x1000,
              "Time's SharedMemoryStruct has the wrong size!");
static_assert(std::is_trivial_v<SharedMemoryStruct>);

class SharedMemory {
public:
    explicit SharedMemory(Core::System& system);

    Kernel::KSharedMemory& GetKSharedMemory() {
        return m_k_shared_memory;
    }

    void SetLocalSystemContext(const SystemClockContext& context);
    void SetNetworkSystemContext(const SystemClockContext& context);
    void SetSteadyClockTimePoint(ClockSourceId clock_source_id, s64 time_diff);
    void SetContinuousAdjustment(const ContinuousAdjustmentTimePoint& time_point);
    void SetAutomaticCorrection(bool automatic_correction);
    void UpdateBaseTime(s64 time);

private:
    Core::System& m_system;
    Kernel::KSharedMemory& m_k_shared_memory;
    SharedMemoryStruct* m_shared_memory_ptr;
};

} // namespace Service::PSC::Time


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/scope_exit.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h"
#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h"
#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h"
#include "core/hle/service/psc/time/clocks/standard_user_system_clock_core.h"
#include "core/hle/service/psc/time/manager.h"
#include "core/hle/service/psc/time/shared_memory.h"
#include "core/hle/service/psc/time/static.h"
#include "core/hle/service/psc/time/steady_clock.h"
#include "core/hle/service/psc/time/system_clock.h"
#include "core/hle/service/psc/time/time_zone.h"
#include "core/hle/service/psc/time/time_zone_service.h"

namespace Service::PSC::Time {
namespace {
constexpr Result GetTimeFromTimePointAndContext(s64* out_time, SteadyClockTimePoint& time_point,
                                                SystemClockContext& context) {
    R_UNLESS(out_time != nullptr, ResultInvalidArgument);
    R_UNLESS(time_point.IdMatches(context.steady_time_point), ResultClockMismatch);

    *out_time = context.offset + time_point.time_point;
    R_SUCCEED();
}
} // namespace

StaticService::StaticService(Core::System& system_, StaticServiceSetupInfo setup_info,
                             std::shared_ptr<TimeManager> time, const char* name)
    : ServiceFramework{system_, name}, m_system{system}, m_setup_info{setup_info}, m_time{time},
      m_local_system_clock{m_time->m_standard_local_system_clock},
      m_user_system_clock{m_time->m_standard_user_system_clock},
      m_network_system_clock{m_time->m_standard_network_system_clock},
      m_time_zone{m_time->m_time_zone},
      m_ephemeral_network_clock{m_time->m_ephemeral_network_clock}, m_shared_memory{
                                                                        m_time->m_shared_memory} {
    // clang-format off
        static const FunctionInfo functions[] = {
            {0,   D<&StaticService::GetStandardUserSystemClock>, "GetStandardUserSystemClock"},
            {1,   D<&StaticService::GetStandardNetworkSystemClock>, "GetStandardNetworkSystemClock"},
            {2,   D<&StaticService::GetStandardSteadyClock>, "GetStandardSteadyClock"},
            {3,   D<&StaticService::GetTimeZoneService>, "GetTimeZoneService"},
            {4,   D<&StaticService::GetStandardLocalSystemClock>, "GetStandardLocalSystemClock"},
            {5,   D<&StaticService::GetEphemeralNetworkSystemClock>, "GetEphemeralNetworkSystemClock"},
            {20,  D<&StaticService::GetSharedMemoryNativeHandle>, "GetSharedMemoryNativeHandle"},
            {50,  D<&StaticService::SetStandardSteadyClockInternalOffset>, "SetStandardSteadyClockInternalOffset"},
            {51,  D<&StaticService::GetStandardSteadyClockRtcValue>, "GetStandardSteadyClockRtcValue"},
            {100, D<&StaticService::IsStandardUserSystemClockAutomaticCorrectionEnabled>, "IsStandardUserSystemClockAutomaticCorrectionEnabled"},
            {101, D<&StaticService::SetStandardUserSystemClockAutomaticCorrectionEnabled>, "SetStandardUserSystemClockAutomaticCorrectionEnabled"},
            {102, D<&StaticService::GetStandardUserSystemClockInitialYear>, "GetStandardUserSystemClockInitialYear"},
            {200, D<&StaticService::IsStandardNetworkSystemClockAccuracySufficient>, "IsStandardNetworkSystemClockAccuracySufficient"},
            {201, D<&StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime>, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"},
            {300, D<&StaticService::CalculateMonotonicSystemClockBaseTimePoint>, "CalculateMonotonicSystemClockBaseTimePoint"},
            {400, D<&StaticService::GetClockSnapshot>, "GetClockSnapshot"},
            {401, D<&StaticService::GetClockSnapshotFromSystemClockContext>, "GetClockSnapshotFromSystemClockContext"},
            {500, D<&StaticService::CalculateStandardUserSystemClockDifferenceByUser>, "CalculateStandardUserSystemClockDifferenceByUser"},
            {501, D<&StaticService::CalculateSpanBetween>, "CalculateSpanBetween"},
        };
    // clang-format on

    RegisterHandlers(functions);
}

Result StaticService::GetStandardUserSystemClock(OutInterface<SystemClock> out_service) {
    LOG_DEBUG(Service_Time, "called.");

    *out_service = std::make_shared<SystemClock>(m_system, m_user_system_clock,
                                                 m_setup_info.can_write_user_clock,
                                                 m_setup_info.can_write_uninitialized_clock);
    R_SUCCEED();
}

Result StaticService::GetStandardNetworkSystemClock(OutInterface<SystemClock> out_service) {
    LOG_DEBUG(Service_Time, "called.");

    *out_service = std::make_shared<SystemClock>(m_system, m_network_system_clock,
                                                 m_setup_info.can_write_network_clock,
                                                 m_setup_info.can_write_uninitialized_clock);
    R_SUCCEED();
}

Result StaticService::GetStandardSteadyClock(OutInterface<SteadyClock> out_service) {
    LOG_DEBUG(Service_Time, "called.");

    *out_service =
        std::make_shared<SteadyClock>(m_system, m_time, m_setup_info.can_write_steady_clock,
                                      m_setup_info.can_write_uninitialized_clock);
    R_SUCCEED();
}

Result StaticService::GetTimeZoneService(OutInterface<TimeZoneService> out_service) {
    LOG_DEBUG(Service_Time, "called.");

    *out_service =
        std::make_shared<TimeZoneService>(m_system, m_time->m_standard_steady_clock, m_time_zone,
                                          m_setup_info.can_write_timezone_device_location);
    R_SUCCEED();
}

Result StaticService::GetStandardLocalSystemClock(OutInterface<SystemClock> out_service) {
    LOG_DEBUG(Service_Time, "called.");

    *out_service = std::make_shared<SystemClock>(m_system, m_local_system_clock,
                                                 m_setup_info.can_write_local_clock,
                                                 m_setup_info.can_write_uninitialized_clock);
    R_SUCCEED();
}

Result StaticService::GetEphemeralNetworkSystemClock(OutInterface<SystemClock> out_service) {
    LOG_DEBUG(Service_Time, "called.");

    *out_service = std::make_shared<SystemClock>(m_system, m_ephemeral_network_clock,
                                                 m_setup_info.can_write_network_clock,
                                                 m_setup_info.can_write_uninitialized_clock);
    R_SUCCEED();
}

Result StaticService::GetSharedMemoryNativeHandle(
    OutCopyHandle<Kernel::KSharedMemory> out_shared_memory) {
    LOG_DEBUG(Service_Time, "called.");

    *out_shared_memory = &m_shared_memory.GetKSharedMemory();
    R_SUCCEED();
}

Result StaticService::SetStandardSteadyClockInternalOffset(s64 offset_ns) {
    LOG_DEBUG(Service_Time, "called. This function is not implemented!");

    R_UNLESS(m_setup_info.can_write_steady_clock, ResultPermissionDenied);

    R_RETURN(ResultNotImplemented);
}

Result StaticService::GetStandardSteadyClockRtcValue(Out<s64> out_rtc_value) {
    LOG_DEBUG(Service_Time, "called. This function is not implemented!");

    R_RETURN(ResultNotImplemented);
}

Result StaticService::IsStandardUserSystemClockAutomaticCorrectionEnabled(
    Out<bool> out_is_enabled) {
    SCOPE_EXIT {
        LOG_DEBUG(Service_Time, "called. out_is_enabled={}", *out_is_enabled);
    };

    R_UNLESS(m_user_system_clock.IsInitialized(), ResultClockUninitialized);

    *out_is_enabled = m_user_system_clock.GetAutomaticCorrection();

    R_SUCCEED();
}

Result StaticService::SetStandardUserSystemClockAutomaticCorrectionEnabled(
    bool automatic_correction) {
    LOG_DEBUG(Service_Time, "called. automatic_correction={}", automatic_correction);

    R_UNLESS(m_user_system_clock.IsInitialized() && m_time->m_standard_steady_clock.IsInitialized(),
             ResultClockUninitialized);
    R_UNLESS(m_setup_info.can_write_user_clock, ResultPermissionDenied);

    R_TRY(m_user_system_clock.SetAutomaticCorrection(automatic_correction));

    m_shared_memory.SetAutomaticCorrection(automatic_correction);

    SteadyClockTimePoint time_point{};
    R_TRY(m_time->m_standard_steady_clock.GetCurrentTimePoint(time_point));

    m_user_system_clock.SetTimePointAndSignal(time_point);
    m_user_system_clock.GetEvent().Signal();
    R_SUCCEED();
}

Result StaticService::GetStandardUserSystemClockInitialYear(Out<s32> out_year) {
    LOG_DEBUG(Service_Time, "called. This function is not implemented!");

    R_RETURN(ResultNotImplemented);
}

Result StaticService::IsStandardNetworkSystemClockAccuracySufficient(Out<bool> out_is_sufficient) {
    SCOPE_EXIT {
        LOG_DEBUG(Service_Time, "called. out_is_sufficient={}", *out_is_sufficient);
    };

    *out_is_sufficient = m_network_system_clock.IsAccuracySufficient();

    R_SUCCEED();
}

Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
    Out<SteadyClockTimePoint> out_time_point) {
    SCOPE_EXIT {
        LOG_DEBUG(Service_Time, "called. out_time_point={}", *out_time_point);
    };

    R_UNLESS(m_user_system_clock.IsInitialized(), ResultClockUninitialized);

    m_user_system_clock.GetTimePoint(*out_time_point);

    R_SUCCEED();
}

Result StaticService::CalculateMonotonicSystemClockBaseTimePoint(
    Out<s64> out_time, const SystemClockContext& context) {
    SCOPE_EXIT {
        LOG_DEBUG(Service_Time, "called. context={} out_time={}", context, *out_time);
    };

    R_UNLESS(m_time->m_standard_steady_clock.IsInitialized(), ResultClockUninitialized);

    SteadyClockTimePoint time_point{};
    R_TRY(m_time->m_standard_steady_clock.GetCurrentTimePoint(time_point));

    R_UNLESS(time_point.IdMatches(context.steady_time_point), ResultClockMismatch);

    auto one_second_ns{
        std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
    auto ticks{m_system.CoreTiming().GetClockTicks()};
    auto current_time_ns{ConvertToTimeSpan(ticks).count()};
    *out_time = ((context.offset + time_point.time_point) - (current_time_ns / one_second_ns));

    R_SUCCEED();
}

Result StaticService::GetClockSnapshot(OutClockSnapshot out_snapshot, TimeType type) {
    SCOPE_EXIT {
        LOG_DEBUG(Service_Time, "called. type={} out_snapshot={}", type, *out_snapshot);
    };

    SystemClockContext user_context{};
    R_TRY(m_user_system_clock.GetContext(user_context));

    SystemClockContext network_context{};
    R_TRY(m_network_system_clock.GetContext(network_context));

    R_RETURN(GetClockSnapshotImpl(out_snapshot, user_context, network_context, type));
}

Result StaticService::GetClockSnapshotFromSystemClockContext(
    TimeType type, OutClockSnapshot out_snapshot, const SystemClockContext& user_context,
    const SystemClockContext& network_context) {
    SCOPE_EXIT {
        LOG_DEBUG(Service_Time,
                  "called. type={} user_context={} network_context={} out_snapshot={}", type,
                  user_context, network_context, *out_snapshot);
    };

    R_RETURN(GetClockSnapshotImpl(out_snapshot, user_context, network_context, type));
}

Result StaticService::CalculateStandardUserSystemClockDifferenceByUser(Out<s64> out_difference,
                                                                       InClockSnapshot a,
                                                                       InClockSnapshot b) {
    SCOPE_EXIT {
        LOG_DEBUG(Service_Time, "called. a={} b={} out_difference={}", *a, *b, *out_difference);
    };

    auto diff_s =
        std::chrono::seconds(b->user_context.offset) - std::chrono::seconds(a->user_context.offset);

    if (a->user_context == b->user_context ||
        !a->user_context.steady_time_point.IdMatches(b->user_context.steady_time_point)) {
        *out_difference = 0;
        R_SUCCEED();
    }

    if (!a->is_automatic_correction_enabled || !b->is_automatic_correction_enabled) {
        *out_difference = std::chrono::duration_cast<std::chrono::nanoseconds>(diff_s).count();
        R_SUCCEED();
    }

    if (a->network_context.steady_time_point.IdMatches(a->steady_clock_time_point) ||
        b->network_context.steady_time_point.IdMatches(b->steady_clock_time_point)) {
        *out_difference = 0;
        R_SUCCEED();
    }

    *out_difference = std::chrono::duration_cast<std::chrono::nanoseconds>(diff_s).count();
    R_SUCCEED();
}

Result StaticService::CalculateSpanBetween(Out<s64> out_time, InClockSnapshot a,
                                           InClockSnapshot b) {
    SCOPE_EXIT {
        LOG_DEBUG(Service_Time, "called. a={} b={} out_time={}", *a, *b, *out_time);
    };

    s64 time_s{};
    auto res =
        GetSpanBetweenTimePoints(&time_s, a->steady_clock_time_point, b->steady_clock_time_point);

    if (res != ResultSuccess) {
        R_UNLESS(a->network_time != 0 && b->network_time != 0, ResultTimeNotFound);
        time_s = b->network_time - a->network_time;
    }

    *out_time =
        std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(time_s)).count();
    R_SUCCEED();
}

Result StaticService::GetClockSnapshotImpl(OutClockSnapshot out_snapshot,
                                           const SystemClockContext& user_context,
                                           const SystemClockContext& network_context,
                                           TimeType type) {
    out_snapshot->user_context = user_context;
    out_snapshot->network_context = network_context;

    R_TRY(
        m_time->m_standard_steady_clock.GetCurrentTimePoint(out_snapshot->steady_clock_time_point));

    out_snapshot->is_automatic_correction_enabled = m_user_system_clock.GetAutomaticCorrection();

    R_TRY(m_time_zone.GetLocationName(out_snapshot->location_name));

    R_TRY(GetTimeFromTimePointAndContext(&out_snapshot->user_time,
                                         out_snapshot->steady_clock_time_point,
                                         out_snapshot->user_context));

    R_TRY(m_time_zone.ToCalendarTimeWithMyRule(out_snapshot->user_calendar_time,
                                               out_snapshot->user_calendar_additional_time,
                                               out_snapshot->user_time));

    if (GetTimeFromTimePointAndContext(&out_snapshot->network_time,
                                       out_snapshot->steady_clock_time_point,
                                       out_snapshot->network_context) != ResultSuccess) {
        out_snapshot->network_time = 0;
    }

    R_TRY(m_time_zone.ToCalendarTimeWithMyRule(out_snapshot->network_calendar_time,
                                               out_snapshot->network_calendar_additional_time,
                                               out_snapshot->network_time));
    out_snapshot->type = type;
    out_snapshot->unk_CE = 0;
    R_SUCCEED();
}

} // namespace Service::PSC::Time


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/service/cmif_types.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/psc/time/common.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"

namespace Core {
class System;
}

namespace Kernel {
class KSharedMemory;
}

namespace Service::PSC::Time {
class TimeManager;
class StandardLocalSystemClockCore;
class StandardUserSystemClockCore;
class StandardNetworkSystemClockCore;
class TimeZone;
class SystemClock;
class SteadyClock;
class TimeZoneService;
class EphemeralNetworkSystemClockCore;
class SharedMemory;

class StaticService final : public ServiceFramework<StaticService> {
    using InClockSnapshot = InLargeData<ClockSnapshot, BufferAttr_HipcPointer>;
    using OutClockSnapshot = OutLargeData<ClockSnapshot, BufferAttr_HipcPointer>;

public:
    explicit StaticService(Core::System& system, StaticServiceSetupInfo setup_info,
                           std::shared_ptr<TimeManager> time, const char* name);

    ~StaticService() override = default;

    Result GetStandardUserSystemClock(OutInterface<SystemClock> out_service);
    Result GetStandardNetworkSystemClock(OutInterface<SystemClock> out_service);
    Result GetStandardSteadyClock(OutInterface<SteadyClock> out_service);
    Result GetTimeZoneService(OutInterface<TimeZoneService> out_service);
    Result GetStandardLocalSystemClock(OutInterface<SystemClock> out_service);
    Result GetEphemeralNetworkSystemClock(OutInterface<SystemClock> out_service);
    Result GetSharedMemoryNativeHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory);
    Result SetStandardSteadyClockInternalOffset(s64 offset_ns);
    Result GetStandardSteadyClockRtcValue(Out<s64> out_rtc_value);
    Result IsStandardUserSystemClockAutomaticCorrectionEnabled(Out<bool> out_is_enabled);
    Result SetStandardUserSystemClockAutomaticCorrectionEnabled(bool automatic_correction);
    Result GetStandardUserSystemClockInitialYear(Out<s32> out_year);
    Result IsStandardNetworkSystemClockAccuracySufficient(Out<bool> out_is_sufficient);
    Result GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
        Out<SteadyClockTimePoint> out_time_point);
    Result CalculateMonotonicSystemClockBaseTimePoint(Out<s64> out_time,
                                                      const SystemClockContext& context);
    Result GetClockSnapshot(OutClockSnapshot out_snapshot, TimeType type);
    Result GetClockSnapshotFromSystemClockContext(TimeType type, OutClockSnapshot out_snapshot,
                                                  const SystemClockContext& user_context,
                                                  const SystemClockContext& network_context);
    Result CalculateStandardUserSystemClockDifferenceByUser(Out<s64> out_difference,
                                                            InClockSnapshot a, InClockSnapshot b);
    Result CalculateSpanBetween(Out<s64> out_time, InClockSnapshot a, InClockSnapshot b);

private:
    Result GetClockSnapshotImpl(OutClockSnapshot out_snapshot,
                                const SystemClockContext& user_context,
                                const SystemClockContext& network_context, TimeType type);

    Core::System& m_system;
    StaticServiceSetupInfo m_setup_info;
    std::shared_ptr<TimeManager> m_time;
    StandardLocalSystemClockCore& m_local_system_clock;
    StandardUserSystemClockCore& m_user_system_clock;
    StandardNetworkSystemClockCore& m_network_system_clock;
    TimeZone& m_time_zone;
    EphemeralNetworkSystemClockCore& m_ephemeral_network_clock;
    SharedMemory& m_shared_memory;
};

} // namespace Service::PSC::Time