1 // Copyright (c) 2012- PPSSPP Project.
2 
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
6 
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 // GNU General Public License 2.0 for more details.
11 
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
14 
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17 
18 #ifdef _WIN32
19 #include "Common/CommonWindows.h"
20  // timeval already defined in xtl.h
21 #include <Winsock2.h>
22 #else
23 #include <sys/time.h>
24 #endif
25 
26 #include <time.h>
27 
28 #include "Common/Serialize/Serializer.h"
29 #include "Common/Serialize/SerializeFuncs.h"
30 #include "Common/TimeUtil.h"
31 #include "Core/HLE/HLE.h"
32 #include "Core/HLE/FunctionWrappers.h"
33 #include "Core/MIPS/MIPS.h"
34 #include "Core/Reporting.h"
35 #include "Core/CoreTiming.h"
36 #include "Core/MemMapHelpers.h"
37 
38 #include "Core/HLE/sceKernel.h"
39 #include "Core/HLE/sceRtc.h"
40 
41 #ifdef HAVE_LIBNX
42 // I guess that works...
43 #define setenv(x, y, z) (void*)0
44 #define tzset() (void*)0
45 #define unsetenv(x) (void*)0
46 #endif // HAVE_LIBNX
47 
48 // This is a base time that everything is relative to.
49 // This way, time doesn't move strangely with savestates, turbo speed, etc.
50 static PSPTimeval rtcBaseTime;
51 static u64 rtcBaseTicks;
52 
53 // Grabbed from JPSCP
54 // This is the # of microseconds between January 1, 0001 and January 1, 1970.
55 const u64 rtcMagicOffset = 62135596800000000ULL;
56 // This is the # of microseconds between January 1, 0001 and January 1, 1601 (for Win32 FILETIME.)
57 const u64 rtcFiletimeOffset = 50491123200000000ULL;
58 
59 // 400 years is a convenient number, since leap days and everything cycle every 400 years.
60 // 400 years is in other words 20871 full weeks.
61 const u64 rtc400YearTicks = (u64)20871 * 7 * 24 * 3600 * 1000000ULL;
62 
63 // This is the last moment the clock was adjusted.
64 // It's possible games may not like the clock being adjusted in the past hour (cheating?)
65 // So this returns a static time.
66 const u64 rtcLastAdjustedTicks = rtcMagicOffset + 41 * 365 * 24 * 3600 * 1000000ULL;
67 // The reincarnated time seems related to the battery or manufacturing date.
68 // On a test PSP, it was over 3 years in the past, so we again pick a fixed date.
69 const u64 rtcLastReincarnatedTicks = rtcMagicOffset + 40 * 365 * 24 * 3600 * 1000000ULL;
70 
71 const int PSP_TIME_INVALID_YEAR = -1;
72 const int PSP_TIME_INVALID_MONTH = -2;
73 const int PSP_TIME_INVALID_DAY = -3;
74 const int PSP_TIME_INVALID_HOUR = -4;
75 const int PSP_TIME_INVALID_MINUTES = -5;
76 const int PSP_TIME_INVALID_SECONDS = -6;
77 const int PSP_TIME_INVALID_MICROSECONDS = -7;
78 
__RtcGetCurrentTick()79 static u64 __RtcGetCurrentTick()
80 {
81 	// TODO: It's probably expecting ticks since January 1, 0001?
82 	return CoreTiming::GetGlobalTimeUs() + rtcBaseTicks;
83 }
84 
85 #if defined(__MINGW32__)
_get_timezone(long * seconds)86 errno_t _get_timezone(long *seconds)
87 {
88   time_t now = time(NULL);
89 
90   struct tm *gm = gmtime(&now);
91   time_t gmt = mktime(gm);
92 
93   struct tm *loc = localtime(&now);
94   time_t local = mktime(loc);
95 
96   *seconds = local - gmt;
97   return 0;
98 }
99 #endif
100 
101 #if defined(_WIN32)
102 #define FILETIME_FROM_UNIX_EPOCH_US (rtcMagicOffset - rtcFiletimeOffset)
103 
gettimeofday(timeval * tv,void * ignore)104 void gettimeofday(timeval *tv, void *ignore)
105 {
106 	FILETIME ft_utc, ft_local;
107 	GetSystemTimeAsFileTime(&ft_utc);
108 	ft_local = ft_utc;
109 
110 	u64 from_1601_us = (((u64) ft_local.dwHighDateTime << 32ULL) + (u64) ft_local.dwLowDateTime) / 10ULL;
111 	u64 from_1970_us = from_1601_us - FILETIME_FROM_UNIX_EPOCH_US;
112 
113 	tv->tv_sec = long(from_1970_us / 1000000UL);
114 	tv->tv_usec = from_1970_us % 1000000UL;
115 }
116 
rtc_timegm(struct tm * tm)117 time_t rtc_timegm(struct tm *tm)
118 {
119 	return _mkgmtime(tm);
120 }
121 
122 #elif (defined(__GLIBC__) && !defined(__ANDROID__))
123 #define rtc_timegm timegm
124 #else
125 
rtc_timegm(struct tm * tm)126 static time_t rtc_timegm(struct tm *tm)
127 {
128 	time_t ret;
129 	char *tz;
130 	std::string tzcopy;
131 
132 	tz = getenv("TZ");
133 	if (tz)
134 		tzcopy = tz;
135 
136 	setenv("TZ", "", 1);
137 	tzset();
138 	ret = mktime(tm);
139 	if (tz)
140 		setenv("TZ", tzcopy.c_str(), 1);
141 	else
142 		unsetenv("TZ");
143 	tzset();
144 	return ret;
145 }
146 
147 #endif
148 
RtcUpdateBaseTicks()149 static void RtcUpdateBaseTicks() {
150 	rtcBaseTicks = 1000000ULL * rtcBaseTime.tv_sec + rtcBaseTime.tv_usec + rtcMagicOffset;
151 }
152 
__RtcInit()153 void __RtcInit()
154 {
155 	// This is the base time, the only case we use gettimeofday() for.
156 	// Everything else is relative to that, "virtual time."
157 	timeval tv;
158 	gettimeofday(&tv, NULL);
159 	rtcBaseTime.tv_sec = tv.tv_sec;
160 	rtcBaseTime.tv_usec = 0;
161 	// Precalculate the current time in microseconds (rtcMagicOffset is offset to 1970.)
162 	RtcUpdateBaseTicks();
163 }
164 
__RtcDoState(PointerWrap & p)165 void __RtcDoState(PointerWrap &p)
166 {
167 	auto s = p.Section("sceRtc", 1);
168 	if (!s)
169 		return;
170 
171 	Do(p, rtcBaseTime);
172 	// Update the precalc, pointless to savestate this as it's just based on the other value.
173 	RtcUpdateBaseTicks();
174 }
175 
__RtcTimeOfDay(PSPTimeval * tv)176 void __RtcTimeOfDay(PSPTimeval *tv)
177 {
178 	s64 additionalUs = CoreTiming::GetGlobalTimeUs();
179 	*tv = rtcBaseTime;
180 
181 	s64 adjustedUs = additionalUs + tv->tv_usec;
182 	tv->tv_sec += long(adjustedUs / 1000000UL);
183 	tv->tv_usec = adjustedUs % 1000000UL;
184 }
185 
RtcBaseTime(int32_t * micro)186 int32_t RtcBaseTime(int32_t *micro) {
187 	if (micro) {
188 		*micro = rtcBaseTime.tv_usec;
189 	}
190 	return rtcBaseTime.tv_sec;
191 }
192 
RtcSetBaseTime(int32_t seconds,int32_t micro)193 void RtcSetBaseTime(int32_t seconds, int32_t micro) {
194 	rtcBaseTime.tv_sec = seconds;
195 	rtcBaseTime.tv_usec = micro;
196 	RtcUpdateBaseTicks();
197 }
198 
__RtcTmToPspTime(ScePspDateTime & t,const tm * val)199 static void __RtcTmToPspTime(ScePspDateTime &t, const tm *val)
200 {
201 	t.year = val->tm_year + 1900;
202 	t.month = val->tm_mon + 1;
203 	t.day = val->tm_mday;
204 	t.hour = val->tm_hour;
205 	t.minute = val->tm_min;
206 	t.second = val->tm_sec;
207 	t.microsecond = 0;
208 }
209 
__RtcPspTimeToTm(tm & val,const ScePspDateTime & pt)210 static void __RtcPspTimeToTm(tm &val, const ScePspDateTime &pt)
211 {
212 	val.tm_year = pt.year - 1900;
213 	val.tm_mon = pt.month - 1;
214 	val.tm_mday = pt.day;
215 	val.tm_wday = -1;
216 	val.tm_yday = -1;
217 	val.tm_hour = pt.hour;
218 	val.tm_min = pt.minute;
219 	val.tm_sec = pt.second;
220 	val.tm_isdst = 0;
221 }
222 
__RtcTicksToPspTime(ScePspDateTime & t,u64 ticks)223 static void __RtcTicksToPspTime(ScePspDateTime &t, u64 ticks)
224 {
225 	int numYearAdd = 0;
226 	if (ticks < 1000000ULL)
227 	{
228 		t.year = 1;
229 		t.month = 1;
230 		t.day = 1;
231 		t.hour = 0;
232 		t.minute = 0;
233 		t.second = 0;
234 		t.microsecond = ticks % 1000000ULL;
235 		return;
236 	}
237 	else if (ticks < rtcMagicOffset)
238 	{
239 		// Need to get a year past 1970 for gmtime
240 		// Add enough 400 year to pass over 1970.
241 		numYearAdd = (int) ((rtcMagicOffset - ticks) / rtc400YearTicks + 1);
242 		ticks += rtc400YearTicks * numYearAdd;
243 	}
244 
245 	while (ticks >= rtcMagicOffset + rtc400YearTicks)
246 	{
247 		ticks -= rtc400YearTicks;
248 		--numYearAdd;
249 	}
250 
251 	time_t time = (ticks - rtcMagicOffset) / 1000000ULL;
252 	t.microsecond = ticks % 1000000ULL;
253 
254 	tm *local = gmtime(&time);
255 	if (!local)
256 	{
257 		ERROR_LOG(SCERTC, "Date is too high/low to handle, pretending to work.");
258 		return;
259 	}
260 
261 	t.year = local->tm_year + 1900 - numYearAdd * 400;
262 	t.month = local->tm_mon + 1;
263 	t.day = local->tm_mday;
264 	t.hour = local->tm_hour;
265 	t.minute = local->tm_min;
266 	t.second = local->tm_sec;
267 }
268 
__RtcPspTimeToTicks(const ScePspDateTime & pt)269 static u64 __RtcPspTimeToTicks(const ScePspDateTime &pt)
270 {
271 	tm local;
272 	__RtcPspTimeToTm(local, pt);
273 
274 	s64 tickOffset = 0;
275 	while (local.tm_year < 70)
276 	{
277 		tickOffset -= rtc400YearTicks;
278 		local.tm_year += 400;
279 	}
280 	while (local.tm_year >= 470)
281 	{
282 		tickOffset += rtc400YearTicks;
283 		local.tm_year -= 400;
284 	}
285 
286 	time_t seconds = rtc_timegm(&local);
287 	u64 result = rtcMagicOffset + (u64) seconds * 1000000ULL;
288 	result += pt.microsecond;
289 	return result + tickOffset;
290 }
291 
__RtcValidatePspTime(const ScePspDateTime & t)292 static bool __RtcValidatePspTime(const ScePspDateTime &t)
293 {
294 	return t.year > 0 && t.year <= 9999;
295 }
296 
sceRtcGetTickResolution()297 static u32 sceRtcGetTickResolution()
298 {
299 	DEBUG_LOG(SCERTC, "sceRtcGetTickResolution()");
300 	return 1000000;
301 }
302 
sceRtcGetCurrentTick(u32 tickPtr)303 static u32 sceRtcGetCurrentTick(u32 tickPtr)
304 {
305 	VERBOSE_LOG(SCERTC, "sceRtcGetCurrentTick(%08x)", tickPtr);
306 
307 	u64 curTick = __RtcGetCurrentTick();
308 	if (Memory::IsValidAddress(tickPtr))
309 		Memory::Write_U64(curTick, tickPtr);
310 	hleEatCycles(300);
311 	hleReSchedule("rtc current tick");
312 	return 0;
313 }
314 
sceRtcGetAccumulativeTime()315 static u64 sceRtcGetAccumulativeTime()
316 {
317 	DEBUG_LOG(SCERTC, "sceRtcGetAccumulativeTime()");
318 	hleEatCycles(300);
319 	hleReSchedule("rtc accumulative time");
320 	return __RtcGetCurrentTick();
321 }
322 
sceRtcGetCurrentClock(u32 pspTimePtr,int tz)323 static u32 sceRtcGetCurrentClock(u32 pspTimePtr, int tz) {
324 	auto pt = PSPPointer<ScePspDateTime>::Create(pspTimePtr);
325 
326 	PSPTimeval tv;
327 	__RtcTimeOfDay(&tv);
328 
329 	time_t sec = (time_t)tv.tv_sec;
330 	tm *utc = gmtime(&sec);
331 	if (!utc) {
332 		return hleLogError(SCERTC, 0, "Date is too high/low to handle, pretending to work");
333 	}
334 
335 	utc->tm_isdst = -1;
336 	utc->tm_min += tz;
337 	rtc_timegm(utc); // Return gmt time with timezone offset.
338 
339 	if (pt.IsValid()) {
340 		__RtcTmToPspTime(*pt, utc);
341 		pt->microsecond = tv.tv_usec;
342 	}
343 
344 	hleEatCycles(1900);
345 	hleReSchedule("rtc current clock");
346 	return hleLogSuccessI(SCERTC, 0);
347 }
348 
sceRtcGetCurrentClockLocalTime(u32 pspTimePtr)349 static u32 sceRtcGetCurrentClockLocalTime(u32 pspTimePtr) {
350 	auto pt = PSPPointer<ScePspDateTime>::Create(pspTimePtr);
351 
352 	PSPTimeval tv;
353 	__RtcTimeOfDay(&tv);
354 
355 	time_t sec = (time_t)tv.tv_sec;
356 	const tm *local = localtime(&sec);
357 	if (!local) {
358 		return hleLogError(SCERTC, 0, "Date is too high/low to handle, pretending to work");
359 	}
360 
361 	if (pt.IsValid()) {
362 		__RtcTmToPspTime(*pt, local);
363 		pt->microsecond = tv.tv_usec;
364 	}
365 
366 	hleEatCycles(2000);
367 	hleReSchedule("rtc current clock local");
368 	return hleLogSuccessI(SCERTC, 0);
369 }
370 
sceRtcSetTick(u32 pspTimePtr,u32 tickPtr)371 static u32 sceRtcSetTick(u32 pspTimePtr, u32 tickPtr) {
372 	auto pt = PSPPointer<ScePspDateTime>::Create(pspTimePtr);
373 	auto tick = PSPPointer<u64_le>::Create(tickPtr);
374 
375 	if (!pt.IsValid() || !tick.IsValid())
376 		return hleLogError(SCERTC, 0, "bad address");
377 
378 	__RtcTicksToPspTime(*pt, *tick);
379 	return hleLogSuccessI(SCERTC, 0);
380 }
381 
sceRtcGetTick(u32 pspTimePtr,u32 tickPtr)382 static u32 sceRtcGetTick(u32 pspTimePtr, u32 tickPtr) {
383 	auto pt = PSPPointer<const ScePspDateTime>::Create(pspTimePtr);
384 	auto tick = PSPPointer<u64_le>::Create(tickPtr);
385 
386 	if (!pt.IsValid() || !tick.IsValid())
387 		return hleLogError(SCERTC, 0, "bad address");
388 	if (!__RtcValidatePspTime(*pt))
389 		return hleLogWarning(SCERTC, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid time");
390 
391 	*tick = __RtcPspTimeToTicks(*pt);
392 	return hleLogSuccessI(SCERTC, 0);
393 }
394 
sceRtcGetDayOfWeek(u32 year,u32 month,u32 day)395 static u32 sceRtcGetDayOfWeek(u32 year, u32 month, u32 day)
396 {
397 	DEBUG_LOG(SCERTC, "sceRtcGetDayOfWeek(%d, %d, %d)", year, month, day);
398 
399 	if(month == 0)	// Mark month 0 as august, don't know why, but works
400 	{
401 		month = 8;
402 	}
403 
404 	if(month > 12) // After month 12, psp months does 31/31/30/31/30 and repeat
405 	{
406 		int restMonth = month-12;
407 		int grp5 = restMonth / 5;
408 		restMonth = restMonth % 5;
409 		day += grp5 * (31*3+30*2);
410 		static u32 t[] = { 31, 31*2, 31*2+30, 31*3+30, 31*3+30*2 };
411 		day += t[restMonth-1];
412 		month = 12;
413 	}
414 
415 	while (year < 1900)
416 		year += 400;
417 	while (year > 2300)
418 		year -= 400;
419 
420 	tm local;
421 	local.tm_year = year - 1900;
422 	local.tm_mon = month - 1;
423 	local.tm_mday = day;
424 	local.tm_wday = -1;
425 	local.tm_yday = -1;
426 	local.tm_hour = 0;
427 	local.tm_min = 0;
428 	local.tm_sec = 0;
429 	local.tm_isdst = -1;
430 
431 	mktime(&local);
432 	return local.tm_wday;
433 }
434 
__RtcIsLeapYear(u32 year)435 static bool __RtcIsLeapYear(u32 year)
436 {
437 	return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
438 }
439 
__RtcDaysInMonth(u32 year,u32 month)440 static int __RtcDaysInMonth(u32 year, u32 month)
441 {
442 	switch (month)
443 	{
444 	case 4:
445 	case 6:
446 	case 9:
447 	case 11:
448 		return 30;
449 
450 	case 2:
451 		if (__RtcIsLeapYear(year))
452 			return 29;
453 		return 28;
454 
455 	default:
456 		return 31;
457 	}
458 }
459 
sceRtcGetDaysInMonth(u32 year,u32 month)460 static u32 sceRtcGetDaysInMonth(u32 year, u32 month)
461 {
462 	DEBUG_LOG(SCERTC, "sceRtcGetDaysInMonth(%d, %d)", year, month);
463 
464 	if (year == 0 || month == 0 || month > 12)
465 		return SCE_KERNEL_ERROR_INVALID_ARGUMENT;
466 
467 	return __RtcDaysInMonth(year, month);
468 }
469 
sceRtcIsLeapYear(u32 year)470 static u32 sceRtcIsLeapYear(u32 year)
471 {
472 	DEBUG_LOG(SCERTC, "sceRtcIsLeapYear(%d)", year);
473 	return __RtcIsLeapYear(year) ? 1 : 0;
474 }
475 
sceRtcConvertLocalTimeToUTC(u32 tickLocalPtr,u32 tickUTCPtr)476 static int sceRtcConvertLocalTimeToUTC(u32 tickLocalPtr,u32 tickUTCPtr)
477 {
478 	DEBUG_LOG(SCERTC, "sceRtcConvertLocalTimeToUTC(%d, %d)", tickLocalPtr, tickUTCPtr);
479 	if (Memory::IsValidAddress(tickLocalPtr) && Memory::IsValidAddress(tickUTCPtr))
480 	{
481 		u64 srcTick = Memory::Read_U64(tickLocalPtr);
482 		// TODO : Let the user select his timezone / daylight saving instead of taking system param ?
483 #ifdef _WIN32
484 		long timezone_val;
485 		_get_timezone(&timezone_val);
486 		srcTick -= -timezone_val * 1000000ULL;
487 #elif !defined(_AIX) && !defined(__sgi) && !defined(__hpux) && !defined(HAVE_LIBNX)
488 		time_t timezone = 0;
489 		tm *time = localtime(&timezone);
490 		srcTick -= time->tm_gmtoff*1000000ULL;
491 #endif
492 		Memory::Write_U64(srcTick, tickUTCPtr);
493 	}
494 	else
495 	{
496 		return 1;
497 	}
498 	return 0;
499 }
500 
sceRtcConvertUtcToLocalTime(u32 tickUTCPtr,u32 tickLocalPtr)501 static int sceRtcConvertUtcToLocalTime(u32 tickUTCPtr,u32 tickLocalPtr)
502 {
503 	DEBUG_LOG(SCERTC, "sceRtcConvertLocalTimeToUTC(%d, %d)", tickLocalPtr, tickUTCPtr);
504 	if (Memory::IsValidAddress(tickLocalPtr) && Memory::IsValidAddress(tickUTCPtr))
505 	{
506 		u64 srcTick = Memory::Read_U64(tickUTCPtr);
507 		// TODO : Let the user select his timezone / daylight saving instead of taking system param ?
508 #ifdef _WIN32
509 		long timezone_val;
510 		_get_timezone(&timezone_val);
511 		srcTick += -timezone_val * 1000000ULL;
512 #elif !defined(_AIX) && !defined(__sgi) && !defined(__hpux) && !defined(HAVE_LIBNX)
513 		time_t timezone = 0;
514 		tm *time = localtime(&timezone);
515 		srcTick += time->tm_gmtoff*1000000ULL;
516 #endif
517 		Memory::Write_U64(srcTick, tickLocalPtr);
518 	}
519 	else
520 	{
521 		return 1;
522 	}
523 	return 0;
524 }
525 
sceRtcCheckValid(u32 datePtr)526 static int sceRtcCheckValid(u32 datePtr) {
527 	auto pt = PSPPointer<const ScePspDateTime>::Create(datePtr);
528 
529 	if (!pt.IsValid() )
530 		return hleLogError(SCERTC, -1, "bad address");
531 
532 	int result = 0;
533 	if (pt->year < 1 || pt->year > 9999)
534 		result = PSP_TIME_INVALID_YEAR;
535 	else if (pt->month < 1 || pt->month > 12)
536 		result = PSP_TIME_INVALID_MONTH;
537 	else if (pt->day < 1 || pt->day > 31)
538 		result = PSP_TIME_INVALID_DAY;
539 	else if (pt->day > __RtcDaysInMonth((s16)pt->year, (s16)pt->month))
540 		result = PSP_TIME_INVALID_DAY;
541 	else if (pt->hour < 0 || pt->hour > 23)
542 		result = PSP_TIME_INVALID_HOUR;
543 	else if (pt->minute < 0 || pt->minute > 59)
544 		result = PSP_TIME_INVALID_MINUTES;
545 	else if (pt->second < 0 || pt->second > 59)
546 		result = PSP_TIME_INVALID_SECONDS;
547 	else if (pt->microsecond >= 1000000UL)
548 		result = PSP_TIME_INVALID_MICROSECONDS;
549 	return hleLogSuccessI(SCERTC, result);
550 }
551 
sceRtcSetTime_t(u32 datePtr,u32 time)552 static int sceRtcSetTime_t(u32 datePtr, u32 time) {
553 	auto pt = PSPPointer<ScePspDateTime>::Create(datePtr);
554 	if (!pt.IsValid())
555 		return hleLogError(SCERTC, 1, "bad address");
556 
557 	__RtcTicksToPspTime(*pt, time * 1000000ULL + rtcMagicOffset);
558 	return hleLogSuccessI(SCERTC, 0);
559 }
560 
sceRtcSetTime64_t(u32 datePtr,u64 time)561 static int sceRtcSetTime64_t(u32 datePtr, u64 time) {
562 	auto pt = PSPPointer<ScePspDateTime>::Create(datePtr);
563 	if (!pt.IsValid())
564 		return hleLogError(SCERTC, 1, "bad address");
565 
566 	__RtcTicksToPspTime(*pt, time * 1000000ULL + rtcMagicOffset);
567 	return hleLogSuccessI(SCERTC, 0);
568 }
569 
sceRtcGetTime_t(u32 datePtr,u32 timePtr)570 static int sceRtcGetTime_t(u32 datePtr, u32 timePtr) {
571 	auto pt = PSPPointer<const ScePspDateTime>::Create(datePtr);
572 	auto timep = PSPPointer<u32_le>::Create(timePtr);
573 	if (!pt.IsValid() || !timep.IsValid())
574 		return hleLogError(SCERTC, 1, "bad address");
575 
576 	*timep = (u32)((__RtcPspTimeToTicks(*pt) - rtcMagicOffset) / 1000000ULL);
577 	return hleLogSuccessI(SCERTC, 0);
578 }
579 
sceRtcGetTime64_t(u32 datePtr,u32 timePtr)580 static int sceRtcGetTime64_t(u32 datePtr, u32 timePtr) {
581 	auto pt = PSPPointer<const ScePspDateTime>::Create(datePtr);
582 	auto timep = PSPPointer<u64_le>::Create(timePtr);
583 	if (!pt.IsValid() || !timep.IsValid())
584 		return hleLogError(SCERTC, 1, "bad address");
585 
586 	*timep = (__RtcPspTimeToTicks(*pt) - rtcMagicOffset) / 1000000ULL;
587 	return hleLogSuccessI(SCERTC, 0);
588 }
589 
sceRtcSetDosTime(u32 datePtr,u32 dosTime)590 static int sceRtcSetDosTime(u32 datePtr, u32 dosTime) {
591 	auto pt = PSPPointer<ScePspDateTime>::Create(datePtr);
592 	if (!pt.IsValid())
593 		return hleLogError(SCERTC, 1, "bad address");
594 
595 	int hms = dosTime & 0xFFFF;
596 	int ymd = dosTime >> 16;
597 
598 	pt->year = 1980 + (ymd >> 9);
599 	pt->month = (ymd >> 5) & 0xF;
600 	pt->day = ymd & 0x1F;
601 	pt->hour = hms >> 11;
602 	pt->minute = (hms >> 5) & 0x3F;
603 	pt->second = (hms << 1) & 0x3E;
604 	pt->microsecond = 0;
605 
606 	return hleLogSuccessI(SCERTC, 0);
607 }
608 
sceRtcGetDosTime(u32 datePtr,u32 dosTime)609 static int sceRtcGetDosTime(u32 datePtr, u32 dosTime) {
610 	auto datep = PSPPointer<ScePspDateTime>::Create(datePtr);
611 	auto dosp = PSPPointer<u32_le>::Create(dosTime);
612 	if (!datep.IsValid() || !dosp.IsValid())
613 		return hleLogError(SCERTC, -1, "bad address");
614 
615 	if (datep->year < 1980) {
616 		*dosp = 0;
617 		return hleLogWarning(SCERTC, -1, "invalid year");
618 	} else if (datep->year >= 2108) {
619 		*dosp = 0xFF9FBF7D;
620 		return hleLogWarning(SCERTC, -1, "invalid year");
621 	}
622 
623 	int year = ((datep->year - 1980) & 0x7F) << 9;
624 	int month = (datep->month & 0xF) << 5;
625 	int hour = (datep->hour & 0x1F) << 11;
626 	int minute = (datep->minute & 0x3F) << 5;
627 	int day = datep->day & 0x1F;
628 	int second = (datep->second >> 1) & 0x1F;
629 	int ymd = year | month | day;
630 	int hms = hour | minute | second;
631 
632 	*dosp = (ymd << 16) | hms;
633 	return hleLogSuccessI(SCERTC, 0);
634 }
635 
sceRtcSetWin32FileTime(u32 datePtr,u64 win32Time)636 static int sceRtcSetWin32FileTime(u32 datePtr, u64 win32Time)
637 {
638 	if (!Memory::IsValidAddress(datePtr))
639 	{
640 		ERROR_LOG_REPORT(SCERTC, "sceRtcSetWin32FileTime(%08x, %lld): invalid address", datePtr, win32Time);
641 		return -1;
642 	}
643 
644 	DEBUG_LOG(SCERTC, "sceRtcSetWin32FileTime(%08x, %lld)", datePtr, win32Time);
645 
646 	u64 ticks = (win32Time / 10) + rtcFiletimeOffset;
647 	auto pspTime = PSPPointer<ScePspDateTime>::Create(datePtr);
648 	__RtcTicksToPspTime(*pspTime, ticks);
649 	return 0;
650 }
651 
sceRtcGetWin32FileTime(u32 datePtr,u32 win32TimePtr)652 static int sceRtcGetWin32FileTime(u32 datePtr, u32 win32TimePtr)
653 {
654 	if (!Memory::IsValidAddress(datePtr))
655 	{
656 		ERROR_LOG_REPORT(SCERTC, "sceRtcGetWin32FileTime(%08x, %08x): invalid address", datePtr, win32TimePtr);
657 		return -1;
658 	}
659 
660 	DEBUG_LOG(SCERTC, "sceRtcGetWin32FileTime(%08x, %08x)", datePtr, win32TimePtr);
661 	if (!Memory::IsValidAddress(win32TimePtr))
662 		return SCE_KERNEL_ERROR_INVALID_VALUE;
663 
664 	auto pspTime = PSPPointer<const ScePspDateTime>::Create(datePtr);
665 	u64 result = __RtcPspTimeToTicks(*pspTime);
666 
667 	if (!__RtcValidatePspTime(*pspTime) || result < rtcFiletimeOffset)
668 	{
669 		Memory::Write_U64(0, win32TimePtr);
670 		return SCE_KERNEL_ERROR_INVALID_VALUE;
671 	}
672 
673 	Memory::Write_U64((result - rtcFiletimeOffset) * 10, win32TimePtr);
674 	return 0;
675 }
676 
sceRtcCompareTick(u32 tick1Ptr,u32 tick2Ptr)677 static int sceRtcCompareTick(u32 tick1Ptr, u32 tick2Ptr)
678 {
679 	DEBUG_LOG(SCERTC, "sceRtcCompareTick(%d,%d)", tick1Ptr, tick2Ptr);
680 	if (Memory::IsValidAddress(tick1Ptr) && Memory::IsValidAddress(tick2Ptr))
681 	{
682 		u64 tick1 = Memory::Read_U64(tick1Ptr);
683 		u64 tick2 = Memory::Read_U64(tick2Ptr);
684 		if (tick1 > tick2)
685 			return 1;
686 		if (tick1 < tick2)
687 			return -1;
688 	}
689 	return 0;
690 }
691 
sceRtcTickAddTicks(u32 destTickPtr,u32 srcTickPtr,u64 numTicks)692 static int sceRtcTickAddTicks(u32 destTickPtr, u32 srcTickPtr, u64 numTicks)
693 {
694 	if (Memory::IsValidAddress(destTickPtr) && Memory::IsValidAddress(srcTickPtr))
695 	{
696 		u64 srcTick = Memory::Read_U64(srcTickPtr);
697 
698 		srcTick += numTicks;
699 		Memory::Write_U64(srcTick, destTickPtr);
700 	}
701 
702 	DEBUG_LOG(SCERTC, "sceRtcTickAddTicks(%x,%x,%llu)", destTickPtr, srcTickPtr, numTicks);
703 	return 0;
704 }
705 
sceRtcTickAddMicroseconds(u32 destTickPtr,u32 srcTickPtr,u64 numMS)706 static int sceRtcTickAddMicroseconds(u32 destTickPtr,u32 srcTickPtr, u64 numMS)
707 {
708 	if (Memory::IsValidAddress(destTickPtr) && Memory::IsValidAddress(srcTickPtr))
709 	{
710 		s64 srcTick = (s64)Memory::Read_U64(srcTickPtr);
711 
712 		srcTick += numMS;
713 		Memory::Write_U64(srcTick, destTickPtr);
714 	}
715 
716 	DEBUG_LOG(SCERTC, "sceRtcTickAddMicroseconds(%x,%x,%llu)", destTickPtr, srcTickPtr, numMS);
717 	return 0;
718 }
719 
sceRtcTickAddSeconds(u32 destTickPtr,u32 srcTickPtr,u64 numSecs)720 static int sceRtcTickAddSeconds(u32 destTickPtr, u32 srcTickPtr, u64 numSecs)
721 {
722 	if (Memory::IsValidAddress(destTickPtr) && Memory::IsValidAddress(srcTickPtr))
723 	{
724 		s64 srcTick = (s64)Memory::Read_U64(srcTickPtr);
725 
726 		srcTick += numSecs * 1000000UL;
727 		Memory::Write_U64(srcTick, destTickPtr);
728 	}
729 	DEBUG_LOG(SCERTC, "sceRtcTickAddSeconds(%x,%x,%llu)", destTickPtr, srcTickPtr, numSecs);
730 	return 0;
731 }
732 
sceRtcTickAddMinutes(u32 destTickPtr,u32 srcTickPtr,u64 numMins)733 static int sceRtcTickAddMinutes(u32 destTickPtr, u32 srcTickPtr, u64 numMins)
734 {
735 	if (Memory::IsValidAddress(destTickPtr) && Memory::IsValidAddress(srcTickPtr))
736 	{
737 		s64 srcTick = (s64)Memory::Read_U64(srcTickPtr);
738 
739 		srcTick += numMins*60000000UL;
740 		Memory::Write_U64(srcTick, destTickPtr);
741 	}
742 	DEBUG_LOG(SCERTC, "sceRtcTickAddMinutes(%x,%x,%llu)", destTickPtr, srcTickPtr, numMins);
743 	return 0;
744 }
745 
sceRtcTickAddHours(u32 destTickPtr,u32 srcTickPtr,int numHours)746 static int sceRtcTickAddHours(u32 destTickPtr, u32 srcTickPtr, int numHours)
747 {
748 	if (Memory::IsValidAddress(destTickPtr) && Memory::IsValidAddress(srcTickPtr))
749 	{
750 		s64 srcTick = (s64)Memory::Read_U64(srcTickPtr);
751 		srcTick += numHours * 3600ULL * 1000000ULL;
752 		Memory::Write_U64(srcTick, destTickPtr);
753 	}
754 	DEBUG_LOG(SCERTC, "sceRtcTickAddMinutes(%d,%d,%d)", destTickPtr, srcTickPtr, numHours);
755 	return 0;
756 }
757 
sceRtcTickAddDays(u32 destTickPtr,u32 srcTickPtr,int numDays)758 static int sceRtcTickAddDays(u32 destTickPtr, u32 srcTickPtr, int numDays)
759 {
760 	if (Memory::IsValidAddress(destTickPtr) && Memory::IsValidAddress(srcTickPtr))
761 	{
762 		s64 srcTick = (s64)Memory::Read_U64(srcTickPtr);
763 
764 		srcTick += numDays * 86400ULL * 1000000ULL;
765 		Memory::Write_U64(srcTick, destTickPtr);
766 	}
767 	DEBUG_LOG(SCERTC, "sceRtcTickAddDays(%d,%d,%d)", destTickPtr, srcTickPtr, numDays);
768 	return 0;
769 }
770 
sceRtcTickAddWeeks(u32 destTickPtr,u32 srcTickPtr,int numWeeks)771 static int sceRtcTickAddWeeks(u32 destTickPtr, u32 srcTickPtr, int numWeeks)
772 {
773 	if (Memory::IsValidAddress(destTickPtr) && Memory::IsValidAddress(srcTickPtr))
774 	{
775 		s64 srcTick = (s64)Memory::Read_U64(srcTickPtr);
776 
777 		srcTick += numWeeks * 7ULL * 86400ULL * 1000000ULL;
778 		Memory::Write_U64(srcTick, destTickPtr);
779 	}
780 	DEBUG_LOG(SCERTC, "sceRtcTickAddWeeks(%d,%d,%d)", destTickPtr, srcTickPtr, numWeeks);
781 	return 0;
782 }
783 
sceRtcTickAddMonths(u32 destTickPtr,u32 srcTickPtr,int numMonths)784 static int sceRtcTickAddMonths(u32 destTickPtr, u32 srcTickPtr, int numMonths)
785 {
786 	if (!Memory::IsValidAddress(destTickPtr) || !Memory::IsValidAddress(srcTickPtr))
787 	{
788 		WARN_LOG(SCERTC, "sceRtcTickAddMonths(%08x, %08x, %d): invalid address", destTickPtr, srcTickPtr, numMonths);
789 		return -1;
790 	}
791 
792 	u64 srcTick = Memory::Read_U64(srcTickPtr);
793 
794 	ScePspDateTime pt;
795 	memset(&pt, 0, sizeof(pt));
796 
797 	__RtcTicksToPspTime(pt,srcTick);
798 	pt.year += numMonths / 12;
799 	pt.month += numMonths % 12;
800 
801 	if (pt.month < 1)
802 	{
803 		pt.month += 12;
804 		pt.year--;
805 	}
806 	if (pt.month > 12)
807 	{
808 		pt.month -= 12;
809 		pt.year++;
810 	}
811 
812 	if (__RtcValidatePspTime(pt))
813 	{
814 		// Did we land on a year that isn't a leap year?
815 		if (pt.month == 2 && pt.day == 29 && !__RtcIsLeapYear((s16)pt.year))
816 			pt.day = 28;
817 		Memory::Write_U64(__RtcPspTimeToTicks(pt), destTickPtr);
818 	}
819 
820 	DEBUG_LOG(SCERTC, "sceRtcTickAddMonths(%08x, %08x = %lld, %d)", destTickPtr, srcTickPtr, srcTick, numMonths);
821 	return 0;
822 }
823 
sceRtcTickAddYears(u32 destTickPtr,u32 srcTickPtr,int numYears)824 static int sceRtcTickAddYears(u32 destTickPtr, u32 srcTickPtr, int numYears)
825 {
826 	if (!Memory::IsValidAddress(destTickPtr) || !Memory::IsValidAddress(srcTickPtr))
827 	{
828 		WARN_LOG(SCERTC, "sceRtcTickAddYears(%08x, %08x, %d): invalid address", destTickPtr, srcTickPtr, numYears);
829 		return -1;
830 	}
831 
832 	u64 srcTick = Memory::Read_U64(srcTickPtr);
833 
834 	ScePspDateTime pt;
835 	memset(&pt, 0, sizeof(pt));
836 
837 	__RtcTicksToPspTime(pt, srcTick);
838 	pt.year += numYears;
839 
840 	if (__RtcValidatePspTime(pt))
841 	{
842 		// Did we land on a year that isn't a leap year?
843 		if (pt.month == 2 && pt.day == 29 && !__RtcIsLeapYear((s16)pt.year))
844 			pt.day = 28;
845 		Memory::Write_U64(__RtcPspTimeToTicks(pt), destTickPtr);
846 	}
847 
848 	DEBUG_LOG(SCERTC, "sceRtcTickAddYears(%08x, %08x = %lld, %d)", destTickPtr, srcTickPtr, srcTick, numYears);
849 	return 0;
850 }
851 
sceRtcParseDateTime(u32 destTickPtr,u32 dateStringPtr)852 static int sceRtcParseDateTime(u32 destTickPtr, u32 dateStringPtr)
853 {
854 	ERROR_LOG_REPORT(SCERTC, "UNIMPL sceRtcParseDateTime(%d,%d)", destTickPtr, dateStringPtr);
855 	return 0;
856 }
857 
sceRtcGetLastAdjustedTime(u32 tickPtr)858 static int sceRtcGetLastAdjustedTime(u32 tickPtr)
859 {
860 	if (Memory::IsValidAddress(tickPtr))
861 		Memory::Write_U64(rtcLastAdjustedTicks, tickPtr);
862 	DEBUG_LOG(SCERTC, "sceRtcGetLastAdjustedTime(%d)", tickPtr);
863 	return 0;
864 }
865 
sceRtcGetLastReincarnatedTime(u32 tickPtr)866 static int sceRtcGetLastReincarnatedTime(u32 tickPtr)
867 {
868 	if (Memory::IsValidAddress(tickPtr))
869 		Memory::Write_U64(rtcLastReincarnatedTicks, tickPtr);
870 	DEBUG_LOG(SCERTC, "sceRtcGetLastReincarnatedTime(%d)", tickPtr);
871 	return 0;
872 }
873 
874 //Returns 0 on success, according to Project Diva 2nd jpcsptrace log
sceRtcSetAlarmTick(u32 unknown1,u32 unknown2)875 static int sceRtcSetAlarmTick(u32 unknown1, u32 unknown2)
876 {
877 	ERROR_LOG_REPORT(SCERTC, "UNIMPL sceRtcSetAlarmTick(%x, %x)", unknown1, unknown2);
878 	return 0;
879 }
880 
__RtcFormatRFC2822(u32 outPtr,u32 srcTickPtr,int tz)881 static int __RtcFormatRFC2822(u32 outPtr, u32 srcTickPtr, int tz)
882 {
883 	u64 srcTick = Memory::Read_U64(srcTickPtr);
884 
885 	ScePspDateTime pt;
886 	memset(&pt, 0, sizeof(pt));
887 
888 	__RtcTicksToPspTime(pt, srcTick);
889 
890 	tm local;
891 	__RtcPspTimeToTm(local, pt);
892 	while (local.tm_year < 70)
893 		local.tm_year += 400;
894 	while (local.tm_year >= 470)
895 		local.tm_year -= 400;
896 	local.tm_min += tz;
897 	rtc_timegm(&local);
898 
899 	char *out = (char *)Memory::GetPointer(outPtr);
900 	char *end = out + 32;
901 	out += strftime(out, end - out, "%a, %d %b ", &local);
902 	out += snprintf(out, end - out, "%04d", pt.year);
903 	out += strftime(out, end - out, " %H:%M:%S ", &local);
904 	if (tz < 0)
905 		out += snprintf(out, end - out, "-%02d%02d", -tz / 60, -tz % 60);
906 	else
907 		out += snprintf(out, end - out, "+%02d%02d", tz / 60, tz % 60);
908 
909 	return 0;
910 }
911 
__RtcFormatRFC3339(u32 outPtr,u32 srcTickPtr,int tz)912 static int __RtcFormatRFC3339(u32 outPtr, u32 srcTickPtr, int tz)
913 {
914 	u64 srcTick = Memory::Read_U64(srcTickPtr);
915 
916 	ScePspDateTime pt;
917 	memset(&pt, 0, sizeof(pt));
918 
919 	__RtcTicksToPspTime(pt, srcTick);
920 
921 	tm local;
922 	__RtcPspTimeToTm(local, pt);
923 	while (local.tm_year < 70)
924 		local.tm_year += 400;
925 	while (local.tm_year >= 470)
926 		local.tm_year -= 400;
927 	local.tm_min += tz;
928 	rtc_timegm(&local);
929 
930 	char *out = (char *)Memory::GetPointer(outPtr);
931 	char *end = out + 32;
932 	out += snprintf(out, end - out, "%04d", pt.year);
933 	out += strftime(out, end - out, "-%m-%dT%H:%M:%S.00", &local);
934 	if (tz == 0)
935 		out += snprintf(out, end - out, "Z");
936 	else if (tz < 0)
937 		out += snprintf(out, end - out, "-%02d:%02d", -tz / 60, -tz % 60);
938 	else
939 		out += snprintf(out, end - out, "+%02d:%02d", tz / 60, tz % 60);
940 
941 	return 0;
942 }
943 
sceRtcFormatRFC2822(u32 outPtr,u32 srcTickPtr,int tz)944 static int sceRtcFormatRFC2822(u32 outPtr, u32 srcTickPtr, int tz)
945 {
946 	if (!Memory::IsValidAddress(outPtr) || !Memory::IsValidAddress(srcTickPtr))
947 	{
948 		// TODO: Not well tested.
949 		ERROR_LOG(SCERTC, "sceRtcFormatRFC2822(%08x, %08x, %d): invalid address", outPtr, srcTickPtr, tz);
950 		return -1;
951 	}
952 
953 	DEBUG_LOG(SCERTC, "sceRtcFormatRFC2822(%08x, %08x, %d)", outPtr, srcTickPtr, tz);
954 	return __RtcFormatRFC2822(outPtr, srcTickPtr, tz);
955 }
956 
sceRtcFormatRFC2822LocalTime(u32 outPtr,u32 srcTickPtr)957 static int sceRtcFormatRFC2822LocalTime(u32 outPtr, u32 srcTickPtr)
958 {
959 	if (!Memory::IsValidAddress(outPtr) || !Memory::IsValidAddress(srcTickPtr))
960 	{
961 		// TODO: Not well tested.
962 		ERROR_LOG(SCERTC, "sceRtcFormatRFC2822LocalTime(%08x, %08x): invalid address", outPtr, srcTickPtr);
963 		return -1;
964 	}
965 
966 	int tz_seconds;
967 #ifdef _WIN32
968 		long timezone_val;
969 		_get_timezone(&timezone_val);
970 		tz_seconds = -timezone_val;
971 #elif !defined(_AIX) && !defined(__sgi) && !defined(__hpux) && !defined(HAVE_LIBNX)
972 		time_t timezone = 0;
973 		tm *time = localtime(&timezone);
974 		tz_seconds = time->tm_gmtoff;
975 #endif
976 
977 	DEBUG_LOG(SCERTC, "sceRtcFormatRFC2822LocalTime(%08x, %08x)", outPtr, srcTickPtr);
978 	return __RtcFormatRFC2822(outPtr, srcTickPtr, tz_seconds / 60);
979 }
980 
sceRtcFormatRFC3339(u32 outPtr,u32 srcTickPtr,int tz)981 static int sceRtcFormatRFC3339(u32 outPtr, u32 srcTickPtr, int tz)
982 {
983 	if (!Memory::IsValidAddress(outPtr) || !Memory::IsValidAddress(srcTickPtr))
984 	{
985 		// TODO: Not well tested.
986 		ERROR_LOG(SCERTC, "sceRtcFormatRFC3339(%08x, %08x, %d): invalid address", outPtr, srcTickPtr, tz);
987 		return -1;
988 	}
989 
990 	DEBUG_LOG(SCERTC, "sceRtcFormatRFC3339(%08x, %08x, %d)", outPtr, srcTickPtr, tz);
991 	return __RtcFormatRFC3339(outPtr, srcTickPtr, tz);
992 }
993 
sceRtcFormatRFC3339LocalTime(u32 outPtr,u32 srcTickPtr)994 static int sceRtcFormatRFC3339LocalTime(u32 outPtr, u32 srcTickPtr)
995 {
996 	if (!Memory::IsValidAddress(outPtr) || !Memory::IsValidAddress(srcTickPtr))
997 	{
998 		// TODO: Not well tested.
999 		ERROR_LOG(SCERTC, "sceRtcFormatRFC3339LocalTime(%08x, %08x): invalid address", outPtr, srcTickPtr);
1000 		return -1;
1001 	}
1002 
1003 	int tz_seconds;
1004 #ifdef _WIN32
1005 		long timezone_val;
1006 		_get_timezone(&timezone_val);
1007 		tz_seconds = -timezone_val;
1008 #elif !defined(_AIX) && !defined(__sgi) && !defined(__hpux) && !defined(HAVE_LIBNX)
1009 		time_t timezone = 0;
1010 		tm *time = localtime(&timezone);
1011 		tz_seconds = time->tm_gmtoff;
1012 #endif
1013 
1014 	DEBUG_LOG(SCERTC, "sceRtcFormatRFC3339LocalTime(%08x, %08x)", outPtr, srcTickPtr);
1015 	return __RtcFormatRFC3339(outPtr, srcTickPtr, tz_seconds / 60);
1016 }
1017 
1018 const HLEFunction sceRtc[] =
1019 {
1020 	{0XC41C2853, &WrapU_V<sceRtcGetTickResolution>,        "sceRtcGetTickResolution",        'x', ""   },
1021 	{0X3F7AD767, &WrapU_U<sceRtcGetCurrentTick>,           "sceRtcGetCurrentTick",           'x', "x"  },
1022 	{0X011F03C1, &WrapU64_V<sceRtcGetAccumulativeTime>,    "sceRtcGetAccumulativeTime",      'X', ""   },
1023 	{0X029CA3B3, &WrapU64_V<sceRtcGetAccumulativeTime>,    "sceRtcGetAccumlativeTime",       'X', ""   },
1024 	{0X4CFA57B0, &WrapU_UI<sceRtcGetCurrentClock>,         "sceRtcGetCurrentClock",          'i', "xi" },
1025 	{0XE7C27D1B, &WrapU_U<sceRtcGetCurrentClockLocalTime>, "sceRtcGetCurrentClockLocalTime", 'i', "x"  },
1026 	{0X34885E0D, &WrapI_UU<sceRtcConvertUtcToLocalTime>,   "sceRtcConvertUtcToLocalTime",    'i', "xx" },
1027 	{0X779242A2, &WrapI_UU<sceRtcConvertLocalTimeToUTC>,   "sceRtcConvertLocalTimeToUTC",    'i', "xx" },
1028 	{0X42307A17, &WrapU_U<sceRtcIsLeapYear>,               "sceRtcIsLeapYear",               'x', "x"  },
1029 	{0X05EF322C, &WrapU_UU<sceRtcGetDaysInMonth>,          "sceRtcGetDaysInMonth",           'x', "xx" },
1030 	{0X57726BC1, &WrapU_UUU<sceRtcGetDayOfWeek>,           "sceRtcGetDayOfWeek",             'x', "xxx"},
1031 	{0X4B1B5E82, &WrapI_U<sceRtcCheckValid>,               "sceRtcCheckValid",               'i', "x"  },
1032 	{0X3A807CC8, &WrapI_UU<sceRtcSetTime_t>,               "sceRtcSetTime_t",                'i', "xx" },
1033 	{0X27C4594C, &WrapI_UU<sceRtcGetTime_t>,               "sceRtcGetTime_t",                'i', "xp" },
1034 	{0XF006F264, &WrapI_UU<sceRtcSetDosTime>,              "sceRtcSetDosTime",               'i', "xx" },
1035 	{0X36075567, &WrapI_UU<sceRtcGetDosTime>,              "sceRtcGetDosTime",               'i', "xp" },
1036 	{0X7ACE4C04, &WrapI_UU64<sceRtcSetWin32FileTime>,      "sceRtcSetWin32FileTime",         'i', "xX" },
1037 	{0XCF561893, &WrapI_UU<sceRtcGetWin32FileTime>,        "sceRtcGetWin32FileTime",         'i', "xx" },
1038 	{0X7ED29E40, &WrapU_UU<sceRtcSetTick>,                 "sceRtcSetTick",                  'x', "xP" },
1039 	{0X6FF40ACC, &WrapU_UU<sceRtcGetTick>,                 "sceRtcGetTick",                  'i', "xP" },
1040 	{0X9ED0AE87, &WrapI_UU<sceRtcCompareTick>,             "sceRtcCompareTick",              'i', "xx" },
1041 	{0X44F45E05, &WrapI_UUU64<sceRtcTickAddTicks>,         "sceRtcTickAddTicks",             'i', "xxX"},
1042 	{0X26D25A5D, &WrapI_UUU64<sceRtcTickAddMicroseconds>,  "sceRtcTickAddMicroseconds",      'i', "xxX"},
1043 	{0XF2A4AFE5, &WrapI_UUU64<sceRtcTickAddSeconds>,       "sceRtcTickAddSeconds",           'i', "xxX"},
1044 	{0XE6605BCA, &WrapI_UUU64<sceRtcTickAddMinutes>,       "sceRtcTickAddMinutes",           'i', "xxX"},
1045 	{0X26D7A24A, &WrapI_UUI<sceRtcTickAddHours>,           "sceRtcTickAddHours",             'i', "xxi"},
1046 	{0XE51B4B7A, &WrapI_UUI<sceRtcTickAddDays>,            "sceRtcTickAddDays",              'i', "xxi"},
1047 	{0XCF3A2CA8, &WrapI_UUI<sceRtcTickAddWeeks>,           "sceRtcTickAddWeeks",             'i', "xxi"},
1048 	{0XDBF74F1B, &WrapI_UUI<sceRtcTickAddMonths>,          "sceRtcTickAddMonths",            'i', "xxi"},
1049 	{0X42842C77, &WrapI_UUI<sceRtcTickAddYears>,           "sceRtcTickAddYears",             'i', "xxi"},
1050 	{0XC663B3B9, &WrapI_UUI<sceRtcFormatRFC2822>,          "sceRtcFormatRFC2822",            'i', "xxi"},
1051 	{0X7DE6711B, &WrapI_UU<sceRtcFormatRFC2822LocalTime>,  "sceRtcFormatRFC2822LocalTime",   'i', "xx" },
1052 	{0X0498FB3C, &WrapI_UUI<sceRtcFormatRFC3339>,          "sceRtcFormatRFC3339",            'i', "xxi"},
1053 	{0X27F98543, &WrapI_UU<sceRtcFormatRFC3339LocalTime>,  "sceRtcFormatRFC3339LocalTime",   'i', "xx" },
1054 	{0XDFBC5F16, &WrapI_UU<sceRtcParseDateTime>,           "sceRtcParseDateTime",            'i', "xx" },
1055 	{0X28E1E988, nullptr,                                  "sceRtcParseRFC3339",             '?', ""   },
1056 	{0XE1C93E47, &WrapI_UU<sceRtcGetTime64_t>,             "sceRtcGetTime64_t",              'i', "xP" },
1057 	{0X1909C99B, &WrapI_UU64<sceRtcSetTime64_t>,           "sceRtcSetTime64_t",              'i', "xX" },
1058 	{0X62685E98, &WrapI_U<sceRtcGetLastAdjustedTime>,      "sceRtcGetLastAdjustedTime",      'i', "x"  },
1059 	{0X203CEB0D, &WrapI_U<sceRtcGetLastReincarnatedTime>,  "sceRtcGetLastReincarnatedTime",  'i', "x"  },
1060 	{0X7D1FBED3, &WrapI_UU<sceRtcSetAlarmTick>,            "sceRtcSetAlarmTick",             'i', "xx" },
1061 	{0XF5FCC995, nullptr,                                  "sceRtcGetCurrentNetworkTick",    '?', ""   },
1062 	{0X81FCDA34, nullptr,                                  "sceRtcIsAlarmed",                '?', ""   },
1063 	{0XFB3B18CD, nullptr,                                  "sceRtcRegisterCallback",         '?', ""   },
1064 	{0X6A676D2D, nullptr,                                  "sceRtcUnregisterCallback",       '?', ""   },
1065 	{0XC2DDBEB5, nullptr,                                  "sceRtcGetAlarmTick",             '?', ""   },
1066 };
1067 
Register_sceRtc()1068 void Register_sceRtc()
1069 {
1070 	RegisterModule("sceRtc", ARRAY_SIZE(sceRtc), sceRtc);
1071 }
1072