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

#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;
}