--- /dev/null
+apocalypse
+------------
+
+This is a very shitty gettimeofday/clock_gettime based speedhack that works on *some* games.
+
+It's also very old and not maintained, but might serve as a good example of:
+
+ * How to hook functions via LD_PRELOAD
+
+ * How NOT to document code properly
--- /dev/null
+gcc -ldl -m64 -fPIC -shared `pkg-config --cflags --libs sdl2` -lGL -o apocalypse64.so gettime_speedhack.c info_window.c config.c
+gcc -ldl -m32 -fPIC -shared `pkg-config --cflags --libs sdl2` -lGL -o apocalypse32.so gettime_speedhack.c info_window.c config.c
+cp *.so ~/.preloads/
--- /dev/null
+#define DEFINED_ENABLES_HERE
+
+#include <dlfcn.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <unistd.h>
+#include <stdint.h>
+
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <time.h>
+
+#include "config.h"
+
+int ENABLE_TIME_HACKS = 0;
+int ENABLE_GETTIMEOFDAY_HOOK = 0;
+int ENABLE_CLOCK_GETTIME_HOOK = 0;
+
+// Is this Isaac: AB or RB?
+int IS_TBOI = 0;
+
+double SLOW_FACTOR = 1.0;
+
+int HAS_LOADED_CONFIG = 0;
+
+struct stat info;
+
+void read_cfg() {
+ FILE* f = fopen("apocalypse.conf", "r");
+ if (!f) {
+ // Doesn't exist, so write the example.
+ fprintf(stderr, "[HOOK] No config file found, writing one out to 'apocalypse.conf'\n");
+ f = fopen("apocalypse.conf", "w");
+ fprintf(f,
+ "#================================\n"
+ "# Apocalypse tool config file\n"
+ "#================================\n\n"
+ "# Enable time hacks. Required for slowdown\n"
+ "# and speedup, as well.\n"
+ "time_hacks=0\n"
+ "# Enable gettimeofday-based time hack.\n"
+ "gettimeofday=0\n"
+ "# Enable clock_gettime-based time hack.\n"
+ "clock_gettime=0\n"
+ "# Slow factor with time hack.\n"
+ "slow_factor=1.0\n\n"
+ "#================================\n"
+ "# Game-specific stuff\n"
+ "#================================\n\n"
+ "# Binding of isaac hooks\n"
+ "isaac=0\n"
+ );
+ fclose(f);
+ f = fopen("apocalypse.conf", "r");
+ }
+
+ stat("apocalypse.conf", &info);
+
+ char* line = NULL;
+ size_t len = 0;
+ int read = 0;
+
+ while ((read = getline(&line, &len, f)) != -1) {
+ // Line less than three characters? Next.
+ if (strlen(line) < 3)
+ continue;
+
+ // Line is \n? Next.
+ if (line[0] == '\n')
+ continue;
+
+ // First character is #? Next.
+ if (line[0] == '#')
+ continue;
+
+ char *prop, *value;
+
+ prop = line;
+
+ int i;
+ for(i=0; i < strlen(line); i++) {
+ if (line[i] == '=') {
+ line[i] = '\0';
+ value = &line[i+1];
+ }
+ }
+
+ if ( !strcmp(prop, "time_hacks") ) {
+ sscanf(value, "%d", & ENABLE_TIME_HACKS);
+ fprintf(stderr, "[HOOK] time_hacks: %d\n", ENABLE_CLOCK_GETTIME_HOOK);
+ }
+ else if ( !strcmp(prop, "gettimeofday") ) {
+ sscanf(value, "%d", & ENABLE_GETTIMEOFDAY_HOOK);
+ fprintf(stderr, "[HOOK] gettimeofday: %d\n", ENABLE_GETTIMEOFDAY_HOOK);
+ }
+ else if ( !strcmp(prop, "clock_gettime") ) {
+ sscanf(value, "%d", & ENABLE_CLOCK_GETTIME_HOOK);
+ fprintf(stderr, "[HOOK] clock_gettime: %d\n", ENABLE_CLOCK_GETTIME_HOOK);
+ }
+ else if ( !strcmp(prop, "slow_factor") ) {
+ sscanf(value, "%lf", & SLOW_FACTOR);
+ fprintf(stderr, "[HOOK] slow factor: %lf\n", SLOW_FACTOR);
+ }
+ }
+
+ fprintf(stderr, "[HOOK] Read 'apocalypse.conf'. Settings loaded.\n");
+
+ fclose(f);
+
+ HAS_LOADED_CONFIG = 1;
+}
+
+int check_cfg_mod() {
+ struct stat new;
+ stat("apocalypse.conf", &new);
+ if (new.st_mtim.tv_sec != info.st_mtim.tv_sec || new.st_mtim.tv_nsec != info.st_mtim.tv_nsec) {
+ // File was modified. Reload.
+ read_cfg();
+ memcpy(&info, &new, sizeof(struct stat));
+ return 1;
+ }
+ return 0;
+}
--- /dev/null
+#define _GNU_SOURCE
+#include <dlfcn.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <unistd.h>
+#include <stdint.h>
+
+#include <sys/time.h>
+#include <time.h>
+
+#include <GL/gl.h>
+#include <GL/glx.h>
+
+#ifndef DEFINED_ENABLES_HERE
+extern int ENABLE_TIME_HACKS;
+extern int ENABLE_GETTIMEOFDAY_HOOK;
+extern int ENABLE_CLOCK_GETTIME_HOOK;
+
+extern int HAS_LOADED_CONFIG;
+
+extern int IS_TBOI;
+
+extern double SLOW_FACTOR;
+
+#endif
+
+void read_cfg();
+int check_cfg_mod(); // Returns 1 if reloaded.
--- /dev/null
+#include "config.h"
+
+typedef int (*gettimeofday_t)(struct timeval *tv, struct timezone *tz);
+typedef int (*clock_gettime_t)(clockid_t clk_id, struct timespec *tp);
+
+gettimeofday_t gettimeofday_REAL = NULL;
+clock_gettime_t clock_gettime_REAL = NULL;
+
+uint64_t time_div = 1;
+uint64_t time_mul = 1;
+double time_speedhack = 1.0f;
+
+int initd_hack = 0;
+struct timeval gtod_time;
+struct timeval gtod_base;
+struct timespec clkgt_m_time;
+/* CLOCK_REALTIME
+ CLOCK_REALTIME_COARSE
+ CLOCK_MONOTONIC
+ CLOCK_MONOTONIC_COARSE
+ CLOCK_BOOTTIME
+ CLOCK_PROCESS_CPUTIME_ID
+ CLOCK_THREAD_CPUTIME_ID */
+struct timespec clkgt_m_base;
+
+int mode_to_int(int mode) {
+ switch(mode) {
+ case CLOCK_REALTIME:
+ return 0;
+ case CLOCK_REALTIME_COARSE:
+ return 1;
+ case CLOCK_MONOTONIC:
+ return 2;
+ case CLOCK_MONOTONIC_COARSE:
+ return 3;
+ case CLOCK_BOOTTIME:
+ return 4;
+ case CLOCK_PROCESS_CPUTIME_ID:
+ return 5;
+ case CLOCK_THREAD_CPUTIME_ID:
+ return 6;
+ default:
+ return -1;
+ }
+}
+
+int int_to_mode(int mode) {
+ switch(mode) {
+ case 0:
+ return CLOCK_REALTIME;
+ case 1:
+ return CLOCK_REALTIME_COARSE;
+ case 2:
+ return CLOCK_MONOTONIC;
+ case 3:
+ return CLOCK_MONOTONIC_COARSE;
+ case 4:
+ return CLOCK_BOOTTIME;
+ case 5:
+ return CLOCK_PROCESS_CPUTIME_ID;
+ case 6:
+ return CLOCK_THREAD_CPUTIME_ID;
+ }
+}
+
+// Init state.
+void clock_hack_init() {
+ check_cfg_mod();
+
+ gettimeofday_REAL = dlsym(RTLD_NEXT, "gettimeofday");
+ clock_gettime_REAL = dlsym(RTLD_NEXT, "clock_gettime");
+
+ if (gettimeofday_REAL(>od_time, NULL)) {
+ fprintf(stderr, "[HOOK] !!! SANITY ERROR !!! - real gettimeofday cannot be used, aborting\n");
+ exit(-1);
+ }
+
+ gtod_base.tv_sec = gtod_time.tv_sec;
+ gtod_base.tv_usec = gtod_time.tv_usec;
+
+ if (clock_gettime_REAL(CLOCK_MONOTONIC, &clkgt_m_time)) {
+ fprintf(stderr, "[HOOK] !!! SANITY ERROR !!! - real gettimeofday cannot be used, aborting\n");
+ exit(-1);
+ }
+
+ clkgt_m_base.tv_sec = clkgt_m_time.tv_sec;
+ clkgt_m_base.tv_nsec = clkgt_m_time.tv_nsec;
+
+ if (!ENABLE_TIME_HACKS)
+ fprintf(stderr, "[HOOK] Speedhack is disabled.\n", time_speedhack);
+ else
+ fprintf(stderr, "[HOOK] Speedhack is applied. Time factor is %lf.\n", time_speedhack);
+
+ initd_hack = 1;
+}
+
+// Transform time based on fraction.
+void xform_timeval(struct timeval *tv, struct timeval *orig) {
+ // What is this? Well, on init we save the initial point. From there,
+ // time moves at half-speed. So if I run a game at 2:00 PM, it's not back in 1990.
+
+ // PROBLEM - Time changes. We add a different value for the 'base'.
+ tv->tv_sec = gtod_base.tv_sec + ((tv->tv_sec - orig->tv_sec) * SLOW_FACTOR);
+ tv->tv_usec = gtod_base.tv_usec + ((tv->tv_usec - orig->tv_usec) * SLOW_FACTOR);
+
+}
+
+void xform_timespec(struct timespec *tv, struct timespec *orig) {
+ tv->tv_sec = clkgt_m_base.tv_sec + ((tv->tv_sec - orig->tv_sec) * SLOW_FACTOR);
+ tv->tv_nsec = clkgt_m_base.tv_nsec + ((tv->tv_nsec - orig->tv_nsec) * SLOW_FACTOR);
+}
+
+int done_gt_print = 0;
+
+// Fake gettimeofday.
+int gettimeofday(struct timeval *tv, struct timezone *tz) {
+ if (!initd_hack) clock_hack_init();
+
+ int r = gettimeofday_REAL(tv, NULL);
+ if (ENABLE_TIME_HACKS && ENABLE_GETTIMEOFDAY_HOOK)
+ xform_timeval(tv, >od_time);
+
+ if (check_cfg_mod()) {
+ gtod_base.tv_sec = tv->tv_sec;
+ gtod_base.tv_usec = tv->tv_usec;
+ }
+
+ if (tv != NULL && tv->tv_sec % 5 == 0) {
+ if (!done_gt_print) printf("[gettimeofday] %llu\t%llu\n", tv->tv_sec, tv->tv_usec);
+ done_gt_print = 1;
+ } else
+ done_gt_print = 0;
+
+ return r;
+}
+
+int done_clkgt_print = 0;
+
+// Fake clock_gettime.
+int clock_gettime(clockid_t clk_id, struct timespec *tp) {
+ if (!initd_hack) clock_hack_init();
+
+ int r = clock_gettime_REAL(clk_id, tp);
+ if (ENABLE_TIME_HACKS && ENABLE_CLOCK_GETTIME_HOOK)
+ xform_timespec(tp, &clkgt_m_time);
+
+ if (check_cfg_mod()) {
+ clkgt_m_base.tv_sec = tp->tv_sec;
+ clkgt_m_base.tv_nsec = tp->tv_nsec;
+ }
+
+ if (tp != NULL && tp->tv_sec % 5 == 0) {
+ mode_to_int(clk_id);
+ if (!done_clkgt_print) printf("[clock_gettime] %llu\t%llu\n", tp->tv_sec, tp->tv_nsec);
+ done_clkgt_print = 1;
+ } else
+ done_clkgt_print = 0;
+
+ return r;
+}
--- /dev/null
+#include "config.h"
+
+// Types.
+typedef __GLXextFuncPtr (*glXGetProcAddressARB_t) (const GLubyte*);
+typedef __GLXextFuncPtr (*glXSwapBuffers_t)(Display* dpy, GLXDrawable drawable);
+typedef GLXContext (*glXCreateContext_t)(Display *dpy, XVisualInfo *vis, GLXContext shareList, Bool direct);
+
+// Function hooks.
+static glXSwapBuffers_t glXSwapBuffers_REAL = NULL;
+static glXGetProcAddressARB_t glXGetProcAddressARB_REAL = NULL;
+static glXCreateContext_t glXCreateContext_REAL = NULL;
+
+void pre_hook() {
+/* char speed[64];
+ speed[0] = '\0';
+ if (ENABLE_TIME_HACKS)
+ snprintf(speed, 64, "speedhack: %02lfx", SLOW_FACTOR);
+ uint8_t* buf = string_to_buf(speed);
+ glDrawPixels(8*strlen(speed), 8, GL_RGBA, GL_UNSIGNED_BYTE, buf);
+ free(buf); */
+}
+
+void post_hook() {}
+
+GLXContext hook_context;
+Display* hook_display;
+XVisualInfo* hook_visinfo;
+
+// glXSwapBuffers hook.
+void glXSwapBuffers_fake(Display* dpy, GLXDrawable drawable) {
+ pre_hook();
+ glXSwapBuffers_REAL(dpy, drawable);
+ post_hook();
+}
+
+// glXCreateContext hook.
+GLXContext glXCreateContext_fake( Display *dpy, XVisualInfo *vis, GLXContext shareList, Bool direct ) {
+ hook_context = glXCreateContext_REAL(dpy, vis, shareList, direct);
+ hook_display = dpy;
+ hook_visinfo = vis;
+
+ fprintf(stderr, "[HOOK] Obtained GL context pointers successfully.\n");
+
+ return hook_context;
+}
+
+// Same-ish thing, thin shim.
+__GLXextFuncPtr glXGetProcAddress (const GLubyte* procName) {
+ return glXGetProcAddressARB(procName);
+}
+
+// glXGetProcAddressARB
+__GLXextFuncPtr glXGetProcAddressARB (const GLubyte* procName)
+{
+ __GLXextFuncPtr result;
+
+ // Fetch pointer of actual glXGetProcAddressARB() function
+ if(!glXGetProcAddressARB_REAL) {
+ char* errorstr;
+ fprintf(stderr, "[HOOK] Overrided glXGetProcAddress.\n");
+ glXGetProcAddressARB_REAL = (glXGetProcAddressARB_t) dlsym(RTLD_NEXT, "glXGetProcAddressARB");
+ if((errorstr = dlerror())) {
+ fprintf(stderr, "dlsym fail: %s\n", errorstr);
+ exit(1);
+ }
+ }
+
+ // Return our own function pointers for things.
+ if (!strcmp( (const char*) procName, "glXSwapBuffers" )) {
+ fprintf(stderr, "[HOOK] Hooked glXSwapBuffers pointer.\n");
+
+ glXSwapBuffers_REAL = (glXSwapBuffers_t) glXGetProcAddressARB_REAL(procName);
+ return (__GLXextFuncPtr) glXSwapBuffers_fake;
+ } else if (!strcmp( (const char*) procName, "glXCreateContext" )) {
+ fprintf(stderr, "[HOOK] Hooked glXCreateContext pointer.\n");
+
+ glXCreateContext_REAL = (glXCreateContext_t) glXGetProcAddressARB_REAL(procName);
+ return (__GLXextFuncPtr) glXCreateContext_fake;
+ }
+
+ // Return default function pointer
+ return glXGetProcAddressARB_REAL(procName);
+}