mirror of
https://github.com/knutwurst/patchdl.git
synced 2026-10-06 11:00:25 +02:00
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:
1 parent
983f39fa89
commit
970c7d8f1d
6 files changed
+294
-61
No files matched your search
+152
-31
@@ -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;
|
||||
}
|
||||
|
||||
memset(&st, 0, sizeof(st));
|
||||
rc = ai_get_status(cid, &st);
|
||||
/* 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(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;
|
||||
}
|
||||
snprintf(msg, msg_sz,
|
||||
"install rejected (InstallByPackage=0x%08x, AppInstallPkg=0x%08x)",
|
||||
(unsigned)rc, (unsigned)rc2);
|
||||
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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
|
||||
@@ -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]) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
+116
-30
@@ -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);
|
||||
|
||||
{
|
||||
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\":\"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\":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);
|
||||
return queue_json_owned(conn, MHD_HTTP_CONFLICT, strdup(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\"}");
|
||||
|
||||
Reference in new issue
Block a user