skinpeel

Change vita's lockscreen foreground and background randomly
git clone https://git.neptards.moe/neptards/skinpeel.git
Log | Files | Refs | README | LICENSE

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 }