diff --git a/LICENSES/BSD-2-Clause.txt b/LICENSES/BSD-2-Clause.txt index f1c72cce8..e9281a4ce 100755 --- a/LICENSES/BSD-2-Clause.txt +++ b/LICENSES/BSD-2-Clause.txt @@ -1,4 +1,4 @@ -Copyright (c) +Copyright (c) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/LICENSES/BSD-3-Clause.txt b/LICENSES/BSD-3-Clause.txt index b8e266097..8c691e1dd 100755 --- a/LICENSES/BSD-3-Clause.txt +++ b/LICENSES/BSD-3-Clause.txt @@ -1,4 +1,4 @@ -Copyright (c) . +Copyright (c) . Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/LICENSES/MPL-2.0.txt b/LICENSES/MPL-2.0.txt index 14e2f777f..a612ad981 100755 --- a/LICENSES/MPL-2.0.txt +++ b/LICENSES/MPL-2.0.txt @@ -35,7 +35,7 @@ Mozilla Public License Version 2.0 means any form of the work other than Source Code Form. 1.7. "Larger Work" - means a work that combines Covered Software with other material, in + means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 1.8. "License" diff --git a/README.md b/README.md index 708861435..b1a773ccf 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 4052. +This is the source code for early-access 4053. ## Legal Notice diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index cd7d10a7e..fbde3abca 100755 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -178,6 +178,9 @@ if (NOT TARGET stb::headers) add_library(stb::headers ALIAS stb) endif() +add_library(tz tz/tz/tz.cpp) +target_include_directories(tz PUBLIC ./tz) + add_library(bc_decoder bc_decoder/bc_decoder.cpp) target_include_directories(bc_decoder PUBLIC ./bc_decoder) diff --git a/externals/ffmpeg/CMakeLists.txt b/externals/ffmpeg/CMakeLists.txt index 46da3b7ff..d05cc7ab8 100755 --- a/externals/ffmpeg/CMakeLists.txt +++ b/externals/ffmpeg/CMakeLists.txt @@ -138,7 +138,7 @@ if (NOT WIN32 AND NOT ANDROID) --cross-prefix=${TOOLCHAIN}/bin/aarch64-linux-android- --sysroot=${SYSROOT} --target-os=android - --extra-ldflags="--ld-path=${TOOLCHAIN}/bin/ld.lld" + --extra-ldflags="--ld-path=${TOOLCHAIN}/bin/ld.lld" --extra-ldflags="-nostdlib" ) endif() diff --git a/externals/tz/tz/tz.cpp b/externals/tz/tz/tz.cpp new file mode 100755 index 000000000..0c8b68217 --- /dev/null +++ b/externals/tz/tz/tz.cpp @@ -0,0 +1,1636 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-FileCopyrightText: 1996 Arthur David Olson +// SPDX-License-Identifier: BSD-2-Clause + +#include +#include +#include + +#include "tz.h" + +namespace Tz { + +namespace { +#define EINVAL 22 + +static Rule gmtmem{}; +static Rule* const gmtptr = &gmtmem; + +struct TzifHeader { + std::array tzh_magic; // "TZif" + std::array tzh_version; + std::array tzh_reserved; + std::array tzh_ttisutcnt; + std::array tzh_ttisstdcnt; + std::array tzh_leapcnt; + std::array tzh_timecnt; + std::array tzh_typecnt; + std::array tzh_charcnt; +}; +static_assert(sizeof(TzifHeader) == 0x2C, "TzifHeader has the wrong size!"); + +struct local_storage { + // Binary layout: + // char buf[2 * sizeof(TzifHeader) + 2 * sizeof(Rule) + 4 * TZ_MAX_TIMES]; + std::span binary; + Rule state; +}; +static local_storage tzloadbody_local_storage; + +enum rtype : s32 { + JULIAN_DAY = 0, + DAY_OF_YEAR = 1, + MONTH_NTH_DAY_OF_WEEK = 2, +}; + +struct tzrule { + rtype r_type; + int r_day; + int r_week; + int r_mon; + s64 r_time; +}; +static_assert(sizeof(tzrule) == 0x18, "tzrule has the wrong size!"); + +constexpr static char UNSPEC[] = "-00"; +constexpr static char TZDEFRULESTRING[] = ",M3.2.0,M11.1.0"; + +enum { + SECSPERMIN = 60, + MINSPERHOUR = 60, + SECSPERHOUR = SECSPERMIN * MINSPERHOUR, + HOURSPERDAY = 24, + DAYSPERWEEK = 7, + DAYSPERNYEAR = 365, + DAYSPERLYEAR = DAYSPERNYEAR + 1, + MONSPERYEAR = 12, + YEARSPERREPEAT = 400 /* years before a Gregorian repeat */ +}; + +#define SECSPERDAY ((s64)SECSPERHOUR * HOURSPERDAY) + +#define DAYSPERREPEAT ((s64)400 * 365 + 100 - 4 + 1) +#define SECSPERREPEAT ((int_fast64_t)DAYSPERREPEAT * SECSPERDAY) +#define AVGSECSPERYEAR (SECSPERREPEAT / YEARSPERREPEAT) + +enum { + TM_SUNDAY, + TM_MONDAY, + TM_TUESDAY, + TM_WEDNESDAY, + TM_THURSDAY, + TM_FRIDAY, + TM_SATURDAY, +}; + +enum { + TM_JANUARY, + TM_FEBRUARY, + TM_MARCH, + TM_APRIL, + TM_MAY, + TM_JUNE, + TM_JULY, + TM_AUGUST, + TM_SEPTEMBER, + TM_OCTOBER, + TM_NOVEMBER, + TM_DECEMBER, +}; + +constexpr s32 TM_YEAR_BASE = 1900; +constexpr s32 TM_WDAY_BASE = TM_MONDAY; +constexpr s32 EPOCH_YEAR = 1970; +constexpr s32 EPOCH_WDAY = TM_THURSDAY; + +#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) + +static constexpr std::array, 2> mon_lengths = { { + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, +} }; + +static constexpr std::array year_lengths = { + DAYSPERNYEAR, + DAYSPERLYEAR, +}; + +constexpr static time_t leaps_thru_end_of_nonneg(time_t y) { + return y / 4 - y / 100 + y / 400; +} + +constexpr static time_t leaps_thru_end_of(time_t y) { + return (y < 0 ? -1 - leaps_thru_end_of_nonneg(-1 - y) : leaps_thru_end_of_nonneg(y)); +} + +#define TWOS_COMPLEMENT(t) ((t) ~(t)0 < 0) + +s32 detzcode(const char* const codep) { + s32 result; + int i; + s32 one = 1; + s32 halfmaxval = one << (32 - 2); + s32 maxval = halfmaxval - 1 + halfmaxval; + s32 minval = -1 - maxval; + + result = codep[0] & 0x7f; + for (i = 1; i < 4; ++i) { + result = (result << 8) | (codep[i] & 0xff); + } + + if (codep[0] & 0x80) { + /* Do two's-complement negation even on non-two's-complement machines. + If the result would be minval - 1, return minval. */ + result -= !TWOS_COMPLEMENT(s32) && result != 0; + result += minval; + } + return result; +} + +int_fast64_t detzcode64(const char* const codep) { + int_fast64_t result; + int i; + int_fast64_t one = 1; + int_fast64_t halfmaxval = one << (64 - 2); + int_fast64_t maxval = halfmaxval - 1 + halfmaxval; + int_fast64_t minval = -static_cast(TWOS_COMPLEMENT(int_fast64_t)) - maxval; + + result = codep[0] & 0x7f; + for (i = 1; i < 8; ++i) { + result = (result << 8) | (codep[i] & 0xff); + } + + if (codep[0] & 0x80) { + /* Do two's-complement negation even on non-two's-complement machines. + If the result would be minval - 1, return minval. */ + result -= !TWOS_COMPLEMENT(int_fast64_t) && result != 0; + result += minval; + } + return result; +} + +/* Initialize *S to a value based on UTOFF, ISDST, and DESIGIDX. */ +constexpr void init_ttinfo(ttinfo* s, s64 utoff, bool isdst, int desigidx) { + s->tt_utoff = static_cast(utoff); + s->tt_isdst = isdst; + s->tt_desigidx = desigidx; + s->tt_ttisstd = false; + s->tt_ttisut = false; +} + +/* Return true if SP's time type I does not specify local time. */ +bool ttunspecified(struct Rule const* sp, int i) { + char const* abbr = &sp->chars[sp->ttis[i].tt_desigidx]; + /* memcmp is likely faster than strcmp, and is safe due to CHARS_EXTRA. */ + return memcmp(abbr, UNSPEC, sizeof(UNSPEC)) == 0; +} + +bool typesequiv(const Rule* sp, int a, int b) { + bool result; + + if (sp == nullptr || a < 0 || a >= sp->typecnt || b < 0 || b >= sp->typecnt) { + result = false; + } + else { + /* Compare the relevant members of *AP and *BP. + Ignore tt_ttisstd and tt_ttisut, as they are + irrelevant now and counting them could cause + sp->goahead to mistakenly remain false. */ + const ttinfo* ap = &sp->ttis[a]; + const ttinfo* bp = &sp->ttis[b]; + result = (ap->tt_utoff == bp->tt_utoff && ap->tt_isdst == bp->tt_isdst && + (strcmp(&sp->chars[ap->tt_desigidx], &sp->chars[bp->tt_desigidx]) == 0)); + } + return result; +} + +constexpr const char* getqzname(const char* strp, const int delim) { + int c; + + while ((c = *strp) != '\0' && c != delim) { + ++strp; + } + return strp; +} + +/* Is C an ASCII digit? */ +constexpr bool is_digit(char c) { + return '0' <= c && c <= '9'; +} + +/* +** Given a pointer into a timezone string, scan until a character that is not +** a valid character in a time zone abbreviation is found. +** Return a pointer to that character. +*/ + +constexpr const char* getzname(const char* strp) { + char c; + + while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' && c != '+') { + ++strp; + } + return strp; +} + +static const char* getnum(const char* strp, int* const nump, const int min, const int max) { + char c; + int num; + + if (strp == nullptr || !is_digit(c = *strp)) { + return nullptr; + } + num = 0; + do { + num = num * 10 + (c - '0'); + if (num > max) { + return nullptr; /* illegal value */ + } + c = *++strp; + } while (is_digit(c)); + if (num < min) { + return nullptr; /* illegal value */ + } + *nump = num; + return strp; +} + +/* +** Given a pointer into a timezone string, extract a number of seconds, +** in hh[:mm[:ss]] form, from the string. +** If any error occurs, return NULL. +** Otherwise, return a pointer to the first character not part of the number +** of seconds. +*/ + +const char* getsecs(const char* strp, s64* const secsp) { + int num; + s64 secsperhour = SECSPERHOUR; + + /* + ** 'HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like + ** "M10.4.6/26", which does not conform to Posix, + ** but which specifies the equivalent of + ** "02:00 on the first Sunday on or after 23 Oct". + */ + strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1); + if (strp == nullptr) { + return nullptr; + } + *secsp = num * secsperhour; + if (*strp == ':') { + ++strp; + strp = getnum(strp, &num, 0, MINSPERHOUR - 1); + if (strp == nullptr) { + return nullptr; + } + *secsp += num * SECSPERMIN; + if (*strp == ':') { + ++strp; + /* 'SECSPERMIN' allows for leap seconds. */ + strp = getnum(strp, &num, 0, SECSPERMIN); + if (strp == nullptr) { + return nullptr; + } + *secsp += num; + } + } + return strp; +} + +/* +** Given a pointer into a timezone string, extract an offset, in +** [+-]hh[:mm[:ss]] form, from the string. +** If any error occurs, return NULL. +** Otherwise, return a pointer to the first character not part of the time. +*/ + +const char* getoffset(const char* strp, s64* const offsetp) { + bool neg = false; + + if (*strp == '-') { + neg = true; + ++strp; + } + else if (*strp == '+') { + ++strp; + } + strp = getsecs(strp, offsetp); + if (strp == nullptr) { + return nullptr; /* illegal time */ + } + if (neg) { + *offsetp = -*offsetp; + } + return strp; +} + +constexpr const char* getrule(const char* strp, tzrule* const rulep) { + if (*strp == 'J') { + /* + ** Julian day. + */ + rulep->r_type = JULIAN_DAY; + ++strp; + strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR); + } + else if (*strp == 'M') { + /* + ** Month, week, day. + */ + rulep->r_type = MONTH_NTH_DAY_OF_WEEK; + ++strp; + strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR); + if (strp == nullptr) { + return nullptr; + } + if (*strp++ != '.') { + return nullptr; + } + strp = getnum(strp, &rulep->r_week, 1, 5); + if (strp == nullptr) { + return nullptr; + } + if (*strp++ != '.') { + return nullptr; + } + strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1); + } + else if (is_digit(*strp)) { + /* + ** Day of year. + */ + rulep->r_type = DAY_OF_YEAR; + strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1); + } + else { + return nullptr; + } /* invalid format */ + if (strp == nullptr) { + return nullptr; + } + if (*strp == '/') { + /* + ** Time specified. + */ + ++strp; + strp = getoffset(strp, &rulep->r_time); + } + else { + rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */ + } + return strp; +} + +constexpr bool increment_overflow(int* ip, int j) { + int const i = *ip; + + /* + ** If i >= 0 there can only be overflow if i + j > INT_MAX + ** or if j > INT_MAX - i; given i >= 0, INT_MAX - i cannot overflow. + ** If i < 0 there can only be overflow if i + j < INT_MIN + ** or if j < INT_MIN - i; given i < 0, INT_MIN - i cannot overflow. + */ + if ((i >= 0) ? (j > INT_MAX - i) : (j < INT_MIN - i)) { + return true; + } + *ip += j; + return false; +} + +constexpr bool increment_overflow32(s64* const lp, int const m) { + s64 const l = *lp; + + if ((l >= 0) ? (m > INT_FAST32_MAX - l) : (m < INT_FAST32_MIN - l)) + return true; + *lp += m; + return false; +} + +constexpr bool increment_overflow_time(time_t* tp, s64 j) { + /* + ** This is like + ** 'if (! (TIME_T_MIN <= *tp + j && *tp + j <= TIME_T_MAX)) ...', + ** except that it does the right thing even if *tp + j would overflow. + */ + if (!(j < 0 ? (std::is_signed_v ? TIME_T_MIN - j <= *tp : -1 - j < *tp) + : *tp <= TIME_T_MAX - j)) { + return true; + } + *tp += j; + return false; +} + +CalendarTimeInternal* timesub(const time_t* timep, s64 offset, const Rule* sp, + CalendarTimeInternal* tmp) { + time_t tdays; + const int* ip; + s64 idays, rem, dayoff, dayrem; + time_t y; + + /* Calculate the year, avoiding integer overflow even if + time_t is unsigned. */ + tdays = *timep / SECSPERDAY; + rem = *timep % SECSPERDAY; + rem += offset % SECSPERDAY + 3 * SECSPERDAY; + dayoff = offset / SECSPERDAY + rem / SECSPERDAY - 3; + rem %= SECSPERDAY; + /* y = (EPOCH_YEAR + + floor((tdays + dayoff) / DAYSPERREPEAT) * YEARSPERREPEAT), + sans overflow. But calculate against 1570 (EPOCH_YEAR - + YEARSPERREPEAT) instead of against 1970 so that things work + for localtime values before 1970 when time_t is unsigned. */ + dayrem = tdays % DAYSPERREPEAT; + dayrem += dayoff % DAYSPERREPEAT; + y = (EPOCH_YEAR - YEARSPERREPEAT + + ((1ull + dayoff / DAYSPERREPEAT + dayrem / DAYSPERREPEAT - ((dayrem % DAYSPERREPEAT) < 0) + + tdays / DAYSPERREPEAT) * + YEARSPERREPEAT)); + /* idays = (tdays + dayoff) mod DAYSPERREPEAT, sans overflow. */ + idays = tdays % DAYSPERREPEAT; + idays += dayoff % DAYSPERREPEAT + 2 * DAYSPERREPEAT; + idays %= DAYSPERREPEAT; + /* Increase Y and decrease IDAYS until IDAYS is in range for Y. */ + while (year_lengths[isleap(y)] <= idays) { + s64 tdelta = idays / DAYSPERLYEAR; + s64 ydelta = tdelta + !tdelta; + time_t newy = y + ydelta; + int leapdays; + leapdays = static_cast(leaps_thru_end_of(newy - 1) - leaps_thru_end_of(y - 1)); + idays -= ydelta * DAYSPERNYEAR; + idays -= leapdays; + y = newy; + } + + if constexpr (!std::is_signed_v && y < TM_YEAR_BASE) { + int signed_y = static_cast(y); + tmp->tm_year = signed_y - TM_YEAR_BASE; + } + else if ((!std::is_signed_v || std::numeric_limits::min() + TM_YEAR_BASE <= y) && + y - TM_YEAR_BASE <= std::numeric_limits::max()) { + tmp->tm_year = static_cast(y - TM_YEAR_BASE); + } + else { + // errno = EOVERFLOW; + return nullptr; + } + + tmp->tm_yday = static_cast(idays); + /* + ** The "extra" mods below avoid overflow problems. + */ + tmp->tm_wday = static_cast( + TM_WDAY_BASE + ((tmp->tm_year % DAYSPERWEEK) * (DAYSPERNYEAR % DAYSPERWEEK)) + + leaps_thru_end_of(y - 1) - leaps_thru_end_of(TM_YEAR_BASE - 1) + idays); + tmp->tm_wday %= DAYSPERWEEK; + if (tmp->tm_wday < 0) { + tmp->tm_wday += DAYSPERWEEK; + } + tmp->tm_hour = static_cast(rem / SECSPERHOUR); + rem %= SECSPERHOUR; + tmp->tm_min = static_cast(rem / SECSPERMIN); + tmp->tm_sec = static_cast(rem % SECSPERMIN); + + ip = mon_lengths[isleap(y)].data(); + for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon)) { + idays -= ip[tmp->tm_mon]; + } + tmp->tm_mday = static_cast(idays + 1); + tmp->tm_isdst = 0; + return tmp; +} + +CalendarTimeInternal* gmtsub([[maybe_unused]] Rule const* sp, time_t const* timep, + s64 offset, CalendarTimeInternal* tmp) { + CalendarTimeInternal* result; + + result = timesub(timep, offset, gmtptr, tmp); + return result; +} + +CalendarTimeInternal* localsub(Rule const* sp, time_t const* timep, s64 setname, + CalendarTimeInternal* const tmp) { + const ttinfo* ttisp; + int i; + CalendarTimeInternal* result; + const time_t t = *timep; + + if (sp == nullptr) { + /* Don't bother to set tzname etc.; tzset has already done it. */ + return gmtsub(gmtptr, timep, 0, tmp); + } + if ((sp->goback && t < sp->ats[0]) || (sp->goahead && t > sp->ats[sp->timecnt - 1])) { + time_t newt; + time_t seconds; + time_t years; + + if (t < sp->ats[0]) { + seconds = sp->ats[0] - t; + } + else { + seconds = t - sp->ats[sp->timecnt - 1]; + } + --seconds; + + /* Beware integer overflow, as SECONDS might + be close to the maximum time_t. */ + years = seconds / SECSPERREPEAT * YEARSPERREPEAT; + seconds = years * AVGSECSPERYEAR; + years += YEARSPERREPEAT; + if (t < sp->ats[0]) { + newt = t + seconds + SECSPERREPEAT; + } + else { + newt = t - seconds - SECSPERREPEAT; + } + + if (newt < sp->ats[0] || newt > sp->ats[sp->timecnt - 1]) { + return nullptr; /* "cannot happen" */ + } + result = localsub(sp, &newt, setname, tmp); + if (result) { + int_fast64_t newy; + + newy = result->tm_year; + if (t < sp->ats[0]) { + newy -= years; + } + else { + newy += years; + } + if (!(std::numeric_limits::min() <= newy && + newy <= std::numeric_limits::max())) { + return nullptr; + } + result->tm_year = static_cast(newy); + } + return result; + } + if (sp->timecnt == 0 || t < sp->ats[0]) { + i = sp->defaulttype; + } + else { + int lo = 1; + int hi = sp->timecnt; + + while (lo < hi) { + int mid = (lo + hi) >> 1; + + if (t < sp->ats[mid]) + hi = mid; + else + lo = mid + 1; + } + i = sp->types[lo - 1]; + } + ttisp = &sp->ttis[i]; + /* + ** To get (wrong) behavior that's compatible with System V Release 2.0 + ** you'd replace the statement below with + ** t += ttisp->tt_utoff; + ** timesub(&t, 0L, sp, tmp); + */ + result = timesub(&t, ttisp->tt_utoff, sp, tmp); + if (result) { + result->tm_isdst = ttisp->tt_isdst; + + if (ttisp->tt_desigidx > static_cast(sp->chars.size() - CHARS_EXTRA)) { + return nullptr; + } + + auto num_chars_to_copy{ + std::min(sp->chars.size() - ttisp->tt_desigidx, result->tm_zone.size()) - 1 }; + std::strncpy(result->tm_zone.data(), &sp->chars[ttisp->tt_desigidx], num_chars_to_copy); + result->tm_zone[num_chars_to_copy] = '\0'; + + auto original_size{ std::strlen(&sp->chars[ttisp->tt_desigidx]) }; + if (original_size > num_chars_to_copy) { + return nullptr; + } + + result->tm_utoff = ttisp->tt_utoff; + result->time_index = i; + } + return result; +} + +/* +** Given a year, a rule, and the offset from UT at the time that rule takes +** effect, calculate the year-relative time that rule takes effect. +*/ + +constexpr s64 transtime(const int year, const tzrule* const rulep, + const s64 offset) { + bool leapyear; + s64 value; + int i; + int d, m1, yy0, yy1, yy2, dow; + + leapyear = isleap(year); + switch (rulep->r_type) { + case JULIAN_DAY: + /* + ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap + ** years. + ** In non-leap years, or if the day number is 59 or less, just + ** add SECSPERDAY times the day number-1 to the time of + ** January 1, midnight, to get the day. + */ + value = (rulep->r_day - 1) * SECSPERDAY; + if (leapyear && rulep->r_day >= 60) { + value += SECSPERDAY; + } + break; + + case DAY_OF_YEAR: + /* + ** n - day of year. + ** Just add SECSPERDAY times the day number to the time of + ** January 1, midnight, to get the day. + */ + value = rulep->r_day * SECSPERDAY; + break; + + case MONTH_NTH_DAY_OF_WEEK: + /* + ** Mm.n.d - nth "dth day" of month m. + */ + + /* + ** Use Zeller's Congruence to get day-of-week of first day of + ** month. + */ + m1 = (rulep->r_mon + 9) % 12 + 1; + yy0 = (rulep->r_mon <= 2) ? (year - 1) : year; + yy1 = yy0 / 100; + yy2 = yy0 % 100; + dow = ((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7; + if (dow < 0) { + dow += DAYSPERWEEK; + } + + /* + ** "dow" is the day-of-week of the first day of the month. Get + ** the day-of-month (zero-origin) of the first "dow" day of the + ** month. + */ + d = rulep->r_day - dow; + if (d < 0) { + d += DAYSPERWEEK; + } + for (i = 1; i < rulep->r_week; ++i) { + if (d + DAYSPERWEEK >= mon_lengths[leapyear][rulep->r_mon - 1]) { + break; + } + d += DAYSPERWEEK; + } + + /* + ** "d" is the day-of-month (zero-origin) of the day we want. + */ + value = d * SECSPERDAY; + for (i = 0; i < rulep->r_mon - 1; ++i) { + value += mon_lengths[leapyear][i] * SECSPERDAY; + } + break; + + default: + //UNREACHABLE(); + break; + } + + /* + ** "value" is the year-relative time of 00:00:00 UT on the day in + ** question. To get the year-relative time of the specified local + ** time on that day, add the transition time and the current offset + ** from UT. + */ + return value + rulep->r_time + offset; +} + +bool tzparse(const char* name, Rule* sp) { + const char* stdname{}; + const char* dstname{}; + s64 stdoffset; + s64 dstoffset; + char* cp; + ptrdiff_t stdlen; + ptrdiff_t dstlen{}; + ptrdiff_t charcnt; + time_t atlo = TIME_T_MIN, leaplo = TIME_T_MIN; + + stdname = name; + if (*name == '<') { + name++; + stdname = name; + name = getqzname(name, '>'); + if (*name != '>') { + return false; + } + stdlen = name - stdname; + name++; + } + else { + name = getzname(name); + stdlen = name - stdname; + } + if (!(0 < stdlen && stdlen <= TZNAME_MAXIMUM)) { + return false; + } + name = getoffset(name, &stdoffset); + if (name == nullptr) { + return false; + } + charcnt = stdlen + 1; + if (charcnt > TZ_MAX_CHARS) { + return false; + } + if (*name != '\0') { + if (*name == '<') { + dstname = ++name; + name = getqzname(name, '>'); + if (*name != '>') + return false; + dstlen = name - dstname; + name++; + } + else { + dstname = name; + name = getzname(name); + dstlen = name - dstname; /* length of DST abbr. */ + } + if (!(0 < dstlen && dstlen <= TZNAME_MAXIMUM)) { + return false; + } + charcnt += dstlen + 1; + if (charcnt > TZ_MAX_CHARS) { + return false; + } + if (*name != '\0' && *name != ',' && *name != ';') { + name = getoffset(name, &dstoffset); + if (name == nullptr) { + return false; + } + } + else { + dstoffset = stdoffset - SECSPERHOUR; + } + if (*name == '\0') { + name = TZDEFRULESTRING; + } + if (*name == ',' || *name == ';') { + struct tzrule start; + struct tzrule end; + int year; + int timecnt; + time_t janfirst; + s64 janoffset = 0; + int yearbeg, yearlim; + + ++name; + if ((name = getrule(name, &start)) == nullptr) { + return false; + } + if (*name++ != ',') { + return false; + } + if ((name = getrule(name, &end)) == nullptr) { + return false; + } + if (*name != '\0') { + return false; + } + sp->typecnt = 2; /* standard time and DST */ + /* + ** Two transitions per year, from EPOCH_YEAR forward. + */ + init_ttinfo(&sp->ttis[0], -stdoffset, false, 0); + init_ttinfo(&sp->ttis[1], -dstoffset, true, static_cast(stdlen + 1)); + sp->defaulttype = 0; + timecnt = 0; + janfirst = 0; + yearbeg = EPOCH_YEAR; + + do { + s64 yearsecs = year_lengths[isleap(yearbeg - 1)] * SECSPERDAY; + yearbeg--; + if (increment_overflow_time(&janfirst, -yearsecs)) { + janoffset = -yearsecs; + break; + } + } while (atlo < janfirst && EPOCH_YEAR - YEARSPERREPEAT / 2 < yearbeg); + + while (true) { + s64 yearsecs = year_lengths[isleap(yearbeg)] * SECSPERDAY; + int yearbeg1 = yearbeg; + time_t janfirst1 = janfirst; + if (increment_overflow_time(&janfirst1, yearsecs) || + increment_overflow(&yearbeg1, 1) || atlo <= janfirst1) { + break; + } + yearbeg = yearbeg1; + janfirst = janfirst1; + } + + yearlim = yearbeg; + if (increment_overflow(&yearlim, YEARSPERREPEAT + 1)) { + yearlim = INT_MAX; + } + for (year = yearbeg; year < yearlim; year++) { + s64 starttime = transtime(year, &start, stdoffset), + endtime = transtime(year, &end, dstoffset); + s64 yearsecs = (year_lengths[isleap(year)] * SECSPERDAY); + bool reversed = endtime < starttime; + if (reversed) { + s64 swap = starttime; + starttime = endtime; + endtime = swap; + } + if (reversed || (starttime < endtime && endtime - starttime < yearsecs)) { + if (TZ_MAX_TIMES - 2 < timecnt) { + break; + } + sp->ats[timecnt] = janfirst; + if (!increment_overflow_time(reinterpret_cast(&sp->ats[timecnt]), janoffset + starttime) && + atlo <= sp->ats[timecnt]) { + sp->types[timecnt++] = !reversed; + } + sp->ats[timecnt] = janfirst; + if (!increment_overflow_time(reinterpret_cast(&sp->ats[timecnt]), janoffset + endtime) && + atlo <= sp->ats[timecnt]) { + sp->types[timecnt++] = reversed; + } + } + if (endtime < leaplo) { + yearlim = year; + if (increment_overflow(&yearlim, YEARSPERREPEAT + 1)) { + yearlim = INT_MAX; + } + } + if (increment_overflow_time(&janfirst, janoffset + yearsecs)) { + break; + } + janoffset = 0; + } + sp->timecnt = timecnt; + if (!timecnt) { + sp->ttis[0] = sp->ttis[1]; + sp->typecnt = 1; /* Perpetual DST. */ + } + else if (YEARSPERREPEAT < year - yearbeg) { + sp->goback = sp->goahead = true; + } + } + else { + s64 theirstdoffset; + s64 theirdstoffset; + s64 theiroffset; + bool isdst; + int i; + int j; + + if (*name != '\0') { + return false; + } + /* + ** Initial values of theirstdoffset and theirdstoffset. + */ + theirstdoffset = 0; + for (i = 0; i < sp->timecnt; ++i) { + j = sp->types[i]; + if (!sp->ttis[j].tt_isdst) { + theirstdoffset = -sp->ttis[j].tt_utoff; + break; + } + } + theirdstoffset = 0; + for (i = 0; i < sp->timecnt; ++i) { + j = sp->types[i]; + if (sp->ttis[j].tt_isdst) { + theirdstoffset = -sp->ttis[j].tt_utoff; + break; + } + } + /* + ** Initially we're assumed to be in standard time. + */ + isdst = false; + /* + ** Now juggle transition times and types + ** tracking offsets as you do. + */ + for (i = 0; i < sp->timecnt; ++i) { + j = sp->types[i]; + sp->types[i] = sp->ttis[j].tt_isdst; + if (sp->ttis[j].tt_ttisut) { + /* No adjustment to transition time */ + } + else { + /* + ** If daylight saving time is in + ** effect, and the transition time was + ** not specified as standard time, add + ** the daylight saving time offset to + ** the transition time; otherwise, add + ** the standard time offset to the + ** transition time. + */ + /* + ** Transitions from DST to DDST + ** will effectively disappear since + ** POSIX provides for only one DST + ** offset. + */ + if (isdst && !sp->ttis[j].tt_ttisstd) { + sp->ats[i] += dstoffset - theirdstoffset; + } + else { + sp->ats[i] += stdoffset - theirstdoffset; + } + } + theiroffset = -sp->ttis[j].tt_utoff; + if (sp->ttis[j].tt_isdst) { + theirdstoffset = theiroffset; + } + else { + theirstdoffset = theiroffset; + } + } + /* + ** Finally, fill in ttis. + */ + init_ttinfo(&sp->ttis[0], -stdoffset, false, 0); + init_ttinfo(&sp->ttis[1], -dstoffset, true, static_cast(stdlen + 1)); + sp->typecnt = 2; + sp->defaulttype = 0; + } + } + else { + dstlen = 0; + sp->typecnt = 1; /* only standard time */ + sp->timecnt = 0; + init_ttinfo(&sp->ttis[0], -stdoffset, false, 0); + sp->defaulttype = 0; + } + sp->charcnt = static_cast(charcnt); + cp = &sp->chars[0]; + memcpy(cp, stdname, stdlen); + cp += stdlen; + *cp++ = '\0'; + if (dstlen != 0) { + memcpy(cp, dstname, dstlen); + *(cp + dstlen) = '\0'; + } + return true; +} + +int tzloadbody(Rule* sp, local_storage& local_storage) { + int i; + int stored; + size_t nread{ local_storage.binary.size_bytes() }; + int tzheadsize = sizeof(struct TzifHeader); + TzifHeader header{}; + + //ASSERT(local_storage.binary.size_bytes() >= sizeof(TzifHeader)); + std::memcpy(&header, local_storage.binary.data(), sizeof(TzifHeader)); + + sp->goback = sp->goahead = false; + + for (stored = 8; stored <= 8; stored *= 2) { + s64 datablock_size; + s32 ttisstdcnt = detzcode(header.tzh_ttisstdcnt.data()); + s32 ttisutcnt = detzcode(header.tzh_ttisutcnt.data()); + s32 leapcnt = detzcode(header.tzh_leapcnt.data()); + s32 timecnt = detzcode(header.tzh_timecnt.data()); + s32 typecnt = detzcode(header.tzh_typecnt.data()); + s32 charcnt = detzcode(header.tzh_charcnt.data()); + /* Although tzfile(5) currently requires typecnt to be nonzero, + support future formats that may allow zero typecnt + in files that have a TZ string and no transitions. */ + if (!(0 <= leapcnt && leapcnt < TZ_MAX_LEAPS && 0 <= typecnt && typecnt < TZ_MAX_TYPES && + 0 <= timecnt && timecnt < TZ_MAX_TIMES && 0 <= charcnt && charcnt < TZ_MAX_CHARS && + 0 <= ttisstdcnt && ttisstdcnt < TZ_MAX_TYPES && 0 <= ttisutcnt && + ttisutcnt < TZ_MAX_TYPES)) { + return EINVAL; + } + datablock_size = (timecnt * stored /* ats */ + + timecnt /* types */ + + typecnt * 6 /* ttinfos */ + + charcnt /* chars */ + + leapcnt * (stored + 4) /* lsinfos */ + + ttisstdcnt /* ttisstds */ + + ttisutcnt); /* ttisuts */ + if (static_cast(local_storage.binary.size_bytes()) < tzheadsize + datablock_size) { + return EINVAL; + } + if (!((ttisstdcnt == typecnt || ttisstdcnt == 0) && + (ttisutcnt == typecnt || ttisutcnt == 0))) { + return EINVAL; + } + + char const* p = (const char*)local_storage.binary.data() + tzheadsize; + + sp->timecnt = timecnt; + sp->typecnt = typecnt; + sp->charcnt = charcnt; + + /* Read transitions, discarding those out of time_t range. + But pretend the last transition before TIME_T_MIN + occurred at TIME_T_MIN. */ + timecnt = 0; + for (i = 0; i < sp->timecnt; ++i) { + int_fast64_t at = stored == 4 ? detzcode(p) : detzcode64(p); + sp->types[i] = at <= TIME_T_MAX; + if (sp->types[i]) { + time_t attime = + ((std::is_signed_v ? at < TIME_T_MIN : at < 0) ? TIME_T_MIN : at); + if (timecnt && attime <= sp->ats[timecnt - 1]) { + if (attime < sp->ats[timecnt - 1]) + return EINVAL; + sp->types[i - 1] = 0; + timecnt--; + } + sp->ats[timecnt++] = attime; + } + p += stored; + } + + timecnt = 0; + for (i = 0; i < sp->timecnt; ++i) { + unsigned char typ = *p++; + if (sp->typecnt <= typ) { + return EINVAL; + } + if (sp->types[i]) { + sp->types[timecnt++] = typ; + } + } + sp->timecnt = timecnt; + for (i = 0; i < sp->typecnt; ++i) { + struct ttinfo* ttisp; + unsigned char isdst, desigidx; + + ttisp = &sp->ttis[i]; + ttisp->tt_utoff = detzcode(p); + p += 4; + isdst = *p++; + if (!(isdst < 2)) { + return EINVAL; + } + ttisp->tt_isdst = isdst != 0; + desigidx = *p++; + if (!(desigidx < sp->charcnt)) { + return EINVAL; + } + ttisp->tt_desigidx = desigidx; + } + for (i = 0; i < sp->charcnt; ++i) { + sp->chars[i] = *p++; + } + /* Ensure '\0'-terminated, and make it safe to call + ttunspecified later. */ + memset(&sp->chars[i], 0, CHARS_EXTRA); + + for (i = 0; i < sp->typecnt; ++i) { + struct ttinfo* ttisp; + + ttisp = &sp->ttis[i]; + if (ttisstdcnt == 0) { + ttisp->tt_ttisstd = false; + } + else { + if (*(bool*)p != true && *(bool*)p != false) { + return EINVAL; + } + ttisp->tt_ttisstd = *(bool*)p++; + } + } + for (i = 0; i < sp->typecnt; ++i) { + struct ttinfo* ttisp; + + ttisp = &sp->ttis[i]; + if (ttisutcnt == 0) { + ttisp->tt_ttisut = false; + } + else { + if (*(bool*)p != true && *(bool*)p != false) { + return EINVAL; + } + ttisp->tt_ttisut = *(bool*)p++; + } + } + + nread += (ptrdiff_t)local_storage.binary.data() - (ptrdiff_t)p; + if (nread < 0) { + return EINVAL; + } + } + + std::array buf{}; + if (nread > buf.size()) { + //ASSERT(false); + return EINVAL; + } + memmove(buf.data(), &local_storage.binary[local_storage.binary.size_bytes() - nread], nread); + + if (nread > 2 && buf[0] == '\n' && buf[nread - 1] == '\n' && sp->typecnt + 2 <= TZ_MAX_TYPES) { + Rule* ts = &local_storage.state; + + buf[nread - 1] = '\0'; + if (tzparse(&buf[1], ts) && local_storage.state.typecnt == 2) { + + /* Attempt to reuse existing abbreviations. + Without this, America/Anchorage would be right on + the edge after 2037 when TZ_MAX_CHARS is 50, as + sp->charcnt equals 40 (for LMT AST AWT APT AHST + AHDT YST AKDT AKST) and ts->charcnt equals 10 + (for AKST AKDT). Reusing means sp->charcnt can + stay 40 in this example. */ + int gotabbr = 0; + int charcnt = sp->charcnt; + for (i = 0; i < ts->typecnt; i++) { + char* tsabbr = &ts->chars[ts->ttis[i].tt_desigidx]; + int j; + for (j = 0; j < charcnt; j++) + if (strcmp(&sp->chars[j], tsabbr) == 0) { + ts->ttis[i].tt_desigidx = j; + gotabbr++; + break; + } + if (!(j < charcnt)) { + int tsabbrlen = static_cast(strlen(tsabbr)); + if (j + tsabbrlen < TZ_MAX_CHARS) { + strcpy(&sp->chars[j], tsabbr); + charcnt = j + tsabbrlen + 1; + ts->ttis[i].tt_desigidx = j; + gotabbr++; + } + } + } + if (gotabbr == ts->typecnt) { + sp->charcnt = charcnt; + + /* Ignore any trailing, no-op transitions generated + by zic as they don't help here and can run afoul + of bugs in zic 2016j or earlier. */ + while (1 < sp->timecnt && + (sp->types[sp->timecnt - 1] == sp->types[sp->timecnt - 2])) { + sp->timecnt--; + } + + for (i = 0; i < ts->timecnt && sp->timecnt < TZ_MAX_TIMES; i++) { + time_t t = ts->ats[i]; + if (0 < sp->timecnt && t <= sp->ats[sp->timecnt - 1]) { + continue; + } + sp->ats[sp->timecnt] = t; + sp->types[sp->timecnt] = static_cast(sp->typecnt + ts->types[i]); + sp->timecnt++; + } + for (i = 0; i < ts->typecnt; i++) { + sp->ttis[sp->typecnt++] = ts->ttis[i]; + } + } + } + } + if (sp->typecnt == 0) { + return EINVAL; + } + + if (sp->timecnt > 1) { + if (sp->ats[0] <= TIME_T_MAX - SECSPERREPEAT) { + time_t repeatat = sp->ats[0] + SECSPERREPEAT; + int repeattype = sp->types[0]; + for (i = 1; i < sp->timecnt; ++i) { + if (sp->ats[i] == repeatat && typesequiv(sp, sp->types[i], repeattype)) { + sp->goback = true; + break; + } + } + } + if (TIME_T_MIN + SECSPERREPEAT <= sp->ats[sp->timecnt - 1]) { + time_t repeatat = sp->ats[sp->timecnt - 1] - SECSPERREPEAT; + int repeattype = sp->types[sp->timecnt - 1]; + for (i = sp->timecnt - 2; i >= 0; --i) { + if (sp->ats[i] == repeatat && typesequiv(sp, sp->types[i], repeattype)) { + sp->goahead = true; + break; + } + } + } + } + + /* Infer sp->defaulttype from the data. Although this default + type is always zero for data from recent tzdb releases, + things are trickier for data from tzdb 2018e or earlier. + + The first set of heuristics work around bugs in 32-bit data + generated by tzdb 2013c or earlier. The workaround is for + zones like Australia/Macquarie where timestamps before the + first transition have a time type that is not the earliest + standard-time type. See: + https://mm.icann.org/pipermail/tz/2013-May/019368.html */ + /* + ** If type 0 does not specify local time, or is unused in transitions, + ** it's the type to use for early times. + */ + for (i = 0; i < sp->timecnt; ++i) { + if (sp->types[i] == 0) { + break; + } + } + i = i < sp->timecnt && !ttunspecified(sp, 0) ? -1 : 0; + /* + ** Absent the above, + ** if there are transition times + ** and the first transition is to a daylight time + ** find the standard type less than and closest to + ** the type of the first transition. + */ + if (i < 0 && sp->timecnt > 0 && sp->ttis[sp->types[0]].tt_isdst) { + i = sp->types[0]; + while (--i >= 0) { + if (!sp->ttis[i].tt_isdst) { + break; + } + } + } + /* The next heuristics are for data generated by tzdb 2018e or + earlier, for zones like EST5EDT where the first transition + is to DST. */ + /* + ** If no result yet, find the first standard type. + ** If there is none, punt to type zero. + */ + if (i < 0) { + i = 0; + while (sp->ttis[i].tt_isdst) { + if (++i >= sp->typecnt) { + i = 0; + break; + } + } + } + /* A simple 'sp->defaulttype = 0;' would suffice here if we + didn't have to worry about 2018e-or-earlier data. Even + simpler would be to remove the defaulttype member and just + use 0 in its place. */ + sp->defaulttype = i; + + return 0; +} + +constexpr int tmcomp(const CalendarTimeInternal* const atmp, + const CalendarTimeInternal* const btmp) { + int result; + + if (atmp->tm_year != btmp->tm_year) { + return atmp->tm_year < btmp->tm_year ? -1 : 1; + } + if ((result = (atmp->tm_mon - btmp->tm_mon)) == 0 && + (result = (atmp->tm_mday - btmp->tm_mday)) == 0 && + (result = (atmp->tm_hour - btmp->tm_hour)) == 0 && + (result = (atmp->tm_min - btmp->tm_min)) == 0) { + result = atmp->tm_sec - btmp->tm_sec; + } + return result; +} + +/* Copy to *DEST from *SRC. Copy only the members needed for mktime, + as other members might not be initialized. */ +constexpr void mktmcpy(struct CalendarTimeInternal* dest, struct CalendarTimeInternal const* src) { + dest->tm_sec = src->tm_sec; + dest->tm_min = src->tm_min; + dest->tm_hour = src->tm_hour; + dest->tm_mday = src->tm_mday; + dest->tm_mon = src->tm_mon; + dest->tm_year = src->tm_year; + dest->tm_isdst = src->tm_isdst; + dest->tm_zone = src->tm_zone; + dest->tm_utoff = src->tm_utoff; + dest->time_index = src->time_index; +} + +constexpr bool normalize_overflow(int* const tensptr, int* const unitsptr, const int base) { + int tensdelta; + + tensdelta = (*unitsptr >= 0) ? (*unitsptr / base) : (-1 - (-1 - *unitsptr) / base); + *unitsptr -= tensdelta * base; + return increment_overflow(tensptr, tensdelta); +} + +constexpr bool normalize_overflow32(s64* tensptr, int* unitsptr, int base) { + int tensdelta; + + tensdelta = (*unitsptr >= 0) ? (*unitsptr / base) : (-1 - (-1 - *unitsptr) / base); + *unitsptr -= tensdelta * base; + return increment_overflow32(tensptr, tensdelta); +} + +int time2sub(time_t* out_time, CalendarTimeInternal* const tmp, + CalendarTimeInternal* (*funcp)(Rule const*, time_t const*, s64, + CalendarTimeInternal*), + Rule const* sp, const s64 offset, bool* okayp, bool do_norm_secs) { + int dir; + int i, j; + int saved_seconds; + s64 li; + time_t lo; + time_t hi; + s64 y; + time_t newt; + time_t t; + CalendarTimeInternal yourtm, mytm; + + *okayp = false; + mktmcpy(&yourtm, tmp); + + if (do_norm_secs) { + if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec, SECSPERMIN)) { + return 1; + } + } + if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR)) { + return 1; + } + if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY)) { + return 1; + } + y = yourtm.tm_year; + if (normalize_overflow32(&y, &yourtm.tm_mon, MONSPERYEAR)) { + return 1; + } + /* + ** Turn y into an actual year number for now. + ** It is converted back to an offset from TM_YEAR_BASE later. + */ + if (increment_overflow32(&y, TM_YEAR_BASE)) { + return 1; + } + while (yourtm.tm_mday <= 0) { + if (increment_overflow32(&y, -1)) { + return 1; + } + li = y + (1 < yourtm.tm_mon); + yourtm.tm_mday += year_lengths[isleap(li)]; + } + while (yourtm.tm_mday > DAYSPERLYEAR) { + li = y + (1 < yourtm.tm_mon); + yourtm.tm_mday -= year_lengths[isleap(li)]; + if (increment_overflow32(&y, 1)) { + return 1; + } + } + for (;;) { + i = mon_lengths[isleap(y)][yourtm.tm_mon]; + if (yourtm.tm_mday <= i) { + break; + } + yourtm.tm_mday -= i; + if (++yourtm.tm_mon >= MONSPERYEAR) { + yourtm.tm_mon = 0; + if (increment_overflow32(&y, 1)) { + return 1; + } + } + } + + if (increment_overflow32(&y, -TM_YEAR_BASE)) { + return 1; + } + if (!(INT_MIN <= y && y <= INT_MAX)) { + return 1; + } + yourtm.tm_year = static_cast(y); + + if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN) { + saved_seconds = 0; + } + else if (yourtm.tm_year < EPOCH_YEAR - TM_YEAR_BASE) { + /* + ** We can't set tm_sec to 0, because that might push the + ** time below the minimum representable time. + ** Set tm_sec to 59 instead. + ** This assumes that the minimum representable time is + ** not in the same minute that a leap second was deleted from, + ** which is a safer assumption than using 58 would be. + */ + if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN)) { + return 1; + } + saved_seconds = yourtm.tm_sec; + yourtm.tm_sec = SECSPERMIN - 1; + } + else { + saved_seconds = yourtm.tm_sec; + yourtm.tm_sec = 0; + } + /* + ** Do a binary search (this works whatever time_t's type is). + */ + lo = TIME_T_MIN; + hi = TIME_T_MAX; + for (;;) { + t = lo / 2 + hi / 2; + if (t < lo) { + t = lo; + } + else if (t > hi) { + t = hi; + } + if (!funcp(sp, &t, offset, &mytm)) { + /* + ** Assume that t is too extreme to be represented in + ** a struct tm; arrange things so that it is less + ** extreme on the next pass. + */ + dir = (t > 0) ? 1 : -1; + } + else { + dir = tmcomp(&mytm, &yourtm); + } + if (dir != 0) { + if (t == lo) { + if (t == TIME_T_MAX) { + return 2; + } + ++t; + ++lo; + } + else if (t == hi) { + if (t == TIME_T_MIN) { + return 2; + } + --t; + --hi; + } + if (lo > hi) { + return 2; + } + if (dir > 0) { + hi = t; + } + else { + lo = t; + } + continue; + } + + if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) { + break; + } + /* + ** Right time, wrong type. + ** Hunt for right time, right type. + ** It's okay to guess wrong since the guess + ** gets checked. + */ + if (sp == nullptr) { + return 2; + } + for (i = sp->typecnt - 1; i >= 0; --i) { + if (sp->ttis[i].tt_isdst != static_cast(yourtm.tm_isdst)) { + continue; + } + for (j = sp->typecnt - 1; j >= 0; --j) { + if (sp->ttis[j].tt_isdst == static_cast(yourtm.tm_isdst)) { + continue; + } + if (ttunspecified(sp, j)) { + continue; + } + newt = (t + sp->ttis[j].tt_utoff - sp->ttis[i].tt_utoff); + if (!funcp(sp, &newt, offset, &mytm)) { + continue; + } + if (tmcomp(&mytm, &yourtm) != 0) { + continue; + } + if (mytm.tm_isdst != yourtm.tm_isdst) { + continue; + } + /* + ** We have a match. + */ + t = newt; + goto label; + } + } + return 2; + } +label: + newt = t + saved_seconds; + t = newt; + if (funcp(sp, &t, offset, tmp) || *okayp) { + *okayp = true; + *out_time = t; + return 0; + } + return 2; +} + +int time2(time_t* out_time, struct CalendarTimeInternal* const tmp, + struct CalendarTimeInternal* (*funcp)(struct Rule const*, time_t const*, s64, + struct CalendarTimeInternal*), + struct Rule const* sp, const s64 offset, bool* okayp) { + int res; + + /* + ** First try without normalization of seconds + ** (in case tm_sec contains a value associated with a leap second). + ** If that fails, try with normalization of seconds. + */ + res = time2sub(out_time, tmp, funcp, sp, offset, okayp, false); + return *okayp ? res : time2sub(out_time, tmp, funcp, sp, offset, okayp, true); +} + +int time1(time_t* out_time, CalendarTimeInternal* const tmp, + CalendarTimeInternal* (*funcp)(Rule const*, time_t const*, s64, + CalendarTimeInternal*), + Rule const* sp, const s64 offset) { + int samei, otheri; + int sameind, otherind; + int i; + int nseen; + char seen[TZ_MAX_TYPES]; + unsigned char types[TZ_MAX_TYPES]; + bool okay; + + if (tmp->tm_isdst > 1) { + tmp->tm_isdst = 1; + } + auto res = time2(out_time, tmp, funcp, sp, offset, &okay); + if (res == 0) { + return res; + } + if (tmp->tm_isdst < 0) { + return res; + } + /* + ** We're supposed to assume that somebody took a time of one type + ** and did some math on it that yielded a "struct tm" that's bad. + ** We try to divine the type they started from and adjust to the + ** type they need. + */ + for (i = 0; i < sp->typecnt; ++i) { + seen[i] = false; + } + + if (sp->timecnt < 1) { + return 2; + } + + nseen = 0; + for (i = sp->timecnt - 1; i >= 0; --i) { + if (!seen[sp->types[i]] && !ttunspecified(sp, sp->types[i])) { + seen[sp->types[i]] = true; + types[nseen++] = sp->types[i]; + } + } + + if (nseen < 1) { + return 2; + } + + for (sameind = 0; sameind < nseen; ++sameind) { + samei = types[sameind]; + if (sp->ttis[samei].tt_isdst != static_cast(tmp->tm_isdst)) { + continue; + } + for (otherind = 0; otherind < nseen; ++otherind) { + otheri = types[otherind]; + if (sp->ttis[otheri].tt_isdst == static_cast(tmp->tm_isdst)) { + continue; + } + tmp->tm_sec += (sp->ttis[otheri].tt_utoff - sp->ttis[samei].tt_utoff); + tmp->tm_isdst = !tmp->tm_isdst; + res = time2(out_time, tmp, funcp, sp, offset, &okay); + if (res == 0) { + return res; + } + tmp->tm_sec -= (sp->ttis[otheri].tt_utoff - sp->ttis[samei].tt_utoff); + tmp->tm_isdst = !tmp->tm_isdst; + } + } + return 2; +} + +} // namespace + +s32 ParseTimeZoneBinary(Rule& out_rule, std::span binary) { + tzloadbody_local_storage.binary = binary; + if (tzloadbody(&out_rule, tzloadbody_local_storage)) { + return 3; + } + return 0; +} + +bool localtime_rz(CalendarTimeInternal* tmp, Rule* sp, time_t* timep) { + return localsub(sp, timep, 0, tmp) == nullptr; +} + +u32 mktime_tzname(time_t* out_time, Rule* sp, CalendarTimeInternal* tmp) { + return time1(out_time, tmp, localsub, sp, 0); +} + +} // namespace Tz diff --git a/externals/tz/tz/tz.h b/externals/tz/tz/tz.h new file mode 100755 index 000000000..38605cfb1 --- /dev/null +++ b/externals/tz/tz/tz.h @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-FileCopyrightText: 1996 Arthur David Olson +// SPDX-License-Identifier: BSD-2-Clause + +#pragma once + +#include +#include +#include +#include +#include + +namespace Tz { +using u8 = uint8_t; +using s8 = int8_t; +using u16 = uint16_t; +using s16 = int16_t; +using u32 = uint32_t; +using s32 = int32_t; +using u64 = uint64_t; +using s64 = int64_t; + +constexpr size_t TZ_MAX_TIMES = 1000; +constexpr size_t TZ_MAX_TYPES = 128; +constexpr size_t TZ_MAX_CHARS = 50; +constexpr size_t MY_TZNAME_MAX = 255; +constexpr size_t TZNAME_MAXIMUM = 255; +constexpr size_t TZ_MAX_LEAPS = 50; +constexpr s64 TIME_T_MAX = std::numeric_limits::max(); +constexpr s64 TIME_T_MIN = std::numeric_limits::min(); +constexpr size_t CHARS_EXTRA = 3; +constexpr size_t MAX_ZONE_CHARS = std::max(TZ_MAX_CHARS + CHARS_EXTRA, sizeof("UTC")); +constexpr size_t MAX_TZNAME_CHARS = 2 * (MY_TZNAME_MAX + 1); + +struct ttinfo { + s32 tt_utoff; + bool tt_isdst; + s32 tt_desigidx; + bool tt_ttisstd; + bool tt_ttisut; +}; +static_assert(sizeof(ttinfo) == 0x10, "ttinfo has the wrong size!"); + +struct Rule { + s32 timecnt; + s32 typecnt; + s32 charcnt; + bool goback; + bool goahead; + std::array padding0; + std::array ats; + std::array types; + std::array ttis; + std::array chars; + s32 defaulttype; + std::array padding1; +}; +static_assert(sizeof(Rule) == 0x4000, "Rule has the wrong size!"); + +struct CalendarTimeInternal { + s32 tm_sec; + s32 tm_min; + s32 tm_hour; + s32 tm_mday; + s32 tm_mon; + s32 tm_year; + s32 tm_wday; + s32 tm_yday; + s32 tm_isdst; + std::array tm_zone; + s32 tm_utoff; + s32 time_index; +}; +static_assert(sizeof(CalendarTimeInternal) == 0x3C, "CalendarTimeInternal has the wrong size!"); + +s32 ParseTimeZoneBinary(Rule& out_rule, std::span binary); + +bool localtime_rz(CalendarTimeInternal* tmp, Rule* sp, time_t* timep); +u32 mktime_tzname(time_t* out_time, Rule* sp, CalendarTimeInternal* tmp); + +} // namespace Tz diff --git a/src/android/app/src/main/jni/android_config.cpp b/src/android/app/src/main/jni/android_config.cpp index 08aed3216..e147560c3 100755 --- a/src/android/app/src/main/jni/android_config.cpp +++ b/src/android/app/src/main/jni/android_config.cpp @@ -21,7 +21,7 @@ void AndroidConfig::ReloadAllValues() { } void AndroidConfig::SaveAllValues() { - Save(); + SaveValues(); SaveAndroidValues(); } diff --git a/src/common/arm64/native_clock.cpp b/src/common/arm64/native_clock.cpp index f437d7187..55147f0f2 100755 --- a/src/common/arm64/native_clock.cpp +++ b/src/common/arm64/native_clock.cpp @@ -29,28 +29,32 @@ NativeClock::NativeClock() { gputick_cntfrq_factor = GetFixedPointFactor(GPUTickFreq, host_cntfrq); } +void NativeClock::Reset() { + start_ticks = GetUptime(); +} + std::chrono::nanoseconds NativeClock::GetTimeNS() const { - return std::chrono::nanoseconds{MultiplyHigh(GetHostTicksElapsed(), ns_cntfrq_factor)}; + return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_cntfrq_factor)}; } std::chrono::microseconds NativeClock::GetTimeUS() const { - return std::chrono::microseconds{MultiplyHigh(GetHostTicksElapsed(), us_cntfrq_factor)}; + return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_cntfrq_factor)}; } std::chrono::milliseconds NativeClock::GetTimeMS() const { - return std::chrono::milliseconds{MultiplyHigh(GetHostTicksElapsed(), ms_cntfrq_factor)}; + return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_cntfrq_factor)}; } -u64 NativeClock::GetCNTPCT() const { - return MultiplyHigh(GetHostTicksElapsed(), guest_cntfrq_factor); +s64 NativeClock::GetCNTPCT() const { + return MultiplyHigh(GetUptime() - start_ticks, guest_cntfrq_factor); } -u64 NativeClock::GetGPUTick() const { - return MultiplyHigh(GetHostTicksElapsed(), gputick_cntfrq_factor); +s64 NativeClock::GetGPUTick() const { + return MultiplyHigh(GetUptime() - start_ticks, gputick_cntfrq_factor); } -u64 NativeClock::GetHostTicksNow() const { - u64 cntvct_el0 = 0; +s64 NativeClock::GetUptime() const { + s64 cntvct_el0 = 0; asm volatile("dsb ish\n\t" "mrs %[cntvct_el0], cntvct_el0\n\t" "dsb ish\n\t" @@ -58,15 +62,11 @@ u64 NativeClock::GetHostTicksNow() const { return cntvct_el0; } -u64 NativeClock::GetHostTicksElapsed() const { - return GetHostTicksNow(); -} - bool NativeClock::IsNative() const { return true; } -u64 NativeClock::GetHostCNTFRQ() { +s64 NativeClock::GetHostCNTFRQ() { u64 cntfrq_el0 = 0; std::string_view board{""}; #ifdef ANDROID diff --git a/src/common/arm64/native_clock.h b/src/common/arm64/native_clock.h index a28b419f2..39cad5710 100755 --- a/src/common/arm64/native_clock.h +++ b/src/common/arm64/native_clock.h @@ -11,23 +11,23 @@ class NativeClock final : public WallClock { public: explicit NativeClock(); + void Reset() override; + std::chrono::nanoseconds GetTimeNS() const override; std::chrono::microseconds GetTimeUS() const override; std::chrono::milliseconds GetTimeMS() const override; - u64 GetCNTPCT() const override; + s64 GetCNTPCT() const override; - u64 GetGPUTick() const override; + s64 GetGPUTick() const override; - u64 GetHostTicksNow() const override; - - u64 GetHostTicksElapsed() const override; + s64 GetUptime() const override; bool IsNative() const override; - static u64 GetHostCNTFRQ(); + static s64 GetHostCNTFRQ(); public: using FactorType = unsigned __int128; @@ -42,6 +42,7 @@ private: FactorType ms_cntfrq_factor; FactorType guest_cntfrq_factor; FactorType gputick_cntfrq_factor; + s64 start_ticks; }; } // namespace Common::Arm64 diff --git a/src/common/memory_detect.h b/src/common/memory_detect.h index 6e77ef01e..9e125d7ab 100755 --- a/src/common/memory_detect.h +++ b/src/common/memory_detect.h @@ -18,4 +18,4 @@ struct MemoryInfo { */ [[nodiscard]] const MemoryInfo& GetMemInfo(); -} // namespace Common \ No newline at end of file +} // namespace Common diff --git a/src/common/uuid.h b/src/common/uuid.h index 917280515..131a7cf9e 100755 --- a/src/common/uuid.h +++ b/src/common/uuid.h @@ -12,9 +12,8 @@ namespace Common { struct UUID { - std::array uuid{}; + std::array uuid; - /// Constructs an invalid UUID. constexpr UUID() = default; /// Constructs a UUID from a reference to a 128 bit array. @@ -34,14 +33,6 @@ struct UUID { */ explicit UUID(std::string_view uuid_string); - ~UUID() = default; - - constexpr UUID(const UUID&) noexcept = default; - constexpr UUID(UUID&&) noexcept = default; - - constexpr UUID& operator=(const UUID&) noexcept = default; - constexpr UUID& operator=(UUID&&) noexcept = default; - /** * Returns whether the stored UUID is valid or not. * @@ -121,6 +112,7 @@ struct UUID { friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default; }; static_assert(sizeof(UUID) == 0x10, "UUID has incorrect size."); +static_assert(std::is_trivial_v); /// An invalid UUID. This UUID has all its bytes set to 0. constexpr UUID InvalidUUID = {}; diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp index 746778616..22174fcc1 100755 --- a/src/common/wall_clock.cpp +++ b/src/common/wall_clock.cpp @@ -18,34 +18,39 @@ namespace Common { class StandardWallClock final : public WallClock { public: - explicit StandardWallClock() : start_time{SteadyClock::Now()} {} + explicit StandardWallClock() {} + + void Reset() override { + start_time = std::chrono::system_clock::now(); + } std::chrono::nanoseconds GetTimeNS() const override { - return SteadyClock::Now() - start_time; + return std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()); } std::chrono::microseconds GetTimeUS() const override { - return static_cast(GetHostTicksElapsed() / NsToUsRatio::den); + return std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()); } std::chrono::milliseconds GetTimeMS() const override { - return static_cast(GetHostTicksElapsed() / NsToMsRatio::den); + return std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()); } - u64 GetCNTPCT() const override { - return GetHostTicksElapsed() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den; + s64 GetCNTPCT() const override { + return GetUptime() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den; } - u64 GetGPUTick() const override { - return GetHostTicksElapsed() * NsToGPUTickRatio::num / NsToGPUTickRatio::den; + s64 GetGPUTick() const override { + return GetUptime() * NsToGPUTickRatio::num / NsToGPUTickRatio::den; } - u64 GetHostTicksNow() const override { - return static_cast(SteadyClock::Now().time_since_epoch().count()); - } - - u64 GetHostTicksElapsed() const override { - return static_cast(GetTimeNS().count()); + s64 GetUptime() const override { + return std::chrono::duration_cast( + std::chrono::system_clock::now() - start_time) + .count(); } bool IsNative() const override { @@ -53,7 +58,7 @@ public: } private: - SteadyClock::time_point start_time; + std::chrono::system_clock::time_point start_time{}; }; std::unique_ptr CreateOptimalClock() { diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h index d885c84e5..192d4466a 100755 --- a/src/common/wall_clock.h +++ b/src/common/wall_clock.h @@ -19,6 +19,8 @@ public: virtual ~WallClock() = default; + virtual void Reset() = 0; + /// @returns The time in nanoseconds since the construction of this clock. virtual std::chrono::nanoseconds GetTimeNS() const = 0; @@ -29,16 +31,13 @@ public: virtual std::chrono::milliseconds GetTimeMS() const = 0; /// @returns The guest CNTPCT ticks since the construction of this clock. - virtual u64 GetCNTPCT() const = 0; + virtual s64 GetCNTPCT() const = 0; /// @returns The guest GPU ticks since the construction of this clock. - virtual u64 GetGPUTick() const = 0; + virtual s64 GetGPUTick() const = 0; /// @returns The raw host timer ticks since an indeterminate epoch. - virtual u64 GetHostTicksNow() const = 0; - - /// @returns The raw host timer ticks since the construction of this clock. - virtual u64 GetHostTicksElapsed() const = 0; + virtual s64 GetUptime() const = 0; /// @returns Whether the clock directly uses the host's hardware clock. virtual bool IsNative() const = 0; diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp index 9fbe75bd1..21dfce8a8 100755 --- a/src/common/x64/native_clock.cpp +++ b/src/common/x64/native_clock.cpp @@ -8,39 +8,39 @@ namespace Common::X64 { NativeClock::NativeClock(u64 rdtsc_frequency_) - : start_ticks{FencedRDTSC()}, rdtsc_frequency{rdtsc_frequency_}, - ns_rdtsc_factor{GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency)}, + : rdtsc_frequency{rdtsc_frequency_}, ns_rdtsc_factor{GetFixedPoint64Factor(NsRatio::den, + rdtsc_frequency)}, us_rdtsc_factor{GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency)}, ms_rdtsc_factor{GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency)}, cntpct_rdtsc_factor{GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency)}, gputick_rdtsc_factor{GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency)} {} +void NativeClock::Reset() { + start_ticks = FencedRDTSC(); +} + std::chrono::nanoseconds NativeClock::GetTimeNS() const { - return std::chrono::nanoseconds{MultiplyHigh(GetHostTicksElapsed(), ns_rdtsc_factor)}; + return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_rdtsc_factor)}; } std::chrono::microseconds NativeClock::GetTimeUS() const { - return std::chrono::microseconds{MultiplyHigh(GetHostTicksElapsed(), us_rdtsc_factor)}; + return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_rdtsc_factor)}; } std::chrono::milliseconds NativeClock::GetTimeMS() const { - return std::chrono::milliseconds{MultiplyHigh(GetHostTicksElapsed(), ms_rdtsc_factor)}; + return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_rdtsc_factor)}; } -u64 NativeClock::GetCNTPCT() const { - return MultiplyHigh(GetHostTicksElapsed(), cntpct_rdtsc_factor); +s64 NativeClock::GetCNTPCT() const { + return MultiplyHigh(GetUptime() - start_ticks, cntpct_rdtsc_factor); } -u64 NativeClock::GetGPUTick() const { - return MultiplyHigh(GetHostTicksElapsed(), gputick_rdtsc_factor); +s64 NativeClock::GetGPUTick() const { + return MultiplyHigh(GetUptime() - start_ticks, gputick_rdtsc_factor); } -u64 NativeClock::GetHostTicksNow() const { - return FencedRDTSC(); -} - -u64 NativeClock::GetHostTicksElapsed() const { - return FencedRDTSC() - start_ticks; +s64 NativeClock::GetUptime() const { + return static_cast(FencedRDTSC()); } bool NativeClock::IsNative() const { diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h index 925d3928c..1fa151b09 100755 --- a/src/common/x64/native_clock.h +++ b/src/common/x64/native_clock.h @@ -11,19 +11,19 @@ class NativeClock final : public WallClock { public: explicit NativeClock(u64 rdtsc_frequency_); + void Reset() override; + std::chrono::nanoseconds GetTimeNS() const override; std::chrono::microseconds GetTimeUS() const override; std::chrono::milliseconds GetTimeMS() const override; - u64 GetCNTPCT() const override; + s64 GetCNTPCT() const override; - u64 GetGPUTick() const override; + s64 GetGPUTick() const override; - u64 GetHostTicksNow() const override; - - u64 GetHostTicksElapsed() const override; + s64 GetUptime() const override; bool IsNative() const override; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 31b0715e5..4d91e01a2 100755 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -511,6 +511,24 @@ add_library(core STATIC hle/service/glue/glue_manager.h hle/service/glue/notif.cpp hle/service/glue/notif.h + hle/service/glue/time/alarm_worker.cpp + hle/service/glue/time/alarm_worker.h + hle/service/glue/time/file_timestamp_worker.cpp + hle/service/glue/time/file_timestamp_worker.h + hle/service/glue/time/manager.cpp + hle/service/glue/time/manager.h + hle/service/glue/time/pm_state_change_handler.cpp + hle/service/glue/time/pm_state_change_handler.h + hle/service/glue/time/standard_steady_clock_resource.cpp + hle/service/glue/time/standard_steady_clock_resource.h + hle/service/glue/time/static.cpp + hle/service/glue/time/static.h + hle/service/glue/time/time_zone.cpp + hle/service/glue/time/time_zone.h + hle/service/glue/time/time_zone_binary.cpp + hle/service/glue/time/time_zone_binary.h + hle/service/glue/time/worker.cpp + hle/service/glue/time/worker.h hle/service/grc/grc.cpp hle/service/grc/grc.h hle/service/hid/hid.cpp @@ -689,6 +707,46 @@ add_library(core STATIC hle/service/prepo/prepo.h hle/service/psc/psc.cpp hle/service/psc/psc.h + hle/service/psc/time/alarms.cpp + hle/service/psc/time/alarms.h + hle/service/psc/time/clocks/context_writers.cpp + hle/service/psc/time/clocks/context_writers.h + hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h + hle/service/psc/time/clocks/standard_local_system_clock_core.cpp + hle/service/psc/time/clocks/standard_local_system_clock_core.h + hle/service/psc/time/clocks/standard_network_system_clock_core.cpp + hle/service/psc/time/clocks/standard_network_system_clock_core.h + hle/service/psc/time/clocks/standard_steady_clock_core.cpp + hle/service/psc/time/clocks/standard_steady_clock_core.h + hle/service/psc/time/clocks/standard_user_system_clock_core.cpp + hle/service/psc/time/clocks/standard_user_system_clock_core.h + hle/service/psc/time/clocks/steady_clock_core.h + hle/service/psc/time/clocks/system_clock_core.cpp + hle/service/psc/time/clocks/system_clock_core.h + hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp + hle/service/psc/time/clocks/tick_based_steady_clock_core.h + hle/service/psc/time/common.cpp + hle/service/psc/time/common.h + hle/service/psc/time/errors.h + hle/service/psc/time/shared_memory.cpp + hle/service/psc/time/shared_memory.h + hle/service/psc/time/static.cpp + hle/service/psc/time/static.h + hle/service/psc/time/manager.h + hle/service/psc/time/power_state_service.cpp + hle/service/psc/time/power_state_service.h + hle/service/psc/time/service_manager.cpp + hle/service/psc/time/service_manager.h + hle/service/psc/time/steady_clock.cpp + hle/service/psc/time/steady_clock.h + hle/service/psc/time/system_clock.cpp + hle/service/psc/time/system_clock.h + hle/service/psc/time/time_zone.cpp + hle/service/psc/time/time_zone.h + hle/service/psc/time/time_zone_service.cpp + hle/service/psc/time/time_zone_service.h + hle/service/psc/time/power_state_request_manager.cpp + hle/service/psc/time/power_state_request_manager.h hle/service/ptm/psm.cpp hle/service/ptm/psm.h hle/service/ptm/ptm.cpp @@ -712,24 +770,24 @@ add_library(core STATIC hle/service/server_manager.h hle/service/service.cpp hle/service/service.h - hle/service/set/set.cpp - hle/service/set/set.h hle/service/set/appln_settings.cpp hle/service/set/appln_settings.h hle/service/set/device_settings.cpp hle/service/set/device_settings.h + hle/service/set/factory_settings_server.cpp + hle/service/set/factory_settings_server.h + hle/service/set/firmware_debug_settings_server.cpp + hle/service/set/firmware_debug_settings_server.h hle/service/set/private_settings.cpp hle/service/set/private_settings.h - hle/service/set/set_cal.cpp - hle/service/set/set_cal.h - hle/service/set/set_fd.cpp - hle/service/set/set_fd.h - hle/service/set/set_sys.cpp - hle/service/set/set_sys.h hle/service/set/settings.cpp hle/service/set/settings.h + hle/service/set/settings_server.cpp + hle/service/set/settings_server.h hle/service/set/system_settings.cpp hle/service/set/system_settings.h + hle/service/set/system_settings_server.cpp + hle/service/set/system_settings_server.h hle/service/sm/sm.cpp hle/service/sm/sm.h hle/service/sm/sm_controller.cpp @@ -755,40 +813,6 @@ add_library(core STATIC hle/service/ssl/ssl.cpp hle/service/ssl/ssl.h hle/service/ssl/ssl_backend.h - hle/service/time/clock_types.h - hle/service/time/ephemeral_network_system_clock_context_writer.h - hle/service/time/ephemeral_network_system_clock_core.h - hle/service/time/errors.h - hle/service/time/local_system_clock_context_writer.h - hle/service/time/network_system_clock_context_writer.h - hle/service/time/standard_local_system_clock_core.h - hle/service/time/standard_network_system_clock_core.h - hle/service/time/standard_steady_clock_core.cpp - hle/service/time/standard_steady_clock_core.h - hle/service/time/standard_user_system_clock_core.cpp - hle/service/time/standard_user_system_clock_core.h - hle/service/time/steady_clock_core.h - hle/service/time/system_clock_context_update_callback.cpp - hle/service/time/system_clock_context_update_callback.h - hle/service/time/system_clock_core.cpp - hle/service/time/system_clock_core.h - hle/service/time/tick_based_steady_clock_core.cpp - hle/service/time/tick_based_steady_clock_core.h - hle/service/time/time.cpp - hle/service/time/time.h - hle/service/time/time_interface.cpp - hle/service/time/time_interface.h - hle/service/time/time_manager.cpp - hle/service/time/time_manager.h - hle/service/time/time_sharedmemory.cpp - hle/service/time/time_sharedmemory.h - hle/service/time/time_zone_content_manager.cpp - hle/service/time/time_zone_content_manager.h - hle/service/time/time_zone_manager.cpp - hle/service/time/time_zone_manager.h - hle/service/time/time_zone_service.cpp - hle/service/time/time_zone_service.h - hle/service/time/time_zone_types.h hle/service/usb/usb.cpp hle/service/usb/usb.h hle/service/vi/display/vi_display.cpp @@ -869,7 +893,7 @@ endif() create_target_directory_groups(core) -target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb) +target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb tz) target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls RenderDoc::API) if (MINGW) target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY}) diff --git a/src/core/core.cpp b/src/core/core.cpp index 479615b12..f33552e64 100755 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -40,9 +40,12 @@ #include "core/hle/service/apm/apm_controller.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/glue/glue_manager.h" +#include "core/hle/service/glue/time/static.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/service.h" #include "core/hle/service/sm/sm.h" -#include "core/hle/service/time/time_manager.h" #include "core/internal_network/network.h" #include "core/loader/loader.h" #include "core/memory.h" @@ -130,8 +133,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, struct System::Impl { explicit Impl(System& system) - : kernel{system}, fs_controller{system}, hid_core{}, room_network{}, cpu_manager{system}, - reporter{system}, applet_manager{system}, profile_manager{}, time_manager{system} {} + : kernel{system}, fs_controller{system}, hid_core{}, room_network{}, + cpu_manager{system}, reporter{system}, applet_manager{system}, profile_manager{} {} void Initialize(System& system) { device_memory = std::make_unique(); @@ -143,7 +146,7 @@ struct System::Impl { core_timing.SetMulticore(is_multicore); core_timing.Initialize([&system]() { system.RegisterHostThread(); }); - RefreshTime(); + RefreshTime(system); // Create a default fs if one doesn't already exist. if (virtual_filesystem == nullptr) { @@ -182,7 +185,7 @@ struct System::Impl { Initialize(system); } - void RefreshTime() { + void RefreshTime(System& system) { const auto posix_time = std::chrono::system_clock::now().time_since_epoch(); const auto current_time = std::chrono::duration_cast(posix_time).count(); @@ -190,6 +193,32 @@ struct System::Impl { (Settings::values.custom_rtc_enabled ? Settings::values.custom_rtc.GetValue() : current_time) - current_time; + + if (!system.IsPoweredOn()) { + return; + } + + auto static_service_a = + system.ServiceManager().GetService("time:a", true); + auto static_service_s = + system.ServiceManager().GetService("time:s", true); + + std::shared_ptr user_clock; + static_service_a->GetStandardUserSystemClock(user_clock); + + std::shared_ptr local_clock; + static_service_a->GetStandardLocalSystemClock(local_clock); + + std::shared_ptr network_clock; + static_service_s->GetStandardNetworkSystemClock(network_clock); + + const auto new_time = Settings::values.custom_rtc_enabled + ? Settings::values.custom_rtc.GetValue() + : current_time; + + user_clock->SetCurrentTime(new_time); + local_clock->SetCurrentTime(new_time); + network_clock->SetCurrentTime(new_time); } void Run() { @@ -265,9 +294,6 @@ struct System::Impl { service_manager = std::make_shared(kernel); services = std::make_unique(service_manager, system); - // Initialize time manager, which must happen after kernel is created - time_manager.Initialize(); - is_powered_on = true; exit_locked = false; exit_requested = false; @@ -416,7 +442,6 @@ struct System::Impl { service_manager.reset(); cheat_engine.reset(); telemetry_session.reset(); - time_manager.Shutdown(); core_timing.ClearPendingEvents(); app_loader.reset(); audio_core.reset(); @@ -532,7 +557,6 @@ struct System::Impl { /// Service State Service::Glue::ARPManager arp_manager; Service::Account::ProfileManager profile_manager; - Service::Time::TimeManager time_manager; /// Service manager std::shared_ptr service_manager; @@ -910,14 +934,6 @@ const Service::Account::ProfileManager& System::GetProfileManager() const { return impl->profile_manager; } -Service::Time::TimeManager& System::GetTimeManager() { - return impl->time_manager; -} - -const Service::Time::TimeManager& System::GetTimeManager() const { - return impl->time_manager; -} - void System::SetExitLocked(bool locked) { impl->exit_locked = locked; } @@ -1029,13 +1045,9 @@ void System::Exit() { } void System::ApplySettings() { - impl->RefreshTime(); + impl->RefreshTime(*this); if (IsPoweredOn()) { - if (Settings::values.custom_rtc_enabled) { - const s64 posix_time{Settings::values.custom_rtc.GetValue()}; - GetTimeManager().UpdateLocalSystemClockTime(posix_time); - } Renderer().RefreshBaseSettings(); } } diff --git a/src/core/core.h b/src/core/core.h index 4e470cb7f..6634098d4 100755 --- a/src/core/core.h +++ b/src/core/core.h @@ -73,10 +73,6 @@ namespace SM { class ServiceManager; } // namespace SM -namespace Time { -class TimeManager; -} // namespace Time - } // namespace Service namespace Tegra { @@ -381,9 +377,6 @@ public: [[nodiscard]] Service::Account::ProfileManager& GetProfileManager(); [[nodiscard]] const Service::Account::ProfileManager& GetProfileManager() const; - [[nodiscard]] Service::Time::TimeManager& GetTimeManager(); - [[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const; - [[nodiscard]] Core::Debugger& GetDebugger(); [[nodiscard]] const Core::Debugger& GetDebugger() const; diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 5cd970b4d..8918f46d3 100755 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -66,6 +66,7 @@ void CoreTiming::Initialize(std::function&& on_thread_init_) { event_fifo_id = 0; shutting_down = false; cpu_ticks = 0; + clock->Reset(); if (is_multicore) { timer_thread = std::make_unique(ThreadEntry, std::ref(*this)); } @@ -157,7 +158,7 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr& event_type, } } - for (auto h : to_remove) { + for (auto& h : to_remove) { event_queue.erase(h); } diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h index 4c8455c73..77094e2bf 100755 --- a/src/core/cpu_manager.h +++ b/src/core/cpu_manager.h @@ -64,7 +64,7 @@ public: return [this] { ShutdownThreadFunction(); }; } - void PreemptSingleCore(bool from_running_enviroment = true); + void PreemptSingleCore(bool from_running_environment = true); std::size_t CurrentCore() const { return current_core.load(); diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp index d31172985..65119c30f 100755 --- a/src/core/debugger/gdbstub.cpp +++ b/src/core/debugger/gdbstub.cpp @@ -559,28 +559,28 @@ void GDBStub::HandleVCont(std::string_view command, std::vector& } constexpr std::array, 22> MemoryStateNames{{ - {"----- Free -----", Kernel::Svc::MemoryState::Free}, - {"Io ", Kernel::Svc::MemoryState::Io}, - {"Static ", Kernel::Svc::MemoryState::Static}, - {"Code ", Kernel::Svc::MemoryState::Code}, - {"CodeData ", Kernel::Svc::MemoryState::CodeData}, - {"Normal ", Kernel::Svc::MemoryState::Normal}, - {"Shared ", Kernel::Svc::MemoryState::Shared}, - {"AliasCode ", Kernel::Svc::MemoryState::AliasCode}, - {"AliasCodeData ", Kernel::Svc::MemoryState::AliasCodeData}, - {"Ipc ", Kernel::Svc::MemoryState::Ipc}, - {"Stack ", Kernel::Svc::MemoryState::Stack}, - {"ThreadLocal ", Kernel::Svc::MemoryState::ThreadLocal}, - {"Transfered ", Kernel::Svc::MemoryState::Transfered}, - {"SharedTransfered", Kernel::Svc::MemoryState::SharedTransfered}, - {"SharedCode ", Kernel::Svc::MemoryState::SharedCode}, - {"Inaccessible ", Kernel::Svc::MemoryState::Inaccessible}, - {"NonSecureIpc ", Kernel::Svc::MemoryState::NonSecureIpc}, - {"NonDeviceIpc ", Kernel::Svc::MemoryState::NonDeviceIpc}, - {"Kernel ", Kernel::Svc::MemoryState::Kernel}, - {"GeneratedCode ", Kernel::Svc::MemoryState::GeneratedCode}, - {"CodeOut ", Kernel::Svc::MemoryState::CodeOut}, - {"Coverage ", Kernel::Svc::MemoryState::Coverage}, + {"----- Free ------", Kernel::Svc::MemoryState::Free}, + {"Io ", Kernel::Svc::MemoryState::Io}, + {"Static ", Kernel::Svc::MemoryState::Static}, + {"Code ", Kernel::Svc::MemoryState::Code}, + {"CodeData ", Kernel::Svc::MemoryState::CodeData}, + {"Normal ", Kernel::Svc::MemoryState::Normal}, + {"Shared ", Kernel::Svc::MemoryState::Shared}, + {"AliasCode ", Kernel::Svc::MemoryState::AliasCode}, + {"AliasCodeData ", Kernel::Svc::MemoryState::AliasCodeData}, + {"Ipc ", Kernel::Svc::MemoryState::Ipc}, + {"Stack ", Kernel::Svc::MemoryState::Stack}, + {"ThreadLocal ", Kernel::Svc::MemoryState::ThreadLocal}, + {"Transferred ", Kernel::Svc::MemoryState::Transferred}, + {"SharedTransferred", Kernel::Svc::MemoryState::SharedTransferred}, + {"SharedCode ", Kernel::Svc::MemoryState::SharedCode}, + {"Inaccessible ", Kernel::Svc::MemoryState::Inaccessible}, + {"NonSecureIpc ", Kernel::Svc::MemoryState::NonSecureIpc}, + {"NonDeviceIpc ", Kernel::Svc::MemoryState::NonDeviceIpc}, + {"Kernel ", Kernel::Svc::MemoryState::Kernel}, + {"GeneratedCode ", Kernel::Svc::MemoryState::GeneratedCode}, + {"CodeOut ", Kernel::Svc::MemoryState::CodeOut}, + {"Coverage ", Kernel::Svc::MemoryState::Coverage}, }}; static constexpr const char* GetMemoryStateName(Kernel::Svc::MemoryState state) { diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 8e5429391..4fb220dca 100755 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -26,7 +26,7 @@ #include "core/file_sys/vfs_vector.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/ns/language.h" -#include "core/hle/service/set/set.h" +#include "core/hle/service/set/settings_server.h" #include "core/loader/loader.h" #include "core/loader/nso.h" #include "core/memory/cheat_engine.h" diff --git a/src/core/file_sys/system_archive/time_zone_binary.cpp b/src/core/file_sys/system_archive/time_zone_binary.cpp index 7cf9f5f15..d4fb1d983 100755 --- a/src/core/file_sys/system_archive/time_zone_binary.cpp +++ b/src/core/file_sys/system_archive/time_zone_binary.cpp @@ -6,7 +6,6 @@ #include "common/swap.h" #include "core/file_sys/system_archive/time_zone_binary.h" #include "core/file_sys/vfs_vector.h" -#include "core/hle/service/time/time_zone_types.h" #include "nx_tzdb.h" diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h index 9e723259b..3a39b5c5f 100755 --- a/src/core/hle/kernel/k_memory_block.h +++ b/src/core/hle/kernel/k_memory_block.h @@ -81,12 +81,12 @@ enum class KMemoryState : u32 { ThreadLocal = static_cast(Svc::MemoryState::ThreadLocal) | FlagLinearMapped, - Transfered = static_cast(Svc::MemoryState::Transfered) | FlagsMisc | - FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc | - FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, + Transferred = static_cast(Svc::MemoryState::Transferred) | FlagsMisc | + FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc | + FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, - SharedTransfered = static_cast(Svc::MemoryState::SharedTransfered) | FlagsMisc | - FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, + SharedTransferred = static_cast(Svc::MemoryState::SharedTransferred) | FlagsMisc | + FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, SharedCode = static_cast(Svc::MemoryState::SharedCode) | FlagMapped | FlagReferenceCounted | FlagLinearMapped | FlagCanUseNonSecureIpc | @@ -130,8 +130,8 @@ static_assert(static_cast(KMemoryState::AliasCodeData) == 0x0FFFBD09); static_assert(static_cast(KMemoryState::Ipc) == 0x045C3C0A); static_assert(static_cast(KMemoryState::Stack) == 0x045C3C0B); static_assert(static_cast(KMemoryState::ThreadLocal) == 0x0400000C); -static_assert(static_cast(KMemoryState::Transfered) == 0x055C3C0D); -static_assert(static_cast(KMemoryState::SharedTransfered) == 0x045C380E); +static_assert(static_cast(KMemoryState::Transferred) == 0x055C3C0D); +static_assert(static_cast(KMemoryState::SharedTransferred) == 0x045C380E); static_assert(static_cast(KMemoryState::SharedCode) == 0x0440380F); static_assert(static_cast(KMemoryState::Inaccessible) == 0x00000010); static_assert(static_cast(KMemoryState::NonSecureIpc) == 0x045C3811); diff --git a/src/core/hle/kernel/k_page_table_base.cpp b/src/core/hle/kernel/k_page_table_base.cpp index 8c1549559..73fbda331 100755 --- a/src/core/hle/kernel/k_page_table_base.cpp +++ b/src/core/hle/kernel/k_page_table_base.cpp @@ -486,8 +486,8 @@ KProcessAddress KPageTableBase::GetRegionAddress(Svc::MemoryState state) const { case Svc::MemoryState::Shared: case Svc::MemoryState::AliasCode: case Svc::MemoryState::AliasCodeData: - case Svc::MemoryState::Transfered: - case Svc::MemoryState::SharedTransfered: + case Svc::MemoryState::Transferred: + case Svc::MemoryState::SharedTransferred: case Svc::MemoryState::SharedCode: case Svc::MemoryState::GeneratedCode: case Svc::MemoryState::CodeOut: @@ -522,8 +522,8 @@ size_t KPageTableBase::GetRegionSize(Svc::MemoryState state) const { case Svc::MemoryState::Shared: case Svc::MemoryState::AliasCode: case Svc::MemoryState::AliasCodeData: - case Svc::MemoryState::Transfered: - case Svc::MemoryState::SharedTransfered: + case Svc::MemoryState::Transferred: + case Svc::MemoryState::SharedTransferred: case Svc::MemoryState::SharedCode: case Svc::MemoryState::GeneratedCode: case Svc::MemoryState::CodeOut: @@ -564,8 +564,8 @@ bool KPageTableBase::CanContain(KProcessAddress addr, size_t size, Svc::MemorySt case Svc::MemoryState::AliasCodeData: case Svc::MemoryState::Stack: case Svc::MemoryState::ThreadLocal: - case Svc::MemoryState::Transfered: - case Svc::MemoryState::SharedTransfered: + case Svc::MemoryState::Transferred: + case Svc::MemoryState::SharedTransferred: case Svc::MemoryState::SharedCode: case Svc::MemoryState::GeneratedCode: case Svc::MemoryState::CodeOut: diff --git a/src/core/hle/kernel/k_transfer_memory.cpp b/src/core/hle/kernel/k_transfer_memory.cpp index 97b198870..6203aae6d 100755 --- a/src/core/hle/kernel/k_transfer_memory.cpp +++ b/src/core/hle/kernel/k_transfer_memory.cpp @@ -76,8 +76,8 @@ Result KTransferMemory::Map(KProcessAddress address, size_t size, Svc::MemoryPer // Map the memory. const KMemoryState state = (m_owner_perm == Svc::MemoryPermission::None) - ? KMemoryState::Transfered - : KMemoryState::SharedTransfered; + ? KMemoryState::Transferred + : KMemoryState::SharedTransferred; R_TRY(GetCurrentProcess(m_kernel).GetPageTable().MapPageGroup( address, *m_page_group, state, KMemoryPermission::UserReadWrite)); @@ -96,8 +96,8 @@ Result KTransferMemory::Unmap(KProcessAddress address, size_t size) { // Unmap the memory. const KMemoryState state = (m_owner_perm == Svc::MemoryPermission::None) - ? KMemoryState::Transfered - : KMemoryState::SharedTransfered; + ? KMemoryState::Transferred + : KMemoryState::SharedTransferred; R_TRY(GetCurrentProcess(m_kernel).GetPageTable().UnmapPageGroup(address, *m_page_group, state)); // Mark ourselves as unmapped. diff --git a/src/core/hle/kernel/svc/svc_transfer_memory.cpp b/src/core/hle/kernel/svc/svc_transfer_memory.cpp index 1f97121b3..671bca23f 100755 --- a/src/core/hle/kernel/svc/svc_transfer_memory.cpp +++ b/src/core/hle/kernel/svc/svc_transfer_memory.cpp @@ -90,7 +90,7 @@ Result MapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t add // Verify that the mapping is in range. R_UNLESS(GetCurrentProcess(system.Kernel()) .GetPageTable() - .CanContain(address, size, KMemoryState::Transfered), + .CanContain(address, size, KMemoryState::Transferred), ResultInvalidMemoryRegion); // Map the transfer memory. @@ -117,7 +117,7 @@ Result UnmapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t a // Verify that the mapping is in range. R_UNLESS(GetCurrentProcess(system.Kernel()) .GetPageTable() - .CanContain(address, size, KMemoryState::Transfered), + .CanContain(address, size, KMemoryState::Transferred), ResultInvalidMemoryRegion); // Unmap the transfer memory. diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h index f4d7011c4..254ba6565 100755 --- a/src/core/hle/kernel/svc_types.h +++ b/src/core/hle/kernel/svc_types.h @@ -27,8 +27,8 @@ enum class MemoryState : u32 { Ipc = 0x0A, Stack = 0x0B, ThreadLocal = 0x0C, - Transfered = 0x0D, - SharedTransfered = 0x0E, + Transferred = 0x0D, + SharedTransferred = 0x0E, SharedCode = 0x0F, Inaccessible = 0x10, NonSecureIpc = 0x11, diff --git a/src/core/hle/service/am/applets/applet_profile_select.h b/src/core/hle/service/am/applets/applet_profile_select.h index d6654751b..8cd1b7b7b 100755 --- a/src/core/hle/service/am/applets/applet_profile_select.h +++ b/src/core/hle/service/am/applets/applet_profile_select.h @@ -76,7 +76,7 @@ struct UiSettingsDisplayOptions { bool is_system_or_launcher; bool is_registration_permitted; bool show_skip_button; - bool aditional_select; + bool additional_select; bool show_user_selector; bool is_unqualified_user_selectable; }; diff --git a/src/core/hle/service/caps/caps_manager.cpp b/src/core/hle/service/caps/caps_manager.cpp index 96b225d5f..c68fe5152 100755 --- a/src/core/hle/service/caps/caps_manager.cpp +++ b/src/core/hle/service/caps/caps_manager.cpp @@ -10,8 +10,11 @@ #include "core/core.h" #include "core/hle/service/caps/caps_manager.h" #include "core/hle/service/caps/caps_result.h" -#include "core/hle/service/time/time_manager.h" -#include "core/hle/service/time/time_zone_content_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/service.h" +#include "core/hle/service/sm/sm.h" namespace Service::Capture { @@ -85,7 +88,7 @@ Result AlbumManager::GetAlbumFileList(std::vector& out_entries, Albu } Result AlbumManager::GetAlbumFileList(std::vector& out_entries, - ContentType contex_type, s64 start_posix_time, + ContentType content_type, s64 start_posix_time, s64 end_posix_time, u64 aruid) const { if (!is_mounted) { return ResultIsNotMounted; @@ -94,7 +97,7 @@ Result AlbumManager::GetAlbumFileList(std::vector& ou std::vector album_entries; const auto start_date = ConvertToAlbumDateTime(start_posix_time); const auto end_date = ConvertToAlbumDateTime(end_posix_time); - const auto result = GetAlbumFileList(album_entries, contex_type, start_date, end_date, aruid); + const auto result = GetAlbumFileList(album_entries, content_type, start_date, end_date, aruid); if (result.IsError()) { return result; @@ -113,14 +116,14 @@ Result AlbumManager::GetAlbumFileList(std::vector& ou } Result AlbumManager::GetAlbumFileList(std::vector& out_entries, - ContentType contex_type, AlbumFileDateTime start_date, + ContentType content_type, AlbumFileDateTime start_date, AlbumFileDateTime end_date, u64 aruid) const { if (!is_mounted) { return ResultIsNotMounted; } for (auto& [file_id, path] : album_files) { - if (file_id.type != contex_type) { + if (file_id.type != content_type) { continue; } if (file_id.date > start_date) { @@ -139,7 +142,7 @@ Result AlbumManager::GetAlbumFileList(std::vector& out_en .hash{}, .datetime = file_id.date, .storage = file_id.storage, - .content = contex_type, + .content = content_type, .unknown = 1, }; out_entries.push_back(entry); @@ -239,10 +242,15 @@ Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry, const ApplicationData& app_data, std::span image_data, u64 aruid) { const u64 title_id = system.GetApplicationProcessProgramID(); - const auto& user_clock = system.GetTimeManager().GetStandardUserSystemClockCore(); + + auto static_service = + system.ServiceManager().GetService("time:u", true); + + std::shared_ptr user_clock{}; + static_service->GetStandardUserSystemClock(user_clock); s64 posix_time{}; - Result result = user_clock.GetCurrentTime(system, posix_time); + auto result = user_clock->GetCurrentTime(posix_time); if (result.IsError()) { return result; @@ -257,10 +265,14 @@ Result AlbumManager::SaveEditedScreenShot(ApplicationAlbumEntry& out_entry, const ScreenShotAttribute& attribute, const AlbumFileId& file_id, std::span image_data) { - const auto& user_clock = system.GetTimeManager().GetStandardUserSystemClockCore(); + auto static_service = + system.ServiceManager().GetService("time:u", true); + + std::shared_ptr user_clock{}; + static_service->GetStandardUserSystemClock(user_clock); s64 posix_time{}; - Result result = user_clock.GetCurrentTime(system, posix_time); + auto result = user_clock->GetCurrentTime(posix_time); if (result.IsError()) { return result; @@ -455,19 +467,23 @@ Result AlbumManager::SaveImage(ApplicationAlbumEntry& out_entry, std::span("time:u", true); - time_zone_manager.ToCalendarTimeWithMyRules(posix_time, calendar_date); + std::shared_ptr timezone_service{}; + static_service->GetTimeZoneService(timezone_service); + + Service::PSC::Time::CalendarTime calendar_time{}; + Service::PSC::Time::CalendarAdditionalInfo additional_info{}; + timezone_service->ToCalendarTimeWithMyRule(calendar_time, additional_info, posix_time); return { - .year = calendar_date.time.year, - .month = calendar_date.time.month, - .day = calendar_date.time.day, - .hour = calendar_date.time.hour, - .minute = calendar_date.time.minute, - .second = calendar_date.time.second, + .year = calendar_time.year, + .month = calendar_time.month, + .day = calendar_time.day, + .hour = calendar_time.hour, + .minute = calendar_time.minute, + .second = calendar_time.second, .unique_id = 0, }; } diff --git a/src/core/hle/service/caps/caps_manager.h b/src/core/hle/service/caps/caps_manager.h index e20c70c7b..6fd34f589 100755 --- a/src/core/hle/service/caps/caps_manager.h +++ b/src/core/hle/service/caps/caps_manager.h @@ -45,10 +45,10 @@ public: Result GetAlbumFileList(std::vector& out_entries, AlbumStorage storage, u8 flags) const; Result GetAlbumFileList(std::vector& out_entries, - ContentType contex_type, s64 start_posix_time, s64 end_posix_time, + ContentType content_type, s64 start_posix_time, s64 end_posix_time, u64 aruid) const; Result GetAlbumFileList(std::vector& out_entries, - ContentType contex_type, AlbumFileDateTime start_date, + ContentType content_type, AlbumFileDateTime start_date, AlbumFileDateTime end_date, u64 aruid) const; Result GetAutoSavingStorage(bool& out_is_autosaving) const; Result LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& out_image_output, diff --git a/src/core/hle/service/caps/caps_result.h b/src/core/hle/service/caps/caps_result.h index c65e5fb9a..179ae4840 100755 --- a/src/core/hle/service/caps/caps_result.h +++ b/src/core/hle/service/caps/caps_result.h @@ -12,7 +12,7 @@ constexpr Result ResultUnknown5(ErrorModule::Capture, 5); constexpr Result ResultUnknown6(ErrorModule::Capture, 6); constexpr Result ResultUnknown7(ErrorModule::Capture, 7); constexpr Result ResultOutOfRange(ErrorModule::Capture, 8); -constexpr Result ResulInvalidTimestamp(ErrorModule::Capture, 12); +constexpr Result ResultInvalidTimestamp(ErrorModule::Capture, 12); constexpr Result ResultInvalidStorage(ErrorModule::Capture, 13); constexpr Result ResultInvalidFileContents(ErrorModule::Capture, 14); constexpr Result ResultIsNotMounted(ErrorModule::Capture, 21); diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index b6887070d..26b2c0673 100755 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp @@ -131,7 +131,7 @@ private: u8 is_favorite; u8 same_app; u8 same_app_played; - u8 arbitary_app_played; + u8 arbitrary_app_played; u64 group_id; }; static_assert(sizeof(SizedFriendFilter) == 0x10, "SizedFriendFilter is an invalid size"); diff --git a/src/core/hle/service/glue/glue.cpp b/src/core/hle/service/glue/glue.cpp index a996f58cf..66b684c30 100755 --- a/src/core/hle/service/glue/glue.cpp +++ b/src/core/hle/service/glue/glue.cpp @@ -8,6 +8,9 @@ #include "core/hle/service/glue/ectx.h" #include "core/hle/service/glue/glue.h" #include "core/hle/service/glue/notif.h" +#include "core/hle/service/glue/time/manager.h" +#include "core/hle/service/glue/time/static.h" +#include "core/hle/service/psc/time/common.h" #include "core/hle/service/server_manager.h" namespace Service::Glue { @@ -31,6 +34,22 @@ void LoopProcess(Core::System& system) { // Notification Services for application server_manager->RegisterNamedService("notif:a", std::make_shared(system)); + // Time + auto time = std::make_shared(system); + + server_manager->RegisterNamedService( + "time:u", + std::make_shared( + system, Service::PSC::Time::StaticServiceSetupInfo{0, 0, 0, 0, 0, 0}, time, "time:u")); + server_manager->RegisterNamedService( + "time:a", + std::make_shared( + system, Service::PSC::Time::StaticServiceSetupInfo{1, 1, 0, 1, 0, 0}, time, "time:a")); + server_manager->RegisterNamedService( + "time:r", + std::make_shared( + system, Service::PSC::Time::StaticServiceSetupInfo{0, 0, 0, 0, 1, 0}, time, "time:r")); + ServerManager::RunServer(std::move(server_manager)); } diff --git a/src/core/hle/service/glue/time/alarm_worker.cpp b/src/core/hle/service/glue/time/alarm_worker.cpp new file mode 100755 index 000000000..f549ed00a --- /dev/null +++ b/src/core/hle/service/glue/time/alarm_worker.cpp @@ -0,0 +1,82 @@ +// 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 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 { + 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 diff --git a/src/core/hle/service/glue/time/alarm_worker.h b/src/core/hle/service/glue/time/alarm_worker.h new file mode 100755 index 000000000..f269cffdb --- /dev/null +++ b/src/core/hle/service/glue/time/alarm_worker.h @@ -0,0 +1,53 @@ +// 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 time_m); + + Kernel::KEvent& 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 m_time_m; + + Kernel::KEvent* m_event{}; + Kernel::KEvent* m_timer_event{}; + std::shared_ptr m_timer_timing_event; + StandardSteadyClockResource& m_steady_clock_resource; +}; + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/file_timestamp_worker.cpp b/src/core/hle/service/glue/time/file_timestamp_worker.cpp new file mode 100755 index 000000000..5a6309549 --- /dev/null +++ b/src/core/hle/service/glue/time/file_timestamp_worker.cpp @@ -0,0 +1,23 @@ +// 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 diff --git a/src/core/hle/service/glue/time/file_timestamp_worker.h b/src/core/hle/service/glue/time/file_timestamp_worker.h new file mode 100755 index 000000000..5f8b9b049 --- /dev/null +++ b/src/core/hle/service/glue/time/file_timestamp_worker.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#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 m_system_clock{}; + std::shared_ptr m_time_zone{}; + bool m_initialized{}; +}; + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/manager.cpp b/src/core/hle/service/glue/time/manager.cpp new file mode 100755 index 000000000..1149cc785 --- /dev/null +++ b/src/core/hle/service/glue/time/manager.cpp @@ -0,0 +1,242 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#include "core/core.h" +#include "core/core_timing.h" + +#include "core/file_sys/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 { + +template +T GetSettingsItemValue(std::shared_ptr& set_sys, + const char* category, const char* name) { + std::vector interval_buf; + auto res = set_sys->GetSettingsItemValue(interval_buf, category, name); + ASSERT(res == ResultSuccess); + + T v{}; + std::memcpy(&v, interval_buf.data(), sizeof(T)); + return v; +} + +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 MonthStartDayOfYear{ + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, + }; + + s16 month_s16{calendar.month}; + s8 month{static_cast(((month_s16 * 43) & ~std::numeric_limits::max()) + + ((month_s16 * 43) >> 9))}; + s8 month_index{static_cast(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& set_sys) { + Service::PSC::Time::CalendarTime calendar{ + .year = GetSettingsItemValue(set_sys, "time", "standard_user_clock_initial_year"), + .month = 1, + .day = 1, + .hour = 0, + .minute = 0, + .second = 0, + }; + return CalendarTimeToEpoch(calendar); +} +} // 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("time:m", true); + + auto res = m_time_m->GetStaticServiceAsServiceManager(m_time_sm); + ASSERT(res == ResultSuccess); + + m_set_sys = + system.ServiceManager().GetService("set:sys", true); + + ResetTimeZoneBinary(); + 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 this clock should initialise with this epoch time, and be updated somewhere else on + // first boot, but I haven't been able to find that point (likely via ntc's auto correct as it's + // defaulted to be enabled), and to get correct times we need to initialise with the current + // time instead. + 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); + + auto network_accuracy_m{GetSettingsItemValue( + m_set_sys, "time", "standard_network_clock_sufficient_accuracy_minutes")}; + auto one_minute_ns{ + std::chrono::duration_cast(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(automatic_correction_time_point, + is_automatic_correction_enabled); + 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) + } + } +} + +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::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{ + GetSettingsItemValue(m_set_sys, "time", "standard_steady_clock_test_offset_minutes")}; + auto one_minute_ns{ + std::chrono::duration_cast(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(clock_source_id, m_steady_clock_resource.GetTime(), + external_steady_clock_internal_offset_ns, + standard_steady_clock_test_offset_ns, + reset_detected); + ASSERT(res == ResultSuccess); + R_SUCCEED(); +} + +Result TimeManager::SetupTimeZoneServiceCore() { + Service::PSC::Time::LocationName name{}; + auto res = m_set_sys->GetDeviceTimeZoneLocationName(name); + ASSERT(res == ResultSuccess); + + 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 rule_buffer{}; + size_t rule_size{}; + res = GetTimeZoneRule(rule_buffer, rule_size, name); + ASSERT(res == ResultSuccess); + + res = m_time_m->SetupTimeZoneServiceCore(name, time_point, rule_version, location_count, + rule_buffer); + ASSERT(res == ResultSuccess); + + R_SUCCEED(); +} + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/manager.h b/src/core/hle/service/glue/time/manager.h new file mode 100755 index 000000000..a46ec6364 --- /dev/null +++ b/src/core/hle/service/glue/time/manager.h @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#include "common/common_types.h" +#include "core/file_sys/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); + + std::shared_ptr m_set_sys; + + std::shared_ptr m_time_m{}; + std::shared_ptr 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 diff --git a/src/core/hle/service/glue/time/pm_state_change_handler.cpp b/src/core/hle/service/glue/time/pm_state_change_handler.cpp new file mode 100755 index 000000000..7470fb225 --- /dev/null +++ b/src/core/hle/service/glue/time/pm_state_change_handler.cpp @@ -0,0 +1,13 @@ +// 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 diff --git a/src/core/hle/service/glue/time/pm_state_change_handler.h b/src/core/hle/service/glue/time/pm_state_change_handler.h new file mode 100755 index 000000000..27d9f7872 --- /dev/null +++ b/src/core/hle/service/glue/time/pm_state_change_handler.h @@ -0,0 +1,18 @@ +// 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 diff --git a/src/core/hle/service/glue/time/standard_steady_clock_resource.cpp b/src/core/hle/service/glue/time/standard_steady_clock_resource.cpp new file mode 100755 index 000000000..fd87b195d --- /dev/null +++ b/src/core/hle/service/glue/time/standard_steady_clock_resource.cpp @@ -0,0 +1,123 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#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) { + if (Settings::values.custom_rtc_enabled) { + out_time_s = Settings::values.custom_rtc.GetValue(); + } else { + out_time_s = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + } + 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::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::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::milliseconds(1)) + .count()); + } +} + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/standard_steady_clock_resource.h b/src/core/hle/service/glue/time/standard_steady_clock_resource.h new file mode 100755 index 000000000..978d6b63b --- /dev/null +++ b/src/core/hle/service/glue/time/standard_steady_clock_resource.h @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#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 diff --git a/src/core/hle/service/glue/time/static.cpp b/src/core/hle/service/glue/time/static.cpp new file mode 100755 index 000000000..945b3cb88 --- /dev/null +++ b/src/core/hle/service/glue/time/static.cpp @@ -0,0 +1,447 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#include "core/core.h" +#include "core/hle/kernel/k_shared_memory.h" +#include "core/hle/kernel/svc.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 { +namespace { +template +T GetSettingsItemValue(std::shared_ptr& set_sys, + const char* category, const char* name) { + std::vector interval_buf; + auto res = set_sys->GetSettingsItemValue(interval_buf, category, name); + ASSERT(res == ResultSuccess); + + T v{}; + std::memcpy(&v, interval_buf.data(), sizeof(T)); + return v; +} +} // namespace + +StaticService::StaticService(Core::System& system_, + Service::PSC::Time::StaticServiceSetupInfo setup_info, + std::shared_ptr 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, &StaticService::Handle_GetStandardUserSystemClock, "GetStandardUserSystemClock"}, + {1, &StaticService::Handle_GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"}, + {2, &StaticService::Handle_GetStandardSteadyClock, "GetStandardSteadyClock"}, + {3, &StaticService::Handle_GetTimeZoneService, "GetTimeZoneService"}, + {4, &StaticService::Handle_GetStandardLocalSystemClock, "GetStandardLocalSystemClock"}, + {5, &StaticService::Handle_GetEphemeralNetworkSystemClock, "GetEphemeralNetworkSystemClock"}, + {20, &StaticService::Handle_GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}, + {50, &StaticService::Handle_SetStandardSteadyClockInternalOffset, "SetStandardSteadyClockInternalOffset"}, + {51, &StaticService::Handle_GetStandardSteadyClockRtcValue, "GetStandardSteadyClockRtcValue"}, + {100, &StaticService::Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled, "IsStandardUserSystemClockAutomaticCorrectionEnabled"}, + {101, &StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled, "SetStandardUserSystemClockAutomaticCorrectionEnabled"}, + {102, &StaticService::Handle_GetStandardUserSystemClockInitialYear, "GetStandardUserSystemClockInitialYear"}, + {200, &StaticService::Handle_IsStandardNetworkSystemClockAccuracySufficient, "IsStandardNetworkSystemClockAccuracySufficient"}, + {201, &StaticService::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"}, + {300, &StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint, "CalculateMonotonicSystemClockBaseTimePoint"}, + {400, &StaticService::Handle_GetClockSnapshot, "GetClockSnapshot"}, + {401, &StaticService::Handle_GetClockSnapshotFromSystemClockContext, "GetClockSnapshotFromSystemClockContext"}, + {500, &StaticService::Handle_CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"}, + {501, &StaticService::Handle_CalculateSpanBetween, "CalculateSpanBetween"}, + }; + // clang-format on + + RegisterHandlers(functions); + + m_set_sys = + m_system.ServiceManager().GetService("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); +} + +void StaticService::Handle_GetStandardUserSystemClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr service{}; + auto res = GetStandardUserSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface(std::move(service)); +} + +void StaticService::Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr service{}; + auto res = GetStandardNetworkSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface(std::move(service)); +} + +void StaticService::Handle_GetStandardSteadyClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr service{}; + auto res = GetStandardSteadyClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface(std::move(service)); +} + +void StaticService::Handle_GetTimeZoneService(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr service{}; + auto res = GetTimeZoneService(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface(std::move(service)); +} + +void StaticService::Handle_GetStandardLocalSystemClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr service{}; + auto res = GetStandardLocalSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface(std::move(service)); +} + +void StaticService::Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr service{}; + auto res = GetEphemeralNetworkSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface(std::move(service)); +} + +void StaticService::Handle_GetSharedMemoryNativeHandle(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Kernel::KSharedMemory* shared_memory{}; + auto res = GetSharedMemoryNativeHandle(&shared_memory); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(res); + rb.PushCopyObjects(shared_memory); +} + +void StaticService::Handle_SetStandardSteadyClockInternalOffset(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto offset_ns{rp.Pop()}; + + auto res = SetStandardSteadyClockInternalOffset(offset_ns); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void StaticService::Handle_GetStandardSteadyClockRtcValue(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + s64 rtc_value{}; + auto res = GetStandardSteadyClockRtcValue(rtc_value); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(rtc_value); +} + +void StaticService::Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + bool is_enabled{}; + auto res = IsStandardUserSystemClockAutomaticCorrectionEnabled(is_enabled); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(is_enabled); +} + +void StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto automatic_correction{rp.Pop()}; + + auto res = SetStandardUserSystemClockAutomaticCorrectionEnabled(automatic_correction); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void StaticService::Handle_GetStandardUserSystemClockInitialYear(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + s32 initial_year{}; + auto res = GetStandardUserSystemClockInitialYear(initial_year); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(initial_year); +} + +void StaticService::Handle_IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + bool is_sufficient{}; + auto res = IsStandardNetworkSystemClockAccuracySufficient(is_sufficient); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(is_sufficient); +} + +void StaticService::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Service::PSC::Time::SteadyClockTimePoint time_point{}; + auto res = GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(time_point); + + IPC::ResponseBuilder rb{ctx, + 2 + sizeof(Service::PSC::Time::SteadyClockTimePoint) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw(time_point); +} + +void StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto context{rp.PopRaw()}; + + s64 time{}; + auto res = CalculateMonotonicSystemClockBaseTimePoint(time, context); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(time); +} + +void StaticService::Handle_GetClockSnapshot(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto type{rp.PopEnum()}; + + Service::PSC::Time::ClockSnapshot snapshot{}; + auto res = GetClockSnapshot(snapshot, type); + + ctx.WriteBuffer(snapshot); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void StaticService::Handle_GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto user_context{rp.PopRaw()}; + auto network_context{rp.PopRaw()}; + auto clock_type{rp.PopEnum()}; + + Service::PSC::Time::ClockSnapshot snapshot{}; + auto res = + GetClockSnapshotFromSystemClockContext(snapshot, user_context, network_context, clock_type); + + ctx.WriteBuffer(snapshot); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void StaticService::Handle_CalculateStandardUserSystemClockDifferenceByUser( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Service::PSC::Time::ClockSnapshot a{}; + Service::PSC::Time::ClockSnapshot b{}; + + auto a_buffer{ctx.ReadBuffer(0)}; + auto b_buffer{ctx.ReadBuffer(1)}; + + std::memcpy(&a, a_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot)); + std::memcpy(&b, b_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot)); + + s64 difference{}; + auto res = CalculateStandardUserSystemClockDifferenceByUser(difference, a, b); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(difference); +} + +void StaticService::Handle_CalculateSpanBetween(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Service::PSC::Time::ClockSnapshot a{}; + Service::PSC::Time::ClockSnapshot b{}; + + auto a_buffer{ctx.ReadBuffer(0)}; + auto b_buffer{ctx.ReadBuffer(1)}; + + std::memcpy(&a, a_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot)); + std::memcpy(&b, b_buffer.data(), sizeof(Service::PSC::Time::ClockSnapshot)); + + s64 time{}; + auto res = CalculateSpanBetween(time, a, b); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(time); +} + +// =============================== Implementations =========================== + +Result StaticService::GetStandardUserSystemClock( + std::shared_ptr& out_service) { + R_RETURN(m_wrapped_service->GetStandardUserSystemClock(out_service)); +} + +Result StaticService::GetStandardNetworkSystemClock( + std::shared_ptr& out_service) { + R_RETURN(m_wrapped_service->GetStandardNetworkSystemClock(out_service)); +} + +Result StaticService::GetStandardSteadyClock( + std::shared_ptr& out_service) { + R_RETURN(m_wrapped_service->GetStandardSteadyClock(out_service)); +} + +Result StaticService::GetTimeZoneService(std::shared_ptr& out_service) { + out_service = std::make_shared(m_system, m_file_timestamp_worker, + m_setup_info.can_write_timezone_device_location, + m_time_zone); + R_SUCCEED(); +} + +Result StaticService::GetStandardLocalSystemClock( + std::shared_ptr& out_service) { + R_RETURN(m_wrapped_service->GetStandardLocalSystemClock(out_service)); +} + +Result StaticService::GetEphemeralNetworkSystemClock( + std::shared_ptr& out_service) { + R_RETURN(m_wrapped_service->GetEphemeralNetworkSystemClock(out_service)); +} + +Result StaticService::GetSharedMemoryNativeHandle(Kernel::KSharedMemory** out_shared_memory) { + R_RETURN(m_wrapped_service->GetSharedMemoryNativeHandle(out_shared_memory)); +} + +Result StaticService::SetStandardSteadyClockInternalOffset(s64 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::seconds(1)).count())); +} + +Result StaticService::GetStandardSteadyClockRtcValue(s64& out_rtc_value) { + R_RETURN(m_standard_steady_clock_resource.GetRtcTimeInSeconds(out_rtc_value)); +} + +Result StaticService::IsStandardUserSystemClockAutomaticCorrectionEnabled( + bool& out_automatic_correction) { + R_RETURN(m_wrapped_service->IsStandardUserSystemClockAutomaticCorrectionEnabled( + out_automatic_correction)); +} + +Result StaticService::SetStandardUserSystemClockAutomaticCorrectionEnabled( + bool automatic_correction) { + R_RETURN(m_wrapped_service->SetStandardUserSystemClockAutomaticCorrectionEnabled( + automatic_correction)); +} + +Result StaticService::GetStandardUserSystemClockInitialYear(s32& out_year) { + out_year = GetSettingsItemValue(m_set_sys, "time", "standard_user_clock_initial_year"); + R_SUCCEED(); +} + +Result StaticService::IsStandardNetworkSystemClockAccuracySufficient(bool& out_is_sufficient) { + R_RETURN(m_wrapped_service->IsStandardNetworkSystemClockAccuracySufficient(out_is_sufficient)); +} + +Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( + Service::PSC::Time::SteadyClockTimePoint& out_time_point) { + R_RETURN(m_wrapped_service->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( + out_time_point)); +} + +Result StaticService::CalculateMonotonicSystemClockBaseTimePoint( + s64& out_time, Service::PSC::Time::SystemClockContext& context) { + R_RETURN(m_wrapped_service->CalculateMonotonicSystemClockBaseTimePoint(out_time, context)); +} + +Result StaticService::GetClockSnapshot(Service::PSC::Time::ClockSnapshot& out_snapshot, + Service::PSC::Time::TimeType type) { + R_RETURN(m_wrapped_service->GetClockSnapshot(out_snapshot, type)); +} + +Result StaticService::GetClockSnapshotFromSystemClockContext( + Service::PSC::Time::ClockSnapshot& out_snapshot, + Service::PSC::Time::SystemClockContext& user_context, + Service::PSC::Time::SystemClockContext& network_context, Service::PSC::Time::TimeType type) { + R_RETURN(m_wrapped_service->GetClockSnapshotFromSystemClockContext(out_snapshot, user_context, + network_context, type)); +} + +Result StaticService::CalculateStandardUserSystemClockDifferenceByUser( + s64& out_time, Service::PSC::Time::ClockSnapshot& a, Service::PSC::Time::ClockSnapshot& b) { + R_RETURN(m_wrapped_service->CalculateSpanBetween(out_time, a, b)); +} + +Result StaticService::CalculateSpanBetween(s64& out_time, Service::PSC::Time::ClockSnapshot& a, + Service::PSC::Time::ClockSnapshot& b) { + R_RETURN(m_wrapped_service->CalculateSpanBetween(out_time, a, b)); +} + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/static.h b/src/core/hle/service/glue/time/static.h new file mode 100755 index 000000000..75fe4e2cd --- /dev/null +++ b/src/core/hle/service/glue/time/static.h @@ -0,0 +1,110 @@ +// 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/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 { +public: + explicit StaticService(Core::System& system, + Service::PSC::Time::StaticServiceSetupInfo setup_info, + std::shared_ptr time, const char* name); + + ~StaticService() override = default; + + Result GetStandardUserSystemClock( + std::shared_ptr& out_service); + Result GetStandardNetworkSystemClock( + std::shared_ptr& out_service); + Result GetStandardSteadyClock(std::shared_ptr& out_service); + Result GetTimeZoneService(std::shared_ptr& out_service); + Result GetStandardLocalSystemClock( + std::shared_ptr& out_service); + Result GetEphemeralNetworkSystemClock( + std::shared_ptr& out_service); + Result GetSharedMemoryNativeHandle(Kernel::KSharedMemory** out_shared_memory); + Result SetStandardSteadyClockInternalOffset(s64 offset); + Result GetStandardSteadyClockRtcValue(s64& out_rtc_value); + Result IsStandardUserSystemClockAutomaticCorrectionEnabled(bool& out_automatic_correction); + Result SetStandardUserSystemClockAutomaticCorrectionEnabled(bool automatic_correction); + Result GetStandardUserSystemClockInitialYear(s32& out_year); + Result IsStandardNetworkSystemClockAccuracySufficient(bool& out_is_sufficient); + Result GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( + Service::PSC::Time::SteadyClockTimePoint& out_time_point); + Result CalculateMonotonicSystemClockBaseTimePoint( + s64& out_time, Service::PSC::Time::SystemClockContext& context); + Result GetClockSnapshot(Service::PSC::Time::ClockSnapshot& out_snapshot, + Service::PSC::Time::TimeType type); + Result GetClockSnapshotFromSystemClockContext( + Service::PSC::Time::ClockSnapshot& out_snapshot, + Service::PSC::Time::SystemClockContext& user_context, + Service::PSC::Time::SystemClockContext& network_context, Service::PSC::Time::TimeType type); + Result CalculateStandardUserSystemClockDifferenceByUser(s64& out_time, + Service::PSC::Time::ClockSnapshot& a, + Service::PSC::Time::ClockSnapshot& b); + Result CalculateSpanBetween(s64& out_time, Service::PSC::Time::ClockSnapshot& a, + Service::PSC::Time::ClockSnapshot& b); + +private: + Result GetClockSnapshotImpl(Service::PSC::Time::ClockSnapshot& out_snapshot, + Service::PSC::Time::SystemClockContext& user_context, + Service::PSC::Time::SystemClockContext& network_context, + Service::PSC::Time::TimeType type); + + void Handle_GetStandardUserSystemClock(HLERequestContext& ctx); + void Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx); + void Handle_GetStandardSteadyClock(HLERequestContext& ctx); + void Handle_GetTimeZoneService(HLERequestContext& ctx); + void Handle_GetStandardLocalSystemClock(HLERequestContext& ctx); + void Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx); + void Handle_GetSharedMemoryNativeHandle(HLERequestContext& ctx); + void Handle_SetStandardSteadyClockInternalOffset(HLERequestContext& ctx); + void Handle_GetStandardSteadyClockRtcValue(HLERequestContext& ctx); + void Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx); + void Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx); + void Handle_GetStandardUserSystemClockInitialYear(HLERequestContext& ctx); + void Handle_IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx); + void Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx); + void Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx); + void Handle_GetClockSnapshot(HLERequestContext& ctx); + void Handle_GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx); + void Handle_CalculateStandardUserSystemClockDifferenceByUser(HLERequestContext& ctx); + void Handle_CalculateSpanBetween(HLERequestContext& ctx); + + Core::System& m_system; + + std::shared_ptr m_set_sys; + std::shared_ptr m_time_m; + std::shared_ptr m_wrapped_service; + + Service::PSC::Time::StaticServiceSetupInfo m_setup_info; + std::shared_ptr m_time_sm; + std::shared_ptr m_time_zone; + FileTimestampWorker& m_file_timestamp_worker; + StandardSteadyClockResource& m_standard_steady_clock_resource; +}; +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/time_zone.cpp b/src/core/hle/service/glue/time/time_zone.cpp new file mode 100755 index 000000000..503c327dd --- /dev/null +++ b/src/core/hle/service/glue/time/time_zone.cpp @@ -0,0 +1,377 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#include "core/core.h" +#include "core/hle/kernel/svc.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::ListType g_list_nodes{}; +} // namespace + +TimeZoneService::TimeZoneService( + Core::System& system_, FileTimestampWorker& file_timestamp_worker, + bool can_write_timezone_device_location, + std::shared_ptr 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, &TimeZoneService::Handle_GetDeviceLocationName, "GetDeviceLocationName"}, + {1, &TimeZoneService::Handle_SetDeviceLocationName, "SetDeviceLocationName"}, + {2, &TimeZoneService::Handle_GetTotalLocationNameCount, "GetTotalLocationNameCount"}, + {3, &TimeZoneService::Handle_LoadLocationNameList, "LoadLocationNameList"}, + {4, &TimeZoneService::Handle_LoadTimeZoneRule, "LoadTimeZoneRule"}, + {5, &TimeZoneService::Handle_GetTimeZoneRuleVersion, "GetTimeZoneRuleVersion"}, + {6, &TimeZoneService::Handle_GetDeviceLocationNameAndUpdatedTime, "GetDeviceLocationNameAndUpdatedTime"}, + {7, &TimeZoneService::Handle_SetDeviceLocationNameWithTimeZoneRule, "SetDeviceLocationNameWithTimeZoneRule"}, + {8, &TimeZoneService::Handle_ParseTimeZoneBinary, "ParseTimeZoneBinary"}, + {20, &TimeZoneService::Handle_GetDeviceLocationNameOperationEventReadableHandle, "GetDeviceLocationNameOperationEventReadableHandle"}, + {100, &TimeZoneService::Handle_ToCalendarTime, "ToCalendarTime"}, + {101, &TimeZoneService::Handle_ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"}, + {201, &TimeZoneService::Handle_ToPosixTime, "ToPosixTime"}, + {202, &TimeZoneService::Handle_ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"}, + }; + // clang-format on + RegisterHandlers(functions); + + g_list_nodes.clear(); + m_set_sys = + m_system.ServiceManager().GetService("set:sys", true); +} + +TimeZoneService::~TimeZoneService() = default; + +void TimeZoneService::Handle_GetDeviceLocationName(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Service::PSC::Time::LocationName name{}; + auto res = GetDeviceLocationName(name); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::LocationName) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw(name); +} + +void TimeZoneService::Handle_SetDeviceLocationName(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto name{rp.PopRaw()}; + + auto res = SetDeviceLocation(name); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void TimeZoneService::Handle_GetTotalLocationNameCount(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + u32 count{}; + auto res = GetTotalLocationNameCount(count); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(count); +} + +void TimeZoneService::Handle_LoadLocationNameList(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto index{rp.Pop()}; + + auto max_names{ctx.GetWriteBufferSize() / sizeof(Service::PSC::Time::LocationName)}; + + std::vector names{}; + u32 count{}; + auto res = LoadLocationNameList(count, names, max_names, index); + + ctx.WriteBuffer(names); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(count); +} + +void TimeZoneService::Handle_LoadTimeZoneRule(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto name{rp.PopRaw()}; + + Tz::Rule rule{}; + auto res = LoadTimeZoneRule(rule, name); + + ctx.WriteBuffer(rule); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void TimeZoneService::Handle_GetTimeZoneRuleVersion(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Service::PSC::Time::RuleVersion rule_version{}; + auto res = GetTimeZoneRuleVersion(rule_version); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::RuleVersion) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw(rule_version); +} + +void TimeZoneService::Handle_GetDeviceLocationNameAndUpdatedTime(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Service::PSC::Time::LocationName name{}; + Service::PSC::Time::SteadyClockTimePoint time_point{}; + auto res = GetDeviceLocationNameAndUpdatedTime(time_point, name); + + IPC::ResponseBuilder rb{ctx, + 2 + (sizeof(Service::PSC::Time::LocationName) / sizeof(u32)) + + (sizeof(Service::PSC::Time::SteadyClockTimePoint) / sizeof(u32))}; + rb.Push(res); + rb.PushRaw(name); + rb.PushRaw(time_point); +} + +void TimeZoneService::Handle_SetDeviceLocationNameWithTimeZoneRule(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + auto res = SetDeviceLocationNameWithTimeZoneRule(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void TimeZoneService::Handle_ParseTimeZoneBinary(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(Service::PSC::Time::ResultNotImplemented); +} + +void TimeZoneService::Handle_GetDeviceLocationNameOperationEventReadableHandle( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Kernel::KEvent* event{}; + auto res = GetDeviceLocationNameOperationEventReadableHandle(&event); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(res); + rb.PushCopyObjects(event->GetReadableEvent()); +} + +void TimeZoneService::Handle_ToCalendarTime(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto time{rp.Pop()}; + + auto rule_buffer{ctx.ReadBuffer()}; + Tz::Rule rule{}; + std::memcpy(&rule, rule_buffer.data(), sizeof(Tz::Rule)); + + Service::PSC::Time::CalendarTime calendar_time{}; + Service::PSC::Time::CalendarAdditionalInfo additional_info{}; + auto res = ToCalendarTime(calendar_time, additional_info, time, rule); + + IPC::ResponseBuilder rb{ctx, + 2 + (sizeof(Service::PSC::Time::CalendarTime) / sizeof(u32)) + + (sizeof(Service::PSC::Time::CalendarAdditionalInfo) / sizeof(u32))}; + rb.Push(res); + rb.PushRaw(calendar_time); + rb.PushRaw(additional_info); +} + +void TimeZoneService::Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto time{rp.Pop()}; + + LOG_DEBUG(Service_Time, "called. time={}", time); + + Service::PSC::Time::CalendarTime calendar_time{}; + Service::PSC::Time::CalendarAdditionalInfo additional_info{}; + auto res = ToCalendarTimeWithMyRule(calendar_time, additional_info, time); + + IPC::ResponseBuilder rb{ctx, + 2 + (sizeof(Service::PSC::Time::CalendarTime) / sizeof(u32)) + + (sizeof(Service::PSC::Time::CalendarAdditionalInfo) / sizeof(u32))}; + rb.Push(res); + rb.PushRaw(calendar_time); + rb.PushRaw(additional_info); +} + +void TimeZoneService::Handle_ToPosixTime(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto calendar{rp.PopRaw()}; + + LOG_DEBUG(Service_Time, "called. calendar year {} month {} day {} hour {} minute {} second {}", + calendar.year, calendar.month, calendar.day, calendar.hour, calendar.minute, + calendar.second); + + auto binary{ctx.ReadBuffer()}; + + Tz::Rule rule{}; + std::memcpy(&rule, binary.data(), sizeof(Tz::Rule)); + + u32 count{}; + std::array times{}; + u32 times_count{static_cast(ctx.GetWriteBufferSize() / sizeof(s64))}; + + auto res = ToPosixTime(count, times, times_count, calendar, rule); + + ctx.WriteBuffer(times); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(count); +} + +void TimeZoneService::Handle_ToPosixTimeWithMyRule(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto calendar{rp.PopRaw()}; + + u32 count{}; + std::array times{}; + u32 times_count{static_cast(ctx.GetWriteBufferSize() / sizeof(s64))}; + + auto res = ToPosixTimeWithMyRule(count, times, times_count, calendar); + + ctx.WriteBuffer(times); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(count); +} + +// =============================== Implementations =========================== + +Result TimeZoneService::GetDeviceLocationName(Service::PSC::Time::LocationName& out_location_name) { + R_RETURN(m_wrapped_service->GetDeviceLocationName(out_location_name)); +} + +Result TimeZoneService::SetDeviceLocation(Service::PSC::Time::LocationName& 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 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(time_point, name)); + + 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(u32& out_count) { + R_RETURN(m_wrapped_service->GetTotalLocationNameCount(out_count)); +} + +Result TimeZoneService::LoadLocationNameList( + u32& out_count, std::vector& out_names, size_t max_names, + u32 index) { + std::scoped_lock l{m_mutex}; + R_RETURN(GetTimeZoneLocationList(out_count, out_names, max_names, index)); +} + +Result TimeZoneService::LoadTimeZoneRule(Tz::Rule& out_rule, + Service::PSC::Time::LocationName& name) { + std::scoped_lock l{m_mutex}; + std::span binary{}; + size_t binary_size{}; + R_TRY(GetTimeZoneRule(binary, binary_size, name)) + R_RETURN(m_wrapped_service->ParseTimeZoneBinary(out_rule, binary)); +} + +Result TimeZoneService::GetTimeZoneRuleVersion(Service::PSC::Time::RuleVersion& out_rule_version) { + R_RETURN(m_wrapped_service->GetTimeZoneRuleVersion(out_rule_version)); +} + +Result TimeZoneService::GetDeviceLocationNameAndUpdatedTime( + Service::PSC::Time::SteadyClockTimePoint& out_time_point, + Service::PSC::Time::LocationName& location_name) { + R_RETURN(m_wrapped_service->GetDeviceLocationNameAndUpdatedTime(out_time_point, location_name)); +} + +Result TimeZoneService::SetDeviceLocationNameWithTimeZoneRule() { + R_UNLESS(m_can_write_timezone_device_location, Service::PSC::Time::ResultPermissionDenied); + R_RETURN(Service::PSC::Time::ResultNotImplemented); +} + +Result TimeZoneService::GetDeviceLocationNameOperationEventReadableHandle( + Kernel::KEvent** out_event) { + 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; + R_SUCCEED(); +} + +Result TimeZoneService::ToCalendarTime( + Service::PSC::Time::CalendarTime& out_calendar_time, + Service::PSC::Time::CalendarAdditionalInfo& out_additional_info, s64 time, Tz::Rule& rule) { + R_RETURN(m_wrapped_service->ToCalendarTime(out_calendar_time, out_additional_info, time, rule)); +} + +Result TimeZoneService::ToCalendarTimeWithMyRule( + Service::PSC::Time::CalendarTime& out_calendar_time, + Service::PSC::Time::CalendarAdditionalInfo& out_additional_info, s64 time) { + R_RETURN( + m_wrapped_service->ToCalendarTimeWithMyRule(out_calendar_time, out_additional_info, time)); +} + +Result TimeZoneService::ToPosixTime(u32& out_count, std::span out_times, + u32 out_times_count, + Service::PSC::Time::CalendarTime& calendar_time, + Tz::Rule& rule) { + R_RETURN( + m_wrapped_service->ToPosixTime(out_count, out_times, out_times_count, calendar_time, rule)); +} + +Result TimeZoneService::ToPosixTimeWithMyRule(u32& out_count, std::span out_times, + u32 out_times_count, + Service::PSC::Time::CalendarTime& calendar_time) { + R_RETURN(m_wrapped_service->ToPosixTimeWithMyRule(out_count, out_times, out_times_count, + calendar_time)); +} + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/time_zone.h b/src/core/hle/service/glue/time/time_zone.h new file mode 100755 index 000000000..3c8ae4bf8 --- /dev/null +++ b/src/core/hle/service/glue/time/time_zone.h @@ -0,0 +1,95 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include + +#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 { +public: + explicit TimeZoneService( + Core::System& system, FileTimestampWorker& file_timestamp_worker, + bool can_write_timezone_device_location, + std::shared_ptr time_zone_service); + + ~TimeZoneService() override; + + Result GetDeviceLocationName(Service::PSC::Time::LocationName& out_location_name); + Result SetDeviceLocation(Service::PSC::Time::LocationName& location_name); + Result GetTotalLocationNameCount(u32& out_count); + Result LoadLocationNameList(u32& out_count, + std::vector& out_names, + size_t max_names, u32 index); + Result LoadTimeZoneRule(Tz::Rule& out_rule, Service::PSC::Time::LocationName& name); + Result GetTimeZoneRuleVersion(Service::PSC::Time::RuleVersion& out_rule_version); + Result GetDeviceLocationNameAndUpdatedTime( + Service::PSC::Time::SteadyClockTimePoint& out_time_point, + Service::PSC::Time::LocationName& location_name); + Result SetDeviceLocationNameWithTimeZoneRule(); + Result GetDeviceLocationNameOperationEventReadableHandle(Kernel::KEvent** out_event); + Result ToCalendarTime(Service::PSC::Time::CalendarTime& out_calendar_time, + Service::PSC::Time::CalendarAdditionalInfo& out_additional_info, s64 time, + Tz::Rule& rule); + Result ToCalendarTimeWithMyRule(Service::PSC::Time::CalendarTime& out_calendar_time, + Service::PSC::Time::CalendarAdditionalInfo& out_additional_info, + s64 time); + Result ToPosixTime(u32& out_count, std::span out_times, u32 out_times_count, + Service::PSC::Time::CalendarTime& calendar_time, Tz::Rule& rule); + Result ToPosixTimeWithMyRule(u32& out_count, std::span out_times, u32 out_times_count, + Service::PSC::Time::CalendarTime& calendar_time); + +private: + void Handle_GetDeviceLocationName(HLERequestContext& ctx); + void Handle_SetDeviceLocationName(HLERequestContext& ctx); + void Handle_GetTotalLocationNameCount(HLERequestContext& ctx); + void Handle_LoadLocationNameList(HLERequestContext& ctx); + void Handle_LoadTimeZoneRule(HLERequestContext& ctx); + void Handle_GetTimeZoneRuleVersion(HLERequestContext& ctx); + void Handle_GetDeviceLocationNameAndUpdatedTime(HLERequestContext& ctx); + void Handle_SetDeviceLocationNameWithTimeZoneRule(HLERequestContext& ctx); + void Handle_ParseTimeZoneBinary(HLERequestContext& ctx); + void Handle_GetDeviceLocationNameOperationEventReadableHandle(HLERequestContext& ctx); + void Handle_ToCalendarTime(HLERequestContext& ctx); + void Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx); + void Handle_ToPosixTime(HLERequestContext& ctx); + void Handle_ToPosixTimeWithMyRule(HLERequestContext& ctx); + + Core::System& m_system; + std::shared_ptr m_set_sys; + + bool m_can_write_timezone_device_location; + FileTimestampWorker& m_file_timestamp_worker; + std::shared_ptr m_wrapped_service; + std::mutex m_mutex; + bool operation_event_initialized{}; + Service::PSC::Time::OperationEvent m_operation_event; +}; + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/time_zone_binary.cpp b/src/core/hle/service/glue/time/time_zone_binary.cpp new file mode 100755 index 000000000..405728e7e --- /dev/null +++ b/src/core/hle/service/glue/time/time_zone_binary.cpp @@ -0,0 +1,205 @@ +// 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.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 g_time_zone_scratch_space(0x2800, 0); + +Result TimeZoneReadBinary(size_t& out_read_size, std::span 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) { + auto& fsc{system.GetFileSystemController()}; + std::unique_ptr nca{}; + + auto* bis_system = fsc.GetSystemNANDContents(); + + R_UNLESS(bis_system, ResultUnknown); + + nca = bis_system->GetEntry(TimeZoneBinaryId, FileSys::ContentRecordType::Data); + + R_UNLESS(nca, ResultUnknown); + + g_time_zone_binary_romfs = FileSys::ExtractRomFS(nca->GetRomFS()); + + 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, 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.name.data()); +} + +bool IsTimeZoneBinaryValid(Service::PSC::Time::LocationName& name) { + std::string path{}; + GetTimeZoneZonePath(path, name); + + auto vfs_file{g_time_zone_binary_romfs->GetFileRelative(path)}; + 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(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(&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& out_rule, size_t& out_rule_size, + 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::vector& 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 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.push_back(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.name.size() - 2, + Service::PSC::Time::ResultFailed); + + current_name.name[current_name_len++] = chr; + } + + R_SUCCEED(); +} + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/time_zone_binary.h b/src/core/hle/service/glue/time/time_zone_binary.h new file mode 100755 index 000000000..2cad6b458 --- /dev/null +++ b/src/core/hle/service/glue/time/time_zone_binary.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include + +#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, Service::PSC::Time::LocationName& name); +bool IsTimeZoneBinaryValid(Service::PSC::Time::LocationName& name); +u32 GetTimeZoneCount(); +Result GetTimeZoneVersion(Service::PSC::Time::RuleVersion& out_rule_version); +Result GetTimeZoneRule(std::span& out_rule, size_t& out_rule_size, + Service::PSC::Time::LocationName& name); +Result GetTimeZoneLocationList(u32& out_count, + std::vector& out_names, + size_t max_names, u32 index); + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/worker.cpp b/src/core/hle/service/glue/time/worker.cpp new file mode 100755 index 000000000..ea0e49b90 --- /dev/null +++ b/src/core/hle/service/glue/time/worker.cpp @@ -0,0 +1,338 @@ +// 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/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 +T GetSettingsItemValue(std::shared_ptr& set_sys, + const char* category, const char* name) { + std::vector interval_buf; + auto res = set_sys->GetSettingsItemValue(interval_buf, category, name); + ASSERT(res == ResultSuccess); + + T v{}; + std::memcpy(&v, interval_buf.data(), sizeof(T)); + return v; +} + +} // namespace + +TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource, + FileTimestampWorker& file_timestamp_worker) + : m_system{system}, m_ctx{m_system, "Glue:58"}, m_event{m_ctx.CreateEvent("Glue:58:Event")}, + m_steady_clock_resource{steady_clock_resource}, + m_file_timestamp_worker{file_timestamp_worker}, m_timer_steady_clock{m_ctx.CreateEvent( + "Glue:58:SteadyClockTimerEvent")}, + m_timer_file_system{m_ctx.CreateEvent("Glue:58: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 { + 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 { + 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 time_sm, + std::shared_ptr set_sys) { + m_set_sys = std::move(set_sys); + m_time_m = + m_system.ServiceManager().GetService("time:m", true); + m_time_sm = std::move(time_sm); + + m_alarm_worker.Initialize(m_time_m); + + auto steady_clock_interval_m = GetSettingsItemValue( + m_set_sys, "time", "standard_steady_clock_rtc_update_interval_minutes"); + + auto one_minute_ns{ + std::chrono::duration_cast(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(m_set_sys, "time", "notify_time_to_fs_interval_seconds"); + auto one_second_ns{ + std::chrono::duration_cast(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); + + enum class EventType { + Exit = 0, + IpmModuleService_GetEvent = 1, + PowerStateChange = 2, + SignalAlarms = 3, + UpdateLocalSystemClock = 4, + UpdateNetworkSystemClock = 5, + UpdateEphemeralSystemClock = 6, + UpdateSteadyClock = 7, + UpdateFileTimestamp = 8, + AutoCorrect = 9, + Max = 10, + }; + + s32 num_objs{}; + std::array(EventType::Max)> wait_objs{}; + std::array(EventType::Max)> wait_indices{}; + + const auto AddWaiter{ + [&](Kernel::KSynchronizationObject* synchronization_object, EventType type) { + // Open a new reference to the object. + synchronization_object->Open(); + + // Insert into the list. + wait_indices[num_objs] = type; + wait_objs[num_objs++] = synchronization_object; + }}; + + while (!stop_token.stop_requested()) { + SCOPE_EXIT({ + for (s32 i = 0; i < num_objs; i++) { + wait_objs[i]->Close(); + } + }); + + num_objs = {}; + wait_objs = {}; + if (m_pm_state_change_handler.m_priority != 0) { + AddWaiter(&m_event->GetReadableEvent(), EventType::Exit); + // TODO + // AddWaiter(gIPmModuleService::GetEvent(), 1); + AddWaiter(&m_alarm_worker.GetEvent().GetReadableEvent(), EventType::PowerStateChange); + } else { + AddWaiter(&m_event->GetReadableEvent(), EventType::Exit); + // TODO + // AddWaiter(gIPmModuleService::GetEvent(), 1); + AddWaiter(&m_alarm_worker.GetEvent().GetReadableEvent(), EventType::PowerStateChange); + AddWaiter(&m_alarm_worker.GetTimerEvent().GetReadableEvent(), EventType::SignalAlarms); + AddWaiter(&m_local_clock_event->GetReadableEvent(), EventType::UpdateLocalSystemClock); + AddWaiter(&m_network_clock_event->GetReadableEvent(), + EventType::UpdateNetworkSystemClock); + AddWaiter(&m_ephemeral_clock_event->GetReadableEvent(), + EventType::UpdateEphemeralSystemClock); + AddWaiter(&m_timer_steady_clock->GetReadableEvent(), EventType::UpdateSteadyClock); + AddWaiter(&m_timer_file_system->GetReadableEvent(), EventType::UpdateFileTimestamp); + AddWaiter(&m_standard_user_auto_correct_clock_event->GetReadableEvent(), + EventType::AutoCorrect); + } + + s32 out_index{-1}; + Kernel::KSynchronizationObject::Wait(m_system.Kernel(), &out_index, wait_objs.data(), + num_objs, -1); + ASSERT(out_index >= 0 && out_index < num_objs); + + if (stop_token.stop_requested()) { + return; + } + + switch (wait_indices[out_index]) { + case EventType::Exit: + return; + + case EventType::IpmModuleService_GetEvent: + // TODO + // IPmModuleService::GetEvent() + // clear the event + // Handle power state change event + break; + + 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{}; + auto res = m_local_clock->GetSystemClockContext(context); + ASSERT(res == ResultSuccess); + + m_set_sys->SetUserSystemClockContext(context); + + m_file_timestamp_worker.SetFilesystemPosixTime(); + } break; + + case EventType::UpdateNetworkSystemClock: { + m_network_clock_event->Clear(); + Service::PSC::Time::SystemClockContext context{}; + auto res = m_network_clock->GetSystemClockContext(context); + ASSERT(res == ResultSuccess); + 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{}; + auto res = m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled( + automatic_correction); + ASSERT(res == ResultSuccess); + + Service::PSC::Time::SteadyClockTimePoint time_point{}; + res = m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(time_point); + ASSERT(res == ResultSuccess); + + m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction); + m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); + } break; + + default: + UNREACHABLE(); + break; + } + } +} + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/worker.h b/src/core/hle/service/glue/time/worker.h new file mode 100755 index 000000000..adbbe6b6d --- /dev/null +++ b/src/core/hle/service/glue/time/worker.h @@ -0,0 +1,64 @@ +// 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 time_sm, + std::shared_ptr set_sys); + + void StartThread(); + +private: + void ThreadFunc(std::stop_token stop_token); + + Core::System& m_system; + KernelHelpers::ServiceContext m_ctx; + std::shared_ptr m_set_sys; + + std::jthread m_thread; + Kernel::KEvent* m_event{}; + std::shared_ptr m_time_m; + std::shared_ptr m_time_sm; + std::shared_ptr m_network_clock; + std::shared_ptr m_local_clock; + std::shared_ptr m_ephemeral_clock; + StandardSteadyClockResource& m_steady_clock_resource; + FileTimestampWorker& m_file_timestamp_worker; + Kernel::KEvent* m_local_clock_event{}; + Kernel::KEvent* m_network_clock_event{}; + Kernel::KEvent* m_ephemeral_clock_event{}; + Kernel::KEvent* m_standard_user_auto_correct_clock_event{}; + Kernel::KEvent* m_timer_steady_clock{}; + std::shared_ptr m_timer_steady_clock_timing_event; + Kernel::KEvent* m_timer_file_system{}; + std::shared_ptr m_timer_file_system_timing_event; + AlarmWorker m_alarm_worker; + PmStateChangeHandler m_pm_state_change_handler; +}; + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 3bbe33be2..7138d301f 100755 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -18,23 +18,23 @@ namespace Service::HID { void LoopProcess(Core::System& system) { auto server_manager = std::make_unique(system); - std::shared_ptr resouce_manager = std::make_shared(system); + std::shared_ptr resource_manager = std::make_shared(system); std::shared_ptr firmware_settings = std::make_shared(); // TODO: Remove this hack until this service is emulated properly. const auto process_list = system.Kernel().GetProcessList(); if (!process_list.empty()) { - resouce_manager->Initialize(); - resouce_manager->RegisterAppletResourceUserId(process_list[0]->GetId(), true); + resource_manager->Initialize(); + resource_manager->RegisterAppletResourceUserId(process_list[0]->GetId(), true); } server_manager->RegisterNamedService( - "hid", std::make_shared(system, resouce_manager, firmware_settings)); + "hid", std::make_shared(system, resource_manager, firmware_settings)); server_manager->RegisterNamedService( - "hid:dbg", std::make_shared(system, resouce_manager)); + "hid:dbg", std::make_shared(system, resource_manager)); server_manager->RegisterNamedService( - "hid:sys", std::make_shared(system, resouce_manager)); + "hid:sys", std::make_shared(system, resource_manager)); server_manager->RegisterNamedService("hidbus", std::make_shared(system)); diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp index 2ff00d30d..74898888a 100755 --- a/src/core/hle/service/hid/hid_server.cpp +++ b/src/core/hle/service/hid/hid_server.cpp @@ -1444,8 +1444,8 @@ void IHidServer::SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx) { const auto parameters{rp.PopRaw()}; - LOG_WARNING(Service_HID, "(STUBBED) called, use_center_clamp={}, applet_resource_user_id={}", - parameters.use_center_clamp, parameters.applet_resource_user_id); + LOG_INFO(Service_HID, "called, use_center_clamp={}, applet_resource_user_id={}", + parameters.use_center_clamp, parameters.applet_resource_user_id); GetResourceManager()->GetNpad()->SetNpadAnalogStickUseCenterClamp( parameters.applet_resource_user_id, parameters.use_center_clamp); @@ -1466,23 +1466,27 @@ void IHidServer::SetNpadCaptureButtonAssignment(HLERequestContext& ctx) { const auto parameters{rp.PopRaw()}; - LOG_WARNING(Service_HID, - "(STUBBED) called, npad_styleset={}, applet_resource_user_id={}, button={}", - parameters.npad_styleset, parameters.applet_resource_user_id, parameters.button); + LOG_INFO(Service_HID, "called, npad_styleset={}, applet_resource_user_id={}, button={}", + parameters.npad_styleset, parameters.applet_resource_user_id, parameters.button); + + const auto result = GetResourceManager()->GetNpad()->SetNpadCaptureButtonAssignment( + parameters.applet_resource_user_id, parameters.npad_styleset, parameters.button); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IHidServer::ClearNpadCaptureButtonAssignment(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop()}; - LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", - applet_resource_user_id); + LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + const auto result = + GetResourceManager()->GetNpad()->ClearNpadCaptureButtonAssignment(applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IHidServer::GetVibrationDeviceInfo(HLERequestContext& ctx) { diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp index 2a65615e8..3a0cb3cb1 100755 --- a/src/core/hle/service/hid/hid_system_server.cpp +++ b/src/core/hle/service/hid/hid_system_server.cpp @@ -46,7 +46,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr()}; + + LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + GetResourceManager()->GetNpad()->AssigningSingleOnSlSrPress(applet_resource_user_id, true); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void IHidSystemServer::DisableAssigningSingleOnSlSrPress(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + GetResourceManager()->GetNpad()->AssigningSingleOnSlSrPress(applet_resource_user_id, false); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void IHidSystemServer::GetLastActiveNpad(HLERequestContext& ctx) { - LOG_DEBUG(Service_HID, "(STUBBED) called"); // Spams a lot when controller applet is running + Core::HID::NpadIdType npad_id{}; + const Result result = GetResourceManager()->GetNpad()->GetLastActiveNpad(npad_id); + + LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id); IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(0); // Dont forget to fix this + rb.Push(result); + rb.PushEnum(npad_id); } void IHidSystemServer::ApplyNpadSystemCommonPolicyFull(HLERequestContext& ctx) { @@ -331,6 +344,27 @@ void IHidSystemServer::SetSupportedNpadStyleSetAll(HLERequestContext& ctx) { rb.Push(result); } +void IHidSystemServer::GetNpadCaptureButtonAssignment(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + const auto capture_button_list_size{ctx.GetWriteBufferNumElements()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + std::vector capture_button_list(capture_button_list_size); + const auto& npad = GetResourceManager()->GetNpad(); + const u64 list_size = + npad->GetNpadCaptureButtonAssignment(capture_button_list, applet_resource_user_id); + + if (list_size != 0) { + ctx.WriteBuffer(capture_button_list); + } + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(list_size); +} + void IHidSystemServer::GetAppletDetailedUiType(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto npad_id_type{rp.PopEnum()}; @@ -423,13 +457,25 @@ void IHidSystemServer::GetUniquePadsFromNpad(HLERequestContext& ctx) { rb.Push(static_cast(unique_pads.size())); } -void IHidSystemServer::GetIrSensorState(HLERequestContext& ctx) { +void IHidSystemServer::SetNpadSystemExtStateEnabled(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; + struct Parameters { + bool is_enabled; + INSERT_PADDING_BYTES_NOINIT(7); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - LOG_WARNING(Service_HID, "(STUBBED) called"); + const auto parameters{rp.PopRaw()}; + + LOG_INFO(Service_HID, "called, is_enabled={}, applet_resource_user_id={}", + parameters.is_enabled, parameters.applet_resource_user_id); + + const auto result = GetResourceManager()->GetNpad()->SetNpadSystemExtStateEnabled( + parameters.applet_resource_user_id, parameters.is_enabled); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IHidSystemServer::RegisterAppletResourceUserId(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; diff --git a/src/core/hle/service/hid/hid_system_server.h b/src/core/hle/service/hid/hid_system_server.h index f467e2aa8..0c2634e3f 100755 --- a/src/core/hle/service/hid/hid_system_server.h +++ b/src/core/hle/service/hid/hid_system_server.h @@ -31,13 +31,14 @@ private: void GetNpadFullKeyGripColor(HLERequestContext& ctx); void GetMaskedSupportedNpadStyleSet(HLERequestContext& ctx); void SetSupportedNpadStyleSetAll(HLERequestContext& ctx); + void GetNpadCaptureButtonAssignment(HLERequestContext& ctx); void GetAppletDetailedUiType(HLERequestContext& ctx); void GetNpadInterfaceType(HLERequestContext& ctx); void GetNpadLeftRightInterfaceType(HLERequestContext& ctx); void HasBattery(HLERequestContext& ctx); void HasLeftRightBattery(HLERequestContext& ctx); void GetUniquePadsFromNpad(HLERequestContext& ctx); - void GetIrSensorState(HLERequestContext& ctx); + void SetNpadSystemExtStateEnabled(HLERequestContext& ctx); void RegisterAppletResourceUserId(HLERequestContext& ctx); void UnregisterAppletResourceUserId(HLERequestContext& ctx); void EnableAppletToGetInput(HLERequestContext& ctx); diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp index ff4d5e528..dde6d07a5 100755 --- a/src/core/hle/service/hid/hidbus.cpp +++ b/src/core/hle/service/hid/hidbus.cpp @@ -67,7 +67,7 @@ HidBus::~HidBus() { void HidBus::UpdateHidbus(std::chrono::nanoseconds ns_late) { if (is_hidbus_enabled) { for (std::size_t i = 0; i < devices.size(); ++i) { - if (!devices[i].is_device_initializated) { + if (!devices[i].is_device_initialized) { continue; } auto& device = devices[i].device; @@ -213,7 +213,7 @@ void HidBus::Initialize(HLERequestContext& ctx) { if (bus_handle_.internal_index == 0 && Settings::values.enable_ring_controller) { MakeDevice(bus_handle_); - devices[device_index.value()].is_device_initializated = true; + devices[device_index.value()].is_device_initialized = true; devices[device_index.value()].device->ActivateDevice(); cur_entry.is_in_focus = true; cur_entry.is_connected = true; @@ -222,7 +222,7 @@ void HidBus::Initialize(HLERequestContext& ctx) { cur_entry.is_polling_mode = false; } else { MakeDevice(bus_handle_); - devices[device_index.value()].is_device_initializated = true; + devices[device_index.value()].is_device_initialized = true; cur_entry.is_in_focus = true; cur_entry.is_connected = false; cur_entry.is_connected_result = ResultSuccess; @@ -261,7 +261,7 @@ void HidBus::Finalize(HLERequestContext& ctx) { const auto entry_index = devices[device_index.value()].handle.internal_index; auto& cur_entry = hidbus_status.entries[entry_index]; auto& device = devices[device_index.value()].device; - devices[device_index.value()].is_device_initializated = false; + devices[device_index.value()].is_device_initialized = false; device->DeactivateDevice(); cur_entry.is_in_focus = true; diff --git a/src/core/hle/service/hid/hidbus.h b/src/core/hle/service/hid/hidbus.h index db7d1873c..0189f191d 100755 --- a/src/core/hle/service/hid/hidbus.h +++ b/src/core/hle/service/hid/hidbus.h @@ -89,7 +89,7 @@ private: static_assert(sizeof(HidbusStatusManager) <= 0x1000, "HidbusStatusManager is an invalid size"); struct HidbusDevice { - bool is_device_initializated{}; + bool is_device_initialized{}; BusHandle handle{}; std::unique_ptr device{nullptr}; }; diff --git a/src/core/hle/service/hle_ipc.cpp b/src/core/hle/service/hle_ipc.cpp index 3a5cae2df..151790913 100755 --- a/src/core/hle/service/hle_ipc.cpp +++ b/src/core/hle/service/hle_ipc.cpp @@ -182,22 +182,22 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { } } - buffer_x_desciptors.reserve(command_header->num_buf_x_descriptors); - buffer_a_desciptors.reserve(command_header->num_buf_a_descriptors); - buffer_b_desciptors.reserve(command_header->num_buf_b_descriptors); - buffer_w_desciptors.reserve(command_header->num_buf_w_descriptors); + buffer_x_descriptors.reserve(command_header->num_buf_x_descriptors); + buffer_a_descriptors.reserve(command_header->num_buf_a_descriptors); + buffer_b_descriptors.reserve(command_header->num_buf_b_descriptors); + buffer_w_descriptors.reserve(command_header->num_buf_w_descriptors); for (u32 i = 0; i < command_header->num_buf_x_descriptors; ++i) { - buffer_x_desciptors.push_back(rp.PopRaw()); + buffer_x_descriptors.push_back(rp.PopRaw()); } for (u32 i = 0; i < command_header->num_buf_a_descriptors; ++i) { - buffer_a_desciptors.push_back(rp.PopRaw()); + buffer_a_descriptors.push_back(rp.PopRaw()); } for (u32 i = 0; i < command_header->num_buf_b_descriptors; ++i) { - buffer_b_desciptors.push_back(rp.PopRaw()); + buffer_b_descriptors.push_back(rp.PopRaw()); } for (u32 i = 0; i < command_header->num_buf_w_descriptors; ++i) { - buffer_w_desciptors.push_back(rp.PopRaw()); + buffer_w_descriptors.push_back(rp.PopRaw()); } const auto buffer_c_offset = rp.GetCurrentOffset() + command_header->data_size; @@ -247,7 +247,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { IPC::CommandHeader::BufferDescriptorCFlag::InlineDescriptor) { if (command_header->buf_c_descriptor_flags == IPC::CommandHeader::BufferDescriptorCFlag::OneDescriptor) { - buffer_c_desciptors.push_back(rp.PopRaw()); + buffer_c_descriptors.push_back(rp.PopRaw()); } else { u32 num_buf_c_descriptors = static_cast(command_header->buf_c_descriptor_flags.Value()) - 2; @@ -257,7 +257,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { ASSERT(num_buf_c_descriptors < 14); for (u32 i = 0; i < num_buf_c_descriptors; ++i) { - buffer_c_desciptors.push_back(rp.PopRaw()); + buffer_c_descriptors.push_back(rp.PopRaw()); } } } diff --git a/src/core/hle/service/hle_ipc.h b/src/core/hle/service/hle_ipc.h index 25c94b0b0..876c506b4 100755 --- a/src/core/hle/service/hle_ipc.h +++ b/src/core/hle/service/hle_ipc.h @@ -234,19 +234,19 @@ public: } [[nodiscard]] const std::vector& BufferDescriptorX() const { - return buffer_x_desciptors; + return buffer_x_descriptors; } [[nodiscard]] const std::vector& BufferDescriptorA() const { - return buffer_a_desciptors; + return buffer_a_descriptors; } [[nodiscard]] const std::vector& BufferDescriptorB() const { - return buffer_b_desciptors; + return buffer_b_descriptors; } [[nodiscard]] const std::vector& BufferDescriptorC() const { - return buffer_c_desciptors; + return buffer_c_descriptors; } [[nodiscard]] const IPC::DomainMessageHeader& GetDomainMessageHeader() const { @@ -408,11 +408,11 @@ private: std::optional handle_descriptor_header; std::optional data_payload_header; std::optional domain_message_header; - std::vector buffer_x_desciptors; - std::vector buffer_a_desciptors; - std::vector buffer_b_desciptors; - std::vector buffer_w_desciptors; - std::vector buffer_c_desciptors; + std::vector buffer_x_descriptors; + std::vector buffer_a_descriptors; + std::vector buffer_b_descriptors; + std::vector buffer_w_descriptors; + std::vector buffer_c_descriptors; u32_le command{}; u64 pid{}; diff --git a/src/core/hle/service/kernel_helpers.cpp b/src/core/hle/service/kernel_helpers.cpp index 95f85d83e..ed8587ffd 100755 --- a/src/core/hle/service/kernel_helpers.cpp +++ b/src/core/hle/service/kernel_helpers.cpp @@ -65,6 +65,9 @@ Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) { } void ServiceContext::CloseEvent(Kernel::KEvent* event) { + if (!event) { + return; + } event->GetReadableEvent().Close(); event->Close(); } diff --git a/src/core/hle/service/nfc/common/amiibo_crypto.cpp b/src/core/hle/service/nfc/common/amiibo_crypto.cpp index 9556e9193..4274a92c9 100755 --- a/src/core/hle/service/nfc/common/amiibo_crypto.cpp +++ b/src/core/hle/service/nfc/common/amiibo_crypto.cpp @@ -19,7 +19,7 @@ namespace Service::NFP::AmiiboCrypto { bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) { const auto& amiibo_data = ntag_file.user_memory; LOG_DEBUG(Service_NFP, "uuid_lock=0x{0:x}", ntag_file.static_lock); - LOG_DEBUG(Service_NFP, "compability_container=0x{0:x}", ntag_file.compability_container); + LOG_DEBUG(Service_NFP, "compatibility_container=0x{0:x}", ntag_file.compatibility_container); LOG_DEBUG(Service_NFP, "write_count={}", static_cast(amiibo_data.write_counter)); LOG_DEBUG(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id); @@ -49,7 +49,7 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) { if (ntag_file.static_lock != 0xE00F) { return false; } - if (ntag_file.compability_container != 0xEEFF10F1U) { + if (ntag_file.compatibility_container != 0xEEFF10F1U) { return false; } if (amiibo_data.model_info.tag_type != NFC::PackedTagType::Type2) { @@ -78,7 +78,7 @@ NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) { encoded_data.uid_crc_check2 = nfc_data.uuid_crc_check2; encoded_data.internal_number = nfc_data.internal_number; encoded_data.static_lock = nfc_data.static_lock; - encoded_data.compability_container = nfc_data.compability_container; + encoded_data.compatibility_container = nfc_data.compatibility_container; encoded_data.hmac_data = nfc_data.user_memory.hmac_data; encoded_data.constant_value = nfc_data.user_memory.constant_value; encoded_data.write_counter = nfc_data.user_memory.write_counter; @@ -112,7 +112,7 @@ EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) { nfc_data.uuid_crc_check2 = encoded_data.uid_crc_check2; nfc_data.internal_number = encoded_data.internal_number; nfc_data.static_lock = encoded_data.static_lock; - nfc_data.compability_container = encoded_data.compability_container; + nfc_data.compatibility_container = encoded_data.compatibility_container; nfc_data.user_memory.hmac_data = encoded_data.hmac_data; nfc_data.user_memory.constant_value = encoded_data.constant_value; nfc_data.user_memory.write_counter = encoded_data.write_counter; @@ -257,7 +257,7 @@ void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& ou out_data.uid_crc_check2 = in_data.uid_crc_check2; out_data.internal_number = in_data.internal_number; out_data.static_lock = in_data.static_lock; - out_data.compability_container = in_data.compability_container; + out_data.compatibility_container = in_data.compatibility_container; out_data.constant_value = in_data.constant_value; out_data.write_counter = in_data.write_counter; diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp index b37fb6da3..ae51a8408 100755 --- a/src/core/hle/service/nfc/common/device.cpp +++ b/src/core/hle/service/nfc/common/device.cpp @@ -29,7 +29,11 @@ #include "core/hle/service/nfc/common/device.h" #include "core/hle/service/nfc/mifare_result.h" #include "core/hle/service/nfc/nfc_result.h" -#include "core/hle/service/time/time_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/time_zone_service.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" #include "hid_core/frontend/emulated_controller.h" #include "hid_core/hid_core.h" #include "hid_core/hid_types.h" @@ -75,7 +79,7 @@ void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) { return; } - if (!is_initalized) { + if (!is_initialized) { return; } @@ -207,7 +211,7 @@ void NfcDevice::Initialize() { return; } - is_initalized = npad_device->AddNfcHandle(); + is_initialized = npad_device->AddNfcHandle(); } void NfcDevice::Finalize() { @@ -226,7 +230,7 @@ void NfcDevice::Finalize() { } device_state = DeviceState::Unavailable; - is_initalized = false; + is_initialized = false; } Result NfcDevice::StartDetection(NfcProtocol allowed_protocol) { @@ -393,8 +397,7 @@ Result NfcDevice::WriteMifare(std::span paramet return result; } -Result NfcDevice::SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout, - std::span command_data, +Result NfcDevice::SendCommandByPassThrough(const s64& timeout, std::span command_data, std::span out_data) { // Not implemented return ResultSuccess; @@ -1396,27 +1399,41 @@ void NfcDevice::SetAmiiboName(NFP::AmiiboSettings& settings, } NFP::AmiiboDate NfcDevice::GetAmiiboDate(s64 posix_time) const { - const auto& time_zone_manager = - system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager(); - Time::TimeZone::CalendarInfo calendar_info{}; + auto static_service = + system.ServiceManager().GetService("time:u", true); + + std::shared_ptr timezone_service{}; + static_service->GetTimeZoneService(timezone_service); + + Service::PSC::Time::CalendarTime calendar_time{}; + Service::PSC::Time::CalendarAdditionalInfo additional_info{}; + NFP::AmiiboDate amiibo_date{}; amiibo_date.SetYear(2000); amiibo_date.SetMonth(1); amiibo_date.SetDay(1); - if (time_zone_manager.ToCalendarTime({}, posix_time, calendar_info) == ResultSuccess) { - amiibo_date.SetYear(calendar_info.time.year); - amiibo_date.SetMonth(calendar_info.time.month); - amiibo_date.SetDay(calendar_info.time.day); + if (timezone_service->ToCalendarTimeWithMyRule(calendar_time, additional_info, posix_time) == + ResultSuccess) { + amiibo_date.SetYear(calendar_time.year); + amiibo_date.SetMonth(calendar_time.month); + amiibo_date.SetDay(calendar_time.day); } return amiibo_date; } -u64 NfcDevice::GetCurrentPosixTime() const { - auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()}; - return standard_steady_clock.GetCurrentTimePoint(system).time_point; +s64 NfcDevice::GetCurrentPosixTime() const { + auto static_service = + system.ServiceManager().GetService("time:u", true); + + std::shared_ptr steady_clock{}; + static_service->GetStandardSteadyClock(steady_clock); + + Service::PSC::Time::SteadyClockTimePoint time_point{}; + R_ASSERT(steady_clock->GetCurrentTimePoint(time_point)); + return time_point.time_point; } u64 NfcDevice::RemoveVersionByte(u64 application_id) const { diff --git a/src/core/hle/service/nfc/common/device.h b/src/core/hle/service/nfc/common/device.h index d8efe25ec..d59202d18 100755 --- a/src/core/hle/service/nfc/common/device.h +++ b/src/core/hle/service/nfc/common/device.h @@ -11,7 +11,6 @@ #include "core/hle/service/nfc/nfc_types.h" #include "core/hle/service/nfp/nfp_types.h" #include "core/hle/service/service.h" -#include "core/hle/service/time/clock_types.h" namespace Kernel { class KEvent; @@ -49,8 +48,8 @@ public: Result WriteMifare(std::span parameters); - Result SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout, - std::span command_data, std::span out_data); + Result SendCommandByPassThrough(const s64& timeout, std::span command_data, + std::span out_data); Result Mount(NFP::ModelType model_type, NFP::MountTarget mount_target); Result Unmount(); @@ -108,7 +107,7 @@ private: NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const; void SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name) const; NFP::AmiiboDate GetAmiiboDate(s64 posix_time) const; - u64 GetCurrentPosixTime() const; + s64 GetCurrentPosixTime() const; u64 RemoveVersionByte(u64 application_id) const; void UpdateSettingsCrc(); void UpdateRegisterInfoCrc(); @@ -126,7 +125,7 @@ private: Kernel::KEvent* deactivate_event = nullptr; Kernel::KEvent* availability_change_event = nullptr; - bool is_initalized{}; + bool is_initialized{}; NfcProtocol allowed_protocols{}; DeviceState device_state{DeviceState::Unavailable}; diff --git a/src/core/hle/service/nfc/common/device_manager.cpp b/src/core/hle/service/nfc/common/device_manager.cpp index 44f651b87..fd2566fb3 100755 --- a/src/core/hle/service/nfc/common/device_manager.cpp +++ b/src/core/hle/service/nfc/common/device_manager.cpp @@ -10,8 +10,10 @@ #include "core/hle/service/nfc/common/device.h" #include "core/hle/service/nfc/common/device_manager.h" #include "core/hle/service/nfc/nfc_result.h" -#include "core/hle/service/time/clock_types.h" -#include "core/hle/service/time/time_manager.h" +#include "core/hle/service/psc/time/static.h" +#include "core/hle/service/psc/time/steady_clock.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" #include "hid_core/hid_types.h" #include "hid_core/hid_util.h" @@ -82,11 +84,19 @@ Result DeviceManager::ListDevices(std::vector& nfp_devices, std::size_t max continue; } if (skip_fatal_errors) { - constexpr u64 MinimumRecoveryTime = 60; - auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()}; - const u64 elapsed_time = standard_steady_clock.GetCurrentTimePoint(system).time_point - - time_since_last_error; + constexpr s64 MinimumRecoveryTime = 60; + auto static_service = + system.ServiceManager().GetService("time:u", + true); + + std::shared_ptr steady_clock{}; + static_service->GetStandardSteadyClock(steady_clock); + + Service::PSC::Time::SteadyClockTimePoint time_point{}; + R_ASSERT(steady_clock->GetCurrentTimePoint(time_point)); + + const s64 elapsed_time = time_point.time_point - time_since_last_error; if (time_since_last_error != 0 && elapsed_time < MinimumRecoveryTime) { continue; } @@ -250,8 +260,7 @@ Result DeviceManager::WriteMifare(u64 device_handle, return result; } -Result DeviceManager::SendCommandByPassThrough(u64 device_handle, - const Time::Clock::TimeSpanType& timeout, +Result DeviceManager::SendCommandByPassThrough(u64 device_handle, const s64& timeout, std::span command_data, std::span out_data) { std::scoped_lock lock{mutex}; @@ -741,8 +750,16 @@ Result DeviceManager::VerifyDeviceResult(std::shared_ptr device, if (operation_result == ResultUnknown112 || operation_result == ResultUnknown114 || operation_result == ResultUnknown115) { - auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()}; - time_since_last_error = standard_steady_clock.GetCurrentTimePoint(system).time_point; + auto static_service = + system.ServiceManager().GetService("time:u", true); + + std::shared_ptr steady_clock{}; + static_service->GetStandardSteadyClock(steady_clock); + + Service::PSC::Time::SteadyClockTimePoint time_point{}; + R_ASSERT(steady_clock->GetCurrentTimePoint(time_point)); + + time_since_last_error = time_point.time_point; } return operation_result; diff --git a/src/core/hle/service/nfc/common/device_manager.h b/src/core/hle/service/nfc/common/device_manager.h index f02bdccf5..c56a2fbda 100755 --- a/src/core/hle/service/nfc/common/device_manager.h +++ b/src/core/hle/service/nfc/common/device_manager.h @@ -13,7 +13,6 @@ #include "core/hle/service/nfc/nfc_types.h" #include "core/hle/service/nfp/nfp_types.h" #include "core/hle/service/service.h" -#include "core/hle/service/time/clock_types.h" #include "hid_core/hid_types.h" namespace Service::NFC { @@ -42,7 +41,7 @@ public: std::span read_data); Result WriteMifare(u64 device_handle, std::span write_parameters); - Result SendCommandByPassThrough(u64 device_handle, const Time::Clock::TimeSpanType& timeout, + Result SendCommandByPassThrough(u64 device_handle, const s64& timeout, std::span command_data, std::span out_data); // Nfp device manager @@ -92,7 +91,7 @@ private: const std::optional> GetNfcDevice(u64 handle) const; bool is_initialized = false; - u64 time_since_last_error = 0; + s64 time_since_last_error = 0; mutable std::mutex mutex; std::array, 10> devices{}; diff --git a/src/core/hle/service/nfc/nfc_interface.cpp b/src/core/hle/service/nfc/nfc_interface.cpp index a71cf74b8..207ac4efe 100755 --- a/src/core/hle/service/nfc/nfc_interface.cpp +++ b/src/core/hle/service/nfc/nfc_interface.cpp @@ -13,7 +13,6 @@ #include "core/hle/service/nfc/nfc_result.h" #include "core/hle/service/nfc/nfc_types.h" #include "core/hle/service/nfp/nfp_result.h" -#include "core/hle/service/time/clock_types.h" #include "hid_core/hid_types.h" namespace Service::NFC { @@ -261,10 +260,10 @@ void NfcInterface::WriteMifare(HLERequestContext& ctx) { void NfcInterface::SendCommandByPassThrough(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop()}; - const auto timeout{rp.PopRaw()}; + const auto timeout{rp.PopRaw()}; const auto command_data{ctx.ReadBuffer()}; LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, timeout={}, data_size={}", - device_handle, timeout.ToSeconds(), command_data.size()); + device_handle, timeout, command_data.size()); std::vector out_data(1); auto result = diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h index faf8ce513..400c24c6d 100755 --- a/src/core/hle/service/nfp/nfp_types.h +++ b/src/core/hle/service/nfp/nfp_types.h @@ -243,12 +243,12 @@ static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid si struct NTAG215File { u8 uid_crc_check2; u8 internal_number; - u16 static_lock; // Set defined pages as read only - u32 compability_container; // Defines available memory - HashData hmac_data; // Hash - u8 constant_value; // Must be A5 - u16_be write_counter; // Number of times the amiibo has been written? - u8 amiibo_version; // Amiibo file version + u16 static_lock; // Set defined pages as read only + u32 compatibility_container; // Defines available memory + HashData hmac_data; // Hash + u8 constant_value; // Must be A5 + u16_be write_counter; // Number of times the amiibo has been written? + u8 amiibo_version; // Amiibo file version AmiiboSettings settings; Service::Mii::Ver3StoreData owner_mii; // Mii data u64_be application_id; // Game id @@ -278,7 +278,7 @@ struct EncryptedNTAG215File { u8 uuid_crc_check2; u8 internal_number; u16 static_lock; // Set defined pages as read only - u32 compability_container; // Defines available memory + u32 compatibility_container; // Defines available memory EncryptedAmiiboFile user_memory; // Writable data u32 dynamic_lock; // Dynamic lock u32 CFG0; // Defines memory protected by password diff --git a/src/core/hle/service/ns/language.cpp b/src/core/hle/service/ns/language.cpp index 1aba96f97..3dad98c32 100755 --- a/src/core/hle/service/ns/language.cpp +++ b/src/core/hle/service/ns/language.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/ns/language.h" -#include "core/hle/service/set/set.h" +#include "core/hle/service/set/settings_server.h" namespace Service::NS { @@ -415,4 +415,4 @@ std::optional ConvertToLanguageCode(const ApplicationLanguage return std::nullopt; } } -} // namespace Service::NS \ No newline at end of file +} // namespace Service::NS diff --git a/src/core/hle/service/ns/language.h b/src/core/hle/service/ns/language.h index 11e5cfda7..7a54265c7 100755 --- a/src/core/hle/service/ns/language.h +++ b/src/core/hle/service/ns/language.h @@ -5,10 +5,7 @@ #include #include "common/common_types.h" - -namespace Service::Set { -enum class LanguageCode : u64; -} +#include "core/hle/service/set/system_settings.h" namespace Service::NS { /// This is nn::ns::detail::ApplicationLanguage diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index 36cb5d095..f868f9f3e 100755 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp @@ -16,7 +16,7 @@ #include "core/hle/service/ns/ns.h" #include "core/hle/service/ns/pdm_qry.h" #include "core/hle/service/server_manager.h" -#include "core/hle/service/set/set.h" +#include "core/hle/service/set/settings_server.h" namespace Service::NS { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index d791fe7ba..8da61fcc6 100755 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -90,7 +90,7 @@ private: u64_le align; }; }; - static_assert(sizeof(IoctlAllocSpace) == 24, "IoctlInitalizeEx is incorrect size"); + static_assert(sizeof(IoctlAllocSpace) == 24, "IoctlInitializeEx is incorrect size"); struct IoctlFreeSpace { u64_le offset{}; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index 1704e42ff..44bff8d31 100755 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -15,7 +15,7 @@ namespace Service::Nvidia::Devices { nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system_, EventInterface& events_interface_) : nvdevice{system_}, events_interface{events_interface_} { error_notifier_event = events_interface.CreateEvent("CtrlGpuErrorNotifier"); - unknown_event = events_interface.CreateEvent("CtrlGpuUknownEvent"); + unknown_event = events_interface.CreateEvent("CtrlGpuUnknownEvent"); } nvhost_ctrl_gpu::~nvhost_ctrl_gpu() { events_interface.FreeEvent(error_notifier_event); diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h index 472a3458d..daaf5ade2 100755 --- a/src/core/hle/service/nvdrv/nvdata.h +++ b/src/core/hle/service/nvdrv/nvdata.h @@ -51,7 +51,7 @@ enum class NvResult : u32 { DispNoDisplaysAttached = 0x20003, DispModeNotSupported = 0x20004, DispNotFound = 0x20005, - DispAttachDissallowed = 0x20006, + DispAttachDisallowed = 0x20006, DispTypeNotSupported = 0x20007, DispAuthenticationFailed = 0x20008, DispNotAttached = 0x20009, diff --git a/src/core/hle/service/pcv/pcv.cpp b/src/core/hle/service/pcv/pcv.cpp index a265e3ab6..fec718ebd 100755 --- a/src/core/hle/service/pcv/pcv.cpp +++ b/src/core/hle/service/pcv/pcv.cpp @@ -54,8 +54,8 @@ public: class IClkrstSession final : public ServiceFramework { public: - explicit IClkrstSession(Core::System& system_, DeviceCode deivce_code_) - : ServiceFramework{system_, "IClkrstSession"}, deivce_code(deivce_code_) { + explicit IClkrstSession(Core::System& system_, DeviceCode device_code_) + : ServiceFramework{system_, "IClkrstSession"}, device_code(device_code_) { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "SetClockEnabled"}, @@ -93,7 +93,7 @@ private: rb.Push(clock_rate); } - DeviceCode deivce_code; + DeviceCode device_code; u32 clock_rate{}; }; @@ -118,9 +118,9 @@ private: void OpenSession(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_code = static_cast(rp.Pop()); - const auto unkonwn_input = rp.Pop(); + const auto unknown_input = rp.Pop(); - LOG_DEBUG(Service_PCV, "called, device_code={}, input={}", device_code, unkonwn_input); + LOG_DEBUG(Service_PCV, "called, device_code={}, input={}", device_code, unknown_input); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); diff --git a/src/core/hle/service/psc/psc.cpp b/src/core/hle/service/psc/psc.cpp index ec1be0a55..94eaa566a 100755 --- a/src/core/hle/service/psc/psc.cpp +++ b/src/core/hle/service/psc/psc.cpp @@ -4,9 +4,13 @@ #include #include "common/logging/log.h" +#include "core/core.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/psc/psc.h" -#include "core/hle/service/server_manager.h" +#include "core/hle/service/psc/time/manager.h" +#include "core/hle/service/psc/time/power_state_service.h" +#include "core/hle/service/psc/time/service_manager.h" +#include "core/hle/service/psc/time/static.h" #include "core/hle/service/service.h" namespace Service::PSC { @@ -76,6 +80,17 @@ void LoopProcess(Core::System& system) { server_manager->RegisterNamedService("psc:c", std::make_shared(system)); server_manager->RegisterNamedService("psc:m", std::make_shared(system)); + + auto time = std::make_shared(system); + + server_manager->RegisterNamedService( + "time:m", std::make_shared(system, time, server_manager.get())); + server_manager->RegisterNamedService( + "time:su", std::make_shared( + system, Time::StaticServiceSetupInfo{0, 0, 0, 0, 0, 1}, time, "time:su")); + server_manager->RegisterNamedService("time:al", + std::make_shared(system, time)); + ServerManager::RunServer(std::move(server_manager)); } diff --git a/src/core/hle/service/psc/time/alarms.cpp b/src/core/hle/service/psc/time/alarms.cpp new file mode 100755 index 000000000..5e52c19f8 --- /dev/null +++ b/src/core/hle/service/psc/time/alarms.cpp @@ -0,0 +1,209 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/psc/time/alarms.h" +#include "core/hle/service/psc/time/manager.h" + +namespace Service::PSC::Time { +Alarm::Alarm(Core::System& system, KernelHelpers::ServiceContext& ctx, AlarmType type) + : m_ctx{ctx}, m_event{ctx.CreateEvent("Psc:Alarm:Event")} { + m_event->Clear(); + + switch (type) { + case WakeupAlarm: + m_priority = 1; + break; + case BackgroundTaskAlarm: + m_priority = 0; + break; + default: + UNREACHABLE(); + return; + } +} + +Alarm::~Alarm() { + m_ctx.CloseEvent(m_event); +} + +Alarms::Alarms(Core::System& system, StandardSteadyClockCore& steady_clock, + PowerStateRequestManager& power_state_request_manager) + : m_system{system}, m_ctx{system, "Psc:Alarms"}, m_steady_clock{steady_clock}, + m_power_state_request_manager{power_state_request_manager}, m_event{m_ctx.CreateEvent( + "Psc:Alarms:Event")} {} + +Alarms::~Alarms() { + m_ctx.CloseEvent(m_event); +} + +Result Alarms::Enable(Alarm& alarm, s64 time) { + R_UNLESS(m_steady_clock.IsInitialized(), ResultClockUninitialized); + + std::scoped_lock l{m_mutex}; + R_UNLESS(alarm.IsLinked(), ResultAlarmNotRegistered); + + auto time_ns{time + m_steady_clock.GetRawTime()}; + auto one_second_ns{ + std::chrono::duration_cast(std::chrono::seconds(1)).count()}; + time_ns = Common::AlignUp(time_ns, one_second_ns); + alarm.SetAlertTime(time_ns); + + Insert(alarm); + R_RETURN(UpdateClosestAndSignal()); +} + +void Alarms::Disable(Alarm& alarm) { + std::scoped_lock l{m_mutex}; + if (!alarm.IsLinked()) { + return; + } + + Erase(alarm); + UpdateClosestAndSignal(); +} + +void Alarms::CheckAndSignal() { + std::scoped_lock l{m_mutex}; + if (m_alarms.empty()) { + return; + } + + bool alarm_signalled{false}; + for (auto& alarm : m_alarms) { + if (m_steady_clock.GetRawTime() >= alarm.GetAlertTime()) { + alarm.Signal(); + alarm.Lock(); + Erase(alarm); + + m_power_state_request_manager.UpdatePendingPowerStateRequestPriority( + alarm.GetPriority()); + alarm_signalled = true; + } + } + + if (!alarm_signalled) { + return; + } + + m_power_state_request_manager.SignalPowerStateRequestAvailability(); + UpdateClosestAndSignal(); +} + +bool Alarms::GetClosestAlarm(Alarm** out_alarm) { + std::scoped_lock l{m_mutex}; + auto alarm = m_alarms.empty() ? nullptr : std::addressof(m_alarms.front()); + *out_alarm = alarm; + return alarm != nullptr; +} + +void Alarms::Insert(Alarm& alarm) { + // Alarms are sorted by alert time, then priority + auto it{m_alarms.begin()}; + while (it != m_alarms.end()) { + if (alarm.GetAlertTime() < it->GetAlertTime() || + (alarm.GetAlertTime() == it->GetAlertTime() && + alarm.GetPriority() < it->GetPriority())) { + m_alarms.insert(it, alarm); + return; + } + it++; + } + + m_alarms.push_back(alarm); +} + +void Alarms::Erase(Alarm& alarm) { + m_alarms.erase(m_alarms.iterator_to(alarm)); +} + +Result Alarms::UpdateClosestAndSignal() { + m_closest_alarm = m_alarms.empty() ? nullptr : std::addressof(m_alarms.front()); + R_SUCCEED_IF(m_closest_alarm == nullptr); + + m_event->Signal(); + + R_SUCCEED(); +} + +IAlarmService::IAlarmService(Core::System& system_, std::shared_ptr manager) + : ServiceFramework{system_, "time:al"}, m_system{system}, m_alarms{manager->m_alarms} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IAlarmService::CreateWakeupAlarm, "CreateWakeupAlarm"}, + {1, &IAlarmService::CreateBackgroundTaskAlarm, "CreateBackgroundTaskAlarm"}, + }; + // clang-format on + RegisterHandlers(functions); +} + +void IAlarmService::CreateWakeupAlarm(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, m_alarms, AlarmType::WakeupAlarm); +} + +void IAlarmService::CreateBackgroundTaskAlarm(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, m_alarms, AlarmType::BackgroundTaskAlarm); +} + +ISteadyClockAlarm::ISteadyClockAlarm(Core::System& system_, Alarms& alarms, AlarmType type) + : ServiceFramework{system_, "ISteadyClockAlarm"}, m_ctx{system, "Psc:ISteadyClockAlarm"}, + m_alarms{alarms}, m_alarm{system, m_ctx, type} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &ISteadyClockAlarm::GetAlarmEvent, "GetAlarmEvent"}, + {1, &ISteadyClockAlarm::Enable, "Enable"}, + {2, &ISteadyClockAlarm::Disable, "Disable"}, + {3, &ISteadyClockAlarm::IsEnabled, "IsEnabled"}, + {10, nullptr, "CreateWakeLock"}, + {11, nullptr, "DestroyWakeLock"}, + }; + // clang-format on + RegisterHandlers(functions); +} + +void ISteadyClockAlarm::GetAlarmEvent(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(m_alarm.GetEventHandle()); +} + +void ISteadyClockAlarm::Enable(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto time{rp.Pop()}; + + auto res = m_alarms.Enable(m_alarm, time); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ISteadyClockAlarm::Disable(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + m_alarms.Disable(m_alarm); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISteadyClockAlarm::IsEnabled(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(m_alarm.IsLinked()); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/alarms.h b/src/core/hle/service/psc/time/alarms.h new file mode 100755 index 000000000..597770028 --- /dev/null +++ b/src/core/hle/service/psc/time/alarms.h @@ -0,0 +1,139 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/psc/time/clocks/standard_steady_clock_core.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/psc/time/power_state_request_manager.h" +#include "core/hle/service/server_manager.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { +class TimeManager; + +enum AlarmType : u32 { + WakeupAlarm = 0, + BackgroundTaskAlarm = 1, +}; + +struct Alarm : public Common::IntrusiveListBaseNode { + using AlarmList = Common::IntrusiveListBaseTraits::ListType; + + Alarm(Core::System& system, KernelHelpers::ServiceContext& ctx, AlarmType type); + ~Alarm(); + + Kernel::KReadableEvent& GetEventHandle() { + return m_event->GetReadableEvent(); + } + + s64 GetAlertTime() const { + return m_alert_time; + } + + void SetAlertTime(s64 time) { + m_alert_time = time; + } + + u32 GetPriority() const { + return m_priority; + } + + void Signal() { + m_event->Signal(); + } + + Result Lock() { + // TODO + // if (m_lock_service) { + // return m_lock_service->Lock(); + // } + R_SUCCEED(); + } + + KernelHelpers::ServiceContext& m_ctx; + + u32 m_priority; + Kernel::KEvent* m_event{}; + s64 m_alert_time{}; + // TODO + // nn::psc::sf::IPmStateLock* m_lock_service{}; +}; + +class Alarms { +public: + explicit Alarms(Core::System& system, StandardSteadyClockCore& steady_clock, + PowerStateRequestManager& power_state_request_manager); + ~Alarms(); + + Kernel::KEvent& GetEvent() { + return *m_event; + } + + s64 GetRawTime() { + return m_steady_clock.GetRawTime(); + } + + Result Enable(Alarm& alarm, s64 time); + void Disable(Alarm& alarm); + void CheckAndSignal(); + bool GetClosestAlarm(Alarm** out_alarm); + +private: + void Insert(Alarm& alarm); + void Erase(Alarm& alarm); + Result UpdateClosestAndSignal(); + + Core::System& m_system; + KernelHelpers::ServiceContext m_ctx; + + StandardSteadyClockCore& m_steady_clock; + PowerStateRequestManager& m_power_state_request_manager; + Alarm::AlarmList m_alarms; + Kernel::KEvent* m_event{}; + Alarm* m_closest_alarm{}; + std::mutex m_mutex; +}; + +class IAlarmService final : public ServiceFramework { +public: + explicit IAlarmService(Core::System& system, std::shared_ptr manager); + + ~IAlarmService() override = default; + +private: + void CreateWakeupAlarm(HLERequestContext& ctx); + void CreateBackgroundTaskAlarm(HLERequestContext& ctx); + + Core::System& m_system; + Alarms& m_alarms; +}; + +class ISteadyClockAlarm final : public ServiceFramework { +public: + explicit ISteadyClockAlarm(Core::System& system, Alarms& alarms, AlarmType type); + + ~ISteadyClockAlarm() override = default; + +private: + void GetAlarmEvent(HLERequestContext& ctx); + void Enable(HLERequestContext& ctx); + void Disable(HLERequestContext& ctx); + void IsEnabled(HLERequestContext& ctx); + + KernelHelpers::ServiceContext m_ctx; + + Alarms& m_alarms; + Alarm m_alarm; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/context_writers.cpp b/src/core/hle/service/psc/time/clocks/context_writers.cpp new file mode 100755 index 000000000..ac8700f76 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/context_writers.cpp @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/psc/time/clocks/context_writers.h" + +namespace Service::PSC::Time { + +void ContextWriter::SignalAllNodes() { + std::scoped_lock l{m_mutex}; + for (auto& operation : m_operation_events) { + operation.m_event->Signal(); + } +} + +void ContextWriter::Link(OperationEvent& operation_event) { + std::scoped_lock l{m_mutex}; + m_operation_events.push_back(operation_event); +} + +LocalSystemClockContextWriter::LocalSystemClockContextWriter(Core::System& system, + SharedMemory& shared_memory) + : m_system{system}, m_shared_memory{shared_memory} {} + +Result LocalSystemClockContextWriter::Write(SystemClockContext& context) { + if (m_in_use) { + R_SUCCEED_IF(context == m_context); + m_context = context; + } else { + m_context = context; + m_in_use = true; + } + + m_shared_memory.SetLocalSystemContext(context); + + SignalAllNodes(); + + R_SUCCEED(); +} + +NetworkSystemClockContextWriter::NetworkSystemClockContextWriter(Core::System& system, + SharedMemory& shared_memory, + SystemClockCore& system_clock) + : m_system{system}, m_shared_memory{shared_memory}, m_system_clock{system_clock} {} + +Result NetworkSystemClockContextWriter::Write(SystemClockContext& context) { + s64 time{}; + [[maybe_unused]] auto res = m_system_clock.GetCurrentTime(&time); + + if (m_in_use) { + R_SUCCEED_IF(context == m_context); + m_context = context; + } else { + m_context = context; + m_in_use = true; + } + + m_shared_memory.SetNetworkSystemContext(context); + + SignalAllNodes(); + + R_SUCCEED(); +} + +EphemeralNetworkSystemClockContextWriter::EphemeralNetworkSystemClockContextWriter( + Core::System& system) + : m_system{system} {} + +Result EphemeralNetworkSystemClockContextWriter::Write(SystemClockContext& context) { + if (m_in_use) { + R_SUCCEED_IF(context == m_context); + m_context = context; + } else { + m_context = context; + m_in_use = true; + } + + SignalAllNodes(); + + R_SUCCEED(); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/context_writers.h b/src/core/hle/service/psc/time/clocks/context_writers.h new file mode 100755 index 000000000..afd3725d4 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/context_writers.h @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "common/common_types.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/psc/time/clocks/system_clock_core.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/psc/time/shared_memory.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { + +class ContextWriter { +private: + using OperationEventList = Common::IntrusiveListBaseTraits::ListType; + +public: + virtual ~ContextWriter() = default; + + virtual Result Write(SystemClockContext& context) = 0; + void SignalAllNodes(); + void Link(OperationEvent& operation_event); + +private: + OperationEventList m_operation_events; + std::mutex m_mutex; +}; + +class LocalSystemClockContextWriter : public ContextWriter { +public: + explicit LocalSystemClockContextWriter(Core::System& system, SharedMemory& shared_memory); + + Result Write(SystemClockContext& context) override; + +private: + Core::System& m_system; + + SharedMemory& m_shared_memory; + bool m_in_use{}; + SystemClockContext m_context{}; +}; + +class NetworkSystemClockContextWriter : public ContextWriter { +public: + explicit NetworkSystemClockContextWriter(Core::System& system, SharedMemory& shared_memory, + SystemClockCore& system_clock); + + Result Write(SystemClockContext& context) override; + +private: + Core::System& m_system; + + SharedMemory& m_shared_memory; + bool m_in_use{}; + SystemClockContext m_context{}; + SystemClockCore& m_system_clock; +}; + +class EphemeralNetworkSystemClockContextWriter : public ContextWriter { +public: + EphemeralNetworkSystemClockContextWriter(Core::System& system); + + Result Write(SystemClockContext& context) override; + +private: + Core::System& m_system; + + bool m_in_use{}; + SystemClockContext m_context{}; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h b/src/core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h new file mode 100755 index 000000000..0a68867d9 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/result.h" +#include "core/hle/service/psc/time/clocks/context_writers.h" +#include "core/hle/service/psc/time/clocks/steady_clock_core.h" +#include "core/hle/service/psc/time/clocks/system_clock_core.h" +#include "core/hle/service/psc/time/common.h" + +namespace Service::PSC::Time { + +class EphemeralNetworkSystemClockCore : public SystemClockCore { +public: + explicit EphemeralNetworkSystemClockCore(SteadyClockCore& steady_clock) + : SystemClockCore{steady_clock} {} + ~EphemeralNetworkSystemClockCore() override = default; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp new file mode 100755 index 000000000..36dca6689 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h" + +namespace Service::PSC::Time { + +void StandardLocalSystemClockCore::Initialize(SystemClockContext& context, s64 time) { + SteadyClockTimePoint time_point{}; + if (GetCurrentTimePoint(time_point) == ResultSuccess && + context.steady_time_point.IdMatches(time_point)) { + SetContextAndWrite(context); + } else if (SetCurrentTime(time) != ResultSuccess) { + LOG_ERROR(Service_Time, "Failed to SetCurrentTime"); + } + + SetInitialized(); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h new file mode 100755 index 000000000..176ba3e94 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/result.h" +#include "core/hle/service/psc/time/clocks/context_writers.h" +#include "core/hle/service/psc/time/clocks/steady_clock_core.h" +#include "core/hle/service/psc/time/clocks/system_clock_core.h" +#include "core/hle/service/psc/time/common.h" + +namespace Service::PSC::Time { + +class StandardLocalSystemClockCore : public SystemClockCore { +public: + explicit StandardLocalSystemClockCore(SteadyClockCore& steady_clock) + : SystemClockCore{steady_clock} {} + ~StandardLocalSystemClockCore() override = default; + + void Initialize(SystemClockContext& context, s64 time); +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp new file mode 100755 index 000000000..8d6cb7db1 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h" + +namespace Service::PSC::Time { + +void StandardNetworkSystemClockCore::Initialize(SystemClockContext& context, s64 accuracy) { + if (SetContextAndWrite(context) != ResultSuccess) { + LOG_ERROR(Service_Time, "Failed to SetContext"); + } + m_sufficient_accuracy = accuracy; + SetInitialized(); +} + +bool StandardNetworkSystemClockCore::IsAccuracySufficient() { + if (!IsInitialized()) { + return false; + } + + SystemClockContext context{}; + SteadyClockTimePoint current_time_point{}; + if (GetCurrentTimePoint(current_time_point) != ResultSuccess || + GetContext(context) != ResultSuccess) { + return false; + } + + s64 seconds{}; + if (GetSpanBetweenTimePoints(&seconds, context.steady_time_point, current_time_point) != + ResultSuccess) { + return false; + } + + if (std::chrono::duration_cast(std::chrono::seconds(seconds)) + .count() < m_sufficient_accuracy) { + return true; + } + + return false; +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h new file mode 100755 index 000000000..933d2c8e3 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "core/hle/result.h" +#include "core/hle/service/psc/time/clocks/context_writers.h" +#include "core/hle/service/psc/time/clocks/steady_clock_core.h" +#include "core/hle/service/psc/time/clocks/system_clock_core.h" +#include "core/hle/service/psc/time/common.h" + +namespace Service::PSC::Time { + +class StandardNetworkSystemClockCore : public SystemClockCore { +public: + explicit StandardNetworkSystemClockCore(SteadyClockCore& steady_clock) + : SystemClockCore{steady_clock} {} + ~StandardNetworkSystemClockCore() override = default; + + void Initialize(SystemClockContext& context, s64 accuracy); + bool IsAccuracySufficient(); + +private: + s64 m_sufficient_accuracy{ + std::chrono ::duration_cast(std::chrono::days(10)).count()}; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.cpp new file mode 100755 index 000000000..ffb8b0bf0 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.cpp @@ -0,0 +1,100 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/service/psc/time/clocks/standard_steady_clock_core.h" + +namespace Service::PSC::Time { + +void StandardSteadyClockCore::Initialize(ClockSourceId clock_source_id, s64 rtc_offset, + s64 internal_offset, s64 test_offset, + bool is_rtc_reset_detected) { + m_clock_source_id = clock_source_id; + m_rtc_offset = rtc_offset; + m_internal_offset = internal_offset; + m_test_offset = test_offset; + if (is_rtc_reset_detected) { + SetResetDetected(); + } + SetInitialized(); +} + +void StandardSteadyClockCore::SetRtcOffset(s64 offset) { + m_rtc_offset = offset; +} + +void StandardSteadyClockCore::SetContinuousAdjustment(ClockSourceId clock_source_id, s64 time) { + auto ticks{m_system.CoreTiming().GetClockTicks()}; + + m_continuous_adjustment_time_point.rtc_offset = ConvertToTimeSpan(ticks).count(); + m_continuous_adjustment_time_point.diff_scale = 0; + m_continuous_adjustment_time_point.shift_amount = 0; + m_continuous_adjustment_time_point.lower = time; + m_continuous_adjustment_time_point.upper = time; + m_continuous_adjustment_time_point.clock_source_id = clock_source_id; +} + +void StandardSteadyClockCore::GetContinuousAdjustment( + ContinuousAdjustmentTimePoint& out_time_point) const { + out_time_point = m_continuous_adjustment_time_point; +} + +void StandardSteadyClockCore::UpdateContinuousAdjustmentTime(s64 in_time) { + auto ticks{m_system.CoreTiming().GetClockTicks()}; + auto global_time_ns{ConvertToTimeSpan(ticks).count()}; + auto expected_time{((global_time_ns - m_continuous_adjustment_time_point.rtc_offset) * + m_continuous_adjustment_time_point.diff_scale) >> + m_continuous_adjustment_time_point.shift_amount}; + + auto last_time_point{m_continuous_adjustment_time_point.upper}; + m_continuous_adjustment_time_point.upper = in_time; + auto t1{std::min(expected_time, last_time_point)}; + expected_time = std::max(expected_time, last_time_point); + expected_time = m_continuous_adjustment_time_point.diff_scale >= 0 ? t1 : expected_time; + + auto new_diff{in_time < expected_time ? -55 : 55}; + + m_continuous_adjustment_time_point.rtc_offset = global_time_ns; + m_continuous_adjustment_time_point.shift_amount = expected_time == in_time ? 0 : 14; + m_continuous_adjustment_time_point.diff_scale = expected_time == in_time ? 0 : new_diff; + m_continuous_adjustment_time_point.lower = expected_time; +} + +Result StandardSteadyClockCore::GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) { + auto current_time_ns = GetCurrentRawTimePointImpl(); + auto current_time_s = + std::chrono::duration_cast(std::chrono::nanoseconds(current_time_ns)); + out_time_point.time_point = current_time_s.count(); + out_time_point.clock_source_id = m_clock_source_id; + R_SUCCEED(); +} + +s64 StandardSteadyClockCore::GetCurrentRawTimePointImpl() { + std::scoped_lock l{m_mutex}; + auto ticks{static_cast(m_system.CoreTiming().GetClockTicks())}; + auto current_time_ns = m_rtc_offset + ConvertToTimeSpan(ticks).count(); + auto time_point = std::max(current_time_ns, m_cached_time_point); + m_cached_time_point = time_point; + return time_point; +} + +s64 StandardSteadyClockCore::GetTestOffsetImpl() const { + return m_test_offset; +} + +void StandardSteadyClockCore::SetTestOffsetImpl(s64 offset) { + m_test_offset = offset; +} + +s64 StandardSteadyClockCore::GetInternalOffsetImpl() const { + return m_internal_offset; +} + +void StandardSteadyClockCore::SetInternalOffsetImpl(s64 offset) { + m_internal_offset = offset; +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.h new file mode 100755 index 000000000..bbf98fcd5 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.h @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "core/hle/service/psc/time/clocks/steady_clock_core.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { +class StandardSteadyClockCore : public SteadyClockCore { +public: + explicit StandardSteadyClockCore(Core::System& system) : m_system{system} {} + ~StandardSteadyClockCore() override = default; + + void Initialize(ClockSourceId clock_source_id, s64 rtc_offset, s64 internal_offset, + s64 test_offset, bool is_rtc_reset_detected); + + void SetRtcOffset(s64 offset); + void SetContinuousAdjustment(ClockSourceId clock_source_id, s64 time); + void GetContinuousAdjustment(ContinuousAdjustmentTimePoint& out_time_point) const; + void UpdateContinuousAdjustmentTime(s64 time); + + Result GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) override; + s64 GetCurrentRawTimePointImpl() override; + s64 GetTestOffsetImpl() const override; + void SetTestOffsetImpl(s64 offset) override; + s64 GetInternalOffsetImpl() const override; + void SetInternalOffsetImpl(s64 offset) override; + + Result GetRtcValueImpl(s64& out_value) override { + R_RETURN(ResultNotImplemented); + } + + Result GetSetupResultValueImpl() override { + R_SUCCEED(); + } + +private: + Core::System& m_system; + + std::mutex m_mutex; + s64 m_test_offset{}; + s64 m_internal_offset{}; + ClockSourceId m_clock_source_id{}; + s64 m_rtc_offset{}; + s64 m_cached_time_point{}; + ContinuousAdjustmentTimePoint m_continuous_adjustment_time_point{}; +}; +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp new file mode 100755 index 000000000..4ae95131e --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp @@ -0,0 +1,66 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/psc/time/clocks/standard_user_system_clock_core.h" + +namespace Service::PSC::Time { + +StandardUserSystemClockCore::StandardUserSystemClockCore( + Core::System& system, StandardLocalSystemClockCore& local_clock, + StandardNetworkSystemClockCore& network_clock) + : SystemClockCore{local_clock.GetSteadyClock()}, m_system{system}, + m_ctx{m_system, "Psc:StandardUserSystemClockCore"}, m_local_system_clock{local_clock}, + m_network_system_clock{network_clock}, m_event{m_ctx.CreateEvent( + "Psc:StandardUserSystemClockCore:Event")} {} + +StandardUserSystemClockCore::~StandardUserSystemClockCore() { + m_ctx.CloseEvent(m_event); +} + +Result StandardUserSystemClockCore::SetAutomaticCorrection(bool automatic_correction) { + if (m_automatic_correction == automatic_correction || + !m_network_system_clock.CheckClockSourceMatches()) { + m_automatic_correction = automatic_correction; + R_SUCCEED(); + } + + SystemClockContext context{}; + R_TRY(m_network_system_clock.GetContext(context)); + R_TRY(m_local_system_clock.SetContextAndWrite(context)); + + m_automatic_correction = automatic_correction; + R_SUCCEED(); +} + +Result StandardUserSystemClockCore::GetContext(SystemClockContext& out_context) const { + if (!m_automatic_correction) { + R_RETURN(m_local_system_clock.GetContext(out_context)); + } + + if (!m_network_system_clock.CheckClockSourceMatches()) { + R_RETURN(m_local_system_clock.GetContext(out_context)); + } + + SystemClockContext context{}; + R_TRY(m_network_system_clock.GetContext(context)); + R_TRY(m_local_system_clock.SetContextAndWrite(context)); + + R_RETURN(m_local_system_clock.GetContext(out_context)); +} + +Result StandardUserSystemClockCore::SetContext(SystemClockContext& context) { + R_RETURN(ResultNotImplemented); +} + +Result StandardUserSystemClockCore::GetTimePoint(SteadyClockTimePoint& out_time_point) { + out_time_point = m_time_point; + R_SUCCEED(); +} + +void StandardUserSystemClockCore::SetTimePointAndSignal(SteadyClockTimePoint& time_point) { + m_time_point = time_point; + m_event->Signal(); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h new file mode 100755 index 000000000..a7fe7648d --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/kernel/k_event.h" +#include "core/hle/result.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/psc/time/clocks/context_writers.h" +#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h" +#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h" +#include "core/hle/service/psc/time/clocks/steady_clock_core.h" +#include "core/hle/service/psc/time/clocks/system_clock_core.h" +#include "core/hle/service/psc/time/common.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { + +class StandardUserSystemClockCore : public SystemClockCore { +public: + explicit StandardUserSystemClockCore(Core::System& system, + StandardLocalSystemClockCore& local_clock, + StandardNetworkSystemClockCore& network_clock); + ~StandardUserSystemClockCore() override; + + Kernel::KEvent& GetEvent() { + return *m_event; + } + + bool GetAutomaticCorrection() const { + return m_automatic_correction; + } + Result SetAutomaticCorrection(bool automatic_correction); + + Result GetContext(SystemClockContext& out_context) const override; + Result SetContext(SystemClockContext& context) override; + + Result GetTimePoint(SteadyClockTimePoint& out_time_point); + void SetTimePointAndSignal(SteadyClockTimePoint& time_point); + +private: + Core::System& m_system; + KernelHelpers::ServiceContext m_ctx; + + bool m_automatic_correction{}; + StandardLocalSystemClockCore& m_local_system_clock; + StandardNetworkSystemClockCore& m_network_system_clock; + SteadyClockTimePoint m_time_point{}; + Kernel::KEvent* m_event{}; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/steady_clock_core.h b/src/core/hle/service/psc/time/clocks/steady_clock_core.h new file mode 100755 index 000000000..f933cc15f --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/steady_clock_core.h @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "core/hle/result.h" +#include "core/hle/service/psc/time/common.h" + +namespace Service::PSC::Time { + +class SteadyClockCore { +public: + SteadyClockCore() = default; + virtual ~SteadyClockCore() = default; + + void SetInitialized() { + m_initialized = true; + } + + bool IsInitialized() const { + return m_initialized; + } + + void SetResetDetected() { + m_reset_detected = true; + } + + bool IsResetDetected() const { + return m_reset_detected; + } + + Result GetCurrentTimePoint(SteadyClockTimePoint& out_time_point) { + R_TRY(GetCurrentTimePointImpl(out_time_point)); + + auto one_second_ns{ + std::chrono::duration_cast(std::chrono::seconds(1)).count()}; + out_time_point.time_point += GetTestOffsetImpl() / one_second_ns; + out_time_point.time_point += GetInternalOffsetImpl() / one_second_ns; + R_SUCCEED(); + } + + s64 GetTestOffset() const { + return GetTestOffsetImpl(); + } + + void SetTestOffset(s64 offset) { + SetTestOffsetImpl(offset); + } + + s64 GetInternalOffset() const { + return GetInternalOffsetImpl(); + } + + s64 GetRawTime() { + return GetCurrentRawTimePointImpl() + GetTestOffsetImpl() + GetInternalOffsetImpl(); + } + + Result GetRtcValue(s64& out_value) { + R_RETURN(GetRtcValueImpl(out_value)); + } + + Result GetSetupResultValue() { + R_RETURN(GetSetupResultValueImpl()); + } + +private: + virtual Result GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) = 0; + virtual s64 GetCurrentRawTimePointImpl() = 0; + virtual s64 GetTestOffsetImpl() const = 0; + virtual void SetTestOffsetImpl(s64 offset) = 0; + virtual s64 GetInternalOffsetImpl() const = 0; + virtual void SetInternalOffsetImpl(s64 offset) = 0; + virtual Result GetRtcValueImpl(s64& out_value) = 0; + virtual Result GetSetupResultValueImpl() = 0; + + bool m_initialized{}; + bool m_reset_detected{}; +}; +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/system_clock_core.cpp new file mode 100755 index 000000000..c507ef517 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/system_clock_core.cpp @@ -0,0 +1,75 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/psc/time/clocks/context_writers.h" +#include "core/hle/service/psc/time/clocks/system_clock_core.h" + +namespace Service::PSC::Time { + +bool SystemClockCore::CheckClockSourceMatches() { + SystemClockContext context{}; + if (GetContext(context) != ResultSuccess) { + return false; + } + + SteadyClockTimePoint time_point{}; + if (m_steady_clock.GetCurrentTimePoint(time_point) != ResultSuccess) { + return false; + } + + return context.steady_time_point.IdMatches(time_point); +} + +Result SystemClockCore::GetCurrentTime(s64* out_time) const { + R_UNLESS(out_time != nullptr, ResultInvalidArgument); + + SystemClockContext context{}; + SteadyClockTimePoint time_point{}; + + R_TRY(m_steady_clock.GetCurrentTimePoint(time_point)); + R_TRY(GetContext(context)); + + R_UNLESS(context.steady_time_point.IdMatches(time_point), ResultClockMismatch); + + *out_time = context.offset + time_point.time_point; + R_SUCCEED(); +} + +Result SystemClockCore::SetCurrentTime(s64 time) { + SteadyClockTimePoint time_point{}; + R_TRY(m_steady_clock.GetCurrentTimePoint(time_point)); + + SystemClockContext context{ + .offset = time - time_point.time_point, + .steady_time_point = time_point, + }; + R_RETURN(SetContextAndWrite(context)); +} + +Result SystemClockCore::GetContext(SystemClockContext& out_context) const { + out_context = m_context; + R_SUCCEED(); +} + +Result SystemClockCore::SetContext(SystemClockContext& context) { + m_context = context; + R_SUCCEED(); +} + +Result SystemClockCore::SetContextAndWrite(SystemClockContext& context) { + R_TRY(SetContext(context)); + + if (m_context_writer) { + R_RETURN(m_context_writer->Write(context)); + } + + R_SUCCEED(); +} + +void SystemClockCore::LinkOperationEvent(OperationEvent& operation_event) { + if (m_context_writer) { + m_context_writer->Link(operation_event); + } +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/system_clock_core.h b/src/core/hle/service/psc/time/clocks/system_clock_core.h new file mode 100755 index 000000000..73811712e --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/system_clock_core.h @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/result.h" +#include "core/hle/service/psc/time/clocks/steady_clock_core.h" +#include "core/hle/service/psc/time/common.h" + +namespace Service::PSC::Time { +class ContextWriter; + +class SystemClockCore { +public: + explicit SystemClockCore(SteadyClockCore& steady_clock) : m_steady_clock{steady_clock} {} + virtual ~SystemClockCore() = default; + + SteadyClockCore& GetSteadyClock() { + return m_steady_clock; + } + + bool IsInitialized() const { + return m_initialized; + } + + void SetInitialized() { + m_initialized = true; + } + + void SetContextWriter(ContextWriter& context_writer) { + m_context_writer = &context_writer; + } + + bool CheckClockSourceMatches(); + + Result GetCurrentTime(s64* out_time) const; + Result SetCurrentTime(s64 time); + + Result GetCurrentTimePoint(SteadyClockTimePoint& out_time_point) { + R_RETURN(m_steady_clock.GetCurrentTimePoint(out_time_point)); + } + + virtual Result GetContext(SystemClockContext& out_context) const; + virtual Result SetContext(SystemClockContext& context); + Result SetContextAndWrite(SystemClockContext& context); + + void LinkOperationEvent(OperationEvent& operation_event); + +private: + bool m_initialized{}; + ContextWriter* m_context_writer{}; + SteadyClockCore& m_steady_clock; + SystemClockContext m_context{}; +}; +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp b/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp new file mode 100755 index 000000000..22da1fbcc --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.cpp @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h" + +namespace Service::PSC::Time { + +Result TickBasedSteadyClockCore::GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) { + auto ticks{m_system.CoreTiming().GetClockTicks()}; + auto current_time_s = + std::chrono::duration_cast(ConvertToTimeSpan(ticks)).count(); + out_time_point.time_point = current_time_s; + out_time_point.clock_source_id = m_clock_source_id; + R_SUCCEED(); +} + +s64 TickBasedSteadyClockCore::GetCurrentRawTimePointImpl() { + SteadyClockTimePoint time_point{}; + if (GetCurrentTimePointImpl(time_point) != ResultSuccess) { + LOG_ERROR(Service_Time, "Failed to GetCurrentTimePoint!"); + } + return std::chrono::duration_cast( + std::chrono::seconds(time_point.time_point)) + .count(); +} + +s64 TickBasedSteadyClockCore::GetTestOffsetImpl() const { + return 0; +} + +void TickBasedSteadyClockCore::SetTestOffsetImpl(s64 offset) {} + +s64 TickBasedSteadyClockCore::GetInternalOffsetImpl() const { + return 0; +} + +void TickBasedSteadyClockCore::SetInternalOffsetImpl(s64 offset) {} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h b/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h new file mode 100755 index 000000000..a7bea86a2 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "common/uuid.h" +#include "core/hle/service/psc/time/clocks/steady_clock_core.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { +class TickBasedSteadyClockCore : public SteadyClockCore { +public: + explicit TickBasedSteadyClockCore(Core::System& system) : m_system{system} {} + ~TickBasedSteadyClockCore() override = default; + + Result GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) override; + s64 GetCurrentRawTimePointImpl() override; + s64 GetTestOffsetImpl() const override; + void SetTestOffsetImpl(s64 offset) override; + s64 GetInternalOffsetImpl() const override; + void SetInternalOffsetImpl(s64 offset) override; + + Result GetRtcValueImpl(s64& out_value) override { + R_RETURN(ResultNotImplemented); + } + + Result GetSetupResultValueImpl() override { + R_SUCCEED(); + } + +private: + Core::System& m_system; + + ClockSourceId m_clock_source_id{Common::UUID::MakeRandom()}; +}; +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/common.cpp b/src/core/hle/service/psc/time/common.cpp new file mode 100755 index 000000000..a6d9f02ca --- /dev/null +++ b/src/core/hle/service/psc/time/common.cpp @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/psc/time/common.h" + +namespace Service::PSC::Time { +OperationEvent::OperationEvent(Core::System& system) + : m_ctx{system, "Time:OperationEvent"}, m_event{ + m_ctx.CreateEvent("Time:OperationEvent:Event")} {} + +OperationEvent::~OperationEvent() { + m_ctx.CloseEvent(m_event); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/common.h b/src/core/hle/service/psc/time/common.h new file mode 100755 index 000000000..d17b31143 --- /dev/null +++ b/src/core/hle/service/psc/time/common.h @@ -0,0 +1,168 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#include "common/common_types.h" +#include "common/intrusive_list.h" +#include "common/uuid.h" +#include "common/wall_clock.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/psc/time/errors.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { +using ClockSourceId = Common::UUID; + +struct SteadyClockTimePoint { + constexpr bool IdMatches(SteadyClockTimePoint& other) { + return clock_source_id == other.clock_source_id; + } + bool operator==(const SteadyClockTimePoint& other) const = default; + + s64 time_point; + ClockSourceId clock_source_id; +}; +static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint has the wrong size!"); +static_assert(std::is_trivial_v); + +struct SystemClockContext { + bool operator==(const SystemClockContext& other) const = default; + + s64 offset; + SteadyClockTimePoint steady_time_point; +}; +static_assert(sizeof(SystemClockContext) == 0x20, "SystemClockContext has the wrong size!"); +static_assert(std::is_trivial_v); + +enum class TimeType : u8 { + UserSystemClock, + NetworkSystemClock, + LocalSystemClock, +}; + +struct CalendarTime { + s16 year; + s8 month; + s8 day; + s8 hour; + s8 minute; + s8 second; +}; +static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime has the wrong size!"); + +struct CalendarAdditionalInfo { + s32 day_of_week; + s32 day_of_year; + std::array name; + s32 is_dst; + s32 ut_offset; +}; +static_assert(sizeof(CalendarAdditionalInfo) == 0x18, "CalendarAdditionalInfo has the wrong size!"); + +struct LocationName { + std::array name; +}; +static_assert(sizeof(LocationName) == 0x24, "LocationName has the wrong size!"); + +struct RuleVersion { + std::array version; +}; +static_assert(sizeof(RuleVersion) == 0x10, "RuleVersion has the wrong size!"); + +struct ClockSnapshot { + SystemClockContext user_context; + SystemClockContext network_context; + s64 user_time; + s64 network_time; + CalendarTime user_calendar_time; + CalendarTime network_calendar_time; + CalendarAdditionalInfo user_calendar_additional_time; + CalendarAdditionalInfo network_calendar_additional_time; + SteadyClockTimePoint steady_clock_time_point; + LocationName location_name; + bool is_automatic_correction_enabled; + TimeType type; + u16 unk_CE; +}; +static_assert(sizeof(ClockSnapshot) == 0xD0, "ClockSnapshot has the wrong size!"); +static_assert(std::is_trivial_v); + +struct ContinuousAdjustmentTimePoint { + s64 rtc_offset; + s64 diff_scale; + s64 shift_amount; + s64 lower; + s64 upper; + ClockSourceId clock_source_id; +}; +static_assert(sizeof(ContinuousAdjustmentTimePoint) == 0x38, + "ContinuousAdjustmentTimePoint has the wrong size!"); +static_assert(std::is_trivial_v); + +struct AlarmInfo { + s64 alert_time; + u32 priority; +}; +static_assert(sizeof(AlarmInfo) == 0x10, "AlarmInfo has the wrong size!"); + +struct StaticServiceSetupInfo { + bool can_write_local_clock; + bool can_write_user_clock; + bool can_write_network_clock; + bool can_write_timezone_device_location; + bool can_write_steady_clock; + bool can_write_uninitialized_clock; +}; +static_assert(sizeof(StaticServiceSetupInfo) == 0x6, "StaticServiceSetupInfo has the wrong size!"); + +struct OperationEvent : public Common::IntrusiveListBaseNode { + using OperationEventList = Common::IntrusiveListBaseTraits::ListType; + + OperationEvent(Core::System& system); + ~OperationEvent(); + + KernelHelpers::ServiceContext m_ctx; + Kernel::KEvent* m_event{}; +}; + +constexpr inline std::chrono::nanoseconds ConvertToTimeSpan(s64 ticks) { + constexpr auto one_second_ns{ + std::chrono::duration_cast(std::chrono::seconds(1)).count()}; + + constexpr s64 max{Common::WallClock::CNTFRQ * + (std::numeric_limits::max() / one_second_ns)}; + + if (ticks > max) { + return std::chrono::nanoseconds(std::numeric_limits::max()); + } else if (ticks < -max) { + return std::chrono::nanoseconds(std::numeric_limits::min()); + } + + auto a{ticks / Common::WallClock::CNTFRQ * one_second_ns}; + auto b{((ticks % Common::WallClock::CNTFRQ) * one_second_ns) / Common::WallClock::CNTFRQ}; + + return std::chrono::nanoseconds(a + b); +} + +constexpr inline Result GetSpanBetweenTimePoints(s64* out_seconds, SteadyClockTimePoint& a, + SteadyClockTimePoint& b) { + R_UNLESS(out_seconds, ResultInvalidArgument); + R_UNLESS(a.IdMatches(b), ResultInvalidArgument); + R_UNLESS(a.time_point >= 0 || b.time_point <= a.time_point + std::numeric_limits::max(), + ResultOverflow); + R_UNLESS(a.time_point < 0 || b.time_point >= a.time_point + std::numeric_limits::min(), + ResultOverflow); + + *out_seconds = b.time_point - a.time_point; + R_SUCCEED(); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/errors.h b/src/core/hle/service/psc/time/errors.h new file mode 100755 index 000000000..6d833a006 --- /dev/null +++ b/src/core/hle/service/psc/time/errors.h @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/result.h" + +namespace Service::PSC::Time { + +constexpr Result ResultPermissionDenied{ErrorModule::Time, 1}; +constexpr Result ResultClockMismatch{ErrorModule::Time, 102}; +constexpr Result ResultClockUninitialized{ErrorModule::Time, 103}; +constexpr Result ResultTimeNotFound{ErrorModule::Time, 200}; +constexpr Result ResultOverflow{ErrorModule::Time, 201}; +constexpr Result ResultFailed{ErrorModule::Time, 801}; +constexpr Result ResultInvalidArgument{ErrorModule::Time, 901}; +constexpr Result ResultTimeZoneOutOfRange{ErrorModule::Time, 902}; +constexpr Result ResultTimeZoneParseFailed{ErrorModule::Time, 903}; +constexpr Result ResultRtcTimeout{ErrorModule::Time, 988}; +constexpr Result ResultTimeZoneNotFound{ErrorModule::Time, 989}; +constexpr Result ResultNotImplemented{ErrorModule::Time, 990}; +constexpr Result ResultAlarmNotRegistered{ErrorModule::Time, 1502}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/manager.h b/src/core/hle/service/psc/time/manager.h new file mode 100755 index 000000000..62ded1247 --- /dev/null +++ b/src/core/hle/service/psc/time/manager.h @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/psc/time/alarms.h" +#include "core/hle/service/psc/time/clocks/context_writers.h" +#include "core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h" +#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h" +#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h" +#include "core/hle/service/psc/time/clocks/standard_steady_clock_core.h" +#include "core/hle/service/psc/time/clocks/standard_user_system_clock_core.h" +#include "core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h" +#include "core/hle/service/psc/time/power_state_request_manager.h" +#include "core/hle/service/psc/time/shared_memory.h" +#include "core/hle/service/psc/time/time_zone.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { +class TimeManager { +public: + explicit TimeManager(Core::System& system) + : m_system{system}, m_standard_steady_clock{system}, m_tick_based_steady_clock{m_system}, + m_standard_local_system_clock{m_standard_steady_clock}, + m_standard_network_system_clock{m_standard_steady_clock}, + m_standard_user_system_clock{m_system, m_standard_local_system_clock, + m_standard_network_system_clock}, + m_ephemeral_network_clock{m_tick_based_steady_clock}, m_shared_memory{m_system}, + m_power_state_request_manager{m_system}, m_alarms{m_system, m_standard_steady_clock, + m_power_state_request_manager}, + m_local_system_clock_context_writer{m_system, m_shared_memory}, + m_network_system_clock_context_writer{m_system, m_shared_memory, + m_standard_user_system_clock}, + m_ephemeral_network_clock_context_writer{m_system} {} + + Core::System& m_system; + + StandardSteadyClockCore m_standard_steady_clock; + TickBasedSteadyClockCore m_tick_based_steady_clock; + StandardLocalSystemClockCore m_standard_local_system_clock; + StandardNetworkSystemClockCore m_standard_network_system_clock; + StandardUserSystemClockCore m_standard_user_system_clock; + EphemeralNetworkSystemClockCore m_ephemeral_network_clock; + TimeZone m_time_zone; + SharedMemory m_shared_memory; + PowerStateRequestManager m_power_state_request_manager; + Alarms m_alarms; + LocalSystemClockContextWriter m_local_system_clock_context_writer; + NetworkSystemClockContextWriter m_network_system_clock_context_writer; + EphemeralNetworkSystemClockContextWriter m_ephemeral_network_clock_context_writer; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/power_state_request_manager.cpp b/src/core/hle/service/psc/time/power_state_request_manager.cpp new file mode 100755 index 000000000..17de0bf4d --- /dev/null +++ b/src/core/hle/service/psc/time/power_state_request_manager.cpp @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/psc/time/power_state_request_manager.h" + +namespace Service::PSC::Time { + +PowerStateRequestManager::PowerStateRequestManager(Core::System& system) + : m_system{system}, m_ctx{system, "Psc:PowerStateRequestManager"}, + m_event{m_ctx.CreateEvent("Psc:PowerStateRequestManager:Event")} {} + +PowerStateRequestManager::~PowerStateRequestManager() { + m_ctx.CloseEvent(m_event); +} + +void PowerStateRequestManager::UpdatePendingPowerStateRequestPriority(u32 priority) { + std::scoped_lock l{m_mutex}; + if (m_has_pending_request) { + m_pending_request_priority = std::max(m_pending_request_priority, priority); + } else { + m_pending_request_priority = priority; + m_has_pending_request = true; + } +} + +void PowerStateRequestManager::SignalPowerStateRequestAvailability() { + std::scoped_lock l{m_mutex}; + if (m_has_pending_request) { + if (!m_has_available_request) { + m_has_available_request = true; + } + m_has_pending_request = false; + m_available_request_priority = m_pending_request_priority; + m_event->Signal(); + } +} + +bool PowerStateRequestManager::GetAndClearPowerStateRequest(u32& out_priority) { + std::scoped_lock l{m_mutex}; + auto had_request{m_has_available_request}; + if (m_has_available_request) { + out_priority = m_available_request_priority; + m_has_available_request = false; + m_event->Clear(); + } + return had_request; +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/power_state_request_manager.h b/src/core/hle/service/psc/time/power_state_request_manager.h new file mode 100755 index 000000000..30a0c947d --- /dev/null +++ b/src/core/hle/service/psc/time/power_state_request_manager.h @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/kernel_helpers.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { + +class PowerStateRequestManager { +public: + explicit PowerStateRequestManager(Core::System& system); + ~PowerStateRequestManager(); + + Kernel::KReadableEvent& GetReadableEvent() { + return m_event->GetReadableEvent(); + } + + void UpdatePendingPowerStateRequestPriority(u32 priority); + void SignalPowerStateRequestAvailability(); + bool GetAndClearPowerStateRequest(u32& out_priority); + +private: + Core::System& m_system; + KernelHelpers::ServiceContext m_ctx; + + Kernel::KEvent* m_event{}; + bool m_has_pending_request{}; + u32 m_pending_request_priority{}; + bool m_has_available_request{}; + u32 m_available_request_priority{}; + std::mutex m_mutex; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/power_state_service.cpp b/src/core/hle/service/psc/time/power_state_service.cpp new file mode 100755 index 000000000..b0ae71bf9 --- /dev/null +++ b/src/core/hle/service/psc/time/power_state_service.cpp @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/psc/time/power_state_service.h" + +namespace Service::PSC::Time { + +IPowerStateRequestHandler::IPowerStateRequestHandler( + Core::System& system_, PowerStateRequestManager& power_state_request_manager) + : ServiceFramework{system_, "time:p"}, m_system{system}, m_power_state_request_manager{ + power_state_request_manager} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IPowerStateRequestHandler::GetPowerStateRequestEventReadableHandle, "GetPowerStateRequestEventReadableHandle"}, + {1, &IPowerStateRequestHandler::GetAndClearPowerStateRequest, "GetAndClearPowerStateRequest"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +void IPowerStateRequestHandler::GetPowerStateRequestEventReadableHandle(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(m_power_state_request_manager.GetReadableEvent()); +} + +void IPowerStateRequestHandler::GetAndClearPowerStateRequest(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + u32 priority{}; + auto cleared = m_power_state_request_manager.GetAndClearPowerStateRequest(priority); + + if (cleared) { + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(priority); + rb.Push(cleared); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(cleared); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/power_state_service.h b/src/core/hle/service/psc/time/power_state_service.h new file mode 100755 index 000000000..3ebfddb79 --- /dev/null +++ b/src/core/hle/service/psc/time/power_state_service.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/psc/time/power_state_request_manager.h" +#include "core/hle/service/server_manager.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { + +class IPowerStateRequestHandler final : public ServiceFramework { +public: + explicit IPowerStateRequestHandler(Core::System& system, + PowerStateRequestManager& power_state_request_manager); + + ~IPowerStateRequestHandler() override = default; + +private: + void GetPowerStateRequestEventReadableHandle(HLERequestContext& ctx); + void GetAndClearPowerStateRequest(HLERequestContext& ctx); + + Core::System& m_system; + PowerStateRequestManager& m_power_state_request_manager; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/service_manager.cpp b/src/core/hle/service/psc/time/service_manager.cpp new file mode 100755 index 000000000..17231d14e --- /dev/null +++ b/src/core/hle/service/psc/time/service_manager.cpp @@ -0,0 +1,462 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/service/psc/time/power_state_service.h" +#include "core/hle/service/psc/time/service_manager.h" +#include "core/hle/service/psc/time/static.h" + +namespace Service::PSC::Time { + +ServiceManager::ServiceManager(Core::System& system_, std::shared_ptr time, + ServerManager* server_manager) + : ServiceFramework{system_, "time:m"}, m_system{system}, m_time{std::move(time)}, + m_server_manager{*server_manager}, + m_local_system_clock{m_time->m_standard_local_system_clock}, + m_user_system_clock{m_time->m_standard_user_system_clock}, + m_network_system_clock{m_time->m_standard_network_system_clock}, + m_steady_clock{m_time->m_standard_steady_clock}, m_time_zone{m_time->m_time_zone}, + m_ephemeral_network_clock{m_time->m_ephemeral_network_clock}, + m_shared_memory{m_time->m_shared_memory}, m_alarms{m_time->m_alarms}, + m_local_system_context_writer{m_time->m_local_system_clock_context_writer}, + m_network_system_context_writer{m_time->m_network_system_clock_context_writer}, + m_ephemeral_system_context_writer{m_time->m_ephemeral_network_clock_context_writer}, + m_local_operation{m_system}, m_network_operation{m_system}, m_ephemeral_operation{m_system} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &ServiceManager::Handle_GetStaticServiceAsUser, "GetStaticServiceAsUser"}, + {5, &ServiceManager::Handle_GetStaticServiceAsAdmin, "GetStaticServiceAsAdmin"}, + {6, &ServiceManager::Handle_GetStaticServiceAsRepair, "GetStaticServiceAsRepair"}, + {9, &ServiceManager::Handle_GetStaticServiceAsServiceManager, "GetStaticServiceAsServiceManager"}, + {10, &ServiceManager::Handle_SetupStandardSteadyClockCore, "SetupStandardSteadyClockCore"}, + {11, &ServiceManager::Handle_SetupStandardLocalSystemClockCore, "SetupStandardLocalSystemClockCore"}, + {12, &ServiceManager::Handle_SetupStandardNetworkSystemClockCore, "SetupStandardNetworkSystemClockCore"}, + {13, &ServiceManager::Handle_SetupStandardUserSystemClockCore, "SetupStandardUserSystemClockCore"}, + {14, &ServiceManager::Handle_SetupTimeZoneServiceCore, "SetupTimeZoneServiceCore"}, + {15, &ServiceManager::Handle_SetupEphemeralNetworkSystemClockCore, "SetupEphemeralNetworkSystemClockCore"}, + {50, &ServiceManager::Handle_GetStandardLocalClockOperationEvent, "GetStandardLocalClockOperationEvent"}, + {51, &ServiceManager::Handle_GetStandardNetworkClockOperationEventForServiceManager, "GetStandardNetworkClockOperationEventForServiceManager"}, + {52, &ServiceManager::Handle_GetEphemeralNetworkClockOperationEventForServiceManager, "GetEphemeralNetworkClockOperationEventForServiceManager"}, + {60, &ServiceManager::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent, "GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent"}, + {100, &ServiceManager::Handle_SetStandardSteadyClockBaseTime, "SetStandardSteadyClockBaseTime"}, + {200, &ServiceManager::Handle_GetClosestAlarmUpdatedEvent, "GetClosestAlarmUpdatedEvent"}, + {201, &ServiceManager::Handle_CheckAndSignalAlarms, "CheckAndSignalAlarms"}, + {202, &ServiceManager::Handle_GetClosestAlarmInfo, "GetClosestAlarmInfo "}, + }; + // clang-format on + RegisterHandlers(functions); + + m_local_system_context_writer.Link(m_local_operation); + m_network_system_context_writer.Link(m_network_operation); + m_ephemeral_system_context_writer.Link(m_ephemeral_operation); +} + +void ServiceManager::SetupSAndP() { + if (!m_is_s_and_p_setup) { + m_is_s_and_p_setup = true; + m_server_manager.RegisterNamedService( + "time:s", std::make_shared( + m_system, StaticServiceSetupInfo{0, 0, 1, 0, 0, 0}, m_time, "time:s")); + m_server_manager.RegisterNamedService("time:p", + std::make_shared( + m_system, m_time->m_power_state_request_manager)); + } +} + +void ServiceManager::CheckAndSetupServicesSAndP() { + if (m_local_system_clock.IsInitialized() && m_user_system_clock.IsInitialized() && + m_network_system_clock.IsInitialized() && m_steady_clock.IsInitialized() && + m_time_zone.IsInitialized() && m_ephemeral_network_clock.IsInitialized()) { + SetupSAndP(); + } +} + +void ServiceManager::Handle_GetStaticServiceAsUser(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr service{}; + auto res = GetStaticServiceAsUser(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface(std::move(service)); +} + +void ServiceManager::Handle_GetStaticServiceAsAdmin(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr service{}; + auto res = GetStaticServiceAsAdmin(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface(std::move(service)); +} + +void ServiceManager::Handle_GetStaticServiceAsRepair(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr service{}; + auto res = GetStaticServiceAsRepair(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface(std::move(service)); +} + +void ServiceManager::Handle_GetStaticServiceAsServiceManager(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr service{}; + auto res = GetStaticServiceAsServiceManager(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface(std::move(service)); +} + +void ServiceManager::Handle_SetupStandardSteadyClockCore(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto clock_source_id{rp.PopRaw()}; + auto rtc_offset{rp.Pop()}; + auto internal_offset{rp.Pop()}; + auto test_offset{rp.Pop()}; + auto is_rtc_reset_detected{rp.Pop()}; + + auto res = SetupStandardSteadyClockCore(clock_source_id, rtc_offset, internal_offset, + test_offset, is_rtc_reset_detected); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ServiceManager::Handle_SetupStandardLocalSystemClockCore(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto context{rp.PopRaw()}; + auto time{rp.Pop()}; + + auto res = SetupStandardLocalSystemClockCore(context, time); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ServiceManager::Handle_SetupStandardNetworkSystemClockCore(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto context{rp.PopRaw()}; + auto accuracy{rp.Pop()}; + + auto res = SetupStandardNetworkSystemClockCore(context, accuracy); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ServiceManager::Handle_SetupStandardUserSystemClockCore(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto time_point{rp.PopRaw()}; + auto automatic_correction{rp.Pop()}; + + auto res = SetupStandardUserSystemClockCore(time_point, automatic_correction); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ServiceManager::Handle_SetupTimeZoneServiceCore(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto name{rp.PopRaw()}; + auto time_point{rp.PopRaw()}; + auto rule_version{rp.PopRaw()}; + auto location_count{rp.Pop()}; + + auto rule_buffer{ctx.ReadBuffer()}; + + auto res = + SetupTimeZoneServiceCore(name, time_point, rule_version, location_count, rule_buffer); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ServiceManager::Handle_SetupEphemeralNetworkSystemClockCore(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + auto res = SetupEphemeralNetworkSystemClockCore(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ServiceManager::Handle_GetStandardLocalClockOperationEvent(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Kernel::KEvent* event{}; + auto res = GetStandardLocalClockOperationEvent(&event); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(res); + rb.PushCopyObjects(event->GetReadableEvent()); +} + +void ServiceManager::Handle_GetStandardNetworkClockOperationEventForServiceManager( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Kernel::KEvent* event{}; + auto res = GetStandardNetworkClockOperationEventForServiceManager(&event); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(res); + rb.PushCopyObjects(event); +} + +void ServiceManager::Handle_GetEphemeralNetworkClockOperationEventForServiceManager( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Kernel::KEvent* event{}; + auto res = GetEphemeralNetworkClockOperationEventForServiceManager(&event); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(res); + rb.PushCopyObjects(event); +} + +void ServiceManager::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Kernel::KEvent* event{}; + auto res = GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(&event); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(res); + rb.PushCopyObjects(event); +} + +void ServiceManager::Handle_SetStandardSteadyClockBaseTime(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto base_time{rp.Pop()}; + + auto res = SetStandardSteadyClockBaseTime(base_time); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ServiceManager::Handle_GetClosestAlarmUpdatedEvent(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Kernel::KEvent* event{}; + auto res = GetClosestAlarmUpdatedEvent(&event); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.PushCopyObjects(event->GetReadableEvent()); +} + +void ServiceManager::Handle_CheckAndSignalAlarms(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + auto res = CheckAndSignalAlarms(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ServiceManager::Handle_GetClosestAlarmInfo(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + AlarmInfo alarm_info{}; + bool is_valid{}; + s64 time{}; + auto res = GetClosestAlarmInfo(is_valid, alarm_info, time); + + IPC::ResponseBuilder rb{ctx, 5 + sizeof(AlarmInfo) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw(alarm_info); + rb.Push(is_valid); + rb.Push(time); +} + +// =============================== Implementations =========================== + +Result ServiceManager::GetStaticService(std::shared_ptr& out_service, + StaticServiceSetupInfo setup_info, const char* name) { + out_service = std::make_shared(m_system, setup_info, m_time, name); + R_SUCCEED(); +} + +Result ServiceManager::GetStaticServiceAsUser(std::shared_ptr& out_service) { + R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{0, 0, 0, 0, 0, 0}, "time:u")); +} + +Result ServiceManager::GetStaticServiceAsAdmin(std::shared_ptr& out_service) { + R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{1, 1, 0, 1, 0, 0}, "time:a")); +} + +Result ServiceManager::GetStaticServiceAsRepair(std::shared_ptr& out_service) { + R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{0, 0, 0, 0, 1, 0}, "time:r")); +} + +Result ServiceManager::GetStaticServiceAsServiceManager( + std::shared_ptr& out_service) { + R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{1, 1, 1, 1, 1, 0}, "time:sm")); +} + +Result ServiceManager::SetupStandardSteadyClockCore(Common::UUID& clock_source_id, s64 rtc_offset, + s64 internal_offset, s64 test_offset, + bool is_rtc_reset_detected) { + m_steady_clock.Initialize(clock_source_id, rtc_offset, internal_offset, test_offset, + is_rtc_reset_detected); + auto time = m_steady_clock.GetRawTime(); + auto ticks = m_system.CoreTiming().GetClockTicks(); + auto boot_time = time - ConvertToTimeSpan(ticks).count(); + m_shared_memory.SetSteadyClockTimePoint(clock_source_id, boot_time); + m_steady_clock.SetContinuousAdjustment(clock_source_id, boot_time); + + ContinuousAdjustmentTimePoint time_point{}; + m_steady_clock.GetContinuousAdjustment(time_point); + m_shared_memory.SetContinuousAdjustment(time_point); + + CheckAndSetupServicesSAndP(); + R_SUCCEED(); +} + +Result ServiceManager::SetupStandardLocalSystemClockCore(SystemClockContext& context, s64 time) { + m_local_system_clock.SetContextWriter(m_local_system_context_writer); + m_local_system_clock.Initialize(context, time); + + CheckAndSetupServicesSAndP(); + R_SUCCEED(); +} + +Result ServiceManager::SetupStandardNetworkSystemClockCore(SystemClockContext& context, + s64 accuracy) { + // TODO this is a hack! The network clock should be updated independently, from the ntc service + // and maybe elsewhere. We do not do that, so fix the network clock to the local clock on boot + // to avoid it being stuck at 0. + m_local_system_clock.GetContext(context); + + m_network_system_clock.SetContextWriter(m_network_system_context_writer); + m_network_system_clock.Initialize(context, accuracy); + + CheckAndSetupServicesSAndP(); + R_SUCCEED(); +} + +Result ServiceManager::SetupStandardUserSystemClockCore(SteadyClockTimePoint& time_point, + bool automatic_correction) { + // TODO this is a hack! I'm not sure where this clock's time point is initialised, but we don't + // want it at 0. + // m_local_system_clock.GetCurrentTimePoint(time_point); + + m_user_system_clock.SetAutomaticCorrection(automatic_correction); + m_user_system_clock.SetTimePointAndSignal(time_point); + m_user_system_clock.SetInitialized(); + m_shared_memory.SetAutomaticCorrection(automatic_correction); + + CheckAndSetupServicesSAndP(); + R_SUCCEED(); +} + +Result ServiceManager::SetupTimeZoneServiceCore(LocationName& name, + SteadyClockTimePoint& time_point, + RuleVersion& rule_version, u32 location_count, + std::span rule_buffer) { + if (m_time_zone.ParseBinary(name, rule_buffer) != ResultSuccess) { + LOG_ERROR(Service_Time, "Failed to parse time zone binary!"); + } + + m_time_zone.SetTimePoint(time_point); + m_time_zone.SetTotalLocationNameCount(location_count); + m_time_zone.SetRuleVersion(rule_version); + m_time_zone.SetInitialized(); + + CheckAndSetupServicesSAndP(); + R_SUCCEED(); +} + +Result ServiceManager::SetupEphemeralNetworkSystemClockCore() { + m_ephemeral_network_clock.SetContextWriter(m_ephemeral_system_context_writer); + m_ephemeral_network_clock.SetInitialized(); + + CheckAndSetupServicesSAndP(); + R_SUCCEED(); +} + +Result ServiceManager::GetStandardLocalClockOperationEvent(Kernel::KEvent** out_event) { + *out_event = m_local_operation.m_event; + R_SUCCEED(); +} + +Result ServiceManager::GetStandardNetworkClockOperationEventForServiceManager( + Kernel::KEvent** out_event) { + *out_event = m_network_operation.m_event; + R_SUCCEED(); +} + +Result ServiceManager::GetEphemeralNetworkClockOperationEventForServiceManager( + Kernel::KEvent** out_event) { + *out_event = m_ephemeral_operation.m_event; + R_SUCCEED(); +} + +Result ServiceManager::GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent( + Kernel::KEvent** out_event) { + *out_event = &m_user_system_clock.GetEvent(); + R_SUCCEED(); +} + +Result ServiceManager::SetStandardSteadyClockBaseTime(s64 base_time) { + m_steady_clock.SetRtcOffset(base_time); + auto time = m_steady_clock.GetRawTime(); + auto ticks = m_system.CoreTiming().GetClockTicks(); + auto diff = time - ConvertToTimeSpan(ticks).count(); + m_shared_memory.UpdateBaseTime(diff); + m_steady_clock.UpdateContinuousAdjustmentTime(diff); + + ContinuousAdjustmentTimePoint time_point{}; + m_steady_clock.GetContinuousAdjustment(time_point); + m_shared_memory.SetContinuousAdjustment(time_point); + R_SUCCEED(); +} + +Result ServiceManager::GetClosestAlarmUpdatedEvent(Kernel::KEvent** out_event) { + *out_event = &m_alarms.GetEvent(); + R_SUCCEED(); +} + +Result ServiceManager::CheckAndSignalAlarms() { + m_alarms.CheckAndSignal(); + R_SUCCEED(); +} + +Result ServiceManager::GetClosestAlarmInfo(bool& out_is_valid, AlarmInfo& out_info, s64& out_time) { + Alarm* alarm{nullptr}; + out_is_valid = m_alarms.GetClosestAlarm(&alarm); + if (out_is_valid) { + out_info = { + .alert_time = alarm->GetAlertTime(), + .priority = alarm->GetPriority(), + }; + out_time = m_alarms.GetRawTime(); + } + R_SUCCEED(); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/service_manager.h b/src/core/hle/service/psc/time/service_manager.h new file mode 100755 index 000000000..1d9952317 --- /dev/null +++ b/src/core/hle/service/psc/time/service_manager.h @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/psc/time/manager.h" +#include "core/hle/service/server_manager.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Kernel { +class KReadableEvent; +} + +namespace Service::PSC::Time { +class StaticService; + +class ServiceManager final : public ServiceFramework { +public: + explicit ServiceManager(Core::System& system, std::shared_ptr time, + ServerManager* server_manager); + ~ServiceManager() override = default; + + Result GetStaticServiceAsUser(std::shared_ptr& out_service); + Result GetStaticServiceAsAdmin(std::shared_ptr& out_service); + Result GetStaticServiceAsRepair(std::shared_ptr& out_service); + Result GetStaticServiceAsServiceManager(std::shared_ptr& out_service); + Result SetupStandardSteadyClockCore(Common::UUID& clock_source_id, s64 rtc_offset, + s64 internal_offset, s64 test_offset, + bool is_rtc_reset_detected); + Result SetupStandardLocalSystemClockCore(SystemClockContext& context, s64 time); + Result SetupStandardNetworkSystemClockCore(SystemClockContext& context, s64 accuracy); + Result SetupStandardUserSystemClockCore(SteadyClockTimePoint& time_point, + bool automatic_correction); + Result SetupTimeZoneServiceCore(LocationName& name, SteadyClockTimePoint& time_point, + RuleVersion& rule_version, u32 location_count, + std::span rule_buffer); + Result SetupEphemeralNetworkSystemClockCore(); + Result GetStandardLocalClockOperationEvent(Kernel::KEvent** out_event); + Result GetStandardNetworkClockOperationEventForServiceManager(Kernel::KEvent** out_event); + Result GetEphemeralNetworkClockOperationEventForServiceManager(Kernel::KEvent** out_event); + Result GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(Kernel::KEvent** out_event); + Result SetStandardSteadyClockBaseTime(s64 base_time); + Result GetClosestAlarmUpdatedEvent(Kernel::KEvent** out_event); + Result CheckAndSignalAlarms(); + Result GetClosestAlarmInfo(bool& out_is_valid, AlarmInfo& out_info, s64& out_time); + +private: + void CheckAndSetupServicesSAndP(); + void SetupSAndP(); + Result GetStaticService(std::shared_ptr& out_service, + StaticServiceSetupInfo setup_info, const char* name); + + void Handle_GetStaticServiceAsUser(HLERequestContext& ctx); + void Handle_GetStaticServiceAsAdmin(HLERequestContext& ctx); + void Handle_GetStaticServiceAsRepair(HLERequestContext& ctx); + void Handle_GetStaticServiceAsServiceManager(HLERequestContext& ctx); + void Handle_SetupStandardSteadyClockCore(HLERequestContext& ctx); + void Handle_SetupStandardLocalSystemClockCore(HLERequestContext& ctx); + void Handle_SetupStandardNetworkSystemClockCore(HLERequestContext& ctx); + void Handle_SetupStandardUserSystemClockCore(HLERequestContext& ctx); + void Handle_SetupTimeZoneServiceCore(HLERequestContext& ctx); + void Handle_SetupEphemeralNetworkSystemClockCore(HLERequestContext& ctx); + void Handle_GetStandardLocalClockOperationEvent(HLERequestContext& ctx); + void Handle_GetStandardNetworkClockOperationEventForServiceManager(HLERequestContext& ctx); + void Handle_GetEphemeralNetworkClockOperationEventForServiceManager(HLERequestContext& ctx); + void Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent(HLERequestContext& ctx); + void Handle_SetStandardSteadyClockBaseTime(HLERequestContext& ctx); + void Handle_GetClosestAlarmUpdatedEvent(HLERequestContext& ctx); + void Handle_CheckAndSignalAlarms(HLERequestContext& ctx); + void Handle_GetClosestAlarmInfo(HLERequestContext& ctx); + + Core::System& m_system; + std::shared_ptr m_time; + ServerManager& m_server_manager; + bool m_is_s_and_p_setup{}; + StandardLocalSystemClockCore& m_local_system_clock; + StandardUserSystemClockCore& m_user_system_clock; + StandardNetworkSystemClockCore& m_network_system_clock; + StandardSteadyClockCore& m_steady_clock; + TimeZone& m_time_zone; + EphemeralNetworkSystemClockCore& m_ephemeral_network_clock; + SharedMemory& m_shared_memory; + Alarms& m_alarms; + LocalSystemClockContextWriter& m_local_system_context_writer; + NetworkSystemClockContextWriter& m_network_system_context_writer; + EphemeralNetworkSystemClockContextWriter& m_ephemeral_system_context_writer; + OperationEvent m_local_operation; + OperationEvent m_network_operation; + OperationEvent m_ephemeral_operation; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/shared_memory.cpp b/src/core/hle/service/psc/time/shared_memory.cpp new file mode 100755 index 000000000..defaceebe --- /dev/null +++ b/src/core/hle/service/psc/time/shared_memory.cpp @@ -0,0 +1,84 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/kernel/k_shared_memory.h" +#include "core/hle/service/psc/time/shared_memory.h" + +namespace Service::PSC::Time { +namespace { +template +constexpr inline T ReadFromLockFreeAtomicType(const LockFreeAtomicType* p) { + while (true) { + // Get the counter. + auto counter = p->m_counter; + + // Get the value. + auto value = p->m_value[counter % 2]; + + // Fence memory. + std::atomic_thread_fence(std::memory_order_acquire); + + // Check that the counter matches. + if (counter == p->m_counter) { + return value; + } + } +} + +template +constexpr inline void WriteToLockFreeAtomicType(LockFreeAtomicType* p, const T& value) { + // Get the current counter. + auto counter = p->m_counter; + + // Increment the counter. + ++counter; + + // Store the updated value. + p->m_value[counter % 2] = value; + + // Fence memory. + std::atomic_thread_fence(std::memory_order_release); + + // Set the updated counter. + p->m_counter = counter; +} +} // namespace + +SharedMemory::SharedMemory(Core::System& system) + : m_system{system}, m_k_shared_memory{m_system.Kernel().GetTimeSharedMem()}, + m_shared_memory_ptr{reinterpret_cast(m_k_shared_memory.GetPointer())} { + std::memset(m_shared_memory_ptr, 0, sizeof(*m_shared_memory_ptr)); +} + +void SharedMemory::SetLocalSystemContext(SystemClockContext& context) { + WriteToLockFreeAtomicType(&m_shared_memory_ptr->local_system_clock_contexts, context); +} + +void SharedMemory::SetNetworkSystemContext(SystemClockContext& context) { + WriteToLockFreeAtomicType(&m_shared_memory_ptr->network_system_clock_contexts, context); +} + +void SharedMemory::SetSteadyClockTimePoint(ClockSourceId clock_source_id, s64 time_point) { + WriteToLockFreeAtomicType(&m_shared_memory_ptr->steady_time_points, + {time_point, clock_source_id}); +} + +void SharedMemory::SetContinuousAdjustment(ContinuousAdjustmentTimePoint& time_point) { + WriteToLockFreeAtomicType(&m_shared_memory_ptr->continuous_adjustment_time_points, time_point); +} + +void SharedMemory::SetAutomaticCorrection(bool automatic_correction) { + WriteToLockFreeAtomicType(&m_shared_memory_ptr->automatic_corrections, automatic_correction); +} + +void SharedMemory::UpdateBaseTime(s64 time) { + SteadyClockTimePoint time_point{ + ReadFromLockFreeAtomicType(&m_shared_memory_ptr->steady_time_points)}; + + time_point.time_point = time; + + WriteToLockFreeAtomicType(&m_shared_memory_ptr->steady_time_points, time_point); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/shared_memory.h b/src/core/hle/service/psc/time/shared_memory.h new file mode 100755 index 000000000..f9bf97d5c --- /dev/null +++ b/src/core/hle/service/psc/time/shared_memory.h @@ -0,0 +1,70 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "common/common_types.h" +#include "core/hle/service/psc/time/common.h" + +namespace Core { +class System; +} + +namespace Kernel { +class KSharedMemory; +} + +namespace Service::PSC::Time { + +template +struct LockFreeAtomicType { + u32 m_counter; + std::array m_value; +}; + +struct SharedMemoryStruct { + LockFreeAtomicType steady_time_points; + LockFreeAtomicType local_system_clock_contexts; + LockFreeAtomicType network_system_clock_contexts; + LockFreeAtomicType automatic_corrections; + LockFreeAtomicType continuous_adjustment_time_points; + std::array pad0148; +}; +static_assert(offsetof(SharedMemoryStruct, steady_time_points) == 0x0, + "steady_time_points are in the wrong place!"); +static_assert(offsetof(SharedMemoryStruct, local_system_clock_contexts) == 0x38, + "local_system_clock_contexts are in the wrong place!"); +static_assert(offsetof(SharedMemoryStruct, network_system_clock_contexts) == 0x80, + "network_system_clock_contexts are in the wrong place!"); +static_assert(offsetof(SharedMemoryStruct, automatic_corrections) == 0xC8, + "automatic_corrections are in the wrong place!"); +static_assert(offsetof(SharedMemoryStruct, continuous_adjustment_time_points) == 0xD0, + "continuous_adjustment_time_points are in the wrong place!"); +static_assert(sizeof(SharedMemoryStruct) == 0x1000, + "Time's SharedMemoryStruct has the wrong size!"); +static_assert(std::is_trivial_v); + +class SharedMemory { +public: + explicit SharedMemory(Core::System& system); + + Kernel::KSharedMemory& GetKSharedMemory() { + return m_k_shared_memory; + } + + void SetLocalSystemContext(SystemClockContext& context); + void SetNetworkSystemContext(SystemClockContext& context); + void SetSteadyClockTimePoint(ClockSourceId clock_source_id, s64 time_diff); + void SetContinuousAdjustment(ContinuousAdjustmentTimePoint& time_point); + void SetAutomaticCorrection(bool automatic_correction); + void UpdateBaseTime(s64 time); + +private: + Core::System& m_system; + Kernel::KSharedMemory& m_k_shared_memory; + SharedMemoryStruct* m_shared_memory_ptr; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/static.cpp b/src/core/hle/service/psc/time/static.cpp new file mode 100755 index 000000000..2f9714113 --- /dev/null +++ b/src/core/hle/service/psc/time/static.cpp @@ -0,0 +1,488 @@ +// 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/k_shared_memory.h" +#include "core/hle/service/psc/time/clocks/ephemeral_network_system_clock_core.h" +#include "core/hle/service/psc/time/clocks/standard_local_system_clock_core.h" +#include "core/hle/service/psc/time/clocks/standard_network_system_clock_core.h" +#include "core/hle/service/psc/time/clocks/standard_user_system_clock_core.h" +#include "core/hle/service/psc/time/manager.h" +#include "core/hle/service/psc/time/shared_memory.h" +#include "core/hle/service/psc/time/static.h" +#include "core/hle/service/psc/time/steady_clock.h" +#include "core/hle/service/psc/time/system_clock.h" +#include "core/hle/service/psc/time/time_zone.h" +#include "core/hle/service/psc/time/time_zone_service.h" + +namespace Service::PSC::Time { +namespace { +constexpr Result GetTimeFromTimePointAndContext(s64* out_time, SteadyClockTimePoint& time_point, + SystemClockContext& context) { + R_UNLESS(out_time != nullptr, ResultInvalidArgument); + R_UNLESS(time_point.IdMatches(context.steady_time_point), ResultClockMismatch); + + *out_time = context.offset + time_point.time_point; + R_SUCCEED(); +} +} // namespace + +StaticService::StaticService(Core::System& system_, StaticServiceSetupInfo setup_info, + std::shared_ptr time, const char* name) + : ServiceFramework{system_, name}, m_system{system}, m_setup_info{setup_info}, m_time{time}, + m_local_system_clock{m_time->m_standard_local_system_clock}, + m_user_system_clock{m_time->m_standard_user_system_clock}, + m_network_system_clock{m_time->m_standard_network_system_clock}, + m_time_zone{m_time->m_time_zone}, + m_ephemeral_network_clock{m_time->m_ephemeral_network_clock}, m_shared_memory{ + m_time->m_shared_memory} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &StaticService::Handle_GetStandardUserSystemClock, "GetStandardUserSystemClock"}, + {1, &StaticService::Handle_GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"}, + {2, &StaticService::Handle_GetStandardSteadyClock, "GetStandardSteadyClock"}, + {3, &StaticService::Handle_GetTimeZoneService, "GetTimeZoneService"}, + {4, &StaticService::Handle_GetStandardLocalSystemClock, "GetStandardLocalSystemClock"}, + {5, &StaticService::Handle_GetEphemeralNetworkSystemClock, "GetEphemeralNetworkSystemClock"}, + {20, &StaticService::Handle_GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}, + {50, &StaticService::Handle_SetStandardSteadyClockInternalOffset, "SetStandardSteadyClockInternalOffset"}, + {51, &StaticService::Handle_GetStandardSteadyClockRtcValue, "GetStandardSteadyClockRtcValue"}, + {100, &StaticService::Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled, "IsStandardUserSystemClockAutomaticCorrectionEnabled"}, + {101, &StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled, "SetStandardUserSystemClockAutomaticCorrectionEnabled"}, + {102, &StaticService::Handle_GetStandardUserSystemClockInitialYear, "GetStandardUserSystemClockInitialYear"}, + {200, &StaticService::Handle_IsStandardNetworkSystemClockAccuracySufficient, "IsStandardNetworkSystemClockAccuracySufficient"}, + {201, &StaticService::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"}, + {300, &StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint, "CalculateMonotonicSystemClockBaseTimePoint"}, + {400, &StaticService::Handle_GetClockSnapshot, "GetClockSnapshot"}, + {401, &StaticService::Handle_GetClockSnapshotFromSystemClockContext, "GetClockSnapshotFromSystemClockContext"}, + {500, &StaticService::Handle_CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"}, + {501, &StaticService::Handle_CalculateSpanBetween, "CalculateSpanBetween"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +Result StaticService::GetClockSnapshotImpl(ClockSnapshot& out_snapshot, + SystemClockContext& user_context, + SystemClockContext& network_context, TimeType type) { + out_snapshot.user_context = user_context; + out_snapshot.network_context = network_context; + + R_TRY( + m_time->m_standard_steady_clock.GetCurrentTimePoint(out_snapshot.steady_clock_time_point)); + + out_snapshot.is_automatic_correction_enabled = m_user_system_clock.GetAutomaticCorrection(); + + R_TRY(m_time_zone.GetLocationName(out_snapshot.location_name)); + + R_TRY(GetTimeFromTimePointAndContext( + &out_snapshot.user_time, out_snapshot.steady_clock_time_point, out_snapshot.user_context)); + + R_TRY(m_time_zone.ToCalendarTimeWithMyRule(out_snapshot.user_calendar_time, + out_snapshot.user_calendar_additional_time, + out_snapshot.user_time)); + + if (GetTimeFromTimePointAndContext(&out_snapshot.network_time, + out_snapshot.steady_clock_time_point, + out_snapshot.network_context) != ResultSuccess) { + out_snapshot.network_time = 0; + } + + R_TRY(m_time_zone.ToCalendarTimeWithMyRule(out_snapshot.network_calendar_time, + out_snapshot.network_calendar_additional_time, + out_snapshot.network_time)); + out_snapshot.type = type; + out_snapshot.unk_CE = 0; + R_SUCCEED(); +} + +void StaticService::Handle_GetStandardUserSystemClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr service{}; + auto res = GetStandardUserSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface(std::move(service)); +} + +void StaticService::Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr service{}; + auto res = GetStandardNetworkSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface(std::move(service)); +} + +void StaticService::Handle_GetStandardSteadyClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr service{}; + auto res = GetStandardSteadyClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface(std::move(service)); +} + +void StaticService::Handle_GetTimeZoneService(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr service{}; + auto res = GetTimeZoneService(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface(std::move(service)); +} + +void StaticService::Handle_GetStandardLocalSystemClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr service{}; + auto res = GetStandardLocalSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface(std::move(service)); +} + +void StaticService::Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr service{}; + auto res = GetEphemeralNetworkSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface(std::move(service)); +} + +void StaticService::Handle_GetSharedMemoryNativeHandle(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Kernel::KSharedMemory* shared_memory{}; + auto res = GetSharedMemoryNativeHandle(&shared_memory); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(res); + rb.PushCopyObjects(shared_memory); +} + +void StaticService::Handle_SetStandardSteadyClockInternalOffset(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(m_setup_info.can_write_steady_clock ? ResultNotImplemented : ResultPermissionDenied); +} + +void StaticService::Handle_GetStandardSteadyClockRtcValue(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultNotImplemented); +} + +void StaticService::Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + bool is_enabled{}; + auto res = IsStandardUserSystemClockAutomaticCorrectionEnabled(is_enabled); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(is_enabled); +} + +void StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto automatic_correction{rp.Pop()}; + + auto res = SetStandardUserSystemClockAutomaticCorrectionEnabled(automatic_correction); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void StaticService::Handle_GetStandardUserSystemClockInitialYear(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultNotImplemented); +} + +void StaticService::Handle_IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + bool is_sufficient{}; + auto res = IsStandardNetworkSystemClockAccuracySufficient(is_sufficient); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(is_sufficient); +} + +void StaticService::Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + SteadyClockTimePoint time_point{}; + auto res = GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(time_point); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(SteadyClockTimePoint) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw(time_point); +} + +void StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto context{rp.PopRaw()}; + + s64 time{}; + auto res = CalculateMonotonicSystemClockBaseTimePoint(time, context); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(time); +} + +void StaticService::Handle_GetClockSnapshot(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto type{rp.PopEnum()}; + + ClockSnapshot snapshot{}; + auto res = GetClockSnapshot(snapshot, type); + + ctx.WriteBuffer(snapshot); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void StaticService::Handle_GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto user_context{rp.PopRaw()}; + auto network_context{rp.PopRaw()}; + auto clock_type{rp.PopEnum()}; + + ClockSnapshot snapshot{}; + auto res = + GetClockSnapshotFromSystemClockContext(snapshot, user_context, network_context, clock_type); + + ctx.WriteBuffer(snapshot); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void StaticService::Handle_CalculateStandardUserSystemClockDifferenceByUser( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + ClockSnapshot a{}; + ClockSnapshot b{}; + + auto a_buffer{ctx.ReadBuffer(0)}; + auto b_buffer{ctx.ReadBuffer(1)}; + + std::memcpy(&a, a_buffer.data(), sizeof(ClockSnapshot)); + std::memcpy(&b, b_buffer.data(), sizeof(ClockSnapshot)); + + s64 difference{}; + auto res = CalculateStandardUserSystemClockDifferenceByUser(difference, a, b); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(difference); +} + +void StaticService::Handle_CalculateSpanBetween(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + ClockSnapshot a{}; + ClockSnapshot b{}; + + auto a_buffer{ctx.ReadBuffer(0)}; + auto b_buffer{ctx.ReadBuffer(1)}; + + std::memcpy(&a, a_buffer.data(), sizeof(ClockSnapshot)); + std::memcpy(&b, b_buffer.data(), sizeof(ClockSnapshot)); + + s64 time{}; + auto res = CalculateSpanBetween(time, a, b); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(time); +} + +// =============================== Implementations =========================== + +Result StaticService::GetStandardUserSystemClock(std::shared_ptr& out_service) { + out_service = std::make_shared(m_system, m_user_system_clock, + m_setup_info.can_write_user_clock, + m_setup_info.can_write_uninitialized_clock); + R_SUCCEED(); +} + +Result StaticService::GetStandardNetworkSystemClock(std::shared_ptr& out_service) { + out_service = std::make_shared(m_system, m_network_system_clock, + m_setup_info.can_write_network_clock, + m_setup_info.can_write_uninitialized_clock); + R_SUCCEED(); +} + +Result StaticService::GetStandardSteadyClock(std::shared_ptr& out_service) { + out_service = + std::make_shared(m_system, m_time, m_setup_info.can_write_steady_clock, + m_setup_info.can_write_uninitialized_clock); + R_SUCCEED(); +} + +Result StaticService::GetTimeZoneService(std::shared_ptr& out_service) { + out_service = + std::make_shared(m_system, m_time->m_standard_steady_clock, m_time_zone, + m_setup_info.can_write_timezone_device_location); + R_SUCCEED(); +} + +Result StaticService::GetStandardLocalSystemClock(std::shared_ptr& out_service) { + out_service = std::make_shared(m_system, m_local_system_clock, + m_setup_info.can_write_local_clock, + m_setup_info.can_write_uninitialized_clock); + R_SUCCEED(); +} + +Result StaticService::GetEphemeralNetworkSystemClock(std::shared_ptr& out_service) { + out_service = std::make_shared(m_system, m_ephemeral_network_clock, + m_setup_info.can_write_network_clock, + m_setup_info.can_write_uninitialized_clock); + R_SUCCEED(); +} + +Result StaticService::GetSharedMemoryNativeHandle(Kernel::KSharedMemory** out_shared_memory) { + *out_shared_memory = &m_shared_memory.GetKSharedMemory(); + R_SUCCEED(); +} + +Result StaticService::IsStandardUserSystemClockAutomaticCorrectionEnabled(bool& out_is_enabled) { + R_UNLESS(m_user_system_clock.IsInitialized(), ResultClockUninitialized); + + out_is_enabled = m_user_system_clock.GetAutomaticCorrection(); + R_SUCCEED(); +} + +Result StaticService::SetStandardUserSystemClockAutomaticCorrectionEnabled( + bool automatic_correction) { + R_UNLESS(m_user_system_clock.IsInitialized() && m_time->m_standard_steady_clock.IsInitialized(), + ResultClockUninitialized); + R_UNLESS(m_setup_info.can_write_user_clock, ResultPermissionDenied); + + R_TRY(m_user_system_clock.SetAutomaticCorrection(automatic_correction)); + + m_shared_memory.SetAutomaticCorrection(automatic_correction); + + SteadyClockTimePoint time_point{}; + R_TRY(m_time->m_standard_steady_clock.GetCurrentTimePoint(time_point)); + + m_user_system_clock.SetTimePointAndSignal(time_point); + m_user_system_clock.GetEvent().Signal(); + R_SUCCEED(); +} + +Result StaticService::IsStandardNetworkSystemClockAccuracySufficient(bool& out_is_sufficient) { + out_is_sufficient = m_network_system_clock.IsAccuracySufficient(); + R_SUCCEED(); +} + +Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( + SteadyClockTimePoint& out_time_point) { + R_UNLESS(m_user_system_clock.IsInitialized(), ResultClockUninitialized); + + m_user_system_clock.GetTimePoint(out_time_point); + + R_SUCCEED(); +} + +Result StaticService::CalculateMonotonicSystemClockBaseTimePoint(s64& out_time, + SystemClockContext& context) { + R_UNLESS(m_time->m_standard_steady_clock.IsInitialized(), ResultClockUninitialized); + + SteadyClockTimePoint time_point{}; + R_TRY(m_time->m_standard_steady_clock.GetCurrentTimePoint(time_point)); + + R_UNLESS(time_point.IdMatches(context.steady_time_point), ResultClockMismatch); + + auto one_second_ns{ + std::chrono::duration_cast(std::chrono::seconds(1)).count()}; + auto ticks{m_system.CoreTiming().GetClockTicks()}; + auto current_time{ConvertToTimeSpan(ticks).count()}; + out_time = ((context.offset + time_point.time_point) - (current_time / one_second_ns)); + R_SUCCEED(); +} + +Result StaticService::GetClockSnapshot(ClockSnapshot& out_snapshot, TimeType type) { + SystemClockContext user_context{}; + R_TRY(m_user_system_clock.GetContext(user_context)); + + SystemClockContext network_context{}; + R_TRY(m_network_system_clock.GetContext(network_context)); + + R_RETURN(GetClockSnapshotImpl(out_snapshot, user_context, network_context, type)); +} + +Result StaticService::GetClockSnapshotFromSystemClockContext(ClockSnapshot& out_snapshot, + SystemClockContext& user_context, + SystemClockContext& network_context, + TimeType type) { + R_RETURN(GetClockSnapshotImpl(out_snapshot, user_context, network_context, type)); +} + +Result StaticService::CalculateStandardUserSystemClockDifferenceByUser(s64& out_time, + ClockSnapshot& a, + ClockSnapshot& b) { + auto diff_s = + std::chrono::seconds(b.user_context.offset) - std::chrono::seconds(a.user_context.offset); + out_time = std::chrono::duration_cast(diff_s).count(); + + if (a.user_context != b.user_context || + (a.is_automatic_correction_enabled && b.is_automatic_correction_enabled) || + !a.network_context.steady_time_point.IdMatches(b.network_context.steady_time_point)) { + out_time = 0; + } + + R_SUCCEED(); +} + +Result StaticService::CalculateSpanBetween(s64& out_time, ClockSnapshot& a, ClockSnapshot& b) { + s64 time_s{}; + auto res = + GetSpanBetweenTimePoints(&time_s, a.steady_clock_time_point, b.steady_clock_time_point); + + if (res != ResultSuccess) { + R_UNLESS(a.network_time != 0 && b.network_time != 0, ResultTimeNotFound); + time_s = b.network_time - a.network_time; + } + + out_time = + std::chrono::duration_cast(std::chrono::seconds(time_s)).count(); + R_SUCCEED(); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/static.h b/src/core/hle/service/psc/time/static.h new file mode 100755 index 000000000..498cd5ab5 --- /dev/null +++ b/src/core/hle/service/psc/time/static.h @@ -0,0 +1,95 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/server_manager.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Kernel { +class KSharedMemory; +} + +namespace Service::PSC::Time { +class TimeManager; +class StandardLocalSystemClockCore; +class StandardUserSystemClockCore; +class StandardNetworkSystemClockCore; +class TimeZone; +class SystemClock; +class SteadyClock; +class TimeZoneService; +class EphemeralNetworkSystemClockCore; +class SharedMemory; + +class StaticService final : public ServiceFramework { +public: + explicit StaticService(Core::System& system, StaticServiceSetupInfo setup_info, + std::shared_ptr time, const char* name); + + ~StaticService() override = default; + + Result GetStandardUserSystemClock(std::shared_ptr& out_service); + Result GetStandardNetworkSystemClock(std::shared_ptr& out_service); + Result GetStandardSteadyClock(std::shared_ptr& out_service); + Result GetTimeZoneService(std::shared_ptr& out_service); + Result GetStandardLocalSystemClock(std::shared_ptr& out_service); + Result GetEphemeralNetworkSystemClock(std::shared_ptr& out_service); + Result GetSharedMemoryNativeHandle(Kernel::KSharedMemory** out_shared_memory); + Result IsStandardUserSystemClockAutomaticCorrectionEnabled(bool& out_is_enabled); + Result SetStandardUserSystemClockAutomaticCorrectionEnabled(bool automatic_correction); + Result IsStandardNetworkSystemClockAccuracySufficient(bool& out_is_sufficient); + Result GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( + SteadyClockTimePoint& out_time_point); + Result CalculateMonotonicSystemClockBaseTimePoint(s64& out_time, SystemClockContext& context); + Result GetClockSnapshot(ClockSnapshot& out_snapshot, TimeType type); + Result GetClockSnapshotFromSystemClockContext(ClockSnapshot& out_snapshot, + SystemClockContext& user_context, + SystemClockContext& network_context, + TimeType type); + Result CalculateStandardUserSystemClockDifferenceByUser(s64& out_time, ClockSnapshot& a, + ClockSnapshot& b); + Result CalculateSpanBetween(s64& out_time, ClockSnapshot& a, ClockSnapshot& b); + +private: + Result GetClockSnapshotImpl(ClockSnapshot& out_snapshot, SystemClockContext& user_context, + SystemClockContext& network_context, TimeType type); + + void Handle_GetStandardUserSystemClock(HLERequestContext& ctx); + void Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx); + void Handle_GetStandardSteadyClock(HLERequestContext& ctx); + void Handle_GetTimeZoneService(HLERequestContext& ctx); + void Handle_GetStandardLocalSystemClock(HLERequestContext& ctx); + void Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx); + void Handle_GetSharedMemoryNativeHandle(HLERequestContext& ctx); + void Handle_SetStandardSteadyClockInternalOffset(HLERequestContext& ctx); + void Handle_GetStandardSteadyClockRtcValue(HLERequestContext& ctx); + void Handle_IsStandardUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx); + void Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx); + void Handle_GetStandardUserSystemClockInitialYear(HLERequestContext& ctx); + void Handle_IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx); + void Handle_GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx); + void Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx); + void Handle_GetClockSnapshot(HLERequestContext& ctx); + void Handle_GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx); + void Handle_CalculateStandardUserSystemClockDifferenceByUser(HLERequestContext& ctx); + void Handle_CalculateSpanBetween(HLERequestContext& ctx); + + Core::System& m_system; + StaticServiceSetupInfo m_setup_info; + std::shared_ptr m_time; + StandardLocalSystemClockCore& m_local_system_clock; + StandardUserSystemClockCore& m_user_system_clock; + StandardNetworkSystemClockCore& m_network_system_clock; + TimeZone& m_time_zone; + EphemeralNetworkSystemClockCore& m_ephemeral_network_clock; + SharedMemory& m_shared_memory; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/steady_clock.cpp b/src/core/hle/service/psc/time/steady_clock.cpp new file mode 100755 index 000000000..a963051c6 --- /dev/null +++ b/src/core/hle/service/psc/time/steady_clock.cpp @@ -0,0 +1,164 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/psc/time/steady_clock.h" + +namespace Service::PSC::Time { + +SteadyClock::SteadyClock(Core::System& system_, std::shared_ptr manager, + bool can_write_steady_clock, bool can_write_uninitialized_clock) + : ServiceFramework{system_, "ISteadyClock"}, m_system{system}, + m_clock_core{manager->m_standard_steady_clock}, + m_can_write_steady_clock{can_write_steady_clock}, m_can_write_uninitialized_clock{ + can_write_uninitialized_clock} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &SteadyClock::Handle_GetCurrentTimePoint, "GetCurrentTimePoint"}, + {1, &SteadyClock::Handle_GetTestOffset, "GetTestOffset"}, + {2, &SteadyClock::Handle_SetTestOffset, "SetTestOffset"}, + {3, &SteadyClock::Handle_GetRtcValue, "GetRtcValue"}, + {4, &SteadyClock::Handle_IsRtcResetDetected, "IsRtcResetDetected"}, + {5, &SteadyClock::Handle_GetSetupResultValue, "GetSetupResultValue"}, + {6, &SteadyClock::Handle_GetInternalOffset, "GetInternalOffset"}, + }; + // clang-format on + RegisterHandlers(functions); +} + +void SteadyClock::Handle_GetCurrentTimePoint(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + SteadyClockTimePoint time_point{}; + auto res = GetCurrentTimePoint(time_point); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(SteadyClockTimePoint) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw(time_point); +} + +void SteadyClock::Handle_GetTestOffset(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + s64 test_offset{}; + auto res = GetTestOffset(test_offset); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(test_offset); +} + +void SteadyClock::Handle_SetTestOffset(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto test_offset{rp.Pop()}; + + auto res = SetTestOffset(test_offset); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void SteadyClock::Handle_GetRtcValue(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + s64 rtc_value{}; + auto res = GetRtcValue(rtc_value); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(rtc_value); +} + +void SteadyClock::Handle_IsRtcResetDetected(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + bool reset_detected{false}; + auto res = IsRtcResetDetected(reset_detected); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(reset_detected); +} + +void SteadyClock::Handle_GetSetupResultValue(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Result result_value{ResultSuccess}; + auto res = GetSetupResultValue(result_value); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(result_value); +} + +void SteadyClock::Handle_GetInternalOffset(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + s64 internal_offset{}; + auto res = GetInternalOffset(internal_offset); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(internal_offset); +} + +// =============================== Implementations =========================== + +Result SteadyClock::GetCurrentTimePoint(SteadyClockTimePoint& out_time_point) { + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + R_RETURN(m_clock_core.GetCurrentTimePoint(out_time_point)); +} + +Result SteadyClock::GetTestOffset(s64& out_test_offset) { + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + out_test_offset = m_clock_core.GetTestOffset(); + R_SUCCEED(); +} + +Result SteadyClock::SetTestOffset(s64 test_offset) { + R_UNLESS(m_can_write_steady_clock, ResultPermissionDenied); + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + m_clock_core.SetTestOffset(test_offset); + R_SUCCEED(); +} + +Result SteadyClock::GetRtcValue(s64& out_rtc_value) { + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + R_RETURN(m_clock_core.GetRtcValue(out_rtc_value)); +} + +Result SteadyClock::IsRtcResetDetected(bool& out_is_detected) { + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + out_is_detected = m_clock_core.IsResetDetected(); + R_SUCCEED(); +} + +Result SteadyClock::GetSetupResultValue(Result& out_result) { + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + out_result = m_clock_core.GetSetupResultValue(); + R_SUCCEED(); +} + +Result SteadyClock::GetInternalOffset(s64& out_internal_offset) { + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + out_internal_offset = m_clock_core.GetInternalOffset(); + R_SUCCEED(); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/steady_clock.h b/src/core/hle/service/psc/time/steady_clock.h new file mode 100755 index 000000000..115e9b138 --- /dev/null +++ b/src/core/hle/service/psc/time/steady_clock.h @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/psc/time/manager.h" +#include "core/hle/service/server_manager.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { + +class SteadyClock final : public ServiceFramework { +public: + explicit SteadyClock(Core::System& system, std::shared_ptr manager, + bool can_write_steady_clock, bool can_write_uninitialized_clock); + + ~SteadyClock() override = default; + + Result GetCurrentTimePoint(SteadyClockTimePoint& out_time_point); + Result GetTestOffset(s64& out_test_offset); + Result SetTestOffset(s64 test_offset); + Result GetRtcValue(s64& out_rtc_value); + Result IsRtcResetDetected(bool& out_is_detected); + Result GetSetupResultValue(Result& out_result); + Result GetInternalOffset(s64& out_internal_offset); + +private: + void Handle_GetCurrentTimePoint(HLERequestContext& ctx); + void Handle_GetTestOffset(HLERequestContext& ctx); + void Handle_SetTestOffset(HLERequestContext& ctx); + void Handle_GetRtcValue(HLERequestContext& ctx); + void Handle_IsRtcResetDetected(HLERequestContext& ctx); + void Handle_GetSetupResultValue(HLERequestContext& ctx); + void Handle_GetInternalOffset(HLERequestContext& ctx); + + Core::System& m_system; + + StandardSteadyClockCore& m_clock_core; + bool m_can_write_steady_clock; + bool m_can_write_uninitialized_clock; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/system_clock.cpp b/src/core/hle/service/psc/time/system_clock.cpp new file mode 100755 index 000000000..00ca3f0f7 --- /dev/null +++ b/src/core/hle/service/psc/time/system_clock.cpp @@ -0,0 +1,129 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/psc/time/system_clock.h" + +namespace Service::PSC::Time { + +SystemClock::SystemClock(Core::System& system_, SystemClockCore& clock_core, bool can_write_clock, + bool can_write_uninitialized_clock) + : ServiceFramework{system_, "ISystemClock"}, m_system{system}, m_clock_core{clock_core}, + m_can_write_clock{can_write_clock}, m_can_write_uninitialized_clock{ + can_write_uninitialized_clock} { + SystemClockContext ctx{}; + m_clock_core.GetContext(ctx); + // clang-format off + static const FunctionInfo functions[] = { + {0, &SystemClock::Handle_GetCurrentTime, "GetCurrentTime"}, + {1, &SystemClock::Handle_SetCurrentTime, "SetCurrentTime"}, + {2, &SystemClock::Handle_GetSystemClockContext, "GetSystemClockContext"}, + {3, &SystemClock::Handle_SetSystemClockContext, "SetSystemClockContext"}, + {4, &SystemClock::Handle_GetOperationEventReadableHandle, "GetOperationEventReadableHandle"}, + }; + // clang-format on + RegisterHandlers(functions); +} + +void SystemClock::Handle_GetCurrentTime(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + s64 time{}; + auto res = GetCurrentTime(time); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(time); +} + +void SystemClock::Handle_SetCurrentTime(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto time{rp.Pop()}; + + auto res = SetCurrentTime(time); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void SystemClock::Handle_GetSystemClockContext(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + SystemClockContext context{}; + auto res = GetSystemClockContext(context); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(SystemClockContext) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw(context); +} + +void SystemClock::Handle_SetSystemClockContext(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto context{rp.PopRaw()}; + + auto res = SetSystemClockContext(context); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void SystemClock::Handle_GetOperationEventReadableHandle(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + Kernel::KEvent* event{}; + auto res = GetOperationEventReadableHandle(&event); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(res); + rb.PushCopyObjects(event->GetReadableEvent()); +} + +// =============================== Implementations =========================== + +Result SystemClock::GetCurrentTime(s64& out_time) { + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + R_RETURN(m_clock_core.GetCurrentTime(&out_time)); +} + +Result SystemClock::SetCurrentTime(s64 time) { + R_UNLESS(m_can_write_clock, ResultPermissionDenied); + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + R_RETURN(m_clock_core.SetCurrentTime(time)); +} + +Result SystemClock::GetSystemClockContext(SystemClockContext& out_context) { + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + R_RETURN(m_clock_core.GetContext(out_context)); +} + +Result SystemClock::SetSystemClockContext(SystemClockContext& context) { + R_UNLESS(m_can_write_clock, ResultPermissionDenied); + R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(), + ResultClockUninitialized); + + R_RETURN(m_clock_core.SetContextAndWrite(context)); +} + +Result SystemClock::GetOperationEventReadableHandle(Kernel::KEvent** out_event) { + R_SUCCEED_IF(m_operation_event != nullptr); + + m_operation_event = std::make_unique(m_system); + R_UNLESS(m_operation_event != nullptr, ResultFailed); + + m_clock_core.LinkOperationEvent(*m_operation_event); + + *out_event = m_operation_event->m_event; + R_SUCCEED(); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/system_clock.h b/src/core/hle/service/psc/time/system_clock.h new file mode 100755 index 000000000..f30027e7b --- /dev/null +++ b/src/core/hle/service/psc/time/system_clock.h @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/psc/time/manager.h" +#include "core/hle/service/server_manager.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { + +class SystemClock final : public ServiceFramework { +public: + explicit SystemClock(Core::System& system, SystemClockCore& system_clock_core, + bool can_write_clock, bool can_write_uninitialized_clock); + + ~SystemClock() override = default; + + Result GetCurrentTime(s64& out_time); + Result SetCurrentTime(s64 time); + Result GetSystemClockContext(SystemClockContext& out_context); + Result SetSystemClockContext(SystemClockContext& context); + Result GetOperationEventReadableHandle(Kernel::KEvent** out_event); + +private: + void Handle_GetCurrentTime(HLERequestContext& ctx); + void Handle_SetCurrentTime(HLERequestContext& ctx); + void Handle_GetSystemClockContext(HLERequestContext& ctx); + void Handle_SetSystemClockContext(HLERequestContext& ctx); + void Handle_GetOperationEventReadableHandle(HLERequestContext& ctx); + + Core::System& m_system; + + SystemClockCore& m_clock_core; + bool m_can_write_clock; + bool m_can_write_uninitialized_clock; + std::unique_ptr m_operation_event{}; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/time_zone.cpp b/src/core/hle/service/psc/time/time_zone.cpp new file mode 100755 index 000000000..cfee8f866 --- /dev/null +++ b/src/core/hle/service/psc/time/time_zone.cpp @@ -0,0 +1,280 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/psc/time/time_zone.h" + +namespace Service::PSC::Time { +namespace { +constexpr Result ValidateRule(Tz::Rule& rule) { + if (rule.typecnt > static_cast(Tz::TZ_MAX_TYPES) || + rule.timecnt > static_cast(Tz::TZ_MAX_TIMES) || + rule.charcnt > static_cast(Tz::TZ_MAX_CHARS)) { + R_RETURN(ResultTimeZoneOutOfRange); + } + + for (s32 i = 0; i < rule.timecnt; i++) { + if (rule.types[i] >= rule.typecnt) { + R_RETURN(ResultTimeZoneOutOfRange); + } + } + + for (s32 i = 0; i < rule.typecnt; i++) { + if (rule.ttis[i].tt_desigidx >= static_cast(rule.chars.size())) { + R_RETURN(ResultTimeZoneOutOfRange); + } + } + R_SUCCEED(); +} + +constexpr bool GetTimeZoneTime(s64& out_time, Tz::Rule& rule, s64 time, s32 index, + s32 index_offset) { + s32 found_idx{}; + s32 expected_index{index + index_offset}; + s64 time_to_find{time + rule.ttis[rule.types[index]].tt_utoff - + rule.ttis[rule.types[expected_index]].tt_utoff}; + + if (rule.timecnt > 1 && rule.ats[0] <= time_to_find) { + s32 low{1}; + s32 high{rule.timecnt}; + + while (low < high) { + auto mid{(low + high) / 2}; + if (rule.ats[mid] <= time_to_find) { + low = mid + 1; + } else if (rule.ats[mid] > time_to_find) { + high = mid; + } + } + found_idx = low - 1; + } + + if (found_idx == expected_index) { + out_time = time_to_find; + } + return found_idx == expected_index; +} +} // namespace + +void TimeZone::SetTimePoint(SteadyClockTimePoint& time_point) { + std::scoped_lock l{m_mutex}; + m_steady_clock_time_point = time_point; +} + +void TimeZone::SetTotalLocationNameCount(u32 count) { + std::scoped_lock l{m_mutex}; + m_total_location_name_count = count; +} + +void TimeZone::SetRuleVersion(RuleVersion& rule_version) { + std::scoped_lock l{m_mutex}; + m_rule_version = rule_version; +} + +Result TimeZone::GetLocationName(LocationName& out_name) { + std::scoped_lock l{m_mutex}; + R_UNLESS(m_initialized, ResultClockUninitialized); + out_name = m_location; + R_SUCCEED(); +} + +Result TimeZone::GetTotalLocationCount(u32& out_count) { + std::scoped_lock l{m_mutex}; + if (!m_initialized) { + return ResultClockUninitialized; + } + + out_count = m_total_location_name_count; + R_SUCCEED(); +} + +Result TimeZone::GetRuleVersion(RuleVersion& out_rule_version) { + std::scoped_lock l{m_mutex}; + if (!m_initialized) { + return ResultClockUninitialized; + } + out_rule_version = m_rule_version; + R_SUCCEED(); +} + +Result TimeZone::GetTimePoint(SteadyClockTimePoint& out_time_point) { + std::scoped_lock l{m_mutex}; + if (!m_initialized) { + return ResultClockUninitialized; + } + out_time_point = m_steady_clock_time_point; + R_SUCCEED(); +} + +Result TimeZone::ToCalendarTime(CalendarTime& out_calendar_time, + CalendarAdditionalInfo& out_additional_info, s64 time, + Tz::Rule& rule) { + std::scoped_lock l{m_mutex}; + R_RETURN(ToCalendarTimeImpl(out_calendar_time, out_additional_info, time, rule)); +} + +Result TimeZone::ToCalendarTimeWithMyRule(CalendarTime& calendar_time, + CalendarAdditionalInfo& calendar_additional, s64 time) { + // This is checked outside the mutex. Bug? + if (!m_initialized) { + return ResultClockUninitialized; + } + + std::scoped_lock l{m_mutex}; + R_RETURN(ToCalendarTimeImpl(calendar_time, calendar_additional, time, m_my_rule)); +} + +Result TimeZone::ParseBinary(LocationName& name, std::span binary) { + std::scoped_lock l{m_mutex}; + + Tz::Rule tmp_rule{}; + R_TRY(ParseBinaryImpl(tmp_rule, binary)); + + m_my_rule = tmp_rule; + m_location = name; + + R_SUCCEED(); +} + +Result TimeZone::ParseBinaryInto(Tz::Rule& out_rule, std::span binary) { + std::scoped_lock l{m_mutex}; + R_RETURN(ParseBinaryImpl(out_rule, binary)); +} + +Result TimeZone::ToPosixTime(u32& out_count, std::span out_times, u32 out_times_count, + CalendarTime& calendar, Tz::Rule& rule) { + std::scoped_lock l{m_mutex}; + + auto res = ToPosixTimeImpl(out_count, out_times, out_times_count, calendar, rule, -1); + + if (res != ResultSuccess) { + if (res == ResultTimeZoneNotFound) { + res = ResultSuccess; + out_count = 0; + } + } else if (out_count == 2 && out_times[0] > out_times[1]) { + std::swap(out_times[0], out_times[1]); + } + R_RETURN(res); +} + +Result TimeZone::ToPosixTimeWithMyRule(u32& out_count, std::span out_times, + u32 out_times_count, CalendarTime& calendar) { + std::scoped_lock l{m_mutex}; + + auto res = ToPosixTimeImpl(out_count, out_times, out_times_count, calendar, m_my_rule, -1); + + if (res != ResultSuccess) { + if (res == ResultTimeZoneNotFound) { + res = ResultSuccess; + out_count = 0; + } + } else if (out_count == 2 && out_times[0] > out_times[1]) { + std::swap(out_times[0], out_times[1]); + } + R_RETURN(res); +} + +Result TimeZone::ParseBinaryImpl(Tz::Rule& out_rule, std::span binary) { + if (Tz::ParseTimeZoneBinary(out_rule, binary)) { + R_RETURN(ResultTimeZoneParseFailed); + } + R_SUCCEED(); +} + +Result TimeZone::ToCalendarTimeImpl(CalendarTime& out_calendar_time, + CalendarAdditionalInfo& out_additional_info, s64 time, + Tz::Rule& rule) { + R_TRY(ValidateRule(rule)); + + Tz::CalendarTimeInternal calendar_internal{}; + time_t time_tmp{static_cast(time)}; + if (Tz::localtime_rz(&calendar_internal, &rule, &time_tmp)) { + R_RETURN(ResultOverflow); + } + + out_calendar_time.year = static_cast(calendar_internal.tm_year + 1900); + out_calendar_time.month = static_cast(calendar_internal.tm_mon + 1); + out_calendar_time.day = static_cast(calendar_internal.tm_mday); + out_calendar_time.hour = static_cast(calendar_internal.tm_hour); + out_calendar_time.minute = static_cast(calendar_internal.tm_min); + out_calendar_time.second = static_cast(calendar_internal.tm_sec); + + out_additional_info.day_of_week = calendar_internal.tm_wday; + out_additional_info.day_of_year = calendar_internal.tm_yday; + + std::memcpy(out_additional_info.name.data(), calendar_internal.tm_zone.data(), + out_additional_info.name.size()); + out_additional_info.name[out_additional_info.name.size() - 1] = '\0'; + + out_additional_info.is_dst = calendar_internal.tm_isdst; + out_additional_info.ut_offset = calendar_internal.tm_utoff; + + R_SUCCEED(); +} + +Result TimeZone::ToPosixTimeImpl(u32& out_count, std::span out_times, u32 out_times_count, + CalendarTime& calendar, Tz::Rule& rule, s32 is_dst) { + R_TRY(ValidateRule(rule)); + + calendar.month -= 1; + calendar.year -= 1900; + + Tz::CalendarTimeInternal internal{ + .tm_sec = calendar.second, + .tm_min = calendar.minute, + .tm_hour = calendar.hour, + .tm_mday = calendar.day, + .tm_mon = calendar.month, + .tm_year = calendar.year, + .tm_wday = 0, + .tm_yday = 0, + .tm_isdst = is_dst, + .tm_zone = {}, + .tm_utoff = 0, + .time_index = 0, + }; + time_t time_tmp{}; + auto res = Tz::mktime_tzname(&time_tmp, &rule, &internal); + s64 time = static_cast(time_tmp); + + if (res == 1) { + R_RETURN(ResultOverflow); + } else if (res == 2) { + R_RETURN(ResultTimeZoneNotFound); + } + + if (internal.tm_sec != calendar.second || internal.tm_min != calendar.minute || + internal.tm_hour != calendar.hour || internal.tm_mday != calendar.day || + internal.tm_mon != calendar.month || internal.tm_year != calendar.year) { + R_RETURN(ResultTimeZoneNotFound); + } + + if (res != 0) { + ASSERT(false); + } + + out_times[0] = time; + if (out_times_count < 2) { + out_count = 1; + R_SUCCEED(); + } + + s64 time2{}; + if (internal.time_index > 0 && GetTimeZoneTime(time2, rule, time, internal.time_index, -1)) { + out_times[1] = time2; + out_count = 2; + R_SUCCEED(); + } + + if (((internal.time_index + 1) < rule.timecnt) && + GetTimeZoneTime(time2, rule, time, internal.time_index, 1)) { + out_times[1] = time2; + out_count = 2; + R_SUCCEED(); + } + + out_count = 1; + R_SUCCEED(); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/time_zone.h b/src/core/hle/service/psc/time/time_zone.h new file mode 100755 index 000000000..ce2acca17 --- /dev/null +++ b/src/core/hle/service/psc/time/time_zone.h @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#include +#include "core/hle/service/psc/time/common.h" + +namespace Service::PSC::Time { + +class TimeZone { +public: + TimeZone() = default; + + bool IsInitialized() const { + return m_initialized; + } + + void SetInitialized() { + m_initialized = true; + } + + void SetTimePoint(SteadyClockTimePoint& time_point); + void SetTotalLocationNameCount(u32 count); + void SetRuleVersion(RuleVersion& rule_version); + Result GetLocationName(LocationName& out_name); + Result GetTotalLocationCount(u32& out_count); + Result GetRuleVersion(RuleVersion& out_rule_version); + Result GetTimePoint(SteadyClockTimePoint& out_time_point); + + Result ToCalendarTime(CalendarTime& out_calendar_time, + CalendarAdditionalInfo& out_additional_info, s64 time, Tz::Rule& rule); + Result ToCalendarTimeWithMyRule(CalendarTime& calendar_time, + CalendarAdditionalInfo& calendar_additional, s64 time); + Result ParseBinary(LocationName& name, std::span binary); + Result ParseBinaryInto(Tz::Rule& out_rule, std::span binary); + Result ToPosixTime(u32& out_count, std::span out_times, u32 out_times_count, + CalendarTime& calendar, Tz::Rule& rule); + Result ToPosixTimeWithMyRule(u32& out_count, std::span out_times, u32 out_times_count, + CalendarTime& calendar); + +private: + Result ParseBinaryImpl(Tz::Rule& out_rule, std::span binary); + Result ToCalendarTimeImpl(CalendarTime& out_calendar_time, + CalendarAdditionalInfo& out_additional_info, s64 time, + Tz::Rule& rule); + Result ToPosixTimeImpl(u32& out_count, std::span out_times, u32 out_times_count, + CalendarTime& calendar, Tz::Rule& rule, s32 is_dst); + + bool m_initialized{}; + std::recursive_mutex m_mutex; + LocationName m_location{}; + Tz::Rule m_my_rule{}; + SteadyClockTimePoint m_steady_clock_time_point{}; + u32 m_total_location_name_count{}; + RuleVersion m_rule_version{}; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/time_zone_service.cpp b/src/core/hle/service/psc/time/time_zone_service.cpp new file mode 100755 index 000000000..beab6b27b --- /dev/null +++ b/src/core/hle/service/psc/time/time_zone_service.cpp @@ -0,0 +1,289 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include "core/core.h" +#include "core/hle/service/psc/time/time_zone_service.h" + +namespace Service::PSC::Time { + +TimeZoneService::TimeZoneService(Core::System& system_, StandardSteadyClockCore& clock_core, + TimeZone& time_zone, bool can_write_timezone_device_location) + : ServiceFramework{system_, "ITimeZoneService"}, m_system{system}, m_clock_core{clock_core}, + m_time_zone{time_zone}, m_can_write_timezone_device_location{ + can_write_timezone_device_location} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &TimeZoneService::Handle_GetDeviceLocationName, "GetDeviceLocationName"}, + {1, &TimeZoneService::Handle_SetDeviceLocationName, "SetDeviceLocationName"}, + {2, &TimeZoneService::Handle_GetTotalLocationNameCount, "GetTotalLocationNameCount"}, + {3, &TimeZoneService::Handle_LoadLocationNameList, "LoadLocationNameList"}, + {4, &TimeZoneService::Handle_LoadTimeZoneRule, "LoadTimeZoneRule"}, + {5, &TimeZoneService::Handle_GetTimeZoneRuleVersion, "GetTimeZoneRuleVersion"}, + {6, &TimeZoneService::Handle_GetDeviceLocationNameAndUpdatedTime, "GetDeviceLocationNameAndUpdatedTime"}, + {7, &TimeZoneService::Handle_SetDeviceLocationNameWithTimeZoneRule, "SetDeviceLocationNameWithTimeZoneRule"}, + {8, &TimeZoneService::Handle_ParseTimeZoneBinary, "ParseTimeZoneBinary"}, + {20, &TimeZoneService::Handle_GetDeviceLocationNameOperationEventReadableHandle, "GetDeviceLocationNameOperationEventReadableHandle"}, + {100, &TimeZoneService::Handle_ToCalendarTime, "ToCalendarTime"}, + {101, &TimeZoneService::Handle_ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"}, + {201, &TimeZoneService::Handle_ToPosixTime, "ToPosixTime"}, + {202, &TimeZoneService::Handle_ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"}, + }; + // clang-format on + RegisterHandlers(functions); +} + +void TimeZoneService::Handle_GetDeviceLocationName(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + LocationName name{}; + auto res = GetDeviceLocationName(name); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(LocationName) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw(name); +} + +void TimeZoneService::Handle_SetDeviceLocationName(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + [[maybe_unused]] auto name{rp.PopRaw()}; + + if (!m_can_write_timezone_device_location) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultPermissionDenied); + return; + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultNotImplemented); +} + +void TimeZoneService::Handle_GetTotalLocationNameCount(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + u32 count{}; + auto res = GetTotalLocationNameCount(count); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(count); +} + +void TimeZoneService::Handle_LoadLocationNameList(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultNotImplemented); +} + +void TimeZoneService::Handle_LoadTimeZoneRule(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultNotImplemented); +} + +void TimeZoneService::Handle_GetTimeZoneRuleVersion(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + RuleVersion rule_version{}; + auto res = GetTimeZoneRuleVersion(rule_version); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(RuleVersion) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw(rule_version); +} + +void TimeZoneService::Handle_GetDeviceLocationNameAndUpdatedTime(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + LocationName name{}; + SteadyClockTimePoint time_point{}; + auto res = GetDeviceLocationNameAndUpdatedTime(time_point, name); + + IPC::ResponseBuilder rb{ctx, 2 + (sizeof(LocationName) / sizeof(u32)) + + (sizeof(SteadyClockTimePoint) / sizeof(u32))}; + rb.Push(res); + rb.PushRaw(name); + rb.PushRaw(time_point); +} + +void TimeZoneService::Handle_SetDeviceLocationNameWithTimeZoneRule(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto name{rp.PopRaw()}; + + auto binary{ctx.ReadBuffer()}; + auto res = SetDeviceLocationNameWithTimeZoneRule(name, binary); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void TimeZoneService::Handle_ParseTimeZoneBinary(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + auto binary{ctx.ReadBuffer()}; + + Tz::Rule rule{}; + auto res = ParseTimeZoneBinary(rule, binary); + + ctx.WriteBuffer(rule); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void TimeZoneService::Handle_GetDeviceLocationNameOperationEventReadableHandle( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultNotImplemented); +} + +void TimeZoneService::Handle_ToCalendarTime(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto time{rp.Pop()}; + + auto rule_buffer{ctx.ReadBuffer()}; + Tz::Rule rule{}; + std::memcpy(&rule, rule_buffer.data(), sizeof(Tz::Rule)); + + CalendarTime calendar_time{}; + CalendarAdditionalInfo additional_info{}; + auto res = ToCalendarTime(calendar_time, additional_info, time, rule); + + IPC::ResponseBuilder rb{ctx, 2 + (sizeof(CalendarTime) / sizeof(u32)) + + (sizeof(CalendarAdditionalInfo) / sizeof(u32))}; + rb.Push(res); + rb.PushRaw(calendar_time); + rb.PushRaw(additional_info); +} + +void TimeZoneService::Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto time{rp.Pop()}; + + CalendarTime calendar_time{}; + CalendarAdditionalInfo additional_info{}; + auto res = ToCalendarTimeWithMyRule(calendar_time, additional_info, time); + + IPC::ResponseBuilder rb{ctx, 2 + (sizeof(CalendarTime) / sizeof(u32)) + + (sizeof(CalendarAdditionalInfo) / sizeof(u32))}; + rb.Push(res); + rb.PushRaw(calendar_time); + rb.PushRaw(additional_info); +} + +void TimeZoneService::Handle_ToPosixTime(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto calendar{rp.PopRaw()}; + + auto binary{ctx.ReadBuffer()}; + + Tz::Rule rule{}; + std::memcpy(&rule, binary.data(), sizeof(Tz::Rule)); + + u32 count{}; + std::array times{}; + u32 times_count{static_cast(ctx.GetWriteBufferSize() / sizeof(s64))}; + + auto res = ToPosixTime(count, times, times_count, calendar, rule); + + ctx.WriteBuffer(times); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(count); +} + +void TimeZoneService::Handle_ToPosixTimeWithMyRule(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto calendar{rp.PopRaw()}; + + u32 count{}; + std::array times{}; + u32 times_count{static_cast(ctx.GetWriteBufferSize() / sizeof(s64))}; + + auto res = ToPosixTimeWithMyRule(count, times, times_count, calendar); + + ctx.WriteBuffer(times); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.Push(count); +} + +// =============================== Implementations =========================== + +Result TimeZoneService::GetDeviceLocationName(LocationName& out_lcoation_name) { + R_RETURN(m_time_zone.GetLocationName(out_lcoation_name)); +} + +Result TimeZoneService::GetTotalLocationNameCount(u32& out_count) { + R_RETURN(m_time_zone.GetTotalLocationCount(out_count)); +} + +Result TimeZoneService::GetTimeZoneRuleVersion(RuleVersion& out_rule_version) { + R_RETURN(m_time_zone.GetRuleVersion(out_rule_version)); +} + +Result TimeZoneService::GetDeviceLocationNameAndUpdatedTime(SteadyClockTimePoint& out_time_point, + LocationName& location_name) { + R_TRY(m_time_zone.GetLocationName(location_name)); + R_RETURN(m_time_zone.GetTimePoint(out_time_point)); +} + +Result TimeZoneService::SetDeviceLocationNameWithTimeZoneRule(LocationName& location_name, + std::span binary) { + R_UNLESS(m_can_write_timezone_device_location, ResultPermissionDenied); + R_TRY(m_time_zone.ParseBinary(location_name, binary)); + + SteadyClockTimePoint time_point{}; + R_TRY(m_clock_core.GetCurrentTimePoint(time_point)); + + m_time_zone.SetTimePoint(time_point); + R_SUCCEED(); +} + +Result TimeZoneService::ParseTimeZoneBinary(Tz::Rule& out_rule, std::span binary) { + R_RETURN(m_time_zone.ParseBinaryInto(out_rule, binary)); +} + +Result TimeZoneService::ToCalendarTime(CalendarTime& out_calendar_time, + CalendarAdditionalInfo& out_additional_info, s64 time, + Tz::Rule& rule) { + R_RETURN(m_time_zone.ToCalendarTime(out_calendar_time, out_additional_info, time, rule)); +} + +Result TimeZoneService::ToCalendarTimeWithMyRule(CalendarTime& out_calendar_time, + CalendarAdditionalInfo& out_additional_info, + s64 time) { + R_RETURN(m_time_zone.ToCalendarTimeWithMyRule(out_calendar_time, out_additional_info, time)); +} + +Result TimeZoneService::ToPosixTime(u32& out_count, std::span out_times, + u32 out_times_count, CalendarTime& calendar_time, + Tz::Rule& rule) { + R_RETURN(m_time_zone.ToPosixTime(out_count, out_times, out_times_count, calendar_time, rule)); +} + +Result TimeZoneService::ToPosixTimeWithMyRule(u32& out_count, std::span out_times, + u32 out_times_count, CalendarTime& calendar_time) { + R_RETURN( + m_time_zone.ToPosixTimeWithMyRule(out_count, out_times, out_times_count, calendar_time)); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/time_zone_service.h b/src/core/hle/service/psc/time/time_zone_service.h new file mode 100755 index 000000000..eb091653d --- /dev/null +++ b/src/core/hle/service/psc/time/time_zone_service.h @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/psc/time/manager.h" +#include "core/hle/service/server_manager.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Tz { +struct Rule; +} + +namespace Service::PSC::Time { + +class TimeZoneService final : public ServiceFramework { +public: + explicit TimeZoneService(Core::System& system, StandardSteadyClockCore& clock_core, + TimeZone& time_zone, bool can_write_timezone_device_location); + + ~TimeZoneService() override = default; + + Result GetDeviceLocationName(LocationName& out_lcoation_name); + Result GetTotalLocationNameCount(u32& out_count); + Result GetTimeZoneRuleVersion(RuleVersion& out_rule_version); + Result GetDeviceLocationNameAndUpdatedTime(SteadyClockTimePoint& out_time_point, + LocationName& location_name); + Result SetDeviceLocationNameWithTimeZoneRule(LocationName& location_name, + std::span binary); + Result ParseTimeZoneBinary(Tz::Rule& out_rule, std::span binary); + Result ToCalendarTime(CalendarTime& out_calendar_time, + CalendarAdditionalInfo& out_additional_info, s64 time, Tz::Rule& rule); + Result ToCalendarTimeWithMyRule(CalendarTime& out_calendar_time, + CalendarAdditionalInfo& out_additional_info, s64 time); + Result ToPosixTime(u32& out_count, std::span out_times, u32 out_times_count, + CalendarTime& calendar_time, Tz::Rule& rule); + Result ToPosixTimeWithMyRule(u32& out_count, std::span out_times, u32 out_times_count, + CalendarTime& calendar_time); + +private: + void Handle_GetDeviceLocationName(HLERequestContext& ctx); + void Handle_SetDeviceLocationName(HLERequestContext& ctx); + void Handle_GetTotalLocationNameCount(HLERequestContext& ctx); + void Handle_LoadLocationNameList(HLERequestContext& ctx); + void Handle_LoadTimeZoneRule(HLERequestContext& ctx); + void Handle_GetTimeZoneRuleVersion(HLERequestContext& ctx); + void Handle_GetDeviceLocationNameAndUpdatedTime(HLERequestContext& ctx); + void Handle_SetDeviceLocationNameWithTimeZoneRule(HLERequestContext& ctx); + void Handle_ParseTimeZoneBinary(HLERequestContext& ctx); + void Handle_GetDeviceLocationNameOperationEventReadableHandle(HLERequestContext& ctx); + void Handle_ToCalendarTime(HLERequestContext& ctx); + void Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx); + void Handle_ToPosixTime(HLERequestContext& ctx); + void Handle_ToPosixTimeWithMyRule(HLERequestContext& ctx); + + Core::System& m_system; + + StandardSteadyClockCore& m_clock_core; + TimeZone& m_time_zone; + bool m_can_write_timezone_device_location; +}; + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index e2060f5f7..e47929bf8 100755 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -66,7 +66,6 @@ #include "core/hle/service/sockets/sockets.h" #include "core/hle/service/spl/spl_module.h" #include "core/hle/service/ssl/ssl.h" -#include "core/hle/service/time/time.h" #include "core/hle/service/usb/usb.h" #include "core/hle/service/vi/vi.h" #include "core/reporter.h" @@ -246,6 +245,9 @@ Services::Services(std::shared_ptr& sm, Core::System& system kernel.RunOnGuestCoreProcess("fatal", [&] { Fatal::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("fgm", [&] { FGM::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("friends", [&] { Friend::LoopProcess(system); }); + // glue depends on settings and psc, so they must come first + kernel.RunOnGuestCoreProcess("settings", [&] { Set::LoopProcess(system); }); + kernel.RunOnGuestCoreProcess("psc", [&] { PSC::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("glue", [&] { Glue::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("grc", [&] { GRC::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("hid", [&] { HID::LoopProcess(system); }); @@ -269,13 +271,10 @@ Services::Services(std::shared_ptr& sm, Core::System& system kernel.RunOnGuestCoreProcess("pcv", [&] { PCV::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("prepo", [&] { PlayReport::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("ProcessManager", [&] { PM::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("psc", [&] { PSC::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("ptm", [&] { PTM::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("ro", [&] { RO::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("settings", [&] { Set::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("spl", [&] { SPL::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("ssl", [&] { SSL::LoopProcess(system); }); - kernel.RunOnGuestCoreProcess("time", [&] { Time::LoopProcess(system); }); kernel.RunOnGuestCoreProcess("usb", [&] { USB::LoopProcess(system); }); // clang-format on } diff --git a/src/core/hle/service/set/factory_settings_server.cpp b/src/core/hle/service/set/factory_settings_server.cpp new file mode 100755 index 000000000..a8e307ae2 --- /dev/null +++ b/src/core/hle/service/set/factory_settings_server.cpp @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/set/factory_settings_server.h" + +namespace Service::Set { + +IFactorySettingsServer::IFactorySettingsServer(Core::System& system_) + : ServiceFramework{system_, "set:cal"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetBluetoothBdAddress"}, + {1, nullptr, "GetConfigurationId1"}, + {2, nullptr, "GetAccelerometerOffset"}, + {3, nullptr, "GetAccelerometerScale"}, + {4, nullptr, "GetGyroscopeOffset"}, + {5, nullptr, "GetGyroscopeScale"}, + {6, nullptr, "GetWirelessLanMacAddress"}, + {7, nullptr, "GetWirelessLanCountryCodeCount"}, + {8, nullptr, "GetWirelessLanCountryCodes"}, + {9, nullptr, "GetSerialNumber"}, + {10, nullptr, "SetInitialSystemAppletProgramId"}, + {11, nullptr, "SetOverlayDispProgramId"}, + {12, nullptr, "GetBatteryLot"}, + {14, nullptr, "GetEciDeviceCertificate"}, + {15, nullptr, "GetEticketDeviceCertificate"}, + {16, nullptr, "GetSslKey"}, + {17, nullptr, "GetSslCertificate"}, + {18, nullptr, "GetGameCardKey"}, + {19, nullptr, "GetGameCardCertificate"}, + {20, nullptr, "GetEciDeviceKey"}, + {21, nullptr, "GetEticketDeviceKey"}, + {22, nullptr, "GetSpeakerParameter"}, + {23, nullptr, "GetLcdVendorId"}, + {24, nullptr, "GetEciDeviceCertificate2"}, + {25, nullptr, "GetEciDeviceKey2"}, + {26, nullptr, "GetAmiiboKey"}, + {27, nullptr, "GetAmiiboEcqvCertificate"}, + {28, nullptr, "GetAmiiboEcdsaCertificate"}, + {29, nullptr, "GetAmiiboEcqvBlsKey"}, + {30, nullptr, "GetAmiiboEcqvBlsCertificate"}, + {31, nullptr, "GetAmiiboEcqvBlsRootCertificate"}, + {32, nullptr, "GetUsbTypeCPowerSourceCircuitVersion"}, + {33, nullptr, "GetAnalogStickModuleTypeL"}, + {34, nullptr, "GetAnalogStickModelParameterL"}, + {35, nullptr, "GetAnalogStickFactoryCalibrationL"}, + {36, nullptr, "GetAnalogStickModuleTypeR"}, + {37, nullptr, "GetAnalogStickModelParameterR"}, + {38, nullptr, "GetAnalogStickFactoryCalibrationR"}, + {39, nullptr, "GetConsoleSixAxisSensorModuleType"}, + {40, nullptr, "GetConsoleSixAxisSensorHorizontalOffset"}, + {41, nullptr, "GetBatteryVersion"}, + {42, nullptr, "GetDeviceId"}, + {43, nullptr, "GetConsoleSixAxisSensorMountType"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IFactorySettingsServer::~IFactorySettingsServer() = default; + +} // namespace Service::Set diff --git a/src/core/hle/service/set/factory_settings_server.h b/src/core/hle/service/set/factory_settings_server.h new file mode 100755 index 000000000..e64cd1380 --- /dev/null +++ b/src/core/hle/service/set/factory_settings_server.h @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::Set { + +class IFactorySettingsServer final : public ServiceFramework { +public: + explicit IFactorySettingsServer(Core::System& system_); + ~IFactorySettingsServer() override; +}; + +} // namespace Service::Set diff --git a/src/core/hle/service/set/firmware_debug_settings_server.cpp b/src/core/hle/service/set/firmware_debug_settings_server.cpp new file mode 100755 index 000000000..b3a5e623b --- /dev/null +++ b/src/core/hle/service/set/firmware_debug_settings_server.cpp @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/set/firmware_debug_settings_server.h" + +namespace Service::Set { + +IFirmwareDebugSettingsServer::IFirmwareDebugSettingsServer(Core::System& system_) + : ServiceFramework{system_, "set:fd"} { + // clang-format off + static const FunctionInfo functions[] = { + {2, nullptr, "SetSettingsItemValue"}, + {3, nullptr, "ResetSettingsItemValue"}, + {4, nullptr, "CreateSettingsItemKeyIterator"}, + {10, nullptr, "ReadSettings"}, + {11, nullptr, "ResetSettings"}, + {20, nullptr, "SetWebInspectorFlag"}, + {21, nullptr, "SetAllowedSslHosts"}, + {22, nullptr, "SetHostFsMountPoint"}, + {23, nullptr, "SetMemoryUsageRateFlag"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IFirmwareDebugSettingsServer::~IFirmwareDebugSettingsServer() = default; + +} // namespace Service::Set diff --git a/src/core/hle/service/set/firmware_debug_settings_server.h b/src/core/hle/service/set/firmware_debug_settings_server.h new file mode 100755 index 000000000..5dae2263e --- /dev/null +++ b/src/core/hle/service/set/firmware_debug_settings_server.h @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::Set { + +class IFirmwareDebugSettingsServer final : public ServiceFramework { +public: + explicit IFirmwareDebugSettingsServer(Core::System& system_); + ~IFirmwareDebugSettingsServer() override; +}; + +} // namespace Service::Set diff --git a/src/core/hle/service/set/private_settings.h b/src/core/hle/service/set/private_settings.h index b63eaf45c..b02291ce7 100755 --- a/src/core/hle/service/set/private_settings.h +++ b/src/core/hle/service/set/private_settings.h @@ -9,7 +9,7 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "common/uuid.h" -#include "core/hle/service/time/clock_types.h" +#include "core/hle/service/psc/time/common.h" namespace Service::Set { @@ -29,14 +29,14 @@ static_assert(sizeof(InitialLaunchFlag) == 4, "InitialLaunchFlag is an invalid s struct InitialLaunchSettings { InitialLaunchFlag flags; INSERT_PADDING_BYTES(0x4); - Service::Time::Clock::SteadyClockTimePoint timestamp; + Service::PSC::Time::SteadyClockTimePoint timestamp; }; static_assert(sizeof(InitialLaunchSettings) == 0x20, "InitialLaunchSettings is incorrect size"); #pragma pack(push, 4) struct InitialLaunchSettingsPacked { InitialLaunchFlag flags; - Service::Time::Clock::SteadyClockTimePoint timestamp; + Service::PSC::Time::SteadyClockTimePoint timestamp; }; #pragma pack(pop) static_assert(sizeof(InitialLaunchSettingsPacked) == 0x1C, diff --git a/src/core/hle/service/set/settings.cpp b/src/core/hle/service/set/settings.cpp index c69c6d068..faaccbb21 100755 --- a/src/core/hle/service/set/settings.cpp +++ b/src/core/hle/service/set/settings.cpp @@ -2,21 +2,24 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/server_manager.h" -#include "core/hle/service/set/set.h" -#include "core/hle/service/set/set_cal.h" -#include "core/hle/service/set/set_fd.h" -#include "core/hle/service/set/set_sys.h" +#include "core/hle/service/set/factory_settings_server.h" +#include "core/hle/service/set/firmware_debug_settings_server.h" #include "core/hle/service/set/settings.h" +#include "core/hle/service/set/settings_server.h" +#include "core/hle/service/set/system_settings_server.h" namespace Service::Set { void LoopProcess(Core::System& system) { auto server_manager = std::make_unique(system); - server_manager->RegisterNamedService("set", std::make_shared(system)); - server_manager->RegisterNamedService("set:cal", std::make_shared(system)); - server_manager->RegisterNamedService("set:fd", std::make_shared(system)); - server_manager->RegisterNamedService("set:sys", std::make_shared(system)); + server_manager->RegisterNamedService("set", std::make_shared(system)); + server_manager->RegisterNamedService("set:cal", + std::make_shared(system)); + server_manager->RegisterNamedService("set:fd", + std::make_shared(system)); + server_manager->RegisterNamedService("set:sys", + std::make_shared(system)); ServerManager::RunServer(std::move(server_manager)); } diff --git a/src/core/hle/service/set/settings_server.cpp b/src/core/hle/service/set/settings_server.cpp new file mode 100755 index 000000000..b2caa00ff --- /dev/null +++ b/src/core/hle/service/set/settings_server.cpp @@ -0,0 +1,166 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include "common/logging/log.h" +#include "common/settings.h" +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/set/settings_server.h" + +namespace Service::Set { +namespace { +constexpr std::size_t PRE_4_0_0_MAX_ENTRIES = 0xF; +constexpr std::size_t POST_4_0_0_MAX_ENTRIES = 0x40; + +constexpr Result ResultInvalidLanguage{ErrorModule::Settings, 625}; + +void PushResponseLanguageCode(HLERequestContext& ctx, std::size_t num_language_codes) { + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast(num_language_codes)); +} + +void GetAvailableLanguageCodesImpl(HLERequestContext& ctx, std::size_t max_entries) { + const std::size_t requested_amount = ctx.GetWriteBufferNumElements(); + const std::size_t max_amount = std::min(requested_amount, max_entries); + const std::size_t copy_amount = std::min(available_language_codes.size(), max_amount); + const std::size_t copy_size = copy_amount * sizeof(LanguageCode); + + ctx.WriteBuffer(available_language_codes.data(), copy_size); + PushResponseLanguageCode(ctx, copy_amount); +} + +void GetKeyCodeMapImpl(HLERequestContext& ctx) { + const auto language_code = + available_language_codes[static_cast(Settings::values.language_index.GetValue())]; + const auto key_code = + std::find_if(language_to_layout.cbegin(), language_to_layout.cend(), + [=](const auto& element) { return element.first == language_code; }); + KeyboardLayout layout = KeyboardLayout::EnglishUs; + if (key_code == language_to_layout.cend()) { + LOG_ERROR(Service_SET, + "Could not find keyboard layout for language index {}, defaulting to English us", + Settings::values.language_index.GetValue()); + } else { + layout = key_code->second; + } + + ctx.WriteBuffer(layout); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} +} // Anonymous namespace + +LanguageCode GetLanguageCodeFromIndex(std::size_t index) { + return available_language_codes.at(index); +} + +ISettingsServer::ISettingsServer(Core::System& system_) : ServiceFramework{system_, "set"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &ISettingsServer::GetLanguageCode, "GetLanguageCode"}, + {1, &ISettingsServer::GetAvailableLanguageCodes, "GetAvailableLanguageCodes"}, + {2, &ISettingsServer::MakeLanguageCode, "MakeLanguageCode"}, + {3, &ISettingsServer::GetAvailableLanguageCodeCount, "GetAvailableLanguageCodeCount"}, + {4, &ISettingsServer::GetRegionCode, "GetRegionCode"}, + {5, &ISettingsServer::GetAvailableLanguageCodes2, "GetAvailableLanguageCodes2"}, + {6, &ISettingsServer::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"}, + {7, &ISettingsServer::GetKeyCodeMap, "GetKeyCodeMap"}, + {8, &ISettingsServer::GetQuestFlag, "GetQuestFlag"}, + {9, &ISettingsServer::GetKeyCodeMap2, "GetKeyCodeMap2"}, + {10, nullptr, "GetFirmwareVersionForDebug"}, + {11, &ISettingsServer::GetDeviceNickName, "GetDeviceNickName"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +ISettingsServer::~ISettingsServer() = default; + +void ISettingsServer::GetAvailableLanguageCodes(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + + GetAvailableLanguageCodesImpl(ctx, PRE_4_0_0_MAX_ENTRIES); +} + +void ISettingsServer::MakeLanguageCode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto index = rp.Pop(); + + if (index >= available_language_codes.size()) { + LOG_ERROR(Service_SET, "Invalid language code index! index={}", index); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(Set::ResultInvalidLanguage); + return; + } + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.PushEnum(available_language_codes[index]); +} + +void ISettingsServer::GetAvailableLanguageCodes2(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + + GetAvailableLanguageCodesImpl(ctx, POST_4_0_0_MAX_ENTRIES); +} + +void ISettingsServer::GetAvailableLanguageCodeCount(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + + PushResponseLanguageCode(ctx, PRE_4_0_0_MAX_ENTRIES); +} + +void ISettingsServer::GetAvailableLanguageCodeCount2(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + + PushResponseLanguageCode(ctx, POST_4_0_0_MAX_ENTRIES); +} + +void ISettingsServer::GetQuestFlag(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast(Settings::values.quest_flag.GetValue())); +} + +void ISettingsServer::GetLanguageCode(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index.GetValue()); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.PushEnum( + available_language_codes[static_cast(Settings::values.language_index.GetValue())]); +} + +void ISettingsServer::GetRegionCode(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast(Settings::values.region_index.GetValue())); +} + +void ISettingsServer::GetKeyCodeMap(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "Called {}", ctx.Description()); + GetKeyCodeMapImpl(ctx); +} + +void ISettingsServer::GetKeyCodeMap2(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "Called {}", ctx.Description()); + GetKeyCodeMapImpl(ctx); +} + +void ISettingsServer::GetDeviceNickName(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + ctx.WriteBuffer(Settings::values.device_name.GetValue()); +} + +} // namespace Service::Set diff --git a/src/core/hle/service/set/settings_server.h b/src/core/hle/service/set/settings_server.h new file mode 100755 index 000000000..a4e78db6c --- /dev/null +++ b/src/core/hle/service/set/settings_server.h @@ -0,0 +1,95 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/service.h" +#include "core/hle/service/set/system_settings.h" + +namespace Core { +class System; +} + +namespace Service::Set { +enum class KeyboardLayout : u64 { + Japanese = 0, + EnglishUs = 1, + EnglishUsInternational = 2, + EnglishUk = 3, + French = 4, + FrenchCa = 5, + Spanish = 6, + SpanishLatin = 7, + German = 8, + Italian = 9, + Portuguese = 10, + Russian = 11, + Korean = 12, + ChineseSimplified = 13, + ChineseTraditional = 14, +}; + +constexpr std::array available_language_codes = {{ + LanguageCode::JA, + LanguageCode::EN_US, + LanguageCode::FR, + LanguageCode::DE, + LanguageCode::IT, + LanguageCode::ES, + LanguageCode::ZH_CN, + LanguageCode::KO, + LanguageCode::NL, + LanguageCode::PT, + LanguageCode::RU, + LanguageCode::ZH_TW, + LanguageCode::EN_GB, + LanguageCode::FR_CA, + LanguageCode::ES_419, + LanguageCode::ZH_HANS, + LanguageCode::ZH_HANT, + LanguageCode::PT_BR, +}}; + +static constexpr std::array, 18> language_to_layout{{ + {LanguageCode::JA, KeyboardLayout::Japanese}, + {LanguageCode::EN_US, KeyboardLayout::EnglishUs}, + {LanguageCode::FR, KeyboardLayout::French}, + {LanguageCode::DE, KeyboardLayout::German}, + {LanguageCode::IT, KeyboardLayout::Italian}, + {LanguageCode::ES, KeyboardLayout::Spanish}, + {LanguageCode::ZH_CN, KeyboardLayout::ChineseSimplified}, + {LanguageCode::KO, KeyboardLayout::Korean}, + {LanguageCode::NL, KeyboardLayout::EnglishUsInternational}, + {LanguageCode::PT, KeyboardLayout::Portuguese}, + {LanguageCode::RU, KeyboardLayout::Russian}, + {LanguageCode::ZH_TW, KeyboardLayout::ChineseTraditional}, + {LanguageCode::EN_GB, KeyboardLayout::EnglishUk}, + {LanguageCode::FR_CA, KeyboardLayout::FrenchCa}, + {LanguageCode::ES_419, KeyboardLayout::SpanishLatin}, + {LanguageCode::ZH_HANS, KeyboardLayout::ChineseSimplified}, + {LanguageCode::ZH_HANT, KeyboardLayout::ChineseTraditional}, + {LanguageCode::PT_BR, KeyboardLayout::Portuguese}, +}}; + +LanguageCode GetLanguageCodeFromIndex(std::size_t idx); + +class ISettingsServer final : public ServiceFramework { +public: + explicit ISettingsServer(Core::System& system_); + ~ISettingsServer() override; + +private: + void GetLanguageCode(HLERequestContext& ctx); + void GetAvailableLanguageCodes(HLERequestContext& ctx); + void MakeLanguageCode(HLERequestContext& ctx); + void GetAvailableLanguageCodes2(HLERequestContext& ctx); + void GetAvailableLanguageCodeCount(HLERequestContext& ctx); + void GetAvailableLanguageCodeCount2(HLERequestContext& ctx); + void GetQuestFlag(HLERequestContext& ctx); + void GetRegionCode(HLERequestContext& ctx); + void GetKeyCodeMap(HLERequestContext& ctx); + void GetKeyCodeMap2(HLERequestContext& ctx); + void GetDeviceNickName(HLERequestContext& ctx); +}; + +} // namespace Service::Set diff --git a/src/core/hle/service/set/system_settings.cpp b/src/core/hle/service/set/system_settings.cpp index 2723417ad..5977429b2 100755 --- a/src/core/hle/service/set/system_settings.cpp +++ b/src/core/hle/service/set/system_settings.cpp @@ -28,7 +28,7 @@ SystemSettings DefaultSystemSettings() { .cmu_mode = CmuMode::None, .tv_underscan = {}, .tv_gama = 1.0f, - .constrast_ratio = 0.5f, + .contrast_ratio = 0.5f, }; settings.initial_launch_settings_packed = { diff --git a/src/core/hle/service/set/system_settings.h b/src/core/hle/service/set/system_settings.h index ded2906ad..b6793484d 100755 --- a/src/core/hle/service/set/system_settings.h +++ b/src/core/hle/service/set/system_settings.h @@ -9,7 +9,6 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "core/hle/service/set/private_settings.h" -#include "core/hle/service/time/clock_types.h" namespace Service::Set { @@ -208,7 +207,7 @@ struct TvSettings { CmuMode cmu_mode; u32 tv_underscan; f32 tv_gama; - f32 constrast_ratio; + f32 contrast_ratio; }; static_assert(sizeof(TvSettings) == 0x20, "TvSettings is an invalid size"); @@ -270,7 +269,7 @@ struct EulaVersion { EulaVersionClockType clock_type; INSERT_PADDING_BYTES(0x4); s64 posix_time; - Time::Clock::SteadyClockTimePoint timestamp; + Service::PSC::Time::SteadyClockTimePoint timestamp; }; static_assert(sizeof(EulaVersion) == 0x30, "EulaVersion is incorrect size"); @@ -341,7 +340,7 @@ struct SystemSettings { std::array reserved_09934; // nn::settings::system::ErrorReportSharePermission - ErrorReportSharePermission error_report_share_permssion; + ErrorReportSharePermission error_report_share_permission; std::array reserved_09974; @@ -481,13 +480,13 @@ struct SystemSettings { std::array reserved_2951C; // nn::time::SystemClockContext - Service::Time::Clock::SystemClockContext user_system_clock_context; - Service::Time::Clock::SystemClockContext network_system_clock_context; + Service::PSC::Time::SystemClockContext user_system_clock_context; + Service::PSC::Time::SystemClockContext network_system_clock_context; bool user_system_clock_automatic_correction_enabled; std::array pad_295C1; std::array reserved_295C4; // nn::time::SteadyClockTimePoint - Service::Time::Clock::SteadyClockTimePoint + Service::PSC::Time::SteadyClockTimePoint user_system_clock_automatic_correction_updated_time_point; std::array reserved_295E0; @@ -580,10 +579,10 @@ struct SystemSettings { std::array reserved_29ED5; // nn::time::LocationName - Service::Time::TimeZone::LocationName device_time_zone_location_name; + Service::PSC::Time::LocationName device_time_zone_location_name; std::array reserved_29F64; // nn::time::SteadyClockTimePoint - Service::Time::Clock::SteadyClockTimePoint device_time_zone_location_updated_time; + Service::PSC::Time::SteadyClockTimePoint device_time_zone_location_updated_time; std::array reserved_29F80; diff --git a/src/core/hle/service/set/system_settings_server.cpp b/src/core/hle/service/set/system_settings_server.cpp new file mode 100755 index 000000000..fd9d279df --- /dev/null +++ b/src/core/hle/service/set/system_settings_server.cpp @@ -0,0 +1,1279 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#include "common/assert.h" +#include "common/fs/file.h" +#include "common/fs/fs.h" +#include "common/fs/path_util.h" +#include "common/logging/log.h" +#include "common/settings.h" +#include "common/string_util.h" +#include "core/core.h" +#include "core/file_sys/content_archive.h" +#include "core/file_sys/errors.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/hle/service/filesystem/filesystem.h" +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/set/settings_server.h" +#include "core/hle/service/set/system_settings_server.h" + +namespace Service::Set { + +namespace { +constexpr u32 SETTINGS_VERSION{1u}; +constexpr auto SETTINGS_MAGIC = Common::MakeMagic('y', 'u', 'z', 'u', '_', 's', 'e', 't'); +struct SettingsHeader { + u64 magic; + u32 version; + u32 reserved; +}; +} // Anonymous namespace + +Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system, + GetFirmwareVersionType type) { + constexpr u64 FirmwareVersionSystemDataId = 0x0100000000000809; + auto& fsc = system.GetFileSystemController(); + + // Attempt to load version data from disk + const FileSys::RegisteredCache* bis_system{}; + std::unique_ptr nca{}; + FileSys::VirtualDir romfs{}; + + bis_system = fsc.GetSystemNANDContents(); + if (bis_system) { + nca = bis_system->GetEntry(FirmwareVersionSystemDataId, FileSys::ContentRecordType::Data); + } + if (nca) { + if (auto nca_romfs = nca->GetRomFS(); nca_romfs) { + romfs = FileSys::ExtractRomFS(nca_romfs); + } + } + if (!romfs) { + romfs = FileSys::ExtractRomFS( + FileSys::SystemArchive::SynthesizeSystemArchive(FirmwareVersionSystemDataId)); + } + + const auto early_exit_failure = [](std::string_view desc, Result code) { + LOG_ERROR(Service_SET, "General failure while attempting to resolve firmware version ({}).", + desc); + return code; + }; + + const auto ver_file = romfs->GetFile("file"); + if (ver_file == nullptr) { + return early_exit_failure("The system version archive didn't contain the file 'file'.", + FileSys::ERROR_INVALID_ARGUMENT); + } + + auto data = ver_file->ReadAllBytes(); + if (data.size() != sizeof(FirmwareVersionFormat)) { + return early_exit_failure("The system version file 'file' was not the correct size.", + FileSys::ERROR_OUT_OF_BOUNDS); + } + + std::memcpy(&out_firmware, data.data(), sizeof(FirmwareVersionFormat)); + + // If the command is GetFirmwareVersion (as opposed to GetFirmwareVersion2), hardware will + // zero out the REVISION_MINOR field. + if (type == GetFirmwareVersionType::Version1) { + out_firmware.revision_minor = 0; + } + + return ResultSuccess; +} + +ISystemSettingsServer::ISystemSettingsServer(Core::System& system_) + : ServiceFramework{system_, "set:sys"}, m_system{system} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &ISystemSettingsServer::SetLanguageCode, "SetLanguageCode"}, + {1, nullptr, "SetNetworkSettings"}, + {2, nullptr, "GetNetworkSettings"}, + {3, &ISystemSettingsServer::GetFirmwareVersion, "GetFirmwareVersion"}, + {4, &ISystemSettingsServer::GetFirmwareVersion2, "GetFirmwareVersion2"}, + {5, nullptr, "GetFirmwareVersionDigest"}, + {7, nullptr, "GetLockScreenFlag"}, + {8, nullptr, "SetLockScreenFlag"}, + {9, nullptr, "GetBacklightSettings"}, + {10, nullptr, "SetBacklightSettings"}, + {11, nullptr, "SetBluetoothDevicesSettings"}, + {12, nullptr, "GetBluetoothDevicesSettings"}, + {13, &ISystemSettingsServer::GetExternalSteadyClockSourceId, "GetExternalSteadyClockSourceId"}, + {14, &ISystemSettingsServer::SetExternalSteadyClockSourceId, "SetExternalSteadyClockSourceId"}, + {15, &ISystemSettingsServer::GetUserSystemClockContext, "GetUserSystemClockContext"}, + {16, &ISystemSettingsServer::SetUserSystemClockContext, "SetUserSystemClockContext"}, + {17, &ISystemSettingsServer::GetAccountSettings, "GetAccountSettings"}, + {18, &ISystemSettingsServer::SetAccountSettings, "SetAccountSettings"}, + {19, nullptr, "GetAudioVolume"}, + {20, nullptr, "SetAudioVolume"}, + {21, &ISystemSettingsServer::GetEulaVersions, "GetEulaVersions"}, + {22, &ISystemSettingsServer::SetEulaVersions, "SetEulaVersions"}, + {23, &ISystemSettingsServer::GetColorSetId, "GetColorSetId"}, + {24, &ISystemSettingsServer::SetColorSetId, "SetColorSetId"}, + {25, nullptr, "GetConsoleInformationUploadFlag"}, + {26, nullptr, "SetConsoleInformationUploadFlag"}, + {27, nullptr, "GetAutomaticApplicationDownloadFlag"}, + {28, nullptr, "SetAutomaticApplicationDownloadFlag"}, + {29, &ISystemSettingsServer::GetNotificationSettings, "GetNotificationSettings"}, + {30, &ISystemSettingsServer::SetNotificationSettings, "SetNotificationSettings"}, + {31, &ISystemSettingsServer::GetAccountNotificationSettings, "GetAccountNotificationSettings"}, + {32, &ISystemSettingsServer::SetAccountNotificationSettings, "SetAccountNotificationSettings"}, + {35, nullptr, "GetVibrationMasterVolume"}, + {36, nullptr, "SetVibrationMasterVolume"}, + {37, &ISystemSettingsServer::GetSettingsItemValueSize, "GetSettingsItemValueSize"}, + {38, &ISystemSettingsServer::GetSettingsItemValue, "GetSettingsItemValue"}, + {39, &ISystemSettingsServer::GetTvSettings, "GetTvSettings"}, + {40, &ISystemSettingsServer::SetTvSettings, "SetTvSettings"}, + {41, nullptr, "GetEdid"}, + {42, nullptr, "SetEdid"}, + {43, nullptr, "GetAudioOutputMode"}, + {44, nullptr, "SetAudioOutputMode"}, + {45, nullptr, "IsForceMuteOnHeadphoneRemoved"}, + {46, nullptr, "SetForceMuteOnHeadphoneRemoved"}, + {47, &ISystemSettingsServer::GetQuestFlag, "GetQuestFlag"}, + {48, nullptr, "SetQuestFlag"}, + {49, nullptr, "GetDataDeletionSettings"}, + {50, nullptr, "SetDataDeletionSettings"}, + {51, nullptr, "GetInitialSystemAppletProgramId"}, + {52, nullptr, "GetOverlayDispProgramId"}, + {53, &ISystemSettingsServer::GetDeviceTimeZoneLocationName, "GetDeviceTimeZoneLocationName"}, + {54, &ISystemSettingsServer::SetDeviceTimeZoneLocationName, "SetDeviceTimeZoneLocationName"}, + {55, nullptr, "GetWirelessCertificationFileSize"}, + {56, nullptr, "GetWirelessCertificationFile"}, + {57, &ISystemSettingsServer::SetRegionCode, "SetRegionCode"}, + {58, &ISystemSettingsServer::GetNetworkSystemClockContext, "GetNetworkSystemClockContext"}, + {59, &ISystemSettingsServer::SetNetworkSystemClockContext, "SetNetworkSystemClockContext"}, + {60, &ISystemSettingsServer::IsUserSystemClockAutomaticCorrectionEnabled, "IsUserSystemClockAutomaticCorrectionEnabled"}, + {61, &ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionEnabled, "SetUserSystemClockAutomaticCorrectionEnabled"}, + {62, &ISystemSettingsServer::GetDebugModeFlag, "GetDebugModeFlag"}, + {63, &ISystemSettingsServer::GetPrimaryAlbumStorage, "GetPrimaryAlbumStorage"}, + {64, nullptr, "SetPrimaryAlbumStorage"}, + {65, nullptr, "GetUsb30EnableFlag"}, + {66, nullptr, "SetUsb30EnableFlag"}, + {67, nullptr, "GetBatteryLot"}, + {68, nullptr, "GetSerialNumber"}, + {69, nullptr, "GetNfcEnableFlag"}, + {70, nullptr, "SetNfcEnableFlag"}, + {71, &ISystemSettingsServer::GetSleepSettings, "GetSleepSettings"}, + {72, &ISystemSettingsServer::SetSleepSettings, "SetSleepSettings"}, + {73, nullptr, "GetWirelessLanEnableFlag"}, + {74, nullptr, "SetWirelessLanEnableFlag"}, + {75, &ISystemSettingsServer::GetInitialLaunchSettings, "GetInitialLaunchSettings"}, + {76, &ISystemSettingsServer::SetInitialLaunchSettings, "SetInitialLaunchSettings"}, + {77, &ISystemSettingsServer::GetDeviceNickName, "GetDeviceNickName"}, + {78, &ISystemSettingsServer::SetDeviceNickName, "SetDeviceNickName"}, + {79, &ISystemSettingsServer::GetProductModel, "GetProductModel"}, + {80, nullptr, "GetLdnChannel"}, + {81, nullptr, "SetLdnChannel"}, + {82, nullptr, "AcquireTelemetryDirtyFlagEventHandle"}, + {83, nullptr, "GetTelemetryDirtyFlags"}, + {84, nullptr, "GetPtmBatteryLot"}, + {85, nullptr, "SetPtmBatteryLot"}, + {86, nullptr, "GetPtmFuelGaugeParameter"}, + {87, nullptr, "SetPtmFuelGaugeParameter"}, + {88, nullptr, "GetBluetoothEnableFlag"}, + {89, nullptr, "SetBluetoothEnableFlag"}, + {90, &ISystemSettingsServer::GetMiiAuthorId, "GetMiiAuthorId"}, + {91, nullptr, "SetShutdownRtcValue"}, + {92, nullptr, "GetShutdownRtcValue"}, + {93, nullptr, "AcquireFatalDirtyFlagEventHandle"}, + {94, nullptr, "GetFatalDirtyFlags"}, + {95, &ISystemSettingsServer::GetAutoUpdateEnableFlag, "GetAutoUpdateEnableFlag"}, + {96, nullptr, "SetAutoUpdateEnableFlag"}, + {97, nullptr, "GetNxControllerSettings"}, + {98, nullptr, "SetNxControllerSettings"}, + {99, &ISystemSettingsServer::GetBatteryPercentageFlag, "GetBatteryPercentageFlag"}, + {100, nullptr, "SetBatteryPercentageFlag"}, + {101, nullptr, "GetExternalRtcResetFlag"}, + {102, nullptr, "SetExternalRtcResetFlag"}, + {103, nullptr, "GetUsbFullKeyEnableFlag"}, + {104, nullptr, "SetUsbFullKeyEnableFlag"}, + {105, &ISystemSettingsServer::SetExternalSteadyClockInternalOffset, "SetExternalSteadyClockInternalOffset"}, + {106, &ISystemSettingsServer::GetExternalSteadyClockInternalOffset, "GetExternalSteadyClockInternalOffset"}, + {107, nullptr, "GetBacklightSettingsEx"}, + {108, nullptr, "SetBacklightSettingsEx"}, + {109, nullptr, "GetHeadphoneVolumeWarningCount"}, + {110, nullptr, "SetHeadphoneVolumeWarningCount"}, + {111, nullptr, "GetBluetoothAfhEnableFlag"}, + {112, nullptr, "SetBluetoothAfhEnableFlag"}, + {113, nullptr, "GetBluetoothBoostEnableFlag"}, + {114, nullptr, "SetBluetoothBoostEnableFlag"}, + {115, nullptr, "GetInRepairProcessEnableFlag"}, + {116, nullptr, "SetInRepairProcessEnableFlag"}, + {117, nullptr, "GetHeadphoneVolumeUpdateFlag"}, + {118, nullptr, "SetHeadphoneVolumeUpdateFlag"}, + {119, nullptr, "NeedsToUpdateHeadphoneVolume"}, + {120, nullptr, "GetPushNotificationActivityModeOnSleep"}, + {121, nullptr, "SetPushNotificationActivityModeOnSleep"}, + {122, nullptr, "GetServiceDiscoveryControlSettings"}, + {123, nullptr, "SetServiceDiscoveryControlSettings"}, + {124, &ISystemSettingsServer::GetErrorReportSharePermission, "GetErrorReportSharePermission"}, + {125, nullptr, "SetErrorReportSharePermission"}, + {126, &ISystemSettingsServer::GetAppletLaunchFlags, "GetAppletLaunchFlags"}, + {127, &ISystemSettingsServer::SetAppletLaunchFlags, "SetAppletLaunchFlags"}, + {128, nullptr, "GetConsoleSixAxisSensorAccelerationBias"}, + {129, nullptr, "SetConsoleSixAxisSensorAccelerationBias"}, + {130, nullptr, "GetConsoleSixAxisSensorAngularVelocityBias"}, + {131, nullptr, "SetConsoleSixAxisSensorAngularVelocityBias"}, + {132, nullptr, "GetConsoleSixAxisSensorAccelerationGain"}, + {133, nullptr, "SetConsoleSixAxisSensorAccelerationGain"}, + {134, nullptr, "GetConsoleSixAxisSensorAngularVelocityGain"}, + {135, nullptr, "SetConsoleSixAxisSensorAngularVelocityGain"}, + {136, &ISystemSettingsServer::GetKeyboardLayout, "GetKeyboardLayout"}, + {137, nullptr, "SetKeyboardLayout"}, + {138, nullptr, "GetWebInspectorFlag"}, + {139, nullptr, "GetAllowedSslHosts"}, + {140, nullptr, "GetHostFsMountPoint"}, + {141, nullptr, "GetRequiresRunRepairTimeReviser"}, + {142, nullptr, "SetRequiresRunRepairTimeReviser"}, + {143, nullptr, "SetBlePairingSettings"}, + {144, nullptr, "GetBlePairingSettings"}, + {145, nullptr, "GetConsoleSixAxisSensorAngularVelocityTimeBias"}, + {146, nullptr, "SetConsoleSixAxisSensorAngularVelocityTimeBias"}, + {147, nullptr, "GetConsoleSixAxisSensorAngularAcceleration"}, + {148, nullptr, "SetConsoleSixAxisSensorAngularAcceleration"}, + {149, nullptr, "GetRebootlessSystemUpdateVersion"}, + {150, &ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime, "GetDeviceTimeZoneLocationUpdatedTime"}, + {151, &ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime, "SetDeviceTimeZoneLocationUpdatedTime"}, + {152, &ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime, "GetUserSystemClockAutomaticCorrectionUpdatedTime"}, + {153, &ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime, "SetUserSystemClockAutomaticCorrectionUpdatedTime"}, + {154, nullptr, "GetAccountOnlineStorageSettings"}, + {155, nullptr, "SetAccountOnlineStorageSettings"}, + {156, nullptr, "GetPctlReadyFlag"}, + {157, nullptr, "SetPctlReadyFlag"}, + {158, nullptr, "GetAnalogStickUserCalibrationL"}, + {159, nullptr, "SetAnalogStickUserCalibrationL"}, + {160, nullptr, "GetAnalogStickUserCalibrationR"}, + {161, nullptr, "SetAnalogStickUserCalibrationR"}, + {162, nullptr, "GetPtmBatteryVersion"}, + {163, nullptr, "SetPtmBatteryVersion"}, + {164, nullptr, "GetUsb30HostEnableFlag"}, + {165, nullptr, "SetUsb30HostEnableFlag"}, + {166, nullptr, "GetUsb30DeviceEnableFlag"}, + {167, nullptr, "SetUsb30DeviceEnableFlag"}, + {168, nullptr, "GetThemeId"}, + {169, nullptr, "SetThemeId"}, + {170, &ISystemSettingsServer::GetChineseTraditionalInputMethod, "GetChineseTraditionalInputMethod"}, + {171, nullptr, "SetChineseTraditionalInputMethod"}, + {172, nullptr, "GetPtmCycleCountReliability"}, + {173, nullptr, "SetPtmCycleCountReliability"}, + {174, &ISystemSettingsServer::GetHomeMenuScheme, "GetHomeMenuScheme"}, + {175, nullptr, "GetThemeSettings"}, + {176, nullptr, "SetThemeSettings"}, + {177, nullptr, "GetThemeKey"}, + {178, nullptr, "SetThemeKey"}, + {179, nullptr, "GetZoomFlag"}, + {180, nullptr, "SetZoomFlag"}, + {181, nullptr, "GetT"}, + {182, nullptr, "SetT"}, + {183, nullptr, "GetPlatformRegion"}, + {184, nullptr, "SetPlatformRegion"}, + {185, &ISystemSettingsServer::GetHomeMenuSchemeModel, "GetHomeMenuSchemeModel"}, + {186, nullptr, "GetMemoryUsageRateFlag"}, + {187, nullptr, "GetTouchScreenMode"}, + {188, nullptr, "SetTouchScreenMode"}, + {189, nullptr, "GetButtonConfigSettingsFull"}, + {190, nullptr, "SetButtonConfigSettingsFull"}, + {191, nullptr, "GetButtonConfigSettingsEmbedded"}, + {192, nullptr, "SetButtonConfigSettingsEmbedded"}, + {193, nullptr, "GetButtonConfigSettingsLeft"}, + {194, nullptr, "SetButtonConfigSettingsLeft"}, + {195, nullptr, "GetButtonConfigSettingsRight"}, + {196, nullptr, "SetButtonConfigSettingsRight"}, + {197, nullptr, "GetButtonConfigRegisteredSettingsEmbedded"}, + {198, nullptr, "SetButtonConfigRegisteredSettingsEmbedded"}, + {199, nullptr, "GetButtonConfigRegisteredSettings"}, + {200, nullptr, "SetButtonConfigRegisteredSettings"}, + {201, &ISystemSettingsServer::GetFieldTestingFlag, "GetFieldTestingFlag"}, + {202, nullptr, "SetFieldTestingFlag"}, + {203, nullptr, "GetPanelCrcMode"}, + {204, nullptr, "SetPanelCrcMode"}, + {205, nullptr, "GetNxControllerSettingsEx"}, + {206, nullptr, "SetNxControllerSettingsEx"}, + {207, nullptr, "GetHearingProtectionSafeguardFlag"}, + {208, nullptr, "SetHearingProtectionSafeguardFlag"}, + {209, nullptr, "GetHearingProtectionSafeguardRemainingTime"}, + {210, nullptr, "SetHearingProtectionSafeguardRemainingTime"}, + }; + // clang-format on + + RegisterHandlers(functions); + + SetupSettings(); + m_save_thread = + std::jthread([this](std::stop_token stop_token) { StoreSettingsThreadFunc(stop_token); }); +} + +ISystemSettingsServer::~ISystemSettingsServer() { + SetSaveNeeded(); + m_save_thread.request_stop(); +} + +bool ISystemSettingsServer::LoadSettingsFile(std::filesystem::path& path, auto&& default_func) { + using settings_type = decltype(default_func()); + + if (!Common::FS::CreateDirs(path)) { + return false; + } + + auto settings_file = path / "settings.dat"; + auto exists = std::filesystem::exists(settings_file); + auto file_size_ok = exists && std::filesystem::file_size(settings_file) == + sizeof(SettingsHeader) + sizeof(settings_type); + + auto ResetToDefault = [&]() { + auto default_settings{default_func()}; + + SettingsHeader hdr{ + .magic = SETTINGS_MAGIC, + .version = SETTINGS_VERSION, + .reserved = 0u, + }; + + std::ofstream out_settings_file(settings_file, std::ios::out | std::ios::binary); + out_settings_file.write(reinterpret_cast(&hdr), sizeof(hdr)); + out_settings_file.write(reinterpret_cast(&default_settings), + sizeof(settings_type)); + out_settings_file.flush(); + out_settings_file.close(); + }; + + constexpr auto IsHeaderValid = [](std::ifstream& file) -> bool { + if (!file.is_open()) { + return false; + } + SettingsHeader hdr{}; + file.read(reinterpret_cast(&hdr), sizeof(hdr)); + return hdr.magic == SETTINGS_MAGIC && hdr.version == SETTINGS_VERSION; + }; + + if (!exists || !file_size_ok) { + ResetToDefault(); + } + + std::ifstream file(settings_file, std::ios::binary | std::ios::in); + if (!IsHeaderValid(file)) { + file.close(); + ResetToDefault(); + file = std::ifstream(settings_file, std::ios::binary | std::ios::in); + if (!IsHeaderValid(file)) { + return false; + } + } + + if constexpr (std::is_same_v) { + file.read(reinterpret_cast(&m_private_settings), sizeof(settings_type)); + } else if constexpr (std::is_same_v) { + file.read(reinterpret_cast(&m_device_settings), sizeof(settings_type)); + } else if constexpr (std::is_same_v) { + file.read(reinterpret_cast(&m_appln_settings), sizeof(settings_type)); + } else if constexpr (std::is_same_v) { + file.read(reinterpret_cast(&m_system_settings), sizeof(settings_type)); + } else { + UNREACHABLE(); + } + file.close(); + + return true; +} + +bool ISystemSettingsServer::StoreSettingsFile(std::filesystem::path& path, auto& settings) { + using settings_type = std::decay_t; + + if (!Common::FS::IsDir(path)) { + return false; + } + + auto settings_base = path / "settings"; + auto settings_tmp_file = settings_base; + settings_tmp_file = settings_tmp_file.replace_extension("tmp"); + std::ofstream file(settings_tmp_file, std::ios::binary | std::ios::out); + if (!file.is_open()) { + return false; + } + + SettingsHeader hdr{ + .magic = SETTINGS_MAGIC, + .version = SETTINGS_VERSION, + .reserved = 0u, + }; + file.write(reinterpret_cast(&hdr), sizeof(hdr)); + + if constexpr (std::is_same_v) { + file.write(reinterpret_cast(&m_private_settings), sizeof(settings_type)); + } else if constexpr (std::is_same_v) { + file.write(reinterpret_cast(&m_device_settings), sizeof(settings_type)); + } else if constexpr (std::is_same_v) { + file.write(reinterpret_cast(&m_appln_settings), sizeof(settings_type)); + } else if constexpr (std::is_same_v) { + file.write(reinterpret_cast(&m_system_settings), sizeof(settings_type)); + } else { + UNREACHABLE(); + } + file.close(); + + std::filesystem::rename(settings_tmp_file, settings_base.replace_extension("dat")); + + return true; +} + +void ISystemSettingsServer::SetLanguageCode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + m_system_settings.language_code = rp.PopEnum(); + SetSaveNeeded(); + + LOG_INFO(Service_SET, "called, language_code={}", m_system_settings.language_code); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetFirmwareVersion(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + + FirmwareVersionFormat firmware_data{}; + const auto result = + GetFirmwareVersionImpl(firmware_data, system, GetFirmwareVersionType::Version1); + + if (result.IsSuccess()) { + ctx.WriteBuffer(firmware_data); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void ISystemSettingsServer::GetFirmwareVersion2(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + + FirmwareVersionFormat firmware_data{}; + const auto result = + GetFirmwareVersionImpl(firmware_data, system, GetFirmwareVersionType::Version2); + + if (result.IsSuccess()) { + ctx.WriteBuffer(firmware_data); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void ISystemSettingsServer::GetExternalSteadyClockSourceId(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + + Common::UUID id{}; + auto res = GetExternalSteadyClockSourceId(id); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(Common::UUID) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw(id); +} + +void ISystemSettingsServer::SetExternalSteadyClockSourceId(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + + IPC::RequestParser rp{ctx}; + auto id{rp.PopRaw()}; + + auto res = SetExternalSteadyClockSourceId(id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ISystemSettingsServer::GetUserSystemClockContext(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + + Service::PSC::Time::SystemClockContext context{}; + auto res = GetUserSystemClockContext(context); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::SystemClockContext) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw(context); +} + +void ISystemSettingsServer::SetUserSystemClockContext(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + + IPC::RequestParser rp{ctx}; + auto context{rp.PopRaw()}; + + auto res = SetUserSystemClockContext(context); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ISystemSettingsServer::GetAccountSettings(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushRaw(m_system_settings.account_settings); +} + +void ISystemSettingsServer::SetAccountSettings(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + m_system_settings.account_settings = rp.PopRaw(); + SetSaveNeeded(); + + LOG_INFO(Service_SET, "called, account_settings_flags={}", + m_system_settings.account_settings.flags); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetEulaVersions(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + + ctx.WriteBuffer(m_system_settings.eula_versions); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(m_system_settings.eula_version_count); +} + +void ISystemSettingsServer::SetEulaVersions(HLERequestContext& ctx) { + const auto elements = ctx.GetReadBufferNumElements(); + const auto buffer_data = ctx.ReadBuffer(); + + LOG_INFO(Service_SET, "called, elements={}", elements); + ASSERT(elements <= m_system_settings.eula_versions.size()); + + m_system_settings.eula_version_count = static_cast(elements); + std::memcpy(&m_system_settings.eula_versions, buffer_data.data(), + sizeof(EulaVersion) * elements); + SetSaveNeeded(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetColorSetId(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(m_system_settings.color_set_id); +} + +void ISystemSettingsServer::SetColorSetId(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + m_system_settings.color_set_id = rp.PopEnum(); + SetSaveNeeded(); + + LOG_DEBUG(Service_SET, "called, color_set={}", m_system_settings.color_set_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetNotificationSettings(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + + IPC::ResponseBuilder rb{ctx, 8}; + rb.Push(ResultSuccess); + rb.PushRaw(m_system_settings.notification_settings); +} + +void ISystemSettingsServer::SetNotificationSettings(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + m_system_settings.notification_settings = rp.PopRaw(); + SetSaveNeeded(); + + LOG_INFO(Service_SET, "called, flags={}, volume={}, head_time={}:{}, tailt_time={}:{}", + m_system_settings.notification_settings.flags.raw, + m_system_settings.notification_settings.volume, + m_system_settings.notification_settings.start_time.hour, + m_system_settings.notification_settings.start_time.minute, + m_system_settings.notification_settings.stop_time.hour, + m_system_settings.notification_settings.stop_time.minute); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetAccountNotificationSettings(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + + ctx.WriteBuffer(m_system_settings.account_notification_settings); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(m_system_settings.account_notification_settings_count); +} + +void ISystemSettingsServer::SetAccountNotificationSettings(HLERequestContext& ctx) { + const auto elements = ctx.GetReadBufferNumElements(); + const auto buffer_data = ctx.ReadBuffer(); + + LOG_INFO(Service_SET, "called, elements={}", elements); + + ASSERT(elements <= m_system_settings.account_notification_settings.size()); + + m_system_settings.account_notification_settings_count = static_cast(elements); + std::memcpy(&m_system_settings.account_notification_settings, buffer_data.data(), + elements * sizeof(AccountNotificationSettings)); + SetSaveNeeded(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +// FIXME: implement support for the real system_settings.ini + +template +static std::vector ToBytes(const T& value) { + static_assert(std::is_trivially_copyable_v); + + const auto* begin = reinterpret_cast(&value); + const auto* end = begin + sizeof(T); + + return std::vector(begin, end); +} + +using Settings = + std::map, std::less<>>, std::less<>>; + +static Settings GetSettings() { + Settings ret; + + ret["hbloader"]["applet_heap_size"] = ToBytes(u64{0x0}); + ret["hbloader"]["applet_heap_reservation_size"] = ToBytes(u64{0x8600000}); + + // Time + ret["time"]["notify_time_to_fs_interval_seconds"] = ToBytes(s32{600}); + ret["time"]["standard_network_clock_sufficient_accuracy_minutes"] = + ToBytes(s32{43200}); // 30 days + ret["time"]["standard_steady_clock_rtc_update_interval_minutes"] = ToBytes(s32{5}); + ret["time"]["standard_steady_clock_test_offset_minutes"] = ToBytes(s32{0}); + ret["time"]["standard_user_clock_initial_year"] = ToBytes(s32{2023}); + + return ret; +} + +void ISystemSettingsServer::GetSettingsItemValueSize(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + + // The category of the setting. This corresponds to the top-level keys of + // system_settings.ini. + const auto setting_category_buf{ctx.ReadBuffer(0)}; + const std::string setting_category{setting_category_buf.begin(), setting_category_buf.end()}; + + // The name of the setting. This corresponds to the second-level keys of + // system_settings.ini. + const auto setting_name_buf{ctx.ReadBuffer(1)}; + const std::string setting_name{setting_name_buf.begin(), setting_name_buf.end()}; + + auto settings{GetSettings()}; + u64 response_size{0}; + + if (settings.contains(setting_category) && settings[setting_category].contains(setting_name)) { + response_size = settings[setting_category][setting_name].size(); + } + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(response_size == 0 ? ResultUnknown : ResultSuccess); + rb.Push(response_size); +} + +void ISystemSettingsServer::GetSettingsItemValue(HLERequestContext& ctx) { + // The category of the setting. This corresponds to the top-level keys of + // system_settings.ini. + const auto setting_category_buf{ctx.ReadBuffer(0)}; + const std::string setting_category{setting_category_buf.begin(), setting_category_buf.end()}; + + // The name of the setting. This corresponds to the second-level keys of + // system_settings.ini. + const auto setting_name_buf{ctx.ReadBuffer(1)}; + const std::string setting_name{setting_name_buf.begin(), setting_name_buf.end()}; + + std::vector value; + auto response = GetSettingsItemValue(value, setting_category, setting_name); + + LOG_INFO(Service_SET, "called. category={}, name={} -- res=0x{:X}", setting_category, + setting_name, response.raw); + + ctx.WriteBuffer(value.data(), value.size()); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(response); +} + +void ISystemSettingsServer::GetTvSettings(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + + IPC::ResponseBuilder rb{ctx, 10}; + rb.Push(ResultSuccess); + rb.PushRaw(m_system_settings.tv_settings); +} + +void ISystemSettingsServer::SetTvSettings(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + m_system_settings.tv_settings = rp.PopRaw(); + SetSaveNeeded(); + + LOG_INFO(Service_SET, + "called, flags={}, cmu_mode={}, contrast_ratio={}, hdmi_content_type={}, " + "rgb_range={}, tv_gama={}, tv_resolution={}, tv_underscan={}", + m_system_settings.tv_settings.flags.raw, m_system_settings.tv_settings.cmu_mode, + m_system_settings.tv_settings.contrast_ratio, + m_system_settings.tv_settings.hdmi_content_type, + m_system_settings.tv_settings.rgb_range, m_system_settings.tv_settings.tv_gama, + m_system_settings.tv_settings.tv_resolution, + m_system_settings.tv_settings.tv_underscan); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetDebugModeFlag(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(0); +} + +void ISystemSettingsServer::GetQuestFlag(HLERequestContext& ctx) { + LOG_WARNING(Service_SET, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(QuestFlag::Retail); +} + +void ISystemSettingsServer::GetDeviceTimeZoneLocationName(HLERequestContext& ctx) { + LOG_WARNING(Service_SET, "called"); + + Service::PSC::Time::LocationName name{}; + auto res = GetDeviceTimeZoneLocationName(name); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::LocationName) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw(name); +} + +void ISystemSettingsServer::SetDeviceTimeZoneLocationName(HLERequestContext& ctx) { + LOG_WARNING(Service_SET, "called"); + + IPC::RequestParser rp{ctx}; + auto name{rp.PopRaw()}; + + auto res = SetDeviceTimeZoneLocationName(name); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ISystemSettingsServer::SetRegionCode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + m_system_settings.region_code = rp.PopEnum(); + SetSaveNeeded(); + + LOG_INFO(Service_SET, "called, region_code={}", m_system_settings.region_code); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetNetworkSystemClockContext(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + + Service::PSC::Time::SystemClockContext context{}; + auto res = GetNetworkSystemClockContext(context); + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::SystemClockContext) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw(context); +} + +void ISystemSettingsServer::SetNetworkSystemClockContext(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + + IPC::RequestParser rp{ctx}; + auto context{rp.PopRaw()}; + + auto res = SetNetworkSystemClockContext(context); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ISystemSettingsServer::IsUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + + bool enabled{}; + auto res = IsUserSystemClockAutomaticCorrectionEnabled(enabled); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.PushRaw(enabled); +} + +void ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + + IPC::RequestParser rp{ctx}; + auto enabled{rp.Pop()}; + + auto res = SetUserSystemClockAutomaticCorrectionEnabled(enabled); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ISystemSettingsServer::GetPrimaryAlbumStorage(HLERequestContext& ctx) { + LOG_WARNING(Service_SET, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(PrimaryAlbumStorage::SdCard); +} + +void ISystemSettingsServer::GetSleepSettings(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + + IPC::ResponseBuilder rb{ctx, 5}; + rb.Push(ResultSuccess); + rb.PushRaw(m_system_settings.sleep_settings); +} + +void ISystemSettingsServer::SetSleepSettings(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + m_system_settings.sleep_settings = rp.PopRaw(); + SetSaveNeeded(); + + LOG_INFO(Service_SET, "called, flags={}, handheld_sleep_plan={}, console_sleep_plan={}", + m_system_settings.sleep_settings.flags.raw, + m_system_settings.sleep_settings.handheld_sleep_plan, + m_system_settings.sleep_settings.console_sleep_plan); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetInitialLaunchSettings(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called"); + IPC::ResponseBuilder rb{ctx, 10}; + rb.Push(ResultSuccess); + rb.PushRaw(m_system_settings.initial_launch_settings_packed); +} + +void ISystemSettingsServer::SetInitialLaunchSettings(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto initial_launch_settings = rp.PopRaw(); + + m_system_settings.initial_launch_settings_packed.flags = initial_launch_settings.flags; + m_system_settings.initial_launch_settings_packed.timestamp = initial_launch_settings.timestamp; + SetSaveNeeded(); + + LOG_INFO(Service_SET, "called, flags={}, timestamp={}", + m_system_settings.initial_launch_settings_packed.flags.raw, + m_system_settings.initial_launch_settings_packed.timestamp.time_point); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetDeviceNickName(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + + ctx.WriteBuffer(::Settings::values.device_name.GetValue()); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::SetDeviceNickName(HLERequestContext& ctx) { + const std::string device_name = Common::StringFromBuffer(ctx.ReadBuffer()); + + LOG_INFO(Service_SET, "called, device_name={}", device_name); + + ::Settings::values.device_name = device_name; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetProductModel(HLERequestContext& ctx) { + const u32 product_model = 1; + + LOG_WARNING(Service_SET, "(STUBBED) called, product_model={}", product_model); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(product_model); +} + +void ISystemSettingsServer::GetMiiAuthorId(HLERequestContext& ctx) { + const auto author_id = Common::UUID::MakeDefault(); + + LOG_WARNING(Service_SET, "(STUBBED) called, author_id={}", author_id.FormattedString()); + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.PushRaw(author_id); +} + +void ISystemSettingsServer::GetAutoUpdateEnableFlag(HLERequestContext& ctx) { + u8 auto_update_flag{}; + + LOG_WARNING(Service_SET, "(STUBBED) called, auto_update_flag={}", auto_update_flag); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(auto_update_flag); +} + +void ISystemSettingsServer::GetBatteryPercentageFlag(HLERequestContext& ctx) { + u8 battery_percentage_flag{1}; + + LOG_WARNING(Service_SET, "(STUBBED) called, battery_percentage_flag={}", + battery_percentage_flag); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(battery_percentage_flag); +} + +void ISystemSettingsServer::SetExternalSteadyClockInternalOffset(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called."); + + IPC::RequestParser rp{ctx}; + auto offset{rp.Pop()}; + + auto res = SetExternalSteadyClockInternalOffset(offset); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ISystemSettingsServer::GetExternalSteadyClockInternalOffset(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called."); + + s64 offset{}; + auto res = GetExternalSteadyClockInternalOffset(offset); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(offset); +} + +void ISystemSettingsServer::GetErrorReportSharePermission(HLERequestContext& ctx) { + LOG_WARNING(Service_SET, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(ErrorReportSharePermission::Denied); +} + +void ISystemSettingsServer::GetAppletLaunchFlags(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called, applet_launch_flag={}", m_system_settings.applet_launch_flag); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(m_system_settings.applet_launch_flag); +} + +void ISystemSettingsServer::SetAppletLaunchFlags(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + m_system_settings.applet_launch_flag = rp.Pop(); + SetSaveNeeded(); + + LOG_INFO(Service_SET, "called, applet_launch_flag={}", m_system_settings.applet_launch_flag); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISystemSettingsServer::GetKeyboardLayout(HLERequestContext& ctx) { + const auto language_code = + available_language_codes[static_cast(::Settings::values.language_index.GetValue())]; + const auto key_code = + std::find_if(language_to_layout.cbegin(), language_to_layout.cend(), + [=](const auto& element) { return element.first == language_code; }); + + KeyboardLayout selected_keyboard_layout = KeyboardLayout::EnglishUs; + if (key_code != language_to_layout.end()) { + selected_keyboard_layout = key_code->second; + } + + LOG_INFO(Service_SET, "called, selected_keyboard_layout={}", selected_keyboard_layout); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast(selected_keyboard_layout)); +} + +void ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) { + LOG_WARNING(Service_SET, "called."); + + Service::PSC::Time::SteadyClockTimePoint time_point{}; + auto res = GetDeviceTimeZoneLocationUpdatedTime(time_point); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.PushRaw(time_point); +} + +void ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) { + LOG_WARNING(Service_SET, "called."); + + IPC::RequestParser rp{ctx}; + auto time_point{rp.PopRaw()}; + + auto res = SetDeviceTimeZoneLocationUpdatedTime(time_point); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime( + HLERequestContext& ctx) { + LOG_WARNING(Service_SET, "called."); + + Service::PSC::Time::SteadyClockTimePoint time_point{}; + auto res = GetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.PushRaw(time_point); +} + +void ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime( + HLERequestContext& ctx) { + LOG_WARNING(Service_SET, "called."); + + IPC::RequestParser rp{ctx}; + auto time_point{rp.PopRaw()}; + + auto res = SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ISystemSettingsServer::GetChineseTraditionalInputMethod(HLERequestContext& ctx) { + LOG_WARNING(Service_SET, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(ChineseTraditionalInputMethod::Unknown0); +} + +void ISystemSettingsServer::GetHomeMenuScheme(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "(STUBBED) called"); + + const HomeMenuScheme default_color = { + .main = 0xFF323232, + .back = 0xFF323232, + .sub = 0xFFFFFFFF, + .bezel = 0xFFFFFFFF, + .extra = 0xFF000000, + }; + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(HomeMenuScheme) / sizeof(u32)}; + rb.Push(ResultSuccess); + rb.PushRaw(default_color); +} + +void ISystemSettingsServer::GetHomeMenuSchemeModel(HLERequestContext& ctx) { + LOG_WARNING(Service_SET, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(0); +} + +void ISystemSettingsServer::GetFieldTestingFlag(HLERequestContext& ctx) { + LOG_WARNING(Service_SET, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(false); +} + +void ISystemSettingsServer::SetupSettings() { + auto system_dir = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000050"; + if (!LoadSettingsFile(system_dir, []() { return DefaultSystemSettings(); })) { + ASSERT(false); + } + + auto private_dir = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000052"; + if (!LoadSettingsFile(private_dir, []() { return DefaultPrivateSettings(); })) { + ASSERT(false); + } + + auto device_dir = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000053"; + if (!LoadSettingsFile(device_dir, []() { return DefaultDeviceSettings(); })) { + ASSERT(false); + } + + auto appln_dir = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000054"; + if (!LoadSettingsFile(appln_dir, []() { return DefaultApplnSettings(); })) { + ASSERT(false); + } +} + +void ISystemSettingsServer::StoreSettings() { + auto system_dir = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000050"; + if (!StoreSettingsFile(system_dir, m_system_settings)) { + LOG_ERROR(Service_SET, "Failed to store System settings"); + } + + auto private_dir = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000052"; + if (!StoreSettingsFile(private_dir, m_private_settings)) { + LOG_ERROR(Service_SET, "Failed to store Private settings"); + } + + auto device_dir = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000053"; + if (!StoreSettingsFile(device_dir, m_device_settings)) { + LOG_ERROR(Service_SET, "Failed to store Device settings"); + } + + auto appln_dir = + Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000054"; + if (!StoreSettingsFile(appln_dir, m_appln_settings)) { + LOG_ERROR(Service_SET, "Failed to store ApplLn settings"); + } +} + +void ISystemSettingsServer::StoreSettingsThreadFunc(std::stop_token stop_token) { + Common::SetCurrentThreadName("SettingsStore"); + + while (Common::StoppableTimedWait(stop_token, std::chrono::minutes(1))) { + std::scoped_lock l{m_save_needed_mutex}; + if (!std::exchange(m_save_needed, false)) { + continue; + } + StoreSettings(); + } +} + +void ISystemSettingsServer::SetSaveNeeded() { + std::scoped_lock l{m_save_needed_mutex}; + m_save_needed = true; +} + +Result ISystemSettingsServer::GetSettingsItemValue(std::vector& out_value, + const std::string& category, + const std::string& name) { + auto settings{GetSettings()}; + R_UNLESS(settings.contains(category) && settings[category].contains(name), ResultUnknown); + + out_value = settings[category][name]; + R_SUCCEED(); +} + +Result ISystemSettingsServer::GetExternalSteadyClockSourceId(Common::UUID& out_id) { + out_id = m_private_settings.external_clock_source_id; + R_SUCCEED(); +} + +Result ISystemSettingsServer::SetExternalSteadyClockSourceId(Common::UUID id) { + m_private_settings.external_clock_source_id = id; + SetSaveNeeded(); + R_SUCCEED(); +} + +Result ISystemSettingsServer::GetUserSystemClockContext( + Service::PSC::Time::SystemClockContext& out_context) { + out_context = m_system_settings.user_system_clock_context; + R_SUCCEED(); +} + +Result ISystemSettingsServer::SetUserSystemClockContext( + Service::PSC::Time::SystemClockContext& context) { + m_system_settings.user_system_clock_context = context; + SetSaveNeeded(); + R_SUCCEED(); +} + +Result ISystemSettingsServer::GetDeviceTimeZoneLocationName( + Service::PSC::Time::LocationName& out_name) { + out_name = m_system_settings.device_time_zone_location_name; + R_SUCCEED(); +} + +Result ISystemSettingsServer::SetDeviceTimeZoneLocationName( + Service::PSC::Time::LocationName& name) { + m_system_settings.device_time_zone_location_name = name; + SetSaveNeeded(); + R_SUCCEED(); +} + +Result ISystemSettingsServer::GetNetworkSystemClockContext( + Service::PSC::Time::SystemClockContext& out_context) { + out_context = m_system_settings.network_system_clock_context; + R_SUCCEED(); +} + +Result ISystemSettingsServer::SetNetworkSystemClockContext( + Service::PSC::Time::SystemClockContext& context) { + m_system_settings.network_system_clock_context = context; + SetSaveNeeded(); + R_SUCCEED(); +} + +Result ISystemSettingsServer::IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled) { + out_enabled = m_system_settings.user_system_clock_automatic_correction_enabled; + R_SUCCEED(); +} + +Result ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionEnabled(bool enabled) { + m_system_settings.user_system_clock_automatic_correction_enabled = enabled; + SetSaveNeeded(); + R_SUCCEED(); +} + +Result ISystemSettingsServer::SetExternalSteadyClockInternalOffset(s64 offset) { + m_private_settings.external_steady_clock_internal_offset = offset; + SetSaveNeeded(); + R_SUCCEED(); +} + +Result ISystemSettingsServer::GetExternalSteadyClockInternalOffset(s64& out_offset) { + out_offset = m_private_settings.external_steady_clock_internal_offset; + R_SUCCEED(); +} + +Result ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime( + Service::PSC::Time::SteadyClockTimePoint& out_time_point) { + out_time_point = m_system_settings.device_time_zone_location_updated_time; + R_SUCCEED(); +} + +Result ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime( + Service::PSC::Time::SteadyClockTimePoint& time_point) { + m_system_settings.device_time_zone_location_updated_time = time_point; + SetSaveNeeded(); + R_SUCCEED(); +} + +Result ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime( + Service::PSC::Time::SteadyClockTimePoint& out_time_point) { + out_time_point = m_system_settings.user_system_clock_automatic_correction_updated_time_point; + R_SUCCEED(); +} + +Result ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime( + Service::PSC::Time::SteadyClockTimePoint out_time_point) { + m_system_settings.user_system_clock_automatic_correction_updated_time_point = out_time_point; + SetSaveNeeded(); + R_SUCCEED(); +} + +} // namespace Service::Set diff --git a/src/core/hle/service/set/system_settings_server.h b/src/core/hle/service/set/system_settings_server.h new file mode 100755 index 000000000..833e6461d --- /dev/null +++ b/src/core/hle/service/set/system_settings_server.h @@ -0,0 +1,152 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include + +#include "common/polyfill_thread.h" +#include "common/uuid.h" +#include "core/hle/result.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/service.h" +#include "core/hle/service/set/appln_settings.h" +#include "core/hle/service/set/device_settings.h" +#include "core/hle/service/set/private_settings.h" +#include "core/hle/service/set/system_settings.h" + +namespace Core { +class System; +} + +namespace Service::Set { +enum class GetFirmwareVersionType { + Version1, + Version2, +}; + +struct FirmwareVersionFormat { + u8 major; + u8 minor; + u8 micro; + INSERT_PADDING_BYTES(1); + u8 revision_major; + u8 revision_minor; + INSERT_PADDING_BYTES(2); + std::array platform; + std::array version_hash; + std::array display_version; + std::array display_title; +}; +static_assert(sizeof(FirmwareVersionFormat) == 0x100, "FirmwareVersionFormat is an invalid size"); + +Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system, + GetFirmwareVersionType type); + +class ISystemSettingsServer final : public ServiceFramework { +public: + explicit ISystemSettingsServer(Core::System& system_); + ~ISystemSettingsServer() override; + + Result GetSettingsItemValue(std::vector& out_value, const std::string& category, + const std::string& name); + + Result GetExternalSteadyClockSourceId(Common::UUID& out_id); + Result SetExternalSteadyClockSourceId(Common::UUID id); + Result GetUserSystemClockContext(Service::PSC::Time::SystemClockContext& out_context); + Result SetUserSystemClockContext(Service::PSC::Time::SystemClockContext& context); + Result GetDeviceTimeZoneLocationName(Service::PSC::Time::LocationName& out_name); + Result SetDeviceTimeZoneLocationName(Service::PSC::Time::LocationName& name); + Result GetNetworkSystemClockContext(Service::PSC::Time::SystemClockContext& out_context); + Result SetNetworkSystemClockContext(Service::PSC::Time::SystemClockContext& context); + Result IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled); + Result SetUserSystemClockAutomaticCorrectionEnabled(bool enabled); + Result SetExternalSteadyClockInternalOffset(s64 offset); + Result GetExternalSteadyClockInternalOffset(s64& out_offset); + Result GetDeviceTimeZoneLocationUpdatedTime( + Service::PSC::Time::SteadyClockTimePoint& out_time_point); + Result SetDeviceTimeZoneLocationUpdatedTime( + Service::PSC::Time::SteadyClockTimePoint& time_point); + Result GetUserSystemClockAutomaticCorrectionUpdatedTime( + Service::PSC::Time::SteadyClockTimePoint& out_time_point); + Result SetUserSystemClockAutomaticCorrectionUpdatedTime( + Service::PSC::Time::SteadyClockTimePoint time_point); + +private: + void SetLanguageCode(HLERequestContext& ctx); + void GetFirmwareVersion(HLERequestContext& ctx); + void GetFirmwareVersion2(HLERequestContext& ctx); + void GetExternalSteadyClockSourceId(HLERequestContext& ctx); + void SetExternalSteadyClockSourceId(HLERequestContext& ctx); + void GetUserSystemClockContext(HLERequestContext& ctx); + void SetUserSystemClockContext(HLERequestContext& ctx); + void GetAccountSettings(HLERequestContext& ctx); + void SetAccountSettings(HLERequestContext& ctx); + void GetEulaVersions(HLERequestContext& ctx); + void SetEulaVersions(HLERequestContext& ctx); + void GetColorSetId(HLERequestContext& ctx); + void SetColorSetId(HLERequestContext& ctx); + void GetNotificationSettings(HLERequestContext& ctx); + void SetNotificationSettings(HLERequestContext& ctx); + void GetAccountNotificationSettings(HLERequestContext& ctx); + void SetAccountNotificationSettings(HLERequestContext& ctx); + void GetSettingsItemValueSize(HLERequestContext& ctx); + void GetSettingsItemValue(HLERequestContext& ctx); + void GetTvSettings(HLERequestContext& ctx); + void SetTvSettings(HLERequestContext& ctx); + void GetDebugModeFlag(HLERequestContext& ctx); + void GetQuestFlag(HLERequestContext& ctx); + void GetDeviceTimeZoneLocationName(HLERequestContext& ctx); + void SetDeviceTimeZoneLocationName(HLERequestContext& ctx); + void SetRegionCode(HLERequestContext& ctx); + void GetNetworkSystemClockContext(HLERequestContext& ctx); + void SetNetworkSystemClockContext(HLERequestContext& ctx); + void IsUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx); + void SetUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx); + void GetPrimaryAlbumStorage(HLERequestContext& ctx); + void GetSleepSettings(HLERequestContext& ctx); + void SetSleepSettings(HLERequestContext& ctx); + void GetInitialLaunchSettings(HLERequestContext& ctx); + void SetInitialLaunchSettings(HLERequestContext& ctx); + void GetDeviceNickName(HLERequestContext& ctx); + void SetDeviceNickName(HLERequestContext& ctx); + void GetProductModel(HLERequestContext& ctx); + void GetMiiAuthorId(HLERequestContext& ctx); + void GetAutoUpdateEnableFlag(HLERequestContext& ctx); + void GetBatteryPercentageFlag(HLERequestContext& ctx); + void SetExternalSteadyClockInternalOffset(HLERequestContext& ctx); + void GetExternalSteadyClockInternalOffset(HLERequestContext& ctx); + void GetErrorReportSharePermission(HLERequestContext& ctx); + void GetAppletLaunchFlags(HLERequestContext& ctx); + void SetAppletLaunchFlags(HLERequestContext& ctx); + void GetKeyboardLayout(HLERequestContext& ctx); + void GetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx); + void SetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx); + void GetUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx); + void SetUserSystemClockAutomaticCorrectionUpdatedTime(HLERequestContext& ctx); + void GetChineseTraditionalInputMethod(HLERequestContext& ctx); + void GetHomeMenuScheme(HLERequestContext& ctx); + void GetHomeMenuSchemeModel(HLERequestContext& ctx); + void GetFieldTestingFlag(HLERequestContext& ctx); + + bool LoadSettingsFile(std::filesystem::path& path, auto&& default_func); + bool StoreSettingsFile(std::filesystem::path& path, auto& settings); + void SetupSettings(); + void StoreSettings(); + void StoreSettingsThreadFunc(std::stop_token stop_token); + void SetSaveNeeded(); + + Core::System& m_system; + SystemSettings m_system_settings{}; + PrivateSettings m_private_settings{}; + DeviceSettings m_device_settings{}; + ApplnSettings m_appln_settings{}; + std::mutex m_save_needed_mutex; + std::jthread m_save_thread; + bool m_save_needed{false}; +}; + +} // namespace Service::Set diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index d17813405..2401ce49c 100755 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include #include @@ -10,6 +11,7 @@ #include "common/concepts.h" #include "core/hle/kernel/k_port.h" +#include "core/hle/kernel/svc.h" #include "core/hle/result.h" #include "core/hle/service/service.h" @@ -62,12 +64,21 @@ public: Result GetServicePort(Kernel::KClientPort** out_client_port, const std::string& name); template T> - std::shared_ptr GetService(const std::string& service_name) const { + std::shared_ptr GetService(const std::string& service_name, bool block = false) const { auto service = registered_services.find(service_name); - if (service == registered_services.end()) { + if (service == registered_services.end() && !block) { LOG_DEBUG(Service, "Can't find service: {}", service_name); return nullptr; + } else if (block) { + using namespace std::literals::chrono_literals; + while (service == registered_services.end()) { + Kernel::Svc::SleepThread( + kernel.System(), + std::chrono::duration_cast(100ms).count()); + service = registered_services.find(service_name); + } } + return std::static_pointer_cast(service->second()); } diff --git a/src/frontend_common/config.cpp b/src/frontend_common/config.cpp index 9eb4799a6..46277e288 100755 --- a/src/frontend_common/config.cpp +++ b/src/frontend_common/config.cpp @@ -5,6 +5,7 @@ #include #include "common/fs/fs.h" #include "common/fs/path_util.h" +#include "common/logging/log.h" #include "common/settings.h" #include "common/settings_common.h" #include "common/settings_enums.h" @@ -58,6 +59,19 @@ void Config::Initialize(const std::optional config_path) { } void Config::WriteToIni() const { + std::string config_type; + switch (type) { + case ConfigType::GlobalConfig: + config_type = "Global"; + break; + case ConfigType::PerGameConfig: + config_type = "Game Specific"; + break; + case ConfigType::InputProfile: + config_type = "Input Profile"; + break; + } + LOG_INFO(Config, "Writing {} configuration to: {}", config_type, config_loc); FILE* fp = nullptr; #ifdef _WIN32 fp = _wfopen(Common::UTF8ToUTF16W(config_loc).data(), L"wb"); @@ -117,10 +131,10 @@ void Config::ReadPlayerValues(const std::size_t player_index) { player_prefix.append("player_").append(ToString(player_index)).append("_"); } + const auto profile_name = ReadStringSetting(std::string(player_prefix).append("profile_name")); + auto& player = Settings::values.players.GetValue()[player_index]; if (IsCustomConfig()) { - const auto profile_name = - ReadStringSetting(std::string(player_prefix).append("profile_name")); if (profile_name.empty()) { // Use the global input config player = Settings::values.players.GetValue(true)[player_index]; @@ -139,6 +153,10 @@ void Config::ReadPlayerValues(const std::size_t player_index) { player.controller_type = controller; } } else { + if (global) { + auto& player_global = Settings::values.players.GetValue(true)[player_index]; + player_global.profile_name = profile_name; + } std::string connected_key = player_prefix; player.connected = ReadBooleanSetting(connected_key.append("connected"), std::make_optional(player_index == 0)); @@ -412,6 +430,11 @@ void Config::SavePlayerValues(const std::size_t player_index) { std::make_optional(static_cast(Settings::ControllerType::ProController))); if (!player_prefix.empty() || !Settings::IsConfiguringGlobal()) { + if (global) { + const auto& player_global = Settings::values.players.GetValue(true)[player_index]; + WriteStringSetting(std::string(player_prefix).append("profile_name"), + player_global.profile_name, std::make_optional(std::string(""))); + } WriteBooleanSetting(std::string(player_prefix).append("connected"), player.connected, std::make_optional(player_index == 0)); WriteIntegerSetting(std::string(player_prefix).append("vibration_enabled"), @@ -468,12 +491,15 @@ void Config::SaveMotionTouchValues() { void Config::SaveValues() { if (global) { + LOG_DEBUG(Config, "Saving global generic configuration values"); SaveDataStorageValues(); SaveDebuggingValues(); SaveDisabledAddOnValues(); SaveNetworkValues(); SaveWebServiceValues(); SaveMiscellaneousValues(); + } else { + LOG_DEBUG(Config, "Saving only generic configuration values"); } SaveControlValues(); SaveCoreValues(); @@ -814,10 +840,6 @@ void Config::Reload() { SaveValues(); } -void Config::Save() { - SaveValues(); -} - void Config::ClearControlPlayerValues() const { // If key is an empty string, all keys in the current group() are removed. const char* section = Settings::TranslateCategory(Settings::Category::Controls); diff --git a/src/frontend_common/config.h b/src/frontend_common/config.h index b01631649..4798d6432 100755 --- a/src/frontend_common/config.h +++ b/src/frontend_common/config.h @@ -51,7 +51,6 @@ protected: [[nodiscard]] bool IsCustomConfig() const; void Reload(); - void Save(); /** * Derived config classes must implement this so they can reload all platform-specific diff --git a/src/hid_core/resources/applet_resource.cpp b/src/hid_core/resources/applet_resource.cpp index d09a525c6..a84826050 100755 --- a/src/hid_core/resources/applet_resource.cpp +++ b/src/hid_core/resources/applet_resource.cpp @@ -130,6 +130,7 @@ void AppletResource::FreeAppletResourceId(u64 aruid) { if (aruid_data.flag.is_assigned) { aruid_data.shared_memory_format = nullptr; aruid_data.flag.is_assigned.Assign(false); + shared_memory_holder[index].Finalize(); } } diff --git a/src/hid_core/resources/npad/npad.cpp b/src/hid_core/resources/npad/npad.cpp index 97f31d26e..1f8a0f8ab 100755 --- a/src/hid_core/resources/npad/npad.cpp +++ b/src/hid_core/resources/npad/npad.cpp @@ -1344,4 +1344,49 @@ AppletDetailedUiType NPad::GetAppletDetailedUiType(Core::HID::NpadIdType npad_id }; } +Result NPad::SetNpadCaptureButtonAssignment(u64 aruid, Core::HID::NpadStyleSet npad_style_set, + Core::HID::NpadButton button_assignment) { + std::scoped_lock lock{mutex}; + return npad_resource.SetNpadCaptureButtonAssignment(aruid, npad_style_set, button_assignment); +} + +Result NPad::ClearNpadCaptureButtonAssignment(u64 aruid) { + std::scoped_lock lock{mutex}; + return npad_resource.ClearNpadCaptureButtonAssignment(aruid); +} + +std::size_t NPad::GetNpadCaptureButtonAssignment(std::span out_list, + u64 aruid) const { + std::scoped_lock lock{mutex}; + return npad_resource.GetNpadCaptureButtonAssignment(out_list, aruid); +} + +Result NPad::SetNpadSystemExtStateEnabled(u64 aruid, bool is_enabled) { + std::scoped_lock lock{mutex}; + const auto result = npad_resource.SetNpadSystemExtStateEnabled(aruid, is_enabled); + + if (result.IsSuccess()) { + std::scoped_lock shared_lock{*applet_resource_holder.shared_mutex}; + // TODO: abstracted_pad->EnableAppletToGetInput(aruid); + } + + return result; +} + +Result NPad::AssigningSingleOnSlSrPress(u64 aruid, bool is_enabled) { + std::scoped_lock lock{mutex}; + bool is_currently_enabled{}; + Result result = npad_resource.IsAssigningSingleOnSlSrPressEnabled(is_currently_enabled, aruid); + if (result.IsSuccess() && is_enabled != is_currently_enabled) { + result = npad_resource.SetAssigningSingleOnSlSrPress(aruid, is_enabled); + } + return result; +} + +Result NPad::GetLastActiveNpad(Core::HID::NpadIdType& out_npad_id) const { + std::scoped_lock lock{mutex}; + out_npad_id = hid_core.GetLastActiveController(); + return ResultSuccess; +} + } // namespace Service::HID diff --git a/src/hid_core/resources/npad/npad.h b/src/hid_core/resources/npad/npad.h index 58f8c7acf..01f3dabb1 100755 --- a/src/hid_core/resources/npad/npad.h +++ b/src/hid_core/resources/npad/npad.h @@ -149,6 +149,18 @@ public: AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id); + Result SetNpadCaptureButtonAssignment(u64 aruid, Core::HID::NpadStyleSet npad_style_set, + Core::HID::NpadButton button_assignment); + Result ClearNpadCaptureButtonAssignment(u64 aruid); + std::size_t GetNpadCaptureButtonAssignment(std::span out_list, + u64 aruid) const; + + Result SetNpadSystemExtStateEnabled(u64 aruid, bool is_enabled); + + Result AssigningSingleOnSlSrPress(u64 aruid, bool is_enabled); + + Result GetLastActiveNpad(Core::HID::NpadIdType& out_npad_id) const; + private: struct VibrationData { bool device_mounted{}; diff --git a/src/shader_recompiler/environment.h b/src/shader_recompiler/environment.h index da0ffa4da..e7ad6c064 100755 --- a/src/shader_recompiler/environment.h +++ b/src/shader_recompiler/environment.h @@ -59,8 +59,8 @@ public: return start_address; } - [[nodiscard]] bool IsPropietaryDriver() const noexcept { - return is_propietary_driver; + [[nodiscard]] bool IsProprietaryDriver() const noexcept { + return is_proprietary_driver; } protected: @@ -68,7 +68,7 @@ protected: std::array gp_passthrough_mask{}; Stage stage{}; u32 start_address{}; - bool is_propietary_driver{}; + bool is_proprietary_driver{}; }; } // namespace Shader diff --git a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp index 6cfa518bd..7c67c70b6 100755 --- a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp +++ b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp @@ -1084,7 +1084,7 @@ void ConstantPropagation(Environment& env, IR::Block& block, IR::Inst& inst) { if (env.HasHLEMacroState()) { FoldConstBuffer(env, block, inst); } - if (env.IsPropietaryDriver()) { + if (env.IsProprietaryDriver()) { FoldDriverConstBuffer(env, block, inst, 1); } break; diff --git a/src/tests/video_core/memory_tracker.cpp b/src/tests/video_core/memory_tracker.cpp index 0e559a590..45b1a91dc 100755 --- a/src/tests/video_core/memory_tracker.cpp +++ b/src/tests/video_core/memory_tracker.cpp @@ -545,4 +545,4 @@ TEST_CASE("MemoryTracker: Cached write downloads") { REQUIRE(!memory_track->IsRegionGpuModified(c + PAGE, PAGE)); memory_track->MarkRegionAsCpuModified(c, WORD); REQUIRE(rasterizer.Count() == 0); -} \ No newline at end of file +} diff --git a/src/video_core/buffer_cache/memory_tracker_base.h b/src/video_core/buffer_cache/memory_tracker_base.h index 6ca5edf9f..c95eed1f6 100755 --- a/src/video_core/buffer_cache/memory_tracker_base.h +++ b/src/video_core/buffer_cache/memory_tracker_base.h @@ -267,10 +267,10 @@ private: top_tier[page_index] = GetNewManager(base_cpu_addr); } - Manager* GetNewManager(VAddr base_cpu_addess) { + Manager* GetNewManager(VAddr base_cpu_address) { const auto on_return = [&] { auto* new_manager = free_managers.front(); - new_manager->SetCpuAddress(base_cpu_addess); + new_manager->SetCpuAddress(base_cpu_address); free_managers.pop_front(); return new_manager; }; diff --git a/src/video_core/control/channel_state_cache.h b/src/video_core/control/channel_state_cache.h index d0aeb81de..efc992d83 100755 --- a/src/video_core/control/channel_state_cache.h +++ b/src/video_core/control/channel_state_cache.h @@ -85,12 +85,12 @@ protected: std::deque free_channel_ids; std::unordered_map channel_map; std::vector active_channel_ids; - struct AddresSpaceRef { + struct AddressSpaceRef { size_t ref_count; size_t storage_id; Tegra::MemoryManager* gpu_memory; }; - std::unordered_map address_spaces; + std::unordered_map address_spaces; mutable std::mutex config_mutex; virtual void OnGPUASRegister([[maybe_unused]] size_t map_id) {} diff --git a/src/video_core/control/channel_state_cache.inc b/src/video_core/control/channel_state_cache.inc index 11f3211b8..5a6ab0052 100755 --- a/src/video_core/control/channel_state_cache.inc +++ b/src/video_core/control/channel_state_cache.inc @@ -38,7 +38,7 @@ void ChannelSetupCaches

::CreateChannel(struct Tegra::Control::ChannelState& c as_it->second.ref_count++; return; } - AddresSpaceRef new_gpu_mem_ref{ + AddressSpaceRef new_gpu_mem_ref{ .ref_count = 1, .storage_id = address_spaces.size(), .gpu_memory = channel.memory_manager.get(), diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index ffaab882f..33fc74fa4 100755 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -958,7 +958,7 @@ public: enum class ClearReport : u32 { ZPassPixelCount = 0x01, ZCullStats = 0x02, - StreamingPrimitvesNeededMinusSucceeded = 0x03, + StreamingPrimitivesNeededMinusSucceeded = 0x03, AlphaBetaClocks = 0x04, StreamingPrimitivesSucceeded = 0x10, StreamingPrimitivesNeeded = 0x11, @@ -2383,8 +2383,8 @@ public: }; enum class Release : u32 { - AfterAllPreceedingReads = 0, - AfterAllPreceedingWrites = 1, + AfterAllPrecedingReads = 0, + AfterAllPrecedingWrites = 1, }; enum class Acquire : u32 { @@ -2869,7 +2869,7 @@ public: u32 global_base_instance_index; ///< 0x1438 INSERT_PADDING_BYTES_NOINIT(0x14); RegisterWatermarks ps_warp_watermarks; ///< 0x1450 - RegisterWatermarks ps_regster_watermarks; ///< 0x1454 + RegisterWatermarks ps_register_watermarks; ///< 0x1454 INSERT_PADDING_BYTES_NOINIT(0xC); u32 store_zcull; ///< 0x1464 INSERT_PADDING_BYTES_NOINIT(0x18); @@ -3444,7 +3444,7 @@ ASSERT_REG_POSITION(invalidate_texture_header_cache_no_wfi, 0x1428); ASSERT_REG_POSITION(global_base_vertex_index, 0x1434); ASSERT_REG_POSITION(global_base_instance_index, 0x1438); ASSERT_REG_POSITION(ps_warp_watermarks, 0x1450); -ASSERT_REG_POSITION(ps_regster_watermarks, 0x1454); +ASSERT_REG_POSITION(ps_register_watermarks, 0x1454); ASSERT_REG_POSITION(store_zcull, 0x1464); ASSERT_REG_POSITION(iterated_blend_constants, 0x1480); ASSERT_REG_POSITION(load_zcull, 0x1500); diff --git a/src/video_core/engines/sw_blitter/blitter.cpp b/src/video_core/engines/sw_blitter/blitter.cpp index 544c25043..4bc079024 100755 --- a/src/video_core/engines/sw_blitter/blitter.cpp +++ b/src/video_core/engines/sw_blitter/blitter.cpp @@ -172,12 +172,12 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst, const bool no_passthrough = src.format != dst.format || src_extent_x != dst_extent_x || src_extent_y != dst_extent_y; - const auto convertion_phase_same_format = [&]() { + const auto conversion_phase_same_format = [&]() { NearestNeighbor(impl->src_buffer, impl->dst_buffer, src_extent_x, src_extent_y, dst_extent_x, dst_extent_y, dst_bytes_per_pixel); }; - const auto convertion_phase_ir = [&]() { + const auto conversion_phase_ir = [&]() { auto* input_converter = impl->converter_factory.GetFormatConverter(src.format); impl->intermediate_src.resize_destructive((src_copy_size / src_bytes_per_pixel) * ir_components); @@ -212,9 +212,9 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst, // Conversion Phase if (no_passthrough) { if (src.format != dst.format || config.filter == Fermi2D::Filter::Bilinear) { - convertion_phase_ir(); + conversion_phase_ir(); } else { - convertion_phase_same_format(); + conversion_phase_same_format(); } } else { impl->dst_buffer.swap(impl->src_buffer); diff --git a/src/video_core/host1x/codecs/vp8.h b/src/video_core/host1x/codecs/vp8.h index 7aa8c29f9..36a7e08e0 100755 --- a/src/video_core/host1x/codecs/vp8.h +++ b/src/video_core/host1x/codecs/vp8.h @@ -42,7 +42,7 @@ private: u8 raw; BitField<0, 2, u8> tile_format; BitField<2, 3, u8> gob_height; - BitField<5, 3, u8> reserverd_surface_format; + BitField<5, 3, u8> reserved_surface_format; }; u8 error_conceal_on; // 1: error conceal on; 0: off u32 first_part_size; // the size of first partition(frame header and mb header partition) diff --git a/src/video_core/host_shaders/astc_decoder.comp b/src/video_core/host_shaders/astc_decoder.comp index 3ab94d435..78b400d63 100755 --- a/src/video_core/host_shaders/astc_decoder.comp +++ b/src/video_core/host_shaders/astc_decoder.comp @@ -803,7 +803,7 @@ void UnquantizeTexelWeights(uvec2 size, bool is_dual_plane) { } } -uint GetUnquantizedTexelWieght(uint offset_base, uint plane, bool is_dual_plane) { +uint GetUnquantizedTexelWeight(uint offset_base, uint plane, bool is_dual_plane) { const uint offset = is_dual_plane ? 2 * offset_base + plane : offset_base; return result_vector[offset]; } @@ -833,23 +833,23 @@ uvec4 GetUnquantizedWeightVector(uint t, uint s, uvec2 size, uint plane_index, b if (v0 < area) { const uint offset_base = v0; - p0.x = GetUnquantizedTexelWieght(offset_base, 0, is_dual_plane); - p1.x = GetUnquantizedTexelWieght(offset_base, 1, is_dual_plane); + p0.x = GetUnquantizedTexelWeight(offset_base, 0, is_dual_plane); + p1.x = GetUnquantizedTexelWeight(offset_base, 1, is_dual_plane); } if ((v0 + 1) < (area)) { const uint offset_base = v0 + 1; - p0.y = GetUnquantizedTexelWieght(offset_base, 0, is_dual_plane); - p1.y = GetUnquantizedTexelWieght(offset_base, 1, is_dual_plane); + p0.y = GetUnquantizedTexelWeight(offset_base, 0, is_dual_plane); + p1.y = GetUnquantizedTexelWeight(offset_base, 1, is_dual_plane); } if ((v0 + size.x) < (area)) { const uint offset_base = v0 + size.x; - p0.z = GetUnquantizedTexelWieght(offset_base, 0, is_dual_plane); - p1.z = GetUnquantizedTexelWieght(offset_base, 1, is_dual_plane); + p0.z = GetUnquantizedTexelWeight(offset_base, 0, is_dual_plane); + p1.z = GetUnquantizedTexelWeight(offset_base, 1, is_dual_plane); } if ((v0 + size.x + 1) < (area)) { const uint offset_base = v0 + size.x + 1; - p0.w = GetUnquantizedTexelWieght(offset_base, 0, is_dual_plane); - p1.w = GetUnquantizedTexelWieght(offset_base, 1, is_dual_plane); + p0.w = GetUnquantizedTexelWeight(offset_base, 0, is_dual_plane); + p1.w = GetUnquantizedTexelWeight(offset_base, 1, is_dual_plane); } const uint primary_weight = (uint(dot(p0, w)) + 8) >> 4; diff --git a/src/video_core/query_cache/query_base.h b/src/video_core/query_cache/query_base.h index aca6a6447..d5d21beaa 100755 --- a/src/video_core/query_cache/query_base.h +++ b/src/video_core/query_cache/query_base.h @@ -67,4 +67,4 @@ public: size_t size_slots{}; }; -} // namespace VideoCommon \ No newline at end of file +} // namespace VideoCommon diff --git a/src/video_core/query_cache/query_cache.h b/src/video_core/query_cache/query_cache.h index 3d4cda91a..08b779055 100755 --- a/src/video_core/query_cache/query_cache.h +++ b/src/video_core/query_cache/query_cache.h @@ -270,7 +270,7 @@ void QueryCacheBase::CounterReport(GPUVAddr addr, QueryType counter_type ASSERT(false); return; } - query_base->value += streamer->GetAmmendValue(); + query_base->value += streamer->GetAmendValue(); streamer->SetAccumulationValue(query_base->value); if (True(query_base->flags & QueryFlagBits::HasTimestamp)) { u64 timestamp = impl->gpu.GetTicks(); diff --git a/src/video_core/query_cache/query_cache_base.h b/src/video_core/query_cache/query_cache_base.h index c12fb75ef..00c25c8d6 100755 --- a/src/video_core/query_cache/query_cache_base.h +++ b/src/video_core/query_cache/query_cache_base.h @@ -175,4 +175,4 @@ protected: std::unique_ptr impl; }; -} // namespace VideoCommon \ No newline at end of file +} // namespace VideoCommon diff --git a/src/video_core/query_cache/query_stream.h b/src/video_core/query_cache/query_stream.h index 39da6ac07..1d11b1275 100755 --- a/src/video_core/query_cache/query_stream.h +++ b/src/video_core/query_cache/query_stream.h @@ -78,12 +78,12 @@ public: return dependence_mask; } - u64 GetAmmendValue() const { - return ammend_value; + u64 GetAmendValue() const { + return amend_value; } void SetAccumulationValue(u64 new_value) { - acumulation_value = new_value; + accumulation_value = new_value; } protected: @@ -95,8 +95,8 @@ protected: const size_t id; u64 dependence_mask; u64 dependent_mask; - u64 ammend_value{}; - u64 acumulation_value{}; + u64 amend_value{}; + u64 accumulation_value{}; }; template @@ -146,4 +146,4 @@ protected: std::deque old_queries; }; -} // namespace VideoCommon \ No newline at end of file +} // namespace VideoCommon diff --git a/src/video_core/query_cache/types.h b/src/video_core/query_cache/types.h index e9226bbfc..0c6a882e2 100755 --- a/src/video_core/query_cache/types.h +++ b/src/video_core/query_cache/types.h @@ -71,4 +71,4 @@ enum class ReductionOp : u32 { MaxReductionOp, }; -} // namespace VideoCommon \ No newline at end of file +} // namespace VideoCommon diff --git a/src/video_core/rasterizer_download_area.h b/src/video_core/rasterizer_download_area.h index 2d7425c79..d28826043 100755 --- a/src/video_core/rasterizer_download_area.h +++ b/src/video_core/rasterizer_download_area.h @@ -13,4 +13,4 @@ struct RasterizerDownloadArea { bool preemtive; }; -} // namespace VideoCore \ No newline at end of file +} // namespace VideoCore diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index 0e213895d..5b0ffe5f5 100755 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -231,10 +231,10 @@ void FixedPipelineState::DynamicState::Refresh(const Maxwell& regs) { void FixedPipelineState::DynamicState::Refresh2(const Maxwell& regs, Maxwell::PrimitiveTopology topology_, - bool base_feautures_supported) { + bool base_features_supported) { logic_op.Assign(PackLogicOp(regs.logic_op.op)); - if (base_feautures_supported) { + if (base_features_supported) { return; } diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index 417d0e994..c22ee473f 100755 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h @@ -165,7 +165,7 @@ struct FixedPipelineState { void Refresh(const Maxwell& regs); void Refresh2(const Maxwell& regs, Maxwell::PrimitiveTopology topology, - bool base_feautures_supported); + bool base_features_supported); void Refresh3(const Maxwell& regs); Maxwell::ComparisonOp DepthTestFunc() const noexcept { diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h index 535750eba..b53c36630 100755 --- a/src/video_core/renderer_vulkan/vk_blit_screen.h +++ b/src/video_core/renderer_vulkan/vk_blit_screen.h @@ -128,7 +128,7 @@ private: vk::DescriptorPool descriptor_pool; vk::DescriptorSetLayout descriptor_set_layout; vk::PipelineLayout pipeline_layout; - vk::Pipeline nearest_neightbor_pipeline; + vk::Pipeline nearest_neighbor_pipeline; vk::Pipeline bilinear_pipeline; vk::Pipeline bicubic_pipeline; vk::Pipeline gaussian_pipeline; diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.h b/src/video_core/renderer_vulkan/vk_descriptor_pool.h index c8a0e0171..6c6b5e0cb 100755 --- a/src/video_core/renderer_vulkan/vk_descriptor_pool.h +++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.h @@ -84,4 +84,4 @@ private: std::vector> banks; }; -} // namespace Vulkan \ No newline at end of file +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index 96de3b001..025d250ba 100755 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp @@ -121,8 +121,8 @@ public: scheduler{scheduler_}, memory_allocator{memory_allocator_} { current_bank = nullptr; current_query = nullptr; - ammend_value = 0; - acumulation_value = 0; + amend_value = 0; + accumulation_value = 0; queries_prefix_scan_pass = std::make_unique( device, scheduler, descriptor_pool, compute_pass_descriptor_queue); @@ -177,8 +177,8 @@ public: } AbandonCurrentQuery(); std::function func([this, counts = pending_flush_queries.size()] { - ammend_value = 0; - acumulation_value = 0; + amend_value = 0; + accumulation_value = 0; }); rasterizer->SyncOperation(std::move(func)); accumulation_since_last_sync = false; @@ -308,7 +308,7 @@ public: } ReplicateCurrentQueryIfNeeded(); - std::function func([this] { ammend_value = acumulation_value; }); + std::function func([this] { amend_value = accumulation_value; }); rasterizer->SyncOperation(std::move(func)); AbandonCurrentQuery(); num_slots_used = 0; @@ -513,7 +513,7 @@ private: pending_flush_queries.push_back(index); std::function func([this, index] { auto* query = GetQuery(index); - query->value += GetAmmendValue(); + query->value += GetAmendValue(); SetAccumulationValue(query->value); Free(index); }); @@ -1173,7 +1173,7 @@ struct QueryCacheRuntimeImpl { primitives_succeeded_streamer( static_cast(QueryType::StreamingPrimitivesSucceeded), runtime, tfb_streamer, device_memory_), - primitives_needed_minus_suceeded_streamer( + primitives_needed_minus_succeeded_streamer( static_cast(QueryType::StreamingPrimitivesNeededMinusSucceeded), runtime, 0u), hcr_setup{}, hcr_is_set{}, is_hcr_running{}, maxwell3d{} { @@ -1212,7 +1212,7 @@ struct QueryCacheRuntimeImpl { SamplesStreamer sample_streamer; TFBCounterStreamer tfb_streamer; PrimitivesSucceededStreamer primitives_succeeded_streamer; - VideoCommon::StubStreamer primitives_needed_minus_suceeded_streamer; + VideoCommon::StubStreamer primitives_needed_minus_succeeded_streamer; std::vector> little_cache; std::vector> buffers_to_upload_to; @@ -1437,7 +1437,7 @@ VideoCommon::StreamerInterface* QueryCacheRuntime::GetStreamerInterface(QueryTyp case QueryType::StreamingPrimitivesSucceeded: return &impl->primitives_succeeded_streamer; case QueryType::StreamingPrimitivesNeededMinusSucceeded: - return &impl->primitives_needed_minus_suceeded_streamer; + return &impl->primitives_needed_minus_succeeded_streamer; default: return nullptr; } diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp index bdd02e16c..f47f2e244 100755 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp @@ -236,14 +236,14 @@ void StagingBufferPool::ReleaseLevel(StagingBuffersCache& cache, size_t log2) { auto& entries = staging.entries; const size_t old_size = entries.size(); - const auto is_deleteable = [this](const StagingBuffer& entry) { + const auto is_deletable = [this](const StagingBuffer& entry) { return scheduler.IsFree(entry.tick); }; const size_t begin_offset = staging.delete_index; const size_t end_offset = std::min(begin_offset + deletions_per_tick, old_size); const auto begin = entries.begin() + begin_offset; const auto end = entries.begin() + end_offset; - entries.erase(std::remove_if(begin, end, is_deleteable), end); + entries.erase(std::remove_if(begin, end, is_deletable), end); const size_t new_size = entries.size(); staging.delete_index += deletions_per_tick; diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index befd76a22..7a1569278 100755 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -125,7 +125,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, info.format); VkImageCreateFlags flags{}; if (info.type == ImageType::e2D && info.resources.layers >= 6 && - info.size.width == info.size.height && !device.HasBrokenCubeImageCompability()) { + info.size.width == info.size.height && !device.HasBrokenCubeImageCompatibility()) { flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; } if (info.type == ImageType::e3D) { diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp index 12002a4ac..513f21f0f 100755 --- a/src/video_core/shader_environment.cpp +++ b/src/video_core/shader_environment.cpp @@ -322,7 +322,7 @@ GraphicsEnvironment::GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_, ASSERT(local_size <= std::numeric_limits::max()); local_memory_size = static_cast(local_size) + sph.common3.shader_local_memory_crs_size; texture_bound = maxwell3d->regs.bindless_texture_const_buffer_slot; - is_propietary_driver = texture_bound == 2; + is_proprietary_driver = texture_bound == 2; has_hle_engine_state = maxwell3d->engine_state == Tegra::Engines::Maxwell3D::EngineHint::OnHLEMacro; } @@ -404,7 +404,7 @@ ComputeEnvironment::ComputeEnvironment(Tegra::Engines::KeplerCompute& kepler_com stage = Shader::Stage::Compute; local_memory_size = qmd.local_pos_alloc + qmd.local_crs_alloc; texture_bound = kepler_compute->regs.tex_cb_index; - is_propietary_driver = texture_bound == 2; + is_proprietary_driver = texture_bound == 2; shared_memory_size = qmd.shared_alloc; workgroup_size = {qmd.block_dim_x, qmd.block_dim_y, qmd.block_dim_z}; } @@ -509,7 +509,7 @@ void FileEnvironment::Deserialize(std::ifstream& file) { file.read(reinterpret_cast(&gp_passthrough_mask), sizeof(gp_passthrough_mask)); } } - is_propietary_driver = texture_bound == 2; + is_proprietary_driver = texture_bound == 2; } void FileEnvironment::Dump(u64 pipeline_hash, u64 shader_hash) { diff --git a/src/video_core/texture_cache/accelerated_swizzle.cpp b/src/video_core/texture_cache/accelerated_swizzle.cpp index 21dae8394..551f9ba48 100755 --- a/src/video_core/texture_cache/accelerated_swizzle.cpp +++ b/src/video_core/texture_cache/accelerated_swizzle.cpp @@ -66,4 +66,4 @@ BlockLinearSwizzle3DParams MakeBlockLinearSwizzle3DParams(const SwizzleParameter }; } -} // namespace VideoCommon::Accelerated \ No newline at end of file +} // namespace VideoCommon::Accelerated diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 4b2fac8ed..88a653e94 100755 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -586,7 +586,7 @@ public: } /// Returns true when the device does not properly support cube compatibility. - bool HasBrokenCubeImageCompability() const { + bool HasBrokenCubeImageCompatibility() const { return has_broken_cube_compatibility; } diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp index 077ee68cb..c6db92865 100755 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp @@ -57,7 +57,7 @@ struct Range { return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; } -[[nodiscard]] VkMemoryPropertyFlags MemoryUsagePreferedVmaFlags(MemoryUsage usage) { +[[nodiscard]] VkMemoryPropertyFlags MemoryUsagePreferredVmaFlags(MemoryUsage usage) { return usage != MemoryUsage::DeviceLocal ? VK_MEMORY_PROPERTY_HOST_COHERENT_BIT : VkMemoryPropertyFlagBits{}; } @@ -256,7 +256,7 @@ vk::Buffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo& ci, MemoryUsa .flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage), .usage = MemoryUsageVma(usage), .requiredFlags = 0, - .preferredFlags = MemoryUsagePreferedVmaFlags(usage), + .preferredFlags = MemoryUsagePreferredVmaFlags(usage), .memoryTypeBits = usage == MemoryUsage::Stream ? 0u : valid_memory_types, .pool = VK_NULL_HANDLE, .pUserData = nullptr, diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 482982c40..249e26cc5 100755 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -1650,9 +1650,21 @@ void ConfigureInputPlayer::SaveProfile() { void ConfigureInputPlayer::UpdateInputProfiles() { ui->comboProfiles->clear(); - for (const auto& profile_name : profiles->GetInputProfileNames()) { + // Set current profile as empty by default + int profile_index = -1; + + // Add every available profile and search the player profile to set it as current one + auto& current_profile = Settings::values.players.GetValue()[player_index].profile_name; + std::vector profile_names = profiles->GetInputProfileNames(); + std::string profile_name; + for (size_t i = 0; i < profile_names.size(); i++) { + profile_name = profile_names[i]; ui->comboProfiles->addItem(QString::fromStdString(profile_name)); + if (current_profile == profile_name) { + profile_index = (int)i; + } } - ui->comboProfiles->setCurrentIndex(-1); + LOG_DEBUG(Frontend, "Setting the current input profile to index {}", profile_index); + ui->comboProfiles->setCurrentIndex(profile_index); } diff --git a/src/yuzu/configuration/configure_ringcon.cpp b/src/yuzu/configuration/configure_ringcon.cpp index 94cfd48b1..656106e38 100755 --- a/src/yuzu/configuration/configure_ringcon.cpp +++ b/src/yuzu/configuration/configure_ringcon.cpp @@ -494,4 +494,4 @@ QString ConfigureRingController::AnalogToText(const Common::ParamPackage& param, } return QObject::tr("[unknown]"); -} \ No newline at end of file +} diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index 0042fb314..2c4180f57 100755 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp @@ -14,7 +14,6 @@ #include #include "common/settings.h" #include "core/core.h" -#include "core/hle/service/time/time_manager.h" #include "ui_configure_system.h" #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_system.h" diff --git a/src/yuzu/configuration/input_profiles.cpp b/src/yuzu/configuration/input_profiles.cpp index f05bf3644..480300277 100755 --- a/src/yuzu/configuration/input_profiles.cpp +++ b/src/yuzu/configuration/input_profiles.cpp @@ -5,6 +5,7 @@ #include "common/fs/fs.h" #include "common/fs/path_util.h" +#include "common/logging/log.h" #include "frontend_common/config.h" #include "yuzu/configuration/input_profiles.h" @@ -113,6 +114,8 @@ bool InputProfiles::LoadProfile(const std::string& profile_name, std::size_t pla return false; } + LOG_INFO(Config, "Loading input profile `{}`", profile_name); + map_profiles[profile_name]->ReadQtControlPlayerValues(player_index); return true; } diff --git a/src/yuzu/configuration/qt_config.cpp b/src/yuzu/configuration/qt_config.cpp index 6aca71d7c..1051031f2 100755 --- a/src/yuzu/configuration/qt_config.cpp +++ b/src/yuzu/configuration/qt_config.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "common/logging/log.h" #include "input_common/main.h" #include "qt_config.h" #include "uisettings.h" @@ -65,7 +66,7 @@ void QtConfig::ReloadAllValues() { } void QtConfig::SaveAllValues() { - Save(); + SaveValues(); SaveQtValues(); } @@ -327,7 +328,10 @@ void QtConfig::ReadMultiplayerValues() { void QtConfig::SaveQtValues() { if (global) { + LOG_DEBUG(Config, "Saving global Qt configuration values"); SaveUIValues(); + } else { + LOG_DEBUG(Config, "Saving Qt configuration values"); } SaveQtControlValues(); @@ -545,6 +549,7 @@ void QtConfig::ReadQtControlPlayerValues(std::size_t player_index) { void QtConfig::SaveQtControlPlayerValues(std::size_t player_index) { BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); + LOG_DEBUG(Config, "Saving players control configuration values"); SavePlayerValues(player_index); SaveQtPlayerValues(player_index); diff --git a/src/yuzu/debugger/console.h b/src/yuzu/debugger/console.h index 767f5a7d1..83102f3d6 100755 --- a/src/yuzu/debugger/console.h +++ b/src/yuzu/debugger/console.h @@ -10,4 +10,4 @@ namespace Debugger { * get a real qt logging window which would work for all platforms. */ void ToggleConsole(); -} // namespace Debugger \ No newline at end of file +} // namespace Debugger diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 18eddb76f..9263b7d54 100755 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -46,7 +46,7 @@ #include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_oe.h" #include "core/hle/service/am/applets/applets.h" -#include "core/hle/service/set/set_sys.h" +#include "core/hle/service/set/system_settings_server.h" #include "hid_core/frontend/emulated_controller.h" #include "hid_core/hid_core.h" #include "yuzu/multiplayer/state.h" diff --git a/src/yuzu_cmd/sdl_config.cpp b/src/yuzu_cmd/sdl_config.cpp index e81bf5d45..995114510 100755 --- a/src/yuzu_cmd/sdl_config.cpp +++ b/src/yuzu_cmd/sdl_config.cpp @@ -5,6 +5,7 @@ #define SDL_MAIN_HANDLED #include +#include "common/logging/log.h" #include "input_common/main.h" #include "sdl_config.h" @@ -64,7 +65,7 @@ void SdlConfig::ReloadAllValues() { } void SdlConfig::SaveAllValues() { - Save(); + SaveValues(); SaveSdlValues(); } @@ -177,6 +178,7 @@ void SdlConfig::ReadHidbusValues() { } void SdlConfig::SaveSdlValues() { + LOG_DEBUG(Config, "Saving SDL configuration values"); SaveSdlControlValues(); WriteToIni();