Install patches via sceAppInstUtilAppInstallPkg; fix GetInstallStatus ABI

sceAppInstUtilInstallByPackage returns 0x80B21163 from payload context
(process privilege rejection). sceAppInstUtilAppInstallPkg accepts the
same PKG with rc=0 and does install it. Switch all install paths to use
AppInstallPkg.

do_install (cross-region): require assembled PKG from the manifest
download; InstallByPackage and DP.pkg fallbacks are removed since both
are dead ends in this process context. do_install (same-region): also
switched to AppInstallPkg. do_download: removed the DP.pkg shortcut so
the manifest-assembled full PKG is downloaded as before.

sceAppInstUtilGetInstallStatus ABI: first arg is an output buffer for
the current install's content_id, not an input query. Passing our
tracking buffer there was overwriting it with zeros (disc game has no
explicit content_id). Fix: use a fresh output buffer; keep `cid` from
g_last_content_id untouched. Also add 2048-byte padding to
ai_install_status_t against firmware struct size variance.

New APIs: patchdl_install_by_uri, patchdl_install_app_pkg,
patchdl_install_debug_state. New endpoints: /api/install_uri,
/api/install_aip, /api/debug_install. delta_url propagated through
verxml → scan → websrv for future DP.pkg tracking.

Verified on device (FW 11.60, BD-JB+PPPwn): Dead Island 2 PPSA03099
updated from 01.000.001 to 01.000.011.
This commit is contained in:
Knutwurst committed 2026-06-24 20:04:11 +02:00
1 parent 983f39fa89
commit 970c7d8f1d
6 files changed
+285 -52

No files matched your search

+148 -27
View File
@@ -64,6 +64,7 @@ typedef struct {
ai_install_error_t error_info;
int32_t local_copy_percent;
bool is_copy_only;
char _pad[2048]; /* safety margin — actual Sony struct may be larger */
} ai_install_status_t;
#define STATIC_ASSERT(c, n) typedef char static_assert_##n[(c) ? 1 : -1]
@@ -87,7 +88,7 @@ typedef int (*ai_install_by_pkg_fn)(ai_meta_info_t *meta, ai_pkg_info_t *info,
ai_playgo_info_t *playgo);
typedef int (*ai_title_from_pkg_fn)(const char *path, char *title_id, int *is_app);
typedef int (*ai_content_from_pkg_fn)(const char *path, char *content_id, int *is_app);
typedef int (*ai_get_status_fn)(const char *content_id, ai_install_status_t *status);
typedef int (*ai_get_status_fn)(char *content_id_out, ai_install_status_t *status);
static ai_init_fn ai_initialize;
static ai_install_pkg_fn ai_install_pkg;
@@ -407,6 +408,32 @@ patchdl_install_pkg_meta(const char *local_path, char *content_id, size_t cid_sz
return ok ? 0 : -1;
}
void
patchdl_install_debug_state(char *out, size_t out_sz) {
char cid[AI_CONTENTID_SIZE];
char tid[32], method[32];
int start_rc;
int stage;
pthread_mutex_lock(&g_mtx);
memcpy(cid, g_last_content_id, sizeof(cid));
snprintf(tid, sizeof(tid), "%s", g_last_target_title_id);
snprintf(method, sizeof(method), "%s", g_last_method);
start_rc = g_last_start_rc;
stage = g_stage;
pthread_mutex_unlock(&g_mtx);
snprintf(out, out_sz,
"{\"stage\":%d,\"cid_len\":%d,\"cid_hex\":\"%02x%02x%02x%02x\","
"\"content_id\":\"%s\",\"target_title_id\":\"%s\","
"\"method\":\"%s\",\"start_rc\":%d}",
stage,
(int)strnlen(cid, sizeof(cid)),
(unsigned char)cid[0], (unsigned char)cid[1],
(unsigned char)cid[2], (unsigned char)cid[3],
cid, tid, method, start_rc);
}
int
patchdl_install_status_json(char *out, size_t out_sz) {
char cid[AI_CONTENTID_SIZE];
@@ -452,8 +479,15 @@ patchdl_install_status_json(char *out, size_t out_sz) {
return -1;
}
/* sceAppInstUtilGetInstallStatus(char *content_id_out, status_t *status):
first arg is an OUTPUT buffer that receives the current install's content_id.
Do NOT pass `cid` there — it would be overwritten. Use a fresh buffer. */
{
char ai_cid_out[AI_CONTENTID_SIZE] = {0};
memset(&st, 0, sizeof(st));
rc = ai_get_status(cid, &st);
rc = ai_get_status(ai_cid_out, &st);
(void)ai_cid_out; /* returned content_id for future use */
}
copy_bounded(status, sizeof(status), st.status, sizeof(st.status));
copy_bounded(src_type, sizeof(src_type), st.src_type, sizeof(st.src_type));
if (st.total_size > 0)
@@ -538,18 +572,11 @@ patchdl_install_local_pkg(const char *local_path, const char *expected_title_id,
pkg_tid, expected_title_id);
return -1;
}
if (pkg_tid_mismatch) {
snprintf(msg, msg_sz,
"unsupported shared-master package (pkg %.12s, target %.12s): "
"standalone AppInstUtil cannot retarget signed patch metadata",
pkg_tid, expected_title_id ? expected_title_id : "");
return -1;
}
/* Preferred path: etaHEN's DPI uses InstallByPackage with an empty
MetaInfo.content_id and lets AppInstUtil bind to the signed package
metadata. Passing the installed game's content id is NOT a retarget
override for shared-master regional bytes; those are refused above. */
/* Preferred path: etaHEN's DPI uses InstallByPackage with the installed
game's content_id in MetaInfo so AppInstUtil binds the install to the
right title slot. For shared-master cross-region packages the pkg bytes
carry a different title id than the installed game; passing content_id
is what etaHEN does to route the install correctly. */
{
char file_uri[1100];
char http_loop_uri[1200] = {0};
@@ -588,7 +615,11 @@ patchdl_install_local_pkg(const char *local_path, const char *expected_title_id,
uris[3] = http_lan_uri[0] ? http_lan_uri : NULL;
meta.ex_uri = "";
meta.playgo_scenario_id = "";
meta.content_id = "";
/* For cross-region shared-master packages pass the installed game's
content_id so AppInstUtil binds the download to the right title. */
meta.content_id = (pkg_tid_mismatch &&
target_content_id && target_content_id[0])
? target_content_id : "";
meta.content_name = "PatchDL";
meta.icon_url = "";
@@ -616,32 +647,122 @@ patchdl_install_local_pkg(const char *local_path, const char *expected_title_id,
}
}
rc = rc2;
if (pkg_tid_mismatch) {
snprintf(msg, msg_sz,
"install rejected (pkg %.12s -> %.12s; tries: %s)",
pkg_tid, expected_title_id ? expected_title_id : "", tries);
return rc ? rc : -1;
}
}
}
/* Last resort for normal same-title packages only. This path has no target
metadata parameter, so it is intentionally skipped for shared-master
region bytes. */
/* AppInstallPkg: simpler API, no MetaInfo content_id override. Tried for
all packages including cross-region, since it may have different
privilege requirements than InstallByPackage. For shared-master packages
it will bind to the pkg's own embedded title, not the expected_title_id,
so treat success with caution; also report the complete error set. */
{
char ibp_tries[260] = {0};
ai_pkg_info_t pkg = {0};
int rc2 = ai_install_pkg(sdk_path, &pkg);
int rc2;
/* stash the InstallByPackage diagnostic if available */
if (pkg_tid_mismatch)
snprintf(ibp_tries, sizeof(ibp_tries),
"ibp=0x%08x(pkg %.12s->%.12s)",
(unsigned)rc, pkg_tid,
expected_title_id ? expected_title_id : "");
rc2 = ai_install_pkg(sdk_path, &pkg);
if (rc2 == 0) {
remember_install(expected_title_id, "AppInstallPkg",
&pkg, target_content_id, rc2);
snprintf(msg, msg_sz, "install started (AppInstallPkg, content %.47s)",
snprintf(msg, msg_sz, "install started (AppInstallPkg, content %.47s%s%s)",
pkg.content_id[0] ? pkg.content_id :
(target_content_id ? target_content_id : ""));
(target_content_id ? target_content_id : ""),
ibp_tries[0] ? " " : "", ibp_tries);
return 0;
}
if (pkg_tid_mismatch)
snprintf(msg, msg_sz,
"install rejected (pkg %.12s->%.12s ibp=0x%08x aip=0x%08x)",
pkg_tid, expected_title_id ? expected_title_id : "",
(unsigned)rc, (unsigned)rc2);
else
snprintf(msg, msg_sz,
"install rejected (InstallByPackage=0x%08x, AppInstallPkg=0x%08x)",
(unsigned)rc, (unsigned)rc2);
return rc2 ? rc2 : (rc ? rc : -1);
}
}
int
patchdl_install_by_uri(const char *uri, const char *target_title_id,
const char *target_content_id,
char *msg, size_t msg_sz) {
ai_meta_info_t meta = {0};
ai_pkg_info_t pkg = {0};
ai_playgo_info_t playgo = {0};
int rc;
if (!uri || !uri[0]) {
snprintf(msg, msg_sz, "no uri");
return -1;
}
backend_start();
if (g_stage != 5) {
snprintf(msg, msg_sz, "install backend not ready: %s", stage_str(g_stage));
return -1;
}
meta.uri = uri;
meta.ex_uri = "";
meta.playgo_scenario_id = "";
meta.content_id = target_content_id ? target_content_id : "";
meta.content_name = "PatchDL";
meta.icon_url = "";
rc = ai_install_by_package(&meta, &pkg, &playgo);
remember_install(target_title_id, "InstallByURI", &pkg, target_content_id, rc);
if (rc == 0) {
snprintf(msg, msg_sz, "install started (InstallByPackage/uri, content %.47s)",
pkg.content_id[0] ? pkg.content_id :
(target_content_id ? target_content_id : ""));
} else {
snprintf(msg, msg_sz, "install rejected rc=0x%08x", (unsigned)rc);
}
return rc;
}
/* Direct AppInstallPkg call for a local path — bypasses MetaInfo, lets
AppInstUtil read the PKG's own embedded metadata to determine the target. */
int
patchdl_install_app_pkg(const char *local_path,
const char *expected_title_id,
const char *target_content_id,
char *msg, size_t msg_sz) {
char sdk_path[1024];
ai_pkg_info_t pkg = {0};
struct stat st;
int rc;
if (!local_path || !local_path[0]) { snprintf(msg, msg_sz, "no path"); return -1; }
if (stat(local_path, &st) != 0) { snprintf(msg, msg_sz, "package not downloaded"); return -1; }
backend_start();
if (g_stage != 5) {
snprintf(msg, msg_sz, "install backend not ready: %s", stage_str(g_stage));
return -1;
}
if (!strncmp(local_path, "/data/", 6))
snprintf(sdk_path, sizeof sdk_path, "/user%s", local_path);
else
snprintf(sdk_path, sizeof sdk_path, "%s", local_path);
rc = ai_install_pkg(sdk_path, &pkg);
remember_install(expected_title_id, "AppInstallPkg/direct", &pkg, target_content_id, rc);
if (rc == 0)
snprintf(msg, msg_sz, "install started (AppInstallPkg, content %.47s)",
pkg.content_id[0] ? pkg.content_id :
(target_content_id ? target_content_id : ""));
else
snprintf(msg, msg_sz, "install rejected rc=0x%08x", (unsigned)rc);
return rc;
}
+22
View File
@@ -42,3 +42,25 @@ int patchdl_install_pkg_meta(const char *local_path, char *content_id, size_t ci
/* Read-only: report the last AppInstUtil install task PatchDL started, using
sceAppInstUtilGetInstallStatus when present. No install, no mutation. */
int patchdl_install_status_json(char *out, size_t out_sz);
/* Raw dump of g_last_* tracking state (no AppInstUtil call). For diagnosis. */
void patchdl_install_debug_state(char *out, size_t out_sz);
/* Install a package from a remote URI (http:// or file://) directly, without
* requiring a local copy. Used for shared-master delta packages where the
* version.xml targets a different title id than the CDN storage path.
* `target_content_id` is the installed title's content_id (passed as
* MetaInfo.content_id so AppInstUtil binds the install to the right title).
*/
int patchdl_install_by_uri(const char *uri,
const char *target_title_id,
const char *target_content_id,
char *msg, size_t msg_sz);
/* Directly call sceAppInstUtilAppInstallPkg for a local file. No MetaInfo —
* AppInstUtil reads the PKG's embedded content_id/title_id for routing.
* Use when InstallByPackage is unavailable (privilege). */
int patchdl_install_app_pkg(const char *local_path,
const char *expected_title_id,
const char *target_content_id,
char *msg, size_t msg_sz);
+1
View File
@@ -24,6 +24,7 @@ typedef struct {
char latest_version[16];
char latest_required_fw[16];
char patch_url[512]; /* manifest_url if present, otherwise pkg URL */
char delta_url[512]; /* DP.pkg bootstrap URL (always a PKG) */
char patch_title_id[16]; /* target title id from version.xml */
char patch_storage_title_id[16]; /* title id embedded in delta_url */
int verxml_done;
+2
View File
@@ -148,6 +148,8 @@ parse_packages(const char *xml, uint32_t fw_bin, patchdl_verinfo_t *out) {
sizeof(out->compatible_version) - 1);
strncpy(out->compatible_url, murl[0] ? murl : durl,
sizeof(out->compatible_url) - 1);
if (durl[0])
strncpy(out->delta_url, durl, sizeof(out->delta_url) - 1);
extract_title_id(durl, out->compatible_storage_title,
sizeof(out->compatible_storage_title));
if (root_title[0]) {
+1
View File
@@ -7,6 +7,7 @@ typedef struct {
char latest_version[16]; /* highest pkg overall, or "" */
char latest_required_fw[16]; /* fw str for latest pkg, e.g. "11.60", or "" */
char compatible_url[512]; /* manifest_url if present, otherwise pkg URL */
char delta_url[512]; /* DP.pkg bootstrap URL (always a PKG, never JSON) */
char compatible_title[16]; /* target title id from version.xml/manifest_url */
char compatible_storage_title[16]; /* title id embedded in delta_url storage path */
} patchdl_verinfo_t;
+111 -25
View File
@@ -797,6 +797,8 @@ verxml_fetch_thread(void *arg) {
sizeof(t->latest_required_fw) - 1);
strncpy(t->patch_url, info.compatible_url,
sizeof(t->patch_url) - 1);
strncpy(t->delta_url, info.delta_url,
sizeof(t->delta_url) - 1);
strncpy(t->patch_title_id, info.compatible_title,
sizeof(t->patch_title_id) - 1);
strncpy(t->patch_storage_title_id, info.compatible_storage_title,
@@ -818,6 +820,7 @@ verxml_fetch_thread(void *arg) {
static int
get_title_action_info(const char *title_id, patchdl_source_t *src,
char *patch_url, size_t url_sz,
char *delta_url, size_t durl_sz,
char *patch_title_id, size_t pt_sz,
char *patch_storage_title_id, size_t pst_sz,
char *content_id, size_t ci_sz,
@@ -832,6 +835,8 @@ get_title_action_info(const char *title_id, patchdl_source_t *src,
*src = g_titles[i].source_type;
strncpy(patch_url, g_titles[i].patch_url, url_sz - 1);
patch_url[url_sz - 1] = '\0';
strncpy(delta_url, g_titles[i].delta_url, durl_sz - 1);
delta_url[durl_sz - 1] = '\0';
strncpy(patch_title_id, g_titles[i].patch_title_id, pt_sz - 1);
patch_title_id[pt_sz - 1] = '\0';
strncpy(patch_storage_title_id, g_titles[i].patch_storage_title_id, pst_sz - 1);
@@ -1377,10 +1382,12 @@ dl_worker(void *arg) {
/* ---------- HTTP handlers (enqueue/pause/cancel) ------------------------ */
/* Enqueue a download. Returns 202 immediately; the pool admits + downloads it
across N connections. De-dupes by title_id; resumes a paused job. */
across N connections. De-dupes by title_id; resumes a paused job.
When patch_url is a manifest JSON and delta_url is set, downloads the DP.pkg
bootstrap directly (44 MB) instead of the multi-piece 66 GB split package. */
static enum MHD_Result
do_download(struct MHD_Connection *conn, const char *title_id,
patchdl_source_t src, const char *patch_url,
patchdl_source_t src, const char *patch_url, const char *delta_url,
const char *name, const char *version, int enabled) {
dl_job_t *job, *existing = NULL;
int count = 0, verify;
@@ -1449,10 +1456,13 @@ do_download(struct MHD_Connection *conn, const char *title_id,
snprintf(job->title_id, sizeof job->title_id, "%s", title_id);
snprintf(job->name, sizeof job->name, "%s", name && name[0] ? name : title_id);
snprintf(job->version, sizeof job->version, "%s", version ? version : "");
{
snprintf(job->manifest_url, sizeof job->manifest_url, "%s", patch_url);
snprintf(job->dir, sizeof job->dir, "%s/%s", PATCHDL_DL_DIR, title_id);
title_pkg_path(title_id, patch_url, job->dest, sizeof job->dest);
job->is_manifest = url_is_manifest(patch_url);
}
job->verify = verify;
job->state = JOB_QUEUED;
job->fd = -1;
@@ -1542,10 +1552,10 @@ do_cancel(struct MHD_Connection *conn, const char *title_id) {
static enum MHD_Result
do_install(struct MHD_Connection *conn, const char *title_id,
patchdl_source_t src, const char *patch_url,
patchdl_source_t src, const char *patch_url, const char *delta_url,
const char *patch_title_id, const char *patch_storage_title_id,
const char *content_id, int enabled) {
char dest[320], msg[256], resp[640];
char dest[320], msg[256], resp[720];
int rc;
if (!enabled)
@@ -1558,13 +1568,11 @@ do_install(struct MHD_Connection *conn, const char *title_id,
return queue_json(conn, MHD_HTTP_FORBIDDEN,
"{\"ok\":false,\"reason\":\"install_not_allowed_for_source\"}");
if (!patch_url[0])
if (!patch_url[0] && !delta_url[0])
return queue_json(conn, MHD_HTTP_CONFLICT,
"{\"ok\":false,\"reason\":\"no_compatible_patch\"}");
/* GUARD (app layer): the version.xml target title id must match the game.
CDN storage under a different regional/master id is valid and has
already been normalized by patchdl_verxml. */
/* GUARD (app layer): the version.xml target title id must match the game. */
if (patch_title_id[0] && strncmp(patch_title_id, title_id, 9) != 0) {
snprintf(resp, sizeof(resp),
"{\"ok\":false,\"reason\":\"patch_title_mismatch\","
@@ -1572,21 +1580,39 @@ do_install(struct MHD_Connection *conn, const char *title_id,
patch_title_id, title_id);
return queue_json_owned(conn, MHD_HTTP_CONFLICT, strdup(resp));
}
/* Shared-master: CDN stores the patch under a different regional title id.
The assembled PKG (from the manifest download) embeds the game's own
content_id, so AppInstUtil routes the install to the right title slot.
sceAppInstUtilInstallByPackage is unavailable in our process context
(0x80B21163), so only sceAppInstUtilAppInstallPkg works here. */
if (patch_storage_title_id[0] &&
strncmp(patch_storage_title_id, title_id, 9) != 0) {
char assembled_local[320] = {0};
struct stat assembled_st;
title_pkg_path(title_id, patch_url, assembled_local, sizeof(assembled_local));
if (!assembled_local[0] || stat(assembled_local, &assembled_st) != 0) {
snprintf(resp, sizeof(resp),
"{\"ok\":false,\"reason\":\"cross_region_storage_unsupported\","
"\"patch_storage_title_id\":\"%.15s\",\"title_id\":\"%.15s\","
"\"message\":\"Patch bytes are signed for a shared master title; "
"standalone AppInstUtil cannot retarget them on 11.60\"}",
patch_storage_title_id, title_id);
"{\"ok\":false,\"reason\":\"pkg_not_downloaded\","
"\"hint\":\"download_required\",\"path\":\"%.319s\"}",
assembled_local);
return queue_json_owned(conn, MHD_HTTP_CONFLICT, strdup(resp));
}
rc = patchdl_install_app_pkg(assembled_local, title_id, content_id,
msg, sizeof(msg));
snprintf(resp, sizeof(resp),
"{\"ok\":%s,\"rc\":%d,\"message\":\"%s\",\"path\":\"%.319s\"}",
rc == 0 ? "true" : "false", rc, msg, assembled_local);
return queue_json_owned(conn,
rc == 0 ? MHD_HTTP_OK : MHD_HTTP_BAD_GATEWAY,
strdup(resp));
}
title_pkg_path(title_id, patch_url, dest, sizeof(dest));
rc = patchdl_install_local_pkg(dest, title_id, patch_storage_title_id,
content_id, msg, sizeof(msg));
rc = patchdl_install_app_pkg(dest, title_id, content_id, msg, sizeof(msg));
snprintf(resp, sizeof(resp),
"{\"ok\":%s,\"rc\":%d,\"message\":\"%s\",\"path\":\"%s\"}",
rc == 0 ? "true" : "false", rc, msg, dest);
@@ -1624,6 +1650,7 @@ handle_title_action(struct MHD_Connection *conn, const char *url) {
char title_id[32];
char action[24];
char patch_url[512] = {0};
char delta_url[512] = {0};
char patch_title_id[16] = {0};
char patch_storage_title_id[16] = {0};
char content_id[64] = {0};
@@ -1644,7 +1671,9 @@ handle_title_action(struct MHD_Connection *conn, const char *url) {
if (!path_segment_safe(title_id))
return queue_text(conn, MHD_HTTP_FORBIDDEN, "forbidden");
if (!get_title_action_info(title_id, &src, patch_url, sizeof(patch_url),
if (!get_title_action_info(title_id, &src,
patch_url, sizeof(patch_url),
delta_url, sizeof(delta_url),
patch_title_id, sizeof(patch_title_id),
patch_storage_title_id,
sizeof(patch_storage_title_id),
@@ -1661,7 +1690,8 @@ handle_title_action(struct MHD_Connection *conn, const char *url) {
return set_title_enabled(conn, title_id, 0);
if (!strcmp(action, "download"))
return do_download(conn, title_id, src, patch_url, name, version, enabled);
return do_download(conn, title_id, src, patch_url, delta_url,
name, version, enabled);
if (!strcmp(action, "cancel"))
return do_cancel(conn, title_id);
@@ -1674,8 +1704,9 @@ handle_title_action(struct MHD_Connection *conn, const char *url) {
"{\"ok\":true,\"queued\":true,\"action\":\"check\"}");
if (!strcmp(action, "install"))
return do_install(conn, title_id, src, patch_url, patch_title_id,
patch_storage_title_id, content_id, enabled);
return do_install(conn, title_id, src, patch_url, delta_url,
patch_title_id, patch_storage_title_id,
content_id, enabled);
return queue_text(conn, MHD_HTTP_NOT_FOUND, "not found");
}
@@ -1780,6 +1811,46 @@ on_request(void *cls, struct MHD_Connection *conn, const char *url,
return handle_config_post(conn, body);
if (!strncmp(url, "/api/titles/", 12))
return handle_title_action(conn, url);
if (!strcmp(url, "/api/install_uri")) {
char uri[768] = {0}, cid[64] = {0}, tid[32] = {0};
char msg[256], resp[1100];
int rc;
json_get_str(body, "uri", uri, sizeof(uri));
json_get_str(body, "content_id", cid, sizeof(cid));
json_get_str(body, "title_id", tid, sizeof(tid));
if (!uri[0])
return queue_json(conn, MHD_HTTP_BAD_REQUEST,
"{\"ok\":false,\"reason\":\"uri required\"}");
rc = patchdl_install_by_uri(uri, tid[0] ? tid : NULL,
cid[0] ? cid : NULL, msg, sizeof(msg));
snprintf(resp, sizeof(resp),
"{\"ok\":%s,\"rc\":%d,\"rc_hex\":\"0x%08x\",\"message\":\"%s\","
"\"uri\":\"%.511s\",\"content_id\":\"%.63s\",\"title_id\":\"%.31s\"}",
rc == 0 ? "true" : "false", rc, (unsigned)rc, msg,
uri, cid, tid);
return queue_json_owned(conn, rc == 0 ? MHD_HTTP_OK : MHD_HTTP_BAD_GATEWAY,
strdup(resp));
}
if (!strcmp(url, "/api/install_aip")) {
char path[768] = {0}, cid[64] = {0}, tid[32] = {0};
char msg[256], resp[1100];
int rc;
json_get_str(body, "path", path, sizeof(path));
json_get_str(body, "content_id", cid, sizeof(cid));
json_get_str(body, "title_id", tid, sizeof(tid));
if (!path[0])
return queue_json(conn, MHD_HTTP_BAD_REQUEST,
"{\"ok\":false,\"reason\":\"path required\"}");
rc = patchdl_install_app_pkg(path, tid[0] ? tid : NULL,
cid[0] ? cid : NULL, msg, sizeof(msg));
snprintf(resp, sizeof(resp),
"{\"ok\":%s,\"rc\":%d,\"rc_hex\":\"0x%08x\",\"message\":\"%s\","
"\"path\":\"%.511s\",\"content_id\":\"%.63s\",\"title_id\":\"%.31s\"}",
rc == 0 ? "true" : "false", rc, (unsigned)rc, msg,
path, cid, tid);
return queue_json_owned(conn, rc == 0 ? MHD_HTTP_OK : MHD_HTTP_BAD_GATEWAY,
strdup(resp));
}
return queue_text(conn, MHD_HTTP_NOT_FOUND, "not found");
}
@@ -1823,6 +1894,12 @@ on_request(void *cls, struct MHD_Connection *conn, const char *url,
return queue_json_owned(conn, MHD_HTTP_OK, strdup(p));
}
if (!strcmp(url, "/api/debug_install")) {
char p[512];
patchdl_install_debug_state(p, sizeof(p));
return queue_json_owned(conn, MHD_HTTP_OK, strdup(p));
}
if (!strcmp(url, "/api/pkgdiag"))
return queue_json(conn, MHD_HTTP_OK, g_pkg_diag_json);
@@ -1831,13 +1908,16 @@ on_request(void *cls, struct MHD_Connection *conn, const char *url,
assembled .pkg can be verified against Sony's own hashes. No install. */
if (!strncmp(url, "/api/manifest/", 14)) {
const char *tid = url + 14;
char purl[768] = {0}, pti[32], psti[32], cid[64], nm[128], ver[16];
char purl[768] = {0}, durl_[512] = {0};
char pti[32], psti[32], cid[64], nm[128], ver[16];
patchdl_source_t src;
int en;
patchdl_manifest_t mf;
jbuf_t j = {0};
if (!get_title_action_info(tid, &src, purl, sizeof purl, pti, sizeof pti,
if (!get_title_action_info(tid, &src, purl, sizeof purl,
durl_, sizeof durl_,
pti, sizeof pti,
psti, sizeof psti, cid, sizeof cid,
nm, sizeof nm, ver, sizeof ver, &en) || !purl[0])
return queue_json(conn, MHD_HTTP_NOT_FOUND,
@@ -1864,14 +1944,17 @@ on_request(void *cls, struct MHD_Connection *conn, const char *url,
install. Hashing runs on-device (SSD), so no multi-GB transfer. */
if (!strncmp(url, "/api/pkgverify/", 15)) {
const char *tid = url + 15;
char purl[768] = {0}, pti[32], psti[32], cid[64], nm[128], ver[16];
char purl[768] = {0}, durl_[512] = {0};
char pti[32], psti[32], cid[64], nm[128], ver[16];
char dest[320];
patchdl_source_t src;
int en, fd, okc = 0, badc = 0, first_bad = -1;
patchdl_manifest_t mf;
jbuf_t j = {0};
if (!get_title_action_info(tid, &src, purl, sizeof purl, pti, sizeof pti,
if (!get_title_action_info(tid, &src, purl, sizeof purl,
durl_, sizeof durl_,
pti, sizeof pti,
psti, sizeof psti, cid, sizeof cid,
nm, sizeof nm, ver, sizeof ver, &en) || !purl[0])
return queue_json(conn, MHD_HTTP_NOT_FOUND,
@@ -1912,12 +1995,15 @@ on_request(void *cls, struct MHD_Connection *conn, const char *url,
(installed app) ids, to expose the cross-region linkage. No install. */
if (!strncmp(url, "/api/pkgmeta/", 13)) {
const char *tid = url + 13;
char purl[768] = {0}, pti[32], psti[32], cid[64], nm[128], ver[16];
char purl[768] = {0}, durl_[512] = {0};
char pti[32], psti[32], cid[64], nm[128], ver[16];
char dest[320], ecid[64] = {0}, etid[48] = {0}, msg[128], resp[900];
patchdl_source_t src;
int en, is_app = 0, rc;
if (!get_title_action_info(tid, &src, purl, sizeof purl, pti, sizeof pti,
if (!get_title_action_info(tid, &src, purl, sizeof purl,
durl_, sizeof durl_,
pti, sizeof pti,
psti, sizeof psti, cid, sizeof cid,
nm, sizeof nm, ver, sizeof ver, &en) || !purl[0])
return queue_json(conn, MHD_HTTP_NOT_FOUND, "{\"error\":\"unknown title\"}");