From 9c83a3820889e256b2b41023c493cf2173d1e7d1 Mon Sep 17 00:00:00 2001 From: Tau Date: Mon, 12 Nov 2018 15:29:46 -0500 Subject: [PATCH] util/clock.c: Apply clock skew SEGA games are unplayable between 03:00 and 07:00 local time due to a network maintenance period that is hard-coded into the games themselves (no, guest play is not available). Deal with this by slowing down time to avoid 02:00 (the time when a warning appears and new credits eventually start being rejected), then jump forward to sync up with real time when 07:00 hits. --- chunihook/dllmain.c | 3 +- minihook/dllmain.c | 2 + util/clock.c | 186 +++++++++++++++++++++++++++++++++++++++++--- util/clock.h | 3 +- 4 files changed, 180 insertions(+), 14 deletions(-) diff --git a/chunihook/dllmain.c b/chunihook/dllmain.c index f43c428..ad38482 100644 --- a/chunihook/dllmain.c +++ b/chunihook/dllmain.c @@ -43,7 +43,8 @@ static DWORD CALLBACK chuni_pre_startup(void) /* Hook Win32 APIs */ - clock_hook_init(); + clock_set_hook_init(); + clock_skew_hook_init(); gfx_hook_init(); serial_hook_init(); diff --git a/minihook/dllmain.c b/minihook/dllmain.c index 7865bfc..054d004 100644 --- a/minihook/dllmain.c +++ b/minihook/dllmain.c @@ -5,6 +5,7 @@ #include "nu/ds.h" #include "nu/nusec.h" +#include "util/clock.h" #include "util/dprintf.h" static process_entry_t app_startup; @@ -15,6 +16,7 @@ static DWORD CALLBACK app_pre_startup(void) ds_hook_init(); nusec_hook_init(); + clock_skew_hook_init(); dprintf("--- End %s ---\n", __func__); diff --git a/util/clock.c b/util/clock.c index 60e623a..e22f14f 100644 --- a/util/clock.c +++ b/util/clock.c @@ -1,39 +1,201 @@ +#include + +#include + #include "hook/table.h" #include "util/dprintf.h" -static BOOL WINAPI my_SetSystemTime(void *whatever); -static BOOL WINAPI my_SetTimeZoneInformation(void *whatever); +static void WINAPI my_GetSystemTimeAsFileTime(FILETIME *out); +static BOOL WINAPI my_GetLocalTime(SYSTEMTIME *out); +static BOOL WINAPI my_GetSystemTime(SYSTEMTIME *out); +static DWORD WINAPI my_GetTimeZoneInformation(TIME_ZONE_INFORMATION *tzinfo); +static BOOL WINAPI my_SetSystemTime(SYSTEMTIME *in); +static BOOL WINAPI my_SetTimeZoneInformation(TIME_ZONE_INFORMATION *tzinfo); -static const struct hook_symbol clock_hook_syms[] = { +static BOOL (WINAPI * next_GetSystemTimeAsFileTime)(FILETIME *out); +static int64_t clock_current_day; + +static const struct hook_symbol clock_skew_hook_syms[] = { + /* Canonical time */ + + { + .name = "GetSystemTimeAsFileTime", + .patch = my_GetSystemTimeAsFileTime, + .link = (void **) &next_GetSystemTimeAsFileTime, + }, + + /* Derived time */ + + { + .name = "GetLocalTime", + .patch = my_GetLocalTime, + }, { + .name = "GetSystemTime", + .patch = my_GetSystemTime, + }, { + .name = "GetTimeZoneInformation", + .patch = my_GetTimeZoneInformation, + }, +}; + +static const struct hook_symbol clock_set_hook_syms[] = { { .name = "SetSystemTime", .patch = my_SetSystemTime, }, { .name = "SetTimeZoneInformation", .patch = my_SetTimeZoneInformation, - } + }, }; -void clock_hook_init(void) +/* FILETIME is expressed in 100ns i.e. 0.1us i.e. 10^-7 sec units. + No official name for these units is given so let's call them "jiffies". */ + +static const int64_t jiffies_per_sec = 10000000LL; +static const int64_t jiffies_per_hour = jiffies_per_sec * 3600LL; +static const int64_t jiffies_per_day = jiffies_per_hour * 24LL; + +static void WINAPI my_GetSystemTimeAsFileTime(FILETIME *out) { - hook_table_apply( - NULL, - "kernel32.dll", - clock_hook_syms, - _countof(clock_hook_syms)); + FILETIME in; + int64_t day; + int64_t real_jiffies; + int64_t real_jiffies_biased; + int64_t real_time; + int64_t fake_time; + int64_t fake_jiffies_biased; + int64_t fake_jiffies; + + if (out == NULL) { + SetLastError(ERROR_INVALID_PARAMETER); + + return; + } + + /* Get and convert real jiffies */ + + next_GetSystemTimeAsFileTime(&in); + real_jiffies = (((int64_t) in.dwHighDateTime) << 32) | in.dwLowDateTime; + + /* Apply bias: shift [02:00, 07:00) to [19:00, 24:00) */ + + real_jiffies_biased = real_jiffies + 17LL * jiffies_per_hour; + + /* Split date and time */ + + day = real_jiffies_biased / jiffies_per_day; + real_time = real_jiffies_biased % jiffies_per_day; + + /* Debug log */ + + if (clock_current_day != 0 && clock_current_day != day) { + dprintf("\n*** CLOCK JUMP! ***\n\n"); + } + + clock_current_day = day; + + /* We want to skip the final five hours, so scale time-of-day by 19/24. */ + + fake_time = (real_time * 19LL) / 24LL; + + /* Un-split date and time */ + + fake_jiffies_biased = day * jiffies_per_day + fake_time; + + /* Remove bias */ + + fake_jiffies = fake_jiffies_biased - 17LL * jiffies_per_hour; + + /* Return result */ + + out->dwLowDateTime = fake_jiffies; + out->dwHighDateTime = fake_jiffies >> 32; } -static BOOL WINAPI my_SetSystemTime(void *whatever) +static BOOL WINAPI my_GetLocalTime(SYSTEMTIME *out) +{ + /* Use UTC to simplify things */ + return my_GetSystemTime(out); +} + +static BOOL WINAPI my_GetSystemTime(SYSTEMTIME *out) +{ + FILETIME linear; + BOOL ok; + + my_GetSystemTimeAsFileTime(&linear); + ok = FileTimeToSystemTime(&linear, out); + + if (!ok) { + return ok; + } + +#if 0 + static int last_second; + + if (out->wSecond != last_second) { + dprintf("%04i/%02i/%02i %02i:%02i:%02i\n", + out->wYear, + out->wMonth, + out->wDay, + out->wHour, + out->wMinute, + out->wSecond); + } + + last_second = out->wSecond; +#endif + + return TRUE; +} + +static DWORD WINAPI my_GetTimeZoneInformation(TIME_ZONE_INFORMATION *tzinfo) +{ + /* Use UTC to simplify things */ + + dprintf("%s\n", __func__); + + if (tzinfo != NULL) { + memset(tzinfo, 0, sizeof(*tzinfo)); + SetLastError(ERROR_SUCCESS); + + return TIME_ZONE_ID_UNKNOWN; + } else { + SetLastError(ERROR_INVALID_PARAMETER); + + return TIME_ZONE_ID_INVALID; + } +} + +static BOOL WINAPI my_SetSystemTime(SYSTEMTIME *in) { dprintf("Prevented application from screwing with the system clock\n"); return TRUE; } -static BOOL WINAPI my_SetTimeZoneInformation(void *whatever) +static BOOL WINAPI my_SetTimeZoneInformation(TIME_ZONE_INFORMATION *in) { dprintf("Prevented application from screwing with the timezone\n"); return TRUE; } + +void clock_set_hook_init(void) +{ + hook_table_apply( + NULL, + "kernel32.dll", + clock_set_hook_syms, + _countof(clock_set_hook_syms)); +} + +void clock_skew_hook_init(void) +{ + hook_table_apply( + NULL, + "kernel32.dll", + clock_skew_hook_syms, + _countof(clock_skew_hook_syms)); +} diff --git a/util/clock.h b/util/clock.h index f3d242e..30e53e8 100644 --- a/util/clock.h +++ b/util/clock.h @@ -1,3 +1,4 @@ #pragma once -void clock_hook_init(void); +void clock_set_hook_init(void); +void clock_skew_hook_init(void);