Entirely rewritten
This commit is contained in:
parent
1dc0d6008b
commit
496135bf34
36
README.md
36
README.md
@ -1,15 +1,29 @@
|
|||||||
![Screenshot](https://i.imgur.com/FvEEEtR.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
|
# How is this "Better"?
|
||||||
- Launching PSP games from Adrenaline directly causes issues, I recommend launching games from their bubbles instead.
|
- As it's rewritten as a kernel plugin, the files that record the playtime won't ever get corrupted.
|
||||||
- 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.
|
- Complete support for Adrenaline and Adrenaline Bubbles.
|
||||||
- Homebrews don't show up.
|
- The app works at 60FPS instead of 30FPS.
|
||||||
- Dead or Alive 5 Plus once made the app crash. I don't know if the issue persists.
|
|
||||||
|
|
||||||
# To-Do
|
# How do I install this?
|
||||||
- Fix all the quirks, hopefully.
|
Just put BetterTrackPlug.skprx under *KERNEL in config.txt
|
||||||
- Add customization back.
|
```
|
||||||
|
*KERNEL
|
||||||
|
ur0:tai/BetterTrackPlug.skprx
|
||||||
|
```
|
||||||
|
and install the vpk.
|
||||||
|
|
||||||
|
# Features To Be Added
|
||||||
|
- Blacklist support so you don't have to see how much time you've spent on VitaShell.
|
||||||
|
|
||||||
|
# Notes
|
||||||
|
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.
|
||||||
|
|
||||||
# Credits
|
# Credits
|
||||||
- [Rinnegatamante](https://github.com/Rinnegatamante) for creating TrackPlug in the first place.
|
- 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.
|
||||||
- [Electry](https://github.com/Electry/) for adding pspemu support.
|
- [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.
|
@ -1,26 +1,22 @@
|
|||||||
-- Creating dirs in case they do not exist
|
-- Creating dirs in case they do not exist
|
||||||
System.createDirectory("ux0:/data/TrackPlug")
|
System.createDirectory("ux0:/data/TrackPlug/Records")
|
||||||
System.createDirectory("ux0:/data/TrackPlugArchive")
|
System.createDirectory("ux0:/data/TrackPlug/Names")
|
||||||
|
System.createDirectory("ux0:/data/TrackPlug/Config")
|
||||||
|
System.createDirectory("ux0:/data/TrackPlug/Assets")
|
||||||
-- Scanning TrackPlug folder
|
-- Scanning TrackPlug folder
|
||||||
local tbl = System.listDirectory("ux0:/data/TrackPlug")
|
local tbl = System.listDirectory("ux0:/data/TrackPlug/Records")
|
||||||
local blacklist = {
|
local blacklist = {}
|
||||||
--"VITASHELL", -- Vitashell
|
|
||||||
--"TPLG00001", -- TrackPlug
|
|
||||||
"NPXS10028", -- PSPEMU app itself
|
|
||||||
"NPXS10083"
|
|
||||||
}
|
|
||||||
-- Removing blacklisted games
|
-- 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)
|
local titleid = string.sub(file.name,1,-5)
|
||||||
for k, toberemoved in pairs(blacklist) do
|
for k, toberemoved in pairs(blacklist) do
|
||||||
if titleid == blacklist[k] then
|
if titleid == blacklist[k] then
|
||||||
System.deleteFile("ux0:/data/TrackPlug/"..file.name)
|
System.deleteFile("ux0:/data/TrackPlug/Records"..file.name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- Reset the table
|
-- Reset the table
|
||||||
tbl = System.listDirectory("ux0:/data/TrackPlug")
|
tbl = System.listDirectory("ux0:/data/TrackPlug/Records")
|
||||||
|
|
||||||
if tbl == nil then
|
if tbl == nil then
|
||||||
tbl = {}
|
tbl = {}
|
||||||
@ -51,7 +47,7 @@ end
|
|||||||
|
|
||||||
-- Recover title from homebrew database
|
-- Recover title from homebrew database
|
||||||
function recoverTitle(tid)
|
function recoverTitle(tid)
|
||||||
local file = System.openFile("ux0:/data/TrackPlugArchive/" .. tid .. ".txt", FREAD)
|
local file = System.openFile("ux0:/data/TrackPlug/Names/" .. tid .. ".txt", FREAD)
|
||||||
fsize = System.sizeFile(file)
|
fsize = System.sizeFile(file)
|
||||||
local title = System.readFile(file, fsize)
|
local title = System.readFile(file, fsize)
|
||||||
System.closeFile(file)
|
System.closeFile(file)
|
||||||
@ -61,14 +57,25 @@ end
|
|||||||
-- Extracts title name from an SFO file
|
-- Extracts title name from an SFO file
|
||||||
function extractTitle(file, tid)
|
function extractTitle(file, tid)
|
||||||
local data = System.extractSfo(file)
|
local data = System.extractSfo(file)
|
||||||
if System.doesFileExist("ux0:/data/TrackPlugArchive/" .. tid .. ".txt") then
|
if System.doesFileExist("ux0:/data/TrackPlug/Names/" .. tid .. ".txt") then
|
||||||
System.deleteFile("ux0:/data/TrackPlugArchive/" .. tid .. ".txt")
|
System.deleteFile("ux0:/data/TrackPlug/Names/" .. tid .. ".txt")
|
||||||
end
|
end
|
||||||
local file = System.openFile("ux0:/data/TrackPlugArchive/" .. tid .. ".txt", FCREATE)
|
local file = System.openFile("ux0:/data/TrackPlug/Names/" .. tid .. ".txt", FCREATE)
|
||||||
System.writeFile(file, data.title, string.len(data.title))
|
System.writeFile(file, data.title, string.len(data.title))
|
||||||
System.closeFile(file)
|
System.closeFile(file)
|
||||||
return data.title
|
return data.title
|
||||||
end
|
end
|
||||||
|
--[[
|
||||||
|
function extractIcon(tid)
|
||||||
|
local icon = System.openFile("ur0:/appmeta/" .. tid .. "/icon0.png", FREAD)
|
||||||
|
local fsize = System.sizeFile(icon)
|
||||||
|
local extractedIcon = System.readFile(icon, fsize)
|
||||||
|
System.closeFile(icon)
|
||||||
|
local file = System.openFile("ux0:/data/TrackPlug/Assets/" .. tid .. ".png", FCREATE)
|
||||||
|
System.writeFile(file, extractedIcon, string.len(extractedIcon))
|
||||||
|
System.closeFile(icon)
|
||||||
|
return 0
|
||||||
|
end]]--
|
||||||
|
|
||||||
function getRegion(titleid)
|
function getRegion(titleid)
|
||||||
local regioncode = string.sub(titleid,1,4)
|
local regioncode = string.sub(titleid,1,4)
|
||||||
@ -129,7 +136,6 @@ function getRegion(titleid)
|
|||||||
elseif string.sub(titleid,1,6) == "PSPEMU" then
|
elseif string.sub(titleid,1,6) == "PSPEMU" then
|
||||||
region = "PSP/PS1"
|
region = "PSP/PS1"
|
||||||
end
|
end
|
||||||
|
|
||||||
return region
|
return region
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -145,8 +151,11 @@ for i, file in pairs(tbl) do
|
|||||||
local titleid = string.sub(file.name,1,-5)
|
local titleid = string.sub(file.name,1,-5)
|
||||||
file.region = getRegion(titleid)
|
file.region = getRegion(titleid)
|
||||||
|
|
||||||
|
--if System.doesFileExist("ux0:/TrackPlug/Assets/" .. titleid .. "/icon0.png") then
|
||||||
|
-- file.icon = Graphics.loadImage("ux0:/TrackPlug/Assets/" .. titleid .. "/icon0.png")
|
||||||
if System.doesFileExist("ur0:/appmeta/" .. titleid .. "/icon0.png") then
|
if System.doesFileExist("ur0:/appmeta/" .. titleid .. "/icon0.png") then
|
||||||
file.icon = Graphics.loadImage("ur0:/appmeta/" .. titleid .. "/icon0.png")
|
file.icon = Graphics.loadImage("ur0:/appmeta/" .. titleid .. "/icon0.png")
|
||||||
|
-- extractIcon(titleid)
|
||||||
else
|
else
|
||||||
file.icon = unk
|
file.icon = unk
|
||||||
end
|
end
|
||||||
@ -158,7 +167,7 @@ for i, file in pairs(tbl) do
|
|||||||
file.title = "Unknown - " .. titleid
|
file.title = "Unknown - " .. titleid
|
||||||
end
|
end
|
||||||
file.id = titleid
|
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.rtime = bin2int(System.readFile(fd, 4))
|
||||||
file.ptime = FormatTime(file.rtime)
|
file.ptime = FormatTime(file.rtime)
|
||||||
System.closeFile(fd)
|
System.closeFile(fd)
|
||||||
@ -167,8 +176,13 @@ end
|
|||||||
|
|
||||||
-- Background wave effect
|
-- Background wave effect
|
||||||
local colors = {
|
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
|
if col_idx == nil then
|
||||||
col_idx = 0
|
col_idx = 0
|
||||||
end
|
end
|
||||||
@ -234,11 +248,11 @@ function showAlarm(title, select_idx)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
local sclr = Color.new(alarm_val, alarm_val, alarm_val)
|
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.debugPrint(205, 205, title, yellow)
|
||||||
Graphics.fillRect(200, 760, 215 + select_idx * 20, 235 + select_idx * 20, sclr)
|
Graphics.fillRect(200, 760, 235 + select_idx * 20, 255 + select_idx * 20, sclr)
|
||||||
Graphics.debugPrint(205, 235, "Yes", white)
|
Graphics.debugPrint(205, 255, "Yes", white)
|
||||||
Graphics.debugPrint(205, 255, "No", white)
|
Graphics.debugPrint(205, 275, "No", white)
|
||||||
end
|
end
|
||||||
-- Scroll-list Renderer
|
-- Scroll-list Renderer
|
||||||
local sel_val = 128
|
local sel_val = 128
|
||||||
@ -324,7 +338,7 @@ while #tbl > 0 do
|
|||||||
wav:init()
|
wav:init()
|
||||||
RenderList()
|
RenderList()
|
||||||
if freeze then
|
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
|
end
|
||||||
Graphics.termBlend()
|
Graphics.termBlend()
|
||||||
Screen.flip()
|
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
|
elseif Controls.check(pad, SCE_CTRL_CROSS) and not Controls.check(oldpad, SCE_CTRL_CROSS) and freeze then
|
||||||
freeze = false
|
freeze = false
|
||||||
if f_idx == 1 then -- Delete
|
if f_idx == 1 then -- Delete
|
||||||
System.deleteFile("ux0:/data/TrackPlug/" .. tbl[list_idx].name)
|
System.deleteFile("ux0:/data/TrackPlug/" .. tbl[list_idx].title)
|
||||||
table.remove(tbl, list_idx)
|
table.remove(tbl, list_idx)
|
||||||
big_tbl = {}
|
big_tbl = {}
|
||||||
list_idx = list_idx - 1
|
list_idx = list_idx - 1
|
||||||
@ -369,7 +383,7 @@ end
|
|||||||
while true do
|
while true do
|
||||||
Graphics.initBlend()
|
Graphics.initBlend()
|
||||||
Screen.clear()
|
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()
|
Graphics.termBlend()
|
||||||
Screen.flip()
|
Screen.flip()
|
||||||
Screen.waitVblankStart()
|
Screen.waitVblankStart()
|
||||||
|
@ -141,6 +141,7 @@ CONT:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _start() __attribute__ ((weak, alias ("module_start")));
|
void _start() __attribute__ ((weak, alias ("module_start")));
|
||||||
|
|
||||||
int module_start(SceSize argc, const void *args) {
|
int module_start(SceSize argc, const void *args) {
|
||||||
char titleid[12];
|
char titleid[12];
|
||||||
sceAppMgrAppParamGetString(0, 12, titleid, 12);
|
sceAppMgrAppParamGetString(0, 12, titleid, 12);
|
||||||
|
8
src/BetterTrackPlug.yml
Normal file
8
src/BetterTrackPlug.yml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
BetterTrackPlug:
|
||||||
|
attributes: 0
|
||||||
|
version:
|
||||||
|
major: 1
|
||||||
|
minor: 0
|
||||||
|
main:
|
||||||
|
start: module_start
|
||||||
|
stop: module_stop
|
43
src/CMakeLists.txt
Normal file
43
src/CMakeLists.txt
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
cmake_minimum_required(VERSION 2.8)
|
||||||
|
|
||||||
|
if(NOT DEFINED CMAKE_TOOLCHAIN_FILE)
|
||||||
|
if(DEFINED ENV{VITASDK})
|
||||||
|
set(CMAKE_TOOLCHAIN_FILE "$ENV{VITASDK}/share/vita.toolchain.cmake" CACHE PATH "toolchain file")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Please define VITASDK to point to your SDK path!")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
link_directories(
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(${PROJECT_NAME}
|
||||||
|
main.c
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(${PROJECT_NAME}
|
||||||
|
SceSysmemForDriver_stub
|
||||||
|
SceIofilemgrForDriver_stub
|
||||||
|
SceSysclibForDriver_stub
|
||||||
|
SceThreadmgrForDriver_stub
|
||||||
|
taihenForKernel_stub
|
||||||
|
SceDebugForDriver_stub
|
||||||
|
SceSysrootForKernel_stub
|
||||||
|
SceIofilemgr_stub
|
||||||
|
SceLibc_stub
|
||||||
|
gcc
|
||||||
|
-nostdlib
|
||||||
|
)
|
||||||
|
vita_create_self(${PROJECT_NAME}.skprx ${PROJECT_NAME}
|
||||||
|
UNSAFE
|
||||||
|
CONFIG ${CMAKE_SOURCE_DIR}/BetterTrackPlug.yml
|
||||||
|
)
|
||||||
|
|
240
src/main.c
Normal file
240
src/main.c
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
#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];
|
||||||
|
snprintf(path, 128, "ux0:/data/TrackPlug/Names/%s.txt", dummy_id);
|
||||||
|
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) {
|
||||||
|
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;
|
||||||
|
}
|
Reference in New Issue
Block a user