Merge pull request #2 from fmudanyali/master

Merge
This commit is contained in:
chinseng85 2020-03-25 21:10:28 +08:00 committed by GitHub
commit eb2053232a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 347 additions and 253 deletions

View File

@ -1,15 +1,35 @@
![Screenshot](https://i.imgur.com/FvEEEtR.png)
![Screenshot](https://i.imgur.com/O1G0EUq.png)
# What is this?
A plugin that keeps track of how long you played your games, and an app to show you the values.
# Known Bugs/Issues
- Launching PSP games from Adrenaline directly causes issues, I recommend launching games from their bubbles instead.
- Using Adrenaline Bubbles Manager, having BubbleID set to PSPEMUXXX makes the app show the title as the games initial plus d, Md or Od for example.
- Homebrews don't show up.
- Dead or Alive 5 Plus once made the app crash. I don't know if the issue persists.
# How is this "Better"?
- As it's rewritten as a kernel plugin, the files that record the playtime won't ever get corrupted.
- It only writes to file when you close/launch or suspend/resume the games so it doesn't murder your memory card, unlike the old one.
- Complete support for Adrenaline and Adrenaline Bubbles.
- The app works at 60FPS instead of 30FPS.
# To-Do
- Fix all the quirks, hopefully.
- Add customization back.
# How do I install this?
Just put BetterTrackPlug.skprx under *KERNEL in config.txt
```
*KERNEL
ur0:tai/BetterTrackPlug.skprx
```
install the vpk.
Reboot.
# Features To Be Added
- Blacklist support so you don't have to see how much time you've spent on VitaShell.
# Notes
![Bubbles](https://i.imgur.com/qZwPMXU.png)
I would advise you to set up your bubbles in a way that their title ID's will be the same as the corresponding PSP game's title ID instead of the default PSPEMUXXX, this way, they will use the same file that stores the playtime and you won't see the game twice on the list if you launch it both from bubble or directly from adrenaline.
As it's currently not possible for me to extract icons from inside Adrenaline, games launched directly from Adrenaline won't have any icons if there is no corresponding bubble for the said game. I also don't think it would be efficient even if I knew how to do it.
Please let me know if you see any bugs, it would be extremely helpful.
# Credits
- [Rinnegatamante](https://github.com/Rinnegatamante) for creating TrackPlug in the first place.
- [Electry](https://github.com/Electry/) for adding pspemu support.
- Special thanks to **teakhanirons**, [dots-tb](https://github.com/dots-tb), [cuevavirus](https://github.com/cuevavirus/) and Team CBPS for helping me make this plugin and not losing their minds because of my questions. Without their help I wouldn't even know where to start.
- [Rinnegatamante](https://github.com/Rinnegatamante) for [LPP-Vita](https://github.com/Rinnegatamante/lpp-vita) and making the idea of TrackPlug in the first place.
- [Electry](https://github.com/Electry/) for his code chunk responsible for getting titles inside Adrenaline.
- **ecamci** for making the assets.
- chinseng85 for adding the ability to store games' icons so when you remove them, you will still be able to see their icons.

View File

@ -1,26 +1,22 @@
-- Creating dirs in case they do not exist
System.createDirectory("ux0:/data/TrackPlug")
System.createDirectory("ux0:/data/TrackPlugArchive")
System.createDirectory("ux0:/data/TrackPlug/")
System.createDirectory("ux0:/data/TrackPlug/Records")
System.createDirectory("ux0:/data/TrackPlug/Config")
System.createDirectory("ux0:/data/TrackPlug/Assets")
-- Scanning TrackPlug folder
local tbl = System.listDirectory("ux0:/data/TrackPlug")
local blacklist = {
--"VITASHELL", -- Vitashell
--"TPLG00001", -- TrackPlug
"NPXS10028", -- PSPEMU app itself
"NPXS10083"
}
local tbl = System.listDirectory("ux0:/data/TrackPlug/Records")
local blacklist = {}
-- Removing blacklisted games
for i, file in pairs(tbl) do
--[[for i, file in pairs(tbl) do
local titleid = string.sub(file.name,1,-5)
for k, toberemoved in pairs(blacklist) do
if titleid == blacklist[k] then
System.deleteFile("ux0:/data/TrackPlug/"..file.name)
System.deleteFile("ux0:/data/TrackPlug/Records"..file.name)
end
end
end
-- Reset the table
tbl = System.listDirectory("ux0:/data/TrackPlug")
tbl = System.listDirectory("ux0:/data/TrackPlug/Records")]]--
if tbl == nil then
tbl = {}
@ -51,7 +47,7 @@ end
-- Recover title from homebrew database
function recoverTitle(tid)
local file = System.openFile("ux0:/data/TrackPlugArchive/" .. tid .. ".txt", FREAD)
local file = System.openFile("ux0:/data/TrackPlug/Assets/" .. tid .. "/title.txt", FREAD)
fsize = System.sizeFile(file)
local title = System.readFile(file, fsize)
System.closeFile(file)
@ -61,15 +57,23 @@ end
-- Extracts title name from an SFO file
function extractTitle(file, tid)
local data = System.extractSfo(file)
if System.doesFileExist("ux0:/data/TrackPlugArchive/" .. tid .. ".txt") then
System.deleteFile("ux0:/data/TrackPlugArchive/" .. tid .. ".txt")
if System.doesFileExist("ux0:/data/TrackPlug/Assets/" .. tid .. "/title.txt") then
System.deleteFile("ux0:/data/TrackPlug/Assets/" .. tid .. "/title.txt")
end
local file = System.openFile("ux0:/data/TrackPlugArchive/" .. tid .. ".txt", FCREATE)
local file = System.openFile("ux0:/data/TrackPlug/Assets/" .. tid .. "/title.txt", FCREATE)
System.writeFile(file, data.title, string.len(data.title))
System.closeFile(file)
return data.title
end
function copyIcon(titleid)
newFile = System.openFile("ux0:/data/TrackPlug/Assets/" .. titleid .. "/icon0.png", FCREATE)
oldFile = System.openFile("ur0:/appmeta/" .. titleid .. "/icon0.png", FREAD)
fileSize = System.sizeFile(oldFile)
icon = System.readFile(oldFile, fileSize)
System.writeFile(newFile, icon, fileSize)
end
function getRegion(titleid)
local regioncode = string.sub(titleid,1,4)
local prefix = string.sub(regioncode,1,2)
@ -129,7 +133,6 @@ function getRegion(titleid)
elseif string.sub(titleid,1,6) == "PSPEMU" then
region = "PSP/PS1"
end
return region
end
@ -145,12 +148,18 @@ for i, file in pairs(tbl) do
local titleid = string.sub(file.name,1,-5)
file.region = getRegion(titleid)
if System.doesFileExist("ur0:/appmeta/" .. titleid .. "/icon0.png") then
if System.doesFileExist("ux0:/data/TrackPlug/Assets/" .. titleid .. "/icon0.png") then
file.icon = Graphics.loadImage("ux0:/data/TrackPlug/Assets/" .. titleid .. "/icon0.png")
elseif System.doesFileExist("ur0:/appmeta/" .. titleid .. "/icon0.png") then
file.icon = Graphics.loadImage("ur0:/appmeta/" .. titleid .. "/icon0.png")
System.createDirectory("ux0:/data/TrackPlug/Assets/" .. titleid .. "")
copyIcon(titleid)
else
file.icon = unk
end
if System.doesFileExist("ux0:/data/TrackPlugArchive/" .. titleid .. ".txt") then
if System.doesFileExist("ux0:/data/TrackPlug/Assets/" .. titleid .. "/title.txt") then
file.title = recoverTitle(titleid)
elseif System.doesFileExist("ux0:/app/" .. titleid .. "/sce_sys/param.sfo") then
file.title = extractTitle("ux0:/app/" .. titleid .. "/sce_sys/param.sfo", titleid)
@ -158,7 +167,7 @@ for i, file in pairs(tbl) do
file.title = "Unknown - " .. titleid
end
file.id = titleid
fd = System.openFile("ux0:/data/TrackPlug/" .. file.name, FREAD)
fd = System.openFile("ux0:/data/TrackPlug/Records/" .. file.name, FREAD)
file.rtime = bin2int(System.readFile(fd, 4))
file.ptime = FormatTime(file.rtime)
System.closeFile(fd)
@ -167,8 +176,13 @@ end
-- Background wave effect
local colors = {
{Color.new(72,72,72), Color.new(30,20,25), Color.new(200,180,180)} -- Black'N'Rose
{Color.new(72,72,72), Color.new(30,20,25), Color.new(200,180,180)},
{Color.new(72,72,72), Color.new(30,20,25), Color.new(200,180,180)},
{Color.new(72,72,72), Color.new(30,20,25), Color.new(200,180,180)},
{Color.new(72,72,72), Color.new(30,20,25), Color.new(200,180,180)},
{Color.new(72,72,72), Color.new(30,20,25), Color.new(200,180,180)}
}
if col_idx == nil then
col_idx = 0
end
@ -234,11 +248,11 @@ function showAlarm(title, select_idx)
end
end
local sclr = Color.new(alarm_val, alarm_val, alarm_val)
Graphics.fillRect(200, 760, 200, 280, grey)
Graphics.fillRect(200, 760, 200, 300, grey)
Graphics.debugPrint(205, 205, title, yellow)
Graphics.fillRect(200, 760, 215 + select_idx * 20, 235 + select_idx * 20, sclr)
Graphics.debugPrint(205, 235, "Yes", white)
Graphics.debugPrint(205, 255, "No", white)
Graphics.fillRect(200, 760, 235 + select_idx * 20, 255 + select_idx * 20, sclr)
Graphics.debugPrint(205, 255, "Yes", white)
Graphics.debugPrint(205, 275, "No", white)
end
-- Scroll-list Renderer
local sel_val = 128
@ -324,7 +338,7 @@ while #tbl > 0 do
wav:init()
RenderList()
if freeze then
showAlarm("Do you want to delete this record permanently?", f_idx)
showAlarm("Do you want to delete this record permanently? \n" .. tbl[list_idx].title, f_idx)
end
Graphics.termBlend()
Screen.flip()
@ -356,7 +370,7 @@ while #tbl > 0 do
elseif Controls.check(pad, SCE_CTRL_CROSS) and not Controls.check(oldpad, SCE_CTRL_CROSS) and freeze then
freeze = false
if f_idx == 1 then -- Delete
System.deleteFile("ux0:/data/TrackPlug/" .. tbl[list_idx].name)
System.deleteFile("ux0:/data/TrackPlug/Records/" .. tbl[list_idx].id .. ".bin")
table.remove(tbl, list_idx)
big_tbl = {}
list_idx = list_idx - 1
@ -369,8 +383,8 @@ end
while true do
Graphics.initBlend()
Screen.clear()
Graphics.debugPrint(5, 5, "No games tracked yet.", white)
Graphics.debugPrint(5, 5, "No games tracked yet. You sure you installed the plugin correctly?", white)
Graphics.termBlend()
Screen.flip()
Screen.waitVblankStart()
end
end

View File

@ -1,8 +0,0 @@
TrackPlugX:
attributes: 0
version:
major: 2
minor: 1
main:
start: module_start
stop: module_stop

View File

@ -1,180 +0,0 @@
#include <vitasdk.h>
#include <taihen.h>
#define SECOND 1000000
#define SAVE_PERIOD 10
int snprintf(char *s, size_t n, const char *format, ...);
// ScePspemu
static SceUID sub_810053F8_hookid = -1;
static tai_hook_ref_t sub_810053F8_hookref = {0};
static char adrenaline_titleid[12];
static uint8_t is_pspemu_loaded = 0;
static uint8_t is_pspemu_custom_bbl = 0;
static uint8_t is_pspemu_title_written = 0;
static uint8_t is_playtime_loaded = 0;
static char playtime_bin_path[128];
static uint64_t playtime_start = 0;
static uint64_t tick_start = 0;
static void load_playtime(const char *titleid) {
snprintf(playtime_bin_path, 128, "ux0:/data/TrackPlug/%s.bin", titleid);
tick_start = sceKernelGetProcessTimeWide();
is_pspemu_title_written = 0;
SceUID fd = sceIoOpen(playtime_bin_path, SCE_O_RDONLY, 0777);
if (fd < 0) {
playtime_start = 0;
is_playtime_loaded = 1;
return;
}
sceIoRead(fd, &playtime_start, sizeof(uint64_t));
sceIoClose(fd);
is_playtime_loaded = 1;
}
static void write_playtime() {
uint32_t playtime = playtime_start +
(sceKernelGetProcessTimeWide() - tick_start) / SECOND;
SceUID fd = sceIoOpen(playtime_bin_path,
SCE_O_WRONLY | SCE_O_CREAT | SCE_O_TRUNC, 0777);
if (fd < 0) {
return;
}
sceIoWrite(fd, &playtime, sizeof(uint64_t));
sceIoClose(fd);
}
void write_title(const char *titleid, const char *title) {
char path[128];
snprintf(path, 128, "ux0:/data/TrackPlugArchive/%s.txt", titleid);
is_pspemu_title_written = 1;
// Check if already exists
SceUID fd = sceIoOpen(path, SCE_O_RDONLY, 0777);
if (fd >= 0) {
sceIoClose(fd);
return;
}
fd = sceIoOpen(path, SCE_O_WRONLY | SCE_O_CREAT, 0777);
if (fd < 0) {
return;
}
sceIoWrite(fd, title, strlen(title));
sceIoClose(fd);
}
static int check_adrenaline() {
void *SceAdrenaline = (void *)0x73CDE000;
char *title = SceAdrenaline + 24;
char *titleid = SceAdrenaline + 152;
if (title[0] == 0 || !strncmp(title, "XMB", 3))
return 0;
// Write custom bubble Title
if (is_pspemu_custom_bbl && !is_pspemu_title_written)
write_title(adrenaline_titleid, title);
// XMB game changed?
if (!is_pspemu_custom_bbl && strncmp(adrenaline_titleid, titleid, 9)) {
if (is_playtime_loaded)
write_playtime(); // Save closed game
is_playtime_loaded = 0;
strcpy(adrenaline_titleid, titleid);
load_playtime(titleid);
write_title(titleid, title);
return 0;
}
return 1;
}
int sub_810053F8_patched(int a1, int a2) {
char *pspemu_titleid = (char *)(a2 + 68);
// If using custom bubble
if (strncmp(pspemu_titleid, "PSPEMUCFW", 9)) {
is_pspemu_custom_bbl = 1;
strcpy(adrenaline_titleid, pspemu_titleid);
load_playtime(pspemu_titleid);
}
is_pspemu_loaded = 1;
return TAI_CONTINUE(int, sub_810053F8_hookref, a1, a2);
}
static int tracker_thread(SceSize args, void *argp) {
while (1) {
if (is_pspemu_loaded) {
// Check if XMB/game has changed, write Title if necessary
int ret = check_adrenaline();
if (!is_pspemu_custom_bbl && !ret) {
goto CONT;
}
}
if (is_playtime_loaded) {
write_playtime();
}
CONT:
sceKernelDelayThread(SAVE_PERIOD * SECOND);
}
return sceKernelExitDeleteThread(0);
}
void _start() __attribute__ ((weak, alias ("module_start")));
int module_start(SceSize argc, const void *args) {
char titleid[12];
sceAppMgrAppParamGetString(0, 12, titleid, 12);
if (!strncmp(titleid, "NPXS10028", 9)) {
tai_module_info_t tai_info;
tai_info.size = sizeof(tai_module_info_t);
taiGetModuleInfo("ScePspemu", &tai_info);
sub_810053F8_hookid = taiHookFunctionOffset(
&sub_810053F8_hookref,
tai_info.modid,
0, 0x53F8, 1,
sub_810053F8_patched);
} else {
load_playtime(titleid);
}
SceUID tracker_thread_id = sceKernelCreateThread(
"TrackPlugX",
tracker_thread,
0x10000100,
0x10000,
0, 0, NULL);
if (tracker_thread_id >= 0)
sceKernelStartThread(tracker_thread_id, 0, NULL);
return SCE_KERNEL_START_SUCCESS;
}
int module_stop(SceSize argc, const void *args) {
if (sub_810053F8_hookid >= 0) {
taiHookRelease(sub_810053F8_hookid, sub_810053F8_hookref);
}
return SCE_KERNEL_STOP_SUCCESS;
}

8
src/BetterTrackPlug.yml Normal file
View File

@ -0,0 +1,8 @@
BetterTrackPlug:
attributes: 0
version:
major: 1
minor: 0
main:
start: module_start
stop: module_stop

View File

@ -8,42 +8,36 @@ if(NOT DEFINED CMAKE_TOOLCHAIN_FILE)
endif()
endif()
project(TrackPlugX)
include("${VITASDK}/share/vita.cmake" REQUIRED)
project(BetterTrackPlug)
include("$ENV{VITASDK}/share/vita.cmake" REQUIRED)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wl,-q -Wall -O3 -std=gnu99")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -nostdlib")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-rtti -fno-exceptions")
include_directories(
)
link_directories(
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_BINARY_DIR}
)
if (NOT ${RELEASE})
add_definitions(-DENABLE_LOGGING)
endif()
add_executable(${PROJECT_NAME}
main.c
main.c
)
target_link_libraries(${PROJECT_NAME}
gcc
taihen_stub
SceAppMgr_stub
SceLibKernel_stub
SceSysmemForDriver_stub
SceIofilemgrForDriver_stub
SceSysclibForDriver_stub
SceThreadmgrForDriver_stub
taihenForKernel_stub
SceDebugForDriver_stub
SceSysrootForKernel_stub
SceIofilemgr_stub
SceLibc_stub
SceKernelModulemgr_stub
SceKernelThreadMgr_stub
gcc
-nostdlib
)
vita_create_self(${PROJECT_NAME}.skprx ${PROJECT_NAME}
UNSAFE
CONFIG ${CMAKE_SOURCE_DIR}/BetterTrackPlug.yml
)
set_target_properties(${PROJECT_NAME}
PROPERTIES LINK_FLAGS "-nostdlib"
)
vita_create_self(${PROJECT_NAME}.suprx ${PROJECT_NAME}
CONFIG ${CMAKE_SOURCE_DIR}/${PROJECT_NAME}.yml
)

246
src/main.c Normal file
View File

@ -0,0 +1,246 @@
#include <stdio.h>
#include <taihen.h>
#include <psp2/io/fcntl.h>
#include <psp2kern/kernel/sysmem.h>
#include <string.h>
#include <stdarg.h>
#include <vitasdkkern.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;
}