mirror of
https://github.com/LisherSong/ps5-web-file-manager.git
synced 2026-10-06 18:00:28 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a61ac54e1f |
No files matched your search
@@ -13,7 +13,7 @@ ifeq ($(MAKECMDGOALS),)
|
||||
endif
|
||||
endif
|
||||
|
||||
VERSION_TAG := v1.1
|
||||
VERSION_TAG := v1.2
|
||||
TITLE_ID := FMGR88888
|
||||
PYTHON ?= python3
|
||||
STRIP ?= $(PS5_PAYLOAD_SDK)/bin/prospero-strip
|
||||
@@ -24,7 +24,7 @@ HOST_PKG_CONFIG ?= pkg-config
|
||||
|
||||
BIN := web-file-mgr.elf
|
||||
LINUX_BIN := web-file-mgr-linux
|
||||
COMMON_SRCS := src/main.c src/websrv.c src/filemgr.c src/file_response.c src/task.c src/upload.c src/download.c src/text.c src/list.c src/space.c src/fs_util.c src/json_util.c src/path_util.c src/asset.c src/mime.c src/notify.c
|
||||
COMMON_SRCS := src/main.c src/websrv.c src/filemgr.c src/file_response.c src/task.c src/upload.c src/download.c src/text.c src/list.c src/space.c src/fs_util.c src/json_util.c src/path_util.c src/asset.c src/mime.c src/notify.c src/pkg_installer.c
|
||||
PS5_SRCS := $(COMMON_SRCS) src/app_installer.c
|
||||
LINUX_SRCS := $(COMMON_SRCS)
|
||||
BASE_ASSETS := $(filter-out %.dds,$(wildcard assets/*))
|
||||
|
||||
@@ -38,6 +38,7 @@ PS5 web file manager payload. It runs an HTTP UI starting at port `8888`, instal
|
||||
- Home screen launcher icon and browser favicon use the same embedded `icon0.png` data in the PS5 build to avoid storing the icon twice in the ELF.
|
||||
- Create/edit text files ending with `.txt`, `.json`, `.xml`, `.ini`, `.cfg`, `.conf`, `.md`, `.log`, `.lua`, `.js`, `.css`, `.html`, `.htm`, `.c`, `.h`, `.cpp`, `.hpp`, `.sh`, `.csv`, `.yaml`, `.yml`, `.shn`.
|
||||
- Preview image files ending with `.png`, `.jpg`, `.jpeg`, `.gif`, `.bmp`, `.webp`.
|
||||
- Install PKG files ends with `.pkg`.
|
||||
|
||||
## Build
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
@@ -43,6 +43,7 @@
|
||||
<button id="renameBtn" data-i18n="rename"></button>
|
||||
<button id="downloadBtn" class="remote-only" data-i18n="download"></button>
|
||||
<button id="deleteBtn" class="danger" data-i18n="delete"></button>
|
||||
<button id="installPkgBtn" class="install-action" data-i18n="install" hidden></button>
|
||||
<button id="pasteBtn" class="paste-action" hidden>
|
||||
<span id="pasteVerb" data-i18n="paste"></span>
|
||||
<span id="pasteName" class="paste-name"></span><span id="pasteCount" class="paste-count"></span>
|
||||
|
||||
@@ -19,7 +19,13 @@ window.WFM_LANG = {
|
||||
refresh: "Refresh",
|
||||
mkdir: "New Folder",
|
||||
newText: "New Text",
|
||||
install: "Install",
|
||||
installPackage: "Install package",
|
||||
pkgInstalling: "Starting package installation: {name}...",
|
||||
pkgInstallStarted: "Package installation started: {name}",
|
||||
file: "File",
|
||||
textFile: "Text",
|
||||
image: "Image",
|
||||
dir: "Folder",
|
||||
parent: "Parent",
|
||||
name: "Name",
|
||||
@@ -118,6 +124,9 @@ window.WFM_LANG = {
|
||||
err_source_destination_same: "Source and destination are the same",
|
||||
err_destination_inside_source: "Cannot copy or move a folder inside itself",
|
||||
err_invalid_path: "Invalid path",
|
||||
err_pkg_type_invalid: "Only .pkg files can be installed",
|
||||
err_pkg_install_failed: "Package installation failed ({arg})",
|
||||
err_pkg_install_unsupported: "Package installation is only available on PS5",
|
||||
err_file_not_found: "File not found",
|
||||
err_file_already_exists: "A file with the same name already exists",
|
||||
err_invalid_method: "Invalid request method",
|
||||
|
||||
@@ -19,7 +19,13 @@ window.WFM_LANG = {
|
||||
refresh: "刷新",
|
||||
mkdir: "新建目录",
|
||||
newText: "新建文本",
|
||||
install: "安装",
|
||||
installPackage: "安装 PKG",
|
||||
pkgInstalling: "正在提交安装:{name}...",
|
||||
pkgInstallStarted: "已开始安装:{name}",
|
||||
file: "文件",
|
||||
textFile: "文本",
|
||||
image: "图片",
|
||||
dir: "目录",
|
||||
parent: "上级目录",
|
||||
name: "名称",
|
||||
@@ -118,6 +124,9 @@ window.WFM_LANG = {
|
||||
err_source_destination_same: "源和目标相同",
|
||||
err_destination_inside_source: "不能复制或移动目录到它自己的内部",
|
||||
err_invalid_path: "路径无效",
|
||||
err_pkg_type_invalid: "只能安装 .pkg 文件",
|
||||
err_pkg_install_failed: "PKG 安装失败({arg})",
|
||||
err_pkg_install_unsupported: "PKG 安装仅支持 PS5 环境",
|
||||
err_file_not_found: "文件不存在",
|
||||
err_file_already_exists: "同名文件已存在",
|
||||
err_invalid_method: "请求方式无效",
|
||||
|
||||
+13
-2
@@ -329,7 +329,7 @@ button.primary:hover {
|
||||
}
|
||||
|
||||
.paste-action {
|
||||
max-width: 480px;
|
||||
max-width: 360px;
|
||||
}
|
||||
|
||||
.paste-action > * {
|
||||
@@ -341,12 +341,23 @@ button.primary:hover {
|
||||
display: inline-block;
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
max-width: 180px;
|
||||
max-width: 120px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.install-action {
|
||||
min-width: 88px;
|
||||
border-color: #8c6425;
|
||||
background: #352b1c;
|
||||
color: #f8dfae;
|
||||
}
|
||||
|
||||
button.install-action:hover {
|
||||
background: #433521;
|
||||
}
|
||||
|
||||
.paste-count {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
+94
-9
@@ -18,6 +18,7 @@ let directoryLoadingStartedAt = 0;
|
||||
let taskOverlayTimer = 0;
|
||||
let lastCompletionId = null;
|
||||
let taskPollFailedAlertShown = false;
|
||||
let localActionBusy = false;
|
||||
let textEditorPath = null;
|
||||
let textEditorVersion = null;
|
||||
let textEditorOriginal = "";
|
||||
@@ -26,7 +27,7 @@ let downloadFrame = null;
|
||||
let uploadXhr = null;
|
||||
let L = {};
|
||||
|
||||
const APP_VERSION = "v1.1";
|
||||
const APP_VERSION = "v1.2";
|
||||
const LAST_PATH_KEY = "ps5-web-file-mgr:last-path";
|
||||
const SORT_KEY = "ps5-web-file-mgr:list-sort";
|
||||
const LOADING_DISPLAY_DELAY = 250;
|
||||
@@ -54,6 +55,7 @@ const pasteVerbEl = document.getElementById("pasteVerb");
|
||||
const pasteNameEl = document.getElementById("pasteName");
|
||||
const pasteCountEl = document.getElementById("pasteCount");
|
||||
const pasteTargetTextEl = document.getElementById("pasteTargetText");
|
||||
const installPkgBtn = document.getElementById("installPkgBtn");
|
||||
const clearClipboardBtn = document.getElementById("clearClipboardBtn");
|
||||
const downloadBtn = document.getElementById("downloadBtn");
|
||||
const uploadMenuEl = document.getElementById("uploadMenu");
|
||||
@@ -559,6 +561,7 @@ function pathIsSameOrChild(parent, child) {
|
||||
}
|
||||
|
||||
function typeLabel(type) {
|
||||
if (type === "parent") return t("parent");
|
||||
return type === "d" ? t("dir") : t("file");
|
||||
}
|
||||
|
||||
@@ -571,6 +574,54 @@ function isPreviewableImage(item) {
|
||||
return item.type === "-" && /\.(png|jpe?g|gif|bmp|webp)$/i.test(item.name);
|
||||
}
|
||||
|
||||
function isPkgPackage(item) {
|
||||
return item.type === "-" && /\.pkg$/i.test(item.name);
|
||||
}
|
||||
|
||||
function itemTypeLabel(item) {
|
||||
if (item.type === "parent" || item.type === "d") return typeLabel(item.type);
|
||||
if (isPkgPackage(item)) return "PKG";
|
||||
if (isPreviewableImage(item)) return t("image");
|
||||
if (isEditableText(item)) return t("textFile");
|
||||
return t("file");
|
||||
}
|
||||
|
||||
async function installPkg(item) {
|
||||
if (busy || loadingPath) return;
|
||||
try {
|
||||
localActionBusy = true;
|
||||
setBusy(true);
|
||||
setStatus(t("pkgInstalling", { name: displayName(item) }));
|
||||
await api("/api/install-pkg", { path: item.path });
|
||||
setStatus(t("pkgInstallStarted", { name: displayName(item) }));
|
||||
} catch (err) {
|
||||
showActionFailed(t("installPackage"), err.message);
|
||||
} finally {
|
||||
localActionBusy = false;
|
||||
setBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function actionInstallSelectedPkgs() {
|
||||
if (busy || loadingPath) return;
|
||||
const items = selectedEntries().filter(isPkgPackage);
|
||||
if (!items.length) return;
|
||||
try {
|
||||
localActionBusy = true;
|
||||
setBusy(true);
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
setStatus(t("pkgInstalling", { name: displayName(items[i]) }));
|
||||
await api("/api/install-pkg", { path: items[i].path });
|
||||
}
|
||||
setStatus(t("pkgInstallStarted", { name: itemTitle(items) }));
|
||||
} catch (err) {
|
||||
showActionFailed(t("installPackage"), err.message);
|
||||
} finally {
|
||||
localActionBusy = false;
|
||||
setBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
function openImagePreview(item) {
|
||||
if (busy) return;
|
||||
imagePreviewNameEl.textContent = displayName(item);
|
||||
@@ -708,6 +759,17 @@ function resetScrollTop() {
|
||||
if (window.requestAnimationFrame) requestAnimationFrame(apply);
|
||||
}
|
||||
|
||||
function revealPathInList(path) {
|
||||
const row = filesEl.querySelector("tr[data-path=\"" + cssEscape(path) + "\"]");
|
||||
if (!row) return;
|
||||
|
||||
const top = row.offsetTop;
|
||||
const maxScroll = contentEl.scrollHeight - contentEl.clientHeight;
|
||||
if (maxScroll <= 0) return;
|
||||
const target = top - Math.floor((contentEl.clientHeight - row.offsetHeight) / 2);
|
||||
contentEl.scrollTop = Math.max(0, Math.min(maxScroll, target));
|
||||
}
|
||||
|
||||
function clipboardTitle() {
|
||||
if (!clipboard || clipboard.items.length === 0) return "";
|
||||
return itemTitle(clipboard.items);
|
||||
@@ -740,7 +802,7 @@ function compareEntries(a, b) {
|
||||
|
||||
let result = 0;
|
||||
if (sortKey === "name") result = compareText(displayName(a), displayName(b));
|
||||
else if (sortKey === "type") result = compareText(typeLabel(a.type), typeLabel(b.type));
|
||||
else if (sortKey === "type") result = compareText(itemTypeLabel(a), itemTypeLabel(b));
|
||||
else if (sortKey === "size") result = (a.type === "d" ? 0 : Number(a.size || 0)) -
|
||||
(b.type === "d" ? 0 : Number(b.size || 0));
|
||||
else if (sortKey === "mtime") result = Number(a.mtime || 0) - Number(b.mtime || 0);
|
||||
@@ -804,6 +866,19 @@ function renderClipboard() {
|
||||
clearClipboardBtn.disabled = locked;
|
||||
}
|
||||
|
||||
function renderInstallPkgButton(items, locked) {
|
||||
const pkgs = items.filter(isPkgPackage);
|
||||
installPkgBtn.hidden = pkgs.length === 0;
|
||||
if (!pkgs.length) {
|
||||
installPkgBtn.title = "";
|
||||
installPkgBtn.disabled = true;
|
||||
return;
|
||||
}
|
||||
const title = itemTitle(pkgs);
|
||||
installPkgBtn.title = t("installPackage") + ": " + title;
|
||||
installPkgBtn.disabled = locked;
|
||||
}
|
||||
|
||||
function singleSelected() {
|
||||
const items = selectedEntries();
|
||||
return items.length === 1 ? items[0] : null;
|
||||
@@ -826,6 +901,7 @@ function updateButtons() {
|
||||
selectAllEl.disabled = locked;
|
||||
selectAllEl.checked = entries.length > 0 && items.length === entries.length;
|
||||
selectAllEl.indeterminate = items.length > 0 && items.length < entries.length;
|
||||
renderInstallPkgButton(items, locked);
|
||||
renderClipboard();
|
||||
}
|
||||
|
||||
@@ -1006,6 +1082,7 @@ function render() {
|
||||
const iconImg = document.createElement("img");
|
||||
iconImg.className = "icon";
|
||||
iconImg.src = isParent ? "/icon-up.png" : item.type === "d" ? "/icon-folder.png" :
|
||||
isPkgPackage(item) ? "/icon-pkg.png" :
|
||||
isPreviewableImage(item) ? "/icon-image.png" :
|
||||
isEditableText(item) ? "/icon-file.png" : "/icon-generic.png";
|
||||
iconImg.alt = "";
|
||||
@@ -1017,7 +1094,7 @@ function render() {
|
||||
tr,
|
||||
selectTd,
|
||||
nameTd,
|
||||
classCell("type-col", isParent ? t("parent") : item.type === "d" ? t("dir") : t("file")),
|
||||
classCell("type-col", itemTypeLabel(item)),
|
||||
classCell("size-col", formatSize(item.size, item.type)),
|
||||
classCell("time-col", formatTime(item.mtime)),
|
||||
classCell("mode-col", isParent ? "" : "0" + Number(item.mode).toString(8))
|
||||
@@ -1164,10 +1241,10 @@ function validatePasteTarget() {
|
||||
return {
|
||||
ok: false,
|
||||
message: t("removeConflictFirst", {
|
||||
existingType: typeLabel(existing.type),
|
||||
existingType: itemTypeLabel(existing),
|
||||
name: item.name,
|
||||
label: clipboard.op === "move" ? t("move") : t("copy"),
|
||||
sourceType: typeLabel(item.type)
|
||||
sourceType: itemTypeLabel(item)
|
||||
})
|
||||
};
|
||||
}
|
||||
@@ -1334,7 +1411,7 @@ function renderTasks(tasks) {
|
||||
DOWNLOAD_OVERLAY_DISPLAY_DELAY : LOADING_DISPLAY_DELAY);
|
||||
else {
|
||||
hideTaskOverlay();
|
||||
setBusy(false);
|
||||
if (!localActionBusy) setBusy(false);
|
||||
}
|
||||
|
||||
if (!hasActive) return;
|
||||
@@ -1436,8 +1513,11 @@ async function pollTasks() {
|
||||
}));
|
||||
}
|
||||
const wasBusy = busy;
|
||||
const wasTaskBusy = Boolean(trackedTask || pendingOverlayText ||
|
||||
!overlayEl.hidden || taskOverlayTimer);
|
||||
renderTasks(tasks);
|
||||
const becameIdle = wasBusy && !busy;
|
||||
const taskBecameIdle = wasTaskBusy && !busy;
|
||||
let shouldRefresh = false;
|
||||
let sawTrackedTask = false;
|
||||
for (const task of tasks) {
|
||||
@@ -1458,9 +1538,9 @@ async function pollTasks() {
|
||||
shouldRefresh = true;
|
||||
clearTrackedTask();
|
||||
}
|
||||
if (becameIdle && !tasks.length) shouldRefresh = true;
|
||||
if (taskBecameIdle && !tasks.length) shouldRefresh = true;
|
||||
if (shouldRefresh) {
|
||||
if (becameIdle) startContentLoadingTimer();
|
||||
if (taskBecameIdle) startContentLoadingTimer();
|
||||
clearSelection(false);
|
||||
await load(taskRefreshPath || cwd, false);
|
||||
await refreshSpaces();
|
||||
@@ -1690,6 +1770,7 @@ document.getElementById("mkdirBtn").addEventListener("click", () => {
|
||||
document.getElementById("copyBtn").addEventListener("click", actionCopy);
|
||||
document.getElementById("moveBtn").addEventListener("click", actionMove);
|
||||
pasteBtn.addEventListener("click", actionPaste);
|
||||
installPkgBtn.addEventListener("click", actionInstallSelectedPkgs);
|
||||
clearClipboardBtn.addEventListener("click", clearClipboard);
|
||||
document.getElementById("renameBtn").addEventListener("click", actionRename);
|
||||
downloadBtn.addEventListener("click", actionDownload);
|
||||
@@ -1755,10 +1836,14 @@ filesEl.addEventListener("click", event => {
|
||||
};
|
||||
focusPath(item.path);
|
||||
if (item.type === "parent" || item.type === "d") {
|
||||
const revealPath = item.type === "parent" ? cwd : "";
|
||||
setTimeout(async () => {
|
||||
if (!(await load(item.path, undefined, false, true))) await load(cwd, false, true);
|
||||
if (await load(item.path, item.type === "parent" ? false : undefined, false, true)) {
|
||||
/* if (revealPath) revealPathInList(revealPath); */
|
||||
} else await load(cwd, false, true);
|
||||
}, 0);
|
||||
}
|
||||
else if (isPkgPackage(item)) installPkg(item);
|
||||
else if (isPreviewableImage(item)) openImagePreview(item);
|
||||
else if (isEditableText(item)) openTextEditor(item);
|
||||
else togglePath(item.path, !selected.has(item.path));
|
||||
|
||||
+2
-2
@@ -9,6 +9,7 @@
|
||||
#include <ps5/kernel.h>
|
||||
|
||||
#include "asset.h"
|
||||
#include "pkg_installer.h"
|
||||
|
||||
#define INCASSET(name, file) \
|
||||
__asm__(".section .rodata\n" \
|
||||
@@ -28,7 +29,6 @@
|
||||
INCASSET(param_json, "assets/param.json");
|
||||
INCASSET(icon0_png, "assets/icon0.png");
|
||||
|
||||
int sceAppInstUtilInitialize(void);
|
||||
int sceAppInstUtilAppInstallAll(void *);
|
||||
|
||||
static int
|
||||
@@ -111,7 +111,7 @@ app_install_if_needed(void) {
|
||||
|
||||
printf("Installing launcher app %s\n", title_id);
|
||||
|
||||
if((err = sceAppInstUtilInitialize())) {
|
||||
if((err = pkg_installer_initialize())) {
|
||||
printf("sceAppInstUtilInitialize: error 0x%08X\n", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "filemgr_internal.h"
|
||||
#include "json_util.h"
|
||||
#include "path_util.h"
|
||||
#include "pkg_installer.h"
|
||||
#include "websrv.h"
|
||||
|
||||
#define COPY_BUFFER_SIZE (8 * 1024 * 1024)
|
||||
@@ -2008,6 +2009,42 @@ api_mkdir(struct MHD_Connection *conn) {
|
||||
: send_json_ok(conn);
|
||||
}
|
||||
|
||||
static enum MHD_Result
|
||||
api_install_pkg(struct MHD_Connection *conn) {
|
||||
char *path = fs_path_value(query_value(conn, "path"));
|
||||
const char *extension;
|
||||
struct stat st;
|
||||
char error_code[16];
|
||||
int result;
|
||||
|
||||
if(!path || !(extension = strrchr(path, '.')) ||
|
||||
strcasecmp(extension, ".pkg")) {
|
||||
free(path);
|
||||
return send_json_error_detail(conn, MHD_HTTP_BAD_REQUEST,
|
||||
"file is not a PKG package",
|
||||
"pkg_type_invalid", NULL);
|
||||
}
|
||||
if(stat(path, &st) || !S_ISREG(st.st_mode)) {
|
||||
free(path);
|
||||
return send_json_error(conn, MHD_HTTP_NOT_FOUND, "file not found");
|
||||
}
|
||||
|
||||
result = pkg_installer_install(path);
|
||||
free(path);
|
||||
if(result == PKG_INSTALL_UNSUPPORTED) {
|
||||
return send_json_error_detail(conn, MHD_HTTP_NOT_IMPLEMENTED,
|
||||
"package installation is only available on PS5",
|
||||
"pkg_install_unsupported", NULL);
|
||||
}
|
||||
if(result) {
|
||||
snprintf(error_code, sizeof(error_code), "0x%08X", (unsigned int)result);
|
||||
return send_json_error_detail(conn, MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||
"package installation failed",
|
||||
"pkg_install_failed", error_code);
|
||||
}
|
||||
return send_json_ok(conn);
|
||||
}
|
||||
|
||||
enum MHD_Result
|
||||
filemgr_api_request(struct MHD_Connection *conn, const char *url,
|
||||
const char *method, const char *body, size_t body_size) {
|
||||
@@ -2029,6 +2066,11 @@ filemgr_api_request(struct MHD_Connection *conn, const char *url,
|
||||
if(!strcmp(url, "/api/upload/finish")) return api_upload_finish(conn);
|
||||
if(!strcmp(url, "/api/rename")) return api_rename(conn);
|
||||
if(!strcmp(url, "/api/mkdir")) return api_mkdir(conn);
|
||||
if(!strcmp(url, "/api/install-pkg")) {
|
||||
return strcmp(method, MHD_HTTP_METHOD_POST) ?
|
||||
send_json_error(conn, MHD_HTTP_METHOD_NOT_ALLOWED, "invalid method") :
|
||||
api_install_pkg(conn);
|
||||
}
|
||||
if(!strcmp(url, "/api/text")) {
|
||||
return strcmp(method, MHD_HTTP_METHOD_GET) ?
|
||||
send_json_error(conn, MHD_HTTP_METHOD_NOT_ALLOWED, "invalid method") :
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
#include "pkg_installer.h"
|
||||
|
||||
#ifndef __linux__
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
typedef struct pkg_metadata {
|
||||
const char *uri;
|
||||
const char *ex_uri;
|
||||
const char *playgo_scenario_id;
|
||||
const char *content_id;
|
||||
const char *content_name;
|
||||
const char *icon_url;
|
||||
} pkg_metadata_t;
|
||||
|
||||
typedef struct pkg_info {
|
||||
char content_id[48];
|
||||
int type;
|
||||
int platform;
|
||||
} pkg_info_t;
|
||||
|
||||
typedef struct playgo_info {
|
||||
char lang[8][30];
|
||||
char scenario_ids[3][64];
|
||||
char content_ids[64];
|
||||
long unknown[810];
|
||||
} playgo_info_t;
|
||||
|
||||
int sceAppInstUtilInitialize(void);
|
||||
int sceAppInstUtilInstallByPackage(const pkg_metadata_t *, pkg_info_t *,
|
||||
playgo_info_t *);
|
||||
|
||||
static pthread_mutex_t installer_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static int installer_initialized;
|
||||
|
||||
static int
|
||||
initialize_locked(void) {
|
||||
int result;
|
||||
|
||||
if(!installer_initialized) {
|
||||
result = sceAppInstUtilInitialize();
|
||||
if(result) return result;
|
||||
installer_initialized = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pkg_installer_initialize(void) {
|
||||
int result;
|
||||
|
||||
pthread_mutex_lock(&installer_lock);
|
||||
result = initialize_locked();
|
||||
pthread_mutex_unlock(&installer_lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
pkg_installer_install(const char *path) {
|
||||
pkg_metadata_t metadata = {
|
||||
.uri = path,
|
||||
.ex_uri = "",
|
||||
.playgo_scenario_id = "",
|
||||
.content_id = "",
|
||||
.content_name = "",
|
||||
.icon_url = ""
|
||||
};
|
||||
pkg_info_t pkg_info = {0};
|
||||
playgo_info_t playgo_info = {0};
|
||||
int result;
|
||||
|
||||
pthread_mutex_lock(&installer_lock);
|
||||
result = initialize_locked();
|
||||
if(result) {
|
||||
pthread_mutex_unlock(&installer_lock);
|
||||
return result;
|
||||
}
|
||||
result = sceAppInstUtilInstallByPackage(&metadata, &pkg_info, &playgo_info);
|
||||
pthread_mutex_unlock(&installer_lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int
|
||||
pkg_installer_initialize(void) {
|
||||
return PKG_INSTALL_UNSUPPORTED;
|
||||
}
|
||||
|
||||
int
|
||||
pkg_installer_install(const char *path) {
|
||||
(void)path;
|
||||
return PKG_INSTALL_UNSUPPORTED;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#define PKG_INSTALL_UNSUPPORTED 0x7fffffff
|
||||
|
||||
int pkg_installer_initialize(void);
|
||||
int pkg_installer_install(const char *path);
|
||||
Reference in new issue
Block a user