diff options
345 files changed, 13660 insertions, 5599 deletions
diff --git a/CMakeModules/FindSimpleIni.cmake b/CMakeModules/FindSimpleIni.cmake index ce75d7690..13426b25b 100644 --- a/CMakeModules/FindSimpleIni.cmake +++ b/CMakeModules/FindSimpleIni.cmake @@ -2,18 +2,20 @@ # # SPDX-License-Identifier: GPL-3.0-or-later -find_path(SimpleIni_INCLUDE_DIR SimpleIni.h) - include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(SimpleIni - REQUIRED_VARS SimpleIni_INCLUDE_DIR -) -if (SimpleIni_FOUND AND NOT TARGET SimpleIni::SimpleIni) - add_library(SimpleIni::SimpleIni INTERFACE IMPORTED) - set_target_properties(SimpleIni::SimpleIni PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${SimpleIni_INCLUDE_DIR}" +find_package(SimpleIni QUIET CONFIG) +if (SimpleIni_CONSIDERED_CONFIGS) + find_package_handle_standard_args(SimpleIni CONFIG_MODE) +else() + find_package(PkgConfig QUIET) + pkg_search_module(SIMPLEINI QUIET IMPORTED_TARGET simpleini) + find_package_handle_standard_args(SimpleIni + REQUIRED_VARS SIMPLEINI_INCLUDEDIR + VERSION_VAR SIMPLEINI_VERSION ) endif() -mark_as_advanced(SimpleIni_INCLUDE_DIR) +if (SimpleIni_FOUND AND NOT TARGET SimpleIni::SimpleIni) + add_library(SimpleIni::SimpleIni ALIAS PkgConfig::SIMPLEINI) +endif() diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 407c5c640..15b444338 100644 --- 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/tz/tz/tz.cpp b/externals/tz/tz/tz.cpp new file mode 100644 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 <climits> +#include <cstring> +#include <ctime> + +#include "tz.h" + +namespace Tz { + +namespace { +#define EINVAL 22 + +static Rule gmtmem{}; +static Rule* const gmtptr = &gmtmem; + +struct TzifHeader { + std::array<char, 4> tzh_magic; // "TZif" + std::array<char, 1> tzh_version; + std::array<char, 15> tzh_reserved; + std::array<char, 4> tzh_ttisutcnt; + std::array<char, 4> tzh_ttisstdcnt; + std::array<char, 4> tzh_leapcnt; + std::array<char, 4> tzh_timecnt; + std::array<char, 4> tzh_typecnt; + std::array<char, 4> 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<const u8> 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<std::array<int, MONSPERYEAR>, 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<int, 2> 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<int_fast64_t>(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<s32>(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> ? 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<s32>(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<time_t> && y < TM_YEAR_BASE) { + int signed_y = static_cast<s32>(y); + tmp->tm_year = signed_y - TM_YEAR_BASE; + } + else if ((!std::is_signed_v<time_t> || std::numeric_limits<s32>::min() + TM_YEAR_BASE <= y) && + y - TM_YEAR_BASE <= std::numeric_limits<s32>::max()) { + tmp->tm_year = static_cast<s32>(y - TM_YEAR_BASE); + } + else { + // errno = EOVERFLOW; + return nullptr; + } + + tmp->tm_yday = static_cast<s32>(idays); + /* + ** The "extra" mods below avoid overflow problems. + */ + tmp->tm_wday = static_cast<s32>( + 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<s32>(rem / SECSPERHOUR); + rem %= SECSPERHOUR; + tmp->tm_min = static_cast<s32>(rem / SECSPERMIN); + tmp->tm_sec = static_cast<s32>(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<s32>(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<s32>::min() <= newy && + newy <= std::numeric_limits<s32>::max())) { + return nullptr; + } + result->tm_year = static_cast<s32>(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<s32>(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<s32>(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<time_t*>(&sp->ats[timecnt]), janoffset + starttime) && + atlo <= sp->ats[timecnt]) { + sp->types[timecnt++] = !reversed; + } + sp->ats[timecnt] = janfirst; + if (!increment_overflow_time(reinterpret_cast<time_t*>(&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<s32>(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<s32>(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<s32>(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<time_t> ? 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<char, 256> 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<s32>(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<u8>(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<s32>(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<bool>(yourtm.tm_isdst)) { + continue; + } + for (j = sp->typecnt - 1; j >= 0; --j) { + if (sp->ttis[j].tt_isdst == static_cast<bool>(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<bool>(tmp->tm_isdst)) { + continue; + } + for (otherind = 0; otherind < nseen; ++otherind) { + otheri = types[otherind]; + if (sp->ttis[otheri].tt_isdst == static_cast<bool>(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<const u8> 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 100644 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 <cstdint> +#include <limits> +#include <span> +#include <array> +#include <time.h> + +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<s64>::max(); +constexpr s64 TIME_T_MIN = std::numeric_limits<s64>::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 <u8, 0x2> padding0; + std::array<s64, TZ_MAX_TIMES> ats; + std::array<u8, TZ_MAX_TIMES> types; + std::array<ttinfo, TZ_MAX_TYPES> ttis; + std::array<char, std::max(MAX_ZONE_CHARS, MAX_TZNAME_CHARS)> chars; + s32 defaulttype; + std::array <u8, 0x12C4> 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<char, 16> 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<const u8> 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/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt index c408485c6..55abba093 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt @@ -303,6 +303,11 @@ object NativeLibrary { */ external fun getCpuBackend(): String + /** + * Returns the current GPU Driver. + */ + external fun getGpuDriver(): String + external fun applySettings() external fun logSettings() @@ -615,6 +620,11 @@ object NativeLibrary { external fun clearFilesystemProvider() /** + * Checks if all necessary keys are present for decryption + */ + external fun areKeysPresent(): Boolean + + /** * Button type for use in onTouchEvent */ object ButtonType { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt index 9b08f008d..26cddecf4 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt @@ -193,6 +193,10 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { return super.dispatchKeyEvent(event) } + if (emulationViewModel.drawerOpen.value) { + return super.dispatchKeyEvent(event) + } + return InputHandler.dispatchKeyEvent(event) } @@ -203,6 +207,10 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { return super.dispatchGenericMotionEvent(event) } + if (emulationViewModel.drawerOpen.value) { + return super.dispatchGenericMotionEvent(event) + } + // Don't attempt to do anything if we are disconnecting a device. if (event.actionMasked == MotionEvent.ACTION_CANCEL) { return true diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractDiffAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractDiffAdapter.kt index f006f9e3d..0ab1b46c3 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractDiffAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/AbstractDiffAdapter.kt @@ -14,15 +14,20 @@ import androidx.recyclerview.widget.RecyclerView * Generic adapter that implements an [AsyncDifferConfig] and covers some of the basic boilerplate * code used in every [RecyclerView]. * Type assigned to [Model] must inherit from [Object] in order to be compared properly. + * @param exact Decides whether each item will be compared by reference or by their contents */ -abstract class AbstractDiffAdapter<Model : Any, Holder : AbstractViewHolder<Model>> : - ListAdapter<Model, Holder>(AsyncDifferConfig.Builder(DiffCallback<Model>()).build()) { +abstract class AbstractDiffAdapter<Model : Any, Holder : AbstractViewHolder<Model>>( + exact: Boolean = true +) : ListAdapter<Model, Holder>(AsyncDifferConfig.Builder(DiffCallback<Model>(exact)).build()) { override fun onBindViewHolder(holder: Holder, position: Int) = holder.bind(currentList[position]) - private class DiffCallback<Model> : DiffUtil.ItemCallback<Model>() { + private class DiffCallback<Model>(val exact: Boolean) : DiffUtil.ItemCallback<Model>() { override fun areItemsTheSame(oldItem: Model & Any, newItem: Model & Any): Boolean { - return oldItem === newItem + if (exact) { + return oldItem === newItem + } + return oldItem == newItem } @SuppressLint("DiffUtilEquals") diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt index b4f4d950f..85c8249e6 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt @@ -30,7 +30,7 @@ import org.yuzu.yuzu_emu.utils.GameIconUtils import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder class GameAdapter(private val activity: AppCompatActivity) : - AbstractDiffAdapter<Game, GameAdapter.GameViewHolder>() { + AbstractDiffAdapter<Game, GameAdapter.GameViewHolder>(exact = false) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameViewHolder { CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false) .also { return GameViewHolder(it) } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt index 5b5f800c1..5ab38ffda 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt @@ -77,7 +77,7 @@ class AboutFragment : Fragment() { } binding.textVersionName.text = BuildConfig.VERSION_NAME - binding.textVersionName.setOnClickListener { + binding.buttonVersionName.setOnClickListener { val clipBoard = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val clip = ClipData.newPlainText(getString(R.string.build), BuildConfig.GIT_HASH) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt index 2a97ae14d..22da1d0e5 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt @@ -141,7 +141,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { // So this fragment doesn't restart on configuration changes; i.e. rotation. retainInstance = true - emulationState = EmulationState(game.path) + emulationState = EmulationState(game.path) { + return@EmulationState driverViewModel.isInteractionAllowed.value + } } /** @@ -183,10 +185,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { override fun onDrawerOpened(drawerView: View) { binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED) + binding.inGameMenu.requestFocus() + emulationViewModel.setDrawerOpen(true) } override fun onDrawerClosed(drawerView: View) { binding.drawerLayout.setDrawerLockMode(IntSetting.LOCK_DRAWER.getInt()) + emulationViewModel.setDrawerOpen(false) } override fun onDrawerStateChanged(newState: Int) { @@ -238,6 +243,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { requireContext().theme ) } + binding.inGameMenu.requestFocus() true } @@ -246,6 +252,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { null, Settings.MenuTag.SECTION_ROOT ) + binding.inGameMenu.requestFocus() binding.root.findNavController().navigate(action) true } @@ -255,6 +262,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { args.game, Settings.MenuTag.SECTION_ROOT ) + binding.inGameMenu.requestFocus() binding.root.findNavController().navigate(action) true } @@ -286,6 +294,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { ) } } + binding.inGameMenu.requestFocus() NativeConfig.saveGlobalConfig() true } @@ -294,7 +303,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { emulationState.stop() emulationViewModel.setIsEmulationStopping(true) binding.drawerLayout.close() - binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) + binding.inGameMenu.requestFocus() true } @@ -311,12 +320,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { if (!NativeLibrary.isRunning()) { return } - - if (binding.drawerLayout.isOpen) { - binding.drawerLayout.close() - } else { - binding.drawerLayout.open() - } + emulationViewModel.setDrawerOpen(!binding.drawerLayout.isOpen) } } ) @@ -371,6 +375,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { } } launch { + repeatOnLifecycle(Lifecycle.State.RESUMED) { + driverViewModel.isInteractionAllowed.collect { + if (it) { + startEmulation() + } + } + } + } + launch { repeatOnLifecycle(Lifecycle.State.CREATED) { emulationViewModel.emulationStarted.collectLatest { if (it) { @@ -399,10 +412,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { } } launch { - repeatOnLifecycle(Lifecycle.State.RESUMED) { - driverViewModel.isInteractionAllowed.collect { + repeatOnLifecycle(Lifecycle.State.CREATED) { + emulationViewModel.drawerOpen.collect { if (it) { - onEmulationStart() + binding.drawerLayout.open() + binding.inGameMenu.requestFocus() + } else { + binding.drawerLayout.close() } } } @@ -410,7 +426,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { } } - private fun onEmulationStart() { + private fun startEmulation() { if (!NativeLibrary.isRunning() && !NativeLibrary.isPaused()) { if (!DirectoryInitialization.areDirectoriesReady) { DirectoryInitialization.start() @@ -485,12 +501,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { val FRAMETIME = 2 val SPEED = 3 perfStatsUpdater = { - if (emulationViewModel.emulationStarted.value) { + if (emulationViewModel.emulationStarted.value && + !emulationViewModel.isEmulationStopping.value + ) { val perfStats = NativeLibrary.getPerfStats() val cpuBackend = NativeLibrary.getCpuBackend() + val gpuDriver = NativeLibrary.getGpuDriver() if (_binding != null) { binding.showFpsText.text = - String.format("FPS: %.1f\n%s", perfStats[FPS], cpuBackend) + String.format("FPS: %.1f\n%s/%s", perfStats[FPS], cpuBackend, gpuDriver) } perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800) } @@ -807,7 +826,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { } } - private class EmulationState(private val gamePath: String) { + private class EmulationState( + private val gamePath: String, + private val emulationCanStart: () -> Boolean + ) { private var state: State private var surface: Surface? = null @@ -901,6 +923,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { State.PAUSED -> Log.warning( "[EmulationFragment] Surface cleared while emulation paused." ) + else -> Log.warning( "[EmulationFragment] Surface cleared while emulation stopped." ) @@ -910,6 +933,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { private fun runWithValidSurface() { NativeLibrary.surfaceChanged(surface) + if (!emulationCanStart.invoke()) { + return + } + when (state) { State.STOPPED -> { val emulationThread = Thread({ diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt index aefae2938..1f3578b22 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt @@ -153,7 +153,13 @@ class HomeSettingsFragment : Fragment() { cancellable = true ) { progressCallback, _ -> val result = NativeLibrary.verifyInstalledContents(progressCallback) - return@newInstance if (result.isEmpty()) { + return@newInstance if (progressCallback.invoke(100, 100)) { + // Invoke the progress callback to check if the process was cancelled + MessageDialogFragment.newInstance( + titleId = R.string.verify_no_result, + descriptionId = R.string.verify_no_result_description + ) + } else if (result.isEmpty()) { MessageDialogFragment.newInstance( titleId = R.string.verify_success, descriptionId = R.string.operation_completed_successfully diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt index 620d8db7c..22b084b9a 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt @@ -26,9 +26,15 @@ class MessageDialogFragment : DialogFragment() { val descriptionId = requireArguments().getInt(DESCRIPTION_ID) val descriptionString = requireArguments().getString(DESCRIPTION_STRING)!! val helpLinkId = requireArguments().getInt(HELP_LINK) + val dismissible = requireArguments().getBoolean(DISMISSIBLE) + val clearPositiveAction = requireArguments().getBoolean(CLEAR_POSITIVE_ACTION) val builder = MaterialAlertDialogBuilder(requireContext()) + if (clearPositiveAction) { + messageDialogViewModel.positiveAction = null + } + if (messageDialogViewModel.positiveAction == null) { builder.setPositiveButton(R.string.close, null) } else { @@ -51,6 +57,8 @@ class MessageDialogFragment : DialogFragment() { } } + isCancelable = dismissible + return builder.show() } @@ -67,6 +75,8 @@ class MessageDialogFragment : DialogFragment() { private const val DESCRIPTION_ID = "DescriptionId" private const val DESCRIPTION_STRING = "DescriptionString" private const val HELP_LINK = "Link" + private const val DISMISSIBLE = "Dismissible" + private const val CLEAR_POSITIVE_ACTION = "ClearPositiveAction" fun newInstance( activity: FragmentActivity? = null, @@ -75,22 +85,28 @@ class MessageDialogFragment : DialogFragment() { descriptionId: Int = 0, descriptionString: String = "", helpLinkId: Int = 0, + dismissible: Boolean = true, positiveAction: (() -> Unit)? = null ): MessageDialogFragment { + var clearPositiveAction = false + if (activity != null) { + ViewModelProvider(activity)[MessageDialogViewModel::class.java].apply { + clear() + this.positiveAction = positiveAction + } + } else { + clearPositiveAction = true + } + val dialog = MessageDialogFragment() - val bundle = Bundle() - bundle.apply { + val bundle = Bundle().apply { putInt(TITLE_ID, titleId) putString(TITLE_STRING, titleString) putInt(DESCRIPTION_ID, descriptionId) putString(DESCRIPTION_STRING, descriptionString) putInt(HELP_LINK, helpLinkId) - } - if (activity != null) { - ViewModelProvider(activity)[MessageDialogViewModel::class.java].apply { - clear() - this.positiveAction = positiveAction - } + putBoolean(DISMISSIBLE, dismissible) + putBoolean(CLEAR_POSITIVE_ACTION, clearPositiveAction) } dialog.arguments = bundle return dialog diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt index 064342cdd..ebf41a639 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt @@ -31,6 +31,7 @@ import androidx.preference.PreferenceManager import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback import com.google.android.material.transition.MaterialFadeThrough import kotlinx.coroutines.launch +import org.yuzu.yuzu_emu.NativeLibrary import java.io.File import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication @@ -162,7 +163,7 @@ class SetupFragment : Fragment() { R.string.install_prod_keys_warning_help, { val file = File(DirectoryInitialization.userDirectory + "/keys/prod.keys") - if (file.exists()) { + if (file.exists() && NativeLibrary.areKeysPresent()) { StepState.COMPLETE } else { StepState.INCOMPLETE @@ -347,7 +348,8 @@ class SetupFragment : Fragment() { val getProdKey = registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> if (result != null) { - if (mainActivity.processKey(result)) { + mainActivity.processKey(result) + if (NativeLibrary.areKeysPresent()) { keyCallback.onStepCompleted() } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt index 15ae3a42b..5ed754c96 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt @@ -144,6 +144,7 @@ class DriverViewModel : ViewModel() { val selectedDriverFile = File(StringSetting.DRIVER_PATH.getString()) val selectedDriverMetadata = GpuDriverHelper.customDriverSettingData if (GpuDriverHelper.installedCustomDriverData == selectedDriverMetadata) { + setDriverReady() return } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt index f34870c2d..b66f47fe7 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt @@ -6,6 +6,7 @@ package org.yuzu.yuzu_emu.model import androidx.lifecycle.ViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow class EmulationViewModel : ViewModel() { val emulationStarted: StateFlow<Boolean> get() = _emulationStarted @@ -23,6 +24,9 @@ class EmulationViewModel : ViewModel() { val shaderMessage: StateFlow<String> get() = _shaderMessage private val _shaderMessage = MutableStateFlow("") + private val _drawerOpen = MutableStateFlow(false) + val drawerOpen = _drawerOpen.asStateFlow() + fun setEmulationStarted(started: Boolean) { _emulationStarted.value = started } @@ -49,6 +53,10 @@ class EmulationViewModel : ViewModel() { setTotalShaders(max) } + fun setDrawerOpen(value: Boolean) { + _drawerOpen.value = value + } + fun clear() { setEmulationStarted(false) setIsEmulationStopping(false) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt index c8a4a2d17..6859b7780 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt @@ -70,11 +70,19 @@ class Game( } override fun equals(other: Any?): Boolean { - if (other !is Game) { - return false - } + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Game + + if (title != other.title) return false + if (path != other.path) return false + if (programId != other.programId) return false + if (developer != other.developer) return false + if (version != other.version) return false + if (isHomebrew != other.isHomebrew) return false - return hashCode() == other.hashCode() + return true } override fun hashCode(): Int { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt index 513ac2fc5..cfc777b81 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt @@ -31,6 +31,9 @@ class HomeViewModel : ViewModel() { private val _reloadPropertiesList = MutableStateFlow(false) val reloadPropertiesList get() = _reloadPropertiesList.asStateFlow() + private val _checkKeys = MutableStateFlow(false) + val checkKeys = _checkKeys.asStateFlow() + var navigatedToSetup = false fun setNavigationVisibility(visible: Boolean, animated: Boolean) { @@ -66,4 +69,8 @@ class HomeViewModel : ViewModel() { fun reloadPropertiesList(reload: Boolean) { _reloadPropertiesList.value = reload } + + fun setCheckKeys(value: Boolean) { + _checkKeys.value = value + } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt index c2cc29961..b3967d294 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt @@ -64,6 +64,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider { override var themeId: Int = 0 + private val CHECKED_DECRYPTION = "CheckedDecryption" + private var checkedDecryption = false + override fun onCreate(savedInstanceState: Bundle?) { val splashScreen = installSplashScreen() splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady } @@ -75,6 +78,18 @@ class MainActivity : AppCompatActivity(), ThemeProvider { binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) + if (savedInstanceState != null) { + checkedDecryption = savedInstanceState.getBoolean(CHECKED_DECRYPTION) + } + if (!checkedDecryption) { + val firstTimeSetup = PreferenceManager.getDefaultSharedPreferences(applicationContext) + .getBoolean(Settings.PREF_FIRST_APP_LAUNCH, true) + if (!firstTimeSetup) { + checkKeys() + } + checkedDecryption = true + } + WindowCompat.setDecorFitsSystemWindows(window, false) window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING) @@ -150,6 +165,16 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } } } + launch { + repeatOnLifecycle(Lifecycle.State.CREATED) { + homeViewModel.checkKeys.collect { + if (it) { + checkKeys() + homeViewModel.setCheckKeys(false) + } + } + } + } } // Dismiss previous notifications (should not happen unless a crash occurred) @@ -158,6 +183,21 @@ class MainActivity : AppCompatActivity(), ThemeProvider { setInsets() } + private fun checkKeys() { + if (!NativeLibrary.areKeysPresent()) { + MessageDialogFragment.newInstance( + titleId = R.string.keys_missing, + descriptionId = R.string.keys_missing_description, + helpLinkId = R.string.keys_missing_help + ).show(supportFragmentManager, MessageDialogFragment.TAG) + } + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putBoolean(CHECKED_DECRYPTION, checkedDecryption) + } + fun finishSetup(navController: NavController) { navController.navigate(R.id.action_firstTimeSetupFragment_to_gamesFragment) (binding.navigationView as NavigationBarView).setupWithNavController(navController) @@ -349,6 +389,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { R.string.install_keys_success, Toast.LENGTH_SHORT ).show() + homeViewModel.setCheckKeys(true) gamesViewModel.reloadGames(true) return true } else { @@ -399,6 +440,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { firmwarePath.deleteRecursively() cacheFirmwareDir.copyRecursively(firmwarePath, true) NativeLibrary.initializeSystem(true) + homeViewModel.setCheckKeys(true) getString(R.string.save_file_imported_success) } } catch (e: Exception) { diff --git a/src/android/app/src/main/jni/game_metadata.cpp b/src/android/app/src/main/jni/game_metadata.cpp index 78f604c70..8f0da1413 100644 --- a/src/android/app/src/main/jni/game_metadata.cpp +++ b/src/android/app/src/main/jni/game_metadata.cpp @@ -1,12 +1,12 @@ // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include <core/core.h> -#include <core/file_sys/mode.h> -#include <core/file_sys/patch_manager.h> -#include <core/loader/nro.h> -#include <jni.h> +#include "core/core.h" +#include "core/file_sys/fs_filesystem.h" +#include "core/file_sys/patch_manager.h" #include "core/loader/loader.h" +#include "core/loader/nro.h" +#include "jni.h" #include "jni/android_common/android_common.h" #include "native.h" @@ -79,7 +79,7 @@ extern "C" { jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsValid(JNIEnv* env, jobject obj, jstring jpath) { const auto file = EmulationSession::GetInstance().System().GetFilesystem()->OpenFile( - GetJString(env, jpath), FileSys::Mode::Read); + GetJString(env, jpath), FileSys::OpenMode::Read); if (!file) { return false; } diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 963f57380..3fd9a500c 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -35,9 +35,10 @@ #include "core/crypto/key_manager.h" #include "core/file_sys/card_image.h" #include "core/file_sys/content_archive.h" +#include "core/file_sys/fs_filesystem.h" #include "core/file_sys/submission_package.h" -#include "core/file_sys/vfs.h" -#include "core/file_sys/vfs_real.h" +#include "core/file_sys/vfs/vfs.h" +#include "core/file_sys/vfs/vfs_real.h" #include "core/frontend/applets/cabinet.h" #include "core/frontend/applets/controller.h" #include "core/frontend/applets/error.h" @@ -154,7 +155,7 @@ void EmulationSession::SurfaceChanged() { } void EmulationSession::ConfigureFilesystemProvider(const std::string& filepath) { - const auto file = m_system.GetFilesystem()->OpenFile(filepath, FileSys::Mode::Read); + const auto file = m_system.GetFilesystem()->OpenFile(filepath, FileSys::OpenMode::Read); if (!file) { return; } @@ -247,6 +248,7 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string m_system.GetCpuManager().OnGpuReady(); m_system.RegisterExitCallback([&] { HaltEmulation(); }); + OnEmulationStarted(); return Core::SystemResultStatus::Success; } @@ -463,8 +465,8 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject }; return static_cast<int>( - ContentManager::InstallNSP(&EmulationSession::GetInstance().System(), - EmulationSession::GetInstance().System().GetFilesystem().get(), + ContentManager::InstallNSP(EmulationSession::GetInstance().System(), + *EmulationSession::GetInstance().System().GetFilesystem(), GetJString(env, j_file), callback)); } @@ -474,8 +476,8 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* en u64 program_id = EmulationSession::GetProgramId(env, jprogramId); std::string updatePath = GetJString(env, jupdatePath); std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>( - EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(updatePath, - FileSys::Mode::Read)); + EmulationSession::GetInstance().System().GetFilesystem()->OpenFile( + updatePath, FileSys::OpenMode::Read)); for (const auto& item : nsp->GetNCAs()) { for (const auto& nca_details : item.second) { if (nca_details.second->GetName().ends_with(".cnmt.nca")) { @@ -674,6 +676,11 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCpuBackend(JNIEnv* env, jclass return ToJString(env, "JIT"); } +jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGpuDriver(JNIEnv* env, jobject jobj) { + return ToJString(env, + EmulationSession::GetInstance().System().GPU().Renderer().GetDeviceVendor()); +} + void Java_org_yuzu_yuzu_1emu_NativeLibrary_applySettings(JNIEnv* env, jobject jobj) { EmulationSession::GetInstance().System().ApplySettings(); } @@ -713,7 +720,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmptyUserDirectory(JNIEnv* jobject instance) { const auto nand_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir); auto vfs_nand_dir = EmulationSession::GetInstance().System().GetFilesystem()->OpenDirectory( - Common::FS::PathToUTF8String(nand_dir), FileSys::Mode::Read); + Common::FS::PathToUTF8String(nand_dir), FileSys::OpenMode::Read); const auto user_id = EmulationSession::GetInstance().System().GetProfileManager().GetUser( static_cast<std::size_t>(0)); @@ -819,7 +826,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeUpdate(JNIEnv* env, jobject job void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeDLC(JNIEnv* env, jobject jobj, jstring jprogramId) { auto program_id = EmulationSession::GetProgramId(env, jprogramId); - ContentManager::RemoveAllDLC(&EmulationSession::GetInstance().System(), program_id); + ContentManager::RemoveAllDLC(EmulationSession::GetInstance().System(), program_id); } void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj, jstring jprogramId, @@ -829,8 +836,9 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj, program_id, GetJString(env, jname)); } -jobject Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env, jobject jobj, - jobject jcallback) { +jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env, + jobject jobj, + jobject jcallback) { auto jlambdaClass = env->GetObjectClass(jcallback); auto jlambdaInvokeMethod = env->GetMethodID( jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); @@ -842,7 +850,7 @@ jobject Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* en auto& session = EmulationSession::GetInstance(); std::vector<std::string> result = ContentManager::VerifyInstalledContents( - &session.System(), session.GetContentProvider(), callback); + session.System(), *session.GetContentProvider(), callback); jobjectArray jresult = env->NewObjectArray(result.size(), IDCache::GetStringClass(), ToJString(env, "")); for (size_t i = 0; i < result.size(); ++i) { @@ -863,7 +871,7 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyGameContents(JNIEnv* env, jobje }; auto& session = EmulationSession::GetInstance(); return static_cast<jint>( - ContentManager::VerifyGameContents(&session.System(), GetJString(env, jpath), callback)); + ContentManager::VerifyGameContents(session.System(), GetJString(env, jpath), callback)); } jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj, @@ -882,7 +890,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject j const auto nandDir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir); auto vfsNandDir = system.GetFilesystem()->OpenDirectory(Common::FS::PathToUTF8String(nandDir), - FileSys::Mode::Read); + FileSys::OpenMode::Read); const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( {}, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, @@ -912,4 +920,10 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_clearFilesystemProvider(JNIEnv* env, EmulationSession::GetInstance().GetContentProvider()->ClearAllEntries(); } +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_areKeysPresent(JNIEnv* env, jobject jobj) { + auto& system = EmulationSession::GetInstance().System(); + system.GetFileSystemController().CreateFactories(*system.GetFilesystem()); + return ContentManager::AreKeysPresent(); +} + } // extern "C" diff --git a/src/android/app/src/main/res/layout-w600dp/fragment_about.xml b/src/android/app/src/main/res/layout-w600dp/fragment_about.xml index 655e49219..a5eba6474 100644 --- a/src/android/app/src/main/res/layout-w600dp/fragment_about.xml +++ b/src/android/app/src/main/res/layout-w600dp/fragment_about.xml @@ -11,12 +11,14 @@ android:id="@+id/appbar_about" android:layout_width="match_parent" android:layout_height="wrap_content" - android:fitsSystemWindows="true"> + android:fitsSystemWindows="true" + android:touchscreenBlocksFocus="false"> <com.google.android.material.appbar.MaterialToolbar android:id="@+id/toolbar_about" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" + android:touchscreenBlocksFocus="false" app:navigationIcon="@drawable/ic_back" app:title="@string/about" /> @@ -28,6 +30,7 @@ android:layout_height="match_parent" android:fadeScrollbars="false" android:scrollbars="vertical" + android:defaultFocusHighlightEnabled="false" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <LinearLayout diff --git a/src/android/app/src/main/res/layout-w600dp/fragment_game_info.xml b/src/android/app/src/main/res/layout-w600dp/fragment_game_info.xml new file mode 100644 index 000000000..90d95dbb7 --- /dev/null +++ b/src/android/app/src/main/res/layout-w600dp/fragment_game_info.xml @@ -0,0 +1,155 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/coordinator_about" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="?attr/colorSurface"> + + <com.google.android.material.appbar.AppBarLayout + android:id="@+id/appbar_info" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:fitsSystemWindows="true" + android:touchscreenBlocksFocus="false"> + + <com.google.android.material.appbar.MaterialToolbar + android:id="@+id/toolbar_info" + android:layout_width="match_parent" + android:layout_height="?attr/actionBarSize" + android:touchscreenBlocksFocus="false" + app:navigationIcon="@drawable/ic_back" /> + + </com.google.android.material.appbar.AppBarLayout> + + <androidx.core.widget.NestedScrollView + android:id="@+id/scroll_info" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:defaultFocusHighlightEnabled="false" + app:layout_behavior="@string/appbar_scrolling_view_behavior"> + + <LinearLayout + android:id="@+id/content_info" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:paddingHorizontal="16dp" + android:baselineAligned="false"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:layout_weight="3" + android:gravity="top|center_horizontal" + android:paddingHorizontal="16dp"> + + <com.google.android.material.button.MaterialButton + android:id="@+id/button_copy" + style="@style/Widget.Material3.Button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="16dp" + android:text="@string/copy_details" /> + + <com.google.android.material.button.MaterialButton + android:id="@+id/button_verify_integrity" + style="@style/Widget.Material3.Button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="10dp" + android:text="@string/verify_integrity" /> + + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:layout_weight="1"> + + <com.google.android.material.textfield.TextInputLayout + android:id="@+id/path" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingTop="16dp"> + + <com.google.android.material.textfield.TextInputEditText + android:id="@+id/path_field" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:editable="false" + android:importantForAutofill="no" + android:inputType="none" + android:minHeight="48dp" + android:textAlignment="viewStart" + tools:text="1.0.0" /> + + </com.google.android.material.textfield.TextInputLayout> + + <com.google.android.material.textfield.TextInputLayout + android:id="@+id/program_id" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingTop="16dp"> + + <com.google.android.material.textfield.TextInputEditText + android:id="@+id/program_id_field" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:editable="false" + android:importantForAutofill="no" + android:inputType="none" + android:minHeight="48dp" + android:textAlignment="viewStart" + tools:text="1.0.0" /> + + </com.google.android.material.textfield.TextInputLayout> + + <com.google.android.material.textfield.TextInputLayout + android:id="@+id/developer" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingTop="16dp"> + + <com.google.android.material.textfield.TextInputEditText + android:id="@+id/developer_field" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:editable="false" + android:importantForAutofill="no" + android:inputType="none" + android:minHeight="48dp" + android:textAlignment="viewStart" + tools:text="1.0.0" /> + + </com.google.android.material.textfield.TextInputLayout> + + <com.google.android.material.textfield.TextInputLayout + android:id="@+id/version" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingTop="16dp"> + + <com.google.android.material.textfield.TextInputEditText + android:id="@+id/version_field" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:editable="false" + android:importantForAutofill="no" + android:inputType="none" + android:minHeight="48dp" + android:textAlignment="viewStart" + tools:text="1.0.0" /> + + </com.google.android.material.textfield.TextInputLayout> + + </LinearLayout> + + </LinearLayout> + + </androidx.core.widget.NestedScrollView> + +</androidx.coordinatorlayout.widget.CoordinatorLayout> diff --git a/src/android/app/src/main/res/layout-w600dp/fragment_game_properties.xml b/src/android/app/src/main/res/layout-w600dp/fragment_game_properties.xml index 551f255c0..7cdef569f 100644 --- a/src/android/app/src/main/res/layout-w600dp/fragment_game_properties.xml +++ b/src/android/app/src/main/res/layout-w600dp/fragment_game_properties.xml @@ -14,6 +14,7 @@ android:clipToPadding="false" android:fadeScrollbars="false" android:scrollbars="vertical" + android:defaultFocusHighlightEnabled="false" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/icon_layout" app:layout_constraintTop_toTopOf="parent"> diff --git a/src/android/app/src/main/res/layout/card_driver_option.xml b/src/android/app/src/main/res/layout/card_driver_option.xml index 1dd9a6d7d..bda524f0f 100644 --- a/src/android/app/src/main/res/layout/card_driver_option.xml +++ b/src/android/app/src/main/res/layout/card_driver_option.xml @@ -23,6 +23,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" + android:focusable="false" android:clickable="false" android:checked="false" /> diff --git a/src/android/app/src/main/res/layout/card_folder.xml b/src/android/app/src/main/res/layout/card_folder.xml index 4e0c04b6b..ed4a7ca8f 100644 --- a/src/android/app/src/main/res/layout/card_folder.xml +++ b/src/android/app/src/main/res/layout/card_folder.xml @@ -6,16 +6,14 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginHorizontal="16dp" - android:layout_marginVertical="12dp" - android:focusable="true"> + android:layout_marginVertical="12dp"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="16dp" - android:layout_gravity="center_vertical" - android:animateLayoutChanges="true"> + android:layout_gravity="center_vertical"> <com.google.android.material.textview.MaterialTextView android:id="@+id/path" diff --git a/src/android/app/src/main/res/layout/fragment_about.xml b/src/android/app/src/main/res/layout/fragment_about.xml index 38090fa50..7f32e139a 100644 --- a/src/android/app/src/main/res/layout/fragment_about.xml +++ b/src/android/app/src/main/res/layout/fragment_about.xml @@ -11,12 +11,14 @@ android:id="@+id/appbar_about" android:layout_width="match_parent" android:layout_height="wrap_content" - android:fitsSystemWindows="true"> + android:fitsSystemWindows="true" + android:touchscreenBlocksFocus="false"> <com.google.android.material.appbar.MaterialToolbar android:id="@+id/toolbar_about" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" + android:touchscreenBlocksFocus="false" app:title="@string/about" app:navigationIcon="@drawable/ic_back" /> @@ -28,6 +30,7 @@ android:layout_height="match_parent" android:scrollbars="vertical" android:fadeScrollbars="false" + android:defaultFocusHighlightEnabled="false" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <LinearLayout diff --git a/src/android/app/src/main/res/layout/fragment_addons.xml b/src/android/app/src/main/res/layout/fragment_addons.xml index a25e82766..b029b4209 100644 --- a/src/android/app/src/main/res/layout/fragment_addons.xml +++ b/src/android/app/src/main/res/layout/fragment_addons.xml @@ -11,6 +11,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:fitsSystemWindows="true" + android:touchscreenBlocksFocus="false" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> @@ -19,6 +20,7 @@ android:id="@+id/toolbar_addons" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" + android:touchscreenBlocksFocus="false" app:navigationIcon="@drawable/ic_back" /> </com.google.android.material.appbar.AppBarLayout> @@ -28,6 +30,8 @@ android:layout_width="match_parent" android:layout_height="0dp" android:clipToPadding="false" + android:defaultFocusHighlightEnabled="false" + android:nextFocusDown="@id/button_install" app:layout_behavior="@string/appbar_scrolling_view_behavior" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" diff --git a/src/android/app/src/main/res/layout/fragment_applet_launcher.xml b/src/android/app/src/main/res/layout/fragment_applet_launcher.xml index fe8fae40f..95e6d6a6b 100644 --- a/src/android/app/src/main/res/layout/fragment_applet_launcher.xml +++ b/src/android/app/src/main/res/layout/fragment_applet_launcher.xml @@ -10,12 +10,14 @@ android:id="@+id/appbar_applets" android:layout_width="match_parent" android:layout_height="wrap_content" - android:fitsSystemWindows="true"> + android:fitsSystemWindows="true" + android:touchscreenBlocksFocus="false"> <com.google.android.material.appbar.MaterialToolbar android:id="@+id/toolbar_applets" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" + android:touchscreenBlocksFocus="false" app:navigationIcon="@drawable/ic_back" app:title="@string/applets" /> diff --git a/src/android/app/src/main/res/layout/fragment_driver_manager.xml b/src/android/app/src/main/res/layout/fragment_driver_manager.xml index 6cea2d164..56d8e6bb8 100644 --- a/src/android/app/src/main/res/layout/fragment_driver_manager.xml +++ b/src/android/app/src/main/res/layout/fragment_driver_manager.xml @@ -15,12 +15,14 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:fitsSystemWindows="true" + android:touchscreenBlocksFocus="false" app:liftOnScrollTargetViewId="@id/list_drivers"> <com.google.android.material.appbar.MaterialToolbar android:id="@+id/toolbar_drivers" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" + android:touchscreenBlocksFocus="false" app:navigationIcon="@drawable/ic_back" app:title="@string/gpu_driver_manager" /> diff --git a/src/android/app/src/main/res/layout/fragment_early_access.xml b/src/android/app/src/main/res/layout/fragment_early_access.xml index 644b4dd45..12e233afc 100644 --- a/src/android/app/src/main/res/layout/fragment_early_access.xml +++ b/src/android/app/src/main/res/layout/fragment_early_access.xml @@ -11,12 +11,14 @@ android:id="@+id/appbar_ea" android:layout_width="match_parent" android:layout_height="wrap_content" - android:fitsSystemWindows="true"> + android:fitsSystemWindows="true" + android:touchscreenBlocksFocus="false"> <com.google.android.material.appbar.MaterialToolbar android:id="@+id/toolbar_about" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" + android:touchscreenBlocksFocus="false" app:navigationIcon="@drawable/ic_back" app:title="@string/early_access" /> @@ -30,6 +32,7 @@ android:paddingBottom="20dp" android:scrollbars="vertical" android:fadeScrollbars="false" + android:defaultFocusHighlightEnabled="false" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <LinearLayout diff --git a/src/android/app/src/main/res/layout/fragment_emulation.xml b/src/android/app/src/main/res/layout/fragment_emulation.xml index 5252adf54..c01117d14 100644 --- a/src/android/app/src/main/res/layout/fragment_emulation.xml +++ b/src/android/app/src/main/res/layout/fragment_emulation.xml @@ -5,6 +5,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:keepScreenOn="true" + android:defaultFocusHighlightEnabled="false" tools:context="org.yuzu.yuzu_emu.fragments.EmulationFragment" tools:openDrawer="start"> @@ -24,7 +25,8 @@ android:layout_height="match_parent" android:layout_gravity="center" android:focusable="false" - android:focusableInTouchMode="false" /> + android:focusableInTouchMode="false" + android:defaultFocusHighlightEnabled="false" /> <com.google.android.material.card.MaterialCardView android:id="@+id/loading_indicator" @@ -32,7 +34,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" - android:focusable="false" + android:defaultFocusHighlightEnabled="false" android:clickable="false"> <androidx.constraintlayout.widget.ConstraintLayout @@ -118,6 +120,7 @@ android:layout_gravity="center" android:focusable="true" android:focusableInTouchMode="true" + android:defaultFocusHighlightEnabled="false" android:visibility="invisible" /> <Button @@ -160,6 +163,7 @@ android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="start" + android:focusedByDefault="true" app:headerLayout="@layout/header_in_game" app:menu="@menu/menu_in_game" tools:visibility="gone" /> diff --git a/src/android/app/src/main/res/layout/fragment_folders.xml b/src/android/app/src/main/res/layout/fragment_folders.xml index 74f2f3754..b5c7676d8 100644 --- a/src/android/app/src/main/res/layout/fragment_folders.xml +++ b/src/android/app/src/main/res/layout/fragment_folders.xml @@ -15,12 +15,14 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:fitsSystemWindows="true" + android:touchscreenBlocksFocus="false" app:liftOnScrollTargetViewId="@id/list_folders"> <com.google.android.material.appbar.MaterialToolbar android:id="@+id/toolbar_folders" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" + android:touchscreenBlocksFocus="false" app:navigationIcon="@drawable/ic_back" app:title="@string/game_folders" /> @@ -31,6 +33,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:clipToPadding="false" + android:defaultFocusHighlightEnabled="false" app:layout_behavior="@string/appbar_scrolling_view_behavior" /> </androidx.coordinatorlayout.widget.CoordinatorLayout> diff --git a/src/android/app/src/main/res/layout/fragment_game_info.xml b/src/android/app/src/main/res/layout/fragment_game_info.xml index 53af15787..f29e7e376 100644 --- a/src/android/app/src/main/res/layout/fragment_game_info.xml +++ b/src/android/app/src/main/res/layout/fragment_game_info.xml @@ -11,12 +11,14 @@ android:id="@+id/appbar_info" android:layout_width="match_parent" android:layout_height="wrap_content" + android:touchscreenBlocksFocus="false" android:fitsSystemWindows="true"> <com.google.android.material.appbar.MaterialToolbar android:id="@+id/toolbar_info" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" + android:touchscreenBlocksFocus="false" app:navigationIcon="@drawable/ic_back" /> </com.google.android.material.appbar.AppBarLayout> @@ -25,6 +27,7 @@ android:id="@+id/scroll_info" android:layout_width="match_parent" android:layout_height="wrap_content" + android:defaultFocusHighlightEnabled="false" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <LinearLayout diff --git a/src/android/app/src/main/res/layout/fragment_game_properties.xml b/src/android/app/src/main/res/layout/fragment_game_properties.xml index cadd0bc4a..436ebd79d 100644 --- a/src/android/app/src/main/res/layout/fragment_game_properties.xml +++ b/src/android/app/src/main/res/layout/fragment_game_properties.xml @@ -12,7 +12,8 @@ android:layout_height="match_parent" android:scrollbars="vertical" android:fadeScrollbars="false" - android:clipToPadding="false"> + android:clipToPadding="false" + android:defaultFocusHighlightEnabled="false"> <LinearLayout android:id="@+id/layout_all" @@ -86,7 +87,7 @@ android:id="@+id/list_properties" android:layout_width="match_parent" android:layout_height="match_parent" - tools:listitem="@layout/card_simple_outlined" /> + android:defaultFocusHighlightEnabled="false" /> </LinearLayout> diff --git a/src/android/app/src/main/res/layout/fragment_games.xml b/src/android/app/src/main/res/layout/fragment_games.xml index a0568668a..cc280b1ff 100644 --- a/src/android/app/src/main/res/layout/fragment_games.xml +++ b/src/android/app/src/main/res/layout/fragment_games.xml @@ -27,6 +27,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:clipToPadding="false" + android:defaultFocusHighlightEnabled="false" tools:listitem="@layout/card_game" /> </RelativeLayout> diff --git a/src/android/app/src/main/res/layout/fragment_home_settings.xml b/src/android/app/src/main/res/layout/fragment_home_settings.xml index d84093ba3..c179f9341 100644 --- a/src/android/app/src/main/res/layout/fragment_home_settings.xml +++ b/src/android/app/src/main/res/layout/fragment_home_settings.xml @@ -7,7 +7,8 @@ android:background="?attr/colorSurface" android:scrollbars="vertical" android:fadeScrollbars="false" - android:clipToPadding="false"> + android:clipToPadding="false" + android:defaultFocusHighlightEnabled="false"> <androidx.appcompat.widget.LinearLayoutCompat android:id="@+id/linear_layout_settings" diff --git a/src/android/app/src/main/res/layout/fragment_installables.xml b/src/android/app/src/main/res/layout/fragment_installables.xml index 3a4df81a6..47ef3869f 100644 --- a/src/android/app/src/main/res/layout/fragment_installables.xml +++ b/src/android/app/src/main/res/layout/fragment_installables.xml @@ -10,12 +10,14 @@ android:id="@+id/appbar_installables" android:layout_width="match_parent" android:layout_height="wrap_content" - android:fitsSystemWindows="true"> + android:fitsSystemWindows="true" + android:touchscreenBlocksFocus="false"> <com.google.android.material.appbar.MaterialToolbar android:id="@+id/toolbar_installables" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" + android:touchscreenBlocksFocus="false" app:title="@string/manage_yuzu_data" app:navigationIcon="@drawable/ic_back" /> diff --git a/src/android/app/src/main/res/layout/fragment_licenses.xml b/src/android/app/src/main/res/layout/fragment_licenses.xml index 6b31ff5b4..59d68b112 100644 --- a/src/android/app/src/main/res/layout/fragment_licenses.xml +++ b/src/android/app/src/main/res/layout/fragment_licenses.xml @@ -10,12 +10,14 @@ android:id="@+id/appbar_licenses" android:layout_width="match_parent" android:layout_height="wrap_content" - android:fitsSystemWindows="true"> + android:fitsSystemWindows="true" + android:touchscreenBlocksFocus="false"> <com.google.android.material.appbar.MaterialToolbar android:id="@+id/toolbar_licenses" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" + android:touchscreenBlocksFocus="false" app:title="@string/licenses" app:navigationIcon="@drawable/ic_back" /> diff --git a/src/android/app/src/main/res/layout/fragment_settings.xml b/src/android/app/src/main/res/layout/fragment_settings.xml index ebedbf1ec..110c70eef 100644 --- a/src/android/app/src/main/res/layout/fragment_settings.xml +++ b/src/android/app/src/main/res/layout/fragment_settings.xml @@ -11,6 +11,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:fitsSystemWindows="true" + android:touchscreenBlocksFocus="false" app:elevation="0dp"> <com.google.android.material.appbar.CollapsingToolbarLayout @@ -24,6 +25,7 @@ android:id="@+id/toolbar_settings" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" + android:touchscreenBlocksFocus="false" app:layout_collapseMode="pin" app:navigationIcon="@drawable/ic_back" /> diff --git a/src/android/app/src/main/res/layout/list_item_addon.xml b/src/android/app/src/main/res/layout/list_item_addon.xml index 3a1382fe2..9b1c0e6fc 100644 --- a/src/android/app/src/main/res/layout/list_item_addon.xml +++ b/src/android/app/src/main/res/layout/list_item_addon.xml @@ -6,7 +6,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/selectableItemBackground" - android:focusable="true" + android:focusable="false" android:paddingHorizontal="20dp" android:paddingVertical="16dp"> diff --git a/src/android/app/src/main/res/layout/list_item_settings_header.xml b/src/android/app/src/main/res/layout/list_item_settings_header.xml index 21276b19e..615860368 100644 --- a/src/android/app/src/main/res/layout/list_item_settings_header.xml +++ b/src/android/app/src/main/res/layout/list_item_settings_header.xml @@ -12,4 +12,5 @@ android:textAlignment="viewStart" android:textColor="?attr/colorPrimary" android:textStyle="bold" + android:focusable="false" tools:text="CPU Settings" /> diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 779eb36a8..3cd1586fd 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -144,6 +144,9 @@ <string name="no_save_data_found">No save data found</string> <string name="verify_installed_content">Verify installed content</string> <string name="verify_installed_content_description">Checks all installed content for corruption</string> + <string name="keys_missing">Encryption keys are missing</string> + <string name="keys_missing_description">Firmware and retail games cannot be decrypted</string> + <string name="keys_missing_help">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> <!-- Applet launcher strings --> <string name="applets">Applet launcher</string> diff --git a/src/common/arm64/native_clock.cpp b/src/common/arm64/native_clock.cpp index f437d7187..76ffb74ba 100644 --- a/src/common/arm64/native_clock.cpp +++ b/src/common/arm64/native_clock.cpp @@ -30,27 +30,27 @@ NativeClock::NativeClock() { } 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(), guest_cntfrq_factor); } -u64 NativeClock::GetGPUTick() const { - return MultiplyHigh(GetHostTicksElapsed(), gputick_cntfrq_factor); +s64 NativeClock::GetGPUTick() const { + return MultiplyHigh(GetUptime(), 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 +58,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..94bc1882e 100644 --- a/src/common/arm64/native_clock.h +++ b/src/common/arm64/native_clock.h @@ -17,17 +17,15 @@ public: 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; diff --git a/src/common/memory_detect.h b/src/common/memory_detect.h index a345e6d28..c8f239aed 100644 --- 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/overflow.h b/src/common/overflow.h index 44d8e7e73..e184ead95 100644 --- a/src/common/overflow.h +++ b/src/common/overflow.h @@ -3,6 +3,7 @@ #pragma once +#include <algorithm> #include <type_traits> #include "bit_cast.h" @@ -19,4 +20,21 @@ inline T WrappingAdd(T lhs, T rhs) { return BitCast<T>(lhs_u + rhs_u); } +template <typename T> + requires(std::is_integral_v<T> && std::is_signed_v<T>) +inline bool CanAddWithoutOverflow(T lhs, T rhs) { +#ifdef _MSC_VER + if (lhs >= 0 && rhs >= 0) { + return WrappingAdd(lhs, rhs) >= std::max(lhs, rhs); + } else if (lhs < 0 && rhs < 0) { + return WrappingAdd(lhs, rhs) <= std::min(lhs, rhs); + } else { + return true; + } +#else + T res; + return !__builtin_add_overflow(lhs, rhs, &res); +#endif +} + } // namespace Common diff --git a/src/common/settings.h b/src/common/settings.h index 07dba53ab..16749ab68 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -419,9 +419,16 @@ struct Values { linkage, false, "custom_rtc_enabled", Category::System, Specialization::Paired, true, true}; SwitchableSetting<s64> custom_rtc{ linkage, 0, "custom_rtc", Category::System, Specialization::Time, - true, true, &custom_rtc_enabled}; - // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc` - s64 custom_rtc_differential; + false, true, &custom_rtc_enabled}; + SwitchableSetting<s64, true> custom_rtc_offset{linkage, + 0, + std::numeric_limits<int>::min(), + std::numeric_limits<int>::max(), + "custom_rtc_offset", + Category::System, + Specialization::Countable, + true, + true}; SwitchableSetting<bool> rng_seed_enabled{ linkage, false, "rng_seed_enabled", Category::System, Specialization::Paired, true, true}; SwitchableSetting<u32> rng_seed{ diff --git a/src/common/time_zone.cpp b/src/common/time_zone.cpp index 69e728a9d..f77df604f 100644 --- a/src/common/time_zone.cpp +++ b/src/common/time_zone.cpp @@ -88,7 +88,17 @@ std::string FindSystemTimeZone() { LOG_ERROR(Common, "Time zone {} not handled, defaulting to hour offset.", tz_index); } } - return fmt::format("Etc/GMT{:s}{:d}", hours > 0 ? "-" : "+", std::abs(hours)); + + // For some reason the Etc/GMT times are reversed. GMT+6 contains -21600 as its offset, + // -6 hours instead of +6 hours, so these signs are purposefully reversed to fix it. + std::string postfix{""}; + if (hours > 0) { + postfix = fmt::format("-{:d}", std::abs(hours)); + } else if (hours < 0) { + postfix = fmt::format("+{:d}", std::abs(hours)); + } + + return fmt::format("Etc/GMT{:s}", postfix); } } // namespace Common::TimeZone diff --git a/src/common/uuid.h b/src/common/uuid.h index 7172ca165..81bfefbbb 100644 --- a/src/common/uuid.h +++ b/src/common/uuid.h @@ -12,9 +12,8 @@ namespace Common { struct UUID { - std::array<u8, 0x10> uuid{}; + std::array<u8, 0x10> 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<UUID>); /// 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 012fdc1e0..e14bf3e65 100644 --- a/src/common/wall_clock.cpp +++ b/src/common/wall_clock.cpp @@ -18,42 +18,40 @@ namespace Common { class StandardWallClock final : public WallClock { public: - explicit StandardWallClock() : start_time{SteadyClock::Now()} {} + explicit StandardWallClock() {} std::chrono::nanoseconds GetTimeNS() const override { - return SteadyClock::Now() - start_time; + return std::chrono::duration_cast<std::chrono::nanoseconds>( + std::chrono::system_clock::now().time_since_epoch()); } std::chrono::microseconds GetTimeUS() const override { - return static_cast<std::chrono::microseconds>(GetHostTicksElapsed() / NsToUsRatio::den); + return std::chrono::duration_cast<std::chrono::microseconds>( + std::chrono::system_clock::now().time_since_epoch()); } std::chrono::milliseconds GetTimeMS() const override { - return static_cast<std::chrono::milliseconds>(GetHostTicksElapsed() / NsToMsRatio::den); + return std::chrono::duration_cast<std::chrono::milliseconds>( + 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<u64>(SteadyClock::Now().time_since_epoch().count()); - } - - u64 GetHostTicksElapsed() const override { - return static_cast<u64>(GetTimeNS().count()); + s64 GetUptime() const override { + return std::chrono::duration_cast<std::chrono::nanoseconds>( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); } bool IsNative() const override { return false; } - -private: - SteadyClock::time_point start_time; }; std::unique_ptr<WallClock> CreateOptimalClock() { diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h index f45d3d8c5..3a0c43909 100644 --- a/src/common/wall_clock.h +++ b/src/common/wall_clock.h @@ -29,16 +29,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 7d2a26bd9..d2d27fafe 100644 --- a/src/common/x64/native_clock.cpp +++ b/src/common/x64/native_clock.cpp @@ -8,39 +8,35 @@ 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)} {} 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(), cntpct_rdtsc_factor); } -u64 NativeClock::GetGPUTick() const { - return MultiplyHigh(GetHostTicksElapsed(), gputick_rdtsc_factor); +s64 NativeClock::GetGPUTick() const { + return MultiplyHigh(GetUptime(), gputick_rdtsc_factor); } -u64 NativeClock::GetHostTicksNow() const { - return FencedRDTSC(); -} - -u64 NativeClock::GetHostTicksElapsed() const { - return FencedRDTSC() - start_ticks; +s64 NativeClock::GetUptime() const { + return static_cast<s64>(FencedRDTSC()); } bool NativeClock::IsNative() const { diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h index 334415eff..b2629b031 100644 --- a/src/common/x64/native_clock.h +++ b/src/common/x64/native_clock.h @@ -17,18 +17,15 @@ public: 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; private: - u64 start_ticks; u64 rdtsc_frequency; u64 ns_rdtsc_factor; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 4ff2c1bb7..347bbf2d0 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -20,28 +20,49 @@ add_library(core STATIC cpu_manager.h crypto/aes_util.cpp crypto/aes_util.h + crypto/ctr_encryption_layer.cpp + crypto/ctr_encryption_layer.h crypto/encryption_layer.cpp crypto/encryption_layer.h crypto/key_manager.cpp crypto/key_manager.h crypto/partition_data_manager.cpp crypto/partition_data_manager.h - crypto/ctr_encryption_layer.cpp - crypto/ctr_encryption_layer.h crypto/xts_encryption_layer.cpp crypto/xts_encryption_layer.h - debugger/debugger_interface.h debugger/debugger.cpp debugger/debugger.h - debugger/gdbstub_arch.cpp - debugger/gdbstub_arch.h + debugger/debugger_interface.h debugger/gdbstub.cpp debugger/gdbstub.h + debugger/gdbstub_arch.cpp + debugger/gdbstub_arch.h device_memory_manager.h device_memory_manager.inc device_memory.cpp device_memory.h + file_sys/bis_factory.cpp + file_sys/bis_factory.h + file_sys/card_image.cpp + file_sys/card_image.h + file_sys/common_funcs.h + file_sys/content_archive.cpp + file_sys/content_archive.h + file_sys/control_metadata.cpp + file_sys/control_metadata.h + file_sys/errors.h + file_sys/fs_directory.h + file_sys/fs_file.h + file_sys/fs_filesystem.h + file_sys/fs_memory_management.h + file_sys/fs_operate_range.h + file_sys/fs_path.h + file_sys/fs_path_utility.h + file_sys/fs_string_util.h + file_sys/fsmitm_romfsbuild.cpp + file_sys/fsmitm_romfsbuild.h file_sys/fssystem/fs_i_storage.h + file_sys/fssystem/fs_types.h file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.h file_sys/fssystem/fssystem_aes_ctr_storage.cpp @@ -83,25 +104,10 @@ add_library(core STATIC file_sys/fssystem/fssystem_switch_storage.h file_sys/fssystem/fssystem_utility.cpp file_sys/fssystem/fssystem_utility.h - file_sys/fssystem/fs_types.h - file_sys/bis_factory.cpp - file_sys/bis_factory.h - file_sys/card_image.cpp - file_sys/card_image.h - file_sys/common_funcs.h - file_sys/content_archive.cpp - file_sys/content_archive.h - file_sys/control_metadata.cpp - file_sys/control_metadata.h - file_sys/directory.h - file_sys/errors.h - file_sys/fsmitm_romfsbuild.cpp - file_sys/fsmitm_romfsbuild.h file_sys/ips_layer.cpp file_sys/ips_layer.h file_sys/kernel_executable.cpp file_sys/kernel_executable.h - file_sys/mode.h file_sys/nca_metadata.cpp file_sys/nca_metadata.h file_sys/partition_filesystem.cpp @@ -146,22 +152,22 @@ add_library(core STATIC file_sys/system_archive/system_version.h file_sys/system_archive/time_zone_binary.cpp file_sys/system_archive/time_zone_binary.h - file_sys/vfs.cpp - file_sys/vfs.h - file_sys/vfs_cached.cpp - file_sys/vfs_cached.h - file_sys/vfs_concat.cpp - file_sys/vfs_concat.h - file_sys/vfs_layered.cpp - file_sys/vfs_layered.h - file_sys/vfs_offset.cpp - file_sys/vfs_offset.h - file_sys/vfs_real.cpp - file_sys/vfs_real.h - file_sys/vfs_static.h - file_sys/vfs_types.h - file_sys/vfs_vector.cpp - file_sys/vfs_vector.h + file_sys/vfs/vfs.cpp + file_sys/vfs/vfs.h + file_sys/vfs/vfs_cached.cpp + file_sys/vfs/vfs_cached.h + file_sys/vfs/vfs_concat.cpp + file_sys/vfs/vfs_concat.h + file_sys/vfs/vfs_layered.cpp + file_sys/vfs/vfs_layered.h + file_sys/vfs/vfs_offset.cpp + file_sys/vfs/vfs_offset.h + file_sys/vfs/vfs_real.cpp + file_sys/vfs/vfs_real.h + file_sys/vfs/vfs_static.h + file_sys/vfs/vfs_types.h + file_sys/vfs/vfs_vector.cpp + file_sys/vfs/vfs_vector.h file_sys/xts_archive.cpp file_sys/xts_archive.h frontend/applets/cabinet.cpp @@ -194,7 +200,6 @@ add_library(core STATIC hle/kernel/board/nintendo/nx/secure_monitor.h hle/kernel/code_set.cpp hle/kernel/code_set.h - hle/kernel/svc_results.h hle/kernel/global_scheduler_context.cpp hle/kernel/global_scheduler_context.h hle/kernel/init/init_slab_setup.cpp @@ -204,11 +209,11 @@ add_library(core STATIC hle/kernel/k_address_arbiter.h hle/kernel/k_address_space_info.cpp hle/kernel/k_address_space_info.h + hle/kernel/k_affinity_mask.h hle/kernel/k_auto_object.cpp hle/kernel/k_auto_object.h hle/kernel/k_auto_object_container.cpp hle/kernel/k_auto_object_container.h - hle/kernel/k_affinity_mask.h hle/kernel/k_capabilities.cpp hle/kernel/k_capabilities.h hle/kernel/k_class_token.cpp @@ -232,9 +237,9 @@ add_library(core STATIC hle/kernel/k_event_info.h hle/kernel/k_handle_table.cpp hle/kernel/k_handle_table.h - hle/kernel/k_hardware_timer_base.h hle/kernel/k_hardware_timer.cpp hle/kernel/k_hardware_timer.h + hle/kernel/k_hardware_timer_base.h hle/kernel/k_interrupt_manager.cpp hle/kernel/k_interrupt_manager.h hle/kernel/k_light_client_session.cpp @@ -261,10 +266,10 @@ add_library(core STATIC hle/kernel/k_page_bitmap.h hle/kernel/k_page_buffer.cpp hle/kernel/k_page_buffer.h - hle/kernel/k_page_heap.cpp - hle/kernel/k_page_heap.h hle/kernel/k_page_group.cpp hle/kernel/k_page_group.h + hle/kernel/k_page_heap.cpp + hle/kernel/k_page_heap.h hle/kernel/k_page_table.h hle/kernel/k_page_table_base.cpp hle/kernel/k_page_table_base.h @@ -329,8 +334,6 @@ add_library(core STATIC hle/kernel/slab_helpers.h hle/kernel/svc.cpp hle/kernel/svc.h - hle/kernel/svc_common.h - hle/kernel/svc_types.h hle/kernel/svc/svc_activity.cpp hle/kernel/svc/svc_address_arbiter.cpp hle/kernel/svc/svc_address_translation.cpp @@ -368,6 +371,9 @@ add_library(core STATIC hle/kernel/svc/svc_thread_profiler.cpp hle/kernel/svc/svc_tick.cpp hle/kernel/svc/svc_transfer_memory.cpp + hle/kernel/svc_common.h + hle/kernel/svc_results.h + hle/kernel/svc_types.h hle/result.h hle/service/acc/acc.cpp hle/service/acc/acc.h @@ -472,6 +478,8 @@ add_library(core STATIC hle/service/caps/caps_types.h hle/service/caps/caps_u.cpp hle/service/caps/caps_u.h + hle/service/cmif_serialization.h + hle/service/cmif_types.h hle/service/erpt/erpt.cpp hle/service/erpt/erpt.h hle/service/es/es.cpp @@ -484,14 +492,25 @@ add_library(core STATIC hle/service/fatal/fatal_p.h hle/service/fatal/fatal_u.cpp hle/service/fatal/fatal_u.h + hle/service/fgm/fgm.cpp + hle/service/fgm/fgm.h hle/service/filesystem/filesystem.cpp hle/service/filesystem/filesystem.h - hle/service/filesystem/fsp_ldr.cpp - hle/service/filesystem/fsp_ldr.h - hle/service/filesystem/fsp_pr.cpp - hle/service/filesystem/fsp_pr.h - hle/service/filesystem/fsp_srv.cpp - hle/service/filesystem/fsp_srv.h + hle/service/filesystem/fsp/fs_i_directory.cpp + hle/service/filesystem/fsp/fs_i_directory.h + hle/service/filesystem/fsp/fs_i_file.cpp + hle/service/filesystem/fsp/fs_i_file.h + hle/service/filesystem/fsp/fs_i_filesystem.cpp + hle/service/filesystem/fsp/fs_i_filesystem.h + hle/service/filesystem/fsp/fs_i_storage.cpp + hle/service/filesystem/fsp/fs_i_storage.h + hle/service/filesystem/fsp/fsp_ldr.cpp + hle/service/filesystem/fsp/fsp_ldr.h + hle/service/filesystem/fsp/fsp_pr.cpp + hle/service/filesystem/fsp/fsp_pr.h + hle/service/filesystem/fsp/fsp_srv.cpp + hle/service/filesystem/fsp/fsp_srv.h + hle/service/filesystem/fsp/fsp_util.h hle/service/filesystem/romfs_controller.cpp hle/service/filesystem/romfs_controller.h hle/service/filesystem/save_data_controller.cpp @@ -515,6 +534,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 @@ -531,13 +568,18 @@ add_library(core STATIC hle/service/hid/irs.h hle/service/hid/xcd.cpp hle/service/hid/xcd.h + hle/service/hle_ipc.cpp + hle/service/hle_ipc.h + hle/service/ipc_helpers.h + hle/service/kernel_helpers.cpp + hle/service/kernel_helpers.h hle/service/lbl/lbl.cpp hle/service/lbl/lbl.h hle/service/ldn/lan_discovery.cpp hle/service/ldn/lan_discovery.h - hle/service/ldn/ldn_results.h hle/service/ldn/ldn.cpp hle/service/ldn/ldn.h + hle/service/ldn/ldn_results.h hle/service/ldn/ldn_types.h hle/service/ldr/ldr.cpp hle/service/ldr/ldr.h @@ -545,16 +587,6 @@ add_library(core STATIC hle/service/lm/lm.h hle/service/mig/mig.cpp hle/service/mig/mig.h - hle/service/mii/types/char_info.cpp - hle/service/mii/types/char_info.h - hle/service/mii/types/core_data.cpp - hle/service/mii/types/core_data.h - hle/service/mii/types/raw_data.cpp - hle/service/mii/types/raw_data.h - hle/service/mii/types/store_data.cpp - hle/service/mii/types/store_data.h - hle/service/mii/types/ver3_store_data.cpp - hle/service/mii/types/ver3_store_data.h hle/service/mii/mii.cpp hle/service/mii/mii.h hle/service/mii/mii_database.cpp @@ -566,10 +598,22 @@ add_library(core STATIC hle/service/mii/mii_result.h hle/service/mii/mii_types.h hle/service/mii/mii_util.h + hle/service/mii/types/char_info.cpp + hle/service/mii/types/char_info.h + hle/service/mii/types/core_data.cpp + hle/service/mii/types/core_data.h + hle/service/mii/types/raw_data.cpp + hle/service/mii/types/raw_data.h + hle/service/mii/types/store_data.cpp + hle/service/mii/types/store_data.h + hle/service/mii/types/ver3_store_data.cpp + hle/service/mii/types/ver3_store_data.h hle/service/mm/mm_u.cpp hle/service/mm/mm_u.h hle/service/mnpp/mnpp_app.cpp hle/service/mnpp/mnpp_app.h + hle/service/mutex.cpp + hle/service/mutex.h hle/service/ncm/ncm.cpp hle/service/ncm/ncm.h hle/service/nfc/common/amiibo_crypto.cpp @@ -693,25 +737,58 @@ 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 hle/service/ptm/ptm.h hle/service/ptm/ts.cpp hle/service/ptm/ts.h - hle/service/hle_ipc.cpp - hle/service/hle_ipc.h - hle/service/ipc_helpers.h - hle/service/kernel_helpers.cpp - hle/service/kernel_helpers.h - hle/service/mutex.cpp - hle/service/mutex.h + hle/service/ro/ro.cpp + hle/service/ro/ro.h hle/service/ro/ro_nro_utils.cpp hle/service/ro/ro_nro_utils.h hle/service/ro/ro_results.h hle/service/ro/ro_types.h - hle/service/ro/ro.cpp - hle/service/ro/ro.h hle/service/server_manager.cpp hle/service/server_manager.h hle/service/service.cpp @@ -760,40 +837,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 @@ -812,9 +855,9 @@ add_library(core STATIC internal_network/network.h internal_network/network_interface.cpp internal_network/network_interface.h - internal_network/sockets.h internal_network/socket_proxy.cpp internal_network/socket_proxy.h + internal_network/sockets.h loader/deconstructed_rom_directory.cpp loader/deconstructed_rom_directory.h loader/kip.cpp @@ -833,13 +876,13 @@ add_library(core STATIC loader/nsp.h loader/xci.cpp loader/xci.h + memory.cpp + memory.h memory/cheat_engine.cpp memory/cheat_engine.h memory/dmnt_cheat_types.h memory/dmnt_cheat_vm.cpp memory/dmnt_cheat_vm.h - memory.cpp - memory.h perf_stats.cpp perf_stats.h precompiled_headers.h @@ -874,7 +917,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 2392fe136..1b412ac98 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -21,13 +21,13 @@ #include "core/debugger/debugger.h" #include "core/device_memory.h" #include "core/file_sys/bis_factory.h" -#include "core/file_sys/mode.h" +#include "core/file_sys/fs_filesystem.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" #include "core/file_sys/romfs_factory.h" #include "core/file_sys/savedata_factory.h" -#include "core/file_sys/vfs_concat.h" -#include "core/file_sys/vfs_real.h" +#include "core/file_sys/vfs/vfs_concat.h" +#include "core/file_sys/vfs/vfs_real.h" #include "core/gpu_dirty_memory_manager.h" #include "core/hle/kernel/k_memory_manager.h" #include "core/hle/kernel/k_process.h" @@ -40,9 +40,14 @@ #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/psc/time/time_zone_service.h" #include "core/hle/service/service.h" +#include "core/hle/service/set/system_settings_server.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" @@ -97,7 +102,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, Common::SplitPath(path, &dir_name, &filename, nullptr); if (filename == "00") { - const auto dir = vfs->OpenDirectory(dir_name, FileSys::Mode::Read); + const auto dir = vfs->OpenDirectory(dir_name, FileSys::OpenMode::Read); std::vector<FileSys::VirtualFile> concat; for (u32 i = 0; i < 0x10; ++i) { @@ -122,16 +127,16 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, } if (Common::FS::IsDir(path)) { - return vfs->OpenFile(path + "/main", FileSys::Mode::Read); + return vfs->OpenFile(path + "/main", FileSys::OpenMode::Read); } - return vfs->OpenFile(path, FileSys::Mode::Read); + return vfs->OpenFile(path, FileSys::OpenMode::Read); } 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<Core::DeviceMemory>(); @@ -143,8 +148,6 @@ struct System::Impl { core_timing.SetMulticore(is_multicore); core_timing.Initialize([&system]() { system.RegisterHostThread(); }); - RefreshTime(); - // Create a default fs if one doesn't already exist. if (virtual_filesystem == nullptr) { virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); @@ -182,14 +185,57 @@ struct System::Impl { Initialize(system); } - void RefreshTime() { + void RefreshTime(System& system) { + if (!system.IsPoweredOn()) { + return; + } + + auto settings_service = + system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", + true); + auto static_service_a = + system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:a", true); + + auto static_service_s = + system.ServiceManager().GetService<Service::PSC::Time::StaticService>("time:s", true); + + std::shared_ptr<Service::PSC::Time::SystemClock> user_clock; + static_service_a->GetStandardUserSystemClock(user_clock); + + std::shared_ptr<Service::PSC::Time::SystemClock> local_clock; + static_service_a->GetStandardLocalSystemClock(local_clock); + + std::shared_ptr<Service::PSC::Time::SystemClock> network_clock; + static_service_s->GetStandardNetworkSystemClock(network_clock); + + std::shared_ptr<Service::Glue::Time::TimeZoneService> timezone_service; + static_service_a->GetTimeZoneService(timezone_service); + + Service::PSC::Time::LocationName name{}; + auto new_name = Settings::GetTimeZoneString(Settings::values.time_zone_index.GetValue()); + std::memcpy(name.name.data(), new_name.data(), std::min(name.name.size(), new_name.size())); + + timezone_service->SetDeviceLocation(name); + + u64 time_offset = 0; + if (Settings::values.custom_rtc_enabled) { + time_offset = Settings::values.custom_rtc_offset.GetValue(); + } + const auto posix_time = std::chrono::system_clock::now().time_since_epoch(); - const auto current_time = - std::chrono::duration_cast<std::chrono::seconds>(posix_time).count(); - Settings::values.custom_rtc_differential = - (Settings::values.custom_rtc_enabled ? Settings::values.custom_rtc.GetValue() - : current_time) - - current_time; + const u64 current_time = + +std::chrono::duration_cast<std::chrono::seconds>(posix_time).count(); + const u64 new_time = current_time + time_offset; + + Service::PSC::Time::SystemClockContext context{}; + settings_service->SetUserSystemClockContext(context); + user_clock->SetCurrentTime(new_time); + + local_clock->SetCurrentTime(new_time); + + network_clock->GetSystemClockContext(context); + settings_service->SetNetworkSystemClockContext(context); + network_clock->SetCurrentTime(new_time); } void Run() { @@ -265,9 +311,6 @@ struct System::Impl { service_manager = std::make_shared<Service::SM::ServiceManager>(kernel); services = std::make_unique<Service::Services>(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; @@ -417,7 +460,6 @@ struct System::Impl { fs_controller.Reset(); cheat_engine.reset(); telemetry_session.reset(); - time_manager.Shutdown(); core_timing.ClearPendingEvents(); app_loader.reset(); audio_core.reset(); @@ -533,7 +575,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::SM::ServiceManager> service_manager; @@ -911,14 +952,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; } @@ -1030,13 +1063,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 80446f385..d8862e9ce 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -13,7 +13,7 @@ #include <vector> #include "common/common_types.h" -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" namespace Core::Frontend { class EmuWindow; @@ -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 fc536413b..1abfa920c 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -157,7 +157,7 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, } } - for (auto h : to_remove) { + for (auto& h : to_remove) { event_queue.erase(h); } diff --git a/src/core/crypto/aes_util.h b/src/core/crypto/aes_util.h index a67ba5352..c2fd587a7 100644 --- a/src/core/crypto/aes_util.h +++ b/src/core/crypto/aes_util.h @@ -7,7 +7,7 @@ #include <span> #include <type_traits> #include "common/common_types.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace Core::Crypto { diff --git a/src/core/crypto/encryption_layer.h b/src/core/crypto/encryption_layer.h index d3082ba53..b53f0b12e 100644 --- a/src/core/crypto/encryption_layer.h +++ b/src/core/crypto/encryption_layer.h @@ -4,7 +4,7 @@ #pragma once #include "common/common_types.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace Core::Crypto { diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp index 97f5c8cea..4b45e72c4 100644 --- a/src/core/crypto/partition_data_manager.cpp +++ b/src/core/crypto/partition_data_manager.cpp @@ -21,9 +21,9 @@ #include "core/crypto/partition_data_manager.h" #include "core/crypto/xts_encryption_layer.h" #include "core/file_sys/kernel_executable.h" -#include "core/file_sys/vfs.h" -#include "core/file_sys/vfs_offset.h" -#include "core/file_sys/vfs_vector.h" +#include "core/file_sys/vfs/vfs.h" +#include "core/file_sys/vfs/vfs_offset.h" +#include "core/file_sys/vfs/vfs_vector.h" #include "core/loader/loader.h" using Common::AsArray; diff --git a/src/core/crypto/partition_data_manager.h b/src/core/crypto/partition_data_manager.h index 057a70683..4354a21e6 100644 --- a/src/core/crypto/partition_data_manager.h +++ b/src/core/crypto/partition_data_manager.h @@ -5,7 +5,7 @@ #include <vector> #include "common/common_types.h" -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" namespace Core::Crypto { diff --git a/src/core/device_memory_manager.inc b/src/core/device_memory_manager.inc index 8ce122872..eab8a2731 100644 --- a/src/core/device_memory_manager.inc +++ b/src/core/device_memory_manager.inc @@ -31,9 +31,8 @@ public: buffer.resize(0); size_t index = 0; const auto add_value = [&](u32 value) { - buffer[index] = value; - index++; - buffer.resize(index); + buffer.resize(index + 1); + buffer[index++] = value; }; u32 iter_entry = start_entry; diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp index c750c0da7..db667438e 100644 --- a/src/core/file_sys/bis_factory.cpp +++ b/src/core/file_sys/bis_factory.cpp @@ -4,9 +4,8 @@ #include <fmt/format.h> #include "common/fs/path_util.h" #include "core/file_sys/bis_factory.h" -#include "core/file_sys/mode.h" #include "core/file_sys/registered_cache.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { @@ -84,7 +83,7 @@ VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id, VirtualFilesystem file_system) const { auto& keys = Core::Crypto::KeyManager::Instance(); Core::Crypto::PartitionDataManager pdm{file_system->OpenDirectory( - Common::FS::GetYuzuPathString(Common::FS::YuzuPath::NANDDir), Mode::Read)}; + Common::FS::GetYuzuPathString(Common::FS::YuzuPath::NANDDir), OpenMode::Read)}; keys.PopulateFromPartitionData(pdm); switch (id) { diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h index 26f0c6e5e..23680b60c 100644 --- a/src/core/file_sys/bis_factory.h +++ b/src/core/file_sys/bis_factory.h @@ -6,7 +6,7 @@ #include <memory> #include "common/common_types.h" -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" namespace FileSys { diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index 8b9a4fc5a..0bcf40cf8 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp @@ -13,8 +13,8 @@ #include "core/file_sys/nca_metadata.h" #include "core/file_sys/partition_filesystem.h" #include "core/file_sys/submission_package.h" -#include "core/file_sys/vfs_offset.h" -#include "core/file_sys/vfs_vector.h" +#include "core/file_sys/vfs/vfs_offset.h" +#include "core/file_sys/vfs/vfs_vector.h" #include "core/loader/loader.h" namespace FileSys { diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h index 9886123e7..97871da4a 100644 --- a/src/core/file_sys/card_image.h +++ b/src/core/file_sys/card_image.h @@ -8,7 +8,7 @@ #include <vector> #include "common/common_types.h" #include "common/swap.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace Core::Crypto { class KeyManager; diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 7d2f0abb8..285fe4db6 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -13,7 +13,7 @@ #include "core/crypto/key_manager.h" #include "core/file_sys/content_archive.h" #include "core/file_sys/partition_filesystem.h" -#include "core/file_sys/vfs_offset.h" +#include "core/file_sys/vfs/vfs_offset.h" #include "core/loader/loader.h" #include "core/file_sys/fssystem/fssystem_compression_configuration.h" diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index af521d453..f68464eb0 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h @@ -13,7 +13,7 @@ #include "common/common_types.h" #include "common/swap.h" #include "core/crypto/key_manager.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace Loader { enum class ResultStatus : u16; diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp index 0697c29ae..f98594335 100644 --- a/src/core/file_sys/control_metadata.cpp +++ b/src/core/file_sys/control_metadata.cpp @@ -5,7 +5,7 @@ #include "common/string_util.h" #include "common/swap.h" #include "core/file_sys/control_metadata.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h index c98efb00d..555b9d8f7 100644 --- a/src/core/file_sys/control_metadata.h +++ b/src/core/file_sys/control_metadata.h @@ -8,7 +8,7 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" namespace FileSys { diff --git a/src/core/file_sys/directory.h b/src/core/file_sys/directory.h deleted file mode 100644 index a853c00f3..000000000 --- a/src/core/file_sys/directory.h +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <cstddef> -#include "common/common_funcs.h" -#include "common/common_types.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// FileSys namespace - -namespace FileSys { - -enum class EntryType : u8 { - Directory = 0, - File = 1, -}; - -// Structure of a directory entry, from -// http://switchbrew.org/index.php?title=Filesystem_services#DirectoryEntry -struct Entry { - Entry(std::string_view view, EntryType entry_type, u64 entry_size) - : type{entry_type}, file_size{entry_size} { - const std::size_t copy_size = view.copy(filename, std::size(filename) - 1); - filename[copy_size] = '\0'; - } - - char filename[0x301]; - INSERT_PADDING_BYTES(3); - EntryType type; - INSERT_PADDING_BYTES(3); - u64 file_size; -}; -static_assert(sizeof(Entry) == 0x310, "Directory Entry struct isn't exactly 0x310 bytes long!"); -static_assert(offsetof(Entry, type) == 0x304, "Wrong offset for type in Entry."); -static_assert(offsetof(Entry, file_size) == 0x308, "Wrong offset for file_size in Entry."); - -} // namespace FileSys diff --git a/src/core/file_sys/errors.h b/src/core/file_sys/errors.h index 2f5045a67..d4e0eb6f4 100644 --- a/src/core/file_sys/errors.h +++ b/src/core/file_sys/errors.h @@ -7,18 +7,13 @@ namespace FileSys { -constexpr Result ERROR_PATH_NOT_FOUND{ErrorModule::FS, 1}; -constexpr Result ERROR_PATH_ALREADY_EXISTS{ErrorModule::FS, 2}; -constexpr Result ERROR_ENTITY_NOT_FOUND{ErrorModule::FS, 1002}; -constexpr Result ERROR_SD_CARD_NOT_FOUND{ErrorModule::FS, 2001}; -constexpr Result ERROR_OUT_OF_BOUNDS{ErrorModule::FS, 3005}; -constexpr Result ERROR_FAILED_MOUNT_ARCHIVE{ErrorModule::FS, 3223}; -constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::FS, 6001}; -constexpr Result ERROR_INVALID_OFFSET{ErrorModule::FS, 6061}; -constexpr Result ERROR_INVALID_SIZE{ErrorModule::FS, 6062}; - +constexpr Result ResultPathNotFound{ErrorModule::FS, 1}; +constexpr Result ResultPathAlreadyExists{ErrorModule::FS, 2}; constexpr Result ResultUnsupportedSdkVersion{ErrorModule::FS, 50}; constexpr Result ResultPartitionNotFound{ErrorModule::FS, 1001}; +constexpr Result ResultTargetNotFound{ErrorModule::FS, 1002}; +constexpr Result ResultPortSdCardNoDevice{ErrorModule::FS, 2001}; +constexpr Result ResultNotImplemented{ErrorModule::FS, 3001}; constexpr Result ResultUnsupportedVersion{ErrorModule::FS, 3002}; constexpr Result ResultOutOfRange{ErrorModule::FS, 3005}; constexpr Result ResultAllocationMemoryFailedInFileSystemBuddyHeapA{ErrorModule::FS, 3294}; @@ -78,10 +73,21 @@ constexpr Result ResultUnexpectedInCompressedStorageA{ErrorModule::FS, 5324}; constexpr Result ResultUnexpectedInCompressedStorageB{ErrorModule::FS, 5325}; constexpr Result ResultUnexpectedInCompressedStorageC{ErrorModule::FS, 5326}; constexpr Result ResultUnexpectedInCompressedStorageD{ErrorModule::FS, 5327}; +constexpr Result ResultUnexpectedInPathA{ErrorModule::FS, 5328}; constexpr Result ResultInvalidArgument{ErrorModule::FS, 6001}; +constexpr Result ResultInvalidPath{ErrorModule::FS, 6002}; +constexpr Result ResultTooLongPath{ErrorModule::FS, 6003}; +constexpr Result ResultInvalidCharacter{ErrorModule::FS, 6004}; +constexpr Result ResultInvalidPathFormat{ErrorModule::FS, 6005}; +constexpr Result ResultDirectoryUnobtainable{ErrorModule::FS, 6006}; +constexpr Result ResultNotNormalized{ErrorModule::FS, 6007}; constexpr Result ResultInvalidOffset{ErrorModule::FS, 6061}; constexpr Result ResultInvalidSize{ErrorModule::FS, 6062}; constexpr Result ResultNullptrArgument{ErrorModule::FS, 6063}; +constexpr Result ResultInvalidOpenMode{ErrorModule::FS, 6072}; +constexpr Result ResultFileExtensionWithoutOpenModeAllowAppend{ErrorModule::FS, 6201}; +constexpr Result ResultReadNotPermitted{ErrorModule::FS, 6202}; +constexpr Result ResultWriteNotPermitted{ErrorModule::FS, 6203}; constexpr Result ResultUnsupportedSetSizeForIndirectStorage{ErrorModule::FS, 6325}; constexpr Result ResultUnsupportedWriteForCompressedStorage{ErrorModule::FS, 6387}; constexpr Result ResultUnsupportedOperateRangeForCompressedStorage{ErrorModule::FS, 6388}; diff --git a/src/core/file_sys/fs_directory.h b/src/core/file_sys/fs_directory.h new file mode 100644 index 000000000..25c9cb18a --- /dev/null +++ b/src/core/file_sys/fs_directory.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +namespace FileSys { + +constexpr inline size_t EntryNameLengthMax = 0x300; + +struct DirectoryEntry { + DirectoryEntry(std::string_view view, s8 entry_type, u64 entry_size) + : type{entry_type}, file_size{static_cast<s64>(entry_size)} { + const std::size_t copy_size = view.copy(name, std::size(name) - 1); + name[copy_size] = '\0'; + } + + char name[EntryNameLengthMax + 1]; + INSERT_PADDING_BYTES(3); + s8 type; + INSERT_PADDING_BYTES(3); + s64 file_size; +}; + +static_assert(sizeof(DirectoryEntry) == 0x310, + "Directory Entry struct isn't exactly 0x310 bytes long!"); +static_assert(offsetof(DirectoryEntry, type) == 0x304, "Wrong offset for type in Entry."); +static_assert(offsetof(DirectoryEntry, file_size) == 0x308, "Wrong offset for file_size in Entry."); + +struct DirectoryHandle { + void* handle; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/fs_file.h b/src/core/file_sys/fs_file.h new file mode 100644 index 000000000..4fb77e8db --- /dev/null +++ b/src/core/file_sys/fs_file.h @@ -0,0 +1,65 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" + +namespace FileSys { + +struct ReadOption { + u32 value; + + static const ReadOption None; +}; + +enum ReadOptionFlag : u32 { + ReadOptionFlag_None = (0 << 0), +}; + +inline constexpr const ReadOption ReadOption::None = {ReadOptionFlag_None}; + +inline constexpr bool operator==(const ReadOption& lhs, const ReadOption& rhs) { + return lhs.value == rhs.value; +} + +inline constexpr bool operator!=(const ReadOption& lhs, const ReadOption& rhs) { + return !(lhs == rhs); +} + +static_assert(sizeof(ReadOption) == sizeof(u32)); + +enum WriteOptionFlag : u32 { + WriteOptionFlag_None = (0 << 0), + WriteOptionFlag_Flush = (1 << 0), +}; + +struct WriteOption { + u32 value; + + constexpr inline bool HasFlushFlag() const { + return value & WriteOptionFlag_Flush; + } + + static const WriteOption None; + static const WriteOption Flush; +}; + +inline constexpr const WriteOption WriteOption::None = {WriteOptionFlag_None}; +inline constexpr const WriteOption WriteOption::Flush = {WriteOptionFlag_Flush}; + +inline constexpr bool operator==(const WriteOption& lhs, const WriteOption& rhs) { + return lhs.value == rhs.value; +} + +inline constexpr bool operator!=(const WriteOption& lhs, const WriteOption& rhs) { + return !(lhs == rhs); +} + +static_assert(sizeof(WriteOption) == sizeof(u32)); + +struct FileHandle { + void* handle; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/fs_filesystem.h b/src/core/file_sys/fs_filesystem.h new file mode 100644 index 000000000..7f237b7fa --- /dev/null +++ b/src/core/file_sys/fs_filesystem.h @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" + +namespace FileSys { + +enum class OpenMode : u32 { + Read = (1 << 0), + Write = (1 << 1), + AllowAppend = (1 << 2), + + ReadWrite = (Read | Write), + All = (ReadWrite | AllowAppend), +}; +DECLARE_ENUM_FLAG_OPERATORS(OpenMode) + +enum class OpenDirectoryMode : u64 { + Directory = (1 << 0), + File = (1 << 1), + + All = (Directory | File), +}; +DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode) + +enum class DirectoryEntryType : u8 { + Directory = 0, + File = 1, +}; + +enum class CreateOption : u8 { + None = (0 << 0), + BigFile = (1 << 0), +}; + +} // namespace FileSys diff --git a/src/core/file_sys/fs_memory_management.h b/src/core/file_sys/fs_memory_management.h new file mode 100644 index 000000000..f03c6354b --- /dev/null +++ b/src/core/file_sys/fs_memory_management.h @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <mutex> +#include "common/alignment.h" + +namespace FileSys { + +constexpr size_t RequiredAlignment = alignof(u64); + +void* AllocateUnsafe(size_t size) { + // Allocate + void* const ptr = ::operator new(size, std::align_val_t{RequiredAlignment}); + + // Check alignment + ASSERT(Common::IsAligned(reinterpret_cast<uintptr_t>(ptr), RequiredAlignment)); + + // Return allocated pointer + return ptr; +} + +void DeallocateUnsafe(void* ptr, size_t size) { + // Deallocate the pointer + ::operator delete(ptr, std::align_val_t{RequiredAlignment}); +} + +void* Allocate(size_t size) { + return AllocateUnsafe(size); +} + +void Deallocate(void* ptr, size_t size) { + // If the pointer is non-null, deallocate it + if (ptr != nullptr) { + DeallocateUnsafe(ptr, size); + } +} + +} // namespace FileSys diff --git a/src/core/file_sys/fs_operate_range.h b/src/core/file_sys/fs_operate_range.h new file mode 100644 index 000000000..04ea64cc0 --- /dev/null +++ b/src/core/file_sys/fs_operate_range.h @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" + +namespace FileSys { + +enum class OperationId : s64 { + FillZero = 0, + DestroySignature = 1, + Invalidate = 2, + QueryRange = 3, + QueryUnpreparedRange = 4, + QueryLazyLoadCompletionRate = 5, + SetLazyLoadPriority = 6, + + ReadLazyLoadFileForciblyForDebug = 10001, +}; + +} // namespace FileSys diff --git a/src/core/file_sys/fs_path.h b/src/core/file_sys/fs_path.h new file mode 100644 index 000000000..56ba08a6a --- /dev/null +++ b/src/core/file_sys/fs_path.h @@ -0,0 +1,566 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/alignment.h" +#include "common/common_funcs.h" +#include "core/file_sys/errors.h" +#include "core/file_sys/fs_memory_management.h" +#include "core/file_sys/fs_path_utility.h" +#include "core/file_sys/fs_string_util.h" +#include "core/hle/result.h" + +namespace FileSys { +class DirectoryPathParser; + +class Path { + YUZU_NON_COPYABLE(Path); + YUZU_NON_MOVEABLE(Path); + +private: + static constexpr const char* EmptyPath = ""; + static constexpr size_t WriteBufferAlignmentLength = 8; + +private: + friend class DirectoryPathParser; + +public: + class WriteBuffer { + YUZU_NON_COPYABLE(WriteBuffer); + + private: + char* m_buffer; + size_t m_length_and_is_normalized; + + public: + constexpr WriteBuffer() : m_buffer(nullptr), m_length_and_is_normalized(0) {} + + constexpr ~WriteBuffer() { + if (m_buffer != nullptr) { + Deallocate(m_buffer, this->GetLength()); + this->ResetBuffer(); + } + } + + constexpr WriteBuffer(WriteBuffer&& rhs) + : m_buffer(rhs.m_buffer), m_length_and_is_normalized(rhs.m_length_and_is_normalized) { + rhs.ResetBuffer(); + } + + constexpr WriteBuffer& operator=(WriteBuffer&& rhs) { + if (m_buffer != nullptr) { + Deallocate(m_buffer, this->GetLength()); + } + + m_buffer = rhs.m_buffer; + m_length_and_is_normalized = rhs.m_length_and_is_normalized; + + rhs.ResetBuffer(); + + return *this; + } + + constexpr void ResetBuffer() { + m_buffer = nullptr; + this->SetLength(0); + } + + constexpr char* Get() const { + return m_buffer; + } + + constexpr size_t GetLength() const { + return m_length_and_is_normalized >> 1; + } + + constexpr bool IsNormalized() const { + return static_cast<bool>(m_length_and_is_normalized & 1); + } + + constexpr void SetNormalized() { + m_length_and_is_normalized |= static_cast<size_t>(1); + } + + constexpr void SetNotNormalized() { + m_length_and_is_normalized &= ~static_cast<size_t>(1); + } + + private: + constexpr WriteBuffer(char* buffer, size_t length) + : m_buffer(buffer), m_length_and_is_normalized(0) { + this->SetLength(length); + } + + public: + static WriteBuffer Make(size_t length) { + if (void* alloc = Allocate(length); alloc != nullptr) { + return WriteBuffer(static_cast<char*>(alloc), length); + } else { + return WriteBuffer(); + } + } + + private: + constexpr void SetLength(size_t size) { + m_length_and_is_normalized = (m_length_and_is_normalized & 1) | (size << 1); + } + }; + +private: + const char* m_str; + WriteBuffer m_write_buffer; + +public: + constexpr Path() : m_str(EmptyPath), m_write_buffer() {} + + constexpr Path(const char* s) : m_str(s), m_write_buffer() { + m_write_buffer.SetNormalized(); + } + + constexpr ~Path() = default; + + constexpr Result SetShallowBuffer(const char* buffer) { + // Check pre-conditions + ASSERT(m_write_buffer.GetLength() == 0); + + // Check the buffer is valid + R_UNLESS(buffer != nullptr, ResultNullptrArgument); + + // Set buffer + this->SetReadOnlyBuffer(buffer); + + // Note that we're normalized + this->SetNormalized(); + + R_SUCCEED(); + } + + constexpr const char* GetString() const { + // Check pre-conditions + ASSERT(this->IsNormalized()); + + return m_str; + } + + constexpr size_t GetLength() const { + if (std::is_constant_evaluated()) { + return Strlen(this->GetString()); + } else { + return std::strlen(this->GetString()); + } + } + + constexpr bool IsEmpty() const { + return *m_str == '\x00'; + } + + constexpr bool IsMatchHead(const char* p, size_t len) const { + return Strncmp(this->GetString(), p, len) == 0; + } + + Result Initialize(const Path& rhs) { + // Check the other path is normalized + const bool normalized = rhs.IsNormalized(); + R_UNLESS(normalized, ResultNotNormalized); + + // Allocate buffer for our path + const auto len = rhs.GetLength(); + R_TRY(this->Preallocate(len + 1)); + + // Copy the path + const size_t copied = Strlcpy<char>(m_write_buffer.Get(), rhs.GetString(), len + 1); + R_UNLESS(copied == len, ResultUnexpectedInPathA); + + // Set normalized + this->SetNormalized(); + R_SUCCEED(); + } + + Result Initialize(const char* path, size_t len) { + // Check the path is valid + R_UNLESS(path != nullptr, ResultNullptrArgument); + + // Initialize + R_TRY(this->InitializeImpl(path, len)); + + // Set not normalized + this->SetNotNormalized(); + + R_SUCCEED(); + } + + Result Initialize(const char* path) { + // Check the path is valid + R_UNLESS(path != nullptr, ResultNullptrArgument); + + R_RETURN(this->Initialize(path, std::strlen(path))); + } + + Result InitializeWithReplaceBackslash(const char* path) { + // Check the path is valid + R_UNLESS(path != nullptr, ResultNullptrArgument); + + // Initialize + R_TRY(this->InitializeImpl(path, std::strlen(path))); + + // Replace slashes as desired + if (const auto write_buffer_length = m_write_buffer.GetLength(); write_buffer_length > 1) { + Replace(m_write_buffer.Get(), write_buffer_length - 1, '\\', '/'); + } + + // Set not normalized + this->SetNotNormalized(); + + R_SUCCEED(); + } + + Result InitializeWithReplaceForwardSlashes(const char* path) { + // Check the path is valid + R_UNLESS(path != nullptr, ResultNullptrArgument); + + // Initialize + R_TRY(this->InitializeImpl(path, std::strlen(path))); + + // Replace slashes as desired + if (m_write_buffer.GetLength() > 1) { + if (auto* p = m_write_buffer.Get(); p[0] == '/' && p[1] == '/') { + p[0] = '\\'; + p[1] = '\\'; + } + } + + // Set not normalized + this->SetNotNormalized(); + + R_SUCCEED(); + } + + Result InitializeWithNormalization(const char* path, size_t size) { + // Check the path is valid + R_UNLESS(path != nullptr, ResultNullptrArgument); + + // Initialize + R_TRY(this->InitializeImpl(path, size)); + + // Set not normalized + this->SetNotNormalized(); + + // Perform normalization + PathFlags path_flags; + if (IsPathRelative(m_str)) { + path_flags.AllowRelativePath(); + } else if (IsWindowsPath(m_str, true)) { + path_flags.AllowWindowsPath(); + } else { + /* NOTE: In this case, Nintendo checks is normalized, then sets is normalized, then + * returns success. */ + /* This seems like a bug. */ + size_t dummy; + bool normalized; + R_TRY(PathFormatter::IsNormalized(std::addressof(normalized), std::addressof(dummy), + m_str)); + + this->SetNormalized(); + R_SUCCEED(); + } + + // Normalize + R_TRY(this->Normalize(path_flags)); + + this->SetNormalized(); + R_SUCCEED(); + } + + Result InitializeWithNormalization(const char* path) { + // Check the path is valid + R_UNLESS(path != nullptr, ResultNullptrArgument); + + R_RETURN(this->InitializeWithNormalization(path, std::strlen(path))); + } + + Result InitializeAsEmpty() { + // Clear our buffer + this->ClearBuffer(); + + // Set normalized + this->SetNormalized(); + + R_SUCCEED(); + } + + Result AppendChild(const char* child) { + // Check the path is valid + R_UNLESS(child != nullptr, ResultNullptrArgument); + + // Basic checks. If we have a path and the child is empty, we have nothing to do + const char* c = child; + if (m_str[0]) { + // Skip an early separator + if (*c == '/') { + ++c; + } + + R_SUCCEED_IF(*c == '\x00'); + } + + // If we don't have a string, we can just initialize + auto cur_len = std::strlen(m_str); + if (cur_len == 0) { + R_RETURN(this->Initialize(child)); + } + + // Remove a trailing separator + if (m_str[cur_len - 1] == '/' || m_str[cur_len - 1] == '\\') { + --cur_len; + } + + // Get the child path's length + auto child_len = std::strlen(c); + + // Reset our write buffer + WriteBuffer old_write_buffer; + if (m_write_buffer.Get() != nullptr) { + old_write_buffer = std::move(m_write_buffer); + this->ClearBuffer(); + } + + // Pre-allocate the new buffer + R_TRY(this->Preallocate(cur_len + 1 + child_len + 1)); + + // Get our write buffer + auto* dst = m_write_buffer.Get(); + if (old_write_buffer.Get() != nullptr && cur_len > 0) { + Strlcpy<char>(dst, old_write_buffer.Get(), cur_len + 1); + } + + // Add separator + dst[cur_len] = '/'; + + // Copy the child path + const size_t copied = Strlcpy<char>(dst + cur_len + 1, c, child_len + 1); + R_UNLESS(copied == child_len, ResultUnexpectedInPathA); + + R_SUCCEED(); + } + + Result AppendChild(const Path& rhs) { + R_RETURN(this->AppendChild(rhs.GetString())); + } + + Result Combine(const Path& parent, const Path& child) { + // Get the lengths + const auto p_len = parent.GetLength(); + const auto c_len = child.GetLength(); + + // Allocate our buffer + R_TRY(this->Preallocate(p_len + c_len + 1)); + + // Initialize as parent + R_TRY(this->Initialize(parent)); + + // If we're empty, we can just initialize as child + if (this->IsEmpty()) { + R_TRY(this->Initialize(child)); + } else { + // Otherwise, we should append the child + R_TRY(this->AppendChild(child)); + } + + R_SUCCEED(); + } + + Result RemoveChild() { + // If we don't have a write-buffer, ensure that we have one + if (m_write_buffer.Get() == nullptr) { + if (const auto len = std::strlen(m_str); len > 0) { + R_TRY(this->Preallocate(len)); + Strlcpy<char>(m_write_buffer.Get(), m_str, len + 1); + } + } + + // Check that it's possible for us to remove a child + auto* p = m_write_buffer.Get(); + s32 len = std::strlen(p); + R_UNLESS(len != 1 || (p[0] != '/' && p[0] != '.'), ResultNotImplemented); + + // Handle a trailing separator + if (len > 0 && (p[len - 1] == '\\' || p[len - 1] == '/')) { + --len; + } + + // Remove the child path segment + while ((--len) >= 0 && p[len]) { + if (p[len] == '/' || p[len] == '\\') { + if (len > 0) { + p[len] = 0; + } else { + p[1] = 0; + len = 1; + } + break; + } + } + + // Check that length remains > 0 + R_UNLESS(len > 0, ResultNotImplemented); + + R_SUCCEED(); + } + + Result Normalize(const PathFlags& flags) { + // If we're already normalized, nothing to do + R_SUCCEED_IF(this->IsNormalized()); + + // Check if we're normalized + bool normalized; + size_t dummy; + R_TRY(PathFormatter::IsNormalized(std::addressof(normalized), std::addressof(dummy), m_str, + flags)); + + // If we're not normalized, normalize + if (!normalized) { + // Determine necessary buffer length + auto len = m_write_buffer.GetLength(); + if (flags.IsRelativePathAllowed() && IsPathRelative(m_str)) { + len += 2; + } + if (flags.IsWindowsPathAllowed() && IsWindowsPath(m_str, true)) { + len += 1; + } + + // Allocate a new buffer + const size_t size = Common::AlignUp(len, WriteBufferAlignmentLength); + auto buf = WriteBuffer::Make(size); + R_UNLESS(buf.Get() != nullptr, ResultAllocationMemoryFailedMakeUnique); + + // Normalize into it + R_TRY(PathFormatter::Normalize(buf.Get(), size, m_write_buffer.Get(), + m_write_buffer.GetLength(), flags)); + + // Set the normalized buffer as our buffer + this->SetModifiableBuffer(std::move(buf)); + } + + // Set normalized + this->SetNormalized(); + R_SUCCEED(); + } + +private: + void ClearBuffer() { + m_write_buffer.ResetBuffer(); + m_str = EmptyPath; + } + + void SetModifiableBuffer(WriteBuffer&& buffer) { + // Check pre-conditions + ASSERT(buffer.Get() != nullptr); + ASSERT(buffer.GetLength() > 0); + ASSERT(Common::IsAligned(buffer.GetLength(), WriteBufferAlignmentLength)); + + // Get whether we're normalized + if (m_write_buffer.IsNormalized()) { + buffer.SetNormalized(); + } else { + buffer.SetNotNormalized(); + } + + // Set write buffer + m_write_buffer = std::move(buffer); + m_str = m_write_buffer.Get(); + } + + constexpr void SetReadOnlyBuffer(const char* buffer) { + m_str = buffer; + m_write_buffer.ResetBuffer(); + } + + Result Preallocate(size_t length) { + // Allocate additional space, if needed + if (length > m_write_buffer.GetLength()) { + // Allocate buffer + const size_t size = Common::AlignUp(length, WriteBufferAlignmentLength); + auto buf = WriteBuffer::Make(size); + R_UNLESS(buf.Get() != nullptr, ResultAllocationMemoryFailedMakeUnique); + + // Set write buffer + this->SetModifiableBuffer(std::move(buf)); + } + + R_SUCCEED(); + } + + Result InitializeImpl(const char* path, size_t size) { + if (size > 0 && path[0]) { + // Pre allocate a buffer for the path + R_TRY(this->Preallocate(size + 1)); + + // Copy the path + const size_t copied = Strlcpy<char>(m_write_buffer.Get(), path, size + 1); + R_UNLESS(copied >= size, ResultUnexpectedInPathA); + } else { + // We can just clear the buffer + this->ClearBuffer(); + } + + R_SUCCEED(); + } + + constexpr char* GetWriteBuffer() { + ASSERT(m_write_buffer.Get() != nullptr); + return m_write_buffer.Get(); + } + + constexpr size_t GetWriteBufferLength() const { + return m_write_buffer.GetLength(); + } + + constexpr bool IsNormalized() const { + return m_write_buffer.IsNormalized(); + } + + constexpr void SetNormalized() { + m_write_buffer.SetNormalized(); + } + + constexpr void SetNotNormalized() { + m_write_buffer.SetNotNormalized(); + } + +public: + bool operator==(const FileSys::Path& rhs) const { + return std::strcmp(this->GetString(), rhs.GetString()) == 0; + } + bool operator!=(const FileSys::Path& rhs) const { + return !(*this == rhs); + } + bool operator==(const char* p) const { + return std::strcmp(this->GetString(), p) == 0; + } + bool operator!=(const char* p) const { + return !(*this == p); + } +}; + +inline Result SetUpFixedPath(FileSys::Path* out, const char* s) { + // Verify the path is normalized + bool normalized; + size_t dummy; + R_TRY(PathNormalizer::IsNormalized(std::addressof(normalized), std::addressof(dummy), s)); + + R_UNLESS(normalized, ResultInvalidPathFormat); + + // Set the fixed path + R_RETURN(out->SetShallowBuffer(s)); +} + +constexpr inline bool IsWindowsDriveRootPath(const FileSys::Path& path) { + const char* const str = path.GetString(); + return IsWindowsDrive(str) && + (str[2] == StringTraits::DirectorySeparator || + str[2] == StringTraits::AlternateDirectorySeparator) && + str[3] == StringTraits::NullTerminator; +} + +} // namespace FileSys diff --git a/src/core/file_sys/fs_path_utility.h b/src/core/file_sys/fs_path_utility.h new file mode 100644 index 000000000..e9011d065 --- /dev/null +++ b/src/core/file_sys/fs_path_utility.h @@ -0,0 +1,1239 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/assert.h" +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/scope_exit.h" +#include "core/file_sys/fs_directory.h" +#include "core/file_sys/fs_memory_management.h" +#include "core/file_sys/fs_string_util.h" +#include "core/hle/result.h" + +namespace FileSys { + +constexpr inline size_t MountNameLengthMax = 15; + +namespace StringTraits { + +constexpr inline char DirectorySeparator = '/'; +constexpr inline char DriveSeparator = ':'; +constexpr inline char Dot = '.'; +constexpr inline char NullTerminator = '\x00'; + +constexpr inline char AlternateDirectorySeparator = '\\'; + +constexpr inline const char InvalidCharacters[6] = {':', '*', '?', '<', '>', '|'}; +constexpr inline const char InvalidCharactersForHostName[6] = {':', '*', '<', '>', '|', '$'}; +constexpr inline const char InvalidCharactersForMountName[5] = {'*', '?', '<', '>', '|'}; + +namespace impl { + +template <const char* InvalidCharacterSet, size_t NumInvalidCharacters> +consteval u64 MakeInvalidCharacterMask(size_t n) { + u64 mask = 0; + for (size_t i = 0; i < NumInvalidCharacters; ++i) { + if ((static_cast<u64>(InvalidCharacterSet[i]) >> 6) == n) { + mask |= static_cast<u64>(1) << (static_cast<u64>(InvalidCharacterSet[i]) & 0x3F); + } + } + return mask; +} + +template <const char* InvalidCharacterSet, size_t NumInvalidCharacters> +constexpr bool IsInvalidCharacterImpl(char c) { + constexpr u64 Masks[4] = { + MakeInvalidCharacterMask<InvalidCharacterSet, NumInvalidCharacters>(0), + MakeInvalidCharacterMask<InvalidCharacterSet, NumInvalidCharacters>(1), + MakeInvalidCharacterMask<InvalidCharacterSet, NumInvalidCharacters>(2), + MakeInvalidCharacterMask<InvalidCharacterSet, NumInvalidCharacters>(3)}; + + return (Masks[static_cast<u64>(c) >> 6] & + (static_cast<u64>(1) << (static_cast<u64>(c) & 0x3F))) != 0; +} + +} // namespace impl + +constexpr bool IsInvalidCharacter(char c) { + return impl::IsInvalidCharacterImpl<InvalidCharacters, Common::Size(InvalidCharacters)>(c); +} +constexpr bool IsInvalidCharacterForHostName(char c) { + return impl::IsInvalidCharacterImpl<InvalidCharactersForHostName, + Common::Size(InvalidCharactersForHostName)>(c); +} +constexpr bool IsInvalidCharacterForMountName(char c) { + return impl::IsInvalidCharacterImpl<InvalidCharactersForMountName, + Common::Size(InvalidCharactersForMountName)>(c); +} + +} // namespace StringTraits + +constexpr inline size_t WindowsDriveLength = 2; +constexpr inline size_t UncPathPrefixLength = 2; +constexpr inline size_t DosDevicePathPrefixLength = 4; + +class PathFlags { +private: + static constexpr u32 WindowsPathFlag = (1 << 0); + static constexpr u32 RelativePathFlag = (1 << 1); + static constexpr u32 EmptyPathFlag = (1 << 2); + static constexpr u32 MountNameFlag = (1 << 3); + static constexpr u32 BackslashFlag = (1 << 4); + static constexpr u32 AllCharactersFlag = (1 << 5); + +private: + u32 m_value; + +public: + constexpr PathFlags() : m_value(0) { /* ... */ + } + +#define DECLARE_PATH_FLAG_HANDLER(__WHICH__) \ + constexpr bool Is##__WHICH__##Allowed() const { return (m_value & __WHICH__##Flag) != 0; } \ + constexpr void Allow##__WHICH__() { m_value |= __WHICH__##Flag; } + + DECLARE_PATH_FLAG_HANDLER(WindowsPath) + DECLARE_PATH_FLAG_HANDLER(RelativePath) + DECLARE_PATH_FLAG_HANDLER(EmptyPath) + DECLARE_PATH_FLAG_HANDLER(MountName) + DECLARE_PATH_FLAG_HANDLER(Backslash) + DECLARE_PATH_FLAG_HANDLER(AllCharacters) + +#undef DECLARE_PATH_FLAG_HANDLER +}; + +template <typename T> + requires(std::same_as<T, char> || std::same_as<T, wchar_t>) +constexpr inline bool IsDosDevicePath(const T* path) { + ASSERT(path != nullptr); + + using namespace StringTraits; + + return path[0] == AlternateDirectorySeparator && path[1] == AlternateDirectorySeparator && + (path[2] == Dot || path[2] == '?') && + (path[3] == DirectorySeparator || path[3] == AlternateDirectorySeparator); +} + +template <typename T> + requires(std::same_as<T, char> || std::same_as<T, wchar_t>) +constexpr inline bool IsUncPath(const T* path, bool allow_forward_slash = true, + bool allow_back_slash = true) { + ASSERT(path != nullptr); + + using namespace StringTraits; + + return (allow_forward_slash && path[0] == DirectorySeparator && + path[1] == DirectorySeparator) || + (allow_back_slash && path[0] == AlternateDirectorySeparator && + path[1] == AlternateDirectorySeparator); +} + +constexpr inline bool IsWindowsDrive(const char* path) { + ASSERT(path != nullptr); + + return (('a' <= path[0] && path[0] <= 'z') || ('A' <= path[0] && path[0] <= 'Z')) && + path[1] == StringTraits::DriveSeparator; +} + +constexpr inline bool IsWindowsPath(const char* path, bool allow_forward_slash_unc) { + return IsWindowsDrive(path) || IsDosDevicePath(path) || + IsUncPath(path, allow_forward_slash_unc, true); +} + +constexpr inline int GetWindowsSkipLength(const char* path) { + if (IsDosDevicePath(path)) { + return DosDevicePathPrefixLength; + } else if (IsWindowsDrive(path)) { + return WindowsDriveLength; + } else if (IsUncPath(path)) { + return UncPathPrefixLength; + } else { + return 0; + } +} + +constexpr inline bool IsPathAbsolute(const char* path) { + return IsWindowsPath(path, false) || path[0] == StringTraits::DirectorySeparator; +} + +constexpr inline bool IsPathRelative(const char* path) { + return path[0] && !IsPathAbsolute(path); +} + +constexpr inline bool IsCurrentDirectory(const char* path) { + return path[0] == StringTraits::Dot && + (path[1] == StringTraits::NullTerminator || path[1] == StringTraits::DirectorySeparator); +} + +constexpr inline bool IsParentDirectory(const char* path) { + return path[0] == StringTraits::Dot && path[1] == StringTraits::Dot && + (path[2] == StringTraits::NullTerminator || path[2] == StringTraits::DirectorySeparator); +} + +constexpr inline bool IsPathStartWithCurrentDirectory(const char* path) { + return IsCurrentDirectory(path) || IsParentDirectory(path); +} + +constexpr inline bool IsSubPath(const char* lhs, const char* rhs) { + // Check pre-conditions + ASSERT(lhs != nullptr); + ASSERT(rhs != nullptr); + + // Import StringTraits names for current scope + using namespace StringTraits; + + // Special case certain paths + if (IsUncPath(lhs) && !IsUncPath(rhs)) { + return false; + } + if (!IsUncPath(lhs) && IsUncPath(rhs)) { + return false; + } + + if (lhs[0] == DirectorySeparator && lhs[1] == NullTerminator && rhs[0] == DirectorySeparator && + rhs[1] != NullTerminator) { + return true; + } + if (rhs[0] == DirectorySeparator && rhs[1] == NullTerminator && lhs[0] == DirectorySeparator && + lhs[1] != NullTerminator) { + return true; + } + + // Check subpath + for (size_t i = 0; /* ... */; ++i) { + if (lhs[i] == NullTerminator) { + return rhs[i] == DirectorySeparator; + } else if (rhs[i] == NullTerminator) { + return lhs[i] == DirectorySeparator; + } else if (lhs[i] != rhs[i]) { + return false; + } + } +} + +// Path utilities +constexpr inline void Replace(char* dst, size_t dst_size, char old_char, char new_char) { + ASSERT(dst != nullptr); + for (char* cur = dst; cur < dst + dst_size && *cur; ++cur) { + if (*cur == old_char) { + *cur = new_char; + } + } +} + +constexpr inline Result CheckUtf8(const char* s) { + // Check pre-conditions + ASSERT(s != nullptr); + + // Iterate, checking for utf8-validity + while (*s) { + char utf8_buf[4] = {}; + + const auto pick_res = PickOutCharacterFromUtf8String(utf8_buf, std::addressof(s)); + R_UNLESS(pick_res == CharacterEncodingResult_Success, ResultInvalidPathFormat); + + u32 dummy; + const auto cvt_res = ConvertCharacterUtf8ToUtf32(std::addressof(dummy), utf8_buf); + R_UNLESS(cvt_res == CharacterEncodingResult_Success, ResultInvalidPathFormat); + } + + R_SUCCEED(); +} + +// Path formatting +class PathNormalizer { +private: + enum class PathState { + Start, + Normal, + FirstSeparator, + Separator, + CurrentDir, + ParentDir, + }; + +private: + static constexpr void ReplaceParentDirectoryPath(char* dst, const char* src) { + // Use StringTraits names for remainder of scope + using namespace StringTraits; + + // Start with a dir-separator + dst[0] = DirectorySeparator; + + auto i = 1; + while (src[i] != NullTerminator) { + if ((src[i - 1] == DirectorySeparator || src[i - 1] == AlternateDirectorySeparator) && + src[i + 0] == Dot && src[i + 1] == Dot && + (src[i + 2] == DirectorySeparator || src[i + 2] == AlternateDirectorySeparator)) { + dst[i - 1] = DirectorySeparator; + dst[i + 0] = Dot; + dst[i + 1] = Dot; + dst[i + 2] = DirectorySeparator; + i += 3; + } else { + if (src[i - 1] == AlternateDirectorySeparator && src[i + 0] == Dot && + src[i + 1] == Dot && src[i + 2] == NullTerminator) { + dst[i - 1] = DirectorySeparator; + dst[i + 0] = Dot; + dst[i + 1] = Dot; + i += 2; + break; + } + + dst[i] = src[i]; + ++i; + } + } + + dst[i] = StringTraits::NullTerminator; + } + +public: + static constexpr bool IsParentDirectoryPathReplacementNeeded(const char* path) { + // Use StringTraits names for remainder of scope + using namespace StringTraits; + + if (path[0] != DirectorySeparator && path[0] != AlternateDirectorySeparator) { + return false; + } + + // Check to find a parent reference using alternate separators + if (path[0] != NullTerminator && path[1] != NullTerminator && path[2] != NullTerminator) { + size_t i; + for (i = 0; path[i + 3] != NullTerminator; ++path) { + if (path[i + 1] != Dot || path[i + 2] != Dot) { + continue; + } + + const char c0 = path[i + 0]; + const char c3 = path[i + 3]; + + if (c0 == AlternateDirectorySeparator && + (c3 == DirectorySeparator || c3 == AlternateDirectorySeparator || + c3 == NullTerminator)) { + return true; + } + + if (c3 == AlternateDirectorySeparator && + (c0 == DirectorySeparator || c0 == AlternateDirectorySeparator)) { + return true; + } + } + + if (path[i + 0] == AlternateDirectorySeparator && path[i + 1] == Dot && + path[i + 2] == Dot /* && path[i + 3] == NullTerminator */) { + return true; + } + } + + return false; + } + + static constexpr Result IsNormalized(bool* out, size_t* out_len, const char* path, + bool allow_all_characters = false) { + // Use StringTraits names for remainder of scope + using namespace StringTraits; + + // Parse the path + auto state = PathState::Start; + size_t len = 0; + while (path[len] != NullTerminator) { + // Get the current character + const char c = path[len++]; + + // Check the current character is valid + if (!allow_all_characters && state != PathState::Start) { + R_UNLESS(!IsInvalidCharacter(c), ResultInvalidCharacter); + } + + // Process depending on current state + switch (state) { + // Import the PathState enums for convenience + using enum PathState; + + case Start: + R_UNLESS(c == DirectorySeparator, ResultInvalidPathFormat); + state = FirstSeparator; + break; + case Normal: + if (c == DirectorySeparator) { + state = Separator; + } + break; + case FirstSeparator: + case Separator: + if (c == DirectorySeparator) { + *out = false; + R_SUCCEED(); + } + + if (c == Dot) { + state = CurrentDir; + } else { + state = Normal; + } + break; + case CurrentDir: + if (c == DirectorySeparator) { + *out = false; + R_SUCCEED(); + } + + if (c == Dot) { + state = ParentDir; + } else { + state = Normal; + } + break; + case ParentDir: + if (c == DirectorySeparator) { + *out = false; + R_SUCCEED(); + } + + state = Normal; + break; + default: + UNREACHABLE(); + break; + } + } + + // Check the final state + switch (state) { + // Import the PathState enums for convenience + using enum PathState; + case Start: + R_THROW(ResultInvalidPathFormat); + case Normal: + case FirstSeparator: + *out = true; + break; + case Separator: + case CurrentDir: + case ParentDir: + *out = false; + break; + default: + UNREACHABLE(); + break; + } + + // Set the output length + *out_len = len; + R_SUCCEED(); + } + + static Result Normalize(char* dst, size_t* out_len, const char* path, size_t max_out_size, + bool is_windows_path, bool is_drive_relative_path, + bool allow_all_characters = false) { + // Use StringTraits names for remainder of scope + using namespace StringTraits; + + // Prepare to iterate + const char* cur_path = path; + size_t total_len = 0; + + // If path begins with a separator, check that we're not drive relative + if (cur_path[0] != DirectorySeparator) { + R_UNLESS(is_drive_relative_path, ResultInvalidPathFormat); + + dst[total_len++] = DirectorySeparator; + } + + // We're going to need to do path replacement, potentially + char* replacement_path = nullptr; + size_t replacement_path_size = 0; + + SCOPE_EXIT({ + if (replacement_path != nullptr) { + if (std::is_constant_evaluated()) { + delete[] replacement_path; + } else { + Deallocate(replacement_path, replacement_path_size); + } + } + }); + + // Perform path replacement, if necessary + if (IsParentDirectoryPathReplacementNeeded(cur_path)) { + if (std::is_constant_evaluated()) { + replacement_path_size = EntryNameLengthMax + 1; + replacement_path = new char[replacement_path_size]; + } else { + replacement_path_size = EntryNameLengthMax + 1; + replacement_path = static_cast<char*>(Allocate(replacement_path_size)); + } + + ReplaceParentDirectoryPath(replacement_path, cur_path); + + cur_path = replacement_path; + } + + // Iterate, normalizing path components + bool skip_next_sep = false; + size_t i = 0; + + while (cur_path[i] != NullTerminator) { + // Process a directory separator, if we run into one + if (cur_path[i] == DirectorySeparator) { + // Swallow separators + do { + ++i; + } while (cur_path[i] == DirectorySeparator); + + // Check if we hit end of string + if (cur_path[i] == NullTerminator) { + break; + } + + // If we aren't skipping the separator, write it, checking that we remain in bounds. + if (!skip_next_sep) { + if (total_len + 1 == max_out_size) { + dst[total_len] = NullTerminator; + *out_len = total_len; + R_THROW(ResultTooLongPath); + } + + dst[total_len++] = DirectorySeparator; + } + + // Don't skip the next separator + skip_next_sep = false; + } + + // Get the length of the current directory component + size_t dir_len = 0; + while (cur_path[i + dir_len] != DirectorySeparator && + cur_path[i + dir_len] != NullTerminator) { + // Check for validity + if (!allow_all_characters) { + R_UNLESS(!IsInvalidCharacter(cur_path[i + dir_len]), ResultInvalidCharacter); + } + + ++dir_len; + } + + // Handle the current dir component + if (IsCurrentDirectory(cur_path + i)) { + skip_next_sep = true; + } else if (IsParentDirectory(cur_path + i)) { + // We should have just written a separator + ASSERT(dst[total_len - 1] == DirectorySeparator); + + // We should have started with a separator, for non-windows paths + if (!is_windows_path) { + ASSERT(dst[0] == DirectorySeparator); + } + + // Remove the previous component + if (total_len == 1) { + R_UNLESS(is_windows_path, ResultDirectoryUnobtainable); + + --total_len; + } else { + total_len -= 2; + + do { + if (dst[total_len] == DirectorySeparator) { + break; + } + } while ((--total_len) != 0); + } + + // We should be pointing to a directory separator, for non-windows paths + if (!is_windows_path) { + ASSERT(dst[total_len] == DirectorySeparator); + } + + // We should remain in bounds + ASSERT(total_len < max_out_size); + } else { + // Copy, possibly truncating + if (total_len + dir_len + 1 > max_out_size) { + const size_t copy_len = max_out_size - (total_len + 1); + + for (size_t j = 0; j < copy_len; ++j) { + dst[total_len++] = cur_path[i + j]; + } + + dst[total_len] = NullTerminator; + *out_len = total_len; + R_THROW(ResultTooLongPath); + } + + for (size_t j = 0; j < dir_len; ++j) { + dst[total_len++] = cur_path[i + j]; + } + } + + // Advance past the current directory component + i += dir_len; + } + + if (skip_next_sep) { + --total_len; + } + + if (total_len == 0 && max_out_size != 0) { + total_len = 1; + dst[0] = DirectorySeparator; + } + + // NOTE: Probable nintendo bug, as max_out_size must be at least total_len + 1 for the null + // terminator. + R_UNLESS(max_out_size >= total_len - 1, ResultTooLongPath); + + dst[total_len] = NullTerminator; + + // Check that the result path is normalized + bool is_normalized; + size_t dummy; + R_TRY(IsNormalized(std::addressof(is_normalized), std::addressof(dummy), dst, + allow_all_characters)); + + // Assert that the result path is normalized + ASSERT(is_normalized); + + // Set the output length + *out_len = total_len; + R_SUCCEED(); + } +}; + +class PathFormatter { +private: + static constexpr Result CheckSharedName(const char* name, size_t len) { + // Use StringTraits names for remainder of scope + using namespace StringTraits; + + if (len == 1) { + R_UNLESS(name[0] != Dot, ResultInvalidPathFormat); + } else if (len == 2) { + R_UNLESS(name[0] != Dot || name[1] != Dot, ResultInvalidPathFormat); + } + + for (size_t i = 0; i < len; ++i) { + R_UNLESS(!IsInvalidCharacter(name[i]), ResultInvalidCharacter); + } + + R_SUCCEED(); + } + + static constexpr Result CheckHostName(const char* name, size_t len) { + // Use StringTraits names for remainder of scope + using namespace StringTraits; + + if (len == 2) { + R_UNLESS(name[0] != Dot || name[1] != Dot, ResultInvalidPathFormat); + } + + for (size_t i = 0; i < len; ++i) { + R_UNLESS(!IsInvalidCharacterForHostName(name[i]), ResultInvalidCharacter); + } + + R_SUCCEED(); + } + + static constexpr Result CheckInvalidBackslash(bool* out_contains_backslash, const char* path, + bool allow_backslash) { + // Use StringTraits names for remainder of scope + using namespace StringTraits; + + // Default to no backslashes, so we can just write if we see one + *out_contains_backslash = false; + + while (*path != NullTerminator) { + if (*(path++) == AlternateDirectorySeparator) { + *out_contains_backslash = true; + + R_UNLESS(allow_backslash, ResultInvalidCharacter); + } + } + + R_SUCCEED(); + } + +public: + static constexpr Result CheckPathFormat(const char* path, const PathFlags& flags) { + bool normalized; + size_t len; + R_RETURN(IsNormalized(std::addressof(normalized), std::addressof(len), path, flags)); + } + + static constexpr Result SkipMountName(const char** out, size_t* out_len, const char* path) { + R_RETURN(ParseMountName(out, out_len, nullptr, 0, path)); + } + + static constexpr Result ParseMountName(const char** out, size_t* out_len, char* out_mount_name, + size_t out_mount_name_buffer_size, const char* path) { + // Check pre-conditions + ASSERT(path != nullptr); + ASSERT(out_len != nullptr); + ASSERT(out != nullptr); + ASSERT((out_mount_name == nullptr) == (out_mount_name_buffer_size == 0)); + + // Use StringTraits names for remainder of scope + using namespace StringTraits; + + // Determine max mount length + const auto max_mount_len = + out_mount_name_buffer_size == 0 + ? MountNameLengthMax + 1 + : std::min(MountNameLengthMax + 1, out_mount_name_buffer_size); + + // Parse the path until we see a drive separator + size_t mount_len = 0; + for (/* ... */; mount_len < max_mount_len && path[mount_len]; ++mount_len) { + const char c = path[mount_len]; + + // If we see a drive separator, advance, then we're done with the pre-drive separator + // part of the mount. + if (c == DriveSeparator) { + ++mount_len; + break; + } + + // If we see a directory separator, we're not in a mount name + if (c == DirectorySeparator || c == AlternateDirectorySeparator) { + *out = path; + *out_len = 0; + R_SUCCEED(); + } + } + + // Check to be sure we're actually looking at a mount name + if (mount_len <= 2 || path[mount_len - 1] != DriveSeparator) { + *out = path; + *out_len = 0; + R_SUCCEED(); + } + + // Check that all characters in the mount name are allowable + for (size_t i = 0; i < mount_len; ++i) { + R_UNLESS(!IsInvalidCharacterForMountName(path[i]), ResultInvalidCharacter); + } + + // Copy out the mount name + if (out_mount_name_buffer_size > 0) { + R_UNLESS(mount_len < out_mount_name_buffer_size, ResultTooLongPath); + + for (size_t i = 0; i < mount_len; ++i) { + out_mount_name[i] = path[i]; + } + out_mount_name[mount_len] = NullTerminator; + } + + // Set the output + *out = path + mount_len; + *out_len = mount_len; + R_SUCCEED(); + } + + static constexpr Result SkipRelativeDotPath(const char** out, size_t* out_len, + const char* path) { + R_RETURN(ParseRelativeDotPath(out, out_len, nullptr, 0, path)); + } + + static constexpr Result ParseRelativeDotPath(const char** out, size_t* out_len, + char* out_relative, + size_t out_relative_buffer_size, + const char* path) { + // Check pre-conditions + ASSERT(path != nullptr); + ASSERT(out_len != nullptr); + ASSERT(out != nullptr); + ASSERT((out_relative == nullptr) == (out_relative_buffer_size == 0)); + + // Use StringTraits names for remainder of scope + using namespace StringTraits; + + // Initialize the output buffer, if we have one + if (out_relative_buffer_size > 0) { + out_relative[0] = NullTerminator; + } + + // Check if the path is relative + if (path[0] == Dot && (path[1] == NullTerminator || path[1] == DirectorySeparator || + path[1] == AlternateDirectorySeparator)) { + if (out_relative_buffer_size > 0) { + R_UNLESS(out_relative_buffer_size >= 2, ResultTooLongPath); + + out_relative[0] = Dot; + out_relative[1] = NullTerminator; + } + + *out = path + 1; + *out_len = 1; + R_SUCCEED(); + } + + // Ensure the path isn't a parent directory + R_UNLESS(!(path[0] == Dot && path[1] == Dot), ResultDirectoryUnobtainable); + + // There was no relative dot path + *out = path; + *out_len = 0; + R_SUCCEED(); + } + + static constexpr Result SkipWindowsPath(const char** out, size_t* out_len, bool* out_normalized, + const char* path, bool has_mount_name) { + // We're normalized if and only if the parsing doesn't throw ResultNotNormalized() + *out_normalized = true; + + R_TRY_CATCH(ParseWindowsPath(out, out_len, nullptr, 0, path, has_mount_name)) { + R_CATCH(ResultNotNormalized) { + *out_normalized = false; + } + } + R_END_TRY_CATCH; + ON_RESULT_INCLUDED(ResultNotNormalized) { + *out_normalized = false; + }; + + R_SUCCEED(); + } + + static constexpr Result ParseWindowsPath(const char** out, size_t* out_len, char* out_win, + size_t out_win_buffer_size, const char* path, + bool has_mount_name) { + // Check pre-conditions + ASSERT(path != nullptr); + ASSERT(out_len != nullptr); + ASSERT(out != nullptr); + ASSERT((out_win == nullptr) == (out_win_buffer_size == 0)); + + // Use StringTraits names for remainder of scope + using namespace StringTraits; + + // Initialize the output buffer, if we have one + if (out_win_buffer_size > 0) { + out_win[0] = NullTerminator; + } + + // Handle path start + const char* cur_path = path; + if (has_mount_name && path[0] == DirectorySeparator) { + if (path[1] == AlternateDirectorySeparator && path[2] == AlternateDirectorySeparator) { + R_UNLESS(out_win_buffer_size > 0, ResultNotNormalized); + + ++cur_path; + } else if (IsWindowsDrive(path + 1)) { + R_UNLESS(out_win_buffer_size > 0, ResultNotNormalized); + + ++cur_path; + } + } + + // Handle windows drive + if (IsWindowsDrive(cur_path)) { + // Parse up to separator + size_t win_path_len = WindowsDriveLength; + for (/* ... */; cur_path[win_path_len] != NullTerminator; ++win_path_len) { + R_UNLESS(!IsInvalidCharacter(cur_path[win_path_len]), ResultInvalidCharacter); + + if (cur_path[win_path_len] == DirectorySeparator || + cur_path[win_path_len] == AlternateDirectorySeparator) { + break; + } + } + + // Ensure that we're normalized, if we're required to be + if (out_win_buffer_size == 0) { + for (size_t i = 0; i < win_path_len; ++i) { + R_UNLESS(cur_path[i] != AlternateDirectorySeparator, ResultNotNormalized); + } + } else { + // Ensure we can copy into the normalized buffer + R_UNLESS(win_path_len < out_win_buffer_size, ResultTooLongPath); + + for (size_t i = 0; i < win_path_len; ++i) { + out_win[i] = cur_path[i]; + } + out_win[win_path_len] = NullTerminator; + + Replace(out_win, win_path_len, AlternateDirectorySeparator, DirectorySeparator); + } + + *out = cur_path + win_path_len; + *out_len = win_path_len; + R_SUCCEED(); + } + + // Handle DOS device + if (IsDosDevicePath(cur_path)) { + size_t dos_prefix_len = DosDevicePathPrefixLength; + + if (IsWindowsDrive(cur_path + dos_prefix_len)) { + dos_prefix_len += WindowsDriveLength; + } else { + --dos_prefix_len; + } + + if (out_win_buffer_size > 0) { + // Ensure we can copy into the normalized buffer + R_UNLESS(dos_prefix_len < out_win_buffer_size, ResultTooLongPath); + + for (size_t i = 0; i < dos_prefix_len; ++i) { + out_win[i] = cur_path[i]; + } + out_win[dos_prefix_len] = NullTerminator; + + Replace(out_win, dos_prefix_len, DirectorySeparator, AlternateDirectorySeparator); + } + + *out = cur_path + dos_prefix_len; + *out_len = dos_prefix_len; + R_SUCCEED(); + } + + // Handle UNC path + if (IsUncPath(cur_path, false, true)) { + const char* final_path = cur_path; + + R_UNLESS(cur_path[UncPathPrefixLength] != DirectorySeparator, ResultInvalidPathFormat); + R_UNLESS(cur_path[UncPathPrefixLength] != AlternateDirectorySeparator, + ResultInvalidPathFormat); + + size_t cur_component_offset = 0; + size_t pos = UncPathPrefixLength; + for (/* ... */; cur_path[pos] != NullTerminator; ++pos) { + if (cur_path[pos] == DirectorySeparator || + cur_path[pos] == AlternateDirectorySeparator) { + if (cur_component_offset != 0) { + R_TRY(CheckSharedName(cur_path + cur_component_offset, + pos - cur_component_offset)); + + final_path = cur_path + pos; + break; + } + + R_UNLESS(cur_path[pos + 1] != DirectorySeparator, ResultInvalidPathFormat); + R_UNLESS(cur_path[pos + 1] != AlternateDirectorySeparator, + ResultInvalidPathFormat); + + R_TRY(CheckHostName(cur_path + 2, pos - 2)); + + cur_component_offset = pos + 1; + } + } + + R_UNLESS(cur_component_offset != pos, ResultInvalidPathFormat); + + if (cur_component_offset != 0 && final_path == cur_path) { + R_TRY(CheckSharedName(cur_path + cur_component_offset, pos - cur_component_offset)); + + final_path = cur_path + pos; + } + + size_t unc_prefix_len = final_path - cur_path; + + // Ensure that we're normalized, if we're required to be + if (out_win_buffer_size == 0) { + for (size_t i = 0; i < unc_prefix_len; ++i) { + R_UNLESS(cur_path[i] != DirectorySeparator, ResultNotNormalized); + } + } else { + // Ensure we can copy into the normalized buffer + R_UNLESS(unc_prefix_len < out_win_buffer_size, ResultTooLongPath); + + for (size_t i = 0; i < unc_prefix_len; ++i) { + out_win[i] = cur_path[i]; + } + out_win[unc_prefix_len] = NullTerminator; + + Replace(out_win, unc_prefix_len, DirectorySeparator, AlternateDirectorySeparator); + } + + *out = cur_path + unc_prefix_len; + *out_len = unc_prefix_len; + R_SUCCEED(); + } + + // There's no windows path to parse + *out = path; + *out_len = 0; + R_SUCCEED(); + } + + static constexpr Result IsNormalized(bool* out, size_t* out_len, const char* path, + const PathFlags& flags = {}) { + // Ensure nothing is null + R_UNLESS(out != nullptr, ResultNullptrArgument); + R_UNLESS(out_len != nullptr, ResultNullptrArgument); + R_UNLESS(path != nullptr, ResultNullptrArgument); + + // Verify that the path is valid utf-8 + R_TRY(CheckUtf8(path)); + + // Use StringTraits names for remainder of scope + using namespace StringTraits; + + // Handle the case where the path is empty + if (path[0] == NullTerminator) { + R_UNLESS(flags.IsEmptyPathAllowed(), ResultInvalidPathFormat); + + *out = true; + *out_len = 0; + R_SUCCEED(); + } + + // All normalized paths start with a directory separator...unless they're windows paths, + // relative paths, or have mount names. + if (path[0] != DirectorySeparator) { + R_UNLESS(flags.IsWindowsPathAllowed() || flags.IsRelativePathAllowed() || + flags.IsMountNameAllowed(), + ResultInvalidPathFormat); + } + + // Check that the path is allowed to be a windows path, if it is + if (IsWindowsPath(path, false)) { + R_UNLESS(flags.IsWindowsPathAllowed(), ResultInvalidPathFormat); + } + + // Skip past the mount name, if one is present + size_t total_len = 0; + size_t mount_name_len = 0; + R_TRY(SkipMountName(std::addressof(path), std::addressof(mount_name_len), path)); + + // If we had a mount name, check that that was allowed + if (mount_name_len > 0) { + R_UNLESS(flags.IsMountNameAllowed(), ResultInvalidPathFormat); + + total_len += mount_name_len; + } + + // Check that the path starts as a normalized path should + if (path[0] != DirectorySeparator && !IsPathStartWithCurrentDirectory(path) && + !IsWindowsPath(path, false)) { + R_UNLESS(flags.IsRelativePathAllowed(), ResultInvalidPathFormat); + R_UNLESS(!IsInvalidCharacter(path[0]), ResultInvalidPathFormat); + + *out = false; + R_SUCCEED(); + } + + // Process relative path + size_t relative_len = 0; + R_TRY(SkipRelativeDotPath(std::addressof(path), std::addressof(relative_len), path)); + + // If we have a relative path, check that was allowed + if (relative_len > 0) { + R_UNLESS(flags.IsRelativePathAllowed(), ResultInvalidPathFormat); + + total_len += relative_len; + + if (path[0] == NullTerminator) { + *out = true; + *out_len = total_len; + R_SUCCEED(); + } + } + + // Process windows path + size_t windows_len = 0; + bool normalized_win = false; + R_TRY(SkipWindowsPath(std::addressof(path), std::addressof(windows_len), + std::addressof(normalized_win), path, mount_name_len > 0)); + + // If the windows path wasn't normalized, we're not normalized + if (!normalized_win) { + R_UNLESS(flags.IsWindowsPathAllowed(), ResultInvalidPathFormat); + + *out = false; + R_SUCCEED(); + } + + // If we had a windows path, check that was allowed + if (windows_len > 0) { + R_UNLESS(flags.IsWindowsPathAllowed(), ResultInvalidPathFormat); + + total_len += windows_len; + + // We can't have both a relative path and a windows path + R_UNLESS(relative_len == 0, ResultInvalidPathFormat); + + // A path ending in a windows path isn't normalized + if (path[0] == NullTerminator) { + *out = false; + R_SUCCEED(); + } + + // Check that there are no windows directory separators in the path + for (size_t i = 0; path[i] != NullTerminator; ++i) { + if (path[i] == AlternateDirectorySeparator) { + *out = false; + R_SUCCEED(); + } + } + } + + // Check that parent directory replacement is not needed if backslashes are allowed + if (flags.IsBackslashAllowed() && + PathNormalizer::IsParentDirectoryPathReplacementNeeded(path)) { + *out = false; + R_SUCCEED(); + } + + // Check that the backslash state is valid + bool is_backslash_contained = false; + R_TRY(CheckInvalidBackslash(std::addressof(is_backslash_contained), path, + flags.IsWindowsPathAllowed() || flags.IsBackslashAllowed())); + + // Check that backslashes are contained only if allowed + if (is_backslash_contained && !flags.IsBackslashAllowed()) { + *out = false; + R_SUCCEED(); + } + + // Check that the final result path is normalized + size_t normal_len = 0; + R_TRY(PathNormalizer::IsNormalized(out, std::addressof(normal_len), path, + flags.IsAllCharactersAllowed())); + + // Add the normal length + total_len += normal_len; + + // Set the output length + *out_len = total_len; + R_SUCCEED(); + } + + static Result Normalize(char* dst, size_t dst_size, const char* path, size_t path_len, + const PathFlags& flags) { + // Use StringTraits names for remainder of scope + using namespace StringTraits; + + // Prepare to iterate + const char* src = path; + size_t cur_pos = 0; + bool is_windows_path = false; + + // Check if the path is empty + if (src[0] == NullTerminator) { + if (dst_size != 0) { + dst[0] = NullTerminator; + } + + R_UNLESS(flags.IsEmptyPathAllowed(), ResultInvalidPathFormat); + + R_SUCCEED(); + } + + // Handle a mount name + size_t mount_name_len = 0; + if (flags.IsMountNameAllowed()) { + R_TRY(ParseMountName(std::addressof(src), std::addressof(mount_name_len), dst + cur_pos, + dst_size - cur_pos, src)); + + cur_pos += mount_name_len; + } + + // Handle a drive-relative prefix + bool is_drive_relative = false; + if (src[0] != DirectorySeparator && !IsPathStartWithCurrentDirectory(src) && + !IsWindowsPath(src, false)) { + R_UNLESS(flags.IsRelativePathAllowed(), ResultInvalidPathFormat); + R_UNLESS(!IsInvalidCharacter(src[0]), ResultInvalidPathFormat); + + dst[cur_pos++] = Dot; + is_drive_relative = true; + } + + size_t relative_len = 0; + if (flags.IsRelativePathAllowed()) { + R_UNLESS(cur_pos < dst_size, ResultTooLongPath); + + R_TRY(ParseRelativeDotPath(std::addressof(src), std::addressof(relative_len), + dst + cur_pos, dst_size - cur_pos, src)); + + cur_pos += relative_len; + + if (src[0] == NullTerminator) { + R_UNLESS(cur_pos < dst_size, ResultTooLongPath); + + dst[cur_pos] = NullTerminator; + R_SUCCEED(); + } + } + + // Handle a windows path + if (flags.IsWindowsPathAllowed()) { + const char* const orig = src; + + R_UNLESS(cur_pos < dst_size, ResultTooLongPath); + + size_t windows_len = 0; + R_TRY(ParseWindowsPath(std::addressof(src), std::addressof(windows_len), dst + cur_pos, + dst_size - cur_pos, src, mount_name_len != 0)); + + cur_pos += windows_len; + + if (src[0] == NullTerminator) { + /* NOTE: Bug in original code here repeated, should be checking cur_pos + 2. */ + R_UNLESS(cur_pos + 1 < dst_size, ResultTooLongPath); + + dst[cur_pos + 0] = DirectorySeparator; + dst[cur_pos + 1] = NullTerminator; + R_SUCCEED(); + } + + if ((src - orig) > 0) { + is_windows_path = true; + } + } + + // Check for invalid backslash + bool backslash_contained = false; + R_TRY(CheckInvalidBackslash(std::addressof(backslash_contained), src, + flags.IsWindowsPathAllowed() || flags.IsBackslashAllowed())); + + // Handle backslash replacement as necessary + if (backslash_contained && flags.IsWindowsPathAllowed()) { + // Create a temporary buffer holding a slash-replaced version of the path. + // NOTE: Nintendo unnecessarily allocates and replaces here a fully copy of the path, + // despite having skipped some of it already. + const size_t replaced_src_len = path_len - (src - path); + + char* replaced_src = nullptr; + SCOPE_EXIT({ + if (replaced_src != nullptr) { + if (std::is_constant_evaluated()) { + delete[] replaced_src; + } else { + Deallocate(replaced_src, replaced_src_len); + } + } + }); + + if (std::is_constant_evaluated()) { + replaced_src = new char[replaced_src_len]; + } else { + replaced_src = static_cast<char*>(Allocate(replaced_src_len)); + } + + Strlcpy<char>(replaced_src, src, replaced_src_len); + + Replace(replaced_src, replaced_src_len, AlternateDirectorySeparator, + DirectorySeparator); + + size_t dummy; + R_TRY(PathNormalizer::Normalize(dst + cur_pos, std::addressof(dummy), replaced_src, + dst_size - cur_pos, is_windows_path, is_drive_relative, + flags.IsAllCharactersAllowed())); + } else { + // We can just do normalization + size_t dummy; + R_TRY(PathNormalizer::Normalize(dst + cur_pos, std::addressof(dummy), src, + dst_size - cur_pos, is_windows_path, is_drive_relative, + flags.IsAllCharactersAllowed())); + } + + R_SUCCEED(); + } +}; + +} // namespace FileSys diff --git a/src/core/file_sys/fs_string_util.h b/src/core/file_sys/fs_string_util.h new file mode 100644 index 000000000..874e09054 --- /dev/null +++ b/src/core/file_sys/fs_string_util.h @@ -0,0 +1,226 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/assert.h" + +namespace FileSys { + +template <typename T> +constexpr int Strlen(const T* str) { + ASSERT(str != nullptr); + + int length = 0; + while (*str++) { + ++length; + } + + return length; +} + +template <typename T> +constexpr int Strnlen(const T* str, int count) { + ASSERT(str != nullptr); + ASSERT(count >= 0); + + int length = 0; + while (count-- && *str++) { + ++length; + } + + return length; +} + +template <typename T> +constexpr int Strncmp(const T* lhs, const T* rhs, int count) { + ASSERT(lhs != nullptr); + ASSERT(rhs != nullptr); + ASSERT(count >= 0); + + if (count == 0) { + return 0; + } + + T l, r; + do { + l = *(lhs++); + r = *(rhs++); + } while (l && (l == r) && (--count)); + + return l - r; +} + +template <typename T> +static constexpr int Strlcpy(T* dst, const T* src, int count) { + ASSERT(dst != nullptr); + ASSERT(src != nullptr); + + const T* cur = src; + if (count > 0) { + while ((--count) && *cur) { + *(dst++) = *(cur++); + } + *dst = 0; + } + + while (*cur) { + cur++; + } + + return static_cast<int>(cur - src); +} + +enum CharacterEncodingResult { + CharacterEncodingResult_Success = 0, + CharacterEncodingResult_InsufficientLength = 1, + CharacterEncodingResult_InvalidFormat = 2, +}; + +namespace impl { + +class CharacterEncodingHelper { +public: + static constexpr int8_t Utf8NBytesInnerTable[0x100 + 1] = { + -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8, + }; + + static constexpr char GetUtf8NBytes(size_t i) { + return static_cast<char>(Utf8NBytesInnerTable[1 + i]); + } +}; + +} // namespace impl + +constexpr inline CharacterEncodingResult ConvertCharacterUtf8ToUtf32(u32* dst, const char* src) { + // Check pre-conditions + ASSERT(dst != nullptr); + ASSERT(src != nullptr); + + // Perform the conversion + const auto* p = src; + switch (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[0]))) { + case 1: + *dst = static_cast<u32>(p[0]); + return CharacterEncodingResult_Success; + case 2: + if ((static_cast<u32>(p[0]) & 0x1E) != 0) { + if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == + 0) { + *dst = (static_cast<u32>(p[0] & 0x1F) << 6) | (static_cast<u32>(p[1] & 0x3F) << 0); + return CharacterEncodingResult_Success; + } + } + break; + case 3: + if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0 && + impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[2])) == 0) { + const u32 c = (static_cast<u32>(p[0] & 0xF) << 12) | + (static_cast<u32>(p[1] & 0x3F) << 6) | + (static_cast<u32>(p[2] & 0x3F) << 0); + if ((c & 0xF800) != 0 && (c & 0xF800) != 0xD800) { + *dst = c; + return CharacterEncodingResult_Success; + } + } + return CharacterEncodingResult_InvalidFormat; + case 4: + if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0 && + impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[2])) == 0 && + impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[3])) == 0) { + const u32 c = + (static_cast<u32>(p[0] & 0x7) << 18) | (static_cast<u32>(p[1] & 0x3F) << 12) | + (static_cast<u32>(p[2] & 0x3F) << 6) | (static_cast<u32>(p[3] & 0x3F) << 0); + if (c >= 0x10000 && c < 0x110000) { + *dst = c; + return CharacterEncodingResult_Success; + } + } + return CharacterEncodingResult_InvalidFormat; + default: + break; + } + + // We failed to convert + return CharacterEncodingResult_InvalidFormat; +} + +constexpr inline CharacterEncodingResult PickOutCharacterFromUtf8String(char* dst, + const char** str) { + // Check pre-conditions + ASSERT(dst != nullptr); + ASSERT(str != nullptr); + ASSERT(*str != nullptr); + + // Clear the output + dst[0] = 0; + dst[1] = 0; + dst[2] = 0; + dst[3] = 0; + + // Perform the conversion + const auto* p = *str; + u32 c = static_cast<u32>(*p); + switch (impl::CharacterEncodingHelper::GetUtf8NBytes(c)) { + case 1: + dst[0] = (*str)[0]; + ++(*str); + break; + case 2: + if ((p[0] & 0x1E) != 0) { + if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == + 0) { + c = (static_cast<u32>(p[0] & 0x1F) << 6) | (static_cast<u32>(p[1] & 0x3F) << 0); + dst[0] = (*str)[0]; + dst[1] = (*str)[1]; + (*str) += 2; + break; + } + } + return CharacterEncodingResult_InvalidFormat; + case 3: + if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0 && + impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[2])) == 0) { + c = (static_cast<u32>(p[0] & 0xF) << 12) | (static_cast<u32>(p[1] & 0x3F) << 6) | + (static_cast<u32>(p[2] & 0x3F) << 0); + if ((c & 0xF800) != 0 && (c & 0xF800) != 0xD800) { + dst[0] = (*str)[0]; + dst[1] = (*str)[1]; + dst[2] = (*str)[2]; + (*str) += 3; + break; + } + } + return CharacterEncodingResult_InvalidFormat; + case 4: + if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0 && + impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[2])) == 0 && + impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[3])) == 0) { + c = (static_cast<u32>(p[0] & 0x7) << 18) | (static_cast<u32>(p[1] & 0x3F) << 12) | + (static_cast<u32>(p[2] & 0x3F) << 6) | (static_cast<u32>(p[3] & 0x3F) << 0); + if (c >= 0x10000 && c < 0x110000) { + dst[0] = (*str)[0]; + dst[1] = (*str)[1]; + dst[2] = (*str)[2]; + dst[3] = (*str)[3]; + (*str) += 4; + break; + } + } + return CharacterEncodingResult_InvalidFormat; + default: + return CharacterEncodingResult_InvalidFormat; + } + + return CharacterEncodingResult_Success; +} + +} // namespace FileSys diff --git a/src/core/file_sys/fsmitm_romfsbuild.cpp b/src/core/file_sys/fsmitm_romfsbuild.cpp index dd9cca103..8807bbd0f 100644 --- a/src/core/file_sys/fsmitm_romfsbuild.cpp +++ b/src/core/file_sys/fsmitm_romfsbuild.cpp @@ -8,8 +8,8 @@ #include "common/assert.h" #include "core/file_sys/fsmitm_romfsbuild.h" #include "core/file_sys/ips_layer.h" -#include "core/file_sys/vfs.h" -#include "core/file_sys/vfs_vector.h" +#include "core/file_sys/vfs/vfs.h" +#include "core/file_sys/vfs/vfs_vector.h" namespace FileSys { diff --git a/src/core/file_sys/fsmitm_romfsbuild.h b/src/core/file_sys/fsmitm_romfsbuild.h index f387c79f1..dd7ed4a7b 100644 --- a/src/core/file_sys/fsmitm_romfsbuild.h +++ b/src/core/file_sys/fsmitm_romfsbuild.h @@ -7,7 +7,7 @@ #include <memory> #include <string> #include "common/common_types.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/fssystem/fs_i_storage.h b/src/core/file_sys/fssystem/fs_i_storage.h index 416dd57b8..37336c9ae 100644 --- a/src/core/file_sys/fssystem/fs_i_storage.h +++ b/src/core/file_sys/fssystem/fs_i_storage.h @@ -5,7 +5,7 @@ #include "common/overflow.h" #include "core/file_sys/errors.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp b/src/core/file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp index f25c95472..bc1cddbb0 100644 --- a/src/core/file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp +++ b/src/core/file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp @@ -4,7 +4,7 @@ #include "core/file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.h" #include "core/file_sys/fssystem/fssystem_aes_ctr_storage.h" #include "core/file_sys/fssystem/fssystem_nca_header.h" -#include "core/file_sys/vfs_offset.h" +#include "core/file_sys/vfs/vfs_offset.h" namespace FileSys { diff --git a/src/core/file_sys/fssystem/fssystem_aes_ctr_storage.h b/src/core/file_sys/fssystem/fssystem_aes_ctr_storage.h index 339e49697..5abd93d33 100644 --- a/src/core/file_sys/fssystem/fssystem_aes_ctr_storage.h +++ b/src/core/file_sys/fssystem/fssystem_aes_ctr_storage.h @@ -9,7 +9,7 @@ #include "core/crypto/key_manager.h" #include "core/file_sys/errors.h" #include "core/file_sys/fssystem/fs_i_storage.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/fssystem/fssystem_bucket_tree.h b/src/core/file_sys/fssystem/fssystem_bucket_tree.h index 46850cd48..3a5e21d1a 100644 --- a/src/core/file_sys/fssystem/fssystem_bucket_tree.h +++ b/src/core/file_sys/fssystem/fssystem_bucket_tree.h @@ -10,7 +10,7 @@ #include "common/common_types.h" #include "common/literals.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" #include "core/hle/result.h" namespace FileSys { diff --git a/src/core/file_sys/fssystem/fssystem_compressed_storage.h b/src/core/file_sys/fssystem/fssystem_compressed_storage.h index 33d93938e..74c98630e 100644 --- a/src/core/file_sys/fssystem/fssystem_compressed_storage.h +++ b/src/core/file_sys/fssystem/fssystem_compressed_storage.h @@ -10,7 +10,7 @@ #include "core/file_sys/fssystem/fssystem_bucket_tree.h" #include "core/file_sys/fssystem/fssystem_compression_common.h" #include "core/file_sys/fssystem/fssystem_pooled_buffer.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.cpp b/src/core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.cpp index 4a75b5308..39bb7b808 100644 --- a/src/core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.cpp +++ b/src/core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h" -#include "core/file_sys/vfs_offset.h" +#include "core/file_sys/vfs/vfs_offset.h" namespace FileSys { diff --git a/src/core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h b/src/core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h index 5cf697efe..bd129db47 100644 --- a/src/core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h +++ b/src/core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h @@ -8,7 +8,7 @@ #include "core/file_sys/fssystem/fs_types.h" #include "core/file_sys/fssystem/fssystem_alignment_matching_storage.h" #include "core/file_sys/fssystem/fssystem_integrity_verification_storage.h" -#include "core/file_sys/vfs_offset.h" +#include "core/file_sys/vfs/vfs_offset.h" namespace FileSys { diff --git a/src/core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.h b/src/core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.h index 18df400af..41d3960b8 100644 --- a/src/core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.h +++ b/src/core/file_sys/fssystem/fssystem_hierarchical_sha256_storage.h @@ -7,7 +7,7 @@ #include "core/file_sys/errors.h" #include "core/file_sys/fssystem/fs_i_storage.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/fssystem/fssystem_indirect_storage.h b/src/core/file_sys/fssystem/fssystem_indirect_storage.h index 7854335bf..d4b95fd27 100644 --- a/src/core/file_sys/fssystem/fssystem_indirect_storage.h +++ b/src/core/file_sys/fssystem/fssystem_indirect_storage.h @@ -7,8 +7,8 @@ #include "core/file_sys/fssystem/fs_i_storage.h" #include "core/file_sys/fssystem/fssystem_bucket_tree.h" #include "core/file_sys/fssystem/fssystem_bucket_tree_template_impl.h" -#include "core/file_sys/vfs.h" -#include "core/file_sys/vfs_offset.h" +#include "core/file_sys/vfs/vfs.h" +#include "core/file_sys/vfs/vfs_offset.h" namespace FileSys { diff --git a/src/core/file_sys/fssystem/fssystem_integrity_romfs_storage.h b/src/core/file_sys/fssystem/fssystem_integrity_romfs_storage.h index 5f8512b2a..240d1e388 100644 --- a/src/core/file_sys/fssystem/fssystem_integrity_romfs_storage.h +++ b/src/core/file_sys/fssystem/fssystem_integrity_romfs_storage.h @@ -5,7 +5,7 @@ #include "core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h" #include "core/file_sys/fssystem/fssystem_nca_header.h" -#include "core/file_sys/vfs_vector.h" +#include "core/file_sys/vfs/vfs_vector.h" namespace FileSys { diff --git a/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.cpp b/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.cpp index 0f5432203..ab5a7984e 100644 --- a/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.cpp +++ b/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.cpp @@ -14,8 +14,8 @@ #include "core/file_sys/fssystem/fssystem_nca_file_system_driver.h" #include "core/file_sys/fssystem/fssystem_sparse_storage.h" #include "core/file_sys/fssystem/fssystem_switch_storage.h" -#include "core/file_sys/vfs_offset.h" -#include "core/file_sys/vfs_vector.h" +#include "core/file_sys/vfs/vfs_offset.h" +#include "core/file_sys/vfs/vfs_vector.h" namespace FileSys { diff --git a/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.h b/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.h index 5771a21fc..5bc838de6 100644 --- a/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.h +++ b/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.h @@ -5,7 +5,7 @@ #include "core/file_sys/fssystem/fssystem_compression_common.h" #include "core/file_sys/fssystem/fssystem_nca_header.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/fssystem/fssystem_nca_reader.cpp b/src/core/file_sys/fssystem/fssystem_nca_reader.cpp index a3714ab37..08924e2a6 100644 --- a/src/core/file_sys/fssystem/fssystem_nca_reader.cpp +++ b/src/core/file_sys/fssystem/fssystem_nca_reader.cpp @@ -3,7 +3,7 @@ #include "core/file_sys/fssystem/fssystem_aes_xts_storage.h" #include "core/file_sys/fssystem/fssystem_nca_file_system_driver.h" -#include "core/file_sys/vfs_offset.h" +#include "core/file_sys/vfs/vfs_offset.h" namespace FileSys { diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp index 31033634c..d1ac24072 100644 --- a/src/core/file_sys/ips_layer.cpp +++ b/src/core/file_sys/ips_layer.cpp @@ -12,7 +12,7 @@ #include "common/logging/log.h" #include "common/swap.h" #include "core/file_sys/ips_layer.h" -#include "core/file_sys/vfs_vector.h" +#include "core/file_sys/vfs/vfs_vector.h" namespace FileSys { diff --git a/src/core/file_sys/ips_layer.h b/src/core/file_sys/ips_layer.h index f2717bae7..d81378e8a 100644 --- a/src/core/file_sys/ips_layer.h +++ b/src/core/file_sys/ips_layer.h @@ -8,7 +8,7 @@ #include <vector> #include "common/common_types.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/kernel_executable.cpp b/src/core/file_sys/kernel_executable.cpp index 70c062f4c..b84492d30 100644 --- a/src/core/file_sys/kernel_executable.cpp +++ b/src/core/file_sys/kernel_executable.cpp @@ -5,7 +5,7 @@ #include "common/string_util.h" #include "core/file_sys/kernel_executable.h" -#include "core/file_sys/vfs_offset.h" +#include "core/file_sys/vfs/vfs_offset.h" #include "core/loader/loader.h" namespace FileSys { diff --git a/src/core/file_sys/kernel_executable.h b/src/core/file_sys/kernel_executable.h index d5b9199b5..928ba2d99 100644 --- a/src/core/file_sys/kernel_executable.h +++ b/src/core/file_sys/kernel_executable.h @@ -10,7 +10,7 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" namespace Loader { enum class ResultStatus : u16; diff --git a/src/core/file_sys/mode.h b/src/core/file_sys/mode.h deleted file mode 100644 index 9596ef4fd..000000000 --- a/src/core/file_sys/mode.h +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/common_funcs.h" -#include "common/common_types.h" - -namespace FileSys { - -enum class Mode : u32 { - Read = 1 << 0, - Write = 1 << 1, - ReadWrite = Read | Write, - Append = 1 << 2, - ReadAppend = Read | Append, - WriteAppend = Write | Append, - All = ReadWrite | Append, -}; - -DECLARE_ENUM_FLAG_OPERATORS(Mode) - -} // namespace FileSys diff --git a/src/core/file_sys/nca_metadata.cpp b/src/core/file_sys/nca_metadata.cpp index f4a774675..9e855c50d 100644 --- a/src/core/file_sys/nca_metadata.cpp +++ b/src/core/file_sys/nca_metadata.cpp @@ -6,7 +6,7 @@ #include "common/logging/log.h" #include "common/swap.h" #include "core/file_sys/nca_metadata.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h index 68e463b5f..6243b822a 100644 --- a/src/core/file_sys/nca_metadata.h +++ b/src/core/file_sys/nca_metadata.h @@ -8,7 +8,7 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" namespace FileSys { class CNMT; diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp index 2422cb51b..dd8de9d8a 100644 --- a/src/core/file_sys/partition_filesystem.cpp +++ b/src/core/file_sys/partition_filesystem.cpp @@ -9,7 +9,7 @@ #include "common/logging/log.h" #include "core/file_sys/partition_filesystem.h" -#include "core/file_sys/vfs_offset.h" +#include "core/file_sys/vfs/vfs_offset.h" #include "core/loader/loader.h" namespace FileSys { diff --git a/src/core/file_sys/partition_filesystem.h b/src/core/file_sys/partition_filesystem.h index b6e3a2b0c..777b9ead9 100644 --- a/src/core/file_sys/partition_filesystem.h +++ b/src/core/file_sys/partition_filesystem.h @@ -9,7 +9,7 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace Loader { enum class ResultStatus : u16; diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 612122224..21d45235e 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -21,9 +21,9 @@ #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" #include "core/file_sys/romfs.h" -#include "core/file_sys/vfs_cached.h" -#include "core/file_sys/vfs_layered.h" -#include "core/file_sys/vfs_vector.h" +#include "core/file_sys/vfs/vfs_cached.h" +#include "core/file_sys/vfs/vfs_layered.h" +#include "core/file_sys/vfs/vfs_vector.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/ns/language.h" #include "core/hle/service/set/settings_server.h" diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h index 2601b8217..552c0fbe2 100644 --- a/src/core/file_sys/patch_manager.h +++ b/src/core/file_sys/patch_manager.h @@ -9,7 +9,7 @@ #include <string> #include "common/common_types.h" #include "core/file_sys/nca_metadata.h" -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" #include "core/memory/dmnt_cheat_types.h" namespace Core { diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index 539c7f7af..ae4e441c9 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp @@ -7,7 +7,7 @@ #include "common/logging/log.h" #include "common/scope_exit.h" #include "core/file_sys/program_metadata.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" #include "core/loader/loader.h" namespace FileSys { diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h index a53092b87..115e6d6cd 100644 --- a/src/core/file_sys/program_metadata.h +++ b/src/core/file_sys/program_metadata.h @@ -10,7 +10,7 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" namespace Loader { enum class ResultStatus : u16; diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index 1cc77ad14..85d30543c 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -17,7 +17,7 @@ #include "core/file_sys/nca_metadata.h" #include "core/file_sys/registered_cache.h" #include "core/file_sys/submission_package.h" -#include "core/file_sys/vfs_concat.h" +#include "core/file_sys/vfs/vfs_concat.h" #include "core/loader/loader.h" namespace FileSys { diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index 64815a845..a7fc55673 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h @@ -11,7 +11,7 @@ #include <boost/container/flat_map.hpp> #include "common/common_types.h" #include "core/crypto/key_manager.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { class CNMT; diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp index 6182598ae..a2b280973 100644 --- a/src/core/file_sys/romfs.cpp +++ b/src/core/file_sys/romfs.cpp @@ -9,11 +9,11 @@ #include "common/swap.h" #include "core/file_sys/fsmitm_romfsbuild.h" #include "core/file_sys/romfs.h" -#include "core/file_sys/vfs.h" -#include "core/file_sys/vfs_cached.h" -#include "core/file_sys/vfs_concat.h" -#include "core/file_sys/vfs_offset.h" -#include "core/file_sys/vfs_vector.h" +#include "core/file_sys/vfs/vfs.h" +#include "core/file_sys/vfs/vfs_cached.h" +#include "core/file_sys/vfs/vfs_concat.h" +#include "core/file_sys/vfs/vfs_offset.h" +#include "core/file_sys/vfs/vfs_vector.h" namespace FileSys { namespace { diff --git a/src/core/file_sys/romfs.h b/src/core/file_sys/romfs.h index b75ff1aad..3c0aca291 100644 --- a/src/core/file_sys/romfs.h +++ b/src/core/file_sys/romfs.h @@ -3,7 +3,7 @@ #pragma once -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h index e4809bc94..11ecfabdf 100644 --- a/src/core/file_sys/romfs_factory.h +++ b/src/core/file_sys/romfs_factory.h @@ -6,7 +6,7 @@ #include <memory> #include "common/common_types.h" -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" #include "core/hle/result.h" namespace Loader { diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index 23196cd5f..cbf411a20 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp @@ -8,7 +8,7 @@ #include "common/uuid.h" #include "core/core.h" #include "core/file_sys/savedata_factory.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h index 30d96928e..5ab7e4d32 100644 --- a/src/core/file_sys/savedata_factory.h +++ b/src/core/file_sys/savedata_factory.h @@ -7,7 +7,7 @@ #include <string> #include "common/common_funcs.h" #include "common/common_types.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" #include "core/hle/result.h" namespace Core { diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp index d5158cd64..f3e2e21f4 100644 --- a/src/core/file_sys/sdmc_factory.cpp +++ b/src/core/file_sys/sdmc_factory.cpp @@ -4,7 +4,7 @@ #include <memory> #include "core/file_sys/registered_cache.h" #include "core/file_sys/sdmc_factory.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" #include "core/file_sys/xts_archive.h" namespace FileSys { diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h index a445fdb16..ee69ccd07 100644 --- a/src/core/file_sys/sdmc_factory.h +++ b/src/core/file_sys/sdmc_factory.h @@ -4,7 +4,7 @@ #pragma once #include <memory> -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" #include "core/hle/result.h" namespace FileSys { diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h index 915bffca9..935e9589d 100644 --- a/src/core/file_sys/submission_package.h +++ b/src/core/file_sys/submission_package.h @@ -9,7 +9,7 @@ #include <vector> #include "common/common_types.h" #include "core/file_sys/nca_metadata.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace Core::Crypto { class KeyManager; diff --git a/src/core/file_sys/system_archive/mii_model.cpp b/src/core/file_sys/system_archive/mii_model.cpp index 5c87b42f8..a96cb2cd2 100644 --- a/src/core/file_sys/system_archive/mii_model.cpp +++ b/src/core/file_sys/system_archive/mii_model.cpp @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "core/file_sys/system_archive/mii_model.h" -#include "core/file_sys/vfs_vector.h" +#include "core/file_sys/vfs/vfs_vector.h" namespace FileSys::SystemArchive { diff --git a/src/core/file_sys/system_archive/mii_model.h b/src/core/file_sys/system_archive/mii_model.h index b6cbefe24..61723ed0d 100644 --- a/src/core/file_sys/system_archive/mii_model.h +++ b/src/core/file_sys/system_archive/mii_model.h @@ -3,7 +3,7 @@ #pragma once -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" namespace FileSys::SystemArchive { diff --git a/src/core/file_sys/system_archive/ng_word.cpp b/src/core/file_sys/system_archive/ng_word.cpp index 5cf6749da..1fa67877d 100644 --- a/src/core/file_sys/system_archive/ng_word.cpp +++ b/src/core/file_sys/system_archive/ng_word.cpp @@ -4,7 +4,7 @@ #include <fmt/format.h> #include "common/common_types.h" #include "core/file_sys/system_archive/ng_word.h" -#include "core/file_sys/vfs_vector.h" +#include "core/file_sys/vfs/vfs_vector.h" namespace FileSys::SystemArchive { diff --git a/src/core/file_sys/system_archive/ng_word.h b/src/core/file_sys/system_archive/ng_word.h index 1d7b49532..51bcc3327 100644 --- a/src/core/file_sys/system_archive/ng_word.h +++ b/src/core/file_sys/system_archive/ng_word.h @@ -3,7 +3,7 @@ #pragma once -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" namespace FileSys::SystemArchive { diff --git a/src/core/file_sys/system_archive/shared_font.cpp b/src/core/file_sys/system_archive/shared_font.cpp index 3210583f0..deb52069d 100644 --- a/src/core/file_sys/system_archive/shared_font.cpp +++ b/src/core/file_sys/system_archive/shared_font.cpp @@ -8,7 +8,7 @@ #include "core/file_sys/system_archive/data/font_nintendo_extended.h" #include "core/file_sys/system_archive/data/font_standard.h" #include "core/file_sys/system_archive/shared_font.h" -#include "core/file_sys/vfs_vector.h" +#include "core/file_sys/vfs/vfs_vector.h" #include "core/hle/service/ns/iplatform_service_manager.h" namespace FileSys::SystemArchive { diff --git a/src/core/file_sys/system_archive/shared_font.h b/src/core/file_sys/system_archive/shared_font.h index d1cd1dc44..2d19fcde3 100644 --- a/src/core/file_sys/system_archive/shared_font.h +++ b/src/core/file_sys/system_archive/shared_font.h @@ -3,7 +3,7 @@ #pragma once -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" namespace FileSys::SystemArchive { diff --git a/src/core/file_sys/system_archive/system_archive.cpp b/src/core/file_sys/system_archive/system_archive.cpp index 6abac793b..b53eef877 100644 --- a/src/core/file_sys/system_archive/system_archive.cpp +++ b/src/core/file_sys/system_archive/system_archive.cpp @@ -67,25 +67,29 @@ constexpr std::array<SystemArchiveDescriptor, SYSTEM_ARCHIVE_COUNT> SYSTEM_ARCHI }}; VirtualFile SynthesizeSystemArchive(const u64 title_id) { - if (title_id < SYSTEM_ARCHIVES.front().title_id || title_id > SYSTEM_ARCHIVES.back().title_id) + if (title_id < SYSTEM_ARCHIVES.front().title_id || title_id > SYSTEM_ARCHIVES.back().title_id) { return nullptr; + } const auto& desc = SYSTEM_ARCHIVES[title_id - SYSTEM_ARCHIVE_BASE_TITLE_ID]; LOG_INFO(Service_FS, "Synthesizing system archive '{}' (0x{:016X}).", desc.name, desc.title_id); - if (desc.supplier == nullptr) + if (desc.supplier == nullptr) { return nullptr; + } const auto dir = desc.supplier(); - if (dir == nullptr) + if (dir == nullptr) { return nullptr; + } const auto romfs = CreateRomFS(dir); - if (romfs == nullptr) + if (romfs == nullptr) { return nullptr; + } LOG_INFO(Service_FS, " - System archive generation successful!"); return romfs; diff --git a/src/core/file_sys/system_archive/system_archive.h b/src/core/file_sys/system_archive/system_archive.h index 02d9157bb..2f64247bc 100644 --- a/src/core/file_sys/system_archive/system_archive.h +++ b/src/core/file_sys/system_archive/system_archive.h @@ -4,7 +4,7 @@ #pragma once #include "common/common_types.h" -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" namespace FileSys::SystemArchive { diff --git a/src/core/file_sys/system_archive/system_version.cpp b/src/core/file_sys/system_archive/system_version.cpp index e4751c2b4..5662004b7 100644 --- a/src/core/file_sys/system_archive/system_version.cpp +++ b/src/core/file_sys/system_archive/system_version.cpp @@ -3,7 +3,7 @@ #include "common/logging/log.h" #include "core/file_sys/system_archive/system_version.h" -#include "core/file_sys/vfs_vector.h" +#include "core/file_sys/vfs/vfs_vector.h" #include "core/hle/api_version.h" namespace FileSys::SystemArchive { diff --git a/src/core/file_sys/system_archive/system_version.h b/src/core/file_sys/system_archive/system_version.h index 21b5514a9..e5f7b952e 100644 --- a/src/core/file_sys/system_archive/system_version.h +++ b/src/core/file_sys/system_archive/system_version.h @@ -4,7 +4,7 @@ #pragma once #include <string> -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" namespace FileSys::SystemArchive { 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 7c17bbefa..316ff0dc6 100644 --- a/src/core/file_sys/system_archive/time_zone_binary.cpp +++ b/src/core/file_sys/system_archive/time_zone_binary.cpp @@ -5,8 +5,7 @@ #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 "core/file_sys/vfs/vfs_vector.h" #include "nx_tzdb.h" diff --git a/src/core/file_sys/system_archive/time_zone_binary.h b/src/core/file_sys/system_archive/time_zone_binary.h index d0e1a4acd..e44fc5007 100644 --- a/src/core/file_sys/system_archive/time_zone_binary.h +++ b/src/core/file_sys/system_archive/time_zone_binary.h @@ -3,7 +3,7 @@ #pragma once -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" namespace FileSys::SystemArchive { diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs/vfs.cpp index b7105c8ff..a04292760 100644 --- a/src/core/file_sys/vfs.cpp +++ b/src/core/file_sys/vfs/vfs.cpp @@ -5,8 +5,7 @@ #include <numeric> #include <string> #include "common/fs/path_util.h" -#include "core/file_sys/mode.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { @@ -36,12 +35,12 @@ VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const { return VfsEntryType::None; } -VirtualFile VfsFilesystem::OpenFile(std::string_view path_, Mode perms) { +VirtualFile VfsFilesystem::OpenFile(std::string_view path_, OpenMode perms) { const auto path = Common::FS::SanitizePath(path_); return root->GetFileRelative(path); } -VirtualFile VfsFilesystem::CreateFile(std::string_view path_, Mode perms) { +VirtualFile VfsFilesystem::CreateFile(std::string_view path_, OpenMode perms) { const auto path = Common::FS::SanitizePath(path_); return root->CreateFileRelative(path); } @@ -54,17 +53,17 @@ VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view if (Common::FS::GetParentPath(old_path) == Common::FS::GetParentPath(new_path)) { if (!root->Copy(Common::FS::GetFilename(old_path), Common::FS::GetFilename(new_path))) return nullptr; - return OpenFile(new_path, Mode::ReadWrite); + return OpenFile(new_path, OpenMode::ReadWrite); } // Do it using RawCopy. Non-default impls are encouraged to optimize this. - const auto old_file = OpenFile(old_path, Mode::Read); + const auto old_file = OpenFile(old_path, OpenMode::Read); if (old_file == nullptr) return nullptr; - auto new_file = OpenFile(new_path, Mode::Read); + auto new_file = OpenFile(new_path, OpenMode::Read); if (new_file != nullptr) return nullptr; - new_file = CreateFile(new_path, Mode::Write); + new_file = CreateFile(new_path, OpenMode::Write); if (new_file == nullptr) return nullptr; if (!VfsRawCopy(old_file, new_file)) @@ -87,18 +86,18 @@ VirtualFile VfsFilesystem::MoveFile(std::string_view old_path, std::string_view bool VfsFilesystem::DeleteFile(std::string_view path_) { const auto path = Common::FS::SanitizePath(path_); - auto parent = OpenDirectory(Common::FS::GetParentPath(path), Mode::Write); + auto parent = OpenDirectory(Common::FS::GetParentPath(path), OpenMode::Write); if (parent == nullptr) return false; return parent->DeleteFile(Common::FS::GetFilename(path)); } -VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { +VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, OpenMode perms) { const auto path = Common::FS::SanitizePath(path_); return root->GetDirectoryRelative(path); } -VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { +VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) { const auto path = Common::FS::SanitizePath(path_); return root->CreateDirectoryRelative(path); } @@ -108,13 +107,13 @@ VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_ const auto new_path = Common::FS::SanitizePath(new_path_); // Non-default impls are highly encouraged to provide a more optimized version of this. - auto old_dir = OpenDirectory(old_path, Mode::Read); + auto old_dir = OpenDirectory(old_path, OpenMode::Read); if (old_dir == nullptr) return nullptr; - auto new_dir = OpenDirectory(new_path, Mode::Read); + auto new_dir = OpenDirectory(new_path, OpenMode::Read); if (new_dir != nullptr) return nullptr; - new_dir = CreateDirectory(new_path, Mode::Write); + new_dir = CreateDirectory(new_path, OpenMode::Write); if (new_dir == nullptr) return nullptr; @@ -149,7 +148,7 @@ VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path, std::string_v bool VfsFilesystem::DeleteDirectory(std::string_view path_) { const auto path = Common::FS::SanitizePath(path_); - auto parent = OpenDirectory(Common::FS::GetParentPath(path), Mode::Write); + auto parent = OpenDirectory(Common::FS::GetParentPath(path), OpenMode::Write); if (parent == nullptr) return false; return parent->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path)); diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs/vfs.h index a7cd1cae3..f846a9669 100644 --- a/src/core/file_sys/vfs.h +++ b/src/core/file_sys/vfs/vfs.h @@ -13,12 +13,11 @@ #include "common/common_funcs.h" #include "common/common_types.h" -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/fs_filesystem.h" +#include "core/file_sys/vfs/vfs_types.h" namespace FileSys { -enum class Mode : u32; - // An enumeration representing what can be at the end of a path in a VfsFilesystem enum class VfsEntryType { None, @@ -49,9 +48,9 @@ public: virtual VfsEntryType GetEntryType(std::string_view path) const; // Opens the file with path relative to root. If it doesn't exist, returns nullptr. - virtual VirtualFile OpenFile(std::string_view path, Mode perms); + virtual VirtualFile OpenFile(std::string_view path, OpenMode perms); // Creates a new, empty file at path - virtual VirtualFile CreateFile(std::string_view path, Mode perms); + virtual VirtualFile CreateFile(std::string_view path, OpenMode perms); // Copies the file from old_path to new_path, returning the new file on success and nullptr on // failure. virtual VirtualFile CopyFile(std::string_view old_path, std::string_view new_path); @@ -62,9 +61,9 @@ public: virtual bool DeleteFile(std::string_view path); // Opens the directory with path relative to root. If it doesn't exist, returns nullptr. - virtual VirtualDir OpenDirectory(std::string_view path, Mode perms); + virtual VirtualDir OpenDirectory(std::string_view path, OpenMode perms); // Creates a new, empty directory at path - virtual VirtualDir CreateDirectory(std::string_view path, Mode perms); + virtual VirtualDir CreateDirectory(std::string_view path, OpenMode perms); // Copies the directory from old_path to new_path, returning the new directory on success and // nullptr on failure. virtual VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path); diff --git a/src/core/file_sys/vfs_cached.cpp b/src/core/file_sys/vfs/vfs_cached.cpp index 7ee5300e5..01cd0f1e0 100644 --- a/src/core/file_sys/vfs_cached.cpp +++ b/src/core/file_sys/vfs/vfs_cached.cpp @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "core/file_sys/vfs_cached.h" -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_cached.h" +#include "core/file_sys/vfs/vfs_types.h" namespace FileSys { diff --git a/src/core/file_sys/vfs_cached.h b/src/core/file_sys/vfs/vfs_cached.h index 1e5300784..47dff7224 100644 --- a/src/core/file_sys/vfs_cached.h +++ b/src/core/file_sys/vfs/vfs_cached.h @@ -5,7 +5,7 @@ #include <string_view> #include <vector> -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/vfs_concat.cpp b/src/core/file_sys/vfs/vfs_concat.cpp index 7c7298527..b5cc9a9e9 100644 --- a/src/core/file_sys/vfs_concat.cpp +++ b/src/core/file_sys/vfs/vfs_concat.cpp @@ -5,8 +5,8 @@ #include <utility> #include "common/assert.h" -#include "core/file_sys/vfs_concat.h" -#include "core/file_sys/vfs_static.h" +#include "core/file_sys/vfs/vfs_concat.h" +#include "core/file_sys/vfs/vfs_static.h" namespace FileSys { diff --git a/src/core/file_sys/vfs_concat.h b/src/core/file_sys/vfs/vfs_concat.h index b5f3d72e3..6d12af762 100644 --- a/src/core/file_sys/vfs_concat.h +++ b/src/core/file_sys/vfs/vfs_concat.h @@ -6,7 +6,7 @@ #include <compare> #include <map> #include <memory> -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/vfs_layered.cpp b/src/core/file_sys/vfs/vfs_layered.cpp index 5551743fb..47b2a3c78 100644 --- a/src/core/file_sys/vfs_layered.cpp +++ b/src/core/file_sys/vfs/vfs_layered.cpp @@ -5,7 +5,7 @@ #include <set> #include <unordered_set> #include <utility> -#include "core/file_sys/vfs_layered.h" +#include "core/file_sys/vfs/vfs_layered.h" namespace FileSys { diff --git a/src/core/file_sys/vfs_layered.h b/src/core/file_sys/vfs/vfs_layered.h index a62112e9d..0027ffa9a 100644 --- a/src/core/file_sys/vfs_layered.h +++ b/src/core/file_sys/vfs/vfs_layered.h @@ -4,7 +4,7 @@ #pragma once #include <memory> -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs/vfs_offset.cpp index d950a6633..1a37d2670 100644 --- a/src/core/file_sys/vfs_offset.cpp +++ b/src/core/file_sys/vfs/vfs_offset.cpp @@ -4,7 +4,7 @@ #include <algorithm> #include <utility> -#include "core/file_sys/vfs_offset.h" +#include "core/file_sys/vfs/vfs_offset.h" namespace FileSys { diff --git a/src/core/file_sys/vfs_offset.h b/src/core/file_sys/vfs/vfs_offset.h index 6c051ca00..4abe41d8e 100644 --- a/src/core/file_sys/vfs_offset.h +++ b/src/core/file_sys/vfs/vfs_offset.h @@ -5,7 +5,7 @@ #include <memory> -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs/vfs_real.cpp index cd9b79786..627d5d251 100644 --- a/src/core/file_sys/vfs_real.cpp +++ b/src/core/file_sys/vfs/vfs_real.cpp @@ -10,8 +10,8 @@ #include "common/fs/fs.h" #include "common/fs/path_util.h" #include "common/logging/log.h" -#include "core/file_sys/vfs.h" -#include "core/file_sys/vfs_real.h" +#include "core/file_sys/vfs/vfs.h" +#include "core/file_sys/vfs/vfs_real.h" // For FileTimeStampRaw #include <sys/stat.h> @@ -28,16 +28,14 @@ namespace { constexpr size_t MaxOpenFiles = 512; -constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(Mode mode) { +constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(OpenMode mode) { switch (mode) { - case Mode::Read: + case OpenMode::Read: return FS::FileAccessMode::Read; - case Mode::Write: - case Mode::ReadWrite: - case Mode::Append: - case Mode::ReadAppend: - case Mode::WriteAppend: - case Mode::All: + case OpenMode::Write: + case OpenMode::ReadWrite: + case OpenMode::AllowAppend: + case OpenMode::All: return FS::FileAccessMode::ReadWrite; default: return {}; @@ -74,7 +72,7 @@ VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const { } VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::optional<u64> size, - Mode perms) { + OpenMode perms) { const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); std::scoped_lock lk{list_lock}; @@ -98,11 +96,11 @@ VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::op return file; } -VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { +VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, OpenMode perms) { return OpenFileFromEntry(path_, {}, perms); } -VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) { +VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, OpenMode perms) { const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); { std::scoped_lock lk{list_lock}; @@ -145,7 +143,7 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_ if (!FS::RenameFile(old_path, new_path)) { return nullptr; } - return OpenFile(new_path, Mode::ReadWrite); + return OpenFile(new_path, OpenMode::ReadWrite); } bool RealVfsFilesystem::DeleteFile(std::string_view path_) { @@ -157,12 +155,12 @@ bool RealVfsFilesystem::DeleteFile(std::string_view path_) { return FS::RemoveFile(path); } -VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { +VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, OpenMode perms) { const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); } -VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { +VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) { const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); if (!FS::CreateDirs(path)) { return nullptr; @@ -184,7 +182,7 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_, if (!FS::RenameDir(old_path, new_path)) { return nullptr; } - return OpenDirectory(new_path, Mode::ReadWrite); + return OpenDirectory(new_path, OpenMode::ReadWrite); } bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) { @@ -193,7 +191,7 @@ bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) { } std::unique_lock<std::mutex> RealVfsFilesystem::RefreshReference(const std::string& path, - Mode perms, + OpenMode perms, FileReference& reference) { std::unique_lock lk{list_lock}; @@ -266,7 +264,7 @@ void RealVfsFilesystem::RemoveReferenceFromListLocked(FileReference& reference) } RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::unique_ptr<FileReference> reference_, - const std::string& path_, Mode perms_, std::optional<u64> size_) + const std::string& path_, OpenMode perms_, std::optional<u64> size_) : base(base_), reference(std::move(reference_)), path(path_), parent_path(FS::GetParentPath(path_)), path_components(FS::SplitPathComponentsCopy(path_)), size(size_), perms(perms_) {} @@ -298,11 +296,11 @@ VirtualDir RealVfsFile::GetContainingDirectory() const { } bool RealVfsFile::IsWritable() const { - return True(perms & Mode::Write); + return True(perms & OpenMode::Write); } bool RealVfsFile::IsReadable() const { - return True(perms & Mode::Read); + return True(perms & OpenMode::Read); } std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { @@ -331,7 +329,7 @@ bool RealVfsFile::Rename(std::string_view name) { template <> std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>() const { - if (perms == Mode::Append) { + if (perms == OpenMode::AllowAppend) { return {}; } @@ -353,7 +351,7 @@ std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>( template <> std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDirectory>() const { - if (perms == Mode::Append) { + if (perms == OpenMode::AllowAppend) { return {}; } @@ -373,10 +371,11 @@ std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDi return out; } -RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_) +RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, + OpenMode perms_) : base(base_), path(FS::RemoveTrailingSlash(path_)), parent_path(FS::GetParentPath(path)), path_components(FS::SplitPathComponentsCopy(path)), perms(perms_) { - if (!FS::Exists(path) && True(perms & Mode::Write)) { + if (!FS::Exists(path) && True(perms & OpenMode::Write)) { void(FS::CreateDirs(path)); } } @@ -456,11 +455,11 @@ std::vector<VirtualDir> RealVfsDirectory::GetSubdirectories() const { } bool RealVfsDirectory::IsWritable() const { - return True(perms & Mode::Write); + return True(perms & OpenMode::Write); } bool RealVfsDirectory::IsReadable() const { - return True(perms & Mode::Read); + return True(perms & OpenMode::Read); } std::string RealVfsDirectory::GetName() const { @@ -507,7 +506,7 @@ std::string RealVfsDirectory::GetFullPath() const { } std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() const { - if (perms == Mode::Append) { + if (perms == OpenMode::AllowAppend) { return {}; } diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs/vfs_real.h index 26ea7df62..5c2172cce 100644 --- a/src/core/file_sys/vfs_real.h +++ b/src/core/file_sys/vfs/vfs_real.h @@ -8,8 +8,8 @@ #include <optional> #include <string_view> #include "common/intrusive_list.h" -#include "core/file_sys/mode.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/fs_filesystem.h" +#include "core/file_sys/vfs/vfs.h" namespace Common::FS { class IOFile; @@ -33,13 +33,14 @@ public: bool IsReadable() const override; bool IsWritable() const override; VfsEntryType GetEntryType(std::string_view path) const override; - VirtualFile OpenFile(std::string_view path, Mode perms = Mode::Read) override; - VirtualFile CreateFile(std::string_view path, Mode perms = Mode::ReadWrite) override; + VirtualFile OpenFile(std::string_view path, OpenMode perms = OpenMode::Read) override; + VirtualFile CreateFile(std::string_view path, OpenMode perms = OpenMode::ReadWrite) override; VirtualFile CopyFile(std::string_view old_path, std::string_view new_path) override; VirtualFile MoveFile(std::string_view old_path, std::string_view new_path) override; bool DeleteFile(std::string_view path) override; - VirtualDir OpenDirectory(std::string_view path, Mode perms = Mode::Read) override; - VirtualDir CreateDirectory(std::string_view path, Mode perms = Mode::ReadWrite) override; + VirtualDir OpenDirectory(std::string_view path, OpenMode perms = OpenMode::Read) override; + VirtualDir CreateDirectory(std::string_view path, + OpenMode perms = OpenMode::ReadWrite) override; VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path) override; VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path) override; bool DeleteDirectory(std::string_view path) override; @@ -54,14 +55,14 @@ private: private: friend class RealVfsFile; - std::unique_lock<std::mutex> RefreshReference(const std::string& path, Mode perms, + std::unique_lock<std::mutex> RefreshReference(const std::string& path, OpenMode perms, FileReference& reference); void DropReference(std::unique_ptr<FileReference>&& reference); private: friend class RealVfsDirectory; VirtualFile OpenFileFromEntry(std::string_view path, std::optional<u64> size, - Mode perms = Mode::Read); + OpenMode perms = OpenMode::Read); private: void EvictSingleReferenceLocked(); @@ -89,7 +90,8 @@ public: private: RealVfsFile(RealVfsFilesystem& base, std::unique_ptr<FileReference> reference, - const std::string& path, Mode perms = Mode::Read, std::optional<u64> size = {}); + const std::string& path, OpenMode perms = OpenMode::Read, + std::optional<u64> size = {}); RealVfsFilesystem& base; std::unique_ptr<FileReference> reference; @@ -97,7 +99,7 @@ private: std::string parent_path; std::vector<std::string> path_components; std::optional<u64> size; - Mode perms; + OpenMode perms; }; // An implementation of VfsDirectory that represents a directory on the user's computer. @@ -130,7 +132,8 @@ public: std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override; private: - RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read); + RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, + OpenMode perms = OpenMode::Read); template <typename T, typename R> std::vector<std::shared_ptr<R>> IterateEntries() const; @@ -139,7 +142,7 @@ private: std::string path; std::string parent_path; std::vector<std::string> path_components; - Mode perms; + OpenMode perms; }; } // namespace FileSys diff --git a/src/core/file_sys/vfs_static.h b/src/core/file_sys/vfs/vfs_static.h index ca3f989ef..bb53560ac 100644 --- a/src/core/file_sys/vfs_static.h +++ b/src/core/file_sys/vfs/vfs_static.h @@ -7,7 +7,7 @@ #include <memory> #include <string_view> -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/vfs_types.h b/src/core/file_sys/vfs/vfs_types.h index 4a583ed64..4a583ed64 100644 --- a/src/core/file_sys/vfs_types.h +++ b/src/core/file_sys/vfs/vfs_types.h diff --git a/src/core/file_sys/vfs_vector.cpp b/src/core/file_sys/vfs/vfs_vector.cpp index 251d9d7c9..0d54461c8 100644 --- a/src/core/file_sys/vfs_vector.cpp +++ b/src/core/file_sys/vfs/vfs_vector.cpp @@ -3,7 +3,7 @@ #include <algorithm> #include <utility> -#include "core/file_sys/vfs_vector.h" +#include "core/file_sys/vfs/vfs_vector.h" namespace FileSys { VectorVfsFile::VectorVfsFile(std::vector<u8> initial_data, std::string name_, VirtualDir parent_) diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs/vfs_vector.h index bfedb6e42..587187dd2 100644 --- a/src/core/file_sys/vfs_vector.h +++ b/src/core/file_sys/vfs/vfs_vector.h @@ -8,7 +8,7 @@ #include <memory> #include <string> #include <vector> -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp index ede0aa11a..6692211e1 100644 --- a/src/core/file_sys/xts_archive.cpp +++ b/src/core/file_sys/xts_archive.cpp @@ -17,7 +17,7 @@ #include "core/crypto/key_manager.h" #include "core/crypto/xts_encryption_layer.h" #include "core/file_sys/content_archive.h" -#include "core/file_sys/vfs_offset.h" +#include "core/file_sys/vfs/vfs_offset.h" #include "core/file_sys/xts_archive.h" #include "core/loader/loader.h" diff --git a/src/core/file_sys/xts_archive.h b/src/core/file_sys/xts_archive.h index abbe5f716..7589b7c38 100644 --- a/src/core/file_sys/xts_archive.h +++ b/src/core/file_sys/xts_archive.h @@ -8,7 +8,7 @@ #include "common/common_types.h" #include "common/swap.h" #include "core/crypto/key_manager.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace Loader { enum class ResultStatus : u16; diff --git a/src/core/frontend/applets/error.cpp b/src/core/frontend/applets/error.cpp index 2e6f7a3d9..53d4be2e5 100644 --- a/src/core/frontend/applets/error.cpp +++ b/src/core/frontend/applets/error.cpp @@ -12,7 +12,7 @@ void DefaultErrorApplet::Close() const {} void DefaultErrorApplet::ShowError(Result error, FinishedCallback finished) const { LOG_CRITICAL(Service_Fatal, "Application requested error display: {:04}-{:04} (raw={:08X})", - error.module.Value(), error.description.Value(), error.raw); + error.GetModule(), error.GetDescription(), error.raw); } void DefaultErrorApplet::ShowErrorWithTimestamp(Result error, std::chrono::seconds time, @@ -20,7 +20,7 @@ void DefaultErrorApplet::ShowErrorWithTimestamp(Result error, std::chrono::secon LOG_CRITICAL( Service_Fatal, "Application requested error display: {:04X}-{:04X} (raw={:08X}) with timestamp={:016X}", - error.module.Value(), error.description.Value(), error.raw, time.count()); + error.GetModule(), error.GetDescription(), error.raw, time.count()); } void DefaultErrorApplet::ShowCustomErrorText(Result error, std::string main_text, @@ -28,7 +28,7 @@ void DefaultErrorApplet::ShowCustomErrorText(Result error, std::string main_text FinishedCallback finished) const { LOG_CRITICAL(Service_Fatal, "Application requested custom error with error_code={:04X}-{:04X} (raw={:08X})", - error.module.Value(), error.description.Value(), error.raw); + error.GetModule(), error.GetDescription(), error.raw); LOG_CRITICAL(Service_Fatal, " Main Text: {}", main_text); LOG_CRITICAL(Service_Fatal, " Detail Text: {}", detail_text); } diff --git a/src/core/hle/kernel/k_page_table_base.cpp b/src/core/hle/kernel/k_page_table_base.cpp index 3f0a39d33..1dd86fb3c 100644 --- a/src/core/hle/kernel/k_page_table_base.cpp +++ b/src/core/hle/kernel/k_page_table_base.cpp @@ -69,9 +69,14 @@ public: }; template <typename AddressType> -void InvalidateInstructionCache(KernelCore& kernel, AddressType addr, u64 size) { +void InvalidateInstructionCache(KernelCore& kernel, KPageTableBase* table, AddressType addr, + u64 size) { // TODO: lock the process list for (auto& process : kernel.GetProcessList()) { + if (std::addressof(process->GetPageTable().GetBasePageTable()) != table) { + continue; + } + for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { auto* interface = process->GetArmInterface(i); if (interface) { @@ -1302,7 +1307,7 @@ Result KPageTableBase::UnmapCodeMemory(KProcessAddress dst_address, KProcessAddr bool reprotected_pages = false; SCOPE_EXIT({ if (reprotected_pages && any_code_pages) { - InvalidateInstructionCache(m_kernel, dst_address, size); + InvalidateInstructionCache(m_kernel, this, dst_address, size); } }); @@ -2036,7 +2041,7 @@ Result KPageTableBase::SetProcessMemoryPermission(KProcessAddress addr, size_t s for (const auto& block : pg) { StoreDataCache(GetHeapVirtualPointer(m_kernel, block.GetAddress()), block.GetSize()); } - InvalidateInstructionCache(m_kernel, addr, size); + InvalidateInstructionCache(m_kernel, this, addr, size); } R_SUCCEED(); @@ -3277,7 +3282,7 @@ Result KPageTableBase::WriteDebugMemory(KProcessAddress dst_address, KProcessAdd R_TRY(PerformCopy()); // Invalidate the instruction cache, as this svc allows modifying executable pages. - InvalidateInstructionCache(m_kernel, dst_address, size); + InvalidateInstructionCache(m_kernel, this, dst_address, size); R_SUCCEED(); } diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 749f51f69..316370266 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -189,14 +189,14 @@ enum class ErrorModule : u32 { union Result { u32 raw; - BitField<0, 9, ErrorModule> module; - BitField<9, 13, u32> description; + using Module = BitField<0, 9, ErrorModule>; + using Description = BitField<9, 13, u32>; Result() = default; constexpr explicit Result(u32 raw_) : raw(raw_) {} constexpr Result(ErrorModule module_, u32 description_) - : raw(module.FormatValue(module_) | description.FormatValue(description_)) {} + : raw(Module::FormatValue(module_) | Description::FormatValue(description_)) {} [[nodiscard]] constexpr bool IsSuccess() const { return raw == 0; @@ -211,7 +211,15 @@ union Result { } [[nodiscard]] constexpr u32 GetInnerValue() const { - return static_cast<u32>(module.Value()) | (description << module.bits); + return raw; + } + + [[nodiscard]] constexpr ErrorModule GetModule() const { + return Module::ExtractValue(raw); + } + + [[nodiscard]] constexpr u32 GetDescription() const { + return Description::ExtractValue(raw); } [[nodiscard]] constexpr bool Includes(Result result) const { @@ -274,8 +282,9 @@ public: } [[nodiscard]] constexpr bool Includes(Result other) const { - return code.module == other.module && code.description <= other.description && - other.description <= description_end; + return code.GetModule() == other.GetModule() && + code.GetDescription() <= other.GetDescription() && + other.GetDescription() <= description_end; } private: @@ -330,6 +339,16 @@ constexpr bool EvaluateResultFailure(const Result& r) { return R_FAILED(r); } +template <auto... R> +constexpr bool EvaluateAnyResultIncludes(const Result& r) { + return ((r == R) || ...); +} + +template <auto... R> +constexpr bool EvaluateResultNotIncluded(const Result& r) { + return !EvaluateAnyResultIncludes<R...>(r); +} + template <typename T> constexpr void UpdateCurrentResultReference(T result_reference, Result result) = delete; // Intentionally not defined @@ -371,6 +390,13 @@ constexpr void UpdateCurrentResultReference<const Result>(Result result_referenc DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(__COUNTER__); \ ON_RESULT_SUCCESS_2 +#define ON_RESULT_INCLUDED_2(...) \ + ON_RESULT_RETURN_IMPL(ResultImpl::EvaluateAnyResultIncludes<__VA_ARGS__>) + +#define ON_RESULT_INCLUDED(...) \ + DECLARE_CURRENT_RESULT_REFERENCE_AND_STORAGE(__COUNTER__); \ + ON_RESULT_INCLUDED_2(__VA_ARGS__) + constexpr inline Result __TmpCurrentResultReference = ResultSuccess; /// Returns a result. diff --git a/src/core/hle/service/am/applets/applet_error.cpp b/src/core/hle/service/am/applets/applet_error.cpp index 5d17c353f..084bc138c 100644 --- a/src/core/hle/service/am/applets/applet_error.cpp +++ b/src/core/hle/service/am/applets/applet_error.cpp @@ -27,8 +27,8 @@ struct ErrorCode { static constexpr ErrorCode FromResult(Result result) { return { - .error_category{2000 + static_cast<u32>(result.module.Value())}, - .error_number{result.description.Value()}, + .error_category{2000 + static_cast<u32>(result.GetModule())}, + .error_number{result.GetDescription()}, }; } diff --git a/src/core/hle/service/am/applets/applet_web_browser.cpp b/src/core/hle/service/am/applets/applet_web_browser.cpp index b0ea2b381..19057ad7b 100644 --- a/src/core/hle/service/am/applets/applet_web_browser.cpp +++ b/src/core/hle/service/am/applets/applet_web_browser.cpp @@ -9,13 +9,13 @@ #include "common/string_util.h" #include "core/core.h" #include "core/file_sys/content_archive.h" -#include "core/file_sys/mode.h" +#include "core/file_sys/fs_filesystem.h" #include "core/file_sys/nca_metadata.h" #include "core/file_sys/patch_manager.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_vector.h" +#include "core/file_sys/vfs/vfs_vector.h" #include "core/frontend/applets/web_browser.h" #include "core/hle/result.h" #include "core/hle/service/am/am.h" @@ -213,7 +213,7 @@ void ExtractSharedFonts(Core::System& system) { std::move(decrypted_data), DECRYPTED_SHARED_FONTS[i]); const auto temp_dir = system.GetFilesystem()->CreateDirectory( - Common::FS::PathToUTF8String(fonts_dir), FileSys::Mode::ReadWrite); + Common::FS::PathToUTF8String(fonts_dir), FileSys::OpenMode::ReadWrite); const auto out_file = temp_dir->CreateFile(DECRYPTED_SHARED_FONTS[i]); @@ -333,7 +333,7 @@ void WebBrowser::ExtractOfflineRomFS() { const auto extracted_romfs_dir = FileSys::ExtractRomFS(offline_romfs); const auto temp_dir = system.GetFilesystem()->CreateDirectory( - Common::FS::PathToUTF8String(offline_cache_dir), FileSys::Mode::ReadWrite); + Common::FS::PathToUTF8String(offline_cache_dir), FileSys::OpenMode::ReadWrite); FileSys::VfsRawCopyD(extracted_romfs_dir, temp_dir); } diff --git a/src/core/hle/service/am/applets/applet_web_browser.h b/src/core/hle/service/am/applets/applet_web_browser.h index 99fe18659..36adb2510 100644 --- a/src/core/hle/service/am/applets/applet_web_browser.h +++ b/src/core/hle/service/am/applets/applet_web_browser.h @@ -7,7 +7,7 @@ #include <optional> #include "common/common_types.h" -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" #include "core/hle/result.h" #include "core/hle/service/am/applets/applet_web_browser_types.h" #include "core/hle/service/am/applets/applets.h" diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index bd4ca753b..05581e6e0 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -139,7 +139,8 @@ private: ctx.WriteBufferC(performance_buffer.data(), performance_buffer.size(), 1); } } else { - LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description); + LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", + result.GetDescription()); } IPC::ResponseBuilder rb{ctx, 2}; diff --git a/src/core/hle/service/bcat/backend/backend.h b/src/core/hle/service/bcat/backend/backend.h index 205ed0702..aa36d29d5 100644 --- a/src/core/hle/service/bcat/backend/backend.h +++ b/src/core/hle/service/bcat/backend/backend.h @@ -8,7 +8,7 @@ #include <string> #include "common/common_types.h" -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" #include "core/hle/result.h" #include "core/hle/service/kernel_helpers.h" diff --git a/src/core/hle/service/bcat/bcat_module.cpp b/src/core/hle/service/bcat/bcat_module.cpp index a6281913a..76d7bb139 100644 --- a/src/core/hle/service/bcat/bcat_module.cpp +++ b/src/core/hle/service/bcat/bcat_module.cpp @@ -8,7 +8,7 @@ #include "common/settings.h" #include "common/string_util.h" #include "core/core.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" #include "core/hle/kernel/k_readable_event.h" #include "core/hle/service/bcat/backend/backend.h" #include "core/hle/service/bcat/bcat.h" diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp index c65e32489..2dc23e674 100644 --- a/src/core/hle/service/btm/btm.cpp +++ b/src/core/hle/service/btm/btm.cpp @@ -283,7 +283,7 @@ public: {17, &IBtmSystemCore::GetConnectedAudioDevices, "GetConnectedAudioDevices"}, {18, nullptr, "DisconnectAudioDevice"}, {19, nullptr, "AcquirePairedAudioDeviceInfoChangedEvent"}, - {20, nullptr, "GetPairedAudioDevices"}, + {20, &IBtmSystemCore::GetPairedAudioDevices, "GetPairedAudioDevices"}, {21, nullptr, "RemoveAudioDevicePairing"}, {22, &IBtmSystemCore::RequestAudioDeviceConnectionRejection, "RequestAudioDeviceConnectionRejection"}, {23, &IBtmSystemCore::CancelAudioDeviceConnectionRejection, "CancelAudioDeviceConnectionRejection"} @@ -327,6 +327,13 @@ private: rb.Push<u32>(0); } + void GetPairedAudioDevices(HLERequestContext& ctx) { + LOG_WARNING(Service_BTM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<u32>(0); + } + void RequestAudioDeviceConnectionRejection(HLERequestContext& ctx) { LOG_WARNING(Service_BTM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; diff --git a/src/core/hle/service/caps/caps_a.cpp b/src/core/hle/service/caps/caps_a.cpp index 9925720a3..69acb3a8b 100644 --- a/src/core/hle/service/caps/caps_a.cpp +++ b/src/core/hle/service/caps/caps_a.cpp @@ -202,14 +202,14 @@ Result IAlbumAccessorService::TranslateResult(Result in_result) { } if ((in_result.raw & 0x3801ff) == ResultUnknown1024.raw) { - if (in_result.description - 0x514 < 100) { + if (in_result.GetDescription() - 0x514 < 100) { return ResultInvalidFileData; } - if (in_result.description - 0x5dc < 100) { + if (in_result.GetDescription() - 0x5dc < 100) { return ResultInvalidFileData; } - if (in_result.description - 0x578 < 100) { + if (in_result.GetDescription() - 0x578 < 100) { if (in_result == ResultFileCountLimit) { return ResultUnknown22; } @@ -244,9 +244,10 @@ Result IAlbumAccessorService::TranslateResult(Result in_result) { return ResultUnknown1024; } - if (in_result.module == ErrorModule::FS) { - if ((in_result.description >> 0xc < 0x7d) || (in_result.description - 1000 < 2000) || - (((in_result.description - 3000) >> 3) < 0x271)) { + if (in_result.GetModule() == ErrorModule::FS) { + if ((in_result.GetDescription() >> 0xc < 0x7d) || + (in_result.GetDescription() - 1000 < 2000) || + (((in_result.GetDescription() - 3000) >> 3) < 0x271)) { // TODO: Translate FS error return in_result; } diff --git a/src/core/hle/service/caps/caps_manager.cpp b/src/core/hle/service/caps/caps_manager.cpp index 261fc204c..e3b8ecf3e 100644 --- a/src/core/hle/service/caps/caps_manager.cpp +++ b/src/core/hle/service/caps/caps_manager.cpp @@ -10,8 +10,10 @@ #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/glue/time/static.h" +#include "core/hle/service/psc/time/system_clock.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" namespace Service::Capture { @@ -239,10 +241,15 @@ Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry, const ApplicationData& app_data, std::span<const u8> image_data, u64 aruid) { const u64 title_id = system.GetApplicationProcessProgramID(); - const auto& user_clock = system.GetTimeManager().GetStandardUserSystemClockCore(); + + auto static_service = + system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true); + + std::shared_ptr<Service::PSC::Time::SystemClock> 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 +264,14 @@ Result AlbumManager::SaveEditedScreenShot(ApplicationAlbumEntry& out_entry, const ScreenShotAttribute& attribute, const AlbumFileId& file_id, std::span<const u8> image_data) { - const auto& user_clock = system.GetTimeManager().GetStandardUserSystemClockCore(); + auto static_service = + system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true); + + std::shared_ptr<Service::PSC::Time::SystemClock> 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 +466,23 @@ Result AlbumManager::SaveImage(ApplicationAlbumEntry& out_entry, std::span<const } AlbumFileDateTime AlbumManager::ConvertToAlbumDateTime(u64 posix_time) const { - Time::TimeZone::CalendarInfo calendar_date{}; - const auto& time_zone_manager = - system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager(); + auto static_service = + system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true); + + std::shared_ptr<Service::Glue::Time::TimeZoneService> timezone_service{}; + static_service->GetTimeZoneService(timezone_service); - time_zone_manager.ToCalendarTimeWithMyRules(posix_time, calendar_date); + 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/cmif_serialization.h b/src/core/hle/service/cmif_serialization.h new file mode 100644 index 000000000..9eb10e816 --- /dev/null +++ b/src/core/hle/service/cmif_serialization.h @@ -0,0 +1,336 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/div_ceil.h" + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/service.h" + +namespace Service { + +// clang-format off +struct RequestLayout { + u32 copy_handle_count; + u32 move_handle_count; + u32 cmif_raw_data_size; + u32 domain_interface_count; +}; + +template <ArgumentType Type1, ArgumentType Type2, typename MethodArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t ArgIndex = 0> +constexpr u32 GetArgumentRawDataSize() { + if constexpr (ArgIndex >= std::tuple_size_v<MethodArguments>) { + return static_cast<u32>(DataOffset); + } else { + using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>; + + if constexpr (ArgumentTraits<ArgType>::Type == Type1 || ArgumentTraits<ArgType>::Type == Type2) { + constexpr size_t ArgAlign = alignof(ArgType); + constexpr size_t ArgSize = sizeof(ArgType); + + static_assert(PrevAlign <= ArgAlign, "Input argument is not ordered by alignment"); + + constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign); + constexpr size_t ArgEnd = ArgOffset + ArgSize; + + return GetArgumentRawDataSize<Type1, Type2, MethodArguments, ArgAlign, ArgEnd, ArgIndex + 1>(); + } else { + return GetArgumentRawDataSize<Type1, Type2, MethodArguments, PrevAlign, DataOffset, ArgIndex + 1>(); + } + } +} + +template <ArgumentType DataType, typename MethodArguments, size_t ArgCount = 0, size_t ArgIndex = 0> +constexpr u32 GetArgumentTypeCount() { + if constexpr (ArgIndex >= std::tuple_size_v<MethodArguments>) { + return static_cast<u32>(ArgCount); + } else { + using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>; + + if constexpr (ArgumentTraits<ArgType>::Type == DataType) { + return GetArgumentTypeCount<DataType, MethodArguments, ArgCount + 1, ArgIndex + 1>(); + } else { + return GetArgumentTypeCount<DataType, MethodArguments, ArgCount, ArgIndex + 1>(); + } + } +} + +template <typename MethodArguments> +constexpr RequestLayout GetNonDomainReplyInLayout() { + return RequestLayout{ + .copy_handle_count = GetArgumentTypeCount<ArgumentType::InCopyHandle, MethodArguments>(), + .move_handle_count = 0, + .cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::InData, ArgumentType::InProcessId, MethodArguments>(), + .domain_interface_count = 0, + }; +} + +template <typename MethodArguments> +constexpr RequestLayout GetDomainReplyInLayout() { + return RequestLayout{ + .copy_handle_count = GetArgumentTypeCount<ArgumentType::InCopyHandle, MethodArguments>(), + .move_handle_count = 0, + .cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::InData, ArgumentType::InProcessId, MethodArguments>(), + .domain_interface_count = GetArgumentTypeCount<ArgumentType::InInterface, MethodArguments>(), + }; +} + +template <typename MethodArguments> +constexpr RequestLayout GetNonDomainReplyOutLayout() { + return RequestLayout{ + .copy_handle_count = GetArgumentTypeCount<ArgumentType::OutCopyHandle, MethodArguments>(), + .move_handle_count = GetArgumentTypeCount<ArgumentType::OutMoveHandle, MethodArguments>() + GetArgumentTypeCount<ArgumentType::OutInterface, MethodArguments>(), + .cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::OutData, ArgumentType::OutData, MethodArguments>(), + .domain_interface_count = 0, + }; +} + +template <typename MethodArguments> +constexpr RequestLayout GetDomainReplyOutLayout() { + return RequestLayout{ + .copy_handle_count = GetArgumentTypeCount<ArgumentType::OutCopyHandle, MethodArguments>(), + .move_handle_count = GetArgumentTypeCount<ArgumentType::OutMoveHandle, MethodArguments>(), + .cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::OutData, ArgumentType::OutData, MethodArguments>(), + .domain_interface_count = GetArgumentTypeCount<ArgumentType::OutInterface, MethodArguments>(), + }; +} + +template <typename MethodArguments> +constexpr RequestLayout GetReplyInLayout(bool is_domain) { + return is_domain ? GetDomainReplyInLayout<MethodArguments>() : GetNonDomainReplyInLayout<MethodArguments>(); +} + +template <typename MethodArguments> +constexpr RequestLayout GetReplyOutLayout(bool is_domain) { + return is_domain ? GetDomainReplyOutLayout<MethodArguments>() : GetNonDomainReplyOutLayout<MethodArguments>(); +} + +using OutTemporaryBuffers = std::array<Common::ScratchBuffer<u8>, 3>; + +template <typename MethodArguments, typename CallArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t HandleIndex = 0, size_t InBufferIndex = 0, size_t OutBufferIndex = 0, bool RawDataFinished = false, size_t ArgIndex = 0> +void ReadInArgument(bool is_domain, CallArguments& args, const u8* raw_data, HLERequestContext& ctx, OutTemporaryBuffers& temp) { + if constexpr (ArgIndex >= std::tuple_size_v<CallArguments>) { + return; + } else { + using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>; + + if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InData || ArgumentTraits<ArgType>::Type == ArgumentType::InProcessId) { + constexpr size_t ArgAlign = alignof(ArgType); + constexpr size_t ArgSize = sizeof(ArgType); + + static_assert(PrevAlign <= ArgAlign, "Input argument is not ordered by alignment"); + static_assert(!RawDataFinished, "All input interface arguments must appear after raw data"); + + constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign); + constexpr size_t ArgEnd = ArgOffset + ArgSize; + + if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InProcessId) { + // TODO: abort parsing if PID is not provided? + // TODO: validate against raw data value? + std::get<ArgIndex>(args).pid = ctx.GetPID(); + } else { + std::memcpy(&std::get<ArgIndex>(args), raw_data + ArgOffset, ArgSize); + } + + return ReadInArgument<MethodArguments, CallArguments, ArgAlign, ArgEnd, HandleIndex, InBufferIndex, OutBufferIndex, false, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InInterface) { + constexpr size_t ArgAlign = alignof(u32); + constexpr size_t ArgSize = sizeof(u32); + constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign); + constexpr size_t ArgEnd = ArgOffset + ArgSize; + + ASSERT(is_domain); + ASSERT(ctx.GetDomainMessageHeader().input_object_count > 0); + + u32 value{}; + std::memcpy(&value, raw_data + ArgOffset, ArgSize); + std::get<ArgIndex>(args) = ctx.GetDomainHandler<ArgType::Type>(value - 1); + + return ReadInArgument<MethodArguments, CallArguments, ArgAlign, ArgEnd, HandleIndex, InBufferIndex, OutBufferIndex, true, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InCopyHandle) { + std::get<ArgIndex>(args) = ctx.GetObjectFromHandle<typename ArgType::Type>(ctx.GetCopyHandle(HandleIndex)).GetPointerUnsafe(); + + return ReadInArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex + 1, InBufferIndex, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InLargeData) { + constexpr size_t BufferSize = sizeof(ArgType); + + // Clear the existing data. + std::memset(&std::get<ArgIndex>(args), 0, BufferSize); + + std::span<const u8> buffer{}; + + ASSERT(ctx.CanReadBuffer(InBufferIndex)); + if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) { + buffer = ctx.ReadBuffer(InBufferIndex); + } else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) { + buffer = ctx.ReadBufferA(InBufferIndex); + } else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ { + buffer = ctx.ReadBufferX(InBufferIndex); + } + + std::memcpy(&std::get<ArgIndex>(args), buffer.data(), std::min(BufferSize, buffer.size())); + + return ReadInArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex + 1, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InBuffer) { + using ElementType = typename ArgType::Type; + + std::span<const u8> buffer{}; + + if (ctx.CanReadBuffer(InBufferIndex)) { + if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) { + buffer = ctx.ReadBuffer(InBufferIndex); + } else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) { + buffer = ctx.ReadBufferA(InBufferIndex); + } else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ { + buffer = ctx.ReadBufferX(InBufferIndex); + } + } + + ElementType* ptr = (ElementType*) buffer.data(); + size_t size = buffer.size() / sizeof(ElementType); + + std::get<ArgIndex>(args) = std::span(ptr, size); + + return ReadInArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex + 1, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutLargeData) { + constexpr size_t BufferSize = sizeof(ArgType); + + // Clear the existing data. + std::memset(&std::get<ArgIndex>(args), 0, BufferSize); + + return ReadInArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutBuffer) { + using ElementType = typename ArgType::Type; + + // Set up scratch buffer. + auto& buffer = temp[OutBufferIndex]; + if (ctx.CanWriteBuffer(OutBufferIndex)) { + buffer.resize_destructive(ctx.GetWriteBufferSize(OutBufferIndex)); + } else { + buffer.resize_destructive(0); + } + + ElementType* ptr = (ElementType*) buffer.data(); + size_t size = buffer.size() / sizeof(ElementType); + + std::get<ArgIndex>(args) = std::span(ptr, size); + + return ReadInArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } else { + return ReadInArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } + } +} + +template <typename MethodArguments, typename CallArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t OutBufferIndex = 0, bool RawDataFinished = false, size_t ArgIndex = 0> +void WriteOutArgument(bool is_domain, CallArguments& args, u8* raw_data, HLERequestContext& ctx, OutTemporaryBuffers& temp) { + if constexpr (ArgIndex >= std::tuple_size_v<CallArguments>) { + return; + } else { + using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>; + + if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutData) { + constexpr size_t ArgAlign = alignof(ArgType); + constexpr size_t ArgSize = sizeof(ArgType); + + static_assert(PrevAlign <= ArgAlign, "Output argument is not ordered by alignment"); + static_assert(!RawDataFinished, "All output interface arguments must appear after raw data"); + + constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign); + constexpr size_t ArgEnd = ArgOffset + ArgSize; + + std::memcpy(raw_data + ArgOffset, &std::get<ArgIndex>(args), ArgSize); + + return WriteOutArgument<MethodArguments, CallArguments, ArgAlign, ArgEnd, OutBufferIndex, false, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutInterface) { + if (is_domain) { + ctx.AddDomainObject(std::get<ArgIndex>(args)); + } else { + ctx.AddMoveInterface(std::get<ArgIndex>(args)); + } + + return WriteOutArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, true, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutCopyHandle) { + ctx.AddCopyObject(std::get<ArgIndex>(args)); + + return WriteOutArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutMoveHandle) { + ctx.AddMoveObject(std::get<ArgIndex>(args)); + + return WriteOutArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutLargeData) { + constexpr size_t BufferSize = sizeof(ArgType); + + ASSERT(ctx.CanWriteBuffer(OutBufferIndex)); + if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) { + ctx.WriteBuffer(std::get<ArgIndex>(args), OutBufferIndex); + } else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) { + ctx.WriteBufferB(&std::get<ArgIndex>(args), BufferSize, OutBufferIndex); + } else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ { + ctx.WriteBufferC(&std::get<ArgIndex>(args), BufferSize, OutBufferIndex); + } + + return WriteOutArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutBuffer) { + auto& buffer = temp[OutBufferIndex]; + const size_t size = buffer.size(); + + if (ctx.CanWriteBuffer(OutBufferIndex)) { + if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) { + ctx.WriteBuffer(buffer.data(), size, OutBufferIndex); + } else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) { + ctx.WriteBufferB(buffer.data(), size, OutBufferIndex); + } else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ { + ctx.WriteBufferC(buffer.data(), size, OutBufferIndex); + } + } + + return WriteOutArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } else { + return WriteOutArgument<MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(is_domain, args, raw_data, ctx, temp); + } + } +} + +template <bool Domain, typename T, typename... A> +void CmifReplyWrapImpl(HLERequestContext& ctx, T& t, Result (T::*f)(A...)) { + // Verify domain state. + if constexpr (!Domain) { + ASSERT_MSG(!ctx.GetManager()->IsDomain(), "Non-domain reply used on domain session"); + } + const bool is_domain = Domain ? ctx.GetManager()->IsDomain() : false; + + using MethodArguments = std::tuple<std::remove_reference_t<A>...>; + + OutTemporaryBuffers buffers{}; + auto call_arguments = std::tuple<typename RemoveOut<A>::Type...>(); + + // Read inputs. + const size_t offset_plus_command_id = ctx.GetDataPayloadOffset() + 2; + ReadInArgument<MethodArguments>(is_domain, call_arguments, reinterpret_cast<u8*>(ctx.CommandBuffer() + offset_plus_command_id), ctx, buffers); + + // Call. + const auto Callable = [&]<typename... CallArgs>(CallArgs&... args) { + return (t.*f)(args...); + }; + const Result res = std::apply(Callable, call_arguments); + + // Write result. + const RequestLayout layout = GetReplyOutLayout<MethodArguments>(is_domain); + IPC::ResponseBuilder rb{ctx, 2 + Common::DivCeil(layout.cmif_raw_data_size, sizeof(u32)), layout.copy_handle_count, layout.move_handle_count + layout.domain_interface_count}; + rb.Push(res); + + // Write out arguments. + WriteOutArgument<MethodArguments>(is_domain, call_arguments, reinterpret_cast<u8*>(ctx.CommandBuffer() + rb.GetCurrentOffset()), ctx, buffers); +} +// clang-format on + +template <typename Self> +template <bool Domain, auto F> +inline void ServiceFramework<Self>::CmifReplyWrap(HLERequestContext& ctx) { + return CmifReplyWrapImpl<Domain>(ctx, *static_cast<Self*>(this), F); +} + +} // namespace Service diff --git a/src/core/hle/service/cmif_types.h b/src/core/hle/service/cmif_types.h new file mode 100644 index 000000000..2610c49f3 --- /dev/null +++ b/src/core/hle/service/cmif_types.h @@ -0,0 +1,294 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <memory> + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "core/hle/service/hle_ipc.h" + +namespace Service { + +// clang-format off +template <typename T> +class Out { +public: + using Type = T; + + /* implicit */ Out(Type& t) : raw(&t) {} + ~Out() = default; + + Type* Get() const { + return raw; + } + + Type& operator*() { + return *raw; + } + +private: + Type* raw; +}; + +template <typename T> +using SharedPointer = std::shared_ptr<T>; + +struct ClientProcessId { + explicit operator bool() const { + return pid != 0; + } + + const u64& operator*() const { + return pid; + } + + u64 pid; +}; + +struct ProcessId { + explicit operator bool() const { + return pid != 0; + } + + const u64& operator*() const { + return pid; + } + + u64 pid; +}; + +using ClientAppletResourceUserId = ClientProcessId; +using AppletResourceUserId = ProcessId; + +template <typename T> +class InCopyHandle { +public: + using Type = T; + + /* implicit */ InCopyHandle(Type* t) : raw(t) {} + /* implicit */ InCopyHandle() : raw() {} + ~InCopyHandle() = default; + + InCopyHandle& operator=(Type* rhs) { + raw = rhs; + return *this; + } + + Type* Get() const { + return raw; + } + + Type& operator*() const { + return *raw; + } + + Type* operator->() const { + return raw; + } + + explicit operator bool() const { + return raw != nullptr; + } + +private: + Type* raw; +}; + +template <typename T> +class OutCopyHandle { +public: + using Type = T*; + + /* implicit */ OutCopyHandle(Type& t) : raw(&t) {} + ~OutCopyHandle() = default; + + Type* Get() const { + return raw; + } + + Type& operator*() { + return *raw; + } + +private: + Type* raw; +}; + +template <typename T> +class OutMoveHandle { +public: + using Type = T*; + + /* implicit */ OutMoveHandle(Type& t) : raw(&t) {} + ~OutMoveHandle() = default; + + Type* Get() const { + return raw; + } + + Type& operator*() { + return *raw; + } + +private: + Type* raw; +}; + +enum BufferAttr : int { + BufferAttr_In = (1U << 0), + BufferAttr_Out = (1U << 1), + BufferAttr_HipcMapAlias = (1U << 2), + BufferAttr_HipcPointer = (1U << 3), + BufferAttr_FixedSize = (1U << 4), + BufferAttr_HipcAutoSelect = (1U << 5), + BufferAttr_HipcMapTransferAllowsNonSecure = (1U << 6), + BufferAttr_HipcMapTransferAllowsNonDevice = (1U << 7), +}; + +template <typename T, int A> +struct Buffer : public std::span<T> { + static_assert(std::is_trivially_copyable_v<T>, "Buffer type must be trivially copyable"); + static_assert((A & BufferAttr_FixedSize) == 0, "Buffer attr must not contain FixedSize"); + static_assert(((A & BufferAttr_In) == 0) ^ ((A & BufferAttr_Out) == 0), "Buffer attr must be In or Out"); + static constexpr BufferAttr Attr = static_cast<BufferAttr>(A); + using Type = T; + + /* implicit */ Buffer(const std::span<T>& rhs) : std::span<T>(rhs) {} + /* implicit */ Buffer() = default; + + Buffer& operator=(const std::span<T>& rhs) { + std::span<T>::operator=(rhs); + return *this; + } + + T& operator*() const { + return *this->data(); + } + + explicit operator bool() const { + return this->size() > 0; + } +}; + +template <BufferAttr A> +using InBuffer = Buffer<const u8, BufferAttr_In | A>; + +template <typename T, BufferAttr A> +using InArray = Buffer<T, BufferAttr_In | A>; + +template <BufferAttr A> +using OutBuffer = Buffer<u8, BufferAttr_Out | A>; + +template <typename T, BufferAttr A> +using OutArray = Buffer<T, BufferAttr_Out | A>; + +template <typename T, int A> +struct LargeData : public T { + static_assert(std::is_trivially_copyable_v<T>, "LargeData type must be trivially copyable"); + static_assert((A & BufferAttr_FixedSize) != 0, "LargeData attr must contain FixedSize"); + static_assert(((A & BufferAttr_In) == 0) ^ ((A & BufferAttr_Out) == 0), "LargeData attr must be In or Out"); + static constexpr BufferAttr Attr = static_cast<BufferAttr>(A); + using Type = T; + + /* implicit */ LargeData(const T& rhs) : T(rhs) {} + /* implicit */ LargeData() = default; +}; + +template <typename T, BufferAttr A> +using InLargeData = LargeData<T, BufferAttr_FixedSize | BufferAttr_In | A>; + +template <typename T, BufferAttr A> +using OutLargeData = LargeData<T, BufferAttr_FixedSize | BufferAttr_Out | A>; + +template <typename T> +struct RemoveOut { + using Type = std::remove_reference_t<T>; +}; + +template <typename T> +struct RemoveOut<Out<T>> { + using Type = typename Out<T>::Type; +}; + +template <typename T> +struct RemoveOut<OutCopyHandle<T>> { + using Type = typename OutCopyHandle<T>::Type; +}; + +template <typename T> +struct RemoveOut<OutMoveHandle<T>> { + using Type = typename OutMoveHandle<T>::Type; +}; + +enum class ArgumentType { + InProcessId, + InData, + InInterface, + InCopyHandle, + OutData, + OutInterface, + OutCopyHandle, + OutMoveHandle, + InBuffer, + InLargeData, + OutBuffer, + OutLargeData, +}; + +template <typename T> +struct ArgumentTraits; + +template <> +struct ArgumentTraits<ClientProcessId> { + static constexpr ArgumentType Type = ArgumentType::InProcessId; +}; + +template <typename T> +struct ArgumentTraits<SharedPointer<T>> { + static constexpr ArgumentType Type = ArgumentType::InInterface; +}; + +template <typename T> +struct ArgumentTraits<InCopyHandle<T>> { + static constexpr ArgumentType Type = ArgumentType::InCopyHandle; +}; + +template <typename T> +struct ArgumentTraits<Out<SharedPointer<T>>> { + static constexpr ArgumentType Type = ArgumentType::OutInterface; +}; + +template <typename T> +struct ArgumentTraits<Out<T>> { + static constexpr ArgumentType Type = ArgumentType::OutData; +}; + +template <typename T> +struct ArgumentTraits<OutCopyHandle<T>> { + static constexpr ArgumentType Type = ArgumentType::OutCopyHandle; +}; + +template <typename T> +struct ArgumentTraits<OutMoveHandle<T>> { + static constexpr ArgumentType Type = ArgumentType::OutMoveHandle; +}; + +template <typename T, int A> +struct ArgumentTraits<Buffer<T, A>> { + static constexpr ArgumentType Type = (A & BufferAttr_In) == 0 ? ArgumentType::OutBuffer : ArgumentType::InBuffer; +}; + +template <typename T, int A> +struct ArgumentTraits<LargeData<T, A>> { + static constexpr ArgumentType Type = (A & BufferAttr_In) == 0 ? ArgumentType::OutLargeData : ArgumentType::InLargeData; +}; + +template <typename T> +struct ArgumentTraits { + static constexpr ArgumentType Type = ArgumentType::InData; +}; +// clang-format on + +} // namespace Service diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp index 31da86074..dfcac1ffd 100644 --- a/src/core/hle/service/fatal/fatal.cpp +++ b/src/core/hle/service/fatal/fatal.cpp @@ -73,8 +73,8 @@ static void GenerateErrorReport(Core::System& system, Result error_code, const F "Program entry point: 0x{:16X}\n" "\n", Common::g_scm_branch, Common::g_scm_desc, title_id, error_code.raw, - 2000 + static_cast<u32>(error_code.module.Value()), - static_cast<u32>(error_code.description.Value()), info.set_flags, info.program_entry_point); + 2000 + static_cast<u32>(error_code.GetModule()), + static_cast<u32>(error_code.GetDescription()), info.set_flags, info.program_entry_point); if (info.backtrace_size != 0x0) { crash_report += "Registers:\n"; for (size_t i = 0; i < info.registers.size(); i++) { diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index ca6d8d607..ae230afc0 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -12,18 +12,17 @@ #include "core/file_sys/card_image.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/errors.h" -#include "core/file_sys/mode.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" #include "core/file_sys/romfs_factory.h" #include "core/file_sys/savedata_factory.h" #include "core/file_sys/sdmc_factory.h" -#include "core/file_sys/vfs.h" -#include "core/file_sys/vfs_offset.h" +#include "core/file_sys/vfs/vfs.h" +#include "core/file_sys/vfs/vfs_offset.h" #include "core/hle/service/filesystem/filesystem.h" -#include "core/hle/service/filesystem/fsp_ldr.h" -#include "core/hle/service/filesystem/fsp_pr.h" -#include "core/hle/service/filesystem/fsp_srv.h" +#include "core/hle/service/filesystem/fsp/fsp_ldr.h" +#include "core/hle/service/filesystem/fsp/fsp_pr.h" +#include "core/hle/service/filesystem/fsp/fsp_srv.h" #include "core/hle/service/filesystem/romfs_controller.h" #include "core/hle/service/filesystem/save_data_controller.h" #include "core/hle/service/server_manager.h" @@ -53,12 +52,12 @@ Result VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size std::string path(Common::FS::SanitizePath(path_)); auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path)); if (dir == nullptr) { - return FileSys::ERROR_PATH_NOT_FOUND; + return FileSys::ResultPathNotFound; } - FileSys::EntryType entry_type{}; + FileSys::DirectoryEntryType entry_type{}; if (GetEntryType(&entry_type, path) == ResultSuccess) { - return FileSys::ERROR_PATH_ALREADY_EXISTS; + return FileSys::ResultPathAlreadyExists; } auto file = dir->CreateFile(Common::FS::GetFilename(path)); @@ -82,7 +81,7 @@ Result VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const { auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path)); if (dir == nullptr || dir->GetFile(Common::FS::GetFilename(path)) == nullptr) { - return FileSys::ERROR_PATH_NOT_FOUND; + return FileSys::ResultPathNotFound; } if (!dir->DeleteFile(Common::FS::GetFilename(path))) { // TODO(DarkLordZach): Find a better error code for this @@ -153,12 +152,12 @@ Result VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_, if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) { // Use more-optimized vfs implementation rename. if (src == nullptr) { - return FileSys::ERROR_PATH_NOT_FOUND; + return FileSys::ResultPathNotFound; } if (dst && Common::FS::Exists(dst->GetFullPath())) { LOG_ERROR(Service_FS, "File at new_path={} already exists", dst->GetFullPath()); - return FileSys::ERROR_PATH_ALREADY_EXISTS; + return FileSys::ResultPathAlreadyExists; } if (!src->Rename(Common::FS::GetFilename(dest_path))) { @@ -195,7 +194,7 @@ Result VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_, if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) { // Use more-optimized vfs implementation rename. if (src == nullptr) - return FileSys::ERROR_PATH_NOT_FOUND; + return FileSys::ResultPathNotFound; if (!src->Rename(Common::FS::GetFilename(dest_path))) { // TODO(DarkLordZach): Find a better error code for this return ResultUnknown; @@ -214,7 +213,8 @@ Result VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_, } Result VfsDirectoryServiceWrapper::OpenFile(FileSys::VirtualFile* out_file, - const std::string& path_, FileSys::Mode mode) const { + const std::string& path_, + FileSys::OpenMode mode) const { const std::string path(Common::FS::SanitizePath(path_)); std::string_view npath = path; while (!npath.empty() && (npath[0] == '/' || npath[0] == '\\')) { @@ -223,10 +223,10 @@ Result VfsDirectoryServiceWrapper::OpenFile(FileSys::VirtualFile* out_file, auto file = backing->GetFileRelative(npath); if (file == nullptr) { - return FileSys::ERROR_PATH_NOT_FOUND; + return FileSys::ResultPathNotFound; } - if (mode == FileSys::Mode::Append) { + if (mode == FileSys::OpenMode::AllowAppend) { *out_file = std::make_shared<FileSys::OffsetVfsFile>(file, 0, file->GetSize()); } else { *out_file = file; @@ -241,50 +241,50 @@ Result VfsDirectoryServiceWrapper::OpenDirectory(FileSys::VirtualDir* out_direct auto dir = GetDirectoryRelativeWrapped(backing, path); if (dir == nullptr) { // TODO(DarkLordZach): Find a better error code for this - return FileSys::ERROR_PATH_NOT_FOUND; + return FileSys::ResultPathNotFound; } *out_directory = dir; return ResultSuccess; } -Result VfsDirectoryServiceWrapper::GetEntryType(FileSys::EntryType* out_entry_type, +Result VfsDirectoryServiceWrapper::GetEntryType(FileSys::DirectoryEntryType* out_entry_type, const std::string& path_) const { std::string path(Common::FS::SanitizePath(path_)); auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path)); if (dir == nullptr) { - return FileSys::ERROR_PATH_NOT_FOUND; + return FileSys::ResultPathNotFound; } auto filename = Common::FS::GetFilename(path); // TODO(Subv): Some games use the '/' path, find out what this means. if (filename.empty()) { - *out_entry_type = FileSys::EntryType::Directory; + *out_entry_type = FileSys::DirectoryEntryType::Directory; return ResultSuccess; } if (dir->GetFile(filename) != nullptr) { - *out_entry_type = FileSys::EntryType::File; + *out_entry_type = FileSys::DirectoryEntryType::File; return ResultSuccess; } if (dir->GetSubdirectory(filename) != nullptr) { - *out_entry_type = FileSys::EntryType::Directory; + *out_entry_type = FileSys::DirectoryEntryType::Directory; return ResultSuccess; } - return FileSys::ERROR_PATH_NOT_FOUND; + return FileSys::ResultPathNotFound; } Result VfsDirectoryServiceWrapper::GetFileTimeStampRaw( FileSys::FileTimeStampRaw* out_file_time_stamp_raw, const std::string& path) const { auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path)); if (dir == nullptr) { - return FileSys::ERROR_PATH_NOT_FOUND; + return FileSys::ResultPathNotFound; } - FileSys::EntryType entry_type; + FileSys::DirectoryEntryType entry_type; if (GetEntryType(&entry_type, path) != ResultSuccess) { - return FileSys::ERROR_PATH_NOT_FOUND; + return FileSys::ResultPathNotFound; } *out_file_time_stamp_raw = dir->GetFileTimeStamp(Common::FS::GetFilename(path)); @@ -317,7 +317,7 @@ Result FileSystemController::OpenProcess( const auto it = registrations.find(process_id); if (it == registrations.end()) { - return FileSys::ERROR_ENTITY_NOT_FOUND; + return FileSys::ResultTargetNotFound; } *out_program_id = it->second.program_id; @@ -347,7 +347,7 @@ std::shared_ptr<SaveDataController> FileSystemController::OpenSaveDataController std::shared_ptr<FileSys::SaveDataFactory> FileSystemController::CreateSaveDataFactory( ProgramId program_id) { using YuzuPath = Common::FS::YuzuPath; - const auto rw_mode = FileSys::Mode::ReadWrite; + const auto rw_mode = FileSys::OpenMode::ReadWrite; auto vfs = system.GetFilesystem(); const auto nand_directory = @@ -360,12 +360,12 @@ Result FileSystemController::OpenSDMC(FileSys::VirtualDir* out_sdmc) const { LOG_TRACE(Service_FS, "Opening SDMC"); if (sdmc_factory == nullptr) { - return FileSys::ERROR_SD_CARD_NOT_FOUND; + return FileSys::ResultPortSdCardNoDevice; } auto sdmc = sdmc_factory->Open(); if (sdmc == nullptr) { - return FileSys::ERROR_SD_CARD_NOT_FOUND; + return FileSys::ResultPortSdCardNoDevice; } *out_sdmc = sdmc; @@ -377,12 +377,12 @@ Result FileSystemController::OpenBISPartition(FileSys::VirtualDir* out_bis_parti LOG_TRACE(Service_FS, "Opening BIS Partition with id={:08X}", id); if (bis_factory == nullptr) { - return FileSys::ERROR_ENTITY_NOT_FOUND; + return FileSys::ResultTargetNotFound; } auto part = bis_factory->OpenPartition(id); if (part == nullptr) { - return FileSys::ERROR_INVALID_ARGUMENT; + return FileSys::ResultInvalidArgument; } *out_bis_partition = part; @@ -394,12 +394,12 @@ Result FileSystemController::OpenBISPartitionStorage( LOG_TRACE(Service_FS, "Opening BIS Partition Storage with id={:08X}", id); if (bis_factory == nullptr) { - return FileSys::ERROR_ENTITY_NOT_FOUND; + return FileSys::ResultTargetNotFound; } auto part = bis_factory->OpenPartitionStorage(id, system.GetFilesystem()); if (part == nullptr) { - return FileSys::ERROR_INVALID_ARGUMENT; + return FileSys::ResultInvalidArgument; } *out_bis_partition_storage = part; @@ -686,15 +686,15 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove using YuzuPath = Common::FS::YuzuPath; const auto sdmc_dir_path = Common::FS::GetYuzuPath(YuzuPath::SDMCDir); const auto sdmc_load_dir_path = sdmc_dir_path / "atmosphere/contents"; - const auto rw_mode = FileSys::Mode::ReadWrite; + const auto rw_mode = FileSys::OpenMode::ReadWrite; auto nand_directory = vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::NANDDir), rw_mode); auto sd_directory = vfs.OpenDirectory(Common::FS::PathToUTF8String(sdmc_dir_path), rw_mode); - auto load_directory = - vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::LoadDir), FileSys::Mode::Read); - auto sd_load_directory = - vfs.OpenDirectory(Common::FS::PathToUTF8String(sdmc_load_dir_path), FileSys::Mode::Read); + auto load_directory = vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::LoadDir), + FileSys::OpenMode::Read); + auto sd_load_directory = vfs.OpenDirectory(Common::FS::PathToUTF8String(sdmc_load_dir_path), + FileSys::OpenMode::Read); auto dump_directory = vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::DumpDir), rw_mode); diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 48f37d289..718500385 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h @@ -4,9 +4,11 @@ #pragma once #include <memory> +#include <mutex> #include "common/common_types.h" -#include "core/file_sys/directory.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/fs_directory.h" +#include "core/file_sys/fs_filesystem.h" +#include "core/file_sys/vfs/vfs.h" #include "core/hle/result.h" namespace Core { @@ -26,7 +28,6 @@ class XCI; enum class BisPartitionId : u32; enum class ContentRecordType : u8; -enum class Mode : u32; enum class SaveDataSpaceId : u8; enum class SaveDataType : u8; enum class StorageId : u8; @@ -57,13 +58,6 @@ enum class ImageDirectoryId : u32 { SdCard, }; -enum class OpenDirectoryMode : u64 { - Directory = (1 << 0), - File = (1 << 1), - All = Directory | File -}; -DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode); - using ProcessId = u64; using ProgramId = u64; @@ -237,7 +231,7 @@ public: * @return Opened file, or error code */ Result OpenFile(FileSys::VirtualFile* out_file, const std::string& path, - FileSys::Mode mode) const; + FileSys::OpenMode mode) const; /** * Open a directory specified by its path @@ -250,7 +244,7 @@ public: * Get the type of the specified path * @return The type of the specified path or error code */ - Result GetEntryType(FileSys::EntryType* out_entry_type, const std::string& path) const; + Result GetEntryType(FileSys::DirectoryEntryType* out_entry_type, const std::string& path) const; /** * Get the timestamp of the specified path diff --git a/src/core/hle/service/filesystem/fsp/fs_i_directory.cpp b/src/core/hle/service/filesystem/fsp/fs_i_directory.cpp new file mode 100644 index 000000000..39690018b --- /dev/null +++ b/src/core/hle/service/filesystem/fsp/fs_i_directory.cpp @@ -0,0 +1,84 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/file_sys/fs_filesystem.h" +#include "core/file_sys/savedata_factory.h" +#include "core/hle/service/filesystem/fsp/fs_i_directory.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::FileSystem { + +template <typename T> +static void BuildEntryIndex(std::vector<FileSys::DirectoryEntry>& entries, + const std::vector<T>& new_data, FileSys::DirectoryEntryType type) { + entries.reserve(entries.size() + new_data.size()); + + for (const auto& new_entry : new_data) { + auto name = new_entry->GetName(); + + if (type == FileSys::DirectoryEntryType::File && + name == FileSys::GetSaveDataSizeFileName()) { + continue; + } + + entries.emplace_back(name, static_cast<s8>(type), + type == FileSys::DirectoryEntryType::Directory ? 0 + : new_entry->GetSize()); + } +} + +IDirectory::IDirectory(Core::System& system_, FileSys::VirtualDir backend_, + FileSys::OpenDirectoryMode mode) + : ServiceFramework{system_, "IDirectory"}, backend(std::move(backend_)) { + static const FunctionInfo functions[] = { + {0, &IDirectory::Read, "Read"}, + {1, &IDirectory::GetEntryCount, "GetEntryCount"}, + }; + RegisterHandlers(functions); + + // TODO(DarkLordZach): Verify that this is the correct behavior. + // Build entry index now to save time later. + if (True(mode & FileSys::OpenDirectoryMode::Directory)) { + BuildEntryIndex(entries, backend->GetSubdirectories(), + FileSys::DirectoryEntryType::Directory); + } + if (True(mode & FileSys::OpenDirectoryMode::File)) { + BuildEntryIndex(entries, backend->GetFiles(), FileSys::DirectoryEntryType::File); + } +} + +void IDirectory::Read(HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called."); + + // Calculate how many entries we can fit in the output buffer + const u64 count_entries = ctx.GetWriteBufferNumElements<FileSys::DirectoryEntry>(); + + // Cap at total number of entries. + const u64 actual_entries = std::min(count_entries, entries.size() - next_entry_index); + + // Determine data start and end + const auto* begin = reinterpret_cast<u8*>(entries.data() + next_entry_index); + const auto* end = reinterpret_cast<u8*>(entries.data() + next_entry_index + actual_entries); + const auto range_size = static_cast<std::size_t>(std::distance(begin, end)); + + next_entry_index += actual_entries; + + // Write the data to memory + ctx.WriteBuffer(begin, range_size); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(actual_entries); +} + +void IDirectory::GetEntryCount(HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + u64 count = entries.size() - next_entry_index; + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(count); +} + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp/fs_i_directory.h b/src/core/hle/service/filesystem/fsp/fs_i_directory.h new file mode 100644 index 000000000..793ecfcd7 --- /dev/null +++ b/src/core/hle/service/filesystem/fsp/fs_i_directory.h @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/file_sys/vfs/vfs.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/service.h" + +namespace FileSys { +struct DirectoryEntry; +} + +namespace Service::FileSystem { + +class IDirectory final : public ServiceFramework<IDirectory> { +public: + explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_, + FileSys::OpenDirectoryMode mode); + +private: + FileSys::VirtualDir backend; + std::vector<FileSys::DirectoryEntry> entries; + u64 next_entry_index = 0; + + void Read(HLERequestContext& ctx); + void GetEntryCount(HLERequestContext& ctx); +}; + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp/fs_i_file.cpp b/src/core/hle/service/filesystem/fsp/fs_i_file.cpp new file mode 100644 index 000000000..9a18f6ec5 --- /dev/null +++ b/src/core/hle/service/filesystem/fsp/fs_i_file.cpp @@ -0,0 +1,127 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/file_sys/errors.h" +#include "core/hle/service/filesystem/fsp/fs_i_file.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::FileSystem { + +IFile::IFile(Core::System& system_, FileSys::VirtualFile backend_) + : ServiceFramework{system_, "IFile"}, backend(std::move(backend_)) { + static const FunctionInfo functions[] = { + {0, &IFile::Read, "Read"}, + {1, &IFile::Write, "Write"}, + {2, &IFile::Flush, "Flush"}, + {3, &IFile::SetSize, "SetSize"}, + {4, &IFile::GetSize, "GetSize"}, + {5, nullptr, "OperateRange"}, + {6, nullptr, "OperateRangeWithBuffer"}, + }; + RegisterHandlers(functions); +} + +void IFile::Read(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u64 option = rp.Pop<u64>(); + const s64 offset = rp.Pop<s64>(); + const s64 length = rp.Pop<s64>(); + + LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, length); + + // Error checking + if (length < 0) { + LOG_ERROR(Service_FS, "Length is less than 0, length={}", length); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(FileSys::ResultInvalidSize); + return; + } + if (offset < 0) { + LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(FileSys::ResultInvalidOffset); + return; + } + + // Read the data from the Storage backend + std::vector<u8> output = backend->ReadBytes(length, offset); + + // Write the data to memory + ctx.WriteBuffer(output); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(static_cast<u64>(output.size())); +} + +void IFile::Write(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u64 option = rp.Pop<u64>(); + const s64 offset = rp.Pop<s64>(); + const s64 length = rp.Pop<s64>(); + + LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, length); + + // Error checking + if (length < 0) { + LOG_ERROR(Service_FS, "Length is less than 0, length={}", length); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(FileSys::ResultInvalidSize); + return; + } + if (offset < 0) { + LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(FileSys::ResultInvalidOffset); + return; + } + + const auto data = ctx.ReadBuffer(); + + ASSERT_MSG(static_cast<s64>(data.size()) <= length, + "Attempting to write more data than requested (requested={:016X}, actual={:016X}).", + length, data.size()); + + // Write the data to the Storage backend + const auto write_size = + static_cast<std::size_t>(std::distance(data.begin(), data.begin() + length)); + const std::size_t written = backend->Write(data.data(), write_size, offset); + + ASSERT_MSG(static_cast<s64>(written) == length, + "Could not write all bytes to file (requested={:016X}, actual={:016X}).", length, + written); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IFile::Flush(HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + // Exists for SDK compatibiltity -- No need to flush file. + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IFile::SetSize(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u64 size = rp.Pop<u64>(); + LOG_DEBUG(Service_FS, "called, size={}", size); + + backend->Resize(size); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IFile::GetSize(HLERequestContext& ctx) { + const u64 size = backend->GetSize(); + LOG_DEBUG(Service_FS, "called, size={}", size); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push<u64>(size); +} + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp/fs_i_file.h b/src/core/hle/service/filesystem/fsp/fs_i_file.h new file mode 100644 index 000000000..5e5430c67 --- /dev/null +++ b/src/core/hle/service/filesystem/fsp/fs_i_file.h @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/service.h" + +namespace Service::FileSystem { + +class IFile final : public ServiceFramework<IFile> { +public: + explicit IFile(Core::System& system_, FileSys::VirtualFile backend_); + +private: + FileSys::VirtualFile backend; + + void Read(HLERequestContext& ctx); + void Write(HLERequestContext& ctx); + void Flush(HLERequestContext& ctx); + void SetSize(HLERequestContext& ctx); + void GetSize(HLERequestContext& ctx); +}; + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp/fs_i_filesystem.cpp b/src/core/hle/service/filesystem/fsp/fs_i_filesystem.cpp new file mode 100644 index 000000000..efa394dd1 --- /dev/null +++ b/src/core/hle/service/filesystem/fsp/fs_i_filesystem.cpp @@ -0,0 +1,262 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/string_util.h" +#include "core/hle/service/filesystem/fsp/fs_i_directory.h" +#include "core/hle/service/filesystem/fsp/fs_i_file.h" +#include "core/hle/service/filesystem/fsp/fs_i_filesystem.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::FileSystem { + +IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_) + : ServiceFramework{system_, "IFileSystem"}, backend{std::move(backend_)}, size{std::move( + size_)} { + static const FunctionInfo functions[] = { + {0, &IFileSystem::CreateFile, "CreateFile"}, + {1, &IFileSystem::DeleteFile, "DeleteFile"}, + {2, &IFileSystem::CreateDirectory, "CreateDirectory"}, + {3, &IFileSystem::DeleteDirectory, "DeleteDirectory"}, + {4, &IFileSystem::DeleteDirectoryRecursively, "DeleteDirectoryRecursively"}, + {5, &IFileSystem::RenameFile, "RenameFile"}, + {6, nullptr, "RenameDirectory"}, + {7, &IFileSystem::GetEntryType, "GetEntryType"}, + {8, &IFileSystem::OpenFile, "OpenFile"}, + {9, &IFileSystem::OpenDirectory, "OpenDirectory"}, + {10, &IFileSystem::Commit, "Commit"}, + {11, &IFileSystem::GetFreeSpaceSize, "GetFreeSpaceSize"}, + {12, &IFileSystem::GetTotalSpaceSize, "GetTotalSpaceSize"}, + {13, &IFileSystem::CleanDirectoryRecursively, "CleanDirectoryRecursively"}, + {14, &IFileSystem::GetFileTimeStampRaw, "GetFileTimeStampRaw"}, + {15, nullptr, "QueryEntry"}, + {16, &IFileSystem::GetFileSystemAttribute, "GetFileSystemAttribute"}, + }; + RegisterHandlers(functions); +} + +void IFileSystem::CreateFile(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const auto file_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(file_buffer); + + const u64 file_mode = rp.Pop<u64>(); + const u32 file_size = rp.Pop<u32>(); + + LOG_DEBUG(Service_FS, "called. file={}, mode=0x{:X}, size=0x{:08X}", name, file_mode, + file_size); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(backend.CreateFile(name, file_size)); +} + +void IFileSystem::DeleteFile(HLERequestContext& ctx) { + const auto file_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(file_buffer); + + LOG_DEBUG(Service_FS, "called. file={}", name); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(backend.DeleteFile(name)); +} + +void IFileSystem::CreateDirectory(HLERequestContext& ctx) { + const auto file_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(file_buffer); + + LOG_DEBUG(Service_FS, "called. directory={}", name); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(backend.CreateDirectory(name)); +} + +void IFileSystem::DeleteDirectory(HLERequestContext& ctx) { + const auto file_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(file_buffer); + + LOG_DEBUG(Service_FS, "called. directory={}", name); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(backend.DeleteDirectory(name)); +} + +void IFileSystem::DeleteDirectoryRecursively(HLERequestContext& ctx) { + const auto file_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(file_buffer); + + LOG_DEBUG(Service_FS, "called. directory={}", name); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(backend.DeleteDirectoryRecursively(name)); +} + +void IFileSystem::CleanDirectoryRecursively(HLERequestContext& ctx) { + const auto file_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(file_buffer); + + LOG_DEBUG(Service_FS, "called. Directory: {}", name); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(backend.CleanDirectoryRecursively(name)); +} + +void IFileSystem::RenameFile(HLERequestContext& ctx) { + const std::string src_name = Common::StringFromBuffer(ctx.ReadBuffer(0)); + const std::string dst_name = Common::StringFromBuffer(ctx.ReadBuffer(1)); + + LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(backend.RenameFile(src_name, dst_name)); +} + +void IFileSystem::OpenFile(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const auto file_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(file_buffer); + + const auto mode = static_cast<FileSys::OpenMode>(rp.Pop<u32>()); + + LOG_DEBUG(Service_FS, "called. file={}, mode={}", name, mode); + + FileSys::VirtualFile vfs_file{}; + auto result = backend.OpenFile(&vfs_file, name, mode); + if (result != ResultSuccess) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + return; + } + + auto file = std::make_shared<IFile>(system, vfs_file); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IFile>(std::move(file)); +} + +void IFileSystem::OpenDirectory(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const auto file_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(file_buffer); + const auto mode = rp.PopRaw<FileSys::OpenDirectoryMode>(); + + LOG_DEBUG(Service_FS, "called. directory={}, mode={}", name, mode); + + FileSys::VirtualDir vfs_dir{}; + auto result = backend.OpenDirectory(&vfs_dir, name); + if (result != ResultSuccess) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + return; + } + + auto directory = std::make_shared<IDirectory>(system, vfs_dir, mode); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IDirectory>(std::move(directory)); +} + +void IFileSystem::GetEntryType(HLERequestContext& ctx) { + const auto file_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(file_buffer); + + LOG_DEBUG(Service_FS, "called. file={}", name); + + FileSys::DirectoryEntryType vfs_entry_type{}; + auto result = backend.GetEntryType(&vfs_entry_type, name); + if (result != ResultSuccess) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + return; + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<u32>(static_cast<u32>(vfs_entry_type)); +} + +void IFileSystem::Commit(HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IFileSystem::GetFreeSpaceSize(HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(size.get_free_size()); +} + +void IFileSystem::GetTotalSpaceSize(HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(size.get_total_size()); +} + +void IFileSystem::GetFileTimeStampRaw(HLERequestContext& ctx) { + const auto file_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(file_buffer); + + LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", name); + + FileSys::FileTimeStampRaw vfs_timestamp{}; + auto result = backend.GetFileTimeStampRaw(&vfs_timestamp, name); + if (result != ResultSuccess) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + return; + } + + IPC::ResponseBuilder rb{ctx, 10}; + rb.Push(ResultSuccess); + rb.PushRaw(vfs_timestamp); +} + +void IFileSystem::GetFileSystemAttribute(HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) called"); + + struct FileSystemAttribute { + u8 dir_entry_name_length_max_defined; + u8 file_entry_name_length_max_defined; + u8 dir_path_name_length_max_defined; + u8 file_path_name_length_max_defined; + INSERT_PADDING_BYTES_NOINIT(0x5); + u8 utf16_dir_entry_name_length_max_defined; + u8 utf16_file_entry_name_length_max_defined; + u8 utf16_dir_path_name_length_max_defined; + u8 utf16_file_path_name_length_max_defined; + INSERT_PADDING_BYTES_NOINIT(0x18); + s32 dir_entry_name_length_max; + s32 file_entry_name_length_max; + s32 dir_path_name_length_max; + s32 file_path_name_length_max; + INSERT_PADDING_WORDS_NOINIT(0x5); + s32 utf16_dir_entry_name_length_max; + s32 utf16_file_entry_name_length_max; + s32 utf16_dir_path_name_length_max; + s32 utf16_file_path_name_length_max; + INSERT_PADDING_WORDS_NOINIT(0x18); + INSERT_PADDING_WORDS_NOINIT(0x1); + }; + static_assert(sizeof(FileSystemAttribute) == 0xc0, "FileSystemAttribute has incorrect size"); + + FileSystemAttribute savedata_attribute{}; + savedata_attribute.dir_entry_name_length_max_defined = true; + savedata_attribute.file_entry_name_length_max_defined = true; + savedata_attribute.dir_entry_name_length_max = 0x40; + savedata_attribute.file_entry_name_length_max = 0x40; + + IPC::ResponseBuilder rb{ctx, 50}; + rb.Push(ResultSuccess); + rb.PushRaw(savedata_attribute); +} + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp/fs_i_filesystem.h b/src/core/hle/service/filesystem/fsp/fs_i_filesystem.h new file mode 100644 index 000000000..b06b3ef0e --- /dev/null +++ b/src/core/hle/service/filesystem/fsp/fs_i_filesystem.h @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/file_sys/vfs/vfs.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/filesystem/fsp/fsp_util.h" +#include "core/hle/service/service.h" + +namespace Service::FileSystem { + +class IFileSystem final : public ServiceFramework<IFileSystem> { +public: + explicit IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_); + + void CreateFile(HLERequestContext& ctx); + void DeleteFile(HLERequestContext& ctx); + void CreateDirectory(HLERequestContext& ctx); + void DeleteDirectory(HLERequestContext& ctx); + void DeleteDirectoryRecursively(HLERequestContext& ctx); + void CleanDirectoryRecursively(HLERequestContext& ctx); + void RenameFile(HLERequestContext& ctx); + void OpenFile(HLERequestContext& ctx); + void OpenDirectory(HLERequestContext& ctx); + void GetEntryType(HLERequestContext& ctx); + void Commit(HLERequestContext& ctx); + void GetFreeSpaceSize(HLERequestContext& ctx); + void GetTotalSpaceSize(HLERequestContext& ctx); + void GetFileTimeStampRaw(HLERequestContext& ctx); + void GetFileSystemAttribute(HLERequestContext& ctx); + +private: + VfsDirectoryServiceWrapper backend; + SizeGetter size; +}; + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp/fs_i_storage.cpp b/src/core/hle/service/filesystem/fsp/fs_i_storage.cpp new file mode 100644 index 000000000..98223c1f9 --- /dev/null +++ b/src/core/hle/service/filesystem/fsp/fs_i_storage.cpp @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/file_sys/errors.h" +#include "core/hle/service/filesystem/fsp/fs_i_storage.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::FileSystem { + +IStorage::IStorage(Core::System& system_, FileSys::VirtualFile backend_) + : ServiceFramework{system_, "IStorage"}, backend(std::move(backend_)) { + static const FunctionInfo functions[] = { + {0, &IStorage::Read, "Read"}, + {1, nullptr, "Write"}, + {2, nullptr, "Flush"}, + {3, nullptr, "SetSize"}, + {4, &IStorage::GetSize, "GetSize"}, + {5, nullptr, "OperateRange"}, + }; + RegisterHandlers(functions); +} + +void IStorage::Read(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const s64 offset = rp.Pop<s64>(); + const s64 length = rp.Pop<s64>(); + + LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length); + + // Error checking + if (length < 0) { + LOG_ERROR(Service_FS, "Length is less than 0, length={}", length); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(FileSys::ResultInvalidSize); + return; + } + if (offset < 0) { + LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(FileSys::ResultInvalidOffset); + return; + } + + // Read the data from the Storage backend + std::vector<u8> output = backend->ReadBytes(length, offset); + // Write the data to memory + ctx.WriteBuffer(output); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IStorage::GetSize(HLERequestContext& ctx) { + const u64 size = backend->GetSize(); + LOG_DEBUG(Service_FS, "called, size={}", size); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push<u64>(size); +} + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp/fs_i_storage.h b/src/core/hle/service/filesystem/fsp/fs_i_storage.h new file mode 100644 index 000000000..cb5bebcc9 --- /dev/null +++ b/src/core/hle/service/filesystem/fsp/fs_i_storage.h @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/file_sys/vfs/vfs.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/service.h" + +namespace Service::FileSystem { + +class IStorage final : public ServiceFramework<IStorage> { +public: + explicit IStorage(Core::System& system_, FileSys::VirtualFile backend_); + +private: + FileSys::VirtualFile backend; + + void Read(HLERequestContext& ctx); + void GetSize(HLERequestContext& ctx); +}; + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp_ldr.cpp b/src/core/hle/service/filesystem/fsp/fsp_ldr.cpp index 1e3366e71..8ee733f47 100644 --- a/src/core/hle/service/filesystem/fsp_ldr.cpp +++ b/src/core/hle/service/filesystem/fsp/fsp_ldr.cpp @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "core/hle/service/filesystem/fsp_ldr.h" +#include "core/hle/service/filesystem/fsp/fsp_ldr.h" namespace Service::FileSystem { diff --git a/src/core/hle/service/filesystem/fsp_ldr.h b/src/core/hle/service/filesystem/fsp/fsp_ldr.h index 358739a87..358739a87 100644 --- a/src/core/hle/service/filesystem/fsp_ldr.h +++ b/src/core/hle/service/filesystem/fsp/fsp_ldr.h diff --git a/src/core/hle/service/filesystem/fsp_pr.cpp b/src/core/hle/service/filesystem/fsp/fsp_pr.cpp index 4ffc31977..7c03ebaea 100644 --- a/src/core/hle/service/filesystem/fsp_pr.cpp +++ b/src/core/hle/service/filesystem/fsp/fsp_pr.cpp @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "core/hle/service/filesystem/fsp_pr.h" +#include "core/hle/service/filesystem/fsp/fsp_pr.h" namespace Service::FileSystem { diff --git a/src/core/hle/service/filesystem/fsp_pr.h b/src/core/hle/service/filesystem/fsp/fsp_pr.h index bd4e0a730..bd4e0a730 100644 --- a/src/core/hle/service/filesystem/fsp_pr.h +++ b/src/core/hle/service/filesystem/fsp/fsp_pr.h diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp index a2397bec4..2be72b021 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp @@ -15,18 +15,20 @@ #include "common/settings.h" #include "common/string_util.h" #include "core/core.h" -#include "core/file_sys/directory.h" #include "core/file_sys/errors.h" -#include "core/file_sys/mode.h" +#include "core/file_sys/fs_directory.h" +#include "core/file_sys/fs_filesystem.h" #include "core/file_sys/nca_metadata.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/romfs_factory.h" #include "core/file_sys/savedata_factory.h" #include "core/file_sys/system_archive/system_archive.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" #include "core/hle/result.h" #include "core/hle/service/filesystem/filesystem.h" -#include "core/hle/service/filesystem/fsp_srv.h" +#include "core/hle/service/filesystem/fsp/fs_i_filesystem.h" +#include "core/hle/service/filesystem/fsp/fs_i_storage.h" +#include "core/hle/service/filesystem/fsp/fsp_srv.h" #include "core/hle/service/filesystem/romfs_controller.h" #include "core/hle/service/filesystem/save_data_controller.h" #include "core/hle/service/hle_ipc.h" @@ -34,19 +36,6 @@ #include "core/reporter.h" namespace Service::FileSystem { - -struct SizeGetter { - std::function<u64()> get_free_size; - std::function<u64()> get_total_size; - - static SizeGetter FromStorageId(const FileSystemController& fsc, FileSys::StorageId id) { - return { - [&fsc, id] { return fsc.GetFreeSpaceSize(id); }, - [&fsc, id] { return fsc.GetTotalSpaceSize(id); }, - }; - } -}; - enum class FileSystemType : u8 { Invalid0 = 0, Invalid1 = 1, @@ -58,525 +47,6 @@ enum class FileSystemType : u8 { ApplicationPackage = 7, }; -class IStorage final : public ServiceFramework<IStorage> { -public: - explicit IStorage(Core::System& system_, FileSys::VirtualFile backend_) - : ServiceFramework{system_, "IStorage"}, backend(std::move(backend_)) { - static const FunctionInfo functions[] = { - {0, &IStorage::Read, "Read"}, - {1, nullptr, "Write"}, - {2, nullptr, "Flush"}, - {3, nullptr, "SetSize"}, - {4, &IStorage::GetSize, "GetSize"}, - {5, nullptr, "OperateRange"}, - }; - RegisterHandlers(functions); - } - -private: - FileSys::VirtualFile backend; - - void Read(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const s64 offset = rp.Pop<s64>(); - const s64 length = rp.Pop<s64>(); - - LOG_DEBUG(Service_FS, "called, offset=0x{:X}, length={}", offset, length); - - // Error checking - if (length < 0) { - LOG_ERROR(Service_FS, "Length is less than 0, length={}", length); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(FileSys::ERROR_INVALID_SIZE); - return; - } - if (offset < 0) { - LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(FileSys::ERROR_INVALID_OFFSET); - return; - } - - // Read the data from the Storage backend - std::vector<u8> output = backend->ReadBytes(length, offset); - // Write the data to memory - ctx.WriteBuffer(output); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void GetSize(HLERequestContext& ctx) { - const u64 size = backend->GetSize(); - LOG_DEBUG(Service_FS, "called, size={}", size); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push<u64>(size); - } -}; - -class IFile final : public ServiceFramework<IFile> { -public: - explicit IFile(Core::System& system_, FileSys::VirtualFile backend_) - : ServiceFramework{system_, "IFile"}, backend(std::move(backend_)) { - static const FunctionInfo functions[] = { - {0, &IFile::Read, "Read"}, - {1, &IFile::Write, "Write"}, - {2, &IFile::Flush, "Flush"}, - {3, &IFile::SetSize, "SetSize"}, - {4, &IFile::GetSize, "GetSize"}, - {5, nullptr, "OperateRange"}, - {6, nullptr, "OperateRangeWithBuffer"}, - }; - RegisterHandlers(functions); - } - -private: - FileSys::VirtualFile backend; - - void Read(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 option = rp.Pop<u64>(); - const s64 offset = rp.Pop<s64>(); - const s64 length = rp.Pop<s64>(); - - LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, - length); - - // Error checking - if (length < 0) { - LOG_ERROR(Service_FS, "Length is less than 0, length={}", length); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(FileSys::ERROR_INVALID_SIZE); - return; - } - if (offset < 0) { - LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(FileSys::ERROR_INVALID_OFFSET); - return; - } - - // Read the data from the Storage backend - std::vector<u8> output = backend->ReadBytes(length, offset); - - // Write the data to memory - ctx.WriteBuffer(output); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(static_cast<u64>(output.size())); - } - - void Write(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 option = rp.Pop<u64>(); - const s64 offset = rp.Pop<s64>(); - const s64 length = rp.Pop<s64>(); - - LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, - length); - - // Error checking - if (length < 0) { - LOG_ERROR(Service_FS, "Length is less than 0, length={}", length); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(FileSys::ERROR_INVALID_SIZE); - return; - } - if (offset < 0) { - LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(FileSys::ERROR_INVALID_OFFSET); - return; - } - - const auto data = ctx.ReadBuffer(); - - ASSERT_MSG( - static_cast<s64>(data.size()) <= length, - "Attempting to write more data than requested (requested={:016X}, actual={:016X}).", - length, data.size()); - - // Write the data to the Storage backend - const auto write_size = - static_cast<std::size_t>(std::distance(data.begin(), data.begin() + length)); - const std::size_t written = backend->Write(data.data(), write_size, offset); - - ASSERT_MSG(static_cast<s64>(written) == length, - "Could not write all bytes to file (requested={:016X}, actual={:016X}).", length, - written); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void Flush(HLERequestContext& ctx) { - LOG_DEBUG(Service_FS, "called"); - - // Exists for SDK compatibiltity -- No need to flush file. - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void SetSize(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 size = rp.Pop<u64>(); - LOG_DEBUG(Service_FS, "called, size={}", size); - - backend->Resize(size); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void GetSize(HLERequestContext& ctx) { - const u64 size = backend->GetSize(); - LOG_DEBUG(Service_FS, "called, size={}", size); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push<u64>(size); - } -}; - -template <typename T> -static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vector<T>& new_data, - FileSys::EntryType type) { - entries.reserve(entries.size() + new_data.size()); - - for (const auto& new_entry : new_data) { - auto name = new_entry->GetName(); - - if (type == FileSys::EntryType::File && name == FileSys::GetSaveDataSizeFileName()) { - continue; - } - - entries.emplace_back(name, type, - type == FileSys::EntryType::Directory ? 0 : new_entry->GetSize()); - } -} - -class IDirectory final : public ServiceFramework<IDirectory> { -public: - explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_, OpenDirectoryMode mode) - : ServiceFramework{system_, "IDirectory"}, backend(std::move(backend_)) { - static const FunctionInfo functions[] = { - {0, &IDirectory::Read, "Read"}, - {1, &IDirectory::GetEntryCount, "GetEntryCount"}, - }; - RegisterHandlers(functions); - - // TODO(DarkLordZach): Verify that this is the correct behavior. - // Build entry index now to save time later. - if (True(mode & OpenDirectoryMode::Directory)) { - BuildEntryIndex(entries, backend->GetSubdirectories(), FileSys::EntryType::Directory); - } - if (True(mode & OpenDirectoryMode::File)) { - BuildEntryIndex(entries, backend->GetFiles(), FileSys::EntryType::File); - } - } - -private: - FileSys::VirtualDir backend; - std::vector<FileSys::Entry> entries; - u64 next_entry_index = 0; - - void Read(HLERequestContext& ctx) { - LOG_DEBUG(Service_FS, "called."); - - // Calculate how many entries we can fit in the output buffer - const u64 count_entries = ctx.GetWriteBufferNumElements<FileSys::Entry>(); - - // Cap at total number of entries. - const u64 actual_entries = std::min(count_entries, entries.size() - next_entry_index); - - // Determine data start and end - const auto* begin = reinterpret_cast<u8*>(entries.data() + next_entry_index); - const auto* end = reinterpret_cast<u8*>(entries.data() + next_entry_index + actual_entries); - const auto range_size = static_cast<std::size_t>(std::distance(begin, end)); - - next_entry_index += actual_entries; - - // Write the data to memory - ctx.WriteBuffer(begin, range_size); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(actual_entries); - } - - void GetEntryCount(HLERequestContext& ctx) { - LOG_DEBUG(Service_FS, "called"); - - u64 count = entries.size() - next_entry_index; - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(count); - } -}; - -class IFileSystem final : public ServiceFramework<IFileSystem> { -public: - explicit IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_) - : ServiceFramework{system_, "IFileSystem"}, backend{std::move(backend_)}, size{std::move( - size_)} { - static const FunctionInfo functions[] = { - {0, &IFileSystem::CreateFile, "CreateFile"}, - {1, &IFileSystem::DeleteFile, "DeleteFile"}, - {2, &IFileSystem::CreateDirectory, "CreateDirectory"}, - {3, &IFileSystem::DeleteDirectory, "DeleteDirectory"}, - {4, &IFileSystem::DeleteDirectoryRecursively, "DeleteDirectoryRecursively"}, - {5, &IFileSystem::RenameFile, "RenameFile"}, - {6, nullptr, "RenameDirectory"}, - {7, &IFileSystem::GetEntryType, "GetEntryType"}, - {8, &IFileSystem::OpenFile, "OpenFile"}, - {9, &IFileSystem::OpenDirectory, "OpenDirectory"}, - {10, &IFileSystem::Commit, "Commit"}, - {11, &IFileSystem::GetFreeSpaceSize, "GetFreeSpaceSize"}, - {12, &IFileSystem::GetTotalSpaceSize, "GetTotalSpaceSize"}, - {13, &IFileSystem::CleanDirectoryRecursively, "CleanDirectoryRecursively"}, - {14, &IFileSystem::GetFileTimeStampRaw, "GetFileTimeStampRaw"}, - {15, nullptr, "QueryEntry"}, - {16, &IFileSystem::GetFileSystemAttribute, "GetFileSystemAttribute"}, - }; - RegisterHandlers(functions); - } - - void CreateFile(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const auto file_buffer = ctx.ReadBuffer(); - const std::string name = Common::StringFromBuffer(file_buffer); - - const u64 file_mode = rp.Pop<u64>(); - const u32 file_size = rp.Pop<u32>(); - - LOG_DEBUG(Service_FS, "called. file={}, mode=0x{:X}, size=0x{:08X}", name, file_mode, - file_size); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(backend.CreateFile(name, file_size)); - } - - void DeleteFile(HLERequestContext& ctx) { - const auto file_buffer = ctx.ReadBuffer(); - const std::string name = Common::StringFromBuffer(file_buffer); - - LOG_DEBUG(Service_FS, "called. file={}", name); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(backend.DeleteFile(name)); - } - - void CreateDirectory(HLERequestContext& ctx) { - const auto file_buffer = ctx.ReadBuffer(); - const std::string name = Common::StringFromBuffer(file_buffer); - - LOG_DEBUG(Service_FS, "called. directory={}", name); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(backend.CreateDirectory(name)); - } - - void DeleteDirectory(HLERequestContext& ctx) { - const auto file_buffer = ctx.ReadBuffer(); - const std::string name = Common::StringFromBuffer(file_buffer); - - LOG_DEBUG(Service_FS, "called. directory={}", name); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(backend.DeleteDirectory(name)); - } - - void DeleteDirectoryRecursively(HLERequestContext& ctx) { - const auto file_buffer = ctx.ReadBuffer(); - const std::string name = Common::StringFromBuffer(file_buffer); - - LOG_DEBUG(Service_FS, "called. directory={}", name); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(backend.DeleteDirectoryRecursively(name)); - } - - void CleanDirectoryRecursively(HLERequestContext& ctx) { - const auto file_buffer = ctx.ReadBuffer(); - const std::string name = Common::StringFromBuffer(file_buffer); - - LOG_DEBUG(Service_FS, "called. Directory: {}", name); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(backend.CleanDirectoryRecursively(name)); - } - - void RenameFile(HLERequestContext& ctx) { - const std::string src_name = Common::StringFromBuffer(ctx.ReadBuffer(0)); - const std::string dst_name = Common::StringFromBuffer(ctx.ReadBuffer(1)); - - LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(backend.RenameFile(src_name, dst_name)); - } - - void OpenFile(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const auto file_buffer = ctx.ReadBuffer(); - const std::string name = Common::StringFromBuffer(file_buffer); - - const auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>()); - - LOG_DEBUG(Service_FS, "called. file={}, mode={}", name, mode); - - FileSys::VirtualFile vfs_file{}; - auto result = backend.OpenFile(&vfs_file, name, mode); - if (result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - auto file = std::make_shared<IFile>(system, vfs_file); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IFile>(std::move(file)); - } - - void OpenDirectory(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const auto file_buffer = ctx.ReadBuffer(); - const std::string name = Common::StringFromBuffer(file_buffer); - const auto mode = rp.PopRaw<OpenDirectoryMode>(); - - LOG_DEBUG(Service_FS, "called. directory={}, mode={}", name, mode); - - FileSys::VirtualDir vfs_dir{}; - auto result = backend.OpenDirectory(&vfs_dir, name); - if (result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - auto directory = std::make_shared<IDirectory>(system, vfs_dir, mode); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IDirectory>(std::move(directory)); - } - - void GetEntryType(HLERequestContext& ctx) { - const auto file_buffer = ctx.ReadBuffer(); - const std::string name = Common::StringFromBuffer(file_buffer); - - LOG_DEBUG(Service_FS, "called. file={}", name); - - FileSys::EntryType vfs_entry_type{}; - auto result = backend.GetEntryType(&vfs_entry_type, name); - if (result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(static_cast<u32>(vfs_entry_type)); - } - - void Commit(HLERequestContext& ctx) { - LOG_WARNING(Service_FS, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void GetFreeSpaceSize(HLERequestContext& ctx) { - LOG_DEBUG(Service_FS, "called"); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(size.get_free_size()); - } - - void GetTotalSpaceSize(HLERequestContext& ctx) { - LOG_DEBUG(Service_FS, "called"); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(size.get_total_size()); - } - - void GetFileTimeStampRaw(HLERequestContext& ctx) { - const auto file_buffer = ctx.ReadBuffer(); - const std::string name = Common::StringFromBuffer(file_buffer); - - LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", name); - - FileSys::FileTimeStampRaw vfs_timestamp{}; - auto result = backend.GetFileTimeStampRaw(&vfs_timestamp, name); - if (result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - IPC::ResponseBuilder rb{ctx, 10}; - rb.Push(ResultSuccess); - rb.PushRaw(vfs_timestamp); - } - - void GetFileSystemAttribute(HLERequestContext& ctx) { - LOG_WARNING(Service_FS, "(STUBBED) called"); - - struct FileSystemAttribute { - u8 dir_entry_name_length_max_defined; - u8 file_entry_name_length_max_defined; - u8 dir_path_name_length_max_defined; - u8 file_path_name_length_max_defined; - INSERT_PADDING_BYTES_NOINIT(0x5); - u8 utf16_dir_entry_name_length_max_defined; - u8 utf16_file_entry_name_length_max_defined; - u8 utf16_dir_path_name_length_max_defined; - u8 utf16_file_path_name_length_max_defined; - INSERT_PADDING_BYTES_NOINIT(0x18); - s32 dir_entry_name_length_max; - s32 file_entry_name_length_max; - s32 dir_path_name_length_max; - s32 file_path_name_length_max; - INSERT_PADDING_WORDS_NOINIT(0x5); - s32 utf16_dir_entry_name_length_max; - s32 utf16_file_entry_name_length_max; - s32 utf16_dir_path_name_length_max; - s32 utf16_file_path_name_length_max; - INSERT_PADDING_WORDS_NOINIT(0x18); - INSERT_PADDING_WORDS_NOINIT(0x1); - }; - static_assert(sizeof(FileSystemAttribute) == 0xc0, - "FileSystemAttribute has incorrect size"); - - FileSystemAttribute savedata_attribute{}; - savedata_attribute.dir_entry_name_length_max_defined = true; - savedata_attribute.file_entry_name_length_max_defined = true; - savedata_attribute.dir_entry_name_length_max = 0x40; - savedata_attribute.file_entry_name_length_max = 0x40; - - IPC::ResponseBuilder rb{ctx, 50}; - rb.Push(ResultSuccess); - rb.PushRaw(savedata_attribute); - } - -private: - VfsDirectoryServiceWrapper backend; - SizeGetter size; -}; - class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> { public: explicit ISaveDataInfoReader(Core::System& system_, @@ -960,7 +430,7 @@ void FSP_SRV::OpenSaveDataFileSystem(HLERequestContext& ctx) { save_data_controller->OpenSaveData(&dir, parameters.space_id, parameters.attribute); if (result != ResultSuccess) { IPC::ResponseBuilder rb{ctx, 2, 0, 0}; - rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); + rb.Push(FileSys::ResultTargetNotFound); return; } @@ -1127,7 +597,7 @@ void FSP_SRV::OpenPatchDataStorageByCurrentProcess(HLERequestContext& ctx) { LOG_DEBUG(Service_FS, "called with storage_id={:02X}, title_id={:016X}", storage_id, title_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); + rb.Push(FileSys::ResultTargetNotFound); } void FSP_SRV::OpenDataStorageWithProgramIndex(HLERequestContext& ctx) { diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp/fsp_srv.h index 26980af99..26980af99 100644 --- a/src/core/hle/service/filesystem/fsp_srv.h +++ b/src/core/hle/service/filesystem/fsp/fsp_srv.h diff --git a/src/core/hle/service/filesystem/fsp/fsp_util.h b/src/core/hle/service/filesystem/fsp/fsp_util.h new file mode 100644 index 000000000..253f866db --- /dev/null +++ b/src/core/hle/service/filesystem/fsp/fsp_util.h @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/filesystem/filesystem.h" + +namespace Service::FileSystem { + +struct SizeGetter { + std::function<u64()> get_free_size; + std::function<u64()> get_total_size; + + static SizeGetter FromStorageId(const FileSystemController& fsc, FileSys::StorageId id) { + return { + [&fsc, id] { return fsc.GetFreeSpaceSize(id); }, + [&fsc, id] { return fsc.GetTotalSpaceSize(id); }, + }; + } +}; + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/romfs_controller.h b/src/core/hle/service/filesystem/romfs_controller.h index 9a478f71d..3c3ead344 100644 --- a/src/core/hle/service/filesystem/romfs_controller.h +++ b/src/core/hle/service/filesystem/romfs_controller.h @@ -5,7 +5,7 @@ #include "core/file_sys/nca_metadata.h" #include "core/file_sys/romfs_factory.h" -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" namespace Service::FileSystem { diff --git a/src/core/hle/service/filesystem/save_data_controller.cpp b/src/core/hle/service/filesystem/save_data_controller.cpp index d19b3ea1e..03e45f7f9 100644 --- a/src/core/hle/service/filesystem/save_data_controller.cpp +++ b/src/core/hle/service/filesystem/save_data_controller.cpp @@ -44,7 +44,7 @@ Result SaveDataController::CreateSaveData(FileSys::VirtualDir* out_save_data, auto save_data = factory->Create(space, attribute); if (save_data == nullptr) { - return FileSys::ERROR_ENTITY_NOT_FOUND; + return FileSys::ResultTargetNotFound; } *out_save_data = save_data; @@ -56,7 +56,7 @@ Result SaveDataController::OpenSaveData(FileSys::VirtualDir* out_save_data, const FileSys::SaveDataAttribute& attribute) { auto save_data = factory->Open(space, attribute); if (save_data == nullptr) { - return FileSys::ERROR_ENTITY_NOT_FOUND; + return FileSys::ResultTargetNotFound; } *out_save_data = save_data; @@ -67,7 +67,7 @@ Result SaveDataController::OpenSaveDataSpace(FileSys::VirtualDir* out_save_data_ FileSys::SaveDataSpaceId space) { auto save_data_space = factory->GetSaveDataSpaceDirectory(space); if (save_data_space == nullptr) { - return FileSys::ERROR_ENTITY_NOT_FOUND; + return FileSys::ResultTargetNotFound; } *out_save_data_space = save_data_space; diff --git a/src/core/hle/service/filesystem/save_data_controller.h b/src/core/hle/service/filesystem/save_data_controller.h index 863188e4c..dc9d713df 100644 --- a/src/core/hle/service/filesystem/save_data_controller.h +++ b/src/core/hle/service/filesystem/save_data_controller.h @@ -5,7 +5,7 @@ #include "core/file_sys/nca_metadata.h" #include "core/file_sys/savedata_factory.h" -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" namespace Service::FileSystem { diff --git a/src/core/hle/service/glue/glue.cpp b/src/core/hle/service/glue/glue.cpp index 993c3d21d..10376bfac 100644 --- 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<NOTIF_A>(system)); + // Time + auto time = std::make_shared<Time::TimeManager>(system); + + server_manager->RegisterNamedService( + "time:u", + std::make_shared<Time::StaticService>( + system, Service::PSC::Time::StaticServiceSetupInfo{0, 0, 0, 0, 0, 0}, time, "time:u")); + server_manager->RegisterNamedService( + "time:a", + std::make_shared<Time::StaticService>( + system, Service::PSC::Time::StaticServiceSetupInfo{1, 1, 0, 1, 0, 0}, time, "time:a")); + server_manager->RegisterNamedService( + "time:r", + std::make_shared<Time::StaticService>( + 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 100644 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<Service::PSC::Time::ServiceManager> time_m) { + m_time_m = std::move(time_m); + + m_timer_event = m_ctx.CreateEvent("Glue:AlarmWorker:TimerEvent"); + m_timer_timing_event = Core::Timing::CreateEvent( + "Glue:AlarmWorker::AlarmTimer", + [this](s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { + m_timer_event->Signal(); + return std::nullopt; + }); + + AttachToClosestAlarmEvent(); +} + +bool AlarmWorker::GetClosestAlarmInfo(Service::PSC::Time::AlarmInfo& out_alarm_info, + s64& out_time) { + bool is_valid{}; + Service::PSC::Time::AlarmInfo alarm_info{}; + s64 closest_time{}; + + auto res = m_time_m->GetClosestAlarmInfo(is_valid, alarm_info, closest_time); + ASSERT(res == ResultSuccess); + + if (is_valid) { + out_alarm_info = alarm_info; + out_time = closest_time; + } + + return is_valid; +} + +void AlarmWorker::OnPowerStateChanged() { + Service::PSC::Time::AlarmInfo closest_alarm_info{}; + s64 closest_time{}; + if (!GetClosestAlarmInfo(closest_alarm_info, closest_time)) { + m_system.CoreTiming().UnscheduleEvent(m_timer_timing_event); + m_timer_event->Clear(); + return; + } + + if (closest_alarm_info.alert_time <= closest_time) { + m_time_m->CheckAndSignalAlarms(); + } else { + auto next_time{closest_alarm_info.alert_time - closest_time}; + + m_system.CoreTiming().UnscheduleEvent(m_timer_timing_event); + m_timer_event->Clear(); + + m_system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds(next_time), + m_timer_timing_event); + } +} + +Result AlarmWorker::AttachToClosestAlarmEvent() { + m_time_m->GetClosestAlarmUpdatedEvent(&m_event); + R_SUCCEED(); +} + +} // namespace Service::Glue::Time 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 100644 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<Service::PSC::Time::ServiceManager> 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<Service::PSC::Time::ServiceManager> m_time_m; + + Kernel::KEvent* m_event{}; + Kernel::KEvent* m_timer_event{}; + std::shared_ptr<Core::Timing::EventType> m_timer_timing_event; + StandardSteadyClockResource& m_steady_clock_resource; +}; + +} // namespace Service::Glue::Time 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 100644 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 100644 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 <memory> + +#include "common/common_types.h" + +namespace Service::PSC::Time { +class SystemClock; +class TimeZoneService; +} // namespace Service::PSC::Time + +namespace Service::Glue::Time { + +class FileTimestampWorker { +public: + FileTimestampWorker() = default; + + void SetFilesystemPosixTime(); + + std::shared_ptr<Service::PSC::Time::SystemClock> m_system_clock{}; + std::shared_ptr<Service::PSC::Time::TimeZoneService> m_time_zone{}; + bool m_initialized{}; +}; + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/manager.cpp b/src/core/hle/service/glue/time/manager.cpp new file mode 100644 index 000000000..b56762941 --- /dev/null +++ b/src/core/hle/service/glue/time/manager.cpp @@ -0,0 +1,277 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <chrono> + +#include "core/core.h" +#include "core/core_timing.h" + +#include "common/settings.h" +#include "common/time_zone.h" +#include "core/file_sys/vfs/vfs.h" +#include "core/hle/kernel/svc.h" +#include "core/hle/service/glue/time/manager.h" +#include "core/hle/service/glue/time/time_zone_binary.h" +#include "core/hle/service/psc/time/service_manager.h" +#include "core/hle/service/psc/time/static.h" +#include "core/hle/service/psc/time/system_clock.h" +#include "core/hle/service/psc/time/time_zone_service.h" +#include "core/hle/service/set/system_settings_server.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::Glue::Time { +namespace { + +template <typename T> +T GetSettingsItemValue(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys, + const char* category, const char* name) { + std::vector<u8> 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<s32, 12> MonthStartDayOfYear{ + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, + }; + + s16 month_s16{calendar.month}; + s8 month{static_cast<s8>(((month_s16 * 43) & ~std::numeric_limits<s16>::max()) + + ((month_s16 * 43) >> 9))}; + s8 month_index{static_cast<s8>(calendar.month - 12 * month)}; + if (month_index == 0) { + month_index = 12; + } + s32 year{(month + calendar.year) - !month_index}; + s32 v8{year >= 0 ? year : year + 3}; + + s64 days_since_epoch = calendar.day + MonthStartDayOfYear[month_index - 1]; + days_since_epoch += (year * 365) + (v8 / 4) - (year / 100) + (year / 400) - 365; + + if (month_index <= 2 && is_leap(year)) { + days_since_epoch--; + } + auto epoch_s{((24ll * days_since_epoch + calendar.hour) * 60ll + calendar.minute) * 60ll + + calendar.second}; + return epoch_s - 62135683200ll; +} + +s64 GetEpochTimeFromInitialYear(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys) { + Service::PSC::Time::CalendarTime calendar{ + .year = GetSettingsItemValue<s16>(set_sys, "time", "standard_user_clock_initial_year"), + .month = 1, + .day = 1, + .hour = 0, + .minute = 0, + .second = 0, + }; + return CalendarTimeToEpoch(calendar); +} + +Service::PSC::Time::LocationName GetTimeZoneString(Service::PSC::Time::LocationName& in_name) { + auto configured_zone = Settings::GetTimeZoneString(Settings::values.time_zone_index.GetValue()); + + Service::PSC::Time::LocationName configured_name{}; + std::memcpy(configured_name.name.data(), configured_zone.data(), + std::min(configured_name.name.size(), configured_zone.size())); + + if (!IsTimeZoneBinaryValid(configured_name)) { + configured_zone = Common::TimeZone::FindSystemTimeZone(); + configured_name = {}; + std::memcpy(configured_name.name.data(), configured_zone.data(), + std::min(configured_name.name.size(), configured_zone.size())); + } + + ASSERT_MSG(IsTimeZoneBinaryValid(configured_name), "Invalid time zone {}!", + configured_name.name.data()); + + return configured_name; +} + +} // namespace + +TimeManager::TimeManager(Core::System& system) + : m_steady_clock_resource{system}, m_worker{system, m_steady_clock_resource, + m_file_timestamp_worker} { + m_time_m = + system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true); + + auto res = m_time_m->GetStaticServiceAsServiceManager(m_time_sm); + ASSERT(res == ResultSuccess); + + m_set_sys = + system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true); + + res = MountTimeZoneBinary(system); + ASSERT(res == ResultSuccess); + + m_worker.Initialize(m_time_sm, m_set_sys); + + res = m_time_sm->GetStandardUserSystemClock(m_file_timestamp_worker.m_system_clock); + ASSERT(res == ResultSuccess); + + res = m_time_sm->GetTimeZoneService(m_file_timestamp_worker.m_time_zone); + ASSERT(res == ResultSuccess); + + res = SetupStandardSteadyClockCore(); + ASSERT(res == ResultSuccess); + + Service::PSC::Time::SystemClockContext user_clock_context{}; + res = m_set_sys->GetUserSystemClockContext(user_clock_context); + ASSERT(res == ResultSuccess); + + // TODO the local clock should initialise with this epoch time, and be updated somewhere else on + // first boot to update it, but I haven't been able to find that point (likely via ntc's auto + // correct as it's defaulted to be enabled). So to get a time that isn't stuck in the past for + // first boot, grab the current real seconds. + auto epoch_time{GetEpochTimeFromInitialYear(m_set_sys)}; + if (user_clock_context == Service::PSC::Time::SystemClockContext{}) { + m_steady_clock_resource.GetRtcTimeInSeconds(epoch_time); + } + + res = m_time_m->SetupStandardLocalSystemClockCore(user_clock_context, epoch_time); + ASSERT(res == ResultSuccess); + + Service::PSC::Time::SystemClockContext network_clock_context{}; + res = m_set_sys->GetNetworkSystemClockContext(network_clock_context); + ASSERT(res == ResultSuccess); + + auto network_accuracy_m{GetSettingsItemValue<s32>( + m_set_sys, "time", "standard_network_clock_sufficient_accuracy_minutes")}; + auto one_minute_ns{ + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::minutes(1)).count()}; + s64 network_accuracy_ns{network_accuracy_m * one_minute_ns}; + + res = m_time_m->SetupStandardNetworkSystemClockCore(network_clock_context, network_accuracy_ns); + ASSERT(res == ResultSuccess); + + bool is_automatic_correction_enabled{}; + res = m_set_sys->IsUserSystemClockAutomaticCorrectionEnabled(is_automatic_correction_enabled); + ASSERT(res == ResultSuccess); + + Service::PSC::Time::SteadyClockTimePoint automatic_correction_time_point{}; + res = m_set_sys->GetUserSystemClockAutomaticCorrectionUpdatedTime( + automatic_correction_time_point); + ASSERT(res == ResultSuccess); + + res = m_time_m->SetupStandardUserSystemClockCore(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::nanoseconds>(std::chrono::seconds(1)).count()}; + s64 external_steady_clock_internal_offset_ns{external_steady_clock_internal_offset_s * + one_second_ns}; + + s32 standard_steady_clock_test_offset_m{ + GetSettingsItemValue<s32>(m_set_sys, "time", "standard_steady_clock_test_offset_minutes")}; + auto one_minute_ns{ + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::minutes(1)).count()}; + s64 standard_steady_clock_test_offset_ns{standard_steady_clock_test_offset_m * one_minute_ns}; + + auto reset_detected = m_steady_clock_resource.GetResetDetected(); + if (reset_detected) { + external_clock_source_id = {}; + } + + Common::UUID clock_source_id{}; + m_steady_clock_resource.Initialize(&clock_source_id, &external_clock_source_id); + + if (clock_source_id != external_clock_source_id) { + m_set_sys->SetExternalSteadyClockSourceId(clock_source_id); + } + + res = m_time_m->SetupStandardSteadyClockCore(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); + + auto configured_zone = GetTimeZoneString(name); + + if (configured_zone.name != name.name) { + m_set_sys->SetDeviceTimeZoneLocationName(configured_zone); + name = configured_zone; + + std::shared_ptr<Service::PSC::Time::SystemClock> local_clock; + m_time_sm->GetStandardLocalSystemClock(local_clock); + Service::PSC::Time::SystemClockContext context{}; + local_clock->GetSystemClockContext(context); + m_set_sys->SetDeviceTimeZoneLocationUpdatedTime(context.steady_time_point); + } + + Service::PSC::Time::SteadyClockTimePoint time_point{}; + res = m_set_sys->GetDeviceTimeZoneLocationUpdatedTime(time_point); + ASSERT(res == ResultSuccess); + + auto location_count = GetTimeZoneCount(); + Service::PSC::Time::RuleVersion rule_version{}; + GetTimeZoneVersion(rule_version); + + std::span<const u8> rule_buffer{}; + size_t rule_size{}; + res = GetTimeZoneRule(rule_buffer, rule_size, name); + ASSERT(res == ResultSuccess); + + res = m_time_m->SetupTimeZoneServiceCore(name, 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 100644 index 000000000..1de93f8f9 --- /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 <functional> +#include <string> + +#include "common/common_types.h" +#include "core/file_sys/vfs/vfs_types.h" +#include "core/hle/service/glue/time/file_timestamp_worker.h" +#include "core/hle/service/glue/time/standard_steady_clock_resource.h" +#include "core/hle/service/glue/time/worker.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { +class ServiceManager; +class StaticService; +} // namespace Service::PSC::Time + +namespace Service::Glue::Time { +class TimeManager { +public: + explicit TimeManager(Core::System& system); + + std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys; + + std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m{}; + std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm{}; + StandardSteadyClockResource m_steady_clock_resource; + FileTimestampWorker m_file_timestamp_worker; + TimeWorker m_worker; + +private: + Result SetupStandardSteadyClockCore(); + Result SetupTimeZoneServiceCore(); +}; +} // namespace Service::Glue::Time 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 100644 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 100644 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 100644 index 000000000..5ebaa33e0 --- /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 <chrono> + +#include "common/settings.h" +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/kernel/svc.h" +#include "core/hle/service/glue/time/standard_steady_clock_resource.h" +#include "core/hle/service/psc/time/errors.h" + +namespace Service::Glue::Time { +namespace { +[[maybe_unused]] constexpr u32 Max77620PmicSession = 0x3A000001; +[[maybe_unused]] constexpr u32 Max77620RtcSession = 0x3B000001; + +Result GetTimeInSeconds(Core::System& system, s64& out_time_s) { + out_time_s = std::chrono::duration_cast<std::chrono::seconds>( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + + if (Settings::values.custom_rtc_enabled) { + out_time_s += Settings::values.custom_rtc_offset.GetValue(); + } + R_SUCCEED(); +} +} // namespace + +StandardSteadyClockResource::StandardSteadyClockResource(Core::System& system) : m_system{system} {} + +void StandardSteadyClockResource::Initialize(Common::UUID* out_source_id, + Common::UUID* external_source_id) { + constexpr size_t NUM_TRIES{20}; + + size_t i{0}; + Result res{ResultSuccess}; + for (; i < NUM_TRIES; i++) { + res = SetCurrentTime(); + if (res == ResultSuccess) { + break; + } + Kernel::Svc::SleepThread(m_system, std::chrono::duration_cast<std::chrono::nanoseconds>( + std::chrono::milliseconds(1)) + .count()); + } + + if (i < NUM_TRIES) { + m_set_time_result = ResultSuccess; + if (*external_source_id != Service::PSC::Time::ClockSourceId{}) { + m_clock_source_id = *external_source_id; + } else { + m_clock_source_id = Common::UUID::MakeRandom(); + } + } else { + m_set_time_result = res; + auto ticks{m_system.CoreTiming().GetClockTicks()}; + m_time = -Service::PSC::Time::ConvertToTimeSpan(ticks).count(); + m_clock_source_id = Common::UUID::MakeRandom(); + } + + if (out_source_id) { + *out_source_id = m_clock_source_id; + } +} + +bool StandardSteadyClockResource::GetResetDetected() { + // TODO: + // call Rtc::GetRtcResetDetected(Max77620RtcSession) + // if detected: + // SetSys::SetExternalSteadyClockSourceId(invalid_id) + // Rtc::ClearRtcResetDetected(Max77620RtcSession) + // set m_rtc_reset to result + // Instead, only set reset to true if we're booting for the first time. + m_rtc_reset = false; + return m_rtc_reset; +} + +Result StandardSteadyClockResource::SetCurrentTime() { + auto start_tick{m_system.CoreTiming().GetClockTicks()}; + + s64 rtc_time_s{}; + // TODO R_TRY(Rtc::GetTimeInSeconds(rtc_time_s, Max77620RtcSession)) + R_TRY(GetTimeInSeconds(m_system, rtc_time_s)); + + auto end_tick{m_system.CoreTiming().GetClockTicks()}; + auto diff{Service::PSC::Time::ConvertToTimeSpan(end_tick - start_tick)}; + // Why is this here? + R_UNLESS(diff < std::chrono::milliseconds(101), Service::PSC::Time::ResultRtcTimeout); + + auto one_second_ns{ + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()}; + s64 boot_time{rtc_time_s * one_second_ns - + Service::PSC::Time::ConvertToTimeSpan(end_tick).count()}; + + std::scoped_lock l{m_mutex}; + m_time = boot_time; + R_SUCCEED(); +} + +Result StandardSteadyClockResource::GetRtcTimeInSeconds(s64& out_time) { + // TODO + // R_TRY(Rtc::GetTimeInSeconds(time_s, Max77620RtcSession) + R_RETURN(GetTimeInSeconds(m_system, out_time)); +} + +void StandardSteadyClockResource::UpdateTime() { + constexpr size_t NUM_TRIES{3}; + + size_t i{0}; + Result res{ResultSuccess}; + for (; i < NUM_TRIES; i++) { + res = SetCurrentTime(); + if (res == ResultSuccess) { + break; + } + Kernel::Svc::SleepThread(m_system, std::chrono::duration_cast<std::chrono::nanoseconds>( + std::chrono::milliseconds(1)) + .count()); + } +} + +} // namespace Service::Glue::Time 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 100644 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 <mutex> + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "core/hle/service/psc/time/common.h" + +namespace Core { +class System; +} + +namespace Service::Glue::Time { +class StandardSteadyClockResource { +public: + StandardSteadyClockResource(Core::System& system); + + void Initialize(Common::UUID* out_source_id, Common::UUID* external_source_id); + + s64 GetTime() const { + return m_time; + } + + bool GetResetDetected(); + Result SetCurrentTime(); + Result GetRtcTimeInSeconds(s64& out_time); + void UpdateTime(); + +private: + Core::System& m_system; + + std::mutex m_mutex; + Service::PSC::Time::ClockSourceId m_clock_source_id{}; + s64 m_time{}; + Result m_set_time_result; + bool m_rtc_reset; +}; +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/glue/time/static.cpp b/src/core/hle/service/glue/time/static.cpp new file mode 100644 index 000000000..63b7d91da --- /dev/null +++ b/src/core/hle/service/glue/time/static.cpp @@ -0,0 +1,448 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <chrono> + +#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 <typename T> +T GetSettingsItemValue(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys, + const char* category, const char* name) { + std::vector<u8> 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<TimeManager> time, const char* name) + : ServiceFramework{system_, name}, m_system{system_}, m_time_m{time->m_time_m}, + m_setup_info{setup_info}, m_time_sm{time->m_time_sm}, + m_file_timestamp_worker{time->m_file_timestamp_worker}, m_standard_steady_clock_resource{ + time->m_steady_clock_resource} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &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<Service::Set::ISystemSettingsServer>("set:sys", true); + + if (m_setup_info.can_write_local_clock && m_setup_info.can_write_user_clock && + !m_setup_info.can_write_network_clock && m_setup_info.can_write_timezone_device_location && + !m_setup_info.can_write_steady_clock && !m_setup_info.can_write_uninitialized_clock) { + m_time_m->GetStaticServiceAsAdmin(m_wrapped_service); + } else if (!m_setup_info.can_write_local_clock && !m_setup_info.can_write_user_clock && + !m_setup_info.can_write_network_clock && + !m_setup_info.can_write_timezone_device_location && + !m_setup_info.can_write_steady_clock && + !m_setup_info.can_write_uninitialized_clock) { + m_time_m->GetStaticServiceAsUser(m_wrapped_service); + } else if (!m_setup_info.can_write_local_clock && !m_setup_info.can_write_user_clock && + !m_setup_info.can_write_network_clock && + !m_setup_info.can_write_timezone_device_location && + m_setup_info.can_write_steady_clock && !m_setup_info.can_write_uninitialized_clock) { + m_time_m->GetStaticServiceAsRepair(m_wrapped_service); + } else { + UNREACHABLE(); + } + + auto res = m_wrapped_service->GetTimeZoneService(m_time_zone); + ASSERT(res == ResultSuccess); +} + +void StaticService::Handle_GetStandardUserSystemClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<Service::PSC::Time::SystemClock> service{}; + auto res = GetStandardUserSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service)); +} + +void StaticService::Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<Service::PSC::Time::SystemClock> service{}; + auto res = GetStandardNetworkSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service)); +} + +void StaticService::Handle_GetStandardSteadyClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<Service::PSC::Time::SteadyClock> 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<TimeZoneService> 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::PSC::Time::SystemClock> service{}; + auto res = GetStandardLocalSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<Service::PSC::Time::SystemClock>(std::move(service)); +} + +void StaticService::Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<Service::PSC::Time::SystemClock> service{}; + auto res = GetEphemeralNetworkSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<Service::PSC::Time::SystemClock>(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<s64>()}; + + 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<bool>(is_enabled); +} + +void StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto automatic_correction{rp.Pop<bool>()}; + + 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<bool>(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<Service::PSC::Time::SteadyClockTimePoint>(time_point); +} + +void StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()}; + + s64 time{}; + auto res = CalculateMonotonicSystemClockBaseTimePoint(time, context); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push<s64>(time); +} + +void StaticService::Handle_GetClockSnapshot(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto type{rp.PopEnum<Service::PSC::Time::TimeType>()}; + + 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 clock_type{rp.PopEnum<Service::PSC::Time::TimeType>()}; + [[maybe_unused]] auto alignment{rp.Pop<u32>()}; + auto user_context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()}; + auto network_context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()}; + + 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<Service::PSC::Time::SystemClock>& out_service) { + R_RETURN(m_wrapped_service->GetStandardUserSystemClock(out_service)); +} + +Result StaticService::GetStandardNetworkSystemClock( + std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) { + R_RETURN(m_wrapped_service->GetStandardNetworkSystemClock(out_service)); +} + +Result StaticService::GetStandardSteadyClock( + std::shared_ptr<Service::PSC::Time::SteadyClock>& out_service) { + R_RETURN(m_wrapped_service->GetStandardSteadyClock(out_service)); +} + +Result StaticService::GetTimeZoneService(std::shared_ptr<TimeZoneService>& out_service) { + out_service = std::make_shared<TimeZoneService>(m_system, m_file_timestamp_worker, + m_setup_info.can_write_timezone_device_location, + m_time_zone); + R_SUCCEED(); +} + +Result StaticService::GetStandardLocalSystemClock( + std::shared_ptr<Service::PSC::Time::SystemClock>& out_service) { + R_RETURN(m_wrapped_service->GetStandardLocalSystemClock(out_service)); +} + +Result StaticService::GetEphemeralNetworkSystemClock( + std::shared_ptr<Service::PSC::Time::SystemClock>& 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::nanoseconds>(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<s32>(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->CalculateStandardUserSystemClockDifferenceByUser(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 100644 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<StaticService> { +public: + explicit StaticService(Core::System& system, + Service::PSC::Time::StaticServiceSetupInfo setup_info, + std::shared_ptr<TimeManager> time, const char* name); + + ~StaticService() override = default; + + Result GetStandardUserSystemClock( + std::shared_ptr<Service::PSC::Time::SystemClock>& out_service); + Result GetStandardNetworkSystemClock( + std::shared_ptr<Service::PSC::Time::SystemClock>& out_service); + Result GetStandardSteadyClock(std::shared_ptr<Service::PSC::Time::SteadyClock>& out_service); + Result GetTimeZoneService(std::shared_ptr<TimeZoneService>& out_service); + Result GetStandardLocalSystemClock( + std::shared_ptr<Service::PSC::Time::SystemClock>& out_service); + Result GetEphemeralNetworkSystemClock( + std::shared_ptr<Service::PSC::Time::SystemClock>& 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<Service::Set::ISystemSettingsServer> m_set_sys; + std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m; + std::shared_ptr<Service::PSC::Time::StaticService> m_wrapped_service; + + Service::PSC::Time::StaticServiceSetupInfo m_setup_info; + std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm; + std::shared_ptr<Service::PSC::Time::TimeZoneService> m_time_zone; + FileTimestampWorker& m_file_timestamp_worker; + StandardSteadyClockResource& m_standard_steady_clock_resource; +}; +} // namespace Service::Glue::Time 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 100644 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 <chrono> + +#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<Service::PSC::Time::OperationEvent>::ListType g_list_nodes{}; +} // namespace + +TimeZoneService::TimeZoneService( + Core::System& system_, FileTimestampWorker& file_timestamp_worker, + bool can_write_timezone_device_location, + std::shared_ptr<Service::PSC::Time::TimeZoneService> time_zone_service) + : ServiceFramework{system_, "ITimeZoneService"}, m_system{system}, + m_can_write_timezone_device_location{can_write_timezone_device_location}, + m_file_timestamp_worker{file_timestamp_worker}, + m_wrapped_service{std::move(time_zone_service)}, m_operation_event{m_system} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &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<Service::Set::ISystemSettingsServer>("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<Service::PSC::Time::LocationName>(name); +} + +void TimeZoneService::Handle_SetDeviceLocationName(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto name{rp.PopRaw<Service::PSC::Time::LocationName>()}; + + 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<u32>()}; + + auto max_names{ctx.GetWriteBufferSize() / sizeof(Service::PSC::Time::LocationName)}; + + std::vector<Service::PSC::Time::LocationName> 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<Service::PSC::Time::LocationName>()}; + + 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<Service::PSC::Time::RuleVersion>(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<Service::PSC::Time::LocationName>(name); + rb.PushRaw<Service::PSC::Time::SteadyClockTimePoint>(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<s64>()}; + + 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<Service::PSC::Time::CalendarTime>(calendar_time); + rb.PushRaw<Service::PSC::Time::CalendarAdditionalInfo>(additional_info); +} + +void TimeZoneService::Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto time{rp.Pop<s64>()}; + + 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<Service::PSC::Time::CalendarTime>(calendar_time); + rb.PushRaw<Service::PSC::Time::CalendarAdditionalInfo>(additional_info); +} + +void TimeZoneService::Handle_ToPosixTime(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto calendar{rp.PopRaw<Service::PSC::Time::CalendarTime>()}; + + 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<s64, 2> times{}; + u32 times_count{static_cast<u32>(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<Service::PSC::Time::CalendarTime>()}; + + u32 count{}; + std::array<s64, 2> times{}; + u32 times_count{static_cast<u32>(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<const u8> binary{}; + size_t binary_size{}; + R_TRY(GetTimeZoneRule(binary, binary_size, location_name)) + + R_TRY(m_wrapped_service->SetDeviceLocationNameWithTimeZoneRule(location_name, binary)); + + m_file_timestamp_worker.SetFilesystemPosixTime(); + + Service::PSC::Time::SteadyClockTimePoint time_point{}; + Service::PSC::Time::LocationName name{}; + R_TRY(m_wrapped_service->GetDeviceLocationNameAndUpdatedTime(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<Service::PSC::Time::LocationName>& 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<const u8> binary{}; + size_t binary_size{}; + R_TRY(GetTimeZoneRule(binary, binary_size, name)) + R_RETURN(m_wrapped_service->ParseTimeZoneBinary(out_rule, binary)); +} + +Result TimeZoneService::GetTimeZoneRuleVersion(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<s64, 2> 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<s64, 2> 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 100644 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 <memory> +#include <mutex> +#include <span> +#include <vector> + +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/server_manager.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Tz { +struct Rule; +} + +namespace Service::Set { +class ISystemSettingsServer; +} + +namespace Service::PSC::Time { +class TimeZoneService; +} + +namespace Service::Glue::Time { +class FileTimestampWorker; + +class TimeZoneService final : public ServiceFramework<TimeZoneService> { +public: + explicit TimeZoneService( + Core::System& system, FileTimestampWorker& file_timestamp_worker, + bool can_write_timezone_device_location, + std::shared_ptr<Service::PSC::Time::TimeZoneService> time_zone_service); + + ~TimeZoneService() override; + + Result GetDeviceLocationName(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<Service::PSC::Time::LocationName>& 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<s64, 2> out_times, u32 out_times_count, + Service::PSC::Time::CalendarTime& calendar_time, Tz::Rule& rule); + Result ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> 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<Service::Set::ISystemSettingsServer> m_set_sys; + + bool m_can_write_timezone_device_location; + FileTimestampWorker& m_file_timestamp_worker; + std::shared_ptr<Service::PSC::Time::TimeZoneService> m_wrapped_service; + std::mutex m_mutex; + bool operation_event_initialized{}; + Service::PSC::Time::OperationEvent m_operation_event; +}; + +} // namespace Service::Glue::Time 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 100644 index 000000000..d33f784c0 --- /dev/null +++ b/src/core/hle/service/glue/time/time_zone_binary.cpp @@ -0,0 +1,221 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/file_sys/content_archive.h" +#include "core/file_sys/nca_metadata.h" +#include "core/file_sys/registered_cache.h" +#include "core/file_sys/romfs.h" +#include "core/file_sys/system_archive/system_archive.h" +#include "core/file_sys/vfs/vfs.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/glue/time/time_zone_binary.h" + +namespace Service::Glue::Time { +namespace { +constexpr u64 TimeZoneBinaryId = 0x10000000000080E; + +static FileSys::VirtualDir g_time_zone_binary_romfs{}; +static Result g_time_zone_binary_mount_result{ResultUnknown}; +static std::vector<u8> g_time_zone_scratch_space(0x2800, 0); + +Result TimeZoneReadBinary(size_t& out_read_size, std::span<u8> out_buffer, size_t out_buffer_size, + std::string_view path) { + R_UNLESS(g_time_zone_binary_mount_result == ResultSuccess, g_time_zone_binary_mount_result); + + auto vfs_file{g_time_zone_binary_romfs->GetFileRelative(path)}; + R_UNLESS(vfs_file, ResultUnknown); + + auto file_size{vfs_file->GetSize()}; + R_UNLESS(file_size > 0, ResultUnknown); + + R_UNLESS(file_size <= out_buffer_size, Service::PSC::Time::ResultFailed); + + out_read_size = vfs_file->Read(out_buffer.data(), file_size); + R_UNLESS(out_read_size > 0, ResultUnknown); + + R_SUCCEED(); +} +} // namespace + +void ResetTimeZoneBinary() { + g_time_zone_binary_romfs = {}; + g_time_zone_binary_mount_result = ResultUnknown; + g_time_zone_scratch_space.clear(); + g_time_zone_scratch_space.resize(0x2800, 0); +} + +Result MountTimeZoneBinary(Core::System& system) { + ResetTimeZoneBinary(); + + auto& fsc{system.GetFileSystemController()}; + std::unique_ptr<FileSys::NCA> nca{}; + + auto* bis_system = fsc.GetSystemNANDContents(); + + R_UNLESS(bis_system, ResultUnknown); + + nca = bis_system->GetEntry(TimeZoneBinaryId, FileSys::ContentRecordType::Data); + + if (nca) { + g_time_zone_binary_romfs = FileSys::ExtractRomFS(nca->GetRomFS()); + } + + if (g_time_zone_binary_romfs) { + // Validate that the romfs is readable, using invalid firmware keys can cause this to get + // set but the files to be garbage. In that case, we want to hit the next path and + // synthesise them instead. + Service::PSC::Time::LocationName name{"Etc/GMT"}; + if (!IsTimeZoneBinaryValid(name)) { + ResetTimeZoneBinary(); + } + } + + if (!g_time_zone_binary_romfs) { + g_time_zone_binary_romfs = FileSys::ExtractRomFS( + FileSys::SystemArchive::SynthesizeSystemArchive(TimeZoneBinaryId)); + } + + R_UNLESS(g_time_zone_binary_romfs, ResultUnknown); + + g_time_zone_binary_mount_result = ResultSuccess; + R_SUCCEED(); +} + +void GetTimeZoneBinaryListPath(std::string& out_path) { + if (g_time_zone_binary_mount_result != ResultSuccess) { + return; + } + // out_path = fmt::format("{}:/binaryList.txt", "TimeZoneBinary"); + out_path = "/binaryList.txt"; +} + +void GetTimeZoneBinaryVersionPath(std::string& out_path) { + if (g_time_zone_binary_mount_result != ResultSuccess) { + return; + } + // out_path = fmt::format("{}:/version.txt", "TimeZoneBinary"); + out_path = "/version.txt"; +} + +void GetTimeZoneZonePath(std::string& out_path, 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)}; + if (!vfs_file) { + LOG_INFO(Service_Time, "Could not find timezone file {}", path); + return false; + } + return vfs_file->GetSize() != 0; +} + +u32 GetTimeZoneCount() { + std::string path{}; + GetTimeZoneBinaryListPath(path); + + size_t bytes_read{}; + if (TimeZoneReadBinary(bytes_read, g_time_zone_scratch_space, 0x2800, path) != ResultSuccess) { + return 0; + } + if (bytes_read == 0) { + return 0; + } + + auto chars = std::span(reinterpret_cast<char*>(g_time_zone_scratch_space.data()), bytes_read); + u32 count{}; + for (auto chr : chars) { + if (chr == '\n') { + count++; + } + } + return count; +} + +Result GetTimeZoneVersion(Service::PSC::Time::RuleVersion& out_rule_version) { + std::string path{}; + GetTimeZoneBinaryVersionPath(path); + + auto rule_version_buffer{std::span(reinterpret_cast<u8*>(&out_rule_version), + sizeof(Service::PSC::Time::RuleVersion))}; + size_t bytes_read{}; + R_TRY(TimeZoneReadBinary(bytes_read, rule_version_buffer, rule_version_buffer.size_bytes(), + path)); + + rule_version_buffer[bytes_read] = 0; + R_SUCCEED(); +} + +Result GetTimeZoneRule(std::span<const u8>& out_rule, size_t& out_rule_size, + 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<Service::PSC::Time::LocationName>& out_names, + size_t max_names, u32 index) { + std::string path{}; + GetTimeZoneBinaryListPath(path); + + size_t bytes_read{}; + R_TRY(TimeZoneReadBinary(bytes_read, g_time_zone_scratch_space, + g_time_zone_scratch_space.size(), path)); + + out_count = 0; + R_SUCCEED_IF(bytes_read == 0); + + Service::PSC::Time::LocationName current_name{}; + size_t current_name_len{}; + std::span<const u8> chars{g_time_zone_scratch_space}; + u32 name_count{}; + + for (auto chr : chars) { + if (chr == '\r') { + continue; + } + + if (chr == '\n') { + if (name_count >= index) { + out_names.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 100644 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 <span> +#include <string> +#include <string_view> + +#include "core/hle/service/psc/time/common.h" + +namespace Core { +class System; +} + +namespace Service::Glue::Time { + +void ResetTimeZoneBinary(); +Result MountTimeZoneBinary(Core::System& system); +void GetTimeZoneBinaryListPath(std::string& out_path); +void GetTimeZoneBinaryVersionPath(std::string& out_path); +void GetTimeZoneZonePath(std::string& out_path, 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<const u8>& out_rule, size_t& out_rule_size, + Service::PSC::Time::LocationName& name); +Result GetTimeZoneLocationList(u32& out_count, + std::vector<Service::PSC::Time::LocationName>& 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 100644 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 <typename T> +T GetSettingsItemValue(std::shared_ptr<Service::Set::ISystemSettingsServer>& set_sys, + const char* category, const char* name) { + std::vector<u8> 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<std::chrono::nanoseconds> { + m_timer_steady_clock->Signal(); + return std::nullopt; + }); + + m_timer_file_system_timing_event = Core::Timing::CreateEvent( + "Time::SteadyClockEvent", + [this](s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { + m_timer_file_system->Signal(); + return std::nullopt; + }); +} + +TimeWorker::~TimeWorker() { + m_local_clock_event->Signal(); + m_network_clock_event->Signal(); + m_ephemeral_clock_event->Signal(); + std::this_thread::sleep_for(std::chrono::milliseconds(16)); + + m_thread.request_stop(); + m_event->Signal(); + m_thread.join(); + + m_ctx.CloseEvent(m_event); + m_system.CoreTiming().UnscheduleEvent(m_timer_steady_clock_timing_event); + m_ctx.CloseEvent(m_timer_steady_clock); + m_system.CoreTiming().UnscheduleEvent(m_timer_file_system_timing_event); + m_ctx.CloseEvent(m_timer_file_system); +} + +void TimeWorker::Initialize(std::shared_ptr<Service::PSC::Time::StaticService> time_sm, + std::shared_ptr<Service::Set::ISystemSettingsServer> set_sys) { + m_set_sys = std::move(set_sys); + m_time_m = + m_system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true); + m_time_sm = std::move(time_sm); + + m_alarm_worker.Initialize(m_time_m); + + auto steady_clock_interval_m = GetSettingsItemValue<s32>( + m_set_sys, "time", "standard_steady_clock_rtc_update_interval_minutes"); + + auto one_minute_ns{ + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::minutes(1)).count()}; + s64 steady_clock_interval_ns{steady_clock_interval_m * one_minute_ns}; + + m_system.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds(0), + std::chrono::nanoseconds(steady_clock_interval_ns), + m_timer_steady_clock_timing_event); + + auto fs_notify_time_s = + GetSettingsItemValue<s32>(m_set_sys, "time", "notify_time_to_fs_interval_seconds"); + auto one_second_ns{ + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()}; + s64 fs_notify_time_ns{fs_notify_time_s * one_second_ns}; + + m_system.CoreTiming().ScheduleLoopingEvent(std::chrono::nanoseconds(0), + std::chrono::nanoseconds(fs_notify_time_ns), + m_timer_file_system_timing_event); + + auto res = m_time_sm->GetStandardLocalSystemClock(m_local_clock); + ASSERT(res == ResultSuccess); + res = m_time_m->GetStandardLocalClockOperationEvent(&m_local_clock_event); + ASSERT(res == ResultSuccess); + + res = m_time_sm->GetStandardNetworkSystemClock(m_network_clock); + ASSERT(res == ResultSuccess); + res = m_time_m->GetStandardNetworkClockOperationEventForServiceManager(&m_network_clock_event); + ASSERT(res == ResultSuccess); + + res = m_time_sm->GetEphemeralNetworkSystemClock(m_ephemeral_clock); + ASSERT(res == ResultSuccess); + res = + m_time_m->GetEphemeralNetworkClockOperationEventForServiceManager(&m_ephemeral_clock_event); + ASSERT(res == ResultSuccess); + + res = m_time_m->GetStandardUserSystemClockAutomaticCorrectionUpdatedEvent( + &m_standard_user_auto_correct_clock_event); + ASSERT(res == ResultSuccess); +} + +void TimeWorker::StartThread() { + m_thread = std::jthread(std::bind_front(&TimeWorker::ThreadFunc, this)); +} + +void TimeWorker::ThreadFunc(std::stop_token stop_token) { + Common::SetCurrentThreadName("TimeWorker"); + Common::SetCurrentThreadPriority(Common::ThreadPriority::Low); + + 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<Kernel::KSynchronizationObject*, static_cast<u32>(EventType::Max)> wait_objs{}; + std::array<EventType, static_cast<u32>(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 100644 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<Service::PSC::Time::StaticService> time_sm, + std::shared_ptr<Service::Set::ISystemSettingsServer> set_sys); + + void StartThread(); + +private: + void ThreadFunc(std::stop_token stop_token); + + Core::System& m_system; + KernelHelpers::ServiceContext m_ctx; + std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys; + + std::jthread m_thread; + Kernel::KEvent* m_event{}; + std::shared_ptr<Service::PSC::Time::ServiceManager> m_time_m; + std::shared_ptr<Service::PSC::Time::StaticService> m_time_sm; + std::shared_ptr<Service::PSC::Time::SystemClock> m_network_clock; + std::shared_ptr<Service::PSC::Time::SystemClock> m_local_clock; + std::shared_ptr<Service::PSC::Time::SystemClock> m_ephemeral_clock; + StandardSteadyClockResource& m_steady_clock_resource; + FileTimestampWorker& m_file_timestamp_worker; + Kernel::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<Core::Timing::EventType> m_timer_steady_clock_timing_event; + Kernel::KEvent* m_timer_file_system{}; + std::shared_ptr<Core::Timing::EventType> m_timer_file_system_timing_event; + AlarmWorker m_alarm_worker; + PmStateChangeHandler m_pm_state_change_handler; +}; + +} // namespace Service::Glue::Time diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 595a3372e..5b28be577 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -33,7 +33,7 @@ void LoopProcess(Core::System& system) { server_manager->RegisterNamedService( "hid:dbg", std::make_shared<IHidDebugServer>(system, resource_manager)); server_manager->RegisterNamedService( - "hid:sys", std::make_shared<IHidSystemServer>(system, resource_manager)); + "hid:sys", std::make_shared<IHidSystemServer>(system, resource_manager, firmware_settings)); server_manager->RegisterNamedService("hidbus", std::make_shared<HidBus>(system)); diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp index 30afed812..09c47b5e3 100644 --- a/src/core/hle/service/hid/hid_server.cpp +++ b/src/core/hle/service/hid/hid_server.cpp @@ -1419,8 +1419,8 @@ void IHidServer::EnableUnintendedHomeButtonInputProtection(HLERequestContext& ct const auto parameters{rp.PopRaw<Parameters>()}; - LOG_INFO(Service_HID, "called, is_enabled={}, npad_id={}, applet_resource_user_id={}", - parameters.is_enabled, parameters.npad_id, parameters.applet_resource_user_id); + LOG_DEBUG(Service_HID, "called, is_enabled={}, npad_id={}, applet_resource_user_id={}", + parameters.is_enabled, parameters.npad_id, parameters.applet_resource_user_id); if (!IsNpadIdValid(parameters.npad_id)) { IPC::ResponseBuilder rb{ctx, 3}; diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp index bf27ddfbf..d1ec42edc 100644 --- a/src/core/hle/service/hid/hid_system_server.cpp +++ b/src/core/hle/service/hid/hid_system_server.cpp @@ -3,8 +3,10 @@ #include "core/hle/service/hid/hid_system_server.h" #include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/set/settings_types.h" #include "hid_core/hid_result.h" #include "hid_core/resource_manager.h" +#include "hid_core/resources/hid_firmware_settings.h" #include "hid_core/resources/npad/npad.h" #include "hid_core/resources/npad/npad_types.h" #include "hid_core/resources/npad/npad_vibration.h" @@ -13,9 +15,10 @@ namespace Service::HID { -IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource) +IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource, + std::shared_ptr<HidFirmwareSettings> settings) : ServiceFramework{system_, "hid:sys"}, service_context{system_, service_name}, - resource_manager{resource} { + resource_manager{resource}, firmware_settings{settings} { // clang-format off static const FunctionInfo functions[] = { {31, nullptr, "SendKeyboardLockKeyEvent"}, @@ -25,7 +28,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour {131, nullptr, "ActivateSleepButton"}, {141, nullptr, "AcquireCaptureButtonEventHandle"}, {151, nullptr, "ActivateCaptureButton"}, - {161, nullptr, "GetPlatformConfig"}, + {161, &IHidSystemServer::GetPlatformConfig, "GetPlatformConfig"}, {210, nullptr, "AcquireNfcDeviceUpdateEventHandle"}, {211, nullptr, "GetNpadsWithNfc"}, {212, nullptr, "AcquireNfcActivateEventHandle"}, @@ -80,7 +83,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour {520, nullptr, "EnableHandheldHids"}, {521, nullptr, "DisableHandheldHids"}, {522, nullptr, "SetJoyConRailEnabled"}, - {523, nullptr, "IsJoyConRailEnabled"}, + {523, &IHidSystemServer::IsJoyConRailEnabled, "IsJoyConRailEnabled"}, {524, nullptr, "IsHandheldHidsEnabled"}, {525, &IHidSystemServer::IsJoyConAttachedOnAllRail, "IsJoyConAttachedOnAllRail"}, {540, nullptr, "AcquirePlayReportControllerUsageUpdateEvent"}, @@ -123,7 +126,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour {831, nullptr, "SetNotificationLedPatternWithTimeout"}, {832, nullptr, "PrepareHidsForNotificationWake"}, {850, &IHidSystemServer::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"}, - {851, nullptr, "EnableUsbFullKeyController"}, + {851, &IHidSystemServer::EnableUsbFullKeyController, "EnableUsbFullKeyController"}, {852, nullptr, "IsUsbConnected"}, {870, &IHidSystemServer::IsHandheldButtonPressedOnConsoleMode, "IsHandheldButtonPressedOnConsoleMode"}, {900, nullptr, "ActivateInputDetector"}, @@ -148,7 +151,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour {1120, &IHidSystemServer::SetFirmwareHotfixUpdateSkipEnabled, "SetFirmwareHotfixUpdateSkipEnabled"}, {1130, &IHidSystemServer::InitializeUsbFirmwareUpdate, "InitializeUsbFirmwareUpdate"}, {1131, &IHidSystemServer::FinalizeUsbFirmwareUpdate, "FinalizeUsbFirmwareUpdate"}, - {1132, nullptr, "CheckUsbFirmwareUpdateRequired"}, + {1132, &IHidSystemServer::CheckUsbFirmwareUpdateRequired, "CheckUsbFirmwareUpdateRequired"}, {1133, nullptr, "StartUsbFirmwareUpdate"}, {1134, nullptr, "GetUsbFirmwareUpdateState"}, {1135, &IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory, "InitializeUsbFirmwareUpdateWithoutMemory"}, @@ -239,6 +242,16 @@ IHidSystemServer::~IHidSystemServer() { service_context.CloseEvent(unique_pad_connection_event); }; +void IHidSystemServer::GetPlatformConfig(HLERequestContext& ctx) { + const auto platform_config = firmware_settings->GetPlatformConfig(); + + LOG_INFO(Service_HID, "called, platform_config={}", platform_config.raw); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushRaw(platform_config); +} + void IHidSystemServer::ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop<u64>()}; @@ -674,6 +687,16 @@ void IHidSystemServer::EndPermitVibrationSession(HLERequestContext& ctx) { rb.Push(result); } +void IHidSystemServer::IsJoyConRailEnabled(HLERequestContext& ctx) { + const bool is_attached = true; + + LOG_WARNING(Service_HID, "(STUBBED) called, is_attached={}", is_attached); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(is_attached); +} + void IHidSystemServer::IsJoyConAttachedOnAllRail(HLERequestContext& ctx) { const bool is_attached = true; @@ -727,7 +750,7 @@ void IHidSystemServer::AcquireUniquePadConnectionEventHandle(HLERequestContext& } void IHidSystemServer::GetUniquePadIds(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_DEBUG(Service_HID, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); @@ -752,6 +775,16 @@ void IHidSystemServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) { rb.Push(is_enabled); } +void IHidSystemServer::EnableUsbFullKeyController(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto is_enabled{rp.Pop<bool>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, is_enabled={}", is_enabled); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void IHidSystemServer::IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx) { const bool button_pressed = false; @@ -798,6 +831,13 @@ void IHidSystemServer::FinalizeUsbFirmwareUpdate(HLERequestContext& ctx) { rb.Push(ResultSuccess); } +void IHidSystemServer::CheckUsbFirmwareUpdateRequired(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx) { LOG_WARNING(Service_HID, "(STUBBED) called"); diff --git a/src/core/hle/service/hid/hid_system_server.h b/src/core/hle/service/hid/hid_system_server.h index 90a719f02..4ab4d3931 100644 --- a/src/core/hle/service/hid/hid_system_server.h +++ b/src/core/hle/service/hid/hid_system_server.h @@ -16,13 +16,16 @@ class KEvent; namespace Service::HID { class ResourceManager; +class HidFirmwareSettings; class IHidSystemServer final : public ServiceFramework<IHidSystemServer> { public: - explicit IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource); + explicit IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource, + std::shared_ptr<HidFirmwareSettings> settings); ~IHidSystemServer() override; private: + void GetPlatformConfig(HLERequestContext& ctx); void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx); void EnableAssigningSingleOnSlSrPress(HLERequestContext& ctx); void DisableAssigningSingleOnSlSrPress(HLERequestContext& ctx); @@ -50,6 +53,7 @@ private: void GetVibrationMasterVolume(HLERequestContext& ctx); void BeginPermitVibrationSession(HLERequestContext& ctx); void EndPermitVibrationSession(HLERequestContext& ctx); + void IsJoyConRailEnabled(HLERequestContext& ctx); void IsJoyConAttachedOnAllRail(HLERequestContext& ctx); void AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx); void AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx); @@ -58,12 +62,14 @@ private: void GetUniquePadIds(HLERequestContext& ctx); void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx); void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx); + void EnableUsbFullKeyController(HLERequestContext& ctx); void IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx); void InitializeFirmwareUpdate(HLERequestContext& ctx); void CheckFirmwareUpdateRequired(HLERequestContext& ctx); void SetFirmwareHotfixUpdateSkipEnabled(HLERequestContext& ctx); void InitializeUsbFirmwareUpdate(HLERequestContext& ctx); void FinalizeUsbFirmwareUpdate(HLERequestContext& ctx); + void CheckUsbFirmwareUpdateRequired(HLERequestContext& ctx); void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx); void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx); void SetForceHandheldStyleVibration(HLERequestContext& ctx); @@ -77,6 +83,7 @@ private: Kernel::KEvent* unique_pad_connection_event; KernelHelpers::ServiceContext service_context; std::shared_ptr<ResourceManager> resource_manager; + std::shared_ptr<HidFirmwareSettings> firmware_settings; }; } // namespace Service::HID diff --git a/src/core/hle/service/hle_ipc.cpp b/src/core/hle/service/hle_ipc.cpp index e491dd260..50e1ed756 100644 --- a/src/core/hle/service/hle_ipc.cpp +++ b/src/core/hle/service/hle_ipc.cpp @@ -501,6 +501,22 @@ bool HLERequestContext::CanWriteBuffer(std::size_t buffer_index) const { } } +void HLERequestContext::AddMoveInterface(SessionRequestHandlerPtr s) { + ASSERT(Kernel::GetCurrentProcess(kernel).GetResourceLimit()->Reserve( + Kernel::LimitableResource::SessionCountMax, 1)); + + auto* session = Kernel::KSession::Create(kernel); + session->Initialize(nullptr, 0); + Kernel::KSession::Register(kernel, session); + + auto& server = manager.lock()->GetServerManager(); + auto next_manager = std::make_shared<Service::SessionRequestManager>(kernel, server); + next_manager->SetSessionHandler(std::move(s)); + server.RegisterSession(&session->GetServerSession(), next_manager); + + AddMoveObject(&session->GetClientSession()); +} + std::string HLERequestContext::Description() const { if (!command_header) { return "No command header available"; diff --git a/src/core/hle/service/hle_ipc.h b/src/core/hle/service/hle_ipc.h index 8329d7265..c2e0e5e8c 100644 --- a/src/core/hle/service/hle_ipc.h +++ b/src/core/hle/service/hle_ipc.h @@ -339,6 +339,8 @@ public: outgoing_move_objects.emplace_back(object); } + void AddMoveInterface(SessionRequestHandlerPtr s); + void AddCopyObject(Kernel::KAutoObject* object) { outgoing_copy_objects.emplace_back(object); } diff --git a/src/core/hle/service/jit/jit.cpp b/src/core/hle/service/jit/jit.cpp index 77aa6d7d1..1f2cbcb61 100644 --- a/src/core/hle/service/jit/jit.cpp +++ b/src/core/hle/service/jit/jit.cpp @@ -6,12 +6,12 @@ #include "core/core.h" #include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/result.h" +#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/jit/jit.h" #include "core/hle/service/jit/jit_code_memory.h" #include "core/hle/service/jit/jit_context.h" #include "core/hle/service/server_manager.h" -#include "core/hle/service/service.h" #include "core/memory.h" namespace Service::JIT { @@ -21,20 +21,24 @@ struct CodeRange { u64 size; }; +using Struct32 = std::array<u64, 4>; +static_assert(sizeof(Struct32) == 32, "Struct32 has wrong size"); + class IJitEnvironment final : public ServiceFramework<IJitEnvironment> { public: explicit IJitEnvironment(Core::System& system_, - Kernel::KScopedAutoObject<Kernel::KProcess>&& process_, + Kernel::KScopedAutoObject<Kernel::KProcess> process_, CodeMemory&& user_rx_, CodeMemory&& user_ro_) : ServiceFramework{system_, "IJitEnvironment"}, process{std::move(process_)}, user_rx{std::move(user_rx_)}, user_ro{std::move(user_ro_)}, context{system_.ApplicationMemory()} { + // clang-format off static const FunctionInfo functions[] = { - {0, &IJitEnvironment::GenerateCode, "GenerateCode"}, - {1, &IJitEnvironment::Control, "Control"}, - {1000, &IJitEnvironment::LoadPlugin, "LoadPlugin"}, - {1001, &IJitEnvironment::GetCodeAddress, "GetCodeAddress"}, + {0, C<&IJitEnvironment::GenerateCode>, "GenerateCode"}, + {1, C<&IJitEnvironment::Control>, "Control"}, + {1000, C<&IJitEnvironment::LoadPlugin>, "LoadPlugin"}, + {1001, C<&IJitEnvironment::GetCodeAddress>, "GetCodeAddress"}, }; // clang-format on @@ -50,28 +54,10 @@ public: configuration.sys_ro_memory = configuration.user_ro_memory; } - void GenerateCode(HLERequestContext& ctx) { - LOG_DEBUG(Service_JIT, "called"); - - struct InputParameters { - u32 data_size; - u64 command; - std::array<CodeRange, 2> ranges; - Struct32 data; - }; - - struct OutputParameters { - s32 return_value; - std::array<CodeRange, 2> ranges; - }; - - IPC::RequestParser rp{ctx}; - const auto parameters{rp.PopRaw<InputParameters>()}; - - // Optional input/output buffers - const auto input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::span<const u8>()}; - std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0); - + Result GenerateCode(Out<s32> out_return_value, Out<CodeRange> out_range0, + Out<CodeRange> out_range1, OutBuffer<BufferAttr_HipcMapAlias> out_buffer, + u32 data_size, u64 command, CodeRange range0, CodeRange range1, + Struct32 data, InBuffer<BufferAttr_HipcMapAlias> buffer) { // Function call prototype: // void GenerateCode(s32* ret, CodeRange* c0_out, CodeRange* c1_out, JITConfiguration* cfg, // u64 cmd, u8* input_buf, size_t input_size, CodeRange* c0_in, @@ -83,66 +69,36 @@ public: // other arguments are used to transfer state between the game and the plugin. const VAddr ret_ptr{context.AddHeap(0u)}; - const VAddr c0_in_ptr{context.AddHeap(parameters.ranges[0])}; - const VAddr c1_in_ptr{context.AddHeap(parameters.ranges[1])}; - const VAddr c0_out_ptr{context.AddHeap(ClearSize(parameters.ranges[0]))}; - const VAddr c1_out_ptr{context.AddHeap(ClearSize(parameters.ranges[1]))}; - - const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())}; - const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())}; - const VAddr data_ptr{context.AddHeap(parameters.data)}; + const VAddr c0_in_ptr{context.AddHeap(range0)}; + const VAddr c1_in_ptr{context.AddHeap(range1)}; + const VAddr c0_out_ptr{context.AddHeap(ClearSize(range0))}; + const VAddr c1_out_ptr{context.AddHeap(ClearSize(range1))}; + + const VAddr input_ptr{context.AddHeap(buffer.data(), buffer.size())}; + const VAddr output_ptr{context.AddHeap(out_buffer.data(), out_buffer.size())}; + const VAddr data_ptr{context.AddHeap(data)}; const VAddr configuration_ptr{context.AddHeap(configuration)}; // The callback does not directly return a value, it only writes to the output pointer context.CallFunction(callbacks.GenerateCode, ret_ptr, c0_out_ptr, c1_out_ptr, - configuration_ptr, parameters.command, input_ptr, input_buffer.size(), - c0_in_ptr, c1_in_ptr, data_ptr, parameters.data_size, output_ptr, - output_buffer.size()); - - const s32 return_value{context.GetHeap<s32>(ret_ptr)}; - - if (return_value == 0) { - // The callback has written to the output executable code range, - // requiring an instruction cache invalidation - Core::InvalidateInstructionCacheRange(process.GetPointerUnsafe(), - configuration.user_rx_memory.offset, - configuration.user_rx_memory.size); - - // Write back to the IPC output buffer, if provided - if (ctx.CanWriteBuffer()) { - context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size()); - ctx.WriteBuffer(output_buffer.data(), output_buffer.size()); - } - - const OutputParameters out{ - .return_value = return_value, - .ranges = - { - context.GetHeap<CodeRange>(c0_out_ptr), - context.GetHeap<CodeRange>(c1_out_ptr), - }, - }; - - IPC::ResponseBuilder rb{ctx, 8}; - rb.Push(ResultSuccess); - rb.PushRaw(out); - } else { - LOG_WARNING(Service_JIT, "plugin GenerateCode callback failed"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - } - }; + configuration_ptr, command, input_ptr, buffer.size(), c0_in_ptr, + c1_in_ptr, data_ptr, data_size, output_ptr, out_buffer.size()); - void Control(HLERequestContext& ctx) { - LOG_DEBUG(Service_JIT, "called"); + *out_return_value = context.GetHeap<s32>(ret_ptr); + *out_range0 = context.GetHeap<CodeRange>(c0_out_ptr); + *out_range1 = context.GetHeap<CodeRange>(c1_out_ptr); + context.GetHeap(output_ptr, out_buffer.data(), out_buffer.size()); - IPC::RequestParser rp{ctx}; - const auto command{rp.PopRaw<u64>()}; + if (*out_return_value != 0) { + LOG_WARNING(Service_JIT, "plugin GenerateCode callback failed"); + R_THROW(ResultUnknown); + } - // Optional input/output buffers - const auto input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::span<const u8>()}; - std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0); + R_SUCCEED(); + } + Result Control(Out<s32> out_return_value, InBuffer<BufferAttr_HipcMapAlias> in_data, + OutBuffer<BufferAttr_HipcMapAlias> out_data, u64 command) { // Function call prototype: // u64 Control(s32* ret, JITConfiguration* cfg, u64 cmd, u8* input_buf, size_t input_size, // u8* output_buf, size_t output_size); @@ -152,53 +108,30 @@ public: const VAddr ret_ptr{context.AddHeap(0u)}; const VAddr configuration_ptr{context.AddHeap(configuration)}; - const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())}; - const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())}; + const VAddr input_ptr{context.AddHeap(in_data.data(), in_data.size())}; + const VAddr output_ptr{context.AddHeap(out_data.data(), out_data.size())}; const u64 wrapper_value{context.CallFunction(callbacks.Control, ret_ptr, configuration_ptr, - command, input_ptr, input_buffer.size(), - output_ptr, output_buffer.size())}; - - const s32 return_value{context.GetHeap<s32>(ret_ptr)}; - - if (wrapper_value == 0 && return_value == 0) { - // Write back to the IPC output buffer, if provided - if (ctx.CanWriteBuffer()) { - context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size()); - ctx.WriteBuffer(output_buffer.data(), output_buffer.size()); - } - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(return_value); - } else { - LOG_WARNING(Service_JIT, "plugin Control callback failed"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - } - } - - void LoadPlugin(HLERequestContext& ctx) { - LOG_DEBUG(Service_JIT, "called"); + command, input_ptr, in_data.size(), output_ptr, + out_data.size())}; - IPC::RequestParser rp{ctx}; - const auto tmem_size{rp.PopRaw<u64>()}; - const auto tmem_handle{ctx.GetCopyHandle(0)}; - const auto nro_plugin{ctx.ReadBuffer(1)}; + *out_return_value = context.GetHeap<s32>(ret_ptr); + context.GetHeap(output_ptr, out_data.data(), out_data.size()); - if (tmem_size == 0) { - LOG_ERROR(Service_JIT, "attempted to load plugin with empty transfer memory"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + if (wrapper_value == 0 && *out_return_value == 0) { + R_SUCCEED(); } - auto tmem{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(tmem_handle)}; - if (tmem.IsNull()) { - LOG_ERROR(Service_JIT, "attempted to load plugin with invalid transfer memory handle"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + LOG_WARNING(Service_JIT, "plugin Control callback failed"); + R_THROW(ResultUnknown); + } + + Result LoadPlugin(u64 tmem_size, InCopyHandle<Kernel::KTransferMemory>& tmem, + InBuffer<BufferAttr_HipcMapAlias> nrr, + InBuffer<BufferAttr_HipcMapAlias> nro) { + if (!tmem) { + LOG_ERROR(Service_JIT, "Invalid transfer memory handle!"); + R_THROW(ResultUnknown); } // Set up the configuration with the required TransferMemory address @@ -206,7 +139,7 @@ public: configuration.transfer_memory.size = tmem_size; // Gather up all the callbacks from the loaded plugin - auto symbols{Core::Symbols::GetSymbols(nro_plugin, true)}; + auto symbols{Core::Symbols::GetSymbols(nro, true)}; const auto GetSymbol{[&](const std::string& name) { return symbols[name].first; }}; callbacks.rtld_fini = GetSymbol("_fini"); @@ -223,16 +156,12 @@ public: if (callbacks.GetVersion == 0 || callbacks.Configure == 0 || callbacks.GenerateCode == 0 || callbacks.OnPrepared == 0) { LOG_ERROR(Service_JIT, "plugin does not implement all necessary functionality"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + R_THROW(ResultUnknown); } - if (!context.LoadNRO(nro_plugin)) { + if (!context.LoadNRO(nro)) { LOG_ERROR(Service_JIT, "failed to load plugin"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + R_THROW(ResultUnknown); } context.MapProcessMemory(configuration.sys_ro_memory.offset, @@ -252,9 +181,7 @@ public: const auto version{context.CallFunction(callbacks.GetVersion)}; if (version != 1) { LOG_ERROR(Service_JIT, "unknown plugin version {}", version); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + R_THROW(ResultUnknown); } // Function prototype: @@ -280,22 +207,19 @@ public: const auto configuration_ptr{context.AddHeap(configuration)}; context.CallFunction(callbacks.OnPrepared, configuration_ptr); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + R_SUCCEED(); } - void GetCodeAddress(HLERequestContext& ctx) { + Result GetCodeAddress(Out<u64> rx_offset, Out<u64> ro_offset) { LOG_DEBUG(Service_JIT, "called"); - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.Push(configuration.user_rx_memory.offset); - rb.Push(configuration.user_ro_memory.offset); + *rx_offset = configuration.user_rx_memory.offset; + *ro_offset = configuration.user_ro_memory.offset; + + R_SUCCEED(); } private: - using Struct32 = std::array<u8, 32>; - struct GuestCallbacks { VAddr rtld_fini; VAddr rtld_init; @@ -335,7 +259,7 @@ public: explicit JITU(Core::System& system_) : ServiceFramework{system_, "jit:u"} { // clang-format off static const FunctionInfo functions[] = { - {0, &JITU::CreateJitEnvironment, "CreateJitEnvironment"}, + {0, C<&JITU::CreateJitEnvironment>, "CreateJitEnvironment"}, }; // clang-format on @@ -343,76 +267,33 @@ public: } private: - void CreateJitEnvironment(HLERequestContext& ctx) { - LOG_DEBUG(Service_JIT, "called"); - - struct Parameters { - u64 rx_size; - u64 ro_size; - }; - - IPC::RequestParser rp{ctx}; - const auto parameters{rp.PopRaw<Parameters>()}; - const auto process_handle{ctx.GetCopyHandle(0)}; - const auto rx_mem_handle{ctx.GetCopyHandle(1)}; - const auto ro_mem_handle{ctx.GetCopyHandle(2)}; - - if (parameters.rx_size == 0 || parameters.ro_size == 0) { - LOG_ERROR(Service_JIT, "attempted to init with empty code regions"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; - } - - auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(process_handle)}; - if (process.IsNull()) { - LOG_ERROR(Service_JIT, "process is null for handle=0x{:08X}", process_handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + Result CreateJitEnvironment(Out<SharedPointer<IJitEnvironment>> out_jit_environment, + u64 rx_size, u64 ro_size, InCopyHandle<Kernel::KProcess>& process, + InCopyHandle<Kernel::KCodeMemory>& rx_mem, + InCopyHandle<Kernel::KCodeMemory>& ro_mem) { + if (!process) { + LOG_ERROR(Service_JIT, "process is null"); + R_THROW(ResultUnknown); } - - auto rx_mem{ctx.GetObjectFromHandle<Kernel::KCodeMemory>(rx_mem_handle)}; - if (rx_mem.IsNull()) { - LOG_ERROR(Service_JIT, "rx_mem is null for handle=0x{:08X}", rx_mem_handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + if (!rx_mem) { + LOG_ERROR(Service_JIT, "rx_mem is null"); + R_THROW(ResultUnknown); } - - auto ro_mem{ctx.GetObjectFromHandle<Kernel::KCodeMemory>(ro_mem_handle)}; - if (ro_mem.IsNull()) { - LOG_ERROR(Service_JIT, "ro_mem is null for handle=0x{:08X}", ro_mem_handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + if (!ro_mem) { + LOG_ERROR(Service_JIT, "ro_mem is null"); + R_THROW(ResultUnknown); } CodeMemory rx, ro; - Result res; - - res = rx.Initialize(*process, *rx_mem, parameters.rx_size, - Kernel::Svc::MemoryPermission::ReadExecute, generate_random); - if (R_FAILED(res)) { - LOG_ERROR(Service_JIT, "rx_mem could not be mapped for handle=0x{:08X}", rx_mem_handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); - return; - } - res = ro.Initialize(*process, *ro_mem, parameters.ro_size, - Kernel::Svc::MemoryPermission::Read, generate_random); - if (R_FAILED(res)) { - LOG_ERROR(Service_JIT, "ro_mem could not be mapped for handle=0x{:08X}", ro_mem_handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); - return; - } + R_TRY(rx.Initialize(*process, *rx_mem, rx_size, Kernel::Svc::MemoryPermission::ReadExecute, + generate_random)); + R_TRY(ro.Initialize(*process, *ro_mem, ro_size, Kernel::Svc::MemoryPermission::Read, + generate_random)); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IJitEnvironment>(system, std::move(process), std::move(rx), - std::move(ro)); + *out_jit_environment = + std::make_shared<IJitEnvironment>(system, process.Get(), std::move(rx), std::move(ro)); + R_SUCCEED(); } private: diff --git a/src/core/hle/service/kernel_helpers.cpp b/src/core/hle/service/kernel_helpers.cpp index f51e63564..f080f7ffa 100644 --- 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/device.cpp b/src/core/hle/service/nfc/common/device.cpp index cc7776efc..1e2d2d212 100644 --- a/src/core/hle/service/nfc/common/device.cpp +++ b/src/core/hle/service/nfc/common/device.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "core/hle/service/glue/time/static.h" +#include "core/hle/service/psc/time/steady_clock.h" #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4701) // Potentially uninitialized local variable 'result' used @@ -29,7 +31,8 @@ #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/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" @@ -393,8 +396,7 @@ Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> paramet return result; } -Result NfcDevice::SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout, - std::span<const u8> command_data, +Result NfcDevice::SendCommandByPassThrough(const s64& timeout, std::span<const u8> command_data, std::span<u8> out_data) { // Not implemented return ResultSuccess; @@ -1399,27 +1401,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<Service::Glue::Time::StaticService>("time:u", true); + + std::shared_ptr<Service::Glue::Time::TimeZoneService> 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<Service::Glue::Time::StaticService>("time:u", true); + + std::shared_ptr<Service::PSC::Time::SteadyClock> 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 15f9b25da..d59202d18 100644 --- 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<const MifareWriteBlockParameter> parameters); - Result SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout, - std::span<const u8> command_data, std::span<u8> out_data); + Result SendCommandByPassThrough(const s64& timeout, std::span<const u8> command_data, + std::span<u8> 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(); diff --git a/src/core/hle/service/nfc/common/device_manager.cpp b/src/core/hle/service/nfc/common/device_manager.cpp index 44f651b87..b60699c45 100644 --- a/src/core/hle/service/nfc/common/device_manager.cpp +++ b/src/core/hle/service/nfc/common/device_manager.cpp @@ -6,12 +6,14 @@ #include "common/logging/log.h" #include "core/core.h" #include "core/hle/kernel/k_event.h" +#include "core/hle/service/glue/time/static.h" #include "core/hle/service/ipc_helpers.h" #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/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<u64>& 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<Service::Glue::Time::StaticService>("time:u", + true); + + std::shared_ptr<Service::PSC::Time::SteadyClock> 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<const u8> command_data, std::span<u8> out_data) { std::scoped_lock lock{mutex}; @@ -741,8 +750,16 @@ Result DeviceManager::VerifyDeviceResult(std::shared_ptr<NfcDevice> 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<Service::Glue::Time::StaticService>("time:u", true); + + std::shared_ptr<Service::PSC::Time::SteadyClock> 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 100644 --- 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<MifareReadBlockData> read_data); Result WriteMifare(u64 device_handle, std::span<const MifareWriteBlockParameter> write_parameters); - Result SendCommandByPassThrough(u64 device_handle, const Time::Clock::TimeSpanType& timeout, + Result SendCommandByPassThrough(u64 device_handle, const s64& timeout, std::span<const u8> command_data, std::span<u8> out_data); // Nfp device manager @@ -92,7 +91,7 @@ private: const std::optional<std::shared_ptr<NfcDevice>> 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<std::shared_ptr<NfcDevice>, 10> devices{}; diff --git a/src/core/hle/service/nfc/nfc_interface.cpp b/src/core/hle/service/nfc/nfc_interface.cpp index a71cf74b8..3e2c7deab 100644 --- 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<u64>()}; - const auto timeout{rp.PopRaw<Time::Clock::TimeSpanType>()}; + const auto timeout{rp.PopRaw<s64>()}; 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<u8> out_data(1); auto result = @@ -302,7 +301,7 @@ Result NfcInterface::TranslateResultToServiceError(Result result) const { return result; } - if (result.module != ErrorModule::NFC) { + if (result.GetModule() != ErrorModule::NFC) { return result; } diff --git a/src/core/hle/service/ns/language.cpp b/src/core/hle/service/ns/language.cpp index b1a7686ff..d187be935 100644 --- a/src/core/hle/service/ns/language.cpp +++ b/src/core/hle/service/ns/language.cpp @@ -415,4 +415,4 @@ std::optional<Set::LanguageCode> 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 ab6b71029..dad9934f2 100644 --- a/src/core/hle/service/ns/language.h +++ b/src/core/hle/service/ns/language.h @@ -5,10 +5,7 @@ #include <optional> #include "common/common_types.h" - -namespace Service::Set { -enum class LanguageCode : u64; -} +#include "core/hle/service/set/system_settings_server.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 a25b79513..2258ee609 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp @@ -6,7 +6,7 @@ #include "core/core.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/glue/glue_manager.h" #include "core/hle/service/ipc_helpers.h" diff --git a/src/core/hle/service/nvdrv/core/container.cpp b/src/core/hle/service/nvdrv/core/container.cpp index 21ef57d27..dc1b4d5be 100644 --- a/src/core/hle/service/nvdrv/core/container.cpp +++ b/src/core/hle/service/nvdrv/core/container.cpp @@ -112,6 +112,7 @@ SessionId Container::OpenSession(Kernel::KProcess* process) { void Container::CloseSession(SessionId session_id) { std::scoped_lock lk(impl->session_guard); + impl->file.UnmapAllHandles(session_id); auto& session = impl->sessions[session_id.id]; auto& smmu = impl->host1x.MemoryManager(); if (session.has_preallocated_area) { diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp index 1b59c6b15..bc1c033c6 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.cpp +++ b/src/core/hle/service/nvdrv/core/nvmap.cpp @@ -326,4 +326,17 @@ std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool interna return freeInfo; } +void NvMap::UnmapAllHandles(NvCore::SessionId session_id) { + auto handles_copy = [&] { + std::scoped_lock lk{handles_lock}; + return handles; + }(); + + for (auto& [id, handle] : handles_copy) { + if (handle->session_id.id == session_id.id) { + FreeHandle(id, false); + } + } +} + } // namespace Service::Nvidia::NvCore diff --git a/src/core/hle/service/nvdrv/core/nvmap.h b/src/core/hle/service/nvdrv/core/nvmap.h index d7f695845..b8be599ae 100644 --- a/src/core/hle/service/nvdrv/core/nvmap.h +++ b/src/core/hle/service/nvdrv/core/nvmap.h @@ -152,6 +152,8 @@ public: */ std::optional<FreeInfo> FreeHandle(Handle::Id handle, bool internal_session); + void UnmapAllHandles(NvCore::SessionId session_id); + private: std::list<std::shared_ptr<Handle>> unmap_queue{}; std::mutex unmap_queue_lock{}; //!< Protects access to `unmap_queue` diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp index 6e4825313..ffe72f281 100644 --- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp +++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp @@ -4,6 +4,7 @@ #include "common/logging/log.h" #include "common/scope_exit.h" +#include "common/string_util.h" #include "core/core.h" #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_process.h" @@ -29,7 +30,7 @@ void NVDRV::Open(HLERequestContext& ctx) { } const auto& buffer = ctx.ReadBuffer(); - const std::string device_name(buffer.begin(), buffer.end()); + const std::string device_name(Common::StringFromBuffer(buffer)); if (device_name == "/dev/nvhost-prof-gpu") { rb.Push<DeviceFD>(0); diff --git a/src/core/hle/service/psc/psc.cpp b/src/core/hle/service/psc/psc.cpp index cd0cc9287..44310756b 100644 --- a/src/core/hle/service/psc/psc.cpp +++ b/src/core/hle/service/psc/psc.cpp @@ -4,9 +4,13 @@ #include <memory> #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<IPmControl>(system)); server_manager->RegisterNamedService("psc:m", std::make_shared<IPmService>(system)); + + auto time = std::make_shared<Time::TimeManager>(system); + + server_manager->RegisterNamedService( + "time:m", std::make_shared<Time::ServiceManager>(system, time, server_manager.get())); + server_manager->RegisterNamedService( + "time:su", std::make_shared<Time::StaticService>( + system, Time::StaticServiceSetupInfo{0, 0, 0, 0, 0, 1}, time, "time:su")); + server_manager->RegisterNamedService("time:al", + std::make_shared<Time::IAlarmService>(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 100644 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::nanoseconds>(std::chrono::seconds(1)).count()}; + time_ns = Common::AlignUp(time_ns, one_second_ns); + alarm.SetAlertTime(time_ns); + + Insert(alarm); + R_RETURN(UpdateClosestAndSignal()); +} + +void Alarms::Disable(Alarm& alarm) { + std::scoped_lock l{m_mutex}; + if (!alarm.IsLinked()) { + return; + } + + Erase(alarm); + UpdateClosestAndSignal(); +} + +void Alarms::CheckAndSignal() { + std::scoped_lock l{m_mutex}; + if (m_alarms.empty()) { + return; + } + + bool alarm_signalled{false}; + for (auto& alarm : m_alarms) { + if (m_steady_clock.GetRawTime() >= alarm.GetAlertTime()) { + alarm.Signal(); + alarm.Lock(); + Erase(alarm); + + m_power_state_request_manager.UpdatePendingPowerStateRequestPriority( + alarm.GetPriority()); + alarm_signalled = true; + } + } + + if (!alarm_signalled) { + return; + } + + m_power_state_request_manager.SignalPowerStateRequestAvailability(); + UpdateClosestAndSignal(); +} + +bool Alarms::GetClosestAlarm(Alarm** out_alarm) { + std::scoped_lock l{m_mutex}; + auto alarm = m_alarms.empty() ? nullptr : std::addressof(m_alarms.front()); + *out_alarm = alarm; + return alarm != nullptr; +} + +void Alarms::Insert(Alarm& alarm) { + // Alarms are sorted by alert time, then priority + auto it{m_alarms.begin()}; + while (it != m_alarms.end()) { + if (alarm.GetAlertTime() < it->GetAlertTime() || + (alarm.GetAlertTime() == it->GetAlertTime() && + alarm.GetPriority() < it->GetPriority())) { + m_alarms.insert(it, alarm); + return; + } + it++; + } + + m_alarms.push_back(alarm); +} + +void Alarms::Erase(Alarm& alarm) { + m_alarms.erase(m_alarms.iterator_to(alarm)); +} + +Result Alarms::UpdateClosestAndSignal() { + m_closest_alarm = m_alarms.empty() ? nullptr : std::addressof(m_alarms.front()); + R_SUCCEED_IF(m_closest_alarm == nullptr); + + m_event->Signal(); + + R_SUCCEED(); +} + +IAlarmService::IAlarmService(Core::System& system_, std::shared_ptr<TimeManager> manager) + : ServiceFramework{system_, "time:al"}, m_system{system}, m_alarms{manager->m_alarms} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IAlarmService::CreateWakeupAlarm, "CreateWakeupAlarm"}, + {1, &IAlarmService::CreateBackgroundTaskAlarm, "CreateBackgroundTaskAlarm"}, + }; + // clang-format on + RegisterHandlers(functions); +} + +void IAlarmService::CreateWakeupAlarm(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<ISteadyClockAlarm>(system, m_alarms, AlarmType::WakeupAlarm); +} + +void IAlarmService::CreateBackgroundTaskAlarm(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<ISteadyClockAlarm>(system, m_alarms, AlarmType::BackgroundTaskAlarm); +} + +ISteadyClockAlarm::ISteadyClockAlarm(Core::System& system_, Alarms& alarms, AlarmType type) + : ServiceFramework{system_, "ISteadyClockAlarm"}, m_ctx{system, "Psc:ISteadyClockAlarm"}, + m_alarms{alarms}, m_alarm{system, m_ctx, type} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &ISteadyClockAlarm::GetAlarmEvent, "GetAlarmEvent"}, + {1, &ISteadyClockAlarm::Enable, "Enable"}, + {2, &ISteadyClockAlarm::Disable, "Disable"}, + {3, &ISteadyClockAlarm::IsEnabled, "IsEnabled"}, + {10, nullptr, "CreateWakeLock"}, + {11, nullptr, "DestroyWakeLock"}, + }; + // clang-format on + RegisterHandlers(functions); +} + +void ISteadyClockAlarm::GetAlarmEvent(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(m_alarm.GetEventHandle()); +} + +void ISteadyClockAlarm::Enable(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto time{rp.Pop<s64>()}; + + auto res = m_alarms.Enable(m_alarm, time); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ISteadyClockAlarm::Disable(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + m_alarms.Disable(m_alarm); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISteadyClockAlarm::IsEnabled(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<bool>(m_alarm.IsLinked()); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/alarms.h b/src/core/hle/service/psc/time/alarms.h new file mode 100644 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 <mutex> + +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/psc/time/clocks/standard_steady_clock_core.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/psc/time/power_state_request_manager.h" +#include "core/hle/service/server_manager.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { +class TimeManager; + +enum AlarmType : u32 { + WakeupAlarm = 0, + BackgroundTaskAlarm = 1, +}; + +struct Alarm : public Common::IntrusiveListBaseNode<Alarm> { + using AlarmList = Common::IntrusiveListBaseTraits<Alarm>::ListType; + + Alarm(Core::System& system, KernelHelpers::ServiceContext& ctx, AlarmType type); + ~Alarm(); + + Kernel::KReadableEvent& GetEventHandle() { + return m_event->GetReadableEvent(); + } + + s64 GetAlertTime() const { + return m_alert_time; + } + + void SetAlertTime(s64 time) { + m_alert_time = time; + } + + u32 GetPriority() const { + return m_priority; + } + + void Signal() { + m_event->Signal(); + } + + Result Lock() { + // TODO + // if (m_lock_service) { + // return m_lock_service->Lock(); + // } + R_SUCCEED(); + } + + KernelHelpers::ServiceContext& m_ctx; + + u32 m_priority; + Kernel::KEvent* m_event{}; + s64 m_alert_time{}; + // TODO + // nn::psc::sf::IPmStateLock* m_lock_service{}; +}; + +class Alarms { +public: + explicit Alarms(Core::System& system, StandardSteadyClockCore& steady_clock, + PowerStateRequestManager& power_state_request_manager); + ~Alarms(); + + Kernel::KEvent& GetEvent() { + return *m_event; + } + + s64 GetRawTime() { + return m_steady_clock.GetRawTime(); + } + + Result Enable(Alarm& alarm, s64 time); + void Disable(Alarm& alarm); + void CheckAndSignal(); + bool GetClosestAlarm(Alarm** out_alarm); + +private: + void Insert(Alarm& alarm); + void Erase(Alarm& alarm); + Result UpdateClosestAndSignal(); + + Core::System& m_system; + KernelHelpers::ServiceContext m_ctx; + + StandardSteadyClockCore& m_steady_clock; + PowerStateRequestManager& m_power_state_request_manager; + Alarm::AlarmList m_alarms; + Kernel::KEvent* m_event{}; + Alarm* m_closest_alarm{}; + std::mutex m_mutex; +}; + +class IAlarmService final : public ServiceFramework<IAlarmService> { +public: + explicit IAlarmService(Core::System& system, std::shared_ptr<TimeManager> manager); + + ~IAlarmService() override = default; + +private: + void CreateWakeupAlarm(HLERequestContext& ctx); + void CreateBackgroundTaskAlarm(HLERequestContext& ctx); + + Core::System& m_system; + Alarms& m_alarms; +}; + +class ISteadyClockAlarm final : public ServiceFramework<ISteadyClockAlarm> { +public: + explicit ISteadyClockAlarm(Core::System& system, Alarms& alarms, AlarmType type); + + ~ISteadyClockAlarm() override = default; + +private: + void GetAlarmEvent(HLERequestContext& ctx); + void Enable(HLERequestContext& ctx); + void Disable(HLERequestContext& ctx); + void IsEnabled(HLERequestContext& ctx); + + KernelHelpers::ServiceContext m_ctx; + + Alarms& m_alarms; + Alarm m_alarm; +}; + +} // namespace Service::PSC::Time 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 100644 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 100644 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 <list> + +#include "common/common_types.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/psc/time/clocks/system_clock_core.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/psc/time/shared_memory.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { + +class ContextWriter { +private: + using OperationEventList = Common::IntrusiveListBaseTraits<OperationEvent>::ListType; + +public: + virtual ~ContextWriter() = default; + + virtual Result Write(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 100644 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 100644 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 100644 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 100644 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::nanoseconds>(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 100644 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 <chrono> + +#include "core/hle/result.h" +#include "core/hle/service/psc/time/clocks/context_writers.h" +#include "core/hle/service/psc/time/clocks/steady_clock_core.h" +#include "core/hle/service/psc/time/clocks/system_clock_core.h" +#include "core/hle/service/psc/time/common.h" + +namespace Service::PSC::Time { + +class StandardNetworkSystemClockCore : public SystemClockCore { +public: + explicit StandardNetworkSystemClockCore(SteadyClockCore& steady_clock) + : SystemClockCore{steady_clock} {} + ~StandardNetworkSystemClockCore() override = default; + + void Initialize(SystemClockContext& context, s64 accuracy); + bool IsAccuracySufficient(); + +private: + s64 m_sufficient_accuracy{ + std::chrono ::duration_cast<std::chrono::nanoseconds>(std::chrono::days(10)).count()}; +}; + +} // namespace Service::PSC::Time 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 100644 index 000000000..7a72d7aa2 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/standard_steady_clock_core.cpp @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <chrono> + +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/service/psc/time/clocks/standard_steady_clock_core.h" + +namespace Service::PSC::Time { + +void StandardSteadyClockCore::Initialize(ClockSourceId clock_source_id, s64 rtc_offset, + s64 internal_offset, s64 test_offset, + bool is_rtc_reset_detected) { + m_clock_source_id = clock_source_id; + m_rtc_offset = rtc_offset; + m_internal_offset = internal_offset; + m_test_offset = test_offset; + if (is_rtc_reset_detected) { + SetResetDetected(); + } + SetInitialized(); +} + +void StandardSteadyClockCore::SetRtcOffset(s64 offset) { + m_rtc_offset = offset; +} + +void StandardSteadyClockCore::SetContinuousAdjustment(ClockSourceId clock_source_id, s64 time) { + auto ticks{m_system.CoreTiming().GetClockTicks()}; + + m_continuous_adjustment_time_point.rtc_offset = ConvertToTimeSpan(ticks).count(); + m_continuous_adjustment_time_point.diff_scale = 0; + m_continuous_adjustment_time_point.shift_amount = 0; + m_continuous_adjustment_time_point.lower = time; + m_continuous_adjustment_time_point.upper = time; + m_continuous_adjustment_time_point.clock_source_id = clock_source_id; +} + +void StandardSteadyClockCore::GetContinuousAdjustment( + ContinuousAdjustmentTimePoint& out_time_point) const { + out_time_point = m_continuous_adjustment_time_point; +} + +void StandardSteadyClockCore::UpdateContinuousAdjustmentTime(s64 in_time) { + auto ticks{m_system.CoreTiming().GetClockTicks()}; + auto uptime_ns{ConvertToTimeSpan(ticks).count()}; + auto adjusted_time{((uptime_ns - m_continuous_adjustment_time_point.rtc_offset) * + m_continuous_adjustment_time_point.diff_scale) >> + m_continuous_adjustment_time_point.shift_amount}; + auto expected_time{adjusted_time + m_continuous_adjustment_time_point.lower}; + + auto last_time_point{m_continuous_adjustment_time_point.upper}; + m_continuous_adjustment_time_point.upper = in_time; + auto t1{std::min<s64>(expected_time, last_time_point)}; + expected_time = std::max<s64>(expected_time, last_time_point); + expected_time = m_continuous_adjustment_time_point.diff_scale >= 0 ? t1 : expected_time; + + auto new_diff{in_time < expected_time ? -55 : 55}; + + m_continuous_adjustment_time_point.rtc_offset = uptime_ns; + m_continuous_adjustment_time_point.shift_amount = expected_time == in_time ? 0 : 14; + m_continuous_adjustment_time_point.diff_scale = expected_time == in_time ? 0 : new_diff; + m_continuous_adjustment_time_point.lower = expected_time; +} + +Result StandardSteadyClockCore::GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) { + auto current_time_ns = GetCurrentRawTimePointImpl(); + auto current_time_s = + std::chrono::duration_cast<std::chrono::seconds>(std::chrono::nanoseconds(current_time_ns)); + out_time_point.time_point = current_time_s.count(); + out_time_point.clock_source_id = m_clock_source_id; + R_SUCCEED(); +} + +s64 StandardSteadyClockCore::GetCurrentRawTimePointImpl() { + std::scoped_lock l{m_mutex}; + auto ticks{static_cast<s64>(m_system.CoreTiming().GetClockTicks())}; + auto current_time_ns = m_rtc_offset + ConvertToTimeSpan(ticks).count(); + auto time_point = std::max<s64>(current_time_ns, m_cached_time_point); + m_cached_time_point = time_point; + return time_point; +} + +s64 StandardSteadyClockCore::GetTestOffsetImpl() const { + return m_test_offset; +} + +void StandardSteadyClockCore::SetTestOffsetImpl(s64 offset) { + m_test_offset = offset; +} + +s64 StandardSteadyClockCore::GetInternalOffsetImpl() const { + return m_internal_offset; +} + +void StandardSteadyClockCore::SetInternalOffsetImpl(s64 offset) { + m_internal_offset = offset; +} + +} // namespace Service::PSC::Time 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 100644 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 <mutex> + +#include "core/hle/service/psc/time/clocks/steady_clock_core.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { +class StandardSteadyClockCore : public SteadyClockCore { +public: + explicit StandardSteadyClockCore(Core::System& system) : m_system{system} {} + ~StandardSteadyClockCore() override = default; + + void Initialize(ClockSourceId clock_source_id, s64 rtc_offset, s64 internal_offset, + s64 test_offset, bool is_rtc_reset_detected); + + void SetRtcOffset(s64 offset); + void SetContinuousAdjustment(ClockSourceId clock_source_id, s64 time); + void GetContinuousAdjustment(ContinuousAdjustmentTimePoint& out_time_point) const; + void UpdateContinuousAdjustmentTime(s64 time); + + Result GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) override; + s64 GetCurrentRawTimePointImpl() override; + s64 GetTestOffsetImpl() const override; + void SetTestOffsetImpl(s64 offset) override; + s64 GetInternalOffsetImpl() const override; + void SetInternalOffsetImpl(s64 offset) override; + + Result GetRtcValueImpl(s64& out_value) override { + R_RETURN(ResultNotImplemented); + } + + Result GetSetupResultValueImpl() override { + R_SUCCEED(); + } + +private: + Core::System& m_system; + + std::mutex m_mutex; + s64 m_test_offset{}; + s64 m_internal_offset{}; + ClockSourceId m_clock_source_id{}; + s64 m_rtc_offset{}; + s64 m_cached_time_point{}; + ContinuousAdjustmentTimePoint m_continuous_adjustment_time_point{}; +}; +} // namespace Service::PSC::Time 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 100644 index 000000000..9e9be05d6 --- /dev/null +++ b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/psc/time/clocks/standard_user_system_clock_core.h" + +namespace Service::PSC::Time { + +StandardUserSystemClockCore::StandardUserSystemClockCore( + Core::System& system, StandardLocalSystemClockCore& local_clock, + StandardNetworkSystemClockCore& network_clock) + : SystemClockCore{local_clock.GetSteadyClock()}, m_system{system}, + m_ctx{m_system, "Psc:StandardUserSystemClockCore"}, m_local_system_clock{local_clock}, + m_network_system_clock{network_clock}, m_event{m_ctx.CreateEvent( + "Psc:StandardUserSystemClockCore:Event")} {} + +StandardUserSystemClockCore::~StandardUserSystemClockCore() { + m_ctx.CloseEvent(m_event); +} + +Result StandardUserSystemClockCore::SetAutomaticCorrection(bool automatic_correction) { + R_SUCCEED_IF(m_automatic_correction == automatic_correction); + R_SUCCEED_IF(!m_network_system_clock.CheckClockSourceMatches()); + + SystemClockContext context{}; + R_TRY(m_network_system_clock.GetContext(context)); + R_TRY(m_local_system_clock.SetContextAndWrite(context)); + + m_automatic_correction = automatic_correction; + R_SUCCEED(); +} + +Result StandardUserSystemClockCore::GetContext(SystemClockContext& out_context) const { + if (!m_automatic_correction) { + R_RETURN(m_local_system_clock.GetContext(out_context)); + } + + if (!m_network_system_clock.CheckClockSourceMatches()) { + R_RETURN(m_local_system_clock.GetContext(out_context)); + } + + SystemClockContext context{}; + R_TRY(m_network_system_clock.GetContext(context)); + R_TRY(m_local_system_clock.SetContextAndWrite(context)); + + R_RETURN(m_local_system_clock.GetContext(out_context)); +} + +Result StandardUserSystemClockCore::SetContext(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 100644 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 100644 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 <chrono> + +#include "core/hle/result.h" +#include "core/hle/service/psc/time/common.h" + +namespace Service::PSC::Time { + +class SteadyClockCore { +public: + SteadyClockCore() = default; + virtual ~SteadyClockCore() = default; + + void SetInitialized() { + m_initialized = true; + } + + bool IsInitialized() const { + return m_initialized; + } + + void SetResetDetected() { + m_reset_detected = true; + } + + bool IsResetDetected() const { + return m_reset_detected; + } + + Result GetCurrentTimePoint(SteadyClockTimePoint& out_time_point) { + R_TRY(GetCurrentTimePointImpl(out_time_point)); + + auto one_second_ns{ + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()}; + out_time_point.time_point += GetTestOffsetImpl() / one_second_ns; + out_time_point.time_point += GetInternalOffsetImpl() / one_second_ns; + R_SUCCEED(); + } + + s64 GetTestOffset() const { + return GetTestOffsetImpl(); + } + + void SetTestOffset(s64 offset) { + SetTestOffsetImpl(offset); + } + + s64 GetInternalOffset() const { + return GetInternalOffsetImpl(); + } + + s64 GetRawTime() { + return GetCurrentRawTimePointImpl() + GetTestOffsetImpl() + GetInternalOffsetImpl(); + } + + Result GetRtcValue(s64& out_value) { + R_RETURN(GetRtcValueImpl(out_value)); + } + + Result GetSetupResultValue() { + R_RETURN(GetSetupResultValueImpl()); + } + +private: + virtual Result GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) = 0; + virtual s64 GetCurrentRawTimePointImpl() = 0; + virtual s64 GetTestOffsetImpl() const = 0; + virtual void SetTestOffsetImpl(s64 offset) = 0; + virtual s64 GetInternalOffsetImpl() const = 0; + virtual void SetInternalOffsetImpl(s64 offset) = 0; + virtual Result GetRtcValueImpl(s64& out_value) = 0; + virtual Result GetSetupResultValueImpl() = 0; + + bool m_initialized{}; + bool m_reset_detected{}; +}; +} // namespace Service::PSC::Time 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 100644 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 100644 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 100644 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 <chrono> + +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/service/psc/time/clocks/tick_based_steady_clock_core.h" + +namespace Service::PSC::Time { + +Result TickBasedSteadyClockCore::GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) { + auto ticks{m_system.CoreTiming().GetClockTicks()}; + auto current_time_s = + std::chrono::duration_cast<std::chrono::seconds>(ConvertToTimeSpan(ticks)).count(); + out_time_point.time_point = current_time_s; + out_time_point.clock_source_id = m_clock_source_id; + R_SUCCEED(); +} + +s64 TickBasedSteadyClockCore::GetCurrentRawTimePointImpl() { + SteadyClockTimePoint time_point{}; + if (GetCurrentTimePointImpl(time_point) != ResultSuccess) { + LOG_ERROR(Service_Time, "Failed to GetCurrentTimePoint!"); + } + return std::chrono::duration_cast<std::chrono::nanoseconds>( + std::chrono::seconds(time_point.time_point)) + .count(); +} + +s64 TickBasedSteadyClockCore::GetTestOffsetImpl() const { + return 0; +} + +void TickBasedSteadyClockCore::SetTestOffsetImpl(s64 offset) {} + +s64 TickBasedSteadyClockCore::GetInternalOffsetImpl() const { + return 0; +} + +void TickBasedSteadyClockCore::SetInternalOffsetImpl(s64 offset) {} + +} // namespace Service::PSC::Time 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 100644 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 <mutex> + +#include "common/uuid.h" +#include "core/hle/service/psc/time/clocks/steady_clock_core.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { +class TickBasedSteadyClockCore : public SteadyClockCore { +public: + explicit TickBasedSteadyClockCore(Core::System& system) : m_system{system} {} + ~TickBasedSteadyClockCore() override = default; + + Result GetCurrentTimePointImpl(SteadyClockTimePoint& out_time_point) override; + s64 GetCurrentRawTimePointImpl() override; + s64 GetTestOffsetImpl() const override; + void SetTestOffsetImpl(s64 offset) override; + s64 GetInternalOffsetImpl() const override; + void SetInternalOffsetImpl(s64 offset) override; + + Result GetRtcValueImpl(s64& out_value) override { + R_RETURN(ResultNotImplemented); + } + + Result GetSetupResultValueImpl() override { + R_SUCCEED(); + } + +private: + Core::System& m_system; + + ClockSourceId m_clock_source_id{Common::UUID::MakeRandom()}; +}; +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/common.cpp b/src/core/hle/service/psc/time/common.cpp new file mode 100644 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 100644 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 <array> +#include <chrono> + +#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<ClockSourceId>); + +struct SystemClockContext { + bool operator==(const SystemClockContext& other) const = default; + + s64 offset; + SteadyClockTimePoint steady_time_point; +}; +static_assert(sizeof(SystemClockContext) == 0x20, "SystemClockContext has the wrong size!"); +static_assert(std::is_trivial_v<SystemClockContext>); + +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<char, 8> name; + s32 is_dst; + s32 ut_offset; +}; +static_assert(sizeof(CalendarAdditionalInfo) == 0x18, "CalendarAdditionalInfo has the wrong size!"); + +struct LocationName { + std::array<char, 36> name; +}; +static_assert(sizeof(LocationName) == 0x24, "LocationName has the wrong size!"); + +struct RuleVersion { + std::array<char, 16> 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<ClockSnapshot>); + +struct ContinuousAdjustmentTimePoint { + s64 rtc_offset; + s64 diff_scale; + s64 shift_amount; + s64 lower; + s64 upper; + ClockSourceId clock_source_id; +}; +static_assert(sizeof(ContinuousAdjustmentTimePoint) == 0x38, + "ContinuousAdjustmentTimePoint has the wrong size!"); +static_assert(std::is_trivial_v<ContinuousAdjustmentTimePoint>); + +struct AlarmInfo { + s64 alert_time; + u32 priority; +}; +static_assert(sizeof(AlarmInfo) == 0x10, "AlarmInfo has the wrong size!"); + +struct StaticServiceSetupInfo { + bool can_write_local_clock; + bool can_write_user_clock; + bool can_write_network_clock; + bool can_write_timezone_device_location; + bool can_write_steady_clock; + bool can_write_uninitialized_clock; +}; +static_assert(sizeof(StaticServiceSetupInfo) == 0x6, "StaticServiceSetupInfo has the wrong size!"); + +struct OperationEvent : public Common::IntrusiveListBaseNode<OperationEvent> { + using OperationEventList = Common::IntrusiveListBaseTraits<OperationEvent>::ListType; + + OperationEvent(Core::System& system); + ~OperationEvent(); + + KernelHelpers::ServiceContext m_ctx; + Kernel::KEvent* m_event{}; +}; + +constexpr inline std::chrono::nanoseconds ConvertToTimeSpan(s64 ticks) { + constexpr auto one_second_ns{ + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1)).count()}; + + constexpr s64 max{Common::WallClock::CNTFRQ * + (std::numeric_limits<s64>::max() / one_second_ns)}; + + if (ticks > max) { + return std::chrono::nanoseconds(std::numeric_limits<s64>::max()); + } else if (ticks < -max) { + return std::chrono::nanoseconds(std::numeric_limits<s64>::min()); + } + + auto a{ticks / Common::WallClock::CNTFRQ * one_second_ns}; + auto b{((ticks % Common::WallClock::CNTFRQ) * one_second_ns) / Common::WallClock::CNTFRQ}; + + return std::chrono::nanoseconds(a + b); +} + +constexpr inline Result GetSpanBetweenTimePoints(s64* out_seconds, 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<s64>::max(), + ResultOverflow); + R_UNLESS(a.time_point < 0 || b.time_point >= a.time_point + std::numeric_limits<s64>::min(), + ResultOverflow); + + *out_seconds = b.time_point - a.time_point; + R_SUCCEED(); +} + +} // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/errors.h b/src/core/hle/service/psc/time/errors.h new file mode 100644 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 100644 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 100644 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 100644 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 <mutex> + +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/kernel_helpers.h" + +namespace Core { +class System; +} + +namespace Service::PSC::Time { + +class PowerStateRequestManager { +public: + explicit PowerStateRequestManager(Core::System& system); + ~PowerStateRequestManager(); + + Kernel::KReadableEvent& GetReadableEvent() { + return m_event->GetReadableEvent(); + } + + void UpdatePendingPowerStateRequestPriority(u32 priority); + void SignalPowerStateRequestAvailability(); + bool GetAndClearPowerStateRequest(u32& out_priority); + +private: + Core::System& m_system; + KernelHelpers::ServiceContext m_ctx; + + Kernel::KEvent* m_event{}; + bool m_has_pending_request{}; + u32 m_pending_request_priority{}; + bool m_has_available_request{}; + u32 m_available_request_priority{}; + std::mutex m_mutex; +}; + +} // namespace Service::PSC::Time 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 100644 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 100644 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<IPowerStateRequestHandler> { +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 100644 index 000000000..60820aa9b --- /dev/null +++ b/src/core/hle/service/psc/time/service_manager.cpp @@ -0,0 +1,494 @@ +// 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<TimeManager> time, + ServerManager* server_manager) + : ServiceFramework{system_, "time:m"}, m_system{system}, m_time{std::move(time)}, + m_server_manager{*server_manager}, + m_local_system_clock{m_time->m_standard_local_system_clock}, + m_user_system_clock{m_time->m_standard_user_system_clock}, + m_network_system_clock{m_time->m_standard_network_system_clock}, + m_steady_clock{m_time->m_standard_steady_clock}, m_time_zone{m_time->m_time_zone}, + m_ephemeral_network_clock{m_time->m_ephemeral_network_clock}, + m_shared_memory{m_time->m_shared_memory}, m_alarms{m_time->m_alarms}, + m_local_system_context_writer{m_time->m_local_system_clock_context_writer}, + m_network_system_context_writer{m_time->m_network_system_clock_context_writer}, + m_ephemeral_system_context_writer{m_time->m_ephemeral_network_clock_context_writer}, + m_local_operation{m_system}, m_network_operation{m_system}, m_ephemeral_operation{m_system} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &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<StaticService>( + m_system, StaticServiceSetupInfo{0, 0, 1, 0, 0, 0}, m_time, "time:s")); + m_server_manager.RegisterNamedService("time:p", + std::make_shared<IPowerStateRequestHandler>( + m_system, m_time->m_power_state_request_manager)); + } +} + +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<StaticService> service{}; + auto res = GetStaticServiceAsUser(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<StaticService>(std::move(service)); +} + +void ServiceManager::Handle_GetStaticServiceAsAdmin(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<StaticService> service{}; + auto res = GetStaticServiceAsAdmin(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<StaticService>(std::move(service)); +} + +void ServiceManager::Handle_GetStaticServiceAsRepair(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<StaticService> service{}; + auto res = GetStaticServiceAsRepair(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<StaticService>(std::move(service)); +} + +void ServiceManager::Handle_GetStaticServiceAsServiceManager(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<StaticService> service{}; + auto res = GetStaticServiceAsServiceManager(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<StaticService>(std::move(service)); +} + +void ServiceManager::Handle_SetupStandardSteadyClockCore(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + struct Parameters { + bool reset_detected; + Common::UUID clock_source_id; + s64 rtc_offset; + s64 internal_offset; + s64 test_offset; + }; + static_assert(sizeof(Parameters) == 0x30); + + IPC::RequestParser rp{ctx}; + auto params{rp.PopRaw<Parameters>()}; + + auto res = SetupStandardSteadyClockCore(params.clock_source_id, params.rtc_offset, + params.internal_offset, params.test_offset, + params.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<SystemClockContext>()}; + auto time{rp.Pop<s64>()}; + + 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<SystemClockContext>()}; + auto accuracy{rp.Pop<s64>()}; + + auto res = SetupStandardNetworkSystemClockCore(context, accuracy); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ServiceManager::Handle_SetupStandardUserSystemClockCore(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + struct Parameters { + bool automatic_correction; + SteadyClockTimePoint time_point; + }; + static_assert(sizeof(Parameters) == 0x20); + + IPC::RequestParser rp{ctx}; + auto params{rp.PopRaw<Parameters>()}; + + auto res = SetupStandardUserSystemClockCore(params.time_point, params.automatic_correction); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void ServiceManager::Handle_SetupTimeZoneServiceCore(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + struct Parameters { + u32 location_count; + LocationName name; + SteadyClockTimePoint time_point; + RuleVersion rule_version; + }; + static_assert(sizeof(Parameters) == 0x50); + + IPC::RequestParser rp{ctx}; + auto params{rp.PopRaw<Parameters>()}; + + auto rule_buffer{ctx.ReadBuffer()}; + + auto res = SetupTimeZoneServiceCore(params.name, params.time_point, params.rule_version, + params.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<s64>()}; + + 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); + + struct OutParameters { + bool is_valid; + AlarmInfo alarm_info; + s64 time; + }; + static_assert(sizeof(OutParameters) == 0x20); + + OutParameters out_params{ + .is_valid = is_valid, + .alarm_info = alarm_info, + .time = time, + }; + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(OutParameters) / sizeof(u32)}; + rb.Push(res); + rb.PushRaw<OutParameters>(out_params); +} + +// =============================== Implementations =========================== + +Result ServiceManager::GetStaticService(std::shared_ptr<StaticService>& out_service, + StaticServiceSetupInfo setup_info, const char* name) { + out_service = std::make_shared<StaticService>(m_system, setup_info, m_time, name); + R_SUCCEED(); +} + +Result ServiceManager::GetStaticServiceAsUser(std::shared_ptr<StaticService>& out_service) { + R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{0, 0, 0, 0, 0, 0}, "time:u")); +} + +Result ServiceManager::GetStaticServiceAsAdmin(std::shared_ptr<StaticService>& out_service) { + R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{1, 1, 0, 1, 0, 0}, "time:a")); +} + +Result ServiceManager::GetStaticServiceAsRepair(std::shared_ptr<StaticService>& out_service) { + R_RETURN(GetStaticService(out_service, StaticServiceSetupInfo{0, 0, 0, 0, 1, 0}, "time:r")); +} + +Result ServiceManager::GetStaticServiceAsServiceManager( + std::shared_ptr<StaticService>& 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 clock to the local clock on first boot + // to avoid it being stuck at 0. + if (context == Service::PSC::Time::SystemClockContext{}) { + 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! The user clock should be updated independently, from the ntc service + // and maybe elsewhere. We do not do that, so fix the clock to the local clock on first boot + // to avoid it being stuck at 0. + if (time_point == Service::PSC::Time::SteadyClockTimePoint{}) { + 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<const u8> 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 100644 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 <list> +#include <memory> + +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/psc/time/common.h" +#include "core/hle/service/psc/time/manager.h" +#include "core/hle/service/server_manager.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Kernel { +class KReadableEvent; +} + +namespace Service::PSC::Time { +class StaticService; + +class ServiceManager final : public ServiceFramework<ServiceManager> { +public: + explicit ServiceManager(Core::System& system, std::shared_ptr<TimeManager> time, + ServerManager* server_manager); + ~ServiceManager() override = default; + + Result GetStaticServiceAsUser(std::shared_ptr<StaticService>& out_service); + Result GetStaticServiceAsAdmin(std::shared_ptr<StaticService>& out_service); + Result GetStaticServiceAsRepair(std::shared_ptr<StaticService>& out_service); + Result GetStaticServiceAsServiceManager(std::shared_ptr<StaticService>& 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<const u8> 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<StaticService>& 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<TimeManager> m_time; + ServerManager& m_server_manager; + bool m_is_s_and_p_setup{}; + StandardLocalSystemClockCore& m_local_system_clock; + StandardUserSystemClockCore& m_user_system_clock; + StandardNetworkSystemClockCore& m_network_system_clock; + StandardSteadyClockCore& m_steady_clock; + TimeZone& m_time_zone; + EphemeralNetworkSystemClockCore& m_ephemeral_network_clock; + SharedMemory& m_shared_memory; + Alarms& m_alarms; + LocalSystemClockContextWriter& m_local_system_context_writer; + NetworkSystemClockContextWriter& m_network_system_context_writer; + EphemeralNetworkSystemClockContextWriter& m_ephemeral_system_context_writer; + OperationEvent m_local_operation; + OperationEvent m_network_operation; + OperationEvent m_ephemeral_operation; +}; + +} // namespace Service::PSC::Time 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 100644 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 <typename T> +constexpr inline T ReadFromLockFreeAtomicType(const LockFreeAtomicType<T>* p) { + while (true) { + // Get the counter. + auto counter = p->m_counter; + + // Get the value. + auto value = p->m_value[counter % 2]; + + // Fence memory. + std::atomic_thread_fence(std::memory_order_acquire); + + // Check that the counter matches. + if (counter == p->m_counter) { + return value; + } + } +} + +template <typename T> +constexpr inline void WriteToLockFreeAtomicType(LockFreeAtomicType<T>* p, const T& value) { + // Get the current counter. + auto counter = p->m_counter; + + // Increment the counter. + ++counter; + + // Store the updated value. + p->m_value[counter % 2] = value; + + // Fence memory. + std::atomic_thread_fence(std::memory_order_release); + + // Set the updated counter. + p->m_counter = counter; +} +} // namespace + +SharedMemory::SharedMemory(Core::System& system) + : m_system{system}, m_k_shared_memory{m_system.Kernel().GetTimeSharedMem()}, + m_shared_memory_ptr{reinterpret_cast<SharedMemoryStruct*>(m_k_shared_memory.GetPointer())} { + std::memset(m_shared_memory_ptr, 0, sizeof(*m_shared_memory_ptr)); +} + +void SharedMemory::SetLocalSystemContext(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 100644 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 <array> + +#include "common/common_types.h" +#include "core/hle/service/psc/time/common.h" + +namespace Core { +class System; +} + +namespace Kernel { +class KSharedMemory; +} + +namespace Service::PSC::Time { + +template <typename T> +struct LockFreeAtomicType { + u32 m_counter; + std::array<T, 2> m_value; +}; + +struct SharedMemoryStruct { + LockFreeAtomicType<SteadyClockTimePoint> steady_time_points; + LockFreeAtomicType<SystemClockContext> local_system_clock_contexts; + LockFreeAtomicType<SystemClockContext> network_system_clock_contexts; + LockFreeAtomicType<bool> automatic_corrections; + LockFreeAtomicType<ContinuousAdjustmentTimePoint> continuous_adjustment_time_points; + std::array<char, 0xEB8> pad0148; +}; +static_assert(offsetof(SharedMemoryStruct, steady_time_points) == 0x0, + "steady_time_points are in the wrong place!"); +static_assert(offsetof(SharedMemoryStruct, local_system_clock_contexts) == 0x38, + "local_system_clock_contexts are in the wrong place!"); +static_assert(offsetof(SharedMemoryStruct, network_system_clock_contexts) == 0x80, + "network_system_clock_contexts are in the wrong place!"); +static_assert(offsetof(SharedMemoryStruct, automatic_corrections) == 0xC8, + "automatic_corrections are in the wrong place!"); +static_assert(offsetof(SharedMemoryStruct, continuous_adjustment_time_points) == 0xD0, + "continuous_adjustment_time_points are in the wrong place!"); +static_assert(sizeof(SharedMemoryStruct) == 0x1000, + "Time's SharedMemoryStruct has the wrong size!"); +static_assert(std::is_trivial_v<SharedMemoryStruct>); + +class SharedMemory { +public: + explicit SharedMemory(Core::System& system); + + Kernel::KSharedMemory& GetKSharedMemory() { + return m_k_shared_memory; + } + + void SetLocalSystemContext(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 100644 index 000000000..6f8cf3f88 --- /dev/null +++ b/src/core/hle/service/psc/time/static.cpp @@ -0,0 +1,500 @@ +// 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<TimeManager> time, const char* name) + : ServiceFramework{system_, name}, m_system{system}, m_setup_info{setup_info}, m_time{time}, + m_local_system_clock{m_time->m_standard_local_system_clock}, + m_user_system_clock{m_time->m_standard_user_system_clock}, + m_network_system_clock{m_time->m_standard_network_system_clock}, + m_time_zone{m_time->m_time_zone}, + m_ephemeral_network_clock{m_time->m_ephemeral_network_clock}, m_shared_memory{ + m_time->m_shared_memory} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &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<SystemClock> service{}; + auto res = GetStandardUserSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<SystemClock>(std::move(service)); +} + +void StaticService::Handle_GetStandardNetworkSystemClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<SystemClock> service{}; + auto res = GetStandardNetworkSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<SystemClock>(std::move(service)); +} + +void StaticService::Handle_GetStandardSteadyClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<SteadyClock> 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<TimeZoneService> 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<SystemClock> service{}; + auto res = GetStandardLocalSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<SystemClock>(std::move(service)); +} + +void StaticService::Handle_GetEphemeralNetworkSystemClock(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + std::shared_ptr<SystemClock> service{}; + auto res = GetEphemeralNetworkSystemClock(service); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface<SystemClock>(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<bool>(is_enabled); +} + +void StaticService::Handle_SetStandardUserSystemClockAutomaticCorrectionEnabled( + HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto automatic_correction{rp.Pop<bool>()}; + + 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<bool>(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<SteadyClockTimePoint>(time_point); +} + +void StaticService::Handle_CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto context{rp.PopRaw<SystemClockContext>()}; + + s64 time{}; + auto res = CalculateMonotonicSystemClockBaseTimePoint(time, context); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push<s64>(time); +} + +void StaticService::Handle_GetClockSnapshot(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto type{rp.PopEnum<TimeType>()}; + + 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 clock_type{rp.PopEnum<TimeType>()}; + [[maybe_unused]] auto alignment{rp.Pop<u32>()}; + auto user_context{rp.PopRaw<SystemClockContext>()}; + auto network_context{rp.PopRaw<SystemClockContext>()}; + + 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<SystemClock>& out_service) { + out_service = std::make_shared<SystemClock>(m_system, m_user_system_clock, + m_setup_info.can_write_user_clock, + m_setup_info.can_write_uninitialized_clock); + R_SUCCEED(); +} + +Result StaticService::GetStandardNetworkSystemClock(std::shared_ptr<SystemClock>& out_service) { + out_service = std::make_shared<SystemClock>(m_system, m_network_system_clock, + m_setup_info.can_write_network_clock, + m_setup_info.can_write_uninitialized_clock); + R_SUCCEED(); +} + +Result StaticService::GetStandardSteadyClock(std::shared_ptr<SteadyClock>& out_service) { + out_service = + std::make_shared<SteadyClock>(m_system, m_time, m_setup_info.can_write_steady_clock, + m_setup_info.can_write_uninitialized_clock); + R_SUCCEED(); +} + +Result StaticService::GetTimeZoneService(std::shared_ptr<TimeZoneService>& out_service) { + out_service = + std::make_shared<TimeZoneService>(m_system, m_time->m_standard_steady_clock, m_time_zone, + m_setup_info.can_write_timezone_device_location); + R_SUCCEED(); +} + +Result StaticService::GetStandardLocalSystemClock(std::shared_ptr<SystemClock>& out_service) { + out_service = std::make_shared<SystemClock>(m_system, m_local_system_clock, + m_setup_info.can_write_local_clock, + m_setup_info.can_write_uninitialized_clock); + R_SUCCEED(); +} + +Result StaticService::GetEphemeralNetworkSystemClock(std::shared_ptr<SystemClock>& out_service) { + out_service = std::make_shared<SystemClock>(m_system, m_ephemeral_network_clock, + m_setup_info.can_write_network_clock, + m_setup_info.can_write_uninitialized_clock); + R_SUCCEED(); +} + +Result StaticService::GetSharedMemoryNativeHandle(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::nanoseconds>(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); + + if (a.user_context == b.user_context || + !a.user_context.steady_time_point.IdMatches(b.user_context.steady_time_point)) { + out_time = 0; + R_SUCCEED(); + } + + if (!a.is_automatic_correction_enabled || !b.is_automatic_correction_enabled) { + out_time = std::chrono::duration_cast<std::chrono::nanoseconds>(diff_s).count(); + R_SUCCEED(); + } + + if (a.network_context.steady_time_point.IdMatches(a.steady_clock_time_point) || + b.network_context.steady_time_point.IdMatches(b.steady_clock_time_point)) { + out_time = 0; + R_SUCCEED(); + } + + out_time = std::chrono::duration_cast<std::chrono::nanoseconds>(diff_s).count(); + 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::nanoseconds>(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 100644 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<StaticService> { +public: + explicit StaticService(Core::System& system, StaticServiceSetupInfo setup_info, + std::shared_ptr<TimeManager> time, const char* name); + + ~StaticService() override = default; + + Result GetStandardUserSystemClock(std::shared_ptr<SystemClock>& out_service); + Result GetStandardNetworkSystemClock(std::shared_ptr<SystemClock>& out_service); + Result GetStandardSteadyClock(std::shared_ptr<SteadyClock>& out_service); + Result GetTimeZoneService(std::shared_ptr<TimeZoneService>& out_service); + Result GetStandardLocalSystemClock(std::shared_ptr<SystemClock>& out_service); + Result GetEphemeralNetworkSystemClock(std::shared_ptr<SystemClock>& 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<TimeManager> m_time; + StandardLocalSystemClockCore& m_local_system_clock; + StandardUserSystemClockCore& m_user_system_clock; + StandardNetworkSystemClockCore& m_network_system_clock; + TimeZone& m_time_zone; + EphemeralNetworkSystemClockCore& m_ephemeral_network_clock; + SharedMemory& m_shared_memory; +}; + +} // namespace Service::PSC::Time 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 100644 index 000000000..1ed5c7679 --- /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<TimeManager> 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"}, + {2, &SteadyClock::Handle_GetTestOffset, "GetTestOffset"}, + {3, &SteadyClock::Handle_SetTestOffset, "SetTestOffset"}, + {100, &SteadyClock::Handle_GetRtcValue, "GetRtcValue"}, + {101, &SteadyClock::Handle_IsRtcResetDetected, "IsRtcResetDetected"}, + {102, &SteadyClock::Handle_GetSetupResultValue, "GetSetupResultValue"}, + {200, &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<SteadyClockTimePoint>(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<s64>()}; + + 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 100644 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<SteadyClock> { +public: + explicit SteadyClock(Core::System& system, std::shared_ptr<TimeManager> 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 100644 index 000000000..13d2f1d11 --- /dev/null +++ b/src/core/hle/service/psc/time/system_clock.cpp @@ -0,0 +1,127 @@ +// 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} { + // 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<s64>(time); +} + +void SystemClock::Handle_SetCurrentTime(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto time{rp.Pop<s64>()}; + + 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<SystemClockContext>(context); +} + +void SystemClock::Handle_SetSystemClockContext(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto context{rp.PopRaw<SystemClockContext>()}; + + 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) { + if (!m_operation_event) { + m_operation_event = std::make_unique<OperationEvent>(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 100644 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<SystemClock> { +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<OperationEvent> 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 100644 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<s32>(Tz::TZ_MAX_TYPES) || + rule.timecnt > static_cast<s32>(Tz::TZ_MAX_TIMES) || + rule.charcnt > static_cast<s32>(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<s32>(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<const u8> 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<const u8> binary) { + std::scoped_lock l{m_mutex}; + R_RETURN(ParseBinaryImpl(out_rule, binary)); +} + +Result TimeZone::ToPosixTime(u32& out_count, std::span<s64, 2> 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<s64, 2> 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<const u8> 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_t>(time)}; + if (Tz::localtime_rz(&calendar_internal, &rule, &time_tmp)) { + R_RETURN(ResultOverflow); + } + + out_calendar_time.year = static_cast<s16>(calendar_internal.tm_year + 1900); + out_calendar_time.month = static_cast<s8>(calendar_internal.tm_mon + 1); + out_calendar_time.day = static_cast<s8>(calendar_internal.tm_mday); + out_calendar_time.hour = static_cast<s8>(calendar_internal.tm_hour); + out_calendar_time.minute = static_cast<s8>(calendar_internal.tm_min); + out_calendar_time.second = static_cast<s8>(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<s64, 2> 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<s64>(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 100644 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 <mutex> +#include <span> + +#include <tz/tz.h> +#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<const u8> binary); + Result ParseBinaryInto(Tz::Rule& out_rule, std::span<const u8> binary); + Result ToPosixTime(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count, + CalendarTime& calendar, Tz::Rule& rule); + Result ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> out_times, u32 out_times_count, + CalendarTime& calendar); + +private: + Result ParseBinaryImpl(Tz::Rule& out_rule, std::span<const u8> binary); + Result ToCalendarTimeImpl(CalendarTime& out_calendar_time, + CalendarAdditionalInfo& out_additional_info, s64 time, + Tz::Rule& rule); + Result ToPosixTimeImpl(u32& out_count, std::span<s64, 2> 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 100644 index 000000000..e304c8387 --- /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 <tz/tz.h> +#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<LocationName>(name); +} + +void TimeZoneService::Handle_SetDeviceLocationName(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + [[maybe_unused]] auto name{rp.PopRaw<LocationName>()}; + + 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<RuleVersion>(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<LocationName>(name); + rb.PushRaw<SteadyClockTimePoint>(time_point); +} + +void TimeZoneService::Handle_SetDeviceLocationNameWithTimeZoneRule(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto name{rp.PopRaw<LocationName>()}; + + 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<s64>()}; + + 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<CalendarTime>(calendar_time); + rb.PushRaw<CalendarAdditionalInfo>(additional_info); +} + +void TimeZoneService::Handle_ToCalendarTimeWithMyRule(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto time{rp.Pop<s64>()}; + + 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<CalendarTime>(calendar_time); + rb.PushRaw<CalendarAdditionalInfo>(additional_info); +} + +void TimeZoneService::Handle_ToPosixTime(HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called."); + + IPC::RequestParser rp{ctx}; + auto calendar{rp.PopRaw<CalendarTime>()}; + + auto binary{ctx.ReadBuffer()}; + + Tz::Rule rule{}; + std::memcpy(&rule, binary.data(), sizeof(Tz::Rule)); + + u32 count{}; + std::array<s64, 2> times{}; + u32 times_count{static_cast<u32>(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<CalendarTime>()}; + + u32 count{}; + std::array<s64, 2> times{}; + u32 times_count{static_cast<u32>(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_location_name) { + R_RETURN(m_time_zone.GetLocationName(out_location_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<const u8> 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<const u8> 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<s64, 2> 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<s64, 2> 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 100644 index 000000000..074c1d4ae --- /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<TimeZoneService> { +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_location_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<const u8> binary); + Result ParseTimeZoneBinary(Tz::Rule& out_rule, std::span<const u8> 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<s64, 2> out_times, u32 out_times_count, + CalendarTime& calendar_time, Tz::Rule& rule); + Result ToPosixTimeWithMyRule(u32& out_count, std::span<s64, 2> 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/ro/ro.cpp b/src/core/hle/service/ro/ro.cpp index f0658bb5d..51196170a 100644 --- a/src/core/hle/service/ro/ro.cpp +++ b/src/core/hle/service/ro/ro.cpp @@ -6,13 +6,13 @@ #include "common/scope_exit.h" #include "core/hle/kernel/k_process.h" +#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ro/ro.h" #include "core/hle/service/ro/ro_nro_utils.h" #include "core/hle/service/ro/ro_results.h" #include "core/hle/service/ro/ro_types.h" #include "core/hle/service/server_manager.h" -#include "core/hle/service/service.h" namespace Service::RO { @@ -500,46 +500,64 @@ private: } }; -class RoInterface { +class RoInterface : public ServiceFramework<RoInterface> { public: - explicit RoInterface(std::shared_ptr<RoContext> ro, NrrKind nrr_kind) - : m_ro(ro), m_context_id(InvalidContextId), m_nrr_kind(nrr_kind) {} + explicit RoInterface(Core::System& system_, const char* name_, std::shared_ptr<RoContext> ro, + NrrKind nrr_kind) + : ServiceFramework{system_, name_}, m_ro(ro), m_context_id(InvalidContextId), + m_nrr_kind(nrr_kind) { + + // clang-format off + static const FunctionInfo functions[] = { + {0, C<&RoInterface::MapManualLoadModuleMemory>, "MapManualLoadModuleMemory"}, + {1, C<&RoInterface::UnmapManualLoadModuleMemory>, "UnmapManualLoadModuleMemory"}, + {2, C<&RoInterface::RegisterModuleInfo>, "RegisterModuleInfo"}, + {3, C<&RoInterface::UnregisterModuleInfo>, "UnregisterModuleInfo"}, + {4, C<&RoInterface::RegisterProcessHandle>, "RegisterProcessHandle"}, + {10, C<&RoInterface::RegisterProcessModuleInfo>, "RegisterProcessModuleInfo"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + ~RoInterface() { m_ro->UnregisterProcess(m_context_id); } - Result MapManualLoadModuleMemory(u64* out_load_address, u64 client_pid, u64 nro_address, - u64 nro_size, u64 bss_address, u64 bss_size) { - R_TRY(m_ro->ValidateProcess(m_context_id, client_pid)); - R_RETURN(m_ro->MapManualLoadModuleMemory(out_load_address, m_context_id, nro_address, + Result MapManualLoadModuleMemory(Out<u64> out_load_address, ClientProcessId client_pid, + u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { + R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid)); + R_RETURN(m_ro->MapManualLoadModuleMemory(out_load_address.Get(), m_context_id, nro_address, nro_size, bss_address, bss_size)); } - Result UnmapManualLoadModuleMemory(u64 client_pid, u64 nro_address) { - R_TRY(m_ro->ValidateProcess(m_context_id, client_pid)); + Result UnmapManualLoadModuleMemory(ClientProcessId client_pid, u64 nro_address) { + R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid)); R_RETURN(m_ro->UnmapManualLoadModuleMemory(m_context_id, nro_address)); } - Result RegisterModuleInfo(u64 client_pid, u64 nrr_address, u64 nrr_size) { - R_TRY(m_ro->ValidateProcess(m_context_id, client_pid)); + Result RegisterModuleInfo(ClientProcessId client_pid, u64 nrr_address, u64 nrr_size) { + R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid)); R_RETURN( m_ro->RegisterModuleInfo(m_context_id, nrr_address, nrr_size, NrrKind::User, true)); } - Result UnregisterModuleInfo(u64 client_pid, u64 nrr_address) { - R_TRY(m_ro->ValidateProcess(m_context_id, client_pid)); + Result UnregisterModuleInfo(ClientProcessId client_pid, u64 nrr_address) { + R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid)); R_RETURN(m_ro->UnregisterModuleInfo(m_context_id, nrr_address)); } - Result RegisterProcessHandle(u64 client_pid, Kernel::KProcess* process) { + Result RegisterProcessHandle(ClientProcessId client_pid, + InCopyHandle<Kernel::KProcess>& process) { // Register the process. - R_RETURN(m_ro->RegisterProcess(std::addressof(m_context_id), process, client_pid)); + R_RETURN(m_ro->RegisterProcess(std::addressof(m_context_id), process.Get(), *client_pid)); } - Result RegisterProcessModuleInfo(u64 client_pid, u64 nrr_address, u64 nrr_size, - Kernel::KProcess* process) { + Result RegisterProcessModuleInfo(ClientProcessId client_pid, u64 nrr_address, u64 nrr_size, + InCopyHandle<Kernel::KProcess>& process) { // Validate the process. - R_TRY(m_ro->ValidateProcess(m_context_id, client_pid)); + R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid)); // Register the module. R_RETURN(m_ro->RegisterModuleInfo(m_context_id, nrr_address, nrr_size, m_nrr_kind, @@ -552,137 +570,6 @@ private: NrrKind m_nrr_kind{}; }; -class IRoInterface : public ServiceFramework<IRoInterface> { -public: - explicit IRoInterface(Core::System& system_, const char* name_, std::shared_ptr<RoContext> ro, - NrrKind nrr_kind) - : ServiceFramework{system_, name_}, interface { - ro, nrr_kind - } { - // clang-format off - static const FunctionInfo functions[] = { - {0, &IRoInterface::MapManualLoadModuleMemory, "MapManualLoadModuleMemory"}, - {1, &IRoInterface::UnmapManualLoadModuleMemory, "UnmapManualLoadModuleMemory"}, - {2, &IRoInterface::RegisterModuleInfo, "RegisterModuleInfo"}, - {3, &IRoInterface::UnregisterModuleInfo, "UnregisterModuleInfo"}, - {4, &IRoInterface::RegisterProcessHandle, "RegisterProcessHandle"}, - {10, &IRoInterface::RegisterProcessModuleInfo, "RegisterProcessModuleInfo"}, - }; - // clang-format on - - RegisterHandlers(functions); - } - -private: - void MapManualLoadModuleMemory(HLERequestContext& ctx) { - LOG_DEBUG(Service_LDR, "(called)"); - - struct InputParameters { - u64 client_pid; - u64 nro_address; - u64 nro_size; - u64 bss_address; - u64 bss_size; - }; - - IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw<InputParameters>(); - - u64 load_address = 0; - auto result = interface.MapManualLoadModuleMemory(&load_address, ctx.GetPID(), - params.nro_address, params.nro_size, - params.bss_address, params.bss_size); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(result); - rb.Push(load_address); - } - - void UnmapManualLoadModuleMemory(HLERequestContext& ctx) { - LOG_DEBUG(Service_LDR, "(called)"); - - struct InputParameters { - u64 client_pid; - u64 nro_address; - }; - - IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw<InputParameters>(); - auto result = interface.UnmapManualLoadModuleMemory(ctx.GetPID(), params.nro_address); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - } - - void RegisterModuleInfo(HLERequestContext& ctx) { - LOG_DEBUG(Service_LDR, "(called)"); - - struct InputParameters { - u64 client_pid; - u64 nrr_address; - u64 nrr_size; - }; - - IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw<InputParameters>(); - auto result = - interface.RegisterModuleInfo(ctx.GetPID(), params.nrr_address, params.nrr_size); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - } - - void UnregisterModuleInfo(HLERequestContext& ctx) { - LOG_DEBUG(Service_LDR, "(called)"); - - struct InputParameters { - u64 client_pid; - u64 nrr_address; - }; - - IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw<InputParameters>(); - auto result = interface.UnregisterModuleInfo(ctx.GetPID(), params.nrr_address); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - } - - void RegisterProcessHandle(HLERequestContext& ctx) { - LOG_DEBUG(Service_LDR, "(called)"); - - auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(ctx.GetCopyHandle(0)); - auto client_pid = ctx.GetPID(); - auto result = interface.RegisterProcessHandle(client_pid, process.GetPointerUnsafe()); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - } - - void RegisterProcessModuleInfo(HLERequestContext& ctx) { - LOG_DEBUG(Service_LDR, "(called)"); - - struct InputParameters { - u64 client_pid; - u64 nrr_address; - u64 nrr_size; - }; - - IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw<InputParameters>(); - auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(ctx.GetCopyHandle(0)); - - auto client_pid = ctx.GetPID(); - auto result = interface.RegisterProcessModuleInfo( - client_pid, params.nrr_address, params.nrr_size, process.GetPointerUnsafe()); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - } - - RoInterface interface; -}; - } // namespace void LoopProcess(Core::System& system) { @@ -691,11 +578,11 @@ void LoopProcess(Core::System& system) { auto ro = std::make_shared<RoContext>(); const auto RoInterfaceFactoryForUser = [&, ro] { - return std::make_shared<IRoInterface>(system, "ldr:ro", ro, NrrKind::User); + return std::make_shared<RoInterface>(system, "ldr:ro", ro, NrrKind::User); }; const auto RoInterfaceFactoryForJitPlugin = [&, ro] { - return std::make_shared<IRoInterface>(system, "ro:1", ro, NrrKind::JitPlugin); + return std::make_shared<RoInterface>(system, "ro:1", ro, NrrKind::JitPlugin); }; server_manager->RegisterNamedService("ldr:ro", std::move(RoInterfaceFactoryForUser)); diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 39124c5fd..06cbad268 100644 --- 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::ServiceManager>& 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::ServiceManager>& 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/service.h b/src/core/hle/service/service.h index d539ed0f4..22d1343d5 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -206,6 +206,22 @@ protected: RegisterHandlersBaseTipc(functions, n); } +protected: + template <bool Domain, auto F> + void CmifReplyWrap(HLERequestContext& ctx); + + /** + * Wraps the template pointer-to-member function for use in a domain session. + */ + template <auto F> + static constexpr HandlerFnP<Self> D = &Self::template CmifReplyWrap<true, F>; + + /** + * Wraps the template pointer-to-member function for use in a non-domain session. + */ + template <auto F> + static constexpr HandlerFnP<Self> C = &Self::template CmifReplyWrap<false, F>; + private: /** * This function is used to allow invocation of pointers to handlers stored in the base class diff --git a/src/core/hle/service/set/private_settings.h b/src/core/hle/service/set/private_settings.h new file mode 100644 index 000000000..b02291ce7 --- /dev/null +++ b/src/core/hle/service/set/private_settings.h @@ -0,0 +1,72 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> + +#include "common/bit_field.h" +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/uuid.h" +#include "core/hle/service/psc/time/common.h" + +namespace Service::Set { + +/// This is nn::settings::system::InitialLaunchFlag +struct InitialLaunchFlag { + union { + u32 raw{}; + + BitField<0, 1, u32> InitialLaunchCompletionFlag; + BitField<8, 1, u32> InitialLaunchUserAdditionFlag; + BitField<16, 1, u32> InitialLaunchTimestampFlag; + }; +}; +static_assert(sizeof(InitialLaunchFlag) == 4, "InitialLaunchFlag is an invalid size"); + +/// This is nn::settings::system::InitialLaunchSettings +struct InitialLaunchSettings { + InitialLaunchFlag flags; + INSERT_PADDING_BYTES(0x4); + Service::PSC::Time::SteadyClockTimePoint timestamp; +}; +static_assert(sizeof(InitialLaunchSettings) == 0x20, "InitialLaunchSettings is incorrect size"); + +#pragma pack(push, 4) +struct InitialLaunchSettingsPacked { + InitialLaunchFlag flags; + Service::PSC::Time::SteadyClockTimePoint timestamp; +}; +#pragma pack(pop) +static_assert(sizeof(InitialLaunchSettingsPacked) == 0x1C, + "InitialLaunchSettingsPacked is incorrect size"); + +struct PrivateSettings { + std::array<u8, 0x10> reserved_00; + + // nn::settings::system::InitialLaunchSettings + InitialLaunchSettings initial_launch_settings; + + std::array<u8, 0x20> reserved_30; + + Common::UUID external_clock_source_id; + s64 shutdown_rtc_value; + s64 external_steady_clock_internal_offset; + + std::array<u8, 0x60> reserved_70; + + // nn::settings::system::PlatformRegion + std::array<u8, 0x4> platform_region; + + std::array<u8, 0x4> reserved_D4; +}; +static_assert(offsetof(PrivateSettings, initial_launch_settings) == 0x10); +static_assert(offsetof(PrivateSettings, external_clock_source_id) == 0x50); +static_assert(offsetof(PrivateSettings, reserved_70) == 0x70); +static_assert(offsetof(PrivateSettings, platform_region) == 0xD0); +static_assert(sizeof(PrivateSettings) == 0xD8, "PrivateSettings has the wrong size!"); + +PrivateSettings DefaultPrivateSettings(); + +} // namespace Service::Set diff --git a/src/core/hle/service/set/setting_formats/private_settings.h b/src/core/hle/service/set/setting_formats/private_settings.h index 6c40f62e1..6579e95e0 100644 --- a/src/core/hle/service/set/setting_formats/private_settings.h +++ b/src/core/hle/service/set/setting_formats/private_settings.h @@ -8,7 +8,6 @@ #include "common/common_types.h" #include "common/uuid.h" #include "core/hle/service/set/settings_types.h" -#include "core/hle/service/time/clock_types.h" namespace Service::Set { diff --git a/src/core/hle/service/set/setting_formats/system_settings.cpp b/src/core/hle/service/set/setting_formats/system_settings.cpp index 66e57651e..88a305f03 100644 --- a/src/core/hle/service/set/setting_formats/system_settings.cpp +++ b/src/core/hle/service/set/setting_formats/system_settings.cpp @@ -45,11 +45,12 @@ SystemSettings DefaultSystemSettings() { }; settings.device_time_zone_location_name = {"UTC"}; - settings.user_system_clock_automatic_correction_enabled = false; + settings.user_system_clock_automatic_correction_enabled = true; settings.primary_album_storage = PrimaryAlbumStorage::SdCard; settings.battery_percentage_flag = true; settings.chinese_traditional_input_method = ChineseTraditionalInputMethod::Unknown0; + settings.vibration_master_volume = 1.0f; return settings; } diff --git a/src/core/hle/service/set/setting_formats/system_settings.h b/src/core/hle/service/set/setting_formats/system_settings.h index 14654f8b1..af5929fa9 100644 --- a/src/core/hle/service/set/setting_formats/system_settings.h +++ b/src/core/hle/service/set/setting_formats/system_settings.h @@ -12,7 +12,6 @@ #include "common/vector_math.h" #include "core/hle/service/set/setting_formats/private_settings.h" #include "core/hle/service/set/settings_types.h" -#include "core/hle/service/time/clock_types.h" namespace Service::Set { @@ -197,12 +196,14 @@ struct SystemSettings { std::array<u8, 0x2C> backlight_settings_mixed_up; INSERT_PADDING_BYTES(0x64); // Reserved - Service::Time::Clock::SystemClockContext user_system_clock_context; - Service::Time::Clock::SystemClockContext network_system_clock_context; + // nn::time::SystemClockContext + Service::PSC::Time::SystemClockContext user_system_clock_context; + Service::PSC::Time::SystemClockContext network_system_clock_context; bool user_system_clock_automatic_correction_enabled; INSERT_PADDING_BYTES(0x3); INSERT_PADDING_BYTES(0x4); // Reserved - Service::Time::Clock::SteadyClockTimePoint + // nn::time::SteadyClockTimePoint + Service::PSC::Time::SteadyClockTimePoint user_system_clock_automatic_correction_updated_time_point; INSERT_PADDING_BYTES(0x10); // Reserved @@ -280,9 +281,12 @@ struct SystemSettings { bool requires_run_repair_time_reviser; INSERT_PADDING_BYTES(0x6B); // Reserved - Service::Time::TimeZone::LocationName device_time_zone_location_name; + // nn::time::LocationName + Service::PSC::Time::LocationName device_time_zone_location_name; INSERT_PADDING_BYTES(0x4); // Reserved - Service::Time::Clock::SteadyClockTimePoint device_time_zone_location_updated_time; + // nn::time::SteadyClockTimePoint + Service::PSC::Time::SteadyClockTimePoint device_time_zone_location_updated_time; + INSERT_PADDING_BYTES(0xC0); // Reserved // nn::settings::system::PrimaryAlbumStorage diff --git a/src/core/hle/service/set/settings_types.h b/src/core/hle/service/set/settings_types.h index 4dee202d7..968425319 100644 --- a/src/core/hle/service/set/settings_types.h +++ b/src/core/hle/service/set/settings_types.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 { @@ -323,6 +323,15 @@ struct NotificationFlag { }; static_assert(sizeof(NotificationFlag) == 4, "NotificationFlag is an invalid size"); +struct PlatformConfig { + union { + u32 raw{}; + BitField<0, 1, u32> has_rail_interface; + BitField<1, 1, u32> has_sio_mcu; + }; +}; +static_assert(sizeof(PlatformConfig) == 0x4, "PlatformConfig is an invalid size"); + /// This is nn::settings::system::TvFlag struct TvFlag { union { @@ -365,7 +374,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"); @@ -398,14 +407,14 @@ static_assert(sizeof(HomeMenuScheme) == 0x14, "HomeMenuScheme is incorrect size" 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/system_settings_server.cpp b/src/core/hle/service/set/system_settings_server.cpp index 2e5785fed..e907b57b6 100644 --- a/src/core/hle/service/set/system_settings_server.cpp +++ b/src/core/hle/service/set/system_settings_server.cpp @@ -67,13 +67,13 @@ Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& 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); + FileSys::ResultInvalidArgument); } 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); + FileSys::ResultOutOfRange); } std::memcpy(&out_firmware, data.data(), sizeof(FirmwareVersionFormat)); @@ -123,8 +123,8 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_) {30, &ISystemSettingsServer::SetNotificationSettings, "SetNotificationSettings"}, {31, &ISystemSettingsServer::GetAccountNotificationSettings, "GetAccountNotificationSettings"}, {32, &ISystemSettingsServer::SetAccountNotificationSettings, "SetAccountNotificationSettings"}, - {35, nullptr, "GetVibrationMasterVolume"}, - {36, nullptr, "SetVibrationMasterVolume"}, + {35, &ISystemSettingsServer::GetVibrationMasterVolume, "GetVibrationMasterVolume"}, + {36, &ISystemSettingsServer::SetVibrationMasterVolume, "SetVibrationMasterVolume"}, {37, &ISystemSettingsServer::GetSettingsItemValueSize, "GetSettingsItemValueSize"}, {38, &ISystemSettingsServer::GetSettingsItemValue, "GetSettingsItemValue"}, {39, &ISystemSettingsServer::GetTvSettings, "GetTvSettings"}, @@ -133,10 +133,10 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_) {42, nullptr, "SetEdid"}, {43, nullptr, "GetAudioOutputMode"}, {44, nullptr, "SetAudioOutputMode"}, - {45, nullptr, "IsForceMuteOnHeadphoneRemoved"}, - {46, nullptr, "SetForceMuteOnHeadphoneRemoved"}, + {45, &ISystemSettingsServer::IsForceMuteOnHeadphoneRemoved, "IsForceMuteOnHeadphoneRemoved"}, + {46, &ISystemSettingsServer::SetForceMuteOnHeadphoneRemoved, "SetForceMuteOnHeadphoneRemoved"}, {47, &ISystemSettingsServer::GetQuestFlag, "GetQuestFlag"}, - {48, nullptr, "SetQuestFlag"}, + {48, &ISystemSettingsServer::SetQuestFlag, "SetQuestFlag"}, {49, nullptr, "GetDataDeletionSettings"}, {50, nullptr, "SetDataDeletionSettings"}, {51, nullptr, "GetInitialSystemAppletProgramId"}, @@ -152,7 +152,7 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_) {61, &ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionEnabled, "SetUserSystemClockAutomaticCorrectionEnabled"}, {62, &ISystemSettingsServer::GetDebugModeFlag, "GetDebugModeFlag"}, {63, &ISystemSettingsServer::GetPrimaryAlbumStorage, "GetPrimaryAlbumStorage"}, - {64, nullptr, "SetPrimaryAlbumStorage"}, + {64, &ISystemSettingsServer::SetPrimaryAlbumStorage, "SetPrimaryAlbumStorage"}, {65, nullptr, "GetUsb30EnableFlag"}, {66, nullptr, "SetUsb30EnableFlag"}, {67, nullptr, "GetBatteryLot"}, @@ -467,7 +467,7 @@ void ISystemSettingsServer::GetExternalSteadyClockSourceId(HLERequestContext& ct LOG_INFO(Service_SET, "called"); Common::UUID id{}; - auto res = GetExternalSteadyClockSourceId(id); + const auto res = GetExternalSteadyClockSourceId(id); IPC::ResponseBuilder rb{ctx, 2 + sizeof(Common::UUID) / sizeof(u32)}; rb.Push(res); @@ -478,9 +478,9 @@ void ISystemSettingsServer::SetExternalSteadyClockSourceId(HLERequestContext& ct LOG_INFO(Service_SET, "called"); IPC::RequestParser rp{ctx}; - auto id{rp.PopRaw<Common::UUID>()}; + const auto id{rp.PopRaw<Common::UUID>()}; - auto res = SetExternalSteadyClockSourceId(id); + const auto res = SetExternalSteadyClockSourceId(id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(res); @@ -489,11 +489,10 @@ void ISystemSettingsServer::SetExternalSteadyClockSourceId(HLERequestContext& ct void ISystemSettingsServer::GetUserSystemClockContext(HLERequestContext& ctx) { LOG_INFO(Service_SET, "called"); - Service::Time::Clock::SystemClockContext context{}; - auto res = GetUserSystemClockContext(context); + Service::PSC::Time::SystemClockContext context{}; + const auto res = GetUserSystemClockContext(context); - IPC::ResponseBuilder rb{ctx, - 2 + sizeof(Service::Time::Clock::SystemClockContext) / sizeof(u32)}; + IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::SystemClockContext) / sizeof(u32)}; rb.Push(res); rb.PushRaw(context); } @@ -502,9 +501,9 @@ void ISystemSettingsServer::SetUserSystemClockContext(HLERequestContext& ctx) { LOG_INFO(Service_SET, "called"); IPC::RequestParser rp{ctx}; - auto context{rp.PopRaw<Service::Time::Clock::SystemClockContext>()}; + const auto context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()}; - auto res = SetUserSystemClockContext(context); + const auto res = SetUserSystemClockContext(context); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(res); @@ -653,6 +652,29 @@ void ISystemSettingsServer::SetAccountNotificationSettings(HLERequestContext& ct rb.Push(ResultSuccess); } +void ISystemSettingsServer::GetVibrationMasterVolume(HLERequestContext& ctx) { + f32 vibration_master_volume = {}; + const auto result = GetVibrationMasterVolume(vibration_master_volume); + + LOG_INFO(Service_SET, "called, master_volume={}", vibration_master_volume); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(result); + rb.Push(vibration_master_volume); +} + +void ISystemSettingsServer::SetVibrationMasterVolume(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto vibration_master_volume = rp.PopRaw<f32>(); + + LOG_INFO(Service_SET, "called, elements={}", m_system_settings.vibration_master_volume); + + const auto result = SetVibrationMasterVolume(vibration_master_volume); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + // FIXME: implement support for the real system_settings.ini template <typename T> @@ -684,6 +706,8 @@ static Settings GetSettings() { ret["time"]["standard_user_clock_initial_year"] = ToBytes(s32{2023}); // HID + ret["hid"]["has_rail_interface"] = ToBytes(bool{true}); + ret["hid"]["has_sio_mcu"] = ToBytes(bool{true}); ret["hid_debug"]["enables_debugpad"] = ToBytes(bool{true}); ret["hid_debug"]["manages_devices"] = ToBytes(bool{true}); ret["hid_debug"]["manages_touch_ic_i2c"] = ToBytes(bool{true}); @@ -701,6 +725,9 @@ static Settings GetSettings() { // Settings ret["settings_debug"]["is_debug_mode_enabled"] = ToBytes(bool{false}); + // Error + ret["err"]["applet_auto_close"] = ToBytes(bool{false}); + return ret; } @@ -710,12 +737,12 @@ void ISystemSettingsServer::GetSettingsItemValueSize(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()}; + const std::string setting_category{Common::StringFromBuffer(setting_category_buf)}; // 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()}; + const std::string setting_name{Common::StringFromBuffer(setting_name_buf)}; auto settings{GetSettings()}; u64 response_size{0}; @@ -733,12 +760,12 @@ 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()}; + const std::string setting_category{Common::StringFromBuffer(setting_category_buf)}; // 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()}; + const std::string setting_name{Common::StringFromBuffer(setting_name_buf)}; std::vector<u8> value; auto response = GetSettingsItemValue(value, setting_category, setting_name); @@ -787,15 +814,25 @@ void ISystemSettingsServer::SetTvSettings(HLERequestContext& ctx) { rb.Push(ResultSuccess); } -void ISystemSettingsServer::GetDebugModeFlag(HLERequestContext& ctx) { - bool is_debug_mode_enabled = false; - GetSettingsItemValue<bool>(is_debug_mode_enabled, "settings_debug", "is_debug_mode_enabled"); - - LOG_DEBUG(Service_SET, "called, is_debug_mode_enabled={}", is_debug_mode_enabled); +void ISystemSettingsServer::IsForceMuteOnHeadphoneRemoved(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called, force_mute_on_headphone_removed={}", + m_system_settings.force_mute_on_headphone_removed); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(is_debug_mode_enabled); + rb.PushRaw(m_system_settings.force_mute_on_headphone_removed); +} + +void ISystemSettingsServer::SetForceMuteOnHeadphoneRemoved(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + m_system_settings.force_mute_on_headphone_removed = rp.PopRaw<bool>(); + SetSaveNeeded(); + + LOG_INFO(Service_SET, "called, force_mute_on_headphone_removed={}", + m_system_settings.force_mute_on_headphone_removed); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); } void ISystemSettingsServer::GetQuestFlag(HLERequestContext& ctx) { @@ -806,24 +843,35 @@ void ISystemSettingsServer::GetQuestFlag(HLERequestContext& ctx) { rb.PushEnum(m_system_settings.quest_flag); } +void ISystemSettingsServer::SetQuestFlag(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + m_system_settings.quest_flag = rp.PopEnum<QuestFlag>(); + SetSaveNeeded(); + + LOG_INFO(Service_SET, "called, quest_flag={}", m_system_settings.quest_flag); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void ISystemSettingsServer::GetDeviceTimeZoneLocationName(HLERequestContext& ctx) { LOG_INFO(Service_SET, "called"); - Service::Time::TimeZone::LocationName name{}; - auto res = GetDeviceTimeZoneLocationName(name); + Service::PSC::Time::LocationName name{}; + const auto res = GetDeviceTimeZoneLocationName(name); - IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::Time::TimeZone::LocationName) / sizeof(u32)}; + IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::LocationName) / sizeof(u32)}; rb.Push(res); - rb.PushRaw<Service::Time::TimeZone::LocationName>(name); + rb.PushRaw<Service::PSC::Time::LocationName>(name); } void ISystemSettingsServer::SetDeviceTimeZoneLocationName(HLERequestContext& ctx) { LOG_INFO(Service_SET, "called"); IPC::RequestParser rp{ctx}; - auto name{rp.PopRaw<Service::Time::TimeZone::LocationName>()}; + auto name{rp.PopRaw<Service::PSC::Time::LocationName>()}; - auto res = SetDeviceTimeZoneLocationName(name); + const auto res = SetDeviceTimeZoneLocationName(name); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(res); @@ -843,11 +891,10 @@ void ISystemSettingsServer::SetRegionCode(HLERequestContext& ctx) { void ISystemSettingsServer::GetNetworkSystemClockContext(HLERequestContext& ctx) { LOG_INFO(Service_SET, "called"); - Service::Time::Clock::SystemClockContext context{}; - auto res = GetNetworkSystemClockContext(context); + Service::PSC::Time::SystemClockContext context{}; + const auto res = GetNetworkSystemClockContext(context); - IPC::ResponseBuilder rb{ctx, - 2 + sizeof(Service::Time::Clock::SystemClockContext) / sizeof(u32)}; + IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::SystemClockContext) / sizeof(u32)}; rb.Push(res); rb.PushRaw(context); } @@ -856,9 +903,9 @@ void ISystemSettingsServer::SetNetworkSystemClockContext(HLERequestContext& ctx) LOG_INFO(Service_SET, "called"); IPC::RequestParser rp{ctx}; - auto context{rp.PopRaw<Service::Time::Clock::SystemClockContext>()}; + const auto context{rp.PopRaw<Service::PSC::Time::SystemClockContext>()}; - auto res = SetNetworkSystemClockContext(context); + const auto res = SetNetworkSystemClockContext(context); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(res); @@ -868,7 +915,7 @@ void ISystemSettingsServer::IsUserSystemClockAutomaticCorrectionEnabled(HLEReque LOG_INFO(Service_SET, "called"); bool enabled{}; - auto res = IsUserSystemClockAutomaticCorrectionEnabled(enabled); + const auto res = IsUserSystemClockAutomaticCorrectionEnabled(enabled); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(res); @@ -881,12 +928,23 @@ void ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionEnabled(HLERequ IPC::RequestParser rp{ctx}; auto enabled{rp.Pop<bool>()}; - auto res = SetUserSystemClockAutomaticCorrectionEnabled(enabled); + const auto res = SetUserSystemClockAutomaticCorrectionEnabled(enabled); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(res); } +void ISystemSettingsServer::GetDebugModeFlag(HLERequestContext& ctx) { + bool is_debug_mode_enabled = false; + GetSettingsItemValue<bool>(is_debug_mode_enabled, "settings_debug", "is_debug_mode_enabled"); + + LOG_DEBUG(Service_SET, "called, is_debug_mode_enabled={}", is_debug_mode_enabled); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(is_debug_mode_enabled); +} + void ISystemSettingsServer::GetPrimaryAlbumStorage(HLERequestContext& ctx) { LOG_INFO(Service_SET, "called, primary_album_storage={}", m_system_settings.primary_album_storage); @@ -896,6 +954,18 @@ void ISystemSettingsServer::GetPrimaryAlbumStorage(HLERequestContext& ctx) { rb.PushEnum(m_system_settings.primary_album_storage); } +void ISystemSettingsServer::SetPrimaryAlbumStorage(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + m_system_settings.primary_album_storage = rp.PopEnum<PrimaryAlbumStorage>(); + SetSaveNeeded(); + + LOG_INFO(Service_SET, "called, primary_album_storage={}", + m_system_settings.primary_album_storage); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void ISystemSettingsServer::GetNfcEnableFlag(HLERequestContext& ctx) { LOG_INFO(Service_SET, "called, nfc_enable_flag={}", m_system_settings.nfc_enable_flag); @@ -1074,7 +1144,7 @@ void ISystemSettingsServer::SetExternalSteadyClockInternalOffset(HLERequestConte IPC::RequestParser rp{ctx}; auto offset{rp.Pop<s64>()}; - auto res = SetExternalSteadyClockInternalOffset(offset); + const auto res = SetExternalSteadyClockInternalOffset(offset); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(res); @@ -1084,7 +1154,7 @@ void ISystemSettingsServer::GetExternalSteadyClockInternalOffset(HLERequestConte LOG_DEBUG(Service_SET, "called."); s64 offset{}; - auto res = GetExternalSteadyClockInternalOffset(offset); + const auto res = GetExternalSteadyClockInternalOffset(offset); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(res); @@ -1141,21 +1211,21 @@ void ISystemSettingsServer::GetKeyboardLayout(HLERequestContext& ctx) { void ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) { LOG_INFO(Service_SET, "called"); - Service::Time::Clock::SteadyClockTimePoint time_point{}; - auto res = GetDeviceTimeZoneLocationUpdatedTime(time_point); + Service::PSC::Time::SteadyClockTimePoint time_point{}; + const auto res = GetDeviceTimeZoneLocationUpdatedTime(time_point); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(res); - rb.PushRaw<Service::Time::Clock::SteadyClockTimePoint>(time_point); + rb.PushRaw<Service::PSC::Time::SteadyClockTimePoint>(time_point); } void ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime(HLERequestContext& ctx) { LOG_INFO(Service_SET, "called"); IPC::RequestParser rp{ctx}; - auto time_point{rp.PopRaw<Service::Time::Clock::SteadyClockTimePoint>()}; + auto time_point{rp.PopRaw<Service::PSC::Time::SteadyClockTimePoint>()}; - auto res = SetDeviceTimeZoneLocationUpdatedTime(time_point); + const auto res = SetDeviceTimeZoneLocationUpdatedTime(time_point); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(res); @@ -1165,12 +1235,12 @@ void ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime( HLERequestContext& ctx) { LOG_INFO(Service_SET, "called"); - Service::Time::Clock::SteadyClockTimePoint time_point{}; - auto res = GetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); + Service::PSC::Time::SteadyClockTimePoint time_point{}; + const auto res = GetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(res); - rb.PushRaw<Service::Time::Clock::SteadyClockTimePoint>(time_point); + rb.PushRaw<Service::PSC::Time::SteadyClockTimePoint>(time_point); } void ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime( @@ -1178,9 +1248,9 @@ void ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime( LOG_INFO(Service_SET, "called"); IPC::RequestParser rp{ctx}; - auto time_point{rp.PopRaw<Service::Time::Clock::SteadyClockTimePoint>()}; + const auto time_point{rp.PopRaw<Service::PSC::Time::SteadyClockTimePoint>()}; - auto res = SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); + const auto res = SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(res); @@ -1257,25 +1327,25 @@ 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(HW_GPU, "Failed to store 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(HW_GPU, "Failed to store 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(HW_GPU, "Failed to store 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(HW_GPU, "Failed to store ApplLn settings"); + LOG_ERROR(Service_SET, "Failed to store ApplLn settings"); } } @@ -1306,57 +1376,68 @@ Result ISystemSettingsServer::GetSettingsItemValue(std::vector<u8>& out_value, R_SUCCEED(); } -Result ISystemSettingsServer::GetExternalSteadyClockSourceId(Common::UUID& out_id) { +Result ISystemSettingsServer::GetVibrationMasterVolume(f32& out_volume) const { + out_volume = m_system_settings.vibration_master_volume; + R_SUCCEED(); +} + +Result ISystemSettingsServer::SetVibrationMasterVolume(f32 volume) { + m_system_settings.vibration_master_volume = volume; + SetSaveNeeded(); + R_SUCCEED(); +} + +Result ISystemSettingsServer::GetExternalSteadyClockSourceId(Common::UUID& out_id) const { out_id = m_private_settings.external_clock_source_id; R_SUCCEED(); } -Result ISystemSettingsServer::SetExternalSteadyClockSourceId(Common::UUID id) { +Result ISystemSettingsServer::SetExternalSteadyClockSourceId(const Common::UUID& id) { m_private_settings.external_clock_source_id = id; SetSaveNeeded(); R_SUCCEED(); } Result ISystemSettingsServer::GetUserSystemClockContext( - Service::Time::Clock::SystemClockContext& out_context) { + Service::PSC::Time::SystemClockContext& out_context) const { out_context = m_system_settings.user_system_clock_context; R_SUCCEED(); } Result ISystemSettingsServer::SetUserSystemClockContext( - Service::Time::Clock::SystemClockContext& context) { + const Service::PSC::Time::SystemClockContext& context) { m_system_settings.user_system_clock_context = context; SetSaveNeeded(); R_SUCCEED(); } Result ISystemSettingsServer::GetDeviceTimeZoneLocationName( - Service::Time::TimeZone::LocationName& out_name) { + Service::PSC::Time::LocationName& out_name) const { out_name = m_system_settings.device_time_zone_location_name; R_SUCCEED(); } Result ISystemSettingsServer::SetDeviceTimeZoneLocationName( - Service::Time::TimeZone::LocationName& name) { + const Service::PSC::Time::LocationName& name) { m_system_settings.device_time_zone_location_name = name; SetSaveNeeded(); R_SUCCEED(); } Result ISystemSettingsServer::GetNetworkSystemClockContext( - Service::Time::Clock::SystemClockContext& out_context) { + Service::PSC::Time::SystemClockContext& out_context) const { out_context = m_system_settings.network_system_clock_context; R_SUCCEED(); } Result ISystemSettingsServer::SetNetworkSystemClockContext( - Service::Time::Clock::SystemClockContext& context) { + const Service::PSC::Time::SystemClockContext& context) { m_system_settings.network_system_clock_context = context; SetSaveNeeded(); R_SUCCEED(); } -Result ISystemSettingsServer::IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled) { +Result ISystemSettingsServer::IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled) const { out_enabled = m_system_settings.user_system_clock_automatic_correction_enabled; R_SUCCEED(); } @@ -1373,32 +1454,32 @@ Result ISystemSettingsServer::SetExternalSteadyClockInternalOffset(s64 offset) { R_SUCCEED(); } -Result ISystemSettingsServer::GetExternalSteadyClockInternalOffset(s64& out_offset) { +Result ISystemSettingsServer::GetExternalSteadyClockInternalOffset(s64& out_offset) const { out_offset = m_private_settings.external_steady_clock_internal_offset; R_SUCCEED(); } Result ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime( - Service::Time::Clock::SteadyClockTimePoint& out_time_point) { + Service::PSC::Time::SteadyClockTimePoint& out_time_point) const { out_time_point = m_system_settings.device_time_zone_location_updated_time; R_SUCCEED(); } Result ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime( - Service::Time::Clock::SteadyClockTimePoint& time_point) { + const Service::PSC::Time::SteadyClockTimePoint& time_point) { m_system_settings.device_time_zone_location_updated_time = time_point; SetSaveNeeded(); R_SUCCEED(); } Result ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime( - Service::Time::Clock::SteadyClockTimePoint& out_time_point) { + Service::PSC::Time::SteadyClockTimePoint& out_time_point) const { out_time_point = m_system_settings.user_system_clock_automatic_correction_updated_time_point; R_SUCCEED(); } Result ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime( - Service::Time::Clock::SteadyClockTimePoint out_time_point) { + const Service::PSC::Time::SteadyClockTimePoint& out_time_point) { m_system_settings.user_system_clock_automatic_correction_updated_time_point = out_time_point; SetSaveNeeded(); R_SUCCEED(); diff --git a/src/core/hle/service/set/system_settings_server.h b/src/core/hle/service/set/system_settings_server.h index 32716f567..acbda8b8c 100644 --- a/src/core/hle/service/set/system_settings_server.h +++ b/src/core/hle/service/set/system_settings_server.h @@ -11,14 +11,13 @@ #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/setting_formats/appln_settings.h" #include "core/hle/service/set/setting_formats/device_settings.h" #include "core/hle/service/set/setting_formats/private_settings.h" #include "core/hle/service/set/setting_formats/system_settings.h" #include "core/hle/service/set/settings_types.h" -#include "core/hle/service/time/clock_types.h" -#include "core/hle/service/time/time_zone_types.h" namespace Core { class System; @@ -49,26 +48,28 @@ public: return result; } - Result GetExternalSteadyClockSourceId(Common::UUID& out_id); - Result SetExternalSteadyClockSourceId(Common::UUID id); - Result GetUserSystemClockContext(Service::Time::Clock::SystemClockContext& out_context); - Result SetUserSystemClockContext(Service::Time::Clock::SystemClockContext& context); - Result GetDeviceTimeZoneLocationName(Service::Time::TimeZone::LocationName& out_name); - Result SetDeviceTimeZoneLocationName(Service::Time::TimeZone::LocationName& name); - Result GetNetworkSystemClockContext(Service::Time::Clock::SystemClockContext& out_context); - Result SetNetworkSystemClockContext(Service::Time::Clock::SystemClockContext& context); - Result IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled); + Result GetVibrationMasterVolume(f32& out_volume) const; + Result SetVibrationMasterVolume(f32 volume); + Result GetExternalSteadyClockSourceId(Common::UUID& out_id) const; + Result SetExternalSteadyClockSourceId(const Common::UUID& id); + Result GetUserSystemClockContext(Service::PSC::Time::SystemClockContext& out_context) const; + Result SetUserSystemClockContext(const Service::PSC::Time::SystemClockContext& context); + Result GetDeviceTimeZoneLocationName(Service::PSC::Time::LocationName& out_name) const; + Result SetDeviceTimeZoneLocationName(const Service::PSC::Time::LocationName& name); + Result GetNetworkSystemClockContext(Service::PSC::Time::SystemClockContext& out_context) const; + Result SetNetworkSystemClockContext(const Service::PSC::Time::SystemClockContext& context); + Result IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled) const; Result SetUserSystemClockAutomaticCorrectionEnabled(bool enabled); Result SetExternalSteadyClockInternalOffset(s64 offset); - Result GetExternalSteadyClockInternalOffset(s64& out_offset); + Result GetExternalSteadyClockInternalOffset(s64& out_offset) const; Result GetDeviceTimeZoneLocationUpdatedTime( - Service::Time::Clock::SteadyClockTimePoint& out_time_point); + Service::PSC::Time::SteadyClockTimePoint& out_time_point) const; Result SetDeviceTimeZoneLocationUpdatedTime( - Service::Time::Clock::SteadyClockTimePoint& time_point); + const Service::PSC::Time::SteadyClockTimePoint& time_point); Result GetUserSystemClockAutomaticCorrectionUpdatedTime( - Service::Time::Clock::SteadyClockTimePoint& out_time_point); + Service::PSC::Time::SteadyClockTimePoint& out_time_point) const; Result SetUserSystemClockAutomaticCorrectionUpdatedTime( - Service::Time::Clock::SteadyClockTimePoint time_point); + const Service::PSC::Time::SteadyClockTimePoint& time_point); private: void SetLanguageCode(HLERequestContext& ctx); @@ -90,12 +91,17 @@ private: void SetNotificationSettings(HLERequestContext& ctx); void GetAccountNotificationSettings(HLERequestContext& ctx); void SetAccountNotificationSettings(HLERequestContext& ctx); + void GetVibrationMasterVolume(HLERequestContext& ctx); + void SetVibrationMasterVolume(HLERequestContext& ctx); void GetSettingsItemValueSize(HLERequestContext& ctx); void GetSettingsItemValue(HLERequestContext& ctx); void GetTvSettings(HLERequestContext& ctx); void SetTvSettings(HLERequestContext& ctx); + void IsForceMuteOnHeadphoneRemoved(HLERequestContext& ctx); + void SetForceMuteOnHeadphoneRemoved(HLERequestContext& ctx); void GetDebugModeFlag(HLERequestContext& ctx); void GetQuestFlag(HLERequestContext& ctx); + void SetQuestFlag(HLERequestContext& ctx); void GetDeviceTimeZoneLocationName(HLERequestContext& ctx); void SetDeviceTimeZoneLocationName(HLERequestContext& ctx); void SetRegionCode(HLERequestContext& ctx); @@ -104,6 +110,7 @@ private: void IsUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx); void SetUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx); void GetPrimaryAlbumStorage(HLERequestContext& ctx); + void SetPrimaryAlbumStorage(HLERequestContext& ctx); void GetNfcEnableFlag(HLERequestContext& ctx); void SetNfcEnableFlag(HLERequestContext& ctx); void GetSleepSettings(HLERequestContext& ctx); @@ -147,8 +154,8 @@ private: PrivateSettings m_private_settings{}; DeviceSettings m_device_settings{}; ApplnSettings m_appln_settings{}; - std::jthread m_save_thread; std::mutex m_save_needed_mutex; + std::jthread m_save_thread; bool m_save_needed{false}; }; diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index 4ae32a9c1..32c218638 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -3,6 +3,7 @@ #pragma once +#include <chrono> #include <memory> #include <mutex> #include <string> @@ -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 <Common::DerivedFrom<SessionRequestHandler> T> - std::shared_ptr<T> GetService(const std::string& service_name) const { + std::shared_ptr<T> 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<std::chrono::nanoseconds>(100ms).count()); + service = registered_services.find(service_name); + } } + return std::static_pointer_cast<T>(service->second()); } diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h deleted file mode 100644 index 7149fffeb..000000000 --- a/src/core/hle/service/time/clock_types.h +++ /dev/null @@ -1,129 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <ratio> - -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "common/uuid.h" -#include "core/hle/service/time/errors.h" -#include "core/hle/service/time/time_zone_types.h" - -// Defined by WinBase.h on Windows -#ifdef GetCurrentTime -#undef GetCurrentTime -#endif - -namespace Service::Time::Clock { - -enum class TimeType : u8 { - UserSystemClock, - NetworkSystemClock, - LocalSystemClock, -}; - -/// https://switchbrew.org/wiki/Glue_services#SteadyClockTimePoint -struct SteadyClockTimePoint { - s64 time_point; - Common::UUID clock_source_id; - - Result GetSpanBetween(SteadyClockTimePoint other, s64& span) const { - span = 0; - - if (clock_source_id != other.clock_source_id) { - return ERROR_TIME_MISMATCH; - } - - span = other.time_point - time_point; - - return ResultSuccess; - } - - static SteadyClockTimePoint GetRandom() { - return {0, Common::UUID::MakeRandom()}; - } -}; -static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size"); -static_assert(std::is_trivially_copyable_v<SteadyClockTimePoint>, - "SteadyClockTimePoint must be trivially copyable"); - -struct SteadyClockContext { - u64 internal_offset; - Common::UUID steady_time_point; -}; -static_assert(sizeof(SteadyClockContext) == 0x18, "SteadyClockContext is incorrect size"); -static_assert(std::is_trivially_copyable_v<SteadyClockContext>, - "SteadyClockContext must be trivially copyable"); -using StandardSteadyClockTimePointType = SteadyClockContext; - -struct SystemClockContext { - s64 offset; - SteadyClockTimePoint steady_time_point; -}; -static_assert(sizeof(SystemClockContext) == 0x20, "SystemClockContext is incorrect size"); -static_assert(std::is_trivially_copyable_v<SystemClockContext>, - "SystemClockContext must be trivially copyable"); - -struct ContinuousAdjustmentTimePoint { - s64 measurement_offset; - s64 diff_scale; - u32 shift_amount; - s64 lower; - s64 upper; - Common::UUID clock_source_id; -}; -static_assert(sizeof(ContinuousAdjustmentTimePoint) == 0x38); -static_assert(std::is_trivially_copyable_v<ContinuousAdjustmentTimePoint>, - "ContinuousAdjustmentTimePoint must be trivially copyable"); - -/// https://switchbrew.org/wiki/Glue_services#TimeSpanType -struct TimeSpanType { - s64 nanoseconds{}; - - s64 ToSeconds() const { - return nanoseconds / std::nano::den; - } - - static TimeSpanType FromSeconds(s64 seconds) { - return {seconds * std::nano::den}; - } - - template <u64 Frequency> - static TimeSpanType FromTicks(u64 ticks) { - using TicksToNSRatio = std::ratio<std::nano::den, Frequency>; - return {static_cast<s64>(ticks * TicksToNSRatio::num / TicksToNSRatio::den)}; - } -}; -static_assert(sizeof(TimeSpanType) == 8, "TimeSpanType is incorrect size"); - -struct ClockSnapshot { - SystemClockContext user_context; - SystemClockContext network_context; - s64 user_time; - s64 network_time; - TimeZone::CalendarTime user_calendar_time; - TimeZone::CalendarTime network_calendar_time; - TimeZone::CalendarAdditionalInfo user_calendar_additional_time; - TimeZone::CalendarAdditionalInfo network_calendar_additional_time; - SteadyClockTimePoint steady_clock_time_point; - TimeZone::LocationName location_name; - u8 is_automatic_correction_enabled; - TimeType type; - INSERT_PADDING_BYTES_NOINIT(0x2); - - static Result GetCurrentTime(s64& current_time, - const SteadyClockTimePoint& steady_clock_time_point, - const SystemClockContext& context) { - if (steady_clock_time_point.clock_source_id != context.steady_time_point.clock_source_id) { - current_time = 0; - return ERROR_TIME_MISMATCH; - } - current_time = steady_clock_time_point.time_point + context.offset; - return ResultSuccess; - } -}; -static_assert(sizeof(ClockSnapshot) == 0xD0, "ClockSnapshot is incorrect size"); - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h b/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h deleted file mode 100644 index 0f928a5a5..000000000 --- a/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/time/system_clock_context_update_callback.h" - -namespace Service::Time::Clock { - -class EphemeralNetworkSystemClockContextWriter final : public SystemClockContextUpdateCallback { -public: - EphemeralNetworkSystemClockContextWriter() : SystemClockContextUpdateCallback{} {} -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/ephemeral_network_system_clock_core.h b/src/core/hle/service/time/ephemeral_network_system_clock_core.h deleted file mode 100644 index 0a5f5aafb..000000000 --- a/src/core/hle/service/time/ephemeral_network_system_clock_core.h +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/time/system_clock_core.h" - -namespace Service::Time::Clock { - -class EphemeralNetworkSystemClockCore final : public SystemClockCore { -public: - explicit EphemeralNetworkSystemClockCore(SteadyClockCore& steady_clock_core_) - : SystemClockCore{steady_clock_core_} {} -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/errors.h b/src/core/hle/service/time/errors.h deleted file mode 100644 index 6655d30e1..000000000 --- a/src/core/hle/service/time/errors.h +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/result.h" - -namespace Service::Time { - -constexpr Result ERROR_PERMISSION_DENIED{ErrorModule::Time, 1}; -constexpr Result ERROR_TIME_MISMATCH{ErrorModule::Time, 102}; -constexpr Result ERROR_UNINITIALIZED_CLOCK{ErrorModule::Time, 103}; -constexpr Result ERROR_TIME_NOT_FOUND{ErrorModule::Time, 200}; -constexpr Result ERROR_OVERFLOW{ErrorModule::Time, 201}; -constexpr Result ERROR_LOCATION_NAME_TOO_LONG{ErrorModule::Time, 801}; -constexpr Result ERROR_OUT_OF_RANGE{ErrorModule::Time, 902}; -constexpr Result ERROR_TIME_ZONE_CONVERSION_FAILED{ErrorModule::Time, 903}; -constexpr Result ERROR_TIME_ZONE_NOT_FOUND{ErrorModule::Time, 989}; -constexpr Result ERROR_NOT_IMPLEMENTED{ErrorModule::Time, 990}; - -} // namespace Service::Time diff --git a/src/core/hle/service/time/local_system_clock_context_writer.h b/src/core/hle/service/time/local_system_clock_context_writer.h deleted file mode 100644 index 1639ef2b9..000000000 --- a/src/core/hle/service/time/local_system_clock_context_writer.h +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/time/system_clock_context_update_callback.h" -#include "core/hle/service/time/time_sharedmemory.h" - -namespace Service::Time::Clock { - -class LocalSystemClockContextWriter final : public SystemClockContextUpdateCallback { -public: - explicit LocalSystemClockContextWriter(SharedMemory& shared_memory_) - : SystemClockContextUpdateCallback{}, shared_memory{shared_memory_} {} - -protected: - Result Update() override { - shared_memory.UpdateLocalSystemClockContext(context); - return ResultSuccess; - } - -private: - SharedMemory& shared_memory; -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/network_system_clock_context_writer.h b/src/core/hle/service/time/network_system_clock_context_writer.h deleted file mode 100644 index 655e4c06d..000000000 --- a/src/core/hle/service/time/network_system_clock_context_writer.h +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/time/errors.h" -#include "core/hle/service/time/system_clock_context_update_callback.h" -#include "core/hle/service/time/time_sharedmemory.h" - -namespace Service::Time::Clock { - -class NetworkSystemClockContextWriter final : public SystemClockContextUpdateCallback { -public: - explicit NetworkSystemClockContextWriter(SharedMemory& shared_memory_) - : SystemClockContextUpdateCallback{}, shared_memory{shared_memory_} {} - -protected: - Result Update() override { - shared_memory.UpdateNetworkSystemClockContext(context); - return ResultSuccess; - } - -private: - SharedMemory& shared_memory; -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/standard_local_system_clock_core.h b/src/core/hle/service/time/standard_local_system_clock_core.h deleted file mode 100644 index ae2ff1bfd..000000000 --- a/src/core/hle/service/time/standard_local_system_clock_core.h +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/time/system_clock_core.h" - -namespace Service::Time::Clock { - -class StandardLocalSystemClockCore final : public SystemClockCore { -public: - explicit StandardLocalSystemClockCore(SteadyClockCore& steady_clock_core_) - : SystemClockCore{steady_clock_core_} {} -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/standard_network_system_clock_core.h b/src/core/hle/service/time/standard_network_system_clock_core.h deleted file mode 100644 index c1ec5252b..000000000 --- a/src/core/hle/service/time/standard_network_system_clock_core.h +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/time/clock_types.h" -#include "core/hle/service/time/steady_clock_core.h" -#include "core/hle/service/time/system_clock_core.h" - -namespace Core { -class System; -} - -namespace Service::Time::Clock { - -class StandardNetworkSystemClockCore final : public SystemClockCore { -public: - explicit StandardNetworkSystemClockCore(SteadyClockCore& steady_clock_core_) - : SystemClockCore{steady_clock_core_} {} - - void SetStandardNetworkClockSufficientAccuracy(TimeSpanType value) { - standard_network_clock_sufficient_accuracy = value; - } - - bool IsStandardNetworkSystemClockAccuracySufficient(Core::System& system) const { - SystemClockContext clock_ctx{}; - if (GetClockContext(system, clock_ctx) != ResultSuccess) { - return {}; - } - - s64 span{}; - if (clock_ctx.steady_time_point.GetSpanBetween( - GetSteadyClockCore().GetCurrentTimePoint(system), span) != ResultSuccess) { - return {}; - } - - return TimeSpanType{span}.nanoseconds < - standard_network_clock_sufficient_accuracy.nanoseconds; - } - -private: - TimeSpanType standard_network_clock_sufficient_accuracy{}; -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/standard_steady_clock_core.cpp b/src/core/hle/service/time/standard_steady_clock_core.cpp deleted file mode 100644 index 5627b7003..000000000 --- a/src/core/hle/service/time/standard_steady_clock_core.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/core.h" -#include "core/core_timing.h" -#include "core/hardware_properties.h" -#include "core/hle/service/time/standard_steady_clock_core.h" - -namespace Service::Time::Clock { - -TimeSpanType StandardSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) { - const TimeSpanType ticks_time_span{ - TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>(system.CoreTiming().GetClockTicks())}; - TimeSpanType raw_time_point{setup_value.nanoseconds + ticks_time_span.nanoseconds}; - - if (raw_time_point.nanoseconds < cached_raw_time_point.nanoseconds) { - raw_time_point.nanoseconds = cached_raw_time_point.nanoseconds; - } - - cached_raw_time_point = raw_time_point; - return raw_time_point; -} - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/standard_steady_clock_core.h b/src/core/hle/service/time/standard_steady_clock_core.h deleted file mode 100644 index 036463b87..000000000 --- a/src/core/hle/service/time/standard_steady_clock_core.h +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/time/clock_types.h" -#include "core/hle/service/time/steady_clock_core.h" - -namespace Core { -class System; -} - -namespace Service::Time::Clock { - -class StandardSteadyClockCore final : public SteadyClockCore { -public: - SteadyClockTimePoint GetTimePoint(Core::System& system) override { - return {GetCurrentRawTimePoint(system).ToSeconds(), GetClockSourceId()}; - } - - TimeSpanType GetInternalOffset() const override { - return internal_offset; - } - - void SetInternalOffset(TimeSpanType value) override { - internal_offset = value; - } - - TimeSpanType GetCurrentRawTimePoint(Core::System& system) override; - - void SetSetupValue(TimeSpanType value) { - setup_value = value; - } - -private: - TimeSpanType setup_value{}; - TimeSpanType internal_offset{}; - TimeSpanType cached_raw_time_point{}; -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/standard_user_system_clock_core.cpp b/src/core/hle/service/time/standard_user_system_clock_core.cpp deleted file mode 100644 index b033757ed..000000000 --- a/src/core/hle/service/time/standard_user_system_clock_core.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/assert.h" -#include "core/core.h" -#include "core/hle/kernel/k_event.h" -#include "core/hle/service/time/standard_local_system_clock_core.h" -#include "core/hle/service/time/standard_network_system_clock_core.h" -#include "core/hle/service/time/standard_user_system_clock_core.h" - -namespace Service::Time::Clock { - -StandardUserSystemClockCore::StandardUserSystemClockCore( - StandardLocalSystemClockCore& local_system_clock_core_, - StandardNetworkSystemClockCore& network_system_clock_core_, Core::System& system_) - : SystemClockCore(local_system_clock_core_.GetSteadyClockCore()), - local_system_clock_core{local_system_clock_core_}, - network_system_clock_core{network_system_clock_core_}, - auto_correction_time{SteadyClockTimePoint::GetRandom()}, service_context{ - system_, - "StandardUserSystemClockCore"} { - auto_correction_event = - service_context.CreateEvent("StandardUserSystemClockCore:AutoCorrectionEvent"); -} - -StandardUserSystemClockCore::~StandardUserSystemClockCore() { - service_context.CloseEvent(auto_correction_event); -} - -Result StandardUserSystemClockCore::SetAutomaticCorrectionEnabled(Core::System& system, - bool value) { - if (const Result result{ApplyAutomaticCorrection(system, value)}; result != ResultSuccess) { - return result; - } - - auto_correction_enabled = value; - - return ResultSuccess; -} - -Result StandardUserSystemClockCore::GetClockContext(Core::System& system, - SystemClockContext& ctx) const { - if (const Result result{ApplyAutomaticCorrection(system, false)}; result != ResultSuccess) { - return result; - } - - return local_system_clock_core.GetClockContext(system, ctx); -} - -Result StandardUserSystemClockCore::Flush(const SystemClockContext&) { - UNIMPLEMENTED(); - return ERROR_NOT_IMPLEMENTED; -} - -Result StandardUserSystemClockCore::SetClockContext(const SystemClockContext&) { - UNIMPLEMENTED(); - return ERROR_NOT_IMPLEMENTED; -} - -Result StandardUserSystemClockCore::ApplyAutomaticCorrection(Core::System& system, - bool value) const { - if (auto_correction_enabled == value) { - return ResultSuccess; - } - - if (!network_system_clock_core.IsClockSetup(system)) { - return ERROR_UNINITIALIZED_CLOCK; - } - - SystemClockContext ctx{}; - if (const Result result{network_system_clock_core.GetClockContext(system, ctx)}; - result != ResultSuccess) { - return result; - } - - local_system_clock_core.SetClockContext(ctx); - - return ResultSuccess; -} - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/standard_user_system_clock_core.h b/src/core/hle/service/time/standard_user_system_clock_core.h deleted file mode 100644 index ee6e29487..000000000 --- a/src/core/hle/service/time/standard_user_system_clock_core.h +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/time/clock_types.h" -#include "core/hle/service/time/system_clock_core.h" - -namespace Core { -class System; -} - -namespace Kernel { -class KEvent; -} - -namespace Service::Time::Clock { - -class StandardLocalSystemClockCore; -class StandardNetworkSystemClockCore; - -class StandardUserSystemClockCore final : public SystemClockCore { -public: - StandardUserSystemClockCore(StandardLocalSystemClockCore& local_system_clock_core_, - StandardNetworkSystemClockCore& network_system_clock_core_, - Core::System& system_); - - ~StandardUserSystemClockCore() override; - - Result SetAutomaticCorrectionEnabled(Core::System& system, bool value); - - Result GetClockContext(Core::System& system, SystemClockContext& ctx) const override; - - bool IsAutomaticCorrectionEnabled() const { - return auto_correction_enabled; - } - - void SetAutomaticCorrectionUpdatedTime(SteadyClockTimePoint steady_clock_time_point) { - auto_correction_time = steady_clock_time_point; - } - -protected: - Result Flush(const SystemClockContext&) override; - - Result SetClockContext(const SystemClockContext&) override; - - Result ApplyAutomaticCorrection(Core::System& system, bool value) const; - - const SteadyClockTimePoint& GetAutomaticCorrectionUpdatedTime() const { - return auto_correction_time; - } - -private: - StandardLocalSystemClockCore& local_system_clock_core; - StandardNetworkSystemClockCore& network_system_clock_core; - bool auto_correction_enabled{}; - SteadyClockTimePoint auto_correction_time; - KernelHelpers::ServiceContext service_context; - Kernel::KEvent* auto_correction_event; -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/steady_clock_core.h b/src/core/hle/service/time/steady_clock_core.h deleted file mode 100644 index 2867c351c..000000000 --- a/src/core/hle/service/time/steady_clock_core.h +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/uuid.h" -#include "core/hle/service/time/clock_types.h" - -namespace Core { -class System; -} - -namespace Service::Time::Clock { - -class SteadyClockCore { -public: - SteadyClockCore() = default; - virtual ~SteadyClockCore() = default; - - const Common::UUID& GetClockSourceId() const { - return clock_source_id; - } - - void SetClockSourceId(const Common::UUID& value) { - clock_source_id = value; - } - - virtual TimeSpanType GetInternalOffset() const = 0; - - virtual void SetInternalOffset(TimeSpanType internal_offset) = 0; - - virtual SteadyClockTimePoint GetTimePoint(Core::System& system) = 0; - - virtual TimeSpanType GetCurrentRawTimePoint(Core::System& system) = 0; - - SteadyClockTimePoint GetCurrentTimePoint(Core::System& system) { - SteadyClockTimePoint result{GetTimePoint(system)}; - result.time_point += GetInternalOffset().ToSeconds(); - return result; - } - - bool IsInitialized() const { - return is_initialized; - } - - void MarkAsInitialized() { - is_initialized = true; - } - -private: - Common::UUID clock_source_id{Common::UUID::MakeRandom()}; - bool is_initialized{}; -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/system_clock_context_update_callback.cpp b/src/core/hle/service/time/system_clock_context_update_callback.cpp deleted file mode 100644 index cafc04ee7..000000000 --- a/src/core/hle/service/time/system_clock_context_update_callback.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/kernel/k_event.h" -#include "core/hle/service/time/errors.h" -#include "core/hle/service/time/system_clock_context_update_callback.h" - -namespace Service::Time::Clock { - -SystemClockContextUpdateCallback::SystemClockContextUpdateCallback() = default; -SystemClockContextUpdateCallback::~SystemClockContextUpdateCallback() = default; - -bool SystemClockContextUpdateCallback::NeedUpdate(const SystemClockContext& value) const { - if (has_context) { - return context.offset != value.offset || - context.steady_time_point.clock_source_id != value.steady_time_point.clock_source_id; - } - - return true; -} - -void SystemClockContextUpdateCallback::RegisterOperationEvent( - std::shared_ptr<Kernel::KEvent>&& event) { - operation_event_list.emplace_back(std::move(event)); -} - -void SystemClockContextUpdateCallback::BroadcastOperationEvent() { - for (const auto& event : operation_event_list) { - event->Signal(); - } -} - -Result SystemClockContextUpdateCallback::Update(const SystemClockContext& value) { - Result result{ResultSuccess}; - - if (NeedUpdate(value)) { - context = value; - has_context = true; - - result = Update(); - - if (result == ResultSuccess) { - BroadcastOperationEvent(); - } - } - - return result; -} - -Result SystemClockContextUpdateCallback::Update() { - return ResultSuccess; -} - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/system_clock_context_update_callback.h b/src/core/hle/service/time/system_clock_context_update_callback.h deleted file mode 100644 index bf657acd9..000000000 --- a/src/core/hle/service/time/system_clock_context_update_callback.h +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <memory> -#include <vector> - -#include "core/hle/service/time/clock_types.h" - -namespace Kernel { -class KEvent; -} - -namespace Service::Time::Clock { - -// Parts of this implementation were based on Ryujinx (https://github.com/Ryujinx/Ryujinx/pull/783). -// This code was released under public domain. - -class SystemClockContextUpdateCallback { -public: - SystemClockContextUpdateCallback(); - virtual ~SystemClockContextUpdateCallback(); - - bool NeedUpdate(const SystemClockContext& value) const; - - void RegisterOperationEvent(std::shared_ptr<Kernel::KEvent>&& event); - - void BroadcastOperationEvent(); - - Result Update(const SystemClockContext& value); - -protected: - virtual Result Update(); - - SystemClockContext context{}; - -private: - bool has_context{}; - std::vector<std::shared_ptr<Kernel::KEvent>> operation_event_list; -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/system_clock_core.cpp b/src/core/hle/service/time/system_clock_core.cpp deleted file mode 100644 index da078241f..000000000 --- a/src/core/hle/service/time/system_clock_core.cpp +++ /dev/null @@ -1,71 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/time/steady_clock_core.h" -#include "core/hle/service/time/system_clock_context_update_callback.h" -#include "core/hle/service/time/system_clock_core.h" - -namespace Service::Time::Clock { - -SystemClockCore::SystemClockCore(SteadyClockCore& steady_clock_core_) - : steady_clock_core{steady_clock_core_} { - context.steady_time_point.clock_source_id = steady_clock_core.GetClockSourceId(); -} - -SystemClockCore::~SystemClockCore() = default; - -Result SystemClockCore::GetCurrentTime(Core::System& system, s64& posix_time) const { - posix_time = 0; - - const SteadyClockTimePoint current_time_point{steady_clock_core.GetCurrentTimePoint(system)}; - - SystemClockContext clock_context{}; - if (const Result result{GetClockContext(system, clock_context)}; result != ResultSuccess) { - return result; - } - - if (current_time_point.clock_source_id != clock_context.steady_time_point.clock_source_id) { - return ERROR_TIME_MISMATCH; - } - - posix_time = clock_context.offset + current_time_point.time_point; - - return ResultSuccess; -} - -Result SystemClockCore::SetCurrentTime(Core::System& system, s64 posix_time) { - const SteadyClockTimePoint current_time_point{steady_clock_core.GetCurrentTimePoint(system)}; - const SystemClockContext clock_context{posix_time - current_time_point.time_point, - current_time_point}; - - if (const Result result{SetClockContext(clock_context)}; result != ResultSuccess) { - return result; - } - return Flush(clock_context); -} - -Result SystemClockCore::Flush(const SystemClockContext& clock_context) { - if (!system_clock_context_update_callback) { - return ResultSuccess; - } - return system_clock_context_update_callback->Update(clock_context); -} - -Result SystemClockCore::SetSystemClockContext(const SystemClockContext& clock_context) { - if (const Result result{SetClockContext(clock_context)}; result != ResultSuccess) { - return result; - } - return Flush(clock_context); -} - -bool SystemClockCore::IsClockSetup(Core::System& system) const { - SystemClockContext value{}; - if (GetClockContext(system, value) == ResultSuccess) { - const SteadyClockTimePoint steady_clock_time_point{ - steady_clock_core.GetCurrentTimePoint(system)}; - return steady_clock_time_point.clock_source_id == value.steady_time_point.clock_source_id; - } - return {}; -} - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/system_clock_core.h b/src/core/hle/service/time/system_clock_core.h deleted file mode 100644 index 8cb34126f..000000000 --- a/src/core/hle/service/time/system_clock_core.h +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <memory> - -#include "common/common_types.h" -#include "core/hle/service/time/clock_types.h" - -namespace Core { -class System; -} - -namespace Service::Time::Clock { - -class SteadyClockCore; -class SystemClockContextUpdateCallback; - -// Parts of this implementation were based on Ryujinx (https://github.com/Ryujinx/Ryujinx/pull/783). -// This code was released under public domain. - -class SystemClockCore { -public: - explicit SystemClockCore(SteadyClockCore& steady_clock_core_); - virtual ~SystemClockCore(); - - SteadyClockCore& GetSteadyClockCore() const { - return steady_clock_core; - } - - Result GetCurrentTime(Core::System& system, s64& posix_time) const; - - Result SetCurrentTime(Core::System& system, s64 posix_time); - - virtual Result GetClockContext([[maybe_unused]] Core::System& system, - SystemClockContext& value) const { - value = context; - return ResultSuccess; - } - - virtual Result SetClockContext(const SystemClockContext& value) { - context = value; - return ResultSuccess; - } - - virtual Result Flush(const SystemClockContext& clock_context); - - void SetUpdateCallbackInstance(std::shared_ptr<SystemClockContextUpdateCallback> callback) { - system_clock_context_update_callback = std::move(callback); - } - - Result SetSystemClockContext(const SystemClockContext& context); - - bool IsInitialized() const { - return is_initialized; - } - - void MarkAsInitialized() { - is_initialized = true; - } - - bool IsClockSetup(Core::System& system) const; - -private: - SteadyClockCore& steady_clock_core; - SystemClockContext context{}; - bool is_initialized{}; - std::shared_ptr<SystemClockContextUpdateCallback> system_clock_context_update_callback; -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.cpp b/src/core/hle/service/time/tick_based_steady_clock_core.cpp deleted file mode 100644 index 0d9fb3143..000000000 --- a/src/core/hle/service/time/tick_based_steady_clock_core.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/core.h" -#include "core/core_timing.h" -#include "core/hardware_properties.h" -#include "core/hle/service/time/tick_based_steady_clock_core.h" - -namespace Service::Time::Clock { - -SteadyClockTimePoint TickBasedSteadyClockCore::GetTimePoint(Core::System& system) { - const TimeSpanType ticks_time_span{ - TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>(system.CoreTiming().GetClockTicks())}; - - return {ticks_time_span.ToSeconds(), GetClockSourceId()}; -} - -TimeSpanType TickBasedSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) { - return TimeSpanType::FromSeconds(GetTimePoint(system).time_point); -} - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.h b/src/core/hle/service/time/tick_based_steady_clock_core.h deleted file mode 100644 index 491185dc3..000000000 --- a/src/core/hle/service/time/tick_based_steady_clock_core.h +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/time/clock_types.h" -#include "core/hle/service/time/steady_clock_core.h" - -namespace Core { -class System; -} - -namespace Service::Time::Clock { - -class TickBasedSteadyClockCore final : public SteadyClockCore { -public: - TimeSpanType GetInternalOffset() const override { - return {}; - } - - void SetInternalOffset(TimeSpanType internal_offset) override {} - - SteadyClockTimePoint GetTimePoint(Core::System& system) override; - - TimeSpanType GetCurrentRawTimePoint(Core::System& system) override; -}; - -} // namespace Service::Time::Clock diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp deleted file mode 100644 index 7197ca30f..000000000 --- a/src/core/hle/service/time/time.cpp +++ /dev/null @@ -1,412 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/logging/log.h" -#include "core/core.h" -#include "core/core_timing.h" -#include "core/hardware_properties.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/server_manager.h" -#include "core/hle/service/time/time.h" -#include "core/hle/service/time/time_interface.h" -#include "core/hle/service/time/time_manager.h" -#include "core/hle/service/time/time_sharedmemory.h" -#include "core/hle/service/time/time_zone_service.h" - -namespace Service::Time { - -class ISystemClock final : public ServiceFramework<ISystemClock> { -public: - explicit ISystemClock(Clock::SystemClockCore& clock_core_, Core::System& system_) - : ServiceFramework{system_, "ISystemClock"}, clock_core{clock_core_} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &ISystemClock::GetCurrentTime, "GetCurrentTime"}, - {1, nullptr, "SetCurrentTime"}, - {2, &ISystemClock::GetSystemClockContext, "GetSystemClockContext"}, - {3, nullptr, "SetSystemClockContext"}, - {4, nullptr, "GetOperationEventReadableHandle"}, - }; - // clang-format on - - RegisterHandlers(functions); - } - -private: - void GetCurrentTime(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - if (!clock_core.IsInitialized()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_UNINITIALIZED_CLOCK); - return; - } - - s64 posix_time{}; - if (const Result result{clock_core.GetCurrentTime(system, posix_time)}; result.IsError()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push<s64>(posix_time); - } - - void GetSystemClockContext(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - if (!clock_core.IsInitialized()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_UNINITIALIZED_CLOCK); - return; - } - - Clock::SystemClockContext system_clock_context{}; - if (const Result result{clock_core.GetClockContext(system, system_clock_context)}; - result.IsError()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - IPC::ResponseBuilder rb{ctx, sizeof(Clock::SystemClockContext) / 4 + 2}; - rb.Push(ResultSuccess); - rb.PushRaw(system_clock_context); - } - - Clock::SystemClockCore& clock_core; -}; - -class ISteadyClock final : public ServiceFramework<ISteadyClock> { -public: - explicit ISteadyClock(Clock::SteadyClockCore& clock_core_, Core::System& system_) - : ServiceFramework{system_, "ISteadyClock"}, clock_core{clock_core_} { - static const FunctionInfo functions[] = { - {0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"}, - {2, nullptr, "GetTestOffset"}, - {3, nullptr, "SetTestOffset"}, - {100, nullptr, "GetRtcValue"}, - {101, nullptr, "IsRtcResetDetected"}, - {102, nullptr, "GetSetupResultValue"}, - {200, nullptr, "GetInternalOffset"}, - {201, nullptr, "SetInternalOffset"}, - }; - RegisterHandlers(functions); - } - -private: - void GetCurrentTimePoint(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - if (!clock_core.IsInitialized()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_UNINITIALIZED_CLOCK); - return; - } - - const Clock::SteadyClockTimePoint time_point{clock_core.GetCurrentTimePoint(system)}; - IPC::ResponseBuilder rb{ctx, (sizeof(Clock::SteadyClockTimePoint) / 4) + 2}; - rb.Push(ResultSuccess); - rb.PushRaw(time_point); - } - - Clock::SteadyClockCore& clock_core; -}; - -Result Module::Interface::GetClockSnapshotFromSystemClockContextInternal( - Kernel::KThread* thread, Clock::SystemClockContext user_context, - Clock::SystemClockContext network_context, Clock::TimeType type, - Clock::ClockSnapshot& clock_snapshot) { - - auto& time_manager{system.GetTimeManager()}; - - clock_snapshot.steady_clock_time_point = - time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system); - clock_snapshot.is_automatic_correction_enabled = - time_manager.GetStandardUserSystemClockCore().IsAutomaticCorrectionEnabled(); - clock_snapshot.type = type; - - if (const Result result{ - time_manager.GetTimeZoneContentManager().GetTimeZoneManager().GetDeviceLocationName( - clock_snapshot.location_name)}; - result != ResultSuccess) { - return result; - } - - clock_snapshot.user_context = user_context; - - if (const Result result{Clock::ClockSnapshot::GetCurrentTime( - clock_snapshot.user_time, clock_snapshot.steady_clock_time_point, - clock_snapshot.user_context)}; - result != ResultSuccess) { - return result; - } - - TimeZone::CalendarInfo userCalendarInfo{}; - if (const Result result{ - time_manager.GetTimeZoneContentManager().GetTimeZoneManager().ToCalendarTimeWithMyRules( - clock_snapshot.user_time, userCalendarInfo)}; - result != ResultSuccess) { - return result; - } - - clock_snapshot.user_calendar_time = userCalendarInfo.time; - clock_snapshot.user_calendar_additional_time = userCalendarInfo.additional_info; - - clock_snapshot.network_context = network_context; - - if (Clock::ClockSnapshot::GetCurrentTime(clock_snapshot.network_time, - clock_snapshot.steady_clock_time_point, - clock_snapshot.network_context) != ResultSuccess) { - clock_snapshot.network_time = 0; - } - - TimeZone::CalendarInfo networkCalendarInfo{}; - if (const Result result{ - time_manager.GetTimeZoneContentManager().GetTimeZoneManager().ToCalendarTimeWithMyRules( - clock_snapshot.network_time, networkCalendarInfo)}; - result != ResultSuccess) { - return result; - } - - clock_snapshot.network_calendar_time = networkCalendarInfo.time; - clock_snapshot.network_calendar_additional_time = networkCalendarInfo.additional_info; - - return ResultSuccess; -} - -void Module::Interface::GetStandardUserSystemClock(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardUserSystemClockCore(), - system); -} - -void Module::Interface::GetStandardNetworkSystemClock(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardNetworkSystemClockCore(), - system); -} - -void Module::Interface::GetStandardSteadyClock(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ISteadyClock>(system.GetTimeManager().GetStandardSteadyClockCore(), system); -} - -void Module::Interface::GetTimeZoneService(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ITimeZoneService>(system, - system.GetTimeManager().GetTimeZoneContentManager()); -} - -void Module::Interface::GetStandardLocalSystemClock(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardLocalSystemClockCore(), - system); -} - -void Module::Interface::IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - auto& clock_core{system.GetTimeManager().GetStandardNetworkSystemClockCore()}; - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(clock_core.IsStandardNetworkSystemClockAccuracySufficient(system)); -} - -void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - auto& steady_clock_core{system.GetTimeManager().GetStandardSteadyClockCore()}; - if (!steady_clock_core.IsInitialized()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_UNINITIALIZED_CLOCK); - return; - } - - IPC::RequestParser rp{ctx}; - const auto context{rp.PopRaw<Clock::SystemClockContext>()}; - const auto current_time_point{steady_clock_core.GetCurrentTimePoint(system)}; - - if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) { - const auto ticks{Clock::TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>( - system.CoreTiming().GetClockTicks())}; - const s64 base_time_point{context.offset + current_time_point.time_point - - ticks.ToSeconds()}; - IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2}; - rb.Push(ResultSuccess); - rb.PushRaw(base_time_point); - return; - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_TIME_MISMATCH); -} - -void Module::Interface::GetClockSnapshot(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto type{rp.PopEnum<Clock::TimeType>()}; - - LOG_DEBUG(Service_Time, "called, type={}", type); - - Clock::SystemClockContext user_context{}; - if (const Result result{ - system.GetTimeManager().GetStandardUserSystemClockCore().GetClockContext(system, - user_context)}; - result.IsError()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - Clock::SystemClockContext network_context{}; - if (const Result result{ - system.GetTimeManager().GetStandardNetworkSystemClockCore().GetClockContext( - system, network_context)}; - result.IsError()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - Clock::ClockSnapshot clock_snapshot{}; - if (const Result result{GetClockSnapshotFromSystemClockContextInternal( - &ctx.GetThread(), user_context, network_context, type, clock_snapshot)}; - result.IsError()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - ctx.WriteBuffer(clock_snapshot); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Module::Interface::GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto type{rp.PopEnum<Clock::TimeType>()}; - - rp.Skip(1, false); - - const Clock::SystemClockContext user_context{rp.PopRaw<Clock::SystemClockContext>()}; - const Clock::SystemClockContext network_context{rp.PopRaw<Clock::SystemClockContext>()}; - - LOG_DEBUG(Service_Time, "called, type={}", type); - - Clock::ClockSnapshot clock_snapshot{}; - if (const Result result{GetClockSnapshotFromSystemClockContextInternal( - &ctx.GetThread(), user_context, network_context, type, clock_snapshot)}; - result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - ctx.WriteBuffer(clock_snapshot); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - Clock::ClockSnapshot snapshot_a; - Clock::ClockSnapshot snapshot_b; - - const auto snapshot_a_data = ctx.ReadBuffer(0); - const auto snapshot_b_data = ctx.ReadBuffer(1); - - std::memcpy(&snapshot_a, snapshot_a_data.data(), sizeof(Clock::ClockSnapshot)); - std::memcpy(&snapshot_b, snapshot_b_data.data(), sizeof(Clock::ClockSnapshot)); - - auto time_span_type{Clock::TimeSpanType::FromSeconds(snapshot_b.user_context.offset - - snapshot_a.user_context.offset)}; - - if ((snapshot_b.user_context.steady_time_point.clock_source_id != - snapshot_a.user_context.steady_time_point.clock_source_id) || - (snapshot_b.is_automatic_correction_enabled && - snapshot_a.is_automatic_correction_enabled)) { - time_span_type.nanoseconds = 0; - } - - IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2}; - rb.Push(ResultSuccess); - rb.PushRaw(time_span_type.nanoseconds); -} - -void Module::Interface::CalculateSpanBetween(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - Clock::ClockSnapshot snapshot_a; - Clock::ClockSnapshot snapshot_b; - - const auto snapshot_a_data = ctx.ReadBuffer(0); - const auto snapshot_b_data = ctx.ReadBuffer(1); - - std::memcpy(&snapshot_a, snapshot_a_data.data(), sizeof(Clock::ClockSnapshot)); - std::memcpy(&snapshot_b, snapshot_b_data.data(), sizeof(Clock::ClockSnapshot)); - - Clock::TimeSpanType time_span_type{}; - s64 span{}; - - if (const Result result{snapshot_a.steady_clock_time_point.GetSpanBetween( - snapshot_b.steady_clock_time_point, span)}; - result != ResultSuccess) { - if (snapshot_a.network_time && snapshot_b.network_time) { - time_span_type = - Clock::TimeSpanType::FromSeconds(snapshot_b.network_time - snapshot_a.network_time); - } else { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_TIME_NOT_FOUND); - return; - } - } else { - time_span_type = Clock::TimeSpanType::FromSeconds(span); - } - - IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2}; - rb.Push(ResultSuccess); - rb.PushRaw(time_span_type.nanoseconds); -} - -void Module::Interface::GetSharedMemoryNativeHandle(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(&system.Kernel().GetTimeSharedMem()); -} - -Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_, - const char* name) - : ServiceFramework{system_, name}, module{std::move(module_)} {} - -Module::Interface::~Interface() = default; - -void LoopProcess(Core::System& system) { - auto server_manager = std::make_unique<ServerManager>(system); - auto module{std::make_shared<Module>()}; - - server_manager->RegisterNamedService("time:a", - std::make_shared<Time>(module, system, "time:a")); - server_manager->RegisterNamedService("time:s", - std::make_shared<Time>(module, system, "time:s")); - server_manager->RegisterNamedService("time:u", - std::make_shared<Time>(module, system, "time:u")); - ServerManager::RunServer(std::move(server_manager)); -} - -} // namespace Service::Time diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h deleted file mode 100644 index b2d754ef3..000000000 --- a/src/core/hle/service/time/time.h +++ /dev/null @@ -1,51 +0,0 @@ -// 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/time/clock_types.h" - -namespace Core { -class System; -} - -namespace Service::Time { - -class Module final { -public: - Module() = default; - - class Interface : public ServiceFramework<Interface> { - public: - explicit Interface(std::shared_ptr<Module> module_, Core::System& system_, - const char* name); - ~Interface() override; - - void GetStandardUserSystemClock(HLERequestContext& ctx); - void GetStandardNetworkSystemClock(HLERequestContext& ctx); - void GetStandardSteadyClock(HLERequestContext& ctx); - void GetTimeZoneService(HLERequestContext& ctx); - void GetStandardLocalSystemClock(HLERequestContext& ctx); - void IsStandardNetworkSystemClockAccuracySufficient(HLERequestContext& ctx); - void CalculateMonotonicSystemClockBaseTimePoint(HLERequestContext& ctx); - void GetClockSnapshot(HLERequestContext& ctx); - void GetClockSnapshotFromSystemClockContext(HLERequestContext& ctx); - void CalculateStandardUserSystemClockDifferenceByUser(HLERequestContext& ctx); - void CalculateSpanBetween(HLERequestContext& ctx); - void GetSharedMemoryNativeHandle(HLERequestContext& ctx); - - private: - Result GetClockSnapshotFromSystemClockContextInternal( - Kernel::KThread* thread, Clock::SystemClockContext user_context, - Clock::SystemClockContext network_context, Clock::TimeType type, - Clock::ClockSnapshot& cloc_snapshot); - - protected: - std::shared_ptr<Module> module; - }; -}; - -void LoopProcess(Core::System& system); - -} // namespace Service::Time diff --git a/src/core/hle/service/time/time_interface.cpp b/src/core/hle/service/time/time_interface.cpp deleted file mode 100644 index 0c53e98ee..000000000 --- a/src/core/hle/service/time/time_interface.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/hle/service/time/time_interface.h" - -namespace Service::Time { - -Time::Time(std::shared_ptr<Module> module_, Core::System& system_, const char* name_) - : Interface{std::move(module_), system_, name_} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"}, - {1, &Time::GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"}, - {2, &Time::GetStandardSteadyClock, "GetStandardSteadyClock"}, - {3, &Time::GetTimeZoneService, "GetTimeZoneService"}, - {4, &Time::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"}, - {5, nullptr, "GetEphemeralNetworkSystemClock"}, - {20, &Time::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}, - {30, nullptr, "GetStandardNetworkClockOperationEventReadableHandle"}, - {31, nullptr, "GetEphemeralNetworkClockOperationEventReadableHandle"}, - {50, nullptr, "SetStandardSteadyClockInternalOffset"}, - {51, nullptr, "GetStandardSteadyClockRtcValue"}, - {100, nullptr, "IsStandardUserSystemClockAutomaticCorrectionEnabled"}, - {101, nullptr, "SetStandardUserSystemClockAutomaticCorrectionEnabled"}, - {102, nullptr, "GetStandardUserSystemClockInitialYear"}, - {200, &Time::IsStandardNetworkSystemClockAccuracySufficient, "IsStandardNetworkSystemClockAccuracySufficient"}, - {201, nullptr, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"}, - {300, &Time::CalculateMonotonicSystemClockBaseTimePoint, "CalculateMonotonicSystemClockBaseTimePoint"}, - {400, &Time::GetClockSnapshot, "GetClockSnapshot"}, - {401, &Time::GetClockSnapshotFromSystemClockContext, "GetClockSnapshotFromSystemClockContext"}, - {500, &Time::CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"}, - {501, &Time::CalculateSpanBetween, "CalculateSpanBetween"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -Time::~Time() = default; - -} // namespace Service::Time diff --git a/src/core/hle/service/time/time_interface.h b/src/core/hle/service/time/time_interface.h deleted file mode 100644 index ceeb0e5ef..000000000 --- a/src/core/hle/service/time/time_interface.h +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "core/hle/service/time/time.h" - -namespace Core { -class System; -} - -namespace Service::Time { - -class Time final : public Module::Interface { -public: - explicit Time(std::shared_ptr<Module> time, Core::System& system_, const char* name_); - ~Time() override; -}; - -} // namespace Service::Time diff --git a/src/core/hle/service/time/time_manager.cpp b/src/core/hle/service/time/time_manager.cpp deleted file mode 100644 index fa0fd0531..000000000 --- a/src/core/hle/service/time/time_manager.cpp +++ /dev/null @@ -1,293 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <chrono> -#include <ctime> - -#include "common/settings.h" -#include "common/time_zone.h" -#include "core/hle/service/time/ephemeral_network_system_clock_context_writer.h" -#include "core/hle/service/time/ephemeral_network_system_clock_core.h" -#include "core/hle/service/time/local_system_clock_context_writer.h" -#include "core/hle/service/time/network_system_clock_context_writer.h" -#include "core/hle/service/time/tick_based_steady_clock_core.h" -#include "core/hle/service/time/time_manager.h" - -namespace Service::Time { -namespace { -constexpr Clock::TimeSpanType standard_network_clock_accuracy{0x0009356907420000ULL}; - -s64 GetSecondsSinceEpoch() { - const auto time_since_epoch = std::chrono::system_clock::now().time_since_epoch(); - return std::chrono::duration_cast<std::chrono::seconds>(time_since_epoch).count() + - Settings::values.custom_rtc_differential; -} -} // Anonymous namespace - -struct TimeManager::Impl final { - explicit Impl(Core::System& system) - : shared_memory{system}, standard_local_system_clock_core{standard_steady_clock_core}, - standard_network_system_clock_core{standard_steady_clock_core}, - standard_user_system_clock_core{standard_local_system_clock_core, - standard_network_system_clock_core, system}, - ephemeral_network_system_clock_core{tick_based_steady_clock_core}, - local_system_clock_context_writer{ - std::make_shared<Clock::LocalSystemClockContextWriter>(shared_memory)}, - network_system_clock_context_writer{ - std::make_shared<Clock::NetworkSystemClockContextWriter>(shared_memory)}, - ephemeral_network_system_clock_context_writer{ - std::make_shared<Clock::EphemeralNetworkSystemClockContextWriter>()}, - time_zone_content_manager{system} { - - const auto system_time{Clock::TimeSpanType::FromSeconds(GetSecondsSinceEpoch())}; - SetupStandardSteadyClock(system, Common::UUID::MakeRandom(), system_time, {}, {}); - SetupStandardLocalSystemClock(system, {}, system_time.ToSeconds()); - - Clock::SystemClockContext clock_context{}; - standard_local_system_clock_core.GetClockContext(system, clock_context); - - SetupStandardNetworkSystemClock(clock_context, standard_network_clock_accuracy); - SetupStandardUserSystemClock(system, {}, Clock::SteadyClockTimePoint::GetRandom()); - SetupEphemeralNetworkSystemClock(); - } - - ~Impl() = default; - - Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() { - return standard_steady_clock_core; - } - - const Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() const { - return standard_steady_clock_core; - } - - Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() { - return standard_local_system_clock_core; - } - - const Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() const { - return standard_local_system_clock_core; - } - - Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() { - return standard_network_system_clock_core; - } - - const Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() const { - return standard_network_system_clock_core; - } - - Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() { - return standard_user_system_clock_core; - } - - const Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() const { - return standard_user_system_clock_core; - } - - TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() { - return time_zone_content_manager; - } - - const TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() const { - return time_zone_content_manager; - } - - SharedMemory& GetSharedMemory() { - return shared_memory; - } - - const SharedMemory& GetSharedMemory() const { - return shared_memory; - } - - void SetupTimeZoneManager(std::string location_name, - Clock::SteadyClockTimePoint time_zone_updated_time_point, - std::vector<std::string> location_names, u128 time_zone_rule_version, - FileSys::VirtualFile& vfs_file) { - if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule( - location_name, vfs_file) != ResultSuccess) { - ASSERT(false); - return; - } - - time_zone_content_manager.GetTimeZoneManager().SetUpdatedTime(time_zone_updated_time_point); - time_zone_content_manager.GetTimeZoneManager().SetTotalLocationNameCount( - location_names.size()); - time_zone_content_manager.GetTimeZoneManager().SetLocationNames(location_names); - time_zone_content_manager.GetTimeZoneManager().SetTimeZoneRuleVersion( - time_zone_rule_version); - time_zone_content_manager.GetTimeZoneManager().MarkAsInitialized(); - } - - void SetupStandardSteadyClock(Core::System& system_, Common::UUID clock_source_id, - Clock::TimeSpanType setup_value, - Clock::TimeSpanType internal_offset, bool is_rtc_reset_detected) { - standard_steady_clock_core.SetClockSourceId(clock_source_id); - standard_steady_clock_core.SetSetupValue(setup_value); - standard_steady_clock_core.SetInternalOffset(internal_offset); - standard_steady_clock_core.MarkAsInitialized(); - - const auto current_time_point{standard_steady_clock_core.GetCurrentRawTimePoint(system_)}; - shared_memory.SetupStandardSteadyClock(clock_source_id, current_time_point); - } - - void SetupStandardLocalSystemClock(Core::System& system_, - Clock::SystemClockContext clock_context, s64 posix_time) { - standard_local_system_clock_core.SetUpdateCallbackInstance( - local_system_clock_context_writer); - - const auto current_time_point{ - standard_local_system_clock_core.GetSteadyClockCore().GetCurrentTimePoint(system_)}; - if (current_time_point.clock_source_id == clock_context.steady_time_point.clock_source_id) { - standard_local_system_clock_core.SetSystemClockContext(clock_context); - } else { - if (standard_local_system_clock_core.SetCurrentTime(system_, posix_time) != - ResultSuccess) { - ASSERT(false); - return; - } - } - - standard_local_system_clock_core.MarkAsInitialized(); - } - - void SetupStandardNetworkSystemClock(Clock::SystemClockContext clock_context, - Clock::TimeSpanType sufficient_accuracy) { - standard_network_system_clock_core.SetUpdateCallbackInstance( - network_system_clock_context_writer); - - if (standard_network_system_clock_core.SetSystemClockContext(clock_context) != - ResultSuccess) { - ASSERT(false); - return; - } - - standard_network_system_clock_core.SetStandardNetworkClockSufficientAccuracy( - sufficient_accuracy); - standard_network_system_clock_core.MarkAsInitialized(); - } - - void SetupStandardUserSystemClock(Core::System& system_, bool is_automatic_correction_enabled, - Clock::SteadyClockTimePoint steady_clock_time_point) { - if (standard_user_system_clock_core.SetAutomaticCorrectionEnabled( - system_, is_automatic_correction_enabled) != ResultSuccess) { - ASSERT(false); - return; - } - - standard_user_system_clock_core.SetAutomaticCorrectionUpdatedTime(steady_clock_time_point); - standard_user_system_clock_core.MarkAsInitialized(); - shared_memory.SetAutomaticCorrectionEnabled(is_automatic_correction_enabled); - } - - void SetupEphemeralNetworkSystemClock() { - ephemeral_network_system_clock_core.SetUpdateCallbackInstance( - ephemeral_network_system_clock_context_writer); - ephemeral_network_system_clock_core.MarkAsInitialized(); - } - - void UpdateLocalSystemClockTime(Core::System& system_, s64 posix_time) { - const auto timespan{Clock::TimeSpanType::FromSeconds(posix_time)}; - if (GetStandardLocalSystemClockCore() - .SetCurrentTime(system_, timespan.ToSeconds()) - .IsError()) { - ASSERT(false); - return; - } - } - - SharedMemory shared_memory; - - Clock::StandardSteadyClockCore standard_steady_clock_core; - Clock::TickBasedSteadyClockCore tick_based_steady_clock_core; - Clock::StandardLocalSystemClockCore standard_local_system_clock_core; - Clock::StandardNetworkSystemClockCore standard_network_system_clock_core; - Clock::StandardUserSystemClockCore standard_user_system_clock_core; - Clock::EphemeralNetworkSystemClockCore ephemeral_network_system_clock_core; - - std::shared_ptr<Clock::LocalSystemClockContextWriter> local_system_clock_context_writer; - std::shared_ptr<Clock::NetworkSystemClockContextWriter> network_system_clock_context_writer; - std::shared_ptr<Clock::EphemeralNetworkSystemClockContextWriter> - ephemeral_network_system_clock_context_writer; - - TimeZone::TimeZoneContentManager time_zone_content_manager; -}; - -TimeManager::TimeManager(Core::System& system_) : system{system_} {} - -TimeManager::~TimeManager() = default; - -void TimeManager::Initialize() { - impl = std::make_unique<Impl>(system); - - // Time zones can only be initialized after impl is valid - impl->time_zone_content_manager.Initialize(*this); -} - -Clock::StandardSteadyClockCore& TimeManager::GetStandardSteadyClockCore() { - return impl->standard_steady_clock_core; -} - -const Clock::StandardSteadyClockCore& TimeManager::GetStandardSteadyClockCore() const { - return impl->standard_steady_clock_core; -} - -Clock::StandardLocalSystemClockCore& TimeManager::GetStandardLocalSystemClockCore() { - return impl->standard_local_system_clock_core; -} - -const Clock::StandardLocalSystemClockCore& TimeManager::GetStandardLocalSystemClockCore() const { - return impl->standard_local_system_clock_core; -} - -Clock::StandardNetworkSystemClockCore& TimeManager::GetStandardNetworkSystemClockCore() { - return impl->standard_network_system_clock_core; -} - -const Clock::StandardNetworkSystemClockCore& TimeManager::GetStandardNetworkSystemClockCore() - const { - return impl->standard_network_system_clock_core; -} - -Clock::StandardUserSystemClockCore& TimeManager::GetStandardUserSystemClockCore() { - return impl->standard_user_system_clock_core; -} - -const Clock::StandardUserSystemClockCore& TimeManager::GetStandardUserSystemClockCore() const { - return impl->standard_user_system_clock_core; -} - -TimeZone::TimeZoneContentManager& TimeManager::GetTimeZoneContentManager() { - return impl->time_zone_content_manager; -} - -const TimeZone::TimeZoneContentManager& TimeManager::GetTimeZoneContentManager() const { - return impl->time_zone_content_manager; -} - -SharedMemory& TimeManager::GetSharedMemory() { - return impl->shared_memory; -} - -const SharedMemory& TimeManager::GetSharedMemory() const { - return impl->shared_memory; -} - -void TimeManager::Shutdown() { - impl.reset(); -} - -void TimeManager::UpdateLocalSystemClockTime(s64 posix_time) { - impl->UpdateLocalSystemClockTime(system, posix_time); -} - -void TimeManager::SetupTimeZoneManager(std::string location_name, - Clock::SteadyClockTimePoint time_zone_updated_time_point, - std::vector<std::string> location_names, - u128 time_zone_rule_version, - FileSys::VirtualFile& vfs_file) { - impl->SetupTimeZoneManager(location_name, time_zone_updated_time_point, location_names, - time_zone_rule_version, vfs_file); -} -} // namespace Service::Time diff --git a/src/core/hle/service/time/time_manager.h b/src/core/hle/service/time/time_manager.h deleted file mode 100644 index 84572dbfa..000000000 --- a/src/core/hle/service/time/time_manager.h +++ /dev/null @@ -1,74 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/common_types.h" -#include "core/file_sys/vfs_types.h" -#include "core/hle/service/time/clock_types.h" -#include "core/hle/service/time/standard_local_system_clock_core.h" -#include "core/hle/service/time/standard_network_system_clock_core.h" -#include "core/hle/service/time/standard_steady_clock_core.h" -#include "core/hle/service/time/standard_user_system_clock_core.h" -#include "core/hle/service/time/time_sharedmemory.h" -#include "core/hle/service/time/time_zone_content_manager.h" - -namespace Service::Time { - -namespace Clock { -class EphemeralNetworkSystemClockContextWriter; -class LocalSystemClockContextWriter; -class NetworkSystemClockContextWriter; -} // namespace Clock - -// Parts of this implementation were based on Ryujinx (https://github.com/Ryujinx/Ryujinx/pull/783). -// This code was released under public domain. - -class TimeManager final { -public: - explicit TimeManager(Core::System& system_); - ~TimeManager(); - - void Initialize(); - - Clock::StandardSteadyClockCore& GetStandardSteadyClockCore(); - - const Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() const; - - Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore(); - - const Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() const; - - Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore(); - - const Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() const; - - Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore(); - - const Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() const; - - TimeZone::TimeZoneContentManager& GetTimeZoneContentManager(); - - const TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() const; - - void UpdateLocalSystemClockTime(s64 posix_time); - - SharedMemory& GetSharedMemory(); - - const SharedMemory& GetSharedMemory() const; - - void Shutdown(); - - void SetupTimeZoneManager(std::string location_name, - Clock::SteadyClockTimePoint time_zone_updated_time_point, - std::vector<std::string> location_names, u128 time_zone_rule_version, - FileSys::VirtualFile& vfs_file); - -private: - Core::System& system; - - struct Impl; - std::unique_ptr<Impl> impl; -}; - -} // namespace Service::Time diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp deleted file mode 100644 index a00676669..000000000 --- a/src/core/hle/service/time/time_sharedmemory.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "core/core.h" -#include "core/core_timing.h" -#include "core/hardware_properties.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/service/time/clock_types.h" -#include "core/hle/service/time/steady_clock_core.h" -#include "core/hle/service/time/time_sharedmemory.h" - -namespace Service::Time { - -static constexpr std::size_t SHARED_MEMORY_SIZE{0x1000}; - -SharedMemory::SharedMemory(Core::System& system_) : system(system_) { - std::memset(system.Kernel().GetTimeSharedMem().GetPointer(), 0, SHARED_MEMORY_SIZE); -} - -SharedMemory::~SharedMemory() = default; - -void SharedMemory::SetupStandardSteadyClock(const Common::UUID& clock_source_id, - Clock::TimeSpanType current_time_point) { - const Clock::TimeSpanType ticks_time_span{ - Clock::TimeSpanType::FromTicks<Core::Hardware::CNTFREQ>( - system.CoreTiming().GetClockTicks())}; - const Clock::SteadyClockContext context{ - static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds), - clock_source_id}; - StoreToLockFreeAtomicType(&GetFormat()->standard_steady_clock_timepoint, context); -} - -void SharedMemory::UpdateLocalSystemClockContext(const Clock::SystemClockContext& context) { - // lower and upper are related to the measurement point for the steady time point, - // and compare equal on boot - const s64 time_point_ns = context.steady_time_point.time_point * 1'000'000'000LL; - - // This adjusts for some sort of time skew - // Both 0 on boot - const s64 diff_scale = 0; - const u32 shift_amount = 0; - - const Clock::ContinuousAdjustmentTimePoint adjustment{ - .measurement_offset = system.CoreTiming().GetGlobalTimeNs().count(), - .diff_scale = diff_scale, - .shift_amount = shift_amount, - .lower = time_point_ns, - .upper = time_point_ns, - .clock_source_id = context.steady_time_point.clock_source_id, - }; - - StoreToLockFreeAtomicType(&GetFormat()->continuous_adjustment_timepoint, adjustment); - StoreToLockFreeAtomicType(&GetFormat()->standard_local_system_clock_context, context); -} - -void SharedMemory::UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context) { - StoreToLockFreeAtomicType(&GetFormat()->standard_network_system_clock_context, context); -} - -void SharedMemory::SetAutomaticCorrectionEnabled(bool is_enabled) { - StoreToLockFreeAtomicType( - &GetFormat()->is_standard_user_system_clock_automatic_correction_enabled, is_enabled); -} - -SharedMemory::Format* SharedMemory::GetFormat() { - return reinterpret_cast<SharedMemory::Format*>(system.Kernel().GetTimeSharedMem().GetPointer()); -} - -} // namespace Service::Time diff --git a/src/core/hle/service/time/time_sharedmemory.h b/src/core/hle/service/time/time_sharedmemory.h deleted file mode 100644 index c89be9765..000000000 --- a/src/core/hle/service/time/time_sharedmemory.h +++ /dev/null @@ -1,89 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/common_types.h" -#include "common/uuid.h" -#include "core/hle/kernel/k_shared_memory.h" -#include "core/hle/service/time/clock_types.h" - -namespace Service::Time { - -// Note: this type is not safe for concurrent writes. -template <typename T> -struct LockFreeAtomicType { - u32 counter_; - std::array<T, 2> value_; -}; - -template <typename T> -static inline void StoreToLockFreeAtomicType(LockFreeAtomicType<T>* p, const T& value) { - // Get the current counter. - auto counter = p->counter_; - - // Increment the counter. - ++counter; - - // Store the updated value. - p->value_[counter % 2] = value; - - // Fence memory. - std::atomic_thread_fence(std::memory_order_release); - - // Set the updated counter. - p->counter_ = counter; -} - -template <typename T> -static inline T LoadFromLockFreeAtomicType(const LockFreeAtomicType<T>* p) { - while (true) { - // Get the counter. - auto counter = p->counter_; - - // Get the value. - auto value = p->value_[counter % 2]; - - // Fence memory. - std::atomic_thread_fence(std::memory_order_acquire); - - // Check that the counter matches. - if (counter == p->counter_) { - return value; - } - } -} - -class SharedMemory final { -public: - explicit SharedMemory(Core::System& system_); - ~SharedMemory(); - - // Shared memory format - struct Format { - LockFreeAtomicType<Clock::StandardSteadyClockTimePointType> standard_steady_clock_timepoint; - LockFreeAtomicType<Clock::SystemClockContext> standard_local_system_clock_context; - LockFreeAtomicType<Clock::SystemClockContext> standard_network_system_clock_context; - LockFreeAtomicType<bool> is_standard_user_system_clock_automatic_correction_enabled; - LockFreeAtomicType<Clock::ContinuousAdjustmentTimePoint> continuous_adjustment_timepoint; - }; - static_assert(offsetof(Format, standard_steady_clock_timepoint) == 0x0); - static_assert(offsetof(Format, standard_local_system_clock_context) == 0x38); - static_assert(offsetof(Format, standard_network_system_clock_context) == 0x80); - static_assert(offsetof(Format, is_standard_user_system_clock_automatic_correction_enabled) == - 0xc8); - static_assert(offsetof(Format, continuous_adjustment_timepoint) == 0xd0); - static_assert(sizeof(Format) == 0x148, "Format is an invalid size"); - - void SetupStandardSteadyClock(const Common::UUID& clock_source_id, - Clock::TimeSpanType current_time_point); - void UpdateLocalSystemClockContext(const Clock::SystemClockContext& context); - void UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context); - void SetAutomaticCorrectionEnabled(bool is_enabled); - Format* GetFormat(); - -private: - Core::System& system; -}; - -} // namespace Service::Time diff --git a/src/core/hle/service/time/time_zone_content_manager.cpp b/src/core/hle/service/time/time_zone_content_manager.cpp deleted file mode 100644 index 1b96de37a..000000000 --- a/src/core/hle/service/time/time_zone_content_manager.cpp +++ /dev/null @@ -1,151 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <chrono> -#include <sstream> -#include <utility> - -#include "common/logging/log.h" -#include "common/settings.h" -#include "common/time_zone.h" -#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/file_sys/vfs_types.h" -#include "core/hle/result.h" -#include "core/hle/service/filesystem/filesystem.h" -#include "core/hle/service/time/errors.h" -#include "core/hle/service/time/time_manager.h" -#include "core/hle/service/time/time_zone_content_manager.h" - -namespace Service::Time::TimeZone { - -constexpr u64 time_zone_binary_titleid{0x010000000000080E}; - -static FileSys::VirtualDir GetTimeZoneBinary(Core::System& system) { - const auto* nand{system.GetFileSystemController().GetSystemNANDContents()}; - const auto nca{nand->GetEntry(time_zone_binary_titleid, FileSys::ContentRecordType::Data)}; - - FileSys::VirtualFile romfs; - if (nca) { - romfs = nca->GetRomFS(); - } - - if (!romfs) { - romfs = FileSys::SystemArchive::SynthesizeSystemArchive(time_zone_binary_titleid); - } - - if (!romfs) { - LOG_ERROR(Service_Time, "Failed to find or synthesize {:016X!}", time_zone_binary_titleid); - return {}; - } - - return FileSys::ExtractRomFS(romfs); -} - -static std::vector<std::string> BuildLocationNameCache( - const FileSys::VirtualDir& time_zone_binary) { - if (!time_zone_binary) { - LOG_ERROR(Service_Time, "Failed to extract RomFS for {:016X}!", time_zone_binary_titleid); - return {}; - } - - const FileSys::VirtualFile binary_list{time_zone_binary->GetFile("binaryList.txt")}; - if (!binary_list) { - LOG_ERROR(Service_Time, "{:016X} has no file binaryList.txt!", time_zone_binary_titleid); - return {}; - } - - std::vector<char> raw_data(binary_list->GetSize() + 1); - binary_list->ReadBytes<char>(raw_data.data(), binary_list->GetSize()); - - std::stringstream data_stream{raw_data.data()}; - std::string name; - std::vector<std::string> location_name_cache; - while (std::getline(data_stream, name)) { - name.pop_back(); // Remove carriage return - location_name_cache.emplace_back(std::move(name)); - } - return location_name_cache; -} - -TimeZoneContentManager::TimeZoneContentManager(Core::System& system_) - : system{system_}, time_zone_binary{GetTimeZoneBinary(system)}, - location_name_cache{BuildLocationNameCache(time_zone_binary)} {} - -void TimeZoneContentManager::Initialize(TimeManager& time_manager) { - const auto timezone_setting = - Settings::GetTimeZoneString(Settings::values.time_zone_index.GetValue()); - - if (FileSys::VirtualFile vfs_file; - GetTimeZoneInfoFile(timezone_setting, vfs_file) == ResultSuccess) { - const auto time_point{ - time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system)}; - time_manager.SetupTimeZoneManager(timezone_setting, time_point, location_name_cache, {}, - vfs_file); - } else { - time_zone_manager.MarkAsInitialized(); - } -} - -Result TimeZoneContentManager::LoadTimeZoneRule(TimeZoneRule& rules, - const std::string& location_name) const { - FileSys::VirtualFile vfs_file; - if (const Result result{GetTimeZoneInfoFile(location_name, vfs_file)}; - result != ResultSuccess) { - return result; - } - - return time_zone_manager.ParseTimeZoneRuleBinary(rules, vfs_file); -} - -bool TimeZoneContentManager::IsLocationNameValid(const std::string& location_name) const { - return std::find(location_name_cache.begin(), location_name_cache.end(), location_name) != - location_name_cache.end(); -} - -Result TimeZoneContentManager::GetTimeZoneInfoFile(const std::string& location_name, - FileSys::VirtualFile& vfs_file) const { - if (!IsLocationNameValid(location_name)) { - return ERROR_TIME_NOT_FOUND; - } - - if (!time_zone_binary) { - LOG_ERROR(Service_Time, "Failed to extract RomFS for {:016X}!", time_zone_binary_titleid); - return ERROR_TIME_NOT_FOUND; - } - - const FileSys::VirtualDir zoneinfo_dir{time_zone_binary->GetSubdirectory("zoneinfo")}; - if (!zoneinfo_dir) { - LOG_ERROR(Service_Time, "{:016X} has no directory zoneinfo!", time_zone_binary_titleid); - return ERROR_TIME_NOT_FOUND; - } - - vfs_file = zoneinfo_dir->GetFileRelative(location_name); - if (!vfs_file) { - LOG_WARNING(Service_Time, "{:016X} has no file \"{}\"! Using system timezone.", - time_zone_binary_titleid, location_name); - const std::string system_time_zone{Common::TimeZone::FindSystemTimeZone()}; - vfs_file = zoneinfo_dir->GetFile(system_time_zone); - } - - if (!vfs_file) { - LOG_WARNING(Service_Time, "{:016X} has no file \"{}\"! Using default timezone.", - time_zone_binary_titleid, location_name); - vfs_file = zoneinfo_dir->GetFile(Common::TimeZone::GetDefaultTimeZone()); - } - - if (!vfs_file) { - LOG_ERROR(Service_Time, "{:016X} has no file \"{}\"!", time_zone_binary_titleid, - location_name); - return ERROR_TIME_NOT_FOUND; - } - - return ResultSuccess; -} - -} // namespace Service::Time::TimeZone diff --git a/src/core/hle/service/time/time_zone_content_manager.h b/src/core/hle/service/time/time_zone_content_manager.h deleted file mode 100644 index a6f9698bc..000000000 --- a/src/core/hle/service/time/time_zone_content_manager.h +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <string> -#include <vector> - -#include "core/file_sys/vfs_types.h" -#include "core/hle/service/time/time_zone_manager.h" - -namespace Core { -class System; -} - -namespace Service::Time { -class TimeManager; -} - -namespace Service::Time::TimeZone { - -class TimeZoneContentManager final { -public: - explicit TimeZoneContentManager(Core::System& system_); - - void Initialize(TimeManager& time_manager); - - TimeZoneManager& GetTimeZoneManager() { - return time_zone_manager; - } - - const TimeZoneManager& GetTimeZoneManager() const { - return time_zone_manager; - } - - Result LoadTimeZoneRule(TimeZoneRule& rules, const std::string& location_name) const; - -private: - bool IsLocationNameValid(const std::string& location_name) const; - Result GetTimeZoneInfoFile(const std::string& location_name, - FileSys::VirtualFile& vfs_file) const; - - Core::System& system; - TimeZoneManager time_zone_manager; - const FileSys::VirtualDir time_zone_binary; - const std::vector<std::string> location_name_cache; -}; - -} // namespace Service::Time::TimeZone diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp deleted file mode 100644 index 205371a26..000000000 --- a/src/core/hle/service/time/time_zone_manager.cpp +++ /dev/null @@ -1,1182 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <climits> -#include <limits> - -#include "common/assert.h" -#include "common/logging/log.h" -#include "core/file_sys/content_archive.h" -#include "core/file_sys/nca_metadata.h" -#include "core/file_sys/registered_cache.h" -#include "core/hle/service/time/time_zone_manager.h" -#include "core/hle/service/time/time_zone_types.h" - -namespace Service::Time::TimeZone { - -static constexpr s32 epoch_year{1970}; -static constexpr s32 year_base{1900}; -static constexpr s32 epoch_week_day{4}; -static constexpr s32 seconds_per_minute{60}; -static constexpr s32 minutes_per_hour{60}; -static constexpr s32 hours_per_day{24}; -static constexpr s32 days_per_week{7}; -static constexpr s32 days_per_normal_year{365}; -static constexpr s32 days_per_leap_year{366}; -static constexpr s32 months_per_year{12}; -static constexpr s32 seconds_per_hour{seconds_per_minute * minutes_per_hour}; -static constexpr s32 seconds_per_day{seconds_per_hour * hours_per_day}; -static constexpr s32 years_per_repeat{400}; -static constexpr s64 average_seconds_per_year{31556952}; -static constexpr s64 seconds_per_repeat{years_per_repeat * average_seconds_per_year}; - -struct Rule { - enum class Type : u32 { JulianDay, DayOfYear, MonthNthDayOfWeek }; - Type rule_type{}; - s32 day{}; - s32 week{}; - s32 month{}; - s32 transition_time{}; -}; - -struct CalendarTimeInternal { - s64 year{}; - s8 month{}; - s8 day{}; - s8 hour{}; - s8 minute{}; - s8 second{}; - int Compare(const CalendarTimeInternal& other) const { - if (year != other.year) { - if (year < other.year) { - return -1; - } - return 1; - } - if (month != other.month) { - return month - other.month; - } - if (day != other.day) { - return day - other.day; - } - if (hour != other.hour) { - return hour - other.hour; - } - if (minute != other.minute) { - return minute - other.minute; - } - if (second != other.second) { - return second - other.second; - } - return {}; - } -}; - -template <typename TResult, typename TOperand> -static bool SafeAdd(TResult& result, TOperand op) { - result = result + op; - return true; -} - -template <typename TResult, typename TUnit, typename TBase> -static bool SafeNormalize(TResult& result, TUnit& unit, TBase base) { - TUnit delta{}; - if (unit >= 0) { - delta = unit / base; - } else { - delta = -1 - (-1 - unit) / base; - } - unit -= delta * base; - return SafeAdd(result, delta); -} - -template <typename T> -static constexpr bool IsLeapYear(T year) { - return ((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0); -} - -template <typename T> -static constexpr T GetYearLengthInDays(T year) { - return IsLeapYear(year) ? days_per_leap_year : days_per_normal_year; -} - -static constexpr s64 GetLeapDaysFromYearPositive(s64 year) { - return year / 4 - year / 100 + year / years_per_repeat; -} - -static constexpr s64 GetLeapDaysFromYear(s64 year) { - if (year < 0) { - return -1 - GetLeapDaysFromYearPositive(-1 - year); - } else { - return GetLeapDaysFromYearPositive(year); - } -} - -static constexpr s8 GetMonthLength(bool is_leap_year, int month) { - constexpr std::array<s8, 12> month_lengths{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - constexpr std::array<s8, 12> month_lengths_leap{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - return is_leap_year ? month_lengths_leap[month] : month_lengths[month]; -} - -static constexpr bool IsDigit(char value) { - return value >= '0' && value <= '9'; -} - -static constexpr int GetQZName(const char* name, int offset, char delimiter) { - while (name[offset] != '\0' && name[offset] != delimiter) { - offset++; - } - return offset; -} - -static constexpr int GetTZName(const char* name, int offset) { - char c; - - while ((c = name[offset]) != '\0' && !IsDigit(c) && c != ',' && c != '-' && c != '+') { - ++offset; - } - return offset; -} - -static constexpr bool GetInteger(const char* name, int& offset, int& value, int min, int max) { - value = 0; - char temp{name[offset]}; - if (!IsDigit(temp)) { - return {}; - } - do { - value = value * 10 + (temp - '0'); - if (value > max) { - return {}; - } - offset++; - temp = name[offset]; - } while (IsDigit(temp)); - - return value >= min; -} - -static constexpr bool GetSeconds(const char* name, int& offset, int& seconds) { - seconds = 0; - int value{}; - if (!GetInteger(name, offset, value, 0, hours_per_day * days_per_week - 1)) { - return {}; - } - seconds = value * seconds_per_hour; - - if (name[offset] == ':') { - offset++; - if (!GetInteger(name, offset, value, 0, minutes_per_hour - 1)) { - return {}; - } - seconds += value * seconds_per_minute; - if (name[offset] == ':') { - offset++; - if (!GetInteger(name, offset, value, 0, seconds_per_minute)) { - return {}; - } - seconds += value; - } - } - return true; -} - -static constexpr bool GetOffset(const char* name, int& offset, int& value) { - bool is_negative{}; - if (name[offset] == '-') { - is_negative = true; - offset++; - } else if (name[offset] == '+') { - offset++; - } - if (!GetSeconds(name, offset, value)) { - return {}; - } - if (is_negative) { - value = -value; - } - return true; -} - -static constexpr bool GetRule(const char* name, int& position, Rule& rule) { - bool is_valid{}; - if (name[position] == 'J') { - position++; - rule.rule_type = Rule::Type::JulianDay; - is_valid = GetInteger(name, position, rule.day, 1, days_per_normal_year); - } else if (name[position] == 'M') { - position++; - rule.rule_type = Rule::Type::MonthNthDayOfWeek; - is_valid = GetInteger(name, position, rule.month, 1, months_per_year); - if (!is_valid) { - return {}; - } - if (name[position++] != '.') { - return {}; - } - is_valid = GetInteger(name, position, rule.week, 1, 5); - if (!is_valid) { - return {}; - } - if (name[position++] != '.') { - return {}; - } - is_valid = GetInteger(name, position, rule.day, 0, days_per_week - 1); - } else if (isdigit(name[position])) { - rule.rule_type = Rule::Type::DayOfYear; - is_valid = GetInteger(name, position, rule.day, 0, days_per_leap_year - 1); - } else { - return {}; - } - if (!is_valid) { - return {}; - } - if (name[position] == '/') { - position++; - return GetOffset(name, position, rule.transition_time); - } else { - rule.transition_time = 2 * seconds_per_hour; - } - return true; -} - -static constexpr int TransitionTime(int year, Rule rule, int offset) { - int value{}; - switch (rule.rule_type) { - case Rule::Type::JulianDay: - value = (rule.day - 1) * seconds_per_day; - if (IsLeapYear(year) && rule.day >= 60) { - value += seconds_per_day; - } - break; - case Rule::Type::DayOfYear: - value = rule.day * seconds_per_day; - break; - case Rule::Type::MonthNthDayOfWeek: { - // Use Zeller's Congruence (https://en.wikipedia.org/wiki/Zeller%27s_congruence) to - // calculate the day of the week for any Julian or Gregorian calendar date. - const int m1{(rule.month + 9) % 12 + 1}; - const int yy0{(rule.month <= 2) ? (year - 1) : year}; - const int yy1{yy0 / 100}; - const int yy2{yy0 % 100}; - int day_of_week{((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7}; - - if (day_of_week < 0) { - day_of_week += days_per_week; - } - int day{rule.day - day_of_week}; - if (day < 0) { - day += days_per_week; - } - for (int i{1}; i < rule.week; i++) { - if (day + days_per_week >= GetMonthLength(IsLeapYear(year), rule.month - 1)) { - break; - } - day += days_per_week; - } - - value = day * seconds_per_day; - for (int index{}; index < rule.month - 1; ++index) { - value += GetMonthLength(IsLeapYear(year), index) * seconds_per_day; - } - break; - } - default: - ASSERT(false); - break; - } - return value + rule.transition_time + offset; -} - -static bool ParsePosixName(const char* name, TimeZoneRule& rule) { - static constexpr char default_rule[]{",M4.1.0,M10.5.0"}; - const char* std_name{name}; - int std_len{}; - int offset{}; - int std_offset{}; - - if (name[offset] == '<') { - offset++; - std_name = name + offset; - const int std_name_offset{offset}; - offset = GetQZName(name, offset, '>'); - if (name[offset] != '>') { - return {}; - } - std_len = offset - std_name_offset; - offset++; - } else { - offset = GetTZName(name, offset); - std_len = offset; - } - if (std_len == 0) { - return {}; - } - if (!GetOffset(name, offset, std_offset)) { - return {}; - } - - int char_count{std_len + 1}; - int dest_len{}; - int dest_offset{}; - const char* dest_name{name + offset}; - if (rule.chars.size() < std::size_t(char_count)) { - return {}; - } - - if (name[offset] != '\0') { - if (name[offset] == '<') { - dest_name = name + (++offset); - const int dest_name_offset{offset}; - offset = GetQZName(name, offset, '>'); - if (name[offset] != '>') { - return {}; - } - dest_len = offset - dest_name_offset; - offset++; - } else { - dest_name = name + (offset); - offset = GetTZName(name, offset); - dest_len = offset; - } - if (dest_len == 0) { - return {}; - } - char_count += dest_len + 1; - if (rule.chars.size() < std::size_t(char_count)) { - return {}; - } - if (name[offset] != '\0' && name[offset] != ',' && name[offset] != ';') { - if (!GetOffset(name, offset, dest_offset)) { - return {}; - } - } else { - dest_offset = std_offset - seconds_per_hour; - } - if (name[offset] == '\0') { - name = default_rule; - offset = 0; - } - if (name[offset] == ',' || name[offset] == ';') { - offset++; - - Rule start{}; - if (!GetRule(name, offset, start)) { - return {}; - } - if (name[offset++] != ',') { - return {}; - } - - Rule end{}; - if (!GetRule(name, offset, end)) { - return {}; - } - if (name[offset] != '\0') { - return {}; - } - - rule.type_count = 2; - rule.ttis[0].gmt_offset = -dest_offset; - rule.ttis[0].is_dst = true; - rule.ttis[0].abbreviation_list_index = std_len + 1; - rule.ttis[1].gmt_offset = -std_offset; - rule.ttis[1].is_dst = false; - rule.ttis[1].abbreviation_list_index = 0; - rule.default_type = 0; - - s64 jan_first{}; - int time_count{}; - int jan_offset{}; - int year_beginning{epoch_year}; - do { - const int year_seconds{GetYearLengthInDays(year_beginning - 1) * seconds_per_day}; - year_beginning--; - if (!SafeAdd(jan_first, -year_seconds)) { - jan_offset = -year_seconds; - break; - } - } while (epoch_year - years_per_repeat / 2 < year_beginning); - - int year_limit{year_beginning + years_per_repeat + 1}; - int year{}; - for (year = year_beginning; year < year_limit; year++) { - int start_time{TransitionTime(year, start, std_offset)}; - int end_time{TransitionTime(year, end, dest_offset)}; - const int year_seconds{GetYearLengthInDays(year) * seconds_per_day}; - const bool is_reversed{end_time < start_time}; - if (is_reversed) { - int swap{start_time}; - start_time = end_time; - end_time = swap; - } - - if (is_reversed || - (start_time < end_time && - (end_time - start_time < (year_seconds + (std_offset - dest_offset))))) { - if (rule.ats.size() - 2 < std::size_t(time_count)) { - break; - } - - rule.ats[time_count] = jan_first; - if (SafeAdd(rule.ats[time_count], jan_offset + start_time)) { - rule.types[time_count++] = is_reversed ? 1 : 0; - } else if (jan_offset != 0) { - rule.default_type = is_reversed ? 1 : 0; - } - - rule.ats[time_count] = jan_first; - if (SafeAdd(rule.ats[time_count], jan_offset + end_time)) { - rule.types[time_count++] = is_reversed ? 0 : 1; - year_limit = year + years_per_repeat + 1; - } else if (jan_offset != 0) { - rule.default_type = is_reversed ? 0 : 1; - } - } - if (!SafeAdd(jan_first, jan_offset + year_seconds)) { - break; - } - jan_offset = 0; - } - rule.time_count = time_count; - if (time_count == 0) { - rule.type_count = 1; - } else if (years_per_repeat < year - year_beginning) { - rule.go_back = true; - rule.go_ahead = true; - } - } else { - if (name[offset] == '\0') { - return {}; - } - - s64 their_std_offset{}; - for (int index{}; index < rule.time_count; ++index) { - const s8 type{rule.types[index]}; - if (rule.ttis[type].is_standard_time_daylight) { - their_std_offset = -rule.ttis[type].gmt_offset; - } - } - - s64 their_offset{their_std_offset}; - for (int index{}; index < rule.time_count; ++index) { - const s8 type{rule.types[index]}; - rule.types[index] = rule.ttis[type].is_dst ? 1 : 0; - if (!rule.ttis[type].is_gmt) { - if (!rule.ttis[type].is_standard_time_daylight) { - rule.ats[index] += dest_offset - their_std_offset; - } else { - rule.ats[index] += std_offset - their_std_offset; - } - } - their_offset = -rule.ttis[type].gmt_offset; - if (!rule.ttis[type].is_dst) { - their_std_offset = their_offset; - } - } - - if (rule.time_count > 0) { - UNIMPLEMENTED(); - // TODO (lat9nq): Implement eggert/tz/localtime.c:tzparse:1329 - // Seems to be unused in yuzu for now: I never hit the UNIMPLEMENTED in testing - } - - rule.ttis[0].gmt_offset = -std_offset; - rule.ttis[0].is_dst = false; - rule.ttis[0].abbreviation_list_index = 0; - rule.ttis[1].gmt_offset = -dest_offset; - rule.ttis[1].is_dst = true; - rule.ttis[1].abbreviation_list_index = std_len + 1; - rule.type_count = 2; - rule.default_type = 0; - } - } else { - // Default is standard time - rule.type_count = 1; - rule.time_count = 0; - rule.default_type = 0; - rule.ttis[0].gmt_offset = -std_offset; - rule.ttis[0].is_dst = false; - rule.ttis[0].abbreviation_list_index = 0; - } - - rule.char_count = char_count; - for (int index{}; index < std_len; ++index) { - rule.chars[index] = std_name[index]; - } - - rule.chars[std_len++] = '\0'; - if (dest_len != 0) { - for (int index{}; index < dest_len; ++index) { - rule.chars[std_len + index] = dest_name[index]; - } - rule.chars[std_len + dest_len] = '\0'; - } - - return true; -} - -static bool ParseTimeZoneBinary(TimeZoneRule& time_zone_rule, FileSys::VirtualFile& vfs_file) { - TzifHeader header{}; - if (vfs_file->ReadObject<TzifHeader>(&header) != sizeof(TzifHeader)) { - return {}; - } - - constexpr s32 time_zone_max_leaps{50}; - constexpr s32 time_zone_max_chars{50}; - constexpr s32 time_zone_max_times{1000}; - if (!(0 <= header.leap_count && header.leap_count < time_zone_max_leaps && - 0 < header.type_count && header.type_count < s32(time_zone_rule.ttis.size()) && - 0 <= header.time_count && header.time_count < s32(time_zone_rule.ats.size()) && - 0 <= header.char_count && header.char_count < time_zone_max_chars && - (header.ttis_std_count == header.type_count || header.ttis_std_count == 0) && - (header.ttis_gmt_count == header.type_count || header.ttis_gmt_count == 0))) { - return {}; - } - time_zone_rule.time_count = header.time_count; - time_zone_rule.type_count = header.type_count; - time_zone_rule.char_count = header.char_count; - - int time_count{}; - u64 read_offset = sizeof(TzifHeader); - for (int index{}; index < time_zone_rule.time_count; ++index) { - s64_be at{}; - vfs_file->ReadObject<s64_be>(&at, read_offset); - time_zone_rule.types[index] = 1; - if (time_count != 0 && at <= time_zone_rule.ats[time_count - 1]) { - if (at < time_zone_rule.ats[time_count - 1]) { - return {}; - } - time_zone_rule.types[index - 1] = 0; - time_count--; - } - time_zone_rule.ats[time_count++] = at; - read_offset += sizeof(s64_be); - } - time_count = 0; - for (int index{}; index < time_zone_rule.time_count; ++index) { - const u8 type{*vfs_file->ReadByte(read_offset)}; - read_offset += sizeof(u8); - if (time_zone_rule.type_count <= type) { - return {}; - } - if (time_zone_rule.types[index] != 0) { - time_zone_rule.types[time_count++] = type; - } - } - time_zone_rule.time_count = time_count; - for (int index{}; index < time_zone_rule.type_count; ++index) { - TimeTypeInfo& ttis{time_zone_rule.ttis[index]}; - u32_be gmt_offset{}; - vfs_file->ReadObject<u32_be>(&gmt_offset, read_offset); - read_offset += sizeof(u32_be); - ttis.gmt_offset = gmt_offset; - - const u8 dst{*vfs_file->ReadByte(read_offset)}; - read_offset += sizeof(u8); - if (dst >= 2) { - return {}; - } - ttis.is_dst = dst != 0; - - const s32 abbreviation_list_index{*vfs_file->ReadByte(read_offset)}; - read_offset += sizeof(u8); - if (abbreviation_list_index >= time_zone_rule.char_count) { - return {}; - } - ttis.abbreviation_list_index = abbreviation_list_index; - } - - vfs_file->ReadArray(time_zone_rule.chars.data(), time_zone_rule.char_count, read_offset); - time_zone_rule.chars[time_zone_rule.char_count] = '\0'; - read_offset += time_zone_rule.char_count; - for (int index{}; index < time_zone_rule.type_count; ++index) { - if (header.ttis_std_count == 0) { - time_zone_rule.ttis[index].is_standard_time_daylight = false; - } else { - const u8 dst{*vfs_file->ReadByte(read_offset)}; - read_offset += sizeof(u8); - if (dst >= 2) { - return {}; - } - time_zone_rule.ttis[index].is_standard_time_daylight = dst != 0; - } - } - - for (int index{}; index < time_zone_rule.type_count; ++index) { - if (header.ttis_std_count == 0) { - time_zone_rule.ttis[index].is_gmt = false; - } else { - const u8 dst{*vfs_file->ReadByte(read_offset)}; - read_offset += sizeof(u8); - if (dst >= 2) { - return {}; - } - time_zone_rule.ttis[index].is_gmt = dst != 0; - } - } - - const u64 position{(read_offset - sizeof(TzifHeader))}; - const s64 bytes_read = s64(vfs_file->GetSize() - sizeof(TzifHeader) - position); - if (bytes_read < 0) { - return {}; - } - constexpr s32 time_zone_name_max{255}; - if (bytes_read > (time_zone_name_max + 1)) { - return {}; - } - - std::array<char, time_zone_name_max + 1> temp_name{}; - vfs_file->ReadArray(temp_name.data(), bytes_read, read_offset); - if (bytes_read > 2 && temp_name[0] == '\n' && temp_name[bytes_read - 1] == '\n' && - std::size_t(time_zone_rule.type_count) + 2 <= time_zone_rule.ttis.size()) { - temp_name[bytes_read - 1] = '\0'; - - std::array<char, time_zone_name_max> name{}; - std::memcpy(name.data(), temp_name.data() + 1, std::size_t(bytes_read - 1)); - - // Fill in computed transition times with temp rule - TimeZoneRule temp_rule; - if (ParsePosixName(name.data(), temp_rule)) { - int have_abbreviation = 0; - int char_count = time_zone_rule.char_count; - - for (int i = 0; i < temp_rule.type_count; i++) { - char* temp_abbreviation = - temp_rule.chars.data() + temp_rule.ttis[i].abbreviation_list_index; - int j; - for (j = 0; j < char_count; j++) { - if (std::strcmp(time_zone_rule.chars.data() + j, temp_abbreviation) == 0) { - temp_rule.ttis[i].abbreviation_list_index = j; - have_abbreviation++; - break; - } - } - if (j >= char_count) { - int temp_abbreviation_length = static_cast<int>(std::strlen(temp_abbreviation)); - if (j + temp_abbreviation_length < time_zone_max_chars) { - std::strcpy(time_zone_rule.chars.data() + j, temp_abbreviation); - char_count = j + temp_abbreviation_length + 1; - temp_rule.ttis[i].abbreviation_list_index = j; - have_abbreviation++; - } - } - } - - if (have_abbreviation == temp_rule.type_count) { - time_zone_rule.char_count = char_count; - - // Original comment: - /* 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. */ - // This is possibly unnecessary for yuzu, since Nintendo doesn't run zic - while (1 < time_zone_rule.time_count && - (time_zone_rule.types[time_zone_rule.time_count - 1] == - time_zone_rule.types[time_zone_rule.time_count - 2])) { - time_zone_rule.time_count--; - } - - for (int i = 0; - i < temp_rule.time_count && time_zone_rule.time_count < time_zone_max_times; - i++) { - const s64 transition_time = temp_rule.ats[i]; - if (0 < time_zone_rule.time_count && - transition_time <= time_zone_rule.ats[time_zone_rule.time_count - 1]) { - continue; - } - - time_zone_rule.ats[time_zone_rule.time_count] = transition_time; - time_zone_rule.types[time_zone_rule.time_count] = - static_cast<s8>(time_zone_rule.type_count + temp_rule.types[i]); - time_zone_rule.time_count++; - } - for (int i = 0; i < temp_rule.type_count; i++) { - time_zone_rule.ttis[time_zone_rule.type_count++] = temp_rule.ttis[i]; - } - } - } - } - - const auto typesequiv = [](TimeZoneRule& rule, int a, int b) -> bool { - if (a < 0 || a >= rule.type_count || b < 0 || b >= rule.type_count) { - return {}; - } - - const struct TimeTypeInfo* ap = &rule.ttis[a]; - const struct TimeTypeInfo* bp = &rule.ttis[b]; - - return (ap->gmt_offset == bp->gmt_offset && ap->is_dst == bp->is_dst && - (std::strcmp(&rule.chars[ap->abbreviation_list_index], - &rule.chars[bp->abbreviation_list_index]) == 0)); - }; - - if (time_zone_rule.type_count == 0) { - return {}; - } - if (time_zone_rule.time_count > 1) { - if (time_zone_rule.ats[0] <= std::numeric_limits<s64>::max() - seconds_per_repeat) { - s64 repeatat = time_zone_rule.ats[0] + seconds_per_repeat; - int repeatattype = time_zone_rule.types[0]; - for (int i = 1; i < time_zone_rule.time_count; ++i) { - if (time_zone_rule.ats[i] == repeatat && - typesequiv(time_zone_rule, time_zone_rule.types[i], repeatattype)) { - time_zone_rule.go_back = true; - break; - } - } - } - if (std::numeric_limits<s64>::min() + seconds_per_repeat <= - time_zone_rule.ats[time_zone_rule.time_count - 1]) { - s64 repeatat = time_zone_rule.ats[time_zone_rule.time_count - 1] - seconds_per_repeat; - int repeatattype = time_zone_rule.types[time_zone_rule.time_count - 1]; - for (int i = time_zone_rule.time_count; i >= 0; --i) { - if (time_zone_rule.ats[i] == repeatat && - typesequiv(time_zone_rule, time_zone_rule.types[i], repeatattype)) { - time_zone_rule.go_ahead = true; - break; - } - } - } - } - - s32 default_type{}; - - for (default_type = 0; default_type < time_zone_rule.time_count; default_type++) { - if (time_zone_rule.types[default_type] == 0) { - break; - } - } - - default_type = default_type < time_zone_rule.time_count ? -1 : 0; - if (default_type < 0 && time_zone_rule.time_count > 0 && - time_zone_rule.ttis[time_zone_rule.types[0]].is_dst) { - default_type = time_zone_rule.types[0]; - while (--default_type >= 0) { - if (!time_zone_rule.ttis[default_type].is_dst) { - break; - } - } - } - if (default_type < 0) { - default_type = 0; - while (time_zone_rule.ttis[default_type].is_dst) { - if (++default_type >= time_zone_rule.type_count) { - default_type = 0; - break; - } - } - } - time_zone_rule.default_type = default_type; - return true; -} - -static Result CreateCalendarTime(s64 time, int gmt_offset, CalendarTimeInternal& calendar_time, - CalendarAdditionalInfo& calendar_additional_info) { - s64 year{epoch_year}; - s64 time_days{time / seconds_per_day}; - s64 remaining_seconds{time % seconds_per_day}; - while (time_days < 0 || time_days >= GetYearLengthInDays(year)) { - s64 delta = time_days / days_per_leap_year; - if (!delta) { - delta = time_days < 0 ? -1 : 1; - } - s64 new_year{year}; - if (!SafeAdd(new_year, delta)) { - return ERROR_OUT_OF_RANGE; - } - time_days -= (new_year - year) * days_per_normal_year; - time_days -= GetLeapDaysFromYear(new_year - 1) - GetLeapDaysFromYear(year - 1); - year = new_year; - } - - s64 day_of_year{time_days}; - remaining_seconds += gmt_offset; - while (remaining_seconds < 0) { - remaining_seconds += seconds_per_day; - day_of_year--; - } - - while (remaining_seconds >= seconds_per_day) { - remaining_seconds -= seconds_per_day; - day_of_year++; - } - - while (day_of_year < 0) { - if (!SafeAdd(year, -1)) { - return ERROR_OUT_OF_RANGE; - } - day_of_year += GetYearLengthInDays(year); - } - - while (day_of_year >= GetYearLengthInDays(year)) { - day_of_year -= GetYearLengthInDays(year); - if (!SafeAdd(year, 1)) { - return ERROR_OUT_OF_RANGE; - } - } - - calendar_time.year = year; - calendar_additional_info.day_of_year = static_cast<u32>(day_of_year); - s64 day_of_week{ - (epoch_week_day + - ((year - epoch_year) % days_per_week) * (days_per_normal_year % days_per_week) + - GetLeapDaysFromYear(year - 1) - GetLeapDaysFromYear(epoch_year - 1) + day_of_year) % - days_per_week}; - if (day_of_week < 0) { - day_of_week += days_per_week; - } - - calendar_additional_info.day_of_week = static_cast<u32>(day_of_week); - calendar_time.hour = static_cast<s8>((remaining_seconds / seconds_per_hour) % seconds_per_hour); - remaining_seconds %= seconds_per_hour; - calendar_time.minute = static_cast<s8>(remaining_seconds / seconds_per_minute); - calendar_time.second = static_cast<s8>(remaining_seconds % seconds_per_minute); - - for (calendar_time.month = 0; - day_of_year >= GetMonthLength(IsLeapYear(year), calendar_time.month); - ++calendar_time.month) { - day_of_year -= GetMonthLength(IsLeapYear(year), calendar_time.month); - } - - calendar_time.day = static_cast<s8>(day_of_year + 1); - calendar_additional_info.is_dst = false; - calendar_additional_info.gmt_offset = gmt_offset; - - return ResultSuccess; -} - -static Result ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time, - CalendarTimeInternal& calendar_time, - CalendarAdditionalInfo& calendar_additional_info) { - ASSERT(rules.go_ahead ? rules.time_count > 0 : true); - if ((rules.go_back && time < rules.ats[0]) || - (rules.go_ahead && time > rules.ats[rules.time_count - 1])) { - s64 seconds{}; - if (time < rules.ats[0]) { - seconds = rules.ats[0] - time; - } else { - seconds = time - rules.ats[rules.time_count - 1]; - } - seconds--; - - const s64 years{(seconds / seconds_per_repeat + 1) * years_per_repeat}; - seconds = years * average_seconds_per_year; - - s64 new_time{time}; - if (time < rules.ats[0]) { - new_time += seconds; - } else { - new_time -= seconds; - } - if (new_time < rules.ats[0] && new_time > rules.ats[rules.time_count - 1]) { - return ERROR_TIME_NOT_FOUND; - } - if (const Result result{ - ToCalendarTimeInternal(rules, new_time, calendar_time, calendar_additional_info)}; - result != ResultSuccess) { - return result; - } - if (time < rules.ats[0]) { - calendar_time.year -= years; - } else { - calendar_time.year += years; - } - - return ResultSuccess; - } - - s32 tti_index{}; - if (rules.time_count == 0 || time < rules.ats[0]) { - tti_index = rules.default_type; - } else { - s32 low{1}; - s32 high{rules.time_count}; - while (low < high) { - s32 mid{(low + high) >> 1}; - if (time < rules.ats[mid]) { - high = mid; - } else { - low = mid + 1; - } - } - tti_index = rules.types[low - 1]; - } - - if (const Result result{CreateCalendarTime(time, rules.ttis[tti_index].gmt_offset, - calendar_time, calendar_additional_info)}; - result != ResultSuccess) { - return result; - } - - calendar_additional_info.is_dst = rules.ttis[tti_index].is_dst; - const char* time_zone{&rules.chars[rules.ttis[tti_index].abbreviation_list_index]}; - u32 index; - for (index = 0; time_zone[index] != '\0' && time_zone[index] != ',' && - index < calendar_additional_info.timezone_name.size() - 1; - ++index) { - calendar_additional_info.timezone_name[index] = time_zone[index]; - } - calendar_additional_info.timezone_name[index] = '\0'; - return ResultSuccess; -} - -static Result ToCalendarTimeImpl(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) { - CalendarTimeInternal calendar_time{}; - const Result result{ - ToCalendarTimeInternal(rules, time, calendar_time, calendar.additional_info)}; - calendar.time.year = static_cast<s16>(calendar_time.year); - - // Internal impl. uses 0-indexed month - calendar.time.month = static_cast<s8>(calendar_time.month + 1); - - calendar.time.day = calendar_time.day; - calendar.time.hour = calendar_time.hour; - calendar.time.minute = calendar_time.minute; - calendar.time.second = calendar_time.second; - return result; -} - -TimeZoneManager::TimeZoneManager() = default; -TimeZoneManager::~TimeZoneManager() = default; - -Result TimeZoneManager::ToCalendarTime(const TimeZoneRule& rules, s64 time, - CalendarInfo& calendar) const { - return ToCalendarTimeImpl(rules, time, calendar); -} - -Result TimeZoneManager::SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name, - FileSys::VirtualFile& vfs_file) { - TimeZoneRule rule{}; - if (ParseTimeZoneBinary(rule, vfs_file)) { - device_location_name = location_name; - time_zone_rule = rule; - return ResultSuccess; - } - return ERROR_TIME_ZONE_CONVERSION_FAILED; -} - -Result TimeZoneManager::SetUpdatedTime(const Clock::SteadyClockTimePoint& value) { - time_zone_update_time_point = value; - return ResultSuccess; -} - -Result TimeZoneManager::ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const { - if (is_initialized) { - return ToCalendarTime(time_zone_rule, time, calendar); - } else { - return ERROR_UNINITIALIZED_CLOCK; - } -} - -Result TimeZoneManager::ParseTimeZoneRuleBinary(TimeZoneRule& rules, - FileSys::VirtualFile& vfs_file) const { - if (!ParseTimeZoneBinary(rules, vfs_file)) { - return ERROR_TIME_ZONE_CONVERSION_FAILED; - } - return ResultSuccess; -} - -Result TimeZoneManager::ToPosixTime(const TimeZoneRule& rules, const CalendarTime& calendar_time, - s64& posix_time) const { - posix_time = 0; - - CalendarTimeInternal internal_time{ - .year = calendar_time.year, - // Internal impl. uses 0-indexed month - .month = static_cast<s8>(calendar_time.month - 1), - .day = calendar_time.day, - .hour = calendar_time.hour, - .minute = calendar_time.minute, - .second = calendar_time.second, - }; - - s32 hour{internal_time.hour}; - s32 minute{internal_time.minute}; - if (!SafeNormalize(hour, minute, minutes_per_hour)) { - return ERROR_OVERFLOW; - } - internal_time.minute = static_cast<s8>(minute); - - s32 day{internal_time.day}; - if (!SafeNormalize(day, hour, hours_per_day)) { - return ERROR_OVERFLOW; - } - internal_time.day = static_cast<s8>(day); - internal_time.hour = static_cast<s8>(hour); - - s64 year{internal_time.year}; - s64 month{internal_time.month}; - if (!SafeNormalize(year, month, months_per_year)) { - return ERROR_OVERFLOW; - } - internal_time.month = static_cast<s8>(month); - - if (!SafeAdd(year, year_base)) { - return ERROR_OVERFLOW; - } - - while (day <= 0) { - if (!SafeAdd(year, -1)) { - return ERROR_OVERFLOW; - } - s64 temp_year{year}; - if (1 < internal_time.month) { - ++temp_year; - } - day += static_cast<s32>(GetYearLengthInDays(temp_year)); - } - - while (day > days_per_leap_year) { - s64 temp_year{year}; - if (1 < internal_time.month) { - temp_year++; - } - day -= static_cast<s32>(GetYearLengthInDays(temp_year)); - if (!SafeAdd(year, 1)) { - return ERROR_OVERFLOW; - } - } - - while (true) { - const s32 month_length{GetMonthLength(IsLeapYear(year), internal_time.month)}; - if (day <= month_length) { - break; - } - day -= month_length; - internal_time.month++; - if (internal_time.month >= months_per_year) { - internal_time.month = 0; - if (!SafeAdd(year, 1)) { - return ERROR_OVERFLOW; - } - } - } - internal_time.day = static_cast<s8>(day); - - if (!SafeAdd(year, -year_base)) { - return ERROR_OVERFLOW; - } - internal_time.year = year; - - s32 saved_seconds{}; - if (internal_time.second >= 0 && internal_time.second < seconds_per_minute) { - saved_seconds = 0; - } else if (year + year_base < epoch_year) { - s32 second{internal_time.second}; - if (!SafeAdd(second, 1 - seconds_per_minute)) { - return ERROR_OVERFLOW; - } - saved_seconds = second; - internal_time.second = 1 - seconds_per_minute; - } else { - saved_seconds = internal_time.second; - internal_time.second = 0; - } - - s64 low{LLONG_MIN}; - s64 high{LLONG_MAX}; - while (true) { - s64 pivot{low / 2 + high / 2}; - if (pivot < low) { - pivot = low; - } else if (pivot > high) { - pivot = high; - } - s32 direction{}; - CalendarTimeInternal candidate_calendar_time{}; - CalendarAdditionalInfo unused{}; - if (ToCalendarTimeInternal(rules, pivot, candidate_calendar_time, unused) != - ResultSuccess) { - if (pivot > 0) { - direction = 1; - } else { - direction = -1; - } - } else { - direction = candidate_calendar_time.Compare(internal_time); - } - if (!direction) { - const s64 time_result{pivot + saved_seconds}; - if ((time_result < pivot) != (saved_seconds < 0)) { - return ERROR_OVERFLOW; - } - posix_time = time_result; - break; - } else { - if (pivot == low) { - if (pivot == LLONG_MAX) { - return ERROR_TIME_NOT_FOUND; - } - pivot++; - low++; - } else if (pivot == high) { - if (pivot == LLONG_MIN) { - return ERROR_TIME_NOT_FOUND; - } - pivot--; - high--; - } - if (low > high) { - return ERROR_TIME_NOT_FOUND; - } - if (direction > 0) { - high = pivot; - } else { - low = pivot; - } - } - } - return ResultSuccess; -} - -Result TimeZoneManager::ToPosixTimeWithMyRule(const CalendarTime& calendar_time, - s64& posix_time) const { - if (is_initialized) { - return ToPosixTime(time_zone_rule, calendar_time, posix_time); - } - posix_time = 0; - return ERROR_UNINITIALIZED_CLOCK; -} - -Result TimeZoneManager::GetDeviceLocationName(LocationName& value) const { - if (!is_initialized) { - return ERROR_UNINITIALIZED_CLOCK; - } - std::memcpy(value.data(), device_location_name.c_str(), device_location_name.size()); - return ResultSuccess; -} - -Result TimeZoneManager::GetTotalLocationNameCount(s32& count) const { - if (!is_initialized) { - return ERROR_UNINITIALIZED_CLOCK; - } - count = static_cast<u32>(total_location_name_count); - - return ResultSuccess; -} - -Result TimeZoneManager::GetTimeZoneRuleVersion(u128& version) const { - if (!is_initialized) { - return ERROR_UNINITIALIZED_CLOCK; - } - version = time_zone_rule_version; - - return ResultSuccess; -} - -Result TimeZoneManager::LoadLocationNameList(std::vector<LocationName>& values) const { - if (!is_initialized) { - return ERROR_UNINITIALIZED_CLOCK; - } - - for (const auto& name : total_location_names) { - LocationName entry{}; - std::memcpy(entry.data(), name.c_str(), name.size()); - values.push_back(entry); - } - - return ResultSuccess; -} - -} // namespace Service::Time::TimeZone diff --git a/src/core/hle/service/time/time_zone_manager.h b/src/core/hle/service/time/time_zone_manager.h deleted file mode 100644 index 8664f28d1..000000000 --- a/src/core/hle/service/time/time_zone_manager.h +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <string> - -#include "common/common_types.h" -#include "core/file_sys/vfs_types.h" -#include "core/hle/service/time/clock_types.h" -#include "core/hle/service/time/time_zone_types.h" - -namespace Service::Time::TimeZone { - -class TimeZoneManager final { -public: - TimeZoneManager(); - ~TimeZoneManager(); - - void SetTotalLocationNameCount(std::size_t value) { - total_location_name_count = value; - } - - void SetLocationNames(std::vector<std::string> location_names) { - total_location_names = location_names; - } - - void SetTimeZoneRuleVersion(const u128& value) { - time_zone_rule_version = value; - } - - void MarkAsInitialized() { - is_initialized = true; - } - - Result SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name, - FileSys::VirtualFile& vfs_file); - Result SetUpdatedTime(const Clock::SteadyClockTimePoint& value); - Result GetDeviceLocationName(TimeZone::LocationName& value) const; - Result GetTotalLocationNameCount(s32& count) const; - Result GetTimeZoneRuleVersion(u128& version) const; - Result LoadLocationNameList(std::vector<TimeZone::LocationName>& values) const; - Result ToCalendarTime(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) const; - Result ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const; - Result ParseTimeZoneRuleBinary(TimeZoneRule& rules, FileSys::VirtualFile& vfs_file) const; - Result ToPosixTime(const TimeZoneRule& rules, const CalendarTime& calendar_time, - s64& posix_time) const; - Result ToPosixTimeWithMyRule(const CalendarTime& calendar_time, s64& posix_time) const; - -private: - bool is_initialized{}; - TimeZoneRule time_zone_rule{}; - std::string device_location_name{"GMT"}; - u128 time_zone_rule_version{}; - std::size_t total_location_name_count{}; - std::vector<std::string> total_location_names{}; - Clock::SteadyClockTimePoint time_zone_update_time_point{ - Clock::SteadyClockTimePoint::GetRandom()}; -}; - -} // namespace Service::Time::TimeZone diff --git a/src/core/hle/service/time/time_zone_service.cpp b/src/core/hle/service/time/time_zone_service.cpp deleted file mode 100644 index 8171c82a5..000000000 --- a/src/core/hle/service/time/time_zone_service.cpp +++ /dev/null @@ -1,217 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/logging/log.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/time/time_zone_content_manager.h" -#include "core/hle/service/time/time_zone_service.h" -#include "core/hle/service/time/time_zone_types.h" - -namespace Service::Time { - -ITimeZoneService::ITimeZoneService(Core::System& system_, - TimeZone::TimeZoneContentManager& time_zone_manager_) - : ServiceFramework{system_, "ITimeZoneService"}, time_zone_content_manager{time_zone_manager_} { - static const FunctionInfo functions[] = { - {0, &ITimeZoneService::GetDeviceLocationName, "GetDeviceLocationName"}, - {1, nullptr, "SetDeviceLocationName"}, - {2, &ITimeZoneService::GetTotalLocationNameCount, "GetTotalLocationNameCount"}, - {3, &ITimeZoneService::LoadLocationNameList, "LoadLocationNameList"}, - {4, &ITimeZoneService::LoadTimeZoneRule, "LoadTimeZoneRule"}, - {5, &ITimeZoneService::GetTimeZoneRuleVersion, "GetTimeZoneRuleVersion"}, - {6, nullptr, "GetDeviceLocationNameAndUpdatedTime"}, - {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"}, - {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"}, - {201, &ITimeZoneService::ToPosixTime, "ToPosixTime"}, - {202, &ITimeZoneService::ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"}, - }; - RegisterHandlers(functions); -} - -void ITimeZoneService::GetDeviceLocationName(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - TimeZone::LocationName location_name{}; - if (const Result result{ - time_zone_content_manager.GetTimeZoneManager().GetDeviceLocationName(location_name)}; - result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - IPC::ResponseBuilder rb{ctx, (sizeof(location_name) / 4) + 2}; - rb.Push(ResultSuccess); - rb.PushRaw(location_name); -} - -void ITimeZoneService::GetTotalLocationNameCount(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - s32 count{}; - if (const Result result{ - time_zone_content_manager.GetTimeZoneManager().GetTotalLocationNameCount(count)}; - result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(count); -} - -void ITimeZoneService::LoadLocationNameList(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - std::vector<TimeZone::LocationName> location_names{}; - if (const Result result{ - time_zone_content_manager.GetTimeZoneManager().LoadLocationNameList(location_names)}; - result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - ctx.WriteBuffer(location_names); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast<s32>(location_names.size())); -} -void ITimeZoneService::GetTimeZoneRuleVersion(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - u128 rule_version{}; - if (const Result result{ - time_zone_content_manager.GetTimeZoneManager().GetTimeZoneRuleVersion(rule_version)}; - result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.PushRaw(rule_version); -} - -void ITimeZoneService::LoadTimeZoneRule(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto raw_location_name{rp.PopRaw<std::array<u8, 0x24>>()}; - - std::string location_name; - for (const auto& byte : raw_location_name) { - // Strip extra bytes - if (byte == '\0') { - break; - } - location_name.push_back(byte); - } - - LOG_DEBUG(Service_Time, "called, location_name={}", location_name); - - TimeZone::TimeZoneRule time_zone_rule{}; - const Result result{time_zone_content_manager.LoadTimeZoneRule(time_zone_rule, location_name)}; - - std::vector<u8> time_zone_rule_outbuffer(sizeof(TimeZone::TimeZoneRule)); - std::memcpy(time_zone_rule_outbuffer.data(), &time_zone_rule, sizeof(TimeZone::TimeZoneRule)); - ctx.WriteBuffer(time_zone_rule_outbuffer); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void ITimeZoneService::ToCalendarTime(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto posix_time{rp.Pop<s64>()}; - - LOG_DEBUG(Service_Time, "called, posix_time=0x{:016X}", posix_time); - - TimeZone::TimeZoneRule time_zone_rule{}; - const auto buffer{ctx.ReadBuffer()}; - std::memcpy(&time_zone_rule, buffer.data(), buffer.size()); - - TimeZone::CalendarInfo calendar_info{}; - if (const Result result{time_zone_content_manager.GetTimeZoneManager().ToCalendarTime( - time_zone_rule, posix_time, calendar_info)}; - result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - IPC::ResponseBuilder rb{ctx, 2 + (sizeof(TimeZone::CalendarInfo) / 4)}; - rb.Push(ResultSuccess); - rb.PushRaw(calendar_info); -} - -void ITimeZoneService::ToCalendarTimeWithMyRule(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto posix_time{rp.Pop<s64>()}; - - LOG_DEBUG(Service_Time, "called, posix_time=0x{:016X}", posix_time); - - TimeZone::CalendarInfo calendar_info{}; - if (const Result result{ - time_zone_content_manager.GetTimeZoneManager().ToCalendarTimeWithMyRules( - posix_time, calendar_info)}; - result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - IPC::ResponseBuilder rb{ctx, 2 + (sizeof(TimeZone::CalendarInfo) / 4)}; - rb.Push(ResultSuccess); - rb.PushRaw(calendar_info); -} - -void ITimeZoneService::ToPosixTime(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - IPC::RequestParser rp{ctx}; - const auto calendar_time{rp.PopRaw<TimeZone::CalendarTime>()}; - TimeZone::TimeZoneRule time_zone_rule{}; - std::memcpy(&time_zone_rule, ctx.ReadBuffer().data(), sizeof(TimeZone::TimeZoneRule)); - - s64 posix_time{}; - if (const Result result{time_zone_content_manager.GetTimeZoneManager().ToPosixTime( - time_zone_rule, calendar_time, posix_time)}; - result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - ctx.WriteBuffer(posix_time); - - // TODO(bunnei): Handle multiple times - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushRaw<u32>(1); // Number of times we're returning -} - -void ITimeZoneService::ToPosixTimeWithMyRule(HLERequestContext& ctx) { - LOG_DEBUG(Service_Time, "called"); - - IPC::RequestParser rp{ctx}; - const auto calendar_time{rp.PopRaw<TimeZone::CalendarTime>()}; - - s64 posix_time{}; - if (const Result result{time_zone_content_manager.GetTimeZoneManager().ToPosixTimeWithMyRule( - calendar_time, posix_time)}; - result != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - ctx.WriteBuffer(posix_time); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushRaw<u32>(1); // Number of times we're returning -} - -} // namespace Service::Time diff --git a/src/core/hle/service/time/time_zone_service.h b/src/core/hle/service/time/time_zone_service.h deleted file mode 100644 index 952fcb0e2..000000000 --- a/src/core/hle/service/time/time_zone_service.h +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 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::Time { - -namespace TimeZone { -class TimeZoneContentManager; -} - -class ITimeZoneService final : public ServiceFramework<ITimeZoneService> { -public: - explicit ITimeZoneService(Core::System& system_, - TimeZone::TimeZoneContentManager& time_zone_manager_); - -private: - void GetDeviceLocationName(HLERequestContext& ctx); - void GetTotalLocationNameCount(HLERequestContext& ctx); - void LoadLocationNameList(HLERequestContext& ctx); - void GetTimeZoneRuleVersion(HLERequestContext& ctx); - void LoadTimeZoneRule(HLERequestContext& ctx); - void ToCalendarTime(HLERequestContext& ctx); - void ToCalendarTimeWithMyRule(HLERequestContext& ctx); - void ToPosixTime(HLERequestContext& ctx); - void ToPosixTimeWithMyRule(HLERequestContext& ctx); - -private: - TimeZone::TimeZoneContentManager& time_zone_content_manager; -}; - -} // namespace Service::Time diff --git a/src/core/hle/service/time/time_zone_types.h b/src/core/hle/service/time/time_zone_types.h deleted file mode 100644 index eb4fb52d1..000000000 --- a/src/core/hle/service/time/time_zone_types.h +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <array> - -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "common/swap.h" - -namespace Service::Time::TimeZone { - -using LocationName = std::array<char, 0x24>; - -/// https://switchbrew.org/wiki/Glue_services#ttinfo -struct TimeTypeInfo { - s32 gmt_offset{}; - u8 is_dst{}; - INSERT_PADDING_BYTES(3); - s32 abbreviation_list_index{}; - u8 is_standard_time_daylight{}; - u8 is_gmt{}; - INSERT_PADDING_BYTES(2); -}; -static_assert(sizeof(TimeTypeInfo) == 0x10, "TimeTypeInfo is incorrect size"); - -/// https://switchbrew.org/wiki/Glue_services#TimeZoneRule -struct TimeZoneRule { - s32 time_count{}; - s32 type_count{}; - s32 char_count{}; - u8 go_back{}; - u8 go_ahead{}; - INSERT_PADDING_BYTES(2); - std::array<s64, 1000> ats{}; - std::array<s8, 1000> types{}; - std::array<TimeTypeInfo, 128> ttis{}; - std::array<char, 512> chars{}; - s32 default_type{}; - INSERT_PADDING_BYTES(0x12C4); -}; -static_assert(sizeof(TimeZoneRule) == 0x4000, "TimeZoneRule is incorrect size"); - -/// https://switchbrew.org/wiki/Glue_services#CalendarAdditionalInfo -struct CalendarAdditionalInfo { - u32 day_of_week; - u32 day_of_year; - std::array<char, 8> timezone_name; - u32 is_dst; - s32 gmt_offset; -}; -static_assert(sizeof(CalendarAdditionalInfo) == 0x18, "CalendarAdditionalInfo is incorrect size"); - -/// https://switchbrew.org/wiki/Glue_services#CalendarTime -struct CalendarTime { - s16 year; - s8 month; - s8 day; - s8 hour; - s8 minute; - s8 second; - INSERT_PADDING_BYTES_NOINIT(1); -}; -static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime is incorrect size"); - -struct CalendarInfo { - CalendarTime time; - CalendarAdditionalInfo additional_info; -}; -static_assert(sizeof(CalendarInfo) == 0x20, "CalendarInfo is incorrect size"); - -struct TzifHeader { - u32_be magic{}; - u8 version{}; - INSERT_PADDING_BYTES(15); - s32_be ttis_gmt_count{}; - s32_be ttis_std_count{}; - s32_be leap_count{}; - s32_be time_count{}; - s32_be type_count{}; - s32_be char_count{}; -}; -static_assert(sizeof(TzifHeader) == 0x2C, "TzifHeader is incorrect size"); - -} // namespace Service::Time::TimeZone diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index bfcc27ddc..1f3d82c57 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -15,6 +15,7 @@ #include "common/logging/log.h" #include "common/math_util.h" #include "common/settings.h" +#include "common/string_util.h" #include "common/swap.h" #include "core/core_timing.h" #include "core/hle/kernel/k_readable_event.h" @@ -694,9 +695,7 @@ private: void OpenLayer(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto name_buf = rp.PopRaw<std::array<u8, 0x40>>(); - const auto end = std::find(name_buf.begin(), name_buf.end(), '\0'); - - const std::string display_name(name_buf.begin(), end); + const std::string display_name(Common::StringFromBuffer(name_buf)); const u64 layer_id = rp.Pop<u64>(); const u64 aruid = rp.Pop<u64>(); diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index b4828f7cd..f4e932cec 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -14,7 +14,7 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "core/file_sys/control_metadata.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs/vfs.h" namespace Core { class System; diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index f8225d697..1d96dc4c8 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -12,7 +12,7 @@ #include "core/core.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/romfs_factory.h" -#include "core/file_sys/vfs_offset.h" +#include "core/file_sys/vfs/vfs_offset.h" #include "core/hle/kernel/code_set.h" #include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_process.h" diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp index 28116ff3a..3016d5f25 100644 --- a/src/core/loader/nsp.cpp +++ b/src/core/loader/nsp.cpp @@ -10,6 +10,7 @@ #include "core/file_sys/nca_metadata.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" +#include "core/file_sys/romfs_factory.h" #include "core/file_sys/submission_package.h" #include "core/hle/kernel/k_process.h" #include "core/hle/service/filesystem/filesystem.h" @@ -109,6 +110,13 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::KProcess& process, Core::S return result; } + if (nsp->IsExtractedType()) { + system.GetFileSystemController().RegisterProcess( + process.GetProcessId(), {}, + std::make_shared<FileSys::RomFSFactory>(*this, system.GetContentProvider(), + system.GetFileSystemController())); + } + FileSys::VirtualFile update_raw; if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { system.GetFileSystemController().SetPackedUpdate(process.GetProcessId(), diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index dc3883528..1a0138697 100644 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp @@ -68,8 +68,8 @@ json GetReportCommonData(u64 title_id, Result result, const std::string& timesta auto out = json{ {"title_id", fmt::format("{:016X}", title_id)}, {"result_raw", fmt::format("{:08X}", result.raw)}, - {"result_module", fmt::format("{:08X}", static_cast<u32>(result.module.Value()))}, - {"result_description", fmt::format("{:08X}", result.description.Value())}, + {"result_module", fmt::format("{:08X}", static_cast<u32>(result.GetModule()))}, + {"result_description", fmt::format("{:08X}", result.GetDescription())}, {"timestamp", timestamp}, }; diff --git a/src/frontend_common/content_manager.h b/src/frontend_common/content_manager.h index 0b0fee73e..f3efe3465 100644 --- a/src/frontend_common/content_manager.h +++ b/src/frontend_common/content_manager.h @@ -9,7 +9,7 @@ #include "core/core.h" #include "core/file_sys/common_funcs.h" #include "core/file_sys/content_archive.h" -#include "core/file_sys/mode.h" +#include "core/file_sys/fs_filesystem.h" #include "core/file_sys/nca_metadata.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" @@ -47,14 +47,14 @@ inline bool RemoveDLC(const Service::FileSystem::FileSystemController& fs_contro /** * \brief Removes all DLC for a game - * \param system Raw pointer to the system instance + * \param system Reference to the system instance * \param program_id Program ID for the game that will have all of its DLC removed * \return Number of DLC removed */ -inline size_t RemoveAllDLC(Core::System* system, const u64 program_id) { +inline size_t RemoveAllDLC(Core::System& system, const u64 program_id) { size_t count{}; - const auto& fs_controller = system->GetFileSystemController(); - const auto dlc_entries = system->GetContentProvider().ListEntriesFilter( + const auto& fs_controller = system.GetFileSystemController(); + const auto dlc_entries = system.GetContentProvider().ListEntriesFilter( FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); std::vector<u64> program_dlc_entries; @@ -124,15 +124,15 @@ inline bool RemoveMod(const Service::FileSystem::FileSystemController& fs_contro /** * \brief Installs an NSP - * \param system Raw pointer to the system instance - * \param vfs Raw pointer to the VfsFilesystem instance in Core::System + * \param system Reference to the system instance + * \param vfs Reference to the VfsFilesystem instance in Core::System * \param filename Path to the NSP file * \param callback Callback to report the progress of the installation. The first size_t * parameter is the total size of the virtual file and the second is the current progress. If you * return true to the callback, it will cancel the installation as soon as possible. * \return [InstallResult] representing how the installation finished */ -inline InstallResult InstallNSP(Core::System* system, FileSys::VfsFilesystem* vfs, +inline InstallResult InstallNSP(Core::System& system, FileSys::VfsFilesystem& vfs, const std::string& filename, const std::function<bool(size_t, size_t)>& callback) { const auto copy = [callback](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, @@ -159,7 +159,7 @@ inline InstallResult InstallNSP(Core::System* system, FileSys::VfsFilesystem* vf }; std::shared_ptr<FileSys::NSP> nsp; - FileSys::VirtualFile file = vfs->OpenFile(filename, FileSys::Mode::Read); + FileSys::VirtualFile file = vfs.OpenFile(filename, FileSys::OpenMode::Read); if (boost::to_lower_copy(file->GetName()).ends_with(std::string("nsp"))) { nsp = std::make_shared<FileSys::NSP>(file); if (nsp->IsExtractedType()) { @@ -173,7 +173,7 @@ inline InstallResult InstallNSP(Core::System* system, FileSys::VfsFilesystem* vf return InstallResult::Failure; } const auto res = - system->GetFileSystemController().GetUserNANDContents()->InstallEntry(*nsp, true, copy); + system.GetFileSystemController().GetUserNANDContents()->InstallEntry(*nsp, true, copy); switch (res) { case FileSys::InstallResult::Success: return InstallResult::Success; @@ -188,17 +188,17 @@ inline InstallResult InstallNSP(Core::System* system, FileSys::VfsFilesystem* vf /** * \brief Installs an NCA - * \param vfs Raw pointer to the VfsFilesystem instance in Core::System + * \param vfs Reference to the VfsFilesystem instance in Core::System * \param filename Path to the NCA file - * \param registered_cache Raw pointer to the registered cache that the NCA will be installed to + * \param registered_cache Reference to the registered cache that the NCA will be installed to * \param title_type Type of NCA package to install * \param callback Callback to report the progress of the installation. The first size_t * parameter is the total size of the virtual file and the second is the current progress. If you * return true to the callback, it will cancel the installation as soon as possible. * \return [InstallResult] representing how the installation finished */ -inline InstallResult InstallNCA(FileSys::VfsFilesystem* vfs, const std::string& filename, - FileSys::RegisteredCache* registered_cache, +inline InstallResult InstallNCA(FileSys::VfsFilesystem& vfs, const std::string& filename, + FileSys::RegisteredCache& registered_cache, const FileSys::TitleType title_type, const std::function<bool(size_t, size_t)>& callback) { const auto copy = [callback](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, @@ -224,7 +224,8 @@ inline InstallResult InstallNCA(FileSys::VfsFilesystem* vfs, const std::string& return true; }; - const auto nca = std::make_shared<FileSys::NCA>(vfs->OpenFile(filename, FileSys::Mode::Read)); + const auto nca = + std::make_shared<FileSys::NCA>(vfs.OpenFile(filename, FileSys::OpenMode::Read)); const auto id = nca->GetStatus(); // Game updates necessary are missing base RomFS @@ -233,7 +234,7 @@ inline InstallResult InstallNCA(FileSys::VfsFilesystem* vfs, const std::string& return InstallResult::Failure; } - const auto res = registered_cache->InstallEntry(*nca, title_type, true, copy); + const auto res = registered_cache.InstallEntry(*nca, title_type, true, copy); if (res == FileSys::InstallResult::Success) { return InstallResult::Success; } else if (res == FileSys::InstallResult::OverwriteExisting) { @@ -245,19 +246,19 @@ inline InstallResult InstallNCA(FileSys::VfsFilesystem* vfs, const std::string& /** * \brief Verifies the installed contents for a given ManualContentProvider - * \param system Raw pointer to the system instance - * \param provider Raw pointer to the content provider that's tracking indexed games + * \param system Reference to the system instance + * \param provider Reference to the content provider that's tracking indexed games * \param callback Callback to report the progress of the installation. The first size_t * parameter is the total size of the installed contents and the second is the current progress. If * you return true to the callback, it will cancel the installation as soon as possible. * \return A list of entries that failed to install. Returns an empty vector if successful. */ inline std::vector<std::string> VerifyInstalledContents( - Core::System* system, FileSys::ManualContentProvider* provider, + Core::System& system, FileSys::ManualContentProvider& provider, const std::function<bool(size_t, size_t)>& callback) { // Get content registries. - auto bis_contents = system->GetFileSystemController().GetSystemNANDContents(); - auto user_contents = system->GetFileSystemController().GetUserNANDContents(); + auto bis_contents = system.GetFileSystemController().GetSystemNANDContents(); + auto user_contents = system.GetFileSystemController().GetUserNANDContents(); std::vector<FileSys::RegisteredCache*> content_providers; if (bis_contents) { @@ -309,11 +310,11 @@ inline std::vector<std::string> VerifyInstalledContents( const auto title_id = nca.GetTitleId(); std::string title_name = "unknown"; - const auto control = provider->GetEntry(FileSys::GetBaseTitleID(title_id), - FileSys::ContentRecordType::Control); + const auto control = provider.GetEntry(FileSys::GetBaseTitleID(title_id), + FileSys::ContentRecordType::Control); if (control && control->GetStatus() == Loader::ResultStatus::Success) { - const FileSys::PatchManager pm{title_id, system->GetFileSystemController(), - *provider}; + const FileSys::PatchManager pm{title_id, system.GetFileSystemController(), + provider}; const auto [nacp, logo] = pm.ParseControlNCA(*control); if (nacp) { title_name = nacp->GetApplicationName(); @@ -335,7 +336,7 @@ inline std::vector<std::string> VerifyInstalledContents( /** * \brief Verifies the contents of a given game - * \param system Raw pointer to the system instance + * \param system Reference to the system instance * \param game_path Patch to the game file * \param callback Callback to report the progress of the installation. The first size_t * parameter is the total size of the installed contents and the second is the current progress. If @@ -343,10 +344,10 @@ inline std::vector<std::string> VerifyInstalledContents( * \return GameVerificationResult representing how the verification process finished */ inline GameVerificationResult VerifyGameContents( - Core::System* system, const std::string& game_path, + Core::System& system, const std::string& game_path, const std::function<bool(size_t, size_t)>& callback) { const auto loader = Loader::GetLoader( - *system, system->GetFilesystem()->OpenFile(game_path, FileSys::Mode::Read)); + system, system.GetFilesystem()->OpenFile(game_path, FileSys::OpenMode::Read)); if (loader == nullptr) { return GameVerificationResult::NotImplemented; } @@ -368,4 +369,11 @@ inline GameVerificationResult VerifyGameContents( return GameVerificationResult::Success; } +/** + * Checks if the keys required for decrypting firmware and games are available + */ +inline bool AreKeysPresent() { + return !Core::Crypto::KeyManager::Instance().BaseDeriveNecessary(); +} + } // namespace ContentManager diff --git a/src/hid_core/resource_manager.cpp b/src/hid_core/resource_manager.cpp index ca824b4a3..a2295219a 100644 --- a/src/hid_core/resource_manager.cpp +++ b/src/hid_core/resource_manager.cpp @@ -6,6 +6,8 @@ #include "core/core_timing.h" #include "core/hle/kernel/k_shared_memory.h" #include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/set/system_settings_server.h" +#include "core/hle/service/sm/sm.h" #include "hid_core/hid_core.h" #include "hid_core/hid_util.h" #include "hid_core/resource_manager.h" @@ -180,7 +182,11 @@ void ResourceManager::InitializeHidCommonSampler() { debug_pad->SetAppletResource(applet_resource, &shared_mutex); digitizer->SetAppletResource(applet_resource, &shared_mutex); keyboard->SetAppletResource(applet_resource, &shared_mutex); - npad->SetNpadExternals(applet_resource, &shared_mutex, handheld_config); + + const auto settings = + system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys"); + npad->SetNpadExternals(applet_resource, &shared_mutex, handheld_config, settings); + six_axis->SetAppletResource(applet_resource, &shared_mutex); mouse->SetAppletResource(applet_resource, &shared_mutex); debug_mouse->SetAppletResource(applet_resource, &shared_mutex); @@ -373,6 +379,10 @@ Result ResourceManager::SendVibrationValue(u64 aruid, device = GetNSVibrationDevice(handle); } if (device != nullptr) { + // Prevent sending vibrations to an inactive vibration handle + if (!device->IsActive()) { + return ResultSuccess; + } result = device->SendVibrationValue(value); } return result; diff --git a/src/hid_core/resources/hid_firmware_settings.cpp b/src/hid_core/resources/hid_firmware_settings.cpp index 00ceff7e6..9c9019e8f 100644 --- a/src/hid_core/resources/hid_firmware_settings.cpp +++ b/src/hid_core/resources/hid_firmware_settings.cpp @@ -40,6 +40,13 @@ void HidFirmwareSettings::LoadSettings(bool reload_config) { m_set_sys->GetSettingsItemValue<bool>(is_touch_firmware_auto_update_disabled, "hid_debug", "touch_firmware_auto_update_disabled"); + bool has_rail_interface{}; + bool has_sio_mcu{}; + m_set_sys->GetSettingsItemValue<bool>(has_rail_interface, "hid", "has_rail_interface"); + m_set_sys->GetSettingsItemValue<bool>(has_sio_mcu, "hid", "has_sio_mcu"); + platform_config.has_rail_interface.Assign(has_rail_interface); + platform_config.has_sio_mcu.Assign(has_sio_mcu); + is_initialized = true; } @@ -103,4 +110,9 @@ HidFirmwareSettings::FeaturesPerId HidFirmwareSettings::FeaturesDisabledPerId() return features_per_id_disabled; } +Set::PlatformConfig HidFirmwareSettings::GetPlatformConfig() { + LoadSettings(false); + return platform_config; +} + } // namespace Service::HID diff --git a/src/hid_core/resources/hid_firmware_settings.h b/src/hid_core/resources/hid_firmware_settings.h index 3694fa9a3..7f146f1e6 100644 --- a/src/hid_core/resources/hid_firmware_settings.h +++ b/src/hid_core/resources/hid_firmware_settings.h @@ -4,6 +4,7 @@ #pragma once #include "common/common_types.h" +#include "core/hle/service/set/settings_types.h" namespace Core { class System; @@ -39,6 +40,7 @@ public: FirmwareSetting GetFirmwareUpdateFailure(); FeaturesPerId FeaturesDisabledPerId(); + Set::PlatformConfig GetPlatformConfig(); private: bool is_initialized{}; @@ -57,6 +59,7 @@ private: bool is_touch_firmware_auto_update_disabled{}; FirmwareSetting is_firmware_update_failure{}; FeaturesPerId features_per_id_disabled{}; + Set::PlatformConfig platform_config{}; std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys; }; diff --git a/src/hid_core/resources/npad/npad.cpp b/src/hid_core/resources/npad/npad.cpp index d13a489c9..cde84b1bb 100644 --- a/src/hid_core/resources/npad/npad.cpp +++ b/src/hid_core/resources/npad/npad.cpp @@ -1080,12 +1080,15 @@ void NPad::UnregisterAppletResourceUserId(u64 aruid) { void NPad::SetNpadExternals(std::shared_ptr<AppletResource> resource, std::recursive_mutex* shared_mutex, - std::shared_ptr<HandheldConfig> handheld_config) { + std::shared_ptr<HandheldConfig> handheld_config, + std::shared_ptr<Service::Set::ISystemSettingsServer> settings) { applet_resource_holder.applet_resource = resource; applet_resource_holder.shared_mutex = shared_mutex; applet_resource_holder.shared_npad_resource = &npad_resource; applet_resource_holder.handheld_config = handheld_config; + vibration_handler.SetSettingsService(settings); + for (auto& abstract_pad : abstracted_pads) { abstract_pad.SetExternals(&applet_resource_holder, nullptr, nullptr, nullptr, nullptr, &vibration_handler, &hid_core); diff --git a/src/hid_core/resources/npad/npad.h b/src/hid_core/resources/npad/npad.h index 88289fa2b..502cb9b55 100644 --- a/src/hid_core/resources/npad/npad.h +++ b/src/hid_core/resources/npad/npad.h @@ -34,6 +34,10 @@ namespace Service::KernelHelpers { class ServiceContext; } // namespace Service::KernelHelpers +namespace Service::Set { +class ISystemSettingsServer; +} + union Result; namespace Service::HID { @@ -128,7 +132,8 @@ public: void UnregisterAppletResourceUserId(u64 aruid); void SetNpadExternals(std::shared_ptr<AppletResource> resource, std::recursive_mutex* shared_mutex, - std::shared_ptr<HandheldConfig> handheld_config); + std::shared_ptr<HandheldConfig> handheld_config, + std::shared_ptr<Service::Set::ISystemSettingsServer> settings); AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id); diff --git a/src/hid_core/resources/npad/npad_vibration.cpp b/src/hid_core/resources/npad/npad_vibration.cpp index 05aad4c54..02b1f0290 100644 --- a/src/hid_core/resources/npad/npad_vibration.cpp +++ b/src/hid_core/resources/npad/npad_vibration.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later +#include "core/hle/service/set/system_settings_server.h" #include "hid_core/hid_result.h" #include "hid_core/resources/npad/npad_vibration.h" @@ -13,10 +14,11 @@ NpadVibration::~NpadVibration() = default; Result NpadVibration::Activate() { std::scoped_lock lock{mutex}; - const f32 master_volume = 1.0f; // nn::settings::system::GetVibrationMasterVolume(); - // if (master_volume < 0.0f || master_volume > 1.0f) { - // return ResultVibrationStrengthOutOfRange; - // } + f32 master_volume = 1.0f; + m_set_sys->GetVibrationMasterVolume(master_volume); + if (master_volume < 0.0f || master_volume > 1.0f) { + return ResultVibrationStrengthOutOfRange; + } volume = master_volume; return ResultSuccess; @@ -26,6 +28,12 @@ Result NpadVibration::Deactivate() { return ResultSuccess; } +Result NpadVibration::SetSettingsService( + std::shared_ptr<Service::Set::ISystemSettingsServer> settings) { + m_set_sys = settings; + return ResultSuccess; +} + Result NpadVibration::SetVibrationMasterVolume(f32 master_volume) { std::scoped_lock lock{mutex}; @@ -34,7 +42,7 @@ Result NpadVibration::SetVibrationMasterVolume(f32 master_volume) { } volume = master_volume; - // nn::settings::system::SetVibrationMasterVolume(master_volume); + m_set_sys->SetVibrationMasterVolume(master_volume); return ResultSuccess; } @@ -48,10 +56,11 @@ Result NpadVibration::GetVibrationVolume(f32& out_volume) const { Result NpadVibration::GetVibrationMasterVolume(f32& out_volume) const { std::scoped_lock lock{mutex}; - const f32 master_volume = 1.0f; // nn::settings::system::GetVibrationMasterVolume(); - // if (master_volume < 0.0f || master_volume > 1.0f) { - // return ResultVibrationStrengthOutOfRange; - // } + f32 master_volume = 1.0f; + m_set_sys->GetVibrationMasterVolume(master_volume); + if (master_volume < 0.0f || master_volume > 1.0f) { + return ResultVibrationStrengthOutOfRange; + } out_volume = master_volume; return ResultSuccess; @@ -67,10 +76,11 @@ Result NpadVibration::BeginPermitVibrationSession(u64 aruid) { Result NpadVibration::EndPermitVibrationSession() { std::scoped_lock lock{mutex}; - const f32 master_volume = 1.0f; // nn::settings::system::GetVibrationMasterVolume(); - // if (master_volume < 0.0f || master_volume > 1.0f) { - // return ResultVibrationStrengthOutOfRange; - // } + f32 master_volume = 1.0f; + m_set_sys->GetVibrationMasterVolume(master_volume); + if (master_volume < 0.0f || master_volume > 1.0f) { + return ResultVibrationStrengthOutOfRange; + } volume = master_volume; session_aruid = 0; diff --git a/src/hid_core/resources/npad/npad_vibration.h b/src/hid_core/resources/npad/npad_vibration.h index d5a95f2a0..6412ca4ab 100644 --- a/src/hid_core/resources/npad/npad_vibration.h +++ b/src/hid_core/resources/npad/npad_vibration.h @@ -8,6 +8,10 @@ #include "common/common_types.h" #include "core/hle/result.h" +namespace Service::Set { +class ISystemSettingsServer; +} + namespace Service::HID { class NpadVibration final { @@ -18,6 +22,7 @@ public: Result Activate(); Result Deactivate(); + Result SetSettingsService(std::shared_ptr<Service::Set::ISystemSettingsServer> settings); Result SetVibrationMasterVolume(f32 master_volume); Result GetVibrationVolume(f32& out_volume) const; Result GetVibrationMasterVolume(f32& out_volume) const; @@ -31,6 +36,8 @@ private: f32 volume{}; u64 session_aruid{}; mutable std::mutex mutex; + + std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys; }; } // namespace Service::HID diff --git a/src/hid_core/resources/vibration/vibration_base.cpp b/src/hid_core/resources/vibration/vibration_base.cpp index f28d30406..90bff88f4 100644 --- a/src/hid_core/resources/vibration/vibration_base.cpp +++ b/src/hid_core/resources/vibration/vibration_base.cpp @@ -23,6 +23,10 @@ Result NpadVibrationBase::Deactivate() { return ResultSuccess; } +bool NpadVibrationBase::IsActive() const { + return ref_counter > 0; +} + bool NpadVibrationBase::IsVibrationMounted() const { return is_mounted; } diff --git a/src/hid_core/resources/vibration/vibration_base.h b/src/hid_core/resources/vibration/vibration_base.h index 69c26e669..8fe35634d 100644 --- a/src/hid_core/resources/vibration/vibration_base.h +++ b/src/hid_core/resources/vibration/vibration_base.h @@ -21,6 +21,7 @@ public: virtual Result Activate(); virtual Result Deactivate(); + bool IsActive() const; bool IsVibrationMounted() const; protected: diff --git a/src/tests/video_core/memory_tracker.cpp b/src/tests/video_core/memory_tracker.cpp index 0e559a590..45b1a91dc 100644 --- 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/query_cache/query_base.h b/src/video_core/query_cache/query_base.h index aca6a6447..d5d21beaa 100644 --- 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_base.h b/src/video_core/query_cache/query_cache_base.h index c12fb75ef..00c25c8d6 100644 --- 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<QueryCacheBaseImpl> 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 d9040acd2..1d11b1275 100644 --- a/src/video_core/query_cache/query_stream.h +++ b/src/video_core/query_cache/query_stream.h @@ -146,4 +146,4 @@ protected: std::deque<size_t> 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 100644 --- 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 100644 --- 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/vk_descriptor_pool.h b/src/video_core/renderer_vulkan/vk_descriptor_pool.h index bd6696b07..4aada5a00 100644 --- 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<std::unique_ptr<DescriptorBank>> banks; }; -} // namespace Vulkan
\ No newline at end of file +} // namespace Vulkan diff --git a/src/video_core/texture_cache/accelerated_swizzle.cpp b/src/video_core/texture_cache/accelerated_swizzle.cpp index 70be1657e..4c3f724d7 100644 --- 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_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index 074aed964..3966bd61e 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -39,6 +39,10 @@ void SortPhysicalDevicesPerVendor(std::vector<VkPhysicalDevice>& devices, } } +bool IsMicrosoftDozen(const char* device_name) { + return std::strstr(device_name, "Microsoft") != nullptr; +} + void SortPhysicalDevices(std::vector<VkPhysicalDevice>& devices, const InstanceDispatch& dld) { // Sort by name, this will set a base and make GPUs with higher numbers appear first // (e.g. GTX 1650 will intentionally be listed before a GTX 1080). @@ -52,6 +56,12 @@ void SortPhysicalDevices(std::vector<VkPhysicalDevice>& devices, const InstanceD }); // Prefer Nvidia over AMD, AMD over Intel, Intel over the rest. SortPhysicalDevicesPerVendor(devices, dld, {0x10DE, 0x1002, 0x8086}); + // Demote Microsoft's Dozen devices to the bottom. + SortPhysicalDevices( + devices, dld, + [](const VkPhysicalDeviceProperties& lhs, const VkPhysicalDeviceProperties& rhs) { + return IsMicrosoftDozen(rhs.deviceName) && !IsMicrosoftDozen(lhs.deviceName); + }); } template <typename T> diff --git a/src/yuzu/applets/qt_error.cpp b/src/yuzu/applets/qt_error.cpp index 1dc4f0383..ad35f4126 100644 --- a/src/yuzu/applets/qt_error.cpp +++ b/src/yuzu/applets/qt_error.cpp @@ -25,8 +25,8 @@ void QtErrorDisplay::ShowError(Result error, FinishedCallback finished) const { callback = std::move(finished); emit MainWindowDisplayError( tr("Error Code: %1-%2 (0x%3)") - .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) - .arg(error.description, 4, 10, QChar::fromLatin1('0')) + .arg(static_cast<u32>(error.GetModule()) + 2000, 4, 10, QChar::fromLatin1('0')) + .arg(error.GetDescription(), 4, 10, QChar::fromLatin1('0')) .arg(error.raw, 8, 16, QChar::fromLatin1('0')), tr("An error has occurred.\nPlease try again or contact the developer of the software.")); } @@ -38,8 +38,8 @@ void QtErrorDisplay::ShowErrorWithTimestamp(Result error, std::chrono::seconds t const QDateTime date_time = QDateTime::fromSecsSinceEpoch(time.count()); emit MainWindowDisplayError( tr("Error Code: %1-%2 (0x%3)") - .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) - .arg(error.description, 4, 10, QChar::fromLatin1('0')) + .arg(static_cast<u32>(error.GetModule()) + 2000, 4, 10, QChar::fromLatin1('0')) + .arg(error.GetDescription(), 4, 10, QChar::fromLatin1('0')) .arg(error.raw, 8, 16, QChar::fromLatin1('0')), tr("An error occurred on %1 at %2.\nPlease try again or contact the developer of the " "software.") @@ -53,8 +53,8 @@ void QtErrorDisplay::ShowCustomErrorText(Result error, std::string dialog_text, callback = std::move(finished); emit MainWindowDisplayError( tr("Error Code: %1-%2 (0x%3)") - .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) - .arg(error.description, 4, 10, QChar::fromLatin1('0')) + .arg(static_cast<u32>(error.GetModule()) + 2000, 4, 10, QChar::fromLatin1('0')) + .arg(error.GetDescription(), 4, 10, QChar::fromLatin1('0')) .arg(error.raw, 8, 16, QChar::fromLatin1('0')), tr("An error has occurred.\n\n%1\n\n%2") .arg(QString::fromStdString(dialog_text)) diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h index c8ee46c04..9daae772c 100644 --- a/src/yuzu/configuration/configure_per_game.h +++ b/src/yuzu/configuration/configure_per_game.h @@ -11,7 +11,7 @@ #include <QList> #include "configuration/shared_widget.h" -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" #include "frontend_common/config.h" #include "vk_device_info.h" #include "yuzu/configuration/configuration_shared.h" diff --git a/src/yuzu/configuration/configure_per_game_addons.h b/src/yuzu/configuration/configure_per_game_addons.h index 53db405c1..32dc5dde6 100644 --- a/src/yuzu/configuration/configure_per_game_addons.h +++ b/src/yuzu/configuration/configure_per_game_addons.h @@ -8,7 +8,7 @@ #include <QList> -#include "core/file_sys/vfs_types.h" +#include "core/file_sys/vfs/vfs_types.h" namespace Core { class System; diff --git a/src/yuzu/configuration/configure_ringcon.cpp b/src/yuzu/configuration/configure_ringcon.cpp index 3a7f6101d..9fd094ab6 100644 --- 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 b0b84f967..e193b5f95 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp @@ -12,9 +12,10 @@ #include <QGraphicsItem> #include <QLineEdit> #include <QMessageBox> +#include <QSpinBox> + #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" @@ -49,6 +50,11 @@ ConfigureSystem::ConfigureSystem(Core::System& system_, : Tab(group_, parent), ui{std::make_unique<Ui::ConfigureSystem>()}, system{system_} { ui->setupUi(this); + const auto posix_time = std::chrono::system_clock::now().time_since_epoch(); + const auto current_time_s = + std::chrono::duration_cast<std::chrono::seconds>(posix_time).count(); + previous_time = current_time_s + Settings::values.custom_rtc_offset.GetValue(); + Setup(builder); const auto locale_check = [this]() { @@ -64,13 +70,28 @@ ConfigureSystem::ConfigureSystem(Core::System& system_, } }; + const auto update_date_offset = [this]() { + if (!checkbox_rtc->isChecked()) { + return; + } + auto offset = date_rtc_offset->value(); + offset += date_rtc->dateTime().toSecsSinceEpoch() - previous_time; + previous_time = date_rtc->dateTime().toSecsSinceEpoch(); + date_rtc_offset->setValue(offset); + }; + const auto update_rtc_date = [this]() { UpdateRtcTime(); }; + connect(combo_language, qOverload<int>(&QComboBox::currentIndexChanged), this, locale_check); connect(combo_region, qOverload<int>(&QComboBox::currentIndexChanged), this, locale_check); + connect(checkbox_rtc, qOverload<int>(&QCheckBox::stateChanged), this, update_rtc_date); + connect(date_rtc_offset, qOverload<int>(&QSpinBox::valueChanged), this, update_rtc_date); + connect(date_rtc, &QDateTimeEdit::dateTimeChanged, this, update_date_offset); ui->label_warn_invalid_locale->setVisible(false); locale_check(); SetConfiguration(); + UpdateRtcTime(); } ConfigureSystem::~ConfigureSystem() = default; @@ -120,14 +141,28 @@ void ConfigureSystem::Setup(const ConfigurationShared::Builder& builder) { continue; } + // Keep track of the region_index (and language_index) combobox to validate the selected + // settings if (setting->Id() == Settings::values.region_index.Id()) { - // Keep track of the region_index (and language_index) combobox to validate the selected - // settings combo_region = widget->combobox; - } else if (setting->Id() == Settings::values.language_index.Id()) { + } + + if (setting->Id() == Settings::values.language_index.Id()) { combo_language = widget->combobox; } + if (setting->Id() == Settings::values.custom_rtc.Id()) { + checkbox_rtc = widget->checkbox; + } + + if (setting->Id() == Settings::values.custom_rtc.Id()) { + date_rtc = widget->date_time_edit; + } + + if (setting->Id() == Settings::values.custom_rtc_offset.Id()) { + date_rtc_offset = widget->spinbox; + } + switch (setting->GetCategory()) { case Settings::Category::Core: core_hold.emplace(setting->Id(), widget); @@ -147,6 +182,19 @@ void ConfigureSystem::Setup(const ConfigurationShared::Builder& builder) { } } +void ConfigureSystem::UpdateRtcTime() { + const auto posix_time = std::chrono::system_clock::now().time_since_epoch(); + previous_time = std::chrono::duration_cast<std::chrono::seconds>(posix_time).count(); + date_rtc_offset->setEnabled(checkbox_rtc->isChecked()); + + if (checkbox_rtc->isChecked()) { + previous_time += date_rtc_offset->value(); + } + + const auto date = QDateTime::fromSecsSinceEpoch(previous_time); + date_rtc->setDateTime(date); +} + void ConfigureSystem::SetConfiguration() {} void ConfigureSystem::ApplyConfiguration() { @@ -154,4 +202,5 @@ void ConfigureSystem::ApplyConfiguration() { for (const auto& func : apply_funcs) { func(powered_on); } + UpdateRtcTime(); } diff --git a/src/yuzu/configuration/configure_system.h b/src/yuzu/configuration/configure_system.h index eab99a48a..4334211f9 100644 --- a/src/yuzu/configuration/configure_system.h +++ b/src/yuzu/configuration/configure_system.h @@ -43,6 +43,8 @@ private: void Setup(const ConfigurationShared::Builder& builder); + void UpdateRtcTime(); + std::vector<std::function<void(bool)>> apply_funcs{}; std::unique_ptr<Ui::ConfigureSystem> ui; @@ -52,4 +54,8 @@ private: QComboBox* combo_region; QComboBox* combo_language; + QCheckBox* checkbox_rtc; + QDateTimeEdit* date_rtc; + QSpinBox* date_rtc_offset; + u64 previous_time; }; diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp index 922eb1b1a..ed9c7d859 100644 --- a/src/yuzu/configuration/shared_translation.cpp +++ b/src/yuzu/configuration/shared_translation.cpp @@ -143,8 +143,10 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) { INSERT(Settings, rng_seed, tr("RNG Seed"), QStringLiteral()); INSERT(Settings, rng_seed_enabled, QStringLiteral(), QStringLiteral()); INSERT(Settings, device_name, tr("Device Name"), QStringLiteral()); - INSERT(Settings, custom_rtc, tr("Custom RTC"), QStringLiteral()); + INSERT(Settings, custom_rtc, tr("Custom RTC Date:"), QStringLiteral()); INSERT(Settings, custom_rtc_enabled, QStringLiteral(), QStringLiteral()); + INSERT(Settings, custom_rtc_offset, QStringLiteral(" "), + QStringLiteral("The number of seconds from the current unix time")); INSERT(Settings, language_index, tr("Language:"), tr("Note: this can be overridden when region setting is auto-select")); INSERT(Settings, region_index, tr("Region:"), QStringLiteral()); diff --git a/src/yuzu/debugger/console.h b/src/yuzu/debugger/console.h index fdb7d174c..2491d1ec1 100644 --- 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/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 9747e3fb3..0cbf5f45e 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp @@ -17,7 +17,7 @@ #include "core/file_sys/card_image.h" #include "core/file_sys/content_archive.h" #include "core/file_sys/control_metadata.h" -#include "core/file_sys/mode.h" +#include "core/file_sys/fs_filesystem.h" #include "core/file_sys/nca_metadata.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" @@ -347,7 +347,7 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa if (!is_dir && (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { - const auto file = vfs->OpenFile(physical_name, FileSys::Mode::Read); + const auto file = vfs->OpenFile(physical_name, FileSys::OpenMode::Read); if (!file) { return true; } diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index d8b0beadf..782bcbb61 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -35,8 +35,8 @@ #include "configuration/configure_per_game.h" #include "configuration/configure_tas.h" #include "core/file_sys/romfs_factory.h" -#include "core/file_sys/vfs.h" -#include "core/file_sys/vfs_real.h" +#include "core/file_sys/vfs/vfs.h" +#include "core/file_sys/vfs/vfs_real.h" #include "core/frontend/applets/cabinet.h" #include "core/frontend/applets/controller.h" #include "core/frontend/applets/general_frontend.h" @@ -56,7 +56,7 @@ // These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows // defines. static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper( - const FileSys::VirtualFilesystem& vfs, const std::string& path, FileSys::Mode mode) { + const FileSys::VirtualFilesystem& vfs, const std::string& path, FileSys::OpenMode mode) { return vfs->CreateDirectory(path, mode); } @@ -423,7 +423,7 @@ GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulk RemoveCachedContents(); // Gen keys if necessary - OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning); + OnCheckFirmwareDecryption(); game_list->LoadCompatibilityList(); game_list->PopulateAsync(UISettings::values.game_dirs); @@ -1574,8 +1574,6 @@ void GMainWindow::ConnectMenuEvents() { connect(multiplayer_state, &MultiplayerState::SaveConfig, this, &GMainWindow::OnSaveConfig); // Tools - connect_menu(ui->action_Rederive, std::bind(&GMainWindow::OnReinitializeKeys, this, - ReinitializeKeyBehavior::Warning)); connect_menu(ui->action_Load_Album, &GMainWindow::OnAlbum); connect_menu(ui->action_Load_Cabinet_Nickname_Owner, [this]() { OnCabinet(Service::NFP::CabinetMode::StartNicknameAndOwnerSettings); }); @@ -1882,7 +1880,7 @@ bool GMainWindow::SelectAndSetCurrentUser( void GMainWindow::ConfigureFilesystemProvider(const std::string& filepath) { // Ensure all NCAs are registered before launching the game - const auto file = vfs->OpenFile(filepath, FileSys::Mode::Read); + const auto file = vfs->OpenFile(filepath, FileSys::OpenMode::Read); if (!file) { return; } @@ -2276,7 +2274,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target open_target = tr("Save Data"); const auto nand_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir); auto vfs_nand_dir = - vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::Mode::Read); + vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::OpenMode::Read); if (has_user_save) { // User save data @@ -2501,7 +2499,7 @@ void GMainWindow::RemoveUpdateContent(u64 program_id, InstalledEntryType type) { } void GMainWindow::RemoveAddOnContent(u64 program_id, InstalledEntryType type) { - const size_t count = ContentManager::RemoveAllDLC(system.get(), program_id); + const size_t count = ContentManager::RemoveAllDLC(*system, program_id); if (count == 0) { QMessageBox::warning(this, GetGameListErrorRemoving(type), tr("There are no DLC installed for this title.")); @@ -2655,7 +2653,7 @@ void GMainWindow::RemoveCustomConfiguration(u64 program_id, const std::string& g void GMainWindow::RemoveCacheStorage(u64 program_id) { const auto nand_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir); auto vfs_nand_dir = - vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::Mode::Read); + vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::OpenMode::Read); const auto cache_storage_path = FileSys::SaveDataFactory::GetFullPath( {}, vfs_nand_dir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::CacheStorage, @@ -2675,7 +2673,8 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa "cancelled the operation.")); }; - const auto loader = Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read)); + const auto loader = + Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::OpenMode::Read)); if (loader == nullptr) { failed(); return; @@ -2719,7 +2718,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa const FileSys::PatchManager pm{title_id, system->GetFileSystemController(), installed}; auto romfs = pm.PatchRomFS(base_nca.get(), base_romfs, type, packed_update_raw, false); - const auto out = VfsFilesystemCreateDirectoryWrapper(vfs, path, FileSys::Mode::ReadWrite); + const auto out = VfsFilesystemCreateDirectoryWrapper(vfs, path, FileSys::OpenMode::ReadWrite); if (out == nullptr) { failed(); @@ -2798,8 +2797,7 @@ void GMainWindow::OnGameListVerifyIntegrity(const std::string& game_path) { return progress.wasCanceled(); }; - const auto result = - ContentManager::VerifyGameContents(system.get(), game_path, QtProgressCallback); + const auto result = ContentManager::VerifyGameContents(*system, game_path, QtProgressCallback); progress.close(); switch (result) { case ContentManager::GameVerificationResult::Success: @@ -3018,7 +3016,7 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga system->GetContentProvider()}; const auto control = pm.GetControlMetadata(); const auto loader = - Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read)); + Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::OpenMode::Read)); game_title = fmt::format("{:016X}", program_id); if (control.first != nullptr) { game_title = control.first->GetApplicationName(); @@ -3268,7 +3266,7 @@ void GMainWindow::OnMenuInstallToNAND() { return false; }; future = QtConcurrent::run([this, &file, progress_callback] { - return ContentManager::InstallNSP(system.get(), vfs.get(), file.toStdString(), + return ContentManager::InstallNSP(*system, *vfs, file.toStdString(), progress_callback); }); @@ -3371,7 +3369,7 @@ ContentManager::InstallResult GMainWindow::InstallNCA(const QString& filename) { } return false; }; - return ContentManager::InstallNCA(vfs.get(), filename.toStdString(), registered_cache, + return ContentManager::InstallNCA(*vfs, filename.toStdString(), *registered_cache, static_cast<FileSys::TitleType>(index), progress_callback); } @@ -4121,7 +4119,7 @@ void GMainWindow::OnVerifyInstalledContents() { }; const std::vector<std::string> result = - ContentManager::VerifyInstalledContents(system.get(), provider.get(), QtProgressCallback); + ContentManager::VerifyInstalledContents(*system, *provider, QtProgressCallback); progress.close(); if (result.empty()) { @@ -4551,122 +4549,20 @@ void GMainWindow::OnMouseActivity() { } } -void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { - if (behavior == ReinitializeKeyBehavior::Warning) { - const auto res = QMessageBox::information( - this, tr("Confirm Key Rederivation"), - tr("You are about to force rederive all of your keys. \nIf you do not know what " - "this " - "means or what you are doing, \nthis is a potentially destructive action. " - "\nPlease " - "make sure this is what you want \nand optionally make backups.\n\nThis will " - "delete " - "your autogenerated key files and re-run the key derivation module."), - QMessageBox::StandardButtons{QMessageBox::Ok, QMessageBox::Cancel}); - - if (res == QMessageBox::Cancel) - return; - - const auto keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir); - - Common::FS::RemoveFile(keys_dir / "prod.keys_autogenerated"); - Common::FS::RemoveFile(keys_dir / "console.keys_autogenerated"); - Common::FS::RemoveFile(keys_dir / "title.keys_autogenerated"); - } - - Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); - bool all_keys_present{true}; - - if (keys.BaseDeriveNecessary()) { - Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory("", FileSys::Mode::Read)}; - - const auto function = [this, &keys, &pdm] { - keys.PopulateFromPartitionData(pdm); - - system->GetFileSystemController().CreateFactories(*vfs); - keys.DeriveETicket(pdm, system->GetContentProvider()); - }; - - QString errors; - if (!pdm.HasFuses()) { - errors += tr("Missing fuses"); - } - if (!pdm.HasBoot0()) { - errors += tr(" - Missing BOOT0"); - } - if (!pdm.HasPackage2()) { - errors += tr(" - Missing BCPKG2-1-Normal-Main"); - } - if (!pdm.HasProdInfo()) { - errors += tr(" - Missing PRODINFO"); - } - if (!errors.isEmpty()) { - all_keys_present = false; - QMessageBox::warning( - this, tr("Derivation Components Missing"), - tr("Encryption keys are missing. " - "<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu " - "quickstart guide</a> to get all your keys, firmware and " - "games.<br><br><small>(%1)</small>") - .arg(errors)); - } - - QProgressDialog prog(this); - prog.setRange(0, 0); - prog.setLabelText(tr("Deriving keys...\nThis may take up to a minute depending \non your " - "system's performance.")); - prog.setWindowTitle(tr("Deriving Keys")); - - prog.show(); - - auto future = QtConcurrent::run(function); - while (!future.isFinished()) { - QCoreApplication::processEvents(); - } - - prog.close(); - } - +void GMainWindow::OnCheckFirmwareDecryption() { system->GetFileSystemController().CreateFactories(*vfs); - - if (all_keys_present && !this->CheckSystemArchiveDecryption()) { - LOG_WARNING(Frontend, "Mii model decryption failed"); + if (!ContentManager::AreKeysPresent()) { QMessageBox::warning( - this, tr("System Archive Decryption Failed"), - tr("Encryption keys failed to decrypt firmware. " + this, tr("Derivation Components Missing"), + tr("Encryption keys are missing. " "<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu " "quickstart guide</a> to get all your keys, firmware and " "games.")); } - SetFirmwareVersion(); - - if (behavior == ReinitializeKeyBehavior::Warning) { - game_list->PopulateAsync(UISettings::values.game_dirs); - } - UpdateMenuState(); } -bool GMainWindow::CheckSystemArchiveDecryption() { - constexpr u64 MiiModelId = 0x0100000000000802; - - auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); - if (!bis_system) { - // Not having system BIS files is not an error. - return true; - } - - auto mii_nca = bis_system->GetEntry(MiiModelId, FileSys::ContentRecordType::Data); - if (!mii_nca) { - // Not having the Mii model is not an error. - return true; - } - - // Return whether we are able to decrypt the RomFS of the Mii model. - return mii_nca->GetRomFS().get() != nullptr; -} - bool GMainWindow::CheckFirmwarePresence() { constexpr u64 MiiEditId = static_cast<u64>(Service::AM::Applets::AppletProgramId::MiiEdit); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 280fae5c3..6b72094ff 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -125,11 +125,6 @@ enum class EmulatedDirectoryTarget { SDMC, }; -enum class ReinitializeKeyBehavior { - NoWarning, - Warning, -}; - namespace VkDeviceInfo { class Record; } @@ -400,7 +395,7 @@ private slots: void OnMiiEdit(); void OnOpenControllerMenu(); void OnCaptureScreenshot(); - void OnReinitializeKeys(ReinitializeKeyBehavior behavior); + void OnCheckFirmwareDecryption(); void OnLanguageChanged(const QString& locale); void OnMouseActivity(); bool OnShutdownBegin(); @@ -441,7 +436,6 @@ private: void LoadTranslation(); void OpenPerGameConfiguration(u64 title_id, const std::string& file_name); bool CheckDarkMode(); - bool CheckSystemArchiveDecryption(); bool CheckFirmwarePresence(); void SetFirmwareVersion(); void ConfigureFilesystemProvider(const std::string& filepath); diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index e53f9951e..6a6b0821f 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -224,11 +224,6 @@ <string>&Stop</string> </property> </action> - <action name="action_Rederive"> - <property name="text"> - <string>&Reinitialize keys...</string> - </property> - </action> <action name="action_Verify_installed_contents"> <property name="text"> <string>&Verify Installed Contents</string> diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index c3cacf852..c39ace2ec 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -25,7 +25,7 @@ #include "core/cpu_manager.h" #include "core/crypto/key_manager.h" #include "core/file_sys/registered_cache.h" -#include "core/file_sys/vfs_real.h" +#include "core/file_sys/vfs/vfs_real.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/loader/loader.h" #include "core/telemetry_session.h" |