Add optional AppInstallAll mode with FW 12+ override

This commit is contained in:
Roman Tarasov committed 2026-04-04 21:53:27 +03:00
1 parent 4e9afa7885
commit 6df8a472e3
10 files changed
+597 -26

No files matched your search

+1
View File
@@ -43,6 +43,7 @@ Supported keys (all optional):
- `quiet_mode=1|0` (`1` suppresses plain informational popups but keeps rich toasts; default is `0`)
- `mount_read_only=1|0` (default: `1`)
- `force_mount=1|0` (mounting even damaged file systems; default: `0`)
- `app_install_all=1|0` (`1` stages new titles and submits them through `sceAppInstUtilAppInstallAll`; default: `0` on FW below `12.00`, forced `1` on FW `12.00+`)
- `image_ro=<image_filename>` (repeatable; force read-only mode for this image filename)
- `image_rw=<image_filename>` (repeatable; force read-write mode for this image filename)
- `image_sector=<image_filename>:<sector_size>` (repeatable; force sector size for this image filename)
+6
View File
@@ -24,6 +24,12 @@ mount_read_only=1
# 0/false/no/off -> default mount options (default)
force_mount=0
# Use batch registration for newly staged titles
# 1/true/yes/on -> submit queued installs through sceAppInstUtilAppInstallAll
# 0/false/no/off -> register each title directly (default on FW below 12.00)
# Forced on automatically on firmware 12.00 and newer
app_install_all=0
# Per-image mount mode override by file name (can be repeated many times):
# image_ro=<image_filename>
# image_rw=<image_filename>
+26
View File
@@ -0,0 +1,26 @@
#ifndef SM_INSTALL_QUEUE_H
#define SM_INSTALL_QUEUE_H
#include <stdbool.h>
#include <stdint.h>
typedef struct scan_candidate scan_candidate_t;
// Return whether a title is already waiting for async batch installation.
bool is_title_install_pending(const char *title_id);
// Return the next pending-install wake deadline in monotonic microseconds.
uint64_t sm_install_next_wake_us(uint64_t now_us);
// Poll async batch installs for success/timeout completion only.
void sm_install_poll_pending(void);
// Poll async batch installs for success/timeout completion and auto-submit
// queued installs when possible.
void sm_install_service_pending(void);
// Queue a staged title for the next batch install request.
bool sm_install_queue_candidate(const scan_candidate_t *candidate,
bool has_src_snd0);
// Submit all queued titles when no batch install is currently pending.
bool sm_install_submit_queued(void);
// Record retry state for every queued title after a batch submit failure.
void sm_install_note_submit_failure(void);
#endif
+1
View File
@@ -37,6 +37,7 @@ typedef struct {
// --- SDK Imports ---
int sceAppInstUtilInitialize(void);
int sceAppInstUtilAppInstallAll(void);
int sceAppInstUtilAppInstallTitleDir(const char *title_id,
const char *install_path, void *reserved);
int sceKernelGetAppInfo(pid_t pid, app_info_t *info);
+2
View File
@@ -106,6 +106,8 @@ typedef struct runtime_config {
bool quiet_mode;
bool mount_read_only;
bool force_mount;
bool app_install_all_enabled;
bool app_install_all_forced;
bool backport_fakelib_enabled;
bool kstuff_game_auto_toggle;
bool kstuff_crash_detection_enabled;
+43 -2
View File
@@ -96,6 +96,8 @@ static bool lookup_kstuff_delay_override_in_file(const char *path,
static bool upsert_kstuff_delay_override_in_file(const char *path,
const char *title_id,
uint32_t delay_seconds);
static uint32_t runtime_firmware_major_version(void);
static void apply_firmware_runtime_overrides(runtime_config_state_t *state);
static char *trim_ascii(char *s) {
while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n')
@@ -149,6 +151,25 @@ static bool parse_ini_line(char *line, char **key_out, char **value_out) {
return true;
}
static uint32_t runtime_firmware_major_version(void) {
uint32_t fw = kernel_get_fw_version();
uint32_t major_bcd = (fw >> 24) & 0xFFu;
return ((major_bcd >> 4) & 0xFu) * 10u + (major_bcd & 0xFu);
}
static void apply_firmware_runtime_overrides(runtime_config_state_t *state) {
if (!state)
return;
if (runtime_firmware_major_version() >= 12u) {
state->cfg.app_install_all_enabled = true;
state->cfg.app_install_all_forced = true;
return;
}
state->cfg.app_install_all_forced = false;
}
static const runtime_config_state_t *active_runtime_state(void) {
return &g_runtime_state_slots[atomic_load_explicit(&g_runtime_state_active_index,
memory_order_acquire)];
@@ -228,6 +249,8 @@ static void init_runtime_config_defaults(runtime_config_state_t *state) {
state->cfg.quiet_mode = false;
state->cfg.mount_read_only = (IMAGE_MOUNT_READ_ONLY != 0);
state->cfg.force_mount = false;
state->cfg.app_install_all_enabled = false;
state->cfg.app_install_all_forced = false;
state->cfg.backport_fakelib_enabled = true;
state->cfg.kstuff_game_auto_toggle = true;
state->cfg.kstuff_crash_detection_enabled = false;
@@ -249,6 +272,7 @@ static void init_runtime_config_defaults(runtime_config_state_t *state) {
memset(state->image_mode_rules, 0, sizeof(state->image_mode_rules));
clear_kstuff_title_rules(state);
init_runtime_scan_paths_defaults(state);
apply_firmware_runtime_overrides(state);
}
static config_file_stamp_t read_config_file_stamp(void) {
@@ -283,6 +307,8 @@ static void apply_reloadable_runtime_fields(runtime_config_state_t *dst,
dst->cfg.quiet_mode = src->cfg.quiet_mode;
dst->cfg.mount_read_only = src->cfg.mount_read_only;
dst->cfg.force_mount = src->cfg.force_mount;
dst->cfg.app_install_all_enabled = src->cfg.app_install_all_enabled;
dst->cfg.app_install_all_forced = src->cfg.app_install_all_forced;
dst->cfg.backport_fakelib_enabled = src->cfg.backport_fakelib_enabled;
dst->cfg.kstuff_game_auto_toggle = src->cfg.kstuff_game_auto_toggle;
dst->cfg.kstuff_crash_detection_enabled =
@@ -1119,6 +1145,16 @@ static config_load_status_t load_runtime_config_state(runtime_config_state_t *st
continue;
}
if (strcasecmp(key, "app_install_all") == 0) {
if (!parse_bool_ini(value, &bval)) {
log_debug(" [CFG] invalid bool at line %d: %s=%s", line_no, key, value);
continue;
}
state->cfg.app_install_all_enabled = bval;
state->cfg.app_install_all_forced = false;
continue;
}
if (strcasecmp(key, "image_ro") == 0 ||
strcasecmp(key, "image_rw") == 0) {
bool rule_read_only = (strcasecmp(key, "image_ro") == 0);
@@ -1331,6 +1367,8 @@ static config_load_status_t load_runtime_config_state(runtime_config_state_t *st
log_debug(" [CFG] recursive_scan=1 is deprecated; forcing scan_depth=2");
}
apply_firmware_runtime_overrides(state);
int image_rule_count = 0;
for (int k = 0; k < MAX_IMAGE_MODE_RULES; k++) {
if (state->image_mode_rules[k].valid)
@@ -1343,7 +1381,8 @@ static config_load_status_t load_runtime_config_state(runtime_config_state_t *st
kstuff_delay_rule_count++;
}
log_debug(" [CFG] loaded: debug=%d quiet=%d ro=%d force=%d scan_depth=%u "
log_debug(" [CFG] loaded: debug=%d quiet=%d ro=%d force=%d "
"app_install_all=%d app_install_all_forced=%d scan_depth=%u "
"legacy_recursive_scan_forced=%d backport_fakelib=%d "
"kstuff_game_auto_toggle=%d kstuff_crash_detection=%d "
"kstuff_pause_delay_image_s=%u kstuff_pause_delay_direct_s=%u "
@@ -1353,7 +1392,9 @@ static config_load_status_t load_runtime_config_state(runtime_config_state_t *st
"kstuff_no_pause=%d kstuff_delay_rules=%d",
state->cfg.debug_enabled ? 1 : 0, state->cfg.quiet_mode ? 1 : 0,
state->cfg.mount_read_only ? 1 : 0,
state->cfg.force_mount ? 1 : 0, state->cfg.scan_depth,
state->cfg.force_mount ? 1 : 0,
state->cfg.app_install_all_enabled ? 1 : 0,
state->cfg.app_install_all_forced ? 1 : 0, state->cfg.scan_depth,
state->cfg.legacy_recursive_scan_forced ? 1 : 0,
state->cfg.backport_fakelib_enabled ? 1 : 0,
state->cfg.kstuff_game_auto_toggle ? 1 : 0,
+49 -7
View File
@@ -1,6 +1,8 @@
#include "sm_platform.h"
#include "sm_config_mount.h"
#include "sm_runtime.h"
#include "sm_install.h"
#include "sm_install_queue.h"
#include "sm_types.h"
#include "sm_game_cache.h"
#include "sm_log.h"
@@ -85,7 +87,9 @@ static bool copy_sce_sys_to_appmeta(const char *src_sce_sys,
// --- Install/Remount Action ---
static bool mount_and_install(const char *src_path, const char *title_id,
const char *title_name, bool is_remount,
bool should_register) {
bool should_register,
bool use_app_install_all,
bool *has_src_snd0_out) {
char user_appmeta_dir[MAX_PATH];
char user_app_dir[MAX_PATH];
char user_sce_sys[MAX_PATH];
@@ -212,12 +216,20 @@ static bool mount_and_install(const char *src_path, const char *title_id,
return true;
}
// REGISTER
if (!metadata_restaged) {
snprintf(src_snd0, sizeof(src_snd0), "%s/sce_sys/snd0.at9", src_path);
has_src_snd0 = path_exists(src_snd0);
}
if (use_app_install_all) {
if (has_src_snd0_out)
*has_src_snd0_out = has_src_snd0;
log_debug(" [REG] Prepared for batch install: %s (%s)", title_name,
title_id);
return true;
}
// REGISTER
mark_register_attempted(title_id);
int res = sceAppInstUtilAppInstallTitleDir(title_id, APP_BASE "/", 0);
sceKernelUsleep(200000);
@@ -253,23 +265,50 @@ static bool mount_and_install(const char *src_path, const char *title_id,
// --- Execution (per discovered candidate) ---
void process_scan_candidates(const scan_candidate_t *candidates,
int candidate_count) {
sm_install_poll_pending();
for (int i = 0; i < candidate_count; i++) {
if (should_stop_requested())
return;
const scan_candidate_t *c = &candidates[i];
bool has_src_snd0 = false;
bool use_app_install_all =
runtime_config()->app_install_all_enabled && !c->in_app_db;
if (c->installed) {
log_debug(" [ACTION] Remounting: %s", c->title_name);
} else {
log_debug(" [ACTION] Installing: %s (%s)", c->title_name, c->title_id);
notify_system_info("Installing: %s (%s)...", c->title_name,
c->title_id);
if (!use_app_install_all) {
notify_system_info("Installing: %s (%s)...", c->title_name,
c->title_id);
}
}
if (mount_and_install(c->path, c->title_id, c->title_name, c->installed,
!c->in_app_db)) {
clear_failed_mount_attempts(c->title_id);
cache_game_entry(c->path, c->title_id, c->title_name);
!c->in_app_db, use_app_install_all,
&has_src_snd0)) {
if (use_app_install_all) {
if (!sm_install_queue_candidate(c, has_src_snd0)) {
log_debug(" [REG] Failed to queue staged install: %s (%s)",
c->title_name, c->title_id);
if (should_stop_requested())
return;
uint8_t failed_attempts = bump_failed_mount_attempts(c->title_id);
if (failed_attempts == MAX_FAILED_MOUNT_ATTEMPTS) {
log_debug(" [RETRY] limit reached (%u/%u): %s (%s)",
(unsigned)failed_attempts,
(unsigned)MAX_FAILED_MOUNT_ATTEMPTS, c->title_name,
c->title_id);
}
continue;
}
clear_failed_mount_attempts(c->title_id);
} else {
clear_failed_mount_attempts(c->title_id);
cache_game_entry(c->path, c->title_id, c->title_name);
}
} else {
if (should_stop_requested())
return;
@@ -283,4 +322,7 @@ void process_scan_candidates(const scan_candidate_t *candidates,
}
}
}
if (!sm_install_submit_queued())
sm_install_note_submit_failure();
}
+436
View File
@@ -0,0 +1,436 @@
#include "sm_platform.h"
#include "sm_install_queue.h"
#include "sm_types.h"
#include "sm_appdb.h"
#include "sm_config_mount.h"
#include "sm_game_cache.h"
#include "sm_limits.h"
#include "sm_log.h"
#include "sm_time.h"
#include "sm_title_state.h"
#define INSTALL_WAIT_TIMEOUT_US (300ull * 1000000ull)
#define INSTALL_POLL_INTERVAL_US (2ull * 1000000ull)
#define INSTALL_SUBMIT_RETRY_US (2ull * 1000000ull)
typedef enum {
INSTALL_TRACK_NONE = 0,
INSTALL_TRACK_QUEUED,
INSTALL_TRACK_SUBMITTED,
} install_track_state_t;
typedef struct {
char title_id[MAX_TITLE_ID];
char title_name[MAX_TITLE_NAME];
char source_path[MAX_PATH];
uint64_t requested_at_us;
bool has_src_snd0;
install_track_state_t state;
} pending_install_entry_t;
static pending_install_entry_t g_pending_installs[MAX_PENDING];
static uint64_t g_pending_install_poll_due_us = 0;
static uint64_t g_queued_install_submit_due_us = 0;
static int g_tracked_install_count = 0;
static int g_submitted_install_count = 0;
static bool g_queued_install_batch_announced = false;
static bool g_queued_install_submit_failure_notified = false;
static int count_queued_installs(void);
static uint8_t note_pending_install_failure(const pending_install_entry_t *entry);
static void drop_queued_install_entry(pending_install_entry_t *entry);
static bool install_queue_active(void) {
return runtime_config()->app_install_all_enabled || g_tracked_install_count > 0;
}
static pending_install_entry_t *find_pending_install_entry(
const char *title_id) {
if (!title_id || title_id[0] == '\0')
return NULL;
for (int i = 0; i < MAX_PENDING; i++) {
if (g_pending_installs[i].state == INSTALL_TRACK_NONE)
continue;
if (strcmp(g_pending_installs[i].title_id, title_id) == 0)
return &g_pending_installs[i];
}
return NULL;
}
static pending_install_entry_t *reserve_pending_install_entry(
const char *title_id) {
pending_install_entry_t *entry = find_pending_install_entry(title_id);
if (entry)
return entry;
for (int i = 0; i < MAX_PENDING; i++) {
if (g_pending_installs[i].state == INSTALL_TRACK_NONE)
return &g_pending_installs[i];
}
return NULL;
}
static uint64_t next_pending_install_timeout_us(void) {
if (g_submitted_install_count <= 0)
return 0;
uint64_t next_deadline = 0;
for (int i = 0; i < MAX_PENDING; i++) {
const pending_install_entry_t *entry = &g_pending_installs[i];
if (entry->state != INSTALL_TRACK_SUBMITTED || entry->requested_at_us == 0)
continue;
uint64_t deadline_us = entry->requested_at_us + INSTALL_WAIT_TIMEOUT_US;
if (next_deadline == 0 || deadline_us < next_deadline)
next_deadline = deadline_us;
}
return next_deadline;
}
static void schedule_pending_install_poll(uint64_t now_us) {
if (g_submitted_install_count <= 0) {
g_pending_install_poll_due_us = 0;
return;
}
g_pending_install_poll_due_us =
now_us == 0 ? 1 : now_us + INSTALL_POLL_INTERVAL_US;
}
static void schedule_queued_install_submit(uint64_t now_us) {
if (count_queued_installs() <= 0 || g_submitted_install_count > 0) {
g_queued_install_submit_due_us = 0;
return;
}
g_queued_install_submit_due_us = now_us == 0 ? 1 : now_us;
}
static void schedule_queued_install_submit_retry(uint64_t now_us) {
if (count_queued_installs() <= 0 || g_submitted_install_count > 0) {
g_queued_install_submit_due_us = 0;
return;
}
g_queued_install_submit_due_us =
now_us == 0 ? 1 : now_us + INSTALL_SUBMIT_RETRY_US;
}
static void clear_pending_install_entry(pending_install_entry_t *entry) {
if (!entry)
return;
if (entry->state == INSTALL_TRACK_SUBMITTED && g_submitted_install_count > 0)
g_submitted_install_count--;
if (entry->state != INSTALL_TRACK_NONE && g_tracked_install_count > 0)
g_tracked_install_count--;
memset(entry, 0, sizeof(*entry));
if (g_submitted_install_count <= 0)
g_pending_install_poll_due_us = 0;
if (g_tracked_install_count <= 0) {
g_queued_install_submit_due_us = 0;
g_queued_install_batch_announced = false;
g_queued_install_submit_failure_notified = false;
}
}
bool is_title_install_pending(const char *title_id) {
return find_pending_install_entry(title_id) != NULL;
}
static void append_batch_install_line(char *message, size_t message_size,
const char *title_name,
const char *title_id, int *shown_count) {
if (!message || message_size == 0 || !title_name || !title_id || !shown_count)
return;
size_t used = strlen(message);
if (used >= message_size)
return;
int written = snprintf(message + used, message_size - used, "\n%s (%s)",
title_name, title_id);
if (written < 0 || (size_t)written >= message_size - used)
return;
(*shown_count)++;
}
bool sm_install_queue_candidate(const scan_candidate_t *candidate,
bool has_src_snd0) {
if (!candidate || candidate->title_id[0] == '\0' ||
candidate->title_name[0] == '\0' || candidate->path[0] == '\0') {
return false;
}
if (!runtime_config()->app_install_all_enabled)
return false;
bool was_queue_empty = (count_queued_installs() <= 0);
pending_install_entry_t *entry =
reserve_pending_install_entry(candidate->title_id);
if (!entry) {
log_debug(" [REG] install queue full, cannot queue %s (%s)",
candidate->title_name, candidate->title_id);
return false;
}
install_track_state_t previous_state = entry->state;
if (previous_state == INSTALL_TRACK_SUBMITTED)
return true;
memset(entry, 0, sizeof(*entry));
(void)strlcpy(entry->title_id, candidate->title_id, sizeof(entry->title_id));
(void)strlcpy(entry->title_name, candidate->title_name,
sizeof(entry->title_name));
(void)strlcpy(entry->source_path, candidate->path, sizeof(entry->source_path));
entry->has_src_snd0 = has_src_snd0;
entry->state = INSTALL_TRACK_QUEUED;
if (previous_state == INSTALL_TRACK_NONE)
g_tracked_install_count++;
if (was_queue_empty) {
g_queued_install_batch_announced = false;
g_queued_install_submit_failure_notified = false;
}
schedule_queued_install_submit(monotonic_time_us());
return true;
}
static void finalize_pending_install_success(pending_install_entry_t *entry) {
if (!entry)
return;
log_debug(" [REG] Installed: %s (%s)", entry->title_name, entry->title_id);
notify_game_installed_rich(entry->title_id);
if (entry->has_src_snd0) {
int snd0_updates = update_snd0info(entry->title_id);
if (snd0_updates >= 0)
log_debug(" [DB] snd0info updated rows=%d", snd0_updates);
}
cache_game_entry(entry->source_path, entry->title_id, entry->title_name);
clear_failed_mount_attempts(entry->title_id);
clear_pending_install_entry(entry);
}
static void finalize_pending_install_timeout(pending_install_entry_t *entry) {
if (!entry)
return;
log_debug(" [REG] Install timeout after 5 minutes: %s (%s)",
entry->title_name, entry->title_id);
notify_system("Install failed: %s (%s)\nTimed out after 5 minutes",
entry->title_name, entry->title_id);
(void)note_pending_install_failure(entry);
clear_pending_install_entry(entry);
}
static void poll_pending_installs(void) {
if (g_submitted_install_count <= 0)
return;
invalidate_app_db_title_cache();
const struct AppDbTitleList *app_db_titles = NULL;
bool app_db_titles_ready = get_app_db_title_list_cached(&app_db_titles);
uint64_t now_us = monotonic_time_us();
if (!app_db_titles_ready)
log_debug(" [DB] app.db title list unavailable while polling installs");
for (int i = 0; i < MAX_PENDING; i++) {
pending_install_entry_t *entry = &g_pending_installs[i];
if (entry->state != INSTALL_TRACK_SUBMITTED)
continue;
if (app_db_titles_ready &&
app_db_title_list_contains(app_db_titles, entry->title_id)) {
finalize_pending_install_success(entry);
continue;
}
if (now_us != 0 && entry->requested_at_us != 0 &&
now_us >= entry->requested_at_us &&
now_us - entry->requested_at_us >= INSTALL_WAIT_TIMEOUT_US) {
finalize_pending_install_timeout(entry);
}
}
schedule_pending_install_poll(now_us);
if (g_submitted_install_count == 0)
schedule_queued_install_submit(now_us);
}
uint64_t sm_install_next_wake_us(uint64_t now_us) {
if (!install_queue_active())
return 0;
if (g_submitted_install_count <= 0)
return g_queued_install_submit_due_us;
uint64_t next_wake_us = next_pending_install_timeout_us();
if (g_pending_install_poll_due_us != 0 &&
(next_wake_us == 0 || g_pending_install_poll_due_us < next_wake_us)) {
next_wake_us = g_pending_install_poll_due_us;
}
if (g_queued_install_submit_due_us != 0 &&
(next_wake_us == 0 || g_queued_install_submit_due_us < next_wake_us)) {
next_wake_us = g_queued_install_submit_due_us;
}
if (next_wake_us == 0)
next_wake_us = now_us == 0 ? 1 : now_us + INSTALL_POLL_INTERVAL_US;
return next_wake_us;
}
void sm_install_poll_pending(void) {
poll_pending_installs();
}
void sm_install_service_pending(void) {
if (!install_queue_active())
return;
sm_install_poll_pending();
if (count_queued_installs() > 0 && !sm_install_submit_queued())
sm_install_note_submit_failure();
}
static void log_batch_submit_retry_limit(const pending_install_entry_t *entry) {
uint8_t failed_attempts = get_failed_mount_attempts(entry->title_id);
if (failed_attempts == MAX_FAILED_MOUNT_ATTEMPTS) {
log_debug(" [RETRY] limit reached (%u/%u): %s (%s)",
(unsigned)failed_attempts,
(unsigned)MAX_FAILED_MOUNT_ATTEMPTS, entry->title_name,
entry->title_id);
}
}
static int count_queued_installs(void) {
int queued_count = g_tracked_install_count - g_submitted_install_count;
return queued_count > 0 ? queued_count : 0;
}
static void log_queued_install_batch(void) {
int queued_count = count_queued_installs();
if (queued_count <= 0)
return;
log_debug(" [REG] Batch install request contains %d title(s):", queued_count);
for (int i = 0; i < MAX_PENDING; i++) {
const pending_install_entry_t *entry = &g_pending_installs[i];
if (entry->state != INSTALL_TRACK_QUEUED)
continue;
log_debug(" - %s (%s)", entry->title_name, entry->title_id);
}
}
static void notify_queued_install_batch(void) {
int queued_count = count_queued_installs();
if (queued_count <= 0 || g_queued_install_batch_announced)
return;
char message[3075];
int shown_count = 0;
snprintf(message, sizeof(message), "Batch install queued (%d):", queued_count);
for (int i = 0; i < MAX_PENDING; i++) {
const pending_install_entry_t *entry = &g_pending_installs[i];
if (entry->state != INSTALL_TRACK_QUEUED)
continue;
append_batch_install_line(message, sizeof(message), entry->title_name,
entry->title_id, &shown_count);
}
if (shown_count < queued_count) {
size_t used = strlen(message);
if (used < sizeof(message)) {
(void)snprintf(message + used, sizeof(message) - used, "\n... and %d more",
queued_count - shown_count);
}
}
notify_system_info("%s", message);
g_queued_install_batch_announced = true;
}
static uint8_t note_pending_install_failure(const pending_install_entry_t *entry) {
if (!entry)
return 0;
uint8_t failed_attempts = bump_failed_mount_attempts(entry->title_id);
log_batch_submit_retry_limit(entry);
return failed_attempts;
}
static void drop_queued_install_entry(pending_install_entry_t *entry) {
if (!entry || entry->state != INSTALL_TRACK_QUEUED)
return;
log_debug(" [REG] Dropping queued install after retry limit: %s (%s)",
entry->title_name, entry->title_id);
clear_pending_install_entry(entry);
}
bool sm_install_submit_queued(void) {
int queued_count = count_queued_installs();
if (queued_count <= 0)
return true;
if (g_submitted_install_count > 0)
return true;
uint64_t now_us = monotonic_time_us();
if (g_queued_install_submit_due_us != 0 && now_us != 0 &&
now_us < g_queued_install_submit_due_us) {
return true;
}
log_queued_install_batch();
notify_queued_install_batch();
int res = sceAppInstUtilAppInstallAll();
if (res != 0) {
log_debug(" [REG] Batch install request failed: 0x%x", res);
if (!g_queued_install_submit_failure_notified) {
notify_system("Batch install failed for %d app(s).\ncode=0x%08X",
queued_count, (uint32_t)res);
g_queued_install_submit_failure_notified = true;
}
schedule_queued_install_submit_retry(now_us);
return false;
}
g_queued_install_submit_due_us = 0;
g_queued_install_submit_failure_notified = false;
for (int i = 0; i < MAX_PENDING; i++) {
pending_install_entry_t *entry = &g_pending_installs[i];
if (entry->state != INSTALL_TRACK_QUEUED)
continue;
entry->state = INSTALL_TRACK_SUBMITTED;
entry->requested_at_us = now_us;
g_submitted_install_count++;
clear_failed_mount_attempts(entry->title_id);
}
invalidate_app_db_title_cache();
schedule_pending_install_poll(now_us);
return true;
}
void sm_install_note_submit_failure(void) {
for (int i = 0; i < MAX_PENDING; i++) {
pending_install_entry_t *entry = &g_pending_installs[i];
if (entry->state != INSTALL_TRACK_QUEUED)
continue;
if (note_pending_install_failure(entry) >= MAX_FAILED_MOUNT_ATTEMPTS)
drop_queued_install_entry(entry);
}
}
+4
View File
@@ -17,6 +17,7 @@
#include "sm_title_state.h"
#include "sm_image_cache.h"
#include "sm_image.h"
#include "sm_install_queue.h"
typedef struct {
char discovered_param_roots[MAX_PENDING][MAX_PATH];
@@ -193,6 +194,9 @@ static existing_directory_result_t handle_existing_directory_candidate(
return EXISTING_DIRECTORY_PREFER_CACHED;
}
if (!in_app_db && is_title_install_pending(info->title_id))
return EXISTING_DIRECTORY_HANDLED;
if (!in_app_db && was_register_attempted(info->title_id)) {
return EXISTING_DIRECTORY_HANDLED;
}
+29 -17
View File
@@ -10,6 +10,7 @@
#include "sm_game_lifecycle.h"
#include "sm_image.h"
#include "sm_install.h"
#include "sm_install_queue.h"
#include "sm_kstuff.h"
#include "sm_limits.h"
#include "sm_log.h"
@@ -807,22 +808,20 @@ static bool run_full_scan_cycle(bool startup_sync, const char *reason,
if (should_abort_scan_cycle())
return false;
if (candidate_count > 0) {
if (startup_sync) {
int new_games = 0;
for (int i = 0; i < candidate_count; i++) {
if (!candidates[i].installed)
new_games++;
}
if (new_games > 0)
notify_system_info("Found %d new games. Executing...", new_games);
if (candidate_count > 0 && startup_sync) {
int new_games = 0;
for (int i = 0; i < candidate_count; i++) {
if (!candidates[i].installed)
new_games++;
}
process_scan_candidates(candidates, candidate_count);
if (should_abort_scan_cycle())
return false;
if (new_games > 0)
notify_system_info("Found %d new games. Executing...", new_games);
}
process_scan_candidates(candidates, candidate_count);
if (should_abort_scan_cycle())
return false;
mount_backport_overlays(&unstable_found);
if (should_abort_scan_cycle())
return false;
@@ -858,8 +857,7 @@ static bool run_targeted_scan_cycle(int scan_root_index,
if (should_abort_scan_cycle())
return false;
if (candidate_count > 0)
process_scan_candidates(candidates, candidate_count);
process_scan_candidates(candidates, candidate_count);
if (should_abort_scan_cycle())
return false;
@@ -911,8 +909,15 @@ static int find_due_dirty_scan_root(uint64_t now_us) {
return selected_root;
}
static uint64_t compute_next_scan_deadline_us(uint64_t full_resync_due_us) {
static uint64_t compute_next_scan_deadline_us(uint64_t now_us,
uint64_t full_resync_due_us) {
uint64_t next_deadline = full_resync_due_us;
uint64_t install_wake_us = sm_install_next_wake_us(now_us);
if (install_wake_us != 0 &&
(next_deadline == 0 || install_wake_us < next_deadline)) {
next_deadline = install_wake_us;
}
if (g_scanner_config_reload_pending &&
(next_deadline == 0 ||
@@ -1192,6 +1197,12 @@ void sm_scanner_run_loop(void) {
continue;
}
uint64_t install_wake_us = sm_install_next_wake_us(now_us);
if (install_wake_us != 0 && now_us >= install_wake_us) {
sm_install_service_pending();
continue;
}
if (now_us >= next_full_resync_us) {
bool unstable_found = false;
if (!run_full_scan_cycle(false, NULL, &unstable_found))
@@ -1266,7 +1277,8 @@ void sm_scanner_run_loop(void) {
continue;
}
uint64_t deadline_us = compute_next_scan_deadline_us(next_full_resync_us);
uint64_t deadline_us =
compute_next_scan_deadline_us(now_us, next_full_resync_us);
struct timespec timeout;
build_wait_timeout(&timeout, now_us, deadline_us);