mirror of
https://github.com/TheCriicom/omega-dashboard-ps5.git
synced 2026-10-17 10:01:25 +02:00
- Il demone legge lo stato di alimentazione dai flag del kernel SceSystemStateMgrInfo e SceSystemStateMgrStatus (come ShadowMountPlus e OnionHEN) e, prima che la console si spenga o si addormenti, ferma musica, voce del party, server di controllo e download di PatchDL e smonta i giochi avviati da un disco esterno (in riposo resta montato quello in primo piano). Al risveglio riparte da solo (power.c). - Spegni, Riavvia e Modalità riposo dal menu di Omega aspettano installazioni, copie e salvataggi online, smontano i giochi esterni e chiedono al demone di fermarsi prima. - /system_ex non resta più montato in scrittura dopo l'avvio di un gioco da disco esterno. - Demone: niente più giri a vuoto se l'uscita audio si rompe; party e notifiche interrogati meno spesso e con attese crescenti senza rete; caricamenti di musica e comandi della voce non bloccano più il server di controllo; la UI riavvia il demone solo se è bloccato davvero; gestore dei crash con stack proprio, SIGTERM gestito, registri sotto 1 MB. - fsync prima di ogni rename (aggiornamenti, caricatori, libreria, lettore, copie dei giochi); le copie interrotte vengono riconosciute e pulite. - Installer: ogni passo di rete finisce in omega-installer.log, incluso nella diagnostica.
367 lines
17 KiB
C
367 lines
17 KiB
C
// Omega UI — aggiornamenti automatici dell'app e del demone.
|
|
//
|
|
// Si interroga sempre OMEGA_BASE_URL, anche se l'utente usa un altro server.
|
|
// Il manifest (/updates/manifest.json) è firmato Ed25519 con una chiave che
|
|
// resta sul computer di chi pubblica: né il server né la rete (il TLS non è
|
|
// verificato) possono far installare altro. Ogni file deve avere lo SHA-256
|
|
// del manifest, e "seq" impedisce di tornare a un manifest più vecchio.
|
|
//
|
|
// I percorsi di destinazione sono fissi, mai presi dal server; un componente
|
|
// che non è installato sulla console non viene aggiunto. L'app nuova vale dal
|
|
// riavvio, il demone dal prossimo avvio della console.
|
|
#include "app.h"
|
|
#include <signal.h>
|
|
#include <arpa/inet.h>
|
|
#include <netinet/in.h>
|
|
#include <sys/socket.h>
|
|
#include "monocypher-ed25519.h"
|
|
#include <ctype.h>
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <sys/stat.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#define UPD_URL OMEGA_BASE_URL "/updates"
|
|
#define UPD_DIR OMEGA_DIR "/update"
|
|
#define UPD_STATE OMEGA_DIR "/update.json"
|
|
#define FIRST_CHECK_MS (20u * 1000)
|
|
#define EVERY_MS (6u * 3600 * 1000)
|
|
|
|
// chiave pubblica di chi firma gli aggiornamenti (publish-update.sh)
|
|
static const char PUBKEY_HEX[] = "231ab09e789dc09d0248a3410dcb3d474cea89c112ddeec790a39689db9076f2";
|
|
|
|
// install_in: se il file manca ma questa cartella esiste, lo si installa;
|
|
// altrimenti si aggiorna solo se è già installato (l'attivazione la fa hen.c, dopo l'OK).
|
|
typedef struct { const char *id; const char *path; int sidecar; const char *install_in; } Target;
|
|
static const Target TARGETS[] = {
|
|
{ "omega_ui", OMEGA_HB_ROOT "/OmegaUI/OmegaUI.elf", 0, NULL },
|
|
// copie da cui la configurazione (hen.c) installa servizio e plugin
|
|
{ "omega_redirect", OMEGA_HB_ROOT "/OmegaUI/payloads/omega_redirect.elf", 0, OMEGA_HB_ROOT "/OmegaUI/payloads" },
|
|
{ "omega_onion", OMEGA_HB_ROOT "/OmegaUI/payloads/OMGA00001.elf", 0, OMEGA_HB_ROOT "/OmegaUI/payloads" },
|
|
{ "patchdl", OMEGA_HB_ROOT "/OmegaUI/payloads/patchdl.elf", 0, OMEGA_HB_ROOT "/OmegaUI/payloads" }, // aggiornamenti dei giochi (gameupd.c)
|
|
// copie attive, ovunque la configurazione le abbia messe (solo se ci sono già)
|
|
{ "omega_redirect", OMEGA_PLD_ROOT "/OmegaRedirect/omega_redirect.elf", 1, NULL }, // + .elf.json di Payload Manager
|
|
{ "omega_redirect", OMEGA_SYSROOT "/data/OnionHEN/payloads/omega_redirect.elf", 0, NULL },
|
|
{ "omega_redirect", OMEGA_SYSROOT "/data/etaHEN/payloads/omega_redirect.elf", 0, NULL },
|
|
{ "omega_redirect", OMEGA_SYSROOT "/data/etaHEN/plugins/omega_redirect.elf", 0, NULL },
|
|
{ "omega_redirect", OMEGA_SYSROOT "/data/ps5_autoloader/omega_redirect.elf", 0, NULL },
|
|
{ "omega_onion", OMEGA_SYSROOT "/data/OnionHEN/plugins/OMGA00001.elf", 0, NULL }, // pagina nel Toolbox di OnionHEN
|
|
};
|
|
#define NT (int)(sizeof TARGETS / sizeof TARGETS[0])
|
|
|
|
// esito per il thread principale
|
|
static SDL_atomic_t done_ui, done_daemon;
|
|
static char new_ver[32];
|
|
|
|
// ------------------------------------------------------------------ utilità --
|
|
static int unhex(const char *h, unsigned char *out, size_t n) {
|
|
if (strlen(h) < n * 2) return -1;
|
|
for (size_t i = 0; i < n; i++) {
|
|
unsigned v; if (sscanf(h + 2 * i, "%2x", &v) != 1) return -1;
|
|
out[i] = (unsigned char)v;
|
|
}
|
|
return 0;
|
|
}
|
|
// "2026.10.03.2" > "1.20": confronto numerico parte per parte
|
|
static void verparts(const char *v, long out[6]) {
|
|
for (int i = 0; i < 6; i++) {
|
|
out[i] = 0;
|
|
while (*v && !isdigit((unsigned char)*v)) v++;
|
|
while (isdigit((unsigned char)*v)) out[i] = out[i] * 10 + (*v++ - '0');
|
|
}
|
|
}
|
|
static int vercmp(const char *a, const char *b) {
|
|
long x[6], y[6]; verparts(a, x); verparts(b, y);
|
|
for (int i = 0; i < 6; i++) if (x[i] != y[i]) return x[i] < y[i] ? -1 : 1;
|
|
return 0;
|
|
}
|
|
|
|
// su disco prima del rename: dopo una corrente mancata il caricatore non deve
|
|
// trovare un ELF vuoto o a metà
|
|
static int fsync_path(const char *p) {
|
|
int fd = open(p, O_WRONLY); if (fd < 0) return -1;
|
|
int rc = fsync(fd); close(fd); return rc;
|
|
}
|
|
|
|
static long state_seq(void) {
|
|
char *s = file_read(UPD_STATE, 4096, NULL); if (!s) return 0;
|
|
JVal *j = json_parse(s); long q = (long)jnum(j, "seq", 0); json_free(j); free(s); return q;
|
|
}
|
|
static void state_save(long seq) {
|
|
FILE *f = fopen(UPD_STATE ".tmp", "w"); if (!f) return;
|
|
fprintf(f, "{\"seq\":%ld,\"checked\":%ld}\n", seq, (long)time(NULL)); fflush(f); fsync(fileno(f)); fclose(f);
|
|
rename(UPD_STATE ".tmp", UPD_STATE);
|
|
}
|
|
|
|
// aggiorna "checksum" e "version" nel .elf.json di Payload Manager, il resto non si tocca
|
|
static void sidecar_update(const char *elf, const char *sha, const char *ver) {
|
|
char p[400]; snprintf(p, sizeof p, "%s.json", elf);
|
|
char *s = file_read(p, 64 * 1024, NULL); if (!s) return;
|
|
static char out[70 * 1024]; size_t o = 0;
|
|
const char *keys[2] = { "\"checksum\"", "\"version\"" }; const char *vals[2] = { sha, ver };
|
|
const char *c = s;
|
|
while (*c && o < sizeof out - 200) {
|
|
int hit = -1;
|
|
for (int k = 0; k < 2; k++) if (!strncmp(c, keys[k], strlen(keys[k]))) hit = k;
|
|
if (hit < 0) { out[o++] = *c++; continue; }
|
|
const char *q = strchr(c + strlen(keys[hit]), '"'); // inizio del valore
|
|
const char *e = q ? strchr(q + 1, '"') : NULL;
|
|
if (!q || !e) { out[o++] = *c++; continue; }
|
|
o += (size_t)snprintf(out + o, sizeof out - o, "%s:\"%s\"", keys[hit], vals[hit]);
|
|
c = e + 1;
|
|
}
|
|
out[o] = 0; free(s);
|
|
char t[420]; snprintf(t, sizeof t, "%s.tmp", p);
|
|
FILE *f = fopen(t, "w"); if (!f) return;
|
|
int ok = fwrite(out, 1, o, f) == o && fflush(f) == 0 && fsync(fileno(f)) == 0;
|
|
if (fclose(f) != 0 || !ok) { unlink(t); return; }
|
|
rename(t, p);
|
|
}
|
|
|
|
// ---------------------------------------------------------------- controllo --
|
|
static int check_once(void) {
|
|
mkdir(OMEGA_DIR, 0777); mkdir(UPD_DIR, 0777);
|
|
char mpath[300], spath[300];
|
|
snprintf(mpath, sizeof mpath, "%s/manifest.json", UPD_DIR);
|
|
snprintf(spath, sizeof spath, "%s/manifest.sig", UPD_DIR);
|
|
if (omega_url_download(UPD_URL "/manifest.json", mpath, NULL, NULL, NULL) != 200) return -1;
|
|
if (omega_url_download(UPD_URL "/manifest.sig", spath, NULL, NULL, NULL) != 200) return -1;
|
|
size_t mlen; char *m = file_read(mpath, 256 * 1024, &mlen); char *sg = file_read(spath, 1024, NULL);
|
|
unsigned char sig[64], pub[32];
|
|
int ok = m && sg && unhex(sg, sig, 64) == 0 && unhex(PUBKEY_HEX, pub, 32) == 0 &&
|
|
crypto_ed25519_check(sig, pub, (const unsigned char *)m, mlen) == 0;
|
|
free(sg);
|
|
if (!ok) { omega_log("aggiornamenti: firma del manifest NON valida, ignorato"); free(m); return -2; }
|
|
JVal *j = json_parse(m); free(m);
|
|
long seq = (long)jnum(j, "seq", 0);
|
|
if (seq < state_seq()) { omega_log("aggiornamenti: manifest vecchio (seq %ld), ignorato", seq); json_free(j); return 0; }
|
|
JVal *comps = jget(j, "components");
|
|
int ui_ready = 0;
|
|
for (int t = 0; t < NT; t++) {
|
|
JVal *c = NULL;
|
|
JFOR(it, comps) if (!strcmp(jstr(it, "id", ""), TARGETS[t].id)) c = it;
|
|
if (!c) continue;
|
|
const char *ver = jstr(c, "version", ""), *sha = jstr(c, "sha256", ""), *file = jstr(c, "file", "");
|
|
struct stat st;
|
|
int fresh = 0;
|
|
if (stat(TARGETS[t].path, &st) != 0) {
|
|
if (!TARGETS[t].install_in || stat(TARGETS[t].install_in, &st) != 0 || !S_ISDIR(st.st_mode)) continue; // non installato
|
|
fresh = 1;
|
|
}
|
|
if (strlen(sha) != 64 || !file[0] || strchr(file, '/') || strstr(file, "..")) continue;
|
|
// l'app confronta la propria versione, il demone il contenuto del file
|
|
if (t == 0 && vercmp(ver, OMEGA_VERSION) <= 0) continue;
|
|
char cur[65] = ""; if (!fresh) file_sha256(TARGETS[t].path, cur);
|
|
if (!strcmp(cur, sha)) continue;
|
|
char url[512], tmp[420];
|
|
snprintf(url, sizeof url, "%s/%s", UPD_URL, file);
|
|
snprintf(tmp, sizeof tmp, "%s.new", TARGETS[t].path);
|
|
omega_log("aggiornamenti: scarico %s %s", TARGETS[t].id, ver);
|
|
if (omega_url_download(url, tmp, NULL, NULL, NULL) != 200) { unlink(tmp); continue; }
|
|
char got[65] = ""; file_sha256(tmp, got);
|
|
if (strcmp(got, sha)) { omega_log("aggiornamenti: sha256 diverso per %s, scartato", TARGETS[t].id); unlink(tmp); continue; }
|
|
chmod(tmp, 0755);
|
|
if (fsync_path(tmp) != 0) { omega_log("aggiornamenti: %s non scritto su disco, scartato", tmp); unlink(tmp); continue; }
|
|
char old[420]; snprintf(old, sizeof old, "%s.old", TARGETS[t].path);
|
|
unlink(old); if (!fresh) link(TARGETS[t].path, old); // la versione precedente resta come .old
|
|
if (rename(tmp, TARGETS[t].path) != 0) { omega_log("aggiornamenti: sostituzione di %s fallita", TARGETS[t].path); unlink(tmp); continue; }
|
|
if (TARGETS[t].sidecar) sidecar_update(TARGETS[t].path, sha, ver);
|
|
|
|
omega_log("aggiornamenti: %s aggiornato a %s", TARGETS[t].id, ver);
|
|
if (t == 0) { snprintf(new_ver, sizeof new_ver, "%s", ver); ui_ready = 1; }
|
|
else if (!strcmp(TARGETS[t].id, "omega_redirect") && !TARGETS[t].install_in) SDL_AtomicSet(&done_daemon, 1);
|
|
}
|
|
json_free(j);
|
|
state_save(seq);
|
|
// il riavvio si propone solo a fine giro: prima devono finire anche servizio e plugin
|
|
if (ui_ready) SDL_AtomicSet(&done_ui, 1);
|
|
return 0;
|
|
}
|
|
|
|
// ------------------------------------------------------------- servizio --
|
|
// Il file del servizio si aggiorna, ma quello in esecuzione resterebbe il vecchio
|
|
// fino al riavvio della console: lo si chiude (/v1/quit) e si avvia il nuovo.
|
|
// Lo stesso se all'avvio di Omega il servizio configurato non risponde.
|
|
static SDL_atomic_t svc_result; // 0 niente, 1 riavviato, 2 serve il riavvio della console
|
|
static Uint32 fail_since; // primo ping senza risposta (0 = risponde); solo il thread degli aggiornamenti
|
|
#define WEDGED_MS (180u * 1000)
|
|
static int svc_alive(void) { char b[256]; return omega_http(HTTP_GET, "http://127.0.0.1:9095/v1/ping", NULL, NULL, b, sizeof b) == 200; }
|
|
// Servizio bloccato senza file del pid (versioni vecchie): lo si cerca per nome
|
|
// tra i processi (sysctl kern.proc). Si avanza con ki_structsize di ogni voce.
|
|
#ifdef PS5
|
|
#include <sys/sysctl.h>
|
|
#include <sys/user.h>
|
|
static int kill_by_name(const char *needle) {
|
|
int mib[3] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC }; size_t len = 0;
|
|
if (sysctl(mib, 3, NULL, &len, NULL, 0) != 0 || !len) return 0;
|
|
len += 64 * 1024;
|
|
char *b = malloc(len); if (!b) return 0;
|
|
int n = 0;
|
|
if (sysctl(mib, 3, b, &len, NULL, 0) == 0) {
|
|
for (size_t off = 0; off + sizeof(struct kinfo_proc) <= len + sizeof(struct kinfo_proc); ) {
|
|
struct kinfo_proc *kp = (struct kinfo_proc *)(b + off);
|
|
int sz = kp->ki_structsize;
|
|
if (sz <= 0 || off + (size_t)sz > len) break;
|
|
char comm[COMMLEN + 1]; memcpy(comm, kp->ki_comm, COMMLEN); comm[COMMLEN] = 0;
|
|
if (strcasestr(comm, "omega") || strcasestr(comm, ".elf") || strcasestr(comm, "redirect")) omega_log("processo %d: %s", (int)kp->ki_pid, comm);
|
|
if (strstr(comm, needle) && kp->ki_pid != getpid()) {
|
|
int rc = kill(kp->ki_pid, SIGKILL);
|
|
omega_log("servizio bloccato: chiuso %s (pid %d, %d)", comm, (int)kp->ki_pid, rc);
|
|
n++;
|
|
}
|
|
off += (size_t)sz;
|
|
}
|
|
}
|
|
free(b);
|
|
return n;
|
|
}
|
|
// il pid del file è ancora il servizio? (il file può essere rimasto da un
|
|
// avvio precedente e il numero ora appartenere a un altro processo)
|
|
static int pid_is(int pid, const char *needle) {
|
|
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid }; struct kinfo_proc kp; size_t len = sizeof kp;
|
|
if (sysctl(mib, 4, &kp, &len, NULL, 0) != 0 || len < sizeof kp) return 0;
|
|
char comm[COMMLEN + 1]; memcpy(comm, kp.ki_comm, COMMLEN); comm[COMMLEN] = 0;
|
|
return strstr(comm, needle) != NULL;
|
|
}
|
|
#else
|
|
static int kill_by_name(const char *needle) { (void)needle; return 0; }
|
|
static int pid_is(int pid, const char *needle) { (void)pid; (void)needle; return 0; }
|
|
#endif
|
|
|
|
// la porta 9095 accetta connessioni (anche se il servizio non risponde)
|
|
static int svc_port_busy(void) {
|
|
#ifdef PS5
|
|
int s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) return 0;
|
|
struct sockaddr_in a; memset(&a, 0, sizeof a);
|
|
a.sin_family = AF_INET; a.sin_port = htons(9095); a.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
int busy = connect(s, (struct sockaddr *)&a, sizeof a) == 0;
|
|
close(s);
|
|
return busy;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
static int port_up(int port) {
|
|
#ifdef PS5
|
|
int s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) return 0;
|
|
struct sockaddr_in a; memset(&a, 0, sizeof a);
|
|
a.sin_family = AF_INET; a.sin_port = htons((unsigned short)port); a.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
int ok = connect(s, (struct sockaddr *)&a, sizeof a) == 0;
|
|
close(s);
|
|
return ok;
|
|
#else
|
|
(void)port; return 1;
|
|
#endif
|
|
}
|
|
static int elfldr_up(void) {
|
|
#ifdef PS5
|
|
int s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) return 0;
|
|
struct sockaddr_in a; memset(&a, 0, sizeof a);
|
|
a.sin_family = AF_INET; a.sin_port = htons(9021); a.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
int ok = connect(s, (struct sockaddr *)&a, sizeof a) == 0;
|
|
close(s);
|
|
return ok;
|
|
#else
|
|
return 1;
|
|
#endif
|
|
}
|
|
static int svc_stale(void) {
|
|
char b[256], path[300]; struct stat st;
|
|
if (omega_http(HTTP_GET, "http://127.0.0.1:9095/v1/ping", NULL, NULL, b, sizeof b) != 200) return 0;
|
|
if (!hen_daemon_path(path, sizeof path) || stat(path, &st) != 0) return 0;
|
|
const char *p = strstr(b, "\"started\":");
|
|
long started = p ? atol(p + 10) : 0; // versioni vecchie: niente "started"
|
|
return (long)st.st_mtime > started + 5;
|
|
}
|
|
static int svc_start(int stop_old) {
|
|
char path[300], err[200], b[256];
|
|
if (!hen_daemon_path(path, sizeof path)) return 0;
|
|
// Non risponde. Si chiude di forza (pid scritto dal servizio) solo se la porta
|
|
// rifiuta le connessioni (processo bloccato o che non ascolta) o se non
|
|
// risponde da almeno 3 minuti: un servizio solo occupato non si uccide. Mai
|
|
// mentre la console sta per spegnersi.
|
|
if (!svc_alive()) {
|
|
if (power_pending()) return 0;
|
|
int busy = svc_port_busy();
|
|
if (busy && (!fail_since || SDL_GetTicks() - fail_since < WEDGED_MS)) return 0;
|
|
char *pf = file_read(OMEGA_DIR "/omega_redirect.pid", 32, NULL);
|
|
int pid = pf ? atoi(pf) : 0; free(pf);
|
|
if (pid > 1 && pid != (int)getpid() && pid_is(pid, "omega_redirect")) {
|
|
int rc = kill(pid, SIGKILL);
|
|
omega_log("servizio bloccato: chiuso il processo %d (%d)", pid, rc);
|
|
for (int i = 0; i < 20 && svc_port_busy(); i++) SDL_Delay(250);
|
|
}
|
|
if (!svc_alive() && kill_by_name("omega_redirect")) SDL_Delay(1500);
|
|
}
|
|
if (svc_alive()) {
|
|
if (!stop_old) return 0;
|
|
if (!elfldr_up() && !port_up(8080)) return 2; // nessun caricatore: il vecchio resta fino al riavvio
|
|
if (omega_http(HTTP_POST, "http://127.0.0.1:9095/v1/quit", NULL, "{}", b, sizeof b) != 200) return 2; // servizio vecchio, senza /v1/quit
|
|
for (int i = 0; i < 20 && svc_alive(); i++) SDL_Delay(250);
|
|
}
|
|
if (payload_run_service(path, err, sizeof err) != 0) { omega_log("servizio: avvio non riuscito (%s)", err); return 2; }
|
|
for (int i = 0; i < 16 && !svc_alive(); i++) SDL_Delay(250);
|
|
int ok = svc_alive();
|
|
omega_log("servizio: %s %s", path, ok ? "avviato" : "non risponde dopo l'avvio");
|
|
return ok ? 1 : 2;
|
|
}
|
|
|
|
static int upd_thread(void *arg) {
|
|
(void)arg;
|
|
SDL_Delay(FIRST_CHECK_MS);
|
|
Uint32 next_check = 0;
|
|
int restarts = 0; Uint32 restart_window = SDL_GetTicks();
|
|
for (;;) {
|
|
// riposo, riavvio o spegnimento in arrivo: il servizio non si tocca
|
|
if (power_pending()) { SDL_Delay(5000); continue; }
|
|
// servizio configurato ma fermo (crash o avvio mancato): si riavvia, al massimo 5 volte l'ora
|
|
if (SDL_GetTicks() - restart_window > 3600u * 1000) { restarts = 0; restart_window = SDL_GetTicks(); }
|
|
int alive = svc_alive();
|
|
if (alive) fail_since = 0; else if (!fail_since) fail_since = SDL_GetTicks() | 1;
|
|
if (restarts < 5 && !alive) { int rr = svc_start(0); if (rr) { restarts++; if (rr == 1) omega_log("servizio: non rispondeva, riavviato"); } }
|
|
else if (restarts < 5 && svc_stale()) { omega_log("servizio: gira una versione vecchia, lo sostituisco"); restarts++; svc_start(1); }
|
|
if (!next_check || SDL_GetTicks() >= next_check) {
|
|
int r = check_once();
|
|
if (SDL_AtomicGet(&done_daemon)) SDL_AtomicSet(&svc_result, svc_start(1));
|
|
if (r < 0 && r != -2) omega_log("aggiornamenti: server Omega non raggiungibile (%d)", r);
|
|
next_check = SDL_GetTicks() + EVERY_MS;
|
|
}
|
|
SDL_Delay(60 * 1000);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void update_init(void) {
|
|
mkdir(OMEGA_HB_ROOT "/OmegaUI/payloads", 0777); // chi viene da versioni vecchie riceve qui le copie
|
|
SDL_CreateThread(upd_thread, "update", NULL);
|
|
}
|
|
|
|
// -------------------------------------------------------- thread principale --
|
|
static void restart_now(int idx, void *ud) {
|
|
(void)idx; (void)ud;
|
|
#ifdef PS5
|
|
// websrv chiude l'app in primo piano (Omega) e avvia la versione nuova
|
|
unsigned char b[16];
|
|
if (omega_url_peek(WEBSRV_URL "/hbldr?pipe=0&daemon=0&path=%2Fdata%2Fhomebrew%2FOmegaUI%2FOmegaUI.elf&cwd=%2Fdata%2Fhomebrew%2FOmegaUI", b, sizeof b) < 0)
|
|
set_msg(_("Riavvia Omega per usare la nuova versione"), 0);
|
|
#else
|
|
set_msg("(desktop) riavvio non disponibile", 0);
|
|
#endif
|
|
}
|
|
|
|
void update_tick(void) {
|
|
int sr = SDL_AtomicGet(&svc_result);
|
|
if (sr && SDL_AtomicGet(&done_daemon) && g_scene == SC_HOME) {
|
|
SDL_AtomicSet(&done_daemon, 0); SDL_AtomicSet(&svc_result, 0);
|
|
if (sr == 1) toast(IC_DOWNLOAD, NULL, 0, _("Servizio Omega aggiornato"), _("Musica e App mobile usano gi\xC3\xA0 la nuova versione"));
|
|
else toast(IC_DOWNLOAD, NULL, 0, _("Servizio Omega aggiornato"), _("Attivo dal prossimo riavvio della console"));
|
|
}
|
|
if (SDL_AtomicGet(&done_ui) && g_scene == SC_HOME && ov_depth() == 0 && !install_busy()) {
|
|
SDL_AtomicSet(&done_ui, 0);
|
|
static char q[512];
|
|
snprintf(q, sizeof q, _("Omega %s \xC3\xA8 stato scaricato e installato. Riavviare ora per usarlo?"), new_ver);
|
|
confirm_open(q, _("Riavvia"), restart_now, NULL);
|
|
}
|
|
}
|