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/kernel/svc.h"
#include "core/hle/service/glue/time/alarm_worker.h"
#include "core/hle/service/psc/time/service_manager.h"
#include "core/hle/service/sm/sm.h"

namespace Service::Glue::Time {

AlarmWorker::AlarmWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource)
    : m_system{system}, m_ctx{system, "Glue:AlarmWorker"}, m_steady_clock_resource{
                                                               steady_clock_resource} {}

AlarmWorker::~AlarmWorker() {
    m_system.CoreTiming().UnscheduleEvent(m_timer_timing_event);

    m_ctx.CloseEvent(m_timer_event);
}

void AlarmWorker::Initialize(std::shared_ptr<Service::PSC::Time::ServiceManager> time_m) {
    m_time_m = std::move(time_m);

    m_timer_event = m_ctx.CreateEvent("Glue:AlarmWorker:TimerEvent");
    m_timer_timing_event = Core::Timing::CreateEvent(
        "Glue:AlarmWorker::AlarmTimer",
        [this](s64 time,
               std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
            m_timer_event->Signal();
            return std::nullopt;
        });

    AttachToClosestAlarmEvent();
}

bool AlarmWorker::GetClosestAlarmInfo(Service::PSC::Time::AlarmInfo& out_alarm_info,
                                      s64& out_time) {
    bool is_valid{};
    Service::PSC::Time::AlarmInfo alarm_info{};
    s64 closest_time{};

    auto res = m_time_m->GetClosestAlarmInfo(&is_valid, &alarm_info, &closest_time);
    ASSERT(res == ResultSuccess);

    if (is_valid) {
        out_alarm_info = alarm_info;
        out_time = closest_time;
    }

    return is_valid;
}

void AlarmWorker::OnPowerStateChanged() {
    Service::PSC::Time::AlarmInfo closest_alarm_info{};
    s64 closest_time{};
    if (!GetClosestAlarmInfo(closest_alarm_info, closest_time)) {
        m_system.CoreTiming().UnscheduleEvent(m_timer_timing_event);
        m_timer_event->Clear();
        return;
    }

    if (closest_alarm_info.alert_time <= closest_time) {
        m_time_m->CheckAndSignalAlarms();
    } else {
        auto next_time{closest_alarm_info.alert_time - closest_time};

        m_system.CoreTiming().UnscheduleEvent(m_timer_timing_event);
        m_timer_event->Clear();

        m_system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds(next_time),
                                            m_timer_timing_event);
    }
}

Result AlarmWorker::AttachToClosestAlarmEvent() {
    m_time_m->GetClosestAlarmUpdatedEvent(&m_event);

    R_SUCCEED();
}

} // namespace Service::Glue::Time


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

#pragma once

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

namespace Core {
class System;
}

namespace Service::PSC::Time {
class ServiceManager;
}

namespace Service::Glue::Time {
class StandardSteadyClockResource;

class AlarmWorker {
public:
    explicit AlarmWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource);
    ~AlarmWorker();

    void Initialize(std::shared_ptr<Service::PSC::Time::ServiceManager> time_m);

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

    Kernel::KEvent& GetTimerEvent() {
        return *m_timer_event;
    }

    void OnPowerStateChanged();

private:
    bool GetClosestAlarmInfo(Service::PSC::Time::AlarmInfo& out_alarm_info, s64& out_time);
    Result AttachToClosestAlarmEvent();

    Core::System& m_system;
    KernelHelpers::ServiceContext m_ctx;
    std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m;

    Kernel::KReadableEvent* m_event{};
    Kernel::KEvent* m_timer_event{};
    std::shared_ptr<Core::Timing::EventType> m_timer_timing_event;
    StandardSteadyClockResource& m_steady_clock_resource;
};

} // namespace Service::Glue::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/hle/service/glue/time/file_timestamp_worker.h"
#include "core/hle/service/psc/time/common.h"
#include "core/hle/service/psc/time/system_clock.h"
#include "core/hle/service/psc/time/time_zone_service.h"

namespace Service::Glue::Time {

void FileTimestampWorker::SetFilesystemPosixTime() {
    s64 time{};
    Service::PSC::Time::CalendarTime calendar_time{};
    Service::PSC::Time::CalendarAdditionalInfo additional_info{};

    if (m_initialized && m_system_clock->GetCurrentTime(&time) == ResultSuccess &&
        m_time_zone->ToCalendarTimeWithMyRule(&calendar_time, &additional_info, time) ==
            ResultSuccess) {
        // TODO IFileSystemProxy::SetCurrentPosixTime
    }
}

} // namespace Service::Glue::Time


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

#pragma once

#include <memory>

#include "common/common_types.h"

namespace Service::PSC::Time {
class SystemClock;
class TimeZoneService;
} // namespace Service::PSC::Time

namespace Service::Glue::Time {

class FileTimestampWorker {
public:
    FileTimestampWorker() = default;

    void SetFilesystemPosixTime();

    std::shared_ptr<Service::PSC::Time::SystemClock> m_system_clock{};
    std::shared_ptr<Service::PSC::Time::TimeZoneService> m_time_zone{};
    bool m_initialized{};
};

} // namespace Service::Glue::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 "common/settings.h"
#include "common/time_zone.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/service/glue/time/manager.h"
#include "core/hle/service/glue/time/time_zone_binary.h"
#include "core/hle/service/psc/time/service_manager.h"
#include "core/hle/service/psc/time/static.h"
#include "core/hle/service/psc/time/system_clock.h"
#include "core/hle/service/psc/time/time_zone_service.h"
#include "core/hle/service/set/system_settings_server.h"
#include "core/hle/service/sm/sm.h"

namespace Service::Glue::Time {
namespace {
s64 CalendarTimeToEpoch(Service::PSC::Time::CalendarTime calendar) {
    constexpr auto is_leap = [](s32 year) -> bool {
        return (((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0));
    };
    constexpr std::array<s32, 12> MonthStartDayOfYear{
        0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
    };

    s16 month_s16{calendar.month};
    s8 month{static_cast<s8>(((month_s16 * 43) & ~std::numeric_limits<s16>::max()) +
                             ((month_s16 * 43) >> 9))};
    s8 month_index{static_cast<s8>(calendar.month - 12 * month)};
    if (month_index == 0) {
        month_index = 12;
    }
    s32 year{(month + calendar.year) - !month_index};
    s32 v8{year >= 0 ? year : year + 3};

    s64 days_since_epoch = calendar.day + MonthStartDayOfYear[month_index - 1];
    days_since_epoch += (year * 365) + (v8 / 4) - (year / 100) + (year / 400) - 365;

    if (month_index <= 2 && is_leap(year)) {
        days_since_epoch--;
    }
    auto epoch_s{((24ll * days_since_epoch + calendar.hour) * 60ll + calendar.minute) * 60ll +
                 calendar.second};
    return epoch_s - 62135683200ll;
}

s64 GetEpochTimeFromInitialYear(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys) {
    s32 year{2000};
    set_sys->GetSettingsItemValueImpl(year, "time", "standard_user_clock_initial_year");

    Service::PSC::Time::CalendarTime calendar{
        .year = static_cast<s16>(year),
        .month = 1,
        .day = 1,
        .hour = 0,
        .minute = 0,
        .second = 0,
    };
    return CalendarTimeToEpoch(calendar);
}

Service::PSC::Time::LocationName GetTimeZoneString(Service::PSC::Time::LocationName& in_name) {
    auto configured_zone = Settings::GetTimeZoneString(Settings::values.time_zone_index.GetValue());

    Service::PSC::Time::LocationName configured_name{};
    std::memcpy(configured_name.data(), configured_zone.data(),
                std::min(configured_name.size(), configured_zone.size()));

    if (!IsTimeZoneBinaryValid(configured_name)) {
        configured_zone = Common::TimeZone::FindSystemTimeZone();
        configured_name = {};
        std::memcpy(configured_name.data(), configured_zone.data(),
                    std::min(configured_name.size(), configured_zone.size()));
    }

    ASSERT_MSG(IsTimeZoneBinaryValid(configured_name), "Invalid time zone {}!",
               configured_name.data());

    return configured_name;
}

} // namespace

TimeManager::TimeManager(Core::System& system)
    : m_steady_clock_resource{system}, m_worker{system, m_steady_clock_resource,
                                                m_file_timestamp_worker} {
    m_time_m =
        system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true);

    auto res = m_time_m->GetStaticServiceAsServiceManager(&m_time_sm);
    ASSERT(res == ResultSuccess);

    m_set_sys =
        system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);

    res = MountTimeZoneBinary(system);
    ASSERT(res == ResultSuccess);

    m_worker.Initialize(m_time_sm, m_set_sys);

    res = m_time_sm->GetStandardUserSystemClock(&m_file_timestamp_worker.m_system_clock);
    ASSERT(res == ResultSuccess);

    res = m_time_sm->GetTimeZoneService(&m_file_timestamp_worker.m_time_zone);
    ASSERT(res == ResultSuccess);

    res = SetupStandardSteadyClockCore();
    ASSERT(res == ResultSuccess);

    Service::PSC::Time::SystemClockContext user_clock_context{};
    res = m_set_sys->GetUserSystemClockContext(&user_clock_context);
    ASSERT(res == ResultSuccess);

    // TODO the local clock should initialise with this epoch time, and be updated somewhere else on
    // first boot to update it, but I haven't been able to find that point (likely via ntc's auto
    // correct as it's defaulted to be enabled). So to get a time that isn't stuck in the past for
    // first boot, grab the current real seconds.
    auto epoch_time{GetEpochTimeFromInitialYear(m_set_sys)};
    if (user_clock_context == Service::PSC::Time::SystemClockContext{}) {
        m_steady_clock_resource.GetRtcTimeInSeconds(epoch_time);
    }

    res = m_time_m->SetupStandardLocalSystemClockCore(user_clock_context, epoch_time);
    ASSERT(res == ResultSuccess);

    Service::PSC::Time::SystemClockContext network_clock_context{};
    res = m_set_sys->GetNetworkSystemClockContext(&network_clock_context);
    ASSERT(res == ResultSuccess);

    s32 network_accuracy_m{};
    m_set_sys->GetSettingsItemValueImpl<s32>(network_accuracy_m, "time",
                                             "standard_network_clock_sufficient_accuracy_minutes");
    auto one_minute_ns{
        std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::minutes(1)).count()};
    s64 network_accuracy_ns{network_accuracy_m * one_minute_ns};

    res = m_time_m->SetupStandardNetworkSystemClockCore(network_clock_context, network_accuracy_ns);
    ASSERT(res == ResultSuccess);

    bool is_automatic_correction_enabled{};
    res = m_set_sys->IsUserSystemClockAutomaticCorrectionEnabled(&is_automatic_correction_enabled);
    ASSERT(res == ResultSuccess);

    Service::PSC::Time::SteadyClockTimePoint automatic_correction_time_point{};
    res = m_set_sys->GetUserSystemClockAutomaticCorrectionUpdatedTime(
        &automatic_correction_time_point);
    ASSERT(res == ResultSuccess);

    res = m_time_m->SetupStandardUserSystemClockCore(is_automatic_correction_enabled,
                                                     automatic_correction_time_point);
    ASSERT(res == ResultSuccess);

    res = m_time_m->SetupEphemeralNetworkSystemClockCore();
    ASSERT(res == ResultSuccess);

    res = SetupTimeZoneServiceCore();
    ASSERT(res == ResultSuccess);

    s64 rtc_time_s{};
    res = m_steady_clock_resource.GetRtcTimeInSeconds(rtc_time_s);
    ASSERT(res == ResultSuccess);

    // TODO system report "launch"
    //      "rtc_reset" = m_steady_clock_resource.m_rtc_reset
    //      "rtc_value" = rtc_time_s

    m_worker.StartThread();

    m_file_timestamp_worker.m_initialized = true;

    s64 system_clock_time{};
    if (m_file_timestamp_worker.m_system_clock->GetCurrentTime(&system_clock_time) ==
        ResultSuccess) {
        Service::PSC::Time::CalendarTime calendar_time{};
        Service::PSC::Time::CalendarAdditionalInfo calendar_additional{};
        if (m_file_timestamp_worker.m_time_zone->ToCalendarTimeWithMyRule(
                &calendar_time, &calendar_additional, system_clock_time) == ResultSuccess) {
            // TODO IFileSystemProxy::SetCurrentPosixTime(system_clock_time,
            // calendar_additional.ut_offset)
        }
    }
}

TimeManager::~TimeManager() {
    ResetTimeZoneBinary();
}

Result TimeManager::SetupStandardSteadyClockCore() {
    Common::UUID external_clock_source_id{};
    auto res = m_set_sys->GetExternalSteadyClockSourceId(&external_clock_source_id);
    ASSERT(res == ResultSuccess);

    s64 external_steady_clock_internal_offset_s{};
    res = m_set_sys->GetExternalSteadyClockInternalOffset(&external_steady_clock_internal_offset_s);
    ASSERT(res == ResultSuccess);

    auto one_second_ns{
        std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
    s64 external_steady_clock_internal_offset_ns{external_steady_clock_internal_offset_s *
                                                 one_second_ns};

    s32 standard_steady_clock_test_offset_m{};
    m_set_sys->GetSettingsItemValueImpl<s32>(standard_steady_clock_test_offset_m, "time",
                                             "standard_steady_clock_test_offset_minutes");
    auto one_minute_ns{
        std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::minutes(1)).count()};
    s64 standard_steady_clock_test_offset_ns{standard_steady_clock_test_offset_m * one_minute_ns};

    auto reset_detected = m_steady_clock_resource.GetResetDetected();
    if (reset_detected) {
        external_clock_source_id = {};
    }

    Common::UUID clock_source_id{};
    m_steady_clock_resource.Initialize(&clock_source_id, &external_clock_source_id);

    if (clock_source_id != external_clock_source_id) {
        m_set_sys->SetExternalSteadyClockSourceId(clock_source_id);
    }

    res = m_time_m->SetupStandardSteadyClockCore(
        reset_detected, clock_source_id, m_steady_clock_resource.GetTime(),
        external_steady_clock_internal_offset_ns, standard_steady_clock_test_offset_ns);
    ASSERT(res == ResultSuccess);
    R_SUCCEED();
}

Result TimeManager::SetupTimeZoneServiceCore() {
    Service::PSC::Time::LocationName name{};
    auto res = m_set_sys->GetDeviceTimeZoneLocationName(&name);
    ASSERT(res == ResultSuccess);

    auto configured_zone = GetTimeZoneString(name);

    if (configured_zone != name) {
        m_set_sys->SetDeviceTimeZoneLocationName(configured_zone);
        name = configured_zone;

        std::shared_ptr<Service::PSC::Time::SystemClock> local_clock;
        m_time_sm->GetStandardLocalSystemClock(&local_clock);

        Service::PSC::Time::SystemClockContext context{};
        local_clock->GetSystemClockContext(&context);
        m_set_sys->SetDeviceTimeZoneLocationUpdatedTime(context.steady_time_point);
    }

    Service::PSC::Time::SteadyClockTimePoint time_point{};
    res = m_set_sys->GetDeviceTimeZoneLocationUpdatedTime(&time_point);
    ASSERT(res == ResultSuccess);

    auto location_count = GetTimeZoneCount();
    Service::PSC::Time::RuleVersion rule_version{};
    GetTimeZoneVersion(rule_version);

    std::span<const u8> rule_buffer{};
    size_t rule_size{};
    res = GetTimeZoneRule(rule_buffer, rule_size, name);
    ASSERT(res == ResultSuccess);

    res = m_time_m->SetupTimeZoneServiceCore(name, rule_version, location_count, time_point,
                                             rule_buffer);
    ASSERT(res == ResultSuccess);

    R_SUCCEED();
}

} // namespace Service::Glue::Time


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

#pragma once

#include <functional>
#include <string>

#include "common/common_types.h"
#include "core/file_sys/vfs/vfs_types.h"
#include "core/hle/service/glue/time/file_timestamp_worker.h"
#include "core/hle/service/glue/time/standard_steady_clock_resource.h"
#include "core/hle/service/glue/time/worker.h"
#include "core/hle/service/service.h"

namespace Core {
class System;
}

namespace Service::PSC::Time {
class ServiceManager;
class StaticService;
} // namespace Service::PSC::Time

namespace Service::Glue::Time {
class TimeManager {
public:
    explicit TimeManager(Core::System& system);
    ~TimeManager();

    std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;

    std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m{};
    std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm{};
    StandardSteadyClockResource m_steady_clock_resource;
    FileTimestampWorker m_file_timestamp_worker;
    TimeWorker m_worker;

private:
    Result SetupStandardSteadyClockCore();
    Result SetupTimeZoneServiceCore();
};
} // namespace Service::Glue::Time


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

#include "core/hle/service/glue/time/pm_state_change_handler.h"

namespace Service::Glue::Time {

PmStateChangeHandler::PmStateChangeHandler(AlarmWorker& alarm_worker)
    : m_alarm_worker{alarm_worker} {
    // TODO Initialize IPmModule, dependent on Rtc and Fs
}

} // namespace Service::Glue::Time


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

#pragma once

#include "common/common_types.h"

namespace Service::Glue::Time {
class AlarmWorker;

class PmStateChangeHandler {
public:
    explicit PmStateChangeHandler(AlarmWorker& alarm_worker);

    AlarmWorker& m_alarm_worker;
    s32 m_priority{};
};
} // namespace Service::Glue::Time


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

#include <chrono>

#include "common/settings.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/service/glue/time/standard_steady_clock_resource.h"
#include "core/hle/service/psc/time/errors.h"

namespace Service::Glue::Time {
namespace {
[[maybe_unused]] constexpr u32 Max77620PmicSession = 0x3A000001;
[[maybe_unused]] constexpr u32 Max77620RtcSession = 0x3B000001;

Result GetTimeInSeconds(Core::System& system, s64& out_time_s) {
    out_time_s = std::chrono::duration_cast<std::chrono::seconds>(
                     std::chrono::system_clock::now().time_since_epoch())
                     .count();

    if (Settings::values.custom_rtc_enabled) {
        out_time_s += Settings::values.custom_rtc_offset.GetValue();
    }
    R_SUCCEED();
}
} // namespace

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

void StandardSteadyClockResource::Initialize(Common::UUID* out_source_id,
                                             Common::UUID* external_source_id) {
    constexpr size_t NUM_TRIES{20};

    size_t i{0};
    Result res{ResultSuccess};
    for (; i < NUM_TRIES; i++) {
        res = SetCurrentTime();
        if (res == ResultSuccess) {
            break;
        }
        Kernel::Svc::SleepThread(m_system, std::chrono::duration_cast<std::chrono::nanoseconds>(
                                               std::chrono::milliseconds(1))
                                               .count());
    }

    if (i < NUM_TRIES) {
        m_set_time_result = ResultSuccess;
        if (*external_source_id != Service::PSC::Time::ClockSourceId{}) {
            m_clock_source_id = *external_source_id;
        } else {
            m_clock_source_id = Common::UUID::MakeRandom();
        }
    } else {
        m_set_time_result = res;
        auto ticks{m_system.CoreTiming().GetClockTicks()};
        m_time = -Service::PSC::Time::ConvertToTimeSpan(ticks).count();
        m_clock_source_id = Common::UUID::MakeRandom();
    }

    if (out_source_id) {
        *out_source_id = m_clock_source_id;
    }
}

bool StandardSteadyClockResource::GetResetDetected() {
    // TODO:
    // call Rtc::GetRtcResetDetected(Max77620RtcSession)
    // if detected:
    //      SetSys::SetExternalSteadyClockSourceId(invalid_id)
    //      Rtc::ClearRtcResetDetected(Max77620RtcSession)
    // set m_rtc_reset to result
    // Instead, only set reset to true if we're booting for the first time.
    m_rtc_reset = false;
    return m_rtc_reset;
}

Result StandardSteadyClockResource::SetCurrentTime() {
    auto start_tick{m_system.CoreTiming().GetClockTicks()};

    s64 rtc_time_s{};
    // TODO R_TRY(Rtc::GetTimeInSeconds(rtc_time_s, Max77620RtcSession))
    R_TRY(GetTimeInSeconds(m_system, rtc_time_s));

    auto end_tick{m_system.CoreTiming().GetClockTicks()};
    auto diff{Service::PSC::Time::ConvertToTimeSpan(end_tick - start_tick)};
    // Why is this here?
    R_UNLESS(diff < std::chrono::milliseconds(101), Service::PSC::Time::ResultRtcTimeout);

    auto one_second_ns{
        std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
    s64 boot_time{rtc_time_s * one_second_ns -
                  Service::PSC::Time::ConvertToTimeSpan(end_tick).count()};

    std::scoped_lock l{m_mutex};
    m_time = boot_time;
    R_SUCCEED();
}

Result StandardSteadyClockResource::GetRtcTimeInSeconds(s64& out_time) {
    // TODO
    // R_TRY(Rtc::GetTimeInSeconds(time_s, Max77620RtcSession)
    R_RETURN(GetTimeInSeconds(m_system, out_time));
}

void StandardSteadyClockResource::UpdateTime() {
    constexpr size_t NUM_TRIES{3};

    size_t i{0};
    Result res{ResultSuccess};
    for (; i < NUM_TRIES; i++) {
        res = SetCurrentTime();
        if (res == ResultSuccess) {
            break;
        }
        Kernel::Svc::SleepThread(m_system, std::chrono::duration_cast<std::chrono::nanoseconds>(
                                               std::chrono::milliseconds(1))
                                               .count());
    }
}

} // namespace Service::Glue::Time


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

#pragma once

#include <mutex>

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

namespace Core {
class System;
}

namespace Service::Glue::Time {
class StandardSteadyClockResource {
public:
    StandardSteadyClockResource(Core::System& system);

    void Initialize(Common::UUID* out_source_id, Common::UUID* external_source_id);

    s64 GetTime() const {
        return m_time;
    }

    bool GetResetDetected();
    Result SetCurrentTime();
    Result GetRtcTimeInSeconds(s64& out_time);
    void UpdateTime();

private:
    Core::System& m_system;

    std::mutex m_mutex;
    Service::PSC::Time::ClockSourceId m_clock_source_id{};
    s64 m_time{};
    Result m_set_time_result;
    bool m_rtc_reset;
};
} // namespace Service::Glue::Time


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

#include <chrono>

#include "common/scope_exit.h"
#include "core/core.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/glue/time/file_timestamp_worker.h"
#include "core/hle/service/glue/time/static.h"
#include "core/hle/service/psc/time/errors.h"
#include "core/hle/service/psc/time/service_manager.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_service.h"
#include "core/hle/service/set/system_settings_server.h"
#include "core/hle/service/sm/sm.h"

namespace Service::Glue::Time {

StaticService::StaticService(Core::System& system_,
                             Service::PSC::Time::StaticServiceSetupInfo setup_info,
                             std::shared_ptr<TimeManager> time, const char* name)
    : ServiceFramework{system_, name}, m_system{system_}, m_time_m{time->m_time_m},
      m_setup_info{setup_info}, m_time_sm{time->m_time_sm},
      m_file_timestamp_worker{time->m_file_timestamp_worker}, m_standard_steady_clock_resource{
                                                                  time->m_steady_clock_resource} {
    // 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);

    m_set_sys =
        m_system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);

    if (m_setup_info.can_write_local_clock && m_setup_info.can_write_user_clock &&
        !m_setup_info.can_write_network_clock && m_setup_info.can_write_timezone_device_location &&
        !m_setup_info.can_write_steady_clock && !m_setup_info.can_write_uninitialized_clock) {
        m_time_m->GetStaticServiceAsAdmin(&m_wrapped_service);
    } else if (!m_setup_info.can_write_local_clock && !m_setup_info.can_write_user_clock &&
               !m_setup_info.can_write_network_clock &&
               !m_setup_info.can_write_timezone_device_location &&
               !m_setup_info.can_write_steady_clock &&
               !m_setup_info.can_write_uninitialized_clock) {
        m_time_m->GetStaticServiceAsUser(&m_wrapped_service);
    } else if (!m_setup_info.can_write_local_clock && !m_setup_info.can_write_user_clock &&
               !m_setup_info.can_write_network_clock &&
               !m_setup_info.can_write_timezone_device_location &&
               m_setup_info.can_write_steady_clock && !m_setup_info.can_write_uninitialized_clock) {
        m_time_m->GetStaticServiceAsRepair(&m_wrapped_service);
    } else {
        UNREACHABLE();
    }

    auto res = m_wrapped_service->GetTimeZoneService(&m_time_zone);
    ASSERT(res == ResultSuccess);
}

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

    R_RETURN(m_wrapped_service->GetStandardUserSystemClock(out_service));
}

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

    R_RETURN(m_wrapped_service->GetStandardNetworkSystemClock(out_service));
}

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

    R_RETURN(m_wrapped_service->GetStandardSteadyClock(out_service));
}

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

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

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

    R_RETURN(m_wrapped_service->GetStandardLocalSystemClock(out_service));
}

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

    R_RETURN(m_wrapped_service->GetEphemeralNetworkSystemClock(out_service));
}

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

    R_RETURN(m_wrapped_service->GetSharedMemoryNativeHandle(out_shared_memory));
}

Result StaticService::SetStandardSteadyClockInternalOffset(s64 offset_ns) {
    LOG_DEBUG(Service_Time, "called. offset_ns={}", offset_ns);

    R_UNLESS(m_setup_info.can_write_steady_clock, Service::PSC::Time::ResultPermissionDenied);

    R_RETURN(m_set_sys->SetExternalSteadyClockInternalOffset(
        offset_ns /
        std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()));
}

Result StaticService::GetStandardSteadyClockRtcValue(Out<s64> out_rtc_value) {
    SCOPE_EXIT {
        LOG_DEBUG(Service_Time, "called. out_rtc_value={}", *out_rtc_value);
    };

    R_RETURN(m_standard_steady_clock_resource.GetRtcTimeInSeconds(*out_rtc_value));
}

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

    R_RETURN(m_wrapped_service->IsStandardUserSystemClockAutomaticCorrectionEnabled(
        out_automatic_correction));
}

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

    R_RETURN(m_wrapped_service->SetStandardUserSystemClockAutomaticCorrectionEnabled(
        automatic_correction));
}

Result StaticService::GetStandardUserSystemClockInitialYear(Out<s32> out_year) {
    SCOPE_EXIT {
        LOG_DEBUG(Service_Time, "called. out_year={}", *out_year);
    };

    R_RETURN(m_set_sys->GetSettingsItemValueImpl<s32>(*out_year, "time",
                                                      "standard_user_clock_initial_year"));
}

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

    R_RETURN(m_wrapped_service->IsStandardNetworkSystemClockAccuracySufficient(out_is_sufficient));
}

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

    R_RETURN(m_wrapped_service->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(
        out_time_point));
}

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

    R_RETURN(m_wrapped_service->CalculateMonotonicSystemClockBaseTimePoint(out_time, context));
}

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

    R_RETURN(m_wrapped_service->GetClockSnapshot(out_snapshot, type));
}

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

    R_RETURN(m_wrapped_service->GetClockSnapshotFromSystemClockContext(
        type, out_snapshot, user_context, network_context));
}

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

    R_RETURN(m_wrapped_service->CalculateStandardUserSystemClockDifferenceByUser(out_time, a, b));
}

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);
    };

    R_RETURN(m_wrapped_service->CalculateSpanBetween(out_time, a, b));
}

} // namespace Service::Glue::Time


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

#pragma once

#include "common/common_types.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/glue/time/manager.h"
#include "core/hle/service/glue/time/time_zone.h"
#include "core/hle/service/psc/time/common.h"

namespace Core {
class System;
}

namespace Service::Set {
class ISystemSettingsServer;
}

namespace Service::PSC::Time {
class StaticService;
class SystemClock;
class SteadyClock;
class TimeZoneService;
class ServiceManager;
} // namespace Service::PSC::Time

namespace Service::Glue::Time {
class FileTimestampWorker;
class StandardSteadyClockResource;

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

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

    ~StaticService() override = default;

    Result GetStandardUserSystemClock(OutInterface<Service::PSC::Time::SystemClock> out_service);
    Result GetStandardNetworkSystemClock(OutInterface<Service::PSC::Time::SystemClock> out_service);
    Result GetStandardSteadyClock(OutInterface<Service::PSC::Time::SteadyClock> out_service);
    Result GetTimeZoneService(OutInterface<TimeZoneService> out_service);
    Result GetStandardLocalSystemClock(OutInterface<Service::PSC::Time::SystemClock> out_service);
    Result GetEphemeralNetworkSystemClock(
        OutInterface<Service::PSC::Time::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<Service::PSC::Time::SteadyClockTimePoint> out_time_point);
    Result CalculateMonotonicSystemClockBaseTimePoint(
        Out<s64> out_time, const Service::PSC::Time::SystemClockContext& context);
    Result GetClockSnapshot(OutClockSnapshot out_snapshot, Service::PSC::Time::TimeType type);
    Result GetClockSnapshotFromSystemClockContext(
        Service::PSC::Time::TimeType type, OutClockSnapshot out_snapshot,
        const Service::PSC::Time::SystemClockContext& user_context,
        const Service::PSC::Time::SystemClockContext& network_context);
    Result CalculateStandardUserSystemClockDifferenceByUser(Out<s64> out_difference,
                                                            InClockSnapshot a, InClockSnapshot b);
    Result CalculateSpanBetween(Out<s64> out_time, InClockSnapshot a, InClockSnapshot b);

private:
    Core::System& m_system;

    std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
    std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m;
    std::shared_ptr<Service::PSC::Time::StaticService> m_wrapped_service;

    Service::PSC::Time::StaticServiceSetupInfo m_setup_info;
    std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm;
    std::shared_ptr<Service::PSC::Time::TimeZoneService> m_time_zone;
    FileTimestampWorker& m_file_timestamp_worker;
    StandardSteadyClockResource& m_standard_steady_clock_resource;
};
} // namespace Service::Glue::Time


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

#include <chrono>

#include "common/scope_exit.h"
#include "core/core.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/glue/time/file_timestamp_worker.h"
#include "core/hle/service/glue/time/time_zone.h"
#include "core/hle/service/glue/time/time_zone_binary.h"
#include "core/hle/service/psc/time/time_zone_service.h"
#include "core/hle/service/set/system_settings_server.h"
#include "core/hle/service/sm/sm.h"

namespace Service::Glue::Time {
namespace {
static std::mutex g_list_mutex;
static Common::IntrusiveListBaseTraits<Service::PSC::Time::OperationEvent>::ListType g_list_nodes{};
} // namespace

TimeZoneService::TimeZoneService(
    Core::System& system_, FileTimestampWorker& file_timestamp_worker,
    bool can_write_timezone_device_location,
    std::shared_ptr<Service::PSC::Time::TimeZoneService> time_zone_service)
    : ServiceFramework{system_, "ITimeZoneService"}, m_system{system},
      m_can_write_timezone_device_location{can_write_timezone_device_location},
      m_file_timestamp_worker{file_timestamp_worker},
      m_wrapped_service{std::move(time_zone_service)}, m_operation_event{m_system} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {0,   D<&TimeZoneService::GetDeviceLocationName>, "GetDeviceLocationName"},
        {1,   D<&TimeZoneService::SetDeviceLocationName>, "SetDeviceLocationName"},
        {2,   D<&TimeZoneService::GetTotalLocationNameCount>, "GetTotalLocationNameCount"},
        {3,   D<&TimeZoneService::LoadLocationNameList>, "LoadLocationNameList"},
        {4,   D<&TimeZoneService::LoadTimeZoneRule>, "LoadTimeZoneRule"},
        {5,   D<&TimeZoneService::GetTimeZoneRuleVersion>, "GetTimeZoneRuleVersion"},
        {6,   D<&TimeZoneService::GetDeviceLocationNameAndUpdatedTime>, "GetDeviceLocationNameAndUpdatedTime"},
        {7,   D<&TimeZoneService::SetDeviceLocationNameWithTimeZoneRule>, "SetDeviceLocationNameWithTimeZoneRule"},
        {8,   D<&TimeZoneService::ParseTimeZoneBinary>, "ParseTimeZoneBinary"},
        {20,  D<&TimeZoneService::GetDeviceLocationNameOperationEventReadableHandle>, "GetDeviceLocationNameOperationEventReadableHandle"},
        {100, D<&TimeZoneService::ToCalendarTime>, "ToCalendarTime"},
        {101, D<&TimeZoneService::ToCalendarTimeWithMyRule>, "ToCalendarTimeWithMyRule"},
        {201, D<&TimeZoneService::ToPosixTime>, "ToPosixTime"},
        {202, D<&TimeZoneService::ToPosixTimeWithMyRule>, "ToPosixTimeWithMyRule"},
    };
    // clang-format on
    RegisterHandlers(functions);

    g_list_nodes.clear();
    m_set_sys =
        m_system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
}

TimeZoneService::~TimeZoneService() = default;

Result TimeZoneService::GetDeviceLocationName(
    Out<Service::PSC::Time::LocationName> out_location_name) {
    SCOPE_EXIT {
        LOG_DEBUG(Service_Time, "called. out_location_name={}", *out_location_name);
    };

    R_RETURN(m_wrapped_service->GetDeviceLocationName(out_location_name));
}

Result TimeZoneService::SetDeviceLocationName(
    const Service::PSC::Time::LocationName& location_name) {
    LOG_DEBUG(Service_Time, "called. location_name={}", location_name);

    R_UNLESS(m_can_write_timezone_device_location, Service::PSC::Time::ResultPermissionDenied);
    R_UNLESS(IsTimeZoneBinaryValid(location_name), Service::PSC::Time::ResultTimeZoneNotFound);

    std::scoped_lock l{m_mutex};

    std::span<const u8> binary{};
    size_t binary_size{};
    R_TRY(GetTimeZoneRule(binary, binary_size, location_name))

    R_TRY(m_wrapped_service->SetDeviceLocationNameWithTimeZoneRule(location_name, binary));

    m_file_timestamp_worker.SetFilesystemPosixTime();

    Service::PSC::Time::SteadyClockTimePoint time_point{};
    Service::PSC::Time::LocationName name{};
    R_TRY(m_wrapped_service->GetDeviceLocationNameAndUpdatedTime(&name, &time_point));

    m_set_sys->SetDeviceTimeZoneLocationName(name);
    m_set_sys->SetDeviceTimeZoneLocationUpdatedTime(time_point);

    std::scoped_lock m{g_list_mutex};
    for (auto& operation_event : g_list_nodes) {
        operation_event.m_event->Signal();
    }
    R_SUCCEED();
}

Result TimeZoneService::GetTotalLocationNameCount(Out<u32> out_count) {
    SCOPE_EXIT {
        LOG_DEBUG(Service_Time, "called. out_count={}", *out_count);
    };

    R_RETURN(m_wrapped_service->GetTotalLocationNameCount(out_count));
}

Result TimeZoneService::LoadLocationNameList(
    Out<u32> out_count,
    OutArray<Service::PSC::Time::LocationName, BufferAttr_HipcMapAlias> out_names, u32 index) {
    SCOPE_EXIT {
        LOG_DEBUG(Service_Time, "called. index={} out_count={} out_names[0]={} out_names[1]={}",
                  index, *out_count, out_names[0], out_names[1]);
    };

    std::scoped_lock l{m_mutex};
    R_RETURN(GetTimeZoneLocationList(*out_count, out_names, out_names.size(), index));
}

Result TimeZoneService::LoadTimeZoneRule(OutRule out_rule,
                                         const Service::PSC::Time::LocationName& name) {
    LOG_DEBUG(Service_Time, "called. name={}", name);

    std::scoped_lock l{m_mutex};
    std::span<const u8> binary{};
    size_t binary_size{};
    R_TRY(GetTimeZoneRule(binary, binary_size, name))
    R_RETURN(m_wrapped_service->ParseTimeZoneBinary(out_rule, binary));
}

Result TimeZoneService::GetTimeZoneRuleVersion(
    Out<Service::PSC::Time::RuleVersion> out_rule_version) {
    SCOPE_EXIT {
        LOG_DEBUG(Service_Time, "called. out_rule_version={}", *out_rule_version);
    };

    R_RETURN(m_wrapped_service->GetTimeZoneRuleVersion(out_rule_version));
}

Result TimeZoneService::GetDeviceLocationNameAndUpdatedTime(
    Out<Service::PSC::Time::LocationName> location_name,
    Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point) {
    SCOPE_EXIT {
        LOG_DEBUG(Service_Time, "called. location_name={} out_time_point={}", *location_name,
                  *out_time_point);
    };

    R_RETURN(m_wrapped_service->GetDeviceLocationNameAndUpdatedTime(location_name, out_time_point));
}

Result TimeZoneService::SetDeviceLocationNameWithTimeZoneRule(
    const Service::PSC::Time::LocationName& location_name,
    InBuffer<BufferAttr_HipcAutoSelect> binary) {
    LOG_DEBUG(Service_Time, "called. location_name={}", location_name);

    R_UNLESS(m_can_write_timezone_device_location, Service::PSC::Time::ResultPermissionDenied);
    R_RETURN(Service::PSC::Time::ResultNotImplemented);
}

Result TimeZoneService::ParseTimeZoneBinary(OutRule out_rule,
                                            InBuffer<BufferAttr_HipcAutoSelect> binary) {
    LOG_DEBUG(Service_Time, "called.");

    R_RETURN(Service::PSC::Time::ResultNotImplemented);
}

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

    if (!operation_event_initialized) {
        operation_event_initialized = false;

        m_operation_event.m_ctx.CloseEvent(m_operation_event.m_event);
        m_operation_event.m_event =
            m_operation_event.m_ctx.CreateEvent("Psc:TimeZoneService:OperationEvent");
        operation_event_initialized = true;
        std::scoped_lock l{m_mutex};
        g_list_nodes.push_back(m_operation_event);
    }

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

Result TimeZoneService::ToCalendarTime(
    Out<Service::PSC::Time::CalendarTime> out_calendar_time,
    Out<Service::PSC::Time::CalendarAdditionalInfo> out_additional_info, s64 time, InRule rule) {
    SCOPE_EXIT {
        LOG_DEBUG(Service_Time, "called. time={} out_calendar_time={} out_additional_info={}", time,
                  *out_calendar_time, *out_additional_info);
    };

    R_RETURN(m_wrapped_service->ToCalendarTime(out_calendar_time, out_additional_info, time, rule));
}

Result TimeZoneService::ToCalendarTimeWithMyRule(
    Out<Service::PSC::Time::CalendarTime> out_calendar_time,
    Out<Service::PSC::Time::CalendarAdditionalInfo> out_additional_info, s64 time) {
    SCOPE_EXIT {
        LOG_DEBUG(Service_Time, "called. time={} out_calendar_time={} out_additional_info={}", time,
                  *out_calendar_time, *out_additional_info);
    };

    R_RETURN(
        m_wrapped_service->ToCalendarTimeWithMyRule(out_calendar_time, out_additional_info, time));
}

Result TimeZoneService::ToPosixTime(Out<u32> out_count,
                                    OutArray<s64, BufferAttr_HipcPointer> out_times,
                                    const Service::PSC::Time::CalendarTime& calendar_time,
                                    InRule rule) {
    SCOPE_EXIT {
        LOG_DEBUG(Service_Time,
                  "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={}",
                  calendar_time, *out_count, out_times[0], out_times[1]);
    };

    R_RETURN(m_wrapped_service->ToPosixTime(out_count, out_times, calendar_time, rule));
}

Result TimeZoneService::ToPosixTimeWithMyRule(
    Out<u32> out_count, OutArray<s64, BufferAttr_HipcPointer> out_times,
    const Service::PSC::Time::CalendarTime& calendar_time) {
    SCOPE_EXIT {
        LOG_DEBUG(Service_Time,
                  "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={}",
                  calendar_time, *out_count, out_times[0], out_times[1]);
    };

    R_RETURN(m_wrapped_service->ToPosixTimeWithMyRule(out_count, out_times, calendar_time));
}

} // namespace Service::Glue::Time


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

#pragma once

#include <memory>
#include <mutex>
#include <span>
#include <vector>

#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 Tz {
struct Rule;
}

namespace Service::Set {
class ISystemSettingsServer;
}

namespace Service::PSC::Time {
class TimeZoneService;
}

namespace Service::Glue::Time {
class FileTimestampWorker;

class TimeZoneService final : public ServiceFramework<TimeZoneService> {
    using InRule = InLargeData<Tz::Rule, BufferAttr_HipcMapAlias>;
    using OutRule = OutLargeData<Tz::Rule, BufferAttr_HipcMapAlias>;

public:
    explicit TimeZoneService(
        Core::System& system, FileTimestampWorker& file_timestamp_worker,
        bool can_write_timezone_device_location,
        std::shared_ptr<Service::PSC::Time::TimeZoneService> time_zone_service);

    ~TimeZoneService() override;

    Result GetDeviceLocationName(Out<Service::PSC::Time::LocationName> out_location_name);
    Result SetDeviceLocationName(const Service::PSC::Time::LocationName& location_name);
    Result GetTotalLocationNameCount(Out<u32> out_count);
    Result LoadLocationNameList(
        Out<u32> out_count,
        OutArray<Service::PSC::Time::LocationName, BufferAttr_HipcMapAlias> out_names, u32 index);
    Result LoadTimeZoneRule(OutRule out_rule,
                            const Service::PSC::Time::LocationName& location_name);
    Result GetTimeZoneRuleVersion(Out<Service::PSC::Time::RuleVersion> out_rule_version);
    Result GetDeviceLocationNameAndUpdatedTime(
        Out<Service::PSC::Time::LocationName> location_name,
        Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point);
    Result SetDeviceLocationNameWithTimeZoneRule(
        const Service::PSC::Time::LocationName& location_name,
        InBuffer<BufferAttr_HipcAutoSelect> binary);
    Result ParseTimeZoneBinary(OutRule out_rule, InBuffer<BufferAttr_HipcAutoSelect> binary);
    Result GetDeviceLocationNameOperationEventReadableHandle(
        OutCopyHandle<Kernel::KReadableEvent> out_event);
    Result ToCalendarTime(Out<Service::PSC::Time::CalendarTime> out_calendar_time,
                          Out<Service::PSC::Time::CalendarAdditionalInfo> out_additional_info,
                          s64 time, InRule rule);
    Result ToCalendarTimeWithMyRule(
        Out<Service::PSC::Time::CalendarTime> out_calendar_time,
        Out<Service::PSC::Time::CalendarAdditionalInfo> out_additional_info, s64 time);
    Result ToPosixTime(Out<u32> out_count, OutArray<s64, BufferAttr_HipcPointer> out_times,
                       const Service::PSC::Time::CalendarTime& calendar_time, InRule rule);
    Result ToPosixTimeWithMyRule(Out<u32> out_count,
                                 OutArray<s64, BufferAttr_HipcPointer> out_times,
                                 const Service::PSC::Time::CalendarTime& calendar_time);

private:
    Core::System& m_system;
    std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;

    bool m_can_write_timezone_device_location;
    FileTimestampWorker& m_file_timestamp_worker;
    std::shared_ptr<Service::PSC::Time::TimeZoneService> m_wrapped_service;
    std::mutex m_mutex;
    bool operation_event_initialized{};
    Service::PSC::Time::OperationEvent m_operation_event;
};

} // namespace Service::Glue::Time


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

#include "core/core.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/system_archive/system_archive.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/glue/time/time_zone_binary.h"

namespace Service::Glue::Time {
namespace {
constexpr u64 TimeZoneBinaryId = 0x10000000000080E;

static FileSys::VirtualDir g_time_zone_binary_romfs{};
static Result g_time_zone_binary_mount_result{ResultUnknown};
static std::vector<u8> g_time_zone_scratch_space(0x2800, 0);

Result TimeZoneReadBinary(size_t& out_read_size, std::span<u8> out_buffer, size_t out_buffer_size,
                          std::string_view path) {
    R_UNLESS(g_time_zone_binary_mount_result == ResultSuccess, g_time_zone_binary_mount_result);

    auto vfs_file{g_time_zone_binary_romfs->GetFileRelative(path)};
    R_UNLESS(vfs_file, ResultUnknown);

    auto file_size{vfs_file->GetSize()};
    R_UNLESS(file_size > 0, ResultUnknown);

    R_UNLESS(file_size <= out_buffer_size, Service::PSC::Time::ResultFailed);

    out_read_size = vfs_file->Read(out_buffer.data(), file_size);
    R_UNLESS(out_read_size > 0, ResultUnknown);

    R_SUCCEED();
}
} // namespace

void ResetTimeZoneBinary() {
    g_time_zone_binary_romfs = {};
    g_time_zone_binary_mount_result = ResultUnknown;
    g_time_zone_scratch_space.clear();
    g_time_zone_scratch_space.resize(0x2800, 0);
}

Result MountTimeZoneBinary(Core::System& system) {
    ResetTimeZoneBinary();

    auto& fsc{system.GetFileSystemController()};
    std::unique_ptr<FileSys::NCA> nca{};

    auto* bis_system = fsc.GetSystemNANDContents();

    R_UNLESS(bis_system, ResultUnknown);

    nca = bis_system->GetEntry(TimeZoneBinaryId, FileSys::ContentRecordType::Data);

    if (nca) {
        g_time_zone_binary_romfs = FileSys::ExtractRomFS(nca->GetRomFS());
    }

    if (g_time_zone_binary_romfs) {
        // Validate that the romfs is readable, using invalid firmware keys can cause this to get
        // set but the files to be garbage. In that case, we want to hit the next path and
        // synthesise them instead.
        g_time_zone_binary_mount_result = ResultSuccess;
        Service::PSC::Time::LocationName name{"Etc/GMT"};
        if (!IsTimeZoneBinaryValid(name)) {
            ResetTimeZoneBinary();
        }
    }

    if (!g_time_zone_binary_romfs) {
        g_time_zone_binary_romfs = FileSys::ExtractRomFS(
            FileSys::SystemArchive::SynthesizeSystemArchive(TimeZoneBinaryId));
    }

    R_UNLESS(g_time_zone_binary_romfs, ResultUnknown);

    g_time_zone_binary_mount_result = ResultSuccess;
    R_SUCCEED();
}

void GetTimeZoneBinaryListPath(std::string& out_path) {
    if (g_time_zone_binary_mount_result != ResultSuccess) {
        return;
    }
    // out_path = fmt::format("{}:/binaryList.txt", "TimeZoneBinary");
    out_path = "/binaryList.txt";
}

void GetTimeZoneBinaryVersionPath(std::string& out_path) {
    if (g_time_zone_binary_mount_result != ResultSuccess) {
        return;
    }
    // out_path = fmt::format("{}:/version.txt", "TimeZoneBinary");
    out_path = "/version.txt";
}

void GetTimeZoneZonePath(std::string& out_path, const Service::PSC::Time::LocationName& name) {
    if (g_time_zone_binary_mount_result != ResultSuccess) {
        return;
    }
    // out_path = fmt::format("{}:/zoneinfo/{}", "TimeZoneBinary", name);
    out_path = fmt::format("/zoneinfo/{}", name.data());
}

bool IsTimeZoneBinaryValid(const Service::PSC::Time::LocationName& name) {
    std::string path{};
    GetTimeZoneZonePath(path, name);

    auto vfs_file{g_time_zone_binary_romfs->GetFileRelative(path)};
    if (!vfs_file) {
        LOG_INFO(Service_Time, "Could not find timezone file {}", path);
        return false;
    }
    return vfs_file->GetSize() != 0;
}

u32 GetTimeZoneCount() {
    std::string path{};
    GetTimeZoneBinaryListPath(path);

    size_t bytes_read{};
    if (TimeZoneReadBinary(bytes_read, g_time_zone_scratch_space, 0x2800, path) != ResultSuccess) {
        return 0;
    }
    if (bytes_read == 0) {
        return 0;
    }

    auto chars = std::span(reinterpret_cast<char*>(g_time_zone_scratch_space.data()), bytes_read);
    u32 count{};
    for (auto chr : chars) {
        if (chr == '\n') {
            count++;
        }
    }
    return count;
}

Result GetTimeZoneVersion(Service::PSC::Time::RuleVersion& out_rule_version) {
    std::string path{};
    GetTimeZoneBinaryVersionPath(path);

    auto rule_version_buffer{std::span(reinterpret_cast<u8*>(&out_rule_version),
                                       sizeof(Service::PSC::Time::RuleVersion))};
    size_t bytes_read{};
    R_TRY(TimeZoneReadBinary(bytes_read, rule_version_buffer, rule_version_buffer.size_bytes(),
                             path));

    rule_version_buffer[bytes_read] = 0;
    R_SUCCEED();
}

Result GetTimeZoneRule(std::span<const u8>& out_rule, size_t& out_rule_size,
                       const Service::PSC::Time::LocationName& name) {
    std::string path{};
    GetTimeZoneZonePath(path, name);

    size_t bytes_read{};
    R_TRY(TimeZoneReadBinary(bytes_read, g_time_zone_scratch_space,
                             g_time_zone_scratch_space.size(), path));

    out_rule = std::span(g_time_zone_scratch_space.data(), bytes_read);
    out_rule_size = bytes_read;
    R_SUCCEED();
}

Result GetTimeZoneLocationList(u32& out_count,
                               std::span<Service::PSC::Time::LocationName> out_names,
                               size_t max_names, u32 index) {
    std::string path{};
    GetTimeZoneBinaryListPath(path);

    size_t bytes_read{};
    R_TRY(TimeZoneReadBinary(bytes_read, g_time_zone_scratch_space,
                             g_time_zone_scratch_space.size(), path));

    out_count = 0;
    R_SUCCEED_IF(bytes_read == 0);

    Service::PSC::Time::LocationName current_name{};
    size_t current_name_len{};
    std::span<const u8> chars{g_time_zone_scratch_space};
    u32 name_count{};

    for (auto chr : chars) {
        if (chr == '\r') {
            continue;
        }

        if (chr == '\n') {
            if (name_count >= index) {
                out_names[out_count] = current_name;
                out_count++;
                if (out_count >= max_names) {
                    break;
                }
            }
            name_count++;
            current_name_len = 0;
            current_name = {};
            continue;
        }

        if (chr == '\0') {
            break;
        }

        R_UNLESS(current_name_len <= current_name.size() - 2, Service::PSC::Time::ResultFailed);

        current_name[current_name_len++] = chr;
    }

    R_SUCCEED();
}

} // namespace Service::Glue::Time


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

#pragma once

#include <span>
#include <string>
#include <string_view>

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

namespace Core {
class System;
}

namespace Service::Glue::Time {

void ResetTimeZoneBinary();
Result MountTimeZoneBinary(Core::System& system);
void GetTimeZoneBinaryListPath(std::string& out_path);
void GetTimeZoneBinaryVersionPath(std::string& out_path);
void GetTimeZoneZonePath(std::string& out_path, const Service::PSC::Time::LocationName& name);
bool IsTimeZoneBinaryValid(const Service::PSC::Time::LocationName& name);
u32 GetTimeZoneCount();
Result GetTimeZoneVersion(Service::PSC::Time::RuleVersion& out_rule_version);
Result GetTimeZoneRule(std::span<const u8>& out_rule, size_t& out_rule_size,
                       const Service::PSC::Time::LocationName& name);
Result GetTimeZoneLocationList(u32& out_count,
                               std::span<Service::PSC::Time::LocationName> out_names,
                               size_t max_names, u32 index);

} // namespace Service::Glue::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/service/glue/time/file_timestamp_worker.h"
#include "core/hle/service/glue/time/standard_steady_clock_resource.h"
#include "core/hle/service/glue/time/worker.h"
#include "core/hle/service/os/multi_wait_utils.h"
#include "core/hle/service/psc/time/common.h"
#include "core/hle/service/psc/time/service_manager.h"
#include "core/hle/service/psc/time/static.h"
#include "core/hle/service/psc/time/system_clock.h"
#include "core/hle/service/set/system_settings_server.h"
#include "core/hle/service/sm/sm.h"

namespace Service::Glue::Time {
namespace {

bool g_ig_report_network_clock_context_set{};
Service::PSC::Time::SystemClockContext g_report_network_clock_context{};
bool g_ig_report_ephemeral_clock_context_set{};
Service::PSC::Time::SystemClockContext g_report_ephemeral_clock_context{};

template <typename T>
T GetSettingsItemValue(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys,
                       const char* category, const char* name) {
    T v{};
    auto res = set_sys->GetSettingsItemValueImpl(v, category, name);
    ASSERT(res == ResultSuccess);
    return v;
}

} // namespace

TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource,
                       FileTimestampWorker& file_timestamp_worker)
    : m_system{system}, m_ctx{m_system, "Glue:TimeWorker"}, m_event{m_ctx.CreateEvent(
                                                                "Glue:TimeWorker:Event")},
      m_steady_clock_resource{steady_clock_resource},
      m_file_timestamp_worker{file_timestamp_worker}, m_timer_steady_clock{m_ctx.CreateEvent(
                                                          "Glue:TimeWorker:SteadyClockTimerEvent")},
      m_timer_file_system{m_ctx.CreateEvent("Glue:TimeWorker:FileTimeTimerEvent")},
      m_alarm_worker{m_system, m_steady_clock_resource}, m_pm_state_change_handler{m_alarm_worker} {
    g_ig_report_network_clock_context_set = false;
    g_report_network_clock_context = {};
    g_ig_report_ephemeral_clock_context_set = false;
    g_report_ephemeral_clock_context = {};

    m_timer_steady_clock_timing_event = Core::Timing::CreateEvent(
        "Time::SteadyClockEvent",
        [this](s64 time,
               std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
            m_timer_steady_clock->Signal();
            return std::nullopt;
        });

    m_timer_file_system_timing_event = Core::Timing::CreateEvent(
        "Time::SteadyClockEvent",
        [this](s64 time,
               std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
            m_timer_file_system->Signal();
            return std::nullopt;
        });
}

TimeWorker::~TimeWorker() {
    m_local_clock_event->Signal();
    m_network_clock_event->Signal();
    m_ephemeral_clock_event->Signal();
    std::this_thread::sleep_for(std::chrono::milliseconds(16));

    m_thread.request_stop();
    m_event->Signal();
    m_thread.join();

    m_ctx.CloseEvent(m_event);
    m_system.CoreTiming().UnscheduleEvent(m_timer_steady_clock_timing_event);
    m_ctx.CloseEvent(m_timer_steady_clock);
    m_system.CoreTiming().UnscheduleEvent(m_timer_file_system_timing_event);
    m_ctx.CloseEvent(m_timer_file_system);
}

void TimeWorker::Initialize(std::shared_ptr<Service::PSC::Time::StaticService> time_sm,
                            std::shared_ptr<Service::Set::ISystemSettingsServer> set_sys) {
    m_set_sys = std::move(set_sys);
    m_time_m =
        m_system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true);
    m_time_sm = std::move(time_sm);

    m_alarm_worker.Initialize(m_time_m);

    auto steady_clock_interval_m = GetSettingsItemValue<s32>(
        m_set_sys, "time", "standard_steady_clock_rtc_update_interval_minutes");

    auto one_minute_ns{
        std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::minutes(1)).count()};
    s64 steady_clock_interval_ns{steady_clock_interval_m * one_minute_ns};

    m_system.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds(0),
                                               std::chrono::nanoseconds(steady_clock_interval_ns),
                                               m_timer_steady_clock_timing_event);

    auto fs_notify_time_s =
        GetSettingsItemValue<s32>(m_set_sys, "time", "notify_time_to_fs_interval_seconds");
    auto one_second_ns{
        std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()};
    s64 fs_notify_time_ns{fs_notify_time_s * one_second_ns};

    m_system.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds(0),
                                               std::chrono::nanoseconds(fs_notify_time_ns),
                                               m_timer_file_system_timing_event);

    auto res = m_time_sm->GetStandardLocalSystemClock(&m_local_clock);
    ASSERT(res == ResultSuccess);
    res = m_time_m->GetStandardLocalClockOperationEvent(&m_local_clock_event);
    ASSERT(res == ResultSuccess);

    res = m_time_sm->GetStandardNetworkSystemClock(&m_network_clock);
    ASSERT(res == ResultSuccess);
    res = m_time_m->GetStandardNetworkClockOperationEventForServiceManager(&m_network_clock_event);
    ASSERT(res == ResultSuccess);

    res = m_time_sm->GetEphemeralNetworkSystemClock(&m_ephemeral_clock);
    ASSERT(res == ResultSuccess);
    res =
        m_time_m->GetEphemeralNetworkClockOperationEventForServiceManager(&m_ephemeral_clock_event);
    ASSERT(res == ResultSuccess);

    res = m_time_m->GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(
        &m_standard_user_auto_correct_clock_event);
    ASSERT(res == ResultSuccess);
}

void TimeWorker::StartThread() {
    m_thread = std::jthread(std::bind_front(&TimeWorker::ThreadFunc, this));
}

void TimeWorker::ThreadFunc(std::stop_token stop_token) {
    Common::SetCurrentThreadName("TimeWorker");
    Common::SetCurrentThreadPriority(Common::ThreadPriority::Low);

    while (!stop_token.stop_requested()) {
        enum class EventType : s32 {
            Exit = 0,
            PowerStateChange = 1,
            SignalAlarms = 2,
            UpdateLocalSystemClock = 3,
            UpdateNetworkSystemClock = 4,
            UpdateEphemeralSystemClock = 5,
            UpdateSteadyClock = 6,
            UpdateFileTimestamp = 7,
            AutoCorrect = 8,
        };

        s32 index{};

        if (m_pm_state_change_handler.m_priority != 0) {
            // TODO: gIPmModuleService::GetEvent() 1
            index = WaitAny(m_system.Kernel(),
                            &m_event->GetReadableEvent(), // 0
                            &m_alarm_worker.GetEvent()    // 1
            );
        } else {
            // TODO: gIPmModuleService::GetEvent() 1
            index = WaitAny(m_system.Kernel(),
                            &m_event->GetReadableEvent(),                       // 0
                            &m_alarm_worker.GetEvent(),                         // 1
                            &m_alarm_worker.GetTimerEvent().GetReadableEvent(), // 2
                            m_local_clock_event,                                // 3
                            m_network_clock_event,                              // 4
                            m_ephemeral_clock_event,                            // 5
                            &m_timer_steady_clock->GetReadableEvent(),          // 6
                            &m_timer_file_system->GetReadableEvent(),           // 7
                            m_standard_user_auto_correct_clock_event            // 8
            );
        }

        switch (static_cast<EventType>(index)) {
        case EventType::Exit:
            return;

        case EventType::PowerStateChange:
            m_alarm_worker.GetEvent().Clear();
            if (m_pm_state_change_handler.m_priority <= 1) {
                m_alarm_worker.OnPowerStateChanged();
            }
            break;

        case EventType::SignalAlarms:
            m_alarm_worker.GetTimerEvent().Clear();
            m_time_m->CheckAndSignalAlarms();
            break;

        case EventType::UpdateLocalSystemClock: {
            m_local_clock_event->Clear();

            Service::PSC::Time::SystemClockContext context{};
            R_ASSERT(m_local_clock->GetSystemClockContext(&context));

            m_set_sys->SetUserSystemClockContext(context);
            m_file_timestamp_worker.SetFilesystemPosixTime();
            break;
        }

        case EventType::UpdateNetworkSystemClock: {
            m_network_clock_event->Clear();

            Service::PSC::Time::SystemClockContext context{};
            R_ASSERT(m_network_clock->GetSystemClockContext(&context));

            m_set_sys->SetNetworkSystemClockContext(context);

            s64 time{};
            if (m_network_clock->GetCurrentTime(&time) != ResultSuccess) {
                break;
            }

            [[maybe_unused]] auto offset_before{
                g_ig_report_network_clock_context_set ? g_report_network_clock_context.offset : 0};
            // TODO system report "standard_netclock_operation"
            //              "clock_time" = time
            //              "context_offset_before" = offset_before
            //              "context_offset_after"  = context.offset
            g_report_network_clock_context = context;
            if (!g_ig_report_network_clock_context_set) {
                g_ig_report_network_clock_context_set = true;
            }

            m_file_timestamp_worker.SetFilesystemPosixTime();
            break;
        }

        case EventType::UpdateEphemeralSystemClock: {
            m_ephemeral_clock_event->Clear();

            Service::PSC::Time::SystemClockContext context{};
            auto res = m_ephemeral_clock->GetSystemClockContext(&context);
            if (res != ResultSuccess) {
                break;
            }

            s64 time{};
            res = m_ephemeral_clock->GetCurrentTime(&time);
            if (res != ResultSuccess) {
                break;
            }

            [[maybe_unused]] auto offset_before{g_ig_report_ephemeral_clock_context_set
                                                    ? g_report_ephemeral_clock_context.offset
                                                    : 0};
            // TODO system report "ephemeral_netclock_operation"
            //              "clock_time" = time
            //              "context_offset_before" = offset_before
            //              "context_offset_after"  = context.offset
            g_report_ephemeral_clock_context = context;
            if (!g_ig_report_ephemeral_clock_context_set) {
                g_ig_report_ephemeral_clock_context_set = true;
            }
            break;
        }

        case EventType::UpdateSteadyClock:
            m_timer_steady_clock->Clear();

            m_steady_clock_resource.UpdateTime();
            m_time_m->SetStandardSteadyClockBaseTime(m_steady_clock_resource.GetTime());
            break;

        case EventType::UpdateFileTimestamp:
            m_timer_file_system->Clear();

            m_file_timestamp_worker.SetFilesystemPosixTime();
            break;

        case EventType::AutoCorrect: {
            m_standard_user_auto_correct_clock_event->Clear();

            bool automatic_correction{};
            R_ASSERT(m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled(
                &automatic_correction));

            Service::PSC::Time::SteadyClockTimePoint time_point{};
            R_ASSERT(
                m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(&time_point));

            m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction);
            m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
            break;
        }

        default:
            UNREACHABLE();
        }
    }
}

} // namespace Service::Glue::Time


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

#pragma once

#include "common/common_types.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/glue/time/alarm_worker.h"
#include "core/hle/service/glue/time/pm_state_change_handler.h"
#include "core/hle/service/kernel_helpers.h"

namespace Service::Set {
class ISystemSettingsServer;
}

namespace Service::PSC::Time {
class StaticService;
class SystemClock;
} // namespace Service::PSC::Time

namespace Service::Glue::Time {
class FileTimestampWorker;
class StandardSteadyClockResource;

class TimeWorker {
public:
    explicit TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource,
                        FileTimestampWorker& file_timestamp_worker);
    ~TimeWorker();

    void Initialize(std::shared_ptr<Service::PSC::Time::StaticService> time_sm,
                    std::shared_ptr<Service::Set::ISystemSettingsServer> set_sys);

    void StartThread();

private:
    void ThreadFunc(std::stop_token stop_token);

    Core::System& m_system;
    KernelHelpers::ServiceContext m_ctx;
    std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;

    std::jthread m_thread;
    Kernel::KEvent* m_event{};
    std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m;
    std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm;
    std::shared_ptr<Service::PSC::Time::SystemClock> m_network_clock;
    std::shared_ptr<Service::PSC::Time::SystemClock> m_local_clock;
    std::shared_ptr<Service::PSC::Time::SystemClock> m_ephemeral_clock;
    StandardSteadyClockResource& m_steady_clock_resource;
    FileTimestampWorker& m_file_timestamp_worker;
    Kernel::KReadableEvent* m_local_clock_event{};
    Kernel::KReadableEvent* m_network_clock_event{};
    Kernel::KReadableEvent* m_ephemeral_clock_event{};
    Kernel::KReadableEvent* m_standard_user_auto_correct_clock_event{};
    Kernel::KEvent* m_timer_steady_clock{};
    std::shared_ptr<Core::Timing::EventType> m_timer_steady_clock_timing_event;
    Kernel::KEvent* m_timer_file_system{};
    std::shared_ptr<Core::Timing::EventType> m_timer_file_system_timing_event;
    AlarmWorker m_alarm_worker;
    PmStateChangeHandler m_pm_state_change_handler;
};

} // namespace Service::Glue::Time


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

#include <memory>

#include "core/hle/service/grc/grc.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"

namespace Service::GRC {

class GRC final : public ServiceFramework<GRC> {
public:
    explicit GRC(Core::System& system_) : ServiceFramework{system_, "grc:c"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {1, nullptr, "OpenContinuousRecorder"},
            {2, nullptr, "OpenGameMovieTrimmer"},
            {3, nullptr, "OpenOffscreenRecorder"},
            {101, nullptr, "CreateMovieMaker"},
            {9903, nullptr, "SetOffscreenRecordingMarker"}
        };
        // clang-format on

        RegisterHandlers(functions);
    }
};

void LoopProcess(Core::System& system) {
    auto server_manager = std::make_unique<ServerManager>(system);

    server_manager->RegisterNamedService("grc:c", std::make_shared<GRC>(system));
    ServerManager::RunServer(std::move(server_manager));
}

} // namespace Service::GRC


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

#pragma once

namespace Core {
class System;
}

namespace Service::GRC {

void LoopProcess(Core::System& system);

} // namespace Service::GRC


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#include "common/logging/log.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/hid/active_vibration_device_list.h"
#include "hid_core/hid_result.h"
#include "hid_core/hid_util.h"
#include "hid_core/resource_manager.h"
#include "hid_core/resources/vibration/vibration_device.h"

namespace Service::HID {

IActiveVibrationDeviceList::IActiveVibrationDeviceList(Core::System& system_,
                                                       std::shared_ptr<ResourceManager> resource)
    : ServiceFramework{system_, "IActiveVibrationDeviceList"}, resource_manager(resource) {
    // clang-format off
        static const FunctionInfo functions[] = {
            {0, C<&IActiveVibrationDeviceList::ActivateVibrationDevice>, "ActivateVibrationDevice"},
        };
    // clang-format on

    RegisterHandlers(functions);
}

IActiveVibrationDeviceList::~IActiveVibrationDeviceList() = default;

Result IActiveVibrationDeviceList::ActivateVibrationDevice(
    Core::HID::VibrationDeviceHandle vibration_device_handle) {
    LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}",
              vibration_device_handle.npad_type, vibration_device_handle.npad_id,
              vibration_device_handle.device_index);

    std::scoped_lock lock{mutex};

    R_TRY(IsVibrationHandleValid(vibration_device_handle));

    for (std::size_t i = 0; i < list_size; i++) {
        if (vibration_device_handle.device_index == vibration_device_list[i].device_index &&
            vibration_device_handle.npad_id == vibration_device_list[i].npad_id &&
            vibration_device_handle.npad_type == vibration_device_list[i].npad_type) {
            R_SUCCEED();
        }
    }

    R_UNLESS(list_size < MaxVibrationDevicesHandles, ResultVibrationDeviceIndexOutOfRange);
    R_TRY(resource_manager->GetVibrationDevice(vibration_device_handle)->Activate());

    vibration_device_list[list_size++] = vibration_device_handle;
    R_SUCCEED();
}

} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include <array>
#include <memory>
#include <mutex>

#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
#include "hid_core/hid_types.h"

namespace Core {
class System;
}

namespace Service::HID {
class ResourceManager;

class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
public:
    explicit IActiveVibrationDeviceList(Core::System& system_,
                                        std::shared_ptr<ResourceManager> resource);
    ~IActiveVibrationDeviceList() override;

private:
    static constexpr std::size_t MaxVibrationDevicesHandles{0x100};

    Result ActivateVibrationDevice(Core::HID::VibrationDeviceHandle vibration_device_handle);

    mutable std::mutex mutex;
    std::size_t list_size{};
    std::array<Core::HID::VibrationDeviceHandle, MaxVibrationDevicesHandles>
        vibration_device_list{};
    std::shared_ptr<ResourceManager> resource_manager;
};

} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#include "common/logging/log.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/hid/applet_resource.h"
#include "hid_core/resource_manager.h"

namespace Service::HID {

IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource,
                                 u64 applet_resource_user_id)
    : ServiceFramework{system_, "IAppletResource"}, aruid{applet_resource_user_id},
      resource_manager{resource} {
    static const FunctionInfo functions[] = {
        {0, C<&IAppletResource::GetSharedMemoryHandle>, "GetSharedMemoryHandle"},
    };
    RegisterHandlers(functions);
}

IAppletResource::~IAppletResource() {
    resource_manager->FreeAppletResourceId(aruid);
}

Result IAppletResource::GetSharedMemoryHandle(
    OutCopyHandle<Kernel::KSharedMemory> out_shared_memory_handle) {
    const auto result = resource_manager->GetSharedMemoryHandle(out_shared_memory_handle, aruid);

    LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, result=0x{:X}", aruid, result.raw);
    R_RETURN(result);
}

} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include <memory>

#include "common/common_types.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"

namespace Core {
class System;
}

namespace Kernel {
class KSharedMemory;
}

namespace Service::HID {
class ResourceManager;

class IAppletResource final : public ServiceFramework<IAppletResource> {
public:
    explicit IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource,
                             u64 applet_resource_user_id);
    ~IAppletResource() override;

private:
    Result GetSharedMemoryHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory_handle);

    u64 aruid{};
    std::shared_ptr<ResourceManager> resource_manager;
};

} // namespace Service::HID


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

#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/hid/hid_debug_server.h"
#include "core/hle/service/hid/hid_server.h"
#include "core/hle/service/hid/hid_system_server.h"
#include "core/hle/service/hid/hidbus.h"
#include "core/hle/service/hid/irs.h"
#include "core/hle/service/hid/xcd.h"
#include "core/hle/service/server_manager.h"
#include "hid_core/resource_manager.h"
#include "hid_core/resources/hid_firmware_settings.h"

namespace Service::HID {

void LoopProcess(Core::System& system) {
    auto server_manager = std::make_unique<ServerManager>(system);
    std::shared_ptr<HidFirmwareSettings> firmware_settings =
        std::make_shared<HidFirmwareSettings>(system);
    std::shared_ptr<ResourceManager> resource_manager =
        std::make_shared<ResourceManager>(system, firmware_settings);

    // TODO: Remove this hack when am is emulated properly.
    resource_manager->Initialize();
    resource_manager->RegisterAppletResourceUserId(system.ApplicationProcess()->GetProcessId(),
                                                   true);
    resource_manager->SetAruidValidForVibration(system.ApplicationProcess()->GetProcessId(), true);

    server_manager->RegisterNamedService(
        "hid", std::make_shared<IHidServer>(system, resource_manager, firmware_settings));
    server_manager->RegisterNamedService(
        "hid:dbg", std::make_shared<IHidDebugServer>(system, resource_manager, firmware_settings));
    server_manager->RegisterNamedService(
        "hid:sys", std::make_shared<IHidSystemServer>(system, resource_manager, firmware_settings));

    server_manager->RegisterNamedService("hidbus", std::make_shared<Hidbus>(system));

    server_manager->RegisterNamedService("irs", std::make_shared<IRS::IRS>(system));
    server_manager->RegisterNamedService("irs:sys", std::make_shared<IRS::IRS_SYS>(system));

    server_manager->RegisterNamedService("xcd:sys", std::make_shared<XCD_SYS>(system));

    system.RunServer(std::move(server_manager));
}

} // namespace Service::HID


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

#pragma once

namespace Core {
class System;
}

namespace Service::HID {

void LoopProcess(Core::System& system);

} // namespace Service::HID


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

#include <algorithm>

#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/hid/hid_debug_server.h"
#include "core/hle/service/ipc_helpers.h"
#include "hid_core/hid_types.h"
#include "hid_core/resource_manager.h"
#include "hid_core/resources/hid_firmware_settings.h"

#include "hid_core/resources/touch_screen/gesture.h"
#include "hid_core/resources/touch_screen/touch_screen.h"

namespace Service::HID {

IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource,
                                 std::shared_ptr<HidFirmwareSettings> settings)
    : ServiceFramework{system_, "hid:dbg"}, resource_manager{resource}, firmware_settings{
                                                                            settings} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {0, nullptr, "DeactivateDebugPad"},
        {1, nullptr, "SetDebugPadAutoPilotState"},
        {2, nullptr, "UnsetDebugPadAutoPilotState"},
        {10, C<&IHidDebugServer::DeactivateTouchScreen>, "DeactivateTouchScreen"},
        {11, C<&IHidDebugServer::SetTouchScreenAutoPilotState>, "SetTouchScreenAutoPilotState"},
        {12, C<&IHidDebugServer::UnsetTouchScreenAutoPilotState>, "UnsetTouchScreenAutoPilotState"},
        {13, C<&IHidDebugServer::GetTouchScreenConfiguration>, "GetTouchScreenConfiguration"},
        {14, C<&IHidDebugServer::ProcessTouchScreenAutoTune>, "ProcessTouchScreenAutoTune"},
        {15, C<&IHidDebugServer::ForceStopTouchScreenManagement>, "ForceStopTouchScreenManagement"},
        {16, C<&IHidDebugServer::ForceRestartTouchScreenManagement>, "ForceRestartTouchScreenManagement"},
        {17, C<&IHidDebugServer::IsTouchScreenManaged>, "IsTouchScreenManaged"},
        {20, nullptr, "DeactivateMouse"},
        {21, nullptr, "SetMouseAutoPilotState"},
        {22, nullptr, "UnsetMouseAutoPilotState"},
        {25, nullptr, "SetDebugMouseAutoPilotState"},
        {26, nullptr, "UnsetDebugMouseAutoPilotState"},
        {30, nullptr, "DeactivateKeyboard"},
        {31, nullptr, "SetKeyboardAutoPilotState"},
        {32, nullptr, "UnsetKeyboardAutoPilotState"},
        {50, nullptr, "DeactivateXpad"},
        {51, nullptr, "SetXpadAutoPilotState"},
        {52, nullptr, "UnsetXpadAutoPilotState"},
        {53, nullptr, "DeactivateJoyXpad"},
        {60, nullptr, "ClearNpadSystemCommonPolicy"},
        {61, nullptr, "DeactivateNpad"},
        {62, nullptr, "ForceDisconnectNpad"},
        {91, C<&IHidDebugServer::DeactivateGesture>, "DeactivateGesture"},
        {110, nullptr, "DeactivateHomeButton"},
        {111, nullptr, "SetHomeButtonAutoPilotState"},
        {112, nullptr, "UnsetHomeButtonAutoPilotState"},
        {120, nullptr, "DeactivateSleepButton"},
        {121, nullptr, "SetSleepButtonAutoPilotState"},
        {122, nullptr, "UnsetSleepButtonAutoPilotState"},
        {123, nullptr, "DeactivateInputDetector"},
        {130, nullptr, "DeactivateCaptureButton"},
        {131, nullptr, "SetCaptureButtonAutoPilotState"},
        {132, nullptr, "UnsetCaptureButtonAutoPilotState"},
        {133, nullptr, "SetShiftAccelerometerCalibrationValue"},
        {134, nullptr, "GetShiftAccelerometerCalibrationValue"},
        {135, nullptr, "SetShiftGyroscopeCalibrationValue"},
        {136, nullptr, "GetShiftGyroscopeCalibrationValue"},
        {140, nullptr, "DeactivateConsoleSixAxisSensor"},
        {141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"},
        {142, nullptr, "DeactivateSevenSixAxisSensor"},
        {143, nullptr, "GetConsoleSixAxisSensorCountStates"},
        {144, nullptr, "GetAccelerometerFsr"},
        {145, nullptr, "SetAccelerometerFsr"},
        {146, nullptr, "GetAccelerometerOdr"},
        {147, nullptr, "SetAccelerometerOdr"},
        {148, nullptr, "GetGyroscopeFsr"},
        {149, nullptr, "SetGyroscopeFsr"},
        {150, nullptr, "GetGyroscopeOdr"},
        {151, nullptr, "SetGyroscopeOdr"},
        {152, nullptr, "GetWhoAmI"},
        {201, nullptr, "ActivateFirmwareUpdate"},
        {202, nullptr, "DeactivateFirmwareUpdate"},
        {203, nullptr, "StartFirmwareUpdate"},
        {204, nullptr, "GetFirmwareUpdateStage"},
        {205, nullptr, "GetFirmwareVersion"},
        {206, nullptr, "GetDestinationFirmwareVersion"},
        {207, nullptr, "DiscardFirmwareInfoCacheForRevert"},
        {208, nullptr, "StartFirmwareUpdateForRevert"},
        {209, nullptr, "GetAvailableFirmwareVersionForRevert"},
        {210, nullptr, "IsFirmwareUpdatingDevice"},
        {211, nullptr, "StartFirmwareUpdateIndividual"},
        {215, nullptr, "SetUsbFirmwareForceUpdateEnabled"},
        {216, nullptr, "SetAllKuinaDevicesToFirmwareUpdateMode"},
        {221, nullptr, "UpdateControllerColor"},
        {222, nullptr, "ConnectUsbPadsAsync"},
        {223, nullptr, "DisconnectUsbPadsAsync"},
        {224, nullptr, "UpdateDesignInfo"},
        {225, nullptr, "GetUniquePadDriverState"},
        {226, nullptr, "GetSixAxisSensorDriverStates"},
        {227, nullptr, "GetRxPacketHistory"},
        {228, nullptr, "AcquireOperationEventHandle"},
        {229, nullptr, "ReadSerialFlash"},
        {230, nullptr, "WriteSerialFlash"},
        {231, nullptr, "GetOperationResult"},
        {232, nullptr, "EnableShipmentMode"},
        {233, nullptr, "ClearPairingInfo"},
        {234, nullptr, "GetUniquePadDeviceTypeSetInternal"},
        {235, nullptr, "EnableAnalogStickPower"},
        {236, nullptr, "RequestKuinaUartClockCal"},
        {237, nullptr, "GetKuinaUartClockCal"},
        {238, nullptr, "SetKuinaUartClockTrim"},
        {239, nullptr, "KuinaLoopbackTest"},
        {240, nullptr, "RequestBatteryVoltage"},
        {241, nullptr, "GetBatteryVoltage"},
        {242, nullptr, "GetUniquePadPowerInfo"},
        {243, nullptr, "RebootUniquePad"},
        {244, nullptr, "RequestKuinaFirmwareVersion"},
        {245, nullptr, "GetKuinaFirmwareVersion"},
        {246, nullptr, "GetVidPid"},
        {247, nullptr, "GetAnalogStickCalibrationValue"},
        {248, nullptr, "GetUniquePadIdsFull"},
        {249, nullptr, "ConnectUniquePad"},
        {250, nullptr, "IsVirtual"},
        {251, nullptr, "GetAnalogStickModuleParam"},
        {301, nullptr, "GetAbstractedPadHandles"},
        {302, nullptr, "GetAbstractedPadState"},
        {303, nullptr, "GetAbstractedPadsState"},
        {321, nullptr, "SetAutoPilotVirtualPadState"},
        {322, nullptr, "UnsetAutoPilotVirtualPadState"},
        {323, nullptr, "UnsetAllAutoPilotVirtualPadState"},
        {324, nullptr, "AttachHdlsWorkBuffer"},
        {325, nullptr, "ReleaseHdlsWorkBuffer"},
        {326, nullptr, "DumpHdlsNpadAssignmentState"},
        {327, nullptr, "DumpHdlsStates"},
        {328, nullptr, "ApplyHdlsNpadAssignmentState"},
        {329, nullptr, "ApplyHdlsStateList"},
        {330, nullptr, "AttachHdlsVirtualDevice"},
        {331, nullptr, "DetachHdlsVirtualDevice"},
        {332, nullptr, "SetHdlsState"},
        {350, nullptr, "AddRegisteredDevice"},
        {400, nullptr, "DisableExternalMcuOnNxDevice"},
        {401, nullptr, "DisableRailDeviceFiltering"},
        {402, nullptr, "EnableWiredPairing"},
        {403, nullptr, "EnableShipmentModeAutoClear"},
        {404, nullptr, "SetRailEnabled"},
        {500, nullptr, "SetFactoryInt"},
        {501, nullptr, "IsFactoryBootEnabled"},
        {550, nullptr, "SetAnalogStickModelDataTemporarily"},
        {551, nullptr, "GetAnalogStickModelData"},
        {552, nullptr, "ResetAnalogStickModelData"},
        {600, nullptr, "ConvertPadState"},
        {650, nullptr, "AddButtonPlayData"},
        {651, nullptr, "StartButtonPlayData"},
        {652, nullptr, "StopButtonPlayData"},
        {2000, nullptr, "DeactivateDigitizer"},
        {2001, nullptr, "SetDigitizerAutoPilotState"},
        {2002, nullptr, "UnsetDigitizerAutoPilotState"},
        {2002, nullptr, "ReloadFirmwareDebugSettings"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

IHidDebugServer::~IHidDebugServer() = default;

Result IHidDebugServer::DeactivateTouchScreen() {
    LOG_INFO(Service_HID, "called");

    if (!firmware_settings->IsDeviceManaged()) {
        R_RETURN(GetResourceManager()->GetTouchScreen()->Deactivate());
    }

    R_SUCCEED();
}

Result IHidDebugServer::SetTouchScreenAutoPilotState(
    InArray<TouchState, BufferAttr_HipcMapAlias> auto_pilot_buffer) {
    AutoPilotState auto_pilot{};

    auto_pilot.count =
        static_cast<u64>(std::min(auto_pilot_buffer.size(), auto_pilot.state.size()));
    memcpy(auto_pilot.state.data(), auto_pilot_buffer.data(),
           auto_pilot.count * sizeof(TouchState));

    LOG_INFO(Service_HID, "called, auto_pilot_count={}", auto_pilot.count);

    R_RETURN(GetResourceManager()->GetTouchScreen()->SetTouchScreenAutoPilotState(auto_pilot));
}

Result IHidDebugServer::UnsetTouchScreenAutoPilotState() {
    LOG_INFO(Service_HID, "called");
    R_RETURN(GetResourceManager()->GetTouchScreen()->UnsetTouchScreenAutoPilotState());
}

Result IHidDebugServer::GetTouchScreenConfiguration(
    Out<Core::HID::TouchScreenConfigurationForNx> out_touchscreen_config,
    ClientAppletResourceUserId aruid) {
    LOG_INFO(Service_HID, "called, applet_resource_user_id={}", aruid.pid);

    R_TRY(GetResourceManager()->GetTouchScreen()->GetTouchScreenConfiguration(
        *out_touchscreen_config, aruid.pid));

    if (out_touchscreen_config->mode != Core::HID::TouchScreenModeForNx::Heat2 &&
        out_touchscreen_config->mode != Core::HID::TouchScreenModeForNx::Finger) {
        out_touchscreen_config->mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
    }

    R_SUCCEED();
}

Result IHidDebugServer::ProcessTouchScreenAutoTune() {
    LOG_INFO(Service_HID, "called");
    R_RETURN(GetResourceManager()->GetTouchScreen()->ProcessTouchScreenAutoTune());
}

Result IHidDebugServer::ForceStopTouchScreenManagement() {
    LOG_INFO(Service_HID, "called");

    if (!firmware_settings->IsDeviceManaged()) {
        R_SUCCEED();
    }

    auto touch_screen = GetResourceManager()->GetTouchScreen();
    auto gesture = GetResourceManager()->GetGesture();

    if (firmware_settings->IsTouchI2cManaged()) {
        bool is_touch_active{};
        bool is_gesture_active{};
        R_TRY(touch_screen->IsActive(is_touch_active));
        R_TRY(gesture->IsActive(is_gesture_active));

        if (is_touch_active) {
            R_TRY(touch_screen->Deactivate());
        }
        if (is_gesture_active) {
            R_TRY(gesture->Deactivate());
        }
    }

    R_SUCCEED();
}

Result IHidDebugServer::ForceRestartTouchScreenManagement(u32 basic_gesture_id,
                                                          ClientAppletResourceUserId aruid) {
    LOG_INFO(Service_HID, "called, basic_gesture_id={}, applet_resource_user_id={}",
             basic_gesture_id, aruid.pid);

    auto touch_screen = GetResourceManager()->GetTouchScreen();
    auto gesture = GetResourceManager()->GetGesture();

    if (firmware_settings->IsDeviceManaged() && firmware_settings->IsTouchI2cManaged()) {
        R_TRY(gesture->Activate());
        R_TRY(gesture->Activate(aruid.pid, basic_gesture_id));
        R_TRY(touch_screen->Activate());
        R_TRY(touch_screen->Activate(aruid.pid));
    }

    R_SUCCEED();
}

Result IHidDebugServer::IsTouchScreenManaged(Out<bool> out_is_managed) {
    LOG_INFO(Service_HID, "called");

    bool is_touch_active{};
    bool is_gesture_active{};
    R_TRY(GetResourceManager()->GetTouchScreen()->IsActive(is_touch_active));
    R_TRY(GetResourceManager()->GetGesture()->IsActive(is_gesture_active));

    *out_is_managed = is_touch_active || is_gesture_active;
    R_SUCCEED();
}

Result IHidDebugServer::DeactivateGesture() {
    LOG_INFO(Service_HID, "called");

    if (!firmware_settings->IsDeviceManaged()) {
        R_RETURN(GetResourceManager()->GetGesture()->Deactivate());
    }

    R_SUCCEED();
}

std::shared_ptr<ResourceManager> IHidDebugServer::GetResourceManager() {
    resource_manager->Initialize();
    return resource_manager;
}

} // namespace Service::HID


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

#pragma once

#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
#include "hid_core/resources/touch_screen/touch_types.h"

namespace Core {
class System;
}

namespace Service::HID {
class ResourceManager;
class HidFirmwareSettings;

class IHidDebugServer final : public ServiceFramework<IHidDebugServer> {
public:
    explicit IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource,
                             std::shared_ptr<HidFirmwareSettings> settings);
    ~IHidDebugServer() override;

private:
    Result DeactivateTouchScreen();
    Result SetTouchScreenAutoPilotState(
        InArray<TouchState, BufferAttr_HipcMapAlias> auto_pilot_buffer);
    Result UnsetTouchScreenAutoPilotState();
    Result GetTouchScreenConfiguration(
        Out<Core::HID::TouchScreenConfigurationForNx> out_touchscreen_config,
        ClientAppletResourceUserId aruid);
    Result ProcessTouchScreenAutoTune();
    Result ForceStopTouchScreenManagement();
    Result ForceRestartTouchScreenManagement(u32 basic_gesture_id,
                                             ClientAppletResourceUserId aruid);
    Result IsTouchScreenManaged(Out<bool> out_is_managed);
    Result DeactivateGesture();

    std::shared_ptr<ResourceManager> GetResourceManager();

    std::shared_ptr<ResourceManager> resource_manager;
    std::shared_ptr<HidFirmwareSettings> firmware_settings;
};

} // namespace Service::HID