Files
CyberMask367andSirHumza e35c47b3f5 upto 13.52 firmware and other fixes and changes
changed game detection logic, resolving retro games and homebrew, new rpc style, custom options in config.json, installer is not fixed yet

Co-Authored-By: SirHumza <204067870+SirHumza@users.noreply.github.com>
2026-10-06 03:06:28 +01:00

151 lines
6.5 KiB
C

/* tmdb_crypto.c - self-contained SHA1 + HMAC-SHA1 + Sony TMDB URL builder
* and response parser. No platform headers: host-unit-testable.
*
* Name/icon resolution idea follows bshar1865/zorua98741
* PS4-Rich-Presence-for-Discord (TMDB key/hash scheme, Tustin's hash
* construction): title metadata comes from Sony's own
* tmdb.np.dl.playstation.net service. Our implementation here (parser,
* transport, caching, presence wiring) is original. */
#include "tmdb_crypto.h"
#include "jsonlite.h"
#include <string.h>
#include <stdint.h>
#include <stdio.h>
/* ---- SHA1 (small, public-domain style) ---- */
typedef struct { uint32_t h[5]; uint64_t len; unsigned char buf[64]; size_t n; } sha1_t;
static uint32_t rol(uint32_t x, int n){ return (x<<n)|(x>>(32-n)); }
static void sha1_block(sha1_t *c, const unsigned char *p){
uint32_t w[80];
for(int i=0;i<16;i++) w[i]=(uint32_t)p[4*i]<<24|(uint32_t)p[4*i+1]<<16|(uint32_t)p[4*i+2]<<8|p[4*i+3];
for(int i=16;i<80;i++) w[i]=rol(w[i-3]^w[i-8]^w[i-14]^w[i-16],1);
uint32_t a=c->h[0],b=c->h[1],d=c->h[2],e=c->h[3],f=c->h[4];
for(int i=0;i<80;i++){
uint32_t t;
if(i<20) t=((b&d)|((~b)&e))+0x5A827999u;
else if(i<40) t=(b^d^e)+0x6ED9EBA1u;
else if(i<60) t=((b&d)|(b&e)|(d&e))+0x8F1BBCDCu;
else t=(b^d^e)+0xCA62C1D6u;
t+=rol(a,5)+f+w[i]; f=e; e=d; d=rol(b,30); b=a; a=t;
}
c->h[0]+=a; c->h[1]+=b; c->h[2]+=d; c->h[3]+=e; c->h[4]+=f;
}
static void sha1_init(sha1_t *c){
c->h[0]=0x67452301u; c->h[1]=0xEFCDAB89u; c->h[2]=0x98BADCFEu;
c->h[3]=0x10325476u; c->h[4]=0xC3D2E1F0u; c->len=0; c->n=0;
}
static void sha1_update(sha1_t *c, const unsigned char *p, size_t n){
c->len+=n;
while(n){
size_t t=64-c->n; if(t>n) t=n;
memcpy(c->buf+c->n,p,t); c->n+=t; p+=t; n-=t;
if(c->n==64){ sha1_block(c,c->buf); c->n=0; }
}
}
static void sha1_final(sha1_t *c, unsigned char out[20]){
uint64_t bits=c->len*8;
unsigned char z=0x80, zz=0x00;
sha1_update(c,&z,1);
while(c->n!=56) sha1_update(c,&zz,1);
unsigned char lb[8];
for(int i=0;i<8;i++) lb[i]=(unsigned char)(bits>>((7-i)*8));
memcpy(c->buf+c->n,lb,8); sha1_block(c,c->buf);
for(int i=0;i<5;i++){
out[4*i]=(unsigned char)(c->h[i]>>24); out[4*i+1]=(unsigned char)(c->h[i]>>16);
out[4*i+2]=(unsigned char)(c->h[i]>>8); out[4*i+3]=(unsigned char)c->h[i];
}
}
void tmdb_sha1(const unsigned char *p, size_t n, unsigned char out[20]){
sha1_t c; sha1_init(&c); sha1_update(&c,p,n); sha1_final(&c,out);
}
void tmdb_hmac_sha1(const unsigned char *key, size_t klen,
const unsigned char *msg, size_t mlen,
unsigned char out[20]){
unsigned char k[64];
if(klen>64){ tmdb_sha1(key,klen,k); memset(k+20,0,44); klen=20; }
else { memcpy(k,key,klen); memset(k+klen,0,64-klen); }
unsigned char ipad[64], opad[64];
for(int i=0;i<64;i++){ ipad[i]=k[i]^0x36; opad[i]=k[i]^0x5C; }
sha1_t c; unsigned char inner[20];
sha1_init(&c); sha1_update(&c,ipad,64); sha1_update(&c,msg,mlen); sha1_final(&c,inner);
sha1_init(&c); sha1_update(&c,opad,64); sha1_update(&c,inner,20); sha1_final(&c,out);
}
/* Sony TMDB service key (same key their own tooling uses). */
static const unsigned char tmdb_key[64] = {
0xF5,0xDE,0x66,0xD2,0x68,0x0E,0x25,0x5B,0x2D,0xF7,0x9E,0x74,0xF8,0x90,0xEB,0xF3,
0x49,0x26,0x2F,0x61,0x8B,0xCA,0xE2,0xA9,0xAC,0xCD,0xEE,0x51,0x56,0xCE,0x8D,0xF2,
0xCD,0xF2,0xD4,0x8C,0x71,0x17,0x3C,0xDC,0x25,0x94,0x46,0x5B,0x87,0x40,0x5D,0x19,
0x7C,0xF1,0xAE,0xD3,0xB7,0xE9,0x67,0x1E,0xEB,0x56,0xCA,0x67,0x53,0xC2,0xE6,0xB0
};
/* Path (no scheme/host): /tmdb2/<TID>_00_<UPPERHEX>/<TID>_00.json Const-size. */
int tmdb_path(const char *titleId, char *out, size_t cap){
static const char hex[] = "0123456789ABCDEF";
char tid[16];
size_t i = 0;
if(!titleId || !out || cap == 0) return -1;
while(titleId[i] && i < sizeof tid - 1){
char c = titleId[i];
if(!((c>='A'&&c<='Z')||(c>='0'&&c<='9'))) return -1;
tid[i] = c; i++;
}
tid[i] = 0;
if(i != 9) return -1;
char msg[16];
memcpy(msg, tid, 9); memcpy(msg+9, "_00", 4); /* includes null */
unsigned char mac[20];
tmdb_hmac_sha1(tmdb_key, sizeof tmdb_key, (unsigned char*)msg, 12, mac);
/* need: /tmdb2/XXXXXXXXX_00_40HEX/XXXXXXXXX_00.json = 7+9+3+40+1+9+5+1 */
if(cap < 80) return -1;
int n = snprintf(out, cap, "/tmdb2/%s_00_", tid);
for(int k = 0; k < 20; k++){ out[n++] = hex[mac[k]>>4]; out[n++] = hex[mac[k]&15]; }
n += snprintf(out+n, cap-(size_t)n, "/%s_00.json", tid);
(void)n;
return 0;
}
/* Parse a TMDB JSON body: names[0].name + icons[0].icon. */
int tmdb_parse(const char *body, size_t len,
char *name, size_t name_cap, char *icon, size_t icon_cap){
if(!body || !name || name_cap == 0) return -1;
name[0] = 0; if(icon && icon_cap) icon[0] = 0;
jl_val_t *r = jl_parse(body, len);
if(!r) return -1;
int rc = -1;
const jl_val_t *names = jl_obj_get(r, "names");
const jl_val_t *n0 = (names && names->type == JL_ARRAY) ? jl_arr_at(names, 0) : NULL;
const jl_val_t *nm = n0 ? jl_obj_get(n0, "name") : NULL;
if(nm && nm->type == JL_STRING && nm->str[0]){
strncpy(name, nm->str, name_cap-1); name[name_cap-1] = 0;
rc = 0;
if(icon && icon_cap){
const jl_val_t *icons = jl_obj_get(r, "icons");
const jl_val_t *i0 = (icons && icons->type == JL_ARRAY) ? jl_arr_at(icons, 0) : NULL;
const jl_val_t *iu = i0 ? jl_obj_get(i0, "icon") : NULL;
if(iu && iu->type == JL_STRING && iu->str[0]){
/* Sony's icon CDN still answers in http on some titles.
* Discord silently drops an activity whose artwork is a
* non-https external URL -- the tile just shows "?" -- so
* upgrade the scheme rather than dropping the asset. */
const char *src = iu->str;
char up[256];
if(!strncmp(src, "http://", 7)){
size_t n = strlen(src + 7);
if(n + 8 <= sizeof up){
memcpy(up, "https://", 8);
memcpy(up + 8, src + 7, n + 1);
src = up;
}
}
/* only http(s) URLs are usable as external assets */
if(!strncmp(src, "http://", 7) || !strncmp(src, "https://", 8)){
strncpy(icon, src, icon_cap-1); icon[icon_cap-1] = 0;
}
}
}
}
jl_free(r);
return rc;
}