Files
ps5upload/payload/include/sdk_param.h
T
phantomptrandClaude Opus 5.5 c14782944d PS5Upload v5.35.0
Upload, install, convert and manage PS5 game packages over the network.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 04:31:13 -07:00

108 lines
4.0 KiB
C

#ifndef PS5UPLOAD_SDK_PARAM_H
#define PS5UPLOAD_SDK_PARAM_H
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
/* param.json version rewriting for the SDK Version Changer.
*
* A PS5 title records the firmware it demands in sce_sys/param.json as
* `requiredSystemSoftwareVersion`, and the SDK it was built against as
* `sdkVersion`. Lowering those is what lets a game built for newer
* firmware start on an older jailbroken console.
*
* Split out from sdk_changer.c so the rewrite is testable on the host.
* The previous in-place implementation could only replace a value when
* the replacement was byte-for-byte the same length, and silently
* changed nothing otherwise — while the caller still reported success.
* Rewriting into an output buffer removes that restriction and makes the
* changed-count observable.
*
* Tests: payload/tests/sdk_param_selftest.c. */
/* The literal param.json stores, e.g. 0x0505000000000000 for FW 5.05. */
static inline int sdk_param_format_version(uint32_t target_sdk, char *out,
size_t cap) {
if (!out || cap == 0) return -1;
int n = snprintf(out, cap, "0x%08x00000000", target_sdk);
if (n < 0 || (size_t)n >= cap) return -1;
return n;
}
/* Replace the string value of `field` in `in`, appending to `out`.
* Returns 1 if the field was found and rewritten, 0 if absent. */
static inline int sdk_param_replace_field(const char *in, size_t in_len,
const char *field, const char *value,
char *out, size_t out_cap,
size_t *out_len) {
char needle[64];
int nn = snprintf(needle, sizeof(needle), "\"%s\"", field);
if (nn < 0 || (size_t)nn >= sizeof(needle)) return 0;
const char *key = strstr(in, needle);
if (!key) return 0;
const char *colon = strchr(key, ':');
if (!colon) return 0;
const char *p = colon + 1;
while (*p == ' ' || *p == '\t') p++;
if (*p != '"') return 0;
p++; /* opening quote of the value */
const char *close = strchr(p, '"');
if (!close) return 0;
size_t head = (size_t)(p - in);
size_t tail_off = (size_t)(close - in);
size_t vlen = strlen(value);
size_t total = head + vlen + (in_len - tail_off);
if (total >= out_cap) return 0;
memcpy(out, in, head);
memcpy(out + head, value, vlen);
memcpy(out + head + vlen, in + tail_off, in_len - tail_off);
if (out_len) *out_len = total;
return 1;
}
/* Rewrite both version fields. Returns how many were actually changed —
* 0 means the document did not contain them, which the caller must
* report rather than claim success. */
static inline int sdk_param_rewrite_json(const char *in, size_t in_len,
uint32_t target_sdk, char *out,
size_t out_cap, size_t *out_len) {
if (!in || !out || out_cap == 0) return -1;
char value[32];
if (sdk_param_format_version(target_sdk, value, sizeof(value)) < 0) return -1;
static const char *const fields[] = {"sdkVersion",
"requiredSystemSoftwareVersion"};
/* Ping-pong between two buffers so each field rewrite sees the
* previous one's output. */
if (in_len >= out_cap) return -1;
memcpy(out, in, in_len);
out[in_len] = '\0';
size_t cur_len = in_len;
char scratch[8192];
int changed = 0;
for (size_t i = 0; i < sizeof(fields) / sizeof(fields[0]); i++) {
if (cur_len >= sizeof(scratch)) return changed;
size_t next_len = 0;
if (sdk_param_replace_field(out, cur_len, fields[i], value, scratch,
sizeof(scratch), &next_len)) {
if (next_len >= out_cap) return changed;
memcpy(out, scratch, next_len);
out[next_len] = '\0';
cur_len = next_len;
changed++;
}
}
if (out_len) *out_len = cur_len;
return changed;
}
#endif