Skip to content

Commit

Permalink
WIP real hooking
Browse files Browse the repository at this point in the history
  • Loading branch information
Kethen committed Apr 28, 2024
1 parent 3e0e6a6 commit a17ab0e
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 154 deletions.
6 changes: 6 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FROM ubuntu:22.04
RUN export noninteractive; apt update; apt install -y wget libreadline8 libusb-0.1-4 tmux make libmpc3
RUN wget https://github.com/pspdev/pspdev/releases/download/latest/pspdev-ubuntu-latest.tar.gz -O - | gzip -d | tar -C /usr/local -x
RUN echo 'export PATH="/usr/local/pspdev/bin:$PATH"' > /etc/profile.d/pspsdk.sh
RUN echo 'export LD_LIBRARY_PATH="/usr/local/pspsdk/lib:$LD_LIBRARY_PATH"' >> /etc/profile.d/pspsdk.sh
ENTRYPOINT ["/bin/bash", "-l"]
10 changes: 4 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
TARGET = GTRemastered
OBJS = main.o exports.o

CFLAGS = -O2 -Os -G0 -Wall -fshort-wchar -fno-pic -mno-check-zero-division
CFLAGS = -O2 -Os -G0 -Wall -fshort-wchar -fno-pic -mno-check-zero-division -DDEBUG_LOG=1
CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti
ASFLAGS = $(CFLAGS)

BUILD_PRX = 1
PRX_EXPORTS = exports.exp

# these do not seem to be available anymore..?
#USE_KERNEL_LIBC = 0
#USE_PSPSDK_LIBC = 1
#USE_KERNEL_LIBS = 1
# use kernel "libc"
USE_KERNEL_LIBS = 1

# use built-in libc to supply sprintf for ppsspp and atoi in general
LIBS = -lc -lpspsystemctrl_kernel
LIBS = -lpspsystemctrl_kernel

PSPSDK = $(shell psp-config --pspsdk-path)
include $(PSPSDK)/lib/build_prx.mak
8 changes: 6 additions & 2 deletions build_podman.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
IMAGE_NAME="pspsdk"

if [ "$REBUILD_IMAGE" == "true" ] && podman image exists $IMAGE_NAME
then
podman image rm -f $IMAGE_NAME
fi

if ! podman image exists $IMAGE_NAME
then
podman pull ghcr.io/pspdev/pspsdk:latest
podman image build -f Dockerfile -t $IMAGE_NAME
fi

podman run \
Expand All @@ -12,6 +17,5 @@ podman run \
-v ./build_podman.sh:/workdir/build_podman.sh:ro \
-v ./script:/workdir/script:ro \
-w /workdir \
--entrypoint /bin/bash \
$IMAGE_NAME \
/workdir/script
2 changes: 2 additions & 0 deletions exports.exp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ PSP_BEGIN_EXPORTS

PSP_EXPORT_START(syslib, 0, 0x8000)
PSP_EXPORT_FUNC(module_start)
PSP_EXPORT_FUNC(module_start)
PSP_EXPORT_FUNC(module_stop)
PSP_EXPORT_VAR(module_info)
PSP_EXPORT_END

Expand Down
209 changes: 63 additions & 146 deletions main.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Remastered Controls: analog to digital
Remastered Controls: RemasteredControls_GTpsp
Copyright (C) 2018, TheFloW
Copyright (C) 2023, Katharine Chui
Expand All @@ -22,6 +22,7 @@
#include <pspctrl.h>
#include <pspiofilemgr.h>
#include <pspthreadman.h>
#include <pspfpu.h>

#include <stdio.h>
#include <stdlib.h>
Expand All @@ -32,7 +33,7 @@
#define MODULE_NAME "GTRemastered"
#define GAME_MODULE_NAME "PDIAPP"

PSP_MODULE_INFO(MODULE_NAME, 0x1007, 1, 0);
PSP_MODULE_INFO(MODULE_NAME, PSP_MODULE_KERNEL, 1, 0);

int sceKernelQuerySystemCall(void *function);

Expand Down Expand Up @@ -66,22 +67,32 @@ static u32 MakeSyscallStub(void *function) {
return stub;
}


// is there a flush..? or the non async version always syncs?
#define DEBUG 1
#if DEBUG
static int logfd;
#define LOG(...) \
if(logfd >= 0){ \
char logbuf[128]; \
int loglen = sprintf(logbuf, __VA_ARGS__); \
if(loglen > 0){ \
sceIoWrite(logfd, logbuf, loglen); \
#if DEBUG_LOG
static int logfd = -1;
#define LOG(...) {\
if(logfd < 0){ \
logfd = sceIoOpen("ms0:/PSP/"MODULE_NAME".log", PSP_O_WRONLY|PSP_O_CREAT|PSP_O_APPEND, 0777); \
if(logfd < 0){ \
logfd = sceIoOpen("ef0:/PSP/"MODULE_NAME".log", PSP_O_WRONLY|PSP_O_CREAT|PSP_O_APPEND, 0777); \
} \
} \
char _log_buf[128]; \
int _log_len = sprintf(_log_buf, __VA_ARGS__); \
if(logfd >= 0){ \
if(_log_len != 0){ \
sceIoWrite(logfd, _log_buf, _log_len); \
} \
sceIoClose(logfd); \
logfd = -1; \
}else{ \
sceIoWrite(2, _log_buf, _log_len); \
} \
}
#else // DEBUG
#else // DEBUG_LOG
#define LOG(...)
#endif // DEBUG
#define VERBOSE 0
#endif // DEBUG_LOG
#if VERBOSE
#define LOG_VERBOSE(...) LOG(__VA_ARGS__)
#else // VERBOSE
Expand Down Expand Up @@ -122,80 +133,6 @@ u32 offset_populate_car_analog_control = 0;
ptr = (void *)patch_buffer; \
}

// XXX ppsspp loading savestate reloads module imports and overwrites this kind of hooking in case HLE
// syscall changed
// https://github.com/hrydgard/ppsspp/blob/master/Core/HLE/sceKernelModule.cpp
// if this kind of hooking on ppsspp cannot be avoided, repatch in a slow thread loop maybe, at least peek
// can be used instead for this particular plugin

// jacking JR_SYSCALL in ppsspp, so just save the two instructions, instead of seeking the target
// also scan other modules for the same pattern and patch them if ppsspp
// for real hw, go the jump target then attempt the more standard two instructions hijack
// hopefully works with the static args loaded sceCtrl functions, at least referencing uofw and joysens
#define HIJACK_SYSCALL_STUB(a, f, ptr) \
{ \
LOG("hijacking syscall stub at 0x%lx with 0x%lx\n", (u32)a, (u32)f); \
u32 _func_ = (u32)a; \
LOG("original instructions: 0x%lx 0x%lx\n", _lw(_func_), _lw(_func_ + 4)); \
u32 pattern[2]; \
_sw(_lw(_func_), (u32)pattern); \
_sw(_lw(_func_ + 4), (u32)pattern + 4); \
u32 ff = (u32)f; \
if(!is_emulator){ \
_func_ = GET_JUMP_TARGET(_lw(_func_)); \
LOG("real hardware mode, retargetting function 0x%lx\n", _func_); \
LOG("original instructions: 0x%lx 0x%lx\n", _lw(_func_), _lw(_func_ + 4)); \
} \
static u32 patch_buffer[3]; \
if(is_emulator){ \
_sw(_lw(_func_), (u32)patch_buffer); \
_sw(_lw(_func_ + 4), (u32)patch_buffer + 4); \
}else{ \
_sw(_lw(_func_), (u32)patch_buffer); \
_sw(_lw(_func_ + 4), (u32)patch_buffer + 8); \
MAKE_JUMP((u32)patch_buffer + 4, _func_ + 8); \
} \
_sw(0x08000000 | (((u32)(ff) >> 2) & 0x03FFFFFF), _func_); \
_sw(0, _func_ + 4); \
ptr = (void *)patch_buffer; \
if(is_emulator){ \
SceUID modules[32]; \
SceKernelModuleInfo info; \
int i, count = 0; \
if (sceKernelGetModuleIdList(modules, sizeof(modules), &count) >= 0) { \
for (i = 0; i < count; i++) { \
info.size = sizeof(SceKernelModuleInfo); \
if (sceKernelQueryModuleInfo(modules[i], &info) < 0) { \
continue; \
} \
if (strcmp(info.name, MODULE_NAME) == 0) { \
continue; \
} \
LOG("scanning module %s in ppsspp mode\n", info.name); \
LOG("info.text_addr: 0x%x info.text_size: 0x%x info.nsegment: 0x%x\n", info.text_addr, info.text_size, (int)info.nsegment); \
u32 j; \
for(j = 0;j < info.nsegment; j++){ \
LOG("info.segmentaddr[%ld]: 0x%x info.segmentsize[%ld]: 0x%x\n", j, info.segmentaddr[j], j, info.segmentsize[j]); \
} \
if(info.text_size == 0){ \
if(info.nsegment >= 1 && info.segmentaddr[0] == info.text_addr){ \
info.text_size = info.segmentsize[0]; \
} \
} \
u32 k; \
for(k = 0; k < info.text_size; k+=4){ \
u32 addr = k + info.text_addr; \
if(/*_lw((u32)pattern) == _lw(addr + 0) &&*/ _lw((u32)pattern + 4) == _lw(addr + 4)){ \
LOG("found instruction pattern 0x%lx 0x%lx at 0x%lx, patching\n", pattern[0], pattern[1], addr); \
_sw(0x08000000 | (((u32)(ff) >> 2) & 0x03FFFFFF), addr); \
_sw(0, addr + 4); \
} \
} \
} \
} \
} \
}

static int get_disc_id_version(char *id_out, char *version_out){
char *sfo_path = "disc0:/PSP_GAME/PARAM.SFO";
int fd = sceIoOpen(sfo_path, PSP_O_RDONLY,0);
Expand Down Expand Up @@ -421,12 +358,12 @@ static void sample_input(SceCtrlData *pad_data, int count, int negative){
if(camera_controls){
if(lyn > 0){
override_camera = 1;
camera_override = (float)(lyn * -1.5) / 127.0f;
camera_override = (float)(lyn * -1.5f) / 127.0f;
}

if(lyp > 0){
override_camera = 1;
camera_override = (float)(lyp * 1.5) / 127.0f;
camera_override = (float)(lyp * 1.5f) / 127.0f;
}
}

Expand Down Expand Up @@ -551,9 +488,12 @@ void populate_car_analog_control_patched(u32 param_1, int *param_2, unsigned cha
LOG("car control struct is at 0x%lx\n", (uint32_t)param_3);
}

populate_car_analog_control_orig(param_1, param_2, param_3, param_4, param_5, param_6);
//populate_car_analog_control_orig(param_1, param_2, param_3, param_4, param_5, param_6);

if(is_emulator){
int k1 = pspSdkSetK1(0);

//if(is_emulator){
if(1){
// attempt to fix save state on ppsspp, but might sample more future input than the game did on the frame this was called...
SceCtrlData pad_data;
int res = sceCtrlPeekBufferPositive(&pad_data, 1);
Expand All @@ -564,6 +504,7 @@ void populate_car_analog_control_patched(u32 param_1, int *param_2, unsigned cha
param_3[0] = param_3[0] | 1;
param_3[1] = param_3[1] | 2;
*steering = steering_override;
LOG("applying steering override, val is %d, steering is %d\n", override_steering, *steering);
}

if(override_accel){
Expand Down Expand Up @@ -610,14 +551,16 @@ void populate_car_analog_control_patched(u32 param_1, int *param_2, unsigned cha
if(override_camera){
*camera_rotation = camera_override;
}
}

int main_thread(SceSize args, void *argp){
LOG("main thread begins\n");
pspSdkSetK1(k1);
}

int init(){
/*
if(!is_emulator){
sceKernelDelayThread(1000 * 1000 * 5);
}
*/

char disc_id[50];
char disc_version[50];
Expand Down Expand Up @@ -658,17 +601,6 @@ int main_thread(SceSize args, void *argp){
//HIJACK_FUNCTION(offset_populate_car_digital_control, populate_car_digital_control_patched, populate_car_digital_control_orig);
HIJACK_FUNCTION(offset_populate_car_analog_control, populate_car_analog_control_patched, populate_car_analog_control_orig);

u32 sceCtrlReadBufferPositive_addr = (u32)sceCtrlReadBufferPositive;

if(sceCtrlReadBufferPositive_addr == 0){
LOG("sceCtrlReadBufferPositive_addr is 0, bailing out\n");
return 1;
}

if(!is_emulator){
HIJACK_SYSCALL_STUB(sceCtrlReadBufferPositive_addr, sceCtrlReadBufferPositivePatched, sceCtrlReadBufferPositiveOrig);
}

sceKernelDcacheWritebackAll();
sceKernelIcacheClearAll();

Expand All @@ -682,41 +614,15 @@ int main_thread(SceSize args, void *argp){
return 0;
}

void init(){
#if DEBUG
logfd = sceIoOpen("ms0:/PSP/"MODULE_NAME".log", PSP_O_WRONLY|PSP_O_CREAT|PSP_O_TRUNC, 0777);
if(logfd < 0){
logfd = sceIoOpen("ef0:/PSP/"MODULE_NAME".log", PSP_O_WRONLY|PSP_O_CREAT|PSP_O_TRUNC, 0777);
}
#endif

LOG("module started\n");
if(!is_emulator){
SceUID thid = sceKernelCreateThread(MODULE_NAME, main_thread, 0x18, 4*1024, 0, NULL);
if(thid < 0){
LOG("failed creating main thread\n")
return;
}
LOG("created thread with thid 0x%x\n", thid);
sceKernelStartThread(thid, 0, NULL);
LOG("main thread started\n");
}else{
// function hijacking has to be done on the module's assigned thread, or it'll break
// savestate loading, crashing the whole emulator
// this happens probably because patching on another ppsspp thread does not flush cache properly,
// when dynarec is also in the equation
main_thread(0, NULL);
}
}

int StartPSP(SceModule2 *mod) {
if(strcmp(mod->modname, GAME_MODULE_NAME) == 0){
game_base_addr = mod->text_addr;
init();
}
init();
}

if (!previous)
return 0;
if (!previous){
return 0;
}

return previous(mod);
}
Expand All @@ -741,13 +647,24 @@ static void StartPPSSPP() {
}
}

int module_start(SceSize args, void *argp) {
is_emulator = sceIoDevctl("kemulator:", EMULATOR_DEVCTL__IS_EMULATOR, NULL, 0, NULL, 0) == 0;
if (is_emulator) {
// Just scan the modules using normal/official syscalls.
StartPPSSPP();
} else {
previous = sctrlHENSetStartModuleHandler(StartPSP);
}
return 0;
int module_start(SceSize args, void *argp){
#if DEBUG_LOG
sceIoRemove("ms0:/PSP/"MODULE_NAME".log");
sceIoRemove("ef0:/PSP/"MODULE_NAME".log");
#endif
LOG("module started\n");

is_emulator = sceIoDevctl("kemulator:", EMULATOR_DEVCTL__IS_EMULATOR, NULL, 0, NULL, 0) == 0;

if (is_emulator) {
// Just scan the modules using normal/official syscalls.
StartPPSSPP();
}else{
previous = sctrlHENSetStartModuleHandler(StartPSP);
}
return 0;
}

int module_stop(SceSize args, void *argp){
return 0;
}
22 changes: 22 additions & 0 deletions start_shell_podman.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
IMAGE_NAME="pspsdk"

set -xe

if [ "$REBUILD_IMAGE" == "true" ] && podman image exists $IMAGE_NAME
then
podman image rm -f $IMAGE_NAME
fi

if ! podman image exists $IMAGE_NAME
then
podman image build -f Dockerfile -t $IMAGE_NAME
fi

mkdir -p workdir

podman run \
--rm -it \
-v /dev/bus/usb:/dev/bus/usb \
-v ./:/workdir \
-w /workdir \
$IMAGE_NAME

0 comments on commit a17ab0e

Please sign in to comment.