Fixed PKG install function for game base PKG

This commit is contained in:
owendswang committed 2026-08-23 21:30:20 +08:00
1 parent bd4d1c45a6
commit 34de7d2483
6 files changed
+156 -48

No files matched your search

+45 -13
View File
@@ -30,6 +30,7 @@ let permissionOriginal = "";
let permissionBusy = false;
let downloadFrame = null;
let uploadXhr = null;
let uploadTerminalAbort = false;
let L = {};
const APP_VERSION = "v1.6";
@@ -458,6 +459,10 @@ function clearTrackedTask() {
downloadFrame.parentNode.removeChild(downloadFrame);
downloadFrame = null;
}
if (trackedTask && trackedTask.op === "upload" && uploadXhr) {
uploadTerminalAbort = true;
uploadXhr.abort();
}
trackedTask = null;
}
@@ -1798,8 +1803,11 @@ function renderTasks(tasks) {
if (confirm(t("cancelTaskConfirm", { label: opLabel(task.op) }))) {
requestTaskCancel(task.id);
api("/api/cancel", { id: task.id }).then(() => {
if (task.op === "upload" && uploadXhr) uploadXhr.abort();
return pollTasks();
if (task.op !== "upload") return pollTasks();
if (uploadXhr) uploadXhr.abort();
return api("/api/upload/finish", { task_id: task.id })
.catch(() => {})
.then(pollTasks);
}).catch(err => {
if (trackedTask && trackedTask.id === task.id) trackedTask.cancelRequested = false;
setStatus(t("cancelFailed", { error: err.message }));
@@ -1941,7 +1949,7 @@ function uploadConflicts(rels) {
return conflicts;
}
function uploadFileRequest(taskId, file, rel, overwrite) {
function uploadFileRequest(taskId, file, rel, overwrite, index) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
uploadXhr = xhr;
@@ -1967,12 +1975,25 @@ function uploadFileRequest(taskId, file, rel, overwrite) {
};
xhr.onerror = () => {
uploadXhr = null;
reject(new Error(xhr.statusText || t("backendError")));
const error = new Error(xhr.statusText || t("backendError"));
api("/api/tasks").then(data => {
const task = (data.tasks || []).find(item => item.id === taskId);
if (task && Number(task.completed_count || 0) >= index) {
resolve();
} else if (task && task.state === "failed") {
reject(new Error(backendErrorText(task.error_code, task.error_arg,
task.error || task.current)));
} else {
reject(error);
}
}).catch(() => reject(error));
};
xhr.onabort = () => {
uploadXhr = null;
const terminal = uploadTerminalAbort;
uploadTerminalAbort = false;
const err = new Error("aborted");
err.name = "AbortError";
err.name = terminal ? "TerminalTaskAbort" : "AbortError";
reject(err);
};
xhr.send(file);
@@ -2030,21 +2051,32 @@ async function uploadFiles(files) {
const file = list[i];
const rel = rels[i];
setStatus(t("uploadingStatus", { index: i + 1, count: list.length, name: rel }));
await uploadFileRequest(taskId, file, rel, overwrite);
await uploadFileRequest(taskId, file, rel, overwrite, i + 1);
}
await api("/api/upload/finish", { task_id: taskId });
await pollTasks();
setStatus(t("uploadDone", { count: list.length }));
} catch (err) {
const terminalAbort = err && err.name === "TerminalTaskAbort";
const canceled = err && err.name === "AbortError";
if (canceled) {
setStatus(t("taskCanceled", { label: opLabel("upload") }));
} else {
const message = t("uploadFailed", { error: err.message });
setStatus(message);
alert(message);
if (!terminalAbort) {
if (taskId) {
try {
await api("/api/cancel", { id: taskId });
await api("/api/upload/finish", { task_id: taskId });
} catch (cancelErr) {
/* The server may already have moved a failed upload to a terminal state. */
}
}
await pollTasks();
if (canceled) {
setStatus(t("taskCanceled", { label: opLabel("upload") }));
} else {
const message = t("uploadFailed", { error: err.message });
setStatus(message);
alert(message);
}
}
await pollTasks();
} finally {
uploadXhr = null;
uploadFilesEl.value = "";
+3 -2
View File
@@ -1853,8 +1853,9 @@ api_tasks(struct MHD_Connection *conn) {
json_escape(&b, task->error_code);
strbuf_append(&b, ",\"error_arg\":");
json_escape(&b, task->error_arg);
strbuf_printf(&b, ",\"src_count\":%zu,\"total\":%llu,\"done\":%llu,\"speed\":%llu,\"eta\":%llu,\"cancel_requested\":%s,\"created_at\":%lld,\"elapsed\":%lld,\"total_elapsed\":%lld,\"updated_at\":%lld}",
task->src_count, task->total, task->done, task->speed, task->eta,
strbuf_printf(&b, ",\"src_count\":%zu,\"completed_count\":%zu,\"total\":%llu,\"done\":%llu,\"speed\":%llu,\"eta\":%llu,\"cancel_requested\":%s,\"created_at\":%lld,\"elapsed\":%lld,\"total_elapsed\":%lld,\"updated_at\":%lld}",
task->src_count, task->upload_completed,
task->total, task->done, task->speed, task->eta,
task->cancel_requested ? "true" : "false",
(long long)task->created_at,
task->transfer_started_at ? (long long)(now - task->transfer_started_at) : 0LL,
+2
View File
@@ -45,6 +45,7 @@ typedef struct file_task {
size_t src_count;
size_t file_count;
size_t dir_count;
size_t upload_completed;
unsigned int chmod_mode;
int recursive;
unsigned long long total;
@@ -57,6 +58,7 @@ typedef struct file_task {
unsigned int eta_sample_next;
unsigned int eta_sample_count;
int cancel_requested;
unsigned int active_streams;
time_t created_at;
time_t transfer_started_at;
time_t updated_at;
+15 -4
View File
@@ -4,6 +4,7 @@
#include <limits.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
@@ -14,8 +15,13 @@ typedef struct pkg_metadata {
const char *content_id;
const char *content_name;
const char *icon_url;
uint32_t slot;
uint32_t is_playgo_enabled;
} pkg_metadata_t;
_Static_assert(sizeof(pkg_metadata_t) == 0x38,
"sceAppInstUtil metadata ABI mismatch");
typedef struct pkg_info {
char content_id[48];
int type;
@@ -23,12 +29,15 @@ typedef struct pkg_info {
} pkg_info_t;
typedef struct playgo_info {
char lang[8][30];
char scenario_ids[3][64];
char content_ids[64];
char languages[30][8];
char scenario_ids[64][3];
char content_ids[64][48];
long unknown[810];
} playgo_info_t;
_Static_assert(sizeof(playgo_info_t) == 0x2700,
"sceAppInstUtil PlayGoInfo ABI mismatch");
int sceAppInstUtilInitialize(void);
int sceAppInstUtilInstallByPackage(const pkg_metadata_t *, pkg_info_t *,
playgo_info_t *);
@@ -68,7 +77,9 @@ pkg_installer_install(const char *path) {
.playgo_scenario_id = "",
.content_id = "",
.content_name = "",
.icon_url = ""
.icon_url = "",
.slot = 0,
.is_playgo_enabled = 0
};
pkg_info_t pkg_info = {0};
playgo_info_t playgo_info = {0};
+1 -1
View File
@@ -82,7 +82,7 @@ remove_finished_tasks_locked(void) {
while(*link) {
file_task_t *task = *link;
if(task_is_active(task)) {
if(task_is_active(task) || task->active_streams) {
link = &task->next;
continue;
}
+90 -28
View File
@@ -28,8 +28,22 @@ typedef struct upload_context {
int failed;
int error;
int task_done;
const char *stage;
char error_message[256];
} upload_context_t;
static const char *
upload_error_message(upload_context_t *ctx) {
if(!ctx->error_message[0]) {
snprintf(ctx->error_message, sizeof(ctx->error_message), "%s failed%s%s: %s",
ctx->stage ? ctx->stage : "upload",
ctx->target[0] ? " for " : "",
ctx->target[0] ? ctx->target : "",
strerror(ctx->error ? ctx->error : EIO));
}
return ctx->error_message;
}
static int
upload_flush(upload_context_t *ctx) {
size_t written = 0;
@@ -81,11 +95,29 @@ finish_upload_task(file_task_t *task, task_state_t state, const char *current,
if(error) {
snprintf(task->error, sizeof(task->error), "%s", error);
}
if(state == TASK_FAILED) {
snprintf(task->error_code, sizeof(task->error_code), "upload_failed");
snprintf(task->error_arg, sizeof(task->error_arg), "%s",
current ? current : task->src);
}
task->updated_at = completed_at;
}
pthread_mutex_unlock(&g_tasks_lock);
}
static void
finish_upload_context_error(upload_context_t *ctx) {
if(!ctx->task || ctx->task_done) {
return;
}
upload_error_message(ctx);
finish_upload_task(ctx->task,
ctx->error == ECANCELED ? TASK_CANCELED : TASK_FAILED,
ctx->target[0] ? ctx->target : ctx->task->current,
ctx->error == ECANCELED ? "canceled" : ctx->error_message);
ctx->task_done = 1;
}
static file_task_t *
upload_task_from_conn(struct MHD_Connection *conn) {
char *idstr = request_value(conn, "X-WFM-Task-ID", "task_id");
@@ -300,12 +332,18 @@ filemgr_upload_begin(struct MHD_Connection *conn, void **upload_ctx) {
return -1;
}
ctx->fd = -1;
ctx->stage = "preparing upload";
*upload_ctx = ctx;
if(size_text) {
ctx->expected = strtoull(size_text, NULL, 10);
}
ctx->task = task;
if(task) {
pthread_mutex_lock(&g_tasks_lock);
task->active_streams++;
pthread_mutex_unlock(&g_tasks_lock);
}
if(task && task_cancel_requested(task)) {
ctx->failed = 1;
ctx->error = ECANCELED;
@@ -316,17 +354,20 @@ filemgr_upload_begin(struct MHD_Connection *conn, void **upload_ctx) {
ctx->error = EBUSY;
goto fail;
}
ctx->stage = "validating target path";
if(!base || !rel || path_join_relative(ctx->target, sizeof(ctx->target),
base, rel)) {
ctx->failed = 1;
ctx->error = errno ? errno : EINVAL;
goto fail;
}
ctx->stage = "creating target folders";
if(ensure_parent_dirs(base, rel)) {
ctx->failed = 1;
ctx->error = errno ? errno : EACCES;
goto fail;
}
ctx->stage = "checking target path";
if(!lstat(ctx->target, &st)) {
if(S_ISDIR(st.st_mode) || !overwrite || strcmp(overwrite, "1")) {
ctx->failed = 1;
@@ -338,19 +379,26 @@ filemgr_upload_begin(struct MHD_Connection *conn, void **upload_ctx) {
ctx->error = errno;
goto fail;
}
ctx->stage = "checking target permissions";
if(check_target_writable(ctx->target, &checked_dirs, &checked_dir_count,
error, sizeof(error), code, sizeof(code),
arg, sizeof(arg))) {
snprintf(ctx->error_message, sizeof(ctx->error_message), "%s",
error[0] ? error : "target is not writable");
ctx->failed = 1;
ctx->error = errno ? errno : EACCES;
goto fail;
}
ctx->stage = "checking target space";
if(check_target_space(ctx->target, ctx->expected, error, sizeof(error),
code, sizeof(code), arg, sizeof(arg))) {
snprintf(ctx->error_message, sizeof(ctx->error_message), "%s",
error[0] ? error : "not enough target space");
ctx->failed = 1;
ctx->error = errno ? errno : ENOSPC;
goto fail;
}
ctx->stage = "creating temporary path";
n = snprintf(ctx->temp, sizeof(ctx->temp), "%s.wfm-upload-%ld-%lld.tmp",
ctx->target, (long)getpid(), (long long)time(NULL));
if(n < 0 || (size_t)n >= sizeof(ctx->temp)) {
@@ -358,12 +406,14 @@ filemgr_upload_begin(struct MHD_Connection *conn, void **upload_ctx) {
ctx->error = ENAMETOOLONG;
goto fail;
}
ctx->stage = "opening temporary file";
ctx->fd = open(ctx->temp, O_WRONLY | O_CREAT | O_TRUNC, 0600);
if(ctx->fd < 0) {
ctx->failed = 1;
ctx->error = errno;
goto fail;
}
ctx->stage = "allocating upload buffer";
if(!(ctx->buffer = malloc(UPLOAD_BUFFER_SIZE))) {
ctx->failed = 1;
ctx->error = ENOMEM;
@@ -374,13 +424,7 @@ fail:
free_paths(checked_dirs, checked_dir_count);
free(base); free(rel); free(overwrite); free(size_text);
if(ctx->failed) {
if(ctx->task) {
finish_upload_task(ctx->task,
ctx->error == ECANCELED ? TASK_CANCELED : TASK_FAILED,
ctx->target[0] ? ctx->target : ctx->task->current,
ctx->error == ECANCELED ? "canceled" : strerror(ctx->error ? ctx->error : EIO));
ctx->task_done = 1;
}
finish_upload_context_error(ctx);
if(ctx->fd >= 0) {
close(ctx->fd);
ctx->fd = -1;
@@ -391,6 +435,7 @@ fail:
errno = ctx->error ? ctx->error : EIO;
return -1;
}
ctx->stage = "writing file";
return 0;
}
@@ -404,6 +449,7 @@ filemgr_upload_data(void *upload_ctx, const char *data, size_t size) {
if(ctx->task && task_cancel_requested(ctx->task)) {
ctx->failed = 1;
ctx->error = ECANCELED;
finish_upload_context_error(ctx);
return -1;
}
while(size) {
@@ -415,6 +461,7 @@ filemgr_upload_data(void *upload_ctx, const char *data, size_t size) {
data += take;
size -= take;
if(ctx->buffered == UPLOAD_BUFFER_SIZE && upload_flush(ctx)) {
finish_upload_context_error(ctx);
return -1;
}
}
@@ -430,46 +477,55 @@ filemgr_upload_finish(struct MHD_Connection *conn, void *upload_ctx) {
return send_json_error(conn, MHD_HTTP_BAD_REQUEST, "invalid path");
}
if(ctx->failed) {
if(ctx->task && !ctx->task_done) {
finish_upload_task(ctx->task,
ctx->error == ECANCELED ? TASK_CANCELED : TASK_FAILED,
ctx->target[0] ? ctx->target : ctx->task->current,
ctx->error == ECANCELED ? "canceled" : strerror(ctx->error ? ctx->error : EIO));
ctx->task_done = 1;
}
finish_upload_context_error(ctx);
errno = ctx->error ? ctx->error : EIO;
return send_json_error(conn, errno == EEXIST ? MHD_HTTP_CONFLICT :
errno == EBUSY ? MHD_HTTP_CONFLICT :
errno == ECANCELED ? MHD_HTTP_CONFLICT :
errno == ENOSPC ? MHD_HTTP_INSUFFICIENT_STORAGE :
MHD_HTTP_INTERNAL_SERVER_ERROR, NULL);
return send_json_error_detail(conn,
errno == EEXIST ? MHD_HTTP_CONFLICT :
errno == EBUSY ? MHD_HTTP_CONFLICT :
errno == ECANCELED ? MHD_HTTP_CONFLICT :
errno == ENOSPC ? MHD_HTTP_INSUFFICIENT_STORAGE :
MHD_HTTP_INTERNAL_SERVER_ERROR,
ctx->error == ECANCELED ? "canceled" : ctx->error_message,
ctx->error == ECANCELED ? NULL : "upload_failed",
ctx->target[0] ? ctx->target : NULL);
}
ctx->stage = "writing file";
if(ctx->buffered && upload_flush(ctx)) {
ctx->error = ctx->error ? ctx->error : EIO;
goto done;
}
ctx->stage = "verifying uploaded size";
if(ctx->expected && ctx->written != ctx->expected) {
ctx->error = EIO;
goto done;
}
ctx->stage = "setting file permissions";
if(fchmod_0777(ctx->fd)) {
ctx->error = errno;
goto done;
}
ctx->stage = "syncing file data";
if(fsync(ctx->fd)) {
ctx->error = errno;
goto done;
}
ctx->stage = "closing temporary file";
if(close(ctx->fd)) {
ctx->fd = -1;
ctx->error = errno;
goto done;
}
ctx->fd = -1;
ctx->stage = "moving temporary file into place";
if(rename(ctx->temp, ctx->target)) {
ctx->error = errno;
goto done;
}
if(ctx->task) {
pthread_mutex_lock(&g_tasks_lock);
ctx->task->upload_completed++;
pthread_mutex_unlock(&g_tasks_lock);
}
ret = 0;
done:
@@ -478,19 +534,18 @@ done:
ctx->fd = -1;
}
if(ret) {
upload_error_message(ctx);
if(ctx->temp[0]) {
unlink(ctx->temp);
}
if(ctx->task && !ctx->task_done) {
finish_upload_task(ctx->task,
ctx->error == ECANCELED ? TASK_CANCELED : TASK_FAILED,
ctx->target[0] ? ctx->target : ctx->task->current,
ctx->error == ECANCELED ? "canceled" : strerror(ctx->error ? ctx->error : EIO));
ctx->task_done = 1;
}
finish_upload_context_error(ctx);
errno = ctx->error ? ctx->error : EIO;
return send_json_error(conn, errno == ECANCELED ? MHD_HTTP_CONFLICT :
MHD_HTTP_INTERNAL_SERVER_ERROR, NULL);
return send_json_error_detail(conn,
errno == ECANCELED ? MHD_HTTP_CONFLICT :
MHD_HTTP_INTERNAL_SERVER_ERROR,
ctx->error == ECANCELED ? "canceled" : ctx->error_message,
ctx->error == ECANCELED ? NULL : "upload_failed",
ctx->target[0] ? ctx->target : NULL);
}
return send_json_ok(conn);
}
@@ -516,5 +571,12 @@ filemgr_upload_free(void *upload_ctx) {
}
}
free(ctx->buffer);
if(ctx->task) {
pthread_mutex_lock(&g_tasks_lock);
if(ctx->task->active_streams) {
ctx->task->active_streams--;
}
pthread_mutex_unlock(&g_tasks_lock);
}
free(ctx);
}