mirror of
https://github.com/libretro/RetroArch
synced 2025-01-31 15:32:59 +00:00
430baf7c21
This can fix a lot of performance issues, like audio crackling and frame time spikes. This requires the GameMode package to be installed. See: https://github.com/FeralInteractive/gamemode This commit adds a "Game Mode" bool option to the "Power Management" and "Latency" settings sections, and it can be toggled on/off without restarting RA. The actual toggling of game mode happens in a new frontend platform interface function. Perhaps this will become useful for other platforms that provide some equivalent of Linux GameMode. Since the GameMode ABI is fixed, and the API comes as a single, header-only file with no actual deps, we simply bundle the header (deps/feralgamemode/gamemode_client.h.) That way, all Linux builds will have support for GameMode regardless of whether the GameMode development package is installed or not.
775 lines
21 KiB
C
775 lines
21 KiB
C
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <boolean.h>
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
#include <dirent.h>
|
|
|
|
#include <file/nbio.h>
|
|
#include <formats/image.h>
|
|
|
|
#ifdef HAVE_LIBNX
|
|
#include <switch.h>
|
|
#include "../../switch_performance_profiles.h"
|
|
#include "../../configuration.h"
|
|
#include <unistd.h>
|
|
#include <malloc.h>
|
|
#else
|
|
#include <libtransistor/nx.h>
|
|
#include <libtransistor/ipc_helpers.h>
|
|
#endif
|
|
|
|
#include <file/file_path.h>
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "../../config.h"
|
|
#endif
|
|
|
|
#ifndef IS_SALAMANDER
|
|
#include <lists/file_list.h>
|
|
#endif
|
|
|
|
#include <string/stdstring.h>
|
|
|
|
#include "../frontend_driver.h"
|
|
#include "../../verbosity.h"
|
|
#include "../../defaults.h"
|
|
#include "../../paths.h"
|
|
#include "../../retroarch.h"
|
|
#include "../../file_path_special.h"
|
|
|
|
#ifndef IS_SALAMANDER
|
|
#ifdef HAVE_MENU
|
|
#include "../../menu/menu_driver.h"
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef HAVE_LIBNX
|
|
#define SD_PREFIX
|
|
#include "../../gfx/common/switch_common.h"
|
|
#else
|
|
#define SD_PREFIX "/sd"
|
|
#endif
|
|
|
|
static enum frontend_fork switch_fork_mode = FRONTEND_FORK_NONE;
|
|
bool platform_switch_has_focus = true;
|
|
|
|
#ifdef HAVE_LIBNX
|
|
static bool psmInitialized = false;
|
|
|
|
static AppletHookCookie applet_hook_cookie;
|
|
|
|
#ifdef NXLINK
|
|
extern bool nxlink_connected;
|
|
#endif
|
|
|
|
void libnx_apply_overclock(void)
|
|
{
|
|
const size_t profiles_count = sizeof(SWITCH_CPU_PROFILES)
|
|
/ sizeof(SWITCH_CPU_PROFILES[1]);
|
|
settings_t *settings = config_get_ptr();
|
|
unsigned libnx_overclock = settings->uints.libnx_overclock;
|
|
|
|
if (libnx_overclock >= 0 && libnx_overclock <= profiles_count)
|
|
{
|
|
if (hosversionBefore(8, 0, 0))
|
|
{
|
|
pcvSetClockRate(PcvModule_CpuBus, SWITCH_CPU_SPEEDS_VALUES[
|
|
libnx_overclock]);
|
|
}
|
|
else
|
|
{
|
|
ClkrstSession session = {0};
|
|
clkrstOpenSession(&session, PcvModuleId_CpuBus, 3);
|
|
clkrstSetClockRate(&session, SWITCH_CPU_SPEEDS_VALUES[libnx_overclock]);
|
|
clkrstCloseSession(&session);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void on_applet_hook(AppletHookType hook, void *param)
|
|
{
|
|
AppletFocusState focus_state;
|
|
|
|
/* Exit request */
|
|
switch (hook)
|
|
{
|
|
case AppletHookType_OnExitRequest:
|
|
RARCH_LOG("Got AppletHook OnExitRequest, exiting.\n");
|
|
retroarch_main_quit();
|
|
break;
|
|
|
|
/* Focus state*/
|
|
case AppletHookType_OnFocusState:
|
|
focus_state = appletGetFocusState();
|
|
RARCH_LOG("Got AppletHook OnFocusState - new focus state is %d\n", focus_state);
|
|
platform_switch_has_focus = focus_state == AppletFocusState_InFocus;
|
|
|
|
if (!platform_switch_has_focus)
|
|
{
|
|
if (hosversionBefore(8, 0, 0))
|
|
{
|
|
pcvSetClockRate(PcvModule_CpuBus, 1020000000);
|
|
}
|
|
else
|
|
{
|
|
ClkrstSession session = {0};
|
|
clkrstOpenSession(&session, PcvModuleId_CpuBus, 3);
|
|
clkrstSetClockRate(&session, 1020000000);
|
|
clkrstCloseSession(&session);
|
|
}
|
|
}
|
|
else
|
|
libnx_apply_overclock();
|
|
break;
|
|
|
|
/* Performance mode */
|
|
case AppletHookType_OnPerformanceMode:
|
|
libnx_apply_overclock();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
#endif /* HAVE_LIBNX */
|
|
|
|
#ifdef IS_SALAMANDER
|
|
static void get_first_valid_core(char *path_return, size_t len)
|
|
{
|
|
DIR *dir;
|
|
struct dirent *ent;
|
|
const char *extension = ".nro";
|
|
|
|
path_return[0] = '\0';
|
|
|
|
dir = opendir(SD_PREFIX "/retroarch/cores");
|
|
if (dir)
|
|
{
|
|
while ((ent = readdir(dir)))
|
|
{
|
|
if (!ent)
|
|
break;
|
|
if (strlen(ent->d_name) > strlen(extension) && !strcmp(ent->d_name + strlen(ent->d_name) - strlen(extension), extension))
|
|
{
|
|
strcpy_literal(path_return, SD_PREFIX "/retroarch/cores");
|
|
strlcat(path_return, "/", len);
|
|
strlcat(path_return, ent->d_name, len);
|
|
break;
|
|
}
|
|
}
|
|
closedir(dir);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void frontend_switch_get_env(
|
|
int *argc, char *argv[], void *args, void *params_data)
|
|
{
|
|
unsigned i;
|
|
#ifndef IS_SALAMANDER
|
|
#if defined(HAVE_LOGGER)
|
|
logger_init();
|
|
#elif defined(HAVE_FILE_LOGGER)
|
|
retro_main_log_file_init(SD_PREFIX "/retroarch-log.txt");
|
|
#endif
|
|
#endif
|
|
|
|
fill_pathname_basedir(g_defaults.dirs[DEFAULT_DIR_PORT], SD_PREFIX "/retroarch/retroarch_switch.nro", sizeof(g_defaults.dirs[DEFAULT_DIR_PORT]));
|
|
RARCH_LOG("port dir: [%s]\n", g_defaults.dirs[DEFAULT_DIR_PORT]);
|
|
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE], g_defaults.dirs[DEFAULT_DIR_PORT],
|
|
"cores", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE]));
|
|
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_INFO], g_defaults.dirs[DEFAULT_DIR_PORT],
|
|
"info", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_INFO]));
|
|
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_AUTOCONFIG], g_defaults.dirs[DEFAULT_DIR_PORT],
|
|
"autoconfig", sizeof(g_defaults.dirs[DEFAULT_DIR_AUTOCONFIG]));
|
|
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_ASSETS], g_defaults.dirs[DEFAULT_DIR_PORT],
|
|
"assets", sizeof(g_defaults.dirs[DEFAULT_DIR_ASSETS]));
|
|
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SAVESTATE], g_defaults.dirs[DEFAULT_DIR_CORE],
|
|
"savestates", sizeof(g_defaults.dirs[DEFAULT_DIR_SAVESTATE]));
|
|
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SRAM], g_defaults.dirs[DEFAULT_DIR_CORE],
|
|
"savefiles", sizeof(g_defaults.dirs[DEFAULT_DIR_SRAM]));
|
|
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SYSTEM], g_defaults.dirs[DEFAULT_DIR_CORE],
|
|
"system", sizeof(g_defaults.dirs[DEFAULT_DIR_SYSTEM]));
|
|
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG], g_defaults.dirs[DEFAULT_DIR_PORT],
|
|
"config", sizeof(g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG]));
|
|
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_REMAP], g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG],
|
|
"remaps", sizeof(g_defaults.dirs[DEFAULT_DIR_REMAP]));
|
|
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_PLAYLIST], g_defaults.dirs[DEFAULT_DIR_PORT],
|
|
"playlists", sizeof(g_defaults.dirs[DEFAULT_DIR_PLAYLIST]));
|
|
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_RECORD_CONFIG], g_defaults.dirs[DEFAULT_DIR_PORT],
|
|
"records_config", sizeof(g_defaults.dirs[DEFAULT_DIR_RECORD_CONFIG]));
|
|
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_RECORD_OUTPUT], g_defaults.dirs[DEFAULT_DIR_PORT],
|
|
"records", sizeof(g_defaults.dirs[DEFAULT_DIR_RECORD_OUTPUT]));
|
|
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CURSOR], g_defaults.dirs[DEFAULT_DIR_PORT],
|
|
"database/cursors", sizeof(g_defaults.dirs[DEFAULT_DIR_CURSOR]));
|
|
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_DATABASE], g_defaults.dirs[DEFAULT_DIR_PORT],
|
|
"database/rdb", sizeof(g_defaults.dirs[DEFAULT_DIR_DATABASE]));
|
|
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER], g_defaults.dirs[DEFAULT_DIR_PORT],
|
|
"filters", sizeof(g_defaults.dirs[DEFAULT_DIR_REMAP]));
|
|
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SHADER], g_defaults.dirs[DEFAULT_DIR_PORT],
|
|
"shaders", sizeof(g_defaults.dirs[DEFAULT_DIR_SHADER]));
|
|
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CHEATS], g_defaults.dirs[DEFAULT_DIR_PORT],
|
|
"cheats", sizeof(g_defaults.dirs[DEFAULT_DIR_CHEATS]));
|
|
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_OVERLAY], g_defaults.dirs[DEFAULT_DIR_PORT],
|
|
"overlay", sizeof(g_defaults.dirs[DEFAULT_DIR_OVERLAY]));
|
|
|
|
#ifdef HAVE_VIDEO_LAYOUT
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_VIDEO_LAYOUT], g_defaults.dirs[DEFAULT_DIR_PORT],
|
|
"layouts", sizeof(g_defaults.dirs[DEFAULT_DIR_VIDEO_LAYOUT]));
|
|
#endif
|
|
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_ASSETS], g_defaults.dirs[DEFAULT_DIR_PORT],
|
|
"downloads", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_ASSETS]));
|
|
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SCREENSHOT], g_defaults.dirs[DEFAULT_DIR_PORT],
|
|
"screenshots", sizeof(g_defaults.dirs[DEFAULT_DIR_SCREENSHOT]));
|
|
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_THUMBNAILS], g_defaults.dirs[DEFAULT_DIR_PORT],
|
|
"thumbnails", sizeof(g_defaults.dirs[DEFAULT_DIR_THUMBNAILS]));
|
|
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_LOGS], g_defaults.dirs[DEFAULT_DIR_PORT],
|
|
"logs", sizeof(g_defaults.dirs[DEFAULT_DIR_LOGS]));
|
|
|
|
for (i = 0; i < DEFAULT_DIR_LAST; i++)
|
|
{
|
|
const char *dir_path = g_defaults.dirs[i];
|
|
if (!string_is_empty(dir_path))
|
|
path_mkdir(dir_path);
|
|
}
|
|
|
|
fill_pathname_join(g_defaults.path_config,
|
|
g_defaults.dirs[DEFAULT_DIR_PORT],
|
|
FILE_PATH_MAIN_CONFIG,
|
|
sizeof(g_defaults.path_config));
|
|
|
|
#ifndef IS_SALAMANDER
|
|
dir_check_defaults("custom.ini");
|
|
#endif
|
|
}
|
|
|
|
static void frontend_switch_deinit(void *data)
|
|
{
|
|
(void)data;
|
|
|
|
#ifdef HAVE_LIBNX
|
|
nifmExit();
|
|
|
|
if (hosversionBefore(8, 0, 0))
|
|
{
|
|
pcvSetClockRate(PcvModule_CpuBus, 1020000000);
|
|
pcvExit();
|
|
}
|
|
else
|
|
{
|
|
ClkrstSession session = {0};
|
|
clkrstOpenSession(&session, PcvModuleId_CpuBus, 3);
|
|
clkrstSetClockRate(&session, 1020000000);
|
|
clkrstCloseSession(&session);
|
|
clkrstExit();
|
|
}
|
|
|
|
#if defined(SWITCH) && defined(NXLINK)
|
|
socketExit();
|
|
#endif
|
|
|
|
if (psmInitialized)
|
|
psmExit();
|
|
|
|
appletUnlockExit();
|
|
#endif
|
|
}
|
|
|
|
#ifdef HAVE_LIBNX
|
|
static void frontend_switch_exec(const char *path, bool should_load_game)
|
|
{
|
|
char game_path[PATH_MAX-4];
|
|
game_path[0] = '\0';
|
|
|
|
RARCH_LOG("Attempt to load core: [%s].\n", path);
|
|
|
|
#ifndef IS_SALAMANDER
|
|
if (should_load_game && !path_is_empty(RARCH_PATH_CONTENT))
|
|
{
|
|
strlcpy(game_path, path_get(RARCH_PATH_CONTENT), sizeof(game_path));
|
|
RARCH_LOG("content path: [%s].\n", path_get(RARCH_PATH_CONTENT));
|
|
}
|
|
#endif
|
|
|
|
if (path && path[0])
|
|
{
|
|
#ifdef IS_SALAMANDER
|
|
struct stat sbuff;
|
|
bool file_exists = stat(path, &sbuff) == 0;
|
|
|
|
if (!file_exists)
|
|
{
|
|
char core_path[PATH_MAX];
|
|
|
|
/* find first valid core and load it if the target core doesnt exist */
|
|
get_first_valid_core(&core_path[0], PATH_MAX);
|
|
|
|
if (core_path[0] == '\0')
|
|
svcExitProcess();
|
|
}
|
|
#endif
|
|
char *arg_buffer = (char *)malloc(PATH_MAX);
|
|
if (should_load_game)
|
|
snprintf(arg_buffer, PATH_MAX, "%s \"%s\"", path, game_path);
|
|
else
|
|
{
|
|
arg_buffer[0] = '\0';
|
|
strlcpy(arg_buffer, path, PATH_MAX);
|
|
}
|
|
|
|
envSetNextLoad(path, arg_buffer);
|
|
}
|
|
}
|
|
|
|
#ifndef IS_SALAMANDER
|
|
static bool frontend_switch_set_fork(enum frontend_fork fork_mode)
|
|
{
|
|
switch (fork_mode)
|
|
{
|
|
case FRONTEND_FORK_CORE:
|
|
RARCH_LOG("FRONTEND_FORK_CORE\n");
|
|
switch_fork_mode = fork_mode;
|
|
break;
|
|
case FRONTEND_FORK_CORE_WITH_ARGS:
|
|
RARCH_LOG("FRONTEND_FORK_CORE_WITH_ARGS\n");
|
|
switch_fork_mode = fork_mode;
|
|
break;
|
|
case FRONTEND_FORK_RESTART:
|
|
RARCH_LOG("FRONTEND_FORK_RESTART\n");
|
|
/* NOTE: We don't implement Salamander, so just turn
|
|
this into FRONTEND_FORK_CORE. */
|
|
switch_fork_mode = FRONTEND_FORK_CORE;
|
|
break;
|
|
case FRONTEND_FORK_NONE:
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
static void frontend_switch_exitspawn(char *s, size_t len, char *args)
|
|
{
|
|
bool should_load_content = false;
|
|
#ifndef IS_SALAMANDER
|
|
if (switch_fork_mode == FRONTEND_FORK_NONE)
|
|
return;
|
|
|
|
switch (switch_fork_mode)
|
|
{
|
|
case FRONTEND_FORK_CORE_WITH_ARGS:
|
|
should_load_content = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
#endif
|
|
frontend_switch_exec(s, should_load_content);
|
|
}
|
|
|
|
int nanosleep(const struct timespec *rqtp, struct timespec *rmtp)
|
|
{
|
|
svcSleepThread(rqtp->tv_nsec + (rqtp->tv_sec * 1000000000));
|
|
return 0;
|
|
}
|
|
|
|
long sysconf(int name)
|
|
{
|
|
if (name == 8)
|
|
return 0x1000;
|
|
return -1;
|
|
}
|
|
|
|
ssize_t readlink(const char *restrict path, char *restrict buf, size_t bufsize)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
/* Taken from glibc */
|
|
char *realpath(const char *name, char *resolved)
|
|
{
|
|
char *rpath, *dest = NULL;
|
|
const char *start, *end, *rpath_limit;
|
|
long int path_max;
|
|
|
|
/* As per Single Unix Specification V2 we must return an error if
|
|
either parameter is a null pointer. We extend this to allow
|
|
the RESOLVED parameter to be NULL in case the we are expected to
|
|
allocate the room for the return value. */
|
|
if (!name)
|
|
return NULL;
|
|
|
|
/* As per Single Unix Specification V2 we must return an error if
|
|
the name argument points to an empty string. */
|
|
if (name[0] == '\0')
|
|
return NULL;
|
|
|
|
#ifdef PATH_MAX
|
|
path_max = PATH_MAX;
|
|
#else
|
|
path_max = pathconf(name, _PC_PATH_MAX);
|
|
if (path_max <= 0)
|
|
path_max = 1024;
|
|
#endif
|
|
|
|
if (!resolved)
|
|
{
|
|
rpath = malloc(path_max);
|
|
if (!rpath)
|
|
return NULL;
|
|
}
|
|
else
|
|
rpath = resolved;
|
|
rpath_limit = rpath + path_max;
|
|
|
|
if (name[0] != '/')
|
|
{
|
|
if (!getcwd(rpath, path_max))
|
|
{
|
|
rpath[0] = '\0';
|
|
goto error;
|
|
}
|
|
dest = memchr(rpath, '\0', path_max);
|
|
}
|
|
else
|
|
{
|
|
rpath[0] = '/';
|
|
dest = rpath + 1;
|
|
}
|
|
|
|
for (start = end = name; *start; start = end)
|
|
{
|
|
/* Skip sequence of multiple path-separators. */
|
|
while (*start == '/')
|
|
++start;
|
|
|
|
/* Find end of path component. */
|
|
for (end = start; *end && *end != '/'; ++end)
|
|
/* Nothing. */;
|
|
|
|
if (end - start == 0)
|
|
break;
|
|
else if (end - start == 1 && start[0] == '.')
|
|
/* nothing */;
|
|
else if (end - start == 2 && start[0] == '.' && start[1] == '.')
|
|
{
|
|
/* Back up to previous component, ignore if at root already. */
|
|
if (dest > rpath + 1)
|
|
while ((--dest)[-1] != '/')
|
|
;
|
|
}
|
|
else
|
|
{
|
|
size_t new_size;
|
|
|
|
if (dest[-1] != '/')
|
|
*dest++ = '/';
|
|
|
|
if (dest + (end - start) >= rpath_limit)
|
|
{
|
|
ptrdiff_t dest_offset = dest - rpath;
|
|
char *new_rpath;
|
|
|
|
if (resolved)
|
|
{
|
|
if (dest > rpath + 1)
|
|
dest--;
|
|
*dest = '\0';
|
|
goto error;
|
|
}
|
|
new_size = rpath_limit - rpath;
|
|
if (end - start + 1 > path_max)
|
|
new_size += end - start + 1;
|
|
else
|
|
new_size += path_max;
|
|
new_rpath = (char *)realloc(rpath, new_size);
|
|
if (!new_rpath)
|
|
goto error;
|
|
rpath = new_rpath;
|
|
rpath_limit = rpath + new_size;
|
|
|
|
dest = rpath + dest_offset;
|
|
}
|
|
|
|
dest = memcpy(dest, start, end - start);
|
|
*dest = '\0';
|
|
}
|
|
}
|
|
if (dest > rpath + 1 && dest[-1] == '/')
|
|
--dest;
|
|
*dest = '\0';
|
|
|
|
return rpath;
|
|
|
|
error:
|
|
if (!resolved)
|
|
free(rpath);
|
|
return NULL;
|
|
}
|
|
|
|
#endif /* HAVE_LIBNX */
|
|
|
|
static void frontend_switch_shutdown(bool unused)
|
|
{
|
|
(void)unused;
|
|
}
|
|
|
|
/* runloop_get_system_info isnt initialized that early.. */
|
|
extern void retro_get_system_info(struct retro_system_info *info);
|
|
|
|
static void frontend_switch_init(void *data)
|
|
{
|
|
#ifdef HAVE_LIBNX
|
|
Result rc;
|
|
bool recording_supported = false;
|
|
|
|
nifmInitialize(NifmServiceType_User);
|
|
|
|
if (hosversionBefore(8, 0, 0))
|
|
pcvInitialize();
|
|
else
|
|
clkrstInitialize();
|
|
|
|
appletLockExit();
|
|
appletHook(&applet_hook_cookie, on_applet_hook, NULL);
|
|
appletSetFocusHandlingMode(AppletFocusHandlingMode_NoSuspend);
|
|
|
|
appletIsGamePlayRecordingSupported(&recording_supported);
|
|
if (recording_supported)
|
|
appletInitializeGamePlayRecording();
|
|
|
|
#ifdef NXLINK
|
|
socketInitializeDefault();
|
|
nxlink_connected = nxlinkStdio() != -1;
|
|
#ifndef IS_SALAMANDER
|
|
verbosity_enable();
|
|
#endif /* IS_SALAMANDER */
|
|
#endif /* NXLINK */
|
|
|
|
rc = psmInitialize();
|
|
if (R_SUCCEEDED(rc))
|
|
psmInitialized = true;
|
|
else
|
|
RARCH_WARN("Error initializing psm\n");
|
|
#endif /* HAVE_LIBNX (splash) */
|
|
}
|
|
|
|
static int frontend_switch_get_rating(void)
|
|
{
|
|
return 11;
|
|
}
|
|
|
|
enum frontend_architecture frontend_switch_get_arch(void)
|
|
{
|
|
return FRONTEND_ARCH_ARMV8;
|
|
}
|
|
|
|
static int frontend_switch_parse_drive_list(void *data, bool load_content)
|
|
{
|
|
#ifndef IS_SALAMANDER
|
|
file_list_t *list = (file_list_t *)data;
|
|
enum msg_hash_enums enum_idx = load_content
|
|
? MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR
|
|
: MENU_ENUM_LABEL_FILE_BROWSER_DIRECTORY;
|
|
|
|
if (!list)
|
|
return -1;
|
|
|
|
menu_entries_append_enum(list,
|
|
"/", msg_hash_to_str(MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR),
|
|
enum_idx,
|
|
FILE_TYPE_DIRECTORY, 0, 0);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint64_t frontend_switch_get_free_mem(void)
|
|
{
|
|
struct mallinfo mem_info = mallinfo();
|
|
return mem_info.fordblks;
|
|
}
|
|
|
|
static uint64_t frontend_switch_get_total_mem(void)
|
|
{
|
|
struct mallinfo mem_info = mallinfo();
|
|
return mem_info.usmblks;
|
|
}
|
|
|
|
static enum frontend_powerstate
|
|
frontend_switch_get_powerstate(int *seconds, int *percent)
|
|
{
|
|
uint32_t pct;
|
|
PsmChargerType ct;
|
|
Result rc;
|
|
if (!psmInitialized)
|
|
return FRONTEND_POWERSTATE_NONE;
|
|
|
|
rc = psmGetBatteryChargePercentage(&pct);
|
|
if (!R_SUCCEEDED(rc))
|
|
return FRONTEND_POWERSTATE_NONE;
|
|
|
|
rc = psmGetChargerType(&ct);
|
|
if (!R_SUCCEEDED(rc))
|
|
return FRONTEND_POWERSTATE_NONE;
|
|
|
|
*percent = (int)pct;
|
|
|
|
if (*percent >= 100)
|
|
return FRONTEND_POWERSTATE_CHARGED;
|
|
|
|
switch (ct)
|
|
{
|
|
case PsmChargerType_EnoughPower:
|
|
case PsmChargerType_LowPower:
|
|
return FRONTEND_POWERSTATE_CHARGING;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return FRONTEND_POWERSTATE_NO_SOURCE;
|
|
}
|
|
|
|
static void frontend_switch_get_os(
|
|
char *s, size_t len, int *major, int *minor)
|
|
{
|
|
#ifdef HAVE_LIBNX
|
|
u32 hosVersion;
|
|
#else
|
|
int patch;
|
|
char firmware_version[0x100];
|
|
result_t r; /* used by LIB_ASSERT_OK macros */
|
|
ipc_object_t set_sys;
|
|
ipc_request_t rq;
|
|
#endif
|
|
|
|
strcpy_literal(s, "Horizon OS");
|
|
|
|
#ifdef HAVE_LIBNX
|
|
*major = 0;
|
|
*minor = 0;
|
|
|
|
hosVersion = hosversionGet();
|
|
*major = HOSVER_MAJOR(hosVersion);
|
|
*minor = HOSVER_MINOR(hosVersion);
|
|
#else
|
|
/* defaults in case we error out */
|
|
*major = 0;
|
|
*minor = 0;
|
|
|
|
LIB_ASSERT_OK(fail, sm_init());
|
|
LIB_ASSERT_OK(fail_sm, sm_get_service(&set_sys, "set:sys"));
|
|
|
|
rq = ipc_make_request(3);
|
|
ipc_buffer_t buffers[] = {
|
|
ipc_make_buffer(firmware_version, 0x100, 0x1a),
|
|
};
|
|
ipc_msg_set_buffers(rq, buffers, buffer_ptrs);
|
|
|
|
LIB_ASSERT_OK(fail_object, ipc_send(set_sys, &rq, &ipc_default_response_fmt));
|
|
|
|
sscanf(firmware_version + 0x68, "%d.%d.%d", major, minor, &patch);
|
|
|
|
fail_object:
|
|
ipc_close(set_sys);
|
|
fail_sm:
|
|
sm_finalize();
|
|
fail:
|
|
return;
|
|
#endif
|
|
}
|
|
|
|
static void frontend_switch_get_name(char *s, size_t len)
|
|
{
|
|
/* TODO: Add Mariko at some point */
|
|
strcpy_literal(s, "Nintendo Switch");
|
|
}
|
|
|
|
void frontend_switch_process_args(int *argc, char *argv[])
|
|
{
|
|
#ifdef HAVE_STATIC_DUMMY
|
|
if (*argc >= 1)
|
|
{
|
|
/* Ensure current Path is set, only works for the static dummy, likely a hbloader args Issue (?) */
|
|
path_set(RARCH_PATH_CORE, argv[0]);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
frontend_ctx_driver_t frontend_ctx_switch =
|
|
{
|
|
frontend_switch_get_env,
|
|
frontend_switch_init,
|
|
frontend_switch_deinit,
|
|
#ifdef HAVE_LIBNX
|
|
frontend_switch_exitspawn,
|
|
frontend_switch_process_args,
|
|
frontend_switch_exec,
|
|
#ifdef IS_SALAMANDER
|
|
NULL,
|
|
#else
|
|
frontend_switch_set_fork,
|
|
#endif
|
|
#else /* HAVE_LIBNX */
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
#endif /* HAVE_LIBNX */
|
|
frontend_switch_shutdown,
|
|
frontend_switch_get_name,
|
|
frontend_switch_get_os,
|
|
frontend_switch_get_rating,
|
|
NULL, /* content_loaded */
|
|
frontend_switch_get_arch, /* get_architecture */
|
|
frontend_switch_get_powerstate, /* get_powerstate */
|
|
frontend_switch_parse_drive_list, /* parse_drive_list */
|
|
frontend_switch_get_total_mem, /* get_total_mem */
|
|
frontend_switch_get_free_mem, /* get_free_mem */
|
|
NULL, /* install_signal_handler */
|
|
NULL, /* get_signal_handler_state */
|
|
NULL, /* set_signal_handler_state */
|
|
NULL, /* destroy_signal_handler_state */
|
|
NULL, /* attach_console */
|
|
NULL, /* detach_console */
|
|
NULL, /* get_lakka_version */
|
|
NULL, /* set_screen_brightness */
|
|
NULL, /* watch_path_for_changes */
|
|
NULL, /* check_for_path_changes */
|
|
NULL, /* set_sustained_performance_mode */
|
|
NULL, /* get_cpu_model_name */
|
|
NULL, /* get_user_language */
|
|
NULL, /* is_narrator_running */
|
|
NULL, /* accessibility_speak */
|
|
NULL, /* set_gamemode */
|
|
"switch", /* ident */
|
|
NULL /* get_video_driver */
|
|
};
|