Files
ShadowMountPlus/src/sm_shellcore_flags.c
T
2026-05-13 05:19:22 +03:00

790 lines
26 KiB
C

#include "sm_platform.h"
#include <pthread.h>
#include "sm_game_lifecycle.h"
#include "sm_kstuff.h"
#include "sm_limits.h"
#include "sm_log.h"
#include "sm_runtime.h"
#include "sm_scan.h"
#include "sm_shellcore_flags.h"
#define SHELLCORE_FLAG_WAITMODE_OR 2u
#define SHELLCORE_FLAG_ALL_BITS UINT64_MAX
#define SYSTEM_STATE_MGR_STATUS_SHELLUI_SHUTDOWN_IN_PROGRESS 0x0000000000200000ULL
typedef intptr_t sm_kernel_event_flag_t;
int sceKernelOpenEventFlag(sm_kernel_event_flag_t *ef, const char *name);
int sceKernelPollEventFlag(sm_kernel_event_flag_t ef, uint64_t bit_pattern,
unsigned int wait_mode, uint64_t *result_pattern);
int sceKernelCloseEventFlag(sm_kernel_event_flag_t ef);
typedef struct {
const char *name;
sm_kernel_event_flag_t handle;
uint64_t last_pattern;
int last_rc;
bool required;
bool is_open;
bool has_last_pattern;
bool has_last_rc;
} shellcore_flag_monitor_t;
typedef struct {
uint64_t mask;
const char *name;
} shellcore_flag_bit_desc_t;
static const shellcore_flag_bit_desc_t g_lnc_util_system_status_bits[] = {
{0x0000000000000001ULL, "EXTRA_AUDIO_CPU_BUDGET_AVAILABLE"},
{0x0000000000000002ULL, "SHELLUI_FG_GAME_BG_CPU_MODE"},
};
static shellcore_flag_monitor_t g_shellcore_flags[] = {
{.name = "SceShellCoreUtilAppFocus", .handle = -1, .required = true},
{.name = "SceSystemStateMgrInfo", .handle = -1, .required = true},
{.name = "SceLncUtilSystemStatus", .handle = -1, .required = false},
{.name = "SceSystemStateMgrStatus", .handle = -1},
/* {.name = "SceSysCoreSuspend", .handle = -1},
{.name = "SceShellCoreUtilPowerControl", .handle = -1},
{.name = "SceShellCoreUtilRunLevel", .handle = -1},
{.name = "SceShellCoreUtilUIStatus", .handle = -1},
{.name = "SceAutoMountUsbMass", .handle = -1},
{.name = "SceVshctlInternalUserFlag", .handle = -1},
{.name = "SceBootStatusFlags", .handle = -1},
{.name = "SceSysCoreReboot", .handle = -1},
*/
};
static pthread_t g_shellcore_flag_thread;
static pthread_mutex_t g_shellcore_flag_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t g_shellcore_flag_cond = PTHREAD_COND_INITIALIZER;
static bool g_shellcore_flag_thread_started = false;
static bool g_shellcore_flag_start_ready = false;
static bool g_shellcore_flag_start_success = false;
static volatile sig_atomic_t g_shellcore_flag_stop_requested = 0;
static const shellcore_flag_bit_desc_t g_system_state_mgr_status_bits[] = {
{0x0000000000000001ULL, "CEC_ONE_TOUCH_PLAY_COMMAND"},
{0x0000000000000002ULL, "MEDIA_PLAYBACK_MODE"},
{0x0000000000000004ULL, "BD_DRIVE_READY"},
{0x0000000000000008ULL, "TICK_MUSIC_PLAYBACK"},
{0x0000000000000010ULL, "TICK_VIDEO_PLAYBACK"},
{0x0000000000000020ULL, "TICK_0x20"},
{0x0000000000000040ULL, "TICK_PARTY_CHAT"},
{0x0000000000000100ULL, "TURN_OFF_REQUEST"},
{0x0000000000000200ULL, "REBOOT_REQUEST"},
{0x0000000000000400ULL, "ENTER_STANDBY_REQUEST"},
{0x0000000000010000ULL, "START_SHUTDOWN_TIMER"},
{0x0000000000020000ULL, "START_REBOOT_TIMER"},
{0x0000000000040000ULL, "START_STANDBY_TIMER"},
{0x0000000000080000ULL, "EXTEND_SHUTDOWN_TIMER"},
{0x0000000000100000ULL, "CANCEL_SHUTDOWN_TIMER"},
{SYSTEM_STATE_MGR_STATUS_SHELLUI_SHUTDOWN_IN_PROGRESS,
"SHELLUI_SHUTDOWN_IN_PROGRESS"},
{0x0000000000400000ULL, "EXTEND_SHUTDOWN_TIMER_POST_AUTOUPDATE"},
{0x0000000001000000ULL, "BIGAPP_LAUNCH_READY"},
{0x0000000004000000ULL, "VSH_AUTO_UPDATE_VERIFY_DONE"},
{0x0000000008000000ULL, "START_REBOOT_TIMER_FAST"},
};
#if 0
static const shellcore_flag_bit_desc_t g_power_control_bits[] = {
{0x0000000000020000ULL, "SCREEN_SAVER_ON"},
{0x0000000000040000ULL, "POWER_TRANSITION"},
{0x0000000000080000ULL, "POWER_LOCK"},
{0x0000000000100000ULL, "SOCIAL_SCREEN_STATUS"},
{0x0000000000200000ULL, "REQUEST_CAMERA_CALIBRATION"},
{0x0000000002000000ULL, "USER_INPUT_PULSE"},
{0x0000000004000000ULL, "IMPOSE_OVERLAY"},
{0x0000000008000000ULL, "AUTO_POWER_DOWN_TIMER_RESET"},
{0x0000000010000000ULL, "REMOTE_PLAY_STATUS"},
{0x0000000020000000ULL, "SHARE_PLAY_STATUS"},
{0x0000000040000000ULL, "GAME_LIVE_STREAMING"},
{0x0000000080000000ULL, "BGM_CPU_BUDGET"},
{0x0000000100000000ULL, "PRENOTIFY_REMOTE_PLAY"},
{0x0000000200000000ULL, "PRENOTIFY_SHARE_PLAY"},
{0x0000000400000000ULL, "PRENOTIFY_GAME_LIVE_STREAMING"},
{0x0000000800000000ULL, "REMOTE_PLAY_CPU_BUDGET"},
{0x0000001000000000ULL, "SHARE_PLAY_CPU_BUDGET"},
{0x0000100000000000ULL, "EXT_STORAGE_APP_MOVE_IN_PROGRESS"},
{0x0000200000000000ULL, "STATE_0x2000000000000"},
};
static const shellcore_flag_bit_desc_t g_shellcore_ui_status_bits[] = {
{0x0000000000000001ULL, "LOW_BIT_0x1"},
{0x0000000000000010ULL, "LOW_BIT_0x10"},
{0x0000000000018000ULL, "BASIC_PRODUCT_SHAPE_FIELD"},
{0x0000000000020000ULL, "EXT_HDD_MOUNT_UNBLOCK"},
{0x0000000400000000ULL, "HIGH32_BIT2"},
};
static const shellcore_flag_bit_desc_t g_vshctl_internal_user_bits[] = {
{0x0000000000000001ULL, "INTERNAL_CRONOS_USER"},
};
static const shellcore_flag_bit_desc_t g_boot_status_bits[] = {
{0x0000000000000002ULL, "BIT_0x2"},
{0x0000000000000040ULL, "BIT_0x40"},
{0x0000000000000080ULL, "BIT_0x80"},
{0x0000000000000200ULL, "BIT_0x200"},
{0x0000000000004000ULL, "BIT_0x4000"},
{0x0000000001000000ULL, "BIGAPP_LAUNCH_READY"},
};
static const shellcore_flag_bit_desc_t g_syscore_reboot_bits[] = {
{0x0000000000000040ULL, "BIT_0x40"},
{0x0000000000004000ULL, "BIT_0x4000"},
{0x0000000100000000ULL, "REBOOT_FLAG"},
};
static const shellcore_flag_bit_desc_t g_syscore_suspend_bits[] = {
{0x0000000000000001ULL, "SUSPEND_ACK"},
{0x0000000000000002ULL, "SUSPEND_REQ_BIT1"},
{0x0000000000000004ULL, "SUSPEND_REQ_BIT2"},
{0x0000000000000008ULL, "SUSPEND_REQ_BIT3"},
};
#endif
enum {
SYSTEM_STATE_MGR_STATE_INVALID = 0u,
SYSTEM_STATE_MGR_STATE_INITIALIZING = 10u,
SYSTEM_STATE_MGR_STATE_SHUTDOWN_ON_GOING = 100u,
SYSTEM_STATE_MGR_STATE_POWER_SAVING = 200u,
SYSTEM_STATE_MGR_STATE_SUSPEND_ON_GOING = 300u,
SYSTEM_STATE_MGR_STATE_MAIN_ON_STANDBY = 500u,
SYSTEM_STATE_MGR_STATE_WORKING = 1000u,
};
enum {
SYSTEM_STATE_MGR_REBOOT_CAUSE_NONE = 0u,
SYSTEM_STATE_MGR_REBOOT_CAUSE_CFF = 1u,
};
static const shellcore_flag_bit_desc_t *get_shellcore_flag_bits(
const shellcore_flag_monitor_t *flag, size_t *count_out) {
if (count_out)
*count_out = 0;
if (!flag || !flag->name)
return NULL;
if (strcmp(flag->name, "SceLncUtilSystemStatus") == 0) {
if (count_out) {
*count_out = sizeof(g_lnc_util_system_status_bits) /
sizeof(g_lnc_util_system_status_bits[0]);
}
return g_lnc_util_system_status_bits;
}
if (strcmp(flag->name, "SceSystemStateMgrStatus") == 0) {
if (count_out) {
*count_out = sizeof(g_system_state_mgr_status_bits) /
sizeof(g_system_state_mgr_status_bits[0]);
}
return g_system_state_mgr_status_bits;
}
#if 0
if (strcmp(flag->name, "SceShellCoreUtilPowerControl") == 0) {
if (count_out)
*count_out = sizeof(g_power_control_bits) / sizeof(g_power_control_bits[0]);
return g_power_control_bits;
}
if (strcmp(flag->name, "SceShellCoreUtilUIStatus") == 0) {
if (count_out) {
*count_out = sizeof(g_shellcore_ui_status_bits) /
sizeof(g_shellcore_ui_status_bits[0]);
}
return g_shellcore_ui_status_bits;
}
if (strcmp(flag->name, "SceVshctlInternalUserFlag") == 0) {
if (count_out) {
*count_out = sizeof(g_vshctl_internal_user_bits) /
sizeof(g_vshctl_internal_user_bits[0]);
}
return g_vshctl_internal_user_bits;
}
if (strcmp(flag->name, "SceBootStatusFlags") == 0) {
if (count_out)
*count_out = sizeof(g_boot_status_bits) / sizeof(g_boot_status_bits[0]);
return g_boot_status_bits;
}
if (strcmp(flag->name, "SceSysCoreReboot") == 0) {
if (count_out)
*count_out = sizeof(g_syscore_reboot_bits) / sizeof(g_syscore_reboot_bits[0]);
return g_syscore_reboot_bits;
}
if (strcmp(flag->name, "SceSysCoreSuspend") == 0) {
if (count_out)
*count_out = sizeof(g_syscore_suspend_bits) /
sizeof(g_syscore_suspend_bits[0]);
return g_syscore_suspend_bits;
}
#endif
return NULL;
}
static const char *get_system_state_mgr_state_name(unsigned state) {
switch (state) {
case SYSTEM_STATE_MGR_STATE_INVALID:
return "INVALID";
case SYSTEM_STATE_MGR_STATE_INITIALIZING:
return "INITIALIZING";
case SYSTEM_STATE_MGR_STATE_SHUTDOWN_ON_GOING:
return "SHUTDOWN_ON_GOING";
case SYSTEM_STATE_MGR_STATE_POWER_SAVING:
return "POWER_SAVING";
case SYSTEM_STATE_MGR_STATE_SUSPEND_ON_GOING:
return "SUSPEND_ON_GOING";
case SYSTEM_STATE_MGR_STATE_MAIN_ON_STANDBY:
return "MAIN_ON_STANDBY";
case SYSTEM_STATE_MGR_STATE_WORKING:
return "WORKING";
default:
return "UNKNOWN";
}
}
static const char *get_system_state_mgr_reboot_cause_name(unsigned cause) {
switch (cause) {
case SYSTEM_STATE_MGR_REBOOT_CAUSE_NONE:
return "NONE";
case SYSTEM_STATE_MGR_REBOOT_CAUSE_CFF:
return "CFF";
default:
return "UNKNOWN";
}
}
static void append_shellcore_flag_token(char *dst, size_t dst_size,
const char *token) {
size_t used;
if (!dst || dst_size == 0 || !token || token[0] == '\0')
return;
used = strnlen(dst, dst_size);
if (used >= dst_size - 1)
return;
(void)snprintf(dst + used, dst_size - used, "%s%s", used == 0 ? "" : "|",
token);
}
static bool format_shellcore_flag_special_known_masks(
const shellcore_flag_monitor_t *flag, char *dst, size_t dst_size) {
if (!flag || !flag->name || !dst || dst_size == 0)
return false;
#if 0
if (strcmp(flag->name, "SceShellCoreUtilRunLevel") == 0) {
append_shellcore_flag_token(dst, dst_size, "RUN_LEVEL_NUMERIC");
append_shellcore_flag_token(dst, dst_size, "POLL_MASK=0xFFFFFFF");
append_shellcore_flag_token(dst, dst_size, "INITIAL=10");
append_shellcore_flag_token(dst, dst_size, "READY=100");
return true;
}
if (strcmp(flag->name, "SceAutoMountUsbMass") == 0) {
append_shellcore_flag_token(dst, dst_size, "USB_MOUNT_BITS=0xFFFFFFF");
return true;
}
#endif
if (strcmp(flag->name, "SceSystemStateMgrInfo") == 0) {
append_shellcore_flag_token(dst, dst_size,
"CURRENT_STATE=bits[0..15]");
append_shellcore_flag_token(dst, dst_size,
"TRIGGER_CODE=bits[32..47]");
append_shellcore_flag_token(dst, dst_size,
"REBOOT_CAUSE=bits[48..63]");
append_shellcore_flag_token(dst, dst_size, "STATE_INVALID=0");
append_shellcore_flag_token(dst, dst_size, "STATE_INITIALIZING=10");
append_shellcore_flag_token(dst, dst_size,
"STATE_SHUTDOWN_ON_GOING=100");
append_shellcore_flag_token(dst, dst_size, "STATE_POWER_SAVING=200");
append_shellcore_flag_token(dst, dst_size,
"STATE_SUSPEND_ON_GOING=300");
append_shellcore_flag_token(dst, dst_size,
"STATE_MAIN_ON_STANDBY=500");
append_shellcore_flag_token(dst, dst_size, "STATE_WORKING=1000");
append_shellcore_flag_token(dst, dst_size, "REBOOT_CAUSE_NONE=0");
append_shellcore_flag_token(dst, dst_size, "REBOOT_CAUSE_CFF=1");
append_shellcore_flag_token(dst, dst_size, "INITIAL=0x10000000A");
return true;
}
return false;
}
static void format_shellcore_flag_known_masks(const shellcore_flag_monitor_t *flag,
char *dst, size_t dst_size) {
size_t bit_count = 0;
const shellcore_flag_bit_desc_t *bits = get_shellcore_flag_bits(flag, &bit_count);
if (!dst || dst_size == 0)
return;
dst[0] = '\0';
(void)format_shellcore_flag_special_known_masks(flag, dst, dst_size);
for (size_t i = 0; i < bit_count; ++i) {
char token[96];
(void)snprintf(token, sizeof(token), "%s=0x%llX", bits[i].name,
(unsigned long long)bits[i].mask);
append_shellcore_flag_token(dst, dst_size, token);
}
if (strcmp(flag->name, "SceShellCoreUtilAppFocus") == 0) {
append_shellcore_flag_token(dst, dst_size, "APP_ID");
}
}
static bool format_shellcore_flag_special_value(const shellcore_flag_monitor_t *flag,
uint64_t pattern, char *dst,
size_t dst_size) {
if (!flag || !flag->name || !dst || dst_size == 0)
return false;
#if 0
if (strcmp(flag->name, "SceShellCoreUtilRunLevel") == 0) {
char token[96];
(void)snprintf(token, sizeof(token), "RUN_LEVEL=%u",
(unsigned)(pattern & 0x0FFFFFFFULL));
append_shellcore_flag_token(dst, dst_size, token);
if ((pattern & 0x0FFFFFFFULL) == 10u) {
append_shellcore_flag_token(dst, dst_size, "INITIAL");
} else if ((pattern & 0x0FFFFFFFULL) == 100u) {
append_shellcore_flag_token(dst, dst_size, "READY");
}
if ((pattern & ~0x0FFFFFFFULL) != 0) {
(void)snprintf(token, sizeof(token), "UNKNOWN=0x%llX",
(unsigned long long)(pattern & ~0x0FFFFFFFULL));
append_shellcore_flag_token(dst, dst_size, token);
}
return true;
}
if (strcmp(flag->name, "SceAutoMountUsbMass") == 0) {
uint64_t usb_bits = pattern & 0x0FFFFFFFULL;
for (unsigned i = 0; i < 28; ++i) {
if ((usb_bits & (1ULL << i)) == 0)
continue;
char token[32];
(void)snprintf(token, sizeof(token), "USB%u", i);
append_shellcore_flag_token(dst, dst_size, token);
}
if ((pattern & ~0x0FFFFFFFULL) != 0) {
char token[64];
(void)snprintf(token, sizeof(token), "UNKNOWN=0x%llX",
(unsigned long long)(pattern & ~0x0FFFFFFFULL));
append_shellcore_flag_token(dst, dst_size, token);
}
if (dst[0] == '\0')
(void)snprintf(dst, dst_size, "0");
return true;
}
#endif
if (strcmp(flag->name, "SceSystemStateMgrInfo") == 0) {
unsigned current_state = (unsigned)(pattern & 0xFFFFu);
unsigned mid16 = (unsigned)((pattern >> 16) & 0xFFFFu);
unsigned trigger_code = (unsigned)((pattern >> 32) & 0xFFFFu);
unsigned reboot_cause = (unsigned)((pattern >> 48) & 0xFFFFu);
char token[128];
(void)snprintf(token, sizeof(token), "STATE=%s(0x%04X)",
get_system_state_mgr_state_name(current_state), current_state);
append_shellcore_flag_token(dst, dst_size, token);
(void)snprintf(token, sizeof(token), "TRIGGER=0x%04X", trigger_code);
append_shellcore_flag_token(dst, dst_size, token);
(void)snprintf(token, sizeof(token), "REBOOT_CAUSE=%s(0x%04X)",
get_system_state_mgr_reboot_cause_name(reboot_cause),
reboot_cause);
append_shellcore_flag_token(dst, dst_size, token);
if (mid16 != 0) {
(void)snprintf(token, sizeof(token), "MID16=0x%04X", mid16);
append_shellcore_flag_token(dst, dst_size, token);
}
return true;
}
if (strcmp(flag->name, "SceShellCoreUtilAppFocus") == 0) {
unsigned low32 = (unsigned)(pattern & 0xFFFFFFFFu);
unsigned high32 = (unsigned)(pattern >> 32);
char token[96];
if (low32 != 0) {
(void)snprintf(token, sizeof(token), "APP_ID=0x%08X", low32);
append_shellcore_flag_token(dst, dst_size, token);
}
if (high32 != 0) {
(void)snprintf(token, sizeof(token), "HIGH32=0x%08X", high32);
append_shellcore_flag_token(dst, dst_size, token);
}
if (dst[0] == '\0')
(void)snprintf(dst, dst_size, "0");
return true;
}
#if 0
if (strcmp(flag->name, "SceShellCoreUtilUIStatus") == 0) {
unsigned shape_field = (unsigned)((pattern >> 15) & 0x3u);
if (shape_field != 0) {
char token[96];
(void)snprintf(token, sizeof(token), "BASIC_PRODUCT_SHAPE=%u",
shape_field - 1u);
append_shellcore_flag_token(dst, dst_size, token);
}
return false;
}
#endif
return false;
}
static void format_shellcore_flag_set_bits(const shellcore_flag_monitor_t *flag,
uint64_t pattern, char *dst,
size_t dst_size) {
size_t bit_count = 0;
uint64_t known_mask = 0;
const shellcore_flag_bit_desc_t *bits = get_shellcore_flag_bits(flag, &bit_count);
if (!dst || dst_size == 0)
return;
dst[0] = '\0';
if (format_shellcore_flag_special_value(flag, pattern, dst, dst_size) &&
dst[0] != '\0') {
return;
}
for (size_t i = 0; i < bit_count; ++i) {
known_mask |= bits[i].mask;
if ((pattern & bits[i].mask) == 0)
continue;
char token[96];
(void)snprintf(token, sizeof(token), "%s", bits[i].name);
append_shellcore_flag_token(dst, dst_size, token);
}
if (strcmp(flag->name, "SceShellCoreUtilAppFocus") == 0 &&
(pattern >> 32) == 0 && pattern != 0) {
char token[64];
(void)snprintf(token, sizeof(token), "APP_ID=0x%08X", (unsigned)pattern);
append_shellcore_flag_token(dst, dst_size, token);
}
if ((pattern & ~known_mask) != 0) {
char token[64];
(void)snprintf(token, sizeof(token), "UNKNOWN=0x%llX",
(unsigned long long)(pattern & ~known_mask));
append_shellcore_flag_token(dst, dst_size, token);
}
if (dst[0] == '\0')
(void)snprintf(dst, dst_size, "0");
}
static void set_shellcore_flag_start_result(bool success) {
pthread_mutex_lock(&g_shellcore_flag_mutex);
g_shellcore_flag_start_success = success;
g_shellcore_flag_start_ready = true;
pthread_cond_broadcast(&g_shellcore_flag_cond);
pthread_mutex_unlock(&g_shellcore_flag_mutex);
}
static void enter_sleep_mode_and_cleanup(const char *reason) {
if (request_runtime_sleep_mode(true, reason)) {
runtime_mount_state_lock();
unmount_usb_sources_for_suspend();
runtime_mount_state_unlock();
}
}
static void reset_shellcore_flag_state(shellcore_flag_monitor_t *flag) {
if (!flag)
return;
flag->handle = -1;
flag->is_open = false;
flag->has_last_pattern = false;
flag->has_last_rc = false;
flag->last_pattern = 0;
flag->last_rc = 0;
}
static void log_shellcore_flag_pattern(const shellcore_flag_monitor_t *flag,
uint64_t pattern) {
char decoded[512];
if (!flag)
return;
format_shellcore_flag_set_bits(flag, pattern, decoded, sizeof(decoded));
if (!flag->has_last_pattern) {
log_debug(" [SHELLFLAG] %s initial=0x%016llX low32=0x%08X high32=0x%08X "
"decoded=%s",
flag->name, (unsigned long long)pattern, (unsigned)(pattern),
(unsigned)(pattern >> 32), decoded);
return;
}
log_debug(" [SHELLFLAG] %s value=0x%016llX delta=0x%016llX low32=0x%08X "
"high32=0x%08X decoded=%s",
flag->name, (unsigned long long)pattern,
(unsigned long long)(flag->last_pattern ^ pattern),
(unsigned)(pattern), (unsigned)(pattern >> 32), decoded);
}
static void close_shellcore_flag(shellcore_flag_monitor_t *flag) {
if (!flag || !flag->is_open)
return;
int rc = sceKernelCloseEventFlag(flag->handle);
if (rc < 0) {
log_debug(" [SHELLFLAG] close failed: %s handle=0x%016llX rc=0x%08X",
flag->name, (unsigned long long)flag->handle, (unsigned)rc);
}
reset_shellcore_flag_state(flag);
}
static void close_shellcore_flags(void) {
for (size_t i = 0; i < sizeof(g_shellcore_flags) / sizeof(g_shellcore_flags[0]);
++i) {
close_shellcore_flag(&g_shellcore_flags[i]);
}
}
static bool open_shellcore_flags(size_t *opened_count_out) {
size_t opened_count = 0;
size_t required_count = 0;
size_t required_opened = 0;
if (opened_count_out)
*opened_count_out = 0;
for (size_t i = 0; i < sizeof(g_shellcore_flags) / sizeof(g_shellcore_flags[0]);
++i) {
shellcore_flag_monitor_t *flag = &g_shellcore_flags[i];
sm_kernel_event_flag_t handle = -1;
if (flag->required)
required_count++;
int rc = sceKernelOpenEventFlag(&handle, flag->name);
if (rc < 0) {
log_debug(" [SHELLFLAG] open failed: %s rc=0x%08X", flag->name,
(unsigned)rc);
reset_shellcore_flag_state(flag);
continue;
}
flag->handle = handle;
flag->is_open = true;
if (flag->required)
required_opened++;
log_debug(" [SHELLFLAG] opened %s handle=0x%016llX", flag->name,
(unsigned long long)flag->handle);
{
char known_masks[1024];
format_shellcore_flag_known_masks(flag, known_masks, sizeof(known_masks));
if (known_masks[0] != '\0') {
log_debug(" [SHELLFLAG] known %s masks: %s", flag->name, known_masks);
}
}
opened_count++;
}
if (opened_count_out)
*opened_count_out = opened_count;
return required_opened == required_count;
}
static void poll_shellcore_flag(shellcore_flag_monitor_t *flag) {
uint64_t result_pattern = 0;
int rc;
bool changed = false;
bool entered_shutdown_on_going = false;
bool entered_main_on_standby = false;
bool entered_suspend_on_going = false;
bool entered_resume_working = false;
bool entered_shellui_shutdown_in_progress = false;
bool is_system_state_mgr_info = false;
unsigned current_state = 0;
unsigned previous_state = 0;
if (!flag || !flag->is_open)
return;
rc = sceKernelPollEventFlag(flag->handle, SHELLCORE_FLAG_ALL_BITS,
SHELLCORE_FLAG_WAITMODE_OR, &result_pattern);
if (rc < 0) {
if (!flag->has_last_rc || flag->last_rc != rc) {
log_debug(" [SHELLFLAG] %s poll rc=0x%08X", flag->name,
(unsigned)rc);
}
flag->last_rc = rc;
flag->has_last_rc = true;
return;
}
if (!flag->has_last_rc || flag->last_rc != 0 || !flag->has_last_pattern ||
flag->last_pattern != result_pattern) {
log_shellcore_flag_pattern(flag, result_pattern);
changed = flag->has_last_pattern && flag->last_pattern != result_pattern;
}
is_system_state_mgr_info = strcmp(flag->name, "SceSystemStateMgrInfo") == 0;
if (is_system_state_mgr_info) {
current_state = (unsigned)(result_pattern & 0xFFFFu);
previous_state =
flag->has_last_pattern ? (unsigned)(flag->last_pattern & 0xFFFFu) : 0;
entered_shutdown_on_going =
current_state == SYSTEM_STATE_MGR_STATE_SHUTDOWN_ON_GOING &&
(!flag->has_last_pattern ||
previous_state != SYSTEM_STATE_MGR_STATE_SHUTDOWN_ON_GOING);
entered_main_on_standby =
current_state == SYSTEM_STATE_MGR_STATE_MAIN_ON_STANDBY &&
(!flag->has_last_pattern ||
previous_state != SYSTEM_STATE_MGR_STATE_MAIN_ON_STANDBY);
entered_suspend_on_going =
current_state == SYSTEM_STATE_MGR_STATE_SUSPEND_ON_GOING &&
(!flag->has_last_pattern ||
previous_state != SYSTEM_STATE_MGR_STATE_SUSPEND_ON_GOING);
entered_resume_working =
flag->has_last_pattern &&
current_state == SYSTEM_STATE_MGR_STATE_WORKING &&
previous_state != SYSTEM_STATE_MGR_STATE_WORKING;
} else if (strcmp(flag->name, "SceSystemStateMgrStatus") == 0) {
entered_shellui_shutdown_in_progress =
(result_pattern &
SYSTEM_STATE_MGR_STATUS_SHELLUI_SHUTDOWN_IN_PROGRESS) != 0 &&
(!flag->has_last_pattern ||
(flag->last_pattern &
SYSTEM_STATE_MGR_STATUS_SHELLUI_SHUTDOWN_IN_PROGRESS) == 0);
}
flag->last_pattern = result_pattern;
flag->has_last_pattern = true;
flag->last_rc = 0;
flag->has_last_rc = true;
if (changed && strcmp(flag->name, "SceShellCoreUtilAppFocus") == 0) {
sm_kstuff_note_app_focus((uint32_t)result_pattern);
wake_game_lifecycle_watcher();
}
if (entered_shutdown_on_going) {
request_shutdown_stop("SceSystemStateMgrInfo=SHUTDOWN_ON_GOING");
}
if (entered_main_on_standby || entered_suspend_on_going) {
const char *sleep_reason =
entered_main_on_standby ? "SceSystemStateMgrInfo=MAIN_ON_STANDBY"
: "SceSystemStateMgrInfo=SUSPEND_ON_GOING";
enter_sleep_mode_and_cleanup(sleep_reason);
}
if (entered_shellui_shutdown_in_progress) {
enter_sleep_mode_and_cleanup(
"SceSystemStateMgrStatus=SHELLUI_SHUTDOWN_IN_PROGRESS");
}
if (entered_resume_working) {
request_scan_now("SceSystemStateMgrInfo=WORKING");
request_runtime_sleep_mode(false, "SceSystemStateMgrInfo=WORKING");
}
}
static void *shellcore_flag_thread_main(void *arg) {
(void)arg;
size_t opened_count = 0;
if (!open_shellcore_flags(&opened_count)) {
if (opened_count != 0)
close_shellcore_flags();
set_shellcore_flag_start_result(false);
return NULL;
}
for (size_t i = 0; i < sizeof(g_shellcore_flags) / sizeof(g_shellcore_flags[0]);
++i) {
poll_shellcore_flag(&g_shellcore_flags[i]);
}
log_debug(" [SHELLFLAG] monitor started (%zu/%zu flags opened)", opened_count,
sizeof(g_shellcore_flags) / sizeof(g_shellcore_flags[0]));
set_shellcore_flag_start_result(true);
while (!g_shellcore_flag_stop_requested && !should_stop_requested()) {
for (size_t i = 0; i < sizeof(g_shellcore_flags) / sizeof(g_shellcore_flags[0]);
++i) {
poll_shellcore_flag(&g_shellcore_flags[i]);
}
sceKernelUsleep(SHELLCORE_FLAG_POLL_INTERVAL_US);
}
close_shellcore_flags();
return NULL;
}
bool sm_shellcore_flags_start(void) {
int rc;
if (g_shellcore_flag_thread_started)
return g_shellcore_flag_start_success;
g_shellcore_flag_stop_requested = 0;
g_shellcore_flag_start_ready = false;
g_shellcore_flag_start_success = false;
close_shellcore_flags();
rc = pthread_create(&g_shellcore_flag_thread, NULL, shellcore_flag_thread_main,
NULL);
if (rc != 0) {
log_debug(" [SHELLFLAG] pthread_create failed: rc=%d", rc);
return false;
}
pthread_mutex_lock(&g_shellcore_flag_mutex);
while (!g_shellcore_flag_start_ready)
pthread_cond_wait(&g_shellcore_flag_cond, &g_shellcore_flag_mutex);
pthread_mutex_unlock(&g_shellcore_flag_mutex);
if (!g_shellcore_flag_start_success) {
pthread_join(g_shellcore_flag_thread, NULL);
close_shellcore_flags();
return false;
}
g_shellcore_flag_thread_started = true;
return true;
}
void sm_shellcore_flags_stop(void) {
if (!g_shellcore_flag_thread_started)
return;
g_shellcore_flag_stop_requested = 1;
pthread_join(g_shellcore_flag_thread, NULL);
g_shellcore_flag_thread_started = false;
g_shellcore_flag_start_ready = false;
g_shellcore_flag_start_success = false;
close_shellcore_flags();
}