This repository has been archived on 2024-06-25. You can view files and clone it, but cannot push or open issues or pull requests.

247 lines
7.7 KiB
C

#include <stdio.h>
#include <taihen.h>
#include <psp2/io/fcntl.h>
#include <psp2kern/kernel/sysmem.h>
#include <string.h>
#include <stdarg.h>
#include <dolcesdkkern.h>
#include <stdlib.h>
#include <psp2/appmgr.h>
#define printf ksceDebugPrintf
#define SECOND 1000000
static tai_hook_ref_t event_handler_ref;
static SceUID hooks[1];
//I don't know if its related but without this 2 line, adrenaline won't launch, as far as I'm aware.
//Taken from Electry's PSVShell code.
SceUID (*_ksceKernelGetProcessMainModule)(SceUID pid);
int (*_ksceKernelGetModuleInfo)(SceUID pid, SceUID modid, SceKernelModuleInfo *info);
// Boolean values
static uint8_t playtime_loaded = 0;
static uint8_t playtime_written = 0;
static uint8_t pspemu = 0;
static uint8_t isxmb = 0;
// String values
static char adrenaline_bin_path[128];
static char playtime_bin_path[128];
static char id[32];
static char adrenaline_id[12] = "pleasePLEASE";
// Timer values
static uint64_t adrenaline_tick_start = 0;
static uint64_t adrenaline_playtime_start = 0;
static uint64_t playtime_start = 0;
static uint64_t tick_start = 0;
// PID for adrenaline
static SceUID pid_of_adrenaline;
// Taken from Adrenaline
typedef struct {
int savestate_mode;
int num;
unsigned int sp;
unsigned int ra;
int pops_mode;
int draw_psp_screen_in_pops;
char title[128];
char titleid[12];
char filename[256];
int psp_cmd;
int vita_cmd;
int psp_response;
int vita_response;
} SceAdrenaline;
static void load_playtime(char *local_titleid) {
// To prevent loading twice.
if (playtime_loaded == 1) return;
playtime_loaded = 1;
playtime_written = 0;
snprintf(playtime_bin_path, 128, "ux0:/data/TrackPlug/Records/%s.bin", local_titleid);
tick_start = ksceKernelGetSystemTimeWide();
SceUID fd = ksceIoOpen(playtime_bin_path, SCE_O_RDONLY, 0777);
if (fd < 0) {
playtime_start = 0;
return;
}
ksceIoRead(fd, &playtime_start, sizeof(uint64_t));
ksceIoClose(fd);
}
static void write_playtime(char *local_titleid) {
// To prevent writing twice.
if (playtime_written == 1) return;
playtime_loaded = 0;
playtime_written = 1;
uint32_t playtime = playtime_start +
(ksceKernelGetSystemTimeWide() - tick_start) / SECOND;
SceUID fd = ksceIoOpen(playtime_bin_path,
SCE_O_WRONLY | SCE_O_CREAT | SCE_O_TRUNC, 0777);
if (fd < 0) return;
ksceIoWrite(fd, &playtime, sizeof(uint64_t));
ksceIoClose(fd);
}
static void adrenaline_titlewriter(char *dummy_id,char *dummy_title){
//We don't want XMB's title to be written.
if (dummy_title[0] == 0 || !strncmp(dummy_title, "XMB", 3)) return;
char path[128];
char pathfolder[128];
snprintf(path, 128, "ux0:/data/TrackPlug/Assets/%s/title.txt", dummy_id);
snprintf(pathfolder, 128, "ux0:/data/TrackPlug/Assets/%s/", dummy_id);
int dfd = ksceIoMkdir(pathfolder,6);
SceUID fd = ksceIoOpen(path,
SCE_O_WRONLY | SCE_O_CREAT | SCE_O_TRUNC, 0777);
if (fd < 0) return;
ksceIoWrite(fd, dummy_title, strlen(dummy_title));
ksceIoClose(fd);
}
static void adrenaline_loader(char *dummy_id) {
snprintf(adrenaline_bin_path, 128, "ux0:/data/TrackPlug/Records/%s.bin", dummy_id);
adrenaline_tick_start = ksceKernelGetSystemTimeWide();
SceUID fd = ksceIoOpen(adrenaline_bin_path, SCE_O_RDONLY, 0777);
if (fd < 0) {
adrenaline_playtime_start = 0;
return;
}
ksceIoRead(fd, &adrenaline_playtime_start, sizeof(uint64_t));
ksceIoClose(fd);
}
static void adrenaline_writer(char *dummy_id) {
if (!strncmp(dummy_id,"pleasePLEASE",12)) return;
uint32_t playtime = adrenaline_playtime_start +
(ksceKernelGetSystemTimeWide() - adrenaline_tick_start) / SECOND;
SceUID fd = ksceIoOpen(adrenaline_bin_path,
SCE_O_WRONLY | SCE_O_CREAT | SCE_O_TRUNC, 0777);
if (fd < 0) return;
ksceIoWrite(fd, &playtime, sizeof(uint64_t));
ksceIoClose(fd);
}
// Load-Write handler for Adrenaline.
static void check_adrenaline() {
// Memleketime can kurban.
SceAdrenaline Bartin;
// This memory location was taken from Electry's TrackPlugX code.
ksceKernelMemcpyUserToKernelForPid(pid_of_adrenaline, &Bartin, (void *)0x73CDE000, sizeof(Bartin));
char *local_title = (void*)&Bartin + 24;
char *local_id = (void*)&Bartin + 152;
//If in the XMB, say its in the xmb and return.
if (local_title[0] == 0 || !strncmp(local_title, "XMB", 3)){
isxmb=1;
}
// If the game is changed, create its titlename, load the games time data and change the global variable to current game.
else if (strncmp(adrenaline_id, local_id, 9)){
isxmb=0;
adrenaline_titlewriter(local_id,local_title);
adrenaline_writer(adrenaline_id);
adrenaline_loader(local_id);
snprintf(adrenaline_id,strlen(local_id),local_id);
}
return;
}
// This thread is to detect if adrenaline is running once in every second.
static int adrenaline_checker(SceSize args, void *argp) {
while (1) {
if(pspemu){
check_adrenaline();
ksceKernelDelayThread(SECOND);
} else ksceKernelDelayThread(SECOND);
}
return ksceKernelExitDeleteThread(0);
}
// This handles the events of every app.
int event_handler(int pid, int ev, int a3, int a4, int *a5, int a6) {
ksceKernelGetProcessTitleId(pid, id, sizeof(id));
//If Adrenaline is launched, start the adrenaline checker.
if (!strncmp(id, "PSPEMUCFW", 9)){
pid_of_adrenaline = pid;
pspemu = 1;
}
// If its a system app, ignore and go on your own way.
else if (!strncmp(id, "main", 4)||!strncmp(id, "NPXS", 4)) {
//pspemu = 0;
return TAI_CONTINUE(int, event_handler_ref, pid, ev, a3, a4, a5, a6);
}
switch(ev){
case 1: // Startup
//We dont need to load game time at adrenaline launch.
if (!pspemu)
load_playtime(id);
break;
case 5: // Resume
if (pspemu)
adrenaline_loader(adrenaline_id);
else
load_playtime(id);
break;
case 3: // Exit
if (pspemu){
//Write games playtime and reset the variable. If Adrenaline was in XMB, no need
//to write it again as it will be handled by the thread.
if (!isxmb) write_playtime(adrenaline_id);
snprintf(adrenaline_id,12,"pleasePLEASE");
pspemu = 0;
} else
write_playtime(id);
break;
case 4: // Suspend
if (pspemu){
// Same story with the Exit.
if (!isxmb) write_playtime(adrenaline_id);
pspemu = 0;
} else
write_playtime(id);
break;
}
return TAI_CONTINUE(int, event_handler_ref, pid, ev, a3, a4, a5, a6);
}
void _start() __attribute__((weak, alias("module_start")));
int module_start(SceSize args, void *argp) {
ksceIoMkdir("ux0:/data/TrackPlug",6);
ksceIoMkdir("ux0:/data/TrackPlug/Assets",6);
ksceIoMkdir("ux0:/data/TrackPlug/Records",6);
hooks[0] = taiHookFunctionImportForKernel(KERNEL_PID,
&event_handler_ref,
"SceProcessmgr",
TAI_ANY_LIBRARY,
0x414CC813, // ksceKernelInvokeProcEventHandler
event_handler);
SceUID adrenaline_checker_id = ksceKernelCreateThread(
"AdrenalineChecker",
adrenaline_checker,
0x10000100,
0x1000, // 4 KiB allocated, as said by cuevavirus.
0, 0, NULL);
if (adrenaline_checker_id >= 0)
ksceKernelStartThread(adrenaline_checker_id, 0, NULL);
return SCE_KERNEL_START_SUCCESS;
}
int module_stop(SceSize args, void *argp) {
taiHookReleaseForKernel(hooks[0], event_handler_ref);
return SCE_KERNEL_STOP_SUCCESS;
}