You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
588 lines
16 KiB
C
588 lines
16 KiB
C
#include <psp2/gxm.h>
|
|
#include <psp2/io/dirent.h>
|
|
#include <psp2/io/fcntl.h>
|
|
#include <psp2/kernel/clib.h>
|
|
#include <psp2/kernel/modulemgr.h>
|
|
#include <psp2/kernel/rng.h>
|
|
#include <psp2/kernel/sysmem.h>
|
|
#include <stdbool.h>
|
|
#include <taihen.h>
|
|
|
|
#include "common.h"
|
|
|
|
#define LOG 0
|
|
#define TAI_SUCCESS 0
|
|
|
|
#define WIDTH 1024
|
|
#define HEIGHT 512
|
|
|
|
int sceClibMemcmp(const void*, const void*, size_t);
|
|
inline int memcmp(const void* a, const void* b, size_t n)
|
|
{ return sceClibMemcmp(a, b, n); }
|
|
inline void* memcpy(void* d, const void* s, size_t n)
|
|
{ return sceClibMemcpy(d, s, n); }
|
|
inline void* memset(void* p, int c, size_t n)
|
|
{ return sceClibMemset(p, c, n); }
|
|
|
|
inline int strcasecmp(const char* a, const char* b)
|
|
{ return sceClibStrncasecmp(a, b, 0xffffffff); }
|
|
inline char* strcat(char* a, const char* b)
|
|
{ return sceClibStrncat(a, b, 0xffffffff); }
|
|
inline int strcmp(const char* a, const char* b)
|
|
{ return sceClibStrcmp(a, b); }
|
|
inline void* strcpy(void* d, const void* s)
|
|
{ return sceClibMemcpy(d, s, __builtin_strlen(s)+1); }
|
|
inline size_t strlen(const char* s)
|
|
{ return sceClibStrnlen(s, 0xffffffff); }
|
|
inline int strncasecmp(const char* a, const char* b, size_t n)
|
|
{ return sceClibStrncasecmp(a, b, n); }
|
|
|
|
#define snprintf sceClibSnprintf
|
|
|
|
#define memcmp __builtin_memcmp
|
|
#define memcpy __builtin_memcpy
|
|
#define memset __builtin_memset
|
|
#define strcasecmp __builtin_strcasecmp
|
|
#define strcat __builtin_strcat
|
|
#define strcmp __builtin_strcmp
|
|
#define strcpy __builtin_strcpy
|
|
#define strlen __builtin_strlen
|
|
#define strncasecmp __builtin_strncasecmp
|
|
|
|
void* malloc(size_t s)
|
|
{
|
|
s += 8;
|
|
SceUID block = sceKernelAllocMemBlock(
|
|
"skinpeel_malloc", SCE_KERNEL_MEMBLOCK_TYPE_USER_RW,
|
|
(s + 4095) & ~4095, NULL);
|
|
if (block < 0) return NULL;
|
|
|
|
void* ptr;
|
|
if (sceKernelGetMemBlockBase(block, &ptr) < 0)
|
|
{ sceKernelFreeMemBlock(block); return NULL; }
|
|
|
|
*(SceUID*) ptr = block;
|
|
return ((char*) ptr) + 8;
|
|
}
|
|
|
|
void free(void* p)
|
|
{
|
|
if (p == NULL) return;
|
|
SceUID block = *(SceUID*) (((char*) p) - 8);
|
|
sceKernelFreeMemBlock(block);
|
|
}
|
|
|
|
#if LOG
|
|
static SceUID log_fd = -1;
|
|
|
|
static void logr(const char* msg)
|
|
{ if (log_fd >= 0) sceIoWrite(log_fd, msg, strlen(msg)); }
|
|
|
|
#define logf(...) \
|
|
do \
|
|
if (log_fd >= 0) \
|
|
{ \
|
|
char logbuf[256]; \
|
|
snprintf(logbuf, 256, __VA_ARGS__); \
|
|
sceIoWrite(log_fd, logbuf, strlen(logbuf)); \
|
|
} \
|
|
while (0)
|
|
#else
|
|
static void logr(const char* msg) {}
|
|
#define logf(...) do ; while (0)
|
|
#endif
|
|
|
|
// https://prng.di.unimi.it/xoshiro128plusplus.c (public domain)
|
|
static inline uint32_t rotl(uint32_t x, int k)
|
|
{ return (x << k) | (x >> (32 - k)); }
|
|
|
|
static uint32_t rand_state[4];
|
|
static uint32_t rand(void)
|
|
{
|
|
const uint32_t result = rotl(rand_state[0] + rand_state[3], 7) + rand_state[0];
|
|
|
|
const uint32_t t = rand_state[1] << 9;
|
|
|
|
rand_state[2] ^= rand_state[0];
|
|
rand_state[3] ^= rand_state[1];
|
|
rand_state[1] ^= rand_state[2];
|
|
rand_state[0] ^= rand_state[3];
|
|
|
|
rand_state[2] ^= t;
|
|
|
|
rand_state[3] = rotl(rand_state[3], 11);
|
|
|
|
return result;
|
|
}
|
|
|
|
static uint32_t rand_range(uint32_t mod)
|
|
{
|
|
if (mod < 2) return 0;
|
|
uint32_t div = (0xffffffff - (mod-1)) / mod + 1;
|
|
uint32_t g;
|
|
do
|
|
g = rand();
|
|
while (g > div * mod - 1);
|
|
return g / div;
|
|
}
|
|
|
|
static int FUN_8100083c(unsigned short *param_1,int *out)
|
|
{
|
|
int res;
|
|
unsigned uVar1;
|
|
unsigned uVar2;
|
|
unsigned uVar3;
|
|
|
|
uVar3 = (unsigned)*param_1;
|
|
uVar1 = (unsigned)param_1[1];
|
|
res = 0;
|
|
if (((uVar3 & 0xf800) == 0xf000) && ((uVar1 & 0xd001) == 0xc000)) {
|
|
uVar2 = uVar3 << 0x15;
|
|
*out = (int)param_1 +
|
|
(((int)((uVar1 & 0x7ff) << 8 | (~((uVar1 << 0x12 ^ uVar2) >> 0x1f) & 1) << 0x1e |
|
|
(~((uVar1 << 0x14 ^ uVar2) >> 0x1f) & 1) << 0x1d |
|
|
(uVar2 & 0x80000000) | (uVar3 & 0x3ff) << 0x13) >> 7) - ((unsigned)param_1 & 3)) + 4;
|
|
}
|
|
else {
|
|
res = -1;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static SceUID shell_modid;
|
|
static uintptr_t lockscreen_init_offset, wallpaper_scale_patch_offset,
|
|
dds_load_offset, scePafToplevelGetResourceTexture_offset;
|
|
static bool GetOffsets(void)
|
|
{
|
|
tai_module_info_t user_info;
|
|
user_info.size = sizeof(user_info);
|
|
if (taiGetModuleInfo("SceShell", &user_info) != TAI_SUCCESS) return false;
|
|
shell_modid = user_info.modid;
|
|
SceKernelModuleInfo kernel_info;
|
|
kernel_info.size = sizeof(kernel_info);
|
|
if (sceKernelGetModuleInfo(user_info.modid, &kernel_info) != 0) return false;
|
|
|
|
logf("nid %x\n", user_info.module_nid);
|
|
switch (user_info.module_nid)
|
|
{
|
|
/* case 0x0703c828: */
|
|
/* case 0x12dac0f3: */
|
|
/* case 0x2053b5a5: */
|
|
/* case 0x34b4d82e: */
|
|
/* case 0x5549bf1f: */
|
|
/* lockscreen_init_offset = 0x23b85a; */
|
|
/* wallpaper_scale_patch_offset = 0x23b6c8; */
|
|
/* break; */
|
|
|
|
case 0x552f692: // 3.60
|
|
lockscreen_init_offset = 0x23b7be;
|
|
wallpaper_scale_patch_offset = 0x23b62c;
|
|
dds_load_offset = 0x240208;
|
|
break;
|
|
|
|
/* case 0xeab89d5c: */
|
|
/* lockscreen_init_offset = 0x233bf2; */
|
|
/* wallpaper_scale_patch_offset = 0x233a60; */
|
|
/* break; */
|
|
|
|
/* case 0x587f9ced: */
|
|
/* lockscreen_init_offset = 0x233c8e; */
|
|
/* wallpaper_scale_patch_offset = 0x233afc; */
|
|
/* break; */
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
int out;
|
|
void* ptr = (void*) ((uintptr_t) kernel_info.segments[0].vaddr +
|
|
lockscreen_init_offset + 0x13e);
|
|
if (FUN_8100083c(ptr, &out) != 0) return false;
|
|
|
|
scePafToplevelGetResourceTexture_offset =
|
|
out - (int) kernel_info.segments[0].vaddr;
|
|
return true;
|
|
}
|
|
|
|
static void destroy_mapped_block(SceUID block)
|
|
{
|
|
void* ptr;
|
|
if (sceKernelGetMemBlockBase(block, &ptr) >= 0)
|
|
sceGxmUnmapMemory(ptr);
|
|
sceKernelFreeMemBlock(block);
|
|
}
|
|
|
|
static size_t get_texture_size(SceGxmTexture* texture)
|
|
{
|
|
SceGxmTextureType type = sceGxmTextureGetType(texture);
|
|
if (type != SCE_GXM_TEXTURE_SWIZZLED && type != SCE_GXM_TEXTURE_LINEAR)
|
|
return 0;
|
|
|
|
SceGxmTextureBaseFormat base = sceGxmTextureGetFormat(texture) & 0xff000000;
|
|
switch (base)
|
|
{
|
|
case SCE_GXM_TEXTURE_BASE_FORMAT_U8:
|
|
case SCE_GXM_TEXTURE_BASE_FORMAT_S8:
|
|
case SCE_GXM_TEXTURE_BASE_FORMAT_P8:
|
|
return sceGxmTextureGetWidth(texture) * sceGxmTextureGetHeight(texture);
|
|
case SCE_GXM_TEXTURE_BASE_FORMAT_UBC1:
|
|
return 8 * ((sceGxmTextureGetWidth(texture) + 3) / 4) *
|
|
((sceGxmTextureGetHeight(texture) + 3) / 4);
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static bool load_dds(
|
|
const char* fname, SceGxmTexture* texture, SceUID* old_uid)
|
|
{
|
|
SceUID io = sceIoOpen(fname, SCE_O_RDONLY, 0);
|
|
logf("load io %x\n", io);
|
|
if (io < 0) return false;
|
|
#define DATA_SIZE (WIDTH * HEIGHT / 2)
|
|
#define DDS_SIZE (DATA_SIZE + sizeof(DDS))
|
|
#define TEX_FORMAT (SCE_GXM_TEXTURE_BASE_FORMAT_UBC1 | SCE_GXM_TEXTURE_SWIZZLE4_1BGR)
|
|
|
|
SceUID block = -1;
|
|
void* ptr;
|
|
|
|
logf("check type %x %x, fmt %x %x, w %d %d, h %d %d; s %d >= %d\n",
|
|
sceGxmTextureGetType(texture), SCE_GXM_TEXTURE_SWIZZLED,
|
|
sceGxmTextureGetFormat(texture), TEX_FORMAT,
|
|
sceGxmTextureGetWidth(texture), WIDTH,
|
|
sceGxmTextureGetHeight(texture), HEIGHT,
|
|
get_texture_size(texture), DATA_SIZE);
|
|
|
|
if (get_texture_size(texture) >= DATA_SIZE)
|
|
{
|
|
logr("reusing old texture data...\n");
|
|
ptr = sceGxmTextureGetData(texture);
|
|
}
|
|
else
|
|
{
|
|
logr("allocating new texture\n");
|
|
block = sceKernelAllocMemBlock(
|
|
"skinpeel_texture", SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE,
|
|
(DATA_SIZE + 4095) & ~4095, NULL);
|
|
if (block < 0) { sceIoClose(io); return false; }
|
|
|
|
if (sceKernelGetMemBlockBase(block, &ptr) < 0)
|
|
{ sceKernelFreeMemBlock(block); sceIoClose(io); return false; }
|
|
}
|
|
|
|
if (!strcasecmp(fname + strlen(fname) - 4, ".dds"))
|
|
{
|
|
DDS* buf = malloc(DDS_SIZE);
|
|
if (!buf) { sceKernelFreeMemBlock(block); sceIoClose(io); return false; }
|
|
|
|
int read = sceIoRead(io, buf, DDS_SIZE);
|
|
logf("load read %x %x\n", read, DDS_SIZE);
|
|
sceIoClose(io);
|
|
if (read != DDS_SIZE)
|
|
{ sceKernelFreeMemBlock(block); free(buf); return false; }
|
|
|
|
if (memcmp(buf->magic, "DDS ", 4) || buf->width != WIDTH ||
|
|
buf->height != HEIGHT || memcmp(buf->ddspf.four_cc, "DXT1", 4))
|
|
{ sceKernelFreeMemBlock(block); free(buf); return false; }
|
|
|
|
uint64_t* src = (uint64_t*) (buf+1);
|
|
uint64_t* dst = (uint64_t*) ptr;
|
|
|
|
#define BLOCKS_W (WIDTH/4)
|
|
#define BLOCKS_H (HEIGHT/4)
|
|
for (size_t i = 0; i < BLOCKS_W * BLOCKS_H; ++i)
|
|
{
|
|
size_t srcx, srcy;
|
|
_Static_assert(WIDTH == 1024 && HEIGHT == 512, "Update this shit");
|
|
swizzle_1024_512(i, &srcx, &srcy);
|
|
memcpy(dst++, src + srcy*BLOCKS_W + srcx, 8);
|
|
}
|
|
|
|
free(buf);
|
|
}
|
|
else
|
|
{
|
|
int read = sceIoRead(io, ptr, DATA_SIZE);
|
|
logf("load read %x %x\n", read, DATA_SIZE);
|
|
sceIoClose(io);
|
|
if (read != DATA_SIZE) { sceKernelFreeMemBlock(block); return false; }
|
|
}
|
|
|
|
if (block < 0)
|
|
{
|
|
if (sceGxmTextureInitSwizzled(
|
|
texture, ptr, TEX_FORMAT, WIDTH, HEIGHT, 1) < 0) return false;
|
|
}
|
|
else
|
|
{
|
|
if (sceGxmMapMemory(ptr, (DATA_SIZE + 4095) & ~4095,
|
|
SCE_GXM_MEMORY_ATTRIB_READ) < 0)
|
|
{ sceKernelFreeMemBlock(block); return false; }
|
|
|
|
logr("load init\n");
|
|
if (sceGxmTextureInitSwizzled(
|
|
texture, ptr, TEX_FORMAT, WIDTH, HEIGHT, 1) < 0)
|
|
{ sceGxmUnmapMemory(ptr); sceKernelFreeMemBlock(block); return false; }
|
|
|
|
if (*old_uid) destroy_mapped_block(*old_uid);
|
|
*old_uid = block;
|
|
}
|
|
|
|
logr("load success\n");
|
|
return true;
|
|
#undef DATA_SIZE
|
|
#undef DDS_SIZE
|
|
}
|
|
|
|
static SceGxmTexture* fore_texture;
|
|
typedef struct
|
|
{
|
|
char pad[12];
|
|
uint32_t field12_0xc;
|
|
} astruct;
|
|
|
|
typedef struct
|
|
{
|
|
char pad[0x44];
|
|
SceGxmTexture** field68_0x44;
|
|
} astruct_2;
|
|
|
|
typedef struct
|
|
{
|
|
astruct_2* field0_0x0;
|
|
} astruct_1;
|
|
|
|
static SceUID old_back_block = -1, old_fore_block = -1;
|
|
|
|
static tai_hook_ref_t scePafToplevelGetResourceTexture_tai;
|
|
static uint32_t scePafToplevelGetResourceTexture_hook(
|
|
astruct_1* param_1, uint32_t param_2, astruct *param_3)
|
|
{
|
|
logf("Inner shit %p\n", param_2);
|
|
uint32_t res = TAI_CONTINUE(uint32_t, scePafToplevelGetResourceTexture_tai,
|
|
param_1, param_2, param_3);
|
|
|
|
if (param_3 == NULL || param_3->field12_0xc != 0xca707736) return res;
|
|
if (param_1 == NULL || param_1->field0_0x0 == NULL ||
|
|
param_1->field0_0x0->field68_0x44 == NULL) return res;
|
|
|
|
logr("Found\n");
|
|
SceGxmTexture* back_texture = *param_1->field0_0x0->field68_0x44;
|
|
|
|
char path[2*256 + 32];
|
|
strcpy(path, "ur0:data/skinpeel/");
|
|
char* dir_pos = path + strlen(path);
|
|
|
|
SceUID d = sceIoDopen("ur0:data/skinpeel");
|
|
if (d < 0) return res;
|
|
|
|
uint32_t n = 0;
|
|
while (true)
|
|
{
|
|
SceIoDirent dent;
|
|
int dres = sceIoDread(d, &dent);
|
|
if (dres < 0) { sceIoDclose(d); return res; }
|
|
if (dres == 0) break;
|
|
logf("entry %d %s\n", dent.d_stat.st_mode, dent.d_name);
|
|
if (!(dent.d_stat.st_mode & SCE_S_IFDIR)) continue;
|
|
if (rand_range(++n)) continue;
|
|
|
|
strcpy(dir_pos, dent.d_name);
|
|
}
|
|
sceIoDclose(d);
|
|
if (!n) return res;
|
|
|
|
strcat(path, "/");
|
|
dir_pos = path + strlen(path);
|
|
d = sceIoDopen(path);
|
|
if (d < 0) return res;
|
|
|
|
uint32_t fg_n = 0, bg_n = 0;
|
|
char fg_name[256];
|
|
while (true)
|
|
{
|
|
SceIoDirent dent;
|
|
int dres = sceIoDread(d, &dent);
|
|
if (dres < 0) { sceIoDclose(d); return res; }
|
|
if (dres == 0) break;
|
|
logf("entry %d %s\n", dent.d_stat.st_mode, dent.d_name);
|
|
if (!(dent.d_stat.st_mode & SCE_S_IFREG)) continue;
|
|
if (strlen(dent.d_name) < 6) continue;
|
|
if (strcasecmp(dent.d_name + strlen(dent.d_name) - 4, ".dds") &&
|
|
strcasecmp(dent.d_name + strlen(dent.d_name) - 6, ".spraw"))
|
|
continue;
|
|
|
|
if (!strncasecmp(dent.d_name, "fg", 2) && !rand_range(++fg_n))
|
|
strcpy(fg_name, dent.d_name);
|
|
if (!strncasecmp(dent.d_name, "bg", 2) && !rand_range(++bg_n))
|
|
strcpy(dir_pos, dent.d_name);
|
|
}
|
|
sceIoDclose(d);
|
|
|
|
logf("fg %d bg %d\n", fg_n, bg_n);
|
|
if (bg_n) load_dds(path, back_texture, &old_back_block);
|
|
if (fg_n && fore_texture)
|
|
{
|
|
strcpy(dir_pos, fg_name);
|
|
load_dds(path, fore_texture, &old_fore_block);
|
|
}
|
|
|
|
logr("Done\n");
|
|
return res;
|
|
}
|
|
|
|
// Paf structs from here:
|
|
// https://github.com/GrapheneCt/ScePaf-headers/blob/main/include/paf/graphics.h
|
|
typedef struct
|
|
{
|
|
SceGxmTexture* texture;
|
|
int32_t unk_04;
|
|
} PafGxmTexture;
|
|
|
|
typedef struct PafSurfaceBase
|
|
{
|
|
struct PafSurfaceBase* prev;
|
|
struct PafSurfaceBase* next;
|
|
int32_t refCount;
|
|
uint16_t width;
|
|
uint16_t height;
|
|
int32_t texType; // linear/swizzled/tiled
|
|
int32_t unk_14;
|
|
int32_t unk_18;
|
|
uint16_t widthAligned;
|
|
uint16_t heightAligned;
|
|
float xScale;
|
|
float yScale;
|
|
int32_t byteSize;
|
|
uint16_t width3;
|
|
uint16_t unk_3e;
|
|
int32_t unk_30;
|
|
PafGxmTexture *texture;
|
|
SceGxmTextureFilter magFilter;
|
|
SceGxmTextureFilter minFilter;
|
|
SceGxmTextureFilter mipFilter;
|
|
SceGxmTextureAddrMode uaddrMode;
|
|
SceGxmTextureAddrMode vaddrMode;
|
|
uint32_t lodBias;
|
|
int32_t unk_50;
|
|
int32_t unk_54;
|
|
int32_t unk_58;
|
|
void* gxmData;
|
|
} PafSurfaceBase;
|
|
|
|
typedef struct
|
|
{
|
|
void* vtbl;
|
|
int32_t tex_format;
|
|
int32_t unk0, unk1;
|
|
PafSurfaceBase base;
|
|
} PafSurface;
|
|
// ^^ until here
|
|
|
|
typedef struct
|
|
{
|
|
char unk0[0x1c];
|
|
uint32_t tex_w;
|
|
uint32_t tex_h;
|
|
uint32_t unk1;
|
|
PafSurface* surf;
|
|
} dds_struct;
|
|
|
|
static tai_hook_ref_t dds_load_tai;
|
|
static int dds_load_hook(dds_struct* this, const char* fname)
|
|
{
|
|
logf("dds_hook %s\n", fname);
|
|
if (strcmp(fname, "ur0:shell/wallpaper/lock.dds") &&
|
|
strcmp(fname, "vs0:data/internal/keylock/keylock.png"))
|
|
return TAI_CONTINUE(int, dds_load_tai, this, fname);
|
|
|
|
int res = TAI_CONTINUE(int, dds_load_tai, this, fname);
|
|
fore_texture = this->surf->base.texture->texture;
|
|
|
|
return res;
|
|
}
|
|
|
|
static tai_hook_ref_t lockscreen_init_tai;
|
|
static uint32_t lockscreen_init_hook(uint32_t param_1, uint32_t param_2)
|
|
{
|
|
logr("lockscreen_init_hook\n");
|
|
SceUID hook_uid = -1;
|
|
hook_uid = taiHookFunctionOffsetForUser(
|
|
&scePafToplevelGetResourceTexture_tai, &(tai_offset_args_t) {
|
|
.size = sizeof(tai_offset_args_t),
|
|
.modid = shell_modid,
|
|
.segidx = 0,
|
|
.offset = scePafToplevelGetResourceTexture_offset,
|
|
.thumb = 0,
|
|
.source = scePafToplevelGetResourceTexture_hook
|
|
});
|
|
|
|
uint8_t src[] = { 0x6e, 0xe0 };
|
|
SceUID inject_uid = taiInjectDataForUser(&(tai_offset_args_t) {
|
|
.size = sizeof(tai_offset_args_t),
|
|
.modid = shell_modid,
|
|
.segidx = 0,
|
|
.offset = wallpaper_scale_patch_offset,
|
|
.source = src, .source_size = 2,
|
|
});
|
|
|
|
uint32_t res = TAI_CONTINUE(uint32_t, lockscreen_init_tai, param_1, param_2);
|
|
|
|
if (inject_uid >= 0) taiInjectRelease(inject_uid);
|
|
if (hook_uid >= 0)
|
|
taiHookRelease(hook_uid, scePafToplevelGetResourceTexture_tai);
|
|
|
|
return res;
|
|
}
|
|
|
|
static SceUID lockscreen_init_uid, dds_load_uid;
|
|
int _start() __attribute__((weak, alias("module_start")));
|
|
int module_start(SceSize argc, const void* args)
|
|
{
|
|
#if LOG
|
|
log_fd = sceIoOpen("ur0:data/skinpeel.log",
|
|
SCE_O_WRONLY | SCE_O_CREAT | SCE_O_TRUNC, 0666);
|
|
#endif
|
|
logr("Log init\n");
|
|
|
|
if (!GetOffsets()) return SCE_KERNEL_START_FAILED;
|
|
|
|
if (sceKernelGetRandomNumber(&rand_state, sizeof(rand_state)) < 0)
|
|
return SCE_KERNEL_START_FAILED;
|
|
|
|
lockscreen_init_uid = taiHookFunctionOffsetForUser(
|
|
&lockscreen_init_tai, &(tai_offset_args_t) {
|
|
.size = sizeof(tai_offset_args_t),
|
|
.modid = shell_modid,
|
|
.segidx = 0,
|
|
.offset = lockscreen_init_offset,
|
|
.thumb = 1,
|
|
.source = lockscreen_init_hook,
|
|
});
|
|
if (lockscreen_init_uid < 0) return SCE_KERNEL_START_FAILED;
|
|
|
|
dds_load_uid = taiHookFunctionOffsetForUser(
|
|
&dds_load_tai, &(tai_offset_args_t) {
|
|
.size = sizeof(tai_offset_args_t),
|
|
.modid = shell_modid,
|
|
.segidx = 0,
|
|
.offset = dds_load_offset,
|
|
.thumb = 1,
|
|
.source = dds_load_hook
|
|
});
|
|
if (dds_load_uid < 0) return SCE_KERNEL_START_FAILED;
|
|
|
|
logr("Init success\n");
|
|
return SCE_KERNEL_START_SUCCESS;
|
|
}
|
|
|
|
int module_stop(SceSize argc, const void* args)
|
|
{
|
|
taiHookRelease(lockscreen_init_uid, lockscreen_init_tai);
|
|
taiHookRelease(dds_load_uid, dds_load_tai);
|
|
#if LOG
|
|
sceIoClose(log_fd);
|
|
#endif
|
|
return SCE_KERNEL_STOP_SUCCESS;
|
|
}
|