From de8e67393532dfa68fbfc5921f68dccc50a51760 Mon Sep 17 00:00:00 2001 From: chinseng85 Date: Sat, 21 Mar 2020 21:29:42 +0800 Subject: [PATCH 01/11] Add files via upload Fix an issue where icon0 and game title id cannot be found after the game is deleted. --- app/index.lua | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/app/index.lua b/app/index.lua index cdf4720..a9db86e 100644 --- a/app/index.lua +++ b/app/index.lua @@ -51,7 +51,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/TrackPlugArchive/" .. tid .. "/title.txt", FREAD) fsize = System.sizeFile(file) local title = System.readFile(file, fsize) System.closeFile(file) @@ -61,15 +61,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/TrackPlugArchive/" .. tid .. "/title.txt") then + System.deleteFile("ux0:/data/TrackPlugArchive/" .. tid .. "/title.txt") end - local file = System.openFile("ux0:/data/TrackPlugArchive/" .. tid .. ".txt", FCREATE) + local file = System.openFile("ux0:/data/TrackPlugArchive/" .. 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/TrackPlugArchive/" .. 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) @@ -145,12 +153,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/TrackPlugArchive/" .. titleid .. "/icon0.png") then + file.icon = Graphics.loadImage("ux0:/data/TrackPlugArchive/" .. 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/TrackPlugArchive/" .. titleid .. "") + copyIcon(titleid) else file.icon = unk end - if System.doesFileExist("ux0:/data/TrackPlugArchive/" .. titleid .. ".txt") then + + + if System.doesFileExist("ux0:/data/TrackPlugArchive/" .. 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) From 496135bf3459c8af179f8aa3f139822a3a579561 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Furkan=20Mudanyal=C4=B1?= Date: Sat, 21 Mar 2020 18:13:25 +0300 Subject: [PATCH 02/11] Entirely rewritten --- README.md | 36 ++++-- app/index.lua | 70 +++++++----- plugin/main.c | 1 + src/BetterTrackPlug.yml | 8 ++ src/CMakeLists.txt | 43 +++++++ src/main.c | 240 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 359 insertions(+), 39 deletions(-) create mode 100644 src/BetterTrackPlug.yml create mode 100644 src/CMakeLists.txt create mode 100644 src/main.c diff --git a/README.md b/README.md index 35b0319..510f0f1 100644 --- a/README.md +++ b/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 -- 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. +- 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 +``` +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 -- [Rinnegatamante](https://github.com/Rinnegatamante) for creating TrackPlug in the first place. -- [Electry](https://github.com/Electry/) for adding pspemu support. \ No newline at end of file +- 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. \ No newline at end of file diff --git a/app/index.lua b/app/index.lua index cdf4720..7541f0d 100644 --- a/app/index.lua +++ b/app/index.lua @@ -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/Records") +System.createDirectory("ux0:/data/TrackPlug/Names") +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 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/Names/" .. tid .. ".txt", FREAD) fsize = System.sizeFile(file) local title = System.readFile(file, fsize) System.closeFile(file) @@ -61,14 +57,25 @@ 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/Names/" .. tid .. ".txt") then + System.deleteFile("ux0:/data/TrackPlug/Names/" .. tid .. ".txt") 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.closeFile(file) return data.title 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) local regioncode = string.sub(titleid,1,4) @@ -129,7 +136,6 @@ function getRegion(titleid) elseif string.sub(titleid,1,6) == "PSPEMU" then region = "PSP/PS1" end - return region end @@ -145,9 +151,12 @@ for i, file in pairs(tbl) do local titleid = string.sub(file.name,1,-5) 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 file.icon = Graphics.loadImage("ur0:/appmeta/" .. titleid .. "/icon0.png") - else + -- extractIcon(titleid) + else file.icon = unk end if System.doesFileExist("ux0:/data/TrackPlugArchive/" .. titleid .. ".txt") then @@ -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/" .. tbl[list_idx].title) 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 \ No newline at end of file diff --git a/plugin/main.c b/plugin/main.c index 638d627..bcc0d69 100644 --- a/plugin/main.c +++ b/plugin/main.c @@ -141,6 +141,7 @@ CONT: } void _start() __attribute__ ((weak, alias ("module_start"))); + int module_start(SceSize argc, const void *args) { char titleid[12]; sceAppMgrAppParamGetString(0, 12, titleid, 12); diff --git a/src/BetterTrackPlug.yml b/src/BetterTrackPlug.yml new file mode 100644 index 0000000..33e11a7 --- /dev/null +++ b/src/BetterTrackPlug.yml @@ -0,0 +1,8 @@ +BetterTrackPlug: + attributes: 0 + version: + major: 1 + minor: 0 + main: + start: module_start + stop: module_stop \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..f6fb25b --- /dev/null +++ b/src/CMakeLists.txt @@ -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 +) + diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..0c2a41d --- /dev/null +++ b/src/main.c @@ -0,0 +1,240 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} \ No newline at end of file From 73d5e0dcc8b41281d583ffdda29f73bf988f5e72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Furkan=20Mudanyal=C4=B1?= Date: Sat, 21 Mar 2020 18:34:53 +0300 Subject: [PATCH 03/11] Added screenshot --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 510f0f1..8974e0d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +![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. @@ -18,6 +19,7 @@ and install the vpk. - 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. From 42be876cb8f7c181125d10db7a69b307b0b0ce60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Furkan=20Mudanyal=C4=B1?= Date: Sat, 21 Mar 2020 18:39:33 +0300 Subject: [PATCH 04/11] better screenshot --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8974e0d..ef7694d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![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. From d608e6051d56b8726db11365b1ef13d9cc2d5629 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Furkan=20Mudanyal=C4=B1?= Date: Sat, 21 Mar 2020 19:07:09 +0300 Subject: [PATCH 05/11] remove old src --- plugin/CMakeLists.txt | 49 ------------ plugin/TrackPlugX.yml | 8 -- plugin/main.c | 181 ------------------------------------------ 3 files changed, 238 deletions(-) delete mode 100644 plugin/CMakeLists.txt delete mode 100755 plugin/TrackPlugX.yml delete mode 100644 plugin/main.c diff --git a/plugin/CMakeLists.txt b/plugin/CMakeLists.txt deleted file mode 100644 index dbc8444..0000000 --- a/plugin/CMakeLists.txt +++ /dev/null @@ -1,49 +0,0 @@ -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(TrackPlugX) -include("${VITASDK}/share/vita.cmake" REQUIRED) - -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wl,-q -Wall -O3 -std=gnu99") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-rtti -fno-exceptions") - -include_directories( -) - -link_directories( - ${CMAKE_CURRENT_BINARY_DIR} -) - -if (NOT ${RELEASE}) - add_definitions(-DENABLE_LOGGING) -endif() - -add_executable(${PROJECT_NAME} - main.c -) - -target_link_libraries(${PROJECT_NAME} - gcc - taihen_stub - SceAppMgr_stub - SceLibKernel_stub - SceIofilemgr_stub - SceLibc_stub - SceKernelModulemgr_stub - SceKernelThreadMgr_stub -) - -set_target_properties(${PROJECT_NAME} - PROPERTIES LINK_FLAGS "-nostdlib" -) - -vita_create_self(${PROJECT_NAME}.suprx ${PROJECT_NAME} - CONFIG ${CMAKE_SOURCE_DIR}/${PROJECT_NAME}.yml -) diff --git a/plugin/TrackPlugX.yml b/plugin/TrackPlugX.yml deleted file mode 100755 index c8b86b3..0000000 --- a/plugin/TrackPlugX.yml +++ /dev/null @@ -1,8 +0,0 @@ -TrackPlugX: - attributes: 0 - version: - major: 2 - minor: 1 - main: - start: module_start - stop: module_stop diff --git a/plugin/main.c b/plugin/main.c deleted file mode 100644 index bcc0d69..0000000 --- a/plugin/main.c +++ /dev/null @@ -1,181 +0,0 @@ -#include -#include - -#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; -} From 0084bf25eea2babcb50f49a94cf9d855cf0da48a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Furkan=20Mudanyal=C4=B1?= Date: Sat, 21 Mar 2020 19:17:45 +0300 Subject: [PATCH 06/11] update --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ef7694d..8b0e792 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ I would advise you to set up your bubbles in a way that their title ID's will be 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 - 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. From 643613a2ecf627d796749247e1ade6555acda791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Furkan=20Mudanyal=C4=B1?= Date: Sat, 21 Mar 2020 19:27:31 +0300 Subject: [PATCH 07/11] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8b0e792..8ffda7e 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ A plugin that keeps track of how long you played your games, and an app to show # 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. @@ -29,4 +30,4 @@ Please let me know if you see any bugs, it would be extremely helpful. - 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. \ No newline at end of file +- **ecamci** for making the assets. From 29504a56839a0188869cda14ac66daddefbed864 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Furkan=20Mudanyal=C4=B1?= Date: Sat, 21 Mar 2020 20:45:50 +0300 Subject: [PATCH 08/11] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8ffda7e..8db5033 100644 --- a/README.md +++ b/README.md @@ -31,3 +31,4 @@ Please let me know if you see any bugs, it would be extremely helpful. - [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. From f5ad46635660dca82cd0f338ea4bbd621f8add4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Furkan=20Mudanyal=C4=B1?= Date: Sat, 21 Mar 2020 21:24:25 +0300 Subject: [PATCH 09/11] The plugin now creates the needed directories --- src/main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main.c b/src/main.c index 48776f6..38c78f2 100644 --- a/src/main.c +++ b/src/main.c @@ -217,6 +217,9 @@ int event_handler(int pid, int ev, int a3, int a4, int *a5, int 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", From d0938ac2df8f2ccc38201f5ee21dbd031c6befa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Furkan=20Mudanyal=C4=B1?= Date: Sun, 22 Mar 2020 00:25:05 +0300 Subject: [PATCH 10/11] Added a clarification on the install steps --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8db5033..9811bdc 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,8 @@ Just put BetterTrackPlug.skprx under *KERNEL in config.txt *KERNEL ur0:tai/BetterTrackPlug.skprx ``` -and install the vpk. +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. From 3e565e5f4a017bd7f41c6ebc794bd24647101e46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Furkan=20Mudanyal=C4=B1?= Date: Sun, 22 Mar 2020 00:49:32 +0300 Subject: [PATCH 11/11] Forgot to update delete commands location after changing file hierarchy --- app/index.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/index.lua b/app/index.lua index 500b19d..a7de7c0 100644 --- a/app/index.lua +++ b/app/index.lua @@ -370,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].title) + System.deleteFile("ux0:/data/TrackPlug/Records/" .. tbl[list_idx].id .. ".bin") table.remove(tbl, list_idx) big_tbl = {} list_idx = list_idx - 1