#include #include #include #include #include #include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" namespace { constexpr int SCE_RTC_ERROR_INVALID_POINTER = static_cast(0x80B50002); constexpr int SCE_RTC_ERROR_INVALID_VALUE = static_cast(0x80B50003); constexpr int SCE_RTC_ERROR_BAD_PARSE = static_cast(0x80B50007); constexpr int SCE_RTC_ERROR_INVALID_YEAR = static_cast(0x80B50008); constexpr int SCE_RTC_ERROR_INVALID_MONTH = static_cast(0x80B50009); constexpr int SCE_RTC_ERROR_INVALID_DAY = static_cast(0x80B5000A); constexpr int SCE_RTC_ERROR_INVALID_HOUR = static_cast(0x80B5000B); constexpr int SCE_RTC_ERROR_INVALID_MINUTE = static_cast(0x80B5000C); constexpr int SCE_RTC_ERROR_INVALID_SECOND = static_cast(0x80B5000D); constexpr int SCE_RTC_ERROR_INVALID_MICROSECOND = static_cast(0x80B5000E); constexpr std::int64_t TICKS_PER_SECOND = 1000000; constexpr std::int64_t TICKS_PER_MINUTE = 60 * TICKS_PER_SECOND; constexpr std::int64_t TICKS_PER_HOUR = 60 * TICKS_PER_MINUTE; constexpr std::int64_t TICKS_PER_DAY = 24 * TICKS_PER_HOUR; constexpr std::int64_t UNIX_EPOCH_DAYS = 719162; constexpr std::int64_t WIN32_EPOCH_DAYS = 584388; constexpr std::uint64_t UNIX_EPOCH_TICK = UNIX_EPOCH_DAYS * TICKS_PER_DAY; constexpr std::uint64_t WIN32_EPOCH_TICK = WIN32_EPOCH_DAYS * TICKS_PER_DAY; constexpr std::uint64_t MAX_TICK = 3652059ull * TICKS_PER_DAY - 1; bool isLeapYear(std::int64_t year) { return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; } int daysInMonth(std::int64_t year, int month) { static constexpr int days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; return month == 2 && isLeapYear(year) ? 29 : days[month - 1]; } std::int64_t daysFromCivil(std::int64_t year, unsigned month, unsigned day) { year -= month <= 2; const std::int64_t era = (year >= 0 ? year : year - 399) / 400; const auto yearOfEra = static_cast(year - era * 400); const unsigned dayOfYear = (153 * (month > 2 ? month - 3 : month + 9) + 2) / 5 + day - 1; const unsigned dayOfEra = yearOfEra * 365 + yearOfEra / 4 - yearOfEra / 100 + dayOfYear; return era * 146097 + static_cast(dayOfEra) - 719468 + UNIX_EPOCH_DAYS; } void civilFromDays(std::int64_t days, std::int64_t& year, unsigned& month, unsigned& day) { days -= UNIX_EPOCH_DAYS - 719468; const std::int64_t era = (days >= 0 ? days : days - 146096) / 146097; const auto dayOfEra = static_cast(days - era * 146097); const unsigned yearOfEra = (dayOfEra - dayOfEra / 1460 + dayOfEra / 36524 - dayOfEra / 146096) / 365; const unsigned dayOfYear = dayOfEra - (365 * yearOfEra + yearOfEra / 4 - yearOfEra / 100); const unsigned shiftedMonth = (5 * dayOfYear + 2) / 153; day = dayOfYear - (153 * shiftedMonth + 2) / 5 + 1; month = shiftedMonth < 10 ? shiftedMonth + 3 : shiftedMonth - 9; year = static_cast(yearOfEra) + era * 400 + (month <= 2); } int validate(const RtcDateTime* time) { if (!time) return SCE_RTC_ERROR_INVALID_POINTER; if (time->year < 1 || time->year > 9999) return SCE_RTC_ERROR_INVALID_YEAR; if (time->month < 1 || time->month > 12) return SCE_RTC_ERROR_INVALID_MONTH; if (time->day < 1 || time->day > daysInMonth(time->year, time->month)) return SCE_RTC_ERROR_INVALID_DAY; if (time->hour > 23) return SCE_RTC_ERROR_INVALID_HOUR; if (time->minute > 59) return SCE_RTC_ERROR_INVALID_MINUTE; if (time->second > 59) return SCE_RTC_ERROR_INVALID_SECOND; if (time->microsecond >= TICKS_PER_SECOND) return SCE_RTC_ERROR_INVALID_MICROSECOND; return 0; } std::uint64_t toTick(const RtcDateTime& time) { return static_cast(daysFromCivil(time.year, time.month, time.day)) * TICKS_PER_DAY + time.hour * TICKS_PER_HOUR + time.minute * TICKS_PER_MINUTE + time.second * TICKS_PER_SECOND + time.microsecond; } RtcDateTime fromTick(std::uint64_t tick) { std::int64_t year = 0; unsigned month = 0; unsigned day = 0; civilFromDays(static_cast(tick / TICKS_PER_DAY), year, month, day); const std::uint64_t timeOfDay = tick % TICKS_PER_DAY; return RtcDateTime{ static_cast(year), static_cast(month), static_cast(day), static_cast(timeOfDay / TICKS_PER_HOUR), static_cast(timeOfDay % TICKS_PER_HOUR / TICKS_PER_MINUTE), static_cast(timeOfDay % TICKS_PER_MINUTE / TICKS_PER_SECOND), static_cast(timeOfDay % TICKS_PER_SECOND)}; } std::uint64_t currentTick() { const auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); return UNIX_EPOCH_TICK + static_cast(now); } std::int64_t localOffsetSeconds(std::uint64_t utcTick) { const auto seconds = static_cast((static_cast(utcTick) - static_cast(UNIX_EPOCH_TICK)) / TICKS_PER_SECOND); std::tm local{}; #ifdef _WIN32 if (localtime_s(&local, &seconds) != 0) return 0; #else if (!localtime_r(&seconds, &local)) return 0; #endif const std::int64_t localSeconds = (daysFromCivil(local.tm_year + 1900, static_cast(local.tm_mon + 1), static_cast(local.tm_mday)) - UNIX_EPOCH_DAYS) * 86400 + local.tm_hour * 3600 + local.tm_min * 60 + local.tm_sec; return localSeconds - static_cast(seconds); } int addTicks(RtcTick* dst, const RtcTick* src, std::int64_t count, std::int64_t unit) { if (!dst || !src) return SCE_RTC_ERROR_INVALID_POINTER; if (src->tick > MAX_TICK) return SCE_RTC_ERROR_INVALID_VALUE; if (count != 0 && (count > std::numeric_limits::max() / unit || count < std::numeric_limits::min() / unit)) return SCE_RTC_ERROR_INVALID_VALUE; const std::int64_t delta = count * unit; if (delta < 0 ? src->tick < static_cast(-(delta + 1)) + 1 : MAX_TICK - src->tick < static_cast(delta)) return SCE_RTC_ERROR_INVALID_VALUE; dst->tick = src->tick + static_cast(delta); return 0; } int addMonths(RtcTick* dst, const RtcTick* src, std::int64_t months) { if (!dst || !src) return SCE_RTC_ERROR_INVALID_POINTER; if (src->tick > MAX_TICK) return SCE_RTC_ERROR_INVALID_VALUE; RtcDateTime time = fromTick(src->tick); const std::int64_t monthIndex = time.year * 12 + (time.month - 1) + months; const std::int64_t year = monthIndex / 12; if (monthIndex < 12 || year > 9999) return SCE_RTC_ERROR_INVALID_VALUE; time.year = static_cast(year); time.month = static_cast(monthIndex % 12 + 1); const int lastDay = daysInMonth(time.year, time.month); if (time.day > lastDay) time.day = static_cast(lastDay); dst->tick = toTick(time); return 0; } bool parseDigits(const char*& cursor, int count, int& value) { value = 0; for (int index = 0; index < count; ++index, ++cursor) { if (*cursor < '0' || *cursor > '9') return false; value = value * 10 + (*cursor - '0'); } return true; } bool parseZoneSuffix(const char*& cursor, std::int64_t& offsetMinutes) { offsetMinutes = 0; if (*cursor == 'Z' || *cursor == 'z') { ++cursor; return true; } if (*cursor == '+' || *cursor == '-') { const int sign = *cursor++ == '-' ? -1 : 1; int offsetHours = 0, offsetMinute = 0; if (!parseDigits(cursor, 2, offsetHours) || *cursor++ != ':' || !parseDigits(cursor, 2, offsetMinute)) return false; if (offsetHours > 23 || offsetMinute > 59) return false; offsetMinutes = sign * (offsetHours * 60 + offsetMinute); return true; } return false; } } extern "C" { int APS5_VABI sceRtcCheckValid(const RtcDateTime* time) { return validate(time); } int APS5_VABI sceRtcIsLeapYear(int year) { if (year < 1) return SCE_RTC_ERROR_INVALID_YEAR; return isLeapYear(year) ? 1 : 0; } int APS5_VABI sceRtcGetDaysInMonth(int year, int month) { if (year < 1) return SCE_RTC_ERROR_INVALID_YEAR; if (month < 1 || month > 12) return SCE_RTC_ERROR_INVALID_MONTH; return daysInMonth(year, month); } int APS5_VABI sceRtcGetDayOfWeek(int year, int month, int day) { if (year < 1 || year > 9999) return SCE_RTC_ERROR_INVALID_YEAR; if (month < 1 || month > 12) return SCE_RTC_ERROR_INVALID_MONTH; if (day < 1 || day > daysInMonth(year, month)) return SCE_RTC_ERROR_INVALID_DAY; return static_cast((daysFromCivil(year, static_cast(month), static_cast(day)) + 1) % 7); } int APS5_VABI sceRtcGetTickResolution(void) { return static_cast(TICKS_PER_SECOND); } int APS5_VABI sceRtcGetTick(const RtcDateTime* time, RtcTick* tick) { if (!tick) return SCE_RTC_ERROR_INVALID_POINTER; if (const int result = validate(time); result != 0) return result; tick->tick = toTick(*time); return 0; } int APS5_VABI sceRtcSetTick(RtcDateTime* time, const RtcTick* tick) { if (!time || !tick) return SCE_RTC_ERROR_INVALID_POINTER; if (tick->tick > MAX_TICK) return SCE_RTC_ERROR_INVALID_VALUE; *time = fromTick(tick->tick); return 0; } int APS5_VABI sceRtcGetCurrentTick(RtcTick* tick) { if (!tick) return SCE_RTC_ERROR_INVALID_POINTER; tick->tick = currentTick(); return 0; } int APS5_VABI sceRtcGetCurrentNetworkTick(RtcTick* tick) { return sceRtcGetCurrentTick(tick); } int APS5_VABI sceRtcGetCurrentClock(RtcDateTime* time, int time_zone_minutes) { if (!time) return SCE_RTC_ERROR_INVALID_POINTER; RtcTick tick{currentTick()}; if (const int result = addTicks(&tick, &tick, time_zone_minutes, TICKS_PER_MINUTE); result != 0) return result; *time = fromTick(tick.tick); return 0; } int APS5_VABI sceRtcGetCurrentClockLocalTime(RtcDateTime* time) { if (!time) return SCE_RTC_ERROR_INVALID_POINTER; const std::uint64_t utc = currentTick(); *time = fromTick(utc + static_cast(localOffsetSeconds(utc) * TICKS_PER_SECOND)); return 0; } int APS5_VABI sceRtcConvertUtcToLocalTime(const RtcTick* utc, RtcTick* local_time) { if (!utc || !local_time) return SCE_RTC_ERROR_INVALID_POINTER; return addTicks(local_time, utc, localOffsetSeconds(utc->tick), TICKS_PER_SECOND); } int APS5_VABI sceRtcConvertLocalTimeToUtc(const RtcTick* local_time, RtcTick* utc) { if (!local_time || !utc) return SCE_RTC_ERROR_INVALID_POINTER; const std::int64_t offset = localOffsetSeconds(local_time->tick - static_cast(localOffsetSeconds(local_time->tick) * TICKS_PER_SECOND)); return addTicks(utc, local_time, -offset, TICKS_PER_SECOND); } int APS5_VABI sceRtcGetTime_t(const RtcDateTime* time, int64_t* seconds) { if (!seconds) return SCE_RTC_ERROR_INVALID_POINTER; if (const int result = validate(time); result != 0) return result; const std::uint64_t tick = toTick(*time); if (tick < UNIX_EPOCH_TICK) return SCE_RTC_ERROR_INVALID_VALUE; *seconds = static_cast((tick - UNIX_EPOCH_TICK) / TICKS_PER_SECOND); return 0; } int APS5_VABI sceRtcSetTime_t(RtcDateTime* time, int64_t seconds) { if (!time) return SCE_RTC_ERROR_INVALID_POINTER; if (seconds < 0 || static_cast(seconds) > (MAX_TICK - UNIX_EPOCH_TICK) / TICKS_PER_SECOND) return SCE_RTC_ERROR_INVALID_VALUE; *time = fromTick(UNIX_EPOCH_TICK + static_cast(seconds) * TICKS_PER_SECOND); return 0; } int APS5_VABI sceRtcGetWin32FileTime(const RtcDateTime* time, uint64_t* win32_time) { if (!win32_time) return SCE_RTC_ERROR_INVALID_POINTER; if (const int result = validate(time); result != 0) return result; const std::uint64_t tick = toTick(*time); if (tick < WIN32_EPOCH_TICK) return SCE_RTC_ERROR_INVALID_VALUE; *win32_time = (tick - WIN32_EPOCH_TICK) * 10; return 0; } int APS5_VABI sceRtcSetWin32FileTime(RtcDateTime* time, uint64_t win32_time) { if (!time) return SCE_RTC_ERROR_INVALID_POINTER; if (win32_time / 10 > MAX_TICK - WIN32_EPOCH_TICK) return SCE_RTC_ERROR_INVALID_VALUE; *time = fromTick(WIN32_EPOCH_TICK + win32_time / 10); return 0; } int APS5_VABI sceRtcFormatRFC3339(char* date_time, const RtcTick* utc, int time_zone_minutes) { if (!date_time || !utc) return SCE_RTC_ERROR_INVALID_POINTER; RtcTick local{}; if (const int result = addTicks(&local, utc, time_zone_minutes, TICKS_PER_MINUTE); result != 0) return result; const RtcDateTime time = fromTick(local.tick); int written = std::snprintf(date_time, 23, "%04u-%02u-%02uT%02u:%02u:%02u.%02u", time.year, time.month, time.day, time.hour, time.minute, time.second, time.microsecond / 10000); if (time_zone_minutes == 0) { std::snprintf(date_time + written, 2, "Z"); } else { const int offset = time_zone_minutes < 0 ? -time_zone_minutes : time_zone_minutes; std::snprintf(date_time + written, 7, "%c%02d:%02d", time_zone_minutes < 0 ? '-' : '+', offset / 60, offset % 60); } return 0; } int APS5_VABI sceRtcParseRFC3339(RtcTick* utc, const char* date_time) { if (!utc || !date_time) return SCE_RTC_ERROR_INVALID_POINTER; const char* cursor = date_time; int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0; if (!parseDigits(cursor, 4, year) || *cursor++ != '-' || !parseDigits(cursor, 2, month) || *cursor++ != '-' || !parseDigits(cursor, 2, day)) return SCE_RTC_ERROR_BAD_PARSE; if (*cursor != 'T' && *cursor != 't' && *cursor != ' ') return SCE_RTC_ERROR_BAD_PARSE; ++cursor; if (!parseDigits(cursor, 2, hour) || *cursor++ != ':' || !parseDigits(cursor, 2, minute) || *cursor++ != ':' || !parseDigits(cursor, 2, second)) return SCE_RTC_ERROR_BAD_PARSE; std::uint32_t microsecond = 0; if (*cursor == '.') { ++cursor; std::uint32_t scale = 100000; if (*cursor < '0' || *cursor > '9') return SCE_RTC_ERROR_BAD_PARSE; for (; *cursor >= '0' && *cursor <= '9'; ++cursor, scale /= 10) microsecond += static_cast(*cursor - '0') * scale; } std::int64_t offsetMinutes = 0; if (!parseZoneSuffix(cursor, offsetMinutes)) return SCE_RTC_ERROR_BAD_PARSE; if (*cursor != 0) return SCE_RTC_ERROR_BAD_PARSE; const RtcDateTime time{static_cast(year), static_cast(month), static_cast(day), static_cast(hour), static_cast(minute), static_cast(second), microsecond}; if (const int result = validate(&time); result != 0) return result; const RtcTick local{toTick(time)}; return addTicks(utc, &local, -offsetMinutes, TICKS_PER_MINUTE); } int APS5_VABI sceRtcParseDateTime(RtcTick* utc, const char* date_time) { if (!utc || !date_time) return SCE_RTC_ERROR_INVALID_POINTER; const char* cursor = date_time; int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0; bool ok = parseDigits(cursor, 4, year) && *cursor++ == '-' && parseDigits(cursor, 2, month) && *cursor++ == '-' && parseDigits(cursor, 2, day); ok = ok && (*cursor == 'T' || *cursor == 't' || *cursor == ' '); if (ok) ++cursor; ok = ok && parseDigits(cursor, 2, hour) && *cursor++ == ':' && parseDigits(cursor, 2, minute) && *cursor++ == ':' && parseDigits(cursor, 2, second); std::uint32_t microsecond = 0; if (ok && *cursor == '.') { ++cursor; std::uint32_t scale = 100000; ok = *cursor >= '0' && *cursor <= '9'; for (; ok && *cursor >= '0' && *cursor <= '9'; ++cursor, scale /= 10) microsecond += static_cast(*cursor - '0') * scale; } std::int64_t offsetMinutes = 0; if (ok && *cursor != 0) ok = parseZoneSuffix(cursor, offsetMinutes); if (ok && *cursor != 0) ok = false; if (!ok) { NotImplemented_nid_no_patch(__func__); return SCE_RTC_ERROR_BAD_PARSE; } const RtcDateTime time{static_cast(year), static_cast(month), static_cast(day), static_cast(hour), static_cast(minute), static_cast(second), microsecond}; if (const int result = validate(&time); result != 0) return result; const RtcTick local{toTick(time)}; return addTicks(utc, &local, -offsetMinutes, TICKS_PER_MINUTE); } int APS5_VABI sceRtcTickAddTicks(RtcTick* dst, const RtcTick* src, int64_t ticks) { return addTicks(dst, src, ticks, 1); } int APS5_VABI sceRtcTickAddMicroseconds(RtcTick* dst, const RtcTick* src, int64_t usec) { return addTicks(dst, src, usec, 1); } int APS5_VABI sceRtcTickAddSeconds(RtcTick* dst, const RtcTick* src, int64_t seconds) { return addTicks(dst, src, seconds, TICKS_PER_SECOND); } int APS5_VABI sceRtcTickAddMinutes(RtcTick* dst, const RtcTick* src, int64_t minutes) { return addTicks(dst, src, minutes, TICKS_PER_MINUTE); } int APS5_VABI sceRtcTickAddHours(RtcTick* dst, const RtcTick* src, int32_t hours) { return addTicks(dst, src, hours, TICKS_PER_HOUR); } int APS5_VABI sceRtcTickAddDays(RtcTick* dst, const RtcTick* src, int32_t days) { return addTicks(dst, src, days, TICKS_PER_DAY); } int APS5_VABI sceRtcTickAddWeeks(RtcTick* dst, const RtcTick* src, int32_t weeks) { return addTicks(dst, src, weeks, 7 * TICKS_PER_DAY); } int APS5_VABI sceRtcTickAddMonths(RtcTick* dst, const RtcTick* src, int32_t months) { return addMonths(dst, src, months); } int APS5_VABI sceRtcTickAddYears(RtcTick* dst, const RtcTick* src, int16_t years) { return addMonths(dst, src, static_cast(years) * 12); } }