skinpeel.c (16304B)
1 #include <psp2/gxm.h> 2 #include <psp2/io/dirent.h> 3 #include <psp2/io/fcntl.h> 4 #include <psp2/kernel/clib.h> 5 #include <psp2/kernel/modulemgr.h> 6 #include <psp2/kernel/rng.h> 7 #include <psp2/kernel/sysmem.h> 8 #include <stdbool.h> 9 #include <taihen.h> 10 11 #include "common.h" 12 13 #define LOG 0 14 #define TAI_SUCCESS 0 15 16 #define WIDTH 1024 17 #define HEIGHT 512 18 19 int sceClibMemcmp(const void*, const void*, size_t); 20 inline int memcmp(const void* a, const void* b, size_t n) 21 { return sceClibMemcmp(a, b, n); } 22 inline void* memcpy(void* d, const void* s, size_t n) 23 { return sceClibMemcpy(d, s, n); } 24 inline void* memset(void* p, int c, size_t n) 25 { return sceClibMemset(p, c, n); } 26 27 inline int strcasecmp(const char* a, const char* b) 28 { return sceClibStrncasecmp(a, b, 0xffffffff); } 29 inline char* strcat(char* a, const char* b) 30 { return sceClibStrncat(a, b, 0xffffffff); } 31 inline int strcmp(const char* a, const char* b) 32 { return sceClibStrcmp(a, b); } 33 inline void* strcpy(void* d, const void* s) 34 { return sceClibMemcpy(d, s, __builtin_strlen(s)+1); } 35 inline size_t strlen(const char* s) 36 { return sceClibStrnlen(s, 0xffffffff); } 37 inline int strncasecmp(const char* a, const char* b, size_t n) 38 { return sceClibStrncasecmp(a, b, n); } 39 40 #define snprintf sceClibSnprintf 41 42 #define memcmp __builtin_memcmp 43 #define memcpy __builtin_memcpy 44 #define memset __builtin_memset 45 #define strcasecmp __builtin_strcasecmp 46 #define strcat __builtin_strcat 47 #define strcmp __builtin_strcmp 48 #define strcpy __builtin_strcpy 49 #define strlen __builtin_strlen 50 #define strncasecmp __builtin_strncasecmp 51 52 void* malloc(size_t s) 53 { 54 s += 8; 55 SceUID block = sceKernelAllocMemBlock( 56 "skinpeel_malloc", SCE_KERNEL_MEMBLOCK_TYPE_USER_RW, 57 (s + 4095) & ~4095, NULL); 58 if (block < 0) return NULL; 59 60 void* ptr; 61 if (sceKernelGetMemBlockBase(block, &ptr) < 0) 62 { sceKernelFreeMemBlock(block); return NULL; } 63 64 *(SceUID*) ptr = block; 65 return ((char*) ptr) + 8; 66 } 67 68 void free(void* p) 69 { 70 if (p == NULL) return; 71 SceUID block = *(SceUID*) (((char*) p) - 8); 72 sceKernelFreeMemBlock(block); 73 } 74 75 #if LOG 76 static SceUID log_fd = -1; 77 78 static void logr(const char* msg) 79 { if (log_fd >= 0) sceIoWrite(log_fd, msg, strlen(msg)); } 80 81 #define logf(...) \ 82 do \ 83 if (log_fd >= 0) \ 84 { \ 85 char logbuf[256]; \ 86 snprintf(logbuf, 256, __VA_ARGS__); \ 87 sceIoWrite(log_fd, logbuf, strlen(logbuf)); \ 88 } \ 89 while (0) 90 #else 91 static void logr(const char* msg) {} 92 #define logf(...) do ; while (0) 93 #endif 94 95 // https://prng.di.unimi.it/xoshiro128plusplus.c (public domain) 96 static inline uint32_t rotl(uint32_t x, int k) 97 { return (x << k) | (x >> (32 - k)); } 98 99 static uint32_t rand_state[4]; 100 static uint32_t rand(void) 101 { 102 const uint32_t result = rotl(rand_state[0] + rand_state[3], 7) + rand_state[0]; 103 104 const uint32_t t = rand_state[1] << 9; 105 106 rand_state[2] ^= rand_state[0]; 107 rand_state[3] ^= rand_state[1]; 108 rand_state[1] ^= rand_state[2]; 109 rand_state[0] ^= rand_state[3]; 110 111 rand_state[2] ^= t; 112 113 rand_state[3] = rotl(rand_state[3], 11); 114 115 return result; 116 } 117 118 static uint32_t rand_range(uint32_t mod) 119 { 120 if (mod < 2) return 0; 121 uint32_t div = (0xffffffff - (mod-1)) / mod + 1; 122 uint32_t g; 123 do 124 g = rand(); 125 while (g > div * mod - 1); 126 return g / div; 127 } 128 129 static int FUN_8100083c(unsigned short *param_1,int *out) 130 { 131 int res; 132 unsigned uVar1; 133 unsigned uVar2; 134 unsigned uVar3; 135 136 uVar3 = (unsigned)*param_1; 137 uVar1 = (unsigned)param_1[1]; 138 res = 0; 139 if (((uVar3 & 0xf800) == 0xf000) && ((uVar1 & 0xd001) == 0xc000)) { 140 uVar2 = uVar3 << 0x15; 141 *out = (int)param_1 + 142 (((int)((uVar1 & 0x7ff) << 8 | (~((uVar1 << 0x12 ^ uVar2) >> 0x1f) & 1) << 0x1e | 143 (~((uVar1 << 0x14 ^ uVar2) >> 0x1f) & 1) << 0x1d | 144 (uVar2 & 0x80000000) | (uVar3 & 0x3ff) << 0x13) >> 7) - ((unsigned)param_1 & 3)) + 4; 145 } 146 else { 147 res = -1; 148 } 149 return res; 150 } 151 152 static SceUID shell_modid; 153 static uintptr_t lockscreen_init_offset, wallpaper_scale_patch_offset, 154 dds_load_offset, scePafToplevelGetResourceTexture_offset; 155 static bool GetOffsets(void) 156 { 157 tai_module_info_t user_info; 158 user_info.size = sizeof(user_info); 159 if (taiGetModuleInfo("SceShell", &user_info) != TAI_SUCCESS) return false; 160 shell_modid = user_info.modid; 161 SceKernelModuleInfo kernel_info; 162 kernel_info.size = sizeof(kernel_info); 163 if (sceKernelGetModuleInfo(user_info.modid, &kernel_info) != 0) return false; 164 165 logf("nid %x\n", user_info.module_nid); 166 switch (user_info.module_nid) 167 { 168 /* case 0x0703c828: */ 169 /* case 0x12dac0f3: */ 170 /* case 0x2053b5a5: */ 171 /* case 0x34b4d82e: */ 172 /* case 0x5549bf1f: */ 173 /* lockscreen_init_offset = 0x23b85a; */ 174 /* wallpaper_scale_patch_offset = 0x23b6c8; */ 175 /* break; */ 176 177 case 0x552f692: // 3.60 178 lockscreen_init_offset = 0x23b7be; 179 wallpaper_scale_patch_offset = 0x23b62c; 180 dds_load_offset = 0x240208; 181 break; 182 183 /* case 0xeab89d5c: */ 184 /* lockscreen_init_offset = 0x233bf2; */ 185 /* wallpaper_scale_patch_offset = 0x233a60; */ 186 /* break; */ 187 188 /* case 0x587f9ced: */ 189 /* lockscreen_init_offset = 0x233c8e; */ 190 /* wallpaper_scale_patch_offset = 0x233afc; */ 191 /* break; */ 192 193 default: 194 return false; 195 } 196 197 int out; 198 void* ptr = (void*) ((uintptr_t) kernel_info.segments[0].vaddr + 199 lockscreen_init_offset + 0x13e); 200 if (FUN_8100083c(ptr, &out) != 0) return false; 201 202 scePafToplevelGetResourceTexture_offset = 203 out - (int) kernel_info.segments[0].vaddr; 204 return true; 205 } 206 207 static void destroy_mapped_block(SceUID block) 208 { 209 void* ptr; 210 if (sceKernelGetMemBlockBase(block, &ptr) >= 0) 211 sceGxmUnmapMemory(ptr); 212 sceKernelFreeMemBlock(block); 213 } 214 215 static size_t get_texture_size(SceGxmTexture* texture) 216 { 217 SceGxmTextureType type = sceGxmTextureGetType(texture); 218 if (type != SCE_GXM_TEXTURE_SWIZZLED && type != SCE_GXM_TEXTURE_LINEAR) 219 return 0; 220 221 SceGxmTextureBaseFormat base = sceGxmTextureGetFormat(texture) & 0xff000000; 222 switch (base) 223 { 224 case SCE_GXM_TEXTURE_BASE_FORMAT_U8: 225 case SCE_GXM_TEXTURE_BASE_FORMAT_S8: 226 case SCE_GXM_TEXTURE_BASE_FORMAT_P8: 227 return sceGxmTextureGetWidth(texture) * sceGxmTextureGetHeight(texture); 228 case SCE_GXM_TEXTURE_BASE_FORMAT_UBC1: 229 return 8 * ((sceGxmTextureGetWidth(texture) + 3) / 4) * 230 ((sceGxmTextureGetHeight(texture) + 3) / 4); 231 default: 232 return 0; 233 } 234 } 235 236 static bool load_dds( 237 const char* fname, SceGxmTexture* texture, SceUID* old_uid) 238 { 239 SceUID io = sceIoOpen(fname, SCE_O_RDONLY, 0); 240 logf("load io %x\n", io); 241 if (io < 0) return false; 242 #define DATA_SIZE (WIDTH * HEIGHT / 2) 243 #define DDS_SIZE (DATA_SIZE + sizeof(DDS)) 244 #define TEX_FORMAT (SCE_GXM_TEXTURE_BASE_FORMAT_UBC1 | SCE_GXM_TEXTURE_SWIZZLE4_1BGR) 245 246 SceUID block = -1; 247 void* ptr; 248 249 logf("check type %x %x, fmt %x %x, w %d %d, h %d %d; s %d >= %d\n", 250 sceGxmTextureGetType(texture), SCE_GXM_TEXTURE_SWIZZLED, 251 sceGxmTextureGetFormat(texture), TEX_FORMAT, 252 sceGxmTextureGetWidth(texture), WIDTH, 253 sceGxmTextureGetHeight(texture), HEIGHT, 254 get_texture_size(texture), DATA_SIZE); 255 256 if (get_texture_size(texture) >= DATA_SIZE) 257 { 258 logr("reusing old texture data...\n"); 259 ptr = sceGxmTextureGetData(texture); 260 } 261 else 262 { 263 logr("allocating new texture\n"); 264 block = sceKernelAllocMemBlock( 265 "skinpeel_texture", SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE, 266 (DATA_SIZE + 4095) & ~4095, NULL); 267 if (block < 0) { sceIoClose(io); return false; } 268 269 if (sceKernelGetMemBlockBase(block, &ptr) < 0) 270 { sceKernelFreeMemBlock(block); sceIoClose(io); return false; } 271 } 272 273 if (!strcasecmp(fname + strlen(fname) - 4, ".dds")) 274 { 275 DDS* buf = malloc(DDS_SIZE); 276 if (!buf) { sceKernelFreeMemBlock(block); sceIoClose(io); return false; } 277 278 int read = sceIoRead(io, buf, DDS_SIZE); 279 logf("load read %x %x\n", read, DDS_SIZE); 280 sceIoClose(io); 281 if (read != DDS_SIZE) 282 { sceKernelFreeMemBlock(block); free(buf); return false; } 283 284 if (memcmp(buf->magic, "DDS ", 4) || buf->width != WIDTH || 285 buf->height != HEIGHT || memcmp(buf->ddspf.four_cc, "DXT1", 4)) 286 { sceKernelFreeMemBlock(block); free(buf); return false; } 287 288 uint64_t* src = (uint64_t*) (buf+1); 289 uint64_t* dst = (uint64_t*) ptr; 290 291 #define BLOCKS_W (WIDTH/4) 292 #define BLOCKS_H (HEIGHT/4) 293 for (size_t i = 0; i < BLOCKS_W * BLOCKS_H; ++i) 294 { 295 size_t srcx, srcy; 296 _Static_assert(WIDTH == 1024 && HEIGHT == 512, "Update this shit"); 297 swizzle_1024_512(i, &srcx, &srcy); 298 memcpy(dst++, src + srcy*BLOCKS_W + srcx, 8); 299 } 300 301 free(buf); 302 } 303 else 304 { 305 int read = sceIoRead(io, ptr, DATA_SIZE); 306 logf("load read %x %x\n", read, DATA_SIZE); 307 sceIoClose(io); 308 if (read != DATA_SIZE) { sceKernelFreeMemBlock(block); return false; } 309 } 310 311 if (block < 0) 312 { 313 if (sceGxmTextureInitSwizzled( 314 texture, ptr, TEX_FORMAT, WIDTH, HEIGHT, 1) < 0) return false; 315 } 316 else 317 { 318 if (sceGxmMapMemory(ptr, (DATA_SIZE + 4095) & ~4095, 319 SCE_GXM_MEMORY_ATTRIB_READ) < 0) 320 { sceKernelFreeMemBlock(block); return false; } 321 322 logr("load init\n"); 323 if (sceGxmTextureInitSwizzled( 324 texture, ptr, TEX_FORMAT, WIDTH, HEIGHT, 1) < 0) 325 { sceGxmUnmapMemory(ptr); sceKernelFreeMemBlock(block); return false; } 326 327 if (*old_uid) destroy_mapped_block(*old_uid); 328 *old_uid = block; 329 } 330 331 logr("load success\n"); 332 return true; 333 #undef DATA_SIZE 334 #undef DDS_SIZE 335 } 336 337 static SceGxmTexture* fore_texture; 338 typedef struct 339 { 340 char pad[12]; 341 uint32_t field12_0xc; 342 } astruct; 343 344 typedef struct 345 { 346 char pad[0x44]; 347 SceGxmTexture** field68_0x44; 348 } astruct_2; 349 350 typedef struct 351 { 352 astruct_2* field0_0x0; 353 } astruct_1; 354 355 static SceUID old_back_block = -1, old_fore_block = -1; 356 357 static tai_hook_ref_t scePafToplevelGetResourceTexture_tai; 358 static uint32_t scePafToplevelGetResourceTexture_hook( 359 astruct_1* param_1, uint32_t param_2, astruct *param_3) 360 { 361 logf("Inner shit %p\n", param_2); 362 uint32_t res = TAI_CONTINUE(uint32_t, scePafToplevelGetResourceTexture_tai, 363 param_1, param_2, param_3); 364 365 if (param_3 == NULL || param_3->field12_0xc != 0xca707736) return res; 366 if (param_1 == NULL || param_1->field0_0x0 == NULL || 367 param_1->field0_0x0->field68_0x44 == NULL) return res; 368 369 logr("Found\n"); 370 SceGxmTexture* back_texture = *param_1->field0_0x0->field68_0x44; 371 372 char path[2*256 + 32]; 373 strcpy(path, "ur0:data/skinpeel/"); 374 char* dir_pos = path + strlen(path); 375 376 SceUID d = sceIoDopen("ur0:data/skinpeel"); 377 if (d < 0) return res; 378 379 uint32_t n = 0; 380 while (true) 381 { 382 SceIoDirent dent; 383 int dres = sceIoDread(d, &dent); 384 if (dres < 0) { sceIoDclose(d); return res; } 385 if (dres == 0) break; 386 logf("entry %d %s\n", dent.d_stat.st_mode, dent.d_name); 387 if (!(dent.d_stat.st_mode & SCE_S_IFDIR)) continue; 388 if (rand_range(++n)) continue; 389 390 strcpy(dir_pos, dent.d_name); 391 } 392 sceIoDclose(d); 393 if (!n) return res; 394 395 strcat(path, "/"); 396 dir_pos = path + strlen(path); 397 d = sceIoDopen(path); 398 if (d < 0) return res; 399 400 uint32_t fg_n = 0, bg_n = 0; 401 char fg_name[256]; 402 while (true) 403 { 404 SceIoDirent dent; 405 int dres = sceIoDread(d, &dent); 406 if (dres < 0) { sceIoDclose(d); return res; } 407 if (dres == 0) break; 408 logf("entry %d %s\n", dent.d_stat.st_mode, dent.d_name); 409 if (!(dent.d_stat.st_mode & SCE_S_IFREG)) continue; 410 if (strlen(dent.d_name) < 6) continue; 411 if (strcasecmp(dent.d_name + strlen(dent.d_name) - 4, ".dds") && 412 strcasecmp(dent.d_name + strlen(dent.d_name) - 6, ".spraw")) 413 continue; 414 415 if (!strncasecmp(dent.d_name, "fg", 2) && !rand_range(++fg_n)) 416 strcpy(fg_name, dent.d_name); 417 if (!strncasecmp(dent.d_name, "bg", 2) && !rand_range(++bg_n)) 418 strcpy(dir_pos, dent.d_name); 419 } 420 sceIoDclose(d); 421 422 logf("fg %d bg %d\n", fg_n, bg_n); 423 if (bg_n) load_dds(path, back_texture, &old_back_block); 424 if (fg_n && fore_texture) 425 { 426 strcpy(dir_pos, fg_name); 427 load_dds(path, fore_texture, &old_fore_block); 428 } 429 430 logr("Done\n"); 431 return res; 432 } 433 434 // Paf structs from here: 435 // https://github.com/GrapheneCt/ScePaf-headers/blob/main/include/paf/graphics.h 436 typedef struct 437 { 438 SceGxmTexture* texture; 439 int32_t unk_04; 440 } PafGxmTexture; 441 442 typedef struct PafSurfaceBase 443 { 444 struct PafSurfaceBase* prev; 445 struct PafSurfaceBase* next; 446 int32_t refCount; 447 uint16_t width; 448 uint16_t height; 449 int32_t texType; // linear/swizzled/tiled 450 int32_t unk_14; 451 int32_t unk_18; 452 uint16_t widthAligned; 453 uint16_t heightAligned; 454 float xScale; 455 float yScale; 456 int32_t byteSize; 457 uint16_t width3; 458 uint16_t unk_3e; 459 int32_t unk_30; 460 PafGxmTexture *texture; 461 SceGxmTextureFilter magFilter; 462 SceGxmTextureFilter minFilter; 463 SceGxmTextureFilter mipFilter; 464 SceGxmTextureAddrMode uaddrMode; 465 SceGxmTextureAddrMode vaddrMode; 466 uint32_t lodBias; 467 int32_t unk_50; 468 int32_t unk_54; 469 int32_t unk_58; 470 void* gxmData; 471 } PafSurfaceBase; 472 473 typedef struct 474 { 475 void* vtbl; 476 int32_t tex_format; 477 int32_t unk0, unk1; 478 PafSurfaceBase base; 479 } PafSurface; 480 // ^^ until here 481 482 typedef struct 483 { 484 char unk0[0x1c]; 485 uint32_t tex_w; 486 uint32_t tex_h; 487 uint32_t unk1; 488 PafSurface* surf; 489 } dds_struct; 490 491 static tai_hook_ref_t dds_load_tai; 492 static int dds_load_hook(dds_struct* this, const char* fname) 493 { 494 logf("dds_hook %s\n", fname); 495 if (strcmp(fname, "ur0:shell/wallpaper/lock.dds") && 496 strcmp(fname, "vs0:data/internal/keylock/keylock.png")) 497 return TAI_CONTINUE(int, dds_load_tai, this, fname); 498 499 int res = TAI_CONTINUE(int, dds_load_tai, this, fname); 500 fore_texture = this->surf->base.texture->texture; 501 502 return res; 503 } 504 505 static tai_hook_ref_t lockscreen_init_tai; 506 static uint32_t lockscreen_init_hook(uint32_t param_1, uint32_t param_2) 507 { 508 logr("lockscreen_init_hook\n"); 509 SceUID hook_uid = -1; 510 hook_uid = taiHookFunctionOffsetForUser( 511 &scePafToplevelGetResourceTexture_tai, &(tai_offset_args_t) { 512 .size = sizeof(tai_offset_args_t), 513 .modid = shell_modid, 514 .segidx = 0, 515 .offset = scePafToplevelGetResourceTexture_offset, 516 .thumb = 0, 517 .source = scePafToplevelGetResourceTexture_hook 518 }); 519 520 uint8_t src[] = { 0x6e, 0xe0 }; 521 SceUID inject_uid = taiInjectDataForUser(&(tai_offset_args_t) { 522 .size = sizeof(tai_offset_args_t), 523 .modid = shell_modid, 524 .segidx = 0, 525 .offset = wallpaper_scale_patch_offset, 526 .source = src, .source_size = 2, 527 }); 528 529 uint32_t res = TAI_CONTINUE(uint32_t, lockscreen_init_tai, param_1, param_2); 530 531 if (inject_uid >= 0) taiInjectRelease(inject_uid); 532 if (hook_uid >= 0) 533 taiHookRelease(hook_uid, scePafToplevelGetResourceTexture_tai); 534 535 return res; 536 } 537 538 static SceUID lockscreen_init_uid, dds_load_uid; 539 int _start() __attribute__((weak, alias("module_start"))); 540 int module_start(SceSize argc, const void* args) 541 { 542 #if LOG 543 log_fd = sceIoOpen("ur0:data/skinpeel.log", 544 SCE_O_WRONLY | SCE_O_CREAT | SCE_O_TRUNC, 0666); 545 #endif 546 logr("Log init\n"); 547 548 if (!GetOffsets()) return SCE_KERNEL_START_FAILED; 549 550 if (sceKernelGetRandomNumber(&rand_state, sizeof(rand_state)) < 0) 551 return SCE_KERNEL_START_FAILED; 552 553 lockscreen_init_uid = taiHookFunctionOffsetForUser( 554 &lockscreen_init_tai, &(tai_offset_args_t) { 555 .size = sizeof(tai_offset_args_t), 556 .modid = shell_modid, 557 .segidx = 0, 558 .offset = lockscreen_init_offset, 559 .thumb = 1, 560 .source = lockscreen_init_hook, 561 }); 562 if (lockscreen_init_uid < 0) return SCE_KERNEL_START_FAILED; 563 564 dds_load_uid = taiHookFunctionOffsetForUser( 565 &dds_load_tai, &(tai_offset_args_t) { 566 .size = sizeof(tai_offset_args_t), 567 .modid = shell_modid, 568 .segidx = 0, 569 .offset = dds_load_offset, 570 .thumb = 1, 571 .source = dds_load_hook 572 }); 573 if (dds_load_uid < 0) return SCE_KERNEL_START_FAILED; 574 575 logr("Init success\n"); 576 return SCE_KERNEL_START_SUCCESS; 577 } 578 579 int module_stop(SceSize argc, const void* args) 580 { 581 taiHookRelease(lockscreen_init_uid, lockscreen_init_tai); 582 taiHookRelease(dds_load_uid, dds_load_tai); 583 #if LOG 584 sceIoClose(log_fd); 585 #endif 586 return SCE_KERNEL_STOP_SUCCESS; 587 }